29bbad6a40ab3c435f1b1b32db1ee6aade2600c8
[claws.git] / src / imap.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2006 Hiroyuki Yamamoto and the Sylpheed-Claws team
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #  include "config.h"
22 #endif
23
24 #include "defs.h"
25
26 #include <glib.h>
27 #include <glib/gi18n.h>
28 #include <stdio.h>
29 #include <string.h>
30 #include "imap.h"
31 #include "imap_gtk.h"
32 #include "inc.h"
33 #include "xml.h"
34
35 #ifdef HAVE_LIBETPAN
36
37 #include <stdlib.h>
38 #include <dirent.h>
39 #include <unistd.h>
40 #include <ctype.h>
41 #include <time.h>
42 #include <errno.h>
43 #if HAVE_ICONV
44 #  include <iconv.h>
45 #endif
46
47 #if USE_OPENSSL
48 #  include "ssl.h"
49 #endif
50
51 #include "folder.h"
52 #include "session.h"
53 #include "procmsg.h"
54 #include "socket.h"
55 #include "recv.h"
56 #include "procheader.h"
57 #include "prefs_account.h"
58 #include "codeconv.h"
59 #include "md5.h"
60 #include "base64.h"
61 #include "utils.h"
62 #include "prefs_common.h"
63 #include "inputdialog.h"
64 #include "log.h"
65 #include "remotefolder.h"
66 #include "alertpanel.h"
67 #include "sylpheed.h"
68 #include "statusbar.h"
69 #include "msgcache.h"
70 #include "imap-thread.h"
71 #include "account.h"
72
73 typedef struct _IMAPFolder      IMAPFolder;
74 typedef struct _IMAPSession     IMAPSession;
75 typedef struct _IMAPNameSpace   IMAPNameSpace;
76 typedef struct _IMAPFolderItem  IMAPFolderItem;
77
78 #include "prefs_account.h"
79
80 #define IMAP_FOLDER(obj)        ((IMAPFolder *)obj)
81 #define IMAP_FOLDER_ITEM(obj)   ((IMAPFolderItem *)obj)
82 #define IMAP_SESSION(obj)       ((IMAPSession *)obj)
83
84 struct _IMAPFolder
85 {
86         RemoteFolder rfolder;
87
88         /* list of IMAPNameSpace */
89         GList *ns_personal;
90         GList *ns_others;
91         GList *ns_shared;
92         gchar last_seen_separator;
93         guint refcnt;
94 };
95
96 struct _IMAPSession
97 {
98         Session session;
99
100         gboolean authenticated;
101
102         GSList *capability;
103         gboolean uidplus;
104
105         gchar *mbox;
106         guint cmd_count;
107
108         /* CLAWS */
109         gboolean folder_content_changed;
110         guint exists;
111         Folder * folder;
112         gboolean busy;
113 };
114
115 struct _IMAPNameSpace
116 {
117         gchar *name;
118         gchar separator;
119 };
120
121 #define IMAP_SUCCESS    0
122 #define IMAP_SOCKET     2
123 #define IMAP_AUTHFAIL   3
124 #define IMAP_PROTOCOL   4
125 #define IMAP_SYNTAX     5
126 #define IMAP_IOERR      6
127 #define IMAP_ERROR      7
128
129 #define IMAPBUFSIZE     8192
130
131 typedef enum
132 {
133         IMAP_FLAG_SEEN          = 1 << 0,
134         IMAP_FLAG_ANSWERED      = 1 << 1,
135         IMAP_FLAG_FLAGGED       = 1 << 2,
136         IMAP_FLAG_DELETED       = 1 << 3,
137         IMAP_FLAG_DRAFT         = 1 << 4
138 } IMAPFlags;
139
140 #define IMAP_IS_SEEN(flags)     ((flags & IMAP_FLAG_SEEN) != 0)
141 #define IMAP_IS_ANSWERED(flags) ((flags & IMAP_FLAG_ANSWERED) != 0)
142 #define IMAP_IS_FLAGGED(flags)  ((flags & IMAP_FLAG_FLAGGED) != 0)
143 #define IMAP_IS_DELETED(flags)  ((flags & IMAP_FLAG_DELETED) != 0)
144 #define IMAP_IS_DRAFT(flags)    ((flags & IMAP_FLAG_DRAFT) != 0)
145
146
147 #define IMAP4_PORT      143
148 #if USE_OPENSSL
149 #define IMAPS_PORT      993
150 #endif
151
152 #define IMAP_CMD_LIMIT  1000
153
154 struct _IMAPFolderItem
155 {
156         FolderItem item;
157
158         guint lastuid;
159         guint uid_next;
160         GSList *uid_list;
161         gboolean batching;
162
163         time_t use_cache;
164         gint c_messages;
165         guint32 c_uid_next;
166         guint32 c_uid_validity;
167         gint c_unseen;
168
169         GHashTable *flags_set_table;
170         GHashTable *flags_unset_table;
171 };
172
173 static XMLTag *imap_item_get_xml(Folder *folder, FolderItem *item);
174 static void imap_item_set_xml(Folder *folder, FolderItem *item, XMLTag *tag);
175
176 static void imap_folder_init            (Folder         *folder,
177                                          const gchar    *name,
178                                          const gchar    *path);
179
180 static Folder   *imap_folder_new        (const gchar    *name,
181                                          const gchar    *path);
182 static void      imap_folder_destroy    (Folder         *folder);
183
184 static IMAPSession *imap_session_new    (Folder         *folder,
185                                          const PrefsAccount     *account);
186 static void     imap_session_authenticate(IMAPSession           *session,
187                                           const PrefsAccount    *account);
188 static void     imap_session_destroy    (Session        *session);
189
190 static gchar   *imap_fetch_msg          (Folder         *folder, 
191                                          FolderItem     *item, 
192                                          gint            uid);
193 static gchar   *imap_fetch_msg_full     (Folder         *folder, 
194                                          FolderItem     *item, 
195                                          gint            uid,
196                                          gboolean        headers,
197                                          gboolean        body);
198 static gint     imap_add_msg            (Folder         *folder,
199                                          FolderItem     *dest,
200                                          const gchar    *file, 
201                                          MsgFlags       *flags);
202 static gint     imap_add_msgs           (Folder         *folder, 
203                                          FolderItem     *dest,
204                                          GSList         *file_list,
205                                          GRelation      *relation);
206
207 static gint     imap_copy_msg           (Folder         *folder,
208                                          FolderItem     *dest, 
209                                          MsgInfo        *msginfo);
210 static gint     imap_copy_msgs          (Folder         *folder, 
211                                          FolderItem     *dest, 
212                                          MsgInfoList    *msglist, 
213                                          GRelation      *relation);
214
215 static gint     imap_remove_msg         (Folder         *folder, 
216                                          FolderItem     *item, 
217                                          gint            uid);
218 static gint     imap_remove_msgs        (Folder         *folder, 
219                                          FolderItem     *dest, 
220                                          MsgInfoList    *msglist, 
221                                          GRelation      *relation);
222 static gint     imap_remove_all_msg     (Folder         *folder, 
223                                          FolderItem     *item);
224
225 static gboolean imap_is_msg_changed     (Folder         *folder,
226                                          FolderItem     *item, 
227                                          MsgInfo        *msginfo);
228
229 static gint     imap_close              (Folder         *folder, 
230                                          FolderItem     *item);
231
232 static gint     imap_scan_tree          (Folder         *folder);
233
234 static gint     imap_create_tree        (Folder         *folder);
235
236 static FolderItem *imap_create_folder   (Folder         *folder,
237                                          FolderItem     *parent,
238                                          const gchar    *name);
239 static gint     imap_rename_folder      (Folder         *folder,
240                                          FolderItem     *item, 
241                                          const gchar    *name);
242 static gint     imap_remove_folder      (Folder         *folder, 
243                                          FolderItem     *item);
244
245 static FolderItem *imap_folder_item_new (Folder         *folder);
246 static void imap_folder_item_destroy    (Folder         *folder,
247                                          FolderItem     *item);
248
249 static IMAPSession *imap_session_get    (Folder         *folder);
250
251 static gint imap_auth                   (IMAPSession    *session,
252                                          const gchar    *user,
253                                          const gchar    *pass,
254                                          IMAPAuthType    type);
255
256 static gint imap_scan_tree_recursive    (IMAPSession    *session,
257                                          FolderItem     *item);
258
259 static void imap_create_missing_folders (Folder         *folder);
260 static FolderItem *imap_create_special_folder
261                                         (Folder                 *folder,
262                                          SpecialFolderItemType   stype,
263                                          const gchar            *name);
264
265 static gint imap_do_copy_msgs           (Folder         *folder,
266                                          FolderItem     *dest,
267                                          MsgInfoList    *msglist,
268                                          GRelation      *relation);
269
270 static void imap_delete_all_cached_messages     (FolderItem     *item);
271 static void imap_set_batch              (Folder         *folder,
272                                          FolderItem     *item,
273                                          gboolean        batch);
274 static gint imap_set_message_flags      (IMAPSession    *session,
275                                          MsgNumberList  *numlist,
276                                          IMAPFlags       flags,
277                                          gboolean        is_set);
278 static gint imap_select                 (IMAPSession    *session,
279                                          IMAPFolder     *folder,
280                                          const gchar    *path,
281                                          gint           *exists,
282                                          gint           *recent,
283                                          gint           *unseen,
284                                          guint32        *uid_validity,
285                                          gboolean        block);
286 static gint imap_status                 (IMAPSession    *session,
287                                          IMAPFolder     *folder,
288                                          const gchar    *path,
289                                          IMAPFolderItem *item,
290                                          gint           *messages,
291                                          guint32        *uid_next,
292                                          guint32        *uid_validity,
293                                          gint           *unseen,
294                                          gboolean        block);
295
296 static IMAPNameSpace *imap_find_namespace       (IMAPFolder     *folder,
297                                                  const gchar    *path);
298 static gchar imap_get_path_separator            (IMAPFolder     *folder,
299                                                  const gchar    *path);
300 static gchar *imap_get_real_path                (IMAPFolder     *folder,
301                                                  const gchar    *path);
302 static void imap_synchronise            (FolderItem     *item);
303
304 static void imap_free_capabilities      (IMAPSession    *session);
305
306 /* low-level IMAP4rev1 commands */
307 static gint imap_cmd_login      (IMAPSession    *session,
308                                  const gchar    *user,
309                                  const gchar    *pass,
310                                  const gchar    *type);
311 static gint imap_cmd_logout     (IMAPSession    *session);
312 static gint imap_cmd_noop       (IMAPSession    *session);
313 #if USE_OPENSSL
314 static gint imap_cmd_starttls   (IMAPSession    *session);
315 #endif
316 static gint imap_cmd_select     (IMAPSession    *session,
317                                  const gchar    *folder,
318                                  gint           *exists,
319                                  gint           *recent,
320                                  gint           *unseen,
321                                  guint32        *uid_validity,
322                                  gboolean        block);
323 static gint imap_cmd_examine    (IMAPSession    *session,
324                                  const gchar    *folder,
325                                  gint           *exists,
326                                  gint           *recent,
327                                  gint           *unseen,
328                                  guint32        *uid_validity,
329                                  gboolean        block);
330 static gint imap_cmd_create     (IMAPSession    *sock,
331                                  const gchar    *folder);
332 static gint imap_cmd_rename     (IMAPSession    *sock,
333                                  const gchar    *oldfolder,
334                                  const gchar    *newfolder);
335 static gint imap_cmd_delete     (IMAPSession    *session,
336                                  const gchar    *folder);
337 static gint imap_cmd_fetch      (IMAPSession    *sock,
338                                  guint32         uid,
339                                  const gchar    *filename,
340                                  gboolean        headers,
341                                  gboolean        body);
342 static gint imap_cmd_append     (IMAPSession    *session,
343                                  const gchar    *destfolder,
344                                  const gchar    *file,
345                                  IMAPFlags       flags,
346                                  guint32        *new_uid);
347 static gint imap_cmd_copy       (IMAPSession *session,
348                                  struct mailimap_set * set,
349                                  const gchar *destfolder,
350                                  GRelation *uid_mapping);
351 static gint imap_cmd_store      (IMAPSession    *session,
352                                  struct mailimap_set * set,
353                                  IMAPFlags flags,
354                                  int do_add);
355 static gint imap_cmd_expunge    (IMAPSession    *session);
356
357 static void imap_path_separator_subst           (gchar          *str,
358                                                  gchar           separator);
359
360 static gchar *imap_utf8_to_modified_utf7        (const gchar    *from);
361 static gchar *imap_modified_utf7_to_utf8        (const gchar    *mutf7_str);
362
363 static gboolean imap_rename_folder_func         (GNode          *node,
364                                                  gpointer        data);
365 static gint imap_get_num_list                   (Folder         *folder,
366                                                  FolderItem     *item,
367                                                  GSList        **list,
368                                                  gboolean       *old_uids_valid);
369 static GSList *imap_get_msginfos                (Folder         *folder,
370                                                  FolderItem     *item,
371                                                  GSList         *msgnum_list);
372 static MsgInfo *imap_get_msginfo                (Folder         *folder,
373                                                  FolderItem     *item,
374                                                  gint            num);
375 static gboolean imap_scan_required              (Folder         *folder,
376                                                  FolderItem     *item);
377 static void imap_change_flags                   (Folder         *folder,
378                                                  FolderItem     *item,
379                                                  MsgInfo        *msginfo,
380                                                  MsgPermFlags    newflags);
381 static gint imap_get_flags                      (Folder         *folder,
382                                                  FolderItem     *item,
383                                                  MsgInfoList    *msglist,
384                                                  GRelation      *msgflags);
385 static gchar *imap_folder_get_path              (Folder         *folder);
386 static gchar *imap_item_get_path                (Folder         *folder,
387                                                  FolderItem     *item);
388 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item);
389
390
391 /* data types conversion libetpan <-> sylpheed */
392 static GSList * imap_list_from_lep(IMAPFolder * folder,
393                                    clist * list, const gchar * real_path, gboolean all);
394 static GSList * imap_get_lep_set_from_numlist(MsgNumberList *numlist);
395 static GSList * imap_get_lep_set_from_msglist(MsgInfoList *msglist);
396 static GSList * imap_uid_list_from_lep(clist * list);
397 static GSList * imap_uid_list_from_lep_tab(carray * list);
398 static MsgInfo *imap_envelope_from_lep(struct imap_fetch_env_info * info,
399                                        FolderItem *item);
400 static void imap_lep_set_free(GSList *seq_list);
401 static struct mailimap_flag_list * imap_flag_to_lep(IMAPFlags flags);
402
403 typedef struct _hashtable_data {
404         IMAPSession *session;
405         GSList *msglist;
406         IMAPFolderItem *item;
407 } hashtable_data;
408
409 static FolderClass imap_class;
410
411 typedef struct _thread_data {
412         gchar *server;
413         gushort port;
414         gboolean done;
415         SockInfo *sock;
416 #ifdef USE_OPENSSL
417         SSLType ssl_type;
418 #endif
419 } thread_data;
420
421 FolderClass *imap_get_class(void)
422 {
423         if (imap_class.idstr == NULL) {
424                 imap_class.type = F_IMAP;
425                 imap_class.idstr = "imap";
426                 imap_class.uistr = "IMAP4";
427
428                 /* Folder functions */
429                 imap_class.new_folder = imap_folder_new;
430                 imap_class.destroy_folder = imap_folder_destroy;
431                 imap_class.scan_tree = imap_scan_tree;
432                 imap_class.create_tree = imap_create_tree;
433
434                 /* FolderItem functions */
435                 imap_class.item_new = imap_folder_item_new;
436                 imap_class.item_destroy = imap_folder_item_destroy;
437                 imap_class.item_get_path = imap_item_get_path;
438                 imap_class.create_folder = imap_create_folder;
439                 imap_class.rename_folder = imap_rename_folder;
440                 imap_class.remove_folder = imap_remove_folder;
441                 imap_class.close = imap_close;
442                 imap_class.get_num_list = imap_get_num_list;
443                 imap_class.scan_required = imap_scan_required;
444                 imap_class.set_xml = folder_set_xml;
445                 imap_class.get_xml = folder_get_xml;
446                 imap_class.item_set_xml = imap_item_set_xml;
447                 imap_class.item_get_xml = imap_item_get_xml;
448
449                 /* Message functions */
450                 imap_class.get_msginfo = imap_get_msginfo;
451                 imap_class.get_msginfos = imap_get_msginfos;
452                 imap_class.fetch_msg = imap_fetch_msg;
453                 imap_class.fetch_msg_full = imap_fetch_msg_full;
454                 imap_class.add_msg = imap_add_msg;
455                 imap_class.add_msgs = imap_add_msgs;
456                 imap_class.copy_msg = imap_copy_msg;
457                 imap_class.copy_msgs = imap_copy_msgs;
458                 imap_class.remove_msg = imap_remove_msg;
459                 imap_class.remove_msgs = imap_remove_msgs;
460                 imap_class.remove_all_msg = imap_remove_all_msg;
461                 imap_class.is_msg_changed = imap_is_msg_changed;
462                 imap_class.change_flags = imap_change_flags;
463                 imap_class.get_flags = imap_get_flags;
464                 imap_class.set_batch = imap_set_batch;
465                 imap_class.synchronise = imap_synchronise;
466 #ifdef USE_PTREAD
467                 pthread_mutex_init(&imap_mutex, NULL);
468 #endif
469         }
470         
471         return &imap_class;
472 }
473
474 static Folder *imap_folder_new(const gchar *name, const gchar *path)
475 {
476         Folder *folder;
477
478         folder = (Folder *)g_new0(IMAPFolder, 1);
479         folder->klass = &imap_class;
480         imap_folder_init(folder, name, path);
481
482         return folder;
483 }
484
485 static void imap_folder_destroy(Folder *folder)
486 {
487         while (imap_folder_get_refcnt(folder) > 0)
488                 gtk_main_iteration();
489         
490         folder_remote_folder_destroy(REMOTE_FOLDER(folder));
491         imap_done(folder);
492 }
493
494 static void imap_folder_init(Folder *folder, const gchar *name,
495                              const gchar *path)
496 {
497         folder_remote_folder_init((Folder *)folder, name, path);
498 }
499
500 static FolderItem *imap_folder_item_new(Folder *folder)
501 {
502         IMAPFolderItem *item;
503         
504         item = g_new0(IMAPFolderItem, 1);
505         item->lastuid = 0;
506         item->uid_next = 0;
507         item->uid_list = NULL;
508
509         return (FolderItem *)item;
510 }
511
512 static void imap_folder_item_destroy(Folder *folder, FolderItem *_item)
513 {
514         IMAPFolderItem *item = (IMAPFolderItem *)_item;
515
516         g_return_if_fail(item != NULL);
517         g_slist_free(item->uid_list);
518
519         g_free(_item);
520 }
521
522 static gboolean imap_reset_uid_lists_func(GNode *node, gpointer data)
523 {
524         IMAPFolderItem *item = (IMAPFolderItem *)node->data;
525         
526         item->lastuid = 0;
527         g_slist_free(item->uid_list);
528         item->uid_list = NULL;
529         
530         return FALSE;
531 }
532
533 static void imap_reset_uid_lists(Folder *folder)
534 {
535         if(folder->node == NULL)
536                 return;
537         
538         /* Destroy all uid lists and rest last uid */
539         g_node_traverse(folder->node, G_IN_ORDER, G_TRAVERSE_ALL, -1, imap_reset_uid_lists_func, NULL); 
540 }
541
542 void imap_get_capabilities(IMAPSession *session)
543 {
544         struct mailimap_capability_data *capabilities = NULL;
545         clistiter *cur;
546
547         if (session->capability != NULL)
548                 return;
549
550         capabilities = imap_threaded_capability(session->folder);
551
552         if (capabilities == NULL)
553                 return;
554
555         for(cur = clist_begin(capabilities->cap_list) ; cur != NULL ;
556             cur = clist_next(cur)) {
557                 struct mailimap_capability * cap = 
558                         clist_content(cur);
559                 if (!cap || cap->cap_data.cap_name == NULL)
560                         continue;
561                 session->capability = g_slist_append
562                                 (session->capability,
563                                  g_strdup(cap->cap_data.cap_name));
564                 debug_print("got capa %s\n", cap->cap_data.cap_name);
565         }
566         mailimap_capability_data_free(capabilities);
567 }
568
569 gboolean imap_has_capability(IMAPSession *session, const gchar *cap) 
570 {
571         GSList *cur;
572         for (cur = session->capability; cur; cur = cur->next) {
573                 if (!g_ascii_strcasecmp(cur->data, cap))
574                         return TRUE;
575         }
576         return FALSE;
577 }
578
579 static gint imap_auth(IMAPSession *session, const gchar *user, const gchar *pass,
580                       IMAPAuthType type)
581 {
582         gint ok = IMAP_ERROR;
583         static time_t last_login_err = 0;
584         
585         imap_get_capabilities(session);
586
587         switch(type) {
588         case IMAP_AUTH_ANON:
589                 ok = imap_cmd_login(session, user, pass, "ANONYMOUS");
590                 break;
591         case IMAP_AUTH_CRAM_MD5:
592                 ok = imap_cmd_login(session, user, pass, "CRAM-MD5");
593                 break;
594         case IMAP_AUTH_LOGIN:
595                 ok = imap_cmd_login(session, user, pass, "LOGIN");
596                 break;
597         default:
598                 debug_print("capabilities:\n"
599                                 "\t ANONYMOUS %d\n"
600                                 "\t CRAM-MD5 %d\n"
601                                 "\t LOGIN %d\n", 
602                         imap_has_capability(session, "ANONYMOUS"),
603                         imap_has_capability(session, "CRAM-MD5"),
604                         imap_has_capability(session, "LOGIN"));
605                 if (imap_has_capability(session, "CRAM-MD5"))
606                         ok = imap_cmd_login(session, user, pass, "CRAM-MD5");
607                 if (ok == IMAP_ERROR) /* we always try LOGIN before giving up */
608                         ok = imap_cmd_login(session, user, pass, "LOGIN");
609         }
610         if (ok == IMAP_SUCCESS)
611                 session->authenticated = TRUE;
612         else {
613                 gchar *ext_info = NULL;
614                 
615                 if (type == IMAP_AUTH_CRAM_MD5) {
616                         ext_info = _("\n\nCRAM-MD5 logins only work if libetpan has been "
617                                      "compiled with SASL support and the "
618                                      "CRAM-MD5 SASL plugin is installed.");
619                 } else {
620                         ext_info = "";
621                 }
622                 
623                 if (time(NULL) - last_login_err > 10) {
624                         if (!prefs_common.no_recv_err_panel) {
625                                 alertpanel_error(_("Connection to %s failed: "
626                                         "login refused.%s"),
627                                         SESSION(session)->server, ext_info);
628                         } else {
629                                 log_error(_("Connection to %s failed: "
630                                         "login refused.%s\n"),
631                                         SESSION(session)->server, ext_info);
632                         }
633                 }
634                 last_login_err = time(NULL);
635         }
636         return ok;
637 }
638
639 static IMAPSession *imap_reconnect_if_possible(Folder *folder, IMAPSession *session)
640 {
641         RemoteFolder *rfolder = REMOTE_FOLDER(folder);
642         /* Check if this is the first try to establish a
643            connection, if yes we don't try to reconnect */
644         debug_print("reconnecting\n");
645         if (rfolder->session == NULL) {
646                 log_warning(_("Connecting to %s failed"),
647                             folder->account->recv_server);
648                 session_destroy(SESSION(session));
649                 session = NULL;
650         } else {
651                 log_warning(_("IMAP4 connection to %s has been"
652                             " disconnected. Reconnecting...\n"),
653                             folder->account->recv_server);
654                 statusbar_print_all(_("IMAP4 connection to %s has been"
655                             " disconnected. Reconnecting...\n"),
656                             folder->account->recv_server);
657                 SESSION(session)->state = SESSION_DISCONNECTED;
658                 session_destroy(SESSION(session));
659                 /* Clear folders session to make imap_session_get create
660                    a new session, because of rfolder->session == NULL
661                    it will not try to reconnect again and so avoid an
662                    endless loop */
663                 rfolder->session = NULL;
664                 session = imap_session_get(folder);
665                 rfolder->session = SESSION(session);
666                 statusbar_pop_all();
667         }
668         return session;
669 }
670
671 #define lock_session() {\
672         debug_print("locking session\n"); \
673         session->busy = TRUE;\
674 }
675
676 #define unlock_session() {\
677         debug_print("unlocking session\n"); \
678         session->busy = FALSE;\
679 }
680
681 static IMAPSession *imap_session_get(Folder *folder)
682 {
683         RemoteFolder *rfolder = REMOTE_FOLDER(folder);
684         IMAPSession *session = NULL;
685         static time_t last_failure = 0;
686
687         g_return_val_if_fail(folder != NULL, NULL);
688         g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, NULL);
689         g_return_val_if_fail(folder->account != NULL, NULL);
690         
691         if (prefs_common.work_offline && 
692             !inc_offline_should_override(
693                 _("Sylpheed-Claws needs network access in order "
694                   "to access the IMAP server."))) {
695                 return NULL;
696         }
697
698         /* Make sure we have a session */
699         if (rfolder->session != NULL) {
700                 session = IMAP_SESSION(rfolder->session);
701                 /* don't do that yet... 
702                 if (session->busy) {
703                         return NULL;
704                 } */
705         } else {
706                 imap_reset_uid_lists(folder);
707                 if (time(NULL) - last_failure <= 2)
708                         return NULL;
709                 session = imap_session_new(folder, folder->account);
710         }
711         if(session == NULL) {
712                 last_failure = time(NULL);
713                 return NULL;
714         }
715
716         /* Make sure session is authenticated */
717         if (!IMAP_SESSION(session)->authenticated)
718                 imap_session_authenticate(IMAP_SESSION(session), folder->account);
719         
720         if (!IMAP_SESSION(session)->authenticated) {
721                 session_destroy(SESSION(session));
722                 rfolder->session = NULL;
723                 last_failure = time(NULL);
724                 return NULL;
725         }
726
727         /* I think the point of this code is to avoid sending a
728          * keepalive if we've used the session recently and therefore
729          * think it's still alive.  Unfortunately, most of the code
730          * does not yet check for errors on the socket, and so if the
731          * connection drops we don't notice until the timeout expires.
732          * A better solution than sending a NOOP every time would be
733          * for every command to be prepared to retry until it is
734          * successfully sent. -- mbp */
735         if (time(NULL) - SESSION(session)->last_access_time > SESSION_TIMEOUT_INTERVAL) {
736                 /* verify that the session is still alive */
737                 if (imap_cmd_noop(session) != IMAP_SUCCESS) {
738                         debug_print("disconnected!\n");
739                         session = imap_reconnect_if_possible(folder, session);
740                 }
741         }
742
743         rfolder->session = SESSION(session);
744         
745         return IMAP_SESSION(session);
746 }
747
748 static IMAPSession *imap_session_new(Folder * folder,
749                                      const PrefsAccount *account)
750 {
751         IMAPSession *session;
752         gushort port;
753         int r;
754         int authenticated;
755         
756 #ifdef USE_OPENSSL
757         /* FIXME: IMAP over SSL only... */ 
758         SSLType ssl_type;
759
760         port = account->set_imapport ? account->imapport
761                 : account->ssl_imap == SSL_TUNNEL ? IMAPS_PORT : IMAP4_PORT;
762         ssl_type = account->ssl_imap;   
763 #else
764         if (account->ssl_imap != SSL_NONE) {
765                 if (alertpanel_full(_("Insecure connection"),
766                         _("This connection is configured to be secured "
767                           "using SSL, but SSL is not available in this "
768                           "build of Sylpheed-Claws. \n\n"
769                           "Do you want to continue connecting to this "
770                           "server? The communication would not be "
771                           "secure."),
772                           GTK_STOCK_CANCEL, _("Con_tinue connecting"), 
773                           NULL, FALSE, NULL, ALERT_WARNING,
774                           G_ALERTDEFAULT) != G_ALERTALTERNATE)
775                         return NULL;
776         }
777         port = account->set_imapport ? account->imapport
778                 : IMAP4_PORT;
779 #endif
780
781         imap_init(folder);
782         statusbar_print_all(_("Connecting to IMAP4 server: %s..."), folder->account->recv_server);
783         if (account->set_tunnelcmd) {
784                 r = imap_threaded_connect_cmd(folder,
785                                               account->tunnelcmd,
786                                               account->recv_server,
787                                               port);
788         }
789         else {
790 #ifdef USE_OPENSSL
791                 if (ssl_type == SSL_TUNNEL) {
792                         r = imap_threaded_connect_ssl(folder,
793                                                       account->recv_server,
794                                                       port);
795                 }
796                 else 
797 #endif
798                 {
799                         r = imap_threaded_connect(folder,
800                                                   account->recv_server,
801                                                   port);
802                 }
803         }
804         
805         statusbar_pop_all();
806         if (r == MAILIMAP_NO_ERROR_AUTHENTICATED) {
807                 authenticated = TRUE;
808         }
809         else if (r == MAILIMAP_NO_ERROR_NON_AUTHENTICATED) {
810                 authenticated = FALSE;
811         }
812         else {
813                 if(!prefs_common.no_recv_err_panel) {
814                         alertpanel_error(_("Can't connect to IMAP4 server: %s:%d"),
815                                          account->recv_server, port);
816                 } else {
817                         log_error(_("Can't connect to IMAP4 server: %s:%d\n"),
818                                          account->recv_server, port);
819                 } 
820                 
821                 return NULL;
822         }
823         
824         session = g_new0(IMAPSession, 1);
825         session_init(SESSION(session));
826         SESSION(session)->type             = SESSION_IMAP;
827         SESSION(session)->server           = g_strdup(account->recv_server);
828         SESSION(session)->sock             = NULL;
829         
830         SESSION(session)->destroy          = imap_session_destroy;
831
832         session->capability = NULL;
833         
834         session->authenticated = authenticated;
835         session->mbox = NULL;
836         session->cmd_count = 0;
837         session->folder = folder;
838         IMAP_FOLDER(session->folder)->last_seen_separator = 0;
839
840 #if USE_OPENSSL
841         if (account->ssl_imap == SSL_STARTTLS) {
842                 gint ok;
843
844                 ok = imap_cmd_starttls(session);
845                 if (ok != IMAP_SUCCESS) {
846                         log_warning(_("Can't start TLS session.\n"));
847                         session_destroy(SESSION(session));
848                         return NULL;
849                 }
850
851                 imap_free_capabilities(session);
852                 session->authenticated = FALSE;
853                 session->uidplus = FALSE;
854                 session->cmd_count = 1;
855         }
856 #endif
857         log_message("IMAP connection is %s-authenticated\n",
858                     (session->authenticated) ? "pre" : "un");
859         
860         return session;
861 }
862
863 static void imap_session_authenticate(IMAPSession *session, 
864                                       const PrefsAccount *account)
865 {
866         gchar *pass, *acc_pass;
867         gboolean failed = FALSE;
868
869         g_return_if_fail(account->userid != NULL);
870         acc_pass = account->passwd;
871 try_again:
872         pass = acc_pass;
873         if (!pass && account->imap_auth_type != IMAP_AUTH_ANON) {
874                 gchar *tmp_pass;
875                 tmp_pass = input_dialog_query_password(account->recv_server, account->userid);
876                 if (!tmp_pass)
877                         return;
878                 Xstrdup_a(pass, tmp_pass, {g_free(tmp_pass); return;});
879                 g_free(tmp_pass);
880         } else if (account->imap_auth_type == IMAP_AUTH_ANON) {
881                 pass = "";
882         }
883         statusbar_print_all(_("Connecting to IMAP4 server %s...\n"),
884                                 account->recv_server);
885         if (imap_auth(session, account->userid, pass, account->imap_auth_type) != IMAP_SUCCESS) {
886                 statusbar_pop_all();
887                 
888                 if (!failed) {
889                         acc_pass = NULL;
890                         failed = TRUE;
891                         goto try_again;
892                 } else {
893                         imap_threaded_disconnect(session->folder);
894                         imap_cmd_logout(session);
895                         alertpanel_error(_("Couldn't login to IMAP server %s."), account->recv_server);
896                 }               
897
898                 return;
899         } 
900
901         statusbar_pop_all();
902         session->authenticated = TRUE;
903 }
904
905 static void imap_session_destroy(Session *session)
906 {
907         if (session->state != SESSION_DISCONNECTED)
908                 imap_threaded_disconnect(IMAP_SESSION(session)->folder);
909         
910         imap_free_capabilities(IMAP_SESSION(session));
911         g_free(IMAP_SESSION(session)->mbox);
912         sock_close(session->sock);
913         session->sock = NULL;
914 }
915
916 static gchar *imap_fetch_msg(Folder *folder, FolderItem *item, gint uid)
917 {
918         return imap_fetch_msg_full(folder, item, uid, TRUE, TRUE);
919 }
920
921 static guint get_size_with_crs(MsgInfo *info) 
922 {
923         FILE *fp = NULL;
924         guint cnt = 0;
925         gchar buf[4096];
926         
927         if (info == NULL)
928                 return -1;
929         
930         fp = procmsg_open_message(info);
931         if (!fp)
932                 return -1;
933         
934         while (fgets(buf, sizeof (buf), fp) != NULL) {
935                 cnt += strlen(buf);
936                 if (!strstr(buf, "\r") && strstr(buf, "\n"))
937                         cnt++;
938         }
939         
940         fclose(fp);
941         return cnt;
942 }
943
944 static gchar *imap_fetch_msg_full(Folder *folder, FolderItem *item, gint uid,
945                                   gboolean headers, gboolean body)
946 {
947         gchar *path, *filename;
948         IMAPSession *session;
949         gint ok;
950
951         g_return_val_if_fail(folder != NULL, NULL);
952         g_return_val_if_fail(item != NULL, NULL);
953
954         if (uid == 0)
955                 return NULL;
956
957         path = folder_item_get_path(item);
958         if (!is_dir_exist(path))
959                 make_dir_hier(path);
960         filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(uid), NULL);
961         g_free(path);
962         debug_print("trying to fetch cached %s\n", filename);
963         if (is_file_exist(filename)) {
964                 /* see whether the local file represents the whole message
965                  * or not. As the IMAP server reports size with \r chars,
966                  * we have to update the local file (UNIX \n only) size */
967                 MsgInfo *msginfo = imap_parse_msg(filename, item);
968                 MsgInfo *cached = msgcache_get_msg(item->cache,uid);
969                 guint have_size = get_size_with_crs(msginfo);
970
971                 if (cached)
972                         debug_print("message %d has been already %scached (%d/%d).\n", uid,
973                                 have_size >= cached->size ? "fully ":"",
974                                 have_size, (int)cached->size);
975                 
976                 if (cached && (cached->size <= have_size || !body)) {
977                         procmsg_msginfo_free(cached);
978                         procmsg_msginfo_free(msginfo);
979                         file_strip_crs(filename);
980                         return filename;
981                 } else if (!cached) {
982                         debug_print("message not cached, considering file complete\n");
983                         procmsg_msginfo_free(msginfo);
984                         file_strip_crs(filename);
985                         return filename;
986                 } else {
987                         procmsg_msginfo_free(cached);
988                         procmsg_msginfo_free(msginfo);
989                 }
990         }
991
992         session = imap_session_get(folder);
993         
994         if (!session) {
995                 g_free(filename);
996                 return NULL;
997         }
998
999         lock_session();
1000
1001         debug_print("IMAP fetching messages\n");
1002         ok = imap_select(session, IMAP_FOLDER(folder), item->path,
1003                          NULL, NULL, NULL, NULL, FALSE);
1004         if (ok != IMAP_SUCCESS) {
1005                 g_warning("can't select mailbox %s\n", item->path);
1006                 g_free(filename);
1007                 unlock_session();
1008                 return NULL;
1009         }
1010
1011         debug_print("getting message %d...\n", uid);
1012         ok = imap_cmd_fetch(session, (guint32)uid, filename, headers, body);
1013
1014         if (ok != IMAP_SUCCESS) {
1015                 g_warning("can't fetch message %d\n", uid);
1016                 g_free(filename);
1017                 unlock_session();
1018                 return NULL;
1019         }
1020
1021         unlock_session();
1022         file_strip_crs(filename);
1023         return filename;
1024 }
1025
1026 static gint imap_add_msg(Folder *folder, FolderItem *dest, 
1027                          const gchar *file, MsgFlags *flags)
1028 {
1029         gint ret;
1030         GSList file_list;
1031         MsgFileInfo fileinfo;
1032
1033         g_return_val_if_fail(file != NULL, -1);
1034
1035         fileinfo.msginfo = NULL;
1036         fileinfo.file = (gchar *)file;
1037         fileinfo.flags = flags;
1038         file_list.data = &fileinfo;
1039         file_list.next = NULL;
1040
1041         ret = imap_add_msgs(folder, dest, &file_list, NULL);
1042         return ret;
1043 }
1044
1045 static gint imap_add_msgs(Folder *folder, FolderItem *dest, GSList *file_list,
1046                    GRelation *relation)
1047 {
1048         gchar *destdir;
1049         IMAPSession *session;
1050         guint32 last_uid = 0;
1051         GSList *cur;
1052         MsgFileInfo *fileinfo;
1053         gint ok;
1054         gint curnum = 0, total = 0;
1055
1056
1057         g_return_val_if_fail(folder != NULL, -1);
1058         g_return_val_if_fail(dest != NULL, -1);
1059         g_return_val_if_fail(file_list != NULL, -1);
1060         
1061         session = imap_session_get(folder);
1062         if (!session) {
1063                 return -1;
1064         }
1065         lock_session();
1066         destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
1067
1068         statusbar_print_all(_("Adding messages..."));
1069         total = g_slist_length(file_list);
1070         for (cur = file_list; cur != NULL; cur = cur->next) {
1071                 IMAPFlags iflags = 0;
1072                 guint32 new_uid = 0;
1073                 gchar *real_file = NULL;
1074                 fileinfo = (MsgFileInfo *)cur->data;
1075
1076                 statusbar_progress_all(curnum, total, 1);
1077                 curnum++;
1078
1079                 if (fileinfo->flags) {
1080                         if (MSG_IS_MARKED(*fileinfo->flags))
1081                                 iflags |= IMAP_FLAG_FLAGGED;
1082                         if (MSG_IS_REPLIED(*fileinfo->flags))
1083                                 iflags |= IMAP_FLAG_ANSWERED;
1084                         if (!MSG_IS_UNREAD(*fileinfo->flags))
1085                                 iflags |= IMAP_FLAG_SEEN;
1086                 }
1087                 
1088                 if (real_file == NULL)
1089                         real_file = g_strdup(fileinfo->file);
1090                 
1091                 if (folder_has_parent_of_type(dest, F_QUEUE) ||
1092                     folder_has_parent_of_type(dest, F_OUTBOX) ||
1093                     folder_has_parent_of_type(dest, F_DRAFT) ||
1094                     folder_has_parent_of_type(dest, F_TRASH))
1095                         iflags |= IMAP_FLAG_SEEN;
1096
1097                 ok = imap_cmd_append(session, destdir, real_file, iflags, 
1098                                      &new_uid);
1099
1100                 if (ok != IMAP_SUCCESS) {
1101                         g_warning("can't append message %s\n", real_file);
1102                         g_free(real_file);
1103                         g_free(destdir);
1104                         unlock_session();
1105                         statusbar_progress_all(0,0,0);
1106                         statusbar_pop_all();
1107                         return -1;
1108                 } else {
1109                         debug_print("appended new message as %d\n", new_uid);
1110                         /* put the local file in the imapcache, so that we don't
1111                          * have to fetch it back later. */
1112                         if (new_uid > 0) {
1113                                 gchar *cache_path = folder_item_get_path(dest);
1114                                 if (!is_dir_exist(cache_path))
1115                                         make_dir_hier(cache_path);
1116                                 if (is_dir_exist(cache_path)) {
1117                                         gchar *cache_file = g_strconcat(
1118                                                 cache_path, G_DIR_SEPARATOR_S, 
1119                                                 itos(new_uid), NULL);
1120                                         copy_file(real_file, cache_file, TRUE);
1121                                         debug_print("copied to cache: %s\n", cache_file);
1122                                         g_free(cache_file);
1123                                 }
1124                                 g_free(cache_path);
1125                         }
1126                 }
1127
1128                 if (relation != NULL)
1129                         g_relation_insert(relation, fileinfo->msginfo != NULL ? 
1130                                           (gpointer) fileinfo->msginfo : (gpointer) fileinfo,
1131                                           GINT_TO_POINTER(dest->last_num + 1));
1132                 if (new_uid == 0) {
1133                         new_uid = dest->last_num+1;
1134                 }
1135                 if (last_uid < new_uid) {
1136                         last_uid = new_uid;
1137                 }
1138
1139                 g_free(real_file);
1140         }
1141         statusbar_progress_all(0,0,0);
1142         statusbar_pop_all();
1143         
1144         imap_cmd_expunge(session);
1145         unlock_session();
1146         
1147         g_free(destdir);
1148
1149         return last_uid;
1150 }
1151
1152 static gint imap_do_copy_msgs(Folder *folder, FolderItem *dest, 
1153                               MsgInfoList *msglist, GRelation *relation)
1154 {
1155         FolderItem *src;
1156         gchar *destdir;
1157         GSList *seq_list, *cur;
1158         MsgInfo *msginfo;
1159         IMAPSession *session;
1160         gint ok = IMAP_SUCCESS;
1161         GRelation *uid_mapping;
1162         gint last_num = 0;
1163
1164         g_return_val_if_fail(folder != NULL, -1);
1165         g_return_val_if_fail(dest != NULL, -1);
1166         g_return_val_if_fail(msglist != NULL, -1);
1167         
1168         session = imap_session_get(folder);
1169         
1170         if (!session) {
1171                 return -1;
1172         }
1173         lock_session();
1174         msginfo = (MsgInfo *)msglist->data;
1175
1176         src = msginfo->folder;
1177         if (src == dest) {
1178                 g_warning("the src folder is identical to the dest.\n");
1179                 unlock_session();
1180                 return -1;
1181         }
1182
1183         if (src->folder != dest->folder) {
1184                 GSList *infolist = NULL, *cur;
1185                 int res = -1;
1186                 for (cur = msglist; cur; cur = cur->next) {
1187                         msginfo = (MsgInfo *)cur->data;
1188                         MsgFileInfo *fileinfo = g_new0(MsgFileInfo, 1);
1189                         fileinfo->file = procmsg_get_message_file(msginfo);
1190                         fileinfo->flags = &(msginfo->flags);
1191                         infolist = g_slist_prepend(infolist, fileinfo);
1192                 }
1193                 infolist = g_slist_reverse(infolist);
1194                 res = folder_item_add_msgs(dest, infolist, FALSE);
1195                 for (cur = infolist; cur; cur = cur->next) {
1196                         MsgFileInfo *info = (MsgFileInfo *)cur->data;
1197                         g_free(info->file);
1198                         g_free(info);
1199                 }
1200                 g_slist_free(infolist);
1201                 return res;
1202         } 
1203
1204         ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
1205                          NULL, NULL, NULL, NULL, FALSE);
1206         if (ok != IMAP_SUCCESS) {
1207                 unlock_session();
1208                 return ok;
1209         }
1210
1211         destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
1212         seq_list = imap_get_lep_set_from_msglist(msglist);
1213         uid_mapping = g_relation_new(2);
1214         g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
1215         
1216         statusbar_print_all(_("Copying messages..."));
1217         for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
1218                 struct mailimap_set * seq_set;
1219                 seq_set = cur->data;
1220
1221                 debug_print("Copying messages from %s to %s ...\n",
1222                             src->path, destdir);
1223
1224                 ok = imap_cmd_copy(session, seq_set, destdir, uid_mapping);
1225                 if (ok != IMAP_SUCCESS) {
1226                         g_relation_destroy(uid_mapping);
1227                         imap_lep_set_free(seq_list);
1228                         unlock_session();
1229                         return -1;
1230                 }
1231         }
1232
1233         for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
1234                 MsgInfo *msginfo = (MsgInfo *)cur->data;
1235                 GTuples *tuples;
1236
1237                 tuples = g_relation_select(uid_mapping, 
1238                                            GINT_TO_POINTER(msginfo->msgnum),
1239                                            0);
1240                 if (tuples->len > 0) {
1241                         gint num = GPOINTER_TO_INT(g_tuples_index(tuples, 0, 1));
1242                         g_relation_insert(relation, msginfo,
1243                                           GPOINTER_TO_INT(num));
1244                         if (num > last_num)
1245                                 last_num = num;
1246                 } else
1247                         g_relation_insert(relation, msginfo,
1248                                           GPOINTER_TO_INT(0));
1249                 g_tuples_destroy(tuples);
1250         }
1251         statusbar_pop_all();
1252
1253         g_relation_destroy(uid_mapping);
1254         imap_lep_set_free(seq_list);
1255
1256         g_free(destdir);
1257         
1258         IMAP_FOLDER_ITEM(dest)->lastuid = 0;
1259         IMAP_FOLDER_ITEM(dest)->uid_next = 0;
1260         g_slist_free(IMAP_FOLDER_ITEM(dest)->uid_list);
1261         IMAP_FOLDER_ITEM(dest)->uid_list = NULL;
1262
1263         unlock_session();
1264         if (ok == IMAP_SUCCESS)
1265                 return last_num;
1266         else
1267                 return -1;
1268 }
1269
1270 static gint imap_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1271 {
1272         GSList msglist;
1273
1274         g_return_val_if_fail(msginfo != NULL, -1);
1275
1276         msglist.data = msginfo;
1277         msglist.next = NULL;
1278
1279         return imap_copy_msgs(folder, dest, &msglist, NULL);
1280 }
1281
1282 static gint imap_copy_msgs(Folder *folder, FolderItem *dest, 
1283                     MsgInfoList *msglist, GRelation *relation)
1284 {
1285         MsgInfo *msginfo;
1286         gint ret;
1287
1288         g_return_val_if_fail(folder != NULL, -1);
1289         g_return_val_if_fail(dest != NULL, -1);
1290         g_return_val_if_fail(msglist != NULL, -1);
1291
1292         msginfo = (MsgInfo *)msglist->data;
1293         g_return_val_if_fail(msginfo->folder != NULL, -1);
1294
1295         ret = imap_do_copy_msgs(folder, dest, msglist, relation);
1296         return ret;
1297 }
1298
1299
1300 static gint imap_do_remove_msgs(Folder *folder, FolderItem *dest, 
1301                                 MsgInfoList *msglist, GRelation *relation)
1302 {
1303         gchar *destdir, *dir;
1304         GSList *numlist = NULL, *cur;
1305         MsgInfo *msginfo;
1306         IMAPSession *session;
1307         gint ok = IMAP_SUCCESS;
1308         GRelation *uid_mapping;
1309         
1310         g_return_val_if_fail(folder != NULL, -1);
1311         g_return_val_if_fail(dest != NULL, -1);
1312         g_return_val_if_fail(msglist != NULL, -1);
1313
1314         session = imap_session_get(folder);
1315         if (!session) {
1316                 return -1;
1317         }
1318         lock_session();
1319         msginfo = (MsgInfo *)msglist->data;
1320
1321         ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
1322                          NULL, NULL, NULL, NULL, FALSE);
1323         if (ok != IMAP_SUCCESS) {
1324                 unlock_session();
1325                 return ok;
1326         }
1327
1328         destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
1329         for (cur = msglist; cur; cur = cur->next) {
1330                 msginfo = (MsgInfo *)cur->data;
1331                 if (!MSG_IS_DELETED(msginfo->flags))
1332                         numlist = g_slist_prepend(numlist, GINT_TO_POINTER(msginfo->msgnum));
1333         }
1334         numlist = g_slist_reverse(numlist);
1335
1336         uid_mapping = g_relation_new(2);
1337         g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
1338
1339         ok = imap_set_message_flags
1340                 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
1341                 numlist, IMAP_FLAG_DELETED, TRUE);
1342         if (ok != IMAP_SUCCESS) {
1343                 log_warning(_("can't set deleted flags\n"));
1344                 unlock_session();
1345                 return ok;
1346         }
1347         ok = imap_cmd_expunge(session);
1348         if (ok != IMAP_SUCCESS) {
1349                 log_warning(_("can't expunge\n"));
1350                 unlock_session();
1351                 return ok;
1352         }
1353         
1354         dir = folder_item_get_path(msginfo->folder);
1355         if (is_dir_exist(dir)) {
1356                 for (cur = msglist; cur; cur = cur->next) {
1357                         msginfo = (MsgInfo *)cur->data;
1358                         remove_numbered_files(dir, msginfo->msgnum, msginfo->msgnum);
1359                 }
1360         }
1361         g_free(dir);
1362
1363         g_relation_destroy(uid_mapping);
1364         g_slist_free(numlist);
1365
1366         g_free(destdir);
1367         unlock_session();
1368         if (ok == IMAP_SUCCESS)
1369                 return 0;
1370         else
1371                 return -1;
1372 }
1373
1374 static gint imap_remove_msgs(Folder *folder, FolderItem *dest, 
1375                     MsgInfoList *msglist, GRelation *relation)
1376 {
1377         MsgInfo *msginfo;
1378
1379         g_return_val_if_fail(folder != NULL, -1);
1380         g_return_val_if_fail(dest != NULL, -1);
1381         if (msglist == NULL)
1382                 return 0;
1383
1384         msginfo = (MsgInfo *)msglist->data;
1385         g_return_val_if_fail(msginfo->folder != NULL, -1);
1386
1387         return imap_do_remove_msgs(folder, dest, msglist, relation);
1388 }
1389
1390 static gint imap_remove_all_msg(Folder *folder, FolderItem *item)
1391 {
1392         GSList *list = folder_item_get_msg_list(item);
1393         gint res = imap_remove_msgs(folder, item, list, NULL);
1394         procmsg_msg_list_free(list);
1395         return res;
1396 }
1397
1398 static gboolean imap_is_msg_changed(Folder *folder, FolderItem *item,
1399                                     MsgInfo *msginfo)
1400 {
1401         /* TODO: properly implement this method */
1402         return FALSE;
1403 }
1404
1405 static gint imap_close(Folder *folder, FolderItem *item)
1406 {
1407         return 0;
1408 }
1409
1410 static gint imap_scan_tree(Folder *folder)
1411 {
1412         FolderItem *item = NULL;
1413         IMAPSession *session;
1414         gchar *root_folder = NULL;
1415
1416         g_return_val_if_fail(folder != NULL, -1);
1417         g_return_val_if_fail(folder->account != NULL, -1);
1418
1419         session = imap_session_get(folder);
1420         if (!session) {
1421                 if (!folder->node) {
1422                         folder_tree_destroy(folder);
1423                         item = folder_item_new(folder, folder->name, NULL);
1424                         item->folder = folder;
1425                         folder->node = item->node = g_node_new(item);
1426                 }
1427                 return -1;
1428         }
1429
1430         lock_session();
1431         if (folder->account->imap_dir && *folder->account->imap_dir) {
1432                 gchar *real_path;
1433                 int r;
1434                 clist * lep_list;
1435
1436                 Xstrdup_a(root_folder, folder->account->imap_dir, {unlock_session();return -1;});
1437                 extract_quote(root_folder, '"');
1438                 subst_char(root_folder,
1439                            imap_get_path_separator(IMAP_FOLDER(folder),
1440                                                    root_folder),
1441                            '/');
1442                 strtailchomp(root_folder, '/');
1443                 real_path = imap_get_real_path
1444                         (IMAP_FOLDER(folder), root_folder);
1445                 debug_print("IMAP root directory: %s\n", real_path);
1446
1447                 /* check if root directory exist */
1448
1449                 r = imap_threaded_list(session->folder, "", real_path,
1450                                        &lep_list);
1451                 if ((r != MAILIMAP_NO_ERROR) || (clist_count(lep_list) == 0)) {
1452                         if (!folder->node) {
1453                                 item = folder_item_new(folder, folder->name, NULL);
1454                                 item->folder = folder;
1455                                 folder->node = item->node = g_node_new(item);
1456                         }
1457                         unlock_session();
1458                         return -1;
1459                 }
1460                 mailimap_list_result_free(lep_list);
1461                 
1462                 g_free(real_path);
1463         }
1464
1465         if (folder->node)
1466                 item = FOLDER_ITEM(folder->node->data);
1467         if (!item || ((item->path || root_folder) &&
1468                       strcmp2(item->path, root_folder) != 0)) {
1469                 folder_tree_destroy(folder);
1470                 item = folder_item_new(folder, folder->name, root_folder);
1471                 item->folder = folder;
1472                 folder->node = item->node = g_node_new(item);
1473         }
1474
1475         imap_scan_tree_recursive(session, FOLDER_ITEM(folder->node->data));
1476         imap_create_missing_folders(folder);
1477         unlock_session();
1478
1479         return 0;
1480 }
1481
1482 static gint imap_scan_tree_recursive(IMAPSession *session, FolderItem *item)
1483 {
1484         Folder *folder;
1485         IMAPFolder *imapfolder;
1486         FolderItem *new_item;
1487         GSList *item_list, *cur;
1488         GNode *node;
1489         gchar *real_path;
1490         gchar *wildcard_path;
1491         gchar separator;
1492         gchar wildcard[3];
1493         clist * lep_list;
1494         int r;
1495         
1496         g_return_val_if_fail(item != NULL, -1);
1497         g_return_val_if_fail(item->folder != NULL, -1);
1498         g_return_val_if_fail(item->no_sub == FALSE, -1);
1499
1500         folder = item->folder;
1501         imapfolder = IMAP_FOLDER(folder);
1502
1503         separator = imap_get_path_separator(imapfolder, item->path);
1504
1505         if (folder->ui_func)
1506                 folder->ui_func(folder, item, folder->ui_func_data);
1507
1508         if (item->path) {
1509                 wildcard[0] = separator;
1510                 wildcard[1] = '%';
1511                 wildcard[2] = '\0';
1512                 real_path = imap_get_real_path(imapfolder, item->path);
1513         } else {
1514                 wildcard[0] = '%';
1515                 wildcard[1] = '\0';
1516                 real_path = g_strdup("");
1517         }
1518
1519         Xstrcat_a(wildcard_path, real_path, wildcard,
1520                   {g_free(real_path); return IMAP_ERROR;});
1521         lep_list = NULL;
1522         r = imap_threaded_list(folder, "", wildcard_path, &lep_list);
1523         if (r != MAILIMAP_NO_ERROR) {
1524                 item_list = NULL;
1525         }
1526         else {
1527                 item_list = imap_list_from_lep(imapfolder,
1528                                                lep_list, real_path, FALSE);
1529                 mailimap_list_result_free(lep_list);
1530         }
1531         
1532         g_free(real_path);
1533
1534         node = item->node->children;
1535         while (node != NULL) {
1536                 FolderItem *old_item = FOLDER_ITEM(node->data);
1537                 GNode *next = node->next;
1538
1539                 new_item = NULL;
1540                 for (cur = item_list; cur != NULL; cur = cur->next) {
1541                         FolderItem *cur_item = FOLDER_ITEM(cur->data);
1542                         if (!strcmp2(old_item->path, cur_item->path)) {
1543                                 new_item = cur_item;
1544                                 break;
1545                         }
1546                 }
1547                 if (!new_item) {
1548                         debug_print("folder '%s' not found. removing...\n",
1549                                     old_item->path);
1550                         folder_item_remove(old_item);
1551                 } else {
1552                         old_item->no_sub = new_item->no_sub;
1553                         old_item->no_select = new_item->no_select;
1554                         if (old_item->no_sub == TRUE && node->children) {
1555                                 debug_print("folder '%s' doesn't have "
1556                                             "subfolders. removing...\n",
1557                                             old_item->path);
1558                                 folder_item_remove_children(old_item);
1559                         }
1560                 }
1561
1562                 node = next;
1563         }
1564
1565         for (cur = item_list; cur != NULL; cur = cur->next) {
1566                 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1567                 new_item = NULL;
1568
1569                 for (node = item->node->children; node != NULL;
1570                      node = node->next) {
1571                         if (!strcmp2(FOLDER_ITEM(node->data)->path,
1572                                      cur_item->path)) {
1573                                 new_item = FOLDER_ITEM(node->data);
1574                                 folder_item_destroy(cur_item);
1575                                 cur_item = NULL;
1576                                 break;
1577                         }
1578                 }
1579                 if (!new_item) {
1580                         new_item = cur_item;
1581                         debug_print("new folder '%s' found.\n", new_item->path);
1582                         folder_item_append(item, new_item);
1583                 }
1584
1585                 if (!strcmp(new_item->path, "INBOX")) {
1586                         new_item->stype = F_INBOX;
1587                         folder->inbox = new_item;
1588                 } else if (!folder_item_parent(item) || item->stype == F_INBOX) {
1589                         gchar *base;
1590
1591                         base = g_path_get_basename(new_item->path);
1592
1593                         if (!folder->outbox && !g_ascii_strcasecmp(base, "Sent")) {
1594                                 new_item->stype = F_OUTBOX;
1595                                 folder->outbox = new_item;
1596                         } else if (!folder->draft && !g_ascii_strcasecmp(base, "Drafts")) {
1597                                 new_item->stype = F_DRAFT;
1598                                 folder->draft = new_item;
1599                         } else if (!folder->queue && !g_ascii_strcasecmp(base, "Queue")) {
1600                                 new_item->stype = F_QUEUE;
1601                                 folder->queue = new_item;
1602                         } else if (!folder->trash && !g_ascii_strcasecmp(base, "Trash")) {
1603                                 new_item->stype = F_TRASH;
1604                                 folder->trash = new_item;
1605                         }
1606                         g_free(base);
1607                 }
1608
1609                 if (new_item->no_sub == FALSE)
1610                         imap_scan_tree_recursive(session, new_item);
1611         }
1612
1613         g_slist_free(item_list);
1614
1615         return IMAP_SUCCESS;
1616 }
1617
1618 static gint imap_create_tree(Folder *folder)
1619 {
1620         g_return_val_if_fail(folder != NULL, -1);
1621         g_return_val_if_fail(folder->node != NULL, -1);
1622         g_return_val_if_fail(folder->node->data != NULL, -1);
1623         g_return_val_if_fail(folder->account != NULL, -1);
1624
1625         imap_scan_tree(folder);
1626         imap_create_missing_folders(folder);
1627
1628         return 0;
1629 }
1630
1631 static void imap_create_missing_folders(Folder *folder)
1632 {
1633         g_return_if_fail(folder != NULL);
1634
1635         if (!folder->inbox)
1636                 folder->inbox = imap_create_special_folder
1637                         (folder, F_INBOX, "INBOX");
1638         if (!folder->trash)
1639                 folder->trash = imap_create_special_folder
1640                         (folder, F_TRASH, "Trash");
1641         if (!folder->queue)
1642                 folder->queue = imap_create_special_folder
1643                         (folder, F_QUEUE, "Queue");
1644         if (!folder->outbox)
1645                 folder->outbox = imap_create_special_folder
1646                         (folder, F_OUTBOX, "Sent");
1647         if (!folder->draft)
1648                 folder->draft = imap_create_special_folder
1649                         (folder, F_DRAFT, "Drafts");
1650 }
1651
1652 static FolderItem *imap_create_special_folder(Folder *folder,
1653                                               SpecialFolderItemType stype,
1654                                               const gchar *name)
1655 {
1656         FolderItem *item;
1657         FolderItem *new_item;
1658
1659         g_return_val_if_fail(folder != NULL, NULL);
1660         g_return_val_if_fail(folder->node != NULL, NULL);
1661         g_return_val_if_fail(folder->node->data != NULL, NULL);
1662         g_return_val_if_fail(folder->account != NULL, NULL);
1663         g_return_val_if_fail(name != NULL, NULL);
1664
1665         item = FOLDER_ITEM(folder->node->data);
1666         new_item = imap_create_folder(folder, item, name);
1667
1668         if (!new_item) {
1669                 g_warning("Can't create '%s'\n", name);
1670                 if (!folder->inbox) return NULL;
1671
1672                 new_item = imap_create_folder(folder, folder->inbox, name);
1673                 if (!new_item)
1674                         g_warning("Can't create '%s' under INBOX\n", name);
1675                 else
1676                         new_item->stype = stype;
1677         } else
1678                 new_item->stype = stype;
1679
1680         return new_item;
1681 }
1682
1683 static gchar *imap_folder_get_path(Folder *folder)
1684 {
1685         gchar *folder_path;
1686
1687         g_return_val_if_fail(folder != NULL, NULL);
1688         g_return_val_if_fail(folder->account != NULL, NULL);
1689
1690         folder_path = g_strconcat(get_imap_cache_dir(),
1691                                   G_DIR_SEPARATOR_S,
1692                                   folder->account->recv_server,
1693                                   G_DIR_SEPARATOR_S,
1694                                   folder->account->userid,
1695                                   NULL);
1696
1697         return folder_path;
1698 }
1699
1700 static gchar *imap_item_get_path(Folder *folder, FolderItem *item)
1701 {
1702         gchar *folder_path, *path;
1703
1704         g_return_val_if_fail(folder != NULL, NULL);
1705         g_return_val_if_fail(item != NULL, NULL);
1706         folder_path = imap_folder_get_path(folder);
1707
1708         g_return_val_if_fail(folder_path != NULL, NULL);
1709         if (folder_path[0] == G_DIR_SEPARATOR) {
1710                 if (item->path)
1711                         path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
1712                                            item->path, NULL);
1713                 else
1714                         path = g_strdup(folder_path);
1715         } else {
1716                 if (item->path)
1717                         path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1718                                            folder_path, G_DIR_SEPARATOR_S,
1719                                            item->path, NULL);
1720                 else
1721                         path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1722                                            folder_path, NULL);
1723         }
1724         g_free(folder_path);
1725
1726         return path;
1727 }
1728
1729 static FolderItem *imap_create_folder(Folder *folder, FolderItem *parent,
1730                                const gchar *name)
1731 {
1732         gchar *dirpath, *imap_path;
1733         IMAPSession *session;
1734         FolderItem *new_item;
1735         gchar separator;
1736         gchar *new_name;
1737         const gchar *p;
1738         gint ok;
1739         gboolean no_select = FALSE, no_sub = FALSE;
1740         
1741         g_return_val_if_fail(folder != NULL, NULL);
1742         g_return_val_if_fail(folder->account != NULL, NULL);
1743         g_return_val_if_fail(parent != NULL, NULL);
1744         g_return_val_if_fail(name != NULL, NULL);
1745
1746         session = imap_session_get(folder);
1747         if (!session) {
1748                 return NULL;
1749         }
1750
1751         lock_session();
1752         if (!folder_item_parent(parent) && strcmp(name, "INBOX") == 0) {
1753                 dirpath = g_strdup(name);
1754         }else if (parent->path)
1755                 dirpath = g_strconcat(parent->path, "/", name, NULL);
1756         else if ((p = strchr(name, '/')) != NULL && *(p + 1) != '\0')
1757                 dirpath = g_strdup(name);
1758         else if (folder->account->imap_dir && *folder->account->imap_dir) {
1759                 gchar *imap_dir;
1760
1761                 Xstrdup_a(imap_dir, folder->account->imap_dir, {unlock_session();return NULL;});
1762                 strtailchomp(imap_dir, '/');
1763                 dirpath = g_strconcat(imap_dir, "/", name, NULL);
1764         } else
1765                 dirpath = g_strdup(name);
1766                 
1767         
1768
1769         /* keep trailing directory separator to create a folder that contains
1770            sub folder */
1771         imap_path = imap_utf8_to_modified_utf7(dirpath);
1772
1773         strtailchomp(dirpath, '/');
1774         Xstrdup_a(new_name, name, {
1775                 g_free(dirpath); 
1776                 unlock_session();               
1777                 return NULL;});
1778
1779         separator = imap_get_path_separator(IMAP_FOLDER(folder), imap_path);
1780         imap_path_separator_subst(imap_path, separator);
1781         /* remove trailing / for display */
1782         strtailchomp(new_name, '/');
1783
1784         if (strcmp(dirpath, "INBOX") != 0) {
1785                 GPtrArray *argbuf;
1786                 gboolean exist = FALSE;
1787                 int r;
1788                 clist * lep_list;
1789                 
1790                 argbuf = g_ptr_array_new();
1791                 r = imap_threaded_list(folder, "", imap_path, &lep_list);
1792                 if (r != MAILIMAP_NO_ERROR) {
1793                         log_warning(_("can't create mailbox: LIST failed\n"));
1794                         g_free(imap_path);
1795                         g_free(dirpath);
1796                         ptr_array_free_strings(argbuf);
1797                         g_ptr_array_free(argbuf, TRUE);
1798                         unlock_session();
1799                         return NULL;
1800                 }
1801                 
1802                 if (clist_count(lep_list) > 0)
1803                         exist = TRUE;
1804                 mailimap_list_result_free(lep_list);
1805                 lep_list = NULL;
1806                 if (!exist) {
1807                         ok = imap_cmd_create(session, imap_path);
1808                         if (ok != IMAP_SUCCESS) {
1809                                 log_warning(_("can't create mailbox\n"));
1810                                 g_free(imap_path);
1811                                 g_free(dirpath);
1812                                 unlock_session();
1813                                 return NULL;
1814                         }
1815                         r = imap_threaded_list(folder, "", imap_path, &lep_list);
1816                         if (r == MAILIMAP_NO_ERROR) {
1817                                 GSList *item_list = imap_list_from_lep(IMAP_FOLDER(folder),
1818                                                lep_list, dirpath, TRUE);
1819                                 if (item_list) {
1820                                         FolderItem *cur_item = FOLDER_ITEM(item_list->data);
1821                                         no_select = cur_item->no_select;
1822                                         no_sub = cur_item->no_sub;
1823                                         g_slist_free(item_list);
1824                                 } 
1825                                 mailimap_list_result_free(lep_list);
1826                         }
1827
1828                 }
1829         } else {
1830                 clist *lep_list;
1831                 int r;
1832                 /* just get flags */
1833                 r = imap_threaded_list(folder, "", "INBOX", &lep_list);
1834                 if (r == MAILIMAP_NO_ERROR) {
1835                         GSList *item_list = imap_list_from_lep(IMAP_FOLDER(folder),
1836                                        lep_list, dirpath, TRUE);
1837                         if (item_list) {
1838                                 FolderItem *cur_item = FOLDER_ITEM(item_list->data);
1839                                 no_select = cur_item->no_select;
1840                                 no_sub = cur_item->no_sub;
1841                                 g_slist_free(item_list);
1842                         } 
1843                         mailimap_list_result_free(lep_list);
1844                 }
1845         }
1846
1847         new_item = folder_item_new(folder, new_name, dirpath);
1848         new_item->no_select = no_select;
1849         new_item->no_sub = no_sub;
1850         folder_item_append(parent, new_item);
1851         g_free(imap_path);
1852         g_free(dirpath);
1853
1854         dirpath = folder_item_get_path(new_item);
1855         if (!is_dir_exist(dirpath))
1856                 make_dir_hier(dirpath);
1857         g_free(dirpath);
1858         unlock_session();
1859         return new_item;
1860 }
1861
1862 static gint imap_rename_folder(Folder *folder, FolderItem *item,
1863                                const gchar *name)
1864 {
1865         gchar *dirpath;
1866         gchar *newpath;
1867         gchar *real_oldpath;
1868         gchar *real_newpath;
1869         gchar *paths[2];
1870         gchar *old_cache_dir;
1871         gchar *new_cache_dir;
1872         IMAPSession *session;
1873         gchar separator;
1874         gint ok;
1875         gint exists, recent, unseen;
1876         guint32 uid_validity;
1877
1878         g_return_val_if_fail(folder != NULL, -1);
1879         g_return_val_if_fail(item != NULL, -1);
1880         g_return_val_if_fail(item->path != NULL, -1);
1881         g_return_val_if_fail(name != NULL, -1);
1882
1883         session = imap_session_get(folder);
1884         if (!session) {
1885                 return -1;
1886         }
1887         lock_session();
1888
1889         if (strchr(name, imap_get_path_separator(IMAP_FOLDER(folder), item->path)) != NULL) {
1890                 g_warning(_("New folder name must not contain the namespace "
1891                             "path separator"));
1892                 unlock_session();
1893                 return -1;
1894         }
1895
1896         real_oldpath = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1897
1898         g_free(session->mbox);
1899         session->mbox = NULL;
1900         ok = imap_cmd_examine(session, "INBOX",
1901                               &exists, &recent, &unseen, &uid_validity, FALSE);
1902         if (ok != IMAP_SUCCESS) {
1903                 g_free(real_oldpath);
1904                 unlock_session();
1905                 return -1;
1906         }
1907
1908         separator = imap_get_path_separator(IMAP_FOLDER(folder), item->path);
1909         if (strchr(item->path, G_DIR_SEPARATOR)) {
1910                 dirpath = g_path_get_dirname(item->path);
1911                 newpath = g_strconcat(dirpath, G_DIR_SEPARATOR_S, name, NULL);
1912                 g_free(dirpath);
1913         } else
1914                 newpath = g_strdup(name);
1915
1916         real_newpath = imap_utf8_to_modified_utf7(newpath);
1917         imap_path_separator_subst(real_newpath, separator);
1918
1919         ok = imap_cmd_rename(session, real_oldpath, real_newpath);
1920         if (ok != IMAP_SUCCESS) {
1921                 log_warning(_("can't rename mailbox: %s to %s\n"),
1922                             real_oldpath, real_newpath);
1923                 g_free(real_oldpath);
1924                 g_free(newpath);
1925                 g_free(real_newpath);
1926                 unlock_session();
1927                 return -1;
1928         }
1929
1930         g_free(item->name);
1931         item->name = g_strdup(name);
1932
1933         old_cache_dir = folder_item_get_path(item);
1934
1935         paths[0] = g_strdup(item->path);
1936         paths[1] = newpath;
1937         g_node_traverse(item->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1938                         imap_rename_folder_func, paths);
1939
1940         if (is_dir_exist(old_cache_dir)) {
1941                 new_cache_dir = folder_item_get_path(item);
1942                 if (rename(old_cache_dir, new_cache_dir) < 0) {
1943                         FILE_OP_ERROR(old_cache_dir, "rename");
1944                 }
1945                 g_free(new_cache_dir);
1946         }
1947
1948         g_free(old_cache_dir);
1949         g_free(paths[0]);
1950         g_free(newpath);
1951         g_free(real_oldpath);
1952         g_free(real_newpath);
1953         unlock_session();
1954         return 0;
1955 }
1956
1957 static gint imap_remove_folder_real(Folder *folder, FolderItem *item)
1958 {
1959         gint ok;
1960         IMAPSession *session;
1961         gchar *path;
1962         gchar *cache_dir;
1963
1964         g_return_val_if_fail(folder != NULL, -1);
1965         g_return_val_if_fail(item != NULL, -1);
1966         g_return_val_if_fail(item->path != NULL, -1);
1967
1968         session = imap_session_get(folder);
1969         if (!session) {
1970                 return -1;
1971         }
1972         lock_session();
1973         path = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1974
1975         ok = imap_cmd_delete(session, path);
1976         if (ok != IMAP_SUCCESS) {
1977                 gchar *tmp = g_strdup_printf("%s%c", path, 
1978                                 imap_get_path_separator(IMAP_FOLDER(folder), path));
1979                 g_free(path);
1980                 path = tmp;
1981                 ok = imap_cmd_delete(session, path);
1982         }
1983
1984         if (ok != IMAP_SUCCESS) {
1985                 log_warning(_("can't delete mailbox\n"));
1986                 g_free(path);
1987                 unlock_session();
1988                 return -1;
1989         }
1990
1991         g_free(path);
1992         cache_dir = folder_item_get_path(item);
1993         if (is_dir_exist(cache_dir) && remove_dir_recursive(cache_dir) < 0)
1994                 g_warning("can't remove directory '%s'\n", cache_dir);
1995         g_free(cache_dir);
1996         folder_item_remove(item);
1997         unlock_session();
1998         return 0;
1999 }
2000
2001 static gint imap_remove_folder(Folder *folder, FolderItem *item)
2002 {
2003         GNode *node, *next;
2004
2005         g_return_val_if_fail(item != NULL, -1);
2006         g_return_val_if_fail(item->folder != NULL, -1);
2007         g_return_val_if_fail(item->node != NULL, -1);
2008
2009         node = item->node->children;
2010         while (node != NULL) {
2011                 next = node->next;
2012                 if (imap_remove_folder(folder, FOLDER_ITEM(node->data)) < 0)
2013                         return -1;
2014                 node = next;
2015         }
2016         debug_print("IMAP removing %s\n", item->path);
2017
2018         if (imap_remove_all_msg(folder, item) < 0)
2019                 return -1;
2020         return imap_remove_folder_real(folder, item);
2021 }
2022
2023 typedef struct _uncached_data {
2024         IMAPSession *session;
2025         FolderItem *item;
2026         MsgNumberList *numlist;
2027         guint cur;
2028         guint total;
2029         gboolean done;
2030 } uncached_data;
2031
2032 static void *imap_get_uncached_messages_thread(void *data)
2033 {
2034         uncached_data *stuff = (uncached_data *)data;
2035         IMAPSession *session = stuff->session;
2036         FolderItem *item = stuff->item;
2037         MsgNumberList *numlist = stuff->numlist;
2038         
2039         GSList *newlist = NULL;
2040         GSList *llast = NULL;
2041         GSList *seq_list, *cur;
2042
2043         debug_print("uncached_messages\n");
2044         
2045         if (session == NULL || item == NULL || item->folder == NULL
2046             || FOLDER_CLASS(item->folder) != &imap_class) {
2047                 stuff->done = TRUE;
2048                 return NULL;
2049         }
2050         
2051         seq_list = imap_get_lep_set_from_numlist(numlist);
2052         debug_print("get msgs info\n");
2053         for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
2054                 struct mailimap_set * imapset;
2055                 unsigned int i;
2056                 int r;
2057                 carray * env_list;
2058                 int count;
2059                 
2060                 imapset = cur->data;
2061                 
2062                 r = imap_threaded_fetch_env(session->folder,
2063                                             imapset, &env_list);
2064                 if (r != MAILIMAP_NO_ERROR)
2065                         continue;
2066                 
2067                 session_set_access_time(SESSION(session));
2068
2069                 count = 0;
2070                 for(i = 0 ; i < carray_count(env_list) ; i ++) {
2071                         struct imap_fetch_env_info * info;
2072                         MsgInfo * msginfo;
2073                         
2074                         info = carray_get(env_list, i);
2075                         msginfo = imap_envelope_from_lep(info, item);
2076                         if (msginfo == NULL)
2077                                 continue;
2078                         msginfo->folder = item;
2079                         if (!newlist)
2080                                 llast = newlist = g_slist_append(newlist, msginfo);
2081                         else {
2082                                 llast = g_slist_append(llast, msginfo);
2083                                 llast = llast->next;
2084                         }
2085                         count ++;
2086                 }
2087                 
2088                 imap_fetch_env_free(env_list);
2089         }
2090         
2091         for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
2092                 struct mailimap_set * imapset;
2093                 
2094                 imapset = cur->data;
2095                 mailimap_set_free(imapset);
2096         }
2097         
2098         session_set_access_time(SESSION(session));
2099         stuff->done = TRUE;
2100         return newlist;
2101 }
2102
2103 #define MAX_MSG_NUM 50
2104
2105 static GSList *imap_get_uncached_messages(IMAPSession *session,
2106                                         FolderItem *item,
2107                                         MsgNumberList *numlist)
2108 {
2109         GSList *result = NULL;
2110         GSList * cur;
2111         uncached_data *data = g_new0(uncached_data, 1);
2112         int finished;
2113         
2114         finished = 0;
2115         cur = numlist;
2116         data->total = g_slist_length(numlist);
2117         debug_print("messages list : %i\n", data->total);
2118
2119         while (cur != NULL) {
2120                 GSList * partial_result;
2121                 int count;
2122                 GSList * newlist;
2123                 GSList * llast;
2124                 
2125                 llast = NULL;
2126                 count = 0;
2127                 newlist = NULL;
2128                 while (count < MAX_MSG_NUM) {
2129                         void * p;
2130                         
2131                         p = cur->data;
2132                         
2133                         if (newlist == NULL)
2134                                 llast = newlist = g_slist_append(newlist, p);
2135                         else {
2136                                 llast = g_slist_append(llast, p);
2137                                 llast = llast->next;
2138                         }
2139                         count ++;
2140                         
2141                         cur = cur->next;
2142                         if (cur == NULL)
2143                                 break;
2144                 }
2145                 
2146                 data->done = FALSE;
2147                 data->session = session;
2148                 data->item = item;
2149                 data->numlist = newlist;
2150                 data->cur += count;
2151                 
2152                 if (prefs_common.work_offline && 
2153                     !inc_offline_should_override(
2154                         _("Sylpheed-Claws needs network access in order "
2155                           "to access the IMAP server."))) {
2156                         g_free(data);
2157                         return NULL;
2158                 }
2159                 
2160                 partial_result =
2161                         (GSList *)imap_get_uncached_messages_thread(data);
2162                 
2163                 statusbar_progress_all(data->cur,data->total, 1);
2164                 
2165                 g_slist_free(newlist);
2166                 
2167                 result = g_slist_concat(result, partial_result);
2168         }
2169         g_free(data);
2170         
2171         statusbar_progress_all(0,0,0);
2172         statusbar_pop_all();
2173         
2174         return result;
2175 }
2176
2177 static void imap_delete_all_cached_messages(FolderItem *item)
2178 {
2179         gchar *dir;
2180
2181         g_return_if_fail(item != NULL);
2182         g_return_if_fail(item->folder != NULL);
2183         g_return_if_fail(FOLDER_CLASS(item->folder) == &imap_class);
2184
2185         debug_print("Deleting all cached messages...\n");
2186
2187         dir = folder_item_get_path(item);
2188         if (is_dir_exist(dir))
2189                 remove_all_numbered_files(dir);
2190         g_free(dir);
2191
2192         debug_print("done.\n");
2193 }
2194
2195 static IMAPNameSpace *imap_find_namespace_from_list(GList *ns_list,
2196                                                     const gchar *path)
2197 {
2198         IMAPNameSpace *namespace = NULL;
2199         gchar *tmp_path, *name;
2200
2201         if (!path) path = "";
2202
2203         for (; ns_list != NULL; ns_list = ns_list->next) {
2204                 IMAPNameSpace *tmp_ns = ns_list->data;
2205
2206                 Xstrcat_a(tmp_path, path, "/", return namespace);
2207                 Xstrdup_a(name, tmp_ns->name, return namespace);
2208                 if (tmp_ns->separator && tmp_ns->separator != '/') {
2209                         subst_char(tmp_path, tmp_ns->separator, '/');
2210                         subst_char(name, tmp_ns->separator, '/');
2211                 }
2212                 if (strncmp(tmp_path, name, strlen(name)) == 0)
2213                         namespace = tmp_ns;
2214         }
2215
2216         return namespace;
2217 }
2218
2219 static IMAPNameSpace *imap_find_namespace(IMAPFolder *folder,
2220                                           const gchar *path)
2221 {
2222         IMAPNameSpace *namespace;
2223
2224         g_return_val_if_fail(folder != NULL, NULL);
2225
2226         namespace = imap_find_namespace_from_list(folder->ns_personal, path);
2227         if (namespace) return namespace;
2228         namespace = imap_find_namespace_from_list(folder->ns_others, path);
2229         if (namespace) return namespace;
2230         namespace = imap_find_namespace_from_list(folder->ns_shared, path);
2231         if (namespace) return namespace;
2232
2233         return NULL;
2234 }
2235
2236 gchar imap_get_path_separator_for_item(FolderItem *item)
2237 {
2238         Folder *folder = NULL;
2239         IMAPFolder *imap_folder = NULL;
2240         if (!item)
2241                 return '/';
2242         folder = item->folder;
2243         
2244         if (!folder)
2245                 return '/';
2246         
2247         imap_folder = IMAP_FOLDER(folder);
2248         
2249         if (!imap_folder)
2250                 return '/';
2251         
2252         return imap_get_path_separator(imap_folder, item->path);
2253 }
2254
2255 static gchar imap_get_path_separator(IMAPFolder *folder, const gchar *path)
2256 {
2257         IMAPNameSpace *namespace;
2258         gchar separator = '/';
2259         IMAPSession *session = imap_session_get(FOLDER(folder));
2260         g_return_val_if_fail(session != NULL, '/');
2261
2262         if (folder->last_seen_separator == 0) {
2263                 clist * lep_list;
2264                 int r = imap_threaded_list((Folder *)folder, "", "", &lep_list);
2265                 if (r != MAILIMAP_NO_ERROR) {
2266                         log_warning(_("LIST failed\n"));
2267                         return '/';
2268                 }
2269                 
2270                 if (clist_count(lep_list) > 0) {
2271                         clistiter * iter = clist_begin(lep_list); 
2272                         struct mailimap_mailbox_list * mb;
2273                         mb = clist_content(iter);
2274                 
2275                         folder->last_seen_separator = mb->mb_delimiter;
2276                         debug_print("got separator: %c\n", folder->last_seen_separator);
2277                 }
2278                 mailimap_list_result_free(lep_list);
2279         }
2280
2281         if (folder->last_seen_separator != 0) {
2282                 debug_print("using separator: %c\n", folder->last_seen_separator);
2283                 return folder->last_seen_separator;
2284         }
2285
2286         namespace = imap_find_namespace(folder, path);
2287         if (namespace && namespace->separator)
2288                 separator = namespace->separator;
2289
2290         return separator;
2291 }
2292
2293 static gchar *imap_get_real_path(IMAPFolder *folder, const gchar *path)
2294 {
2295         gchar *real_path;
2296         gchar separator;
2297
2298         g_return_val_if_fail(folder != NULL, NULL);
2299         g_return_val_if_fail(path != NULL, NULL);
2300
2301         real_path = imap_utf8_to_modified_utf7(path);
2302         separator = imap_get_path_separator(folder, path);
2303         imap_path_separator_subst(real_path, separator);
2304
2305         return real_path;
2306 }
2307
2308 static gint imap_set_message_flags(IMAPSession *session,
2309                                    MsgNumberList *numlist,
2310                                    IMAPFlags flags,
2311                                    gboolean is_set)
2312 {
2313         gint ok = 0;
2314         GSList *seq_list;
2315         GSList * cur;
2316
2317         seq_list = imap_get_lep_set_from_numlist(numlist);
2318         
2319         for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) {
2320                 struct mailimap_set * imapset;
2321                 
2322                 imapset = cur->data;
2323                 
2324                 ok = imap_cmd_store(session, imapset,
2325                                     flags, is_set);
2326         }
2327         
2328         imap_lep_set_free(seq_list);
2329         
2330         return IMAP_SUCCESS;
2331 }
2332
2333 typedef struct _select_data {
2334         IMAPSession *session;
2335         gchar *real_path;
2336         gint *exists;
2337         gint *recent;
2338         gint *unseen;
2339         guint32 *uid_validity;
2340         gboolean done;
2341 } select_data;
2342
2343 static gint imap_select(IMAPSession *session, IMAPFolder *folder,
2344                         const gchar *path,
2345                         gint *exists, gint *recent, gint *unseen,
2346                         guint32 *uid_validity, gboolean block)
2347 {
2348         gchar *real_path;
2349         gint ok;
2350         gint exists_, recent_, unseen_;
2351         guint32 uid_validity_;
2352         
2353         if (!exists && !recent && !unseen && !uid_validity) {
2354                 if (session->mbox && strcmp(session->mbox, path) == 0)
2355                         return IMAP_SUCCESS;
2356         }
2357         if (!exists)
2358                 exists = &exists_;
2359         if (!recent)
2360                 recent = &recent_;
2361         if (!unseen)
2362                 unseen = &unseen_;
2363         if (!uid_validity)
2364                 uid_validity = &uid_validity_;
2365
2366         g_free(session->mbox);
2367         session->mbox = NULL;
2368
2369         real_path = imap_get_real_path(folder, path);
2370
2371         ok = imap_cmd_select(session, real_path,
2372                              exists, recent, unseen, uid_validity, block);
2373         if (ok != IMAP_SUCCESS)
2374                 log_warning(_("can't select folder: %s\n"), real_path);
2375         else {
2376                 session->mbox = g_strdup(path);
2377                 session->folder_content_changed = FALSE;
2378         }
2379         g_free(real_path);
2380
2381         return ok;
2382 }
2383
2384 static gint imap_status(IMAPSession *session, IMAPFolder *folder,
2385                         const gchar *path, IMAPFolderItem *item,
2386                         gint *messages,
2387                         guint32 *uid_next, guint32 *uid_validity,
2388                         gint *unseen, gboolean block)
2389 {
2390         int r;
2391         clistiter * iter;
2392         struct mailimap_mailbox_data_status * data_status;
2393         int got_values;
2394         gchar *real_path;
2395         guint mask = 0;
2396         
2397         real_path = imap_get_real_path(folder, path);
2398
2399         if (messages) {
2400                 mask |= 1 << 0;
2401         }
2402         if (uid_next) {
2403                 mask |= 1 << 2;
2404         }
2405         if (uid_validity) {
2406                 mask |= 1 << 3;
2407         }
2408         if (unseen) {
2409                 mask |= 1 << 4;
2410         }
2411         r = imap_threaded_status(FOLDER(folder), real_path, 
2412                 &data_status, mask);
2413
2414         g_free(real_path);
2415         if (r != MAILIMAP_NO_ERROR) {
2416                 debug_print("status err %d\n", r);
2417                 return IMAP_ERROR;
2418         }
2419         
2420         if (data_status->st_info_list == NULL) {
2421                 mailimap_mailbox_data_status_free(data_status);
2422                 debug_print("status->st_info_list == NULL\n");
2423                 return IMAP_ERROR;
2424         }
2425         
2426         got_values = 0;
2427         for(iter = clist_begin(data_status->st_info_list) ; iter != NULL ;
2428             iter = clist_next(iter)) {
2429                 struct mailimap_status_info * info;             
2430                 
2431                 info = clist_content(iter);
2432                 switch (info->st_att) {
2433                 case MAILIMAP_STATUS_ATT_MESSAGES:
2434                         * messages = info->st_value;
2435                         got_values |= 1 << 0;
2436                         break;
2437                         
2438                 case MAILIMAP_STATUS_ATT_UIDNEXT:
2439                         * uid_next = info->st_value;
2440                         got_values |= 1 << 2;
2441                         break;
2442                         
2443                 case MAILIMAP_STATUS_ATT_UIDVALIDITY:
2444                         * uid_validity = info->st_value;
2445                         got_values |= 1 << 3;
2446                         break;
2447                         
2448                 case MAILIMAP_STATUS_ATT_UNSEEN:
2449                         * unseen = info->st_value;
2450                         got_values |= 1 << 4;
2451                         break;
2452                 }
2453         }
2454         mailimap_mailbox_data_status_free(data_status);
2455         
2456         if (got_values != mask) {
2457                 debug_print("status: incomplete values received (%d)\n", got_values);
2458                 return IMAP_ERROR;
2459         }
2460         return IMAP_SUCCESS;
2461 }
2462
2463 static void imap_free_capabilities(IMAPSession *session)
2464 {
2465         slist_free_strings(session->capability);
2466         g_slist_free(session->capability);
2467         session->capability = NULL;
2468 }
2469
2470 /* low-level IMAP4rev1 commands */
2471
2472 static gint imap_cmd_login(IMAPSession *session,
2473                            const gchar *user, const gchar *pass,
2474                            const gchar *type)
2475 {
2476         int r;
2477         gint ok;
2478
2479         log_print("IMAP4> Logging %s to %s using %s\n", 
2480                         user,
2481                         SESSION(session)->server,
2482                         type);
2483         r = imap_threaded_login(session->folder, user, pass, type);
2484         if (r != MAILIMAP_NO_ERROR) {
2485                 log_print("IMAP4< Error logging in to %s\n",
2486                                 SESSION(session)->server);
2487                 ok = IMAP_ERROR;
2488         } else {
2489                 log_print("IMAP4< Login to %s successful\n",
2490                                 SESSION(session)->server);
2491                 ok = IMAP_SUCCESS;
2492         }
2493         return ok;
2494 }
2495
2496 static gint imap_cmd_logout(IMAPSession *session)
2497 {
2498         imap_threaded_disconnect(session->folder);
2499
2500         return IMAP_SUCCESS;
2501 }
2502
2503 static gint imap_cmd_noop(IMAPSession *session)
2504 {
2505         int r;
2506         unsigned int exists;
2507         
2508         r = imap_threaded_noop(session->folder, &exists);
2509         if (r != MAILIMAP_NO_ERROR) {
2510                 debug_print("noop err %d\n", r);
2511                 return IMAP_ERROR;
2512         }
2513         session->exists = exists;
2514         session_set_access_time(SESSION(session));
2515
2516         return IMAP_SUCCESS;
2517 }
2518
2519 #if USE_OPENSSL
2520 static gint imap_cmd_starttls(IMAPSession *session)
2521 {
2522         int r;
2523         
2524         r = imap_threaded_starttls(session->folder, 
2525                 SESSION(session)->server, SESSION(session)->port);
2526         if (r != MAILIMAP_NO_ERROR) {
2527                 debug_print("starttls err %d\n", r);
2528                 return IMAP_ERROR;
2529         }
2530         return IMAP_SUCCESS;
2531 }
2532 #endif
2533
2534 static gint imap_cmd_select(IMAPSession *session, const gchar *folder,
2535                             gint *exists, gint *recent, gint *unseen,
2536                             guint32 *uid_validity, gboolean block)
2537 {
2538         int r;
2539
2540         r = imap_threaded_select(session->folder, folder,
2541                                  exists, recent, unseen, uid_validity);
2542         if (r != MAILIMAP_NO_ERROR) {
2543                 debug_print("select err %d\n", r);
2544                 return IMAP_ERROR;
2545         }
2546         return IMAP_SUCCESS;
2547 }
2548
2549 static gint imap_cmd_examine(IMAPSession *session, const gchar *folder,
2550                              gint *exists, gint *recent, gint *unseen,
2551                              guint32 *uid_validity, gboolean block)
2552 {
2553         int r;
2554
2555         r = imap_threaded_examine(session->folder, folder,
2556                                   exists, recent, unseen, uid_validity);
2557         if (r != MAILIMAP_NO_ERROR) {
2558                 debug_print("examine err %d\n", r);
2559                 
2560                 return IMAP_ERROR;
2561         }
2562         return IMAP_SUCCESS;
2563 }
2564
2565 static gint imap_cmd_create(IMAPSession *session, const gchar *folder)
2566 {
2567         int r;
2568
2569         r = imap_threaded_create(session->folder, folder);
2570         if (r != MAILIMAP_NO_ERROR) {
2571                 
2572                 return IMAP_ERROR;
2573         }
2574
2575         return IMAP_SUCCESS;
2576 }
2577
2578 static gint imap_cmd_rename(IMAPSession *session, const gchar *old_folder,
2579                             const gchar *new_folder)
2580 {
2581         int r;
2582
2583         r = imap_threaded_rename(session->folder, old_folder,
2584                                  new_folder);
2585         if (r != MAILIMAP_NO_ERROR) {
2586                 
2587                 return IMAP_ERROR;
2588         }
2589
2590         return IMAP_SUCCESS;
2591 }
2592
2593 static gint imap_cmd_delete(IMAPSession *session, const gchar *folder)
2594 {
2595         int r;
2596         
2597
2598         r = imap_threaded_delete(session->folder, folder);
2599         if (r != MAILIMAP_NO_ERROR) {
2600                 
2601                 return IMAP_ERROR;
2602         }
2603
2604         return IMAP_SUCCESS;
2605 }
2606
2607 typedef struct _fetch_data {
2608         IMAPSession *session;
2609         guint32 uid;
2610         const gchar *filename;
2611         gboolean headers;
2612         gboolean body;
2613         gboolean done;
2614 } fetch_data;
2615
2616 static void *imap_cmd_fetch_thread(void *data)
2617 {
2618         fetch_data *stuff = (fetch_data *)data;
2619         IMAPSession *session = stuff->session;
2620         guint32 uid = stuff->uid;
2621         const gchar *filename = stuff->filename;
2622         int r;
2623         
2624         if (stuff->body) {
2625                 r = imap_threaded_fetch_content(session->folder,
2626                                                uid, 1, filename);
2627         }
2628         else {
2629                 r = imap_threaded_fetch_content(session->folder,
2630                                                 uid, 0, filename);
2631         }
2632         if (r != MAILIMAP_NO_ERROR) {
2633                 debug_print("fetch err %d\n", r);
2634                 return GINT_TO_POINTER(IMAP_ERROR);
2635         }
2636         return GINT_TO_POINTER(IMAP_SUCCESS);
2637 }
2638
2639 static gint imap_cmd_fetch(IMAPSession *session, guint32 uid,
2640                                 const gchar *filename, gboolean headers,
2641                                 gboolean body)
2642 {
2643         fetch_data *data = g_new0(fetch_data, 1);
2644         int result = 0;
2645         data->done = FALSE;
2646         data->session = session;
2647         data->uid = uid;
2648         data->filename = filename;
2649         data->headers = headers;
2650         data->body = body;
2651
2652         if (prefs_common.work_offline && 
2653             !inc_offline_should_override(
2654                 _("Sylpheed-Claws needs network access in order "
2655                   "to access the IMAP server."))) {
2656                 g_free(data);
2657                 return -1;
2658         }
2659         statusbar_print_all(_("Fetching message..."));
2660         result = GPOINTER_TO_INT(imap_cmd_fetch_thread(data));
2661         statusbar_pop_all();
2662         g_free(data);
2663         return result;
2664 }
2665
2666
2667 static gint imap_cmd_append(IMAPSession *session, const gchar *destfolder,
2668                             const gchar *file, IMAPFlags flags, 
2669                             guint32 *new_uid)
2670 {
2671         struct mailimap_flag_list * flag_list;
2672         int r;
2673         
2674         g_return_val_if_fail(file != NULL, IMAP_ERROR);
2675
2676         flag_list = imap_flag_to_lep(flags);
2677         r = imap_threaded_append(session->folder, destfolder,
2678                          file, flag_list, (int *)new_uid);
2679         mailimap_flag_list_free(flag_list);
2680
2681         if (r != MAILIMAP_NO_ERROR) {
2682                 debug_print("append err %d\n", r);
2683                 return IMAP_ERROR;
2684         }
2685         return IMAP_SUCCESS;
2686 }
2687
2688 static gint imap_cmd_copy(IMAPSession *session, struct mailimap_set * set,
2689                           const gchar *destfolder, GRelation *uid_mapping)
2690 {
2691         int r;
2692         
2693         g_return_val_if_fail(session != NULL, IMAP_ERROR);
2694         g_return_val_if_fail(set != NULL, IMAP_ERROR);
2695         g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
2696
2697         r = imap_threaded_copy(session->folder, set, destfolder);
2698         if (r != MAILIMAP_NO_ERROR) {
2699                 
2700                 return IMAP_ERROR;
2701         }
2702
2703         return IMAP_SUCCESS;
2704 }
2705
2706 static gint imap_cmd_store(IMAPSession *session, struct mailimap_set * set,
2707                            IMAPFlags flags, int do_add)
2708 {
2709         int r;
2710         struct mailimap_flag_list * flag_list;
2711         struct mailimap_store_att_flags * store_att_flags;
2712         
2713         flag_list = imap_flag_to_lep(flags);
2714         
2715         if (do_add)
2716                 store_att_flags =
2717                         mailimap_store_att_flags_new_add_flags_silent(flag_list);
2718         else
2719                 store_att_flags =
2720                         mailimap_store_att_flags_new_remove_flags_silent(flag_list);
2721         
2722         r = imap_threaded_store(session->folder, set, store_att_flags);
2723         mailimap_store_att_flags_free(store_att_flags);
2724         if (r != MAILIMAP_NO_ERROR) {
2725                 
2726                 return IMAP_ERROR;
2727         }
2728         
2729         return IMAP_SUCCESS;
2730 }
2731
2732 static gint imap_cmd_expunge(IMAPSession *session)
2733 {
2734         int r;
2735         
2736         if (prefs_common.work_offline && 
2737             !inc_offline_should_override(
2738                 _("Sylpheed-Claws needs network access in order "
2739                   "to access the IMAP server."))) {
2740                 return -1;
2741         }
2742
2743         r = imap_threaded_expunge(session->folder);
2744         if (r != MAILIMAP_NO_ERROR) {
2745                 
2746                 return IMAP_ERROR;
2747         }
2748
2749         return IMAP_SUCCESS;
2750 }
2751
2752 static void imap_path_separator_subst(gchar *str, gchar separator)
2753 {
2754         gchar *p;
2755         gboolean in_escape = FALSE;
2756
2757         if (!separator || separator == '/') return;
2758
2759         for (p = str; *p != '\0'; p++) {
2760                 if (*p == '/' && !in_escape)
2761                         *p = separator;
2762                 else if (*p == '&' && *(p + 1) != '-' && !in_escape)
2763                         in_escape = TRUE;
2764                 else if (*p == '-' && in_escape)
2765                         in_escape = FALSE;
2766         }
2767 }
2768
2769 static gchar *imap_modified_utf7_to_utf8(const gchar *mutf7_str)
2770 {
2771         static iconv_t cd = (iconv_t)-1;
2772         static gboolean iconv_ok = TRUE;
2773         GString *norm_utf7;
2774         gchar *norm_utf7_p;
2775         size_t norm_utf7_len;
2776         const gchar *p;
2777         gchar *to_str, *to_p;
2778         size_t to_len;
2779         gboolean in_escape = FALSE;
2780
2781         if (!iconv_ok) return g_strdup(mutf7_str);
2782
2783         if (cd == (iconv_t)-1) {
2784                 cd = iconv_open(CS_INTERNAL, CS_UTF_7);
2785                 if (cd == (iconv_t)-1) {
2786                         g_warning("iconv cannot convert UTF-7 to %s\n",
2787                                   CS_INTERNAL);
2788                         iconv_ok = FALSE;
2789                         return g_strdup(mutf7_str);
2790                 }
2791         }
2792
2793         /* modified UTF-7 to normal UTF-7 conversion */
2794         norm_utf7 = g_string_new(NULL);
2795
2796         for (p = mutf7_str; *p != '\0'; p++) {
2797                 /* replace: '&'  -> '+',
2798                             "&-" -> '&',
2799                             escaped ','  -> '/' */
2800                 if (!in_escape && *p == '&') {
2801                         if (*(p + 1) != '-') {
2802                                 g_string_append_c(norm_utf7, '+');
2803                                 in_escape = TRUE;
2804                         } else {
2805                                 g_string_append_c(norm_utf7, '&');
2806                                 p++;
2807                         }
2808                 } else if (in_escape && *p == ',') {
2809                         g_string_append_c(norm_utf7, '/');
2810                 } else if (in_escape && *p == '-') {
2811                         g_string_append_c(norm_utf7, '-');
2812                         in_escape = FALSE;
2813                 } else {
2814                         g_string_append_c(norm_utf7, *p);
2815                 }
2816         }
2817
2818         norm_utf7_p = norm_utf7->str;
2819         norm_utf7_len = norm_utf7->len;
2820         to_len = strlen(mutf7_str) * 5;
2821         to_p = to_str = g_malloc(to_len + 1);
2822
2823         if (iconv(cd, (ICONV_CONST gchar **)&norm_utf7_p, &norm_utf7_len,
2824                   &to_p, &to_len) == -1) {
2825                 g_warning(_("iconv cannot convert UTF-7 to %s\n"),
2826                           conv_get_locale_charset_str());
2827                 g_string_free(norm_utf7, TRUE);
2828                 g_free(to_str);
2829                 return g_strdup(mutf7_str);
2830         }
2831
2832         /* second iconv() call for flushing */
2833         iconv(cd, NULL, NULL, &to_p, &to_len);
2834         g_string_free(norm_utf7, TRUE);
2835         *to_p = '\0';
2836
2837         return to_str;
2838 }
2839
2840 static gchar *imap_utf8_to_modified_utf7(const gchar *from)
2841 {
2842         static iconv_t cd = (iconv_t)-1;
2843         static gboolean iconv_ok = TRUE;
2844         gchar *norm_utf7, *norm_utf7_p;
2845         size_t from_len, norm_utf7_len;
2846         GString *to_str;
2847         gchar *from_tmp, *to, *p;
2848         gboolean in_escape = FALSE;
2849
2850         if (!iconv_ok) return g_strdup(from);
2851
2852         if (cd == (iconv_t)-1) {
2853                 cd = iconv_open(CS_UTF_7, CS_INTERNAL);
2854                 if (cd == (iconv_t)-1) {
2855                         g_warning(_("iconv cannot convert %s to UTF-7\n"),
2856                                   CS_INTERNAL);
2857                         iconv_ok = FALSE;
2858                         return g_strdup(from);
2859                 }
2860         }
2861
2862         /* UTF-8 to normal UTF-7 conversion */
2863         Xstrdup_a(from_tmp, from, return g_strdup(from));
2864         from_len = strlen(from);
2865         norm_utf7_len = from_len * 5;
2866         Xalloca(norm_utf7, norm_utf7_len + 1, return g_strdup(from));
2867         norm_utf7_p = norm_utf7;
2868
2869 #define IS_PRINT(ch) (isprint(ch) && IS_ASCII(ch))
2870
2871         while (from_len > 0) {
2872                 if (*from_tmp == '+') {
2873                         *norm_utf7_p++ = '+';
2874                         *norm_utf7_p++ = '-';
2875                         norm_utf7_len -= 2;
2876                         from_tmp++;
2877                         from_len--;
2878                 } else if (IS_PRINT(*(guchar *)from_tmp)) {
2879                         /* printable ascii char */
2880                         *norm_utf7_p = *from_tmp;
2881                         norm_utf7_p++;
2882                         norm_utf7_len--;
2883                         from_tmp++;
2884                         from_len--;
2885                 } else {
2886                         size_t conv_len = 0;
2887
2888                         /* unprintable char: convert to UTF-7 */
2889                         p = from_tmp;
2890                         while (!IS_PRINT(*(guchar *)p) && conv_len < from_len) {
2891                                 conv_len += g_utf8_skip[*(guchar *)p];
2892                                 p += g_utf8_skip[*(guchar *)p];
2893                         }
2894
2895                         from_len -= conv_len;
2896                         if (iconv(cd, (ICONV_CONST gchar **)&from_tmp,
2897                                   &conv_len,
2898                                   &norm_utf7_p, &norm_utf7_len) == -1) {
2899                                 g_warning(_("iconv cannot convert UTF-8 to UTF-7\n"));
2900                                 return g_strdup(from);
2901                         }
2902
2903                         /* second iconv() call for flushing */
2904                         iconv(cd, NULL, NULL, &norm_utf7_p, &norm_utf7_len);
2905                 }
2906         }
2907
2908 #undef IS_PRINT
2909
2910         *norm_utf7_p = '\0';
2911         to_str = g_string_new(NULL);
2912         for (p = norm_utf7; p < norm_utf7_p; p++) {
2913                 /* replace: '&' -> "&-",
2914                             '+' -> '&',
2915                             "+-" -> '+',
2916                             BASE64 '/' -> ',' */
2917                 if (!in_escape && *p == '&') {
2918                         g_string_append(to_str, "&-");
2919                 } else if (!in_escape && *p == '+') {
2920                         if (*(p + 1) == '-') {
2921                                 g_string_append_c(to_str, '+');
2922                                 p++;
2923                         } else {
2924                                 g_string_append_c(to_str, '&');
2925                                 in_escape = TRUE;
2926                         }
2927                 } else if (in_escape && *p == '/') {
2928                         g_string_append_c(to_str, ',');
2929                 } else if (in_escape && *p == '-') {
2930                         g_string_append_c(to_str, '-');
2931                         in_escape = FALSE;
2932                 } else {
2933                         g_string_append_c(to_str, *p);
2934                 }
2935         }
2936
2937         if (in_escape) {
2938                 in_escape = FALSE;
2939                 g_string_append_c(to_str, '-');
2940         }
2941
2942         to = to_str->str;
2943         g_string_free(to_str, FALSE);
2944
2945         return to;
2946 }
2947
2948 static gboolean imap_rename_folder_func(GNode *node, gpointer data)
2949 {
2950         FolderItem *item = node->data;
2951         gchar **paths = data;
2952         const gchar *oldpath = paths[0];
2953         const gchar *newpath = paths[1];
2954         gchar *base;
2955         gchar *new_itempath;
2956         gint oldpathlen;
2957
2958         oldpathlen = strlen(oldpath);
2959         if (strncmp(oldpath, item->path, oldpathlen) != 0) {
2960                 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
2961                 return TRUE;
2962         }
2963
2964         base = item->path + oldpathlen;
2965         while (*base == G_DIR_SEPARATOR) base++;
2966         if (*base == '\0')
2967                 new_itempath = g_strdup(newpath);
2968         else
2969                 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
2970                                            NULL);
2971         g_free(item->path);
2972         item->path = new_itempath;
2973
2974         return FALSE;
2975 }
2976
2977 typedef struct _get_list_uid_data {
2978         Folder *folder;
2979         IMAPSession *session;
2980         IMAPFolderItem *item;
2981         GSList **msgnum_list;
2982         gboolean done;
2983 } get_list_uid_data;
2984
2985 static void *get_list_of_uids_thread(void *data)
2986 {
2987         get_list_uid_data *stuff = (get_list_uid_data *)data;
2988         Folder *folder = stuff->folder;
2989         IMAPFolderItem *item = stuff->item;
2990         GSList **msgnum_list = stuff->msgnum_list;
2991         gint ok, nummsgs = 0, lastuid_old;
2992         IMAPSession *session;
2993         GSList *uidlist, *elem;
2994         struct mailimap_set * set;
2995         clist * lep_uidlist;
2996         int r;
2997
2998         session = stuff->session;
2999         if (session == NULL) {
3000                 stuff->done = TRUE;
3001                 return GINT_TO_POINTER(-1);
3002         }
3003         /* no session locking here, it's already locked by caller */
3004         ok = imap_select(session, IMAP_FOLDER(folder), item->item.path,
3005                          NULL, NULL, NULL, NULL, TRUE);
3006         if (ok != IMAP_SUCCESS) {
3007                 stuff->done = TRUE;
3008                 return GINT_TO_POINTER(-1);
3009         }
3010
3011         uidlist = NULL;
3012         
3013         set = mailimap_set_new_interval(item->lastuid + 1, 0);
3014
3015         r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SIMPLE, set,
3016                                  &lep_uidlist);
3017         mailimap_set_free(set);
3018         
3019         if (r == MAILIMAP_NO_ERROR) {
3020                 GSList * fetchuid_list;
3021                 
3022                 fetchuid_list =
3023                         imap_uid_list_from_lep(lep_uidlist);
3024                 mailimap_search_result_free(lep_uidlist);
3025                 
3026                 uidlist = g_slist_concat(fetchuid_list, uidlist);
3027         }
3028         else {
3029                 GSList * fetchuid_list;
3030                 carray * lep_uidtab;
3031                 
3032                 r = imap_threaded_fetch_uid(folder, item->lastuid + 1,
3033                                             &lep_uidtab);
3034                 if (r == MAILIMAP_NO_ERROR) {
3035                         fetchuid_list =
3036                                 imap_uid_list_from_lep_tab(lep_uidtab);
3037                         imap_fetch_uid_list_free(lep_uidtab);
3038                         uidlist = g_slist_concat(fetchuid_list, uidlist);
3039                 }
3040         }
3041         
3042         lastuid_old = item->lastuid;
3043         *msgnum_list = g_slist_copy(item->uid_list);
3044         nummsgs = g_slist_length(*msgnum_list);
3045         debug_print("Got %d uids from cache\n", g_slist_length(item->uid_list));
3046
3047         for (elem = uidlist; elem != NULL; elem = g_slist_next(elem)) {
3048                 guint msgnum;
3049
3050                 msgnum = GPOINTER_TO_INT(elem->data);
3051                 if (msgnum > lastuid_old) {
3052                         *msgnum_list = g_slist_prepend(*msgnum_list, GINT_TO_POINTER(msgnum));
3053                         item->uid_list = g_slist_prepend(item->uid_list, GINT_TO_POINTER(msgnum));
3054                         nummsgs++;
3055
3056                         if(msgnum > item->lastuid)
3057                                 item->lastuid = msgnum;
3058                 }
3059         }
3060         g_slist_free(uidlist);
3061         stuff->done = TRUE;
3062         return GINT_TO_POINTER(nummsgs);
3063 }
3064
3065 static gint get_list_of_uids(IMAPSession *session, Folder *folder, IMAPFolderItem *item, GSList **msgnum_list)
3066 {
3067         gint result;
3068         get_list_uid_data *data = g_new0(get_list_uid_data, 1);
3069         data->done = FALSE;
3070         data->folder = folder;
3071         data->item = item;
3072         data->msgnum_list = msgnum_list;
3073         data->session = session;
3074         if (prefs_common.work_offline && 
3075             !inc_offline_should_override(
3076                 _("Sylpheed-Claws needs network access in order "
3077                   "to access the IMAP server."))) {
3078                 g_free(data);
3079                 return -1;
3080         }
3081
3082         result = GPOINTER_TO_INT(get_list_of_uids_thread(data));
3083         g_free(data);
3084         return result;
3085
3086 }
3087
3088 gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list, gboolean *old_uids_valid)
3089 {
3090         IMAPFolderItem *item = (IMAPFolderItem *)_item;
3091         IMAPSession *session;
3092         gint ok, nummsgs = 0, exists;
3093         guint32 uid_next = 0, uid_val = 0;
3094         GSList *uidlist = NULL;
3095         gchar *dir;
3096         gboolean selected_folder;
3097         
3098         debug_print("get_num_list\n");
3099         
3100         g_return_val_if_fail(folder != NULL, -1);
3101         g_return_val_if_fail(item != NULL, -1);
3102         g_return_val_if_fail(item->item.path != NULL, -1);
3103         g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3104         g_return_val_if_fail(folder->account != NULL, -1);
3105
3106         session = imap_session_get(folder);
3107         g_return_val_if_fail(session != NULL, -1);
3108         lock_session();
3109
3110         if (FOLDER_ITEM(item)->path) 
3111                 statusbar_print_all(_("Scanning folder %s%c%s ..."),
3112                                       FOLDER_ITEM(item)->folder->name, 
3113                                       G_DIR_SEPARATOR,
3114                                       FOLDER_ITEM(item)->path);
3115         else
3116                 statusbar_print_all(_("Scanning folder %s ..."),
3117                                       FOLDER_ITEM(item)->folder->name);
3118
3119         selected_folder = (session->mbox != NULL) &&
3120                           (!strcmp(session->mbox, item->item.path));
3121         if (selected_folder && time(NULL) - item->use_cache < 2) {
3122                 ok = imap_cmd_noop(session);
3123                 if (ok != IMAP_SUCCESS) {
3124                         debug_print("disconnected!\n");
3125                         session = imap_reconnect_if_possible(folder, session);
3126                         if (session == NULL) {
3127                                 statusbar_pop_all();
3128                                 unlock_session();
3129                                 return -1;
3130                         }
3131                 }
3132                 exists = session->exists;
3133
3134                 uid_next = item->c_uid_next;
3135                 uid_val = item->c_uid_validity;
3136                 *old_uids_valid = TRUE;
3137         } else {
3138                 if (item->use_cache && time(NULL) - item->use_cache < 2) {
3139                         exists = item->c_messages;
3140                         uid_next = item->c_uid_next;
3141                         uid_val = item->c_uid_validity;
3142                         ok = IMAP_SUCCESS;
3143                         debug_print("using cache %d %d %d\n", exists, uid_next, uid_val);
3144                 } else {
3145                         ok = imap_status(session, IMAP_FOLDER(folder), item->item.path, item,
3146                                  &exists, &uid_next, &uid_val, NULL, FALSE);
3147                 }
3148                 item->item.last_num = uid_next - 1;
3149                 
3150                 item->use_cache = (time_t)0;
3151                 if (ok != IMAP_SUCCESS) {
3152                         statusbar_pop_all();
3153                         unlock_session();
3154                         return -1;
3155                 }
3156                 if(item->item.mtime == uid_val)
3157                         *old_uids_valid = TRUE;
3158                 else {
3159                         *old_uids_valid = FALSE;
3160
3161                         debug_print("Freeing imap uid cache (%d != %d)\n",
3162                                         (int)item->item.mtime, uid_val);
3163                         item->lastuid = 0;
3164                         g_slist_free(item->uid_list);
3165                         item->uid_list = NULL;
3166                 
3167                         item->item.mtime = uid_val;
3168
3169                         imap_delete_all_cached_messages((FolderItem *)item);
3170                 }
3171         }
3172
3173         /* If old uid_next matches new uid_next we can be sure no message
3174            was added to the folder */
3175         debug_print("uid_next is %d and item->uid_next %d \n", 
3176                 uid_next, item->uid_next);
3177         if (uid_next == item->uid_next) {
3178                 nummsgs = g_slist_length(item->uid_list);
3179
3180                 /* If number of messages is still the same we
3181                    know our caches message numbers are still valid,
3182                    otherwise if the number of messages has decrease
3183                    we discard our cache to start a new scan to find
3184                    out which numbers have been removed */
3185                 if (exists == nummsgs) {
3186                         debug_print("exists == nummsgs\n");
3187                         *msgnum_list = g_slist_copy(item->uid_list);
3188                         statusbar_pop_all();
3189                         unlock_session();
3190                         return nummsgs;
3191                 } else if (exists < nummsgs) {
3192                         debug_print("Freeing imap uid cache");
3193                         item->lastuid = 0;
3194                         g_slist_free(item->uid_list);
3195                         item->uid_list = NULL;
3196                 }
3197         }
3198
3199         if (exists == 0) {
3200                 *msgnum_list = NULL;
3201                 statusbar_pop_all();
3202                 unlock_session();
3203                 return 0;
3204         }
3205
3206         nummsgs = get_list_of_uids(session, folder, item, &uidlist);
3207
3208         if (nummsgs < 0) {
3209                 statusbar_pop_all();
3210                 unlock_session();
3211                 return -1;
3212         }
3213
3214         if (nummsgs != exists) {
3215                 /* Cache contains more messages then folder, we have cached
3216                    an old UID of a message that was removed and new messages
3217                    have been added too, otherwise the uid_next check would
3218                    not have failed */
3219                 debug_print("Freeing imap uid cache");
3220                 item->lastuid = 0;
3221                 g_slist_free(item->uid_list);
3222                 item->uid_list = NULL;
3223
3224                 g_slist_free(*msgnum_list);
3225
3226                 nummsgs = get_list_of_uids(session, folder, item, &uidlist);
3227         }
3228
3229         *msgnum_list = uidlist;
3230
3231         dir = folder_item_get_path((FolderItem *)item);
3232         debug_print("removing old messages from %s\n", dir);
3233         remove_numbered_files_not_in_list(dir, *msgnum_list);
3234         g_free(dir);
3235         
3236         item->uid_next = uid_next;
3237         
3238         debug_print("get_num_list - ok - %i\n", nummsgs);
3239         statusbar_pop_all();
3240         unlock_session();
3241         return nummsgs;
3242 }
3243
3244 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item)
3245 {
3246         MsgInfo *msginfo;
3247         MsgFlags flags;
3248
3249         flags.perm_flags = MSG_NEW|MSG_UNREAD;
3250         flags.tmp_flags = 0;
3251
3252         g_return_val_if_fail(item != NULL, NULL);
3253         g_return_val_if_fail(file != NULL, NULL);
3254
3255         if (folder_has_parent_of_type(item, F_QUEUE)) {
3256                 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
3257         } else if (folder_has_parent_of_type(item, F_DRAFT)) {
3258                 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
3259         }
3260
3261         msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3262         if (!msginfo) return NULL;
3263         
3264         msginfo->plaintext_file = g_strdup(file);
3265         msginfo->folder = item;
3266
3267         return msginfo;
3268 }
3269
3270 GSList *imap_get_msginfos(Folder *folder, FolderItem *item,
3271                           GSList *msgnum_list)
3272 {
3273         IMAPSession *session;
3274         MsgInfoList *ret = NULL;
3275         gint ok;
3276         
3277         debug_print("get_msginfos\n");
3278         
3279         g_return_val_if_fail(folder != NULL, NULL);
3280         g_return_val_if_fail(item != NULL, NULL);
3281         g_return_val_if_fail(msgnum_list != NULL, NULL);
3282
3283         session = imap_session_get(folder);
3284         g_return_val_if_fail(session != NULL, NULL);
3285         lock_session();
3286         debug_print("IMAP getting msginfos\n");
3287         ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3288                          NULL, NULL, NULL, NULL, FALSE);
3289         if (ok != IMAP_SUCCESS) {
3290                 unlock_session();
3291                 return NULL;
3292         }
3293         if (!(folder_has_parent_of_type(item, F_DRAFT) || 
3294               folder_has_parent_of_type(item, F_QUEUE))) {
3295                 ret = g_slist_concat(ret,
3296                         imap_get_uncached_messages(session, item,
3297                                                    msgnum_list));
3298         } else {
3299                 MsgNumberList *sorted_list, *elem, *llast = NULL;
3300                 gint startnum, lastnum;
3301
3302                 sorted_list = g_slist_sort(g_slist_copy(msgnum_list), g_int_compare);
3303
3304                 startnum = lastnum = GPOINTER_TO_INT(sorted_list->data);
3305
3306                 llast = g_slist_last(ret);
3307                 for (elem = sorted_list;; elem = g_slist_next(elem)) {
3308                         guint num = 0;
3309
3310                         if (elem)
3311                                 num = GPOINTER_TO_INT(elem->data);
3312
3313                         if (num > lastnum + 1 || elem == NULL) {
3314                                 int i;
3315                                 for (i = startnum; i <= lastnum; ++i) {
3316                                         gchar *file;
3317                         
3318                                         file = imap_fetch_msg(folder, item, i);
3319                                         if (file != NULL) {
3320                                                 MsgInfo *msginfo = imap_parse_msg(file, item);
3321                                                 if (msginfo != NULL) {
3322                                                         msginfo->msgnum = i;
3323                                                         if (llast == NULL)
3324                                                                 llast = ret = g_slist_append(ret, msginfo);
3325                                                         else {
3326                                                                 llast = g_slist_append(llast, msginfo);
3327                                                                 llast = llast->next;
3328                                                         }
3329                                                 }
3330                                                 g_free(file);
3331                                         }
3332                                         session_set_access_time(SESSION(session));
3333                                 }
3334
3335                                 if (elem == NULL)
3336                                         break;
3337
3338                                 startnum = num;
3339                         }
3340                         lastnum = num;
3341                 }
3342
3343                 g_slist_free(sorted_list);
3344         }
3345         unlock_session();
3346         return ret;
3347 }
3348
3349 MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid)
3350 {
3351         MsgInfo *msginfo = NULL;
3352         MsgInfoList *msginfolist;
3353         MsgNumberList numlist;
3354
3355         numlist.next = NULL;
3356         numlist.data = GINT_TO_POINTER(uid);
3357
3358         msginfolist = imap_get_msginfos(folder, item, &numlist);
3359         if (msginfolist != NULL) {
3360                 msginfo = msginfolist->data;
3361                 g_slist_free(msginfolist);
3362         }
3363
3364         return msginfo;
3365 }
3366
3367 gboolean imap_scan_required(Folder *folder, FolderItem *_item)
3368 {
3369         IMAPSession *session;
3370         IMAPFolderItem *item = (IMAPFolderItem *)_item;
3371         gint ok, exists = 0, unseen = 0;
3372         guint32 uid_next, uid_val;
3373         gboolean selected_folder;
3374         
3375         g_return_val_if_fail(folder != NULL, FALSE);
3376         g_return_val_if_fail(item != NULL, FALSE);
3377         g_return_val_if_fail(item->item.folder != NULL, FALSE);
3378         g_return_val_if_fail(FOLDER_CLASS(item->item.folder) == &imap_class, FALSE);
3379
3380         if (item->item.path == NULL)
3381                 return FALSE;
3382
3383         session = imap_session_get(folder);
3384         g_return_val_if_fail(session != NULL, FALSE);
3385         lock_session();
3386         selected_folder = (session->mbox != NULL) &&
3387                           (!strcmp(session->mbox, item->item.path));
3388         if (selected_folder && time(NULL) - item->use_cache < 2) {
3389                 ok = imap_cmd_noop(session);
3390                 if (ok != IMAP_SUCCESS) {
3391                         debug_print("disconnected!\n");
3392                         session = imap_reconnect_if_possible(folder, session);
3393                         if (session == NULL)
3394                                 return FALSE;
3395                         lock_session();
3396                 }
3397
3398                 if (session->folder_content_changed
3399                 ||  session->exists != item->item.total_msgs) {
3400                         unlock_session();
3401                         return TRUE;
3402                 }
3403         } else {
3404                 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path, IMAP_FOLDER_ITEM(item),
3405                                  &exists, &uid_next, &uid_val, &unseen, FALSE);
3406                 if (ok != IMAP_SUCCESS) {
3407                         unlock_session();
3408                         return FALSE;
3409                 }
3410
3411                 item->use_cache = time(NULL);
3412                 item->c_messages = exists;
3413                 item->c_uid_next = uid_next;
3414                 item->c_uid_validity = uid_val;
3415                 item->c_unseen = unseen;
3416                 item->item.last_num = uid_next - 1;
3417                 debug_print("uidnext %d, item->uid_next %d, exists %d, item->item.total_msgs %d\n", 
3418                         uid_next, item->uid_next, exists, item->item.total_msgs);
3419                 if ((uid_next != item->uid_next) || (exists != item->item.total_msgs)
3420                     || unseen != item->item.unread_msgs || uid_val != item->item.mtime) {
3421                         unlock_session();
3422                         return TRUE;
3423                 }
3424         }
3425         unlock_session();
3426         return FALSE;
3427 }
3428
3429 void imap_change_flags(Folder *folder, FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
3430 {
3431         IMAPSession *session;
3432         IMAPFlags flags_set = 0, flags_unset = 0;
3433         gint ok = IMAP_SUCCESS;
3434         MsgNumberList numlist;
3435         hashtable_data *ht_data = NULL;
3436
3437         g_return_if_fail(folder != NULL);
3438         g_return_if_fail(folder->klass == &imap_class);
3439         g_return_if_fail(item != NULL);
3440         g_return_if_fail(item->folder == folder);
3441         g_return_if_fail(msginfo != NULL);
3442         g_return_if_fail(msginfo->folder == item);
3443
3444         session = imap_session_get(folder);
3445         if (!session) {
3446                 return;
3447         }
3448
3449         if (!MSG_IS_MARKED(msginfo->flags) &&  (newflags & MSG_MARKED))
3450                 flags_set |= IMAP_FLAG_FLAGGED;
3451         if ( MSG_IS_MARKED(msginfo->flags) && !(newflags & MSG_MARKED))
3452                 flags_unset |= IMAP_FLAG_FLAGGED;
3453
3454         if (!MSG_IS_UNREAD(msginfo->flags) &&  (newflags & MSG_UNREAD))
3455                 flags_unset |= IMAP_FLAG_SEEN;
3456         if ( MSG_IS_UNREAD(msginfo->flags) && !(newflags & MSG_UNREAD))
3457                 flags_set |= IMAP_FLAG_SEEN;
3458
3459         if (!MSG_IS_REPLIED(msginfo->flags) &&  (newflags & MSG_REPLIED))
3460                 flags_set |= IMAP_FLAG_ANSWERED;
3461         if ( MSG_IS_REPLIED(msginfo->flags) && !(newflags & MSG_REPLIED))
3462                 flags_unset |= IMAP_FLAG_ANSWERED;
3463
3464         if (!MSG_IS_DELETED(msginfo->flags) &&  (newflags & MSG_DELETED))
3465                 flags_set |= IMAP_FLAG_DELETED;
3466         if ( MSG_IS_DELETED(msginfo->flags) && !(newflags & MSG_DELETED))
3467                 flags_unset |= IMAP_FLAG_DELETED;
3468
3469         if (!flags_set && !flags_unset) {
3470                 /* the changed flags were not translatable to IMAP-speak.
3471                  * like MSG_POSTFILTERED, so just apply. */
3472                 msginfo->flags.perm_flags = newflags;
3473                 return;
3474         }
3475
3476         lock_session();
3477         if ((ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
3478             NULL, NULL, NULL, NULL, FALSE)) != IMAP_SUCCESS) {
3479                 unlock_session();
3480                 return;
3481         }
3482         numlist.next = NULL;
3483         numlist.data = GINT_TO_POINTER(msginfo->msgnum);
3484
3485         if (IMAP_FOLDER_ITEM(item)->batching) {
3486                 /* instead of performing an UID STORE command for each message change,
3487                  * as a lot of them can change "together", we just fill in hashtables
3488                  * and defer the treatment so that we're able to send only one
3489                  * command.
3490                  */
3491                 debug_print("IMAP batch mode on, deferring flags change\n");
3492                 if (flags_set) {
3493                         ht_data = g_hash_table_lookup(IMAP_FOLDER_ITEM(item)->flags_set_table, 
3494                                 GINT_TO_POINTER(flags_set));
3495                         if (ht_data == NULL) {
3496                                 ht_data = g_new0(hashtable_data, 1);
3497                                 ht_data->session = session;
3498                                 ht_data->item = IMAP_FOLDER_ITEM(item);
3499                                 g_hash_table_insert(IMAP_FOLDER_ITEM(item)->flags_set_table, 
3500                                         GINT_TO_POINTER(flags_set), ht_data);
3501                         }
3502                         if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
3503                                 ht_data->msglist = g_slist_prepend(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum));
3504                 } 
3505                 if (flags_unset) {
3506                         ht_data = g_hash_table_lookup(IMAP_FOLDER_ITEM(item)->flags_unset_table, 
3507                                 GINT_TO_POINTER(flags_unset));
3508                         if (ht_data == NULL) {
3509                                 ht_data = g_new0(hashtable_data, 1);
3510                                 ht_data->session = session;
3511                                 ht_data->item = IMAP_FOLDER_ITEM(item);
3512                                 g_hash_table_insert(IMAP_FOLDER_ITEM(item)->flags_unset_table, 
3513                                         GINT_TO_POINTER(flags_unset), ht_data);
3514                         }
3515                         if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
3516                                 ht_data->msglist = g_slist_prepend(ht_data->msglist, 
3517                                         GINT_TO_POINTER(msginfo->msgnum));              
3518                 }
3519         } else {
3520                 debug_print("IMAP changing flags\n");
3521                 if (flags_set) {
3522                         ok = imap_set_message_flags(session, &numlist, flags_set, TRUE);
3523                         if (ok != IMAP_SUCCESS) {
3524                                 unlock_session();
3525                                 return;
3526                         }
3527                 }
3528
3529                 if (flags_unset) {
3530                         ok = imap_set_message_flags(session, &numlist, flags_unset, FALSE);
3531                         if (ok != IMAP_SUCCESS) {
3532                                 unlock_session();
3533                                 return;
3534                         }
3535                 }
3536         }
3537         msginfo->flags.perm_flags = newflags;
3538         unlock_session();
3539         return;
3540 }
3541
3542 static gint imap_remove_msg(Folder *folder, FolderItem *item, gint uid)
3543 {
3544         gint ok;
3545         IMAPSession *session;
3546         gchar *dir;
3547         MsgNumberList numlist;
3548         
3549         g_return_val_if_fail(folder != NULL, -1);
3550         g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3551         g_return_val_if_fail(item != NULL, -1);
3552
3553         session = imap_session_get(folder);
3554         if (!session) return -1;
3555         lock_session();
3556         ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3557                          NULL, NULL, NULL, NULL, FALSE);
3558         if (ok != IMAP_SUCCESS) {
3559                 unlock_session();
3560                 return ok;
3561         }
3562         numlist.next = NULL;
3563         numlist.data = GINT_TO_POINTER(uid);
3564         
3565         ok = imap_set_message_flags
3566                 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
3567                 &numlist, IMAP_FLAG_DELETED, TRUE);
3568         if (ok != IMAP_SUCCESS) {
3569                 log_warning(_("can't set deleted flags: %d\n"), uid);
3570                 unlock_session();
3571                 return ok;
3572         }
3573
3574         if (!session->uidplus) {
3575                 ok = imap_cmd_expunge(session);
3576         } else {
3577                 gchar *uidstr;
3578
3579                 uidstr = g_strdup_printf("%u", uid);
3580                 ok = imap_cmd_expunge(session);
3581                 g_free(uidstr);
3582         }
3583         if (ok != IMAP_SUCCESS) {
3584                 log_warning(_("can't expunge\n"));
3585                 unlock_session();
3586                 return ok;
3587         }
3588
3589         IMAP_FOLDER_ITEM(item)->uid_list = g_slist_remove(
3590             IMAP_FOLDER_ITEM(item)->uid_list, numlist.data);
3591         dir = folder_item_get_path(item);
3592         if (is_dir_exist(dir))
3593                 remove_numbered_files(dir, uid, uid);
3594         g_free(dir);
3595         unlock_session();
3596         return IMAP_SUCCESS;
3597 }
3598
3599 static gint compare_msginfo(gconstpointer a, gconstpointer b)
3600 {
3601         return ((MsgInfo *)a)->msgnum - ((MsgInfo *)b)->msgnum;
3602 }
3603
3604 static guint gslist_find_next_num(MsgNumberList **list, guint num)
3605 {
3606         GSList *elem;
3607
3608         g_return_val_if_fail(list != NULL, -1);
3609
3610         for (elem = *list; elem != NULL; elem = g_slist_next(elem))
3611                 if (GPOINTER_TO_INT(elem->data) >= num)
3612                         break;
3613         *list = elem;
3614         return elem != NULL ? GPOINTER_TO_INT(elem->data) : (gint)-1;
3615 }
3616
3617 /*
3618  * NEW and DELETED flags are not syncronized
3619  * - The NEW/RECENT flags in IMAP folders can not really be directly
3620  *   modified by Sylpheed
3621  * - The DELETE/DELETED flag in IMAP and Sylpheed don't have the same
3622  *   meaning, in IMAP it always removes the messages from the FolderItem
3623  *   in Sylpheed it can mean to move the message to trash
3624  */
3625
3626 typedef struct _get_flags_data {
3627         Folder *folder;
3628         FolderItem *item;
3629         MsgInfoList *msginfo_list;
3630         GRelation *msgflags;
3631         gboolean full_search;
3632         gboolean done;
3633 } get_flags_data;
3634
3635 static /*gint*/ void *imap_get_flags_thread(void *data)
3636 {
3637         get_flags_data *stuff = (get_flags_data *)data;
3638         Folder *folder = stuff->folder;
3639         FolderItem *item = stuff->item;
3640         MsgInfoList *msginfo_list = stuff->msginfo_list;
3641         GRelation *msgflags = stuff->msgflags;
3642         gboolean full_search = stuff->full_search;
3643         IMAPSession *session;
3644         GSList *sorted_list = NULL;
3645         GSList *unseen = NULL, *answered = NULL, *flagged = NULL, *deleted = NULL;
3646         GSList *p_unseen, *p_answered, *p_flagged, *p_deleted;
3647         GSList *elem;
3648         GSList *seq_list, *cur;
3649         gboolean reverse_seen = FALSE;
3650         GString *cmd_buf;
3651         gint ok;
3652         gint exists_cnt, unseen_cnt;
3653         gboolean selected_folder;
3654         
3655         if (folder == NULL || item == NULL) {
3656                 stuff->done = TRUE;
3657                 return GINT_TO_POINTER(-1);
3658         }
3659
3660         session = imap_session_get(folder);
3661         if (session == NULL) {
3662                 stuff->done = TRUE;
3663                 return GINT_TO_POINTER(-1);
3664         }
3665         lock_session();
3666         selected_folder = (session->mbox != NULL) &&
3667                           (!strcmp(session->mbox, item->path));
3668
3669         if (!selected_folder) {
3670                 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3671                         &exists_cnt, NULL, &unseen_cnt, NULL, TRUE);
3672                 if (ok != IMAP_SUCCESS) {
3673                         stuff->done = TRUE;
3674                         unlock_session();
3675                         return GINT_TO_POINTER(-1);
3676                 }
3677
3678                 if (unseen_cnt > exists_cnt / 2)
3679                         reverse_seen = TRUE;
3680         } 
3681         else {
3682                 if (item->unread_msgs > item->total_msgs / 2)
3683                         reverse_seen = TRUE;
3684         }
3685
3686         cmd_buf = g_string_new(NULL);
3687
3688         sorted_list = g_slist_sort(g_slist_copy(msginfo_list), compare_msginfo);
3689         if (!full_search) {
3690                 seq_list = imap_get_lep_set_from_msglist(msginfo_list);
3691         } else {
3692                 struct mailimap_set * set;
3693                 set = mailimap_set_new_interval(1, 0);
3694                 seq_list = g_slist_append(NULL, set);
3695         }
3696
3697         for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
3698                 struct mailimap_set * imapset;
3699                 clist * lep_uidlist;
3700                 int r;
3701                 
3702                 imapset = cur->data;
3703                 if (reverse_seen) {
3704                         r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SEEN,
3705                                                  full_search ? NULL:imapset, &lep_uidlist);
3706                 }
3707                 else {
3708                         r = imap_threaded_search(folder,
3709                                                  IMAP_SEARCH_TYPE_UNSEEN,
3710                                                  full_search ? NULL:imapset, &lep_uidlist);
3711                 }
3712                 if (r == MAILIMAP_NO_ERROR) {
3713                         GSList * uidlist;
3714                         
3715                         uidlist = imap_uid_list_from_lep(lep_uidlist);
3716                         mailimap_search_result_free(lep_uidlist);
3717                         
3718                         unseen = g_slist_concat(unseen, uidlist);
3719                 }
3720                 
3721                 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_FLAGGED,
3722                                          full_search ? NULL:imapset, &lep_uidlist);
3723                 if (r == MAILIMAP_NO_ERROR) {
3724                         GSList * uidlist;
3725
3726                         uidlist = imap_uid_list_from_lep(lep_uidlist);
3727                         mailimap_search_result_free(lep_uidlist);
3728
3729                         flagged = g_slist_concat(flagged, uidlist);
3730                 }
3731
3732                 if (item->opened || item->processing_pending || item == folder->inbox) {
3733                         r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_ANSWERED,
3734                                                  full_search ? NULL:imapset, &lep_uidlist);
3735                         if (r == MAILIMAP_NO_ERROR) {
3736                                 GSList * uidlist;
3737
3738                                 uidlist = imap_uid_list_from_lep(lep_uidlist);
3739                                 mailimap_search_result_free(lep_uidlist);
3740
3741                                 answered = g_slist_concat(answered, uidlist);
3742                         }
3743
3744                         r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_DELETED,
3745                                                  full_search ? NULL:imapset, &lep_uidlist);
3746                         if (r == MAILIMAP_NO_ERROR) {
3747                                 GSList * uidlist;
3748
3749                                 uidlist = imap_uid_list_from_lep(lep_uidlist);
3750                                 mailimap_search_result_free(lep_uidlist);
3751
3752                                 deleted = g_slist_concat(deleted, uidlist);
3753                         }
3754                 }
3755         }
3756
3757         p_unseen = unseen;
3758         p_answered = answered;
3759         p_flagged = flagged;
3760         p_deleted = deleted;
3761
3762         for (elem = sorted_list; elem != NULL; elem = g_slist_next(elem)) {
3763                 MsgInfo *msginfo;
3764                 MsgPermFlags flags;
3765                 gboolean wasnew;
3766                 
3767                 msginfo = (MsgInfo *) elem->data;
3768                 flags = msginfo->flags.perm_flags;
3769                 wasnew = (flags & MSG_NEW);
3770                 if (item->opened || item->processing_pending || item == folder->inbox) {
3771                         flags &= ~((reverse_seen ? 0 : MSG_UNREAD | MSG_NEW) | MSG_REPLIED | MSG_MARKED);
3772                 } else {
3773                         flags &= ~((reverse_seen ? 0 : MSG_UNREAD | MSG_NEW | MSG_MARKED));
3774                 }
3775                 if (reverse_seen)
3776                         flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
3777                 if (gslist_find_next_num(&p_unseen, msginfo->msgnum) == msginfo->msgnum) {
3778                         if (!reverse_seen) {
3779                                 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
3780                         } else {
3781                                 flags &= ~(MSG_UNREAD | MSG_NEW);
3782                         }
3783                 }
3784                 
3785                 if (gslist_find_next_num(&p_flagged, msginfo->msgnum) == msginfo->msgnum)
3786                         flags |= MSG_MARKED;
3787                 else
3788                         flags &= ~MSG_MARKED;
3789
3790                 if (item->opened || item->processing_pending || item == folder->inbox) {
3791                         if (gslist_find_next_num(&p_answered, msginfo->msgnum) == msginfo->msgnum)
3792                                 flags |= MSG_REPLIED;
3793                         else
3794                                 flags &= ~MSG_REPLIED;
3795                         if (gslist_find_next_num(&p_deleted, msginfo->msgnum) == msginfo->msgnum)
3796                                 flags |= MSG_DELETED;
3797                         else
3798                                 flags &= ~MSG_DELETED;
3799                 }
3800                 g_relation_insert(msgflags, msginfo, GINT_TO_POINTER(flags));
3801         }
3802
3803         imap_lep_set_free(seq_list);
3804         g_slist_free(flagged);
3805         g_slist_free(deleted);
3806         g_slist_free(answered);
3807         g_slist_free(unseen);
3808         g_slist_free(sorted_list);
3809         g_string_free(cmd_buf, TRUE);
3810
3811         stuff->done = TRUE;
3812         unlock_session();
3813         return GINT_TO_POINTER(0);
3814 }
3815
3816 static gint imap_get_flags(Folder *folder, FolderItem *item,
3817                            MsgInfoList *msginfo_list, GRelation *msgflags)
3818 {
3819         gint result;
3820         get_flags_data *data = g_new0(get_flags_data, 1);
3821         data->done = FALSE;
3822         data->folder = folder;
3823         data->item = item;
3824         data->msginfo_list = msginfo_list;
3825         data->msgflags = msgflags;
3826         data->full_search = FALSE;
3827
3828         GSList *tmp = NULL, *cur;
3829         
3830         if (prefs_common.work_offline && 
3831             !inc_offline_should_override(
3832                 _("Sylpheed-Claws needs network access in order "
3833                   "to access the IMAP server."))) {
3834                 g_free(data);
3835                 return -1;
3836         }
3837
3838         tmp = folder_item_get_msg_list(item);
3839
3840         if (g_slist_length(tmp) <= g_slist_length(msginfo_list))
3841                 data->full_search = TRUE;
3842         
3843         for (cur = tmp; cur; cur = cur->next)
3844                 procmsg_msginfo_free((MsgInfo *)cur->data);
3845         
3846         g_slist_free(tmp);
3847
3848         result = GPOINTER_TO_INT(imap_get_flags_thread(data));
3849         
3850         g_free(data);
3851         return result;
3852
3853 }
3854
3855 static gboolean process_flags(gpointer key, gpointer value, gpointer user_data)
3856 {
3857         gboolean flags_set = GPOINTER_TO_INT(user_data);
3858         gint flags_value = GPOINTER_TO_INT(key);
3859         hashtable_data *data = (hashtable_data *)value;
3860         IMAPFolderItem *_item = data->item;
3861         FolderItem *item = (FolderItem *)_item;
3862         gint ok = IMAP_ERROR;
3863         IMAPSession *session = imap_session_get(item->folder);
3864
3865         data->msglist = g_slist_reverse(data->msglist);
3866         
3867         debug_print("IMAP %ssetting flags to %d for %d messages\n",
3868                 flags_set?"":"un",
3869                 flags_value,
3870                 g_slist_length(data->msglist));
3871         
3872         if (session) {
3873                 lock_session();
3874                 ok = imap_select(session, IMAP_FOLDER(item->folder), item->path,
3875                          NULL, NULL, NULL, NULL, FALSE);
3876         }
3877         if (ok == IMAP_SUCCESS) {
3878                 imap_set_message_flags(data->session, data->msglist, flags_value, flags_set);
3879         } else {
3880                 g_warning("can't select mailbox %s\n", item->path);
3881         }
3882         if (session)
3883                 unlock_session();
3884         g_slist_free(data->msglist);    
3885         g_free(data);
3886         return TRUE;
3887 }
3888
3889 static void process_hashtable(IMAPFolderItem *item)
3890 {
3891         if (item->flags_set_table) {
3892                 g_hash_table_foreach_remove(item->flags_set_table, process_flags, GINT_TO_POINTER(TRUE));
3893                 g_hash_table_destroy(item->flags_set_table);
3894                 item->flags_set_table = NULL;
3895         }
3896         if (item->flags_unset_table) {
3897                 g_hash_table_foreach_remove(item->flags_unset_table, process_flags, GINT_TO_POINTER(FALSE));
3898                 g_hash_table_destroy(item->flags_unset_table);
3899                 item->flags_unset_table = NULL;
3900         }
3901 }
3902
3903 static void imap_set_batch (Folder *folder, FolderItem *_item, gboolean batch)
3904 {
3905         IMAPFolderItem *item = (IMAPFolderItem *)_item;
3906
3907         g_return_if_fail(item != NULL);
3908         
3909         if (item->batching == batch)
3910                 return;
3911         
3912         if (batch) {
3913                 item->batching = TRUE;
3914                 debug_print("IMAP switching to batch mode\n");
3915                 if (!item->flags_set_table) {
3916                         item->flags_set_table = g_hash_table_new(NULL, g_direct_equal);
3917                 }
3918                 if (!item->flags_unset_table) {
3919                         item->flags_unset_table = g_hash_table_new(NULL, g_direct_equal);
3920                 }
3921         } else {
3922                 debug_print("IMAP switching away from batch mode\n");
3923                 /* process stuff */
3924                 process_hashtable(item);
3925                 item->batching = FALSE;
3926         }
3927 }
3928
3929
3930
3931 /* data types conversion libetpan <-> sylpheed */
3932
3933
3934
3935 #define ETPAN_IMAP_MB_MARKED      1
3936 #define ETPAN_IMAP_MB_UNMARKED    2
3937 #define ETPAN_IMAP_MB_NOSELECT    4
3938 #define ETPAN_IMAP_MB_NOINFERIORS 8
3939
3940 static int imap_flags_to_flags(struct mailimap_mbx_list_flags * imap_flags)
3941 {
3942   int flags;
3943   clistiter * cur;
3944   
3945   flags = 0;
3946   if (imap_flags->mbf_type == MAILIMAP_MBX_LIST_FLAGS_SFLAG) {
3947     switch (imap_flags->mbf_sflag) {
3948     case MAILIMAP_MBX_LIST_SFLAG_MARKED:
3949       flags |= ETPAN_IMAP_MB_MARKED;
3950       break;
3951     case MAILIMAP_MBX_LIST_SFLAG_NOSELECT:
3952       flags |= ETPAN_IMAP_MB_NOSELECT;
3953       break;
3954     case MAILIMAP_MBX_LIST_SFLAG_UNMARKED:
3955       flags |= ETPAN_IMAP_MB_UNMARKED;
3956       break;
3957     }
3958   }
3959   
3960   for(cur = clist_begin(imap_flags->mbf_oflags) ; cur != NULL ;
3961       cur = clist_next(cur)) {
3962     struct mailimap_mbx_list_oflag * oflag;
3963     
3964     oflag = clist_content(cur);
3965     
3966     switch (oflag->of_type) {
3967     case MAILIMAP_MBX_LIST_OFLAG_NOINFERIORS:
3968       flags |= ETPAN_IMAP_MB_NOINFERIORS;
3969       break;
3970     }
3971   }
3972   
3973   return flags;
3974 }
3975
3976 static GSList * imap_list_from_lep(IMAPFolder * folder,
3977                                    clist * list, const gchar * real_path, gboolean all)
3978 {
3979         clistiter * iter;
3980         GSList * item_list = NULL, *llast = NULL;
3981         
3982         for(iter = clist_begin(list) ; iter != NULL ;
3983             iter = clist_next(iter)) {
3984                 struct mailimap_mailbox_list * mb;
3985                 int flags;
3986                 char delimiter;
3987                 char * name;
3988                 char * dup_name;
3989                 gchar * base;
3990                 gchar * loc_name;
3991                 gchar * loc_path;
3992                 FolderItem *new_item;
3993                 
3994                 mb = clist_content(iter);
3995
3996                 if (mb == NULL)
3997                         continue;
3998
3999                 flags = 0;
4000                 if (mb->mb_flag != NULL)
4001                         flags = imap_flags_to_flags(mb->mb_flag);
4002                 
4003                 delimiter = mb->mb_delimiter;
4004                 name = mb->mb_name;
4005                 
4006                 dup_name = strdup(name);                
4007                 if (delimiter != '\0')
4008                         subst_char(dup_name, delimiter, '/');
4009                 
4010                 base = g_path_get_basename(dup_name);
4011                 if (base[0] == '.') {
4012                         g_free(base);
4013                         free(dup_name);
4014                         continue;
4015                 }
4016                 
4017                 if (!all && strcmp(dup_name, real_path) == 0) {
4018                         g_free(base);
4019                         free(dup_name);
4020                         continue;
4021                 }
4022
4023                 if (!all && dup_name[strlen(dup_name)-1] == '/') {
4024                         g_free(base);
4025                         free(dup_name);
4026                         continue;
4027                 }
4028                 
4029                 loc_name = imap_modified_utf7_to_utf8(base);
4030                 loc_path = imap_modified_utf7_to_utf8(dup_name);
4031                 
4032                 new_item = folder_item_new(FOLDER(folder), loc_name, loc_path);
4033                 if ((flags & ETPAN_IMAP_MB_NOINFERIORS) != 0)
4034                         new_item->no_sub = TRUE;
4035                 if (strcmp(dup_name, "INBOX") != 0 &&
4036                     ((flags & ETPAN_IMAP_MB_NOSELECT) != 0))
4037                         new_item->no_select = TRUE;
4038                 
4039                 if (item_list == NULL)
4040                         llast = item_list = g_slist_append(item_list, new_item);
4041                 else {
4042                         llast = g_slist_append(llast, new_item);
4043                         llast = llast->next;
4044                 }
4045                 debug_print("folder '%s' found.\n", loc_path);
4046                 g_free(base);
4047                 g_free(loc_path);
4048                 g_free(loc_name);
4049                 
4050                 free(dup_name);
4051         }
4052         
4053         return item_list;
4054 }
4055
4056 static GSList * imap_get_lep_set_from_numlist(MsgNumberList *numlist)
4057 {
4058         GSList *sorted_list, *cur;
4059         guint first, last, next;
4060         GSList *ret_list = NULL, *llast = NULL;
4061         unsigned int count;
4062         struct mailimap_set * current_set;
4063         unsigned int item_count;
4064         
4065         if (numlist == NULL)
4066                 return NULL;
4067         
4068         count = 0;
4069         current_set = mailimap_set_new_empty();
4070         
4071         sorted_list = g_slist_copy(numlist);
4072         sorted_list = g_slist_sort(sorted_list, g_int_compare);
4073
4074         first = GPOINTER_TO_INT(sorted_list->data);
4075         
4076         item_count = 0;
4077         for (cur = sorted_list; cur != NULL; cur = g_slist_next(cur)) {
4078                 if (GPOINTER_TO_INT(cur->data) == 0)
4079                         continue;
4080                 
4081                 item_count ++;
4082
4083                 last = GPOINTER_TO_INT(cur->data);
4084                 if (cur->next)
4085                         next = GPOINTER_TO_INT(cur->next->data);
4086                 else
4087                         next = 0;
4088
4089                 if (last + 1 != next || next == 0) {
4090
4091                         struct mailimap_set_item * item;
4092                         item = mailimap_set_item_new(first, last);
4093                         mailimap_set_add(current_set, item);
4094                         count ++;
4095                         
4096                         first = next;
4097                         
4098                         if (count >= IMAP_SET_MAX_COUNT) {
4099                                 if (ret_list == NULL)
4100                                         llast = ret_list = g_slist_append(ret_list,
4101                                                           current_set);
4102                                 else {
4103                                         llast = g_slist_append(llast, current_set);
4104                                         llast = llast->next;
4105                                 }
4106                                 current_set = mailimap_set_new_empty();
4107                                 count = 0;
4108                                 item_count = 0;
4109                         }
4110                 }
4111         }
4112         
4113         if (clist_count(current_set->set_list) > 0) {
4114                 ret_list = g_slist_append(ret_list,
4115                                           current_set);
4116         }
4117         
4118         g_slist_free(sorted_list);
4119
4120         return ret_list;
4121 }
4122
4123 static GSList * imap_get_lep_set_from_msglist(MsgInfoList *msglist)
4124 {
4125         MsgNumberList *numlist = NULL;
4126         MsgInfoList *cur;
4127         GSList *seq_list;
4128
4129         for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
4130                 MsgInfo *msginfo = (MsgInfo *) cur->data;
4131
4132                 numlist = g_slist_prepend(numlist, GINT_TO_POINTER(msginfo->msgnum));
4133         }
4134         numlist = g_slist_reverse(numlist);
4135         seq_list = imap_get_lep_set_from_numlist(numlist);
4136         g_slist_free(numlist);
4137
4138         return seq_list;
4139 }
4140
4141 static GSList * imap_uid_list_from_lep(clist * list)
4142 {
4143         clistiter * iter;
4144         GSList * result;
4145         
4146         result = NULL;
4147         
4148         for(iter = clist_begin(list) ; iter != NULL ;
4149             iter = clist_next(iter)) {
4150                 uint32_t * puid;
4151                 
4152                 puid = clist_content(iter);
4153                 result = g_slist_prepend(result, GINT_TO_POINTER(* puid));
4154         }
4155         
4156         result = g_slist_reverse(result);
4157         return result;
4158 }
4159
4160 static GSList * imap_uid_list_from_lep_tab(carray * list)
4161 {
4162         unsigned int i;
4163         GSList * result;
4164         
4165         result = NULL;
4166         
4167         for(i = 0 ; i < carray_count(list) ; i ++) {
4168                 uint32_t * puid;
4169                 
4170                 puid = carray_get(list, i);
4171                 result = g_slist_prepend(result, GINT_TO_POINTER(* puid));
4172         }
4173         result = g_slist_reverse(result);
4174         return result;
4175 }
4176
4177 static MsgInfo *imap_envelope_from_lep(struct imap_fetch_env_info * info,
4178                                        FolderItem *item)
4179 {
4180         MsgInfo *msginfo = NULL;
4181         guint32 uid = 0;
4182         size_t size = 0;
4183         MsgFlags flags = {0, 0};
4184         
4185         if (info->headers == NULL)
4186                 return NULL;
4187
4188         MSG_SET_TMP_FLAGS(flags, MSG_IMAP);
4189         if (folder_has_parent_of_type(item, F_QUEUE)) {
4190                 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
4191         } else if (folder_has_parent_of_type(item, F_DRAFT)) {
4192                 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
4193         }
4194         flags.perm_flags = info->flags;
4195         
4196         uid = info->uid;
4197         size = info->size;
4198         msginfo = procheader_parse_str(info->headers, flags, FALSE, FALSE);
4199         
4200         if (msginfo) {
4201                 msginfo->msgnum = uid;
4202                 msginfo->size = size;
4203         }
4204
4205         return msginfo;
4206 }
4207
4208 static void imap_lep_set_free(GSList *seq_list)
4209 {
4210         GSList * cur;
4211         
4212         for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) {
4213                 struct mailimap_set * imapset;
4214                 
4215                 imapset = cur->data;
4216                 mailimap_set_free(imapset);
4217         }
4218         g_slist_free(seq_list);
4219 }
4220
4221 static struct mailimap_flag_list * imap_flag_to_lep(IMAPFlags flags)
4222 {
4223         struct mailimap_flag_list * flag_list;
4224         
4225         flag_list = mailimap_flag_list_new_empty();
4226         
4227         if (IMAP_IS_SEEN(flags))
4228                 mailimap_flag_list_add(flag_list,
4229                                        mailimap_flag_new_seen());
4230         if (IMAP_IS_ANSWERED(flags))
4231                 mailimap_flag_list_add(flag_list,
4232                                        mailimap_flag_new_answered());
4233         if (IMAP_IS_FLAGGED(flags))
4234                 mailimap_flag_list_add(flag_list,
4235                                        mailimap_flag_new_flagged());
4236         if (IMAP_IS_DELETED(flags))
4237                 mailimap_flag_list_add(flag_list,
4238                                        mailimap_flag_new_deleted());
4239         if (IMAP_IS_DRAFT(flags))
4240                 mailimap_flag_list_add(flag_list,
4241                                        mailimap_flag_new_draft());
4242         
4243         return flag_list;
4244 }
4245
4246 guint imap_folder_get_refcnt(Folder *folder)
4247 {
4248         return ((IMAPFolder *)folder)->refcnt;
4249 }
4250
4251 void imap_folder_ref(Folder *folder)
4252 {
4253         ((IMAPFolder *)folder)->refcnt++;
4254 }
4255
4256 void imap_disconnect_all(void)
4257 {
4258         GList *list;
4259         for (list = account_get_list(); list != NULL; list = list->next) {
4260                 PrefsAccount *account = list->data;
4261                 if (account->protocol == A_IMAP4) {
4262                         RemoteFolder *folder = (RemoteFolder *)account->folder;
4263                         if (folder && folder->session) {
4264                                 IMAPSession *session = (IMAPSession *)folder->session;
4265                                 imap_threaded_disconnect(FOLDER(folder));
4266                                 session_destroy(session);
4267                                 folder->session = NULL;
4268                         }
4269                 }
4270         }
4271 }
4272
4273 void imap_folder_unref(Folder *folder)
4274 {
4275         if (((IMAPFolder *)folder)->refcnt > 0)
4276                 ((IMAPFolder *)folder)->refcnt--;
4277 }
4278
4279 #else /* HAVE_LIBETPAN */
4280
4281 static FolderClass imap_class;
4282
4283 static XMLTag *imap_item_get_xml(Folder *folder, FolderItem *item);
4284 static void imap_item_set_xml(Folder *folder, FolderItem *item, XMLTag *tag);
4285
4286 static Folder   *imap_folder_new        (const gchar    *name,
4287                                          const gchar    *path)
4288 {
4289         return NULL;
4290 }
4291 static gint     imap_create_tree        (Folder         *folder)
4292 {
4293         return -1;
4294 }
4295 static FolderItem *imap_create_folder   (Folder         *folder,
4296                                          FolderItem     *parent,
4297                                          const gchar    *name)
4298 {
4299         return NULL;
4300 }
4301 static gint     imap_rename_folder      (Folder         *folder,
4302                                          FolderItem     *item, 
4303                                          const gchar    *name)
4304 {
4305         return -1;
4306 }
4307
4308 gchar imap_get_path_separator_for_item(FolderItem *item)
4309 {
4310         return '/';
4311 }
4312
4313 FolderClass *imap_get_class(void)
4314 {
4315         if (imap_class.idstr == NULL) {
4316                 imap_class.type = F_IMAP;
4317                 imap_class.idstr = "imap";
4318                 imap_class.uistr = "IMAP4";
4319
4320                 imap_class.new_folder = imap_folder_new;
4321                 imap_class.create_tree = imap_create_tree;
4322                 imap_class.create_folder = imap_create_folder;
4323                 imap_class.rename_folder = imap_rename_folder;
4324
4325                 imap_class.set_xml = folder_set_xml;
4326                 imap_class.get_xml = folder_get_xml;
4327                 imap_class.item_set_xml = imap_item_set_xml;
4328                 imap_class.item_get_xml = imap_item_get_xml;
4329                 /* nothing implemented */
4330         }
4331
4332         return &imap_class;
4333 }
4334
4335 void imap_disconnect_all(void)
4336 {
4337 }
4338
4339 #endif
4340
4341 void imap_synchronise(FolderItem *item) 
4342 {
4343         imap_gtk_synchronise(item);
4344 }
4345
4346 static void imap_item_set_xml(Folder *folder, FolderItem *item, XMLTag *tag)
4347 {
4348 #ifdef HAVE_LIBETPAN
4349         GList *cur;
4350 #endif
4351         folder_item_set_xml(folder, item, tag);
4352         
4353 #ifdef HAVE_LIBETPAN
4354         for (cur = tag->attr; cur != NULL; cur = g_list_next(cur)) {
4355                 XMLAttr *attr = (XMLAttr *) cur->data;
4356
4357                 if (!attr || !attr->name || !attr->value) continue;
4358                 if (!strcmp(attr->name, "uidnext"))
4359                         IMAP_FOLDER_ITEM(item)->uid_next = atoi(attr->value);
4360         }
4361 #endif
4362 }
4363
4364 static XMLTag *imap_item_get_xml(Folder *folder, FolderItem *item)
4365 {
4366         XMLTag *tag;
4367
4368         tag = folder_item_get_xml(folder, item);
4369
4370 #ifdef HAVE_LIBETPAN
4371         xml_tag_add_attr(tag, xml_attr_new_int("uidnext", 
4372                         IMAP_FOLDER_ITEM(item)->uid_next));
4373
4374 #endif
4375         return tag;
4376 }