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_new(IMAPSession, 1);
559 SESSION(session)->type = SESSION_IMAP;
560 SESSION(session)->server = g_strdup(account->recv_server);
561 SESSION(session)->sock = imap_sock;
562 SESSION(session)->connected = TRUE;
563 SESSION(session)->phase = SESSION_READY;
564 SESSION(session)->last_access_time = time(NULL);
565 SESSION(session)->data = NULL;
567 SESSION(session)->destroy = imap_session_destroy;
569 session->capability = NULL;
571 session->mbox = NULL;
572 session->authenticated = is_preauth;
574 session_list = g_list_append(session_list, session);
576 return SESSION(session);
579 void imap_session_authenticate(IMAPSession *session, const PrefsAccount *account)
583 g_return_if_fail(account->userid != NULL);
585 pass = account->passwd;
588 tmp_pass = input_dialog_query_password(account->recv_server, account->userid);
591 Xstrdup_a(pass, tmp_pass, {g_free(tmp_pass); return;});
595 if (imap_cmd_login(SESSION(session)->sock, account->userid, pass) != IMAP_SUCCESS) {
596 imap_cmd_logout(SESSION(session)->sock);
600 session->authenticated = TRUE;
603 void imap_session_destroy(Session *session)
605 sock_close(session->sock);
606 session->sock = NULL;
608 g_free(IMAP_SESSION(session)->mbox);
610 g_strfreev(IMAP_SESSION(session)->capability);
612 session_list = g_list_remove(session_list, session);
615 void imap_session_destroy_all(void)
617 while (session_list != NULL) {
618 IMAPSession *session = (IMAPSession *)session_list->data;
620 imap_cmd_logout(SESSION(session)->sock);
621 session_destroy(SESSION(session));
625 gchar *imap_fetch_msg(Folder *folder, FolderItem *item, gint uid)
627 gchar *path, *filename;
628 IMAPSession *session;
631 g_return_val_if_fail(folder != NULL, NULL);
632 g_return_val_if_fail(item != NULL, NULL);
634 path = folder_item_get_path(item);
635 if (!is_dir_exist(path))
637 filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(uid), NULL);
640 if (is_file_exist(filename)) {
641 debug_print("message %d has been already cached.\n", uid);
645 session = imap_session_get(folder);
651 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
652 NULL, NULL, NULL, NULL);
653 if (ok != IMAP_SUCCESS) {
654 g_warning("can't select mailbox %s\n", item->path);
659 debug_print("getting message %d...\n", uid);
660 ok = imap_cmd_fetch(SESSION(session)->sock, (guint32)uid, filename);
662 if (ok != IMAP_SUCCESS) {
663 g_warning("can't fetch message %d\n", uid);
671 gint imap_add_msg(Folder *folder, FolderItem *dest, const gchar *file,
672 gboolean remove_source)
675 IMAPSession *session;
678 g_return_val_if_fail(folder != NULL, -1);
679 g_return_val_if_fail(dest != NULL, -1);
680 g_return_val_if_fail(file != NULL, -1);
682 session = imap_session_get(folder);
683 if (!session) return -1;
685 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
686 ok = imap_cmd_append(session, destdir, file, &newuid);
689 if (ok != IMAP_SUCCESS) {
690 g_warning("can't append message %s\n", file);
695 if (unlink(file) < 0)
696 FILE_OP_ERROR(file, "unlink");
702 static gint imap_do_copy(Folder *folder, FolderItem *dest, MsgInfo *msginfo,
703 gboolean remove_source)
706 IMAPSession *session;
707 IMAPFlags iflags = 0;
711 g_return_val_if_fail(folder != NULL, -1);
712 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
713 g_return_val_if_fail(dest != NULL, -1);
714 g_return_val_if_fail(msginfo != NULL, -1);
716 session = imap_session_get(folder);
717 if (!session) return -1;
719 if (msginfo->folder == dest) {
720 g_warning("the src folder is identical to the dest.\n");
724 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
726 /* ensure source folder selected */
727 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
728 NULL, NULL, NULL, NULL);
729 if (ok != IMAP_SUCCESS)
733 debug_print("Moving message %s%c%d to %s ...\n",
734 msginfo->folder->path, G_DIR_SEPARATOR,
735 msginfo->msgnum, destdir);
737 debug_print("Copying message %s%c%d to %s ...\n",
738 msginfo->folder->path, G_DIR_SEPARATOR,
739 msginfo->msgnum, destdir);
741 ok = imap_cmd_copy(session, msginfo->msgnum, destdir, &newuid);
743 if (ok == IMAP_SUCCESS && remove_source) {
744 imap_set_message_flags(session, msginfo->msgnum, msginfo->msgnum,
745 IMAP_FLAG_DELETED, TRUE);
746 ok = imap_cmd_expunge(SESSION(session)->sock);
749 /* get the dest folder to set the flags */
751 ok = imap_select(session, IMAP_FOLDER(folder), dest->path,
752 NULL, NULL, NULL, NULL);
753 if (ok != IMAP_SUCCESS) /* the folder disappeared? */
756 if (msginfo->flags.perm_flags & MSG_MARKED) iflags |= IMAP_FLAG_FLAGGED;
757 if (msginfo->flags.perm_flags & MSG_REPLIED) iflags |= IMAP_FLAG_ANSWERED;
759 if (imap_set_message_flags(session, newuid, newuid, iflags, TRUE)
766 if (ok == IMAP_SUCCESS)
773 static gint imap_do_copy_msgs_with_dest(Folder *folder, FolderItem *dest,
775 gboolean remove_source)
780 IMAPSession *session;
781 gint ok = IMAP_SUCCESS;
783 g_return_val_if_fail(folder != NULL, -1);
784 g_return_val_if_fail(dest != NULL, -1);
785 g_return_val_if_fail(msglist != NULL, -1);
787 session = imap_session_get(folder);
788 if (!session) return -1;
790 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
792 for (cur = msglist; cur != NULL; cur = cur->next) {
793 msginfo = (MsgInfo *)cur->data;
795 if (msginfo->folder == dest) {
796 g_warning("the src folder is identical to the dest.\n");
800 /* ensure source folder selected */
801 ok = imap_select(session, IMAP_FOLDER(folder),
802 msginfo->folder->path, NULL, NULL, NULL, NULL);
805 debug_print("Moving message %s%c%d to %s ...\n",
806 msginfo->folder->path, G_DIR_SEPARATOR,
807 msginfo->msgnum, destdir);
809 debug_print("Copying message %s%c%d to %s ...\n",
810 msginfo->folder->path, G_DIR_SEPARATOR,
811 msginfo->msgnum, destdir);
813 ok = imap_cmd_copy(session, msginfo, destdir);
815 if (ok == IMAP_SUCCESS && remove_source) {
816 imap_set_message_flags
817 (session, msginfo->msgnum, msginfo->msgnum,
818 IMAP_FLAG_DELETED, TRUE);
823 ok = imap_cmd_expunge(SESSION(session)->sock);
827 if (ok == IMAP_SUCCESS)
834 gint imap_move_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
839 g_return_val_if_fail(folder != NULL, -1);
840 g_return_val_if_fail(dest != NULL, -1);
841 g_return_val_if_fail(msginfo != NULL, -1);
842 g_return_val_if_fail(msginfo->folder != NULL, -1);
844 if (folder == msginfo->folder->folder)
845 return imap_do_copy(folder, dest, msginfo, TRUE);
847 srcfile = procmsg_get_message_file(msginfo);
848 if (!srcfile) return -1;
850 ret = imap_add_msg(folder, dest, srcfile, FALSE);
854 if(folder_item_remove_msg(msginfo->folder, msginfo->msgnum)) {
863 gint imap_move_msgs_with_dest(Folder *folder, FolderItem *dest,
870 g_return_val_if_fail(folder != NULL, -1);
871 g_return_val_if_fail(dest != NULL, -1);
872 g_return_val_if_fail(msglist != NULL, -1);
874 msginfo = (MsgInfo *)msglist->data;
875 if (folder == msginfo->folder->folder)
876 return imap_do_copy_msgs_with_dest(folder, dest, msglist, TRUE);
878 for (cur = msglist; cur != NULL; cur = cur->next) {
879 msginfo = (MsgInfo *)cur->data;
880 ret = imap_move_msg(folder, dest, msginfo);
881 if (ret == -1) break;
888 gint imap_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
893 g_return_val_if_fail(folder != NULL, -1);
894 g_return_val_if_fail(dest != NULL, -1);
895 g_return_val_if_fail(msginfo != NULL, -1);
896 g_return_val_if_fail(msginfo->folder != NULL, -1);
898 if (folder == msginfo->folder->folder)
899 return imap_do_copy(folder, dest, msginfo, FALSE);
901 srcfile = procmsg_get_message_file(msginfo);
902 if (!srcfile) return -1;
904 ret = imap_add_msg(folder, dest, srcfile, FALSE);
912 gint imap_copy_msgs_with_dest(Folder *folder, FolderItem *dest,
919 g_return_val_if_fail(folder != NULL, -1);
920 g_return_val_if_fail(dest != NULL, -1);
921 g_return_val_if_fail(msglist != NULL, -1);
923 msginfo = (MsgInfo *)msglist->data;
924 if (folder == msginfo->folder->folder)
925 return imap_do_copy_msgs_with_dest
926 (folder, dest, msglist, FALSE);
928 for (cur = msglist; cur != NULL; cur = cur->next) {
929 msginfo = (MsgInfo *)cur->data;
930 ret = imap_copy_msg(folder, dest, msginfo);
931 if (ret == -1) break;
938 gint imap_remove_msg(Folder *folder, FolderItem *item, gint uid)
941 IMAPSession *session;
944 g_return_val_if_fail(folder != NULL, -1);
945 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
946 g_return_val_if_fail(item != NULL, -1);
948 session = imap_session_get(folder);
949 if (!session) return -1;
951 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
952 NULL, NULL, NULL, NULL);
953 if (ok != IMAP_SUCCESS)
956 ok = imap_set_message_flags
957 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
958 (guint32)uid, (guint32)uid, IMAP_FLAG_DELETED, TRUE);
959 if (ok != IMAP_SUCCESS) {
960 log_warning(_("can't set deleted flags: %d\n"), uid);
964 ok = imap_cmd_expunge(SESSION(session)->sock);
965 if (ok != IMAP_SUCCESS) {
966 log_warning(_("can't expunge\n"));
970 dir = folder_item_get_path(item);
971 if (is_dir_exist(dir))
972 remove_numbered_files(dir, uid, uid);
978 gint imap_remove_msgs(Folder *folder, FolderItem *item, GSList *msglist)
981 IMAPSession *session;
987 g_return_val_if_fail(folder != NULL, -1);
988 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
989 g_return_val_if_fail(item != NULL, -1);
990 g_return_val_if_fail(msglist != NULL, -1);
992 session = imap_session_get(folder);
993 if (!session) return -1;
995 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
996 NULL, NULL, NULL, NULL);
997 if (ok != IMAP_SUCCESS)
1000 for (cur = msglist; cur != NULL; cur = cur->next) {
1001 msginfo = (MsgInfo *)cur->data;
1002 uid = msginfo->msgnum;
1003 ok = imap_set_message_flags
1004 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
1005 uid, uid, IMAP_FLAG_DELETED, TRUE);
1006 if (ok != IMAP_SUCCESS) {
1007 log_warning(_("can't set deleted flags: %d\n"), uid);
1012 ok = imap_cmd_expunge(SESSION(session)->sock);
1013 if (ok != IMAP_SUCCESS) {
1014 log_warning(_("can't expunge\n"));
1018 dir = folder_item_get_path(item);
1019 if (is_dir_exist(dir)) {
1020 for (cur = msglist; cur != NULL; cur = cur->next) {
1021 msginfo = (MsgInfo *)cur->data;
1022 uid = msginfo->msgnum;
1023 remove_numbered_files(dir, uid, uid);
1028 return IMAP_SUCCESS;
1031 gint imap_remove_all_msg(Folder *folder, FolderItem *item)
1033 gint exists, recent, unseen;
1034 guint32 uid_validity;
1036 IMAPSession *session;
1039 g_return_val_if_fail(folder != NULL, -1);
1040 g_return_val_if_fail(item != NULL, -1);
1042 session = imap_session_get(folder);
1043 if (!session) return -1;
1045 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
1046 &exists, &recent, &unseen, &uid_validity);
1047 if (ok != IMAP_SUCCESS)
1050 return IMAP_SUCCESS;
1052 imap_cmd_gen_send(SESSION(session)->sock,
1053 "STORE 1:%d +FLAGS (\\Deleted)", exists);
1054 ok = imap_cmd_ok(SESSION(session)->sock, NULL);
1055 if (ok != IMAP_SUCCESS) {
1056 log_warning(_("can't set deleted flags: 1:%d\n"), exists);
1060 ok = imap_cmd_expunge(SESSION(session)->sock);
1061 if (ok != IMAP_SUCCESS) {
1062 log_warning(_("can't expunge\n"));
1066 dir = folder_item_get_path(item);
1067 if (is_dir_exist(dir))
1068 remove_all_numbered_files(dir);
1071 return IMAP_SUCCESS;
1074 gboolean imap_is_msg_changed(Folder *folder, FolderItem *item, MsgInfo *msginfo)
1076 /* TODO: properly implement this method */
1080 gint imap_scan_folder(Folder *folder, FolderItem *item)
1082 IMAPSession *session;
1083 gint messages, recent, unseen;
1084 guint32 uid_next, uid_validity;
1087 g_return_val_if_fail(folder != NULL, -1);
1088 g_return_val_if_fail(item != NULL, -1);
1090 session = imap_session_get(folder);
1091 if (!session) return -1;
1093 ok = imap_status(session, IMAP_FOLDER(folder), item->path,
1094 &messages, &recent, &uid_next, &uid_validity, &unseen);
1095 if (ok != IMAP_SUCCESS) return -1;
1097 item->new = unseen > 0 ? recent : 0;
1098 item->unread = unseen;
1099 item->total = messages;
1100 item->last_num = (messages > 0 && uid_next > 0) ? uid_next - 1 : 0;
1101 /* item->mtime = uid_validity; */
1106 void imap_scan_tree(Folder *folder)
1109 IMAPSession *session;
1110 gchar *root_folder = NULL;
1112 g_return_if_fail(folder != NULL);
1113 g_return_if_fail(folder->account != NULL);
1115 session = imap_session_get(folder);
1117 if (!folder->node) {
1118 folder_tree_destroy(folder);
1119 item = folder_item_new(folder, folder->name, NULL);
1120 item->folder = folder;
1121 folder->node = g_node_new(item);
1126 if (folder->account->imap_dir && *folder->account->imap_dir) {
1127 Xstrdup_a(root_folder, folder->account->imap_dir, return);
1128 strtailchomp(root_folder, '/');
1129 debug_print("IMAP root directory: %s\n", root_folder);
1132 item = folder_item_new(folder, folder->name, root_folder);
1133 item->folder = folder;
1134 item->no_select = TRUE;
1135 folder->node = g_node_new(item);
1137 imap_scan_tree_recursive(session, item);
1139 imap_create_missing_folders(folder);
1142 static gint imap_scan_tree_recursive(IMAPSession *session, FolderItem *item)
1145 IMAPFolder *imapfolder;
1146 FolderItem *new_item;
1147 GSList *item_list, *cur;
1149 gchar *wildcard_path;
1153 g_return_val_if_fail(item != NULL, -1);
1154 g_return_val_if_fail(item->folder != NULL, -1);
1155 g_return_val_if_fail(item->no_sub == FALSE, -1);
1157 folder = FOLDER(item->folder);
1158 imapfolder = IMAP_FOLDER(folder);
1160 separator = imap_get_path_separator(imapfolder, item->path);
1162 if (item->folder->ui_func)
1163 item->folder->ui_func(folder, item, folder->ui_func_data);
1166 wildcard[0] = separator;
1169 real_path = imap_get_real_path(imapfolder, item->path);
1173 real_path = g_strdup("");
1176 Xstrcat_a(wildcard_path, real_path, wildcard,
1177 {g_free(real_path); return IMAP_ERROR;});
1178 QUOTE_IF_REQUIRED(wildcard_path, wildcard_path);
1180 imap_cmd_gen_send(SESSION(session)->sock, "LIST \"\" %s",
1183 strtailchomp(real_path, separator);
1184 item_list = imap_parse_list(folder, session, real_path, NULL);
1187 for (cur = item_list; cur != NULL; cur = cur->next) {
1188 new_item = cur->data;
1189 if (!strcmp(new_item->path, "INBOX")) {
1190 if (!folder->inbox) {
1191 new_item->stype = F_INBOX;
1192 item->folder->inbox = new_item;
1194 folder_item_destroy(new_item);
1197 } else if (!item->parent || item->stype == F_INBOX) {
1200 base = g_basename(new_item->path);
1202 if (!folder->outbox && !strcasecmp(base, "Sent")) {
1203 new_item->stype = F_OUTBOX;
1204 folder->outbox = new_item;
1205 } else if (!folder->draft && !strcasecmp(base, "Drafts")) {
1206 new_item->stype = F_DRAFT;
1207 folder->draft = new_item;
1208 } else if (!folder->queue && !strcasecmp(base, "Queue")) {
1209 new_item->stype = F_QUEUE;
1210 folder->queue = new_item;
1211 } else if (!folder->trash && !strcasecmp(base, "Trash")) {
1212 new_item->stype = F_TRASH;
1213 folder->trash = new_item;
1216 folder_item_append(item, new_item);
1217 if (new_item->no_select == FALSE)
1218 imap_scan_folder(folder, new_item);
1219 if (new_item->no_sub == FALSE)
1220 imap_scan_tree_recursive(session, new_item);
1223 return IMAP_SUCCESS;
1226 static GSList *imap_parse_list(Folder *folder, IMAPSession *session,
1227 const gchar *real_path, gchar *separator)
1229 gchar buf[IMAPBUFSIZE];
1231 gchar separator_str[16];
1234 gchar *loc_name, *loc_path;
1235 GSList *item_list = NULL;
1237 FolderItem *new_item;
1239 debug_print("getting list of %s ...\n",
1240 *real_path ? real_path : "\"\"");
1242 str = g_string_new(NULL);
1245 if (sock_gets(SESSION(session)->sock, buf, sizeof(buf)) <= 0) {
1246 log_warning(_("error occured while getting LIST.\n"));
1250 if (buf[0] != '*' || buf[1] != ' ') {
1251 log_print("IMAP4< %s\n", buf);
1254 debug_print("IMAP4< %s\n", buf);
1256 g_string_assign(str, buf);
1258 if (strncmp(p, "LIST ", 5) != 0) continue;
1261 if (*p != '(') continue;
1263 p = strchr_cpy(p, ')', flags, sizeof(flags));
1265 while (*p == ' ') p++;
1267 p = strchr_cpy(p, ' ', separator_str, sizeof(separator_str));
1269 extract_quote(separator_str, '"');
1270 if (!strcmp(separator_str, "NIL"))
1271 separator_str[0] = '\0';
1273 *separator = separator_str[0];
1276 while (*p == ' ') p++;
1277 if (*p == '{' || *p == '"')
1278 p = imap_parse_atom(SESSION(session)->sock, p,
1279 buf, sizeof(buf), str);
1281 strncpy2(buf, p, sizeof(buf));
1282 strtailchomp(buf, separator_str[0]);
1283 if (buf[0] == '\0') continue;
1284 if (!strcmp(buf, real_path)) continue;
1286 if (separator_str[0] != '\0')
1287 subst_char(buf, separator_str[0], '/');
1288 name = g_basename(buf);
1289 if (name[0] == '.') continue;
1291 loc_name = imap_modified_utf7_to_locale(name);
1292 loc_path = imap_modified_utf7_to_locale(buf);
1293 new_item = folder_item_new(folder, loc_name, loc_path);
1294 if (strcasestr(flags, "\\Noinferiors") != NULL)
1295 new_item->no_sub = TRUE;
1296 if (strcmp(buf, "INBOX") != 0 &&
1297 strcasestr(flags, "\\Noselect") != NULL)
1298 new_item->no_select = TRUE;
1300 item_list = g_slist_append(item_list, new_item);
1302 debug_print("folder %s has been added.\n", loc_path);
1307 g_string_free(str, TRUE);
1312 gint imap_create_tree(Folder *folder)
1314 g_return_val_if_fail(folder != NULL, -1);
1315 g_return_val_if_fail(folder->node != NULL, -1);
1316 g_return_val_if_fail(folder->node->data != NULL, -1);
1317 g_return_val_if_fail(folder->account != NULL, -1);
1319 imap_scan_tree(folder);
1320 imap_create_missing_folders(folder);
1325 static void imap_create_missing_folders(Folder *folder)
1327 g_return_if_fail(folder != NULL);
1330 folder->inbox = imap_create_special_folder
1331 (folder, F_INBOX, "INBOX");
1333 if (!folder->outbox)
1334 folder->outbox = imap_create_special_folder
1335 (folder, F_OUTBOX, "Sent");
1337 folder->draft = imap_create_special_folder
1338 (folder, F_DRAFT, "Drafts");
1340 folder->queue = imap_create_special_folder
1341 (folder, F_QUEUE, "Queue");
1344 folder->trash = imap_create_special_folder
1345 (folder, F_TRASH, "Trash");
1348 static FolderItem *imap_create_special_folder(Folder *folder,
1349 SpecialFolderItemType stype,
1353 FolderItem *new_item;
1355 g_return_val_if_fail(folder != NULL, NULL);
1356 g_return_val_if_fail(folder->node != NULL, NULL);
1357 g_return_val_if_fail(folder->node->data != NULL, NULL);
1358 g_return_val_if_fail(folder->account != NULL, NULL);
1359 g_return_val_if_fail(name != NULL, NULL);
1361 item = FOLDER_ITEM(folder->node->data);
1362 new_item = imap_create_folder(folder, item, name);
1365 g_warning("Can't create '%s'\n", name);
1366 if (!folder->inbox) return NULL;
1368 new_item = imap_create_folder(folder, folder->inbox, name);
1370 g_warning("Can't create '%s' under INBOX\n", name);
1372 new_item->stype = stype;
1374 new_item->stype = stype;
1379 FolderItem *imap_create_folder(Folder *folder, FolderItem *parent,
1382 gchar *dirpath, *imap_path;
1383 IMAPSession *session;
1384 FolderItem *new_item;
1390 g_return_val_if_fail(folder != NULL, NULL);
1391 g_return_val_if_fail(folder->account != NULL, NULL);
1392 g_return_val_if_fail(parent != NULL, NULL);
1393 g_return_val_if_fail(name != NULL, NULL);
1395 session = imap_session_get(folder);
1396 if (!session) return NULL;
1398 if (!parent->parent && strcmp(name, "INBOX") == 0)
1399 dirpath = g_strdup(name);
1400 else if (parent->path)
1401 dirpath = g_strconcat(parent->path, "/", name, NULL);
1402 else if ((p = strchr(name, '/')) != NULL && *(p + 1) != '\0')
1403 dirpath = g_strdup(name);
1404 else if (folder->account->imap_dir && *folder->account->imap_dir) {
1407 Xstrdup_a(imap_dir, folder->account->imap_dir, return NULL);
1408 strtailchomp(imap_dir, '/');
1409 dirpath = g_strconcat(imap_dir, "/", name, NULL);
1411 dirpath = g_strdup(name);
1413 /* keep trailing directory separator to create a folder that contains
1415 imap_path = imap_locale_to_modified_utf7(dirpath);
1416 strtailchomp(dirpath, '/');
1417 Xstrdup_a(new_name, name, {g_free(dirpath); return NULL;});
1418 strtailchomp(new_name, '/');
1419 separator = imap_get_path_separator(IMAP_FOLDER(folder), imap_path);
1420 imap_path_separator_subst(imap_path, separator);
1421 subst_char(new_name, '/', separator);
1423 if (strcmp(name, "INBOX") != 0) {
1426 gboolean exist = FALSE;
1428 argbuf = g_ptr_array_new();
1429 ok = imap_cmd_list(SESSION(session)->sock, NULL, imap_path,
1431 if (ok != IMAP_SUCCESS) {
1432 log_warning(_("can't create mailbox: LIST failed\n"));
1435 ptr_array_free_strings(argbuf);
1436 g_ptr_array_free(argbuf, TRUE);
1440 for (i = 0; i < argbuf->len; i++) {
1442 str = g_ptr_array_index(argbuf, i);
1443 if (!strncmp(str, "LIST ", 5)) {
1448 ptr_array_free_strings(argbuf);
1449 g_ptr_array_free(argbuf, TRUE);
1452 ok = imap_cmd_create(SESSION(session)->sock, imap_path);
1453 if (ok != IMAP_SUCCESS) {
1454 log_warning(_("can't create mailbox\n"));
1462 new_item = folder_item_new(folder, new_name, dirpath);
1463 folder_item_append(parent, new_item);
1467 dirpath = folder_item_get_path(new_item);
1468 if (!is_dir_exist(dirpath))
1469 make_dir_hier(dirpath);
1475 gint imap_rename_folder(Folder *folder, FolderItem *item, const gchar *name)
1479 gchar *real_oldpath;
1480 gchar *real_newpath;
1483 gchar *old_cache_dir;
1484 gchar *new_cache_dir;
1485 IMAPSession *session;
1488 gint exists, recent, unseen;
1489 guint32 uid_validity;
1491 g_return_val_if_fail(folder != NULL, -1);
1492 g_return_val_if_fail(item != NULL, -1);
1493 g_return_val_if_fail(item->path != NULL, -1);
1494 g_return_val_if_fail(name != NULL, -1);
1496 session = imap_session_get(folder);
1497 if (!session) return -1;
1499 real_oldpath = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1501 g_free(session->mbox);
1502 session->mbox = NULL;
1503 ok = imap_cmd_examine(SESSION(session)->sock, "INBOX",
1504 &exists, &recent, &unseen, &uid_validity);
1505 if (ok != IMAP_SUCCESS) {
1506 g_free(real_oldpath);
1510 separator = imap_get_path_separator(IMAP_FOLDER(folder), item->path);
1511 if (strchr(item->path, G_DIR_SEPARATOR)) {
1512 dirpath = g_dirname(item->path);
1513 newpath = g_strconcat(dirpath, G_DIR_SEPARATOR_S, name, NULL);
1516 newpath = g_strdup(name);
1518 real_newpath = imap_locale_to_modified_utf7(newpath);
1519 imap_path_separator_subst(real_newpath, separator);
1521 ok = imap_cmd_rename(SESSION(session)->sock, real_oldpath, real_newpath);
1522 if (ok != IMAP_SUCCESS) {
1523 log_warning(_("can't rename mailbox: %s to %s\n"),
1524 real_oldpath, real_newpath);
1525 g_free(real_oldpath);
1527 g_free(real_newpath);
1532 item->name = g_strdup(name);
1534 old_cache_dir = folder_item_get_path(item);
1536 node = g_node_find(item->folder->node, G_PRE_ORDER, G_TRAVERSE_ALL,
1538 paths[0] = g_strdup(item->path);
1540 g_node_traverse(node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1541 imap_rename_folder_func, paths);
1543 if (is_dir_exist(old_cache_dir)) {
1544 new_cache_dir = folder_item_get_path(item);
1545 if (rename(old_cache_dir, new_cache_dir) < 0) {
1546 FILE_OP_ERROR(old_cache_dir, "rename");
1548 g_free(new_cache_dir);
1551 g_free(old_cache_dir);
1554 g_free(real_oldpath);
1555 g_free(real_newpath);
1560 gint imap_remove_folder(Folder *folder, FolderItem *item)
1563 IMAPSession *session;
1566 gint exists, recent, unseen;
1567 guint32 uid_validity;
1569 g_return_val_if_fail(folder != NULL, -1);
1570 g_return_val_if_fail(item != NULL, -1);
1571 g_return_val_if_fail(item->path != NULL, -1);
1573 session = imap_session_get(folder);
1574 if (!session) return -1;
1576 path = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1578 ok = imap_cmd_examine(SESSION(session)->sock, "INBOX",
1579 &exists, &recent, &unseen, &uid_validity);
1580 if (ok != IMAP_SUCCESS) {
1585 ok = imap_cmd_delete(SESSION(session)->sock, path);
1586 if (ok != IMAP_SUCCESS) {
1587 log_warning(_("can't delete mailbox\n"));
1593 cache_dir = folder_item_get_path(item);
1594 if (is_dir_exist(cache_dir) && remove_dir_recursive(cache_dir) < 0)
1595 g_warning("can't remove directory '%s'\n", cache_dir);
1597 folder_item_remove(item);
1602 static GSList *imap_get_uncached_messages(IMAPSession *session,
1604 guint32 first_uid, guint32 last_uid)
1607 GSList *newlist = NULL;
1608 GSList *llast = NULL;
1612 g_return_val_if_fail(session != NULL, NULL);
1613 g_return_val_if_fail(item != NULL, NULL);
1614 g_return_val_if_fail(item->folder != NULL, NULL);
1615 g_return_val_if_fail(FOLDER_CLASS(item->folder) == &imap_class, NULL);
1616 g_return_val_if_fail(first_uid <= last_uid, NULL);
1618 if (imap_cmd_envelope(SESSION(session)->sock, first_uid, last_uid)
1620 log_warning(_("can't get envelope\n"));
1624 str = g_string_new(NULL);
1627 if ((tmp = sock_getline(SESSION(session)->sock)) == NULL) {
1628 log_warning(_("error occurred while getting envelope.\n"));
1629 g_string_free(str, TRUE);
1633 if (tmp[0] != '*' || tmp[1] != ' ') {
1634 log_print("IMAP4< %s\n", tmp);
1638 if (strstr(tmp, "FETCH") == NULL) {
1639 log_print("IMAP4< %s\n", tmp);
1643 log_print("IMAP4< %s\n", tmp);
1644 g_string_assign(str, tmp);
1647 msginfo = imap_parse_envelope
1648 (SESSION(session)->sock, item, str);
1650 log_warning(_("can't parse envelope: %s\n"), str->str);
1653 if (item->stype == F_QUEUE) {
1654 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_QUEUED);
1655 } else if (item->stype == F_DRAFT) {
1656 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_DRAFT);
1659 msginfo->folder = item;
1662 llast = newlist = g_slist_append(newlist, msginfo);
1664 llast = g_slist_append(llast, msginfo);
1665 llast = llast->next;
1669 g_string_free(str, TRUE);
1674 static void imap_delete_all_cached_messages(FolderItem *item)
1678 g_return_if_fail(item != NULL);
1679 g_return_if_fail(item->folder != NULL);
1680 g_return_if_fail(FOLDER_CLASS(item->folder) == &imap_class);
1682 debug_print("Deleting all cached messages...\n");
1684 dir = folder_item_get_path(item);
1685 if (is_dir_exist(dir))
1686 remove_all_numbered_files(dir);
1689 debug_print("done.\n");
1693 static SockInfo *imap_open_tunnel(const gchar *server,
1694 const gchar *tunnelcmd,
1697 static SockInfo *imap_open_tunnel(const gchar *server,
1698 const gchar *tunnelcmd)
1703 if ((sock = sock_connect_cmd(server, tunnelcmd)) == NULL) {
1704 log_warning(_("Can't establish IMAP4 session with: %s\n"),
1709 return imap_init_sock(sock, ssl_type);
1711 return imap_init_sock(sock);
1717 static SockInfo *imap_open(const gchar *server, gushort port,
1720 static SockInfo *imap_open(const gchar *server, gushort port)
1725 if ((sock = sock_connect(server, port)) == NULL) {
1726 log_warning(_("Can't connect to IMAP4 server: %s:%d\n"),
1732 if (ssl_type == SSL_TUNNEL && !ssl_init_socket(sock)) {
1733 log_warning(_("Can't establish IMAP4 session with: %s:%d\n"),
1738 return imap_init_sock(sock, ssl_type);
1740 return imap_init_sock(sock);
1745 static SockInfo *imap_init_sock(SockInfo *sock, SSLType ssl_type)
1747 static SockInfo *imap_init_sock(SockInfo *sock)
1752 if (ssl_type == SSL_STARTTLS) {
1755 ok = imap_cmd_starttls(sock);
1756 if (ok != IMAP_SUCCESS) {
1757 log_warning(_("Can't start TLS session.\n"));
1761 if (!ssl_init_socket_with_method(sock, SSL_METHOD_TLSv1)) {
1771 static GList *imap_parse_namespace_str(gchar *str)
1776 IMAPNameSpace *namespace;
1777 GList *ns_list = NULL;
1779 while (*p != '\0') {
1780 /* parse ("#foo" "/") */
1782 while (*p && *p != '(') p++;
1783 if (*p == '\0') break;
1786 while (*p && *p != '"') p++;
1787 if (*p == '\0') break;
1791 while (*p && *p != '"') p++;
1792 if (*p == '\0') break;
1796 while (*p && isspace(*p)) p++;
1797 if (*p == '\0') break;
1798 if (strncmp(p, "NIL", 3) == 0)
1800 else if (*p == '"') {
1803 while (*p && *p != '"') p++;
1804 if (*p == '\0') break;
1809 while (*p && *p != ')') p++;
1810 if (*p == '\0') break;
1813 namespace = g_new(IMAPNameSpace, 1);
1814 namespace->name = g_strdup(name);
1815 namespace->separator = separator ? separator[0] : '\0';
1816 ns_list = g_list_append(ns_list, namespace);
1822 static void imap_parse_namespace(IMAPSession *session, IMAPFolder *folder)
1827 g_return_if_fail(session != NULL);
1828 g_return_if_fail(folder != NULL);
1830 if (folder->ns_personal != NULL ||
1831 folder->ns_others != NULL ||
1832 folder->ns_shared != NULL)
1835 if (!imap_has_capability(session, "NAMESPACE")) {
1836 imap_get_namespace_by_list(session, folder);
1840 if (imap_cmd_namespace(SESSION(session)->sock, &ns_str)
1842 log_warning(_("can't get namespace\n"));
1846 str_array = strsplit_parenthesis(ns_str, '(', ')', 3);
1847 if (str_array == NULL) {
1849 imap_get_namespace_by_list(session, folder);
1853 folder->ns_personal = imap_parse_namespace_str(str_array[0]);
1854 if (str_array[0] && str_array[1])
1855 folder->ns_others = imap_parse_namespace_str(str_array[1]);
1856 if (str_array[0] && str_array[1] && str_array[2])
1857 folder->ns_shared = imap_parse_namespace_str(str_array[2]);
1858 g_strfreev(str_array);
1862 static void imap_get_namespace_by_list(IMAPSession *session, IMAPFolder *folder)
1864 GSList *item_list, *cur;
1865 gchar separator = '\0';
1866 IMAPNameSpace *namespace;
1868 g_return_if_fail(session != NULL);
1869 g_return_if_fail(folder != NULL);
1871 if (folder->ns_personal != NULL ||
1872 folder->ns_others != NULL ||
1873 folder->ns_shared != NULL)
1876 imap_cmd_gen_send(SESSION(session)->sock, "LIST \"\" \"\"");
1877 item_list = imap_parse_list(NULL, session, "", &separator);
1878 for (cur = item_list; cur != NULL; cur = cur->next)
1879 folder_item_destroy(FOLDER_ITEM(cur->data));
1880 g_slist_free(item_list);
1882 namespace = g_new(IMAPNameSpace, 1);
1883 namespace->name = g_strdup("");
1884 namespace->separator = separator;
1885 folder->ns_personal = g_list_append(NULL, namespace);
1888 static IMAPNameSpace *imap_find_namespace_from_list(GList *ns_list,
1891 IMAPNameSpace *namespace = NULL;
1892 gchar *tmp_path, *name;
1894 if (!path) path = "";
1896 Xstrcat_a(tmp_path, path, "/", return NULL);
1898 for (; ns_list != NULL; ns_list = ns_list->next) {
1899 IMAPNameSpace *tmp_ns = ns_list->data;
1901 Xstrdup_a(name, tmp_ns->name, return namespace);
1902 if (tmp_ns->separator && tmp_ns->separator != '/')
1903 subst_char(name, tmp_ns->separator, '/');
1904 if (strncmp(tmp_path, name, strlen(name)) == 0)
1911 static IMAPNameSpace *imap_find_namespace(IMAPFolder *folder,
1914 IMAPNameSpace *namespace;
1916 g_return_val_if_fail(folder != NULL, NULL);
1918 namespace = imap_find_namespace_from_list(folder->ns_personal, path);
1919 if (namespace) return namespace;
1920 namespace = imap_find_namespace_from_list(folder->ns_others, path);
1921 if (namespace) return namespace;
1922 namespace = imap_find_namespace_from_list(folder->ns_shared, path);
1923 if (namespace) return namespace;
1928 static gchar imap_get_path_separator(IMAPFolder *folder, const gchar *path)
1930 IMAPNameSpace *namespace;
1931 gchar separator = '/';
1933 namespace = imap_find_namespace(folder, path);
1934 if (namespace && namespace->separator)
1935 separator = namespace->separator;
1940 static gchar *imap_get_real_path(IMAPFolder *folder, const gchar *path)
1945 g_return_val_if_fail(folder != NULL, NULL);
1946 g_return_val_if_fail(path != NULL, NULL);
1948 real_path = imap_locale_to_modified_utf7(path);
1949 separator = imap_get_path_separator(folder, path);
1950 imap_path_separator_subst(real_path, separator);
1955 static gchar *imap_parse_atom(SockInfo *sock, gchar *src,
1956 gchar *dest, gint dest_len, GString *str)
1958 gchar *cur_pos = src;
1961 g_return_val_if_fail(str != NULL, cur_pos);
1963 /* read the next line if the current response buffer is empty */
1964 while (isspace(*cur_pos)) cur_pos++;
1965 while (*cur_pos == '\0') {
1966 if ((nextline = sock_getline(sock)) == NULL)
1968 g_string_assign(str, nextline);
1970 strretchomp(nextline);
1971 /* log_print("IMAP4< %s\n", nextline); */
1972 debug_print("IMAP4< %s\n", nextline);
1975 while (isspace(*cur_pos)) cur_pos++;
1978 if (!strncmp(cur_pos, "NIL", 3)) {
1981 } else if (*cur_pos == '\"') {
1984 p = get_quoted(cur_pos, '\"', dest, dest_len);
1985 cur_pos = p ? p : cur_pos + 2;
1986 } else if (*cur_pos == '{') {
1991 cur_pos = strchr_cpy(cur_pos + 1, '}', buf, sizeof(buf));
1994 g_string_truncate(str, 0);
1998 if ((nextline = sock_getline(sock)) == NULL)
2000 line_len += strlen(nextline);
2001 g_string_append(str, nextline);
2003 strretchomp(nextline);
2004 /* log_print("IMAP4< %s\n", nextline); */
2005 debug_print("IMAP4< %s\n", nextline);
2007 } while (line_len < len);
2009 memcpy(dest, cur_pos, MIN(len, dest_len - 1));
2010 dest[MIN(len, dest_len - 1)] = '\0';
2017 static gchar *imap_get_header(SockInfo *sock, gchar *cur_pos, gchar **headers,
2027 g_return_val_if_fail(str != NULL, cur_pos);
2029 while (isspace(*cur_pos)) cur_pos++;
2031 g_return_val_if_fail(*cur_pos == '{', cur_pos);
2033 cur_pos = strchr_cpy(cur_pos + 1, '}', buf, sizeof(buf));
2036 g_string_truncate(str, 0);
2040 if ((nextline = sock_getline(sock)) == NULL)
2042 block_len += strlen(nextline);
2043 g_string_append(str, nextline);
2045 strretchomp(nextline);
2046 /* debug_print("IMAP4< %s\n", nextline); */
2048 } while (block_len < len);
2050 debug_print("IMAP4< [contents of BODY.PEEK[HEADER.FIELDS (...)]]\n");
2052 *headers = g_strndup(cur_pos, len);
2055 while (isspace(*cur_pos)) cur_pos++;
2056 while (*cur_pos == '\0') {
2057 if ((nextline = sock_getline(sock)) == NULL)
2059 g_string_assign(str, nextline);
2061 strretchomp(nextline);
2062 debug_print("IMAP4< %s\n", nextline);
2065 while (isspace(*cur_pos)) cur_pos++;
2071 static MsgFlags imap_parse_flags(const gchar *flag_str)
2073 const gchar *p = flag_str;
2074 MsgFlags flags = {0, 0};
2076 flags.perm_flags = MSG_UNREAD;
2078 while ((p = strchr(p, '\\')) != NULL) {
2081 if (g_strncasecmp(p, "Recent", 6) == 0 && MSG_IS_UNREAD(flags)) {
2082 MSG_SET_PERM_FLAGS(flags, MSG_NEW);
2083 } else if (g_strncasecmp(p, "Seen", 4) == 0) {
2084 MSG_UNSET_PERM_FLAGS(flags, MSG_NEW|MSG_UNREAD);
2085 } else if (g_strncasecmp(p, "Deleted", 7) == 0) {
2086 MSG_SET_PERM_FLAGS(flags, MSG_DELETED);
2087 } else if (g_strncasecmp(p, "Flagged", 7) == 0) {
2088 MSG_SET_PERM_FLAGS(flags, MSG_MARKED);
2089 } else if (g_strncasecmp(p, "Answered", 8) == 0) {
2090 MSG_SET_PERM_FLAGS(flags, MSG_REPLIED);
2097 static MsgInfo *imap_parse_envelope(SockInfo *sock, FolderItem *item,
2100 gchar buf[IMAPBUFSIZE];
2101 MsgInfo *msginfo = NULL;
2106 MsgFlags flags = {0, 0}, imap_flags = {0, 0};
2108 g_return_val_if_fail(line_str != NULL, NULL);
2109 g_return_val_if_fail(line_str->str[0] == '*' &&
2110 line_str->str[1] == ' ', NULL);
2112 MSG_SET_TMP_FLAGS(flags, MSG_IMAP);
2113 if (item->stype == F_QUEUE) {
2114 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
2115 } else if (item->stype == F_DRAFT) {
2116 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
2119 cur_pos = line_str->str + 2;
2121 #define PARSE_ONE_ELEMENT(ch) \
2123 cur_pos = strchr_cpy(cur_pos, ch, buf, sizeof(buf)); \
2124 if (cur_pos == NULL) { \
2125 g_warning("cur_pos == NULL\n"); \
2126 procmsg_msginfo_free(msginfo); \
2131 PARSE_ONE_ELEMENT(' ');
2134 PARSE_ONE_ELEMENT(' ');
2135 g_return_val_if_fail(!strcmp(buf, "FETCH"), NULL);
2137 g_return_val_if_fail(*cur_pos == '(', NULL);
2140 while (*cur_pos != '\0' && *cur_pos != ')') {
2141 while (*cur_pos == ' ') cur_pos++;
2143 if (!strncmp(cur_pos, "UID ", 4)) {
2145 uid = strtoul(cur_pos, &cur_pos, 10);
2146 } else if (!strncmp(cur_pos, "FLAGS ", 6)) {
2148 if (*cur_pos != '(') {
2149 g_warning("*cur_pos != '('\n");
2150 procmsg_msginfo_free(msginfo);
2154 PARSE_ONE_ELEMENT(')');
2155 imap_flags = imap_parse_flags(buf);
2156 } else if (!strncmp(cur_pos, "RFC822.SIZE ", 12)) {
2158 size = strtol(cur_pos, &cur_pos, 10);
2159 } else if (!strncmp(cur_pos, "BODY[HEADER.FIELDS ", 19)) {
2163 if (*cur_pos != '(') {
2164 g_warning("*cur_pos != '('\n");
2165 procmsg_msginfo_free(msginfo);
2169 PARSE_ONE_ELEMENT(')');
2170 if (*cur_pos != ']') {
2171 g_warning("*cur_pos != ']'\n");
2172 procmsg_msginfo_free(msginfo);
2177 cur_pos = imap_get_header(sock, cur_pos, &headers,
2179 msginfo = procheader_parse_str(headers, flags, FALSE, FALSE);
2182 g_warning("invalid FETCH response: %s\n", cur_pos);
2188 msginfo->msgnum = uid;
2189 msginfo->size = size;
2190 msginfo->flags.tmp_flags |= imap_flags.tmp_flags;
2191 msginfo->flags.perm_flags = imap_flags.perm_flags;
2197 gint imap_msg_set_perm_flags(MsgInfo *msginfo, MsgPermFlags flags)
2200 IMAPSession *session;
2201 IMAPFlags iflags = 0;
2202 gint ok = IMAP_SUCCESS;
2204 g_return_val_if_fail(msginfo != NULL, -1);
2205 g_return_val_if_fail(msginfo->folder != NULL, -1);
2206 g_return_val_if_fail(msginfo->folder->folder != NULL, -1);
2208 folder = msginfo->folder->folder;
2209 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
2211 session = imap_session_get(folder);
2212 if (!session) return -1;
2214 if ((ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
2215 NULL, NULL, NULL, NULL)) != IMAP_SUCCESS)
2218 if (flags & MSG_MARKED) iflags |= IMAP_FLAG_FLAGGED;
2219 if (flags & MSG_REPLIED) iflags |= IMAP_FLAG_ANSWERED;
2221 ok = imap_set_message_flags(session, msginfo->msgnum,
2222 msginfo->msgnum, iflags, TRUE);
2223 if (ok != IMAP_SUCCESS) return ok;
2226 if (flags & MSG_UNREAD)
2227 ok = imap_set_message_flags(session, msginfo->msgnum,
2228 msginfo->msgnum, IMAP_FLAG_SEEN,
2233 gint imap_msg_unset_perm_flags(MsgInfo *msginfo, MsgPermFlags flags)
2236 IMAPSession *session;
2237 IMAPFlags iflags = 0;
2238 gint ok = IMAP_SUCCESS;
2240 g_return_val_if_fail(msginfo != NULL, -1);
2241 g_return_val_if_fail(msginfo->folder != NULL, -1);
2242 g_return_val_if_fail(msginfo->folder->folder != NULL, -1);
2244 folder = msginfo->folder->folder;
2245 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
2247 session = imap_session_get(folder);
2248 if (!session) return -1;
2250 if (flags & MSG_MARKED) iflags |= IMAP_FLAG_FLAGGED;
2251 if (flags & MSG_REPLIED) iflags |= IMAP_FLAG_ANSWERED;
2253 ok = imap_set_message_flags(session, msginfo->msgnum,
2254 msginfo->msgnum, iflags, FALSE);
2255 if (ok != IMAP_SUCCESS) return ok;
2258 if (flags & MSG_UNREAD)
2259 ok = imap_set_message_flags(session, msginfo->msgnum,
2260 msginfo->msgnum, IMAP_FLAG_SEEN,
2265 static gint imap_set_message_flags(IMAPSession *session,
2274 buf = g_string_new(is_set ? "+FLAGS (" : "-FLAGS (");
2276 if (IMAP_IS_SEEN(flags)) g_string_append(buf, "\\Seen ");
2277 if (IMAP_IS_ANSWERED(flags)) g_string_append(buf, "\\Answered ");
2278 if (IMAP_IS_FLAGGED(flags)) g_string_append(buf, "\\Flagged ");
2279 if (IMAP_IS_DELETED(flags)) g_string_append(buf, "\\Deleted ");
2280 if (IMAP_IS_DRAFT(flags)) g_string_append(buf, "\\Draft");
2282 if (buf->str[buf->len - 1] == ' ')
2283 g_string_truncate(buf, buf->len - 1);
2285 g_string_append_c(buf, ')');
2287 ok = imap_cmd_store(SESSION(session)->sock, first_uid, last_uid,
2289 g_string_free(buf, TRUE);
2294 static gint imap_select(IMAPSession *session, IMAPFolder *folder,
2296 gint *exists, gint *recent, gint *unseen,
2297 guint32 *uid_validity)
2301 gint exists_, recent_, unseen_, uid_validity_;
2303 if (!exists || !recent || !unseen || !uid_validity) {
2304 if (session->mbox && strcmp(session->mbox, path) == 0)
2305 return IMAP_SUCCESS;
2309 uid_validity = &uid_validity_;
2312 g_free(session->mbox);
2313 session->mbox = NULL;
2315 real_path = imap_get_real_path(folder, path);
2316 ok = imap_cmd_select(SESSION(session)->sock, real_path,
2317 exists, recent, unseen, uid_validity);
2318 if (ok != IMAP_SUCCESS)
2319 log_warning(_("can't select folder: %s\n"), real_path);
2321 session->mbox = g_strdup(path);
2327 #define THROW(err) { ok = err; goto catch; }
2329 static gint imap_status(IMAPSession *session, IMAPFolder *folder,
2331 gint *messages, gint *recent,
2332 guint32 *uid_next, guint32 *uid_validity,
2341 *messages = *recent = *uid_next = *uid_validity = *unseen = 0;
2343 argbuf = g_ptr_array_new();
2345 real_path = imap_get_real_path(folder, path);
2346 QUOTE_IF_REQUIRED(real_path_, real_path);
2347 imap_cmd_gen_send(SESSION(session)->sock, "STATUS %s "
2348 "(MESSAGES RECENT UIDNEXT UIDVALIDITY UNSEEN)",
2351 ok = imap_cmd_ok(SESSION(session)->sock, argbuf);
2352 if (ok != IMAP_SUCCESS) THROW(ok);
2354 str = search_array_str(argbuf, "STATUS");
2355 if (!str) THROW(IMAP_ERROR);
2357 str = strchr(str, '(');
2358 if (!str) THROW(IMAP_ERROR);
2360 while (*str != '\0' && *str != ')') {
2361 while (*str == ' ') str++;
2363 if (!strncmp(str, "MESSAGES ", 9)) {
2365 *messages = strtol(str, &str, 10);
2366 } else if (!strncmp(str, "RECENT ", 7)) {
2368 *recent = strtol(str, &str, 10);
2369 } else if (!strncmp(str, "UIDNEXT ", 8)) {
2371 *uid_next = strtoul(str, &str, 10);
2372 } else if (!strncmp(str, "UIDVALIDITY ", 12)) {
2374 *uid_validity = strtoul(str, &str, 10);
2375 } else if (!strncmp(str, "UNSEEN ", 7)) {
2377 *unseen = strtol(str, &str, 10);
2379 g_warning("invalid STATUS response: %s\n", str);
2386 ptr_array_free_strings(argbuf);
2387 g_ptr_array_free(argbuf, TRUE);
2395 /* low-level IMAP4rev1 commands */
2397 static gint imap_cmd_login(SockInfo *sock,
2398 const gchar *user, const gchar *pass)
2400 gchar *user_, *pass_;
2403 QUOTE_IF_REQUIRED(user_, user);
2404 QUOTE_IF_REQUIRED(pass_, pass);
2405 imap_cmd_gen_send(sock, "LOGIN %s %s", user_, pass_);
2407 ok = imap_cmd_ok(sock, NULL);
2408 if (ok != IMAP_SUCCESS)
2409 log_warning(_("IMAP4 login failed.\n"));
2414 static gint imap_cmd_logout(SockInfo *sock)
2416 imap_cmd_gen_send(sock, "LOGOUT");
2417 return imap_cmd_ok(sock, NULL);
2420 /* Send a NOOP, and examine the server's response to see whether this
2421 * connection is pre-authenticated or not. */
2422 static gint imap_greeting(SockInfo *sock, gboolean *is_preauth)
2427 imap_cmd_gen_send(sock, "NOOP");
2428 argbuf = g_ptr_array_new(); /* will hold messages sent back */
2429 r = imap_cmd_ok(sock, argbuf);
2430 *is_preauth = search_array_str(argbuf, "PREAUTH") != NULL;
2432 ptr_array_free_strings(argbuf);
2433 g_ptr_array_free(argbuf, TRUE);
2438 static void imap_get_capability(Session *session)
2443 if (IMAP_SESSION(session)->capability != NULL)
2446 imap_cmd_gen_send(session->sock, "CAPABILITY");
2448 argbuf = g_ptr_array_new();
2450 if (imap_cmd_ok(session->sock, argbuf) != IMAP_SUCCESS ||
2451 ((capstr = search_array_str(argbuf, "CAPABILITY ")) == NULL)) {
2452 ptr_array_free_strings(argbuf);
2453 g_ptr_array_free(argbuf, TRUE);
2457 capstr += strlen("CAPABILITY ");
2459 IMAP_SESSION(session)->capability = g_strsplit(capstr, " ", 0);
2461 ptr_array_free_strings(argbuf);
2462 g_ptr_array_free(argbuf, TRUE);
2465 static gboolean imap_has_capability(IMAPSession *session, const gchar *cap)
2469 for (p = session->capability; *p != NULL; ++p)
2470 if (g_strcasecmp(*p, cap) == 0)
2476 static gint imap_cmd_noop(SockInfo *sock)
2478 imap_cmd_gen_send(sock, "NOOP");
2479 return imap_cmd_ok(sock, NULL);
2482 static gint imap_cmd_starttls(SockInfo *sock)
2484 imap_cmd_gen_send(sock, "STARTTLS");
2485 return imap_cmd_ok(sock, NULL);
2488 #define THROW(err) { ok = err; goto catch; }
2490 static gint imap_cmd_namespace(SockInfo *sock, gchar **ns_str)
2496 argbuf = g_ptr_array_new();
2498 imap_cmd_gen_send(sock, "NAMESPACE");
2499 if ((ok = imap_cmd_ok(sock, argbuf)) != IMAP_SUCCESS) THROW(ok);
2501 str = search_array_str(argbuf, "NAMESPACE");
2502 if (!str) THROW(IMAP_ERROR);
2504 *ns_str = g_strdup(str);
2507 ptr_array_free_strings(argbuf);
2508 g_ptr_array_free(argbuf, TRUE);
2515 static gint imap_cmd_list(SockInfo *sock, const gchar *ref,
2516 const gchar *mailbox, GPtrArray *argbuf)
2518 gchar *ref_, *mailbox_;
2520 if (!ref) ref = "\"\"";
2521 if (!mailbox) mailbox = "\"\"";
2523 QUOTE_IF_REQUIRED(ref_, ref);
2524 QUOTE_IF_REQUIRED(mailbox_, mailbox);
2525 imap_cmd_gen_send(sock, "LIST %s %s", ref_, mailbox_);
2527 return imap_cmd_ok(sock, argbuf);
2530 #define THROW goto catch
2532 static gint imap_cmd_do_select(SockInfo *sock, const gchar *folder,
2534 gint *exists, gint *recent, gint *unseen,
2535 guint32 *uid_validity)
2543 *exists = *recent = *unseen = *uid_validity = 0;
2544 argbuf = g_ptr_array_new();
2547 select_cmd = "EXAMINE";
2549 select_cmd = "SELECT";
2551 QUOTE_IF_REQUIRED(folder_, folder);
2552 imap_cmd_gen_send(sock, "%s %s", select_cmd, folder_);
2554 if ((ok = imap_cmd_ok(sock, argbuf)) != IMAP_SUCCESS) THROW;
2556 resp_str = search_array_contain_str(argbuf, "EXISTS");
2558 if (sscanf(resp_str,"%d EXISTS", exists) != 1) {
2559 g_warning("imap_cmd_select(): invalid EXISTS line.\n");
2564 resp_str = search_array_contain_str(argbuf, "RECENT");
2566 if (sscanf(resp_str, "%d RECENT", recent) != 1) {
2567 g_warning("imap_cmd_select(): invalid RECENT line.\n");
2572 resp_str = search_array_contain_str(argbuf, "UIDVALIDITY");
2574 if (sscanf(resp_str, "OK [UIDVALIDITY %u] ", uid_validity)
2576 g_warning("imap_cmd_select(): invalid UIDVALIDITY line.\n");
2581 resp_str = search_array_contain_str(argbuf, "UNSEEN");
2583 if (sscanf(resp_str, "OK [UNSEEN %d] ", unseen) != 1) {
2584 g_warning("imap_cmd_select(): invalid UNSEEN line.\n");
2590 ptr_array_free_strings(argbuf);
2591 g_ptr_array_free(argbuf, TRUE);
2596 static gint imap_cmd_select(SockInfo *sock, const gchar *folder,
2597 gint *exists, gint *recent, gint *unseen,
2598 guint32 *uid_validity)
2600 return imap_cmd_do_select(sock, folder, FALSE,
2601 exists, recent, unseen, uid_validity);
2604 static gint imap_cmd_examine(SockInfo *sock, const gchar *folder,
2605 gint *exists, gint *recent, gint *unseen,
2606 guint32 *uid_validity)
2608 return imap_cmd_do_select(sock, folder, TRUE,
2609 exists, recent, unseen, uid_validity);
2614 static gint imap_cmd_create(SockInfo *sock, const gchar *folder)
2618 QUOTE_IF_REQUIRED(folder_, folder);
2619 imap_cmd_gen_send(sock, "CREATE %s", folder_);
2621 return imap_cmd_ok(sock, NULL);
2624 static gint imap_cmd_rename(SockInfo *sock, const gchar *old_folder,
2625 const gchar *new_folder)
2627 gchar *old_folder_, *new_folder_;
2629 QUOTE_IF_REQUIRED(old_folder_, old_folder);
2630 QUOTE_IF_REQUIRED(new_folder_, new_folder);
2631 imap_cmd_gen_send(sock, "RENAME %s %s", old_folder_, new_folder_);
2633 return imap_cmd_ok(sock, NULL);
2636 static gint imap_cmd_delete(SockInfo *sock, const gchar *folder)
2640 QUOTE_IF_REQUIRED(folder_, folder);
2641 imap_cmd_gen_send(sock, "DELETE %s", folder_);
2643 return imap_cmd_ok(sock, NULL);
2646 static gint imap_cmd_search(SockInfo *sock, const gchar *criteria, GSList **list)
2652 g_return_val_if_fail(criteria != NULL, IMAP_ERROR);
2653 g_return_val_if_fail(list != NULL, IMAP_ERROR);
2657 argbuf = g_ptr_array_new();
2658 imap_cmd_gen_send(sock, "UID SEARCH %s", criteria);
2660 ok = imap_cmd_ok(sock, argbuf);
2661 if (ok != IMAP_SUCCESS) {
2662 ptr_array_free_strings(argbuf);
2663 g_ptr_array_free(argbuf, TRUE);
2667 if ((uidlist = search_array_str(argbuf, "SEARCH ")) != NULL) {
2668 gchar **strlist, **p;
2670 strlist = g_strsplit(uidlist + 7, " ", 0);
2671 for (p = strlist; *p != NULL; ++p) {
2674 if (sscanf(*p, "%d", &msgnum) == 1)
2675 *list = g_slist_append(*list, GINT_TO_POINTER(msgnum));
2677 g_strfreev(strlist);
2679 ptr_array_free_strings(argbuf);
2680 g_ptr_array_free(argbuf, TRUE);
2682 return IMAP_SUCCESS;
2685 static gint imap_cmd_fetch(SockInfo *sock, guint32 uid, const gchar *filename)
2693 g_return_val_if_fail(filename != NULL, IMAP_ERROR);
2695 imap_cmd_gen_send(sock, "UID FETCH %d BODY[]", uid);
2697 while ((ok = imap_cmd_gen_recv(sock, &buf))
2699 if (buf[0] != '*' || buf[1] != ' ') {
2703 if (strstr(buf, "FETCH") != NULL)
2706 if (ok != IMAP_SUCCESS)
2709 cur_pos = strchr(buf, '{');
2710 if (cur_pos == NULL) {
2714 cur_pos = strchr_cpy(cur_pos + 1, '}', size_str, sizeof(size_str));
2715 if (cur_pos == NULL) {
2719 size_num = atol(size_str);
2721 if (*cur_pos != '\0') {
2726 if (recv_bytes_write_to_file(sock, size_num, filename) != 0) {
2731 if (imap_cmd_gen_recv(sock, &buf) != IMAP_SUCCESS) {
2736 if (buf[0] == '\0' || buf[strlen(buf) - 1] != ')') {
2742 ok = imap_cmd_ok(sock, NULL);
2747 static gint imap_cmd_append(IMAPSession *session, const gchar *destfolder,
2748 const gchar *file, gint32 *new_uid)
2753 gchar buf[BUFFSIZE], *imapbuf;
2758 g_return_val_if_fail(file != NULL, IMAP_ERROR);
2760 size = get_file_size_as_crlf(file);
2761 if ((fp = fopen(file, "rb")) == NULL) {
2762 FILE_OP_ERROR(file, "fopen");
2765 QUOTE_IF_REQUIRED(destfolder_, destfolder);
2766 imap_cmd_gen_send(SESSION(session)->sock, "APPEND %s (\\Seen) {%d}", destfolder_, size);
2768 ok = imap_cmd_gen_recv(SESSION(session)->sock, &imapbuf);
2769 if (ok != IMAP_SUCCESS || imapbuf[0] != '+' || imapbuf[1] != ' ') {
2770 log_warning(_("can't append %s to %s\n"), file, destfolder_);
2777 log_print("IMAP4> %s\n", _("(sending file...)"));
2779 while (fgets(buf, sizeof(buf), fp) != NULL) {
2781 if (sock_puts(SESSION(session)->sock, buf) < 0) {
2783 sock_close(SESSION(session)->sock);
2789 FILE_OP_ERROR(file, "fgets");
2791 sock_close(SESSION(session)->sock);
2795 sock_puts(SESSION(session)->sock, "");
2799 reply = g_ptr_array_new();
2802 ok = imap_cmd_ok(SESSION(session)->sock, reply);
2803 if (ok != IMAP_SUCCESS)
2804 log_warning(_("can't append message to %s\n"), destfolder_);
2806 (new_uid != NULL) &&
2807 (imap_has_capability(session, "UIDPLUS") && reply->len > 0) &&
2808 ((okmsginfo = g_ptr_array_index(reply, reply->len - 1)) != NULL) &&
2809 (sscanf(okmsginfo, "%*u OK [APPENDUID %*u %u]", &newuid) == 1)) {
2813 ptr_array_free_strings(reply);
2814 g_ptr_array_free(reply, TRUE);
2819 static gint imap_cmd_copy(IMAPSession * session,
2821 const gchar * destfolder, gint32 * new_uid)
2824 gint32 olduid, newuid;
2829 g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
2830 g_return_val_if_fail(session != NULL, IMAP_ERROR);
2831 g_return_val_if_fail(new_uid != NULL, IMAP_ERROR);
2833 QUOTE_IF_REQUIRED(destfolder_, destfolder);
2834 imap_cmd_gen_send(SESSION(session)->sock, "UID COPY %d %s", msgnum, destfolder_);
2836 reply = g_ptr_array_new();
2839 ok = imap_cmd_ok(SESSION(session)->sock, reply);
2840 if (ok != IMAP_SUCCESS)
2841 log_warning(_("can't copy %d to %s\n"), msgnum, destfolder_);
2842 else if (imap_has_capability(session, "UIDPLUS") && reply->len > 0)
2843 if ((okmsginfo = g_ptr_array_index(reply, reply->len - 1)) != NULL &&
2844 sscanf(okmsginfo, "%*u OK [COPYUID %*u %u %u]", &olduid, &newuid) == 2 &&
2848 ptr_array_free_strings(reply);
2849 g_ptr_array_free(reply, TRUE);
2853 gint imap_cmd_envelope(SockInfo *sock, guint32 first_uid, guint32 last_uid)
2855 static GString *header_fields = NULL;
2857 if (header_fields == NULL) {
2858 const HeaderEntry *headers, *elem;
2860 headers = procheader_get_headernames(FALSE);
2861 header_fields = g_string_new("");
2863 for (elem = headers; elem->name != NULL; ++elem) {
2864 gint namelen = strlen(elem->name);
2866 /* Header fields ending with space are not rfc822 headers */
2867 if (elem->name[namelen - 1] == ' ')
2870 /* strip : at the of header field */
2871 if(elem->name[namelen - 1] == ':')
2877 g_string_sprintfa(header_fields, "%s%.*s",
2878 header_fields->str[0] != '\0' ? " " : "",
2879 namelen, elem->name);
2884 (sock, "UID FETCH %d:%d (UID FLAGS RFC822.SIZE BODY.PEEK[HEADER.FIELDS (%s)])",
2885 first_uid, last_uid,
2886 header_fields->str);
2888 return IMAP_SUCCESS;
2891 static gint imap_cmd_store(SockInfo *sock, guint32 first_uid, guint32 last_uid,
2896 imap_cmd_gen_send(sock, "UID STORE %d:%d %s",
2897 first_uid, last_uid, sub_cmd);
2899 if ((ok = imap_cmd_ok(sock, NULL)) != IMAP_SUCCESS) {
2900 log_warning(_("error while imap command: STORE %d:%d %s\n"),
2901 first_uid, last_uid, sub_cmd);
2905 return IMAP_SUCCESS;
2908 static gint imap_cmd_expunge(SockInfo *sock)
2912 imap_cmd_gen_send(sock, "EXPUNGE");
2913 if ((ok = imap_cmd_ok(sock, NULL)) != IMAP_SUCCESS) {
2914 log_warning(_("error while imap command: EXPUNGE\n"));
2918 return IMAP_SUCCESS;
2921 static gint imap_cmd_ok(SockInfo *sock, GPtrArray *argbuf)
2926 gchar cmd_status[IMAPBUFSIZE];
2928 while ((ok = imap_cmd_gen_recv(sock, &buf))
2930 if (buf[0] == '*' && buf[1] == ' ') {
2932 g_ptr_array_add(argbuf, g_strdup(buf + 2));
2936 if (sscanf(buf, "%d %s", &cmd_num, cmd_status) < 2)
2938 else if (cmd_num == imap_cmd_count &&
2939 !strcmp(cmd_status, "OK")) {
2941 g_ptr_array_add(argbuf, g_strdup(buf));
2943 return IMAP_SUCCESS;
2954 static void imap_cmd_gen_send(SockInfo *sock, const gchar *format, ...)
2956 gchar buf[IMAPBUFSIZE];
2957 gchar tmp[IMAPBUFSIZE];
2961 va_start(args, format);
2962 g_vsnprintf(tmp, sizeof(tmp), format, args);
2967 g_snprintf(buf, sizeof(buf), "%d %s\r\n", imap_cmd_count, tmp);
2968 if (!strncasecmp(tmp, "LOGIN ", 6) && (p = strchr(tmp + 6, ' '))) {
2970 log_print("IMAP4> %d %s ********\n", imap_cmd_count, tmp);
2972 log_print("IMAP4> %d %s\n", imap_cmd_count, tmp);
2974 sock_write(sock, buf, strlen(buf));
2977 static gint imap_cmd_gen_recv(SockInfo *sock, gchar **buf)
2979 if ((*buf = sock_getline(sock)) == NULL)
2984 log_print("IMAP4< %s\n", *buf);
2986 return IMAP_SUCCESS;
2990 /* misc utility functions */
2992 static gchar *strchr_cpy(const gchar *src, gchar ch, gchar *dest, gint len)
2997 tmp = strchr(src, ch);
3001 memcpy(dest, src, MIN(tmp - src, len - 1));
3002 dest[MIN(tmp - src, len - 1)] = '\0';
3007 static gchar *get_quoted(const gchar *src, gchar ch, gchar *dest, gint len)
3009 const gchar *p = src;
3012 g_return_val_if_fail(*p == ch, NULL);
3017 while (*p != '\0' && *p != ch) {
3019 if (*p == '\\' && *(p + 1) != '\0')
3028 return (gchar *)(*p == ch ? p + 1 : p);
3031 static gchar *search_array_contain_str(GPtrArray *array, const gchar *str)
3035 for (i = 0; i < array->len; i++) {
3038 tmp = g_ptr_array_index(array, i);
3039 if (strstr(tmp, str) != NULL)
3046 static gchar *search_array_str(GPtrArray *array, const gchar *str)
3053 for (i = 0; i < array->len; i++) {
3056 tmp = g_ptr_array_index(array, i);
3057 if (!strncmp(tmp, str, len))
3064 static void imap_path_separator_subst(gchar *str, gchar separator)
3067 gboolean in_escape = FALSE;
3069 if (!separator || separator == '/') return;
3071 for (p = str; *p != '\0'; p++) {
3072 if (*p == '/' && !in_escape)
3074 else if (*p == '&' && *(p + 1) != '-' && !in_escape)
3076 else if (*p == '-' && in_escape)
3081 static gchar *imap_modified_utf7_to_locale(const gchar *mutf7_str)
3084 return g_strdup(mutf7_str);
3086 static iconv_t cd = (iconv_t)-1;
3087 static gboolean iconv_ok = TRUE;
3090 size_t norm_utf7_len;
3092 gchar *to_str, *to_p;
3094 gboolean in_escape = FALSE;
3096 if (!iconv_ok) return g_strdup(mutf7_str);
3098 if (cd == (iconv_t)-1) {
3099 cd = iconv_open(conv_get_current_charset_str(), "UTF-7");
3100 if (cd == (iconv_t)-1) {
3101 g_warning("iconv cannot convert UTF-7 to %s\n",
3102 conv_get_current_charset_str());
3104 return g_strdup(mutf7_str);
3108 norm_utf7 = g_string_new(NULL);
3110 for (p = mutf7_str; *p != '\0'; p++) {
3111 /* replace: '&' -> '+',
3113 escaped ',' -> '/' */
3114 if (!in_escape && *p == '&') {
3115 if (*(p + 1) != '-') {
3116 g_string_append_c(norm_utf7, '+');
3119 g_string_append_c(norm_utf7, '&');
3122 } else if (in_escape && *p == ',') {
3123 g_string_append_c(norm_utf7, '/');
3124 } else if (in_escape && *p == '-') {
3125 g_string_append_c(norm_utf7, '-');
3128 g_string_append_c(norm_utf7, *p);
3132 norm_utf7_p = norm_utf7->str;
3133 norm_utf7_len = norm_utf7->len;
3134 to_len = strlen(mutf7_str) * 5;
3135 to_p = to_str = g_malloc(to_len + 1);
3137 if (iconv(cd, (ICONV_CONST gchar **)&norm_utf7_p, &norm_utf7_len,
3138 &to_p, &to_len) == -1) {
3139 g_warning(_("iconv cannot convert UTF-7 to %s\n"),
3140 conv_get_current_charset_str());
3141 g_string_free(norm_utf7, TRUE);
3143 return g_strdup(mutf7_str);
3146 /* second iconv() call for flushing */
3147 iconv(cd, NULL, NULL, &to_p, &to_len);
3148 g_string_free(norm_utf7, TRUE);
3152 #endif /* !HAVE_ICONV */
3155 static gchar *imap_locale_to_modified_utf7(const gchar *from)
3158 return g_strdup(from);
3160 static iconv_t cd = (iconv_t)-1;
3161 static gboolean iconv_ok = TRUE;
3162 gchar *norm_utf7, *norm_utf7_p;
3163 size_t from_len, norm_utf7_len;
3165 gchar *from_tmp, *to, *p;
3166 gboolean in_escape = FALSE;
3168 if (!iconv_ok) return g_strdup(from);
3170 if (cd == (iconv_t)-1) {
3171 cd = iconv_open("UTF-7", conv_get_current_charset_str());
3172 if (cd == (iconv_t)-1) {
3173 g_warning("iconv cannot convert %s to UTF-7\n",
3174 conv_get_current_charset_str());
3176 return g_strdup(from);
3180 Xstrdup_a(from_tmp, from, return g_strdup(from));
3181 from_len = strlen(from);
3182 norm_utf7_len = from_len * 5;
3183 Xalloca(norm_utf7, norm_utf7_len + 1, return g_strdup(from));
3184 norm_utf7_p = norm_utf7;
3186 #define IS_PRINT(ch) (isprint(ch) && isascii(ch))
3188 while (from_len > 0) {
3189 if (IS_PRINT(*from_tmp)) {
3190 /* printable ascii char */
3191 *norm_utf7_p = *from_tmp;
3198 /* unprintable char: convert to UTF-7 */
3200 !IS_PRINT(from_tmp[mblen]) && mblen < from_len;
3204 if (iconv(cd, (ICONV_CONST gchar **)&from_tmp, &mblen,
3205 &norm_utf7_p, &norm_utf7_len) == -1) {
3206 g_warning("iconv cannot convert %s to UTF-7\n",
3207 conv_get_current_charset_str());
3208 return g_strdup(from);
3211 /* second iconv() call for flushing */
3212 iconv(cd, NULL, NULL, &norm_utf7_p, &norm_utf7_len);
3218 *norm_utf7_p = '\0';
3219 to_str = g_string_new(NULL);
3220 for (p = norm_utf7; p < norm_utf7_p; p++) {
3221 /* replace: '&' -> "&-",
3223 escaped '/' -> ',' */
3224 if (!in_escape && *p == '&') {
3225 g_string_append(to_str, "&-");
3226 } else if (!in_escape && *p == '+') {
3227 g_string_append_c(to_str, '&');
3229 } else if (in_escape && *p == '/') {
3230 g_string_append_c(to_str, ',');
3231 } else if (in_escape && *p == '-') {
3233 g_string_append_c(to_str, '-');
3235 g_string_append_c(to_str, *p);
3241 g_string_append_c(to_str, '-');
3245 g_string_free(to_str, FALSE);
3248 #endif /* !HAVE_ICONV */
3251 static gboolean imap_rename_folder_func(GNode *node, gpointer data)
3253 FolderItem *item = node->data;
3254 gchar **paths = data;
3255 const gchar *oldpath = paths[0];
3256 const gchar *newpath = paths[1];
3258 gchar *new_itempath;
3261 oldpathlen = strlen(oldpath);
3262 if (strncmp(oldpath, item->path, oldpathlen) != 0) {
3263 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
3267 base = item->path + oldpathlen;
3268 while (*base == G_DIR_SEPARATOR) base++;
3270 new_itempath = g_strdup(newpath);
3272 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
3275 item->path = new_itempath;
3280 static gint get_list_of_uids(Folder *folder, IMAPFolderItem *item, GSList **msgnum_list)
3282 gint ok, nummsgs = 0, lastuid_old;
3283 IMAPSession *session;
3284 GSList *uidlist, *elem;
3287 session = imap_session_get(folder);
3288 g_return_val_if_fail(session != NULL, -1);
3290 ok = imap_select(session, IMAP_FOLDER(folder), item->item.path,
3291 NULL, NULL, NULL, NULL);
3292 if (ok != IMAP_SUCCESS)
3295 cmd_buf = g_strdup_printf("UID %d:*", item->lastuid + 1);
3296 ok = imap_cmd_search(SESSION(session)->sock, cmd_buf, &uidlist);
3299 if (ok == IMAP_SOCKET) {
3300 session_destroy((Session *)session);
3301 ((RemoteFolder *)folder)->session = NULL;
3305 if (ok != IMAP_SUCCESS) {
3309 argbuf = g_ptr_array_new();
3311 cmd_buf = g_strdup_printf("UID FETCH %d:* (UID)", item->lastuid + 1);
3312 imap_cmd_gen_send(SESSION(session)->sock, cmd_buf);
3314 ok = imap_cmd_ok(SESSION(session)->sock, argbuf);
3315 if (ok != IMAP_SUCCESS) {
3316 ptr_array_free_strings(argbuf);
3317 g_ptr_array_free(argbuf, TRUE);
3321 for(i = 0; i < argbuf->len; i++) {
3324 if((ret = sscanf(g_ptr_array_index(argbuf, i),
3325 "%*d FETCH (UID %d)", &msgnum)) == 1)
3326 uidlist = g_slist_prepend(uidlist, GINT_TO_POINTER(msgnum));
3328 ptr_array_free_strings(argbuf);
3329 g_ptr_array_free(argbuf, TRUE);
3332 lastuid_old = item->lastuid;
3333 *msgnum_list = g_slist_copy(item->uid_list);
3334 nummsgs = g_slist_length(*msgnum_list);
3335 debug_print("Got %d uids from cache\n", g_slist_length(item->uid_list));
3337 for (elem = uidlist; elem != NULL; elem = g_slist_next(elem)) {
3340 msgnum = GPOINTER_TO_INT(elem->data);
3341 if (msgnum > lastuid_old) {
3342 *msgnum_list = g_slist_prepend(*msgnum_list, GINT_TO_POINTER(msgnum));
3343 item->uid_list = g_slist_prepend(item->uid_list, GINT_TO_POINTER(msgnum));
3346 if(msgnum > item->lastuid)
3347 item->lastuid = msgnum;
3350 g_slist_free(uidlist);
3355 gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list)
3357 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3358 IMAPSession *session;
3359 gint ok, nummsgs = 0, exists, recent, unseen, uid_val, uid_next;
3363 g_return_val_if_fail(folder != NULL, -1);
3364 g_return_val_if_fail(item != NULL, -1);
3365 g_return_val_if_fail(item->item.path != NULL, -1);
3366 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3367 g_return_val_if_fail(folder->account != NULL, -1);
3369 session = imap_session_get(folder);
3370 g_return_val_if_fail(session != NULL, -1);
3372 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
3373 &exists, &recent, &uid_next, &uid_val, &unseen);
3374 if (ok != IMAP_SUCCESS)
3377 /* If old uid_next matches new uid_next we can be sure no message
3378 was added to the folder */
3379 if (uid_next == item->uid_next) {
3380 nummsgs = g_slist_length(item->uid_list);
3382 /* If number of messages is still the same we
3383 know our caches message numbers are still valid,
3384 otherwise if the number of messages has decrease
3385 we discard our cache to start a new scan to find
3386 out which numbers have been removed */
3387 if (exists == nummsgs) {
3388 *msgnum_list = g_slist_copy(item->uid_list);
3390 } else if (exists < nummsgs) {
3391 debug_print("Freeing imap uid cache");
3393 g_slist_free(item->uid_list);
3394 item->uid_list = NULL;
3397 item->uid_next = uid_next;
3400 *msgnum_list = NULL;
3404 nummsgs = get_list_of_uids(folder, item, &uidlist);
3406 if (nummsgs != exists) {
3407 /* Cache contains more messages then folder, we have cached
3408 an old UID of a message that was removed and new messages
3409 have been added too, otherwise the uid_next check would
3411 debug_print("Freeing imap uid cache");
3413 g_slist_free(item->uid_list);
3414 item->uid_list = NULL;
3416 g_slist_free(*msgnum_list);
3418 nummsgs = get_list_of_uids(folder, item, &uidlist);
3421 *msgnum_list = uidlist;
3423 dir = folder_item_get_path((FolderItem *)item);
3424 debug_print("removing old messages from %s\n", dir);
3425 remove_numbered_files_not_in_list(dir, *msgnum_list);
3431 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item)
3436 flags.perm_flags = MSG_NEW|MSG_UNREAD;
3437 flags.tmp_flags = 0;
3439 g_return_val_if_fail(item != NULL, NULL);
3440 g_return_val_if_fail(file != NULL, NULL);
3442 if (item->stype == F_QUEUE) {
3443 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
3444 } else if (item->stype == F_DRAFT) {
3445 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
3448 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3449 if (!msginfo) return NULL;
3451 msginfo->folder = item;
3456 static int compare_uint(gconstpointer a, gconstpointer b)
3461 GSList *imap_get_msginfos(Folder *folder, FolderItem *item, GSList *msgnum_list)
3463 IMAPSession *session;
3464 GSList *sorted_list, *elem, *ret = NULL;
3465 gint ok, startnum, lastnum;
3467 g_return_val_if_fail(folder != NULL, NULL);
3468 g_return_val_if_fail(item != NULL, NULL);
3469 g_return_val_if_fail(msgnum_list != NULL, NULL);
3471 session = imap_session_get(folder);
3472 g_return_val_if_fail(session != NULL, NULL);
3474 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3475 NULL, NULL, NULL, NULL);
3476 if (ok != IMAP_SUCCESS)
3479 sorted_list = g_slist_sort(g_slist_copy(msgnum_list), compare_uint);
3481 startnum = lastnum = GPOINTER_TO_INT(sorted_list->data);
3483 for (elem = sorted_list;; elem = g_slist_next(elem)) {
3487 num = GPOINTER_TO_INT(elem->data);
3489 if (num > lastnum + 1 || elem == NULL) {
3490 if (!(item->stype == F_QUEUE || item->stype == F_DRAFT)) {
3491 ret = g_slist_concat(ret,
3492 imap_get_uncached_messages(
3493 session, item, startnum, lastnum));
3496 for (i = startnum; i <= lastnum; ++i) {
3499 file = imap_fetch_msg(folder, item, i);
3501 MsgInfo *msginfo = imap_parse_msg(file, item);
3502 if (msginfo != NULL) {
3503 msginfo->msgnum = i;
3504 ret = g_slist_append(ret, msginfo);
3519 g_slist_free(sorted_list);
3524 MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid)
3526 IMAPSession *session;
3527 MsgInfo *msginfo = NULL;
3530 g_return_val_if_fail(folder != NULL, NULL);
3531 g_return_val_if_fail(item != NULL, NULL);
3533 session = imap_session_get(folder);
3534 g_return_val_if_fail(session != NULL, NULL);
3536 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3537 NULL, NULL, NULL, NULL);
3538 if (ok != IMAP_SUCCESS)
3541 if (!(item->stype == F_QUEUE || item->stype == F_DRAFT)) {
3544 list = imap_get_uncached_messages(session, item, uid, uid);
3546 msginfo = (MsgInfo *)list->data;
3549 procmsg_msg_list_free(list);
3553 file = imap_fetch_msg(folder, item, uid);
3555 msginfo = imap_parse_msg(file, item);
3556 if (msginfo != NULL)
3557 msginfo->msgnum = uid;
3565 gboolean imap_check_msgnum_validity(Folder *folder, FolderItem *_item)
3567 IMAPSession *session;
3568 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3569 gint ok, exists = 0, recent = 0, unseen = 0;
3570 guint32 uid_next, uid_validity = 0;
3572 g_return_val_if_fail(folder != NULL, FALSE);
3573 g_return_val_if_fail(item != NULL, FALSE);
3574 g_return_val_if_fail(item->item.folder != NULL, FALSE);
3575 g_return_val_if_fail(FOLDER_CLASS(item->item.folder) == &imap_class, FALSE);
3577 session = imap_session_get(folder);
3578 g_return_val_if_fail(session != NULL, FALSE);
3580 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
3581 &exists, &recent, &uid_next, &uid_validity, &unseen);
3582 if (ok != IMAP_SUCCESS)
3585 if(item->item.mtime == uid_validity)
3588 debug_print("Freeing imap uid cache");
3590 g_slist_free(item->uid_list);
3591 item->uid_list = NULL;
3593 item->item.mtime = uid_validity;
3595 imap_delete_all_cached_messages((FolderItem *)item);
3600 void imap_change_flags(Folder *folder, FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
3602 IMAPSession *session;
3603 IMAPFlags flags_set = 0, flags_unset = 0;
3604 gint ok = IMAP_SUCCESS;
3606 g_return_if_fail(folder != NULL);
3607 g_return_if_fail(folder->class == &imap_class);
3608 g_return_if_fail(item != NULL);
3609 g_return_if_fail(item->folder == folder);
3610 g_return_if_fail(msginfo != NULL);
3611 g_return_if_fail(msginfo->folder == item);
3613 session = imap_session_get(folder);
3614 if (!session) return;
3616 if ((ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
3617 NULL, NULL, NULL, NULL)) != IMAP_SUCCESS)
3620 if (!MSG_IS_MARKED(msginfo->flags) && (newflags & MSG_MARKED))
3621 flags_set |= IMAP_FLAG_FLAGGED;
3622 if ( MSG_IS_MARKED(msginfo->flags) && !(newflags & MSG_MARKED))
3623 flags_unset |= IMAP_FLAG_FLAGGED;
3625 if (!MSG_IS_UNREAD(msginfo->flags) && (newflags & MSG_UNREAD))
3626 flags_unset |= IMAP_FLAG_SEEN;
3627 if ( MSG_IS_UNREAD(msginfo->flags) && !(newflags & MSG_UNREAD))
3628 flags_set |= IMAP_FLAG_SEEN;
3630 if (!MSG_IS_REPLIED(msginfo->flags) && (newflags & MSG_REPLIED))
3631 flags_set |= IMAP_FLAG_ANSWERED;
3632 if ( MSG_IS_REPLIED(msginfo->flags) && !(newflags & MSG_REPLIED))
3633 flags_set |= IMAP_FLAG_ANSWERED;
3636 ok = imap_set_message_flags(session, msginfo->msgnum,
3637 msginfo->msgnum, flags_set, TRUE);
3638 if (ok != IMAP_SUCCESS) return;
3642 ok = imap_set_message_flags(session, msginfo->msgnum,
3643 msginfo->msgnum, flags_unset, FALSE);
3644 if (ok != IMAP_SUCCESS) return;
3647 msginfo->flags.perm_flags = newflags;