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