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