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 */
82 gboolean authenticated;
91 time_t last_access_time;
92 gboolean folder_content_changed;
102 #define IMAP_SUCCESS 0
103 #define IMAP_SOCKET 2
104 #define IMAP_AUTHFAIL 3
105 #define IMAP_PROTOCOL 4
106 #define IMAP_SYNTAX 5
110 #define IMAPBUFSIZE 8192
114 IMAP_FLAG_SEEN = 1 << 0,
115 IMAP_FLAG_ANSWERED = 1 << 1,
116 IMAP_FLAG_FLAGGED = 1 << 2,
117 IMAP_FLAG_DELETED = 1 << 3,
118 IMAP_FLAG_DRAFT = 1 << 4
121 #define IMAP_IS_SEEN(flags) ((flags & IMAP_FLAG_SEEN) != 0)
122 #define IMAP_IS_ANSWERED(flags) ((flags & IMAP_FLAG_ANSWERED) != 0)
123 #define IMAP_IS_FLAGGED(flags) ((flags & IMAP_FLAG_FLAGGED) != 0)
124 #define IMAP_IS_DELETED(flags) ((flags & IMAP_FLAG_DELETED) != 0)
125 #define IMAP_IS_DRAFT(flags) ((flags & IMAP_FLAG_DRAFT) != 0)
128 #define IMAP4_PORT 143
130 #define IMAPS_PORT 993
133 #define IMAP_CMD_LIMIT 1000
135 #define QUOTE_IF_REQUIRED(out, str) \
137 if (*str != '"' && strpbrk(str, " \t(){}%*") != NULL) { \
141 len = strlen(str) + 3; \
142 Xalloca(__tmp, len, return IMAP_ERROR); \
143 g_snprintf(__tmp, len, "\"%s\"", str); \
146 Xstrdup_a(out, str, return IMAP_ERROR); \
150 typedef gchar * IMAPSet;
152 struct _IMAPFolderItem
161 static Folder *imap_folder_new(const gchar * name, const gchar * path);
162 static void imap_folder_destroy(Folder * folder);
164 static IMAPSession *imap_session_new(const PrefsAccount * account);
165 static void imap_session_authenticate(IMAPSession * session,
166 const PrefsAccount * account);
167 static void imap_session_destroy(Session * session);
169 static gchar *imap_fetch_msg(Folder * folder, FolderItem * item, gint uid);
170 static gint imap_add_msg(Folder * folder,
172 const gchar * file, MsgFlags * flags);
173 static gint imap_add_msgs(Folder * folder, FolderItem * dest,
175 GRelation *relation);
177 static gint imap_copy_msg(Folder * folder,
178 FolderItem * dest, MsgInfo * msginfo);
179 static gint imap_copy_msgs(Folder *folder, FolderItem *dest,
180 MsgInfoList *msglist, GRelation *relation);
182 static gint imap_remove_msg(Folder * folder, FolderItem * item, gint uid);
183 static gint imap_remove_all_msg(Folder * folder, FolderItem * item);
185 static gboolean imap_is_msg_changed(Folder * folder,
186 FolderItem * item, MsgInfo * msginfo);
188 static gint imap_close(Folder * folder, FolderItem * item);
190 static void imap_scan_tree(Folder * folder);
192 static gint imap_create_tree(Folder * folder);
194 static FolderItem *imap_create_folder(Folder * folder,
197 static gint imap_rename_folder(Folder * folder,
198 FolderItem * item, const gchar * name);
199 static gint imap_remove_folder(Folder * folder, FolderItem * item);
202 static void imap_folder_init (Folder *folder,
206 static FolderItem *imap_folder_item_new (Folder *folder);
207 static void imap_folder_item_destroy (Folder *folder,
210 static IMAPSession *imap_session_get (Folder *folder);
212 static gint imap_greeting (IMAPSession *session);
213 static gint imap_auth (IMAPSession *session,
218 static gint imap_scan_tree_recursive (IMAPSession *session,
220 static GSList *imap_parse_list (IMAPFolder *folder,
221 IMAPSession *session,
222 const gchar *real_path,
225 static void imap_create_missing_folders (Folder *folder);
226 static FolderItem *imap_create_special_folder
228 SpecialFolderItemType stype,
231 static gint imap_do_copy_msgs (Folder *folder,
233 MsgInfoList *msglist,
234 GRelation *relation);
236 static void imap_delete_all_cached_messages (FolderItem *item);
239 static SockInfo *imap_open (const gchar *server,
243 static SockInfo *imap_open (const gchar *server,
248 static SockInfo *imap_open_tunnel(const gchar *server,
249 const gchar *tunnelcmd,
252 static SockInfo *imap_open_tunnel(const gchar *server,
253 const gchar *tunnelcmd);
257 static SockInfo *imap_init_sock(SockInfo *sock, SSLType ssl_type);
259 static SockInfo *imap_init_sock(SockInfo *sock);
262 static gchar *imap_get_flag_str (IMAPFlags flags);
263 static gint imap_set_message_flags (IMAPSession *session,
264 MsgNumberList *numlist,
267 static gint imap_select (IMAPSession *session,
273 guint32 *uid_validity);
274 static gint imap_status (IMAPSession *session,
280 guint32 *uid_validity,
283 static void imap_parse_namespace (IMAPSession *session,
285 static void imap_get_namespace_by_list (IMAPSession *session,
287 static IMAPNameSpace *imap_find_namespace (IMAPFolder *folder,
289 static gchar imap_get_path_separator (IMAPFolder *folder,
291 static gchar *imap_get_real_path (IMAPFolder *folder,
294 static gchar *imap_parse_atom (SockInfo *sock,
299 static MsgFlags imap_parse_flags (const gchar *flag_str);
300 static MsgInfo *imap_parse_envelope (SockInfo *sock,
304 static gboolean imap_has_capability (IMAPSession *session,
306 static void imap_free_capabilities (IMAPSession *session);
308 /* low-level IMAP4rev1 commands */
309 static gint imap_cmd_authenticate
310 (IMAPSession *session,
314 static gint imap_cmd_login (IMAPSession *sock,
317 static gint imap_cmd_logout (IMAPSession *sock);
318 static gint imap_cmd_noop (IMAPSession *sock);
319 static gint imap_cmd_starttls (IMAPSession *sock);
320 static gint imap_cmd_namespace (IMAPSession *sock,
322 static gint imap_cmd_list (IMAPSession *session,
324 const gchar *mailbox,
326 static gint imap_cmd_do_select (IMAPSession *sock,
332 guint32 *uid_validity);
333 static gint imap_cmd_select (IMAPSession *sock,
338 guint32 *uid_validity);
339 static gint imap_cmd_examine (IMAPSession *sock,
344 guint32 *uid_validity);
345 static gint imap_cmd_create (IMAPSession *sock,
346 const gchar *folder);
347 static gint imap_cmd_rename (IMAPSession *sock,
348 const gchar *oldfolder,
349 const gchar *newfolder);
350 static gint imap_cmd_delete (IMAPSession *sock,
351 const gchar *folder);
352 static gint imap_cmd_envelope (IMAPSession *sock,
354 static gint imap_cmd_fetch (IMAPSession *sock,
356 const gchar *filename);
357 static gint imap_cmd_append (IMAPSession *session,
358 const gchar *destfolder,
362 static gint imap_cmd_copy (IMAPSession *session,
363 const gchar *seq_set,
364 const gchar *destfolder,
365 GRelation *uid_mapping);
366 static gint imap_cmd_store (IMAPSession *sock,
369 static gint imap_cmd_expunge (IMAPSession *sock);
370 static gint imap_cmd_close (IMAPSession *session);
372 static gint imap_cmd_ok (IMAPSession *session,
374 static void imap_gen_send (IMAPSession *sock,
375 const gchar *format, ...);
376 static gint imap_gen_recv (IMAPSession *sock,
379 /* misc utility functions */
380 static gchar *strchr_cpy (const gchar *src,
384 static gchar *get_quoted (const gchar *src,
388 static gchar *search_array_contain_str (GPtrArray *array,
390 static gchar *search_array_str (GPtrArray *array,
392 static void imap_path_separator_subst (gchar *str,
395 static gchar *imap_utf8_to_modified_utf7 (const gchar *from);
396 static gchar *imap_modified_utf7_to_utf8 (const gchar *mutf7_str);
398 static GSList *imap_get_seq_set_from_numlist (MsgNumberList *msglist);
399 static GSList *imap_get_seq_set_from_msglist (MsgInfoList *msglist);
400 static void imap_seq_set_free (GSList *seq_list);
402 static gboolean imap_rename_folder_func (GNode *node,
404 static gint imap_get_num_list (Folder *folder,
407 gboolean *old_uids_valid);
408 static GSList *imap_get_msginfos (Folder *folder,
410 GSList *msgnum_list);
411 static MsgInfo *imap_get_msginfo (Folder *folder,
414 static gboolean imap_scan_required (Folder *folder,
416 static void imap_change_flags (Folder *folder,
419 MsgPermFlags newflags);
420 static gchar *imap_folder_get_path (Folder *folder);
421 static gchar *imap_item_get_path (Folder *folder,
424 FolderClass imap_class =
430 /* Folder functions */
436 /* FolderItem functions */
437 imap_folder_item_new,
438 imap_folder_item_destroy,
450 /* Message functions */
464 FolderClass *imap_get_class(void)
469 Folder *imap_folder_new(const gchar *name, const gchar *path)
473 folder = (Folder *)g_new0(IMAPFolder, 1);
474 folder->klass = &imap_class;
475 imap_folder_init(folder, name, path);
480 void imap_folder_destroy(Folder *folder)
484 dir = imap_folder_get_path(folder);
485 if (is_dir_exist(dir))
486 remove_dir_recursive(dir);
489 folder_remote_folder_destroy(REMOTE_FOLDER(folder));
492 static void imap_folder_init(Folder *folder, const gchar *name,
495 folder_remote_folder_init((Folder *)folder, name, path);
498 static FolderItem *imap_folder_item_new(Folder *folder)
500 IMAPFolderItem *item;
502 item = g_new0(IMAPFolderItem, 1);
505 item->uid_list = NULL;
507 return (FolderItem *)item;
510 static void imap_folder_item_destroy(Folder *folder, FolderItem *_item)
512 IMAPFolderItem *item = (IMAPFolderItem *)_item;
514 g_return_if_fail(item != NULL);
515 g_slist_free(item->uid_list);
520 static gboolean imap_reset_uid_lists_func(GNode *node, gpointer data)
522 IMAPFolderItem *item = (IMAPFolderItem *)node->data;
526 g_slist_free(item->uid_list);
527 item->uid_list = NULL;
532 static void imap_reset_uid_lists(Folder *folder)
534 if(folder->node == NULL)
537 /* Destroy all uid lists and rest last uid */
538 g_node_traverse(folder->node, G_IN_ORDER, G_TRAVERSE_ALL, -1, imap_reset_uid_lists_func, NULL);
541 /* Send CAPABILITY, and examine the server's response to see whether this
542 * connection is pre-authenticated or not and build a list of CAPABILITIES. */
543 static gint imap_greeting(IMAPSession *session)
548 imap_gen_send(session, "CAPABILITY");
550 argbuf = g_ptr_array_new();
552 if (imap_cmd_ok(session, argbuf) != IMAP_SUCCESS ||
553 ((capstr = search_array_str(argbuf, "CAPABILITY ")) == NULL)) {
554 ptr_array_free_strings(argbuf);
555 g_ptr_array_free(argbuf, TRUE);
559 session->authenticated = search_array_str(argbuf, "PREAUTH") != NULL;
561 capstr += strlen("CAPABILITY ");
563 IMAP_SESSION(session)->capability = g_strsplit(capstr, " ", 0);
565 ptr_array_free_strings(argbuf);
566 g_ptr_array_free(argbuf, TRUE);
568 if (imap_has_capability(session, "UIDPLUS"))
569 session->uidplus = TRUE;
574 static gint imap_auth(IMAPSession *session, const gchar *user, const gchar *pass,
579 if (type == 0 || type == IMAP_AUTH_LOGIN)
580 ok = imap_cmd_login(session, user, pass);
582 ok = imap_cmd_authenticate(session, user, pass, type);
584 if (ok == IMAP_SUCCESS)
585 session->authenticated = TRUE;
590 static IMAPSession *imap_session_get(Folder *folder)
592 RemoteFolder *rfolder = REMOTE_FOLDER(folder);
593 IMAPSession *session = NULL;
595 g_return_val_if_fail(folder != NULL, NULL);
596 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, NULL);
597 g_return_val_if_fail(folder->account != NULL, NULL);
599 /* Make sure we have a session */
600 if (rfolder->session != NULL) {
601 session = IMAP_SESSION(rfolder->session);
603 imap_reset_uid_lists(folder);
604 session = imap_session_new(folder->account);
609 if (SESSION(session)->sock->state == CONN_DISCONNECTED) {
610 debug_print("IMAP server disconnected\n");
611 session_destroy(SESSION(session));
612 imap_reset_uid_lists(folder);
613 session = imap_session_new(folder->account);
616 /* Make sure session is authenticated */
617 if (!IMAP_SESSION(session)->authenticated)
618 imap_session_authenticate(IMAP_SESSION(session), folder->account);
619 if (!IMAP_SESSION(session)->authenticated) {
620 session_destroy(SESSION(session));
621 rfolder->session = NULL;
625 /* Make sure we have parsed the IMAP namespace */
626 imap_parse_namespace(IMAP_SESSION(session),
627 IMAP_FOLDER(folder));
629 /* I think the point of this code is to avoid sending a
630 * keepalive if we've used the session recently and therefore
631 * think it's still alive. Unfortunately, most of the code
632 * does not yet check for errors on the socket, and so if the
633 * connection drops we don't notice until the timeout expires.
634 * A better solution than sending a NOOP every time would be
635 * for every command to be prepared to retry until it is
636 * successfully sent. -- mbp */
637 if (time(NULL) - session->last_access_time > SESSION_TIMEOUT) {
638 /* verify that the session is still alive */
639 if (imap_cmd_noop(session) != IMAP_SUCCESS) {
640 /* Check if this is the first try to establish a
641 connection, if yes we don't try to reconnect */
642 if (rfolder->session == NULL) {
643 log_warning(_("Connecting %s failed"),
644 folder->account->recv_server);
645 session_destroy(SESSION(session));
648 log_warning(_("IMAP4 connection to %s has been"
649 " disconnected. Reconnecting...\n"),
650 folder->account->recv_server);
651 session_destroy(SESSION(session));
652 /* Clear folders session to make imap_session_get create
653 a new session, because of rfolder->session == NULL
654 it will not try to reconnect again and so avoid an
656 rfolder->session = NULL;
657 session = imap_session_get(folder);
662 rfolder->session = SESSION(session);
664 session->last_access_time = time(NULL);
666 return IMAP_SESSION(session);
669 IMAPSession *imap_session_new(const PrefsAccount *account)
671 IMAPSession *session;
676 /* FIXME: IMAP over SSL only... */
679 port = account->set_imapport ? account->imapport
680 : account->ssl_imap == SSL_TUNNEL ? IMAPS_PORT : IMAP4_PORT;
681 ssl_type = account->ssl_imap;
683 port = account->set_imapport ? account->imapport
687 if (account->set_tunnelcmd) {
688 log_message(_("creating tunneled IMAP4 connection\n"));
690 if ((imap_sock = imap_open_tunnel(account->recv_server,
694 if ((imap_sock = imap_open_tunnel(account->recv_server,
695 account->tunnelcmd)) == NULL)
699 g_return_val_if_fail(account->recv_server != NULL, NULL);
701 log_message(_("creating IMAP4 connection to %s:%d ...\n"),
702 account->recv_server, port);
705 if ((imap_sock = imap_open(account->recv_server, port,
708 if ((imap_sock = imap_open(account->recv_server, port)) == NULL)
713 session = g_new0(IMAPSession, 1);
714 session_init(SESSION(session));
715 SESSION(session)->type = SESSION_IMAP;
716 SESSION(session)->server = g_strdup(account->recv_server);
717 SESSION(session)->sock = imap_sock;
719 SESSION(session)->destroy = imap_session_destroy;
721 session->capability = NULL;
723 session->authenticated = FALSE;
724 session->mbox = NULL;
725 session->cmd_count = 0;
727 /* Only need to log in if the connection was not PREAUTH */
728 if (imap_greeting(session) != IMAP_SUCCESS) {
729 session_destroy(SESSION(session));
734 if (account->ssl_imap == SSL_STARTTLS &&
735 imap_has_capability(session, "STARTTLS")) {
738 ok = imap_cmd_starttls(session);
739 if (ok != IMAP_SUCCESS) {
740 log_warning(_("Can't start TLS session.\n"));
741 session_destroy(SESSION(session));
744 if (!ssl_init_socket_with_method(SESSION(session)->sock, SSL_METHOD_TLSv1)) {
745 session_destroy(SESSION(session));
749 imap_free_capabilities(session);
750 session->authenticated = FALSE;
751 session->uidplus = FALSE;
752 session->cmd_count = 1;
754 if (imap_greeting(session) != IMAP_SUCCESS) {
755 session_destroy(SESSION(session));
760 log_message("IMAP connection is %s-authenticated\n",
761 (session->authenticated) ? "pre" : "un");
766 void imap_session_authenticate(IMAPSession *session, const PrefsAccount *account)
770 g_return_if_fail(account->userid != NULL);
772 pass = account->passwd;
775 tmp_pass = input_dialog_query_password(account->recv_server, account->userid);
778 Xstrdup_a(pass, tmp_pass, {g_free(tmp_pass); return;});
782 if (imap_auth(session, account->userid, pass, account->imap_auth_type) != IMAP_SUCCESS) {
783 imap_cmd_logout(session);
787 session->authenticated = TRUE;
790 void imap_session_destroy(Session *session)
792 imap_free_capabilities(IMAP_SESSION(session));
793 g_free(IMAP_SESSION(session)->mbox);
794 sock_close(session->sock);
795 session->sock = NULL;
798 gchar *imap_fetch_msg(Folder *folder, FolderItem *item, gint uid)
800 gchar *path, *filename;
801 IMAPSession *session;
804 g_return_val_if_fail(folder != NULL, NULL);
805 g_return_val_if_fail(item != NULL, NULL);
807 path = folder_item_get_path(item);
808 if (!is_dir_exist(path))
810 filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(uid), NULL);
813 if (is_file_exist(filename)) {
814 debug_print("message %d has been already cached.\n", uid);
818 session = imap_session_get(folder);
824 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
825 NULL, NULL, NULL, NULL);
826 if (ok != IMAP_SUCCESS) {
827 g_warning("can't select mailbox %s\n", item->path);
832 debug_print("getting message %d...\n", uid);
833 ok = imap_cmd_fetch(session, (guint32)uid, filename);
835 if (ok != IMAP_SUCCESS) {
836 g_warning("can't fetch message %d\n", uid);
844 gint imap_add_msg(Folder *folder, FolderItem *dest, const gchar *file, MsgFlags *flags)
848 MsgFileInfo fileinfo;
850 g_return_val_if_fail(file != NULL, -1);
852 fileinfo.msginfo = NULL;
853 fileinfo.file = (gchar *)file;
854 fileinfo.flags = flags;
855 file_list.data = &fileinfo;
856 file_list.next = NULL;
858 ret = imap_add_msgs(folder, dest, &file_list, NULL);
862 gint imap_add_msgs(Folder *folder, FolderItem *dest, GSList *file_list,
866 IMAPSession *session;
867 guint32 last_uid = 0;
869 MsgFileInfo *fileinfo;
872 g_return_val_if_fail(folder != NULL, -1);
873 g_return_val_if_fail(dest != NULL, -1);
874 g_return_val_if_fail(file_list != NULL, -1);
876 session = imap_session_get(folder);
877 if (!session) return -1;
879 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
881 for (cur = file_list; cur != NULL; cur = cur->next) {
882 IMAPFlags iflags = 0;
885 fileinfo = (MsgFileInfo *)cur->data;
887 if (fileinfo->flags) {
888 if (MSG_IS_MARKED(*fileinfo->flags))
889 iflags |= IMAP_FLAG_FLAGGED;
890 if (MSG_IS_REPLIED(*fileinfo->flags))
891 iflags |= IMAP_FLAG_ANSWERED;
892 if (!MSG_IS_UNREAD(*fileinfo->flags))
893 iflags |= IMAP_FLAG_SEEN;
896 if (dest->stype == F_OUTBOX ||
897 dest->stype == F_QUEUE ||
898 dest->stype == F_DRAFT ||
899 dest->stype == F_TRASH)
900 iflags |= IMAP_FLAG_SEEN;
902 ok = imap_cmd_append(session, destdir, fileinfo->file, iflags,
905 if (ok != IMAP_SUCCESS) {
906 g_warning("can't append message %s\n", fileinfo->file);
911 if (relation != NULL)
912 g_relation_insert(relation, fileinfo->msginfo != NULL ?
913 (gpointer) fileinfo->msginfo : (gpointer) fileinfo,
914 GINT_TO_POINTER(dest->last_num + 1));
915 if (last_uid < new_uid)
924 static gint imap_do_copy_msgs(Folder *folder, FolderItem *dest,
925 MsgInfoList *msglist, GRelation *relation)
929 GSList *seq_list, *cur;
931 IMAPSession *session;
932 gint ok = IMAP_SUCCESS;
933 GRelation *uid_mapping;
936 g_return_val_if_fail(folder != NULL, -1);
937 g_return_val_if_fail(dest != NULL, -1);
938 g_return_val_if_fail(msglist != NULL, -1);
940 session = imap_session_get(folder);
941 if (!session) return -1;
943 msginfo = (MsgInfo *)msglist->data;
945 src = msginfo->folder;
947 g_warning("the src folder is identical to the dest.\n");
951 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
952 NULL, NULL, NULL, NULL);
953 if (ok != IMAP_SUCCESS)
956 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
957 seq_list = imap_get_seq_set_from_msglist(msglist);
958 uid_mapping = g_relation_new(2);
959 g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
961 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
962 gchar *seq_set = (gchar *)cur->data;
964 debug_print("Copying message %s%c[%s] to %s ...\n",
965 src->path, G_DIR_SEPARATOR,
968 ok = imap_cmd_copy(session, seq_set, destdir, uid_mapping);
969 if (ok != IMAP_SUCCESS) {
970 g_relation_destroy(uid_mapping);
971 imap_seq_set_free(seq_list);
976 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
977 MsgInfo *msginfo = (MsgInfo *)cur->data;
980 tuples = g_relation_select(uid_mapping,
981 GINT_TO_POINTER(msginfo->msgnum),
983 if (tuples->len > 0) {
984 gint num = GPOINTER_TO_INT(g_tuples_index(tuples, 0, 1));
985 g_relation_insert(relation, msginfo,
986 GPOINTER_TO_INT(num));
990 g_relation_insert(relation, msginfo,
992 g_tuples_destroy(tuples);
995 imap_seq_set_free(seq_list);
999 if (ok == IMAP_SUCCESS)
1005 gint imap_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1009 g_return_val_if_fail(msginfo != NULL, -1);
1011 msglist.data = msginfo;
1012 msglist.next = NULL;
1014 return imap_copy_msgs(folder, dest, &msglist, NULL);
1017 gint imap_copy_msgs(Folder *folder, FolderItem *dest,
1018 MsgInfoList *msglist, GRelation *relation)
1024 g_return_val_if_fail(folder != NULL, -1);
1025 g_return_val_if_fail(dest != NULL, -1);
1026 g_return_val_if_fail(msglist != NULL, -1);
1028 msginfo = (MsgInfo *)msglist->data;
1029 g_return_val_if_fail(msginfo->folder != NULL, -1);
1031 if (folder == msginfo->folder->folder)
1032 return imap_do_copy_msgs(folder, dest, msglist, relation);
1034 file_list = procmsg_get_message_file_list(msglist);
1035 g_return_val_if_fail(file_list != NULL, -1);
1037 ret = imap_add_msgs(folder, dest, file_list, relation);
1039 procmsg_message_file_list_free(file_list);
1044 gint imap_remove_msg(Folder *folder, FolderItem *item, gint uid)
1047 IMAPSession *session;
1049 MsgNumberList numlist;
1051 g_return_val_if_fail(folder != NULL, -1);
1052 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
1053 g_return_val_if_fail(item != NULL, -1);
1055 session = imap_session_get(folder);
1056 if (!session) return -1;
1058 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
1059 NULL, NULL, NULL, NULL);
1060 if (ok != IMAP_SUCCESS)
1063 numlist.next = NULL;
1064 numlist.data = GINT_TO_POINTER(uid);
1066 ok = imap_set_message_flags
1067 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
1068 &numlist, IMAP_FLAG_DELETED, TRUE);
1069 if (ok != IMAP_SUCCESS) {
1070 log_warning(_("can't set deleted flags: %d\n"), uid);
1074 ok = imap_cmd_expunge(session);
1075 if (ok != IMAP_SUCCESS) {
1076 log_warning(_("can't expunge\n"));
1080 dir = folder_item_get_path(item);
1081 if (is_dir_exist(dir))
1082 remove_numbered_files(dir, uid, uid);
1085 return IMAP_SUCCESS;
1088 gint imap_remove_all_msg(Folder *folder, FolderItem *item)
1091 IMAPSession *session;
1094 g_return_val_if_fail(folder != NULL, -1);
1095 g_return_val_if_fail(item != NULL, -1);
1097 session = imap_session_get(folder);
1098 if (!session) return -1;
1100 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
1101 NULL, NULL, NULL, NULL);
1102 if (ok != IMAP_SUCCESS)
1105 imap_gen_send(session, "STORE 1:* +FLAGS.SILENT (\\Deleted)");
1106 ok = imap_cmd_ok(session, NULL);
1107 if (ok != IMAP_SUCCESS) {
1108 log_warning(_("can't set deleted flags: 1:*\n"));
1112 ok = imap_cmd_expunge(session);
1113 if (ok != IMAP_SUCCESS) {
1114 log_warning(_("can't expunge\n"));
1118 dir = folder_item_get_path(item);
1119 if (is_dir_exist(dir))
1120 remove_all_numbered_files(dir);
1123 return IMAP_SUCCESS;
1126 gboolean imap_is_msg_changed(Folder *folder, FolderItem *item, MsgInfo *msginfo)
1128 /* TODO: properly implement this method */
1132 gint imap_close(Folder *folder, FolderItem *item)
1135 IMAPSession *session;
1137 g_return_val_if_fail(folder != NULL, -1);
1138 g_return_val_if_fail(item != NULL, -1);
1139 g_return_val_if_fail(item->path != NULL, -1);
1141 session = imap_session_get(folder);
1142 if (!session) return -1;
1144 if (session->mbox) {
1145 if (strcmp(item->path, session->mbox))
1148 ok = imap_cmd_close(session);
1149 if (ok != IMAP_SUCCESS)
1150 log_warning(_("can't close folder\n"));
1152 g_free(session->mbox);
1153 session->mbox = NULL;
1161 void imap_scan_tree(Folder *folder)
1163 FolderItem *item = NULL;
1164 IMAPSession *session;
1165 gchar *root_folder = NULL;
1167 g_return_if_fail(folder != NULL);
1168 g_return_if_fail(folder->account != NULL);
1170 session = imap_session_get(folder);
1172 if (!folder->node) {
1173 folder_tree_destroy(folder);
1174 item = folder_item_new(folder, folder->name, NULL);
1175 item->folder = folder;
1176 folder->node = item->node = g_node_new(item);
1181 if (folder->account->imap_dir && *folder->account->imap_dir) {
1182 Xstrdup_a(root_folder, folder->account->imap_dir, return);
1183 strtailchomp(root_folder, '/');
1184 debug_print("IMAP root directory: %s\n", root_folder);
1188 item = FOLDER_ITEM(folder->node->data);
1189 if (!item || ((item->path || root_folder) &&
1190 strcmp2(item->path, root_folder) != 0)) {
1191 folder_tree_destroy(folder);
1192 item = folder_item_new(folder, folder->name, root_folder);
1193 item->folder = folder;
1194 folder->node = item->node = g_node_new(item);
1197 imap_scan_tree_recursive(session, FOLDER_ITEM(folder->node->data));
1198 imap_create_missing_folders(folder);
1201 static gint imap_scan_tree_recursive(IMAPSession *session, FolderItem *item)
1204 IMAPFolder *imapfolder;
1205 FolderItem *new_item;
1206 GSList *item_list, *cur;
1209 gchar *wildcard_path;
1213 g_return_val_if_fail(item != NULL, -1);
1214 g_return_val_if_fail(item->folder != NULL, -1);
1215 g_return_val_if_fail(item->no_sub == FALSE, -1);
1217 folder = item->folder;
1218 imapfolder = IMAP_FOLDER(folder);
1220 separator = imap_get_path_separator(imapfolder, item->path);
1222 if (folder->ui_func)
1223 folder->ui_func(folder, item, folder->ui_func_data);
1226 wildcard[0] = separator;
1229 real_path = imap_get_real_path(imapfolder, item->path);
1233 real_path = g_strdup("");
1236 Xstrcat_a(wildcard_path, real_path, wildcard,
1237 {g_free(real_path); return IMAP_ERROR;});
1238 QUOTE_IF_REQUIRED(wildcard_path, wildcard_path);
1240 imap_gen_send(session, "LIST \"\" %s",
1243 strtailchomp(real_path, separator);
1244 item_list = imap_parse_list(imapfolder, session, real_path, NULL);
1247 node = item->node->children;
1248 while (node != NULL) {
1249 FolderItem *old_item = FOLDER_ITEM(node->data);
1250 GNode *next = node->next;
1253 for (cur = item_list; cur != NULL; cur = cur->next) {
1254 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1255 if (!strcmp2(old_item->path, cur_item->path)) {
1256 new_item = cur_item;
1261 debug_print("folder '%s' not found. removing...\n",
1263 folder_item_remove(old_item);
1265 old_item->no_sub = new_item->no_sub;
1266 old_item->no_select = new_item->no_select;
1267 if (old_item->no_sub == TRUE && node->children) {
1268 debug_print("folder '%s' doesn't have "
1269 "subfolders. removing...\n",
1271 folder_item_remove_children(old_item);
1278 for (cur = item_list; cur != NULL; cur = cur->next) {
1279 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1281 for (node = item->node->children; node != NULL;
1282 node = node->next) {
1283 if (!strcmp2(FOLDER_ITEM(node->data)->path,
1285 new_item = FOLDER_ITEM(node->data);
1286 folder_item_destroy(cur_item);
1292 new_item = cur_item;
1293 debug_print("new folder '%s' found.\n", new_item->path);
1294 folder_item_append(item, new_item);
1297 if (!strcmp(new_item->path, "INBOX")) {
1298 new_item->stype = F_INBOX;
1299 folder->inbox = new_item;
1300 } else if (!item->parent || item->stype == F_INBOX) {
1303 base = g_basename(new_item->path);
1305 if (!folder->outbox && !strcasecmp(base, "Sent")) {
1306 new_item->stype = F_OUTBOX;
1307 folder->outbox = new_item;
1308 } else if (!folder->draft && !strcasecmp(base, "Drafts")) {
1309 new_item->stype = F_DRAFT;
1310 folder->draft = new_item;
1311 } else if (!folder->queue && !strcasecmp(base, "Queue")) {
1312 new_item->stype = F_QUEUE;
1313 folder->queue = new_item;
1314 } else if (!folder->trash && !strcasecmp(base, "Trash")) {
1315 new_item->stype = F_TRASH;
1316 folder->trash = new_item;
1320 if (new_item->no_sub == FALSE)
1321 imap_scan_tree_recursive(session, new_item);
1324 g_slist_free(item_list);
1326 return IMAP_SUCCESS;
1329 static GSList *imap_parse_list(IMAPFolder *folder, IMAPSession *session,
1330 const gchar *real_path, gchar *separator)
1332 gchar buf[IMAPBUFSIZE];
1334 gchar separator_str[16];
1337 gchar *loc_name, *loc_path;
1338 GSList *item_list = NULL;
1340 FolderItem *new_item;
1342 debug_print("getting list of %s ...\n",
1343 *real_path ? real_path : "\"\"");
1345 str = g_string_new(NULL);
1348 if (sock_gets(SESSION(session)->sock, buf, sizeof(buf)) <= 0) {
1349 log_warning(_("error occurred while getting LIST.\n"));
1353 if (buf[0] != '*' || buf[1] != ' ') {
1354 log_print("IMAP4< %s\n", buf);
1357 debug_print("IMAP4< %s\n", buf);
1359 g_string_assign(str, buf);
1361 if (strncmp(p, "LIST ", 5) != 0) continue;
1364 if (*p != '(') continue;
1366 p = strchr_cpy(p, ')', flags, sizeof(flags));
1368 while (*p == ' ') p++;
1370 p = strchr_cpy(p, ' ', separator_str, sizeof(separator_str));
1372 extract_quote(separator_str, '"');
1373 if (!strcmp(separator_str, "NIL"))
1374 separator_str[0] = '\0';
1376 *separator = separator_str[0];
1379 while (*p == ' ') p++;
1380 if (*p == '{' || *p == '"')
1381 p = imap_parse_atom(SESSION(session)->sock, p,
1382 buf, sizeof(buf), str);
1384 strncpy2(buf, p, sizeof(buf));
1385 strtailchomp(buf, separator_str[0]);
1386 if (buf[0] == '\0') continue;
1387 if (!strcmp(buf, real_path)) continue;
1389 if (separator_str[0] != '\0')
1390 subst_char(buf, separator_str[0], '/');
1391 name = g_basename(buf);
1392 if (name[0] == '.') continue;
1394 loc_name = imap_modified_utf7_to_utf8(name);
1395 loc_path = imap_modified_utf7_to_utf8(buf);
1396 new_item = folder_item_new(FOLDER(folder), loc_name, loc_path);
1397 if (strcasestr(flags, "\\Noinferiors") != NULL)
1398 new_item->no_sub = TRUE;
1399 if (strcmp(buf, "INBOX") != 0 &&
1400 strcasestr(flags, "\\Noselect") != NULL)
1401 new_item->no_select = TRUE;
1403 item_list = g_slist_append(item_list, new_item);
1405 debug_print("folder '%s' found.\n", loc_path);
1410 g_string_free(str, TRUE);
1415 gint imap_create_tree(Folder *folder)
1417 g_return_val_if_fail(folder != NULL, -1);
1418 g_return_val_if_fail(folder->node != NULL, -1);
1419 g_return_val_if_fail(folder->node->data != NULL, -1);
1420 g_return_val_if_fail(folder->account != NULL, -1);
1422 imap_scan_tree(folder);
1423 imap_create_missing_folders(folder);
1428 static void imap_create_missing_folders(Folder *folder)
1430 g_return_if_fail(folder != NULL);
1433 folder->inbox = imap_create_special_folder
1434 (folder, F_INBOX, "INBOX");
1436 if (!folder->outbox)
1437 folder->outbox = imap_create_special_folder
1438 (folder, F_OUTBOX, "Sent");
1440 folder->draft = imap_create_special_folder
1441 (folder, F_DRAFT, "Drafts");
1443 folder->queue = imap_create_special_folder
1444 (folder, F_QUEUE, "Queue");
1447 folder->trash = imap_create_special_folder
1448 (folder, F_TRASH, "Trash");
1451 static FolderItem *imap_create_special_folder(Folder *folder,
1452 SpecialFolderItemType stype,
1456 FolderItem *new_item;
1458 g_return_val_if_fail(folder != NULL, NULL);
1459 g_return_val_if_fail(folder->node != NULL, NULL);
1460 g_return_val_if_fail(folder->node->data != NULL, NULL);
1461 g_return_val_if_fail(folder->account != NULL, NULL);
1462 g_return_val_if_fail(name != NULL, NULL);
1464 item = FOLDER_ITEM(folder->node->data);
1465 new_item = imap_create_folder(folder, item, name);
1468 g_warning("Can't create '%s'\n", name);
1469 if (!folder->inbox) return NULL;
1471 new_item = imap_create_folder(folder, folder->inbox, name);
1473 g_warning("Can't create '%s' under INBOX\n", name);
1475 new_item->stype = stype;
1477 new_item->stype = stype;
1482 static gchar *imap_folder_get_path(Folder *folder)
1486 g_return_val_if_fail(folder != NULL, NULL);
1487 g_return_val_if_fail(folder->account != NULL, NULL);
1489 folder_path = g_strconcat(get_imap_cache_dir(),
1491 folder->account->recv_server,
1493 folder->account->userid,
1499 static gchar *imap_item_get_path(Folder *folder, FolderItem *item)
1501 gchar *folder_path, *path;
1503 g_return_val_if_fail(folder != NULL, NULL);
1504 g_return_val_if_fail(item != NULL, NULL);
1505 folder_path = imap_folder_get_path(folder);
1507 g_return_val_if_fail(folder_path != NULL, NULL);
1508 if (folder_path[0] == G_DIR_SEPARATOR) {
1510 path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
1513 path = g_strdup(folder_path);
1516 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1517 folder_path, G_DIR_SEPARATOR_S,
1520 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1523 g_free(folder_path);
1528 FolderItem *imap_create_folder(Folder *folder, FolderItem *parent,
1531 gchar *dirpath, *imap_path;
1532 IMAPSession *session;
1533 FolderItem *new_item;
1539 g_return_val_if_fail(folder != NULL, NULL);
1540 g_return_val_if_fail(folder->account != NULL, NULL);
1541 g_return_val_if_fail(parent != NULL, NULL);
1542 g_return_val_if_fail(name != NULL, NULL);
1544 session = imap_session_get(folder);
1545 if (!session) return NULL;
1547 if (!parent->parent && strcmp(name, "INBOX") == 0)
1548 dirpath = g_strdup(name);
1549 else if (parent->path)
1550 dirpath = g_strconcat(parent->path, "/", name, NULL);
1551 else if ((p = strchr(name, '/')) != NULL && *(p + 1) != '\0')
1552 dirpath = g_strdup(name);
1553 else if (folder->account->imap_dir && *folder->account->imap_dir) {
1556 Xstrdup_a(imap_dir, folder->account->imap_dir, return NULL);
1557 strtailchomp(imap_dir, '/');
1558 dirpath = g_strconcat(imap_dir, "/", name, NULL);
1560 dirpath = g_strdup(name);
1562 /* keep trailing directory separator to create a folder that contains
1564 imap_path = imap_utf8_to_modified_utf7(dirpath);
1565 strtailchomp(dirpath, '/');
1566 Xstrdup_a(new_name, name, {g_free(dirpath); return NULL;});
1567 strtailchomp(new_name, '/');
1568 separator = imap_get_path_separator(IMAP_FOLDER(folder), imap_path);
1569 imap_path_separator_subst(imap_path, separator);
1570 subst_char(new_name, '/', separator);
1572 if (strcmp(name, "INBOX") != 0) {
1575 gboolean exist = FALSE;
1577 argbuf = g_ptr_array_new();
1578 ok = imap_cmd_list(session, NULL, imap_path,
1580 if (ok != IMAP_SUCCESS) {
1581 log_warning(_("can't create mailbox: LIST failed\n"));
1584 ptr_array_free_strings(argbuf);
1585 g_ptr_array_free(argbuf, TRUE);
1589 for (i = 0; i < argbuf->len; i++) {
1591 str = g_ptr_array_index(argbuf, i);
1592 if (!strncmp(str, "LIST ", 5)) {
1597 ptr_array_free_strings(argbuf);
1598 g_ptr_array_free(argbuf, TRUE);
1601 ok = imap_cmd_create(session, imap_path);
1602 if (ok != IMAP_SUCCESS) {
1603 log_warning(_("can't create mailbox\n"));
1611 new_item = folder_item_new(folder, new_name, dirpath);
1612 folder_item_append(parent, new_item);
1616 dirpath = folder_item_get_path(new_item);
1617 if (!is_dir_exist(dirpath))
1618 make_dir_hier(dirpath);
1624 gint imap_rename_folder(Folder *folder, FolderItem *item, const gchar *name)
1628 gchar *real_oldpath;
1629 gchar *real_newpath;
1631 gchar *old_cache_dir;
1632 gchar *new_cache_dir;
1633 IMAPSession *session;
1636 gint exists, recent, unseen;
1637 guint32 uid_validity;
1639 g_return_val_if_fail(folder != NULL, -1);
1640 g_return_val_if_fail(item != NULL, -1);
1641 g_return_val_if_fail(item->path != NULL, -1);
1642 g_return_val_if_fail(name != NULL, -1);
1644 session = imap_session_get(folder);
1645 if (!session) return -1;
1647 real_oldpath = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1649 g_free(session->mbox);
1650 session->mbox = NULL;
1651 ok = imap_cmd_examine(session, "INBOX",
1652 &exists, &recent, &unseen, &uid_validity);
1653 if (ok != IMAP_SUCCESS) {
1654 g_free(real_oldpath);
1658 separator = imap_get_path_separator(IMAP_FOLDER(folder), item->path);
1659 if (strchr(item->path, G_DIR_SEPARATOR)) {
1660 dirpath = g_dirname(item->path);
1661 newpath = g_strconcat(dirpath, G_DIR_SEPARATOR_S, name, NULL);
1664 newpath = g_strdup(name);
1666 real_newpath = imap_utf8_to_modified_utf7(newpath);
1667 imap_path_separator_subst(real_newpath, separator);
1669 ok = imap_cmd_rename(session, real_oldpath, real_newpath);
1670 if (ok != IMAP_SUCCESS) {
1671 log_warning(_("can't rename mailbox: %s to %s\n"),
1672 real_oldpath, real_newpath);
1673 g_free(real_oldpath);
1675 g_free(real_newpath);
1680 item->name = g_strdup(name);
1682 old_cache_dir = folder_item_get_path(item);
1684 paths[0] = g_strdup(item->path);
1686 g_node_traverse(item->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1687 imap_rename_folder_func, paths);
1689 if (is_dir_exist(old_cache_dir)) {
1690 new_cache_dir = folder_item_get_path(item);
1691 if (rename(old_cache_dir, new_cache_dir) < 0) {
1692 FILE_OP_ERROR(old_cache_dir, "rename");
1694 g_free(new_cache_dir);
1697 g_free(old_cache_dir);
1700 g_free(real_oldpath);
1701 g_free(real_newpath);
1706 gint imap_remove_folder(Folder *folder, FolderItem *item)
1709 IMAPSession *session;
1712 gint exists, recent, unseen;
1713 guint32 uid_validity;
1715 g_return_val_if_fail(folder != NULL, -1);
1716 g_return_val_if_fail(item != NULL, -1);
1717 g_return_val_if_fail(item->path != NULL, -1);
1719 session = imap_session_get(folder);
1720 if (!session) return -1;
1722 path = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1724 ok = imap_cmd_examine(session, "INBOX",
1725 &exists, &recent, &unseen, &uid_validity);
1726 if (ok != IMAP_SUCCESS) {
1731 ok = imap_cmd_delete(session, path);
1732 if (ok != IMAP_SUCCESS) {
1733 log_warning(_("can't delete mailbox\n"));
1739 cache_dir = folder_item_get_path(item);
1740 if (is_dir_exist(cache_dir) && remove_dir_recursive(cache_dir) < 0)
1741 g_warning("can't remove directory '%s'\n", cache_dir);
1743 folder_item_remove(item);
1748 static GSList *imap_get_uncached_messages(IMAPSession *session,
1750 MsgNumberList *numlist)
1753 GSList *newlist = NULL;
1754 GSList *llast = NULL;
1757 GSList *seq_list, *cur;
1760 g_return_val_if_fail(session != NULL, NULL);
1761 g_return_val_if_fail(item != NULL, NULL);
1762 g_return_val_if_fail(item->folder != NULL, NULL);
1763 g_return_val_if_fail(FOLDER_CLASS(item->folder) == &imap_class, NULL);
1765 seq_list = imap_get_seq_set_from_numlist(numlist);
1766 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
1767 imapset = cur->data;
1769 if (imap_cmd_envelope(session, imapset)
1771 log_warning(_("can't get envelope\n"));
1775 str = g_string_new(NULL);
1778 if ((tmp = sock_getline(SESSION(session)->sock)) == NULL) {
1779 log_warning(_("error occurred while getting envelope.\n"));
1780 g_string_free(str, TRUE);
1784 if (tmp[0] != '*' || tmp[1] != ' ') {
1785 log_print("IMAP4< %s\n", tmp);
1789 if (strstr(tmp, "FETCH") == NULL) {
1790 log_print("IMAP4< %s\n", tmp);
1794 log_print("IMAP4< %s\n", tmp);
1795 g_string_assign(str, tmp);
1798 msginfo = imap_parse_envelope
1799 (SESSION(session)->sock, item, str);
1801 log_warning(_("can't parse envelope: %s\n"), str->str);
1804 if (item->stype == F_QUEUE) {
1805 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_QUEUED);
1806 } else if (item->stype == F_DRAFT) {
1807 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_DRAFT);
1810 msginfo->folder = item;
1813 llast = newlist = g_slist_append(newlist, msginfo);
1815 llast = g_slist_append(llast, msginfo);
1816 llast = llast->next;
1820 g_string_free(str, TRUE);
1822 imap_seq_set_free(seq_list);
1827 static void imap_delete_all_cached_messages(FolderItem *item)
1831 g_return_if_fail(item != NULL);
1832 g_return_if_fail(item->folder != NULL);
1833 g_return_if_fail(FOLDER_CLASS(item->folder) == &imap_class);
1835 debug_print("Deleting all cached messages...\n");
1837 dir = folder_item_get_path(item);
1838 if (is_dir_exist(dir))
1839 remove_all_numbered_files(dir);
1842 debug_print("done.\n");
1846 static SockInfo *imap_open_tunnel(const gchar *server,
1847 const gchar *tunnelcmd,
1850 static SockInfo *imap_open_tunnel(const gchar *server,
1851 const gchar *tunnelcmd)
1856 if ((sock = sock_connect_cmd(server, tunnelcmd)) == NULL) {
1857 log_warning(_("Can't establish IMAP4 session with: %s\n"),
1862 return imap_init_sock(sock, ssl_type);
1864 return imap_init_sock(sock);
1870 static SockInfo *imap_open(const gchar *server, gushort port,
1873 static SockInfo *imap_open(const gchar *server, gushort port)
1878 if ((sock = sock_connect(server, port)) == NULL) {
1879 log_warning(_("Can't connect to IMAP4 server: %s:%d\n"),
1885 if (ssl_type == SSL_TUNNEL && !ssl_init_socket(sock)) {
1886 log_warning(_("Can't establish IMAP4 session with: %s:%d\n"),
1896 static SockInfo *imap_init_sock(SockInfo *sock, SSLType ssl_type)
1898 static SockInfo *imap_init_sock(SockInfo *sock)
1905 static GList *imap_parse_namespace_str(gchar *str)
1910 IMAPNameSpace *namespace;
1911 GList *ns_list = NULL;
1913 while (*p != '\0') {
1914 /* parse ("#foo" "/") */
1916 while (*p && *p != '(') p++;
1917 if (*p == '\0') break;
1920 while (*p && *p != '"') p++;
1921 if (*p == '\0') break;
1925 while (*p && *p != '"') p++;
1926 if (*p == '\0') break;
1930 while (*p && isspace(*p)) p++;
1931 if (*p == '\0') break;
1932 if (strncmp(p, "NIL", 3) == 0)
1934 else if (*p == '"') {
1937 while (*p && *p != '"') p++;
1938 if (*p == '\0') break;
1943 while (*p && *p != ')') p++;
1944 if (*p == '\0') break;
1947 namespace = g_new(IMAPNameSpace, 1);
1948 namespace->name = g_strdup(name);
1949 namespace->separator = separator ? separator[0] : '\0';
1950 ns_list = g_list_append(ns_list, namespace);
1956 static void imap_parse_namespace(IMAPSession *session, IMAPFolder *folder)
1961 g_return_if_fail(session != NULL);
1962 g_return_if_fail(folder != NULL);
1964 if (folder->ns_personal != NULL ||
1965 folder->ns_others != NULL ||
1966 folder->ns_shared != NULL)
1969 if (!imap_has_capability(session, "NAMESPACE")) {
1970 imap_get_namespace_by_list(session, folder);
1974 if (imap_cmd_namespace(session, &ns_str)
1976 log_warning(_("can't get namespace\n"));
1980 str_array = strsplit_parenthesis(ns_str, '(', ')', 3);
1981 if (str_array == NULL) {
1983 imap_get_namespace_by_list(session, folder);
1987 folder->ns_personal = imap_parse_namespace_str(str_array[0]);
1988 if (str_array[0] && str_array[1])
1989 folder->ns_others = imap_parse_namespace_str(str_array[1]);
1990 if (str_array[0] && str_array[1] && str_array[2])
1991 folder->ns_shared = imap_parse_namespace_str(str_array[2]);
1992 g_strfreev(str_array);
1996 static void imap_get_namespace_by_list(IMAPSession *session, IMAPFolder *folder)
1998 GSList *item_list, *cur;
1999 gchar separator = '\0';
2000 IMAPNameSpace *namespace;
2002 g_return_if_fail(session != NULL);
2003 g_return_if_fail(folder != NULL);
2005 if (folder->ns_personal != NULL ||
2006 folder->ns_others != NULL ||
2007 folder->ns_shared != NULL)
2010 imap_gen_send(session, "LIST \"\" \"\"");
2011 item_list = imap_parse_list(folder, session, "", &separator);
2012 for (cur = item_list; cur != NULL; cur = cur->next)
2013 folder_item_destroy(FOLDER_ITEM(cur->data));
2014 g_slist_free(item_list);
2016 namespace = g_new(IMAPNameSpace, 1);
2017 namespace->name = g_strdup("");
2018 namespace->separator = separator;
2019 folder->ns_personal = g_list_append(NULL, namespace);
2022 static IMAPNameSpace *imap_find_namespace_from_list(GList *ns_list,
2025 IMAPNameSpace *namespace = NULL;
2026 gchar *tmp_path, *name;
2028 if (!path) path = "";
2030 Xstrcat_a(tmp_path, path, "/", return NULL);
2032 for (; ns_list != NULL; ns_list = ns_list->next) {
2033 IMAPNameSpace *tmp_ns = ns_list->data;
2035 Xstrdup_a(name, tmp_ns->name, return namespace);
2036 if (tmp_ns->separator && tmp_ns->separator != '/')
2037 subst_char(name, tmp_ns->separator, '/');
2038 if (strncmp(tmp_path, name, strlen(name)) == 0)
2045 static IMAPNameSpace *imap_find_namespace(IMAPFolder *folder,
2048 IMAPNameSpace *namespace;
2050 g_return_val_if_fail(folder != NULL, NULL);
2052 namespace = imap_find_namespace_from_list(folder->ns_personal, path);
2053 if (namespace) return namespace;
2054 namespace = imap_find_namespace_from_list(folder->ns_others, path);
2055 if (namespace) return namespace;
2056 namespace = imap_find_namespace_from_list(folder->ns_shared, path);
2057 if (namespace) return namespace;
2062 static gchar imap_get_path_separator(IMAPFolder *folder, const gchar *path)
2064 IMAPNameSpace *namespace;
2065 gchar separator = '/';
2067 namespace = imap_find_namespace(folder, path);
2068 if (namespace && namespace->separator)
2069 separator = namespace->separator;
2074 static gchar *imap_get_real_path(IMAPFolder *folder, const gchar *path)
2079 g_return_val_if_fail(folder != NULL, NULL);
2080 g_return_val_if_fail(path != NULL, NULL);
2082 real_path = imap_utf8_to_modified_utf7(path);
2083 separator = imap_get_path_separator(folder, path);
2084 imap_path_separator_subst(real_path, separator);
2089 static gchar *imap_parse_atom(SockInfo *sock, gchar *src,
2090 gchar *dest, gint dest_len, GString *str)
2092 gchar *cur_pos = src;
2095 g_return_val_if_fail(str != NULL, cur_pos);
2097 /* read the next line if the current response buffer is empty */
2098 while (isspace(*cur_pos)) cur_pos++;
2099 while (*cur_pos == '\0') {
2100 if ((nextline = sock_getline(sock)) == NULL)
2102 g_string_assign(str, nextline);
2104 strretchomp(nextline);
2105 /* log_print("IMAP4< %s\n", nextline); */
2106 debug_print("IMAP4< %s\n", nextline);
2109 while (isspace(*cur_pos)) cur_pos++;
2112 if (!strncmp(cur_pos, "NIL", 3)) {
2115 } else if (*cur_pos == '\"') {
2118 p = get_quoted(cur_pos, '\"', dest, dest_len);
2119 cur_pos = p ? p : cur_pos + 2;
2120 } else if (*cur_pos == '{') {
2125 cur_pos = strchr_cpy(cur_pos + 1, '}', buf, sizeof(buf));
2127 g_return_val_if_fail(len > 0, cur_pos);
2129 g_string_truncate(str, 0);
2133 if ((nextline = sock_getline(sock)) == NULL)
2135 line_len += strlen(nextline);
2136 g_string_append(str, nextline);
2138 strretchomp(nextline);
2139 /* log_print("IMAP4< %s\n", nextline); */
2140 debug_print("IMAP4< %s\n", nextline);
2142 } while (line_len < len);
2144 memcpy(dest, cur_pos, MIN(len, dest_len - 1));
2145 dest[MIN(len, dest_len - 1)] = '\0';
2152 static gchar *imap_get_header(SockInfo *sock, gchar *cur_pos, gchar **headers,
2162 g_return_val_if_fail(str != NULL, cur_pos);
2164 while (isspace(*cur_pos)) cur_pos++;
2166 g_return_val_if_fail(*cur_pos == '{', cur_pos);
2168 cur_pos = strchr_cpy(cur_pos + 1, '}', buf, sizeof(buf));
2170 g_return_val_if_fail(len > 0, cur_pos);
2172 g_string_truncate(str, 0);
2176 if ((nextline = sock_getline(sock)) == NULL)
2178 block_len += strlen(nextline);
2179 g_string_append(str, nextline);
2181 strretchomp(nextline);
2182 /* debug_print("IMAP4< %s\n", nextline); */
2184 } while (block_len < len);
2186 debug_print("IMAP4< [contents of BODY.PEEK[HEADER.FIELDS (...)]]\n");
2188 *headers = g_strndup(cur_pos, len);
2191 while (isspace(*cur_pos)) cur_pos++;
2192 while (*cur_pos == '\0') {
2193 if ((nextline = sock_getline(sock)) == NULL)
2195 g_string_assign(str, nextline);
2197 strretchomp(nextline);
2198 debug_print("IMAP4< %s\n", nextline);
2201 while (isspace(*cur_pos)) cur_pos++;
2207 static MsgFlags imap_parse_flags(const gchar *flag_str)
2209 const gchar *p = flag_str;
2210 MsgFlags flags = {0, 0};
2212 flags.perm_flags = MSG_UNREAD;
2214 while ((p = strchr(p, '\\')) != NULL) {
2217 if (g_strncasecmp(p, "Recent", 6) == 0 && MSG_IS_UNREAD(flags)) {
2218 MSG_SET_PERM_FLAGS(flags, MSG_NEW);
2219 } else if (g_strncasecmp(p, "Seen", 4) == 0) {
2220 MSG_UNSET_PERM_FLAGS(flags, MSG_NEW|MSG_UNREAD);
2221 } else if (g_strncasecmp(p, "Deleted", 7) == 0) {
2222 MSG_SET_PERM_FLAGS(flags, MSG_DELETED);
2223 } else if (g_strncasecmp(p, "Flagged", 7) == 0) {
2224 MSG_SET_PERM_FLAGS(flags, MSG_MARKED);
2225 } else if (g_strncasecmp(p, "Answered", 8) == 0) {
2226 MSG_SET_PERM_FLAGS(flags, MSG_REPLIED);
2233 static MsgInfo *imap_parse_envelope(SockInfo *sock, FolderItem *item,
2236 gchar buf[IMAPBUFSIZE];
2237 MsgInfo *msginfo = NULL;
2242 MsgFlags flags = {0, 0}, imap_flags = {0, 0};
2244 g_return_val_if_fail(line_str != NULL, NULL);
2245 g_return_val_if_fail(line_str->str[0] == '*' &&
2246 line_str->str[1] == ' ', NULL);
2248 MSG_SET_TMP_FLAGS(flags, MSG_IMAP);
2249 if (item->stype == F_QUEUE) {
2250 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
2251 } else if (item->stype == F_DRAFT) {
2252 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
2255 cur_pos = line_str->str + 2;
2257 #define PARSE_ONE_ELEMENT(ch) \
2259 cur_pos = strchr_cpy(cur_pos, ch, buf, sizeof(buf)); \
2260 if (cur_pos == NULL) { \
2261 g_warning("cur_pos == NULL\n"); \
2262 procmsg_msginfo_free(msginfo); \
2267 PARSE_ONE_ELEMENT(' ');
2270 PARSE_ONE_ELEMENT(' ');
2271 g_return_val_if_fail(!strcmp(buf, "FETCH"), NULL);
2273 g_return_val_if_fail(*cur_pos == '(', NULL);
2276 while (*cur_pos != '\0' && *cur_pos != ')') {
2277 while (*cur_pos == ' ') cur_pos++;
2279 if (!strncmp(cur_pos, "UID ", 4)) {
2281 uid = strtoul(cur_pos, &cur_pos, 10);
2282 } else if (!strncmp(cur_pos, "FLAGS ", 6)) {
2284 if (*cur_pos != '(') {
2285 g_warning("*cur_pos != '('\n");
2286 procmsg_msginfo_free(msginfo);
2290 PARSE_ONE_ELEMENT(')');
2291 imap_flags = imap_parse_flags(buf);
2292 } else if (!strncmp(cur_pos, "RFC822.SIZE ", 12)) {
2294 size = strtol(cur_pos, &cur_pos, 10);
2295 } else if (!strncmp(cur_pos, "BODY[HEADER.FIELDS ", 19)) {
2299 if (*cur_pos != '(') {
2300 g_warning("*cur_pos != '('\n");
2301 procmsg_msginfo_free(msginfo);
2305 PARSE_ONE_ELEMENT(')');
2306 if (*cur_pos != ']') {
2307 g_warning("*cur_pos != ']'\n");
2308 procmsg_msginfo_free(msginfo);
2313 cur_pos = imap_get_header(sock, cur_pos, &headers,
2315 msginfo = procheader_parse_str(headers, flags, FALSE, FALSE);
2318 g_warning("invalid FETCH response: %s\n", cur_pos);
2324 msginfo->msgnum = uid;
2325 msginfo->size = size;
2326 msginfo->flags.tmp_flags |= imap_flags.tmp_flags;
2327 msginfo->flags.perm_flags = imap_flags.perm_flags;
2333 static gchar *imap_get_flag_str(IMAPFlags flags)
2338 str = g_string_new(NULL);
2340 if (IMAP_IS_SEEN(flags)) g_string_append(str, "\\Seen ");
2341 if (IMAP_IS_ANSWERED(flags)) g_string_append(str, "\\Answered ");
2342 if (IMAP_IS_FLAGGED(flags)) g_string_append(str, "\\Flagged ");
2343 if (IMAP_IS_DELETED(flags)) g_string_append(str, "\\Deleted ");
2344 if (IMAP_IS_DRAFT(flags)) g_string_append(str, "\\Draft");
2346 if (str->len > 0 && str->str[str->len - 1] == ' ')
2347 g_string_truncate(str, str->len - 1);
2350 g_string_free(str, FALSE);
2355 static gint imap_set_message_flags(IMAPSession *session,
2356 MsgNumberList *numlist,
2363 GSList *seq_list, *cur;
2366 flag_str = imap_get_flag_str(flags);
2367 cmd = g_strconcat(is_set ? "+FLAGS.SILENT (" : "-FLAGS.SILENT (",
2368 flag_str, ")", NULL);
2371 seq_list = imap_get_seq_set_from_numlist(numlist);
2372 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
2373 imapset = cur->data;
2375 ok = imap_cmd_store(session, imapset, cmd);
2377 imap_seq_set_free(seq_list);
2383 static gint imap_select(IMAPSession *session, IMAPFolder *folder,
2385 gint *exists, gint *recent, gint *unseen,
2386 guint32 *uid_validity)
2390 gint exists_, recent_, unseen_, uid_validity_;
2392 if (!exists || !recent || !unseen || !uid_validity) {
2393 if (session->mbox && strcmp(session->mbox, path) == 0)
2394 return IMAP_SUCCESS;
2398 uid_validity = &uid_validity_;
2401 g_free(session->mbox);
2402 session->mbox = NULL;
2404 real_path = imap_get_real_path(folder, path);
2405 ok = imap_cmd_select(session, real_path,
2406 exists, recent, unseen, uid_validity);
2407 if (ok != IMAP_SUCCESS)
2408 log_warning(_("can't select folder: %s\n"), real_path);
2410 session->mbox = g_strdup(path);
2411 session->folder_content_changed = FALSE;
2418 #define THROW(err) { ok = err; goto catch; }
2420 static gint imap_status(IMAPSession *session, IMAPFolder *folder,
2422 gint *messages, gint *recent,
2423 guint32 *uid_next, guint32 *uid_validity,
2432 *messages = *recent = *uid_next = *uid_validity = *unseen = 0;
2437 argbuf = g_ptr_array_new();
2439 real_path = imap_get_real_path(folder, path);
2440 QUOTE_IF_REQUIRED(real_path_, real_path);
2441 imap_gen_send(session, "STATUS %s "
2442 "(MESSAGES RECENT UIDNEXT UIDVALIDITY UNSEEN)",
2445 ok = imap_cmd_ok(session, argbuf);
2446 if (ok != IMAP_SUCCESS) THROW(ok);
2448 str = search_array_str(argbuf, "STATUS");
2449 if (!str) THROW(IMAP_ERROR);
2451 str = strchr(str, '(');
2452 if (!str) THROW(IMAP_ERROR);
2454 while (*str != '\0' && *str != ')') {
2455 while (*str == ' ') str++;
2457 if (!strncmp(str, "MESSAGES ", 9)) {
2459 *messages = strtol(str, &str, 10);
2460 } else if (!strncmp(str, "RECENT ", 7)) {
2462 *recent = strtol(str, &str, 10);
2463 } else if (!strncmp(str, "UIDNEXT ", 8)) {
2465 *uid_next = strtoul(str, &str, 10);
2466 } else if (!strncmp(str, "UIDVALIDITY ", 12)) {
2468 *uid_validity = strtoul(str, &str, 10);
2469 } else if (!strncmp(str, "UNSEEN ", 7)) {
2471 *unseen = strtol(str, &str, 10);
2473 g_warning("invalid STATUS response: %s\n", str);
2480 ptr_array_free_strings(argbuf);
2481 g_ptr_array_free(argbuf, TRUE);
2488 static gboolean imap_has_capability(IMAPSession *session, const gchar *cap)
2492 for (p = session->capability; *p != NULL; ++p) {
2493 if (!g_strcasecmp(*p, cap))
2500 void imap_free_capabilities(IMAPSession *session)
2502 g_strfreev(session->capability);
2503 session->capability = NULL;
2506 /* low-level IMAP4rev1 commands */
2508 static gint imap_cmd_authenticate(IMAPSession *session, const gchar *user,
2509 const gchar *pass, IMAPAuthType type)
2516 gchar hexdigest[33];
2520 auth_type = "CRAM-MD5";
2522 imap_gen_send(session, "AUTHENTICATE %s", auth_type);
2523 ok = imap_gen_recv(session, &buf);
2524 if (ok != IMAP_SUCCESS || buf[0] != '+' || buf[1] != ' ') {
2529 challenge = g_malloc(strlen(buf + 2) + 1);
2530 challenge_len = base64_decode(challenge, buf + 2, -1);
2531 challenge[challenge_len] = '\0';
2533 log_print("IMAP< [Decoded: %s]\n", challenge);
2535 md5_hex_hmac(hexdigest, challenge, challenge_len, pass, strlen(pass));
2538 response = g_strdup_printf("%s %s", user, hexdigest);
2539 log_print("IMAP> [Encoded: %s]\n", response);
2540 response64 = g_malloc((strlen(response) + 3) * 2 + 1);
2541 base64_encode(response64, response, strlen(response));
2544 log_print("IMAP> %s\n", response64);
2545 sock_puts(SESSION(session)->sock, response64);
2546 ok = imap_cmd_ok(session, NULL);
2547 if (ok != IMAP_SUCCESS)
2548 log_warning(_("IMAP4 authentication failed.\n"));
2553 static gint imap_cmd_login(IMAPSession *session,
2554 const gchar *user, const gchar *pass)
2556 gchar *user_, *pass_;
2559 QUOTE_IF_REQUIRED(user_, user);
2560 QUOTE_IF_REQUIRED(pass_, pass);
2561 imap_gen_send(session, "LOGIN %s %s", user_, pass_);
2563 ok = imap_cmd_ok(session, NULL);
2564 if (ok != IMAP_SUCCESS)
2565 log_warning(_("IMAP4 login failed.\n"));
2570 static gint imap_cmd_logout(IMAPSession *session)
2572 imap_gen_send(session, "LOGOUT");
2573 return imap_cmd_ok(session, NULL);
2576 static gint imap_cmd_noop(IMAPSession *session)
2578 imap_gen_send(session, "NOOP");
2579 return imap_cmd_ok(session, NULL);
2582 static gint imap_cmd_starttls(IMAPSession *session)
2584 imap_gen_send(session, "STARTTLS");
2585 return imap_cmd_ok(session, NULL);
2588 #define THROW(err) { ok = err; goto catch; }
2590 static gint imap_cmd_namespace(IMAPSession *session, gchar **ns_str)
2596 argbuf = g_ptr_array_new();
2598 imap_gen_send(session, "NAMESPACE");
2599 if ((ok = imap_cmd_ok(session, argbuf)) != IMAP_SUCCESS) THROW(ok);
2601 str = search_array_str(argbuf, "NAMESPACE");
2602 if (!str) THROW(IMAP_ERROR);
2604 *ns_str = g_strdup(str);
2607 ptr_array_free_strings(argbuf);
2608 g_ptr_array_free(argbuf, TRUE);
2615 static gint imap_cmd_list(IMAPSession *session, const gchar *ref,
2616 const gchar *mailbox, GPtrArray *argbuf)
2618 gchar *ref_, *mailbox_;
2620 if (!ref) ref = "\"\"";
2621 if (!mailbox) mailbox = "\"\"";
2623 QUOTE_IF_REQUIRED(ref_, ref);
2624 QUOTE_IF_REQUIRED(mailbox_, mailbox);
2625 imap_gen_send(session, "LIST %s %s", ref_, mailbox_);
2627 return imap_cmd_ok(session, argbuf);
2630 #define THROW goto catch
2632 static gint imap_cmd_do_select(IMAPSession *session, const gchar *folder,
2634 gint *exists, gint *recent, gint *unseen,
2635 guint32 *uid_validity)
2643 *exists = *recent = *unseen = *uid_validity = 0;
2644 argbuf = g_ptr_array_new();
2647 select_cmd = "EXAMINE";
2649 select_cmd = "SELECT";
2651 QUOTE_IF_REQUIRED(folder_, folder);
2652 imap_gen_send(session, "%s %s", select_cmd, folder_);
2654 if ((ok = imap_cmd_ok(session, argbuf)) != IMAP_SUCCESS) THROW;
2656 resp_str = search_array_contain_str(argbuf, "EXISTS");
2658 if (sscanf(resp_str,"%d EXISTS", exists) != 1) {
2659 g_warning("imap_cmd_select(): invalid EXISTS line.\n");
2664 resp_str = search_array_contain_str(argbuf, "RECENT");
2666 if (sscanf(resp_str, "%d RECENT", recent) != 1) {
2667 g_warning("imap_cmd_select(): invalid RECENT line.\n");
2672 resp_str = search_array_contain_str(argbuf, "UIDVALIDITY");
2674 if (sscanf(resp_str, "OK [UIDVALIDITY %u] ", uid_validity)
2676 g_warning("imap_cmd_select(): invalid UIDVALIDITY line.\n");
2681 resp_str = search_array_contain_str(argbuf, "UNSEEN");
2683 if (sscanf(resp_str, "OK [UNSEEN %d] ", unseen) != 1) {
2684 g_warning("imap_cmd_select(): invalid UNSEEN line.\n");
2690 ptr_array_free_strings(argbuf);
2691 g_ptr_array_free(argbuf, TRUE);
2696 static gint imap_cmd_select(IMAPSession *session, const gchar *folder,
2697 gint *exists, gint *recent, gint *unseen,
2698 guint32 *uid_validity)
2700 return imap_cmd_do_select(session, folder, FALSE,
2701 exists, recent, unseen, uid_validity);
2704 static gint imap_cmd_examine(IMAPSession *session, const gchar *folder,
2705 gint *exists, gint *recent, gint *unseen,
2706 guint32 *uid_validity)
2708 return imap_cmd_do_select(session, folder, TRUE,
2709 exists, recent, unseen, uid_validity);
2714 static gint imap_cmd_create(IMAPSession *session, const gchar *folder)
2718 QUOTE_IF_REQUIRED(folder_, folder);
2719 imap_gen_send(session, "CREATE %s", folder_);
2721 return imap_cmd_ok(session, NULL);
2724 static gint imap_cmd_rename(IMAPSession *session, const gchar *old_folder,
2725 const gchar *new_folder)
2727 gchar *old_folder_, *new_folder_;
2729 QUOTE_IF_REQUIRED(old_folder_, old_folder);
2730 QUOTE_IF_REQUIRED(new_folder_, new_folder);
2731 imap_gen_send(session, "RENAME %s %s", old_folder_, new_folder_);
2733 return imap_cmd_ok(session, NULL);
2736 static gint imap_cmd_delete(IMAPSession *session, const gchar *folder)
2740 QUOTE_IF_REQUIRED(folder_, folder);
2741 imap_gen_send(session, "DELETE %s", folder_);
2743 return imap_cmd_ok(session, NULL);
2746 static gint imap_cmd_search(IMAPSession *session, const gchar *criteria, GSList **list)
2752 g_return_val_if_fail(criteria != NULL, IMAP_ERROR);
2753 g_return_val_if_fail(list != NULL, IMAP_ERROR);
2757 argbuf = g_ptr_array_new();
2758 imap_gen_send(session, "UID SEARCH %s", criteria);
2760 ok = imap_cmd_ok(session, argbuf);
2761 if (ok != IMAP_SUCCESS) {
2762 ptr_array_free_strings(argbuf);
2763 g_ptr_array_free(argbuf, TRUE);
2767 if ((uidlist = search_array_str(argbuf, "SEARCH ")) != NULL) {
2768 gchar **strlist, **p;
2770 strlist = g_strsplit(uidlist + 7, " ", 0);
2771 for (p = strlist; *p != NULL; ++p) {
2774 if (sscanf(*p, "%d", &msgnum) == 1)
2775 *list = g_slist_append(*list, GINT_TO_POINTER(msgnum));
2777 g_strfreev(strlist);
2779 ptr_array_free_strings(argbuf);
2780 g_ptr_array_free(argbuf, TRUE);
2782 return IMAP_SUCCESS;
2785 static gint imap_cmd_fetch(IMAPSession *session, guint32 uid, const gchar *filename)
2793 g_return_val_if_fail(filename != NULL, IMAP_ERROR);
2795 imap_gen_send(session, "UID FETCH %d BODY.PEEK[]", uid);
2797 while ((ok = imap_gen_recv(session, &buf)) == IMAP_SUCCESS) {
2798 if (buf[0] != '*' || buf[1] != ' ') {
2802 if (strstr(buf, "FETCH") != NULL) break;
2805 if (ok != IMAP_SUCCESS) {
2810 #define RETURN_ERROR_IF_FAIL(cond) \
2813 return IMAP_ERROR; \
2816 cur_pos = strchr(buf, '{');
2817 RETURN_ERROR_IF_FAIL(cur_pos != NULL);
2818 cur_pos = strchr_cpy(cur_pos + 1, '}', size_str, sizeof(size_str));
2819 RETURN_ERROR_IF_FAIL(cur_pos != NULL);
2820 size_num = atol(size_str);
2821 RETURN_ERROR_IF_FAIL(size_num > 0);
2823 RETURN_ERROR_IF_FAIL(*cur_pos == '\0');
2825 #undef RETURN_ERROR_IF_FAIL
2829 if (recv_bytes_write_to_file(SESSION(session)->sock, size_num, filename) != 0)
2832 if (imap_gen_recv(session, &buf) != IMAP_SUCCESS) {
2837 if (buf[0] == '\0' || buf[strlen(buf) - 1] != ')') {
2843 ok = imap_cmd_ok(session, NULL);
2848 static gint imap_cmd_append(IMAPSession *session, const gchar *destfolder,
2849 const gchar *file, IMAPFlags flags, guint32 *new_uid)
2857 gchar buf[BUFFSIZE];
2862 g_return_val_if_fail(file != NULL, IMAP_ERROR);
2864 size = get_file_size_as_crlf(file);
2865 if ((fp = fopen(file, "rb")) == NULL) {
2866 FILE_OP_ERROR(file, "fopen");
2869 QUOTE_IF_REQUIRED(destfolder_, destfolder);
2870 flag_str = imap_get_flag_str(flags);
2871 imap_gen_send(session, "APPEND %s (%s) {%d}",
2872 destfolder_, flag_str, size);
2875 ok = imap_gen_recv(session, &ret);
2876 if (ok != IMAP_SUCCESS || ret[0] != '+' || ret[1] != ' ') {
2877 log_warning(_("can't append %s to %s\n"), file, destfolder_);
2884 log_print("IMAP4> %s\n", _("(sending file...)"));
2886 while (fgets(buf, sizeof(buf), fp) != NULL) {
2888 if (sock_puts(SESSION(session)->sock, buf) < 0) {
2895 FILE_OP_ERROR(file, "fgets");
2900 sock_puts(SESSION(session)->sock, "");
2904 if (new_uid != NULL)
2907 if (new_uid != NULL && session->uidplus) {
2908 argbuf = g_ptr_array_new();
2910 ok = imap_cmd_ok(session, argbuf);
2911 if (ok != IMAP_SUCCESS)
2912 log_warning(_("can't append message to %s\n"),
2914 else if (argbuf->len > 0) {
2915 resp_str = g_ptr_array_index(argbuf, argbuf->len - 1);
2917 sscanf(resp_str, "%*u OK [APPENDUID %*u %u]",
2919 *new_uid = new_uid_;
2923 ptr_array_free_strings(argbuf);
2924 g_ptr_array_free(argbuf, TRUE);
2926 ok = imap_cmd_ok(session, NULL);
2931 static gint imap_cmd_copy(IMAPSession *session, const gchar *seq_set,
2932 const gchar *destfolder, GRelation *uid_mapping)
2938 g_return_val_if_fail(session != NULL, IMAP_ERROR);
2939 g_return_val_if_fail(seq_set != NULL, IMAP_ERROR);
2940 g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
2942 QUOTE_IF_REQUIRED(destfolder_, destfolder);
2943 imap_gen_send(session, "UID COPY %s %s", seq_set, destfolder_);
2945 reply = g_ptr_array_new();
2947 ok = imap_cmd_ok(session, reply);
2948 if (ok != IMAP_SUCCESS)
2949 log_warning(_("can't copy %s to %s\n"), seq_set, destfolder_);
2953 - split IMAPSets into uids
2954 - g_relation_insert(uid_mapping, olduid, newuid);
2956 else if (imap_has_capability(session, "UIDPLUS") && reply->len > 0)
2957 if ((okmsginfo = g_ptr_array_index(reply, reply->len - 1)) != NULL &&
2958 sscanf(okmsginfo, "%*u OK [COPYUID %*u %u %u]", &olduid, &newuid) == 2 &&
2962 ptr_array_free_strings(reply);
2963 g_ptr_array_free(reply, TRUE);
2967 gint imap_cmd_envelope(IMAPSession *session, IMAPSet set)
2969 static GString *header_fields = NULL;
2971 if (header_fields == NULL) {
2972 const HeaderEntry *headers, *elem;
2974 headers = procheader_get_headernames(FALSE);
2975 header_fields = g_string_new("");
2977 for (elem = headers; elem->name != NULL; ++elem) {
2978 gint namelen = strlen(elem->name);
2980 /* Header fields ending with space are not rfc822 headers */
2981 if (elem->name[namelen - 1] == ' ')
2984 /* strip : at the of header field */
2985 if(elem->name[namelen - 1] == ':')
2991 g_string_sprintfa(header_fields, "%s%.*s",
2992 header_fields->str[0] != '\0' ? " " : "",
2993 namelen, elem->name);
2998 (session, "UID FETCH %s (UID FLAGS RFC822.SIZE BODY.PEEK[HEADER.FIELDS (%s)])",
2999 set, header_fields->str);
3001 return IMAP_SUCCESS;
3004 static gint imap_cmd_store(IMAPSession *session, IMAPSet seq_set,
3009 imap_gen_send(session, "UID STORE %s %s", seq_set, sub_cmd);
3011 if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS) {
3012 log_warning(_("error while imap command: STORE %s %s\n"),
3017 return IMAP_SUCCESS;
3020 static gint imap_cmd_expunge(IMAPSession *session)
3024 imap_gen_send(session, "EXPUNGE");
3025 if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS) {
3026 log_warning(_("error while imap command: EXPUNGE\n"));
3030 return IMAP_SUCCESS;
3033 static gint imap_cmd_close(IMAPSession *session)
3037 imap_gen_send(session, "CLOSE");
3038 if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS)
3039 log_warning(_("error while imap command: CLOSE\n"));
3044 static gint imap_cmd_ok(IMAPSession *session, GPtrArray *argbuf)
3046 gint ok = IMAP_SUCCESS;
3051 while ((ok = imap_gen_recv(session, &buf))
3053 // make sure data is long enough for any substring of buf
3054 data = alloca(strlen(buf) + 1);
3056 // untagged line read
3057 if (buf[0] == '*' && buf[1] == ' ') {
3060 g_ptr_array_add(argbuf, g_strdup(buf + 2));
3062 if (sscanf(buf + 2, "%d %s", &num, data) >= 2) {
3063 if (!strcmp(data, "EXISTS")) {
3064 session->exists = num;
3065 session->folder_content_changed = TRUE;
3068 if(!strcmp(data, "EXPUNGE")) {
3070 session->folder_content_changed = TRUE;
3073 // tagged line with correct tag and OK response found
3074 } else if ((sscanf(buf, "%d %s", &cmd_num, data) >= 2) &&
3075 (cmd_num == session->cmd_count) &&
3076 !strcmp(data, "OK")) {
3078 g_ptr_array_add(argbuf, g_strdup(buf));
3092 static void imap_gen_send(IMAPSession *session, const gchar *format, ...)
3099 va_start(args, format);
3100 tmp = g_strdup_vprintf(format, args);
3103 session->cmd_count++;
3105 buf = g_strdup_printf("%d %s\r\n", session->cmd_count, tmp);
3106 if (!strncasecmp(tmp, "LOGIN ", 6) && (p = strchr(tmp + 6, ' '))) {
3108 log_print("IMAP4> %d %s ********\n", session->cmd_count, tmp);
3110 log_print("IMAP4> %d %s\n", session->cmd_count, tmp);
3112 sock_write_all(SESSION(session)->sock, buf, strlen(buf));
3117 static gint imap_gen_recv(IMAPSession *session, gchar **ret)
3119 if ((*ret = sock_getline(SESSION(session)->sock)) == NULL)
3124 log_print("IMAP4< %s\n", *ret);
3126 return IMAP_SUCCESS;
3130 /* misc utility functions */
3132 static gchar *strchr_cpy(const gchar *src, gchar ch, gchar *dest, gint len)
3137 tmp = strchr(src, ch);
3141 memcpy(dest, src, MIN(tmp - src, len - 1));
3142 dest[MIN(tmp - src, len - 1)] = '\0';
3147 static gchar *get_quoted(const gchar *src, gchar ch, gchar *dest, gint len)
3149 const gchar *p = src;
3152 g_return_val_if_fail(*p == ch, NULL);
3157 while (*p != '\0' && *p != ch) {
3159 if (*p == '\\' && *(p + 1) != '\0')
3168 return (gchar *)(*p == ch ? p + 1 : p);
3171 static gchar *search_array_contain_str(GPtrArray *array, const gchar *str)
3175 for (i = 0; i < array->len; i++) {
3178 tmp = g_ptr_array_index(array, i);
3179 if (strstr(tmp, str) != NULL)
3186 static gchar *search_array_str(GPtrArray *array, const gchar *str)
3193 for (i = 0; i < array->len; i++) {
3196 tmp = g_ptr_array_index(array, i);
3197 if (!strncmp(tmp, str, len))
3204 static void imap_path_separator_subst(gchar *str, gchar separator)
3207 gboolean in_escape = FALSE;
3209 if (!separator || separator == '/') return;
3211 for (p = str; *p != '\0'; p++) {
3212 if (*p == '/' && !in_escape)
3214 else if (*p == '&' && *(p + 1) != '-' && !in_escape)
3216 else if (*p == '-' && in_escape)
3221 static gchar *imap_modified_utf7_to_utf8(const gchar *mutf7_str)
3224 const gchar *from_p;
3227 to = g_malloc(strlen(mutf7_str) + 1);
3230 for (from_p = mutf7_str; *from_p != '\0'; from_p++) {
3231 if (*from_p == '&' && *(from_p + 1) == '-') {
3241 static iconv_t cd = (iconv_t)-1;
3242 static gboolean iconv_ok = TRUE;
3245 size_t norm_utf7_len;
3247 gchar *to_str, *to_p;
3249 gboolean in_escape = FALSE;
3251 if (!iconv_ok) return g_strdup(mutf7_str);
3253 if (cd == (iconv_t)-1) {
3254 cd = iconv_open(conv_get_current_charset_str(), CS_UTF_8);
3255 if (cd == (iconv_t)-1) {
3256 g_warning(_("iconv cannot convert UTF-7 to UTF-8\n"));
3258 return g_strdup(mutf7_str);
3262 norm_utf7 = g_string_new(NULL);
3264 for (p = mutf7_str; *p != '\0'; p++) {
3265 /* replace: '&' -> '+',
3267 escaped ',' -> '/' */
3268 if (!in_escape && *p == '&') {
3269 if (*(p + 1) != '-') {
3270 g_string_append_c(norm_utf7, '+');
3273 g_string_append_c(norm_utf7, '&');
3276 } else if (in_escape && *p == ',') {
3277 g_string_append_c(norm_utf7, '/');
3278 } else if (in_escape && *p == '-') {
3279 g_string_append_c(norm_utf7, '-');
3282 g_string_append_c(norm_utf7, *p);
3286 norm_utf7_p = norm_utf7->str;
3287 norm_utf7_len = norm_utf7->len;
3288 to_len = strlen(mutf7_str) * 5;
3289 to_p = to_str = g_malloc(to_len + 1);
3291 if (iconv(cd, (ICONV_CONST gchar **)&norm_utf7_p, &norm_utf7_len,
3292 &to_p, &to_len) == -1) {
3293 g_warning(_("iconv cannot convert UTF-7 to UTF-8\n"));
3294 g_string_free(norm_utf7, TRUE);
3296 return g_strdup(mutf7_str);
3299 /* second iconv() call for flushing */
3300 iconv(cd, NULL, NULL, &to_p, &to_len);
3301 g_string_free(norm_utf7, TRUE);
3305 #endif /* !HAVE_ICONV */
3308 static gchar *imap_utf8_to_modified_utf7(const gchar *from)
3311 const gchar *from_p;
3314 to = g_malloc(strlen(from) * 2 + 1);
3317 for (from_p = from; *from_p != '\0'; from_p++) {
3318 if (*from_p == '&') {
3328 static iconv_t cd = (iconv_t)-1;
3329 static gboolean iconv_ok = TRUE;
3330 gchar *norm_utf7, *norm_utf7_p;
3331 size_t from_len, norm_utf7_len;
3333 gchar *from_tmp, *to, *p;
3334 gboolean in_escape = FALSE;
3336 if (!iconv_ok) return g_strdup(from);
3338 if (cd == (iconv_t)-1) {
3339 cd = iconv_open("UTF-7", CS_UTF_8);
3340 if (cd == (iconv_t)-1) {
3341 g_warning("iconv cannot convert UTF-8 to UTF-7\n");
3343 return g_strdup(from);
3347 Xstrdup_a(from_tmp, from, return g_strdup(from));
3348 from_len = strlen(from);
3349 norm_utf7_len = from_len * 5;
3350 Xalloca(norm_utf7, norm_utf7_len + 1, return g_strdup(from));
3351 norm_utf7_p = norm_utf7;
3353 #define IS_PRINT(ch) (isprint(ch) && isascii(ch))
3355 while (from_len > 0) {
3356 if (*from_tmp == '+') {
3357 *norm_utf7_p++ = '+';
3358 *norm_utf7_p++ = '-';
3362 } else if (IS_PRINT(*from_tmp)) {
3363 /* printable ascii char */
3364 *norm_utf7_p = *from_tmp;
3370 size_t mb_len = 0, conv_len = 0;
3372 /* unprintable char: convert to UTF-7 */
3374 while (!IS_PRINT(*p) && conv_len < from_len) {
3375 mb_len = mblen(p, MB_LEN_MAX);
3377 g_warning("wrong multibyte sequence\n");
3378 return g_strdup(from);
3384 from_len -= conv_len;
3385 if (iconv(cd, (ICONV_CONST gchar **)&from_tmp,
3387 &norm_utf7_p, &norm_utf7_len) == -1) {
3388 g_warning(_("iconv cannot convert UTF-8 to UTF-7\n"));
3389 return g_strdup(from);
3392 /* second iconv() call for flushing */
3393 iconv(cd, NULL, NULL, &norm_utf7_p, &norm_utf7_len);
3399 *norm_utf7_p = '\0';
3400 to_str = g_string_new(NULL);
3401 for (p = norm_utf7; p < norm_utf7_p; p++) {
3402 /* replace: '&' -> "&-",
3405 BASE64 '/' -> ',' */
3406 if (!in_escape && *p == '&') {
3407 g_string_append(to_str, "&-");
3408 } else if (!in_escape && *p == '+') {
3409 if (*(p + 1) == '-') {
3410 g_string_append_c(to_str, '+');
3413 g_string_append_c(to_str, '&');
3416 } else if (in_escape && *p == '/') {
3417 g_string_append_c(to_str, ',');
3418 } else if (in_escape && *p == '-') {
3419 g_string_append_c(to_str, '-');
3422 g_string_append_c(to_str, *p);
3428 g_string_append_c(to_str, '-');
3432 g_string_free(to_str, FALSE);
3435 #endif /* !HAVE_ICONV */
3438 static GSList *imap_get_seq_set_from_numlist(MsgNumberList *numlist)
3441 GSList *sorted_list, *cur;
3442 guint first, last, next;
3444 GSList *ret_list = NULL;
3446 if (numlist == NULL)
3449 str = g_string_sized_new(256);
3451 sorted_list = g_slist_copy(numlist);
3452 sorted_list = g_slist_sort(sorted_list, g_int_compare);
3454 first = GPOINTER_TO_INT(sorted_list->data);
3456 for (cur = sorted_list; cur != NULL; cur = g_slist_next(cur)) {
3457 last = GPOINTER_TO_INT(cur->data);
3459 next = GPOINTER_TO_INT(cur->next->data);
3463 if (last + 1 != next || next == 0) {
3465 g_string_append_c(str, ',');
3467 g_string_sprintfa(str, "%u", first);
3469 g_string_sprintfa(str, "%u:%u", first, last);
3473 if (str->len > IMAP_CMD_LIMIT) {
3474 ret_str = g_strdup(str->str);
3475 ret_list = g_slist_append(ret_list, ret_str);
3476 g_string_truncate(str, 0);
3482 ret_str = g_strdup(str->str);
3483 ret_list = g_slist_append(ret_list, ret_str);
3486 g_slist_free(sorted_list);
3487 g_string_free(str, TRUE);
3492 static GSList *imap_get_seq_set_from_msglist(MsgInfoList *msglist)
3494 MsgNumberList *numlist = NULL;
3498 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
3499 MsgInfo *msginfo = (MsgInfo *) cur->data;
3501 numlist = g_slist_append(numlist, GINT_TO_POINTER(msginfo->msgnum));
3503 seq_list = imap_get_seq_set_from_numlist(numlist);
3504 g_slist_free(numlist);
3509 static void imap_seq_set_free(GSList *seq_list)
3511 slist_free_strings(seq_list);
3512 g_slist_free(seq_list);
3516 static gboolean imap_rename_folder_func(GNode *node, gpointer data)
3518 FolderItem *item = node->data;
3519 gchar **paths = data;
3520 const gchar *oldpath = paths[0];
3521 const gchar *newpath = paths[1];
3523 gchar *new_itempath;
3526 oldpathlen = strlen(oldpath);
3527 if (strncmp(oldpath, item->path, oldpathlen) != 0) {
3528 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
3532 base = item->path + oldpathlen;
3533 while (*base == G_DIR_SEPARATOR) base++;
3535 new_itempath = g_strdup(newpath);
3537 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
3540 item->path = new_itempath;
3545 static gint get_list_of_uids(Folder *folder, IMAPFolderItem *item, GSList **msgnum_list)
3547 gint ok, nummsgs = 0, lastuid_old;
3548 IMAPSession *session;
3549 GSList *uidlist, *elem;
3552 session = imap_session_get(folder);
3553 g_return_val_if_fail(session != NULL, -1);
3555 ok = imap_select(session, IMAP_FOLDER(folder), item->item.path,
3556 NULL, NULL, NULL, NULL);
3557 if (ok != IMAP_SUCCESS)
3560 cmd_buf = g_strdup_printf("UID %d:*", item->lastuid + 1);
3561 ok = imap_cmd_search(session, cmd_buf, &uidlist);
3564 if (ok == IMAP_SOCKET) {
3565 session_destroy((Session *)session);
3566 ((RemoteFolder *)folder)->session = NULL;
3570 if (ok != IMAP_SUCCESS) {
3574 argbuf = g_ptr_array_new();
3576 cmd_buf = g_strdup_printf("UID FETCH %d:* (UID)", item->lastuid + 1);
3577 imap_gen_send(session, cmd_buf);
3579 ok = imap_cmd_ok(session, argbuf);
3580 if (ok != IMAP_SUCCESS) {
3581 ptr_array_free_strings(argbuf);
3582 g_ptr_array_free(argbuf, TRUE);
3586 for(i = 0; i < argbuf->len; i++) {
3589 if((ret = sscanf(g_ptr_array_index(argbuf, i),
3590 "%*d FETCH (UID %d)", &msgnum)) == 1)
3591 uidlist = g_slist_prepend(uidlist, GINT_TO_POINTER(msgnum));
3593 ptr_array_free_strings(argbuf);
3594 g_ptr_array_free(argbuf, TRUE);
3597 lastuid_old = item->lastuid;
3598 *msgnum_list = g_slist_copy(item->uid_list);
3599 nummsgs = g_slist_length(*msgnum_list);
3600 debug_print("Got %d uids from cache\n", g_slist_length(item->uid_list));
3602 for (elem = uidlist; elem != NULL; elem = g_slist_next(elem)) {
3605 msgnum = GPOINTER_TO_INT(elem->data);
3606 if (msgnum > lastuid_old) {
3607 *msgnum_list = g_slist_prepend(*msgnum_list, GINT_TO_POINTER(msgnum));
3608 item->uid_list = g_slist_prepend(item->uid_list, GINT_TO_POINTER(msgnum));
3611 if(msgnum > item->lastuid)
3612 item->lastuid = msgnum;
3615 g_slist_free(uidlist);
3620 gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list, gboolean *old_uids_valid)
3622 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3623 IMAPSession *session;
3624 gint ok, nummsgs = 0, exists, recent, uid_val, uid_next, unseen;
3627 gboolean selected_folder;
3629 g_return_val_if_fail(folder != NULL, -1);
3630 g_return_val_if_fail(item != NULL, -1);
3631 g_return_val_if_fail(item->item.path != NULL, -1);
3632 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3633 g_return_val_if_fail(folder->account != NULL, -1);
3635 session = imap_session_get(folder);
3636 g_return_val_if_fail(session != NULL, -1);
3638 selected_folder = (session->mbox != NULL) &&
3639 (!strcmp(session->mbox, item->item.path));
3640 if (selected_folder) {
3641 ok = imap_cmd_noop(session);
3642 if (ok != IMAP_SUCCESS)
3644 exists = session->exists;
3646 *old_uids_valid = TRUE;
3648 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
3649 &exists, &recent, &uid_next, &uid_val, &unseen);
3650 if (ok != IMAP_SUCCESS)
3653 if(item->item.mtime == uid_val)
3654 *old_uids_valid = TRUE;
3656 *old_uids_valid = FALSE;
3658 debug_print("Freeing imap uid cache\n");
3660 g_slist_free(item->uid_list);
3661 item->uid_list = NULL;
3663 item->item.mtime = uid_val;
3665 imap_delete_all_cached_messages((FolderItem *)item);
3669 if (!selected_folder)
3670 item->uid_next = uid_next;
3672 /* If old uid_next matches new uid_next we can be sure no message
3673 was added to the folder */
3674 if (( selected_folder && !session->folder_content_changed) ||
3675 (!selected_folder && uid_next == item->uid_next)) {
3676 nummsgs = g_slist_length(item->uid_list);
3678 /* If number of messages is still the same we
3679 know our caches message numbers are still valid,
3680 otherwise if the number of messages has decrease
3681 we discard our cache to start a new scan to find
3682 out which numbers have been removed */
3683 if (exists == nummsgs) {
3684 *msgnum_list = g_slist_copy(item->uid_list);
3686 } else if (exists < nummsgs) {
3687 debug_print("Freeing imap uid cache");
3689 g_slist_free(item->uid_list);
3690 item->uid_list = NULL;
3695 *msgnum_list = NULL;
3699 nummsgs = get_list_of_uids(folder, item, &uidlist);
3701 if (nummsgs != exists) {
3702 /* Cache contains more messages then folder, we have cached
3703 an old UID of a message that was removed and new messages
3704 have been added too, otherwise the uid_next check would
3706 debug_print("Freeing imap uid cache");
3708 g_slist_free(item->uid_list);
3709 item->uid_list = NULL;
3711 g_slist_free(*msgnum_list);
3713 nummsgs = get_list_of_uids(folder, item, &uidlist);
3716 *msgnum_list = uidlist;
3718 dir = folder_item_get_path((FolderItem *)item);
3719 debug_print("removing old messages from %s\n", dir);
3720 remove_numbered_files_not_in_list(dir, *msgnum_list);
3726 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item)
3731 flags.perm_flags = MSG_NEW|MSG_UNREAD;
3732 flags.tmp_flags = 0;
3734 g_return_val_if_fail(item != NULL, NULL);
3735 g_return_val_if_fail(file != NULL, NULL);
3737 if (item->stype == F_QUEUE) {
3738 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
3739 } else if (item->stype == F_DRAFT) {
3740 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
3743 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3744 if (!msginfo) return NULL;
3746 msginfo->folder = item;
3751 GSList *imap_get_msginfos(Folder *folder, FolderItem *item, GSList *msgnum_list)
3753 IMAPSession *session;
3754 MsgInfoList *ret = NULL;
3757 g_return_val_if_fail(folder != NULL, NULL);
3758 g_return_val_if_fail(item != NULL, NULL);
3759 g_return_val_if_fail(msgnum_list != NULL, NULL);
3761 session = imap_session_get(folder);
3762 g_return_val_if_fail(session != NULL, NULL);
3764 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3765 NULL, NULL, NULL, NULL);
3766 if (ok != IMAP_SUCCESS)
3769 if (!(item->stype == F_QUEUE || item->stype == F_DRAFT)) {
3770 ret = g_slist_concat(ret,
3771 imap_get_uncached_messages(
3772 session, item, msgnum_list));
3774 MsgNumberList *sorted_list, *elem;
3775 gint startnum, lastnum;
3777 sorted_list = g_slist_sort(g_slist_copy(msgnum_list), g_int_compare);
3779 startnum = lastnum = GPOINTER_TO_INT(sorted_list->data);
3781 for (elem = sorted_list;; elem = g_slist_next(elem)) {
3785 num = GPOINTER_TO_INT(elem->data);
3787 if (num > lastnum + 1 || elem == NULL) {
3789 for (i = startnum; i <= lastnum; ++i) {
3792 file = imap_fetch_msg(folder, item, i);
3794 MsgInfo *msginfo = imap_parse_msg(file, item);
3795 if (msginfo != NULL) {
3796 msginfo->msgnum = i;
3797 ret = g_slist_append(ret, msginfo);
3811 g_slist_free(sorted_list);
3817 MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid)
3819 MsgInfo *msginfo = NULL;
3820 MsgInfoList *msginfolist;
3821 MsgNumberList numlist;
3823 numlist.next = NULL;
3824 numlist.data = GINT_TO_POINTER(uid);
3826 msginfolist = imap_get_msginfos(folder, item, &numlist);
3827 if (msginfolist != NULL) {
3828 msginfo = msginfolist->data;
3829 g_slist_free(msginfolist);
3835 gboolean imap_scan_required(Folder *folder, FolderItem *_item)
3837 IMAPSession *session;
3838 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3839 gint ok, exists = 0, recent = 0, unseen = 0;
3840 guint32 uid_next, uid_val = 0;
3841 gboolean selected_folder;
3843 g_return_val_if_fail(folder != NULL, FALSE);
3844 g_return_val_if_fail(item != NULL, FALSE);
3845 g_return_val_if_fail(item->item.folder != NULL, FALSE);
3846 g_return_val_if_fail(FOLDER_CLASS(item->item.folder) == &imap_class, FALSE);
3848 if (item->item.path == NULL)
3851 session = imap_session_get(folder);
3852 g_return_val_if_fail(session != NULL, FALSE);
3854 selected_folder = (session->mbox != NULL) &&
3855 (!strcmp(session->mbox, item->item.path));
3856 if (selected_folder) {
3857 ok = imap_cmd_noop(session);
3858 if (ok != IMAP_SUCCESS)
3861 if (session->folder_content_changed)
3864 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
3865 &exists, &recent, &uid_next, &uid_val, &unseen);
3866 if (ok != IMAP_SUCCESS)
3869 if ((uid_next != item->uid_next) || (exists < item->item.total_msgs))
3876 void imap_change_flags(Folder *folder, FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
3878 IMAPSession *session;
3879 IMAPFlags flags_set = 0, flags_unset = 0;
3880 gint ok = IMAP_SUCCESS;
3881 MsgNumberList numlist;
3883 g_return_if_fail(folder != NULL);
3884 g_return_if_fail(folder->klass == &imap_class);
3885 g_return_if_fail(item != NULL);
3886 g_return_if_fail(item->folder == folder);
3887 g_return_if_fail(msginfo != NULL);
3888 g_return_if_fail(msginfo->folder == item);
3890 session = imap_session_get(folder);
3891 if (!session) return;
3893 if ((ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
3894 NULL, NULL, NULL, NULL)) != IMAP_SUCCESS)
3897 if (!MSG_IS_MARKED(msginfo->flags) && (newflags & MSG_MARKED))
3898 flags_set |= IMAP_FLAG_FLAGGED;
3899 if ( MSG_IS_MARKED(msginfo->flags) && !(newflags & MSG_MARKED))
3900 flags_unset |= IMAP_FLAG_FLAGGED;
3902 if (!MSG_IS_UNREAD(msginfo->flags) && (newflags & MSG_UNREAD))
3903 flags_unset |= IMAP_FLAG_SEEN;
3904 if ( MSG_IS_UNREAD(msginfo->flags) && !(newflags & MSG_UNREAD))
3905 flags_set |= IMAP_FLAG_SEEN;
3907 if (!MSG_IS_REPLIED(msginfo->flags) && (newflags & MSG_REPLIED))
3908 flags_set |= IMAP_FLAG_ANSWERED;
3909 if ( MSG_IS_REPLIED(msginfo->flags) && !(newflags & MSG_REPLIED))
3910 flags_set |= IMAP_FLAG_ANSWERED;
3912 numlist.next = NULL;
3913 numlist.data = GINT_TO_POINTER(msginfo->msgnum);
3916 ok = imap_set_message_flags(session, &numlist, flags_set, TRUE);
3917 if (ok != IMAP_SUCCESS) return;
3921 ok = imap_set_message_flags(session, &numlist, flags_unset, FALSE);
3922 if (ok != IMAP_SUCCESS) return;
3925 msginfo->flags.perm_flags = newflags;