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