0.8.11claws148
[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()
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
1863                 g_string_truncate(str, 0);
1864                 cur_pos = str->str;
1865
1866                 do {
1867                         if ((nextline = sock_getline(sock)) == NULL)
1868                                 return cur_pos;
1869                         line_len += strlen(nextline);
1870                         g_string_append(str, nextline);
1871                         cur_pos = str->str;
1872                         strretchomp(nextline);
1873                         /* log_print("IMAP4< %s\n", nextline); */
1874                         debug_print("IMAP4< %s\n", nextline);
1875                         g_free(nextline);
1876                 } while (line_len < len);
1877
1878                 memcpy(dest, cur_pos, MIN(len, dest_len - 1));
1879                 dest[MIN(len, dest_len - 1)] = '\0';
1880                 cur_pos += len;
1881         }
1882
1883         return cur_pos;
1884 }
1885
1886 static gchar *imap_get_header(SockInfo *sock, gchar *cur_pos, gchar **headers,
1887                               GString *str)
1888 {
1889         gchar *nextline;
1890         gchar buf[32];
1891         gint len;
1892         gint block_len = 0;
1893
1894         *headers = NULL;
1895
1896         g_return_val_if_fail(str != NULL, cur_pos);
1897
1898         while (isspace(*cur_pos)) cur_pos++;
1899
1900         g_return_val_if_fail(*cur_pos == '{', cur_pos);
1901
1902         cur_pos = strchr_cpy(cur_pos + 1, '}', buf, sizeof(buf));
1903         len = atoi(buf);
1904
1905         g_string_truncate(str, 0);
1906         cur_pos = str->str;
1907
1908         do {
1909                 if ((nextline = sock_getline(sock)) == NULL)
1910                         return cur_pos;
1911                 block_len += strlen(nextline);
1912                 g_string_append(str, nextline);
1913                 cur_pos = str->str;
1914                 strretchomp(nextline);
1915                 /* debug_print("IMAP4< %s\n", nextline); */
1916                 g_free(nextline);
1917         } while (block_len < len);
1918
1919         debug_print("IMAP4< [contents of BODY.PEEK[HEADER.FIELDS (...)]]\n");
1920
1921         *headers = g_strndup(cur_pos, len);
1922         cur_pos += len;
1923
1924         while (isspace(*cur_pos)) cur_pos++;
1925         while (*cur_pos == '\0') {
1926                 if ((nextline = sock_getline(sock)) == NULL)
1927                         return cur_pos;
1928                 g_string_assign(str, nextline);
1929                 cur_pos = str->str;
1930                 strretchomp(nextline);
1931                 debug_print("IMAP4< %s\n", nextline);
1932                 g_free(nextline);
1933
1934                 while (isspace(*cur_pos)) cur_pos++;
1935         }
1936
1937         return cur_pos;
1938 }
1939
1940 static MsgFlags imap_parse_flags(const gchar *flag_str)  
1941 {
1942         const gchar *p = flag_str;
1943         MsgFlags flags = {0, 0};
1944
1945         flags.perm_flags = MSG_UNREAD;
1946
1947         while ((p = strchr(p, '\\')) != NULL) {
1948                 p++;
1949
1950                 if (g_strncasecmp(p, "Recent", 6) == 0 && MSG_IS_UNREAD(flags)) {
1951                         MSG_SET_PERM_FLAGS(flags, MSG_NEW);
1952                 } else if (g_strncasecmp(p, "Seen", 4) == 0) {
1953                         MSG_UNSET_PERM_FLAGS(flags, MSG_NEW|MSG_UNREAD);
1954                 } else if (g_strncasecmp(p, "Deleted", 7) == 0) {
1955                         MSG_SET_PERM_FLAGS(flags, MSG_DELETED);
1956                 } else if (g_strncasecmp(p, "Flagged", 7) == 0) {
1957                         MSG_SET_PERM_FLAGS(flags, MSG_MARKED);
1958                 } else if (g_strncasecmp(p, "Answered", 8) == 0) {
1959                         MSG_SET_PERM_FLAGS(flags, MSG_REPLIED);
1960                 }
1961         }
1962
1963         return flags;
1964 }
1965
1966 static MsgInfo *imap_parse_envelope(SockInfo *sock, FolderItem *item,
1967                                     GString *line_str)
1968 {
1969         gchar buf[IMAPBUFSIZE];
1970         MsgInfo *msginfo = NULL;
1971         gchar *cur_pos;
1972         gint msgnum;
1973         guint32 uid = 0;
1974         size_t size = 0;
1975         MsgFlags flags = {0, 0}, imap_flags = {0, 0};
1976
1977         g_return_val_if_fail(line_str != NULL, NULL);
1978         g_return_val_if_fail(line_str->str[0] == '*' &&
1979                              line_str->str[1] == ' ', NULL);
1980
1981         MSG_SET_TMP_FLAGS(flags, MSG_IMAP);
1982         if (item->stype == F_QUEUE) {
1983                 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
1984         } else if (item->stype == F_DRAFT) {
1985                 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
1986         }
1987
1988         cur_pos = line_str->str + 2;
1989
1990 #define PARSE_ONE_ELEMENT(ch)                                   \
1991 {                                                               \
1992         cur_pos = strchr_cpy(cur_pos, ch, buf, sizeof(buf));    \
1993         if (cur_pos == NULL) {                                  \
1994                 g_warning("cur_pos == NULL\n");                 \
1995                 procmsg_msginfo_free(msginfo);                  \
1996                 return NULL;                                    \
1997         }                                                       \
1998 }
1999
2000         PARSE_ONE_ELEMENT(' ');
2001         msgnum = atoi(buf);
2002
2003         PARSE_ONE_ELEMENT(' ');
2004         g_return_val_if_fail(!strcmp(buf, "FETCH"), NULL);
2005
2006         g_return_val_if_fail(*cur_pos == '(', NULL);
2007         cur_pos++;
2008
2009         while (*cur_pos != '\0' && *cur_pos != ')') {
2010                 while (*cur_pos == ' ') cur_pos++;
2011
2012                 if (!strncmp(cur_pos, "UID ", 4)) {
2013                         cur_pos += 4;
2014                         uid = strtoul(cur_pos, &cur_pos, 10);
2015                 } else if (!strncmp(cur_pos, "FLAGS ", 6)) {
2016                         cur_pos += 6;
2017                         if (*cur_pos != '(') {
2018                                 g_warning("*cur_pos != '('\n");
2019                                 procmsg_msginfo_free(msginfo);
2020                                 return NULL;
2021                         }
2022                         cur_pos++;
2023                         PARSE_ONE_ELEMENT(')');
2024                         imap_flags = imap_parse_flags(buf);
2025                 } else if (!strncmp(cur_pos, "RFC822.SIZE ", 12)) {
2026                         cur_pos += 12;
2027                         size = strtol(cur_pos, &cur_pos, 10);
2028                 } else if (!strncmp(cur_pos, "BODY[HEADER.FIELDS ", 19)) {
2029                         gchar *headers;
2030
2031                         cur_pos += 19;
2032                         if (*cur_pos != '(') {
2033                                 g_warning("*cur_pos != '('\n");
2034                                 procmsg_msginfo_free(msginfo);
2035                                 return NULL;
2036                         }
2037                         cur_pos++;
2038                         PARSE_ONE_ELEMENT(')');
2039                         if (*cur_pos != ']') {
2040                                 g_warning("*cur_pos != ']'\n");
2041                                 procmsg_msginfo_free(msginfo);
2042                                 return NULL;
2043                         }
2044                         cur_pos++;
2045
2046                         cur_pos = imap_get_header(sock, cur_pos, &headers,
2047                                                   line_str);
2048                         msginfo = procheader_parse_str(headers, flags, FALSE, FALSE);
2049                         g_free(headers);
2050                 } else {
2051                         g_warning("invalid FETCH response: %s\n", cur_pos);
2052                         break;
2053                 }
2054         }
2055
2056         if (msginfo) {
2057                 msginfo->msgnum = uid;
2058                 msginfo->size = size;
2059                 msginfo->flags.tmp_flags |= imap_flags.tmp_flags;
2060                 msginfo->flags.perm_flags = imap_flags.perm_flags;
2061         }
2062
2063         return msginfo;
2064 }
2065
2066 static gint imap_set_message_flags(IMAPSession *session,
2067                                    MsgNumberList *numlist,
2068                                    IMAPFlags flags,
2069                                    gboolean is_set)
2070 {
2071         GString *buf;
2072         gint ok;
2073
2074         buf = g_string_new(is_set ? "+FLAGS (" : "-FLAGS (");
2075
2076         if (IMAP_IS_SEEN(flags))        g_string_append(buf, "\\Seen ");
2077         if (IMAP_IS_ANSWERED(flags))    g_string_append(buf, "\\Answered ");
2078         if (IMAP_IS_FLAGGED(flags))     g_string_append(buf, "\\Flagged ");
2079         if (IMAP_IS_DELETED(flags))     g_string_append(buf, "\\Deleted ");
2080         if (IMAP_IS_DRAFT(flags))       g_string_append(buf, "\\Draft");
2081
2082         if (buf->str[buf->len - 1] == ' ')
2083                 g_string_truncate(buf, buf->len - 1);
2084
2085         g_string_append_c(buf, ')');
2086
2087         ok = imap_cmd_store(session, numberlist_to_imapset(numlist),
2088                             buf->str);
2089         g_string_free(buf, TRUE);
2090
2091         return ok;
2092 }
2093
2094 static gint imap_select(IMAPSession *session, IMAPFolder *folder,
2095                         const gchar *path,
2096                         gint *exists, gint *recent, gint *unseen,
2097                         guint32 *uid_validity)
2098 {
2099         gchar *real_path;
2100         gint ok;
2101         gint exists_, recent_, unseen_, uid_validity_;
2102
2103         if (!exists || !recent || !unseen || !uid_validity) {
2104                 if (session->mbox && strcmp(session->mbox, path) == 0)
2105                         return IMAP_SUCCESS;
2106                 exists = &exists_;
2107                 recent = &recent_;
2108                 unseen = &unseen_;
2109                 uid_validity = &uid_validity_;
2110         }
2111
2112         g_free(session->mbox);
2113         session->mbox = NULL;
2114
2115         real_path = imap_get_real_path(folder, path);
2116         ok = imap_cmd_select(session, real_path,
2117                              exists, recent, unseen, uid_validity);
2118         if (ok != IMAP_SUCCESS)
2119                 log_warning(_("can't select folder: %s\n"), real_path);
2120         else
2121                 session->mbox = g_strdup(path);
2122         g_free(real_path);
2123
2124         return ok;
2125 }
2126
2127 #define THROW(err) { ok = err; goto catch; }
2128
2129 static gint imap_status(IMAPSession *session, IMAPFolder *folder,
2130                         const gchar *path,
2131                         gint *messages, gint *recent,
2132                         guint32 *uid_next, guint32 *uid_validity,
2133                         gint *unseen)
2134 {
2135         gchar *real_path;
2136         gchar *real_path_;
2137         gint ok;
2138         GPtrArray *argbuf;
2139         gchar *str;
2140
2141         *messages = *recent = *uid_next = *uid_validity = *unseen = 0;
2142
2143         argbuf = g_ptr_array_new();
2144
2145         real_path = imap_get_real_path(folder, path);
2146         QUOTE_IF_REQUIRED(real_path_, real_path);
2147         imap_gen_send(session, "STATUS %s "
2148                       "(MESSAGES RECENT UIDNEXT UIDVALIDITY UNSEEN)",
2149                       real_path_);
2150
2151         ok = imap_cmd_ok(session, argbuf);
2152         if (ok != IMAP_SUCCESS) THROW(ok);
2153
2154         str = search_array_str(argbuf, "STATUS");
2155         if (!str) THROW(IMAP_ERROR);
2156
2157         str = strchr(str, '(');
2158         if (!str) THROW(IMAP_ERROR);
2159         str++;
2160         while (*str != '\0' && *str != ')') {
2161                 while (*str == ' ') str++;
2162
2163                 if (!strncmp(str, "MESSAGES ", 9)) {
2164                         str += 9;
2165                         *messages = strtol(str, &str, 10);
2166                 } else if (!strncmp(str, "RECENT ", 7)) {
2167                         str += 7;
2168                         *recent = strtol(str, &str, 10);
2169                 } else if (!strncmp(str, "UIDNEXT ", 8)) {
2170                         str += 8;
2171                         *uid_next = strtoul(str, &str, 10);
2172                 } else if (!strncmp(str, "UIDVALIDITY ", 12)) {
2173                         str += 12;
2174                         *uid_validity = strtoul(str, &str, 10);
2175                 } else if (!strncmp(str, "UNSEEN ", 7)) {
2176                         str += 7;
2177                         *unseen = strtol(str, &str, 10);
2178                 } else {
2179                         g_warning("invalid STATUS response: %s\n", str);
2180                         break;
2181                 }
2182         }
2183
2184 catch:
2185         g_free(real_path);
2186         ptr_array_free_strings(argbuf);
2187         g_ptr_array_free(argbuf, TRUE);
2188
2189         return ok;
2190 }
2191
2192 #undef THROW
2193
2194
2195 /* low-level IMAP4rev1 commands */
2196
2197 static gint imap_cmd_login(IMAPSession *session,
2198                            const gchar *user, const gchar *pass)
2199 {
2200         gchar *user_, *pass_;
2201         gint ok;
2202
2203         QUOTE_IF_REQUIRED(user_, user);
2204         QUOTE_IF_REQUIRED(pass_, pass);
2205         imap_gen_send(session, "LOGIN %s %s", user_, pass_);
2206
2207         ok = imap_cmd_ok(session, NULL);
2208         if (ok != IMAP_SUCCESS)
2209                 log_warning(_("IMAP4 login failed.\n"));
2210
2211         return ok;
2212 }
2213
2214 static gint imap_cmd_logout(IMAPSession *session)
2215 {
2216         imap_gen_send(session, "LOGOUT");
2217         return imap_cmd_ok(session, NULL);
2218 }
2219
2220 /* Send CAPABILITY, and examine the server's response to see whether this
2221  * connection is pre-authenticated or not and build a list of CAPABILITIES. */
2222 static gint imap_greeting(IMAPSession *session)
2223 {
2224         gchar *capstr;
2225         GPtrArray *argbuf;
2226
2227         imap_gen_send(session, "CAPABILITY");
2228         
2229         argbuf = g_ptr_array_new();
2230
2231         if (imap_cmd_ok(session, argbuf) != IMAP_SUCCESS ||
2232             ((capstr = search_array_str(argbuf, "CAPABILITY ")) == NULL)) {
2233                 ptr_array_free_strings(argbuf);
2234                 g_ptr_array_free(argbuf, TRUE);
2235                 return -1;
2236         }
2237
2238         session->authenticated = search_array_str(argbuf, "PREAUTH") != NULL;
2239         
2240         capstr += strlen("CAPABILITY ");
2241
2242         IMAP_SESSION(session)->capability = g_strsplit(capstr, " ", 0);
2243         
2244         ptr_array_free_strings(argbuf);
2245         g_ptr_array_free(argbuf, TRUE);
2246
2247         return 0;
2248 }
2249
2250 static gboolean imap_has_capability(IMAPSession *session, const gchar *cap)
2251 {
2252         gchar **p;
2253         
2254         for (p = session->capability; *p != NULL; ++p)
2255                 if (g_strcasecmp(*p, cap) == 0)
2256                         return TRUE;
2257
2258         return FALSE;
2259 }
2260
2261 void imap_free_capabilities(IMAPSession *session)
2262 {
2263         g_strfreev(session->capability);
2264         session->capability = NULL;
2265 }
2266
2267 static const IMAPSet numberlist_to_imapset(MsgNumberList *list)
2268 {
2269         static GString *imapset = NULL;
2270         MsgNumberList *numlist, *elem;
2271         guint first, last, next;
2272
2273         if (imapset == NULL)
2274                 imapset = g_string_sized_new(256);
2275         else
2276                 g_string_truncate(imapset, 0);
2277
2278         numlist = g_slist_copy(list);
2279         numlist = g_slist_sort(numlist, g_int_compare);
2280
2281         first = GPOINTER_TO_INT(numlist->data);
2282         last = first;
2283         for(elem = g_slist_next(numlist); elem != NULL; elem = g_slist_next(elem)) {
2284                 next = GPOINTER_TO_INT(elem->data);
2285                 if(next != (last + 1)) {
2286                         if (imapset->len > 0)
2287                                 g_string_append(imapset, ",");
2288                         if (first == last)
2289                                 g_string_sprintfa(imapset, "%d", first);
2290                         else
2291                                 g_string_sprintfa(imapset, "%d:%d", first, last);
2292
2293                         first = next;
2294                 }
2295                 last = next;
2296         }
2297         if (imapset->len > 0)
2298                 g_string_append(imapset, ",");
2299         if (first == last)
2300                 g_string_sprintfa(imapset, "%d", first);
2301         else
2302                 g_string_sprintfa(imapset, "%d:%d", first, last);
2303
2304         g_slist_free(numlist);
2305
2306         return imapset->str;
2307 }
2308
2309 static gint imap_cmd_noop(IMAPSession *session)
2310 {
2311         imap_gen_send(session, "NOOP");
2312         return imap_cmd_ok(session, NULL);
2313 }
2314
2315 static gint imap_cmd_starttls(IMAPSession *session)
2316 {
2317         imap_gen_send(session, "STARTTLS");
2318         return imap_cmd_ok(session, NULL);
2319 }
2320
2321 #define THROW(err) { ok = err; goto catch; }
2322
2323 static gint imap_cmd_namespace(IMAPSession *session, gchar **ns_str)
2324 {
2325         gint ok;
2326         GPtrArray *argbuf;
2327         gchar *str;
2328
2329         argbuf = g_ptr_array_new();
2330
2331         imap_gen_send(session, "NAMESPACE");
2332         if ((ok = imap_cmd_ok(session, argbuf)) != IMAP_SUCCESS) THROW(ok);
2333
2334         str = search_array_str(argbuf, "NAMESPACE");
2335         if (!str) THROW(IMAP_ERROR);
2336
2337         *ns_str = g_strdup(str);
2338
2339 catch:
2340         ptr_array_free_strings(argbuf);
2341         g_ptr_array_free(argbuf, TRUE);
2342
2343         return ok;
2344 }
2345
2346 #undef THROW
2347
2348 static gint imap_cmd_list(IMAPSession *session, const gchar *ref,
2349                           const gchar *mailbox, GPtrArray *argbuf)
2350 {
2351         gchar *ref_, *mailbox_;
2352
2353         if (!ref) ref = "\"\"";
2354         if (!mailbox) mailbox = "\"\"";
2355
2356         QUOTE_IF_REQUIRED(ref_, ref);
2357         QUOTE_IF_REQUIRED(mailbox_, mailbox);
2358         imap_gen_send(session, "LIST %s %s", ref_, mailbox_);
2359
2360         return imap_cmd_ok(session, argbuf);
2361 }
2362
2363 #define THROW goto catch
2364
2365 static gint imap_cmd_do_select(IMAPSession *session, const gchar *folder,
2366                                gboolean examine,
2367                                gint *exists, gint *recent, gint *unseen,
2368                                guint32 *uid_validity)
2369 {
2370         gint ok;
2371         gchar *resp_str;
2372         GPtrArray *argbuf;
2373         gchar *select_cmd;
2374         gchar *folder_;
2375
2376         *exists = *recent = *unseen = *uid_validity = 0;
2377         argbuf = g_ptr_array_new();
2378
2379         if (examine)
2380                 select_cmd = "EXAMINE";
2381         else
2382                 select_cmd = "SELECT";
2383
2384         QUOTE_IF_REQUIRED(folder_, folder);
2385         imap_gen_send(session, "%s %s", select_cmd, folder_);
2386
2387         if ((ok = imap_cmd_ok(session, argbuf)) != IMAP_SUCCESS) THROW;
2388
2389         resp_str = search_array_contain_str(argbuf, "EXISTS");
2390         if (resp_str) {
2391                 if (sscanf(resp_str,"%d EXISTS", exists) != 1) {
2392                         g_warning("imap_cmd_select(): invalid EXISTS line.\n");
2393                         THROW;
2394                 }
2395         }
2396
2397         resp_str = search_array_contain_str(argbuf, "RECENT");
2398         if (resp_str) {
2399                 if (sscanf(resp_str, "%d RECENT", recent) != 1) {
2400                         g_warning("imap_cmd_select(): invalid RECENT line.\n");
2401                         THROW;
2402                 }
2403         }
2404
2405         resp_str = search_array_contain_str(argbuf, "UIDVALIDITY");
2406         if (resp_str) {
2407                 if (sscanf(resp_str, "OK [UIDVALIDITY %u] ", uid_validity)
2408                     != 1) {
2409                         g_warning("imap_cmd_select(): invalid UIDVALIDITY line.\n");
2410                         THROW;
2411                 }
2412         }
2413
2414         resp_str = search_array_contain_str(argbuf, "UNSEEN");
2415         if (resp_str) {
2416                 if (sscanf(resp_str, "OK [UNSEEN %d] ", unseen) != 1) {
2417                         g_warning("imap_cmd_select(): invalid UNSEEN line.\n");
2418                         THROW;
2419                 }
2420         }
2421
2422 catch:
2423         ptr_array_free_strings(argbuf);
2424         g_ptr_array_free(argbuf, TRUE);
2425
2426         return ok;
2427 }
2428
2429 static gint imap_cmd_select(IMAPSession *session, const gchar *folder,
2430                             gint *exists, gint *recent, gint *unseen,
2431                             guint32 *uid_validity)
2432 {
2433         return imap_cmd_do_select(session, folder, FALSE,
2434                                   exists, recent, unseen, uid_validity);
2435 }
2436
2437 static gint imap_cmd_examine(IMAPSession *session, const gchar *folder,
2438                              gint *exists, gint *recent, gint *unseen,
2439                              guint32 *uid_validity)
2440 {
2441         return imap_cmd_do_select(session, folder, TRUE,
2442                                   exists, recent, unseen, uid_validity);
2443 }
2444
2445 #undef THROW
2446
2447 static gint imap_cmd_create(IMAPSession *session, const gchar *folder)
2448 {
2449         gchar *folder_;
2450
2451         QUOTE_IF_REQUIRED(folder_, folder);
2452         imap_gen_send(session, "CREATE %s", folder_);
2453
2454         return imap_cmd_ok(session, NULL);
2455 }
2456
2457 static gint imap_cmd_rename(IMAPSession *session, const gchar *old_folder,
2458                             const gchar *new_folder)
2459 {
2460         gchar *old_folder_, *new_folder_;
2461
2462         QUOTE_IF_REQUIRED(old_folder_, old_folder);
2463         QUOTE_IF_REQUIRED(new_folder_, new_folder);
2464         imap_gen_send(session, "RENAME %s %s", old_folder_, new_folder_);
2465
2466         return imap_cmd_ok(session, NULL);
2467 }
2468
2469 static gint imap_cmd_delete(IMAPSession *session, const gchar *folder)
2470 {
2471         gchar *folder_;
2472
2473         QUOTE_IF_REQUIRED(folder_, folder);
2474         imap_gen_send(session, "DELETE %s", folder_);
2475
2476         return imap_cmd_ok(session, NULL);
2477 }
2478
2479 static gint imap_cmd_search(IMAPSession *session, const gchar *criteria, GSList **list)
2480 {
2481         gint ok;
2482         gchar *uidlist;
2483         GPtrArray *argbuf;
2484
2485         g_return_val_if_fail(criteria != NULL, IMAP_ERROR);
2486         g_return_val_if_fail(list != NULL, IMAP_ERROR);
2487
2488         *list = NULL;
2489         
2490         argbuf = g_ptr_array_new();
2491         imap_gen_send(session, "UID SEARCH %s", criteria);
2492
2493         ok = imap_cmd_ok(session, argbuf);
2494         if (ok != IMAP_SUCCESS) {
2495                 ptr_array_free_strings(argbuf);
2496                 g_ptr_array_free(argbuf, TRUE);
2497                 return ok;
2498         }
2499
2500         if ((uidlist = search_array_str(argbuf, "SEARCH ")) != NULL) {
2501                 gchar **strlist, **p;
2502
2503                 strlist = g_strsplit(uidlist + 7, " ", 0);
2504                 for (p = strlist; *p != NULL; ++p) {
2505                         guint msgnum;
2506
2507                         if (sscanf(*p, "%d", &msgnum) == 1)
2508                                 *list = g_slist_append(*list, GINT_TO_POINTER(msgnum));
2509                 }
2510                 g_strfreev(strlist);
2511         }
2512         ptr_array_free_strings(argbuf);
2513         g_ptr_array_free(argbuf, TRUE);
2514
2515         return IMAP_SUCCESS;
2516 }
2517
2518 static gint imap_cmd_fetch(IMAPSession *session, guint32 uid, const gchar *filename)
2519 {
2520         gint ok;
2521         gchar *buf;
2522         gchar *cur_pos;
2523         gchar size_str[32];
2524         glong size_num;
2525
2526         g_return_val_if_fail(filename != NULL, IMAP_ERROR);
2527
2528         imap_gen_send(session, "UID FETCH %d BODY.PEEK[]", uid);
2529
2530         while ((ok = imap_gen_recv(session, &buf))
2531                == IMAP_SUCCESS) {
2532                 if (buf[0] != '*' || buf[1] != ' ') {
2533                         g_free(buf);
2534                         return IMAP_ERROR;
2535                 }
2536                 if (strstr(buf, "FETCH") != NULL)
2537                         break;
2538         }
2539         if (ok != IMAP_SUCCESS)
2540                 return ok;
2541
2542         cur_pos = strchr(buf, '{');
2543         if (cur_pos == NULL) {
2544                 g_free(buf);
2545                 return IMAP_ERROR;
2546         }
2547         cur_pos = strchr_cpy(cur_pos + 1, '}', size_str, sizeof(size_str));
2548         if (cur_pos == NULL) {
2549                 g_free(buf);
2550                 return IMAP_ERROR;
2551         }
2552         size_num = atol(size_str);
2553
2554         if (*cur_pos != '\0') {
2555                 g_free(buf);
2556                 return IMAP_ERROR;
2557         }
2558
2559         if (recv_bytes_write_to_file(SESSION(session)->sock, size_num, filename) != 0) {
2560                 g_free(buf);
2561                 return IMAP_ERROR;
2562         }
2563
2564         if (imap_gen_recv(session, &buf) != IMAP_SUCCESS) {
2565                 g_free(buf);
2566                 return IMAP_ERROR;
2567         }
2568
2569         if (buf[0] == '\0' || buf[strlen(buf) - 1] != ')') {
2570                 g_free(buf);
2571                 return IMAP_ERROR;
2572         }
2573
2574         g_free(buf);
2575         ok = imap_cmd_ok(session, NULL);
2576
2577         return ok;
2578 }
2579
2580 static gint imap_cmd_append(IMAPSession *session, const gchar *destfolder,
2581                             const gchar *file, gint32 *new_uid)
2582 {
2583         gint ok;
2584         gint size, newuid;
2585         gchar *destfolder_;
2586         gchar buf[BUFFSIZE], *imapbuf;
2587         FILE *fp;
2588         GPtrArray *reply;
2589         gchar *okmsginfo;
2590
2591         g_return_val_if_fail(file != NULL, IMAP_ERROR);
2592
2593         size = get_file_size_as_crlf(file);
2594         if ((fp = fopen(file, "rb")) == NULL) {
2595                 FILE_OP_ERROR(file, "fopen");
2596                 return -1;
2597         }
2598         QUOTE_IF_REQUIRED(destfolder_, destfolder);
2599         imap_gen_send(session, "APPEND %s (\\Seen) {%d}", destfolder_, size);
2600
2601         ok = imap_gen_recv(session, &imapbuf);
2602         if (ok != IMAP_SUCCESS || imapbuf[0] != '+' || imapbuf[1] != ' ') {
2603                 log_warning(_("can't append %s to %s\n"), file, destfolder_);
2604                 g_free(imapbuf);
2605                 fclose(fp);
2606                 return IMAP_ERROR;
2607         }
2608         g_free(imapbuf);
2609
2610         log_print("IMAP4> %s\n", _("(sending file...)"));
2611
2612         while (fgets(buf, sizeof(buf), fp) != NULL) {
2613                 strretchomp(buf);
2614                 if (sock_puts(SESSION(session)->sock, buf) < 0) {
2615                         fclose(fp);
2616                         return -1;
2617                 }
2618         }
2619
2620         if (ferror(fp)) {
2621                 FILE_OP_ERROR(file, "fgets");
2622                 fclose(fp);
2623                 return -1;
2624         }
2625
2626         sock_puts(SESSION(session)->sock, "");
2627
2628         fclose(fp);
2629
2630         reply = g_ptr_array_new();
2631
2632         *new_uid = 0;
2633         ok = imap_cmd_ok(session, reply);
2634         if (ok != IMAP_SUCCESS)
2635                 log_warning(_("can't append message to %s\n"), destfolder_);
2636         else if (
2637             (new_uid != NULL) && 
2638             (imap_has_capability(session, "UIDPLUS") && reply->len > 0) &&
2639             ((okmsginfo = g_ptr_array_index(reply, reply->len - 1)) != NULL) &&
2640             (sscanf(okmsginfo, "%*u OK [APPENDUID %*u %u]", &newuid) == 1)) {
2641                 *new_uid = newuid;
2642         }
2643
2644         ptr_array_free_strings(reply);
2645         g_ptr_array_free(reply, TRUE);
2646         return ok;
2647 }
2648
2649
2650 static gint imap_cmd_copy(IMAPSession * session,
2651                           gint32 msgnum,
2652                           const gchar * destfolder, gint32 * new_uid)
2653 {
2654         gint ok;
2655         gint32 olduid, newuid;
2656         gchar *okmsginfo;
2657         gchar *destfolder_;
2658         GPtrArray *reply;
2659
2660         g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
2661         g_return_val_if_fail(session != NULL, IMAP_ERROR);
2662         g_return_val_if_fail(new_uid != NULL, IMAP_ERROR);
2663
2664         QUOTE_IF_REQUIRED(destfolder_, destfolder);
2665         imap_gen_send(session, "UID COPY %d %s", msgnum, destfolder_);
2666
2667         reply = g_ptr_array_new();
2668
2669         *new_uid = 0;
2670         ok = imap_cmd_ok(session, reply);
2671         if (ok != IMAP_SUCCESS)
2672                 log_warning(_("can't copy %d to %s\n"), msgnum, destfolder_);
2673         else if (imap_has_capability(session, "UIDPLUS") && reply->len > 0)
2674                 if ((okmsginfo = g_ptr_array_index(reply, reply->len - 1)) != NULL &&
2675                     sscanf(okmsginfo, "%*u OK [COPYUID %*u %u %u]", &olduid, &newuid) == 2 &&
2676                     olduid == msgnum)
2677                         *new_uid = newuid;
2678
2679         ptr_array_free_strings(reply);
2680         g_ptr_array_free(reply, TRUE);
2681         return ok;
2682 }
2683
2684 gint imap_cmd_envelope(IMAPSession *session, IMAPSet set)
2685 {
2686         static GString *header_fields = NULL;
2687
2688         if (header_fields == NULL) {
2689                 const HeaderEntry *headers, *elem;
2690
2691                 headers = procheader_get_headernames(FALSE);
2692                 header_fields = g_string_new("");
2693
2694                 for (elem = headers; elem->name != NULL; ++elem) {
2695                         gint namelen = strlen(elem->name);
2696
2697                         /* Header fields ending with space are not rfc822 headers */
2698                         if (elem->name[namelen - 1] == ' ')
2699                                 continue;
2700
2701                         /* strip : at the of header field */
2702                         if(elem->name[namelen - 1] == ':')
2703                                 namelen--;
2704                         
2705                         if (namelen <= 0)
2706                                 continue;
2707
2708                         g_string_sprintfa(header_fields, "%s%.*s",
2709                                         header_fields->str[0] != '\0' ? " " : "",
2710                                         namelen, elem->name);
2711                 }
2712         }
2713
2714         imap_gen_send
2715                 (session, "UID FETCH %s (UID FLAGS RFC822.SIZE BODY.PEEK[HEADER.FIELDS (%s)])",
2716                  set, header_fields->str);
2717
2718         return IMAP_SUCCESS;
2719 }
2720
2721 static gint imap_cmd_store(IMAPSession *session, IMAPSet set,
2722                            gchar *sub_cmd)
2723 {
2724         gint ok;
2725
2726         imap_gen_send(session, "UID STORE %s %s",
2727                           set, sub_cmd);
2728
2729         if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS) {
2730                 log_warning(_("error while imap command: STORE %s %s\n"),
2731                             set, sub_cmd);
2732                 return ok;
2733         }
2734
2735         return IMAP_SUCCESS;
2736 }
2737
2738 static gint imap_cmd_expunge(IMAPSession *session)
2739 {
2740         gint ok;
2741
2742         imap_gen_send(session, "EXPUNGE");
2743         if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS) {
2744                 log_warning(_("error while imap command: EXPUNGE\n"));
2745                 return ok;
2746         }
2747
2748         return IMAP_SUCCESS;
2749 }
2750
2751 static gint imap_cmd_ok(IMAPSession *session, GPtrArray *argbuf)
2752 {
2753         gint ok;
2754         gchar *buf;
2755         gint cmd_num;
2756         gchar cmd_status[IMAPBUFSIZE];
2757
2758         while ((ok = imap_gen_recv(session, &buf))
2759                == IMAP_SUCCESS) {
2760                 if (buf[0] == '*' && buf[1] == ' ') {
2761                         if (argbuf)
2762                                 g_ptr_array_add(argbuf, g_strdup(buf + 2));
2763                         continue;
2764                 }
2765
2766                 if (sscanf(buf, "%d %s", &cmd_num, cmd_status) < 2) {
2767                         g_free(buf);
2768                         return IMAP_ERROR;
2769                 } else if (cmd_num == session->cmd_count &&
2770                          !strcmp(cmd_status, "OK")) {
2771                         if (argbuf)
2772                                 g_ptr_array_add(argbuf, g_strdup(buf));
2773                         g_free(buf);
2774                         return IMAP_SUCCESS;
2775                 } else {
2776                         g_free(buf);
2777                         return IMAP_ERROR;
2778                 }
2779         }
2780         g_free(buf);
2781
2782         return ok;
2783 }
2784
2785 static void imap_gen_send(IMAPSession *session, const gchar *format, ...)
2786 {
2787         gchar buf[IMAPBUFSIZE];
2788         gchar tmp[IMAPBUFSIZE];
2789         gchar *p;
2790         va_list args;
2791
2792         va_start(args, format);
2793         g_vsnprintf(tmp, sizeof(tmp), format, args);
2794         va_end(args);
2795
2796         session->cmd_count++;
2797
2798         g_snprintf(buf, sizeof(buf), "%d %s\r\n", session->cmd_count, tmp);
2799         if (!strncasecmp(tmp, "LOGIN ", 6) && (p = strchr(tmp + 6, ' '))) {
2800                 *p = '\0';
2801                 log_print("IMAP4> %d %s ********\n", session->cmd_count, tmp);
2802         } else
2803                 log_print("IMAP4> %d %s\n", session->cmd_count, tmp);
2804
2805         sock_write_all(SESSION(session)->sock, buf, strlen(buf));
2806 }
2807
2808 static gint imap_gen_recv(IMAPSession *session, gchar **buf)
2809 {
2810         if ((*buf = sock_getline(SESSION(session)->sock)) == NULL)
2811                 return IMAP_SOCKET;
2812
2813         strretchomp(*buf);
2814
2815         log_print("IMAP4< %s\n", *buf);
2816
2817         return IMAP_SUCCESS;
2818 }
2819
2820
2821 /* misc utility functions */
2822
2823 static gchar *strchr_cpy(const gchar *src, gchar ch, gchar *dest, gint len)
2824 {
2825         gchar *tmp;
2826
2827         dest[0] = '\0';
2828         tmp = strchr(src, ch);
2829         if (!tmp)
2830                 return NULL;
2831
2832         memcpy(dest, src, MIN(tmp - src, len - 1));
2833         dest[MIN(tmp - src, len - 1)] = '\0';
2834
2835         return tmp + 1;
2836 }
2837
2838 static gchar *get_quoted(const gchar *src, gchar ch, gchar *dest, gint len)
2839 {
2840         const gchar *p = src;
2841         gint n = 0;
2842
2843         g_return_val_if_fail(*p == ch, NULL);
2844
2845         *dest = '\0';
2846         p++;
2847
2848         while (*p != '\0' && *p != ch) {
2849                 if (n < len - 1) {
2850                         if (*p == '\\' && *(p + 1) != '\0')
2851                                 p++;
2852                         *dest++ = *p++;
2853                 } else
2854                         p++;
2855                 n++;
2856         }
2857
2858         *dest = '\0';
2859         return (gchar *)(*p == ch ? p + 1 : p);
2860 }
2861
2862 static gchar *search_array_contain_str(GPtrArray *array, const gchar *str)
2863 {
2864         gint i;
2865
2866         for (i = 0; i < array->len; i++) {
2867                 gchar *tmp;
2868
2869                 tmp = g_ptr_array_index(array, i);
2870                 if (strstr(tmp, str) != NULL)
2871                         return tmp;
2872         }
2873
2874         return NULL;
2875 }
2876
2877 static gchar *search_array_str(GPtrArray *array, const gchar *str)
2878 {
2879         gint i;
2880         gint len;
2881
2882         len = strlen(str);
2883
2884         for (i = 0; i < array->len; i++) {
2885                 gchar *tmp;
2886
2887                 tmp = g_ptr_array_index(array, i);
2888                 if (!strncmp(tmp, str, len))
2889                         return tmp;
2890         }
2891
2892         return NULL;
2893 }
2894
2895 static void imap_path_separator_subst(gchar *str, gchar separator)
2896 {
2897         gchar *p;
2898         gboolean in_escape = FALSE;
2899
2900         if (!separator || separator == '/') return;
2901
2902         for (p = str; *p != '\0'; p++) {
2903                 if (*p == '/' && !in_escape)
2904                         *p = separator;
2905                 else if (*p == '&' && *(p + 1) != '-' && !in_escape)
2906                         in_escape = TRUE;
2907                 else if (*p == '-' && in_escape)
2908                         in_escape = FALSE;
2909         }
2910 }
2911
2912 static gchar *imap_modified_utf7_to_locale(const gchar *mutf7_str)
2913 {
2914 #if !HAVE_ICONV
2915         return g_strdup(mutf7_str);
2916 #else
2917         static iconv_t cd = (iconv_t)-1;
2918         static gboolean iconv_ok = TRUE;
2919         GString *norm_utf7;
2920         gchar *norm_utf7_p;
2921         size_t norm_utf7_len;
2922         const gchar *p;
2923         gchar *to_str, *to_p;
2924         size_t to_len;
2925         gboolean in_escape = FALSE;
2926
2927         if (!iconv_ok) return g_strdup(mutf7_str);
2928
2929         if (cd == (iconv_t)-1) {
2930                 cd = iconv_open(conv_get_current_charset_str(), "UTF-7");
2931                 if (cd == (iconv_t)-1) {
2932                         g_warning("iconv cannot convert UTF-7 to %s\n",
2933                                   conv_get_current_charset_str());
2934                         iconv_ok = FALSE;
2935                         return g_strdup(mutf7_str);
2936                 }
2937         }
2938
2939         norm_utf7 = g_string_new(NULL);
2940
2941         for (p = mutf7_str; *p != '\0'; p++) {
2942                 /* replace: '&'  -> '+',
2943                             "&-" -> '&',
2944                             escaped ','  -> '/' */
2945                 if (!in_escape && *p == '&') {
2946                         if (*(p + 1) != '-') {
2947                                 g_string_append_c(norm_utf7, '+');
2948                                 in_escape = TRUE;
2949                         } else {
2950                                 g_string_append_c(norm_utf7, '&');
2951                                 p++;
2952                         }
2953                 } else if (in_escape && *p == ',') {
2954                         g_string_append_c(norm_utf7, '/');
2955                 } else if (in_escape && *p == '-') {
2956                         g_string_append_c(norm_utf7, '-');
2957                         in_escape = FALSE;
2958                 } else {
2959                         g_string_append_c(norm_utf7, *p);
2960                 }
2961         }
2962
2963         norm_utf7_p = norm_utf7->str;
2964         norm_utf7_len = norm_utf7->len;
2965         to_len = strlen(mutf7_str) * 5;
2966         to_p = to_str = g_malloc(to_len + 1);
2967
2968         if (iconv(cd, (ICONV_CONST gchar **)&norm_utf7_p, &norm_utf7_len,
2969                   &to_p, &to_len) == -1) {
2970                 g_warning(_("iconv cannot convert UTF-7 to %s\n"),
2971                           conv_get_current_charset_str());
2972                 g_string_free(norm_utf7, TRUE);
2973                 g_free(to_str);
2974                 return g_strdup(mutf7_str);
2975         }
2976
2977         /* second iconv() call for flushing */
2978         iconv(cd, NULL, NULL, &to_p, &to_len);
2979         g_string_free(norm_utf7, TRUE);
2980         *to_p = '\0';
2981
2982         return to_str;
2983 #endif /* !HAVE_ICONV */
2984 }
2985
2986 static gchar *imap_locale_to_modified_utf7(const gchar *from)
2987 {
2988 #if !HAVE_ICONV
2989         return g_strdup(from);
2990 #else
2991         static iconv_t cd = (iconv_t)-1;
2992         static gboolean iconv_ok = TRUE;
2993         gchar *norm_utf7, *norm_utf7_p;
2994         size_t from_len, norm_utf7_len;
2995         GString *to_str;
2996         gchar *from_tmp, *to, *p;
2997         gboolean in_escape = FALSE;
2998
2999         if (!iconv_ok) return g_strdup(from);
3000
3001         if (cd == (iconv_t)-1) {
3002                 cd = iconv_open("UTF-7", conv_get_current_charset_str());
3003                 if (cd == (iconv_t)-1) {
3004                         g_warning("iconv cannot convert %s to UTF-7\n",
3005                                   conv_get_current_charset_str());
3006                         iconv_ok = FALSE;
3007                         return g_strdup(from);
3008                 }
3009         }
3010
3011         Xstrdup_a(from_tmp, from, return g_strdup(from));
3012         from_len = strlen(from);
3013         norm_utf7_len = from_len * 5;
3014         Xalloca(norm_utf7, norm_utf7_len + 1, return g_strdup(from));
3015         norm_utf7_p = norm_utf7;
3016
3017 #define IS_PRINT(ch) (isprint(ch) && isascii(ch))
3018
3019         while (from_len > 0) {
3020                 if (IS_PRINT(*from_tmp)) {
3021                         /* printable ascii char */
3022                         *norm_utf7_p = *from_tmp;
3023                         norm_utf7_p++;
3024                         from_tmp++;
3025                         from_len--;
3026                 } else {
3027                         size_t mblen;
3028
3029                         /* unprintable char: convert to UTF-7 */
3030                         for (mblen = 0;
3031                              !IS_PRINT(from_tmp[mblen]) && mblen < from_len;
3032                              mblen++)
3033                                 ;
3034                         from_len -= mblen;
3035                         if (iconv(cd, (ICONV_CONST gchar **)&from_tmp, &mblen,
3036                                   &norm_utf7_p, &norm_utf7_len) == -1) {
3037                                 g_warning("iconv cannot convert %s to UTF-7\n",
3038                                           conv_get_current_charset_str());
3039                                 return g_strdup(from);
3040                         }
3041
3042                         /* second iconv() call for flushing */
3043                         iconv(cd, NULL, NULL, &norm_utf7_p, &norm_utf7_len);
3044                 }
3045         }
3046
3047 #undef IS_PRINT
3048
3049         *norm_utf7_p = '\0';
3050         to_str = g_string_new(NULL);
3051         for (p = norm_utf7; p < norm_utf7_p; p++) {
3052                 /* replace: '&' -> "&-",
3053                             '+' -> '&',
3054                             escaped '/' -> ',' */
3055                 if (!in_escape && *p == '&') {
3056                         g_string_append(to_str, "&-");
3057                 } else if (!in_escape && *p == '+') {
3058                         g_string_append_c(to_str, '&');
3059                         in_escape = TRUE;
3060                 } else if (in_escape && *p == '/') {
3061                         g_string_append_c(to_str, ',');
3062                 } else if (in_escape && *p == '-') {
3063                         in_escape = FALSE;
3064                         g_string_append_c(to_str, '-');
3065                 } else {
3066                         g_string_append_c(to_str, *p);
3067                 }
3068         }
3069
3070         if (in_escape) {
3071                 in_escape = FALSE;
3072                 g_string_append_c(to_str, '-');
3073         }
3074
3075         to = to_str->str;
3076         g_string_free(to_str, FALSE);
3077
3078         return to;
3079 #endif /* !HAVE_ICONV */
3080 }
3081
3082 static gboolean imap_rename_folder_func(GNode *node, gpointer data)
3083 {
3084         FolderItem *item = node->data;
3085         gchar **paths = data;
3086         const gchar *oldpath = paths[0];
3087         const gchar *newpath = paths[1];
3088         gchar *base;
3089         gchar *new_itempath;
3090         gint oldpathlen;
3091
3092         oldpathlen = strlen(oldpath);
3093         if (strncmp(oldpath, item->path, oldpathlen) != 0) {
3094                 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
3095                 return TRUE;
3096         }
3097
3098         base = item->path + oldpathlen;
3099         while (*base == G_DIR_SEPARATOR) base++;
3100         if (*base == '\0')
3101                 new_itempath = g_strdup(newpath);
3102         else
3103                 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
3104                                            NULL);
3105         g_free(item->path);
3106         item->path = new_itempath;
3107
3108         return FALSE;
3109 }
3110
3111 static gint get_list_of_uids(Folder *folder, IMAPFolderItem *item, GSList **msgnum_list)
3112 {
3113         gint ok, nummsgs = 0, lastuid_old;
3114         IMAPSession *session;
3115         GSList *uidlist, *elem;
3116         gchar *cmd_buf;
3117
3118         session = imap_session_get(folder);
3119         g_return_val_if_fail(session != NULL, -1);
3120
3121         ok = imap_select(session, IMAP_FOLDER(folder), item->item.path,
3122                          NULL, NULL, NULL, NULL);
3123         if (ok != IMAP_SUCCESS)
3124                 return -1;
3125
3126         cmd_buf = g_strdup_printf("UID %d:*", item->lastuid + 1);
3127         ok = imap_cmd_search(session, cmd_buf, &uidlist);
3128         g_free(cmd_buf);
3129
3130         if (ok == IMAP_SOCKET) {
3131                 session_destroy((Session *)session);
3132                 ((RemoteFolder *)folder)->session = NULL;
3133                 return -1;
3134         }
3135
3136         if (ok != IMAP_SUCCESS) {
3137                 gint i;
3138                 GPtrArray *argbuf;
3139
3140                 argbuf = g_ptr_array_new();
3141
3142                 cmd_buf = g_strdup_printf("UID FETCH %d:* (UID)", item->lastuid + 1);
3143                 imap_gen_send(session, cmd_buf);
3144                 g_free(cmd_buf);
3145                 ok = imap_cmd_ok(session, argbuf);
3146                 if (ok != IMAP_SUCCESS) {
3147                         ptr_array_free_strings(argbuf);
3148                         g_ptr_array_free(argbuf, TRUE);
3149                         return -1;
3150                 }
3151         
3152                 for(i = 0; i < argbuf->len; i++) {
3153                         int ret, msgnum;
3154         
3155                         if((ret = sscanf(g_ptr_array_index(argbuf, i), 
3156                                     "%*d FETCH (UID %d)", &msgnum)) == 1)
3157                                 uidlist = g_slist_prepend(uidlist, GINT_TO_POINTER(msgnum));
3158                 }
3159                 ptr_array_free_strings(argbuf);
3160                 g_ptr_array_free(argbuf, TRUE);
3161         }
3162
3163         lastuid_old = item->lastuid;
3164         *msgnum_list = g_slist_copy(item->uid_list);
3165         nummsgs = g_slist_length(*msgnum_list);
3166         debug_print("Got %d uids from cache\n", g_slist_length(item->uid_list));
3167
3168         for (elem = uidlist; elem != NULL; elem = g_slist_next(elem)) {
3169                 guint msgnum;
3170
3171                 msgnum = GPOINTER_TO_INT(elem->data);
3172                 if (msgnum > lastuid_old) {
3173                         *msgnum_list = g_slist_prepend(*msgnum_list, GINT_TO_POINTER(msgnum));
3174                         item->uid_list = g_slist_prepend(item->uid_list, GINT_TO_POINTER(msgnum));
3175                         nummsgs++;
3176
3177                         if(msgnum > item->lastuid)
3178                                 item->lastuid = msgnum;
3179                 }
3180         }
3181         g_slist_free(uidlist);
3182
3183         return nummsgs;
3184 }
3185
3186 gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list)
3187 {
3188         IMAPFolderItem *item = (IMAPFolderItem *)_item;
3189         IMAPSession *session;
3190         gint ok, nummsgs = 0, exists, recent, unseen, uid_val, uid_next;
3191         GSList *uidlist;
3192         gchar *dir;
3193
3194         g_return_val_if_fail(folder != NULL, -1);
3195         g_return_val_if_fail(item != NULL, -1);
3196         g_return_val_if_fail(item->item.path != NULL, -1);
3197         g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3198         g_return_val_if_fail(folder->account != NULL, -1);
3199
3200         session = imap_session_get(folder);
3201         g_return_val_if_fail(session != NULL, -1);
3202
3203         ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
3204                          &exists, &recent, &uid_next, &uid_val, &unseen);
3205         if (ok != IMAP_SUCCESS)
3206                 return -1;
3207
3208         /* If old uid_next matches new uid_next we can be sure no message
3209            was added to the folder */
3210         if (uid_next == item->uid_next) {
3211                 nummsgs = g_slist_length(item->uid_list);
3212                 
3213                 /* If number of messages is still the same we
3214                    know our caches message numbers are still valid,
3215                    otherwise if the number of messages has decrease
3216                    we discard our cache to start a new scan to find
3217                    out which numbers have been removed */
3218                 if (exists == nummsgs) {
3219                         *msgnum_list = g_slist_copy(item->uid_list);
3220                         return nummsgs;
3221                 } else if (exists < nummsgs) {
3222                         debug_print("Freeing imap uid cache");
3223                         item->lastuid = 0;
3224                         g_slist_free(item->uid_list);
3225                         item->uid_list = NULL;
3226                 }
3227         }
3228         item->uid_next = uid_next;
3229
3230         if (exists == 0) {
3231                 *msgnum_list = NULL;
3232                 return 0;
3233         }
3234
3235         nummsgs = get_list_of_uids(folder, item, &uidlist);
3236
3237         if (nummsgs != exists) {
3238                 /* Cache contains more messages then folder, we have cached
3239                    an old UID of a message that was removed and new messages
3240                    have been added too, otherwise the uid_next check would
3241                    not have failed */
3242                 debug_print("Freeing imap uid cache");
3243                 item->lastuid = 0;
3244                 g_slist_free(item->uid_list);
3245                 item->uid_list = NULL;
3246
3247                 g_slist_free(*msgnum_list);
3248
3249                 nummsgs = get_list_of_uids(folder, item, &uidlist);
3250         }
3251
3252         *msgnum_list = uidlist;
3253
3254         dir = folder_item_get_path((FolderItem *)item);
3255         debug_print("removing old messages from %s\n", dir);
3256         remove_numbered_files_not_in_list(dir, *msgnum_list);
3257         g_free(dir);
3258
3259         return nummsgs;
3260 }
3261
3262 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item)
3263 {
3264         MsgInfo *msginfo;
3265         MsgFlags flags;
3266
3267         flags.perm_flags = MSG_NEW|MSG_UNREAD;
3268         flags.tmp_flags = 0;
3269
3270         g_return_val_if_fail(item != NULL, NULL);
3271         g_return_val_if_fail(file != NULL, NULL);
3272
3273         if (item->stype == F_QUEUE) {
3274                 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
3275         } else if (item->stype == F_DRAFT) {
3276                 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
3277         }
3278
3279         msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3280         if (!msginfo) return NULL;
3281
3282         msginfo->folder = item;
3283
3284         return msginfo;
3285 }
3286
3287 GSList *imap_get_msginfos(Folder *folder, FolderItem *item, GSList *msgnum_list)
3288 {
3289         IMAPSession *session;
3290         MsgInfoList *ret = NULL;
3291         gint ok;
3292
3293         g_return_val_if_fail(folder != NULL, NULL);
3294         g_return_val_if_fail(item != NULL, NULL);
3295         g_return_val_if_fail(msgnum_list != NULL, NULL);
3296
3297         session = imap_session_get(folder);
3298         g_return_val_if_fail(session != NULL, NULL);
3299
3300         ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3301                          NULL, NULL, NULL, NULL);
3302         if (ok != IMAP_SUCCESS)
3303                 return NULL;
3304
3305         if (!(item->stype == F_QUEUE || item->stype == F_DRAFT)) {
3306                 ret = g_slist_concat(ret,
3307                         imap_get_uncached_messages(
3308                         session, item, msgnum_list));
3309         } else {
3310                 MsgNumberList *sorted_list, *elem;
3311                 gint startnum, lastnum;
3312
3313                 sorted_list = g_slist_sort(g_slist_copy(msgnum_list), g_int_compare);
3314
3315                 startnum = lastnum = GPOINTER_TO_INT(sorted_list->data);
3316
3317                 for (elem = sorted_list;; elem = g_slist_next(elem)) {
3318                         guint num;
3319
3320                         if (elem)
3321                                 num = GPOINTER_TO_INT(elem->data);
3322
3323                         if (num > lastnum + 1 || elem == NULL) {
3324                                 int i;
3325                                 for (i = startnum; i <= lastnum; ++i) {
3326                                         gchar *file;
3327                         
3328                                         file = imap_fetch_msg(folder, item, i);
3329                                         if (file != NULL) {
3330                                                 MsgInfo *msginfo = imap_parse_msg(file, item);
3331                                                 if (msginfo != NULL) {
3332                                                         msginfo->msgnum = i;
3333                                                         ret = g_slist_append(ret, msginfo);
3334                                                 }
3335                                                 g_free(file);
3336                                         }
3337                                 }
3338
3339                                 if (elem == NULL)
3340                                         break;
3341
3342                                 startnum = num;
3343                         }
3344                         lastnum = num;
3345                 }
3346
3347                 g_slist_free(sorted_list);
3348         }
3349
3350         return ret;
3351 }
3352
3353 MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid)
3354 {
3355         MsgInfo *msginfo = NULL;
3356         MsgInfoList *msginfolist;
3357         MsgNumberList numlist;
3358
3359         numlist.next = NULL;
3360         numlist.data = GINT_TO_POINTER(uid);
3361
3362         msginfolist = imap_get_msginfos(folder, item, &numlist);
3363         if (msginfolist != NULL) {
3364                 msginfo = msginfolist->data;
3365                 g_slist_free(msginfolist);
3366         }
3367
3368         return msginfo;
3369 }
3370
3371 gboolean imap_check_msgnum_validity(Folder *folder, FolderItem *_item)
3372 {
3373         IMAPSession *session;
3374         IMAPFolderItem *item = (IMAPFolderItem *)_item;
3375         gint ok, exists = 0, recent = 0, unseen = 0;
3376         guint32 uid_next, uid_validity = 0;
3377         
3378         g_return_val_if_fail(folder != NULL, FALSE);
3379         g_return_val_if_fail(item != NULL, FALSE);
3380         g_return_val_if_fail(item->item.folder != NULL, FALSE);
3381         g_return_val_if_fail(FOLDER_CLASS(item->item.folder) == &imap_class, FALSE);
3382
3383         session = imap_session_get(folder);
3384         g_return_val_if_fail(session != NULL, FALSE);
3385
3386         ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
3387                          &exists, &recent, &uid_next, &uid_validity, &unseen);
3388         if (ok != IMAP_SUCCESS)
3389                 return FALSE;
3390
3391         if(item->item.mtime == uid_validity)
3392                 return TRUE;
3393
3394         debug_print("Freeing imap uid cache");
3395         item->lastuid = 0;
3396         g_slist_free(item->uid_list);
3397         item->uid_list = NULL;
3398                 
3399         item->item.mtime = uid_validity;
3400
3401         imap_delete_all_cached_messages((FolderItem *)item);
3402
3403         return FALSE;
3404 }
3405
3406 void imap_change_flags(Folder *folder, FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
3407 {
3408         IMAPSession *session;
3409         IMAPFlags flags_set = 0, flags_unset = 0;
3410         gint ok = IMAP_SUCCESS;
3411         MsgNumberList numlist;
3412         
3413         g_return_if_fail(folder != NULL);
3414         g_return_if_fail(folder->klass == &imap_class);
3415         g_return_if_fail(item != NULL);
3416         g_return_if_fail(item->folder == folder);
3417         g_return_if_fail(msginfo != NULL);
3418         g_return_if_fail(msginfo->folder == item);
3419
3420         session = imap_session_get(folder);
3421         if (!session) return;
3422
3423         if ((ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
3424             NULL, NULL, NULL, NULL)) != IMAP_SUCCESS)
3425                 return;
3426
3427         if (!MSG_IS_MARKED(msginfo->flags) &&  (newflags & MSG_MARKED))
3428                 flags_set |= IMAP_FLAG_FLAGGED;
3429         if ( MSG_IS_MARKED(msginfo->flags) && !(newflags & MSG_MARKED))
3430                 flags_unset |= IMAP_FLAG_FLAGGED;
3431
3432         if (!MSG_IS_UNREAD(msginfo->flags) &&  (newflags & MSG_UNREAD))
3433                 flags_unset |= IMAP_FLAG_SEEN;
3434         if ( MSG_IS_UNREAD(msginfo->flags) && !(newflags & MSG_UNREAD))
3435                 flags_set |= IMAP_FLAG_SEEN;
3436
3437         if (!MSG_IS_REPLIED(msginfo->flags) &&  (newflags & MSG_REPLIED))
3438                 flags_set |= IMAP_FLAG_ANSWERED;
3439         if ( MSG_IS_REPLIED(msginfo->flags) && !(newflags & MSG_REPLIED))
3440                 flags_set |= IMAP_FLAG_ANSWERED;
3441
3442         numlist.next = NULL;
3443         numlist.data = GINT_TO_POINTER(msginfo->msgnum);
3444         
3445         if (flags_set) {
3446                 ok = imap_set_message_flags(session, &numlist, flags_set, TRUE);
3447                 if (ok != IMAP_SUCCESS) return;
3448         }
3449
3450         if (flags_unset) {
3451                 ok = imap_set_message_flags(session, &numlist, flags_unset, FALSE);
3452                 if (ok != IMAP_SUCCESS) return;
3453         }
3454
3455         msginfo->flags.perm_flags = newflags;
3456
3457         return;
3458 }