sync with 0.8.11cvs36
[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                         return IMAP_ERROR;
2709                 else if (cmd_num == imap_cmd_count &&
2710                          !strcmp(cmd_status, "OK")) {
2711                         if (argbuf)
2712                                 g_ptr_array_add(argbuf, g_strdup(buf));
2713                         g_free(buf);
2714                         return IMAP_SUCCESS;
2715                 } else {
2716                         g_free(buf);
2717                         return IMAP_ERROR;
2718                 }
2719         }
2720         g_free(buf);
2721
2722         return ok;
2723 }
2724
2725 static void imap_cmd_gen_send(SockInfo *sock, const gchar *format, ...)
2726 {
2727         gchar buf[IMAPBUFSIZE];
2728         gchar tmp[IMAPBUFSIZE];
2729         gchar *p;
2730         va_list args;
2731
2732         va_start(args, format);
2733         g_vsnprintf(tmp, sizeof(tmp), format, args);
2734         va_end(args);
2735
2736         imap_cmd_count++;
2737
2738         g_snprintf(buf, sizeof(buf), "%d %s\r\n", imap_cmd_count, tmp);
2739         if (!strncasecmp(tmp, "LOGIN ", 6) && (p = strchr(tmp + 6, ' '))) {
2740                 *p = '\0';
2741                 log_print("IMAP4> %d %s ********\n", imap_cmd_count, tmp);
2742         } else
2743                 log_print("IMAP4> %d %s\n", imap_cmd_count, tmp);
2744
2745         sock_write_all(sock, buf, strlen(buf));
2746 }
2747
2748 static gint imap_cmd_gen_recv(SockInfo *sock, gchar **buf)
2749 {
2750         if ((*buf = sock_getline(sock)) == NULL)
2751                 return IMAP_SOCKET;
2752
2753         strretchomp(*buf);
2754
2755         log_print("IMAP4< %s\n", *buf);
2756
2757         return IMAP_SUCCESS;
2758 }
2759
2760
2761 /* misc utility functions */
2762
2763 static gchar *strchr_cpy(const gchar *src, gchar ch, gchar *dest, gint len)
2764 {
2765         gchar *tmp;
2766
2767         dest[0] = '\0';
2768         tmp = strchr(src, ch);
2769         if (!tmp)
2770                 return NULL;
2771
2772         memcpy(dest, src, MIN(tmp - src, len - 1));
2773         dest[MIN(tmp - src, len - 1)] = '\0';
2774
2775         return tmp + 1;
2776 }
2777
2778 static gchar *get_quoted(const gchar *src, gchar ch, gchar *dest, gint len)
2779 {
2780         const gchar *p = src;
2781         gint n = 0;
2782
2783         g_return_val_if_fail(*p == ch, NULL);
2784
2785         *dest = '\0';
2786         p++;
2787
2788         while (*p != '\0' && *p != ch) {
2789                 if (n < len - 1) {
2790                         if (*p == '\\' && *(p + 1) != '\0')
2791                                 p++;
2792                         *dest++ = *p++;
2793                 } else
2794                         p++;
2795                 n++;
2796         }
2797
2798         *dest = '\0';
2799         return (gchar *)(*p == ch ? p + 1 : p);
2800 }
2801
2802 static gchar *search_array_contain_str(GPtrArray *array, const gchar *str)
2803 {
2804         gint i;
2805
2806         for (i = 0; i < array->len; i++) {
2807                 gchar *tmp;
2808
2809                 tmp = g_ptr_array_index(array, i);
2810                 if (strstr(tmp, str) != NULL)
2811                         return tmp;
2812         }
2813
2814         return NULL;
2815 }
2816
2817 static gchar *search_array_str(GPtrArray *array, const gchar *str)
2818 {
2819         gint i;
2820         gint len;
2821
2822         len = strlen(str);
2823
2824         for (i = 0; i < array->len; i++) {
2825                 gchar *tmp;
2826
2827                 tmp = g_ptr_array_index(array, i);
2828                 if (!strncmp(tmp, str, len))
2829                         return tmp;
2830         }
2831
2832         return NULL;
2833 }
2834
2835 static void imap_path_separator_subst(gchar *str, gchar separator)
2836 {
2837         gchar *p;
2838         gboolean in_escape = FALSE;
2839
2840         if (!separator || separator == '/') return;
2841
2842         for (p = str; *p != '\0'; p++) {
2843                 if (*p == '/' && !in_escape)
2844                         *p = separator;
2845                 else if (*p == '&' && *(p + 1) != '-' && !in_escape)
2846                         in_escape = TRUE;
2847                 else if (*p == '-' && in_escape)
2848                         in_escape = FALSE;
2849         }
2850 }
2851
2852 static gchar *imap_modified_utf7_to_locale(const gchar *mutf7_str)
2853 {
2854 #if !HAVE_ICONV
2855         return g_strdup(mutf7_str);
2856 #else
2857         static iconv_t cd = (iconv_t)-1;
2858         static gboolean iconv_ok = TRUE;
2859         GString *norm_utf7;
2860         gchar *norm_utf7_p;
2861         size_t norm_utf7_len;
2862         const gchar *p;
2863         gchar *to_str, *to_p;
2864         size_t to_len;
2865         gboolean in_escape = FALSE;
2866
2867         if (!iconv_ok) return g_strdup(mutf7_str);
2868
2869         if (cd == (iconv_t)-1) {
2870                 cd = iconv_open(conv_get_current_charset_str(), "UTF-7");
2871                 if (cd == (iconv_t)-1) {
2872                         g_warning("iconv cannot convert UTF-7 to %s\n",
2873                                   conv_get_current_charset_str());
2874                         iconv_ok = FALSE;
2875                         return g_strdup(mutf7_str);
2876                 }
2877         }
2878
2879         norm_utf7 = g_string_new(NULL);
2880
2881         for (p = mutf7_str; *p != '\0'; p++) {
2882                 /* replace: '&'  -> '+',
2883                             "&-" -> '&',
2884                             escaped ','  -> '/' */
2885                 if (!in_escape && *p == '&') {
2886                         if (*(p + 1) != '-') {
2887                                 g_string_append_c(norm_utf7, '+');
2888                                 in_escape = TRUE;
2889                         } else {
2890                                 g_string_append_c(norm_utf7, '&');
2891                                 p++;
2892                         }
2893                 } else if (in_escape && *p == ',') {
2894                         g_string_append_c(norm_utf7, '/');
2895                 } else if (in_escape && *p == '-') {
2896                         g_string_append_c(norm_utf7, '-');
2897                         in_escape = FALSE;
2898                 } else {
2899                         g_string_append_c(norm_utf7, *p);
2900                 }
2901         }
2902
2903         norm_utf7_p = norm_utf7->str;
2904         norm_utf7_len = norm_utf7->len;
2905         to_len = strlen(mutf7_str) * 5;
2906         to_p = to_str = g_malloc(to_len + 1);
2907
2908         if (iconv(cd, (ICONV_CONST gchar **)&norm_utf7_p, &norm_utf7_len,
2909                   &to_p, &to_len) == -1) {
2910                 g_warning(_("iconv cannot convert UTF-7 to %s\n"),
2911                           conv_get_current_charset_str());
2912                 g_string_free(norm_utf7, TRUE);
2913                 g_free(to_str);
2914                 return g_strdup(mutf7_str);
2915         }
2916
2917         /* second iconv() call for flushing */
2918         iconv(cd, NULL, NULL, &to_p, &to_len);
2919         g_string_free(norm_utf7, TRUE);
2920         *to_p = '\0';
2921
2922         return to_str;
2923 #endif /* !HAVE_ICONV */
2924 }
2925
2926 static gchar *imap_locale_to_modified_utf7(const gchar *from)
2927 {
2928 #if !HAVE_ICONV
2929         return g_strdup(from);
2930 #else
2931         static iconv_t cd = (iconv_t)-1;
2932         static gboolean iconv_ok = TRUE;
2933         gchar *norm_utf7, *norm_utf7_p;
2934         size_t from_len, norm_utf7_len;
2935         GString *to_str;
2936         gchar *from_tmp, *to, *p;
2937         gboolean in_escape = FALSE;
2938
2939         if (!iconv_ok) return g_strdup(from);
2940
2941         if (cd == (iconv_t)-1) {
2942                 cd = iconv_open("UTF-7", conv_get_current_charset_str());
2943                 if (cd == (iconv_t)-1) {
2944                         g_warning("iconv cannot convert %s to UTF-7\n",
2945                                   conv_get_current_charset_str());
2946                         iconv_ok = FALSE;
2947                         return g_strdup(from);
2948                 }
2949         }
2950
2951         Xstrdup_a(from_tmp, from, return g_strdup(from));
2952         from_len = strlen(from);
2953         norm_utf7_len = from_len * 5;
2954         Xalloca(norm_utf7, norm_utf7_len + 1, return g_strdup(from));
2955         norm_utf7_p = norm_utf7;
2956
2957 #define IS_PRINT(ch) (isprint(ch) && isascii(ch))
2958
2959         while (from_len > 0) {
2960                 if (IS_PRINT(*from_tmp)) {
2961                         /* printable ascii char */
2962                         *norm_utf7_p = *from_tmp;
2963                         norm_utf7_p++;
2964                         from_tmp++;
2965                         from_len--;
2966                 } else {
2967                         size_t mblen;
2968
2969                         /* unprintable char: convert to UTF-7 */
2970                         for (mblen = 0;
2971                              !IS_PRINT(from_tmp[mblen]) && mblen < from_len;
2972                              mblen++)
2973                                 ;
2974                         from_len -= mblen;
2975                         if (iconv(cd, (ICONV_CONST gchar **)&from_tmp, &mblen,
2976                                   &norm_utf7_p, &norm_utf7_len) == -1) {
2977                                 g_warning("iconv cannot convert %s to UTF-7\n",
2978                                           conv_get_current_charset_str());
2979                                 return g_strdup(from);
2980                         }
2981
2982                         /* second iconv() call for flushing */
2983                         iconv(cd, NULL, NULL, &norm_utf7_p, &norm_utf7_len);
2984                 }
2985         }
2986
2987 #undef IS_PRINT
2988
2989         *norm_utf7_p = '\0';
2990         to_str = g_string_new(NULL);
2991         for (p = norm_utf7; p < norm_utf7_p; p++) {
2992                 /* replace: '&' -> "&-",
2993                             '+' -> '&',
2994                             escaped '/' -> ',' */
2995                 if (!in_escape && *p == '&') {
2996                         g_string_append(to_str, "&-");
2997                 } else if (!in_escape && *p == '+') {
2998                         g_string_append_c(to_str, '&');
2999                         in_escape = TRUE;
3000                 } else if (in_escape && *p == '/') {
3001                         g_string_append_c(to_str, ',');
3002                 } else if (in_escape && *p == '-') {
3003                         in_escape = FALSE;
3004                         g_string_append_c(to_str, '-');
3005                 } else {
3006                         g_string_append_c(to_str, *p);
3007                 }
3008         }
3009
3010         if (in_escape) {
3011                 in_escape = FALSE;
3012                 g_string_append_c(to_str, '-');
3013         }
3014
3015         to = to_str->str;
3016         g_string_free(to_str, FALSE);
3017
3018         return to;
3019 #endif /* !HAVE_ICONV */
3020 }
3021
3022 static gboolean imap_rename_folder_func(GNode *node, gpointer data)
3023 {
3024         FolderItem *item = node->data;
3025         gchar **paths = data;
3026         const gchar *oldpath = paths[0];
3027         const gchar *newpath = paths[1];
3028         gchar *base;
3029         gchar *new_itempath;
3030         gint oldpathlen;
3031
3032         oldpathlen = strlen(oldpath);
3033         if (strncmp(oldpath, item->path, oldpathlen) != 0) {
3034                 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
3035                 return TRUE;
3036         }
3037
3038         base = item->path + oldpathlen;
3039         while (*base == G_DIR_SEPARATOR) base++;
3040         if (*base == '\0')
3041                 new_itempath = g_strdup(newpath);
3042         else
3043                 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
3044                                            NULL);
3045         g_free(item->path);
3046         item->path = new_itempath;
3047
3048         return FALSE;
3049 }
3050
3051 static gint get_list_of_uids(Folder *folder, IMAPFolderItem *item, GSList **msgnum_list)
3052 {
3053         gint ok, nummsgs = 0, lastuid_old;
3054         IMAPSession *session;
3055         GSList *uidlist, *elem;
3056         gchar *cmd_buf;
3057
3058         session = imap_session_get(folder);
3059         g_return_val_if_fail(session != NULL, -1);
3060
3061         ok = imap_select(session, IMAP_FOLDER(folder), item->item.path,
3062                          NULL, NULL, NULL, NULL);
3063         if (ok != IMAP_SUCCESS)
3064                 return -1;
3065
3066         cmd_buf = g_strdup_printf("UID %d:*", item->lastuid + 1);
3067         ok = imap_cmd_search(SESSION(session)->sock, cmd_buf, &uidlist);
3068         g_free(cmd_buf);
3069
3070         if (ok == IMAP_SOCKET) {
3071                 session_destroy((Session *)session);
3072                 ((RemoteFolder *)folder)->session = NULL;
3073                 return -1;
3074         }
3075
3076         if (ok != IMAP_SUCCESS) {
3077                 gint i;
3078                 GPtrArray *argbuf;
3079
3080                 argbuf = g_ptr_array_new();
3081
3082                 cmd_buf = g_strdup_printf("UID FETCH %d:* (UID)", item->lastuid + 1);
3083                 imap_cmd_gen_send(SESSION(session)->sock, cmd_buf);
3084                 g_free(cmd_buf);
3085                 ok = imap_cmd_ok(SESSION(session)->sock, argbuf);
3086                 if (ok != IMAP_SUCCESS) {
3087                         ptr_array_free_strings(argbuf);
3088                         g_ptr_array_free(argbuf, TRUE);
3089                         return -1;
3090                 }
3091         
3092                 for(i = 0; i < argbuf->len; i++) {
3093                         int ret, msgnum;
3094         
3095                         if((ret = sscanf(g_ptr_array_index(argbuf, i), 
3096                                     "%*d FETCH (UID %d)", &msgnum)) == 1)
3097                                 uidlist = g_slist_prepend(uidlist, GINT_TO_POINTER(msgnum));
3098                 }
3099                 ptr_array_free_strings(argbuf);
3100                 g_ptr_array_free(argbuf, TRUE);
3101         }
3102
3103         lastuid_old = item->lastuid;
3104         *msgnum_list = g_slist_copy(item->uid_list);
3105         nummsgs = g_slist_length(*msgnum_list);
3106         debug_print("Got %d uids from cache\n", g_slist_length(item->uid_list));
3107
3108         for (elem = uidlist; elem != NULL; elem = g_slist_next(elem)) {
3109                 guint msgnum;
3110
3111                 msgnum = GPOINTER_TO_INT(elem->data);
3112                 if (msgnum > lastuid_old) {
3113                         *msgnum_list = g_slist_prepend(*msgnum_list, GINT_TO_POINTER(msgnum));
3114                         item->uid_list = g_slist_prepend(item->uid_list, GINT_TO_POINTER(msgnum));
3115                         nummsgs++;
3116
3117                         if(msgnum > item->lastuid)
3118                                 item->lastuid = msgnum;
3119                 }
3120         }
3121         g_slist_free(uidlist);
3122
3123         return nummsgs;
3124 }
3125
3126 gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list)
3127 {
3128         IMAPFolderItem *item = (IMAPFolderItem *)_item;
3129         IMAPSession *session;
3130         gint ok, nummsgs = 0, exists, recent, unseen, uid_val, uid_next;
3131         GSList *uidlist;
3132         gchar *dir;
3133
3134         g_return_val_if_fail(folder != NULL, -1);
3135         g_return_val_if_fail(item != NULL, -1);
3136         g_return_val_if_fail(item->item.path != NULL, -1);
3137         g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3138         g_return_val_if_fail(folder->account != NULL, -1);
3139
3140         session = imap_session_get(folder);
3141         g_return_val_if_fail(session != NULL, -1);
3142
3143         ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
3144                          &exists, &recent, &uid_next, &uid_val, &unseen);
3145         if (ok != IMAP_SUCCESS)
3146                 return -1;
3147
3148         /* If old uid_next matches new uid_next we can be sure no message
3149            was added to the folder */
3150         if (uid_next == item->uid_next) {
3151                 nummsgs = g_slist_length(item->uid_list);
3152                 
3153                 /* If number of messages is still the same we
3154                    know our caches message numbers are still valid,
3155                    otherwise if the number of messages has decrease
3156                    we discard our cache to start a new scan to find
3157                    out which numbers have been removed */
3158                 if (exists == nummsgs) {
3159                         *msgnum_list = g_slist_copy(item->uid_list);
3160                         return nummsgs;
3161                 } else if (exists < nummsgs) {
3162                         debug_print("Freeing imap uid cache");
3163                         item->lastuid = 0;
3164                         g_slist_free(item->uid_list);
3165                         item->uid_list = NULL;
3166                 }
3167         }
3168         item->uid_next = uid_next;
3169
3170         if (exists == 0) {
3171                 *msgnum_list = NULL;
3172                 return 0;
3173         }
3174
3175         nummsgs = get_list_of_uids(folder, item, &uidlist);
3176
3177         if (nummsgs != exists) {
3178                 /* Cache contains more messages then folder, we have cached
3179                    an old UID of a message that was removed and new messages
3180                    have been added too, otherwise the uid_next check would
3181                    not have failed */
3182                 debug_print("Freeing imap uid cache");
3183                 item->lastuid = 0;
3184                 g_slist_free(item->uid_list);
3185                 item->uid_list = NULL;
3186
3187                 g_slist_free(*msgnum_list);
3188
3189                 nummsgs = get_list_of_uids(folder, item, &uidlist);
3190         }
3191
3192         *msgnum_list = uidlist;
3193
3194         dir = folder_item_get_path((FolderItem *)item);
3195         debug_print("removing old messages from %s\n", dir);
3196         remove_numbered_files_not_in_list(dir, *msgnum_list);
3197         g_free(dir);
3198
3199         return nummsgs;
3200 }
3201
3202 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item)
3203 {
3204         MsgInfo *msginfo;
3205         MsgFlags flags;
3206
3207         flags.perm_flags = MSG_NEW|MSG_UNREAD;
3208         flags.tmp_flags = 0;
3209
3210         g_return_val_if_fail(item != NULL, NULL);
3211         g_return_val_if_fail(file != NULL, NULL);
3212
3213         if (item->stype == F_QUEUE) {
3214                 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
3215         } else if (item->stype == F_DRAFT) {
3216                 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
3217         }
3218
3219         msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3220         if (!msginfo) return NULL;
3221
3222         msginfo->folder = item;
3223
3224         return msginfo;
3225 }
3226
3227 GSList *imap_get_msginfos(Folder *folder, FolderItem *item, GSList *msgnum_list)
3228 {
3229         IMAPSession *session;
3230         MsgInfoList *ret = NULL;
3231         gint ok;
3232
3233         g_return_val_if_fail(folder != NULL, NULL);
3234         g_return_val_if_fail(item != NULL, NULL);
3235         g_return_val_if_fail(msgnum_list != NULL, NULL);
3236
3237         session = imap_session_get(folder);
3238         g_return_val_if_fail(session != NULL, NULL);
3239
3240         ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3241                          NULL, NULL, NULL, NULL);
3242         if (ok != IMAP_SUCCESS)
3243                 return NULL;
3244
3245         if (!(item->stype == F_QUEUE || item->stype == F_DRAFT)) {
3246                 ret = g_slist_concat(ret,
3247                         imap_get_uncached_messages(
3248                         session, item, msgnum_list));
3249         } else {
3250                 MsgNumberList *sorted_list, *elem;
3251                 gint startnum, lastnum;
3252
3253                 sorted_list = g_slist_sort(g_slist_copy(msgnum_list), g_int_compare);
3254
3255                 startnum = lastnum = GPOINTER_TO_INT(sorted_list->data);
3256
3257                 for (elem = sorted_list;; elem = g_slist_next(elem)) {
3258                         guint num;
3259
3260                         if (elem)
3261                                 num = GPOINTER_TO_INT(elem->data);
3262
3263                         if (num > lastnum + 1 || elem == NULL) {
3264                                 int i;
3265                                 for (i = startnum; i <= lastnum; ++i) {
3266                                         gchar *file;
3267                         
3268                                         file = imap_fetch_msg(folder, item, i);
3269                                         if (file != NULL) {
3270                                                 MsgInfo *msginfo = imap_parse_msg(file, item);
3271                                                 if (msginfo != NULL) {
3272                                                         msginfo->msgnum = i;
3273                                                         ret = g_slist_append(ret, msginfo);
3274                                                 }
3275                                                 g_free(file);
3276                                         }
3277                                 }
3278
3279                                 if (elem == NULL)
3280                                         break;
3281
3282                                 startnum = num;
3283                         }
3284                         lastnum = num;
3285                 }
3286
3287                 g_slist_free(sorted_list);
3288         }
3289
3290         return ret;
3291 }
3292
3293 MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid)
3294 {
3295         MsgInfo *msginfo = NULL;
3296         MsgInfoList *msginfolist;
3297         MsgNumberList numlist;
3298
3299         numlist.next = NULL;
3300         numlist.data = GINT_TO_POINTER(uid);
3301
3302         msginfolist = imap_get_msginfos(folder, item, &numlist);
3303         if (msginfolist != NULL) {
3304                 msginfo = msginfolist->data;
3305                 g_slist_free(msginfolist);
3306         }
3307
3308         return msginfo;
3309 }
3310
3311 gboolean imap_check_msgnum_validity(Folder *folder, FolderItem *_item)
3312 {
3313         IMAPSession *session;
3314         IMAPFolderItem *item = (IMAPFolderItem *)_item;
3315         gint ok, exists = 0, recent = 0, unseen = 0;
3316         guint32 uid_next, uid_validity = 0;
3317         
3318         g_return_val_if_fail(folder != NULL, FALSE);
3319         g_return_val_if_fail(item != NULL, FALSE);
3320         g_return_val_if_fail(item->item.folder != NULL, FALSE);
3321         g_return_val_if_fail(FOLDER_CLASS(item->item.folder) == &imap_class, FALSE);
3322
3323         session = imap_session_get(folder);
3324         g_return_val_if_fail(session != NULL, FALSE);
3325
3326         ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
3327                          &exists, &recent, &uid_next, &uid_validity, &unseen);
3328         if (ok != IMAP_SUCCESS)
3329                 return FALSE;
3330
3331         if(item->item.mtime == uid_validity)
3332                 return TRUE;
3333
3334         debug_print("Freeing imap uid cache");
3335         item->lastuid = 0;
3336         g_slist_free(item->uid_list);
3337         item->uid_list = NULL;
3338                 
3339         item->item.mtime = uid_validity;
3340
3341         imap_delete_all_cached_messages((FolderItem *)item);
3342
3343         return FALSE;
3344 }
3345
3346 void imap_change_flags(Folder *folder, FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
3347 {
3348         IMAPSession *session;
3349         IMAPFlags flags_set = 0, flags_unset = 0;
3350         gint ok = IMAP_SUCCESS;
3351         MsgNumberList numlist;
3352         
3353         g_return_if_fail(folder != NULL);
3354         g_return_if_fail(folder->klass == &imap_class);
3355         g_return_if_fail(item != NULL);
3356         g_return_if_fail(item->folder == folder);
3357         g_return_if_fail(msginfo != NULL);
3358         g_return_if_fail(msginfo->folder == item);
3359
3360         session = imap_session_get(folder);
3361         if (!session) return;
3362
3363         if ((ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
3364             NULL, NULL, NULL, NULL)) != IMAP_SUCCESS)
3365                 return;
3366
3367         if (!MSG_IS_MARKED(msginfo->flags) &&  (newflags & MSG_MARKED))
3368                 flags_set |= IMAP_FLAG_FLAGGED;
3369         if ( MSG_IS_MARKED(msginfo->flags) && !(newflags & MSG_MARKED))
3370                 flags_unset |= IMAP_FLAG_FLAGGED;
3371
3372         if (!MSG_IS_UNREAD(msginfo->flags) &&  (newflags & MSG_UNREAD))
3373                 flags_unset |= IMAP_FLAG_SEEN;
3374         if ( MSG_IS_UNREAD(msginfo->flags) && !(newflags & MSG_UNREAD))
3375                 flags_set |= IMAP_FLAG_SEEN;
3376
3377         if (!MSG_IS_REPLIED(msginfo->flags) &&  (newflags & MSG_REPLIED))
3378                 flags_set |= IMAP_FLAG_ANSWERED;
3379         if ( MSG_IS_REPLIED(msginfo->flags) && !(newflags & MSG_REPLIED))
3380                 flags_set |= IMAP_FLAG_ANSWERED;
3381
3382         numlist.next = NULL;
3383         numlist.data = GINT_TO_POINTER(msginfo->msgnum);
3384         
3385         if (flags_set) {
3386                 ok = imap_set_message_flags(session, &numlist, flags_set, TRUE);
3387                 if (ok != IMAP_SUCCESS) return;
3388         }
3389
3390         if (flags_unset) {
3391                 ok = imap_set_message_flags(session, &numlist, flags_unset, FALSE);
3392                 if (ok != IMAP_SUCCESS) return;
3393         }
3394
3395         msginfo->flags.perm_flags = newflags;
3396
3397         return;
3398 }