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