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