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