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