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