2009-11-25 [colin] 3.7.3cvs21
[claws.git] / src / imap.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2009 Hiroyuki Yamamoto and the Claws Mail team
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program. If not, see <http://www.gnu.org/licenses/>.
17  * 
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #  include "config.h"
22 #endif
23
24 #include "defs.h"
25
26 #include <glib.h>
27 #include <glib/gi18n.h>
28 #include <stdio.h>
29 #include <string.h>
30 #include "imap.h"
31 #include "imap_gtk.h"
32 #include "inc.h"
33 #include "xml.h"
34 #include "alertpanel.h"
35
36 #ifdef HAVE_LIBETPAN
37
38 #include <stdlib.h>
39 #include <dirent.h>
40 #include <unistd.h>
41 #include <ctype.h>
42 #include <time.h>
43 #include <errno.h>
44 #if HAVE_ICONV
45 #  include <iconv.h>
46 #endif
47
48 #ifdef USE_GNUTLS
49 #  include "ssl.h"
50 #endif
51
52 #include "folder.h"
53 #include "session.h"
54 #include "procmsg.h"
55 #include "socket.h"
56 #include "recv.h"
57 #include "procheader.h"
58 #include "prefs_account.h"
59 #include "codeconv.h"
60 #include "md5.h"
61 #include "base64.h"
62 #include "utils.h"
63 #include "prefs_common.h"
64 #include "inputdialog.h"
65 #include "log.h"
66 #include "remotefolder.h"
67 #include "claws.h"
68 #include "statusbar.h"
69 #include "msgcache.h"
70 #include "imap-thread.h"
71 #include "account.h"
72 #include "tags.h"
73 #include "main.h"
74
75 typedef struct _IMAPFolder      IMAPFolder;
76 typedef struct _IMAPSession     IMAPSession;
77 typedef struct _IMAPNameSpace   IMAPNameSpace;
78 typedef struct _IMAPFolderItem  IMAPFolderItem;
79
80 #include "prefs_account.h"
81
82 #define IMAP_FOLDER(obj)        ((IMAPFolder *)obj)
83 #define IMAP_FOLDER_ITEM(obj)   ((IMAPFolderItem *)obj)
84 #define IMAP_SESSION(obj)       ((IMAPSession *)obj)
85
86 struct _IMAPFolder
87 {
88         RemoteFolder rfolder;
89
90         /* list of IMAPNameSpace */
91         GList *ns_personal;
92         GList *ns_others;
93         GList *ns_shared;
94         gchar last_seen_separator;
95         guint refcnt;
96         guint max_set_size;
97 };
98
99 struct _IMAPSession
100 {
101         Session session;
102
103         gboolean authenticated;
104
105         GSList *capability;
106         gboolean uidplus;
107
108         gchar *mbox;
109         guint cmd_count;
110
111         /* CLAWS */
112         gboolean folder_content_changed;
113         guint exists;
114         guint recent;
115         guint expunge;
116         guint unseen;
117         guint uid_validity;
118         guint uid_next;
119
120         Folder * folder;
121         gboolean busy;
122         gboolean cancelled;
123         gboolean sens_update_block;
124 };
125
126 struct _IMAPNameSpace
127 {
128         gchar *name;
129         gchar separator;
130 };
131
132 #define IMAPBUFSIZE     8192
133
134 #define IMAP_IS_SEEN(flags)     ((flags & IMAP_FLAG_SEEN) != 0)
135 #define IMAP_IS_ANSWERED(flags) ((flags & IMAP_FLAG_ANSWERED) != 0)
136 #define IMAP_IS_FLAGGED(flags)  ((flags & IMAP_FLAG_FLAGGED) != 0)
137 #define IMAP_IS_DELETED(flags)  ((flags & IMAP_FLAG_DELETED) != 0)
138 #define IMAP_IS_DRAFT(flags)    ((flags & IMAP_FLAG_DRAFT) != 0)
139 #define IMAP_IS_FORWARDED(flags)        ((flags & IMAP_FLAG_FORWARDED) != 0)
140 #define IMAP_IS_SPAM(flags)     ((flags & IMAP_FLAG_SPAM) != 0)
141 #define IMAP_IS_HAM(flags)      ((flags & IMAP_FLAG_HAM) != 0)
142
143
144 #define IMAP4_PORT      143
145 #ifdef USE_GNUTLS
146 #define IMAPS_PORT      993
147 #endif
148
149 #define IMAP_CMD_LIMIT  1000
150
151 enum {
152         ITEM_CAN_CREATE_FLAGS_UNKNOWN = 0,
153         ITEM_CAN_CREATE_FLAGS,
154         ITEM_CANNOT_CREATE_FLAGS
155 };
156
157 struct _IMAPFolderItem
158 {
159         FolderItem item;
160
161         guint lastuid;
162         guint uid_next;
163         GSList *uid_list;
164         gboolean batching;
165
166         GHashTable *flags_set_table;
167         GHashTable *flags_unset_table;
168         guint32 last_change;
169         guint32 last_sync;
170         gboolean should_update;
171         gboolean should_trash_cache;
172         gint can_create_flags;
173
174         GHashTable *tags_set_table;
175         GHashTable *tags_unset_table;
176         GSList *ok_flags;
177
178 };
179
180 static XMLTag *imap_item_get_xml(Folder *folder, FolderItem *item);
181 static void imap_item_set_xml(Folder *folder, FolderItem *item, XMLTag *tag);
182
183 static void imap_folder_init            (Folder         *folder,
184                                          const gchar    *name,
185                                          const gchar    *path);
186
187 static Folder   *imap_folder_new        (const gchar    *name,
188                                          const gchar    *path);
189 static void      imap_folder_destroy    (Folder         *folder);
190
191 static IMAPSession *imap_session_new    (Folder         *folder,
192                                          const PrefsAccount     *account);
193 static gint     imap_session_authenticate(IMAPSession   *session,
194                                           PrefsAccount  *account);
195 static void     imap_session_destroy    (Session        *session);
196
197 static gchar   *imap_fetch_msg          (Folder         *folder, 
198                                          FolderItem     *item, 
199                                          gint            uid);
200 static gchar   *imap_fetch_msg_full     (Folder         *folder, 
201                                          FolderItem     *item, 
202                                          gint            uid,
203                                          gboolean        headers,
204                                          gboolean        body);
205 static void     imap_remove_cached_msg  (Folder         *folder, 
206                                          FolderItem     *item, 
207                                          MsgInfo        *msginfo);
208 static gint     imap_add_msg            (Folder         *folder,
209                                          FolderItem     *dest,
210                                          const gchar    *file, 
211                                          MsgFlags       *flags);
212 static gint     imap_add_msgs           (Folder         *folder, 
213                                          FolderItem     *dest,
214                                          GSList         *file_list,
215                                          GRelation      *relation);
216
217 static gint     imap_copy_msg           (Folder         *folder,
218                                          FolderItem     *dest, 
219                                          MsgInfo        *msginfo);
220 static gint     imap_copy_msgs          (Folder         *folder, 
221                                          FolderItem     *dest, 
222                                          MsgInfoList    *msglist, 
223                                          GRelation      *relation);
224
225 static gint     imap_remove_msg         (Folder         *folder, 
226                                          FolderItem     *item, 
227                                          gint            uid);
228 static gint     imap_remove_msgs        (Folder         *folder, 
229                                          FolderItem     *dest, 
230                                          MsgInfoList    *msglist, 
231                                          GRelation      *relation);
232 static gint     imap_expunge            (Folder         *folder, 
233                                          FolderItem     *dest);
234 static gint     imap_remove_all_msg     (Folder         *folder, 
235                                          FolderItem     *item);
236
237 static gboolean imap_is_msg_changed     (Folder         *folder,
238                                          FolderItem     *item, 
239                                          MsgInfo        *msginfo);
240
241 static gint     imap_close              (Folder         *folder, 
242                                          FolderItem     *item);
243
244 static gint     imap_scan_tree          (Folder         *folder);
245
246 static gint     imap_create_tree        (Folder         *folder);
247
248 static FolderItem *imap_create_folder   (Folder         *folder,
249                                          FolderItem     *parent,
250                                          const gchar    *name);
251 static gint     imap_rename_folder      (Folder         *folder,
252                                          FolderItem     *item, 
253                                          const gchar    *name);
254 static gint     imap_remove_folder      (Folder         *folder, 
255                                          FolderItem     *item);
256
257 static FolderItem *imap_folder_item_new (Folder         *folder);
258 static void imap_folder_item_destroy    (Folder         *folder,
259                                          FolderItem     *item);
260
261 static IMAPSession *imap_session_get    (Folder         *folder);
262
263 static gint imap_auth                   (IMAPSession    *session,
264                                          const gchar    *user,
265                                          const gchar    *pass,
266                                          IMAPAuthType    type);
267
268 static gint imap_scan_tree_recursive    (IMAPSession    *session,
269                                          FolderItem     *item,
270                                          gboolean        subs_only);
271
272 static void imap_create_missing_folders (Folder         *folder);
273 static FolderItem *imap_create_special_folder
274                                         (Folder                 *folder,
275                                          SpecialFolderItemType   stype,
276                                          const gchar            *name);
277
278 static gint imap_do_copy_msgs           (Folder         *folder,
279                                          FolderItem     *dest,
280                                          MsgInfoList    *msglist,
281                                          GRelation      *relation);
282
283 static void imap_delete_all_cached_messages     (FolderItem     *item);
284 static void imap_set_batch              (Folder         *folder,
285                                          FolderItem     *item,
286                                          gboolean        batch);
287 static gint imap_set_message_flags      (IMAPSession    *session,
288                                          IMAPFolderItem *item,
289                                          MsgNumberList  *numlist,
290                                          IMAPFlags       flags,
291                                          GSList         *tags,
292                                          gboolean        is_set);
293 static gint imap_select                 (IMAPSession    *session,
294                                          IMAPFolder     *folder,
295                                          FolderItem     *item,
296                                          gint           *exists,
297                                          gint           *recent,
298                                          gint           *unseen,
299                                          guint32        *uid_validity,
300                                          gint           *can_create_flags,
301                                          gboolean        block);
302 static gint imap_status                 (IMAPSession    *session,
303                                          IMAPFolder     *folder,
304                                          const gchar    *path,
305                                          IMAPFolderItem *item,
306                                          gint           *messages,
307                                          guint32        *uid_next,
308                                          guint32        *uid_validity,
309                                          gint           *unseen,
310                                          gboolean        block);
311 static void     imap_commit_tags        (FolderItem     *item, 
312                                          MsgInfo        *msginfo,
313                                          GSList         *set_tags,
314                                          GSList         *unset_tags);
315
316 static gchar imap_get_path_separator            (IMAPSession    *session,
317                                                  IMAPFolder     *folder,
318                                                  const gchar    *path,
319                                                  gint           *ok);
320 static gchar *imap_get_real_path                (IMAPSession    *session,
321                                                  IMAPFolder     *folder,
322                                                  const gchar    *path,
323                                                  gint           *ok);
324 #ifdef HAVE_LIBETPAN
325 static void imap_synchronise            (FolderItem     *item, gint days);
326 #endif
327 static gboolean imap_is_busy            (Folder *folder);
328
329 static void imap_free_capabilities      (IMAPSession    *session);
330
331 /* low-level IMAP4rev1 commands */
332 static gint imap_cmd_login      (IMAPSession    *session,
333                                  const gchar    *user,
334                                  const gchar    *pass,
335                                  const gchar    *type);
336 static gint imap_cmd_noop       (IMAPSession    *session);
337 #ifdef USE_GNUTLS
338 static gint imap_cmd_starttls   (IMAPSession    *session);
339 #endif
340 static gint imap_cmd_select     (IMAPSession    *session,
341                                  const gchar    *folder,
342                                  gint           *exists,
343                                  gint           *recent,
344                                  gint           *unseen,
345                                  guint32        *uid_validity,
346                                  gint           *can_create_flags,
347                                  GSList         **ok_flags,
348                                  gboolean        block);
349 static gint imap_cmd_close      (IMAPSession    *session);
350 static gint imap_cmd_examine    (IMAPSession    *session,
351                                  const gchar    *folder,
352                                  gint           *exists,
353                                  gint           *recent,
354                                  gint           *unseen,
355                                  guint32        *uid_validity,
356                                  gboolean        block);
357 static gint imap_cmd_create     (IMAPSession    *sock,
358                                  const gchar    *folder);
359 static gint imap_cmd_rename     (IMAPSession    *sock,
360                                  const gchar    *oldfolder,
361                                  const gchar    *newfolder);
362 static gint imap_cmd_delete     (IMAPSession    *session,
363                                  const gchar    *folder);
364 static gint imap_cmd_fetch      (IMAPSession    *sock,
365                                  guint32         uid,
366                                  const gchar    *filename,
367                                  gboolean        headers,
368                                  gboolean        body);
369 static gint imap_cmd_append     (IMAPSession    *session,
370                                  IMAPFolderItem *item,
371                                  const gchar    *destfolder,
372                                  const gchar    *file,
373                                  IMAPFlags       flags,
374                                  guint32        *new_uid);
375 static gint imap_cmd_copy       (IMAPSession *session,
376                                  struct mailimap_set * set,
377                                  const gchar *destfolder,
378                                  struct mailimap_set ** source,
379                                  struct mailimap_set ** dest);
380 static gint imap_cmd_store      (IMAPSession    *session,
381                                  IMAPFolderItem *item,
382                                  struct mailimap_set * set,
383                                  IMAPFlags flags,
384                                  GSList *tags,
385                                  int do_add);
386 static gint imap_cmd_expunge    (IMAPSession    *session, gboolean force);
387
388 static void imap_path_separator_subst           (gchar          *str,
389                                                  gchar           separator);
390
391 static gboolean imap_rename_folder_func         (GNode          *node,
392                                                  gpointer        data);
393 static gint imap_get_num_list                   (Folder         *folder,
394                                                  FolderItem     *item,
395                                                  GSList        **list,
396                                                  gboolean       *old_uids_valid);
397 static GSList *imap_get_msginfos                (Folder         *folder,
398                                                  FolderItem     *item,
399                                                  GSList         *msgnum_list);
400 static MsgInfo *imap_get_msginfo                (Folder         *folder,
401                                                  FolderItem     *item,
402                                                  gint            num);
403 static gboolean imap_scan_required              (Folder         *folder,
404                                                  FolderItem     *item);
405 static void imap_change_flags                   (Folder         *folder,
406                                                  FolderItem     *item,
407                                                  MsgInfo        *msginfo,
408                                                  MsgPermFlags    newflags);
409 static gint imap_get_flags                      (Folder         *folder,
410                                                  FolderItem     *item,
411                                                  MsgInfoList    *msglist,
412                                                  GRelation      *msgflags);
413 static gchar *imap_folder_get_path              (Folder         *folder);
414 static gchar *imap_item_get_path                (Folder         *folder,
415                                                  FolderItem     *item);
416 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item);
417
418
419 /* data types conversion libetpan <-> claws */
420 static GSList * imap_list_from_lep(IMAPFolder * folder,
421                                    clist * list, const gchar * real_path, gboolean all);
422 static GSList * imap_get_lep_set_from_numlist(IMAPFolder *folder, MsgNumberList *numlist);
423 static GSList * imap_get_lep_set_from_msglist(IMAPFolder *folder, MsgInfoList *msglist);
424 static GSList * imap_uid_list_from_lep(clist * list);
425 static GSList * imap_uid_list_from_lep_tab(carray * list);
426 static void imap_flags_hash_from_lep_uid_flags_tab(carray * list,
427                                                    GHashTable * hash,
428                                                    GHashTable *tags_hash);
429 static MsgInfo *imap_envelope_from_lep(struct imap_fetch_env_info * info,
430                                        FolderItem *item);
431 static void imap_lep_set_free(GSList *seq_list);
432 static struct mailimap_flag_list * imap_flag_to_lep(IMAPFolderItem *item, IMAPFlags flags, GSList *tags);
433
434 typedef struct _hashtable_data {
435         GSList *msglist;
436         IMAPFolderItem *item;
437 } hashtable_data;
438
439 static FolderClass imap_class;
440
441 FolderClass *imap_get_class(void)
442 {
443         if (imap_class.idstr == NULL) {
444                 imap_class.type = F_IMAP;
445                 imap_class.idstr = "imap";
446                 imap_class.uistr = "IMAP4";
447
448                 /* Folder functions */
449                 imap_class.new_folder = imap_folder_new;
450                 imap_class.destroy_folder = imap_folder_destroy;
451                 imap_class.scan_tree = imap_scan_tree;
452                 imap_class.create_tree = imap_create_tree;
453
454                 /* FolderItem functions */
455                 imap_class.item_new = imap_folder_item_new;
456                 imap_class.item_destroy = imap_folder_item_destroy;
457                 imap_class.item_get_path = imap_item_get_path;
458                 imap_class.create_folder = imap_create_folder;
459                 imap_class.rename_folder = imap_rename_folder;
460                 imap_class.remove_folder = imap_remove_folder;
461                 imap_class.close = imap_close;
462                 imap_class.get_num_list = imap_get_num_list;
463                 imap_class.scan_required = imap_scan_required;
464                 imap_class.set_xml = folder_set_xml;
465                 imap_class.get_xml = folder_get_xml;
466                 imap_class.item_set_xml = imap_item_set_xml;
467                 imap_class.item_get_xml = imap_item_get_xml;
468
469                 /* Message functions */
470                 imap_class.get_msginfo = imap_get_msginfo;
471                 imap_class.get_msginfos = imap_get_msginfos;
472                 imap_class.fetch_msg = imap_fetch_msg;
473                 imap_class.fetch_msg_full = imap_fetch_msg_full;
474                 imap_class.add_msg = imap_add_msg;
475                 imap_class.add_msgs = imap_add_msgs;
476                 imap_class.copy_msg = imap_copy_msg;
477                 imap_class.copy_msgs = imap_copy_msgs;
478                 imap_class.remove_msg = imap_remove_msg;
479                 imap_class.remove_msgs = imap_remove_msgs;
480                 imap_class.expunge = imap_expunge;
481                 imap_class.remove_all_msg = imap_remove_all_msg;
482                 imap_class.is_msg_changed = imap_is_msg_changed;
483                 imap_class.change_flags = imap_change_flags;
484                 imap_class.get_flags = imap_get_flags;
485                 imap_class.set_batch = imap_set_batch;
486                 imap_class.synchronise = imap_synchronise;
487                 imap_class.remove_cached_msg = imap_remove_cached_msg;
488                 imap_class.commit_tags = imap_commit_tags;
489 #ifdef USE_PTREAD
490                 pthread_mutex_init(&imap_mutex, NULL);
491 #endif
492         }
493         
494         return &imap_class;
495 }
496
497 static void imap_refresh_sensitivity (IMAPSession *session)
498 {
499         MainWindow *mainwin;
500
501         if (session->sens_update_block)
502                 return;
503         mainwin = mainwindow_get_mainwindow();
504         if (mainwin) {
505                 toolbar_main_set_sensitive(mainwin);
506                 main_window_set_menu_sensitive(mainwin);
507         }
508 }
509
510 static void lock_session(IMAPSession *session)
511 {
512         if (session) {
513                 debug_print("locking session %p (%d)\n", session, session->busy);
514                 if (session->busy)
515                         debug_print("         SESSION WAS LOCKED !!      \n");
516                 session->busy = TRUE;
517                 imap_refresh_sensitivity(session);
518         } else {
519                 debug_print("can't lock null session\n");
520         }
521 }
522
523 static void unlock_session(IMAPSession *session)
524 {
525         if (session) {
526                 debug_print("unlocking session %p\n", session);
527                 session->busy = FALSE;
528                 imap_refresh_sensitivity(session);
529         } else {
530                 debug_print("can't unlock null session\n");
531         }
532 }
533
534 static void imap_disc_session_destroy(IMAPSession *session)
535 {
536         RemoteFolder *rfolder = NULL;
537
538         if (session == NULL)
539                 return;
540
541         rfolder = REMOTE_FOLDER(IMAP_SESSION(session)->folder);
542         
543         if (rfolder == NULL)
544                 return;
545         log_warning(LOG_PROTOCOL, _("IMAP4 connection broken\n"));
546         SESSION(session)->state = SESSION_DISCONNECTED;
547         SESSION(session)->sock = NULL;
548 }
549
550 static gboolean is_fatal(int libetpan_errcode)
551 {
552         switch(libetpan_errcode) {
553         case MAILIMAP_ERROR_STREAM:
554         case MAILIMAP_ERROR_PROTOCOL:
555         case MAILIMAP_ERROR_PARSE:
556         case MAILIMAP_ERROR_BAD_STATE:
557                 return TRUE;
558         default:
559                 return FALSE;
560         }
561 }
562
563 static void imap_handle_error(Session *session, int libetpan_errcode)
564 {
565         const gchar *session_server = (session ? session->server:"(null)");
566         switch(libetpan_errcode) {
567         case MAILIMAP_NO_ERROR:
568                 return;
569         case MAILIMAP_NO_ERROR_AUTHENTICATED:
570                 log_warning(LOG_PROTOCOL, _("IMAP error on %s: authenticated\n"), session_server);
571                 break;
572         case MAILIMAP_NO_ERROR_NON_AUTHENTICATED:
573                 log_warning(LOG_PROTOCOL, _("IMAP error on %s: not authenticated\n"), session_server);
574                 break;
575         case MAILIMAP_ERROR_BAD_STATE:
576                 log_warning(LOG_PROTOCOL, _("IMAP error on %s: bad state\n"), session_server);
577                 break;
578         case MAILIMAP_ERROR_STREAM:
579                 log_warning(LOG_PROTOCOL, _("IMAP error on %s: stream error\n"), session_server);
580                 break;
581         case MAILIMAP_ERROR_PARSE:
582                 log_warning(LOG_PROTOCOL, _("IMAP error on %s: parse error "
583                                             "(very probably non-RFC compliance from the server)\n"), session_server);
584                 break;
585         case MAILIMAP_ERROR_CONNECTION_REFUSED:
586                 log_warning(LOG_PROTOCOL, _("IMAP error on %s: connection refused\n"), session_server);
587                 break;
588         case MAILIMAP_ERROR_MEMORY:
589                 log_warning(LOG_PROTOCOL, _("IMAP error on %s: memory error\n"), session_server);
590                 break;
591         case MAILIMAP_ERROR_FATAL:
592                 log_warning(LOG_PROTOCOL, _("IMAP error on %s: fatal error\n"), session_server);
593                 break;
594         case MAILIMAP_ERROR_PROTOCOL:
595                 log_warning(LOG_PROTOCOL, _("IMAP error on %s: protocol error"
596                                             "(very probably non-RFC compliance from the server)\n"), session_server);
597                 break;
598         case MAILIMAP_ERROR_DONT_ACCEPT_CONNECTION:
599                 log_warning(LOG_PROTOCOL, _("IMAP error on %s: connection not accepted\n"), session_server);
600                 break;
601         case MAILIMAP_ERROR_APPEND:
602                 log_warning(LOG_PROTOCOL, _("IMAP error on %s: APPEND error\n"), session_server);
603                 break;
604         case MAILIMAP_ERROR_NOOP:
605                 log_warning(LOG_PROTOCOL, _("IMAP error on %s: NOOP error\n"), session_server);
606                 break;
607         case MAILIMAP_ERROR_LOGOUT:
608                 log_warning(LOG_PROTOCOL, _("IMAP error on %s: LOGOUT error\n"), session_server);
609                 break;
610         case MAILIMAP_ERROR_CAPABILITY:
611                 log_warning(LOG_PROTOCOL, _("IMAP error on %s: CAPABILITY error\n"), session_server);
612                 break;
613         case MAILIMAP_ERROR_CHECK:
614                 log_warning(LOG_PROTOCOL, _("IMAP error on %s: CHECK error\n"), session_server);
615                 break;
616         case MAILIMAP_ERROR_CLOSE:
617                 log_warning(LOG_PROTOCOL, _("IMAP error on %s: CLOSE error\n"), session_server);
618                 break;
619         case MAILIMAP_ERROR_EXPUNGE:
620                 log_warning(LOG_PROTOCOL, _("IMAP error on %s: EXPUNGE error\n"), session_server);
621                 break;
622         case MAILIMAP_ERROR_COPY:
623                 log_warning(LOG_PROTOCOL, _("IMAP error on %s: COPY error\n"), session_server);
624                 break;
625         case MAILIMAP_ERROR_UID_COPY:
626                 log_warning(LOG_PROTOCOL, _("IMAP error on %s: UID COPY error\n"), session_server);
627                 break;
628         case MAILIMAP_ERROR_CREATE:
629                 log_warning(LOG_PROTOCOL, _("IMAP error on %s: CREATE error\n"), session_server);
630                 break;
631         case MAILIMAP_ERROR_DELETE:
632                 log_warning(LOG_PROTOCOL, _("IMAP error on %s: DELETE error\n"), session_server);
633                 break;
634         case MAILIMAP_ERROR_EXAMINE:
635                 log_warning(LOG_PROTOCOL, _("IMAP error on %s: EXAMINE error\n"), session_server);
636                 break;
637         case MAILIMAP_ERROR_FETCH:
638                 log_warning(LOG_PROTOCOL, _("IMAP error on %s: FETCH error\n"), session_server);
639                 break;
640         case MAILIMAP_ERROR_UID_FETCH:
641                 log_warning(LOG_PROTOCOL, _("IMAP error on %s: UID FETCH error\n"), session_server);
642                 break;
643         case MAILIMAP_ERROR_LIST:
644                 log_warning(LOG_PROTOCOL, _("IMAP error on %s: LIST error\n"), session_server);
645                 break;
646         case MAILIMAP_ERROR_LOGIN:
647                 log_warning(LOG_PROTOCOL, _("IMAP error on %s: LOGIN error\n"), session_server);
648                 break;
649         case MAILIMAP_ERROR_LSUB:
650                 log_warning(LOG_PROTOCOL, _("IMAP error on %s: LSUB error\n"), session_server);
651                 break;
652         case MAILIMAP_ERROR_RENAME:
653                 log_warning(LOG_PROTOCOL, _("IMAP error on %s: RENAME error\n"), session_server);
654                 break;
655         case MAILIMAP_ERROR_SEARCH:
656                 log_warning(LOG_PROTOCOL, _("IMAP error on %s: SEARCH error\n"), session_server);
657                 break;
658         case MAILIMAP_ERROR_UID_SEARCH:
659                 log_warning(LOG_PROTOCOL, _("IMAP error on %s: UID SEARCH error\n"), session_server);
660                 break;
661         case MAILIMAP_ERROR_SELECT:
662                 log_warning(LOG_PROTOCOL, _("IMAP error on %s: SELECT error\n"), session_server);
663                 break;
664         case MAILIMAP_ERROR_STATUS:
665                 log_warning(LOG_PROTOCOL, _("IMAP error on %s: STATUS error\n"), session_server);
666                 break;
667         case MAILIMAP_ERROR_STORE:
668                 log_warning(LOG_PROTOCOL, _("IMAP error on %s: STORE error\n"), session_server);
669                 break;
670         case MAILIMAP_ERROR_UID_STORE:
671                 log_warning(LOG_PROTOCOL, _("IMAP error on %s: UID STORE error\n"), session_server);
672                 break;
673         case MAILIMAP_ERROR_SUBSCRIBE:
674                 log_warning(LOG_PROTOCOL, _("IMAP error on %s: SUBSCRIBE error\n"), session_server);
675                 break;
676         case MAILIMAP_ERROR_UNSUBSCRIBE:
677                 log_warning(LOG_PROTOCOL, _("IMAP error on %s: UNSUBSCRIBE error\n"), session_server);
678                 break;
679         case MAILIMAP_ERROR_STARTTLS:
680                 log_warning(LOG_PROTOCOL, _("IMAP error on %s: STARTTLS error\n"), session_server);
681                 break;
682         case MAILIMAP_ERROR_INVAL:
683                 log_warning(LOG_PROTOCOL, _("IMAP error on %s: INVAL error\n"), session_server);
684                 break;
685         case MAILIMAP_ERROR_EXTENSION:
686                 log_warning(LOG_PROTOCOL, _("IMAP error on %s: EXTENSION error\n"), session_server);
687                 break;
688         case MAILIMAP_ERROR_SASL:
689                 log_warning(LOG_PROTOCOL, _("IMAP error on %s: SASL error\n"), session_server);
690                 break;
691 #ifdef USE_GNUTLS
692         case MAILIMAP_ERROR_SSL:
693                 log_warning(LOG_PROTOCOL, _("IMAP error on %s: SSL error\n"), session_server);
694                 break;
695 #endif
696         default:
697                 log_warning(LOG_PROTOCOL, _("IMAP error on %s: Unknown error [%d]\n"),
698                         session_server, libetpan_errcode);
699                 break;
700         }
701
702         if (session && is_fatal(libetpan_errcode)) {
703                 imap_disc_session_destroy(IMAP_SESSION(session));
704         } else if (session && !is_fatal(libetpan_errcode)) {
705                 if (IMAP_SESSION(session)->busy)
706                         unlock_session(IMAP_SESSION(session));
707         }
708 }
709
710 static Folder *imap_folder_new(const gchar *name, const gchar *path)
711 {
712         Folder *folder;
713
714         folder = (Folder *)g_new0(IMAPFolder, 1);
715         folder->klass = &imap_class;
716         imap_folder_init(folder, name, path);
717
718         return folder;
719 }
720
721 static void imap_folder_destroy(Folder *folder)
722 {
723         while (imap_folder_get_refcnt(folder) > 0)
724                 gtk_main_iteration();
725         
726         folder_remote_folder_destroy(REMOTE_FOLDER(folder));
727         imap_done(folder);
728 }
729
730 static void imap_folder_init(Folder *folder, const gchar *name,
731                              const gchar *path)
732 {
733         folder_remote_folder_init((Folder *)folder, name, path);
734         IMAP_FOLDER(folder)->max_set_size = IMAP_SET_MAX_COUNT;
735 }
736
737 static FolderItem *imap_folder_item_new(Folder *folder)
738 {
739         IMAPFolderItem *item;
740         
741         item = g_new0(IMAPFolderItem, 1);
742         item->lastuid = 0;
743         item->uid_next = 0;
744         item->uid_list = NULL;
745
746         return (FolderItem *)item;
747 }
748
749 static void imap_folder_item_destroy(Folder *folder, FolderItem *_item)
750 {
751         IMAPFolderItem *item = (IMAPFolderItem *)_item;
752
753         g_return_if_fail(item != NULL);
754         g_slist_free(item->uid_list);
755
756         g_free(_item);
757 }
758
759 static gboolean imap_reset_uid_lists_func(GNode *node, gpointer data)
760 {
761         IMAPFolderItem *item = (IMAPFolderItem *)node->data;
762         
763         item->lastuid = 0;
764         g_slist_free(item->uid_list);
765         item->uid_list = NULL;
766         
767         return FALSE;
768 }
769
770 static void imap_reset_uid_lists(Folder *folder)
771 {
772         if(folder->node == NULL)
773                 return;
774         
775         /* Destroy all uid lists and rest last uid */
776         g_node_traverse(folder->node, G_IN_ORDER, G_TRAVERSE_ALL, -1, imap_reset_uid_lists_func, NULL); 
777 }
778
779 static int imap_get_capabilities(IMAPSession *session)
780 {
781         struct mailimap_capability_data *capabilities = NULL;
782         clistiter *cur;
783         int result = -1;
784
785         if (session->capability != NULL)
786                 return MAILIMAP_NO_ERROR;
787
788         capabilities = imap_threaded_capability(session->folder, &result);
789
790         if (result != MAILIMAP_NO_ERROR) {
791                 return result;
792         }
793
794         if (capabilities == NULL || capabilities->cap_list == NULL) {
795                 return MAILIMAP_NO_ERROR;
796         }
797
798         for(cur = clist_begin(capabilities->cap_list) ; cur != NULL ;
799             cur = clist_next(cur)) {
800                 struct mailimap_capability * cap = 
801                         clist_content(cur);
802                 if (!cap || cap->cap_data.cap_name == NULL)
803                         continue;
804                 session->capability = g_slist_append
805                                 (session->capability,
806                                  g_strdup(cap->cap_data.cap_name));
807                 debug_print("got capa %s\n", cap->cap_data.cap_name);
808         }
809         mailimap_capability_data_free(capabilities);
810         return MAILIMAP_NO_ERROR;
811 }
812
813 static gboolean imap_has_capability(IMAPSession *session, const gchar *cap) 
814 {
815         GSList *cur;
816         for (cur = session->capability; cur; cur = cur->next) {
817                 if (!g_ascii_strcasecmp(cur->data, cap))
818                         return TRUE;
819         }
820         return FALSE;
821 }
822
823 static gint imap_auth(IMAPSession *session, const gchar *user, const gchar *pass,
824                       IMAPAuthType type)
825 {
826         gint ok = MAILIMAP_ERROR_LOGIN;
827         static time_t last_login_err = 0;
828         gchar *ext_info = "";
829         int r;
830         gchar *server = NULL;
831         if ((r = imap_get_capabilities(session)) != MAILIMAP_NO_ERROR) {
832                 imap_handle_error(SESSION(session), r);
833                 return r;
834         }
835         server = g_strdup(SESSION(session)->server);
836         switch(type) {
837         case IMAP_AUTH_ANON:
838                 ok = imap_cmd_login(session, user, pass, "ANONYMOUS");
839                 break;
840         case IMAP_AUTH_CRAM_MD5:
841                 ok = imap_cmd_login(session, user, pass, "CRAM-MD5");
842                 break;
843         case IMAP_AUTH_DIGEST_MD5:
844                 ok = imap_cmd_login(session, user, pass, "DIGEST-MD5");
845                 break;
846         case IMAP_AUTH_LOGIN:
847                 ok = imap_cmd_login(session, user, pass, "LOGIN");
848                 break;
849         case IMAP_AUTH_GSSAPI:
850                 ok = imap_cmd_login(session, user, pass, "GSSAPI");
851                 break;
852         default:
853                 debug_print("capabilities:\n"
854                                 "\t ANONYMOUS %d\n"
855                                 "\t CRAM-MD5 %d\n"
856                                 "\t DIGEST-MD5 %d\n"
857                                 "\t LOGIN %d\n"
858                                 "\t GSSAPI %d\n", 
859                         imap_has_capability(session, "ANONYMOUS"),
860                         imap_has_capability(session, "CRAM-MD5"),
861                         imap_has_capability(session, "DIGEST-MD5"),
862                         imap_has_capability(session, "LOGIN"),
863                         imap_has_capability(session, "GSSAPI"));
864                 if (imap_has_capability(session, "CRAM-MD5"))
865                         ok = imap_cmd_login(session, user, pass, "CRAM-MD5");
866                 if (ok == MAILIMAP_ERROR_LOGIN && imap_has_capability(session, "DIGEST-MD5"))
867                         ok = imap_cmd_login(session, user, pass, "DIGEST-MD5");
868                 if (ok == MAILIMAP_ERROR_LOGIN && imap_has_capability(session, "GSSAPI"))
869                         ok = imap_cmd_login(session, user, pass, "GSSAPI");
870                 if (ok == MAILIMAP_ERROR_LOGIN) /* we always try LOGIN before giving up */
871                         ok = imap_cmd_login(session, user, pass, "LOGIN");
872         }
873
874         if (ok == MAILIMAP_NO_ERROR)
875                 session->authenticated = TRUE;
876         else {
877                 if (type == IMAP_AUTH_CRAM_MD5) {
878                         ext_info = _("\n\nCRAM-MD5 logins only work if libetpan has been "
879                                      "compiled with SASL support and the "
880                                      "CRAM-MD5 SASL plugin is installed.");
881                 } 
882
883                 if (type == IMAP_AUTH_DIGEST_MD5) {
884                         ext_info = _("\n\nDIGEST-MD5 logins only work if libetpan has been "
885                                      "compiled with SASL support and the "
886                                      "DIGEST-MD5 SASL plugin is installed.");
887                 } 
888
889                 if (time(NULL) - last_login_err > 10) {
890                         if (!prefs_common.no_recv_err_panel) {
891                                 alertpanel_error_log(_("Connection to %s failed: "
892                                         "login refused.%s"),
893                                         server, ext_info);
894                         } else {
895                                 log_error(LOG_PROTOCOL, _("Connection to %s failed: "
896                                         "login refused.%s\n"),
897                                         server, ext_info);
898                         }
899                 }
900                 last_login_err = time(NULL);
901         }
902         g_free(server);
903         return ok;
904 }
905
906 static IMAPSession *imap_reconnect_if_possible(Folder *folder, IMAPSession *session)
907 {
908         RemoteFolder *rfolder = REMOTE_FOLDER(folder);
909         /* Check if this is the first try to establish a
910            connection, if yes we don't try to reconnect */
911         debug_print("reconnecting\n");
912         if (rfolder->session == NULL) {
913                 log_warning(LOG_PROTOCOL, _("Connecting to %s failed"),
914                             folder->account->recv_server);
915                 SESSION(session)->sock = NULL;
916                 session_destroy(SESSION(session));
917                 session = NULL;
918         } else {
919                 rfolder->session = NULL;
920                 log_warning(LOG_PROTOCOL, _("IMAP4 connection to %s has been"
921                             " disconnected. Reconnecting...\n"),
922                             folder->account->recv_server);
923                 statusbar_print_all(_("IMAP4 connection to %s has been"
924                             " disconnected. Reconnecting...\n"),
925                             folder->account->recv_server);
926                 SESSION(session)->state = SESSION_DISCONNECTED;
927                 SESSION(session)->sock = NULL;
928                 session_destroy(SESSION(session));
929                 /* Clear folders session to make imap_session_get create
930                    a new session, because of rfolder->session == NULL
931                    it will not try to reconnect again and so avoid an
932                    endless loop */
933                 debug_print("getting session...\n");
934                 session = imap_session_get(folder);
935                 rfolder->session = SESSION(session);
936                 statusbar_pop_all();
937         }
938         return session;
939 }
940
941 static IMAPSession *imap_session_get(Folder *folder)
942 {
943         RemoteFolder *rfolder = REMOTE_FOLDER(folder);
944         IMAPSession *session = NULL;
945         gint r = MAILIMAP_NO_ERROR;
946
947         g_return_val_if_fail(folder != NULL, NULL);
948         g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, NULL);
949         g_return_val_if_fail(folder->account != NULL, NULL);
950         
951         if (prefs_common.work_offline && 
952             !inc_offline_should_override(FALSE,
953                 _("Claws Mail needs network access in order "
954                   "to access the IMAP server."))) {
955                 return NULL;
956         }
957
958         /* Make sure we have a session */
959         if (rfolder->session != NULL && rfolder->session->state != SESSION_DISCONNECTED) {
960                 session = IMAP_SESSION(rfolder->session);
961         } else if (rfolder->session != NULL && rfolder->session->state == SESSION_DISCONNECTED) {
962                 session_destroy(SESSION(rfolder->session));
963                 rfolder->session = NULL;
964                 goto new_conn;
965         } else if (rfolder->connecting) {
966                 debug_print("already connecting\n");
967                 return NULL;
968         } else {
969 new_conn:
970                 imap_reset_uid_lists(folder);
971                 if (time(NULL) - rfolder->last_failure <= 2)
972                         return NULL;
973                 rfolder->connecting = TRUE;
974                 session = imap_session_new(folder, folder->account);
975         }
976         if(session == NULL) {
977                 rfolder->last_failure = time(NULL);
978                 rfolder->connecting = FALSE;
979                 return NULL;
980         }
981
982         /* Make sure session is authenticated */
983         if (!IMAP_SESSION(session)->authenticated)
984                 r = imap_session_authenticate(IMAP_SESSION(session), folder->account);
985         
986         if (r != MAILIMAP_NO_ERROR || (!is_fatal(r) && !IMAP_SESSION(session)->authenticated)) {
987                 rfolder->session = NULL;
988                 if (!is_fatal(r)) {
989                         imap_threaded_disconnect(session->folder);
990                         SESSION(session)->state = SESSION_DISCONNECTED;
991                         SESSION(session)->sock = NULL;
992                         session_destroy(SESSION(session));
993                 }
994                 rfolder->last_failure = time(NULL);
995                 rfolder->connecting = FALSE;
996                 return NULL;
997         }
998
999         /* I think the point of this code is to avoid sending a
1000          * keepalive if we've used the session recently and therefore
1001          * think it's still alive.  Unfortunately, most of the code
1002          * does not yet check for errors on the socket, and so if the
1003          * connection drops we don't notice until the timeout expires.
1004          * A better solution than sending a NOOP every time would be
1005          * for every command to be prepared to retry until it is
1006          * successfully sent. -- mbp */
1007         if ((time(NULL) - SESSION(session)->last_access_time > SESSION_TIMEOUT_INTERVAL) || session->cancelled) {
1008                 /* verify that the session is still alive */
1009                 if ((r = imap_cmd_noop(session)) != MAILIMAP_NO_ERROR) {
1010                         debug_print("disconnected!\n");
1011                         if (!is_fatal(r))
1012                                 session = imap_reconnect_if_possible(folder, session);
1013                         else {
1014                                 rfolder->session = NULL;
1015                                 rfolder->connecting = FALSE;
1016                                 session = imap_session_get(folder);
1017                         }
1018                 }
1019                 if (session)
1020                         session->cancelled = FALSE;
1021         }
1022
1023         rfolder->session = SESSION(session);
1024         rfolder->connecting = FALSE;
1025
1026         return IMAP_SESSION(session);
1027 }
1028
1029 static IMAPSession *imap_session_new(Folder * folder,
1030                                      const PrefsAccount *account)
1031 {
1032         IMAPSession *session;
1033         gushort port;
1034         int r;
1035         int authenticated = FALSE;
1036         
1037 #ifdef USE_GNUTLS
1038         /* FIXME: IMAP over SSL only... */ 
1039         SSLType ssl_type;
1040
1041         port = account->set_imapport ? account->imapport
1042                 : account->ssl_imap == SSL_TUNNEL ? IMAPS_PORT : IMAP4_PORT;
1043         ssl_type = account->ssl_imap;   
1044 #else
1045         if (account->ssl_imap != SSL_NONE) {
1046                 if (alertpanel_full(_("Insecure connection"),
1047                         _("This connection is configured to be secured "
1048                           "using SSL, but SSL is not available in this "
1049                           "build of Claws Mail. \n\n"
1050                           "Do you want to continue connecting to this "
1051                           "server? The communication would not be "
1052                           "secure."),
1053                           GTK_STOCK_CANCEL, _("Con_tinue connecting"), 
1054                           NULL, FALSE, NULL, ALERT_WARNING,
1055                           G_ALERTDEFAULT) != G_ALERTALTERNATE)
1056                         return NULL;
1057         }
1058         port = account->set_imapport ? account->imapport
1059                 : IMAP4_PORT;
1060 #endif
1061
1062         imap_init(folder);
1063         statuswindow_print_all(_("Connecting to IMAP4 server: %s..."), folder->account->recv_server);
1064 #ifndef G_OS_WIN32
1065         if (account->set_tunnelcmd) {
1066                 r = imap_threaded_connect_cmd(folder,
1067                                               account->tunnelcmd,
1068                                               account->recv_server,
1069                                               port);
1070         }
1071         else 
1072 #endif
1073         {
1074 #ifdef USE_GNUTLS
1075                 if (ssl_type == SSL_TUNNEL) {
1076                         r = imap_threaded_connect_ssl(folder,
1077                                                       account->recv_server,
1078                                                       port);
1079                 }
1080                 else 
1081 #endif
1082                 {
1083                         r = imap_threaded_connect(folder,
1084                                                   account->recv_server,
1085                                                   port);
1086                 }
1087         }
1088         
1089         statuswindow_pop_all();
1090         if (r == MAILIMAP_NO_ERROR_AUTHENTICATED) {
1091                 authenticated = TRUE;
1092         }
1093         else if (r == MAILIMAP_NO_ERROR_NON_AUTHENTICATED) {
1094                 authenticated = FALSE;
1095         }
1096         else {
1097 #ifdef USE_GNUTLS
1098                 if (r == MAILIMAP_ERROR_SSL)
1099                         log_error(LOG_PROTOCOL, _("SSL handshake failed\n"));
1100                 else
1101 #endif
1102                         imap_handle_error(NULL, r);
1103
1104                 if(!prefs_common.no_recv_err_panel) {
1105                         alertpanel_error_log(_("Can't connect to IMAP4 server: %s:%d"),
1106                                          account->recv_server, port);
1107                 } else {
1108                         log_error(LOG_PROTOCOL, _("Can't connect to IMAP4 server: %s:%d\n"),
1109                                          account->recv_server, port);
1110                 } 
1111                 
1112                 return NULL;
1113         }
1114         
1115         session = g_new0(IMAPSession, 1);
1116         session_init(SESSION(session), account, FALSE);
1117         SESSION(session)->type             = SESSION_IMAP;
1118         SESSION(session)->server           = g_strdup(account->recv_server);
1119         SESSION(session)->port             = port;
1120         SESSION(session)->sock             = NULL;
1121         
1122         SESSION(session)->destroy          = imap_session_destroy;
1123
1124         session->capability = NULL;
1125         
1126         session->authenticated = authenticated;
1127         session->mbox = NULL;
1128         session->exists = 0;
1129         session->recent = 0;
1130         session->expunge = 0;
1131         session->cmd_count = 0;
1132         session->folder = folder;
1133         IMAP_FOLDER(session->folder)->last_seen_separator = 0;
1134
1135 #ifdef USE_GNUTLS
1136         if (account->ssl_imap == SSL_STARTTLS) {
1137                 gint ok;
1138
1139                 ok = imap_cmd_starttls(session);
1140                 if (ok != MAILIMAP_NO_ERROR) {
1141                         log_warning(LOG_PROTOCOL, _("Can't start TLS session.\n"));
1142                         if (!is_fatal(ok)) {
1143                                 SESSION(session)->sock = NULL;
1144                                 session_destroy(SESSION(session));
1145                         }
1146                         return NULL;
1147                 }
1148
1149                 imap_free_capabilities(session);
1150                 session->authenticated = FALSE;
1151                 session->uidplus = FALSE;
1152                 session->cmd_count = 1;
1153         }
1154 #endif
1155         log_message(LOG_PROTOCOL, "IMAP connection is %s-authenticated\n",
1156                     (session->authenticated) ? "pre" : "un");
1157         
1158         return session;
1159 }
1160
1161 static gint imap_session_authenticate(IMAPSession *session, 
1162                                       PrefsAccount *account)
1163 {
1164         gchar *pass, *acc_pass;
1165         gboolean failed = FALSE;
1166         gint ok = MAILIMAP_NO_ERROR;
1167         g_return_val_if_fail(account->userid != NULL, MAILIMAP_ERROR_BAD_STATE);
1168         acc_pass = account->passwd;
1169 try_again:
1170         pass = acc_pass;
1171         if (!pass && account->imap_auth_type != IMAP_AUTH_ANON && account->imap_auth_type != IMAP_AUTH_GSSAPI) {
1172                 gchar *tmp_pass;
1173                 tmp_pass = input_dialog_query_password_keep(account->recv_server, 
1174                                                             account->userid,
1175                                                             &(account->session_passwd));
1176                 if (!tmp_pass)
1177                         return MAILIMAP_NO_ERROR;
1178                 Xstrdup_a(pass, tmp_pass, {g_free(tmp_pass); return MAILIMAP_NO_ERROR;});
1179                 g_free(tmp_pass);
1180         } else if (account->imap_auth_type == IMAP_AUTH_ANON || account->imap_auth_type == IMAP_AUTH_GSSAPI) {
1181                 pass = "";
1182         }
1183         statuswindow_print_all(_("Connecting to IMAP4 server %s...\n"),
1184                                 account->recv_server);
1185         if ((ok = imap_auth(session, account->userid, pass, account->imap_auth_type)) != MAILIMAP_NO_ERROR) {
1186                 statusbar_pop_all();
1187                 
1188                 if (!failed && !is_fatal(ok)) {
1189                         acc_pass = NULL;
1190                         failed = TRUE;
1191                         if (account->session_passwd != NULL) {
1192                                 g_free(account->session_passwd);
1193                                 account->session_passwd = NULL;
1194                         }
1195                         goto try_again;
1196                 } else {
1197                         if (prefs_common.no_recv_err_panel) {
1198                                 log_error(LOG_PROTOCOL, _("Couldn't login to IMAP server %s.\n"), account->recv_server);
1199                                 mainwindow_show_error();
1200                         } else
1201                                 alertpanel_error_log(_("Couldn't login to IMAP server %s."), account->recv_server);
1202                 }               
1203
1204                 return ok;
1205         } 
1206
1207         statuswindow_pop_all();
1208         session->authenticated = TRUE;
1209         return MAILIMAP_NO_ERROR;
1210 }
1211
1212 static void imap_session_destroy(Session *session)
1213 {
1214         if (session->state != SESSION_DISCONNECTED)
1215                 imap_threaded_disconnect(IMAP_SESSION(session)->folder);
1216         
1217         imap_free_capabilities(IMAP_SESSION(session));
1218         g_free(IMAP_SESSION(session)->mbox);
1219 }
1220
1221 static gchar *imap_fetch_msg(Folder *folder, FolderItem *item, gint uid)
1222 {
1223         return imap_fetch_msg_full(folder, item, uid, TRUE, TRUE);
1224 }
1225
1226 static guint get_file_size_with_crs(const gchar *filename) 
1227 {
1228         FILE *fp = NULL;
1229         guint cnt = 0;
1230         gchar buf[4096];
1231         
1232         if (filename == NULL)
1233                 return -1;
1234         
1235         fp = g_fopen(filename, "rb");
1236         if (!fp)
1237                 return -1;
1238         
1239         while (fgets(buf, sizeof (buf), fp) != NULL) {
1240                 cnt += strlen(buf);
1241                 if (!strstr(buf, "\r\n") && strstr(buf, "\n"))
1242                         cnt++;
1243         }
1244         
1245         fclose(fp);
1246         return cnt;
1247 }
1248
1249 static void imap_remove_cached_msg(Folder *folder, FolderItem *item, MsgInfo *msginfo)
1250 {
1251         gchar *path, *filename;
1252
1253         path = folder_item_get_path(item);
1254
1255         if (!is_dir_exist(path)) {
1256                 g_free(path);
1257                 return;
1258         }
1259
1260         filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(msginfo->msgnum), NULL);
1261         g_free(path);
1262
1263         if (is_file_exist(filename)) {
1264                 claws_unlink(filename);
1265         }
1266         g_free(filename);
1267 }
1268
1269 typedef struct _TagsData {
1270         gchar *str;
1271         GSList *msglist;
1272         IMAPFolderItem *item;
1273 } TagsData;
1274
1275 static void imap_commit_tags(FolderItem *item, MsgInfo *msginfo, GSList *tags_set, GSList *tags_unset)
1276 {
1277         IMAPSession *session;
1278         gint ok, can_create_tags;
1279         Folder *folder = NULL;
1280         TagsData *ht_data = NULL;
1281         GSList *cur;
1282
1283         g_return_if_fail(item != NULL);
1284         g_return_if_fail(msginfo != NULL);
1285
1286         folder = item->folder;
1287         debug_print("getting session...\n");
1288         session = imap_session_get(folder);
1289         
1290         if (!session) {
1291                 debug_print("can't get session\n");
1292                 return;
1293         }
1294
1295         ok = imap_select(session, IMAP_FOLDER(folder), item,
1296                          NULL, NULL, NULL, NULL, &can_create_tags, FALSE);
1297
1298         if (ok != MAILIMAP_NO_ERROR) {
1299                 return;
1300         }
1301
1302         
1303         if (IMAP_FOLDER_ITEM(item)->can_create_flags != ITEM_CAN_CREATE_FLAGS)
1304                 return;
1305         
1306         if (IMAP_FOLDER_ITEM(item)->batching) {
1307                 /* instead of performing an UID STORE command for each message change,
1308                  * as a lot of them can change "together", we just fill in hashtables
1309                  * and defer the treatment so that we're able to send only one
1310                  * command.
1311                  */
1312                 debug_print("IMAP batch mode on, deferring tags change\n");
1313                 for (cur = tags_set; cur; cur = cur->next) {
1314                         gint cur_tag = GPOINTER_TO_INT(cur->data);
1315                         if (cur_tag) {
1316                                 ht_data = g_hash_table_lookup(IMAP_FOLDER_ITEM(item)->tags_set_table, 
1317                                         GINT_TO_POINTER(cur_tag));
1318                                 if (ht_data == NULL) {
1319                                         ht_data = g_new0(TagsData, 1);
1320                                         ht_data->str = g_strdup(tags_get_tag(cur_tag));
1321                                         ht_data->item = IMAP_FOLDER_ITEM(item);
1322                                         g_hash_table_insert(IMAP_FOLDER_ITEM(item)->tags_set_table, 
1323                                                 GINT_TO_POINTER(cur_tag), ht_data);
1324                                 }
1325                                 ht_data->msglist = g_slist_prepend(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum));
1326                         } 
1327                 }
1328                 for (cur = tags_unset; cur; cur = cur->next) {
1329                         gint cur_tag = GPOINTER_TO_INT(cur->data);
1330                         if (cur_tag) {
1331                                 ht_data = g_hash_table_lookup(IMAP_FOLDER_ITEM(item)->tags_unset_table, 
1332                                         GINT_TO_POINTER(cur_tag));
1333                                 if (ht_data == NULL) {
1334                                         ht_data = g_new0(TagsData, 1);
1335                                         ht_data->str = g_strdup(tags_get_tag(cur_tag));
1336                                         ht_data->item = IMAP_FOLDER_ITEM(item);
1337                                         g_hash_table_insert(IMAP_FOLDER_ITEM(item)->tags_unset_table, 
1338                                                 GINT_TO_POINTER(cur_tag), ht_data);
1339                                 }
1340                                 ht_data->msglist = g_slist_prepend(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum));
1341                         }
1342                 }
1343         } else {
1344                 GSList *list_set = NULL;
1345                 GSList *list_unset = NULL;
1346                 GSList numlist;
1347                 
1348                 numlist.data = GINT_TO_POINTER(msginfo->msgnum);
1349                 numlist.next = NULL;
1350         
1351                 debug_print("IMAP changing tags NOW\n");
1352                 for (cur = tags_set; cur; cur = cur->next) {
1353                         gint cur_tag = GPOINTER_TO_INT(cur->data);
1354                         const gchar *str = tags_get_tag(cur_tag);
1355                         if (strcmp(str, "$Forwarded") && strcmp(str, "Junk") && strcmp(str, "NonJunk") && strcmp(str, "NotJunk") && strcmp(str, "NoJunk") && strcmp(str, "Junk") )
1356                                 list_set = g_slist_prepend(list_set, g_strdup(str));
1357                 }
1358                 if (list_set) {
1359                         ok = imap_set_message_flags(session, 
1360                                 IMAP_FOLDER_ITEM(item), &numlist, 0, list_set, TRUE);
1361                         slist_free_strings(list_set);
1362                         g_slist_free(list_set);
1363                         if (ok != MAILIMAP_NO_ERROR) {
1364                                 return;
1365                         }
1366                 }
1367
1368                 for (cur = tags_unset; cur; cur = cur->next) {
1369                         gint cur_tag = GPOINTER_TO_INT(cur->data);
1370                         const gchar *str = tags_get_tag(cur_tag);
1371                         if (strcmp(str, "$Forwarded") && strcmp(str, "Junk") && strcmp(str, "NonJunk") && strcmp(str, "NotJunk") && strcmp(str, "NoJunk") && strcmp(str, "Junk") )
1372                                 list_unset = g_slist_prepend(list_unset, g_strdup(str));
1373                 }
1374                 if (list_unset) {
1375                         ok = imap_set_message_flags(session, 
1376                                 IMAP_FOLDER_ITEM(item), &numlist, 0, list_unset, FALSE);
1377                         slist_free_strings(list_unset);
1378                         g_slist_free(list_unset);
1379                         if (ok != MAILIMAP_NO_ERROR) {
1380                                 return;
1381                         }
1382                 }
1383         }
1384 }
1385
1386 static gchar *imap_fetch_msg_full(Folder *folder, FolderItem *item, gint uid,
1387                                   gboolean headers, gboolean body)
1388 {
1389         gchar *path, *filename;
1390         IMAPSession *session;
1391         gint ok;
1392
1393         g_return_val_if_fail(folder != NULL, NULL);
1394         g_return_val_if_fail(item != NULL, NULL);
1395
1396         if (uid == 0)
1397                 return NULL;
1398
1399         path = folder_item_get_path(item);
1400         if (!is_dir_exist(path))
1401                 make_dir_hier(path);
1402         filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(uid), NULL);
1403         g_free(path);
1404         debug_print("trying to fetch cached %s\n", filename);
1405         if (is_file_exist(filename)) {
1406                 /* see whether the local file represents the whole message
1407                  * or not. As the IMAP server reports size with \r chars,
1408                  * we have to update the local file (UNIX \n only) size */
1409                 MsgInfo *cached = msgcache_get_msg(item->cache,uid);
1410                 guint have_size = -1;
1411
1412                 if (cached)
1413                         debug_print("message %d has been already %scached.\n", uid,
1414                                 MSG_IS_FULLY_CACHED(cached->flags) ? "fully ":"");
1415                 
1416                 if (!cached || !MSG_IS_FULLY_CACHED(cached->flags)) {
1417                         have_size = get_file_size_with_crs(filename);
1418                         if (cached && (cached->size <= have_size || !body)) {
1419                                 procmsg_msginfo_free(cached);
1420                                 ok = file_strip_crs(filename);
1421                                 if (ok == 0 && cached && cached->size <= have_size) {
1422                                         /* we have it all and stripped */
1423                                         debug_print("...fully cached in fact; setting flag.\n");
1424                                         procmsg_msginfo_set_flags(cached, MSG_FULLY_CACHED, 0);
1425                                 }
1426                                 return filename;
1427                         } else if (!cached && time(NULL) - get_file_mtime(filename) < 60) {
1428                                 debug_print("message not cached and file recent, considering file complete\n");
1429                                 ok = file_strip_crs(filename);
1430                                 if (ok == 0)
1431                                         return filename;
1432                         } else {
1433                                 procmsg_msginfo_free(cached);
1434                         }
1435                 }
1436                 if (cached && MSG_IS_FULLY_CACHED(cached->flags)) {
1437                         procmsg_msginfo_free(cached);
1438                         return filename;
1439                 }
1440         } else {
1441                 MsgInfo *cached = msgcache_get_msg(item->cache,uid);
1442                 if (cached) {
1443                         procmsg_msginfo_unset_flags(cached, MSG_FULLY_CACHED, 0);
1444                         procmsg_msginfo_free(cached);
1445                 }
1446         }
1447
1448         debug_print("getting session...\n");
1449         session = imap_session_get(folder);
1450         
1451         if (!session) {
1452                 g_free(filename);
1453                 return NULL;
1454         }
1455         session_set_access_time(SESSION(session));
1456         lock_session(session); /* unlocked later in the function */
1457
1458         debug_print("IMAP fetching messages\n");
1459         ok = imap_select(session, IMAP_FOLDER(folder), item,
1460                          NULL, NULL, NULL, NULL, NULL, FALSE);
1461         if (ok != MAILIMAP_NO_ERROR) {
1462                 g_warning("can't select mailbox %s\n", item->path);
1463                 g_free(filename);
1464                 return NULL;
1465         }
1466
1467         session_set_access_time(SESSION(session));
1468
1469         debug_print("getting message %d...\n", uid);
1470         ok = imap_cmd_fetch(session, (guint32)uid, filename, headers, body);
1471
1472         if (ok != MAILIMAP_NO_ERROR) {
1473                 g_warning("can't fetch message %d\n", uid);
1474                 g_free(filename);
1475                 return NULL;
1476         }
1477
1478         session_set_access_time(SESSION(session));
1479         unlock_session(session);
1480
1481         ok = file_strip_crs(filename);
1482
1483         if (ok == 0 && headers && body) {
1484                 MsgInfo *cached = msgcache_get_msg(item->cache,uid);
1485                 if (cached) {
1486                         procmsg_msginfo_set_flags(cached, MSG_FULLY_CACHED, 0);
1487                         procmsg_msginfo_free(cached);
1488                 }
1489         } else if (ok == -1) {
1490                 MsgInfo *cached = msgcache_get_msg(item->cache,uid);
1491                 if (cached) {
1492                         procmsg_msginfo_unset_flags(cached, MSG_FULLY_CACHED, 0);
1493                         procmsg_msginfo_free(cached);
1494                 }
1495         }
1496         return filename;
1497 }
1498
1499 static gboolean imap_is_msg_fully_cached(Folder *folder, FolderItem *item, gint uid)
1500 {
1501         gchar *path, *filename;
1502         guint size = 0;
1503         MsgInfo *cached = msgcache_get_msg(item->cache,uid);
1504         
1505         if (!cached)
1506                 return FALSE;
1507
1508         if (MSG_IS_FULLY_CACHED(cached->flags)) {
1509                 procmsg_msginfo_free(cached);
1510                 return TRUE;
1511         }
1512         path = folder_item_get_path(item);
1513         if (!is_dir_exist(path))
1514                 return FALSE;
1515
1516         filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(uid), NULL);
1517         g_free(path);
1518         if (is_file_exist(filename)) {
1519                 if (cached && cached->total_size == cached->size) {
1520                         /* fast path */
1521                         g_free(filename);
1522                         procmsg_msginfo_set_flags(cached, MSG_FULLY_CACHED, 0);
1523                         return TRUE;
1524                 }
1525                 size = get_file_size_with_crs(filename);
1526                 g_free(filename);
1527         }
1528         if (cached && size >= cached->size) {
1529                 cached->total_size = cached->size;
1530                 procmsg_msginfo_set_flags(cached, MSG_FULLY_CACHED, 0);
1531                 procmsg_msginfo_free(cached);
1532                 return TRUE;
1533         }
1534         if (cached)
1535                 procmsg_msginfo_free(cached);
1536         return FALSE;   
1537 }
1538
1539 void imap_cache_msg(FolderItem *item, gint msgnum)
1540 {
1541         Folder *folder = NULL;
1542         
1543         if (!item)
1544                 return;
1545         folder = item->folder;
1546         
1547         if (!imap_is_msg_fully_cached(folder, item, msgnum)) {
1548                 gchar *tmp = imap_fetch_msg_full(folder, item, msgnum, TRUE, TRUE);
1549                 debug_print("fetched %s\n", tmp);
1550                 g_free(tmp);
1551         }
1552 }
1553
1554 static gint imap_add_msg(Folder *folder, FolderItem *dest, 
1555                          const gchar *file, MsgFlags *flags)
1556 {
1557         gint ret;
1558         GSList file_list;
1559         MsgFileInfo fileinfo;
1560
1561         g_return_val_if_fail(file != NULL, -1);
1562
1563         fileinfo.msginfo = NULL;
1564         fileinfo.file = (gchar *)file;
1565         fileinfo.flags = flags;
1566         file_list.data = &fileinfo;
1567         file_list.next = NULL;
1568
1569         ret = imap_add_msgs(folder, dest, &file_list, NULL);
1570         return ret;
1571 }
1572
1573 static gint imap_add_msgs(Folder *folder, FolderItem *dest, GSList *file_list,
1574                    GRelation *relation)
1575 {
1576         gchar *destdir;
1577         IMAPSession *session;
1578         guint32 last_uid = 0;
1579         GSList *cur;
1580         MsgFileInfo *fileinfo;
1581         gint ok = MAILIMAP_NO_ERROR;
1582         gint curnum = 0, total = 0;
1583         gboolean missing_uids = FALSE;
1584
1585         g_return_val_if_fail(folder != NULL, -1);
1586         g_return_val_if_fail(dest != NULL, -1);
1587         g_return_val_if_fail(file_list != NULL, -1);
1588         
1589         debug_print("getting session...\n");
1590         session = imap_session_get(folder);
1591         if (!session) {
1592                 return -1;
1593         }
1594         destdir = imap_get_real_path(session, IMAP_FOLDER(folder), dest->path, &ok);
1595         if (is_fatal(ok))
1596                 return -1;
1597         statusbar_print_all(_("Adding messages..."));
1598         total = g_slist_length(file_list);
1599         for (cur = file_list; cur != NULL; cur = cur->next) {
1600                 IMAPFlags iflags = 0;
1601                 guint32 new_uid = 0;
1602                 gchar *real_file = NULL;
1603                 fileinfo = (MsgFileInfo *)cur->data;
1604
1605                 statusbar_progress_all(curnum, total, total < 10 ? 1:10);
1606                 curnum++;
1607
1608                 if (fileinfo->flags) {
1609                         if (MSG_IS_MARKED(*fileinfo->flags))
1610                                 iflags |= IMAP_FLAG_FLAGGED;
1611                         if (MSG_IS_REPLIED(*fileinfo->flags))
1612                                 iflags |= IMAP_FLAG_ANSWERED;
1613                         if (MSG_IS_FORWARDED(*fileinfo->flags))
1614                                 iflags |= IMAP_FLAG_FORWARDED;
1615                         if (MSG_IS_SPAM(*fileinfo->flags))
1616                                 iflags |= IMAP_FLAG_SPAM;
1617                         else
1618                                 iflags |= IMAP_FLAG_HAM;
1619                         if (!MSG_IS_UNREAD(*fileinfo->flags))
1620                                 iflags |= IMAP_FLAG_SEEN;
1621                         
1622                 }
1623                 
1624                 if (real_file == NULL)
1625                         real_file = g_strdup(fileinfo->file);
1626                 
1627                 if (folder_has_parent_of_type(dest, F_QUEUE) ||
1628                     folder_has_parent_of_type(dest, F_OUTBOX) ||
1629                     folder_has_parent_of_type(dest, F_DRAFT) ||
1630                     folder_has_parent_of_type(dest, F_TRASH))
1631                         iflags |= IMAP_FLAG_SEEN;
1632
1633                 ok = imap_cmd_append(session, IMAP_FOLDER_ITEM(dest), destdir, real_file, iflags, 
1634                                      &new_uid);
1635
1636                 if (ok != MAILIMAP_NO_ERROR) {
1637                         g_warning("can't append message %s\n", real_file);
1638                         g_free(real_file);
1639                         g_free(destdir);
1640                         statusbar_progress_all(0,0,0);
1641                         statusbar_pop_all();
1642                         return -1;
1643                 } else {
1644                         debug_print("appended new message as %d\n", new_uid);
1645                         /* put the local file in the imapcache, so that we don't
1646                          * have to fetch it back later. */
1647                         
1648                         if (new_uid == 0) {
1649                                 missing_uids = TRUE;
1650                                 debug_print("Missing UID (0)\n");
1651                         }
1652                         if (new_uid > 0) {
1653                                 gchar *cache_path = folder_item_get_path(dest);
1654                                 if (!is_dir_exist(cache_path))
1655                                         make_dir_hier(cache_path);
1656                                 if (is_dir_exist(cache_path)) {
1657                                         gchar *cache_file = g_strconcat(
1658                                                 cache_path, G_DIR_SEPARATOR_S, 
1659                                                 itos(new_uid), NULL);
1660                                         copy_file(real_file, cache_file, TRUE);
1661                                         debug_print("got UID %d, copied to cache: %s\n", new_uid, cache_file);
1662                                         g_free(cache_file);
1663                                 }
1664                                 g_free(cache_path);
1665                         }
1666                 }
1667
1668                 if (relation != NULL)
1669                         g_relation_insert(relation, fileinfo->msginfo != NULL ? 
1670                                           (gpointer) fileinfo->msginfo : (gpointer) fileinfo,
1671                                           GINT_TO_POINTER(new_uid));
1672                 if (last_uid < new_uid) {
1673                         last_uid = new_uid;
1674                 }
1675
1676                 g_free(real_file);
1677         }
1678         
1679         statusbar_progress_all(0,0,0);
1680         statusbar_pop_all();
1681         
1682         
1683         g_free(destdir);
1684
1685         imap_scan_required(folder, dest);
1686
1687         session = imap_session_get(folder);
1688         if (!session) {
1689                 return -1;
1690         }
1691         if (missing_uids) {
1692                 gint a;
1693                 ok = imap_select(session, IMAP_FOLDER(folder), dest,
1694                          &a, NULL, NULL, NULL, NULL, FALSE);
1695         }
1696         return last_uid;
1697 }
1698
1699 static GSList *flatten_mailimap_set(struct mailimap_set * set) 
1700 {
1701         GSList *result = NULL;
1702         clistiter *list;
1703         int start, end, t;
1704         GSList *cur;
1705
1706         if (!set || !set->set_list)
1707                 return NULL;
1708
1709         for (list = clist_begin(set->set_list); list; list = clist_next(list)) {
1710                 struct mailimap_set_item *item = (struct mailimap_set_item *)clist_content(list);
1711                 start = item->set_first;
1712                 end = item->set_last;
1713                 for (t = start; t <= end; t++) {
1714                         result = g_slist_prepend(result, GINT_TO_POINTER(t));
1715                 }
1716         }
1717         result = g_slist_reverse(result);
1718         if (debug_get_mode()) {
1719                 debug_print("flat imap set: ");
1720                 for (cur = result; cur; cur = cur->next) {
1721                         debug_print("%d ", GPOINTER_TO_INT(cur->data));
1722                 }
1723                 debug_print("\n");
1724         }
1725         
1726         return result;
1727 }
1728 static gint imap_do_copy_msgs(Folder *folder, FolderItem *dest, 
1729                               MsgInfoList *msglist, GRelation *relation)
1730 {
1731         FolderItem *src;
1732         gchar *destdir;
1733         GSList *seq_list, *cur;
1734         MsgInfo *msginfo;
1735         IMAPSession *session;
1736         gint ok = MAILIMAP_NO_ERROR;
1737         GHashTable *uid_hash;
1738         gint last_num = 0;
1739         gboolean single = FALSE;
1740
1741         g_return_val_if_fail(folder != NULL, -1);
1742         g_return_val_if_fail(dest != NULL, -1);
1743         g_return_val_if_fail(msglist != NULL, -1);
1744         
1745         debug_print("getting session...\n");
1746         session = imap_session_get(folder);
1747         
1748         if (!session) {
1749                 return -1;
1750         }
1751
1752         msginfo = (MsgInfo *)msglist->data;
1753         if (msglist->next == NULL)
1754                 single = TRUE;
1755         src = msginfo->folder;
1756         if (src == dest) {
1757                 g_warning("the src folder is identical to the dest.\n");
1758                 return -1;
1759         }
1760
1761         if (src->folder != dest->folder) {
1762                 GSList *infolist = NULL, *cur;
1763                 int res = -1;
1764                 for (cur = msglist; cur; cur = cur->next) {
1765                         msginfo = (MsgInfo *)cur->data;
1766                         MsgFileInfo *fileinfo = g_new0(MsgFileInfo, 1);
1767                         fileinfo->file = procmsg_get_message_file(msginfo);
1768                         fileinfo->flags = &(msginfo->flags);
1769                         infolist = g_slist_prepend(infolist, fileinfo);
1770                 }
1771                 infolist = g_slist_reverse(infolist);
1772                 res = folder_item_add_msgs(dest, infolist, FALSE);
1773                 for (cur = infolist; cur; cur = cur->next) {
1774                         MsgFileInfo *info = (MsgFileInfo *)cur->data;
1775                         g_free(info->file);
1776                         g_free(info);
1777                 }
1778                 g_slist_free(infolist);
1779                 return res;
1780         } 
1781
1782         lock_session(session); /* unlocked later in the function */
1783
1784         ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder,
1785                          NULL, NULL, NULL, NULL, NULL, FALSE);
1786         if (ok != MAILIMAP_NO_ERROR) {
1787                 return ok;
1788         }
1789
1790         unlock_session(session);
1791
1792         destdir = imap_get_real_path(session, IMAP_FOLDER(folder), dest->path, &ok);
1793
1794         if (is_fatal(ok))
1795                 return ok;
1796
1797         seq_list = imap_get_lep_set_from_msglist(IMAP_FOLDER(folder), msglist);
1798         uid_hash = g_hash_table_new(g_direct_hash, g_direct_equal);
1799         
1800         statusbar_print_all(_("Copying messages..."));
1801         for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
1802                 struct mailimap_set * seq_set;
1803                 struct mailimap_set * source = NULL;
1804                 struct mailimap_set * dest = NULL;
1805                 seq_set = cur->data;
1806
1807                 debug_print("Copying messages from %s to %s ...\n",
1808                             src->path, destdir);
1809
1810                 lock_session(session); /* unlocked later in the function */
1811                 ok = imap_cmd_copy(session, seq_set, destdir,
1812                         &source, &dest);
1813                 
1814                 if (is_fatal(ok)) {
1815                         session = NULL;
1816                 }
1817
1818                 if (ok == MAILIMAP_NO_ERROR) {
1819                         unlock_session(session);
1820                         if (relation && source && dest) {
1821                                 GSList *s_list = flatten_mailimap_set(source);
1822                                 GSList *d_list = flatten_mailimap_set(dest);
1823                                 GSList *s_cur, *d_cur;
1824                                 if (g_slist_length(s_list) == g_slist_length(d_list)) {
1825
1826                                         for (s_cur = s_list, d_cur = d_list; 
1827                                              s_cur && d_cur; 
1828                                              s_cur = s_cur->next, d_cur = d_cur->next) {
1829                                                 g_hash_table_insert(uid_hash, s_cur->data, d_cur->data);
1830                                         }
1831
1832                                 } else {
1833                                         debug_print("hhhmm, source list length != dest list length.\n");
1834                                 }
1835                                 g_slist_free(s_list);
1836                                 g_slist_free(d_list);
1837                         }
1838                 }
1839
1840
1841                 if (source)
1842                         mailimap_set_free(source);
1843                 if (dest)
1844                         mailimap_set_free(dest);
1845
1846                 if (ok != MAILIMAP_NO_ERROR) {
1847                         g_hash_table_destroy(uid_hash);
1848                         imap_lep_set_free(seq_list);
1849                         statusbar_pop_all();
1850                         return -1;
1851                 }
1852         }
1853
1854         for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
1855                 MsgInfo *msginfo = (MsgInfo *)cur->data;
1856                 gpointer hashval;
1857
1858                 hashval = g_hash_table_lookup(uid_hash, GINT_TO_POINTER(msginfo->msgnum));
1859                 
1860                 if (hashval != NULL) {
1861                         gint num = GPOINTER_TO_INT(hashval);
1862                         g_relation_insert(relation, msginfo,
1863                                           GINT_TO_POINTER(num));
1864                         if (num > last_num)
1865                                 last_num = num;
1866                         debug_print("copied message %d as %d\n", msginfo->msgnum, num);
1867                         /* put the local file in the imapcache, so that we don't
1868                          * have to fetch it back later. */
1869                         if (num > 0) {
1870                                 gchar *cache_path = folder_item_get_path(msginfo->folder);
1871                                 gchar *real_file = g_strconcat(
1872                                         cache_path, G_DIR_SEPARATOR_S, 
1873                                         itos(msginfo->msgnum), NULL);
1874                                 gchar *cache_file = NULL;
1875                                 g_free(cache_path);
1876                                 cache_path = folder_item_get_path(dest);
1877                                 cache_file = g_strconcat(
1878                                         cache_path, G_DIR_SEPARATOR_S, 
1879                                         itos(num), NULL);
1880                                 if (!is_dir_exist(cache_path))
1881                                         make_dir_hier(cache_path);
1882                                 if (is_file_exist(real_file) && is_dir_exist(cache_path)) {
1883                                         copy_file(real_file, cache_file, TRUE);
1884                                         debug_print("copied to cache: %s\n", cache_file);
1885                                 }
1886                                 g_free(real_file);
1887                                 g_free(cache_file);
1888                                 g_free(cache_path);
1889                         }
1890                 } else
1891                         g_relation_insert(relation, msginfo,
1892                                           GINT_TO_POINTER(0));
1893         }
1894         statusbar_pop_all();
1895
1896         g_hash_table_destroy(uid_hash);
1897         imap_lep_set_free(seq_list);
1898
1899         g_free(destdir);
1900         
1901         IMAP_FOLDER_ITEM(dest)->lastuid = 0;
1902         IMAP_FOLDER_ITEM(dest)->uid_next = 0;
1903         g_slist_free(IMAP_FOLDER_ITEM(dest)->uid_list);
1904         IMAP_FOLDER_ITEM(dest)->uid_list = NULL;
1905
1906         imap_scan_required(folder, dest);
1907         if (ok == MAILIMAP_NO_ERROR)
1908                 return last_num;
1909         else
1910                 return -1;
1911 }
1912
1913 static gint imap_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1914 {
1915         GSList msglist;
1916
1917         g_return_val_if_fail(msginfo != NULL, -1);
1918
1919         msglist.data = msginfo;
1920         msglist.next = NULL;
1921
1922         return imap_copy_msgs(folder, dest, &msglist, NULL);
1923 }
1924
1925 static gint imap_copy_msgs(Folder *folder, FolderItem *dest, 
1926                     MsgInfoList *msglist, GRelation *relation)
1927 {
1928         MsgInfo *msginfo;
1929         gint ret;
1930
1931         g_return_val_if_fail(folder != NULL, -1);
1932         g_return_val_if_fail(dest != NULL, -1);
1933         g_return_val_if_fail(msglist != NULL, -1);
1934
1935         msginfo = (MsgInfo *)msglist->data;
1936         g_return_val_if_fail(msginfo->folder != NULL, -1);
1937
1938         ret = imap_do_copy_msgs(folder, dest, msglist, relation);
1939         return ret;
1940 }
1941
1942
1943 static gint imap_do_remove_msgs(Folder *folder, FolderItem *dest, 
1944                                 MsgInfoList *msglist, GRelation *relation)
1945 {
1946         gchar *destdir, *dir;
1947         GSList *numlist = NULL, *cur;
1948         MsgInfo *msginfo;
1949         IMAPSession *session;
1950         gint ok = MAILIMAP_NO_ERROR;
1951         
1952         g_return_val_if_fail(folder != NULL, -1);
1953         g_return_val_if_fail(dest != NULL, -1);
1954         g_return_val_if_fail(msglist != NULL, -1);
1955
1956         debug_print("getting session...\n");
1957         session = imap_session_get(folder);
1958         if (!session) {
1959                 return -1;
1960         }
1961
1962         lock_session(session); /* unlocked later in the function */
1963
1964         msginfo = (MsgInfo *)msglist->data;
1965
1966         ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder,
1967                          NULL, NULL, NULL, NULL, NULL, FALSE);
1968         if (ok != MAILIMAP_NO_ERROR) {
1969                 return ok;
1970         }
1971
1972         destdir = imap_get_real_path(session, IMAP_FOLDER(folder), dest->path, &ok);
1973         if (is_fatal(ok)) {
1974                 g_free(destdir);
1975                 return ok;
1976         }
1977         for (cur = msglist; cur; cur = cur->next) {
1978                 msginfo = (MsgInfo *)cur->data;
1979                 if (!MSG_IS_DELETED(msginfo->flags))
1980                         numlist = g_slist_prepend(numlist, GINT_TO_POINTER(msginfo->msgnum));
1981         }
1982         numlist = g_slist_reverse(numlist);
1983
1984         if (numlist != NULL) {
1985                 ok = imap_set_message_flags
1986                         (session, IMAP_FOLDER_ITEM(msginfo->folder), numlist, IMAP_FLAG_DELETED, NULL, TRUE);
1987                 if (ok != MAILIMAP_NO_ERROR) {
1988                         log_warning(LOG_PROTOCOL, _("can't set deleted flags\n"));
1989                         g_free(destdir);
1990                         return ok;
1991                 }
1992         } /* else we just need to expunge */
1993         ok = imap_cmd_expunge(session, folder->account->imap_use_trash);
1994         if (ok != MAILIMAP_NO_ERROR) {
1995                 log_warning(LOG_PROTOCOL, _("can't expunge\n"));
1996                 g_free(destdir);
1997                 return ok;
1998         }
1999         
2000         session->folder_content_changed = TRUE;
2001         unlock_session(session);
2002
2003         dir = folder_item_get_path(msginfo->folder);
2004         if (is_dir_exist(dir)) {
2005                 for (cur = msglist; cur; cur = cur->next) {
2006                         msginfo = (MsgInfo *)cur->data;
2007                         remove_numbered_files(dir, msginfo->msgnum, msginfo->msgnum);
2008                 }
2009         }
2010         g_free(dir);
2011
2012         g_slist_free(numlist);
2013
2014         imap_scan_required(folder, dest);
2015
2016         g_free(destdir);
2017         if (ok == MAILIMAP_NO_ERROR)
2018                 return 0;
2019         else
2020                 return -1;
2021 }
2022
2023 static gint imap_remove_msgs(Folder *folder, FolderItem *dest, 
2024                     MsgInfoList *msglist, GRelation *relation)
2025 {
2026         MsgInfo *msginfo;
2027
2028         g_return_val_if_fail(folder != NULL, -1);
2029         g_return_val_if_fail(dest != NULL, -1);
2030         if (msglist == NULL)
2031                 return 0;
2032
2033         msginfo = (MsgInfo *)msglist->data;
2034         g_return_val_if_fail(msginfo->folder != NULL, -1);
2035
2036         return imap_do_remove_msgs(folder, dest, msglist, relation);
2037 }
2038
2039 static gint imap_remove_all_msg(Folder *folder, FolderItem *item)
2040 {
2041         GSList *list = folder_item_get_msg_list(item);
2042         gint res = imap_remove_msgs(folder, item, list, NULL);
2043         procmsg_msg_list_free(list);
2044         return res;
2045 }
2046
2047 static gboolean imap_is_msg_changed(Folder *folder, FolderItem *item,
2048                                     MsgInfo *msginfo)
2049 {
2050         /* TODO: properly implement this method */
2051         return FALSE;
2052 }
2053
2054 static gint imap_close(Folder *folder, FolderItem *item)
2055 {
2056         return 0;
2057 }
2058
2059 static gint imap_scan_tree_real(Folder *folder, gboolean subs_only)
2060 {
2061         FolderItem *item = NULL;
2062         IMAPSession *session;
2063         gchar *root_folder = NULL;
2064
2065         g_return_val_if_fail(folder != NULL, -1);
2066         g_return_val_if_fail(folder->account != NULL, -1);
2067
2068         debug_print("getting session...\n");
2069         session = imap_session_get(folder);
2070         if (!session) {
2071                 if (!folder->node) {
2072                         folder_tree_destroy(folder);
2073                         item = folder_item_new(folder, folder->name, NULL);
2074                         item->folder = folder;
2075                         folder->node = item->node = g_node_new(item);
2076                 }
2077                 return -1;
2078         }
2079
2080         if (folder->account->imap_dir && *folder->account->imap_dir) {
2081                 gchar *real_path;
2082                 int r = MAILIMAP_NO_ERROR;
2083                 clist * lep_list;
2084
2085                 Xstrdup_a(root_folder, folder->account->imap_dir, {return -1;});
2086                 extract_quote(root_folder, '"');
2087                 subst_char(root_folder,
2088                            imap_get_path_separator(session, IMAP_FOLDER(folder),
2089                                                    root_folder, &r),
2090                            '/');
2091                 if (is_fatal(r))
2092                         return -1;
2093                 strtailchomp(root_folder, '/');
2094                 real_path = imap_get_real_path
2095                         (session, IMAP_FOLDER(folder), root_folder, &r);
2096                 if (is_fatal(r))
2097                         return -1;
2098                 debug_print("IMAP root directory: %s\n", real_path);
2099
2100                 /* check if root directory exist */
2101
2102                 r = imap_threaded_list(session->folder, "", real_path,
2103                                        &lep_list);
2104
2105                 if (r != MAILIMAP_NO_ERROR)
2106                         imap_handle_error(SESSION(session), r);
2107
2108                 if ((r != MAILIMAP_NO_ERROR) || (clist_count(lep_list) == 0)) {
2109                         if (!folder->node) {
2110                                 item = folder_item_new(folder, folder->name, NULL);
2111                                 item->folder = folder;
2112                                 folder->node = item->node = g_node_new(item);
2113                         }
2114                         return -1;
2115                 }
2116                 mailimap_list_result_free(lep_list);
2117                                 
2118                 g_free(real_path);
2119         }
2120
2121         if (folder->node)
2122                 item = FOLDER_ITEM(folder->node->data);
2123                 
2124         if (item && !item->path && root_folder) {
2125                 item->path = g_strdup(root_folder);
2126         }
2127
2128         if (!item || ((item->path || root_folder) &&
2129                       strcmp2(item->path, root_folder) != 0)) {
2130                 folder_tree_destroy(folder);
2131                 item = folder_item_new(folder, folder->name, root_folder);
2132                 item->folder = folder;
2133                 folder->node = item->node = g_node_new(item);
2134         }
2135
2136         imap_scan_tree_recursive(session, FOLDER_ITEM(folder->node->data), subs_only);
2137         imap_create_missing_folders(folder);
2138
2139         return 0;
2140 }
2141
2142 static gint imap_scan_tree(Folder *folder)
2143 {
2144         gboolean subs_only = FALSE;
2145         if (folder->account) {
2146                 debug_print(" scanning only subs %d\n", folder->account->imap_subsonly);
2147                 subs_only = folder->account->imap_subsonly;
2148         }
2149         return imap_scan_tree_real(folder, subs_only);
2150 }
2151
2152 static gint imap_scan_tree_recursive(IMAPSession *session, FolderItem *item, gboolean subs_only)
2153 {
2154         Folder *folder;
2155         IMAPFolder *imapfolder;
2156         FolderItem *new_item;
2157         GSList *item_list, *cur;
2158         GNode *node;
2159         gchar *real_path;
2160         gchar *wildcard_path;
2161         gchar separator;
2162         gchar wildcard[3];
2163         clist * lep_list;
2164         int r = MAILIMAP_NO_ERROR;
2165         
2166         g_return_val_if_fail(item != NULL, -1);
2167         g_return_val_if_fail(item->folder != NULL, -1);
2168         g_return_val_if_fail(item->no_sub == FALSE, -1);
2169
2170         folder = item->folder;
2171         imapfolder = IMAP_FOLDER(folder);
2172
2173         separator = imap_get_path_separator(session, imapfolder, item->path, &r);
2174         if (is_fatal(r))
2175                 return r;
2176
2177         if (folder->ui_func)
2178                 folder->ui_func(folder, item, folder->ui_func_data);
2179
2180         if (item->path) {
2181                 wildcard[0] = separator;
2182                 wildcard[1] = '%';
2183                 wildcard[2] = '\0';
2184                 real_path = imap_get_real_path(session, imapfolder, item->path, &r);
2185                 if (is_fatal(r)) {
2186                         g_free(real_path);
2187                         return r;
2188                 }
2189         } else {
2190                 wildcard[0] = '%';
2191                 wildcard[1] = '\0';
2192                 real_path = g_strdup("");
2193         }
2194
2195         Xstrcat_a(wildcard_path, real_path, wildcard,
2196                   {g_free(real_path); return MAILIMAP_ERROR_BAD_STATE;});
2197         lep_list = NULL;
2198         
2199         if (subs_only)
2200                 r = imap_threaded_lsub(folder, "", wildcard_path, &lep_list);
2201         else
2202                 r = imap_threaded_list(folder, "", wildcard_path, &lep_list);
2203
2204         if (r != MAILIMAP_NO_ERROR) {
2205                 imap_handle_error(SESSION(session), r);
2206                 item_list = NULL;
2207                 g_free(real_path);
2208                 return r;
2209         }
2210         else {
2211                 item_list = imap_list_from_lep(imapfolder,
2212                                                lep_list, real_path, FALSE);
2213                 mailimap_list_result_free(lep_list);
2214         }
2215         
2216         g_free(real_path);
2217
2218         node = item->node->children;
2219         while (node != NULL) {
2220                 FolderItem *old_item = FOLDER_ITEM(node->data);
2221                 GNode *next = node->next;
2222
2223                 new_item = NULL;
2224                 for (cur = item_list; cur != NULL; cur = cur->next) {
2225                         FolderItem *cur_item = FOLDER_ITEM(cur->data);
2226                         if (!strcmp2(old_item->path, cur_item->path)) {
2227                                 new_item = cur_item;
2228                                 break;
2229                         }
2230                 }
2231                 if (!new_item) {
2232                         if (old_item && old_item->path && !strcmp(old_item->path, "INBOX")) {
2233                                 debug_print("not removing INBOX\n");
2234                         } else {
2235                                 debug_print("folder '%s' not found. removing...\n",
2236                                             old_item->path);
2237                                 folder_item_remove(old_item);
2238                         }
2239                 } else {
2240                         old_item->no_sub = new_item->no_sub;
2241                         old_item->no_select = new_item->no_select;
2242                         if (old_item->no_sub == TRUE && node->children) {
2243                                 debug_print("folder '%s' doesn't have "
2244                                             "subfolders. removing...\n",
2245                                             old_item->path);
2246                                 folder_item_remove_children(old_item);
2247                         }
2248                 }
2249
2250                 node = next;
2251         }
2252
2253         for (cur = item_list; cur != NULL; cur = cur->next) {
2254                 FolderItem *cur_item = FOLDER_ITEM(cur->data);
2255                 new_item = NULL;
2256
2257                 for (node = item->node->children; node != NULL;
2258                      node = node->next) {
2259                         if (!strcmp2(FOLDER_ITEM(node->data)->path,
2260                                      cur_item->path)) {
2261                                 new_item = FOLDER_ITEM(node->data);
2262                                 folder_item_destroy(cur_item);
2263                                 cur_item = NULL;
2264                                 break;
2265                         }
2266                 }
2267                 if (!new_item) {
2268                         new_item = cur_item;
2269                         debug_print("new folder '%s' found.\n", new_item->path);
2270                         folder_item_append(item, new_item);
2271                 }
2272
2273                 if (!strcmp(new_item->path, "INBOX")) {
2274                         new_item->stype = F_INBOX;
2275                         folder->inbox = new_item;
2276                 } else if (!folder_item_parent(item) || item->stype == F_INBOX) {
2277                         gchar *base;
2278
2279                         base = g_path_get_basename(new_item->path);
2280
2281                         if (!folder->outbox && !g_ascii_strcasecmp(base, "Sent")) {
2282                                 new_item->stype = F_OUTBOX;
2283                                 folder->outbox = new_item;
2284                         } else if (!folder->draft && !g_ascii_strcasecmp(base, "Drafts")) {
2285                                 new_item->stype = F_DRAFT;
2286                                 folder->draft = new_item;
2287                         } else if (!folder->queue && !g_ascii_strcasecmp(base, "Queue")) {
2288                                 new_item->stype = F_QUEUE;
2289                                 folder->queue = new_item;
2290                         } else if (!folder->trash && !g_ascii_strcasecmp(base, "Trash")) {
2291                                 new_item->stype = F_TRASH;
2292                                 folder->trash = new_item;
2293                         }
2294                         g_free(base);
2295                 }
2296
2297                 if (new_item->no_sub == FALSE)
2298                         imap_scan_tree_recursive(session, new_item, subs_only);
2299         }
2300
2301         g_slist_free(item_list);
2302
2303         return MAILIMAP_NO_ERROR;
2304 }
2305
2306 GList *imap_scan_subtree(Folder *folder, FolderItem *item, gboolean unsubs_only, gboolean recursive)
2307 {
2308         IMAPSession *session = imap_session_get(folder);
2309         gchar *real_path;
2310         gchar *wildcard_path;
2311         gchar separator;
2312         gchar wildcard[3];
2313         clist * lep_list;
2314         GSList *item_list = NULL, *cur;
2315         GList *child_list = NULL, *tmplist = NULL;
2316         GSList *sub_list = NULL;
2317         int r = MAILIMAP_NO_ERROR;
2318
2319         if (!session)
2320                 return NULL;
2321
2322         separator = imap_get_path_separator(session, IMAP_FOLDER(folder), item->path, &r);
2323         if (is_fatal(r))
2324                 return NULL;
2325
2326         if (item->path) {
2327                 wildcard[0] = separator;
2328                 wildcard[1] = '%';
2329                 wildcard[2] = '\0';
2330                 real_path = imap_get_real_path(session, IMAP_FOLDER(folder), item->path, &r);
2331                 if (is_fatal(r)) {
2332                         g_free(real_path);
2333                         return NULL;
2334                 }
2335         } else {
2336                 wildcard[0] = '%';
2337                 wildcard[1] = '\0';
2338                 real_path = g_strdup("");
2339         }
2340
2341         Xstrcat_a(wildcard_path, real_path, wildcard,
2342                   {g_free(real_path); return NULL;});
2343         lep_list = NULL;
2344         
2345         if (unsubs_only)
2346                 statusbar_print_all(_("Looking for unsubscribed folders in %s..."), 
2347                                 item->path?item->path:item->name);
2348         else
2349                 statusbar_print_all(_("Looking for subfolders of %s..."), 
2350                                 item->path?item->path:item->name);
2351
2352         r = imap_threaded_list(folder, "", wildcard_path, &lep_list);
2353         if (r) {
2354                 g_free(real_path);
2355                 statusbar_pop_all();
2356                 return NULL;
2357         }
2358         item_list = imap_list_from_lep(IMAP_FOLDER(folder),
2359                                lep_list, real_path, FALSE);
2360         mailimap_list_result_free(lep_list);
2361
2362         for (cur = item_list; cur != NULL; cur = cur->next) {
2363                 FolderItem *cur_item = FOLDER_ITEM(cur->data);
2364                 if (recursive) {
2365                         tmplist = imap_scan_subtree(folder, cur_item, 
2366                                         unsubs_only, recursive);
2367                         if (tmplist)
2368                                 child_list = g_list_concat(child_list, tmplist);
2369                 }
2370                 child_list = g_list_prepend(child_list,
2371                                 imap_get_real_path(session, 
2372                                         IMAP_FOLDER(folder), cur_item->path, &r));
2373                 if (is_fatal(r)) {
2374                         g_free(real_path);
2375                         statusbar_pop_all();
2376                         return NULL;
2377                 }
2378                 folder_item_destroy(cur_item);
2379         }
2380         child_list = g_list_reverse(child_list);
2381         g_slist_free(item_list);
2382
2383         if (unsubs_only) {
2384                 r = imap_threaded_lsub(folder, "", wildcard_path, &lep_list);
2385                 if (r) {
2386                         g_free(real_path);
2387                         statusbar_pop_all();
2388                         return NULL;
2389                 }
2390                 sub_list = imap_list_from_lep(IMAP_FOLDER(folder),
2391                                        lep_list, real_path, FALSE);
2392                 mailimap_list_result_free(lep_list);
2393
2394                 for (cur = sub_list; cur != NULL; cur = cur->next) {
2395                         FolderItem *cur_item = FOLDER_ITEM(cur->data);
2396                         GList *oldlitem = NULL;
2397                         gchar *tmp = imap_get_real_path(session, 
2398                                         IMAP_FOLDER(folder), cur_item->path, &r);
2399                         if (r) {
2400                                 g_free(real_path);
2401                                 statusbar_pop_all();
2402                                 return NULL;
2403                         }
2404                         folder_item_destroy(cur_item);
2405                         oldlitem = g_list_find_custom(
2406                                         child_list, tmp, (GCompareFunc)strcmp2);
2407                         if (oldlitem) {
2408                                 child_list = g_list_remove_link(child_list, oldlitem);
2409                                 g_free(oldlitem->data);
2410                                 g_list_free(oldlitem);
2411                         }
2412                         g_free(tmp);
2413                 }
2414         }
2415
2416         g_free(real_path);
2417         statusbar_pop_all();
2418
2419         return child_list;
2420 }
2421
2422 static gint imap_create_tree(Folder *folder)
2423 {
2424         g_return_val_if_fail(folder != NULL, -1);
2425         g_return_val_if_fail(folder->node != NULL, -1);
2426         g_return_val_if_fail(folder->node->data != NULL, -1);
2427         g_return_val_if_fail(folder->account != NULL, -1);
2428
2429         imap_scan_tree(folder);
2430         imap_create_missing_folders(folder);
2431
2432         return 0;
2433 }
2434
2435 static void imap_create_missing_folders(Folder *folder)
2436 {
2437         g_return_if_fail(folder != NULL);
2438
2439         if (!folder->inbox)
2440                 folder->inbox = imap_create_special_folder
2441                         (folder, F_INBOX, "INBOX");
2442         if (!folder->trash)
2443                 folder->trash = imap_create_special_folder
2444                         (folder, F_TRASH, "Trash");
2445         if (!folder->queue)
2446                 folder->queue = imap_create_special_folder
2447                         (folder, F_QUEUE, "Queue");
2448         if (!folder->outbox)
2449                 folder->outbox = imap_create_special_folder
2450                         (folder, F_OUTBOX, "Sent");
2451         if (!folder->draft)
2452                 folder->draft = imap_create_special_folder
2453                         (folder, F_DRAFT, "Drafts");
2454 }
2455
2456 static FolderItem *imap_create_special_folder(Folder *folder,
2457                                               SpecialFolderItemType stype,
2458                                               const gchar *name)
2459 {
2460         FolderItem *item;
2461         FolderItem *new_item;
2462
2463         g_return_val_if_fail(folder != NULL, NULL);
2464         g_return_val_if_fail(folder->node != NULL, NULL);
2465         g_return_val_if_fail(folder->node->data != NULL, NULL);
2466         g_return_val_if_fail(folder->account != NULL, NULL);
2467         g_return_val_if_fail(name != NULL, NULL);
2468
2469         item = FOLDER_ITEM(folder->node->data);
2470         new_item = imap_create_folder(folder, item, name);
2471
2472         if (!new_item) {
2473                 g_warning("Can't create '%s'\n", name);
2474                 if (!folder->inbox) return NULL;
2475
2476                 new_item = imap_create_folder(folder, folder->inbox, name);
2477                 if (!new_item)
2478                         g_warning("Can't create '%s' under INBOX\n", name);
2479                 else
2480                         new_item->stype = stype;
2481         } else
2482                 new_item->stype = stype;
2483
2484         return new_item;
2485 }
2486
2487 static gchar *imap_folder_get_path(Folder *folder)
2488 {
2489         gchar *folder_path;
2490
2491         g_return_val_if_fail(folder != NULL, NULL);
2492         g_return_val_if_fail(folder->account != NULL, NULL);
2493
2494         folder_path = g_strconcat(get_imap_cache_dir(),
2495                                   G_DIR_SEPARATOR_S,
2496                                   folder->account->recv_server,
2497                                   G_DIR_SEPARATOR_S,
2498                                   folder->account->userid,
2499                                   NULL);
2500
2501         return folder_path;
2502 }
2503
2504 #ifdef G_OS_WIN32
2505 static gchar *imap_encode_unsafe_chars(const gchar *str)
2506 {
2507         gchar *ret = NULL, *o_ret;
2508         gchar *i;
2509         if (!str) 
2510                 return NULL;
2511         ret = g_malloc(3*strlen(str)+1);
2512         o_ret = ret;
2513         for (i = str; *i; i++) {
2514                 switch(*i) {
2515                         case ':':
2516                         case '|':
2517                         case '<':
2518                         case '>':
2519                         case '*':
2520                         case '?':
2521                         case '#':
2522                                 *ret++ = '%';
2523                                 *ret++ = '0'+(*i/10);
2524                                 *ret++ = '0'+(*i%10);
2525                                 break;
2526                         default:
2527                                 *ret++ = *i;
2528                 }
2529         }
2530         *ret++ = '\0';
2531         return o_ret;
2532 }
2533 #endif
2534 static gchar *imap_item_get_path(Folder *folder, FolderItem *item)
2535 {
2536         gchar *folder_path, *path;
2537         gchar *item_path = NULL;
2538         
2539         g_return_val_if_fail(folder != NULL, NULL);
2540         g_return_val_if_fail(item != NULL, NULL);
2541         folder_path = imap_folder_get_path(folder);
2542
2543         g_return_val_if_fail(folder_path != NULL, NULL);
2544
2545 #ifdef G_OS_UNIX
2546         item_path = g_strdup(item->path);
2547 #else
2548         item_path = imap_encode_unsafe_chars(item->path);
2549 #endif  
2550
2551         if (g_path_is_absolute(folder_path)) {
2552                 if (item_path)
2553                         path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
2554                                            item_path, NULL);
2555                 else
2556                         path = g_strdup(folder_path);
2557         } else {
2558                 if (item_path)
2559                         path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
2560                                            folder_path, G_DIR_SEPARATOR_S,
2561                                            item_path, NULL);
2562                 else
2563                         path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
2564                                            folder_path, NULL);
2565         }
2566         g_free(folder_path);
2567         g_free(item_path);
2568 #ifdef G_OS_WIN32
2569         while (strchr(path, '/'))
2570                 *strchr(path, '/') = '\\';
2571 #endif
2572
2573         return path;
2574 }
2575
2576 static FolderItem *imap_create_folder(Folder *folder, FolderItem *parent,
2577                                const gchar *name)
2578 {
2579         gchar *dirpath, *imap_path;
2580         IMAPSession *session;
2581         FolderItem *new_item;
2582         gchar separator;
2583         gchar *new_name;
2584         const gchar *p;
2585         gint ok = MAILIMAP_NO_ERROR;
2586         gboolean no_select = FALSE, no_sub = FALSE;
2587         gboolean exist = FALSE;
2588         
2589         g_return_val_if_fail(folder != NULL, NULL);
2590         g_return_val_if_fail(folder->account != NULL, NULL);
2591         g_return_val_if_fail(parent != NULL, NULL);
2592         g_return_val_if_fail(name != NULL, NULL);
2593
2594         debug_print("getting session...\n");
2595         session = imap_session_get(folder);
2596         if (!session) {
2597                 return NULL;
2598         }
2599
2600         if (!folder_item_parent(parent) && strcmp(name, "INBOX") == 0) {
2601                 dirpath = g_strdup(name);
2602         }else if (parent->path)
2603                 dirpath = g_strconcat(parent->path, "/", name, NULL);
2604         else if ((p = strchr(name, '/')) != NULL && *(p + 1) != '\0')
2605                 dirpath = g_strdup(name);
2606         else if (folder->account->imap_dir && *folder->account->imap_dir) {
2607                 gchar *imap_dir;
2608
2609                 Xstrdup_a(imap_dir, folder->account->imap_dir, {return NULL;});
2610                 strtailchomp(imap_dir, '/');
2611                 dirpath = g_strconcat(imap_dir, "/", name, NULL);
2612         } else
2613                 dirpath = g_strdup(name);
2614                 
2615         
2616
2617         /* keep trailing directory separator to create a folder that contains
2618            sub folder */
2619         imap_path = imap_utf8_to_modified_utf7(dirpath, FALSE);
2620
2621         strtailchomp(dirpath, '/');
2622         Xstrdup_a(new_name, name, {
2623                 g_free(dirpath); 
2624                 g_free(imap_path);
2625                 return NULL;});
2626
2627         separator = imap_get_path_separator(session, IMAP_FOLDER(folder), imap_path, &ok);
2628         if (is_fatal(ok)) {
2629                 g_free(imap_path);
2630                 return NULL;
2631         }
2632         imap_path_separator_subst(imap_path, separator);
2633         /* remove trailing / for display */
2634         strtailchomp(new_name, '/');
2635
2636         if (strcmp(dirpath, "INBOX") != 0) {
2637                 GPtrArray *argbuf;
2638                 int r;
2639                 clist * lep_list;
2640                 
2641                 argbuf = g_ptr_array_new();
2642                 r = imap_threaded_list(folder, "", imap_path, &lep_list);
2643                 if (r != MAILIMAP_NO_ERROR) {
2644                         imap_handle_error(SESSION(session), r);
2645                         log_warning(LOG_PROTOCOL, _("can't create mailbox: LIST failed\n"));
2646                         g_free(imap_path);
2647                         g_free(dirpath);
2648                         ptr_array_free_strings(argbuf);
2649                         g_ptr_array_free(argbuf, TRUE);
2650                         return NULL;
2651                 }
2652                 
2653                 if (clist_count(lep_list) > 0)
2654                         exist = TRUE;
2655                 mailimap_list_result_free(lep_list);
2656                 lep_list = NULL;
2657                 if (!exist) {
2658                         ok = imap_cmd_create(session, imap_path);
2659                         if (ok != MAILIMAP_NO_ERROR) {
2660                                 log_warning(LOG_PROTOCOL, _("can't create mailbox\n"));
2661                                 g_free(imap_path);
2662                                 g_free(dirpath);
2663                                 return NULL;
2664                         }
2665                         r = imap_threaded_list(folder, "", imap_path, &lep_list);
2666                         if (r == MAILIMAP_NO_ERROR) {
2667                                 GSList *item_list = imap_list_from_lep(IMAP_FOLDER(folder),
2668                                                lep_list, dirpath, TRUE);
2669                                 if (item_list) {
2670                                         FolderItem *cur_item = FOLDER_ITEM(item_list->data);
2671                                         no_select = cur_item->no_select;
2672                                         no_sub = cur_item->no_sub;
2673                                         g_slist_free(item_list);
2674                                 } 
2675                                 mailimap_list_result_free(lep_list);
2676                         } else {
2677                                 imap_handle_error(SESSION(session), r);
2678                         }
2679                 }
2680                 imap_threaded_subscribe(folder, imap_path, TRUE);
2681         } else {
2682                 clist *lep_list;
2683                 int r;
2684                 /* just get flags */
2685                 r = imap_threaded_list(folder, "", "INBOX", &lep_list);
2686                 if (r == MAILIMAP_NO_ERROR) {
2687                         GSList *item_list = imap_list_from_lep(IMAP_FOLDER(folder),
2688                                        lep_list, dirpath, TRUE);
2689                         if (item_list) {
2690                                 FolderItem *cur_item = FOLDER_ITEM(item_list->data);
2691                                 no_select = cur_item->no_select;
2692                                 no_sub = cur_item->no_sub;
2693                                 g_slist_free(item_list);
2694                         } 
2695                         mailimap_list_result_free(lep_list);
2696                 } else {
2697                         imap_handle_error(SESSION(session), r);
2698                 }
2699         }
2700
2701         new_item = folder_item_new(folder, new_name, dirpath);
2702         new_item->no_select = no_select;
2703         new_item->no_sub = no_sub;
2704         folder_item_append(parent, new_item);
2705         g_free(imap_path);
2706         g_free(dirpath);
2707
2708         dirpath = folder_item_get_path(new_item);
2709         if (!is_dir_exist(dirpath))
2710                 make_dir_hier(dirpath);
2711         g_free(dirpath);
2712
2713         if (exist) {
2714                 /* folder existed, scan it */
2715                 imap_scan_required(folder, new_item);
2716                 folder_item_scan_full(new_item, FALSE);
2717         }
2718
2719         return new_item;
2720 }
2721
2722 static gint imap_rename_folder(Folder *folder, FolderItem *item,
2723                                const gchar *name)
2724 {
2725         gchar *dirpath;
2726         gchar *newpath;
2727         gchar *real_oldpath;
2728         gchar *real_newpath;
2729         gchar *paths[2];
2730         gchar *old_cache_dir;
2731         gchar *new_cache_dir;
2732         IMAPSession *session;
2733         gchar separator;
2734         gint ok = MAILIMAP_NO_ERROR;
2735         gint exists, recent, unseen;
2736         guint32 uid_validity;
2737
2738         g_return_val_if_fail(folder != NULL, -1);
2739         g_return_val_if_fail(item != NULL, -1);
2740         g_return_val_if_fail(item->path != NULL, -1);
2741         g_return_val_if_fail(name != NULL, -1);
2742
2743         debug_print("getting session...\n");
2744         session = imap_session_get(folder);
2745         if (!session) {
2746                 return -1;
2747         }
2748
2749         if (strchr(name, imap_get_path_separator(session, IMAP_FOLDER(folder), item->path, &ok)) != NULL ||
2750                 is_fatal(ok)) {
2751                 g_warning(_("New folder name must not contain the namespace "
2752                             "path separator"));
2753                 return -1;
2754         }
2755
2756         real_oldpath = imap_get_real_path(session, IMAP_FOLDER(folder), item->path, &ok);
2757         if (is_fatal(ok)) {
2758                 return -1;
2759         }
2760
2761         g_free(session->mbox);
2762         session->mbox = NULL;
2763         session->exists = 0;
2764         session->recent = 0;
2765         session->expunge = 0;
2766         ok = imap_cmd_examine(session, "INBOX",
2767                               &exists, &recent, &unseen, &uid_validity, FALSE);
2768         if (ok != MAILIMAP_NO_ERROR) {
2769                 g_free(real_oldpath);
2770                 return -1;
2771         }
2772
2773         separator = imap_get_path_separator(session, IMAP_FOLDER(folder), item->path, &ok);
2774         if (is_fatal(ok))
2775                 return -1;
2776         if (strchr(item->path, G_DIR_SEPARATOR)) {
2777                 dirpath = g_path_get_dirname(item->path);
2778                 newpath = g_strconcat(dirpath, G_DIR_SEPARATOR_S, name, NULL);
2779                 g_free(dirpath);
2780         } else
2781                 newpath = g_strdup(name);
2782
2783         real_newpath = imap_utf8_to_modified_utf7(newpath, FALSE);
2784         imap_path_separator_subst(real_newpath, separator);
2785
2786         ok = imap_cmd_rename(session, real_oldpath, real_newpath);
2787         if (ok != MAILIMAP_NO_ERROR) {
2788                 log_warning(LOG_PROTOCOL, _("can't rename mailbox: %s to %s\n"),
2789                             real_oldpath, real_newpath);
2790                 g_free(real_oldpath);
2791                 g_free(newpath);
2792                 g_free(real_newpath);
2793                 return -1;
2794         }
2795         g_free(item->name);
2796         item->name = g_strdup(name);
2797
2798         old_cache_dir = folder_item_get_path(item);
2799
2800         paths[0] = g_strdup(item->path);
2801         paths[1] = newpath;
2802         g_node_traverse(item->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
2803                         imap_rename_folder_func, paths);
2804
2805         if (is_dir_exist(old_cache_dir)) {
2806                 new_cache_dir = folder_item_get_path(item);
2807                 if (g_rename(old_cache_dir, new_cache_dir) < 0) {
2808                         FILE_OP_ERROR(old_cache_dir, "rename");
2809                 }
2810                 g_free(new_cache_dir);
2811         }
2812
2813         g_free(old_cache_dir);
2814         g_free(paths[0]);
2815         g_free(newpath);
2816         g_free(real_oldpath);
2817         g_free(real_newpath);
2818         return 0;
2819 }
2820
2821 gint imap_subscribe(Folder *folder, FolderItem *item, gchar *rpath, gboolean sub)
2822 {
2823         gchar *path;
2824         gint r = MAILIMAP_NO_ERROR;
2825         IMAPSession *session;
2826         debug_print("getting session...\n");
2827
2828         session = imap_session_get(folder);
2829         if (!session) {
2830                 return -1;
2831         }
2832         if (item && item->path) {
2833                 path = imap_get_real_path(session, IMAP_FOLDER(folder), item->path, &r);
2834                 if (!path)
2835                         return -1;
2836                 if (is_fatal(r)) {
2837                         g_free(path);
2838                         return -1;
2839                 }
2840                 if (!strcmp(path, "INBOX") && sub == FALSE) {
2841                         g_free(path);
2842                         return -1;
2843                 }
2844                 debug_print("%ssubscribing %s\n", sub?"":"un", path);
2845                 r = imap_threaded_subscribe(folder, path, sub);
2846                 g_free(path);
2847         } else if (rpath) {
2848                 r = imap_threaded_subscribe(folder, rpath, sub);
2849         } else
2850                 return -1;
2851         return r;
2852 }
2853
2854 static gint imap_remove_folder_real(Folder *folder, FolderItem *item)
2855 {
2856         gint ok = MAILIMAP_NO_ERROR;
2857         IMAPSession *session;
2858         gchar *path;
2859         gchar *cache_dir;
2860         gboolean selected_folder;
2861
2862         g_return_val_if_fail(folder != NULL, -1);
2863         g_return_val_if_fail(item != NULL, -1);
2864         g_return_val_if_fail(item->path != NULL, -1);
2865
2866         debug_print("getting session...\n");
2867         session = imap_session_get(folder);
2868         if (!session) {
2869                 return -1;
2870         }
2871         path = imap_get_real_path(session, IMAP_FOLDER(folder), item->path, &ok);
2872         if (is_fatal(ok))
2873                 return -1;
2874
2875         imap_threaded_subscribe(folder, path, FALSE);
2876
2877         selected_folder = (session->mbox != NULL) &&
2878                           (!strcmp(session->mbox, item->path));
2879         if (selected_folder) {
2880                 ok = imap_cmd_close(session);
2881                 if (ok != MAILIMAP_NO_ERROR) {
2882                         debug_print("close err %d\n", ok);
2883                         return ok;
2884                 }
2885         }
2886         ok = imap_cmd_delete(session, path);
2887         if (ok != MAILIMAP_NO_ERROR && !is_fatal(ok)) {
2888                 gchar *tmp = NULL;
2889                 
2890                 ok = MAILIMAP_NO_ERROR;
2891                 tmp = g_strdup_printf("%s%c", path, 
2892                                 imap_get_path_separator(session, IMAP_FOLDER(folder), path, &ok));
2893                 g_free(path);
2894                 path = tmp;
2895                 if (!is_fatal(ok))
2896                         ok = imap_cmd_delete(session, path);
2897         }
2898
2899         if (ok != MAILIMAP_NO_ERROR) {
2900                 log_warning(LOG_PROTOCOL, _("can't delete mailbox\n"));
2901                 g_free(path);
2902                 return -1;
2903         }
2904
2905         g_free(path);
2906         cache_dir = folder_item_get_path(item);
2907         if (is_dir_exist(cache_dir) && remove_dir_recursive(cache_dir) < 0)
2908                 g_warning("can't remove directory '%s'\n", cache_dir);
2909         g_free(cache_dir);
2910         folder_item_remove(item);
2911         return 0;
2912 }
2913
2914 static gint imap_remove_folder(Folder *folder, FolderItem *item)
2915 {
2916         GNode *node, *next;
2917
2918         g_return_val_if_fail(item != NULL, -1);
2919         g_return_val_if_fail(item->folder != NULL, -1);
2920         g_return_val_if_fail(item->node != NULL, -1);
2921
2922         node = item->node->children;
2923         while (node != NULL) {
2924                 next = node->next;
2925                 if (imap_remove_folder(folder, FOLDER_ITEM(node->data)) < 0)
2926                         return -1;
2927                 node = next;
2928         }
2929         debug_print("IMAP removing %s\n", item->path);
2930
2931         if (imap_remove_all_msg(folder, item) < 0)
2932                 return -1;
2933         return imap_remove_folder_real(folder, item);
2934 }
2935
2936 typedef struct _uncached_data {
2937         IMAPSession *session;
2938         FolderItem *item;
2939         MsgNumberList *numlist;
2940         guint cur;
2941         guint total;
2942         gboolean done;
2943         int ok;
2944 } uncached_data;
2945
2946 static void *imap_get_uncached_messages_thread(void *data)
2947 {
2948         uncached_data *stuff = (uncached_data *)data;
2949         IMAPSession *session = stuff->session;
2950         FolderItem *item = stuff->item;
2951         MsgNumberList *numlist = stuff->numlist;
2952         GSList *newlist = NULL;
2953         GSList *llast = NULL;
2954         GSList *seq_list, *cur;
2955         gboolean got_alien_tags = FALSE;
2956
2957         debug_print("uncached_messages\n");
2958         
2959         if (session == NULL || item == NULL || item->folder == NULL
2960             || FOLDER_CLASS(item->folder) != &imap_class) {
2961                 stuff->done = TRUE;
2962                 return NULL;
2963         }
2964         
2965         seq_list = imap_get_lep_set_from_numlist(IMAP_FOLDER(item->folder), numlist);
2966         debug_print("get msgs info\n");
2967         for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
2968                 struct mailimap_set * imapset;
2969                 unsigned int i;
2970                 int r;
2971                 carray * env_list;
2972                 int count;
2973                 
2974                 if (session->cancelled)
2975                         break;
2976                 
2977                 imapset = cur->data;
2978                 
2979                 r = imap_threaded_fetch_env(session->folder,
2980                                             imapset, &env_list);
2981                 if (r != MAILIMAP_NO_ERROR) {
2982                         imap_handle_error(SESSION(session), r);
2983                         if (is_fatal(r)) {
2984                                 stuff->ok = r;
2985                                 return NULL;
2986                         }
2987                         continue;
2988                 }
2989
2990                 session_set_access_time(SESSION(session));
2991
2992                 count = 0;
2993                 for(i = 0 ; i < carray_count(env_list) ; i += 2) {
2994                         struct imap_fetch_env_info * info;
2995                         MsgInfo * msginfo;
2996                         GSList *tags = NULL, *cur = NULL;
2997                         info = carray_get(env_list, i);
2998                         tags = carray_get(env_list, i+1);
2999                         msginfo = imap_envelope_from_lep(info, item);
3000                         if (msginfo == NULL) {
3001                                 slist_free_strings(tags);
3002                                 g_slist_free(tags);
3003                                 continue;
3004                         }
3005                         g_slist_free(msginfo->tags);
3006                         msginfo->tags = NULL;
3007
3008                         for (cur = tags; cur; cur = cur->next) {
3009                                 gchar *real_tag = imap_modified_utf7_to_utf8(cur->data, TRUE);
3010                                 gint id = 0;
3011                                 id = tags_get_id_for_str(real_tag);
3012                                 if (id == -1) {
3013                                         id = tags_add_tag(real_tag);
3014                                         got_alien_tags = TRUE;
3015                                 }
3016                                 if (!g_slist_find(msginfo->tags, GINT_TO_POINTER(id))) {
3017                                         msginfo->tags = g_slist_prepend(
3018                                                         msginfo->tags,
3019                                                         GINT_TO_POINTER(id));
3020                                 }
3021                                 g_free(real_tag);
3022                         }
3023                         if (msginfo->tags)
3024                                 msginfo->tags = g_slist_reverse(msginfo->tags);
3025                         slist_free_strings(tags);
3026                         g_slist_free(tags);
3027                         msginfo->folder = item;
3028                         if (!newlist)
3029                                 llast = newlist = g_slist_append(newlist, msginfo);
3030                         else {
3031                                 llast = g_slist_append(llast, msginfo);
3032                                 llast = llast->next;
3033                         }
3034                         count ++;
3035                 }
3036                 
3037                 imap_fetch_env_free(env_list);
3038         }
3039         
3040         if (got_alien_tags) {
3041                 tags_write_tags();
3042                 main_window_reflect_tags_changes(mainwindow_get_mainwindow());
3043         }
3044
3045         for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
3046                 struct mailimap_set * imapset;
3047                 
3048                 imapset = cur->data;
3049                 mailimap_set_free(imapset);
3050         }
3051         
3052         session_set_access_time(SESSION(session));
3053         stuff->done = TRUE;
3054         return newlist;
3055 }
3056
3057 #define MAX_MSG_NUM 50
3058
3059 static GSList *imap_get_uncached_messages(IMAPSession *session,
3060                                         FolderItem *item,
3061                                         MsgNumberList *numlist,
3062                                         int *r)
3063 {
3064         GSList *result = NULL;
3065         GSList * cur;
3066         uncached_data *data = g_new0(uncached_data, 1);
3067         int finished;
3068         
3069         finished = 0;
3070         cur = numlist;
3071         data->total = g_slist_length(numlist);
3072         data->ok = MAILIMAP_NO_ERROR;
3073         debug_print("messages list : %i\n", data->total);
3074
3075         while (cur != NULL) {
3076                 GSList * partial_result;
3077                 int count;
3078                 GSList * newlist;
3079                 GSList * llast;
3080                 
3081                 llast = NULL;
3082                 count = 0;
3083                 newlist = NULL;
3084                 while (count < MAX_MSG_NUM) {
3085                         void * p;
3086                         
3087                         p = cur->data;
3088                         
3089                         if (newlist == NULL)
3090                                 llast = newlist = g_slist_append(newlist, p);
3091                         else {
3092                                 llast = g_slist_append(llast, p);
3093                                 llast = llast->next;
3094                         }
3095                         count ++;
3096                         
3097                         cur = cur->next;
3098                         if (cur == NULL)
3099                                 break;
3100                 }
3101                 
3102                 data->done = FALSE;
3103                 data->session = session;
3104                 data->item = item;
3105                 data->numlist = newlist;
3106                 data->cur += count;
3107                 
3108                 if (prefs_common.work_offline && 
3109                     !inc_offline_should_override(FALSE,
3110                         _("Claws Mail needs network access in order "
3111                           "to access the IMAP server."))) {
3112                         g_free(data);
3113                         return NULL;
3114                 }
3115                 
3116                 partial_result =
3117                         (GSList *)imap_get_uncached_messages_thread(data);
3118                 *r = data->ok;
3119                 if (data->ok != MAILIMAP_NO_ERROR) {
3120                         goto bail;
3121                 }
3122                 statusbar_progress_all(data->cur,data->total, 1);
3123                 
3124                 g_slist_free(newlist);
3125                 
3126                 result = g_slist_concat(result, partial_result);
3127         }
3128 bail:
3129         g_free(data);
3130         
3131         statusbar_progress_all(0,0,0);
3132         statusbar_pop_all();
3133         
3134         return result;
3135 }
3136
3137 static void imap_delete_all_cached_messages(FolderItem *item)
3138 {
3139         gchar *dir;
3140
3141         g_return_if_fail(item != NULL);
3142         g_return_if_fail(item->folder != NULL);
3143         g_return_if_fail(FOLDER_CLASS(item->folder) == &imap_class);
3144
3145         debug_print("Deleting all cached messages...\n");
3146
3147         dir = folder_item_get_path(item);
3148         if (is_dir_exist(dir))
3149                 remove_all_numbered_files(dir);
3150         g_free(dir);
3151
3152         debug_print("done.\n");
3153 }
3154
3155 gchar imap_get_path_separator_for_item(FolderItem *item)
3156 {
3157         Folder *folder = NULL;
3158         IMAPFolder *imap_folder = NULL;
3159         IMAPSession *session = NULL;
3160         gchar result = '/';
3161         gint ok = MAILIMAP_NO_ERROR;
3162         if (!item)
3163                 return '/';
3164         folder = item->folder;
3165         
3166         if (!folder)
3167                 return '/';
3168         
3169         imap_folder = IMAP_FOLDER(folder);
3170         
3171         if (!imap_folder)
3172                 return '/';
3173         
3174         debug_print("getting session...");
3175         session = imap_session_get(FOLDER(folder));
3176         result = imap_get_path_separator(session, imap_folder, item->path, &ok);
3177         return result;
3178 }
3179
3180 static gchar imap_refresh_path_separator(IMAPSession *session, IMAPFolder *folder, const gchar *subfolder, gint *ok)
3181 {
3182         clist * lep_list;
3183         int r;
3184         gchar separator = '\0';
3185         
3186         g_return_val_if_fail(session != NULL, '/');
3187         r = imap_threaded_list((Folder *)folder, "", subfolder, &lep_list);
3188         
3189         if (r != MAILIMAP_NO_ERROR) {
3190                 imap_handle_error(SESSION(session), r);
3191                 log_warning(LOG_PROTOCOL, _("LIST failed\n"));
3192                 *ok = r;
3193                 return '\0';
3194         }
3195
3196         if (lep_list != NULL && clist_count(lep_list) > 0) {
3197                 clistiter * iter = clist_begin(lep_list); 
3198                 struct mailimap_mailbox_list * mb;
3199                 mb = clist_content(iter);
3200
3201                 separator = mb->mb_delimiter;
3202                 debug_print("got separator: %c\n", folder->last_seen_separator);
3203         }
3204         *ok = MAILIMAP_NO_ERROR;
3205         mailimap_list_result_free(lep_list);
3206         return separator;
3207 }
3208
3209 static gchar imap_get_path_separator(IMAPSession *session, IMAPFolder *folder, const gchar *path, gint *ok)
3210 {
3211         gchar separator = '/';
3212         *ok = MAILIMAP_NO_ERROR;
3213         if (folder->last_seen_separator == 0) {
3214                 folder->last_seen_separator = imap_refresh_path_separator(session, folder, "", ok);
3215         }
3216
3217         if (folder->last_seen_separator == 0) {
3218                 folder->last_seen_separator = imap_refresh_path_separator(session, folder, "INBOX", ok);
3219         }
3220
3221         if (folder->last_seen_separator != 0) {
3222                 debug_print("using separator: %c\n", folder->last_seen_separator);
3223                 return folder->last_seen_separator;
3224         }
3225
3226         return separator;
3227 }
3228
3229 static gchar *imap_get_real_path(IMAPSession *session, IMAPFolder *folder, const gchar *path, gint *ok)
3230 {
3231         gchar *real_path = NULL;
3232         gchar separator;
3233         
3234         g_return_val_if_fail(folder != NULL, NULL);
3235         g_return_val_if_fail(path != NULL, NULL);
3236
3237         *ok = MAILIMAP_NO_ERROR;
3238         
3239         real_path = imap_utf8_to_modified_utf7(path, FALSE);
3240         separator = imap_get_path_separator(session, folder, path, ok);
3241         if (*ok == MAILIMAP_NO_ERROR)
3242                 imap_path_separator_subst(real_path, separator);
3243
3244         return real_path;
3245 }
3246
3247 static gint imap_set_message_flags(IMAPSession *session,
3248                                    IMAPFolderItem *item,
3249                                    MsgNumberList *numlist,
3250                                    IMAPFlags flags,
3251                                    GSList *tags,
3252                                    gboolean is_set)
3253 {
3254         gint ok = 0;
3255         GSList *seq_list;
3256         GSList * cur;
3257         gint total = 0;
3258         IMAPFolder *folder = NULL;
3259         GSList *sorted_list = NULL;
3260
3261         if (numlist == NULL || session == NULL)
3262                 return MAILIMAP_ERROR_BAD_STATE;
3263         
3264         folder = IMAP_FOLDER(session->folder);
3265         
3266         sorted_list = g_slist_copy(numlist);
3267         sorted_list = g_slist_sort(sorted_list, g_int_compare);
3268         
3269         cur = g_slist_last(sorted_list);
3270
3271         if (cur)
3272                 total = GPOINTER_TO_INT(cur->data);
3273         
3274         seq_list = imap_get_lep_set_from_numlist(IMAP_FOLDER(session->folder), sorted_list);
3275
3276         statusbar_print_all(_("Flagging messages..."));
3277
3278         for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) {
3279                 struct mailimap_set * imapset = (struct mailimap_set *)cur->data;
3280                 struct mailimap_set_item *set_item = NULL;
3281                 
3282                 if (imapset->set_list)
3283                         set_item = clist_content(clist_begin(imapset->set_list));
3284                 else
3285                         continue;
3286
3287                 if (set_item == NULL)
3288                         continue;
3289
3290                 statusbar_progress_all(set_item->set_first, total, 1);
3291
3292                 ok = imap_cmd_store(session, item, imapset,
3293                                     flags, tags, is_set);
3294                 statusbar_progress_all(set_item->set_last, total, 1);
3295                 if (ok != MAILIMAP_NO_ERROR && folder->max_set_size > 20) {
3296                         /* reduce max set size */
3297                         folder->max_set_size /= 2;
3298                 }
3299                 if (ok != MAILIMAP_NO_ERROR && is_fatal(ok)) {
3300                         break;
3301                 }
3302         }
3303         
3304         g_slist_free(sorted_list);
3305
3306         statusbar_progress_all(0,0,0);
3307         statusbar_pop_all();
3308
3309         imap_lep_set_free(seq_list);
3310         
3311         return ok;
3312 }
3313
3314 typedef struct _select_data {
3315         IMAPSession *session;
3316         gchar *real_path;
3317         gint *exists;
3318         gint *recent;
3319         gint *unseen;
3320         guint32 *uid_validity;
3321         gboolean done;
3322 } select_data;
3323
3324 static gint imap_select(IMAPSession *session, IMAPFolder *folder,
3325                         FolderItem *item,
3326                         gint *exists, gint *recent, gint *unseen,
3327                         guint32 *uid_validity, gint *can_create_flags,
3328                         gboolean block)
3329 {
3330         gchar *real_path;
3331         gint ok = MAILIMAP_NO_ERROR;
3332         gint exists_, recent_, unseen_;