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