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