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