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