2005-10-23 [wwp] 1.9.15cvs95
[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                         if (msginfo == NULL)
2031                                 continue;
2032                         msginfo->folder = item;
2033                         if (!newlist)
2034                                 llast = newlist = g_slist_append(newlist, msginfo);
2035                         else {
2036                                 llast = g_slist_append(llast, msginfo);
2037                                 llast = llast->next;
2038                         }
2039                         count ++;
2040                 }
2041                 
2042                 imap_fetch_env_free(env_list);
2043         }
2044         
2045         session_set_access_time(SESSION(session));
2046         stuff->done = TRUE;
2047         return newlist;
2048 }
2049
2050 #define MAX_MSG_NUM 50
2051
2052 static GSList *imap_get_uncached_messages(IMAPSession *session,
2053                                         FolderItem *item,
2054                                         MsgNumberList *numlist)
2055 {
2056         GSList *result = NULL;
2057         GSList * cur;
2058         uncached_data *data = g_new0(uncached_data, 1);
2059         int finished;
2060         
2061         finished = 0;
2062         cur = numlist;
2063         data->total = g_slist_length(numlist);
2064         debug_print("messages list : %i\n", data->total);
2065
2066         while (cur != NULL) {
2067                 GSList * partial_result;
2068                 int count;
2069                 GSList * newlist;
2070                 GSList * llast;
2071                 
2072                 llast = NULL;
2073                 count = 0;
2074                 newlist = NULL;
2075                 while (count < MAX_MSG_NUM) {
2076                         void * p;
2077                         
2078                         p = cur->data;
2079                         
2080                         if (newlist == NULL)
2081                                 llast = newlist = g_slist_append(newlist, p);
2082                         else {
2083                                 llast = g_slist_append(llast, p);
2084                                 llast = llast->next;
2085                         }
2086                         count ++;
2087                         
2088                         cur = cur->next;
2089                         if (cur == NULL)
2090                                 break;
2091                 }
2092                 
2093                 data->done = FALSE;
2094                 data->session = session;
2095                 data->item = item;
2096                 data->numlist = newlist;
2097                 data->cur += count;
2098                 
2099                 if (prefs_common.work_offline && !inc_offline_should_override()) {
2100                         g_free(data);
2101                         return NULL;
2102                 }
2103                 
2104                 partial_result =
2105                         (GSList *)imap_get_uncached_messages_thread(data);
2106                 
2107                 statusbar_progress_all(data->cur,data->total, 1);
2108                 
2109                 g_slist_free(newlist);
2110                 
2111                 result = g_slist_concat(result, partial_result);
2112         }
2113         g_free(data);
2114         
2115         statusbar_progress_all(0,0,0);
2116         statusbar_pop_all();
2117         
2118         return result;
2119 }
2120
2121 static void imap_delete_all_cached_messages(FolderItem *item)
2122 {
2123         gchar *dir;
2124
2125         g_return_if_fail(item != NULL);
2126         g_return_if_fail(item->folder != NULL);
2127         g_return_if_fail(FOLDER_CLASS(item->folder) == &imap_class);
2128
2129         debug_print("Deleting all cached messages...\n");
2130
2131         dir = folder_item_get_path(item);
2132         if (is_dir_exist(dir))
2133                 remove_all_numbered_files(dir);
2134         g_free(dir);
2135
2136         debug_print("done.\n");
2137 }
2138
2139 static IMAPNameSpace *imap_find_namespace_from_list(GList *ns_list,
2140                                                     const gchar *path)
2141 {
2142         IMAPNameSpace *namespace = NULL;
2143         gchar *tmp_path, *name;
2144
2145         if (!path) path = "";
2146
2147         for (; ns_list != NULL; ns_list = ns_list->next) {
2148                 IMAPNameSpace *tmp_ns = ns_list->data;
2149
2150                 Xstrcat_a(tmp_path, path, "/", return namespace);
2151                 Xstrdup_a(name, tmp_ns->name, return namespace);
2152                 if (tmp_ns->separator && tmp_ns->separator != '/') {
2153                         subst_char(tmp_path, tmp_ns->separator, '/');
2154                         subst_char(name, tmp_ns->separator, '/');
2155                 }
2156                 if (strncmp(tmp_path, name, strlen(name)) == 0)
2157                         namespace = tmp_ns;
2158         }
2159
2160         return namespace;
2161 }
2162
2163 static IMAPNameSpace *imap_find_namespace(IMAPFolder *folder,
2164                                           const gchar *path)
2165 {
2166         IMAPNameSpace *namespace;
2167
2168         g_return_val_if_fail(folder != NULL, NULL);
2169
2170         namespace = imap_find_namespace_from_list(folder->ns_personal, path);
2171         if (namespace) return namespace;
2172         namespace = imap_find_namespace_from_list(folder->ns_others, path);
2173         if (namespace) return namespace;
2174         namespace = imap_find_namespace_from_list(folder->ns_shared, path);
2175         if (namespace) return namespace;
2176
2177         return NULL;
2178 }
2179
2180
2181 static gchar imap_get_path_separator(IMAPFolder *folder, const gchar *path)
2182 {
2183         IMAPNameSpace *namespace;
2184         gchar separator = '/';
2185
2186         if (folder->last_seen_separator == 0) {
2187                 clist * lep_list;
2188                 int r = imap_threaded_list((Folder *)folder, "", "", &lep_list);
2189                 if (r != MAILIMAP_NO_ERROR) {
2190                         log_warning(_("LIST failed\n"));
2191                         return '/';
2192                 }
2193                 
2194                 if (clist_count(lep_list) > 0) {
2195                         clistiter * iter = clist_begin(lep_list); 
2196                         struct mailimap_mailbox_list * mb;
2197                         mb = clist_content(iter);
2198                 
2199                         folder->last_seen_separator = mb->mb_delimiter;
2200                         debug_print("got separator: %c\n", folder->last_seen_separator);
2201                 }
2202                 mailimap_list_result_free(lep_list);
2203         }
2204
2205         if (folder->last_seen_separator != 0) {
2206                 debug_print("using separator: %c\n", folder->last_seen_separator);
2207                 return folder->last_seen_separator;
2208         }
2209
2210         namespace = imap_find_namespace(folder, path);
2211         if (namespace && namespace->separator)
2212                 separator = namespace->separator;
2213
2214         return separator;
2215 }
2216
2217 static gchar *imap_get_real_path(IMAPFolder *folder, const gchar *path)
2218 {
2219         gchar *real_path;
2220         gchar separator;
2221
2222         g_return_val_if_fail(folder != NULL, NULL);
2223         g_return_val_if_fail(path != NULL, NULL);
2224
2225         real_path = imap_utf8_to_modified_utf7(path);
2226         separator = imap_get_path_separator(folder, path);
2227         imap_path_separator_subst(real_path, separator);
2228
2229         return real_path;
2230 }
2231
2232 static gint imap_set_message_flags(IMAPSession *session,
2233                                    MsgNumberList *numlist,
2234                                    IMAPFlags flags,
2235                                    gboolean is_set)
2236 {
2237         gint ok = 0;
2238         GSList *seq_list;
2239         GSList * cur;
2240
2241         seq_list = imap_get_lep_set_from_numlist(numlist);
2242         
2243         for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) {
2244                 struct mailimap_set * imapset;
2245                 
2246                 imapset = cur->data;
2247                 
2248                 ok = imap_cmd_store(session, imapset,
2249                                     flags, is_set);
2250         }
2251         
2252         imap_lep_set_free(seq_list);
2253         
2254         return IMAP_SUCCESS;
2255 }
2256
2257 typedef struct _select_data {
2258         IMAPSession *session;
2259         gchar *real_path;
2260         gint *exists;
2261         gint *recent;
2262         gint *unseen;
2263         guint32 *uid_validity;
2264         gboolean done;
2265 } select_data;
2266
2267 static gint imap_select(IMAPSession *session, IMAPFolder *folder,
2268                         const gchar *path,
2269                         gint *exists, gint *recent, gint *unseen,
2270                         guint32 *uid_validity, gboolean block)
2271 {
2272         gchar *real_path;
2273         gint ok;
2274         gint exists_, recent_, unseen_;
2275         guint32 uid_validity_;
2276         
2277         if (!exists && !recent && !unseen && !uid_validity) {
2278                 if (session->mbox && strcmp(session->mbox, path) == 0)
2279                         return IMAP_SUCCESS;
2280         }
2281         if (!exists)
2282                 exists = &exists_;
2283         if (!recent)
2284                 recent = &recent_;
2285         if (!unseen)
2286                 unseen = &unseen_;
2287         if (!uid_validity)
2288                 uid_validity = &uid_validity_;
2289
2290         g_free(session->mbox);
2291         session->mbox = NULL;
2292
2293         real_path = imap_get_real_path(folder, path);
2294
2295         ok = imap_cmd_select(session, real_path,
2296                              exists, recent, unseen, uid_validity, block);
2297         if (ok != IMAP_SUCCESS)
2298                 log_warning(_("can't select folder: %s\n"), real_path);
2299         else {
2300                 session->mbox = g_strdup(path);
2301                 session->folder_content_changed = FALSE;
2302         }
2303         g_free(real_path);
2304
2305         return ok;
2306 }
2307
2308 static gint imap_status(IMAPSession *session, IMAPFolder *folder,
2309                         const gchar *path, IMAPFolderItem *item,
2310                         gint *messages,
2311                         guint32 *uid_next, guint32 *uid_validity,
2312                         gint *unseen, gboolean block)
2313 {
2314         int r;
2315         clistiter * iter;
2316         struct mailimap_mailbox_data_status * data_status;
2317         int got_values;
2318         gchar *real_path;
2319         guint mask = 0;
2320         
2321         real_path = imap_get_real_path(folder, path);
2322
2323 #if 0
2324         if (time(NULL) - item->last_update >= 5 && item->last_update != 1) {
2325                 /* do the full stuff */
2326                 item->last_update = 1; /* force update */
2327                 debug_print("updating everything\n");
2328                 r = imap_status(session, folder, path, item,
2329                 &item->c_messages, &item->c_uid_next,
2330                 &item->c_uid_validity, &item->c_unseen, block);
2331                 if (r != MAILIMAP_NO_ERROR) {
2332                         debug_print("status err %d\n", r);
2333                         return IMAP_ERROR;
2334                 }
2335                 item->last_update = time(NULL);
2336                 if (messages) 
2337                         *messages = item->c_messages;
2338                 if (uid_next)
2339                         *uid_next = item->c_uid_next;
2340                 if (uid_validity)
2341                         *uid_validity = item->c_uid_validity;
2342                 if (unseen)
2343                         *unseen = item->c_unseen;
2344                 return 0;
2345         } else if (time(NULL) - item->last_update < 5) {
2346                 /* return cached stuff */
2347                 debug_print("using cache\n");
2348                 if (messages) 
2349                         *messages = item->c_messages;
2350                 if (uid_next)
2351                         *uid_next = item->c_uid_next;
2352                 if (uid_validity)
2353                         *uid_validity = item->c_uid_validity;
2354                 if (unseen)
2355                         *unseen = item->c_unseen;
2356                 return 0;
2357         }
2358 #endif
2359
2360         /* if we get there, we're updating cache */
2361
2362         if (messages) {
2363                 mask |= 1 << 0;
2364         }
2365         if (uid_next) {
2366                 mask |= 1 << 2;
2367         }
2368         if (uid_validity) {
2369                 mask |= 1 << 3;
2370         }
2371         if (unseen) {
2372                 mask |= 1 << 4;
2373         }
2374         r = imap_threaded_status(FOLDER(folder), real_path, 
2375                 &data_status, mask);
2376
2377         g_free(real_path);
2378         if (r != MAILIMAP_NO_ERROR) {
2379                 debug_print("status err %d\n", r);
2380                 return IMAP_ERROR;
2381         }
2382         
2383         if (data_status->st_info_list == NULL) {
2384                 mailimap_mailbox_data_status_free(data_status);
2385                 debug_print("status->st_info_list == NULL\n");
2386                 return IMAP_ERROR;
2387         }
2388         
2389         got_values = 0;
2390         for(iter = clist_begin(data_status->st_info_list) ; iter != NULL ;
2391             iter = clist_next(iter)) {
2392                 struct mailimap_status_info * info;             
2393                 
2394                 info = clist_content(iter);
2395                 switch (info->st_att) {
2396                 case MAILIMAP_STATUS_ATT_MESSAGES:
2397                         * messages = info->st_value;
2398                         got_values |= 1 << 0;
2399                         break;
2400                         
2401                 case MAILIMAP_STATUS_ATT_UIDNEXT:
2402                         * uid_next = info->st_value;
2403                         got_values |= 1 << 2;
2404                         break;
2405                         
2406                 case MAILIMAP_STATUS_ATT_UIDVALIDITY:
2407                         * uid_validity = info->st_value;
2408                         got_values |= 1 << 3;
2409                         break;
2410                         
2411                 case MAILIMAP_STATUS_ATT_UNSEEN:
2412                         * unseen = info->st_value;
2413                         got_values |= 1 << 4;
2414                         break;
2415                 }
2416         }
2417         mailimap_mailbox_data_status_free(data_status);
2418         
2419         if (got_values != mask) {
2420                 debug_print("status: incomplete values received (%d)\n", got_values);
2421                 return IMAP_ERROR;
2422         }
2423         return IMAP_SUCCESS;
2424 }
2425
2426 static void imap_free_capabilities(IMAPSession *session)
2427 {
2428         slist_free_strings(session->capability);
2429         g_slist_free(session->capability);
2430         session->capability = NULL;
2431 }
2432
2433 /* low-level IMAP4rev1 commands */
2434
2435 #if 0
2436 static gint imap_cmd_authenticate(IMAPSession *session, const gchar *user,
2437                                   const gchar *pass, IMAPAuthType type)
2438 {
2439         gchar *auth_type;
2440         gint ok;
2441         gchar *buf = NULL;
2442         gchar *challenge;
2443         gint challenge_len;
2444         gchar hexdigest[33];
2445         gchar *response;
2446         gchar *response64;
2447
2448         auth_type = "CRAM-MD5";
2449
2450         imap_gen_send(session, "AUTHENTICATE %s", auth_type);
2451         ok = imap_gen_recv(session, &buf);
2452         if (ok != IMAP_SUCCESS || buf[0] != '+' || buf[1] != ' ') {
2453                 g_free(buf);
2454                 return IMAP_ERROR;
2455         }
2456
2457         challenge = g_malloc(strlen(buf + 2) + 1);
2458         challenge_len = base64_decode(challenge, buf + 2, -1);
2459         challenge[challenge_len] = '\0';
2460         g_free(buf);
2461
2462         md5_hex_hmac(hexdigest, challenge, challenge_len, pass, strlen(pass));
2463         g_free(challenge);
2464
2465         response = g_strdup_printf("%s %s", user, hexdigest);
2466         response64 = g_malloc((strlen(response) + 3) * 2 + 1);
2467         base64_encode(response64, response, strlen(response));
2468         g_free(response);
2469
2470         sock_puts(SESSION(session)->sock, response64);
2471         ok = imap_cmd_ok(session, NULL);
2472         if (ok != IMAP_SUCCESS)
2473                 log_warning(_("IMAP4 authentication failed.\n"));
2474
2475         return ok;
2476 }
2477 #endif
2478
2479 static gint imap_cmd_login(IMAPSession *session,
2480                            const gchar *user, const gchar *pass,
2481                            const gchar *type)
2482 {
2483         int r;
2484         gint ok;
2485
2486         log_print("IMAP4> Logging %s to %s using %s\n", 
2487                         user,
2488                         SESSION(session)->server,
2489                         type);
2490         r = imap_threaded_login(session->folder, user, pass, type);
2491         if (r != MAILIMAP_NO_ERROR) {
2492                 log_error("IMAP4< Error logging in to %s\n",
2493                                 SESSION(session)->server);
2494                 ok = IMAP_ERROR;
2495         } else {
2496                 ok = IMAP_SUCCESS;
2497         }
2498         return ok;
2499 }
2500
2501 static gint imap_cmd_logout(IMAPSession *session)
2502 {
2503         imap_threaded_disconnect(session->folder);
2504
2505         return IMAP_SUCCESS;
2506 }
2507
2508 static gint imap_cmd_noop(IMAPSession *session)
2509 {
2510         int r;
2511         unsigned int exists;
2512         
2513         r = imap_threaded_noop(session->folder, &exists);
2514         if (r != MAILIMAP_NO_ERROR) {
2515                 debug_print("noop err %d\n", r);
2516                 return IMAP_ERROR;
2517         }
2518         session->exists = exists;
2519         session_set_access_time(SESSION(session));
2520
2521         return IMAP_SUCCESS;
2522 }
2523
2524 #if USE_OPENSSL
2525 static gint imap_cmd_starttls(IMAPSession *session)
2526 {
2527         int r;
2528         
2529         r = imap_threaded_starttls(session->folder);
2530         if (r != MAILIMAP_NO_ERROR) {
2531                 debug_print("starttls err %d\n", r);
2532                 return IMAP_ERROR;
2533         }
2534         return IMAP_SUCCESS;
2535 }
2536 #endif
2537
2538 static gint imap_cmd_select(IMAPSession *session, const gchar *folder,
2539                             gint *exists, gint *recent, gint *unseen,
2540                             guint32 *uid_validity, gboolean block)
2541 {
2542         int r;
2543
2544         r = imap_threaded_select(session->folder, folder,
2545                                  exists, recent, unseen, uid_validity);
2546         if (r != MAILIMAP_NO_ERROR) {
2547                 debug_print("select err %d\n", r);
2548                 return IMAP_ERROR;
2549         }
2550         return IMAP_SUCCESS;
2551 }
2552
2553 static gint imap_cmd_examine(IMAPSession *session, const gchar *folder,
2554                              gint *exists, gint *recent, gint *unseen,
2555                              guint32 *uid_validity, gboolean block)
2556 {
2557         int r;
2558
2559         r = imap_threaded_examine(session->folder, folder,
2560                                   exists, recent, unseen, uid_validity);
2561         if (r != MAILIMAP_NO_ERROR) {
2562                 debug_print("examine err %d\n", r);
2563                 
2564                 return IMAP_ERROR;
2565         }
2566         return IMAP_SUCCESS;
2567 }
2568
2569 static gint imap_cmd_create(IMAPSession *session, const gchar *folder)
2570 {
2571         int r;
2572
2573         r = imap_threaded_create(session->folder, folder);
2574         if (r != MAILIMAP_NO_ERROR) {
2575                 
2576                 return IMAP_ERROR;
2577         }
2578
2579         return IMAP_SUCCESS;
2580 }
2581
2582 static gint imap_cmd_rename(IMAPSession *session, const gchar *old_folder,
2583                             const gchar *new_folder)
2584 {
2585         int r;
2586
2587         r = imap_threaded_rename(session->folder, old_folder,
2588                                  new_folder);
2589         if (r != MAILIMAP_NO_ERROR) {
2590                 
2591                 return IMAP_ERROR;
2592         }
2593
2594         return IMAP_SUCCESS;
2595 }
2596
2597 static gint imap_cmd_delete(IMAPSession *session, const gchar *folder)
2598 {
2599         int r;
2600         
2601
2602         r = imap_threaded_delete(session->folder, folder);
2603         if (r != MAILIMAP_NO_ERROR) {
2604                 
2605                 return IMAP_ERROR;
2606         }
2607
2608         return IMAP_SUCCESS;
2609 }
2610
2611 typedef struct _fetch_data {
2612         IMAPSession *session;
2613         guint32 uid;
2614         const gchar *filename;
2615         gboolean headers;
2616         gboolean body;
2617         gboolean done;
2618 } fetch_data;
2619
2620 static void *imap_cmd_fetch_thread(void *data)
2621 {
2622         fetch_data *stuff = (fetch_data *)data;
2623         IMAPSession *session = stuff->session;
2624         guint32 uid = stuff->uid;
2625         const gchar *filename = stuff->filename;
2626         int r;
2627         
2628         if (stuff->body) {
2629                 r = imap_threaded_fetch_content(session->folder,
2630                                                uid, 1, filename);
2631         }
2632         else {
2633                 r = imap_threaded_fetch_content(session->folder,
2634                                                 uid, 0, filename);
2635         }
2636         if (r != MAILIMAP_NO_ERROR) {
2637                 debug_print("fetch err %d\n", r);
2638                 return GINT_TO_POINTER(IMAP_ERROR);
2639         }
2640         return GINT_TO_POINTER(IMAP_SUCCESS);
2641 }
2642
2643 static gint imap_cmd_fetch(IMAPSession *session, guint32 uid,
2644                                 const gchar *filename, gboolean headers,
2645                                 gboolean body)
2646 {
2647         fetch_data *data = g_new0(fetch_data, 1);
2648         int result = 0;
2649         data->done = FALSE;
2650         data->session = session;
2651         data->uid = uid;
2652         data->filename = filename;
2653         data->headers = headers;
2654         data->body = body;
2655
2656         if (prefs_common.work_offline && !inc_offline_should_override()) {
2657                 g_free(data);
2658                 return -1;
2659         }
2660         statusbar_print_all(_("Fetching message..."));
2661         result = GPOINTER_TO_INT(imap_cmd_fetch_thread(data));
2662         statusbar_pop_all();
2663         g_free(data);
2664         return result;
2665 }
2666
2667
2668 static gint imap_cmd_append(IMAPSession *session, const gchar *destfolder,
2669                             const gchar *file, IMAPFlags flags, 
2670                             guint32 *new_uid)
2671 {
2672         struct mailimap_flag_list * flag_list;
2673         int r;
2674         
2675         g_return_val_if_fail(file != NULL, IMAP_ERROR);
2676
2677         flag_list = imap_flag_to_lep(flags);
2678         r = imap_threaded_append(session->folder, destfolder,
2679                          file, flag_list);
2680         if (new_uid != NULL)
2681                 *new_uid = 0;
2682
2683         if (r != MAILIMAP_NO_ERROR) {
2684                 debug_print("append err %d\n", r);
2685                 return IMAP_ERROR;
2686         }
2687         return IMAP_SUCCESS;
2688 }
2689
2690 static gint imap_cmd_copy(IMAPSession *session, struct mailimap_set * set,
2691                           const gchar *destfolder, GRelation *uid_mapping)
2692 {
2693         int r;
2694         
2695         g_return_val_if_fail(session != NULL, IMAP_ERROR);
2696         g_return_val_if_fail(set != NULL, IMAP_ERROR);
2697         g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
2698
2699         r = imap_threaded_copy(session->folder, set, destfolder);
2700         if (r != MAILIMAP_NO_ERROR) {
2701                 
2702                 return IMAP_ERROR;
2703         }
2704
2705         return IMAP_SUCCESS;
2706 }
2707
2708 static gint imap_cmd_store(IMAPSession *session, struct mailimap_set * set,
2709                            IMAPFlags flags, int do_add)
2710 {
2711         int r;
2712         struct mailimap_flag_list * flag_list;
2713         struct mailimap_store_att_flags * store_att_flags;
2714         
2715         flag_list = imap_flag_to_lep(flags);
2716         
2717         if (do_add)
2718                 store_att_flags =
2719                         mailimap_store_att_flags_new_add_flags_silent(flag_list);
2720         else
2721                 store_att_flags =
2722                         mailimap_store_att_flags_new_remove_flags_silent(flag_list);
2723         
2724         r = imap_threaded_store(session->folder, set, store_att_flags);
2725         if (r != MAILIMAP_NO_ERROR) {
2726                 
2727                 return IMAP_ERROR;
2728         }
2729         
2730         return IMAP_SUCCESS;
2731 }
2732
2733 static gint imap_cmd_expunge(IMAPSession *session)
2734 {
2735         int r;
2736         
2737         if (prefs_common.work_offline && !inc_offline_should_override()) {
2738                 return -1;
2739         }
2740
2741         r = imap_threaded_expunge(session->folder);
2742         if (r != MAILIMAP_NO_ERROR) {
2743                 
2744                 return IMAP_ERROR;
2745         }
2746
2747         return IMAP_SUCCESS;
2748 }
2749
2750 static void imap_path_separator_subst(gchar *str, gchar separator)
2751 {
2752         gchar *p;
2753         gboolean in_escape = FALSE;
2754
2755         if (!separator || separator == '/') return;
2756
2757         for (p = str; *p != '\0'; p++) {
2758                 if (*p == '/' && !in_escape)
2759                         *p = separator;
2760                 else if (*p == '&' && *(p + 1) != '-' && !in_escape)
2761                         in_escape = TRUE;
2762                 else if (*p == '-' && in_escape)
2763                         in_escape = FALSE;
2764         }
2765 }
2766
2767 static gchar *imap_modified_utf7_to_utf8(const gchar *mutf7_str)
2768 {
2769         static iconv_t cd = (iconv_t)-1;
2770         static gboolean iconv_ok = TRUE;
2771         GString *norm_utf7;
2772         gchar *norm_utf7_p;
2773         size_t norm_utf7_len;
2774         const gchar *p;
2775         gchar *to_str, *to_p;
2776         size_t to_len;
2777         gboolean in_escape = FALSE;
2778
2779         if (!iconv_ok) return g_strdup(mutf7_str);
2780
2781         if (cd == (iconv_t)-1) {
2782                 cd = iconv_open(CS_INTERNAL, CS_UTF_7);
2783                 if (cd == (iconv_t)-1) {
2784                         g_warning("iconv cannot convert UTF-7 to %s\n",
2785                                   CS_INTERNAL);
2786                         iconv_ok = FALSE;
2787                         return g_strdup(mutf7_str);
2788                 }
2789         }
2790
2791         /* modified UTF-7 to normal UTF-7 conversion */
2792         norm_utf7 = g_string_new(NULL);
2793
2794         for (p = mutf7_str; *p != '\0'; p++) {
2795                 /* replace: '&'  -> '+',
2796                             "&-" -> '&',
2797                             escaped ','  -> '/' */
2798                 if (!in_escape && *p == '&') {
2799                         if (*(p + 1) != '-') {
2800                                 g_string_append_c(norm_utf7, '+');
2801                                 in_escape = TRUE;
2802                         } else {
2803                                 g_string_append_c(norm_utf7, '&');
2804                                 p++;
2805                         }
2806                 } else if (in_escape && *p == ',') {
2807                         g_string_append_c(norm_utf7, '/');
2808                 } else if (in_escape && *p == '-') {
2809                         g_string_append_c(norm_utf7, '-');
2810                         in_escape = FALSE;
2811                 } else {
2812                         g_string_append_c(norm_utf7, *p);
2813                 }
2814         }
2815
2816         norm_utf7_p = norm_utf7->str;
2817         norm_utf7_len = norm_utf7->len;
2818         to_len = strlen(mutf7_str) * 5;
2819         to_p = to_str = g_malloc(to_len + 1);
2820
2821         if (iconv(cd, (ICONV_CONST gchar **)&norm_utf7_p, &norm_utf7_len,
2822                   &to_p, &to_len) == -1) {
2823                 g_warning(_("iconv cannot convert UTF-7 to %s\n"),
2824                           conv_get_locale_charset_str());
2825                 g_string_free(norm_utf7, TRUE);
2826                 g_free(to_str);
2827                 return g_strdup(mutf7_str);
2828         }
2829
2830         /* second iconv() call for flushing */
2831         iconv(cd, NULL, NULL, &to_p, &to_len);
2832         g_string_free(norm_utf7, TRUE);
2833         *to_p = '\0';
2834
2835         return to_str;
2836 }
2837
2838 static gchar *imap_utf8_to_modified_utf7(const gchar *from)
2839 {
2840         static iconv_t cd = (iconv_t)-1;
2841         static gboolean iconv_ok = TRUE;
2842         gchar *norm_utf7, *norm_utf7_p;
2843         size_t from_len, norm_utf7_len;
2844         GString *to_str;
2845         gchar *from_tmp, *to, *p;
2846         gboolean in_escape = FALSE;
2847
2848         if (!iconv_ok) return g_strdup(from);
2849
2850         if (cd == (iconv_t)-1) {
2851                 cd = iconv_open(CS_UTF_7, CS_INTERNAL);
2852                 if (cd == (iconv_t)-1) {
2853                         g_warning(_("iconv cannot convert %s to UTF-7\n"),
2854                                   CS_INTERNAL);
2855                         iconv_ok = FALSE;
2856                         return g_strdup(from);
2857                 }
2858         }
2859
2860         /* UTF-8 to normal UTF-7 conversion */
2861         Xstrdup_a(from_tmp, from, return g_strdup(from));
2862         from_len = strlen(from);
2863         norm_utf7_len = from_len * 5;
2864         Xalloca(norm_utf7, norm_utf7_len + 1, return g_strdup(from));
2865         norm_utf7_p = norm_utf7;
2866
2867 #define IS_PRINT(ch) (isprint(ch) && IS_ASCII(ch))
2868
2869         while (from_len > 0) {
2870                 if (*from_tmp == '+') {
2871                         *norm_utf7_p++ = '+';
2872                         *norm_utf7_p++ = '-';
2873                         norm_utf7_len -= 2;
2874                         from_tmp++;
2875                         from_len--;
2876                 } else if (IS_PRINT(*(guchar *)from_tmp)) {
2877                         /* printable ascii char */
2878                         *norm_utf7_p = *from_tmp;
2879                         norm_utf7_p++;
2880                         norm_utf7_len--;
2881                         from_tmp++;
2882                         from_len--;
2883                 } else {
2884                         size_t conv_len = 0;
2885
2886                         /* unprintable char: convert to UTF-7 */
2887                         p = from_tmp;
2888                         while (!IS_PRINT(*(guchar *)p) && conv_len < from_len) {
2889                                 conv_len += g_utf8_skip[*(guchar *)p];
2890                                 p += g_utf8_skip[*(guchar *)p];
2891                         }
2892
2893                         from_len -= conv_len;
2894                         if (iconv(cd, (ICONV_CONST gchar **)&from_tmp,
2895                                   &conv_len,
2896                                   &norm_utf7_p, &norm_utf7_len) == -1) {
2897                                 g_warning(_("iconv cannot convert UTF-8 to UTF-7\n"));
2898                                 return g_strdup(from);
2899                         }
2900
2901                         /* second iconv() call for flushing */
2902                         iconv(cd, NULL, NULL, &norm_utf7_p, &norm_utf7_len);
2903                 }
2904         }
2905
2906 #undef IS_PRINT
2907
2908         *norm_utf7_p = '\0';
2909         to_str = g_string_new(NULL);
2910         for (p = norm_utf7; p < norm_utf7_p; p++) {
2911                 /* replace: '&' -> "&-",
2912                             '+' -> '&',
2913                             "+-" -> '+',
2914                             BASE64 '/' -> ',' */
2915                 if (!in_escape && *p == '&') {
2916                         g_string_append(to_str, "&-");
2917                 } else if (!in_escape && *p == '+') {
2918                         if (*(p + 1) == '-') {
2919                                 g_string_append_c(to_str, '+');
2920                                 p++;
2921                         } else {
2922                                 g_string_append_c(to_str, '&');
2923                                 in_escape = TRUE;
2924                         }
2925                 } else if (in_escape && *p == '/') {
2926                         g_string_append_c(to_str, ',');
2927                 } else if (in_escape && *p == '-') {
2928                         g_string_append_c(to_str, '-');
2929                         in_escape = FALSE;
2930                 } else {
2931                         g_string_append_c(to_str, *p);
2932                 }
2933         }
2934
2935         if (in_escape) {
2936                 in_escape = FALSE;
2937                 g_string_append_c(to_str, '-');
2938         }
2939
2940         to = to_str->str;
2941         g_string_free(to_str, FALSE);
2942
2943         return to;
2944 }
2945
2946 static gboolean imap_rename_folder_func(GNode *node, gpointer data)
2947 {
2948         FolderItem *item = node->data;
2949         gchar **paths = data;
2950         const gchar *oldpath = paths[0];
2951         const gchar *newpath = paths[1];
2952         gchar *base;
2953         gchar *new_itempath;
2954         gint oldpathlen;
2955
2956         oldpathlen = strlen(oldpath);
2957         if (strncmp(oldpath, item->path, oldpathlen) != 0) {
2958                 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
2959                 return TRUE;
2960         }
2961
2962         base = item->path + oldpathlen;
2963         while (*base == G_DIR_SEPARATOR) base++;
2964         if (*base == '\0')
2965                 new_itempath = g_strdup(newpath);
2966         else
2967                 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
2968                                            NULL);
2969         g_free(item->path);
2970         item->path = new_itempath;
2971
2972         return FALSE;
2973 }
2974
2975 typedef struct _get_list_uid_data {
2976         Folder *folder;
2977         IMAPSession *session;
2978         IMAPFolderItem *item;
2979         GSList **msgnum_list;
2980         gboolean done;
2981 } get_list_uid_data;
2982
2983 static void *get_list_of_uids_thread(void *data)
2984 {
2985         get_list_uid_data *stuff = (get_list_uid_data *)data;
2986         Folder *folder = stuff->folder;
2987         IMAPFolderItem *item = stuff->item;
2988         GSList **msgnum_list = stuff->msgnum_list;
2989         gint ok, nummsgs = 0, lastuid_old;
2990         IMAPSession *session;
2991         GSList *uidlist, *elem;
2992         struct mailimap_set * set;
2993         clist * lep_uidlist;
2994         int r;
2995
2996         session = stuff->session;
2997         if (session == NULL) {
2998                 stuff->done = TRUE;
2999                 return GINT_TO_POINTER(-1);
3000         }
3001         /* no session locking here, it's already locked by caller */
3002         ok = imap_select(session, IMAP_FOLDER(folder), item->item.path,
3003                          NULL, NULL, NULL, NULL, TRUE);
3004         if (ok != IMAP_SUCCESS) {
3005                 stuff->done = TRUE;
3006                 return GINT_TO_POINTER(-1);
3007         }
3008
3009         uidlist = NULL;
3010         
3011         set = mailimap_set_new_interval(item->lastuid + 1, 0);
3012
3013         r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SIMPLE, set,
3014                                  &lep_uidlist);
3015         if (r == MAILIMAP_NO_ERROR) {
3016                 GSList * fetchuid_list;
3017                 
3018                 fetchuid_list =
3019                         imap_uid_list_from_lep(lep_uidlist);
3020                 uidlist = g_slist_concat(fetchuid_list, uidlist);
3021         }
3022         else {
3023                 GSList * fetchuid_list;
3024                 carray * lep_uidtab;
3025                 
3026                 r = imap_threaded_fetch_uid(folder, item->lastuid + 1,
3027                                             &lep_uidtab);
3028                 if (r == MAILIMAP_NO_ERROR) {
3029                         fetchuid_list =
3030                                 imap_uid_list_from_lep_tab(lep_uidtab);
3031                         uidlist = g_slist_concat(fetchuid_list, uidlist);
3032                 }
3033         }
3034         
3035         lastuid_old = item->lastuid;
3036         *msgnum_list = g_slist_copy(item->uid_list);
3037         nummsgs = g_slist_length(*msgnum_list);
3038         debug_print("Got %d uids from cache\n", g_slist_length(item->uid_list));
3039
3040         for (elem = uidlist; elem != NULL; elem = g_slist_next(elem)) {
3041                 guint msgnum;
3042
3043                 msgnum = GPOINTER_TO_INT(elem->data);
3044                 if (msgnum > lastuid_old) {
3045                         *msgnum_list = g_slist_prepend(*msgnum_list, GINT_TO_POINTER(msgnum));
3046                         item->uid_list = g_slist_prepend(item->uid_list, GINT_TO_POINTER(msgnum));
3047                         nummsgs++;
3048
3049                         if(msgnum > item->lastuid)
3050                                 item->lastuid = msgnum;
3051                 }
3052         }
3053         g_slist_free(uidlist);
3054         stuff->done = TRUE;
3055         return GINT_TO_POINTER(nummsgs);
3056 }
3057
3058 static gint get_list_of_uids(IMAPSession *session, Folder *folder, IMAPFolderItem *item, GSList **msgnum_list)
3059 {
3060         gint result;
3061         get_list_uid_data *data = g_new0(get_list_uid_data, 1);
3062         data->done = FALSE;
3063         data->folder = folder;
3064         data->item = item;
3065         data->msgnum_list = msgnum_list;
3066         data->session = session;
3067         if (prefs_common.work_offline && !inc_offline_should_override()) {
3068                 g_free(data);
3069                 return -1;
3070         }
3071
3072         result = GPOINTER_TO_INT(get_list_of_uids_thread(data));
3073         g_free(data);
3074         return result;
3075
3076 }
3077
3078 gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list, gboolean *old_uids_valid)
3079 {
3080         IMAPFolderItem *item = (IMAPFolderItem *)_item;
3081         IMAPSession *session;
3082         gint ok, nummsgs = 0, exists, uid_val, uid_next;
3083         GSList *uidlist = NULL;
3084         gchar *dir;
3085         gboolean selected_folder;
3086         
3087         debug_print("get_num_list\n");
3088         
3089         g_return_val_if_fail(folder != NULL, -1);
3090         g_return_val_if_fail(item != NULL, -1);
3091         g_return_val_if_fail(item->item.path != NULL, -1);
3092         g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3093         g_return_val_if_fail(folder->account != NULL, -1);
3094
3095         session = imap_session_get(folder);
3096         g_return_val_if_fail(session != NULL, -1);
3097         lock_session();
3098         statusbar_print_all("Scanning %s...\n", FOLDER_ITEM(item)->path 
3099                                 ? FOLDER_ITEM(item)->path:"");
3100
3101         selected_folder = (session->mbox != NULL) &&
3102                           (!strcmp(session->mbox, item->item.path));
3103         if (selected_folder) {
3104                 ok = imap_cmd_noop(session);
3105                 if (ok != IMAP_SUCCESS) {
3106                         debug_print("disconnected!\n");
3107                         session = imap_reconnect_if_possible(folder, session);
3108                         if (session == NULL) {
3109                                 statusbar_pop_all();
3110                                 unlock_session();
3111                                 return -1;
3112                         }
3113                 }
3114                 exists = session->exists;
3115
3116                 *old_uids_valid = TRUE;
3117         } else {
3118                 if (item->use_cache && time(NULL) - item->use_cache < 2) {
3119                         exists = item->c_messages;
3120                         uid_next = item->c_uid_next;
3121                         uid_val = item->c_uid_validity;
3122                         ok = IMAP_SUCCESS;
3123                         debug_print("using cache %d %d %d\n", exists, uid_next, uid_val);
3124                 } else {
3125                         ok = imap_status(session, IMAP_FOLDER(folder), item->item.path, item,
3126                                  &exists, &uid_next, &uid_val, NULL, FALSE);
3127                 }
3128                 item->use_cache = (time_t)0;
3129                 if (ok != IMAP_SUCCESS) {
3130                         statusbar_pop_all();
3131                         unlock_session();
3132                         return -1;
3133                 }
3134                 if(item->item.mtime == uid_val)
3135                         *old_uids_valid = TRUE;
3136                 else {
3137                         *old_uids_valid = FALSE;
3138
3139                         debug_print("Freeing imap uid cache\n");
3140                         item->lastuid = 0;
3141                         g_slist_free(item->uid_list);
3142                         item->uid_list = NULL;
3143                 
3144                         item->item.mtime = uid_val;
3145
3146                         imap_delete_all_cached_messages((FolderItem *)item);
3147                 }
3148         }
3149
3150         if (!selected_folder)
3151                 item->uid_next = uid_next;
3152
3153         /* If old uid_next matches new uid_next we can be sure no message
3154            was added to the folder */
3155         if (( selected_folder && !session->folder_content_changed) ||
3156             (!selected_folder && uid_next == item->uid_next)) {
3157                 nummsgs = g_slist_length(item->uid_list);
3158
3159                 /* If number of messages is still the same we
3160                    know our caches message numbers are still valid,
3161                    otherwise if the number of messages has decrease
3162                    we discard our cache to start a new scan to find
3163                    out which numbers have been removed */
3164                 if (exists == nummsgs) {
3165                         *msgnum_list = g_slist_copy(item->uid_list);
3166                         statusbar_pop_all();
3167                         unlock_session();
3168                         return nummsgs;
3169                 } else if (exists < nummsgs) {
3170                         debug_print("Freeing imap uid cache");
3171                         item->lastuid = 0;
3172                         g_slist_free(item->uid_list);
3173                         item->uid_list = NULL;
3174                 }
3175         }
3176
3177         if (exists == 0) {
3178                 *msgnum_list = NULL;
3179                 statusbar_pop_all();
3180                 unlock_session();
3181                 return 0;
3182         }
3183
3184         nummsgs = get_list_of_uids(session, folder, item, &uidlist);
3185
3186         if (nummsgs < 0) {
3187                 statusbar_pop_all();
3188                 unlock_session();
3189                 return -1;
3190         }
3191
3192         if (nummsgs != exists) {
3193                 /* Cache contains more messages then folder, we have cached
3194                    an old UID of a message that was removed and new messages
3195                    have been added too, otherwise the uid_next check would
3196                    not have failed */
3197                 debug_print("Freeing imap uid cache");
3198                 item->lastuid = 0;
3199                 g_slist_free(item->uid_list);
3200                 item->uid_list = NULL;
3201
3202                 g_slist_free(*msgnum_list);
3203
3204                 nummsgs = get_list_of_uids(session, folder, item, &uidlist);
3205         }
3206
3207         *msgnum_list = uidlist;
3208
3209         dir = folder_item_get_path((FolderItem *)item);
3210         debug_print("removing old messages from %s\n", dir);
3211         remove_numbered_files_not_in_list(dir, *msgnum_list);
3212         g_free(dir);
3213         
3214         debug_print("get_num_list - ok - %i\n", nummsgs);
3215         statusbar_pop_all();
3216         unlock_session();
3217         return nummsgs;
3218 }
3219
3220 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item)
3221 {
3222         MsgInfo *msginfo;
3223         MsgFlags flags;
3224
3225         flags.perm_flags = MSG_NEW|MSG_UNREAD;
3226         flags.tmp_flags = 0;
3227
3228         g_return_val_if_fail(item != NULL, NULL);
3229         g_return_val_if_fail(file != NULL, NULL);
3230
3231         if (folder_has_parent_of_type(item, F_QUEUE)) {
3232                 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
3233         } else if (folder_has_parent_of_type(item, F_DRAFT)) {
3234                 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
3235         }
3236
3237         msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3238         if (!msginfo) return NULL;
3239         
3240         msginfo->plaintext_file = g_strdup(file);
3241         msginfo->folder = item;
3242
3243         return msginfo;
3244 }
3245
3246 GSList *imap_get_msginfos(Folder *folder, FolderItem *item,
3247                           GSList *msgnum_list)
3248 {
3249         IMAPSession *session;
3250         MsgInfoList *ret = NULL;
3251         gint ok;
3252         
3253         debug_print("get_msginfos\n");
3254         
3255         g_return_val_if_fail(folder != NULL, NULL);
3256         g_return_val_if_fail(item != NULL, NULL);
3257         g_return_val_if_fail(msgnum_list != NULL, NULL);
3258
3259         session = imap_session_get(folder);
3260         g_return_val_if_fail(session != NULL, NULL);
3261         lock_session();
3262         debug_print("IMAP getting msginfos\n");
3263         ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3264                          NULL, NULL, NULL, NULL, FALSE);
3265         if (ok != IMAP_SUCCESS) {
3266                 unlock_session();
3267                 return NULL;
3268         }
3269         if (!(folder_has_parent_of_type(item, F_DRAFT) || 
3270               folder_has_parent_of_type(item, F_QUEUE))) {
3271                 ret = g_slist_concat(ret,
3272                         imap_get_uncached_messages(session, item,
3273                                                    msgnum_list));
3274         } else {
3275                 MsgNumberList *sorted_list, *elem;
3276                 gint startnum, lastnum;
3277
3278                 sorted_list = g_slist_sort(g_slist_copy(msgnum_list), g_int_compare);
3279
3280                 startnum = lastnum = GPOINTER_TO_INT(sorted_list->data);
3281
3282                 for (elem = sorted_list;; elem = g_slist_next(elem)) {
3283                         guint num = 0;
3284
3285                         if (elem)
3286                                 num = GPOINTER_TO_INT(elem->data);
3287
3288                         if (num > lastnum + 1 || elem == NULL) {
3289                                 int i;
3290                                 for (i = startnum; i <= lastnum; ++i) {
3291                                         gchar *file;
3292                         
3293                                         file = imap_fetch_msg(folder, item, i);
3294                                         if (file != NULL) {
3295                                                 MsgInfo *msginfo = imap_parse_msg(file, item);
3296                                                 if (msginfo != NULL) {
3297                                                         msginfo->msgnum = i;
3298                                                         ret = g_slist_append(ret, msginfo);
3299                                                 }
3300                                                 g_free(file);
3301                                         }
3302                                 }
3303
3304                                 if (elem == NULL)
3305                                         break;
3306
3307                                 startnum = num;
3308                         }
3309                         lastnum = num;
3310                 }
3311
3312                 g_slist_free(sorted_list);
3313         }
3314         unlock_session();
3315         return ret;
3316 }
3317
3318 MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid)
3319 {
3320         MsgInfo *msginfo = NULL;
3321         MsgInfoList *msginfolist;
3322         MsgNumberList numlist;
3323
3324         numlist.next = NULL;
3325         numlist.data = GINT_TO_POINTER(uid);
3326
3327         msginfolist = imap_get_msginfos(folder, item, &numlist);
3328         if (msginfolist != NULL) {
3329                 msginfo = msginfolist->data;
3330                 g_slist_free(msginfolist);
3331         }
3332
3333         return msginfo;
3334 }
3335
3336 gboolean imap_scan_required(Folder *folder, FolderItem *_item)
3337 {
3338         IMAPSession *session;
3339         IMAPFolderItem *item = (IMAPFolderItem *)_item;
3340         gint ok, exists = 0, unseen = 0;
3341         guint32 uid_next, uid_val;
3342         gboolean selected_folder;
3343         
3344         g_return_val_if_fail(folder != NULL, FALSE);
3345         g_return_val_if_fail(item != NULL, FALSE);
3346         g_return_val_if_fail(item->item.folder != NULL, FALSE);
3347         g_return_val_if_fail(FOLDER_CLASS(item->item.folder) == &imap_class, FALSE);
3348
3349         if (item->item.path == NULL)
3350                 return FALSE;
3351
3352         session = imap_session_get(folder);
3353         g_return_val_if_fail(session != NULL, FALSE);
3354         lock_session();
3355         selected_folder = (session->mbox != NULL) &&
3356                           (!strcmp(session->mbox, item->item.path));
3357         if (selected_folder) {
3358                 ok = imap_cmd_noop(session);
3359                 if (ok != IMAP_SUCCESS) {
3360                         debug_print("disconnected!\n");
3361                         session = imap_reconnect_if_possible(folder, session);
3362                         if (session == NULL)
3363                                 return FALSE;
3364                         lock_session();
3365                 }
3366
3367                 if (session->folder_content_changed
3368                 ||  session->exists != item->item.total_msgs) {
3369                         unlock_session();
3370                         return TRUE;
3371                 }
3372         } else {
3373                 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path, IMAP_FOLDER_ITEM(item),
3374                                  &exists, &uid_next, &uid_val, &unseen, FALSE);
3375                 if (ok != IMAP_SUCCESS) {
3376                         unlock_session();
3377                         return FALSE;
3378                 }
3379
3380                 item->use_cache = time(NULL);
3381                 item->c_messages = exists;
3382                 item->c_uid_next = uid_next;
3383                 item->c_uid_validity = uid_val;
3384                 item->c_unseen = unseen;
3385
3386                 if ((uid_next != item->uid_next) || (exists != item->item.total_msgs)) {
3387                         unlock_session();
3388                         return TRUE;
3389                 }
3390         }
3391         unlock_session();
3392         return FALSE;
3393 }
3394
3395 void imap_change_flags(Folder *folder, FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
3396 {
3397         IMAPSession *session;
3398         IMAPFlags flags_set = 0, flags_unset = 0;
3399         gint ok = IMAP_SUCCESS;
3400         MsgNumberList numlist;
3401         hashtable_data *ht_data = NULL;
3402
3403         g_return_if_fail(folder != NULL);
3404         g_return_if_fail(folder->klass == &imap_class);
3405         g_return_if_fail(item != NULL);
3406         g_return_if_fail(item->folder == folder);
3407         g_return_if_fail(msginfo != NULL);
3408         g_return_if_fail(msginfo->folder == item);
3409
3410         session = imap_session_get(folder);
3411         if (!session) {
3412                 return;
3413         }
3414         lock_session();
3415         if ((ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
3416             NULL, NULL, NULL, NULL, FALSE)) != IMAP_SUCCESS) {
3417                 unlock_session();
3418                 return;
3419         }
3420
3421         if (!MSG_IS_MARKED(msginfo->flags) &&  (newflags & MSG_MARKED))
3422                 flags_set |= IMAP_FLAG_FLAGGED;
3423         if ( MSG_IS_MARKED(msginfo->flags) && !(newflags & MSG_MARKED))
3424                 flags_unset |= IMAP_FLAG_FLAGGED;
3425
3426         if (!MSG_IS_UNREAD(msginfo->flags) &&  (newflags & MSG_UNREAD))
3427                 flags_unset |= IMAP_FLAG_SEEN;
3428         if ( MSG_IS_UNREAD(msginfo->flags) && !(newflags & MSG_UNREAD))
3429                 flags_set |= IMAP_FLAG_SEEN;
3430
3431         if (!MSG_IS_REPLIED(msginfo->flags) &&  (newflags & MSG_REPLIED))
3432                 flags_set |= IMAP_FLAG_ANSWERED;
3433         if ( MSG_IS_REPLIED(msginfo->flags) && !(newflags & MSG_REPLIED))
3434                 flags_unset |= IMAP_FLAG_ANSWERED;
3435
3436         if (!MSG_IS_DELETED(msginfo->flags) &&  (newflags & MSG_DELETED))
3437                 flags_set |= IMAP_FLAG_DELETED;
3438         if ( MSG_IS_DELETED(msginfo->flags) && !(newflags & MSG_DELETED))
3439                 flags_unset |= IMAP_FLAG_DELETED;
3440
3441         numlist.next = NULL;
3442         numlist.data = GINT_TO_POINTER(msginfo->msgnum);
3443
3444         if (IMAP_FOLDER_ITEM(item)->batching) {
3445                 /* instead of performing an UID STORE command for each message change,
3446                  * as a lot of them can change "together", we just fill in hashtables
3447                  * and defer the treatment so that we're able to send only one
3448                  * command.
3449                  */
3450                 debug_print("IMAP batch mode on, deferring flags change\n");
3451                 if (flags_set) {
3452                         ht_data = g_hash_table_lookup(IMAP_FOLDER_ITEM(item)->flags_set_table, 
3453                                 GINT_TO_POINTER(flags_set));
3454                         if (ht_data == NULL) {
3455                                 ht_data = g_new0(hashtable_data, 1);
3456                                 ht_data->session = session;
3457                                 ht_data->item = IMAP_FOLDER_ITEM(item);
3458                                 g_hash_table_insert(IMAP_FOLDER_ITEM(item)->flags_set_table, 
3459                                         GINT_TO_POINTER(flags_set), ht_data);
3460                         }
3461                         if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
3462                                 ht_data->msglist = g_slist_prepend(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum));
3463                 } 
3464                 if (flags_unset) {
3465                         ht_data = g_hash_table_lookup(IMAP_FOLDER_ITEM(item)->flags_unset_table, 
3466                                 GINT_TO_POINTER(flags_unset));
3467                         if (ht_data == NULL) {
3468                                 ht_data = g_new0(hashtable_data, 1);
3469                                 ht_data->session = session;
3470                                 ht_data->item = IMAP_FOLDER_ITEM(item);
3471                                 g_hash_table_insert(IMAP_FOLDER_ITEM(item)->flags_unset_table, 
3472                                         GINT_TO_POINTER(flags_unset), ht_data);
3473                         }
3474                         if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
3475                                 ht_data->msglist = g_slist_prepend(ht_data->msglist, 
3476                                         GINT_TO_POINTER(msginfo->msgnum));              
3477                 }
3478         } else {
3479                 debug_print("IMAP changing flags\n");
3480                 if (flags_set) {
3481                         ok = imap_set_message_flags(session, &numlist, flags_set, TRUE);
3482                         if (ok != IMAP_SUCCESS) {
3483                                 unlock_session();
3484                                 return;
3485                         }
3486                 }
3487
3488                 if (flags_unset) {
3489                         ok = imap_set_message_flags(session, &numlist, flags_unset, FALSE);
3490                         if (ok != IMAP_SUCCESS) {
3491                                 unlock_session();
3492                                 return;
3493                         }
3494                 }
3495         }
3496         msginfo->flags.perm_flags = newflags;
3497         unlock_session();
3498         return;
3499 }
3500
3501 static gint imap_remove_msg(Folder *folder, FolderItem *item, gint uid)
3502 {
3503         gint ok;
3504         IMAPSession *session;
3505         gchar *dir;
3506         MsgNumberList numlist;
3507         
3508         g_return_val_if_fail(folder != NULL, -1);
3509         g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3510         g_return_val_if_fail(item != NULL, -1);
3511
3512         session = imap_session_get(folder);
3513         if (!session) return -1;
3514         lock_session();
3515         ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3516                          NULL, NULL, NULL, NULL, FALSE);
3517         if (ok != IMAP_SUCCESS) {
3518                 unlock_session();
3519                 return ok;
3520         }
3521         numlist.next = NULL;
3522         numlist.data = GINT_TO_POINTER(uid);
3523         
3524         ok = imap_set_message_flags
3525                 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
3526                 &numlist, IMAP_FLAG_DELETED, TRUE);
3527         if (ok != IMAP_SUCCESS) {
3528                 log_warning(_("can't set deleted flags: %d\n"), uid);
3529                 unlock_session();
3530                 return ok;
3531         }
3532
3533         if (!session->uidplus) {
3534                 ok = imap_cmd_expunge(session);
3535         } else {
3536                 gchar *uidstr;
3537
3538                 uidstr = g_strdup_printf("%u", uid);
3539                 ok = imap_cmd_expunge(session);
3540                 g_free(uidstr);
3541         }
3542         if (ok != IMAP_SUCCESS) {
3543                 log_warning(_("can't expunge\n"));
3544                 unlock_session();
3545                 return ok;
3546         }
3547
3548         IMAP_FOLDER_ITEM(item)->uid_list = g_slist_remove(
3549             IMAP_FOLDER_ITEM(item)->uid_list, numlist.data);
3550         dir = folder_item_get_path(item);
3551         if (is_dir_exist(dir))
3552                 remove_numbered_files(dir, uid, uid);
3553         g_free(dir);
3554         unlock_session();
3555         return IMAP_SUCCESS;
3556 }
3557
3558 static gint compare_msginfo(gconstpointer a, gconstpointer b)
3559 {
3560         return ((MsgInfo *)a)->msgnum - ((MsgInfo *)b)->msgnum;
3561 }
3562
3563 static guint gslist_find_next_num(MsgNumberList **list, guint num)
3564 {
3565         GSList *elem;
3566
3567         g_return_val_if_fail(list != NULL, -1);
3568
3569         for (elem = *list; elem != NULL; elem = g_slist_next(elem))
3570                 if (GPOINTER_TO_INT(elem->data) >= num)
3571                         break;
3572         *list = elem;
3573         return elem != NULL ? GPOINTER_TO_INT(elem->data) : (gint)-1;
3574 }
3575
3576 /*
3577  * NEW and DELETED flags are not syncronized
3578  * - The NEW/RECENT flags in IMAP folders can not really be directly
3579  *   modified by Sylpheed
3580  * - The DELETE/DELETED flag in IMAP and Sylpheed don't have the same
3581  *   meaning, in IMAP it always removes the messages from the FolderItem
3582  *   in Sylpheed it can mean to move the message to trash
3583  */
3584
3585 typedef struct _get_flags_data {
3586         Folder *folder;
3587         FolderItem *item;
3588         MsgInfoList *msginfo_list;
3589         GRelation *msgflags;
3590         gboolean full_search;
3591         gboolean done;
3592 } get_flags_data;
3593
3594 static /*gint*/ void *imap_get_flags_thread(void *data)
3595 {
3596         get_flags_data *stuff = (get_flags_data *)data;
3597         Folder *folder = stuff->folder;
3598         FolderItem *item = stuff->item;
3599         MsgInfoList *msginfo_list = stuff->msginfo_list;
3600         GRelation *msgflags = stuff->msgflags;
3601         gboolean full_search = stuff->full_search;
3602         IMAPSession *session;
3603         GSList *sorted_list = NULL;
3604         GSList *unseen = NULL, *answered = NULL, *flagged = NULL, *deleted = NULL;
3605         GSList *p_unseen, *p_answered, *p_flagged, *p_deleted;
3606         GSList *elem;
3607         GSList *seq_list, *cur;
3608         gboolean reverse_seen = FALSE;
3609         GString *cmd_buf;
3610         gint ok;
3611         gint exists_cnt, unseen_cnt;
3612         gboolean selected_folder;
3613         
3614         if (folder == NULL || item == NULL) {
3615                 stuff->done = TRUE;
3616                 return GINT_TO_POINTER(-1);
3617         }
3618
3619         session = imap_session_get(folder);
3620         if (session == NULL) {
3621                 stuff->done = TRUE;
3622                 return GINT_TO_POINTER(-1);
3623         }
3624         lock_session();
3625         selected_folder = (session->mbox != NULL) &&
3626                           (!strcmp(session->mbox, item->path));
3627
3628         if (!selected_folder) {
3629                 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3630                         &exists_cnt, NULL, &unseen_cnt, NULL, TRUE);
3631                 if (ok != IMAP_SUCCESS) {
3632                         stuff->done = TRUE;
3633                         unlock_session();
3634                         return GINT_TO_POINTER(-1);
3635                 }
3636
3637                 if (unseen_cnt > exists_cnt / 2)
3638                         reverse_seen = TRUE;
3639         } 
3640         else {
3641                 if (item->unread_msgs > item->total_msgs / 2)
3642                         reverse_seen = TRUE;
3643         }
3644
3645         cmd_buf = g_string_new(NULL);
3646
3647         sorted_list = g_slist_sort(g_slist_copy(msginfo_list), compare_msginfo);
3648         if (!full_search) {
3649                 seq_list = imap_get_lep_set_from_msglist(msginfo_list);
3650         } else {
3651                 struct mailimap_set * set;
3652                 set = mailimap_set_new_interval(1, 0);
3653                 seq_list = g_slist_append(NULL, set);
3654         }
3655
3656         for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
3657                 struct mailimap_set * imapset;
3658                 clist * lep_uidlist;
3659                 int r;
3660                 
3661                 imapset = cur->data;
3662                 if (reverse_seen) {
3663                         r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SEEN,
3664                                                  imapset, &lep_uidlist);
3665                 }
3666                 else {
3667                         r = imap_threaded_search(folder,
3668                                                  IMAP_SEARCH_TYPE_UNSEEN,
3669                                                  imapset, &lep_uidlist);
3670                 }
3671                 if (r == MAILIMAP_NO_ERROR) {
3672                         GSList * uidlist;
3673                         
3674                         uidlist = imap_uid_list_from_lep(lep_uidlist);
3675                         mailimap_search_result_free(lep_uidlist);
3676                         
3677                         unseen = g_slist_concat(unseen, uidlist);
3678                 }
3679                 
3680                 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_ANSWERED,
3681                                          imapset, &lep_uidlist);
3682                 if (r == MAILIMAP_NO_ERROR) {
3683                         GSList * uidlist;
3684                         
3685                         uidlist = imap_uid_list_from_lep(lep_uidlist);
3686                         mailimap_search_result_free(lep_uidlist);
3687                         
3688                         answered = g_slist_concat(answered, uidlist);
3689                 }
3690
3691                 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_FLAGGED,
3692                                          imapset, &lep_uidlist);
3693                 if (r == MAILIMAP_NO_ERROR) {
3694                         GSList * uidlist;
3695                         
3696                         uidlist = imap_uid_list_from_lep(lep_uidlist);
3697                         mailimap_search_result_free(lep_uidlist);
3698                         
3699                         flagged = g_slist_concat(flagged, uidlist);
3700                 }
3701                 
3702                 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_DELETED,
3703                                          imapset, &lep_uidlist);
3704                 if (r == MAILIMAP_NO_ERROR) {
3705                         GSList * uidlist;
3706                         
3707                         uidlist = imap_uid_list_from_lep(lep_uidlist);
3708                         mailimap_search_result_free(lep_uidlist);
3709                         
3710                         deleted = g_slist_concat(deleted, uidlist);
3711                 }
3712         }
3713
3714         p_unseen = unseen;
3715         p_answered = answered;
3716         p_flagged = flagged;
3717         p_deleted = deleted;
3718
3719         for (elem = sorted_list; elem != NULL; elem = g_slist_next(elem)) {
3720                 MsgInfo *msginfo;
3721                 MsgPermFlags flags;
3722                 gboolean wasnew;
3723                 
3724                 msginfo = (MsgInfo *) elem->data;
3725                 flags = msginfo->flags.perm_flags;
3726                 wasnew = (flags & MSG_NEW);
3727                 flags &= ~((reverse_seen ? 0 : MSG_UNREAD | MSG_NEW) | MSG_REPLIED | MSG_MARKED);
3728                 if (reverse_seen)
3729                         flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
3730                 if (gslist_find_next_num(&p_unseen, msginfo->msgnum) == msginfo->msgnum) {
3731                         if (!reverse_seen) {
3732                                 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
3733                         } else {
3734                                 flags &= ~(MSG_UNREAD | MSG_NEW);
3735                         }
3736                 }
3737                 if (gslist_find_next_num(&p_answered, msginfo->msgnum) == msginfo->msgnum)
3738                         flags |= MSG_REPLIED;
3739                 else
3740                         flags &= ~MSG_REPLIED;
3741                 if (gslist_find_next_num(&p_flagged, msginfo->msgnum) == msginfo->msgnum)
3742                         flags |= MSG_MARKED;
3743                 else
3744                         flags &= ~MSG_MARKED;
3745                 if (gslist_find_next_num(&p_deleted, msginfo->msgnum) == msginfo->msgnum)
3746                         flags |= MSG_DELETED;
3747                 else
3748                         flags &= ~MSG_DELETED;
3749                 g_relation_insert(msgflags, msginfo, GINT_TO_POINTER(flags));
3750         }
3751
3752         imap_lep_set_free(seq_list);
3753         g_slist_free(flagged);
3754         g_slist_free(deleted);
3755         g_slist_free(answered);
3756         g_slist_free(unseen);
3757         g_slist_free(sorted_list);
3758         g_string_free(cmd_buf, TRUE);
3759
3760         stuff->done = TRUE;
3761         unlock_session();
3762         return GINT_TO_POINTER(0);
3763 }
3764
3765 static gint imap_get_flags(Folder *folder, FolderItem *item,
3766                            MsgInfoList *msginfo_list, GRelation *msgflags)
3767 {
3768         gint result;
3769         get_flags_data *data = g_new0(get_flags_data, 1);
3770         data->done = FALSE;
3771         data->folder = folder;
3772         data->item = item;
3773         data->msginfo_list = msginfo_list;
3774         data->msgflags = msgflags;
3775         data->full_search = FALSE;
3776
3777         GSList *tmp = NULL, *cur;
3778         
3779         if (prefs_common.work_offline && !inc_offline_should_override()) {
3780                 g_free(data);
3781                 return -1;
3782         }
3783
3784         tmp = folder_item_get_msg_list(item);
3785
3786         if (g_slist_length(tmp) == g_slist_length(msginfo_list))
3787                 data->full_search = TRUE;
3788         
3789         for (cur = tmp; cur; cur = cur->next)
3790                 procmsg_msginfo_free((MsgInfo *)cur->data);
3791         
3792         g_slist_free(tmp);
3793
3794         result = GPOINTER_TO_INT(imap_get_flags_thread(data));
3795         
3796         g_free(data);
3797         return result;
3798
3799 }
3800
3801 static gboolean process_flags(gpointer key, gpointer value, gpointer user_data)
3802 {
3803         gboolean flags_set = GPOINTER_TO_INT(user_data);
3804         gint flags_value = GPOINTER_TO_INT(key);
3805         hashtable_data *data = (hashtable_data *)value;
3806         IMAPFolderItem *_item = data->item;
3807         FolderItem *item = (FolderItem *)_item;
3808         gint ok = IMAP_ERROR;
3809         IMAPSession *session = imap_session_get(item->folder);
3810
3811         data->msglist = g_slist_reverse(data->msglist);
3812         
3813         debug_print("IMAP %ssetting flags to %d for %d messages\n",
3814                 flags_set?"":"un",
3815                 flags_value,
3816                 g_slist_length(data->msglist));
3817         
3818         if (session) {
3819                 lock_session();
3820                 ok = imap_select(session, IMAP_FOLDER(item->folder), item->path,
3821                          NULL, NULL, NULL, NULL, FALSE);
3822         }
3823         if (ok == IMAP_SUCCESS) {
3824                 imap_set_message_flags(data->session, data->msglist, flags_value, flags_set);
3825         } else {
3826                 g_warning("can't select mailbox %s\n", item->path);
3827         }
3828         if (session)
3829                 unlock_session();
3830         g_slist_free(data->msglist);    
3831         g_free(data);
3832         return TRUE;
3833 }
3834
3835 static void process_hashtable(IMAPFolderItem *item)
3836 {
3837         if (item->flags_set_table) {
3838                 g_hash_table_foreach_remove(item->flags_set_table, process_flags, GINT_TO_POINTER(TRUE));
3839                 g_free(item->flags_set_table);
3840                 item->flags_set_table = NULL;
3841         }
3842         if (item->flags_unset_table) {
3843                 g_hash_table_foreach_remove(item->flags_unset_table, process_flags, GINT_TO_POINTER(FALSE));
3844                 g_free(item->flags_unset_table);
3845                 item->flags_unset_table = NULL;
3846         }
3847 }
3848
3849 static void imap_set_batch (Folder *folder, FolderItem *_item, gboolean batch)
3850 {
3851         IMAPFolderItem *item = (IMAPFolderItem *)_item;
3852
3853         g_return_if_fail(item != NULL);
3854         
3855         if (item->batching == batch)
3856                 return;
3857         
3858         if (batch) {
3859                 item->batching = TRUE;
3860                 debug_print("IMAP switching to batch mode\n");
3861                 if (!item->flags_set_table) {
3862                         item->flags_set_table = g_hash_table_new(NULL, g_direct_equal);
3863                 }
3864                 if (!item->flags_unset_table) {
3865                         item->flags_unset_table = g_hash_table_new(NULL, g_direct_equal);
3866                 }
3867         } else {
3868                 debug_print("IMAP switching away from batch mode\n");
3869                 /* process stuff */
3870                 process_hashtable(item);
3871                 item->batching = FALSE;
3872         }
3873 }
3874
3875
3876
3877 /* data types conversion libetpan <-> sylpheed */
3878
3879
3880
3881 #define ETPAN_IMAP_MB_MARKED      1
3882 #define ETPAN_IMAP_MB_UNMARKED    2
3883 #define ETPAN_IMAP_MB_NOSELECT    4
3884 #define ETPAN_IMAP_MB_NOINFERIORS 8
3885
3886 static int imap_flags_to_flags(struct mailimap_mbx_list_flags * imap_flags)
3887 {
3888   int flags;
3889   clistiter * cur;
3890   
3891   flags = 0;
3892   if (imap_flags->mbf_type == MAILIMAP_MBX_LIST_FLAGS_SFLAG) {
3893     switch (imap_flags->mbf_sflag) {
3894     case MAILIMAP_MBX_LIST_SFLAG_MARKED:
3895       flags |= ETPAN_IMAP_MB_MARKED;
3896       break;
3897     case MAILIMAP_MBX_LIST_SFLAG_NOSELECT:
3898       flags |= ETPAN_IMAP_MB_NOSELECT;
3899       break;
3900     case MAILIMAP_MBX_LIST_SFLAG_UNMARKED:
3901       flags |= ETPAN_IMAP_MB_UNMARKED;
3902       break;
3903     }
3904   }
3905   
3906   for(cur = clist_begin(imap_flags->mbf_oflags) ; cur != NULL ;
3907       cur = clist_next(cur)) {
3908     struct mailimap_mbx_list_oflag * oflag;
3909     
3910     oflag = clist_content(cur);
3911     
3912     switch (oflag->of_type) {
3913     case MAILIMAP_MBX_LIST_OFLAG_NOINFERIORS:
3914       flags |= ETPAN_IMAP_MB_NOINFERIORS;
3915       break;
3916     }
3917   }
3918   
3919   return flags;
3920 }
3921
3922 static GSList * imap_list_from_lep(IMAPFolder * folder,
3923                                    clist * list, const gchar * real_path, gboolean all)
3924 {
3925         clistiter * iter;
3926         GSList * item_list;
3927         
3928         item_list = NULL;
3929         
3930         for(iter = clist_begin(list) ; iter != NULL ;
3931             iter = clist_next(iter)) {
3932                 struct mailimap_mailbox_list * mb;
3933                 int flags;
3934                 char delimiter;
3935                 char * name;
3936                 char * dup_name;
3937                 gchar * base;
3938                 gchar * loc_name;
3939                 gchar * loc_path;
3940                 FolderItem *new_item;
3941                 
3942                 mb = clist_content(iter);
3943
3944                 if (mb == NULL)
3945                         continue;
3946
3947                 flags = 0;
3948                 if (mb->mb_flag != NULL)
3949                         flags = imap_flags_to_flags(mb->mb_flag);
3950                 
3951                 delimiter = mb->mb_delimiter;
3952                 name = mb->mb_name;
3953                 
3954                 dup_name = strdup(name);                
3955                 if (delimiter != '\0')
3956                         subst_char(dup_name, delimiter, '/');
3957                 
3958                 base = g_path_get_basename(dup_name);
3959                 if (base[0] == '.') {
3960                         g_free(base);
3961                         free(dup_name);
3962                         continue;
3963                 }
3964                 
3965                 if (!all && strcmp(dup_name, real_path) == 0) {
3966                         g_free(base);
3967                         free(dup_name);
3968                         continue;
3969                 }
3970
3971                 if (!all && dup_name[strlen(dup_name)-1] == '/') {
3972                         g_free(base);
3973                         free(dup_name);
3974                         continue;
3975                 }
3976                 
3977                 loc_name = imap_modified_utf7_to_utf8(base);
3978                 loc_path = imap_modified_utf7_to_utf8(dup_name);
3979                 
3980                 new_item = folder_item_new(FOLDER(folder), loc_name, loc_path);
3981                 if ((flags & ETPAN_IMAP_MB_NOINFERIORS) != 0)
3982                         new_item->no_sub = TRUE;
3983                 if (strcmp(dup_name, "INBOX") != 0 &&
3984                     ((flags & ETPAN_IMAP_MB_NOSELECT) != 0))
3985                         new_item->no_select = TRUE;
3986                 
3987                 item_list = g_slist_append(item_list, new_item);
3988                 
3989                 debug_print("folder '%s' found.\n", loc_path);
3990                 g_free(base);
3991                 g_free(loc_path);
3992                 g_free(loc_name);
3993                 
3994                 free(dup_name);
3995         }
3996         
3997         return item_list;
3998 }
3999
4000 static GSList * imap_get_lep_set_from_numlist(MsgNumberList *numlist)
4001 {
4002         GSList *sorted_list, *cur;
4003         guint first, last, next;
4004         GSList *ret_list = NULL;
4005         unsigned int count;
4006         struct mailimap_set * current_set;
4007         unsigned int item_count;
4008         
4009         if (numlist == NULL)
4010                 return NULL;
4011         
4012         count = 0;
4013         current_set = mailimap_set_new_empty();
4014         
4015         sorted_list = g_slist_copy(numlist);
4016         sorted_list = g_slist_sort(sorted_list, g_int_compare);
4017
4018         first = GPOINTER_TO_INT(sorted_list->data);
4019         
4020         item_count = 0;
4021         for (cur = sorted_list; cur != NULL; cur = g_slist_next(cur)) {
4022                 if (GPOINTER_TO_INT(cur->data) == 0)
4023                         continue;
4024                 
4025                 item_count ++;
4026
4027                 last = GPOINTER_TO_INT(cur->data);
4028                 if (cur->next)
4029                         next = GPOINTER_TO_INT(cur->next->data);
4030                 else
4031                         next = 0;
4032
4033                 if (last + 1 != next || next == 0) {
4034
4035                         struct mailimap_set_item * item;
4036                         item = mailimap_set_item_new(first, last);
4037                         mailimap_set_add(current_set, item);
4038                         count ++;
4039                         
4040                         first = next;
4041                         
4042                         if (count >= IMAP_SET_MAX_COUNT) {
4043                                 ret_list = g_slist_append(ret_list,
4044                                                           current_set);
4045                                 current_set = mailimap_set_new_empty();
4046                                 count = 0;
4047                                 item_count = 0;
4048                         }
4049                 }
4050         }
4051         
4052         if (clist_count(current_set->set_list) > 0) {
4053                 ret_list = g_slist_append(ret_list,
4054                                           current_set);
4055         }
4056         
4057         g_slist_free(sorted_list);
4058
4059         return ret_list;
4060 }
4061
4062 static GSList * imap_get_lep_set_from_msglist(MsgInfoList *msglist)
4063 {
4064         MsgNumberList *numlist = NULL;
4065         MsgInfoList *cur;
4066         GSList *seq_list;
4067
4068         for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
4069                 MsgInfo *msginfo = (MsgInfo *) cur->data;
4070
4071                 numlist = g_slist_append(numlist, GINT_TO_POINTER(msginfo->msgnum));
4072         }
4073         seq_list = imap_get_lep_set_from_numlist(numlist);
4074         g_slist_free(numlist);
4075
4076         return seq_list;
4077 }
4078
4079 static GSList * imap_uid_list_from_lep(clist * list)
4080 {
4081         clistiter * iter;
4082         GSList * result;
4083         
4084         result = NULL;
4085         
4086         for(iter = clist_begin(list) ; iter != NULL ;
4087             iter = clist_next(iter)) {
4088                 uint32_t * puid;
4089                 
4090                 puid = clist_content(iter);
4091                 result = g_slist_append(result, GINT_TO_POINTER(* puid));
4092         }
4093         
4094         return result;
4095 }
4096
4097 static GSList * imap_uid_list_from_lep_tab(carray * list)
4098 {
4099         unsigned int i;
4100         GSList * result;
4101         
4102         result = NULL;
4103         
4104         for(i = 0 ; i < carray_count(list) ; i ++) {
4105                 uint32_t * puid;
4106                 
4107                 puid = carray_get(list, i);
4108                 result = g_slist_append(result, GINT_TO_POINTER(* puid));
4109         }
4110         
4111         return result;
4112 }
4113
4114 static MsgInfo *imap_envelope_from_lep(struct imap_fetch_env_info * info,
4115                                        FolderItem *item)
4116 {
4117         MsgInfo *msginfo = NULL;
4118         guint32 uid = 0;
4119         size_t size = 0;
4120         MsgFlags flags = {0, 0};
4121         
4122         if (info->headers == NULL)
4123                 return NULL;
4124
4125         MSG_SET_TMP_FLAGS(flags, MSG_IMAP);
4126         if (folder_has_parent_of_type(item, F_QUEUE)) {
4127                 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
4128         } else if (folder_has_parent_of_type(item, F_DRAFT)) {
4129                 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
4130         }
4131         flags.perm_flags = info->flags;
4132         
4133         uid = info->uid;
4134         size = info->size;
4135         msginfo = procheader_parse_str(info->headers, flags, FALSE, FALSE);
4136         
4137         if (msginfo) {
4138                 msginfo->msgnum = uid;
4139                 msginfo->size = size;
4140         }
4141
4142         return msginfo;
4143 }
4144
4145 static void imap_lep_set_free(GSList *seq_list)
4146 {
4147         GSList * cur;
4148         
4149         for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) {
4150                 struct mailimap_set * imapset;
4151                 
4152                 imapset = cur->data;
4153                 mailimap_set_free(imapset);
4154         }
4155         g_slist_free(seq_list);
4156 }
4157
4158 static struct mailimap_flag_list * imap_flag_to_lep(IMAPFlags flags)
4159 {
4160         struct mailimap_flag_list * flag_list;
4161         
4162         flag_list = mailimap_flag_list_new_empty();
4163         
4164         if (IMAP_IS_SEEN(flags))
4165                 mailimap_flag_list_add(flag_list,
4166                                        mailimap_flag_new_seen());
4167         if (IMAP_IS_ANSWERED(flags))
4168                 mailimap_flag_list_add(flag_list,
4169                                        mailimap_flag_new_answered());
4170         if (IMAP_IS_FLAGGED(flags))
4171                 mailimap_flag_list_add(flag_list,
4172                                        mailimap_flag_new_flagged());
4173         if (IMAP_IS_DELETED(flags))
4174                 mailimap_flag_list_add(flag_list,
4175                                        mailimap_flag_new_deleted());
4176         if (IMAP_IS_DRAFT(flags))
4177                 mailimap_flag_list_add(flag_list,
4178                                        mailimap_flag_new_draft());
4179         
4180         return flag_list;
4181 }
4182
4183 guint imap_folder_get_refcnt(Folder *folder)
4184 {
4185         return ((IMAPFolder *)folder)->refcnt;
4186 }
4187
4188 void imap_folder_ref(Folder *folder)
4189 {
4190         ((IMAPFolder *)folder)->refcnt++;
4191 }
4192
4193 void imap_folder_unref(Folder *folder)
4194 {
4195         if (((IMAPFolder *)folder)->refcnt > 0)
4196                 ((IMAPFolder *)folder)->refcnt--;
4197 }
4198
4199 #else /* HAVE_LIBETPAN */
4200
4201 static FolderClass imap_class;
4202
4203 static Folder   *imap_folder_new        (const gchar    *name,
4204                                          const gchar    *path)
4205 {
4206         return NULL;
4207 }
4208 static gint     imap_create_tree        (Folder         *folder)
4209 {
4210         return -1;
4211 }
4212 static FolderItem *imap_create_folder   (Folder         *folder,
4213                                          FolderItem     *parent,
4214                                          const gchar    *name)
4215 {
4216         return NULL;
4217 }
4218 static gint     imap_rename_folder      (Folder         *folder,
4219                                          FolderItem     *item, 
4220                                          const gchar    *name)
4221 {
4222         return -1;
4223 }
4224
4225 FolderClass *imap_get_class(void)
4226 {
4227         if (imap_class.idstr == NULL) {
4228                 imap_class.type = F_IMAP;
4229                 imap_class.idstr = "imap";
4230                 imap_class.uistr = "IMAP4";
4231
4232                 imap_class.new_folder = imap_folder_new;
4233                 imap_class.create_tree = imap_create_tree;
4234                 imap_class.create_folder = imap_create_folder;
4235                 imap_class.rename_folder = imap_rename_folder;
4236                 /* nothing implemented */
4237         }
4238
4239         return &imap_class;
4240 }
4241 #endif
4242
4243 void imap_synchronise(FolderItem *item) 
4244 {
4245         imap_gtk_synchronise(item);
4246 }