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