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