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