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