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