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