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.
49 #include "procheader.h"
50 #include "prefs_account.h"
55 #include "inputdialog.h"
58 typedef struct _IMAPFolder IMAPFolder;
59 typedef struct _IMAPSession IMAPSession;
60 typedef struct _IMAPNameSpace IMAPNameSpace;
61 typedef struct _IMAPFolderItem IMAPFolderItem;
63 #include "prefs_account.h"
65 #define IMAP_FOLDER(obj) ((IMAPFolder *)obj)
66 #define IMAP_SESSION(obj) ((IMAPSession *)obj)
72 /* list of IMAPNameSpace */
84 time_t last_access_time;
85 gboolean authenticated;
87 gboolean folder_content_changed;
97 #define IMAP_SUCCESS 0
99 #define IMAP_AUTHFAIL 3
100 #define IMAP_PROTOCOL 4
101 #define IMAP_SYNTAX 5
105 #define IMAPBUFSIZE 8192
106 #define IMAPCMDLIMIT 1000
110 IMAP_FLAG_SEEN = 1 << 0,
111 IMAP_FLAG_ANSWERED = 1 << 1,
112 IMAP_FLAG_FLAGGED = 1 << 2,
113 IMAP_FLAG_DELETED = 1 << 3,
114 IMAP_FLAG_DRAFT = 1 << 4
117 #define IMAP_IS_SEEN(flags) ((flags & IMAP_FLAG_SEEN) != 0)
118 #define IMAP_IS_ANSWERED(flags) ((flags & IMAP_FLAG_ANSWERED) != 0)
119 #define IMAP_IS_FLAGGED(flags) ((flags & IMAP_FLAG_FLAGGED) != 0)
120 #define IMAP_IS_DELETED(flags) ((flags & IMAP_FLAG_DELETED) != 0)
121 #define IMAP_IS_DRAFT(flags) ((flags & IMAP_FLAG_DRAFT) != 0)
124 #define IMAP4_PORT 143
126 #define IMAPS_PORT 993
129 #define QUOTE_IF_REQUIRED(out, str) \
131 if (*str != '"' && strpbrk(str, " \t(){}%*") != NULL) { \
135 len = strlen(str) + 3; \
136 Xalloca(__tmp, len, return IMAP_ERROR); \
137 g_snprintf(__tmp, len, "\"%s\"", str); \
140 Xstrdup_a(out, str, return IMAP_ERROR); \
144 typedef gchar * IMAPSet;
146 struct _IMAPFolderItem
155 static Folder *imap_folder_new(const gchar * name, const gchar * path);
156 static void imap_folder_destroy(Folder * folder);
158 static IMAPSession *imap_session_new(const PrefsAccount * account);
159 static void imap_session_authenticate(IMAPSession * session,
160 const PrefsAccount * account);
161 static void imap_session_destroy(Session * session);
163 static gchar *imap_fetch_msg(Folder * folder, FolderItem * item, gint uid);
164 static gint imap_add_msg(Folder * folder,
166 const gchar * file, MsgFlags * flags);
167 static gint imap_add_msgs(Folder * folder, FolderItem * dest,
169 MsgNumberList **newnum_list);
171 static gint imap_copy_msg(Folder * folder,
172 FolderItem * dest, MsgInfo * msginfo);
174 static gint imap_remove_msg(Folder * folder, FolderItem * item, gint uid);
175 static gint imap_remove_all_msg(Folder * folder, FolderItem * item);
177 static gboolean imap_is_msg_changed(Folder * folder,
178 FolderItem * item, MsgInfo * msginfo);
180 static gint imap_close(Folder * folder, FolderItem * item);
182 static void imap_scan_tree(Folder * folder);
184 static gint imap_create_tree(Folder * folder);
186 static FolderItem *imap_create_folder(Folder * folder,
189 static gint imap_rename_folder(Folder * folder,
190 FolderItem * item, const gchar * name);
191 static gint imap_remove_folder(Folder * folder, FolderItem * item);
194 static void imap_folder_init (Folder *folder,
198 static FolderItem *imap_folder_item_new (Folder *folder);
199 static void imap_folder_item_destroy (Folder *folder,
202 static IMAPSession *imap_session_get (Folder *folder);
204 static gint imap_auth (IMAPSession *session,
209 static gint imap_scan_tree_recursive (IMAPSession *session,
211 static GSList *imap_parse_list (IMAPFolder *folder,
212 IMAPSession *session,
213 const gchar *real_path,
216 static void imap_create_missing_folders (Folder *folder);
217 static FolderItem *imap_create_special_folder
219 SpecialFolderItemType stype,
222 static gint imap_do_copy (Folder *folder,
225 gboolean remove_source);
227 static void imap_delete_all_cached_messages (FolderItem *item);
230 static SockInfo *imap_open (const gchar *server,
234 static SockInfo *imap_open (const gchar *server,
239 static SockInfo *imap_open_tunnel(const gchar *server,
240 const gchar *tunnelcmd,
243 static SockInfo *imap_open_tunnel(const gchar *server,
244 const gchar *tunnelcmd);
248 static SockInfo *imap_init_sock(SockInfo *sock, SSLType ssl_type);
250 static SockInfo *imap_init_sock(SockInfo *sock);
253 static gchar *imap_get_flag_str (IMAPFlags flags);
254 static gint imap_set_message_flags (IMAPSession *session,
255 MsgNumberList *numlist,
258 static gint imap_select (IMAPSession *session,
264 guint32 *uid_validity);
265 static gint imap_status (IMAPSession *session,
271 guint32 *uid_validity,
274 static void imap_parse_namespace (IMAPSession *session,
276 static void imap_get_namespace_by_list (IMAPSession *session,
278 static IMAPNameSpace *imap_find_namespace (IMAPFolder *folder,
280 static gchar imap_get_path_separator (IMAPFolder *folder,
282 static gchar *imap_get_real_path (IMAPFolder *folder,
285 static gchar *imap_parse_atom (SockInfo *sock,
290 static MsgFlags imap_parse_flags (const gchar *flag_str);
291 static MsgInfo *imap_parse_envelope (SockInfo *sock,
294 static gint imap_greeting (IMAPSession *session);
295 static gboolean imap_has_capability (IMAPSession *session,
297 void imap_free_capabilities (IMAPSession *session);
298 static const IMAPSet numberlist_to_imapset
299 (MsgNumberList *list);
301 /* low-level IMAP4rev1 commands */
302 static gint imap_cmd_authenticate
303 (IMAPSession *session,
307 static gint imap_cmd_login (IMAPSession *sock,
310 static gint imap_cmd_logout (IMAPSession *sock);
311 static gint imap_cmd_noop (IMAPSession *sock);
312 static gint imap_cmd_starttls (IMAPSession *sock);
313 static gint imap_cmd_namespace (IMAPSession *sock,
315 static gint imap_cmd_list (IMAPSession *session,
317 const gchar *mailbox,
319 static gint imap_cmd_do_select (IMAPSession *sock,
325 guint32 *uid_validity);
326 static gint imap_cmd_select (IMAPSession *sock,
331 guint32 *uid_validity);
332 static gint imap_cmd_examine (IMAPSession *sock,
337 guint32 *uid_validity);
338 static gint imap_cmd_create (IMAPSession *sock,
339 const gchar *folder);
340 static gint imap_cmd_rename (IMAPSession *sock,
341 const gchar *oldfolder,
342 const gchar *newfolder);
343 static gint imap_cmd_delete (IMAPSession *sock,
344 const gchar *folder);
345 static gint imap_cmd_envelope (IMAPSession *sock,
347 static gint imap_cmd_fetch (IMAPSession *sock,
349 const gchar *filename);
350 static gint imap_cmd_append (IMAPSession *session,
351 const gchar *destfolder,
355 static gint imap_cmd_copy (IMAPSession *session,
357 const gchar *destfolder,
359 static gint imap_cmd_store (IMAPSession *sock,
362 static gint imap_cmd_expunge (IMAPSession *sock);
363 static gint imap_cmd_close (IMAPSession *session);
365 static gint imap_cmd_ok (IMAPSession *session,
367 static void imap_gen_send (IMAPSession *sock,
368 const gchar *format, ...);
369 static gint imap_gen_recv (IMAPSession *sock,
372 /* misc utility functions */
373 static gchar *strchr_cpy (const gchar *src,
377 static gchar *get_quoted (const gchar *src,
381 static gchar *search_array_contain_str (GPtrArray *array,
383 static gchar *search_array_str (GPtrArray *array,
385 static void imap_path_separator_subst (gchar *str,
388 static gchar *imap_modified_utf7_to_locale (const gchar *mutf7_str);
389 static gchar *imap_locale_to_modified_utf7 (const gchar *from);
391 static gboolean imap_rename_folder_func (GNode *node,
393 static gint imap_get_num_list (Folder *folder,
396 static GSList *imap_get_msginfos (Folder *folder,
398 GSList *msgnum_list);
399 static MsgInfo *imap_get_msginfo (Folder *folder,
402 static gboolean imap_check_msgnum_validity (Folder *folder,
404 static void imap_change_flags (Folder *folder,
407 MsgPermFlags newflags);
408 static gchar *imap_folder_get_path (Folder *folder);
409 static gchar *imap_item_get_path (Folder *folder,
412 FolderClass imap_class =
418 /* Folder functions */
424 /* FolderItem functions */
425 imap_folder_item_new,
426 imap_folder_item_destroy,
436 imap_check_msgnum_validity,
438 /* Message functions */
452 FolderClass *imap_get_class(void)
457 Folder *imap_folder_new(const gchar *name, const gchar *path)
461 folder = (Folder *)g_new0(IMAPFolder, 1);
462 folder->klass = &imap_class;
463 imap_folder_init(folder, name, path);
468 void imap_folder_destroy(Folder *folder)
472 dir = imap_folder_get_path(folder);
473 if (is_dir_exist(dir))
474 remove_dir_recursive(dir);
477 folder_remote_folder_destroy(REMOTE_FOLDER(folder));
480 static void imap_folder_init(Folder *folder, const gchar *name,
483 folder_remote_folder_init((Folder *)folder, name, path);
486 static FolderItem *imap_folder_item_new(Folder *folder)
488 IMAPFolderItem *item;
490 item = g_new0(IMAPFolderItem, 1);
493 item->uid_list = NULL;
495 return (FolderItem *)item;
498 static void imap_folder_item_destroy(Folder *folder, FolderItem *_item)
500 IMAPFolderItem *item = (IMAPFolderItem *)_item;
502 g_return_if_fail(item != NULL);
503 g_slist_free(item->uid_list);
508 static gboolean imap_reset_uid_lists_func(GNode *node, gpointer data)
510 IMAPFolderItem *item = (IMAPFolderItem *)node->data;
514 g_slist_free(item->uid_list);
515 item->uid_list = NULL;
520 static void imap_reset_uid_lists(Folder *folder)
522 if(folder->node == NULL)
525 /* Destroy all uid lists and rest last uid */
526 g_node_traverse(folder->node, G_IN_ORDER, G_TRAVERSE_ALL, -1, imap_reset_uid_lists_func, NULL);
529 static gint imap_auth(IMAPSession *session, const gchar *user, const gchar *pass,
532 if (type == 0 || type == IMAP_AUTH_LOGIN)
533 return imap_cmd_login(session, user, pass);
535 return imap_cmd_authenticate(session, user, pass, type);
538 static IMAPSession *imap_session_get(Folder *folder)
540 RemoteFolder *rfolder = REMOTE_FOLDER(folder);
541 IMAPSession *session = NULL;
543 g_return_val_if_fail(folder != NULL, NULL);
544 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, NULL);
545 g_return_val_if_fail(folder->account != NULL, NULL);
547 /* Make sure we have a session */
548 if (rfolder->session != NULL) {
549 session = IMAP_SESSION(rfolder->session);
551 imap_reset_uid_lists(folder);
552 session = imap_session_new(folder->account);
557 if (SESSION(session)->sock->state == CONN_DISCONNECTED) {
558 debug_print("IMAP server disconnected\n");
559 session_destroy(SESSION(session));
560 imap_reset_uid_lists(folder);
561 session = imap_session_new(folder->account);
564 /* Make sure session is authenticated */
565 if (!IMAP_SESSION(session)->authenticated)
566 imap_session_authenticate(IMAP_SESSION(session), folder->account);
567 if (!IMAP_SESSION(session)->authenticated) {
568 session_destroy(SESSION(session));
569 rfolder->session = NULL;
573 /* Make sure we have parsed the IMAP namespace */
574 imap_parse_namespace(IMAP_SESSION(session),
575 IMAP_FOLDER(folder));
577 /* I think the point of this code is to avoid sending a
578 * keepalive if we've used the session recently and therefore
579 * think it's still alive. Unfortunately, most of the code
580 * does not yet check for errors on the socket, and so if the
581 * connection drops we don't notice until the timeout expires.
582 * A better solution than sending a NOOP every time would be
583 * for every command to be prepared to retry until it is
584 * successfully sent. -- mbp */
585 if (time(NULL) - session->last_access_time > SESSION_TIMEOUT) {
586 /* verify that the session is still alive */
587 if (imap_cmd_noop(session) != IMAP_SUCCESS) {
588 /* Check if this is the first try to establish a
589 connection, if yes we don't try to reconnect */
590 if (rfolder->session == NULL) {
591 log_warning(_("Connecting %s failed"),
592 folder->account->recv_server);
593 session_destroy(SESSION(session));
596 log_warning(_("IMAP4 connection to %s has been"
597 " disconnected. Reconnecting...\n"),
598 folder->account->recv_server);
599 session_destroy(SESSION(session));
600 /* Clear folders session to make imap_session_get create
601 a new session, because of rfolder->session == NULL
602 it will not try to reconnect again and so avoid an
604 rfolder->session = NULL;
605 session = imap_session_get(folder);
610 rfolder->session = SESSION(session);
612 session->last_access_time = time(NULL);
614 return IMAP_SESSION(session);
617 IMAPSession *imap_session_new(const PrefsAccount *account)
619 IMAPSession *session;
624 /* FIXME: IMAP over SSL only... */
627 port = account->set_imapport ? account->imapport
628 : account->ssl_imap == SSL_TUNNEL ? IMAPS_PORT : IMAP4_PORT;
629 ssl_type = account->ssl_imap;
631 port = account->set_imapport ? account->imapport
635 if (account->set_tunnelcmd) {
636 log_message(_("creating tunneled IMAP4 connection\n"));
638 if ((imap_sock = imap_open_tunnel(account->recv_server,
642 if ((imap_sock = imap_open_tunnel(account->recv_server,
643 account->tunnelcmd)) == NULL)
647 g_return_val_if_fail(account->recv_server != NULL, NULL);
649 log_message(_("creating IMAP4 connection to %s:%d ...\n"),
650 account->recv_server, port);
653 if ((imap_sock = imap_open(account->recv_server, port,
656 if ((imap_sock = imap_open(account->recv_server, port)) == NULL)
661 session = g_new0(IMAPSession, 1);
662 session_init(SESSION(session));
663 SESSION(session)->type = SESSION_IMAP;
664 SESSION(session)->server = g_strdup(account->recv_server);
665 SESSION(session)->sock = imap_sock;
667 SESSION(session)->destroy = imap_session_destroy;
669 session->capability = NULL;
671 session->mbox = NULL;
672 session->authenticated = FALSE;
673 session->cmd_count = 0;
675 /* Only need to log in if the connection was not PREAUTH */
676 if (imap_greeting(session) != IMAP_SUCCESS) {
677 session_destroy(SESSION(session));
682 if (account->ssl_imap == SSL_STARTTLS && imap_has_capability(session, "STARTTLS")) {
685 ok = imap_cmd_starttls(session);
686 if (ok != IMAP_SUCCESS) {
687 log_warning(_("Can't start TLS session.\n"));
688 session_destroy(SESSION(session));
691 if (!ssl_init_socket_with_method(SESSION(session)->sock, SSL_METHOD_TLSv1)) {
692 session_destroy(SESSION(session));
696 imap_free_capabilities(session);
697 session->authenticated = FALSE;
698 session->cmd_count = 1;
700 if (imap_greeting(session) != IMAP_SUCCESS) {
701 session_destroy(SESSION(session));
706 log_message("IMAP connection is %s-authenticated\n",
707 (session->authenticated) ? "pre" : "un");
712 void imap_session_authenticate(IMAPSession *session, const PrefsAccount *account)
716 g_return_if_fail(account->userid != NULL);
718 pass = account->passwd;
721 tmp_pass = input_dialog_query_password(account->recv_server, account->userid);
724 Xstrdup_a(pass, tmp_pass, {g_free(tmp_pass); return;});
728 if (imap_auth(session, account->userid, pass, account->imap_auth_type) != IMAP_SUCCESS) {
729 imap_cmd_logout(session);
733 session->authenticated = TRUE;
736 void imap_session_destroy(Session *session)
738 sock_close(session->sock);
739 session->sock = NULL;
741 g_free(IMAP_SESSION(session)->mbox);
742 imap_free_capabilities(IMAP_SESSION(session));
745 gchar *imap_fetch_msg(Folder *folder, FolderItem *item, gint uid)
747 gchar *path, *filename;
748 IMAPSession *session;
751 g_return_val_if_fail(folder != NULL, NULL);
752 g_return_val_if_fail(item != NULL, NULL);
754 path = folder_item_get_path(item);
755 if (!is_dir_exist(path))
757 filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(uid), NULL);
760 if (is_file_exist(filename)) {
761 debug_print("message %d has been already cached.\n", uid);
765 session = imap_session_get(folder);
771 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
772 NULL, NULL, NULL, NULL);
773 if (ok != IMAP_SUCCESS) {
774 g_warning("can't select mailbox %s\n", item->path);
779 debug_print("getting message %d...\n", uid);
780 ok = imap_cmd_fetch(session, (guint32)uid, filename);
782 if (ok != IMAP_SUCCESS) {
783 g_warning("can't fetch message %d\n", uid);
791 gint imap_add_msg(Folder *folder, FolderItem *dest, const gchar *file, MsgFlags *flags)
795 MsgFileInfo fileinfo;
796 MsgNumberList *msgnum_list = NULL;
798 g_return_val_if_fail(file != NULL, -1);
800 fileinfo.file = (gchar *)file;
801 fileinfo.flags = flags;
802 file_list.data = &fileinfo;
803 file_list.next = NULL;
805 ret = imap_add_msgs(folder, dest, &file_list, &msgnum_list);
806 g_slist_free(msgnum_list);
810 gint imap_add_msgs(Folder *folder, FolderItem *dest, GSList *file_list,
811 MsgNumberList **newnum_list)
814 IMAPSession *session;
815 guint32 last_uid = 0;
817 MsgFileInfo *fileinfo;
820 g_return_val_if_fail(folder != NULL, -1);
821 g_return_val_if_fail(dest != NULL, -1);
822 g_return_val_if_fail(file_list != NULL, -1);
824 session = imap_session_get(folder);
825 if (!session) return -1;
827 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
829 for (cur = file_list; cur != NULL; cur = cur->next) {
830 IMAPFlags iflags = 0;
832 fileinfo = (MsgFileInfo *)cur->data;
834 if (fileinfo->flags) {
835 if (MSG_IS_MARKED(*fileinfo->flags))
836 iflags |= IMAP_FLAG_FLAGGED;
837 if (MSG_IS_REPLIED(*fileinfo->flags))
838 iflags |= IMAP_FLAG_ANSWERED;
839 if (!MSG_IS_UNREAD(*fileinfo->flags))
840 iflags |= IMAP_FLAG_SEEN;
843 if (dest->stype == F_OUTBOX ||
844 dest->stype == F_QUEUE ||
845 dest->stype == F_DRAFT ||
846 dest->stype == F_TRASH)
847 iflags |= IMAP_FLAG_SEEN;
849 ok = imap_cmd_append(session, destdir, fileinfo->file, iflags, &newnum);
851 if (ok != IMAP_SUCCESS) {
852 g_warning("can't append message %s\n", fileinfo->file);
857 *newnum_list = g_slist_append(*newnum_list, GINT_TO_POINTER(newnum));
858 if (newnum > last_uid)
867 static gint imap_do_copy(Folder *folder, FolderItem *dest, MsgInfo *msginfo,
868 gboolean remove_source)
871 IMAPSession *session;
875 g_return_val_if_fail(folder != NULL, -1);
876 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
877 g_return_val_if_fail(dest != NULL, -1);
878 g_return_val_if_fail(msginfo != NULL, -1);
880 session = imap_session_get(folder);
881 if (!session) return -1;
883 if (msginfo->folder == dest) {
884 g_warning("the src folder is identical to the dest.\n");
888 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
890 /* ensure source folder selected */
891 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
892 NULL, NULL, NULL, NULL);
893 if (ok != IMAP_SUCCESS)
897 debug_print("Moving message %s%c%d to %s ...\n",
898 msginfo->folder->path, G_DIR_SEPARATOR,
899 msginfo->msgnum, destdir);
901 debug_print("Copying message %s%c%d to %s ...\n",
902 msginfo->folder->path, G_DIR_SEPARATOR,
903 msginfo->msgnum, destdir);
905 ok = imap_cmd_copy(session, msginfo->msgnum, destdir, &newuid);
907 if (ok == IMAP_SUCCESS && remove_source) {
908 MsgNumberList numlist;
911 numlist.data = GINT_TO_POINTER(msginfo->msgnum);
913 imap_set_message_flags(session, &numlist,
914 IMAP_FLAG_DELETED, TRUE);
915 ok = imap_cmd_expunge(session);
920 if (ok == IMAP_SUCCESS)
926 gint imap_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
931 g_return_val_if_fail(folder != NULL, -1);
932 g_return_val_if_fail(dest != NULL, -1);
933 g_return_val_if_fail(msginfo != NULL, -1);
934 g_return_val_if_fail(msginfo->folder != NULL, -1);
936 if (folder == msginfo->folder->folder)
937 return imap_do_copy(folder, dest, msginfo, FALSE);
939 srcfile = procmsg_get_message_file(msginfo);
940 if (!srcfile) return -1;
942 ret = imap_add_msg(folder, dest, srcfile, NULL);
949 gint imap_remove_msg(Folder *folder, FolderItem *item, gint uid)
952 IMAPSession *session;
954 MsgNumberList numlist;
956 g_return_val_if_fail(folder != NULL, -1);
957 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
958 g_return_val_if_fail(item != NULL, -1);
960 session = imap_session_get(folder);
961 if (!session) return -1;
963 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
964 NULL, NULL, NULL, NULL);
965 if (ok != IMAP_SUCCESS)
969 numlist.data = GINT_TO_POINTER(uid);
971 ok = imap_set_message_flags
972 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
973 &numlist, IMAP_FLAG_DELETED, TRUE);
974 if (ok != IMAP_SUCCESS) {
975 log_warning(_("can't set deleted flags: %d\n"), uid);
979 ok = imap_cmd_expunge(session);
980 if (ok != IMAP_SUCCESS) {
981 log_warning(_("can't expunge\n"));
985 dir = folder_item_get_path(item);
986 if (is_dir_exist(dir))
987 remove_numbered_files(dir, uid, uid);
993 gint imap_remove_all_msg(Folder *folder, FolderItem *item)
995 gint exists, recent, unseen;
996 guint32 uid_validity;
998 IMAPSession *session;
1001 g_return_val_if_fail(folder != NULL, -1);
1002 g_return_val_if_fail(item != NULL, -1);
1004 session = imap_session_get(folder);
1005 if (!session) return -1;
1007 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
1008 &exists, &recent, &unseen, &uid_validity);
1009 if (ok != IMAP_SUCCESS)
1012 return IMAP_SUCCESS;
1014 imap_gen_send(session,
1015 "STORE 1:%d +FLAGS (\\Deleted)", exists);
1016 ok = imap_cmd_ok(session, NULL);
1017 if (ok != IMAP_SUCCESS) {
1018 log_warning(_("can't set deleted flags: 1:%d\n"), exists);
1022 ok = imap_cmd_expunge(session);
1023 if (ok != IMAP_SUCCESS) {
1024 log_warning(_("can't expunge\n"));
1028 dir = folder_item_get_path(item);
1029 if (is_dir_exist(dir))
1030 remove_all_numbered_files(dir);
1033 return IMAP_SUCCESS;
1036 gboolean imap_is_msg_changed(Folder *folder, FolderItem *item, MsgInfo *msginfo)
1038 /* TODO: properly implement this method */
1042 gint imap_close(Folder *folder, FolderItem *item)
1045 IMAPSession *session;
1047 g_return_val_if_fail(folder != NULL, -1);
1049 session = imap_session_get(folder);
1050 if (!session) return -1;
1052 if (session->mbox) {
1053 ok = imap_cmd_close(session);
1054 if (ok != IMAP_SUCCESS)
1055 log_warning(_("can't close folder\n"));
1057 g_free(session->mbox);
1058 session->mbox = NULL;
1066 void imap_scan_tree(Folder *folder)
1069 IMAPSession *session;
1070 gchar *root_folder = NULL;
1072 g_return_if_fail(folder != NULL);
1073 g_return_if_fail(folder->account != NULL);
1075 session = imap_session_get(folder);
1077 if (!folder->node) {
1078 folder_tree_destroy(folder);
1079 item = folder_item_new(folder, folder->name, NULL);
1080 item->folder = folder;
1081 folder->node = g_node_new(item);
1086 if (folder->account->imap_dir && *folder->account->imap_dir) {
1087 Xstrdup_a(root_folder, folder->account->imap_dir, return);
1088 strtailchomp(root_folder, '/');
1089 debug_print("IMAP root directory: %s\n", root_folder);
1092 item = folder_item_new(folder, folder->name, root_folder);
1093 item->folder = folder;
1094 item->no_select = TRUE;
1095 folder->node = g_node_new(item);
1097 imap_scan_tree_recursive(session, item);
1099 imap_create_missing_folders(folder);
1102 static gint imap_scan_tree_recursive(IMAPSession *session, FolderItem *item)
1105 IMAPFolder *imapfolder;
1106 FolderItem *new_item;
1107 GSList *item_list, *cur;
1109 gchar *wildcard_path;
1113 g_return_val_if_fail(item != NULL, -1);
1114 g_return_val_if_fail(item->folder != NULL, -1);
1115 g_return_val_if_fail(item->no_sub == FALSE, -1);
1117 folder = FOLDER(item->folder);
1118 imapfolder = IMAP_FOLDER(folder);
1120 separator = imap_get_path_separator(imapfolder, item->path);
1122 if (item->folder->ui_func)
1123 item->folder->ui_func(folder, item, folder->ui_func_data);
1126 wildcard[0] = separator;
1129 real_path = imap_get_real_path(imapfolder, item->path);
1133 real_path = g_strdup("");
1136 Xstrcat_a(wildcard_path, real_path, wildcard,
1137 {g_free(real_path); return IMAP_ERROR;});
1138 QUOTE_IF_REQUIRED(wildcard_path, wildcard_path);
1140 imap_gen_send(session, "LIST \"\" %s",
1143 strtailchomp(real_path, separator);
1144 item_list = imap_parse_list(imapfolder, session, real_path, NULL);
1147 for (cur = item_list; cur != NULL; cur = cur->next) {
1148 new_item = cur->data;
1149 if (!strcmp(new_item->path, "INBOX")) {
1150 if (!folder->inbox) {
1151 new_item->stype = F_INBOX;
1152 item->folder->inbox = new_item;
1154 folder_item_destroy(new_item);
1157 } else if (!item->parent || item->stype == F_INBOX) {
1160 base = g_basename(new_item->path);
1162 if (!folder->outbox && !strcasecmp(base, "Sent")) {
1163 new_item->stype = F_OUTBOX;
1164 folder->outbox = new_item;
1165 } else if (!folder->draft && !strcasecmp(base, "Drafts")) {
1166 new_item->stype = F_DRAFT;
1167 folder->draft = new_item;
1168 } else if (!folder->queue && !strcasecmp(base, "Queue")) {
1169 new_item->stype = F_QUEUE;
1170 folder->queue = new_item;
1171 } else if (!folder->trash && !strcasecmp(base, "Trash")) {
1172 new_item->stype = F_TRASH;
1173 folder->trash = new_item;
1176 folder_item_append(item, new_item);
1177 if (new_item->no_sub == FALSE)
1178 imap_scan_tree_recursive(session, new_item);
1181 return IMAP_SUCCESS;
1184 static GSList *imap_parse_list(IMAPFolder *folder, IMAPSession *session,
1185 const gchar *real_path, gchar *separator)
1187 gchar buf[IMAPBUFSIZE];
1189 gchar separator_str[16];
1192 gchar *loc_name, *loc_path;
1193 GSList *item_list = NULL;
1195 FolderItem *new_item;
1197 debug_print("getting list of %s ...\n",
1198 *real_path ? real_path : "\"\"");
1200 str = g_string_new(NULL);
1203 if (sock_gets(SESSION(session)->sock, buf, sizeof(buf)) <= 0) {
1204 log_warning(_("error occurred while getting LIST.\n"));
1208 if (buf[0] != '*' || buf[1] != ' ') {
1209 log_print("IMAP4< %s\n", buf);
1212 debug_print("IMAP4< %s\n", buf);
1214 g_string_assign(str, buf);
1216 if (strncmp(p, "LIST ", 5) != 0) continue;
1219 if (*p != '(') continue;
1221 p = strchr_cpy(p, ')', flags, sizeof(flags));
1223 while (*p == ' ') p++;
1225 p = strchr_cpy(p, ' ', separator_str, sizeof(separator_str));
1227 extract_quote(separator_str, '"');
1228 if (!strcmp(separator_str, "NIL"))
1229 separator_str[0] = '\0';
1231 *separator = separator_str[0];
1234 while (*p == ' ') p++;
1235 if (*p == '{' || *p == '"')
1236 p = imap_parse_atom(SESSION(session)->sock, p,
1237 buf, sizeof(buf), str);
1239 strncpy2(buf, p, sizeof(buf));
1240 strtailchomp(buf, separator_str[0]);
1241 if (buf[0] == '\0') continue;
1242 if (!strcmp(buf, real_path)) continue;
1244 if (separator_str[0] != '\0')
1245 subst_char(buf, separator_str[0], '/');
1246 name = g_basename(buf);
1247 if (name[0] == '.') continue;
1249 loc_name = imap_modified_utf7_to_locale(name);
1250 loc_path = imap_modified_utf7_to_locale(buf);
1251 new_item = folder_item_new(FOLDER(folder), loc_name, loc_path);
1252 if (strcasestr(flags, "\\Noinferiors") != NULL)
1253 new_item->no_sub = TRUE;
1254 if (strcmp(buf, "INBOX") != 0 &&
1255 strcasestr(flags, "\\Noselect") != NULL)
1256 new_item->no_select = TRUE;
1258 item_list = g_slist_append(item_list, new_item);
1260 debug_print("folder %s has been added.\n", loc_path);
1265 g_string_free(str, TRUE);
1270 gint imap_create_tree(Folder *folder)
1272 g_return_val_if_fail(folder != NULL, -1);
1273 g_return_val_if_fail(folder->node != NULL, -1);
1274 g_return_val_if_fail(folder->node->data != NULL, -1);
1275 g_return_val_if_fail(folder->account != NULL, -1);
1277 imap_scan_tree(folder);
1278 imap_create_missing_folders(folder);
1283 static void imap_create_missing_folders(Folder *folder)
1285 g_return_if_fail(folder != NULL);
1288 folder->inbox = imap_create_special_folder
1289 (folder, F_INBOX, "INBOX");
1291 if (!folder->outbox)
1292 folder->outbox = imap_create_special_folder
1293 (folder, F_OUTBOX, "Sent");
1295 folder->draft = imap_create_special_folder
1296 (folder, F_DRAFT, "Drafts");
1298 folder->queue = imap_create_special_folder
1299 (folder, F_QUEUE, "Queue");
1302 folder->trash = imap_create_special_folder
1303 (folder, F_TRASH, "Trash");
1306 static FolderItem *imap_create_special_folder(Folder *folder,
1307 SpecialFolderItemType stype,
1311 FolderItem *new_item;
1313 g_return_val_if_fail(folder != NULL, NULL);
1314 g_return_val_if_fail(folder->node != NULL, NULL);
1315 g_return_val_if_fail(folder->node->data != NULL, NULL);
1316 g_return_val_if_fail(folder->account != NULL, NULL);
1317 g_return_val_if_fail(name != NULL, NULL);
1319 item = FOLDER_ITEM(folder->node->data);
1320 new_item = imap_create_folder(folder, item, name);
1323 g_warning("Can't create '%s'\n", name);
1324 if (!folder->inbox) return NULL;
1326 new_item = imap_create_folder(folder, folder->inbox, name);
1328 g_warning("Can't create '%s' under INBOX\n", name);
1330 new_item->stype = stype;
1332 new_item->stype = stype;
1337 static gchar *imap_folder_get_path(Folder *folder)
1341 g_return_val_if_fail(folder != NULL, NULL);
1342 g_return_val_if_fail(folder->account != NULL, NULL);
1344 folder_path = g_strconcat(get_imap_cache_dir(),
1346 folder->account->recv_server,
1348 folder->account->userid,
1354 static gchar *imap_item_get_path(Folder *folder, FolderItem *item)
1356 gchar *folder_path, *path;
1358 g_return_val_if_fail(folder != NULL, NULL);
1359 g_return_val_if_fail(item != NULL, NULL);
1360 folder_path = imap_folder_get_path(folder);
1362 g_return_val_if_fail(folder_path != NULL, NULL);
1363 if (folder_path[0] == G_DIR_SEPARATOR) {
1365 path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
1368 path = g_strdup(folder_path);
1371 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1372 folder_path, G_DIR_SEPARATOR_S,
1375 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1378 g_free(folder_path);
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, 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, 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, "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, 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, "INBOX",
1583 &exists, &recent, &unseen, &uid_validity);
1584 if (ok != IMAP_SUCCESS) {
1589 ok = imap_cmd_delete(session, 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 MsgNumberList *numlist)
1611 GSList *newlist = NULL;
1612 GSList *llast = NULL;
1617 g_return_val_if_fail(session != NULL, NULL);
1618 g_return_val_if_fail(item != NULL, NULL);
1619 g_return_val_if_fail(item->folder != NULL, NULL);
1620 g_return_val_if_fail(FOLDER_CLASS(item->folder) == &imap_class, NULL);
1622 imapset = numberlist_to_imapset(numlist);
1623 while (imapset != NULL) {
1624 if (imap_cmd_envelope(session, imapset)
1626 log_warning(_("can't get envelope\n"));
1630 str = g_string_new(NULL);
1633 if ((tmp = sock_getline(SESSION(session)->sock)) == NULL) {
1634 log_warning(_("error occurred while getting envelope.\n"));
1635 g_string_free(str, TRUE);
1639 if (tmp[0] != '*' || tmp[1] != ' ') {
1640 log_print("IMAP4< %s\n", tmp);
1644 if (strstr(tmp, "FETCH") == NULL) {
1645 log_print("IMAP4< %s\n", tmp);
1649 log_print("IMAP4< %s\n", tmp);
1650 g_string_assign(str, tmp);
1653 msginfo = imap_parse_envelope
1654 (SESSION(session)->sock, item, str);
1656 log_warning(_("can't parse envelope: %s\n"), str->str);
1659 if (item->stype == F_QUEUE) {
1660 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_QUEUED);
1661 } else if (item->stype == F_DRAFT) {
1662 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_DRAFT);
1665 msginfo->folder = item;
1668 llast = newlist = g_slist_append(newlist, msginfo);
1670 llast = g_slist_append(llast, msginfo);
1671 llast = llast->next;
1675 g_string_free(str, TRUE);
1676 imapset = numberlist_to_imapset(NULL);
1682 static void imap_delete_all_cached_messages(FolderItem *item)
1686 g_return_if_fail(item != NULL);
1687 g_return_if_fail(item->folder != NULL);
1688 g_return_if_fail(FOLDER_CLASS(item->folder) == &imap_class);
1690 debug_print("Deleting all cached messages...\n");
1692 dir = folder_item_get_path(item);
1693 if (is_dir_exist(dir))
1694 remove_all_numbered_files(dir);
1697 debug_print("done.\n");
1701 static SockInfo *imap_open_tunnel(const gchar *server,
1702 const gchar *tunnelcmd,
1705 static SockInfo *imap_open_tunnel(const gchar *server,
1706 const gchar *tunnelcmd)
1711 if ((sock = sock_connect_cmd(server, tunnelcmd)) == NULL) {
1712 log_warning(_("Can't establish IMAP4 session with: %s\n"),
1717 return imap_init_sock(sock, ssl_type);
1719 return imap_init_sock(sock);
1725 static SockInfo *imap_open(const gchar *server, gushort port,
1728 static SockInfo *imap_open(const gchar *server, gushort port)
1733 if ((sock = sock_connect(server, port)) == NULL) {
1734 log_warning(_("Can't connect to IMAP4 server: %s:%d\n"),
1740 if (ssl_type == SSL_TUNNEL && !ssl_init_socket(sock)) {
1741 log_warning(_("Can't establish IMAP4 session with: %s:%d\n"),
1751 static SockInfo *imap_init_sock(SockInfo *sock, SSLType ssl_type)
1753 static SockInfo *imap_init_sock(SockInfo *sock)
1760 static GList *imap_parse_namespace_str(gchar *str)
1765 IMAPNameSpace *namespace;
1766 GList *ns_list = NULL;
1768 while (*p != '\0') {
1769 /* parse ("#foo" "/") */
1771 while (*p && *p != '(') p++;
1772 if (*p == '\0') break;
1775 while (*p && *p != '"') p++;
1776 if (*p == '\0') break;
1780 while (*p && *p != '"') p++;
1781 if (*p == '\0') break;
1785 while (*p && isspace(*p)) p++;
1786 if (*p == '\0') break;
1787 if (strncmp(p, "NIL", 3) == 0)
1789 else if (*p == '"') {
1792 while (*p && *p != '"') p++;
1793 if (*p == '\0') break;
1798 while (*p && *p != ')') p++;
1799 if (*p == '\0') break;
1802 namespace = g_new(IMAPNameSpace, 1);
1803 namespace->name = g_strdup(name);
1804 namespace->separator = separator ? separator[0] : '\0';
1805 ns_list = g_list_append(ns_list, namespace);
1811 static void imap_parse_namespace(IMAPSession *session, IMAPFolder *folder)
1816 g_return_if_fail(session != NULL);
1817 g_return_if_fail(folder != NULL);
1819 if (folder->ns_personal != NULL ||
1820 folder->ns_others != NULL ||
1821 folder->ns_shared != NULL)
1824 if (!imap_has_capability(session, "NAMESPACE")) {
1825 imap_get_namespace_by_list(session, folder);
1829 if (imap_cmd_namespace(session, &ns_str)
1831 log_warning(_("can't get namespace\n"));
1835 str_array = strsplit_parenthesis(ns_str, '(', ')', 3);
1836 if (str_array == NULL) {
1838 imap_get_namespace_by_list(session, folder);
1842 folder->ns_personal = imap_parse_namespace_str(str_array[0]);
1843 if (str_array[0] && str_array[1])
1844 folder->ns_others = imap_parse_namespace_str(str_array[1]);
1845 if (str_array[0] && str_array[1] && str_array[2])
1846 folder->ns_shared = imap_parse_namespace_str(str_array[2]);
1847 g_strfreev(str_array);
1851 static void imap_get_namespace_by_list(IMAPSession *session, IMAPFolder *folder)
1853 GSList *item_list, *cur;
1854 gchar separator = '\0';
1855 IMAPNameSpace *namespace;
1857 g_return_if_fail(session != NULL);
1858 g_return_if_fail(folder != NULL);
1860 if (folder->ns_personal != NULL ||
1861 folder->ns_others != NULL ||
1862 folder->ns_shared != NULL)
1865 imap_gen_send(session, "LIST \"\" \"\"");
1866 item_list = imap_parse_list(folder, session, "", &separator);
1867 for (cur = item_list; cur != NULL; cur = cur->next)
1868 folder_item_destroy(FOLDER_ITEM(cur->data));
1869 g_slist_free(item_list);
1871 namespace = g_new(IMAPNameSpace, 1);
1872 namespace->name = g_strdup("");
1873 namespace->separator = separator;
1874 folder->ns_personal = g_list_append(NULL, namespace);
1877 static IMAPNameSpace *imap_find_namespace_from_list(GList *ns_list,
1880 IMAPNameSpace *namespace = NULL;
1881 gchar *tmp_path, *name;
1883 if (!path) path = "";
1885 Xstrcat_a(tmp_path, path, "/", return NULL);
1887 for (; ns_list != NULL; ns_list = ns_list->next) {
1888 IMAPNameSpace *tmp_ns = ns_list->data;
1890 Xstrdup_a(name, tmp_ns->name, return namespace);
1891 if (tmp_ns->separator && tmp_ns->separator != '/')
1892 subst_char(name, tmp_ns->separator, '/');
1893 if (strncmp(tmp_path, name, strlen(name)) == 0)
1900 static IMAPNameSpace *imap_find_namespace(IMAPFolder *folder,
1903 IMAPNameSpace *namespace;
1905 g_return_val_if_fail(folder != NULL, NULL);
1907 namespace = imap_find_namespace_from_list(folder->ns_personal, path);
1908 if (namespace) return namespace;
1909 namespace = imap_find_namespace_from_list(folder->ns_others, path);
1910 if (namespace) return namespace;
1911 namespace = imap_find_namespace_from_list(folder->ns_shared, path);
1912 if (namespace) return namespace;
1917 static gchar imap_get_path_separator(IMAPFolder *folder, const gchar *path)
1919 IMAPNameSpace *namespace;
1920 gchar separator = '/';
1922 namespace = imap_find_namespace(folder, path);
1923 if (namespace && namespace->separator)
1924 separator = namespace->separator;
1929 static gchar *imap_get_real_path(IMAPFolder *folder, const gchar *path)
1934 g_return_val_if_fail(folder != NULL, NULL);
1935 g_return_val_if_fail(path != NULL, NULL);
1937 real_path = imap_locale_to_modified_utf7(path);
1938 separator = imap_get_path_separator(folder, path);
1939 imap_path_separator_subst(real_path, separator);
1944 static gchar *imap_parse_atom(SockInfo *sock, gchar *src,
1945 gchar *dest, gint dest_len, GString *str)
1947 gchar *cur_pos = src;
1950 g_return_val_if_fail(str != NULL, cur_pos);
1952 /* read the next line if the current response buffer is empty */
1953 while (isspace(*cur_pos)) cur_pos++;
1954 while (*cur_pos == '\0') {
1955 if ((nextline = sock_getline(sock)) == NULL)
1957 g_string_assign(str, nextline);
1959 strretchomp(nextline);
1960 /* log_print("IMAP4< %s\n", nextline); */
1961 debug_print("IMAP4< %s\n", nextline);
1964 while (isspace(*cur_pos)) cur_pos++;
1967 if (!strncmp(cur_pos, "NIL", 3)) {
1970 } else if (*cur_pos == '\"') {
1973 p = get_quoted(cur_pos, '\"', dest, dest_len);
1974 cur_pos = p ? p : cur_pos + 2;
1975 } else if (*cur_pos == '{') {
1980 cur_pos = strchr_cpy(cur_pos + 1, '}', buf, sizeof(buf));
1982 g_return_val_if_fail(len > 0, cur_pos);
1984 g_string_truncate(str, 0);
1988 if ((nextline = sock_getline(sock)) == NULL)
1990 line_len += strlen(nextline);
1991 g_string_append(str, nextline);
1993 strretchomp(nextline);
1994 /* log_print("IMAP4< %s\n", nextline); */
1995 debug_print("IMAP4< %s\n", nextline);
1997 } while (line_len < len);
1999 memcpy(dest, cur_pos, MIN(len, dest_len - 1));
2000 dest[MIN(len, dest_len - 1)] = '\0';
2007 static gchar *imap_get_header(SockInfo *sock, gchar *cur_pos, gchar **headers,
2017 g_return_val_if_fail(str != NULL, cur_pos);
2019 while (isspace(*cur_pos)) cur_pos++;
2021 g_return_val_if_fail(*cur_pos == '{', cur_pos);
2023 cur_pos = strchr_cpy(cur_pos + 1, '}', buf, sizeof(buf));
2025 g_return_val_if_fail(len > 0, cur_pos);
2027 g_string_truncate(str, 0);
2031 if ((nextline = sock_getline(sock)) == NULL)
2033 block_len += strlen(nextline);
2034 g_string_append(str, nextline);
2036 strretchomp(nextline);
2037 /* debug_print("IMAP4< %s\n", nextline); */
2039 } while (block_len < len);
2041 debug_print("IMAP4< [contents of BODY.PEEK[HEADER.FIELDS (...)]]\n");
2043 *headers = g_strndup(cur_pos, len);
2046 while (isspace(*cur_pos)) cur_pos++;
2047 while (*cur_pos == '\0') {
2048 if ((nextline = sock_getline(sock)) == NULL)
2050 g_string_assign(str, nextline);
2052 strretchomp(nextline);
2053 debug_print("IMAP4< %s\n", nextline);
2056 while (isspace(*cur_pos)) cur_pos++;
2062 static MsgFlags imap_parse_flags(const gchar *flag_str)
2064 const gchar *p = flag_str;
2065 MsgFlags flags = {0, 0};
2067 flags.perm_flags = MSG_UNREAD;
2069 while ((p = strchr(p, '\\')) != NULL) {
2072 if (g_strncasecmp(p, "Recent", 6) == 0 && MSG_IS_UNREAD(flags)) {
2073 MSG_SET_PERM_FLAGS(flags, MSG_NEW);
2074 } else if (g_strncasecmp(p, "Seen", 4) == 0) {
2075 MSG_UNSET_PERM_FLAGS(flags, MSG_NEW|MSG_UNREAD);
2076 } else if (g_strncasecmp(p, "Deleted", 7) == 0) {
2077 MSG_SET_PERM_FLAGS(flags, MSG_DELETED);
2078 } else if (g_strncasecmp(p, "Flagged", 7) == 0) {
2079 MSG_SET_PERM_FLAGS(flags, MSG_MARKED);
2080 } else if (g_strncasecmp(p, "Answered", 8) == 0) {
2081 MSG_SET_PERM_FLAGS(flags, MSG_REPLIED);
2088 static MsgInfo *imap_parse_envelope(SockInfo *sock, FolderItem *item,
2091 gchar buf[IMAPBUFSIZE];
2092 MsgInfo *msginfo = NULL;
2097 MsgFlags flags = {0, 0}, imap_flags = {0, 0};
2099 g_return_val_if_fail(line_str != NULL, NULL);
2100 g_return_val_if_fail(line_str->str[0] == '*' &&
2101 line_str->str[1] == ' ', NULL);
2103 MSG_SET_TMP_FLAGS(flags, MSG_IMAP);
2104 if (item->stype == F_QUEUE) {
2105 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
2106 } else if (item->stype == F_DRAFT) {
2107 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
2110 cur_pos = line_str->str + 2;
2112 #define PARSE_ONE_ELEMENT(ch) \
2114 cur_pos = strchr_cpy(cur_pos, ch, buf, sizeof(buf)); \
2115 if (cur_pos == NULL) { \
2116 g_warning("cur_pos == NULL\n"); \
2117 procmsg_msginfo_free(msginfo); \
2122 PARSE_ONE_ELEMENT(' ');
2125 PARSE_ONE_ELEMENT(' ');
2126 g_return_val_if_fail(!strcmp(buf, "FETCH"), NULL);
2128 g_return_val_if_fail(*cur_pos == '(', NULL);
2131 while (*cur_pos != '\0' && *cur_pos != ')') {
2132 while (*cur_pos == ' ') cur_pos++;
2134 if (!strncmp(cur_pos, "UID ", 4)) {
2136 uid = strtoul(cur_pos, &cur_pos, 10);
2137 } else if (!strncmp(cur_pos, "FLAGS ", 6)) {
2139 if (*cur_pos != '(') {
2140 g_warning("*cur_pos != '('\n");
2141 procmsg_msginfo_free(msginfo);
2145 PARSE_ONE_ELEMENT(')');
2146 imap_flags = imap_parse_flags(buf);
2147 } else if (!strncmp(cur_pos, "RFC822.SIZE ", 12)) {
2149 size = strtol(cur_pos, &cur_pos, 10);
2150 } else if (!strncmp(cur_pos, "BODY[HEADER.FIELDS ", 19)) {
2154 if (*cur_pos != '(') {
2155 g_warning("*cur_pos != '('\n");
2156 procmsg_msginfo_free(msginfo);
2160 PARSE_ONE_ELEMENT(')');
2161 if (*cur_pos != ']') {
2162 g_warning("*cur_pos != ']'\n");
2163 procmsg_msginfo_free(msginfo);
2168 cur_pos = imap_get_header(sock, cur_pos, &headers,
2170 msginfo = procheader_parse_str(headers, flags, FALSE, FALSE);
2173 g_warning("invalid FETCH response: %s\n", cur_pos);
2179 msginfo->msgnum = uid;
2180 msginfo->size = size;
2181 msginfo->flags.tmp_flags |= imap_flags.tmp_flags;
2182 msginfo->flags.perm_flags = imap_flags.perm_flags;
2188 static gchar *imap_get_flag_str(IMAPFlags flags)
2193 str = g_string_new(NULL);
2195 if (IMAP_IS_SEEN(flags)) g_string_append(str, "\\Seen ");
2196 if (IMAP_IS_ANSWERED(flags)) g_string_append(str, "\\Answered ");
2197 if (IMAP_IS_FLAGGED(flags)) g_string_append(str, "\\Flagged ");
2198 if (IMAP_IS_DELETED(flags)) g_string_append(str, "\\Deleted ");
2199 if (IMAP_IS_DRAFT(flags)) g_string_append(str, "\\Draft");
2201 if (str->len > 0 && str->str[str->len - 1] == ' ')
2202 g_string_truncate(str, str->len - 1);
2205 g_string_free(str, FALSE);
2210 static gint imap_set_message_flags(IMAPSession *session,
2211 MsgNumberList *numlist,
2220 flag_str = imap_get_flag_str(flags);
2221 cmd = g_strconcat(is_set ? "+FLAGS.SILENT (" : "-FLAGS.SILENT (",
2222 flag_str, ")", NULL);
2225 imapset = numberlist_to_imapset(numlist);
2226 while (imapset != NULL) {
2227 ok = imap_cmd_store(session, imapset, cmd);
2228 imapset = numberlist_to_imapset(NULL);
2235 static gint imap_select(IMAPSession *session, IMAPFolder *folder,
2237 gint *exists, gint *recent, gint *unseen,
2238 guint32 *uid_validity)
2242 gint exists_, recent_, unseen_, uid_validity_;
2244 if (!exists || !recent || !unseen || !uid_validity) {
2245 if (session->mbox && strcmp(session->mbox, path) == 0)
2246 return IMAP_SUCCESS;
2250 uid_validity = &uid_validity_;
2253 g_free(session->mbox);
2254 session->mbox = NULL;
2256 real_path = imap_get_real_path(folder, path);
2257 ok = imap_cmd_select(session, real_path,
2258 exists, recent, unseen, uid_validity);
2259 if (ok != IMAP_SUCCESS)
2260 log_warning(_("can't select folder: %s\n"), real_path);
2262 session->mbox = g_strdup(path);
2263 session->folder_content_changed = FALSE;
2270 #define THROW(err) { ok = err; goto catch; }
2272 static gint imap_status(IMAPSession *session, IMAPFolder *folder,
2274 gint *messages, gint *recent,
2275 guint32 *uid_next, guint32 *uid_validity,
2284 *messages = *recent = *uid_next = *uid_validity = *unseen = 0;
2286 argbuf = g_ptr_array_new();
2288 real_path = imap_get_real_path(folder, path);
2289 QUOTE_IF_REQUIRED(real_path_, real_path);
2290 imap_gen_send(session, "STATUS %s "
2291 "(MESSAGES RECENT UIDNEXT UIDVALIDITY UNSEEN)",
2294 ok = imap_cmd_ok(session, argbuf);
2295 if (ok != IMAP_SUCCESS) THROW(ok);
2297 str = search_array_str(argbuf, "STATUS");
2298 if (!str) THROW(IMAP_ERROR);
2300 str = strchr(str, '(');
2301 if (!str) THROW(IMAP_ERROR);
2303 while (*str != '\0' && *str != ')') {
2304 while (*str == ' ') str++;
2306 if (!strncmp(str, "MESSAGES ", 9)) {
2308 *messages = strtol(str, &str, 10);
2309 } else if (!strncmp(str, "RECENT ", 7)) {
2311 *recent = strtol(str, &str, 10);
2312 } else if (!strncmp(str, "UIDNEXT ", 8)) {
2314 *uid_next = strtoul(str, &str, 10);
2315 } else if (!strncmp(str, "UIDVALIDITY ", 12)) {
2317 *uid_validity = strtoul(str, &str, 10);
2318 } else if (!strncmp(str, "UNSEEN ", 7)) {
2320 *unseen = strtol(str, &str, 10);
2322 g_warning("invalid STATUS response: %s\n", str);
2329 ptr_array_free_strings(argbuf);
2330 g_ptr_array_free(argbuf, TRUE);
2338 /* low-level IMAP4rev1 commands */
2340 static gint imap_cmd_authenticate(IMAPSession *session, const gchar *user,
2341 const gchar *pass, IMAPAuthType type)
2348 gchar hexdigest[33];
2352 auth_type = "CRAM-MD5";
2354 imap_gen_send(session, "AUTHENTICATE %s", auth_type);
2355 ok = imap_gen_recv(session, &buf);
2356 if (ok != IMAP_SUCCESS || buf[0] != '+' || buf[1] != ' ') {
2361 challenge = g_malloc(strlen(buf + 2) + 1);
2362 challenge_len = base64_decode(challenge, buf + 2, -1);
2363 challenge[challenge_len] = '\0';
2365 log_print("IMAP< [Decoded: %s]\n", challenge);
2367 md5_hex_hmac(hexdigest, challenge, challenge_len, pass, strlen(pass));
2370 response = g_strdup_printf("%s %s", user, hexdigest);
2371 log_print("IMAP> [Encoded: %s]\n", response);
2372 response64 = g_malloc((strlen(response) + 3) * 2 + 1);
2373 base64_encode(response64, response, strlen(response));
2376 log_print("IMAP> %s\n", response64);
2377 sock_puts(SESSION(session)->sock, response64);
2378 ok = imap_cmd_ok(session, NULL);
2379 if (ok != IMAP_SUCCESS)
2380 log_warning(_("IMAP4 authentication failed.\n"));
2385 static gint imap_cmd_login(IMAPSession *session,
2386 const gchar *user, const gchar *pass)
2388 gchar *user_, *pass_;
2391 QUOTE_IF_REQUIRED(user_, user);
2392 QUOTE_IF_REQUIRED(pass_, pass);
2393 imap_gen_send(session, "LOGIN %s %s", user_, pass_);
2395 ok = imap_cmd_ok(session, NULL);
2396 if (ok != IMAP_SUCCESS)
2397 log_warning(_("IMAP4 login failed.\n"));
2402 static gint imap_cmd_logout(IMAPSession *session)
2404 imap_gen_send(session, "LOGOUT");
2405 return imap_cmd_ok(session, NULL);
2408 /* Send CAPABILITY, and examine the server's response to see whether this
2409 * connection is pre-authenticated or not and build a list of CAPABILITIES. */
2410 static gint imap_greeting(IMAPSession *session)
2415 imap_gen_send(session, "CAPABILITY");
2417 argbuf = g_ptr_array_new();
2419 if (imap_cmd_ok(session, argbuf) != IMAP_SUCCESS ||
2420 ((capstr = search_array_str(argbuf, "CAPABILITY ")) == NULL)) {
2421 ptr_array_free_strings(argbuf);
2422 g_ptr_array_free(argbuf, TRUE);
2426 session->authenticated = search_array_str(argbuf, "PREAUTH") != NULL;
2428 capstr += strlen("CAPABILITY ");
2430 IMAP_SESSION(session)->capability = g_strsplit(capstr, " ", 0);
2432 ptr_array_free_strings(argbuf);
2433 g_ptr_array_free(argbuf, TRUE);
2438 static gboolean imap_has_capability(IMAPSession *session, const gchar *cap)
2442 for (p = session->capability; *p != NULL; ++p)
2443 if (g_strcasecmp(*p, cap) == 0)
2449 void imap_free_capabilities(IMAPSession *session)
2451 g_strfreev(session->capability);
2452 session->capability = NULL;
2455 static const IMAPSet numberlist_to_imapset(MsgNumberList *list)
2457 static GString *imapset = NULL;
2458 static MsgNumberList *numlist, *elem;
2459 guint first, last, next;
2461 if (imapset == NULL)
2462 imapset = g_string_sized_new(256);
2464 g_string_truncate(imapset, 0);
2467 g_slist_free(numlist);
2468 numlist = g_slist_copy(list);
2469 numlist = g_slist_sort(numlist, g_int_compare);
2470 } else if (numlist == NULL) {
2474 first = GPOINTER_TO_INT(numlist->data);
2476 for(elem = g_slist_next(numlist); elem != NULL; elem = g_slist_next(elem)) {
2477 next = GPOINTER_TO_INT(elem->data);
2479 if(next != (last + 1)) {
2480 if (imapset->len > 0)
2481 g_string_append(imapset, ",");
2483 g_string_sprintfa(imapset, "%d", first);
2485 g_string_sprintfa(imapset, "%d:%d", first, last);
2487 if (imapset->len > IMAPCMDLIMIT) {
2497 if (imapset->len > 0)
2498 g_string_append(imapset, ",");
2500 g_string_sprintfa(imapset, "%d", first);
2502 g_string_sprintfa(imapset, "%d:%d", first, last);
2504 g_slist_free(numlist);
2507 MsgNumberList *remaining;
2509 remaining = elem->next;
2510 remaining = g_slist_prepend(remaining, elem->data);
2512 g_slist_free(numlist);
2513 numlist = remaining;
2516 return imapset->str;
2519 static gint imap_cmd_noop(IMAPSession *session)
2521 imap_gen_send(session, "NOOP");
2522 return imap_cmd_ok(session, NULL);
2525 static gint imap_cmd_starttls(IMAPSession *session)
2527 imap_gen_send(session, "STARTTLS");
2528 return imap_cmd_ok(session, NULL);
2531 #define THROW(err) { ok = err; goto catch; }
2533 static gint imap_cmd_namespace(IMAPSession *session, gchar **ns_str)
2539 argbuf = g_ptr_array_new();
2541 imap_gen_send(session, "NAMESPACE");
2542 if ((ok = imap_cmd_ok(session, argbuf)) != IMAP_SUCCESS) THROW(ok);
2544 str = search_array_str(argbuf, "NAMESPACE");
2545 if (!str) THROW(IMAP_ERROR);
2547 *ns_str = g_strdup(str);
2550 ptr_array_free_strings(argbuf);
2551 g_ptr_array_free(argbuf, TRUE);
2558 static gint imap_cmd_list(IMAPSession *session, const gchar *ref,
2559 const gchar *mailbox, GPtrArray *argbuf)
2561 gchar *ref_, *mailbox_;
2563 if (!ref) ref = "\"\"";
2564 if (!mailbox) mailbox = "\"\"";
2566 QUOTE_IF_REQUIRED(ref_, ref);
2567 QUOTE_IF_REQUIRED(mailbox_, mailbox);
2568 imap_gen_send(session, "LIST %s %s", ref_, mailbox_);
2570 return imap_cmd_ok(session, argbuf);
2573 #define THROW goto catch
2575 static gint imap_cmd_do_select(IMAPSession *session, const gchar *folder,
2577 gint *exists, gint *recent, gint *unseen,
2578 guint32 *uid_validity)
2586 *exists = *recent = *unseen = *uid_validity = 0;
2587 argbuf = g_ptr_array_new();
2590 select_cmd = "EXAMINE";
2592 select_cmd = "SELECT";
2594 QUOTE_IF_REQUIRED(folder_, folder);
2595 imap_gen_send(session, "%s %s", select_cmd, folder_);
2597 if ((ok = imap_cmd_ok(session, argbuf)) != IMAP_SUCCESS) THROW;
2599 resp_str = search_array_contain_str(argbuf, "EXISTS");
2601 if (sscanf(resp_str,"%d EXISTS", exists) != 1) {
2602 g_warning("imap_cmd_select(): invalid EXISTS line.\n");
2607 resp_str = search_array_contain_str(argbuf, "RECENT");
2609 if (sscanf(resp_str, "%d RECENT", recent) != 1) {
2610 g_warning("imap_cmd_select(): invalid RECENT line.\n");
2615 resp_str = search_array_contain_str(argbuf, "UIDVALIDITY");
2617 if (sscanf(resp_str, "OK [UIDVALIDITY %u] ", uid_validity)
2619 g_warning("imap_cmd_select(): invalid UIDVALIDITY line.\n");
2624 resp_str = search_array_contain_str(argbuf, "UNSEEN");
2626 if (sscanf(resp_str, "OK [UNSEEN %d] ", unseen) != 1) {
2627 g_warning("imap_cmd_select(): invalid UNSEEN line.\n");
2633 ptr_array_free_strings(argbuf);
2634 g_ptr_array_free(argbuf, TRUE);
2639 static gint imap_cmd_select(IMAPSession *session, const gchar *folder,
2640 gint *exists, gint *recent, gint *unseen,
2641 guint32 *uid_validity)
2643 return imap_cmd_do_select(session, folder, FALSE,
2644 exists, recent, unseen, uid_validity);
2647 static gint imap_cmd_examine(IMAPSession *session, const gchar *folder,
2648 gint *exists, gint *recent, gint *unseen,
2649 guint32 *uid_validity)
2651 return imap_cmd_do_select(session, folder, TRUE,
2652 exists, recent, unseen, uid_validity);
2657 static gint imap_cmd_create(IMAPSession *session, const gchar *folder)
2661 QUOTE_IF_REQUIRED(folder_, folder);
2662 imap_gen_send(session, "CREATE %s", folder_);
2664 return imap_cmd_ok(session, NULL);
2667 static gint imap_cmd_rename(IMAPSession *session, const gchar *old_folder,
2668 const gchar *new_folder)
2670 gchar *old_folder_, *new_folder_;
2672 QUOTE_IF_REQUIRED(old_folder_, old_folder);
2673 QUOTE_IF_REQUIRED(new_folder_, new_folder);
2674 imap_gen_send(session, "RENAME %s %s", old_folder_, new_folder_);
2676 return imap_cmd_ok(session, NULL);
2679 static gint imap_cmd_delete(IMAPSession *session, const gchar *folder)
2683 QUOTE_IF_REQUIRED(folder_, folder);
2684 imap_gen_send(session, "DELETE %s", folder_);
2686 return imap_cmd_ok(session, NULL);
2689 static gint imap_cmd_search(IMAPSession *session, const gchar *criteria, GSList **list)
2695 g_return_val_if_fail(criteria != NULL, IMAP_ERROR);
2696 g_return_val_if_fail(list != NULL, IMAP_ERROR);
2700 argbuf = g_ptr_array_new();
2701 imap_gen_send(session, "UID SEARCH %s", criteria);
2703 ok = imap_cmd_ok(session, argbuf);
2704 if (ok != IMAP_SUCCESS) {
2705 ptr_array_free_strings(argbuf);
2706 g_ptr_array_free(argbuf, TRUE);
2710 if ((uidlist = search_array_str(argbuf, "SEARCH ")) != NULL) {
2711 gchar **strlist, **p;
2713 strlist = g_strsplit(uidlist + 7, " ", 0);
2714 for (p = strlist; *p != NULL; ++p) {
2717 if (sscanf(*p, "%d", &msgnum) == 1)
2718 *list = g_slist_append(*list, GINT_TO_POINTER(msgnum));
2720 g_strfreev(strlist);
2722 ptr_array_free_strings(argbuf);
2723 g_ptr_array_free(argbuf, TRUE);
2725 return IMAP_SUCCESS;
2728 static gint imap_cmd_fetch(IMAPSession *session, guint32 uid, const gchar *filename)
2736 g_return_val_if_fail(filename != NULL, IMAP_ERROR);
2738 imap_gen_send(session, "UID FETCH %d BODY.PEEK[]", uid);
2740 while ((ok = imap_gen_recv(session, &buf)) == IMAP_SUCCESS) {
2741 if (buf[0] != '*' || buf[1] != ' ') {
2745 if (strstr(buf, "FETCH") != NULL) break;
2748 if (ok != IMAP_SUCCESS) {
2753 #define RETURN_ERROR_IF_FAIL(cond) \
2756 return IMAP_ERROR; \
2759 cur_pos = strchr(buf, '{');
2760 RETURN_ERROR_IF_FAIL(cur_pos != NULL);
2761 cur_pos = strchr_cpy(cur_pos + 1, '}', size_str, sizeof(size_str));
2762 RETURN_ERROR_IF_FAIL(cur_pos != NULL);
2763 size_num = atol(size_str);
2764 RETURN_ERROR_IF_FAIL(size_num > 0);
2766 RETURN_ERROR_IF_FAIL(*cur_pos == '\0');
2768 #undef RETURN_ERROR_IF_FAIL
2772 if (recv_bytes_write_to_file(SESSION(session)->sock, size_num, filename) != 0)
2775 if (imap_gen_recv(session, &buf) != IMAP_SUCCESS) {
2780 if (buf[0] == '\0' || buf[strlen(buf) - 1] != ')') {
2786 ok = imap_cmd_ok(session, NULL);
2791 static gint imap_cmd_append(IMAPSession *session, const gchar *destfolder,
2792 const gchar *file, IMAPFlags flags, gint32 *new_uid)
2799 gchar buf[BUFFSIZE];
2804 g_return_val_if_fail(file != NULL, IMAP_ERROR);
2806 size = get_file_size_as_crlf(file);
2807 if ((fp = fopen(file, "rb")) == NULL) {
2808 FILE_OP_ERROR(file, "fopen");
2811 QUOTE_IF_REQUIRED(destfolder_, destfolder);
2812 flag_str = imap_get_flag_str(flags);
2813 imap_gen_send(session, "APPEND %s (%s) {%d}",
2814 destfolder_, flag_str, size);
2817 ok = imap_gen_recv(session, &ret);
2818 if (ok != IMAP_SUCCESS || ret[0] != '+' || ret[1] != ' ') {
2819 log_warning(_("can't append %s to %s\n"), file, destfolder_);
2826 log_print("IMAP4> %s\n", _("(sending file...)"));
2828 while (fgets(buf, sizeof(buf), fp) != NULL) {
2830 if (sock_puts(SESSION(session)->sock, buf) < 0) {
2837 FILE_OP_ERROR(file, "fgets");
2842 sock_puts(SESSION(session)->sock, "");
2846 reply = g_ptr_array_new();
2849 ok = imap_cmd_ok(session, reply);
2850 if (ok != IMAP_SUCCESS)
2851 log_warning(_("can't append message to %s\n"), destfolder_);
2853 (new_uid != NULL) &&
2854 (imap_has_capability(session, "UIDPLUS") && reply->len > 0) &&
2855 ((okmsginfo = g_ptr_array_index(reply, reply->len - 1)) != NULL) &&
2856 (sscanf(okmsginfo, "%*u OK [APPENDUID %*u %u]", &newuid) == 1)) {
2860 ptr_array_free_strings(reply);
2861 g_ptr_array_free(reply, TRUE);
2866 static gint imap_cmd_copy(IMAPSession * session,
2868 const gchar * destfolder, gint32 * new_uid)
2871 gint32 olduid, newuid;
2876 g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
2877 g_return_val_if_fail(session != NULL, IMAP_ERROR);
2878 g_return_val_if_fail(new_uid != NULL, IMAP_ERROR);
2880 QUOTE_IF_REQUIRED(destfolder_, destfolder);
2881 imap_gen_send(session, "UID COPY %d %s", msgnum, destfolder_);
2883 reply = g_ptr_array_new();
2886 ok = imap_cmd_ok(session, reply);
2887 if (ok != IMAP_SUCCESS)
2888 log_warning(_("can't copy %d to %s\n"), msgnum, destfolder_);
2889 else if (imap_has_capability(session, "UIDPLUS") && reply->len > 0)
2890 if ((okmsginfo = g_ptr_array_index(reply, reply->len - 1)) != NULL &&
2891 sscanf(okmsginfo, "%*u OK [COPYUID %*u %u %u]", &olduid, &newuid) == 2 &&
2895 ptr_array_free_strings(reply);
2896 g_ptr_array_free(reply, TRUE);
2900 gint imap_cmd_envelope(IMAPSession *session, IMAPSet set)
2902 static GString *header_fields = NULL;
2904 if (header_fields == NULL) {
2905 const HeaderEntry *headers, *elem;
2907 headers = procheader_get_headernames(FALSE);
2908 header_fields = g_string_new("");
2910 for (elem = headers; elem->name != NULL; ++elem) {
2911 gint namelen = strlen(elem->name);
2913 /* Header fields ending with space are not rfc822 headers */
2914 if (elem->name[namelen - 1] == ' ')
2917 /* strip : at the of header field */
2918 if(elem->name[namelen - 1] == ':')
2924 g_string_sprintfa(header_fields, "%s%.*s",
2925 header_fields->str[0] != '\0' ? " " : "",
2926 namelen, elem->name);
2931 (session, "UID FETCH %s (UID FLAGS RFC822.SIZE BODY.PEEK[HEADER.FIELDS (%s)])",
2932 set, header_fields->str);
2934 return IMAP_SUCCESS;
2937 static gint imap_cmd_store(IMAPSession *session, IMAPSet set,
2942 imap_gen_send(session, "UID STORE %s %s",
2945 if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS) {
2946 log_warning(_("error while imap command: STORE %s %s\n"),
2951 return IMAP_SUCCESS;
2954 static gint imap_cmd_expunge(IMAPSession *session)
2958 imap_gen_send(session, "EXPUNGE");
2959 if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS) {
2960 log_warning(_("error while imap command: EXPUNGE\n"));
2964 return IMAP_SUCCESS;
2967 static gint imap_cmd_close(IMAPSession *session)
2971 imap_gen_send(session, "CLOSE");
2972 if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS)
2973 log_warning(_("error while imap command: CLOSE\n"));
2978 static gint imap_cmd_ok(IMAPSession *session, GPtrArray *argbuf)
2980 gint ok = IMAP_SUCCESS;
2985 while ((ok = imap_gen_recv(session, &buf))
2987 // make sure data is long enough for any substring of buf
2988 data = alloca(strlen(buf) + 1);
2990 // untagged line read
2991 if (buf[0] == '*' && buf[1] == ' ') {
2994 g_ptr_array_add(argbuf, g_strdup(buf + 2));
2996 if (sscanf(buf + 2, "%d %s", &num, data) >= 2) {
2997 if (!strcmp(data, "EXISTS")) {
2998 session->exists = num;
2999 session->folder_content_changed = TRUE;
3002 if(!strcmp(data, "EXPUNGE")) {
3004 session->folder_content_changed = TRUE;
3007 // tagged line with correct tag and OK response found
3008 } else if ((sscanf(buf, "%d %s", &cmd_num, data) >= 2) &&
3009 (cmd_num == session->cmd_count) &&
3010 !strcmp(data, "OK")) {
3012 g_ptr_array_add(argbuf, g_strdup(buf));
3026 static void imap_gen_send(IMAPSession *session, const gchar *format, ...)
3033 va_start(args, format);
3034 tmp = g_strdup_vprintf(format, args);
3037 session->cmd_count++;
3039 buf = g_strdup_printf("%d %s\r\n", session->cmd_count, tmp);
3040 if (!strncasecmp(tmp, "LOGIN ", 6) && (p = strchr(tmp + 6, ' '))) {
3042 log_print("IMAP4> %d %s ********\n", session->cmd_count, tmp);
3044 log_print("IMAP4> %d %s\n", session->cmd_count, tmp);
3046 sock_write_all(SESSION(session)->sock, buf, strlen(buf));
3051 static gint imap_gen_recv(IMAPSession *session, gchar **ret)
3053 if ((*ret = sock_getline(SESSION(session)->sock)) == NULL)
3058 log_print("IMAP4< %s\n", *ret);
3060 return IMAP_SUCCESS;
3064 /* misc utility functions */
3066 static gchar *strchr_cpy(const gchar *src, gchar ch, gchar *dest, gint len)
3071 tmp = strchr(src, ch);
3075 memcpy(dest, src, MIN(tmp - src, len - 1));
3076 dest[MIN(tmp - src, len - 1)] = '\0';
3081 static gchar *get_quoted(const gchar *src, gchar ch, gchar *dest, gint len)
3083 const gchar *p = src;
3086 g_return_val_if_fail(*p == ch, NULL);
3091 while (*p != '\0' && *p != ch) {
3093 if (*p == '\\' && *(p + 1) != '\0')
3102 return (gchar *)(*p == ch ? p + 1 : p);
3105 static gchar *search_array_contain_str(GPtrArray *array, const gchar *str)
3109 for (i = 0; i < array->len; i++) {
3112 tmp = g_ptr_array_index(array, i);
3113 if (strstr(tmp, str) != NULL)
3120 static gchar *search_array_str(GPtrArray *array, const gchar *str)
3127 for (i = 0; i < array->len; i++) {
3130 tmp = g_ptr_array_index(array, i);
3131 if (!strncmp(tmp, str, len))
3138 static void imap_path_separator_subst(gchar *str, gchar separator)
3141 gboolean in_escape = FALSE;
3143 if (!separator || separator == '/') return;
3145 for (p = str; *p != '\0'; p++) {
3146 if (*p == '/' && !in_escape)
3148 else if (*p == '&' && *(p + 1) != '-' && !in_escape)
3150 else if (*p == '-' && in_escape)
3155 static gchar *imap_modified_utf7_to_locale(const gchar *mutf7_str)
3158 const gchar *from_p;
3161 to = g_malloc(strlen(mutf7_str) + 1);
3164 for (from_p = mutf7_str; *from_p != '\0'; from_p++) {
3165 if (*from_p == '&' && *(from_p + 1) == '-') {
3175 static iconv_t cd = (iconv_t)-1;
3176 static gboolean iconv_ok = TRUE;
3179 size_t norm_utf7_len;
3181 gchar *to_str, *to_p;
3183 gboolean in_escape = FALSE;
3185 if (!iconv_ok) return g_strdup(mutf7_str);
3187 if (cd == (iconv_t)-1) {
3188 cd = iconv_open(conv_get_current_charset_str(), "UTF-7");
3189 if (cd == (iconv_t)-1) {
3190 g_warning("iconv cannot convert UTF-7 to %s\n",
3191 conv_get_current_charset_str());
3193 return g_strdup(mutf7_str);
3197 norm_utf7 = g_string_new(NULL);
3199 for (p = mutf7_str; *p != '\0'; p++) {
3200 /* replace: '&' -> '+',
3202 escaped ',' -> '/' */
3203 if (!in_escape && *p == '&') {
3204 if (*(p + 1) != '-') {
3205 g_string_append_c(norm_utf7, '+');
3208 g_string_append_c(norm_utf7, '&');
3211 } else if (in_escape && *p == ',') {
3212 g_string_append_c(norm_utf7, '/');
3213 } else if (in_escape && *p == '-') {
3214 g_string_append_c(norm_utf7, '-');
3217 g_string_append_c(norm_utf7, *p);
3221 norm_utf7_p = norm_utf7->str;
3222 norm_utf7_len = norm_utf7->len;
3223 to_len = strlen(mutf7_str) * 5;
3224 to_p = to_str = g_malloc(to_len + 1);
3226 if (iconv(cd, (ICONV_CONST gchar **)&norm_utf7_p, &norm_utf7_len,
3227 &to_p, &to_len) == -1) {
3228 g_warning(_("iconv cannot convert UTF-7 to %s\n"),
3229 conv_get_current_charset_str());
3230 g_string_free(norm_utf7, TRUE);
3232 return g_strdup(mutf7_str);
3235 /* second iconv() call for flushing */
3236 iconv(cd, NULL, NULL, &to_p, &to_len);
3237 g_string_free(norm_utf7, TRUE);
3241 #endif /* !HAVE_ICONV */
3244 static gchar *imap_locale_to_modified_utf7(const gchar *from)
3247 const gchar *from_p;
3250 to = g_malloc(strlen(from) * 2 + 1);
3253 for (from_p = from; *from_p != '\0'; from_p++) {
3254 if (*from_p == '&') {
3264 static iconv_t cd = (iconv_t)-1;
3265 static gboolean iconv_ok = TRUE;
3266 gchar *norm_utf7, *norm_utf7_p;
3267 size_t from_len, norm_utf7_len;
3269 gchar *from_tmp, *to, *p;
3270 gboolean in_escape = FALSE;
3272 if (!iconv_ok) return g_strdup(from);
3274 if (cd == (iconv_t)-1) {
3275 cd = iconv_open("UTF-7", conv_get_current_charset_str());
3276 if (cd == (iconv_t)-1) {
3277 g_warning("iconv cannot convert %s to UTF-7\n",
3278 conv_get_current_charset_str());
3280 return g_strdup(from);
3284 Xstrdup_a(from_tmp, from, return g_strdup(from));
3285 from_len = strlen(from);
3286 norm_utf7_len = from_len * 5;
3287 Xalloca(norm_utf7, norm_utf7_len + 1, return g_strdup(from));
3288 norm_utf7_p = norm_utf7;
3290 #define IS_PRINT(ch) (isprint(ch) && isascii(ch))
3292 while (from_len > 0) {
3293 if (*from_tmp == '+') {
3294 *norm_utf7_p++ = '+';
3295 *norm_utf7_p++ = '-';
3299 } else if (IS_PRINT(*from_tmp)) {
3300 /* printable ascii char */
3301 *norm_utf7_p = *from_tmp;
3307 size_t mb_len = 0, conv_len = 0;
3309 /* unprintable char: convert to UTF-7 */
3311 while (!IS_PRINT(*p) && conv_len < from_len) {
3312 mb_len = mblen(p, MB_LEN_MAX);
3314 g_warning("wrong multibyte sequence\n");
3315 return g_strdup(from);
3321 from_len -= conv_len;
3322 if (iconv(cd, (ICONV_CONST gchar **)&from_tmp,
3324 &norm_utf7_p, &norm_utf7_len) == -1) {
3325 g_warning("iconv cannot convert %s to UTF-7\n",
3326 conv_get_current_charset_str());
3327 return g_strdup(from);
3330 /* second iconv() call for flushing */
3331 iconv(cd, NULL, NULL, &norm_utf7_p, &norm_utf7_len);
3337 *norm_utf7_p = '\0';
3338 to_str = g_string_new(NULL);
3339 for (p = norm_utf7; p < norm_utf7_p; p++) {
3340 /* replace: '&' -> "&-",
3343 BASE64 '/' -> ',' */
3344 if (!in_escape && *p == '&') {
3345 g_string_append(to_str, "&-");
3346 } else if (!in_escape && *p == '+') {
3347 if (*(p + 1) == '-') {
3348 g_string_append_c(to_str, '+');
3351 g_string_append_c(to_str, '&');
3354 } else if (in_escape && *p == '/') {
3355 g_string_append_c(to_str, ',');
3356 } else if (in_escape && *p == '-') {
3357 g_string_append_c(to_str, '-');
3360 g_string_append_c(to_str, *p);
3366 g_string_append_c(to_str, '-');
3370 g_string_free(to_str, FALSE);
3373 #endif /* !HAVE_ICONV */
3376 static gboolean imap_rename_folder_func(GNode *node, gpointer data)
3378 FolderItem *item = node->data;
3379 gchar **paths = data;
3380 const gchar *oldpath = paths[0];
3381 const gchar *newpath = paths[1];
3383 gchar *new_itempath;
3386 oldpathlen = strlen(oldpath);
3387 if (strncmp(oldpath, item->path, oldpathlen) != 0) {
3388 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
3392 base = item->path + oldpathlen;
3393 while (*base == G_DIR_SEPARATOR) base++;
3395 new_itempath = g_strdup(newpath);
3397 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
3400 item->path = new_itempath;
3405 static gint get_list_of_uids(Folder *folder, IMAPFolderItem *item, GSList **msgnum_list)
3407 gint ok, nummsgs = 0, lastuid_old;
3408 IMAPSession *session;
3409 GSList *uidlist, *elem;
3412 session = imap_session_get(folder);
3413 g_return_val_if_fail(session != NULL, -1);
3415 ok = imap_select(session, IMAP_FOLDER(folder), item->item.path,
3416 NULL, NULL, NULL, NULL);
3417 if (ok != IMAP_SUCCESS)
3420 cmd_buf = g_strdup_printf("UID %d:*", item->lastuid + 1);
3421 ok = imap_cmd_search(session, cmd_buf, &uidlist);
3424 if (ok == IMAP_SOCKET) {
3425 session_destroy((Session *)session);
3426 ((RemoteFolder *)folder)->session = NULL;
3430 if (ok != IMAP_SUCCESS) {
3434 argbuf = g_ptr_array_new();
3436 cmd_buf = g_strdup_printf("UID FETCH %d:* (UID)", item->lastuid + 1);
3437 imap_gen_send(session, cmd_buf);
3439 ok = imap_cmd_ok(session, argbuf);
3440 if (ok != IMAP_SUCCESS) {
3441 ptr_array_free_strings(argbuf);
3442 g_ptr_array_free(argbuf, TRUE);
3446 for(i = 0; i < argbuf->len; i++) {
3449 if((ret = sscanf(g_ptr_array_index(argbuf, i),
3450 "%*d FETCH (UID %d)", &msgnum)) == 1)
3451 uidlist = g_slist_prepend(uidlist, GINT_TO_POINTER(msgnum));
3453 ptr_array_free_strings(argbuf);
3454 g_ptr_array_free(argbuf, TRUE);
3457 lastuid_old = item->lastuid;
3458 *msgnum_list = g_slist_copy(item->uid_list);
3459 nummsgs = g_slist_length(*msgnum_list);
3460 debug_print("Got %d uids from cache\n", g_slist_length(item->uid_list));
3462 for (elem = uidlist; elem != NULL; elem = g_slist_next(elem)) {
3465 msgnum = GPOINTER_TO_INT(elem->data);
3466 if (msgnum > lastuid_old) {
3467 *msgnum_list = g_slist_prepend(*msgnum_list, GINT_TO_POINTER(msgnum));
3468 item->uid_list = g_slist_prepend(item->uid_list, GINT_TO_POINTER(msgnum));
3471 if(msgnum > item->lastuid)
3472 item->lastuid = msgnum;
3475 g_slist_free(uidlist);
3480 gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list)
3482 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3483 IMAPSession *session;
3484 gint ok, nummsgs = 0, exists, recent, unseen, uid_val, uid_next;
3487 gboolean selected_folder;
3489 g_return_val_if_fail(folder != NULL, -1);
3490 g_return_val_if_fail(item != NULL, -1);
3491 g_return_val_if_fail(item->item.path != NULL, -1);
3492 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3493 g_return_val_if_fail(folder->account != NULL, -1);
3495 session = imap_session_get(folder);
3496 g_return_val_if_fail(session != NULL, -1);
3498 selected_folder = (session->mbox != NULL) &&
3499 (!strcmp(session->mbox, item->item.path));
3500 if (selected_folder) {
3501 ok = imap_cmd_noop(session);
3502 if (ok != IMAP_SUCCESS)
3504 exists = session->exists;
3506 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
3507 &exists, &recent, &uid_next, &uid_val, &unseen);
3508 if (ok != IMAP_SUCCESS)
3512 /* If old uid_next matches new uid_next we can be sure no message
3513 was added to the folder */
3514 if (( selected_folder && !session->folder_content_changed) ||
3515 (!selected_folder && uid_next == item->uid_next)) {
3516 nummsgs = g_slist_length(item->uid_list);
3518 /* If number of messages is still the same we
3519 know our caches message numbers are still valid,
3520 otherwise if the number of messages has decrease
3521 we discard our cache to start a new scan to find
3522 out which numbers have been removed */
3523 if (exists == nummsgs) {
3524 *msgnum_list = g_slist_copy(item->uid_list);
3526 } else if (exists < nummsgs) {
3527 debug_print("Freeing imap uid cache");
3529 g_slist_free(item->uid_list);
3530 item->uid_list = NULL;
3533 if (!selected_folder)
3534 item->uid_next = uid_next;
3537 *msgnum_list = NULL;
3541 nummsgs = get_list_of_uids(folder, item, &uidlist);
3543 if (nummsgs != exists) {
3544 /* Cache contains more messages then folder, we have cached
3545 an old UID of a message that was removed and new messages
3546 have been added too, otherwise the uid_next check would
3548 debug_print("Freeing imap uid cache");
3550 g_slist_free(item->uid_list);
3551 item->uid_list = NULL;
3553 g_slist_free(*msgnum_list);
3555 nummsgs = get_list_of_uids(folder, item, &uidlist);
3558 *msgnum_list = uidlist;
3560 dir = folder_item_get_path((FolderItem *)item);
3561 debug_print("removing old messages from %s\n", dir);
3562 remove_numbered_files_not_in_list(dir, *msgnum_list);
3568 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item)
3573 flags.perm_flags = MSG_NEW|MSG_UNREAD;
3574 flags.tmp_flags = 0;
3576 g_return_val_if_fail(item != NULL, NULL);
3577 g_return_val_if_fail(file != NULL, NULL);
3579 if (item->stype == F_QUEUE) {
3580 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
3581 } else if (item->stype == F_DRAFT) {
3582 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
3585 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3586 if (!msginfo) return NULL;
3588 msginfo->folder = item;
3593 GSList *imap_get_msginfos(Folder *folder, FolderItem *item, GSList *msgnum_list)
3595 IMAPSession *session;
3596 MsgInfoList *ret = NULL;
3599 g_return_val_if_fail(folder != NULL, NULL);
3600 g_return_val_if_fail(item != NULL, NULL);
3601 g_return_val_if_fail(msgnum_list != NULL, NULL);
3603 session = imap_session_get(folder);
3604 g_return_val_if_fail(session != NULL, NULL);
3606 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3607 NULL, NULL, NULL, NULL);
3608 if (ok != IMAP_SUCCESS)
3611 if (!(item->stype == F_QUEUE || item->stype == F_DRAFT)) {
3612 ret = g_slist_concat(ret,
3613 imap_get_uncached_messages(
3614 session, item, msgnum_list));
3616 MsgNumberList *sorted_list, *elem;
3617 gint startnum, lastnum;
3619 sorted_list = g_slist_sort(g_slist_copy(msgnum_list), g_int_compare);
3621 startnum = lastnum = GPOINTER_TO_INT(sorted_list->data);
3623 for (elem = sorted_list;; elem = g_slist_next(elem)) {
3627 num = GPOINTER_TO_INT(elem->data);
3629 if (num > lastnum + 1 || elem == NULL) {
3631 for (i = startnum; i <= lastnum; ++i) {
3634 file = imap_fetch_msg(folder, item, i);
3636 MsgInfo *msginfo = imap_parse_msg(file, item);
3637 if (msginfo != NULL) {
3638 msginfo->msgnum = i;
3639 ret = g_slist_append(ret, msginfo);
3653 g_slist_free(sorted_list);
3659 MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid)
3661 MsgInfo *msginfo = NULL;
3662 MsgInfoList *msginfolist;
3663 MsgNumberList numlist;
3665 numlist.next = NULL;
3666 numlist.data = GINT_TO_POINTER(uid);
3668 msginfolist = imap_get_msginfos(folder, item, &numlist);
3669 if (msginfolist != NULL) {
3670 msginfo = msginfolist->data;
3671 g_slist_free(msginfolist);
3677 gboolean imap_check_msgnum_validity(Folder *folder, FolderItem *_item)
3679 IMAPSession *session;
3680 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3681 gint ok, exists = 0, recent = 0, unseen = 0;
3682 guint32 uid_next, uid_validity = 0;
3684 g_return_val_if_fail(folder != NULL, FALSE);
3685 g_return_val_if_fail(item != NULL, FALSE);
3686 g_return_val_if_fail(item->item.folder != NULL, FALSE);
3687 g_return_val_if_fail(FOLDER_CLASS(item->item.folder) == &imap_class, FALSE);
3689 session = imap_session_get(folder);
3690 g_return_val_if_fail(session != NULL, FALSE);
3692 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
3693 &exists, &recent, &uid_next, &uid_validity, &unseen);
3694 if (ok != IMAP_SUCCESS)
3697 if(item->item.mtime == uid_validity)
3700 debug_print("Freeing imap uid cache\n");
3702 g_slist_free(item->uid_list);
3703 item->uid_list = NULL;
3705 item->item.mtime = uid_validity;
3707 imap_delete_all_cached_messages((FolderItem *)item);
3712 void imap_change_flags(Folder *folder, FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
3714 IMAPSession *session;
3715 IMAPFlags flags_set = 0, flags_unset = 0;
3716 gint ok = IMAP_SUCCESS;
3717 MsgNumberList numlist;
3719 g_return_if_fail(folder != NULL);
3720 g_return_if_fail(folder->klass == &imap_class);
3721 g_return_if_fail(item != NULL);
3722 g_return_if_fail(item->folder == folder);
3723 g_return_if_fail(msginfo != NULL);
3724 g_return_if_fail(msginfo->folder == item);
3726 session = imap_session_get(folder);
3727 if (!session) return;
3729 if ((ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
3730 NULL, NULL, NULL, NULL)) != IMAP_SUCCESS)
3733 if (!MSG_IS_MARKED(msginfo->flags) && (newflags & MSG_MARKED))
3734 flags_set |= IMAP_FLAG_FLAGGED;
3735 if ( MSG_IS_MARKED(msginfo->flags) && !(newflags & MSG_MARKED))
3736 flags_unset |= IMAP_FLAG_FLAGGED;
3738 if (!MSG_IS_UNREAD(msginfo->flags) && (newflags & MSG_UNREAD))
3739 flags_unset |= IMAP_FLAG_SEEN;
3740 if ( MSG_IS_UNREAD(msginfo->flags) && !(newflags & MSG_UNREAD))
3741 flags_set |= IMAP_FLAG_SEEN;
3743 if (!MSG_IS_REPLIED(msginfo->flags) && (newflags & MSG_REPLIED))
3744 flags_set |= IMAP_FLAG_ANSWERED;
3745 if ( MSG_IS_REPLIED(msginfo->flags) && !(newflags & MSG_REPLIED))
3746 flags_set |= IMAP_FLAG_ANSWERED;
3748 numlist.next = NULL;
3749 numlist.data = GINT_TO_POINTER(msginfo->msgnum);
3752 ok = imap_set_message_flags(session, &numlist, flags_set, TRUE);
3753 if (ok != IMAP_SUCCESS) return;
3757 ok = imap_set_message_flags(session, &numlist, flags_unset, FALSE);
3758 if (ok != IMAP_SUCCESS) return;
3761 msginfo->flags.perm_flags = newflags;