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