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