2005-01-31 [colin] 1.0.0cvs24.1
[claws.git] / src / imap.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2004 Hiroyuki Yamamoto
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #  include "config.h"
22 #endif
23
24 #include "defs.h"
25
26 #include <glib.h>
27 #include <stdio.h>
28 #include <string.h>
29 #include <stdlib.h>
30 #include <dirent.h>
31 #include <unistd.h>
32 #include <ctype.h>
33 #include <time.h>
34 #if HAVE_ICONV
35 #  include <iconv.h>
36 #endif
37
38 #if USE_OPENSSL
39 #  include "ssl.h"
40 #endif
41 #include "folder.h"
42 #include "session.h"
43 #include "procmsg.h"
44 #include "intl.h"
45 #include "imap.h"
46 #include "socket.h"
47 #include "ssl.h"
48 #include "recv.h"
49 #include "procheader.h"
50 #include "prefs_account.h"
51 #include "codeconv.h"
52 #include "md5.h"
53 #include "base64.h"
54 #include "utils.h"
55 #include "prefs_common.h"
56 #include "inputdialog.h"
57 #include "log.h"
58 #include "remotefolder.h"
59 #include "alertpanel.h"
60
61 typedef struct _IMAPFolder      IMAPFolder;
62 typedef struct _IMAPSession     IMAPSession;
63 typedef struct _IMAPNameSpace   IMAPNameSpace;
64 typedef struct _IMAPFolderItem  IMAPFolderItem;
65
66 #include "prefs_account.h"
67
68 #define IMAP_FOLDER(obj)        ((IMAPFolder *)obj)
69 #define IMAP_FOLDER_ITEM(obj)   ((IMAPFolderItem *)obj)
70 #define IMAP_SESSION(obj)       ((IMAPSession *)obj)
71
72 struct _IMAPFolder
73 {
74         RemoteFolder rfolder;
75
76         /* list of IMAPNameSpace */
77         GList *ns_personal;
78         GList *ns_others;
79         GList *ns_shared;
80 };
81
82 struct _IMAPSession
83 {
84         Session session;
85
86         gboolean authenticated;
87
88         gchar **capability;
89         gboolean uidplus;
90
91         gchar *mbox;
92         guint cmd_count;
93
94         /* CLAWS */
95         gboolean folder_content_changed;
96         guint exists;
97 };
98
99 struct _IMAPNameSpace
100 {
101         gchar *name;
102         gchar separator;
103 };
104
105 #define IMAP_SUCCESS    0
106 #define IMAP_SOCKET     2
107 #define IMAP_AUTHFAIL   3
108 #define IMAP_PROTOCOL   4
109 #define IMAP_SYNTAX     5
110 #define IMAP_IOERR      6
111 #define IMAP_ERROR      7
112
113 #define IMAPBUFSIZE     8192
114
115 typedef enum
116 {
117         IMAP_FLAG_SEEN          = 1 << 0,
118         IMAP_FLAG_ANSWERED      = 1 << 1,
119         IMAP_FLAG_FLAGGED       = 1 << 2,
120         IMAP_FLAG_DELETED       = 1 << 3,
121         IMAP_FLAG_DRAFT         = 1 << 4
122 } IMAPFlags;
123
124 #define IMAP_IS_SEEN(flags)     ((flags & IMAP_FLAG_SEEN) != 0)
125 #define IMAP_IS_ANSWERED(flags) ((flags & IMAP_FLAG_ANSWERED) != 0)
126 #define IMAP_IS_FLAGGED(flags)  ((flags & IMAP_FLAG_FLAGGED) != 0)
127 #define IMAP_IS_DELETED(flags)  ((flags & IMAP_FLAG_DELETED) != 0)
128 #define IMAP_IS_DRAFT(flags)    ((flags & IMAP_FLAG_DRAFT) != 0)
129
130
131 #define IMAP4_PORT      143
132 #if USE_OPENSSL
133 #define IMAPS_PORT      993
134 #endif
135
136 #define IMAP_CMD_LIMIT  1000
137
138 #define QUOTE_IF_REQUIRED(out, str)                             \
139 {                                                               \
140         if (*str != '"' && strpbrk(str, " \t(){}%*") != NULL) { \
141                 gchar *__tmp;                                   \
142                 gint len;                                       \
143                                                                 \
144                 len = strlen(str) + 3;                          \
145                 Xalloca(__tmp, len, return IMAP_ERROR);         \
146                 g_snprintf(__tmp, len, "\"%s\"", str);          \
147                 out = __tmp;                                    \
148         } else {                                                \
149                 Xstrdup_a(out, str, return IMAP_ERROR);         \
150         }                                                       \
151 }
152
153 typedef gchar * IMAPSet;
154
155 struct _IMAPFolderItem
156 {
157         FolderItem item;
158
159         guint lastuid;
160         guint uid_next;
161         GSList *uid_list;
162 };
163
164 static void imap_folder_init            (Folder         *folder,
165                                          const gchar    *name,
166                                          const gchar    *path);
167
168 static Folder   *imap_folder_new        (const gchar    *name,
169                                          const gchar    *path);
170 static void      imap_folder_destroy    (Folder         *folder);
171
172 static IMAPSession *imap_session_new    (const PrefsAccount     *account);
173 static void     imap_session_authenticate(IMAPSession           *session,
174                                           const PrefsAccount    *account);
175 static void     imap_session_destroy    (Session        *session);
176
177 static gchar   *imap_fetch_msg          (Folder         *folder, 
178                                          FolderItem     *item, 
179                                          gint            uid);
180 static gint     imap_add_msg            (Folder         *folder,
181                                          FolderItem     *dest,
182                                          const gchar    *file, 
183                                          MsgFlags       *flags);
184 static gint     imap_add_msgs           (Folder         *folder, 
185                                          FolderItem     *dest,
186                                          GSList         *file_list,
187                                          GRelation      *relation);
188
189 static gint     imap_copy_msg           (Folder         *folder,
190                                          FolderItem     *dest, 
191                                          MsgInfo        *msginfo);
192 static gint     imap_copy_msgs          (Folder         *folder, 
193                                          FolderItem     *dest, 
194                                          MsgInfoList    *msglist, 
195                                          GRelation      *relation);
196
197 static gint     imap_remove_msg         (Folder         *folder, 
198                                          FolderItem     *item, 
199                                          gint            uid);
200 static gint     imap_remove_all_msg     (Folder         *folder, 
201                                          FolderItem     *item);
202
203 static gboolean imap_is_msg_changed     (Folder         *folder,
204                                          FolderItem     *item, 
205                                          MsgInfo        *msginfo);
206
207 static gint     imap_close              (Folder         *folder, 
208                                          FolderItem     *item);
209
210 static gint     imap_scan_tree          (Folder         *folder);
211
212 static gint     imap_create_tree        (Folder         *folder);
213
214 static FolderItem *imap_create_folder   (Folder         *folder,
215                                          FolderItem     *parent,
216                                          const gchar    *name);
217 static gint     imap_rename_folder      (Folder         *folder,
218                                          FolderItem     *item, 
219                                          const gchar    *name);
220 static gint     imap_remove_folder      (Folder         *folder, 
221                                          FolderItem     *item);
222
223 static FolderItem *imap_folder_item_new (Folder         *folder);
224 static void imap_folder_item_destroy    (Folder         *folder,
225                                          FolderItem     *item);
226
227 static IMAPSession *imap_session_get    (Folder         *folder);
228
229 static gint imap_greeting               (IMAPSession    *session);
230 static gint imap_auth                   (IMAPSession    *session,
231                                          const gchar    *user,
232                                          const gchar    *pass,
233                                          IMAPAuthType    type);
234
235 static gint imap_scan_tree_recursive    (IMAPSession    *session,
236                                          FolderItem     *item);
237 static GSList *imap_parse_list          (IMAPFolder     *folder,
238                                          IMAPSession    *session,
239                                          const gchar    *real_path,
240                                          gchar          *separator);
241
242 static void imap_create_missing_folders (Folder         *folder);
243 static FolderItem *imap_create_special_folder
244                                         (Folder                 *folder,
245                                          SpecialFolderItemType   stype,
246                                          const gchar            *name);
247
248 static gint imap_do_copy_msgs           (Folder         *folder,
249                                          FolderItem     *dest,
250                                          MsgInfoList    *msglist,
251                                          GRelation      *relation);
252
253 static void imap_delete_all_cached_messages     (FolderItem     *item);
254
255 #if USE_OPENSSL
256 static SockInfo *imap_open              (const gchar    *server,
257                                          gushort         port,
258                                          SSLType         ssl_type);
259 #else
260 static SockInfo *imap_open              (const gchar    *server,
261                                          gushort         port);
262 #endif
263
264 #if USE_OPENSSL
265 static SockInfo *imap_open_tunnel(const gchar *server,
266                                   const gchar *tunnelcmd,
267                                   SSLType ssl_type);
268 #else
269 static SockInfo *imap_open_tunnel(const gchar *server,
270                                   const gchar *tunnelcmd);
271 #endif
272
273 #if USE_OPENSSL
274 static SockInfo *imap_init_sock(SockInfo *sock, SSLType ssl_type);
275 #else
276 static SockInfo *imap_init_sock(SockInfo *sock);
277 #endif
278
279 static gchar *imap_get_flag_str         (IMAPFlags       flags);
280 static gint imap_set_message_flags      (IMAPSession    *session,
281                                          MsgNumberList  *numlist,
282                                          IMAPFlags       flags,
283                                          gboolean        is_set);
284 static gint imap_select                 (IMAPSession    *session,
285                                          IMAPFolder     *folder,
286                                          const gchar    *path,
287                                          gint           *exists,
288                                          gint           *recent,
289                                          gint           *unseen,
290                                          guint32        *uid_validity);
291 static gint imap_status                 (IMAPSession    *session,
292                                          IMAPFolder     *folder,
293                                          const gchar    *path,
294                                          gint           *messages,
295                                          gint           *recent,
296                                          guint32        *uid_next,
297                                          guint32        *uid_validity,
298                                          gint           *unseen);
299
300 static void imap_parse_namespace                (IMAPSession    *session,
301                                                  IMAPFolder     *folder);
302 static void imap_get_namespace_by_list          (IMAPSession    *session,
303                                                  IMAPFolder     *folder);
304 static IMAPNameSpace *imap_find_namespace       (IMAPFolder     *folder,
305                                                  const gchar    *path);
306 static gchar imap_get_path_separator            (IMAPFolder     *folder,
307                                                  const gchar    *path);
308 static gchar *imap_get_real_path                (IMAPFolder     *folder,
309                                                  const gchar    *path);
310
311 static gchar *imap_parse_atom           (SockInfo       *sock,
312                                          gchar          *src,
313                                          gchar          *dest,
314                                          gint            dest_len,
315                                          GString        *str);
316 static MsgFlags imap_parse_flags        (const gchar    *flag_str);
317 static MsgInfo *imap_parse_envelope     (SockInfo       *sock,
318                                          FolderItem     *item,
319                                          GString        *line_str);
320
321 static gboolean imap_has_capability     (IMAPSession    *session,
322                                          const gchar    *cap);
323 static void imap_free_capabilities      (IMAPSession    *session);
324
325 /* low-level IMAP4rev1 commands */
326 static gint imap_cmd_authenticate
327                                 (IMAPSession    *session,
328                                  const gchar    *user,
329                                  const gchar    *pass,
330                                  IMAPAuthType    type);
331 static gint imap_cmd_login      (IMAPSession    *session,
332                                  const gchar    *user,
333                                  const gchar    *pass);
334 static gint imap_cmd_logout     (IMAPSession    *session);
335 static gint imap_cmd_noop       (IMAPSession    *session);
336 #if USE_OPENSSL
337 static gint imap_cmd_starttls   (IMAPSession    *session);
338 #endif
339 static gint imap_cmd_namespace  (IMAPSession    *session,
340                                  gchar         **ns_str);
341 static gint imap_cmd_list       (IMAPSession    *session,
342                                  const gchar    *ref,
343                                  const gchar    *mailbox,
344                                  GPtrArray      *argbuf);
345 static gint imap_cmd_do_select  (IMAPSession    *session,
346                                  const gchar    *folder,
347                                  gboolean        examine,
348                                  gint           *exists,
349                                  gint           *recent,
350                                  gint           *unseen,
351                                  guint32        *uid_validity);
352 static gint imap_cmd_select     (IMAPSession    *session,
353                                  const gchar    *folder,
354                                  gint           *exists,
355                                  gint           *recent,
356                                  gint           *unseen,
357                                  guint32        *uid_validity);
358 static gint imap_cmd_examine    (IMAPSession    *session,
359                                  const gchar    *folder,
360                                  gint           *exists,
361                                  gint           *recent,
362                                  gint           *unseen,
363                                  guint32        *uid_validity);
364 static gint imap_cmd_create     (IMAPSession    *sock,
365                                  const gchar    *folder);
366 static gint imap_cmd_rename     (IMAPSession    *sock,
367                                  const gchar    *oldfolder,
368                                  const gchar    *newfolder);
369 static gint imap_cmd_delete     (IMAPSession    *session,
370                                  const gchar    *folder);
371 static gint imap_cmd_envelope   (IMAPSession    *session,
372                                  IMAPSet         set);
373 static gint imap_cmd_fetch      (IMAPSession    *sock,
374                                  guint32         uid,
375                                  const gchar    *filename);
376 static gint imap_cmd_append     (IMAPSession    *session,
377                                  const gchar    *destfolder,
378                                  const gchar    *file,
379                                  IMAPFlags       flags,
380                                  guint32        *new_uid);
381 static gint imap_cmd_copy       (IMAPSession    *session, 
382                                  const gchar    *seq_set, 
383                                  const gchar    *destfolder,
384                                  GRelation      *uid_mapping);
385 static gint imap_cmd_store      (IMAPSession    *session,
386                                  IMAPSet         set,
387                                  gchar          *sub_cmd);
388 static gint imap_cmd_expunge    (IMAPSession    *session,
389                                  IMAPSet         seq_set);
390 static gint imap_cmd_close      (IMAPSession    *session);
391
392 static gint imap_cmd_ok         (IMAPSession    *session,
393                                  GPtrArray      *argbuf);
394 static void imap_gen_send       (IMAPSession    *session,
395                                  const gchar    *format, ...);
396 static gint imap_gen_recv       (IMAPSession    *session,
397                                  gchar         **ret);
398
399 /* misc utility functions */
400 static gchar *strchr_cpy                        (const gchar    *src,
401                                                  gchar           ch,
402                                                  gchar          *dest,
403                                                  gint            len);
404 static gchar *get_quoted                        (const gchar    *src,
405                                                  gchar           ch,
406                                                  gchar          *dest,
407                                                  gint            len);
408 static gchar *search_array_contain_str          (GPtrArray      *array,
409                                                  const gchar    *str);
410 static gchar *search_array_str                  (GPtrArray      *array,
411                                                  const gchar    *str);
412 static void imap_path_separator_subst           (gchar          *str,
413                                                  gchar           separator);
414
415 static gchar *imap_utf8_to_modified_utf7        (const gchar    *from);
416 static gchar *imap_modified_utf7_to_utf8        (const gchar    *mutf7_str);
417
418 static GSList *imap_get_seq_set_from_numlist    (MsgNumberList  *msglist);
419 static GSList *imap_get_seq_set_from_msglist    (MsgInfoList    *msglist);
420 static void imap_seq_set_free                   (GSList         *seq_list);
421
422 static gboolean imap_rename_folder_func         (GNode          *node,
423                                                  gpointer        data);
424 static gint imap_get_num_list                   (Folder         *folder,
425                                                  FolderItem     *item,
426                                                  GSList        **list,
427                                                  gboolean       *old_uids_valid);
428 static GSList *imap_get_msginfos                (Folder         *folder,
429                                                  FolderItem     *item,
430                                                  GSList         *msgnum_list);
431 static MsgInfo *imap_get_msginfo                (Folder         *folder,
432                                                  FolderItem     *item,
433                                                  gint            num);
434 static gboolean imap_scan_required              (Folder         *folder,
435                                                  FolderItem     *item);
436 static void imap_change_flags                   (Folder         *folder,
437                                                  FolderItem     *item,
438                                                  MsgInfo        *msginfo,
439                                                  MsgPermFlags    newflags);
440 static gint imap_get_flags                      (Folder         *folder,
441                                                  FolderItem     *item,
442                                                  MsgInfoList    *msglist,
443                                                  GRelation      *msgflags);
444 static gchar *imap_folder_get_path              (Folder         *folder);
445 static gchar *imap_item_get_path                (Folder         *folder,
446                                                  FolderItem     *item);
447
448 static FolderClass imap_class;
449
450 FolderClass *imap_get_class(void)
451 {
452         if (imap_class.idstr == NULL) {
453                 imap_class.type = F_IMAP;
454                 imap_class.idstr = "imap";
455                 imap_class.uistr = "IMAP4";
456
457                 /* Folder functions */
458                 imap_class.new_folder = imap_folder_new;
459                 imap_class.destroy_folder = imap_folder_destroy;
460                 imap_class.scan_tree = imap_scan_tree;
461                 imap_class.create_tree = imap_create_tree;
462
463                 /* FolderItem functions */
464                 imap_class.item_new = imap_folder_item_new;
465                 imap_class.item_destroy = imap_folder_item_destroy;
466                 imap_class.item_get_path = imap_item_get_path;
467                 imap_class.create_folder = imap_create_folder;
468                 imap_class.rename_folder = imap_rename_folder;
469                 imap_class.remove_folder = imap_remove_folder;
470                 imap_class.close = imap_close;
471                 imap_class.get_num_list = imap_get_num_list;
472                 imap_class.scan_required = imap_scan_required;
473
474                 /* Message functions */
475                 imap_class.get_msginfo = imap_get_msginfo;
476                 imap_class.get_msginfos = imap_get_msginfos;
477                 imap_class.fetch_msg = imap_fetch_msg;
478                 imap_class.add_msg = imap_add_msg;
479                 imap_class.add_msgs = imap_add_msgs;
480                 imap_class.copy_msg = imap_copy_msg;
481                 imap_class.copy_msgs = imap_copy_msgs;
482                 imap_class.remove_msg = imap_remove_msg;
483                 imap_class.remove_all_msg = imap_remove_all_msg;
484                 imap_class.is_msg_changed = imap_is_msg_changed;
485                 imap_class.change_flags = imap_change_flags;
486                 imap_class.get_flags = imap_get_flags;
487         }
488         
489         return &imap_class;
490 }
491
492 static Folder *imap_folder_new(const gchar *name, const gchar *path)
493 {
494         Folder *folder;
495
496         folder = (Folder *)g_new0(IMAPFolder, 1);
497         folder->klass = &imap_class;
498         imap_folder_init(folder, name, path);
499
500         return folder;
501 }
502
503 static void imap_folder_destroy(Folder *folder)
504 {
505         gchar *dir;
506
507         dir = imap_folder_get_path(folder);
508         if (is_dir_exist(dir))
509                 remove_dir_recursive(dir);
510         g_free(dir);
511
512         folder_remote_folder_destroy(REMOTE_FOLDER(folder));
513 }
514
515 static void imap_folder_init(Folder *folder, const gchar *name,
516                              const gchar *path)
517 {
518         folder_remote_folder_init((Folder *)folder, name, path);
519 }
520
521 static FolderItem *imap_folder_item_new(Folder *folder)
522 {
523         IMAPFolderItem *item;
524         
525         item = g_new0(IMAPFolderItem, 1);
526         item->lastuid = 0;
527         item->uid_next = 0;
528         item->uid_list = NULL;
529
530         return (FolderItem *)item;
531 }
532
533 static void imap_folder_item_destroy(Folder *folder, FolderItem *_item)
534 {
535         IMAPFolderItem *item = (IMAPFolderItem *)_item;
536
537         g_return_if_fail(item != NULL);
538         g_slist_free(item->uid_list);
539
540         g_free(_item);
541 }
542
543 static gboolean imap_reset_uid_lists_func(GNode *node, gpointer data)
544 {
545         IMAPFolderItem *item = (IMAPFolderItem *)node->data;
546         
547         item->lastuid = 0;
548         item->uid_next = 0;
549         g_slist_free(item->uid_list);
550         item->uid_list = NULL;
551         
552         return FALSE;
553 }
554
555 static void imap_reset_uid_lists(Folder *folder)
556 {
557         if(folder->node == NULL)
558                 return;
559         
560         /* Destroy all uid lists and rest last uid */
561         g_node_traverse(folder->node, G_IN_ORDER, G_TRAVERSE_ALL, -1, imap_reset_uid_lists_func, NULL); 
562 }
563
564 /* Send CAPABILITY, and examine the server's response to see whether this
565  * connection is pre-authenticated or not and build a list of CAPABILITIES. */
566 static gint imap_greeting(IMAPSession *session)
567 {
568         gchar *capstr;
569         GPtrArray *argbuf;
570
571         imap_gen_send(session, "CAPABILITY");
572         
573         argbuf = g_ptr_array_new();
574
575         if (imap_cmd_ok(session, argbuf) != IMAP_SUCCESS ||
576             ((capstr = search_array_str(argbuf, "CAPABILITY ")) == NULL)) {
577                 ptr_array_free_strings(argbuf);
578                 g_ptr_array_free(argbuf, TRUE);
579                 return -1;
580         }
581
582         session->authenticated = search_array_str(argbuf, "PREAUTH") != NULL;
583         
584         capstr += strlen("CAPABILITY ");
585
586         IMAP_SESSION(session)->capability = g_strsplit(capstr, " ", 0);
587         
588         ptr_array_free_strings(argbuf);
589         g_ptr_array_free(argbuf, TRUE);
590
591         if (imap_has_capability(session, "UIDPLUS")) 
592                 session->uidplus = TRUE; 
593
594         return 0;
595 }
596
597 static gint imap_auth(IMAPSession *session, const gchar *user, const gchar *pass,
598                       IMAPAuthType type)
599 {
600         gint ok;
601
602         if (type == 0 || type == IMAP_AUTH_LOGIN)
603                 ok = imap_cmd_login(session, user, pass);
604         else
605                 ok = imap_cmd_authenticate(session, user, pass, type);
606
607         if (ok == IMAP_SUCCESS)
608                 session->authenticated = TRUE;
609
610         return ok;
611 }
612
613 static IMAPSession *imap_session_get(Folder *folder)
614 {
615         RemoteFolder *rfolder = REMOTE_FOLDER(folder);
616         IMAPSession *session = NULL;
617
618         g_return_val_if_fail(folder != NULL, NULL);
619         g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, NULL);
620         g_return_val_if_fail(folder->account != NULL, NULL);
621         
622         if (prefs_common.work_offline)
623                 return NULL;
624
625         /* Make sure we have a session */
626         if (rfolder->session != NULL) {
627                 session = IMAP_SESSION(rfolder->session);
628         } else {
629                 imap_reset_uid_lists(folder);
630                 session = imap_session_new(folder->account);
631         }
632         if(session == NULL)
633                 return NULL;
634
635         if (SESSION(session)->sock->state == CONN_DISCONNECTED) {
636                 debug_print("IMAP server disconnected\n");
637                 session_destroy(SESSION(session));
638                 imap_reset_uid_lists(folder);
639                 session = imap_session_new(folder->account);
640         }
641
642         /* Make sure session is authenticated */
643         if (!IMAP_SESSION(session)->authenticated)
644                 imap_session_authenticate(IMAP_SESSION(session), folder->account);
645         if (!IMAP_SESSION(session)->authenticated) {
646                 session_destroy(SESSION(session));
647                 rfolder->session = NULL;
648                 return NULL;
649         }
650
651         /* Make sure we have parsed the IMAP namespace */
652         imap_parse_namespace(IMAP_SESSION(session),
653                              IMAP_FOLDER(folder));
654
655         /* I think the point of this code is to avoid sending a
656          * keepalive if we've used the session recently and therefore
657          * think it's still alive.  Unfortunately, most of the code
658          * does not yet check for errors on the socket, and so if the
659          * connection drops we don't notice until the timeout expires.
660          * A better solution than sending a NOOP every time would be
661          * for every command to be prepared to retry until it is
662          * successfully sent. -- mbp */
663         if (time(NULL) - SESSION(session)->last_access_time > SESSION_TIMEOUT_INTERVAL) {
664                 /* verify that the session is still alive */
665                 if (imap_cmd_noop(session) != IMAP_SUCCESS) {
666                         /* Check if this is the first try to establish a
667                            connection, if yes we don't try to reconnect */
668                         if (rfolder->session == NULL) {
669                                 log_warning(_("Connecting to %s failed"),
670                                             folder->account->recv_server);
671                                 session_destroy(SESSION(session));
672                                 session = NULL;
673                         } else {
674                                 log_warning(_("IMAP4 connection to %s has been"
675                                             " disconnected. Reconnecting...\n"),
676                                             folder->account->recv_server);
677                                 session_destroy(SESSION(session));
678                                 /* Clear folders session to make imap_session_get create
679                                    a new session, because of rfolder->session == NULL
680                                    it will not try to reconnect again and so avoid an
681                                    endless loop */
682                                 rfolder->session = NULL;
683                                 session = imap_session_get(folder);
684                         }
685                 }
686         }
687
688         rfolder->session = SESSION(session);
689         
690         return IMAP_SESSION(session);
691 }
692
693 static IMAPSession *imap_session_new(const PrefsAccount *account)
694 {
695         IMAPSession *session;
696         SockInfo *imap_sock;
697         gushort port;
698
699 #ifdef USE_OPENSSL
700         /* FIXME: IMAP over SSL only... */ 
701         SSLType ssl_type;
702
703         port = account->set_imapport ? account->imapport
704                 : account->ssl_imap == SSL_TUNNEL ? IMAPS_PORT : IMAP4_PORT;
705         ssl_type = account->ssl_imap;   
706 #else
707         port = account->set_imapport ? account->imapport
708                 : IMAP4_PORT;
709 #endif
710
711         if (account->set_tunnelcmd) {
712                 log_message(_("creating tunneled IMAP4 connection\n"));
713 #if USE_OPENSSL
714                 if ((imap_sock = imap_open_tunnel(account->recv_server, 
715                                                   account->tunnelcmd,
716                                                   ssl_type)) == NULL)
717 #else
718                 if ((imap_sock = imap_open_tunnel(account->recv_server, 
719                                                   account->tunnelcmd)) == NULL)
720 #endif
721                         return NULL;
722         } else {
723                 g_return_val_if_fail(account->recv_server != NULL, NULL);
724
725                 log_message(_("creating IMAP4 connection to %s:%d ...\n"),
726                             account->recv_server, port);
727                 
728 #if USE_OPENSSL
729                 if ((imap_sock = imap_open(account->recv_server, port,
730                                            ssl_type)) == NULL)
731 #else
732                 if ((imap_sock = imap_open(account->recv_server, port)) == NULL)
733 #endif
734                         return NULL;
735         }
736
737         session = g_new0(IMAPSession, 1);
738         session_init(SESSION(session));
739         SESSION(session)->type             = SESSION_IMAP;
740         SESSION(session)->server           = g_strdup(account->recv_server);
741         SESSION(session)->sock             = imap_sock;
742
743         SESSION(session)->destroy          = imap_session_destroy;
744
745         session->capability = NULL;
746
747         session->authenticated = FALSE;
748         session->mbox = NULL;
749         session->cmd_count = 0;
750
751         /* Only need to log in if the connection was not PREAUTH */
752         if (imap_greeting(session) != IMAP_SUCCESS) {
753                 session_destroy(SESSION(session));
754                 return NULL;
755         }
756
757 #if USE_OPENSSL
758         if (account->ssl_imap == SSL_STARTTLS && 
759             imap_has_capability(session, "STARTTLS")) {
760                 gint ok;
761
762                 ok = imap_cmd_starttls(session);
763                 if (ok != IMAP_SUCCESS) {
764                         log_warning(_("Can't start TLS session.\n"));
765                         session_destroy(SESSION(session));
766                         return NULL;
767                 }
768                 if (!ssl_init_socket_with_method(SESSION(session)->sock, 
769                     SSL_METHOD_TLSv1)) {
770                         session_destroy(SESSION(session));
771                         return NULL;
772                 }
773
774                 imap_free_capabilities(session);
775                 session->authenticated = FALSE;
776                 session->uidplus = FALSE;
777                 session->cmd_count = 1;
778
779                 if (imap_greeting(session) != IMAP_SUCCESS) {
780                         session_destroy(SESSION(session));
781                         return NULL;
782                 }               
783         }
784 #endif
785         log_message("IMAP connection is %s-authenticated\n",
786                     (session->authenticated) ? "pre" : "un");
787
788         return session;
789 }
790
791 static void imap_session_authenticate(IMAPSession *session, 
792                                       const PrefsAccount *account)
793 {
794         gchar *pass;
795
796         g_return_if_fail(account->userid != NULL);
797
798         pass = account->passwd;
799         if (!pass) {
800                 gchar *tmp_pass;
801                 tmp_pass = input_dialog_query_password(account->recv_server, account->userid);
802                 if (!tmp_pass)
803                         return;
804                 Xstrdup_a(pass, tmp_pass, {g_free(tmp_pass); return;});
805                 g_free(tmp_pass);
806         }
807
808         if (imap_auth(session, account->userid, pass, account->imap_auth_type) != IMAP_SUCCESS) {
809                 imap_cmd_logout(session);
810                 return;
811         }
812
813         session->authenticated = TRUE;
814 }
815
816 static void imap_session_destroy(Session *session)
817 {
818         imap_free_capabilities(IMAP_SESSION(session));
819         g_free(IMAP_SESSION(session)->mbox);
820         sock_close(session->sock);
821         session->sock = NULL;
822 }
823
824 static gchar *imap_fetch_msg(Folder *folder, FolderItem *item, gint uid)
825 {
826         gchar *path, *filename;
827         IMAPSession *session;
828         gint ok;
829
830         g_return_val_if_fail(folder != NULL, NULL);
831         g_return_val_if_fail(item != NULL, NULL);
832
833         path = folder_item_get_path(item);
834         if (!is_dir_exist(path))
835                 make_dir_hier(path);
836         filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(uid), NULL);
837         g_free(path);
838
839         if (is_file_exist(filename)) {
840                 debug_print("message %d has been already cached.\n", uid);
841                 return filename;
842         }
843
844         session = imap_session_get(folder);
845         if (!session) {
846                 g_free(filename);
847                 return NULL;
848         }
849
850         ok = imap_select(session, IMAP_FOLDER(folder), item->path,
851                          NULL, NULL, NULL, NULL);
852         if (ok != IMAP_SUCCESS) {
853                 g_warning("can't select mailbox %s\n", item->path);
854                 g_free(filename);
855                 return NULL;
856         }
857
858         debug_print("getting message %d...\n", uid);
859         ok = imap_cmd_fetch(session, (guint32)uid, filename);
860
861         if (ok != IMAP_SUCCESS) {
862                 g_warning("can't fetch message %d\n", uid);
863                 g_free(filename);
864                 return NULL;
865         }
866
867         return filename;
868 }
869
870 static gint imap_add_msg(Folder *folder, FolderItem *dest, 
871                          const gchar *file, MsgFlags *flags)
872 {
873         gint ret;
874         GSList file_list;
875         MsgFileInfo fileinfo;
876
877         g_return_val_if_fail(file != NULL, -1);
878
879         fileinfo.msginfo = NULL;
880         fileinfo.file = (gchar *)file;
881         fileinfo.flags = flags;
882         file_list.data = &fileinfo;
883         file_list.next = NULL;
884
885         ret = imap_add_msgs(folder, dest, &file_list, NULL);
886         return ret;
887 }
888
889 static gint imap_add_msgs(Folder *folder, FolderItem *dest, GSList *file_list,
890                    GRelation *relation)
891 {
892         gchar *destdir;
893         IMAPSession *session;
894         guint32 last_uid = 0;
895         GSList *cur;
896         MsgFileInfo *fileinfo;
897         gint ok;
898
899         g_return_val_if_fail(folder != NULL, -1);
900         g_return_val_if_fail(dest != NULL, -1);
901         g_return_val_if_fail(file_list != NULL, -1);
902
903         session = imap_session_get(folder);
904         if (!session) return -1;
905
906         destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
907
908         for (cur = file_list; cur != NULL; cur = cur->next) {
909                 IMAPFlags iflags = 0;
910                 guint32 new_uid = 0;
911
912                 fileinfo = (MsgFileInfo *)cur->data;
913
914                 if (fileinfo->flags) {
915                         if (MSG_IS_MARKED(*fileinfo->flags))
916                                 iflags |= IMAP_FLAG_FLAGGED;
917                         if (MSG_IS_REPLIED(*fileinfo->flags))
918                                 iflags |= IMAP_FLAG_ANSWERED;
919                         if (!MSG_IS_UNREAD(*fileinfo->flags))
920                                 iflags |= IMAP_FLAG_SEEN;
921                 }
922
923                 if (dest->stype == F_OUTBOX ||
924                     dest->stype == F_QUEUE  ||
925                     dest->stype == F_DRAFT  ||
926                     dest->stype == F_TRASH)
927                         iflags |= IMAP_FLAG_SEEN;
928
929                 ok = imap_cmd_append(session, destdir, fileinfo->file, iflags, 
930                                      &new_uid);
931
932                 if (ok != IMAP_SUCCESS) {
933                         g_warning("can't append message %s\n", fileinfo->file);
934                         g_free(destdir);
935                         return -1;
936                 }
937
938                 if (relation != NULL)
939                         g_relation_insert(relation, fileinfo->msginfo != NULL ? 
940                                           (gpointer) fileinfo->msginfo : (gpointer) fileinfo,
941                                           GINT_TO_POINTER(dest->last_num + 1));
942                 if (last_uid < new_uid)
943                         last_uid = new_uid;
944         }
945
946         g_free(destdir);
947
948         return last_uid;
949 }
950
951 static gint imap_do_copy_msgs(Folder *folder, FolderItem *dest, 
952                               MsgInfoList *msglist, GRelation *relation)
953 {
954         FolderItem *src;
955         gchar *destdir;
956         GSList *seq_list, *cur;
957         MsgInfo *msginfo;
958         IMAPSession *session;
959         gint ok = IMAP_SUCCESS;
960         GRelation *uid_mapping;
961         gint last_num = 0;
962         
963         g_return_val_if_fail(folder != NULL, -1);
964         g_return_val_if_fail(dest != NULL, -1);
965         g_return_val_if_fail(msglist != NULL, -1);
966
967         session = imap_session_get(folder);
968         if (!session) return -1;
969
970         msginfo = (MsgInfo *)msglist->data;
971
972         src = msginfo->folder;
973         if (src == dest) {
974                 g_warning("the src folder is identical to the dest.\n");
975                 return -1;
976         }
977
978         ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
979                          NULL, NULL, NULL, NULL);
980         if (ok != IMAP_SUCCESS)
981                 return ok;
982
983         destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
984         seq_list = imap_get_seq_set_from_msglist(msglist);
985         uid_mapping = g_relation_new(2);
986         g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
987         
988         for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
989                 gchar *seq_set = (gchar *)cur->data;
990
991                 debug_print("Copying message %s%c[%s] to %s ...\n",
992                             src->path, G_DIR_SEPARATOR,
993                             seq_set, destdir);
994
995                 ok = imap_cmd_copy(session, seq_set, destdir, uid_mapping);
996                 if (ok != IMAP_SUCCESS) {
997                         g_relation_destroy(uid_mapping);
998                         imap_seq_set_free(seq_list);
999                         return -1;
1000                 }
1001         }
1002
1003         for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
1004                 MsgInfo *msginfo = (MsgInfo *)cur->data;
1005                 GTuples *tuples;
1006
1007                 tuples = g_relation_select(uid_mapping, 
1008                                            GINT_TO_POINTER(msginfo->msgnum),
1009                                            0);
1010                 if (tuples->len > 0) {
1011                         gint num = GPOINTER_TO_INT(g_tuples_index(tuples, 0, 1));
1012                         g_relation_insert(relation, msginfo,
1013                                           GPOINTER_TO_INT(num));
1014                         if (num > last_num)
1015                                 last_num = num;
1016                 } else
1017                         g_relation_insert(relation, msginfo,
1018                                           GPOINTER_TO_INT(0));
1019                 g_tuples_destroy(tuples);
1020         }
1021
1022         g_relation_destroy(uid_mapping);
1023         imap_seq_set_free(seq_list);
1024
1025         g_free(destdir);
1026
1027         if (ok == IMAP_SUCCESS)
1028                 return last_num;
1029         else
1030                 return -1;
1031 }
1032
1033 static gint imap_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1034 {
1035         GSList msglist;
1036
1037         g_return_val_if_fail(msginfo != NULL, -1);
1038
1039         msglist.data = msginfo;
1040         msglist.next = NULL;
1041
1042         return imap_copy_msgs(folder, dest, &msglist, NULL);
1043 }
1044
1045 static gint imap_copy_msgs(Folder *folder, FolderItem *dest, 
1046                     MsgInfoList *msglist, GRelation *relation)
1047 {
1048         MsgInfo *msginfo;
1049         GSList *file_list;
1050         gint ret;
1051
1052         g_return_val_if_fail(folder != NULL, -1);
1053         g_return_val_if_fail(dest != NULL, -1);
1054         g_return_val_if_fail(msglist != NULL, -1);
1055
1056         msginfo = (MsgInfo *)msglist->data;
1057         g_return_val_if_fail(msginfo->folder != NULL, -1);
1058
1059         if (folder == msginfo->folder->folder)
1060                 return imap_do_copy_msgs(folder, dest, msglist, relation);
1061
1062         file_list = procmsg_get_message_file_list(msglist);
1063         g_return_val_if_fail(file_list != NULL, -1);
1064
1065         ret = imap_add_msgs(folder, dest, file_list, relation);
1066
1067         procmsg_message_file_list_free(file_list);
1068
1069         return ret;
1070 }
1071
1072 static gint imap_remove_msg(Folder *folder, FolderItem *item, gint uid)
1073 {
1074         gint ok;
1075         IMAPSession *session;
1076         gchar *dir;
1077         MsgNumberList numlist;
1078         
1079         g_return_val_if_fail(folder != NULL, -1);
1080         g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
1081         g_return_val_if_fail(item != NULL, -1);
1082
1083         session = imap_session_get(folder);
1084         if (!session) return -1;
1085
1086         ok = imap_select(session, IMAP_FOLDER(folder), item->path,
1087                          NULL, NULL, NULL, NULL);
1088         if (ok != IMAP_SUCCESS)
1089                 return ok;
1090
1091         numlist.next = NULL;
1092         numlist.data = GINT_TO_POINTER(uid);
1093         
1094         ok = imap_set_message_flags
1095                 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
1096                 &numlist, IMAP_FLAG_DELETED, TRUE);
1097         if (ok != IMAP_SUCCESS) {
1098                 log_warning(_("can't set deleted flags: %d\n"), uid);
1099                 return ok;
1100         }
1101
1102         if (!session->uidplus) {
1103                 ok = imap_cmd_expunge(session, NULL);
1104         } else {
1105                 gchar *uidstr;
1106
1107                 uidstr = g_strdup_printf("%u", uid);
1108                 ok = imap_cmd_expunge(session, uidstr);
1109                 g_free(uidstr);
1110         }
1111         if (ok != IMAP_SUCCESS) {
1112                 log_warning(_("can't expunge\n"));
1113                 return ok;
1114         }
1115
1116         IMAP_FOLDER_ITEM(item)->uid_list = g_slist_remove(
1117             IMAP_FOLDER_ITEM(item)->uid_list, numlist.data);
1118         dir = folder_item_get_path(item);
1119         if (is_dir_exist(dir))
1120                 remove_numbered_files(dir, uid, uid);
1121         g_free(dir);
1122
1123         return IMAP_SUCCESS;
1124 }
1125
1126 static gint imap_remove_all_msg(Folder *folder, FolderItem *item)
1127 {
1128         gint ok;
1129         IMAPSession *session;
1130         gchar *dir;
1131
1132         g_return_val_if_fail(folder != NULL, -1);
1133         g_return_val_if_fail(item != NULL, -1);
1134
1135         session = imap_session_get(folder);
1136         if (!session) return -1;
1137
1138         ok = imap_select(session, IMAP_FOLDER(folder), item->path,
1139                          NULL, NULL, NULL, NULL);
1140         if (ok != IMAP_SUCCESS)
1141                 return ok;
1142
1143         imap_gen_send(session, "STORE 1:* +FLAGS.SILENT (\\Deleted)");
1144         ok = imap_cmd_ok(session, NULL);
1145         if (ok != IMAP_SUCCESS) {
1146                 log_warning(_("can't set deleted flags: 1:*\n"));
1147                 return ok;
1148         }
1149
1150         ok = imap_cmd_expunge(session, NULL);
1151         if (ok != IMAP_SUCCESS) {
1152                 log_warning(_("can't expunge\n"));
1153                 return ok;
1154         }
1155
1156         dir = folder_item_get_path(item);
1157         if (is_dir_exist(dir))
1158                 remove_all_numbered_files(dir);
1159         g_free(dir);
1160
1161         return IMAP_SUCCESS;
1162 }
1163
1164 static gboolean imap_is_msg_changed(Folder *folder, FolderItem *item,
1165                                     MsgInfo *msginfo)
1166 {
1167         /* TODO: properly implement this method */
1168         return FALSE;
1169 }
1170
1171 static gint imap_close(Folder *folder, FolderItem *item)
1172 {
1173         gint ok;
1174         IMAPSession *session;
1175
1176         g_return_val_if_fail(folder != NULL, -1);
1177         g_return_val_if_fail(item != NULL, -1);
1178         g_return_val_if_fail(item->path != NULL, -1);
1179
1180         session = imap_session_get(folder);
1181         if (!session) return -1;
1182
1183         if (session->mbox) {
1184                 if (strcmp2(session->mbox, item->path) != 0) return -1;
1185
1186                 ok = imap_cmd_close(session);
1187                 if (ok != IMAP_SUCCESS)
1188                         log_warning(_("can't close folder\n"));
1189
1190                 g_free(session->mbox);
1191                 session->mbox = NULL;
1192
1193                 return ok;
1194         }
1195
1196         return 0;
1197 }
1198
1199 static gint imap_scan_tree(Folder *folder)
1200 {
1201         FolderItem *item = NULL;
1202         IMAPSession *session;
1203         gchar *root_folder = NULL;
1204
1205         g_return_val_if_fail(folder != NULL, -1);
1206         g_return_val_if_fail(folder->account != NULL, -1);
1207
1208         session = imap_session_get(folder);
1209         if (!session) {
1210                 if (!folder->node) {
1211                         folder_tree_destroy(folder);
1212                         item = folder_item_new(folder, folder->name, NULL);
1213                         item->folder = folder;
1214                         folder->node = item->node = g_node_new(item);
1215                 }
1216                 return -1;
1217         }
1218
1219         if (folder->account->imap_dir && *folder->account->imap_dir) {
1220                 gchar *real_path;
1221                 GPtrArray *argbuf;
1222                 gint ok;
1223
1224                 Xstrdup_a(root_folder, folder->account->imap_dir, return -1);
1225                 extract_quote(root_folder, '"');
1226                 subst_char(root_folder,
1227                            imap_get_path_separator(IMAP_FOLDER(folder),
1228                                                    root_folder),
1229                            '/');
1230                 strtailchomp(root_folder, '/');
1231                 real_path = imap_get_real_path
1232                         (IMAP_FOLDER(folder), root_folder);
1233                 debug_print("IMAP root directory: %s\n", real_path);
1234
1235                 /* check if root directory exist */
1236                 argbuf = g_ptr_array_new();
1237                 ok = imap_cmd_list(session, NULL, real_path, argbuf);
1238                 if (ok != IMAP_SUCCESS ||
1239                     search_array_str(argbuf, "LIST ") == NULL) {
1240                         log_warning(_("root folder %s does not exist\n"), real_path);
1241                         g_ptr_array_free(argbuf, TRUE);
1242                         g_free(real_path);
1243
1244                         if (!folder->node) {
1245                                 item = folder_item_new(folder, folder->name, NULL);
1246                                 item->folder = folder;
1247                                 folder->node = item->node = g_node_new(item);
1248                         }
1249                         return -1;
1250                 }
1251                 g_ptr_array_free(argbuf, TRUE);
1252                 g_free(real_path);
1253         }
1254
1255         if (folder->node)
1256                 item = FOLDER_ITEM(folder->node->data);
1257         if (!item || ((item->path || root_folder) &&
1258                       strcmp2(item->path, root_folder) != 0)) {
1259                 folder_tree_destroy(folder);
1260                 item = folder_item_new(folder, folder->name, root_folder);
1261                 item->folder = folder;
1262                 folder->node = item->node = g_node_new(item);
1263         }
1264
1265         imap_scan_tree_recursive(session, FOLDER_ITEM(folder->node->data));
1266         imap_create_missing_folders(folder);
1267
1268         return 0;
1269 }
1270
1271 static gint imap_scan_tree_recursive(IMAPSession *session, FolderItem *item)
1272 {
1273         Folder *folder;
1274         IMAPFolder *imapfolder;
1275         FolderItem *new_item;
1276         GSList *item_list, *cur;
1277         GNode *node;
1278         gchar *real_path;
1279         gchar *wildcard_path;
1280         gchar separator;
1281         gchar wildcard[3];
1282
1283         g_return_val_if_fail(item != NULL, -1);
1284         g_return_val_if_fail(item->folder != NULL, -1);
1285         g_return_val_if_fail(item->no_sub == FALSE, -1);
1286
1287         folder = item->folder;
1288         imapfolder = IMAP_FOLDER(folder);
1289
1290         separator = imap_get_path_separator(imapfolder, item->path);
1291
1292         if (folder->ui_func)
1293                 folder->ui_func(folder, item, folder->ui_func_data);
1294
1295         if (item->path) {
1296                 wildcard[0] = separator;
1297                 wildcard[1] = '%';
1298                 wildcard[2] = '\0';
1299                 real_path = imap_get_real_path(imapfolder, item->path);
1300         } else {
1301                 wildcard[0] = '%';
1302                 wildcard[1] = '\0';
1303                 real_path = g_strdup("");
1304         }
1305
1306         Xstrcat_a(wildcard_path, real_path, wildcard,
1307                   {g_free(real_path); return IMAP_ERROR;});
1308         QUOTE_IF_REQUIRED(wildcard_path, wildcard_path);
1309
1310         imap_gen_send(session, "LIST \"\" %s",
1311                       wildcard_path);
1312
1313         strtailchomp(real_path, separator);
1314         item_list = imap_parse_list(imapfolder, session, real_path, NULL);
1315         g_free(real_path);
1316
1317         node = item->node->children;
1318         while (node != NULL) {
1319                 FolderItem *old_item = FOLDER_ITEM(node->data);
1320                 GNode *next = node->next;
1321
1322                 new_item = NULL;
1323                 for (cur = item_list; cur != NULL; cur = cur->next) {
1324                         FolderItem *cur_item = FOLDER_ITEM(cur->data);
1325                         if (!strcmp2(old_item->path, cur_item->path)) {
1326                                 new_item = cur_item;
1327                                 break;
1328                         }
1329                 }
1330                 if (!new_item) {
1331                         debug_print("folder '%s' not found. removing...\n",
1332                                     old_item->path);
1333                         folder_item_remove(old_item);
1334                 } else {
1335                         old_item->no_sub = new_item->no_sub;
1336                         old_item->no_select = new_item->no_select;
1337                         if (old_item->no_sub == TRUE && node->children) {
1338                                 debug_print("folder '%s' doesn't have "
1339                                             "subfolders. removing...\n",
1340                                             old_item->path);
1341                                 folder_item_remove_children(old_item);
1342                         }
1343                 }
1344
1345                 node = next;
1346         }
1347
1348         for (cur = item_list; cur != NULL; cur = cur->next) {
1349                 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1350                 new_item = NULL;
1351                 for (node = item->node->children; node != NULL;
1352                      node = node->next) {
1353                         if (!strcmp2(FOLDER_ITEM(node->data)->path,
1354                                      cur_item->path)) {
1355                                 new_item = FOLDER_ITEM(node->data);
1356                                 folder_item_destroy(cur_item);
1357                                 cur_item = NULL;
1358                                 break;
1359                         }
1360                 }
1361                 if (!new_item) {
1362                         new_item = cur_item;
1363                         debug_print("new folder '%s' found.\n", new_item->path);
1364                         folder_item_append(item, new_item);
1365                 }
1366
1367                 if (!strcmp(new_item->path, "INBOX")) {
1368                         new_item->stype = F_INBOX;
1369                         folder->inbox = new_item;
1370                 } else if (!folder_item_parent(item) || item->stype == F_INBOX) {
1371                         gchar *base;
1372
1373                         base = g_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 #if !HAVE_ICONV
3370         const gchar *from_p;
3371         gchar *to, *to_p;
3372
3373         to = g_malloc(strlen(mutf7_str) + 1);
3374         to_p = to;
3375
3376         for (from_p = mutf7_str; *from_p != '\0'; from_p++) {
3377                 if (*from_p == '&' && *(from_p + 1) == '-') {
3378                         *to_p++ = '&';
3379                         from_p++;
3380                 } else
3381                         *to_p++ = *from_p;
3382         }
3383         *to_p = '\0';
3384
3385         return to;
3386 #else
3387         static iconv_t cd = (iconv_t)-1;
3388         static gboolean iconv_ok = TRUE;
3389         GString *norm_utf7;
3390         gchar *norm_utf7_p;
3391         size_t norm_utf7_len;
3392         const gchar *p;
3393         gchar *to_str, *to_p;
3394         size_t to_len;
3395         gboolean in_escape = FALSE;
3396
3397         if (!iconv_ok) return g_strdup(mutf7_str);
3398
3399         if (cd == (iconv_t)-1) {
3400                 cd = iconv_open(conv_get_current_charset_str(), CS_UTF_8);
3401                 if (cd == (iconv_t)-1) {
3402                         g_warning(_("iconv cannot convert UTF-7 to UTF-8\n"));
3403                         iconv_ok = FALSE;
3404                         return g_strdup(mutf7_str);
3405                 }
3406         }
3407
3408         norm_utf7 = g_string_new(NULL);
3409
3410         for (p = mutf7_str; *p != '\0'; p++) {
3411                 /* replace: '&'  -> '+',
3412                             "&-" -> '&',
3413                             escaped ','  -> '/' */
3414                 if (!in_escape && *p == '&') {
3415                         if (*(p + 1) != '-') {
3416                                 g_string_append_c(norm_utf7, '+');
3417                                 in_escape = TRUE;
3418                         } else {
3419                                 g_string_append_c(norm_utf7, '&');
3420                                 p++;
3421                         }
3422                 } else if (in_escape && *p == ',') {
3423                         g_string_append_c(norm_utf7, '/');
3424                 } else if (in_escape && *p == '-') {
3425                         g_string_append_c(norm_utf7, '-');
3426                         in_escape = FALSE;
3427                 } else {
3428                         g_string_append_c(norm_utf7, *p);
3429                 }
3430         }
3431
3432         norm_utf7_p = norm_utf7->str;
3433         norm_utf7_len = norm_utf7->len;
3434         to_len = strlen(mutf7_str) * 5;
3435         to_p = to_str = g_malloc(to_len + 1);
3436
3437         if (iconv(cd, (ICONV_CONST gchar **)&norm_utf7_p, &norm_utf7_len,
3438                   &to_p, &to_len) == -1) {
3439                 g_warning(_("iconv cannot convert UTF-7 to UTF-8\n"));
3440                 g_string_free(norm_utf7, TRUE);
3441                 g_free(to_str);
3442                 return g_strdup(mutf7_str);
3443         }
3444
3445         /* second iconv() call for flushing */
3446         iconv(cd, NULL, NULL, &to_p, &to_len);
3447         g_string_free(norm_utf7, TRUE);
3448         *to_p = '\0';
3449
3450         return to_str;
3451 #endif /* !HAVE_ICONV */
3452 }
3453
3454 static gchar *imap_utf8_to_modified_utf7(const gchar *from)
3455 {
3456 #if !HAVE_ICONV
3457         const gchar *from_p;
3458         gchar *to, *to_p;
3459
3460         to = g_malloc(strlen(from) * 2 + 1);
3461         to_p = to;
3462
3463         for (from_p = from; *from_p != '\0'; from_p++) {
3464                 if (*from_p == '&') {
3465                         *to_p++ = '&';
3466                         *to_p++ = '-';
3467                 } else
3468                         *to_p++ = *from_p;
3469         }
3470         *to_p = '\0';
3471
3472         return to;
3473 #else
3474         static iconv_t cd = (iconv_t)-1;
3475         static gboolean iconv_ok = TRUE;
3476         gchar *norm_utf7, *norm_utf7_p;
3477         size_t from_len, norm_utf7_len;
3478         GString *to_str;
3479         gchar *from_tmp, *to, *p;
3480         gboolean in_escape = FALSE;
3481
3482         if (!iconv_ok) return g_strdup(from);
3483
3484         if (cd == (iconv_t)-1) {
3485                 cd = iconv_open("UTF-7", CS_UTF_8);
3486                 if (cd == (iconv_t)-1) {
3487                         g_warning("iconv cannot convert UTF-8 to UTF-7\n");
3488                         iconv_ok = FALSE;
3489                         return g_strdup(from);
3490                 }
3491         }
3492
3493         Xstrdup_a(from_tmp, from, return g_strdup(from));
3494         from_len = strlen(from);
3495         norm_utf7_len = from_len * 5;
3496         Xalloca(norm_utf7, norm_utf7_len + 1, return g_strdup(from));
3497         norm_utf7_p = norm_utf7;
3498
3499 #define IS_PRINT(ch) (isprint(ch) && IS_ASCII(ch))
3500
3501         while (from_len > 0) {
3502                 if (*from_tmp == '+') {
3503                         *norm_utf7_p++ = '+';
3504                         *norm_utf7_p++ = '-';
3505                         norm_utf7_len -= 2;
3506                         from_tmp++;
3507                         from_len--;
3508                 } else if (IS_PRINT(*(guchar *)from_tmp)) {
3509                         /* printable ascii char */
3510                         *norm_utf7_p = *from_tmp;
3511                         norm_utf7_p++;
3512                         norm_utf7_len--;
3513                         from_tmp++;
3514                         from_len--;
3515                 } else {
3516                         size_t mb_len = 0, conv_len = 0;
3517
3518                         /* unprintable char: convert to UTF-7 */
3519                         p = from_tmp;
3520                         while (!IS_PRINT(*(guchar *)p) && conv_len < from_len) {
3521                                 mb_len = mblen(p, MB_LEN_MAX);
3522                                 if (mb_len <= 0) {
3523                                         g_warning("wrong multibyte sequence\n");
3524                                         return g_strdup(from);
3525                                 }
3526                                 conv_len += mb_len;
3527                                 p += mb_len;
3528                         }
3529
3530                         from_len -= conv_len;
3531                         if (iconv(cd, (ICONV_CONST gchar **)&from_tmp,
3532                                   &conv_len,
3533                                   &norm_utf7_p, &norm_utf7_len) == -1) {
3534                                 g_warning(_("iconv cannot convert UTF-8 to UTF-7\n"));
3535                                 return g_strdup(from);
3536                         }
3537
3538                         /* second iconv() call for flushing */
3539                         iconv(cd, NULL, NULL, &norm_utf7_p, &norm_utf7_len);
3540                 }
3541         }
3542
3543 #undef IS_PRINT
3544
3545         *norm_utf7_p = '\0';
3546         to_str = g_string_new(NULL);
3547         for (p = norm_utf7; p < norm_utf7_p; p++) {
3548                 /* replace: '&' -> "&-",
3549                             '+' -> '&',
3550                             "+-" -> '+',
3551                             BASE64 '/' -> ',' */
3552                 if (!in_escape && *p == '&') {
3553                         g_string_append(to_str, "&-");
3554                 } else if (!in_escape && *p == '+') {
3555                         if (*(p + 1) == '-') {
3556                                 g_string_append_c(to_str, '+');
3557                                 p++;
3558                         } else {
3559                                 g_string_append_c(to_str, '&');
3560                                 in_escape = TRUE;
3561                         }
3562                 } else if (in_escape && *p == '/') {
3563                         g_string_append_c(to_str, ',');
3564                 } else if (in_escape && *p == '-') {
3565                         g_string_append_c(to_str, '-');
3566                         in_escape = FALSE;
3567                 } else {
3568                         g_string_append_c(to_str, *p);
3569                 }
3570         }
3571
3572         if (in_escape) {
3573                 in_escape = FALSE;
3574                 g_string_append_c(to_str, '-');
3575         }
3576
3577         to = to_str->str;
3578         g_string_free(to_str, FALSE);
3579
3580         return to;
3581 #endif /* !HAVE_ICONV */
3582 }
3583
3584 static GSList *imap_get_seq_set_from_numlist(MsgNumberList *numlist)
3585 {
3586         GString *str;
3587         GSList *sorted_list, *cur;
3588         guint first, last, next;
3589         gchar *ret_str;
3590         GSList *ret_list = NULL;
3591
3592         if (numlist == NULL)
3593                 return NULL;
3594
3595         str = g_string_sized_new(256);
3596
3597         sorted_list = g_slist_copy(numlist);
3598         sorted_list = g_slist_sort(sorted_list, g_int_compare);
3599
3600         first = GPOINTER_TO_INT(sorted_list->data);
3601
3602         for (cur = sorted_list; cur != NULL; cur = g_slist_next(cur)) {
3603                 last = GPOINTER_TO_INT(cur->data);
3604                 if (cur->next)
3605                         next = GPOINTER_TO_INT(cur->next->data);
3606                 else
3607                         next = 0;
3608
3609                 if (last + 1 != next || next == 0) {
3610                         if (str->len > 0)
3611                                 g_string_append_c(str, ',');
3612                         if (first == last)
3613                                 g_string_append_printf(str, "%u", first);
3614                         else
3615                                 g_string_append_printf(str, "%u:%u", first, last);
3616
3617                         first = next;
3618
3619                         if (str->len > IMAP_CMD_LIMIT) {
3620                                 ret_str = g_strdup(str->str);
3621                                 ret_list = g_slist_append(ret_list, ret_str);
3622                                 g_string_truncate(str, 0);
3623                         }
3624                 }
3625         }
3626
3627         if (str->len > 0) {
3628                 ret_str = g_strdup(str->str);
3629                 ret_list = g_slist_append(ret_list, ret_str);
3630         }
3631
3632         g_slist_free(sorted_list);
3633         g_string_free(str, TRUE);
3634
3635         return ret_list;
3636 }
3637
3638 static GSList *imap_get_seq_set_from_msglist(MsgInfoList *msglist)
3639 {
3640         MsgNumberList *numlist = NULL;
3641         MsgInfoList *cur;
3642         GSList *seq_list;
3643
3644         for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
3645                 MsgInfo *msginfo = (MsgInfo *) cur->data;
3646
3647                 numlist = g_slist_append(numlist, GINT_TO_POINTER(msginfo->msgnum));
3648         }
3649         seq_list = imap_get_seq_set_from_numlist(numlist);
3650         g_slist_free(numlist);
3651
3652         return seq_list;
3653 }
3654
3655 static void imap_seq_set_free(GSList *seq_list)
3656 {
3657         slist_free_strings(seq_list);
3658         g_slist_free(seq_list);
3659 }
3660
3661
3662 static gboolean imap_rename_folder_func(GNode *node, gpointer data)
3663 {
3664         FolderItem *item = node->data;
3665         gchar **paths = data;
3666         const gchar *oldpath = paths[0];
3667         const gchar *newpath = paths[1];
3668         gchar *base;
3669         gchar *new_itempath;
3670         gint oldpathlen;
3671
3672         oldpathlen = strlen(oldpath);
3673         if (strncmp(oldpath, item->path, oldpathlen) != 0) {
3674                 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
3675                 return TRUE;
3676         }
3677
3678         base = item->path + oldpathlen;
3679         while (*base == G_DIR_SEPARATOR) base++;
3680         if (*base == '\0')
3681                 new_itempath = g_strdup(newpath);
3682         else
3683                 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
3684                                            NULL);
3685         g_free(item->path);
3686         item->path = new_itempath;
3687
3688         return FALSE;
3689 }
3690
3691 static gint get_list_of_uids(Folder *folder, IMAPFolderItem *item, GSList **msgnum_list)
3692 {
3693         gint ok, nummsgs = 0, lastuid_old;
3694         IMAPSession *session;
3695         GSList *uidlist, *elem;
3696         gchar *cmd_buf;
3697
3698         session = imap_session_get(folder);
3699         g_return_val_if_fail(session != NULL, -1);
3700
3701         ok = imap_select(session, IMAP_FOLDER(folder), item->item.path,
3702                          NULL, NULL, NULL, NULL);
3703         if (ok != IMAP_SUCCESS)
3704                 return -1;
3705
3706         cmd_buf = g_strdup_printf("UID %d:*", item->lastuid + 1);
3707         ok = imap_cmd_search(session, cmd_buf, &uidlist);
3708         g_free(cmd_buf);
3709
3710         if (ok == IMAP_SOCKET) {
3711                 session_destroy((Session *)session);
3712                 ((RemoteFolder *)folder)->session = NULL;
3713                 return -1;
3714         }
3715
3716         if (ok != IMAP_SUCCESS) {
3717                 gint i;
3718                 GPtrArray *argbuf;
3719
3720                 argbuf = g_ptr_array_new();
3721
3722                 cmd_buf = g_strdup_printf("UID FETCH %d:* (UID)", item->lastuid + 1);
3723                 imap_gen_send(session, cmd_buf);
3724                 g_free(cmd_buf);
3725                 ok = imap_cmd_ok(session, argbuf);
3726                 if (ok != IMAP_SUCCESS) {
3727                         ptr_array_free_strings(argbuf);
3728                         g_ptr_array_free(argbuf, TRUE);
3729                         return -1;
3730                 }
3731         
3732                 for(i = 0; i < argbuf->len; i++) {
3733                         int ret, msgnum;
3734         
3735                         if((ret = sscanf(g_ptr_array_index(argbuf, i), 
3736                                     "%*d FETCH (UID %d)", &msgnum)) == 1)
3737                                 uidlist = g_slist_prepend(uidlist, GINT_TO_POINTER(msgnum));
3738                 }
3739                 ptr_array_free_strings(argbuf);
3740                 g_ptr_array_free(argbuf, TRUE);
3741         }
3742
3743         lastuid_old = item->lastuid;
3744         *msgnum_list = g_slist_copy(item->uid_list);
3745         nummsgs = g_slist_length(*msgnum_list);
3746         debug_print("Got %d uids from cache\n", g_slist_length(item->uid_list));
3747
3748         for (elem = uidlist; elem != NULL; elem = g_slist_next(elem)) {
3749                 guint msgnum;
3750
3751                 msgnum = GPOINTER_TO_INT(elem->data);
3752                 if (msgnum > lastuid_old) {
3753                         *msgnum_list = g_slist_prepend(*msgnum_list, GINT_TO_POINTER(msgnum));
3754                         item->uid_list = g_slist_prepend(item->uid_list, GINT_TO_POINTER(msgnum));
3755                         nummsgs++;
3756
3757                         if(msgnum > item->lastuid)
3758                                 item->lastuid = msgnum;
3759                 }
3760         }
3761         g_slist_free(uidlist);
3762
3763         return nummsgs;
3764 }
3765
3766 gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list, gboolean *old_uids_valid)
3767 {
3768         IMAPFolderItem *item = (IMAPFolderItem *)_item;
3769         IMAPSession *session;
3770         gint ok, nummsgs = 0, exists, recent, uid_val, uid_next, unseen;
3771         GSList *uidlist = NULL;
3772         gchar *dir;
3773         gboolean selected_folder;
3774
3775         g_return_val_if_fail(folder != NULL, -1);
3776         g_return_val_if_fail(item != NULL, -1);
3777         g_return_val_if_fail(item->item.path != NULL, -1);
3778         g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3779         g_return_val_if_fail(folder->account != NULL, -1);
3780
3781         session = imap_session_get(folder);
3782         g_return_val_if_fail(session != NULL, -1);
3783
3784         selected_folder = (session->mbox != NULL) &&
3785                           (!strcmp(session->mbox, item->item.path));
3786         if (selected_folder) {
3787                 ok = imap_cmd_noop(session);
3788                 if (ok != IMAP_SUCCESS)
3789                         return -1;
3790                 exists = session->exists;
3791
3792                 *old_uids_valid = TRUE;
3793         } else {
3794                 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
3795                                  &exists, &recent, &uid_next, &uid_val, &unseen);
3796                 if (ok != IMAP_SUCCESS)
3797                         return -1;
3798
3799                 if(item->item.mtime == uid_val)
3800                         *old_uids_valid = TRUE;
3801                 else {
3802                         *old_uids_valid = FALSE;
3803
3804                         debug_print("Freeing imap uid cache\n");
3805                         item->lastuid = 0;
3806                         g_slist_free(item->uid_list);
3807                         item->uid_list = NULL;
3808                 
3809                         item->item.mtime = uid_val;
3810
3811                         imap_delete_all_cached_messages((FolderItem *)item);
3812                 }
3813         }
3814
3815         if (!selected_folder)
3816                 item->uid_next = uid_next;
3817
3818         /* If old uid_next matches new uid_next we can be sure no message
3819            was added to the folder */
3820         if (( selected_folder && !session->folder_content_changed) ||
3821             (!selected_folder && uid_next == item->uid_next)) {
3822                 nummsgs = g_slist_length(item->uid_list);
3823
3824                 /* If number of messages is still the same we
3825                    know our caches message numbers are still valid,
3826                    otherwise if the number of messages has decrease
3827                    we discard our cache to start a new scan to find
3828                    out which numbers have been removed */
3829                 if (exists == nummsgs) {
3830                         *msgnum_list = g_slist_copy(item->uid_list);
3831                         return nummsgs;
3832                 } else if (exists < nummsgs) {
3833                         debug_print("Freeing imap uid cache");
3834                         item->lastuid = 0;
3835                         g_slist_free(item->uid_list);
3836                         item->uid_list = NULL;
3837                 }
3838         }
3839
3840         if (exists == 0) {
3841                 *msgnum_list = NULL;
3842                 return 0;
3843         }
3844
3845         nummsgs = get_list_of_uids(folder, item, &uidlist);
3846
3847         if (nummsgs != exists) {
3848                 /* Cache contains more messages then folder, we have cached
3849                    an old UID of a message that was removed and new messages
3850                    have been added too, otherwise the uid_next check would
3851                    not have failed */
3852                 debug_print("Freeing imap uid cache");
3853                 item->lastuid = 0;
3854                 g_slist_free(item->uid_list);
3855                 item->uid_list = NULL;
3856
3857                 g_slist_free(*msgnum_list);
3858
3859                 nummsgs = get_list_of_uids(folder, item, &uidlist);
3860         }
3861
3862         *msgnum_list = uidlist;
3863
3864         dir = folder_item_get_path((FolderItem *)item);
3865         debug_print("removing old messages from %s\n", dir);
3866         remove_numbered_files_not_in_list(dir, *msgnum_list);
3867         g_free(dir);
3868
3869         return nummsgs;
3870 }
3871
3872 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item)
3873 {
3874         MsgInfo *msginfo;
3875         MsgFlags flags;
3876
3877         flags.perm_flags = MSG_NEW|MSG_UNREAD;
3878         flags.tmp_flags = 0;
3879
3880         g_return_val_if_fail(item != NULL, NULL);
3881         g_return_val_if_fail(file != NULL, NULL);
3882
3883         if (item->stype == F_QUEUE) {
3884                 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
3885         } else if (item->stype == F_DRAFT) {
3886                 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
3887         }
3888
3889         msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3890         if (!msginfo) return NULL;
3891
3892         msginfo->folder = item;
3893
3894         return msginfo;
3895 }
3896
3897 GSList *imap_get_msginfos(Folder *folder, FolderItem *item, GSList *msgnum_list)
3898 {
3899         IMAPSession *session;
3900         MsgInfoList *ret = NULL;
3901         gint ok;
3902
3903         g_return_val_if_fail(folder != NULL, NULL);
3904         g_return_val_if_fail(item != NULL, NULL);
3905         g_return_val_if_fail(msgnum_list != NULL, NULL);
3906
3907         session = imap_session_get(folder);
3908         g_return_val_if_fail(session != NULL, NULL);
3909
3910         ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3911                          NULL, NULL, NULL, NULL);
3912         if (ok != IMAP_SUCCESS)
3913                 return NULL;
3914
3915         if (!(item->stype == F_QUEUE || item->stype == F_DRAFT)) {
3916                 ret = g_slist_concat(ret,
3917                         imap_get_uncached_messages(
3918                         session, item, msgnum_list));
3919         } else {
3920                 MsgNumberList *sorted_list, *elem;
3921                 gint startnum, lastnum;
3922
3923                 sorted_list = g_slist_sort(g_slist_copy(msgnum_list), g_int_compare);
3924
3925                 startnum = lastnum = GPOINTER_TO_INT(sorted_list->data);
3926
3927                 for (elem = sorted_list;; elem = g_slist_next(elem)) {
3928                         guint num = 0;
3929
3930                         if (elem)
3931                                 num = GPOINTER_TO_INT(elem->data);
3932
3933                         if (num > lastnum + 1 || elem == NULL) {
3934                                 int i;
3935                                 for (i = startnum; i <= lastnum; ++i) {
3936                                         gchar *file;
3937                         
3938                                         file = imap_fetch_msg(folder, item, i);
3939                                         if (file != NULL) {
3940                                                 MsgInfo *msginfo = imap_parse_msg(file, item);
3941                                                 if (msginfo != NULL) {
3942                                                         msginfo->msgnum = i;
3943                                                         ret = g_slist_append(ret, msginfo);
3944                                                 }
3945                                                 g_free(file);
3946                                         }
3947                                 }
3948
3949                                 if (elem == NULL)
3950                                         break;
3951
3952                                 startnum = num;
3953                         }
3954                         lastnum = num;
3955                 }
3956
3957                 g_slist_free(sorted_list);
3958         }
3959
3960         return ret;
3961 }
3962
3963 MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid)
3964 {
3965         MsgInfo *msginfo = NULL;
3966         MsgInfoList *msginfolist;
3967         MsgNumberList numlist;
3968
3969         numlist.next = NULL;
3970         numlist.data = GINT_TO_POINTER(uid);
3971
3972         msginfolist = imap_get_msginfos(folder, item, &numlist);
3973         if (msginfolist != NULL) {
3974                 msginfo = msginfolist->data;
3975                 g_slist_free(msginfolist);
3976         }
3977
3978         return msginfo;
3979 }
3980
3981 gboolean imap_scan_required(Folder *folder, FolderItem *_item)
3982 {
3983         IMAPSession *session;
3984         IMAPFolderItem *item = (IMAPFolderItem *)_item;
3985         gint ok, exists = 0, recent = 0, unseen = 0;
3986         guint32 uid_next, uid_val = 0;
3987         gboolean selected_folder;
3988         
3989         g_return_val_if_fail(folder != NULL, FALSE);
3990         g_return_val_if_fail(item != NULL, FALSE);
3991         g_return_val_if_fail(item->item.folder != NULL, FALSE);
3992         g_return_val_if_fail(FOLDER_CLASS(item->item.folder) == &imap_class, FALSE);
3993
3994         if (item->item.path == NULL)
3995                 return FALSE;
3996
3997         session = imap_session_get(folder);
3998         g_return_val_if_fail(session != NULL, FALSE);
3999
4000         selected_folder = (session->mbox != NULL) &&
4001                           (!strcmp(session->mbox, item->item.path));
4002         if (selected_folder) {
4003                 ok = imap_cmd_noop(session);
4004                 if (ok != IMAP_SUCCESS)
4005                         return FALSE;
4006
4007                 if (session->folder_content_changed)
4008                         return TRUE;
4009         } else {
4010                 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
4011                                  &exists, &recent, &uid_next, &uid_val, &unseen);
4012                 if (ok != IMAP_SUCCESS)
4013                         return FALSE;
4014
4015                 if ((uid_next != item->uid_next) || (exists < item->item.total_msgs))
4016                         return TRUE;
4017         }
4018
4019         return FALSE;
4020 }
4021
4022 void imap_change_flags(Folder *folder, FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
4023 {
4024         IMAPSession *session;
4025         IMAPFlags flags_set = 0, flags_unset = 0;
4026         gint ok = IMAP_SUCCESS;
4027         MsgNumberList numlist;
4028         
4029         g_return_if_fail(folder != NULL);
4030         g_return_if_fail(folder->klass == &imap_class);
4031         g_return_if_fail(item != NULL);
4032         g_return_if_fail(item->folder == folder);
4033         g_return_if_fail(msginfo != NULL);
4034         g_return_if_fail(msginfo->folder == item);
4035
4036         session = imap_session_get(folder);
4037         if (!session) return;
4038
4039         if ((ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
4040             NULL, NULL, NULL, NULL)) != IMAP_SUCCESS)
4041                 return;
4042
4043         if (!MSG_IS_MARKED(msginfo->flags) &&  (newflags & MSG_MARKED))
4044                 flags_set |= IMAP_FLAG_FLAGGED;
4045         if ( MSG_IS_MARKED(msginfo->flags) && !(newflags & MSG_MARKED))
4046                 flags_unset |= IMAP_FLAG_FLAGGED;
4047
4048         if (!MSG_IS_UNREAD(msginfo->flags) &&  (newflags & MSG_UNREAD))
4049                 flags_unset |= IMAP_FLAG_SEEN;
4050         if ( MSG_IS_UNREAD(msginfo->flags) && !(newflags & MSG_UNREAD))
4051                 flags_set |= IMAP_FLAG_SEEN;
4052
4053         if (!MSG_IS_REPLIED(msginfo->flags) &&  (newflags & MSG_REPLIED))
4054                 flags_set |= IMAP_FLAG_ANSWERED;
4055         if ( MSG_IS_REPLIED(msginfo->flags) && !(newflags & MSG_REPLIED))
4056                 flags_set |= IMAP_FLAG_ANSWERED;
4057
4058         numlist.next = NULL;
4059         numlist.data = GINT_TO_POINTER(msginfo->msgnum);
4060         
4061         if (flags_set) {
4062                 ok = imap_set_message_flags(session, &numlist, flags_set, TRUE);
4063                 if (ok != IMAP_SUCCESS) return;
4064         }
4065
4066         if (flags_unset) {
4067                 ok = imap_set_message_flags(session, &numlist, flags_unset, FALSE);
4068                 if (ok != IMAP_SUCCESS) return;
4069         }
4070
4071         msginfo->flags.perm_flags = newflags;
4072
4073         return;
4074 }
4075
4076 static gint compare_msginfo(gconstpointer a, gconstpointer b)
4077 {
4078         return ((MsgInfo *)a)->msgnum - ((MsgInfo *)b)->msgnum;
4079 }
4080
4081 static guint gslist_find_next_num(MsgNumberList **list, guint num)
4082 {
4083         GSList *elem;
4084
4085         g_return_val_if_fail(list != NULL, -1);
4086
4087         for (elem = *list; elem != NULL; elem = g_slist_next(elem))
4088                 if (GPOINTER_TO_INT(elem->data) >= num)
4089                         break;
4090         *list = elem;
4091         return elem != NULL ? GPOINTER_TO_INT(elem->data) : (gint)-1;
4092 }
4093
4094 /*
4095  * NEW and DELETED flags are not syncronized
4096  * - The NEW/RECENT flags in IMAP folders can not really be directly
4097  *   modified by Sylpheed
4098  * - The DELETE/DELETED flag in IMAP and Sylpheed don't have the same
4099  *   meaning, in IMAP it always removes the messages from the FolderItem
4100  *   in Sylpheed it can mean to move the message to trash
4101  */
4102 static gint imap_get_flags(Folder *folder, FolderItem *item,
4103                            MsgInfoList *msginfo_list, GRelation *msgflags)
4104 {
4105         IMAPSession *session;
4106         GSList *sorted_list;
4107         /*
4108         GSList *new = NULL, *p_new;
4109         GSList *deleted = NULL, *p_deleted;
4110         */
4111         GSList *unseen = NULL, *answered = NULL, *flagged = NULL;
4112         GSList *p_unseen, *p_answered, *p_flagged;
4113         GSList *elem;
4114         GSList *seq_list, *cur;
4115         gboolean reverse_seen = FALSE;
4116         GString *cmd_buf;
4117         gint ok;
4118         gint exists_cnt, recent_cnt, unseen_cnt, uid_next;
4119         guint32 uidvalidity;
4120
4121         g_return_val_if_fail(folder != NULL, -1);
4122         g_return_val_if_fail(item != NULL, -1);
4123         if (msginfo_list == NULL)
4124                 return 0;
4125
4126         session = imap_session_get(folder);
4127         g_return_val_if_fail(session != NULL, -1);
4128
4129         ok = imap_select(session, IMAP_FOLDER(folder), item->path,
4130                         NULL, NULL, NULL, NULL);
4131         if (ok != IMAP_SUCCESS)
4132                 return -1;
4133
4134         ok = imap_status(session, IMAP_FOLDER(folder), item->path,
4135                          &exists_cnt, &recent_cnt, &uid_next, &uidvalidity, &unseen_cnt);
4136
4137         if (unseen_cnt > exists_cnt / 2)
4138                 reverse_seen = TRUE;
4139
4140         cmd_buf = g_string_new(NULL);
4141
4142         sorted_list = g_slist_sort(g_slist_copy(msginfo_list), compare_msginfo);
4143
4144         seq_list = imap_get_seq_set_from_msglist(msginfo_list);
4145
4146         for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
4147                 IMAPSet imapset = cur->data;
4148 /*
4149                 g_string_sprintf(cmd_buf, "RECENT UID %s", imapset);
4150                 imap_cmd_search(session, cmd_buf->str, &p_new);
4151                 new = g_slist_concat(new, p_new);
4152 */
4153                 g_string_sprintf(cmd_buf, "%sSEEN UID %s", reverse_seen ? "" : "UN", imapset);
4154                 imap_cmd_search(session, cmd_buf->str, &p_unseen);
4155                 unseen = g_slist_concat(unseen, p_unseen);
4156
4157                 g_string_sprintf(cmd_buf, "ANSWERED UID %s", imapset);
4158                 imap_cmd_search(session, cmd_buf->str, &p_answered);
4159                 answered = g_slist_concat(answered, p_answered);
4160
4161                 g_string_sprintf(cmd_buf, "FLAGGED UID %s", imapset);
4162                 imap_cmd_search(session, cmd_buf->str, &p_flagged);
4163                 flagged = g_slist_concat(flagged, p_flagged);
4164 /*
4165                 g_string_sprintf(cmd_buf, "DELETED UID %s", imapset);
4166                 imap_cmd_search(session, cmd_buf->str, &p_deleted);
4167                 deleted = g_slist_concat(deleted, p_deleted);
4168 */
4169         }
4170
4171 /*
4172         p_new = new;
4173 */
4174         p_unseen = unseen;
4175         p_answered = answered;
4176         p_flagged = flagged;
4177 /*
4178         p_deleted = deleted;
4179 */      
4180         for (elem = sorted_list; elem != NULL; elem = g_slist_next(elem)) {
4181                 MsgInfo *msginfo;
4182                 MsgPermFlags flags;
4183                 gboolean wasnew;
4184                 
4185                 msginfo = (MsgInfo *) elem->data;
4186                 flags = msginfo->flags.perm_flags;
4187                 wasnew = (flags & MSG_NEW);
4188                 flags &= ~((reverse_seen ? 0 : MSG_UNREAD | MSG_NEW) | MSG_REPLIED | MSG_MARKED);
4189                 if (reverse_seen)
4190                         flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
4191                 /*
4192                 if (gslist_find_next_num(&p_new, msginfo->msgnum) == msginfo->msgnum)
4193                         flags |= MSG_NEW;
4194                 */
4195                 if (gslist_find_next_num(&p_unseen, msginfo->msgnum) == msginfo->msgnum) {
4196                         if (!reverse_seen) {
4197                                 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
4198                         } else {
4199                                 flags &= ~(MSG_UNREAD | MSG_NEW);
4200                         }
4201                 }
4202                 if (gslist_find_next_num(&p_answered, msginfo->msgnum) == msginfo->msgnum)
4203                         flags |= MSG_REPLIED;
4204                 if (gslist_find_next_num(&p_flagged, msginfo->msgnum) == msginfo->msgnum)
4205                         flags |= MSG_MARKED;
4206                 /*
4207                 if (gslist_find_next_num(&p_deleted, msginfo->msgnum) == msginfo->msgnum)
4208                         MSG_SET_PERM_FLAGS(msginfo->flags, MSG_DELETED);
4209                  */
4210                 g_relation_insert(msgflags, msginfo, GINT_TO_POINTER(flags));
4211         }
4212
4213         imap_seq_set_free(seq_list);
4214         /* g_slist_free(deleted); */
4215         g_slist_free(flagged);
4216         g_slist_free(answered);
4217         g_slist_free(unseen);
4218         /* new not freed in original patch ??? */
4219         g_slist_free(sorted_list);
4220         g_string_free(cmd_buf, TRUE);
4221
4222         return 0;
4223 }