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