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