ccd7761076937639263cf6e2a6a92066a1b1f8d5
[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
2275         if (folder->last_seen_separator == 0) {
2276                 clist * lep_list;
2277                 int r = imap_threaded_list((Folder *)folder, "", "", &lep_list);
2278                 if (r != MAILIMAP_NO_ERROR) {
2279                         log_warning(_("LIST failed\n"));
2280                         return '/';
2281                 }
2282                 
2283                 if (clist_count(lep_list) > 0) {
2284                         clistiter * iter = clist_begin(lep_list); 
2285                         struct mailimap_mailbox_list * mb;
2286                         mb = clist_content(iter);
2287                 
2288                         folder->last_seen_separator = mb->mb_delimiter;
2289                         debug_print("got separator: %c\n", folder->last_seen_separator);
2290                 }
2291                 mailimap_list_result_free(lep_list);
2292         }
2293
2294         if (folder->last_seen_separator != 0) {
2295                 debug_print("using separator: %c\n", folder->last_seen_separator);
2296                 return folder->last_seen_separator;
2297         }
2298
2299         namespace = imap_find_namespace(folder, path);
2300         if (namespace && namespace->separator)
2301                 separator = namespace->separator;
2302
2303         return separator;
2304 }
2305
2306 static gchar *imap_get_real_path(IMAPFolder *folder, const gchar *path)
2307 {
2308         gchar *real_path;
2309         gchar separator;
2310
2311         g_return_val_if_fail(folder != NULL, NULL);
2312         g_return_val_if_fail(path != NULL, NULL);
2313
2314         real_path = imap_utf8_to_modified_utf7(path);
2315         separator = imap_get_path_separator(folder, path);
2316         imap_path_separator_subst(real_path, separator);
2317
2318         return real_path;
2319 }
2320
2321 static gint imap_set_message_flags(IMAPSession *session,
2322                                    MsgNumberList *numlist,
2323                                    IMAPFlags flags,
2324                                    gboolean is_set)
2325 {
2326         gint ok = 0;
2327         GSList *seq_list;
2328         GSList * cur;
2329
2330         seq_list = imap_get_lep_set_from_numlist(numlist);
2331         
2332         for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) {
2333                 struct mailimap_set * imapset;
2334                 
2335                 imapset = cur->data;
2336                 
2337                 ok = imap_cmd_store(session, imapset,
2338                                     flags, is_set);
2339         }
2340         
2341         imap_lep_set_free(seq_list);
2342         
2343         return IMAP_SUCCESS;
2344 }
2345
2346 typedef struct _select_data {
2347         IMAPSession *session;
2348         gchar *real_path;
2349         gint *exists;
2350         gint *recent;
2351         gint *unseen;
2352         guint32 *uid_validity;
2353         gboolean done;
2354 } select_data;
2355
2356 static gint imap_select(IMAPSession *session, IMAPFolder *folder,
2357                         const gchar *path,
2358                         gint *exists, gint *recent, gint *unseen,
2359                         guint32 *uid_validity, gboolean block)
2360 {
2361         gchar *real_path;
2362         gint ok;
2363         gint exists_, recent_, unseen_;
2364         guint32 uid_validity_;
2365         
2366         if (!exists && !recent && !unseen && !uid_validity) {
2367                 if (session->mbox && strcmp(session->mbox, path) == 0)
2368                         return IMAP_SUCCESS;
2369         }
2370         if (!exists)
2371                 exists = &exists_;
2372         if (!recent)
2373                 recent = &recent_;
2374         if (!unseen)
2375                 unseen = &unseen_;
2376         if (!uid_validity)
2377                 uid_validity = &uid_validity_;
2378
2379         g_free(session->mbox);
2380         session->mbox = NULL;
2381
2382         real_path = imap_get_real_path(folder, path);
2383
2384         ok = imap_cmd_select(session, real_path,
2385                              exists, recent, unseen, uid_validity, block);
2386         if (ok != IMAP_SUCCESS)
2387                 log_warning(_("can't select folder: %s\n"), real_path);
2388         else {
2389                 session->mbox = g_strdup(path);
2390                 session->folder_content_changed = FALSE;
2391         }
2392         g_free(real_path);
2393
2394         return ok;
2395 }
2396
2397 static gint imap_status(IMAPSession *session, IMAPFolder *folder,
2398                         const gchar *path, IMAPFolderItem *item,
2399                         gint *messages,
2400                         guint32 *uid_next, guint32 *uid_validity,
2401                         gint *unseen, gboolean block)
2402 {
2403         int r;
2404         clistiter * iter;
2405         struct mailimap_mailbox_data_status * data_status;
2406         int got_values;
2407         gchar *real_path;
2408         guint mask = 0;
2409         
2410         real_path = imap_get_real_path(folder, path);
2411
2412         if (messages) {
2413                 mask |= 1 << 0;
2414         }
2415         if (uid_next) {
2416                 mask |= 1 << 2;
2417         }
2418         if (uid_validity) {
2419                 mask |= 1 << 3;
2420         }
2421         if (unseen) {
2422                 mask |= 1 << 4;
2423         }
2424         r = imap_threaded_status(FOLDER(folder), real_path, 
2425                 &data_status, mask);
2426
2427         g_free(real_path);
2428         if (r != MAILIMAP_NO_ERROR) {
2429                 debug_print("status err %d\n", r);
2430                 return IMAP_ERROR;
2431         }
2432         
2433         if (data_status->st_info_list == NULL) {
2434                 mailimap_mailbox_data_status_free(data_status);
2435                 debug_print("status->st_info_list == NULL\n");
2436                 return IMAP_ERROR;
2437         }
2438         
2439         got_values = 0;
2440         for(iter = clist_begin(data_status->st_info_list) ; iter != NULL ;
2441             iter = clist_next(iter)) {
2442                 struct mailimap_status_info * info;             
2443                 
2444                 info = clist_content(iter);
2445                 switch (info->st_att) {
2446                 case MAILIMAP_STATUS_ATT_MESSAGES:
2447                         * messages = info->st_value;
2448                         got_values |= 1 << 0;
2449                         break;
2450                         
2451                 case MAILIMAP_STATUS_ATT_UIDNEXT:
2452                         * uid_next = info->st_value;
2453                         got_values |= 1 << 2;
2454                         break;
2455                         
2456                 case MAILIMAP_STATUS_ATT_UIDVALIDITY:
2457                         * uid_validity = info->st_value;
2458                         got_values |= 1 << 3;
2459                         break;
2460                         
2461                 case MAILIMAP_STATUS_ATT_UNSEEN:
2462                         * unseen = info->st_value;
2463                         got_values |= 1 << 4;
2464                         break;
2465                 }
2466         }
2467         mailimap_mailbox_data_status_free(data_status);
2468         
2469         if (got_values != mask) {
2470                 debug_print("status: incomplete values received (%d)\n", got_values);
2471                 return IMAP_ERROR;
2472         }
2473         return IMAP_SUCCESS;
2474 }
2475
2476 static void imap_free_capabilities(IMAPSession *session)
2477 {
2478         slist_free_strings(session->capability);
2479         g_slist_free(session->capability);
2480         session->capability = NULL;
2481 }
2482
2483 /* low-level IMAP4rev1 commands */
2484
2485 static gint imap_cmd_login(IMAPSession *session,
2486                            const gchar *user, const gchar *pass,
2487                            const gchar *type)
2488 {
2489         int r;
2490         gint ok;
2491
2492         log_print("IMAP4> Logging %s to %s using %s\n", 
2493                         user,
2494                         SESSION(session)->server,
2495                         type);
2496         r = imap_threaded_login(session->folder, user, pass, type);
2497         if (r != MAILIMAP_NO_ERROR) {
2498                 log_print("IMAP4< Error logging in to %s\n",
2499                                 SESSION(session)->server);
2500                 ok = IMAP_ERROR;
2501         } else {
2502                 log_print("IMAP4< Login to %s successful\n",
2503                                 SESSION(session)->server);
2504                 ok = IMAP_SUCCESS;
2505         }
2506         return ok;
2507 }
2508
2509 static gint imap_cmd_logout(IMAPSession *session)
2510 {
2511         imap_threaded_disconnect(session->folder);
2512
2513         return IMAP_SUCCESS;
2514 }
2515
2516 static gint imap_cmd_noop(IMAPSession *session)
2517 {
2518         int r;
2519         unsigned int exists;
2520         
2521         r = imap_threaded_noop(session->folder, &exists);
2522         if (r != MAILIMAP_NO_ERROR) {
2523                 debug_print("noop err %d\n", r);
2524                 return IMAP_ERROR;
2525         }
2526         session->exists = exists;
2527         session_set_access_time(SESSION(session));
2528
2529         return IMAP_SUCCESS;
2530 }
2531
2532 #if USE_OPENSSL
2533 static gint imap_cmd_starttls(IMAPSession *session)
2534 {
2535         int r;
2536         
2537         r = imap_threaded_starttls(session->folder, 
2538                 SESSION(session)->server, SESSION(session)->port);
2539         if (r != MAILIMAP_NO_ERROR) {
2540                 debug_print("starttls err %d\n", r);
2541                 return IMAP_ERROR;
2542         }
2543         return IMAP_SUCCESS;
2544 }
2545 #endif
2546
2547 static gint imap_cmd_select(IMAPSession *session, const gchar *folder,
2548                             gint *exists, gint *recent, gint *unseen,
2549                             guint32 *uid_validity, gboolean block)
2550 {
2551         int r;
2552
2553         r = imap_threaded_select(session->folder, folder,
2554                                  exists, recent, unseen, uid_validity);
2555         if (r != MAILIMAP_NO_ERROR) {
2556                 debug_print("select err %d\n", r);
2557                 return IMAP_ERROR;
2558         }
2559         return IMAP_SUCCESS;
2560 }
2561
2562 static gint imap_cmd_examine(IMAPSession *session, const gchar *folder,
2563                              gint *exists, gint *recent, gint *unseen,
2564                              guint32 *uid_validity, gboolean block)
2565 {
2566         int r;
2567
2568         r = imap_threaded_examine(session->folder, folder,
2569                                   exists, recent, unseen, uid_validity);
2570         if (r != MAILIMAP_NO_ERROR) {
2571                 debug_print("examine err %d\n", r);
2572                 
2573                 return IMAP_ERROR;
2574         }
2575         return IMAP_SUCCESS;
2576 }
2577
2578 static gint imap_cmd_create(IMAPSession *session, const gchar *folder)
2579 {
2580         int r;
2581
2582         r = imap_threaded_create(session->folder, folder);
2583         if (r != MAILIMAP_NO_ERROR) {
2584                 
2585                 return IMAP_ERROR;
2586         }
2587
2588         return IMAP_SUCCESS;
2589 }
2590
2591 static gint imap_cmd_rename(IMAPSession *session, const gchar *old_folder,
2592                             const gchar *new_folder)
2593 {
2594         int r;
2595
2596         r = imap_threaded_rename(session->folder, old_folder,
2597                                  new_folder);
2598         if (r != MAILIMAP_NO_ERROR) {
2599                 
2600                 return IMAP_ERROR;
2601         }
2602
2603         return IMAP_SUCCESS;
2604 }
2605
2606 static gint imap_cmd_delete(IMAPSession *session, const gchar *folder)
2607 {
2608         int r;
2609         
2610
2611         r = imap_threaded_delete(session->folder, folder);
2612         if (r != MAILIMAP_NO_ERROR) {
2613                 
2614                 return IMAP_ERROR;
2615         }
2616
2617         return IMAP_SUCCESS;
2618 }
2619
2620 typedef struct _fetch_data {
2621         IMAPSession *session;
2622         guint32 uid;
2623         const gchar *filename;
2624         gboolean headers;
2625         gboolean body;
2626         gboolean done;
2627 } fetch_data;
2628
2629 static void *imap_cmd_fetch_thread(void *data)
2630 {
2631         fetch_data *stuff = (fetch_data *)data;
2632         IMAPSession *session = stuff->session;
2633         guint32 uid = stuff->uid;
2634         const gchar *filename = stuff->filename;
2635         int r;
2636         
2637         if (stuff->body) {
2638                 r = imap_threaded_fetch_content(session->folder,
2639                                                uid, 1, filename);
2640         }
2641         else {
2642                 r = imap_threaded_fetch_content(session->folder,
2643                                                 uid, 0, filename);
2644         }
2645         if (r != MAILIMAP_NO_ERROR) {
2646                 debug_print("fetch err %d\n", r);
2647                 return GINT_TO_POINTER(IMAP_ERROR);
2648         }
2649         return GINT_TO_POINTER(IMAP_SUCCESS);
2650 }
2651
2652 static gint imap_cmd_fetch(IMAPSession *session, guint32 uid,
2653                                 const gchar *filename, gboolean headers,
2654                                 gboolean body)
2655 {
2656         fetch_data *data = g_new0(fetch_data, 1);
2657         int result = 0;
2658         data->done = FALSE;
2659         data->session = session;
2660         data->uid = uid;
2661         data->filename = filename;
2662         data->headers = headers;
2663         data->body = body;
2664
2665         if (prefs_common.work_offline && 
2666             !inc_offline_should_override(
2667                 _("Sylpheed-Claws needs network access in order "
2668                   "to access the IMAP server."))) {
2669                 g_free(data);
2670                 return -1;
2671         }
2672         statusbar_print_all(_("Fetching message..."));
2673         result = GPOINTER_TO_INT(imap_cmd_fetch_thread(data));
2674         statusbar_pop_all();
2675         g_free(data);
2676         return result;
2677 }
2678
2679
2680 static gint imap_cmd_append(IMAPSession *session, const gchar *destfolder,
2681                             const gchar *file, IMAPFlags flags, 
2682                             guint32 *new_uid)
2683 {
2684         struct mailimap_flag_list * flag_list;
2685         int r;
2686         
2687         g_return_val_if_fail(file != NULL, IMAP_ERROR);
2688
2689         flag_list = imap_flag_to_lep(flags);
2690         r = imap_threaded_append(session->folder, destfolder,
2691                          file, flag_list, new_uid);
2692         mailimap_flag_list_free(flag_list);
2693
2694         if (r != MAILIMAP_NO_ERROR) {
2695                 debug_print("append err %d\n", r);
2696                 return IMAP_ERROR;
2697         }
2698         return IMAP_SUCCESS;
2699 }
2700
2701 static gint imap_cmd_copy(IMAPSession *session, struct mailimap_set * set,
2702                           const gchar *destfolder, GRelation *uid_mapping)
2703 {
2704         int r;
2705         
2706         g_return_val_if_fail(session != NULL, IMAP_ERROR);
2707         g_return_val_if_fail(set != NULL, IMAP_ERROR);
2708         g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
2709
2710         r = imap_threaded_copy(session->folder, set, destfolder);
2711         if (r != MAILIMAP_NO_ERROR) {
2712                 
2713                 return IMAP_ERROR;
2714         }
2715
2716         return IMAP_SUCCESS;
2717 }
2718
2719 static gint imap_cmd_store(IMAPSession *session, struct mailimap_set * set,
2720                            IMAPFlags flags, int do_add)
2721 {
2722         int r;
2723         struct mailimap_flag_list * flag_list;
2724         struct mailimap_store_att_flags * store_att_flags;
2725         
2726         flag_list = imap_flag_to_lep(flags);
2727         
2728         if (do_add)
2729                 store_att_flags =
2730                         mailimap_store_att_flags_new_add_flags_silent(flag_list);
2731         else
2732                 store_att_flags =
2733                         mailimap_store_att_flags_new_remove_flags_silent(flag_list);
2734         
2735         r = imap_threaded_store(session->folder, set, store_att_flags);
2736         mailimap_store_att_flags_free(store_att_flags);
2737         if (r != MAILIMAP_NO_ERROR) {
2738                 
2739                 return IMAP_ERROR;
2740         }
2741         
2742         return IMAP_SUCCESS;
2743 }
2744
2745 static gint imap_cmd_expunge(IMAPSession *session)
2746 {
2747         int r;
2748         
2749         if (prefs_common.work_offline && 
2750             !inc_offline_should_override(
2751                 _("Sylpheed-Claws needs network access in order "
2752                   "to access the IMAP server."))) {
2753                 return -1;
2754         }
2755
2756         r = imap_threaded_expunge(session->folder);
2757         if (r != MAILIMAP_NO_ERROR) {
2758                 
2759                 return IMAP_ERROR;
2760         }
2761
2762         return IMAP_SUCCESS;
2763 }
2764
2765 static void imap_path_separator_subst(gchar *str, gchar separator)
2766 {
2767         gchar *p;
2768         gboolean in_escape = FALSE;
2769
2770         if (!separator || separator == '/') return;
2771
2772         for (p = str; *p != '\0'; p++) {
2773                 if (*p == '/' && !in_escape)
2774                         *p = separator;
2775                 else if (*p == '&' && *(p + 1) != '-' && !in_escape)
2776                         in_escape = TRUE;
2777                 else if (*p == '-' && in_escape)
2778                         in_escape = FALSE;
2779         }
2780 }
2781
2782 static gchar *imap_modified_utf7_to_utf8(const gchar *mutf7_str)
2783 {
2784         static iconv_t cd = (iconv_t)-1;
2785         static gboolean iconv_ok = TRUE;
2786         GString *norm_utf7;
2787         gchar *norm_utf7_p;
2788         size_t norm_utf7_len;
2789         const gchar *p;
2790         gchar *to_str, *to_p;
2791         size_t to_len;
2792         gboolean in_escape = FALSE;
2793
2794         if (!iconv_ok) return g_strdup(mutf7_str);
2795
2796         if (cd == (iconv_t)-1) {
2797                 cd = iconv_open(CS_INTERNAL, CS_UTF_7);
2798                 if (cd == (iconv_t)-1) {
2799                         g_warning("iconv cannot convert UTF-7 to %s\n",
2800                                   CS_INTERNAL);
2801                         iconv_ok = FALSE;
2802                         return g_strdup(mutf7_str);
2803                 }
2804         }
2805
2806         /* modified UTF-7 to normal UTF-7 conversion */
2807         norm_utf7 = g_string_new(NULL);
2808
2809         for (p = mutf7_str; *p != '\0'; p++) {
2810                 /* replace: '&'  -> '+',
2811                             "&-" -> '&',
2812                             escaped ','  -> '/' */
2813                 if (!in_escape && *p == '&') {
2814                         if (*(p + 1) != '-') {
2815                                 g_string_append_c(norm_utf7, '+');
2816                                 in_escape = TRUE;
2817                         } else {
2818                                 g_string_append_c(norm_utf7, '&');
2819                                 p++;
2820                         }
2821                 } else if (in_escape && *p == ',') {
2822                         g_string_append_c(norm_utf7, '/');
2823                 } else if (in_escape && *p == '-') {
2824                         g_string_append_c(norm_utf7, '-');
2825                         in_escape = FALSE;
2826                 } else {
2827                         g_string_append_c(norm_utf7, *p);
2828                 }
2829         }
2830
2831         norm_utf7_p = norm_utf7->str;
2832         norm_utf7_len = norm_utf7->len;
2833         to_len = strlen(mutf7_str) * 5;
2834         to_p = to_str = g_malloc(to_len + 1);
2835
2836         if (iconv(cd, (ICONV_CONST gchar **)&norm_utf7_p, &norm_utf7_len,
2837                   &to_p, &to_len) == -1) {
2838                 g_warning(_("iconv cannot convert UTF-7 to %s\n"),
2839                           conv_get_locale_charset_str());
2840                 g_string_free(norm_utf7, TRUE);
2841                 g_free(to_str);
2842                 return g_strdup(mutf7_str);
2843         }
2844
2845         /* second iconv() call for flushing */
2846         iconv(cd, NULL, NULL, &to_p, &to_len);
2847         g_string_free(norm_utf7, TRUE);
2848         *to_p = '\0';
2849
2850         return to_str;
2851 }
2852
2853 static gchar *imap_utf8_to_modified_utf7(const gchar *from)
2854 {
2855         static iconv_t cd = (iconv_t)-1;
2856         static gboolean iconv_ok = TRUE;
2857         gchar *norm_utf7, *norm_utf7_p;
2858         size_t from_len, norm_utf7_len;
2859         GString *to_str;
2860         gchar *from_tmp, *to, *p;
2861         gboolean in_escape = FALSE;
2862
2863         if (!iconv_ok) return g_strdup(from);
2864
2865         if (cd == (iconv_t)-1) {
2866                 cd = iconv_open(CS_UTF_7, CS_INTERNAL);
2867                 if (cd == (iconv_t)-1) {
2868                         g_warning(_("iconv cannot convert %s to UTF-7\n"),
2869                                   CS_INTERNAL);
2870                         iconv_ok = FALSE;
2871                         return g_strdup(from);
2872                 }
2873         }
2874
2875         /* UTF-8 to normal UTF-7 conversion */
2876         Xstrdup_a(from_tmp, from, return g_strdup(from));
2877         from_len = strlen(from);
2878         norm_utf7_len = from_len * 5;
2879         Xalloca(norm_utf7, norm_utf7_len + 1, return g_strdup(from));
2880         norm_utf7_p = norm_utf7;
2881
2882 #define IS_PRINT(ch) (isprint(ch) && IS_ASCII(ch))
2883
2884         while (from_len > 0) {
2885                 if (*from_tmp == '+') {
2886                         *norm_utf7_p++ = '+';
2887                         *norm_utf7_p++ = '-';
2888                         norm_utf7_len -= 2;
2889                         from_tmp++;
2890                         from_len--;
2891                 } else if (IS_PRINT(*(guchar *)from_tmp)) {
2892                         /* printable ascii char */
2893                         *norm_utf7_p = *from_tmp;
2894                         norm_utf7_p++;
2895                         norm_utf7_len--;
2896                         from_tmp++;
2897                         from_len--;
2898                 } else {
2899                         size_t conv_len = 0;
2900
2901                         /* unprintable char: convert to UTF-7 */
2902                         p = from_tmp;
2903                         while (!IS_PRINT(*(guchar *)p) && conv_len < from_len) {
2904                                 conv_len += g_utf8_skip[*(guchar *)p];
2905                                 p += g_utf8_skip[*(guchar *)p];
2906                         }
2907
2908                         from_len -= conv_len;
2909                         if (iconv(cd, (ICONV_CONST gchar **)&from_tmp,
2910                                   &conv_len,
2911                                   &norm_utf7_p, &norm_utf7_len) == -1) {
2912                                 g_warning(_("iconv cannot convert UTF-8 to UTF-7\n"));
2913                                 return g_strdup(from);
2914                         }
2915
2916                         /* second iconv() call for flushing */
2917                         iconv(cd, NULL, NULL, &norm_utf7_p, &norm_utf7_len);
2918                 }
2919         }
2920
2921 #undef IS_PRINT
2922
2923         *norm_utf7_p = '\0';
2924         to_str = g_string_new(NULL);
2925         for (p = norm_utf7; p < norm_utf7_p; p++) {
2926                 /* replace: '&' -> "&-",
2927                             '+' -> '&',
2928                             "+-" -> '+',
2929                             BASE64 '/' -> ',' */
2930                 if (!in_escape && *p == '&') {
2931                         g_string_append(to_str, "&-");
2932                 } else if (!in_escape && *p == '+') {
2933                         if (*(p + 1) == '-') {
2934                                 g_string_append_c(to_str, '+');
2935                                 p++;
2936                         } else {
2937                                 g_string_append_c(to_str, '&');
2938                                 in_escape = TRUE;
2939                         }
2940                 } else if (in_escape && *p == '/') {
2941                         g_string_append_c(to_str, ',');
2942                 } else if (in_escape && *p == '-') {
2943                         g_string_append_c(to_str, '-');
2944                         in_escape = FALSE;
2945                 } else {
2946                         g_string_append_c(to_str, *p);
2947                 }
2948         }
2949
2950         if (in_escape) {
2951                 in_escape = FALSE;
2952                 g_string_append_c(to_str, '-');
2953         }
2954
2955         to = to_str->str;
2956         g_string_free(to_str, FALSE);
2957
2958         return to;
2959 }
2960
2961 static gboolean imap_rename_folder_func(GNode *node, gpointer data)
2962 {
2963         FolderItem *item = node->data;
2964         gchar **paths = data;
2965         const gchar *oldpath = paths[0];
2966         const gchar *newpath = paths[1];
2967         gchar *base;
2968         gchar *new_itempath;
2969         gint oldpathlen;
2970
2971         oldpathlen = strlen(oldpath);
2972         if (strncmp(oldpath, item->path, oldpathlen) != 0) {
2973                 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
2974                 return TRUE;
2975         }
2976
2977         base = item->path + oldpathlen;
2978         while (*base == G_DIR_SEPARATOR) base++;
2979         if (*base == '\0')
2980                 new_itempath = g_strdup(newpath);
2981         else
2982                 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
2983                                            NULL);
2984         g_free(item->path);
2985         item->path = new_itempath;
2986
2987         return FALSE;
2988 }
2989
2990 typedef struct _get_list_uid_data {
2991         Folder *folder;
2992         IMAPSession *session;
2993         IMAPFolderItem *item;
2994         GSList **msgnum_list;
2995         gboolean done;
2996 } get_list_uid_data;
2997
2998 static void *get_list_of_uids_thread(void *data)
2999 {
3000         get_list_uid_data *stuff = (get_list_uid_data *)data;
3001         Folder *folder = stuff->folder;
3002         IMAPFolderItem *item = stuff->item;
3003         GSList **msgnum_list = stuff->msgnum_list;
3004         gint ok, nummsgs = 0, lastuid_old;
3005         IMAPSession *session;
3006         GSList *uidlist, *elem;
3007         struct mailimap_set * set;
3008         clist * lep_uidlist;
3009         int r;
3010
3011         session = stuff->session;
3012         if (session == NULL) {
3013                 stuff->done = TRUE;
3014                 return GINT_TO_POINTER(-1);
3015         }
3016         /* no session locking here, it's already locked by caller */
3017         ok = imap_select(session, IMAP_FOLDER(folder), item->item.path,
3018                          NULL, NULL, NULL, NULL, TRUE);
3019         if (ok != IMAP_SUCCESS) {
3020                 stuff->done = TRUE;
3021                 return GINT_TO_POINTER(-1);
3022         }
3023
3024         uidlist = NULL;
3025         
3026         set = mailimap_set_new_interval(item->lastuid + 1, 0);
3027
3028         r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SIMPLE, set,
3029                                  &lep_uidlist);
3030         mailimap_set_free(set);
3031         
3032         if (r == MAILIMAP_NO_ERROR) {
3033                 GSList * fetchuid_list;
3034                 
3035                 fetchuid_list =
3036                         imap_uid_list_from_lep(lep_uidlist);
3037                 mailimap_search_result_free(lep_uidlist);
3038                 
3039                 uidlist = g_slist_concat(fetchuid_list, uidlist);
3040         }
3041         else {
3042                 GSList * fetchuid_list;
3043                 carray * lep_uidtab;
3044                 
3045                 r = imap_threaded_fetch_uid(folder, item->lastuid + 1,
3046                                             &lep_uidtab);
3047                 if (r == MAILIMAP_NO_ERROR) {
3048                         fetchuid_list =
3049                                 imap_uid_list_from_lep_tab(lep_uidtab);
3050                         imap_fetch_uid_list_free(lep_uidtab);
3051                         uidlist = g_slist_concat(fetchuid_list, uidlist);
3052                 }
3053         }
3054         
3055         lastuid_old = item->lastuid;
3056         *msgnum_list = g_slist_copy(item->uid_list);
3057         nummsgs = g_slist_length(*msgnum_list);
3058         debug_print("Got %d uids from cache\n", g_slist_length(item->uid_list));
3059
3060         for (elem = uidlist; elem != NULL; elem = g_slist_next(elem)) {
3061                 guint msgnum;
3062
3063                 msgnum = GPOINTER_TO_INT(elem->data);
3064                 if (msgnum > lastuid_old) {
3065                         *msgnum_list = g_slist_prepend(*msgnum_list, GINT_TO_POINTER(msgnum));
3066                         item->uid_list = g_slist_prepend(item->uid_list, GINT_TO_POINTER(msgnum));
3067                         nummsgs++;
3068
3069                         if(msgnum > item->lastuid)
3070                                 item->lastuid = msgnum;
3071                 }
3072         }
3073         g_slist_free(uidlist);
3074         stuff->done = TRUE;
3075         return GINT_TO_POINTER(nummsgs);
3076 }
3077
3078 static gint get_list_of_uids(IMAPSession *session, Folder *folder, IMAPFolderItem *item, GSList **msgnum_list)
3079 {
3080         gint result;
3081         get_list_uid_data *data = g_new0(get_list_uid_data, 1);
3082         data->done = FALSE;
3083         data->folder = folder;
3084         data->item = item;
3085         data->msgnum_list = msgnum_list;
3086         data->session = session;
3087         if (prefs_common.work_offline && 
3088             !inc_offline_should_override(
3089                 _("Sylpheed-Claws needs network access in order "
3090                   "to access the IMAP server."))) {
3091                 g_free(data);
3092                 return -1;
3093         }
3094
3095         result = GPOINTER_TO_INT(get_list_of_uids_thread(data));
3096         g_free(data);
3097         return result;
3098
3099 }
3100
3101 gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list, gboolean *old_uids_valid)
3102 {
3103         IMAPFolderItem *item = (IMAPFolderItem *)_item;
3104         IMAPSession *session;
3105         gint ok, nummsgs = 0, exists, uid_val, uid_next = 0;
3106         GSList *uidlist = NULL;
3107         gchar *dir;
3108         gboolean selected_folder;
3109         
3110         debug_print("get_num_list\n");
3111         
3112         g_return_val_if_fail(folder != NULL, -1);
3113         g_return_val_if_fail(item != NULL, -1);
3114         g_return_val_if_fail(item->item.path != NULL, -1);
3115         g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3116         g_return_val_if_fail(folder->account != NULL, -1);
3117
3118         session = imap_session_get(folder);
3119         g_return_val_if_fail(session != NULL, -1);
3120         lock_session();
3121
3122         if (FOLDER_ITEM(item)->path) 
3123                 statusbar_print_all(_("Scanning folder %s%c%s ..."),
3124                                       FOLDER_ITEM(item)->folder->name, 
3125                                       G_DIR_SEPARATOR,
3126                                       FOLDER_ITEM(item)->path);
3127         else
3128                 statusbar_print_all(_("Scanning folder %s ..."),
3129                                       FOLDER_ITEM(item)->folder->name);
3130
3131         selected_folder = (session->mbox != NULL) &&
3132                           (!strcmp(session->mbox, item->item.path));
3133         if (selected_folder && time(NULL) - item->use_cache < 2) {
3134                 ok = imap_cmd_noop(session);
3135                 if (ok != IMAP_SUCCESS) {
3136                         debug_print("disconnected!\n");
3137                         session = imap_reconnect_if_possible(folder, session);
3138                         if (session == NULL) {
3139                                 statusbar_pop_all();
3140                                 unlock_session();
3141                                 return -1;
3142                         }
3143                 }
3144                 exists = session->exists;
3145
3146                 uid_next = item->c_uid_next;
3147                 uid_val = item->c_uid_validity;
3148                 *old_uids_valid = TRUE;
3149         } else {
3150                 if (item->use_cache && time(NULL) - item->use_cache < 2) {
3151                         exists = item->c_messages;
3152                         uid_next = item->c_uid_next;
3153                         uid_val = item->c_uid_validity;
3154                         ok = IMAP_SUCCESS;
3155                         debug_print("using cache %d %d %d\n", exists, uid_next, uid_val);
3156                 } else {
3157                         ok = imap_status(session, IMAP_FOLDER(folder), item->item.path, item,
3158                                  &exists, &uid_next, &uid_val, NULL, FALSE);
3159                 }
3160                 item->item.last_num = uid_next - 1;
3161                 
3162                 item->use_cache = (time_t)0;
3163                 if (ok != IMAP_SUCCESS) {
3164                         statusbar_pop_all();
3165                         unlock_session();
3166                         return -1;
3167                 }
3168                 if(item->item.mtime == uid_val)
3169                         *old_uids_valid = TRUE;
3170                 else {
3171                         *old_uids_valid = FALSE;
3172
3173                         debug_print("Freeing imap uid cache\n");
3174                         item->lastuid = 0;
3175                         g_slist_free(item->uid_list);
3176                         item->uid_list = NULL;
3177                 
3178                         item->item.mtime = uid_val;
3179
3180                         imap_delete_all_cached_messages((FolderItem *)item);
3181                 }
3182         }
3183
3184         /* If old uid_next matches new uid_next we can be sure no message
3185            was added to the folder */
3186         debug_print("uid_next is %d and item->uid_next %d \n", 
3187                 uid_next, item->uid_next);
3188         if (uid_next == item->uid_next) {
3189                 nummsgs = g_slist_length(item->uid_list);
3190
3191                 /* If number of messages is still the same we
3192                    know our caches message numbers are still valid,
3193                    otherwise if the number of messages has decrease
3194                    we discard our cache to start a new scan to find
3195                    out which numbers have been removed */
3196                 if (exists == nummsgs) {
3197                         debug_print("exists == nummsgs\n");
3198                         *msgnum_list = g_slist_copy(item->uid_list);
3199                         statusbar_pop_all();
3200                         unlock_session();
3201                         return nummsgs;
3202                 } else if (exists < nummsgs) {
3203                         debug_print("Freeing imap uid cache");
3204                         item->lastuid = 0;
3205                         g_slist_free(item->uid_list);
3206                         item->uid_list = NULL;
3207                 }
3208         }
3209
3210         if (exists == 0) {
3211                 *msgnum_list = NULL;
3212                 statusbar_pop_all();
3213                 unlock_session();
3214                 return 0;
3215         }
3216
3217         nummsgs = get_list_of_uids(session, folder, item, &uidlist);
3218
3219         if (nummsgs < 0) {
3220                 statusbar_pop_all();
3221                 unlock_session();
3222                 return -1;
3223         }
3224
3225         if (nummsgs != exists) {
3226                 /* Cache contains more messages then folder, we have cached
3227                    an old UID of a message that was removed and new messages
3228                    have been added too, otherwise the uid_next check would
3229                    not have failed */
3230                 debug_print("Freeing imap uid cache");
3231                 item->lastuid = 0;
3232                 g_slist_free(item->uid_list);
3233                 item->uid_list = NULL;
3234
3235                 g_slist_free(*msgnum_list);
3236
3237                 nummsgs = get_list_of_uids(session, folder, item, &uidlist);
3238         }
3239
3240         *msgnum_list = uidlist;
3241
3242         dir = folder_item_get_path((FolderItem *)item);
3243         debug_print("removing old messages from %s\n", dir);
3244         remove_numbered_files_not_in_list(dir, *msgnum_list);
3245         g_free(dir);
3246         
3247         item->uid_next = uid_next;
3248         
3249         debug_print("get_num_list - ok - %i\n", nummsgs);
3250         statusbar_pop_all();
3251         unlock_session();
3252         return nummsgs;
3253 }
3254
3255 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item)
3256 {
3257         MsgInfo *msginfo;
3258         MsgFlags flags;
3259
3260         flags.perm_flags = MSG_NEW|MSG_UNREAD;
3261         flags.tmp_flags = 0;
3262
3263         g_return_val_if_fail(item != NULL, NULL);
3264         g_return_val_if_fail(file != NULL, NULL);
3265
3266         if (folder_has_parent_of_type(item, F_QUEUE)) {
3267                 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
3268         } else if (folder_has_parent_of_type(item, F_DRAFT)) {
3269                 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
3270         }
3271
3272         msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3273         if (!msginfo) return NULL;
3274         
3275         msginfo->plaintext_file = g_strdup(file);
3276         msginfo->folder = item;
3277
3278         return msginfo;
3279 }
3280
3281 GSList *imap_get_msginfos(Folder *folder, FolderItem *item,
3282                           GSList *msgnum_list)
3283 {
3284         IMAPSession *session;
3285         MsgInfoList *ret = NULL;
3286         gint ok;
3287         
3288         debug_print("get_msginfos\n");
3289         
3290         g_return_val_if_fail(folder != NULL, NULL);
3291         g_return_val_if_fail(item != NULL, NULL);
3292         g_return_val_if_fail(msgnum_list != NULL, NULL);
3293
3294         session = imap_session_get(folder);
3295         g_return_val_if_fail(session != NULL, NULL);
3296         lock_session();
3297         debug_print("IMAP getting msginfos\n");
3298         ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3299                          NULL, NULL, NULL, NULL, FALSE);
3300         if (ok != IMAP_SUCCESS) {
3301                 unlock_session();
3302                 return NULL;
3303         }
3304         if (!(folder_has_parent_of_type(item, F_DRAFT) || 
3305               folder_has_parent_of_type(item, F_QUEUE))) {
3306                 ret = g_slist_concat(ret,
3307                         imap_get_uncached_messages(session, item,
3308                                                    msgnum_list));
3309         } else {
3310                 MsgNumberList *sorted_list, *elem;
3311                 gint startnum, lastnum;
3312
3313                 sorted_list = g_slist_sort(g_slist_copy(msgnum_list), g_int_compare);
3314
3315                 startnum = lastnum = GPOINTER_TO_INT(sorted_list->data);
3316
3317                 for (elem = sorted_list;; elem = g_slist_next(elem)) {
3318                         guint num = 0;
3319
3320                         if (elem)
3321                                 num = GPOINTER_TO_INT(elem->data);
3322
3323                         if (num > lastnum + 1 || elem == NULL) {
3324                                 int i;
3325                                 for (i = startnum; i <= lastnum; ++i) {
3326                                         gchar *file;
3327                         
3328                                         file = imap_fetch_msg(folder, item, i);
3329                                         if (file != NULL) {
3330                                                 MsgInfo *msginfo = imap_parse_msg(file, item);
3331                                                 if (msginfo != NULL) {
3332                                                         msginfo->msgnum = i;
3333                                                         ret = g_slist_append(ret, msginfo);
3334                                                 }
3335                                                 g_free(file);
3336                                         }
3337                                         session_set_access_time(SESSION(session));
3338                                 }
3339
3340                                 if (elem == NULL)
3341                                         break;
3342
3343                                 startnum = num;
3344                         }
3345                         lastnum = num;
3346                 }
3347
3348                 g_slist_free(sorted_list);
3349         }
3350         unlock_session();
3351         return ret;
3352 }
3353
3354 MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid)
3355 {
3356         MsgInfo *msginfo = NULL;
3357         MsgInfoList *msginfolist;
3358         MsgNumberList numlist;
3359
3360         numlist.next = NULL;
3361         numlist.data = GINT_TO_POINTER(uid);
3362
3363         msginfolist = imap_get_msginfos(folder, item, &numlist);
3364         if (msginfolist != NULL) {
3365                 msginfo = msginfolist->data;
3366                 g_slist_free(msginfolist);
3367         }
3368
3369         return msginfo;
3370 }
3371
3372 gboolean imap_scan_required(Folder *folder, FolderItem *_item)
3373 {
3374         IMAPSession *session;
3375         IMAPFolderItem *item = (IMAPFolderItem *)_item;
3376         gint ok, exists = 0, unseen = 0;
3377         guint32 uid_next, uid_val;
3378         gboolean selected_folder;
3379         
3380         g_return_val_if_fail(folder != NULL, FALSE);
3381         g_return_val_if_fail(item != NULL, FALSE);
3382         g_return_val_if_fail(item->item.folder != NULL, FALSE);
3383         g_return_val_if_fail(FOLDER_CLASS(item->item.folder) == &imap_class, FALSE);
3384
3385         if (item->item.path == NULL)
3386                 return FALSE;
3387
3388         session = imap_session_get(folder);
3389         g_return_val_if_fail(session != NULL, FALSE);
3390         lock_session();
3391         selected_folder = (session->mbox != NULL) &&
3392                           (!strcmp(session->mbox, item->item.path));
3393         if (selected_folder && time(NULL) - item->use_cache < 2) {
3394                 ok = imap_cmd_noop(session);
3395                 if (ok != IMAP_SUCCESS) {
3396                         debug_print("disconnected!\n");
3397                         session = imap_reconnect_if_possible(folder, session);
3398                         if (session == NULL)
3399                                 return FALSE;
3400                         lock_session();
3401                 }
3402
3403                 if (session->folder_content_changed
3404                 ||  session->exists != item->item.total_msgs) {
3405                         unlock_session();
3406                         return TRUE;
3407                 }
3408         } else {
3409                 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path, IMAP_FOLDER_ITEM(item),
3410                                  &exists, &uid_next, &uid_val, &unseen, FALSE);
3411                 if (ok != IMAP_SUCCESS) {
3412                         unlock_session();
3413                         return FALSE;
3414                 }
3415
3416                 item->use_cache = time(NULL);
3417                 item->c_messages = exists;
3418                 item->c_uid_next = uid_next;
3419                 item->c_uid_validity = uid_val;
3420                 item->c_unseen = unseen;
3421                 item->item.last_num = uid_next - 1;
3422                 debug_print("uidnext %d, item->uid_next %d, exists %d, item->item.total_msgs %d\n", 
3423                         uid_next, item->uid_next, exists, item->item.total_msgs);
3424                 if ((uid_next != item->uid_next) || (exists != item->item.total_msgs)
3425                     || unseen != item->item.unread_msgs || uid_val != item->item.mtime) {
3426                         unlock_session();
3427                         return TRUE;
3428                 }
3429         }
3430         unlock_session();
3431         return FALSE;
3432 }
3433
3434 void imap_change_flags(Folder *folder, FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
3435 {
3436         IMAPSession *session;
3437         IMAPFlags flags_set = 0, flags_unset = 0;
3438         gint ok = IMAP_SUCCESS;
3439         MsgNumberList numlist;
3440         hashtable_data *ht_data = NULL;
3441
3442         g_return_if_fail(folder != NULL);
3443         g_return_if_fail(folder->klass == &imap_class);
3444         g_return_if_fail(item != NULL);
3445         g_return_if_fail(item->folder == folder);
3446         g_return_if_fail(msginfo != NULL);
3447         g_return_if_fail(msginfo->folder == item);
3448
3449         session = imap_session_get(folder);
3450         if (!session) {
3451                 return;
3452         }
3453         lock_session();
3454         if ((ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
3455             NULL, NULL, NULL, NULL, FALSE)) != IMAP_SUCCESS) {
3456                 unlock_session();
3457                 return;
3458         }
3459
3460         if (!MSG_IS_MARKED(msginfo->flags) &&  (newflags & MSG_MARKED))
3461                 flags_set |= IMAP_FLAG_FLAGGED;
3462         if ( MSG_IS_MARKED(msginfo->flags) && !(newflags & MSG_MARKED))
3463                 flags_unset |= IMAP_FLAG_FLAGGED;
3464
3465         if (!MSG_IS_UNREAD(msginfo->flags) &&  (newflags & MSG_UNREAD))
3466                 flags_unset |= IMAP_FLAG_SEEN;
3467         if ( MSG_IS_UNREAD(msginfo->flags) && !(newflags & MSG_UNREAD))
3468                 flags_set |= IMAP_FLAG_SEEN;
3469
3470         if (!MSG_IS_REPLIED(msginfo->flags) &&  (newflags & MSG_REPLIED))
3471                 flags_set |= IMAP_FLAG_ANSWERED;
3472         if ( MSG_IS_REPLIED(msginfo->flags) && !(newflags & MSG_REPLIED))
3473                 flags_unset |= IMAP_FLAG_ANSWERED;
3474
3475         if (!MSG_IS_DELETED(msginfo->flags) &&  (newflags & MSG_DELETED))
3476                 flags_set |= IMAP_FLAG_DELETED;
3477         if ( MSG_IS_DELETED(msginfo->flags) && !(newflags & MSG_DELETED))
3478                 flags_unset |= IMAP_FLAG_DELETED;
3479
3480         numlist.next = NULL;
3481         numlist.data = GINT_TO_POINTER(msginfo->msgnum);
3482
3483         if (IMAP_FOLDER_ITEM(item)->batching) {
3484                 /* instead of performing an UID STORE command for each message change,
3485                  * as a lot of them can change "together", we just fill in hashtables
3486                  * and defer the treatment so that we're able to send only one
3487                  * command.
3488                  */
3489                 debug_print("IMAP batch mode on, deferring flags change\n");
3490                 if (flags_set) {
3491                         ht_data = g_hash_table_lookup(IMAP_FOLDER_ITEM(item)->flags_set_table, 
3492                                 GINT_TO_POINTER(flags_set));
3493                         if (ht_data == NULL) {
3494                                 ht_data = g_new0(hashtable_data, 1);
3495                                 ht_data->session = session;
3496                                 ht_data->item = IMAP_FOLDER_ITEM(item);
3497                                 g_hash_table_insert(IMAP_FOLDER_ITEM(item)->flags_set_table, 
3498                                         GINT_TO_POINTER(flags_set), ht_data);
3499                         }
3500                         if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
3501                                 ht_data->msglist = g_slist_prepend(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum));
3502                 } 
3503                 if (flags_unset) {
3504                         ht_data = g_hash_table_lookup(IMAP_FOLDER_ITEM(item)->flags_unset_table, 
3505                                 GINT_TO_POINTER(flags_unset));
3506                         if (ht_data == NULL) {
3507                                 ht_data = g_new0(hashtable_data, 1);
3508                                 ht_data->session = session;
3509                                 ht_data->item = IMAP_FOLDER_ITEM(item);
3510                                 g_hash_table_insert(IMAP_FOLDER_ITEM(item)->flags_unset_table, 
3511                                         GINT_TO_POINTER(flags_unset), ht_data);
3512                         }
3513                         if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
3514                                 ht_data->msglist = g_slist_prepend(ht_data->msglist, 
3515                                         GINT_TO_POINTER(msginfo->msgnum));              
3516                 }
3517         } else {
3518                 debug_print("IMAP changing flags\n");
3519                 if (flags_set) {
3520                         ok = imap_set_message_flags(session, &numlist, flags_set, TRUE);
3521                         if (ok != IMAP_SUCCESS) {
3522                                 unlock_session();
3523                                 return;
3524                         }
3525                 }
3526
3527                 if (flags_unset) {
3528                         ok = imap_set_message_flags(session, &numlist, flags_unset, FALSE);
3529                         if (ok != IMAP_SUCCESS) {
3530                                 unlock_session();
3531                                 return;
3532                         }
3533                 }
3534         }
3535         msginfo->flags.perm_flags = newflags;
3536         unlock_session();
3537         return;
3538 }
3539
3540 static gint imap_remove_msg(Folder *folder, FolderItem *item, gint uid)
3541 {
3542         gint ok;
3543         IMAPSession *session;
3544         gchar *dir;
3545         MsgNumberList numlist;
3546         
3547         g_return_val_if_fail(folder != NULL, -1);
3548         g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3549         g_return_val_if_fail(item != NULL, -1);
3550
3551         session = imap_session_get(folder);
3552         if (!session) return -1;
3553         lock_session();
3554         ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3555                          NULL, NULL, NULL, NULL, FALSE);
3556         if (ok != IMAP_SUCCESS) {
3557                 unlock_session();
3558                 return ok;
3559         }
3560         numlist.next = NULL;
3561         numlist.data = GINT_TO_POINTER(uid);
3562         
3563         ok = imap_set_message_flags
3564                 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
3565                 &numlist, IMAP_FLAG_DELETED, TRUE);
3566         if (ok != IMAP_SUCCESS) {
3567                 log_warning(_("can't set deleted flags: %d\n"), uid);
3568                 unlock_session();
3569                 return ok;
3570         }
3571
3572         if (!session->uidplus) {
3573                 ok = imap_cmd_expunge(session);
3574         } else {
3575                 gchar *uidstr;
3576
3577                 uidstr = g_strdup_printf("%u", uid);
3578                 ok = imap_cmd_expunge(session);
3579                 g_free(uidstr);
3580         }
3581         if (ok != IMAP_SUCCESS) {
3582                 log_warning(_("can't expunge\n"));
3583                 unlock_session();
3584                 return ok;
3585         }
3586
3587         IMAP_FOLDER_ITEM(item)->uid_list = g_slist_remove(
3588             IMAP_FOLDER_ITEM(item)->uid_list, numlist.data);
3589         dir = folder_item_get_path(item);
3590         if (is_dir_exist(dir))
3591                 remove_numbered_files(dir, uid, uid);
3592         g_free(dir);
3593         unlock_session();
3594         return IMAP_SUCCESS;
3595 }
3596
3597 static gint compare_msginfo(gconstpointer a, gconstpointer b)
3598 {
3599         return ((MsgInfo *)a)->msgnum - ((MsgInfo *)b)->msgnum;
3600 }
3601
3602 static guint gslist_find_next_num(MsgNumberList **list, guint num)
3603 {
3604         GSList *elem;
3605
3606         g_return_val_if_fail(list != NULL, -1);
3607
3608         for (elem = *list; elem != NULL; elem = g_slist_next(elem))
3609                 if (GPOINTER_TO_INT(elem->data) >= num)
3610                         break;
3611         *list = elem;
3612         return elem != NULL ? GPOINTER_TO_INT(elem->data) : (gint)-1;
3613 }
3614
3615 /*
3616  * NEW and DELETED flags are not syncronized
3617  * - The NEW/RECENT flags in IMAP folders can not really be directly
3618  *   modified by Sylpheed
3619  * - The DELETE/DELETED flag in IMAP and Sylpheed don't have the same
3620  *   meaning, in IMAP it always removes the messages from the FolderItem
3621  *   in Sylpheed it can mean to move the message to trash
3622  */
3623
3624 typedef struct _get_flags_data {
3625         Folder *folder;
3626         FolderItem *item;
3627         MsgInfoList *msginfo_list;
3628         GRelation *msgflags;
3629         gboolean full_search;
3630         gboolean done;
3631 } get_flags_data;
3632
3633 static /*gint*/ void *imap_get_flags_thread(void *data)
3634 {
3635         get_flags_data *stuff = (get_flags_data *)data;
3636         Folder *folder = stuff->folder;
3637         FolderItem *item = stuff->item;
3638         MsgInfoList *msginfo_list = stuff->msginfo_list;
3639         GRelation *msgflags = stuff->msgflags;
3640         gboolean full_search = stuff->full_search;
3641         IMAPSession *session;
3642         GSList *sorted_list = NULL;
3643         GSList *unseen = NULL, *answered = NULL, *flagged = NULL, *deleted = NULL;
3644         GSList *p_unseen, *p_answered, *p_flagged, *p_deleted;
3645         GSList *elem;
3646         GSList *seq_list, *cur;
3647         gboolean reverse_seen = FALSE;
3648         GString *cmd_buf;
3649         gint ok;
3650         gint exists_cnt, unseen_cnt;
3651         gboolean selected_folder;
3652         
3653         if (folder == NULL || item == NULL) {
3654                 stuff->done = TRUE;
3655                 return GINT_TO_POINTER(-1);
3656         }
3657
3658         session = imap_session_get(folder);
3659         if (session == NULL) {
3660                 stuff->done = TRUE;
3661                 return GINT_TO_POINTER(-1);
3662         }
3663         lock_session();
3664         selected_folder = (session->mbox != NULL) &&
3665                           (!strcmp(session->mbox, item->path));
3666
3667         if (!selected_folder) {
3668                 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3669                         &exists_cnt, NULL, &unseen_cnt, NULL, TRUE);
3670                 if (ok != IMAP_SUCCESS) {
3671                         stuff->done = TRUE;
3672                         unlock_session();
3673                         return GINT_TO_POINTER(-1);
3674                 }
3675
3676                 if (unseen_cnt > exists_cnt / 2)
3677                         reverse_seen = TRUE;
3678         } 
3679         else {
3680                 if (item->unread_msgs > item->total_msgs / 2)
3681                         reverse_seen = TRUE;
3682         }
3683
3684         cmd_buf = g_string_new(NULL);
3685
3686         sorted_list = g_slist_sort(g_slist_copy(msginfo_list), compare_msginfo);
3687         if (!full_search) {
3688                 seq_list = imap_get_lep_set_from_msglist(msginfo_list);
3689         } else {
3690                 struct mailimap_set * set;
3691                 set = mailimap_set_new_interval(1, 0);
3692                 seq_list = g_slist_append(NULL, set);
3693         }
3694
3695         for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
3696                 struct mailimap_set * imapset;
3697                 clist * lep_uidlist;
3698                 int r;
3699                 
3700                 imapset = cur->data;
3701                 if (reverse_seen) {
3702                         r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SEEN,
3703                                                  imapset, &lep_uidlist);
3704                 }
3705                 else {
3706                         r = imap_threaded_search(folder,
3707                                                  IMAP_SEARCH_TYPE_UNSEEN,
3708                                                  imapset, &lep_uidlist);
3709                 }
3710                 if (r == MAILIMAP_NO_ERROR) {
3711                         GSList * uidlist;
3712                         
3713                         uidlist = imap_uid_list_from_lep(lep_uidlist);
3714                         mailimap_search_result_free(lep_uidlist);
3715                         
3716                         unseen = g_slist_concat(unseen, uidlist);
3717                 }
3718                 
3719                 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_FLAGGED,
3720                                          imapset, &lep_uidlist);
3721                 if (r == MAILIMAP_NO_ERROR) {
3722                         GSList * uidlist;
3723
3724                         uidlist = imap_uid_list_from_lep(lep_uidlist);
3725                         mailimap_search_result_free(lep_uidlist);
3726
3727                         flagged = g_slist_concat(flagged, uidlist);
3728                 }
3729
3730                 if (item->opened || item->processing_pending || item == folder->inbox) {
3731                         r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_ANSWERED,
3732                                                  imapset, &lep_uidlist);
3733                         if (r == MAILIMAP_NO_ERROR) {
3734                                 GSList * uidlist;
3735
3736                                 uidlist = imap_uid_list_from_lep(lep_uidlist);
3737                                 mailimap_search_result_free(lep_uidlist);
3738
3739                                 answered = g_slist_concat(answered, uidlist);
3740                         }
3741
3742                         r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_DELETED,
3743                                                  imapset, &lep_uidlist);
3744                         if (r == MAILIMAP_NO_ERROR) {
3745                                 GSList * uidlist;
3746
3747                                 uidlist = imap_uid_list_from_lep(lep_uidlist);
3748                                 mailimap_search_result_free(lep_uidlist);
3749
3750                                 deleted = g_slist_concat(deleted, uidlist);
3751                         }
3752                 }
3753         }
3754
3755         p_unseen = unseen;
3756         p_answered = answered;
3757         p_flagged = flagged;
3758         p_deleted = deleted;
3759
3760         for (elem = sorted_list; elem != NULL; elem = g_slist_next(elem)) {
3761                 MsgInfo *msginfo;
3762                 MsgPermFlags flags;
3763                 gboolean wasnew;
3764                 
3765                 msginfo = (MsgInfo *) elem->data;
3766                 flags = msginfo->flags.perm_flags;
3767                 wasnew = (flags & MSG_NEW);
3768                 if (item->opened || item->processing_pending || item == folder->inbox) {
3769                         flags &= ~((reverse_seen ? 0 : MSG_UNREAD | MSG_NEW) | MSG_REPLIED | MSG_MARKED);
3770                 } else {
3771                         flags &= ~((reverse_seen ? 0 : MSG_UNREAD | MSG_NEW | MSG_MARKED));
3772                 }
3773                 if (reverse_seen)
3774                         flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
3775                 if (gslist_find_next_num(&p_unseen, msginfo->msgnum) == msginfo->msgnum) {
3776                         if (!reverse_seen) {
3777                                 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
3778                         } else {
3779                                 flags &= ~(MSG_UNREAD | MSG_NEW);
3780                         }
3781                 }
3782                 
3783                 if (gslist_find_next_num(&p_flagged, msginfo->msgnum) == msginfo->msgnum)
3784                         flags |= MSG_MARKED;
3785                 else
3786                         flags &= ~MSG_MARKED;
3787
3788                 if (item->opened || item->processing_pending || item == folder->inbox) {
3789                         if (gslist_find_next_num(&p_answered, msginfo->msgnum) == msginfo->msgnum)
3790                                 flags |= MSG_REPLIED;
3791                         else
3792                                 flags &= ~MSG_REPLIED;
3793                         if (gslist_find_next_num(&p_deleted, msginfo->msgnum) == msginfo->msgnum)
3794                                 flags |= MSG_DELETED;
3795                         else
3796                                 flags &= ~MSG_DELETED;
3797                 }
3798                 g_relation_insert(msgflags, msginfo, GINT_TO_POINTER(flags));
3799         }
3800
3801         imap_lep_set_free(seq_list);
3802         g_slist_free(flagged);
3803         g_slist_free(deleted);
3804         g_slist_free(answered);
3805         g_slist_free(unseen);
3806         g_slist_free(sorted_list);
3807         g_string_free(cmd_buf, TRUE);
3808
3809         stuff->done = TRUE;
3810         unlock_session();
3811         return GINT_TO_POINTER(0);
3812 }
3813
3814 static gint imap_get_flags(Folder *folder, FolderItem *item,
3815                            MsgInfoList *msginfo_list, GRelation *msgflags)
3816 {
3817         gint result;
3818         get_flags_data *data = g_new0(get_flags_data, 1);
3819         data->done = FALSE;
3820         data->folder = folder;
3821         data->item = item;
3822         data->msginfo_list = msginfo_list;
3823         data->msgflags = msgflags;
3824         data->full_search = FALSE;
3825
3826         GSList *tmp = NULL, *cur;
3827         
3828         if (prefs_common.work_offline && 
3829             !inc_offline_should_override(
3830                 _("Sylpheed-Claws needs network access in order "
3831                   "to access the IMAP server."))) {
3832                 g_free(data);
3833                 return -1;
3834         }
3835
3836         tmp = folder_item_get_msg_list(item);
3837
3838         if (g_slist_length(tmp) <= g_slist_length(msginfo_list))
3839                 data->full_search = TRUE;
3840         
3841         for (cur = tmp; cur; cur = cur->next)
3842                 procmsg_msginfo_free((MsgInfo *)cur->data);
3843         
3844         g_slist_free(tmp);
3845
3846         result = GPOINTER_TO_INT(imap_get_flags_thread(data));
3847         
3848         g_free(data);
3849         return result;
3850
3851 }
3852
3853 static gboolean process_flags(gpointer key, gpointer value, gpointer user_data)
3854 {
3855         gboolean flags_set = GPOINTER_TO_INT(user_data);
3856         gint flags_value = GPOINTER_TO_INT(key);
3857         hashtable_data *data = (hashtable_data *)value;
3858         IMAPFolderItem *_item = data->item;
3859         FolderItem *item = (FolderItem *)_item;
3860         gint ok = IMAP_ERROR;
3861         IMAPSession *session = imap_session_get(item->folder);
3862
3863         data->msglist = g_slist_reverse(data->msglist);
3864         
3865         debug_print("IMAP %ssetting flags to %d for %d messages\n",
3866                 flags_set?"":"un",
3867                 flags_value,
3868                 g_slist_length(data->msglist));
3869         
3870         if (session) {
3871                 lock_session();
3872                 ok = imap_select(session, IMAP_FOLDER(item->folder), item->path,
3873                          NULL, NULL, NULL, NULL, FALSE);
3874         }
3875         if (ok == IMAP_SUCCESS) {
3876                 imap_set_message_flags(data->session, data->msglist, flags_value, flags_set);
3877         } else {
3878                 g_warning("can't select mailbox %s\n", item->path);
3879         }
3880         if (session)
3881                 unlock_session();
3882         g_slist_free(data->msglist);    
3883         g_free(data);
3884         return TRUE;
3885 }
3886
3887 static void process_hashtable(IMAPFolderItem *item)
3888 {
3889         if (item->flags_set_table) {
3890                 g_hash_table_foreach_remove(item->flags_set_table, process_flags, GINT_TO_POINTER(TRUE));
3891                 g_hash_table_destroy(item->flags_set_table);
3892                 item->flags_set_table = NULL;
3893         }
3894         if (item->flags_unset_table) {
3895                 g_hash_table_foreach_remove(item->flags_unset_table, process_flags, GINT_TO_POINTER(FALSE));
3896                 g_hash_table_destroy(item->flags_unset_table);
3897                 item->flags_unset_table = NULL;
3898         }
3899 }
3900
3901 static void imap_set_batch (Folder *folder, FolderItem *_item, gboolean batch)
3902 {
3903         IMAPFolderItem *item = (IMAPFolderItem *)_item;
3904
3905         g_return_if_fail(item != NULL);
3906         
3907         if (item->batching == batch)
3908                 return;
3909         
3910         if (batch) {
3911                 item->batching = TRUE;
3912                 debug_print("IMAP switching to batch mode\n");
3913                 if (!item->flags_set_table) {
3914                         item->flags_set_table = g_hash_table_new(NULL, g_direct_equal);
3915                 }
3916                 if (!item->flags_unset_table) {
3917                         item->flags_unset_table = g_hash_table_new(NULL, g_direct_equal);
3918                 }
3919         } else {
3920                 debug_print("IMAP switching away from batch mode\n");
3921                 /* process stuff */
3922                 process_hashtable(item);
3923                 item->batching = FALSE;
3924         }
3925 }
3926
3927
3928
3929 /* data types conversion libetpan <-> sylpheed */
3930
3931
3932
3933 #define ETPAN_IMAP_MB_MARKED      1
3934 #define ETPAN_IMAP_MB_UNMARKED    2
3935 #define ETPAN_IMAP_MB_NOSELECT    4
3936 #define ETPAN_IMAP_MB_NOINFERIORS 8
3937
3938 static int imap_flags_to_flags(struct mailimap_mbx_list_flags * imap_flags)
3939 {
3940   int flags;
3941   clistiter * cur;
3942   
3943   flags = 0;
3944   if (imap_flags->mbf_type == MAILIMAP_MBX_LIST_FLAGS_SFLAG) {
3945     switch (imap_flags->mbf_sflag) {
3946     case MAILIMAP_MBX_LIST_SFLAG_MARKED:
3947       flags |= ETPAN_IMAP_MB_MARKED;
3948       break;
3949     case MAILIMAP_MBX_LIST_SFLAG_NOSELECT:
3950       flags |= ETPAN_IMAP_MB_NOSELECT;
3951       break;
3952     case MAILIMAP_MBX_LIST_SFLAG_UNMARKED:
3953       flags |= ETPAN_IMAP_MB_UNMARKED;
3954       break;
3955     }
3956   }
3957   
3958   for(cur = clist_begin(imap_flags->mbf_oflags) ; cur != NULL ;
3959       cur = clist_next(cur)) {
3960     struct mailimap_mbx_list_oflag * oflag;
3961     
3962     oflag = clist_content(cur);
3963     
3964     switch (oflag->of_type) {
3965     case MAILIMAP_MBX_LIST_OFLAG_NOINFERIORS:
3966       flags |= ETPAN_IMAP_MB_NOINFERIORS;
3967       break;
3968     }
3969   }
3970   
3971   return flags;
3972 }
3973
3974 static GSList * imap_list_from_lep(IMAPFolder * folder,
3975                                    clist * list, const gchar * real_path, gboolean all)
3976 {
3977         clistiter * iter;
3978         GSList * item_list;
3979         
3980         item_list = NULL;
3981         
3982         for(iter = clist_begin(list) ; iter != NULL ;
3983             iter = clist_next(iter)) {
3984                 struct mailimap_mailbox_list * mb;
3985                 int flags;
3986                 char delimiter;
3987                 char * name;
3988                 char * dup_name;
3989                 gchar * base;
3990                 gchar * loc_name;
3991                 gchar * loc_path;
3992                 FolderItem *new_item;
3993                 
3994                 mb = clist_content(iter);
3995
3996                 if (mb == NULL)
3997                         continue;
3998
3999                 flags = 0;
4000                 if (mb->mb_flag != NULL)
4001                         flags = imap_flags_to_flags(mb->mb_flag);
4002                 
4003                 delimiter = mb->mb_delimiter;
4004                 name = mb->mb_name;
4005                 
4006                 dup_name = strdup(name);                
4007                 if (delimiter != '\0')
4008                         subst_char(dup_name, delimiter, '/');
4009                 
4010                 base = g_path_get_basename(dup_name);
4011                 if (base[0] == '.') {
4012                         g_free(base);
4013                         free(dup_name);
4014                         continue;
4015                 }
4016                 
4017                 if (!all && strcmp(dup_name, real_path) == 0) {
4018                         g_free(base);
4019                         free(dup_name);
4020                         continue;
4021                 }
4022
4023                 if (!all && dup_name[strlen(dup_name)-1] == '/') {
4024                         g_free(base);
4025                         free(dup_name);
4026                         continue;
4027                 }
4028                 
4029                 loc_name = imap_modified_utf7_to_utf8(base);
4030                 loc_path = imap_modified_utf7_to_utf8(dup_name);
4031                 
4032                 new_item = folder_item_new(FOLDER(folder), loc_name, loc_path);
4033                 if ((flags & ETPAN_IMAP_MB_NOINFERIORS) != 0)
4034                         new_item->no_sub = TRUE;
4035                 if (strcmp(dup_name, "INBOX") != 0 &&
4036                     ((flags & ETPAN_IMAP_MB_NOSELECT) != 0))
4037                         new_item->no_select = TRUE;
4038                 
4039                 item_list = g_slist_append(item_list, new_item);
4040                 
4041                 debug_print("folder '%s' found.\n", loc_path);
4042                 g_free(base);
4043                 g_free(loc_path);
4044                 g_free(loc_name);
4045                 
4046                 free(dup_name);
4047         }
4048         
4049         return item_list;
4050 }
4051
4052 static GSList * imap_get_lep_set_from_numlist(MsgNumberList *numlist)
4053 {
4054         GSList *sorted_list, *cur;
4055         guint first, last, next;
4056         GSList *ret_list = NULL;
4057         unsigned int count;
4058         struct mailimap_set * current_set;
4059         unsigned int item_count;
4060         
4061         if (numlist == NULL)
4062                 return NULL;
4063         
4064         count = 0;
4065         current_set = mailimap_set_new_empty();
4066         
4067         sorted_list = g_slist_copy(numlist);
4068         sorted_list = g_slist_sort(sorted_list, g_int_compare);
4069
4070         first = GPOINTER_TO_INT(sorted_list->data);
4071         
4072         item_count = 0;
4073         for (cur = sorted_list; cur != NULL; cur = g_slist_next(cur)) {
4074                 if (GPOINTER_TO_INT(cur->data) == 0)
4075                         continue;
4076                 
4077                 item_count ++;
4078
4079                 last = GPOINTER_TO_INT(cur->data);
4080                 if (cur->next)
4081                         next = GPOINTER_TO_INT(cur->next->data);
4082                 else
4083                         next = 0;
4084
4085                 if (last + 1 != next || next == 0) {
4086
4087                         struct mailimap_set_item * item;
4088                         item = mailimap_set_item_new(first, last);
4089                         mailimap_set_add(current_set, item);
4090                         count ++;
4091                         
4092                         first = next;
4093                         
4094                         if (count >= IMAP_SET_MAX_COUNT) {
4095                                 ret_list = g_slist_append(ret_list,
4096                                                           current_set);
4097                                 current_set = mailimap_set_new_empty();
4098                                 count = 0;
4099                                 item_count = 0;
4100                         }
4101                 }
4102         }
4103         
4104         if (clist_count(current_set->set_list) > 0) {
4105                 ret_list = g_slist_append(ret_list,
4106                                           current_set);
4107         }
4108         
4109         g_slist_free(sorted_list);
4110
4111         return ret_list;
4112 }
4113
4114 static GSList * imap_get_lep_set_from_msglist(MsgInfoList *msglist)
4115 {
4116         MsgNumberList *numlist = NULL;
4117         MsgInfoList *cur;
4118         GSList *seq_list;
4119
4120         for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
4121                 MsgInfo *msginfo = (MsgInfo *) cur->data;
4122
4123                 numlist = g_slist_append(numlist, GINT_TO_POINTER(msginfo->msgnum));
4124         }
4125         seq_list = imap_get_lep_set_from_numlist(numlist);
4126         g_slist_free(numlist);
4127
4128         return seq_list;
4129 }
4130
4131 static GSList * imap_uid_list_from_lep(clist * list)
4132 {
4133         clistiter * iter;
4134         GSList * result;
4135         
4136         result = NULL;
4137         
4138         for(iter = clist_begin(list) ; iter != NULL ;
4139             iter = clist_next(iter)) {
4140                 uint32_t * puid;
4141                 
4142                 puid = clist_content(iter);
4143                 result = g_slist_append(result, GINT_TO_POINTER(* puid));
4144         }
4145         
4146         return result;
4147 }
4148
4149 static GSList * imap_uid_list_from_lep_tab(carray * list)
4150 {
4151         unsigned int i;
4152         GSList * result;
4153         
4154         result = NULL;
4155         
4156         for(i = 0 ; i < carray_count(list) ; i ++) {
4157                 uint32_t * puid;
4158                 
4159                 puid = carray_get(list, i);
4160                 result = g_slist_append(result, GINT_TO_POINTER(* puid));
4161         }
4162         
4163         return result;
4164 }
4165
4166 static MsgInfo *imap_envelope_from_lep(struct imap_fetch_env_info * info,
4167                                        FolderItem *item)
4168 {
4169         MsgInfo *msginfo = NULL;
4170         guint32 uid = 0;
4171         size_t size = 0;
4172         MsgFlags flags = {0, 0};
4173         
4174         if (info->headers == NULL)
4175                 return NULL;
4176
4177         MSG_SET_TMP_FLAGS(flags, MSG_IMAP);
4178         if (folder_has_parent_of_type(item, F_QUEUE)) {
4179                 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
4180         } else if (folder_has_parent_of_type(item, F_DRAFT)) {
4181                 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
4182         }
4183         flags.perm_flags = info->flags;
4184         
4185         uid = info->uid;
4186         size = info->size;
4187         msginfo = procheader_parse_str(info->headers, flags, FALSE, FALSE);
4188         
4189         if (msginfo) {
4190                 msginfo->msgnum = uid;
4191                 msginfo->size = size;
4192         }
4193
4194         return msginfo;
4195 }
4196
4197 static void imap_lep_set_free(GSList *seq_list)
4198 {
4199         GSList * cur;
4200         
4201         for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) {
4202                 struct mailimap_set * imapset;
4203                 
4204                 imapset = cur->data;
4205                 mailimap_set_free(imapset);
4206         }
4207         g_slist_free(seq_list);
4208 }
4209
4210 static struct mailimap_flag_list * imap_flag_to_lep(IMAPFlags flags)
4211 {
4212         struct mailimap_flag_list * flag_list;
4213         
4214         flag_list = mailimap_flag_list_new_empty();
4215         
4216         if (IMAP_IS_SEEN(flags))
4217                 mailimap_flag_list_add(flag_list,
4218                                        mailimap_flag_new_seen());
4219         if (IMAP_IS_ANSWERED(flags))
4220                 mailimap_flag_list_add(flag_list,
4221                                        mailimap_flag_new_answered());
4222         if (IMAP_IS_FLAGGED(flags))
4223                 mailimap_flag_list_add(flag_list,
4224                                        mailimap_flag_new_flagged());
4225         if (IMAP_IS_DELETED(flags))
4226                 mailimap_flag_list_add(flag_list,
4227                                        mailimap_flag_new_deleted());
4228         if (IMAP_IS_DRAFT(flags))
4229                 mailimap_flag_list_add(flag_list,
4230                                        mailimap_flag_new_draft());
4231         
4232         return flag_list;
4233 }
4234
4235 guint imap_folder_get_refcnt(Folder *folder)
4236 {
4237         return ((IMAPFolder *)folder)->refcnt;
4238 }
4239
4240 void imap_folder_ref(Folder *folder)
4241 {
4242         ((IMAPFolder *)folder)->refcnt++;
4243 }
4244
4245 void imap_folder_unref(Folder *folder)
4246 {
4247         if (((IMAPFolder *)folder)->refcnt > 0)
4248                 ((IMAPFolder *)folder)->refcnt--;
4249 }
4250
4251 #else /* HAVE_LIBETPAN */
4252
4253 static FolderClass imap_class;
4254
4255 static XMLTag *imap_item_get_xml(Folder *folder, FolderItem *item);
4256 static void imap_item_set_xml(Folder *folder, FolderItem *item, XMLTag *tag);
4257
4258 static Folder   *imap_folder_new        (const gchar    *name,
4259                                          const gchar    *path)
4260 {
4261         return NULL;
4262 }
4263 static gint     imap_create_tree        (Folder         *folder)
4264 {
4265         return -1;
4266 }
4267 static FolderItem *imap_create_folder   (Folder         *folder,
4268                                          FolderItem     *parent,
4269                                          const gchar    *name)
4270 {
4271         return NULL;
4272 }
4273 static gint     imap_rename_folder      (Folder         *folder,
4274                                          FolderItem     *item, 
4275                                          const gchar    *name)
4276 {
4277         return -1;
4278 }
4279
4280 gchar imap_get_path_separator_for_item(FolderItem *item)
4281 {
4282         return '/';
4283 }
4284
4285 FolderClass *imap_get_class(void)
4286 {
4287         if (imap_class.idstr == NULL) {
4288                 imap_class.type = F_IMAP;
4289                 imap_class.idstr = "imap";
4290                 imap_class.uistr = "IMAP4";
4291
4292                 imap_class.new_folder = imap_folder_new;
4293                 imap_class.create_tree = imap_create_tree;
4294                 imap_class.create_folder = imap_create_folder;
4295                 imap_class.rename_folder = imap_rename_folder;
4296
4297                 imap_class.set_xml = folder_set_xml;
4298                 imap_class.get_xml = folder_get_xml;
4299                 imap_class.item_set_xml = imap_item_set_xml;
4300                 imap_class.item_get_xml = imap_item_get_xml;
4301                 /* nothing implemented */
4302         }
4303
4304         return &imap_class;
4305 }
4306 #endif
4307
4308 void imap_synchronise(FolderItem *item) 
4309 {
4310         imap_gtk_synchronise(item);
4311 }
4312
4313 static void imap_item_set_xml(Folder *folder, FolderItem *item, XMLTag *tag)
4314 {
4315 #ifdef HAVE_LIBETPAN
4316         GList *cur;
4317 #endif
4318         folder_item_set_xml(folder, item, tag);
4319         
4320 #ifdef HAVE_LIBETPAN
4321         for (cur = tag->attr; cur != NULL; cur = g_list_next(cur)) {
4322                 XMLAttr *attr = (XMLAttr *) cur->data;
4323
4324                 if (!attr || !attr->name || !attr->value) continue;
4325                 if (!strcmp(attr->name, "uidnext"))
4326                         IMAP_FOLDER_ITEM(item)->uid_next = atoi(attr->value);
4327         }
4328 #endif
4329 }
4330
4331 static XMLTag *imap_item_get_xml(Folder *folder, FolderItem *item)
4332 {
4333         XMLTag *tag;
4334
4335         tag = folder_item_get_xml(folder, item);
4336
4337 #ifdef HAVE_LIBETPAN
4338         xml_tag_add_attr(tag, xml_attr_new_int("uidnext", 
4339                         IMAP_FOLDER_ITEM(item)->uid_next));
4340
4341 #endif
4342         return tag;
4343 }