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
109 IMAP_FLAG_SEEN = 1 << 0,
110 IMAP_FLAG_ANSWERED = 1 << 1,
111 IMAP_FLAG_FLAGGED = 1 << 2,
112 IMAP_FLAG_DELETED = 1 << 3,
113 IMAP_FLAG_DRAFT = 1 << 4
116 #define IMAP_IS_SEEN(flags) ((flags & IMAP_FLAG_SEEN) != 0)
117 #define IMAP_IS_ANSWERED(flags) ((flags & IMAP_FLAG_ANSWERED) != 0)
118 #define IMAP_IS_FLAGGED(flags) ((flags & IMAP_FLAG_FLAGGED) != 0)
119 #define IMAP_IS_DELETED(flags) ((flags & IMAP_FLAG_DELETED) != 0)
120 #define IMAP_IS_DRAFT(flags) ((flags & IMAP_FLAG_DRAFT) != 0)
123 #define IMAP4_PORT 143
125 #define IMAPS_PORT 993
128 #define IMAP_CMD_LIMIT 1000
130 #define QUOTE_IF_REQUIRED(out, str) \
132 if (*str != '"' && strpbrk(str, " \t(){}%*") != NULL) { \
136 len = strlen(str) + 3; \
137 Xalloca(__tmp, len, return IMAP_ERROR); \
138 g_snprintf(__tmp, len, "\"%s\"", str); \
141 Xstrdup_a(out, str, return IMAP_ERROR); \
145 typedef gchar * IMAPSet;
147 struct _IMAPFolderItem
156 static Folder *imap_folder_new(const gchar * name, const gchar * path);
157 static void imap_folder_destroy(Folder * folder);
159 static IMAPSession *imap_session_new(const PrefsAccount * account);
160 static void imap_session_authenticate(IMAPSession * session,
161 const PrefsAccount * account);
162 static void imap_session_destroy(Session * session);
164 static gchar *imap_fetch_msg(Folder * folder, FolderItem * item, gint uid);
165 static gint imap_add_msg(Folder * folder,
167 const gchar * file, MsgFlags * flags);
168 static gint imap_add_msgs(Folder * folder, FolderItem * dest,
170 GRelation *relation);
172 static gint imap_copy_msg(Folder * folder,
173 FolderItem * dest, MsgInfo * msginfo);
174 static gint imap_copy_msgs(Folder *folder, FolderItem *dest,
175 MsgInfoList *msglist, GRelation *relation);
177 static gint imap_remove_msg(Folder * folder, FolderItem * item, gint uid);
178 static gint imap_remove_all_msg(Folder * folder, FolderItem * item);
180 static gboolean imap_is_msg_changed(Folder * folder,
181 FolderItem * item, MsgInfo * msginfo);
183 static gint imap_close(Folder * folder, FolderItem * item);
185 static void imap_scan_tree(Folder * folder);
187 static gint imap_create_tree(Folder * folder);
189 static FolderItem *imap_create_folder(Folder * folder,
192 static gint imap_rename_folder(Folder * folder,
193 FolderItem * item, const gchar * name);
194 static gint imap_remove_folder(Folder * folder, FolderItem * item);
197 static void imap_folder_init (Folder *folder,
201 static FolderItem *imap_folder_item_new (Folder *folder);
202 static void imap_folder_item_destroy (Folder *folder,
205 static IMAPSession *imap_session_get (Folder *folder);
207 static gint imap_auth (IMAPSession *session,
212 static gint imap_scan_tree_recursive (IMAPSession *session,
214 static GSList *imap_parse_list (IMAPFolder *folder,
215 IMAPSession *session,
216 const gchar *real_path,
219 static void imap_create_missing_folders (Folder *folder);
220 static FolderItem *imap_create_special_folder
222 SpecialFolderItemType stype,
225 static gint imap_do_copy_msgs (Folder *folder,
227 MsgInfoList *msglist,
228 GRelation *relation);
230 static void imap_delete_all_cached_messages (FolderItem *item);
233 static SockInfo *imap_open (const gchar *server,
237 static SockInfo *imap_open (const gchar *server,
242 static SockInfo *imap_open_tunnel(const gchar *server,
243 const gchar *tunnelcmd,
246 static SockInfo *imap_open_tunnel(const gchar *server,
247 const gchar *tunnelcmd);
251 static SockInfo *imap_init_sock(SockInfo *sock, SSLType ssl_type);
253 static SockInfo *imap_init_sock(SockInfo *sock);
256 static gchar *imap_get_flag_str (IMAPFlags flags);
257 static gint imap_set_message_flags (IMAPSession *session,
258 MsgNumberList *numlist,
261 static gint imap_select (IMAPSession *session,
267 guint32 *uid_validity);
268 static gint imap_status (IMAPSession *session,
274 guint32 *uid_validity,
277 static void imap_parse_namespace (IMAPSession *session,
279 static void imap_get_namespace_by_list (IMAPSession *session,
281 static IMAPNameSpace *imap_find_namespace (IMAPFolder *folder,
283 static gchar imap_get_path_separator (IMAPFolder *folder,
285 static gchar *imap_get_real_path (IMAPFolder *folder,
288 static gchar *imap_parse_atom (SockInfo *sock,
293 static MsgFlags imap_parse_flags (const gchar *flag_str);
294 static MsgInfo *imap_parse_envelope (SockInfo *sock,
297 static gint imap_greeting (IMAPSession *session);
298 static gboolean imap_has_capability (IMAPSession *session,
300 void imap_free_capabilities (IMAPSession *session);
302 /* low-level IMAP4rev1 commands */
303 static gint imap_cmd_authenticate
304 (IMAPSession *session,
308 static gint imap_cmd_login (IMAPSession *sock,
311 static gint imap_cmd_logout (IMAPSession *sock);
312 static gint imap_cmd_noop (IMAPSession *sock);
313 static gint imap_cmd_starttls (IMAPSession *sock);
314 static gint imap_cmd_namespace (IMAPSession *sock,
316 static gint imap_cmd_list (IMAPSession *session,
318 const gchar *mailbox,
320 static gint imap_cmd_do_select (IMAPSession *sock,
326 guint32 *uid_validity);
327 static gint imap_cmd_select (IMAPSession *sock,
332 guint32 *uid_validity);
333 static gint imap_cmd_examine (IMAPSession *sock,
338 guint32 *uid_validity);
339 static gint imap_cmd_create (IMAPSession *sock,
340 const gchar *folder);
341 static gint imap_cmd_rename (IMAPSession *sock,
342 const gchar *oldfolder,
343 const gchar *newfolder);
344 static gint imap_cmd_delete (IMAPSession *sock,
345 const gchar *folder);
346 static gint imap_cmd_envelope (IMAPSession *sock,
348 static gint imap_cmd_fetch (IMAPSession *sock,
350 const gchar *filename);
351 static gint imap_cmd_append (IMAPSession *session,
352 const gchar *destfolder,
356 static gint imap_cmd_copy (IMAPSession *session,
357 const gchar *seq_set,
358 const gchar *destfolder,
359 GRelation *uid_mapping);
360 static gint imap_cmd_store (IMAPSession *sock,
363 static gint imap_cmd_expunge (IMAPSession *sock);
364 static gint imap_cmd_close (IMAPSession *session);
366 static gint imap_cmd_ok (IMAPSession *session,
368 static void imap_gen_send (IMAPSession *sock,
369 const gchar *format, ...);
370 static gint imap_gen_recv (IMAPSession *sock,
373 /* misc utility functions */
374 static gchar *strchr_cpy (const gchar *src,
378 static gchar *get_quoted (const gchar *src,
382 static gchar *search_array_contain_str (GPtrArray *array,
384 static gchar *search_array_str (GPtrArray *array,
386 static void imap_path_separator_subst (gchar *str,
389 static gchar *imap_modified_utf7_to_locale (const gchar *mutf7_str);
390 static gchar *imap_locale_to_modified_utf7 (const gchar *from);
392 static GSList *imap_get_seq_set_from_numlist (MsgNumberList *msglist);
393 static GSList *imap_get_seq_set_from_msglist (MsgInfoList *msglist);
394 static void imap_seq_set_free (GSList *seq_list);
396 static gboolean imap_rename_folder_func (GNode *node,
398 static gint imap_get_num_list (Folder *folder,
401 static GSList *imap_get_msginfos (Folder *folder,
403 GSList *msgnum_list);
404 static MsgInfo *imap_get_msginfo (Folder *folder,
407 static gboolean imap_check_msgnum_validity (Folder *folder,
409 static void imap_change_flags (Folder *folder,
412 MsgPermFlags newflags);
413 static gchar *imap_folder_get_path (Folder *folder);
414 static gchar *imap_item_get_path (Folder *folder,
417 FolderClass imap_class =
423 /* Folder functions */
429 /* FolderItem functions */
430 imap_folder_item_new,
431 imap_folder_item_destroy,
441 imap_check_msgnum_validity,
443 /* Message functions */
457 FolderClass *imap_get_class(void)
462 Folder *imap_folder_new(const gchar *name, const gchar *path)
466 folder = (Folder *)g_new0(IMAPFolder, 1);
467 folder->klass = &imap_class;
468 imap_folder_init(folder, name, path);
473 void imap_folder_destroy(Folder *folder)
477 dir = imap_folder_get_path(folder);
478 if (is_dir_exist(dir))
479 remove_dir_recursive(dir);
482 folder_remote_folder_destroy(REMOTE_FOLDER(folder));
485 static void imap_folder_init(Folder *folder, const gchar *name,
488 folder_remote_folder_init((Folder *)folder, name, path);
491 static FolderItem *imap_folder_item_new(Folder *folder)
493 IMAPFolderItem *item;
495 item = g_new0(IMAPFolderItem, 1);
498 item->uid_list = NULL;
500 return (FolderItem *)item;
503 static void imap_folder_item_destroy(Folder *folder, FolderItem *_item)
505 IMAPFolderItem *item = (IMAPFolderItem *)_item;
507 g_return_if_fail(item != NULL);
508 g_slist_free(item->uid_list);
513 static gboolean imap_reset_uid_lists_func(GNode *node, gpointer data)
515 IMAPFolderItem *item = (IMAPFolderItem *)node->data;
519 g_slist_free(item->uid_list);
520 item->uid_list = NULL;
525 static void imap_reset_uid_lists(Folder *folder)
527 if(folder->node == NULL)
530 /* Destroy all uid lists and rest last uid */
531 g_node_traverse(folder->node, G_IN_ORDER, G_TRAVERSE_ALL, -1, imap_reset_uid_lists_func, NULL);
534 static gint imap_auth(IMAPSession *session, const gchar *user, const gchar *pass,
537 if (type == 0 || type == IMAP_AUTH_LOGIN)
538 return imap_cmd_login(session, user, pass);
540 return imap_cmd_authenticate(session, user, pass, type);
543 static IMAPSession *imap_session_get(Folder *folder)
545 RemoteFolder *rfolder = REMOTE_FOLDER(folder);
546 IMAPSession *session = NULL;
548 g_return_val_if_fail(folder != NULL, NULL);
549 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, NULL);
550 g_return_val_if_fail(folder->account != NULL, NULL);
552 /* Make sure we have a session */
553 if (rfolder->session != NULL) {
554 session = IMAP_SESSION(rfolder->session);
556 imap_reset_uid_lists(folder);
557 session = imap_session_new(folder->account);
562 if (SESSION(session)->sock->state == CONN_DISCONNECTED) {
563 debug_print("IMAP server disconnected\n");
564 session_destroy(SESSION(session));
565 imap_reset_uid_lists(folder);
566 session = imap_session_new(folder->account);
569 /* Make sure session is authenticated */
570 if (!IMAP_SESSION(session)->authenticated)
571 imap_session_authenticate(IMAP_SESSION(session), folder->account);
572 if (!IMAP_SESSION(session)->authenticated) {
573 session_destroy(SESSION(session));
574 rfolder->session = NULL;
578 /* Make sure we have parsed the IMAP namespace */
579 imap_parse_namespace(IMAP_SESSION(session),
580 IMAP_FOLDER(folder));
582 /* I think the point of this code is to avoid sending a
583 * keepalive if we've used the session recently and therefore
584 * think it's still alive. Unfortunately, most of the code
585 * does not yet check for errors on the socket, and so if the
586 * connection drops we don't notice until the timeout expires.
587 * A better solution than sending a NOOP every time would be
588 * for every command to be prepared to retry until it is
589 * successfully sent. -- mbp */
590 if (time(NULL) - session->last_access_time > SESSION_TIMEOUT) {
591 /* verify that the session is still alive */
592 if (imap_cmd_noop(session) != IMAP_SUCCESS) {
593 /* Check if this is the first try to establish a
594 connection, if yes we don't try to reconnect */
595 if (rfolder->session == NULL) {
596 log_warning(_("Connecting %s failed"),
597 folder->account->recv_server);
598 session_destroy(SESSION(session));
601 log_warning(_("IMAP4 connection to %s has been"
602 " disconnected. Reconnecting...\n"),
603 folder->account->recv_server);
604 session_destroy(SESSION(session));
605 /* Clear folders session to make imap_session_get create
606 a new session, because of rfolder->session == NULL
607 it will not try to reconnect again and so avoid an
609 rfolder->session = NULL;
610 session = imap_session_get(folder);
615 rfolder->session = SESSION(session);
617 session->last_access_time = time(NULL);
619 return IMAP_SESSION(session);
622 IMAPSession *imap_session_new(const PrefsAccount *account)
624 IMAPSession *session;
629 /* FIXME: IMAP over SSL only... */
632 port = account->set_imapport ? account->imapport
633 : account->ssl_imap == SSL_TUNNEL ? IMAPS_PORT : IMAP4_PORT;
634 ssl_type = account->ssl_imap;
636 port = account->set_imapport ? account->imapport
640 if (account->set_tunnelcmd) {
641 log_message(_("creating tunneled IMAP4 connection\n"));
643 if ((imap_sock = imap_open_tunnel(account->recv_server,
647 if ((imap_sock = imap_open_tunnel(account->recv_server,
648 account->tunnelcmd)) == NULL)
652 g_return_val_if_fail(account->recv_server != NULL, NULL);
654 log_message(_("creating IMAP4 connection to %s:%d ...\n"),
655 account->recv_server, port);
658 if ((imap_sock = imap_open(account->recv_server, port,
661 if ((imap_sock = imap_open(account->recv_server, port)) == NULL)
666 session = g_new0(IMAPSession, 1);
667 session_init(SESSION(session));
668 SESSION(session)->type = SESSION_IMAP;
669 SESSION(session)->server = g_strdup(account->recv_server);
670 SESSION(session)->sock = imap_sock;
672 SESSION(session)->destroy = imap_session_destroy;
674 session->capability = NULL;
676 session->mbox = NULL;
677 session->authenticated = FALSE;
678 session->cmd_count = 0;
680 /* Only need to log in if the connection was not PREAUTH */
681 if (imap_greeting(session) != IMAP_SUCCESS) {
682 session_destroy(SESSION(session));
687 if (account->ssl_imap == SSL_STARTTLS && imap_has_capability(session, "STARTTLS")) {
690 ok = imap_cmd_starttls(session);
691 if (ok != IMAP_SUCCESS) {
692 log_warning(_("Can't start TLS session.\n"));
693 session_destroy(SESSION(session));
696 if (!ssl_init_socket_with_method(SESSION(session)->sock, SSL_METHOD_TLSv1)) {
697 session_destroy(SESSION(session));
701 imap_free_capabilities(session);
702 session->authenticated = FALSE;
703 session->cmd_count = 1;
705 if (imap_greeting(session) != IMAP_SUCCESS) {
706 session_destroy(SESSION(session));
711 log_message("IMAP connection is %s-authenticated\n",
712 (session->authenticated) ? "pre" : "un");
717 void imap_session_authenticate(IMAPSession *session, const PrefsAccount *account)
721 g_return_if_fail(account->userid != NULL);
723 pass = account->passwd;
726 tmp_pass = input_dialog_query_password(account->recv_server, account->userid);
729 Xstrdup_a(pass, tmp_pass, {g_free(tmp_pass); return;});
733 if (imap_auth(session, account->userid, pass, account->imap_auth_type) != IMAP_SUCCESS) {
734 imap_cmd_logout(session);
738 session->authenticated = TRUE;
741 void imap_session_destroy(Session *session)
743 sock_close(session->sock);
744 session->sock = NULL;
746 g_free(IMAP_SESSION(session)->mbox);
747 imap_free_capabilities(IMAP_SESSION(session));
750 gchar *imap_fetch_msg(Folder *folder, FolderItem *item, gint uid)
752 gchar *path, *filename;
753 IMAPSession *session;
756 g_return_val_if_fail(folder != NULL, NULL);
757 g_return_val_if_fail(item != NULL, NULL);
759 path = folder_item_get_path(item);
760 if (!is_dir_exist(path))
762 filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(uid), NULL);
765 if (is_file_exist(filename)) {
766 debug_print("message %d has been already cached.\n", uid);
770 session = imap_session_get(folder);
776 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
777 NULL, NULL, NULL, NULL);
778 if (ok != IMAP_SUCCESS) {
779 g_warning("can't select mailbox %s\n", item->path);
784 debug_print("getting message %d...\n", uid);
785 ok = imap_cmd_fetch(session, (guint32)uid, filename);
787 if (ok != IMAP_SUCCESS) {
788 g_warning("can't fetch message %d\n", uid);
796 gint imap_add_msg(Folder *folder, FolderItem *dest, const gchar *file, MsgFlags *flags)
800 MsgFileInfo fileinfo;
802 g_return_val_if_fail(file != NULL, -1);
804 fileinfo.msginfo = NULL;
805 fileinfo.file = (gchar *)file;
806 fileinfo.flags = flags;
807 file_list.data = &fileinfo;
808 file_list.next = NULL;
810 ret = imap_add_msgs(folder, dest, &file_list, NULL);
814 gint imap_add_msgs(Folder *folder, FolderItem *dest, GSList *file_list,
818 IMAPSession *session;
819 guint32 last_uid = 0;
821 MsgFileInfo *fileinfo;
824 g_return_val_if_fail(folder != NULL, -1);
825 g_return_val_if_fail(dest != NULL, -1);
826 g_return_val_if_fail(file_list != NULL, -1);
828 session = imap_session_get(folder);
829 if (!session) return -1;
831 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
833 for (cur = file_list; cur != NULL; cur = cur->next) {
834 IMAPFlags iflags = 0;
836 fileinfo = (MsgFileInfo *)cur->data;
838 if (fileinfo->flags) {
839 if (MSG_IS_MARKED(*fileinfo->flags))
840 iflags |= IMAP_FLAG_FLAGGED;
841 if (MSG_IS_REPLIED(*fileinfo->flags))
842 iflags |= IMAP_FLAG_ANSWERED;
843 if (!MSG_IS_UNREAD(*fileinfo->flags))
844 iflags |= IMAP_FLAG_SEEN;
847 if (dest->stype == F_OUTBOX ||
848 dest->stype == F_QUEUE ||
849 dest->stype == F_DRAFT ||
850 dest->stype == F_TRASH)
851 iflags |= IMAP_FLAG_SEEN;
853 ok = imap_cmd_append(session, destdir, fileinfo->file, iflags, &newnum);
855 if (ok != IMAP_SUCCESS) {
856 g_warning("can't append message %s\n", fileinfo->file);
861 if (relation != NULL)
862 g_relation_insert(relation, fileinfo->msginfo != NULL ?
863 fileinfo->msginfo : fileinfo,
864 GINT_TO_POINTER(dest->last_num + 1));
865 if (newnum > last_uid)
874 static gint imap_do_copy_msgs(Folder *folder, FolderItem *dest,
875 MsgInfoList *msglist, GRelation *relation)
878 GSList *seq_list, *cur;
880 IMAPSession *session;
881 gint ok = IMAP_SUCCESS;
882 GRelation *uid_mapping;
885 g_return_val_if_fail(folder != NULL, -1);
886 g_return_val_if_fail(dest != NULL, -1);
887 g_return_val_if_fail(msglist != NULL, -1);
889 session = imap_session_get(folder);
890 if (!session) return -1;
892 msginfo = (MsgInfo *)msglist->data;
893 if (msginfo->folder == dest) {
894 g_warning("the src folder is identical to the dest.\n");
898 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
899 seq_list = imap_get_seq_set_from_msglist(msglist);
900 uid_mapping = g_relation_new(2);
901 g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
903 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
904 gchar *seq_set = (gchar *)cur->data;
906 debug_print("Copying message %s%c[%s] to %s ...\n",
907 msginfo->folder->path, G_DIR_SEPARATOR,
910 ok = imap_cmd_copy(session, seq_set, destdir, uid_mapping);
911 if (ok != IMAP_SUCCESS) {
912 g_relation_destroy(uid_mapping);
913 imap_seq_set_free(seq_list);
918 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
919 MsgInfo *msginfo = (MsgInfo *)cur->data;
922 tuples = g_relation_select(uid_mapping,
923 GINT_TO_POINTER(msginfo->msgnum),
925 if (tuples->len > 0) {
926 gint num = GPOINTER_TO_INT(g_tuples_index(tuples, 0, 1));
927 g_relation_insert(relation, msginfo,
928 GPOINTER_TO_INT(num));
932 g_relation_insert(relation, msginfo,
934 g_tuples_destroy(tuples);
937 imap_seq_set_free(seq_list);
941 if (ok == IMAP_SUCCESS)
947 gint imap_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
951 g_return_val_if_fail(msginfo != NULL, -1);
953 msglist.data = msginfo;
956 return imap_copy_msgs(folder, dest, &msglist, NULL);
959 gint imap_copy_msgs(Folder *folder, FolderItem *dest,
960 MsgInfoList *msglist, GRelation *relation)
966 g_return_val_if_fail(folder != NULL, -1);
967 g_return_val_if_fail(dest != NULL, -1);
968 g_return_val_if_fail(msglist != NULL, -1);
970 msginfo = (MsgInfo *)msglist->data;
971 g_return_val_if_fail(msginfo->folder != NULL, -1);
973 if (folder == msginfo->folder->folder)
974 return imap_do_copy_msgs(folder, dest, msglist, relation);
976 file_list = procmsg_get_message_file_list(msglist);
977 g_return_val_if_fail(file_list != NULL, -1);
979 ret = imap_add_msgs(folder, dest, file_list, relation);
981 procmsg_message_file_list_free(file_list);
986 gint imap_remove_msg(Folder *folder, FolderItem *item, gint uid)
989 IMAPSession *session;
991 MsgNumberList numlist;
993 g_return_val_if_fail(folder != NULL, -1);
994 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
995 g_return_val_if_fail(item != NULL, -1);
997 session = imap_session_get(folder);
998 if (!session) return -1;
1000 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
1001 NULL, NULL, NULL, NULL);
1002 if (ok != IMAP_SUCCESS)
1005 numlist.next = NULL;
1006 numlist.data = GINT_TO_POINTER(uid);
1008 ok = imap_set_message_flags
1009 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
1010 &numlist, IMAP_FLAG_DELETED, TRUE);
1011 if (ok != IMAP_SUCCESS) {
1012 log_warning(_("can't set deleted flags: %d\n"), uid);
1016 ok = imap_cmd_expunge(session);
1017 if (ok != IMAP_SUCCESS) {
1018 log_warning(_("can't expunge\n"));
1022 dir = folder_item_get_path(item);
1023 if (is_dir_exist(dir))
1024 remove_numbered_files(dir, uid, uid);
1027 return IMAP_SUCCESS;
1030 gint imap_remove_all_msg(Folder *folder, FolderItem *item)
1032 gint exists, recent, unseen;
1033 guint32 uid_validity;
1035 IMAPSession *session;
1038 g_return_val_if_fail(folder != NULL, -1);
1039 g_return_val_if_fail(item != NULL, -1);
1041 session = imap_session_get(folder);
1042 if (!session) return -1;
1044 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
1045 &exists, &recent, &unseen, &uid_validity);
1046 if (ok != IMAP_SUCCESS)
1049 return IMAP_SUCCESS;
1051 imap_gen_send(session,
1052 "STORE 1:%d +FLAGS.SILENT (\\Deleted)", exists);
1053 ok = imap_cmd_ok(session, NULL);
1054 if (ok != IMAP_SUCCESS) {
1055 log_warning(_("can't set deleted flags: 1:%d\n"), exists);
1059 ok = imap_cmd_expunge(session);
1060 if (ok != IMAP_SUCCESS) {
1061 log_warning(_("can't expunge\n"));
1065 dir = folder_item_get_path(item);
1066 if (is_dir_exist(dir))
1067 remove_all_numbered_files(dir);
1070 return IMAP_SUCCESS;
1073 gboolean imap_is_msg_changed(Folder *folder, FolderItem *item, MsgInfo *msginfo)
1075 /* TODO: properly implement this method */
1079 gint imap_close(Folder *folder, FolderItem *item)
1082 IMAPSession *session;
1084 g_return_val_if_fail(folder != NULL, -1);
1086 session = imap_session_get(folder);
1087 if (!session) return -1;
1089 if (session->mbox) {
1090 ok = imap_cmd_close(session);
1091 if (ok != IMAP_SUCCESS)
1092 log_warning(_("can't close folder\n"));
1094 g_free(session->mbox);
1095 session->mbox = NULL;
1103 void imap_scan_tree(Folder *folder)
1106 IMAPSession *session;
1107 gchar *root_folder = NULL;
1109 g_return_if_fail(folder != NULL);
1110 g_return_if_fail(folder->account != NULL);
1112 session = imap_session_get(folder);
1114 if (!folder->node) {
1115 folder_tree_destroy(folder);
1116 item = folder_item_new(folder, folder->name, NULL);
1117 item->folder = folder;
1118 folder->node = g_node_new(item);
1123 if (folder->account->imap_dir && *folder->account->imap_dir) {
1124 Xstrdup_a(root_folder, folder->account->imap_dir, return);
1125 strtailchomp(root_folder, '/');
1126 debug_print("IMAP root directory: %s\n", root_folder);
1129 item = folder_item_new(folder, folder->name, root_folder);
1130 item->folder = folder;
1131 item->no_select = TRUE;
1132 folder->node = g_node_new(item);
1134 imap_scan_tree_recursive(session, item);
1136 imap_create_missing_folders(folder);
1139 static gint imap_scan_tree_recursive(IMAPSession *session, FolderItem *item)
1142 IMAPFolder *imapfolder;
1143 FolderItem *new_item;
1144 GSList *item_list, *cur;
1146 gchar *wildcard_path;
1150 g_return_val_if_fail(item != NULL, -1);
1151 g_return_val_if_fail(item->folder != NULL, -1);
1152 g_return_val_if_fail(item->no_sub == FALSE, -1);
1154 folder = FOLDER(item->folder);
1155 imapfolder = IMAP_FOLDER(folder);
1157 separator = imap_get_path_separator(imapfolder, item->path);
1159 if (item->folder->ui_func)
1160 item->folder->ui_func(folder, item, folder->ui_func_data);
1163 wildcard[0] = separator;
1166 real_path = imap_get_real_path(imapfolder, item->path);
1170 real_path = g_strdup("");
1173 Xstrcat_a(wildcard_path, real_path, wildcard,
1174 {g_free(real_path); return IMAP_ERROR;});
1175 QUOTE_IF_REQUIRED(wildcard_path, wildcard_path);
1177 imap_gen_send(session, "LIST \"\" %s",
1180 strtailchomp(real_path, separator);
1181 item_list = imap_parse_list(imapfolder, session, real_path, NULL);
1184 for (cur = item_list; cur != NULL; cur = cur->next) {
1185 new_item = cur->data;
1186 if (!strcmp(new_item->path, "INBOX")) {
1187 if (!folder->inbox) {
1188 new_item->stype = F_INBOX;
1189 item->folder->inbox = new_item;
1191 folder_item_destroy(new_item);
1194 } else if (!item->parent || item->stype == F_INBOX) {
1197 base = g_basename(new_item->path);
1199 if (!folder->outbox && !strcasecmp(base, "Sent")) {
1200 new_item->stype = F_OUTBOX;
1201 folder->outbox = new_item;
1202 } else if (!folder->draft && !strcasecmp(base, "Drafts")) {
1203 new_item->stype = F_DRAFT;
1204 folder->draft = new_item;
1205 } else if (!folder->queue && !strcasecmp(base, "Queue")) {
1206 new_item->stype = F_QUEUE;
1207 folder->queue = new_item;
1208 } else if (!folder->trash && !strcasecmp(base, "Trash")) {
1209 new_item->stype = F_TRASH;
1210 folder->trash = new_item;
1213 folder_item_append(item, new_item);
1214 if (new_item->no_sub == FALSE)
1215 imap_scan_tree_recursive(session, new_item);
1218 return IMAP_SUCCESS;
1221 static GSList *imap_parse_list(IMAPFolder *folder, IMAPSession *session,
1222 const gchar *real_path, gchar *separator)
1224 gchar buf[IMAPBUFSIZE];
1226 gchar separator_str[16];
1229 gchar *loc_name, *loc_path;
1230 GSList *item_list = NULL;
1232 FolderItem *new_item;
1234 debug_print("getting list of %s ...\n",
1235 *real_path ? real_path : "\"\"");
1237 str = g_string_new(NULL);
1240 if (sock_gets(SESSION(session)->sock, buf, sizeof(buf)) <= 0) {
1241 log_warning(_("error occurred while getting LIST.\n"));
1245 if (buf[0] != '*' || buf[1] != ' ') {
1246 log_print("IMAP4< %s\n", buf);
1249 debug_print("IMAP4< %s\n", buf);
1251 g_string_assign(str, buf);
1253 if (strncmp(p, "LIST ", 5) != 0) continue;
1256 if (*p != '(') continue;
1258 p = strchr_cpy(p, ')', flags, sizeof(flags));
1260 while (*p == ' ') p++;
1262 p = strchr_cpy(p, ' ', separator_str, sizeof(separator_str));
1264 extract_quote(separator_str, '"');
1265 if (!strcmp(separator_str, "NIL"))
1266 separator_str[0] = '\0';
1268 *separator = separator_str[0];
1271 while (*p == ' ') p++;
1272 if (*p == '{' || *p == '"')
1273 p = imap_parse_atom(SESSION(session)->sock, p,
1274 buf, sizeof(buf), str);
1276 strncpy2(buf, p, sizeof(buf));
1277 strtailchomp(buf, separator_str[0]);
1278 if (buf[0] == '\0') continue;
1279 if (!strcmp(buf, real_path)) continue;
1281 if (separator_str[0] != '\0')
1282 subst_char(buf, separator_str[0], '/');
1283 name = g_basename(buf);
1284 if (name[0] == '.') continue;
1286 loc_name = imap_modified_utf7_to_locale(name);
1287 loc_path = imap_modified_utf7_to_locale(buf);
1288 new_item = folder_item_new(FOLDER(folder), loc_name, loc_path);
1289 if (strcasestr(flags, "\\Noinferiors") != NULL)
1290 new_item->no_sub = TRUE;
1291 if (strcmp(buf, "INBOX") != 0 &&
1292 strcasestr(flags, "\\Noselect") != NULL)
1293 new_item->no_select = TRUE;
1295 item_list = g_slist_append(item_list, new_item);
1297 debug_print("folder %s has been added.\n", loc_path);
1302 g_string_free(str, TRUE);
1307 gint imap_create_tree(Folder *folder)
1309 g_return_val_if_fail(folder != NULL, -1);
1310 g_return_val_if_fail(folder->node != NULL, -1);
1311 g_return_val_if_fail(folder->node->data != NULL, -1);
1312 g_return_val_if_fail(folder->account != NULL, -1);
1314 imap_scan_tree(folder);
1315 imap_create_missing_folders(folder);
1320 static void imap_create_missing_folders(Folder *folder)
1322 g_return_if_fail(folder != NULL);
1325 folder->inbox = imap_create_special_folder
1326 (folder, F_INBOX, "INBOX");
1328 if (!folder->outbox)
1329 folder->outbox = imap_create_special_folder
1330 (folder, F_OUTBOX, "Sent");
1332 folder->draft = imap_create_special_folder
1333 (folder, F_DRAFT, "Drafts");
1335 folder->queue = imap_create_special_folder
1336 (folder, F_QUEUE, "Queue");
1339 folder->trash = imap_create_special_folder
1340 (folder, F_TRASH, "Trash");
1343 static FolderItem *imap_create_special_folder(Folder *folder,
1344 SpecialFolderItemType stype,
1348 FolderItem *new_item;
1350 g_return_val_if_fail(folder != NULL, NULL);
1351 g_return_val_if_fail(folder->node != NULL, NULL);
1352 g_return_val_if_fail(folder->node->data != NULL, NULL);
1353 g_return_val_if_fail(folder->account != NULL, NULL);
1354 g_return_val_if_fail(name != NULL, NULL);
1356 item = FOLDER_ITEM(folder->node->data);
1357 new_item = imap_create_folder(folder, item, name);
1360 g_warning("Can't create '%s'\n", name);
1361 if (!folder->inbox) return NULL;
1363 new_item = imap_create_folder(folder, folder->inbox, name);
1365 g_warning("Can't create '%s' under INBOX\n", name);
1367 new_item->stype = stype;
1369 new_item->stype = stype;
1374 static gchar *imap_folder_get_path(Folder *folder)
1378 g_return_val_if_fail(folder != NULL, NULL);
1379 g_return_val_if_fail(folder->account != NULL, NULL);
1381 folder_path = g_strconcat(get_imap_cache_dir(),
1383 folder->account->recv_server,
1385 folder->account->userid,
1391 static gchar *imap_item_get_path(Folder *folder, FolderItem *item)
1393 gchar *folder_path, *path;
1395 g_return_val_if_fail(folder != NULL, NULL);
1396 g_return_val_if_fail(item != NULL, NULL);
1397 folder_path = imap_folder_get_path(folder);
1399 g_return_val_if_fail(folder_path != NULL, NULL);
1400 if (folder_path[0] == G_DIR_SEPARATOR) {
1402 path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
1405 path = g_strdup(folder_path);
1408 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1409 folder_path, G_DIR_SEPARATOR_S,
1412 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1415 g_free(folder_path);
1420 FolderItem *imap_create_folder(Folder *folder, FolderItem *parent,
1423 gchar *dirpath, *imap_path;
1424 IMAPSession *session;
1425 FolderItem *new_item;
1431 g_return_val_if_fail(folder != NULL, NULL);
1432 g_return_val_if_fail(folder->account != NULL, NULL);
1433 g_return_val_if_fail(parent != NULL, NULL);
1434 g_return_val_if_fail(name != NULL, NULL);
1436 session = imap_session_get(folder);
1437 if (!session) return NULL;
1439 if (!parent->parent && strcmp(name, "INBOX") == 0)
1440 dirpath = g_strdup(name);
1441 else if (parent->path)
1442 dirpath = g_strconcat(parent->path, "/", name, NULL);
1443 else if ((p = strchr(name, '/')) != NULL && *(p + 1) != '\0')
1444 dirpath = g_strdup(name);
1445 else if (folder->account->imap_dir && *folder->account->imap_dir) {
1448 Xstrdup_a(imap_dir, folder->account->imap_dir, return NULL);
1449 strtailchomp(imap_dir, '/');
1450 dirpath = g_strconcat(imap_dir, "/", name, NULL);
1452 dirpath = g_strdup(name);
1454 /* keep trailing directory separator to create a folder that contains
1456 imap_path = imap_locale_to_modified_utf7(dirpath);
1457 strtailchomp(dirpath, '/');
1458 Xstrdup_a(new_name, name, {g_free(dirpath); return NULL;});
1459 strtailchomp(new_name, '/');
1460 separator = imap_get_path_separator(IMAP_FOLDER(folder), imap_path);
1461 imap_path_separator_subst(imap_path, separator);
1462 subst_char(new_name, '/', separator);
1464 if (strcmp(name, "INBOX") != 0) {
1467 gboolean exist = FALSE;
1469 argbuf = g_ptr_array_new();
1470 ok = imap_cmd_list(session, NULL, imap_path,
1472 if (ok != IMAP_SUCCESS) {
1473 log_warning(_("can't create mailbox: LIST failed\n"));
1476 ptr_array_free_strings(argbuf);
1477 g_ptr_array_free(argbuf, TRUE);
1481 for (i = 0; i < argbuf->len; i++) {
1483 str = g_ptr_array_index(argbuf, i);
1484 if (!strncmp(str, "LIST ", 5)) {
1489 ptr_array_free_strings(argbuf);
1490 g_ptr_array_free(argbuf, TRUE);
1493 ok = imap_cmd_create(session, imap_path);
1494 if (ok != IMAP_SUCCESS) {
1495 log_warning(_("can't create mailbox\n"));
1503 new_item = folder_item_new(folder, new_name, dirpath);
1504 folder_item_append(parent, new_item);
1508 dirpath = folder_item_get_path(new_item);
1509 if (!is_dir_exist(dirpath))
1510 make_dir_hier(dirpath);
1516 gint imap_rename_folder(Folder *folder, FolderItem *item, const gchar *name)
1520 gchar *real_oldpath;
1521 gchar *real_newpath;
1524 gchar *old_cache_dir;
1525 gchar *new_cache_dir;
1526 IMAPSession *session;
1529 gint exists, recent, unseen;
1530 guint32 uid_validity;
1532 g_return_val_if_fail(folder != NULL, -1);
1533 g_return_val_if_fail(item != NULL, -1);
1534 g_return_val_if_fail(item->path != NULL, -1);
1535 g_return_val_if_fail(name != NULL, -1);
1537 session = imap_session_get(folder);
1538 if (!session) return -1;
1540 real_oldpath = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1542 g_free(session->mbox);
1543 session->mbox = NULL;
1544 ok = imap_cmd_examine(session, "INBOX",
1545 &exists, &recent, &unseen, &uid_validity);
1546 if (ok != IMAP_SUCCESS) {
1547 g_free(real_oldpath);
1551 separator = imap_get_path_separator(IMAP_FOLDER(folder), item->path);
1552 if (strchr(item->path, G_DIR_SEPARATOR)) {
1553 dirpath = g_dirname(item->path);
1554 newpath = g_strconcat(dirpath, G_DIR_SEPARATOR_S, name, NULL);
1557 newpath = g_strdup(name);
1559 real_newpath = imap_locale_to_modified_utf7(newpath);
1560 imap_path_separator_subst(real_newpath, separator);
1562 ok = imap_cmd_rename(session, real_oldpath, real_newpath);
1563 if (ok != IMAP_SUCCESS) {
1564 log_warning(_("can't rename mailbox: %s to %s\n"),
1565 real_oldpath, real_newpath);
1566 g_free(real_oldpath);
1568 g_free(real_newpath);
1573 item->name = g_strdup(name);
1575 old_cache_dir = folder_item_get_path(item);
1577 node = g_node_find(item->folder->node, G_PRE_ORDER, G_TRAVERSE_ALL,
1579 paths[0] = g_strdup(item->path);
1581 g_node_traverse(node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1582 imap_rename_folder_func, paths);
1584 if (is_dir_exist(old_cache_dir)) {
1585 new_cache_dir = folder_item_get_path(item);
1586 if (rename(old_cache_dir, new_cache_dir) < 0) {
1587 FILE_OP_ERROR(old_cache_dir, "rename");
1589 g_free(new_cache_dir);
1592 g_free(old_cache_dir);
1595 g_free(real_oldpath);
1596 g_free(real_newpath);
1601 gint imap_remove_folder(Folder *folder, FolderItem *item)
1604 IMAPSession *session;
1607 gint exists, recent, unseen;
1608 guint32 uid_validity;
1610 g_return_val_if_fail(folder != NULL, -1);
1611 g_return_val_if_fail(item != NULL, -1);
1612 g_return_val_if_fail(item->path != NULL, -1);
1614 session = imap_session_get(folder);
1615 if (!session) return -1;
1617 path = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1619 ok = imap_cmd_examine(session, "INBOX",
1620 &exists, &recent, &unseen, &uid_validity);
1621 if (ok != IMAP_SUCCESS) {
1626 ok = imap_cmd_delete(session, path);
1627 if (ok != IMAP_SUCCESS) {
1628 log_warning(_("can't delete mailbox\n"));
1634 cache_dir = folder_item_get_path(item);
1635 if (is_dir_exist(cache_dir) && remove_dir_recursive(cache_dir) < 0)
1636 g_warning("can't remove directory '%s'\n", cache_dir);
1638 folder_item_remove(item);
1643 static GSList *imap_get_uncached_messages(IMAPSession *session,
1645 MsgNumberList *numlist)
1648 GSList *newlist = NULL;
1649 GSList *llast = NULL;
1652 GSList *seq_list, *cur;
1655 g_return_val_if_fail(session != NULL, NULL);
1656 g_return_val_if_fail(item != NULL, NULL);
1657 g_return_val_if_fail(item->folder != NULL, NULL);
1658 g_return_val_if_fail(FOLDER_CLASS(item->folder) == &imap_class, NULL);
1660 seq_list = imap_get_seq_set_from_numlist(numlist);
1661 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
1662 imapset = cur->data;
1664 if (imap_cmd_envelope(session, imapset)
1666 log_warning(_("can't get envelope\n"));
1670 str = g_string_new(NULL);
1673 if ((tmp = sock_getline(SESSION(session)->sock)) == NULL) {
1674 log_warning(_("error occurred while getting envelope.\n"));
1675 g_string_free(str, TRUE);
1679 if (tmp[0] != '*' || tmp[1] != ' ') {
1680 log_print("IMAP4< %s\n", tmp);
1684 if (strstr(tmp, "FETCH") == NULL) {
1685 log_print("IMAP4< %s\n", tmp);
1689 log_print("IMAP4< %s\n", tmp);
1690 g_string_assign(str, tmp);
1693 msginfo = imap_parse_envelope
1694 (SESSION(session)->sock, item, str);
1696 log_warning(_("can't parse envelope: %s\n"), str->str);
1699 if (item->stype == F_QUEUE) {
1700 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_QUEUED);
1701 } else if (item->stype == F_DRAFT) {
1702 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_DRAFT);
1705 msginfo->folder = item;
1708 llast = newlist = g_slist_append(newlist, msginfo);
1710 llast = g_slist_append(llast, msginfo);
1711 llast = llast->next;
1715 g_string_free(str, TRUE);
1717 imap_seq_set_free(seq_list);
1722 static void imap_delete_all_cached_messages(FolderItem *item)
1726 g_return_if_fail(item != NULL);
1727 g_return_if_fail(item->folder != NULL);
1728 g_return_if_fail(FOLDER_CLASS(item->folder) == &imap_class);
1730 debug_print("Deleting all cached messages...\n");
1732 dir = folder_item_get_path(item);
1733 if (is_dir_exist(dir))
1734 remove_all_numbered_files(dir);
1737 debug_print("done.\n");
1741 static SockInfo *imap_open_tunnel(const gchar *server,
1742 const gchar *tunnelcmd,
1745 static SockInfo *imap_open_tunnel(const gchar *server,
1746 const gchar *tunnelcmd)
1751 if ((sock = sock_connect_cmd(server, tunnelcmd)) == NULL) {
1752 log_warning(_("Can't establish IMAP4 session with: %s\n"),
1757 return imap_init_sock(sock, ssl_type);
1759 return imap_init_sock(sock);
1765 static SockInfo *imap_open(const gchar *server, gushort port,
1768 static SockInfo *imap_open(const gchar *server, gushort port)
1773 if ((sock = sock_connect(server, port)) == NULL) {
1774 log_warning(_("Can't connect to IMAP4 server: %s:%d\n"),
1780 if (ssl_type == SSL_TUNNEL && !ssl_init_socket(sock)) {
1781 log_warning(_("Can't establish IMAP4 session with: %s:%d\n"),
1791 static SockInfo *imap_init_sock(SockInfo *sock, SSLType ssl_type)
1793 static SockInfo *imap_init_sock(SockInfo *sock)
1800 static GList *imap_parse_namespace_str(gchar *str)
1805 IMAPNameSpace *namespace;
1806 GList *ns_list = NULL;
1808 while (*p != '\0') {
1809 /* parse ("#foo" "/") */
1811 while (*p && *p != '(') p++;
1812 if (*p == '\0') break;
1815 while (*p && *p != '"') p++;
1816 if (*p == '\0') break;
1820 while (*p && *p != '"') p++;
1821 if (*p == '\0') break;
1825 while (*p && isspace(*p)) p++;
1826 if (*p == '\0') break;
1827 if (strncmp(p, "NIL", 3) == 0)
1829 else if (*p == '"') {
1832 while (*p && *p != '"') p++;
1833 if (*p == '\0') break;
1838 while (*p && *p != ')') p++;
1839 if (*p == '\0') break;
1842 namespace = g_new(IMAPNameSpace, 1);
1843 namespace->name = g_strdup(name);
1844 namespace->separator = separator ? separator[0] : '\0';
1845 ns_list = g_list_append(ns_list, namespace);
1851 static void imap_parse_namespace(IMAPSession *session, IMAPFolder *folder)
1856 g_return_if_fail(session != NULL);
1857 g_return_if_fail(folder != NULL);
1859 if (folder->ns_personal != NULL ||
1860 folder->ns_others != NULL ||
1861 folder->ns_shared != NULL)
1864 if (!imap_has_capability(session, "NAMESPACE")) {
1865 imap_get_namespace_by_list(session, folder);
1869 if (imap_cmd_namespace(session, &ns_str)
1871 log_warning(_("can't get namespace\n"));
1875 str_array = strsplit_parenthesis(ns_str, '(', ')', 3);
1876 if (str_array == NULL) {
1878 imap_get_namespace_by_list(session, folder);
1882 folder->ns_personal = imap_parse_namespace_str(str_array[0]);
1883 if (str_array[0] && str_array[1])
1884 folder->ns_others = imap_parse_namespace_str(str_array[1]);
1885 if (str_array[0] && str_array[1] && str_array[2])
1886 folder->ns_shared = imap_parse_namespace_str(str_array[2]);
1887 g_strfreev(str_array);
1891 static void imap_get_namespace_by_list(IMAPSession *session, IMAPFolder *folder)
1893 GSList *item_list, *cur;
1894 gchar separator = '\0';
1895 IMAPNameSpace *namespace;
1897 g_return_if_fail(session != NULL);
1898 g_return_if_fail(folder != NULL);
1900 if (folder->ns_personal != NULL ||
1901 folder->ns_others != NULL ||
1902 folder->ns_shared != NULL)
1905 imap_gen_send(session, "LIST \"\" \"\"");
1906 item_list = imap_parse_list(folder, session, "", &separator);
1907 for (cur = item_list; cur != NULL; cur = cur->next)
1908 folder_item_destroy(FOLDER_ITEM(cur->data));
1909 g_slist_free(item_list);
1911 namespace = g_new(IMAPNameSpace, 1);
1912 namespace->name = g_strdup("");
1913 namespace->separator = separator;
1914 folder->ns_personal = g_list_append(NULL, namespace);
1917 static IMAPNameSpace *imap_find_namespace_from_list(GList *ns_list,
1920 IMAPNameSpace *namespace = NULL;
1921 gchar *tmp_path, *name;
1923 if (!path) path = "";
1925 Xstrcat_a(tmp_path, path, "/", return NULL);
1927 for (; ns_list != NULL; ns_list = ns_list->next) {
1928 IMAPNameSpace *tmp_ns = ns_list->data;
1930 Xstrdup_a(name, tmp_ns->name, return namespace);
1931 if (tmp_ns->separator && tmp_ns->separator != '/')
1932 subst_char(name, tmp_ns->separator, '/');
1933 if (strncmp(tmp_path, name, strlen(name)) == 0)
1940 static IMAPNameSpace *imap_find_namespace(IMAPFolder *folder,
1943 IMAPNameSpace *namespace;
1945 g_return_val_if_fail(folder != NULL, NULL);
1947 namespace = imap_find_namespace_from_list(folder->ns_personal, path);
1948 if (namespace) return namespace;
1949 namespace = imap_find_namespace_from_list(folder->ns_others, path);
1950 if (namespace) return namespace;
1951 namespace = imap_find_namespace_from_list(folder->ns_shared, path);
1952 if (namespace) return namespace;
1957 static gchar imap_get_path_separator(IMAPFolder *folder, const gchar *path)
1959 IMAPNameSpace *namespace;
1960 gchar separator = '/';
1962 namespace = imap_find_namespace(folder, path);
1963 if (namespace && namespace->separator)
1964 separator = namespace->separator;
1969 static gchar *imap_get_real_path(IMAPFolder *folder, const gchar *path)
1974 g_return_val_if_fail(folder != NULL, NULL);
1975 g_return_val_if_fail(path != NULL, NULL);
1977 real_path = imap_locale_to_modified_utf7(path);
1978 separator = imap_get_path_separator(folder, path);
1979 imap_path_separator_subst(real_path, separator);
1984 static gchar *imap_parse_atom(SockInfo *sock, gchar *src,
1985 gchar *dest, gint dest_len, GString *str)
1987 gchar *cur_pos = src;
1990 g_return_val_if_fail(str != NULL, cur_pos);
1992 /* read the next line if the current response buffer is empty */
1993 while (isspace(*cur_pos)) cur_pos++;
1994 while (*cur_pos == '\0') {
1995 if ((nextline = sock_getline(sock)) == NULL)
1997 g_string_assign(str, nextline);
1999 strretchomp(nextline);
2000 /* log_print("IMAP4< %s\n", nextline); */
2001 debug_print("IMAP4< %s\n", nextline);
2004 while (isspace(*cur_pos)) cur_pos++;
2007 if (!strncmp(cur_pos, "NIL", 3)) {
2010 } else if (*cur_pos == '\"') {
2013 p = get_quoted(cur_pos, '\"', dest, dest_len);
2014 cur_pos = p ? p : cur_pos + 2;
2015 } else if (*cur_pos == '{') {
2020 cur_pos = strchr_cpy(cur_pos + 1, '}', buf, sizeof(buf));
2022 g_return_val_if_fail(len > 0, cur_pos);
2024 g_string_truncate(str, 0);
2028 if ((nextline = sock_getline(sock)) == NULL)
2030 line_len += strlen(nextline);
2031 g_string_append(str, nextline);
2033 strretchomp(nextline);
2034 /* log_print("IMAP4< %s\n", nextline); */
2035 debug_print("IMAP4< %s\n", nextline);
2037 } while (line_len < len);
2039 memcpy(dest, cur_pos, MIN(len, dest_len - 1));
2040 dest[MIN(len, dest_len - 1)] = '\0';
2047 static gchar *imap_get_header(SockInfo *sock, gchar *cur_pos, gchar **headers,
2057 g_return_val_if_fail(str != NULL, cur_pos);
2059 while (isspace(*cur_pos)) cur_pos++;
2061 g_return_val_if_fail(*cur_pos == '{', cur_pos);
2063 cur_pos = strchr_cpy(cur_pos + 1, '}', buf, sizeof(buf));
2065 g_return_val_if_fail(len > 0, cur_pos);
2067 g_string_truncate(str, 0);
2071 if ((nextline = sock_getline(sock)) == NULL)
2073 block_len += strlen(nextline);
2074 g_string_append(str, nextline);
2076 strretchomp(nextline);
2077 /* debug_print("IMAP4< %s\n", nextline); */
2079 } while (block_len < len);
2081 debug_print("IMAP4< [contents of BODY.PEEK[HEADER.FIELDS (...)]]\n");
2083 *headers = g_strndup(cur_pos, len);
2086 while (isspace(*cur_pos)) cur_pos++;
2087 while (*cur_pos == '\0') {
2088 if ((nextline = sock_getline(sock)) == NULL)
2090 g_string_assign(str, nextline);
2092 strretchomp(nextline);
2093 debug_print("IMAP4< %s\n", nextline);
2096 while (isspace(*cur_pos)) cur_pos++;
2102 static MsgFlags imap_parse_flags(const gchar *flag_str)
2104 const gchar *p = flag_str;
2105 MsgFlags flags = {0, 0};
2107 flags.perm_flags = MSG_UNREAD;
2109 while ((p = strchr(p, '\\')) != NULL) {
2112 if (g_strncasecmp(p, "Recent", 6) == 0 && MSG_IS_UNREAD(flags)) {
2113 MSG_SET_PERM_FLAGS(flags, MSG_NEW);
2114 } else if (g_strncasecmp(p, "Seen", 4) == 0) {
2115 MSG_UNSET_PERM_FLAGS(flags, MSG_NEW|MSG_UNREAD);
2116 } else if (g_strncasecmp(p, "Deleted", 7) == 0) {
2117 MSG_SET_PERM_FLAGS(flags, MSG_DELETED);
2118 } else if (g_strncasecmp(p, "Flagged", 7) == 0) {
2119 MSG_SET_PERM_FLAGS(flags, MSG_MARKED);
2120 } else if (g_strncasecmp(p, "Answered", 8) == 0) {
2121 MSG_SET_PERM_FLAGS(flags, MSG_REPLIED);
2128 static MsgInfo *imap_parse_envelope(SockInfo *sock, FolderItem *item,
2131 gchar buf[IMAPBUFSIZE];
2132 MsgInfo *msginfo = NULL;
2137 MsgFlags flags = {0, 0}, imap_flags = {0, 0};
2139 g_return_val_if_fail(line_str != NULL, NULL);
2140 g_return_val_if_fail(line_str->str[0] == '*' &&
2141 line_str->str[1] == ' ', NULL);
2143 MSG_SET_TMP_FLAGS(flags, MSG_IMAP);
2144 if (item->stype == F_QUEUE) {
2145 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
2146 } else if (item->stype == F_DRAFT) {
2147 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
2150 cur_pos = line_str->str + 2;
2152 #define PARSE_ONE_ELEMENT(ch) \
2154 cur_pos = strchr_cpy(cur_pos, ch, buf, sizeof(buf)); \
2155 if (cur_pos == NULL) { \
2156 g_warning("cur_pos == NULL\n"); \
2157 procmsg_msginfo_free(msginfo); \
2162 PARSE_ONE_ELEMENT(' ');
2165 PARSE_ONE_ELEMENT(' ');
2166 g_return_val_if_fail(!strcmp(buf, "FETCH"), NULL);
2168 g_return_val_if_fail(*cur_pos == '(', NULL);
2171 while (*cur_pos != '\0' && *cur_pos != ')') {
2172 while (*cur_pos == ' ') cur_pos++;
2174 if (!strncmp(cur_pos, "UID ", 4)) {
2176 uid = strtoul(cur_pos, &cur_pos, 10);
2177 } else if (!strncmp(cur_pos, "FLAGS ", 6)) {
2179 if (*cur_pos != '(') {
2180 g_warning("*cur_pos != '('\n");
2181 procmsg_msginfo_free(msginfo);
2185 PARSE_ONE_ELEMENT(')');
2186 imap_flags = imap_parse_flags(buf);
2187 } else if (!strncmp(cur_pos, "RFC822.SIZE ", 12)) {
2189 size = strtol(cur_pos, &cur_pos, 10);
2190 } else if (!strncmp(cur_pos, "BODY[HEADER.FIELDS ", 19)) {
2194 if (*cur_pos != '(') {
2195 g_warning("*cur_pos != '('\n");
2196 procmsg_msginfo_free(msginfo);
2200 PARSE_ONE_ELEMENT(')');
2201 if (*cur_pos != ']') {
2202 g_warning("*cur_pos != ']'\n");
2203 procmsg_msginfo_free(msginfo);
2208 cur_pos = imap_get_header(sock, cur_pos, &headers,
2210 msginfo = procheader_parse_str(headers, flags, FALSE, FALSE);
2213 g_warning("invalid FETCH response: %s\n", cur_pos);
2219 msginfo->msgnum = uid;
2220 msginfo->size = size;
2221 msginfo->flags.tmp_flags |= imap_flags.tmp_flags;
2222 msginfo->flags.perm_flags = imap_flags.perm_flags;
2228 static gchar *imap_get_flag_str(IMAPFlags flags)
2233 str = g_string_new(NULL);
2235 if (IMAP_IS_SEEN(flags)) g_string_append(str, "\\Seen ");
2236 if (IMAP_IS_ANSWERED(flags)) g_string_append(str, "\\Answered ");
2237 if (IMAP_IS_FLAGGED(flags)) g_string_append(str, "\\Flagged ");
2238 if (IMAP_IS_DELETED(flags)) g_string_append(str, "\\Deleted ");
2239 if (IMAP_IS_DRAFT(flags)) g_string_append(str, "\\Draft");
2241 if (str->len > 0 && str->str[str->len - 1] == ' ')
2242 g_string_truncate(str, str->len - 1);
2245 g_string_free(str, FALSE);
2250 static gint imap_set_message_flags(IMAPSession *session,
2251 MsgNumberList *numlist,
2258 GSList *seq_list, *cur;
2261 flag_str = imap_get_flag_str(flags);
2262 cmd = g_strconcat(is_set ? "+FLAGS.SILENT (" : "-FLAGS.SILENT (",
2263 flag_str, ")", NULL);
2266 seq_list = imap_get_seq_set_from_numlist(numlist);
2267 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
2268 imapset = cur->data;
2270 ok = imap_cmd_store(session, imapset, cmd);
2272 imap_seq_set_free(seq_list);
2278 static gint imap_select(IMAPSession *session, IMAPFolder *folder,
2280 gint *exists, gint *recent, gint *unseen,
2281 guint32 *uid_validity)
2285 gint exists_, recent_, unseen_, uid_validity_;
2287 if (!exists || !recent || !unseen || !uid_validity) {
2288 if (session->mbox && strcmp(session->mbox, path) == 0)
2289 return IMAP_SUCCESS;
2293 uid_validity = &uid_validity_;
2296 g_free(session->mbox);
2297 session->mbox = NULL;
2299 real_path = imap_get_real_path(folder, path);
2300 ok = imap_cmd_select(session, real_path,
2301 exists, recent, unseen, uid_validity);
2302 if (ok != IMAP_SUCCESS)
2303 log_warning(_("can't select folder: %s\n"), real_path);
2305 session->mbox = g_strdup(path);
2306 session->folder_content_changed = FALSE;
2313 #define THROW(err) { ok = err; goto catch; }
2315 static gint imap_status(IMAPSession *session, IMAPFolder *folder,
2317 gint *messages, gint *recent,
2318 guint32 *uid_next, guint32 *uid_validity,
2327 *messages = *recent = *uid_next = *uid_validity = *unseen = 0;
2329 argbuf = g_ptr_array_new();
2331 real_path = imap_get_real_path(folder, path);
2332 QUOTE_IF_REQUIRED(real_path_, real_path);
2333 imap_gen_send(session, "STATUS %s "
2334 "(MESSAGES RECENT UIDNEXT UIDVALIDITY UNSEEN)",
2337 ok = imap_cmd_ok(session, argbuf);
2338 if (ok != IMAP_SUCCESS) THROW(ok);
2340 str = search_array_str(argbuf, "STATUS");
2341 if (!str) THROW(IMAP_ERROR);
2343 str = strchr(str, '(');
2344 if (!str) THROW(IMAP_ERROR);
2346 while (*str != '\0' && *str != ')') {
2347 while (*str == ' ') str++;
2349 if (!strncmp(str, "MESSAGES ", 9)) {
2351 *messages = strtol(str, &str, 10);
2352 } else if (!strncmp(str, "RECENT ", 7)) {
2354 *recent = strtol(str, &str, 10);
2355 } else if (!strncmp(str, "UIDNEXT ", 8)) {
2357 *uid_next = strtoul(str, &str, 10);
2358 } else if (!strncmp(str, "UIDVALIDITY ", 12)) {
2360 *uid_validity = strtoul(str, &str, 10);
2361 } else if (!strncmp(str, "UNSEEN ", 7)) {
2363 *unseen = strtol(str, &str, 10);
2365 g_warning("invalid STATUS response: %s\n", str);
2372 ptr_array_free_strings(argbuf);
2373 g_ptr_array_free(argbuf, TRUE);
2381 /* low-level IMAP4rev1 commands */
2383 static gint imap_cmd_authenticate(IMAPSession *session, const gchar *user,
2384 const gchar *pass, IMAPAuthType type)
2391 gchar hexdigest[33];
2395 auth_type = "CRAM-MD5";
2397 imap_gen_send(session, "AUTHENTICATE %s", auth_type);
2398 ok = imap_gen_recv(session, &buf);
2399 if (ok != IMAP_SUCCESS || buf[0] != '+' || buf[1] != ' ') {
2404 challenge = g_malloc(strlen(buf + 2) + 1);
2405 challenge_len = base64_decode(challenge, buf + 2, -1);
2406 challenge[challenge_len] = '\0';
2408 log_print("IMAP< [Decoded: %s]\n", challenge);
2410 md5_hex_hmac(hexdigest, challenge, challenge_len, pass, strlen(pass));
2413 response = g_strdup_printf("%s %s", user, hexdigest);
2414 log_print("IMAP> [Encoded: %s]\n", response);
2415 response64 = g_malloc((strlen(response) + 3) * 2 + 1);
2416 base64_encode(response64, response, strlen(response));
2419 log_print("IMAP> %s\n", response64);
2420 sock_puts(SESSION(session)->sock, response64);
2421 ok = imap_cmd_ok(session, NULL);
2422 if (ok != IMAP_SUCCESS)
2423 log_warning(_("IMAP4 authentication failed.\n"));
2428 static gint imap_cmd_login(IMAPSession *session,
2429 const gchar *user, const gchar *pass)
2431 gchar *user_, *pass_;
2434 QUOTE_IF_REQUIRED(user_, user);
2435 QUOTE_IF_REQUIRED(pass_, pass);
2436 imap_gen_send(session, "LOGIN %s %s", user_, pass_);
2438 ok = imap_cmd_ok(session, NULL);
2439 if (ok != IMAP_SUCCESS)
2440 log_warning(_("IMAP4 login failed.\n"));
2445 static gint imap_cmd_logout(IMAPSession *session)
2447 imap_gen_send(session, "LOGOUT");
2448 return imap_cmd_ok(session, NULL);
2451 /* Send CAPABILITY, and examine the server's response to see whether this
2452 * connection is pre-authenticated or not and build a list of CAPABILITIES. */
2453 static gint imap_greeting(IMAPSession *session)
2458 imap_gen_send(session, "CAPABILITY");
2460 argbuf = g_ptr_array_new();
2462 if (imap_cmd_ok(session, argbuf) != IMAP_SUCCESS ||
2463 ((capstr = search_array_str(argbuf, "CAPABILITY ")) == NULL)) {
2464 ptr_array_free_strings(argbuf);
2465 g_ptr_array_free(argbuf, TRUE);
2469 session->authenticated = search_array_str(argbuf, "PREAUTH") != NULL;
2471 capstr += strlen("CAPABILITY ");
2473 IMAP_SESSION(session)->capability = g_strsplit(capstr, " ", 0);
2475 ptr_array_free_strings(argbuf);
2476 g_ptr_array_free(argbuf, TRUE);
2481 static gboolean imap_has_capability(IMAPSession *session, const gchar *cap)
2485 for (p = session->capability; *p != NULL; ++p)
2486 if (g_strcasecmp(*p, cap) == 0)
2492 void imap_free_capabilities(IMAPSession *session)
2494 g_strfreev(session->capability);
2495 session->capability = NULL;
2498 static gint imap_cmd_noop(IMAPSession *session)
2500 imap_gen_send(session, "NOOP");
2501 return imap_cmd_ok(session, NULL);
2504 static gint imap_cmd_starttls(IMAPSession *session)
2506 imap_gen_send(session, "STARTTLS");
2507 return imap_cmd_ok(session, NULL);
2510 #define THROW(err) { ok = err; goto catch; }
2512 static gint imap_cmd_namespace(IMAPSession *session, gchar **ns_str)
2518 argbuf = g_ptr_array_new();
2520 imap_gen_send(session, "NAMESPACE");
2521 if ((ok = imap_cmd_ok(session, argbuf)) != IMAP_SUCCESS) THROW(ok);
2523 str = search_array_str(argbuf, "NAMESPACE");
2524 if (!str) THROW(IMAP_ERROR);
2526 *ns_str = g_strdup(str);
2529 ptr_array_free_strings(argbuf);
2530 g_ptr_array_free(argbuf, TRUE);
2537 static gint imap_cmd_list(IMAPSession *session, const gchar *ref,
2538 const gchar *mailbox, GPtrArray *argbuf)
2540 gchar *ref_, *mailbox_;
2542 if (!ref) ref = "\"\"";
2543 if (!mailbox) mailbox = "\"\"";
2545 QUOTE_IF_REQUIRED(ref_, ref);
2546 QUOTE_IF_REQUIRED(mailbox_, mailbox);
2547 imap_gen_send(session, "LIST %s %s", ref_, mailbox_);
2549 return imap_cmd_ok(session, argbuf);
2552 #define THROW goto catch
2554 static gint imap_cmd_do_select(IMAPSession *session, const gchar *folder,
2556 gint *exists, gint *recent, gint *unseen,
2557 guint32 *uid_validity)
2565 *exists = *recent = *unseen = *uid_validity = 0;
2566 argbuf = g_ptr_array_new();
2569 select_cmd = "EXAMINE";
2571 select_cmd = "SELECT";
2573 QUOTE_IF_REQUIRED(folder_, folder);
2574 imap_gen_send(session, "%s %s", select_cmd, folder_);
2576 if ((ok = imap_cmd_ok(session, argbuf)) != IMAP_SUCCESS) THROW;
2578 resp_str = search_array_contain_str(argbuf, "EXISTS");
2580 if (sscanf(resp_str,"%d EXISTS", exists) != 1) {
2581 g_warning("imap_cmd_select(): invalid EXISTS line.\n");
2586 resp_str = search_array_contain_str(argbuf, "RECENT");
2588 if (sscanf(resp_str, "%d RECENT", recent) != 1) {
2589 g_warning("imap_cmd_select(): invalid RECENT line.\n");
2594 resp_str = search_array_contain_str(argbuf, "UIDVALIDITY");
2596 if (sscanf(resp_str, "OK [UIDVALIDITY %u] ", uid_validity)
2598 g_warning("imap_cmd_select(): invalid UIDVALIDITY line.\n");
2603 resp_str = search_array_contain_str(argbuf, "UNSEEN");
2605 if (sscanf(resp_str, "OK [UNSEEN %d] ", unseen) != 1) {
2606 g_warning("imap_cmd_select(): invalid UNSEEN line.\n");
2612 ptr_array_free_strings(argbuf);
2613 g_ptr_array_free(argbuf, TRUE);
2618 static gint imap_cmd_select(IMAPSession *session, const gchar *folder,
2619 gint *exists, gint *recent, gint *unseen,
2620 guint32 *uid_validity)
2622 return imap_cmd_do_select(session, folder, FALSE,
2623 exists, recent, unseen, uid_validity);
2626 static gint imap_cmd_examine(IMAPSession *session, const gchar *folder,
2627 gint *exists, gint *recent, gint *unseen,
2628 guint32 *uid_validity)
2630 return imap_cmd_do_select(session, folder, TRUE,
2631 exists, recent, unseen, uid_validity);
2636 static gint imap_cmd_create(IMAPSession *session, const gchar *folder)
2640 QUOTE_IF_REQUIRED(folder_, folder);
2641 imap_gen_send(session, "CREATE %s", folder_);
2643 return imap_cmd_ok(session, NULL);
2646 static gint imap_cmd_rename(IMAPSession *session, const gchar *old_folder,
2647 const gchar *new_folder)
2649 gchar *old_folder_, *new_folder_;
2651 QUOTE_IF_REQUIRED(old_folder_, old_folder);
2652 QUOTE_IF_REQUIRED(new_folder_, new_folder);
2653 imap_gen_send(session, "RENAME %s %s", old_folder_, new_folder_);
2655 return imap_cmd_ok(session, NULL);
2658 static gint imap_cmd_delete(IMAPSession *session, const gchar *folder)
2662 QUOTE_IF_REQUIRED(folder_, folder);
2663 imap_gen_send(session, "DELETE %s", folder_);
2665 return imap_cmd_ok(session, NULL);
2668 static gint imap_cmd_search(IMAPSession *session, const gchar *criteria, GSList **list)
2674 g_return_val_if_fail(criteria != NULL, IMAP_ERROR);
2675 g_return_val_if_fail(list != NULL, IMAP_ERROR);
2679 argbuf = g_ptr_array_new();
2680 imap_gen_send(session, "UID SEARCH %s", criteria);
2682 ok = imap_cmd_ok(session, argbuf);
2683 if (ok != IMAP_SUCCESS) {
2684 ptr_array_free_strings(argbuf);
2685 g_ptr_array_free(argbuf, TRUE);
2689 if ((uidlist = search_array_str(argbuf, "SEARCH ")) != NULL) {
2690 gchar **strlist, **p;
2692 strlist = g_strsplit(uidlist + 7, " ", 0);
2693 for (p = strlist; *p != NULL; ++p) {
2696 if (sscanf(*p, "%d", &msgnum) == 1)
2697 *list = g_slist_append(*list, GINT_TO_POINTER(msgnum));
2699 g_strfreev(strlist);
2701 ptr_array_free_strings(argbuf);
2702 g_ptr_array_free(argbuf, TRUE);
2704 return IMAP_SUCCESS;
2707 static gint imap_cmd_fetch(IMAPSession *session, guint32 uid, const gchar *filename)
2715 g_return_val_if_fail(filename != NULL, IMAP_ERROR);
2717 imap_gen_send(session, "UID FETCH %d BODY.PEEK[]", uid);
2719 while ((ok = imap_gen_recv(session, &buf)) == IMAP_SUCCESS) {
2720 if (buf[0] != '*' || buf[1] != ' ') {
2724 if (strstr(buf, "FETCH") != NULL) break;
2727 if (ok != IMAP_SUCCESS) {
2732 #define RETURN_ERROR_IF_FAIL(cond) \
2735 return IMAP_ERROR; \
2738 cur_pos = strchr(buf, '{');
2739 RETURN_ERROR_IF_FAIL(cur_pos != NULL);
2740 cur_pos = strchr_cpy(cur_pos + 1, '}', size_str, sizeof(size_str));
2741 RETURN_ERROR_IF_FAIL(cur_pos != NULL);
2742 size_num = atol(size_str);
2743 RETURN_ERROR_IF_FAIL(size_num > 0);
2745 RETURN_ERROR_IF_FAIL(*cur_pos == '\0');
2747 #undef RETURN_ERROR_IF_FAIL
2751 if (recv_bytes_write_to_file(SESSION(session)->sock, size_num, filename) != 0)
2754 if (imap_gen_recv(session, &buf) != IMAP_SUCCESS) {
2759 if (buf[0] == '\0' || buf[strlen(buf) - 1] != ')') {
2765 ok = imap_cmd_ok(session, NULL);
2770 static gint imap_cmd_append(IMAPSession *session, const gchar *destfolder,
2771 const gchar *file, IMAPFlags flags, gint32 *new_uid)
2778 gchar buf[BUFFSIZE];
2783 g_return_val_if_fail(file != NULL, IMAP_ERROR);
2785 size = get_file_size_as_crlf(file);
2786 if ((fp = fopen(file, "rb")) == NULL) {
2787 FILE_OP_ERROR(file, "fopen");
2790 QUOTE_IF_REQUIRED(destfolder_, destfolder);
2791 flag_str = imap_get_flag_str(flags);
2792 imap_gen_send(session, "APPEND %s (%s) {%d}",
2793 destfolder_, flag_str, size);
2796 ok = imap_gen_recv(session, &ret);
2797 if (ok != IMAP_SUCCESS || ret[0] != '+' || ret[1] != ' ') {
2798 log_warning(_("can't append %s to %s\n"), file, destfolder_);
2805 log_print("IMAP4> %s\n", _("(sending file...)"));
2807 while (fgets(buf, sizeof(buf), fp) != NULL) {
2809 if (sock_puts(SESSION(session)->sock, buf) < 0) {
2816 FILE_OP_ERROR(file, "fgets");
2821 sock_puts(SESSION(session)->sock, "");
2825 reply = g_ptr_array_new();
2828 ok = imap_cmd_ok(session, reply);
2829 if (ok != IMAP_SUCCESS)
2830 log_warning(_("can't append message to %s\n"), destfolder_);
2832 (new_uid != NULL) &&
2833 (imap_has_capability(session, "UIDPLUS") && reply->len > 0) &&
2834 ((okmsginfo = g_ptr_array_index(reply, reply->len - 1)) != NULL) &&
2835 (sscanf(okmsginfo, "%*u OK [APPENDUID %*u %u]", &newuid) == 1)) {
2839 ptr_array_free_strings(reply);
2840 g_ptr_array_free(reply, TRUE);
2844 static gint imap_cmd_copy(IMAPSession *session, const gchar *seq_set,
2845 const gchar *destfolder, GRelation *uid_mapping)
2851 g_return_val_if_fail(session != NULL, IMAP_ERROR);
2852 g_return_val_if_fail(seq_set != NULL, IMAP_ERROR);
2853 g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
2855 QUOTE_IF_REQUIRED(destfolder_, destfolder);
2856 imap_gen_send(session, "UID COPY %s %s", seq_set, destfolder_);
2858 reply = g_ptr_array_new();
2860 ok = imap_cmd_ok(session, reply);
2861 if (ok != IMAP_SUCCESS)
2862 log_warning(_("can't copy %s to %s\n"), seq_set, destfolder_);
2866 - split IMAPSets into uids
2867 - g_relation_insert(uid_mapping, olduid, newuid);
2869 else if (imap_has_capability(session, "UIDPLUS") && reply->len > 0)
2870 if ((okmsginfo = g_ptr_array_index(reply, reply->len - 1)) != NULL &&
2871 sscanf(okmsginfo, "%*u OK [COPYUID %*u %u %u]", &olduid, &newuid) == 2 &&
2875 ptr_array_free_strings(reply);
2876 g_ptr_array_free(reply, TRUE);
2880 gint imap_cmd_envelope(IMAPSession *session, IMAPSet set)
2882 static GString *header_fields = NULL;
2884 if (header_fields == NULL) {
2885 const HeaderEntry *headers, *elem;
2887 headers = procheader_get_headernames(FALSE);
2888 header_fields = g_string_new("");
2890 for (elem = headers; elem->name != NULL; ++elem) {
2891 gint namelen = strlen(elem->name);
2893 /* Header fields ending with space are not rfc822 headers */
2894 if (elem->name[namelen - 1] == ' ')
2897 /* strip : at the of header field */
2898 if(elem->name[namelen - 1] == ':')
2904 g_string_sprintfa(header_fields, "%s%.*s",
2905 header_fields->str[0] != '\0' ? " " : "",
2906 namelen, elem->name);
2911 (session, "UID FETCH %s (UID FLAGS RFC822.SIZE BODY.PEEK[HEADER.FIELDS (%s)])",
2912 set, header_fields->str);
2914 return IMAP_SUCCESS;
2917 static gint imap_cmd_store(IMAPSession *session, IMAPSet seq_set,
2922 imap_gen_send(session, "UID STORE %s %s", seq_set, sub_cmd);
2924 if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS) {
2925 log_warning(_("error while imap command: STORE %s %s\n"),
2930 return IMAP_SUCCESS;
2933 static gint imap_cmd_expunge(IMAPSession *session)
2937 imap_gen_send(session, "EXPUNGE");
2938 if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS) {
2939 log_warning(_("error while imap command: EXPUNGE\n"));
2943 return IMAP_SUCCESS;
2946 static gint imap_cmd_close(IMAPSession *session)
2950 imap_gen_send(session, "CLOSE");
2951 if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS)
2952 log_warning(_("error while imap command: CLOSE\n"));
2957 static gint imap_cmd_ok(IMAPSession *session, GPtrArray *argbuf)
2959 gint ok = IMAP_SUCCESS;
2964 while ((ok = imap_gen_recv(session, &buf))
2966 // make sure data is long enough for any substring of buf
2967 data = alloca(strlen(buf) + 1);
2969 // untagged line read
2970 if (buf[0] == '*' && buf[1] == ' ') {
2973 g_ptr_array_add(argbuf, g_strdup(buf + 2));
2975 if (sscanf(buf + 2, "%d %s", &num, data) >= 2) {
2976 if (!strcmp(data, "EXISTS")) {
2977 session->exists = num;
2978 session->folder_content_changed = TRUE;
2981 if(!strcmp(data, "EXPUNGE")) {
2983 session->folder_content_changed = TRUE;
2986 // tagged line with correct tag and OK response found
2987 } else if ((sscanf(buf, "%d %s", &cmd_num, data) >= 2) &&
2988 (cmd_num == session->cmd_count) &&
2989 !strcmp(data, "OK")) {
2991 g_ptr_array_add(argbuf, g_strdup(buf));
3005 static void imap_gen_send(IMAPSession *session, const gchar *format, ...)
3012 va_start(args, format);
3013 tmp = g_strdup_vprintf(format, args);
3016 session->cmd_count++;
3018 buf = g_strdup_printf("%d %s\r\n", session->cmd_count, tmp);
3019 if (!strncasecmp(tmp, "LOGIN ", 6) && (p = strchr(tmp + 6, ' '))) {
3021 log_print("IMAP4> %d %s ********\n", session->cmd_count, tmp);
3023 log_print("IMAP4> %d %s\n", session->cmd_count, tmp);
3025 sock_write_all(SESSION(session)->sock, buf, strlen(buf));
3030 static gint imap_gen_recv(IMAPSession *session, gchar **ret)
3032 if ((*ret = sock_getline(SESSION(session)->sock)) == NULL)
3037 log_print("IMAP4< %s\n", *ret);
3039 return IMAP_SUCCESS;
3043 /* misc utility functions */
3045 static gchar *strchr_cpy(const gchar *src, gchar ch, gchar *dest, gint len)
3050 tmp = strchr(src, ch);
3054 memcpy(dest, src, MIN(tmp - src, len - 1));
3055 dest[MIN(tmp - src, len - 1)] = '\0';
3060 static gchar *get_quoted(const gchar *src, gchar ch, gchar *dest, gint len)
3062 const gchar *p = src;
3065 g_return_val_if_fail(*p == ch, NULL);
3070 while (*p != '\0' && *p != ch) {
3072 if (*p == '\\' && *(p + 1) != '\0')
3081 return (gchar *)(*p == ch ? p + 1 : p);
3084 static gchar *search_array_contain_str(GPtrArray *array, const gchar *str)
3088 for (i = 0; i < array->len; i++) {
3091 tmp = g_ptr_array_index(array, i);
3092 if (strstr(tmp, str) != NULL)
3099 static gchar *search_array_str(GPtrArray *array, const gchar *str)
3106 for (i = 0; i < array->len; i++) {
3109 tmp = g_ptr_array_index(array, i);
3110 if (!strncmp(tmp, str, len))
3117 static void imap_path_separator_subst(gchar *str, gchar separator)
3120 gboolean in_escape = FALSE;
3122 if (!separator || separator == '/') return;
3124 for (p = str; *p != '\0'; p++) {
3125 if (*p == '/' && !in_escape)
3127 else if (*p == '&' && *(p + 1) != '-' && !in_escape)
3129 else if (*p == '-' && in_escape)
3134 static gchar *imap_modified_utf7_to_locale(const gchar *mutf7_str)
3137 const gchar *from_p;
3140 to = g_malloc(strlen(mutf7_str) + 1);
3143 for (from_p = mutf7_str; *from_p != '\0'; from_p++) {
3144 if (*from_p == '&' && *(from_p + 1) == '-') {
3154 static iconv_t cd = (iconv_t)-1;
3155 static gboolean iconv_ok = TRUE;
3158 size_t norm_utf7_len;
3160 gchar *to_str, *to_p;
3162 gboolean in_escape = FALSE;
3164 if (!iconv_ok) return g_strdup(mutf7_str);
3166 if (cd == (iconv_t)-1) {
3167 cd = iconv_open(conv_get_current_charset_str(), "UTF-7");
3168 if (cd == (iconv_t)-1) {
3169 g_warning("iconv cannot convert UTF-7 to %s\n",
3170 conv_get_current_charset_str());
3172 return g_strdup(mutf7_str);
3176 norm_utf7 = g_string_new(NULL);
3178 for (p = mutf7_str; *p != '\0'; p++) {
3179 /* replace: '&' -> '+',
3181 escaped ',' -> '/' */
3182 if (!in_escape && *p == '&') {
3183 if (*(p + 1) != '-') {
3184 g_string_append_c(norm_utf7, '+');
3187 g_string_append_c(norm_utf7, '&');
3190 } else if (in_escape && *p == ',') {
3191 g_string_append_c(norm_utf7, '/');
3192 } else if (in_escape && *p == '-') {
3193 g_string_append_c(norm_utf7, '-');
3196 g_string_append_c(norm_utf7, *p);
3200 norm_utf7_p = norm_utf7->str;
3201 norm_utf7_len = norm_utf7->len;
3202 to_len = strlen(mutf7_str) * 5;
3203 to_p = to_str = g_malloc(to_len + 1);
3205 if (iconv(cd, (ICONV_CONST gchar **)&norm_utf7_p, &norm_utf7_len,
3206 &to_p, &to_len) == -1) {
3207 g_warning(_("iconv cannot convert UTF-7 to %s\n"),
3208 conv_get_current_charset_str());
3209 g_string_free(norm_utf7, TRUE);
3211 return g_strdup(mutf7_str);
3214 /* second iconv() call for flushing */
3215 iconv(cd, NULL, NULL, &to_p, &to_len);
3216 g_string_free(norm_utf7, TRUE);
3220 #endif /* !HAVE_ICONV */
3223 static gchar *imap_locale_to_modified_utf7(const gchar *from)
3226 const gchar *from_p;
3229 to = g_malloc(strlen(from) * 2 + 1);
3232 for (from_p = from; *from_p != '\0'; from_p++) {
3233 if (*from_p == '&') {
3243 static iconv_t cd = (iconv_t)-1;
3244 static gboolean iconv_ok = TRUE;
3245 gchar *norm_utf7, *norm_utf7_p;
3246 size_t from_len, norm_utf7_len;
3248 gchar *from_tmp, *to, *p;
3249 gboolean in_escape = FALSE;
3251 if (!iconv_ok) return g_strdup(from);
3253 if (cd == (iconv_t)-1) {
3254 cd = iconv_open("UTF-7", conv_get_current_charset_str());
3255 if (cd == (iconv_t)-1) {
3256 g_warning("iconv cannot convert %s to UTF-7\n",
3257 conv_get_current_charset_str());
3259 return g_strdup(from);
3263 Xstrdup_a(from_tmp, from, return g_strdup(from));
3264 from_len = strlen(from);
3265 norm_utf7_len = from_len * 5;
3266 Xalloca(norm_utf7, norm_utf7_len + 1, return g_strdup(from));
3267 norm_utf7_p = norm_utf7;
3269 #define IS_PRINT(ch) (isprint(ch) && isascii(ch))
3271 while (from_len > 0) {
3272 if (*from_tmp == '+') {
3273 *norm_utf7_p++ = '+';
3274 *norm_utf7_p++ = '-';
3278 } else if (IS_PRINT(*from_tmp)) {
3279 /* printable ascii char */
3280 *norm_utf7_p = *from_tmp;
3286 size_t mb_len = 0, conv_len = 0;
3288 /* unprintable char: convert to UTF-7 */
3290 while (!IS_PRINT(*p) && conv_len < from_len) {
3291 mb_len = mblen(p, MB_LEN_MAX);
3293 g_warning("wrong multibyte sequence\n");
3294 return g_strdup(from);
3300 from_len -= conv_len;
3301 if (iconv(cd, (ICONV_CONST gchar **)&from_tmp,
3303 &norm_utf7_p, &norm_utf7_len) == -1) {
3304 g_warning("iconv cannot convert %s to UTF-7\n",
3305 conv_get_current_charset_str());
3306 return g_strdup(from);
3309 /* second iconv() call for flushing */
3310 iconv(cd, NULL, NULL, &norm_utf7_p, &norm_utf7_len);
3316 *norm_utf7_p = '\0';
3317 to_str = g_string_new(NULL);
3318 for (p = norm_utf7; p < norm_utf7_p; p++) {
3319 /* replace: '&' -> "&-",
3322 BASE64 '/' -> ',' */
3323 if (!in_escape && *p == '&') {
3324 g_string_append(to_str, "&-");
3325 } else if (!in_escape && *p == '+') {
3326 if (*(p + 1) == '-') {
3327 g_string_append_c(to_str, '+');
3330 g_string_append_c(to_str, '&');
3333 } else if (in_escape && *p == '/') {
3334 g_string_append_c(to_str, ',');
3335 } else if (in_escape && *p == '-') {
3336 g_string_append_c(to_str, '-');
3339 g_string_append_c(to_str, *p);
3345 g_string_append_c(to_str, '-');
3349 g_string_free(to_str, FALSE);
3352 #endif /* !HAVE_ICONV */
3355 static GSList *imap_get_seq_set_from_numlist(MsgNumberList *numlist)
3358 GSList *sorted_list, *cur;
3359 guint first, last, next;
3361 GSList *ret_list = NULL;
3363 if (numlist == NULL)
3366 str = g_string_sized_new(256);
3368 sorted_list = g_slist_copy(numlist);
3369 sorted_list = g_slist_sort(sorted_list, g_int_compare);
3371 first = GPOINTER_TO_INT(sorted_list->data);
3373 for (cur = sorted_list; cur != NULL; cur = g_slist_next(cur)) {
3374 last = GPOINTER_TO_INT(cur->data);
3376 next = GPOINTER_TO_INT(cur->next->data);
3380 if (last + 1 != next || next == 0) {
3382 g_string_append_c(str, ',');
3384 g_string_sprintfa(str, "%u", first);
3386 g_string_sprintfa(str, "%u:%u", first, last);
3390 if (str->len > IMAP_CMD_LIMIT) {
3391 ret_str = g_strdup(str->str);
3392 ret_list = g_slist_append(ret_list, ret_str);
3393 g_string_truncate(str, 0);
3399 ret_str = g_strdup(str->str);
3400 ret_list = g_slist_append(ret_list, ret_str);
3403 g_slist_free(sorted_list);
3404 g_string_free(str, TRUE);
3409 static GSList *imap_get_seq_set_from_msglist(MsgInfoList *msglist)
3411 MsgNumberList *numlist = NULL;
3415 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
3416 MsgInfo *msginfo = (MsgInfo *) cur->data;
3418 numlist = g_slist_append(numlist, GINT_TO_POINTER(msginfo->msgnum));
3420 seq_list = imap_get_seq_set_from_numlist(numlist);
3421 g_slist_free(numlist);
3426 static void imap_seq_set_free(GSList *seq_list)
3428 slist_free_strings(seq_list);
3429 g_slist_free(seq_list);
3433 static gboolean imap_rename_folder_func(GNode *node, gpointer data)
3435 FolderItem *item = node->data;
3436 gchar **paths = data;
3437 const gchar *oldpath = paths[0];
3438 const gchar *newpath = paths[1];
3440 gchar *new_itempath;
3443 oldpathlen = strlen(oldpath);
3444 if (strncmp(oldpath, item->path, oldpathlen) != 0) {
3445 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
3449 base = item->path + oldpathlen;
3450 while (*base == G_DIR_SEPARATOR) base++;
3452 new_itempath = g_strdup(newpath);
3454 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
3457 item->path = new_itempath;
3462 static gint get_list_of_uids(Folder *folder, IMAPFolderItem *item, GSList **msgnum_list)
3464 gint ok, nummsgs = 0, lastuid_old;
3465 IMAPSession *session;
3466 GSList *uidlist, *elem;
3469 session = imap_session_get(folder);
3470 g_return_val_if_fail(session != NULL, -1);
3472 ok = imap_select(session, IMAP_FOLDER(folder), item->item.path,
3473 NULL, NULL, NULL, NULL);
3474 if (ok != IMAP_SUCCESS)
3477 cmd_buf = g_strdup_printf("UID %d:*", item->lastuid + 1);
3478 ok = imap_cmd_search(session, cmd_buf, &uidlist);
3481 if (ok == IMAP_SOCKET) {
3482 session_destroy((Session *)session);
3483 ((RemoteFolder *)folder)->session = NULL;
3487 if (ok != IMAP_SUCCESS) {
3491 argbuf = g_ptr_array_new();
3493 cmd_buf = g_strdup_printf("UID FETCH %d:* (UID)", item->lastuid + 1);
3494 imap_gen_send(session, cmd_buf);
3496 ok = imap_cmd_ok(session, argbuf);
3497 if (ok != IMAP_SUCCESS) {
3498 ptr_array_free_strings(argbuf);
3499 g_ptr_array_free(argbuf, TRUE);
3503 for(i = 0; i < argbuf->len; i++) {
3506 if((ret = sscanf(g_ptr_array_index(argbuf, i),
3507 "%*d FETCH (UID %d)", &msgnum)) == 1)
3508 uidlist = g_slist_prepend(uidlist, GINT_TO_POINTER(msgnum));
3510 ptr_array_free_strings(argbuf);
3511 g_ptr_array_free(argbuf, TRUE);
3514 lastuid_old = item->lastuid;
3515 *msgnum_list = g_slist_copy(item->uid_list);
3516 nummsgs = g_slist_length(*msgnum_list);
3517 debug_print("Got %d uids from cache\n", g_slist_length(item->uid_list));
3519 for (elem = uidlist; elem != NULL; elem = g_slist_next(elem)) {
3522 msgnum = GPOINTER_TO_INT(elem->data);
3523 if (msgnum > lastuid_old) {
3524 *msgnum_list = g_slist_prepend(*msgnum_list, GINT_TO_POINTER(msgnum));
3525 item->uid_list = g_slist_prepend(item->uid_list, GINT_TO_POINTER(msgnum));
3528 if(msgnum > item->lastuid)
3529 item->lastuid = msgnum;
3532 g_slist_free(uidlist);
3537 gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list)
3539 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3540 IMAPSession *session;
3541 gint ok, nummsgs = 0, exists, recent, unseen, uid_val, uid_next;
3544 gboolean selected_folder;
3546 g_return_val_if_fail(folder != NULL, -1);
3547 g_return_val_if_fail(item != NULL, -1);
3548 g_return_val_if_fail(item->item.path != NULL, -1);
3549 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3550 g_return_val_if_fail(folder->account != NULL, -1);
3552 session = imap_session_get(folder);
3553 g_return_val_if_fail(session != NULL, -1);
3555 selected_folder = (session->mbox != NULL) &&
3556 (!strcmp(session->mbox, item->item.path));
3557 if (selected_folder) {
3558 ok = imap_cmd_noop(session);
3559 if (ok != IMAP_SUCCESS)
3561 exists = session->exists;
3563 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
3564 &exists, &recent, &uid_next, &uid_val, &unseen);
3565 if (ok != IMAP_SUCCESS)
3569 /* If old uid_next matches new uid_next we can be sure no message
3570 was added to the folder */
3571 if (( selected_folder && !session->folder_content_changed) ||
3572 (!selected_folder && uid_next == item->uid_next)) {
3573 nummsgs = g_slist_length(item->uid_list);
3575 /* If number of messages is still the same we
3576 know our caches message numbers are still valid,
3577 otherwise if the number of messages has decrease
3578 we discard our cache to start a new scan to find
3579 out which numbers have been removed */
3580 if (exists == nummsgs) {
3581 *msgnum_list = g_slist_copy(item->uid_list);
3583 } else if (exists < nummsgs) {
3584 debug_print("Freeing imap uid cache");
3586 g_slist_free(item->uid_list);
3587 item->uid_list = NULL;
3590 if (!selected_folder)
3591 item->uid_next = uid_next;
3594 *msgnum_list = NULL;
3598 nummsgs = get_list_of_uids(folder, item, &uidlist);
3600 if (nummsgs != exists) {
3601 /* Cache contains more messages then folder, we have cached
3602 an old UID of a message that was removed and new messages
3603 have been added too, otherwise the uid_next check would
3605 debug_print("Freeing imap uid cache");
3607 g_slist_free(item->uid_list);
3608 item->uid_list = NULL;
3610 g_slist_free(*msgnum_list);
3612 nummsgs = get_list_of_uids(folder, item, &uidlist);
3615 *msgnum_list = uidlist;
3617 dir = folder_item_get_path((FolderItem *)item);
3618 debug_print("removing old messages from %s\n", dir);
3619 remove_numbered_files_not_in_list(dir, *msgnum_list);
3625 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item)
3630 flags.perm_flags = MSG_NEW|MSG_UNREAD;
3631 flags.tmp_flags = 0;
3633 g_return_val_if_fail(item != NULL, NULL);
3634 g_return_val_if_fail(file != NULL, NULL);
3636 if (item->stype == F_QUEUE) {
3637 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
3638 } else if (item->stype == F_DRAFT) {
3639 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
3642 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3643 if (!msginfo) return NULL;
3645 msginfo->folder = item;
3650 GSList *imap_get_msginfos(Folder *folder, FolderItem *item, GSList *msgnum_list)
3652 IMAPSession *session;
3653 MsgInfoList *ret = NULL;
3656 g_return_val_if_fail(folder != NULL, NULL);
3657 g_return_val_if_fail(item != NULL, NULL);
3658 g_return_val_if_fail(msgnum_list != NULL, NULL);
3660 session = imap_session_get(folder);
3661 g_return_val_if_fail(session != NULL, NULL);
3663 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3664 NULL, NULL, NULL, NULL);
3665 if (ok != IMAP_SUCCESS)
3668 if (!(item->stype == F_QUEUE || item->stype == F_DRAFT)) {
3669 ret = g_slist_concat(ret,
3670 imap_get_uncached_messages(
3671 session, item, msgnum_list));
3673 MsgNumberList *sorted_list, *elem;
3674 gint startnum, lastnum;
3676 sorted_list = g_slist_sort(g_slist_copy(msgnum_list), g_int_compare);
3678 startnum = lastnum = GPOINTER_TO_INT(sorted_list->data);
3680 for (elem = sorted_list;; elem = g_slist_next(elem)) {
3684 num = GPOINTER_TO_INT(elem->data);
3686 if (num > lastnum + 1 || elem == NULL) {
3688 for (i = startnum; i <= lastnum; ++i) {
3691 file = imap_fetch_msg(folder, item, i);
3693 MsgInfo *msginfo = imap_parse_msg(file, item);
3694 if (msginfo != NULL) {
3695 msginfo->msgnum = i;
3696 ret = g_slist_append(ret, msginfo);
3710 g_slist_free(sorted_list);
3716 MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid)
3718 MsgInfo *msginfo = NULL;
3719 MsgInfoList *msginfolist;
3720 MsgNumberList numlist;
3722 numlist.next = NULL;
3723 numlist.data = GINT_TO_POINTER(uid);
3725 msginfolist = imap_get_msginfos(folder, item, &numlist);
3726 if (msginfolist != NULL) {
3727 msginfo = msginfolist->data;
3728 g_slist_free(msginfolist);
3734 gboolean imap_check_msgnum_validity(Folder *folder, FolderItem *_item)
3736 IMAPSession *session;
3737 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3738 gint ok, exists = 0, recent = 0, unseen = 0;
3739 guint32 uid_next, uid_validity = 0;
3741 g_return_val_if_fail(folder != NULL, FALSE);
3742 g_return_val_if_fail(item != NULL, FALSE);
3743 g_return_val_if_fail(item->item.folder != NULL, FALSE);
3744 g_return_val_if_fail(FOLDER_CLASS(item->item.folder) == &imap_class, FALSE);
3746 session = imap_session_get(folder);
3747 g_return_val_if_fail(session != NULL, FALSE);
3749 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
3750 &exists, &recent, &uid_next, &uid_validity, &unseen);
3751 if (ok != IMAP_SUCCESS)
3754 if(item->item.mtime == uid_validity)
3757 debug_print("Freeing imap uid cache\n");
3759 g_slist_free(item->uid_list);
3760 item->uid_list = NULL;
3762 item->item.mtime = uid_validity;
3764 imap_delete_all_cached_messages((FolderItem *)item);
3769 void imap_change_flags(Folder *folder, FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
3771 IMAPSession *session;
3772 IMAPFlags flags_set = 0, flags_unset = 0;
3773 gint ok = IMAP_SUCCESS;
3774 MsgNumberList numlist;
3776 g_return_if_fail(folder != NULL);
3777 g_return_if_fail(folder->klass == &imap_class);
3778 g_return_if_fail(item != NULL);
3779 g_return_if_fail(item->folder == folder);
3780 g_return_if_fail(msginfo != NULL);
3781 g_return_if_fail(msginfo->folder == item);
3783 session = imap_session_get(folder);
3784 if (!session) return;
3786 if ((ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
3787 NULL, NULL, NULL, NULL)) != IMAP_SUCCESS)
3790 if (!MSG_IS_MARKED(msginfo->flags) && (newflags & MSG_MARKED))
3791 flags_set |= IMAP_FLAG_FLAGGED;
3792 if ( MSG_IS_MARKED(msginfo->flags) && !(newflags & MSG_MARKED))
3793 flags_unset |= IMAP_FLAG_FLAGGED;
3795 if (!MSG_IS_UNREAD(msginfo->flags) && (newflags & MSG_UNREAD))
3796 flags_unset |= IMAP_FLAG_SEEN;
3797 if ( MSG_IS_UNREAD(msginfo->flags) && !(newflags & MSG_UNREAD))
3798 flags_set |= IMAP_FLAG_SEEN;
3800 if (!MSG_IS_REPLIED(msginfo->flags) && (newflags & MSG_REPLIED))
3801 flags_set |= IMAP_FLAG_ANSWERED;
3802 if ( MSG_IS_REPLIED(msginfo->flags) && !(newflags & MSG_REPLIED))
3803 flags_set |= IMAP_FLAG_ANSWERED;
3805 numlist.next = NULL;
3806 numlist.data = GINT_TO_POINTER(msginfo->msgnum);
3809 ok = imap_set_message_flags(session, &numlist, flags_set, TRUE);
3810 if (ok != IMAP_SUCCESS) return;
3814 ok = imap_set_message_flags(session, &numlist, flags_unset, FALSE);
3815 if (ok != IMAP_SUCCESS) return;
3818 msginfo->flags.perm_flags = newflags;