2006-06-19 [colin] 2.3.0cvs23
[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
1127                 g_free(real_file);
1128         }
1129         statusbar_progress_all(0,0,0);
1130         statusbar_pop_all();
1131         
1132         imap_cmd_expunge(session);
1133         unlock_session();
1134         
1135         g_free(destdir);
1136
1137         return last_uid;
1138 }
1139
1140 static gint imap_do_copy_msgs(Folder *folder, FolderItem *dest, 
1141                               MsgInfoList *msglist, GRelation *relation)
1142 {
1143         FolderItem *src;
1144         gchar *destdir;
1145         GSList *seq_list, *cur;
1146         MsgInfo *msginfo;
1147         IMAPSession *session;
1148         gint ok = IMAP_SUCCESS;
1149         GRelation *uid_mapping;
1150         gint last_num = 0;
1151
1152         g_return_val_if_fail(folder != NULL, -1);
1153         g_return_val_if_fail(dest != NULL, -1);
1154         g_return_val_if_fail(msglist != NULL, -1);
1155         
1156         session = imap_session_get(folder);
1157         
1158         if (!session) {
1159                 return -1;
1160         }
1161         lock_session();
1162         msginfo = (MsgInfo *)msglist->data;
1163
1164         src = msginfo->folder;
1165         if (src == dest) {
1166                 g_warning("the src folder is identical to the dest.\n");
1167                 unlock_session();
1168                 return -1;
1169         }
1170
1171         if (src->folder != dest->folder) {
1172                 GSList *infolist = NULL, *cur;
1173                 int res = -1;
1174                 for (cur = msglist; cur; cur = cur->next) {
1175                         msginfo = (MsgInfo *)cur->data;
1176                         MsgFileInfo *fileinfo = g_new0(MsgFileInfo, 1);
1177                         fileinfo->file = procmsg_get_message_file(msginfo);
1178                         fileinfo->flags = &(msginfo->flags);
1179                         infolist = g_slist_prepend(infolist, fileinfo);
1180                 }
1181                 infolist = g_slist_reverse(infolist);
1182                 res = folder_item_add_msgs(dest, infolist, FALSE);
1183                 for (cur = infolist; cur; cur = cur->next) {
1184                         MsgFileInfo *info = (MsgFileInfo *)cur->data;
1185                         g_free(info->file);
1186                         g_free(info);
1187                 }
1188                 g_slist_free(infolist);
1189                 return res;
1190         } 
1191
1192         ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
1193                          NULL, NULL, NULL, NULL, FALSE);
1194         if (ok != IMAP_SUCCESS) {
1195                 unlock_session();
1196                 return ok;
1197         }
1198
1199         destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
1200         seq_list = imap_get_lep_set_from_msglist(msglist);
1201         uid_mapping = g_relation_new(2);
1202         g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
1203         
1204         statusbar_print_all(_("Copying messages..."));
1205         for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
1206                 struct mailimap_set * seq_set;
1207                 seq_set = cur->data;
1208
1209                 debug_print("Copying messages from %s to %s ...\n",
1210                             src->path, destdir);
1211
1212                 ok = imap_cmd_copy(session, seq_set, destdir, uid_mapping);
1213                 if (ok != IMAP_SUCCESS) {
1214                         g_relation_destroy(uid_mapping);
1215                         imap_lep_set_free(seq_list);
1216                         unlock_session();
1217                         return -1;
1218                 }
1219         }
1220
1221         for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
1222                 MsgInfo *msginfo = (MsgInfo *)cur->data;
1223                 GTuples *tuples;
1224
1225                 tuples = g_relation_select(uid_mapping, 
1226                                            GINT_TO_POINTER(msginfo->msgnum),
1227                                            0);
1228                 if (tuples->len > 0) {
1229                         gint num = GPOINTER_TO_INT(g_tuples_index(tuples, 0, 1));
1230                         g_relation_insert(relation, msginfo,
1231                                           GPOINTER_TO_INT(num));
1232                         if (num > last_num)
1233                                 last_num = num;
1234                 } else
1235                         g_relation_insert(relation, msginfo,
1236                                           GPOINTER_TO_INT(0));
1237                 g_tuples_destroy(tuples);
1238         }
1239         statusbar_pop_all();
1240
1241         g_relation_destroy(uid_mapping);
1242         imap_lep_set_free(seq_list);
1243
1244         g_free(destdir);
1245         
1246         IMAP_FOLDER_ITEM(dest)->lastuid = 0;
1247         IMAP_FOLDER_ITEM(dest)->uid_next = 0;
1248         g_slist_free(IMAP_FOLDER_ITEM(dest)->uid_list);
1249         IMAP_FOLDER_ITEM(dest)->uid_list = NULL;
1250
1251         unlock_session();
1252         if (ok == IMAP_SUCCESS)
1253                 return last_num;
1254         else
1255                 return -1;
1256 }
1257
1258 static gint imap_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1259 {
1260         GSList msglist;
1261
1262         g_return_val_if_fail(msginfo != NULL, -1);
1263
1264         msglist.data = msginfo;
1265         msglist.next = NULL;
1266
1267         return imap_copy_msgs(folder, dest, &msglist, NULL);
1268 }
1269
1270 static gint imap_copy_msgs(Folder *folder, FolderItem *dest, 
1271                     MsgInfoList *msglist, GRelation *relation)
1272 {
1273         MsgInfo *msginfo;
1274         gint ret;
1275
1276         g_return_val_if_fail(folder != NULL, -1);
1277         g_return_val_if_fail(dest != NULL, -1);
1278         g_return_val_if_fail(msglist != NULL, -1);
1279
1280         msginfo = (MsgInfo *)msglist->data;
1281         g_return_val_if_fail(msginfo->folder != NULL, -1);
1282
1283         ret = imap_do_copy_msgs(folder, dest, msglist, relation);
1284         return ret;
1285 }
1286
1287
1288 static gint imap_do_remove_msgs(Folder *folder, FolderItem *dest, 
1289                                 MsgInfoList *msglist, GRelation *relation)
1290 {
1291         gchar *destdir, *dir;
1292         GSList *numlist = NULL, *cur;
1293         MsgInfo *msginfo;
1294         IMAPSession *session;
1295         gint ok = IMAP_SUCCESS;
1296         GRelation *uid_mapping;
1297         
1298         g_return_val_if_fail(folder != NULL, -1);
1299         g_return_val_if_fail(dest != NULL, -1);
1300         g_return_val_if_fail(msglist != NULL, -1);
1301
1302         session = imap_session_get(folder);
1303         if (!session) {
1304                 return -1;
1305         }
1306         lock_session();
1307         msginfo = (MsgInfo *)msglist->data;
1308
1309         ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
1310                          NULL, NULL, NULL, NULL, FALSE);
1311         if (ok != IMAP_SUCCESS) {
1312                 unlock_session();
1313                 return ok;
1314         }
1315
1316         destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
1317         for (cur = msglist; cur; cur = cur->next) {
1318                 msginfo = (MsgInfo *)cur->data;
1319                 if (!MSG_IS_DELETED(msginfo->flags))
1320                         numlist = g_slist_prepend(numlist, GINT_TO_POINTER(msginfo->msgnum));
1321         }
1322         numlist = g_slist_reverse(numlist);
1323
1324         uid_mapping = g_relation_new(2);
1325         g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
1326
1327         ok = imap_set_message_flags
1328                 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
1329                 numlist, IMAP_FLAG_DELETED, TRUE);
1330         if (ok != IMAP_SUCCESS) {
1331                 log_warning(_("can't set deleted flags\n"));
1332                 unlock_session();
1333                 return ok;
1334         }
1335         ok = imap_cmd_expunge(session);
1336         if (ok != IMAP_SUCCESS) {
1337                 log_warning(_("can't expunge\n"));
1338                 unlock_session();
1339                 return ok;
1340         }
1341         
1342         dir = folder_item_get_path(msginfo->folder);
1343         if (is_dir_exist(dir)) {
1344                 for (cur = msglist; cur; cur = cur->next) {
1345                         msginfo = (MsgInfo *)cur->data;
1346                         remove_numbered_files(dir, msginfo->msgnum, msginfo->msgnum);
1347                 }
1348         }
1349         g_free(dir);
1350
1351         g_relation_destroy(uid_mapping);
1352         g_slist_free(numlist);
1353
1354         g_free(destdir);
1355         unlock_session();
1356         if (ok == IMAP_SUCCESS)
1357                 return 0;
1358         else
1359                 return -1;
1360 }
1361
1362 static gint imap_remove_msgs(Folder *folder, FolderItem *dest, 
1363                     MsgInfoList *msglist, GRelation *relation)
1364 {
1365         MsgInfo *msginfo;
1366
1367         g_return_val_if_fail(folder != NULL, -1);
1368         g_return_val_if_fail(dest != NULL, -1);
1369         if (msglist == NULL)
1370                 return 0;
1371
1372         msginfo = (MsgInfo *)msglist->data;
1373         g_return_val_if_fail(msginfo->folder != NULL, -1);
1374
1375         return imap_do_remove_msgs(folder, dest, msglist, relation);
1376 }
1377
1378 static gint imap_remove_all_msg(Folder *folder, FolderItem *item)
1379 {
1380         GSList *list = folder_item_get_msg_list(item);
1381         gint res = imap_remove_msgs(folder, item, list, NULL);
1382         procmsg_msg_list_free(list);
1383         return res;
1384 }
1385
1386 static gboolean imap_is_msg_changed(Folder *folder, FolderItem *item,
1387                                     MsgInfo *msginfo)
1388 {
1389         /* TODO: properly implement this method */
1390         return FALSE;
1391 }
1392
1393 static gint imap_close(Folder *folder, FolderItem *item)
1394 {
1395         return 0;
1396 }
1397
1398 static gint imap_scan_tree(Folder *folder)
1399 {
1400         FolderItem *item = NULL;
1401         IMAPSession *session;
1402         gchar *root_folder = NULL;
1403
1404         g_return_val_if_fail(folder != NULL, -1);
1405         g_return_val_if_fail(folder->account != NULL, -1);
1406
1407         session = imap_session_get(folder);
1408         if (!session) {
1409                 if (!folder->node) {
1410                         folder_tree_destroy(folder);
1411                         item = folder_item_new(folder, folder->name, NULL);
1412                         item->folder = folder;
1413                         folder->node = item->node = g_node_new(item);
1414                 }
1415                 return -1;
1416         }
1417
1418         lock_session();
1419         if (folder->account->imap_dir && *folder->account->imap_dir) {
1420                 gchar *real_path;
1421                 int r;
1422                 clist * lep_list;
1423
1424                 Xstrdup_a(root_folder, folder->account->imap_dir, {unlock_session();return -1;});
1425                 extract_quote(root_folder, '"');
1426                 subst_char(root_folder,
1427                            imap_get_path_separator(IMAP_FOLDER(folder),
1428                                                    root_folder),
1429                            '/');
1430                 strtailchomp(root_folder, '/');
1431                 real_path = imap_get_real_path
1432                         (IMAP_FOLDER(folder), root_folder);
1433                 debug_print("IMAP root directory: %s\n", real_path);
1434
1435                 /* check if root directory exist */
1436
1437                 r = imap_threaded_list(session->folder, "", real_path,
1438                                        &lep_list);
1439                 if ((r != MAILIMAP_NO_ERROR) || (clist_count(lep_list) == 0)) {
1440                         if (!folder->node) {
1441                                 item = folder_item_new(folder, folder->name, NULL);
1442                                 item->folder = folder;
1443                                 folder->node = item->node = g_node_new(item);
1444                         }
1445                         unlock_session();
1446                         return -1;
1447                 }
1448                 mailimap_list_result_free(lep_list);
1449                 
1450                 g_free(real_path);
1451         }
1452
1453         if (folder->node)
1454                 item = FOLDER_ITEM(folder->node->data);
1455         if (!item || ((item->path || root_folder) &&
1456                       strcmp2(item->path, root_folder) != 0)) {
1457                 folder_tree_destroy(folder);
1458                 item = folder_item_new(folder, folder->name, root_folder);
1459                 item->folder = folder;
1460                 folder->node = item->node = g_node_new(item);
1461         }
1462
1463         imap_scan_tree_recursive(session, FOLDER_ITEM(folder->node->data));
1464         imap_create_missing_folders(folder);
1465         unlock_session();
1466
1467         return 0;
1468 }
1469
1470 static gint imap_scan_tree_recursive(IMAPSession *session, FolderItem *item)
1471 {
1472         Folder *folder;
1473         IMAPFolder *imapfolder;
1474         FolderItem *new_item;
1475         GSList *item_list, *cur;
1476         GNode *node;
1477         gchar *real_path;
1478         gchar *wildcard_path;
1479         gchar separator;
1480         gchar wildcard[3];
1481         clist * lep_list;
1482         int r;
1483         
1484         g_return_val_if_fail(item != NULL, -1);
1485         g_return_val_if_fail(item->folder != NULL, -1);
1486         g_return_val_if_fail(item->no_sub == FALSE, -1);
1487
1488         folder = item->folder;
1489         imapfolder = IMAP_FOLDER(folder);
1490
1491         separator = imap_get_path_separator(imapfolder, item->path);
1492
1493         if (folder->ui_func)
1494                 folder->ui_func(folder, item, folder->ui_func_data);
1495
1496         if (item->path) {
1497                 wildcard[0] = separator;
1498                 wildcard[1] = '%';
1499                 wildcard[2] = '\0';
1500                 real_path = imap_get_real_path(imapfolder, item->path);
1501         } else {
1502                 wildcard[0] = '%';
1503                 wildcard[1] = '\0';
1504                 real_path = g_strdup("");
1505         }
1506
1507         Xstrcat_a(wildcard_path, real_path, wildcard,
1508                   {g_free(real_path); return IMAP_ERROR;});
1509         lep_list = NULL;
1510         r = imap_threaded_list(folder, "", wildcard_path, &lep_list);
1511         if (r != MAILIMAP_NO_ERROR) {
1512                 item_list = NULL;
1513         }
1514         else {
1515                 item_list = imap_list_from_lep(imapfolder,
1516                                                lep_list, real_path, FALSE);
1517                 mailimap_list_result_free(lep_list);
1518         }
1519         
1520         g_free(real_path);
1521
1522         node = item->node->children;
1523         while (node != NULL) {
1524                 FolderItem *old_item = FOLDER_ITEM(node->data);
1525                 GNode *next = node->next;
1526
1527                 new_item = NULL;
1528                 for (cur = item_list; cur != NULL; cur = cur->next) {
1529                         FolderItem *cur_item = FOLDER_ITEM(cur->data);
1530                         if (!strcmp2(old_item->path, cur_item->path)) {
1531                                 new_item = cur_item;
1532                                 break;
1533                         }
1534                 }
1535                 if (!new_item) {
1536                         debug_print("folder '%s' not found. removing...\n",
1537                                     old_item->path);
1538                         folder_item_remove(old_item);
1539                 } else {
1540                         old_item->no_sub = new_item->no_sub;
1541                         old_item->no_select = new_item->no_select;
1542                         if (old_item->no_sub == TRUE && node->children) {
1543                                 debug_print("folder '%s' doesn't have "
1544                                             "subfolders. removing...\n",
1545                                             old_item->path);
1546                                 folder_item_remove_children(old_item);
1547                         }
1548                 }
1549
1550                 node = next;
1551         }
1552
1553         for (cur = item_list; cur != NULL; cur = cur->next) {
1554                 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1555                 new_item = NULL;
1556
1557                 for (node = item->node->children; node != NULL;
1558                      node = node->next) {
1559                         if (!strcmp2(FOLDER_ITEM(node->data)->path,
1560                                      cur_item->path)) {
1561                                 new_item = FOLDER_ITEM(node->data);
1562                                 folder_item_destroy(cur_item);
1563                                 cur_item = NULL;
1564                                 break;
1565                         }
1566                 }
1567                 if (!new_item) {
1568                         new_item = cur_item;
1569                         debug_print("new folder '%s' found.\n", new_item->path);
1570                         folder_item_append(item, new_item);
1571                 }
1572
1573                 if (!strcmp(new_item->path, "INBOX")) {
1574                         new_item->stype = F_INBOX;
1575                         folder->inbox = new_item;
1576                 } else if (!folder_item_parent(item) || item->stype == F_INBOX) {
1577                         gchar *base;
1578
1579                         base = g_path_get_basename(new_item->path);
1580
1581                         if (!folder->outbox && !g_ascii_strcasecmp(base, "Sent")) {
1582                                 new_item->stype = F_OUTBOX;
1583                                 folder->outbox = new_item;
1584                         } else if (!folder->draft && !g_ascii_strcasecmp(base, "Drafts")) {
1585                                 new_item->stype = F_DRAFT;
1586                                 folder->draft = new_item;
1587                         } else if (!folder->queue && !g_ascii_strcasecmp(base, "Queue")) {
1588                                 new_item->stype = F_QUEUE;
1589                                 folder->queue = new_item;
1590                         } else if (!folder->trash && !g_ascii_strcasecmp(base, "Trash")) {
1591                                 new_item->stype = F_TRASH;
1592                                 folder->trash = new_item;
1593                         }
1594                         g_free(base);
1595                 }
1596
1597                 if (new_item->no_sub == FALSE)
1598                         imap_scan_tree_recursive(session, new_item);
1599         }
1600
1601         g_slist_free(item_list);
1602
1603         return IMAP_SUCCESS;
1604 }
1605
1606 static gint imap_create_tree(Folder *folder)
1607 {
1608         g_return_val_if_fail(folder != NULL, -1);
1609         g_return_val_if_fail(folder->node != NULL, -1);
1610         g_return_val_if_fail(folder->node->data != NULL, -1);
1611         g_return_val_if_fail(folder->account != NULL, -1);
1612
1613         imap_scan_tree(folder);
1614         imap_create_missing_folders(folder);
1615
1616         return 0;
1617 }
1618
1619 static void imap_create_missing_folders(Folder *folder)
1620 {
1621         g_return_if_fail(folder != NULL);
1622
1623         if (!folder->inbox)
1624                 folder->inbox = imap_create_special_folder
1625                         (folder, F_INBOX, "INBOX");
1626         if (!folder->trash)
1627                 folder->trash = imap_create_special_folder
1628                         (folder, F_TRASH, "Trash");
1629         if (!folder->queue)
1630                 folder->queue = imap_create_special_folder
1631                         (folder, F_QUEUE, "Queue");
1632         if (!folder->outbox)
1633                 folder->outbox = imap_create_special_folder
1634                         (folder, F_OUTBOX, "Sent");
1635         if (!folder->draft)
1636                 folder->draft = imap_create_special_folder
1637                         (folder, F_DRAFT, "Drafts");
1638 }
1639
1640 static FolderItem *imap_create_special_folder(Folder *folder,
1641                                               SpecialFolderItemType stype,
1642                                               const gchar *name)
1643 {
1644         FolderItem *item;
1645         FolderItem *new_item;
1646
1647         g_return_val_if_fail(folder != NULL, NULL);
1648         g_return_val_if_fail(folder->node != NULL, NULL);
1649         g_return_val_if_fail(folder->node->data != NULL, NULL);
1650         g_return_val_if_fail(folder->account != NULL, NULL);
1651         g_return_val_if_fail(name != NULL, NULL);
1652
1653         item = FOLDER_ITEM(folder->node->data);
1654         new_item = imap_create_folder(folder, item, name);
1655
1656         if (!new_item) {
1657                 g_warning("Can't create '%s'\n", name);
1658                 if (!folder->inbox) return NULL;
1659
1660                 new_item = imap_create_folder(folder, folder->inbox, name);
1661                 if (!new_item)
1662                         g_warning("Can't create '%s' under INBOX\n", name);
1663                 else
1664                         new_item->stype = stype;
1665         } else
1666                 new_item->stype = stype;
1667
1668         return new_item;
1669 }
1670
1671 static gchar *imap_folder_get_path(Folder *folder)
1672 {
1673         gchar *folder_path;
1674
1675         g_return_val_if_fail(folder != NULL, NULL);
1676         g_return_val_if_fail(folder->account != NULL, NULL);
1677
1678         folder_path = g_strconcat(get_imap_cache_dir(),
1679                                   G_DIR_SEPARATOR_S,
1680                                   folder->account->recv_server,
1681                                   G_DIR_SEPARATOR_S,
1682                                   folder->account->userid,
1683                                   NULL);
1684
1685         return folder_path;
1686 }
1687
1688 static gchar *imap_item_get_path(Folder *folder, FolderItem *item)
1689 {
1690         gchar *folder_path, *path;
1691
1692         g_return_val_if_fail(folder != NULL, NULL);
1693         g_return_val_if_fail(item != NULL, NULL);
1694         folder_path = imap_folder_get_path(folder);
1695
1696         g_return_val_if_fail(folder_path != NULL, NULL);
1697         if (folder_path[0] == G_DIR_SEPARATOR) {
1698                 if (item->path)
1699                         path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
1700                                            item->path, NULL);
1701                 else
1702                         path = g_strdup(folder_path);
1703         } else {
1704                 if (item->path)
1705                         path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1706                                            folder_path, G_DIR_SEPARATOR_S,
1707                                            item->path, NULL);
1708                 else
1709                         path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1710                                            folder_path, NULL);
1711         }
1712         g_free(folder_path);
1713
1714         return path;
1715 }
1716
1717 static FolderItem *imap_create_folder(Folder *folder, FolderItem *parent,
1718                                const gchar *name)
1719 {
1720         gchar *dirpath, *imap_path;
1721         IMAPSession *session;
1722         FolderItem *new_item;
1723         gchar separator;
1724         gchar *new_name;
1725         const gchar *p;
1726         gint ok;
1727         gboolean no_select = FALSE, no_sub = FALSE;
1728         
1729         g_return_val_if_fail(folder != NULL, NULL);
1730         g_return_val_if_fail(folder->account != NULL, NULL);
1731         g_return_val_if_fail(parent != NULL, NULL);
1732         g_return_val_if_fail(name != NULL, NULL);
1733
1734         session = imap_session_get(folder);
1735         if (!session) {
1736                 return NULL;
1737         }
1738
1739         lock_session();
1740         if (!folder_item_parent(parent) && strcmp(name, "INBOX") == 0) {
1741                 dirpath = g_strdup(name);
1742         }else if (parent->path)
1743                 dirpath = g_strconcat(parent->path, "/", name, NULL);
1744         else if ((p = strchr(name, '/')) != NULL && *(p + 1) != '\0')
1745                 dirpath = g_strdup(name);
1746         else if (folder->account->imap_dir && *folder->account->imap_dir) {
1747                 gchar *imap_dir;
1748
1749                 Xstrdup_a(imap_dir, folder->account->imap_dir, {unlock_session();return NULL;});
1750                 strtailchomp(imap_dir, '/');
1751                 dirpath = g_strconcat(imap_dir, "/", name, NULL);
1752         } else
1753                 dirpath = g_strdup(name);
1754                 
1755         
1756
1757         /* keep trailing directory separator to create a folder that contains
1758            sub folder */
1759         imap_path = imap_utf8_to_modified_utf7(dirpath);
1760
1761         strtailchomp(dirpath, '/');
1762         Xstrdup_a(new_name, name, {
1763                 g_free(dirpath); 
1764                 unlock_session();               
1765                 return NULL;});
1766
1767         separator = imap_get_path_separator(IMAP_FOLDER(folder), imap_path);
1768         imap_path_separator_subst(imap_path, separator);
1769         /* remove trailing / for display */
1770         strtailchomp(new_name, '/');
1771
1772         if (strcmp(dirpath, "INBOX") != 0) {
1773                 GPtrArray *argbuf;
1774                 gboolean exist = FALSE;
1775                 int r;
1776                 clist * lep_list;
1777                 
1778                 argbuf = g_ptr_array_new();
1779                 r = imap_threaded_list(folder, "", imap_path, &lep_list);
1780                 if (r != MAILIMAP_NO_ERROR) {
1781                         log_warning(_("can't create mailbox: LIST failed\n"));
1782                         g_free(imap_path);
1783                         g_free(dirpath);
1784                         ptr_array_free_strings(argbuf);
1785                         g_ptr_array_free(argbuf, TRUE);
1786                         unlock_session();
1787                         return NULL;
1788                 }
1789                 
1790                 if (clist_count(lep_list) > 0)
1791                         exist = TRUE;
1792                 mailimap_list_result_free(lep_list);
1793                 lep_list = NULL;
1794                 if (!exist) {
1795                         ok = imap_cmd_create(session, imap_path);
1796                         if (ok != IMAP_SUCCESS) {
1797                                 log_warning(_("can't create mailbox\n"));
1798                                 g_free(imap_path);
1799                                 g_free(dirpath);
1800                                 unlock_session();
1801                                 return NULL;
1802                         }
1803                         r = imap_threaded_list(folder, "", imap_path, &lep_list);
1804                         if (r == MAILIMAP_NO_ERROR) {
1805                                 GSList *item_list = imap_list_from_lep(IMAP_FOLDER(folder),
1806                                                lep_list, dirpath, TRUE);
1807                                 if (item_list) {
1808                                         FolderItem *cur_item = FOLDER_ITEM(item_list->data);
1809                                         no_select = cur_item->no_select;
1810                                         no_sub = cur_item->no_sub;
1811                                         g_slist_free(item_list);
1812                                 } 
1813                                 mailimap_list_result_free(lep_list);
1814                         }
1815
1816                 }
1817         } else {
1818                 clist *lep_list;
1819                 int r;
1820                 /* just get flags */
1821                 r = imap_threaded_list(folder, "", "INBOX", &lep_list);
1822                 if (r == MAILIMAP_NO_ERROR) {
1823                         GSList *item_list = imap_list_from_lep(IMAP_FOLDER(folder),
1824                                        lep_list, dirpath, TRUE);
1825                         if (item_list) {
1826                                 FolderItem *cur_item = FOLDER_ITEM(item_list->data);
1827                                 no_select = cur_item->no_select;
1828                                 no_sub = cur_item->no_sub;
1829                                 g_slist_free(item_list);
1830                         } 
1831                         mailimap_list_result_free(lep_list);
1832                 }
1833         }
1834
1835         new_item = folder_item_new(folder, new_name, dirpath);
1836         new_item->no_select = no_select;
1837         new_item->no_sub = no_sub;
1838         folder_item_append(parent, new_item);
1839         g_free(imap_path);
1840         g_free(dirpath);
1841
1842         dirpath = folder_item_get_path(new_item);
1843         if (!is_dir_exist(dirpath))
1844                 make_dir_hier(dirpath);
1845         g_free(dirpath);
1846         unlock_session();
1847         return new_item;
1848 }
1849
1850 static gint imap_rename_folder(Folder *folder, FolderItem *item,
1851                                const gchar *name)
1852 {
1853         gchar *dirpath;
1854         gchar *newpath;
1855         gchar *real_oldpath;
1856         gchar *real_newpath;
1857         gchar *paths[2];
1858         gchar *old_cache_dir;
1859         gchar *new_cache_dir;
1860         IMAPSession *session;
1861         gchar separator;
1862         gint ok;
1863         gint exists, recent, unseen;
1864         guint32 uid_validity;
1865
1866         g_return_val_if_fail(folder != NULL, -1);
1867         g_return_val_if_fail(item != NULL, -1);
1868         g_return_val_if_fail(item->path != NULL, -1);
1869         g_return_val_if_fail(name != NULL, -1);
1870
1871         session = imap_session_get(folder);
1872         if (!session) {
1873                 return -1;
1874         }
1875         lock_session();
1876
1877         if (strchr(name, imap_get_path_separator(IMAP_FOLDER(folder), item->path)) != NULL) {
1878                 g_warning(_("New folder name must not contain the namespace "
1879                             "path separator"));
1880                 unlock_session();
1881                 return -1;
1882         }
1883
1884         real_oldpath = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1885
1886         g_free(session->mbox);
1887         session->mbox = NULL;
1888         ok = imap_cmd_examine(session, "INBOX",
1889                               &exists, &recent, &unseen, &uid_validity, FALSE);
1890         if (ok != IMAP_SUCCESS) {
1891                 g_free(real_oldpath);
1892                 unlock_session();
1893                 return -1;
1894         }
1895
1896         separator = imap_get_path_separator(IMAP_FOLDER(folder), item->path);
1897         if (strchr(item->path, G_DIR_SEPARATOR)) {
1898                 dirpath = g_path_get_dirname(item->path);
1899                 newpath = g_strconcat(dirpath, G_DIR_SEPARATOR_S, name, NULL);
1900                 g_free(dirpath);
1901         } else
1902                 newpath = g_strdup(name);
1903
1904         real_newpath = imap_utf8_to_modified_utf7(newpath);
1905         imap_path_separator_subst(real_newpath, separator);
1906
1907         ok = imap_cmd_rename(session, real_oldpath, real_newpath);
1908         if (ok != IMAP_SUCCESS) {
1909                 log_warning(_("can't rename mailbox: %s to %s\n"),
1910                             real_oldpath, real_newpath);
1911                 g_free(real_oldpath);
1912                 g_free(newpath);
1913                 g_free(real_newpath);
1914                 unlock_session();
1915                 return -1;
1916         }
1917
1918         g_free(item->name);
1919         item->name = g_strdup(name);
1920
1921         old_cache_dir = folder_item_get_path(item);
1922
1923         paths[0] = g_strdup(item->path);
1924         paths[1] = newpath;
1925         g_node_traverse(item->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1926                         imap_rename_folder_func, paths);
1927
1928         if (is_dir_exist(old_cache_dir)) {
1929                 new_cache_dir = folder_item_get_path(item);
1930                 if (rename(old_cache_dir, new_cache_dir) < 0) {
1931                         FILE_OP_ERROR(old_cache_dir, "rename");
1932                 }
1933                 g_free(new_cache_dir);
1934         }
1935
1936         g_free(old_cache_dir);
1937         g_free(paths[0]);
1938         g_free(newpath);
1939         g_free(real_oldpath);
1940         g_free(real_newpath);
1941         unlock_session();
1942         return 0;
1943 }
1944
1945 static gint imap_remove_folder_real(Folder *folder, FolderItem *item)
1946 {
1947         gint ok;
1948         IMAPSession *session;
1949         gchar *path;
1950         gchar *cache_dir;
1951
1952         g_return_val_if_fail(folder != NULL, -1);
1953         g_return_val_if_fail(item != NULL, -1);
1954         g_return_val_if_fail(item->path != NULL, -1);
1955
1956         session = imap_session_get(folder);
1957         if (!session) {
1958                 return -1;
1959         }
1960         lock_session();
1961         path = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1962
1963         ok = imap_cmd_delete(session, path);
1964         if (ok != IMAP_SUCCESS) {
1965                 gchar *tmp = g_strdup_printf("%s%c", path, 
1966                                 imap_get_path_separator(IMAP_FOLDER(folder), path));
1967                 g_free(path);
1968                 path = tmp;
1969                 ok = imap_cmd_delete(session, path);
1970         }
1971
1972         if (ok != IMAP_SUCCESS) {
1973                 log_warning(_("can't delete mailbox\n"));
1974                 g_free(path);
1975                 unlock_session();
1976                 return -1;
1977         }
1978
1979         g_free(path);
1980         cache_dir = folder_item_get_path(item);
1981         if (is_dir_exist(cache_dir) && remove_dir_recursive(cache_dir) < 0)
1982                 g_warning("can't remove directory '%s'\n", cache_dir);
1983         g_free(cache_dir);
1984         folder_item_remove(item);
1985         unlock_session();
1986         return 0;
1987 }
1988
1989 static gint imap_remove_folder(Folder *folder, FolderItem *item)
1990 {
1991         GNode *node, *next;
1992
1993         g_return_val_if_fail(item != NULL, -1);
1994         g_return_val_if_fail(item->folder != NULL, -1);
1995         g_return_val_if_fail(item->node != NULL, -1);
1996
1997         node = item->node->children;
1998         while (node != NULL) {
1999                 next = node->next;
2000                 if (imap_remove_folder(folder, FOLDER_ITEM(node->data)) < 0)
2001                         return -1;
2002                 node = next;
2003         }
2004         debug_print("IMAP removing %s\n", item->path);
2005
2006         if (imap_remove_all_msg(folder, item) < 0)
2007                 return -1;
2008         return imap_remove_folder_real(folder, item);
2009 }
2010
2011 typedef struct _uncached_data {
2012         IMAPSession *session;
2013         FolderItem *item;
2014         MsgNumberList *numlist;
2015         guint cur;
2016         guint total;
2017         gboolean done;
2018 } uncached_data;
2019
2020 static void *imap_get_uncached_messages_thread(void *data)
2021 {
2022         uncached_data *stuff = (uncached_data *)data;
2023         IMAPSession *session = stuff->session;
2024         FolderItem *item = stuff->item;
2025         MsgNumberList *numlist = stuff->numlist;
2026         
2027         GSList *newlist = NULL;
2028         GSList *llast = NULL;
2029         GSList *seq_list, *cur;
2030
2031         debug_print("uncached_messages\n");
2032         
2033         if (session == NULL || item == NULL || item->folder == NULL
2034             || FOLDER_CLASS(item->folder) != &imap_class) {
2035                 stuff->done = TRUE;
2036                 return NULL;
2037         }
2038         
2039         seq_list = imap_get_lep_set_from_numlist(numlist);
2040         debug_print("get msgs info\n");
2041         for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
2042                 struct mailimap_set * imapset;
2043                 unsigned int i;
2044                 int r;
2045                 carray * env_list;
2046                 int count;
2047                 
2048                 imapset = cur->data;
2049                 
2050                 r = imap_threaded_fetch_env(session->folder,
2051                                             imapset, &env_list);
2052                 if (r != MAILIMAP_NO_ERROR)
2053                         continue;
2054                 
2055                 session_set_access_time(SESSION(session));
2056
2057                 count = 0;
2058                 for(i = 0 ; i < carray_count(env_list) ; i ++) {
2059                         struct imap_fetch_env_info * info;
2060                         MsgInfo * msginfo;
2061                         
2062                         info = carray_get(env_list, i);
2063                         msginfo = imap_envelope_from_lep(info, item);
2064                         if (msginfo == NULL)
2065                                 continue;
2066                         msginfo->folder = item;
2067                         if (!newlist)
2068                                 llast = newlist = g_slist_append(newlist, msginfo);
2069                         else {
2070                                 llast = g_slist_append(llast, msginfo);
2071                                 llast = llast->next;
2072                         }
2073                         count ++;
2074                 }
2075                 
2076                 imap_fetch_env_free(env_list);
2077         }
2078         
2079         for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
2080                 struct mailimap_set * imapset;
2081                 
2082                 imapset = cur->data;
2083                 mailimap_set_free(imapset);
2084         }
2085         
2086         session_set_access_time(SESSION(session));
2087         stuff->done = TRUE;
2088         return newlist;
2089 }
2090
2091 #define MAX_MSG_NUM 50
2092
2093 static GSList *imap_get_uncached_messages(IMAPSession *session,
2094                                         FolderItem *item,
2095                                         MsgNumberList *numlist)
2096 {
2097         GSList *result = NULL;
2098         GSList * cur;
2099         uncached_data *data = g_new0(uncached_data, 1);
2100         int finished;
2101         
2102         finished = 0;
2103         cur = numlist;
2104         data->total = g_slist_length(numlist);
2105         debug_print("messages list : %i\n", data->total);
2106
2107         while (cur != NULL) {
2108                 GSList * partial_result;
2109                 int count;
2110                 GSList * newlist;
2111                 GSList * llast;
2112                 
2113                 llast = NULL;
2114                 count = 0;
2115                 newlist = NULL;
2116                 while (count < MAX_MSG_NUM) {
2117                         void * p;
2118                         
2119                         p = cur->data;
2120                         
2121                         if (newlist == NULL)
2122                                 llast = newlist = g_slist_append(newlist, p);
2123                         else {
2124                                 llast = g_slist_append(llast, p);
2125                                 llast = llast->next;
2126                         }
2127                         count ++;
2128                         
2129                         cur = cur->next;
2130                         if (cur == NULL)
2131                                 break;
2132                 }
2133                 
2134                 data->done = FALSE;
2135                 data->session = session;
2136                 data->item = item;
2137                 data->numlist = newlist;
2138                 data->cur += count;
2139                 
2140                 if (prefs_common.work_offline && 
2141                     !inc_offline_should_override(
2142                         _("Sylpheed-Claws needs network access in order "
2143                           "to access the IMAP server."))) {
2144                         g_free(data);
2145                         return NULL;
2146                 }
2147                 
2148                 partial_result =
2149                         (GSList *)imap_get_uncached_messages_thread(data);
2150                 
2151                 statusbar_progress_all(data->cur,data->total, 1);
2152                 
2153                 g_slist_free(newlist);
2154                 
2155                 result = g_slist_concat(result, partial_result);
2156         }
2157         g_free(data);
2158         
2159         statusbar_progress_all(0,0,0);
2160         statusbar_pop_all();
2161         
2162         return result;
2163 }
2164
2165 static void imap_delete_all_cached_messages(FolderItem *item)
2166 {
2167         gchar *dir;
2168
2169         g_return_if_fail(item != NULL);
2170         g_return_if_fail(item->folder != NULL);
2171         g_return_if_fail(FOLDER_CLASS(item->folder) == &imap_class);
2172
2173         debug_print("Deleting all cached messages...\n");
2174
2175         dir = folder_item_get_path(item);
2176         if (is_dir_exist(dir))
2177                 remove_all_numbered_files(dir);
2178         g_free(dir);
2179
2180         debug_print("done.\n");
2181 }
2182
2183 static IMAPNameSpace *imap_find_namespace_from_list(GList *ns_list,
2184                                                     const gchar *path)
2185 {
2186         IMAPNameSpace *namespace = NULL;
2187         gchar *tmp_path, *name;
2188
2189         if (!path) path = "";
2190
2191         for (; ns_list != NULL; ns_list = ns_list->next) {
2192                 IMAPNameSpace *tmp_ns = ns_list->data;
2193
2194                 Xstrcat_a(tmp_path, path, "/", return namespace);
2195                 Xstrdup_a(name, tmp_ns->name, return namespace);
2196                 if (tmp_ns->separator && tmp_ns->separator != '/') {
2197                         subst_char(tmp_path, tmp_ns->separator, '/');
2198                         subst_char(name, tmp_ns->separator, '/');
2199                 }
2200                 if (strncmp(tmp_path, name, strlen(name)) == 0)
2201                         namespace = tmp_ns;
2202         }
2203
2204         return namespace;
2205 }
2206
2207 static IMAPNameSpace *imap_find_namespace(IMAPFolder *folder,
2208                                           const gchar *path)
2209 {
2210         IMAPNameSpace *namespace;
2211
2212         g_return_val_if_fail(folder != NULL, NULL);
2213
2214         namespace = imap_find_namespace_from_list(folder->ns_personal, path);
2215         if (namespace) return namespace;
2216         namespace = imap_find_namespace_from_list(folder->ns_others, path);
2217         if (namespace) return namespace;
2218         namespace = imap_find_namespace_from_list(folder->ns_shared, path);
2219         if (namespace) return namespace;
2220
2221         return NULL;
2222 }
2223
2224 gchar imap_get_path_separator_for_item(FolderItem *item)
2225 {
2226         Folder *folder = NULL;
2227         IMAPFolder *imap_folder = NULL;
2228         if (!item)
2229                 return '/';
2230         folder = item->folder;
2231         
2232         if (!folder)
2233                 return '/';
2234         
2235         imap_folder = IMAP_FOLDER(folder);
2236         
2237         if (!imap_folder)
2238                 return '/';
2239         
2240         return imap_get_path_separator(imap_folder, item->path);
2241 }
2242
2243 static gchar imap_get_path_separator(IMAPFolder *folder, const gchar *path)
2244 {
2245         IMAPNameSpace *namespace;
2246         gchar separator = '/';
2247         IMAPSession *session = imap_session_get(FOLDER(folder));
2248         g_return_val_if_fail(session != NULL, '/');
2249
2250         if (folder->last_seen_separator == 0) {
2251                 clist * lep_list;
2252                 int r = imap_threaded_list((Folder *)folder, "", "", &lep_list);
2253                 if (r != MAILIMAP_NO_ERROR) {
2254                         log_warning(_("LIST failed\n"));
2255                         return '/';
2256                 }
2257                 
2258                 if (clist_count(lep_list) > 0) {
2259                         clistiter * iter = clist_begin(lep_list); 
2260                         struct mailimap_mailbox_list * mb;
2261                         mb = clist_content(iter);
2262                 
2263                         folder->last_seen_separator = mb->mb_delimiter;
2264                         debug_print("got separator: %c\n", folder->last_seen_separator);
2265                 }
2266                 mailimap_list_result_free(lep_list);
2267         }
2268
2269         if (folder->last_seen_separator != 0) {
2270                 debug_print("using separator: %c\n", folder->last_seen_separator);
2271                 return folder->last_seen_separator;
2272         }
2273
2274         namespace = imap_find_namespace(folder, path);
2275         if (namespace && namespace->separator)
2276                 separator = namespace->separator;
2277
2278         return separator;
2279 }
2280
2281 static gchar *imap_get_real_path(IMAPFolder *folder, const gchar *path)
2282 {
2283         gchar *real_path;
2284         gchar separator;
2285
2286         g_return_val_if_fail(folder != NULL, NULL);
2287         g_return_val_if_fail(path != NULL, NULL);
2288
2289         real_path = imap_utf8_to_modified_utf7(path);
2290         separator = imap_get_path_separator(folder, path);
2291         imap_path_separator_subst(real_path, separator);
2292
2293         return real_path;
2294 }
2295
2296 static gint imap_set_message_flags(IMAPSession *session,
2297                                    MsgNumberList *numlist,
2298                                    IMAPFlags flags,
2299                                    gboolean is_set)
2300 {
2301         gint ok = 0;
2302         GSList *seq_list;
2303         GSList * cur;
2304
2305         seq_list = imap_get_lep_set_from_numlist(numlist);
2306         
2307         for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) {
2308                 struct mailimap_set * imapset;
2309                 
2310                 imapset = cur->data;
2311                 
2312                 ok = imap_cmd_store(session, imapset,
2313                                     flags, is_set);
2314         }
2315         
2316         imap_lep_set_free(seq_list);
2317         
2318         return IMAP_SUCCESS;
2319 }
2320
2321 typedef struct _select_data {
2322         IMAPSession *session;
2323         gchar *real_path;
2324         gint *exists;
2325         gint *recent;
2326         gint *unseen;
2327         guint32 *uid_validity;
2328         gboolean done;
2329 } select_data;
2330
2331 static gint imap_select(IMAPSession *session, IMAPFolder *folder,
2332                         const gchar *path,
2333                         gint *exists, gint *recent, gint *unseen,
2334                         guint32 *uid_validity, gboolean block)
2335 {
2336         gchar *real_path;
2337         gint ok;
2338         gint exists_, recent_, unseen_;
2339         guint32 uid_validity_;
2340         
2341         if (!exists && !recent && !unseen && !uid_validity) {
2342                 if (session->mbox && strcmp(session->mbox, path) == 0)
2343                         return IMAP_SUCCESS;
2344         }
2345         if (!exists)
2346                 exists = &exists_;
2347         if (!recent)
2348                 recent = &recent_;
2349         if (!unseen)
2350                 unseen = &unseen_;
2351         if (!uid_validity)
2352                 uid_validity = &uid_validity_;
2353
2354         g_free(session->mbox);
2355         session->mbox = NULL;
2356
2357         real_path = imap_get_real_path(folder, path);
2358
2359         ok = imap_cmd_select(session, real_path,
2360                              exists, recent, unseen, uid_validity, block);
2361         if (ok != IMAP_SUCCESS)
2362                 log_warning(_("can't select folder: %s\n"), real_path);
2363         else {
2364                 session->mbox = g_strdup(path);
2365                 session->folder_content_changed = FALSE;
2366         }
2367         g_free(real_path);
2368
2369         return ok;
2370 }
2371
2372 static gint imap_status(IMAPSession *session, IMAPFolder *folder,
2373                         const gchar *path, IMAPFolderItem *item,
2374                         gint *messages,
2375                         guint32 *uid_next, guint32 *uid_validity,
2376                         gint *unseen, gboolean block)
2377 {
2378         int r;
2379         clistiter * iter;
2380         struct mailimap_mailbox_data_status * data_status;
2381         int got_values;
2382         gchar *real_path;
2383         guint mask = 0;
2384         
2385         real_path = imap_get_real_path(folder, path);
2386
2387         if (messages) {
2388                 mask |= 1 << 0;
2389         }
2390         if (uid_next) {
2391                 mask |= 1 << 2;
2392         }
2393         if (uid_validity) {
2394                 mask |= 1 << 3;
2395         }
2396         if (unseen) {
2397                 mask |= 1 << 4;
2398         }
2399         r = imap_threaded_status(FOLDER(folder), real_path, 
2400                 &data_status, mask);
2401
2402         g_free(real_path);
2403         if (r != MAILIMAP_NO_ERROR) {
2404                 debug_print("status err %d\n", r);
2405                 return IMAP_ERROR;
2406         }
2407         
2408         if (data_status->st_info_list == NULL) {
2409                 mailimap_mailbox_data_status_free(data_status);
2410                 debug_print("status->st_info_list == NULL\n");
2411                 return IMAP_ERROR;
2412         }
2413         
2414         got_values = 0;
2415         for(iter = clist_begin(data_status->st_info_list) ; iter != NULL ;
2416             iter = clist_next(iter)) {
2417                 struct mailimap_status_info * info;             
2418                 
2419                 info = clist_content(iter);
2420                 switch (info->st_att) {
2421                 case MAILIMAP_STATUS_ATT_MESSAGES:
2422                         * messages = info->st_value;
2423                         got_values |= 1 << 0;
2424                         break;
2425                         
2426                 case MAILIMAP_STATUS_ATT_UIDNEXT:
2427                         * uid_next = info->st_value;
2428                         got_values |= 1 << 2;
2429                         break;
2430                         
2431                 case MAILIMAP_STATUS_ATT_UIDVALIDITY:
2432                         * uid_validity = info->st_value;
2433                         got_values |= 1 << 3;
2434                         break;
2435                         
2436                 case MAILIMAP_STATUS_ATT_UNSEEN:
2437                         * unseen = info->st_value;
2438                         got_values |= 1 << 4;
2439                         break;
2440                 }
2441         }
2442         mailimap_mailbox_data_status_free(data_status);
2443         
2444         if (got_values != mask) {
2445                 debug_print("status: incomplete values received (%d)\n", got_values);
2446                 return IMAP_ERROR;
2447         }
2448         return IMAP_SUCCESS;
2449 }
2450
2451 static void imap_free_capabilities(IMAPSession *session)
2452 {
2453         slist_free_strings(session->capability);
2454         g_slist_free(session->capability);
2455         session->capability = NULL;
2456 }
2457
2458 /* low-level IMAP4rev1 commands */
2459
2460 static gint imap_cmd_login(IMAPSession *session,
2461                            const gchar *user, const gchar *pass,
2462                            const gchar *type)
2463 {
2464         int r;
2465         gint ok;
2466
2467         log_print("IMAP4> Logging %s to %s using %s\n", 
2468                         user,
2469                         SESSION(session)->server,
2470                         type);
2471         r = imap_threaded_login(session->folder, user, pass, type);
2472         if (r != MAILIMAP_NO_ERROR) {
2473                 log_print("IMAP4< Error logging in to %s\n",
2474                                 SESSION(session)->server);
2475                 ok = IMAP_ERROR;
2476         } else {
2477                 log_print("IMAP4< Login to %s successful\n",
2478                                 SESSION(session)->server);
2479                 ok = IMAP_SUCCESS;
2480         }
2481         return ok;
2482 }
2483
2484 static gint imap_cmd_logout(IMAPSession *session)
2485 {
2486         imap_threaded_disconnect(session->folder);
2487
2488         return IMAP_SUCCESS;
2489 }
2490
2491 static gint imap_cmd_noop(IMAPSession *session)
2492 {
2493         int r;
2494         unsigned int exists;
2495         
2496         r = imap_threaded_noop(session->folder, &exists);
2497         if (r != MAILIMAP_NO_ERROR) {
2498                 debug_print("noop err %d\n", r);
2499                 return IMAP_ERROR;
2500         }
2501         session->exists = exists;
2502         session_set_access_time(SESSION(session));
2503
2504         return IMAP_SUCCESS;
2505 }
2506
2507 #if USE_OPENSSL
2508 static gint imap_cmd_starttls(IMAPSession *session)
2509 {
2510         int r;
2511         
2512         r = imap_threaded_starttls(session->folder, 
2513                 SESSION(session)->server, SESSION(session)->port);
2514         if (r != MAILIMAP_NO_ERROR) {
2515                 debug_print("starttls err %d\n", r);
2516                 return IMAP_ERROR;
2517         }
2518         return IMAP_SUCCESS;
2519 }
2520 #endif
2521
2522 static gint imap_cmd_select(IMAPSession *session, const gchar *folder,
2523                             gint *exists, gint *recent, gint *unseen,
2524                             guint32 *uid_validity, gboolean block)
2525 {
2526         int r;
2527
2528         r = imap_threaded_select(session->folder, folder,
2529                                  exists, recent, unseen, uid_validity);
2530         if (r != MAILIMAP_NO_ERROR) {
2531                 debug_print("select err %d\n", r);
2532                 return IMAP_ERROR;
2533         }
2534         return IMAP_SUCCESS;
2535 }
2536
2537 static gint imap_cmd_examine(IMAPSession *session, const gchar *folder,
2538                              gint *exists, gint *recent, gint *unseen,
2539                              guint32 *uid_validity, gboolean block)
2540 {
2541         int r;
2542
2543         r = imap_threaded_examine(session->folder, folder,
2544                                   exists, recent, unseen, uid_validity);
2545         if (r != MAILIMAP_NO_ERROR) {
2546                 debug_print("examine err %d\n", r);
2547                 
2548                 return IMAP_ERROR;
2549         }
2550         return IMAP_SUCCESS;
2551 }
2552
2553 static gint imap_cmd_create(IMAPSession *session, const gchar *folder)
2554 {
2555         int r;
2556
2557         r = imap_threaded_create(session->folder, folder);
2558         if (r != MAILIMAP_NO_ERROR) {
2559                 
2560                 return IMAP_ERROR;
2561         }
2562
2563         return IMAP_SUCCESS;
2564 }
2565
2566 static gint imap_cmd_rename(IMAPSession *session, const gchar *old_folder,
2567                             const gchar *new_folder)
2568 {
2569         int r;
2570
2571         r = imap_threaded_rename(session->folder, old_folder,
2572                                  new_folder);
2573         if (r != MAILIMAP_NO_ERROR) {
2574                 
2575                 return IMAP_ERROR;
2576         }
2577
2578         return IMAP_SUCCESS;
2579 }
2580
2581 static gint imap_cmd_delete(IMAPSession *session, const gchar *folder)
2582 {
2583         int r;
2584         
2585
2586         r = imap_threaded_delete(session->folder, folder);
2587         if (r != MAILIMAP_NO_ERROR) {
2588                 
2589                 return IMAP_ERROR;
2590         }
2591
2592         return IMAP_SUCCESS;
2593 }
2594
2595 typedef struct _fetch_data {
2596         IMAPSession *session;
2597         guint32 uid;
2598         const gchar *filename;
2599         gboolean headers;
2600         gboolean body;
2601         gboolean done;
2602 } fetch_data;
2603
2604 static void *imap_cmd_fetch_thread(void *data)
2605 {
2606         fetch_data *stuff = (fetch_data *)data;
2607         IMAPSession *session = stuff->session;
2608         guint32 uid = stuff->uid;
2609         const gchar *filename = stuff->filename;
2610         int r;
2611         
2612         if (stuff->body) {
2613                 r = imap_threaded_fetch_content(session->folder,
2614                                                uid, 1, filename);
2615         }
2616         else {
2617                 r = imap_threaded_fetch_content(session->folder,
2618                                                 uid, 0, filename);
2619         }
2620         if (r != MAILIMAP_NO_ERROR) {
2621                 debug_print("fetch err %d\n", r);
2622                 return GINT_TO_POINTER(IMAP_ERROR);
2623         }
2624         return GINT_TO_POINTER(IMAP_SUCCESS);
2625 }
2626
2627 static gint imap_cmd_fetch(IMAPSession *session, guint32 uid,
2628                                 const gchar *filename, gboolean headers,
2629                                 gboolean body)
2630 {
2631         fetch_data *data = g_new0(fetch_data, 1);
2632         int result = 0;
2633         data->done = FALSE;
2634         data->session = session;
2635         data->uid = uid;
2636         data->filename = filename;
2637         data->headers = headers;
2638         data->body = body;
2639
2640         if (prefs_common.work_offline && 
2641             !inc_offline_should_override(
2642                 _("Sylpheed-Claws needs network access in order "
2643                   "to access the IMAP server."))) {
2644                 g_free(data);
2645                 return -1;
2646         }
2647         statusbar_print_all(_("Fetching message..."));
2648         result = GPOINTER_TO_INT(imap_cmd_fetch_thread(data));
2649         statusbar_pop_all();
2650         g_free(data);
2651         return result;
2652 }
2653
2654
2655 static gint imap_cmd_append(IMAPSession *session, const gchar *destfolder,
2656                             const gchar *file, IMAPFlags flags, 
2657                             guint32 *new_uid)
2658 {
2659         struct mailimap_flag_list * flag_list;
2660         int r;
2661         
2662         g_return_val_if_fail(file != NULL, IMAP_ERROR);
2663
2664         flag_list = imap_flag_to_lep(flags);
2665         r = imap_threaded_append(session->folder, destfolder,
2666                          file, flag_list, new_uid);
2667         mailimap_flag_list_free(flag_list);
2668
2669         if (r != MAILIMAP_NO_ERROR) {
2670                 debug_print("append err %d\n", r);
2671                 return IMAP_ERROR;
2672         }
2673         return IMAP_SUCCESS;
2674 }
2675
2676 static gint imap_cmd_copy(IMAPSession *session, struct mailimap_set * set,
2677                           const gchar *destfolder, GRelation *uid_mapping)
2678 {
2679         int r;
2680         
2681         g_return_val_if_fail(session != NULL, IMAP_ERROR);
2682         g_return_val_if_fail(set != NULL, IMAP_ERROR);
2683         g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
2684
2685         r = imap_threaded_copy(session->folder, set, destfolder);
2686         if (r != MAILIMAP_NO_ERROR) {
2687                 
2688                 return IMAP_ERROR;
2689         }
2690
2691         return IMAP_SUCCESS;
2692 }
2693
2694 static gint imap_cmd_store(IMAPSession *session, struct mailimap_set * set,
2695                            IMAPFlags flags, int do_add)
2696 {
2697         int r;
2698         struct mailimap_flag_list * flag_list;
2699         struct mailimap_store_att_flags * store_att_flags;
2700         
2701         flag_list = imap_flag_to_lep(flags);
2702         
2703         if (do_add)
2704                 store_att_flags =
2705                         mailimap_store_att_flags_new_add_flags_silent(flag_list);
2706         else
2707                 store_att_flags =
2708                         mailimap_store_att_flags_new_remove_flags_silent(flag_list);
2709         
2710         r = imap_threaded_store(session->folder, set, store_att_flags);
2711         mailimap_store_att_flags_free(store_att_flags);
2712         if (r != MAILIMAP_NO_ERROR) {
2713                 
2714                 return IMAP_ERROR;
2715         }
2716         
2717         return IMAP_SUCCESS;
2718 }
2719
2720 static gint imap_cmd_expunge(IMAPSession *session)
2721 {
2722         int r;
2723         
2724         if (prefs_common.work_offline && 
2725             !inc_offline_should_override(
2726                 _("Sylpheed-Claws needs network access in order "
2727                   "to access the IMAP server."))) {
2728                 return -1;
2729         }
2730
2731         r = imap_threaded_expunge(session->folder);
2732         if (r != MAILIMAP_NO_ERROR) {
2733                 
2734                 return IMAP_ERROR;
2735         }
2736
2737         return IMAP_SUCCESS;
2738 }
2739
2740 static void imap_path_separator_subst(gchar *str, gchar separator)
2741 {
2742         gchar *p;
2743         gboolean in_escape = FALSE;
2744
2745         if (!separator || separator == '/') return;
2746
2747         for (p = str; *p != '\0'; p++) {
2748                 if (*p == '/' && !in_escape)
2749                         *p = separator;
2750                 else if (*p == '&' && *(p + 1) != '-' && !in_escape)
2751                         in_escape = TRUE;
2752                 else if (*p == '-' && in_escape)
2753                         in_escape = FALSE;
2754         }
2755 }
2756
2757 static gchar *imap_modified_utf7_to_utf8(const gchar *mutf7_str)
2758 {
2759         static iconv_t cd = (iconv_t)-1;
2760         static gboolean iconv_ok = TRUE;
2761         GString *norm_utf7;
2762         gchar *norm_utf7_p;
2763         size_t norm_utf7_len;
2764         const gchar *p;
2765         gchar *to_str, *to_p;
2766         size_t to_len;
2767         gboolean in_escape = FALSE;
2768
2769         if (!iconv_ok) return g_strdup(mutf7_str);
2770
2771         if (cd == (iconv_t)-1) {
2772                 cd = iconv_open(CS_INTERNAL, CS_UTF_7);
2773                 if (cd == (iconv_t)-1) {
2774                         g_warning("iconv cannot convert UTF-7 to %s\n",
2775                                   CS_INTERNAL);
2776                         iconv_ok = FALSE;
2777                         return g_strdup(mutf7_str);
2778                 }
2779         }
2780
2781         /* modified UTF-7 to normal UTF-7 conversion */
2782         norm_utf7 = g_string_new(NULL);
2783
2784         for (p = mutf7_str; *p != '\0'; p++) {
2785                 /* replace: '&'  -> '+',
2786                             "&-" -> '&',
2787                             escaped ','  -> '/' */
2788                 if (!in_escape && *p == '&') {
2789                         if (*(p + 1) != '-') {
2790                                 g_string_append_c(norm_utf7, '+');
2791                                 in_escape = TRUE;
2792                         } else {
2793                                 g_string_append_c(norm_utf7, '&');
2794                                 p++;
2795                         }
2796                 } else if (in_escape && *p == ',') {
2797                         g_string_append_c(norm_utf7, '/');
2798                 } else if (in_escape && *p == '-') {
2799                         g_string_append_c(norm_utf7, '-');
2800                         in_escape = FALSE;
2801                 } else {
2802                         g_string_append_c(norm_utf7, *p);
2803                 }
2804         }
2805
2806         norm_utf7_p = norm_utf7->str;
2807         norm_utf7_len = norm_utf7->len;
2808         to_len = strlen(mutf7_str) * 5;
2809         to_p = to_str = g_malloc(to_len + 1);
2810
2811         if (iconv(cd, (ICONV_CONST gchar **)&norm_utf7_p, &norm_utf7_len,
2812                   &to_p, &to_len) == -1) {
2813                 g_warning(_("iconv cannot convert UTF-7 to %s\n"),
2814                           conv_get_locale_charset_str());
2815                 g_string_free(norm_utf7, TRUE);
2816                 g_free(to_str);
2817                 return g_strdup(mutf7_str);
2818         }
2819
2820         /* second iconv() call for flushing */
2821         iconv(cd, NULL, NULL, &to_p, &to_len);
2822         g_string_free(norm_utf7, TRUE);
2823         *to_p = '\0';
2824
2825         return to_str;
2826 }
2827
2828 static gchar *imap_utf8_to_modified_utf7(const gchar *from)
2829 {
2830         static iconv_t cd = (iconv_t)-1;
2831         static gboolean iconv_ok = TRUE;
2832         gchar *norm_utf7, *norm_utf7_p;
2833         size_t from_len, norm_utf7_len;
2834         GString *to_str;
2835         gchar *from_tmp, *to, *p;
2836         gboolean in_escape = FALSE;
2837
2838         if (!iconv_ok) return g_strdup(from);
2839
2840         if (cd == (iconv_t)-1) {
2841                 cd = iconv_open(CS_UTF_7, CS_INTERNAL);
2842                 if (cd == (iconv_t)-1) {
2843                         g_warning(_("iconv cannot convert %s to UTF-7\n"),
2844                                   CS_INTERNAL);
2845                         iconv_ok = FALSE;
2846                         return g_strdup(from);
2847                 }
2848         }
2849
2850         /* UTF-8 to normal UTF-7 conversion */
2851         Xstrdup_a(from_tmp, from, return g_strdup(from));
2852         from_len = strlen(from);
2853         norm_utf7_len = from_len * 5;
2854         Xalloca(norm_utf7, norm_utf7_len + 1, return g_strdup(from));
2855         norm_utf7_p = norm_utf7;
2856
2857 #define IS_PRINT(ch) (isprint(ch) && IS_ASCII(ch))
2858
2859         while (from_len > 0) {
2860                 if (*from_tmp == '+') {
2861                         *norm_utf7_p++ = '+';
2862                         *norm_utf7_p++ = '-';
2863                         norm_utf7_len -= 2;
2864                         from_tmp++;
2865                         from_len--;
2866                 } else if (IS_PRINT(*(guchar *)from_tmp)) {
2867                         /* printable ascii char */
2868                         *norm_utf7_p = *from_tmp;
2869                         norm_utf7_p++;
2870                         norm_utf7_len--;
2871                         from_tmp++;
2872                         from_len--;
2873                 } else {
2874                         size_t conv_len = 0;
2875
2876                         /* unprintable char: convert to UTF-7 */
2877                         p = from_tmp;
2878                         while (!IS_PRINT(*(guchar *)p) && conv_len < from_len) {
2879                                 conv_len += g_utf8_skip[*(guchar *)p];
2880                                 p += g_utf8_skip[*(guchar *)p];
2881                         }
2882
2883                         from_len -= conv_len;
2884                         if (iconv(cd, (ICONV_CONST gchar **)&from_tmp,
2885                                   &conv_len,
2886                                   &norm_utf7_p, &norm_utf7_len) == -1) {
2887                                 g_warning(_("iconv cannot convert UTF-8 to UTF-7\n"));
2888                                 return g_strdup(from);
2889                         }
2890
2891                         /* second iconv() call for flushing */
2892                         iconv(cd, NULL, NULL, &norm_utf7_p, &norm_utf7_len);
2893                 }
2894         }
2895
2896 #undef IS_PRINT
2897
2898         *norm_utf7_p = '\0';
2899         to_str = g_string_new(NULL);
2900         for (p = norm_utf7; p < norm_utf7_p; p++) {
2901                 /* replace: '&' -> "&-",
2902                             '+' -> '&',
2903                             "+-" -> '+',
2904                             BASE64 '/' -> ',' */
2905                 if (!in_escape && *p == '&') {
2906                         g_string_append(to_str, "&-");
2907                 } else if (!in_escape && *p == '+') {
2908                         if (*(p + 1) == '-') {
2909                                 g_string_append_c(to_str, '+');
2910                                 p++;
2911                         } else {
2912                                 g_string_append_c(to_str, '&');
2913                                 in_escape = TRUE;
2914                         }
2915                 } else if (in_escape && *p == '/') {
2916                         g_string_append_c(to_str, ',');
2917                 } else if (in_escape && *p == '-') {
2918                         g_string_append_c(to_str, '-');
2919                         in_escape = FALSE;
2920                 } else {
2921                         g_string_append_c(to_str, *p);
2922                 }
2923         }
2924
2925         if (in_escape) {
2926                 in_escape = FALSE;
2927                 g_string_append_c(to_str, '-');
2928         }
2929
2930         to = to_str->str;
2931         g_string_free(to_str, FALSE);
2932
2933         return to;
2934 }
2935
2936 static gboolean imap_rename_folder_func(GNode *node, gpointer data)
2937 {
2938         FolderItem *item = node->data;
2939         gchar **paths = data;
2940         const gchar *oldpath = paths[0];
2941         const gchar *newpath = paths[1];
2942         gchar *base;
2943         gchar *new_itempath;
2944         gint oldpathlen;
2945
2946         oldpathlen = strlen(oldpath);
2947         if (strncmp(oldpath, item->path, oldpathlen) != 0) {
2948                 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
2949                 return TRUE;
2950         }
2951
2952         base = item->path + oldpathlen;
2953         while (*base == G_DIR_SEPARATOR) base++;
2954         if (*base == '\0')
2955                 new_itempath = g_strdup(newpath);
2956         else
2957                 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
2958                                            NULL);
2959         g_free(item->path);
2960         item->path = new_itempath;
2961
2962         return FALSE;
2963 }
2964
2965 typedef struct _get_list_uid_data {
2966         Folder *folder;
2967         IMAPSession *session;
2968         IMAPFolderItem *item;
2969         GSList **msgnum_list;
2970         gboolean done;
2971 } get_list_uid_data;
2972
2973 static void *get_list_of_uids_thread(void *data)
2974 {
2975         get_list_uid_data *stuff = (get_list_uid_data *)data;
2976         Folder *folder = stuff->folder;
2977         IMAPFolderItem *item = stuff->item;
2978         GSList **msgnum_list = stuff->msgnum_list;
2979         gint ok, nummsgs = 0, lastuid_old;
2980         IMAPSession *session;
2981         GSList *uidlist, *elem;
2982         struct mailimap_set * set;
2983         clist * lep_uidlist;
2984         int r;
2985
2986         session = stuff->session;
2987         if (session == NULL) {
2988                 stuff->done = TRUE;
2989                 return GINT_TO_POINTER(-1);
2990         }
2991         /* no session locking here, it's already locked by caller */
2992         ok = imap_select(session, IMAP_FOLDER(folder), item->item.path,
2993                          NULL, NULL, NULL, NULL, TRUE);
2994         if (ok != IMAP_SUCCESS) {
2995                 stuff->done = TRUE;
2996                 return GINT_TO_POINTER(-1);
2997         }
2998
2999         uidlist = NULL;
3000         
3001         set = mailimap_set_new_interval(item->lastuid + 1, 0);
3002
3003         r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SIMPLE, set,
3004                                  &lep_uidlist);
3005         mailimap_set_free(set);
3006         
3007         if (r == MAILIMAP_NO_ERROR) {
3008                 GSList * fetchuid_list;
3009                 
3010                 fetchuid_list =
3011                         imap_uid_list_from_lep(lep_uidlist);
3012                 mailimap_search_result_free(lep_uidlist);
3013                 
3014                 uidlist = g_slist_concat(fetchuid_list, uidlist);
3015         }
3016         else {
3017                 GSList * fetchuid_list;
3018                 carray * lep_uidtab;
3019                 
3020                 r = imap_threaded_fetch_uid(folder, item->lastuid + 1,
3021                                             &lep_uidtab);
3022                 if (r == MAILIMAP_NO_ERROR) {
3023                         fetchuid_list =
3024                                 imap_uid_list_from_lep_tab(lep_uidtab);
3025                         imap_fetch_uid_list_free(lep_uidtab);
3026                         uidlist = g_slist_concat(fetchuid_list, uidlist);
3027                 }
3028         }
3029         
3030         lastuid_old = item->lastuid;
3031         *msgnum_list = g_slist_copy(item->uid_list);
3032         nummsgs = g_slist_length(*msgnum_list);
3033         debug_print("Got %d uids from cache\n", g_slist_length(item->uid_list));
3034
3035         for (elem = uidlist; elem != NULL; elem = g_slist_next(elem)) {
3036                 guint msgnum;
3037
3038                 msgnum = GPOINTER_TO_INT(elem->data);
3039                 if (msgnum > lastuid_old) {
3040                         *msgnum_list = g_slist_prepend(*msgnum_list, GINT_TO_POINTER(msgnum));
3041                         item->uid_list = g_slist_prepend(item->uid_list, GINT_TO_POINTER(msgnum));
3042                         nummsgs++;
3043
3044                         if(msgnum > item->lastuid)
3045                                 item->lastuid = msgnum;
3046                 }
3047         }
3048         g_slist_free(uidlist);
3049         stuff->done = TRUE;
3050         return GINT_TO_POINTER(nummsgs);
3051 }
3052
3053 static gint get_list_of_uids(IMAPSession *session, Folder *folder, IMAPFolderItem *item, GSList **msgnum_list)
3054 {
3055         gint result;
3056         get_list_uid_data *data = g_new0(get_list_uid_data, 1);
3057         data->done = FALSE;
3058         data->folder = folder;
3059         data->item = item;
3060         data->msgnum_list = msgnum_list;
3061         data->session = session;
3062         if (prefs_common.work_offline && 
3063             !inc_offline_should_override(
3064                 _("Sylpheed-Claws needs network access in order "
3065                   "to access the IMAP server."))) {
3066                 g_free(data);
3067                 return -1;
3068         }
3069
3070         result = GPOINTER_TO_INT(get_list_of_uids_thread(data));
3071         g_free(data);
3072         return result;
3073
3074 }
3075
3076 gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list, gboolean *old_uids_valid)
3077 {
3078         IMAPFolderItem *item = (IMAPFolderItem *)_item;
3079         IMAPSession *session;
3080         gint ok, nummsgs = 0, exists, uid_val, uid_next = 0;
3081         GSList *uidlist = NULL;
3082         gchar *dir;
3083         gboolean selected_folder;
3084         
3085         debug_print("get_num_list\n");
3086         
3087         g_return_val_if_fail(folder != NULL, -1);
3088         g_return_val_if_fail(item != NULL, -1);
3089         g_return_val_if_fail(item->item.path != NULL, -1);
3090         g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3091         g_return_val_if_fail(folder->account != NULL, -1);
3092
3093         session = imap_session_get(folder);
3094         g_return_val_if_fail(session != NULL, -1);
3095         lock_session();
3096
3097         if (FOLDER_ITEM(item)->path) 
3098                 statusbar_print_all(_("Scanning folder %s%c%s ..."),
3099                                       FOLDER_ITEM(item)->folder->name, 
3100                                       G_DIR_SEPARATOR,
3101                                       FOLDER_ITEM(item)->path);
3102         else
3103                 statusbar_print_all(_("Scanning folder %s ..."),
3104                                       FOLDER_ITEM(item)->folder->name);
3105
3106         selected_folder = (session->mbox != NULL) &&
3107                           (!strcmp(session->mbox, item->item.path));
3108         if (selected_folder && time(NULL) - item->use_cache < 2) {
3109                 ok = imap_cmd_noop(session);
3110                 if (ok != IMAP_SUCCESS) {
3111                         debug_print("disconnected!\n");
3112                         session = imap_reconnect_if_possible(folder, session);
3113                         if (session == NULL) {
3114                                 statusbar_pop_all();
3115                                 unlock_session();
3116                                 return -1;
3117                         }
3118                 }
3119                 exists = session->exists;
3120
3121                 uid_next = item->c_uid_next;
3122                 uid_val = item->c_uid_validity;
3123                 *old_uids_valid = TRUE;
3124         } else {
3125                 if (item->use_cache && time(NULL) - item->use_cache < 2) {
3126                         exists = item->c_messages;
3127                         uid_next = item->c_uid_next;
3128                         uid_val = item->c_uid_validity;
3129                         ok = IMAP_SUCCESS;
3130                         debug_print("using cache %d %d %d\n", exists, uid_next, uid_val);
3131                 } else {
3132                         ok = imap_status(session, IMAP_FOLDER(folder), item->item.path, item,
3133                                  &exists, &uid_next, &uid_val, NULL, FALSE);
3134                 }
3135                 item->item.last_num = uid_next - 1;
3136                 
3137                 item->use_cache = (time_t)0;
3138                 if (ok != IMAP_SUCCESS) {
3139                         statusbar_pop_all();
3140                         unlock_session();
3141                         return -1;
3142                 }
3143                 if(item->item.mtime == uid_val)
3144                         *old_uids_valid = TRUE;
3145                 else {
3146                         *old_uids_valid = FALSE;
3147
3148                         debug_print("Freeing imap uid cache (%d != %d)\n",
3149                                         item->item.mtime, uid_val);
3150                         item->lastuid = 0;
3151                         g_slist_free(item->uid_list);
3152                         item->uid_list = NULL;
3153                 
3154                         item->item.mtime = uid_val;
3155
3156                         imap_delete_all_cached_messages((FolderItem *)item);
3157                 }
3158         }
3159
3160         /* If old uid_next matches new uid_next we can be sure no message
3161            was added to the folder */
3162         debug_print("uid_next is %d and item->uid_next %d \n", 
3163                 uid_next, item->uid_next);
3164         if (uid_next == item->uid_next) {
3165                 nummsgs = g_slist_length(item->uid_list);
3166
3167                 /* If number of messages is still the same we
3168                    know our caches message numbers are still valid,
3169                    otherwise if the number of messages has decrease
3170                    we discard our cache to start a new scan to find
3171                    out which numbers have been removed */
3172                 if (exists == nummsgs) {
3173                         debug_print("exists == nummsgs\n");
3174                         *msgnum_list = g_slist_copy(item->uid_list);
3175                         statusbar_pop_all();
3176                         unlock_session();
3177                         return nummsgs;
3178                 } else if (exists < nummsgs) {
3179                         debug_print("Freeing imap uid cache");
3180                         item->lastuid = 0;
3181                         g_slist_free(item->uid_list);
3182                         item->uid_list = NULL;
3183                 }
3184         }
3185
3186         if (exists == 0) {
3187                 *msgnum_list = NULL;
3188                 statusbar_pop_all();
3189                 unlock_session();
3190                 return 0;
3191         }
3192
3193         nummsgs = get_list_of_uids(session, folder, item, &uidlist);
3194
3195         if (nummsgs < 0) {
3196                 statusbar_pop_all();
3197                 unlock_session();
3198                 return -1;
3199         }
3200
3201         if (nummsgs != exists) {
3202                 /* Cache contains more messages then folder, we have cached
3203                    an old UID of a message that was removed and new messages
3204                    have been added too, otherwise the uid_next check would
3205                    not have failed */
3206                 debug_print("Freeing imap uid cache");
3207                 item->lastuid = 0;
3208                 g_slist_free(item->uid_list);
3209                 item->uid_list = NULL;
3210
3211                 g_slist_free(*msgnum_list);
3212
3213                 nummsgs = get_list_of_uids(session, folder, item, &uidlist);
3214         }
3215
3216         *msgnum_list = uidlist;
3217
3218         dir = folder_item_get_path((FolderItem *)item);
3219         debug_print("removing old messages from %s\n", dir);
3220         remove_numbered_files_not_in_list(dir, *msgnum_list);
3221         g_free(dir);
3222         
3223         item->uid_next = uid_next;
3224         
3225         debug_print("get_num_list - ok - %i\n", nummsgs);
3226         statusbar_pop_all();
3227         unlock_session();
3228         return nummsgs;
3229 }
3230
3231 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item)
3232 {
3233         MsgInfo *msginfo;
3234         MsgFlags flags;
3235
3236         flags.perm_flags = MSG_NEW|MSG_UNREAD;
3237         flags.tmp_flags = 0;
3238
3239         g_return_val_if_fail(item != NULL, NULL);
3240         g_return_val_if_fail(file != NULL, NULL);
3241
3242         if (folder_has_parent_of_type(item, F_QUEUE)) {
3243                 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
3244         } else if (folder_has_parent_of_type(item, F_DRAFT)) {
3245                 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
3246         }
3247
3248         msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3249         if (!msginfo) return NULL;
3250         
3251         msginfo->plaintext_file = g_strdup(file);
3252         msginfo->folder = item;
3253
3254         return msginfo;
3255 }
3256
3257 GSList *imap_get_msginfos(Folder *folder, FolderItem *item,
3258                           GSList *msgnum_list)
3259 {
3260         IMAPSession *session;
3261         MsgInfoList *ret = NULL;
3262         gint ok;
3263         
3264         debug_print("get_msginfos\n");
3265         
3266         g_return_val_if_fail(folder != NULL, NULL);
3267         g_return_val_if_fail(item != NULL, NULL);
3268         g_return_val_if_fail(msgnum_list != NULL, NULL);
3269
3270         session = imap_session_get(folder);
3271         g_return_val_if_fail(session != NULL, NULL);
3272         lock_session();
3273         debug_print("IMAP getting msginfos\n");
3274         ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3275                          NULL, NULL, NULL, NULL, FALSE);
3276         if (ok != IMAP_SUCCESS) {
3277                 unlock_session();
3278                 return NULL;
3279         }
3280         if (!(folder_has_parent_of_type(item, F_DRAFT) || 
3281               folder_has_parent_of_type(item, F_QUEUE))) {
3282                 ret = g_slist_concat(ret,
3283                         imap_get_uncached_messages(session, item,
3284                                                    msgnum_list));
3285         } else {
3286                 MsgNumberList *sorted_list, *elem, *llast = NULL;
3287                 gint startnum, lastnum;
3288
3289                 sorted_list = g_slist_sort(g_slist_copy(msgnum_list), g_int_compare);
3290
3291                 startnum = lastnum = GPOINTER_TO_INT(sorted_list->data);
3292
3293                 llast = g_slist_last(ret);
3294                 for (elem = sorted_list;; elem = g_slist_next(elem)) {
3295                         guint num = 0;
3296
3297                         if (elem)
3298                                 num = GPOINTER_TO_INT(elem->data);
3299
3300                         if (num > lastnum + 1 || elem == NULL) {
3301                                 int i;
3302                                 for (i = startnum; i <= lastnum; ++i) {
3303                                         gchar *file;
3304                         
3305                                         file = imap_fetch_msg(folder, item, i);
3306                                         if (file != NULL) {
3307                                                 MsgInfo *msginfo = imap_parse_msg(file, item);
3308                                                 if (msginfo != NULL) {
3309                                                         msginfo->msgnum = i;
3310                                                         if (llast == NULL)
3311                                                                 llast = ret = g_slist_append(ret, msginfo);
3312                                                         else {
3313                                                                 llast = g_slist_append(llast, msginfo);
3314                                                                 llast = llast->next;
3315                                                         }
3316                                                 }
3317                                                 g_free(file);
3318                                         }
3319                                         session_set_access_time(SESSION(session));
3320                                 }
3321
3322                                 if (elem == NULL)
3323                                         break;
3324
3325                                 startnum = num;
3326                         }
3327                         lastnum = num;
3328                 }
3329
3330                 g_slist_free(sorted_list);
3331         }
3332         unlock_session();
3333         return ret;
3334 }
3335
3336 MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid)
3337 {
3338         MsgInfo *msginfo = NULL;
3339         MsgInfoList *msginfolist;
3340         MsgNumberList numlist;
3341
3342         numlist.next = NULL;
3343         numlist.data = GINT_TO_POINTER(uid);
3344
3345         msginfolist = imap_get_msginfos(folder, item, &numlist);
3346         if (msginfolist != NULL) {
3347                 msginfo = msginfolist->data;
3348                 g_slist_free(msginfolist);
3349         }
3350
3351         return msginfo;
3352 }
3353
3354 gboolean imap_scan_required(Folder *folder, FolderItem *_item)
3355 {
3356         IMAPSession *session;
3357         IMAPFolderItem *item = (IMAPFolderItem *)_item;
3358         gint ok, exists = 0, unseen = 0;
3359         guint32 uid_next, uid_val;
3360         gboolean selected_folder;
3361         
3362         g_return_val_if_fail(folder != NULL, FALSE);
3363         g_return_val_if_fail(item != NULL, FALSE);
3364         g_return_val_if_fail(item->item.folder != NULL, FALSE);
3365         g_return_val_if_fail(FOLDER_CLASS(item->item.folder) == &imap_class, FALSE);
3366
3367         if (item->item.path == NULL)
3368                 return FALSE;
3369
3370         session = imap_session_get(folder);
3371         g_return_val_if_fail(session != NULL, FALSE);
3372         lock_session();
3373         selected_folder = (session->mbox != NULL) &&
3374                           (!strcmp(session->mbox, item->item.path));
3375         if (selected_folder && time(NULL) - item->use_cache < 2) {
3376                 ok = imap_cmd_noop(session);
3377                 if (ok != IMAP_SUCCESS) {
3378                         debug_print("disconnected!\n");
3379                         session = imap_reconnect_if_possible(folder, session);
3380                         if (session == NULL)
3381                                 return FALSE;
3382                         lock_session();
3383                 }
3384
3385                 if (session->folder_content_changed
3386                 ||  session->exists != item->item.total_msgs) {
3387                         unlock_session();
3388                         return TRUE;
3389                 }
3390         } else {
3391                 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path, IMAP_FOLDER_ITEM(item),
3392                                  &exists, &uid_next, &uid_val, &unseen, FALSE);
3393                 if (ok != IMAP_SUCCESS) {
3394                         unlock_session();
3395                         return FALSE;
3396                 }
3397
3398                 item->use_cache = time(NULL);
3399                 item->c_messages = exists;
3400                 item->c_uid_next = uid_next;
3401                 item->c_uid_validity = uid_val;
3402                 item->c_unseen = unseen;
3403                 item->item.last_num = uid_next - 1;
3404                 debug_print("uidnext %d, item->uid_next %d, exists %d, item->item.total_msgs %d\n", 
3405                         uid_next, item->uid_next, exists, item->item.total_msgs);
3406                 if ((uid_next != item->uid_next) || (exists != item->item.total_msgs)
3407                     || unseen != item->item.unread_msgs || uid_val != item->item.mtime) {
3408                         unlock_session();
3409                         return TRUE;
3410                 }
3411         }
3412         unlock_session();
3413         return FALSE;
3414 }
3415
3416 void imap_change_flags(Folder *folder, FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
3417 {
3418         IMAPSession *session;
3419         IMAPFlags flags_set = 0, flags_unset = 0;
3420         gint ok = IMAP_SUCCESS;
3421         MsgNumberList numlist;
3422         hashtable_data *ht_data = NULL;
3423
3424         g_return_if_fail(folder != NULL);
3425         g_return_if_fail(folder->klass == &imap_class);
3426         g_return_if_fail(item != NULL);
3427         g_return_if_fail(item->folder == folder);
3428         g_return_if_fail(msginfo != NULL);
3429         g_return_if_fail(msginfo->folder == item);
3430
3431         session = imap_session_get(folder);
3432         if (!session) {
3433                 return;
3434         }
3435
3436         if (!MSG_IS_MARKED(msginfo->flags) &&  (newflags & MSG_MARKED))
3437                 flags_set |= IMAP_FLAG_FLAGGED;
3438         if ( MSG_IS_MARKED(msginfo->flags) && !(newflags & MSG_MARKED))
3439                 flags_unset |= IMAP_FLAG_FLAGGED;
3440
3441         if (!MSG_IS_UNREAD(msginfo->flags) &&  (newflags & MSG_UNREAD))
3442                 flags_unset |= IMAP_FLAG_SEEN;
3443         if ( MSG_IS_UNREAD(msginfo->flags) && !(newflags & MSG_UNREAD))
3444                 flags_set |= IMAP_FLAG_SEEN;
3445
3446         if (!MSG_IS_REPLIED(msginfo->flags) &&  (newflags & MSG_REPLIED))
3447                 flags_set |= IMAP_FLAG_ANSWERED;
3448         if ( MSG_IS_REPLIED(msginfo->flags) && !(newflags & MSG_REPLIED))
3449                 flags_unset |= IMAP_FLAG_ANSWERED;
3450
3451         if (!MSG_IS_DELETED(msginfo->flags) &&  (newflags & MSG_DELETED))
3452                 flags_set |= IMAP_FLAG_DELETED;
3453         if ( MSG_IS_DELETED(msginfo->flags) && !(newflags & MSG_DELETED))
3454                 flags_unset |= IMAP_FLAG_DELETED;
3455
3456         if (!flags_set && !flags_unset) {
3457                 /* the changed flags were not translatable to IMAP-speak.
3458                  * like MSG_POSTFILTERED, so just apply. */
3459                 msginfo->flags.perm_flags = newflags;
3460                 return;
3461         }
3462
3463         lock_session();
3464         if ((ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
3465             NULL, NULL, NULL, NULL, FALSE)) != IMAP_SUCCESS) {
3466                 unlock_session();
3467                 return;
3468         }
3469         numlist.next = NULL;
3470         numlist.data = GINT_TO_POINTER(msginfo->msgnum);
3471
3472         if (IMAP_FOLDER_ITEM(item)->batching) {
3473                 /* instead of performing an UID STORE command for each message change,
3474                  * as a lot of them can change "together", we just fill in hashtables
3475                  * and defer the treatment so that we're able to send only one
3476                  * command.
3477                  */
3478                 debug_print("IMAP batch mode on, deferring flags change\n");
3479                 if (flags_set) {
3480                         ht_data = g_hash_table_lookup(IMAP_FOLDER_ITEM(item)->flags_set_table, 
3481                                 GINT_TO_POINTER(flags_set));
3482                         if (ht_data == NULL) {
3483                                 ht_data = g_new0(hashtable_data, 1);
3484                                 ht_data->session = session;
3485                                 ht_data->item = IMAP_FOLDER_ITEM(item);
3486                                 g_hash_table_insert(IMAP_FOLDER_ITEM(item)->flags_set_table, 
3487                                         GINT_TO_POINTER(flags_set), ht_data);
3488                         }
3489                         if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
3490                                 ht_data->msglist = g_slist_prepend(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum));
3491                 } 
3492                 if (flags_unset) {
3493                         ht_data = g_hash_table_lookup(IMAP_FOLDER_ITEM(item)->flags_unset_table, 
3494                                 GINT_TO_POINTER(flags_unset));
3495                         if (ht_data == NULL) {
3496                                 ht_data = g_new0(hashtable_data, 1);
3497                                 ht_data->session = session;
3498                                 ht_data->item = IMAP_FOLDER_ITEM(item);
3499                                 g_hash_table_insert(IMAP_FOLDER_ITEM(item)->flags_unset_table, 
3500                                         GINT_TO_POINTER(flags_unset), ht_data);
3501                         }
3502                         if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
3503                                 ht_data->msglist = g_slist_prepend(ht_data->msglist, 
3504                                         GINT_TO_POINTER(msginfo->msgnum));              
3505                 }
3506         } else {
3507                 debug_print("IMAP changing flags\n");
3508                 if (flags_set) {
3509                         ok = imap_set_message_flags(session, &numlist, flags_set, TRUE);
3510                         if (ok != IMAP_SUCCESS) {
3511                                 unlock_session();
3512                                 return;
3513                         }
3514                 }
3515
3516                 if (flags_unset) {
3517                         ok = imap_set_message_flags(session, &numlist, flags_unset, FALSE);
3518                         if (ok != IMAP_SUCCESS) {
3519                                 unlock_session();
3520                                 return;
3521                         }
3522                 }
3523         }
3524         msginfo->flags.perm_flags = newflags;
3525         unlock_session();
3526         return;
3527 }
3528
3529 static gint imap_remove_msg(Folder *folder, FolderItem *item, gint uid)
3530 {
3531         gint ok;
3532         IMAPSession *session;
3533         gchar *dir;
3534         MsgNumberList numlist;
3535         
3536         g_return_val_if_fail(folder != NULL, -1);
3537         g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3538         g_return_val_if_fail(item != NULL, -1);
3539
3540         session = imap_session_get(folder);
3541         if (!session) return -1;
3542         lock_session();
3543         ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3544                          NULL, NULL, NULL, NULL, FALSE);
3545         if (ok != IMAP_SUCCESS) {
3546                 unlock_session();
3547                 return ok;
3548         }
3549         numlist.next = NULL;
3550         numlist.data = GINT_TO_POINTER(uid);
3551         
3552         ok = imap_set_message_flags
3553                 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
3554                 &numlist, IMAP_FLAG_DELETED, TRUE);
3555         if (ok != IMAP_SUCCESS) {
3556                 log_warning(_("can't set deleted flags: %d\n"), uid);
3557                 unlock_session();
3558                 return ok;
3559         }
3560
3561         if (!session->uidplus) {
3562                 ok = imap_cmd_expunge(session);
3563         } else {
3564                 gchar *uidstr;
3565
3566                 uidstr = g_strdup_printf("%u", uid);
3567                 ok = imap_cmd_expunge(session);
3568                 g_free(uidstr);
3569         }
3570         if (ok != IMAP_SUCCESS) {
3571                 log_warning(_("can't expunge\n"));
3572                 unlock_session();
3573                 return ok;
3574         }
3575
3576         IMAP_FOLDER_ITEM(item)->uid_list = g_slist_remove(
3577             IMAP_FOLDER_ITEM(item)->uid_list, numlist.data);
3578         dir = folder_item_get_path(item);
3579         if (is_dir_exist(dir))
3580                 remove_numbered_files(dir, uid, uid);
3581         g_free(dir);
3582         unlock_session();
3583         return IMAP_SUCCESS;
3584 }
3585
3586 static gint compare_msginfo(gconstpointer a, gconstpointer b)
3587 {
3588         return ((MsgInfo *)a)->msgnum - ((MsgInfo *)b)->msgnum;
3589 }
3590
3591 static guint gslist_find_next_num(MsgNumberList **list, guint num)
3592 {
3593         GSList *elem;
3594
3595         g_return_val_if_fail(list != NULL, -1);
3596
3597         for (elem = *list; elem != NULL; elem = g_slist_next(elem))
3598                 if (GPOINTER_TO_INT(elem->data) >= num)
3599                         break;
3600         *list = elem;
3601         return elem != NULL ? GPOINTER_TO_INT(elem->data) : (gint)-1;
3602 }
3603
3604 /*
3605  * NEW and DELETED flags are not syncronized
3606  * - The NEW/RECENT flags in IMAP folders can not really be directly
3607  *   modified by Sylpheed
3608  * - The DELETE/DELETED flag in IMAP and Sylpheed don't have the same
3609  *   meaning, in IMAP it always removes the messages from the FolderItem
3610  *   in Sylpheed it can mean to move the message to trash
3611  */
3612
3613 typedef struct _get_flags_data {
3614         Folder *folder;
3615         FolderItem *item;
3616         MsgInfoList *msginfo_list;
3617         GRelation *msgflags;
3618         gboolean full_search;
3619         gboolean done;
3620 } get_flags_data;
3621
3622 static /*gint*/ void *imap_get_flags_thread(void *data)
3623 {
3624         get_flags_data *stuff = (get_flags_data *)data;
3625         Folder *folder = stuff->folder;
3626         FolderItem *item = stuff->item;
3627         MsgInfoList *msginfo_list = stuff->msginfo_list;
3628         GRelation *msgflags = stuff->msgflags;
3629         gboolean full_search = stuff->full_search;
3630         IMAPSession *session;
3631         GSList *sorted_list = NULL;
3632         GSList *unseen = NULL, *answered = NULL, *flagged = NULL, *deleted = NULL;
3633         GSList *p_unseen, *p_answered, *p_flagged, *p_deleted;
3634         GSList *elem;
3635         GSList *seq_list, *cur;
3636         gboolean reverse_seen = FALSE;
3637         GString *cmd_buf;
3638         gint ok;
3639         gint exists_cnt, unseen_cnt;
3640         gboolean selected_folder;
3641         
3642         if (folder == NULL || item == NULL) {
3643                 stuff->done = TRUE;
3644                 return GINT_TO_POINTER(-1);
3645         }
3646
3647         session = imap_session_get(folder);
3648         if (session == NULL) {
3649                 stuff->done = TRUE;
3650                 return GINT_TO_POINTER(-1);
3651         }
3652         lock_session();
3653         selected_folder = (session->mbox != NULL) &&
3654                           (!strcmp(session->mbox, item->path));
3655
3656         if (!selected_folder) {
3657                 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3658                         &exists_cnt, NULL, &unseen_cnt, NULL, TRUE);
3659                 if (ok != IMAP_SUCCESS) {
3660                         stuff->done = TRUE;
3661                         unlock_session();
3662                         return GINT_TO_POINTER(-1);
3663                 }
3664
3665                 if (unseen_cnt > exists_cnt / 2)
3666                         reverse_seen = TRUE;
3667         } 
3668         else {
3669                 if (item->unread_msgs > item->total_msgs / 2)
3670                         reverse_seen = TRUE;
3671         }
3672
3673         cmd_buf = g_string_new(NULL);
3674
3675         sorted_list = g_slist_sort(g_slist_copy(msginfo_list), compare_msginfo);
3676         if (!full_search) {
3677                 seq_list = imap_get_lep_set_from_msglist(msginfo_list);
3678         } else {
3679                 struct mailimap_set * set;
3680                 set = mailimap_set_new_interval(1, 0);
3681                 seq_list = g_slist_append(NULL, set);
3682         }
3683
3684         for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
3685                 struct mailimap_set * imapset;
3686                 clist * lep_uidlist;
3687                 int r;
3688                 
3689                 imapset = cur->data;
3690                 if (reverse_seen) {
3691                         r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SEEN,
3692                                                  imapset, &lep_uidlist);
3693                 }
3694                 else {
3695                         r = imap_threaded_search(folder,
3696                                                  IMAP_SEARCH_TYPE_UNSEEN,
3697                                                  imapset, &lep_uidlist);
3698                 }
3699                 if (r == MAILIMAP_NO_ERROR) {
3700                         GSList * uidlist;
3701                         
3702                         uidlist = imap_uid_list_from_lep(lep_uidlist);
3703                         mailimap_search_result_free(lep_uidlist);
3704                         
3705                         unseen = g_slist_concat(unseen, uidlist);
3706                 }
3707                 
3708                 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_FLAGGED,
3709                                          imapset, &lep_uidlist);
3710                 if (r == MAILIMAP_NO_ERROR) {
3711                         GSList * uidlist;
3712
3713                         uidlist = imap_uid_list_from_lep(lep_uidlist);
3714                         mailimap_search_result_free(lep_uidlist);
3715
3716                         flagged = g_slist_concat(flagged, uidlist);
3717                 }
3718
3719                 if (item->opened || item->processing_pending || item == folder->inbox) {
3720                         r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_ANSWERED,
3721                                                  imapset, &lep_uidlist);
3722                         if (r == MAILIMAP_NO_ERROR) {
3723                                 GSList * uidlist;
3724
3725                                 uidlist = imap_uid_list_from_lep(lep_uidlist);
3726                                 mailimap_search_result_free(lep_uidlist);
3727
3728                                 answered = g_slist_concat(answered, uidlist);
3729                         }
3730
3731                         r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_DELETED,
3732                                                  imapset, &lep_uidlist);
3733                         if (r == MAILIMAP_NO_ERROR) {
3734                                 GSList * uidlist;
3735
3736                                 uidlist = imap_uid_list_from_lep(lep_uidlist);
3737                                 mailimap_search_result_free(lep_uidlist);
3738
3739                                 deleted = g_slist_concat(deleted, uidlist);
3740                         }
3741                 }
3742         }
3743
3744         p_unseen = unseen;
3745         p_answered = answered;
3746         p_flagged = flagged;
3747         p_deleted = deleted;
3748
3749         for (elem = sorted_list; elem != NULL; elem = g_slist_next(elem)) {
3750                 MsgInfo *msginfo;
3751                 MsgPermFlags flags;
3752                 gboolean wasnew;
3753                 
3754                 msginfo = (MsgInfo *) elem->data;
3755                 flags = msginfo->flags.perm_flags;
3756                 wasnew = (flags & MSG_NEW);
3757                 if (item->opened || item->processing_pending || item == folder->inbox) {
3758                         flags &= ~((reverse_seen ? 0 : MSG_UNREAD | MSG_NEW) | MSG_REPLIED | MSG_MARKED);
3759                 } else {
3760                         flags &= ~((reverse_seen ? 0 : MSG_UNREAD | MSG_NEW | MSG_MARKED));
3761                 }
3762                 if (reverse_seen)
3763                         flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
3764                 if (gslist_find_next_num(&p_unseen, msginfo->msgnum) == msginfo->msgnum) {
3765                         if (!reverse_seen) {
3766                                 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
3767                         } else {
3768                                 flags &= ~(MSG_UNREAD | MSG_NEW);
3769                         }
3770                 }
3771                 
3772                 if (gslist_find_next_num(&p_flagged, msginfo->msgnum) == msginfo->msgnum)
3773                         flags |= MSG_MARKED;
3774                 else
3775                         flags &= ~MSG_MARKED;
3776
3777                 if (item->opened || item->processing_pending || item == folder->inbox) {
3778                         if (gslist_find_next_num(&p_answered, msginfo->msgnum) == msginfo->msgnum)
3779                                 flags |= MSG_REPLIED;
3780                         else
3781                                 flags &= ~MSG_REPLIED;
3782                         if (gslist_find_next_num(&p_deleted, msginfo->msgnum) == msginfo->msgnum)
3783                                 flags |= MSG_DELETED;
3784                         else
3785                                 flags &= ~MSG_DELETED;
3786                 }
3787                 g_relation_insert(msgflags, msginfo, GINT_TO_POINTER(flags));
3788         }
3789
3790         imap_lep_set_free(seq_list);
3791         g_slist_free(flagged);
3792         g_slist_free(deleted);
3793         g_slist_free(answered);
3794         g_slist_free(unseen);
3795         g_slist_free(sorted_list);
3796         g_string_free(cmd_buf, TRUE);
3797
3798         stuff->done = TRUE;
3799         unlock_session();
3800         return GINT_TO_POINTER(0);
3801 }
3802
3803 static gint imap_get_flags(Folder *folder, FolderItem *item,
3804                            MsgInfoList *msginfo_list, GRelation *msgflags)
3805 {
3806         gint result;
3807         get_flags_data *data = g_new0(get_flags_data, 1);
3808         data->done = FALSE;
3809         data->folder = folder;
3810         data->item = item;
3811         data->msginfo_list = msginfo_list;
3812         data->msgflags = msgflags;
3813         data->full_search = FALSE;
3814
3815         GSList *tmp = NULL, *cur;
3816         
3817         if (prefs_common.work_offline && 
3818             !inc_offline_should_override(
3819                 _("Sylpheed-Claws needs network access in order "
3820                   "to access the IMAP server."))) {
3821                 g_free(data);
3822                 return -1;
3823         }
3824
3825         tmp = folder_item_get_msg_list(item);
3826
3827         if (g_slist_length(tmp) <= g_slist_length(msginfo_list))
3828                 data->full_search = TRUE;
3829         
3830         for (cur = tmp; cur; cur = cur->next)
3831                 procmsg_msginfo_free((MsgInfo *)cur->data);
3832         
3833         g_slist_free(tmp);
3834
3835         result = GPOINTER_TO_INT(imap_get_flags_thread(data));
3836         
3837         g_free(data);
3838         return result;
3839
3840 }
3841
3842 static gboolean process_flags(gpointer key, gpointer value, gpointer user_data)
3843 {
3844         gboolean flags_set = GPOINTER_TO_INT(user_data);
3845         gint flags_value = GPOINTER_TO_INT(key);
3846         hashtable_data *data = (hashtable_data *)value;
3847         IMAPFolderItem *_item = data->item;
3848         FolderItem *item = (FolderItem *)_item;
3849         gint ok = IMAP_ERROR;
3850         IMAPSession *session = imap_session_get(item->folder);
3851
3852         data->msglist = g_slist_reverse(data->msglist);
3853         
3854         debug_print("IMAP %ssetting flags to %d for %d messages\n",
3855                 flags_set?"":"un",
3856                 flags_value,
3857                 g_slist_length(data->msglist));
3858         
3859         if (session) {
3860                 lock_session();
3861                 ok = imap_select(session, IMAP_FOLDER(item->folder), item->path,
3862                          NULL, NULL, NULL, NULL, FALSE);
3863         }
3864         if (ok == IMAP_SUCCESS) {
3865                 imap_set_message_flags(data->session, data->msglist, flags_value, flags_set);
3866         } else {
3867                 g_warning("can't select mailbox %s\n", item->path);
3868         }
3869         if (session)
3870                 unlock_session();
3871         g_slist_free(data->msglist);    
3872         g_free(data);
3873         return TRUE;
3874 }
3875
3876 static void process_hashtable(IMAPFolderItem *item)
3877 {
3878         if (item->flags_set_table) {
3879                 g_hash_table_foreach_remove(item->flags_set_table, process_flags, GINT_TO_POINTER(TRUE));
3880                 g_hash_table_destroy(item->flags_set_table);
3881                 item->flags_set_table = NULL;
3882         }
3883         if (item->flags_unset_table) {
3884                 g_hash_table_foreach_remove(item->flags_unset_table, process_flags, GINT_TO_POINTER(FALSE));
3885                 g_hash_table_destroy(item->flags_unset_table);
3886                 item->flags_unset_table = NULL;
3887         }
3888 }
3889
3890 static void imap_set_batch (Folder *folder, FolderItem *_item, gboolean batch)
3891 {
3892         IMAPFolderItem *item = (IMAPFolderItem *)_item;
3893
3894         g_return_if_fail(item != NULL);
3895         
3896         if (item->batching == batch)
3897                 return;
3898         
3899         if (batch) {
3900                 item->batching = TRUE;
3901                 debug_print("IMAP switching to batch mode\n");
3902                 if (!item->flags_set_table) {
3903                         item->flags_set_table = g_hash_table_new(NULL, g_direct_equal);
3904                 }
3905                 if (!item->flags_unset_table) {
3906                         item->flags_unset_table = g_hash_table_new(NULL, g_direct_equal);
3907                 }
3908         } else {
3909                 debug_print("IMAP switching away from batch mode\n");
3910                 /* process stuff */
3911                 process_hashtable(item);
3912                 item->batching = FALSE;
3913         }
3914 }
3915
3916
3917
3918 /* data types conversion libetpan <-> sylpheed */
3919
3920
3921
3922 #define ETPAN_IMAP_MB_MARKED      1
3923 #define ETPAN_IMAP_MB_UNMARKED    2
3924 #define ETPAN_IMAP_MB_NOSELECT    4
3925 #define ETPAN_IMAP_MB_NOINFERIORS 8
3926
3927 static int imap_flags_to_flags(struct mailimap_mbx_list_flags * imap_flags)
3928 {
3929   int flags;
3930   clistiter * cur;
3931   
3932   flags = 0;
3933   if (imap_flags->mbf_type == MAILIMAP_MBX_LIST_FLAGS_SFLAG) {
3934     switch (imap_flags->mbf_sflag) {
3935     case MAILIMAP_MBX_LIST_SFLAG_MARKED:
3936       flags |= ETPAN_IMAP_MB_MARKED;
3937       break;
3938     case MAILIMAP_MBX_LIST_SFLAG_NOSELECT:
3939       flags |= ETPAN_IMAP_MB_NOSELECT;
3940       break;
3941     case MAILIMAP_MBX_LIST_SFLAG_UNMARKED:
3942       flags |= ETPAN_IMAP_MB_UNMARKED;
3943       break;
3944     }
3945   }
3946   
3947   for(cur = clist_begin(imap_flags->mbf_oflags) ; cur != NULL ;
3948       cur = clist_next(cur)) {
3949     struct mailimap_mbx_list_oflag * oflag;
3950     
3951     oflag = clist_content(cur);
3952     
3953     switch (oflag->of_type) {
3954     case MAILIMAP_MBX_LIST_OFLAG_NOINFERIORS:
3955       flags |= ETPAN_IMAP_MB_NOINFERIORS;
3956       break;
3957     }
3958   }
3959   
3960   return flags;
3961 }
3962
3963 static GSList * imap_list_from_lep(IMAPFolder * folder,
3964                                    clist * list, const gchar * real_path, gboolean all)
3965 {
3966         clistiter * iter;
3967         GSList * item_list = NULL, *llast = NULL;
3968         
3969         for(iter = clist_begin(list) ; iter != NULL ;
3970             iter = clist_next(iter)) {
3971                 struct mailimap_mailbox_list * mb;
3972                 int flags;
3973                 char delimiter;
3974                 char * name;
3975                 char * dup_name;
3976                 gchar * base;
3977                 gchar * loc_name;
3978                 gchar * loc_path;
3979                 FolderItem *new_item;
3980                 
3981                 mb = clist_content(iter);
3982
3983                 if (mb == NULL)
3984                         continue;
3985
3986                 flags = 0;
3987                 if (mb->mb_flag != NULL)
3988                         flags = imap_flags_to_flags(mb->mb_flag);
3989                 
3990                 delimiter = mb->mb_delimiter;
3991                 name = mb->mb_name;
3992                 
3993                 dup_name = strdup(name);                
3994                 if (delimiter != '\0')
3995                         subst_char(dup_name, delimiter, '/');
3996                 
3997                 base = g_path_get_basename(dup_name);
3998                 if (base[0] == '.') {
3999                         g_free(base);
4000                         free(dup_name);
4001                         continue;
4002                 }
4003                 
4004                 if (!all && strcmp(dup_name, real_path) == 0) {
4005                         g_free(base);
4006                         free(dup_name);
4007                         continue;
4008                 }
4009
4010                 if (!all && dup_name[strlen(dup_name)-1] == '/') {
4011                         g_free(base);
4012                         free(dup_name);
4013                         continue;
4014                 }
4015                 
4016                 loc_name = imap_modified_utf7_to_utf8(base);
4017                 loc_path = imap_modified_utf7_to_utf8(dup_name);
4018                 
4019                 new_item = folder_item_new(FOLDER(folder), loc_name, loc_path);
4020                 if ((flags & ETPAN_IMAP_MB_NOINFERIORS) != 0)
4021                         new_item->no_sub = TRUE;
4022                 if (strcmp(dup_name, "INBOX") != 0 &&
4023                     ((flags & ETPAN_IMAP_MB_NOSELECT) != 0))
4024                         new_item->no_select = TRUE;
4025                 
4026                 if (item_list == NULL)
4027                         llast = item_list = g_slist_append(item_list, new_item);
4028                 else {
4029                         llast = g_slist_append(llast, new_item);
4030                         llast = llast->next;
4031                 }
4032                 debug_print("folder '%s' found.\n", loc_path);
4033                 g_free(base);
4034                 g_free(loc_path);
4035                 g_free(loc_name);
4036                 
4037                 free(dup_name);
4038         }
4039         
4040         return item_list;
4041 }
4042
4043 static GSList * imap_get_lep_set_from_numlist(MsgNumberList *numlist)
4044 {
4045         GSList *sorted_list, *cur;
4046         guint first, last, next;
4047         GSList *ret_list = NULL, *llast = NULL;
4048         unsigned int count;
4049         struct mailimap_set * current_set;
4050         unsigned int item_count;
4051         
4052         if (numlist == NULL)
4053                 return NULL;
4054         
4055         count = 0;
4056         current_set = mailimap_set_new_empty();
4057         
4058         sorted_list = g_slist_copy(numlist);
4059         sorted_list = g_slist_sort(sorted_list, g_int_compare);
4060
4061         first = GPOINTER_TO_INT(sorted_list->data);
4062         
4063         item_count = 0;
4064         for (cur = sorted_list; cur != NULL; cur = g_slist_next(cur)) {
4065                 if (GPOINTER_TO_INT(cur->data) == 0)
4066                         continue;
4067                 
4068                 item_count ++;
4069
4070                 last = GPOINTER_TO_INT(cur->data);
4071                 if (cur->next)
4072                         next = GPOINTER_TO_INT(cur->next->data);
4073                 else
4074                         next = 0;
4075
4076                 if (last + 1 != next || next == 0) {
4077
4078                         struct mailimap_set_item * item;
4079                         item = mailimap_set_item_new(first, last);
4080                         mailimap_set_add(current_set, item);
4081                         count ++;
4082                         
4083                         first = next;
4084                         
4085                         if (count >= IMAP_SET_MAX_COUNT) {
4086                                 if (ret_list == NULL)
4087                                         llast = ret_list = g_slist_append(ret_list,
4088                                                           current_set);
4089                                 else {
4090                                         llast = g_slist_append(llast, current_set);
4091                                         llast = llast->next;
4092                                 }
4093                                 current_set = mailimap_set_new_empty();
4094                                 count = 0;
4095                                 item_count = 0;
4096                         }
4097                 }
4098         }
4099         
4100         if (clist_count(current_set->set_list) > 0) {
4101                 ret_list = g_slist_append(ret_list,
4102                                           current_set);
4103         }
4104         
4105         g_slist_free(sorted_list);
4106
4107         return ret_list;
4108 }
4109
4110 static GSList * imap_get_lep_set_from_msglist(MsgInfoList *msglist)
4111 {
4112         MsgNumberList *numlist = NULL;
4113         MsgInfoList *cur;
4114         GSList *seq_list;
4115
4116         for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
4117                 MsgInfo *msginfo = (MsgInfo *) cur->data;
4118
4119                 numlist = g_slist_prepend(numlist, GINT_TO_POINTER(msginfo->msgnum));
4120         }
4121         numlist = g_slist_reverse(numlist);
4122         seq_list = imap_get_lep_set_from_numlist(numlist);
4123         g_slist_free(numlist);
4124
4125         return seq_list;
4126 }
4127
4128 static GSList * imap_uid_list_from_lep(clist * list)
4129 {
4130         clistiter * iter;
4131         GSList * result;
4132         
4133         result = NULL;
4134         
4135         for(iter = clist_begin(list) ; iter != NULL ;
4136             iter = clist_next(iter)) {
4137                 uint32_t * puid;
4138                 
4139                 puid = clist_content(iter);
4140                 result = g_slist_prepend(result, GINT_TO_POINTER(* puid));
4141         }
4142         
4143         result = g_slist_reverse(result);
4144         return result;
4145 }
4146
4147 static GSList * imap_uid_list_from_lep_tab(carray * list)
4148 {
4149         unsigned int i;
4150         GSList * result;
4151         
4152         result = NULL;
4153         
4154         for(i = 0 ; i < carray_count(list) ; i ++) {
4155                 uint32_t * puid;
4156                 
4157                 puid = carray_get(list, i);
4158                 result = g_slist_prepend(result, GINT_TO_POINTER(* puid));
4159         }
4160         result = g_slist_reverse(result);
4161         return result;
4162 }
4163
4164 static MsgInfo *imap_envelope_from_lep(struct imap_fetch_env_info * info,
4165                                        FolderItem *item)
4166 {
4167         MsgInfo *msginfo = NULL;
4168         guint32 uid = 0;
4169         size_t size = 0;
4170         MsgFlags flags = {0, 0};
4171         
4172         if (info->headers == NULL)
4173                 return NULL;
4174
4175         MSG_SET_TMP_FLAGS(flags, MSG_IMAP);
4176         if (folder_has_parent_of_type(item, F_QUEUE)) {
4177                 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
4178         } else if (folder_has_parent_of_type(item, F_DRAFT)) {
4179                 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
4180         }
4181         flags.perm_flags = info->flags;
4182         
4183         uid = info->uid;
4184         size = info->size;
4185         msginfo = procheader_parse_str(info->headers, flags, FALSE, FALSE);
4186         
4187         if (msginfo) {
4188                 msginfo->msgnum = uid;
4189                 msginfo->size = size;
4190         }
4191
4192         return msginfo;
4193 }
4194
4195 static void imap_lep_set_free(GSList *seq_list)
4196 {
4197         GSList * cur;
4198         
4199         for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) {
4200                 struct mailimap_set * imapset;
4201                 
4202                 imapset = cur->data;
4203                 mailimap_set_free(imapset);
4204         }
4205         g_slist_free(seq_list);
4206 }
4207
4208 static struct mailimap_flag_list * imap_flag_to_lep(IMAPFlags flags)
4209 {
4210         struct mailimap_flag_list * flag_list;
4211         
4212         flag_list = mailimap_flag_list_new_empty();
4213         
4214         if (IMAP_IS_SEEN(flags))
4215                 mailimap_flag_list_add(flag_list,
4216                                        mailimap_flag_new_seen());
4217         if (IMAP_IS_ANSWERED(flags))
4218                 mailimap_flag_list_add(flag_list,
4219                                        mailimap_flag_new_answered());
4220         if (IMAP_IS_FLAGGED(flags))
4221                 mailimap_flag_list_add(flag_list,
4222                                        mailimap_flag_new_flagged());
4223         if (IMAP_IS_DELETED(flags))
4224                 mailimap_flag_list_add(flag_list,
4225                                        mailimap_flag_new_deleted());
4226         if (IMAP_IS_DRAFT(flags))
4227                 mailimap_flag_list_add(flag_list,
4228                                        mailimap_flag_new_draft());
4229         
4230         return flag_list;
4231 }
4232
4233 guint imap_folder_get_refcnt(Folder *folder)
4234 {
4235         return ((IMAPFolder *)folder)->refcnt;
4236 }
4237
4238 void imap_folder_ref(Folder *folder)
4239 {
4240         ((IMAPFolder *)folder)->refcnt++;
4241 }
4242
4243 void imap_folder_unref(Folder *folder)
4244 {
4245         if (((IMAPFolder *)folder)->refcnt > 0)
4246                 ((IMAPFolder *)folder)->refcnt--;
4247 }
4248
4249 #else /* HAVE_LIBETPAN */
4250
4251 static FolderClass imap_class;
4252
4253 static XMLTag *imap_item_get_xml(Folder *folder, FolderItem *item);
4254 static void imap_item_set_xml(Folder *folder, FolderItem *item, XMLTag *tag);
4255
4256 static Folder   *imap_folder_new        (const gchar    *name,
4257                                          const gchar    *path)
4258 {
4259         return NULL;
4260 }
4261 static gint     imap_create_tree        (Folder         *folder)
4262 {
4263         return -1;
4264 }
4265 static FolderItem *imap_create_folder   (Folder         *folder,
4266                                          FolderItem     *parent,
4267                                          const gchar    *name)
4268 {
4269         return NULL;
4270 }
4271 static gint     imap_rename_folder      (Folder         *folder,
4272                                          FolderItem     *item, 
4273                                          const gchar    *name)
4274 {
4275         return -1;
4276 }
4277
4278 gchar imap_get_path_separator_for_item(FolderItem *item)
4279 {
4280         return '/';
4281 }
4282
4283 FolderClass *imap_get_class(void)
4284 {
4285         if (imap_class.idstr == NULL) {
4286                 imap_class.type = F_IMAP;
4287                 imap_class.idstr = "imap";
4288                 imap_class.uistr = "IMAP4";
4289
4290                 imap_class.new_folder = imap_folder_new;
4291                 imap_class.create_tree = imap_create_tree;
4292                 imap_class.create_folder = imap_create_folder;
4293                 imap_class.rename_folder = imap_rename_folder;
4294
4295                 imap_class.set_xml = folder_set_xml;
4296                 imap_class.get_xml = folder_get_xml;
4297                 imap_class.item_set_xml = imap_item_set_xml;
4298                 imap_class.item_get_xml = imap_item_get_xml;
4299                 /* nothing implemented */
4300         }
4301
4302         return &imap_class;
4303 }
4304 #endif
4305
4306 void imap_synchronise(FolderItem *item) 
4307 {
4308         imap_gtk_synchronise(item);
4309 }
4310
4311 static void imap_item_set_xml(Folder *folder, FolderItem *item, XMLTag *tag)
4312 {
4313 #ifdef HAVE_LIBETPAN
4314         GList *cur;
4315 #endif
4316         folder_item_set_xml(folder, item, tag);
4317         
4318 #ifdef HAVE_LIBETPAN
4319         for (cur = tag->attr; cur != NULL; cur = g_list_next(cur)) {
4320                 XMLAttr *attr = (XMLAttr *) cur->data;
4321
4322                 if (!attr || !attr->name || !attr->value) continue;
4323                 if (!strcmp(attr->name, "uidnext"))
4324                         IMAP_FOLDER_ITEM(item)->uid_next = atoi(attr->value);
4325         }
4326 #endif
4327 }
4328
4329 static XMLTag *imap_item_get_xml(Folder *folder, FolderItem *item)
4330 {
4331         XMLTag *tag;
4332
4333         tag = folder_item_get_xml(folder, item);
4334
4335 #ifdef HAVE_LIBETPAN
4336         xml_tag_add_attr(tag, xml_attr_new_int("uidnext", 
4337                         IMAP_FOLDER_ITEM(item)->uid_next));
4338
4339 #endif
4340         return tag;
4341 }