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