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