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