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