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