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