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