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