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