1032586fce92df391ec1c94dcd435bcb92591063
[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
1912         g_return_val_if_fail(folder != NULL, -1);
1913         g_return_val_if_fail(item != NULL, -1);
1914         g_return_val_if_fail(item->path != NULL, -1);
1915
1916         session = imap_session_get(folder);
1917         if (!session) {
1918                 return -1;
1919         }
1920         lock_session();
1921         path = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1922
1923         ok = imap_cmd_delete(session, path);
1924         if (ok != IMAP_SUCCESS) {
1925                 gchar *tmp = g_strdup_printf("%s%c", path, 
1926                                 imap_get_path_separator(IMAP_FOLDER(folder), path));
1927                 g_free(path);
1928                 path = tmp;
1929                 ok = imap_cmd_delete(session, path);
1930         }
1931
1932         if (ok != IMAP_SUCCESS) {
1933                 log_warning(_("can't delete mailbox\n"));
1934                 g_free(path);
1935                 unlock_session();
1936                 return -1;
1937         }
1938
1939         g_free(path);
1940         cache_dir = folder_item_get_path(item);
1941         if (is_dir_exist(cache_dir) && remove_dir_recursive(cache_dir) < 0)
1942                 g_warning("can't remove directory '%s'\n", cache_dir);
1943         g_free(cache_dir);
1944         folder_item_remove(item);
1945         unlock_session();
1946         return 0;
1947 }
1948
1949 static gint imap_remove_folder(Folder *folder, FolderItem *item)
1950 {
1951         GNode *node, *next;
1952
1953         g_return_val_if_fail(item != NULL, -1);
1954         g_return_val_if_fail(item->folder != NULL, -1);
1955         g_return_val_if_fail(item->node != NULL, -1);
1956
1957         node = item->node->children;
1958         while (node != NULL) {
1959                 next = node->next;
1960                 if (imap_remove_folder(folder, FOLDER_ITEM(node->data)) < 0)
1961                         return -1;
1962                 node = next;
1963         }
1964         debug_print("IMAP removing %s\n", item->path);
1965
1966         if (imap_remove_all_msg(folder, item) < 0)
1967                 return -1;
1968         return imap_remove_folder_real(folder, item);
1969 }
1970
1971 typedef struct _uncached_data {
1972         IMAPSession *session;
1973         FolderItem *item;
1974         MsgNumberList *numlist;
1975         guint cur;
1976         guint total;
1977         gboolean done;
1978 } uncached_data;
1979
1980 static void *imap_get_uncached_messages_thread(void *data)
1981 {
1982         uncached_data *stuff = (uncached_data *)data;
1983         IMAPSession *session = stuff->session;
1984         FolderItem *item = stuff->item;
1985         MsgNumberList *numlist = stuff->numlist;
1986         
1987         GSList *newlist = NULL;
1988         GSList *llast = NULL;
1989         GSList *seq_list, *cur;
1990
1991         debug_print("uncached_messages\n");
1992         
1993         if (session == NULL || item == NULL || item->folder == NULL
1994             || FOLDER_CLASS(item->folder) != &imap_class) {
1995                 stuff->done = TRUE;
1996                 return NULL;
1997         }
1998         
1999         seq_list = imap_get_lep_set_from_numlist(numlist);
2000         debug_print("get msgs info\n");
2001         for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
2002                 struct mailimap_set * imapset;
2003                 unsigned int i;
2004                 int r;
2005                 carray * env_list;
2006                 int count;
2007                 
2008                 imapset = cur->data;
2009                 
2010                 r = imap_threaded_fetch_env(session->folder,
2011                                             imapset, &env_list);
2012                 if (r != MAILIMAP_NO_ERROR)
2013                         continue;
2014                 
2015                 count = 0;
2016                 for(i = 0 ; i < carray_count(env_list) ; i ++) {
2017                         struct imap_fetch_env_info * info;
2018                         MsgInfo * msginfo;
2019                         
2020                         info = carray_get(env_list, i);
2021                         msginfo = imap_envelope_from_lep(info, item);
2022                         if (msginfo == NULL)
2023                                 continue;
2024                         msginfo->folder = item;
2025                         if (!newlist)
2026                                 llast = newlist = g_slist_append(newlist, msginfo);
2027                         else {
2028                                 llast = g_slist_append(llast, msginfo);
2029                                 llast = llast->next;
2030                         }
2031                         count ++;
2032                 }
2033                 
2034                 imap_fetch_env_free(env_list);
2035         }
2036         
2037         session_set_access_time(SESSION(session));
2038         stuff->done = TRUE;
2039         return newlist;
2040 }
2041
2042 #define MAX_MSG_NUM 50
2043
2044 static GSList *imap_get_uncached_messages(IMAPSession *session,
2045                                         FolderItem *item,
2046                                         MsgNumberList *numlist)
2047 {
2048         GSList *result = NULL;
2049         GSList * cur;
2050         uncached_data *data = g_new0(uncached_data, 1);
2051         int finished;
2052         
2053         finished = 0;
2054         cur = numlist;
2055         data->total = g_slist_length(numlist);
2056         debug_print("messages list : %i\n", data->total);
2057
2058         while (cur != NULL) {
2059                 GSList * partial_result;
2060                 int count;
2061                 GSList * newlist;
2062                 GSList * llast;
2063                 
2064                 llast = NULL;
2065                 count = 0;
2066                 newlist = NULL;
2067                 while (count < MAX_MSG_NUM) {
2068                         void * p;
2069                         
2070                         p = cur->data;
2071                         
2072                         if (newlist == NULL)
2073                                 llast = newlist = g_slist_append(newlist, p);
2074                         else {
2075                                 llast = g_slist_append(llast, p);
2076                                 llast = llast->next;
2077                         }
2078                         count ++;
2079                         
2080                         cur = cur->next;
2081                         if (cur == NULL)
2082                                 break;
2083                 }
2084                 
2085                 data->done = FALSE;
2086                 data->session = session;
2087                 data->item = item;
2088                 data->numlist = newlist;
2089                 data->cur += count;
2090                 
2091                 if (prefs_common.work_offline && !inc_offline_should_override()) {
2092                         g_free(data);
2093                         return NULL;
2094                 }
2095                 
2096                 partial_result =
2097                         (GSList *)imap_get_uncached_messages_thread(data);
2098                 
2099                 statusbar_progress_all(data->cur,data->total, 1);
2100                 
2101                 g_slist_free(newlist);
2102                 
2103                 result = g_slist_concat(result, partial_result);
2104         }
2105         g_free(data);
2106         
2107         statusbar_progress_all(0,0,0);
2108         statusbar_pop_all();
2109         
2110         return result;
2111 }
2112
2113 static void imap_delete_all_cached_messages(FolderItem *item)
2114 {
2115         gchar *dir;
2116
2117         g_return_if_fail(item != NULL);
2118         g_return_if_fail(item->folder != NULL);
2119         g_return_if_fail(FOLDER_CLASS(item->folder) == &imap_class);
2120
2121         debug_print("Deleting all cached messages...\n");
2122
2123         dir = folder_item_get_path(item);
2124         if (is_dir_exist(dir))
2125                 remove_all_numbered_files(dir);
2126         g_free(dir);
2127
2128         debug_print("done.\n");
2129 }
2130
2131 static IMAPNameSpace *imap_find_namespace_from_list(GList *ns_list,
2132                                                     const gchar *path)
2133 {
2134         IMAPNameSpace *namespace = NULL;
2135         gchar *tmp_path, *name;
2136
2137         if (!path) path = "";
2138
2139         for (; ns_list != NULL; ns_list = ns_list->next) {
2140                 IMAPNameSpace *tmp_ns = ns_list->data;
2141
2142                 Xstrcat_a(tmp_path, path, "/", return namespace);
2143                 Xstrdup_a(name, tmp_ns->name, return namespace);
2144                 if (tmp_ns->separator && tmp_ns->separator != '/') {
2145                         subst_char(tmp_path, tmp_ns->separator, '/');
2146                         subst_char(name, tmp_ns->separator, '/');
2147                 }
2148                 if (strncmp(tmp_path, name, strlen(name)) == 0)
2149                         namespace = tmp_ns;
2150         }
2151
2152         return namespace;
2153 }
2154
2155 static IMAPNameSpace *imap_find_namespace(IMAPFolder *folder,
2156                                           const gchar *path)
2157 {
2158         IMAPNameSpace *namespace;
2159
2160         g_return_val_if_fail(folder != NULL, NULL);
2161
2162         namespace = imap_find_namespace_from_list(folder->ns_personal, path);
2163         if (namespace) return namespace;
2164         namespace = imap_find_namespace_from_list(folder->ns_others, path);
2165         if (namespace) return namespace;
2166         namespace = imap_find_namespace_from_list(folder->ns_shared, path);
2167         if (namespace) return namespace;
2168
2169         return NULL;
2170 }
2171
2172
2173 static gchar imap_get_path_separator(IMAPFolder *folder, const gchar *path)
2174 {
2175         IMAPNameSpace *namespace;
2176         gchar separator = '/';
2177
2178         if (folder->last_seen_separator == 0) {
2179                 clist * lep_list;
2180                 int r = imap_threaded_list((Folder *)folder, "", "", &lep_list);
2181                 if (r != MAILIMAP_NO_ERROR) {
2182                         log_warning(_("LIST failed\n"));
2183                         return '/';
2184                 }
2185                 
2186                 if (clist_count(lep_list) > 0) {
2187                         clistiter * iter = clist_begin(lep_list); 
2188                         struct mailimap_mailbox_list * mb;
2189                         mb = clist_content(iter);
2190                 
2191                         folder->last_seen_separator = mb->mb_delimiter;
2192                         debug_print("got separator: %c\n", folder->last_seen_separator);
2193                 }
2194                 mailimap_list_result_free(lep_list);
2195         }
2196
2197         if (folder->last_seen_separator != 0) {
2198                 debug_print("using separator: %c\n", folder->last_seen_separator);
2199                 return folder->last_seen_separator;
2200         }
2201
2202         namespace = imap_find_namespace(folder, path);
2203         if (namespace && namespace->separator)
2204                 separator = namespace->separator;
2205
2206         return separator;
2207 }
2208
2209 static gchar *imap_get_real_path(IMAPFolder *folder, const gchar *path)
2210 {
2211         gchar *real_path;
2212         gchar separator;
2213
2214         g_return_val_if_fail(folder != NULL, NULL);
2215         g_return_val_if_fail(path != NULL, NULL);
2216
2217         real_path = imap_utf8_to_modified_utf7(path);
2218         separator = imap_get_path_separator(folder, path);
2219         imap_path_separator_subst(real_path, separator);
2220
2221         return real_path;
2222 }
2223
2224 static gint imap_set_message_flags(IMAPSession *session,
2225                                    MsgNumberList *numlist,
2226                                    IMAPFlags flags,
2227                                    gboolean is_set)
2228 {
2229         gint ok = 0;
2230         GSList *seq_list;
2231         GSList * cur;
2232
2233         seq_list = imap_get_lep_set_from_numlist(numlist);
2234         
2235         for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) {
2236                 struct mailimap_set * imapset;
2237                 
2238                 imapset = cur->data;
2239                 
2240                 ok = imap_cmd_store(session, imapset,
2241                                     flags, is_set);
2242         }
2243         
2244         imap_lep_set_free(seq_list);
2245         
2246         return IMAP_SUCCESS;
2247 }
2248
2249 typedef struct _select_data {
2250         IMAPSession *session;
2251         gchar *real_path;
2252         gint *exists;
2253         gint *recent;
2254         gint *unseen;
2255         guint32 *uid_validity;
2256         gboolean done;
2257 } select_data;
2258
2259 static gint imap_select(IMAPSession *session, IMAPFolder *folder,
2260                         const gchar *path,
2261                         gint *exists, gint *recent, gint *unseen,
2262                         guint32 *uid_validity, gboolean block)
2263 {
2264         gchar *real_path;
2265         gint ok;
2266         gint exists_, recent_, unseen_;
2267         guint32 uid_validity_;
2268         
2269         if (!exists && !recent && !unseen && !uid_validity) {
2270                 if (session->mbox && strcmp(session->mbox, path) == 0)
2271                         return IMAP_SUCCESS;
2272         }
2273         if (!exists)
2274                 exists = &exists_;
2275         if (!recent)
2276                 recent = &recent_;
2277         if (!unseen)
2278                 unseen = &unseen_;
2279         if (!uid_validity)
2280                 uid_validity = &uid_validity_;
2281
2282         g_free(session->mbox);
2283         session->mbox = NULL;
2284
2285         real_path = imap_get_real_path(folder, path);
2286
2287         ok = imap_cmd_select(session, real_path,
2288                              exists, recent, unseen, uid_validity, block);
2289         if (ok != IMAP_SUCCESS)
2290                 log_warning(_("can't select folder: %s\n"), real_path);
2291         else {
2292                 session->mbox = g_strdup(path);
2293                 session->folder_content_changed = FALSE;
2294         }
2295         g_free(real_path);
2296
2297         return ok;
2298 }
2299
2300 static gint imap_status(IMAPSession *session, IMAPFolder *folder,
2301                         const gchar *path, IMAPFolderItem *item,
2302                         gint *messages,
2303                         guint32 *uid_next, guint32 *uid_validity,
2304                         gint *unseen, gboolean block)
2305 {
2306         int r;
2307         clistiter * iter;
2308         struct mailimap_mailbox_data_status * data_status;
2309         int got_values;
2310         gchar *real_path;
2311         guint mask = 0;
2312         
2313         real_path = imap_get_real_path(folder, path);
2314
2315 #if 0
2316         if (time(NULL) - item->last_update >= 5 && item->last_update != 1) {
2317                 /* do the full stuff */
2318                 item->last_update = 1; /* force update */
2319                 debug_print("updating everything\n");
2320                 r = imap_status(session, folder, path, item,
2321                 &item->c_messages, &item->c_uid_next,
2322                 &item->c_uid_validity, &item->c_unseen, block);
2323                 if (r != MAILIMAP_NO_ERROR) {
2324                         debug_print("status err %d\n", r);
2325                         return IMAP_ERROR;
2326                 }
2327                 item->last_update = time(NULL);
2328                 if (messages) 
2329                         *messages = item->c_messages;
2330                 if (uid_next)
2331                         *uid_next = item->c_uid_next;
2332                 if (uid_validity)
2333                         *uid_validity = item->c_uid_validity;
2334                 if (unseen)
2335                         *unseen = item->c_unseen;
2336                 return 0;
2337         } else if (time(NULL) - item->last_update < 5) {
2338                 /* return cached stuff */
2339                 debug_print("using cache\n");
2340                 if (messages) 
2341                         *messages = item->c_messages;
2342                 if (uid_next)
2343                         *uid_next = item->c_uid_next;
2344                 if (uid_validity)
2345                         *uid_validity = item->c_uid_validity;
2346                 if (unseen)
2347                         *unseen = item->c_unseen;
2348                 return 0;
2349         }
2350 #endif
2351
2352         /* if we get there, we're updating cache */
2353
2354         if (messages) {
2355                 mask |= 1 << 0;
2356         }
2357         if (uid_next) {
2358                 mask |= 1 << 2;
2359         }
2360         if (uid_validity) {
2361                 mask |= 1 << 3;
2362         }
2363         if (unseen) {
2364                 mask |= 1 << 4;
2365         }
2366         r = imap_threaded_status(FOLDER(folder), real_path, 
2367                 &data_status, mask);
2368
2369         g_free(real_path);
2370         if (r != MAILIMAP_NO_ERROR) {
2371                 debug_print("status err %d\n", r);
2372                 return IMAP_ERROR;
2373         }
2374         
2375         if (data_status->st_info_list == NULL) {
2376                 mailimap_mailbox_data_status_free(data_status);
2377                 debug_print("status->st_info_list == NULL\n");
2378                 return IMAP_ERROR;
2379         }
2380         
2381         got_values = 0;
2382         for(iter = clist_begin(data_status->st_info_list) ; iter != NULL ;
2383             iter = clist_next(iter)) {
2384                 struct mailimap_status_info * info;             
2385                 
2386                 info = clist_content(iter);
2387                 switch (info->st_att) {
2388                 case MAILIMAP_STATUS_ATT_MESSAGES:
2389                         * messages = info->st_value;
2390                         got_values |= 1 << 0;
2391                         break;
2392                         
2393                 case MAILIMAP_STATUS_ATT_UIDNEXT:
2394                         * uid_next = info->st_value;
2395                         got_values |= 1 << 2;
2396                         break;
2397                         
2398                 case MAILIMAP_STATUS_ATT_UIDVALIDITY:
2399                         * uid_validity = info->st_value;
2400                         got_values |= 1 << 3;
2401                         break;
2402                         
2403                 case MAILIMAP_STATUS_ATT_UNSEEN:
2404                         * unseen = info->st_value;
2405                         got_values |= 1 << 4;
2406                         break;
2407                 }
2408         }
2409         mailimap_mailbox_data_status_free(data_status);
2410         
2411         if (got_values != mask) {
2412                 debug_print("status: incomplete values received (%d)\n", got_values);
2413                 return IMAP_ERROR;
2414         }
2415         return IMAP_SUCCESS;
2416 }
2417
2418 static void imap_free_capabilities(IMAPSession *session)
2419 {
2420         slist_free_strings(session->capability);
2421         g_slist_free(session->capability);
2422         session->capability = NULL;
2423 }
2424
2425 /* low-level IMAP4rev1 commands */
2426
2427 #if 0
2428 static gint imap_cmd_authenticate(IMAPSession *session, const gchar *user,
2429                                   const gchar *pass, IMAPAuthType type)
2430 {
2431         gchar *auth_type;
2432         gint ok;
2433         gchar *buf = NULL;
2434         gchar *challenge;
2435         gint challenge_len;
2436         gchar hexdigest[33];
2437         gchar *response;
2438         gchar *response64;
2439
2440         auth_type = "CRAM-MD5";
2441
2442         imap_gen_send(session, "AUTHENTICATE %s", auth_type);
2443         ok = imap_gen_recv(session, &buf);
2444         if (ok != IMAP_SUCCESS || buf[0] != '+' || buf[1] != ' ') {
2445                 g_free(buf);
2446                 return IMAP_ERROR;
2447         }
2448
2449         challenge = g_malloc(strlen(buf + 2) + 1);
2450         challenge_len = base64_decode(challenge, buf + 2, -1);
2451         challenge[challenge_len] = '\0';
2452         g_free(buf);
2453
2454         md5_hex_hmac(hexdigest, challenge, challenge_len, pass, strlen(pass));
2455         g_free(challenge);
2456
2457         response = g_strdup_printf("%s %s", user, hexdigest);
2458         response64 = g_malloc((strlen(response) + 3) * 2 + 1);
2459         base64_encode(response64, response, strlen(response));
2460         g_free(response);
2461
2462         sock_puts(SESSION(session)->sock, response64);
2463         ok = imap_cmd_ok(session, NULL);
2464         if (ok != IMAP_SUCCESS)
2465                 log_warning(_("IMAP4 authentication failed.\n"));
2466
2467         return ok;
2468 }
2469 #endif
2470
2471 static gint imap_cmd_login(IMAPSession *session,
2472                            const gchar *user, const gchar *pass,
2473                            const gchar *type)
2474 {
2475         int r;
2476         gint ok;
2477
2478         log_print("IMAP4> Logging %s to %s using %s\n", 
2479                         user,
2480                         SESSION(session)->server,
2481                         type);
2482         r = imap_threaded_login(session->folder, user, pass, type);
2483         if (r != MAILIMAP_NO_ERROR) {
2484                 log_error("IMAP4< Error logging in to %s\n",
2485                                 SESSION(session)->server);
2486                 ok = IMAP_ERROR;
2487         } else {
2488                 ok = IMAP_SUCCESS;
2489         }
2490         return ok;
2491 }
2492
2493 static gint imap_cmd_logout(IMAPSession *session)
2494 {
2495         imap_threaded_disconnect(session->folder);
2496
2497         return IMAP_SUCCESS;
2498 }
2499
2500 static gint imap_cmd_noop(IMAPSession *session)
2501 {
2502         int r;
2503         unsigned int exists;
2504         
2505         r = imap_threaded_noop(session->folder, &exists);
2506         if (r != MAILIMAP_NO_ERROR) {
2507                 debug_print("noop err %d\n", r);
2508                 return IMAP_ERROR;
2509         }
2510         session->exists = exists;
2511         session_set_access_time(SESSION(session));
2512
2513         return IMAP_SUCCESS;
2514 }
2515
2516 #if USE_OPENSSL
2517 static gint imap_cmd_starttls(IMAPSession *session)
2518 {
2519         int r;
2520         
2521         r = imap_threaded_starttls(session->folder);
2522         if (r != MAILIMAP_NO_ERROR) {
2523                 debug_print("starttls err %d\n", r);
2524                 return IMAP_ERROR;
2525         }
2526         return IMAP_SUCCESS;
2527 }
2528 #endif
2529
2530 static gint imap_cmd_select(IMAPSession *session, const gchar *folder,
2531                             gint *exists, gint *recent, gint *unseen,
2532                             guint32 *uid_validity, gboolean block)
2533 {
2534         int r;
2535
2536         r = imap_threaded_select(session->folder, folder,
2537                                  exists, recent, unseen, uid_validity);
2538         if (r != MAILIMAP_NO_ERROR) {
2539                 debug_print("select err %d\n", r);
2540                 return IMAP_ERROR;
2541         }
2542         return IMAP_SUCCESS;
2543 }
2544
2545 static gint imap_cmd_examine(IMAPSession *session, const gchar *folder,
2546                              gint *exists, gint *recent, gint *unseen,
2547                              guint32 *uid_validity, gboolean block)
2548 {
2549         int r;
2550
2551         r = imap_threaded_examine(session->folder, folder,
2552                                   exists, recent, unseen, uid_validity);
2553         if (r != MAILIMAP_NO_ERROR) {
2554                 debug_print("examine err %d\n", r);
2555                 
2556                 return IMAP_ERROR;
2557         }
2558         return IMAP_SUCCESS;
2559 }
2560
2561 static gint imap_cmd_create(IMAPSession *session, const gchar *folder)
2562 {
2563         int r;
2564
2565         r = imap_threaded_create(session->folder, folder);
2566         if (r != MAILIMAP_NO_ERROR) {
2567                 
2568                 return IMAP_ERROR;
2569         }
2570
2571         return IMAP_SUCCESS;
2572 }
2573
2574 static gint imap_cmd_rename(IMAPSession *session, const gchar *old_folder,
2575                             const gchar *new_folder)
2576 {
2577         int r;
2578
2579         r = imap_threaded_rename(session->folder, old_folder,
2580                                  new_folder);
2581         if (r != MAILIMAP_NO_ERROR) {
2582                 
2583                 return IMAP_ERROR;
2584         }
2585
2586         return IMAP_SUCCESS;
2587 }
2588
2589 static gint imap_cmd_delete(IMAPSession *session, const gchar *folder)
2590 {
2591         int r;
2592         
2593
2594         r = imap_threaded_delete(session->folder, folder);
2595         if (r != MAILIMAP_NO_ERROR) {
2596                 
2597                 return IMAP_ERROR;
2598         }
2599
2600         return IMAP_SUCCESS;
2601 }
2602
2603 typedef struct _fetch_data {
2604         IMAPSession *session;
2605         guint32 uid;
2606         const gchar *filename;
2607         gboolean headers;
2608         gboolean body;
2609         gboolean done;
2610 } fetch_data;
2611
2612 static void *imap_cmd_fetch_thread(void *data)
2613 {
2614         fetch_data *stuff = (fetch_data *)data;
2615         IMAPSession *session = stuff->session;
2616         guint32 uid = stuff->uid;
2617         const gchar *filename = stuff->filename;
2618         int r;
2619         
2620         if (stuff->body) {
2621                 r = imap_threaded_fetch_content(session->folder,
2622                                                uid, 1, filename);
2623         }
2624         else {
2625                 r = imap_threaded_fetch_content(session->folder,
2626                                                 uid, 0, filename);
2627         }
2628         if (r != MAILIMAP_NO_ERROR) {
2629                 debug_print("fetch err %d\n", r);
2630                 return GINT_TO_POINTER(IMAP_ERROR);
2631         }
2632         return GINT_TO_POINTER(IMAP_SUCCESS);
2633 }
2634
2635 static gint imap_cmd_fetch(IMAPSession *session, guint32 uid,
2636                                 const gchar *filename, gboolean headers,
2637                                 gboolean body)
2638 {
2639         fetch_data *data = g_new0(fetch_data, 1);
2640         int result = 0;
2641         data->done = FALSE;
2642         data->session = session;
2643         data->uid = uid;
2644         data->filename = filename;
2645         data->headers = headers;
2646         data->body = body;
2647
2648         if (prefs_common.work_offline && !inc_offline_should_override()) {
2649                 g_free(data);
2650                 return -1;
2651         }
2652         statusbar_print_all(_("Fetching message..."));
2653         result = GPOINTER_TO_INT(imap_cmd_fetch_thread(data));
2654         statusbar_pop_all();
2655         g_free(data);
2656         return result;
2657 }
2658
2659
2660 static gint imap_cmd_append(IMAPSession *session, const gchar *destfolder,
2661                             const gchar *file, IMAPFlags flags, 
2662                             guint32 *new_uid)
2663 {
2664         struct mailimap_flag_list * flag_list;
2665         int r;
2666         
2667         g_return_val_if_fail(file != NULL, IMAP_ERROR);
2668
2669         flag_list = imap_flag_to_lep(flags);
2670         r = imap_threaded_append(session->folder, destfolder,
2671                          file, flag_list);
2672         if (new_uid != NULL)
2673                 *new_uid = 0;
2674
2675         if (r != MAILIMAP_NO_ERROR) {
2676                 debug_print("append err %d\n", r);
2677                 return IMAP_ERROR;
2678         }
2679         return IMAP_SUCCESS;
2680 }
2681
2682 static gint imap_cmd_copy(IMAPSession *session, struct mailimap_set * set,
2683                           const gchar *destfolder, GRelation *uid_mapping)
2684 {
2685         int r;
2686         
2687         g_return_val_if_fail(session != NULL, IMAP_ERROR);
2688         g_return_val_if_fail(set != NULL, IMAP_ERROR);
2689         g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
2690
2691         r = imap_threaded_copy(session->folder, set, destfolder);
2692         if (r != MAILIMAP_NO_ERROR) {
2693                 
2694                 return IMAP_ERROR;
2695         }
2696
2697         return IMAP_SUCCESS;
2698 }
2699
2700 static gint imap_cmd_store(IMAPSession *session, struct mailimap_set * set,
2701                            IMAPFlags flags, int do_add)
2702 {
2703         int r;
2704         struct mailimap_flag_list * flag_list;
2705         struct mailimap_store_att_flags * store_att_flags;
2706         
2707         flag_list = imap_flag_to_lep(flags);
2708         
2709         if (do_add)
2710                 store_att_flags =
2711                         mailimap_store_att_flags_new_add_flags_silent(flag_list);
2712         else
2713                 store_att_flags =
2714                         mailimap_store_att_flags_new_remove_flags_silent(flag_list);
2715         
2716         r = imap_threaded_store(session->folder, set, store_att_flags);
2717         if (r != MAILIMAP_NO_ERROR) {
2718                 
2719                 return IMAP_ERROR;
2720         }
2721         
2722         return IMAP_SUCCESS;
2723 }
2724
2725 static gint imap_cmd_expunge(IMAPSession *session)
2726 {
2727         int r;
2728         
2729         if (prefs_common.work_offline && !inc_offline_should_override()) {
2730                 return -1;
2731         }
2732
2733         r = imap_threaded_expunge(session->folder);
2734         if (r != MAILIMAP_NO_ERROR) {
2735                 
2736                 return IMAP_ERROR;
2737         }
2738
2739         return IMAP_SUCCESS;
2740 }
2741
2742 static void imap_path_separator_subst(gchar *str, gchar separator)
2743 {
2744         gchar *p;
2745         gboolean in_escape = FALSE;
2746
2747         if (!separator || separator == '/') return;
2748
2749         for (p = str; *p != '\0'; p++) {
2750                 if (*p == '/' && !in_escape)
2751                         *p = separator;
2752                 else if (*p == '&' && *(p + 1) != '-' && !in_escape)
2753                         in_escape = TRUE;
2754                 else if (*p == '-' && in_escape)
2755                         in_escape = FALSE;
2756         }
2757 }
2758
2759 static gchar *imap_modified_utf7_to_utf8(const gchar *mutf7_str)
2760 {
2761         static iconv_t cd = (iconv_t)-1;
2762         static gboolean iconv_ok = TRUE;
2763         GString *norm_utf7;
2764         gchar *norm_utf7_p;
2765         size_t norm_utf7_len;
2766         const gchar *p;
2767         gchar *to_str, *to_p;
2768         size_t to_len;
2769         gboolean in_escape = FALSE;
2770
2771         if (!iconv_ok) return g_strdup(mutf7_str);
2772
2773         if (cd == (iconv_t)-1) {
2774                 cd = iconv_open(CS_INTERNAL, CS_UTF_7);
2775                 if (cd == (iconv_t)-1) {
2776                         g_warning("iconv cannot convert UTF-7 to %s\n",
2777                                   CS_INTERNAL);
2778                         iconv_ok = FALSE;
2779                         return g_strdup(mutf7_str);
2780                 }
2781         }
2782
2783         /* modified UTF-7 to normal UTF-7 conversion */
2784         norm_utf7 = g_string_new(NULL);
2785
2786         for (p = mutf7_str; *p != '\0'; p++) {
2787                 /* replace: '&'  -> '+',
2788                             "&-" -> '&',
2789                             escaped ','  -> '/' */
2790                 if (!in_escape && *p == '&') {
2791                         if (*(p + 1) != '-') {
2792                                 g_string_append_c(norm_utf7, '+');
2793                                 in_escape = TRUE;
2794                         } else {
2795                                 g_string_append_c(norm_utf7, '&');
2796                                 p++;
2797                         }
2798                 } else if (in_escape && *p == ',') {
2799                         g_string_append_c(norm_utf7, '/');
2800                 } else if (in_escape && *p == '-') {
2801                         g_string_append_c(norm_utf7, '-');
2802                         in_escape = FALSE;
2803                 } else {
2804                         g_string_append_c(norm_utf7, *p);
2805                 }
2806         }
2807
2808         norm_utf7_p = norm_utf7->str;
2809         norm_utf7_len = norm_utf7->len;
2810         to_len = strlen(mutf7_str) * 5;
2811         to_p = to_str = g_malloc(to_len + 1);
2812
2813         if (iconv(cd, (ICONV_CONST gchar **)&norm_utf7_p, &norm_utf7_len,
2814                   &to_p, &to_len) == -1) {
2815                 g_warning(_("iconv cannot convert UTF-7 to %s\n"),
2816                           conv_get_locale_charset_str());
2817                 g_string_free(norm_utf7, TRUE);
2818                 g_free(to_str);
2819                 return g_strdup(mutf7_str);
2820         }
2821
2822         /* second iconv() call for flushing */
2823         iconv(cd, NULL, NULL, &to_p, &to_len);
2824         g_string_free(norm_utf7, TRUE);
2825         *to_p = '\0';
2826
2827         return to_str;
2828 }
2829
2830 static gchar *imap_utf8_to_modified_utf7(const gchar *from)
2831 {
2832         static iconv_t cd = (iconv_t)-1;
2833         static gboolean iconv_ok = TRUE;
2834         gchar *norm_utf7, *norm_utf7_p;
2835         size_t from_len, norm_utf7_len;
2836         GString *to_str;
2837         gchar *from_tmp, *to, *p;
2838         gboolean in_escape = FALSE;
2839
2840         if (!iconv_ok) return g_strdup(from);
2841
2842         if (cd == (iconv_t)-1) {
2843                 cd = iconv_open(CS_UTF_7, CS_INTERNAL);
2844                 if (cd == (iconv_t)-1) {
2845                         g_warning(_("iconv cannot convert %s to UTF-7\n"),
2846                                   CS_INTERNAL);
2847                         iconv_ok = FALSE;
2848                         return g_strdup(from);
2849                 }
2850         }
2851
2852         /* UTF-8 to normal UTF-7 conversion */
2853         Xstrdup_a(from_tmp, from, return g_strdup(from));
2854         from_len = strlen(from);
2855         norm_utf7_len = from_len * 5;
2856         Xalloca(norm_utf7, norm_utf7_len + 1, return g_strdup(from));
2857         norm_utf7_p = norm_utf7;
2858
2859 #define IS_PRINT(ch) (isprint(ch) && IS_ASCII(ch))
2860
2861         while (from_len > 0) {
2862                 if (*from_tmp == '+') {
2863                         *norm_utf7_p++ = '+';
2864                         *norm_utf7_p++ = '-';
2865                         norm_utf7_len -= 2;
2866                         from_tmp++;
2867                         from_len--;
2868                 } else if (IS_PRINT(*(guchar *)from_tmp)) {
2869                         /* printable ascii char */
2870                         *norm_utf7_p = *from_tmp;
2871                         norm_utf7_p++;
2872                         norm_utf7_len--;
2873                         from_tmp++;
2874                         from_len--;
2875                 } else {
2876                         size_t conv_len = 0;
2877
2878                         /* unprintable char: convert to UTF-7 */
2879                         p = from_tmp;
2880                         while (!IS_PRINT(*(guchar *)p) && conv_len < from_len) {
2881                                 conv_len += g_utf8_skip[*(guchar *)p];
2882                                 p += g_utf8_skip[*(guchar *)p];
2883                         }
2884
2885                         from_len -= conv_len;
2886                         if (iconv(cd, (ICONV_CONST gchar **)&from_tmp,
2887                                   &conv_len,
2888                                   &norm_utf7_p, &norm_utf7_len) == -1) {
2889                                 g_warning(_("iconv cannot convert UTF-8 to UTF-7\n"));
2890                                 return g_strdup(from);
2891                         }
2892
2893                         /* second iconv() call for flushing */
2894                         iconv(cd, NULL, NULL, &norm_utf7_p, &norm_utf7_len);
2895                 }
2896         }
2897
2898 #undef IS_PRINT
2899
2900         *norm_utf7_p = '\0';
2901         to_str = g_string_new(NULL);
2902         for (p = norm_utf7; p < norm_utf7_p; p++) {
2903                 /* replace: '&' -> "&-",
2904                             '+' -> '&',
2905                             "+-" -> '+',
2906                             BASE64 '/' -> ',' */
2907                 if (!in_escape && *p == '&') {
2908                         g_string_append(to_str, "&-");
2909                 } else if (!in_escape && *p == '+') {
2910                         if (*(p + 1) == '-') {
2911                                 g_string_append_c(to_str, '+');
2912                                 p++;
2913                         } else {
2914                                 g_string_append_c(to_str, '&');
2915                                 in_escape = TRUE;
2916                         }
2917                 } else if (in_escape && *p == '/') {
2918                         g_string_append_c(to_str, ',');
2919                 } else if (in_escape && *p == '-') {
2920                         g_string_append_c(to_str, '-');
2921                         in_escape = FALSE;
2922                 } else {
2923                         g_string_append_c(to_str, *p);
2924                 }
2925         }
2926
2927         if (in_escape) {
2928                 in_escape = FALSE;
2929                 g_string_append_c(to_str, '-');
2930         }
2931
2932         to = to_str->str;
2933         g_string_free(to_str, FALSE);
2934
2935         return to;
2936 }
2937
2938 static gboolean imap_rename_folder_func(GNode *node, gpointer data)
2939 {
2940         FolderItem *item = node->data;
2941         gchar **paths = data;
2942         const gchar *oldpath = paths[0];
2943         const gchar *newpath = paths[1];
2944         gchar *base;
2945         gchar *new_itempath;
2946         gint oldpathlen;
2947
2948         oldpathlen = strlen(oldpath);
2949         if (strncmp(oldpath, item->path, oldpathlen) != 0) {
2950                 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
2951                 return TRUE;
2952         }
2953
2954         base = item->path + oldpathlen;
2955         while (*base == G_DIR_SEPARATOR) base++;
2956         if (*base == '\0')
2957                 new_itempath = g_strdup(newpath);
2958         else
2959                 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
2960                                            NULL);
2961         g_free(item->path);
2962         item->path = new_itempath;
2963
2964         return FALSE;
2965 }
2966
2967 typedef struct _get_list_uid_data {
2968         Folder *folder;
2969         IMAPSession *session;
2970         IMAPFolderItem *item;
2971         GSList **msgnum_list;
2972         gboolean done;
2973 } get_list_uid_data;
2974
2975 static void *get_list_of_uids_thread(void *data)
2976 {
2977         get_list_uid_data *stuff = (get_list_uid_data *)data;
2978         Folder *folder = stuff->folder;
2979         IMAPFolderItem *item = stuff->item;
2980         GSList **msgnum_list = stuff->msgnum_list;
2981         gint ok, nummsgs = 0, lastuid_old;
2982         IMAPSession *session;
2983         GSList *uidlist, *elem;
2984         struct mailimap_set * set;
2985         clist * lep_uidlist;
2986         int r;
2987
2988         session = stuff->session;
2989         if (session == NULL) {
2990                 stuff->done = TRUE;
2991                 return GINT_TO_POINTER(-1);
2992         }
2993         /* no session locking here, it's already locked by caller */
2994         ok = imap_select(session, IMAP_FOLDER(folder), item->item.path,
2995                          NULL, NULL, NULL, NULL, TRUE);
2996         if (ok != IMAP_SUCCESS) {
2997                 stuff->done = TRUE;
2998                 return GINT_TO_POINTER(-1);
2999         }
3000
3001         uidlist = NULL;
3002         
3003         set = mailimap_set_new_interval(item->lastuid + 1, 0);
3004
3005         r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SIMPLE, set,
3006                                  &lep_uidlist);
3007         if (r == MAILIMAP_NO_ERROR) {
3008                 GSList * fetchuid_list;
3009                 
3010                 fetchuid_list =
3011                         imap_uid_list_from_lep(lep_uidlist);
3012                 uidlist = g_slist_concat(fetchuid_list, uidlist);
3013         }
3014         else {
3015                 GSList * fetchuid_list;
3016                 carray * lep_uidtab;
3017                 
3018                 r = imap_threaded_fetch_uid(folder, item->lastuid + 1,
3019                                             &lep_uidtab);
3020                 if (r == MAILIMAP_NO_ERROR) {
3021                         fetchuid_list =
3022                                 imap_uid_list_from_lep_tab(lep_uidtab);
3023                         uidlist = g_slist_concat(fetchuid_list, uidlist);
3024                 }
3025         }
3026         
3027         lastuid_old = item->lastuid;
3028         *msgnum_list = g_slist_copy(item->uid_list);
3029         nummsgs = g_slist_length(*msgnum_list);
3030         debug_print("Got %d uids from cache\n", g_slist_length(item->uid_list));
3031
3032         for (elem = uidlist; elem != NULL; elem = g_slist_next(elem)) {
3033                 guint msgnum;
3034
3035                 msgnum = GPOINTER_TO_INT(elem->data);
3036                 if (msgnum > lastuid_old) {
3037                         *msgnum_list = g_slist_prepend(*msgnum_list, GINT_TO_POINTER(msgnum));
3038                         item->uid_list = g_slist_prepend(item->uid_list, GINT_TO_POINTER(msgnum));
3039                         nummsgs++;
3040
3041                         if(msgnum > item->lastuid)
3042                                 item->lastuid = msgnum;
3043                 }
3044         }
3045         g_slist_free(uidlist);
3046         stuff->done = TRUE;
3047         return GINT_TO_POINTER(nummsgs);
3048 }
3049
3050 static gint get_list_of_uids(IMAPSession *session, Folder *folder, IMAPFolderItem *item, GSList **msgnum_list)
3051 {
3052         gint result;
3053         get_list_uid_data *data = g_new0(get_list_uid_data, 1);
3054         data->done = FALSE;
3055         data->folder = folder;
3056         data->item = item;
3057         data->msgnum_list = msgnum_list;
3058         data->session = session;
3059         if (prefs_common.work_offline && !inc_offline_should_override()) {
3060                 g_free(data);
3061                 return -1;
3062         }
3063
3064         result = GPOINTER_TO_INT(get_list_of_uids_thread(data));
3065         g_free(data);
3066         return result;
3067
3068 }
3069
3070 gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list, gboolean *old_uids_valid)
3071 {
3072         IMAPFolderItem *item = (IMAPFolderItem *)_item;
3073         IMAPSession *session;
3074         gint ok, nummsgs = 0, exists, uid_val, uid_next;
3075         GSList *uidlist = NULL;
3076         gchar *dir;
3077         gboolean selected_folder;
3078         
3079         debug_print("get_num_list\n");
3080         
3081         g_return_val_if_fail(folder != NULL, -1);
3082         g_return_val_if_fail(item != NULL, -1);
3083         g_return_val_if_fail(item->item.path != NULL, -1);
3084         g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3085         g_return_val_if_fail(folder->account != NULL, -1);
3086
3087         session = imap_session_get(folder);
3088         g_return_val_if_fail(session != NULL, -1);
3089         lock_session();
3090         statusbar_print_all("Scanning %s...\n", FOLDER_ITEM(item)->path 
3091                                 ? FOLDER_ITEM(item)->path:"");
3092
3093         selected_folder = (session->mbox != NULL) &&
3094                           (!strcmp(session->mbox, item->item.path));
3095         if (selected_folder) {
3096                 ok = imap_cmd_noop(session);
3097                 if (ok != IMAP_SUCCESS) {
3098                         debug_print("disconnected!\n");
3099                         session = imap_reconnect_if_possible(folder, session);
3100                         if (session == NULL) {
3101                                 statusbar_pop_all();
3102                                 unlock_session();
3103                                 return -1;
3104                         }
3105                 }
3106                 exists = session->exists;
3107
3108                 *old_uids_valid = TRUE;
3109         } else {
3110                 if (item->use_cache && time(NULL) - item->use_cache < 2) {
3111                         exists = item->c_messages;
3112                         uid_next = item->c_uid_next;
3113                         uid_val = item->c_uid_validity;
3114                         ok = IMAP_SUCCESS;
3115                         debug_print("using cache %d %d %d\n", exists, uid_next, uid_val);
3116                 } else {
3117                         ok = imap_status(session, IMAP_FOLDER(folder), item->item.path, item,
3118                                  &exists, &uid_next, &uid_val, NULL, FALSE);
3119                 }
3120                 item->use_cache = (time_t)0;
3121                 if (ok != IMAP_SUCCESS) {
3122                         statusbar_pop_all();
3123                         unlock_session();
3124                         return -1;
3125                 }
3126                 if(item->item.mtime == uid_val)
3127                         *old_uids_valid = TRUE;
3128                 else {
3129                         *old_uids_valid = FALSE;
3130
3131                         debug_print("Freeing imap uid cache\n");
3132                         item->lastuid = 0;
3133                         g_slist_free(item->uid_list);
3134                         item->uid_list = NULL;
3135                 
3136                         item->item.mtime = uid_val;
3137
3138                         imap_delete_all_cached_messages((FolderItem *)item);
3139                 }
3140         }
3141
3142         if (!selected_folder)
3143                 item->uid_next = uid_next;
3144
3145         /* If old uid_next matches new uid_next we can be sure no message
3146            was added to the folder */
3147         if (( selected_folder && !session->folder_content_changed) ||
3148             (!selected_folder && uid_next == item->uid_next)) {
3149                 nummsgs = g_slist_length(item->uid_list);
3150
3151                 /* If number of messages is still the same we
3152                    know our caches message numbers are still valid,
3153                    otherwise if the number of messages has decrease
3154                    we discard our cache to start a new scan to find
3155                    out which numbers have been removed */
3156                 if (exists == nummsgs) {
3157                         *msgnum_list = g_slist_copy(item->uid_list);
3158                         statusbar_pop_all();
3159                         unlock_session();
3160                         return nummsgs;
3161                 } else if (exists < nummsgs) {
3162                         debug_print("Freeing imap uid cache");
3163                         item->lastuid = 0;
3164                         g_slist_free(item->uid_list);
3165                         item->uid_list = NULL;
3166                 }
3167         }
3168
3169         if (exists == 0) {
3170                 *msgnum_list = NULL;
3171                 statusbar_pop_all();
3172                 unlock_session();
3173                 return 0;
3174         }
3175
3176         nummsgs = get_list_of_uids(session, folder, item, &uidlist);
3177
3178         if (nummsgs < 0) {
3179                 statusbar_pop_all();
3180                 unlock_session();
3181                 return -1;
3182         }
3183
3184         if (nummsgs != exists) {
3185                 /* Cache contains more messages then folder, we have cached
3186                    an old UID of a message that was removed and new messages
3187                    have been added too, otherwise the uid_next check would
3188                    not have failed */
3189                 debug_print("Freeing imap uid cache");
3190                 item->lastuid = 0;
3191                 g_slist_free(item->uid_list);
3192                 item->uid_list = NULL;
3193
3194                 g_slist_free(*msgnum_list);
3195
3196                 nummsgs = get_list_of_uids(session, folder, item, &uidlist);
3197         }
3198
3199         *msgnum_list = uidlist;
3200
3201         dir = folder_item_get_path((FolderItem *)item);
3202         debug_print("removing old messages from %s\n", dir);
3203         remove_numbered_files_not_in_list(dir, *msgnum_list);
3204         g_free(dir);
3205         
3206         debug_print("get_num_list - ok - %i\n", nummsgs);
3207         statusbar_pop_all();
3208         unlock_session();
3209         return nummsgs;
3210 }
3211
3212 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item)
3213 {
3214         MsgInfo *msginfo;
3215         MsgFlags flags;
3216
3217         flags.perm_flags = MSG_NEW|MSG_UNREAD;
3218         flags.tmp_flags = 0;
3219
3220         g_return_val_if_fail(item != NULL, NULL);
3221         g_return_val_if_fail(file != NULL, NULL);
3222
3223         if (folder_has_parent_of_type(item, F_QUEUE)) {
3224                 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
3225         } else if (folder_has_parent_of_type(item, F_DRAFT)) {
3226                 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
3227         }
3228
3229         msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3230         if (!msginfo) return NULL;
3231         
3232         msginfo->plaintext_file = g_strdup(file);
3233         msginfo->folder = item;
3234
3235         return msginfo;
3236 }
3237
3238 GSList *imap_get_msginfos(Folder *folder, FolderItem *item,
3239                           GSList *msgnum_list)
3240 {
3241         IMAPSession *session;
3242         MsgInfoList *ret = NULL;
3243         gint ok;
3244         
3245         debug_print("get_msginfos\n");
3246         
3247         g_return_val_if_fail(folder != NULL, NULL);
3248         g_return_val_if_fail(item != NULL, NULL);
3249         g_return_val_if_fail(msgnum_list != NULL, NULL);
3250
3251         session = imap_session_get(folder);
3252         g_return_val_if_fail(session != NULL, NULL);
3253         lock_session();
3254         debug_print("IMAP getting msginfos\n");
3255         ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3256                          NULL, NULL, NULL, NULL, FALSE);
3257         if (ok != IMAP_SUCCESS) {
3258                 unlock_session();
3259                 return NULL;
3260         }
3261         if (!(folder_has_parent_of_type(item, F_DRAFT) || 
3262               folder_has_parent_of_type(item, F_QUEUE))) {
3263                 ret = g_slist_concat(ret,
3264                         imap_get_uncached_messages(session, item,
3265                                                    msgnum_list));
3266         } else {
3267                 MsgNumberList *sorted_list, *elem;
3268                 gint startnum, lastnum;
3269
3270                 sorted_list = g_slist_sort(g_slist_copy(msgnum_list), g_int_compare);
3271
3272                 startnum = lastnum = GPOINTER_TO_INT(sorted_list->data);
3273
3274                 for (elem = sorted_list;; elem = g_slist_next(elem)) {
3275                         guint num = 0;
3276
3277                         if (elem)
3278                                 num = GPOINTER_TO_INT(elem->data);
3279
3280                         if (num > lastnum + 1 || elem == NULL) {
3281                                 int i;
3282                                 for (i = startnum; i <= lastnum; ++i) {
3283                                         gchar *file;
3284                         
3285                                         file = imap_fetch_msg(folder, item, i);
3286                                         if (file != NULL) {
3287                                                 MsgInfo *msginfo = imap_parse_msg(file, item);
3288                                                 if (msginfo != NULL) {
3289                                                         msginfo->msgnum = i;
3290                                                         ret = g_slist_append(ret, msginfo);
3291                                                 }
3292                                                 g_free(file);
3293                                         }
3294                                 }
3295
3296                                 if (elem == NULL)
3297                                         break;
3298
3299                                 startnum = num;
3300                         }
3301                         lastnum = num;
3302                 }
3303
3304                 g_slist_free(sorted_list);
3305         }
3306         unlock_session();
3307         return ret;
3308 }
3309
3310 MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid)
3311 {
3312         MsgInfo *msginfo = NULL;
3313         MsgInfoList *msginfolist;
3314         MsgNumberList numlist;
3315
3316         numlist.next = NULL;
3317         numlist.data = GINT_TO_POINTER(uid);
3318
3319         msginfolist = imap_get_msginfos(folder, item, &numlist);
3320         if (msginfolist != NULL) {
3321                 msginfo = msginfolist->data;
3322                 g_slist_free(msginfolist);
3323         }
3324
3325         return msginfo;
3326 }
3327
3328 gboolean imap_scan_required(Folder *folder, FolderItem *_item)
3329 {
3330         IMAPSession *session;
3331         IMAPFolderItem *item = (IMAPFolderItem *)_item;
3332         gint ok, exists = 0, unseen = 0;
3333         guint32 uid_next, uid_val;
3334         gboolean selected_folder;
3335         
3336         g_return_val_if_fail(folder != NULL, FALSE);
3337         g_return_val_if_fail(item != NULL, FALSE);
3338         g_return_val_if_fail(item->item.folder != NULL, FALSE);
3339         g_return_val_if_fail(FOLDER_CLASS(item->item.folder) == &imap_class, FALSE);
3340
3341         if (item->item.path == NULL)
3342                 return FALSE;
3343
3344         session = imap_session_get(folder);
3345         g_return_val_if_fail(session != NULL, FALSE);
3346         lock_session();
3347         selected_folder = (session->mbox != NULL) &&
3348                           (!strcmp(session->mbox, item->item.path));
3349         if (selected_folder) {
3350                 ok = imap_cmd_noop(session);
3351                 if (ok != IMAP_SUCCESS) {
3352                         debug_print("disconnected!\n");
3353                         session = imap_reconnect_if_possible(folder, session);
3354                         if (session == NULL)
3355                                 return FALSE;
3356                         lock_session();
3357                 }
3358
3359                 if (session->folder_content_changed
3360                 ||  session->exists != item->item.total_msgs) {
3361                         unlock_session();
3362                         return TRUE;
3363                 }
3364         } else {
3365                 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path, IMAP_FOLDER_ITEM(item),
3366                                  &exists, &uid_next, &uid_val, &unseen, FALSE);
3367                 if (ok != IMAP_SUCCESS) {
3368                         unlock_session();
3369                         return FALSE;
3370                 }
3371
3372                 item->use_cache = time(NULL);
3373                 item->c_messages = exists;
3374                 item->c_uid_next = uid_next;
3375                 item->c_uid_validity = uid_val;
3376                 item->c_unseen = unseen;
3377
3378                 if ((uid_next != item->uid_next) || (exists != item->item.total_msgs)) {
3379                         unlock_session();
3380                         return TRUE;
3381                 }
3382         }
3383         unlock_session();
3384         return FALSE;
3385 }
3386
3387 void imap_change_flags(Folder *folder, FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
3388 {
3389         IMAPSession *session;
3390         IMAPFlags flags_set = 0, flags_unset = 0;
3391         gint ok = IMAP_SUCCESS;
3392         MsgNumberList numlist;
3393         hashtable_data *ht_data = NULL;
3394
3395         g_return_if_fail(folder != NULL);
3396         g_return_if_fail(folder->klass == &imap_class);
3397         g_return_if_fail(item != NULL);
3398         g_return_if_fail(item->folder == folder);
3399         g_return_if_fail(msginfo != NULL);
3400         g_return_if_fail(msginfo->folder == item);
3401
3402         session = imap_session_get(folder);
3403         if (!session) {
3404                 return;
3405         }
3406         lock_session();
3407         if ((ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
3408             NULL, NULL, NULL, NULL, FALSE)) != IMAP_SUCCESS) {
3409                 unlock_session();
3410                 return;
3411         }
3412
3413         if (!MSG_IS_MARKED(msginfo->flags) &&  (newflags & MSG_MARKED))
3414                 flags_set |= IMAP_FLAG_FLAGGED;
3415         if ( MSG_IS_MARKED(msginfo->flags) && !(newflags & MSG_MARKED))
3416                 flags_unset |= IMAP_FLAG_FLAGGED;
3417
3418         if (!MSG_IS_UNREAD(msginfo->flags) &&  (newflags & MSG_UNREAD))
3419                 flags_unset |= IMAP_FLAG_SEEN;
3420         if ( MSG_IS_UNREAD(msginfo->flags) && !(newflags & MSG_UNREAD))
3421                 flags_set |= IMAP_FLAG_SEEN;
3422
3423         if (!MSG_IS_REPLIED(msginfo->flags) &&  (newflags & MSG_REPLIED))
3424                 flags_set |= IMAP_FLAG_ANSWERED;
3425         if ( MSG_IS_REPLIED(msginfo->flags) && !(newflags & MSG_REPLIED))
3426                 flags_unset |= IMAP_FLAG_ANSWERED;
3427
3428         if (!MSG_IS_DELETED(msginfo->flags) &&  (newflags & MSG_DELETED))
3429                 flags_set |= IMAP_FLAG_DELETED;
3430         if ( MSG_IS_DELETED(msginfo->flags) && !(newflags & MSG_DELETED))
3431                 flags_unset |= IMAP_FLAG_DELETED;
3432
3433         numlist.next = NULL;
3434         numlist.data = GINT_TO_POINTER(msginfo->msgnum);
3435
3436         if (IMAP_FOLDER_ITEM(item)->batching) {
3437                 /* instead of performing an UID STORE command for each message change,
3438                  * as a lot of them can change "together", we just fill in hashtables
3439                  * and defer the treatment so that we're able to send only one
3440                  * command.
3441                  */
3442                 debug_print("IMAP batch mode on, deferring flags change\n");
3443                 if (flags_set) {
3444                         ht_data = g_hash_table_lookup(IMAP_FOLDER_ITEM(item)->flags_set_table, 
3445                                 GINT_TO_POINTER(flags_set));
3446                         if (ht_data == NULL) {
3447                                 ht_data = g_new0(hashtable_data, 1);
3448                                 ht_data->session = session;
3449                                 ht_data->item = IMAP_FOLDER_ITEM(item);
3450                                 g_hash_table_insert(IMAP_FOLDER_ITEM(item)->flags_set_table, 
3451                                         GINT_TO_POINTER(flags_set), ht_data);
3452                         }