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