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