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