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