2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2003 Hiroyuki Yamamoto
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.
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.
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.
44 #include "procheader.h"
46 #include "prefs_account.h"
49 #include "inputdialog.h"
52 #define IMAP4_PORT 143
54 #define IMAPS_PORT 993
57 #define QUOTE_IF_REQUIRED(out, str) \
59 if (*str != '"' && strpbrk(str, " \t(){}%*") != NULL) { \
63 len = strlen(str) + 3; \
64 Xalloca(__tmp, len, return IMAP_ERROR); \
65 g_snprintf(__tmp, len, "\"%s\"", str); \
68 Xstrdup_a(out, str, return IMAP_ERROR); \
72 struct _IMAPFolderItem
81 static GList *session_list = NULL;
83 static gint imap_cmd_count = 0;
85 static void imap_folder_init (Folder *folder,
89 static FolderItem *imap_folder_item_new (Folder *folder);
90 static void imap_folder_item_destroy (Folder *folder,
93 static IMAPSession *imap_session_get (Folder *folder);
95 static gint imap_scan_tree_recursive (IMAPSession *session,
97 static GSList *imap_parse_list (Folder *folder,
99 const gchar *real_path,
102 static void imap_create_missing_folders (Folder *folder);
103 static FolderItem *imap_create_special_folder
105 SpecialFolderItemType stype,
108 static gint imap_do_copy (Folder *folder,
111 gboolean remove_source);
113 static gint imap_do_copy_msgs_with_dest (Folder *folder,
116 gboolean remove_source);
119 static GSList *imap_get_uncached_messages (IMAPSession *session,
123 static void imap_delete_all_cached_messages (FolderItem *item);
126 static SockInfo *imap_open (const gchar *server,
130 static SockInfo *imap_open (const gchar *server,
135 static SockInfo *imap_open_tunnel(const gchar *server,
136 const gchar *tunnelcmd,
139 static SockInfo *imap_open_tunnel(const gchar *server,
140 const gchar *tunnelcmd);
144 static SockInfo *imap_init_sock(SockInfo *sock, SSLType ssl_type);
146 static SockInfo *imap_init_sock(SockInfo *sock);
149 static gint imap_set_message_flags (IMAPSession *session,
154 static gint imap_select (IMAPSession *session,
160 guint32 *uid_validity);
161 static gint imap_status (IMAPSession *session,
167 guint32 *uid_validity,
170 static void imap_parse_namespace (IMAPSession *session,
172 static void imap_get_namespace_by_list (IMAPSession *session,
174 static IMAPNameSpace *imap_find_namespace (IMAPFolder *folder,
176 static gchar imap_get_path_separator (IMAPFolder *folder,
178 static gchar *imap_get_real_path (IMAPFolder *folder,
181 static gchar *imap_parse_atom (SockInfo *sock,
186 static MsgFlags imap_parse_flags (const gchar *flag_str);
187 static MsgInfo *imap_parse_envelope (SockInfo *sock,
190 static gint imap_greeting (SockInfo *sock,
191 gboolean *is_preauth);
192 static void imap_get_capability (Session *session);
193 static gboolean imap_has_capability (IMAPSession *session,
196 /* low-level IMAP4rev1 commands */
197 static gint imap_cmd_login (SockInfo *sock,
200 static gint imap_cmd_logout (SockInfo *sock);
201 static gint imap_cmd_noop (SockInfo *sock);
202 static gint imap_cmd_starttls (SockInfo *sock);
203 static gint imap_cmd_namespace (SockInfo *sock,
205 static gint imap_cmd_list (SockInfo *sock,
207 const gchar *mailbox,
209 static gint imap_cmd_do_select (SockInfo *sock,
215 guint32 *uid_validity);
216 static gint imap_cmd_select (SockInfo *sock,
221 guint32 *uid_validity);
222 static gint imap_cmd_examine (SockInfo *sock,
227 guint32 *uid_validity);
228 static gint imap_cmd_create (SockInfo *sock,
229 const gchar *folder);
230 static gint imap_cmd_rename (SockInfo *sock,
231 const gchar *oldfolder,
232 const gchar *newfolder);
233 static gint imap_cmd_delete (SockInfo *sock,
234 const gchar *folder);
235 static gint imap_cmd_envelope (SockInfo *sock,
239 static gint imap_cmd_search (SockInfo *sock,
242 static gint imap_cmd_fetch (SockInfo *sock,
244 const gchar *filename);
245 static gint imap_cmd_append (IMAPSession *session,
246 const gchar *destfolder,
249 static gint imap_cmd_copy (IMAPSession *session,
251 const gchar *destfolder,
253 static gint imap_cmd_store (SockInfo *sock,
257 static gint imap_cmd_expunge (SockInfo *sock);
259 static gint imap_cmd_ok (SockInfo *sock,
261 static void imap_cmd_gen_send (SockInfo *sock,
262 const gchar *format, ...);
263 static gint imap_cmd_gen_recv (SockInfo *sock,
266 /* misc utility functions */
267 static gchar *strchr_cpy (const gchar *src,
271 static gchar *get_quoted (const gchar *src,
275 static gchar *search_array_contain_str (GPtrArray *array,
277 static gchar *search_array_str (GPtrArray *array,
279 static void imap_path_separator_subst (gchar *str,
282 static gchar *imap_modified_utf7_to_locale (const gchar *mutf7_str);
283 static gchar *imap_locale_to_modified_utf7 (const gchar *from);
285 static gboolean imap_rename_folder_func (GNode *node,
287 gint imap_get_num_list (Folder *folder,
290 GSList *imap_get_msginfos (Folder *folder,
292 GSList *msgnum_list);
293 MsgInfo *imap_get_msginfo (Folder *folder,
296 gboolean imap_check_msgnum_validity (Folder *folder,
298 void imap_change_flags (Folder *folder,
301 MsgPermFlags newflags);
303 FolderClass imap_class =
309 /* Folder functions */
315 /* FolderItem functions */
316 imap_folder_item_new,
317 imap_folder_item_destroy,
325 imap_check_msgnum_validity,
327 /* Message functions */
343 FolderClass *imap_get_class()
348 Folder *imap_folder_new(const gchar *name, const gchar *path)
352 folder = (Folder *)g_new0(IMAPFolder, 1);
353 folder->class = &imap_class;
354 imap_folder_init(folder, name, path);
359 void imap_folder_destroy(Folder *folder)
363 dir = folder_get_path(folder);
364 if (is_dir_exist(dir))
365 remove_dir_recursive(dir);
368 folder_remote_folder_destroy(REMOTE_FOLDER(folder));
371 static void imap_folder_init(Folder *folder, const gchar *name,
374 folder_remote_folder_init((Folder *)folder, name, path);
377 static FolderItem *imap_folder_item_new(Folder *folder)
379 IMAPFolderItem *item;
381 item = g_new0(IMAPFolderItem, 1);
384 item->uid_list = NULL;
386 return (FolderItem *)item;
389 static void imap_folder_item_destroy(Folder *folder, FolderItem *_item)
391 IMAPFolderItem *item = (IMAPFolderItem *)_item;
393 g_return_if_fail(item != NULL);
394 g_slist_free(item->uid_list);
399 static gboolean imap_reset_uid_lists_func(GNode *node, gpointer data)
401 IMAPFolderItem *item = (IMAPFolderItem *)node->data;
405 g_slist_free(item->uid_list);
406 item->uid_list = NULL;
411 static void imap_reset_uid_lists(Folder *folder)
413 if(folder->node == NULL)
416 /* Destroy all uid lists and rest last uid */
417 g_node_traverse(folder->node, G_IN_ORDER, G_TRAVERSE_ALL, -1, imap_reset_uid_lists_func, NULL);
420 static IMAPSession *imap_session_get(Folder *folder)
422 RemoteFolder *rfolder = REMOTE_FOLDER(folder);
423 Session *session = NULL;
426 g_return_val_if_fail(folder != NULL, NULL);
427 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, NULL);
428 g_return_val_if_fail(folder->account != NULL, NULL);
431 port = folder->account->set_imapport ? folder->account->imapport
432 : folder->account->ssl_imap == SSL_TUNNEL
433 ? IMAPS_PORT : IMAP4_PORT;
435 port = folder->account->set_imapport ? folder->account->imapport
439 /* Make sure we have a session */
440 if (rfolder->session != NULL) {
441 session = rfolder->session;
443 imap_reset_uid_lists(folder);
444 session = imap_session_new(folder->account);
450 imap_get_capability(session);
452 /* Make sure session is authenticated */
453 if (!IMAP_SESSION(session)->authenticated)
454 imap_session_authenticate(IMAP_SESSION(session), folder->account);
455 if (!IMAP_SESSION(session)->authenticated) {
456 session_destroy(session);
457 rfolder->session = NULL;
461 /* Make sure we have parsed the IMAP namespace */
462 imap_parse_namespace(IMAP_SESSION(session),
463 IMAP_FOLDER(folder));
465 /* I think the point of this code is to avoid sending a
466 * keepalive if we've used the session recently and therefore
467 * think it's still alive. Unfortunately, most of the code
468 * does not yet check for errors on the socket, and so if the
469 * connection drops we don't notice until the timeout expires.
470 * A better solution than sending a NOOP every time would be
471 * for every command to be prepared to retry until it is
472 * successfully sent. -- mbp */
473 if (time(NULL) - session->last_access_time > SESSION_TIMEOUT) {
474 /* verify that the session is still alive */
475 if (imap_cmd_noop(session->sock) != IMAP_SUCCESS) {
476 /* Check if this is the first try to establish a
477 connection, if yes we don't try to reconnect */
478 if (rfolder->session == NULL) {
479 log_warning(_("Connecting %s:%d failed"),
480 folder->account->recv_server, port);
481 session_destroy(session);
484 log_warning(_("IMAP4 connection to %s:%d has been"
485 " disconnected. Reconnecting...\n"),
486 folder->account->recv_server, port);
487 session_destroy(session);
488 /* Clear folders session to make imap_session_get create
489 a new session, because of rfolder->session == NULL
490 it will not try to reconnect again and so avoid an
492 rfolder->session = NULL;
493 session = SESSION(imap_session_get(folder));
498 rfolder->session = session;
500 session->last_access_time = time(NULL);
502 return IMAP_SESSION(session);
505 Session *imap_session_new(const PrefsAccount *account)
507 IMAPSession *session;
513 /* FIXME: IMAP over SSL only... */
516 port = account->set_imapport ? account->imapport
517 : account->ssl_imap == SSL_TUNNEL ? IMAPS_PORT : IMAP4_PORT;
518 ssl_type = account->ssl_imap;
520 port = account->set_imapport ? account->imapport
524 if (account->set_tunnelcmd) {
525 log_message(_("creating tunneled IMAP4 connection\n"));
527 if ((imap_sock = imap_open_tunnel(account->recv_server,
531 if ((imap_sock = imap_open_tunnel(account->recv_server,
532 account->tunnelcmd)) == NULL)
536 g_return_val_if_fail(account->recv_server != NULL, NULL);
538 log_message(_("creating IMAP4 connection to %s:%d ...\n"),
539 account->recv_server, port);
542 if ((imap_sock = imap_open(account->recv_server, port,
545 if ((imap_sock = imap_open(account->recv_server, port)) == NULL)
550 /* Only need to log in if the connection was not PREAUTH */
551 if (imap_greeting(imap_sock, &is_preauth) != IMAP_SUCCESS) {
552 sock_close(imap_sock);
555 log_message("IMAP connection is %s-authenticated\n",
556 (is_preauth) ? "pre" : "un");
558 session = g_new0(IMAPSession, 1);
559 session_init(SESSION(session));
560 SESSION(session)->type = SESSION_IMAP;
561 SESSION(session)->server = g_strdup(account->recv_server);
562 SESSION(session)->sock = imap_sock;
564 SESSION(session)->destroy = imap_session_destroy;
566 session->capability = NULL;
568 session->mbox = NULL;
569 session->authenticated = is_preauth;
571 session_list = g_list_append(session_list, session);
573 return SESSION(session);
576 void imap_session_authenticate(IMAPSession *session, const PrefsAccount *account)
580 g_return_if_fail(account->userid != NULL);
582 pass = account->passwd;
585 tmp_pass = input_dialog_query_password(account->recv_server, account->userid);
588 Xstrdup_a(pass, tmp_pass, {g_free(tmp_pass); return;});
592 if (imap_cmd_login(SESSION(session)->sock, account->userid, pass) != IMAP_SUCCESS) {
593 imap_cmd_logout(SESSION(session)->sock);
597 session->authenticated = TRUE;
600 void imap_session_destroy(Session *session)
602 sock_close(session->sock);
603 session->sock = NULL;
605 g_free(IMAP_SESSION(session)->mbox);
607 g_strfreev(IMAP_SESSION(session)->capability);
609 session_list = g_list_remove(session_list, session);
612 void imap_session_destroy_all(void)
614 while (session_list != NULL) {
615 IMAPSession *session = (IMAPSession *)session_list->data;
617 imap_cmd_logout(SESSION(session)->sock);
618 session_destroy(SESSION(session));
622 gchar *imap_fetch_msg(Folder *folder, FolderItem *item, gint uid)
624 gchar *path, *filename;
625 IMAPSession *session;
628 g_return_val_if_fail(folder != NULL, NULL);
629 g_return_val_if_fail(item != NULL, NULL);
631 path = folder_item_get_path(item);
632 if (!is_dir_exist(path))
634 filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(uid), NULL);
637 if (is_file_exist(filename)) {
638 debug_print("message %d has been already cached.\n", uid);
642 session = imap_session_get(folder);
648 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
649 NULL, NULL, NULL, NULL);
650 if (ok != IMAP_SUCCESS) {
651 g_warning("can't select mailbox %s\n", item->path);
656 debug_print("getting message %d...\n", uid);
657 ok = imap_cmd_fetch(SESSION(session)->sock, (guint32)uid, filename);
659 if (ok != IMAP_SUCCESS) {
660 g_warning("can't fetch message %d\n", uid);
668 gint imap_add_msg(Folder *folder, FolderItem *dest, const gchar *file,
669 gboolean remove_source)
672 IMAPSession *session;
675 g_return_val_if_fail(folder != NULL, -1);
676 g_return_val_if_fail(dest != NULL, -1);
677 g_return_val_if_fail(file != NULL, -1);
679 session = imap_session_get(folder);
680 if (!session) return -1;
682 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
683 ok = imap_cmd_append(session, destdir, file, &newuid);
686 if (ok != IMAP_SUCCESS) {
687 g_warning("can't append message %s\n", file);
692 if (unlink(file) < 0)
693 FILE_OP_ERROR(file, "unlink");
699 static gint imap_do_copy(Folder *folder, FolderItem *dest, MsgInfo *msginfo,
700 gboolean remove_source)
703 IMAPSession *session;
704 IMAPFlags iflags = 0;
708 g_return_val_if_fail(folder != NULL, -1);
709 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
710 g_return_val_if_fail(dest != NULL, -1);
711 g_return_val_if_fail(msginfo != NULL, -1);
713 session = imap_session_get(folder);
714 if (!session) return -1;
716 if (msginfo->folder == dest) {
717 g_warning("the src folder is identical to the dest.\n");
721 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
723 /* ensure source folder selected */
724 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
725 NULL, NULL, NULL, NULL);
726 if (ok != IMAP_SUCCESS)
730 debug_print("Moving message %s%c%d to %s ...\n",
731 msginfo->folder->path, G_DIR_SEPARATOR,
732 msginfo->msgnum, destdir);
734 debug_print("Copying message %s%c%d to %s ...\n",
735 msginfo->folder->path, G_DIR_SEPARATOR,
736 msginfo->msgnum, destdir);
738 ok = imap_cmd_copy(session, msginfo->msgnum, destdir, &newuid);
740 if (ok == IMAP_SUCCESS && remove_source) {
741 imap_set_message_flags(session, msginfo->msgnum, msginfo->msgnum,
742 IMAP_FLAG_DELETED, TRUE);
743 ok = imap_cmd_expunge(SESSION(session)->sock);
746 /* get the dest folder to set the flags */
748 ok = imap_select(session, IMAP_FOLDER(folder), dest->path,
749 NULL, NULL, NULL, NULL);
750 if (ok != IMAP_SUCCESS) /* the folder disappeared? */
753 if (msginfo->flags.perm_flags & MSG_MARKED) iflags |= IMAP_FLAG_FLAGGED;
754 if (msginfo->flags.perm_flags & MSG_REPLIED) iflags |= IMAP_FLAG_ANSWERED;
756 if (imap_set_message_flags(session, newuid, newuid, iflags, TRUE)
763 if (ok == IMAP_SUCCESS)
770 static gint imap_do_copy_msgs_with_dest(Folder *folder, FolderItem *dest,
772 gboolean remove_source)
777 IMAPSession *session;
778 gint ok = IMAP_SUCCESS;
780 g_return_val_if_fail(folder != NULL, -1);
781 g_return_val_if_fail(dest != NULL, -1);
782 g_return_val_if_fail(msglist != NULL, -1);
784 session = imap_session_get(folder);
785 if (!session) return -1;
787 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
789 for (cur = msglist; cur != NULL; cur = cur->next) {
790 msginfo = (MsgInfo *)cur->data;
792 if (msginfo->folder == dest) {
793 g_warning("the src folder is identical to the dest.\n");
797 /* ensure source folder selected */
798 ok = imap_select(session, IMAP_FOLDER(folder),
799 msginfo->folder->path, NULL, NULL, NULL, NULL);
802 debug_print("Moving message %s%c%d to %s ...\n",
803 msginfo->folder->path, G_DIR_SEPARATOR,
804 msginfo->msgnum, destdir);
806 debug_print("Copying message %s%c%d to %s ...\n",
807 msginfo->folder->path, G_DIR_SEPARATOR,
808 msginfo->msgnum, destdir);
810 ok = imap_cmd_copy(session, msginfo, destdir);
812 if (ok == IMAP_SUCCESS && remove_source) {
813 imap_set_message_flags
814 (session, msginfo->msgnum, msginfo->msgnum,
815 IMAP_FLAG_DELETED, TRUE);
820 ok = imap_cmd_expunge(SESSION(session)->sock);
824 if (ok == IMAP_SUCCESS)
831 gint imap_move_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
836 g_return_val_if_fail(folder != NULL, -1);
837 g_return_val_if_fail(dest != NULL, -1);
838 g_return_val_if_fail(msginfo != NULL, -1);
839 g_return_val_if_fail(msginfo->folder != NULL, -1);
841 if (folder == msginfo->folder->folder)
842 return imap_do_copy(folder, dest, msginfo, TRUE);
844 srcfile = procmsg_get_message_file(msginfo);
845 if (!srcfile) return -1;
847 ret = imap_add_msg(folder, dest, srcfile, FALSE);
851 if(folder_item_remove_msg(msginfo->folder, msginfo->msgnum)) {
860 gint imap_move_msgs_with_dest(Folder *folder, FolderItem *dest,
867 g_return_val_if_fail(folder != NULL, -1);
868 g_return_val_if_fail(dest != NULL, -1);
869 g_return_val_if_fail(msglist != NULL, -1);
871 msginfo = (MsgInfo *)msglist->data;
872 if (folder == msginfo->folder->folder)
873 return imap_do_copy_msgs_with_dest(folder, dest, msglist, TRUE);
875 for (cur = msglist; cur != NULL; cur = cur->next) {
876 msginfo = (MsgInfo *)cur->data;
877 ret = imap_move_msg(folder, dest, msginfo);
878 if (ret == -1) break;
885 gint imap_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
890 g_return_val_if_fail(folder != NULL, -1);
891 g_return_val_if_fail(dest != NULL, -1);
892 g_return_val_if_fail(msginfo != NULL, -1);
893 g_return_val_if_fail(msginfo->folder != NULL, -1);
895 if (folder == msginfo->folder->folder)
896 return imap_do_copy(folder, dest, msginfo, FALSE);
898 srcfile = procmsg_get_message_file(msginfo);
899 if (!srcfile) return -1;
901 ret = imap_add_msg(folder, dest, srcfile, FALSE);
909 gint imap_copy_msgs_with_dest(Folder *folder, FolderItem *dest,
916 g_return_val_if_fail(folder != NULL, -1);
917 g_return_val_if_fail(dest != NULL, -1);
918 g_return_val_if_fail(msglist != NULL, -1);
920 msginfo = (MsgInfo *)msglist->data;
921 if (folder == msginfo->folder->folder)
922 return imap_do_copy_msgs_with_dest
923 (folder, dest, msglist, FALSE);
925 for (cur = msglist; cur != NULL; cur = cur->next) {
926 msginfo = (MsgInfo *)cur->data;
927 ret = imap_copy_msg(folder, dest, msginfo);
928 if (ret == -1) break;
935 gint imap_remove_msg(Folder *folder, FolderItem *item, gint uid)
938 IMAPSession *session;
941 g_return_val_if_fail(folder != NULL, -1);
942 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
943 g_return_val_if_fail(item != NULL, -1);
945 session = imap_session_get(folder);
946 if (!session) return -1;
948 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
949 NULL, NULL, NULL, NULL);
950 if (ok != IMAP_SUCCESS)
953 ok = imap_set_message_flags
954 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
955 (guint32)uid, (guint32)uid, IMAP_FLAG_DELETED, TRUE);
956 if (ok != IMAP_SUCCESS) {
957 log_warning(_("can't set deleted flags: %d\n"), uid);
961 ok = imap_cmd_expunge(SESSION(session)->sock);
962 if (ok != IMAP_SUCCESS) {
963 log_warning(_("can't expunge\n"));
967 dir = folder_item_get_path(item);
968 if (is_dir_exist(dir))
969 remove_numbered_files(dir, uid, uid);
975 gint imap_remove_msgs(Folder *folder, FolderItem *item, GSList *msglist)
978 IMAPSession *session;
984 g_return_val_if_fail(folder != NULL, -1);
985 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
986 g_return_val_if_fail(item != NULL, -1);
987 g_return_val_if_fail(msglist != NULL, -1);
989 session = imap_session_get(folder);
990 if (!session) return -1;
992 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
993 NULL, NULL, NULL, NULL);
994 if (ok != IMAP_SUCCESS)
997 for (cur = msglist; cur != NULL; cur = cur->next) {
998 msginfo = (MsgInfo *)cur->data;
999 uid = msginfo->msgnum;
1000 ok = imap_set_message_flags
1001 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
1002 uid, uid, IMAP_FLAG_DELETED, TRUE);
1003 if (ok != IMAP_SUCCESS) {
1004 log_warning(_("can't set deleted flags: %d\n"), uid);
1009 ok = imap_cmd_expunge(SESSION(session)->sock);
1010 if (ok != IMAP_SUCCESS) {
1011 log_warning(_("can't expunge\n"));
1015 dir = folder_item_get_path(item);
1016 if (is_dir_exist(dir)) {
1017 for (cur = msglist; cur != NULL; cur = cur->next) {
1018 msginfo = (MsgInfo *)cur->data;
1019 uid = msginfo->msgnum;
1020 remove_numbered_files(dir, uid, uid);
1025 return IMAP_SUCCESS;
1028 gint imap_remove_all_msg(Folder *folder, FolderItem *item)
1030 gint exists, recent, unseen;
1031 guint32 uid_validity;
1033 IMAPSession *session;
1036 g_return_val_if_fail(folder != NULL, -1);
1037 g_return_val_if_fail(item != NULL, -1);
1039 session = imap_session_get(folder);
1040 if (!session) return -1;
1042 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
1043 &exists, &recent, &unseen, &uid_validity);
1044 if (ok != IMAP_SUCCESS)
1047 return IMAP_SUCCESS;
1049 imap_cmd_gen_send(SESSION(session)->sock,
1050 "STORE 1:%d +FLAGS (\\Deleted)", exists);
1051 ok = imap_cmd_ok(SESSION(session)->sock, NULL);
1052 if (ok != IMAP_SUCCESS) {
1053 log_warning(_("can't set deleted flags: 1:%d\n"), exists);
1057 ok = imap_cmd_expunge(SESSION(session)->sock);
1058 if (ok != IMAP_SUCCESS) {
1059 log_warning(_("can't expunge\n"));
1063 dir = folder_item_get_path(item);
1064 if (is_dir_exist(dir))
1065 remove_all_numbered_files(dir);
1068 return IMAP_SUCCESS;
1071 gboolean imap_is_msg_changed(Folder *folder, FolderItem *item, MsgInfo *msginfo)
1073 /* TODO: properly implement this method */
1077 gint imap_scan_folder(Folder *folder, FolderItem *item)
1079 IMAPSession *session;
1080 gint messages, recent, unseen;
1081 guint32 uid_next, uid_validity;
1084 g_return_val_if_fail(folder != NULL, -1);
1085 g_return_val_if_fail(item != NULL, -1);
1087 session = imap_session_get(folder);
1088 if (!session) return -1;
1090 ok = imap_status(session, IMAP_FOLDER(folder), item->path,
1091 &messages, &recent, &uid_next, &uid_validity, &unseen);
1092 if (ok != IMAP_SUCCESS) return -1;
1094 item->new = unseen > 0 ? recent : 0;
1095 item->unread = unseen;
1096 item->total = messages;
1097 item->last_num = (messages > 0 && uid_next > 0) ? uid_next - 1 : 0;
1098 /* item->mtime = uid_validity; */
1103 void imap_scan_tree(Folder *folder)
1106 IMAPSession *session;
1107 gchar *root_folder = NULL;
1109 g_return_if_fail(folder != NULL);
1110 g_return_if_fail(folder->account != NULL);
1112 session = imap_session_get(folder);
1114 if (!folder->node) {
1115 folder_tree_destroy(folder);
1116 item = folder_item_new(folder, folder->name, NULL);
1117 item->folder = folder;
1118 folder->node = g_node_new(item);
1123 if (folder->account->imap_dir && *folder->account->imap_dir) {
1124 Xstrdup_a(root_folder, folder->account->imap_dir, return);
1125 strtailchomp(root_folder, '/');
1126 debug_print("IMAP root directory: %s\n", root_folder);
1129 item = folder_item_new(folder, folder->name, root_folder);
1130 item->folder = folder;
1131 item->no_select = TRUE;
1132 folder->node = g_node_new(item);
1134 imap_scan_tree_recursive(session, item);
1136 imap_create_missing_folders(folder);
1139 static gint imap_scan_tree_recursive(IMAPSession *session, FolderItem *item)
1142 IMAPFolder *imapfolder;
1143 FolderItem *new_item;
1144 GSList *item_list, *cur;
1146 gchar *wildcard_path;
1150 g_return_val_if_fail(item != NULL, -1);
1151 g_return_val_if_fail(item->folder != NULL, -1);
1152 g_return_val_if_fail(item->no_sub == FALSE, -1);
1154 folder = FOLDER(item->folder);
1155 imapfolder = IMAP_FOLDER(folder);
1157 separator = imap_get_path_separator(imapfolder, item->path);
1159 if (item->folder->ui_func)
1160 item->folder->ui_func(folder, item, folder->ui_func_data);
1163 wildcard[0] = separator;
1166 real_path = imap_get_real_path(imapfolder, item->path);
1170 real_path = g_strdup("");
1173 Xstrcat_a(wildcard_path, real_path, wildcard,
1174 {g_free(real_path); return IMAP_ERROR;});
1175 QUOTE_IF_REQUIRED(wildcard_path, wildcard_path);
1177 imap_cmd_gen_send(SESSION(session)->sock, "LIST \"\" %s",
1180 strtailchomp(real_path, separator);
1181 item_list = imap_parse_list(folder, session, real_path, NULL);
1184 for (cur = item_list; cur != NULL; cur = cur->next) {
1185 new_item = cur->data;
1186 if (!strcmp(new_item->path, "INBOX")) {
1187 if (!folder->inbox) {
1188 new_item->stype = F_INBOX;
1189 item->folder->inbox = new_item;
1191 folder_item_destroy(new_item);
1194 } else if (!item->parent || item->stype == F_INBOX) {
1197 base = g_basename(new_item->path);
1199 if (!folder->outbox && !strcasecmp(base, "Sent")) {
1200 new_item->stype = F_OUTBOX;
1201 folder->outbox = new_item;
1202 } else if (!folder->draft && !strcasecmp(base, "Drafts")) {
1203 new_item->stype = F_DRAFT;
1204 folder->draft = new_item;
1205 } else if (!folder->queue && !strcasecmp(base, "Queue")) {
1206 new_item->stype = F_QUEUE;
1207 folder->queue = new_item;
1208 } else if (!folder->trash && !strcasecmp(base, "Trash")) {
1209 new_item->stype = F_TRASH;
1210 folder->trash = new_item;
1213 folder_item_append(item, new_item);
1214 if (new_item->no_select == FALSE)
1215 imap_scan_folder(folder, new_item);
1216 if (new_item->no_sub == FALSE)
1217 imap_scan_tree_recursive(session, new_item);
1220 return IMAP_SUCCESS;
1223 static GSList *imap_parse_list(Folder *folder, IMAPSession *session,
1224 const gchar *real_path, gchar *separator)
1226 gchar buf[IMAPBUFSIZE];
1228 gchar separator_str[16];
1231 gchar *loc_name, *loc_path;
1232 GSList *item_list = NULL;
1234 FolderItem *new_item;
1236 debug_print("getting list of %s ...\n",
1237 *real_path ? real_path : "\"\"");
1239 str = g_string_new(NULL);
1242 if (sock_gets(SESSION(session)->sock, buf, sizeof(buf)) <= 0) {
1243 log_warning(_("error occured while getting LIST.\n"));
1247 if (buf[0] != '*' || buf[1] != ' ') {
1248 log_print("IMAP4< %s\n", buf);
1251 debug_print("IMAP4< %s\n", buf);
1253 g_string_assign(str, buf);
1255 if (strncmp(p, "LIST ", 5) != 0) continue;
1258 if (*p != '(') continue;
1260 p = strchr_cpy(p, ')', flags, sizeof(flags));
1262 while (*p == ' ') p++;
1264 p = strchr_cpy(p, ' ', separator_str, sizeof(separator_str));
1266 extract_quote(separator_str, '"');
1267 if (!strcmp(separator_str, "NIL"))
1268 separator_str[0] = '\0';
1270 *separator = separator_str[0];
1273 while (*p == ' ') p++;
1274 if (*p == '{' || *p == '"')
1275 p = imap_parse_atom(SESSION(session)->sock, p,
1276 buf, sizeof(buf), str);
1278 strncpy2(buf, p, sizeof(buf));
1279 strtailchomp(buf, separator_str[0]);
1280 if (buf[0] == '\0') continue;
1281 if (!strcmp(buf, real_path)) continue;
1283 if (separator_str[0] != '\0')
1284 subst_char(buf, separator_str[0], '/');
1285 name = g_basename(buf);
1286 if (name[0] == '.') continue;
1288 loc_name = imap_modified_utf7_to_locale(name);
1289 loc_path = imap_modified_utf7_to_locale(buf);
1290 new_item = folder_item_new(folder, loc_name, loc_path);
1291 if (strcasestr(flags, "\\Noinferiors") != NULL)
1292 new_item->no_sub = TRUE;
1293 if (strcmp(buf, "INBOX") != 0 &&
1294 strcasestr(flags, "\\Noselect") != NULL)
1295 new_item->no_select = TRUE;
1297 item_list = g_slist_append(item_list, new_item);
1299 debug_print("folder %s has been added.\n", loc_path);
1304 g_string_free(str, TRUE);
1309 gint imap_create_tree(Folder *folder)
1311 g_return_val_if_fail(folder != NULL, -1);
1312 g_return_val_if_fail(folder->node != NULL, -1);
1313 g_return_val_if_fail(folder->node->data != NULL, -1);
1314 g_return_val_if_fail(folder->account != NULL, -1);
1316 imap_scan_tree(folder);
1317 imap_create_missing_folders(folder);
1322 static void imap_create_missing_folders(Folder *folder)
1324 g_return_if_fail(folder != NULL);
1327 folder->inbox = imap_create_special_folder
1328 (folder, F_INBOX, "INBOX");
1330 if (!folder->outbox)
1331 folder->outbox = imap_create_special_folder
1332 (folder, F_OUTBOX, "Sent");
1334 folder->draft = imap_create_special_folder
1335 (folder, F_DRAFT, "Drafts");
1337 folder->queue = imap_create_special_folder
1338 (folder, F_QUEUE, "Queue");
1341 folder->trash = imap_create_special_folder
1342 (folder, F_TRASH, "Trash");
1345 static FolderItem *imap_create_special_folder(Folder *folder,
1346 SpecialFolderItemType stype,
1350 FolderItem *new_item;
1352 g_return_val_if_fail(folder != NULL, NULL);
1353 g_return_val_if_fail(folder->node != NULL, NULL);
1354 g_return_val_if_fail(folder->node->data != NULL, NULL);
1355 g_return_val_if_fail(folder->account != NULL, NULL);
1356 g_return_val_if_fail(name != NULL, NULL);
1358 item = FOLDER_ITEM(folder->node->data);
1359 new_item = imap_create_folder(folder, item, name);
1362 g_warning("Can't create '%s'\n", name);
1363 if (!folder->inbox) return NULL;
1365 new_item = imap_create_folder(folder, folder->inbox, name);
1367 g_warning("Can't create '%s' under INBOX\n", name);
1369 new_item->stype = stype;
1371 new_item->stype = stype;
1376 FolderItem *imap_create_folder(Folder *folder, FolderItem *parent,
1379 gchar *dirpath, *imap_path;
1380 IMAPSession *session;
1381 FolderItem *new_item;
1387 g_return_val_if_fail(folder != NULL, NULL);
1388 g_return_val_if_fail(folder->account != NULL, NULL);
1389 g_return_val_if_fail(parent != NULL, NULL);
1390 g_return_val_if_fail(name != NULL, NULL);
1392 session = imap_session_get(folder);
1393 if (!session) return NULL;
1395 if (!parent->parent && strcmp(name, "INBOX") == 0)
1396 dirpath = g_strdup(name);
1397 else if (parent->path)
1398 dirpath = g_strconcat(parent->path, "/", name, NULL);
1399 else if ((p = strchr(name, '/')) != NULL && *(p + 1) != '\0')
1400 dirpath = g_strdup(name);
1401 else if (folder->account->imap_dir && *folder->account->imap_dir) {
1404 Xstrdup_a(imap_dir, folder->account->imap_dir, return NULL);
1405 strtailchomp(imap_dir, '/');
1406 dirpath = g_strconcat(imap_dir, "/", name, NULL);
1408 dirpath = g_strdup(name);
1410 /* keep trailing directory separator to create a folder that contains
1412 imap_path = imap_locale_to_modified_utf7(dirpath);
1413 strtailchomp(dirpath, '/');
1414 Xstrdup_a(new_name, name, {g_free(dirpath); return NULL;});
1415 strtailchomp(new_name, '/');
1416 separator = imap_get_path_separator(IMAP_FOLDER(folder), imap_path);
1417 imap_path_separator_subst(imap_path, separator);
1418 subst_char(new_name, '/', separator);
1420 if (strcmp(name, "INBOX") != 0) {
1423 gboolean exist = FALSE;
1425 argbuf = g_ptr_array_new();
1426 ok = imap_cmd_list(SESSION(session)->sock, NULL, imap_path,
1428 if (ok != IMAP_SUCCESS) {
1429 log_warning(_("can't create mailbox: LIST failed\n"));
1432 ptr_array_free_strings(argbuf);
1433 g_ptr_array_free(argbuf, TRUE);
1437 for (i = 0; i < argbuf->len; i++) {
1439 str = g_ptr_array_index(argbuf, i);
1440 if (!strncmp(str, "LIST ", 5)) {
1445 ptr_array_free_strings(argbuf);
1446 g_ptr_array_free(argbuf, TRUE);
1449 ok = imap_cmd_create(SESSION(session)->sock, imap_path);
1450 if (ok != IMAP_SUCCESS) {
1451 log_warning(_("can't create mailbox\n"));
1459 new_item = folder_item_new(folder, new_name, dirpath);
1460 folder_item_append(parent, new_item);
1464 dirpath = folder_item_get_path(new_item);
1465 if (!is_dir_exist(dirpath))
1466 make_dir_hier(dirpath);
1472 gint imap_rename_folder(Folder *folder, FolderItem *item, const gchar *name)
1476 gchar *real_oldpath;
1477 gchar *real_newpath;
1480 gchar *old_cache_dir;
1481 gchar *new_cache_dir;
1482 IMAPSession *session;
1485 gint exists, recent, unseen;
1486 guint32 uid_validity;
1488 g_return_val_if_fail(folder != NULL, -1);
1489 g_return_val_if_fail(item != NULL, -1);
1490 g_return_val_if_fail(item->path != NULL, -1);
1491 g_return_val_if_fail(name != NULL, -1);
1493 session = imap_session_get(folder);
1494 if (!session) return -1;
1496 real_oldpath = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1498 g_free(session->mbox);
1499 session->mbox = NULL;
1500 ok = imap_cmd_examine(SESSION(session)->sock, "INBOX",
1501 &exists, &recent, &unseen, &uid_validity);
1502 if (ok != IMAP_SUCCESS) {
1503 g_free(real_oldpath);
1507 separator = imap_get_path_separator(IMAP_FOLDER(folder), item->path);
1508 if (strchr(item->path, G_DIR_SEPARATOR)) {
1509 dirpath = g_dirname(item->path);
1510 newpath = g_strconcat(dirpath, G_DIR_SEPARATOR_S, name, NULL);
1513 newpath = g_strdup(name);
1515 real_newpath = imap_locale_to_modified_utf7(newpath);
1516 imap_path_separator_subst(real_newpath, separator);
1518 ok = imap_cmd_rename(SESSION(session)->sock, real_oldpath, real_newpath);
1519 if (ok != IMAP_SUCCESS) {
1520 log_warning(_("can't rename mailbox: %s to %s\n"),
1521 real_oldpath, real_newpath);
1522 g_free(real_oldpath);
1524 g_free(real_newpath);
1529 item->name = g_strdup(name);
1531 old_cache_dir = folder_item_get_path(item);
1533 node = g_node_find(item->folder->node, G_PRE_ORDER, G_TRAVERSE_ALL,
1535 paths[0] = g_strdup(item->path);
1537 g_node_traverse(node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1538 imap_rename_folder_func, paths);
1540 if (is_dir_exist(old_cache_dir)) {
1541 new_cache_dir = folder_item_get_path(item);
1542 if (rename(old_cache_dir, new_cache_dir) < 0) {
1543 FILE_OP_ERROR(old_cache_dir, "rename");
1545 g_free(new_cache_dir);
1548 g_free(old_cache_dir);
1551 g_free(real_oldpath);
1552 g_free(real_newpath);
1557 gint imap_remove_folder(Folder *folder, FolderItem *item)
1560 IMAPSession *session;
1563 gint exists, recent, unseen;
1564 guint32 uid_validity;
1566 g_return_val_if_fail(folder != NULL, -1);
1567 g_return_val_if_fail(item != NULL, -1);
1568 g_return_val_if_fail(item->path != NULL, -1);
1570 session = imap_session_get(folder);
1571 if (!session) return -1;
1573 path = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1575 ok = imap_cmd_examine(SESSION(session)->sock, "INBOX",
1576 &exists, &recent, &unseen, &uid_validity);
1577 if (ok != IMAP_SUCCESS) {
1582 ok = imap_cmd_delete(SESSION(session)->sock, path);
1583 if (ok != IMAP_SUCCESS) {
1584 log_warning(_("can't delete mailbox\n"));
1590 cache_dir = folder_item_get_path(item);
1591 if (is_dir_exist(cache_dir) && remove_dir_recursive(cache_dir) < 0)
1592 g_warning("can't remove directory '%s'\n", cache_dir);
1594 folder_item_remove(item);
1599 static GSList *imap_get_uncached_messages(IMAPSession *session,
1601 guint32 first_uid, guint32 last_uid)
1604 GSList *newlist = NULL;
1605 GSList *llast = NULL;
1609 g_return_val_if_fail(session != NULL, NULL);
1610 g_return_val_if_fail(item != NULL, NULL);
1611 g_return_val_if_fail(item->folder != NULL, NULL);
1612 g_return_val_if_fail(FOLDER_CLASS(item->folder) == &imap_class, NULL);
1613 g_return_val_if_fail(first_uid <= last_uid, NULL);
1615 if (imap_cmd_envelope(SESSION(session)->sock, first_uid, last_uid)
1617 log_warning(_("can't get envelope\n"));
1621 str = g_string_new(NULL);
1624 if ((tmp = sock_getline(SESSION(session)->sock)) == NULL) {
1625 log_warning(_("error occurred while getting envelope.\n"));
1626 g_string_free(str, TRUE);
1630 if (tmp[0] != '*' || tmp[1] != ' ') {
1631 log_print("IMAP4< %s\n", tmp);
1635 if (strstr(tmp, "FETCH") == NULL) {
1636 log_print("IMAP4< %s\n", tmp);
1640 log_print("IMAP4< %s\n", tmp);
1641 g_string_assign(str, tmp);
1644 msginfo = imap_parse_envelope
1645 (SESSION(session)->sock, item, str);
1647 log_warning(_("can't parse envelope: %s\n"), str->str);
1650 if (item->stype == F_QUEUE) {
1651 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_QUEUED);
1652 } else if (item->stype == F_DRAFT) {
1653 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_DRAFT);
1656 msginfo->folder = item;
1659 llast = newlist = g_slist_append(newlist, msginfo);
1661 llast = g_slist_append(llast, msginfo);
1662 llast = llast->next;
1666 g_string_free(str, TRUE);
1671 static void imap_delete_all_cached_messages(FolderItem *item)
1675 g_return_if_fail(item != NULL);
1676 g_return_if_fail(item->folder != NULL);
1677 g_return_if_fail(FOLDER_CLASS(item->folder) == &imap_class);
1679 debug_print("Deleting all cached messages...\n");
1681 dir = folder_item_get_path(item);
1682 if (is_dir_exist(dir))
1683 remove_all_numbered_files(dir);
1686 debug_print("done.\n");
1690 static SockInfo *imap_open_tunnel(const gchar *server,
1691 const gchar *tunnelcmd,
1694 static SockInfo *imap_open_tunnel(const gchar *server,
1695 const gchar *tunnelcmd)
1700 if ((sock = sock_connect_cmd(server, tunnelcmd)) == NULL) {
1701 log_warning(_("Can't establish IMAP4 session with: %s\n"),
1706 return imap_init_sock(sock, ssl_type);
1708 return imap_init_sock(sock);
1714 static SockInfo *imap_open(const gchar *server, gushort port,
1717 static SockInfo *imap_open(const gchar *server, gushort port)
1722 if ((sock = sock_connect(server, port)) == NULL) {
1723 log_warning(_("Can't connect to IMAP4 server: %s:%d\n"),
1729 if (ssl_type == SSL_TUNNEL && !ssl_init_socket(sock)) {
1730 log_warning(_("Can't establish IMAP4 session with: %s:%d\n"),
1735 return imap_init_sock(sock, ssl_type);
1737 return imap_init_sock(sock);
1742 static SockInfo *imap_init_sock(SockInfo *sock, SSLType ssl_type)
1744 static SockInfo *imap_init_sock(SockInfo *sock)
1749 if (ssl_type == SSL_STARTTLS) {
1752 ok = imap_cmd_starttls(sock);
1753 if (ok != IMAP_SUCCESS) {
1754 log_warning(_("Can't start TLS session.\n"));
1758 if (!ssl_init_socket_with_method(sock, SSL_METHOD_TLSv1)) {
1768 static GList *imap_parse_namespace_str(gchar *str)
1773 IMAPNameSpace *namespace;
1774 GList *ns_list = NULL;
1776 while (*p != '\0') {
1777 /* parse ("#foo" "/") */
1779 while (*p && *p != '(') p++;
1780 if (*p == '\0') break;
1783 while (*p && *p != '"') p++;
1784 if (*p == '\0') break;
1788 while (*p && *p != '"') p++;
1789 if (*p == '\0') break;
1793 while (*p && isspace(*p)) p++;
1794 if (*p == '\0') break;
1795 if (strncmp(p, "NIL", 3) == 0)
1797 else if (*p == '"') {
1800 while (*p && *p != '"') p++;
1801 if (*p == '\0') break;
1806 while (*p && *p != ')') p++;
1807 if (*p == '\0') break;
1810 namespace = g_new(IMAPNameSpace, 1);
1811 namespace->name = g_strdup(name);
1812 namespace->separator = separator ? separator[0] : '\0';
1813 ns_list = g_list_append(ns_list, namespace);
1819 static void imap_parse_namespace(IMAPSession *session, IMAPFolder *folder)
1824 g_return_if_fail(session != NULL);
1825 g_return_if_fail(folder != NULL);
1827 if (folder->ns_personal != NULL ||
1828 folder->ns_others != NULL ||
1829 folder->ns_shared != NULL)
1832 if (!imap_has_capability(session, "NAMESPACE")) {
1833 imap_get_namespace_by_list(session, folder);
1837 if (imap_cmd_namespace(SESSION(session)->sock, &ns_str)
1839 log_warning(_("can't get namespace\n"));
1843 str_array = strsplit_parenthesis(ns_str, '(', ')', 3);
1844 if (str_array == NULL) {
1846 imap_get_namespace_by_list(session, folder);
1850 folder->ns_personal = imap_parse_namespace_str(str_array[0]);
1851 if (str_array[0] && str_array[1])
1852 folder->ns_others = imap_parse_namespace_str(str_array[1]);
1853 if (str_array[0] && str_array[1] && str_array[2])
1854 folder->ns_shared = imap_parse_namespace_str(str_array[2]);
1855 g_strfreev(str_array);
1859 static void imap_get_namespace_by_list(IMAPSession *session, IMAPFolder *folder)
1861 GSList *item_list, *cur;
1862 gchar separator = '\0';
1863 IMAPNameSpace *namespace;
1865 g_return_if_fail(session != NULL);
1866 g_return_if_fail(folder != NULL);
1868 if (folder->ns_personal != NULL ||
1869 folder->ns_others != NULL ||
1870 folder->ns_shared != NULL)
1873 imap_cmd_gen_send(SESSION(session)->sock, "LIST \"\" \"\"");
1874 item_list = imap_parse_list(NULL, session, "", &separator);
1875 for (cur = item_list; cur != NULL; cur = cur->next)
1876 folder_item_destroy(FOLDER_ITEM(cur->data));
1877 g_slist_free(item_list);
1879 namespace = g_new(IMAPNameSpace, 1);
1880 namespace->name = g_strdup("");
1881 namespace->separator = separator;
1882 folder->ns_personal = g_list_append(NULL, namespace);
1885 static IMAPNameSpace *imap_find_namespace_from_list(GList *ns_list,
1888 IMAPNameSpace *namespace = NULL;
1889 gchar *tmp_path, *name;
1891 if (!path) path = "";
1893 Xstrcat_a(tmp_path, path, "/", return NULL);
1895 for (; ns_list != NULL; ns_list = ns_list->next) {
1896 IMAPNameSpace *tmp_ns = ns_list->data;
1898 Xstrdup_a(name, tmp_ns->name, return namespace);
1899 if (tmp_ns->separator && tmp_ns->separator != '/')
1900 subst_char(name, tmp_ns->separator, '/');
1901 if (strncmp(tmp_path, name, strlen(name)) == 0)
1908 static IMAPNameSpace *imap_find_namespace(IMAPFolder *folder,
1911 IMAPNameSpace *namespace;
1913 g_return_val_if_fail(folder != NULL, NULL);
1915 namespace = imap_find_namespace_from_list(folder->ns_personal, path);
1916 if (namespace) return namespace;
1917 namespace = imap_find_namespace_from_list(folder->ns_others, path);
1918 if (namespace) return namespace;
1919 namespace = imap_find_namespace_from_list(folder->ns_shared, path);
1920 if (namespace) return namespace;
1925 static gchar imap_get_path_separator(IMAPFolder *folder, const gchar *path)
1927 IMAPNameSpace *namespace;
1928 gchar separator = '/';
1930 namespace = imap_find_namespace(folder, path);
1931 if (namespace && namespace->separator)
1932 separator = namespace->separator;
1937 static gchar *imap_get_real_path(IMAPFolder *folder, const gchar *path)
1942 g_return_val_if_fail(folder != NULL, NULL);
1943 g_return_val_if_fail(path != NULL, NULL);
1945 real_path = imap_locale_to_modified_utf7(path);
1946 separator = imap_get_path_separator(folder, path);
1947 imap_path_separator_subst(real_path, separator);
1952 static gchar *imap_parse_atom(SockInfo *sock, gchar *src,
1953 gchar *dest, gint dest_len, GString *str)
1955 gchar *cur_pos = src;
1958 g_return_val_if_fail(str != NULL, cur_pos);
1960 /* read the next line if the current response buffer is empty */
1961 while (isspace(*cur_pos)) cur_pos++;
1962 while (*cur_pos == '\0') {
1963 if ((nextline = sock_getline(sock)) == NULL)
1965 g_string_assign(str, nextline);
1967 strretchomp(nextline);
1968 /* log_print("IMAP4< %s\n", nextline); */
1969 debug_print("IMAP4< %s\n", nextline);
1972 while (isspace(*cur_pos)) cur_pos++;
1975 if (!strncmp(cur_pos, "NIL", 3)) {
1978 } else if (*cur_pos == '\"') {
1981 p = get_quoted(cur_pos, '\"', dest, dest_len);
1982 cur_pos = p ? p : cur_pos + 2;
1983 } else if (*cur_pos == '{') {
1988 cur_pos = strchr_cpy(cur_pos + 1, '}', buf, sizeof(buf));
1991 g_string_truncate(str, 0);
1995 if ((nextline = sock_getline(sock)) == NULL)
1997 line_len += strlen(nextline);
1998 g_string_append(str, nextline);
2000 strretchomp(nextline);
2001 /* log_print("IMAP4< %s\n", nextline); */
2002 debug_print("IMAP4< %s\n", nextline);
2004 } while (line_len < len);
2006 memcpy(dest, cur_pos, MIN(len, dest_len - 1));
2007 dest[MIN(len, dest_len - 1)] = '\0';
2014 static gchar *imap_get_header(SockInfo *sock, gchar *cur_pos, gchar **headers,
2024 g_return_val_if_fail(str != NULL, cur_pos);
2026 while (isspace(*cur_pos)) cur_pos++;
2028 g_return_val_if_fail(*cur_pos == '{', cur_pos);
2030 cur_pos = strchr_cpy(cur_pos + 1, '}', buf, sizeof(buf));
2033 g_string_truncate(str, 0);
2037 if ((nextline = sock_getline(sock)) == NULL)
2039 block_len += strlen(nextline);
2040 g_string_append(str, nextline);
2042 strretchomp(nextline);
2043 /* debug_print("IMAP4< %s\n", nextline); */
2045 } while (block_len < len);
2047 debug_print("IMAP4< [contents of BODY.PEEK[HEADER.FIELDS (...)]]\n");
2049 *headers = g_strndup(cur_pos, len);
2052 while (isspace(*cur_pos)) cur_pos++;
2053 while (*cur_pos == '\0') {
2054 if ((nextline = sock_getline(sock)) == NULL)
2056 g_string_assign(str, nextline);
2058 strretchomp(nextline);
2059 debug_print("IMAP4< %s\n", nextline);
2062 while (isspace(*cur_pos)) cur_pos++;
2068 static MsgFlags imap_parse_flags(const gchar *flag_str)
2070 const gchar *p = flag_str;
2071 MsgFlags flags = {0, 0};
2073 flags.perm_flags = MSG_UNREAD;
2075 while ((p = strchr(p, '\\')) != NULL) {
2078 if (g_strncasecmp(p, "Recent", 6) == 0 && MSG_IS_UNREAD(flags)) {
2079 MSG_SET_PERM_FLAGS(flags, MSG_NEW);
2080 } else if (g_strncasecmp(p, "Seen", 4) == 0) {
2081 MSG_UNSET_PERM_FLAGS(flags, MSG_NEW|MSG_UNREAD);
2082 } else if (g_strncasecmp(p, "Deleted", 7) == 0) {
2083 MSG_SET_PERM_FLAGS(flags, MSG_DELETED);
2084 } else if (g_strncasecmp(p, "Flagged", 7) == 0) {
2085 MSG_SET_PERM_FLAGS(flags, MSG_MARKED);
2086 } else if (g_strncasecmp(p, "Answered", 8) == 0) {
2087 MSG_SET_PERM_FLAGS(flags, MSG_REPLIED);
2094 static MsgInfo *imap_parse_envelope(SockInfo *sock, FolderItem *item,
2097 gchar buf[IMAPBUFSIZE];
2098 MsgInfo *msginfo = NULL;
2103 MsgFlags flags = {0, 0}, imap_flags = {0, 0};
2105 g_return_val_if_fail(line_str != NULL, NULL);
2106 g_return_val_if_fail(line_str->str[0] == '*' &&
2107 line_str->str[1] == ' ', NULL);
2109 MSG_SET_TMP_FLAGS(flags, MSG_IMAP);
2110 if (item->stype == F_QUEUE) {
2111 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
2112 } else if (item->stype == F_DRAFT) {
2113 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
2116 cur_pos = line_str->str + 2;
2118 #define PARSE_ONE_ELEMENT(ch) \
2120 cur_pos = strchr_cpy(cur_pos, ch, buf, sizeof(buf)); \
2121 if (cur_pos == NULL) { \
2122 g_warning("cur_pos == NULL\n"); \
2123 procmsg_msginfo_free(msginfo); \
2128 PARSE_ONE_ELEMENT(' ');
2131 PARSE_ONE_ELEMENT(' ');
2132 g_return_val_if_fail(!strcmp(buf, "FETCH"), NULL);
2134 g_return_val_if_fail(*cur_pos == '(', NULL);
2137 while (*cur_pos != '\0' && *cur_pos != ')') {
2138 while (*cur_pos == ' ') cur_pos++;
2140 if (!strncmp(cur_pos, "UID ", 4)) {
2142 uid = strtoul(cur_pos, &cur_pos, 10);
2143 } else if (!strncmp(cur_pos, "FLAGS ", 6)) {
2145 if (*cur_pos != '(') {
2146 g_warning("*cur_pos != '('\n");
2147 procmsg_msginfo_free(msginfo);
2151 PARSE_ONE_ELEMENT(')');
2152 imap_flags = imap_parse_flags(buf);
2153 } else if (!strncmp(cur_pos, "RFC822.SIZE ", 12)) {
2155 size = strtol(cur_pos, &cur_pos, 10);
2156 } else if (!strncmp(cur_pos, "BODY[HEADER.FIELDS ", 19)) {
2160 if (*cur_pos != '(') {
2161 g_warning("*cur_pos != '('\n");
2162 procmsg_msginfo_free(msginfo);
2166 PARSE_ONE_ELEMENT(')');
2167 if (*cur_pos != ']') {
2168 g_warning("*cur_pos != ']'\n");
2169 procmsg_msginfo_free(msginfo);
2174 cur_pos = imap_get_header(sock, cur_pos, &headers,
2176 msginfo = procheader_parse_str(headers, flags, FALSE, FALSE);
2179 g_warning("invalid FETCH response: %s\n", cur_pos);
2185 msginfo->msgnum = uid;
2186 msginfo->size = size;
2187 msginfo->flags.tmp_flags |= imap_flags.tmp_flags;
2188 msginfo->flags.perm_flags = imap_flags.perm_flags;
2194 gint imap_msg_set_perm_flags(MsgInfo *msginfo, MsgPermFlags flags)
2197 IMAPSession *session;
2198 IMAPFlags iflags = 0;
2199 gint ok = IMAP_SUCCESS;
2201 g_return_val_if_fail(msginfo != NULL, -1);
2202 g_return_val_if_fail(msginfo->folder != NULL, -1);
2203 g_return_val_if_fail(msginfo->folder->folder != NULL, -1);
2205 folder = msginfo->folder->folder;
2206 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
2208 session = imap_session_get(folder);
2209 if (!session) return -1;
2211 if ((ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
2212 NULL, NULL, NULL, NULL)) != IMAP_SUCCESS)
2215 if (flags & MSG_MARKED) iflags |= IMAP_FLAG_FLAGGED;
2216 if (flags & MSG_REPLIED) iflags |= IMAP_FLAG_ANSWERED;
2218 ok = imap_set_message_flags(session, msginfo->msgnum,
2219 msginfo->msgnum, iflags, TRUE);
2220 if (ok != IMAP_SUCCESS) return ok;
2223 if (flags & MSG_UNREAD)
2224 ok = imap_set_message_flags(session, msginfo->msgnum,
2225 msginfo->msgnum, IMAP_FLAG_SEEN,
2230 gint imap_msg_unset_perm_flags(MsgInfo *msginfo, MsgPermFlags flags)
2233 IMAPSession *session;
2234 IMAPFlags iflags = 0;
2235 gint ok = IMAP_SUCCESS;
2237 g_return_val_if_fail(msginfo != NULL, -1);
2238 g_return_val_if_fail(msginfo->folder != NULL, -1);
2239 g_return_val_if_fail(msginfo->folder->folder != NULL, -1);
2241 folder = msginfo->folder->folder;
2242 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
2244 session = imap_session_get(folder);
2245 if (!session) return -1;
2247 if (flags & MSG_MARKED) iflags |= IMAP_FLAG_FLAGGED;
2248 if (flags & MSG_REPLIED) iflags |= IMAP_FLAG_ANSWERED;
2250 ok = imap_set_message_flags(session, msginfo->msgnum,
2251 msginfo->msgnum, iflags, FALSE);
2252 if (ok != IMAP_SUCCESS) return ok;
2255 if (flags & MSG_UNREAD)
2256 ok = imap_set_message_flags(session, msginfo->msgnum,
2257 msginfo->msgnum, IMAP_FLAG_SEEN,
2262 static gint imap_set_message_flags(IMAPSession *session,
2271 buf = g_string_new(is_set ? "+FLAGS (" : "-FLAGS (");
2273 if (IMAP_IS_SEEN(flags)) g_string_append(buf, "\\Seen ");
2274 if (IMAP_IS_ANSWERED(flags)) g_string_append(buf, "\\Answered ");
2275 if (IMAP_IS_FLAGGED(flags)) g_string_append(buf, "\\Flagged ");
2276 if (IMAP_IS_DELETED(flags)) g_string_append(buf, "\\Deleted ");
2277 if (IMAP_IS_DRAFT(flags)) g_string_append(buf, "\\Draft");
2279 if (buf->str[buf->len - 1] == ' ')
2280 g_string_truncate(buf, buf->len - 1);
2282 g_string_append_c(buf, ')');
2284 ok = imap_cmd_store(SESSION(session)->sock, first_uid, last_uid,
2286 g_string_free(buf, TRUE);
2291 static gint imap_select(IMAPSession *session, IMAPFolder *folder,
2293 gint *exists, gint *recent, gint *unseen,
2294 guint32 *uid_validity)
2298 gint exists_, recent_, unseen_, uid_validity_;
2300 if (!exists || !recent || !unseen || !uid_validity) {
2301 if (session->mbox && strcmp(session->mbox, path) == 0)
2302 return IMAP_SUCCESS;
2306 uid_validity = &uid_validity_;
2309 g_free(session->mbox);
2310 session->mbox = NULL;
2312 real_path = imap_get_real_path(folder, path);
2313 ok = imap_cmd_select(SESSION(session)->sock, real_path,
2314 exists, recent, unseen, uid_validity);
2315 if (ok != IMAP_SUCCESS)
2316 log_warning(_("can't select folder: %s\n"), real_path);
2318 session->mbox = g_strdup(path);
2324 #define THROW(err) { ok = err; goto catch; }
2326 static gint imap_status(IMAPSession *session, IMAPFolder *folder,
2328 gint *messages, gint *recent,
2329 guint32 *uid_next, guint32 *uid_validity,
2338 *messages = *recent = *uid_next = *uid_validity = *unseen = 0;
2340 argbuf = g_ptr_array_new();
2342 real_path = imap_get_real_path(folder, path);
2343 QUOTE_IF_REQUIRED(real_path_, real_path);
2344 imap_cmd_gen_send(SESSION(session)->sock, "STATUS %s "
2345 "(MESSAGES RECENT UIDNEXT UIDVALIDITY UNSEEN)",
2348 ok = imap_cmd_ok(SESSION(session)->sock, argbuf);
2349 if (ok != IMAP_SUCCESS) THROW(ok);
2351 str = search_array_str(argbuf, "STATUS");
2352 if (!str) THROW(IMAP_ERROR);
2354 str = strchr(str, '(');
2355 if (!str) THROW(IMAP_ERROR);
2357 while (*str != '\0' && *str != ')') {
2358 while (*str == ' ') str++;
2360 if (!strncmp(str, "MESSAGES ", 9)) {
2362 *messages = strtol(str, &str, 10);
2363 } else if (!strncmp(str, "RECENT ", 7)) {
2365 *recent = strtol(str, &str, 10);
2366 } else if (!strncmp(str, "UIDNEXT ", 8)) {
2368 *uid_next = strtoul(str, &str, 10);
2369 } else if (!strncmp(str, "UIDVALIDITY ", 12)) {
2371 *uid_validity = strtoul(str, &str, 10);
2372 } else if (!strncmp(str, "UNSEEN ", 7)) {
2374 *unseen = strtol(str, &str, 10);
2376 g_warning("invalid STATUS response: %s\n", str);
2383 ptr_array_free_strings(argbuf);
2384 g_ptr_array_free(argbuf, TRUE);
2392 /* low-level IMAP4rev1 commands */
2394 static gint imap_cmd_login(SockInfo *sock,
2395 const gchar *user, const gchar *pass)
2397 gchar *user_, *pass_;
2400 QUOTE_IF_REQUIRED(user_, user);
2401 QUOTE_IF_REQUIRED(pass_, pass);
2402 imap_cmd_gen_send(sock, "LOGIN %s %s", user_, pass_);
2404 ok = imap_cmd_ok(sock, NULL);
2405 if (ok != IMAP_SUCCESS)
2406 log_warning(_("IMAP4 login failed.\n"));
2411 static gint imap_cmd_logout(SockInfo *sock)
2413 imap_cmd_gen_send(sock, "LOGOUT");
2414 return imap_cmd_ok(sock, NULL);
2417 /* Send a NOOP, and examine the server's response to see whether this
2418 * connection is pre-authenticated or not. */
2419 static gint imap_greeting(SockInfo *sock, gboolean *is_preauth)
2424 imap_cmd_gen_send(sock, "NOOP");
2425 argbuf = g_ptr_array_new(); /* will hold messages sent back */
2426 r = imap_cmd_ok(sock, argbuf);
2427 *is_preauth = search_array_str(argbuf, "PREAUTH") != NULL;
2429 ptr_array_free_strings(argbuf);
2430 g_ptr_array_free(argbuf, TRUE);
2435 static void imap_get_capability(Session *session)
2440 if (IMAP_SESSION(session)->capability != NULL)
2443 imap_cmd_gen_send(session->sock, "CAPABILITY");
2445 argbuf = g_ptr_array_new();
2447 if (imap_cmd_ok(session->sock, argbuf) != IMAP_SUCCESS ||
2448 ((capstr = search_array_str(argbuf, "CAPABILITY ")) == NULL)) {
2449 ptr_array_free_strings(argbuf);
2450 g_ptr_array_free(argbuf, TRUE);
2454 capstr += strlen("CAPABILITY ");
2456 IMAP_SESSION(session)->capability = g_strsplit(capstr, " ", 0);
2458 ptr_array_free_strings(argbuf);
2459 g_ptr_array_free(argbuf, TRUE);
2462 static gboolean imap_has_capability(IMAPSession *session, const gchar *cap)
2466 for (p = session->capability; *p != NULL; ++p)
2467 if (g_strcasecmp(*p, cap) == 0)
2473 static gint imap_cmd_noop(SockInfo *sock)
2475 imap_cmd_gen_send(sock, "NOOP");
2476 return imap_cmd_ok(sock, NULL);
2479 static gint imap_cmd_starttls(SockInfo *sock)
2481 imap_cmd_gen_send(sock, "STARTTLS");
2482 return imap_cmd_ok(sock, NULL);
2485 #define THROW(err) { ok = err; goto catch; }
2487 static gint imap_cmd_namespace(SockInfo *sock, gchar **ns_str)
2493 argbuf = g_ptr_array_new();
2495 imap_cmd_gen_send(sock, "NAMESPACE");
2496 if ((ok = imap_cmd_ok(sock, argbuf)) != IMAP_SUCCESS) THROW(ok);
2498 str = search_array_str(argbuf, "NAMESPACE");
2499 if (!str) THROW(IMAP_ERROR);
2501 *ns_str = g_strdup(str);
2504 ptr_array_free_strings(argbuf);
2505 g_ptr_array_free(argbuf, TRUE);
2512 static gint imap_cmd_list(SockInfo *sock, const gchar *ref,
2513 const gchar *mailbox, GPtrArray *argbuf)
2515 gchar *ref_, *mailbox_;
2517 if (!ref) ref = "\"\"";
2518 if (!mailbox) mailbox = "\"\"";
2520 QUOTE_IF_REQUIRED(ref_, ref);
2521 QUOTE_IF_REQUIRED(mailbox_, mailbox);
2522 imap_cmd_gen_send(sock, "LIST %s %s", ref_, mailbox_);
2524 return imap_cmd_ok(sock, argbuf);
2527 #define THROW goto catch
2529 static gint imap_cmd_do_select(SockInfo *sock, const gchar *folder,
2531 gint *exists, gint *recent, gint *unseen,
2532 guint32 *uid_validity)
2540 *exists = *recent = *unseen = *uid_validity = 0;
2541 argbuf = g_ptr_array_new();
2544 select_cmd = "EXAMINE";
2546 select_cmd = "SELECT";
2548 QUOTE_IF_REQUIRED(folder_, folder);
2549 imap_cmd_gen_send(sock, "%s %s", select_cmd, folder_);
2551 if ((ok = imap_cmd_ok(sock, argbuf)) != IMAP_SUCCESS) THROW;
2553 resp_str = search_array_contain_str(argbuf, "EXISTS");
2555 if (sscanf(resp_str,"%d EXISTS", exists) != 1) {
2556 g_warning("imap_cmd_select(): invalid EXISTS line.\n");
2561 resp_str = search_array_contain_str(argbuf, "RECENT");
2563 if (sscanf(resp_str, "%d RECENT", recent) != 1) {
2564 g_warning("imap_cmd_select(): invalid RECENT line.\n");
2569 resp_str = search_array_contain_str(argbuf, "UIDVALIDITY");
2571 if (sscanf(resp_str, "OK [UIDVALIDITY %u] ", uid_validity)
2573 g_warning("imap_cmd_select(): invalid UIDVALIDITY line.\n");
2578 resp_str = search_array_contain_str(argbuf, "UNSEEN");
2580 if (sscanf(resp_str, "OK [UNSEEN %d] ", unseen) != 1) {
2581 g_warning("imap_cmd_select(): invalid UNSEEN line.\n");
2587 ptr_array_free_strings(argbuf);
2588 g_ptr_array_free(argbuf, TRUE);
2593 static gint imap_cmd_select(SockInfo *sock, const gchar *folder,
2594 gint *exists, gint *recent, gint *unseen,
2595 guint32 *uid_validity)
2597 return imap_cmd_do_select(sock, folder, FALSE,
2598 exists, recent, unseen, uid_validity);
2601 static gint imap_cmd_examine(SockInfo *sock, const gchar *folder,
2602 gint *exists, gint *recent, gint *unseen,
2603 guint32 *uid_validity)
2605 return imap_cmd_do_select(sock, folder, TRUE,
2606 exists, recent, unseen, uid_validity);
2611 static gint imap_cmd_create(SockInfo *sock, const gchar *folder)
2615 QUOTE_IF_REQUIRED(folder_, folder);
2616 imap_cmd_gen_send(sock, "CREATE %s", folder_);
2618 return imap_cmd_ok(sock, NULL);
2621 static gint imap_cmd_rename(SockInfo *sock, const gchar *old_folder,
2622 const gchar *new_folder)
2624 gchar *old_folder_, *new_folder_;
2626 QUOTE_IF_REQUIRED(old_folder_, old_folder);
2627 QUOTE_IF_REQUIRED(new_folder_, new_folder);
2628 imap_cmd_gen_send(sock, "RENAME %s %s", old_folder_, new_folder_);
2630 return imap_cmd_ok(sock, NULL);
2633 static gint imap_cmd_delete(SockInfo *sock, const gchar *folder)
2637 QUOTE_IF_REQUIRED(folder_, folder);
2638 imap_cmd_gen_send(sock, "DELETE %s", folder_);
2640 return imap_cmd_ok(sock, NULL);
2643 static gint imap_cmd_search(SockInfo *sock, const gchar *criteria, GSList **list)
2649 g_return_val_if_fail(criteria != NULL, IMAP_ERROR);
2650 g_return_val_if_fail(list != NULL, IMAP_ERROR);
2654 argbuf = g_ptr_array_new();
2655 imap_cmd_gen_send(sock, "UID SEARCH %s", criteria);
2657 ok = imap_cmd_ok(sock, argbuf);
2658 if (ok != IMAP_SUCCESS) {
2659 ptr_array_free_strings(argbuf);
2660 g_ptr_array_free(argbuf, TRUE);
2664 if ((uidlist = search_array_str(argbuf, "SEARCH ")) != NULL) {
2665 gchar **strlist, **p;
2667 strlist = g_strsplit(uidlist + 7, " ", 0);
2668 for (p = strlist; *p != NULL; ++p) {
2671 if (sscanf(*p, "%d", &msgnum) == 1)
2672 *list = g_slist_append(*list, GINT_TO_POINTER(msgnum));
2674 g_strfreev(strlist);
2676 ptr_array_free_strings(argbuf);
2677 g_ptr_array_free(argbuf, TRUE);
2679 return IMAP_SUCCESS;
2682 static gint imap_cmd_fetch(SockInfo *sock, guint32 uid, const gchar *filename)
2690 g_return_val_if_fail(filename != NULL, IMAP_ERROR);
2692 imap_cmd_gen_send(sock, "UID FETCH %d BODY[]", uid);
2694 while ((ok = imap_cmd_gen_recv(sock, &buf))
2696 if (buf[0] != '*' || buf[1] != ' ') {
2700 if (strstr(buf, "FETCH") != NULL)
2703 if (ok != IMAP_SUCCESS)
2706 cur_pos = strchr(buf, '{');
2707 if (cur_pos == NULL) {
2711 cur_pos = strchr_cpy(cur_pos + 1, '}', size_str, sizeof(size_str));
2712 if (cur_pos == NULL) {
2716 size_num = atol(size_str);
2718 if (*cur_pos != '\0') {
2723 if (recv_bytes_write_to_file(sock, size_num, filename) != 0) {
2728 if (imap_cmd_gen_recv(sock, &buf) != IMAP_SUCCESS) {
2733 if (buf[0] == '\0' || buf[strlen(buf) - 1] != ')') {
2739 ok = imap_cmd_ok(sock, NULL);
2744 static gint imap_cmd_append(IMAPSession *session, const gchar *destfolder,
2745 const gchar *file, gint32 *new_uid)
2750 gchar buf[BUFFSIZE], *imapbuf;
2755 g_return_val_if_fail(file != NULL, IMAP_ERROR);
2757 size = get_file_size_as_crlf(file);
2758 if ((fp = fopen(file, "rb")) == NULL) {
2759 FILE_OP_ERROR(file, "fopen");
2762 QUOTE_IF_REQUIRED(destfolder_, destfolder);
2763 imap_cmd_gen_send(SESSION(session)->sock, "APPEND %s (\\Seen) {%d}", destfolder_, size);
2765 ok = imap_cmd_gen_recv(SESSION(session)->sock, &imapbuf);
2766 if (ok != IMAP_SUCCESS || imapbuf[0] != '+' || imapbuf[1] != ' ') {
2767 log_warning(_("can't append %s to %s\n"), file, destfolder_);
2774 log_print("IMAP4> %s\n", _("(sending file...)"));
2776 while (fgets(buf, sizeof(buf), fp) != NULL) {
2778 if (sock_puts(SESSION(session)->sock, buf) < 0) {
2785 FILE_OP_ERROR(file, "fgets");
2790 sock_puts(SESSION(session)->sock, "");
2794 reply = g_ptr_array_new();
2797 ok = imap_cmd_ok(SESSION(session)->sock, reply);
2798 if (ok != IMAP_SUCCESS)
2799 log_warning(_("can't append message to %s\n"), destfolder_);
2801 (new_uid != NULL) &&
2802 (imap_has_capability(session, "UIDPLUS") && reply->len > 0) &&
2803 ((okmsginfo = g_ptr_array_index(reply, reply->len - 1)) != NULL) &&
2804 (sscanf(okmsginfo, "%*u OK [APPENDUID %*u %u]", &newuid) == 1)) {
2808 ptr_array_free_strings(reply);
2809 g_ptr_array_free(reply, TRUE);
2814 static gint imap_cmd_copy(IMAPSession * session,
2816 const gchar * destfolder, gint32 * new_uid)
2819 gint32 olduid, newuid;
2824 g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
2825 g_return_val_if_fail(session != NULL, IMAP_ERROR);
2826 g_return_val_if_fail(new_uid != NULL, IMAP_ERROR);
2828 QUOTE_IF_REQUIRED(destfolder_, destfolder);
2829 imap_cmd_gen_send(SESSION(session)->sock, "UID COPY %d %s", msgnum, destfolder_);
2831 reply = g_ptr_array_new();
2834 ok = imap_cmd_ok(SESSION(session)->sock, reply);
2835 if (ok != IMAP_SUCCESS)
2836 log_warning(_("can't copy %d to %s\n"), msgnum, destfolder_);
2837 else if (imap_has_capability(session, "UIDPLUS") && reply->len > 0)
2838 if ((okmsginfo = g_ptr_array_index(reply, reply->len - 1)) != NULL &&
2839 sscanf(okmsginfo, "%*u OK [COPYUID %*u %u %u]", &olduid, &newuid) == 2 &&
2843 ptr_array_free_strings(reply);
2844 g_ptr_array_free(reply, TRUE);
2848 gint imap_cmd_envelope(SockInfo *sock, guint32 first_uid, guint32 last_uid)
2850 static GString *header_fields = NULL;
2852 if (header_fields == NULL) {
2853 const HeaderEntry *headers, *elem;
2855 headers = procheader_get_headernames(FALSE);
2856 header_fields = g_string_new("");
2858 for (elem = headers; elem->name != NULL; ++elem) {
2859 gint namelen = strlen(elem->name);
2861 /* Header fields ending with space are not rfc822 headers */
2862 if (elem->name[namelen - 1] == ' ')
2865 /* strip : at the of header field */
2866 if(elem->name[namelen - 1] == ':')
2872 g_string_sprintfa(header_fields, "%s%.*s",
2873 header_fields->str[0] != '\0' ? " " : "",
2874 namelen, elem->name);
2879 (sock, "UID FETCH %d:%d (UID FLAGS RFC822.SIZE BODY.PEEK[HEADER.FIELDS (%s)])",
2880 first_uid, last_uid,
2881 header_fields->str);
2883 return IMAP_SUCCESS;
2886 static gint imap_cmd_store(SockInfo *sock, guint32 first_uid, guint32 last_uid,
2891 imap_cmd_gen_send(sock, "UID STORE %d:%d %s",
2892 first_uid, last_uid, sub_cmd);
2894 if ((ok = imap_cmd_ok(sock, NULL)) != IMAP_SUCCESS) {
2895 log_warning(_("error while imap command: STORE %d:%d %s\n"),
2896 first_uid, last_uid, sub_cmd);
2900 return IMAP_SUCCESS;
2903 static gint imap_cmd_expunge(SockInfo *sock)
2907 imap_cmd_gen_send(sock, "EXPUNGE");
2908 if ((ok = imap_cmd_ok(sock, NULL)) != IMAP_SUCCESS) {
2909 log_warning(_("error while imap command: EXPUNGE\n"));
2913 return IMAP_SUCCESS;
2916 static gint imap_cmd_ok(SockInfo *sock, GPtrArray *argbuf)
2921 gchar cmd_status[IMAPBUFSIZE];
2923 while ((ok = imap_cmd_gen_recv(sock, &buf))
2925 if (buf[0] == '*' && buf[1] == ' ') {
2927 g_ptr_array_add(argbuf, g_strdup(buf + 2));
2931 if (sscanf(buf, "%d %s", &cmd_num, cmd_status) < 2)
2933 else if (cmd_num == imap_cmd_count &&
2934 !strcmp(cmd_status, "OK")) {
2936 g_ptr_array_add(argbuf, g_strdup(buf));
2938 return IMAP_SUCCESS;
2949 static void imap_cmd_gen_send(SockInfo *sock, const gchar *format, ...)
2951 gchar buf[IMAPBUFSIZE];
2952 gchar tmp[IMAPBUFSIZE];
2956 va_start(args, format);
2957 g_vsnprintf(tmp, sizeof(tmp), format, args);
2962 g_snprintf(buf, sizeof(buf), "%d %s\r\n", imap_cmd_count, tmp);
2963 if (!strncasecmp(tmp, "LOGIN ", 6) && (p = strchr(tmp + 6, ' '))) {
2965 log_print("IMAP4> %d %s ********\n", imap_cmd_count, tmp);
2967 log_print("IMAP4> %d %s\n", imap_cmd_count, tmp);
2969 sock_write_all(sock, buf, strlen(buf));
2972 static gint imap_cmd_gen_recv(SockInfo *sock, gchar **buf)
2974 if ((*buf = sock_getline(sock)) == NULL)
2979 log_print("IMAP4< %s\n", *buf);
2981 return IMAP_SUCCESS;
2985 /* misc utility functions */
2987 static gchar *strchr_cpy(const gchar *src, gchar ch, gchar *dest, gint len)
2992 tmp = strchr(src, ch);
2996 memcpy(dest, src, MIN(tmp - src, len - 1));
2997 dest[MIN(tmp - src, len - 1)] = '\0';
3002 static gchar *get_quoted(const gchar *src, gchar ch, gchar *dest, gint len)
3004 const gchar *p = src;
3007 g_return_val_if_fail(*p == ch, NULL);
3012 while (*p != '\0' && *p != ch) {
3014 if (*p == '\\' && *(p + 1) != '\0')
3023 return (gchar *)(*p == ch ? p + 1 : p);
3026 static gchar *search_array_contain_str(GPtrArray *array, const gchar *str)
3030 for (i = 0; i < array->len; i++) {
3033 tmp = g_ptr_array_index(array, i);
3034 if (strstr(tmp, str) != NULL)
3041 static gchar *search_array_str(GPtrArray *array, const gchar *str)
3048 for (i = 0; i < array->len; i++) {
3051 tmp = g_ptr_array_index(array, i);
3052 if (!strncmp(tmp, str, len))
3059 static void imap_path_separator_subst(gchar *str, gchar separator)
3062 gboolean in_escape = FALSE;
3064 if (!separator || separator == '/') return;
3066 for (p = str; *p != '\0'; p++) {
3067 if (*p == '/' && !in_escape)
3069 else if (*p == '&' && *(p + 1) != '-' && !in_escape)
3071 else if (*p == '-' && in_escape)
3076 static gchar *imap_modified_utf7_to_locale(const gchar *mutf7_str)
3079 return g_strdup(mutf7_str);
3081 static iconv_t cd = (iconv_t)-1;
3082 static gboolean iconv_ok = TRUE;
3085 size_t norm_utf7_len;
3087 gchar *to_str, *to_p;
3089 gboolean in_escape = FALSE;
3091 if (!iconv_ok) return g_strdup(mutf7_str);
3093 if (cd == (iconv_t)-1) {
3094 cd = iconv_open(conv_get_current_charset_str(), "UTF-7");
3095 if (cd == (iconv_t)-1) {
3096 g_warning("iconv cannot convert UTF-7 to %s\n",
3097 conv_get_current_charset_str());
3099 return g_strdup(mutf7_str);
3103 norm_utf7 = g_string_new(NULL);
3105 for (p = mutf7_str; *p != '\0'; p++) {
3106 /* replace: '&' -> '+',
3108 escaped ',' -> '/' */
3109 if (!in_escape && *p == '&') {
3110 if (*(p + 1) != '-') {
3111 g_string_append_c(norm_utf7, '+');
3114 g_string_append_c(norm_utf7, '&');
3117 } else if (in_escape && *p == ',') {
3118 g_string_append_c(norm_utf7, '/');
3119 } else if (in_escape && *p == '-') {
3120 g_string_append_c(norm_utf7, '-');
3123 g_string_append_c(norm_utf7, *p);
3127 norm_utf7_p = norm_utf7->str;
3128 norm_utf7_len = norm_utf7->len;
3129 to_len = strlen(mutf7_str) * 5;
3130 to_p = to_str = g_malloc(to_len + 1);
3132 if (iconv(cd, (ICONV_CONST gchar **)&norm_utf7_p, &norm_utf7_len,
3133 &to_p, &to_len) == -1) {
3134 g_warning(_("iconv cannot convert UTF-7 to %s\n"),
3135 conv_get_current_charset_str());
3136 g_string_free(norm_utf7, TRUE);
3138 return g_strdup(mutf7_str);
3141 /* second iconv() call for flushing */
3142 iconv(cd, NULL, NULL, &to_p, &to_len);
3143 g_string_free(norm_utf7, TRUE);
3147 #endif /* !HAVE_ICONV */
3150 static gchar *imap_locale_to_modified_utf7(const gchar *from)
3153 return g_strdup(from);
3155 static iconv_t cd = (iconv_t)-1;
3156 static gboolean iconv_ok = TRUE;
3157 gchar *norm_utf7, *norm_utf7_p;
3158 size_t from_len, norm_utf7_len;
3160 gchar *from_tmp, *to, *p;
3161 gboolean in_escape = FALSE;
3163 if (!iconv_ok) return g_strdup(from);
3165 if (cd == (iconv_t)-1) {
3166 cd = iconv_open("UTF-7", conv_get_current_charset_str());
3167 if (cd == (iconv_t)-1) {
3168 g_warning("iconv cannot convert %s to UTF-7\n",
3169 conv_get_current_charset_str());
3171 return g_strdup(from);
3175 Xstrdup_a(from_tmp, from, return g_strdup(from));
3176 from_len = strlen(from);
3177 norm_utf7_len = from_len * 5;
3178 Xalloca(norm_utf7, norm_utf7_len + 1, return g_strdup(from));
3179 norm_utf7_p = norm_utf7;
3181 #define IS_PRINT(ch) (isprint(ch) && isascii(ch))
3183 while (from_len > 0) {
3184 if (IS_PRINT(*from_tmp)) {
3185 /* printable ascii char */
3186 *norm_utf7_p = *from_tmp;
3193 /* unprintable char: convert to UTF-7 */
3195 !IS_PRINT(from_tmp[mblen]) && mblen < from_len;
3199 if (iconv(cd, (ICONV_CONST gchar **)&from_tmp, &mblen,
3200 &norm_utf7_p, &norm_utf7_len) == -1) {
3201 g_warning("iconv cannot convert %s to UTF-7\n",
3202 conv_get_current_charset_str());
3203 return g_strdup(from);
3206 /* second iconv() call for flushing */
3207 iconv(cd, NULL, NULL, &norm_utf7_p, &norm_utf7_len);
3213 *norm_utf7_p = '\0';
3214 to_str = g_string_new(NULL);
3215 for (p = norm_utf7; p < norm_utf7_p; p++) {
3216 /* replace: '&' -> "&-",
3218 escaped '/' -> ',' */
3219 if (!in_escape && *p == '&') {
3220 g_string_append(to_str, "&-");
3221 } else if (!in_escape && *p == '+') {
3222 g_string_append_c(to_str, '&');
3224 } else if (in_escape && *p == '/') {
3225 g_string_append_c(to_str, ',');
3226 } else if (in_escape && *p == '-') {
3228 g_string_append_c(to_str, '-');
3230 g_string_append_c(to_str, *p);
3236 g_string_append_c(to_str, '-');
3240 g_string_free(to_str, FALSE);
3243 #endif /* !HAVE_ICONV */
3246 static gboolean imap_rename_folder_func(GNode *node, gpointer data)
3248 FolderItem *item = node->data;
3249 gchar **paths = data;
3250 const gchar *oldpath = paths[0];
3251 const gchar *newpath = paths[1];
3253 gchar *new_itempath;
3256 oldpathlen = strlen(oldpath);
3257 if (strncmp(oldpath, item->path, oldpathlen) != 0) {
3258 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
3262 base = item->path + oldpathlen;
3263 while (*base == G_DIR_SEPARATOR) base++;
3265 new_itempath = g_strdup(newpath);
3267 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
3270 item->path = new_itempath;
3275 static gint get_list_of_uids(Folder *folder, IMAPFolderItem *item, GSList **msgnum_list)
3277 gint ok, nummsgs = 0, lastuid_old;
3278 IMAPSession *session;
3279 GSList *uidlist, *elem;
3282 session = imap_session_get(folder);
3283 g_return_val_if_fail(session != NULL, -1);
3285 ok = imap_select(session, IMAP_FOLDER(folder), item->item.path,
3286 NULL, NULL, NULL, NULL);
3287 if (ok != IMAP_SUCCESS)
3290 cmd_buf = g_strdup_printf("UID %d:*", item->lastuid + 1);
3291 ok = imap_cmd_search(SESSION(session)->sock, cmd_buf, &uidlist);
3294 if (ok == IMAP_SOCKET) {
3295 session_destroy((Session *)session);
3296 ((RemoteFolder *)folder)->session = NULL;
3300 if (ok != IMAP_SUCCESS) {
3304 argbuf = g_ptr_array_new();
3306 cmd_buf = g_strdup_printf("UID FETCH %d:* (UID)", item->lastuid + 1);
3307 imap_cmd_gen_send(SESSION(session)->sock, cmd_buf);
3309 ok = imap_cmd_ok(SESSION(session)->sock, argbuf);
3310 if (ok != IMAP_SUCCESS) {
3311 ptr_array_free_strings(argbuf);
3312 g_ptr_array_free(argbuf, TRUE);
3316 for(i = 0; i < argbuf->len; i++) {
3319 if((ret = sscanf(g_ptr_array_index(argbuf, i),
3320 "%*d FETCH (UID %d)", &msgnum)) == 1)
3321 uidlist = g_slist_prepend(uidlist, GINT_TO_POINTER(msgnum));
3323 ptr_array_free_strings(argbuf);
3324 g_ptr_array_free(argbuf, TRUE);
3327 lastuid_old = item->lastuid;
3328 *msgnum_list = g_slist_copy(item->uid_list);
3329 nummsgs = g_slist_length(*msgnum_list);
3330 debug_print("Got %d uids from cache\n", g_slist_length(item->uid_list));
3332 for (elem = uidlist; elem != NULL; elem = g_slist_next(elem)) {
3335 msgnum = GPOINTER_TO_INT(elem->data);
3336 if (msgnum > lastuid_old) {
3337 *msgnum_list = g_slist_prepend(*msgnum_list, GINT_TO_POINTER(msgnum));
3338 item->uid_list = g_slist_prepend(item->uid_list, GINT_TO_POINTER(msgnum));
3341 if(msgnum > item->lastuid)
3342 item->lastuid = msgnum;
3345 g_slist_free(uidlist);
3350 gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list)
3352 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3353 IMAPSession *session;
3354 gint ok, nummsgs = 0, exists, recent, unseen, uid_val, uid_next;
3358 g_return_val_if_fail(folder != NULL, -1);
3359 g_return_val_if_fail(item != NULL, -1);
3360 g_return_val_if_fail(item->item.path != NULL, -1);
3361 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3362 g_return_val_if_fail(folder->account != NULL, -1);
3364 session = imap_session_get(folder);
3365 g_return_val_if_fail(session != NULL, -1);
3367 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
3368 &exists, &recent, &uid_next, &uid_val, &unseen);
3369 if (ok != IMAP_SUCCESS)
3372 /* If old uid_next matches new uid_next we can be sure no message
3373 was added to the folder */
3374 if (uid_next == item->uid_next) {
3375 nummsgs = g_slist_length(item->uid_list);
3377 /* If number of messages is still the same we
3378 know our caches message numbers are still valid,
3379 otherwise if the number of messages has decrease
3380 we discard our cache to start a new scan to find
3381 out which numbers have been removed */
3382 if (exists == nummsgs) {
3383 *msgnum_list = g_slist_copy(item->uid_list);
3385 } else if (exists < nummsgs) {
3386 debug_print("Freeing imap uid cache");
3388 g_slist_free(item->uid_list);
3389 item->uid_list = NULL;
3392 item->uid_next = uid_next;
3395 *msgnum_list = NULL;
3399 nummsgs = get_list_of_uids(folder, item, &uidlist);
3401 if (nummsgs != exists) {
3402 /* Cache contains more messages then folder, we have cached
3403 an old UID of a message that was removed and new messages
3404 have been added too, otherwise the uid_next check would
3406 debug_print("Freeing imap uid cache");
3408 g_slist_free(item->uid_list);
3409 item->uid_list = NULL;
3411 g_slist_free(*msgnum_list);
3413 nummsgs = get_list_of_uids(folder, item, &uidlist);
3416 *msgnum_list = uidlist;
3418 dir = folder_item_get_path((FolderItem *)item);
3419 debug_print("removing old messages from %s\n", dir);
3420 remove_numbered_files_not_in_list(dir, *msgnum_list);
3426 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item)
3431 flags.perm_flags = MSG_NEW|MSG_UNREAD;
3432 flags.tmp_flags = 0;
3434 g_return_val_if_fail(item != NULL, NULL);
3435 g_return_val_if_fail(file != NULL, NULL);
3437 if (item->stype == F_QUEUE) {
3438 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
3439 } else if (item->stype == F_DRAFT) {
3440 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
3443 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3444 if (!msginfo) return NULL;
3446 msginfo->folder = item;
3451 static int compare_uint(gconstpointer a, gconstpointer b)
3456 GSList *imap_get_msginfos(Folder *folder, FolderItem *item, GSList *msgnum_list)
3458 IMAPSession *session;
3459 GSList *sorted_list, *elem, *ret = NULL;
3460 gint ok, startnum, lastnum;
3462 g_return_val_if_fail(folder != NULL, NULL);
3463 g_return_val_if_fail(item != NULL, NULL);
3464 g_return_val_if_fail(msgnum_list != NULL, NULL);
3466 session = imap_session_get(folder);
3467 g_return_val_if_fail(session != NULL, NULL);
3469 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3470 NULL, NULL, NULL, NULL);
3471 if (ok != IMAP_SUCCESS)
3474 sorted_list = g_slist_sort(g_slist_copy(msgnum_list), compare_uint);
3476 startnum = lastnum = GPOINTER_TO_INT(sorted_list->data);
3478 for (elem = sorted_list;; elem = g_slist_next(elem)) {
3482 num = GPOINTER_TO_INT(elem->data);
3484 if (num > lastnum + 1 || elem == NULL) {
3485 if (!(item->stype == F_QUEUE || item->stype == F_DRAFT)) {
3486 ret = g_slist_concat(ret,
3487 imap_get_uncached_messages(
3488 session, item, startnum, lastnum));
3491 for (i = startnum; i <= lastnum; ++i) {
3494 file = imap_fetch_msg(folder, item, i);
3496 MsgInfo *msginfo = imap_parse_msg(file, item);
3497 if (msginfo != NULL) {
3498 msginfo->msgnum = i;
3499 ret = g_slist_append(ret, msginfo);
3514 g_slist_free(sorted_list);
3519 MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid)
3521 IMAPSession *session;
3522 MsgInfo *msginfo = NULL;
3525 g_return_val_if_fail(folder != NULL, NULL);
3526 g_return_val_if_fail(item != NULL, NULL);
3528 session = imap_session_get(folder);
3529 g_return_val_if_fail(session != NULL, NULL);
3531 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3532 NULL, NULL, NULL, NULL);
3533 if (ok != IMAP_SUCCESS)
3536 if (!(item->stype == F_QUEUE || item->stype == F_DRAFT)) {
3539 list = imap_get_uncached_messages(session, item, uid, uid);
3541 msginfo = (MsgInfo *)list->data;
3544 procmsg_msg_list_free(list);
3548 file = imap_fetch_msg(folder, item, uid);
3550 msginfo = imap_parse_msg(file, item);
3551 if (msginfo != NULL)
3552 msginfo->msgnum = uid;
3560 gboolean imap_check_msgnum_validity(Folder *folder, FolderItem *_item)
3562 IMAPSession *session;
3563 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3564 gint ok, exists = 0, recent = 0, unseen = 0;
3565 guint32 uid_next, uid_validity = 0;
3567 g_return_val_if_fail(folder != NULL, FALSE);
3568 g_return_val_if_fail(item != NULL, FALSE);
3569 g_return_val_if_fail(item->item.folder != NULL, FALSE);
3570 g_return_val_if_fail(FOLDER_CLASS(item->item.folder) == &imap_class, FALSE);
3572 session = imap_session_get(folder);
3573 g_return_val_if_fail(session != NULL, FALSE);
3575 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
3576 &exists, &recent, &uid_next, &uid_validity, &unseen);
3577 if (ok != IMAP_SUCCESS)
3580 if(item->item.mtime == uid_validity)
3583 debug_print("Freeing imap uid cache");
3585 g_slist_free(item->uid_list);
3586 item->uid_list = NULL;
3588 item->item.mtime = uid_validity;
3590 imap_delete_all_cached_messages((FolderItem *)item);
3595 void imap_change_flags(Folder *folder, FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
3597 IMAPSession *session;
3598 IMAPFlags flags_set = 0, flags_unset = 0;
3599 gint ok = IMAP_SUCCESS;
3601 g_return_if_fail(folder != NULL);
3602 g_return_if_fail(folder->class == &imap_class);
3603 g_return_if_fail(item != NULL);
3604 g_return_if_fail(item->folder == folder);
3605 g_return_if_fail(msginfo != NULL);
3606 g_return_if_fail(msginfo->folder == item);
3608 session = imap_session_get(folder);
3609 if (!session) return;
3611 if ((ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
3612 NULL, NULL, NULL, NULL)) != IMAP_SUCCESS)
3615 if (!MSG_IS_MARKED(msginfo->flags) && (newflags & MSG_MARKED))
3616 flags_set |= IMAP_FLAG_FLAGGED;
3617 if ( MSG_IS_MARKED(msginfo->flags) && !(newflags & MSG_MARKED))
3618 flags_unset |= IMAP_FLAG_FLAGGED;
3620 if (!MSG_IS_UNREAD(msginfo->flags) && (newflags & MSG_UNREAD))
3621 flags_unset |= IMAP_FLAG_SEEN;
3622 if ( MSG_IS_UNREAD(msginfo->flags) && !(newflags & MSG_UNREAD))
3623 flags_set |= IMAP_FLAG_SEEN;
3625 if (!MSG_IS_REPLIED(msginfo->flags) && (newflags & MSG_REPLIED))
3626 flags_set |= IMAP_FLAG_ANSWERED;
3627 if ( MSG_IS_REPLIED(msginfo->flags) && !(newflags & MSG_REPLIED))
3628 flags_set |= IMAP_FLAG_ANSWERED;
3631 ok = imap_set_message_flags(session, msginfo->msgnum,
3632 msginfo->msgnum, flags_set, TRUE);
3633 if (ok != IMAP_SUCCESS) return;
3637 ok = imap_set_message_flags(session, msginfo->msgnum,
3638 msginfo->msgnum, flags_unset, FALSE);
3639 if (ok != IMAP_SUCCESS) return;
3642 msginfo->flags.perm_flags = newflags;