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