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