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