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