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