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