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