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