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