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 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
948 seq_list = imap_get_seq_set_from_msglist(msglist);
949 uid_mapping = g_relation_new(2);
950 g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
952 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
953 gchar *seq_set = (gchar *)cur->data;
955 debug_print("Copying message %s%c[%s] to %s ...\n",
956 msginfo->folder->path, G_DIR_SEPARATOR,
959 ok = imap_cmd_copy(session, seq_set, destdir, uid_mapping);
960 if (ok != IMAP_SUCCESS) {
961 g_relation_destroy(uid_mapping);
962 imap_seq_set_free(seq_list);
967 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
968 MsgInfo *msginfo = (MsgInfo *)cur->data;
971 tuples = g_relation_select(uid_mapping,
972 GINT_TO_POINTER(msginfo->msgnum),
974 if (tuples->len > 0) {
975 gint num = GPOINTER_TO_INT(g_tuples_index(tuples, 0, 1));
976 g_relation_insert(relation, msginfo,
977 GPOINTER_TO_INT(num));
981 g_relation_insert(relation, msginfo,
983 g_tuples_destroy(tuples);
986 imap_seq_set_free(seq_list);
990 if (ok == IMAP_SUCCESS)
996 gint imap_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1000 g_return_val_if_fail(msginfo != NULL, -1);
1002 msglist.data = msginfo;
1003 msglist.next = NULL;
1005 return imap_copy_msgs(folder, dest, &msglist, NULL);
1008 gint imap_copy_msgs(Folder *folder, FolderItem *dest,
1009 MsgInfoList *msglist, GRelation *relation)
1015 g_return_val_if_fail(folder != NULL, -1);
1016 g_return_val_if_fail(dest != NULL, -1);
1017 g_return_val_if_fail(msglist != NULL, -1);
1019 msginfo = (MsgInfo *)msglist->data;
1020 g_return_val_if_fail(msginfo->folder != NULL, -1);
1022 if (folder == msginfo->folder->folder)
1023 return imap_do_copy_msgs(folder, dest, msglist, relation);
1025 file_list = procmsg_get_message_file_list(msglist);
1026 g_return_val_if_fail(file_list != NULL, -1);
1028 ret = imap_add_msgs(folder, dest, file_list, relation);
1030 procmsg_message_file_list_free(file_list);
1035 gint imap_remove_msg(Folder *folder, FolderItem *item, gint uid)
1038 IMAPSession *session;
1040 MsgNumberList numlist;
1042 g_return_val_if_fail(folder != NULL, -1);
1043 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
1044 g_return_val_if_fail(item != NULL, -1);
1046 session = imap_session_get(folder);
1047 if (!session) return -1;
1049 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
1050 NULL, NULL, NULL, NULL);
1051 if (ok != IMAP_SUCCESS)
1054 numlist.next = NULL;
1055 numlist.data = GINT_TO_POINTER(uid);
1057 ok = imap_set_message_flags
1058 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
1059 &numlist, IMAP_FLAG_DELETED, TRUE);
1060 if (ok != IMAP_SUCCESS) {
1061 log_warning(_("can't set deleted flags: %d\n"), uid);
1065 ok = imap_cmd_expunge(session);
1066 if (ok != IMAP_SUCCESS) {
1067 log_warning(_("can't expunge\n"));
1071 dir = folder_item_get_path(item);
1072 if (is_dir_exist(dir))
1073 remove_numbered_files(dir, uid, uid);
1076 return IMAP_SUCCESS;
1079 gint imap_remove_all_msg(Folder *folder, FolderItem *item)
1081 gint exists, recent, unseen;
1082 guint32 uid_validity;
1084 IMAPSession *session;
1087 g_return_val_if_fail(folder != NULL, -1);
1088 g_return_val_if_fail(item != NULL, -1);
1090 session = imap_session_get(folder);
1091 if (!session) return -1;
1093 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
1094 &exists, &recent, &unseen, &uid_validity);
1095 if (ok != IMAP_SUCCESS)
1098 return IMAP_SUCCESS;
1100 imap_gen_send(session,
1101 "STORE 1:%d +FLAGS.SILENT (\\Deleted)", exists);
1102 ok = imap_cmd_ok(session, NULL);
1103 if (ok != IMAP_SUCCESS) {
1104 log_warning(_("can't set deleted flags: 1:%d\n"), exists);
1108 ok = imap_cmd_expunge(session);
1109 if (ok != IMAP_SUCCESS) {
1110 log_warning(_("can't expunge\n"));
1114 dir = folder_item_get_path(item);
1115 if (is_dir_exist(dir))
1116 remove_all_numbered_files(dir);
1119 return IMAP_SUCCESS;
1122 gboolean imap_is_msg_changed(Folder *folder, FolderItem *item, MsgInfo *msginfo)
1124 /* TODO: properly implement this method */
1128 gint imap_close(Folder *folder, FolderItem *item)
1131 IMAPSession *session;
1133 g_return_val_if_fail(folder != NULL, -1);
1135 session = imap_session_get(folder);
1136 if (!session) return -1;
1138 if (session->mbox) {
1139 ok = imap_cmd_close(session);
1140 if (ok != IMAP_SUCCESS)
1141 log_warning(_("can't close folder\n"));
1143 g_free(session->mbox);
1144 session->mbox = NULL;
1152 void imap_scan_tree(Folder *folder)
1155 IMAPSession *session;
1156 gchar *root_folder = NULL;
1158 g_return_if_fail(folder != NULL);
1159 g_return_if_fail(folder->account != NULL);
1161 session = imap_session_get(folder);
1163 if (!folder->node) {
1164 folder_tree_destroy(folder);
1165 item = folder_item_new(folder, folder->name, NULL);
1166 item->folder = folder;
1167 folder->node = g_node_new(item);
1172 if (folder->account->imap_dir && *folder->account->imap_dir) {
1173 Xstrdup_a(root_folder, folder->account->imap_dir, return);
1174 strtailchomp(root_folder, '/');
1175 debug_print("IMAP root directory: %s\n", root_folder);
1178 item = folder_item_new(folder, folder->name, root_folder);
1179 item->folder = folder;
1180 item->no_select = TRUE;
1181 folder->node = g_node_new(item);
1183 imap_scan_tree_recursive(session, item);
1185 imap_create_missing_folders(folder);
1188 static gint imap_scan_tree_recursive(IMAPSession *session, FolderItem *item)
1191 IMAPFolder *imapfolder;
1192 FolderItem *new_item;
1193 GSList *item_list, *cur;
1195 gchar *wildcard_path;
1199 g_return_val_if_fail(item != NULL, -1);
1200 g_return_val_if_fail(item->folder != NULL, -1);
1201 g_return_val_if_fail(item->no_sub == FALSE, -1);
1203 folder = FOLDER(item->folder);
1204 imapfolder = IMAP_FOLDER(folder);
1206 separator = imap_get_path_separator(imapfolder, item->path);
1208 if (item->folder->ui_func)
1209 item->folder->ui_func(folder, item, folder->ui_func_data);
1212 wildcard[0] = separator;
1215 real_path = imap_get_real_path(imapfolder, item->path);
1219 real_path = g_strdup("");
1222 Xstrcat_a(wildcard_path, real_path, wildcard,
1223 {g_free(real_path); return IMAP_ERROR;});
1224 QUOTE_IF_REQUIRED(wildcard_path, wildcard_path);
1226 imap_gen_send(session, "LIST \"\" %s",
1229 strtailchomp(real_path, separator);
1230 item_list = imap_parse_list(imapfolder, session, real_path, NULL);
1233 for (cur = item_list; cur != NULL; cur = cur->next) {
1234 new_item = cur->data;
1235 if (!strcmp(new_item->path, "INBOX")) {
1236 if (!folder->inbox) {
1237 new_item->stype = F_INBOX;
1238 item->folder->inbox = new_item;
1240 folder_item_destroy(new_item);
1243 } else if (!item->parent || item->stype == F_INBOX) {
1246 base = g_basename(new_item->path);
1248 if (!folder->outbox && !strcasecmp(base, "Sent")) {
1249 new_item->stype = F_OUTBOX;
1250 folder->outbox = new_item;
1251 } else if (!folder->draft && !strcasecmp(base, "Drafts")) {
1252 new_item->stype = F_DRAFT;
1253 folder->draft = new_item;
1254 } else if (!folder->queue && !strcasecmp(base, "Queue")) {
1255 new_item->stype = F_QUEUE;
1256 folder->queue = new_item;
1257 } else if (!folder->trash && !strcasecmp(base, "Trash")) {
1258 new_item->stype = F_TRASH;
1259 folder->trash = new_item;
1262 folder_item_append(item, new_item);
1263 if (new_item->no_sub == FALSE)
1264 imap_scan_tree_recursive(session, new_item);
1267 return IMAP_SUCCESS;
1270 static GSList *imap_parse_list(IMAPFolder *folder, IMAPSession *session,
1271 const gchar *real_path, gchar *separator)
1273 gchar buf[IMAPBUFSIZE];
1275 gchar separator_str[16];
1278 gchar *loc_name, *loc_path;
1279 GSList *item_list = NULL;
1281 FolderItem *new_item;
1283 debug_print("getting list of %s ...\n",
1284 *real_path ? real_path : "\"\"");
1286 str = g_string_new(NULL);
1289 if (sock_gets(SESSION(session)->sock, buf, sizeof(buf)) <= 0) {
1290 log_warning(_("error occurred while getting LIST.\n"));
1294 if (buf[0] != '*' || buf[1] != ' ') {
1295 log_print("IMAP4< %s\n", buf);
1298 debug_print("IMAP4< %s\n", buf);
1300 g_string_assign(str, buf);
1302 if (strncmp(p, "LIST ", 5) != 0) continue;
1305 if (*p != '(') continue;
1307 p = strchr_cpy(p, ')', flags, sizeof(flags));
1309 while (*p == ' ') p++;
1311 p = strchr_cpy(p, ' ', separator_str, sizeof(separator_str));
1313 extract_quote(separator_str, '"');
1314 if (!strcmp(separator_str, "NIL"))
1315 separator_str[0] = '\0';
1317 *separator = separator_str[0];
1320 while (*p == ' ') p++;
1321 if (*p == '{' || *p == '"')
1322 p = imap_parse_atom(SESSION(session)->sock, p,
1323 buf, sizeof(buf), str);
1325 strncpy2(buf, p, sizeof(buf));
1326 strtailchomp(buf, separator_str[0]);
1327 if (buf[0] == '\0') continue;
1328 if (!strcmp(buf, real_path)) continue;
1330 if (separator_str[0] != '\0')
1331 subst_char(buf, separator_str[0], '/');
1332 name = g_basename(buf);
1333 if (name[0] == '.') continue;
1335 loc_name = imap_modified_utf7_to_locale(name);
1336 loc_path = imap_modified_utf7_to_locale(buf);
1337 new_item = folder_item_new(FOLDER(folder), loc_name, loc_path);
1338 if (strcasestr(flags, "\\Noinferiors") != NULL)
1339 new_item->no_sub = TRUE;
1340 if (strcmp(buf, "INBOX") != 0 &&
1341 strcasestr(flags, "\\Noselect") != NULL)
1342 new_item->no_select = TRUE;
1344 item_list = g_slist_append(item_list, new_item);
1346 debug_print("folder %s has been added.\n", loc_path);
1351 g_string_free(str, TRUE);
1356 gint imap_create_tree(Folder *folder)
1358 g_return_val_if_fail(folder != NULL, -1);
1359 g_return_val_if_fail(folder->node != NULL, -1);
1360 g_return_val_if_fail(folder->node->data != NULL, -1);
1361 g_return_val_if_fail(folder->account != NULL, -1);
1363 imap_scan_tree(folder);
1364 imap_create_missing_folders(folder);
1369 static void imap_create_missing_folders(Folder *folder)
1371 g_return_if_fail(folder != NULL);
1374 folder->inbox = imap_create_special_folder
1375 (folder, F_INBOX, "INBOX");
1377 if (!folder->outbox)
1378 folder->outbox = imap_create_special_folder
1379 (folder, F_OUTBOX, "Sent");
1381 folder->draft = imap_create_special_folder
1382 (folder, F_DRAFT, "Drafts");
1384 folder->queue = imap_create_special_folder
1385 (folder, F_QUEUE, "Queue");
1388 folder->trash = imap_create_special_folder
1389 (folder, F_TRASH, "Trash");
1392 static FolderItem *imap_create_special_folder(Folder *folder,
1393 SpecialFolderItemType stype,
1397 FolderItem *new_item;
1399 g_return_val_if_fail(folder != NULL, NULL);
1400 g_return_val_if_fail(folder->node != NULL, NULL);
1401 g_return_val_if_fail(folder->node->data != NULL, NULL);
1402 g_return_val_if_fail(folder->account != NULL, NULL);
1403 g_return_val_if_fail(name != NULL, NULL);
1405 item = FOLDER_ITEM(folder->node->data);
1406 new_item = imap_create_folder(folder, item, name);
1409 g_warning("Can't create '%s'\n", name);
1410 if (!folder->inbox) return NULL;
1412 new_item = imap_create_folder(folder, folder->inbox, name);
1414 g_warning("Can't create '%s' under INBOX\n", name);
1416 new_item->stype = stype;
1418 new_item->stype = stype;
1423 static gchar *imap_folder_get_path(Folder *folder)
1427 g_return_val_if_fail(folder != NULL, NULL);
1428 g_return_val_if_fail(folder->account != NULL, NULL);
1430 folder_path = g_strconcat(get_imap_cache_dir(),
1432 folder->account->recv_server,
1434 folder->account->userid,
1440 static gchar *imap_item_get_path(Folder *folder, FolderItem *item)
1442 gchar *folder_path, *path;
1444 g_return_val_if_fail(folder != NULL, NULL);
1445 g_return_val_if_fail(item != NULL, NULL);
1446 folder_path = imap_folder_get_path(folder);
1448 g_return_val_if_fail(folder_path != NULL, NULL);
1449 if (folder_path[0] == G_DIR_SEPARATOR) {
1451 path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
1454 path = g_strdup(folder_path);
1457 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1458 folder_path, G_DIR_SEPARATOR_S,
1461 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1464 g_free(folder_path);
1469 FolderItem *imap_create_folder(Folder *folder, FolderItem *parent,
1472 gchar *dirpath, *imap_path;
1473 IMAPSession *session;
1474 FolderItem *new_item;
1480 g_return_val_if_fail(folder != NULL, NULL);
1481 g_return_val_if_fail(folder->account != NULL, NULL);
1482 g_return_val_if_fail(parent != NULL, NULL);
1483 g_return_val_if_fail(name != NULL, NULL);
1485 session = imap_session_get(folder);
1486 if (!session) return NULL;
1488 if (!parent->parent && strcmp(name, "INBOX") == 0)
1489 dirpath = g_strdup(name);
1490 else if (parent->path)
1491 dirpath = g_strconcat(parent->path, "/", name, NULL);
1492 else if ((p = strchr(name, '/')) != NULL && *(p + 1) != '\0')
1493 dirpath = g_strdup(name);
1494 else if (folder->account->imap_dir && *folder->account->imap_dir) {
1497 Xstrdup_a(imap_dir, folder->account->imap_dir, return NULL);
1498 strtailchomp(imap_dir, '/');
1499 dirpath = g_strconcat(imap_dir, "/", name, NULL);
1501 dirpath = g_strdup(name);
1503 /* keep trailing directory separator to create a folder that contains
1505 imap_path = imap_locale_to_modified_utf7(dirpath);
1506 strtailchomp(dirpath, '/');
1507 Xstrdup_a(new_name, name, {g_free(dirpath); return NULL;});
1508 strtailchomp(new_name, '/');
1509 separator = imap_get_path_separator(IMAP_FOLDER(folder), imap_path);
1510 imap_path_separator_subst(imap_path, separator);
1511 subst_char(new_name, '/', separator);
1513 if (strcmp(name, "INBOX") != 0) {
1516 gboolean exist = FALSE;
1518 argbuf = g_ptr_array_new();
1519 ok = imap_cmd_list(session, NULL, imap_path,
1521 if (ok != IMAP_SUCCESS) {
1522 log_warning(_("can't create mailbox: LIST failed\n"));
1525 ptr_array_free_strings(argbuf);
1526 g_ptr_array_free(argbuf, TRUE);
1530 for (i = 0; i < argbuf->len; i++) {
1532 str = g_ptr_array_index(argbuf, i);
1533 if (!strncmp(str, "LIST ", 5)) {
1538 ptr_array_free_strings(argbuf);
1539 g_ptr_array_free(argbuf, TRUE);
1542 ok = imap_cmd_create(session, imap_path);
1543 if (ok != IMAP_SUCCESS) {
1544 log_warning(_("can't create mailbox\n"));
1552 new_item = folder_item_new(folder, new_name, dirpath);
1553 folder_item_append(parent, new_item);
1557 dirpath = folder_item_get_path(new_item);
1558 if (!is_dir_exist(dirpath))
1559 make_dir_hier(dirpath);
1565 gint imap_rename_folder(Folder *folder, FolderItem *item, const gchar *name)
1569 gchar *real_oldpath;
1570 gchar *real_newpath;
1573 gchar *old_cache_dir;
1574 gchar *new_cache_dir;
1575 IMAPSession *session;
1578 gint exists, recent, unseen;
1579 guint32 uid_validity;
1581 g_return_val_if_fail(folder != NULL, -1);
1582 g_return_val_if_fail(item != NULL, -1);
1583 g_return_val_if_fail(item->path != NULL, -1);
1584 g_return_val_if_fail(name != NULL, -1);
1586 session = imap_session_get(folder);
1587 if (!session) return -1;
1589 real_oldpath = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1591 g_free(session->mbox);
1592 session->mbox = NULL;
1593 ok = imap_cmd_examine(session, "INBOX",
1594 &exists, &recent, &unseen, &uid_validity);
1595 if (ok != IMAP_SUCCESS) {
1596 g_free(real_oldpath);
1600 separator = imap_get_path_separator(IMAP_FOLDER(folder), item->path);
1601 if (strchr(item->path, G_DIR_SEPARATOR)) {
1602 dirpath = g_dirname(item->path);
1603 newpath = g_strconcat(dirpath, G_DIR_SEPARATOR_S, name, NULL);
1606 newpath = g_strdup(name);
1608 real_newpath = imap_locale_to_modified_utf7(newpath);
1609 imap_path_separator_subst(real_newpath, separator);
1611 ok = imap_cmd_rename(session, real_oldpath, real_newpath);
1612 if (ok != IMAP_SUCCESS) {
1613 log_warning(_("can't rename mailbox: %s to %s\n"),
1614 real_oldpath, real_newpath);
1615 g_free(real_oldpath);
1617 g_free(real_newpath);
1622 item->name = g_strdup(name);
1624 old_cache_dir = folder_item_get_path(item);
1626 node = g_node_find(item->folder->node, G_PRE_ORDER, G_TRAVERSE_ALL,
1628 paths[0] = g_strdup(item->path);
1630 g_node_traverse(node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1631 imap_rename_folder_func, paths);
1633 if (is_dir_exist(old_cache_dir)) {
1634 new_cache_dir = folder_item_get_path(item);
1635 if (rename(old_cache_dir, new_cache_dir) < 0) {
1636 FILE_OP_ERROR(old_cache_dir, "rename");
1638 g_free(new_cache_dir);
1641 g_free(old_cache_dir);
1644 g_free(real_oldpath);
1645 g_free(real_newpath);
1650 gint imap_remove_folder(Folder *folder, FolderItem *item)
1653 IMAPSession *session;
1656 gint exists, recent, unseen;
1657 guint32 uid_validity;
1659 g_return_val_if_fail(folder != NULL, -1);
1660 g_return_val_if_fail(item != NULL, -1);
1661 g_return_val_if_fail(item->path != NULL, -1);
1663 session = imap_session_get(folder);
1664 if (!session) return -1;
1666 path = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1668 ok = imap_cmd_examine(session, "INBOX",
1669 &exists, &recent, &unseen, &uid_validity);
1670 if (ok != IMAP_SUCCESS) {
1675 ok = imap_cmd_delete(session, path);
1676 if (ok != IMAP_SUCCESS) {
1677 log_warning(_("can't delete mailbox\n"));
1683 cache_dir = folder_item_get_path(item);
1684 if (is_dir_exist(cache_dir) && remove_dir_recursive(cache_dir) < 0)
1685 g_warning("can't remove directory '%s'\n", cache_dir);
1687 folder_item_remove(item);
1692 static GSList *imap_get_uncached_messages(IMAPSession *session,
1694 MsgNumberList *numlist)
1697 GSList *newlist = NULL;
1698 GSList *llast = NULL;
1701 GSList *seq_list, *cur;
1704 g_return_val_if_fail(session != NULL, NULL);
1705 g_return_val_if_fail(item != NULL, NULL);
1706 g_return_val_if_fail(item->folder != NULL, NULL);
1707 g_return_val_if_fail(FOLDER_CLASS(item->folder) == &imap_class, NULL);
1709 seq_list = imap_get_seq_set_from_numlist(numlist);
1710 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
1711 imapset = cur->data;
1713 if (imap_cmd_envelope(session, imapset)
1715 log_warning(_("can't get envelope\n"));
1719 str = g_string_new(NULL);
1722 if ((tmp = sock_getline(SESSION(session)->sock)) == NULL) {
1723 log_warning(_("error occurred while getting envelope.\n"));
1724 g_string_free(str, TRUE);
1728 if (tmp[0] != '*' || tmp[1] != ' ') {
1729 log_print("IMAP4< %s\n", tmp);
1733 if (strstr(tmp, "FETCH") == NULL) {
1734 log_print("IMAP4< %s\n", tmp);
1738 log_print("IMAP4< %s\n", tmp);
1739 g_string_assign(str, tmp);
1742 msginfo = imap_parse_envelope
1743 (SESSION(session)->sock, item, str);
1745 log_warning(_("can't parse envelope: %s\n"), str->str);
1748 if (item->stype == F_QUEUE) {
1749 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_QUEUED);
1750 } else if (item->stype == F_DRAFT) {
1751 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_DRAFT);
1754 msginfo->folder = item;
1757 llast = newlist = g_slist_append(newlist, msginfo);
1759 llast = g_slist_append(llast, msginfo);
1760 llast = llast->next;
1764 g_string_free(str, TRUE);
1766 imap_seq_set_free(seq_list);
1771 static void imap_delete_all_cached_messages(FolderItem *item)
1775 g_return_if_fail(item != NULL);
1776 g_return_if_fail(item->folder != NULL);
1777 g_return_if_fail(FOLDER_CLASS(item->folder) == &imap_class);
1779 debug_print("Deleting all cached messages...\n");
1781 dir = folder_item_get_path(item);
1782 if (is_dir_exist(dir))
1783 remove_all_numbered_files(dir);
1786 debug_print("done.\n");
1790 static SockInfo *imap_open_tunnel(const gchar *server,
1791 const gchar *tunnelcmd,
1794 static SockInfo *imap_open_tunnel(const gchar *server,
1795 const gchar *tunnelcmd)
1800 if ((sock = sock_connect_cmd(server, tunnelcmd)) == NULL) {
1801 log_warning(_("Can't establish IMAP4 session with: %s\n"),
1806 return imap_init_sock(sock, ssl_type);
1808 return imap_init_sock(sock);
1814 static SockInfo *imap_open(const gchar *server, gushort port,
1817 static SockInfo *imap_open(const gchar *server, gushort port)
1822 if ((sock = sock_connect(server, port)) == NULL) {
1823 log_warning(_("Can't connect to IMAP4 server: %s:%d\n"),
1829 if (ssl_type == SSL_TUNNEL && !ssl_init_socket(sock)) {
1830 log_warning(_("Can't establish IMAP4 session with: %s:%d\n"),
1840 static SockInfo *imap_init_sock(SockInfo *sock, SSLType ssl_type)
1842 static SockInfo *imap_init_sock(SockInfo *sock)
1849 static GList *imap_parse_namespace_str(gchar *str)
1854 IMAPNameSpace *namespace;
1855 GList *ns_list = NULL;
1857 while (*p != '\0') {
1858 /* parse ("#foo" "/") */
1860 while (*p && *p != '(') p++;
1861 if (*p == '\0') break;
1864 while (*p && *p != '"') p++;
1865 if (*p == '\0') break;
1869 while (*p && *p != '"') p++;
1870 if (*p == '\0') break;
1874 while (*p && isspace(*p)) p++;
1875 if (*p == '\0') break;
1876 if (strncmp(p, "NIL", 3) == 0)
1878 else if (*p == '"') {
1881 while (*p && *p != '"') p++;
1882 if (*p == '\0') break;
1887 while (*p && *p != ')') p++;
1888 if (*p == '\0') break;
1891 namespace = g_new(IMAPNameSpace, 1);
1892 namespace->name = g_strdup(name);
1893 namespace->separator = separator ? separator[0] : '\0';
1894 ns_list = g_list_append(ns_list, namespace);
1900 static void imap_parse_namespace(IMAPSession *session, IMAPFolder *folder)
1905 g_return_if_fail(session != NULL);
1906 g_return_if_fail(folder != NULL);
1908 if (folder->ns_personal != NULL ||
1909 folder->ns_others != NULL ||
1910 folder->ns_shared != NULL)
1913 if (!imap_has_capability(session, "NAMESPACE")) {
1914 imap_get_namespace_by_list(session, folder);
1918 if (imap_cmd_namespace(session, &ns_str)
1920 log_warning(_("can't get namespace\n"));
1924 str_array = strsplit_parenthesis(ns_str, '(', ')', 3);
1925 if (str_array == NULL) {
1927 imap_get_namespace_by_list(session, folder);
1931 folder->ns_personal = imap_parse_namespace_str(str_array[0]);
1932 if (str_array[0] && str_array[1])
1933 folder->ns_others = imap_parse_namespace_str(str_array[1]);
1934 if (str_array[0] && str_array[1] && str_array[2])
1935 folder->ns_shared = imap_parse_namespace_str(str_array[2]);
1936 g_strfreev(str_array);
1940 static void imap_get_namespace_by_list(IMAPSession *session, IMAPFolder *folder)
1942 GSList *item_list, *cur;
1943 gchar separator = '\0';
1944 IMAPNameSpace *namespace;
1946 g_return_if_fail(session != NULL);
1947 g_return_if_fail(folder != NULL);
1949 if (folder->ns_personal != NULL ||
1950 folder->ns_others != NULL ||
1951 folder->ns_shared != NULL)
1954 imap_gen_send(session, "LIST \"\" \"\"");
1955 item_list = imap_parse_list(folder, session, "", &separator);
1956 for (cur = item_list; cur != NULL; cur = cur->next)
1957 folder_item_destroy(FOLDER_ITEM(cur->data));
1958 g_slist_free(item_list);
1960 namespace = g_new(IMAPNameSpace, 1);
1961 namespace->name = g_strdup("");
1962 namespace->separator = separator;
1963 folder->ns_personal = g_list_append(NULL, namespace);
1966 static IMAPNameSpace *imap_find_namespace_from_list(GList *ns_list,
1969 IMAPNameSpace *namespace = NULL;
1970 gchar *tmp_path, *name;
1972 if (!path) path = "";
1974 Xstrcat_a(tmp_path, path, "/", return NULL);
1976 for (; ns_list != NULL; ns_list = ns_list->next) {
1977 IMAPNameSpace *tmp_ns = ns_list->data;
1979 Xstrdup_a(name, tmp_ns->name, return namespace);
1980 if (tmp_ns->separator && tmp_ns->separator != '/')
1981 subst_char(name, tmp_ns->separator, '/');
1982 if (strncmp(tmp_path, name, strlen(name)) == 0)
1989 static IMAPNameSpace *imap_find_namespace(IMAPFolder *folder,
1992 IMAPNameSpace *namespace;
1994 g_return_val_if_fail(folder != NULL, NULL);
1996 namespace = imap_find_namespace_from_list(folder->ns_personal, path);
1997 if (namespace) return namespace;
1998 namespace = imap_find_namespace_from_list(folder->ns_others, path);
1999 if (namespace) return namespace;
2000 namespace = imap_find_namespace_from_list(folder->ns_shared, path);
2001 if (namespace) return namespace;
2006 static gchar imap_get_path_separator(IMAPFolder *folder, const gchar *path)
2008 IMAPNameSpace *namespace;
2009 gchar separator = '/';
2011 namespace = imap_find_namespace(folder, path);
2012 if (namespace && namespace->separator)
2013 separator = namespace->separator;
2018 static gchar *imap_get_real_path(IMAPFolder *folder, const gchar *path)
2023 g_return_val_if_fail(folder != NULL, NULL);
2024 g_return_val_if_fail(path != NULL, NULL);
2026 real_path = imap_locale_to_modified_utf7(path);
2027 separator = imap_get_path_separator(folder, path);
2028 imap_path_separator_subst(real_path, separator);
2033 static gchar *imap_parse_atom(SockInfo *sock, gchar *src,
2034 gchar *dest, gint dest_len, GString *str)
2036 gchar *cur_pos = src;
2039 g_return_val_if_fail(str != NULL, cur_pos);
2041 /* read the next line if the current response buffer is empty */
2042 while (isspace(*cur_pos)) cur_pos++;
2043 while (*cur_pos == '\0') {
2044 if ((nextline = sock_getline(sock)) == NULL)
2046 g_string_assign(str, nextline);
2048 strretchomp(nextline);
2049 /* log_print("IMAP4< %s\n", nextline); */
2050 debug_print("IMAP4< %s\n", nextline);
2053 while (isspace(*cur_pos)) cur_pos++;
2056 if (!strncmp(cur_pos, "NIL", 3)) {
2059 } else if (*cur_pos == '\"') {
2062 p = get_quoted(cur_pos, '\"', dest, dest_len);
2063 cur_pos = p ? p : cur_pos + 2;
2064 } else if (*cur_pos == '{') {
2069 cur_pos = strchr_cpy(cur_pos + 1, '}', buf, sizeof(buf));
2071 g_return_val_if_fail(len > 0, cur_pos);
2073 g_string_truncate(str, 0);
2077 if ((nextline = sock_getline(sock)) == NULL)
2079 line_len += strlen(nextline);
2080 g_string_append(str, nextline);
2082 strretchomp(nextline);
2083 /* log_print("IMAP4< %s\n", nextline); */
2084 debug_print("IMAP4< %s\n", nextline);
2086 } while (line_len < len);
2088 memcpy(dest, cur_pos, MIN(len, dest_len - 1));
2089 dest[MIN(len, dest_len - 1)] = '\0';
2096 static gchar *imap_get_header(SockInfo *sock, gchar *cur_pos, gchar **headers,
2106 g_return_val_if_fail(str != NULL, cur_pos);
2108 while (isspace(*cur_pos)) cur_pos++;
2110 g_return_val_if_fail(*cur_pos == '{', cur_pos);
2112 cur_pos = strchr_cpy(cur_pos + 1, '}', buf, sizeof(buf));
2114 g_return_val_if_fail(len > 0, cur_pos);
2116 g_string_truncate(str, 0);
2120 if ((nextline = sock_getline(sock)) == NULL)
2122 block_len += strlen(nextline);
2123 g_string_append(str, nextline);
2125 strretchomp(nextline);
2126 /* debug_print("IMAP4< %s\n", nextline); */
2128 } while (block_len < len);
2130 debug_print("IMAP4< [contents of BODY.PEEK[HEADER.FIELDS (...)]]\n");
2132 *headers = g_strndup(cur_pos, len);
2135 while (isspace(*cur_pos)) cur_pos++;
2136 while (*cur_pos == '\0') {
2137 if ((nextline = sock_getline(sock)) == NULL)
2139 g_string_assign(str, nextline);
2141 strretchomp(nextline);
2142 debug_print("IMAP4< %s\n", nextline);
2145 while (isspace(*cur_pos)) cur_pos++;
2151 static MsgFlags imap_parse_flags(const gchar *flag_str)
2153 const gchar *p = flag_str;
2154 MsgFlags flags = {0, 0};
2156 flags.perm_flags = MSG_UNREAD;
2158 while ((p = strchr(p, '\\')) != NULL) {
2161 if (g_strncasecmp(p, "Recent", 6) == 0 && MSG_IS_UNREAD(flags)) {
2162 MSG_SET_PERM_FLAGS(flags, MSG_NEW);
2163 } else if (g_strncasecmp(p, "Seen", 4) == 0) {
2164 MSG_UNSET_PERM_FLAGS(flags, MSG_NEW|MSG_UNREAD);
2165 } else if (g_strncasecmp(p, "Deleted", 7) == 0) {
2166 MSG_SET_PERM_FLAGS(flags, MSG_DELETED);
2167 } else if (g_strncasecmp(p, "Flagged", 7) == 0) {
2168 MSG_SET_PERM_FLAGS(flags, MSG_MARKED);
2169 } else if (g_strncasecmp(p, "Answered", 8) == 0) {
2170 MSG_SET_PERM_FLAGS(flags, MSG_REPLIED);
2177 static MsgInfo *imap_parse_envelope(SockInfo *sock, FolderItem *item,
2180 gchar buf[IMAPBUFSIZE];
2181 MsgInfo *msginfo = NULL;
2186 MsgFlags flags = {0, 0}, imap_flags = {0, 0};
2188 g_return_val_if_fail(line_str != NULL, NULL);
2189 g_return_val_if_fail(line_str->str[0] == '*' &&
2190 line_str->str[1] == ' ', NULL);
2192 MSG_SET_TMP_FLAGS(flags, MSG_IMAP);
2193 if (item->stype == F_QUEUE) {
2194 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
2195 } else if (item->stype == F_DRAFT) {
2196 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
2199 cur_pos = line_str->str + 2;
2201 #define PARSE_ONE_ELEMENT(ch) \
2203 cur_pos = strchr_cpy(cur_pos, ch, buf, sizeof(buf)); \
2204 if (cur_pos == NULL) { \
2205 g_warning("cur_pos == NULL\n"); \
2206 procmsg_msginfo_free(msginfo); \
2211 PARSE_ONE_ELEMENT(' ');
2214 PARSE_ONE_ELEMENT(' ');
2215 g_return_val_if_fail(!strcmp(buf, "FETCH"), NULL);
2217 g_return_val_if_fail(*cur_pos == '(', NULL);
2220 while (*cur_pos != '\0' && *cur_pos != ')') {
2221 while (*cur_pos == ' ') cur_pos++;
2223 if (!strncmp(cur_pos, "UID ", 4)) {
2225 uid = strtoul(cur_pos, &cur_pos, 10);
2226 } else if (!strncmp(cur_pos, "FLAGS ", 6)) {
2228 if (*cur_pos != '(') {
2229 g_warning("*cur_pos != '('\n");
2230 procmsg_msginfo_free(msginfo);
2234 PARSE_ONE_ELEMENT(')');
2235 imap_flags = imap_parse_flags(buf);
2236 } else if (!strncmp(cur_pos, "RFC822.SIZE ", 12)) {
2238 size = strtol(cur_pos, &cur_pos, 10);
2239 } else if (!strncmp(cur_pos, "BODY[HEADER.FIELDS ", 19)) {
2243 if (*cur_pos != '(') {
2244 g_warning("*cur_pos != '('\n");
2245 procmsg_msginfo_free(msginfo);
2249 PARSE_ONE_ELEMENT(')');
2250 if (*cur_pos != ']') {
2251 g_warning("*cur_pos != ']'\n");
2252 procmsg_msginfo_free(msginfo);
2257 cur_pos = imap_get_header(sock, cur_pos, &headers,
2259 msginfo = procheader_parse_str(headers, flags, FALSE, FALSE);
2262 g_warning("invalid FETCH response: %s\n", cur_pos);
2268 msginfo->msgnum = uid;
2269 msginfo->size = size;
2270 msginfo->flags.tmp_flags |= imap_flags.tmp_flags;
2271 msginfo->flags.perm_flags = imap_flags.perm_flags;
2277 static gchar *imap_get_flag_str(IMAPFlags flags)
2282 str = g_string_new(NULL);
2284 if (IMAP_IS_SEEN(flags)) g_string_append(str, "\\Seen ");
2285 if (IMAP_IS_ANSWERED(flags)) g_string_append(str, "\\Answered ");
2286 if (IMAP_IS_FLAGGED(flags)) g_string_append(str, "\\Flagged ");
2287 if (IMAP_IS_DELETED(flags)) g_string_append(str, "\\Deleted ");
2288 if (IMAP_IS_DRAFT(flags)) g_string_append(str, "\\Draft");
2290 if (str->len > 0 && str->str[str->len - 1] == ' ')
2291 g_string_truncate(str, str->len - 1);
2294 g_string_free(str, FALSE);
2299 static gint imap_set_message_flags(IMAPSession *session,
2300 MsgNumberList *numlist,
2307 GSList *seq_list, *cur;
2310 flag_str = imap_get_flag_str(flags);
2311 cmd = g_strconcat(is_set ? "+FLAGS.SILENT (" : "-FLAGS.SILENT (",
2312 flag_str, ")", NULL);
2315 seq_list = imap_get_seq_set_from_numlist(numlist);
2316 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
2317 imapset = cur->data;
2319 ok = imap_cmd_store(session, imapset, cmd);
2321 imap_seq_set_free(seq_list);
2327 static gint imap_select(IMAPSession *session, IMAPFolder *folder,
2329 gint *exists, gint *recent, gint *unseen,
2330 guint32 *uid_validity)
2334 gint exists_, recent_, unseen_, uid_validity_;
2336 if (!exists || !recent || !unseen || !uid_validity) {
2337 if (session->mbox && strcmp(session->mbox, path) == 0)
2338 return IMAP_SUCCESS;
2342 uid_validity = &uid_validity_;
2345 g_free(session->mbox);
2346 session->mbox = NULL;
2348 real_path = imap_get_real_path(folder, path);
2349 ok = imap_cmd_select(session, real_path,
2350 exists, recent, unseen, uid_validity);
2351 if (ok != IMAP_SUCCESS)
2352 log_warning(_("can't select folder: %s\n"), real_path);
2354 session->mbox = g_strdup(path);
2355 session->folder_content_changed = FALSE;
2362 #define THROW(err) { ok = err; goto catch; }
2364 static gint imap_status(IMAPSession *session, IMAPFolder *folder,
2366 gint *messages, gint *recent,
2367 guint32 *uid_next, guint32 *uid_validity,
2376 *messages = *recent = *uid_next = *uid_validity = *unseen = 0;
2378 argbuf = g_ptr_array_new();
2380 real_path = imap_get_real_path(folder, path);
2381 QUOTE_IF_REQUIRED(real_path_, real_path);
2382 imap_gen_send(session, "STATUS %s "
2383 "(MESSAGES RECENT UIDNEXT UIDVALIDITY UNSEEN)",
2386 ok = imap_cmd_ok(session, argbuf);
2387 if (ok != IMAP_SUCCESS) THROW(ok);
2389 str = search_array_str(argbuf, "STATUS");
2390 if (!str) THROW(IMAP_ERROR);
2392 str = strchr(str, '(');
2393 if (!str) THROW(IMAP_ERROR);
2395 while (*str != '\0' && *str != ')') {
2396 while (*str == ' ') str++;
2398 if (!strncmp(str, "MESSAGES ", 9)) {
2400 *messages = strtol(str, &str, 10);
2401 } else if (!strncmp(str, "RECENT ", 7)) {
2403 *recent = strtol(str, &str, 10);
2404 } else if (!strncmp(str, "UIDNEXT ", 8)) {
2406 *uid_next = strtoul(str, &str, 10);
2407 } else if (!strncmp(str, "UIDVALIDITY ", 12)) {
2409 *uid_validity = strtoul(str, &str, 10);
2410 } else if (!strncmp(str, "UNSEEN ", 7)) {
2412 *unseen = strtol(str, &str, 10);
2414 g_warning("invalid STATUS response: %s\n", str);
2421 ptr_array_free_strings(argbuf);
2422 g_ptr_array_free(argbuf, TRUE);
2429 static gboolean imap_has_capability(IMAPSession *session, const gchar *cap)
2433 for (p = session->capability; *p != NULL; ++p) {
2434 if (!g_strcasecmp(*p, cap))
2441 void imap_free_capabilities(IMAPSession *session)
2443 g_strfreev(session->capability);
2444 session->capability = NULL;
2447 /* low-level IMAP4rev1 commands */
2449 static gint imap_cmd_authenticate(IMAPSession *session, const gchar *user,
2450 const gchar *pass, IMAPAuthType type)
2457 gchar hexdigest[33];
2461 auth_type = "CRAM-MD5";
2463 imap_gen_send(session, "AUTHENTICATE %s", auth_type);
2464 ok = imap_gen_recv(session, &buf);
2465 if (ok != IMAP_SUCCESS || buf[0] != '+' || buf[1] != ' ') {
2470 challenge = g_malloc(strlen(buf + 2) + 1);
2471 challenge_len = base64_decode(challenge, buf + 2, -1);
2472 challenge[challenge_len] = '\0';
2474 log_print("IMAP< [Decoded: %s]\n", challenge);
2476 md5_hex_hmac(hexdigest, challenge, challenge_len, pass, strlen(pass));
2479 response = g_strdup_printf("%s %s", user, hexdigest);
2480 log_print("IMAP> [Encoded: %s]\n", response);
2481 response64 = g_malloc((strlen(response) + 3) * 2 + 1);
2482 base64_encode(response64, response, strlen(response));
2485 log_print("IMAP> %s\n", response64);
2486 sock_puts(SESSION(session)->sock, response64);
2487 ok = imap_cmd_ok(session, NULL);
2488 if (ok != IMAP_SUCCESS)
2489 log_warning(_("IMAP4 authentication failed.\n"));
2494 static gint imap_cmd_login(IMAPSession *session,
2495 const gchar *user, const gchar *pass)
2497 gchar *user_, *pass_;
2500 QUOTE_IF_REQUIRED(user_, user);
2501 QUOTE_IF_REQUIRED(pass_, pass);
2502 imap_gen_send(session, "LOGIN %s %s", user_, pass_);
2504 ok = imap_cmd_ok(session, NULL);
2505 if (ok != IMAP_SUCCESS)
2506 log_warning(_("IMAP4 login failed.\n"));
2511 static gint imap_cmd_logout(IMAPSession *session)
2513 imap_gen_send(session, "LOGOUT");
2514 return imap_cmd_ok(session, NULL);
2517 static gint imap_cmd_noop(IMAPSession *session)
2519 imap_gen_send(session, "NOOP");
2520 return imap_cmd_ok(session, NULL);
2523 static gint imap_cmd_starttls(IMAPSession *session)
2525 imap_gen_send(session, "STARTTLS");
2526 return imap_cmd_ok(session, NULL);
2529 #define THROW(err) { ok = err; goto catch; }
2531 static gint imap_cmd_namespace(IMAPSession *session, gchar **ns_str)
2537 argbuf = g_ptr_array_new();
2539 imap_gen_send(session, "NAMESPACE");
2540 if ((ok = imap_cmd_ok(session, argbuf)) != IMAP_SUCCESS) THROW(ok);
2542 str = search_array_str(argbuf, "NAMESPACE");
2543 if (!str) THROW(IMAP_ERROR);
2545 *ns_str = g_strdup(str);
2548 ptr_array_free_strings(argbuf);
2549 g_ptr_array_free(argbuf, TRUE);
2556 static gint imap_cmd_list(IMAPSession *session, const gchar *ref,
2557 const gchar *mailbox, GPtrArray *argbuf)
2559 gchar *ref_, *mailbox_;
2561 if (!ref) ref = "\"\"";
2562 if (!mailbox) mailbox = "\"\"";
2564 QUOTE_IF_REQUIRED(ref_, ref);
2565 QUOTE_IF_REQUIRED(mailbox_, mailbox);
2566 imap_gen_send(session, "LIST %s %s", ref_, mailbox_);
2568 return imap_cmd_ok(session, argbuf);
2571 #define THROW goto catch
2573 static gint imap_cmd_do_select(IMAPSession *session, const gchar *folder,
2575 gint *exists, gint *recent, gint *unseen,
2576 guint32 *uid_validity)
2584 *exists = *recent = *unseen = *uid_validity = 0;
2585 argbuf = g_ptr_array_new();
2588 select_cmd = "EXAMINE";
2590 select_cmd = "SELECT";
2592 QUOTE_IF_REQUIRED(folder_, folder);
2593 imap_gen_send(session, "%s %s", select_cmd, folder_);
2595 if ((ok = imap_cmd_ok(session, argbuf)) != IMAP_SUCCESS) THROW;
2597 resp_str = search_array_contain_str(argbuf, "EXISTS");
2599 if (sscanf(resp_str,"%d EXISTS", exists) != 1) {
2600 g_warning("imap_cmd_select(): invalid EXISTS line.\n");
2605 resp_str = search_array_contain_str(argbuf, "RECENT");
2607 if (sscanf(resp_str, "%d RECENT", recent) != 1) {
2608 g_warning("imap_cmd_select(): invalid RECENT line.\n");
2613 resp_str = search_array_contain_str(argbuf, "UIDVALIDITY");
2615 if (sscanf(resp_str, "OK [UIDVALIDITY %u] ", uid_validity)
2617 g_warning("imap_cmd_select(): invalid UIDVALIDITY line.\n");
2622 resp_str = search_array_contain_str(argbuf, "UNSEEN");
2624 if (sscanf(resp_str, "OK [UNSEEN %d] ", unseen) != 1) {
2625 g_warning("imap_cmd_select(): invalid UNSEEN line.\n");
2631 ptr_array_free_strings(argbuf);
2632 g_ptr_array_free(argbuf, TRUE);
2637 static gint imap_cmd_select(IMAPSession *session, const gchar *folder,
2638 gint *exists, gint *recent, gint *unseen,
2639 guint32 *uid_validity)
2641 return imap_cmd_do_select(session, folder, FALSE,
2642 exists, recent, unseen, uid_validity);
2645 static gint imap_cmd_examine(IMAPSession *session, const gchar *folder,
2646 gint *exists, gint *recent, gint *unseen,
2647 guint32 *uid_validity)
2649 return imap_cmd_do_select(session, folder, TRUE,
2650 exists, recent, unseen, uid_validity);
2655 static gint imap_cmd_create(IMAPSession *session, const gchar *folder)
2659 QUOTE_IF_REQUIRED(folder_, folder);
2660 imap_gen_send(session, "CREATE %s", folder_);
2662 return imap_cmd_ok(session, NULL);
2665 static gint imap_cmd_rename(IMAPSession *session, const gchar *old_folder,
2666 const gchar *new_folder)
2668 gchar *old_folder_, *new_folder_;
2670 QUOTE_IF_REQUIRED(old_folder_, old_folder);
2671 QUOTE_IF_REQUIRED(new_folder_, new_folder);
2672 imap_gen_send(session, "RENAME %s %s", old_folder_, new_folder_);
2674 return imap_cmd_ok(session, NULL);
2677 static gint imap_cmd_delete(IMAPSession *session, const gchar *folder)
2681 QUOTE_IF_REQUIRED(folder_, folder);
2682 imap_gen_send(session, "DELETE %s", folder_);
2684 return imap_cmd_ok(session, NULL);
2687 static gint imap_cmd_search(IMAPSession *session, const gchar *criteria, GSList **list)
2693 g_return_val_if_fail(criteria != NULL, IMAP_ERROR);
2694 g_return_val_if_fail(list != NULL, IMAP_ERROR);
2698 argbuf = g_ptr_array_new();
2699 imap_gen_send(session, "UID SEARCH %s", criteria);
2701 ok = imap_cmd_ok(session, argbuf);
2702 if (ok != IMAP_SUCCESS) {
2703 ptr_array_free_strings(argbuf);
2704 g_ptr_array_free(argbuf, TRUE);
2708 if ((uidlist = search_array_str(argbuf, "SEARCH ")) != NULL) {
2709 gchar **strlist, **p;
2711 strlist = g_strsplit(uidlist + 7, " ", 0);
2712 for (p = strlist; *p != NULL; ++p) {
2715 if (sscanf(*p, "%d", &msgnum) == 1)
2716 *list = g_slist_append(*list, GINT_TO_POINTER(msgnum));
2718 g_strfreev(strlist);
2720 ptr_array_free_strings(argbuf);
2721 g_ptr_array_free(argbuf, TRUE);
2723 return IMAP_SUCCESS;
2726 static gint imap_cmd_fetch(IMAPSession *session, guint32 uid, const gchar *filename)
2734 g_return_val_if_fail(filename != NULL, IMAP_ERROR);
2736 imap_gen_send(session, "UID FETCH %d BODY.PEEK[]", uid);
2738 while ((ok = imap_gen_recv(session, &buf)) == IMAP_SUCCESS) {
2739 if (buf[0] != '*' || buf[1] != ' ') {
2743 if (strstr(buf, "FETCH") != NULL) break;
2746 if (ok != IMAP_SUCCESS) {
2751 #define RETURN_ERROR_IF_FAIL(cond) \
2754 return IMAP_ERROR; \
2757 cur_pos = strchr(buf, '{');
2758 RETURN_ERROR_IF_FAIL(cur_pos != NULL);
2759 cur_pos = strchr_cpy(cur_pos + 1, '}', size_str, sizeof(size_str));
2760 RETURN_ERROR_IF_FAIL(cur_pos != NULL);
2761 size_num = atol(size_str);
2762 RETURN_ERROR_IF_FAIL(size_num > 0);
2764 RETURN_ERROR_IF_FAIL(*cur_pos == '\0');
2766 #undef RETURN_ERROR_IF_FAIL
2770 if (recv_bytes_write_to_file(SESSION(session)->sock, size_num, filename) != 0)
2773 if (imap_gen_recv(session, &buf) != IMAP_SUCCESS) {
2778 if (buf[0] == '\0' || buf[strlen(buf) - 1] != ')') {
2784 ok = imap_cmd_ok(session, NULL);
2789 static gint imap_cmd_append(IMAPSession *session, const gchar *destfolder,
2790 const gchar *file, IMAPFlags flags, guint32 *new_uid)
2798 gchar buf[BUFFSIZE];
2803 g_return_val_if_fail(file != NULL, IMAP_ERROR);
2805 size = get_file_size_as_crlf(file);
2806 if ((fp = fopen(file, "rb")) == NULL) {
2807 FILE_OP_ERROR(file, "fopen");
2810 QUOTE_IF_REQUIRED(destfolder_, destfolder);
2811 flag_str = imap_get_flag_str(flags);
2812 imap_gen_send(session, "APPEND %s (%s) {%d}",
2813 destfolder_, flag_str, size);
2816 ok = imap_gen_recv(session, &ret);
2817 if (ok != IMAP_SUCCESS || ret[0] != '+' || ret[1] != ' ') {
2818 log_warning(_("can't append %s to %s\n"), file, destfolder_);
2825 log_print("IMAP4> %s\n", _("(sending file...)"));
2827 while (fgets(buf, sizeof(buf), fp) != NULL) {
2829 if (sock_puts(SESSION(session)->sock, buf) < 0) {
2836 FILE_OP_ERROR(file, "fgets");
2841 sock_puts(SESSION(session)->sock, "");
2845 if (new_uid != NULL)
2848 if (new_uid != NULL && session->uidplus) {
2849 argbuf = g_ptr_array_new();
2851 ok = imap_cmd_ok(session, argbuf);
2852 if (ok != IMAP_SUCCESS)
2853 log_warning(_("can't append message to %s\n"),
2855 else if (argbuf->len > 0) {
2856 resp_str = g_ptr_array_index(argbuf, argbuf->len - 1);
2858 sscanf(resp_str, "%*u OK [APPENDUID %*u %u]",
2860 *new_uid = new_uid_;
2864 ptr_array_free_strings(argbuf);
2865 g_ptr_array_free(argbuf, TRUE);
2867 ok = imap_cmd_ok(session, NULL);
2872 static gint imap_cmd_copy(IMAPSession *session, const gchar *seq_set,
2873 const gchar *destfolder, GRelation *uid_mapping)
2879 g_return_val_if_fail(session != NULL, IMAP_ERROR);
2880 g_return_val_if_fail(seq_set != NULL, IMAP_ERROR);
2881 g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
2883 QUOTE_IF_REQUIRED(destfolder_, destfolder);
2884 imap_gen_send(session, "UID COPY %s %s", seq_set, destfolder_);
2886 reply = g_ptr_array_new();
2888 ok = imap_cmd_ok(session, reply);
2889 if (ok != IMAP_SUCCESS)
2890 log_warning(_("can't copy %s to %s\n"), seq_set, destfolder_);
2894 - split IMAPSets into uids
2895 - g_relation_insert(uid_mapping, olduid, newuid);
2897 else if (imap_has_capability(session, "UIDPLUS") && reply->len > 0)
2898 if ((okmsginfo = g_ptr_array_index(reply, reply->len - 1)) != NULL &&
2899 sscanf(okmsginfo, "%*u OK [COPYUID %*u %u %u]", &olduid, &newuid) == 2 &&
2903 ptr_array_free_strings(reply);
2904 g_ptr_array_free(reply, TRUE);
2908 gint imap_cmd_envelope(IMAPSession *session, IMAPSet set)
2910 static GString *header_fields = NULL;
2912 if (header_fields == NULL) {
2913 const HeaderEntry *headers, *elem;
2915 headers = procheader_get_headernames(FALSE);
2916 header_fields = g_string_new("");
2918 for (elem = headers; elem->name != NULL; ++elem) {
2919 gint namelen = strlen(elem->name);
2921 /* Header fields ending with space are not rfc822 headers */
2922 if (elem->name[namelen - 1] == ' ')
2925 /* strip : at the of header field */
2926 if(elem->name[namelen - 1] == ':')
2932 g_string_sprintfa(header_fields, "%s%.*s",
2933 header_fields->str[0] != '\0' ? " " : "",
2934 namelen, elem->name);
2939 (session, "UID FETCH %s (UID FLAGS RFC822.SIZE BODY.PEEK[HEADER.FIELDS (%s)])",
2940 set, header_fields->str);
2942 return IMAP_SUCCESS;
2945 static gint imap_cmd_store(IMAPSession *session, IMAPSet seq_set,
2950 imap_gen_send(session, "UID STORE %s %s", seq_set, sub_cmd);
2952 if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS) {
2953 log_warning(_("error while imap command: STORE %s %s\n"),
2958 return IMAP_SUCCESS;
2961 static gint imap_cmd_expunge(IMAPSession *session)
2965 imap_gen_send(session, "EXPUNGE");
2966 if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS) {
2967 log_warning(_("error while imap command: EXPUNGE\n"));
2971 return IMAP_SUCCESS;
2974 static gint imap_cmd_close(IMAPSession *session)
2978 imap_gen_send(session, "CLOSE");
2979 if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS)
2980 log_warning(_("error while imap command: CLOSE\n"));
2985 static gint imap_cmd_ok(IMAPSession *session, GPtrArray *argbuf)
2987 gint ok = IMAP_SUCCESS;
2992 while ((ok = imap_gen_recv(session, &buf))
2994 // make sure data is long enough for any substring of buf
2995 data = alloca(strlen(buf) + 1);
2997 // untagged line read
2998 if (buf[0] == '*' && buf[1] == ' ') {
3001 g_ptr_array_add(argbuf, g_strdup(buf + 2));
3003 if (sscanf(buf + 2, "%d %s", &num, data) >= 2) {
3004 if (!strcmp(data, "EXISTS")) {
3005 session->exists = num;
3006 session->folder_content_changed = TRUE;
3009 if(!strcmp(data, "EXPUNGE")) {
3011 session->folder_content_changed = TRUE;
3014 // tagged line with correct tag and OK response found
3015 } else if ((sscanf(buf, "%d %s", &cmd_num, data) >= 2) &&
3016 (cmd_num == session->cmd_count) &&
3017 !strcmp(data, "OK")) {
3019 g_ptr_array_add(argbuf, g_strdup(buf));
3033 static void imap_gen_send(IMAPSession *session, const gchar *format, ...)
3040 va_start(args, format);
3041 tmp = g_strdup_vprintf(format, args);
3044 session->cmd_count++;
3046 buf = g_strdup_printf("%d %s\r\n", session->cmd_count, tmp);
3047 if (!strncasecmp(tmp, "LOGIN ", 6) && (p = strchr(tmp + 6, ' '))) {
3049 log_print("IMAP4> %d %s ********\n", session->cmd_count, tmp);
3051 log_print("IMAP4> %d %s\n", session->cmd_count, tmp);
3053 sock_write_all(SESSION(session)->sock, buf, strlen(buf));
3058 static gint imap_gen_recv(IMAPSession *session, gchar **ret)
3060 if ((*ret = sock_getline(SESSION(session)->sock)) == NULL)
3065 log_print("IMAP4< %s\n", *ret);
3067 return IMAP_SUCCESS;
3071 /* misc utility functions */
3073 static gchar *strchr_cpy(const gchar *src, gchar ch, gchar *dest, gint len)
3078 tmp = strchr(src, ch);
3082 memcpy(dest, src, MIN(tmp - src, len - 1));
3083 dest[MIN(tmp - src, len - 1)] = '\0';
3088 static gchar *get_quoted(const gchar *src, gchar ch, gchar *dest, gint len)
3090 const gchar *p = src;
3093 g_return_val_if_fail(*p == ch, NULL);
3098 while (*p != '\0' && *p != ch) {
3100 if (*p == '\\' && *(p + 1) != '\0')
3109 return (gchar *)(*p == ch ? p + 1 : p);
3112 static gchar *search_array_contain_str(GPtrArray *array, const gchar *str)
3116 for (i = 0; i < array->len; i++) {
3119 tmp = g_ptr_array_index(array, i);
3120 if (strstr(tmp, str) != NULL)
3127 static gchar *search_array_str(GPtrArray *array, const gchar *str)
3134 for (i = 0; i < array->len; i++) {
3137 tmp = g_ptr_array_index(array, i);
3138 if (!strncmp(tmp, str, len))
3145 static void imap_path_separator_subst(gchar *str, gchar separator)
3148 gboolean in_escape = FALSE;
3150 if (!separator || separator == '/') return;
3152 for (p = str; *p != '\0'; p++) {
3153 if (*p == '/' && !in_escape)
3155 else if (*p == '&' && *(p + 1) != '-' && !in_escape)
3157 else if (*p == '-' && in_escape)
3162 static gchar *imap_modified_utf7_to_locale(const gchar *mutf7_str)
3165 const gchar *from_p;
3168 to = g_malloc(strlen(mutf7_str) + 1);
3171 for (from_p = mutf7_str; *from_p != '\0'; from_p++) {
3172 if (*from_p == '&' && *(from_p + 1) == '-') {
3182 static iconv_t cd = (iconv_t)-1;
3183 static gboolean iconv_ok = TRUE;
3186 size_t norm_utf7_len;
3188 gchar *to_str, *to_p;
3190 gboolean in_escape = FALSE;
3192 if (!iconv_ok) return g_strdup(mutf7_str);
3194 if (cd == (iconv_t)-1) {
3195 cd = iconv_open(conv_get_current_charset_str(), "UTF-7");
3196 if (cd == (iconv_t)-1) {
3197 g_warning("iconv cannot convert UTF-7 to %s\n",
3198 conv_get_current_charset_str());
3200 return g_strdup(mutf7_str);
3204 norm_utf7 = g_string_new(NULL);
3206 for (p = mutf7_str; *p != '\0'; p++) {
3207 /* replace: '&' -> '+',
3209 escaped ',' -> '/' */
3210 if (!in_escape && *p == '&') {
3211 if (*(p + 1) != '-') {
3212 g_string_append_c(norm_utf7, '+');
3215 g_string_append_c(norm_utf7, '&');
3218 } else if (in_escape && *p == ',') {
3219 g_string_append_c(norm_utf7, '/');
3220 } else if (in_escape && *p == '-') {
3221 g_string_append_c(norm_utf7, '-');
3224 g_string_append_c(norm_utf7, *p);
3228 norm_utf7_p = norm_utf7->str;
3229 norm_utf7_len = norm_utf7->len;
3230 to_len = strlen(mutf7_str) * 5;
3231 to_p = to_str = g_malloc(to_len + 1);
3233 if (iconv(cd, (ICONV_CONST gchar **)&norm_utf7_p, &norm_utf7_len,
3234 &to_p, &to_len) == -1) {
3235 g_warning(_("iconv cannot convert UTF-7 to %s\n"),
3236 conv_get_current_charset_str());
3237 g_string_free(norm_utf7, TRUE);
3239 return g_strdup(mutf7_str);
3242 /* second iconv() call for flushing */
3243 iconv(cd, NULL, NULL, &to_p, &to_len);
3244 g_string_free(norm_utf7, TRUE);
3248 #endif /* !HAVE_ICONV */
3251 static gchar *imap_locale_to_modified_utf7(const gchar *from)
3254 const gchar *from_p;
3257 to = g_malloc(strlen(from) * 2 + 1);
3260 for (from_p = from; *from_p != '\0'; from_p++) {
3261 if (*from_p == '&') {
3271 static iconv_t cd = (iconv_t)-1;
3272 static gboolean iconv_ok = TRUE;
3273 gchar *norm_utf7, *norm_utf7_p;
3274 size_t from_len, norm_utf7_len;
3276 gchar *from_tmp, *to, *p;
3277 gboolean in_escape = FALSE;
3279 if (!iconv_ok) return g_strdup(from);
3281 if (cd == (iconv_t)-1) {
3282 cd = iconv_open("UTF-7", conv_get_current_charset_str());
3283 if (cd == (iconv_t)-1) {
3284 g_warning("iconv cannot convert %s to UTF-7\n",
3285 conv_get_current_charset_str());
3287 return g_strdup(from);
3291 Xstrdup_a(from_tmp, from, return g_strdup(from));
3292 from_len = strlen(from);
3293 norm_utf7_len = from_len * 5;
3294 Xalloca(norm_utf7, norm_utf7_len + 1, return g_strdup(from));
3295 norm_utf7_p = norm_utf7;
3297 #define IS_PRINT(ch) (isprint(ch) && isascii(ch))
3299 while (from_len > 0) {
3300 if (*from_tmp == '+') {
3301 *norm_utf7_p++ = '+';
3302 *norm_utf7_p++ = '-';
3306 } else if (IS_PRINT(*from_tmp)) {
3307 /* printable ascii char */
3308 *norm_utf7_p = *from_tmp;
3314 size_t mb_len = 0, conv_len = 0;
3316 /* unprintable char: convert to UTF-7 */
3318 while (!IS_PRINT(*p) && conv_len < from_len) {
3319 mb_len = mblen(p, MB_LEN_MAX);
3321 g_warning("wrong multibyte sequence\n");
3322 return g_strdup(from);
3328 from_len -= conv_len;
3329 if (iconv(cd, (ICONV_CONST gchar **)&from_tmp,
3331 &norm_utf7_p, &norm_utf7_len) == -1) {
3332 g_warning("iconv cannot convert %s to UTF-7\n",
3333 conv_get_current_charset_str());
3334 return g_strdup(from);
3337 /* second iconv() call for flushing */
3338 iconv(cd, NULL, NULL, &norm_utf7_p, &norm_utf7_len);
3344 *norm_utf7_p = '\0';
3345 to_str = g_string_new(NULL);
3346 for (p = norm_utf7; p < norm_utf7_p; p++) {
3347 /* replace: '&' -> "&-",
3350 BASE64 '/' -> ',' */
3351 if (!in_escape && *p == '&') {
3352 g_string_append(to_str, "&-");
3353 } else if (!in_escape && *p == '+') {
3354 if (*(p + 1) == '-') {
3355 g_string_append_c(to_str, '+');
3358 g_string_append_c(to_str, '&');
3361 } else if (in_escape && *p == '/') {
3362 g_string_append_c(to_str, ',');
3363 } else if (in_escape && *p == '-') {
3364 g_string_append_c(to_str, '-');
3367 g_string_append_c(to_str, *p);
3373 g_string_append_c(to_str, '-');
3377 g_string_free(to_str, FALSE);
3380 #endif /* !HAVE_ICONV */
3383 static GSList *imap_get_seq_set_from_numlist(MsgNumberList *numlist)
3386 GSList *sorted_list, *cur;
3387 guint first, last, next;
3389 GSList *ret_list = NULL;
3391 if (numlist == NULL)
3394 str = g_string_sized_new(256);
3396 sorted_list = g_slist_copy(numlist);
3397 sorted_list = g_slist_sort(sorted_list, g_int_compare);
3399 first = GPOINTER_TO_INT(sorted_list->data);
3401 for (cur = sorted_list; cur != NULL; cur = g_slist_next(cur)) {
3402 last = GPOINTER_TO_INT(cur->data);
3404 next = GPOINTER_TO_INT(cur->next->data);
3408 if (last + 1 != next || next == 0) {
3410 g_string_append_c(str, ',');
3412 g_string_sprintfa(str, "%u", first);
3414 g_string_sprintfa(str, "%u:%u", first, last);
3418 if (str->len > IMAP_CMD_LIMIT) {
3419 ret_str = g_strdup(str->str);
3420 ret_list = g_slist_append(ret_list, ret_str);
3421 g_string_truncate(str, 0);
3427 ret_str = g_strdup(str->str);
3428 ret_list = g_slist_append(ret_list, ret_str);
3431 g_slist_free(sorted_list);
3432 g_string_free(str, TRUE);
3437 static GSList *imap_get_seq_set_from_msglist(MsgInfoList *msglist)
3439 MsgNumberList *numlist = NULL;
3443 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
3444 MsgInfo *msginfo = (MsgInfo *) cur->data;
3446 numlist = g_slist_append(numlist, GINT_TO_POINTER(msginfo->msgnum));
3448 seq_list = imap_get_seq_set_from_numlist(numlist);
3449 g_slist_free(numlist);
3454 static void imap_seq_set_free(GSList *seq_list)
3456 slist_free_strings(seq_list);
3457 g_slist_free(seq_list);
3461 static gboolean imap_rename_folder_func(GNode *node, gpointer data)
3463 FolderItem *item = node->data;
3464 gchar **paths = data;
3465 const gchar *oldpath = paths[0];
3466 const gchar *newpath = paths[1];
3468 gchar *new_itempath;
3471 oldpathlen = strlen(oldpath);
3472 if (strncmp(oldpath, item->path, oldpathlen) != 0) {
3473 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
3477 base = item->path + oldpathlen;
3478 while (*base == G_DIR_SEPARATOR) base++;
3480 new_itempath = g_strdup(newpath);
3482 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
3485 item->path = new_itempath;
3490 static gint get_list_of_uids(Folder *folder, IMAPFolderItem *item, GSList **msgnum_list)
3492 gint ok, nummsgs = 0, lastuid_old;
3493 IMAPSession *session;
3494 GSList *uidlist, *elem;
3497 session = imap_session_get(folder);
3498 g_return_val_if_fail(session != NULL, -1);
3500 ok = imap_select(session, IMAP_FOLDER(folder), item->item.path,
3501 NULL, NULL, NULL, NULL);
3502 if (ok != IMAP_SUCCESS)
3505 cmd_buf = g_strdup_printf("UID %d:*", item->lastuid + 1);
3506 ok = imap_cmd_search(session, cmd_buf, &uidlist);
3509 if (ok == IMAP_SOCKET) {
3510 session_destroy((Session *)session);
3511 ((RemoteFolder *)folder)->session = NULL;
3515 if (ok != IMAP_SUCCESS) {
3519 argbuf = g_ptr_array_new();
3521 cmd_buf = g_strdup_printf("UID FETCH %d:* (UID)", item->lastuid + 1);
3522 imap_gen_send(session, cmd_buf);
3524 ok = imap_cmd_ok(session, argbuf);
3525 if (ok != IMAP_SUCCESS) {
3526 ptr_array_free_strings(argbuf);
3527 g_ptr_array_free(argbuf, TRUE);
3531 for(i = 0; i < argbuf->len; i++) {
3534 if((ret = sscanf(g_ptr_array_index(argbuf, i),
3535 "%*d FETCH (UID %d)", &msgnum)) == 1)
3536 uidlist = g_slist_prepend(uidlist, GINT_TO_POINTER(msgnum));
3538 ptr_array_free_strings(argbuf);
3539 g_ptr_array_free(argbuf, TRUE);
3542 lastuid_old = item->lastuid;
3543 *msgnum_list = g_slist_copy(item->uid_list);
3544 nummsgs = g_slist_length(*msgnum_list);
3545 debug_print("Got %d uids from cache\n", g_slist_length(item->uid_list));
3547 for (elem = uidlist; elem != NULL; elem = g_slist_next(elem)) {
3550 msgnum = GPOINTER_TO_INT(elem->data);
3551 if (msgnum > lastuid_old) {
3552 *msgnum_list = g_slist_prepend(*msgnum_list, GINT_TO_POINTER(msgnum));
3553 item->uid_list = g_slist_prepend(item->uid_list, GINT_TO_POINTER(msgnum));
3556 if(msgnum > item->lastuid)
3557 item->lastuid = msgnum;
3560 g_slist_free(uidlist);
3565 gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list)
3567 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3568 IMAPSession *session;
3569 gint ok, nummsgs = 0, exists, recent, unseen, uid_val, uid_next;
3572 gboolean selected_folder;
3574 g_return_val_if_fail(folder != NULL, -1);
3575 g_return_val_if_fail(item != NULL, -1);
3576 g_return_val_if_fail(item->item.path != NULL, -1);
3577 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3578 g_return_val_if_fail(folder->account != NULL, -1);
3580 session = imap_session_get(folder);
3581 g_return_val_if_fail(session != NULL, -1);
3583 selected_folder = (session->mbox != NULL) &&
3584 (!strcmp(session->mbox, item->item.path));
3585 if (selected_folder) {
3586 ok = imap_cmd_noop(session);
3587 if (ok != IMAP_SUCCESS)
3589 exists = session->exists;
3591 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
3592 &exists, &recent, &uid_next, &uid_val, &unseen);
3593 if (ok != IMAP_SUCCESS)
3597 /* If old uid_next matches new uid_next we can be sure no message
3598 was added to the folder */
3599 if (( selected_folder && !session->folder_content_changed) ||
3600 (!selected_folder && uid_next == item->uid_next)) {
3601 nummsgs = g_slist_length(item->uid_list);
3603 /* If number of messages is still the same we
3604 know our caches message numbers are still valid,
3605 otherwise if the number of messages has decrease
3606 we discard our cache to start a new scan to find
3607 out which numbers have been removed */
3608 if (exists == nummsgs) {
3609 *msgnum_list = g_slist_copy(item->uid_list);
3611 } else if (exists < nummsgs) {
3612 debug_print("Freeing imap uid cache");
3614 g_slist_free(item->uid_list);
3615 item->uid_list = NULL;
3618 if (!selected_folder)
3619 item->uid_next = uid_next;
3622 *msgnum_list = NULL;
3626 nummsgs = get_list_of_uids(folder, item, &uidlist);
3628 if (nummsgs != exists) {
3629 /* Cache contains more messages then folder, we have cached
3630 an old UID of a message that was removed and new messages
3631 have been added too, otherwise the uid_next check would
3633 debug_print("Freeing imap uid cache");
3635 g_slist_free(item->uid_list);
3636 item->uid_list = NULL;
3638 g_slist_free(*msgnum_list);
3640 nummsgs = get_list_of_uids(folder, item, &uidlist);
3643 *msgnum_list = uidlist;
3645 dir = folder_item_get_path((FolderItem *)item);
3646 debug_print("removing old messages from %s\n", dir);
3647 remove_numbered_files_not_in_list(dir, *msgnum_list);
3653 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item)
3658 flags.perm_flags = MSG_NEW|MSG_UNREAD;
3659 flags.tmp_flags = 0;
3661 g_return_val_if_fail(item != NULL, NULL);
3662 g_return_val_if_fail(file != NULL, NULL);
3664 if (item->stype == F_QUEUE) {
3665 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
3666 } else if (item->stype == F_DRAFT) {
3667 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
3670 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3671 if (!msginfo) return NULL;
3673 msginfo->folder = item;
3678 GSList *imap_get_msginfos(Folder *folder, FolderItem *item, GSList *msgnum_list)
3680 IMAPSession *session;
3681 MsgInfoList *ret = NULL;
3684 g_return_val_if_fail(folder != NULL, NULL);
3685 g_return_val_if_fail(item != NULL, NULL);
3686 g_return_val_if_fail(msgnum_list != NULL, NULL);
3688 session = imap_session_get(folder);
3689 g_return_val_if_fail(session != NULL, NULL);
3691 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3692 NULL, NULL, NULL, NULL);
3693 if (ok != IMAP_SUCCESS)
3696 if (!(item->stype == F_QUEUE || item->stype == F_DRAFT)) {
3697 ret = g_slist_concat(ret,
3698 imap_get_uncached_messages(
3699 session, item, msgnum_list));
3701 MsgNumberList *sorted_list, *elem;
3702 gint startnum, lastnum;
3704 sorted_list = g_slist_sort(g_slist_copy(msgnum_list), g_int_compare);
3706 startnum = lastnum = GPOINTER_TO_INT(sorted_list->data);
3708 for (elem = sorted_list;; elem = g_slist_next(elem)) {
3712 num = GPOINTER_TO_INT(elem->data);
3714 if (num > lastnum + 1 || elem == NULL) {
3716 for (i = startnum; i <= lastnum; ++i) {
3719 file = imap_fetch_msg(folder, item, i);
3721 MsgInfo *msginfo = imap_parse_msg(file, item);
3722 if (msginfo != NULL) {
3723 msginfo->msgnum = i;
3724 ret = g_slist_append(ret, msginfo);
3738 g_slist_free(sorted_list);
3744 MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid)
3746 MsgInfo *msginfo = NULL;
3747 MsgInfoList *msginfolist;
3748 MsgNumberList numlist;
3750 numlist.next = NULL;
3751 numlist.data = GINT_TO_POINTER(uid);
3753 msginfolist = imap_get_msginfos(folder, item, &numlist);
3754 if (msginfolist != NULL) {
3755 msginfo = msginfolist->data;
3756 g_slist_free(msginfolist);
3762 gboolean imap_check_msgnum_validity(Folder *folder, FolderItem *_item)
3764 IMAPSession *session;
3765 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3766 gint ok, exists = 0, recent = 0, unseen = 0;
3767 guint32 uid_next, uid_validity = 0;
3769 g_return_val_if_fail(folder != NULL, FALSE);
3770 g_return_val_if_fail(item != NULL, FALSE);
3771 g_return_val_if_fail(item->item.folder != NULL, FALSE);
3772 g_return_val_if_fail(FOLDER_CLASS(item->item.folder) == &imap_class, FALSE);
3774 session = imap_session_get(folder);
3775 g_return_val_if_fail(session != NULL, FALSE);
3777 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
3778 &exists, &recent, &uid_next, &uid_validity, &unseen);
3779 if (ok != IMAP_SUCCESS)
3782 if(item->item.mtime == uid_validity)
3785 debug_print("Freeing imap uid cache\n");
3787 g_slist_free(item->uid_list);
3788 item->uid_list = NULL;
3790 item->item.mtime = uid_validity;
3792 imap_delete_all_cached_messages((FolderItem *)item);
3797 void imap_change_flags(Folder *folder, FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
3799 IMAPSession *session;
3800 IMAPFlags flags_set = 0, flags_unset = 0;
3801 gint ok = IMAP_SUCCESS;
3802 MsgNumberList numlist;
3804 g_return_if_fail(folder != NULL);
3805 g_return_if_fail(folder->klass == &imap_class);
3806 g_return_if_fail(item != NULL);
3807 g_return_if_fail(item->folder == folder);
3808 g_return_if_fail(msginfo != NULL);
3809 g_return_if_fail(msginfo->folder == item);
3811 session = imap_session_get(folder);
3812 if (!session) return;
3814 if ((ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
3815 NULL, NULL, NULL, NULL)) != IMAP_SUCCESS)
3818 if (!MSG_IS_MARKED(msginfo->flags) && (newflags & MSG_MARKED))
3819 flags_set |= IMAP_FLAG_FLAGGED;
3820 if ( MSG_IS_MARKED(msginfo->flags) && !(newflags & MSG_MARKED))
3821 flags_unset |= IMAP_FLAG_FLAGGED;
3823 if (!MSG_IS_UNREAD(msginfo->flags) && (newflags & MSG_UNREAD))
3824 flags_unset |= IMAP_FLAG_SEEN;
3825 if ( MSG_IS_UNREAD(msginfo->flags) && !(newflags & MSG_UNREAD))
3826 flags_set |= IMAP_FLAG_SEEN;
3828 if (!MSG_IS_REPLIED(msginfo->flags) && (newflags & MSG_REPLIED))
3829 flags_set |= IMAP_FLAG_ANSWERED;
3830 if ( MSG_IS_REPLIED(msginfo->flags) && !(newflags & MSG_REPLIED))
3831 flags_set |= IMAP_FLAG_ANSWERED;
3833 numlist.next = NULL;
3834 numlist.data = GINT_TO_POINTER(msginfo->msgnum);
3837 ok = imap_set_message_flags(session, &numlist, flags_set, TRUE);
3838 if (ok != IMAP_SUCCESS) return;
3842 ok = imap_set_message_flags(session, &numlist, flags_unset, FALSE);
3843 if (ok != IMAP_SUCCESS) return;
3846 msginfo->flags.perm_flags = newflags;