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_modified_utf7_to_locale (const gchar *mutf7_str);
396 static gchar *imap_locale_to_modified_utf7 (const gchar *from);
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 static GSList *imap_get_msginfos (Folder *folder,
409 GSList *msgnum_list);
410 static MsgInfo *imap_get_msginfo (Folder *folder,
413 static gboolean imap_check_msgnum_validity (Folder *folder,
415 static void imap_change_flags (Folder *folder,
418 MsgPermFlags newflags);
419 static gchar *imap_folder_get_path (Folder *folder);
420 static gchar *imap_item_get_path (Folder *folder,
423 FolderClass imap_class =
429 /* Folder functions */
435 /* FolderItem functions */
436 imap_folder_item_new,
437 imap_folder_item_destroy,
447 imap_check_msgnum_validity,
449 /* Message functions */
463 FolderClass *imap_get_class(void)
468 Folder *imap_folder_new(const gchar *name, const gchar *path)
472 folder = (Folder *)g_new0(IMAPFolder, 1);
473 folder->klass = &imap_class;
474 imap_folder_init(folder, name, path);
479 void imap_folder_destroy(Folder *folder)
483 dir = imap_folder_get_path(folder);
484 if (is_dir_exist(dir))
485 remove_dir_recursive(dir);
488 folder_remote_folder_destroy(REMOTE_FOLDER(folder));
491 static void imap_folder_init(Folder *folder, const gchar *name,
494 folder_remote_folder_init((Folder *)folder, name, path);
497 static FolderItem *imap_folder_item_new(Folder *folder)
499 IMAPFolderItem *item;
501 item = g_new0(IMAPFolderItem, 1);
504 item->uid_list = NULL;
506 return (FolderItem *)item;
509 static void imap_folder_item_destroy(Folder *folder, FolderItem *_item)
511 IMAPFolderItem *item = (IMAPFolderItem *)_item;
513 g_return_if_fail(item != NULL);
514 g_slist_free(item->uid_list);
519 static gboolean imap_reset_uid_lists_func(GNode *node, gpointer data)
521 IMAPFolderItem *item = (IMAPFolderItem *)node->data;
525 g_slist_free(item->uid_list);
526 item->uid_list = NULL;
531 static void imap_reset_uid_lists(Folder *folder)
533 if(folder->node == NULL)
536 /* Destroy all uid lists and rest last uid */
537 g_node_traverse(folder->node, G_IN_ORDER, G_TRAVERSE_ALL, -1, imap_reset_uid_lists_func, NULL);
540 /* Send CAPABILITY, and examine the server's response to see whether this
541 * connection is pre-authenticated or not and build a list of CAPABILITIES. */
542 static gint imap_greeting(IMAPSession *session)
547 imap_gen_send(session, "CAPABILITY");
549 argbuf = g_ptr_array_new();
551 if (imap_cmd_ok(session, argbuf) != IMAP_SUCCESS ||
552 ((capstr = search_array_str(argbuf, "CAPABILITY ")) == NULL)) {
553 ptr_array_free_strings(argbuf);
554 g_ptr_array_free(argbuf, TRUE);
558 session->authenticated = search_array_str(argbuf, "PREAUTH") != NULL;
560 capstr += strlen("CAPABILITY ");
562 IMAP_SESSION(session)->capability = g_strsplit(capstr, " ", 0);
564 ptr_array_free_strings(argbuf);
565 g_ptr_array_free(argbuf, TRUE);
567 if (imap_has_capability(session, "UIDPLUS"))
568 session->uidplus = TRUE;
573 static gint imap_auth(IMAPSession *session, const gchar *user, const gchar *pass,
578 if (type == 0 || type == IMAP_AUTH_LOGIN)
579 ok = imap_cmd_login(session, user, pass);
581 ok = imap_cmd_authenticate(session, user, pass, type);
583 if (ok == IMAP_SUCCESS)
584 session->authenticated = TRUE;
589 static IMAPSession *imap_session_get(Folder *folder)
591 RemoteFolder *rfolder = REMOTE_FOLDER(folder);
592 IMAPSession *session = NULL;
594 g_return_val_if_fail(folder != NULL, NULL);
595 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, NULL);
596 g_return_val_if_fail(folder->account != NULL, NULL);
598 /* Make sure we have a session */
599 if (rfolder->session != NULL) {
600 session = IMAP_SESSION(rfolder->session);
602 imap_reset_uid_lists(folder);
603 session = imap_session_new(folder->account);
608 if (SESSION(session)->sock->state == CONN_DISCONNECTED) {
609 debug_print("IMAP server disconnected\n");
610 session_destroy(SESSION(session));
611 imap_reset_uid_lists(folder);
612 session = imap_session_new(folder->account);
615 /* Make sure session is authenticated */
616 if (!IMAP_SESSION(session)->authenticated)
617 imap_session_authenticate(IMAP_SESSION(session), folder->account);
618 if (!IMAP_SESSION(session)->authenticated) {
619 session_destroy(SESSION(session));
620 rfolder->session = NULL;
624 /* Make sure we have parsed the IMAP namespace */
625 imap_parse_namespace(IMAP_SESSION(session),
626 IMAP_FOLDER(folder));
628 /* I think the point of this code is to avoid sending a
629 * keepalive if we've used the session recently and therefore
630 * think it's still alive. Unfortunately, most of the code
631 * does not yet check for errors on the socket, and so if the
632 * connection drops we don't notice until the timeout expires.
633 * A better solution than sending a NOOP every time would be
634 * for every command to be prepared to retry until it is
635 * successfully sent. -- mbp */
636 if (time(NULL) - session->last_access_time > SESSION_TIMEOUT) {
637 /* verify that the session is still alive */
638 if (imap_cmd_noop(session) != IMAP_SUCCESS) {
639 /* Check if this is the first try to establish a
640 connection, if yes we don't try to reconnect */
641 if (rfolder->session == NULL) {
642 log_warning(_("Connecting %s failed"),
643 folder->account->recv_server);
644 session_destroy(SESSION(session));
647 log_warning(_("IMAP4 connection to %s has been"
648 " disconnected. Reconnecting...\n"),
649 folder->account->recv_server);
650 session_destroy(SESSION(session));
651 /* Clear folders session to make imap_session_get create
652 a new session, because of rfolder->session == NULL
653 it will not try to reconnect again and so avoid an
655 rfolder->session = NULL;
656 session = imap_session_get(folder);
661 rfolder->session = SESSION(session);
663 session->last_access_time = time(NULL);
665 return IMAP_SESSION(session);
668 IMAPSession *imap_session_new(const PrefsAccount *account)
670 IMAPSession *session;
675 /* FIXME: IMAP over SSL only... */
678 port = account->set_imapport ? account->imapport
679 : account->ssl_imap == SSL_TUNNEL ? IMAPS_PORT : IMAP4_PORT;
680 ssl_type = account->ssl_imap;
682 port = account->set_imapport ? account->imapport
686 if (account->set_tunnelcmd) {
687 log_message(_("creating tunneled IMAP4 connection\n"));
689 if ((imap_sock = imap_open_tunnel(account->recv_server,
693 if ((imap_sock = imap_open_tunnel(account->recv_server,
694 account->tunnelcmd)) == NULL)
698 g_return_val_if_fail(account->recv_server != NULL, NULL);
700 log_message(_("creating IMAP4 connection to %s:%d ...\n"),
701 account->recv_server, port);
704 if ((imap_sock = imap_open(account->recv_server, port,
707 if ((imap_sock = imap_open(account->recv_server, port)) == NULL)
712 session = g_new0(IMAPSession, 1);
713 session_init(SESSION(session));
714 SESSION(session)->type = SESSION_IMAP;
715 SESSION(session)->server = g_strdup(account->recv_server);
716 SESSION(session)->sock = imap_sock;
718 SESSION(session)->destroy = imap_session_destroy;
720 session->capability = NULL;
722 session->authenticated = FALSE;
723 session->mbox = NULL;
724 session->cmd_count = 0;
726 /* Only need to log in if the connection was not PREAUTH */
727 if (imap_greeting(session) != IMAP_SUCCESS) {
728 session_destroy(SESSION(session));
733 if (account->ssl_imap == SSL_STARTTLS &&
734 imap_has_capability(session, "STARTTLS")) {
737 ok = imap_cmd_starttls(session);
738 if (ok != IMAP_SUCCESS) {
739 log_warning(_("Can't start TLS session.\n"));
740 session_destroy(SESSION(session));
743 if (!ssl_init_socket_with_method(SESSION(session)->sock, SSL_METHOD_TLSv1)) {
744 session_destroy(SESSION(session));
748 imap_free_capabilities(session);
749 session->authenticated = FALSE;
750 session->uidplus = FALSE;
751 session->cmd_count = 1;
753 if (imap_greeting(session) != IMAP_SUCCESS) {
754 session_destroy(SESSION(session));
759 log_message("IMAP connection is %s-authenticated\n",
760 (session->authenticated) ? "pre" : "un");
765 void imap_session_authenticate(IMAPSession *session, const PrefsAccount *account)
769 g_return_if_fail(account->userid != NULL);
771 pass = account->passwd;
774 tmp_pass = input_dialog_query_password(account->recv_server, account->userid);
777 Xstrdup_a(pass, tmp_pass, {g_free(tmp_pass); return;});
781 if (imap_auth(session, account->userid, pass, account->imap_auth_type) != IMAP_SUCCESS) {
782 imap_cmd_logout(session);
786 session->authenticated = TRUE;
789 void imap_session_destroy(Session *session)
791 imap_free_capabilities(IMAP_SESSION(session));
792 g_free(IMAP_SESSION(session)->mbox);
793 sock_close(session->sock);
794 session->sock = NULL;
797 gchar *imap_fetch_msg(Folder *folder, FolderItem *item, gint uid)
799 gchar *path, *filename;
800 IMAPSession *session;
803 g_return_val_if_fail(folder != NULL, NULL);
804 g_return_val_if_fail(item != NULL, NULL);
806 path = folder_item_get_path(item);
807 if (!is_dir_exist(path))
809 filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(uid), NULL);
812 if (is_file_exist(filename)) {
813 debug_print("message %d has been already cached.\n", uid);
817 session = imap_session_get(folder);
823 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
824 NULL, NULL, NULL, NULL);
825 if (ok != IMAP_SUCCESS) {
826 g_warning("can't select mailbox %s\n", item->path);
831 debug_print("getting message %d...\n", uid);
832 ok = imap_cmd_fetch(session, (guint32)uid, filename);
834 if (ok != IMAP_SUCCESS) {
835 g_warning("can't fetch message %d\n", uid);
843 gint imap_add_msg(Folder *folder, FolderItem *dest, const gchar *file, MsgFlags *flags)
847 MsgFileInfo fileinfo;
849 g_return_val_if_fail(file != NULL, -1);
851 fileinfo.msginfo = NULL;
852 fileinfo.file = (gchar *)file;
853 fileinfo.flags = flags;
854 file_list.data = &fileinfo;
855 file_list.next = NULL;
857 ret = imap_add_msgs(folder, dest, &file_list, NULL);
861 gint imap_add_msgs(Folder *folder, FolderItem *dest, GSList *file_list,
865 IMAPSession *session;
866 guint32 last_uid = 0;
868 MsgFileInfo *fileinfo;
871 g_return_val_if_fail(folder != NULL, -1);
872 g_return_val_if_fail(dest != NULL, -1);
873 g_return_val_if_fail(file_list != NULL, -1);
875 session = imap_session_get(folder);
876 if (!session) return -1;
878 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
880 for (cur = file_list; cur != NULL; cur = cur->next) {
881 IMAPFlags iflags = 0;
884 fileinfo = (MsgFileInfo *)cur->data;
886 if (fileinfo->flags) {
887 if (MSG_IS_MARKED(*fileinfo->flags))
888 iflags |= IMAP_FLAG_FLAGGED;
889 if (MSG_IS_REPLIED(*fileinfo->flags))
890 iflags |= IMAP_FLAG_ANSWERED;
891 if (!MSG_IS_UNREAD(*fileinfo->flags))
892 iflags |= IMAP_FLAG_SEEN;
895 if (dest->stype == F_OUTBOX ||
896 dest->stype == F_QUEUE ||
897 dest->stype == F_DRAFT ||
898 dest->stype == F_TRASH)
899 iflags |= IMAP_FLAG_SEEN;
901 ok = imap_cmd_append(session, destdir, fileinfo->file, iflags,
904 if (ok != IMAP_SUCCESS) {
905 g_warning("can't append message %s\n", fileinfo->file);
910 if (relation != NULL)
911 g_relation_insert(relation, fileinfo->msginfo != NULL ?
912 (gpointer) fileinfo->msginfo : (gpointer) fileinfo,
913 GINT_TO_POINTER(dest->last_num + 1));
914 if (last_uid < new_uid)
923 static gint imap_do_copy_msgs(Folder *folder, FolderItem *dest,
924 MsgInfoList *msglist, GRelation *relation)
927 GSList *seq_list, *cur;
929 IMAPSession *session;
930 gint ok = IMAP_SUCCESS;
931 GRelation *uid_mapping;
934 g_return_val_if_fail(folder != NULL, -1);
935 g_return_val_if_fail(dest != NULL, -1);
936 g_return_val_if_fail(msglist != NULL, -1);
938 session = imap_session_get(folder);
939 if (!session) return -1;
941 msginfo = (MsgInfo *)msglist->data;
942 if (msginfo->folder == dest) {
943 g_warning("the src folder is identical to the dest.\n");
947 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
948 NULL, NULL, NULL, NULL);
949 if (ok != IMAP_SUCCESS)
952 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
953 seq_list = imap_get_seq_set_from_msglist(msglist);
954 uid_mapping = g_relation_new(2);
955 g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
957 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
958 gchar *seq_set = (gchar *)cur->data;
960 debug_print("Copying message %s%c[%s] to %s ...\n",
961 msginfo->folder->path, G_DIR_SEPARATOR,
964 ok = imap_cmd_copy(session, seq_set, destdir, uid_mapping);
965 if (ok != IMAP_SUCCESS) {
966 g_relation_destroy(uid_mapping);
967 imap_seq_set_free(seq_list);
972 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
973 MsgInfo *msginfo = (MsgInfo *)cur->data;
976 tuples = g_relation_select(uid_mapping,
977 GINT_TO_POINTER(msginfo->msgnum),
979 if (tuples->len > 0) {
980 gint num = GPOINTER_TO_INT(g_tuples_index(tuples, 0, 1));
981 g_relation_insert(relation, msginfo,
982 GPOINTER_TO_INT(num));
986 g_relation_insert(relation, msginfo,
988 g_tuples_destroy(tuples);
991 imap_seq_set_free(seq_list);
995 if (ok == IMAP_SUCCESS)
1001 gint imap_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1005 g_return_val_if_fail(msginfo != NULL, -1);
1007 msglist.data = msginfo;
1008 msglist.next = NULL;
1010 return imap_copy_msgs(folder, dest, &msglist, NULL);
1013 gint imap_copy_msgs(Folder *folder, FolderItem *dest,
1014 MsgInfoList *msglist, GRelation *relation)
1020 g_return_val_if_fail(folder != NULL, -1);
1021 g_return_val_if_fail(dest != NULL, -1);
1022 g_return_val_if_fail(msglist != NULL, -1);
1024 msginfo = (MsgInfo *)msglist->data;
1025 g_return_val_if_fail(msginfo->folder != NULL, -1);
1027 if (folder == msginfo->folder->folder)
1028 return imap_do_copy_msgs(folder, dest, msglist, relation);
1030 file_list = procmsg_get_message_file_list(msglist);
1031 g_return_val_if_fail(file_list != NULL, -1);
1033 ret = imap_add_msgs(folder, dest, file_list, relation);
1035 procmsg_message_file_list_free(file_list);
1040 gint imap_remove_msg(Folder *folder, FolderItem *item, gint uid)
1043 IMAPSession *session;
1045 MsgNumberList numlist;
1047 g_return_val_if_fail(folder != NULL, -1);
1048 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
1049 g_return_val_if_fail(item != NULL, -1);
1051 session = imap_session_get(folder);
1052 if (!session) return -1;
1054 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
1055 NULL, NULL, NULL, NULL);
1056 if (ok != IMAP_SUCCESS)
1059 numlist.next = NULL;
1060 numlist.data = GINT_TO_POINTER(uid);
1062 ok = imap_set_message_flags
1063 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
1064 &numlist, IMAP_FLAG_DELETED, TRUE);
1065 if (ok != IMAP_SUCCESS) {
1066 log_warning(_("can't set deleted flags: %d\n"), uid);
1070 ok = imap_cmd_expunge(session);
1071 if (ok != IMAP_SUCCESS) {
1072 log_warning(_("can't expunge\n"));
1076 dir = folder_item_get_path(item);
1077 if (is_dir_exist(dir))
1078 remove_numbered_files(dir, uid, uid);
1081 return IMAP_SUCCESS;
1084 gint imap_remove_all_msg(Folder *folder, FolderItem *item)
1086 gint exists, recent, unseen;
1087 guint32 uid_validity;
1089 IMAPSession *session;
1092 g_return_val_if_fail(folder != NULL, -1);
1093 g_return_val_if_fail(item != NULL, -1);
1095 session = imap_session_get(folder);
1096 if (!session) return -1;
1098 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
1099 &exists, &recent, &unseen, &uid_validity);
1100 if (ok != IMAP_SUCCESS)
1103 return IMAP_SUCCESS;
1105 imap_gen_send(session,
1106 "STORE 1:%d +FLAGS.SILENT (\\Deleted)", exists);
1107 ok = imap_cmd_ok(session, NULL);
1108 if (ok != IMAP_SUCCESS) {
1109 log_warning(_("can't set deleted flags: 1:%d\n"), exists);
1113 ok = imap_cmd_expunge(session);
1114 if (ok != IMAP_SUCCESS) {
1115 log_warning(_("can't expunge\n"));
1119 dir = folder_item_get_path(item);
1120 if (is_dir_exist(dir))
1121 remove_all_numbered_files(dir);
1124 return IMAP_SUCCESS;
1127 gboolean imap_is_msg_changed(Folder *folder, FolderItem *item, MsgInfo *msginfo)
1129 /* TODO: properly implement this method */
1133 gint imap_close(Folder *folder, FolderItem *item)
1136 IMAPSession *session;
1138 g_return_val_if_fail(folder != NULL, -1);
1140 session = imap_session_get(folder);
1141 if (!session) return -1;
1143 if (session->mbox) {
1144 if (strcmp(item->path, session->mbox))
1147 ok = imap_cmd_close(session);
1148 if (ok != IMAP_SUCCESS)
1149 log_warning(_("can't close folder\n"));
1151 g_free(session->mbox);
1152 session->mbox = NULL;
1160 void imap_scan_tree(Folder *folder)
1163 IMAPSession *session;
1164 gchar *root_folder = NULL;
1166 g_return_if_fail(folder != NULL);
1167 g_return_if_fail(folder->account != NULL);
1169 session = imap_session_get(folder);
1171 if (!folder->node) {
1172 folder_tree_destroy(folder);
1173 item = folder_item_new(folder, folder->name, NULL);
1174 item->folder = folder;
1175 folder->node = g_node_new(item);
1180 if (folder->account->imap_dir && *folder->account->imap_dir) {
1181 Xstrdup_a(root_folder, folder->account->imap_dir, return);
1182 strtailchomp(root_folder, '/');
1183 debug_print("IMAP root directory: %s\n", root_folder);
1186 item = folder_item_new(folder, folder->name, root_folder);
1187 item->folder = folder;
1188 item->no_select = TRUE;
1189 folder->node = g_node_new(item);
1191 imap_scan_tree_recursive(session, item);
1193 imap_create_missing_folders(folder);
1196 static gint imap_scan_tree_recursive(IMAPSession *session, FolderItem *item)
1199 IMAPFolder *imapfolder;
1200 FolderItem *new_item;
1201 GSList *item_list, *cur;
1203 gchar *wildcard_path;
1207 g_return_val_if_fail(item != NULL, -1);
1208 g_return_val_if_fail(item->folder != NULL, -1);
1209 g_return_val_if_fail(item->no_sub == FALSE, -1);
1211 folder = FOLDER(item->folder);
1212 imapfolder = IMAP_FOLDER(folder);
1214 separator = imap_get_path_separator(imapfolder, item->path);
1216 if (item->folder->ui_func)
1217 item->folder->ui_func(folder, item, folder->ui_func_data);
1220 wildcard[0] = separator;
1223 real_path = imap_get_real_path(imapfolder, item->path);
1227 real_path = g_strdup("");
1230 Xstrcat_a(wildcard_path, real_path, wildcard,
1231 {g_free(real_path); return IMAP_ERROR;});
1232 QUOTE_IF_REQUIRED(wildcard_path, wildcard_path);
1234 imap_gen_send(session, "LIST \"\" %s",
1237 strtailchomp(real_path, separator);
1238 item_list = imap_parse_list(imapfolder, session, real_path, NULL);
1241 for (cur = item_list; cur != NULL; cur = cur->next) {
1242 new_item = cur->data;
1243 if (!strcmp(new_item->path, "INBOX")) {
1244 if (!folder->inbox) {
1245 new_item->stype = F_INBOX;
1246 item->folder->inbox = new_item;
1248 folder_item_destroy(new_item);
1251 } else if (!item->parent || item->stype == F_INBOX) {
1254 base = g_basename(new_item->path);
1256 if (!folder->outbox && !strcasecmp(base, "Sent")) {
1257 new_item->stype = F_OUTBOX;
1258 folder->outbox = new_item;
1259 } else if (!folder->draft && !strcasecmp(base, "Drafts")) {
1260 new_item->stype = F_DRAFT;
1261 folder->draft = new_item;
1262 } else if (!folder->queue && !strcasecmp(base, "Queue")) {
1263 new_item->stype = F_QUEUE;
1264 folder->queue = new_item;
1265 } else if (!folder->trash && !strcasecmp(base, "Trash")) {
1266 new_item->stype = F_TRASH;
1267 folder->trash = new_item;
1270 folder_item_append(item, new_item);
1271 if (new_item->no_sub == FALSE)
1272 imap_scan_tree_recursive(session, new_item);
1275 return IMAP_SUCCESS;
1278 static GSList *imap_parse_list(IMAPFolder *folder, IMAPSession *session,
1279 const gchar *real_path, gchar *separator)
1281 gchar buf[IMAPBUFSIZE];
1283 gchar separator_str[16];
1286 gchar *loc_name, *loc_path;
1287 GSList *item_list = NULL;
1289 FolderItem *new_item;
1291 debug_print("getting list of %s ...\n",
1292 *real_path ? real_path : "\"\"");
1294 str = g_string_new(NULL);
1297 if (sock_gets(SESSION(session)->sock, buf, sizeof(buf)) <= 0) {
1298 log_warning(_("error occurred while getting LIST.\n"));
1302 if (buf[0] != '*' || buf[1] != ' ') {
1303 log_print("IMAP4< %s\n", buf);
1306 debug_print("IMAP4< %s\n", buf);
1308 g_string_assign(str, buf);
1310 if (strncmp(p, "LIST ", 5) != 0) continue;
1313 if (*p != '(') continue;
1315 p = strchr_cpy(p, ')', flags, sizeof(flags));
1317 while (*p == ' ') p++;
1319 p = strchr_cpy(p, ' ', separator_str, sizeof(separator_str));
1321 extract_quote(separator_str, '"');
1322 if (!strcmp(separator_str, "NIL"))
1323 separator_str[0] = '\0';
1325 *separator = separator_str[0];
1328 while (*p == ' ') p++;
1329 if (*p == '{' || *p == '"')
1330 p = imap_parse_atom(SESSION(session)->sock, p,
1331 buf, sizeof(buf), str);
1333 strncpy2(buf, p, sizeof(buf));
1334 strtailchomp(buf, separator_str[0]);
1335 if (buf[0] == '\0') continue;
1336 if (!strcmp(buf, real_path)) continue;
1338 if (separator_str[0] != '\0')
1339 subst_char(buf, separator_str[0], '/');
1340 name = g_basename(buf);
1341 if (name[0] == '.') continue;
1343 loc_name = imap_modified_utf7_to_locale(name);
1344 loc_path = imap_modified_utf7_to_locale(buf);
1345 new_item = folder_item_new(FOLDER(folder), loc_name, loc_path);
1346 if (strcasestr(flags, "\\Noinferiors") != NULL)
1347 new_item->no_sub = TRUE;
1348 if (strcmp(buf, "INBOX") != 0 &&
1349 strcasestr(flags, "\\Noselect") != NULL)
1350 new_item->no_select = TRUE;
1352 item_list = g_slist_append(item_list, new_item);
1354 debug_print("folder %s has been added.\n", loc_path);
1359 g_string_free(str, TRUE);
1364 gint imap_create_tree(Folder *folder)
1366 g_return_val_if_fail(folder != NULL, -1);
1367 g_return_val_if_fail(folder->node != NULL, -1);
1368 g_return_val_if_fail(folder->node->data != NULL, -1);
1369 g_return_val_if_fail(folder->account != NULL, -1);
1371 imap_scan_tree(folder);
1372 imap_create_missing_folders(folder);
1377 static void imap_create_missing_folders(Folder *folder)
1379 g_return_if_fail(folder != NULL);
1382 folder->inbox = imap_create_special_folder
1383 (folder, F_INBOX, "INBOX");
1385 if (!folder->outbox)
1386 folder->outbox = imap_create_special_folder
1387 (folder, F_OUTBOX, "Sent");
1389 folder->draft = imap_create_special_folder
1390 (folder, F_DRAFT, "Drafts");
1392 folder->queue = imap_create_special_folder
1393 (folder, F_QUEUE, "Queue");
1396 folder->trash = imap_create_special_folder
1397 (folder, F_TRASH, "Trash");
1400 static FolderItem *imap_create_special_folder(Folder *folder,
1401 SpecialFolderItemType stype,
1405 FolderItem *new_item;
1407 g_return_val_if_fail(folder != NULL, NULL);
1408 g_return_val_if_fail(folder->node != NULL, NULL);
1409 g_return_val_if_fail(folder->node->data != NULL, NULL);
1410 g_return_val_if_fail(folder->account != NULL, NULL);
1411 g_return_val_if_fail(name != NULL, NULL);
1413 item = FOLDER_ITEM(folder->node->data);
1414 new_item = imap_create_folder(folder, item, name);
1417 g_warning("Can't create '%s'\n", name);
1418 if (!folder->inbox) return NULL;
1420 new_item = imap_create_folder(folder, folder->inbox, name);
1422 g_warning("Can't create '%s' under INBOX\n", name);
1424 new_item->stype = stype;
1426 new_item->stype = stype;
1431 static gchar *imap_folder_get_path(Folder *folder)
1435 g_return_val_if_fail(folder != NULL, NULL);
1436 g_return_val_if_fail(folder->account != NULL, NULL);
1438 folder_path = g_strconcat(get_imap_cache_dir(),
1440 folder->account->recv_server,
1442 folder->account->userid,
1448 static gchar *imap_item_get_path(Folder *folder, FolderItem *item)
1450 gchar *folder_path, *path;
1452 g_return_val_if_fail(folder != NULL, NULL);
1453 g_return_val_if_fail(item != NULL, NULL);
1454 folder_path = imap_folder_get_path(folder);
1456 g_return_val_if_fail(folder_path != NULL, NULL);
1457 if (folder_path[0] == G_DIR_SEPARATOR) {
1459 path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
1462 path = g_strdup(folder_path);
1465 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1466 folder_path, G_DIR_SEPARATOR_S,
1469 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1472 g_free(folder_path);
1477 FolderItem *imap_create_folder(Folder *folder, FolderItem *parent,
1480 gchar *dirpath, *imap_path;
1481 IMAPSession *session;
1482 FolderItem *new_item;
1488 g_return_val_if_fail(folder != NULL, NULL);
1489 g_return_val_if_fail(folder->account != NULL, NULL);
1490 g_return_val_if_fail(parent != NULL, NULL);
1491 g_return_val_if_fail(name != NULL, NULL);
1493 session = imap_session_get(folder);
1494 if (!session) return NULL;
1496 if (!parent->parent && strcmp(name, "INBOX") == 0)
1497 dirpath = g_strdup(name);
1498 else if (parent->path)
1499 dirpath = g_strconcat(parent->path, "/", name, NULL);
1500 else if ((p = strchr(name, '/')) != NULL && *(p + 1) != '\0')
1501 dirpath = g_strdup(name);
1502 else if (folder->account->imap_dir && *folder->account->imap_dir) {
1505 Xstrdup_a(imap_dir, folder->account->imap_dir, return NULL);
1506 strtailchomp(imap_dir, '/');
1507 dirpath = g_strconcat(imap_dir, "/", name, NULL);
1509 dirpath = g_strdup(name);
1511 /* keep trailing directory separator to create a folder that contains
1513 imap_path = imap_locale_to_modified_utf7(dirpath);
1514 strtailchomp(dirpath, '/');
1515 Xstrdup_a(new_name, name, {g_free(dirpath); return NULL;});
1516 strtailchomp(new_name, '/');
1517 separator = imap_get_path_separator(IMAP_FOLDER(folder), imap_path);
1518 imap_path_separator_subst(imap_path, separator);
1519 subst_char(new_name, '/', separator);
1521 if (strcmp(name, "INBOX") != 0) {
1524 gboolean exist = FALSE;
1526 argbuf = g_ptr_array_new();
1527 ok = imap_cmd_list(session, NULL, imap_path,
1529 if (ok != IMAP_SUCCESS) {
1530 log_warning(_("can't create mailbox: LIST failed\n"));
1533 ptr_array_free_strings(argbuf);
1534 g_ptr_array_free(argbuf, TRUE);
1538 for (i = 0; i < argbuf->len; i++) {
1540 str = g_ptr_array_index(argbuf, i);
1541 if (!strncmp(str, "LIST ", 5)) {
1546 ptr_array_free_strings(argbuf);
1547 g_ptr_array_free(argbuf, TRUE);
1550 ok = imap_cmd_create(session, imap_path);
1551 if (ok != IMAP_SUCCESS) {
1552 log_warning(_("can't create mailbox\n"));
1560 new_item = folder_item_new(folder, new_name, dirpath);
1561 folder_item_append(parent, new_item);
1565 dirpath = folder_item_get_path(new_item);
1566 if (!is_dir_exist(dirpath))
1567 make_dir_hier(dirpath);
1573 gint imap_rename_folder(Folder *folder, FolderItem *item, const gchar *name)
1577 gchar *real_oldpath;
1578 gchar *real_newpath;
1581 gchar *old_cache_dir;
1582 gchar *new_cache_dir;
1583 IMAPSession *session;
1586 gint exists, recent, unseen;
1587 guint32 uid_validity;
1589 g_return_val_if_fail(folder != NULL, -1);
1590 g_return_val_if_fail(item != NULL, -1);
1591 g_return_val_if_fail(item->path != NULL, -1);
1592 g_return_val_if_fail(name != NULL, -1);
1594 session = imap_session_get(folder);
1595 if (!session) return -1;
1597 real_oldpath = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1599 g_free(session->mbox);
1600 session->mbox = NULL;
1601 ok = imap_cmd_examine(session, "INBOX",
1602 &exists, &recent, &unseen, &uid_validity);
1603 if (ok != IMAP_SUCCESS) {
1604 g_free(real_oldpath);
1608 separator = imap_get_path_separator(IMAP_FOLDER(folder), item->path);
1609 if (strchr(item->path, G_DIR_SEPARATOR)) {
1610 dirpath = g_dirname(item->path);
1611 newpath = g_strconcat(dirpath, G_DIR_SEPARATOR_S, name, NULL);
1614 newpath = g_strdup(name);
1616 real_newpath = imap_locale_to_modified_utf7(newpath);
1617 imap_path_separator_subst(real_newpath, separator);
1619 ok = imap_cmd_rename(session, real_oldpath, real_newpath);
1620 if (ok != IMAP_SUCCESS) {
1621 log_warning(_("can't rename mailbox: %s to %s\n"),
1622 real_oldpath, real_newpath);
1623 g_free(real_oldpath);
1625 g_free(real_newpath);
1630 item->name = g_strdup(name);
1632 old_cache_dir = folder_item_get_path(item);
1634 node = g_node_find(item->folder->node, G_PRE_ORDER, G_TRAVERSE_ALL,
1636 paths[0] = g_strdup(item->path);
1638 g_node_traverse(node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1639 imap_rename_folder_func, paths);
1641 if (is_dir_exist(old_cache_dir)) {
1642 new_cache_dir = folder_item_get_path(item);
1643 if (rename(old_cache_dir, new_cache_dir) < 0) {
1644 FILE_OP_ERROR(old_cache_dir, "rename");
1646 g_free(new_cache_dir);
1649 g_free(old_cache_dir);
1652 g_free(real_oldpath);
1653 g_free(real_newpath);
1658 gint imap_remove_folder(Folder *folder, FolderItem *item)
1661 IMAPSession *session;
1664 gint exists, recent, unseen;
1665 guint32 uid_validity;
1667 g_return_val_if_fail(folder != NULL, -1);
1668 g_return_val_if_fail(item != NULL, -1);
1669 g_return_val_if_fail(item->path != NULL, -1);
1671 session = imap_session_get(folder);
1672 if (!session) return -1;
1674 path = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1676 ok = imap_cmd_examine(session, "INBOX",
1677 &exists, &recent, &unseen, &uid_validity);
1678 if (ok != IMAP_SUCCESS) {
1683 ok = imap_cmd_delete(session, path);
1684 if (ok != IMAP_SUCCESS) {
1685 log_warning(_("can't delete mailbox\n"));
1691 cache_dir = folder_item_get_path(item);
1692 if (is_dir_exist(cache_dir) && remove_dir_recursive(cache_dir) < 0)
1693 g_warning("can't remove directory '%s'\n", cache_dir);
1695 folder_item_remove(item);
1700 static GSList *imap_get_uncached_messages(IMAPSession *session,
1702 MsgNumberList *numlist)
1705 GSList *newlist = NULL;
1706 GSList *llast = NULL;
1709 GSList *seq_list, *cur;
1712 g_return_val_if_fail(session != NULL, NULL);
1713 g_return_val_if_fail(item != NULL, NULL);
1714 g_return_val_if_fail(item->folder != NULL, NULL);
1715 g_return_val_if_fail(FOLDER_CLASS(item->folder) == &imap_class, NULL);
1717 seq_list = imap_get_seq_set_from_numlist(numlist);
1718 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
1719 imapset = cur->data;
1721 if (imap_cmd_envelope(session, imapset)
1723 log_warning(_("can't get envelope\n"));
1727 str = g_string_new(NULL);
1730 if ((tmp = sock_getline(SESSION(session)->sock)) == NULL) {
1731 log_warning(_("error occurred while getting envelope.\n"));
1732 g_string_free(str, TRUE);
1736 if (tmp[0] != '*' || tmp[1] != ' ') {
1737 log_print("IMAP4< %s\n", tmp);
1741 if (strstr(tmp, "FETCH") == NULL) {
1742 log_print("IMAP4< %s\n", tmp);
1746 log_print("IMAP4< %s\n", tmp);
1747 g_string_assign(str, tmp);
1750 msginfo = imap_parse_envelope
1751 (SESSION(session)->sock, item, str);
1753 log_warning(_("can't parse envelope: %s\n"), str->str);
1756 if (item->stype == F_QUEUE) {
1757 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_QUEUED);
1758 } else if (item->stype == F_DRAFT) {
1759 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_DRAFT);
1762 msginfo->folder = item;
1765 llast = newlist = g_slist_append(newlist, msginfo);
1767 llast = g_slist_append(llast, msginfo);
1768 llast = llast->next;
1772 g_string_free(str, TRUE);
1774 imap_seq_set_free(seq_list);
1779 static void imap_delete_all_cached_messages(FolderItem *item)
1783 g_return_if_fail(item != NULL);
1784 g_return_if_fail(item->folder != NULL);
1785 g_return_if_fail(FOLDER_CLASS(item->folder) == &imap_class);
1787 debug_print("Deleting all cached messages...\n");
1789 dir = folder_item_get_path(item);
1790 if (is_dir_exist(dir))
1791 remove_all_numbered_files(dir);
1794 debug_print("done.\n");
1798 static SockInfo *imap_open_tunnel(const gchar *server,
1799 const gchar *tunnelcmd,
1802 static SockInfo *imap_open_tunnel(const gchar *server,
1803 const gchar *tunnelcmd)
1808 if ((sock = sock_connect_cmd(server, tunnelcmd)) == NULL) {
1809 log_warning(_("Can't establish IMAP4 session with: %s\n"),
1814 return imap_init_sock(sock, ssl_type);
1816 return imap_init_sock(sock);
1822 static SockInfo *imap_open(const gchar *server, gushort port,
1825 static SockInfo *imap_open(const gchar *server, gushort port)
1830 if ((sock = sock_connect(server, port)) == NULL) {
1831 log_warning(_("Can't connect to IMAP4 server: %s:%d\n"),
1837 if (ssl_type == SSL_TUNNEL && !ssl_init_socket(sock)) {
1838 log_warning(_("Can't establish IMAP4 session with: %s:%d\n"),
1848 static SockInfo *imap_init_sock(SockInfo *sock, SSLType ssl_type)
1850 static SockInfo *imap_init_sock(SockInfo *sock)
1857 static GList *imap_parse_namespace_str(gchar *str)
1862 IMAPNameSpace *namespace;
1863 GList *ns_list = NULL;
1865 while (*p != '\0') {
1866 /* parse ("#foo" "/") */
1868 while (*p && *p != '(') p++;
1869 if (*p == '\0') break;
1872 while (*p && *p != '"') p++;
1873 if (*p == '\0') break;
1877 while (*p && *p != '"') p++;
1878 if (*p == '\0') break;
1882 while (*p && isspace(*p)) p++;
1883 if (*p == '\0') break;
1884 if (strncmp(p, "NIL", 3) == 0)
1886 else if (*p == '"') {
1889 while (*p && *p != '"') p++;
1890 if (*p == '\0') break;
1895 while (*p && *p != ')') p++;
1896 if (*p == '\0') break;
1899 namespace = g_new(IMAPNameSpace, 1);
1900 namespace->name = g_strdup(name);
1901 namespace->separator = separator ? separator[0] : '\0';
1902 ns_list = g_list_append(ns_list, namespace);
1908 static void imap_parse_namespace(IMAPSession *session, IMAPFolder *folder)
1913 g_return_if_fail(session != NULL);
1914 g_return_if_fail(folder != NULL);
1916 if (folder->ns_personal != NULL ||
1917 folder->ns_others != NULL ||
1918 folder->ns_shared != NULL)
1921 if (!imap_has_capability(session, "NAMESPACE")) {
1922 imap_get_namespace_by_list(session, folder);
1926 if (imap_cmd_namespace(session, &ns_str)
1928 log_warning(_("can't get namespace\n"));
1932 str_array = strsplit_parenthesis(ns_str, '(', ')', 3);
1933 if (str_array == NULL) {
1935 imap_get_namespace_by_list(session, folder);
1939 folder->ns_personal = imap_parse_namespace_str(str_array[0]);
1940 if (str_array[0] && str_array[1])
1941 folder->ns_others = imap_parse_namespace_str(str_array[1]);
1942 if (str_array[0] && str_array[1] && str_array[2])
1943 folder->ns_shared = imap_parse_namespace_str(str_array[2]);
1944 g_strfreev(str_array);
1948 static void imap_get_namespace_by_list(IMAPSession *session, IMAPFolder *folder)
1950 GSList *item_list, *cur;
1951 gchar separator = '\0';
1952 IMAPNameSpace *namespace;
1954 g_return_if_fail(session != NULL);
1955 g_return_if_fail(folder != NULL);
1957 if (folder->ns_personal != NULL ||
1958 folder->ns_others != NULL ||
1959 folder->ns_shared != NULL)
1962 imap_gen_send(session, "LIST \"\" \"\"");
1963 item_list = imap_parse_list(folder, session, "", &separator);
1964 for (cur = item_list; cur != NULL; cur = cur->next)
1965 folder_item_destroy(FOLDER_ITEM(cur->data));
1966 g_slist_free(item_list);
1968 namespace = g_new(IMAPNameSpace, 1);
1969 namespace->name = g_strdup("");
1970 namespace->separator = separator;
1971 folder->ns_personal = g_list_append(NULL, namespace);
1974 static IMAPNameSpace *imap_find_namespace_from_list(GList *ns_list,
1977 IMAPNameSpace *namespace = NULL;
1978 gchar *tmp_path, *name;
1980 if (!path) path = "";
1982 Xstrcat_a(tmp_path, path, "/", return NULL);
1984 for (; ns_list != NULL; ns_list = ns_list->next) {
1985 IMAPNameSpace *tmp_ns = ns_list->data;
1987 Xstrdup_a(name, tmp_ns->name, return namespace);
1988 if (tmp_ns->separator && tmp_ns->separator != '/')
1989 subst_char(name, tmp_ns->separator, '/');
1990 if (strncmp(tmp_path, name, strlen(name)) == 0)
1997 static IMAPNameSpace *imap_find_namespace(IMAPFolder *folder,
2000 IMAPNameSpace *namespace;
2002 g_return_val_if_fail(folder != NULL, NULL);
2004 namespace = imap_find_namespace_from_list(folder->ns_personal, path);
2005 if (namespace) return namespace;
2006 namespace = imap_find_namespace_from_list(folder->ns_others, path);
2007 if (namespace) return namespace;
2008 namespace = imap_find_namespace_from_list(folder->ns_shared, path);
2009 if (namespace) return namespace;
2014 static gchar imap_get_path_separator(IMAPFolder *folder, const gchar *path)
2016 IMAPNameSpace *namespace;
2017 gchar separator = '/';
2019 namespace = imap_find_namespace(folder, path);
2020 if (namespace && namespace->separator)
2021 separator = namespace->separator;
2026 static gchar *imap_get_real_path(IMAPFolder *folder, const gchar *path)
2031 g_return_val_if_fail(folder != NULL, NULL);
2032 g_return_val_if_fail(path != NULL, NULL);
2034 real_path = imap_locale_to_modified_utf7(path);
2035 separator = imap_get_path_separator(folder, path);
2036 imap_path_separator_subst(real_path, separator);
2041 static gchar *imap_parse_atom(SockInfo *sock, gchar *src,
2042 gchar *dest, gint dest_len, GString *str)
2044 gchar *cur_pos = src;
2047 g_return_val_if_fail(str != NULL, cur_pos);
2049 /* read the next line if the current response buffer is empty */
2050 while (isspace(*cur_pos)) cur_pos++;
2051 while (*cur_pos == '\0') {
2052 if ((nextline = sock_getline(sock)) == NULL)
2054 g_string_assign(str, nextline);
2056 strretchomp(nextline);
2057 /* log_print("IMAP4< %s\n", nextline); */
2058 debug_print("IMAP4< %s\n", nextline);
2061 while (isspace(*cur_pos)) cur_pos++;
2064 if (!strncmp(cur_pos, "NIL", 3)) {
2067 } else if (*cur_pos == '\"') {
2070 p = get_quoted(cur_pos, '\"', dest, dest_len);
2071 cur_pos = p ? p : cur_pos + 2;
2072 } else if (*cur_pos == '{') {
2077 cur_pos = strchr_cpy(cur_pos + 1, '}', buf, sizeof(buf));
2079 g_return_val_if_fail(len > 0, cur_pos);
2081 g_string_truncate(str, 0);
2085 if ((nextline = sock_getline(sock)) == NULL)
2087 line_len += strlen(nextline);
2088 g_string_append(str, nextline);
2090 strretchomp(nextline);
2091 /* log_print("IMAP4< %s\n", nextline); */
2092 debug_print("IMAP4< %s\n", nextline);
2094 } while (line_len < len);
2096 memcpy(dest, cur_pos, MIN(len, dest_len - 1));
2097 dest[MIN(len, dest_len - 1)] = '\0';
2104 static gchar *imap_get_header(SockInfo *sock, gchar *cur_pos, gchar **headers,
2114 g_return_val_if_fail(str != NULL, cur_pos);
2116 while (isspace(*cur_pos)) cur_pos++;
2118 g_return_val_if_fail(*cur_pos == '{', cur_pos);
2120 cur_pos = strchr_cpy(cur_pos + 1, '}', buf, sizeof(buf));
2122 g_return_val_if_fail(len > 0, cur_pos);
2124 g_string_truncate(str, 0);
2128 if ((nextline = sock_getline(sock)) == NULL)
2130 block_len += strlen(nextline);
2131 g_string_append(str, nextline);
2133 strretchomp(nextline);
2134 /* debug_print("IMAP4< %s\n", nextline); */
2136 } while (block_len < len);
2138 debug_print("IMAP4< [contents of BODY.PEEK[HEADER.FIELDS (...)]]\n");
2140 *headers = g_strndup(cur_pos, len);
2143 while (isspace(*cur_pos)) cur_pos++;
2144 while (*cur_pos == '\0') {
2145 if ((nextline = sock_getline(sock)) == NULL)
2147 g_string_assign(str, nextline);
2149 strretchomp(nextline);
2150 debug_print("IMAP4< %s\n", nextline);
2153 while (isspace(*cur_pos)) cur_pos++;
2159 static MsgFlags imap_parse_flags(const gchar *flag_str)
2161 const gchar *p = flag_str;
2162 MsgFlags flags = {0, 0};
2164 flags.perm_flags = MSG_UNREAD;
2166 while ((p = strchr(p, '\\')) != NULL) {
2169 if (g_strncasecmp(p, "Recent", 6) == 0 && MSG_IS_UNREAD(flags)) {
2170 MSG_SET_PERM_FLAGS(flags, MSG_NEW);
2171 } else if (g_strncasecmp(p, "Seen", 4) == 0) {
2172 MSG_UNSET_PERM_FLAGS(flags, MSG_NEW|MSG_UNREAD);
2173 } else if (g_strncasecmp(p, "Deleted", 7) == 0) {
2174 MSG_SET_PERM_FLAGS(flags, MSG_DELETED);
2175 } else if (g_strncasecmp(p, "Flagged", 7) == 0) {
2176 MSG_SET_PERM_FLAGS(flags, MSG_MARKED);
2177 } else if (g_strncasecmp(p, "Answered", 8) == 0) {
2178 MSG_SET_PERM_FLAGS(flags, MSG_REPLIED);
2185 static MsgInfo *imap_parse_envelope(SockInfo *sock, FolderItem *item,
2188 gchar buf[IMAPBUFSIZE];
2189 MsgInfo *msginfo = NULL;
2194 MsgFlags flags = {0, 0}, imap_flags = {0, 0};
2196 g_return_val_if_fail(line_str != NULL, NULL);
2197 g_return_val_if_fail(line_str->str[0] == '*' &&
2198 line_str->str[1] == ' ', NULL);
2200 MSG_SET_TMP_FLAGS(flags, MSG_IMAP);
2201 if (item->stype == F_QUEUE) {
2202 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
2203 } else if (item->stype == F_DRAFT) {
2204 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
2207 cur_pos = line_str->str + 2;
2209 #define PARSE_ONE_ELEMENT(ch) \
2211 cur_pos = strchr_cpy(cur_pos, ch, buf, sizeof(buf)); \
2212 if (cur_pos == NULL) { \
2213 g_warning("cur_pos == NULL\n"); \
2214 procmsg_msginfo_free(msginfo); \
2219 PARSE_ONE_ELEMENT(' ');
2222 PARSE_ONE_ELEMENT(' ');
2223 g_return_val_if_fail(!strcmp(buf, "FETCH"), NULL);
2225 g_return_val_if_fail(*cur_pos == '(', NULL);
2228 while (*cur_pos != '\0' && *cur_pos != ')') {
2229 while (*cur_pos == ' ') cur_pos++;
2231 if (!strncmp(cur_pos, "UID ", 4)) {
2233 uid = strtoul(cur_pos, &cur_pos, 10);
2234 } else if (!strncmp(cur_pos, "FLAGS ", 6)) {
2236 if (*cur_pos != '(') {
2237 g_warning("*cur_pos != '('\n");
2238 procmsg_msginfo_free(msginfo);
2242 PARSE_ONE_ELEMENT(')');
2243 imap_flags = imap_parse_flags(buf);
2244 } else if (!strncmp(cur_pos, "RFC822.SIZE ", 12)) {
2246 size = strtol(cur_pos, &cur_pos, 10);
2247 } else if (!strncmp(cur_pos, "BODY[HEADER.FIELDS ", 19)) {
2251 if (*cur_pos != '(') {
2252 g_warning("*cur_pos != '('\n");
2253 procmsg_msginfo_free(msginfo);
2257 PARSE_ONE_ELEMENT(')');
2258 if (*cur_pos != ']') {
2259 g_warning("*cur_pos != ']'\n");
2260 procmsg_msginfo_free(msginfo);
2265 cur_pos = imap_get_header(sock, cur_pos, &headers,
2267 msginfo = procheader_parse_str(headers, flags, FALSE, FALSE);
2270 g_warning("invalid FETCH response: %s\n", cur_pos);
2276 msginfo->msgnum = uid;
2277 msginfo->size = size;
2278 msginfo->flags.tmp_flags |= imap_flags.tmp_flags;
2279 msginfo->flags.perm_flags = imap_flags.perm_flags;
2285 static gchar *imap_get_flag_str(IMAPFlags flags)
2290 str = g_string_new(NULL);
2292 if (IMAP_IS_SEEN(flags)) g_string_append(str, "\\Seen ");
2293 if (IMAP_IS_ANSWERED(flags)) g_string_append(str, "\\Answered ");
2294 if (IMAP_IS_FLAGGED(flags)) g_string_append(str, "\\Flagged ");
2295 if (IMAP_IS_DELETED(flags)) g_string_append(str, "\\Deleted ");
2296 if (IMAP_IS_DRAFT(flags)) g_string_append(str, "\\Draft");
2298 if (str->len > 0 && str->str[str->len - 1] == ' ')
2299 g_string_truncate(str, str->len - 1);
2302 g_string_free(str, FALSE);
2307 static gint imap_set_message_flags(IMAPSession *session,
2308 MsgNumberList *numlist,
2315 GSList *seq_list, *cur;
2318 flag_str = imap_get_flag_str(flags);
2319 cmd = g_strconcat(is_set ? "+FLAGS.SILENT (" : "-FLAGS.SILENT (",
2320 flag_str, ")", NULL);
2323 seq_list = imap_get_seq_set_from_numlist(numlist);
2324 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
2325 imapset = cur->data;
2327 ok = imap_cmd_store(session, imapset, cmd);
2329 imap_seq_set_free(seq_list);
2335 static gint imap_select(IMAPSession *session, IMAPFolder *folder,
2337 gint *exists, gint *recent, gint *unseen,
2338 guint32 *uid_validity)
2342 gint exists_, recent_, unseen_, uid_validity_;
2344 if (!exists || !recent || !unseen || !uid_validity) {
2345 if (session->mbox && strcmp(session->mbox, path) == 0)
2346 return IMAP_SUCCESS;
2350 uid_validity = &uid_validity_;
2353 g_free(session->mbox);
2354 session->mbox = NULL;
2356 real_path = imap_get_real_path(folder, path);
2357 ok = imap_cmd_select(session, real_path,
2358 exists, recent, unseen, uid_validity);
2359 if (ok != IMAP_SUCCESS)
2360 log_warning(_("can't select folder: %s\n"), real_path);
2362 session->mbox = g_strdup(path);
2363 session->folder_content_changed = FALSE;
2370 #define THROW(err) { ok = err; goto catch; }
2372 static gint imap_status(IMAPSession *session, IMAPFolder *folder,
2374 gint *messages, gint *recent,
2375 guint32 *uid_next, guint32 *uid_validity,
2384 *messages = *recent = *uid_next = *uid_validity = *unseen = 0;
2386 argbuf = g_ptr_array_new();
2388 real_path = imap_get_real_path(folder, path);
2389 QUOTE_IF_REQUIRED(real_path_, real_path);
2390 imap_gen_send(session, "STATUS %s "
2391 "(MESSAGES RECENT UIDNEXT UIDVALIDITY UNSEEN)",
2394 ok = imap_cmd_ok(session, argbuf);
2395 if (ok != IMAP_SUCCESS) THROW(ok);
2397 str = search_array_str(argbuf, "STATUS");
2398 if (!str) THROW(IMAP_ERROR);
2400 str = strchr(str, '(');
2401 if (!str) THROW(IMAP_ERROR);
2403 while (*str != '\0' && *str != ')') {
2404 while (*str == ' ') str++;
2406 if (!strncmp(str, "MESSAGES ", 9)) {
2408 *messages = strtol(str, &str, 10);
2409 } else if (!strncmp(str, "RECENT ", 7)) {
2411 *recent = strtol(str, &str, 10);
2412 } else if (!strncmp(str, "UIDNEXT ", 8)) {
2414 *uid_next = strtoul(str, &str, 10);
2415 } else if (!strncmp(str, "UIDVALIDITY ", 12)) {
2417 *uid_validity = strtoul(str, &str, 10);
2418 } else if (!strncmp(str, "UNSEEN ", 7)) {
2420 *unseen = strtol(str, &str, 10);
2422 g_warning("invalid STATUS response: %s\n", str);
2429 ptr_array_free_strings(argbuf);
2430 g_ptr_array_free(argbuf, TRUE);
2437 static gboolean imap_has_capability(IMAPSession *session, const gchar *cap)
2441 for (p = session->capability; *p != NULL; ++p) {
2442 if (!g_strcasecmp(*p, cap))
2449 void imap_free_capabilities(IMAPSession *session)
2451 g_strfreev(session->capability);
2452 session->capability = NULL;
2455 /* low-level IMAP4rev1 commands */
2457 static gint imap_cmd_authenticate(IMAPSession *session, const gchar *user,
2458 const gchar *pass, IMAPAuthType type)
2465 gchar hexdigest[33];
2469 auth_type = "CRAM-MD5";
2471 imap_gen_send(session, "AUTHENTICATE %s", auth_type);
2472 ok = imap_gen_recv(session, &buf);
2473 if (ok != IMAP_SUCCESS || buf[0] != '+' || buf[1] != ' ') {
2478 challenge = g_malloc(strlen(buf + 2) + 1);
2479 challenge_len = base64_decode(challenge, buf + 2, -1);
2480 challenge[challenge_len] = '\0';
2482 log_print("IMAP< [Decoded: %s]\n", challenge);
2484 md5_hex_hmac(hexdigest, challenge, challenge_len, pass, strlen(pass));
2487 response = g_strdup_printf("%s %s", user, hexdigest);
2488 log_print("IMAP> [Encoded: %s]\n", response);
2489 response64 = g_malloc((strlen(response) + 3) * 2 + 1);
2490 base64_encode(response64, response, strlen(response));
2493 log_print("IMAP> %s\n", response64);
2494 sock_puts(SESSION(session)->sock, response64);
2495 ok = imap_cmd_ok(session, NULL);
2496 if (ok != IMAP_SUCCESS)
2497 log_warning(_("IMAP4 authentication failed.\n"));
2502 static gint imap_cmd_login(IMAPSession *session,
2503 const gchar *user, const gchar *pass)
2505 gchar *user_, *pass_;
2508 QUOTE_IF_REQUIRED(user_, user);
2509 QUOTE_IF_REQUIRED(pass_, pass);
2510 imap_gen_send(session, "LOGIN %s %s", user_, pass_);
2512 ok = imap_cmd_ok(session, NULL);
2513 if (ok != IMAP_SUCCESS)
2514 log_warning(_("IMAP4 login failed.\n"));
2519 static gint imap_cmd_logout(IMAPSession *session)
2521 imap_gen_send(session, "LOGOUT");
2522 return imap_cmd_ok(session, NULL);
2525 static gint imap_cmd_noop(IMAPSession *session)
2527 imap_gen_send(session, "NOOP");
2528 return imap_cmd_ok(session, NULL);
2531 static gint imap_cmd_starttls(IMAPSession *session)
2533 imap_gen_send(session, "STARTTLS");
2534 return imap_cmd_ok(session, NULL);
2537 #define THROW(err) { ok = err; goto catch; }
2539 static gint imap_cmd_namespace(IMAPSession *session, gchar **ns_str)
2545 argbuf = g_ptr_array_new();
2547 imap_gen_send(session, "NAMESPACE");
2548 if ((ok = imap_cmd_ok(session, argbuf)) != IMAP_SUCCESS) THROW(ok);
2550 str = search_array_str(argbuf, "NAMESPACE");
2551 if (!str) THROW(IMAP_ERROR);
2553 *ns_str = g_strdup(str);
2556 ptr_array_free_strings(argbuf);
2557 g_ptr_array_free(argbuf, TRUE);
2564 static gint imap_cmd_list(IMAPSession *session, const gchar *ref,
2565 const gchar *mailbox, GPtrArray *argbuf)
2567 gchar *ref_, *mailbox_;
2569 if (!ref) ref = "\"\"";
2570 if (!mailbox) mailbox = "\"\"";
2572 QUOTE_IF_REQUIRED(ref_, ref);
2573 QUOTE_IF_REQUIRED(mailbox_, mailbox);
2574 imap_gen_send(session, "LIST %s %s", ref_, mailbox_);
2576 return imap_cmd_ok(session, argbuf);
2579 #define THROW goto catch
2581 static gint imap_cmd_do_select(IMAPSession *session, const gchar *folder,
2583 gint *exists, gint *recent, gint *unseen,
2584 guint32 *uid_validity)
2592 *exists = *recent = *unseen = *uid_validity = 0;
2593 argbuf = g_ptr_array_new();
2596 select_cmd = "EXAMINE";
2598 select_cmd = "SELECT";
2600 QUOTE_IF_REQUIRED(folder_, folder);
2601 imap_gen_send(session, "%s %s", select_cmd, folder_);
2603 if ((ok = imap_cmd_ok(session, argbuf)) != IMAP_SUCCESS) THROW;
2605 resp_str = search_array_contain_str(argbuf, "EXISTS");
2607 if (sscanf(resp_str,"%d EXISTS", exists) != 1) {
2608 g_warning("imap_cmd_select(): invalid EXISTS line.\n");
2613 resp_str = search_array_contain_str(argbuf, "RECENT");
2615 if (sscanf(resp_str, "%d RECENT", recent) != 1) {
2616 g_warning("imap_cmd_select(): invalid RECENT line.\n");
2621 resp_str = search_array_contain_str(argbuf, "UIDVALIDITY");
2623 if (sscanf(resp_str, "OK [UIDVALIDITY %u] ", uid_validity)
2625 g_warning("imap_cmd_select(): invalid UIDVALIDITY line.\n");
2630 resp_str = search_array_contain_str(argbuf, "UNSEEN");
2632 if (sscanf(resp_str, "OK [UNSEEN %d] ", unseen) != 1) {
2633 g_warning("imap_cmd_select(): invalid UNSEEN line.\n");
2639 ptr_array_free_strings(argbuf);
2640 g_ptr_array_free(argbuf, TRUE);
2645 static gint imap_cmd_select(IMAPSession *session, const gchar *folder,
2646 gint *exists, gint *recent, gint *unseen,
2647 guint32 *uid_validity)
2649 return imap_cmd_do_select(session, folder, FALSE,
2650 exists, recent, unseen, uid_validity);
2653 static gint imap_cmd_examine(IMAPSession *session, const gchar *folder,
2654 gint *exists, gint *recent, gint *unseen,
2655 guint32 *uid_validity)
2657 return imap_cmd_do_select(session, folder, TRUE,
2658 exists, recent, unseen, uid_validity);
2663 static gint imap_cmd_create(IMAPSession *session, const gchar *folder)
2667 QUOTE_IF_REQUIRED(folder_, folder);
2668 imap_gen_send(session, "CREATE %s", folder_);
2670 return imap_cmd_ok(session, NULL);
2673 static gint imap_cmd_rename(IMAPSession *session, const gchar *old_folder,
2674 const gchar *new_folder)
2676 gchar *old_folder_, *new_folder_;
2678 QUOTE_IF_REQUIRED(old_folder_, old_folder);
2679 QUOTE_IF_REQUIRED(new_folder_, new_folder);
2680 imap_gen_send(session, "RENAME %s %s", old_folder_, new_folder_);
2682 return imap_cmd_ok(session, NULL);
2685 static gint imap_cmd_delete(IMAPSession *session, const gchar *folder)
2689 QUOTE_IF_REQUIRED(folder_, folder);
2690 imap_gen_send(session, "DELETE %s", folder_);
2692 return imap_cmd_ok(session, NULL);
2695 static gint imap_cmd_search(IMAPSession *session, const gchar *criteria, GSList **list)
2701 g_return_val_if_fail(criteria != NULL, IMAP_ERROR);
2702 g_return_val_if_fail(list != NULL, IMAP_ERROR);
2706 argbuf = g_ptr_array_new();
2707 imap_gen_send(session, "UID SEARCH %s", criteria);
2709 ok = imap_cmd_ok(session, argbuf);
2710 if (ok != IMAP_SUCCESS) {
2711 ptr_array_free_strings(argbuf);
2712 g_ptr_array_free(argbuf, TRUE);
2716 if ((uidlist = search_array_str(argbuf, "SEARCH ")) != NULL) {
2717 gchar **strlist, **p;
2719 strlist = g_strsplit(uidlist + 7, " ", 0);
2720 for (p = strlist; *p != NULL; ++p) {
2723 if (sscanf(*p, "%d", &msgnum) == 1)
2724 *list = g_slist_append(*list, GINT_TO_POINTER(msgnum));
2726 g_strfreev(strlist);
2728 ptr_array_free_strings(argbuf);
2729 g_ptr_array_free(argbuf, TRUE);
2731 return IMAP_SUCCESS;
2734 static gint imap_cmd_fetch(IMAPSession *session, guint32 uid, const gchar *filename)
2742 g_return_val_if_fail(filename != NULL, IMAP_ERROR);
2744 imap_gen_send(session, "UID FETCH %d BODY.PEEK[]", uid);
2746 while ((ok = imap_gen_recv(session, &buf)) == IMAP_SUCCESS) {
2747 if (buf[0] != '*' || buf[1] != ' ') {
2751 if (strstr(buf, "FETCH") != NULL) break;
2754 if (ok != IMAP_SUCCESS) {
2759 #define RETURN_ERROR_IF_FAIL(cond) \
2762 return IMAP_ERROR; \
2765 cur_pos = strchr(buf, '{');
2766 RETURN_ERROR_IF_FAIL(cur_pos != NULL);
2767 cur_pos = strchr_cpy(cur_pos + 1, '}', size_str, sizeof(size_str));
2768 RETURN_ERROR_IF_FAIL(cur_pos != NULL);
2769 size_num = atol(size_str);
2770 RETURN_ERROR_IF_FAIL(size_num > 0);
2772 RETURN_ERROR_IF_FAIL(*cur_pos == '\0');
2774 #undef RETURN_ERROR_IF_FAIL
2778 if (recv_bytes_write_to_file(SESSION(session)->sock, size_num, filename) != 0)
2781 if (imap_gen_recv(session, &buf) != IMAP_SUCCESS) {
2786 if (buf[0] == '\0' || buf[strlen(buf) - 1] != ')') {
2792 ok = imap_cmd_ok(session, NULL);
2797 static gint imap_cmd_append(IMAPSession *session, const gchar *destfolder,
2798 const gchar *file, IMAPFlags flags, guint32 *new_uid)
2806 gchar buf[BUFFSIZE];
2811 g_return_val_if_fail(file != NULL, IMAP_ERROR);
2813 size = get_file_size_as_crlf(file);
2814 if ((fp = fopen(file, "rb")) == NULL) {
2815 FILE_OP_ERROR(file, "fopen");
2818 QUOTE_IF_REQUIRED(destfolder_, destfolder);
2819 flag_str = imap_get_flag_str(flags);
2820 imap_gen_send(session, "APPEND %s (%s) {%d}",
2821 destfolder_, flag_str, size);
2824 ok = imap_gen_recv(session, &ret);
2825 if (ok != IMAP_SUCCESS || ret[0] != '+' || ret[1] != ' ') {
2826 log_warning(_("can't append %s to %s\n"), file, destfolder_);
2833 log_print("IMAP4> %s\n", _("(sending file...)"));
2835 while (fgets(buf, sizeof(buf), fp) != NULL) {
2837 if (sock_puts(SESSION(session)->sock, buf) < 0) {
2844 FILE_OP_ERROR(file, "fgets");
2849 sock_puts(SESSION(session)->sock, "");
2853 if (new_uid != NULL)
2856 if (new_uid != NULL && session->uidplus) {
2857 argbuf = g_ptr_array_new();
2859 ok = imap_cmd_ok(session, argbuf);
2860 if (ok != IMAP_SUCCESS)
2861 log_warning(_("can't append message to %s\n"),
2863 else if (argbuf->len > 0) {
2864 resp_str = g_ptr_array_index(argbuf, argbuf->len - 1);
2866 sscanf(resp_str, "%*u OK [APPENDUID %*u %u]",
2868 *new_uid = new_uid_;
2872 ptr_array_free_strings(argbuf);
2873 g_ptr_array_free(argbuf, TRUE);
2875 ok = imap_cmd_ok(session, NULL);
2880 static gint imap_cmd_copy(IMAPSession *session, const gchar *seq_set,
2881 const gchar *destfolder, GRelation *uid_mapping)
2887 g_return_val_if_fail(session != NULL, IMAP_ERROR);
2888 g_return_val_if_fail(seq_set != NULL, IMAP_ERROR);
2889 g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
2891 QUOTE_IF_REQUIRED(destfolder_, destfolder);
2892 imap_gen_send(session, "UID COPY %s %s", seq_set, destfolder_);
2894 reply = g_ptr_array_new();
2896 ok = imap_cmd_ok(session, reply);
2897 if (ok != IMAP_SUCCESS)
2898 log_warning(_("can't copy %s to %s\n"), seq_set, destfolder_);
2902 - split IMAPSets into uids
2903 - g_relation_insert(uid_mapping, olduid, newuid);
2905 else if (imap_has_capability(session, "UIDPLUS") && reply->len > 0)
2906 if ((okmsginfo = g_ptr_array_index(reply, reply->len - 1)) != NULL &&
2907 sscanf(okmsginfo, "%*u OK [COPYUID %*u %u %u]", &olduid, &newuid) == 2 &&
2911 ptr_array_free_strings(reply);
2912 g_ptr_array_free(reply, TRUE);
2916 gint imap_cmd_envelope(IMAPSession *session, IMAPSet set)
2918 static GString *header_fields = NULL;
2920 if (header_fields == NULL) {
2921 const HeaderEntry *headers, *elem;
2923 headers = procheader_get_headernames(FALSE);
2924 header_fields = g_string_new("");
2926 for (elem = headers; elem->name != NULL; ++elem) {
2927 gint namelen = strlen(elem->name);
2929 /* Header fields ending with space are not rfc822 headers */
2930 if (elem->name[namelen - 1] == ' ')
2933 /* strip : at the of header field */
2934 if(elem->name[namelen - 1] == ':')
2940 g_string_sprintfa(header_fields, "%s%.*s",
2941 header_fields->str[0] != '\0' ? " " : "",
2942 namelen, elem->name);
2947 (session, "UID FETCH %s (UID FLAGS RFC822.SIZE BODY.PEEK[HEADER.FIELDS (%s)])",
2948 set, header_fields->str);
2950 return IMAP_SUCCESS;
2953 static gint imap_cmd_store(IMAPSession *session, IMAPSet seq_set,
2958 imap_gen_send(session, "UID STORE %s %s", seq_set, sub_cmd);
2960 if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS) {
2961 log_warning(_("error while imap command: STORE %s %s\n"),
2966 return IMAP_SUCCESS;
2969 static gint imap_cmd_expunge(IMAPSession *session)
2973 imap_gen_send(session, "EXPUNGE");
2974 if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS) {
2975 log_warning(_("error while imap command: EXPUNGE\n"));
2979 return IMAP_SUCCESS;
2982 static gint imap_cmd_close(IMAPSession *session)
2986 imap_gen_send(session, "CLOSE");
2987 if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS)
2988 log_warning(_("error while imap command: CLOSE\n"));
2993 static gint imap_cmd_ok(IMAPSession *session, GPtrArray *argbuf)
2995 gint ok = IMAP_SUCCESS;
3000 while ((ok = imap_gen_recv(session, &buf))
3002 // make sure data is long enough for any substring of buf
3003 data = alloca(strlen(buf) + 1);
3005 // untagged line read
3006 if (buf[0] == '*' && buf[1] == ' ') {
3009 g_ptr_array_add(argbuf, g_strdup(buf + 2));
3011 if (sscanf(buf + 2, "%d %s", &num, data) >= 2) {
3012 if (!strcmp(data, "EXISTS")) {
3013 session->exists = num;
3014 session->folder_content_changed = TRUE;
3017 if(!strcmp(data, "EXPUNGE")) {
3019 session->folder_content_changed = TRUE;
3022 // tagged line with correct tag and OK response found
3023 } else if ((sscanf(buf, "%d %s", &cmd_num, data) >= 2) &&
3024 (cmd_num == session->cmd_count) &&
3025 !strcmp(data, "OK")) {
3027 g_ptr_array_add(argbuf, g_strdup(buf));
3041 static void imap_gen_send(IMAPSession *session, const gchar *format, ...)
3048 va_start(args, format);
3049 tmp = g_strdup_vprintf(format, args);
3052 session->cmd_count++;
3054 buf = g_strdup_printf("%d %s\r\n", session->cmd_count, tmp);
3055 if (!strncasecmp(tmp, "LOGIN ", 6) && (p = strchr(tmp + 6, ' '))) {
3057 log_print("IMAP4> %d %s ********\n", session->cmd_count, tmp);
3059 log_print("IMAP4> %d %s\n", session->cmd_count, tmp);
3061 sock_write_all(SESSION(session)->sock, buf, strlen(buf));
3066 static gint imap_gen_recv(IMAPSession *session, gchar **ret)
3068 if ((*ret = sock_getline(SESSION(session)->sock)) == NULL)
3073 log_print("IMAP4< %s\n", *ret);
3075 return IMAP_SUCCESS;
3079 /* misc utility functions */
3081 static gchar *strchr_cpy(const gchar *src, gchar ch, gchar *dest, gint len)
3086 tmp = strchr(src, ch);
3090 memcpy(dest, src, MIN(tmp - src, len - 1));
3091 dest[MIN(tmp - src, len - 1)] = '\0';
3096 static gchar *get_quoted(const gchar *src, gchar ch, gchar *dest, gint len)
3098 const gchar *p = src;
3101 g_return_val_if_fail(*p == ch, NULL);
3106 while (*p != '\0' && *p != ch) {
3108 if (*p == '\\' && *(p + 1) != '\0')
3117 return (gchar *)(*p == ch ? p + 1 : p);
3120 static gchar *search_array_contain_str(GPtrArray *array, const gchar *str)
3124 for (i = 0; i < array->len; i++) {
3127 tmp = g_ptr_array_index(array, i);
3128 if (strstr(tmp, str) != NULL)
3135 static gchar *search_array_str(GPtrArray *array, const gchar *str)
3142 for (i = 0; i < array->len; i++) {
3145 tmp = g_ptr_array_index(array, i);
3146 if (!strncmp(tmp, str, len))
3153 static void imap_path_separator_subst(gchar *str, gchar separator)
3156 gboolean in_escape = FALSE;
3158 if (!separator || separator == '/') return;
3160 for (p = str; *p != '\0'; p++) {
3161 if (*p == '/' && !in_escape)
3163 else if (*p == '&' && *(p + 1) != '-' && !in_escape)
3165 else if (*p == '-' && in_escape)
3170 static gchar *imap_modified_utf7_to_locale(const gchar *mutf7_str)
3173 const gchar *from_p;
3176 to = g_malloc(strlen(mutf7_str) + 1);
3179 for (from_p = mutf7_str; *from_p != '\0'; from_p++) {
3180 if (*from_p == '&' && *(from_p + 1) == '-') {
3190 static iconv_t cd = (iconv_t)-1;
3191 static gboolean iconv_ok = TRUE;
3194 size_t norm_utf7_len;
3196 gchar *to_str, *to_p;
3198 gboolean in_escape = FALSE;
3200 if (!iconv_ok) return g_strdup(mutf7_str);
3202 if (cd == (iconv_t)-1) {
3203 cd = iconv_open(conv_get_current_charset_str(), "UTF-7");
3204 if (cd == (iconv_t)-1) {
3205 g_warning("iconv cannot convert UTF-7 to %s\n",
3206 conv_get_current_charset_str());
3208 return g_strdup(mutf7_str);
3212 norm_utf7 = g_string_new(NULL);
3214 for (p = mutf7_str; *p != '\0'; p++) {
3215 /* replace: '&' -> '+',
3217 escaped ',' -> '/' */
3218 if (!in_escape && *p == '&') {
3219 if (*(p + 1) != '-') {
3220 g_string_append_c(norm_utf7, '+');
3223 g_string_append_c(norm_utf7, '&');
3226 } else if (in_escape && *p == ',') {
3227 g_string_append_c(norm_utf7, '/');
3228 } else if (in_escape && *p == '-') {
3229 g_string_append_c(norm_utf7, '-');
3232 g_string_append_c(norm_utf7, *p);
3236 norm_utf7_p = norm_utf7->str;
3237 norm_utf7_len = norm_utf7->len;
3238 to_len = strlen(mutf7_str) * 5;
3239 to_p = to_str = g_malloc(to_len + 1);
3241 if (iconv(cd, (ICONV_CONST gchar **)&norm_utf7_p, &norm_utf7_len,
3242 &to_p, &to_len) == -1) {
3243 g_warning(_("iconv cannot convert UTF-7 to %s\n"),
3244 conv_get_current_charset_str());
3245 g_string_free(norm_utf7, TRUE);
3247 return g_strdup(mutf7_str);
3250 /* second iconv() call for flushing */
3251 iconv(cd, NULL, NULL, &to_p, &to_len);
3252 g_string_free(norm_utf7, TRUE);
3256 #endif /* !HAVE_ICONV */
3259 static gchar *imap_locale_to_modified_utf7(const gchar *from)
3262 const gchar *from_p;
3265 to = g_malloc(strlen(from) * 2 + 1);
3268 for (from_p = from; *from_p != '\0'; from_p++) {
3269 if (*from_p == '&') {
3279 static iconv_t cd = (iconv_t)-1;
3280 static gboolean iconv_ok = TRUE;
3281 gchar *norm_utf7, *norm_utf7_p;
3282 size_t from_len, norm_utf7_len;
3284 gchar *from_tmp, *to, *p;
3285 gboolean in_escape = FALSE;
3287 if (!iconv_ok) return g_strdup(from);
3289 if (cd == (iconv_t)-1) {
3290 cd = iconv_open("UTF-7", conv_get_current_charset_str());
3291 if (cd == (iconv_t)-1) {
3292 g_warning("iconv cannot convert %s to UTF-7\n",
3293 conv_get_current_charset_str());
3295 return g_strdup(from);
3299 Xstrdup_a(from_tmp, from, return g_strdup(from));
3300 from_len = strlen(from);
3301 norm_utf7_len = from_len * 5;
3302 Xalloca(norm_utf7, norm_utf7_len + 1, return g_strdup(from));
3303 norm_utf7_p = norm_utf7;
3305 #define IS_PRINT(ch) (isprint(ch) && isascii(ch))
3307 while (from_len > 0) {
3308 if (*from_tmp == '+') {
3309 *norm_utf7_p++ = '+';
3310 *norm_utf7_p++ = '-';
3314 } else if (IS_PRINT(*from_tmp)) {
3315 /* printable ascii char */
3316 *norm_utf7_p = *from_tmp;
3322 size_t mb_len = 0, conv_len = 0;
3324 /* unprintable char: convert to UTF-7 */
3326 while (!IS_PRINT(*p) && conv_len < from_len) {
3327 mb_len = mblen(p, MB_LEN_MAX);
3329 g_warning("wrong multibyte sequence\n");
3330 return g_strdup(from);
3336 from_len -= conv_len;
3337 if (iconv(cd, (ICONV_CONST gchar **)&from_tmp,
3339 &norm_utf7_p, &norm_utf7_len) == -1) {
3340 g_warning("iconv cannot convert %s to UTF-7\n",
3341 conv_get_current_charset_str());
3342 return g_strdup(from);
3345 /* second iconv() call for flushing */
3346 iconv(cd, NULL, NULL, &norm_utf7_p, &norm_utf7_len);
3352 *norm_utf7_p = '\0';
3353 to_str = g_string_new(NULL);
3354 for (p = norm_utf7; p < norm_utf7_p; p++) {
3355 /* replace: '&' -> "&-",
3358 BASE64 '/' -> ',' */
3359 if (!in_escape && *p == '&') {
3360 g_string_append(to_str, "&-");
3361 } else if (!in_escape && *p == '+') {
3362 if (*(p + 1) == '-') {
3363 g_string_append_c(to_str, '+');
3366 g_string_append_c(to_str, '&');
3369 } else if (in_escape && *p == '/') {
3370 g_string_append_c(to_str, ',');
3371 } else if (in_escape && *p == '-') {
3372 g_string_append_c(to_str, '-');
3375 g_string_append_c(to_str, *p);
3381 g_string_append_c(to_str, '-');
3385 g_string_free(to_str, FALSE);
3388 #endif /* !HAVE_ICONV */
3391 static GSList *imap_get_seq_set_from_numlist(MsgNumberList *numlist)
3394 GSList *sorted_list, *cur;
3395 guint first, last, next;
3397 GSList *ret_list = NULL;
3399 if (numlist == NULL)
3402 str = g_string_sized_new(256);
3404 sorted_list = g_slist_copy(numlist);
3405 sorted_list = g_slist_sort(sorted_list, g_int_compare);
3407 first = GPOINTER_TO_INT(sorted_list->data);
3409 for (cur = sorted_list; cur != NULL; cur = g_slist_next(cur)) {
3410 last = GPOINTER_TO_INT(cur->data);
3412 next = GPOINTER_TO_INT(cur->next->data);
3416 if (last + 1 != next || next == 0) {
3418 g_string_append_c(str, ',');
3420 g_string_sprintfa(str, "%u", first);
3422 g_string_sprintfa(str, "%u:%u", first, last);
3426 if (str->len > IMAP_CMD_LIMIT) {
3427 ret_str = g_strdup(str->str);
3428 ret_list = g_slist_append(ret_list, ret_str);
3429 g_string_truncate(str, 0);
3435 ret_str = g_strdup(str->str);
3436 ret_list = g_slist_append(ret_list, ret_str);
3439 g_slist_free(sorted_list);
3440 g_string_free(str, TRUE);
3445 static GSList *imap_get_seq_set_from_msglist(MsgInfoList *msglist)
3447 MsgNumberList *numlist = NULL;
3451 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
3452 MsgInfo *msginfo = (MsgInfo *) cur->data;
3454 numlist = g_slist_append(numlist, GINT_TO_POINTER(msginfo->msgnum));
3456 seq_list = imap_get_seq_set_from_numlist(numlist);
3457 g_slist_free(numlist);
3462 static void imap_seq_set_free(GSList *seq_list)
3464 slist_free_strings(seq_list);
3465 g_slist_free(seq_list);
3469 static gboolean imap_rename_folder_func(GNode *node, gpointer data)
3471 FolderItem *item = node->data;
3472 gchar **paths = data;
3473 const gchar *oldpath = paths[0];
3474 const gchar *newpath = paths[1];
3476 gchar *new_itempath;
3479 oldpathlen = strlen(oldpath);
3480 if (strncmp(oldpath, item->path, oldpathlen) != 0) {
3481 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
3485 base = item->path + oldpathlen;
3486 while (*base == G_DIR_SEPARATOR) base++;
3488 new_itempath = g_strdup(newpath);
3490 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
3493 item->path = new_itempath;
3498 static gint get_list_of_uids(Folder *folder, IMAPFolderItem *item, GSList **msgnum_list)
3500 gint ok, nummsgs = 0, lastuid_old;
3501 IMAPSession *session;
3502 GSList *uidlist, *elem;
3505 session = imap_session_get(folder);
3506 g_return_val_if_fail(session != NULL, -1);
3508 ok = imap_select(session, IMAP_FOLDER(folder), item->item.path,
3509 NULL, NULL, NULL, NULL);
3510 if (ok != IMAP_SUCCESS)
3513 cmd_buf = g_strdup_printf("UID %d:*", item->lastuid + 1);
3514 ok = imap_cmd_search(session, cmd_buf, &uidlist);
3517 if (ok == IMAP_SOCKET) {
3518 session_destroy((Session *)session);
3519 ((RemoteFolder *)folder)->session = NULL;
3523 if (ok != IMAP_SUCCESS) {
3527 argbuf = g_ptr_array_new();
3529 cmd_buf = g_strdup_printf("UID FETCH %d:* (UID)", item->lastuid + 1);
3530 imap_gen_send(session, cmd_buf);
3532 ok = imap_cmd_ok(session, argbuf);
3533 if (ok != IMAP_SUCCESS) {
3534 ptr_array_free_strings(argbuf);
3535 g_ptr_array_free(argbuf, TRUE);
3539 for(i = 0; i < argbuf->len; i++) {
3542 if((ret = sscanf(g_ptr_array_index(argbuf, i),
3543 "%*d FETCH (UID %d)", &msgnum)) == 1)
3544 uidlist = g_slist_prepend(uidlist, GINT_TO_POINTER(msgnum));
3546 ptr_array_free_strings(argbuf);
3547 g_ptr_array_free(argbuf, TRUE);
3550 lastuid_old = item->lastuid;
3551 *msgnum_list = g_slist_copy(item->uid_list);
3552 nummsgs = g_slist_length(*msgnum_list);
3553 debug_print("Got %d uids from cache\n", g_slist_length(item->uid_list));
3555 for (elem = uidlist; elem != NULL; elem = g_slist_next(elem)) {
3558 msgnum = GPOINTER_TO_INT(elem->data);
3559 if (msgnum > lastuid_old) {
3560 *msgnum_list = g_slist_prepend(*msgnum_list, GINT_TO_POINTER(msgnum));
3561 item->uid_list = g_slist_prepend(item->uid_list, GINT_TO_POINTER(msgnum));
3564 if(msgnum > item->lastuid)
3565 item->lastuid = msgnum;
3568 g_slist_free(uidlist);
3573 gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list)
3575 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3576 IMAPSession *session;
3577 gint ok, nummsgs = 0, exists, recent, unseen, uid_val, uid_next;
3580 gboolean selected_folder;
3582 g_return_val_if_fail(folder != NULL, -1);
3583 g_return_val_if_fail(item != NULL, -1);
3584 g_return_val_if_fail(item->item.path != NULL, -1);
3585 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3586 g_return_val_if_fail(folder->account != NULL, -1);
3588 session = imap_session_get(folder);
3589 g_return_val_if_fail(session != NULL, -1);
3591 selected_folder = (session->mbox != NULL) &&
3592 (!strcmp(session->mbox, item->item.path));
3593 if (selected_folder) {
3594 ok = imap_cmd_noop(session);
3595 if (ok != IMAP_SUCCESS)
3597 exists = session->exists;
3599 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
3600 &exists, &recent, &uid_next, &uid_val, &unseen);
3601 if (ok != IMAP_SUCCESS)
3605 /* If old uid_next matches new uid_next we can be sure no message
3606 was added to the folder */
3607 if (( selected_folder && !session->folder_content_changed) ||
3608 (!selected_folder && uid_next == item->uid_next)) {
3609 nummsgs = g_slist_length(item->uid_list);
3611 /* If number of messages is still the same we
3612 know our caches message numbers are still valid,
3613 otherwise if the number of messages has decrease
3614 we discard our cache to start a new scan to find
3615 out which numbers have been removed */
3616 if (exists == nummsgs) {
3617 *msgnum_list = g_slist_copy(item->uid_list);
3619 } else if (exists < nummsgs) {
3620 debug_print("Freeing imap uid cache");
3622 g_slist_free(item->uid_list);
3623 item->uid_list = NULL;
3626 if (!selected_folder)
3627 item->uid_next = uid_next;
3630 *msgnum_list = NULL;
3634 nummsgs = get_list_of_uids(folder, item, &uidlist);
3636 if (nummsgs != exists) {
3637 /* Cache contains more messages then folder, we have cached
3638 an old UID of a message that was removed and new messages
3639 have been added too, otherwise the uid_next check would
3641 debug_print("Freeing imap uid cache");
3643 g_slist_free(item->uid_list);
3644 item->uid_list = NULL;
3646 g_slist_free(*msgnum_list);
3648 nummsgs = get_list_of_uids(folder, item, &uidlist);
3651 *msgnum_list = uidlist;
3653 dir = folder_item_get_path((FolderItem *)item);
3654 debug_print("removing old messages from %s\n", dir);
3655 remove_numbered_files_not_in_list(dir, *msgnum_list);
3661 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item)
3666 flags.perm_flags = MSG_NEW|MSG_UNREAD;
3667 flags.tmp_flags = 0;
3669 g_return_val_if_fail(item != NULL, NULL);
3670 g_return_val_if_fail(file != NULL, NULL);
3672 if (item->stype == F_QUEUE) {
3673 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
3674 } else if (item->stype == F_DRAFT) {
3675 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
3678 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3679 if (!msginfo) return NULL;
3681 msginfo->folder = item;
3686 GSList *imap_get_msginfos(Folder *folder, FolderItem *item, GSList *msgnum_list)
3688 IMAPSession *session;
3689 MsgInfoList *ret = NULL;
3692 g_return_val_if_fail(folder != NULL, NULL);
3693 g_return_val_if_fail(item != NULL, NULL);
3694 g_return_val_if_fail(msgnum_list != NULL, NULL);
3696 session = imap_session_get(folder);
3697 g_return_val_if_fail(session != NULL, NULL);
3699 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3700 NULL, NULL, NULL, NULL);
3701 if (ok != IMAP_SUCCESS)
3704 if (!(item->stype == F_QUEUE || item->stype == F_DRAFT)) {
3705 ret = g_slist_concat(ret,
3706 imap_get_uncached_messages(
3707 session, item, msgnum_list));
3709 MsgNumberList *sorted_list, *elem;
3710 gint startnum, lastnum;
3712 sorted_list = g_slist_sort(g_slist_copy(msgnum_list), g_int_compare);
3714 startnum = lastnum = GPOINTER_TO_INT(sorted_list->data);
3716 for (elem = sorted_list;; elem = g_slist_next(elem)) {
3720 num = GPOINTER_TO_INT(elem->data);
3722 if (num > lastnum + 1 || elem == NULL) {
3724 for (i = startnum; i <= lastnum; ++i) {
3727 file = imap_fetch_msg(folder, item, i);
3729 MsgInfo *msginfo = imap_parse_msg(file, item);
3730 if (msginfo != NULL) {
3731 msginfo->msgnum = i;
3732 ret = g_slist_append(ret, msginfo);
3746 g_slist_free(sorted_list);
3752 MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid)
3754 MsgInfo *msginfo = NULL;
3755 MsgInfoList *msginfolist;
3756 MsgNumberList numlist;
3758 numlist.next = NULL;
3759 numlist.data = GINT_TO_POINTER(uid);
3761 msginfolist = imap_get_msginfos(folder, item, &numlist);
3762 if (msginfolist != NULL) {
3763 msginfo = msginfolist->data;
3764 g_slist_free(msginfolist);
3770 gboolean imap_check_msgnum_validity(Folder *folder, FolderItem *_item)
3772 IMAPSession *session;
3773 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3774 gint ok, exists = 0, recent = 0, unseen = 0;
3775 guint32 uid_next, uid_validity = 0;
3777 g_return_val_if_fail(folder != NULL, FALSE);
3778 g_return_val_if_fail(item != NULL, FALSE);
3779 g_return_val_if_fail(item->item.folder != NULL, FALSE);
3780 g_return_val_if_fail(FOLDER_CLASS(item->item.folder) == &imap_class, FALSE);
3782 session = imap_session_get(folder);
3783 g_return_val_if_fail(session != NULL, FALSE);
3785 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
3786 &exists, &recent, &uid_next, &uid_validity, &unseen);
3787 if (ok != IMAP_SUCCESS)
3790 if(item->item.mtime == uid_validity)
3793 debug_print("Freeing imap uid cache\n");
3795 g_slist_free(item->uid_list);
3796 item->uid_list = NULL;
3798 item->item.mtime = uid_validity;
3800 imap_delete_all_cached_messages((FolderItem *)item);
3805 void imap_change_flags(Folder *folder, FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
3807 IMAPSession *session;
3808 IMAPFlags flags_set = 0, flags_unset = 0;
3809 gint ok = IMAP_SUCCESS;
3810 MsgNumberList numlist;
3812 g_return_if_fail(folder != NULL);
3813 g_return_if_fail(folder->klass == &imap_class);
3814 g_return_if_fail(item != NULL);
3815 g_return_if_fail(item->folder == folder);
3816 g_return_if_fail(msginfo != NULL);
3817 g_return_if_fail(msginfo->folder == item);
3819 session = imap_session_get(folder);
3820 if (!session) return;
3822 if ((ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
3823 NULL, NULL, NULL, NULL)) != IMAP_SUCCESS)
3826 if (!MSG_IS_MARKED(msginfo->flags) && (newflags & MSG_MARKED))
3827 flags_set |= IMAP_FLAG_FLAGGED;
3828 if ( MSG_IS_MARKED(msginfo->flags) && !(newflags & MSG_MARKED))
3829 flags_unset |= IMAP_FLAG_FLAGGED;
3831 if (!MSG_IS_UNREAD(msginfo->flags) && (newflags & MSG_UNREAD))
3832 flags_unset |= IMAP_FLAG_SEEN;
3833 if ( MSG_IS_UNREAD(msginfo->flags) && !(newflags & MSG_UNREAD))
3834 flags_set |= IMAP_FLAG_SEEN;
3836 if (!MSG_IS_REPLIED(msginfo->flags) && (newflags & MSG_REPLIED))
3837 flags_set |= IMAP_FLAG_ANSWERED;
3838 if ( MSG_IS_REPLIED(msginfo->flags) && !(newflags & MSG_REPLIED))
3839 flags_set |= IMAP_FLAG_ANSWERED;
3841 numlist.next = NULL;
3842 numlist.data = GINT_TO_POINTER(msginfo->msgnum);
3845 ok = imap_set_message_flags(session, &numlist, flags_set, TRUE);
3846 if (ok != IMAP_SUCCESS) return;
3850 ok = imap_set_message_flags(session, &numlist, flags_unset, FALSE);
3851 if (ok != IMAP_SUCCESS) return;
3854 msginfo->flags.perm_flags = newflags;