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)
928 GSList *seq_list, *cur;
930 IMAPSession *session;
931 gint ok = IMAP_SUCCESS;
932 GRelation *uid_mapping;
935 g_return_val_if_fail(folder != NULL, -1);
936 g_return_val_if_fail(dest != NULL, -1);
937 g_return_val_if_fail(msglist != NULL, -1);
939 session = imap_session_get(folder);
940 if (!session) return -1;
942 msginfo = (MsgInfo *)msglist->data;
944 src = msginfo->folder;
946 g_warning("the src folder is identical to the dest.\n");
950 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
951 NULL, NULL, NULL, NULL);
952 if (ok != IMAP_SUCCESS)
955 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
956 seq_list = imap_get_seq_set_from_msglist(msglist);
957 uid_mapping = g_relation_new(2);
958 g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
960 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
961 gchar *seq_set = (gchar *)cur->data;
963 debug_print("Copying message %s%c[%s] to %s ...\n",
964 src->path, G_DIR_SEPARATOR,
967 ok = imap_cmd_copy(session, seq_set, destdir, uid_mapping);
968 if (ok != IMAP_SUCCESS) {
969 g_relation_destroy(uid_mapping);
970 imap_seq_set_free(seq_list);
975 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
976 MsgInfo *msginfo = (MsgInfo *)cur->data;
979 tuples = g_relation_select(uid_mapping,
980 GINT_TO_POINTER(msginfo->msgnum),
982 if (tuples->len > 0) {
983 gint num = GPOINTER_TO_INT(g_tuples_index(tuples, 0, 1));
984 g_relation_insert(relation, msginfo,
985 GPOINTER_TO_INT(num));
989 g_relation_insert(relation, msginfo,
991 g_tuples_destroy(tuples);
994 imap_seq_set_free(seq_list);
998 if (ok == IMAP_SUCCESS)
1004 gint imap_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1008 g_return_val_if_fail(msginfo != NULL, -1);
1010 msglist.data = msginfo;
1011 msglist.next = NULL;
1013 return imap_copy_msgs(folder, dest, &msglist, NULL);
1016 gint imap_copy_msgs(Folder *folder, FolderItem *dest,
1017 MsgInfoList *msglist, GRelation *relation)
1023 g_return_val_if_fail(folder != NULL, -1);
1024 g_return_val_if_fail(dest != NULL, -1);
1025 g_return_val_if_fail(msglist != NULL, -1);
1027 msginfo = (MsgInfo *)msglist->data;
1028 g_return_val_if_fail(msginfo->folder != NULL, -1);
1030 if (folder == msginfo->folder->folder)
1031 return imap_do_copy_msgs(folder, dest, msglist, relation);
1033 file_list = procmsg_get_message_file_list(msglist);
1034 g_return_val_if_fail(file_list != NULL, -1);
1036 ret = imap_add_msgs(folder, dest, file_list, relation);
1038 procmsg_message_file_list_free(file_list);
1043 gint imap_remove_msg(Folder *folder, FolderItem *item, gint uid)
1046 IMAPSession *session;
1048 MsgNumberList numlist;
1050 g_return_val_if_fail(folder != NULL, -1);
1051 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
1052 g_return_val_if_fail(item != NULL, -1);
1054 session = imap_session_get(folder);
1055 if (!session) return -1;
1057 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
1058 NULL, NULL, NULL, NULL);
1059 if (ok != IMAP_SUCCESS)
1062 numlist.next = NULL;
1063 numlist.data = GINT_TO_POINTER(uid);
1065 ok = imap_set_message_flags
1066 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
1067 &numlist, IMAP_FLAG_DELETED, TRUE);
1068 if (ok != IMAP_SUCCESS) {
1069 log_warning(_("can't set deleted flags: %d\n"), uid);
1073 ok = imap_cmd_expunge(session);
1074 if (ok != IMAP_SUCCESS) {
1075 log_warning(_("can't expunge\n"));
1079 dir = folder_item_get_path(item);
1080 if (is_dir_exist(dir))
1081 remove_numbered_files(dir, uid, uid);
1084 return IMAP_SUCCESS;
1087 gint imap_remove_all_msg(Folder *folder, FolderItem *item)
1090 IMAPSession *session;
1093 g_return_val_if_fail(folder != NULL, -1);
1094 g_return_val_if_fail(item != NULL, -1);
1096 session = imap_session_get(folder);
1097 if (!session) return -1;
1099 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
1100 NULL, NULL, NULL, NULL);
1101 if (ok != IMAP_SUCCESS)
1104 imap_gen_send(session, "STORE 1:* +FLAGS.SILENT (\\Deleted)");
1105 ok = imap_cmd_ok(session, NULL);
1106 if (ok != IMAP_SUCCESS) {
1107 log_warning(_("can't set deleted flags: 1:*\n"));
1111 ok = imap_cmd_expunge(session);
1112 if (ok != IMAP_SUCCESS) {
1113 log_warning(_("can't expunge\n"));
1117 dir = folder_item_get_path(item);
1118 if (is_dir_exist(dir))
1119 remove_all_numbered_files(dir);
1122 return IMAP_SUCCESS;
1125 gboolean imap_is_msg_changed(Folder *folder, FolderItem *item, MsgInfo *msginfo)
1127 /* TODO: properly implement this method */
1131 gint imap_close(Folder *folder, FolderItem *item)
1134 IMAPSession *session;
1136 g_return_val_if_fail(folder != NULL, -1);
1138 session = imap_session_get(folder);
1139 if (!session) return -1;
1141 if (session->mbox) {
1142 if (strcmp(item->path, session->mbox))
1145 ok = imap_cmd_close(session);
1146 if (ok != IMAP_SUCCESS)
1147 log_warning(_("can't close folder\n"));
1149 g_free(session->mbox);
1150 session->mbox = NULL;
1158 void imap_scan_tree(Folder *folder)
1161 IMAPSession *session;
1162 gchar *root_folder = NULL;
1164 g_return_if_fail(folder != NULL);
1165 g_return_if_fail(folder->account != NULL);
1167 session = imap_session_get(folder);
1169 if (!folder->node) {
1170 folder_tree_destroy(folder);
1171 item = folder_item_new(folder, folder->name, NULL);
1172 item->folder = folder;
1173 folder->node = g_node_new(item);
1178 if (folder->account->imap_dir && *folder->account->imap_dir) {
1179 Xstrdup_a(root_folder, folder->account->imap_dir, return);
1180 strtailchomp(root_folder, '/');
1181 debug_print("IMAP root directory: %s\n", root_folder);
1184 item = folder_item_new(folder, folder->name, root_folder);
1185 item->folder = folder;
1186 item->no_select = TRUE;
1187 folder->node = g_node_new(item);
1189 imap_scan_tree_recursive(session, item);
1191 imap_create_missing_folders(folder);
1194 static gint imap_scan_tree_recursive(IMAPSession *session, FolderItem *item)
1197 IMAPFolder *imapfolder;
1198 FolderItem *new_item;
1199 GSList *item_list, *cur;
1201 gchar *wildcard_path;
1205 g_return_val_if_fail(item != NULL, -1);
1206 g_return_val_if_fail(item->folder != NULL, -1);
1207 g_return_val_if_fail(item->no_sub == FALSE, -1);
1209 folder = FOLDER(item->folder);
1210 imapfolder = IMAP_FOLDER(folder);
1212 separator = imap_get_path_separator(imapfolder, item->path);
1214 if (item->folder->ui_func)
1215 item->folder->ui_func(folder, item, folder->ui_func_data);
1218 wildcard[0] = separator;
1221 real_path = imap_get_real_path(imapfolder, item->path);
1225 real_path = g_strdup("");
1228 Xstrcat_a(wildcard_path, real_path, wildcard,
1229 {g_free(real_path); return IMAP_ERROR;});
1230 QUOTE_IF_REQUIRED(wildcard_path, wildcard_path);
1232 imap_gen_send(session, "LIST \"\" %s",
1235 strtailchomp(real_path, separator);
1236 item_list = imap_parse_list(imapfolder, session, real_path, NULL);
1239 for (cur = item_list; cur != NULL; cur = cur->next) {
1240 new_item = cur->data;
1241 if (!strcmp(new_item->path, "INBOX")) {
1242 if (!folder->inbox) {
1243 new_item->stype = F_INBOX;
1244 item->folder->inbox = new_item;
1246 folder_item_destroy(new_item);
1249 } else if (!item->parent || item->stype == F_INBOX) {
1252 base = g_basename(new_item->path);
1254 if (!folder->outbox && !strcasecmp(base, "Sent")) {
1255 new_item->stype = F_OUTBOX;
1256 folder->outbox = new_item;
1257 } else if (!folder->draft && !strcasecmp(base, "Drafts")) {
1258 new_item->stype = F_DRAFT;
1259 folder->draft = new_item;
1260 } else if (!folder->queue && !strcasecmp(base, "Queue")) {
1261 new_item->stype = F_QUEUE;
1262 folder->queue = new_item;
1263 } else if (!folder->trash && !strcasecmp(base, "Trash")) {
1264 new_item->stype = F_TRASH;
1265 folder->trash = new_item;
1268 folder_item_append(item, new_item);
1269 if (new_item->no_sub == FALSE)
1270 imap_scan_tree_recursive(session, new_item);
1273 return IMAP_SUCCESS;
1276 static GSList *imap_parse_list(IMAPFolder *folder, IMAPSession *session,
1277 const gchar *real_path, gchar *separator)
1279 gchar buf[IMAPBUFSIZE];
1281 gchar separator_str[16];
1284 gchar *loc_name, *loc_path;
1285 GSList *item_list = NULL;
1287 FolderItem *new_item;
1289 debug_print("getting list of %s ...\n",
1290 *real_path ? real_path : "\"\"");
1292 str = g_string_new(NULL);
1295 if (sock_gets(SESSION(session)->sock, buf, sizeof(buf)) <= 0) {
1296 log_warning(_("error occurred while getting LIST.\n"));
1300 if (buf[0] != '*' || buf[1] != ' ') {
1301 log_print("IMAP4< %s\n", buf);
1304 debug_print("IMAP4< %s\n", buf);
1306 g_string_assign(str, buf);
1308 if (strncmp(p, "LIST ", 5) != 0) continue;
1311 if (*p != '(') continue;
1313 p = strchr_cpy(p, ')', flags, sizeof(flags));
1315 while (*p == ' ') p++;
1317 p = strchr_cpy(p, ' ', separator_str, sizeof(separator_str));
1319 extract_quote(separator_str, '"');
1320 if (!strcmp(separator_str, "NIL"))
1321 separator_str[0] = '\0';
1323 *separator = separator_str[0];
1326 while (*p == ' ') p++;
1327 if (*p == '{' || *p == '"')
1328 p = imap_parse_atom(SESSION(session)->sock, p,
1329 buf, sizeof(buf), str);
1331 strncpy2(buf, p, sizeof(buf));
1332 strtailchomp(buf, separator_str[0]);
1333 if (buf[0] == '\0') continue;
1334 if (!strcmp(buf, real_path)) continue;
1336 if (separator_str[0] != '\0')
1337 subst_char(buf, separator_str[0], '/');
1338 name = g_basename(buf);
1339 if (name[0] == '.') continue;
1341 loc_name = imap_modified_utf7_to_locale(name);
1342 loc_path = imap_modified_utf7_to_locale(buf);
1343 new_item = folder_item_new(FOLDER(folder), loc_name, loc_path);
1344 if (strcasestr(flags, "\\Noinferiors") != NULL)
1345 new_item->no_sub = TRUE;
1346 if (strcmp(buf, "INBOX") != 0 &&
1347 strcasestr(flags, "\\Noselect") != NULL)
1348 new_item->no_select = TRUE;
1350 item_list = g_slist_append(item_list, new_item);
1352 debug_print("folder %s has been added.\n", loc_path);
1357 g_string_free(str, TRUE);
1362 gint imap_create_tree(Folder *folder)
1364 g_return_val_if_fail(folder != NULL, -1);
1365 g_return_val_if_fail(folder->node != NULL, -1);
1366 g_return_val_if_fail(folder->node->data != NULL, -1);
1367 g_return_val_if_fail(folder->account != NULL, -1);
1369 imap_scan_tree(folder);
1370 imap_create_missing_folders(folder);
1375 static void imap_create_missing_folders(Folder *folder)
1377 g_return_if_fail(folder != NULL);
1380 folder->inbox = imap_create_special_folder
1381 (folder, F_INBOX, "INBOX");
1383 if (!folder->outbox)
1384 folder->outbox = imap_create_special_folder
1385 (folder, F_OUTBOX, "Sent");
1387 folder->draft = imap_create_special_folder
1388 (folder, F_DRAFT, "Drafts");
1390 folder->queue = imap_create_special_folder
1391 (folder, F_QUEUE, "Queue");
1394 folder->trash = imap_create_special_folder
1395 (folder, F_TRASH, "Trash");
1398 static FolderItem *imap_create_special_folder(Folder *folder,
1399 SpecialFolderItemType stype,
1403 FolderItem *new_item;
1405 g_return_val_if_fail(folder != NULL, NULL);
1406 g_return_val_if_fail(folder->node != NULL, NULL);
1407 g_return_val_if_fail(folder->node->data != NULL, NULL);
1408 g_return_val_if_fail(folder->account != NULL, NULL);
1409 g_return_val_if_fail(name != NULL, NULL);
1411 item = FOLDER_ITEM(folder->node->data);
1412 new_item = imap_create_folder(folder, item, name);
1415 g_warning("Can't create '%s'\n", name);
1416 if (!folder->inbox) return NULL;
1418 new_item = imap_create_folder(folder, folder->inbox, name);
1420 g_warning("Can't create '%s' under INBOX\n", name);
1422 new_item->stype = stype;
1424 new_item->stype = stype;
1429 static gchar *imap_folder_get_path(Folder *folder)
1433 g_return_val_if_fail(folder != NULL, NULL);
1434 g_return_val_if_fail(folder->account != NULL, NULL);
1436 folder_path = g_strconcat(get_imap_cache_dir(),
1438 folder->account->recv_server,
1440 folder->account->userid,
1446 static gchar *imap_item_get_path(Folder *folder, FolderItem *item)
1448 gchar *folder_path, *path;
1450 g_return_val_if_fail(folder != NULL, NULL);
1451 g_return_val_if_fail(item != NULL, NULL);
1452 folder_path = imap_folder_get_path(folder);
1454 g_return_val_if_fail(folder_path != NULL, NULL);
1455 if (folder_path[0] == G_DIR_SEPARATOR) {
1457 path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
1460 path = g_strdup(folder_path);
1463 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1464 folder_path, G_DIR_SEPARATOR_S,
1467 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1470 g_free(folder_path);
1475 FolderItem *imap_create_folder(Folder *folder, FolderItem *parent,
1478 gchar *dirpath, *imap_path;
1479 IMAPSession *session;
1480 FolderItem *new_item;
1486 g_return_val_if_fail(folder != NULL, NULL);
1487 g_return_val_if_fail(folder->account != NULL, NULL);
1488 g_return_val_if_fail(parent != NULL, NULL);
1489 g_return_val_if_fail(name != NULL, NULL);
1491 session = imap_session_get(folder);
1492 if (!session) return NULL;
1494 if (!parent->parent && strcmp(name, "INBOX") == 0)
1495 dirpath = g_strdup(name);
1496 else if (parent->path)
1497 dirpath = g_strconcat(parent->path, "/", name, NULL);
1498 else if ((p = strchr(name, '/')) != NULL && *(p + 1) != '\0')
1499 dirpath = g_strdup(name);
1500 else if (folder->account->imap_dir && *folder->account->imap_dir) {
1503 Xstrdup_a(imap_dir, folder->account->imap_dir, return NULL);
1504 strtailchomp(imap_dir, '/');
1505 dirpath = g_strconcat(imap_dir, "/", name, NULL);
1507 dirpath = g_strdup(name);
1509 /* keep trailing directory separator to create a folder that contains
1511 imap_path = imap_locale_to_modified_utf7(dirpath);
1512 strtailchomp(dirpath, '/');
1513 Xstrdup_a(new_name, name, {g_free(dirpath); return NULL;});
1514 strtailchomp(new_name, '/');
1515 separator = imap_get_path_separator(IMAP_FOLDER(folder), imap_path);
1516 imap_path_separator_subst(imap_path, separator);
1517 subst_char(new_name, '/', separator);
1519 if (strcmp(name, "INBOX") != 0) {
1522 gboolean exist = FALSE;
1524 argbuf = g_ptr_array_new();
1525 ok = imap_cmd_list(session, NULL, imap_path,
1527 if (ok != IMAP_SUCCESS) {
1528 log_warning(_("can't create mailbox: LIST failed\n"));
1531 ptr_array_free_strings(argbuf);
1532 g_ptr_array_free(argbuf, TRUE);
1536 for (i = 0; i < argbuf->len; i++) {
1538 str = g_ptr_array_index(argbuf, i);
1539 if (!strncmp(str, "LIST ", 5)) {
1544 ptr_array_free_strings(argbuf);
1545 g_ptr_array_free(argbuf, TRUE);
1548 ok = imap_cmd_create(session, imap_path);
1549 if (ok != IMAP_SUCCESS) {
1550 log_warning(_("can't create mailbox\n"));
1558 new_item = folder_item_new(folder, new_name, dirpath);
1559 folder_item_append(parent, new_item);
1563 dirpath = folder_item_get_path(new_item);
1564 if (!is_dir_exist(dirpath))
1565 make_dir_hier(dirpath);
1571 gint imap_rename_folder(Folder *folder, FolderItem *item, const gchar *name)
1575 gchar *real_oldpath;
1576 gchar *real_newpath;
1579 gchar *old_cache_dir;
1580 gchar *new_cache_dir;
1581 IMAPSession *session;
1584 gint exists, recent, unseen;
1585 guint32 uid_validity;
1587 g_return_val_if_fail(folder != NULL, -1);
1588 g_return_val_if_fail(item != NULL, -1);
1589 g_return_val_if_fail(item->path != NULL, -1);
1590 g_return_val_if_fail(name != NULL, -1);
1592 session = imap_session_get(folder);
1593 if (!session) return -1;
1595 real_oldpath = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1597 g_free(session->mbox);
1598 session->mbox = NULL;
1599 ok = imap_cmd_examine(session, "INBOX",
1600 &exists, &recent, &unseen, &uid_validity);
1601 if (ok != IMAP_SUCCESS) {
1602 g_free(real_oldpath);
1606 separator = imap_get_path_separator(IMAP_FOLDER(folder), item->path);
1607 if (strchr(item->path, G_DIR_SEPARATOR)) {
1608 dirpath = g_dirname(item->path);
1609 newpath = g_strconcat(dirpath, G_DIR_SEPARATOR_S, name, NULL);
1612 newpath = g_strdup(name);
1614 real_newpath = imap_locale_to_modified_utf7(newpath);
1615 imap_path_separator_subst(real_newpath, separator);
1617 ok = imap_cmd_rename(session, real_oldpath, real_newpath);
1618 if (ok != IMAP_SUCCESS) {
1619 log_warning(_("can't rename mailbox: %s to %s\n"),
1620 real_oldpath, real_newpath);
1621 g_free(real_oldpath);
1623 g_free(real_newpath);
1628 item->name = g_strdup(name);
1630 old_cache_dir = folder_item_get_path(item);
1632 node = g_node_find(item->folder->node, G_PRE_ORDER, G_TRAVERSE_ALL,
1634 paths[0] = g_strdup(item->path);
1636 g_node_traverse(node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1637 imap_rename_folder_func, paths);
1639 if (is_dir_exist(old_cache_dir)) {
1640 new_cache_dir = folder_item_get_path(item);
1641 if (rename(old_cache_dir, new_cache_dir) < 0) {
1642 FILE_OP_ERROR(old_cache_dir, "rename");
1644 g_free(new_cache_dir);
1647 g_free(old_cache_dir);
1650 g_free(real_oldpath);
1651 g_free(real_newpath);
1656 gint imap_remove_folder(Folder *folder, FolderItem *item)
1659 IMAPSession *session;
1662 gint exists, recent, unseen;
1663 guint32 uid_validity;
1665 g_return_val_if_fail(folder != NULL, -1);
1666 g_return_val_if_fail(item != NULL, -1);
1667 g_return_val_if_fail(item->path != NULL, -1);
1669 session = imap_session_get(folder);
1670 if (!session) return -1;
1672 path = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1674 ok = imap_cmd_examine(session, "INBOX",
1675 &exists, &recent, &unseen, &uid_validity);
1676 if (ok != IMAP_SUCCESS) {
1681 ok = imap_cmd_delete(session, path);
1682 if (ok != IMAP_SUCCESS) {
1683 log_warning(_("can't delete mailbox\n"));
1689 cache_dir = folder_item_get_path(item);
1690 if (is_dir_exist(cache_dir) && remove_dir_recursive(cache_dir) < 0)
1691 g_warning("can't remove directory '%s'\n", cache_dir);
1693 folder_item_remove(item);
1698 static GSList *imap_get_uncached_messages(IMAPSession *session,
1700 MsgNumberList *numlist)
1703 GSList *newlist = NULL;
1704 GSList *llast = NULL;
1707 GSList *seq_list, *cur;
1710 g_return_val_if_fail(session != NULL, NULL);
1711 g_return_val_if_fail(item != NULL, NULL);
1712 g_return_val_if_fail(item->folder != NULL, NULL);
1713 g_return_val_if_fail(FOLDER_CLASS(item->folder) == &imap_class, NULL);
1715 seq_list = imap_get_seq_set_from_numlist(numlist);
1716 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
1717 imapset = cur->data;
1719 if (imap_cmd_envelope(session, imapset)
1721 log_warning(_("can't get envelope\n"));
1725 str = g_string_new(NULL);
1728 if ((tmp = sock_getline(SESSION(session)->sock)) == NULL) {
1729 log_warning(_("error occurred while getting envelope.\n"));
1730 g_string_free(str, TRUE);
1734 if (tmp[0] != '*' || tmp[1] != ' ') {
1735 log_print("IMAP4< %s\n", tmp);
1739 if (strstr(tmp, "FETCH") == NULL) {
1740 log_print("IMAP4< %s\n", tmp);
1744 log_print("IMAP4< %s\n", tmp);
1745 g_string_assign(str, tmp);
1748 msginfo = imap_parse_envelope
1749 (SESSION(session)->sock, item, str);
1751 log_warning(_("can't parse envelope: %s\n"), str->str);
1754 if (item->stype == F_QUEUE) {
1755 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_QUEUED);
1756 } else if (item->stype == F_DRAFT) {
1757 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_DRAFT);
1760 msginfo->folder = item;
1763 llast = newlist = g_slist_append(newlist, msginfo);
1765 llast = g_slist_append(llast, msginfo);
1766 llast = llast->next;
1770 g_string_free(str, TRUE);
1772 imap_seq_set_free(seq_list);
1777 static void imap_delete_all_cached_messages(FolderItem *item)
1781 g_return_if_fail(item != NULL);
1782 g_return_if_fail(item->folder != NULL);
1783 g_return_if_fail(FOLDER_CLASS(item->folder) == &imap_class);
1785 debug_print("Deleting all cached messages...\n");
1787 dir = folder_item_get_path(item);
1788 if (is_dir_exist(dir))
1789 remove_all_numbered_files(dir);
1792 debug_print("done.\n");
1796 static SockInfo *imap_open_tunnel(const gchar *server,
1797 const gchar *tunnelcmd,
1800 static SockInfo *imap_open_tunnel(const gchar *server,
1801 const gchar *tunnelcmd)
1806 if ((sock = sock_connect_cmd(server, tunnelcmd)) == NULL) {
1807 log_warning(_("Can't establish IMAP4 session with: %s\n"),
1812 return imap_init_sock(sock, ssl_type);
1814 return imap_init_sock(sock);
1820 static SockInfo *imap_open(const gchar *server, gushort port,
1823 static SockInfo *imap_open(const gchar *server, gushort port)
1828 if ((sock = sock_connect(server, port)) == NULL) {
1829 log_warning(_("Can't connect to IMAP4 server: %s:%d\n"),
1835 if (ssl_type == SSL_TUNNEL && !ssl_init_socket(sock)) {
1836 log_warning(_("Can't establish IMAP4 session with: %s:%d\n"),
1846 static SockInfo *imap_init_sock(SockInfo *sock, SSLType ssl_type)
1848 static SockInfo *imap_init_sock(SockInfo *sock)
1855 static GList *imap_parse_namespace_str(gchar *str)
1860 IMAPNameSpace *namespace;
1861 GList *ns_list = NULL;
1863 while (*p != '\0') {
1864 /* parse ("#foo" "/") */
1866 while (*p && *p != '(') p++;
1867 if (*p == '\0') break;
1870 while (*p && *p != '"') p++;
1871 if (*p == '\0') break;
1875 while (*p && *p != '"') p++;
1876 if (*p == '\0') break;
1880 while (*p && isspace(*p)) p++;
1881 if (*p == '\0') break;
1882 if (strncmp(p, "NIL", 3) == 0)
1884 else if (*p == '"') {
1887 while (*p && *p != '"') p++;
1888 if (*p == '\0') break;
1893 while (*p && *p != ')') p++;
1894 if (*p == '\0') break;
1897 namespace = g_new(IMAPNameSpace, 1);
1898 namespace->name = g_strdup(name);
1899 namespace->separator = separator ? separator[0] : '\0';
1900 ns_list = g_list_append(ns_list, namespace);
1906 static void imap_parse_namespace(IMAPSession *session, IMAPFolder *folder)
1911 g_return_if_fail(session != NULL);
1912 g_return_if_fail(folder != NULL);
1914 if (folder->ns_personal != NULL ||
1915 folder->ns_others != NULL ||
1916 folder->ns_shared != NULL)
1919 if (!imap_has_capability(session, "NAMESPACE")) {
1920 imap_get_namespace_by_list(session, folder);
1924 if (imap_cmd_namespace(session, &ns_str)
1926 log_warning(_("can't get namespace\n"));
1930 str_array = strsplit_parenthesis(ns_str, '(', ')', 3);
1931 if (str_array == NULL) {
1933 imap_get_namespace_by_list(session, folder);
1937 folder->ns_personal = imap_parse_namespace_str(str_array[0]);
1938 if (str_array[0] && str_array[1])
1939 folder->ns_others = imap_parse_namespace_str(str_array[1]);
1940 if (str_array[0] && str_array[1] && str_array[2])
1941 folder->ns_shared = imap_parse_namespace_str(str_array[2]);
1942 g_strfreev(str_array);
1946 static void imap_get_namespace_by_list(IMAPSession *session, IMAPFolder *folder)
1948 GSList *item_list, *cur;
1949 gchar separator = '\0';
1950 IMAPNameSpace *namespace;
1952 g_return_if_fail(session != NULL);
1953 g_return_if_fail(folder != NULL);
1955 if (folder->ns_personal != NULL ||
1956 folder->ns_others != NULL ||
1957 folder->ns_shared != NULL)
1960 imap_gen_send(session, "LIST \"\" \"\"");
1961 item_list = imap_parse_list(folder, session, "", &separator);
1962 for (cur = item_list; cur != NULL; cur = cur->next)
1963 folder_item_destroy(FOLDER_ITEM(cur->data));
1964 g_slist_free(item_list);
1966 namespace = g_new(IMAPNameSpace, 1);
1967 namespace->name = g_strdup("");
1968 namespace->separator = separator;
1969 folder->ns_personal = g_list_append(NULL, namespace);
1972 static IMAPNameSpace *imap_find_namespace_from_list(GList *ns_list,
1975 IMAPNameSpace *namespace = NULL;
1976 gchar *tmp_path, *name;
1978 if (!path) path = "";
1980 Xstrcat_a(tmp_path, path, "/", return NULL);
1982 for (; ns_list != NULL; ns_list = ns_list->next) {
1983 IMAPNameSpace *tmp_ns = ns_list->data;
1985 Xstrdup_a(name, tmp_ns->name, return namespace);
1986 if (tmp_ns->separator && tmp_ns->separator != '/')
1987 subst_char(name, tmp_ns->separator, '/');
1988 if (strncmp(tmp_path, name, strlen(name)) == 0)
1995 static IMAPNameSpace *imap_find_namespace(IMAPFolder *folder,
1998 IMAPNameSpace *namespace;
2000 g_return_val_if_fail(folder != NULL, NULL);
2002 namespace = imap_find_namespace_from_list(folder->ns_personal, path);
2003 if (namespace) return namespace;
2004 namespace = imap_find_namespace_from_list(folder->ns_others, path);
2005 if (namespace) return namespace;
2006 namespace = imap_find_namespace_from_list(folder->ns_shared, path);
2007 if (namespace) return namespace;
2012 static gchar imap_get_path_separator(IMAPFolder *folder, const gchar *path)
2014 IMAPNameSpace *namespace;
2015 gchar separator = '/';
2017 namespace = imap_find_namespace(folder, path);
2018 if (namespace && namespace->separator)
2019 separator = namespace->separator;
2024 static gchar *imap_get_real_path(IMAPFolder *folder, const gchar *path)
2029 g_return_val_if_fail(folder != NULL, NULL);
2030 g_return_val_if_fail(path != NULL, NULL);
2032 real_path = imap_locale_to_modified_utf7(path);
2033 separator = imap_get_path_separator(folder, path);
2034 imap_path_separator_subst(real_path, separator);
2039 static gchar *imap_parse_atom(SockInfo *sock, gchar *src,
2040 gchar *dest, gint dest_len, GString *str)
2042 gchar *cur_pos = src;
2045 g_return_val_if_fail(str != NULL, cur_pos);
2047 /* read the next line if the current response buffer is empty */
2048 while (isspace(*cur_pos)) cur_pos++;
2049 while (*cur_pos == '\0') {
2050 if ((nextline = sock_getline(sock)) == NULL)
2052 g_string_assign(str, nextline);
2054 strretchomp(nextline);
2055 /* log_print("IMAP4< %s\n", nextline); */
2056 debug_print("IMAP4< %s\n", nextline);
2059 while (isspace(*cur_pos)) cur_pos++;
2062 if (!strncmp(cur_pos, "NIL", 3)) {
2065 } else if (*cur_pos == '\"') {
2068 p = get_quoted(cur_pos, '\"', dest, dest_len);
2069 cur_pos = p ? p : cur_pos + 2;
2070 } else if (*cur_pos == '{') {
2075 cur_pos = strchr_cpy(cur_pos + 1, '}', buf, sizeof(buf));
2077 g_return_val_if_fail(len > 0, cur_pos);
2079 g_string_truncate(str, 0);
2083 if ((nextline = sock_getline(sock)) == NULL)
2085 line_len += strlen(nextline);
2086 g_string_append(str, nextline);
2088 strretchomp(nextline);
2089 /* log_print("IMAP4< %s\n", nextline); */
2090 debug_print("IMAP4< %s\n", nextline);
2092 } while (line_len < len);
2094 memcpy(dest, cur_pos, MIN(len, dest_len - 1));
2095 dest[MIN(len, dest_len - 1)] = '\0';
2102 static gchar *imap_get_header(SockInfo *sock, gchar *cur_pos, gchar **headers,
2112 g_return_val_if_fail(str != NULL, cur_pos);
2114 while (isspace(*cur_pos)) cur_pos++;
2116 g_return_val_if_fail(*cur_pos == '{', cur_pos);
2118 cur_pos = strchr_cpy(cur_pos + 1, '}', buf, sizeof(buf));
2120 g_return_val_if_fail(len > 0, cur_pos);
2122 g_string_truncate(str, 0);
2126 if ((nextline = sock_getline(sock)) == NULL)
2128 block_len += strlen(nextline);
2129 g_string_append(str, nextline);
2131 strretchomp(nextline);
2132 /* debug_print("IMAP4< %s\n", nextline); */
2134 } while (block_len < len);
2136 debug_print("IMAP4< [contents of BODY.PEEK[HEADER.FIELDS (...)]]\n");
2138 *headers = g_strndup(cur_pos, len);
2141 while (isspace(*cur_pos)) cur_pos++;
2142 while (*cur_pos == '\0') {
2143 if ((nextline = sock_getline(sock)) == NULL)
2145 g_string_assign(str, nextline);
2147 strretchomp(nextline);
2148 debug_print("IMAP4< %s\n", nextline);
2151 while (isspace(*cur_pos)) cur_pos++;
2157 static MsgFlags imap_parse_flags(const gchar *flag_str)
2159 const gchar *p = flag_str;
2160 MsgFlags flags = {0, 0};
2162 flags.perm_flags = MSG_UNREAD;
2164 while ((p = strchr(p, '\\')) != NULL) {
2167 if (g_strncasecmp(p, "Recent", 6) == 0 && MSG_IS_UNREAD(flags)) {
2168 MSG_SET_PERM_FLAGS(flags, MSG_NEW);
2169 } else if (g_strncasecmp(p, "Seen", 4) == 0) {
2170 MSG_UNSET_PERM_FLAGS(flags, MSG_NEW|MSG_UNREAD);
2171 } else if (g_strncasecmp(p, "Deleted", 7) == 0) {
2172 MSG_SET_PERM_FLAGS(flags, MSG_DELETED);
2173 } else if (g_strncasecmp(p, "Flagged", 7) == 0) {
2174 MSG_SET_PERM_FLAGS(flags, MSG_MARKED);
2175 } else if (g_strncasecmp(p, "Answered", 8) == 0) {
2176 MSG_SET_PERM_FLAGS(flags, MSG_REPLIED);
2183 static MsgInfo *imap_parse_envelope(SockInfo *sock, FolderItem *item,
2186 gchar buf[IMAPBUFSIZE];
2187 MsgInfo *msginfo = NULL;
2192 MsgFlags flags = {0, 0}, imap_flags = {0, 0};
2194 g_return_val_if_fail(line_str != NULL, NULL);
2195 g_return_val_if_fail(line_str->str[0] == '*' &&
2196 line_str->str[1] == ' ', NULL);
2198 MSG_SET_TMP_FLAGS(flags, MSG_IMAP);
2199 if (item->stype == F_QUEUE) {
2200 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
2201 } else if (item->stype == F_DRAFT) {
2202 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
2205 cur_pos = line_str->str + 2;
2207 #define PARSE_ONE_ELEMENT(ch) \
2209 cur_pos = strchr_cpy(cur_pos, ch, buf, sizeof(buf)); \
2210 if (cur_pos == NULL) { \
2211 g_warning("cur_pos == NULL\n"); \
2212 procmsg_msginfo_free(msginfo); \
2217 PARSE_ONE_ELEMENT(' ');
2220 PARSE_ONE_ELEMENT(' ');
2221 g_return_val_if_fail(!strcmp(buf, "FETCH"), NULL);
2223 g_return_val_if_fail(*cur_pos == '(', NULL);
2226 while (*cur_pos != '\0' && *cur_pos != ')') {
2227 while (*cur_pos == ' ') cur_pos++;
2229 if (!strncmp(cur_pos, "UID ", 4)) {
2231 uid = strtoul(cur_pos, &cur_pos, 10);
2232 } else if (!strncmp(cur_pos, "FLAGS ", 6)) {
2234 if (*cur_pos != '(') {
2235 g_warning("*cur_pos != '('\n");
2236 procmsg_msginfo_free(msginfo);
2240 PARSE_ONE_ELEMENT(')');
2241 imap_flags = imap_parse_flags(buf);
2242 } else if (!strncmp(cur_pos, "RFC822.SIZE ", 12)) {
2244 size = strtol(cur_pos, &cur_pos, 10);
2245 } else if (!strncmp(cur_pos, "BODY[HEADER.FIELDS ", 19)) {
2249 if (*cur_pos != '(') {
2250 g_warning("*cur_pos != '('\n");
2251 procmsg_msginfo_free(msginfo);
2255 PARSE_ONE_ELEMENT(')');
2256 if (*cur_pos != ']') {
2257 g_warning("*cur_pos != ']'\n");
2258 procmsg_msginfo_free(msginfo);
2263 cur_pos = imap_get_header(sock, cur_pos, &headers,
2265 msginfo = procheader_parse_str(headers, flags, FALSE, FALSE);
2268 g_warning("invalid FETCH response: %s\n", cur_pos);
2274 msginfo->msgnum = uid;
2275 msginfo->size = size;
2276 msginfo->flags.tmp_flags |= imap_flags.tmp_flags;
2277 msginfo->flags.perm_flags = imap_flags.perm_flags;
2283 static gchar *imap_get_flag_str(IMAPFlags flags)
2288 str = g_string_new(NULL);
2290 if (IMAP_IS_SEEN(flags)) g_string_append(str, "\\Seen ");
2291 if (IMAP_IS_ANSWERED(flags)) g_string_append(str, "\\Answered ");
2292 if (IMAP_IS_FLAGGED(flags)) g_string_append(str, "\\Flagged ");
2293 if (IMAP_IS_DELETED(flags)) g_string_append(str, "\\Deleted ");
2294 if (IMAP_IS_DRAFT(flags)) g_string_append(str, "\\Draft");
2296 if (str->len > 0 && str->str[str->len - 1] == ' ')
2297 g_string_truncate(str, str->len - 1);
2300 g_string_free(str, FALSE);
2305 static gint imap_set_message_flags(IMAPSession *session,
2306 MsgNumberList *numlist,
2313 GSList *seq_list, *cur;
2316 flag_str = imap_get_flag_str(flags);
2317 cmd = g_strconcat(is_set ? "+FLAGS.SILENT (" : "-FLAGS.SILENT (",
2318 flag_str, ")", NULL);
2321 seq_list = imap_get_seq_set_from_numlist(numlist);
2322 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
2323 imapset = cur->data;
2325 ok = imap_cmd_store(session, imapset, cmd);
2327 imap_seq_set_free(seq_list);
2333 static gint imap_select(IMAPSession *session, IMAPFolder *folder,
2335 gint *exists, gint *recent, gint *unseen,
2336 guint32 *uid_validity)
2340 gint exists_, recent_, unseen_, uid_validity_;
2342 if (!exists || !recent || !unseen || !uid_validity) {
2343 if (session->mbox && strcmp(session->mbox, path) == 0)
2344 return IMAP_SUCCESS;
2348 uid_validity = &uid_validity_;
2351 g_free(session->mbox);
2352 session->mbox = NULL;
2354 real_path = imap_get_real_path(folder, path);
2355 ok = imap_cmd_select(session, real_path,
2356 exists, recent, unseen, uid_validity);
2357 if (ok != IMAP_SUCCESS)
2358 log_warning(_("can't select folder: %s\n"), real_path);
2360 session->mbox = g_strdup(path);
2361 session->folder_content_changed = FALSE;
2368 #define THROW(err) { ok = err; goto catch; }
2370 static gint imap_status(IMAPSession *session, IMAPFolder *folder,
2372 gint *messages, gint *recent,
2373 guint32 *uid_next, guint32 *uid_validity,
2382 *messages = *recent = *uid_next = *uid_validity = *unseen = 0;
2384 argbuf = g_ptr_array_new();
2386 real_path = imap_get_real_path(folder, path);
2387 QUOTE_IF_REQUIRED(real_path_, real_path);
2388 imap_gen_send(session, "STATUS %s "
2389 "(MESSAGES RECENT UIDNEXT UIDVALIDITY UNSEEN)",
2392 ok = imap_cmd_ok(session, argbuf);
2393 if (ok != IMAP_SUCCESS) THROW(ok);
2395 str = search_array_str(argbuf, "STATUS");
2396 if (!str) THROW(IMAP_ERROR);
2398 str = strchr(str, '(');
2399 if (!str) THROW(IMAP_ERROR);
2401 while (*str != '\0' && *str != ')') {
2402 while (*str == ' ') str++;
2404 if (!strncmp(str, "MESSAGES ", 9)) {
2406 *messages = strtol(str, &str, 10);
2407 } else if (!strncmp(str, "RECENT ", 7)) {
2409 *recent = strtol(str, &str, 10);
2410 } else if (!strncmp(str, "UIDNEXT ", 8)) {
2412 *uid_next = strtoul(str, &str, 10);
2413 } else if (!strncmp(str, "UIDVALIDITY ", 12)) {
2415 *uid_validity = strtoul(str, &str, 10);
2416 } else if (!strncmp(str, "UNSEEN ", 7)) {
2418 *unseen = strtol(str, &str, 10);
2420 g_warning("invalid STATUS response: %s\n", str);
2427 ptr_array_free_strings(argbuf);
2428 g_ptr_array_free(argbuf, TRUE);
2435 static gboolean imap_has_capability(IMAPSession *session, const gchar *cap)
2439 for (p = session->capability; *p != NULL; ++p) {
2440 if (!g_strcasecmp(*p, cap))
2447 void imap_free_capabilities(IMAPSession *session)
2449 g_strfreev(session->capability);
2450 session->capability = NULL;
2453 /* low-level IMAP4rev1 commands */
2455 static gint imap_cmd_authenticate(IMAPSession *session, const gchar *user,
2456 const gchar *pass, IMAPAuthType type)
2463 gchar hexdigest[33];
2467 auth_type = "CRAM-MD5";
2469 imap_gen_send(session, "AUTHENTICATE %s", auth_type);
2470 ok = imap_gen_recv(session, &buf);
2471 if (ok != IMAP_SUCCESS || buf[0] != '+' || buf[1] != ' ') {
2476 challenge = g_malloc(strlen(buf + 2) + 1);
2477 challenge_len = base64_decode(challenge, buf + 2, -1);
2478 challenge[challenge_len] = '\0';
2480 log_print("IMAP< [Decoded: %s]\n", challenge);
2482 md5_hex_hmac(hexdigest, challenge, challenge_len, pass, strlen(pass));
2485 response = g_strdup_printf("%s %s", user, hexdigest);
2486 log_print("IMAP> [Encoded: %s]\n", response);
2487 response64 = g_malloc((strlen(response) + 3) * 2 + 1);
2488 base64_encode(response64, response, strlen(response));
2491 log_print("IMAP> %s\n", response64);
2492 sock_puts(SESSION(session)->sock, response64);
2493 ok = imap_cmd_ok(session, NULL);
2494 if (ok != IMAP_SUCCESS)
2495 log_warning(_("IMAP4 authentication failed.\n"));
2500 static gint imap_cmd_login(IMAPSession *session,
2501 const gchar *user, const gchar *pass)
2503 gchar *user_, *pass_;
2506 QUOTE_IF_REQUIRED(user_, user);
2507 QUOTE_IF_REQUIRED(pass_, pass);
2508 imap_gen_send(session, "LOGIN %s %s", user_, pass_);
2510 ok = imap_cmd_ok(session, NULL);
2511 if (ok != IMAP_SUCCESS)
2512 log_warning(_("IMAP4 login failed.\n"));
2517 static gint imap_cmd_logout(IMAPSession *session)
2519 imap_gen_send(session, "LOGOUT");
2520 return imap_cmd_ok(session, NULL);
2523 static gint imap_cmd_noop(IMAPSession *session)
2525 imap_gen_send(session, "NOOP");
2526 return imap_cmd_ok(session, NULL);
2529 static gint imap_cmd_starttls(IMAPSession *session)
2531 imap_gen_send(session, "STARTTLS");
2532 return imap_cmd_ok(session, NULL);
2535 #define THROW(err) { ok = err; goto catch; }
2537 static gint imap_cmd_namespace(IMAPSession *session, gchar **ns_str)
2543 argbuf = g_ptr_array_new();
2545 imap_gen_send(session, "NAMESPACE");
2546 if ((ok = imap_cmd_ok(session, argbuf)) != IMAP_SUCCESS) THROW(ok);
2548 str = search_array_str(argbuf, "NAMESPACE");
2549 if (!str) THROW(IMAP_ERROR);
2551 *ns_str = g_strdup(str);
2554 ptr_array_free_strings(argbuf);
2555 g_ptr_array_free(argbuf, TRUE);
2562 static gint imap_cmd_list(IMAPSession *session, const gchar *ref,
2563 const gchar *mailbox, GPtrArray *argbuf)
2565 gchar *ref_, *mailbox_;
2567 if (!ref) ref = "\"\"";
2568 if (!mailbox) mailbox = "\"\"";
2570 QUOTE_IF_REQUIRED(ref_, ref);
2571 QUOTE_IF_REQUIRED(mailbox_, mailbox);
2572 imap_gen_send(session, "LIST %s %s", ref_, mailbox_);
2574 return imap_cmd_ok(session, argbuf);
2577 #define THROW goto catch
2579 static gint imap_cmd_do_select(IMAPSession *session, const gchar *folder,
2581 gint *exists, gint *recent, gint *unseen,
2582 guint32 *uid_validity)
2590 *exists = *recent = *unseen = *uid_validity = 0;
2591 argbuf = g_ptr_array_new();
2594 select_cmd = "EXAMINE";
2596 select_cmd = "SELECT";
2598 QUOTE_IF_REQUIRED(folder_, folder);
2599 imap_gen_send(session, "%s %s", select_cmd, folder_);
2601 if ((ok = imap_cmd_ok(session, argbuf)) != IMAP_SUCCESS) THROW;
2603 resp_str = search_array_contain_str(argbuf, "EXISTS");
2605 if (sscanf(resp_str,"%d EXISTS", exists) != 1) {
2606 g_warning("imap_cmd_select(): invalid EXISTS line.\n");
2611 resp_str = search_array_contain_str(argbuf, "RECENT");
2613 if (sscanf(resp_str, "%d RECENT", recent) != 1) {
2614 g_warning("imap_cmd_select(): invalid RECENT line.\n");
2619 resp_str = search_array_contain_str(argbuf, "UIDVALIDITY");
2621 if (sscanf(resp_str, "OK [UIDVALIDITY %u] ", uid_validity)
2623 g_warning("imap_cmd_select(): invalid UIDVALIDITY line.\n");
2628 resp_str = search_array_contain_str(argbuf, "UNSEEN");
2630 if (sscanf(resp_str, "OK [UNSEEN %d] ", unseen) != 1) {
2631 g_warning("imap_cmd_select(): invalid UNSEEN line.\n");
2637 ptr_array_free_strings(argbuf);
2638 g_ptr_array_free(argbuf, TRUE);
2643 static gint imap_cmd_select(IMAPSession *session, const gchar *folder,
2644 gint *exists, gint *recent, gint *unseen,
2645 guint32 *uid_validity)
2647 return imap_cmd_do_select(session, folder, FALSE,
2648 exists, recent, unseen, uid_validity);
2651 static gint imap_cmd_examine(IMAPSession *session, const gchar *folder,
2652 gint *exists, gint *recent, gint *unseen,
2653 guint32 *uid_validity)
2655 return imap_cmd_do_select(session, folder, TRUE,
2656 exists, recent, unseen, uid_validity);
2661 static gint imap_cmd_create(IMAPSession *session, const gchar *folder)
2665 QUOTE_IF_REQUIRED(folder_, folder);
2666 imap_gen_send(session, "CREATE %s", folder_);
2668 return imap_cmd_ok(session, NULL);
2671 static gint imap_cmd_rename(IMAPSession *session, const gchar *old_folder,
2672 const gchar *new_folder)
2674 gchar *old_folder_, *new_folder_;
2676 QUOTE_IF_REQUIRED(old_folder_, old_folder);
2677 QUOTE_IF_REQUIRED(new_folder_, new_folder);
2678 imap_gen_send(session, "RENAME %s %s", old_folder_, new_folder_);
2680 return imap_cmd_ok(session, NULL);
2683 static gint imap_cmd_delete(IMAPSession *session, const gchar *folder)
2687 QUOTE_IF_REQUIRED(folder_, folder);
2688 imap_gen_send(session, "DELETE %s", folder_);
2690 return imap_cmd_ok(session, NULL);
2693 static gint imap_cmd_search(IMAPSession *session, const gchar *criteria, GSList **list)
2699 g_return_val_if_fail(criteria != NULL, IMAP_ERROR);
2700 g_return_val_if_fail(list != NULL, IMAP_ERROR);
2704 argbuf = g_ptr_array_new();
2705 imap_gen_send(session, "UID SEARCH %s", criteria);
2707 ok = imap_cmd_ok(session, argbuf);
2708 if (ok != IMAP_SUCCESS) {
2709 ptr_array_free_strings(argbuf);
2710 g_ptr_array_free(argbuf, TRUE);
2714 if ((uidlist = search_array_str(argbuf, "SEARCH ")) != NULL) {
2715 gchar **strlist, **p;
2717 strlist = g_strsplit(uidlist + 7, " ", 0);
2718 for (p = strlist; *p != NULL; ++p) {
2721 if (sscanf(*p, "%d", &msgnum) == 1)
2722 *list = g_slist_append(*list, GINT_TO_POINTER(msgnum));
2724 g_strfreev(strlist);
2726 ptr_array_free_strings(argbuf);
2727 g_ptr_array_free(argbuf, TRUE);
2729 return IMAP_SUCCESS;
2732 static gint imap_cmd_fetch(IMAPSession *session, guint32 uid, const gchar *filename)
2740 g_return_val_if_fail(filename != NULL, IMAP_ERROR);
2742 imap_gen_send(session, "UID FETCH %d BODY.PEEK[]", uid);
2744 while ((ok = imap_gen_recv(session, &buf)) == IMAP_SUCCESS) {
2745 if (buf[0] != '*' || buf[1] != ' ') {
2749 if (strstr(buf, "FETCH") != NULL) break;
2752 if (ok != IMAP_SUCCESS) {
2757 #define RETURN_ERROR_IF_FAIL(cond) \
2760 return IMAP_ERROR; \
2763 cur_pos = strchr(buf, '{');
2764 RETURN_ERROR_IF_FAIL(cur_pos != NULL);
2765 cur_pos = strchr_cpy(cur_pos + 1, '}', size_str, sizeof(size_str));
2766 RETURN_ERROR_IF_FAIL(cur_pos != NULL);
2767 size_num = atol(size_str);
2768 RETURN_ERROR_IF_FAIL(size_num > 0);
2770 RETURN_ERROR_IF_FAIL(*cur_pos == '\0');
2772 #undef RETURN_ERROR_IF_FAIL
2776 if (recv_bytes_write_to_file(SESSION(session)->sock, size_num, filename) != 0)
2779 if (imap_gen_recv(session, &buf) != IMAP_SUCCESS) {
2784 if (buf[0] == '\0' || buf[strlen(buf) - 1] != ')') {
2790 ok = imap_cmd_ok(session, NULL);
2795 static gint imap_cmd_append(IMAPSession *session, const gchar *destfolder,
2796 const gchar *file, IMAPFlags flags, guint32 *new_uid)
2804 gchar buf[BUFFSIZE];
2809 g_return_val_if_fail(file != NULL, IMAP_ERROR);
2811 size = get_file_size_as_crlf(file);
2812 if ((fp = fopen(file, "rb")) == NULL) {
2813 FILE_OP_ERROR(file, "fopen");
2816 QUOTE_IF_REQUIRED(destfolder_, destfolder);
2817 flag_str = imap_get_flag_str(flags);
2818 imap_gen_send(session, "APPEND %s (%s) {%d}",
2819 destfolder_, flag_str, size);
2822 ok = imap_gen_recv(session, &ret);
2823 if (ok != IMAP_SUCCESS || ret[0] != '+' || ret[1] != ' ') {
2824 log_warning(_("can't append %s to %s\n"), file, destfolder_);
2831 log_print("IMAP4> %s\n", _("(sending file...)"));
2833 while (fgets(buf, sizeof(buf), fp) != NULL) {
2835 if (sock_puts(SESSION(session)->sock, buf) < 0) {
2842 FILE_OP_ERROR(file, "fgets");
2847 sock_puts(SESSION(session)->sock, "");
2851 if (new_uid != NULL)
2854 if (new_uid != NULL && session->uidplus) {
2855 argbuf = g_ptr_array_new();
2857 ok = imap_cmd_ok(session, argbuf);
2858 if (ok != IMAP_SUCCESS)
2859 log_warning(_("can't append message to %s\n"),
2861 else if (argbuf->len > 0) {
2862 resp_str = g_ptr_array_index(argbuf, argbuf->len - 1);
2864 sscanf(resp_str, "%*u OK [APPENDUID %*u %u]",
2866 *new_uid = new_uid_;
2870 ptr_array_free_strings(argbuf);
2871 g_ptr_array_free(argbuf, TRUE);
2873 ok = imap_cmd_ok(session, NULL);
2878 static gint imap_cmd_copy(IMAPSession *session, const gchar *seq_set,
2879 const gchar *destfolder, GRelation *uid_mapping)
2885 g_return_val_if_fail(session != NULL, IMAP_ERROR);
2886 g_return_val_if_fail(seq_set != NULL, IMAP_ERROR);
2887 g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
2889 QUOTE_IF_REQUIRED(destfolder_, destfolder);
2890 imap_gen_send(session, "UID COPY %s %s", seq_set, destfolder_);
2892 reply = g_ptr_array_new();
2894 ok = imap_cmd_ok(session, reply);
2895 if (ok != IMAP_SUCCESS)
2896 log_warning(_("can't copy %s to %s\n"), seq_set, destfolder_);
2900 - split IMAPSets into uids
2901 - g_relation_insert(uid_mapping, olduid, newuid);
2903 else if (imap_has_capability(session, "UIDPLUS") && reply->len > 0)
2904 if ((okmsginfo = g_ptr_array_index(reply, reply->len - 1)) != NULL &&
2905 sscanf(okmsginfo, "%*u OK [COPYUID %*u %u %u]", &olduid, &newuid) == 2 &&
2909 ptr_array_free_strings(reply);
2910 g_ptr_array_free(reply, TRUE);
2914 gint imap_cmd_envelope(IMAPSession *session, IMAPSet set)
2916 static GString *header_fields = NULL;
2918 if (header_fields == NULL) {
2919 const HeaderEntry *headers, *elem;
2921 headers = procheader_get_headernames(FALSE);
2922 header_fields = g_string_new("");
2924 for (elem = headers; elem->name != NULL; ++elem) {
2925 gint namelen = strlen(elem->name);
2927 /* Header fields ending with space are not rfc822 headers */
2928 if (elem->name[namelen - 1] == ' ')
2931 /* strip : at the of header field */
2932 if(elem->name[namelen - 1] == ':')
2938 g_string_sprintfa(header_fields, "%s%.*s",
2939 header_fields->str[0] != '\0' ? " " : "",
2940 namelen, elem->name);
2945 (session, "UID FETCH %s (UID FLAGS RFC822.SIZE BODY.PEEK[HEADER.FIELDS (%s)])",
2946 set, header_fields->str);
2948 return IMAP_SUCCESS;
2951 static gint imap_cmd_store(IMAPSession *session, IMAPSet seq_set,
2956 imap_gen_send(session, "UID STORE %s %s", seq_set, sub_cmd);
2958 if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS) {
2959 log_warning(_("error while imap command: STORE %s %s\n"),
2964 return IMAP_SUCCESS;
2967 static gint imap_cmd_expunge(IMAPSession *session)
2971 imap_gen_send(session, "EXPUNGE");
2972 if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS) {
2973 log_warning(_("error while imap command: EXPUNGE\n"));
2977 return IMAP_SUCCESS;
2980 static gint imap_cmd_close(IMAPSession *session)
2984 imap_gen_send(session, "CLOSE");
2985 if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS)
2986 log_warning(_("error while imap command: CLOSE\n"));
2991 static gint imap_cmd_ok(IMAPSession *session, GPtrArray *argbuf)
2993 gint ok = IMAP_SUCCESS;
2998 while ((ok = imap_gen_recv(session, &buf))
3000 // make sure data is long enough for any substring of buf
3001 data = alloca(strlen(buf) + 1);
3003 // untagged line read
3004 if (buf[0] == '*' && buf[1] == ' ') {
3007 g_ptr_array_add(argbuf, g_strdup(buf + 2));
3009 if (sscanf(buf + 2, "%d %s", &num, data) >= 2) {
3010 if (!strcmp(data, "EXISTS")) {
3011 session->exists = num;
3012 session->folder_content_changed = TRUE;
3015 if(!strcmp(data, "EXPUNGE")) {
3017 session->folder_content_changed = TRUE;
3020 // tagged line with correct tag and OK response found
3021 } else if ((sscanf(buf, "%d %s", &cmd_num, data) >= 2) &&
3022 (cmd_num == session->cmd_count) &&
3023 !strcmp(data, "OK")) {
3025 g_ptr_array_add(argbuf, g_strdup(buf));
3039 static void imap_gen_send(IMAPSession *session, const gchar *format, ...)
3046 va_start(args, format);
3047 tmp = g_strdup_vprintf(format, args);
3050 session->cmd_count++;
3052 buf = g_strdup_printf("%d %s\r\n", session->cmd_count, tmp);
3053 if (!strncasecmp(tmp, "LOGIN ", 6) && (p = strchr(tmp + 6, ' '))) {
3055 log_print("IMAP4> %d %s ********\n", session->cmd_count, tmp);
3057 log_print("IMAP4> %d %s\n", session->cmd_count, tmp);
3059 sock_write_all(SESSION(session)->sock, buf, strlen(buf));
3064 static gint imap_gen_recv(IMAPSession *session, gchar **ret)
3066 if ((*ret = sock_getline(SESSION(session)->sock)) == NULL)
3071 log_print("IMAP4< %s\n", *ret);
3073 return IMAP_SUCCESS;
3077 /* misc utility functions */
3079 static gchar *strchr_cpy(const gchar *src, gchar ch, gchar *dest, gint len)
3084 tmp = strchr(src, ch);
3088 memcpy(dest, src, MIN(tmp - src, len - 1));
3089 dest[MIN(tmp - src, len - 1)] = '\0';
3094 static gchar *get_quoted(const gchar *src, gchar ch, gchar *dest, gint len)
3096 const gchar *p = src;
3099 g_return_val_if_fail(*p == ch, NULL);
3104 while (*p != '\0' && *p != ch) {
3106 if (*p == '\\' && *(p + 1) != '\0')
3115 return (gchar *)(*p == ch ? p + 1 : p);
3118 static gchar *search_array_contain_str(GPtrArray *array, const gchar *str)
3122 for (i = 0; i < array->len; i++) {
3125 tmp = g_ptr_array_index(array, i);
3126 if (strstr(tmp, str) != NULL)
3133 static gchar *search_array_str(GPtrArray *array, const gchar *str)
3140 for (i = 0; i < array->len; i++) {
3143 tmp = g_ptr_array_index(array, i);
3144 if (!strncmp(tmp, str, len))
3151 static void imap_path_separator_subst(gchar *str, gchar separator)
3154 gboolean in_escape = FALSE;
3156 if (!separator || separator == '/') return;
3158 for (p = str; *p != '\0'; p++) {
3159 if (*p == '/' && !in_escape)
3161 else if (*p == '&' && *(p + 1) != '-' && !in_escape)
3163 else if (*p == '-' && in_escape)
3168 static gchar *imap_modified_utf7_to_locale(const gchar *mutf7_str)
3171 const gchar *from_p;
3174 to = g_malloc(strlen(mutf7_str) + 1);
3177 for (from_p = mutf7_str; *from_p != '\0'; from_p++) {
3178 if (*from_p == '&' && *(from_p + 1) == '-') {
3188 static iconv_t cd = (iconv_t)-1;
3189 static gboolean iconv_ok = TRUE;
3192 size_t norm_utf7_len;
3194 gchar *to_str, *to_p;
3196 gboolean in_escape = FALSE;
3198 if (!iconv_ok) return g_strdup(mutf7_str);
3200 if (cd == (iconv_t)-1) {
3201 cd = iconv_open(conv_get_current_charset_str(), "UTF-7");
3202 if (cd == (iconv_t)-1) {
3203 g_warning("iconv cannot convert UTF-7 to %s\n",
3204 conv_get_current_charset_str());
3206 return g_strdup(mutf7_str);
3210 norm_utf7 = g_string_new(NULL);
3212 for (p = mutf7_str; *p != '\0'; p++) {
3213 /* replace: '&' -> '+',
3215 escaped ',' -> '/' */
3216 if (!in_escape && *p == '&') {
3217 if (*(p + 1) != '-') {
3218 g_string_append_c(norm_utf7, '+');
3221 g_string_append_c(norm_utf7, '&');
3224 } else if (in_escape && *p == ',') {
3225 g_string_append_c(norm_utf7, '/');
3226 } else if (in_escape && *p == '-') {
3227 g_string_append_c(norm_utf7, '-');
3230 g_string_append_c(norm_utf7, *p);
3234 norm_utf7_p = norm_utf7->str;
3235 norm_utf7_len = norm_utf7->len;
3236 to_len = strlen(mutf7_str) * 5;
3237 to_p = to_str = g_malloc(to_len + 1);
3239 if (iconv(cd, (ICONV_CONST gchar **)&norm_utf7_p, &norm_utf7_len,
3240 &to_p, &to_len) == -1) {
3241 g_warning(_("iconv cannot convert UTF-7 to %s\n"),
3242 conv_get_current_charset_str());
3243 g_string_free(norm_utf7, TRUE);
3245 return g_strdup(mutf7_str);
3248 /* second iconv() call for flushing */
3249 iconv(cd, NULL, NULL, &to_p, &to_len);
3250 g_string_free(norm_utf7, TRUE);
3254 #endif /* !HAVE_ICONV */
3257 static gchar *imap_locale_to_modified_utf7(const gchar *from)
3260 const gchar *from_p;
3263 to = g_malloc(strlen(from) * 2 + 1);
3266 for (from_p = from; *from_p != '\0'; from_p++) {
3267 if (*from_p == '&') {
3277 static iconv_t cd = (iconv_t)-1;
3278 static gboolean iconv_ok = TRUE;
3279 gchar *norm_utf7, *norm_utf7_p;
3280 size_t from_len, norm_utf7_len;
3282 gchar *from_tmp, *to, *p;
3283 gboolean in_escape = FALSE;
3285 if (!iconv_ok) return g_strdup(from);
3287 if (cd == (iconv_t)-1) {
3288 cd = iconv_open("UTF-7", conv_get_current_charset_str());
3289 if (cd == (iconv_t)-1) {
3290 g_warning("iconv cannot convert %s to UTF-7\n",
3291 conv_get_current_charset_str());
3293 return g_strdup(from);
3297 Xstrdup_a(from_tmp, from, return g_strdup(from));
3298 from_len = strlen(from);
3299 norm_utf7_len = from_len * 5;
3300 Xalloca(norm_utf7, norm_utf7_len + 1, return g_strdup(from));
3301 norm_utf7_p = norm_utf7;
3303 #define IS_PRINT(ch) (isprint(ch) && isascii(ch))
3305 while (from_len > 0) {
3306 if (*from_tmp == '+') {
3307 *norm_utf7_p++ = '+';
3308 *norm_utf7_p++ = '-';
3312 } else if (IS_PRINT(*from_tmp)) {
3313 /* printable ascii char */
3314 *norm_utf7_p = *from_tmp;
3320 size_t mb_len = 0, conv_len = 0;
3322 /* unprintable char: convert to UTF-7 */
3324 while (!IS_PRINT(*p) && conv_len < from_len) {
3325 mb_len = mblen(p, MB_LEN_MAX);
3327 g_warning("wrong multibyte sequence\n");
3328 return g_strdup(from);
3334 from_len -= conv_len;
3335 if (iconv(cd, (ICONV_CONST gchar **)&from_tmp,
3337 &norm_utf7_p, &norm_utf7_len) == -1) {
3338 g_warning("iconv cannot convert %s to UTF-7\n",
3339 conv_get_current_charset_str());
3340 return g_strdup(from);
3343 /* second iconv() call for flushing */
3344 iconv(cd, NULL, NULL, &norm_utf7_p, &norm_utf7_len);
3350 *norm_utf7_p = '\0';
3351 to_str = g_string_new(NULL);
3352 for (p = norm_utf7; p < norm_utf7_p; p++) {
3353 /* replace: '&' -> "&-",
3356 BASE64 '/' -> ',' */
3357 if (!in_escape && *p == '&') {
3358 g_string_append(to_str, "&-");
3359 } else if (!in_escape && *p == '+') {
3360 if (*(p + 1) == '-') {
3361 g_string_append_c(to_str, '+');
3364 g_string_append_c(to_str, '&');
3367 } else if (in_escape && *p == '/') {
3368 g_string_append_c(to_str, ',');
3369 } else if (in_escape && *p == '-') {
3370 g_string_append_c(to_str, '-');
3373 g_string_append_c(to_str, *p);
3379 g_string_append_c(to_str, '-');
3383 g_string_free(to_str, FALSE);
3386 #endif /* !HAVE_ICONV */
3389 static GSList *imap_get_seq_set_from_numlist(MsgNumberList *numlist)
3392 GSList *sorted_list, *cur;
3393 guint first, last, next;
3395 GSList *ret_list = NULL;
3397 if (numlist == NULL)
3400 str = g_string_sized_new(256);
3402 sorted_list = g_slist_copy(numlist);
3403 sorted_list = g_slist_sort(sorted_list, g_int_compare);
3405 first = GPOINTER_TO_INT(sorted_list->data);
3407 for (cur = sorted_list; cur != NULL; cur = g_slist_next(cur)) {
3408 last = GPOINTER_TO_INT(cur->data);
3410 next = GPOINTER_TO_INT(cur->next->data);
3414 if (last + 1 != next || next == 0) {
3416 g_string_append_c(str, ',');
3418 g_string_sprintfa(str, "%u", first);
3420 g_string_sprintfa(str, "%u:%u", first, last);
3424 if (str->len > IMAP_CMD_LIMIT) {
3425 ret_str = g_strdup(str->str);
3426 ret_list = g_slist_append(ret_list, ret_str);
3427 g_string_truncate(str, 0);
3433 ret_str = g_strdup(str->str);
3434 ret_list = g_slist_append(ret_list, ret_str);
3437 g_slist_free(sorted_list);
3438 g_string_free(str, TRUE);
3443 static GSList *imap_get_seq_set_from_msglist(MsgInfoList *msglist)
3445 MsgNumberList *numlist = NULL;
3449 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
3450 MsgInfo *msginfo = (MsgInfo *) cur->data;
3452 numlist = g_slist_append(numlist, GINT_TO_POINTER(msginfo->msgnum));
3454 seq_list = imap_get_seq_set_from_numlist(numlist);
3455 g_slist_free(numlist);
3460 static void imap_seq_set_free(GSList *seq_list)
3462 slist_free_strings(seq_list);
3463 g_slist_free(seq_list);
3467 static gboolean imap_rename_folder_func(GNode *node, gpointer data)
3469 FolderItem *item = node->data;
3470 gchar **paths = data;
3471 const gchar *oldpath = paths[0];
3472 const gchar *newpath = paths[1];
3474 gchar *new_itempath;
3477 oldpathlen = strlen(oldpath);
3478 if (strncmp(oldpath, item->path, oldpathlen) != 0) {
3479 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
3483 base = item->path + oldpathlen;
3484 while (*base == G_DIR_SEPARATOR) base++;
3486 new_itempath = g_strdup(newpath);
3488 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
3491 item->path = new_itempath;
3496 static gint get_list_of_uids(Folder *folder, IMAPFolderItem *item, GSList **msgnum_list)
3498 gint ok, nummsgs = 0, lastuid_old;
3499 IMAPSession *session;
3500 GSList *uidlist, *elem;
3503 session = imap_session_get(folder);
3504 g_return_val_if_fail(session != NULL, -1);
3506 ok = imap_select(session, IMAP_FOLDER(folder), item->item.path,
3507 NULL, NULL, NULL, NULL);
3508 if (ok != IMAP_SUCCESS)
3511 cmd_buf = g_strdup_printf("UID %d:*", item->lastuid + 1);
3512 ok = imap_cmd_search(session, cmd_buf, &uidlist);
3515 if (ok == IMAP_SOCKET) {
3516 session_destroy((Session *)session);
3517 ((RemoteFolder *)folder)->session = NULL;
3521 if (ok != IMAP_SUCCESS) {
3525 argbuf = g_ptr_array_new();
3527 cmd_buf = g_strdup_printf("UID FETCH %d:* (UID)", item->lastuid + 1);
3528 imap_gen_send(session, cmd_buf);
3530 ok = imap_cmd_ok(session, argbuf);
3531 if (ok != IMAP_SUCCESS) {
3532 ptr_array_free_strings(argbuf);
3533 g_ptr_array_free(argbuf, TRUE);
3537 for(i = 0; i < argbuf->len; i++) {
3540 if((ret = sscanf(g_ptr_array_index(argbuf, i),
3541 "%*d FETCH (UID %d)", &msgnum)) == 1)
3542 uidlist = g_slist_prepend(uidlist, GINT_TO_POINTER(msgnum));
3544 ptr_array_free_strings(argbuf);
3545 g_ptr_array_free(argbuf, TRUE);
3548 lastuid_old = item->lastuid;
3549 *msgnum_list = g_slist_copy(item->uid_list);
3550 nummsgs = g_slist_length(*msgnum_list);
3551 debug_print("Got %d uids from cache\n", g_slist_length(item->uid_list));
3553 for (elem = uidlist; elem != NULL; elem = g_slist_next(elem)) {
3556 msgnum = GPOINTER_TO_INT(elem->data);
3557 if (msgnum > lastuid_old) {
3558 *msgnum_list = g_slist_prepend(*msgnum_list, GINT_TO_POINTER(msgnum));
3559 item->uid_list = g_slist_prepend(item->uid_list, GINT_TO_POINTER(msgnum));
3562 if(msgnum > item->lastuid)
3563 item->lastuid = msgnum;
3566 g_slist_free(uidlist);
3571 gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list)
3573 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3574 IMAPSession *session;
3575 gint ok, nummsgs = 0, exists, recent, unseen, uid_val, uid_next;
3578 gboolean selected_folder;
3580 g_return_val_if_fail(folder != NULL, -1);
3581 g_return_val_if_fail(item != NULL, -1);
3582 g_return_val_if_fail(item->item.path != NULL, -1);
3583 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3584 g_return_val_if_fail(folder->account != NULL, -1);
3586 session = imap_session_get(folder);
3587 g_return_val_if_fail(session != NULL, -1);
3589 selected_folder = (session->mbox != NULL) &&
3590 (!strcmp(session->mbox, item->item.path));
3591 if (selected_folder) {
3592 ok = imap_cmd_noop(session);
3593 if (ok != IMAP_SUCCESS)
3595 exists = session->exists;
3597 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
3598 &exists, &recent, &uid_next, &uid_val, &unseen);
3599 if (ok != IMAP_SUCCESS)
3603 /* If old uid_next matches new uid_next we can be sure no message
3604 was added to the folder */
3605 if (( selected_folder && !session->folder_content_changed) ||
3606 (!selected_folder && uid_next == item->uid_next)) {
3607 nummsgs = g_slist_length(item->uid_list);
3609 /* If number of messages is still the same we
3610 know our caches message numbers are still valid,
3611 otherwise if the number of messages has decrease
3612 we discard our cache to start a new scan to find
3613 out which numbers have been removed */
3614 if (exists == nummsgs) {
3615 *msgnum_list = g_slist_copy(item->uid_list);
3617 } else if (exists < nummsgs) {
3618 debug_print("Freeing imap uid cache");
3620 g_slist_free(item->uid_list);
3621 item->uid_list = NULL;
3624 if (!selected_folder)
3625 item->uid_next = uid_next;
3628 *msgnum_list = NULL;
3632 nummsgs = get_list_of_uids(folder, item, &uidlist);
3634 if (nummsgs != exists) {
3635 /* Cache contains more messages then folder, we have cached
3636 an old UID of a message that was removed and new messages
3637 have been added too, otherwise the uid_next check would
3639 debug_print("Freeing imap uid cache");
3641 g_slist_free(item->uid_list);
3642 item->uid_list = NULL;
3644 g_slist_free(*msgnum_list);
3646 nummsgs = get_list_of_uids(folder, item, &uidlist);
3649 *msgnum_list = uidlist;
3651 dir = folder_item_get_path((FolderItem *)item);
3652 debug_print("removing old messages from %s\n", dir);
3653 remove_numbered_files_not_in_list(dir, *msgnum_list);
3659 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item)
3664 flags.perm_flags = MSG_NEW|MSG_UNREAD;
3665 flags.tmp_flags = 0;
3667 g_return_val_if_fail(item != NULL, NULL);
3668 g_return_val_if_fail(file != NULL, NULL);
3670 if (item->stype == F_QUEUE) {
3671 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
3672 } else if (item->stype == F_DRAFT) {
3673 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
3676 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3677 if (!msginfo) return NULL;
3679 msginfo->folder = item;
3684 GSList *imap_get_msginfos(Folder *folder, FolderItem *item, GSList *msgnum_list)
3686 IMAPSession *session;
3687 MsgInfoList *ret = NULL;
3690 g_return_val_if_fail(folder != NULL, NULL);
3691 g_return_val_if_fail(item != NULL, NULL);
3692 g_return_val_if_fail(msgnum_list != NULL, NULL);
3694 session = imap_session_get(folder);
3695 g_return_val_if_fail(session != NULL, NULL);
3697 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3698 NULL, NULL, NULL, NULL);
3699 if (ok != IMAP_SUCCESS)
3702 if (!(item->stype == F_QUEUE || item->stype == F_DRAFT)) {
3703 ret = g_slist_concat(ret,
3704 imap_get_uncached_messages(
3705 session, item, msgnum_list));
3707 MsgNumberList *sorted_list, *elem;
3708 gint startnum, lastnum;
3710 sorted_list = g_slist_sort(g_slist_copy(msgnum_list), g_int_compare);
3712 startnum = lastnum = GPOINTER_TO_INT(sorted_list->data);
3714 for (elem = sorted_list;; elem = g_slist_next(elem)) {
3718 num = GPOINTER_TO_INT(elem->data);
3720 if (num > lastnum + 1 || elem == NULL) {
3722 for (i = startnum; i <= lastnum; ++i) {
3725 file = imap_fetch_msg(folder, item, i);
3727 MsgInfo *msginfo = imap_parse_msg(file, item);
3728 if (msginfo != NULL) {
3729 msginfo->msgnum = i;
3730 ret = g_slist_append(ret, msginfo);
3744 g_slist_free(sorted_list);
3750 MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid)
3752 MsgInfo *msginfo = NULL;
3753 MsgInfoList *msginfolist;
3754 MsgNumberList numlist;
3756 numlist.next = NULL;
3757 numlist.data = GINT_TO_POINTER(uid);
3759 msginfolist = imap_get_msginfos(folder, item, &numlist);
3760 if (msginfolist != NULL) {
3761 msginfo = msginfolist->data;
3762 g_slist_free(msginfolist);
3768 gboolean imap_check_msgnum_validity(Folder *folder, FolderItem *_item)
3770 IMAPSession *session;
3771 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3772 gint ok, exists = 0, recent = 0, unseen = 0;
3773 guint32 uid_next, uid_validity = 0;
3775 g_return_val_if_fail(folder != NULL, FALSE);
3776 g_return_val_if_fail(item != NULL, FALSE);
3777 g_return_val_if_fail(item->item.folder != NULL, FALSE);
3778 g_return_val_if_fail(FOLDER_CLASS(item->item.folder) == &imap_class, FALSE);
3780 session = imap_session_get(folder);
3781 g_return_val_if_fail(session != NULL, FALSE);
3783 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
3784 &exists, &recent, &uid_next, &uid_validity, &unseen);
3785 if (ok != IMAP_SUCCESS)
3788 if(item->item.mtime == uid_validity)
3791 debug_print("Freeing imap uid cache\n");
3793 g_slist_free(item->uid_list);
3794 item->uid_list = NULL;
3796 item->item.mtime = uid_validity;
3798 imap_delete_all_cached_messages((FolderItem *)item);
3803 void imap_change_flags(Folder *folder, FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
3805 IMAPSession *session;
3806 IMAPFlags flags_set = 0, flags_unset = 0;
3807 gint ok = IMAP_SUCCESS;
3808 MsgNumberList numlist;
3810 g_return_if_fail(folder != NULL);
3811 g_return_if_fail(folder->klass == &imap_class);
3812 g_return_if_fail(item != NULL);
3813 g_return_if_fail(item->folder == folder);
3814 g_return_if_fail(msginfo != NULL);
3815 g_return_if_fail(msginfo->folder == item);
3817 session = imap_session_get(folder);
3818 if (!session) return;
3820 if ((ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
3821 NULL, NULL, NULL, NULL)) != IMAP_SUCCESS)
3824 if (!MSG_IS_MARKED(msginfo->flags) && (newflags & MSG_MARKED))
3825 flags_set |= IMAP_FLAG_FLAGGED;
3826 if ( MSG_IS_MARKED(msginfo->flags) && !(newflags & MSG_MARKED))
3827 flags_unset |= IMAP_FLAG_FLAGGED;
3829 if (!MSG_IS_UNREAD(msginfo->flags) && (newflags & MSG_UNREAD))
3830 flags_unset |= IMAP_FLAG_SEEN;
3831 if ( MSG_IS_UNREAD(msginfo->flags) && !(newflags & MSG_UNREAD))
3832 flags_set |= IMAP_FLAG_SEEN;
3834 if (!MSG_IS_REPLIED(msginfo->flags) && (newflags & MSG_REPLIED))
3835 flags_set |= IMAP_FLAG_ANSWERED;
3836 if ( MSG_IS_REPLIED(msginfo->flags) && !(newflags & MSG_REPLIED))
3837 flags_set |= IMAP_FLAG_ANSWERED;
3839 numlist.next = NULL;
3840 numlist.data = GINT_TO_POINTER(msginfo->msgnum);
3843 ok = imap_set_message_flags(session, &numlist, flags_set, TRUE);
3844 if (ok != IMAP_SUCCESS) return;
3848 ok = imap_set_message_flags(session, &numlist, flags_unset, FALSE);
3849 if (ok != IMAP_SUCCESS) return;
3852 msginfo->flags.perm_flags = newflags;