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