2005-02-18 [colin] 1.0.1cvs14.1
[claws.git] / src / imap.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2005 Hiroyuki Yamamoto
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #  include "config.h"
22 #endif
23
24 #include "defs.h"
25
26 #include <glib.h>
27 #include <glib/gi18n.h>
28 #include <stdio.h>
29 #include <string.h>
30 #include <stdlib.h>
31 #include <dirent.h>
32 #include <unistd.h>
33 #include <ctype.h>
34 #include <time.h>
35 #if HAVE_ICONV
36 #  include <iconv.h>
37 #endif
38
39 #if USE_OPENSSL
40 #  include "ssl.h"
41 #endif
42 #include "folder.h"
43 #include "session.h"
44 #include "procmsg.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_path_get_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                         g_free(base);
1389                 }
1390
1391                 if (new_item->no_sub == FALSE)
1392                         imap_scan_tree_recursive(session, new_item);
1393         }
1394
1395         g_slist_free(item_list);
1396
1397         return IMAP_SUCCESS;
1398 }
1399
1400 static GSList *imap_parse_list(IMAPFolder *folder, IMAPSession *session,
1401                                const gchar *real_path, gchar *separator)
1402 {
1403         gchar buf[IMAPBUFSIZE];
1404         gchar flags[256];
1405         gchar separator_str[16];
1406         gchar *p;
1407         gchar *base;
1408         gchar *loc_name, *loc_path;
1409         GSList *item_list = NULL;
1410         GString *str;
1411         FolderItem *new_item;
1412
1413         debug_print("getting list of %s ...\n",
1414                     *real_path ? real_path : "\"\"");
1415
1416         str = g_string_new(NULL);
1417
1418         for (;;) {
1419                 if (sock_gets(SESSION(session)->sock, buf, sizeof(buf)) <= 0) {
1420                         log_warning(_("error occurred while getting LIST.\n"));
1421                         break;
1422                 }
1423                 strretchomp(buf);
1424                 if (buf[0] != '*' || buf[1] != ' ') {
1425                         log_print("IMAP4< %s\n", buf);
1426                         if (sscanf(buf, "%*d %16s", buf) < 1 ||
1427                             strcmp(buf, "OK") != 0)
1428                                 log_warning(_("error occurred while getting LIST.\n"));
1429                                 
1430                         break;
1431                 }
1432                 debug_print("IMAP4< %s\n", buf);
1433
1434                 g_string_assign(str, buf);
1435                 p = str->str + 2;
1436                 if (strncmp(p, "LIST ", 5) != 0) continue;
1437                 p += 5;
1438
1439                 if (*p != '(') continue;
1440                 p++;
1441                 p = strchr_cpy(p, ')', flags, sizeof(flags));
1442                 if (!p) continue;
1443                 while (*p == ' ') p++;
1444
1445                 p = strchr_cpy(p, ' ', separator_str, sizeof(separator_str));
1446                 if (!p) continue;
1447                 extract_quote(separator_str, '"');
1448                 if (!strcmp(separator_str, "NIL"))
1449                         separator_str[0] = '\0';
1450                 if (separator)
1451                         *separator = separator_str[0];
1452
1453                 buf[0] = '\0';
1454                 while (*p == ' ') p++;
1455                 if (*p == '{' || *p == '"')
1456                         p = imap_parse_atom(SESSION(session)->sock, p,
1457                                             buf, sizeof(buf), str);
1458                 else
1459                         strncpy2(buf, p, sizeof(buf));
1460                 strtailchomp(buf, separator_str[0]);
1461                 if (buf[0] == '\0') continue;
1462                 if (!strcmp(buf, real_path)) continue;
1463
1464                 if (separator_str[0] != '\0')
1465                         subst_char(buf, separator_str[0], '/');
1466                 base = g_path_get_basename(buf);
1467                 if (base[0] == '.') continue;
1468
1469                 loc_name = imap_modified_utf7_to_utf8(base);
1470                 loc_path = imap_modified_utf7_to_utf8(buf);
1471                 new_item = folder_item_new(FOLDER(folder), loc_name, loc_path);
1472                 if (strcasestr(flags, "\\Noinferiors") != NULL)
1473                         new_item->no_sub = TRUE;
1474                 if (strcmp(buf, "INBOX") != 0 &&
1475                     strcasestr(flags, "\\Noselect") != NULL)
1476                         new_item->no_select = TRUE;
1477
1478                 item_list = g_slist_append(item_list, new_item);
1479
1480                 debug_print("folder '%s' found.\n", loc_path);
1481                 g_free(base);
1482                 g_free(loc_path);
1483                 g_free(loc_name);
1484         }
1485
1486         g_string_free(str, TRUE);
1487
1488         return item_list;
1489 }
1490
1491 static gint imap_create_tree(Folder *folder)
1492 {
1493         g_return_val_if_fail(folder != NULL, -1);
1494         g_return_val_if_fail(folder->node != NULL, -1);
1495         g_return_val_if_fail(folder->node->data != NULL, -1);
1496         g_return_val_if_fail(folder->account != NULL, -1);
1497
1498         imap_scan_tree(folder);
1499         imap_create_missing_folders(folder);
1500
1501         return 0;
1502 }
1503
1504 static void imap_create_missing_folders(Folder *folder)
1505 {
1506         g_return_if_fail(folder != NULL);
1507
1508         if (!folder->inbox)
1509                 folder->inbox = imap_create_special_folder
1510                         (folder, F_INBOX, "INBOX");
1511 #if 0
1512         if (!folder->outbox)
1513                 folder->outbox = imap_create_special_folder
1514                         (folder, F_OUTBOX, "Sent");
1515         if (!folder->draft)
1516                 folder->draft = imap_create_special_folder
1517                         (folder, F_DRAFT, "Drafts");
1518         if (!folder->queue)
1519                 folder->queue = imap_create_special_folder
1520                         (folder, F_QUEUE, "Queue");
1521 #endif
1522         if (!folder->trash)
1523                 folder->trash = imap_create_special_folder
1524                         (folder, F_TRASH, "Trash");
1525 }
1526
1527 static FolderItem *imap_create_special_folder(Folder *folder,
1528                                               SpecialFolderItemType stype,
1529                                               const gchar *name)
1530 {
1531         FolderItem *item;
1532         FolderItem *new_item;
1533
1534         g_return_val_if_fail(folder != NULL, NULL);
1535         g_return_val_if_fail(folder->node != NULL, NULL);
1536         g_return_val_if_fail(folder->node->data != NULL, NULL);
1537         g_return_val_if_fail(folder->account != NULL, NULL);
1538         g_return_val_if_fail(name != NULL, NULL);
1539
1540         item = FOLDER_ITEM(folder->node->data);
1541         new_item = imap_create_folder(folder, item, name);
1542
1543         if (!new_item) {
1544                 g_warning("Can't create '%s'\n", name);
1545                 if (!folder->inbox) return NULL;
1546
1547                 new_item = imap_create_folder(folder, folder->inbox, name);
1548                 if (!new_item)
1549                         g_warning("Can't create '%s' under INBOX\n", name);
1550                 else
1551                         new_item->stype = stype;
1552         } else
1553                 new_item->stype = stype;
1554
1555         return new_item;
1556 }
1557
1558 static gchar *imap_folder_get_path(Folder *folder)
1559 {
1560         gchar *folder_path;
1561
1562         g_return_val_if_fail(folder != NULL, NULL);
1563         g_return_val_if_fail(folder->account != NULL, NULL);
1564
1565         folder_path = g_strconcat(get_imap_cache_dir(),
1566                                   G_DIR_SEPARATOR_S,
1567                                   folder->account->recv_server,
1568                                   G_DIR_SEPARATOR_S,
1569                                   folder->account->userid,
1570                                   NULL);
1571
1572         return folder_path;
1573 }
1574
1575 static gchar *imap_item_get_path(Folder *folder, FolderItem *item)
1576 {
1577         gchar *folder_path, *path;
1578
1579         g_return_val_if_fail(folder != NULL, NULL);
1580         g_return_val_if_fail(item != NULL, NULL);
1581         folder_path = imap_folder_get_path(folder);
1582
1583         g_return_val_if_fail(folder_path != NULL, NULL);
1584         if (folder_path[0] == G_DIR_SEPARATOR) {
1585                 if (item->path)
1586                         path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
1587                                            item->path, NULL);
1588                 else
1589                         path = g_strdup(folder_path);
1590         } else {
1591                 if (item->path)
1592                         path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1593                                            folder_path, G_DIR_SEPARATOR_S,
1594                                            item->path, NULL);
1595                 else
1596                         path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1597                                            folder_path, NULL);
1598         }
1599         g_free(folder_path);
1600
1601         return path;
1602 }
1603
1604 static FolderItem *imap_create_folder(Folder *folder, FolderItem *parent,
1605                                const gchar *name)
1606 {
1607         gchar *dirpath, *imap_path;
1608         IMAPSession *session;
1609         FolderItem *new_item;
1610         gchar separator;
1611         gchar *new_name;
1612         const gchar *p;
1613         gint ok;
1614
1615         g_return_val_if_fail(folder != NULL, NULL);
1616         g_return_val_if_fail(folder->account != NULL, NULL);
1617         g_return_val_if_fail(parent != NULL, NULL);
1618         g_return_val_if_fail(name != NULL, NULL);
1619
1620         session = imap_session_get(folder);
1621         if (!session) return NULL;
1622
1623         if (!folder_item_parent(parent) && strcmp(name, "INBOX") == 0)
1624                 dirpath = g_strdup(name);
1625         else if (parent->path)
1626                 dirpath = g_strconcat(parent->path, "/", name, NULL);
1627         else if ((p = strchr(name, '/')) != NULL && *(p + 1) != '\0')
1628                 dirpath = g_strdup(name);
1629         else if (folder->account->imap_dir && *folder->account->imap_dir) {
1630                 gchar *imap_dir;
1631
1632                 Xstrdup_a(imap_dir, folder->account->imap_dir, return NULL);
1633                 strtailchomp(imap_dir, '/');
1634                 dirpath = g_strconcat(imap_dir, "/", name, NULL);
1635         } else
1636                 dirpath = g_strdup(name);
1637
1638         /* keep trailing directory separator to create a folder that contains
1639            sub folder */
1640         imap_path = imap_utf8_to_modified_utf7(dirpath);
1641         strtailchomp(dirpath, '/');
1642         Xstrdup_a(new_name, name, {g_free(dirpath); return NULL;});
1643         strtailchomp(new_name, '/');
1644         separator = imap_get_path_separator(IMAP_FOLDER(folder), imap_path);
1645         imap_path_separator_subst(imap_path, separator);
1646         subst_char(new_name, '/', separator);
1647
1648         if (strcmp(name, "INBOX") != 0) {
1649                 GPtrArray *argbuf;
1650                 gint i;
1651                 gboolean exist = FALSE;
1652
1653                 argbuf = g_ptr_array_new();
1654                 ok = imap_cmd_list(session, NULL, imap_path,
1655                                    argbuf);
1656                 if (ok != IMAP_SUCCESS) {
1657                         log_warning(_("can't create mailbox: LIST failed\n"));
1658                         g_free(imap_path);
1659                         g_free(dirpath);
1660                         ptr_array_free_strings(argbuf);
1661                         g_ptr_array_free(argbuf, TRUE);
1662                         return NULL;
1663                 }
1664
1665                 for (i = 0; i < argbuf->len; i++) {
1666                         gchar *str;
1667                         str = g_ptr_array_index(argbuf, i);
1668                         if (!strncmp(str, "LIST ", 5)) {
1669                                 exist = TRUE;
1670                                 break;
1671                         }
1672                 }
1673                 ptr_array_free_strings(argbuf);
1674                 g_ptr_array_free(argbuf, TRUE);
1675
1676                 if (!exist) {
1677                         ok = imap_cmd_create(session, imap_path);
1678                         if (ok != IMAP_SUCCESS) {
1679                                 log_warning(_("can't create mailbox\n"));
1680                                 g_free(imap_path);
1681                                 g_free(dirpath);
1682                                 return NULL;
1683                         }
1684                 }
1685         }
1686
1687         new_item = folder_item_new(folder, new_name, dirpath);
1688         folder_item_append(parent, new_item);
1689         g_free(imap_path);
1690         g_free(dirpath);
1691
1692         dirpath = folder_item_get_path(new_item);
1693         if (!is_dir_exist(dirpath))
1694                 make_dir_hier(dirpath);
1695         g_free(dirpath);
1696
1697         return new_item;
1698 }
1699
1700 static gint imap_rename_folder(Folder *folder, FolderItem *item,
1701                                const gchar *name)
1702 {
1703         gchar *dirpath;
1704         gchar *newpath;
1705         gchar *real_oldpath;
1706         gchar *real_newpath;
1707         gchar *paths[2];
1708         gchar *old_cache_dir;
1709         gchar *new_cache_dir;
1710         IMAPSession *session;
1711         gchar separator;
1712         gint ok;
1713         gint exists, recent, unseen;
1714         guint32 uid_validity;
1715
1716         g_return_val_if_fail(folder != NULL, -1);
1717         g_return_val_if_fail(item != NULL, -1);
1718         g_return_val_if_fail(item->path != NULL, -1);
1719         g_return_val_if_fail(name != NULL, -1);
1720
1721         if (strchr(name, imap_get_path_separator(IMAP_FOLDER(folder), item->path)) != NULL) {
1722                 g_warning(_("New folder name must not contain the namespace "
1723                             "path separator"));
1724                 return -1;
1725         }
1726
1727         session = imap_session_get(folder);
1728         if (!session) return -1;
1729
1730         real_oldpath = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1731
1732         g_free(session->mbox);
1733         session->mbox = NULL;
1734         ok = imap_cmd_examine(session, "INBOX",
1735                               &exists, &recent, &unseen, &uid_validity);
1736         if (ok != IMAP_SUCCESS) {
1737                 g_free(real_oldpath);
1738                 return -1;
1739         }
1740
1741         separator = imap_get_path_separator(IMAP_FOLDER(folder), item->path);
1742         if (strchr(item->path, G_DIR_SEPARATOR)) {
1743                 dirpath = g_path_get_dirname(item->path);
1744                 newpath = g_strconcat(dirpath, G_DIR_SEPARATOR_S, name, NULL);
1745                 g_free(dirpath);
1746         } else
1747                 newpath = g_strdup(name);
1748
1749         real_newpath = imap_utf8_to_modified_utf7(newpath);
1750         imap_path_separator_subst(real_newpath, separator);
1751
1752         ok = imap_cmd_rename(session, real_oldpath, real_newpath);
1753         if (ok != IMAP_SUCCESS) {
1754                 log_warning(_("can't rename mailbox: %s to %s\n"),
1755                             real_oldpath, real_newpath);
1756                 g_free(real_oldpath);
1757                 g_free(newpath);
1758                 g_free(real_newpath);
1759                 return -1;
1760         }
1761
1762         g_free(item->name);
1763         item->name = g_strdup(name);
1764
1765         old_cache_dir = folder_item_get_path(item);
1766
1767         paths[0] = g_strdup(item->path);
1768         paths[1] = newpath;
1769         g_node_traverse(item->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1770                         imap_rename_folder_func, paths);
1771
1772         if (is_dir_exist(old_cache_dir)) {
1773                 new_cache_dir = folder_item_get_path(item);
1774                 if (rename(old_cache_dir, new_cache_dir) < 0) {
1775                         FILE_OP_ERROR(old_cache_dir, "rename");
1776                 }
1777                 g_free(new_cache_dir);
1778         }
1779
1780         g_free(old_cache_dir);
1781         g_free(paths[0]);
1782         g_free(newpath);
1783         g_free(real_oldpath);
1784         g_free(real_newpath);
1785
1786         return 0;
1787 }
1788
1789 static gint imap_remove_folder(Folder *folder, FolderItem *item)
1790 {
1791         gint ok;
1792         IMAPSession *session;
1793         gchar *path;
1794         gchar *cache_dir;
1795         gint exists, recent, unseen;
1796         guint32 uid_validity;
1797
1798         g_return_val_if_fail(folder != NULL, -1);
1799         g_return_val_if_fail(item != NULL, -1);
1800         g_return_val_if_fail(item->path != NULL, -1);
1801
1802         session = imap_session_get(folder);
1803         if (!session) return -1;
1804
1805         path = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1806
1807         ok = imap_cmd_examine(session, "INBOX",
1808                               &exists, &recent, &unseen, &uid_validity);
1809         if (ok != IMAP_SUCCESS) {
1810                 g_free(path);
1811                 return -1;
1812         }
1813
1814         ok = imap_cmd_delete(session, path);
1815         if (ok != IMAP_SUCCESS) {
1816                 log_warning(_("can't delete mailbox\n"));
1817                 g_free(path);
1818                 return -1;
1819         }
1820
1821         g_free(path);
1822         cache_dir = folder_item_get_path(item);
1823         if (is_dir_exist(cache_dir) && remove_dir_recursive(cache_dir) < 0)
1824                 g_warning("can't remove directory '%s'\n", cache_dir);
1825         g_free(cache_dir);
1826         folder_item_remove(item);
1827
1828         return 0;
1829 }
1830
1831 static GSList *imap_get_uncached_messages(IMAPSession *session,
1832                                           FolderItem *item,
1833                                           MsgNumberList *numlist)
1834 {
1835         gchar *tmp;
1836         GSList *newlist = NULL;
1837         GSList *llast = NULL;
1838         GString *str;
1839         MsgInfo *msginfo;
1840         GSList *seq_list, *cur;
1841         IMAPSet imapset;
1842
1843         g_return_val_if_fail(session != NULL, NULL);
1844         g_return_val_if_fail(item != NULL, NULL);
1845         g_return_val_if_fail(item->folder != NULL, NULL);
1846         g_return_val_if_fail(FOLDER_CLASS(item->folder) == &imap_class, NULL);
1847
1848         seq_list = imap_get_seq_set_from_numlist(numlist);
1849         for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
1850                 imapset = cur->data;
1851
1852                 if (imap_cmd_envelope(session, imapset)
1853                     != IMAP_SUCCESS) {
1854                         log_warning(_("can't get envelope\n"));
1855                         continue;
1856                 }
1857
1858                 str = g_string_new(NULL);
1859
1860                 for (;;) {
1861                         if ((tmp = sock_getline(SESSION(session)->sock)) == NULL) {
1862                                 log_warning(_("error occurred while getting envelope.\n"));
1863                                 g_string_free(str, TRUE);
1864                                 break;
1865                         }
1866                         strretchomp(tmp);
1867                         if (tmp[0] != '*' || tmp[1] != ' ') {
1868                                 log_print("IMAP4< %s\n", tmp);
1869                                 g_free(tmp);
1870                                 break;
1871                         }
1872                         if (strstr(tmp, "FETCH") == NULL) {
1873                                 log_print("IMAP4< %s\n", tmp);
1874                                 g_free(tmp);
1875                                 continue;
1876                         }
1877                         log_print("IMAP4< %s\n", tmp);
1878                         g_string_assign(str, tmp);
1879                         g_free(tmp);
1880
1881                         msginfo = imap_parse_envelope
1882                                 (SESSION(session)->sock, item, str);
1883                         if (!msginfo) {
1884                                 log_warning(_("can't parse envelope: %s\n"), str->str);
1885                                 continue;
1886                         }
1887                         if (item->stype == F_QUEUE) {
1888                                 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_QUEUED);
1889                         } else if (item->stype == F_DRAFT) {
1890                                 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_DRAFT);
1891                         }
1892
1893                         msginfo->folder = item;
1894
1895                         if (!newlist)
1896                                 llast = newlist = g_slist_append(newlist, msginfo);
1897                         else {
1898                                 llast = g_slist_append(llast, msginfo);
1899                                 llast = llast->next;
1900                         }
1901                 }
1902
1903                 g_string_free(str, TRUE);
1904         }
1905         imap_seq_set_free(seq_list);
1906         
1907         session_set_access_time(SESSION(session));
1908
1909         return newlist;
1910 }
1911
1912 static void imap_delete_all_cached_messages(FolderItem *item)
1913 {
1914         gchar *dir;
1915
1916         g_return_if_fail(item != NULL);
1917         g_return_if_fail(item->folder != NULL);
1918         g_return_if_fail(FOLDER_CLASS(item->folder) == &imap_class);
1919
1920         debug_print("Deleting all cached messages...\n");
1921
1922         dir = folder_item_get_path(item);
1923         if (is_dir_exist(dir))
1924                 remove_all_numbered_files(dir);
1925         g_free(dir);
1926
1927         debug_print("done.\n");
1928 }
1929
1930 #if USE_OPENSSL
1931 static SockInfo *imap_open_tunnel(const gchar *server,
1932                            const gchar *tunnelcmd,
1933                            SSLType ssl_type)
1934 #else
1935 static SockInfo *imap_open_tunnel(const gchar *server,
1936                            const gchar *tunnelcmd)
1937 #endif
1938 {
1939         SockInfo *sock;
1940
1941         if ((sock = sock_connect_cmd(server, tunnelcmd)) == NULL) {
1942                 log_warning(_("Can't establish IMAP4 session with: %s\n"),
1943                             server);
1944                 return NULL;
1945         }
1946 #if USE_OPENSSL
1947         return imap_init_sock(sock, ssl_type);
1948 #else
1949         return imap_init_sock(sock);
1950 #endif
1951 }
1952
1953
1954 #if USE_OPENSSL
1955 static SockInfo *imap_open(const gchar *server, gushort port,
1956                            SSLType ssl_type)
1957 #else
1958 static SockInfo *imap_open(const gchar *server, gushort port)
1959 #endif
1960 {
1961         SockInfo *sock;
1962
1963         if ((sock = sock_connect(server, port)) == NULL) {
1964                 log_warning(_("Can't connect to IMAP4 server: %s:%d\n"),
1965                             server, port);
1966                 return NULL;
1967         }
1968
1969 #if USE_OPENSSL
1970         if (ssl_type == SSL_TUNNEL && !ssl_init_socket(sock)) {
1971                 log_warning(_("Can't establish IMAP4 session with: %s:%d\n"),
1972                             server, port);
1973                 sock_close(sock);
1974                 return NULL;
1975         }
1976 #endif
1977         return sock;
1978 }
1979
1980 #if USE_OPENSSL
1981 static SockInfo *imap_init_sock(SockInfo *sock, SSLType ssl_type)
1982 #else
1983 static SockInfo *imap_init_sock(SockInfo *sock)
1984 #endif
1985 {
1986
1987         return sock;
1988 }
1989
1990 static GList *imap_parse_namespace_str(gchar *str)
1991 {
1992         guchar *p = str;
1993         gchar *name;
1994         gchar *separator;
1995         IMAPNameSpace *namespace;
1996         GList *ns_list = NULL;
1997
1998         while (*p != '\0') {
1999                 /* parse ("#foo" "/") */
2000
2001                 while (*p && *p != '(') p++;
2002                 if (*p == '\0') break;
2003                 p++;
2004
2005                 while (*p && *p != '"') p++;
2006                 if (*p == '\0') break;
2007                 p++;
2008                 name = p;
2009
2010                 while (*p && *p != '"') p++;
2011                 if (*p == '\0') break;
2012                 *p = '\0';
2013                 p++;
2014
2015                 while (*p && isspace(*p)) p++;
2016                 if (*p == '\0') break;
2017                 if (strncmp(p, "NIL", 3) == 0)
2018                         separator = NULL;
2019                 else if (*p == '"') {
2020                         p++;
2021                         separator = p;
2022                         while (*p && *p != '"') p++;
2023                         if (*p == '\0') break;
2024                         *p = '\0';
2025                         p++;
2026                 } else break;
2027
2028                 while (*p && *p != ')') p++;
2029                 if (*p == '\0') break;
2030                 p++;
2031
2032                 namespace = g_new(IMAPNameSpace, 1);
2033                 namespace->name = g_strdup(name);
2034                 namespace->separator = separator ? separator[0] : '\0';
2035                 ns_list = g_list_append(ns_list, namespace);
2036         }
2037
2038         return ns_list;
2039 }
2040
2041 static void imap_parse_namespace(IMAPSession *session, IMAPFolder *folder)
2042 {
2043         gchar *ns_str;
2044         gchar **str_array;
2045
2046         g_return_if_fail(session != NULL);
2047         g_return_if_fail(folder != NULL);
2048
2049         if (folder->ns_personal != NULL ||
2050             folder->ns_others   != NULL ||
2051             folder->ns_shared   != NULL)
2052                 return;
2053
2054         if (!imap_has_capability(session, "NAMESPACE")) {
2055                 imap_get_namespace_by_list(session, folder);
2056                 return;
2057         }
2058         
2059         if (imap_cmd_namespace(session, &ns_str)
2060             != IMAP_SUCCESS) {
2061                 log_warning(_("can't get namespace\n"));
2062                 return;
2063         }
2064
2065         str_array = strsplit_parenthesis(ns_str, '(', ')', 3);
2066         if (str_array == NULL) {
2067                 g_free(ns_str);
2068                 imap_get_namespace_by_list(session, folder);
2069                 return;
2070         }
2071         if (str_array[0])
2072                 folder->ns_personal = imap_parse_namespace_str(str_array[0]);
2073         if (str_array[0] && str_array[1])
2074                 folder->ns_others = imap_parse_namespace_str(str_array[1]);
2075         if (str_array[0] && str_array[1] && str_array[2])
2076                 folder->ns_shared = imap_parse_namespace_str(str_array[2]);
2077         g_strfreev(str_array);
2078         g_free(ns_str);
2079 }
2080
2081 static void imap_get_namespace_by_list(IMAPSession *session, IMAPFolder *folder)
2082 {
2083         GSList *item_list, *cur;
2084         gchar separator = '\0';
2085         IMAPNameSpace *namespace;
2086
2087         g_return_if_fail(session != NULL);
2088         g_return_if_fail(folder != NULL);
2089
2090         if (folder->ns_personal != NULL ||
2091             folder->ns_others   != NULL ||
2092             folder->ns_shared   != NULL)
2093                 return;
2094
2095         imap_gen_send(session, "LIST \"\" \"\"");
2096         item_list = imap_parse_list(folder, session, "", &separator);
2097         for (cur = item_list; cur != NULL; cur = cur->next)
2098                 folder_item_destroy(FOLDER_ITEM(cur->data));
2099         g_slist_free(item_list);
2100
2101         namespace = g_new(IMAPNameSpace, 1);
2102         namespace->name = g_strdup("");
2103         namespace->separator = separator;
2104         folder->ns_personal = g_list_append(NULL, namespace);
2105 }
2106
2107 static IMAPNameSpace *imap_find_namespace_from_list(GList *ns_list,
2108                                                     const gchar *path)
2109 {
2110         IMAPNameSpace *namespace = NULL;
2111         gchar *tmp_path, *name;
2112
2113         if (!path) path = "";
2114
2115         for (; ns_list != NULL; ns_list = ns_list->next) {
2116                 IMAPNameSpace *tmp_ns = ns_list->data;
2117
2118                 Xstrcat_a(tmp_path, path, "/", return namespace);
2119                 Xstrdup_a(name, tmp_ns->name, return namespace);
2120                 if (tmp_ns->separator && tmp_ns->separator != '/') {
2121                         subst_char(tmp_path, tmp_ns->separator, '/');
2122                         subst_char(name, tmp_ns->separator, '/');
2123                 }
2124                 if (strncmp(tmp_path, name, strlen(name)) == 0)
2125                         namespace = tmp_ns;
2126         }
2127
2128         return namespace;
2129 }
2130
2131 static IMAPNameSpace *imap_find_namespace(IMAPFolder *folder,
2132                                           const gchar *path)
2133 {
2134         IMAPNameSpace *namespace;
2135
2136         g_return_val_if_fail(folder != NULL, NULL);
2137
2138         namespace = imap_find_namespace_from_list(folder->ns_personal, path);
2139         if (namespace) return namespace;
2140         namespace = imap_find_namespace_from_list(folder->ns_others, path);
2141         if (namespace) return namespace;
2142         namespace = imap_find_namespace_from_list(folder->ns_shared, path);
2143         if (namespace) return namespace;
2144
2145         return NULL;
2146 }
2147
2148 static gchar imap_get_path_separator(IMAPFolder *folder, const gchar *path)
2149 {
2150         IMAPNameSpace *namespace;
2151         gchar separator = '/';
2152
2153         namespace = imap_find_namespace(folder, path);
2154         if (namespace && namespace->separator)
2155                 separator = namespace->separator;
2156
2157         return separator;
2158 }
2159
2160 static gchar *imap_get_real_path(IMAPFolder *folder, const gchar *path)
2161 {
2162         gchar *real_path;
2163         gchar separator;
2164
2165         g_return_val_if_fail(folder != NULL, NULL);
2166         g_return_val_if_fail(path != NULL, NULL);
2167
2168         real_path = imap_utf8_to_modified_utf7(path);
2169         separator = imap_get_path_separator(folder, path);
2170         imap_path_separator_subst(real_path, separator);
2171
2172         return real_path;
2173 }
2174
2175 static gchar *imap_parse_atom(SockInfo *sock, gchar *src,
2176                               gchar *dest, gint dest_len, GString *str)
2177 {
2178         gchar *cur_pos = src;
2179         gchar *nextline;
2180
2181         g_return_val_if_fail(str != NULL, cur_pos);
2182
2183         /* read the next line if the current response buffer is empty */
2184         while (isspace(*(guchar *)cur_pos)) cur_pos++;
2185         while (*cur_pos == '\0') {
2186                 if ((nextline = sock_getline(sock)) == NULL)
2187                         return cur_pos;
2188                 g_string_assign(str, nextline);
2189                 cur_pos = str->str;
2190                 strretchomp(nextline);
2191                 /* log_print("IMAP4< %s\n", nextline); */
2192                 debug_print("IMAP4< %s\n", nextline);
2193                 g_free(nextline);
2194
2195                 while (isspace(*(guchar *)cur_pos)) cur_pos++;
2196         }
2197
2198         if (!strncmp(cur_pos, "NIL", 3)) {
2199                 *dest = '\0';
2200                 cur_pos += 3;
2201         } else if (*cur_pos == '\"') {
2202                 gchar *p;
2203
2204                 p = get_quoted(cur_pos, '\"', dest, dest_len);
2205                 cur_pos = p ? p : cur_pos + 2;
2206         } else if (*cur_pos == '{') {
2207                 gchar buf[32];
2208                 gint len;
2209                 gint line_len = 0;
2210
2211                 cur_pos = strchr_cpy(cur_pos + 1, '}', buf, sizeof(buf));
2212                 len = atoi(buf);
2213                 g_return_val_if_fail(len >= 0, cur_pos);
2214
2215                 g_string_truncate(str, 0);
2216                 cur_pos = str->str;
2217
2218                 do {
2219                         if ((nextline = sock_getline(sock)) == NULL)
2220                                 return cur_pos;
2221                         line_len += strlen(nextline);
2222                         g_string_append(str, nextline);
2223                         cur_pos = str->str;
2224                         strretchomp(nextline);
2225                         /* log_print("IMAP4< %s\n", nextline); */
2226                         debug_print("IMAP4< %s\n", nextline);
2227                         g_free(nextline);
2228                 } while (line_len < len);
2229
2230                 memcpy(dest, cur_pos, MIN(len, dest_len - 1));
2231                 dest[MIN(len, dest_len - 1)] = '\0';
2232                 cur_pos += len;
2233         }
2234
2235         return cur_pos;
2236 }
2237
2238 static gchar *imap_get_header(SockInfo *sock, gchar *cur_pos, gchar **headers,
2239                               GString *str)
2240 {
2241         gchar *nextline;
2242         gchar buf[32];
2243         gint len;
2244         gint block_len = 0;
2245
2246         *headers = NULL;
2247
2248         g_return_val_if_fail(str != NULL, cur_pos);
2249
2250         while (isspace(*(guchar *)cur_pos)) cur_pos++;
2251
2252         g_return_val_if_fail(*cur_pos == '{', cur_pos);
2253
2254         cur_pos = strchr_cpy(cur_pos + 1, '}', buf, sizeof(buf));
2255         len = atoi(buf);
2256         g_return_val_if_fail(len >= 0, cur_pos);
2257
2258         g_string_truncate(str, 0);
2259         cur_pos = str->str;
2260
2261         do {
2262                 if ((nextline = sock_getline(sock)) == NULL)
2263                         return cur_pos;
2264                 block_len += strlen(nextline);
2265                 g_string_append(str, nextline);
2266                 cur_pos = str->str;
2267                 strretchomp(nextline);
2268                 /* debug_print("IMAP4< %s\n", nextline); */
2269                 g_free(nextline);
2270         } while (block_len < len);
2271
2272         debug_print("IMAP4< [contents of BODY.PEEK[HEADER.FIELDS (...)]]\n");
2273
2274         *headers = g_strndup(cur_pos, len);
2275         cur_pos += len;
2276
2277         while (isspace(*(guchar *)cur_pos)) cur_pos++;
2278         while (*cur_pos == '\0') {
2279                 if ((nextline = sock_getline(sock)) == NULL)
2280                         return cur_pos;
2281                 g_string_assign(str, nextline);
2282                 cur_pos = str->str;
2283                 strretchomp(nextline);
2284                 debug_print("IMAP4< %s\n", nextline);
2285                 g_free(nextline);
2286
2287                 while (isspace(*(guchar *)cur_pos)) cur_pos++;
2288         }
2289
2290         return cur_pos;
2291 }
2292
2293 static MsgFlags imap_parse_flags(const gchar *flag_str)  
2294 {
2295         const gchar *p = flag_str;
2296         MsgFlags flags = {0, 0};
2297
2298         flags.perm_flags = MSG_UNREAD;
2299
2300         while ((p = strchr(p, '\\')) != NULL) {
2301                 p++;
2302
2303                 if (g_ascii_strncasecmp(p, "Recent", 6) == 0 && MSG_IS_UNREAD(flags)) {
2304                         MSG_SET_PERM_FLAGS(flags, MSG_NEW);
2305                 } else if (g_ascii_strncasecmp(p, "Seen", 4) == 0) {
2306                         MSG_UNSET_PERM_FLAGS(flags, MSG_NEW|MSG_UNREAD);
2307                 } else if (g_ascii_strncasecmp(p, "Deleted", 7) == 0) {
2308                         MSG_SET_PERM_FLAGS(flags, MSG_DELETED);
2309                 } else if (g_ascii_strncasecmp(p, "Flagged", 7) == 0) {
2310                         MSG_SET_PERM_FLAGS(flags, MSG_MARKED);
2311                 } else if (g_ascii_strncasecmp(p, "Answered", 8) == 0) {
2312                         MSG_SET_PERM_FLAGS(flags, MSG_REPLIED);
2313                 }
2314         }
2315
2316         return flags;
2317 }
2318
2319 static MsgInfo *imap_parse_envelope(SockInfo *sock, FolderItem *item,
2320                                     GString *line_str)
2321 {
2322         gchar buf[IMAPBUFSIZE];
2323         MsgInfo *msginfo = NULL;
2324         gchar *cur_pos;
2325         gint msgnum;
2326         guint32 uid = 0;
2327         size_t size = 0;
2328         MsgFlags flags = {0, 0}, imap_flags = {0, 0};
2329
2330         g_return_val_if_fail(line_str != NULL, NULL);
2331         g_return_val_if_fail(line_str->str[0] == '*' &&
2332                              line_str->str[1] == ' ', NULL);
2333
2334         MSG_SET_TMP_FLAGS(flags, MSG_IMAP);
2335         if (item->stype == F_QUEUE) {
2336                 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
2337         } else if (item->stype == F_DRAFT) {
2338                 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
2339         }
2340
2341         cur_pos = line_str->str + 2;
2342
2343 #define PARSE_ONE_ELEMENT(ch)                                   \
2344 {                                                               \
2345         cur_pos = strchr_cpy(cur_pos, ch, buf, sizeof(buf));    \
2346         if (cur_pos == NULL) {                                  \
2347                 g_warning("cur_pos == NULL\n");                 \
2348                 procmsg_msginfo_free(msginfo);                  \
2349                 return NULL;                                    \
2350         }                                                       \
2351 }
2352
2353         PARSE_ONE_ELEMENT(' ');
2354         msgnum = atoi(buf);
2355
2356         PARSE_ONE_ELEMENT(' ');
2357         g_return_val_if_fail(!strcmp(buf, "FETCH"), NULL);
2358
2359         g_return_val_if_fail(*cur_pos == '(', NULL);
2360         cur_pos++;
2361
2362         while (*cur_pos != '\0' && *cur_pos != ')') {
2363                 while (*cur_pos == ' ') cur_pos++;
2364
2365                 if (!strncmp(cur_pos, "UID ", 4)) {
2366                         cur_pos += 4;
2367                         uid = strtoul(cur_pos, &cur_pos, 10);
2368                 } else if (!strncmp(cur_pos, "FLAGS ", 6)) {
2369                         cur_pos += 6;
2370                         if (*cur_pos != '(') {
2371                                 g_warning("*cur_pos != '('\n");
2372                                 procmsg_msginfo_free(msginfo);
2373                                 return NULL;
2374                         }
2375                         cur_pos++;
2376                         PARSE_ONE_ELEMENT(')');
2377                         imap_flags = imap_parse_flags(buf);
2378                 } else if (!strncmp(cur_pos, "RFC822.SIZE ", 12)) {
2379                         cur_pos += 12;
2380                         size = strtol(cur_pos, &cur_pos, 10);
2381                 } else if (!strncmp(cur_pos, "BODY[HEADER.FIELDS ", 19)) {
2382                         gchar *headers;
2383
2384                         cur_pos += 19;
2385                         if (*cur_pos != '(') {
2386                                 g_warning("*cur_pos != '('\n");
2387                                 procmsg_msginfo_free(msginfo);
2388                                 return NULL;
2389                         }
2390                         cur_pos++;
2391                         PARSE_ONE_ELEMENT(')');
2392                         if (*cur_pos != ']') {
2393                                 g_warning("*cur_pos != ']'\n");
2394                                 procmsg_msginfo_free(msginfo);
2395                                 return NULL;
2396                         }
2397                         cur_pos++;
2398
2399                         cur_pos = imap_get_header(sock, cur_pos, &headers,
2400                                                   line_str);
2401                         msginfo = procheader_parse_str(headers, flags, FALSE, FALSE);
2402                         g_free(headers);
2403                 } else {
2404                         g_warning("invalid FETCH response: %s\n", cur_pos);
2405                         break;
2406                 }
2407         }
2408
2409         if (msginfo) {
2410                 msginfo->msgnum = uid;
2411                 msginfo->size = size;
2412                 msginfo->flags.tmp_flags |= imap_flags.tmp_flags;
2413                 msginfo->flags.perm_flags = imap_flags.perm_flags;
2414         }
2415
2416         return msginfo;
2417 }
2418
2419 static gchar *imap_get_flag_str(IMAPFlags flags)
2420 {
2421         GString *str;
2422         gchar *ret;
2423
2424         str = g_string_new(NULL);
2425
2426         if (IMAP_IS_SEEN(flags))        g_string_append(str, "\\Seen ");
2427         if (IMAP_IS_ANSWERED(flags))    g_string_append(str, "\\Answered ");
2428         if (IMAP_IS_FLAGGED(flags))     g_string_append(str, "\\Flagged ");
2429         if (IMAP_IS_DELETED(flags))     g_string_append(str, "\\Deleted ");
2430         if (IMAP_IS_DRAFT(flags))       g_string_append(str, "\\Draft");
2431
2432         if (str->len > 0 && str->str[str->len - 1] == ' ')
2433                 g_string_truncate(str, str->len - 1);
2434
2435         ret = str->str;
2436         g_string_free(str, FALSE);
2437
2438         return ret;
2439 }
2440
2441 static gint imap_set_message_flags(IMAPSession *session,
2442                                    MsgNumberList *numlist,
2443                                    IMAPFlags flags,
2444                                    gboolean is_set)
2445 {
2446         gchar *cmd;
2447         gchar *flag_str;
2448         gint ok = 0;
2449         GSList *seq_list, *cur;
2450         IMAPSet imapset;
2451
2452         flag_str = imap_get_flag_str(flags);
2453         cmd = g_strconcat(is_set ? "+FLAGS.SILENT (" : "-FLAGS.SILENT (",
2454                           flag_str, ")", NULL);
2455         g_free(flag_str);
2456
2457         seq_list = imap_get_seq_set_from_numlist(numlist);
2458         for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
2459                 imapset = cur->data;
2460
2461                 ok = imap_cmd_store(session, imapset, cmd);
2462         }
2463         imap_seq_set_free(seq_list);
2464         g_free(cmd);
2465
2466         return ok;
2467 }
2468
2469 static gint imap_select(IMAPSession *session, IMAPFolder *folder,
2470                         const gchar *path,
2471                         gint *exists, gint *recent, gint *unseen,
2472                         guint32 *uid_validity)
2473 {
2474         gchar *real_path;
2475         gint ok;
2476         gint exists_, recent_, unseen_;
2477         guint32 uid_validity_;
2478
2479         if (!exists || !recent || !unseen || !uid_validity) {
2480                 if (session->mbox && strcmp(session->mbox, path) == 0)
2481                         return IMAP_SUCCESS;
2482                 exists = &exists_;
2483                 recent = &recent_;
2484                 unseen = &unseen_;
2485                 uid_validity = &uid_validity_;
2486         }
2487
2488         g_free(session->mbox);
2489         session->mbox = NULL;
2490
2491         real_path = imap_get_real_path(folder, path);
2492         ok = imap_cmd_select(session, real_path,
2493                              exists, recent, unseen, uid_validity);
2494         if (ok != IMAP_SUCCESS)
2495                 log_warning(_("can't select folder: %s\n"), real_path);
2496         else {
2497                 session->mbox = g_strdup(path);
2498                 session->folder_content_changed = FALSE;
2499         }
2500         g_free(real_path);
2501
2502         return ok;
2503 }
2504
2505 #define THROW(err) { ok = err; goto catch; }
2506
2507 static gint imap_status(IMAPSession *session, IMAPFolder *folder,
2508                         const gchar *path,
2509                         gint *messages, gint *recent,
2510                         guint32 *uid_next, guint32 *uid_validity,
2511                         gint *unseen)
2512 {
2513         gchar *real_path;
2514         gchar *real_path_;
2515         gint ok;
2516         GPtrArray *argbuf = NULL;
2517         gchar *str;
2518
2519         if (messages && recent && uid_next && uid_validity && unseen) {
2520                 *messages = *recent = *uid_next = *uid_validity = *unseen = 0;
2521                 argbuf = g_ptr_array_new();
2522         }
2523
2524         real_path = imap_get_real_path(folder, path);
2525         QUOTE_IF_REQUIRED(real_path_, real_path);
2526         imap_gen_send(session, "STATUS %s "
2527                           "(MESSAGES RECENT UIDNEXT UIDVALIDITY UNSEEN)",
2528                           real_path_);
2529
2530         ok = imap_cmd_ok(session, argbuf);
2531         if (ok != IMAP_SUCCESS || !argbuf) THROW(ok);
2532
2533         str = search_array_str(argbuf, "STATUS");
2534         if (!str) THROW(IMAP_ERROR);
2535
2536         str = strchr(str, '(');
2537         if (!str) THROW(IMAP_ERROR);
2538         str++;
2539         while (*str != '\0' && *str != ')') {
2540                 while (*str == ' ') str++;
2541
2542                 if (!strncmp(str, "MESSAGES ", 9)) {
2543                         str += 9;
2544                         *messages = strtol(str, &str, 10);
2545                 } else if (!strncmp(str, "RECENT ", 7)) {
2546                         str += 7;
2547                         *recent = strtol(str, &str, 10);
2548                 } else if (!strncmp(str, "UIDNEXT ", 8)) {
2549                         str += 8;
2550                         *uid_next = strtoul(str, &str, 10);
2551                 } else if (!strncmp(str, "UIDVALIDITY ", 12)) {
2552                         str += 12;
2553                         *uid_validity = strtoul(str, &str, 10);
2554                 } else if (!strncmp(str, "UNSEEN ", 7)) {
2555                         str += 7;
2556                         *unseen = strtol(str, &str, 10);
2557                 } else {
2558                         g_warning("invalid STATUS response: %s\n", str);
2559                         break;
2560                 }
2561         }
2562
2563 catch:
2564         g_free(real_path);
2565         if (argbuf) {
2566                 ptr_array_free_strings(argbuf);
2567                 g_ptr_array_free(argbuf, TRUE);
2568         }
2569
2570         return ok;
2571 }
2572
2573 #undef THROW
2574
2575 static gboolean imap_has_capability(IMAPSession *session, const gchar *cap)
2576 {
2577         gchar **p;
2578         
2579         for (p = session->capability; *p != NULL; ++p) {
2580                 if (!g_ascii_strcasecmp(*p, cap))
2581                         return TRUE;
2582         }
2583
2584         return FALSE;
2585 }
2586
2587 static void imap_free_capabilities(IMAPSession *session)
2588 {
2589         g_strfreev(session->capability);
2590         session->capability = NULL;
2591 }
2592
2593 /* low-level IMAP4rev1 commands */
2594
2595 static gint imap_cmd_authenticate(IMAPSession *session, const gchar *user,
2596                                   const gchar *pass, IMAPAuthType type)
2597 {
2598         gchar *auth_type;
2599         gint ok;
2600         gchar *buf = NULL;
2601         gchar *challenge;
2602         gint challenge_len;
2603         gchar hexdigest[33];
2604         gchar *response;
2605         gchar *response64;
2606
2607         auth_type = "CRAM-MD5";
2608
2609         imap_gen_send(session, "AUTHENTICATE %s", auth_type);
2610         ok = imap_gen_recv(session, &buf);
2611         if (ok != IMAP_SUCCESS || buf[0] != '+' || buf[1] != ' ') {
2612                 g_free(buf);
2613                 return IMAP_ERROR;
2614         }
2615
2616         challenge = g_malloc(strlen(buf + 2) + 1);
2617         challenge_len = base64_decode(challenge, buf + 2, -1);
2618         challenge[challenge_len] = '\0';
2619         g_free(buf);
2620         log_print("IMAP< [Decoded: %s]\n", challenge);
2621
2622         md5_hex_hmac(hexdigest, challenge, challenge_len, pass, strlen(pass));
2623         g_free(challenge);
2624
2625         response = g_strdup_printf("%s %s", user, hexdigest);
2626         log_print("IMAP> [Encoded: %s]\n", response);
2627         response64 = g_malloc((strlen(response) + 3) * 2 + 1);
2628         base64_encode(response64, response, strlen(response));
2629         g_free(response);
2630
2631         log_print("IMAP> %s\n", response64);
2632         sock_puts(SESSION(session)->sock, response64);
2633         ok = imap_cmd_ok(session, NULL);
2634         if (ok != IMAP_SUCCESS)
2635                 log_warning(_("IMAP4 authentication failed.\n"));
2636
2637         return ok;
2638 }
2639
2640 static gint imap_cmd_login(IMAPSession *session,
2641                            const gchar *user, const gchar *pass)
2642 {
2643         gchar *user_, *pass_;
2644         gint ok;
2645
2646         QUOTE_IF_REQUIRED(user_, user);
2647         QUOTE_IF_REQUIRED(pass_, pass);
2648         imap_gen_send(session, "LOGIN %s %s", user_, pass_);
2649
2650         ok = imap_cmd_ok(session, NULL);
2651         if (ok != IMAP_SUCCESS)
2652                 log_warning(_("IMAP4 login failed.\n"));
2653
2654         return ok;
2655 }
2656
2657 static gint imap_cmd_logout(IMAPSession *session)
2658 {
2659         imap_gen_send(session, "LOGOUT");
2660         return imap_cmd_ok(session, NULL);
2661 }
2662
2663 static gint imap_cmd_noop(IMAPSession *session)
2664 {
2665         imap_gen_send(session, "NOOP");
2666         return imap_cmd_ok(session, NULL);
2667 }
2668
2669 #if USE_OPENSSL
2670 static gint imap_cmd_starttls(IMAPSession *session)
2671 {
2672         imap_gen_send(session, "STARTTLS");
2673         return imap_cmd_ok(session, NULL);
2674 }
2675 #endif
2676
2677 #define THROW(err) { ok = err; goto catch; }
2678
2679 static gint imap_cmd_namespace(IMAPSession *session, gchar **ns_str)
2680 {
2681         gint ok;
2682         GPtrArray *argbuf;
2683         gchar *str;
2684
2685         argbuf = g_ptr_array_new();
2686
2687         imap_gen_send(session, "NAMESPACE");
2688         if ((ok = imap_cmd_ok(session, argbuf)) != IMAP_SUCCESS) THROW(ok);
2689
2690         str = search_array_str(argbuf, "NAMESPACE");
2691         if (!str) THROW(IMAP_ERROR);
2692
2693         *ns_str = g_strdup(str);
2694
2695 catch:
2696         ptr_array_free_strings(argbuf);
2697         g_ptr_array_free(argbuf, TRUE);
2698
2699         return ok;
2700 }
2701
2702 #undef THROW
2703
2704 static gint imap_cmd_list(IMAPSession *session, const gchar *ref,
2705                           const gchar *mailbox, GPtrArray *argbuf)
2706 {
2707         gchar *ref_, *mailbox_;
2708
2709         if (!ref) ref = "\"\"";
2710         if (!mailbox) mailbox = "\"\"";
2711
2712         QUOTE_IF_REQUIRED(ref_, ref);
2713         QUOTE_IF_REQUIRED(mailbox_, mailbox);
2714         imap_gen_send(session, "LIST %s %s", ref_, mailbox_);
2715
2716         return imap_cmd_ok(session, argbuf);
2717 }
2718
2719 #define THROW goto catch
2720
2721 static gint imap_cmd_do_select(IMAPSession *session, const gchar *folder,
2722                                gboolean examine,
2723                                gint *exists, gint *recent, gint *unseen,
2724                                guint32 *uid_validity)
2725 {
2726         gint ok;
2727         gchar *resp_str;
2728         GPtrArray *argbuf;
2729         gchar *select_cmd;
2730         gchar *folder_;
2731         unsigned int uid_validity_;
2732
2733         *exists = *recent = *unseen = *uid_validity = 0;
2734         argbuf = g_ptr_array_new();
2735
2736         if (examine)
2737                 select_cmd = "EXAMINE";
2738         else
2739                 select_cmd = "SELECT";
2740
2741         QUOTE_IF_REQUIRED(folder_, folder);
2742         imap_gen_send(session, "%s %s", select_cmd, folder_);
2743
2744         if ((ok = imap_cmd_ok(session, argbuf)) != IMAP_SUCCESS) THROW;
2745
2746         resp_str = search_array_contain_str(argbuf, "EXISTS");
2747         if (resp_str) {
2748                 if (sscanf(resp_str,"%d EXISTS", exists) != 1) {
2749                         g_warning("imap_cmd_select(): invalid EXISTS line.\n");
2750                         THROW;
2751                 }
2752         }
2753
2754         resp_str = search_array_contain_str(argbuf, "RECENT");
2755         if (resp_str) {
2756                 if (sscanf(resp_str, "%d RECENT", recent) != 1) {
2757                         g_warning("imap_cmd_select(): invalid RECENT line.\n");
2758                         THROW;
2759                 }
2760         }
2761
2762         resp_str = search_array_contain_str(argbuf, "UIDVALIDITY");
2763         if (resp_str) {
2764                 if (sscanf(resp_str, "OK [UIDVALIDITY %u] ", &uid_validity_)
2765                     != 1) {
2766                         g_warning("imap_cmd_select(): invalid UIDVALIDITY line.\n");
2767                         THROW;
2768                 }
2769                 *uid_validity = uid_validity_;
2770         }
2771
2772         resp_str = search_array_contain_str(argbuf, "UNSEEN");
2773         if (resp_str) {
2774                 if (sscanf(resp_str, "OK [UNSEEN %d] ", unseen) != 1) {
2775                         g_warning("imap_cmd_select(): invalid UNSEEN line.\n");
2776                         THROW;
2777                 }
2778         }
2779
2780 catch:
2781         ptr_array_free_strings(argbuf);
2782         g_ptr_array_free(argbuf, TRUE);
2783
2784         return ok;
2785 }
2786
2787 static gint imap_cmd_select(IMAPSession *session, const gchar *folder,
2788                             gint *exists, gint *recent, gint *unseen,
2789                             guint32 *uid_validity)
2790 {
2791         return imap_cmd_do_select(session, folder, FALSE,
2792                                   exists, recent, unseen, uid_validity);
2793 }
2794
2795 static gint imap_cmd_examine(IMAPSession *session, const gchar *folder,
2796                              gint *exists, gint *recent, gint *unseen,
2797                              guint32 *uid_validity)
2798 {
2799         return imap_cmd_do_select(session, folder, TRUE,
2800                                   exists, recent, unseen, uid_validity);
2801 }
2802
2803 #undef THROW
2804
2805 static gint imap_cmd_create(IMAPSession *session, const gchar *folder)
2806 {
2807         gchar *folder_;
2808
2809         QUOTE_IF_REQUIRED(folder_, folder);
2810         imap_gen_send(session, "CREATE %s", folder_);
2811
2812         return imap_cmd_ok(session, NULL);
2813 }
2814
2815 static gint imap_cmd_rename(IMAPSession *session, const gchar *old_folder,
2816                             const gchar *new_folder)
2817 {
2818         gchar *old_folder_, *new_folder_;
2819
2820         QUOTE_IF_REQUIRED(old_folder_, old_folder);
2821         QUOTE_IF_REQUIRED(new_folder_, new_folder);
2822         imap_gen_send(session, "RENAME %s %s", old_folder_, new_folder_);
2823
2824         return imap_cmd_ok(session, NULL);
2825 }
2826
2827 static gint imap_cmd_delete(IMAPSession *session, const gchar *folder)
2828 {
2829         gchar *folder_;
2830
2831         QUOTE_IF_REQUIRED(folder_, folder);
2832         imap_gen_send(session, "DELETE %s", folder_);
2833
2834         return imap_cmd_ok(session, NULL);
2835 }
2836
2837 static gint imap_cmd_search(IMAPSession *session, const gchar *criteria, 
2838                             GSList **list)
2839 {
2840         gint ok;
2841         gchar *uidlist;
2842         GPtrArray *argbuf;
2843
2844         g_return_val_if_fail(criteria != NULL, IMAP_ERROR);
2845         g_return_val_if_fail(list != NULL, IMAP_ERROR);
2846
2847         *list = NULL;
2848         
2849         argbuf = g_ptr_array_new();
2850         imap_gen_send(session, "UID SEARCH %s", criteria);
2851
2852         ok = imap_cmd_ok(session, argbuf);
2853         if (ok != IMAP_SUCCESS) {
2854                 ptr_array_free_strings(argbuf);
2855                 g_ptr_array_free(argbuf, TRUE);
2856                 return ok;
2857         }
2858
2859         if ((uidlist = search_array_str(argbuf, "SEARCH ")) != NULL) {
2860                 gchar **strlist, **p;
2861
2862                 strlist = g_strsplit(uidlist + 7, " ", 0);
2863                 for (p = strlist; *p != NULL; ++p) {
2864                         guint msgnum;
2865
2866                         if (sscanf(*p, "%u", &msgnum) == 1)
2867                                 *list = g_slist_append(*list, GINT_TO_POINTER(msgnum));
2868                 }
2869                 g_strfreev(strlist);
2870         }
2871         ptr_array_free_strings(argbuf);
2872         g_ptr_array_free(argbuf, TRUE);
2873
2874         return IMAP_SUCCESS;
2875 }
2876
2877 static gint imap_cmd_fetch(IMAPSession *session, guint32 uid, 
2878                            const gchar *filename)
2879 {
2880         gint ok;
2881         gchar *buf = NULL;
2882         gchar *cur_pos;
2883         gchar size_str[32];
2884         glong size_num;
2885
2886         g_return_val_if_fail(filename != NULL, IMAP_ERROR);
2887
2888         imap_gen_send(session, "UID FETCH %d BODY.PEEK[]", uid);
2889
2890         while ((ok = imap_gen_recv(session, &buf)) == IMAP_SUCCESS) {
2891                 if (buf[0] != '*' || buf[1] != ' ') {
2892                         g_free(buf);
2893                         return IMAP_ERROR;
2894                 }
2895                 if (strstr(buf, "FETCH") != NULL) break;
2896                 g_free(buf);
2897         }
2898         if (ok != IMAP_SUCCESS) {
2899                 g_free(buf);
2900                 return ok;
2901         }
2902
2903 #define RETURN_ERROR_IF_FAIL(cond)      \
2904         if (!(cond)) {                  \
2905                 g_free(buf);            \
2906                 return IMAP_ERROR;      \
2907         }
2908
2909         cur_pos = strchr(buf, '{');
2910         RETURN_ERROR_IF_FAIL(cur_pos != NULL);
2911         cur_pos = strchr_cpy(cur_pos + 1, '}', size_str, sizeof(size_str));
2912         RETURN_ERROR_IF_FAIL(cur_pos != NULL);
2913         size_num = atol(size_str);
2914         RETURN_ERROR_IF_FAIL(size_num >= 0);
2915
2916         RETURN_ERROR_IF_FAIL(*cur_pos == '\0');
2917
2918 #undef RETURN_ERROR_IF_FAIL
2919
2920         g_free(buf);
2921
2922         if (recv_bytes_write_to_file(SESSION(session)->sock,
2923                                      size_num, filename) != 0)
2924                 return IMAP_ERROR;
2925
2926         if (imap_gen_recv(session, &buf) != IMAP_SUCCESS) {
2927                 g_free(buf);
2928                 return IMAP_ERROR;
2929         }
2930
2931         if (buf[0] == '\0' || buf[strlen(buf) - 1] != ')') {
2932                 g_free(buf);
2933                 return IMAP_ERROR;
2934         }
2935         g_free(buf);
2936
2937         ok = imap_cmd_ok(session, NULL);
2938
2939         return ok;
2940 }
2941
2942 static gint imap_cmd_append(IMAPSession *session, const gchar *destfolder,
2943                             const gchar *file, IMAPFlags flags, 
2944                             guint32 *new_uid)
2945 {
2946         gint ok;
2947         gint size;
2948         gchar *destfolder_;
2949         gchar *flag_str;
2950         unsigned int new_uid_;
2951         gchar *ret = NULL;
2952         gchar buf[BUFFSIZE];
2953         FILE *fp;
2954         GPtrArray *argbuf;
2955         gchar *resp_str;
2956
2957         g_return_val_if_fail(file != NULL, IMAP_ERROR);
2958
2959         size = get_file_size_as_crlf(file);
2960         if ((fp = fopen(file, "rb")) == NULL) {
2961                 FILE_OP_ERROR(file, "fopen");
2962                 return -1;
2963         }
2964         QUOTE_IF_REQUIRED(destfolder_, destfolder);
2965         flag_str = imap_get_flag_str(flags);
2966         imap_gen_send(session, "APPEND %s (%s) {%d}", 
2967                       destfolder_, flag_str, size);
2968         g_free(flag_str);
2969
2970         ok = imap_gen_recv(session, &ret);
2971         if (ok != IMAP_SUCCESS || ret[0] != '+' || ret[1] != ' ') {
2972                 log_warning(_("can't append %s to %s\n"), file, destfolder_);
2973                 g_free(ret);
2974                 fclose(fp);
2975                 return IMAP_ERROR;
2976         }
2977         g_free(ret);
2978
2979         log_print("IMAP4> %s\n", "(sending file...)");
2980
2981         while (fgets(buf, sizeof(buf), fp) != NULL) {
2982                 strretchomp(buf);
2983                 if (sock_puts(SESSION(session)->sock, buf) < 0) {
2984                         fclose(fp);
2985                         return -1;
2986                 }
2987         }
2988
2989         if (ferror(fp)) {
2990                 FILE_OP_ERROR(file, "fgets");
2991                 fclose(fp);
2992                 return -1;
2993         }
2994
2995         sock_puts(SESSION(session)->sock, "");
2996
2997         fclose(fp);
2998
2999         if (new_uid != NULL)
3000                 *new_uid = 0;
3001
3002         if (new_uid != NULL && session->uidplus) {
3003                 argbuf = g_ptr_array_new();
3004
3005                 ok = imap_cmd_ok(session, argbuf);
3006                 if ((ok == IMAP_SUCCESS) && (argbuf->len > 0)) {
3007                         resp_str = g_ptr_array_index(argbuf, argbuf->len - 1);
3008                         if (resp_str &&
3009                             sscanf(resp_str, "%*u OK [APPENDUID %*u %u]",
3010                                    &new_uid_) == 1) {
3011                                 *new_uid = new_uid_;
3012                         }
3013                 }
3014
3015                 ptr_array_free_strings(argbuf);
3016                 g_ptr_array_free(argbuf, TRUE);
3017         } else
3018                 ok = imap_cmd_ok(session, NULL);
3019
3020         if (ok != IMAP_SUCCESS)
3021                 log_warning(_("can't append message to %s\n"),
3022                             destfolder_);
3023
3024         return ok;
3025 }
3026
3027 static MsgNumberList *imapset_to_numlist(IMAPSet imapset)
3028 {
3029         gchar **ranges, **range;
3030         unsigned int low, high;
3031         MsgNumberList *uids = NULL;
3032         
3033         ranges = g_strsplit(imapset, ",", 0);
3034         for (range = ranges; *range != NULL; range++) {
3035                 printf("%s\n", *range);
3036                 if (sscanf(*range, "%u:%u", &low, &high) == 1)
3037                         uids = g_slist_prepend(uids, GINT_TO_POINTER(low));
3038                 else {
3039                         int i;
3040                         for (i = low; i <= high; i++)
3041                                 uids = g_slist_prepend(uids, GINT_TO_POINTER(i));
3042                 }
3043         }
3044         uids = g_slist_reverse(uids);
3045         g_strfreev(ranges);
3046
3047         return uids;
3048 }
3049
3050 static gint imap_cmd_copy(IMAPSession *session, const gchar *seq_set,
3051                           const gchar *destfolder, GRelation *uid_mapping)
3052 {
3053         gint ok;
3054         gchar *destfolder_;
3055         
3056         g_return_val_if_fail(session != NULL, IMAP_ERROR);
3057         g_return_val_if_fail(seq_set != NULL, IMAP_ERROR);
3058         g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
3059
3060         QUOTE_IF_REQUIRED(destfolder_, destfolder);
3061         imap_gen_send(session, "UID COPY %s %s", seq_set, destfolder_);
3062
3063         if (uid_mapping != NULL && session->uidplus) {
3064                 GPtrArray *reply;               
3065                 gchar *resp_str = NULL, *olduids_str, *newuids_str;
3066                 MsgNumberList *olduids, *old_cur, *newuids, *new_cur;
3067
3068                 reply = g_ptr_array_new();
3069                 ok = imap_cmd_ok(session, reply);
3070                 if ((ok == IMAP_SUCCESS) && (reply->len > 0)) {
3071                         resp_str = g_ptr_array_index(reply, reply->len - 1);
3072                         if (resp_str) {
3073                                 olduids_str = g_new0(gchar, strlen(resp_str));
3074                                 newuids_str = g_new0(gchar, strlen(resp_str));
3075                                 if (sscanf(resp_str, "%*s OK [COPYUID %*u %[0-9,:] %[0-9,:]]",
3076                                            olduids_str, newuids_str) == 2) {
3077                                         olduids = imapset_to_numlist(olduids_str);
3078                                         newuids = imapset_to_numlist(newuids_str);
3079
3080                                         old_cur = olduids;
3081                                         new_cur = newuids;
3082                                         while(old_cur != NULL && new_cur != NULL) {
3083                                                 g_relation_insert(uid_mapping, 
3084                                                                   GPOINTER_TO_INT(old_cur->data),
3085                                                                   GPOINTER_TO_INT(new_cur->data));
3086                                                 old_cur = g_slist_next(old_cur);
3087                                                 new_cur = g_slist_next(new_cur);
3088                                         }
3089
3090                                         g_slist_free(olduids);
3091                                         g_slist_free(newuids);
3092                                 }
3093                                 g_free(olduids_str);
3094                                 g_free(newuids_str);
3095                         }
3096                 }
3097                 ptr_array_free_strings(reply);
3098                 g_ptr_array_free(reply, TRUE);
3099         } else
3100                 ok = imap_cmd_ok(session, NULL);
3101
3102         if (ok != IMAP_SUCCESS)
3103                 log_warning(_("can't copy %s to %s\n"), seq_set, destfolder_);
3104
3105         return ok;
3106 }
3107
3108 gint imap_cmd_envelope(IMAPSession *session, IMAPSet set)
3109 {
3110         static GString *header_fields = NULL;
3111
3112         if (header_fields == NULL) {
3113                 const HeaderEntry *headers, *elem;
3114
3115                 headers = procheader_get_headernames(FALSE);
3116                 header_fields = g_string_new("");
3117
3118                 for (elem = headers; elem->name != NULL; ++elem) {
3119                         gint namelen = strlen(elem->name);
3120
3121                         /* Header fields ending with space are not rfc822 headers */
3122                         if (elem->name[namelen - 1] == ' ')
3123                                 continue;
3124
3125                         /* strip : at the of header field */
3126                         if(elem->name[namelen - 1] == ':')
3127                                 namelen--;
3128                         
3129                         if (namelen <= 0)
3130                                 continue;
3131
3132                         g_string_append_printf(header_fields, "%s%.*s",
3133                                         header_fields->str[0] != '\0' ? " " : "",
3134                                         namelen, elem->name);
3135                 }
3136         }
3137
3138         imap_gen_send
3139                 (session, "UID FETCH %s (UID FLAGS RFC822.SIZE BODY.PEEK[HEADER.FIELDS (%s)])",
3140                  set, header_fields->str);
3141
3142         return IMAP_SUCCESS;
3143 }
3144
3145 static gint imap_cmd_store(IMAPSession *session, IMAPSet seq_set,
3146                            gchar *sub_cmd)
3147 {
3148         gint ok;
3149
3150         imap_gen_send(session, "UID STORE %s %s", seq_set, sub_cmd);
3151
3152         if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS) {
3153                 log_warning(_("error while imap command: STORE %s %s\n"),
3154                             seq_set, sub_cmd);
3155                 return ok;
3156         }
3157
3158         return IMAP_SUCCESS;
3159 }
3160
3161 static gint imap_cmd_expunge(IMAPSession *session, IMAPSet seq_set)
3162 {
3163         gint ok;
3164
3165         if (seq_set && session->uidplus)
3166                 imap_gen_send(session, "UID EXPUNGE %s", seq_set);
3167         else    
3168                 imap_gen_send(session, "EXPUNGE");
3169         if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS) {
3170                 log_warning(_("error while imap command: EXPUNGE\n"));
3171                 return ok;
3172         }
3173
3174         return IMAP_SUCCESS;
3175 }
3176
3177 static gint imap_cmd_close(IMAPSession *session)
3178 {
3179         gint ok;
3180
3181         imap_gen_send(session, "CLOSE");
3182         if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS)
3183                 log_warning(_("error while imap command: CLOSE\n"));
3184
3185         return ok;
3186 }
3187
3188 static gint imap_cmd_ok(IMAPSession *session, GPtrArray *argbuf)
3189 {
3190         gint ok = IMAP_SUCCESS;
3191         gchar *buf;
3192         gint cmd_num;
3193         gchar *data;
3194
3195         while ((ok = imap_gen_recv(session, &buf))
3196                == IMAP_SUCCESS) {
3197                 /* make sure data is long enough for any substring of buf */
3198                 data = alloca(strlen(buf) + 1);
3199
3200                 /* untagged line read */
3201                 if (buf[0] == '*' && buf[1] == ' ') {
3202                         gint num;
3203                         if (argbuf)
3204                                 g_ptr_array_add(argbuf, g_strdup(buf + 2));
3205
3206                         if (sscanf(buf + 2, "%d %s", &num, data) >= 2) {
3207                                 if (!strcmp(data, "EXISTS")) {
3208                                         session->exists = num;
3209                                         session->folder_content_changed = TRUE;
3210                                 }
3211
3212                                 if(!strcmp(data, "EXPUNGE")) {
3213                                         session->exists--;
3214                                         session->folder_content_changed = TRUE;
3215                                 }
3216                         }
3217                 /* tagged line with correct tag and OK response found */
3218                 } else if ((sscanf(buf, "%d %s", &cmd_num, data) >= 2) &&
3219                            (cmd_num == session->cmd_count) &&
3220                            !strcmp(data, "OK")) {
3221                         if (argbuf)
3222                                 g_ptr_array_add(argbuf, g_strdup(buf));
3223                         break;
3224                 /* everything else */
3225                 } else {
3226                         ok = IMAP_ERROR;
3227                         break;
3228                 }
3229                 g_free(buf);
3230         }
3231         g_free(buf);
3232
3233         return ok;
3234 }
3235
3236 static void imap_gen_send(IMAPSession *session, const gchar *format, ...)
3237 {
3238         gchar *buf;
3239         gchar *tmp;
3240         gchar *p;
3241         va_list args;
3242
3243         va_start(args, format);
3244         tmp = g_strdup_vprintf(format, args);
3245         va_end(args);
3246
3247         session->cmd_count++;
3248
3249         buf = g_strdup_printf("%d %s\r\n", session->cmd_count, tmp);
3250         if (!g_ascii_strncasecmp(tmp, "LOGIN ", 6) && (p = strchr(tmp + 6, ' '))) {
3251                 *p = '\0';
3252                 log_print("IMAP4> %d %s ********\n", session->cmd_count, tmp);
3253         } else
3254                 log_print("IMAP4> %d %s\n", session->cmd_count, tmp);
3255
3256         sock_write_all(SESSION(session)->sock, buf, strlen(buf));
3257         g_free(tmp);
3258         g_free(buf);
3259 }
3260
3261 static gint imap_gen_recv(IMAPSession *session, gchar **ret)
3262 {
3263         if ((*ret = sock_getline(SESSION(session)->sock)) == NULL)
3264                 return IMAP_SOCKET;
3265
3266         strretchomp(*ret);
3267
3268         log_print("IMAP4< %s\n", *ret);
3269         
3270         session_set_access_time(SESSION(session));
3271
3272         return IMAP_SUCCESS;
3273 }
3274
3275
3276 /* misc utility functions */
3277
3278 static gchar *strchr_cpy(const gchar *src, gchar ch, gchar *dest, gint len)
3279 {
3280         gchar *tmp;
3281
3282         dest[0] = '\0';
3283         tmp = strchr(src, ch);
3284         if (!tmp)
3285                 return NULL;
3286
3287         memcpy(dest, src, MIN(tmp - src, len - 1));
3288         dest[MIN(tmp - src, len - 1)] = '\0';
3289
3290         return tmp + 1;
3291 }
3292
3293 static gchar *get_quoted(const gchar *src, gchar ch, gchar *dest, gint len)
3294 {
3295         const gchar *p = src;
3296         gint n = 0;
3297
3298         g_return_val_if_fail(*p == ch, NULL);
3299
3300         *dest = '\0';
3301         p++;
3302
3303         while (*p != '\0' && *p != ch) {
3304                 if (n < len - 1) {
3305                         if (*p == '\\' && *(p + 1) != '\0')
3306                                 p++;
3307                         *dest++ = *p++;
3308                 } else
3309                         p++;
3310                 n++;
3311         }
3312
3313         *dest = '\0';
3314         return (gchar *)(*p == ch ? p + 1 : p);
3315 }
3316
3317 static gchar *search_array_contain_str(GPtrArray *array, const gchar *str)
3318 {
3319         gint i;
3320
3321         for (i = 0; i < array->len; i++) {
3322                 gchar *tmp;
3323
3324                 tmp = g_ptr_array_index(array, i);
3325                 if (strstr(tmp, str) != NULL)
3326                         return tmp;
3327         }
3328
3329         return NULL;
3330 }
3331
3332 static gchar *search_array_str(GPtrArray *array, const gchar *str)
3333 {
3334         gint i;
3335         gint len;
3336
3337         len = strlen(str);
3338
3339         for (i = 0; i < array->len; i++) {
3340                 gchar *tmp;
3341
3342                 tmp = g_ptr_array_index(array, i);
3343                 if (!strncmp(tmp, str, len))
3344                         return tmp;
3345         }
3346
3347         return NULL;
3348 }
3349
3350 static void imap_path_separator_subst(gchar *str, gchar separator)
3351 {
3352         gchar *p;
3353         gboolean in_escape = FALSE;
3354
3355         if (!separator || separator == '/') return;
3356
3357         for (p = str; *p != '\0'; p++) {
3358                 if (*p == '/' && !in_escape)
3359                         *p = separator;
3360                 else if (*p == '&' && *(p + 1) != '-' && !in_escape)
3361                         in_escape = TRUE;
3362                 else if (*p == '-' && in_escape)
3363                         in_escape = FALSE;
3364         }
3365 }
3366
3367 static gchar *imap_modified_utf7_to_utf8(const gchar *mutf7_str)
3368 {
3369         static iconv_t cd = (iconv_t)-1;
3370         static gboolean iconv_ok = TRUE;
3371         GString *norm_utf7;
3372         gchar *norm_utf7_p;
3373         size_t norm_utf7_len;
3374         const gchar *p;
3375         gchar *to_str, *to_p;
3376         size_t to_len;
3377         gboolean in_escape = FALSE;
3378
3379         if (!iconv_ok) return g_strdup(mutf7_str);
3380
3381         if (cd == (iconv_t)-1) {
3382                 cd = iconv_open(CS_INTERNAL, CS_UTF_7);
3383                 if (cd == (iconv_t)-1) {
3384                         g_warning("iconv cannot convert UTF-7 to %s\n",
3385                                   CS_INTERNAL);
3386                         iconv_ok = FALSE;
3387                         return g_strdup(mutf7_str);
3388                 }
3389         }
3390
3391         /* modified UTF-7 to normal UTF-7 conversion */
3392         norm_utf7 = g_string_new(NULL);
3393
3394         for (p = mutf7_str; *p != '\0'; p++) {
3395                 /* replace: '&'  -> '+',
3396                             "&-" -> '&',
3397                             escaped ','  -> '/' */
3398                 if (!in_escape && *p == '&') {
3399                         if (*(p + 1) != '-') {
3400                                 g_string_append_c(norm_utf7, '+');
3401                                 in_escape = TRUE;
3402                         } else {
3403                                 g_string_append_c(norm_utf7, '&');
3404                                 p++;
3405                         }
3406                 } else if (in_escape && *p == ',') {
3407                         g_string_append_c(norm_utf7, '/');
3408                 } else if (in_escape && *p == '-') {
3409                         g_string_append_c(norm_utf7, '-');
3410                         in_escape = FALSE;
3411                 } else {
3412                         g_string_append_c(norm_utf7, *p);
3413                 }
3414         }
3415
3416         norm_utf7_p = norm_utf7->str;
3417         norm_utf7_len = norm_utf7->len;
3418         to_len = strlen(mutf7_str) * 5;
3419         to_p = to_str = g_malloc(to_len + 1);
3420
3421         if (iconv(cd, (ICONV_CONST gchar **)&norm_utf7_p, &norm_utf7_len,
3422                   &to_p, &to_len) == -1) {
3423                 g_warning(_("iconv cannot convert UTF-7 to %s\n"),
3424                           conv_get_locale_charset_str());
3425                 g_string_free(norm_utf7, TRUE);
3426                 g_free(to_str);
3427                 return g_strdup(mutf7_str);
3428         }
3429
3430         /* second iconv() call for flushing */
3431         iconv(cd, NULL, NULL, &to_p, &to_len);
3432         g_string_free(norm_utf7, TRUE);
3433         *to_p = '\0';
3434
3435         return to_str;
3436 }
3437
3438 static gchar *imap_utf8_to_modified_utf7(const gchar *from)
3439 {
3440         static iconv_t cd = (iconv_t)-1;
3441         static gboolean iconv_ok = TRUE;
3442         gchar *norm_utf7, *norm_utf7_p;
3443         size_t from_len, norm_utf7_len;
3444         GString *to_str;
3445         gchar *from_tmp, *to, *p;
3446         gboolean in_escape = FALSE;
3447
3448         if (!iconv_ok) return g_strdup(from);
3449
3450         if (cd == (iconv_t)-1) {
3451                 cd = iconv_open(CS_UTF_7, CS_INTERNAL);
3452                 if (cd == (iconv_t)-1) {
3453                         g_warning(_("iconv cannot convert %s to UTF-7\n"),
3454                                   CS_INTERNAL);
3455                         iconv_ok = FALSE;
3456                         return g_strdup(from);
3457                 }
3458         }
3459
3460         /* UTF-8 to normal UTF-7 conversion */
3461         Xstrdup_a(from_tmp, from, return g_strdup(from));
3462         from_len = strlen(from);
3463         norm_utf7_len = from_len * 5;
3464         Xalloca(norm_utf7, norm_utf7_len + 1, return g_strdup(from));
3465         norm_utf7_p = norm_utf7;
3466
3467 #define IS_PRINT(ch) (isprint(ch) && IS_ASCII(ch))
3468
3469         while (from_len > 0) {
3470                 if (*from_tmp == '+') {
3471                         *norm_utf7_p++ = '+';
3472                         *norm_utf7_p++ = '-';
3473                         norm_utf7_len -= 2;
3474                         from_tmp++;
3475                         from_len--;
3476                 } else if (IS_PRINT(*(guchar *)from_tmp)) {
3477                         /* printable ascii char */
3478                         *norm_utf7_p = *from_tmp;
3479                         norm_utf7_p++;
3480                         norm_utf7_len--;
3481                         from_tmp++;
3482                         from_len--;
3483                 } else {
3484                         size_t conv_len = 0;
3485
3486                         /* unprintable char: convert to UTF-7 */
3487                         p = from_tmp;
3488                         while (!IS_PRINT(*(guchar *)p) && conv_len < from_len) {
3489                                 conv_len += g_utf8_skip[*(guchar *)p];
3490                                 p += g_utf8_skip[*(guchar *)p];
3491                         }
3492
3493                         from_len -= conv_len;
3494                         if (iconv(cd, (ICONV_CONST gchar **)&from_tmp,
3495                                   &conv_len,
3496                                   &norm_utf7_p, &norm_utf7_len) == -1) {
3497                                 g_warning(_("iconv cannot convert UTF-8 to UTF-7\n"));
3498                                 return g_strdup(from);
3499                         }
3500
3501                         /* second iconv() call for flushing */
3502                         iconv(cd, NULL, NULL, &norm_utf7_p, &norm_utf7_len);
3503                 }
3504         }
3505
3506 #undef IS_PRINT
3507
3508         *norm_utf7_p = '\0';
3509         to_str = g_string_new(NULL);
3510         for (p = norm_utf7; p < norm_utf7_p; p++) {
3511                 /* replace: '&' -> "&-",
3512                             '+' -> '&',
3513                             "+-" -> '+',
3514                             BASE64 '/' -> ',' */
3515                 if (!in_escape && *p == '&') {
3516                         g_string_append(to_str, "&-");
3517                 } else if (!in_escape && *p == '+') {
3518                         if (*(p + 1) == '-') {
3519                                 g_string_append_c(to_str, '+');
3520                                 p++;
3521                         } else {
3522                                 g_string_append_c(to_str, '&');
3523                                 in_escape = TRUE;
3524                         }
3525                 } else if (in_escape && *p == '/') {
3526                         g_string_append_c(to_str, ',');
3527                 } else if (in_escape && *p == '-') {
3528                         g_string_append_c(to_str, '-');
3529                         in_escape = FALSE;
3530                 } else {
3531                         g_string_append_c(to_str, *p);
3532                 }
3533         }
3534
3535         if (in_escape) {
3536                 in_escape = FALSE;
3537                 g_string_append_c(to_str, '-');
3538         }
3539
3540         to = to_str->str;
3541         g_string_free(to_str, FALSE);
3542
3543         return to;
3544 }
3545
3546 static GSList *imap_get_seq_set_from_numlist(MsgNumberList *numlist)
3547 {
3548         GString *str;
3549         GSList *sorted_list, *cur;
3550         guint first, last, next;
3551         gchar *ret_str;
3552         GSList *ret_list = NULL;
3553
3554         if (numlist == NULL)
3555                 return NULL;
3556
3557         str = g_string_sized_new(256);
3558
3559         sorted_list = g_slist_copy(numlist);
3560         sorted_list = g_slist_sort(sorted_list, g_int_compare);
3561
3562         first = GPOINTER_TO_INT(sorted_list->data);
3563
3564         for (cur = sorted_list; cur != NULL; cur = g_slist_next(cur)) {
3565                 last = GPOINTER_TO_INT(cur->data);
3566                 if (cur->next)
3567                         next = GPOINTER_TO_INT(cur->next->data);
3568                 else
3569                         next = 0;
3570
3571                 if (last + 1 != next || next == 0) {
3572                         if (str->len > 0)
3573                                 g_string_append_c(str, ',');
3574                         if (first == last)
3575                                 g_string_append_printf(str, "%u", first);
3576                         else
3577                                 g_string_append_printf(str, "%u:%u", first, last);
3578
3579                         first = next;
3580
3581                         if (str->len > IMAP_CMD_LIMIT) {
3582                                 ret_str = g_strdup(str->str);
3583                                 ret_list = g_slist_append(ret_list, ret_str);
3584                                 g_string_truncate(str, 0);
3585                         }
3586                 }
3587         }
3588
3589         if (str->len > 0) {
3590                 ret_str = g_strdup(str->str);
3591                 ret_list = g_slist_append(ret_list, ret_str);
3592         }
3593
3594         g_slist_free(sorted_list);
3595         g_string_free(str, TRUE);
3596
3597         return ret_list;
3598 }
3599
3600 static GSList *imap_get_seq_set_from_msglist(MsgInfoList *msglist)
3601 {
3602         MsgNumberList *numlist = NULL;
3603         MsgInfoList *cur;
3604         GSList *seq_list;
3605
3606         for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
3607                 MsgInfo *msginfo = (MsgInfo *) cur->data;
3608
3609                 numlist = g_slist_append(numlist, GINT_TO_POINTER(msginfo->msgnum));
3610         }
3611         seq_list = imap_get_seq_set_from_numlist(numlist);
3612         g_slist_free(numlist);
3613
3614         return seq_list;
3615 }
3616
3617 static void imap_seq_set_free(GSList *seq_list)
3618 {
3619         slist_free_strings(seq_list);
3620         g_slist_free(seq_list);
3621 }
3622
3623
3624 static gboolean imap_rename_folder_func(GNode *node, gpointer data)
3625 {
3626         FolderItem *item = node->data;
3627         gchar **paths = data;
3628         const gchar *oldpath = paths[0];
3629         const gchar *newpath = paths[1];
3630         gchar *base;
3631         gchar *new_itempath;
3632         gint oldpathlen;
3633
3634         oldpathlen = strlen(oldpath);
3635         if (strncmp(oldpath, item->path, oldpathlen) != 0) {
3636                 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
3637                 return TRUE;
3638         }
3639
3640         base = item->path + oldpathlen;
3641         while (*base == G_DIR_SEPARATOR) base++;
3642         if (*base == '\0')
3643                 new_itempath = g_strdup(newpath);
3644         else
3645                 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
3646                                            NULL);
3647         g_free(item->path);
3648         item->path = new_itempath;
3649
3650         return FALSE;
3651 }
3652
3653 static gint get_list_of_uids(Folder *folder, IMAPFolderItem *item, GSList **msgnum_list)
3654 {
3655         gint ok, nummsgs = 0, lastuid_old;
3656         IMAPSession *session;
3657         GSList *uidlist, *elem;
3658         gchar *cmd_buf;
3659
3660         session = imap_session_get(folder);
3661         g_return_val_if_fail(session != NULL, -1);
3662
3663         ok = imap_select(session, IMAP_FOLDER(folder), item->item.path,
3664                          NULL, NULL, NULL, NULL);
3665         if (ok != IMAP_SUCCESS)
3666                 return -1;
3667
3668         cmd_buf = g_strdup_printf("UID %d:*", item->lastuid + 1);
3669         ok = imap_cmd_search(session, cmd_buf, &uidlist);
3670         g_free(cmd_buf);
3671
3672         if (ok == IMAP_SOCKET) {
3673                 session_destroy((Session *)session);
3674                 ((RemoteFolder *)folder)->session = NULL;
3675                 return -1;
3676         }
3677
3678         if (ok != IMAP_SUCCESS) {
3679                 gint i;
3680                 GPtrArray *argbuf;
3681
3682                 argbuf = g_ptr_array_new();
3683
3684                 cmd_buf = g_strdup_printf("UID FETCH %d:* (UID)", item->lastuid + 1);
3685                 imap_gen_send(session, cmd_buf);
3686                 g_free(cmd_buf);
3687                 ok = imap_cmd_ok(session, argbuf);
3688                 if (ok != IMAP_SUCCESS) {
3689                         ptr_array_free_strings(argbuf);
3690                         g_ptr_array_free(argbuf, TRUE);
3691                         return -1;
3692                 }
3693         
3694                 for(i = 0; i < argbuf->len; i++) {
3695                         int ret, msgnum;
3696         
3697                         if((ret = sscanf(g_ptr_array_index(argbuf, i), 
3698                                     "%*d FETCH (UID %d)", &msgnum)) == 1)
3699                                 uidlist = g_slist_prepend(uidlist, GINT_TO_POINTER(msgnum));
3700                 }
3701                 ptr_array_free_strings(argbuf);
3702                 g_ptr_array_free(argbuf, TRUE);
3703         }
3704
3705         lastuid_old = item->lastuid;
3706         *msgnum_list = g_slist_copy(item->uid_list);
3707         nummsgs = g_slist_length(*msgnum_list);
3708         debug_print("Got %d uids from cache\n", g_slist_length(item->uid_list));
3709
3710         for (elem = uidlist; elem != NULL; elem = g_slist_next(elem)) {
3711                 guint msgnum;
3712
3713                 msgnum = GPOINTER_TO_INT(elem->data);
3714                 if (msgnum > lastuid_old) {
3715                         *msgnum_list = g_slist_prepend(*msgnum_list, GINT_TO_POINTER(msgnum));
3716                         item->uid_list = g_slist_prepend(item->uid_list, GINT_TO_POINTER(msgnum));
3717                         nummsgs++;
3718
3719                         if(msgnum > item->lastuid)
3720                                 item->lastuid = msgnum;
3721                 }
3722         }
3723         g_slist_free(uidlist);
3724
3725         return nummsgs;
3726 }
3727
3728 gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list, gboolean *old_uids_valid)
3729 {
3730         IMAPFolderItem *item = (IMAPFolderItem *)_item;
3731         IMAPSession *session;
3732         gint ok, nummsgs = 0, exists, recent, uid_val, uid_next, unseen;
3733         GSList *uidlist = NULL;
3734         gchar *dir;
3735         gboolean selected_folder;
3736
3737         g_return_val_if_fail(folder != NULL, -1);
3738         g_return_val_if_fail(item != NULL, -1);
3739         g_return_val_if_fail(item->item.path != NULL, -1);
3740         g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3741         g_return_val_if_fail(folder->account != NULL, -1);
3742
3743         session = imap_session_get(folder);
3744         g_return_val_if_fail(session != NULL, -1);
3745
3746         selected_folder = (session->mbox != NULL) &&
3747                           (!strcmp(session->mbox, item->item.path));
3748         if (selected_folder) {
3749                 ok = imap_cmd_noop(session);
3750                 if (ok != IMAP_SUCCESS)
3751                         return -1;
3752                 exists = session->exists;
3753
3754                 *old_uids_valid = TRUE;
3755         } else {
3756                 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
3757                                  &exists, &recent, &uid_next, &uid_val, &unseen);
3758                 if (ok != IMAP_SUCCESS)
3759                         return -1;
3760
3761                 if(item->item.mtime == uid_val)
3762                         *old_uids_valid = TRUE;
3763                 else {
3764                         *old_uids_valid = FALSE;
3765
3766                         debug_print("Freeing imap uid cache\n");
3767                         item->lastuid = 0;
3768                         g_slist_free(item->uid_list);
3769                         item->uid_list = NULL;
3770                 
3771                         item->item.mtime = uid_val;
3772
3773                         imap_delete_all_cached_messages((FolderItem *)item);
3774                 }
3775         }
3776
3777         if (!selected_folder)
3778                 item->uid_next = uid_next;
3779
3780         /* If old uid_next matches new uid_next we can be sure no message
3781            was added to the folder */
3782         if (( selected_folder && !session->folder_content_changed) ||
3783             (!selected_folder && uid_next == item->uid_next)) {
3784                 nummsgs = g_slist_length(item->uid_list);
3785
3786                 /* If number of messages is still the same we
3787                    know our caches message numbers are still valid,
3788                    otherwise if the number of messages has decrease
3789                    we discard our cache to start a new scan to find
3790                    out which numbers have been removed */
3791                 if (exists == nummsgs) {
3792                         *msgnum_list = g_slist_copy(item->uid_list);
3793                         return nummsgs;
3794                 } else if (exists < nummsgs) {
3795                         debug_print("Freeing imap uid cache");
3796                         item->lastuid = 0;
3797                         g_slist_free(item->uid_list);
3798                         item->uid_list = NULL;
3799                 }
3800         }
3801
3802         if (exists == 0) {
3803                 *msgnum_list = NULL;
3804                 return 0;
3805         }
3806
3807         nummsgs = get_list_of_uids(folder, item, &uidlist);
3808
3809         if (nummsgs != exists) {
3810                 /* Cache contains more messages then folder, we have cached
3811                    an old UID of a message that was removed and new messages
3812                    have been added too, otherwise the uid_next check would
3813                    not have failed */
3814                 debug_print("Freeing imap uid cache");
3815                 item->lastuid = 0;
3816                 g_slist_free(item->uid_list);
3817                 item->uid_list = NULL;
3818
3819                 g_slist_free(*msgnum_list);
3820
3821                 nummsgs = get_list_of_uids(folder, item, &uidlist);
3822         }
3823
3824         *msgnum_list = uidlist;
3825
3826         dir = folder_item_get_path((FolderItem *)item);
3827         debug_print("removing old messages from %s\n", dir);
3828         remove_numbered_files_not_in_list(dir, *msgnum_list);
3829         g_free(dir);
3830
3831         return nummsgs;
3832 }
3833
3834 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item)
3835 {
3836         MsgInfo *msginfo;
3837         MsgFlags flags;
3838
3839         flags.perm_flags = MSG_NEW|MSG_UNREAD;
3840         flags.tmp_flags = 0;
3841
3842         g_return_val_if_fail(item != NULL, NULL);
3843         g_return_val_if_fail(file != NULL, NULL);
3844
3845         if (item->stype == F_QUEUE) {
3846                 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
3847         } else if (item->stype == F_DRAFT) {
3848                 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
3849         }
3850
3851         msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3852         if (!msginfo) return NULL;
3853
3854         msginfo->folder = item;
3855
3856         return msginfo;
3857 }
3858
3859 GSList *imap_get_msginfos(Folder *folder, FolderItem *item, GSList *msgnum_list)
3860 {
3861         IMAPSession *session;
3862         MsgInfoList *ret = NULL;
3863         gint ok;
3864
3865         g_return_val_if_fail(folder != NULL, NULL);
3866         g_return_val_if_fail(item != NULL, NULL);
3867         g_return_val_if_fail(msgnum_list != NULL, NULL);
3868
3869         session = imap_session_get(folder);
3870         g_return_val_if_fail(session != NULL, NULL);
3871
3872         ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3873                          NULL, NULL, NULL, NULL);
3874         if (ok != IMAP_SUCCESS)
3875                 return NULL;
3876
3877         if (!(item->stype == F_QUEUE || item->stype == F_DRAFT)) {
3878                 ret = g_slist_concat(ret,
3879                         imap_get_uncached_messages(
3880                         session, item, msgnum_list));
3881         } else {
3882                 MsgNumberList *sorted_list, *elem;
3883                 gint startnum, lastnum;
3884
3885                 sorted_list = g_slist_sort(g_slist_copy(msgnum_list), g_int_compare);
3886
3887                 startnum = lastnum = GPOINTER_TO_INT(sorted_list->data);
3888
3889                 for (elem = sorted_list;; elem = g_slist_next(elem)) {
3890                         guint num = 0;
3891
3892                         if (elem)
3893                                 num = GPOINTER_TO_INT(elem->data);
3894
3895                         if (num > lastnum + 1 || elem == NULL) {
3896                                 int i;
3897                                 for (i = startnum; i <= lastnum; ++i) {
3898                                         gchar *file;
3899                         
3900                                         file = imap_fetch_msg(folder, item, i);
3901                                         if (file != NULL) {
3902                                                 MsgInfo *msginfo = imap_parse_msg(file, item);
3903                                                 if (msginfo != NULL) {
3904                                                         msginfo->msgnum = i;
3905                                                         ret = g_slist_append(ret, msginfo);
3906                                                 }
3907                                                 g_free(file);
3908                                         }
3909                                 }
3910
3911                                 if (elem == NULL)
3912                                         break;
3913
3914                                 startnum = num;
3915                         }
3916                         lastnum = num;
3917                 }
3918
3919                 g_slist_free(sorted_list);
3920         }
3921
3922         return ret;
3923 }
3924
3925 MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid)
3926 {
3927         MsgInfo *msginfo = NULL;
3928         MsgInfoList *msginfolist;
3929         MsgNumberList numlist;
3930
3931         numlist.next = NULL;
3932         numlist.data = GINT_TO_POINTER(uid);
3933
3934         msginfolist = imap_get_msginfos(folder, item, &numlist);
3935         if (msginfolist != NULL) {
3936                 msginfo = msginfolist->data;
3937                 g_slist_free(msginfolist);
3938         }
3939
3940         return msginfo;
3941 }
3942
3943 gboolean imap_scan_required(Folder *folder, FolderItem *_item)
3944 {
3945         IMAPSession *session;
3946         IMAPFolderItem *item = (IMAPFolderItem *)_item;
3947         gint ok, exists = 0, recent = 0, unseen = 0;
3948         guint32 uid_next, uid_val = 0;
3949         gboolean selected_folder;
3950         
3951         g_return_val_if_fail(folder != NULL, FALSE);
3952         g_return_val_if_fail(item != NULL, FALSE);
3953         g_return_val_if_fail(item->item.folder != NULL, FALSE);
3954         g_return_val_if_fail(FOLDER_CLASS(item->item.folder) == &imap_class, FALSE);
3955
3956         if (item->item.path == NULL)
3957                 return FALSE;
3958
3959         session = imap_session_get(folder);
3960         g_return_val_if_fail(session != NULL, FALSE);
3961
3962         selected_folder = (session->mbox != NULL) &&
3963                           (!strcmp(session->mbox, item->item.path));
3964         if (selected_folder) {
3965                 ok = imap_cmd_noop(session);
3966                 if (ok != IMAP_SUCCESS)
3967                         return FALSE;
3968
3969                 if (session->folder_content_changed)
3970                         return TRUE;
3971         } else {
3972                 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
3973                                  &exists, &recent, &uid_next, &uid_val, &unseen);
3974                 if (ok != IMAP_SUCCESS)
3975                         return FALSE;
3976
3977                 if ((uid_next != item->uid_next) || (exists < item->item.total_msgs))
3978                         return TRUE;
3979         }
3980
3981         return FALSE;
3982 }
3983
3984 void imap_change_flags(Folder *folder, FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
3985 {
3986         IMAPSession *session;
3987         IMAPFlags flags_set = 0, flags_unset = 0;
3988         gint ok = IMAP_SUCCESS;
3989         MsgNumberList numlist;
3990         
3991         g_return_if_fail(folder != NULL);
3992         g_return_if_fail(folder->klass == &imap_class);
3993         g_return_if_fail(item != NULL);
3994         g_return_if_fail(item->folder == folder);
3995         g_return_if_fail(msginfo != NULL);
3996         g_return_if_fail(msginfo->folder == item);
3997
3998         session = imap_session_get(folder);
3999         if (!session) return;
4000
4001         if ((ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
4002             NULL, NULL, NULL, NULL)) != IMAP_SUCCESS)
4003                 return;
4004
4005         if (!MSG_IS_MARKED(msginfo->flags) &&  (newflags & MSG_MARKED))
4006                 flags_set |= IMAP_FLAG_FLAGGED;
4007         if ( MSG_IS_MARKED(msginfo->flags) && !(newflags & MSG_MARKED))
4008                 flags_unset |= IMAP_FLAG_FLAGGED;
4009
4010         if (!MSG_IS_UNREAD(msginfo->flags) &&  (newflags & MSG_UNREAD))
4011                 flags_unset |= IMAP_FLAG_SEEN;
4012         if ( MSG_IS_UNREAD(msginfo->flags) && !(newflags & MSG_UNREAD))
4013                 flags_set |= IMAP_FLAG_SEEN;
4014
4015         if (!MSG_IS_REPLIED(msginfo->flags) &&  (newflags & MSG_REPLIED))
4016                 flags_set |= IMAP_FLAG_ANSWERED;
4017         if ( MSG_IS_REPLIED(msginfo->flags) && !(newflags & MSG_REPLIED))
4018                 flags_set |= IMAP_FLAG_ANSWERED;
4019
4020         numlist.next = NULL;
4021         numlist.data = GINT_TO_POINTER(msginfo->msgnum);
4022         
4023         if (flags_set) {
4024                 ok = imap_set_message_flags(session, &numlist, flags_set, TRUE);
4025                 if (ok != IMAP_SUCCESS) return;
4026         }
4027
4028         if (flags_unset) {
4029                 ok = imap_set_message_flags(session, &numlist, flags_unset, FALSE);
4030                 if (ok != IMAP_SUCCESS) return;
4031         }
4032
4033         msginfo->flags.perm_flags = newflags;
4034
4035         return;
4036 }
4037
4038 static gint compare_msginfo(gconstpointer a, gconstpointer b)
4039 {
4040         return ((MsgInfo *)a)->msgnum - ((MsgInfo *)b)->msgnum;
4041 }
4042
4043 static guint gslist_find_next_num(MsgNumberList **list, guint num)
4044 {
4045         GSList *elem;
4046
4047         g_return_val_if_fail(list != NULL, -1);
4048
4049         for (elem = *list; elem != NULL; elem = g_slist_next(elem))
4050                 if (GPOINTER_TO_INT(elem->data) >= num)
4051                         break;
4052         *list = elem;
4053         return elem != NULL ? GPOINTER_TO_INT(elem->data) : (gint)-1;
4054 }
4055
4056 /*
4057  * NEW and DELETED flags are not syncronized
4058  * - The NEW/RECENT flags in IMAP folders can not really be directly
4059  *   modified by Sylpheed
4060  * - The DELETE/DELETED flag in IMAP and Sylpheed don't have the same
4061  *   meaning, in IMAP it always removes the messages from the FolderItem
4062  *   in Sylpheed it can mean to move the message to trash
4063  */
4064 static gint imap_get_flags(Folder *folder, FolderItem *item,
4065                            MsgInfoList *msginfo_list, GRelation *msgflags)
4066 {
4067         IMAPSession *session;
4068         GSList *sorted_list;
4069         /*
4070         GSList *new = NULL, *p_new;
4071         GSList *deleted = NULL, *p_deleted;
4072         */
4073         GSList *unseen = NULL, *answered = NULL, *flagged = NULL;
4074         GSList *p_unseen, *p_answered, *p_flagged;
4075         GSList *elem;
4076         GSList *seq_list, *cur;
4077         gboolean reverse_seen = FALSE;
4078         GString *cmd_buf;
4079         gint ok;
4080         gint exists_cnt, recent_cnt, unseen_cnt, uid_next;
4081         guint32 uidvalidity;
4082
4083         g_return_val_if_fail(folder != NULL, -1);
4084         g_return_val_if_fail(item != NULL, -1);
4085         if (msginfo_list == NULL)
4086                 return 0;
4087
4088         session = imap_session_get(folder);
4089         g_return_val_if_fail(session != NULL, -1);
4090
4091         ok = imap_select(session, IMAP_FOLDER(folder), item->path,
4092                         NULL, NULL, NULL, NULL);
4093         if (ok != IMAP_SUCCESS)
4094                 return -1;
4095
4096         ok = imap_status(session, IMAP_FOLDER(folder), item->path,
4097                          &exists_cnt, &recent_cnt, &uid_next, &uidvalidity, &unseen_cnt);
4098
4099         if (unseen_cnt > exists_cnt / 2)
4100                 reverse_seen = TRUE;
4101
4102         cmd_buf = g_string_new(NULL);
4103
4104         sorted_list = g_slist_sort(g_slist_copy(msginfo_list), compare_msginfo);
4105
4106         seq_list = imap_get_seq_set_from_msglist(msginfo_list);
4107
4108         for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
4109                 IMAPSet imapset = cur->data;
4110 /*
4111                 g_string_sprintf(cmd_buf, "RECENT UID %s", imapset);
4112                 imap_cmd_search(session, cmd_buf->str, &p_new);
4113                 new = g_slist_concat(new, p_new);
4114 */
4115                 g_string_sprintf(cmd_buf, "%sSEEN UID %s", reverse_seen ? "" : "UN", imapset);
4116                 imap_cmd_search(session, cmd_buf->str, &p_unseen);
4117                 unseen = g_slist_concat(unseen, p_unseen);
4118
4119                 g_string_sprintf(cmd_buf, "ANSWERED UID %s", imapset);
4120                 imap_cmd_search(session, cmd_buf->str, &p_answered);
4121                 answered = g_slist_concat(answered, p_answered);
4122
4123                 g_string_sprintf(cmd_buf, "FLAGGED UID %s", imapset);
4124                 imap_cmd_search(session, cmd_buf->str, &p_flagged);
4125                 flagged = g_slist_concat(flagged, p_flagged);
4126 /*
4127                 g_string_sprintf(cmd_buf, "DELETED UID %s", imapset);
4128                 imap_cmd_search(session, cmd_buf->str, &p_deleted);
4129                 deleted = g_slist_concat(deleted, p_deleted);
4130 */
4131         }
4132
4133 /*
4134         p_new = new;
4135 */
4136         p_unseen = unseen;
4137         p_answered = answered;
4138         p_flagged = flagged;
4139 /*
4140         p_deleted = deleted;
4141 */      
4142         for (elem = sorted_list; elem != NULL; elem = g_slist_next(elem)) {
4143                 MsgInfo *msginfo;
4144                 MsgPermFlags flags;
4145                 gboolean wasnew;
4146                 
4147                 msginfo = (MsgInfo *) elem->data;
4148                 flags = msginfo->flags.perm_flags;
4149                 wasnew = (flags & MSG_NEW);
4150                 flags &= ~((reverse_seen ? 0 : MSG_UNREAD | MSG_NEW) | MSG_REPLIED | MSG_MARKED);
4151                 if (reverse_seen)
4152                         flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
4153                 /*
4154                 if (gslist_find_next_num(&p_new, msginfo->msgnum) == msginfo->msgnum)
4155                         flags |= MSG_NEW;
4156                 */
4157                 if (gslist_find_next_num(&p_unseen, msginfo->msgnum) == msginfo->msgnum) {
4158                         if (!reverse_seen) {
4159                                 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
4160                         } else {
4161                                 flags &= ~(MSG_UNREAD | MSG_NEW);
4162                         }
4163                 }
4164                 if (gslist_find_next_num(&p_answered, msginfo->msgnum) == msginfo->msgnum)
4165                         flags |= MSG_REPLIED;
4166                 if (gslist_find_next_num(&p_flagged, msginfo->msgnum) == msginfo->msgnum)
4167                         flags |= MSG_MARKED;
4168                 /*
4169                 if (gslist_find_next_num(&p_deleted, msginfo->msgnum) == msginfo->msgnum)
4170                         MSG_SET_PERM_FLAGS(msginfo->flags, MSG_DELETED);
4171                  */
4172                 g_relation_insert(msgflags, msginfo, GINT_TO_POINTER(flags));
4173         }
4174
4175         imap_seq_set_free(seq_list);
4176         /* g_slist_free(deleted); */
4177         g_slist_free(flagged);
4178         g_slist_free(answered);
4179         g_slist_free(unseen);
4180         /* new not freed in original patch ??? */
4181         g_slist_free(sorted_list);
4182         g_string_free(cmd_buf, TRUE);
4183
4184         return 0;
4185 }