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