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);
449 if (session->sock->state == CONN_DISCONNECTED) {
450 debug_print("IMAP server disconnected\n");
451 session_destroy(session);
452 imap_reset_uid_lists(folder);
453 session = imap_session_new(folder->account);
457 imap_get_capability(session);
459 /* Make sure session is authenticated */
460 if (!IMAP_SESSION(session)->authenticated)
461 imap_session_authenticate(IMAP_SESSION(session), folder->account);
462 if (!IMAP_SESSION(session)->authenticated) {
463 session_destroy(session);
464 rfolder->session = NULL;
468 /* Make sure we have parsed the IMAP namespace */
469 imap_parse_namespace(IMAP_SESSION(session),
470 IMAP_FOLDER(folder));
472 /* I think the point of this code is to avoid sending a
473 * keepalive if we've used the session recently and therefore
474 * think it's still alive. Unfortunately, most of the code
475 * does not yet check for errors on the socket, and so if the
476 * connection drops we don't notice until the timeout expires.
477 * A better solution than sending a NOOP every time would be
478 * for every command to be prepared to retry until it is
479 * successfully sent. -- mbp */
480 if (time(NULL) - session->last_access_time > SESSION_TIMEOUT) {
481 /* verify that the session is still alive */
482 if (imap_cmd_noop(session->sock) != IMAP_SUCCESS) {
483 /* Check if this is the first try to establish a
484 connection, if yes we don't try to reconnect */
485 if (rfolder->session == NULL) {
486 log_warning(_("Connecting %s:%d failed"),
487 folder->account->recv_server, port);
488 session_destroy(session);
491 log_warning(_("IMAP4 connection to %s:%d has been"
492 " disconnected. Reconnecting...\n"),
493 folder->account->recv_server, port);
494 session_destroy(session);
495 /* Clear folders session to make imap_session_get create
496 a new session, because of rfolder->session == NULL
497 it will not try to reconnect again and so avoid an
499 rfolder->session = NULL;
500 session = SESSION(imap_session_get(folder));
505 rfolder->session = session;
507 session->last_access_time = time(NULL);
509 return IMAP_SESSION(session);
512 Session *imap_session_new(const PrefsAccount *account)
514 IMAPSession *session;
520 /* FIXME: IMAP over SSL only... */
523 port = account->set_imapport ? account->imapport
524 : account->ssl_imap == SSL_TUNNEL ? IMAPS_PORT : IMAP4_PORT;
525 ssl_type = account->ssl_imap;
527 port = account->set_imapport ? account->imapport
531 if (account->set_tunnelcmd) {
532 log_message(_("creating tunneled IMAP4 connection\n"));
534 if ((imap_sock = imap_open_tunnel(account->recv_server,
538 if ((imap_sock = imap_open_tunnel(account->recv_server,
539 account->tunnelcmd)) == NULL)
543 g_return_val_if_fail(account->recv_server != NULL, NULL);
545 log_message(_("creating IMAP4 connection to %s:%d ...\n"),
546 account->recv_server, port);
549 if ((imap_sock = imap_open(account->recv_server, port,
552 if ((imap_sock = imap_open(account->recv_server, port)) == NULL)
557 /* Only need to log in if the connection was not PREAUTH */
558 if (imap_greeting(imap_sock, &is_preauth) != IMAP_SUCCESS) {
559 sock_close(imap_sock);
562 log_message("IMAP connection is %s-authenticated\n",
563 (is_preauth) ? "pre" : "un");
565 session = g_new0(IMAPSession, 1);
566 session_init(SESSION(session));
567 SESSION(session)->type = SESSION_IMAP;
568 SESSION(session)->server = g_strdup(account->recv_server);
569 SESSION(session)->sock = imap_sock;
571 SESSION(session)->destroy = imap_session_destroy;
573 session->capability = NULL;
575 session->mbox = NULL;
576 session->authenticated = is_preauth;
578 session_list = g_list_append(session_list, session);
580 return SESSION(session);
583 void imap_session_authenticate(IMAPSession *session, const PrefsAccount *account)
587 g_return_if_fail(account->userid != NULL);
589 pass = account->passwd;
592 tmp_pass = input_dialog_query_password(account->recv_server, account->userid);
595 Xstrdup_a(pass, tmp_pass, {g_free(tmp_pass); return;});
599 if (imap_cmd_login(SESSION(session)->sock, account->userid, pass) != IMAP_SUCCESS) {
600 imap_cmd_logout(SESSION(session)->sock);
604 session->authenticated = TRUE;
607 void imap_session_destroy(Session *session)
609 sock_close(session->sock);
610 session->sock = NULL;
612 g_free(IMAP_SESSION(session)->mbox);
614 g_strfreev(IMAP_SESSION(session)->capability);
616 session_list = g_list_remove(session_list, session);
619 void imap_session_destroy_all(void)
621 while (session_list != NULL) {
622 IMAPSession *session = (IMAPSession *)session_list->data;
624 imap_cmd_logout(SESSION(session)->sock);
625 session_destroy(SESSION(session));
629 gchar *imap_fetch_msg(Folder *folder, FolderItem *item, gint uid)
631 gchar *path, *filename;
632 IMAPSession *session;
635 g_return_val_if_fail(folder != NULL, NULL);
636 g_return_val_if_fail(item != NULL, NULL);
638 path = folder_item_get_path(item);
639 if (!is_dir_exist(path))
641 filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(uid), NULL);
644 if (is_file_exist(filename)) {
645 debug_print("message %d has been already cached.\n", uid);
649 session = imap_session_get(folder);
655 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
656 NULL, NULL, NULL, NULL);
657 if (ok != IMAP_SUCCESS) {
658 g_warning("can't select mailbox %s\n", item->path);
663 debug_print("getting message %d...\n", uid);
664 ok = imap_cmd_fetch(SESSION(session)->sock, (guint32)uid, filename);
666 if (ok != IMAP_SUCCESS) {
667 g_warning("can't fetch message %d\n", uid);
675 gint imap_add_msg(Folder *folder, FolderItem *dest, const gchar *file,
676 gboolean remove_source)
679 IMAPSession *session;
682 g_return_val_if_fail(folder != NULL, -1);
683 g_return_val_if_fail(dest != NULL, -1);
684 g_return_val_if_fail(file != NULL, -1);
686 session = imap_session_get(folder);
687 if (!session) return -1;
689 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
690 ok = imap_cmd_append(session, destdir, file, &newuid);
693 if (ok != IMAP_SUCCESS) {
694 g_warning("can't append message %s\n", file);
699 if (unlink(file) < 0)
700 FILE_OP_ERROR(file, "unlink");
706 static gint imap_do_copy(Folder *folder, FolderItem *dest, MsgInfo *msginfo,
707 gboolean remove_source)
710 IMAPSession *session;
711 IMAPFlags iflags = 0;
715 g_return_val_if_fail(folder != NULL, -1);
716 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
717 g_return_val_if_fail(dest != NULL, -1);
718 g_return_val_if_fail(msginfo != NULL, -1);
720 session = imap_session_get(folder);
721 if (!session) return -1;
723 if (msginfo->folder == dest) {
724 g_warning("the src folder is identical to the dest.\n");
728 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
730 /* ensure source folder selected */
731 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
732 NULL, NULL, NULL, NULL);
733 if (ok != IMAP_SUCCESS)
737 debug_print("Moving message %s%c%d to %s ...\n",
738 msginfo->folder->path, G_DIR_SEPARATOR,
739 msginfo->msgnum, destdir);
741 debug_print("Copying message %s%c%d to %s ...\n",
742 msginfo->folder->path, G_DIR_SEPARATOR,
743 msginfo->msgnum, destdir);
745 ok = imap_cmd_copy(session, msginfo->msgnum, destdir, &newuid);
747 if (ok == IMAP_SUCCESS && remove_source) {
748 imap_set_message_flags(session, msginfo->msgnum, msginfo->msgnum,
749 IMAP_FLAG_DELETED, TRUE);
750 ok = imap_cmd_expunge(SESSION(session)->sock);
753 /* get the dest folder to set the flags */
755 ok = imap_select(session, IMAP_FOLDER(folder), dest->path,
756 NULL, NULL, NULL, NULL);
757 if (ok != IMAP_SUCCESS) /* the folder disappeared? */
760 if (msginfo->flags.perm_flags & MSG_MARKED) iflags |= IMAP_FLAG_FLAGGED;
761 if (msginfo->flags.perm_flags & MSG_REPLIED) iflags |= IMAP_FLAG_ANSWERED;
763 if (imap_set_message_flags(session, newuid, newuid, iflags, TRUE)
770 if (ok == IMAP_SUCCESS)
777 static gint imap_do_copy_msgs_with_dest(Folder *folder, FolderItem *dest,
779 gboolean remove_source)
784 IMAPSession *session;
785 gint ok = IMAP_SUCCESS;
787 g_return_val_if_fail(folder != NULL, -1);
788 g_return_val_if_fail(dest != NULL, -1);
789 g_return_val_if_fail(msglist != NULL, -1);
791 session = imap_session_get(folder);
792 if (!session) return -1;
794 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
796 for (cur = msglist; cur != NULL; cur = cur->next) {
797 msginfo = (MsgInfo *)cur->data;
799 if (msginfo->folder == dest) {
800 g_warning("the src folder is identical to the dest.\n");
804 /* ensure source folder selected */
805 ok = imap_select(session, IMAP_FOLDER(folder),
806 msginfo->folder->path, NULL, NULL, NULL, NULL);
809 debug_print("Moving message %s%c%d to %s ...\n",
810 msginfo->folder->path, G_DIR_SEPARATOR,
811 msginfo->msgnum, destdir);
813 debug_print("Copying message %s%c%d to %s ...\n",
814 msginfo->folder->path, G_DIR_SEPARATOR,
815 msginfo->msgnum, destdir);
817 ok = imap_cmd_copy(session, msginfo, destdir);
819 if (ok == IMAP_SUCCESS && remove_source) {
820 imap_set_message_flags
821 (session, msginfo->msgnum, msginfo->msgnum,
822 IMAP_FLAG_DELETED, TRUE);
827 ok = imap_cmd_expunge(SESSION(session)->sock);
831 if (ok == IMAP_SUCCESS)
838 gint imap_move_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
843 g_return_val_if_fail(folder != NULL, -1);
844 g_return_val_if_fail(dest != NULL, -1);
845 g_return_val_if_fail(msginfo != NULL, -1);
846 g_return_val_if_fail(msginfo->folder != NULL, -1);
848 if (folder == msginfo->folder->folder)
849 return imap_do_copy(folder, dest, msginfo, TRUE);
851 srcfile = procmsg_get_message_file(msginfo);
852 if (!srcfile) return -1;
854 ret = imap_add_msg(folder, dest, srcfile, FALSE);
858 if(folder_item_remove_msg(msginfo->folder, msginfo->msgnum)) {
867 gint imap_move_msgs_with_dest(Folder *folder, FolderItem *dest,
874 g_return_val_if_fail(folder != NULL, -1);
875 g_return_val_if_fail(dest != NULL, -1);
876 g_return_val_if_fail(msglist != NULL, -1);
878 msginfo = (MsgInfo *)msglist->data;
879 if (folder == msginfo->folder->folder)
880 return imap_do_copy_msgs_with_dest(folder, dest, msglist, TRUE);
882 for (cur = msglist; cur != NULL; cur = cur->next) {
883 msginfo = (MsgInfo *)cur->data;
884 ret = imap_move_msg(folder, dest, msginfo);
885 if (ret == -1) break;
892 gint imap_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
897 g_return_val_if_fail(folder != NULL, -1);
898 g_return_val_if_fail(dest != NULL, -1);
899 g_return_val_if_fail(msginfo != NULL, -1);
900 g_return_val_if_fail(msginfo->folder != NULL, -1);
902 if (folder == msginfo->folder->folder)
903 return imap_do_copy(folder, dest, msginfo, FALSE);
905 srcfile = procmsg_get_message_file(msginfo);
906 if (!srcfile) return -1;
908 ret = imap_add_msg(folder, dest, srcfile, FALSE);
916 gint imap_copy_msgs_with_dest(Folder *folder, FolderItem *dest,
923 g_return_val_if_fail(folder != NULL, -1);
924 g_return_val_if_fail(dest != NULL, -1);
925 g_return_val_if_fail(msglist != NULL, -1);
927 msginfo = (MsgInfo *)msglist->data;
928 if (folder == msginfo->folder->folder)
929 return imap_do_copy_msgs_with_dest
930 (folder, dest, msglist, FALSE);
932 for (cur = msglist; cur != NULL; cur = cur->next) {
933 msginfo = (MsgInfo *)cur->data;
934 ret = imap_copy_msg(folder, dest, msginfo);
935 if (ret == -1) break;
942 gint imap_remove_msg(Folder *folder, FolderItem *item, gint uid)
945 IMAPSession *session;
948 g_return_val_if_fail(folder != NULL, -1);
949 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
950 g_return_val_if_fail(item != NULL, -1);
952 session = imap_session_get(folder);
953 if (!session) return -1;
955 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
956 NULL, NULL, NULL, NULL);
957 if (ok != IMAP_SUCCESS)
960 ok = imap_set_message_flags
961 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
962 (guint32)uid, (guint32)uid, IMAP_FLAG_DELETED, TRUE);
963 if (ok != IMAP_SUCCESS) {
964 log_warning(_("can't set deleted flags: %d\n"), uid);
968 ok = imap_cmd_expunge(SESSION(session)->sock);
969 if (ok != IMAP_SUCCESS) {
970 log_warning(_("can't expunge\n"));
974 dir = folder_item_get_path(item);
975 if (is_dir_exist(dir))
976 remove_numbered_files(dir, uid, uid);
982 gint imap_remove_msgs(Folder *folder, FolderItem *item, GSList *msglist)
985 IMAPSession *session;
991 g_return_val_if_fail(folder != NULL, -1);
992 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
993 g_return_val_if_fail(item != NULL, -1);
994 g_return_val_if_fail(msglist != NULL, -1);
996 session = imap_session_get(folder);
997 if (!session) return -1;
999 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
1000 NULL, NULL, NULL, NULL);
1001 if (ok != IMAP_SUCCESS)
1004 for (cur = msglist; cur != NULL; cur = cur->next) {
1005 msginfo = (MsgInfo *)cur->data;
1006 uid = msginfo->msgnum;
1007 ok = imap_set_message_flags
1008 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
1009 uid, uid, IMAP_FLAG_DELETED, TRUE);
1010 if (ok != IMAP_SUCCESS) {
1011 log_warning(_("can't set deleted flags: %d\n"), uid);
1016 ok = imap_cmd_expunge(SESSION(session)->sock);
1017 if (ok != IMAP_SUCCESS) {
1018 log_warning(_("can't expunge\n"));
1022 dir = folder_item_get_path(item);
1023 if (is_dir_exist(dir)) {
1024 for (cur = msglist; cur != NULL; cur = cur->next) {
1025 msginfo = (MsgInfo *)cur->data;
1026 uid = msginfo->msgnum;
1027 remove_numbered_files(dir, uid, uid);
1032 return IMAP_SUCCESS;
1035 gint imap_remove_all_msg(Folder *folder, FolderItem *item)
1037 gint exists, recent, unseen;
1038 guint32 uid_validity;
1040 IMAPSession *session;
1043 g_return_val_if_fail(folder != NULL, -1);
1044 g_return_val_if_fail(item != NULL, -1);
1046 session = imap_session_get(folder);
1047 if (!session) return -1;
1049 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
1050 &exists, &recent, &unseen, &uid_validity);
1051 if (ok != IMAP_SUCCESS)
1054 return IMAP_SUCCESS;
1056 imap_cmd_gen_send(SESSION(session)->sock,
1057 "STORE 1:%d +FLAGS (\\Deleted)", exists);
1058 ok = imap_cmd_ok(SESSION(session)->sock, NULL);
1059 if (ok != IMAP_SUCCESS) {
1060 log_warning(_("can't set deleted flags: 1:%d\n"), exists);
1064 ok = imap_cmd_expunge(SESSION(session)->sock);
1065 if (ok != IMAP_SUCCESS) {
1066 log_warning(_("can't expunge\n"));
1070 dir = folder_item_get_path(item);
1071 if (is_dir_exist(dir))
1072 remove_all_numbered_files(dir);
1075 return IMAP_SUCCESS;
1078 gboolean imap_is_msg_changed(Folder *folder, FolderItem *item, MsgInfo *msginfo)
1080 /* TODO: properly implement this method */
1084 gint imap_scan_folder(Folder *folder, FolderItem *item)
1086 IMAPSession *session;
1087 gint messages, recent, unseen;
1088 guint32 uid_next, uid_validity;
1091 g_return_val_if_fail(folder != NULL, -1);
1092 g_return_val_if_fail(item != NULL, -1);
1094 session = imap_session_get(folder);
1095 if (!session) return -1;
1097 ok = imap_status(session, IMAP_FOLDER(folder), item->path,
1098 &messages, &recent, &uid_next, &uid_validity, &unseen);
1099 if (ok != IMAP_SUCCESS) return -1;
1101 item->new = unseen > 0 ? recent : 0;
1102 item->unread = unseen;
1103 item->total = messages;
1104 item->last_num = (messages > 0 && uid_next > 0) ? uid_next - 1 : 0;
1105 /* item->mtime = uid_validity; */
1110 void imap_scan_tree(Folder *folder)
1113 IMAPSession *session;
1114 gchar *root_folder = NULL;
1116 g_return_if_fail(folder != NULL);
1117 g_return_if_fail(folder->account != NULL);
1119 session = imap_session_get(folder);
1121 if (!folder->node) {
1122 folder_tree_destroy(folder);
1123 item = folder_item_new(folder, folder->name, NULL);
1124 item->folder = folder;
1125 folder->node = g_node_new(item);
1130 if (folder->account->imap_dir && *folder->account->imap_dir) {
1131 Xstrdup_a(root_folder, folder->account->imap_dir, return);
1132 strtailchomp(root_folder, '/');
1133 debug_print("IMAP root directory: %s\n", root_folder);
1136 item = folder_item_new(folder, folder->name, root_folder);
1137 item->folder = folder;
1138 item->no_select = TRUE;
1139 folder->node = g_node_new(item);
1141 imap_scan_tree_recursive(session, item);
1143 imap_create_missing_folders(folder);
1146 static gint imap_scan_tree_recursive(IMAPSession *session, FolderItem *item)
1149 IMAPFolder *imapfolder;
1150 FolderItem *new_item;
1151 GSList *item_list, *cur;
1153 gchar *wildcard_path;
1157 g_return_val_if_fail(item != NULL, -1);
1158 g_return_val_if_fail(item->folder != NULL, -1);
1159 g_return_val_if_fail(item->no_sub == FALSE, -1);
1161 folder = FOLDER(item->folder);
1162 imapfolder = IMAP_FOLDER(folder);
1164 separator = imap_get_path_separator(imapfolder, item->path);
1166 if (item->folder->ui_func)
1167 item->folder->ui_func(folder, item, folder->ui_func_data);
1170 wildcard[0] = separator;
1173 real_path = imap_get_real_path(imapfolder, item->path);
1177 real_path = g_strdup("");
1180 Xstrcat_a(wildcard_path, real_path, wildcard,
1181 {g_free(real_path); return IMAP_ERROR;});
1182 QUOTE_IF_REQUIRED(wildcard_path, wildcard_path);
1184 imap_cmd_gen_send(SESSION(session)->sock, "LIST \"\" %s",
1187 strtailchomp(real_path, separator);
1188 item_list = imap_parse_list(folder, session, real_path, NULL);
1191 for (cur = item_list; cur != NULL; cur = cur->next) {
1192 new_item = cur->data;
1193 if (!strcmp(new_item->path, "INBOX")) {
1194 if (!folder->inbox) {
1195 new_item->stype = F_INBOX;
1196 item->folder->inbox = new_item;
1198 folder_item_destroy(new_item);
1201 } else if (!item->parent || item->stype == F_INBOX) {
1204 base = g_basename(new_item->path);
1206 if (!folder->outbox && !strcasecmp(base, "Sent")) {
1207 new_item->stype = F_OUTBOX;
1208 folder->outbox = new_item;
1209 } else if (!folder->draft && !strcasecmp(base, "Drafts")) {
1210 new_item->stype = F_DRAFT;
1211 folder->draft = new_item;
1212 } else if (!folder->queue && !strcasecmp(base, "Queue")) {
1213 new_item->stype = F_QUEUE;
1214 folder->queue = new_item;
1215 } else if (!folder->trash && !strcasecmp(base, "Trash")) {
1216 new_item->stype = F_TRASH;
1217 folder->trash = new_item;
1220 folder_item_append(item, new_item);
1221 if (new_item->no_select == FALSE)
1222 imap_scan_folder(folder, new_item);
1223 if (new_item->no_sub == FALSE)
1224 imap_scan_tree_recursive(session, new_item);
1227 return IMAP_SUCCESS;
1230 static GSList *imap_parse_list(Folder *folder, IMAPSession *session,
1231 const gchar *real_path, gchar *separator)
1233 gchar buf[IMAPBUFSIZE];
1235 gchar separator_str[16];
1238 gchar *loc_name, *loc_path;
1239 GSList *item_list = NULL;
1241 FolderItem *new_item;
1243 debug_print("getting list of %s ...\n",
1244 *real_path ? real_path : "\"\"");
1246 str = g_string_new(NULL);
1249 if (sock_gets(SESSION(session)->sock, buf, sizeof(buf)) <= 0) {
1250 log_warning(_("error occured while getting LIST.\n"));
1254 if (buf[0] != '*' || buf[1] != ' ') {
1255 log_print("IMAP4< %s\n", buf);
1258 debug_print("IMAP4< %s\n", buf);
1260 g_string_assign(str, buf);
1262 if (strncmp(p, "LIST ", 5) != 0) continue;
1265 if (*p != '(') continue;
1267 p = strchr_cpy(p, ')', flags, sizeof(flags));
1269 while (*p == ' ') p++;
1271 p = strchr_cpy(p, ' ', separator_str, sizeof(separator_str));
1273 extract_quote(separator_str, '"');
1274 if (!strcmp(separator_str, "NIL"))
1275 separator_str[0] = '\0';
1277 *separator = separator_str[0];
1280 while (*p == ' ') p++;
1281 if (*p == '{' || *p == '"')
1282 p = imap_parse_atom(SESSION(session)->sock, p,
1283 buf, sizeof(buf), str);
1285 strncpy2(buf, p, sizeof(buf));
1286 strtailchomp(buf, separator_str[0]);
1287 if (buf[0] == '\0') continue;
1288 if (!strcmp(buf, real_path)) continue;
1290 if (separator_str[0] != '\0')
1291 subst_char(buf, separator_str[0], '/');
1292 name = g_basename(buf);
1293 if (name[0] == '.') continue;
1295 loc_name = imap_modified_utf7_to_locale(name);
1296 loc_path = imap_modified_utf7_to_locale(buf);
1297 new_item = folder_item_new(folder, loc_name, loc_path);
1298 if (strcasestr(flags, "\\Noinferiors") != NULL)
1299 new_item->no_sub = TRUE;
1300 if (strcmp(buf, "INBOX") != 0 &&
1301 strcasestr(flags, "\\Noselect") != NULL)
1302 new_item->no_select = TRUE;
1304 item_list = g_slist_append(item_list, new_item);
1306 debug_print("folder %s has been added.\n", loc_path);
1311 g_string_free(str, TRUE);
1316 gint imap_create_tree(Folder *folder)
1318 g_return_val_if_fail(folder != NULL, -1);
1319 g_return_val_if_fail(folder->node != NULL, -1);
1320 g_return_val_if_fail(folder->node->data != NULL, -1);
1321 g_return_val_if_fail(folder->account != NULL, -1);
1323 imap_scan_tree(folder);
1324 imap_create_missing_folders(folder);
1329 static void imap_create_missing_folders(Folder *folder)
1331 g_return_if_fail(folder != NULL);
1334 folder->inbox = imap_create_special_folder
1335 (folder, F_INBOX, "INBOX");
1337 if (!folder->outbox)
1338 folder->outbox = imap_create_special_folder
1339 (folder, F_OUTBOX, "Sent");
1341 folder->draft = imap_create_special_folder
1342 (folder, F_DRAFT, "Drafts");
1344 folder->queue = imap_create_special_folder
1345 (folder, F_QUEUE, "Queue");
1348 folder->trash = imap_create_special_folder
1349 (folder, F_TRASH, "Trash");
1352 static FolderItem *imap_create_special_folder(Folder *folder,
1353 SpecialFolderItemType stype,
1357 FolderItem *new_item;
1359 g_return_val_if_fail(folder != NULL, NULL);
1360 g_return_val_if_fail(folder->node != NULL, NULL);
1361 g_return_val_if_fail(folder->node->data != NULL, NULL);
1362 g_return_val_if_fail(folder->account != NULL, NULL);
1363 g_return_val_if_fail(name != NULL, NULL);
1365 item = FOLDER_ITEM(folder->node->data);
1366 new_item = imap_create_folder(folder, item, name);
1369 g_warning("Can't create '%s'\n", name);
1370 if (!folder->inbox) return NULL;
1372 new_item = imap_create_folder(folder, folder->inbox, name);
1374 g_warning("Can't create '%s' under INBOX\n", name);
1376 new_item->stype = stype;
1378 new_item->stype = stype;
1383 FolderItem *imap_create_folder(Folder *folder, FolderItem *parent,
1386 gchar *dirpath, *imap_path;
1387 IMAPSession *session;
1388 FolderItem *new_item;
1394 g_return_val_if_fail(folder != NULL, NULL);
1395 g_return_val_if_fail(folder->account != NULL, NULL);
1396 g_return_val_if_fail(parent != NULL, NULL);
1397 g_return_val_if_fail(name != NULL, NULL);
1399 session = imap_session_get(folder);
1400 if (!session) return NULL;
1402 if (!parent->parent && strcmp(name, "INBOX") == 0)
1403 dirpath = g_strdup(name);
1404 else if (parent->path)
1405 dirpath = g_strconcat(parent->path, "/", name, NULL);
1406 else if ((p = strchr(name, '/')) != NULL && *(p + 1) != '\0')
1407 dirpath = g_strdup(name);
1408 else if (folder->account->imap_dir && *folder->account->imap_dir) {
1411 Xstrdup_a(imap_dir, folder->account->imap_dir, return NULL);
1412 strtailchomp(imap_dir, '/');
1413 dirpath = g_strconcat(imap_dir, "/", name, NULL);
1415 dirpath = g_strdup(name);
1417 /* keep trailing directory separator to create a folder that contains
1419 imap_path = imap_locale_to_modified_utf7(dirpath);
1420 strtailchomp(dirpath, '/');
1421 Xstrdup_a(new_name, name, {g_free(dirpath); return NULL;});
1422 strtailchomp(new_name, '/');
1423 separator = imap_get_path_separator(IMAP_FOLDER(folder), imap_path);
1424 imap_path_separator_subst(imap_path, separator);
1425 subst_char(new_name, '/', separator);
1427 if (strcmp(name, "INBOX") != 0) {
1430 gboolean exist = FALSE;
1432 argbuf = g_ptr_array_new();
1433 ok = imap_cmd_list(SESSION(session)->sock, NULL, imap_path,
1435 if (ok != IMAP_SUCCESS) {
1436 log_warning(_("can't create mailbox: LIST failed\n"));
1439 ptr_array_free_strings(argbuf);
1440 g_ptr_array_free(argbuf, TRUE);
1444 for (i = 0; i < argbuf->len; i++) {
1446 str = g_ptr_array_index(argbuf, i);
1447 if (!strncmp(str, "LIST ", 5)) {
1452 ptr_array_free_strings(argbuf);
1453 g_ptr_array_free(argbuf, TRUE);
1456 ok = imap_cmd_create(SESSION(session)->sock, imap_path);
1457 if (ok != IMAP_SUCCESS) {
1458 log_warning(_("can't create mailbox\n"));
1466 new_item = folder_item_new(folder, new_name, dirpath);
1467 folder_item_append(parent, new_item);
1471 dirpath = folder_item_get_path(new_item);
1472 if (!is_dir_exist(dirpath))
1473 make_dir_hier(dirpath);
1479 gint imap_rename_folder(Folder *folder, FolderItem *item, const gchar *name)
1483 gchar *real_oldpath;
1484 gchar *real_newpath;
1487 gchar *old_cache_dir;
1488 gchar *new_cache_dir;
1489 IMAPSession *session;
1492 gint exists, recent, unseen;
1493 guint32 uid_validity;
1495 g_return_val_if_fail(folder != NULL, -1);
1496 g_return_val_if_fail(item != NULL, -1);
1497 g_return_val_if_fail(item->path != NULL, -1);
1498 g_return_val_if_fail(name != NULL, -1);
1500 session = imap_session_get(folder);
1501 if (!session) return -1;
1503 real_oldpath = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1505 g_free(session->mbox);
1506 session->mbox = NULL;
1507 ok = imap_cmd_examine(SESSION(session)->sock, "INBOX",
1508 &exists, &recent, &unseen, &uid_validity);
1509 if (ok != IMAP_SUCCESS) {
1510 g_free(real_oldpath);
1514 separator = imap_get_path_separator(IMAP_FOLDER(folder), item->path);
1515 if (strchr(item->path, G_DIR_SEPARATOR)) {
1516 dirpath = g_dirname(item->path);
1517 newpath = g_strconcat(dirpath, G_DIR_SEPARATOR_S, name, NULL);
1520 newpath = g_strdup(name);
1522 real_newpath = imap_locale_to_modified_utf7(newpath);
1523 imap_path_separator_subst(real_newpath, separator);
1525 ok = imap_cmd_rename(SESSION(session)->sock, real_oldpath, real_newpath);
1526 if (ok != IMAP_SUCCESS) {
1527 log_warning(_("can't rename mailbox: %s to %s\n"),
1528 real_oldpath, real_newpath);
1529 g_free(real_oldpath);
1531 g_free(real_newpath);
1536 item->name = g_strdup(name);
1538 old_cache_dir = folder_item_get_path(item);
1540 node = g_node_find(item->folder->node, G_PRE_ORDER, G_TRAVERSE_ALL,
1542 paths[0] = g_strdup(item->path);
1544 g_node_traverse(node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1545 imap_rename_folder_func, paths);
1547 if (is_dir_exist(old_cache_dir)) {
1548 new_cache_dir = folder_item_get_path(item);
1549 if (rename(old_cache_dir, new_cache_dir) < 0) {
1550 FILE_OP_ERROR(old_cache_dir, "rename");
1552 g_free(new_cache_dir);
1555 g_free(old_cache_dir);
1558 g_free(real_oldpath);
1559 g_free(real_newpath);
1564 gint imap_remove_folder(Folder *folder, FolderItem *item)
1567 IMAPSession *session;
1570 gint exists, recent, unseen;
1571 guint32 uid_validity;
1573 g_return_val_if_fail(folder != NULL, -1);
1574 g_return_val_if_fail(item != NULL, -1);
1575 g_return_val_if_fail(item->path != NULL, -1);
1577 session = imap_session_get(folder);
1578 if (!session) return -1;
1580 path = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1582 ok = imap_cmd_examine(SESSION(session)->sock, "INBOX",
1583 &exists, &recent, &unseen, &uid_validity);
1584 if (ok != IMAP_SUCCESS) {
1589 ok = imap_cmd_delete(SESSION(session)->sock, path);
1590 if (ok != IMAP_SUCCESS) {
1591 log_warning(_("can't delete mailbox\n"));
1597 cache_dir = folder_item_get_path(item);
1598 if (is_dir_exist(cache_dir) && remove_dir_recursive(cache_dir) < 0)
1599 g_warning("can't remove directory '%s'\n", cache_dir);
1601 folder_item_remove(item);
1606 static GSList *imap_get_uncached_messages(IMAPSession *session,
1608 guint32 first_uid, guint32 last_uid)
1611 GSList *newlist = NULL;
1612 GSList *llast = NULL;
1616 g_return_val_if_fail(session != NULL, NULL);
1617 g_return_val_if_fail(item != NULL, NULL);
1618 g_return_val_if_fail(item->folder != NULL, NULL);
1619 g_return_val_if_fail(FOLDER_CLASS(item->folder) == &imap_class, NULL);
1620 g_return_val_if_fail(first_uid <= last_uid, NULL);
1622 if (imap_cmd_envelope(SESSION(session)->sock, first_uid, last_uid)
1624 log_warning(_("can't get envelope\n"));
1628 str = g_string_new(NULL);
1631 if ((tmp = sock_getline(SESSION(session)->sock)) == NULL) {
1632 log_warning(_("error occurred while getting envelope.\n"));
1633 g_string_free(str, TRUE);
1637 if (tmp[0] != '*' || tmp[1] != ' ') {
1638 log_print("IMAP4< %s\n", tmp);
1642 if (strstr(tmp, "FETCH") == NULL) {
1643 log_print("IMAP4< %s\n", tmp);
1647 log_print("IMAP4< %s\n", tmp);
1648 g_string_assign(str, tmp);
1651 msginfo = imap_parse_envelope
1652 (SESSION(session)->sock, item, str);
1654 log_warning(_("can't parse envelope: %s\n"), str->str);
1657 if (item->stype == F_QUEUE) {
1658 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_QUEUED);
1659 } else if (item->stype == F_DRAFT) {
1660 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_DRAFT);
1663 msginfo->folder = item;
1666 llast = newlist = g_slist_append(newlist, msginfo);
1668 llast = g_slist_append(llast, msginfo);
1669 llast = llast->next;
1673 g_string_free(str, TRUE);
1678 static void imap_delete_all_cached_messages(FolderItem *item)
1682 g_return_if_fail(item != NULL);
1683 g_return_if_fail(item->folder != NULL);
1684 g_return_if_fail(FOLDER_CLASS(item->folder) == &imap_class);
1686 debug_print("Deleting all cached messages...\n");
1688 dir = folder_item_get_path(item);
1689 if (is_dir_exist(dir))
1690 remove_all_numbered_files(dir);
1693 debug_print("done.\n");
1697 static SockInfo *imap_open_tunnel(const gchar *server,
1698 const gchar *tunnelcmd,
1701 static SockInfo *imap_open_tunnel(const gchar *server,
1702 const gchar *tunnelcmd)
1707 if ((sock = sock_connect_cmd(server, tunnelcmd)) == NULL) {
1708 log_warning(_("Can't establish IMAP4 session with: %s\n"),
1713 return imap_init_sock(sock, ssl_type);
1715 return imap_init_sock(sock);
1721 static SockInfo *imap_open(const gchar *server, gushort port,
1724 static SockInfo *imap_open(const gchar *server, gushort port)
1729 if ((sock = sock_connect(server, port)) == NULL) {
1730 log_warning(_("Can't connect to IMAP4 server: %s:%d\n"),
1736 if (ssl_type == SSL_TUNNEL && !ssl_init_socket(sock)) {
1737 log_warning(_("Can't establish IMAP4 session with: %s:%d\n"),
1742 return imap_init_sock(sock, ssl_type);
1744 return imap_init_sock(sock);
1749 static SockInfo *imap_init_sock(SockInfo *sock, SSLType ssl_type)
1751 static SockInfo *imap_init_sock(SockInfo *sock)
1756 if (ssl_type == SSL_STARTTLS) {
1759 ok = imap_cmd_starttls(sock);
1760 if (ok != IMAP_SUCCESS) {
1761 log_warning(_("Can't start TLS session.\n"));
1765 if (!ssl_init_socket_with_method(sock, SSL_METHOD_TLSv1)) {
1775 static GList *imap_parse_namespace_str(gchar *str)
1780 IMAPNameSpace *namespace;
1781 GList *ns_list = NULL;
1783 while (*p != '\0') {
1784 /* parse ("#foo" "/") */
1786 while (*p && *p != '(') p++;
1787 if (*p == '\0') break;
1790 while (*p && *p != '"') p++;
1791 if (*p == '\0') break;
1795 while (*p && *p != '"') p++;
1796 if (*p == '\0') break;
1800 while (*p && isspace(*p)) p++;
1801 if (*p == '\0') break;
1802 if (strncmp(p, "NIL", 3) == 0)
1804 else if (*p == '"') {
1807 while (*p && *p != '"') p++;
1808 if (*p == '\0') break;
1813 while (*p && *p != ')') p++;
1814 if (*p == '\0') break;
1817 namespace = g_new(IMAPNameSpace, 1);
1818 namespace->name = g_strdup(name);
1819 namespace->separator = separator ? separator[0] : '\0';
1820 ns_list = g_list_append(ns_list, namespace);
1826 static void imap_parse_namespace(IMAPSession *session, IMAPFolder *folder)
1831 g_return_if_fail(session != NULL);
1832 g_return_if_fail(folder != NULL);
1834 if (folder->ns_personal != NULL ||
1835 folder->ns_others != NULL ||
1836 folder->ns_shared != NULL)
1839 if (!imap_has_capability(session, "NAMESPACE")) {
1840 imap_get_namespace_by_list(session, folder);
1844 if (imap_cmd_namespace(SESSION(session)->sock, &ns_str)
1846 log_warning(_("can't get namespace\n"));
1850 str_array = strsplit_parenthesis(ns_str, '(', ')', 3);
1851 if (str_array == NULL) {
1853 imap_get_namespace_by_list(session, folder);
1857 folder->ns_personal = imap_parse_namespace_str(str_array[0]);
1858 if (str_array[0] && str_array[1])
1859 folder->ns_others = imap_parse_namespace_str(str_array[1]);
1860 if (str_array[0] && str_array[1] && str_array[2])
1861 folder->ns_shared = imap_parse_namespace_str(str_array[2]);
1862 g_strfreev(str_array);
1866 static void imap_get_namespace_by_list(IMAPSession *session, IMAPFolder *folder)
1868 GSList *item_list, *cur;
1869 gchar separator = '\0';
1870 IMAPNameSpace *namespace;
1872 g_return_if_fail(session != NULL);
1873 g_return_if_fail(folder != NULL);
1875 if (folder->ns_personal != NULL ||
1876 folder->ns_others != NULL ||
1877 folder->ns_shared != NULL)
1880 imap_cmd_gen_send(SESSION(session)->sock, "LIST \"\" \"\"");
1881 item_list = imap_parse_list(NULL, session, "", &separator);
1882 for (cur = item_list; cur != NULL; cur = cur->next)
1883 folder_item_destroy(FOLDER_ITEM(cur->data));
1884 g_slist_free(item_list);
1886 namespace = g_new(IMAPNameSpace, 1);
1887 namespace->name = g_strdup("");
1888 namespace->separator = separator;
1889 folder->ns_personal = g_list_append(NULL, namespace);
1892 static IMAPNameSpace *imap_find_namespace_from_list(GList *ns_list,
1895 IMAPNameSpace *namespace = NULL;
1896 gchar *tmp_path, *name;
1898 if (!path) path = "";
1900 Xstrcat_a(tmp_path, path, "/", return NULL);
1902 for (; ns_list != NULL; ns_list = ns_list->next) {
1903 IMAPNameSpace *tmp_ns = ns_list->data;
1905 Xstrdup_a(name, tmp_ns->name, return namespace);
1906 if (tmp_ns->separator && tmp_ns->separator != '/')
1907 subst_char(name, tmp_ns->separator, '/');
1908 if (strncmp(tmp_path, name, strlen(name)) == 0)
1915 static IMAPNameSpace *imap_find_namespace(IMAPFolder *folder,
1918 IMAPNameSpace *namespace;
1920 g_return_val_if_fail(folder != NULL, NULL);
1922 namespace = imap_find_namespace_from_list(folder->ns_personal, path);
1923 if (namespace) return namespace;
1924 namespace = imap_find_namespace_from_list(folder->ns_others, path);
1925 if (namespace) return namespace;
1926 namespace = imap_find_namespace_from_list(folder->ns_shared, path);
1927 if (namespace) return namespace;
1932 static gchar imap_get_path_separator(IMAPFolder *folder, const gchar *path)
1934 IMAPNameSpace *namespace;
1935 gchar separator = '/';
1937 namespace = imap_find_namespace(folder, path);
1938 if (namespace && namespace->separator)
1939 separator = namespace->separator;
1944 static gchar *imap_get_real_path(IMAPFolder *folder, const gchar *path)
1949 g_return_val_if_fail(folder != NULL, NULL);
1950 g_return_val_if_fail(path != NULL, NULL);
1952 real_path = imap_locale_to_modified_utf7(path);
1953 separator = imap_get_path_separator(folder, path);
1954 imap_path_separator_subst(real_path, separator);
1959 static gchar *imap_parse_atom(SockInfo *sock, gchar *src,
1960 gchar *dest, gint dest_len, GString *str)
1962 gchar *cur_pos = src;
1965 g_return_val_if_fail(str != NULL, cur_pos);
1967 /* read the next line if the current response buffer is empty */
1968 while (isspace(*cur_pos)) cur_pos++;
1969 while (*cur_pos == '\0') {
1970 if ((nextline = sock_getline(sock)) == NULL)
1972 g_string_assign(str, nextline);
1974 strretchomp(nextline);
1975 /* log_print("IMAP4< %s\n", nextline); */
1976 debug_print("IMAP4< %s\n", nextline);
1979 while (isspace(*cur_pos)) cur_pos++;
1982 if (!strncmp(cur_pos, "NIL", 3)) {
1985 } else if (*cur_pos == '\"') {
1988 p = get_quoted(cur_pos, '\"', dest, dest_len);
1989 cur_pos = p ? p : cur_pos + 2;
1990 } else if (*cur_pos == '{') {
1995 cur_pos = strchr_cpy(cur_pos + 1, '}', buf, sizeof(buf));
1998 g_string_truncate(str, 0);
2002 if ((nextline = sock_getline(sock)) == NULL)
2004 line_len += strlen(nextline);
2005 g_string_append(str, nextline);
2007 strretchomp(nextline);
2008 /* log_print("IMAP4< %s\n", nextline); */
2009 debug_print("IMAP4< %s\n", nextline);
2011 } while (line_len < len);
2013 memcpy(dest, cur_pos, MIN(len, dest_len - 1));
2014 dest[MIN(len, dest_len - 1)] = '\0';
2021 static gchar *imap_get_header(SockInfo *sock, gchar *cur_pos, gchar **headers,
2031 g_return_val_if_fail(str != NULL, cur_pos);
2033 while (isspace(*cur_pos)) cur_pos++;
2035 g_return_val_if_fail(*cur_pos == '{', cur_pos);
2037 cur_pos = strchr_cpy(cur_pos + 1, '}', buf, sizeof(buf));
2040 g_string_truncate(str, 0);
2044 if ((nextline = sock_getline(sock)) == NULL)
2046 block_len += strlen(nextline);
2047 g_string_append(str, nextline);
2049 strretchomp(nextline);
2050 /* debug_print("IMAP4< %s\n", nextline); */
2052 } while (block_len < len);
2054 debug_print("IMAP4< [contents of BODY.PEEK[HEADER.FIELDS (...)]]\n");
2056 *headers = g_strndup(cur_pos, len);
2059 while (isspace(*cur_pos)) cur_pos++;
2060 while (*cur_pos == '\0') {
2061 if ((nextline = sock_getline(sock)) == NULL)
2063 g_string_assign(str, nextline);
2065 strretchomp(nextline);
2066 debug_print("IMAP4< %s\n", nextline);
2069 while (isspace(*cur_pos)) cur_pos++;
2075 static MsgFlags imap_parse_flags(const gchar *flag_str)
2077 const gchar *p = flag_str;
2078 MsgFlags flags = {0, 0};
2080 flags.perm_flags = MSG_UNREAD;
2082 while ((p = strchr(p, '\\')) != NULL) {
2085 if (g_strncasecmp(p, "Recent", 6) == 0 && MSG_IS_UNREAD(flags)) {
2086 MSG_SET_PERM_FLAGS(flags, MSG_NEW);
2087 } else if (g_strncasecmp(p, "Seen", 4) == 0) {
2088 MSG_UNSET_PERM_FLAGS(flags, MSG_NEW|MSG_UNREAD);
2089 } else if (g_strncasecmp(p, "Deleted", 7) == 0) {
2090 MSG_SET_PERM_FLAGS(flags, MSG_DELETED);
2091 } else if (g_strncasecmp(p, "Flagged", 7) == 0) {
2092 MSG_SET_PERM_FLAGS(flags, MSG_MARKED);
2093 } else if (g_strncasecmp(p, "Answered", 8) == 0) {
2094 MSG_SET_PERM_FLAGS(flags, MSG_REPLIED);
2101 static MsgInfo *imap_parse_envelope(SockInfo *sock, FolderItem *item,
2104 gchar buf[IMAPBUFSIZE];
2105 MsgInfo *msginfo = NULL;
2110 MsgFlags flags = {0, 0}, imap_flags = {0, 0};
2112 g_return_val_if_fail(line_str != NULL, NULL);
2113 g_return_val_if_fail(line_str->str[0] == '*' &&
2114 line_str->str[1] == ' ', NULL);
2116 MSG_SET_TMP_FLAGS(flags, MSG_IMAP);
2117 if (item->stype == F_QUEUE) {
2118 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
2119 } else if (item->stype == F_DRAFT) {
2120 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
2123 cur_pos = line_str->str + 2;
2125 #define PARSE_ONE_ELEMENT(ch) \
2127 cur_pos = strchr_cpy(cur_pos, ch, buf, sizeof(buf)); \
2128 if (cur_pos == NULL) { \
2129 g_warning("cur_pos == NULL\n"); \
2130 procmsg_msginfo_free(msginfo); \
2135 PARSE_ONE_ELEMENT(' ');
2138 PARSE_ONE_ELEMENT(' ');
2139 g_return_val_if_fail(!strcmp(buf, "FETCH"), NULL);
2141 g_return_val_if_fail(*cur_pos == '(', NULL);
2144 while (*cur_pos != '\0' && *cur_pos != ')') {
2145 while (*cur_pos == ' ') cur_pos++;
2147 if (!strncmp(cur_pos, "UID ", 4)) {
2149 uid = strtoul(cur_pos, &cur_pos, 10);
2150 } else if (!strncmp(cur_pos, "FLAGS ", 6)) {
2152 if (*cur_pos != '(') {
2153 g_warning("*cur_pos != '('\n");
2154 procmsg_msginfo_free(msginfo);
2158 PARSE_ONE_ELEMENT(')');
2159 imap_flags = imap_parse_flags(buf);
2160 } else if (!strncmp(cur_pos, "RFC822.SIZE ", 12)) {
2162 size = strtol(cur_pos, &cur_pos, 10);
2163 } else if (!strncmp(cur_pos, "BODY[HEADER.FIELDS ", 19)) {
2167 if (*cur_pos != '(') {
2168 g_warning("*cur_pos != '('\n");
2169 procmsg_msginfo_free(msginfo);
2173 PARSE_ONE_ELEMENT(')');
2174 if (*cur_pos != ']') {
2175 g_warning("*cur_pos != ']'\n");
2176 procmsg_msginfo_free(msginfo);
2181 cur_pos = imap_get_header(sock, cur_pos, &headers,
2183 msginfo = procheader_parse_str(headers, flags, FALSE, FALSE);
2186 g_warning("invalid FETCH response: %s\n", cur_pos);
2192 msginfo->msgnum = uid;
2193 msginfo->size = size;
2194 msginfo->flags.tmp_flags |= imap_flags.tmp_flags;
2195 msginfo->flags.perm_flags = imap_flags.perm_flags;
2201 gint imap_msg_set_perm_flags(MsgInfo *msginfo, MsgPermFlags flags)
2204 IMAPSession *session;
2205 IMAPFlags iflags = 0;
2206 gint ok = IMAP_SUCCESS;
2208 g_return_val_if_fail(msginfo != NULL, -1);
2209 g_return_val_if_fail(msginfo->folder != NULL, -1);
2210 g_return_val_if_fail(msginfo->folder->folder != NULL, -1);
2212 folder = msginfo->folder->folder;
2213 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
2215 session = imap_session_get(folder);
2216 if (!session) return -1;
2218 if ((ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
2219 NULL, NULL, NULL, NULL)) != IMAP_SUCCESS)
2222 if (flags & MSG_MARKED) iflags |= IMAP_FLAG_FLAGGED;
2223 if (flags & MSG_REPLIED) iflags |= IMAP_FLAG_ANSWERED;
2225 ok = imap_set_message_flags(session, msginfo->msgnum,
2226 msginfo->msgnum, iflags, TRUE);
2227 if (ok != IMAP_SUCCESS) return ok;
2230 if (flags & MSG_UNREAD)
2231 ok = imap_set_message_flags(session, msginfo->msgnum,
2232 msginfo->msgnum, IMAP_FLAG_SEEN,
2237 gint imap_msg_unset_perm_flags(MsgInfo *msginfo, MsgPermFlags flags)
2240 IMAPSession *session;
2241 IMAPFlags iflags = 0;
2242 gint ok = IMAP_SUCCESS;
2244 g_return_val_if_fail(msginfo != NULL, -1);
2245 g_return_val_if_fail(msginfo->folder != NULL, -1);
2246 g_return_val_if_fail(msginfo->folder->folder != NULL, -1);
2248 folder = msginfo->folder->folder;
2249 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
2251 session = imap_session_get(folder);
2252 if (!session) return -1;
2254 if (flags & MSG_MARKED) iflags |= IMAP_FLAG_FLAGGED;
2255 if (flags & MSG_REPLIED) iflags |= IMAP_FLAG_ANSWERED;
2257 ok = imap_set_message_flags(session, msginfo->msgnum,
2258 msginfo->msgnum, iflags, FALSE);
2259 if (ok != IMAP_SUCCESS) return ok;
2262 if (flags & MSG_UNREAD)
2263 ok = imap_set_message_flags(session, msginfo->msgnum,
2264 msginfo->msgnum, IMAP_FLAG_SEEN,
2269 static gint imap_set_message_flags(IMAPSession *session,
2278 buf = g_string_new(is_set ? "+FLAGS (" : "-FLAGS (");
2280 if (IMAP_IS_SEEN(flags)) g_string_append(buf, "\\Seen ");
2281 if (IMAP_IS_ANSWERED(flags)) g_string_append(buf, "\\Answered ");
2282 if (IMAP_IS_FLAGGED(flags)) g_string_append(buf, "\\Flagged ");
2283 if (IMAP_IS_DELETED(flags)) g_string_append(buf, "\\Deleted ");
2284 if (IMAP_IS_DRAFT(flags)) g_string_append(buf, "\\Draft");
2286 if (buf->str[buf->len - 1] == ' ')
2287 g_string_truncate(buf, buf->len - 1);
2289 g_string_append_c(buf, ')');
2291 ok = imap_cmd_store(SESSION(session)->sock, first_uid, last_uid,
2293 g_string_free(buf, TRUE);
2298 static gint imap_select(IMAPSession *session, IMAPFolder *folder,
2300 gint *exists, gint *recent, gint *unseen,
2301 guint32 *uid_validity)
2305 gint exists_, recent_, unseen_, uid_validity_;
2307 if (!exists || !recent || !unseen || !uid_validity) {
2308 if (session->mbox && strcmp(session->mbox, path) == 0)
2309 return IMAP_SUCCESS;
2313 uid_validity = &uid_validity_;
2316 g_free(session->mbox);
2317 session->mbox = NULL;
2319 real_path = imap_get_real_path(folder, path);
2320 ok = imap_cmd_select(SESSION(session)->sock, real_path,
2321 exists, recent, unseen, uid_validity);
2322 if (ok != IMAP_SUCCESS)
2323 log_warning(_("can't select folder: %s\n"), real_path);
2325 session->mbox = g_strdup(path);
2331 #define THROW(err) { ok = err; goto catch; }
2333 static gint imap_status(IMAPSession *session, IMAPFolder *folder,
2335 gint *messages, gint *recent,
2336 guint32 *uid_next, guint32 *uid_validity,
2345 *messages = *recent = *uid_next = *uid_validity = *unseen = 0;
2347 argbuf = g_ptr_array_new();
2349 real_path = imap_get_real_path(folder, path);
2350 QUOTE_IF_REQUIRED(real_path_, real_path);
2351 imap_cmd_gen_send(SESSION(session)->sock, "STATUS %s "
2352 "(MESSAGES RECENT UIDNEXT UIDVALIDITY UNSEEN)",
2355 ok = imap_cmd_ok(SESSION(session)->sock, argbuf);
2356 if (ok != IMAP_SUCCESS) THROW(ok);
2358 str = search_array_str(argbuf, "STATUS");
2359 if (!str) THROW(IMAP_ERROR);
2361 str = strchr(str, '(');
2362 if (!str) THROW(IMAP_ERROR);
2364 while (*str != '\0' && *str != ')') {
2365 while (*str == ' ') str++;
2367 if (!strncmp(str, "MESSAGES ", 9)) {
2369 *messages = strtol(str, &str, 10);
2370 } else if (!strncmp(str, "RECENT ", 7)) {
2372 *recent = strtol(str, &str, 10);
2373 } else if (!strncmp(str, "UIDNEXT ", 8)) {
2375 *uid_next = strtoul(str, &str, 10);
2376 } else if (!strncmp(str, "UIDVALIDITY ", 12)) {
2378 *uid_validity = strtoul(str, &str, 10);
2379 } else if (!strncmp(str, "UNSEEN ", 7)) {
2381 *unseen = strtol(str, &str, 10);
2383 g_warning("invalid STATUS response: %s\n", str);
2390 ptr_array_free_strings(argbuf);
2391 g_ptr_array_free(argbuf, TRUE);
2399 /* low-level IMAP4rev1 commands */
2401 static gint imap_cmd_login(SockInfo *sock,
2402 const gchar *user, const gchar *pass)
2404 gchar *user_, *pass_;
2407 QUOTE_IF_REQUIRED(user_, user);
2408 QUOTE_IF_REQUIRED(pass_, pass);
2409 imap_cmd_gen_send(sock, "LOGIN %s %s", user_, pass_);
2411 ok = imap_cmd_ok(sock, NULL);
2412 if (ok != IMAP_SUCCESS)
2413 log_warning(_("IMAP4 login failed.\n"));
2418 static gint imap_cmd_logout(SockInfo *sock)
2420 imap_cmd_gen_send(sock, "LOGOUT");
2421 return imap_cmd_ok(sock, NULL);
2424 /* Send a NOOP, and examine the server's response to see whether this
2425 * connection is pre-authenticated or not. */
2426 static gint imap_greeting(SockInfo *sock, gboolean *is_preauth)
2431 imap_cmd_gen_send(sock, "NOOP");
2432 argbuf = g_ptr_array_new(); /* will hold messages sent back */
2433 r = imap_cmd_ok(sock, argbuf);
2434 *is_preauth = search_array_str(argbuf, "PREAUTH") != NULL;
2436 ptr_array_free_strings(argbuf);
2437 g_ptr_array_free(argbuf, TRUE);
2442 static void imap_get_capability(Session *session)
2447 if (IMAP_SESSION(session)->capability != NULL)
2450 imap_cmd_gen_send(session->sock, "CAPABILITY");
2452 argbuf = g_ptr_array_new();
2454 if (imap_cmd_ok(session->sock, argbuf) != IMAP_SUCCESS ||
2455 ((capstr = search_array_str(argbuf, "CAPABILITY ")) == NULL)) {
2456 ptr_array_free_strings(argbuf);
2457 g_ptr_array_free(argbuf, TRUE);
2461 capstr += strlen("CAPABILITY ");
2463 IMAP_SESSION(session)->capability = g_strsplit(capstr, " ", 0);
2465 ptr_array_free_strings(argbuf);
2466 g_ptr_array_free(argbuf, TRUE);
2469 static gboolean imap_has_capability(IMAPSession *session, const gchar *cap)
2473 for (p = session->capability; *p != NULL; ++p)
2474 if (g_strcasecmp(*p, cap) == 0)
2480 static gint imap_cmd_noop(SockInfo *sock)
2482 imap_cmd_gen_send(sock, "NOOP");
2483 return imap_cmd_ok(sock, NULL);
2486 static gint imap_cmd_starttls(SockInfo *sock)
2488 imap_cmd_gen_send(sock, "STARTTLS");
2489 return imap_cmd_ok(sock, NULL);
2492 #define THROW(err) { ok = err; goto catch; }
2494 static gint imap_cmd_namespace(SockInfo *sock, gchar **ns_str)
2500 argbuf = g_ptr_array_new();
2502 imap_cmd_gen_send(sock, "NAMESPACE");
2503 if ((ok = imap_cmd_ok(sock, argbuf)) != IMAP_SUCCESS) THROW(ok);
2505 str = search_array_str(argbuf, "NAMESPACE");
2506 if (!str) THROW(IMAP_ERROR);
2508 *ns_str = g_strdup(str);
2511 ptr_array_free_strings(argbuf);
2512 g_ptr_array_free(argbuf, TRUE);
2519 static gint imap_cmd_list(SockInfo *sock, const gchar *ref,
2520 const gchar *mailbox, GPtrArray *argbuf)
2522 gchar *ref_, *mailbox_;
2524 if (!ref) ref = "\"\"";
2525 if (!mailbox) mailbox = "\"\"";
2527 QUOTE_IF_REQUIRED(ref_, ref);
2528 QUOTE_IF_REQUIRED(mailbox_, mailbox);
2529 imap_cmd_gen_send(sock, "LIST %s %s", ref_, mailbox_);
2531 return imap_cmd_ok(sock, argbuf);
2534 #define THROW goto catch
2536 static gint imap_cmd_do_select(SockInfo *sock, const gchar *folder,
2538 gint *exists, gint *recent, gint *unseen,
2539 guint32 *uid_validity)
2547 *exists = *recent = *unseen = *uid_validity = 0;
2548 argbuf = g_ptr_array_new();
2551 select_cmd = "EXAMINE";
2553 select_cmd = "SELECT";
2555 QUOTE_IF_REQUIRED(folder_, folder);
2556 imap_cmd_gen_send(sock, "%s %s", select_cmd, folder_);
2558 if ((ok = imap_cmd_ok(sock, argbuf)) != IMAP_SUCCESS) THROW;
2560 resp_str = search_array_contain_str(argbuf, "EXISTS");
2562 if (sscanf(resp_str,"%d EXISTS", exists) != 1) {
2563 g_warning("imap_cmd_select(): invalid EXISTS line.\n");
2568 resp_str = search_array_contain_str(argbuf, "RECENT");
2570 if (sscanf(resp_str, "%d RECENT", recent) != 1) {
2571 g_warning("imap_cmd_select(): invalid RECENT line.\n");
2576 resp_str = search_array_contain_str(argbuf, "UIDVALIDITY");
2578 if (sscanf(resp_str, "OK [UIDVALIDITY %u] ", uid_validity)
2580 g_warning("imap_cmd_select(): invalid UIDVALIDITY line.\n");
2585 resp_str = search_array_contain_str(argbuf, "UNSEEN");
2587 if (sscanf(resp_str, "OK [UNSEEN %d] ", unseen) != 1) {
2588 g_warning("imap_cmd_select(): invalid UNSEEN line.\n");
2594 ptr_array_free_strings(argbuf);
2595 g_ptr_array_free(argbuf, TRUE);
2600 static gint imap_cmd_select(SockInfo *sock, const gchar *folder,
2601 gint *exists, gint *recent, gint *unseen,
2602 guint32 *uid_validity)
2604 return imap_cmd_do_select(sock, folder, FALSE,
2605 exists, recent, unseen, uid_validity);
2608 static gint imap_cmd_examine(SockInfo *sock, const gchar *folder,
2609 gint *exists, gint *recent, gint *unseen,
2610 guint32 *uid_validity)
2612 return imap_cmd_do_select(sock, folder, TRUE,
2613 exists, recent, unseen, uid_validity);
2618 static gint imap_cmd_create(SockInfo *sock, const gchar *folder)
2622 QUOTE_IF_REQUIRED(folder_, folder);
2623 imap_cmd_gen_send(sock, "CREATE %s", folder_);
2625 return imap_cmd_ok(sock, NULL);
2628 static gint imap_cmd_rename(SockInfo *sock, const gchar *old_folder,
2629 const gchar *new_folder)
2631 gchar *old_folder_, *new_folder_;
2633 QUOTE_IF_REQUIRED(old_folder_, old_folder);
2634 QUOTE_IF_REQUIRED(new_folder_, new_folder);
2635 imap_cmd_gen_send(sock, "RENAME %s %s", old_folder_, new_folder_);
2637 return imap_cmd_ok(sock, NULL);
2640 static gint imap_cmd_delete(SockInfo *sock, const gchar *folder)
2644 QUOTE_IF_REQUIRED(folder_, folder);
2645 imap_cmd_gen_send(sock, "DELETE %s", folder_);
2647 return imap_cmd_ok(sock, NULL);
2650 static gint imap_cmd_search(SockInfo *sock, const gchar *criteria, GSList **list)
2656 g_return_val_if_fail(criteria != NULL, IMAP_ERROR);
2657 g_return_val_if_fail(list != NULL, IMAP_ERROR);
2661 argbuf = g_ptr_array_new();
2662 imap_cmd_gen_send(sock, "UID SEARCH %s", criteria);
2664 ok = imap_cmd_ok(sock, argbuf);
2665 if (ok != IMAP_SUCCESS) {
2666 ptr_array_free_strings(argbuf);
2667 g_ptr_array_free(argbuf, TRUE);
2671 if ((uidlist = search_array_str(argbuf, "SEARCH ")) != NULL) {
2672 gchar **strlist, **p;
2674 strlist = g_strsplit(uidlist + 7, " ", 0);
2675 for (p = strlist; *p != NULL; ++p) {
2678 if (sscanf(*p, "%d", &msgnum) == 1)
2679 *list = g_slist_append(*list, GINT_TO_POINTER(msgnum));
2681 g_strfreev(strlist);
2683 ptr_array_free_strings(argbuf);
2684 g_ptr_array_free(argbuf, TRUE);
2686 return IMAP_SUCCESS;
2689 static gint imap_cmd_fetch(SockInfo *sock, guint32 uid, const gchar *filename)
2697 g_return_val_if_fail(filename != NULL, IMAP_ERROR);
2699 imap_cmd_gen_send(sock, "UID FETCH %d BODY[]", uid);
2701 while ((ok = imap_cmd_gen_recv(sock, &buf))
2703 if (buf[0] != '*' || buf[1] != ' ') {
2707 if (strstr(buf, "FETCH") != NULL)
2710 if (ok != IMAP_SUCCESS)
2713 cur_pos = strchr(buf, '{');
2714 if (cur_pos == NULL) {
2718 cur_pos = strchr_cpy(cur_pos + 1, '}', size_str, sizeof(size_str));
2719 if (cur_pos == NULL) {
2723 size_num = atol(size_str);
2725 if (*cur_pos != '\0') {
2730 if (recv_bytes_write_to_file(sock, size_num, filename) != 0) {
2735 if (imap_cmd_gen_recv(sock, &buf) != IMAP_SUCCESS) {
2740 if (buf[0] == '\0' || buf[strlen(buf) - 1] != ')') {
2746 ok = imap_cmd_ok(sock, NULL);
2751 static gint imap_cmd_append(IMAPSession *session, const gchar *destfolder,
2752 const gchar *file, gint32 *new_uid)
2757 gchar buf[BUFFSIZE], *imapbuf;
2762 g_return_val_if_fail(file != NULL, IMAP_ERROR);
2764 size = get_file_size_as_crlf(file);
2765 if ((fp = fopen(file, "rb")) == NULL) {
2766 FILE_OP_ERROR(file, "fopen");
2769 QUOTE_IF_REQUIRED(destfolder_, destfolder);
2770 imap_cmd_gen_send(SESSION(session)->sock, "APPEND %s (\\Seen) {%d}", destfolder_, size);
2772 ok = imap_cmd_gen_recv(SESSION(session)->sock, &imapbuf);
2773 if (ok != IMAP_SUCCESS || imapbuf[0] != '+' || imapbuf[1] != ' ') {
2774 log_warning(_("can't append %s to %s\n"), file, destfolder_);
2781 log_print("IMAP4> %s\n", _("(sending file...)"));
2783 while (fgets(buf, sizeof(buf), fp) != NULL) {
2785 if (sock_puts(SESSION(session)->sock, buf) < 0) {
2792 FILE_OP_ERROR(file, "fgets");
2797 sock_puts(SESSION(session)->sock, "");
2801 reply = g_ptr_array_new();
2804 ok = imap_cmd_ok(SESSION(session)->sock, reply);
2805 if (ok != IMAP_SUCCESS)
2806 log_warning(_("can't append message to %s\n"), destfolder_);
2808 (new_uid != NULL) &&
2809 (imap_has_capability(session, "UIDPLUS") && reply->len > 0) &&
2810 ((okmsginfo = g_ptr_array_index(reply, reply->len - 1)) != NULL) &&
2811 (sscanf(okmsginfo, "%*u OK [APPENDUID %*u %u]", &newuid) == 1)) {
2815 ptr_array_free_strings(reply);
2816 g_ptr_array_free(reply, TRUE);
2821 static gint imap_cmd_copy(IMAPSession * session,
2823 const gchar * destfolder, gint32 * new_uid)
2826 gint32 olduid, newuid;
2831 g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
2832 g_return_val_if_fail(session != NULL, IMAP_ERROR);
2833 g_return_val_if_fail(new_uid != NULL, IMAP_ERROR);
2835 QUOTE_IF_REQUIRED(destfolder_, destfolder);
2836 imap_cmd_gen_send(SESSION(session)->sock, "UID COPY %d %s", msgnum, destfolder_);
2838 reply = g_ptr_array_new();
2841 ok = imap_cmd_ok(SESSION(session)->sock, reply);
2842 if (ok != IMAP_SUCCESS)
2843 log_warning(_("can't copy %d to %s\n"), msgnum, destfolder_);
2844 else if (imap_has_capability(session, "UIDPLUS") && reply->len > 0)
2845 if ((okmsginfo = g_ptr_array_index(reply, reply->len - 1)) != NULL &&
2846 sscanf(okmsginfo, "%*u OK [COPYUID %*u %u %u]", &olduid, &newuid) == 2 &&
2850 ptr_array_free_strings(reply);
2851 g_ptr_array_free(reply, TRUE);
2855 gint imap_cmd_envelope(SockInfo *sock, guint32 first_uid, guint32 last_uid)
2857 static GString *header_fields = NULL;
2859 if (header_fields == NULL) {
2860 const HeaderEntry *headers, *elem;
2862 headers = procheader_get_headernames(FALSE);
2863 header_fields = g_string_new("");
2865 for (elem = headers; elem->name != NULL; ++elem) {
2866 gint namelen = strlen(elem->name);
2868 /* Header fields ending with space are not rfc822 headers */
2869 if (elem->name[namelen - 1] == ' ')
2872 /* strip : at the of header field */
2873 if(elem->name[namelen - 1] == ':')
2879 g_string_sprintfa(header_fields, "%s%.*s",
2880 header_fields->str[0] != '\0' ? " " : "",
2881 namelen, elem->name);
2886 (sock, "UID FETCH %d:%d (UID FLAGS RFC822.SIZE BODY.PEEK[HEADER.FIELDS (%s)])",
2887 first_uid, last_uid,
2888 header_fields->str);
2890 return IMAP_SUCCESS;
2893 static gint imap_cmd_store(SockInfo *sock, guint32 first_uid, guint32 last_uid,
2898 imap_cmd_gen_send(sock, "UID STORE %d:%d %s",
2899 first_uid, last_uid, sub_cmd);
2901 if ((ok = imap_cmd_ok(sock, NULL)) != IMAP_SUCCESS) {
2902 log_warning(_("error while imap command: STORE %d:%d %s\n"),
2903 first_uid, last_uid, sub_cmd);
2907 return IMAP_SUCCESS;
2910 static gint imap_cmd_expunge(SockInfo *sock)
2914 imap_cmd_gen_send(sock, "EXPUNGE");
2915 if ((ok = imap_cmd_ok(sock, NULL)) != IMAP_SUCCESS) {
2916 log_warning(_("error while imap command: EXPUNGE\n"));
2920 return IMAP_SUCCESS;
2923 static gint imap_cmd_ok(SockInfo *sock, GPtrArray *argbuf)
2928 gchar cmd_status[IMAPBUFSIZE];
2930 while ((ok = imap_cmd_gen_recv(sock, &buf))
2932 if (buf[0] == '*' && buf[1] == ' ') {
2934 g_ptr_array_add(argbuf, g_strdup(buf + 2));
2938 if (sscanf(buf, "%d %s", &cmd_num, cmd_status) < 2)
2940 else if (cmd_num == imap_cmd_count &&
2941 !strcmp(cmd_status, "OK")) {
2943 g_ptr_array_add(argbuf, g_strdup(buf));
2945 return IMAP_SUCCESS;
2956 static void imap_cmd_gen_send(SockInfo *sock, const gchar *format, ...)
2958 gchar buf[IMAPBUFSIZE];
2959 gchar tmp[IMAPBUFSIZE];
2963 va_start(args, format);
2964 g_vsnprintf(tmp, sizeof(tmp), format, args);
2969 g_snprintf(buf, sizeof(buf), "%d %s\r\n", imap_cmd_count, tmp);
2970 if (!strncasecmp(tmp, "LOGIN ", 6) && (p = strchr(tmp + 6, ' '))) {
2972 log_print("IMAP4> %d %s ********\n", imap_cmd_count, tmp);
2974 log_print("IMAP4> %d %s\n", imap_cmd_count, tmp);
2976 sock_write_all(sock, buf, strlen(buf));
2979 static gint imap_cmd_gen_recv(SockInfo *sock, gchar **buf)
2981 if ((*buf = sock_getline(sock)) == NULL)
2986 log_print("IMAP4< %s\n", *buf);
2988 return IMAP_SUCCESS;
2992 /* misc utility functions */
2994 static gchar *strchr_cpy(const gchar *src, gchar ch, gchar *dest, gint len)
2999 tmp = strchr(src, ch);
3003 memcpy(dest, src, MIN(tmp - src, len - 1));
3004 dest[MIN(tmp - src, len - 1)] = '\0';
3009 static gchar *get_quoted(const gchar *src, gchar ch, gchar *dest, gint len)
3011 const gchar *p = src;
3014 g_return_val_if_fail(*p == ch, NULL);
3019 while (*p != '\0' && *p != ch) {
3021 if (*p == '\\' && *(p + 1) != '\0')
3030 return (gchar *)(*p == ch ? p + 1 : p);
3033 static gchar *search_array_contain_str(GPtrArray *array, const gchar *str)
3037 for (i = 0; i < array->len; i++) {
3040 tmp = g_ptr_array_index(array, i);
3041 if (strstr(tmp, str) != NULL)
3048 static gchar *search_array_str(GPtrArray *array, const gchar *str)
3055 for (i = 0; i < array->len; i++) {
3058 tmp = g_ptr_array_index(array, i);
3059 if (!strncmp(tmp, str, len))
3066 static void imap_path_separator_subst(gchar *str, gchar separator)
3069 gboolean in_escape = FALSE;
3071 if (!separator || separator == '/') return;
3073 for (p = str; *p != '\0'; p++) {
3074 if (*p == '/' && !in_escape)
3076 else if (*p == '&' && *(p + 1) != '-' && !in_escape)
3078 else if (*p == '-' && in_escape)
3083 static gchar *imap_modified_utf7_to_locale(const gchar *mutf7_str)
3086 return g_strdup(mutf7_str);
3088 static iconv_t cd = (iconv_t)-1;
3089 static gboolean iconv_ok = TRUE;
3092 size_t norm_utf7_len;
3094 gchar *to_str, *to_p;
3096 gboolean in_escape = FALSE;
3098 if (!iconv_ok) return g_strdup(mutf7_str);
3100 if (cd == (iconv_t)-1) {
3101 cd = iconv_open(conv_get_current_charset_str(), "UTF-7");
3102 if (cd == (iconv_t)-1) {
3103 g_warning("iconv cannot convert UTF-7 to %s\n",
3104 conv_get_current_charset_str());
3106 return g_strdup(mutf7_str);
3110 norm_utf7 = g_string_new(NULL);
3112 for (p = mutf7_str; *p != '\0'; p++) {
3113 /* replace: '&' -> '+',
3115 escaped ',' -> '/' */
3116 if (!in_escape && *p == '&') {
3117 if (*(p + 1) != '-') {
3118 g_string_append_c(norm_utf7, '+');
3121 g_string_append_c(norm_utf7, '&');
3124 } else if (in_escape && *p == ',') {
3125 g_string_append_c(norm_utf7, '/');
3126 } else if (in_escape && *p == '-') {
3127 g_string_append_c(norm_utf7, '-');
3130 g_string_append_c(norm_utf7, *p);
3134 norm_utf7_p = norm_utf7->str;
3135 norm_utf7_len = norm_utf7->len;
3136 to_len = strlen(mutf7_str) * 5;
3137 to_p = to_str = g_malloc(to_len + 1);
3139 if (iconv(cd, (ICONV_CONST gchar **)&norm_utf7_p, &norm_utf7_len,
3140 &to_p, &to_len) == -1) {
3141 g_warning(_("iconv cannot convert UTF-7 to %s\n"),
3142 conv_get_current_charset_str());
3143 g_string_free(norm_utf7, TRUE);
3145 return g_strdup(mutf7_str);
3148 /* second iconv() call for flushing */
3149 iconv(cd, NULL, NULL, &to_p, &to_len);
3150 g_string_free(norm_utf7, TRUE);
3154 #endif /* !HAVE_ICONV */
3157 static gchar *imap_locale_to_modified_utf7(const gchar *from)
3160 return g_strdup(from);
3162 static iconv_t cd = (iconv_t)-1;
3163 static gboolean iconv_ok = TRUE;
3164 gchar *norm_utf7, *norm_utf7_p;
3165 size_t from_len, norm_utf7_len;
3167 gchar *from_tmp, *to, *p;
3168 gboolean in_escape = FALSE;
3170 if (!iconv_ok) return g_strdup(from);
3172 if (cd == (iconv_t)-1) {
3173 cd = iconv_open("UTF-7", conv_get_current_charset_str());
3174 if (cd == (iconv_t)-1) {
3175 g_warning("iconv cannot convert %s to UTF-7\n",
3176 conv_get_current_charset_str());
3178 return g_strdup(from);
3182 Xstrdup_a(from_tmp, from, return g_strdup(from));
3183 from_len = strlen(from);
3184 norm_utf7_len = from_len * 5;
3185 Xalloca(norm_utf7, norm_utf7_len + 1, return g_strdup(from));
3186 norm_utf7_p = norm_utf7;
3188 #define IS_PRINT(ch) (isprint(ch) && isascii(ch))
3190 while (from_len > 0) {
3191 if (IS_PRINT(*from_tmp)) {
3192 /* printable ascii char */
3193 *norm_utf7_p = *from_tmp;
3200 /* unprintable char: convert to UTF-7 */
3202 !IS_PRINT(from_tmp[mblen]) && mblen < from_len;
3206 if (iconv(cd, (ICONV_CONST gchar **)&from_tmp, &mblen,
3207 &norm_utf7_p, &norm_utf7_len) == -1) {
3208 g_warning("iconv cannot convert %s to UTF-7\n",
3209 conv_get_current_charset_str());
3210 return g_strdup(from);
3213 /* second iconv() call for flushing */
3214 iconv(cd, NULL, NULL, &norm_utf7_p, &norm_utf7_len);
3220 *norm_utf7_p = '\0';
3221 to_str = g_string_new(NULL);
3222 for (p = norm_utf7; p < norm_utf7_p; p++) {
3223 /* replace: '&' -> "&-",
3225 escaped '/' -> ',' */
3226 if (!in_escape && *p == '&') {
3227 g_string_append(to_str, "&-");
3228 } else if (!in_escape && *p == '+') {
3229 g_string_append_c(to_str, '&');
3231 } else if (in_escape && *p == '/') {
3232 g_string_append_c(to_str, ',');
3233 } else if (in_escape && *p == '-') {
3235 g_string_append_c(to_str, '-');
3237 g_string_append_c(to_str, *p);
3243 g_string_append_c(to_str, '-');
3247 g_string_free(to_str, FALSE);
3250 #endif /* !HAVE_ICONV */
3253 static gboolean imap_rename_folder_func(GNode *node, gpointer data)
3255 FolderItem *item = node->data;
3256 gchar **paths = data;
3257 const gchar *oldpath = paths[0];
3258 const gchar *newpath = paths[1];
3260 gchar *new_itempath;
3263 oldpathlen = strlen(oldpath);
3264 if (strncmp(oldpath, item->path, oldpathlen) != 0) {
3265 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
3269 base = item->path + oldpathlen;
3270 while (*base == G_DIR_SEPARATOR) base++;
3272 new_itempath = g_strdup(newpath);
3274 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
3277 item->path = new_itempath;
3282 static gint get_list_of_uids(Folder *folder, IMAPFolderItem *item, GSList **msgnum_list)
3284 gint ok, nummsgs = 0, lastuid_old;
3285 IMAPSession *session;
3286 GSList *uidlist, *elem;
3289 session = imap_session_get(folder);
3290 g_return_val_if_fail(session != NULL, -1);
3292 ok = imap_select(session, IMAP_FOLDER(folder), item->item.path,
3293 NULL, NULL, NULL, NULL);
3294 if (ok != IMAP_SUCCESS)
3297 cmd_buf = g_strdup_printf("UID %d:*", item->lastuid + 1);
3298 ok = imap_cmd_search(SESSION(session)->sock, cmd_buf, &uidlist);
3301 if (ok == IMAP_SOCKET) {
3302 session_destroy((Session *)session);
3303 ((RemoteFolder *)folder)->session = NULL;
3307 if (ok != IMAP_SUCCESS) {
3311 argbuf = g_ptr_array_new();
3313 cmd_buf = g_strdup_printf("UID FETCH %d:* (UID)", item->lastuid + 1);
3314 imap_cmd_gen_send(SESSION(session)->sock, cmd_buf);
3316 ok = imap_cmd_ok(SESSION(session)->sock, argbuf);
3317 if (ok != IMAP_SUCCESS) {
3318 ptr_array_free_strings(argbuf);
3319 g_ptr_array_free(argbuf, TRUE);
3323 for(i = 0; i < argbuf->len; i++) {
3326 if((ret = sscanf(g_ptr_array_index(argbuf, i),
3327 "%*d FETCH (UID %d)", &msgnum)) == 1)
3328 uidlist = g_slist_prepend(uidlist, GINT_TO_POINTER(msgnum));
3330 ptr_array_free_strings(argbuf);
3331 g_ptr_array_free(argbuf, TRUE);
3334 lastuid_old = item->lastuid;
3335 *msgnum_list = g_slist_copy(item->uid_list);
3336 nummsgs = g_slist_length(*msgnum_list);
3337 debug_print("Got %d uids from cache\n", g_slist_length(item->uid_list));
3339 for (elem = uidlist; elem != NULL; elem = g_slist_next(elem)) {
3342 msgnum = GPOINTER_TO_INT(elem->data);
3343 if (msgnum > lastuid_old) {
3344 *msgnum_list = g_slist_prepend(*msgnum_list, GINT_TO_POINTER(msgnum));
3345 item->uid_list = g_slist_prepend(item->uid_list, GINT_TO_POINTER(msgnum));
3348 if(msgnum > item->lastuid)
3349 item->lastuid = msgnum;
3352 g_slist_free(uidlist);
3357 gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list)
3359 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3360 IMAPSession *session;
3361 gint ok, nummsgs = 0, exists, recent, unseen, uid_val, uid_next;
3365 g_return_val_if_fail(folder != NULL, -1);
3366 g_return_val_if_fail(item != NULL, -1);
3367 g_return_val_if_fail(item->item.path != NULL, -1);
3368 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3369 g_return_val_if_fail(folder->account != NULL, -1);
3371 session = imap_session_get(folder);
3372 g_return_val_if_fail(session != NULL, -1);
3374 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
3375 &exists, &recent, &uid_next, &uid_val, &unseen);
3376 if (ok != IMAP_SUCCESS)
3379 /* If old uid_next matches new uid_next we can be sure no message
3380 was added to the folder */
3381 if (uid_next == item->uid_next) {
3382 nummsgs = g_slist_length(item->uid_list);
3384 /* If number of messages is still the same we
3385 know our caches message numbers are still valid,
3386 otherwise if the number of messages has decrease
3387 we discard our cache to start a new scan to find
3388 out which numbers have been removed */
3389 if (exists == nummsgs) {
3390 *msgnum_list = g_slist_copy(item->uid_list);
3392 } else if (exists < nummsgs) {
3393 debug_print("Freeing imap uid cache");
3395 g_slist_free(item->uid_list);
3396 item->uid_list = NULL;
3399 item->uid_next = uid_next;
3402 *msgnum_list = NULL;
3406 nummsgs = get_list_of_uids(folder, item, &uidlist);
3408 if (nummsgs != exists) {
3409 /* Cache contains more messages then folder, we have cached
3410 an old UID of a message that was removed and new messages
3411 have been added too, otherwise the uid_next check would
3413 debug_print("Freeing imap uid cache");
3415 g_slist_free(item->uid_list);
3416 item->uid_list = NULL;
3418 g_slist_free(*msgnum_list);
3420 nummsgs = get_list_of_uids(folder, item, &uidlist);
3423 *msgnum_list = uidlist;
3425 dir = folder_item_get_path((FolderItem *)item);
3426 debug_print("removing old messages from %s\n", dir);
3427 remove_numbered_files_not_in_list(dir, *msgnum_list);
3433 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item)
3438 flags.perm_flags = MSG_NEW|MSG_UNREAD;
3439 flags.tmp_flags = 0;
3441 g_return_val_if_fail(item != NULL, NULL);
3442 g_return_val_if_fail(file != NULL, NULL);
3444 if (item->stype == F_QUEUE) {
3445 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
3446 } else if (item->stype == F_DRAFT) {
3447 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
3450 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3451 if (!msginfo) return NULL;
3453 msginfo->folder = item;
3458 static int compare_uint(gconstpointer a, gconstpointer b)
3463 GSList *imap_get_msginfos(Folder *folder, FolderItem *item, GSList *msgnum_list)
3465 IMAPSession *session;
3466 GSList *sorted_list, *elem, *ret = NULL;
3467 gint ok, startnum, lastnum;
3469 g_return_val_if_fail(folder != NULL, NULL);
3470 g_return_val_if_fail(item != NULL, NULL);
3471 g_return_val_if_fail(msgnum_list != NULL, NULL);
3473 session = imap_session_get(folder);
3474 g_return_val_if_fail(session != NULL, NULL);
3476 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3477 NULL, NULL, NULL, NULL);
3478 if (ok != IMAP_SUCCESS)
3481 sorted_list = g_slist_sort(g_slist_copy(msgnum_list), compare_uint);
3483 startnum = lastnum = GPOINTER_TO_INT(sorted_list->data);
3485 for (elem = sorted_list;; elem = g_slist_next(elem)) {
3489 num = GPOINTER_TO_INT(elem->data);
3491 if (num > lastnum + 1 || elem == NULL) {
3492 if (!(item->stype == F_QUEUE || item->stype == F_DRAFT)) {
3493 ret = g_slist_concat(ret,
3494 imap_get_uncached_messages(
3495 session, item, startnum, lastnum));
3498 for (i = startnum; i <= lastnum; ++i) {
3501 file = imap_fetch_msg(folder, item, i);
3503 MsgInfo *msginfo = imap_parse_msg(file, item);
3504 if (msginfo != NULL) {
3505 msginfo->msgnum = i;
3506 ret = g_slist_append(ret, msginfo);
3521 g_slist_free(sorted_list);
3526 MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid)
3528 IMAPSession *session;
3529 MsgInfo *msginfo = NULL;
3532 g_return_val_if_fail(folder != NULL, NULL);
3533 g_return_val_if_fail(item != NULL, NULL);
3535 session = imap_session_get(folder);
3536 g_return_val_if_fail(session != NULL, NULL);
3538 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3539 NULL, NULL, NULL, NULL);
3540 if (ok != IMAP_SUCCESS)
3543 if (!(item->stype == F_QUEUE || item->stype == F_DRAFT)) {
3546 list = imap_get_uncached_messages(session, item, uid, uid);
3548 msginfo = (MsgInfo *)list->data;
3551 procmsg_msg_list_free(list);
3555 file = imap_fetch_msg(folder, item, uid);
3557 msginfo = imap_parse_msg(file, item);
3558 if (msginfo != NULL)
3559 msginfo->msgnum = uid;
3567 gboolean imap_check_msgnum_validity(Folder *folder, FolderItem *_item)
3569 IMAPSession *session;
3570 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3571 gint ok, exists = 0, recent = 0, unseen = 0;
3572 guint32 uid_next, uid_validity = 0;
3574 g_return_val_if_fail(folder != NULL, FALSE);
3575 g_return_val_if_fail(item != NULL, FALSE);
3576 g_return_val_if_fail(item->item.folder != NULL, FALSE);
3577 g_return_val_if_fail(FOLDER_CLASS(item->item.folder) == &imap_class, FALSE);
3579 session = imap_session_get(folder);
3580 g_return_val_if_fail(session != NULL, FALSE);
3582 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
3583 &exists, &recent, &uid_next, &uid_validity, &unseen);
3584 if (ok != IMAP_SUCCESS)
3587 if(item->item.mtime == uid_validity)
3590 debug_print("Freeing imap uid cache");
3592 g_slist_free(item->uid_list);
3593 item->uid_list = NULL;
3595 item->item.mtime = uid_validity;
3597 imap_delete_all_cached_messages((FolderItem *)item);
3602 void imap_change_flags(Folder *folder, FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
3604 IMAPSession *session;
3605 IMAPFlags flags_set = 0, flags_unset = 0;
3606 gint ok = IMAP_SUCCESS;
3608 g_return_if_fail(folder != NULL);
3609 g_return_if_fail(folder->class == &imap_class);
3610 g_return_if_fail(item != NULL);
3611 g_return_if_fail(item->folder == folder);
3612 g_return_if_fail(msginfo != NULL);
3613 g_return_if_fail(msginfo->folder == item);
3615 session = imap_session_get(folder);
3616 if (!session) return;
3618 if ((ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
3619 NULL, NULL, NULL, NULL)) != IMAP_SUCCESS)
3622 if (!MSG_IS_MARKED(msginfo->flags) && (newflags & MSG_MARKED))
3623 flags_set |= IMAP_FLAG_FLAGGED;
3624 if ( MSG_IS_MARKED(msginfo->flags) && !(newflags & MSG_MARKED))
3625 flags_unset |= IMAP_FLAG_FLAGGED;
3627 if (!MSG_IS_UNREAD(msginfo->flags) && (newflags & MSG_UNREAD))
3628 flags_unset |= IMAP_FLAG_SEEN;
3629 if ( MSG_IS_UNREAD(msginfo->flags) && !(newflags & MSG_UNREAD))
3630 flags_set |= IMAP_FLAG_SEEN;
3632 if (!MSG_IS_REPLIED(msginfo->flags) && (newflags & MSG_REPLIED))
3633 flags_set |= IMAP_FLAG_ANSWERED;
3634 if ( MSG_IS_REPLIED(msginfo->flags) && !(newflags & MSG_REPLIED))
3635 flags_set |= IMAP_FLAG_ANSWERED;
3638 ok = imap_set_message_flags(session, msginfo->msgnum,
3639 msginfo->msgnum, flags_set, TRUE);
3640 if (ok != IMAP_SUCCESS) return;
3644 ok = imap_set_message_flags(session, msginfo->msgnum,
3645 msginfo->msgnum, flags_unset, FALSE);
3646 if (ok != IMAP_SUCCESS) return;
3649 msginfo->flags.perm_flags = newflags;