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