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