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