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