4498f5de380783b470e21d1e76b782d50e6e3bc0
[claws.git] / src / imap.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2009 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 3 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, see <http://www.gnu.org/licenses/>.
17  * 
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #  include "config.h"
22 #endif
23
24 #include "defs.h"
25
26 #include <glib.h>
27 #include <glib/gi18n.h>
28 #include <stdio.h>
29 #include <string.h>
30 #include "imap.h"
31 #include "imap_gtk.h"
32 #include "inc.h"
33 #include "xml.h"
34 #include "alertpanel.h"
35
36 #ifdef HAVE_LIBETPAN
37
38 #include <stdlib.h>
39 #include <dirent.h>
40 #include <unistd.h>
41 #include <ctype.h>
42 #include <time.h>
43 #include <errno.h>
44 #if HAVE_ICONV
45 #  include <iconv.h>
46 #endif
47
48 #ifdef USE_GNUTLS
49 #  include "ssl.h"
50 #endif
51
52 #include "folder.h"
53 #include "session.h"
54 #include "procmsg.h"
55 #include "socket.h"
56 #include "recv.h"
57 #include "procheader.h"
58 #include "prefs_account.h"
59 #include "codeconv.h"
60 #include "md5.h"
61 #include "base64.h"
62 #include "utils.h"
63 #include "prefs_common.h"
64 #include "inputdialog.h"
65 #include "log.h"
66 #include "remotefolder.h"
67 #include "claws.h"
68 #include "statusbar.h"
69 #include "msgcache.h"
70 #include "imap-thread.h"
71 #include "account.h"
72 #include "tags.h"
73 #include "main.h"
74
75 typedef struct _IMAPFolder      IMAPFolder;
76 typedef struct _IMAPSession     IMAPSession;
77 typedef struct _IMAPNameSpace   IMAPNameSpace;
78 typedef struct _IMAPFolderItem  IMAPFolderItem;
79
80 #include "prefs_account.h"
81
82 #define IMAP_FOLDER(obj)        ((IMAPFolder *)obj)
83 #define IMAP_FOLDER_ITEM(obj)   ((IMAPFolderItem *)obj)
84 #define IMAP_SESSION(obj)       ((IMAPSession *)obj)
85
86 struct _IMAPFolder
87 {
88         RemoteFolder rfolder;
89
90         /* list of IMAPNameSpace */
91         GList *ns_personal;
92         GList *ns_others;
93         GList *ns_shared;
94         gchar last_seen_separator;
95         guint refcnt;
96         guint max_set_size;
97 };
98
99 struct _IMAPSession
100 {
101         Session session;
102
103         gboolean authenticated;
104
105         GSList *capability;
106         gboolean uidplus;
107
108         gchar *mbox;
109         guint cmd_count;
110
111         /* CLAWS */
112         gboolean folder_content_changed;
113         guint exists;
114         guint recent;
115         guint expunge;
116         guint unseen;
117         guint uid_validity;
118         guint uid_next;
119
120         Folder * folder;
121         gboolean busy;
122         gboolean cancelled;
123         gboolean sens_update_block;
124 };
125
126 struct _IMAPNameSpace
127 {
128         gchar *name;
129         gchar separator;
130 };
131
132 #define IMAPBUFSIZE     8192
133
134 #define IMAP_IS_SEEN(flags)     ((flags & IMAP_FLAG_SEEN) != 0)
135 #define IMAP_IS_ANSWERED(flags) ((flags & IMAP_FLAG_ANSWERED) != 0)
136 #define IMAP_IS_FLAGGED(flags)  ((flags & IMAP_FLAG_FLAGGED) != 0)
137 #define IMAP_IS_DELETED(flags)  ((flags & IMAP_FLAG_DELETED) != 0)
138 #define IMAP_IS_DRAFT(flags)    ((flags & IMAP_FLAG_DRAFT) != 0)
139 #define IMAP_IS_FORWARDED(flags)        ((flags & IMAP_FLAG_FORWARDED) != 0)
140 #define IMAP_IS_SPAM(flags)     ((flags & IMAP_FLAG_SPAM) != 0)
141 #define IMAP_IS_HAM(flags)      ((flags & IMAP_FLAG_HAM) != 0)
142
143
144 #define IMAP4_PORT      143
145 #ifdef USE_GNUTLS
146 #define IMAPS_PORT      993
147 #endif
148
149 #define IMAP_CMD_LIMIT  1000
150
151 enum {
152         ITEM_CAN_CREATE_FLAGS_UNKNOWN = 0,
153         ITEM_CAN_CREATE_FLAGS,
154         ITEM_CANNOT_CREATE_FLAGS
155 };
156
157 struct _IMAPFolderItem
158 {
159         FolderItem item;
160
161         guint lastuid;
162         guint uid_next;
163         GSList *uid_list;
164         gboolean batching;
165
166         GHashTable *flags_set_table;
167         GHashTable *flags_unset_table;
168         guint32 last_change;
169         guint32 last_sync;
170         gboolean should_update;
171         gboolean should_trash_cache;
172         gint can_create_flags;
173
174         GHashTable *tags_set_table;
175         GHashTable *tags_unset_table;
176         GSList *ok_flags;
177
178 };
179
180 static XMLTag *imap_item_get_xml(Folder *folder, FolderItem *item);
181 static void imap_item_set_xml(Folder *folder, FolderItem *item, XMLTag *tag);
182
183 static void imap_folder_init            (Folder         *folder,
184                                          const gchar    *name,
185                                          const gchar    *path);
186
187 static Folder   *imap_folder_new        (const gchar    *name,
188                                          const gchar    *path);
189 static void      imap_folder_destroy    (Folder         *folder);
190
191 static IMAPSession *imap_session_new    (Folder         *folder,
192                                          const PrefsAccount     *account);
193 static gint     imap_session_authenticate(IMAPSession   *session,
194                                           PrefsAccount  *account);
195 static void     imap_session_destroy    (Session        *session);
196
197 static gchar   *imap_fetch_msg          (Folder         *folder, 
198                                          FolderItem     *item, 
199                                          gint            uid);
200 static gchar   *imap_fetch_msg_full     (Folder         *folder, 
201                                          FolderItem     *item, 
202                                          gint            uid,
203                                          gboolean        headers,
204                                          gboolean        body);
205 static void     imap_remove_cached_msg  (Folder         *folder, 
206                                          FolderItem     *item, 
207                                          MsgInfo        *msginfo);
208 static gint     imap_add_msg            (Folder         *folder,
209                                          FolderItem     *dest,
210                                          const gchar    *file, 
211                                          MsgFlags       *flags);
212 static gint     imap_add_msgs           (Folder         *folder, 
213                                          FolderItem     *dest,
214                                          GSList         *file_list,
215                                          GRelation      *relation);
216
217 static gint     imap_copy_msg           (Folder         *folder,
218                                          FolderItem     *dest, 
219                                          MsgInfo        *msginfo);
220 static gint     imap_copy_msgs          (Folder         *folder, 
221                                          FolderItem     *dest, 
222                                          MsgInfoList    *msglist, 
223                                          GRelation      *relation);
224
225 static gint     imap_remove_msg         (Folder         *folder, 
226                                          FolderItem     *item, 
227                                          gint            uid);
228 static gint     imap_remove_msgs        (Folder         *folder, 
229                                          FolderItem     *dest, 
230                                          MsgInfoList    *msglist, 
231                                          GRelation      *relation);
232 static gint     imap_expunge            (Folder         *folder, 
233                                          FolderItem     *dest);
234 static gint     imap_remove_all_msg     (Folder         *folder, 
235                                          FolderItem     *item);
236
237 static gboolean imap_is_msg_changed     (Folder         *folder,
238                                          FolderItem     *item, 
239                                          MsgInfo        *msginfo);
240
241 static gint     imap_close              (Folder         *folder, 
242                                          FolderItem     *item);
243
244 static gint     imap_scan_tree          (Folder         *folder);
245
246 static gint     imap_create_tree        (Folder         *folder);
247
248 static FolderItem *imap_create_folder   (Folder         *folder,
249                                          FolderItem     *parent,
250                                          const gchar    *name);
251 static gint     imap_rename_folder      (Folder         *folder,
252                                          FolderItem     *item, 
253                                          const gchar    *name);
254 static gint     imap_remove_folder      (Folder         *folder, 
255                                          FolderItem     *item);
256
257 static FolderItem *imap_folder_item_new (Folder         *folder);
258 static void imap_folder_item_destroy    (Folder         *folder,
259                                          FolderItem     *item);
260
261 static IMAPSession *imap_session_get    (Folder         *folder);
262
263 static gint imap_auth                   (IMAPSession    *session,
264                                          const gchar    *user,
265                                          const gchar    *pass,
266                                          IMAPAuthType    type);
267
268 static gint imap_scan_tree_recursive    (IMAPSession    *session,
269                                          FolderItem     *item,
270                                          gboolean        subs_only);
271
272 static void imap_create_missing_folders (Folder         *folder);
273 static FolderItem *imap_create_special_folder
274                                         (Folder                 *folder,
275                                          SpecialFolderItemType   stype,
276                                          const gchar            *name);
277
278 static gint imap_do_copy_msgs           (Folder         *folder,
279                                          FolderItem     *dest,
280                                          MsgInfoList    *msglist,
281                                          GRelation      *relation);
282
283 static void imap_delete_all_cached_messages     (FolderItem     *item);
284 static void imap_set_batch              (Folder         *folder,
285                                          FolderItem     *item,
286                                          gboolean        batch);
287 static gint imap_set_message_flags      (IMAPSession    *session,
288                                          IMAPFolderItem *item,
289                                          MsgNumberList  *numlist,
290                                          IMAPFlags       flags,
291                                          GSList         *tags,
292                                          gboolean        is_set);
293 static gint imap_select                 (IMAPSession    *session,
294                                          IMAPFolder     *folder,
295                                          FolderItem     *item,
296                                          gint           *exists,
297                                          gint           *recent,
298                                          gint           *unseen,
299                                          guint32        *uid_validity,
300                                          gint           *can_create_flags,
301                                          gboolean        block);
302 static gint imap_status                 (IMAPSession    *session,
303                                          IMAPFolder     *folder,
304                                          const gchar    *path,
305                                          IMAPFolderItem *item,
306                                          gint           *messages,
307                                          guint32        *uid_next,
308                                          guint32        *uid_validity,
309                                          gint           *unseen,
310                                          gboolean        block);
311 static void     imap_commit_tags        (FolderItem     *item, 
312                                          MsgInfo        *msginfo,
313                                          GSList         *set_tags,
314                                          GSList         *unset_tags);
315
316 static gchar imap_get_path_separator            (IMAPSession    *session,
317                                                  IMAPFolder     *folder,
318                                                  const gchar    *path,
319                                                  gint           *ok);
320 static gchar *imap_get_real_path                (IMAPSession    *session,
321                                                  IMAPFolder     *folder,
322                                                  const gchar    *path,
323                                                  gint           *ok);
324 #ifdef HAVE_LIBETPAN
325 static void imap_synchronise            (FolderItem     *item, gint days);
326 #endif
327 static gboolean imap_is_busy            (Folder *folder);
328
329 static void imap_free_capabilities      (IMAPSession    *session);
330
331 /* low-level IMAP4rev1 commands */
332 static gint imap_cmd_login      (IMAPSession    *session,
333                                  const gchar    *user,
334                                  const gchar    *pass,
335                                  const gchar    *type);
336 static gint imap_cmd_noop       (IMAPSession    *session);
337 #ifdef USE_GNUTLS
338 static gint imap_cmd_starttls   (IMAPSession    *session);
339 #endif
340 static gint imap_cmd_select     (IMAPSession    *session,
341                                  const gchar    *folder,
342                                  gint           *exists,
343                                  gint           *recent,
344                                  gint           *unseen,
345                                  guint32        *uid_validity,
346                                  gint           *can_create_flags,
347                                  GSList         **ok_flags,
348                                  gboolean        block);
349 static gint imap_cmd_close      (IMAPSession    *session);
350 static gint imap_cmd_examine    (IMAPSession    *session,
351                                  const gchar    *folder,
352                                  gint           *exists,
353                                  gint           *recent,
354                                  gint           *unseen,
355                                  guint32        *uid_validity,
356                                  gboolean        block);
357 static gint imap_cmd_create     (IMAPSession    *sock,
358                                  const gchar    *folder);
359 static gint imap_cmd_rename     (IMAPSession    *sock,
360                                  const gchar    *oldfolder,
361                                  const gchar    *newfolder);
362 static gint imap_cmd_delete     (IMAPSession    *session,
363                                  const gchar    *folder);
364 static gint imap_cmd_fetch      (IMAPSession    *sock,
365                                  guint32         uid,
366                                  const gchar    *filename,
367                                  gboolean        headers,
368                                  gboolean        body);
369 static gint imap_cmd_append     (IMAPSession    *session,
370                                  IMAPFolderItem *item,
371                                  const gchar    *destfolder,
372                                  const gchar    *file,
373                                  IMAPFlags       flags,
374                                  guint32        *new_uid);
375 static gint imap_cmd_copy       (IMAPSession *session,
376                                  struct mailimap_set * set,
377                                  const gchar *destfolder,
378                                  struct mailimap_set ** source,
379                                  struct mailimap_set ** dest);
380 static gint imap_cmd_store      (IMAPSession    *session,
381                                  IMAPFolderItem *item,
382                                  struct mailimap_set * set,
383                                  IMAPFlags flags,
384                                  GSList *tags,
385                                  int do_add);
386 static gint imap_cmd_expunge    (IMAPSession    *session, gboolean force);
387
388 static void imap_path_separator_subst           (gchar          *str,
389                                                  gchar           separator);
390
391 static gboolean imap_rename_folder_func         (GNode          *node,
392                                                  gpointer        data);
393 static gint imap_get_num_list                   (Folder         *folder,
394                                                  FolderItem     *item,
395                                                  GSList        **list,
396                                                  gboolean       *old_uids_valid);
397 static GSList *imap_get_msginfos                (Folder         *folder,
398                                                  FolderItem     *item,
399                                                  GSList         *msgnum_list);
400 static MsgInfo *imap_get_msginfo                (Folder         *folder,
401                                                  FolderItem     *item,
402                                                  gint            num);
403 static gboolean imap_scan_required              (Folder         *folder,
404                                                  FolderItem     *item);
405 static void imap_change_flags                   (Folder         *folder,
406                                                  FolderItem     *item,
407                                                  MsgInfo        *msginfo,
408                                                  MsgPermFlags    newflags);
409 static gint imap_get_flags                      (Folder         *folder,
410                                                  FolderItem     *item,
411                                                  MsgInfoList    *msglist,
412                                                  GRelation      *msgflags);
413 static gchar *imap_folder_get_path              (Folder         *folder);
414 static gchar *imap_item_get_path                (Folder         *folder,
415                                                  FolderItem     *item);
416 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item);
417
418
419 /* data types conversion libetpan <-> claws */
420 static GSList * imap_list_from_lep(IMAPFolder * folder,
421                                    clist * list, const gchar * real_path, gboolean all);
422 static GSList * imap_get_lep_set_from_numlist(IMAPFolder *folder, MsgNumberList *numlist);
423 static GSList * imap_get_lep_set_from_msglist(IMAPFolder *folder, MsgInfoList *msglist);
424 static GSList * imap_uid_list_from_lep(clist * list);
425 static GSList * imap_uid_list_from_lep_tab(carray * list);
426 static void imap_flags_hash_from_lep_uid_flags_tab(carray * list,
427                                                    GHashTable * hash,
428                                                    GHashTable *tags_hash);
429 static MsgInfo *imap_envelope_from_lep(struct imap_fetch_env_info * info,
430                                        FolderItem *item);
431 static void imap_lep_set_free(GSList *seq_list);
432 static struct mailimap_flag_list * imap_flag_to_lep(IMAPFolderItem *item, IMAPFlags flags, GSList *tags);
433
434 typedef struct _hashtable_data {
435         GSList *msglist;
436         IMAPFolderItem *item;
437 } hashtable_data;
438
439 static FolderClass imap_class;
440
441 FolderClass *imap_get_class(void)
442 {
443         if (imap_class.idstr == NULL) {
444                 imap_class.type = F_IMAP;
445                 imap_class.idstr = "imap";
446                 imap_class.uistr = "IMAP4";
447
448                 /* Folder functions */
449                 imap_class.new_folder = imap_folder_new;
450                 imap_class.destroy_folder = imap_folder_destroy;
451                 imap_class.scan_tree = imap_scan_tree;
452                 imap_class.create_tree = imap_create_tree;
453
454                 /* FolderItem functions */
455                 imap_class.item_new = imap_folder_item_new;
456                 imap_class.item_destroy = imap_folder_item_destroy;
457                 imap_class.item_get_path = imap_item_get_path;
458                 imap_class.create_folder = imap_create_folder;
459                 imap_class.rename_folder = imap_rename_folder;
460                 imap_class.remove_folder = imap_remove_folder;
461                 imap_class.close = imap_close;
462                 imap_class.get_num_list = imap_get_num_list;
463                 imap_class.scan_required = imap_scan_required;
464                 imap_class.set_xml = folder_set_xml;
465                 imap_class.get_xml = folder_get_xml;
466                 imap_class.item_set_xml = imap_item_set_xml;
467                 imap_class.item_get_xml = imap_item_get_xml;
468
469                 /* Message functions */
470                 imap_class.get_msginfo = imap_get_msginfo;
471                 imap_class.get_msginfos = imap_get_msginfos;
472                 imap_class.fetch_msg = imap_fetch_msg;
473                 imap_class.fetch_msg_full = imap_fetch_msg_full;
474                 imap_class.add_msg = imap_add_msg;
475                 imap_class.add_msgs = imap_add_msgs;
476                 imap_class.copy_msg = imap_copy_msg;
477                 imap_class.copy_msgs = imap_copy_msgs;
478                 imap_class.remove_msg = imap_remove_msg;
479                 imap_class.remove_msgs = imap_remove_msgs;
480                 imap_class.expunge = imap_expunge;
481                 imap_class.remove_all_msg = imap_remove_all_msg;
482                 imap_class.is_msg_changed = imap_is_msg_changed;
483                 imap_class.change_flags = imap_change_flags;
484                 imap_class.get_flags = imap_get_flags;
485                 imap_class.set_batch = imap_set_batch;
486                 imap_class.synchronise = imap_synchronise;
487                 imap_class.remove_cached_msg = imap_remove_cached_msg;
488                 imap_class.commit_tags = imap_commit_tags;
489 #ifdef USE_PTREAD
490                 pthread_mutex_init(&imap_mutex, NULL);
491 #endif
492         }
493         
494         return &imap_class;
495 }
496
497 static void imap_refresh_sensitivity (IMAPSession *session)
498 {
499         MainWindow *mainwin;
500
501         if (session->sens_update_block)
502                 return;
503         mainwin = mainwindow_get_mainwindow();
504         if (mainwin) {
505                 toolbar_main_set_sensitive(mainwin);
506                 main_window_set_menu_sensitive(mainwin);
507         }
508 }
509
510 static void lock_session(IMAPSession *session)
511 {
512         if (session) {
513                 debug_print("locking session %p (%d)\n", session, session->busy);
514                 if (session->busy)
515                         debug_print("         SESSION WAS LOCKED !!      \n");
516                 session->busy = TRUE;
517                 imap_refresh_sensitivity(session);
518         } else {
519                 debug_print("can't lock null session\n");
520         }
521 }
522
523 static void unlock_session(IMAPSession *session)
524 {
525         if (session) {
526                 debug_print("unlocking session %p\n", session);
527                 session->busy = FALSE;
528                 imap_refresh_sensitivity(session);
529         } else {
530                 debug_print("can't unlock null session\n");
531         }
532 }
533
534 static void imap_disc_session_destroy(IMAPSession *session)
535 {
536         RemoteFolder *rfolder = NULL;
537
538         if (session == NULL)
539                 return;
540
541         rfolder = REMOTE_FOLDER(IMAP_SESSION(session)->folder);
542         
543         if (rfolder == NULL)
544                 return;
545         log_warning(LOG_PROTOCOL, _("IMAP4 connection broken\n"));
546         SESSION(session)->state = SESSION_DISCONNECTED;
547         SESSION(session)->sock = NULL;
548 }
549
550 static gboolean is_fatal(int libetpan_errcode)
551 {
552         switch(libetpan_errcode) {
553         case MAILIMAP_ERROR_STREAM:
554         case MAILIMAP_ERROR_PROTOCOL:
555         case MAILIMAP_ERROR_PARSE:
556         case MAILIMAP_ERROR_BAD_STATE:
557                 return TRUE;
558         default:
559                 return FALSE;
560         }
561 }
562
563 static void imap_handle_error(Session *session, int libetpan_errcode)
564 {
565         const gchar *session_server = (session ? session->server:"(null)");
566         switch(libetpan_errcode) {
567         case MAILIMAP_NO_ERROR:
568                 return;
569         case MAILIMAP_NO_ERROR_AUTHENTICATED:
570                 log_warning(LOG_PROTOCOL, _("IMAP error on %s: authenticated\n"), session_server);
571                 break;
572         case MAILIMAP_NO_ERROR_NON_AUTHENTICATED:
573                 log_warning(LOG_PROTOCOL, _("IMAP error on %s: not authenticated\n"), session_server);
574                 break;
575         case MAILIMAP_ERROR_BAD_STATE:
576                 log_warning(LOG_PROTOCOL, _("IMAP error on %s: bad state\n"), session_server);
577                 break;
578         case MAILIMAP_ERROR_STREAM:
579                 log_warning(LOG_PROTOCOL, _("IMAP error on %s: stream error\n"), session_server);
580                 break;
581         case MAILIMAP_ERROR_PARSE:
582                 log_warning(LOG_PROTOCOL, _("IMAP error on %s: parse error "
583                                             "(very probably non-RFC compliance from the server)\n"), session_server);
584                 break;
585         case MAILIMAP_ERROR_CONNECTION_REFUSED:
586                 log_warning(LOG_PROTOCOL, _("IMAP error on %s: connection refused\n"), session_server);
587                 break;
588         case MAILIMAP_ERROR_MEMORY:
589                 log_warning(LOG_PROTOCOL, _("IMAP error on %s: memory error\n"), session_server);
590                 break;
591         case MAILIMAP_ERROR_FATAL:
592                 log_warning(LOG_PROTOCOL, _("IMAP error on %s: fatal error\n"), session_server);
593                 break;
594         case MAILIMAP_ERROR_PROTOCOL:
595                 log_warning(LOG_PROTOCOL, _("IMAP error on %s: protocol error"
596                                             "(very probably non-RFC compliance from the server)\n"), session_server);
597                 break;
598         case MAILIMAP_ERROR_DONT_ACCEPT_CONNECTION:
599                 log_warning(LOG_PROTOCOL, _("IMAP error on %s: connection not accepted\n"), session_server);
600                 break;
601         case MAILIMAP_ERROR_APPEND:
602                 log_warning(LOG_PROTOCOL, _("IMAP error on %s: APPEND error\n"), session_server);
603                 break;
604         case MAILIMAP_ERROR_NOOP:
605                 log_warning(LOG_PROTOCOL, _("IMAP error on %s: NOOP error\n"), session_server);
606                 break;
607         case MAILIMAP_ERROR_LOGOUT:
608                 log_warning(LOG_PROTOCOL, _("IMAP error on %s: LOGOUT error\n"), session_server);
609                 break;
610         case MAILIMAP_ERROR_CAPABILITY:
611                 log_warning(LOG_PROTOCOL, _("IMAP error on %s: CAPABILITY error\n"), session_server);
612                 break;
613         case MAILIMAP_ERROR_CHECK:
614                 log_warning(LOG_PROTOCOL, _("IMAP error on %s: CHECK error\n"), session_server);
615                 break;
616         case MAILIMAP_ERROR_CLOSE:
617                 log_warning(LOG_PROTOCOL, _("IMAP error on %s: CLOSE error\n"), session_server);
618                 break;
619         case MAILIMAP_ERROR_EXPUNGE:
620                 log_warning(LOG_PROTOCOL, _("IMAP error on %s: EXPUNGE error\n"), session_server);
621                 break;
622         case MAILIMAP_ERROR_COPY:
623                 log_warning(LOG_PROTOCOL, _("IMAP error on %s: COPY error\n"), session_server);
624                 break;
625         case MAILIMAP_ERROR_UID_COPY:
626                 log_warning(LOG_PROTOCOL, _("IMAP error on %s: UID COPY error\n"), session_server);
627                 break;
628         case MAILIMAP_ERROR_CREATE:
629                 log_warning(LOG_PROTOCOL, _("IMAP error on %s: CREATE error\n"), session_server);
630                 break;
631         case MAILIMAP_ERROR_DELETE:
632                 log_warning(LOG_PROTOCOL, _("IMAP error on %s: DELETE error\n"), session_server);
633                 break;
634         case MAILIMAP_ERROR_EXAMINE:
635                 log_warning(LOG_PROTOCOL, _("IMAP error on %s: EXAMINE error\n"), session_server);
636                 break;
637         case MAILIMAP_ERROR_FETCH:
638                 log_warning(LOG_PROTOCOL, _("IMAP error on %s: FETCH error\n"), session_server);
639                 break;
640         case MAILIMAP_ERROR_UID_FETCH:
641                 log_warning(LOG_PROTOCOL, _("IMAP error on %s: UID FETCH error\n"), session_server);
642                 break;
643         case MAILIMAP_ERROR_LIST:
644                 log_warning(LOG_PROTOCOL, _("IMAP error on %s: LIST error\n"), session_server);
645                 break;
646         case MAILIMAP_ERROR_LOGIN:
647                 log_warning(LOG_PROTOCOL, _("IMAP error on %s: LOGIN error\n"), session_server);
648                 break;
649         case MAILIMAP_ERROR_LSUB:
650                 log_warning(LOG_PROTOCOL, _("IMAP error on %s: LSUB error\n"), session_server);
651                 break;
652         case MAILIMAP_ERROR_RENAME:
653                 log_warning(LOG_PROTOCOL, _("IMAP error on %s: RENAME error\n"), session_server);
654                 break;
655         case MAILIMAP_ERROR_SEARCH:
656                 log_warning(LOG_PROTOCOL, _("IMAP error on %s: SEARCH error\n"), session_server);
657                 break;
658         case MAILIMAP_ERROR_UID_SEARCH:
659                 log_warning(LOG_PROTOCOL, _("IMAP error on %s: UID SEARCH error\n"), session_server);
660                 break;
661         case MAILIMAP_ERROR_SELECT:
662                 log_warning(LOG_PROTOCOL, _("IMAP error on %s: SELECT error\n"), session_server);
663                 break;
664         case MAILIMAP_ERROR_STATUS:
665                 log_warning(LOG_PROTOCOL, _("IMAP error on %s: STATUS error\n"), session_server);
666                 break;
667         case MAILIMAP_ERROR_STORE:
668                 log_warning(LOG_PROTOCOL, _("IMAP error on %s: STORE error\n"), session_server);
669                 break;
670         case MAILIMAP_ERROR_UID_STORE:
671                 log_warning(LOG_PROTOCOL, _("IMAP error on %s: UID STORE error\n"), session_server);
672                 break;
673         case MAILIMAP_ERROR_SUBSCRIBE:
674                 log_warning(LOG_PROTOCOL, _("IMAP error on %s: SUBSCRIBE error\n"), session_server);
675                 break;
676         case MAILIMAP_ERROR_UNSUBSCRIBE:
677                 log_warning(LOG_PROTOCOL, _("IMAP error on %s: UNSUBSCRIBE error\n"), session_server);
678                 break;
679         case MAILIMAP_ERROR_STARTTLS:
680                 log_warning(LOG_PROTOCOL, _("IMAP error on %s: STARTTLS error\n"), session_server);
681                 break;
682         case MAILIMAP_ERROR_INVAL:
683                 log_warning(LOG_PROTOCOL, _("IMAP error on %s: INVAL error\n"), session_server);
684                 break;
685         case MAILIMAP_ERROR_EXTENSION:
686                 log_warning(LOG_PROTOCOL, _("IMAP error on %s: EXTENSION error\n"), session_server);
687                 break;
688         case MAILIMAP_ERROR_SASL:
689                 log_warning(LOG_PROTOCOL, _("IMAP error on %s: SASL error\n"), session_server);
690                 break;
691 #ifdef USE_GNUTLS
692         case MAILIMAP_ERROR_SSL:
693                 log_warning(LOG_PROTOCOL, _("IMAP error on %s: SSL error\n"), session_server);
694                 break;
695 #endif
696         default:
697                 log_warning(LOG_PROTOCOL, _("IMAP error on %s: Unknown error [%d]\n"),
698                         session_server, libetpan_errcode);
699                 break;
700         }
701
702         if (session && is_fatal(libetpan_errcode)) {
703                 imap_disc_session_destroy(IMAP_SESSION(session));
704         } else if (session && !is_fatal(libetpan_errcode)) {
705                 if (IMAP_SESSION(session)->busy)
706                         unlock_session(IMAP_SESSION(session));
707         }
708 }
709
710 static Folder *imap_folder_new(const gchar *name, const gchar *path)
711 {
712         Folder *folder;
713
714         folder = (Folder *)g_new0(IMAPFolder, 1);
715         folder->klass = &imap_class;
716         imap_folder_init(folder, name, path);
717
718         return folder;
719 }
720
721 static void imap_folder_destroy(Folder *folder)
722 {
723         while (imap_folder_get_refcnt(folder) > 0)
724                 gtk_main_iteration();
725         
726         folder_remote_folder_destroy(REMOTE_FOLDER(folder));
727         imap_done(folder);
728 }
729
730 static void imap_folder_init(Folder *folder, const gchar *name,
731                              const gchar *path)
732 {
733         folder_remote_folder_init((Folder *)folder, name, path);
734         IMAP_FOLDER(folder)->max_set_size = IMAP_SET_MAX_COUNT;
735 }
736
737 static FolderItem *imap_folder_item_new(Folder *folder)
738 {
739         IMAPFolderItem *item;
740         
741         item = g_new0(IMAPFolderItem, 1);
742         item->lastuid = 0;
743         item->uid_next = 0;
744         item->uid_list = NULL;
745
746         return (FolderItem *)item;
747 }
748
749 static void imap_folder_item_destroy(Folder *folder, FolderItem *_item)
750 {
751         IMAPFolderItem *item = (IMAPFolderItem *)_item;
752
753         g_return_if_fail(item != NULL);
754         g_slist_free(item->uid_list);
755
756         g_free(_item);
757 }
758
759 static gboolean imap_reset_uid_lists_func(GNode *node, gpointer data)
760 {
761         IMAPFolderItem *item = (IMAPFolderItem *)node->data;
762         
763         item->lastuid = 0;
764         g_slist_free(item->uid_list);
765         item->uid_list = NULL;
766         
767         return FALSE;
768 }
769
770 static void imap_reset_uid_lists(Folder *folder)
771 {
772         if(folder->node == NULL)
773                 return;
774         
775         /* Destroy all uid lists and rest last uid */
776         g_node_traverse(folder->node, G_IN_ORDER, G_TRAVERSE_ALL, -1, imap_reset_uid_lists_func, NULL); 
777 }
778
779 static int imap_get_capabilities(IMAPSession *session)
780 {
781         struct mailimap_capability_data *capabilities = NULL;
782         clistiter *cur;
783         int result = -1;
784
785         if (session->capability != NULL)
786                 return MAILIMAP_NO_ERROR;
787
788         capabilities = imap_threaded_capability(session->folder, &result);
789
790         if (result != MAILIMAP_NO_ERROR) {
791                 return result;
792         }
793
794         if (capabilities == NULL || capabilities->cap_list == NULL) {
795                 return MAILIMAP_NO_ERROR;
796         }
797
798         for(cur = clist_begin(capabilities->cap_list) ; cur != NULL ;
799             cur = clist_next(cur)) {
800                 struct mailimap_capability * cap = 
801                         clist_content(cur);
802                 if (!cap || cap->cap_data.cap_name == NULL)
803                         continue;
804                 session->capability = g_slist_append
805                                 (session->capability,
806                                  g_strdup(cap->cap_data.cap_name));
807                 debug_print("got capa %s\n", cap->cap_data.cap_name);
808         }
809         mailimap_capability_data_free(capabilities);
810         return MAILIMAP_NO_ERROR;
811 }
812
813 static gboolean imap_has_capability(IMAPSession *session, const gchar *cap) 
814 {
815         GSList *cur;
816         for (cur = session->capability; cur; cur = cur->next) {
817                 if (!g_ascii_strcasecmp(cur->data, cap))
818                         return TRUE;
819         }
820         return FALSE;
821 }
822
823 static gint imap_auth(IMAPSession *session, const gchar *user, const gchar *pass,
824                       IMAPAuthType type)
825 {
826         gint ok = MAILIMAP_ERROR_LOGIN;
827         static time_t last_login_err = 0;
828         gchar *ext_info = "";
829         int r;
830         gchar *server = NULL;
831         if ((r = imap_get_capabilities(session)) != MAILIMAP_NO_ERROR) {
832                 imap_handle_error(SESSION(session), r);
833                 return r;
834         }
835         server = g_strdup(SESSION(session)->server);
836         switch(type) {
837         case IMAP_AUTH_ANON:
838                 ok = imap_cmd_login(session, user, pass, "ANONYMOUS");
839                 break;
840         case IMAP_AUTH_CRAM_MD5:
841                 ok = imap_cmd_login(session, user, pass, "CRAM-MD5");
842                 break;
843         case IMAP_AUTH_DIGEST_MD5:
844                 ok = imap_cmd_login(session, user, pass, "DIGEST-MD5");
845                 break;
846         case IMAP_AUTH_LOGIN:
847                 ok = imap_cmd_login(session, user, pass, "LOGIN");
848                 break;
849         case IMAP_AUTH_GSSAPI:
850                 ok = imap_cmd_login(session, user, pass, "GSSAPI");
851                 break;
852         default:
853                 debug_print("capabilities:\n"
854                                 "\t ANONYMOUS %d\n"
855                                 "\t CRAM-MD5 %d\n"
856                                 "\t DIGEST-MD5 %d\n"
857                                 "\t LOGIN %d\n"
858                                 "\t GSSAPI %d\n", 
859                         imap_has_capability(session, "ANONYMOUS"),
860                         imap_has_capability(session, "CRAM-MD5"),
861                         imap_has_capability(session, "DIGEST-MD5"),
862                         imap_has_capability(session, "LOGIN"),
863                         imap_has_capability(session, "GSSAPI"));
864                 if (imap_has_capability(session, "CRAM-MD5"))
865                         ok = imap_cmd_login(session, user, pass, "CRAM-MD5");
866                 if (ok == MAILIMAP_ERROR_LOGIN && imap_has_capability(session, "DIGEST-MD5"))
867                         ok = imap_cmd_login(session, user, pass, "DIGEST-MD5");
868                 if (ok == MAILIMAP_ERROR_LOGIN && imap_has_capability(session, "GSSAPI"))
869                         ok = imap_cmd_login(session, user, pass, "GSSAPI");
870                 if (ok == MAILIMAP_ERROR_LOGIN) /* we always try LOGIN before giving up */
871                         ok = imap_cmd_login(session, user, pass, "LOGIN");
872         }
873
874         if (ok == MAILIMAP_NO_ERROR)
875                 session->authenticated = TRUE;
876         else {
877                 if (type == IMAP_AUTH_CRAM_MD5) {
878                         ext_info = _("\n\nCRAM-MD5 logins only work if libetpan has been "
879                                      "compiled with SASL support and the "
880                                      "CRAM-MD5 SASL plugin is installed.");
881                 } 
882
883                 if (type == IMAP_AUTH_DIGEST_MD5) {
884                         ext_info = _("\n\nDIGEST-MD5 logins only work if libetpan has been "
885                                      "compiled with SASL support and the "
886                                      "DIGEST-MD5 SASL plugin is installed.");
887                 } 
888
889                 if (time(NULL) - last_login_err > 10) {
890                         if (!prefs_common.no_recv_err_panel) {
891                                 alertpanel_error_log(_("Connection to %s failed: "
892                                         "login refused.%s"),
893                                         server, ext_info);
894                         } else {
895                                 log_error(LOG_PROTOCOL, _("Connection to %s failed: "
896                                         "login refused.%s\n"),
897                                         server, ext_info);
898                         }
899                 }
900                 last_login_err = time(NULL);
901         }
902         g_free(server);
903         return ok;
904 }
905
906 static IMAPSession *imap_reconnect_if_possible(Folder *folder, IMAPSession *session)
907 {
908         RemoteFolder *rfolder = REMOTE_FOLDER(folder);
909         /* Check if this is the first try to establish a
910            connection, if yes we don't try to reconnect */
911         debug_print("reconnecting\n");
912         if (rfolder->session == NULL) {
913                 log_warning(LOG_PROTOCOL, _("Connecting to %s failed"),
914                             folder->account->recv_server);
915                 SESSION(session)->sock = NULL;
916                 session_destroy(SESSION(session));
917                 session = NULL;
918         } else {
919                 rfolder->session = NULL;
920                 log_warning(LOG_PROTOCOL, _("IMAP4 connection to %s has been"
921                             " disconnected. Reconnecting...\n"),
922                             folder->account->recv_server);
923                 statusbar_print_all(_("IMAP4 connection to %s has been"
924                             " disconnected. Reconnecting...\n"),
925                             folder->account->recv_server);
926                 SESSION(session)->state = SESSION_DISCONNECTED;
927                 SESSION(session)->sock = NULL;
928                 session_destroy(SESSION(session));
929                 /* Clear folders session to make imap_session_get create
930                    a new session, because of rfolder->session == NULL
931                    it will not try to reconnect again and so avoid an
932                    endless loop */
933                 debug_print("getting session...\n");
934                 session = imap_session_get(folder);
935                 rfolder->session = SESSION(session);
936                 statusbar_pop_all();
937         }
938         return session;
939 }
940
941 static IMAPSession *imap_session_get(Folder *folder)
942 {
943         RemoteFolder *rfolder = REMOTE_FOLDER(folder);
944         IMAPSession *session = NULL;
945         gint r = MAILIMAP_NO_ERROR;
946
947         g_return_val_if_fail(folder != NULL, NULL);
948         g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, NULL);
949         g_return_val_if_fail(folder->account != NULL, NULL);
950         
951         if (prefs_common.work_offline && 
952             !inc_offline_should_override(FALSE,
953                 _("Claws Mail needs network access in order "
954                   "to access the IMAP server."))) {
955                 return NULL;
956         }
957
958         /* Make sure we have a session */
959         if (rfolder->session != NULL && rfolder->session->state != SESSION_DISCONNECTED) {
960                 session = IMAP_SESSION(rfolder->session);
961         } else if (rfolder->session != NULL && rfolder->session->state == SESSION_DISCONNECTED) {
962                 session_destroy(SESSION(rfolder->session));
963                 rfolder->session = NULL;
964                 goto new_conn;
965         } else if (rfolder->connecting) {
966                 debug_print("already connecting\n");
967                 return NULL;
968         } else {
969 new_conn:
970                 imap_reset_uid_lists(folder);
971                 if (time(NULL) - rfolder->last_failure <= 2)
972                         return NULL;
973                 rfolder->connecting = TRUE;
974                 session = imap_session_new(folder, folder->account);
975         }
976         if(session == NULL) {
977                 rfolder->last_failure = time(NULL);
978                 rfolder->connecting = FALSE;
979                 return NULL;
980         }
981
982         /* Make sure session is authenticated */
983         if (!IMAP_SESSION(session)->authenticated)
984                 r = imap_session_authenticate(IMAP_SESSION(session), folder->account);
985         
986         if (r != MAILIMAP_NO_ERROR || (!is_fatal(r) && !IMAP_SESSION(session)->authenticated)) {
987                 rfolder->session = NULL;
988                 if (!is_fatal(r)) {
989                         imap_threaded_disconnect(session->folder);
990                         SESSION(session)->state = SESSION_DISCONNECTED;
991                         SESSION(session)->sock = NULL;
992                         session_destroy(SESSION(session));
993                 }
994                 rfolder->last_failure = time(NULL);
995                 rfolder->connecting = FALSE;
996                 return NULL;
997         }
998
999         /* I think the point of this code is to avoid sending a
1000          * keepalive if we've used the session recently and therefore
1001          * think it's still alive.  Unfortunately, most of the code
1002          * does not yet check for errors on the socket, and so if the
1003          * connection drops we don't notice until the timeout expires.
1004          * A better solution than sending a NOOP every time would be
1005          * for every command to be prepared to retry until it is
1006          * successfully sent. -- mbp */
1007         if ((time(NULL) - SESSION(session)->last_access_time > SESSION_TIMEOUT_INTERVAL) || session->cancelled) {
1008                 /* verify that the session is still alive */
1009                 if ((r = imap_cmd_noop(session)) != MAILIMAP_NO_ERROR) {
1010                         debug_print("disconnected!\n");
1011                         if (!is_fatal(r))
1012                                 session = imap_reconnect_if_possible(folder, session);
1013                         else {
1014                                 rfolder->session = NULL;
1015                                 rfolder->connecting = FALSE;
1016                                 session = imap_session_get(folder);
1017                         }
1018                 }
1019                 if (session)
1020                         session->cancelled = FALSE;
1021         }
1022
1023         rfolder->session = SESSION(session);
1024         rfolder->connecting = FALSE;
1025
1026         return IMAP_SESSION(session);
1027 }
1028
1029 static IMAPSession *imap_session_new(Folder * folder,
1030                                      const PrefsAccount *account)
1031 {
1032         IMAPSession *session;
1033         gushort port;
1034         int r;
1035         int authenticated = FALSE;
1036         
1037 #ifdef USE_GNUTLS
1038         /* FIXME: IMAP over SSL only... */ 
1039         SSLType ssl_type;
1040
1041         port = account->set_imapport ? account->imapport
1042                 : account->ssl_imap == SSL_TUNNEL ? IMAPS_PORT : IMAP4_PORT;
1043         ssl_type = account->ssl_imap;   
1044 #else
1045         if (account->ssl_imap != SSL_NONE) {
1046                 if (alertpanel_full(_("Insecure connection"),
1047                         _("This connection is configured to be secured "
1048                           "using SSL, but SSL is not available in this "
1049                           "build of Claws Mail. \n\n"
1050                           "Do you want to continue connecting to this "
1051                           "server? The communication would not be "
1052                           "secure."),
1053                           GTK_STOCK_CANCEL, _("Con_tinue connecting"), 
1054                           NULL, FALSE, NULL, ALERT_WARNING,
1055                           G_ALERTDEFAULT) != G_ALERTALTERNATE)
1056                         return NULL;
1057         }
1058         port = account->set_imapport ? account->imapport
1059                 : IMAP4_PORT;
1060 #endif
1061
1062         imap_init(folder);
1063         statuswindow_print_all(_("Connecting to IMAP4 server: %s..."), folder->account->recv_server);
1064 #ifndef G_OS_WIN32
1065         if (account->set_tunnelcmd) {
1066                 r = imap_threaded_connect_cmd(folder,
1067                                               account->tunnelcmd,
1068                                               account->recv_server,
1069                                               port);
1070         }
1071         else 
1072 #endif
1073         {
1074 #ifdef USE_GNUTLS
1075                 if (ssl_type == SSL_TUNNEL) {
1076                         r = imap_threaded_connect_ssl(folder,
1077                                                       account->recv_server,
1078                                                       port);
1079                 }
1080                 else 
1081 #endif
1082                 {
1083                         r = imap_threaded_connect(folder,
1084                                                   account->recv_server,
1085                                                   port);
1086                 }
1087         }
1088         
1089         statuswindow_pop_all();
1090         if (r == MAILIMAP_NO_ERROR_AUTHENTICATED) {
1091                 authenticated = TRUE;
1092         }
1093         else if (r == MAILIMAP_NO_ERROR_NON_AUTHENTICATED) {
1094                 authenticated = FALSE;
1095         }
1096         else {
1097 #ifdef USE_GNUTLS
1098                 if (r == MAILIMAP_ERROR_SSL)
1099                         log_error(LOG_PROTOCOL, _("SSL handshake failed\n"));
1100                 else
1101 #endif
1102                         imap_handle_error(NULL, r);
1103
1104                 if(!prefs_common.no_recv_err_panel) {
1105                         alertpanel_error_log(_("Can't connect to IMAP4 server: %s:%d"),
1106                                          account->recv_server, port);
1107                 } else {
1108                         log_error(LOG_PROTOCOL, _("Can't connect to IMAP4 server: %s:%d\n"),
1109                                          account->recv_server, port);
1110                 } 
1111                 
1112                 return NULL;
1113         }
1114         
1115         session = g_new0(IMAPSession, 1);
1116         session_init(SESSION(session), account, FALSE);
1117         SESSION(session)->type             = SESSION_IMAP;
1118         SESSION(session)->server           = g_strdup(account->recv_server);
1119         SESSION(session)->port             = port;
1120         SESSION(session)->sock             = NULL;
1121         
1122         SESSION(session)->destroy          = imap_session_destroy;
1123
1124         session->capability = NULL;
1125         
1126         session->authenticated = authenticated;
1127         session->mbox = NULL;
1128         session->exists = 0;
1129         session->recent = 0;
1130         session->expunge = 0;
1131         session->cmd_count = 0;
1132         session->folder = folder;
1133         IMAP_FOLDER(session->folder)->last_seen_separator = 0;
1134
1135 #ifdef USE_GNUTLS
1136         if (account->ssl_imap == SSL_STARTTLS) {
1137                 gint ok;
1138
1139                 ok = imap_cmd_starttls(session);
1140                 if (ok != MAILIMAP_NO_ERROR) {
1141                         log_warning(LOG_PROTOCOL, _("Can't start TLS session.\n"));
1142                         if (!is_fatal(ok)) {
1143                                 SESSION(session)->sock = NULL;
1144                                 session_destroy(SESSION(session));
1145                         }
1146                         return NULL;
1147                 }
1148
1149                 imap_free_capabilities(session);
1150                 session->authenticated = FALSE;
1151                 session->uidplus = FALSE;
1152                 session->cmd_count = 1;
1153         }
1154 #endif
1155         log_message(LOG_PROTOCOL, "IMAP connection is %s-authenticated\n",
1156                     (session->authenticated) ? "pre" : "un");
1157         
1158         return session;
1159 }
1160
1161 static gint imap_session_authenticate(IMAPSession *session, 
1162                                       PrefsAccount *account)
1163 {
1164         gchar *pass, *acc_pass;
1165         gboolean failed = FALSE;
1166         gint ok = MAILIMAP_NO_ERROR;
1167         g_return_val_if_fail(account->userid != NULL, MAILIMAP_ERROR_BAD_STATE);
1168         acc_pass = account->passwd;
1169 try_again:
1170         pass = acc_pass;
1171         if (!pass && account->imap_auth_type != IMAP_AUTH_ANON && account->imap_auth_type != IMAP_AUTH_GSSAPI) {
1172                 gchar *tmp_pass;
1173                 tmp_pass = input_dialog_query_password_keep(account->recv_server, 
1174                                                             account->userid,
1175                                                             &(account->session_passwd));
1176                 if (!tmp_pass)
1177                         return MAILIMAP_NO_ERROR;
1178                 Xstrdup_a(pass, tmp_pass, {g_free(tmp_pass); return MAILIMAP_NO_ERROR;});
1179                 g_free(tmp_pass);
1180         } else if (account->imap_auth_type == IMAP_AUTH_ANON || account->imap_auth_type == IMAP_AUTH_GSSAPI) {
1181                 pass = "";
1182         }
1183         statuswindow_print_all(_("Connecting to IMAP4 server %s...\n"),
1184                                 account->recv_server);
1185         if ((ok = imap_auth(session, account->userid, pass, account->imap_auth_type)) != MAILIMAP_NO_ERROR) {
1186                 statusbar_pop_all();
1187                 
1188                 if (!failed && !is_fatal(ok)) {
1189                         acc_pass = NULL;
1190                         failed = TRUE;
1191                         if (account->session_passwd != NULL) {
1192                                 g_free(account->session_passwd);
1193                                 account->session_passwd = NULL;
1194                         }
1195                         goto try_again;
1196                 } else {
1197                         if (prefs_common.no_recv_err_panel) {
1198                                 log_error(LOG_PROTOCOL, _("Couldn't login to IMAP server %s.\n"), account->recv_server);
1199                                 mainwindow_show_error();
1200                         } else
1201                                 alertpanel_error_log(_("Couldn't login to IMAP server %s."), account->recv_server);
1202                 }               
1203
1204                 return ok;
1205         } 
1206
1207         statuswindow_pop_all();
1208         session->authenticated = TRUE;
1209         return MAILIMAP_NO_ERROR;
1210 }
1211
1212 static void imap_session_destroy(Session *session)
1213 {
1214         if (session->state != SESSION_DISCONNECTED)
1215                 imap_threaded_disconnect(IMAP_SESSION(session)->folder);
1216         
1217         imap_free_capabilities(IMAP_SESSION(session));
1218         g_free(IMAP_SESSION(session)->mbox);
1219 }
1220
1221 static gchar *imap_fetch_msg(Folder *folder, FolderItem *item, gint uid)
1222 {
1223         return imap_fetch_msg_full(folder, item, uid, TRUE, TRUE);
1224 }
1225
1226 static guint get_file_size_with_crs(const gchar *filename) 
1227 {
1228         FILE *fp = NULL;
1229         guint cnt = 0;
1230         gchar buf[4096];
1231         
1232         if (filename == NULL)
1233                 return -1;
1234         
1235         fp = g_fopen(filename, "rb");
1236         if (!fp)
1237                 return -1;
1238         
1239         while (fgets(buf, sizeof (buf), fp) != NULL) {
1240                 cnt += strlen(buf);
1241                 if (!strstr(buf, "\r\n") && strstr(buf, "\n"))
1242                         cnt++;
1243         }
1244         
1245         fclose(fp);
1246         return cnt;
1247 }
1248
1249 static void imap_remove_cached_msg(Folder *folder, FolderItem *item, MsgInfo *msginfo)
1250 {
1251         gchar *path, *filename;
1252
1253         path = folder_item_get_path(item);
1254
1255         if (!is_dir_exist(path)) {
1256                 g_free(path);
1257                 return;
1258         }
1259
1260         filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(msginfo->msgnum), NULL);
1261         g_free(path);
1262
1263         if (is_file_exist(filename)) {
1264                 claws_unlink(filename);
1265         }
1266         g_free(filename);
1267 }
1268
1269 typedef struct _TagsData {
1270         gchar *str;
1271         GSList *msglist;
1272         IMAPFolderItem *item;
1273 } TagsData;
1274
1275 static void imap_commit_tags(FolderItem *item, MsgInfo *msginfo, GSList *tags_set, GSList *tags_unset)
1276 {
1277         IMAPSession *session;
1278         gint ok, can_create_tags;
1279         Folder *folder = NULL;
1280         TagsData *ht_data = NULL;
1281         GSList *cur;
1282
1283         g_return_if_fail(item != NULL);
1284         g_return_if_fail(msginfo != NULL);
1285
1286         folder = item->folder;
1287         debug_print("getting session...\n");
1288         session = imap_session_get(folder);
1289         
1290         if (!session) {
1291                 debug_print("can't get session\n");
1292                 return;
1293         }
1294
1295         ok = imap_select(session, IMAP_FOLDER(folder), item,
1296                          NULL, NULL, NULL, NULL, &can_create_tags, FALSE);
1297
1298         if (ok != MAILIMAP_NO_ERROR) {
1299                 return;
1300         }
1301
1302         
1303         if (IMAP_FOLDER_ITEM(item)->can_create_flags != ITEM_CAN_CREATE_FLAGS)
1304                 return;
1305         
1306         if (IMAP_FOLDER_ITEM(item)->batching) {
1307                 /* instead of performing an UID STORE command for each message change,
1308                  * as a lot of them can change "together", we just fill in hashtables
1309                  * and defer the treatment so that we're able to send only one
1310                  * command.
1311                  */
1312                 debug_print("IMAP batch mode on, deferring tags change\n");
1313                 for (cur = tags_set; cur; cur = cur->next) {
1314                         gint cur_tag = GPOINTER_TO_INT(cur->data);
1315                         if (cur_tag) {
1316                                 ht_data = g_hash_table_lookup(IMAP_FOLDER_ITEM(item)->tags_set_table, 
1317                                         GINT_TO_POINTER(cur_tag));
1318                                 if (ht_data == NULL) {
1319                                         ht_data = g_new0(TagsData, 1);
1320                                         ht_data->str = g_strdup(tags_get_tag(cur_tag));
1321                                         ht_data->item = IMAP_FOLDER_ITEM(item);
1322                                         g_hash_table_insert(IMAP_FOLDER_ITEM(item)->tags_set_table, 
1323                                                 GINT_TO_POINTER(cur_tag), ht_data);
1324                                 }
1325                                 ht_data->msglist = g_slist_prepend(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum));
1326                         } 
1327                 }
1328                 for (cur = tags_unset; cur; cur = cur->next) {
1329                         gint cur_tag = GPOINTER_TO_INT(cur->data);
1330                         if (cur_tag) {
1331                                 ht_data = g_hash_table_lookup(IMAP_FOLDER_ITEM(item)->tags_unset_table, 
1332                                         GINT_TO_POINTER(cur_tag));
1333                                 if (ht_data == NULL) {
1334                                         ht_data = g_new0(TagsData, 1);
1335                                         ht_data->str = g_strdup(tags_get_tag(cur_tag));
1336                                         ht_data->item = IMAP_FOLDER_ITEM(item);
1337                                         g_hash_table_insert(IMAP_FOLDER_ITEM(item)->tags_unset_table, 
1338                                                 GINT_TO_POINTER(cur_tag), ht_data);
1339                                 }
1340                                 ht_data->msglist = g_slist_prepend(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum));
1341                         }
1342                 }
1343         } else {
1344                 GSList *list_set = NULL;
1345                 GSList *list_unset = NULL;
1346                 GSList numlist;
1347                 
1348                 numlist.data = GINT_TO_POINTER(msginfo->msgnum);
1349                 numlist.next = NULL;
1350         
1351                 debug_print("IMAP changing tags NOW\n");
1352                 for (cur = tags_set; cur; cur = cur->next) {
1353                         gint cur_tag = GPOINTER_TO_INT(cur->data);
1354                         const gchar *str = tags_get_tag(cur_tag);
1355                         if (strcmp(str, "$Forwarded") && strcmp(str, "Junk") && strcmp(str, "NonJunk") && strcmp(str, "NotJunk") && strcmp(str, "NoJunk") && strcmp(str, "Junk") )
1356                                 list_set = g_slist_prepend(list_set, g_strdup(str));
1357                 }
1358                 if (list_set) {
1359                         ok = imap_set_message_flags(session, 
1360                                 IMAP_FOLDER_ITEM(item), &numlist, 0, list_set, TRUE);
1361                         slist_free_strings(list_set);
1362                         g_slist_free(list_set);
1363                         if (ok != MAILIMAP_NO_ERROR) {
1364                                 return;
1365                         }
1366                 }
1367
1368                 for (cur = tags_unset; cur; cur = cur->next) {
1369                         gint cur_tag = GPOINTER_TO_INT(cur->data);
1370                         const gchar *str = tags_get_tag(cur_tag);
1371                         if (strcmp(str, "$Forwarded") && strcmp(str, "Junk") && strcmp(str, "NonJunk") && strcmp(str, "NotJunk") && strcmp(str, "NoJunk") && strcmp(str, "Junk") )
1372                                 list_unset = g_slist_prepend(list_unset, g_strdup(str));
1373                 }
1374                 if (list_unset) {
1375                         ok = imap_set_message_flags(session, 
1376                                 IMAP_FOLDER_ITEM(item), &numlist, 0, list_unset, FALSE);
1377                         slist_free_strings(list_unset);
1378                         g_slist_free(list_unset);
1379                         if (ok != MAILIMAP_NO_ERROR) {
1380                                 return;
1381                         }
1382                 }
1383         }
1384 }
1385
1386 static gchar *imap_fetch_msg_full(Folder *folder, FolderItem *item, gint uid,
1387                                   gboolean headers, gboolean body)
1388 {
1389         gchar *path, *filename;
1390         IMAPSession *session;
1391         gint ok;
1392
1393         g_return_val_if_fail(folder != NULL, NULL);
1394         g_return_val_if_fail(item != NULL, NULL);
1395
1396         if (uid == 0)
1397                 return NULL;
1398
1399         path = folder_item_get_path(item);
1400         if (!is_dir_exist(path))
1401                 make_dir_hier(path);
1402         filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(uid), NULL);
1403         g_free(path);
1404         debug_print("trying to fetch cached %s\n", filename);
1405         if (is_file_exist(filename)) {
1406                 /* see whether the local file represents the whole message
1407                  * or not. As the IMAP server reports size with \r chars,
1408                  * we have to update the local file (UNIX \n only) size */
1409                 MsgInfo *cached = msgcache_get_msg(item->cache,uid);
1410                 guint have_size = -1;
1411
1412                 if (cached)
1413                         debug_print("message %d has been already %scached.\n", uid,
1414                                 MSG_IS_FULLY_CACHED(cached->flags) ? "fully ":"");
1415                 
1416                 if (!cached || !MSG_IS_FULLY_CACHED(cached->flags)) {
1417                         have_size = get_file_size_with_crs(filename);
1418                         if (cached && (cached->size <= have_size || !body)) {
1419                                 procmsg_msginfo_free(cached);
1420                                 ok = file_strip_crs(filename);
1421                                 if (ok == 0 && cached && cached->size <= have_size) {
1422                                         /* we have it all and stripped */
1423                                         debug_print("...fully cached in fact; setting flag.\n");
1424                                         procmsg_msginfo_set_flags(cached, MSG_FULLY_CACHED, 0);
1425                                 }
1426                                 return filename;
1427                         } else if (!cached && time(NULL) - get_file_mtime(filename) < 60) {
1428                                 debug_print("message not cached and file recent, considering file complete\n");
1429                                 ok = file_strip_crs(filename);
1430                                 if (ok == 0)
1431                                         return filename;
1432                         } else {
1433                                 procmsg_msginfo_free(cached);
1434                         }
1435                 }
1436                 if (cached && MSG_IS_FULLY_CACHED(cached->flags)) {
1437                         procmsg_msginfo_free(cached);
1438                         return filename;
1439                 }
1440         } else {
1441                 MsgInfo *cached = msgcache_get_msg(item->cache,uid);
1442                 if (cached) {
1443                         procmsg_msginfo_unset_flags(cached, MSG_FULLY_CACHED, 0);
1444                         procmsg_msginfo_free(cached);
1445                 }
1446         }
1447
1448         debug_print("getting session...\n");
1449         session = imap_session_get(folder);
1450         
1451         if (!session) {
1452                 g_free(filename);
1453                 return NULL;
1454         }
1455         session_set_access_time(SESSION(session));
1456         lock_session(session); /* unlocked later in the function */
1457
1458         debug_print("IMAP fetching messages\n");
1459         ok = imap_select(session, IMAP_FOLDER(folder), item,
1460                          NULL, NULL, NULL, NULL, NULL, FALSE);
1461         if (ok != MAILIMAP_NO_ERROR) {
1462                 g_warning("can't select mailbox %s\n", item->path);
1463                 g_free(filename);
1464                 return NULL;
1465         }
1466
1467         session_set_access_time(SESSION(session));
1468
1469         debug_print("getting message %d...\n", uid);
1470         ok = imap_cmd_fetch(session, (guint32)uid, filename, headers, body);
1471
1472         if (ok != MAILIMAP_NO_ERROR) {
1473                 g_warning("can't fetch message %d\n", uid);
1474                 g_free(filename);
1475                 return NULL;
1476         }
1477
1478         session_set_access_time(SESSION(session));
1479         unlock_session(session);
1480
1481         ok = file_strip_crs(filename);
1482
1483         if (ok == 0 && headers && body) {
1484                 MsgInfo *cached = msgcache_get_msg(item->cache,uid);
1485                 if (cached) {
1486                         procmsg_msginfo_set_flags(cached, MSG_FULLY_CACHED, 0);
1487                         procmsg_msginfo_free(cached);
1488                 }
1489         } else if (ok == -1) {
1490                 MsgInfo *cached = msgcache_get_msg(item->cache,uid);
1491                 if (cached) {
1492                         procmsg_msginfo_unset_flags(cached, MSG_FULLY_CACHED, 0);
1493                         procmsg_msginfo_free(cached);
1494                 }
1495         }
1496         return filename;
1497 }
1498
1499 static gboolean imap_is_msg_fully_cached(Folder *folder, FolderItem *item, gint uid)
1500 {
1501         gchar *path, *filename;
1502         guint size = 0;
1503         MsgInfo *cached = msgcache_get_msg(item->cache,uid);
1504         
1505         if (!cached)
1506                 return FALSE;
1507
1508         if (MSG_IS_FULLY_CACHED(cached->flags)) {
1509                 procmsg_msginfo_free(cached);
1510                 return TRUE;
1511         }
1512         path = folder_item_get_path(item);
1513         if (!is_dir_exist(path))
1514                 return FALSE;
1515
1516         filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(uid), NULL);
1517         g_free(path);
1518         if (is_file_exist(filename)) {
1519                 if (cached && cached->total_size == cached->size) {
1520                         /* fast path */
1521                         g_free(filename);
1522                         procmsg_msginfo_set_flags(cached, MSG_FULLY_CACHED, 0);
1523                         return TRUE;
1524                 }
1525                 size = get_file_size_with_crs(filename);
1526                 g_free(filename);
1527         }
1528         if (cached && size >= cached->size) {
1529                 cached->total_size = cached->size;
1530                 procmsg_msginfo_set_flags(cached, MSG_FULLY_CACHED, 0);
1531                 procmsg_msginfo_free(cached);
1532                 return TRUE;
1533         }
1534         if (cached)
1535                 procmsg_msginfo_free(cached);
1536         return FALSE;   
1537 }
1538
1539 void imap_cache_msg(FolderItem *item, gint msgnum)
1540 {
1541         Folder *folder = NULL;
1542         
1543         if (!item)
1544                 return;
1545         folder = item->folder;
1546         
1547         if (!imap_is_msg_fully_cached(folder, item, msgnum)) {
1548                 gchar *tmp = imap_fetch_msg_full(folder, item, msgnum, TRUE, TRUE);
1549                 debug_print("fetched %s\n", tmp);
1550                 g_free(tmp);
1551         }
1552 }
1553
1554 static gint imap_add_msg(Folder *folder, FolderItem *dest, 
1555                          const gchar *file, MsgFlags *flags)
1556 {
1557         gint ret;
1558         GSList file_list;
1559         MsgFileInfo fileinfo;
1560
1561         g_return_val_if_fail(file != NULL, -1);
1562
1563         fileinfo.msginfo = NULL;
1564         fileinfo.file = (gchar *)file;
1565         fileinfo.flags = flags;
1566         file_list.data = &fileinfo;
1567         file_list.next = NULL;
1568
1569         ret = imap_add_msgs(folder, dest, &file_list, NULL);
1570         return ret;
1571 }
1572
1573 static gint imap_add_msgs(Folder *folder, FolderItem *dest, GSList *file_list,
1574                    GRelation *relation)
1575 {
1576         gchar *destdir;
1577         IMAPSession *session;
1578         guint32 last_uid = 0;
1579         GSList *cur;
1580         MsgFileInfo *fileinfo;
1581         gint ok = MAILIMAP_NO_ERROR;
1582         gint curnum = 0, total = 0;
1583         gboolean missing_uids = FALSE;
1584
1585         g_return_val_if_fail(folder != NULL, -1);
1586         g_return_val_if_fail(dest != NULL, -1);
1587         g_return_val_if_fail(file_list != NULL, -1);
1588         
1589         debug_print("getting session...\n");
1590         session = imap_session_get(folder);
1591         if (!session) {
1592                 return -1;
1593         }
1594         destdir = imap_get_real_path(session, IMAP_FOLDER(folder), dest->path, &ok);
1595         if (is_fatal(ok))
1596                 return -1;
1597         statusbar_print_all(_("Adding messages..."));
1598         total = g_slist_length(file_list);
1599         for (cur = file_list; cur != NULL; cur = cur->next) {
1600                 IMAPFlags iflags = 0;
1601                 guint32 new_uid = 0;
1602                 gchar *real_file = NULL;
1603                 fileinfo = (MsgFileInfo *)cur->data;
1604
1605                 statusbar_progress_all(curnum, total, total < 10 ? 1:10);
1606                 curnum++;
1607
1608                 if (fileinfo->flags) {
1609                         if (MSG_IS_MARKED(*fileinfo->flags))
1610                                 iflags |= IMAP_FLAG_FLAGGED;
1611                         if (MSG_IS_REPLIED(*fileinfo->flags))
1612                                 iflags |= IMAP_FLAG_ANSWERED;
1613                         if (MSG_IS_FORWARDED(*fileinfo->flags))
1614                                 iflags |= IMAP_FLAG_FORWARDED;
1615                         if (MSG_IS_SPAM(*fileinfo->flags))
1616                                 iflags |= IMAP_FLAG_SPAM;
1617                         else
1618                                 iflags |= IMAP_FLAG_HAM;
1619                         if (!MSG_IS_UNREAD(*fileinfo->flags))
1620                                 iflags |= IMAP_FLAG_SEEN;
1621                         
1622                 }
1623                 
1624                 if (real_file == NULL)
1625                         real_file = g_strdup(fileinfo->file);
1626                 
1627                 if (folder_has_parent_of_type(dest, F_QUEUE) ||
1628                     folder_has_parent_of_type(dest, F_OUTBOX) ||
1629                     folder_has_parent_of_type(dest, F_DRAFT) ||
1630                     folder_has_parent_of_type(dest, F_TRASH))
1631                         iflags |= IMAP_FLAG_SEEN;
1632
1633                 ok = imap_cmd_append(session, IMAP_FOLDER_ITEM(dest), destdir, real_file, iflags, 
1634                                      &new_uid);
1635
1636                 if (ok != MAILIMAP_NO_ERROR) {
1637                         g_warning("can't append message %s\n", real_file);
1638                         g_free(real_file);
1639                         g_free(destdir);
1640                         statusbar_progress_all(0,0,0);
1641                         statusbar_pop_all();
1642                         return -1;
1643                 } else {
1644                         debug_print("appended new message as %d\n", new_uid);
1645                         /* put the local file in the imapcache, so that we don't
1646                          * have to fetch it back later. */
1647                         
1648                         if (new_uid == 0) {
1649                                 missing_uids = TRUE;
1650                                 debug_print("Missing UID (0)\n");
1651                         }
1652                         if (new_uid > 0) {
1653                                 gchar *cache_path = folder_item_get_path(dest);
1654                                 if (!is_dir_exist(cache_path))
1655                                         make_dir_hier(cache_path);
1656                                 if (is_dir_exist(cache_path)) {
1657                                         gchar *cache_file = g_strconcat(
1658                                                 cache_path, G_DIR_SEPARATOR_S, 
1659                                                 itos(new_uid), NULL);
1660                                         copy_file(real_file, cache_file, TRUE);
1661                                         debug_print("got UID %d, copied to cache: %s\n", new_uid, cache_file);
1662                                         g_free(cache_file);
1663                                 }
1664                                 g_free(cache_path);
1665                         }
1666                 }
1667
1668                 if (relation != NULL)
1669                         g_relation_insert(relation, fileinfo->msginfo != NULL ? 
1670                                           (gpointer) fileinfo->msginfo : (gpointer) fileinfo,
1671                                           GINT_TO_POINTER(new_uid));
1672                 if (last_uid < new_uid) {
1673                         last_uid = new_uid;
1674                 }
1675
1676                 g_free(real_file);
1677         }
1678         
1679         statusbar_progress_all(0,0,0);
1680         statusbar_pop_all();
1681         
1682         
1683         g_free(destdir);
1684
1685         imap_scan_required(folder, dest);
1686
1687         session = imap_session_get(folder);
1688         if (!session) {
1689                 return -1;
1690         }
1691         if (missing_uids) {
1692                 gint a;
1693                 ok = imap_select(session, IMAP_FOLDER(folder), dest,
1694                          &a, NULL, NULL, NULL, NULL, FALSE);
1695         }
1696         return last_uid;
1697 }
1698
1699 static GSList *flatten_mailimap_set(struct mailimap_set * set) 
1700 {
1701         GSList *result = NULL;
1702         clistiter *list;
1703         int start, end, t;
1704         GSList *cur;
1705
1706         if (!set || !set->set_list)
1707                 return NULL;
1708
1709         for (list = clist_begin(set->set_list); list; list = clist_next(list)) {
1710                 struct mailimap_set_item *item = (struct mailimap_set_item *)clist_content(list);
1711                 start = item->set_first;
1712                 end = item->set_last;
1713                 for (t = start; t <= end; t++) {
1714                         result = g_slist_prepend(result, GINT_TO_POINTER(t));
1715                 }
1716         }
1717         result = g_slist_reverse(result);
1718         if (debug_get_mode()) {
1719                 debug_print("flat imap set: ");
1720                 for (cur = result; cur; cur = cur->next) {
1721                         debug_print("%d ", GPOINTER_TO_INT(cur->data));
1722                 }
1723                 debug_print("\n");
1724         }
1725         
1726         return result;
1727 }
1728 static gint imap_do_copy_msgs(Folder *folder, FolderItem *dest, 
1729                               MsgInfoList *msglist, GRelation *relation)
1730 {
1731         FolderItem *src;
1732         gchar *destdir;
1733         GSList *seq_list, *cur;
1734         MsgInfo *msginfo;
1735         IMAPSession *session;
1736         gint ok = MAILIMAP_NO_ERROR;
1737         GHashTable *uid_hash;
1738         gint last_num = 0;
1739         gboolean single = FALSE;
1740
1741         g_return_val_if_fail(folder != NULL, -1);
1742         g_return_val_if_fail(dest != NULL, -1);
1743         g_return_val_if_fail(msglist != NULL, -1);
1744         
1745         debug_print("getting session...\n");
1746         session = imap_session_get(folder);
1747         
1748         if (!session) {
1749                 return -1;
1750         }
1751
1752         msginfo = (MsgInfo *)msglist->data;
1753         if (msglist->next == NULL)
1754                 single = TRUE;
1755         src = msginfo->folder;
1756         if (src == dest) {
1757                 g_warning("the src folder is identical to the dest.\n");
1758                 return -1;
1759         }
1760
1761         if (src->folder != dest->folder) {
1762                 GSList *infolist = NULL, *cur;
1763                 int res = -1;
1764                 for (cur = msglist; cur; cur = cur->next) {
1765                         msginfo = (MsgInfo *)cur->data;
1766                         MsgFileInfo *fileinfo = g_new0(MsgFileInfo, 1);
1767                         fileinfo->file = procmsg_get_message_file(msginfo);
1768                         fileinfo->flags = &(msginfo->flags);
1769                         infolist = g_slist_prepend(infolist, fileinfo);
1770                 }
1771                 infolist = g_slist_reverse(infolist);
1772                 res = folder_item_add_msgs(dest, infolist, FALSE);
1773                 for (cur = infolist; cur; cur = cur->next) {
1774                         MsgFileInfo *info = (MsgFileInfo *)cur->data;
1775                         g_free(info->file);
1776                         g_free(info);
1777                 }
1778                 g_slist_free(infolist);
1779                 return res;
1780         } 
1781
1782         lock_session(session); /* unlocked later in the function */
1783
1784         ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder,
1785                          NULL, NULL, NULL, NULL, NULL, FALSE);
1786         if (ok != MAILIMAP_NO_ERROR) {
1787                 return ok;
1788         }
1789
1790         unlock_session(session);
1791
1792         destdir = imap_get_real_path(session, IMAP_FOLDER(folder), dest->path, &ok);
1793
1794         if (is_fatal(ok))
1795                 return ok;
1796
1797         seq_list = imap_get_lep_set_from_msglist(IMAP_FOLDER(folder), msglist);
1798         uid_hash = g_hash_table_new(g_direct_hash, g_direct_equal);
1799         
1800         statusbar_print_all(_("Copying messages..."));
1801         for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
1802                 struct mailimap_set * seq_set;
1803                 struct mailimap_set * source = NULL;
1804                 struct mailimap_set * dest = NULL;
1805                 seq_set = cur->data;
1806
1807                 debug_print("Copying messages from %s to %s ...\n",
1808                             src->path, destdir);
1809
1810                 lock_session(session); /* unlocked later in the function */
1811                 ok = imap_cmd_copy(session, seq_set, destdir,
1812                         &source, &dest);
1813                 
1814                 if (is_fatal(ok)) {
1815                         session = NULL;
1816                 }
1817
1818                 if (ok == MAILIMAP_NO_ERROR) {
1819                         unlock_session(session);
1820                         if (relation && source && dest) {
1821                                 GSList *s_list = flatten_mailimap_set(source);
1822                                 GSList *d_list = flatten_mailimap_set(dest);
1823                                 GSList *s_cur, *d_cur;
1824                                 if (g_slist_length(s_list) == g_slist_length(d_list)) {
1825
1826                                         for (s_cur = s_list, d_cur = d_list; 
1827                                              s_cur && d_cur; 
1828                                              s_cur = s_cur->next, d_cur = d_cur->next) {
1829                                                 g_hash_table_insert(uid_hash, s_cur->data, d_cur->data);
1830                                         }
1831
1832                                 } else {
1833                                         debug_print("hhhmm, source list length != dest list length.\n");
1834                                 }
1835                                 g_slist_free(s_list);
1836                                 g_slist_free(d_list);
1837                         }
1838                 }
1839
1840
1841                 if (source)
1842                         mailimap_set_free(source);
1843                 if (dest)
1844                         mailimap_set_free(dest);
1845
1846                 if (ok != MAILIMAP_NO_ERROR) {
1847                         g_hash_table_destroy(uid_hash);
1848                         imap_lep_set_free(seq_list);
1849                         statusbar_pop_all();
1850                         return -1;
1851                 }
1852         }
1853
1854         for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
1855                 MsgInfo *msginfo = (MsgInfo *)cur->data;
1856                 gpointer hashval;
1857
1858                 hashval = g_hash_table_lookup(uid_hash, GINT_TO_POINTER(msginfo->msgnum));
1859                 
1860                 if (hashval != NULL) {
1861                         gint num = GPOINTER_TO_INT(hashval);
1862                         g_relation_insert(relation, msginfo,
1863                                           GINT_TO_POINTER(num));
1864                         if (num > last_num)
1865                                 last_num = num;
1866                         debug_print("copied message %d as %d\n", msginfo->msgnum, num);
1867                         /* put the local file in the imapcache, so that we don't
1868                          * have to fetch it back later. */
1869                         if (num > 0) {
1870                                 gchar *cache_path = folder_item_get_path(msginfo->folder);
1871                                 gchar *real_file = g_strconcat(
1872                                         cache_path, G_DIR_SEPARATOR_S, 
1873                                         itos(msginfo->msgnum), NULL);
1874                                 gchar *cache_file = NULL;
1875                                 g_free(cache_path);
1876                                 cache_path = folder_item_get_path(dest);
1877                                 cache_file = g_strconcat(
1878                                         cache_path, G_DIR_SEPARATOR_S, 
1879                                         itos(num), NULL);
1880                                 if (!is_dir_exist(cache_path))
1881                                         make_dir_hier(cache_path);
1882                                 if (is_file_exist(real_file) && is_dir_exist(cache_path)) {
1883                                         copy_file(real_file, cache_file, TRUE);
1884                                         debug_print("copied to cache: %s\n", cache_file);
1885                                 }
1886                                 g_free(real_file);
1887                                 g_free(cache_file);
1888                                 g_free(cache_path);
1889                         }
1890                 } else
1891                         g_relation_insert(relation, msginfo,
1892                                           GINT_TO_POINTER(0));
1893         }
1894         statusbar_pop_all();
1895
1896         g_hash_table_destroy(uid_hash);
1897         imap_lep_set_free(seq_list);
1898
1899         g_free(destdir);
1900         
1901         IMAP_FOLDER_ITEM(dest)->lastuid = 0;
1902         IMAP_FOLDER_ITEM(dest)->uid_next = 0;
1903         g_slist_free(IMAP_FOLDER_ITEM(dest)->uid_list);
1904         IMAP_FOLDER_ITEM(dest)->uid_list = NULL;
1905
1906         imap_scan_required(folder, dest);
1907         if (ok == MAILIMAP_NO_ERROR)
1908                 return last_num;
1909         else
1910                 return -1;
1911 }
1912
1913 static gint imap_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1914 {
1915         GSList msglist;
1916
1917         g_return_val_if_fail(msginfo != NULL, -1);
1918
1919         msglist.data = msginfo;
1920         msglist.next = NULL;
1921
1922         return imap_copy_msgs(folder, dest, &msglist, NULL);
1923 }
1924
1925 static gint imap_copy_msgs(Folder *folder, FolderItem *dest, 
1926                     MsgInfoList *msglist, GRelation *relation)
1927 {
1928         MsgInfo *msginfo;
1929         gint ret;
1930
1931         g_return_val_if_fail(folder != NULL, -1);
1932         g_return_val_if_fail(dest != NULL, -1);
1933         g_return_val_if_fail(msglist != NULL, -1);
1934
1935         msginfo = (MsgInfo *)msglist->data;
1936         g_return_val_if_fail(msginfo->folder != NULL, -1);
1937
1938         ret = imap_do_copy_msgs(folder, dest, msglist, relation);
1939         return ret;
1940 }
1941
1942
1943 static gint imap_do_remove_msgs(Folder *folder, FolderItem *dest, 
1944                                 MsgInfoList *msglist, GRelation *relation)
1945 {
1946         gchar *destdir, *dir;
1947         GSList *numlist = NULL, *cur;
1948         MsgInfo *msginfo;
1949         IMAPSession *session;
1950         gint ok = MAILIMAP_NO_ERROR;
1951         
1952         g_return_val_if_fail(folder != NULL, -1);
1953         g_return_val_if_fail(dest != NULL, -1);
1954         g_return_val_if_fail(msglist != NULL, -1);
1955
1956         debug_print("getting session...\n");
1957         session = imap_session_get(folder);
1958         if (!session) {
1959                 return -1;
1960         }
1961
1962         lock_session(session); /* unlocked later in the function */
1963
1964         msginfo = (MsgInfo *)msglist->data;
1965
1966         ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder,
1967                          NULL, NULL, NULL, NULL, NULL, FALSE);
1968         if (ok != MAILIMAP_NO_ERROR) {
1969                 return ok;
1970         }
1971
1972         destdir = imap_get_real_path(session, IMAP_FOLDER(folder), dest->path, &ok);
1973         if (is_fatal(ok)) {
1974                 g_free(destdir);
1975                 return ok;
1976         }
1977         for (cur = msglist; cur; cur = cur->next) {
1978                 msginfo = (MsgInfo *)cur->data;
1979                 if (!MSG_IS_DELETED(msginfo->flags))
1980                         numlist = g_slist_prepend(numlist, GINT_TO_POINTER(msginfo->msgnum));
1981         }
1982         numlist = g_slist_reverse(numlist);
1983
1984         if (numlist != NULL) {
1985                 ok = imap_set_message_flags
1986                         (session, IMAP_FOLDER_ITEM(msginfo->folder), numlist, IMAP_FLAG_DELETED, NULL, TRUE);
1987                 if (ok != MAILIMAP_NO_ERROR) {
1988                         log_warning(LOG_PROTOCOL, _("can't set deleted flags\n"));
1989                         g_free(destdir);
1990                         return ok;
1991                 }
1992         } /* else we just need to expunge */
1993         ok = imap_cmd_expunge(session, folder->account->imap_use_trash);
1994         if (ok != MAILIMAP_NO_ERROR) {
1995                 log_warning(LOG_PROTOCOL, _("can't expunge\n"));
1996                 g_free(destdir);
1997                 return ok;
1998         }
1999         
2000         session->folder_content_changed = TRUE;
2001         unlock_session(session);
2002
2003         dir = folder_item_get_path(msginfo->folder);
2004         if (is_dir_exist(dir)) {
2005                 for (cur = msglist; cur; cur = cur->next) {
2006                         msginfo = (MsgInfo *)cur->data;
2007                         remove_numbered_files(dir, msginfo->msgnum, msginfo->msgnum);
2008                 }
2009         }
2010         g_free(dir);
2011
2012         g_slist_free(numlist);
2013
2014         imap_scan_required(folder, dest);
2015
2016         g_free(destdir);
2017         if (ok == MAILIMAP_NO_ERROR)
2018                 return 0;
2019         else
2020                 return -1;
2021 }
2022
2023 static gint imap_remove_msgs(Folder *folder, FolderItem *dest, 
2024                     MsgInfoList *msglist, GRelation *relation)
2025 {
2026         MsgInfo *msginfo;
2027
2028         g_return_val_if_fail(folder != NULL, -1);
2029         g_return_val_if_fail(dest != NULL, -1);
2030         if (msglist == NULL)
2031                 return 0;
2032
2033         msginfo = (MsgInfo *)msglist->data;
2034         g_return_val_if_fail(msginfo->folder != NULL, -1);
2035
2036         return imap_do_remove_msgs(folder, dest, msglist, relation);
2037 }
2038
2039 static gint imap_remove_all_msg(Folder *folder, FolderItem *item)
2040 {
2041         GSList *list = folder_item_get_msg_list(item);
2042         gint res = imap_remove_msgs(folder, item, list, NULL);
2043         procmsg_msg_list_free(list);
2044         return res;
2045 }
2046
2047 static gboolean imap_is_msg_changed(Folder *folder, FolderItem *item,
2048                                     MsgInfo *msginfo)
2049 {
2050         /* TODO: properly implement this method */
2051         return FALSE;
2052 }
2053
2054 static gint imap_close(Folder *folder, FolderItem *item)
2055 {
2056         return 0;
2057 }
2058
2059 static gint imap_scan_tree_real(Folder *folder, gboolean subs_only)
2060 {
2061         FolderItem *item = NULL;
2062         IMAPSession *session;
2063         gchar *root_folder = NULL;
2064
2065         g_return_val_if_fail(folder != NULL, -1);
2066         g_return_val_if_fail(folder->account != NULL, -1);
2067
2068         debug_print("getting session...\n");
2069         session = imap_session_get(folder);
2070         if (!session) {
2071                 if (!folder->node) {
2072                         folder_tree_destroy(folder);
2073                         item = folder_item_new(folder, folder->name, NULL);
2074                         item->folder = folder;
2075                         folder->node = item->node = g_node_new(item);
2076                 }
2077                 return -1;
2078         }
2079
2080         if (folder->account->imap_dir && *folder->account->imap_dir) {
2081                 gchar *real_path;
2082                 int r = MAILIMAP_NO_ERROR;
2083                 clist * lep_list;
2084
2085                 Xstrdup_a(root_folder, folder->account->imap_dir, {return -1;});
2086                 extract_quote(root_folder, '"');
2087                 subst_char(root_folder,
2088                            imap_get_path_separator(session, IMAP_FOLDER(folder),
2089                                                    root_folder, &r),
2090                            '/');
2091                 if (is_fatal(r))
2092                         return -1;
2093                 strtailchomp(root_folder, '/');
2094                 real_path = imap_get_real_path
2095                         (session, IMAP_FOLDER(folder), root_folder, &r);
2096                 if (is_fatal(r))
2097                         return -1;
2098                 debug_print("IMAP root directory: %s\n", real_path);
2099
2100                 /* check if root directory exist */
2101
2102                 r = imap_threaded_list(session->folder, "", real_path,
2103                                        &lep_list);
2104
2105                 if (r != MAILIMAP_NO_ERROR)
2106                         imap_handle_error(SESSION(session), r);
2107
2108                 if ((r != MAILIMAP_NO_ERROR) || (clist_count(lep_list) == 0)) {
2109                         if (!folder->node) {
2110                                 item = folder_item_new(folder, folder->name, NULL);
2111                                 item->folder = folder;
2112                                 folder->node = item->node = g_node_new(item);
2113                         }
2114                         return -1;
2115                 }
2116                 mailimap_list_result_free(lep_list);
2117                                 
2118                 g_free(real_path);
2119         }
2120
2121         if (folder->node)
2122                 item = FOLDER_ITEM(folder->node->data);
2123                 
2124         if (item && !item->path && root_folder) {
2125                 item->path = g_strdup(root_folder);
2126         }
2127
2128         if (!item || ((item->path || root_folder) &&
2129                       strcmp2(item->path, root_folder) != 0)) {
2130                 folder_tree_destroy(folder);
2131                 item = folder_item_new(folder, folder->name, root_folder);
2132                 item->folder = folder;
2133                 folder->node = item->node = g_node_new(item);
2134         }
2135
2136         imap_scan_tree_recursive(session, FOLDER_ITEM(folder->node->data), subs_only);
2137         imap_create_missing_folders(folder);
2138
2139         return 0;
2140 }
2141
2142 static gint imap_scan_tree(Folder *folder)
2143 {
2144         gboolean subs_only = FALSE;
2145         if (folder->account) {
2146                 debug_print(" scanning only subs %d\n", folder->account->imap_subsonly);
2147                 subs_only = folder->account->imap_subsonly;
2148         }
2149         return imap_scan_tree_real(folder, subs_only);
2150 }
2151
2152 static gint imap_scan_tree_recursive(IMAPSession *session, FolderItem *item, gboolean subs_only)
2153 {
2154         Folder *folder;
2155         IMAPFolder *imapfolder;
2156         FolderItem *new_item;
2157         GSList *item_list, *cur;
2158         GNode *node;
2159         gchar *real_path;
2160         gchar *wildcard_path;
2161         gchar separator;
2162         gchar wildcard[3];
2163         clist * lep_list;
2164         int r = MAILIMAP_NO_ERROR;
2165         
2166         g_return_val_if_fail(item != NULL, -1);
2167         g_return_val_if_fail(item->folder != NULL, -1);
2168         g_return_val_if_fail(item->no_sub == FALSE, -1);
2169
2170         folder = item->folder;
2171         imapfolder = IMAP_FOLDER(folder);
2172
2173         separator = imap_get_path_separator(session, imapfolder, item->path, &r);
2174         if (is_fatal(r))
2175                 return r;
2176
2177         if (folder->ui_func)
2178                 folder->ui_func(folder, item, folder->ui_func_data);
2179
2180         if (item->path) {
2181                 wildcard[0] = separator;
2182                 wildcard[1] = '%';
2183                 wildcard[2] = '\0';
2184                 real_path = imap_get_real_path(session, imapfolder, item->path, &r);
2185                 if (is_fatal(r)) {
2186                         g_free(real_path);
2187                         return r;
2188                 }
2189         } else {
2190                 wildcard[0] = '%';
2191                 wildcard[1] = '\0';
2192                 real_path = g_strdup("");
2193         }
2194
2195         Xstrcat_a(wildcard_path, real_path, wildcard,
2196                   {g_free(real_path); return MAILIMAP_ERROR_BAD_STATE;});
2197         lep_list = NULL;
2198         
2199         if (subs_only)
2200                 r = imap_threaded_lsub(folder, "", wildcard_path, &lep_list);
2201         else
2202                 r = imap_threaded_list(folder, "", wildcard_path, &lep_list);
2203
2204         if (r != MAILIMAP_NO_ERROR) {
2205                 imap_handle_error(SESSION(session), r);
2206                 item_list = NULL;
2207                 g_free(real_path);
2208                 return r;
2209         }
2210         else {
2211                 item_list = imap_list_from_lep(imapfolder,
2212                                                lep_list, real_path, FALSE);
2213                 mailimap_list_result_free(lep_list);
2214         }
2215         
2216         g_free(real_path);
2217
2218         node = item->node->children;
2219         while (node != NULL) {
2220                 FolderItem *old_item = FOLDER_ITEM(node->data);
2221                 GNode *next = node->next;
2222
2223                 new_item = NULL;
2224                 for (cur = item_list; cur != NULL; cur = cur->next) {
2225                         FolderItem *cur_item = FOLDER_ITEM(cur->data);
2226                         if (!strcmp2(old_item->path, cur_item->path)) {
2227                                 new_item = cur_item;
2228                                 break;
2229                         }
2230                 }
2231                 if (!new_item) {
2232                         if (old_item && old_item->path && !strcmp(old_item->path, "INBOX")) {
2233                                 debug_print("not removing INBOX\n");
2234                         } else {
2235                                 debug_print("folder '%s' not found. removing...\n",
2236                                             old_item->path);
2237                                 folder_item_remove(old_item);
2238                         }
2239                 } else {
2240                         old_item->no_sub = new_item->no_sub;
2241                         old_item->no_select = new_item->no_select;
2242                         if (old_item->no_sub == TRUE && node->children) {
2243                                 debug_print("folder '%s' doesn't have "
2244                                             "subfolders. removing...\n",
2245                                             old_item->path);
2246                                 folder_item_remove_children(old_item);
2247                         }
2248                 }
2249
2250                 node = next;
2251         }
2252
2253         for (cur = item_list; cur != NULL; cur = cur->next) {
2254                 FolderItem *cur_item = FOLDER_ITEM(cur->data);
2255                 new_item = NULL;
2256
2257                 for (node = item->node->children; node != NULL;
2258                      node = node->next) {
2259                         if (!strcmp2(FOLDER_ITEM(node->data)->path,
2260                                      cur_item->path)) {
2261                                 new_item = FOLDER_ITEM(node->data);
2262                                 folder_item_destroy(cur_item);
2263                                 cur_item = NULL;
2264                                 break;
2265                         }
2266                 }
2267                 if (!new_item) {
2268                         new_item = cur_item;
2269                         debug_print("new folder '%s' found.\n", new_item->path);
2270                         folder_item_append(item, new_item);
2271                 }
2272
2273                 if (!strcmp(new_item->path, "INBOX")) {
2274                         new_item->stype = F_INBOX;
2275                         folder->inbox = new_item;
2276                 } else if (!folder_item_parent(item) || item->stype == F_INBOX) {
2277                         gchar *base;
2278
2279                         base = g_path_get_basename(new_item->path);
2280
2281                         if (!folder->outbox && !g_ascii_strcasecmp(base, "Sent")) {
2282                                 new_item->stype = F_OUTBOX;
2283                                 folder->outbox = new_item;
2284                         } else if (!folder->draft && !g_ascii_strcasecmp(base, "Drafts")) {
2285                                 new_item->stype = F_DRAFT;
2286                                 folder->draft = new_item;
2287                         } else if (!folder->queue && !g_ascii_strcasecmp(base, "Queue")) {
2288                                 new_item->stype = F_QUEUE;
2289                                 folder->queue = new_item;
2290                         } else if (!folder->trash && !g_ascii_strcasecmp(base, "Trash")) {
2291                                 new_item->stype = F_TRASH;
2292                                 folder->trash = new_item;
2293                         }
2294                         g_free(base);
2295                 }
2296
2297                 if (new_item->no_sub == FALSE)
2298                         imap_scan_tree_recursive(session, new_item, subs_only);
2299         }
2300
2301         g_slist_free(item_list);
2302
2303         return MAILIMAP_NO_ERROR;
2304 }
2305
2306 GList *imap_scan_subtree(Folder *folder, FolderItem *item, gboolean unsubs_only, gboolean recursive)
2307 {
2308         IMAPSession *session = imap_session_get(folder);
2309         gchar *real_path;
2310         gchar *wildcard_path;
2311         gchar separator;
2312         gchar wildcard[3];
2313         clist * lep_list;
2314         GSList *item_list = NULL, *cur;
2315         GList *child_list = NULL, *tmplist = NULL;
2316         GSList *sub_list = NULL;
2317         int r = MAILIMAP_NO_ERROR;
2318
2319         if (!session)
2320                 return NULL;
2321
2322         separator = imap_get_path_separator(session, IMAP_FOLDER(folder), item->path, &r);
2323         if (is_fatal(r))
2324                 return NULL;
2325
2326         if (item->path) {
2327                 wildcard[0] = separator;
2328                 wildcard[1] = '%';
2329                 wildcard[2] = '\0';
2330                 real_path = imap_get_real_path(session, IMAP_FOLDER(folder), item->path, &r);
2331                 if (is_fatal(r)) {
2332                         g_free(real_path);
2333                         return NULL;
2334                 }
2335         } else {
2336                 wildcard[0] = '%';
2337                 wildcard[1] = '\0';
2338                 real_path = g_strdup("");
2339         }
2340
2341         Xstrcat_a(wildcard_path, real_path, wildcard,
2342                   {g_free(real_path); return NULL;});
2343         lep_list = NULL;
2344         
2345         if (unsubs_only)
2346                 statusbar_print_all(_("Looking for unsubscribed folders in %s..."), 
2347                                 item->path?item->path:item->name);
2348         else
2349                 statusbar_print_all(_("Looking for subfolders of %s..."), 
2350                                 item->path?item->path:item->name);
2351
2352         r = imap_threaded_list(folder, "", wildcard_path, &lep_list);
2353         if (r) {
2354                 g_free(real_path);
2355                 statusbar_pop_all();
2356                 return NULL;
2357         }
2358         item_list = imap_list_from_lep(IMAP_FOLDER(folder),
2359                                lep_list, real_path, FALSE);
2360         mailimap_list_result_free(lep_list);
2361
2362         for (cur = item_list; cur != NULL; cur = cur->next) {
2363                 FolderItem *cur_item = FOLDER_ITEM(cur->data);
2364                 if (recursive) {
2365                         tmplist = imap_scan_subtree(folder, cur_item, 
2366                                         unsubs_only, recursive);
2367                         if (tmplist)
2368                                 child_list = g_list_concat(child_list, tmplist);
2369                 }
2370                 child_list = g_list_prepend(child_list,
2371                                 imap_get_real_path(session, 
2372                                         IMAP_FOLDER(folder), cur_item->path, &r));
2373                 if (is_fatal(r)) {
2374                         g_free(real_path);
2375                         statusbar_pop_all();
2376                         return NULL;
2377                 }
2378                 folder_item_destroy(cur_item);
2379         }
2380         child_list = g_list_reverse(child_list);
2381         g_slist_free(item_list);
2382
2383         if (unsubs_only) {
2384                 r = imap_threaded_lsub(folder, "", wildcard_path, &lep_list);
2385                 if (r) {
2386                         g_free(real_path);
2387                         statusbar_pop_all();
2388                         return NULL;
2389                 }
2390                 sub_list = imap_list_from_lep(IMAP_FOLDER(folder),
2391                                        lep_list, real_path, FALSE);
2392                 mailimap_list_result_free(lep_list);
2393
2394                 for (cur = sub_list; cur != NULL; cur = cur->next) {
2395                         FolderItem *cur_item = FOLDER_ITEM(cur->data);
2396                         GList *oldlitem = NULL;
2397                         gchar *tmp = imap_get_real_path(session, 
2398                                         IMAP_FOLDER(folder), cur_item->path, &r);
2399                         if (r) {
2400                                 g_free(real_path);
2401                                 statusbar_pop_all();
2402                                 return NULL;
2403                         }
2404                         folder_item_destroy(cur_item);
2405                         oldlitem = g_list_find_custom(
2406                                         child_list, tmp, (GCompareFunc)strcmp2);
2407                         if (oldlitem) {
2408                                 child_list = g_list_remove_link(child_list, oldlitem);
2409                                 g_free(oldlitem->data);
2410                                 g_list_free(oldlitem);
2411                         }
2412                         g_free(tmp);
2413                 }
2414         }
2415
2416         g_free(real_path);
2417         statusbar_pop_all();
2418
2419         return child_list;
2420 }
2421
2422 static gint imap_create_tree(Folder *folder)
2423 {
2424         g_return_val_if_fail(folder != NULL, -1);
2425         g_return_val_if_fail(folder->node != NULL, -1);
2426         g_return_val_if_fail(folder->node->data != NULL, -1);
2427         g_return_val_if_fail(folder->account != NULL, -1);
2428
2429         imap_scan_tree(folder);
2430         imap_create_missing_folders(folder);
2431
2432         return 0;
2433 }
2434
2435 static void imap_create_missing_folders(Folder *folder)
2436 {
2437         g_return_if_fail(folder != NULL);
2438
2439         if (!folder->inbox)
2440                 folder->inbox = imap_create_special_folder
2441                         (folder, F_INBOX, "INBOX");
2442         if (!folder->trash)
2443                 folder->trash = imap_create_special_folder
2444                         (folder, F_TRASH, "Trash");
2445         if (!folder->queue)
2446                 folder->queue = imap_create_special_folder
2447                         (folder, F_QUEUE, "Queue");
2448         if (!folder->outbox)
2449                 folder->outbox = imap_create_special_folder
2450                         (folder, F_OUTBOX, "Sent");
2451         if (!folder->draft)
2452                 folder->draft = imap_create_special_folder
2453                         (folder, F_DRAFT, "Drafts");
2454 }
2455
2456 static FolderItem *imap_create_special_folder(Folder *folder,
2457                                               SpecialFolderItemType stype,
2458                                               const gchar *name)
2459 {
2460         FolderItem *item;
2461         FolderItem *new_item;
2462
2463         g_return_val_if_fail(folder != NULL, NULL);
2464         g_return_val_if_fail(folder->node != NULL, NULL);
2465         g_return_val_if_fail(folder->node->data != NULL, NULL);
2466         g_return_val_if_fail(folder->account != NULL, NULL);
2467         g_return_val_if_fail(name != NULL, NULL);
2468
2469         item = FOLDER_ITEM(folder->node->data);
2470         new_item = imap_create_folder(folder, item, name);
2471
2472         if (!new_item) {
2473                 g_warning("Can't create '%s'\n", name);
2474                 if (!folder->inbox) return NULL;
2475
2476                 new_item = imap_create_folder(folder, folder->inbox, name);
2477                 if (!new_item)
2478                         g_warning("Can't create '%s' under INBOX\n", name);
2479                 else
2480                         new_item->stype = stype;
2481         } else
2482                 new_item->stype = stype;
2483
2484         return new_item;
2485 }
2486
2487 static gchar *imap_folder_get_path(Folder *folder)
2488 {
2489         gchar *folder_path;
2490
2491         g_return_val_if_fail(folder != NULL, NULL);
2492         g_return_val_if_fail(folder->account != NULL, NULL);
2493
2494         folder_path = g_strconcat(get_imap_cache_dir(),
2495                                   G_DIR_SEPARATOR_S,
2496                                   folder->account->recv_server,
2497                                   G_DIR_SEPARATOR_S,
2498                                   folder->account->userid,
2499                                   NULL);
2500
2501         return folder_path;
2502 }
2503
2504 #ifdef G_OS_WIN32
2505 static gchar *imap_encode_unsafe_chars(const gchar *str)
2506 {
2507         gchar *ret = NULL, *o_ret;
2508         gchar *i;
2509         if (!str) 
2510                 return NULL;
2511         ret = g_malloc(3*strlen(str)+1);
2512         o_ret = ret;
2513         for (i = str; *i; i++) {
2514                 switch(*i) {
2515                         case ':':
2516                         case '|':
2517                         case '<':
2518                         case '>':
2519                         case '*':
2520                         case '?':
2521                         case '#':
2522                                 *ret++ = '%';
2523                                 *ret++ = '0'+(*i/10);
2524                                 *ret++ = '0'+(*i%10);
2525                                 break;
2526                         default:
2527                                 *ret++ = *i;
2528                 }
2529         }
2530         *ret++ = '\0';
2531         return o_ret;
2532 }
2533 #endif
2534 static gchar *imap_item_get_path(Folder *folder, FolderItem *item)
2535 {
2536         gchar *folder_path, *path;
2537         gchar *item_path = NULL;
2538         
2539         g_return_val_if_fail(folder != NULL, NULL);
2540         g_return_val_if_fail(item != NULL, NULL);
2541         folder_path = imap_folder_get_path(folder);
2542
2543         g_return_val_if_fail(folder_path != NULL, NULL);
2544
2545 #ifdef G_OS_UNIX
2546         item_path = g_strdup(item->path);
2547 #else
2548         item_path = imap_encode_unsafe_chars(item->path);
2549 #endif  
2550
2551         if (g_path_is_absolute(folder_path)) {
2552                 if (item_path)
2553                         path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
2554                                            item_path, NULL);
2555                 else
2556                         path = g_strdup(folder_path);
2557         } else {
2558                 if (item_path)
2559                         path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
2560                                            folder_path, G_DIR_SEPARATOR_S,
2561                                            item_path, NULL);
2562                 else
2563                         path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
2564                                            folder_path, NULL);
2565         }
2566         g_free(folder_path);
2567         g_free(item_path);
2568 #ifdef G_OS_WIN32
2569         while (strchr(path, '/'))
2570                 *strchr(path, '/') = '\\';
2571 #endif
2572
2573         return path;
2574 }
2575
2576 static FolderItem *imap_create_folder(Folder *folder, FolderItem *parent,
2577                                const gchar *name)
2578 {
2579         gchar *dirpath, *imap_path;
2580         IMAPSession *session;
2581         FolderItem *new_item;
2582         gchar separator;
2583         gchar *new_name;
2584         const gchar *p;
2585         gint ok = MAILIMAP_NO_ERROR;
2586         gboolean no_select = FALSE, no_sub = FALSE;
2587         gboolean exist = FALSE;
2588         
2589         g_return_val_if_fail(folder != NULL, NULL);
2590         g_return_val_if_fail(folder->account != NULL, NULL);
2591         g_return_val_if_fail(parent != NULL, NULL);
2592         g_return_val_if_fail(name != NULL, NULL);
2593
2594         debug_print("getting session...\n");
2595         session = imap_session_get(folder);
2596         if (!session) {
2597                 return NULL;
2598         }
2599
2600         if (!folder_item_parent(parent) && strcmp(name, "INBOX") == 0) {
2601                 dirpath = g_strdup(name);
2602         }else if (parent->path)
2603                 dirpath = g_strconcat(parent->path, "/", name, NULL);
2604         else if ((p = strchr(name, '/')) != NULL && *(p + 1) != '\0')
2605                 dirpath = g_strdup(name);
2606         else if (folder->account->imap_dir && *folder->account->imap_dir) {
2607                 gchar *imap_dir;
2608
2609                 Xstrdup_a(imap_dir, folder->account->imap_dir, {return NULL;});
2610                 strtailchomp(imap_dir, '/');
2611                 dirpath = g_strconcat(imap_dir, "/", name, NULL);
2612         } else
2613                 dirpath = g_strdup(name);
2614                 
2615         
2616
2617         /* keep trailing directory separator to create a folder that contains
2618            sub folder */
2619         imap_path = imap_utf8_to_modified_utf7(dirpath, FALSE);
2620
2621         strtailchomp(dirpath, '/');
2622         Xstrdup_a(new_name, name, {
2623                 g_free(dirpath); 
2624                 g_free(imap_path);
2625                 return NULL;});
2626
2627         separator = imap_get_path_separator(session, IMAP_FOLDER(folder), imap_path, &ok);
2628         if (is_fatal(ok)) {
2629                 g_free(imap_path);
2630                 return NULL;
2631         }
2632         imap_path_separator_subst(imap_path, separator);
2633         /* remove trailing / for display */
2634         strtailchomp(new_name, '/');
2635
2636         if (strcmp(dirpath, "INBOX") != 0) {
2637                 GPtrArray *argbuf;
2638                 int r;
2639                 clist * lep_list;
2640                 
2641                 argbuf = g_ptr_array_new();
2642                 r = imap_threaded_list(folder, "", imap_path, &lep_list);
2643                 if (r != MAILIMAP_NO_ERROR) {
2644                         imap_handle_error(SESSION(session), r);
2645                         log_warning(LOG_PROTOCOL, _("can't create mailbox: LIST failed\n"));
2646                         g_free(imap_path);
2647                         g_free(dirpath);
2648                         ptr_array_free_strings(argbuf);
2649                         g_ptr_array_free(argbuf, TRUE);
2650                         return NULL;
2651                 }
2652                 
2653                 if (clist_count(lep_list) > 0)
2654                         exist = TRUE;
2655                 mailimap_list_result_free(lep_list);
2656                 lep_list = NULL;
2657                 if (!exist) {
2658                         ok = imap_cmd_create(session, imap_path);
2659                         if (ok != MAILIMAP_NO_ERROR) {
2660                                 log_warning(LOG_PROTOCOL, _("can't create mailbox\n"));
2661                                 g_free(imap_path);
2662                                 g_free(dirpath);
2663                                 return NULL;
2664                         }
2665                         r = imap_threaded_list(folder, "", imap_path, &lep_list);
2666                         if (r == MAILIMAP_NO_ERROR) {
2667                                 GSList *item_list = imap_list_from_lep(IMAP_FOLDER(folder),
2668                                                lep_list, dirpath, TRUE);
2669                                 if (item_list) {
2670                                         FolderItem *cur_item = FOLDER_ITEM(item_list->data);
2671                                         no_select = cur_item->no_select;
2672                                         no_sub = cur_item->no_sub;
2673                                         g_slist_free(item_list);
2674                                 } 
2675                                 mailimap_list_result_free(lep_list);
2676                         } else {
2677                                 imap_handle_error(SESSION(session), r);
2678                         }
2679                 }
2680                 imap_threaded_subscribe(folder, imap_path, TRUE);
2681         } else {
2682                 clist *lep_list;
2683                 int r;
2684                 /* just get flags */
2685                 r = imap_threaded_list(folder, "", "INBOX", &lep_list);
2686                 if (r == MAILIMAP_NO_ERROR) {
2687                         GSList *item_list = imap_list_from_lep(IMAP_FOLDER(folder),
2688                                        lep_list, dirpath, TRUE);
2689                         if (item_list) {
2690                                 FolderItem *cur_item = FOLDER_ITEM(item_list->data);
2691                                 no_select = cur_item->no_select;
2692                                 no_sub = cur_item->no_sub;
2693                                 g_slist_free(item_list);
2694                         } 
2695                         mailimap_list_result_free(lep_list);
2696                 } else {
2697                         imap_handle_error(SESSION(session), r);
2698                 }
2699         }
2700
2701         new_item = folder_item_new(folder, new_name, dirpath);
2702         new_item->no_select = no_select;
2703         new_item->no_sub = no_sub;
2704         folder_item_append(parent, new_item);
2705         g_free(imap_path);
2706         g_free(dirpath);
2707
2708         dirpath = folder_item_get_path(new_item);
2709         if (!is_dir_exist(dirpath))
2710                 make_dir_hier(dirpath);
2711         g_free(dirpath);
2712
2713         if (exist) {
2714                 /* folder existed, scan it */
2715                 imap_scan_required(folder, new_item);
2716                 folder_item_scan_full(new_item, FALSE);
2717         }
2718
2719         return new_item;
2720 }
2721
2722 static gint imap_rename_folder(Folder *folder, FolderItem *item,
2723                                const gchar *name)
2724 {
2725         gchar *dirpath;
2726         gchar *newpath;
2727         gchar *real_oldpath;
2728         gchar *real_newpath;
2729         gchar *paths[2];
2730         gchar *old_cache_dir;
2731         gchar *new_cache_dir;
2732         IMAPSession *session;
2733         gchar separator;
2734         gint ok = MAILIMAP_NO_ERROR;
2735         gint exists, recent, unseen;
2736         guint32 uid_validity;
2737
2738         g_return_val_if_fail(folder != NULL, -1);
2739         g_return_val_if_fail(item != NULL, -1);
2740         g_return_val_if_fail(item->path != NULL, -1);
2741         g_return_val_if_fail(name != NULL, -1);
2742
2743         debug_print("getting session...\n");
2744         session = imap_session_get(folder);
2745         if (!session) {
2746                 return -1;
2747         }
2748
2749         if (strchr(name, imap_get_path_separator(session, IMAP_FOLDER(folder), item->path, &ok)) != NULL ||
2750                 is_fatal(ok)) {
2751                 g_warning(_("New folder name must not contain the namespace "
2752                             "path separator"));
2753                 return -1;
2754         }
2755
2756         real_oldpath = imap_get_real_path(session, IMAP_FOLDER(folder), item->path, &ok);
2757         if (is_fatal(ok)) {
2758                 return -1;
2759         }
2760
2761         g_free(session->mbox);
2762         session->mbox = NULL;
2763         session->exists = 0;
2764         session->recent = 0;
2765         session->expunge = 0;
2766         ok = imap_cmd_examine(session, "INBOX",
2767                               &exists, &recent, &unseen, &uid_validity, FALSE);
2768         if (ok != MAILIMAP_NO_ERROR) {
2769                 g_free(real_oldpath);
2770                 return -1;
2771         }
2772
2773         separator = imap_get_path_separator(session, IMAP_FOLDER(folder), item->path, &ok);
2774         if (is_fatal(ok))
2775                 return -1;
2776         if (strchr(item->path, G_DIR_SEPARATOR)) {
2777                 dirpath = g_path_get_dirname(item->path);
2778                 newpath = g_strconcat(dirpath, G_DIR_SEPARATOR_S, name, NULL);
2779                 g_free(dirpath);
2780         } else
2781                 newpath = g_strdup(name);
2782
2783         real_newpath = imap_utf8_to_modified_utf7(newpath, FALSE);
2784         imap_path_separator_subst(real_newpath, separator);
2785
2786         ok = imap_cmd_rename(session, real_oldpath, real_newpath);
2787         if (ok != MAILIMAP_NO_ERROR) {
2788                 log_warning(LOG_PROTOCOL, _("can't rename mailbox: %s to %s\n"),
2789                             real_oldpath, real_newpath);
2790                 g_free(real_oldpath);
2791                 g_free(newpath);
2792                 g_free(real_newpath);
2793                 return -1;
2794         }
2795         g_free(item->name);
2796         item->name = g_strdup(name);
2797
2798         old_cache_dir = folder_item_get_path(item);
2799
2800         paths[0] = g_strdup(item->path);
2801         paths[1] = newpath;
2802         g_node_traverse(item->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
2803                         imap_rename_folder_func, paths);
2804
2805         if (is_dir_exist(old_cache_dir)) {
2806                 new_cache_dir = folder_item_get_path(item);
2807                 if (g_rename(old_cache_dir, new_cache_dir) < 0) {
2808                         FILE_OP_ERROR(old_cache_dir, "rename");
2809                 }
2810                 g_free(new_cache_dir);
2811         }
2812
2813         g_free(old_cache_dir);
2814         g_free(paths[0]);
2815         g_free(newpath);
2816         g_free(real_oldpath);
2817         g_free(real_newpath);
2818         return 0;
2819 }
2820
2821 gint imap_subscribe(Folder *folder, FolderItem *item, gchar *rpath, gboolean sub)
2822 {
2823         gchar *path;
2824         gint r = MAILIMAP_NO_ERROR;
2825         IMAPSession *session;
2826         debug_print("getting session...\n");
2827
2828         session = imap_session_get(folder);
2829         if (!session) {
2830                 return -1;
2831         }
2832         if (item && item->path) {
2833                 path = imap_get_real_path(session, IMAP_FOLDER(folder), item->path, &r);
2834                 if (!path)
2835                         return -1;
2836                 if (is_fatal(r)) {
2837                         g_free(path);
2838                         return -1;
2839                 }
2840                 if (!strcmp(path, "INBOX") && sub == FALSE) {
2841                         g_free(path);
2842                         return -1;
2843                 }
2844                 debug_print("%ssubscribing %s\n", sub?"":"un", path);
2845                 r = imap_threaded_subscribe(folder, path, sub);
2846                 g_free(path);
2847         } else if (rpath) {
2848                 r = imap_threaded_subscribe(folder, rpath, sub);
2849         } else
2850                 return -1;
2851         return r;
2852 }
2853
2854 static gint imap_remove_folder_real(Folder *folder, FolderItem *item)
2855 {
2856         gint ok = MAILIMAP_NO_ERROR;
2857         IMAPSession *session;
2858         gchar *path;
2859         gchar *cache_dir;
2860         gboolean selected_folder;
2861
2862         g_return_val_if_fail(folder != NULL, -1);
2863         g_return_val_if_fail(item != NULL, -1);
2864         g_return_val_if_fail(item->path != NULL, -1);
2865
2866         debug_print("getting session...\n");
2867         session = imap_session_get(folder);
2868         if (!session) {
2869                 return -1;
2870         }
2871         path = imap_get_real_path(session, IMAP_FOLDER(folder), item->path, &ok);
2872         if (is_fatal(ok))
2873                 return -1;
2874
2875         imap_threaded_subscribe(folder, path, FALSE);
2876
2877         selected_folder = (session->mbox != NULL) &&
2878                           (!strcmp(session->mbox, item->path));
2879         if (selected_folder) {
2880                 ok = imap_cmd_close(session);
2881                 if (ok != MAILIMAP_NO_ERROR) {
2882                         debug_print("close err %d\n", ok);
2883                         return ok;
2884                 }
2885         }
2886         ok = imap_cmd_delete(session, path);
2887         if (ok != MAILIMAP_NO_ERROR && !is_fatal(ok)) {
2888                 gchar *tmp = NULL;
2889                 
2890                 ok = MAILIMAP_NO_ERROR;
2891                 tmp = g_strdup_printf("%s%c", path, 
2892                                 imap_get_path_separator(session, IMAP_FOLDER(folder), path, &ok));
2893                 g_free(path);
2894                 path = tmp;
2895                 if (!is_fatal(ok))
2896                         ok = imap_cmd_delete(session, path);
2897         }
2898
2899         if (ok != MAILIMAP_NO_ERROR) {
2900                 log_warning(LOG_PROTOCOL, _("can't delete mailbox\n"));
2901                 g_free(path);
2902                 return -1;
2903         }
2904
2905         g_free(path);
2906         cache_dir = folder_item_get_path(item);
2907         if (is_dir_exist(cache_dir) && remove_dir_recursive(cache_dir) < 0)
2908                 g_warning("can't remove directory '%s'\n", cache_dir);
2909         g_free(cache_dir);
2910         folder_item_remove(item);
2911         return 0;
2912 }
2913
2914 static gint imap_remove_folder(Folder *folder, FolderItem *item)
2915 {
2916         GNode *node, *next;
2917
2918         g_return_val_if_fail(item != NULL, -1);
2919         g_return_val_if_fail(item->folder != NULL, -1);
2920         g_return_val_if_fail(item->node != NULL, -1);
2921
2922         node = item->node->children;
2923         while (node != NULL) {
2924                 next = node->next;
2925                 if (imap_remove_folder(folder, FOLDER_ITEM(node->data)) < 0)
2926                         return -1;
2927                 node = next;
2928         }
2929         debug_print("IMAP removing %s\n", item->path);
2930
2931         if (imap_remove_all_msg(folder, item) < 0)
2932                 return -1;
2933         return imap_remove_folder_real(folder, item);
2934 }
2935
2936 typedef struct _uncached_data {
2937         IMAPSession *session;
2938         FolderItem *item;
2939         MsgNumberList *numlist;
2940         guint cur;
2941         guint total;
2942         gboolean done;
2943         int ok;
2944 } uncached_data;
2945
2946 static void *imap_get_uncached_messages_thread(void *data)
2947 {
2948         uncached_data *stuff = (uncached_data *)data;
2949         IMAPSession *session = stuff->session;
2950         FolderItem *item = stuff->item;
2951         MsgNumberList *numlist = stuff->numlist;
2952         GSList *newlist = NULL;
2953         GSList *llast = NULL;
2954         GSList *seq_list, *cur;
2955         gboolean got_alien_tags = FALSE;
2956
2957         debug_print("uncached_messages\n");
2958         
2959         if (session == NULL || item == NULL || item->folder == NULL
2960             || FOLDER_CLASS(item->folder) != &imap_class) {
2961                 stuff->done = TRUE;
2962                 return NULL;
2963         }
2964         
2965         seq_list = imap_get_lep_set_from_numlist(IMAP_FOLDER(item->folder), numlist);
2966         debug_print("get msgs info\n");
2967         for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
2968                 struct mailimap_set * imapset;
2969                 unsigned int i;
2970                 int r;
2971                 carray * env_list;
2972                 int count;
2973                 
2974                 if (session->cancelled)
2975                         break;
2976                 
2977                 imapset = cur->data;
2978                 
2979                 r = imap_threaded_fetch_env(session->folder,
2980                                             imapset, &env_list);
2981                 if (r != MAILIMAP_NO_ERROR) {
2982                         imap_handle_error(SESSION(session), r);
2983                         if (is_fatal(r)) {
2984                                 stuff->ok = r;
2985                                 return NULL;
2986                         }
2987                         continue;
2988                 }
2989
2990                 session_set_access_time(SESSION(session));
2991
2992                 count = 0;
2993                 for(i = 0 ; i < carray_count(env_list) ; i += 2) {
2994                         struct imap_fetch_env_info * info;
2995                         MsgInfo * msginfo;
2996                         GSList *tags = NULL, *cur = NULL;
2997                         info = carray_get(env_list, i);
2998                         tags = carray_get(env_list, i+1);
2999                         msginfo = imap_envelope_from_lep(info, item);
3000                         if (msginfo == NULL) {
3001                                 slist_free_strings(tags);
3002                                 g_slist_free(tags);
3003                                 continue;
3004                         }
3005                         g_slist_free(msginfo->tags);
3006                         msginfo->tags = NULL;
3007
3008                         for (cur = tags; cur; cur = cur->next) {
3009                                 gchar *real_tag = imap_modified_utf7_to_utf8(cur->data, TRUE);
3010                                 gint id = 0;
3011                                 id = tags_get_id_for_str(real_tag);
3012                                 if (id == -1) {
3013                                         id = tags_add_tag(real_tag);
3014                                         got_alien_tags = TRUE;
3015                                 }
3016                                 if (!g_slist_find(msginfo->tags, GINT_TO_POINTER(id))) {
3017                                         msginfo->tags = g_slist_prepend(
3018                                                         msginfo->tags,
3019                                                         GINT_TO_POINTER(id));
3020                                 }
3021                                 g_free(real_tag);
3022                         }
3023                         if (msginfo->tags)
3024                                 msginfo->tags = g_slist_reverse(msginfo->tags);
3025                         slist_free_strings(tags);
3026                         g_slist_free(tags);
3027                         msginfo->folder = item;
3028                         if (!newlist)
3029                                 llast = newlist = g_slist_append(newlist, msginfo);
3030                         else {
3031                                 llast = g_slist_append(llast, msginfo);
3032                                 llast = llast->next;
3033                         }
3034                         count ++;
3035                 }
3036                 
3037                 imap_fetch_env_free(env_list);
3038         }
3039         
3040         if (got_alien_tags) {
3041                 tags_write_tags();
3042                 main_window_reflect_tags_changes(mainwindow_get_mainwindow());
3043         }
3044
3045         for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
3046                 struct mailimap_set * imapset;
3047                 
3048                 imapset = cur->data;
3049                 mailimap_set_free(imapset);
3050         }
3051         
3052         session_set_access_time(SESSION(session));
3053         stuff->done = TRUE;
3054         return newlist;
3055 }
3056
3057 #define MAX_MSG_NUM 50
3058
3059 static GSList *imap_get_uncached_messages(IMAPSession *session,
3060                                         FolderItem *item,
3061                                         MsgNumberList *numlist,
3062                                         int *r)
3063 {
3064         GSList *result = NULL;
3065         GSList * cur;
3066         uncached_data *data = g_new0(uncached_data, 1);
3067         int finished;
3068         
3069         finished = 0;
3070         cur = numlist;
3071         data->total = g_slist_length(numlist);
3072         data->ok = MAILIMAP_NO_ERROR;
3073         debug_print("messages list : %i\n", data->total);
3074
3075         while (cur != NULL) {
3076                 GSList * partial_result;
3077                 int count;
3078                 GSList * newlist;
3079                 GSList * llast;
3080                 
3081                 llast = NULL;
3082                 count = 0;
3083                 newlist = NULL;
3084                 while (count < MAX_MSG_NUM) {
3085                         void * p;
3086                         
3087                         p = cur->data;
3088                         
3089                         if (newlist == NULL)
3090                                 llast = newlist = g_slist_append(newlist, p);
3091                         else {
3092                                 llast = g_slist_append(llast, p);
3093                                 llast = llast->next;
3094                         }
3095                         count ++;
3096                         
3097                         cur = cur->next;
3098                         if (cur == NULL)
3099                                 break;
3100                 }
3101                 
3102                 data->done = FALSE;
3103                 data->session = session;
3104                 data->item = item;
3105                 data->numlist = newlist;
3106                 data->cur += count;
3107                 
3108                 if (prefs_common.work_offline && 
3109                     !inc_offline_should_override(FALSE,
3110                         _("Claws Mail needs network access in order "
3111                           "to access the IMAP server."))) {
3112                         g_free(data);
3113                         return NULL;
3114                 }
3115                 
3116                 partial_result =
3117                         (GSList *)imap_get_uncached_messages_thread(data);
3118                 *r = data->ok;
3119                 if (data->ok != MAILIMAP_NO_ERROR) {
3120                         goto bail;
3121                 }
3122                 statusbar_progress_all(data->cur,data->total, 1);
3123                 
3124                 g_slist_free(newlist);
3125                 
3126                 result = g_slist_concat(result, partial_result);
3127         }
3128 bail:
3129         g_free(data);
3130         
3131         statusbar_progress_all(0,0,0);
3132         statusbar_pop_all();
3133         
3134         return result;
3135 }
3136
3137 static void imap_delete_all_cached_messages(FolderItem *item)
3138 {
3139         gchar *dir;
3140
3141         g_return_if_fail(item != NULL);
3142         g_return_if_fail(item->folder != NULL);
3143         g_return_if_fail(FOLDER_CLASS(item->folder) == &imap_class);
3144
3145         debug_print("Deleting all cached messages...\n");
3146
3147         dir = folder_item_get_path(item);
3148         if (is_dir_exist(dir))
3149                 remove_all_numbered_files(dir);
3150         g_free(dir);
3151
3152         debug_print("done.\n");
3153 }
3154
3155 gchar imap_get_path_separator_for_item(FolderItem *item)
3156 {
3157         Folder *folder = NULL;
3158         IMAPFolder *imap_folder = NULL;
3159         IMAPSession *session = NULL;
3160         gchar result = '/';
3161         gint ok = MAILIMAP_NO_ERROR;
3162         if (!item)
3163                 return '/';
3164         folder = item->folder;
3165         
3166         if (!folder)
3167                 return '/';
3168         
3169         imap_folder = IMAP_FOLDER(folder);
3170         
3171         if (!imap_folder)
3172                 return '/';
3173         
3174         debug_print("getting session...");
3175         session = imap_session_get(FOLDER(folder));
3176         result = imap_get_path_separator(session, imap_folder, item->path, &ok);
3177         return result;
3178 }
3179
3180 static gchar imap_refresh_path_separator(IMAPSession *session, IMAPFolder *folder, const gchar *subfolder, gint *ok)
3181 {
3182         clist * lep_list;
3183         int r;
3184         gchar separator = '\0';
3185         
3186         g_return_val_if_fail(session != NULL, '/');
3187         r = imap_threaded_list((Folder *)folder, "", subfolder, &lep_list);
3188         
3189         if (r != MAILIMAP_NO_ERROR) {
3190                 imap_handle_error(SESSION(session), r);
3191                 log_warning(LOG_PROTOCOL, _("LIST failed\n"));
3192                 *ok = r;
3193                 return '\0';
3194         }
3195
3196         if (lep_list != NULL && clist_count(lep_list) > 0) {
3197                 clistiter * iter = clist_begin(lep_list); 
3198                 struct mailimap_mailbox_list * mb;
3199                 mb = clist_content(iter);
3200
3201                 separator = mb->mb_delimiter;
3202                 debug_print("got separator: %c\n", folder->last_seen_separator);
3203         }
3204         *ok = MAILIMAP_NO_ERROR;
3205         mailimap_list_result_free(lep_list);
3206         return separator;
3207 }
3208
3209 static gchar imap_get_path_separator(IMAPSession *session, IMAPFolder *folder, const gchar *path, gint *ok)
3210 {
3211         gchar separator = '/';
3212         *ok = MAILIMAP_NO_ERROR;
3213         if (folder->last_seen_separator == 0) {
3214                 folder->last_seen_separator = imap_refresh_path_separator(session, folder, "", ok);
3215         }
3216
3217         if (folder->last_seen_separator == 0) {
3218                 folder->last_seen_separator = imap_refresh_path_separator(session, folder, "INBOX", ok);
3219         }
3220
3221         if (folder->last_seen_separator != 0) {
3222                 debug_print("using separator: %c\n", folder->last_seen_separator);
3223                 return folder->last_seen_separator;
3224         }
3225
3226         return separator;
3227 }
3228
3229 static gchar *imap_get_real_path(IMAPSession *session, IMAPFolder *folder, const gchar *path, gint *ok)
3230 {
3231         gchar *real_path = NULL;
3232         gchar separator;
3233         
3234         g_return_val_if_fail(folder != NULL, NULL);
3235         g_return_val_if_fail(path != NULL, NULL);
3236
3237         *ok = MAILIMAP_NO_ERROR;
3238         
3239         real_path = imap_utf8_to_modified_utf7(path, FALSE);
3240         separator = imap_get_path_separator(session, folder, path, ok);
3241         if (*ok == MAILIMAP_NO_ERROR)
3242                 imap_path_separator_subst(real_path, separator);
3243
3244         return real_path;
3245 }
3246
3247 static gint imap_set_message_flags(IMAPSession *session,
3248                                    IMAPFolderItem *item,
3249                                    MsgNumberList *numlist,
3250                                    IMAPFlags flags,
3251                                    GSList *tags,
3252                                    gboolean is_set)
3253 {
3254         gint ok = 0;
3255         GSList *seq_list;
3256         GSList * cur;
3257         gint total = 0;
3258         IMAPFolder *folder = NULL;
3259         GSList *sorted_list = NULL;
3260
3261         if (numlist == NULL || session == NULL)
3262                 return MAILIMAP_ERROR_BAD_STATE;
3263         
3264         folder = IMAP_FOLDER(session->folder);
3265         
3266         sorted_list = g_slist_copy(numlist);
3267         sorted_list = g_slist_sort(sorted_list, g_int_compare);
3268         
3269         cur = g_slist_last(sorted_list);
3270
3271         if (cur)
3272                 total = GPOINTER_TO_INT(cur->data);
3273         
3274         seq_list = imap_get_lep_set_from_numlist(IMAP_FOLDER(session->folder), sorted_list);
3275
3276         statusbar_print_all(_("Flagging messages..."));
3277
3278         for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) {
3279                 struct mailimap_set * imapset = (struct mailimap_set *)cur->data;
3280                 struct mailimap_set_item *set_item = NULL;
3281                 
3282                 if (imapset->set_list)
3283                         set_item = clist_content(clist_begin(imapset->set_list));
3284                 else
3285                         continue;
3286
3287                 if (set_item == NULL)
3288                         continue;
3289
3290                 statusbar_progress_all(set_item->set_first, total, 1);
3291
3292                 ok = imap_cmd_store(session, item, imapset,
3293                                     flags, tags, is_set);
3294                 statusbar_progress_all(set_item->set_last, total, 1);
3295                 if (ok != MAILIMAP_NO_ERROR && folder->max_set_size > 20) {
3296                         /* reduce max set size */
3297                         folder->max_set_size /= 2;
3298                 }
3299                 if (ok != MAILIMAP_NO_ERROR && is_fatal(ok)) {
3300                         break;
3301                 }
3302         }
3303         
3304         g_slist_free(sorted_list);
3305
3306         statusbar_progress_all(0,0,0);
3307         statusbar_pop_all();
3308
3309         imap_lep_set_free(seq_list);
3310         
3311         return ok;
3312 }
3313
3314 typedef struct _select_data {
3315         IMAPSession *session;
3316         gchar *real_path;
3317         gint *exists;
3318         gint *recent;
3319         gint *unseen;
3320         guint32 *uid_validity;
3321         gboolean done;
3322 } select_data;
3323
3324 static gint imap_select(IMAPSession *session, IMAPFolder *folder,
3325                         FolderItem *item,
3326                         gint *exists, gint *recent, gint *unseen,
3327                         guint32 *uid_validity, gint *can_create_flags,
3328                         gboolean block)
3329 {
3330         gchar *real_path;
3331         gint ok = MAILIMAP_NO_ERROR;
3332         gint exists_, recent_, unseen_;
3333         guint32 uid_validity_;
3334         gint can_create_flags_;
3335         const gchar *path = item ? item->path:NULL;
3336
3337         if (!item) {
3338                 return MAILIMAP_ERROR_BAD_STATE;
3339         }
3340
3341         if (!exists && !recent && !unseen && !uid_validity && !can_create_flags) {
3342                 if (session->mbox && strcmp(session->mbox, path) == 0)
3343                         return MAILIMAP_NO_ERROR;
3344         }
3345         if (!exists && !recent && !unseen && !uid_validity && can_create_flags) {
3346                 if (session->mbox && strcmp(session->mbox, path) == 0) {
3347                         if (IMAP_FOLDER_ITEM(item)->can_create_flags != ITEM_CAN_CREATE_FLAGS_UNKNOWN)
3348                                 return MAILIMAP_NO_ERROR;
3349                 }
3350         }
3351         if (!exists)
3352                 exists = &exists_;
3353         if (!recent)
3354                 recent = &recent_;
3355         if (!unseen)
3356                 unseen = &unseen_;
3357         if (!uid_validity)
3358                 uid_validity = &uid_validity_;
3359         if (!can_create_flags)
3360                 can_create_flags = &can_create_flags_;
3361
3362         g_free(session->mbox);
3363         session->mbox = NULL;
3364         session->exists = 0;
3365         session->recent = 0;
3366         session->expunge = 0;
3367
3368         real_path = imap_get_real_path(session, folder, path, &ok);
3369         if (is_fatal(ok))
3370                 return ok;              
3371         g_slist_free(IMAP_FOLDER_ITEM(item)->ok_flags);
3372         IMAP_FOLDER_ITEM(item)->ok_flags = NULL;
3373         ok = imap_cmd_select(session, real_path,
3374                              exists, recent, unseen, uid_validity, can_create_flags, 
3375                              &(IMAP_FOLDER_ITEM(item)->ok_flags), block);
3376         if (ok != MAILIMAP_NO_ERROR) {
3377                 log_warning(LOG_PROTOCOL, _("can't select folder: %s\n"), real_path);
3378         } else {
3379                 session->mbox = g_strdup(path);
3380                 session->folder_content_changed = FALSE;
3381                 session->exists = *exists;
3382                 session->recent = *recent;
3383                 session->expunge = 0;
3384                 session->unseen = *unseen;
3385                 session->uid_validity = *uid_validity;
3386                 debug_print("select: exists %d recent %d expunge %d uid_validity %d can_create_flags %d\n", 
3387                         session->exists, session->recent, session->expunge,
3388                         session->uid_validity, *can_create_flags);
3389         }
3390         if (*can_create_flags) {
3391                 IMAP_FOLDER_ITEM(item)->can_create_flags = ITEM_CAN_CREATE_FLAGS;
3392         } else {
3393                 IMAP_FOLDER_ITEM(item)->can_create_flags = ITEM_CANNOT_CREATE_FLAGS;
3394         }
3395         g_free(real_path);
3396
3397         return ok;
3398 }
3399
3400 static gint imap_status(IMAPSession *session, IMAPFolder *folder,
3401                         const gchar *path, IMAPFolderItem *item,
3402                         gint *messages,
3403                         guint32 *uid_next, guint32 *uid_validity,
3404                         gint *unseen, gboolean block)
3405 {
3406         int r = MAILIMAP_NO_ERROR;
3407         clistiter * iter;
3408         struct mailimap_mailbox_data_status * data_status;
3409         int got_values;
3410         gchar *real_path;
3411         guint mask = 0;
3412         
3413         real_path = imap_get_real_path(session, folder, path, &r);
3414         if (is_fatal(r)) {
3415                 g_free(real_path);
3416                 return r;
3417         }
3418         if (messages) {
3419                 mask |= 1 << 0;
3420                 *messages = 0;
3421         }
3422         if (uid_next) {
3423                 mask |= 1 << 2;
3424                 *uid_next = 0;
3425         }
3426         if (uid_validity) {
3427                 mask |= 1 << 3;
3428                 *uid_validity = 0;
3429         }
3430         if (unseen) {
3431                 mask |= 1 << 4;
3432                 *unseen = 0;
3433         }
3434         
3435         if (session->mbox != NULL &&
3436             !strcmp(session->mbox, item->item.path)) {
3437                 r = imap_cmd_close(session);
3438                 if (r != MAILIMAP_NO_ERROR) {
3439                         debug_print("close err %d\n", r);
3440                         g_free(real_path);
3441                         return r;
3442                 }
3443         }
3444         
3445         r = imap_threaded_status(FOLDER(folder), real_path, 
3446                 &data_status, mask);
3447
3448         g_free(real_path);
3449         if (r != MAILIMAP_NO_ERROR) {
3450                 imap_handle_error(SESSION(session), r);
3451                 debug_print("status err %d\n", r);
3452                 return r;
3453         }
3454         
3455         if (data_status == NULL || data_status->st_info_list == NULL) {
3456                 debug_print("data_status %p\n", data_status);
3457                 if (data_status) {
3458                         debug_print("data_status->st_info_list %p\n", data_status->st_info_list);
3459                         mailimap_mailbox_data_status_free(data_status);
3460                 }
3461                 return MAILIMAP_ERROR_BAD_STATE;
3462         }
3463         
3464         got_values = 0;
3465         if (data_status->st_info_list) {
3466                 for(iter = clist_begin(data_status->st_info_list) ; iter != NULL ;
3467                     iter = clist_next(iter)) {
3468                         struct mailimap_status_info * info;             
3469
3470                         info = clist_content(iter);
3471                         switch (info->st_att) {
3472                         case MAILIMAP_STATUS_ATT_MESSAGES:
3473                                 if (messages) {
3474                                         * messages = info->st_value;
3475                                         got_values |= 1 << 0;
3476                                 }
3477                                 break;
3478
3479                         case MAILIMAP_STATUS_ATT_UIDNEXT:
3480                                 if (uid_next) {
3481                                         * uid_next = info->st_value;
3482                                         got_values |= 1 << 2;
3483                                 }
3484                                 break;
3485
3486                         case MAILIMAP_STATUS_ATT_UIDVALIDITY:
3487                                 if (uid_validity) {
3488                                         * uid_validity = info->st_value;
3489                                         got_values |= 1 << 3;
3490                                 }
3491                                 break;
3492
3493                         case MAILIMAP_STATUS_ATT_UNSEEN:
3494                                 if (unseen) {
3495                                         * unseen = info->st_value;
3496                                         got_values |= 1 << 4;
3497                                 }
3498                                 break;
3499                         }
3500                 }
3501         }
3502         mailimap_mailbox_data_status_free(data_status);
3503         
3504         if (got_values != mask) {
3505                 g_warning("status: incomplete values received (%d)\n", got_values);
3506         }
3507         return MAILIMAP_NO_ERROR;
3508 }
3509
3510 static void imap_free_capabilities(IMAPSession *session)
3511 {
3512         slist_free_strings(session->capability);
3513         g_slist_free(session->capability);
3514         session->capability = NULL;
3515 }
3516
3517 /* low-level IMAP4rev1 commands */
3518
3519 static gint imap_cmd_login(IMAPSession *session,
3520                            const gchar *user, const gchar *pass,
3521                            const gchar *type)
3522 {
3523         int r;
3524         gint ok;
3525
3526         if (!strcmp(type, "LOGIN") && imap_has_capability(session, "LOGINDISABLED")) {
3527                 gint ok = MAILIMAP_ERROR_BAD_STATE;
3528                 if (imap_has_capability(session, "STARTTLS")) {
3529 #ifdef USE_GNUTLS
3530                         log_warning(LOG_PROTOCOL, _("Server requires TLS to log in.\n"));
3531                         ok = imap_cmd_starttls(session);
3532                         if (ok != MAILIMAP_NO_ERROR) {
3533                                 log_warning(LOG_PROTOCOL, _("Can't start TLS session.\n"));
3534                                 return ok;
3535                         } else {
3536                                 /* refresh capas */
3537                                 imap_free_capabilities(session);
3538                                 if ((r = imap_get_capabilities(session)) != MAILIMAP_NO_ERROR) {
3539                                         imap_handle_error(SESSION(session), r);
3540                                         log_warning(LOG_PROTOCOL, _("Can't refresh capabilities.\n"));
3541                                         return r;
3542                                 }
3543                         }
3544 #else           
3545                         log_error(LOG_PROTOCOL, _("Connection to %s failed: "
3546                                         "server requires TLS, but Claws Mail "
3547                                         "has been compiled without OpenSSL "
3548                                         "support.\n"),
3549                                         SESSION(session)->server);
3550                         return MAILIMAP_ERROR_LOGIN;
3551 #endif
3552                 } else {
3553                         log_error(LOG_PROTOCOL, _("Server logins are disabled.\n"));
3554                         return MAILIMAP_ERROR_LOGIN;
3555                 }
3556         }
3557
3558         log_print(LOG_PROTOCOL, "IMAP4> Logging %s to %s using %s\n", 
3559                         user,
3560                         SESSION(session)->server,
3561                         type);
3562         r = imap_threaded_login(session->folder, user, pass, type);
3563         if (r != MAILIMAP_NO_ERROR) {
3564                 imap_handle_error(SESSION(session), r);
3565                 log_print(LOG_PROTOCOL, "IMAP4< Error logging in to %s\n",
3566                                 SESSION(session)->server);
3567                 ok = r;
3568         } else {
3569                 log_print(LOG_PROTOCOL, "IMAP4< Login to %s successful\n",
3570                                 SESSION(session)->server);
3571                 ok = MAILIMAP_NO_ERROR;
3572         }
3573         return ok;
3574 }
3575
3576 static gint imap_cmd_noop(IMAPSession *session)
3577 {
3578         int r;
3579         unsigned int exists, recent, expunge, unseen, uidnext, uidval;
3580         
3581         r = imap_threaded_noop(session->folder, &exists, &recent, &expunge, &unseen, &uidnext, &uidval);
3582         if (r != MAILIMAP_NO_ERROR) {
3583                 imap_handle_error(SESSION(session), r);
3584                 debug_print("noop err %d\n", r);
3585                 return r;
3586         }
3587
3588         session->folder_content_changed = FALSE;
3589
3590         if ((exists && exists != session->exists)
3591          || (recent && recent != session->recent)
3592          || (expunge && expunge != session->expunge)
3593          || (unseen && unseen != session->unseen)) {
3594                 session->folder_content_changed = TRUE;
3595         }
3596         if (uidnext != 0 && uidnext != session->uid_next) {
3597                 session->uid_next = uidnext;
3598                 session->folder_content_changed = TRUE;
3599         }
3600         if (uidval != 0 && uidval != session->uid_validity) {
3601                 session->uid_validity = uidval;
3602                 session->folder_content_changed = TRUE;
3603         }
3604
3605         session->exists = exists;
3606         session->recent = recent;
3607         session->expunge = expunge;
3608         session->unseen = unseen;
3609
3610         session_set_access_time(SESSION(session));
3611
3612         return MAILIMAP_NO_ERROR;
3613 }
3614
3615 #ifdef USE_GNUTLS
3616 static gint imap_cmd_starttls(IMAPSession *session)
3617 {
3618         int r;
3619         
3620         r = imap_threaded_starttls(session->folder, 
3621                 SESSION(session)->server, SESSION(session)->port);
3622         if (r != MAILIMAP_NO_ERROR) {
3623                 imap_handle_error(SESSION(session), r);
3624                 debug_print("starttls err %d\n", r);
3625                 return r;
3626         }
3627         return MAILIMAP_NO_ERROR;
3628 }
3629 #endif
3630
3631 static gint imap_cmd_select(IMAPSession *session, const gchar *folder,
3632                             gint *exists, gint *recent, gint *unseen,
3633                             guint32 *uid_validity, gint *can_create_flags,
3634                             GSList **ok_flags, gboolean block)
3635 {
3636         int r;
3637
3638         r = imap_threaded_select(session->folder, folder,
3639                                  exists, recent, unseen, uid_validity, can_create_flags, ok_flags);
3640         if (r != MAILIMAP_NO_ERROR) {
3641                 imap_handle_error(SESSION(session), r);
3642                 debug_print("select err %d\n", r);
3643                 return r;
3644         }
3645         return MAILIMAP_NO_ERROR;
3646 }
3647
3648 static gint imap_cmd_close(IMAPSession *session)
3649 {
3650         int r;
3651
3652         r = imap_threaded_close(session->folder);
3653         if (r != MAILIMAP_NO_ERROR) {
3654                 imap_handle_error(SESSION(session), r);
3655                 debug_print("close err %d\n", r);
3656                 return r;
3657         }
3658         g_free(session->mbox);
3659         session->mbox = NULL;
3660         session->exists = 0;
3661         session->recent = 0;
3662         session->expunge = 0;
3663         return MAILIMAP_NO_ERROR;
3664 }
3665
3666 static gint imap_cmd_examine(IMAPSession *session, const gchar *folder,
3667                              gint *exists, gint *recent, gint *unseen,
3668                              guint32 *uid_validity, gboolean block)
3669 {
3670         int r;
3671
3672         r = imap_threaded_examine(session->folder, folder,
3673                                   exists, recent, unseen, uid_validity);
3674         if (r != MAILIMAP_NO_ERROR) {
3675                 imap_handle_error(SESSION(session), r);
3676                 debug_print("examine err %d\n", r);
3677                 
3678                 return r;
3679         }
3680         return MAILIMAP_NO_ERROR;
3681 }
3682
3683 static gint imap_cmd_create(IMAPSession *session, const gchar *folder)
3684 {
3685         int r;
3686
3687         r = imap_threaded_create(session->folder, folder);
3688         if (r != MAILIMAP_NO_ERROR) {
3689                 imap_handle_error(SESSION(session), r);
3690                 return r;
3691         }
3692
3693         return MAILIMAP_NO_ERROR;
3694 }
3695
3696 static gint imap_cmd_rename(IMAPSession *session, const gchar *old_folder,
3697                             const gchar *new_folder)
3698 {
3699         int r;
3700
3701         r = imap_threaded_rename(session->folder, old_folder,
3702                                  new_folder);
3703         if (r != MAILIMAP_NO_ERROR) {
3704                 imap_handle_error(SESSION(session), r);
3705                 return r;
3706         }
3707
3708         return MAILIMAP_NO_ERROR;
3709 }
3710
3711 static gint imap_cmd_delete(IMAPSession *session, const gchar *folder)
3712 {
3713         int r;
3714         
3715
3716         r = imap_threaded_delete(session->folder, folder);
3717         if (r != MAILIMAP_NO_ERROR) {
3718                 imap_handle_error(SESSION(session), r);
3719                 return r;
3720         }
3721
3722         return MAILIMAP_NO_ERROR;
3723 }
3724
3725 typedef struct _fetch_data {
3726         IMAPSession *session;
3727         guint32 uid;
3728         const gchar *filename;
3729         gboolean headers;
3730         gboolean body;
3731         gboolean done;
3732 } fetch_data;
3733
3734 static void *imap_cmd_fetch_thread(void *data)
3735 {
3736         fetch_data *stuff = (fetch_data *)data;
3737         IMAPSession *session = stuff->session;
3738         guint32 uid = stuff->uid;
3739         const gchar *filename = stuff->filename;
3740         int r;
3741         
3742         if (stuff->body) {
3743                 r = imap_threaded_fetch_content(session->folder,
3744                                                uid, 1, filename);
3745         }
3746         else {
3747                 r = imap_threaded_fetch_content(session->folder,
3748                                                 uid, 0, filename);
3749         }
3750         if (r != MAILIMAP_NO_ERROR) {
3751                 imap_handle_error(SESSION(session), r);
3752                 debug_print("fetch err %d\n", r);
3753                 return GINT_TO_POINTER(r);
3754         }
3755         return GINT_TO_POINTER(MAILIMAP_NO_ERROR);
3756 }
3757
3758 static gint imap_cmd_fetch(IMAPSession *session, guint32 uid,
3759                                 const gchar *filename, gboolean headers,
3760                                 gboolean body)
3761 {
3762         fetch_data *data = g_new0(fetch_data, 1);
3763         int result = 0;
3764         data->done = FALSE;
3765         data->session = session;
3766         data->uid = uid;
3767         data->filename = filename;
3768         data->headers = headers;
3769         data->body = body;
3770
3771         if (prefs_common.work_offline && 
3772             !inc_offline_should_override(FALSE,
3773                 _("Claws Mail needs network access in order "
3774                   "to access the IMAP server."))) {
3775                 g_free(data);
3776                 return -1;
3777         }
3778         statusbar_print_all(_("Fetching message..."));
3779         result = GPOINTER_TO_INT(imap_cmd_fetch_thread(data));
3780         statusbar_pop_all();
3781         g_free(data);
3782         return result;
3783 }
3784
3785
3786 static gint imap_cmd_append(IMAPSession *session, 
3787                             IMAPFolderItem *item,
3788                             const gchar *destfolder,
3789                             const gchar *file, IMAPFlags flags, 
3790                             guint32 *new_uid)
3791 {
3792         struct mailimap_flag_list * flag_list;
3793         int r;
3794         
3795         cm_return_val_if_fail(file != NULL, MAILIMAP_ERROR_BAD_STATE);
3796
3797         flag_list = imap_flag_to_lep(item, flags, NULL);
3798         lock_session(session);
3799         r = imap_threaded_append(session->folder, destfolder,
3800                          file, flag_list, (int *)new_uid);
3801         mailimap_flag_list_free(flag_list);
3802
3803         if (r != MAILIMAP_NO_ERROR) {
3804                 imap_handle_error(SESSION(session), r);
3805                 debug_print("append err %d\n", r);
3806                 return r;
3807         }
3808
3809         unlock_session(session);
3810
3811         return MAILIMAP_NO_ERROR;
3812 }
3813
3814 static gint imap_cmd_copy(IMAPSession *session, struct mailimap_set * set,
3815                           const gchar *destfolder,
3816                           struct mailimap_set **source, struct mailimap_set **dest)
3817 {
3818         int r;
3819         
3820         g_return_val_if_fail(session != NULL, MAILIMAP_ERROR_BAD_STATE);
3821         g_return_val_if_fail(set != NULL, MAILIMAP_ERROR_BAD_STATE);
3822         g_return_val_if_fail(destfolder != NULL, MAILIMAP_ERROR_BAD_STATE);
3823
3824         r = imap_threaded_copy(session->folder, set, destfolder, source, dest);
3825         if (r != MAILIMAP_NO_ERROR) {
3826                 imap_handle_error(SESSION(session), r);
3827                 return r;
3828         }
3829
3830         return MAILIMAP_NO_ERROR;
3831 }
3832
3833 static gint imap_cmd_store(IMAPSession *session, 
3834                            IMAPFolderItem *item,
3835                            struct mailimap_set * set,
3836                            IMAPFlags flags, GSList *tags, int do_add)
3837 {
3838         int r;
3839         struct mailimap_flag_list * flag_list = NULL;
3840         struct mailimap_store_att_flags * store_att_flags;
3841         
3842         flag_list = imap_flag_to_lep(item, flags, tags);
3843
3844         if (do_add)
3845                 store_att_flags =
3846                         mailimap_store_att_flags_new_add_flags_silent(flag_list);
3847         else
3848                 store_att_flags =
3849                         mailimap_store_att_flags_new_remove_flags_silent(flag_list);
3850         
3851         r = imap_threaded_store(session->folder, set, store_att_flags);
3852         mailimap_store_att_flags_free(store_att_flags);
3853         if (r != MAILIMAP_NO_ERROR) {
3854                 imap_handle_error(SESSION(session), r);
3855                 return r;
3856         }
3857         
3858         return MAILIMAP_NO_ERROR;
3859 }
3860
3861 static gint imap_cmd_expunge(IMAPSession *session, gboolean do_expunge)
3862 {
3863         int r;
3864         
3865         if (!do_expunge)
3866                 return MAILIMAP_NO_ERROR;
3867
3868         if (prefs_common.work_offline && 
3869             !inc_offline_should_override(FALSE,
3870                 _("Claws Mail needs network access in order "
3871                   "to access the IMAP server."))) {
3872                 return -1;
3873         }
3874
3875         r = imap_threaded_expunge(session->folder);
3876         if (r != MAILIMAP_NO_ERROR) {
3877                 imap_handle_error(SESSION(session), r);
3878                 return r;
3879         }
3880
3881         return MAILIMAP_NO_ERROR;
3882 }
3883
3884 gint imap_expunge(Folder *folder, FolderItem *item)
3885 {
3886         IMAPSession *session = imap_session_get(folder);
3887         if (session == NULL)
3888                 return -1;
3889         
3890         return imap_cmd_expunge(session, TRUE);
3891 }
3892
3893 static void imap_path_separator_subst(gchar *str, gchar separator)
3894 {
3895         gchar *p;
3896         gboolean in_escape = FALSE;
3897
3898         if (!separator || separator == '/') return;
3899
3900         for (p = str; *p != '\0'; p++) {
3901                 if (*p == '/' && !in_escape)
3902                         *p = separator;
3903                 else if (*p == '&' && *(p + 1) != '-' && !in_escape)
3904                         in_escape = TRUE;
3905                 else if (*p == '-' && in_escape)
3906                         in_escape = FALSE;
3907         }
3908 }
3909
3910 static gboolean imap_rename_folder_func(GNode *node, gpointer data)
3911 {
3912         FolderItem *item = node->data;
3913         gchar **paths = data;
3914         const gchar *oldpath = paths[0];
3915         const gchar *newpath = paths[1];
3916         gchar *real_oldpath, *real_newpath;
3917         gchar *base;
3918         gchar *new_itempath;
3919         gint oldpathlen;
3920         IMAPSession *session = imap_session_get(item->folder);
3921         gint ok = MAILIMAP_NO_ERROR;
3922         oldpathlen = strlen(oldpath);
3923         if (strncmp(oldpath, item->path, oldpathlen) != 0) {
3924                 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
3925                 return TRUE;
3926         }
3927
3928         base = item->path + oldpathlen;
3929         while (*base == G_DIR_SEPARATOR) base++;
3930         if (*base == '\0')
3931                 new_itempath = g_strdup(newpath);
3932         else
3933                 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
3934                                            NULL);
3935
3936         real_oldpath = imap_get_real_path(session, IMAP_FOLDER(item->folder), item->path, &ok);
3937         g_free(item->path);
3938         item->path = new_itempath;
3939         
3940         real_newpath = imap_get_real_path(session, IMAP_FOLDER(item->folder), item->path, &ok);
3941         
3942         imap_threaded_subscribe(item->folder, real_oldpath, FALSE);
3943         imap_threaded_subscribe(item->folder, real_newpath, TRUE);
3944
3945         g_free(real_oldpath);
3946         g_free(real_newpath);
3947         return FALSE;
3948 }
3949
3950 static gint get_list_of_uids(IMAPSession *session, Folder *folder, IMAPFolderItem *item, GSList **msgnum_list)
3951 {
3952         GSList *uidlist, *elem;
3953         int r = -1;
3954         clist * lep_uidlist;
3955         gint ok, nummsgs = 0, lastuid_old;
3956
3957         if (session == NULL) {
3958                 return -1;
3959         }
3960
3961         ok = imap_select(session, IMAP_FOLDER(folder), FOLDER_ITEM(item),
3962                          NULL, NULL, NULL, NULL, NULL, TRUE);
3963         if (ok != MAILIMAP_NO_ERROR) {
3964                 return -1;
3965         }
3966
3967         g_slist_free(item->uid_list);
3968         item->uid_list = NULL;
3969
3970         uidlist = NULL;
3971         
3972         if (folder->account && folder->account->low_bandwidth) {
3973                 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SIMPLE, NULL,
3974                                  &lep_uidlist);
3975         }
3976         
3977         if (r == MAILIMAP_NO_ERROR) {
3978                 GSList * fetchuid_list =
3979                         imap_uid_list_from_lep(lep_uidlist);
3980                 mailimap_search_result_free(lep_uidlist);
3981                 
3982                 uidlist = g_slist_concat(fetchuid_list, uidlist);
3983         } else {
3984                 carray * lep_uidtab;
3985                 if (r != -1) { /* inited */
3986                         imap_handle_error(SESSION(session), r);
3987                         if (is_fatal(r))
3988                                 return -1;
3989                 }
3990                 r = imap_threaded_fetch_uid(folder, 1,
3991                                     &lep_uidtab);
3992                 if (r == MAILIMAP_NO_ERROR) {
3993                         GSList * fetchuid_list =
3994                                 imap_uid_list_from_lep_tab(lep_uidtab);
3995                         imap_fetch_uid_list_free(lep_uidtab);
3996                         uidlist = g_slist_concat(fetchuid_list, uidlist);
3997                 }
3998         }
3999         
4000         if (r != MAILIMAP_NO_ERROR) {
4001                 imap_handle_error(SESSION(session), r);
4002                 return -1;
4003         }
4004
4005         lastuid_old = item->lastuid;
4006
4007         for (elem = uidlist; elem != NULL; elem = g_slist_next(elem)) {
4008                 guint msgnum;
4009
4010                 msgnum = GPOINTER_TO_INT(elem->data);
4011
4012                 *msgnum_list = g_slist_prepend(*msgnum_list, GINT_TO_POINTER(msgnum));
4013                 item->uid_list = g_slist_prepend(item->uid_list, GINT_TO_POINTER(msgnum));
4014                 nummsgs++;
4015         }
4016         g_slist_free(uidlist);
4017
4018         unlock_session(session); /* locked from imap_get_num_list */
4019
4020         return nummsgs;
4021
4022 }
4023
4024 gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list, gboolean *old_uids_valid)
4025 {
4026         IMAPFolderItem *item = (IMAPFolderItem *)_item;
4027         IMAPSession *session;
4028         gint nummsgs;
4029         GSList *uidlist = NULL;
4030         gchar *dir;
4031         gboolean selected_folder;
4032         gint known_list_len = 0;
4033         debug_print("get_num_list\n");
4034         
4035         g_return_val_if_fail(folder != NULL, -1);
4036         g_return_val_if_fail(item != NULL, -1);
4037         g_return_val_if_fail(item->item.path != NULL, -1);
4038         g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
4039         g_return_val_if_fail(folder->account != NULL, -1);
4040
4041         known_list_len = g_slist_length(item->uid_list);
4042         if (!item->should_update) {
4043                 debug_print("get_num_list: nothing to update\n");
4044                 *old_uids_valid = TRUE;
4045                 if (known_list_len == item->item.total_msgs
4046                  && known_list_len > 0) {
4047                         *msgnum_list = g_slist_copy(item->uid_list);
4048                         return known_list_len;
4049                 } else {
4050                         debug_print("don't know the list length...\n");
4051                 }
4052         }
4053
4054         if (prefs_common.work_offline && 
4055             !inc_offline_should_override(FALSE,
4056                 _("Claws Mail needs network access in order "
4057                   "to access the IMAP server."))) {
4058                 return -1;
4059         }
4060         
4061         debug_print("getting session...\n");
4062         session = imap_session_get(folder);
4063         g_return_val_if_fail(session != NULL, -1);
4064
4065         lock_session(session); /* unlocked by get_list_of_uids */
4066         if (FOLDER_ITEM(item)->path) 
4067                 statusbar_print_all(_("Scanning folder %s%c%s ..."),
4068                                       FOLDER_ITEM(item)->folder->name, 
4069                                       G_DIR_SEPARATOR,
4070                                       FOLDER_ITEM(item)->path);
4071         else
4072                 statusbar_print_all(_("Scanning folder %s ..."),
4073                                       FOLDER_ITEM(item)->folder->name);
4074
4075         selected_folder = (session->mbox != NULL) &&
4076                           (!strcmp(session->mbox, item->item.path));
4077         
4078         if (item->should_trash_cache) {
4079                 *old_uids_valid = FALSE;
4080                 debug_print("get_num_list: trashing num list\n");
4081                 debug_print("Freeing imap uid cache\n");
4082                 item->lastuid = 0;
4083                 g_slist_free(item->uid_list);
4084                 item->uid_list = NULL;
4085
4086                 imap_delete_all_cached_messages((FolderItem *)item);
4087         } else {
4088                 debug_print("get_num_list: updating num list\n");
4089                 *old_uids_valid = TRUE;
4090         }
4091
4092         nummsgs = get_list_of_uids(session, folder, item, &uidlist);
4093         /* session could be broken now, in case of fatal error */
4094
4095         debug_print("get_num_list: got %d msgs\n", nummsgs);
4096
4097         if (nummsgs < 0) {
4098                 statusbar_pop_all();
4099                 return -1;
4100         }
4101
4102         *msgnum_list = uidlist;
4103
4104         dir = folder_item_get_path((FolderItem *)item);
4105         debug_print("removing old messages from %s\n", dir);
4106         remove_numbered_files_not_in_list(dir, *msgnum_list);
4107         g_free(dir);
4108         
4109         debug_print("get_num_list - ok - %i\n", nummsgs);
4110         statusbar_pop_all();
4111         item->should_trash_cache = FALSE;
4112         item->should_update = FALSE;
4113         return nummsgs;
4114 }
4115
4116 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item)
4117 {
4118         MsgInfo *msginfo;
4119         MsgFlags flags;
4120
4121         flags.perm_flags = MSG_NEW|MSG_UNREAD;
4122         flags.tmp_flags = 0;
4123
4124         g_return_val_if_fail(item != NULL, NULL);
4125         g_return_val_if_fail(file != NULL, NULL);
4126
4127         if (folder_has_parent_of_type(item, F_QUEUE)) {
4128                 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
4129         } else if (folder_has_parent_of_type(item, F_DRAFT)) {
4130                 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
4131         }
4132
4133         msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
4134         if (!msginfo) return NULL;
4135         
4136         msginfo->plaintext_file = g_strdup(file);
4137         msginfo->folder = item;
4138
4139         return msginfo;
4140 }
4141
4142 GSList *imap_get_msginfos(Folder *folder, FolderItem *item,
4143                           GSList *msgnum_list)
4144 {
4145         IMAPSession *session;
4146         MsgInfoList *ret = NULL;
4147         gint ok;
4148         
4149         debug_print("get_msginfos\n");
4150         
4151         g_return_val_if_fail(folder != NULL, NULL);
4152         g_return_val_if_fail(item != NULL, NULL);
4153         g_return_val_if_fail(msgnum_list != NULL, NULL);
4154
4155         debug_print("getting session...\n");
4156         session = imap_session_get(folder);
4157         g_return_val_if_fail(session != NULL, NULL);
4158
4159         lock_session(session); /* unlocked later in the function */
4160
4161         debug_print("IMAP getting msginfos\n");
4162         ok = imap_select(session, IMAP_FOLDER(folder), item,
4163                          NULL, NULL, NULL, NULL, NULL, FALSE);
4164         if (ok != MAILIMAP_NO_ERROR) {
4165                 return NULL;
4166         }
4167         if (!(folder_has_parent_of_type(item, F_DRAFT) || 
4168               folder_has_parent_of_type(item, F_QUEUE))) {
4169                 ret = g_slist_concat(ret,
4170                         imap_get_uncached_messages(session, item,
4171                                                    msgnum_list, &ok));
4172                 if (ok != MAILIMAP_NO_ERROR)
4173                         return NULL;
4174                 unlock_session(session);
4175         } else {
4176                 MsgNumberList *sorted_list, *elem, *llast = NULL;
4177                 gint startnum, lastnum;
4178         
4179                 unlock_session(session);
4180
4181                 sorted_list = g_slist_sort(g_slist_copy(msgnum_list), g_int_compare);
4182
4183                 startnum = lastnum = GPOINTER_TO_INT(sorted_list->data);
4184
4185                 llast = g_slist_last(ret);
4186                 for (elem = sorted_list;; elem = g_slist_next(elem)) {
4187                         guint num = 0;
4188
4189                         if (elem)
4190                                 num = GPOINTER_TO_INT(elem->data);
4191
4192                         if (num > lastnum + 1 || elem == NULL) {
4193                                 int i;
4194                                 for (i = startnum; i <= lastnum; ++i) {
4195                                         gchar *file;
4196                                         file = imap_fetch_msg(folder, item, i);
4197                                         if (file != NULL) {
4198                                                 MsgInfo *msginfo = imap_parse_msg(file, item);
4199                                                 if (msginfo != NULL) {
4200                                                         msginfo->msgnum = i;
4201                                                         if (llast == NULL)
4202                                                                 llast = ret = g_slist_append(ret, msginfo);
4203                                                         else {
4204                                                                 llast = g_slist_append(llast, msginfo);
4205                                                                 llast = llast->next;
4206                                                         }
4207                                                 }
4208                                                 g_free(file);
4209                                         }
4210                                 }
4211
4212                                 if (elem == NULL)
4213                                         break;
4214
4215                                 startnum = num;
4216                         }
4217                         lastnum = num;
4218                 }
4219
4220                 g_slist_free(sorted_list);
4221         }
4222         return ret;
4223 }
4224
4225 MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid)
4226 {
4227         MsgInfo *msginfo = NULL;
4228         MsgInfoList *msginfolist;
4229         MsgNumberList numlist;
4230
4231         numlist.next = NULL;
4232         numlist.data = GINT_TO_POINTER(uid);
4233
4234         msginfolist = imap_get_msginfos(folder, item, &numlist);
4235         if (msginfolist != NULL) {
4236                 msginfo = msginfolist->data;
4237                 g_slist_free(msginfolist);
4238         }
4239
4240         return msginfo;
4241 }
4242
4243 gboolean imap_scan_required(Folder *folder, FolderItem *_item)
4244 {
4245         IMAPSession *session;
4246         IMAPFolderItem *item = (IMAPFolderItem *)_item;
4247         gint ok, exists = 0, unseen = 0;
4248         guint32 uid_next = 0, uid_val = 0;
4249         gboolean selected_folder;
4250         
4251         g_return_val_if_fail(folder != NULL, FALSE);
4252         g_return_val_if_fail(item != NULL, FALSE);
4253         g_return_val_if_fail(item->item.folder != NULL, FALSE);
4254         g_return_val_if_fail(FOLDER_CLASS(item->item.folder) == &imap_class, FALSE);
4255
4256         if (item->item.path == NULL)
4257                 return FALSE;
4258
4259         if (item->should_update) {
4260                 debug_print("scan already required\n");
4261                 return TRUE;
4262         }
4263         debug_print("getting session...\n");
4264         session = imap_session_get(folder);
4265         
4266         g_return_val_if_fail(session != NULL, FALSE);
4267         lock_session(session); /* unlocked later in the function */
4268
4269         selected_folder = (session->mbox != NULL) &&
4270                           (!strcmp(session->mbox, item->item.path));
4271         if (selected_folder) {
4272                 if (!session->folder_content_changed) {
4273                         ok = imap_cmd_noop(session);
4274                         if (ok != MAILIMAP_NO_ERROR) {
4275                                 debug_print("disconnected!\n");
4276                                 if (!is_fatal(ok))
4277                                         session = imap_reconnect_if_possible(folder, session);
4278                                 else
4279                                         session = imap_session_get(folder);
4280                                 if (session == NULL)
4281                                         return FALSE;
4282                         }
4283
4284                         if (session->folder_content_changed) {
4285                                 debug_print("CHANGED (self-noop)! scan_required\n");
4286                                 item->should_update = TRUE;
4287                                 if (session->uid_validity && session->uid_validity != item->item.mtime) {
4288                                         item->item.mtime = session->uid_validity;
4289                                         item->should_trash_cache = TRUE;
4290                                 }
4291                                 unlock_session(session);
4292                                 return TRUE;
4293                         }
4294                 } else {
4295                         debug_print("CHANGED (previous noop)! scan_required\n");
4296                         item->should_update = TRUE;
4297                         if (session->uid_validity && session->uid_validity != item->item.mtime) {
4298                                 item->item.mtime = session->uid_validity;
4299                                 item->should_trash_cache = TRUE;
4300                         }
4301                         unlock_session(session);
4302                         return TRUE;
4303                 }
4304         } else {
4305                 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path, IMAP_FOLDER_ITEM(item),
4306                                  &exists, &uid_next, &uid_val, &unseen, FALSE);
4307                 if (ok != MAILIMAP_NO_ERROR) {
4308                         return FALSE;
4309                 }
4310                 
4311                 debug_print("exists %d, item->item.total_msgs %d\n", 
4312                         exists, item->item.total_msgs);
4313                 if (exists != item->item.total_msgs
4314                     || unseen != item->item.unread_msgs 
4315                     || uid_next != item->uid_next
4316                     || uid_val != item->item.mtime) {
4317                         debug_print("CHANGED (status)! scan_required\n");
4318                         item->last_change = time(NULL);
4319                         item->should_update = TRUE;
4320                         item->uid_next = uid_next;
4321                         if (uid_val != item->item.mtime) {
4322                                 item->item.mtime = uid_val;
4323                                 item->should_trash_cache = TRUE;
4324                         }
4325                         unlock_session(session);
4326                         return TRUE;
4327                 }
4328         }
4329         unlock_session(session);
4330
4331         item->should_update = FALSE;
4332         return FALSE;
4333 }
4334
4335 void imap_change_flags(Folder *folder, FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
4336 {
4337         IMAPSession *session;
4338         IMAPFlags flags_set = 0, flags_unset = 0;
4339         gint ok = MAILIMAP_NO_ERROR;
4340         MsgNumberList numlist;
4341         hashtable_data *ht_data = NULL;
4342
4343         g_return_if_fail(folder != NULL);
4344         g_return_if_fail(folder->klass == &imap_class);
4345         g_return_if_fail(item != NULL);
4346         g_return_if_fail(item->folder == folder);
4347         g_return_if_fail(msginfo != NULL);
4348         g_return_if_fail(msginfo->folder == item);
4349
4350         if (!MSG_IS_MARKED(msginfo->flags) &&  (newflags & MSG_MARKED))
4351                 flags_set |= IMAP_FLAG_FLAGGED;
4352         if ( MSG_IS_MARKED(msginfo->flags) && !(newflags & MSG_MARKED))
4353                 flags_unset |= IMAP_FLAG_FLAGGED;
4354
4355         if (!MSG_IS_UNREAD(msginfo->flags) &&  (newflags & MSG_UNREAD))
4356                 flags_unset |= IMAP_FLAG_SEEN;
4357         if ( MSG_IS_UNREAD(msginfo->flags) && !(newflags & MSG_UNREAD))
4358                 flags_set |= IMAP_FLAG_SEEN;
4359
4360         if (!MSG_IS_REPLIED(msginfo->flags) &&  (newflags & MSG_REPLIED))
4361                 flags_set |= IMAP_FLAG_ANSWERED;
4362         if ( MSG_IS_REPLIED(msginfo->flags) && !(newflags & MSG_REPLIED))
4363                 flags_unset |= IMAP_FLAG_ANSWERED;
4364
4365         if (!MSG_IS_FORWARDED(msginfo->flags) &&  (newflags & MSG_FORWARDED))
4366                 flags_set |= IMAP_FLAG_FORWARDED;
4367         if ( MSG_IS_FORWARDED(msginfo->flags) && !(newflags & MSG_FORWARDED))
4368                 flags_unset |= IMAP_FLAG_FORWARDED;
4369
4370         if (!MSG_IS_SPAM(msginfo->flags) &&  (newflags & MSG_SPAM)) {
4371                 flags_set |= IMAP_FLAG_SPAM;
4372                 flags_unset |= IMAP_FLAG_HAM;
4373         }
4374         if ( MSG_IS_SPAM(msginfo->flags) && !(newflags & MSG_SPAM)) {
4375                 flags_set |= IMAP_FLAG_HAM;
4376                 flags_unset |= IMAP_FLAG_SPAM;
4377         }
4378         if (!MSG_IS_DELETED(msginfo->flags) &&  (newflags & MSG_DELETED))
4379                 flags_set |= IMAP_FLAG_DELETED;
4380         if ( MSG_IS_DELETED(msginfo->flags) && !(newflags & MSG_DELETED))
4381                 flags_unset |= IMAP_FLAG_DELETED;
4382
4383         if (!flags_set && !flags_unset) {
4384                 /* the changed flags were not translatable to IMAP-speak.
4385                  * like MSG_POSTFILTERED, so just apply. */
4386                 msginfo->flags.perm_flags = newflags;
4387                 return;
4388         }
4389
4390         debug_print("getting session...\n");
4391         session = imap_session_get(folder);
4392         if (!session) {
4393                 return;
4394         }
4395
4396         if ((ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder,
4397             NULL, NULL, NULL, NULL, NULL, FALSE)) != MAILIMAP_NO_ERROR) {
4398                 return;
4399         }
4400         numlist.next = NULL;
4401         numlist.data = GINT_TO_POINTER(msginfo->msgnum);
4402
4403         if (IMAP_FOLDER_ITEM(item)->batching) {
4404                 /* instead of performing an UID STORE command for each message change,
4405                  * as a lot of them can change "together", we just fill in hashtables
4406                  * and defer the treatment so that we're able to send only one
4407                  * command.
4408                  */
4409                 debug_print("IMAP batch mode on, deferring flags change\n");
4410                 if (flags_set) {
4411                         ht_data = g_hash_table_lookup(IMAP_FOLDER_ITEM(item)->flags_set_table, 
4412                                 GINT_TO_POINTER(flags_set));
4413                         if (ht_data == NULL) {
4414                                 ht_data = g_new0(hashtable_data, 1);
4415                                 ht_data->item = IMAP_FOLDER_ITEM(item);
4416                                 g_hash_table_insert(IMAP_FOLDER_ITEM(item)->flags_set_table, 
4417                                         GINT_TO_POINTER(flags_set), ht_data);
4418                         }
4419                         ht_data->msglist = g_slist_prepend(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum));
4420                 } 
4421                 if (flags_unset) {
4422                         ht_data = g_hash_table_lookup(IMAP_FOLDER_ITEM(item)->flags_unset_table, 
4423                                 GINT_TO_POINTER(flags_unset));
4424                         if (ht_data == NULL) {
4425                                 ht_data = g_new0(hashtable_data, 1);
4426                                 ht_data->item = IMAP_FOLDER_ITEM(item);
4427                                 g_hash_table_insert(IMAP_FOLDER_ITEM(item)->flags_unset_table, 
4428                                         GINT_TO_POINTER(flags_unset), ht_data);
4429                         }
4430                         ht_data->msglist = g_slist_prepend(ht_data->msglist, 
4431                                         GINT_TO_POINTER(msginfo->msgnum));              
4432                 }
4433         } else {
4434                 debug_print("IMAP changing flags\n");
4435                 if (flags_set) {
4436                         ok = imap_set_message_flags(session, IMAP_FOLDER_ITEM(item), &numlist, flags_set, NULL, TRUE);
4437                         if (ok != MAILIMAP_NO_ERROR) {
4438                                 return;
4439                         }
4440                 }
4441
4442                 if (flags_unset) {
4443                         ok = imap_set_message_flags(session, IMAP_FOLDER_ITEM(item), &numlist, flags_unset, NULL, FALSE);
4444                         if (ok != MAILIMAP_NO_ERROR) {
4445                                 return;
4446                         }
4447                 }
4448         }
4449         msginfo->flags.perm_flags = newflags;
4450         return;
4451 }
4452
4453 static gint imap_remove_msg(Folder *folder, FolderItem *item, gint uid)
4454 {
4455         gint ok;
4456         IMAPSession *session;
4457         gchar *dir;
4458         MsgNumberList numlist;
4459         
4460         g_return_val_if_fail(folder != NULL, -1);
4461         g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
4462         g_return_val_if_fail(item != NULL, -1);
4463
4464         debug_print("getting session...\n");
4465         session = imap_session_get(folder);
4466         if (!session) return -1;
4467
4468         ok = imap_select(session, IMAP_FOLDER(folder), item,
4469                          NULL, NULL, NULL, NULL, NULL, FALSE);
4470         if (ok != MAILIMAP_NO_ERROR) {
4471                 return ok;
4472         }
4473         numlist.next = NULL;
4474         numlist.data = GINT_TO_POINTER(uid);
4475         
4476         ok = imap_set_message_flags
4477                 (session, IMAP_FOLDER_ITEM(item), &numlist, IMAP_FLAG_DELETED, NULL, TRUE);
4478         if (ok != MAILIMAP_NO_ERROR) {
4479                 log_warning(LOG_PROTOCOL, _("can't set deleted flags: %d\n"), uid);
4480                 return ok;
4481         }
4482
4483         ok = imap_cmd_expunge(session, folder->account->imap_use_trash);
4484
4485         if (ok != MAILIMAP_NO_ERROR) {
4486                 log_warning(LOG_PROTOCOL, _("can't expunge\n"));
4487                 return ok;
4488         }
4489
4490         IMAP_FOLDER_ITEM(item)->uid_list = g_slist_remove(
4491             IMAP_FOLDER_ITEM(item)->uid_list, numlist.data);
4492         dir = folder_item_get_path(item);
4493         if (is_dir_exist(dir))
4494                 remove_numbered_files(dir, uid, uid);
4495         g_free(dir);
4496         return MAILIMAP_NO_ERROR;
4497 }
4498
4499 static gint compare_msginfo(gconstpointer a, gconstpointer b)
4500 {
4501         return ((MsgInfo *)a)->msgnum - ((MsgInfo *)b)->msgnum;
4502 }
4503
4504 static guint gslist_find_next_num(MsgNumberList **list, guint num)
4505 {
4506         GSList *elem;
4507
4508         g_return_val_if_fail(list != NULL, -1);
4509
4510         for (elem = *list; elem != NULL; elem = g_slist_next(elem))
4511                 if (GPOINTER_TO_INT(elem->data) >= num)
4512                         break;
4513         *list = elem;
4514         return elem != NULL ? GPOINTER_TO_INT(elem->data) : (gint)-1;
4515 }
4516
4517 static gboolean flag_ok(IMAPFolderItem *item, guint flag)
4518 {
4519         if (item->ok_flags && g_slist_find(item->ok_flags, GUINT_TO_POINTER(flag))) {
4520                 debug_print("flag %d is OK\n", flag);
4521                 return TRUE;
4522         }
4523         if (item->can_create_flags == ITEM_CAN_CREATE_FLAGS) {
4524                 debug_print("creating flags is OK\n");
4525                 return TRUE;
4526         }
4527         return FALSE;
4528 }
4529
4530 /*
4531  * NEW and DELETED flags are not syncronized
4532  * - The NEW/RECENT flags in IMAP folders can not really be directly
4533  *   modified by Sylpheed
4534  * - The DELETE/DELETED flag in IMAP and Sylpheed don't have the same
4535  *   meaning, in IMAP it always removes the messages from the FolderItem
4536  *   in Sylpheed it can mean to move the message to trash
4537  */
4538
4539 typedef struct _get_flags_data {
4540         Folder *folder;
4541         FolderItem *item;
4542         MsgInfoList *msginfo_list;
4543         GRelation *msgflags;
4544         gboolean full_search;
4545         gboolean done;
4546 } get_flags_data;
4547
4548 static /*gint*/ void *imap_get_flags_thread(void *data)
4549 {
4550         get_flags_data *stuff = (get_flags_data *)data;
4551         Folder *folder = stuff->folder;
4552         FolderItem *fitem = (FolderItem *) stuff->item;
4553         MsgInfoList *msginfo_list = stuff->msginfo_list;
4554         GRelation *msgflags = stuff->msgflags;
4555         GSList *elem;
4556         carray * lep_uidtab;
4557         IMAPSession *session;
4558         gint ok;
4559         int r = MAILIMAP_NO_ERROR;
4560         GHashTable *flags_hash = NULL;
4561         GHashTable *tags_hash = NULL;
4562         gboolean full_search = stuff->full_search;
4563         GSList *sorted_list = NULL;
4564         GSList *unseen = NULL, *answered = NULL, *flagged = NULL, *deleted = NULL, *forwarded = NULL, *spam = NULL;
4565         GSList *seq_list, *cur;
4566         gboolean reverse_seen = FALSE;
4567         gboolean selected_folder;
4568         gint exists_cnt, unseen_cnt;
4569         gboolean got_alien_tags = FALSE;
4570
4571         session = imap_session_get(folder);
4572
4573         if (session == NULL) {
4574                 stuff->done = TRUE;
4575                 return GINT_TO_POINTER(-1);
4576         }
4577         selected_folder = (session->mbox != NULL) &&
4578                           (!strcmp(session->mbox, fitem->path));
4579
4580         lock_session(session);
4581         if (!selected_folder) {
4582                 ok = imap_select(session, IMAP_FOLDER(folder), fitem,
4583                         &exists_cnt, NULL, &unseen_cnt, NULL, NULL, TRUE);
4584                 if (ok != MAILIMAP_NO_ERROR) {
4585                         stuff->done = TRUE;
4586                         return GINT_TO_POINTER(-1);
4587                 }
4588
4589                 if (unseen_cnt > exists_cnt / 2)
4590                         reverse_seen = TRUE;
4591         } 
4592         else {
4593                 if (fitem->unread_msgs > fitem->total_msgs / 2)
4594                         reverse_seen = TRUE;
4595         }
4596
4597         sorted_list = g_slist_sort(g_slist_copy(msginfo_list), compare_msginfo);
4598         if (!full_search) {
4599                 seq_list = imap_get_lep_set_from_msglist(IMAP_FOLDER(folder), msginfo_list);
4600         } else {
4601                 struct mailimap_set * set;
4602                 set = mailimap_set_new_interval(1, 0);
4603                 seq_list = g_slist_append(NULL, set);
4604         }
4605
4606         if (folder->account && folder->account->low_bandwidth) {
4607                 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
4608                         struct mailimap_set * imapset;
4609                         clist * lep_uidlist;
4610                         int r;
4611
4612                         imapset = cur->data;
4613                         if (reverse_seen) {
4614                                 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SEEN,
4615                                                          full_search ? NULL:imapset, &lep_uidlist);
4616                         }
4617                         else {
4618                                 r = imap_threaded_search(folder,
4619                                                          IMAP_SEARCH_TYPE_UNSEEN,
4620                                                          full_search ? NULL:imapset, &lep_uidlist);
4621                         }
4622                         if (r == MAILIMAP_NO_ERROR) {
4623                                 GSList * uidlist;
4624
4625                                 uidlist = imap_uid_list_from_lep(lep_uidlist);
4626                                 mailimap_search_result_free(lep_uidlist);
4627
4628                                 unseen = g_slist_concat(unseen, uidlist);
4629                         } else {
4630                                 imap_handle_error(SESSION(session), r);
4631                                 goto bail;
4632                         }
4633
4634                         r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_FLAGGED,
4635                                                  full_search ? NULL:imapset, &lep_uidlist);
4636                         if (r == MAILIMAP_NO_ERROR) {
4637                                 GSList * uidlist;
4638
4639                                 uidlist = imap_uid_list_from_lep(lep_uidlist);
4640                                 mailimap_search_result_free(lep_uidlist);
4641
4642                                 flagged = g_slist_concat(flagged, uidlist);
4643                         } else {
4644                                 imap_handle_error(SESSION(session), r);
4645                                 goto bail;
4646                         }
4647
4648                         if (fitem->opened || fitem->processing_pending || fitem == folder->inbox) {
4649                                 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_ANSWERED,
4650                                                          full_search ? NULL:imapset, &lep_uidlist);
4651                                 if (r == MAILIMAP_NO_ERROR) {
4652                                         GSList * uidlist;
4653
4654                                         uidlist = imap_uid_list_from_lep(lep_uidlist);
4655                                         mailimap_search_result_free(lep_uidlist);
4656
4657                                         answered = g_slist_concat(answered, uidlist);
4658                                 } else {
4659                                         imap_handle_error(SESSION(session), r);
4660                                         goto bail;
4661                                 }
4662
4663                                 if (flag_ok(IMAP_FOLDER_ITEM(fitem), IMAP_FLAG_FORWARDED)) {
4664                                         r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_FORWARDED,
4665                                                                  full_search ? NULL:imapset, &lep_uidlist);
4666                                         if (r == MAILIMAP_NO_ERROR) {
4667                                                 GSList * uidlist;
4668
4669                                                 uidlist = imap_uid_list_from_lep(lep_uidlist);
4670                                                 mailimap_search_result_free(lep_uidlist);
4671
4672                                                 forwarded = g_slist_concat(forwarded, uidlist);
4673                                         } else {
4674                                                 imap_handle_error(SESSION(session), r);
4675                                                 goto bail;
4676                                         }
4677                                 }
4678
4679                                 if (flag_ok(IMAP_FOLDER_ITEM(fitem), IMAP_FLAG_SPAM)) {
4680                                         r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SPAM,
4681                                                                  full_search ? NULL:imapset, &lep_uidlist);
4682                                         if (r == MAILIMAP_NO_ERROR) {
4683                                                 GSList * uidlist;
4684
4685                                                 uidlist = imap_uid_list_from_lep(lep_uidlist);
4686                                                 mailimap_search_result_free(lep_uidlist);
4687
4688                                                 spam = g_slist_concat(spam, uidlist);
4689                                         } else {
4690                                                 imap_handle_error(SESSION(session), r);
4691                                                 goto bail;
4692                                         }
4693                                 }
4694
4695                                 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_DELETED,
4696                                                          full_search ? NULL:imapset, &lep_uidlist);
4697                                 if (r == MAILIMAP_NO_ERROR) {
4698                                         GSList * uidlist;
4699
4700                                         uidlist = imap_uid_list_from_lep(lep_uidlist);
4701                                         mailimap_search_result_free(lep_uidlist);
4702
4703                                         deleted = g_slist_concat(deleted, uidlist);
4704                                 } else {
4705                                         imap_handle_error(SESSION(session), r);
4706                                         goto bail;
4707                                 }
4708                         }
4709                 }
4710
4711         } else {
4712                 r = imap_threaded_fetch_uid_flags(folder, 1, &lep_uidtab);
4713                 if (r == MAILIMAP_NO_ERROR) {
4714                         flags_hash = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, NULL);
4715                         tags_hash = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, NULL);
4716                         imap_flags_hash_from_lep_uid_flags_tab(lep_uidtab, flags_hash, tags_hash);
4717                         imap_fetch_uid_flags_list_free(lep_uidtab);
4718                 } else {
4719                         imap_handle_error(SESSION(session), r);
4720                         goto bail;
4721                 }
4722         }
4723
4724 bail:
4725         if (r == MAILIMAP_NO_ERROR)
4726                 unlock_session(session);
4727         
4728         for (elem = sorted_list; elem != NULL; elem = g_slist_next(elem)) {
4729                 MsgInfo *msginfo;
4730                 MsgPermFlags flags, oldflags;
4731                 gboolean wasnew;
4732
4733                 msginfo = (MsgInfo *) elem->data;
4734                 flags = msginfo->flags.perm_flags;
4735                 wasnew = (flags & MSG_NEW);
4736                 oldflags = flags & ~(MSG_NEW|MSG_UNREAD|MSG_REPLIED|MSG_FORWARDED|MSG_MARKED|MSG_DELETED|MSG_SPAM);
4737
4738                 if (folder->account && folder->account->low_bandwidth) {
4739                         if (fitem->opened || fitem->processing_pending || fitem == folder->inbox) {
4740                                 flags &= ~((reverse_seen ? 0 : MSG_UNREAD | MSG_NEW) | MSG_REPLIED | MSG_FORWARDED | MSG_MARKED | MSG_SPAM);
4741                         } else {
4742                                 flags &= ~((reverse_seen ? 0 : MSG_UNREAD | MSG_NEW | MSG_MARKED));
4743                         }
4744                         if (reverse_seen)
4745                                 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
4746                         if (gslist_find_next_num(&unseen, msginfo->msgnum) == msginfo->msgnum) {
4747                                 if (!reverse_seen) {
4748                                         flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
4749                                 } else {
4750                                         flags &= ~(MSG_UNREAD | MSG_NEW);
4751                                 }
4752                         }
4753
4754                         if (gslist_find_next_num(&flagged, msginfo->msgnum) == msginfo->msgnum)
4755                                 flags |= MSG_MARKED;
4756                         else
4757                                 flags &= ~MSG_MARKED;
4758
4759                         if (fitem->opened || fitem->processing_pending || fitem == folder->inbox) {
4760                                 if (gslist_find_next_num(&answered, msginfo->msgnum) == msginfo->msgnum)
4761                                         flags |= MSG_REPLIED;
4762                                 else
4763                                         flags &= ~MSG_REPLIED;
4764                                 if (gslist_find_next_num(&forwarded, msginfo->msgnum) == msginfo->msgnum)
4765                                         flags |= MSG_FORWARDED;
4766                                 else
4767                                         flags &= ~MSG_FORWARDED;
4768                                 if (gslist_find_next_num(&spam, msginfo->msgnum) == msginfo->msgnum)
4769                                         flags |= MSG_SPAM;
4770                                 else
4771                                         flags &= ~MSG_SPAM;
4772                                 if (gslist_find_next_num(&deleted, msginfo->msgnum) == msginfo->msgnum)
4773                                         flags |= MSG_DELETED;
4774                                 else
4775                                         flags &= ~MSG_DELETED;
4776                         }
4777                 } else {
4778                         if (flags_hash != NULL) {
4779
4780                                 flags = GPOINTER_TO_INT(g_hash_table_lookup(flags_hash, 
4781                                                 GINT_TO_POINTER(msginfo->msgnum)));
4782                         }
4783
4784                         if ((flags & MSG_UNREAD) == 0)
4785                                 flags &= ~MSG_NEW;
4786                         else if (wasnew)
4787                                 flags |= MSG_NEW;
4788                         flags |= oldflags;
4789                         
4790                         if (tags_hash != NULL) {
4791                                 GSList *tags = g_hash_table_lookup(tags_hash, GINT_TO_POINTER(msginfo->msgnum));
4792                                 GSList *cur;
4793
4794                                 if (tags != NULL) {
4795                                         g_slist_free(msginfo->tags);
4796                                         msginfo->tags = NULL;
4797                                 }
4798                                 for (cur = tags; cur; cur = cur->next) {
4799                                         gchar *real_tag = imap_modified_utf7_to_utf8(cur->data, TRUE);
4800                                         gint id = 0;
4801                                         id = tags_get_id_for_str(real_tag);
4802                                         if (id == -1) {
4803                                                 id = tags_add_tag(real_tag);
4804                                                 got_alien_tags = TRUE;
4805                                         }
4806                                         if (!g_slist_find(msginfo->tags, GINT_TO_POINTER(id))) {
4807                                                 msginfo->tags = g_slist_append(
4808                                                                 msginfo->tags,
4809                                                                 GINT_TO_POINTER(id));
4810                                         }
4811                                         g_free(real_tag);
4812                                 }
4813                                 slist_free_strings(tags);
4814                                 g_slist_free(tags);
4815                         }
4816                 }
4817
4818                 g_relation_insert(msgflags, msginfo, GINT_TO_POINTER(flags));
4819         }
4820         
4821         if (got_alien_tags) {
4822                 tags_write_tags();
4823                 main_window_reflect_tags_changes(mainwindow_get_mainwindow());
4824         }
4825
4826         if (flags_hash)
4827                 g_hash_table_destroy(flags_hash);
4828         if (tags_hash)
4829                 g_hash_table_destroy(tags_hash);
4830
4831         imap_lep_set_free(seq_list);
4832         g_slist_free(flagged);
4833         g_slist_free(deleted);
4834         g_slist_free(answered);
4835         g_slist_free(forwarded);
4836         g_slist_free(spam);
4837         g_slist_free(unseen);
4838         g_slist_free(sorted_list);
4839
4840         stuff->done = TRUE;
4841         return GINT_TO_POINTER(0);
4842 }
4843
4844 static gint imap_get_flags(Folder *folder, FolderItem *item,
4845                            MsgInfoList *msginfo_list, GRelation *msgflags)
4846 {
4847         gint result;
4848         get_flags_data *data = g_new0(get_flags_data, 1);
4849         data->done = FALSE;
4850         data->folder = folder;
4851         data->item = item;
4852         data->msginfo_list = msginfo_list;
4853         data->msgflags = msgflags;
4854         data->full_search = FALSE;
4855
4856         GSList *tmp = NULL, *cur;
4857         
4858         if (prefs_common.work_offline && 
4859             !inc_offline_should_override(FALSE,
4860                 _("Claws Mail needs network access in order "
4861                   "to access the IMAP server."))) {
4862                 g_free(data);
4863                 return -1;
4864         }
4865
4866         tmp = folder_item_get_msg_list(item);
4867
4868         if (g_slist_length(tmp) <= g_slist_length(msginfo_list))
4869                 data->full_search = TRUE;
4870         
4871         for (cur = tmp; cur; cur = cur->next)
4872                 procmsg_msginfo_free((MsgInfo *)cur->data);
4873         
4874         g_slist_free(tmp);
4875
4876         result = GPOINTER_TO_INT(imap_get_flags_thread(data));
4877         
4878         g_free(data);
4879         return result;
4880
4881 }
4882
4883 static gboolean process_flags(gpointer key, gpointer value, gpointer user_data)
4884 {
4885         gboolean flags_set = GPOINTER_TO_INT(user_data);
4886         gint flags_value = GPOINTER_TO_INT(key);
4887         hashtable_data *data = (hashtable_data *)value;
4888         IMAPFolderItem *_item = data->item;
4889         FolderItem *item = (FolderItem *)_item;
4890         gint ok = MAILIMAP_ERROR_BAD_STATE;
4891         IMAPSession *session = NULL;
4892         
4893         debug_print("getting session...\n");
4894         session = imap_session_get(item->folder);
4895
4896         data->msglist = g_slist_reverse(data->msglist);
4897         
4898         debug_print("IMAP %ssetting flags to %d for %d messages\n",
4899                 flags_set?"":"un",
4900                 flags_value,
4901                 g_slist_length(data->msglist));
4902         
4903         lock_session(session);
4904         if (session) {
4905                 ok = imap_select(session, IMAP_FOLDER(item->folder), item,
4906                          NULL, NULL, NULL, NULL, NULL, FALSE);
4907         }
4908         if (ok == MAILIMAP_NO_ERROR) {
4909                 ok = imap_set_message_flags(session, IMAP_FOLDER_ITEM(item),
4910                         data->msglist, flags_value, NULL, flags_set);
4911         } else {
4912                 g_warning("can't select mailbox %s\n", item->path);
4913         }
4914
4915         if (!is_fatal(ok))
4916                 unlock_session(session);
4917
4918         g_slist_free(data->msglist);    
4919         g_free(data);
4920         return TRUE;
4921 }
4922
4923 static gboolean process_tags(gpointer key, gpointer value, gpointer user_data)
4924 {
4925         gboolean tags_set = GPOINTER_TO_INT(user_data);
4926         TagsData *data = (TagsData *)value;
4927         IMAPFolderItem *_item = data->item;
4928         FolderItem *item = (FolderItem *)_item;
4929         gchar *str = data->str;
4930         gint ok = MAILIMAP_ERROR_BAD_STATE;
4931         IMAPSession *session = NULL;
4932         
4933         debug_print("getting session...\n");
4934         session = imap_session_get(item->folder);
4935
4936         data->msglist = g_slist_reverse(data->msglist);
4937         
4938         debug_print("IMAP %ssetting tags %s for %d messages\n",
4939                 tags_set?"":"un",
4940                 str,
4941                 g_slist_length(data->msglist));
4942         
4943         lock_session(session);
4944         if (session) {
4945                 ok = imap_select(session, IMAP_FOLDER(item->folder), item,
4946                          NULL, NULL, NULL, NULL, NULL, FALSE);
4947         }
4948         if (ok == MAILIMAP_NO_ERROR) {
4949                 GSList list;
4950                 list.data = str;
4951                 list.next = NULL;
4952                 ok = imap_set_message_flags(session, IMAP_FOLDER_ITEM(item),
4953                         data->msglist, 0, &list, tags_set);
4954         } else {
4955                 g_warning("can't select mailbox %s\n", item->path);
4956         }
4957
4958         if (!is_fatal(ok))
4959                 unlock_session(session);
4960
4961         g_slist_free(data->msglist);    
4962         g_free(data->str);
4963         g_free(data);
4964         return TRUE;
4965 }
4966
4967 static void process_hashtable(IMAPFolderItem *item)
4968 {
4969         if (item->flags_set_table) {
4970                 g_hash_table_foreach_remove(item->flags_set_table, process_flags, GINT_TO_POINTER(TRUE));
4971                 g_hash_table_destroy(item->flags_set_table);
4972                 item->flags_set_table = NULL;
4973         }
4974         if (item->flags_unset_table) {
4975                 g_hash_table_foreach_remove(item->flags_unset_table, process_flags, GINT_TO_POINTER(FALSE));
4976                 g_hash_table_destroy(item->flags_unset_table);
4977                 item->flags_unset_table = NULL;
4978         }
4979         if (item->tags_set_table) {
4980                 g_hash_table_foreach_remove(item->tags_set_table, process_tags, GINT_TO_POINTER(TRUE));
4981                 g_hash_table_destroy(item->tags_set_table);
4982                 item->tags_set_table = NULL;
4983         }
4984         if (item->tags_unset_table) {
4985                 g_hash_table_foreach_remove(item->tags_unset_table, process_tags, GINT_TO_POINTER(FALSE));
4986                 g_hash_table_destroy(item->tags_unset_table);
4987                 item->tags_unset_table = NULL;
4988         }
4989         
4990 }
4991
4992 static void imap_set_batch (Folder *folder, FolderItem *_item, gboolean batch)
4993 {
4994         IMAPFolderItem *item = (IMAPFolderItem *)_item;
4995         IMAPSession *session;
4996
4997         g_return_if_fail(item != NULL);
4998         
4999         if (item->batching == batch)
5000                 return;
5001         
5002         if (batch) {
5003                 item->batching = TRUE;
5004                 debug_print("IMAP switching to batch mode\n");
5005                 if (!item->flags_set_table) {
5006                         item->flags_set_table = g_hash_table_new(NULL, g_direct_equal);
5007                 }
5008                 if (!item->flags_unset_table) {
5009                         item->flags_unset_table = g_hash_table_new(NULL, g_direct_equal);
5010                 }
5011                 if (!item->tags_set_table) {
5012                         item->tags_set_table = g_hash_table_new(NULL, g_direct_equal);
5013                 }
5014                 if (!item->tags_unset_table) {
5015                         item->tags_unset_table = g_hash_table_new(NULL, g_direct_equal);
5016                 }
5017                 session = imap_session_get(folder);
5018                 if (session) {
5019                         imap_refresh_sensitivity(session);
5020                         session->sens_update_block = TRUE;
5021                 }
5022         } else {
5023                 debug_print("IMAP switching away from batch mode\n");
5024                 /* process stuff */
5025                 process_hashtable(item);
5026                 item->batching = FALSE;
5027                 session = imap_session_get(folder);
5028                 if (session) {
5029                         session->sens_update_block = FALSE;
5030                         imap_refresh_sensitivity(session);
5031                 }
5032         }
5033 }
5034
5035
5036
5037 /* data types conversion libetpan <-> claws */
5038
5039
5040
5041 #define ETPAN_IMAP_MB_MARKED      1
5042 #define ETPAN_IMAP_MB_UNMARKED    2
5043 #define ETPAN_IMAP_MB_NOSELECT    4
5044 #define ETPAN_IMAP_MB_NOINFERIORS 8
5045
5046 static int imap_flags_to_flags(struct mailimap_mbx_list_flags * imap_flags)
5047 {
5048   int flags;
5049   clistiter * cur;
5050   
5051   flags = 0;
5052   if (imap_flags->mbf_type == MAILIMAP_MBX_LIST_FLAGS_SFLAG) {
5053     switch (imap_flags->mbf_sflag) {
5054     case MAILIMAP_MBX_LIST_SFLAG_MARKED:
5055       flags |= ETPAN_IMAP_MB_MARKED;
5056       break;
5057     case MAILIMAP_MBX_LIST_SFLAG_NOSELECT:
5058       flags |= ETPAN_IMAP_MB_NOSELECT;
5059       break;
5060     case MAILIMAP_MBX_LIST_SFLAG_UNMARKED:
5061       flags |= ETPAN_IMAP_MB_UNMARKED;
5062       break;
5063     }
5064   }
5065   
5066   if (imap_flags->mbf_oflags) {
5067     for(cur = clist_begin(imap_flags->mbf_oflags) ; cur != NULL ;
5068         cur = clist_next(cur)) {
5069       struct mailimap_mbx_list_oflag * oflag;
5070
5071       oflag = clist_content(cur);
5072
5073       switch (oflag->of_type) {
5074       case MAILIMAP_MBX_LIST_OFLAG_NOINFERIORS:
5075         flags |= ETPAN_IMAP_MB_NOINFERIORS;
5076         break;
5077       }
5078     }
5079   }  
5080   return flags;
5081 }
5082
5083 static GSList * imap_list_from_lep(IMAPFolder * folder,
5084                                    clist * list, const gchar * real_path, gboolean all)
5085 {
5086         clistiter * iter;
5087         GSList * item_list = NULL, *llast = NULL;
5088         
5089         if (list) {
5090                 for(iter = clist_begin(list) ; iter != NULL ;
5091                     iter = clist_next(iter)) {
5092                         struct mailimap_mailbox_list * mb;
5093                         int flags;
5094                         char delimiter;
5095                         char * name;
5096                         char * dup_name;
5097                         gchar * base;
5098                         gchar * loc_name;
5099                         gchar * loc_path;
5100                         FolderItem *new_item;
5101
5102                         mb = clist_content(iter);
5103
5104                         if (mb == NULL)
5105                                 continue;
5106
5107                         flags = 0;
5108                         if (mb->mb_flag != NULL)
5109                                 flags = imap_flags_to_flags(mb->mb_flag);
5110
5111                         delimiter = mb->mb_delimiter;
5112                         name = mb->mb_name;
5113
5114                         dup_name = strdup(name);                
5115                         if (delimiter != '\0')
5116                                 subst_char(dup_name, delimiter, '/');
5117
5118                         base = g_path_get_basename(dup_name);
5119                         if (base[0] == '.') {
5120                                 g_free(base);
5121                                 free(dup_name);
5122                                 continue;
5123                         }
5124                         if (!all && path_cmp(name, real_path) == 0) {
5125                                 g_free(base);
5126                                 free(dup_name);
5127                                 continue;
5128                         }
5129
5130                         if (!all && dup_name[strlen(dup_name)-1] == '/') {
5131                                 dup_name[strlen(dup_name)-1] = '\0';
5132                         }
5133
5134                         loc_name = imap_modified_utf7_to_utf8(base, FALSE);
5135                         loc_path = imap_modified_utf7_to_utf8(dup_name, FALSE);
5136
5137                         new_item = folder_item_new(FOLDER(folder), loc_name, loc_path);
5138                         if ((flags & ETPAN_IMAP_MB_NOINFERIORS) != 0)
5139                                 new_item->no_sub = TRUE;
5140                         if (strcmp(dup_name, "INBOX") != 0 &&
5141                             ((flags & ETPAN_IMAP_MB_NOSELECT) != 0))
5142                                 new_item->no_select = TRUE;
5143
5144                         if (item_list == NULL)
5145                                 llast = item_list = g_slist_append(item_list, new_item);
5146                         else {
5147                                 llast = g_slist_append(llast, new_item);
5148                                 llast = llast->next;
5149                         }
5150                         debug_print("folder '%s' found.\n", loc_path);
5151                         g_free(base);
5152                         g_free(loc_path);
5153                         g_free(loc_name);
5154
5155                         free(dup_name);
5156                 }
5157         }       
5158         return item_list;
5159 }
5160
5161 static GSList * imap_get_lep_set_from_numlist(IMAPFolder *folder, MsgNumberList *numlist)
5162 {
5163         GSList *sorted_list, *cur;
5164         guint first, last, next;
5165         GSList *ret_list = NULL, *llast = NULL;
5166         struct mailimap_set * current_set;
5167         unsigned int item_count;
5168         
5169         if (numlist == NULL)
5170                 return NULL;
5171         
5172         current_set = mailimap_set_new_empty();
5173         
5174         sorted_list = g_slist_copy(numlist);
5175         sorted_list = g_slist_sort(sorted_list, g_int_compare);
5176
5177         first = GPOINTER_TO_INT(sorted_list->data);
5178         
5179         item_count = 0;
5180         for (cur = sorted_list; cur != NULL; cur = g_slist_next(cur)) {
5181                 if (GPOINTER_TO_INT(cur->data) == 0)
5182                         continue;
5183                 
5184                 item_count ++;
5185
5186                 last = GPOINTER_TO_INT(cur->data);
5187                 if (cur->next)
5188                         next = GPOINTER_TO_INT(cur->next->data);
5189                 else
5190                         next = 0;
5191
5192                 if (last + 1 != next || next == 0 || item_count >= folder->max_set_size) {
5193
5194                         struct mailimap_set_item * item;
5195                         item = mailimap_set_item_new(first, last);
5196                         mailimap_set_add(current_set, item);
5197                         
5198                         first = next;
5199
5200                         if (item_count >= folder->max_set_size) {
5201                                 if (ret_list == NULL)
5202                                         llast = ret_list = g_slist_append(ret_list,
5203                                                           current_set);
5204                                 else {
5205                                         llast = g_slist_append(llast, current_set);
5206                                         llast = llast->next;
5207                                 }
5208
5209                                 current_set = mailimap_set_new_empty();
5210                                 item_count = 0;
5211                         }
5212                 } 
5213         }
5214         
5215         if (clist_count(current_set->set_list) > 0) {
5216                 ret_list = g_slist_append(ret_list,
5217                                           current_set);
5218         }
5219         
5220         g_slist_free(sorted_list);
5221
5222         return ret_list;
5223 }
5224
5225 static GSList * imap_get_lep_set_from_msglist(IMAPFolder *folder, MsgInfoList *msglist)
5226 {
5227         MsgNumberList *numlist = NULL;
5228         MsgInfoList *cur;
5229         GSList *seq_list;
5230
5231         for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
5232                 MsgInfo *msginfo = (MsgInfo *) cur->data;
5233
5234                 numlist = g_slist_prepend(numlist, GINT_TO_POINTER(msginfo->msgnum));
5235         }
5236         numlist = g_slist_reverse(numlist);
5237         seq_list = imap_get_lep_set_from_numlist(folder, numlist);
5238         g_slist_free(numlist);
5239
5240         return seq_list;
5241 }
5242
5243 static GSList * imap_uid_list_from_lep(clist * list)
5244 {
5245         clistiter * iter;
5246         GSList * result;
5247         
5248         result = NULL;
5249         
5250         if (list) {
5251                 for(iter = clist_begin(list) ; iter != NULL ;
5252                     iter = clist_next(iter)) {
5253                         uint32_t * puid;
5254
5255                         puid = clist_content(iter);
5256                         result = g_slist_prepend(result, GINT_TO_POINTER(* puid));
5257                 }
5258                 result = g_slist_reverse(result);
5259         }       
5260         return result;
5261 }
5262
5263 static GSList * imap_uid_list_from_lep_tab(carray * list)
5264 {
5265         unsigned int i;
5266         GSList * result;
5267         
5268         result = NULL;
5269         
5270         for(i = 0 ; i < carray_count(list) ; i ++) {
5271                 uint32_t * puid;
5272                 
5273                 puid = carray_get(list, i);
5274                 result = g_slist_prepend(result, GINT_TO_POINTER(* puid));
5275         }
5276         result = g_slist_reverse(result);
5277         return result;
5278 }
5279
5280 static void imap_flags_hash_from_lep_uid_flags_tab(carray * list,
5281                                                    GHashTable * hash,
5282                                                    GHashTable * tags_hash)
5283 {
5284         unsigned int i;
5285         GSList * result;
5286         
5287         result = NULL;
5288         
5289         for(i = 0 ; i < carray_count(list) ; i += 3) {
5290                 uint32_t * puid;
5291                 int * pflags;
5292                 GSList *tags;
5293                 
5294                 puid = carray_get(list, i);
5295                 pflags = carray_get(list, i + 1);
5296                 tags = carray_get(list, i + 2);
5297                 
5298                 g_hash_table_insert(hash, GINT_TO_POINTER(*puid), GINT_TO_POINTER(* pflags));
5299                 g_hash_table_insert(tags_hash, GINT_TO_POINTER(*puid), tags);
5300         }
5301 }
5302
5303 static MsgInfo *imap_envelope_from_lep(struct imap_fetch_env_info * info,
5304                                        FolderItem *item)
5305 {
5306         MsgInfo *msginfo = NULL;
5307         guint32 uid = 0;
5308         goffset size = 0;
5309         MsgFlags flags = {0, 0};
5310         
5311         if (info->headers == NULL)
5312                 return NULL;
5313
5314         MSG_SET_TMP_FLAGS(flags, MSG_IMAP);
5315         if (folder_has_parent_of_type(item, F_QUEUE)) {
5316                 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
5317         } else if (folder_has_parent_of_type(item, F_DRAFT)) {
5318                 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
5319         }
5320         flags.perm_flags = info->flags;
5321         
5322         uid = info->uid;
5323         size = (goffset) info->size;
5324         msginfo = procheader_parse_str(info->headers, flags, FALSE, FALSE);
5325         
5326         if (msginfo) {
5327                 msginfo->msgnum = uid;
5328                 msginfo->size = size;
5329         }
5330
5331         return msginfo;
5332 }
5333
5334 static void imap_lep_set_free(GSList *seq_list)
5335 {
5336         GSList * cur;
5337         
5338         for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) {
5339                 struct mailimap_set * imapset;
5340                 
5341                 imapset = cur->data;
5342                 mailimap_set_free(imapset);
5343         }
5344         g_slist_free(seq_list);
5345 }
5346
5347 static struct mailimap_flag_list * imap_flag_to_lep(IMAPFolderItem *item, IMAPFlags flags, GSList *tags)
5348 {
5349         struct mailimap_flag_list * flag_list;
5350         GSList *cur = tags;
5351
5352         flag_list = mailimap_flag_list_new_empty();
5353         
5354         if (IMAP_IS_SEEN(flags))
5355                 mailimap_flag_list_add(flag_list,
5356                                        mailimap_flag_new_seen());
5357         if (IMAP_IS_ANSWERED(flags))
5358                 mailimap_flag_list_add(flag_list,
5359                                        mailimap_flag_new_answered());
5360         if (IMAP_IS_FLAGGED(flags))
5361                 mailimap_flag_list_add(flag_list,
5362                                        mailimap_flag_new_flagged());
5363         if (IMAP_IS_DELETED(flags))
5364                 mailimap_flag_list_add(flag_list,
5365                                        mailimap_flag_new_deleted());
5366         if (IMAP_IS_DRAFT(flags))
5367                 mailimap_flag_list_add(flag_list,
5368                                        mailimap_flag_new_draft());
5369         if (IMAP_IS_FORWARDED(flags) && flag_ok(item, IMAP_FLAG_FORWARDED))
5370                 mailimap_flag_list_add(flag_list,
5371                                        mailimap_flag_new_flag_keyword(strdup("$Forwarded")));
5372         if (IMAP_IS_SPAM(flags) && flag_ok(item, IMAP_FLAG_SPAM))
5373                 mailimap_flag_list_add(flag_list,
5374                                        mailimap_flag_new_flag_keyword(strdup("Junk")));
5375         else if (IMAP_IS_HAM(flags) && flag_ok(item, IMAP_FLAG_HAM))
5376                 mailimap_flag_list_add(flag_list,
5377                                        mailimap_flag_new_flag_keyword(strdup("NonJunk")));
5378         
5379         for (; cur; cur = cur->next) {
5380                 gchar *enc_str = 
5381                         imap_utf8_to_modified_utf7(cur->data, TRUE);
5382                 g_strstrip(enc_str);
5383         
5384                 mailimap_flag_list_add(flag_list,
5385                         mailimap_flag_new_flag_keyword(enc_str));
5386         }
5387
5388         return flag_list;
5389 }
5390
5391 guint imap_folder_get_refcnt(Folder *folder)
5392 {
5393         return ((IMAPFolder *)folder)->refcnt;
5394 }
5395
5396 void imap_folder_ref(Folder *folder)
5397 {
5398         ((IMAPFolder *)folder)->refcnt++;
5399 }
5400
5401 void imap_disconnect_all(gboolean have_connectivity)
5402 {
5403         GList *list;
5404         gboolean short_timeout;
5405 #ifdef HAVE_NETWORKMANAGER_SUPPORT
5406         GError *error;
5407 #endif
5408
5409 #ifdef HAVE_NETWORKMANAGER_SUPPORT
5410         error = NULL;
5411         short_timeout = !networkmanager_is_online(&error);
5412         if(error) {
5413                 short_timeout = TRUE;
5414                 g_error_free(error);
5415         }
5416 #else
5417         short_timeout = TRUE;
5418 #endif
5419
5420         if(short_timeout)
5421                 imap_main_set_timeout(1);
5422
5423         for (list = account_get_list(); list != NULL; list = list->next) {
5424                 PrefsAccount *account = list->data;
5425                 if (account->protocol == A_IMAP4) {
5426                         RemoteFolder *folder = (RemoteFolder *)account->folder;
5427                         if (folder && folder->session) {
5428                                 IMAPSession *session = (IMAPSession *)folder->session;
5429                                 if (have_connectivity)
5430                                         imap_threaded_disconnect(FOLDER(folder));
5431                                 SESSION(session)->state = SESSION_DISCONNECTED;
5432                                 SESSION(session)->sock = NULL;
5433                                 session_destroy(SESSION(session));
5434                                 folder->session = NULL;
5435                         }
5436                 }
5437         }
5438
5439         if(short_timeout)
5440                 imap_main_set_timeout(prefs_common.io_timeout_secs);
5441 }
5442
5443 void imap_folder_unref(Folder *folder)
5444 {
5445         if (((IMAPFolder *)folder)->refcnt > 0)
5446                 ((IMAPFolder *)folder)->refcnt--;
5447 }
5448
5449 void imap_cancel_all(void)
5450 {
5451         GList *folderlist;
5452         GList *cur;
5453         
5454         folderlist = folder_get_list();
5455         for (cur = folderlist; cur != NULL; cur = g_list_next(cur)) {
5456                 Folder *folder = (Folder *) cur->data;
5457
5458                 if (folder->klass == &imap_class) {
5459                         if (imap_is_busy(folder)) {
5460                                 IMAPSession *imap_session;
5461                                 RemoteFolder *rfolder;
5462                                 
5463                                 g_printerr("cancelled\n");
5464                                 imap_threaded_cancel(folder);
5465                                 rfolder = (RemoteFolder *) folder;
5466                                 imap_session = (IMAPSession *) rfolder->session;
5467                                 if (imap_session)
5468                                         imap_session->cancelled = 1;
5469                         }
5470                 }
5471         }
5472 }
5473
5474 gboolean imap_cancel_all_enabled(void)
5475 {
5476         GList *folderlist;
5477         GList *cur;
5478         
5479         folderlist = folder_get_list();
5480         for (cur = folderlist; cur != NULL; cur = g_list_next(cur)) {
5481                 Folder *folder = (Folder *) cur->data;
5482
5483                 if (folder->klass == &imap_class) {
5484                         if (imap_is_busy(folder)) {
5485                                 return TRUE;
5486                         }
5487                 }
5488         }
5489         
5490         return FALSE;
5491 }
5492
5493 static gboolean imap_is_busy(Folder *folder)
5494 {
5495         IMAPSession *imap_session;
5496         RemoteFolder *rfolder;
5497         
5498         rfolder = (RemoteFolder *) folder;
5499         imap_session = (IMAPSession *) rfolder->session;
5500         if (imap_session == NULL)
5501                 return FALSE;
5502         
5503         return imap_session->busy;
5504 }
5505
5506 #else /* HAVE_LIBETPAN */
5507
5508 static FolderClass imap_class;
5509
5510 static XMLTag *imap_item_get_xml(Folder *folder, FolderItem *item);
5511 static void imap_item_set_xml(Folder *folder, FolderItem *item, XMLTag *tag);
5512
5513 static Folder   *imap_folder_new        (const gchar    *name,
5514                                          const gchar    *path)
5515 {
5516         static gboolean missing_imap_warning = TRUE;
5517         if (missing_imap_warning) {
5518                 missing_imap_warning = FALSE;
5519                 alertpanel_error(
5520                         _("You have one or more IMAP accounts "
5521                           "defined. However this version of "
5522                           "Claws Mail has been built without "
5523                           "IMAP support; your IMAP account(s) are "
5524                           "disabled.\n\n"
5525                           "You probably need to "
5526                           "install libetpan and recompile "
5527                           "Claws Mail."));
5528         }
5529         return NULL;
5530 }
5531 static gint     imap_create_tree        (Folder         *folder)
5532 {
5533         return -1;
5534 }
5535 static FolderItem *imap_create_folder   (Folder         *folder,
5536                                          FolderItem     *parent,
5537                                          const gchar    *name)
5538 {
5539         return NULL;
5540 }
5541 static gint     imap_rename_folder      (Folder         *folder,
5542                                          FolderItem     *item, 
5543                                          const gchar    *name)
5544 {
5545         return -1;
5546 }
5547
5548 gchar imap_get_path_separator_for_item(FolderItem *item)
5549 {
5550         return '/';
5551 }
5552
5553 FolderClass *imap_get_class(void)
5554 {
5555         if (imap_class.idstr == NULL) {
5556                 imap_class.type = F_IMAP;
5557                 imap_class.idstr = "imap";
5558                 imap_class.uistr = "IMAP4";
5559
5560                 imap_class.new_folder = imap_folder_new;
5561                 imap_class.create_tree = imap_create_tree;
5562                 imap_class.create_folder = imap_create_folder;
5563                 imap_class.rename_folder = imap_rename_folder;
5564
5565                 imap_class.set_xml = folder_set_xml;
5566                 imap_class.get_xml = folder_get_xml;
5567                 imap_class.item_set_xml = imap_item_set_xml;
5568                 imap_class.item_get_xml = imap_item_get_xml;
5569                 /* nothing implemented */
5570         }
5571
5572         return &imap_class;
5573 }
5574
5575 void imap_disconnect_all(gboolean have_connectivity)
5576 {
5577 }
5578
5579 gint imap_subscribe(Folder *folder, FolderItem *item, gchar *rpath, gboolean sub)
5580 {
5581         return -1;
5582 }
5583
5584 GList * imap_scan_subtree(Folder *folder, FolderItem *item, gboolean unsubs_only, gboolean recursive)
5585 {
5586         return NULL;
5587 }
5588
5589 void imap_cache_msg(FolderItem *item, gint msgnum)
5590 {
5591 }
5592
5593 void imap_cancel_all(void)
5594 {
5595 }
5596
5597 gboolean imap_cancel_all_enabled(void)
5598 {
5599         return FALSE;
5600 }
5601
5602 #endif
5603
5604 #ifdef HAVE_LIBETPAN
5605 static void imap_synchronise(FolderItem *item, gint days) 
5606 {
5607         if (IMAP_FOLDER_ITEM(item)->last_sync == IMAP_FOLDER_ITEM(item)->last_change) {
5608                 debug_print("%s already synced\n", item->path?item->path:item->name);
5609                 return;
5610         }
5611         debug_print("syncing %s\n", item->path?item->path:item->name);
5612         imap_gtk_synchronise(item, days);
5613         IMAP_FOLDER_ITEM(item)->last_sync = IMAP_FOLDER_ITEM(item)->last_change;
5614 }
5615 #endif
5616
5617 static void imap_item_set_xml(Folder *folder, FolderItem *item, XMLTag *tag)
5618 {
5619 #ifdef HAVE_LIBETPAN
5620         GList *cur;
5621 #endif
5622         folder_item_set_xml(folder, item, tag);
5623         
5624 #ifdef HAVE_LIBETPAN
5625         for (cur = tag->attr; cur != NULL; cur = g_list_next(cur)) {
5626                 XMLAttr *attr = (XMLAttr *) cur->data;
5627
5628                 if (!attr || !attr->name || !attr->value) continue;
5629                 if (!strcmp(attr->name, "uidnext"))
5630                         IMAP_FOLDER_ITEM(item)->uid_next = atoi(attr->value);
5631                 if (!strcmp(attr->name, "last_sync"))
5632                         IMAP_FOLDER_ITEM(item)->last_sync = atoi(attr->value);
5633                 if (!strcmp(attr->name, "last_change"))
5634                         IMAP_FOLDER_ITEM(item)->last_change = atoi(attr->value);
5635         }
5636         if (IMAP_FOLDER_ITEM(item)->last_change == 0)
5637                 IMAP_FOLDER_ITEM(item)->last_change = time(NULL);
5638 #endif
5639 }
5640
5641 static XMLTag *imap_item_get_xml(Folder *folder, FolderItem *item)
5642 {
5643         XMLTag *tag;
5644
5645         tag = folder_item_get_xml(folder, item);
5646
5647 #ifdef HAVE_LIBETPAN
5648         xml_tag_add_attr(tag, xml_attr_new_int("uidnext", 
5649                         IMAP_FOLDER_ITEM(item)->uid_next));
5650         xml_tag_add_attr(tag, xml_attr_new_int("last_sync", 
5651                         IMAP_FOLDER_ITEM(item)->last_sync));
5652         xml_tag_add_attr(tag, xml_attr_new_int("last_change", 
5653                         IMAP_FOLDER_ITEM(item)->last_change));
5654
5655 #endif
5656         return tag;
5657 }
5658
5659 /* ===================================================================
5660  * UTF-7 conversion routines as in RFC 2192
5661  * =================================================================== 
5662  * These two functions from: 
5663  * libimap library.
5664  * Copyright (C) 2003-2004 Pawel Salek. */
5665
5666 /* UTF7 modified base64 alphabet */
5667 static char base64chars[] =
5668   "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+,";
5669 #define UNDEFINED 64
5670
5671 /* UTF16 definitions */
5672 #define UTF16MASK       0x03FFUL
5673 #define UTF16SHIFT      10
5674 #define UTF16BASE       0x10000UL
5675 #define UTF16HIGHSTART  0xD800UL
5676 #define UTF16HIGHEND    0xDBFFUL
5677 #define UTF16LOSTART    0xDC00UL
5678 #define UTF16LOEND      0xDFFFUL
5679
5680
5681 /* Convert an IMAP mailbox to a UTF-8 string.
5682  *  dst needs to have roughly 4 times the storage space of src
5683  *    Hex encoding can triple the size of the input
5684  *    UTF-7 can be slightly denser than UTF-8
5685  *     (worst case: 8 octets UTF-7 becomes 9 octets UTF-8)
5686  */
5687 char* imap_modified_utf7_to_utf8(const char *mbox, gboolean change_spaces)
5688 {
5689   unsigned c, i, bitcount;
5690   unsigned long ucs4, utf16, bitbuf;
5691   unsigned char base64[256];
5692   const char *src;
5693   char *dst, *res  = malloc(2*strlen(mbox)+1);
5694
5695   bitbuf = 0;
5696   dst = res;
5697   src = mbox;
5698   if(!dst) return NULL;
5699   /* initialize modified base64 decoding table */
5700   memset(base64, UNDEFINED, sizeof (base64));
5701   for (i = 0; i < sizeof (base64chars); ++i) {
5702     base64[(unsigned)base64chars[i]] = i;
5703   }
5704
5705   /* loop until end of string */
5706   while (*src != '\0') {
5707     c = *src++;
5708     /* deal with literal characters and &- */
5709     if (c != '&' || *src == '-') {
5710       /* encode literally */
5711       if (change_spaces && c == '_')
5712         *dst++ = ' ';
5713       else
5714         *dst++ = c;
5715       /* skip over the '-' if this is an &- sequence */
5716       if (c == '&') ++src;
5717     } else {
5718       /* convert modified UTF-7 -> UTF-16 -> UCS-4 -> UTF-8 -> HEX */
5719       bitbuf = 0;
5720       bitcount = 0;
5721       ucs4 = 0;
5722       while ((c = base64[(unsigned char) *src]) != UNDEFINED) {
5723         ++src;
5724         bitbuf = (bitbuf << 6) | c;
5725         bitcount += 6;
5726         /* enough bits for a UTF-16 character? */
5727         if (bitcount >= 16) {
5728           bitcount -= 16;
5729           utf16 = (bitcount ? bitbuf >> bitcount
5730                    : bitbuf) & 0xffff;
5731           /* convert UTF16 to UCS4 */
5732           if
5733             (utf16 >= UTF16HIGHSTART && utf16 <= UTF16HIGHEND) {
5734             ucs4 = (utf16 - UTF16HIGHSTART) << UTF16SHIFT;
5735             continue;
5736           } else if
5737             (utf16 >= UTF16LOSTART && utf16 <= UTF16LOEND) {
5738             ucs4 += utf16 - UTF16LOSTART + UTF16BASE;
5739           } else {
5740             ucs4 = utf16;
5741           }
5742
5743           /* convert UTF-16 range of UCS4 to UTF-8 */
5744           if (ucs4 <= 0x7fUL) {
5745             dst[0] = ucs4;
5746             dst += 1;
5747           } else if (ucs4 <= 0x7ffUL) {
5748             dst[0] = 0xc0 | (ucs4 >> 6);
5749             dst[1] = 0x80 | (ucs4 & 0x3f);
5750             dst += 2;
5751           } else if (ucs4 <= 0xffffUL) {
5752             dst[0] = 0xe0 | (ucs4 >> 12);
5753             dst[1] = 0x80 | ((ucs4 >> 6) & 0x3f);
5754             dst[2] = 0x80 | (ucs4 & 0x3f);
5755             dst += 3;
5756           } else {
5757             dst[0] = 0xf0 | (ucs4 >> 18);
5758             dst[1] = 0x80 | ((ucs4 >> 12) & 0x3f);
5759             dst[2] = 0x80 | ((ucs4 >> 6) & 0x3f);
5760             dst[3] = 0x80 | (ucs4 & 0x3f);
5761             dst += 4;
5762           }
5763         }
5764       }
5765       /* skip over trailing '-' in modified UTF-7 encoding */
5766       if (*src == '-') ++src;
5767     }
5768   }
5769   /* terminate destination string */
5770   *dst = '\0';
5771   return res;
5772 }
5773
5774 /* Convert hex coded UTF-8 string to modified UTF-7 IMAP mailbox
5775  *  dst should be about twice the length of src to deal with non-hex
5776  *  coded URLs
5777  */
5778 char* imap_utf8_to_modified_utf7(const char *src, gboolean change_spaces)
5779 {
5780   unsigned int utf8pos, utf8total, c, utf7mode, bitstogo, utf16flag;
5781   unsigned long ucs4 = 0, bitbuf = 0;
5782
5783   /* initialize hex lookup table */
5784   char *dst, *res;
5785
5786   if (!src) return NULL;
5787
5788   res = malloc(2*strlen(src)+1);
5789   dst = res;
5790   if(!dst) return NULL;
5791
5792   utf7mode = 0;
5793   utf8total = 0;
5794   bitstogo = 0;
5795   utf8pos = 0;
5796   while ((c = (unsigned char)*src) != '\0') {
5797     ++src;
5798     /* normal character? */
5799     if (c >= ' ' && c <= '~' && (c != '_' || !change_spaces)) {
5800       /* switch out of UTF-7 mode */
5801       if (utf7mode) {
5802         if (bitstogo) {
5803           *dst++ = base64chars[(bitbuf << (6 - bitstogo)) & 0x3F];
5804         }
5805         *dst++ = '-';
5806         utf7mode = 0;
5807         utf8pos  = 0;
5808         bitstogo = 0;
5809         utf8total= 0;
5810       }
5811       if (change_spaces && c == ' ')
5812         *dst++ = '_';
5813       else
5814         *dst++ = c;
5815       /* encode '&' as '&-' */
5816       if (c == '&') {
5817         *dst++ = '-';
5818       }
5819       continue;
5820     }
5821     /* switch to UTF-7 mode */
5822     if (!utf7mode) {
5823       *dst++ = '&';
5824       utf7mode = 1;
5825     }
5826     /* Encode US-ASCII characters as themselves */
5827     if (c < 0x80) {
5828       ucs4 = c;
5829       utf8total = 1;
5830     } else if (utf8total) {
5831       /* save UTF8 bits into UCS4 */
5832       ucs4 = (ucs4 << 6) | (c & 0x3FUL);
5833       if (++utf8pos < utf8total) {
5834         continue;
5835       }
5836     } else {
5837       utf8pos = 1;
5838       if (c < 0xE0) {
5839         utf8total = 2;
5840         ucs4 = c & 0x1F;
5841       } else if (c < 0xF0) {
5842         utf8total = 3;
5843         ucs4 = c & 0x0F;
5844       } else {
5845         /* NOTE: can't convert UTF8 sequences longer than 4 */
5846         utf8total = 4;
5847         ucs4 = c & 0x03;
5848       }
5849       continue;
5850     }
5851     /* loop to split ucs4 into two utf16 chars if necessary */
5852     utf8total = 0;
5853     do {
5854       if (ucs4 >= UTF16BASE) {
5855         ucs4 -= UTF16BASE;
5856         bitbuf = (bitbuf << 16) | ((ucs4 >> UTF16SHIFT)
5857                                    + UTF16HIGHSTART);
5858         ucs4 = (ucs4 & UTF16MASK) + UTF16LOSTART;
5859         utf16flag = 1;
5860       } else {
5861         bitbuf = (bitbuf << 16) | ucs4;
5862         utf16flag = 0;
5863       }
5864       bitstogo += 16;
5865       /* spew out base64 */
5866       while (bitstogo >= 6) {
5867         bitstogo -= 6;
5868         *dst++ = base64chars[(bitstogo ? (bitbuf >> bitstogo)
5869                               : bitbuf)
5870                              & 0x3F];
5871       }
5872     } while (utf16flag);
5873   }
5874   /* if in UTF-7 mode, finish in ASCII */
5875   if (utf7mode) {
5876     if (bitstogo) {
5877       *dst++ = base64chars[(bitbuf << (6 - bitstogo)) & 0x3F];
5878     }
5879     *dst++ = '-';
5880   }
5881   /* tie off string */
5882   *dst = '\0';
5883   return res;
5884 }