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