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