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