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