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 gint 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 gboolean *old_uids_valid);
408 static GSList *imap_get_msginfos (Folder *folder,
410 GSList *msgnum_list);
411 static MsgInfo *imap_get_msginfo (Folder *folder,
414 static gboolean imap_scan_required (Folder *folder,
416 static void imap_change_flags (Folder *folder,
419 MsgPermFlags newflags);
420 static gchar *imap_folder_get_path (Folder *folder);
421 static gchar *imap_item_get_path (Folder *folder,
424 FolderClass imap_class =
430 /* Folder functions */
436 /* FolderItem functions */
437 imap_folder_item_new,
438 imap_folder_item_destroy,
450 /* Message functions */
464 FolderClass *imap_get_class(void)
469 Folder *imap_folder_new(const gchar *name, const gchar *path)
473 folder = (Folder *)g_new0(IMAPFolder, 1);
474 folder->klass = &imap_class;
475 imap_folder_init(folder, name, path);
480 void imap_folder_destroy(Folder *folder)
484 dir = imap_folder_get_path(folder);
485 if (is_dir_exist(dir))
486 remove_dir_recursive(dir);
489 folder_remote_folder_destroy(REMOTE_FOLDER(folder));
492 static void imap_folder_init(Folder *folder, const gchar *name,
495 folder_remote_folder_init((Folder *)folder, name, path);
498 static FolderItem *imap_folder_item_new(Folder *folder)
500 IMAPFolderItem *item;
502 item = g_new0(IMAPFolderItem, 1);
505 item->uid_list = NULL;
507 return (FolderItem *)item;
510 static void imap_folder_item_destroy(Folder *folder, FolderItem *_item)
512 IMAPFolderItem *item = (IMAPFolderItem *)_item;
514 g_return_if_fail(item != NULL);
515 g_slist_free(item->uid_list);
520 static gboolean imap_reset_uid_lists_func(GNode *node, gpointer data)
522 IMAPFolderItem *item = (IMAPFolderItem *)node->data;
526 g_slist_free(item->uid_list);
527 item->uid_list = NULL;
532 static void imap_reset_uid_lists(Folder *folder)
534 if(folder->node == NULL)
537 /* Destroy all uid lists and rest last uid */
538 g_node_traverse(folder->node, G_IN_ORDER, G_TRAVERSE_ALL, -1, imap_reset_uid_lists_func, NULL);
541 /* Send CAPABILITY, and examine the server's response to see whether this
542 * connection is pre-authenticated or not and build a list of CAPABILITIES. */
543 static gint imap_greeting(IMAPSession *session)
548 imap_gen_send(session, "CAPABILITY");
550 argbuf = g_ptr_array_new();
552 if (imap_cmd_ok(session, argbuf) != IMAP_SUCCESS ||
553 ((capstr = search_array_str(argbuf, "CAPABILITY ")) == NULL)) {
554 ptr_array_free_strings(argbuf);
555 g_ptr_array_free(argbuf, TRUE);
559 session->authenticated = search_array_str(argbuf, "PREAUTH") != NULL;
561 capstr += strlen("CAPABILITY ");
563 IMAP_SESSION(session)->capability = g_strsplit(capstr, " ", 0);
565 ptr_array_free_strings(argbuf);
566 g_ptr_array_free(argbuf, TRUE);
568 if (imap_has_capability(session, "UIDPLUS"))
569 session->uidplus = TRUE;
574 static gint imap_auth(IMAPSession *session, const gchar *user, const gchar *pass,
579 if (type == 0 || type == IMAP_AUTH_LOGIN)
580 ok = imap_cmd_login(session, user, pass);
582 ok = imap_cmd_authenticate(session, user, pass, type);
584 if (ok == IMAP_SUCCESS)
585 session->authenticated = TRUE;
590 static IMAPSession *imap_session_get(Folder *folder)
592 RemoteFolder *rfolder = REMOTE_FOLDER(folder);
593 IMAPSession *session = NULL;
595 g_return_val_if_fail(folder != NULL, NULL);
596 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, NULL);
597 g_return_val_if_fail(folder->account != NULL, NULL);
599 /* Make sure we have a session */
600 if (rfolder->session != NULL) {
601 session = IMAP_SESSION(rfolder->session);
603 imap_reset_uid_lists(folder);
604 session = imap_session_new(folder->account);
609 if (SESSION(session)->sock->state == CONN_DISCONNECTED) {
610 debug_print("IMAP server disconnected\n");
611 session_destroy(SESSION(session));
612 imap_reset_uid_lists(folder);
613 session = imap_session_new(folder->account);
616 /* Make sure session is authenticated */
617 if (!IMAP_SESSION(session)->authenticated)
618 imap_session_authenticate(IMAP_SESSION(session), folder->account);
619 if (!IMAP_SESSION(session)->authenticated) {
620 session_destroy(SESSION(session));
621 rfolder->session = NULL;
625 /* Make sure we have parsed the IMAP namespace */
626 imap_parse_namespace(IMAP_SESSION(session),
627 IMAP_FOLDER(folder));
629 /* I think the point of this code is to avoid sending a
630 * keepalive if we've used the session recently and therefore
631 * think it's still alive. Unfortunately, most of the code
632 * does not yet check for errors on the socket, and so if the
633 * connection drops we don't notice until the timeout expires.
634 * A better solution than sending a NOOP every time would be
635 * for every command to be prepared to retry until it is
636 * successfully sent. -- mbp */
637 if (time(NULL) - session->last_access_time > SESSION_TIMEOUT) {
638 /* verify that the session is still alive */
639 if (imap_cmd_noop(session) != IMAP_SUCCESS) {
640 /* Check if this is the first try to establish a
641 connection, if yes we don't try to reconnect */
642 if (rfolder->session == NULL) {
643 log_warning(_("Connecting %s failed"),
644 folder->account->recv_server);
645 session_destroy(SESSION(session));
648 log_warning(_("IMAP4 connection to %s has been"
649 " disconnected. Reconnecting...\n"),
650 folder->account->recv_server);
651 session_destroy(SESSION(session));
652 /* Clear folders session to make imap_session_get create
653 a new session, because of rfolder->session == NULL
654 it will not try to reconnect again and so avoid an
656 rfolder->session = NULL;
657 session = imap_session_get(folder);
662 rfolder->session = SESSION(session);
664 session->last_access_time = time(NULL);
666 return IMAP_SESSION(session);
669 IMAPSession *imap_session_new(const PrefsAccount *account)
671 IMAPSession *session;
676 /* FIXME: IMAP over SSL only... */
679 port = account->set_imapport ? account->imapport
680 : account->ssl_imap == SSL_TUNNEL ? IMAPS_PORT : IMAP4_PORT;
681 ssl_type = account->ssl_imap;
683 port = account->set_imapport ? account->imapport
687 if (account->set_tunnelcmd) {
688 log_message(_("creating tunneled IMAP4 connection\n"));
690 if ((imap_sock = imap_open_tunnel(account->recv_server,
694 if ((imap_sock = imap_open_tunnel(account->recv_server,
695 account->tunnelcmd)) == NULL)
699 g_return_val_if_fail(account->recv_server != NULL, NULL);
701 log_message(_("creating IMAP4 connection to %s:%d ...\n"),
702 account->recv_server, port);
705 if ((imap_sock = imap_open(account->recv_server, port,
708 if ((imap_sock = imap_open(account->recv_server, port)) == NULL)
713 session = g_new0(IMAPSession, 1);
714 session_init(SESSION(session));
715 SESSION(session)->type = SESSION_IMAP;
716 SESSION(session)->server = g_strdup(account->recv_server);
717 SESSION(session)->sock = imap_sock;
719 SESSION(session)->destroy = imap_session_destroy;
721 session->capability = NULL;
723 session->authenticated = FALSE;
724 session->mbox = NULL;
725 session->cmd_count = 0;
727 /* Only need to log in if the connection was not PREAUTH */
728 if (imap_greeting(session) != IMAP_SUCCESS) {
729 session_destroy(SESSION(session));
734 if (account->ssl_imap == SSL_STARTTLS &&
735 imap_has_capability(session, "STARTTLS")) {
738 ok = imap_cmd_starttls(session);
739 if (ok != IMAP_SUCCESS) {
740 log_warning(_("Can't start TLS session.\n"));
741 session_destroy(SESSION(session));
744 if (!ssl_init_socket_with_method(SESSION(session)->sock, SSL_METHOD_TLSv1)) {
745 session_destroy(SESSION(session));
749 imap_free_capabilities(session);
750 session->authenticated = FALSE;
751 session->uidplus = FALSE;
752 session->cmd_count = 1;
754 if (imap_greeting(session) != IMAP_SUCCESS) {
755 session_destroy(SESSION(session));
760 log_message("IMAP connection is %s-authenticated\n",
761 (session->authenticated) ? "pre" : "un");
766 void imap_session_authenticate(IMAPSession *session, const PrefsAccount *account)
770 g_return_if_fail(account->userid != NULL);
772 pass = account->passwd;
775 tmp_pass = input_dialog_query_password(account->recv_server, account->userid);
778 Xstrdup_a(pass, tmp_pass, {g_free(tmp_pass); return;});
782 if (imap_auth(session, account->userid, pass, account->imap_auth_type) != IMAP_SUCCESS) {
783 imap_cmd_logout(session);
787 session->authenticated = TRUE;
790 void imap_session_destroy(Session *session)
792 imap_free_capabilities(IMAP_SESSION(session));
793 g_free(IMAP_SESSION(session)->mbox);
794 sock_close(session->sock);
795 session->sock = NULL;
798 gchar *imap_fetch_msg(Folder *folder, FolderItem *item, gint uid)
800 gchar *path, *filename;
801 IMAPSession *session;
804 g_return_val_if_fail(folder != NULL, NULL);
805 g_return_val_if_fail(item != NULL, NULL);
807 path = folder_item_get_path(item);
808 if (!is_dir_exist(path))
810 filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(uid), NULL);
813 if (is_file_exist(filename)) {
814 debug_print("message %d has been already cached.\n", uid);
818 session = imap_session_get(folder);
824 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
825 NULL, NULL, NULL, NULL);
826 if (ok != IMAP_SUCCESS) {
827 g_warning("can't select mailbox %s\n", item->path);
832 debug_print("getting message %d...\n", uid);
833 ok = imap_cmd_fetch(session, (guint32)uid, filename);
835 if (ok != IMAP_SUCCESS) {
836 g_warning("can't fetch message %d\n", uid);
844 gint imap_add_msg(Folder *folder, FolderItem *dest, const gchar *file, MsgFlags *flags)
848 MsgFileInfo fileinfo;
850 g_return_val_if_fail(file != NULL, -1);
852 fileinfo.msginfo = NULL;
853 fileinfo.file = (gchar *)file;
854 fileinfo.flags = flags;
855 file_list.data = &fileinfo;
856 file_list.next = NULL;
858 ret = imap_add_msgs(folder, dest, &file_list, NULL);
862 gint imap_add_msgs(Folder *folder, FolderItem *dest, GSList *file_list,
866 IMAPSession *session;
867 guint32 last_uid = 0;
869 MsgFileInfo *fileinfo;
872 g_return_val_if_fail(folder != NULL, -1);
873 g_return_val_if_fail(dest != NULL, -1);
874 g_return_val_if_fail(file_list != NULL, -1);
876 session = imap_session_get(folder);
877 if (!session) return -1;
879 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
881 for (cur = file_list; cur != NULL; cur = cur->next) {
882 IMAPFlags iflags = 0;
885 fileinfo = (MsgFileInfo *)cur->data;
887 if (fileinfo->flags) {
888 if (MSG_IS_MARKED(*fileinfo->flags))
889 iflags |= IMAP_FLAG_FLAGGED;
890 if (MSG_IS_REPLIED(*fileinfo->flags))
891 iflags |= IMAP_FLAG_ANSWERED;
892 if (!MSG_IS_UNREAD(*fileinfo->flags))
893 iflags |= IMAP_FLAG_SEEN;
896 if (dest->stype == F_OUTBOX ||
897 dest->stype == F_QUEUE ||
898 dest->stype == F_DRAFT ||
899 dest->stype == F_TRASH)
900 iflags |= IMAP_FLAG_SEEN;
902 ok = imap_cmd_append(session, destdir, fileinfo->file, iflags,
905 if (ok != IMAP_SUCCESS) {
906 g_warning("can't append message %s\n", fileinfo->file);
911 if (relation != NULL)
912 g_relation_insert(relation, fileinfo->msginfo != NULL ?
913 (gpointer) fileinfo->msginfo : (gpointer) fileinfo,
914 GINT_TO_POINTER(dest->last_num + 1));
915 if (last_uid < new_uid)
924 static gint imap_do_copy_msgs(Folder *folder, FolderItem *dest,
925 MsgInfoList *msglist, GRelation *relation)
929 GSList *seq_list, *cur;
931 IMAPSession *session;
932 gint ok = IMAP_SUCCESS;
933 GRelation *uid_mapping;
936 g_return_val_if_fail(folder != NULL, -1);
937 g_return_val_if_fail(dest != NULL, -1);
938 g_return_val_if_fail(msglist != NULL, -1);
940 session = imap_session_get(folder);
941 if (!session) return -1;
943 msginfo = (MsgInfo *)msglist->data;
945 src = msginfo->folder;
947 g_warning("the src folder is identical to the dest.\n");
951 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
952 NULL, NULL, NULL, NULL);
953 if (ok != IMAP_SUCCESS)
956 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
957 seq_list = imap_get_seq_set_from_msglist(msglist);
958 uid_mapping = g_relation_new(2);
959 g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
961 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
962 gchar *seq_set = (gchar *)cur->data;
964 debug_print("Copying message %s%c[%s] to %s ...\n",
965 src->path, G_DIR_SEPARATOR,
968 ok = imap_cmd_copy(session, seq_set, destdir, uid_mapping);
969 if (ok != IMAP_SUCCESS) {
970 g_relation_destroy(uid_mapping);
971 imap_seq_set_free(seq_list);
976 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
977 MsgInfo *msginfo = (MsgInfo *)cur->data;
980 tuples = g_relation_select(uid_mapping,
981 GINT_TO_POINTER(msginfo->msgnum),
983 if (tuples->len > 0) {
984 gint num = GPOINTER_TO_INT(g_tuples_index(tuples, 0, 1));
985 g_relation_insert(relation, msginfo,
986 GPOINTER_TO_INT(num));
990 g_relation_insert(relation, msginfo,
992 g_tuples_destroy(tuples);
995 imap_seq_set_free(seq_list);
999 if (ok == IMAP_SUCCESS)
1005 gint imap_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1009 g_return_val_if_fail(msginfo != NULL, -1);
1011 msglist.data = msginfo;
1012 msglist.next = NULL;
1014 return imap_copy_msgs(folder, dest, &msglist, NULL);
1017 gint imap_copy_msgs(Folder *folder, FolderItem *dest,
1018 MsgInfoList *msglist, GRelation *relation)
1024 g_return_val_if_fail(folder != NULL, -1);
1025 g_return_val_if_fail(dest != NULL, -1);
1026 g_return_val_if_fail(msglist != NULL, -1);
1028 msginfo = (MsgInfo *)msglist->data;
1029 g_return_val_if_fail(msginfo->folder != NULL, -1);
1031 if (folder == msginfo->folder->folder)
1032 return imap_do_copy_msgs(folder, dest, msglist, relation);
1034 file_list = procmsg_get_message_file_list(msglist);
1035 g_return_val_if_fail(file_list != NULL, -1);
1037 ret = imap_add_msgs(folder, dest, file_list, relation);
1039 procmsg_message_file_list_free(file_list);
1044 gint imap_remove_msg(Folder *folder, FolderItem *item, gint uid)
1047 IMAPSession *session;
1049 MsgNumberList numlist;
1051 g_return_val_if_fail(folder != NULL, -1);
1052 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
1053 g_return_val_if_fail(item != NULL, -1);
1055 session = imap_session_get(folder);
1056 if (!session) return -1;
1058 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
1059 NULL, NULL, NULL, NULL);
1060 if (ok != IMAP_SUCCESS)
1063 numlist.next = NULL;
1064 numlist.data = GINT_TO_POINTER(uid);
1066 ok = imap_set_message_flags
1067 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
1068 &numlist, IMAP_FLAG_DELETED, TRUE);
1069 if (ok != IMAP_SUCCESS) {
1070 log_warning(_("can't set deleted flags: %d\n"), uid);
1074 ok = imap_cmd_expunge(session);
1075 if (ok != IMAP_SUCCESS) {
1076 log_warning(_("can't expunge\n"));
1080 dir = folder_item_get_path(item);
1081 if (is_dir_exist(dir))
1082 remove_numbered_files(dir, uid, uid);
1085 return IMAP_SUCCESS;
1088 gint imap_remove_all_msg(Folder *folder, FolderItem *item)
1091 IMAPSession *session;
1094 g_return_val_if_fail(folder != NULL, -1);
1095 g_return_val_if_fail(item != NULL, -1);
1097 session = imap_session_get(folder);
1098 if (!session) return -1;
1100 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
1101 NULL, NULL, NULL, NULL);
1102 if (ok != IMAP_SUCCESS)
1105 imap_gen_send(session, "STORE 1:* +FLAGS.SILENT (\\Deleted)");
1106 ok = imap_cmd_ok(session, NULL);
1107 if (ok != IMAP_SUCCESS) {
1108 log_warning(_("can't set deleted flags: 1:*\n"));
1112 ok = imap_cmd_expunge(session);
1113 if (ok != IMAP_SUCCESS) {
1114 log_warning(_("can't expunge\n"));
1118 dir = folder_item_get_path(item);
1119 if (is_dir_exist(dir))
1120 remove_all_numbered_files(dir);
1123 return IMAP_SUCCESS;
1126 gboolean imap_is_msg_changed(Folder *folder, FolderItem *item, MsgInfo *msginfo)
1128 /* TODO: properly implement this method */
1132 gint imap_close(Folder *folder, FolderItem *item)
1135 IMAPSession *session;
1137 g_return_val_if_fail(folder != NULL, -1);
1138 g_return_val_if_fail(item != NULL, -1);
1139 g_return_val_if_fail(item->path != NULL, -1);
1141 session = imap_session_get(folder);
1142 if (!session) return -1;
1144 if (session->mbox) {
1145 if (strcmp2(session->mbox, item->path) != 0) return -1;
1147 ok = imap_cmd_close(session);
1148 if (ok != IMAP_SUCCESS)
1149 log_warning(_("can't close folder\n"));
1151 g_free(session->mbox);
1152 session->mbox = NULL;
1160 gint imap_scan_tree(Folder *folder)
1162 FolderItem *item = NULL;
1163 IMAPSession *session;
1164 gchar *root_folder = NULL;
1166 g_return_val_if_fail(folder != NULL, -1);
1167 g_return_val_if_fail(folder->account != NULL, -1);
1169 session = imap_session_get(folder);
1171 if (!folder->node) {
1172 folder_tree_destroy(folder);
1173 item = folder_item_new(folder, folder->name, NULL);
1174 item->folder = folder;
1175 folder->node = item->node = g_node_new(item);
1180 if (folder->account->imap_dir && *folder->account->imap_dir) {
1183 Xstrdup_a(root_folder, folder->account->imap_dir, return -1);
1184 strtailchomp(root_folder, '/');
1185 extract_quote(root_folder, '"');
1186 real_path = imap_get_real_path
1187 (IMAP_FOLDER(folder), root_folder);
1188 debug_print("IMAP root directory: %s\n", real_path);
1189 if (imap_status(session, IMAP_FOLDER(folder), root_folder,
1190 NULL, NULL, NULL, NULL, NULL)
1192 if (imap_cmd_create(session, real_path)
1194 log_warning(_("can't create root folder %s\n"),
1204 item = FOLDER_ITEM(folder->node->data);
1205 if (!item || ((item->path || root_folder) &&
1206 strcmp2(item->path, root_folder) != 0)) {
1207 folder_tree_destroy(folder);
1208 item = folder_item_new(folder, folder->name, root_folder);
1209 item->folder = folder;
1210 folder->node = item->node = g_node_new(item);
1213 imap_scan_tree_recursive(session, FOLDER_ITEM(folder->node->data));
1214 imap_create_missing_folders(folder);
1219 static gint imap_scan_tree_recursive(IMAPSession *session, FolderItem *item)
1222 IMAPFolder *imapfolder;
1223 FolderItem *new_item;
1224 GSList *item_list, *cur;
1227 gchar *wildcard_path;
1231 g_return_val_if_fail(item != NULL, -1);
1232 g_return_val_if_fail(item->folder != NULL, -1);
1233 g_return_val_if_fail(item->no_sub == FALSE, -1);
1235 folder = item->folder;
1236 imapfolder = IMAP_FOLDER(folder);
1238 separator = imap_get_path_separator(imapfolder, item->path);
1240 if (folder->ui_func)
1241 folder->ui_func(folder, item, folder->ui_func_data);
1244 wildcard[0] = separator;
1247 real_path = imap_get_real_path(imapfolder, item->path);
1251 real_path = g_strdup("");
1254 Xstrcat_a(wildcard_path, real_path, wildcard,
1255 {g_free(real_path); return IMAP_ERROR;});
1256 QUOTE_IF_REQUIRED(wildcard_path, wildcard_path);
1258 imap_gen_send(session, "LIST \"\" %s",
1261 strtailchomp(real_path, separator);
1262 item_list = imap_parse_list(imapfolder, session, real_path, NULL);
1265 node = item->node->children;
1266 while (node != NULL) {
1267 FolderItem *old_item = FOLDER_ITEM(node->data);
1268 GNode *next = node->next;
1271 for (cur = item_list; cur != NULL; cur = cur->next) {
1272 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1273 if (!strcmp2(old_item->path, cur_item->path)) {
1274 new_item = cur_item;
1279 debug_print("folder '%s' not found. removing...\n",
1281 folder_item_remove(old_item);
1283 old_item->no_sub = new_item->no_sub;
1284 old_item->no_select = new_item->no_select;
1285 if (old_item->no_sub == TRUE && node->children) {
1286 debug_print("folder '%s' doesn't have "
1287 "subfolders. removing...\n",
1289 folder_item_remove_children(old_item);
1296 for (cur = item_list; cur != NULL; cur = cur->next) {
1297 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1299 for (node = item->node->children; node != NULL;
1300 node = node->next) {
1301 if (!strcmp2(FOLDER_ITEM(node->data)->path,
1303 new_item = FOLDER_ITEM(node->data);
1304 folder_item_destroy(cur_item);
1310 new_item = cur_item;
1311 debug_print("new folder '%s' found.\n", new_item->path);
1312 folder_item_append(item, new_item);
1315 if (!strcmp(new_item->path, "INBOX")) {
1316 new_item->stype = F_INBOX;
1317 folder->inbox = new_item;
1318 } else if (!item->parent || item->stype == F_INBOX) {
1321 base = g_basename(new_item->path);
1323 if (!folder->outbox && !strcasecmp(base, "Sent")) {
1324 new_item->stype = F_OUTBOX;
1325 folder->outbox = new_item;
1326 } else if (!folder->draft && !strcasecmp(base, "Drafts")) {
1327 new_item->stype = F_DRAFT;
1328 folder->draft = new_item;
1329 } else if (!folder->queue && !strcasecmp(base, "Queue")) {
1330 new_item->stype = F_QUEUE;
1331 folder->queue = new_item;
1332 } else if (!folder->trash && !strcasecmp(base, "Trash")) {
1333 new_item->stype = F_TRASH;
1334 folder->trash = new_item;
1338 if (new_item->no_sub == FALSE)
1339 imap_scan_tree_recursive(session, new_item);
1342 g_slist_free(item_list);
1344 return IMAP_SUCCESS;
1347 static GSList *imap_parse_list(IMAPFolder *folder, IMAPSession *session,
1348 const gchar *real_path, gchar *separator)
1350 gchar buf[IMAPBUFSIZE];
1352 gchar separator_str[16];
1355 gchar *loc_name, *loc_path;
1356 GSList *item_list = NULL;
1358 FolderItem *new_item;
1360 debug_print("getting list of %s ...\n",
1361 *real_path ? real_path : "\"\"");
1363 str = g_string_new(NULL);
1366 if (sock_gets(SESSION(session)->sock, buf, sizeof(buf)) <= 0) {
1367 log_warning(_("error occurred while getting LIST.\n"));
1371 if (buf[0] != '*' || buf[1] != ' ') {
1372 log_print("IMAP4< %s\n", buf);
1373 if (sscanf(buf, "%*d %16s", buf) < 1 ||
1374 strcmp(buf, "OK") != 0)
1375 log_warning(_("error occurred while getting LIST.\n"));
1379 debug_print("IMAP4< %s\n", buf);
1381 g_string_assign(str, buf);
1383 if (strncmp(p, "LIST ", 5) != 0) continue;
1386 if (*p != '(') continue;
1388 p = strchr_cpy(p, ')', flags, sizeof(flags));
1390 while (*p == ' ') p++;
1392 p = strchr_cpy(p, ' ', separator_str, sizeof(separator_str));
1394 extract_quote(separator_str, '"');
1395 if (!strcmp(separator_str, "NIL"))
1396 separator_str[0] = '\0';
1398 *separator = separator_str[0];
1401 while (*p == ' ') p++;
1402 if (*p == '{' || *p == '"')
1403 p = imap_parse_atom(SESSION(session)->sock, p,
1404 buf, sizeof(buf), str);
1406 strncpy2(buf, p, sizeof(buf));
1407 strtailchomp(buf, separator_str[0]);
1408 if (buf[0] == '\0') continue;
1409 if (!strcmp(buf, real_path)) continue;
1411 if (separator_str[0] != '\0')
1412 subst_char(buf, separator_str[0], '/');
1413 name = g_basename(buf);
1414 if (name[0] == '.') continue;
1416 loc_name = imap_modified_utf7_to_locale(name);
1417 loc_path = imap_modified_utf7_to_locale(buf);
1418 new_item = folder_item_new(FOLDER(folder), loc_name, loc_path);
1419 if (strcasestr(flags, "\\Noinferiors") != NULL)
1420 new_item->no_sub = TRUE;
1421 if (strcmp(buf, "INBOX") != 0 &&
1422 strcasestr(flags, "\\Noselect") != NULL)
1423 new_item->no_select = TRUE;
1425 item_list = g_slist_append(item_list, new_item);
1427 debug_print("folder '%s' found.\n", loc_path);
1432 g_string_free(str, TRUE);
1437 gint imap_create_tree(Folder *folder)
1439 g_return_val_if_fail(folder != NULL, -1);
1440 g_return_val_if_fail(folder->node != NULL, -1);
1441 g_return_val_if_fail(folder->node->data != NULL, -1);
1442 g_return_val_if_fail(folder->account != NULL, -1);
1444 imap_scan_tree(folder);
1445 imap_create_missing_folders(folder);
1450 static void imap_create_missing_folders(Folder *folder)
1452 g_return_if_fail(folder != NULL);
1455 folder->inbox = imap_create_special_folder
1456 (folder, F_INBOX, "INBOX");
1458 if (!folder->outbox)
1459 folder->outbox = imap_create_special_folder
1460 (folder, F_OUTBOX, "Sent");
1462 folder->draft = imap_create_special_folder
1463 (folder, F_DRAFT, "Drafts");
1465 folder->queue = imap_create_special_folder
1466 (folder, F_QUEUE, "Queue");
1469 folder->trash = imap_create_special_folder
1470 (folder, F_TRASH, "Trash");
1473 static FolderItem *imap_create_special_folder(Folder *folder,
1474 SpecialFolderItemType stype,
1478 FolderItem *new_item;
1480 g_return_val_if_fail(folder != NULL, NULL);
1481 g_return_val_if_fail(folder->node != NULL, NULL);
1482 g_return_val_if_fail(folder->node->data != NULL, NULL);
1483 g_return_val_if_fail(folder->account != NULL, NULL);
1484 g_return_val_if_fail(name != NULL, NULL);
1486 item = FOLDER_ITEM(folder->node->data);
1487 new_item = imap_create_folder(folder, item, name);
1490 g_warning("Can't create '%s'\n", name);
1491 if (!folder->inbox) return NULL;
1493 new_item = imap_create_folder(folder, folder->inbox, name);
1495 g_warning("Can't create '%s' under INBOX\n", name);
1497 new_item->stype = stype;
1499 new_item->stype = stype;
1504 static gchar *imap_folder_get_path(Folder *folder)
1508 g_return_val_if_fail(folder != NULL, NULL);
1509 g_return_val_if_fail(folder->account != NULL, NULL);
1511 folder_path = g_strconcat(get_imap_cache_dir(),
1513 folder->account->recv_server,
1515 folder->account->userid,
1521 static gchar *imap_item_get_path(Folder *folder, FolderItem *item)
1523 gchar *folder_path, *path;
1525 g_return_val_if_fail(folder != NULL, NULL);
1526 g_return_val_if_fail(item != NULL, NULL);
1527 folder_path = imap_folder_get_path(folder);
1529 g_return_val_if_fail(folder_path != NULL, NULL);
1530 if (folder_path[0] == G_DIR_SEPARATOR) {
1532 path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
1535 path = g_strdup(folder_path);
1538 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1539 folder_path, G_DIR_SEPARATOR_S,
1542 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1545 g_free(folder_path);
1550 FolderItem *imap_create_folder(Folder *folder, FolderItem *parent,
1553 gchar *dirpath, *imap_path;
1554 IMAPSession *session;
1555 FolderItem *new_item;
1561 g_return_val_if_fail(folder != NULL, NULL);
1562 g_return_val_if_fail(folder->account != NULL, NULL);
1563 g_return_val_if_fail(parent != NULL, NULL);
1564 g_return_val_if_fail(name != NULL, NULL);
1566 session = imap_session_get(folder);
1567 if (!session) return NULL;
1569 if (!parent->parent && strcmp(name, "INBOX") == 0)
1570 dirpath = g_strdup(name);
1571 else if (parent->path)
1572 dirpath = g_strconcat(parent->path, "/", name, NULL);
1573 else if ((p = strchr(name, '/')) != NULL && *(p + 1) != '\0')
1574 dirpath = g_strdup(name);
1575 else if (folder->account->imap_dir && *folder->account->imap_dir) {
1578 Xstrdup_a(imap_dir, folder->account->imap_dir, return NULL);
1579 strtailchomp(imap_dir, '/');
1580 dirpath = g_strconcat(imap_dir, "/", name, NULL);
1582 dirpath = g_strdup(name);
1584 /* keep trailing directory separator to create a folder that contains
1586 imap_path = imap_locale_to_modified_utf7(dirpath);
1587 strtailchomp(dirpath, '/');
1588 Xstrdup_a(new_name, name, {g_free(dirpath); return NULL;});
1589 strtailchomp(new_name, '/');
1590 separator = imap_get_path_separator(IMAP_FOLDER(folder), imap_path);
1591 imap_path_separator_subst(imap_path, separator);
1592 subst_char(new_name, '/', separator);
1594 if (strcmp(name, "INBOX") != 0) {
1597 gboolean exist = FALSE;
1599 argbuf = g_ptr_array_new();
1600 ok = imap_cmd_list(session, NULL, imap_path,
1602 if (ok != IMAP_SUCCESS) {
1603 log_warning(_("can't create mailbox: LIST failed\n"));
1606 ptr_array_free_strings(argbuf);
1607 g_ptr_array_free(argbuf, TRUE);
1611 for (i = 0; i < argbuf->len; i++) {
1613 str = g_ptr_array_index(argbuf, i);
1614 if (!strncmp(str, "LIST ", 5)) {
1619 ptr_array_free_strings(argbuf);
1620 g_ptr_array_free(argbuf, TRUE);
1623 ok = imap_cmd_create(session, imap_path);
1624 if (ok != IMAP_SUCCESS) {
1625 log_warning(_("can't create mailbox\n"));
1633 new_item = folder_item_new(folder, new_name, dirpath);
1634 folder_item_append(parent, new_item);
1638 dirpath = folder_item_get_path(new_item);
1639 if (!is_dir_exist(dirpath))
1640 make_dir_hier(dirpath);
1646 gint imap_rename_folder(Folder *folder, FolderItem *item, const gchar *name)
1650 gchar *real_oldpath;
1651 gchar *real_newpath;
1653 gchar *old_cache_dir;
1654 gchar *new_cache_dir;
1655 IMAPSession *session;
1658 gint exists, recent, unseen;
1659 guint32 uid_validity;
1661 g_return_val_if_fail(folder != NULL, -1);
1662 g_return_val_if_fail(item != NULL, -1);
1663 g_return_val_if_fail(item->path != NULL, -1);
1664 g_return_val_if_fail(name != NULL, -1);
1666 session = imap_session_get(folder);
1667 if (!session) return -1;
1669 real_oldpath = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1671 g_free(session->mbox);
1672 session->mbox = NULL;
1673 ok = imap_cmd_examine(session, "INBOX",
1674 &exists, &recent, &unseen, &uid_validity);
1675 if (ok != IMAP_SUCCESS) {
1676 g_free(real_oldpath);
1680 separator = imap_get_path_separator(IMAP_FOLDER(folder), item->path);
1681 if (strchr(item->path, G_DIR_SEPARATOR)) {
1682 dirpath = g_dirname(item->path);
1683 newpath = g_strconcat(dirpath, G_DIR_SEPARATOR_S, name, NULL);
1686 newpath = g_strdup(name);
1688 real_newpath = imap_locale_to_modified_utf7(newpath);
1689 imap_path_separator_subst(real_newpath, separator);
1691 ok = imap_cmd_rename(session, real_oldpath, real_newpath);
1692 if (ok != IMAP_SUCCESS) {
1693 log_warning(_("can't rename mailbox: %s to %s\n"),
1694 real_oldpath, real_newpath);
1695 g_free(real_oldpath);
1697 g_free(real_newpath);
1702 item->name = g_strdup(name);
1704 old_cache_dir = folder_item_get_path(item);
1706 paths[0] = g_strdup(item->path);
1708 g_node_traverse(item->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1709 imap_rename_folder_func, paths);
1711 if (is_dir_exist(old_cache_dir)) {
1712 new_cache_dir = folder_item_get_path(item);
1713 if (rename(old_cache_dir, new_cache_dir) < 0) {
1714 FILE_OP_ERROR(old_cache_dir, "rename");
1716 g_free(new_cache_dir);
1719 g_free(old_cache_dir);
1722 g_free(real_oldpath);
1723 g_free(real_newpath);
1728 gint imap_remove_folder(Folder *folder, FolderItem *item)
1731 IMAPSession *session;
1734 gint exists, recent, unseen;
1735 guint32 uid_validity;
1737 g_return_val_if_fail(folder != NULL, -1);
1738 g_return_val_if_fail(item != NULL, -1);
1739 g_return_val_if_fail(item->path != NULL, -1);
1741 session = imap_session_get(folder);
1742 if (!session) return -1;
1744 path = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1746 ok = imap_cmd_examine(session, "INBOX",
1747 &exists, &recent, &unseen, &uid_validity);
1748 if (ok != IMAP_SUCCESS) {
1753 ok = imap_cmd_delete(session, path);
1754 if (ok != IMAP_SUCCESS) {
1755 log_warning(_("can't delete mailbox\n"));
1761 cache_dir = folder_item_get_path(item);
1762 if (is_dir_exist(cache_dir) && remove_dir_recursive(cache_dir) < 0)
1763 g_warning("can't remove directory '%s'\n", cache_dir);
1765 folder_item_remove(item);
1770 static GSList *imap_get_uncached_messages(IMAPSession *session,
1772 MsgNumberList *numlist)
1775 GSList *newlist = NULL;
1776 GSList *llast = NULL;
1779 GSList *seq_list, *cur;
1782 g_return_val_if_fail(session != NULL, NULL);
1783 g_return_val_if_fail(item != NULL, NULL);
1784 g_return_val_if_fail(item->folder != NULL, NULL);
1785 g_return_val_if_fail(FOLDER_CLASS(item->folder) == &imap_class, NULL);
1787 seq_list = imap_get_seq_set_from_numlist(numlist);
1788 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
1789 imapset = cur->data;
1791 if (imap_cmd_envelope(session, imapset)
1793 log_warning(_("can't get envelope\n"));
1797 str = g_string_new(NULL);
1800 if ((tmp = sock_getline(SESSION(session)->sock)) == NULL) {
1801 log_warning(_("error occurred while getting envelope.\n"));
1802 g_string_free(str, TRUE);
1806 if (tmp[0] != '*' || tmp[1] != ' ') {
1807 log_print("IMAP4< %s\n", tmp);
1811 if (strstr(tmp, "FETCH") == NULL) {
1812 log_print("IMAP4< %s\n", tmp);
1816 log_print("IMAP4< %s\n", tmp);
1817 g_string_assign(str, tmp);
1820 msginfo = imap_parse_envelope
1821 (SESSION(session)->sock, item, str);
1823 log_warning(_("can't parse envelope: %s\n"), str->str);
1826 if (item->stype == F_QUEUE) {
1827 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_QUEUED);
1828 } else if (item->stype == F_DRAFT) {
1829 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_DRAFT);
1832 msginfo->folder = item;
1835 llast = newlist = g_slist_append(newlist, msginfo);
1837 llast = g_slist_append(llast, msginfo);
1838 llast = llast->next;
1842 g_string_free(str, TRUE);
1844 imap_seq_set_free(seq_list);
1849 static void imap_delete_all_cached_messages(FolderItem *item)
1853 g_return_if_fail(item != NULL);
1854 g_return_if_fail(item->folder != NULL);
1855 g_return_if_fail(FOLDER_CLASS(item->folder) == &imap_class);
1857 debug_print("Deleting all cached messages...\n");
1859 dir = folder_item_get_path(item);
1860 if (is_dir_exist(dir))
1861 remove_all_numbered_files(dir);
1864 debug_print("done.\n");
1868 static SockInfo *imap_open_tunnel(const gchar *server,
1869 const gchar *tunnelcmd,
1872 static SockInfo *imap_open_tunnel(const gchar *server,
1873 const gchar *tunnelcmd)
1878 if ((sock = sock_connect_cmd(server, tunnelcmd)) == NULL) {
1879 log_warning(_("Can't establish IMAP4 session with: %s\n"),
1884 return imap_init_sock(sock, ssl_type);
1886 return imap_init_sock(sock);
1892 static SockInfo *imap_open(const gchar *server, gushort port,
1895 static SockInfo *imap_open(const gchar *server, gushort port)
1900 if ((sock = sock_connect(server, port)) == NULL) {
1901 log_warning(_("Can't connect to IMAP4 server: %s:%d\n"),
1907 if (ssl_type == SSL_TUNNEL && !ssl_init_socket(sock)) {
1908 log_warning(_("Can't establish IMAP4 session with: %s:%d\n"),
1918 static SockInfo *imap_init_sock(SockInfo *sock, SSLType ssl_type)
1920 static SockInfo *imap_init_sock(SockInfo *sock)
1927 static GList *imap_parse_namespace_str(gchar *str)
1932 IMAPNameSpace *namespace;
1933 GList *ns_list = NULL;
1935 while (*p != '\0') {
1936 /* parse ("#foo" "/") */
1938 while (*p && *p != '(') p++;
1939 if (*p == '\0') break;
1942 while (*p && *p != '"') p++;
1943 if (*p == '\0') break;
1947 while (*p && *p != '"') p++;
1948 if (*p == '\0') break;
1952 while (*p && isspace(*p)) p++;
1953 if (*p == '\0') break;
1954 if (strncmp(p, "NIL", 3) == 0)
1956 else if (*p == '"') {
1959 while (*p && *p != '"') p++;
1960 if (*p == '\0') break;
1965 while (*p && *p != ')') p++;
1966 if (*p == '\0') break;
1969 namespace = g_new(IMAPNameSpace, 1);
1970 namespace->name = g_strdup(name);
1971 namespace->separator = separator ? separator[0] : '\0';
1972 ns_list = g_list_append(ns_list, namespace);
1978 static void imap_parse_namespace(IMAPSession *session, IMAPFolder *folder)
1983 g_return_if_fail(session != NULL);
1984 g_return_if_fail(folder != NULL);
1986 if (folder->ns_personal != NULL ||
1987 folder->ns_others != NULL ||
1988 folder->ns_shared != NULL)
1991 if (!imap_has_capability(session, "NAMESPACE")) {
1992 imap_get_namespace_by_list(session, folder);
1996 if (imap_cmd_namespace(session, &ns_str)
1998 log_warning(_("can't get namespace\n"));
2002 str_array = strsplit_parenthesis(ns_str, '(', ')', 3);
2003 if (str_array == NULL) {
2005 imap_get_namespace_by_list(session, folder);
2009 folder->ns_personal = imap_parse_namespace_str(str_array[0]);
2010 if (str_array[0] && str_array[1])
2011 folder->ns_others = imap_parse_namespace_str(str_array[1]);
2012 if (str_array[0] && str_array[1] && str_array[2])
2013 folder->ns_shared = imap_parse_namespace_str(str_array[2]);
2014 g_strfreev(str_array);
2018 static void imap_get_namespace_by_list(IMAPSession *session, IMAPFolder *folder)
2020 GSList *item_list, *cur;
2021 gchar separator = '\0';
2022 IMAPNameSpace *namespace;
2024 g_return_if_fail(session != NULL);
2025 g_return_if_fail(folder != NULL);
2027 if (folder->ns_personal != NULL ||
2028 folder->ns_others != NULL ||
2029 folder->ns_shared != NULL)
2032 imap_gen_send(session, "LIST \"\" \"\"");
2033 item_list = imap_parse_list(folder, session, "", &separator);
2034 for (cur = item_list; cur != NULL; cur = cur->next)
2035 folder_item_destroy(FOLDER_ITEM(cur->data));
2036 g_slist_free(item_list);
2038 namespace = g_new(IMAPNameSpace, 1);
2039 namespace->name = g_strdup("");
2040 namespace->separator = separator;
2041 folder->ns_personal = g_list_append(NULL, namespace);
2044 static IMAPNameSpace *imap_find_namespace_from_list(GList *ns_list,
2047 IMAPNameSpace *namespace = NULL;
2048 gchar *tmp_path, *name;
2050 if (!path) path = "";
2052 for (; ns_list != NULL; ns_list = ns_list->next) {
2053 IMAPNameSpace *tmp_ns = ns_list->data;
2055 Xstrcat_a(tmp_path, path, "/", return namespace);
2056 Xstrdup_a(name, tmp_ns->name, return namespace);
2057 if (tmp_ns->separator && tmp_ns->separator != '/') {
2058 subst_char(tmp_path, tmp_ns->separator, '/');
2059 subst_char(name, tmp_ns->separator, '/');
2061 if (strncmp(tmp_path, name, strlen(name)) == 0)
2068 static IMAPNameSpace *imap_find_namespace(IMAPFolder *folder,
2071 IMAPNameSpace *namespace;
2073 g_return_val_if_fail(folder != NULL, NULL);
2075 namespace = imap_find_namespace_from_list(folder->ns_personal, path);
2076 if (namespace) return namespace;
2077 namespace = imap_find_namespace_from_list(folder->ns_others, path);
2078 if (namespace) return namespace;
2079 namespace = imap_find_namespace_from_list(folder->ns_shared, path);
2080 if (namespace) return namespace;
2085 static gchar imap_get_path_separator(IMAPFolder *folder, const gchar *path)
2087 IMAPNameSpace *namespace;
2088 gchar separator = '/';
2090 namespace = imap_find_namespace(folder, path);
2091 if (namespace && namespace->separator)
2092 separator = namespace->separator;
2097 static gchar *imap_get_real_path(IMAPFolder *folder, const gchar *path)
2102 g_return_val_if_fail(folder != NULL, NULL);
2103 g_return_val_if_fail(path != NULL, NULL);
2105 real_path = imap_locale_to_modified_utf7(path);
2106 separator = imap_get_path_separator(folder, path);
2107 imap_path_separator_subst(real_path, separator);
2112 static gchar *imap_parse_atom(SockInfo *sock, gchar *src,
2113 gchar *dest, gint dest_len, GString *str)
2115 gchar *cur_pos = src;
2118 g_return_val_if_fail(str != NULL, cur_pos);
2120 /* read the next line if the current response buffer is empty */
2121 while (isspace(*cur_pos)) cur_pos++;
2122 while (*cur_pos == '\0') {
2123 if ((nextline = sock_getline(sock)) == NULL)
2125 g_string_assign(str, nextline);
2127 strretchomp(nextline);
2128 /* log_print("IMAP4< %s\n", nextline); */
2129 debug_print("IMAP4< %s\n", nextline);
2132 while (isspace(*cur_pos)) cur_pos++;
2135 if (!strncmp(cur_pos, "NIL", 3)) {
2138 } else if (*cur_pos == '\"') {
2141 p = get_quoted(cur_pos, '\"', dest, dest_len);
2142 cur_pos = p ? p : cur_pos + 2;
2143 } else if (*cur_pos == '{') {
2148 cur_pos = strchr_cpy(cur_pos + 1, '}', buf, sizeof(buf));
2150 g_return_val_if_fail(len >= 0, cur_pos);
2152 g_string_truncate(str, 0);
2156 if ((nextline = sock_getline(sock)) == NULL)
2158 line_len += strlen(nextline);
2159 g_string_append(str, nextline);
2161 strretchomp(nextline);
2162 /* log_print("IMAP4< %s\n", nextline); */
2163 debug_print("IMAP4< %s\n", nextline);
2165 } while (line_len < len);
2167 memcpy(dest, cur_pos, MIN(len, dest_len - 1));
2168 dest[MIN(len, dest_len - 1)] = '\0';
2175 static gchar *imap_get_header(SockInfo *sock, gchar *cur_pos, gchar **headers,
2185 g_return_val_if_fail(str != NULL, cur_pos);
2187 while (isspace(*cur_pos)) cur_pos++;
2189 g_return_val_if_fail(*cur_pos == '{', cur_pos);
2191 cur_pos = strchr_cpy(cur_pos + 1, '}', buf, sizeof(buf));
2193 g_return_val_if_fail(len >= 0, cur_pos);
2195 g_string_truncate(str, 0);
2199 if ((nextline = sock_getline(sock)) == NULL)
2201 block_len += strlen(nextline);
2202 g_string_append(str, nextline);
2204 strretchomp(nextline);
2205 /* debug_print("IMAP4< %s\n", nextline); */
2207 } while (block_len < len);
2209 debug_print("IMAP4< [contents of BODY.PEEK[HEADER.FIELDS (...)]]\n");
2211 *headers = g_strndup(cur_pos, len);
2214 while (isspace(*cur_pos)) cur_pos++;
2215 while (*cur_pos == '\0') {
2216 if ((nextline = sock_getline(sock)) == NULL)
2218 g_string_assign(str, nextline);
2220 strretchomp(nextline);
2221 debug_print("IMAP4< %s\n", nextline);
2224 while (isspace(*cur_pos)) cur_pos++;
2230 static MsgFlags imap_parse_flags(const gchar *flag_str)
2232 const gchar *p = flag_str;
2233 MsgFlags flags = {0, 0};
2235 flags.perm_flags = MSG_UNREAD;
2237 while ((p = strchr(p, '\\')) != NULL) {
2240 if (g_strncasecmp(p, "Recent", 6) == 0 && MSG_IS_UNREAD(flags)) {
2241 MSG_SET_PERM_FLAGS(flags, MSG_NEW);
2242 } else if (g_strncasecmp(p, "Seen", 4) == 0) {
2243 MSG_UNSET_PERM_FLAGS(flags, MSG_NEW|MSG_UNREAD);
2244 } else if (g_strncasecmp(p, "Deleted", 7) == 0) {
2245 MSG_SET_PERM_FLAGS(flags, MSG_DELETED);
2246 } else if (g_strncasecmp(p, "Flagged", 7) == 0) {
2247 MSG_SET_PERM_FLAGS(flags, MSG_MARKED);
2248 } else if (g_strncasecmp(p, "Answered", 8) == 0) {
2249 MSG_SET_PERM_FLAGS(flags, MSG_REPLIED);
2256 static MsgInfo *imap_parse_envelope(SockInfo *sock, FolderItem *item,
2259 gchar buf[IMAPBUFSIZE];
2260 MsgInfo *msginfo = NULL;
2265 MsgFlags flags = {0, 0}, imap_flags = {0, 0};
2267 g_return_val_if_fail(line_str != NULL, NULL);
2268 g_return_val_if_fail(line_str->str[0] == '*' &&
2269 line_str->str[1] == ' ', NULL);
2271 MSG_SET_TMP_FLAGS(flags, MSG_IMAP);
2272 if (item->stype == F_QUEUE) {
2273 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
2274 } else if (item->stype == F_DRAFT) {
2275 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
2278 cur_pos = line_str->str + 2;
2280 #define PARSE_ONE_ELEMENT(ch) \
2282 cur_pos = strchr_cpy(cur_pos, ch, buf, sizeof(buf)); \
2283 if (cur_pos == NULL) { \
2284 g_warning("cur_pos == NULL\n"); \
2285 procmsg_msginfo_free(msginfo); \
2290 PARSE_ONE_ELEMENT(' ');
2293 PARSE_ONE_ELEMENT(' ');
2294 g_return_val_if_fail(!strcmp(buf, "FETCH"), NULL);
2296 g_return_val_if_fail(*cur_pos == '(', NULL);
2299 while (*cur_pos != '\0' && *cur_pos != ')') {
2300 while (*cur_pos == ' ') cur_pos++;
2302 if (!strncmp(cur_pos, "UID ", 4)) {
2304 uid = strtoul(cur_pos, &cur_pos, 10);
2305 } else if (!strncmp(cur_pos, "FLAGS ", 6)) {
2307 if (*cur_pos != '(') {
2308 g_warning("*cur_pos != '('\n");
2309 procmsg_msginfo_free(msginfo);
2313 PARSE_ONE_ELEMENT(')');
2314 imap_flags = imap_parse_flags(buf);
2315 } else if (!strncmp(cur_pos, "RFC822.SIZE ", 12)) {
2317 size = strtol(cur_pos, &cur_pos, 10);
2318 } else if (!strncmp(cur_pos, "BODY[HEADER.FIELDS ", 19)) {
2322 if (*cur_pos != '(') {
2323 g_warning("*cur_pos != '('\n");
2324 procmsg_msginfo_free(msginfo);
2328 PARSE_ONE_ELEMENT(')');
2329 if (*cur_pos != ']') {
2330 g_warning("*cur_pos != ']'\n");
2331 procmsg_msginfo_free(msginfo);
2336 cur_pos = imap_get_header(sock, cur_pos, &headers,
2338 msginfo = procheader_parse_str(headers, flags, FALSE, FALSE);
2341 g_warning("invalid FETCH response: %s\n", cur_pos);
2347 msginfo->msgnum = uid;
2348 msginfo->size = size;
2349 msginfo->flags.tmp_flags |= imap_flags.tmp_flags;
2350 msginfo->flags.perm_flags = imap_flags.perm_flags;
2356 static gchar *imap_get_flag_str(IMAPFlags flags)
2361 str = g_string_new(NULL);
2363 if (IMAP_IS_SEEN(flags)) g_string_append(str, "\\Seen ");
2364 if (IMAP_IS_ANSWERED(flags)) g_string_append(str, "\\Answered ");
2365 if (IMAP_IS_FLAGGED(flags)) g_string_append(str, "\\Flagged ");
2366 if (IMAP_IS_DELETED(flags)) g_string_append(str, "\\Deleted ");
2367 if (IMAP_IS_DRAFT(flags)) g_string_append(str, "\\Draft");
2369 if (str->len > 0 && str->str[str->len - 1] == ' ')
2370 g_string_truncate(str, str->len - 1);
2373 g_string_free(str, FALSE);
2378 static gint imap_set_message_flags(IMAPSession *session,
2379 MsgNumberList *numlist,
2386 GSList *seq_list, *cur;
2389 flag_str = imap_get_flag_str(flags);
2390 cmd = g_strconcat(is_set ? "+FLAGS.SILENT (" : "-FLAGS.SILENT (",
2391 flag_str, ")", NULL);
2394 seq_list = imap_get_seq_set_from_numlist(numlist);
2395 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
2396 imapset = cur->data;
2398 ok = imap_cmd_store(session, imapset, cmd);
2400 imap_seq_set_free(seq_list);
2406 static gint imap_select(IMAPSession *session, IMAPFolder *folder,
2408 gint *exists, gint *recent, gint *unseen,
2409 guint32 *uid_validity)
2413 gint exists_, recent_, unseen_, uid_validity_;
2415 if (!exists || !recent || !unseen || !uid_validity) {
2416 if (session->mbox && strcmp(session->mbox, path) == 0)
2417 return IMAP_SUCCESS;
2421 uid_validity = &uid_validity_;
2424 g_free(session->mbox);
2425 session->mbox = NULL;
2427 real_path = imap_get_real_path(folder, path);
2428 ok = imap_cmd_select(session, real_path,
2429 exists, recent, unseen, uid_validity);
2430 if (ok != IMAP_SUCCESS)
2431 log_warning(_("can't select folder: %s\n"), real_path);
2433 session->mbox = g_strdup(path);
2434 session->folder_content_changed = FALSE;
2441 #define THROW(err) { ok = err; goto catch; }
2443 static gint imap_status(IMAPSession *session, IMAPFolder *folder,
2445 gint *messages, gint *recent,
2446 guint32 *uid_next, guint32 *uid_validity,
2452 GPtrArray *argbuf = NULL;
2455 if (messages && recent && uid_next && uid_validity && unseen) {
2456 *messages = *recent = *uid_next = *uid_validity = *unseen = 0;
2457 argbuf = g_ptr_array_new();
2460 real_path = imap_get_real_path(folder, path);
2461 QUOTE_IF_REQUIRED(real_path_, real_path);
2462 imap_gen_send(session, "STATUS %s "
2463 "(MESSAGES RECENT UIDNEXT UIDVALIDITY UNSEEN)",
2466 ok = imap_cmd_ok(session, argbuf);
2467 if (ok != IMAP_SUCCESS || !argbuf) THROW(ok);
2469 str = search_array_str(argbuf, "STATUS");
2470 if (!str) THROW(IMAP_ERROR);
2472 str = strchr(str, '(');
2473 if (!str) THROW(IMAP_ERROR);
2475 while (*str != '\0' && *str != ')') {
2476 while (*str == ' ') str++;
2478 if (!strncmp(str, "MESSAGES ", 9)) {
2480 *messages = strtol(str, &str, 10);
2481 } else if (!strncmp(str, "RECENT ", 7)) {
2483 *recent = strtol(str, &str, 10);
2484 } else if (!strncmp(str, "UIDNEXT ", 8)) {
2486 *uid_next = strtoul(str, &str, 10);
2487 } else if (!strncmp(str, "UIDVALIDITY ", 12)) {
2489 *uid_validity = strtoul(str, &str, 10);
2490 } else if (!strncmp(str, "UNSEEN ", 7)) {
2492 *unseen = strtol(str, &str, 10);
2494 g_warning("invalid STATUS response: %s\n", str);
2502 ptr_array_free_strings(argbuf);
2503 g_ptr_array_free(argbuf, TRUE);
2511 static gboolean imap_has_capability(IMAPSession *session, const gchar *cap)
2515 for (p = session->capability; *p != NULL; ++p) {
2516 if (!g_strcasecmp(*p, cap))
2523 void imap_free_capabilities(IMAPSession *session)
2525 g_strfreev(session->capability);
2526 session->capability = NULL;
2529 /* low-level IMAP4rev1 commands */
2531 static gint imap_cmd_authenticate(IMAPSession *session, const gchar *user,
2532 const gchar *pass, IMAPAuthType type)
2539 gchar hexdigest[33];
2543 auth_type = "CRAM-MD5";
2545 imap_gen_send(session, "AUTHENTICATE %s", auth_type);
2546 ok = imap_gen_recv(session, &buf);
2547 if (ok != IMAP_SUCCESS || buf[0] != '+' || buf[1] != ' ') {
2552 challenge = g_malloc(strlen(buf + 2) + 1);
2553 challenge_len = base64_decode(challenge, buf + 2, -1);
2554 challenge[challenge_len] = '\0';
2556 log_print("IMAP< [Decoded: %s]\n", challenge);
2558 md5_hex_hmac(hexdigest, challenge, challenge_len, pass, strlen(pass));
2561 response = g_strdup_printf("%s %s", user, hexdigest);
2562 log_print("IMAP> [Encoded: %s]\n", response);
2563 response64 = g_malloc((strlen(response) + 3) * 2 + 1);
2564 base64_encode(response64, response, strlen(response));
2567 log_print("IMAP> %s\n", response64);
2568 sock_puts(SESSION(session)->sock, response64);
2569 ok = imap_cmd_ok(session, NULL);
2570 if (ok != IMAP_SUCCESS)
2571 log_warning(_("IMAP4 authentication failed.\n"));
2576 static gint imap_cmd_login(IMAPSession *session,
2577 const gchar *user, const gchar *pass)
2579 gchar *user_, *pass_;
2582 QUOTE_IF_REQUIRED(user_, user);
2583 QUOTE_IF_REQUIRED(pass_, pass);
2584 imap_gen_send(session, "LOGIN %s %s", user_, pass_);
2586 ok = imap_cmd_ok(session, NULL);
2587 if (ok != IMAP_SUCCESS)
2588 log_warning(_("IMAP4 login failed.\n"));
2593 static gint imap_cmd_logout(IMAPSession *session)
2595 imap_gen_send(session, "LOGOUT");
2596 return imap_cmd_ok(session, NULL);
2599 static gint imap_cmd_noop(IMAPSession *session)
2601 imap_gen_send(session, "NOOP");
2602 return imap_cmd_ok(session, NULL);
2605 static gint imap_cmd_starttls(IMAPSession *session)
2607 imap_gen_send(session, "STARTTLS");
2608 return imap_cmd_ok(session, NULL);
2611 #define THROW(err) { ok = err; goto catch; }
2613 static gint imap_cmd_namespace(IMAPSession *session, gchar **ns_str)
2619 argbuf = g_ptr_array_new();
2621 imap_gen_send(session, "NAMESPACE");
2622 if ((ok = imap_cmd_ok(session, argbuf)) != IMAP_SUCCESS) THROW(ok);
2624 str = search_array_str(argbuf, "NAMESPACE");
2625 if (!str) THROW(IMAP_ERROR);
2627 *ns_str = g_strdup(str);
2630 ptr_array_free_strings(argbuf);
2631 g_ptr_array_free(argbuf, TRUE);
2638 static gint imap_cmd_list(IMAPSession *session, const gchar *ref,
2639 const gchar *mailbox, GPtrArray *argbuf)
2641 gchar *ref_, *mailbox_;
2643 if (!ref) ref = "\"\"";
2644 if (!mailbox) mailbox = "\"\"";
2646 QUOTE_IF_REQUIRED(ref_, ref);
2647 QUOTE_IF_REQUIRED(mailbox_, mailbox);
2648 imap_gen_send(session, "LIST %s %s", ref_, mailbox_);
2650 return imap_cmd_ok(session, argbuf);
2653 #define THROW goto catch
2655 static gint imap_cmd_do_select(IMAPSession *session, const gchar *folder,
2657 gint *exists, gint *recent, gint *unseen,
2658 guint32 *uid_validity)
2666 *exists = *recent = *unseen = *uid_validity = 0;
2667 argbuf = g_ptr_array_new();
2670 select_cmd = "EXAMINE";
2672 select_cmd = "SELECT";
2674 QUOTE_IF_REQUIRED(folder_, folder);
2675 imap_gen_send(session, "%s %s", select_cmd, folder_);
2677 if ((ok = imap_cmd_ok(session, argbuf)) != IMAP_SUCCESS) THROW;
2679 resp_str = search_array_contain_str(argbuf, "EXISTS");
2681 if (sscanf(resp_str,"%d EXISTS", exists) != 1) {
2682 g_warning("imap_cmd_select(): invalid EXISTS line.\n");
2687 resp_str = search_array_contain_str(argbuf, "RECENT");
2689 if (sscanf(resp_str, "%d RECENT", recent) != 1) {
2690 g_warning("imap_cmd_select(): invalid RECENT line.\n");
2695 resp_str = search_array_contain_str(argbuf, "UIDVALIDITY");
2697 if (sscanf(resp_str, "OK [UIDVALIDITY %u] ", uid_validity)
2699 g_warning("imap_cmd_select(): invalid UIDVALIDITY line.\n");
2704 resp_str = search_array_contain_str(argbuf, "UNSEEN");
2706 if (sscanf(resp_str, "OK [UNSEEN %d] ", unseen) != 1) {
2707 g_warning("imap_cmd_select(): invalid UNSEEN line.\n");
2713 ptr_array_free_strings(argbuf);
2714 g_ptr_array_free(argbuf, TRUE);
2719 static gint imap_cmd_select(IMAPSession *session, const gchar *folder,
2720 gint *exists, gint *recent, gint *unseen,
2721 guint32 *uid_validity)
2723 return imap_cmd_do_select(session, folder, FALSE,
2724 exists, recent, unseen, uid_validity);
2727 static gint imap_cmd_examine(IMAPSession *session, const gchar *folder,
2728 gint *exists, gint *recent, gint *unseen,
2729 guint32 *uid_validity)
2731 return imap_cmd_do_select(session, folder, TRUE,
2732 exists, recent, unseen, uid_validity);
2737 static gint imap_cmd_create(IMAPSession *session, const gchar *folder)
2741 QUOTE_IF_REQUIRED(folder_, folder);
2742 imap_gen_send(session, "CREATE %s", folder_);
2744 return imap_cmd_ok(session, NULL);
2747 static gint imap_cmd_rename(IMAPSession *session, const gchar *old_folder,
2748 const gchar *new_folder)
2750 gchar *old_folder_, *new_folder_;
2752 QUOTE_IF_REQUIRED(old_folder_, old_folder);
2753 QUOTE_IF_REQUIRED(new_folder_, new_folder);
2754 imap_gen_send(session, "RENAME %s %s", old_folder_, new_folder_);
2756 return imap_cmd_ok(session, NULL);
2759 static gint imap_cmd_delete(IMAPSession *session, const gchar *folder)
2763 QUOTE_IF_REQUIRED(folder_, folder);
2764 imap_gen_send(session, "DELETE %s", folder_);
2766 return imap_cmd_ok(session, NULL);
2769 static gint imap_cmd_search(IMAPSession *session, const gchar *criteria, GSList **list)
2775 g_return_val_if_fail(criteria != NULL, IMAP_ERROR);
2776 g_return_val_if_fail(list != NULL, IMAP_ERROR);
2780 argbuf = g_ptr_array_new();
2781 imap_gen_send(session, "UID SEARCH %s", criteria);
2783 ok = imap_cmd_ok(session, argbuf);
2784 if (ok != IMAP_SUCCESS) {
2785 ptr_array_free_strings(argbuf);
2786 g_ptr_array_free(argbuf, TRUE);
2790 if ((uidlist = search_array_str(argbuf, "SEARCH ")) != NULL) {
2791 gchar **strlist, **p;
2793 strlist = g_strsplit(uidlist + 7, " ", 0);
2794 for (p = strlist; *p != NULL; ++p) {
2797 if (sscanf(*p, "%d", &msgnum) == 1)
2798 *list = g_slist_append(*list, GINT_TO_POINTER(msgnum));
2800 g_strfreev(strlist);
2802 ptr_array_free_strings(argbuf);
2803 g_ptr_array_free(argbuf, TRUE);
2805 return IMAP_SUCCESS;
2808 static gint imap_cmd_fetch(IMAPSession *session, guint32 uid, const gchar *filename)
2816 g_return_val_if_fail(filename != NULL, IMAP_ERROR);
2818 imap_gen_send(session, "UID FETCH %d BODY.PEEK[]", uid);
2820 while ((ok = imap_gen_recv(session, &buf)) == IMAP_SUCCESS) {
2821 if (buf[0] != '*' || buf[1] != ' ') {
2825 if (strstr(buf, "FETCH") != NULL) break;
2828 if (ok != IMAP_SUCCESS) {
2833 #define RETURN_ERROR_IF_FAIL(cond) \
2836 return IMAP_ERROR; \
2839 cur_pos = strchr(buf, '{');
2840 RETURN_ERROR_IF_FAIL(cur_pos != NULL);
2841 cur_pos = strchr_cpy(cur_pos + 1, '}', size_str, sizeof(size_str));
2842 RETURN_ERROR_IF_FAIL(cur_pos != NULL);
2843 size_num = atol(size_str);
2844 RETURN_ERROR_IF_FAIL(size_num >= 0);
2846 RETURN_ERROR_IF_FAIL(*cur_pos == '\0');
2848 #undef RETURN_ERROR_IF_FAIL
2852 if (recv_bytes_write_to_file(SESSION(session)->sock, size_num, filename) != 0)
2855 if (imap_gen_recv(session, &buf) != IMAP_SUCCESS) {
2860 if (buf[0] == '\0' || buf[strlen(buf) - 1] != ')') {
2866 ok = imap_cmd_ok(session, NULL);
2871 static gint imap_cmd_append(IMAPSession *session, const gchar *destfolder,
2872 const gchar *file, IMAPFlags flags, guint32 *new_uid)
2880 gchar buf[BUFFSIZE];
2885 g_return_val_if_fail(file != NULL, IMAP_ERROR);
2887 size = get_file_size_as_crlf(file);
2888 if ((fp = fopen(file, "rb")) == NULL) {
2889 FILE_OP_ERROR(file, "fopen");
2892 QUOTE_IF_REQUIRED(destfolder_, destfolder);
2893 flag_str = imap_get_flag_str(flags);
2894 imap_gen_send(session, "APPEND %s (%s) {%d}",
2895 destfolder_, flag_str, size);
2898 ok = imap_gen_recv(session, &ret);
2899 if (ok != IMAP_SUCCESS || ret[0] != '+' || ret[1] != ' ') {
2900 log_warning(_("can't append %s to %s\n"), file, destfolder_);
2907 log_print("IMAP4> %s\n", _("(sending file...)"));
2909 while (fgets(buf, sizeof(buf), fp) != NULL) {
2911 if (sock_puts(SESSION(session)->sock, buf) < 0) {
2918 FILE_OP_ERROR(file, "fgets");
2923 sock_puts(SESSION(session)->sock, "");
2927 if (new_uid != NULL)
2930 if (new_uid != NULL && session->uidplus) {
2931 argbuf = g_ptr_array_new();
2933 ok = imap_cmd_ok(session, argbuf);
2934 if (ok != IMAP_SUCCESS)
2935 log_warning(_("can't append message to %s\n"),
2937 else if (argbuf->len > 0) {
2938 resp_str = g_ptr_array_index(argbuf, argbuf->len - 1);
2940 sscanf(resp_str, "%*u OK [APPENDUID %*u %u]",
2942 *new_uid = new_uid_;
2946 ptr_array_free_strings(argbuf);
2947 g_ptr_array_free(argbuf, TRUE);
2949 ok = imap_cmd_ok(session, NULL);
2954 static gint imap_cmd_copy(IMAPSession *session, const gchar *seq_set,
2955 const gchar *destfolder, GRelation *uid_mapping)
2961 g_return_val_if_fail(session != NULL, IMAP_ERROR);
2962 g_return_val_if_fail(seq_set != NULL, IMAP_ERROR);
2963 g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
2965 QUOTE_IF_REQUIRED(destfolder_, destfolder);
2966 imap_gen_send(session, "UID COPY %s %s", seq_set, destfolder_);
2968 reply = g_ptr_array_new();
2970 ok = imap_cmd_ok(session, reply);
2971 if (ok != IMAP_SUCCESS)
2972 log_warning(_("can't copy %s to %s\n"), seq_set, destfolder_);
2976 - split IMAPSets into uids
2977 - g_relation_insert(uid_mapping, olduid, newuid);
2979 else if (imap_has_capability(session, "UIDPLUS") && reply->len > 0)
2980 if ((okmsginfo = g_ptr_array_index(reply, reply->len - 1)) != NULL &&
2981 sscanf(okmsginfo, "%*u OK [COPYUID %*u %u %u]", &olduid, &newuid) == 2 &&
2985 ptr_array_free_strings(reply);
2986 g_ptr_array_free(reply, TRUE);
2990 gint imap_cmd_envelope(IMAPSession *session, IMAPSet set)
2992 static GString *header_fields = NULL;
2994 if (header_fields == NULL) {
2995 const HeaderEntry *headers, *elem;
2997 headers = procheader_get_headernames(FALSE);
2998 header_fields = g_string_new("");
3000 for (elem = headers; elem->name != NULL; ++elem) {
3001 gint namelen = strlen(elem->name);
3003 /* Header fields ending with space are not rfc822 headers */
3004 if (elem->name[namelen - 1] == ' ')
3007 /* strip : at the of header field */
3008 if(elem->name[namelen - 1] == ':')
3014 g_string_sprintfa(header_fields, "%s%.*s",
3015 header_fields->str[0] != '\0' ? " " : "",
3016 namelen, elem->name);
3021 (session, "UID FETCH %s (UID FLAGS RFC822.SIZE BODY.PEEK[HEADER.FIELDS (%s)])",
3022 set, header_fields->str);
3024 return IMAP_SUCCESS;
3027 static gint imap_cmd_store(IMAPSession *session, IMAPSet seq_set,
3032 imap_gen_send(session, "UID STORE %s %s", seq_set, sub_cmd);
3034 if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS) {
3035 log_warning(_("error while imap command: STORE %s %s\n"),
3040 return IMAP_SUCCESS;
3043 static gint imap_cmd_expunge(IMAPSession *session)
3047 imap_gen_send(session, "EXPUNGE");
3048 if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS) {
3049 log_warning(_("error while imap command: EXPUNGE\n"));
3053 return IMAP_SUCCESS;
3056 static gint imap_cmd_close(IMAPSession *session)
3060 imap_gen_send(session, "CLOSE");
3061 if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS)
3062 log_warning(_("error while imap command: CLOSE\n"));
3067 static gint imap_cmd_ok(IMAPSession *session, GPtrArray *argbuf)
3069 gint ok = IMAP_SUCCESS;
3074 while ((ok = imap_gen_recv(session, &buf))
3076 // make sure data is long enough for any substring of buf
3077 data = alloca(strlen(buf) + 1);
3079 // untagged line read
3080 if (buf[0] == '*' && buf[1] == ' ') {
3083 g_ptr_array_add(argbuf, g_strdup(buf + 2));
3085 if (sscanf(buf + 2, "%d %s", &num, data) >= 2) {
3086 if (!strcmp(data, "EXISTS")) {
3087 session->exists = num;
3088 session->folder_content_changed = TRUE;
3091 if(!strcmp(data, "EXPUNGE")) {
3093 session->folder_content_changed = TRUE;
3096 // tagged line with correct tag and OK response found
3097 } else if ((sscanf(buf, "%d %s", &cmd_num, data) >= 2) &&
3098 (cmd_num == session->cmd_count) &&
3099 !strcmp(data, "OK")) {
3101 g_ptr_array_add(argbuf, g_strdup(buf));
3115 static void imap_gen_send(IMAPSession *session, const gchar *format, ...)
3122 va_start(args, format);
3123 tmp = g_strdup_vprintf(format, args);
3126 session->cmd_count++;
3128 buf = g_strdup_printf("%d %s\r\n", session->cmd_count, tmp);
3129 if (!strncasecmp(tmp, "LOGIN ", 6) && (p = strchr(tmp + 6, ' '))) {
3131 log_print("IMAP4> %d %s ********\n", session->cmd_count, tmp);
3133 log_print("IMAP4> %d %s\n", session->cmd_count, tmp);
3135 sock_write_all(SESSION(session)->sock, buf, strlen(buf));
3140 static gint imap_gen_recv(IMAPSession *session, gchar **ret)
3142 if ((*ret = sock_getline(SESSION(session)->sock)) == NULL)
3147 log_print("IMAP4< %s\n", *ret);
3149 return IMAP_SUCCESS;
3153 /* misc utility functions */
3155 static gchar *strchr_cpy(const gchar *src, gchar ch, gchar *dest, gint len)
3160 tmp = strchr(src, ch);
3164 memcpy(dest, src, MIN(tmp - src, len - 1));
3165 dest[MIN(tmp - src, len - 1)] = '\0';
3170 static gchar *get_quoted(const gchar *src, gchar ch, gchar *dest, gint len)
3172 const gchar *p = src;
3175 g_return_val_if_fail(*p == ch, NULL);
3180 while (*p != '\0' && *p != ch) {
3182 if (*p == '\\' && *(p + 1) != '\0')
3191 return (gchar *)(*p == ch ? p + 1 : p);
3194 static gchar *search_array_contain_str(GPtrArray *array, const gchar *str)
3198 for (i = 0; i < array->len; i++) {
3201 tmp = g_ptr_array_index(array, i);
3202 if (strstr(tmp, str) != NULL)
3209 static gchar *search_array_str(GPtrArray *array, const gchar *str)
3216 for (i = 0; i < array->len; i++) {
3219 tmp = g_ptr_array_index(array, i);
3220 if (!strncmp(tmp, str, len))
3227 static void imap_path_separator_subst(gchar *str, gchar separator)
3230 gboolean in_escape = FALSE;
3232 if (!separator || separator == '/') return;
3234 for (p = str; *p != '\0'; p++) {
3235 if (*p == '/' && !in_escape)
3237 else if (*p == '&' && *(p + 1) != '-' && !in_escape)
3239 else if (*p == '-' && in_escape)
3244 static gchar *imap_modified_utf7_to_locale(const gchar *mutf7_str)
3247 const gchar *from_p;
3250 to = g_malloc(strlen(mutf7_str) + 1);
3253 for (from_p = mutf7_str; *from_p != '\0'; from_p++) {
3254 if (*from_p == '&' && *(from_p + 1) == '-') {
3264 static iconv_t cd = (iconv_t)-1;
3265 static gboolean iconv_ok = TRUE;
3268 size_t norm_utf7_len;
3270 gchar *to_str, *to_p;
3272 gboolean in_escape = FALSE;
3274 if (!iconv_ok) return g_strdup(mutf7_str);
3276 if (cd == (iconv_t)-1) {
3277 cd = iconv_open(conv_get_current_charset_str(), "UTF-7");
3278 if (cd == (iconv_t)-1) {
3279 g_warning("iconv cannot convert UTF-7 to %s\n",
3280 conv_get_current_charset_str());
3282 return g_strdup(mutf7_str);
3286 norm_utf7 = g_string_new(NULL);
3288 for (p = mutf7_str; *p != '\0'; p++) {
3289 /* replace: '&' -> '+',
3291 escaped ',' -> '/' */
3292 if (!in_escape && *p == '&') {
3293 if (*(p + 1) != '-') {
3294 g_string_append_c(norm_utf7, '+');
3297 g_string_append_c(norm_utf7, '&');
3300 } else if (in_escape && *p == ',') {
3301 g_string_append_c(norm_utf7, '/');
3302 } else if (in_escape && *p == '-') {
3303 g_string_append_c(norm_utf7, '-');
3306 g_string_append_c(norm_utf7, *p);
3310 norm_utf7_p = norm_utf7->str;
3311 norm_utf7_len = norm_utf7->len;
3312 to_len = strlen(mutf7_str) * 5;
3313 to_p = to_str = g_malloc(to_len + 1);
3315 if (iconv(cd, (ICONV_CONST gchar **)&norm_utf7_p, &norm_utf7_len,
3316 &to_p, &to_len) == -1) {
3317 g_warning(_("iconv cannot convert UTF-7 to %s\n"),
3318 conv_get_current_charset_str());
3319 g_string_free(norm_utf7, TRUE);
3321 return g_strdup(mutf7_str);
3324 /* second iconv() call for flushing */
3325 iconv(cd, NULL, NULL, &to_p, &to_len);
3326 g_string_free(norm_utf7, TRUE);
3330 #endif /* !HAVE_ICONV */
3333 static gchar *imap_locale_to_modified_utf7(const gchar *from)
3336 const gchar *from_p;
3339 to = g_malloc(strlen(from) * 2 + 1);
3342 for (from_p = from; *from_p != '\0'; from_p++) {
3343 if (*from_p == '&') {
3353 static iconv_t cd = (iconv_t)-1;
3354 static gboolean iconv_ok = TRUE;
3355 gchar *norm_utf7, *norm_utf7_p;
3356 size_t from_len, norm_utf7_len;
3358 gchar *from_tmp, *to, *p;
3359 gboolean in_escape = FALSE;
3361 if (!iconv_ok) return g_strdup(from);
3363 if (cd == (iconv_t)-1) {
3364 cd = iconv_open("UTF-7", conv_get_current_charset_str());
3365 if (cd == (iconv_t)-1) {
3366 g_warning("iconv cannot convert %s to UTF-7\n",
3367 conv_get_current_charset_str());
3369 return g_strdup(from);
3373 Xstrdup_a(from_tmp, from, return g_strdup(from));
3374 from_len = strlen(from);
3375 norm_utf7_len = from_len * 5;
3376 Xalloca(norm_utf7, norm_utf7_len + 1, return g_strdup(from));
3377 norm_utf7_p = norm_utf7;
3379 #define IS_PRINT(ch) (isprint(ch) && isascii(ch))
3381 while (from_len > 0) {
3382 if (*from_tmp == '+') {
3383 *norm_utf7_p++ = '+';
3384 *norm_utf7_p++ = '-';
3388 } else if (IS_PRINT(*from_tmp)) {
3389 /* printable ascii char */
3390 *norm_utf7_p = *from_tmp;
3396 size_t mb_len = 0, conv_len = 0;
3398 /* unprintable char: convert to UTF-7 */
3400 while (!IS_PRINT(*p) && conv_len < from_len) {
3401 mb_len = mblen(p, MB_LEN_MAX);
3403 g_warning("wrong multibyte sequence\n");
3404 return g_strdup(from);
3410 from_len -= conv_len;
3411 if (iconv(cd, (ICONV_CONST gchar **)&from_tmp,
3413 &norm_utf7_p, &norm_utf7_len) == -1) {
3414 g_warning("iconv cannot convert %s to UTF-7\n",
3415 conv_get_current_charset_str());
3416 return g_strdup(from);
3419 /* second iconv() call for flushing */
3420 iconv(cd, NULL, NULL, &norm_utf7_p, &norm_utf7_len);
3426 *norm_utf7_p = '\0';
3427 to_str = g_string_new(NULL);
3428 for (p = norm_utf7; p < norm_utf7_p; p++) {
3429 /* replace: '&' -> "&-",
3432 BASE64 '/' -> ',' */
3433 if (!in_escape && *p == '&') {
3434 g_string_append(to_str, "&-");
3435 } else if (!in_escape && *p == '+') {
3436 if (*(p + 1) == '-') {
3437 g_string_append_c(to_str, '+');
3440 g_string_append_c(to_str, '&');
3443 } else if (in_escape && *p == '/') {
3444 g_string_append_c(to_str, ',');
3445 } else if (in_escape && *p == '-') {
3446 g_string_append_c(to_str, '-');
3449 g_string_append_c(to_str, *p);
3455 g_string_append_c(to_str, '-');
3459 g_string_free(to_str, FALSE);
3462 #endif /* !HAVE_ICONV */
3465 static GSList *imap_get_seq_set_from_numlist(MsgNumberList *numlist)
3468 GSList *sorted_list, *cur;
3469 guint first, last, next;
3471 GSList *ret_list = NULL;
3473 if (numlist == NULL)
3476 str = g_string_sized_new(256);
3478 sorted_list = g_slist_copy(numlist);
3479 sorted_list = g_slist_sort(sorted_list, g_int_compare);
3481 first = GPOINTER_TO_INT(sorted_list->data);
3483 for (cur = sorted_list; cur != NULL; cur = g_slist_next(cur)) {
3484 last = GPOINTER_TO_INT(cur->data);
3486 next = GPOINTER_TO_INT(cur->next->data);
3490 if (last + 1 != next || next == 0) {
3492 g_string_append_c(str, ',');
3494 g_string_sprintfa(str, "%u", first);
3496 g_string_sprintfa(str, "%u:%u", first, last);
3500 if (str->len > IMAP_CMD_LIMIT) {
3501 ret_str = g_strdup(str->str);
3502 ret_list = g_slist_append(ret_list, ret_str);
3503 g_string_truncate(str, 0);
3509 ret_str = g_strdup(str->str);
3510 ret_list = g_slist_append(ret_list, ret_str);
3513 g_slist_free(sorted_list);
3514 g_string_free(str, TRUE);
3519 static GSList *imap_get_seq_set_from_msglist(MsgInfoList *msglist)
3521 MsgNumberList *numlist = NULL;
3525 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
3526 MsgInfo *msginfo = (MsgInfo *) cur->data;
3528 numlist = g_slist_append(numlist, GINT_TO_POINTER(msginfo->msgnum));
3530 seq_list = imap_get_seq_set_from_numlist(numlist);
3531 g_slist_free(numlist);
3536 static void imap_seq_set_free(GSList *seq_list)
3538 slist_free_strings(seq_list);
3539 g_slist_free(seq_list);
3543 static gboolean imap_rename_folder_func(GNode *node, gpointer data)
3545 FolderItem *item = node->data;
3546 gchar **paths = data;
3547 const gchar *oldpath = paths[0];
3548 const gchar *newpath = paths[1];
3550 gchar *new_itempath;
3553 oldpathlen = strlen(oldpath);
3554 if (strncmp(oldpath, item->path, oldpathlen) != 0) {
3555 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
3559 base = item->path + oldpathlen;
3560 while (*base == G_DIR_SEPARATOR) base++;
3562 new_itempath = g_strdup(newpath);
3564 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
3567 item->path = new_itempath;
3572 static gint get_list_of_uids(Folder *folder, IMAPFolderItem *item, GSList **msgnum_list)
3574 gint ok, nummsgs = 0, lastuid_old;
3575 IMAPSession *session;
3576 GSList *uidlist, *elem;
3579 session = imap_session_get(folder);
3580 g_return_val_if_fail(session != NULL, -1);
3582 ok = imap_select(session, IMAP_FOLDER(folder), item->item.path,
3583 NULL, NULL, NULL, NULL);
3584 if (ok != IMAP_SUCCESS)
3587 cmd_buf = g_strdup_printf("UID %d:*", item->lastuid + 1);
3588 ok = imap_cmd_search(session, cmd_buf, &uidlist);
3591 if (ok == IMAP_SOCKET) {
3592 session_destroy((Session *)session);
3593 ((RemoteFolder *)folder)->session = NULL;
3597 if (ok != IMAP_SUCCESS) {
3601 argbuf = g_ptr_array_new();
3603 cmd_buf = g_strdup_printf("UID FETCH %d:* (UID)", item->lastuid + 1);
3604 imap_gen_send(session, cmd_buf);
3606 ok = imap_cmd_ok(session, argbuf);
3607 if (ok != IMAP_SUCCESS) {
3608 ptr_array_free_strings(argbuf);
3609 g_ptr_array_free(argbuf, TRUE);
3613 for(i = 0; i < argbuf->len; i++) {
3616 if((ret = sscanf(g_ptr_array_index(argbuf, i),
3617 "%*d FETCH (UID %d)", &msgnum)) == 1)
3618 uidlist = g_slist_prepend(uidlist, GINT_TO_POINTER(msgnum));
3620 ptr_array_free_strings(argbuf);
3621 g_ptr_array_free(argbuf, TRUE);
3624 lastuid_old = item->lastuid;
3625 *msgnum_list = g_slist_copy(item->uid_list);
3626 nummsgs = g_slist_length(*msgnum_list);
3627 debug_print("Got %d uids from cache\n", g_slist_length(item->uid_list));
3629 for (elem = uidlist; elem != NULL; elem = g_slist_next(elem)) {
3632 msgnum = GPOINTER_TO_INT(elem->data);
3633 if (msgnum > lastuid_old) {
3634 *msgnum_list = g_slist_prepend(*msgnum_list, GINT_TO_POINTER(msgnum));
3635 item->uid_list = g_slist_prepend(item->uid_list, GINT_TO_POINTER(msgnum));
3638 if(msgnum > item->lastuid)
3639 item->lastuid = msgnum;
3642 g_slist_free(uidlist);
3647 gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list, gboolean *old_uids_valid)
3649 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3650 IMAPSession *session;
3651 gint ok, nummsgs = 0, exists, recent, uid_val, uid_next, unseen;
3654 gboolean selected_folder;
3656 g_return_val_if_fail(folder != NULL, -1);
3657 g_return_val_if_fail(item != NULL, -1);
3658 g_return_val_if_fail(item->item.path != NULL, -1);
3659 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3660 g_return_val_if_fail(folder->account != NULL, -1);
3662 session = imap_session_get(folder);
3663 g_return_val_if_fail(session != NULL, -1);
3665 selected_folder = (session->mbox != NULL) &&
3666 (!strcmp(session->mbox, item->item.path));
3667 if (selected_folder) {
3668 ok = imap_cmd_noop(session);
3669 if (ok != IMAP_SUCCESS)
3671 exists = session->exists;
3673 *old_uids_valid = TRUE;
3675 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
3676 &exists, &recent, &uid_next, &uid_val, &unseen);
3677 if (ok != IMAP_SUCCESS)
3680 if(item->item.mtime == uid_val)
3681 *old_uids_valid = TRUE;
3683 *old_uids_valid = FALSE;
3685 debug_print("Freeing imap uid cache\n");
3687 g_slist_free(item->uid_list);
3688 item->uid_list = NULL;
3690 item->item.mtime = uid_val;
3692 imap_delete_all_cached_messages((FolderItem *)item);
3696 if (!selected_folder)
3697 item->uid_next = uid_next;
3699 /* If old uid_next matches new uid_next we can be sure no message
3700 was added to the folder */
3701 if (( selected_folder && !session->folder_content_changed) ||
3702 (!selected_folder && uid_next == item->uid_next)) {
3703 nummsgs = g_slist_length(item->uid_list);
3705 /* If number of messages is still the same we
3706 know our caches message numbers are still valid,
3707 otherwise if the number of messages has decrease
3708 we discard our cache to start a new scan to find
3709 out which numbers have been removed */
3710 if (exists == nummsgs) {
3711 *msgnum_list = g_slist_copy(item->uid_list);
3713 } else if (exists < nummsgs) {
3714 debug_print("Freeing imap uid cache");
3716 g_slist_free(item->uid_list);
3717 item->uid_list = NULL;
3722 *msgnum_list = NULL;
3726 nummsgs = get_list_of_uids(folder, item, &uidlist);
3728 if (nummsgs != exists) {
3729 /* Cache contains more messages then folder, we have cached
3730 an old UID of a message that was removed and new messages
3731 have been added too, otherwise the uid_next check would
3733 debug_print("Freeing imap uid cache");
3735 g_slist_free(item->uid_list);
3736 item->uid_list = NULL;
3738 g_slist_free(*msgnum_list);
3740 nummsgs = get_list_of_uids(folder, item, &uidlist);
3743 *msgnum_list = uidlist;
3745 dir = folder_item_get_path((FolderItem *)item);
3746 debug_print("removing old messages from %s\n", dir);
3747 remove_numbered_files_not_in_list(dir, *msgnum_list);
3753 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item)
3758 flags.perm_flags = MSG_NEW|MSG_UNREAD;
3759 flags.tmp_flags = 0;
3761 g_return_val_if_fail(item != NULL, NULL);
3762 g_return_val_if_fail(file != NULL, NULL);
3764 if (item->stype == F_QUEUE) {
3765 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
3766 } else if (item->stype == F_DRAFT) {
3767 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
3770 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3771 if (!msginfo) return NULL;
3773 msginfo->folder = item;
3778 GSList *imap_get_msginfos(Folder *folder, FolderItem *item, GSList *msgnum_list)
3780 IMAPSession *session;
3781 MsgInfoList *ret = NULL;
3784 g_return_val_if_fail(folder != NULL, NULL);
3785 g_return_val_if_fail(item != NULL, NULL);
3786 g_return_val_if_fail(msgnum_list != NULL, NULL);
3788 session = imap_session_get(folder);
3789 g_return_val_if_fail(session != NULL, NULL);
3791 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3792 NULL, NULL, NULL, NULL);
3793 if (ok != IMAP_SUCCESS)
3796 if (!(item->stype == F_QUEUE || item->stype == F_DRAFT)) {
3797 ret = g_slist_concat(ret,
3798 imap_get_uncached_messages(
3799 session, item, msgnum_list));
3801 MsgNumberList *sorted_list, *elem;
3802 gint startnum, lastnum;
3804 sorted_list = g_slist_sort(g_slist_copy(msgnum_list), g_int_compare);
3806 startnum = lastnum = GPOINTER_TO_INT(sorted_list->data);
3808 for (elem = sorted_list;; elem = g_slist_next(elem)) {
3812 num = GPOINTER_TO_INT(elem->data);
3814 if (num > lastnum + 1 || elem == NULL) {
3816 for (i = startnum; i <= lastnum; ++i) {
3819 file = imap_fetch_msg(folder, item, i);
3821 MsgInfo *msginfo = imap_parse_msg(file, item);
3822 if (msginfo != NULL) {
3823 msginfo->msgnum = i;
3824 ret = g_slist_append(ret, msginfo);
3838 g_slist_free(sorted_list);
3844 MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid)
3846 MsgInfo *msginfo = NULL;
3847 MsgInfoList *msginfolist;
3848 MsgNumberList numlist;
3850 numlist.next = NULL;
3851 numlist.data = GINT_TO_POINTER(uid);
3853 msginfolist = imap_get_msginfos(folder, item, &numlist);
3854 if (msginfolist != NULL) {
3855 msginfo = msginfolist->data;
3856 g_slist_free(msginfolist);
3862 gboolean imap_scan_required(Folder *folder, FolderItem *_item)
3864 IMAPSession *session;
3865 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3866 gint ok, exists = 0, recent = 0, unseen = 0;
3867 guint32 uid_next, uid_val = 0;
3868 gboolean selected_folder;
3870 g_return_val_if_fail(folder != NULL, FALSE);
3871 g_return_val_if_fail(item != NULL, FALSE);
3872 g_return_val_if_fail(item->item.folder != NULL, FALSE);
3873 g_return_val_if_fail(FOLDER_CLASS(item->item.folder) == &imap_class, FALSE);
3875 if (item->item.path == NULL)
3878 session = imap_session_get(folder);
3879 g_return_val_if_fail(session != NULL, FALSE);
3881 selected_folder = (session->mbox != NULL) &&
3882 (!strcmp(session->mbox, item->item.path));
3883 if (selected_folder) {
3884 ok = imap_cmd_noop(session);
3885 if (ok != IMAP_SUCCESS)
3888 if (session->folder_content_changed)
3891 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
3892 &exists, &recent, &uid_next, &uid_val, &unseen);
3893 if (ok != IMAP_SUCCESS)
3896 if ((uid_next != item->uid_next) || (exists < item->item.total_msgs))
3903 void imap_change_flags(Folder *folder, FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
3905 IMAPSession *session;
3906 IMAPFlags flags_set = 0, flags_unset = 0;
3907 gint ok = IMAP_SUCCESS;
3908 MsgNumberList numlist;
3910 g_return_if_fail(folder != NULL);
3911 g_return_if_fail(folder->klass == &imap_class);
3912 g_return_if_fail(item != NULL);
3913 g_return_if_fail(item->folder == folder);
3914 g_return_if_fail(msginfo != NULL);
3915 g_return_if_fail(msginfo->folder == item);
3917 session = imap_session_get(folder);
3918 if (!session) return;
3920 if ((ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
3921 NULL, NULL, NULL, NULL)) != IMAP_SUCCESS)
3924 if (!MSG_IS_MARKED(msginfo->flags) && (newflags & MSG_MARKED))
3925 flags_set |= IMAP_FLAG_FLAGGED;
3926 if ( MSG_IS_MARKED(msginfo->flags) && !(newflags & MSG_MARKED))
3927 flags_unset |= IMAP_FLAG_FLAGGED;
3929 if (!MSG_IS_UNREAD(msginfo->flags) && (newflags & MSG_UNREAD))
3930 flags_unset |= IMAP_FLAG_SEEN;
3931 if ( MSG_IS_UNREAD(msginfo->flags) && !(newflags & MSG_UNREAD))
3932 flags_set |= IMAP_FLAG_SEEN;
3934 if (!MSG_IS_REPLIED(msginfo->flags) && (newflags & MSG_REPLIED))
3935 flags_set |= IMAP_FLAG_ANSWERED;
3936 if ( MSG_IS_REPLIED(msginfo->flags) && !(newflags & MSG_REPLIED))
3937 flags_set |= IMAP_FLAG_ANSWERED;
3939 numlist.next = NULL;
3940 numlist.data = GINT_TO_POINTER(msginfo->msgnum);
3943 ok = imap_set_message_flags(session, &numlist, flags_set, TRUE);
3944 if (ok != IMAP_SUCCESS) return;
3948 ok = imap_set_message_flags(session, &numlist, flags_unset, FALSE);
3949 if (ok != IMAP_SUCCESS) return;
3952 msginfo->flags.perm_flags = newflags;