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