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_FOLDER_ITEM(obj) ((IMAPFolderItem *)obj)
67 #define IMAP_SESSION(obj) ((IMAPSession *)obj)
73 /* list of IMAPNameSpace */
83 gboolean authenticated;
92 time_t last_access_time;
93 gboolean folder_content_changed;
103 #define IMAP_SUCCESS 0
104 #define IMAP_SOCKET 2
105 #define IMAP_AUTHFAIL 3
106 #define IMAP_PROTOCOL 4
107 #define IMAP_SYNTAX 5
111 #define IMAPBUFSIZE 8192
115 IMAP_FLAG_SEEN = 1 << 0,
116 IMAP_FLAG_ANSWERED = 1 << 1,
117 IMAP_FLAG_FLAGGED = 1 << 2,
118 IMAP_FLAG_DELETED = 1 << 3,
119 IMAP_FLAG_DRAFT = 1 << 4
122 #define IMAP_IS_SEEN(flags) ((flags & IMAP_FLAG_SEEN) != 0)
123 #define IMAP_IS_ANSWERED(flags) ((flags & IMAP_FLAG_ANSWERED) != 0)
124 #define IMAP_IS_FLAGGED(flags) ((flags & IMAP_FLAG_FLAGGED) != 0)
125 #define IMAP_IS_DELETED(flags) ((flags & IMAP_FLAG_DELETED) != 0)
126 #define IMAP_IS_DRAFT(flags) ((flags & IMAP_FLAG_DRAFT) != 0)
129 #define IMAP4_PORT 143
131 #define IMAPS_PORT 993
134 #define IMAP_CMD_LIMIT 1000
136 #define QUOTE_IF_REQUIRED(out, str) \
138 if (*str != '"' && strpbrk(str, " \t(){}%*") != NULL) { \
142 len = strlen(str) + 3; \
143 Xalloca(__tmp, len, return IMAP_ERROR); \
144 g_snprintf(__tmp, len, "\"%s\"", str); \
147 Xstrdup_a(out, str, return IMAP_ERROR); \
151 typedef gchar * IMAPSet;
153 struct _IMAPFolderItem
162 static Folder *imap_folder_new(const gchar * name, const gchar * path);
163 static void imap_folder_destroy(Folder * folder);
165 static IMAPSession *imap_session_new(const PrefsAccount * account);
166 static void imap_session_authenticate(IMAPSession * session,
167 const PrefsAccount * account);
168 static void imap_session_destroy(Session * session);
170 static gchar *imap_fetch_msg(Folder * folder, FolderItem * item, gint uid);
171 static gint imap_add_msg(Folder * folder,
173 const gchar * file, MsgFlags * flags);
174 static gint imap_add_msgs(Folder * folder, FolderItem * dest,
176 GRelation *relation);
178 static gint imap_copy_msg(Folder * folder,
179 FolderItem * dest, MsgInfo * msginfo);
180 static gint imap_copy_msgs(Folder *folder, FolderItem *dest,
181 MsgInfoList *msglist, GRelation *relation);
183 static gint imap_remove_msg(Folder * folder, FolderItem * item, gint uid);
184 static gint imap_remove_all_msg(Folder * folder, FolderItem * item);
186 static gboolean imap_is_msg_changed(Folder * folder,
187 FolderItem * item, MsgInfo * msginfo);
189 static gint imap_close(Folder * folder, FolderItem * item);
191 static gint imap_scan_tree(Folder * folder);
193 static gint imap_create_tree(Folder * folder);
195 static FolderItem *imap_create_folder(Folder * folder,
198 static gint imap_rename_folder(Folder * folder,
199 FolderItem * item, const gchar * name);
200 static gint imap_remove_folder(Folder * folder, FolderItem * item);
203 static void imap_folder_init (Folder *folder,
207 static FolderItem *imap_folder_item_new (Folder *folder);
208 static void imap_folder_item_destroy (Folder *folder,
211 static IMAPSession *imap_session_get (Folder *folder);
213 static gint imap_greeting (IMAPSession *session);
214 static gint imap_auth (IMAPSession *session,
219 static gint imap_scan_tree_recursive (IMAPSession *session,
221 static GSList *imap_parse_list (IMAPFolder *folder,
222 IMAPSession *session,
223 const gchar *real_path,
226 static void imap_create_missing_folders (Folder *folder);
227 static FolderItem *imap_create_special_folder
229 SpecialFolderItemType stype,
232 static gint imap_do_copy_msgs (Folder *folder,
234 MsgInfoList *msglist,
235 GRelation *relation);
237 static void imap_delete_all_cached_messages (FolderItem *item);
240 static SockInfo *imap_open (const gchar *server,
244 static SockInfo *imap_open (const gchar *server,
249 static SockInfo *imap_open_tunnel(const gchar *server,
250 const gchar *tunnelcmd,
253 static SockInfo *imap_open_tunnel(const gchar *server,
254 const gchar *tunnelcmd);
258 static SockInfo *imap_init_sock(SockInfo *sock, SSLType ssl_type);
260 static SockInfo *imap_init_sock(SockInfo *sock);
263 static gchar *imap_get_flag_str (IMAPFlags flags);
264 static gint imap_set_message_flags (IMAPSession *session,
265 MsgNumberList *numlist,
268 static gint imap_select (IMAPSession *session,
274 guint32 *uid_validity);
275 static gint imap_status (IMAPSession *session,
281 guint32 *uid_validity,
284 static void imap_parse_namespace (IMAPSession *session,
286 static void imap_get_namespace_by_list (IMAPSession *session,
288 static IMAPNameSpace *imap_find_namespace (IMAPFolder *folder,
290 static gchar imap_get_path_separator (IMAPFolder *folder,
292 static gchar *imap_get_real_path (IMAPFolder *folder,
295 static gchar *imap_parse_atom (SockInfo *sock,
300 static MsgFlags imap_parse_flags (const gchar *flag_str);
301 static MsgInfo *imap_parse_envelope (SockInfo *sock,
305 static gboolean imap_has_capability (IMAPSession *session,
307 static void imap_free_capabilities (IMAPSession *session);
309 /* low-level IMAP4rev1 commands */
310 static gint imap_cmd_authenticate
311 (IMAPSession *session,
315 static gint imap_cmd_login (IMAPSession *sock,
318 static gint imap_cmd_logout (IMAPSession *sock);
319 static gint imap_cmd_noop (IMAPSession *sock);
320 static gint imap_cmd_starttls (IMAPSession *sock);
321 static gint imap_cmd_namespace (IMAPSession *sock,
323 static gint imap_cmd_list (IMAPSession *session,
325 const gchar *mailbox,
327 static gint imap_cmd_do_select (IMAPSession *sock,
333 guint32 *uid_validity);
334 static gint imap_cmd_select (IMAPSession *sock,
339 guint32 *uid_validity);
340 static gint imap_cmd_examine (IMAPSession *sock,
345 guint32 *uid_validity);
346 static gint imap_cmd_create (IMAPSession *sock,
347 const gchar *folder);
348 static gint imap_cmd_rename (IMAPSession *sock,
349 const gchar *oldfolder,
350 const gchar *newfolder);
351 static gint imap_cmd_delete (IMAPSession *sock,
352 const gchar *folder);
353 static gint imap_cmd_envelope (IMAPSession *sock,
355 static gint imap_cmd_fetch (IMAPSession *sock,
357 const gchar *filename);
358 static gint imap_cmd_append (IMAPSession *session,
359 const gchar *destfolder,
363 static gint imap_cmd_copy (IMAPSession *session,
364 const gchar *seq_set,
365 const gchar *destfolder,
366 GRelation *uid_mapping);
367 static gint imap_cmd_store (IMAPSession *sock,
370 static gint imap_cmd_expunge (IMAPSession *sock);
371 static gint imap_cmd_close (IMAPSession *session);
373 static gint imap_cmd_ok (IMAPSession *session,
375 static void imap_gen_send (IMAPSession *sock,
376 const gchar *format, ...);
377 static gint imap_gen_recv (IMAPSession *sock,
380 /* misc utility functions */
381 static gchar *strchr_cpy (const gchar *src,
385 static gchar *get_quoted (const gchar *src,
389 static gchar *search_array_contain_str (GPtrArray *array,
391 static gchar *search_array_str (GPtrArray *array,
393 static void imap_path_separator_subst (gchar *str,
396 static gchar *imap_modified_utf7_to_locale (const gchar *mutf7_str);
397 static gchar *imap_locale_to_modified_utf7 (const gchar *from);
399 static GSList *imap_get_seq_set_from_numlist (MsgNumberList *msglist);
400 static GSList *imap_get_seq_set_from_msglist (MsgInfoList *msglist);
401 static void imap_seq_set_free (GSList *seq_list);
403 static gboolean imap_rename_folder_func (GNode *node,
405 static gint imap_get_num_list (Folder *folder,
408 gboolean *old_uids_valid);
409 static GSList *imap_get_msginfos (Folder *folder,
411 GSList *msgnum_list);
412 static MsgInfo *imap_get_msginfo (Folder *folder,
415 static gboolean imap_scan_required (Folder *folder,
417 static void imap_change_flags (Folder *folder,
420 MsgPermFlags newflags);
421 static gchar *imap_folder_get_path (Folder *folder);
422 static gchar *imap_item_get_path (Folder *folder,
425 FolderClass imap_class =
431 /* Folder functions */
437 /* FolderItem functions */
438 imap_folder_item_new,
439 imap_folder_item_destroy,
451 /* Message functions */
465 FolderClass *imap_get_class(void)
470 Folder *imap_folder_new(const gchar *name, const gchar *path)
474 folder = (Folder *)g_new0(IMAPFolder, 1);
475 folder->klass = &imap_class;
476 imap_folder_init(folder, name, path);
481 void imap_folder_destroy(Folder *folder)
485 dir = imap_folder_get_path(folder);
486 if (is_dir_exist(dir))
487 remove_dir_recursive(dir);
490 folder_remote_folder_destroy(REMOTE_FOLDER(folder));
493 static void imap_folder_init(Folder *folder, const gchar *name,
496 folder_remote_folder_init((Folder *)folder, name, path);
499 static FolderItem *imap_folder_item_new(Folder *folder)
501 IMAPFolderItem *item;
503 item = g_new0(IMAPFolderItem, 1);
506 item->uid_list = NULL;
508 return (FolderItem *)item;
511 static void imap_folder_item_destroy(Folder *folder, FolderItem *_item)
513 IMAPFolderItem *item = (IMAPFolderItem *)_item;
515 g_return_if_fail(item != NULL);
516 g_slist_free(item->uid_list);
521 static gboolean imap_reset_uid_lists_func(GNode *node, gpointer data)
523 IMAPFolderItem *item = (IMAPFolderItem *)node->data;
527 g_slist_free(item->uid_list);
528 item->uid_list = NULL;
533 static void imap_reset_uid_lists(Folder *folder)
535 if(folder->node == NULL)
538 /* Destroy all uid lists and rest last uid */
539 g_node_traverse(folder->node, G_IN_ORDER, G_TRAVERSE_ALL, -1, imap_reset_uid_lists_func, NULL);
542 /* Send CAPABILITY, and examine the server's response to see whether this
543 * connection is pre-authenticated or not and build a list of CAPABILITIES. */
544 static gint imap_greeting(IMAPSession *session)
549 imap_gen_send(session, "CAPABILITY");
551 argbuf = g_ptr_array_new();
553 if (imap_cmd_ok(session, argbuf) != IMAP_SUCCESS ||
554 ((capstr = search_array_str(argbuf, "CAPABILITY ")) == NULL)) {
555 ptr_array_free_strings(argbuf);
556 g_ptr_array_free(argbuf, TRUE);
560 session->authenticated = search_array_str(argbuf, "PREAUTH") != NULL;
562 capstr += strlen("CAPABILITY ");
564 IMAP_SESSION(session)->capability = g_strsplit(capstr, " ", 0);
566 ptr_array_free_strings(argbuf);
567 g_ptr_array_free(argbuf, TRUE);
569 if (imap_has_capability(session, "UIDPLUS"))
570 session->uidplus = TRUE;
575 static gint imap_auth(IMAPSession *session, const gchar *user, const gchar *pass,
580 if (type == 0 || type == IMAP_AUTH_LOGIN)
581 ok = imap_cmd_login(session, user, pass);
583 ok = imap_cmd_authenticate(session, user, pass, type);
585 if (ok == IMAP_SUCCESS)
586 session->authenticated = TRUE;
591 static IMAPSession *imap_session_get(Folder *folder)
593 RemoteFolder *rfolder = REMOTE_FOLDER(folder);
594 IMAPSession *session = NULL;
596 g_return_val_if_fail(folder != NULL, NULL);
597 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, NULL);
598 g_return_val_if_fail(folder->account != NULL, NULL);
600 /* Make sure we have a session */
601 if (rfolder->session != NULL) {
602 session = IMAP_SESSION(rfolder->session);
604 imap_reset_uid_lists(folder);
605 session = imap_session_new(folder->account);
610 if (SESSION(session)->sock->state == CONN_DISCONNECTED) {
611 debug_print("IMAP server disconnected\n");
612 session_destroy(SESSION(session));
613 imap_reset_uid_lists(folder);
614 session = imap_session_new(folder->account);
617 /* Make sure session is authenticated */
618 if (!IMAP_SESSION(session)->authenticated)
619 imap_session_authenticate(IMAP_SESSION(session), folder->account);
620 if (!IMAP_SESSION(session)->authenticated) {
621 session_destroy(SESSION(session));
622 rfolder->session = NULL;
626 /* Make sure we have parsed the IMAP namespace */
627 imap_parse_namespace(IMAP_SESSION(session),
628 IMAP_FOLDER(folder));
630 /* I think the point of this code is to avoid sending a
631 * keepalive if we've used the session recently and therefore
632 * think it's still alive. Unfortunately, most of the code
633 * does not yet check for errors on the socket, and so if the
634 * connection drops we don't notice until the timeout expires.
635 * A better solution than sending a NOOP every time would be
636 * for every command to be prepared to retry until it is
637 * successfully sent. -- mbp */
638 if (time(NULL) - session->last_access_time > SESSION_TIMEOUT) {
639 /* verify that the session is still alive */
640 if (imap_cmd_noop(session) != IMAP_SUCCESS) {
641 /* Check if this is the first try to establish a
642 connection, if yes we don't try to reconnect */
643 if (rfolder->session == NULL) {
644 log_warning(_("Connecting %s failed"),
645 folder->account->recv_server);
646 session_destroy(SESSION(session));
649 log_warning(_("IMAP4 connection to %s has been"
650 " disconnected. Reconnecting...\n"),
651 folder->account->recv_server);
652 session_destroy(SESSION(session));
653 /* Clear folders session to make imap_session_get create
654 a new session, because of rfolder->session == NULL
655 it will not try to reconnect again and so avoid an
657 rfolder->session = NULL;
658 session = imap_session_get(folder);
663 rfolder->session = SESSION(session);
665 session->last_access_time = time(NULL);
667 return IMAP_SESSION(session);
670 IMAPSession *imap_session_new(const PrefsAccount *account)
672 IMAPSession *session;
677 /* FIXME: IMAP over SSL only... */
680 port = account->set_imapport ? account->imapport
681 : account->ssl_imap == SSL_TUNNEL ? IMAPS_PORT : IMAP4_PORT;
682 ssl_type = account->ssl_imap;
684 port = account->set_imapport ? account->imapport
688 if (account->set_tunnelcmd) {
689 log_message(_("creating tunneled IMAP4 connection\n"));
691 if ((imap_sock = imap_open_tunnel(account->recv_server,
695 if ((imap_sock = imap_open_tunnel(account->recv_server,
696 account->tunnelcmd)) == NULL)
700 g_return_val_if_fail(account->recv_server != NULL, NULL);
702 log_message(_("creating IMAP4 connection to %s:%d ...\n"),
703 account->recv_server, port);
706 if ((imap_sock = imap_open(account->recv_server, port,
709 if ((imap_sock = imap_open(account->recv_server, port)) == NULL)
714 session = g_new0(IMAPSession, 1);
715 session_init(SESSION(session));
716 SESSION(session)->type = SESSION_IMAP;
717 SESSION(session)->server = g_strdup(account->recv_server);
718 SESSION(session)->sock = imap_sock;
720 SESSION(session)->destroy = imap_session_destroy;
722 session->capability = NULL;
724 session->authenticated = FALSE;
725 session->mbox = NULL;
726 session->cmd_count = 0;
728 /* Only need to log in if the connection was not PREAUTH */
729 if (imap_greeting(session) != IMAP_SUCCESS) {
730 session_destroy(SESSION(session));
735 if (account->ssl_imap == SSL_STARTTLS &&
736 imap_has_capability(session, "STARTTLS")) {
739 ok = imap_cmd_starttls(session);
740 if (ok != IMAP_SUCCESS) {
741 log_warning(_("Can't start TLS session.\n"));
742 session_destroy(SESSION(session));
745 if (!ssl_init_socket_with_method(SESSION(session)->sock, SSL_METHOD_TLSv1)) {
746 session_destroy(SESSION(session));
750 imap_free_capabilities(session);
751 session->authenticated = FALSE;
752 session->uidplus = FALSE;
753 session->cmd_count = 1;
755 if (imap_greeting(session) != IMAP_SUCCESS) {
756 session_destroy(SESSION(session));
761 log_message("IMAP connection is %s-authenticated\n",
762 (session->authenticated) ? "pre" : "un");
767 void imap_session_authenticate(IMAPSession *session, const PrefsAccount *account)
771 g_return_if_fail(account->userid != NULL);
773 pass = account->passwd;
776 tmp_pass = input_dialog_query_password(account->recv_server, account->userid);
779 Xstrdup_a(pass, tmp_pass, {g_free(tmp_pass); return;});
783 if (imap_auth(session, account->userid, pass, account->imap_auth_type) != IMAP_SUCCESS) {
784 imap_cmd_logout(session);
788 session->authenticated = TRUE;
791 void imap_session_destroy(Session *session)
793 imap_free_capabilities(IMAP_SESSION(session));
794 g_free(IMAP_SESSION(session)->mbox);
795 sock_close(session->sock);
796 session->sock = NULL;
799 gchar *imap_fetch_msg(Folder *folder, FolderItem *item, gint uid)
801 gchar *path, *filename;
802 IMAPSession *session;
805 g_return_val_if_fail(folder != NULL, NULL);
806 g_return_val_if_fail(item != NULL, NULL);
808 path = folder_item_get_path(item);
809 if (!is_dir_exist(path))
811 filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(uid), NULL);
814 if (is_file_exist(filename)) {
815 debug_print("message %d has been already cached.\n", uid);
819 session = imap_session_get(folder);
825 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
826 NULL, NULL, NULL, NULL);
827 if (ok != IMAP_SUCCESS) {
828 g_warning("can't select mailbox %s\n", item->path);
833 debug_print("getting message %d...\n", uid);
834 ok = imap_cmd_fetch(session, (guint32)uid, filename);
836 if (ok != IMAP_SUCCESS) {
837 g_warning("can't fetch message %d\n", uid);
845 gint imap_add_msg(Folder *folder, FolderItem *dest, const gchar *file, MsgFlags *flags)
849 MsgFileInfo fileinfo;
851 g_return_val_if_fail(file != NULL, -1);
853 fileinfo.msginfo = NULL;
854 fileinfo.file = (gchar *)file;
855 fileinfo.flags = flags;
856 file_list.data = &fileinfo;
857 file_list.next = NULL;
859 ret = imap_add_msgs(folder, dest, &file_list, NULL);
863 gint imap_add_msgs(Folder *folder, FolderItem *dest, GSList *file_list,
867 IMAPSession *session;
868 guint32 last_uid = 0;
870 MsgFileInfo *fileinfo;
873 g_return_val_if_fail(folder != NULL, -1);
874 g_return_val_if_fail(dest != NULL, -1);
875 g_return_val_if_fail(file_list != NULL, -1);
877 session = imap_session_get(folder);
878 if (!session) return -1;
880 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
882 for (cur = file_list; cur != NULL; cur = cur->next) {
883 IMAPFlags iflags = 0;
886 fileinfo = (MsgFileInfo *)cur->data;
888 if (fileinfo->flags) {
889 if (MSG_IS_MARKED(*fileinfo->flags))
890 iflags |= IMAP_FLAG_FLAGGED;
891 if (MSG_IS_REPLIED(*fileinfo->flags))
892 iflags |= IMAP_FLAG_ANSWERED;
893 if (!MSG_IS_UNREAD(*fileinfo->flags))
894 iflags |= IMAP_FLAG_SEEN;
897 if (dest->stype == F_OUTBOX ||
898 dest->stype == F_QUEUE ||
899 dest->stype == F_DRAFT ||
900 dest->stype == F_TRASH)
901 iflags |= IMAP_FLAG_SEEN;
903 ok = imap_cmd_append(session, destdir, fileinfo->file, iflags,
906 if (ok != IMAP_SUCCESS) {
907 g_warning("can't append message %s\n", fileinfo->file);
912 if (relation != NULL)
913 g_relation_insert(relation, fileinfo->msginfo != NULL ?
914 (gpointer) fileinfo->msginfo : (gpointer) fileinfo,
915 GINT_TO_POINTER(dest->last_num + 1));
916 if (last_uid < new_uid)
925 static gint imap_do_copy_msgs(Folder *folder, FolderItem *dest,
926 MsgInfoList *msglist, GRelation *relation)
930 GSList *seq_list, *cur;
932 IMAPSession *session;
933 gint ok = IMAP_SUCCESS;
934 GRelation *uid_mapping;
937 g_return_val_if_fail(folder != NULL, -1);
938 g_return_val_if_fail(dest != NULL, -1);
939 g_return_val_if_fail(msglist != NULL, -1);
941 session = imap_session_get(folder);
942 if (!session) return -1;
944 msginfo = (MsgInfo *)msglist->data;
946 src = msginfo->folder;
948 g_warning("the src folder is identical to the dest.\n");
952 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
953 NULL, NULL, NULL, NULL);
954 if (ok != IMAP_SUCCESS)
957 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
958 seq_list = imap_get_seq_set_from_msglist(msglist);
959 uid_mapping = g_relation_new(2);
960 g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
962 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
963 gchar *seq_set = (gchar *)cur->data;
965 debug_print("Copying message %s%c[%s] to %s ...\n",
966 src->path, G_DIR_SEPARATOR,
969 ok = imap_cmd_copy(session, seq_set, destdir, uid_mapping);
970 if (ok != IMAP_SUCCESS) {
971 g_relation_destroy(uid_mapping);
972 imap_seq_set_free(seq_list);
977 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
978 MsgInfo *msginfo = (MsgInfo *)cur->data;
981 tuples = g_relation_select(uid_mapping,
982 GINT_TO_POINTER(msginfo->msgnum),
984 if (tuples->len > 0) {
985 gint num = GPOINTER_TO_INT(g_tuples_index(tuples, 0, 1));
986 g_relation_insert(relation, msginfo,
987 GPOINTER_TO_INT(num));
991 g_relation_insert(relation, msginfo,
993 g_tuples_destroy(tuples);
996 imap_seq_set_free(seq_list);
1000 if (ok == IMAP_SUCCESS)
1006 gint imap_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1010 g_return_val_if_fail(msginfo != NULL, -1);
1012 msglist.data = msginfo;
1013 msglist.next = NULL;
1015 return imap_copy_msgs(folder, dest, &msglist, NULL);
1018 gint imap_copy_msgs(Folder *folder, FolderItem *dest,
1019 MsgInfoList *msglist, GRelation *relation)
1025 g_return_val_if_fail(folder != NULL, -1);
1026 g_return_val_if_fail(dest != NULL, -1);
1027 g_return_val_if_fail(msglist != NULL, -1);
1029 msginfo = (MsgInfo *)msglist->data;
1030 g_return_val_if_fail(msginfo->folder != NULL, -1);
1032 if (folder == msginfo->folder->folder)
1033 return imap_do_copy_msgs(folder, dest, msglist, relation);
1035 file_list = procmsg_get_message_file_list(msglist);
1036 g_return_val_if_fail(file_list != NULL, -1);
1038 ret = imap_add_msgs(folder, dest, file_list, relation);
1040 procmsg_message_file_list_free(file_list);
1045 gint imap_remove_msg(Folder *folder, FolderItem *item, gint uid)
1048 IMAPSession *session;
1050 MsgNumberList numlist;
1052 g_return_val_if_fail(folder != NULL, -1);
1053 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
1054 g_return_val_if_fail(item != NULL, -1);
1056 session = imap_session_get(folder);
1057 if (!session) return -1;
1059 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
1060 NULL, NULL, NULL, NULL);
1061 if (ok != IMAP_SUCCESS)
1064 numlist.next = NULL;
1065 numlist.data = GINT_TO_POINTER(uid);
1067 ok = imap_set_message_flags
1068 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
1069 &numlist, IMAP_FLAG_DELETED, TRUE);
1070 if (ok != IMAP_SUCCESS) {
1071 log_warning(_("can't set deleted flags: %d\n"), uid);
1075 ok = imap_cmd_expunge(session);
1076 if (ok != IMAP_SUCCESS) {
1077 log_warning(_("can't expunge\n"));
1081 IMAP_FOLDER_ITEM(item)->uid_list = g_slist_remove(
1082 IMAP_FOLDER_ITEM(item)->uid_list, numlist.data);
1083 dir = folder_item_get_path(item);
1084 if (is_dir_exist(dir))
1085 remove_numbered_files(dir, uid, uid);
1088 return IMAP_SUCCESS;
1091 gint imap_remove_all_msg(Folder *folder, FolderItem *item)
1094 IMAPSession *session;
1097 g_return_val_if_fail(folder != NULL, -1);
1098 g_return_val_if_fail(item != NULL, -1);
1100 session = imap_session_get(folder);
1101 if (!session) return -1;
1103 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
1104 NULL, NULL, NULL, NULL);
1105 if (ok != IMAP_SUCCESS)
1108 imap_gen_send(session, "STORE 1:* +FLAGS.SILENT (\\Deleted)");
1109 ok = imap_cmd_ok(session, NULL);
1110 if (ok != IMAP_SUCCESS) {
1111 log_warning(_("can't set deleted flags: 1:*\n"));
1115 ok = imap_cmd_expunge(session);
1116 if (ok != IMAP_SUCCESS) {
1117 log_warning(_("can't expunge\n"));
1121 dir = folder_item_get_path(item);
1122 if (is_dir_exist(dir))
1123 remove_all_numbered_files(dir);
1126 return IMAP_SUCCESS;
1129 gboolean imap_is_msg_changed(Folder *folder, FolderItem *item, MsgInfo *msginfo)
1131 /* TODO: properly implement this method */
1135 gint imap_close(Folder *folder, FolderItem *item)
1138 IMAPSession *session;
1140 g_return_val_if_fail(folder != NULL, -1);
1141 g_return_val_if_fail(item != NULL, -1);
1142 g_return_val_if_fail(item->path != NULL, -1);
1144 session = imap_session_get(folder);
1145 if (!session) return -1;
1147 if (session->mbox) {
1148 if (strcmp2(session->mbox, item->path) != 0) return -1;
1150 ok = imap_cmd_close(session);
1151 if (ok != IMAP_SUCCESS)
1152 log_warning(_("can't close folder\n"));
1154 g_free(session->mbox);
1155 session->mbox = NULL;
1163 gint imap_scan_tree(Folder *folder)
1165 FolderItem *item = NULL;
1166 IMAPSession *session;
1167 gchar *root_folder = NULL;
1169 g_return_val_if_fail(folder != NULL, -1);
1170 g_return_val_if_fail(folder->account != NULL, -1);
1172 session = imap_session_get(folder);
1174 if (!folder->node) {
1175 folder_tree_destroy(folder);
1176 item = folder_item_new(folder, folder->name, NULL);
1177 item->folder = folder;
1178 folder->node = item->node = g_node_new(item);
1183 if (folder->account->imap_dir && *folder->account->imap_dir) {
1186 Xstrdup_a(root_folder, folder->account->imap_dir, return -1);
1187 strtailchomp(root_folder, '/');
1188 extract_quote(root_folder, '"');
1189 real_path = imap_get_real_path
1190 (IMAP_FOLDER(folder), root_folder);
1191 debug_print("IMAP root directory: %s\n", real_path);
1192 if (imap_status(session, IMAP_FOLDER(folder), root_folder,
1193 NULL, NULL, NULL, NULL, NULL)
1195 if (imap_cmd_create(session, real_path)
1197 log_warning(_("can't create root folder %s\n"),
1207 item = FOLDER_ITEM(folder->node->data);
1208 if (!item || ((item->path || root_folder) &&
1209 strcmp2(item->path, root_folder) != 0)) {
1210 folder_tree_destroy(folder);
1211 item = folder_item_new(folder, folder->name, root_folder);
1212 item->folder = folder;
1213 folder->node = item->node = g_node_new(item);
1216 imap_scan_tree_recursive(session, FOLDER_ITEM(folder->node->data));
1217 imap_create_missing_folders(folder);
1222 static gint imap_scan_tree_recursive(IMAPSession *session, FolderItem *item)
1225 IMAPFolder *imapfolder;
1226 FolderItem *new_item;
1227 GSList *item_list, *cur;
1230 gchar *wildcard_path;
1234 g_return_val_if_fail(item != NULL, -1);
1235 g_return_val_if_fail(item->folder != NULL, -1);
1236 g_return_val_if_fail(item->no_sub == FALSE, -1);
1238 folder = item->folder;
1239 imapfolder = IMAP_FOLDER(folder);
1241 separator = imap_get_path_separator(imapfolder, item->path);
1243 if (folder->ui_func)
1244 folder->ui_func(folder, item, folder->ui_func_data);
1247 wildcard[0] = separator;
1250 real_path = imap_get_real_path(imapfolder, item->path);
1254 real_path = g_strdup("");
1257 Xstrcat_a(wildcard_path, real_path, wildcard,
1258 {g_free(real_path); return IMAP_ERROR;});
1259 QUOTE_IF_REQUIRED(wildcard_path, wildcard_path);
1261 imap_gen_send(session, "LIST \"\" %s",
1264 strtailchomp(real_path, separator);
1265 item_list = imap_parse_list(imapfolder, session, real_path, NULL);
1268 node = item->node->children;
1269 while (node != NULL) {
1270 FolderItem *old_item = FOLDER_ITEM(node->data);
1271 GNode *next = node->next;
1274 for (cur = item_list; cur != NULL; cur = cur->next) {
1275 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1276 if (!strcmp2(old_item->path, cur_item->path)) {
1277 new_item = cur_item;
1282 debug_print("folder '%s' not found. removing...\n",
1284 folder_item_remove(old_item);
1286 old_item->no_sub = new_item->no_sub;
1287 old_item->no_select = new_item->no_select;
1288 if (old_item->no_sub == TRUE && node->children) {
1289 debug_print("folder '%s' doesn't have "
1290 "subfolders. removing...\n",
1292 folder_item_remove_children(old_item);
1299 for (cur = item_list; cur != NULL; cur = cur->next) {
1300 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1302 for (node = item->node->children; node != NULL;
1303 node = node->next) {
1304 if (!strcmp2(FOLDER_ITEM(node->data)->path,
1306 new_item = FOLDER_ITEM(node->data);
1307 folder_item_destroy(cur_item);
1313 new_item = cur_item;
1314 debug_print("new folder '%s' found.\n", new_item->path);
1315 folder_item_append(item, new_item);
1318 if (!strcmp(new_item->path, "INBOX")) {
1319 new_item->stype = F_INBOX;
1320 folder->inbox = new_item;
1321 } else if (!item->parent || item->stype == F_INBOX) {
1324 base = g_basename(new_item->path);
1326 if (!folder->outbox && !strcasecmp(base, "Sent")) {
1327 new_item->stype = F_OUTBOX;
1328 folder->outbox = new_item;
1329 } else if (!folder->draft && !strcasecmp(base, "Drafts")) {
1330 new_item->stype = F_DRAFT;
1331 folder->draft = new_item;
1332 } else if (!folder->queue && !strcasecmp(base, "Queue")) {
1333 new_item->stype = F_QUEUE;
1334 folder->queue = new_item;
1335 } else if (!folder->trash && !strcasecmp(base, "Trash")) {
1336 new_item->stype = F_TRASH;
1337 folder->trash = new_item;
1341 if (new_item->no_sub == FALSE)
1342 imap_scan_tree_recursive(session, new_item);
1345 g_slist_free(item_list);
1347 return IMAP_SUCCESS;
1350 static GSList *imap_parse_list(IMAPFolder *folder, IMAPSession *session,
1351 const gchar *real_path, gchar *separator)
1353 gchar buf[IMAPBUFSIZE];
1355 gchar separator_str[16];
1358 gchar *loc_name, *loc_path;
1359 GSList *item_list = NULL;
1361 FolderItem *new_item;
1363 debug_print("getting list of %s ...\n",
1364 *real_path ? real_path : "\"\"");
1366 str = g_string_new(NULL);
1369 if (sock_gets(SESSION(session)->sock, buf, sizeof(buf)) <= 0) {
1370 log_warning(_("error occurred while getting LIST.\n"));
1374 if (buf[0] != '*' || buf[1] != ' ') {
1375 log_print("IMAP4< %s\n", buf);
1376 if (sscanf(buf, "%*d %16s", buf) < 1 ||
1377 strcmp(buf, "OK") != 0)
1378 log_warning(_("error occurred while getting LIST.\n"));
1382 debug_print("IMAP4< %s\n", buf);
1384 g_string_assign(str, buf);
1386 if (strncmp(p, "LIST ", 5) != 0) continue;
1389 if (*p != '(') continue;
1391 p = strchr_cpy(p, ')', flags, sizeof(flags));
1393 while (*p == ' ') p++;
1395 p = strchr_cpy(p, ' ', separator_str, sizeof(separator_str));
1397 extract_quote(separator_str, '"');
1398 if (!strcmp(separator_str, "NIL"))
1399 separator_str[0] = '\0';
1401 *separator = separator_str[0];
1404 while (*p == ' ') p++;
1405 if (*p == '{' || *p == '"')
1406 p = imap_parse_atom(SESSION(session)->sock, p,
1407 buf, sizeof(buf), str);
1409 strncpy2(buf, p, sizeof(buf));
1410 strtailchomp(buf, separator_str[0]);
1411 if (buf[0] == '\0') continue;
1412 if (!strcmp(buf, real_path)) continue;
1414 if (separator_str[0] != '\0')
1415 subst_char(buf, separator_str[0], '/');
1416 name = g_basename(buf);
1417 if (name[0] == '.') continue;
1419 loc_name = imap_modified_utf7_to_locale(name);
1420 loc_path = imap_modified_utf7_to_locale(buf);
1421 new_item = folder_item_new(FOLDER(folder), loc_name, loc_path);
1422 if (strcasestr(flags, "\\Noinferiors") != NULL)
1423 new_item->no_sub = TRUE;
1424 if (strcmp(buf, "INBOX") != 0 &&
1425 strcasestr(flags, "\\Noselect") != NULL)
1426 new_item->no_select = TRUE;
1428 item_list = g_slist_append(item_list, new_item);
1430 debug_print("folder '%s' found.\n", loc_path);
1435 g_string_free(str, TRUE);
1440 gint imap_create_tree(Folder *folder)
1442 g_return_val_if_fail(folder != NULL, -1);
1443 g_return_val_if_fail(folder->node != NULL, -1);
1444 g_return_val_if_fail(folder->node->data != NULL, -1);
1445 g_return_val_if_fail(folder->account != NULL, -1);
1447 imap_scan_tree(folder);
1448 imap_create_missing_folders(folder);
1453 static void imap_create_missing_folders(Folder *folder)
1455 g_return_if_fail(folder != NULL);
1458 folder->inbox = imap_create_special_folder
1459 (folder, F_INBOX, "INBOX");
1461 if (!folder->outbox)
1462 folder->outbox = imap_create_special_folder
1463 (folder, F_OUTBOX, "Sent");
1465 folder->draft = imap_create_special_folder
1466 (folder, F_DRAFT, "Drafts");
1468 folder->queue = imap_create_special_folder
1469 (folder, F_QUEUE, "Queue");
1472 folder->trash = imap_create_special_folder
1473 (folder, F_TRASH, "Trash");
1476 static FolderItem *imap_create_special_folder(Folder *folder,
1477 SpecialFolderItemType stype,
1481 FolderItem *new_item;
1483 g_return_val_if_fail(folder != NULL, NULL);
1484 g_return_val_if_fail(folder->node != NULL, NULL);
1485 g_return_val_if_fail(folder->node->data != NULL, NULL);
1486 g_return_val_if_fail(folder->account != NULL, NULL);
1487 g_return_val_if_fail(name != NULL, NULL);
1489 item = FOLDER_ITEM(folder->node->data);
1490 new_item = imap_create_folder(folder, item, name);
1493 g_warning("Can't create '%s'\n", name);
1494 if (!folder->inbox) return NULL;
1496 new_item = imap_create_folder(folder, folder->inbox, name);
1498 g_warning("Can't create '%s' under INBOX\n", name);
1500 new_item->stype = stype;
1502 new_item->stype = stype;
1507 static gchar *imap_folder_get_path(Folder *folder)
1511 g_return_val_if_fail(folder != NULL, NULL);
1512 g_return_val_if_fail(folder->account != NULL, NULL);
1514 folder_path = g_strconcat(get_imap_cache_dir(),
1516 folder->account->recv_server,
1518 folder->account->userid,
1524 static gchar *imap_item_get_path(Folder *folder, FolderItem *item)
1526 gchar *folder_path, *path;
1528 g_return_val_if_fail(folder != NULL, NULL);
1529 g_return_val_if_fail(item != NULL, NULL);
1530 folder_path = imap_folder_get_path(folder);
1532 g_return_val_if_fail(folder_path != NULL, NULL);
1533 if (folder_path[0] == G_DIR_SEPARATOR) {
1535 path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
1538 path = g_strdup(folder_path);
1541 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1542 folder_path, G_DIR_SEPARATOR_S,
1545 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1548 g_free(folder_path);
1553 FolderItem *imap_create_folder(Folder *folder, FolderItem *parent,
1556 gchar *dirpath, *imap_path;
1557 IMAPSession *session;
1558 FolderItem *new_item;
1564 g_return_val_if_fail(folder != NULL, NULL);
1565 g_return_val_if_fail(folder->account != NULL, NULL);
1566 g_return_val_if_fail(parent != NULL, NULL);
1567 g_return_val_if_fail(name != NULL, NULL);
1569 session = imap_session_get(folder);
1570 if (!session) return NULL;
1572 if (!parent->parent && strcmp(name, "INBOX") == 0)
1573 dirpath = g_strdup(name);
1574 else if (parent->path)
1575 dirpath = g_strconcat(parent->path, "/", name, NULL);
1576 else if ((p = strchr(name, '/')) != NULL && *(p + 1) != '\0')
1577 dirpath = g_strdup(name);
1578 else if (folder->account->imap_dir && *folder->account->imap_dir) {
1581 Xstrdup_a(imap_dir, folder->account->imap_dir, return NULL);
1582 strtailchomp(imap_dir, '/');
1583 dirpath = g_strconcat(imap_dir, "/", name, NULL);
1585 dirpath = g_strdup(name);
1587 /* keep trailing directory separator to create a folder that contains
1589 imap_path = imap_locale_to_modified_utf7(dirpath);
1590 strtailchomp(dirpath, '/');
1591 Xstrdup_a(new_name, name, {g_free(dirpath); return NULL;});
1592 strtailchomp(new_name, '/');
1593 separator = imap_get_path_separator(IMAP_FOLDER(folder), imap_path);
1594 imap_path_separator_subst(imap_path, separator);
1595 subst_char(new_name, '/', separator);
1597 if (strcmp(name, "INBOX") != 0) {
1600 gboolean exist = FALSE;
1602 argbuf = g_ptr_array_new();
1603 ok = imap_cmd_list(session, NULL, imap_path,
1605 if (ok != IMAP_SUCCESS) {
1606 log_warning(_("can't create mailbox: LIST failed\n"));
1609 ptr_array_free_strings(argbuf);
1610 g_ptr_array_free(argbuf, TRUE);
1614 for (i = 0; i < argbuf->len; i++) {
1616 str = g_ptr_array_index(argbuf, i);
1617 if (!strncmp(str, "LIST ", 5)) {
1622 ptr_array_free_strings(argbuf);
1623 g_ptr_array_free(argbuf, TRUE);
1626 ok = imap_cmd_create(session, imap_path);
1627 if (ok != IMAP_SUCCESS) {
1628 log_warning(_("can't create mailbox\n"));
1636 new_item = folder_item_new(folder, new_name, dirpath);
1637 folder_item_append(parent, new_item);
1641 dirpath = folder_item_get_path(new_item);
1642 if (!is_dir_exist(dirpath))
1643 make_dir_hier(dirpath);
1649 gint imap_rename_folder(Folder *folder, FolderItem *item, const gchar *name)
1653 gchar *real_oldpath;
1654 gchar *real_newpath;
1656 gchar *old_cache_dir;
1657 gchar *new_cache_dir;
1658 IMAPSession *session;
1661 gint exists, recent, unseen;
1662 guint32 uid_validity;
1664 g_return_val_if_fail(folder != NULL, -1);
1665 g_return_val_if_fail(item != NULL, -1);
1666 g_return_val_if_fail(item->path != NULL, -1);
1667 g_return_val_if_fail(name != NULL, -1);
1669 session = imap_session_get(folder);
1670 if (!session) return -1;
1672 real_oldpath = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1674 g_free(session->mbox);
1675 session->mbox = NULL;
1676 ok = imap_cmd_examine(session, "INBOX",
1677 &exists, &recent, &unseen, &uid_validity);
1678 if (ok != IMAP_SUCCESS) {
1679 g_free(real_oldpath);
1683 separator = imap_get_path_separator(IMAP_FOLDER(folder), item->path);
1684 if (strchr(item->path, G_DIR_SEPARATOR)) {
1685 dirpath = g_dirname(item->path);
1686 newpath = g_strconcat(dirpath, G_DIR_SEPARATOR_S, name, NULL);
1689 newpath = g_strdup(name);
1691 real_newpath = imap_locale_to_modified_utf7(newpath);
1692 imap_path_separator_subst(real_newpath, separator);
1694 ok = imap_cmd_rename(session, real_oldpath, real_newpath);
1695 if (ok != IMAP_SUCCESS) {
1696 log_warning(_("can't rename mailbox: %s to %s\n"),
1697 real_oldpath, real_newpath);
1698 g_free(real_oldpath);
1700 g_free(real_newpath);
1705 item->name = g_strdup(name);
1707 old_cache_dir = folder_item_get_path(item);
1709 paths[0] = g_strdup(item->path);
1711 g_node_traverse(item->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1712 imap_rename_folder_func, paths);
1714 if (is_dir_exist(old_cache_dir)) {
1715 new_cache_dir = folder_item_get_path(item);
1716 if (rename(old_cache_dir, new_cache_dir) < 0) {
1717 FILE_OP_ERROR(old_cache_dir, "rename");
1719 g_free(new_cache_dir);
1722 g_free(old_cache_dir);
1725 g_free(real_oldpath);
1726 g_free(real_newpath);
1731 gint imap_remove_folder(Folder *folder, FolderItem *item)
1734 IMAPSession *session;
1737 gint exists, recent, unseen;
1738 guint32 uid_validity;
1740 g_return_val_if_fail(folder != NULL, -1);
1741 g_return_val_if_fail(item != NULL, -1);
1742 g_return_val_if_fail(item->path != NULL, -1);
1744 session = imap_session_get(folder);
1745 if (!session) return -1;
1747 path = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1749 ok = imap_cmd_examine(session, "INBOX",
1750 &exists, &recent, &unseen, &uid_validity);
1751 if (ok != IMAP_SUCCESS) {
1756 ok = imap_cmd_delete(session, path);
1757 if (ok != IMAP_SUCCESS) {
1758 log_warning(_("can't delete mailbox\n"));
1764 cache_dir = folder_item_get_path(item);
1765 if (is_dir_exist(cache_dir) && remove_dir_recursive(cache_dir) < 0)
1766 g_warning("can't remove directory '%s'\n", cache_dir);
1768 folder_item_remove(item);
1773 static GSList *imap_get_uncached_messages(IMAPSession *session,
1775 MsgNumberList *numlist)
1778 GSList *newlist = NULL;
1779 GSList *llast = NULL;
1782 GSList *seq_list, *cur;
1785 g_return_val_if_fail(session != NULL, NULL);
1786 g_return_val_if_fail(item != NULL, NULL);
1787 g_return_val_if_fail(item->folder != NULL, NULL);
1788 g_return_val_if_fail(FOLDER_CLASS(item->folder) == &imap_class, NULL);
1790 seq_list = imap_get_seq_set_from_numlist(numlist);
1791 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
1792 imapset = cur->data;
1794 if (imap_cmd_envelope(session, imapset)
1796 log_warning(_("can't get envelope\n"));
1800 str = g_string_new(NULL);
1803 if ((tmp = sock_getline(SESSION(session)->sock)) == NULL) {
1804 log_warning(_("error occurred while getting envelope.\n"));
1805 g_string_free(str, TRUE);
1809 if (tmp[0] != '*' || tmp[1] != ' ') {
1810 log_print("IMAP4< %s\n", tmp);
1814 if (strstr(tmp, "FETCH") == NULL) {
1815 log_print("IMAP4< %s\n", tmp);
1819 log_print("IMAP4< %s\n", tmp);
1820 g_string_assign(str, tmp);
1823 msginfo = imap_parse_envelope
1824 (SESSION(session)->sock, item, str);
1826 log_warning(_("can't parse envelope: %s\n"), str->str);
1829 if (item->stype == F_QUEUE) {
1830 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_QUEUED);
1831 } else if (item->stype == F_DRAFT) {
1832 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_DRAFT);
1835 msginfo->folder = item;
1838 llast = newlist = g_slist_append(newlist, msginfo);
1840 llast = g_slist_append(llast, msginfo);
1841 llast = llast->next;
1845 g_string_free(str, TRUE);
1847 imap_seq_set_free(seq_list);
1852 static void imap_delete_all_cached_messages(FolderItem *item)
1856 g_return_if_fail(item != NULL);
1857 g_return_if_fail(item->folder != NULL);
1858 g_return_if_fail(FOLDER_CLASS(item->folder) == &imap_class);
1860 debug_print("Deleting all cached messages...\n");
1862 dir = folder_item_get_path(item);
1863 if (is_dir_exist(dir))
1864 remove_all_numbered_files(dir);
1867 debug_print("done.\n");
1871 static SockInfo *imap_open_tunnel(const gchar *server,
1872 const gchar *tunnelcmd,
1875 static SockInfo *imap_open_tunnel(const gchar *server,
1876 const gchar *tunnelcmd)
1881 if ((sock = sock_connect_cmd(server, tunnelcmd)) == NULL) {
1882 log_warning(_("Can't establish IMAP4 session with: %s\n"),
1887 return imap_init_sock(sock, ssl_type);
1889 return imap_init_sock(sock);
1895 static SockInfo *imap_open(const gchar *server, gushort port,
1898 static SockInfo *imap_open(const gchar *server, gushort port)
1903 if ((sock = sock_connect(server, port)) == NULL) {
1904 log_warning(_("Can't connect to IMAP4 server: %s:%d\n"),
1910 if (ssl_type == SSL_TUNNEL && !ssl_init_socket(sock)) {
1911 log_warning(_("Can't establish IMAP4 session with: %s:%d\n"),
1921 static SockInfo *imap_init_sock(SockInfo *sock, SSLType ssl_type)
1923 static SockInfo *imap_init_sock(SockInfo *sock)
1930 static GList *imap_parse_namespace_str(gchar *str)
1935 IMAPNameSpace *namespace;
1936 GList *ns_list = NULL;
1938 while (*p != '\0') {
1939 /* parse ("#foo" "/") */
1941 while (*p && *p != '(') p++;
1942 if (*p == '\0') break;
1945 while (*p && *p != '"') p++;
1946 if (*p == '\0') break;
1950 while (*p && *p != '"') p++;
1951 if (*p == '\0') break;
1955 while (*p && isspace(*p)) p++;
1956 if (*p == '\0') break;
1957 if (strncmp(p, "NIL", 3) == 0)
1959 else if (*p == '"') {
1962 while (*p && *p != '"') p++;
1963 if (*p == '\0') break;
1968 while (*p && *p != ')') p++;
1969 if (*p == '\0') break;
1972 namespace = g_new(IMAPNameSpace, 1);
1973 namespace->name = g_strdup(name);
1974 namespace->separator = separator ? separator[0] : '\0';
1975 ns_list = g_list_append(ns_list, namespace);
1981 static void imap_parse_namespace(IMAPSession *session, IMAPFolder *folder)
1986 g_return_if_fail(session != NULL);
1987 g_return_if_fail(folder != NULL);
1989 if (folder->ns_personal != NULL ||
1990 folder->ns_others != NULL ||
1991 folder->ns_shared != NULL)
1994 if (!imap_has_capability(session, "NAMESPACE")) {
1995 imap_get_namespace_by_list(session, folder);
1999 if (imap_cmd_namespace(session, &ns_str)
2001 log_warning(_("can't get namespace\n"));
2005 str_array = strsplit_parenthesis(ns_str, '(', ')', 3);
2006 if (str_array == NULL) {
2008 imap_get_namespace_by_list(session, folder);
2012 folder->ns_personal = imap_parse_namespace_str(str_array[0]);
2013 if (str_array[0] && str_array[1])
2014 folder->ns_others = imap_parse_namespace_str(str_array[1]);
2015 if (str_array[0] && str_array[1] && str_array[2])
2016 folder->ns_shared = imap_parse_namespace_str(str_array[2]);
2017 g_strfreev(str_array);
2021 static void imap_get_namespace_by_list(IMAPSession *session, IMAPFolder *folder)
2023 GSList *item_list, *cur;
2024 gchar separator = '\0';
2025 IMAPNameSpace *namespace;
2027 g_return_if_fail(session != NULL);
2028 g_return_if_fail(folder != NULL);
2030 if (folder->ns_personal != NULL ||
2031 folder->ns_others != NULL ||
2032 folder->ns_shared != NULL)
2035 imap_gen_send(session, "LIST \"\" \"\"");
2036 item_list = imap_parse_list(folder, session, "", &separator);
2037 for (cur = item_list; cur != NULL; cur = cur->next)
2038 folder_item_destroy(FOLDER_ITEM(cur->data));
2039 g_slist_free(item_list);
2041 namespace = g_new(IMAPNameSpace, 1);
2042 namespace->name = g_strdup("");
2043 namespace->separator = separator;
2044 folder->ns_personal = g_list_append(NULL, namespace);
2047 static IMAPNameSpace *imap_find_namespace_from_list(GList *ns_list,
2050 IMAPNameSpace *namespace = NULL;
2051 gchar *tmp_path, *name;
2053 if (!path) path = "";
2055 for (; ns_list != NULL; ns_list = ns_list->next) {
2056 IMAPNameSpace *tmp_ns = ns_list->data;
2058 Xstrcat_a(tmp_path, path, "/", return namespace);
2059 Xstrdup_a(name, tmp_ns->name, return namespace);
2060 if (tmp_ns->separator && tmp_ns->separator != '/') {
2061 subst_char(tmp_path, tmp_ns->separator, '/');
2062 subst_char(name, tmp_ns->separator, '/');
2064 if (strncmp(tmp_path, name, strlen(name)) == 0)
2071 static IMAPNameSpace *imap_find_namespace(IMAPFolder *folder,
2074 IMAPNameSpace *namespace;
2076 g_return_val_if_fail(folder != NULL, NULL);
2078 namespace = imap_find_namespace_from_list(folder->ns_personal, path);
2079 if (namespace) return namespace;
2080 namespace = imap_find_namespace_from_list(folder->ns_others, path);
2081 if (namespace) return namespace;
2082 namespace = imap_find_namespace_from_list(folder->ns_shared, path);
2083 if (namespace) return namespace;
2088 static gchar imap_get_path_separator(IMAPFolder *folder, const gchar *path)
2090 IMAPNameSpace *namespace;
2091 gchar separator = '/';
2093 namespace = imap_find_namespace(folder, path);
2094 if (namespace && namespace->separator)
2095 separator = namespace->separator;
2100 static gchar *imap_get_real_path(IMAPFolder *folder, const gchar *path)
2105 g_return_val_if_fail(folder != NULL, NULL);
2106 g_return_val_if_fail(path != NULL, NULL);
2108 real_path = imap_locale_to_modified_utf7(path);
2109 separator = imap_get_path_separator(folder, path);
2110 imap_path_separator_subst(real_path, separator);
2115 static gchar *imap_parse_atom(SockInfo *sock, gchar *src,
2116 gchar *dest, gint dest_len, GString *str)
2118 gchar *cur_pos = src;
2121 g_return_val_if_fail(str != NULL, cur_pos);
2123 /* read the next line if the current response buffer is empty */
2124 while (isspace(*cur_pos)) cur_pos++;
2125 while (*cur_pos == '\0') {
2126 if ((nextline = sock_getline(sock)) == NULL)
2128 g_string_assign(str, nextline);
2130 strretchomp(nextline);
2131 /* log_print("IMAP4< %s\n", nextline); */
2132 debug_print("IMAP4< %s\n", nextline);
2135 while (isspace(*cur_pos)) cur_pos++;
2138 if (!strncmp(cur_pos, "NIL", 3)) {
2141 } else if (*cur_pos == '\"') {
2144 p = get_quoted(cur_pos, '\"', dest, dest_len);
2145 cur_pos = p ? p : cur_pos + 2;
2146 } else if (*cur_pos == '{') {
2151 cur_pos = strchr_cpy(cur_pos + 1, '}', buf, sizeof(buf));
2153 g_return_val_if_fail(len >= 0, cur_pos);
2155 g_string_truncate(str, 0);
2159 if ((nextline = sock_getline(sock)) == NULL)
2161 line_len += strlen(nextline);
2162 g_string_append(str, nextline);
2164 strretchomp(nextline);
2165 /* log_print("IMAP4< %s\n", nextline); */
2166 debug_print("IMAP4< %s\n", nextline);
2168 } while (line_len < len);
2170 memcpy(dest, cur_pos, MIN(len, dest_len - 1));
2171 dest[MIN(len, dest_len - 1)] = '\0';
2178 static gchar *imap_get_header(SockInfo *sock, gchar *cur_pos, gchar **headers,
2188 g_return_val_if_fail(str != NULL, cur_pos);
2190 while (isspace(*cur_pos)) cur_pos++;
2192 g_return_val_if_fail(*cur_pos == '{', cur_pos);
2194 cur_pos = strchr_cpy(cur_pos + 1, '}', buf, sizeof(buf));
2196 g_return_val_if_fail(len >= 0, cur_pos);
2198 g_string_truncate(str, 0);
2202 if ((nextline = sock_getline(sock)) == NULL)
2204 block_len += strlen(nextline);
2205 g_string_append(str, nextline);
2207 strretchomp(nextline);
2208 /* debug_print("IMAP4< %s\n", nextline); */
2210 } while (block_len < len);
2212 debug_print("IMAP4< [contents of BODY.PEEK[HEADER.FIELDS (...)]]\n");
2214 *headers = g_strndup(cur_pos, len);
2217 while (isspace(*cur_pos)) cur_pos++;
2218 while (*cur_pos == '\0') {
2219 if ((nextline = sock_getline(sock)) == NULL)
2221 g_string_assign(str, nextline);
2223 strretchomp(nextline);
2224 debug_print("IMAP4< %s\n", nextline);
2227 while (isspace(*cur_pos)) cur_pos++;
2233 static MsgFlags imap_parse_flags(const gchar *flag_str)
2235 const gchar *p = flag_str;
2236 MsgFlags flags = {0, 0};
2238 flags.perm_flags = MSG_UNREAD;
2240 while ((p = strchr(p, '\\')) != NULL) {
2243 if (g_strncasecmp(p, "Recent", 6) == 0 && MSG_IS_UNREAD(flags)) {
2244 MSG_SET_PERM_FLAGS(flags, MSG_NEW);
2245 } else if (g_strncasecmp(p, "Seen", 4) == 0) {
2246 MSG_UNSET_PERM_FLAGS(flags, MSG_NEW|MSG_UNREAD);
2247 } else if (g_strncasecmp(p, "Deleted", 7) == 0) {
2248 MSG_SET_PERM_FLAGS(flags, MSG_DELETED);
2249 } else if (g_strncasecmp(p, "Flagged", 7) == 0) {
2250 MSG_SET_PERM_FLAGS(flags, MSG_MARKED);
2251 } else if (g_strncasecmp(p, "Answered", 8) == 0) {
2252 MSG_SET_PERM_FLAGS(flags, MSG_REPLIED);
2259 static MsgInfo *imap_parse_envelope(SockInfo *sock, FolderItem *item,
2262 gchar buf[IMAPBUFSIZE];
2263 MsgInfo *msginfo = NULL;
2268 MsgFlags flags = {0, 0}, imap_flags = {0, 0};
2270 g_return_val_if_fail(line_str != NULL, NULL);
2271 g_return_val_if_fail(line_str->str[0] == '*' &&
2272 line_str->str[1] == ' ', NULL);
2274 MSG_SET_TMP_FLAGS(flags, MSG_IMAP);
2275 if (item->stype == F_QUEUE) {
2276 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
2277 } else if (item->stype == F_DRAFT) {
2278 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
2281 cur_pos = line_str->str + 2;
2283 #define PARSE_ONE_ELEMENT(ch) \
2285 cur_pos = strchr_cpy(cur_pos, ch, buf, sizeof(buf)); \
2286 if (cur_pos == NULL) { \
2287 g_warning("cur_pos == NULL\n"); \
2288 procmsg_msginfo_free(msginfo); \
2293 PARSE_ONE_ELEMENT(' ');
2296 PARSE_ONE_ELEMENT(' ');
2297 g_return_val_if_fail(!strcmp(buf, "FETCH"), NULL);
2299 g_return_val_if_fail(*cur_pos == '(', NULL);
2302 while (*cur_pos != '\0' && *cur_pos != ')') {
2303 while (*cur_pos == ' ') cur_pos++;
2305 if (!strncmp(cur_pos, "UID ", 4)) {
2307 uid = strtoul(cur_pos, &cur_pos, 10);
2308 } else if (!strncmp(cur_pos, "FLAGS ", 6)) {
2310 if (*cur_pos != '(') {
2311 g_warning("*cur_pos != '('\n");
2312 procmsg_msginfo_free(msginfo);
2316 PARSE_ONE_ELEMENT(')');
2317 imap_flags = imap_parse_flags(buf);
2318 } else if (!strncmp(cur_pos, "RFC822.SIZE ", 12)) {
2320 size = strtol(cur_pos, &cur_pos, 10);
2321 } else if (!strncmp(cur_pos, "BODY[HEADER.FIELDS ", 19)) {
2325 if (*cur_pos != '(') {
2326 g_warning("*cur_pos != '('\n");
2327 procmsg_msginfo_free(msginfo);
2331 PARSE_ONE_ELEMENT(')');
2332 if (*cur_pos != ']') {
2333 g_warning("*cur_pos != ']'\n");
2334 procmsg_msginfo_free(msginfo);
2339 cur_pos = imap_get_header(sock, cur_pos, &headers,
2341 msginfo = procheader_parse_str(headers, flags, FALSE, FALSE);
2344 g_warning("invalid FETCH response: %s\n", cur_pos);
2350 msginfo->msgnum = uid;
2351 msginfo->size = size;
2352 msginfo->flags.tmp_flags |= imap_flags.tmp_flags;
2353 msginfo->flags.perm_flags = imap_flags.perm_flags;
2359 static gchar *imap_get_flag_str(IMAPFlags flags)
2364 str = g_string_new(NULL);
2366 if (IMAP_IS_SEEN(flags)) g_string_append(str, "\\Seen ");
2367 if (IMAP_IS_ANSWERED(flags)) g_string_append(str, "\\Answered ");
2368 if (IMAP_IS_FLAGGED(flags)) g_string_append(str, "\\Flagged ");
2369 if (IMAP_IS_DELETED(flags)) g_string_append(str, "\\Deleted ");
2370 if (IMAP_IS_DRAFT(flags)) g_string_append(str, "\\Draft");
2372 if (str->len > 0 && str->str[str->len - 1] == ' ')
2373 g_string_truncate(str, str->len - 1);
2376 g_string_free(str, FALSE);
2381 static gint imap_set_message_flags(IMAPSession *session,
2382 MsgNumberList *numlist,
2389 GSList *seq_list, *cur;
2392 flag_str = imap_get_flag_str(flags);
2393 cmd = g_strconcat(is_set ? "+FLAGS.SILENT (" : "-FLAGS.SILENT (",
2394 flag_str, ")", NULL);
2397 seq_list = imap_get_seq_set_from_numlist(numlist);
2398 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
2399 imapset = cur->data;
2401 ok = imap_cmd_store(session, imapset, cmd);
2403 imap_seq_set_free(seq_list);
2409 static gint imap_select(IMAPSession *session, IMAPFolder *folder,
2411 gint *exists, gint *recent, gint *unseen,
2412 guint32 *uid_validity)
2416 gint exists_, recent_, unseen_, uid_validity_;
2418 if (!exists || !recent || !unseen || !uid_validity) {
2419 if (session->mbox && strcmp(session->mbox, path) == 0)
2420 return IMAP_SUCCESS;
2424 uid_validity = &uid_validity_;
2427 g_free(session->mbox);
2428 session->mbox = NULL;
2430 real_path = imap_get_real_path(folder, path);
2431 ok = imap_cmd_select(session, real_path,
2432 exists, recent, unseen, uid_validity);
2433 if (ok != IMAP_SUCCESS)
2434 log_warning(_("can't select folder: %s\n"), real_path);
2436 session->mbox = g_strdup(path);
2437 session->folder_content_changed = FALSE;
2444 #define THROW(err) { ok = err; goto catch; }
2446 static gint imap_status(IMAPSession *session, IMAPFolder *folder,
2448 gint *messages, gint *recent,
2449 guint32 *uid_next, guint32 *uid_validity,
2455 GPtrArray *argbuf = NULL;
2458 if (messages && recent && uid_next && uid_validity && unseen) {
2459 *messages = *recent = *uid_next = *uid_validity = *unseen = 0;
2460 argbuf = g_ptr_array_new();
2463 real_path = imap_get_real_path(folder, path);
2464 QUOTE_IF_REQUIRED(real_path_, real_path);
2465 imap_gen_send(session, "STATUS %s "
2466 "(MESSAGES RECENT UIDNEXT UIDVALIDITY UNSEEN)",
2469 ok = imap_cmd_ok(session, argbuf);
2470 if (ok != IMAP_SUCCESS || !argbuf) THROW(ok);
2472 str = search_array_str(argbuf, "STATUS");
2473 if (!str) THROW(IMAP_ERROR);
2475 str = strchr(str, '(');
2476 if (!str) THROW(IMAP_ERROR);
2478 while (*str != '\0' && *str != ')') {
2479 while (*str == ' ') str++;
2481 if (!strncmp(str, "MESSAGES ", 9)) {
2483 *messages = strtol(str, &str, 10);
2484 } else if (!strncmp(str, "RECENT ", 7)) {
2486 *recent = strtol(str, &str, 10);
2487 } else if (!strncmp(str, "UIDNEXT ", 8)) {
2489 *uid_next = strtoul(str, &str, 10);
2490 } else if (!strncmp(str, "UIDVALIDITY ", 12)) {
2492 *uid_validity = strtoul(str, &str, 10);
2493 } else if (!strncmp(str, "UNSEEN ", 7)) {
2495 *unseen = strtol(str, &str, 10);
2497 g_warning("invalid STATUS response: %s\n", str);
2505 ptr_array_free_strings(argbuf);
2506 g_ptr_array_free(argbuf, TRUE);
2514 static gboolean imap_has_capability(IMAPSession *session, const gchar *cap)
2518 for (p = session->capability; *p != NULL; ++p) {
2519 if (!g_strcasecmp(*p, cap))
2526 void imap_free_capabilities(IMAPSession *session)
2528 g_strfreev(session->capability);
2529 session->capability = NULL;
2532 /* low-level IMAP4rev1 commands */
2534 static gint imap_cmd_authenticate(IMAPSession *session, const gchar *user,
2535 const gchar *pass, IMAPAuthType type)
2542 gchar hexdigest[33];
2546 auth_type = "CRAM-MD5";
2548 imap_gen_send(session, "AUTHENTICATE %s", auth_type);
2549 ok = imap_gen_recv(session, &buf);
2550 if (ok != IMAP_SUCCESS || buf[0] != '+' || buf[1] != ' ') {
2555 challenge = g_malloc(strlen(buf + 2) + 1);
2556 challenge_len = base64_decode(challenge, buf + 2, -1);
2557 challenge[challenge_len] = '\0';
2559 log_print("IMAP< [Decoded: %s]\n", challenge);
2561 md5_hex_hmac(hexdigest, challenge, challenge_len, pass, strlen(pass));
2564 response = g_strdup_printf("%s %s", user, hexdigest);
2565 log_print("IMAP> [Encoded: %s]\n", response);
2566 response64 = g_malloc((strlen(response) + 3) * 2 + 1);
2567 base64_encode(response64, response, strlen(response));
2570 log_print("IMAP> %s\n", response64);
2571 sock_puts(SESSION(session)->sock, response64);
2572 ok = imap_cmd_ok(session, NULL);
2573 if (ok != IMAP_SUCCESS)
2574 log_warning(_("IMAP4 authentication failed.\n"));
2579 static gint imap_cmd_login(IMAPSession *session,
2580 const gchar *user, const gchar *pass)
2582 gchar *user_, *pass_;
2585 QUOTE_IF_REQUIRED(user_, user);
2586 QUOTE_IF_REQUIRED(pass_, pass);
2587 imap_gen_send(session, "LOGIN %s %s", user_, pass_);
2589 ok = imap_cmd_ok(session, NULL);
2590 if (ok != IMAP_SUCCESS)
2591 log_warning(_("IMAP4 login failed.\n"));
2596 static gint imap_cmd_logout(IMAPSession *session)
2598 imap_gen_send(session, "LOGOUT");
2599 return imap_cmd_ok(session, NULL);
2602 static gint imap_cmd_noop(IMAPSession *session)
2604 imap_gen_send(session, "NOOP");
2605 return imap_cmd_ok(session, NULL);
2608 static gint imap_cmd_starttls(IMAPSession *session)
2610 imap_gen_send(session, "STARTTLS");
2611 return imap_cmd_ok(session, NULL);
2614 #define THROW(err) { ok = err; goto catch; }
2616 static gint imap_cmd_namespace(IMAPSession *session, gchar **ns_str)
2622 argbuf = g_ptr_array_new();
2624 imap_gen_send(session, "NAMESPACE");
2625 if ((ok = imap_cmd_ok(session, argbuf)) != IMAP_SUCCESS) THROW(ok);
2627 str = search_array_str(argbuf, "NAMESPACE");
2628 if (!str) THROW(IMAP_ERROR);
2630 *ns_str = g_strdup(str);
2633 ptr_array_free_strings(argbuf);
2634 g_ptr_array_free(argbuf, TRUE);
2641 static gint imap_cmd_list(IMAPSession *session, const gchar *ref,
2642 const gchar *mailbox, GPtrArray *argbuf)
2644 gchar *ref_, *mailbox_;
2646 if (!ref) ref = "\"\"";
2647 if (!mailbox) mailbox = "\"\"";
2649 QUOTE_IF_REQUIRED(ref_, ref);
2650 QUOTE_IF_REQUIRED(mailbox_, mailbox);
2651 imap_gen_send(session, "LIST %s %s", ref_, mailbox_);
2653 return imap_cmd_ok(session, argbuf);
2656 #define THROW goto catch
2658 static gint imap_cmd_do_select(IMAPSession *session, const gchar *folder,
2660 gint *exists, gint *recent, gint *unseen,
2661 guint32 *uid_validity)
2669 *exists = *recent = *unseen = *uid_validity = 0;
2670 argbuf = g_ptr_array_new();
2673 select_cmd = "EXAMINE";
2675 select_cmd = "SELECT";
2677 QUOTE_IF_REQUIRED(folder_, folder);
2678 imap_gen_send(session, "%s %s", select_cmd, folder_);
2680 if ((ok = imap_cmd_ok(session, argbuf)) != IMAP_SUCCESS) THROW;
2682 resp_str = search_array_contain_str(argbuf, "EXISTS");
2684 if (sscanf(resp_str,"%d EXISTS", exists) != 1) {
2685 g_warning("imap_cmd_select(): invalid EXISTS line.\n");
2690 resp_str = search_array_contain_str(argbuf, "RECENT");
2692 if (sscanf(resp_str, "%d RECENT", recent) != 1) {
2693 g_warning("imap_cmd_select(): invalid RECENT line.\n");
2698 resp_str = search_array_contain_str(argbuf, "UIDVALIDITY");
2700 if (sscanf(resp_str, "OK [UIDVALIDITY %u] ", uid_validity)
2702 g_warning("imap_cmd_select(): invalid UIDVALIDITY line.\n");
2707 resp_str = search_array_contain_str(argbuf, "UNSEEN");
2709 if (sscanf(resp_str, "OK [UNSEEN %d] ", unseen) != 1) {
2710 g_warning("imap_cmd_select(): invalid UNSEEN line.\n");
2716 ptr_array_free_strings(argbuf);
2717 g_ptr_array_free(argbuf, TRUE);
2722 static gint imap_cmd_select(IMAPSession *session, const gchar *folder,
2723 gint *exists, gint *recent, gint *unseen,
2724 guint32 *uid_validity)
2726 return imap_cmd_do_select(session, folder, FALSE,
2727 exists, recent, unseen, uid_validity);
2730 static gint imap_cmd_examine(IMAPSession *session, const gchar *folder,
2731 gint *exists, gint *recent, gint *unseen,
2732 guint32 *uid_validity)
2734 return imap_cmd_do_select(session, folder, TRUE,
2735 exists, recent, unseen, uid_validity);
2740 static gint imap_cmd_create(IMAPSession *session, const gchar *folder)
2744 QUOTE_IF_REQUIRED(folder_, folder);
2745 imap_gen_send(session, "CREATE %s", folder_);
2747 return imap_cmd_ok(session, NULL);
2750 static gint imap_cmd_rename(IMAPSession *session, const gchar *old_folder,
2751 const gchar *new_folder)
2753 gchar *old_folder_, *new_folder_;
2755 QUOTE_IF_REQUIRED(old_folder_, old_folder);
2756 QUOTE_IF_REQUIRED(new_folder_, new_folder);
2757 imap_gen_send(session, "RENAME %s %s", old_folder_, new_folder_);
2759 return imap_cmd_ok(session, NULL);
2762 static gint imap_cmd_delete(IMAPSession *session, const gchar *folder)
2766 QUOTE_IF_REQUIRED(folder_, folder);
2767 imap_gen_send(session, "DELETE %s", folder_);
2769 return imap_cmd_ok(session, NULL);
2772 static gint imap_cmd_search(IMAPSession *session, const gchar *criteria, GSList **list)
2778 g_return_val_if_fail(criteria != NULL, IMAP_ERROR);
2779 g_return_val_if_fail(list != NULL, IMAP_ERROR);
2783 argbuf = g_ptr_array_new();
2784 imap_gen_send(session, "UID SEARCH %s", criteria);
2786 ok = imap_cmd_ok(session, argbuf);
2787 if (ok != IMAP_SUCCESS) {
2788 ptr_array_free_strings(argbuf);
2789 g_ptr_array_free(argbuf, TRUE);
2793 if ((uidlist = search_array_str(argbuf, "SEARCH ")) != NULL) {
2794 gchar **strlist, **p;
2796 strlist = g_strsplit(uidlist + 7, " ", 0);
2797 for (p = strlist; *p != NULL; ++p) {
2800 if (sscanf(*p, "%d", &msgnum) == 1)
2801 *list = g_slist_append(*list, GINT_TO_POINTER(msgnum));
2803 g_strfreev(strlist);
2805 ptr_array_free_strings(argbuf);
2806 g_ptr_array_free(argbuf, TRUE);
2808 return IMAP_SUCCESS;
2811 static gint imap_cmd_fetch(IMAPSession *session, guint32 uid, const gchar *filename)
2819 g_return_val_if_fail(filename != NULL, IMAP_ERROR);
2821 imap_gen_send(session, "UID FETCH %d BODY.PEEK[]", uid);
2823 while ((ok = imap_gen_recv(session, &buf)) == IMAP_SUCCESS) {
2824 if (buf[0] != '*' || buf[1] != ' ') {
2828 if (strstr(buf, "FETCH") != NULL) break;
2831 if (ok != IMAP_SUCCESS) {
2836 #define RETURN_ERROR_IF_FAIL(cond) \
2839 return IMAP_ERROR; \
2842 cur_pos = strchr(buf, '{');
2843 RETURN_ERROR_IF_FAIL(cur_pos != NULL);
2844 cur_pos = strchr_cpy(cur_pos + 1, '}', size_str, sizeof(size_str));
2845 RETURN_ERROR_IF_FAIL(cur_pos != NULL);
2846 size_num = atol(size_str);
2847 RETURN_ERROR_IF_FAIL(size_num >= 0);
2849 RETURN_ERROR_IF_FAIL(*cur_pos == '\0');
2851 #undef RETURN_ERROR_IF_FAIL
2855 if (recv_bytes_write_to_file(SESSION(session)->sock, size_num, filename) != 0)
2858 if (imap_gen_recv(session, &buf) != IMAP_SUCCESS) {
2863 if (buf[0] == '\0' || buf[strlen(buf) - 1] != ')') {
2869 ok = imap_cmd_ok(session, NULL);
2874 static gint imap_cmd_append(IMAPSession *session, const gchar *destfolder,
2875 const gchar *file, IMAPFlags flags, guint32 *new_uid)
2883 gchar buf[BUFFSIZE];
2888 g_return_val_if_fail(file != NULL, IMAP_ERROR);
2890 size = get_file_size_as_crlf(file);
2891 if ((fp = fopen(file, "rb")) == NULL) {
2892 FILE_OP_ERROR(file, "fopen");
2895 QUOTE_IF_REQUIRED(destfolder_, destfolder);
2896 flag_str = imap_get_flag_str(flags);
2897 imap_gen_send(session, "APPEND %s (%s) {%d}",
2898 destfolder_, flag_str, size);
2901 ok = imap_gen_recv(session, &ret);
2902 if (ok != IMAP_SUCCESS || ret[0] != '+' || ret[1] != ' ') {
2903 log_warning(_("can't append %s to %s\n"), file, destfolder_);
2910 log_print("IMAP4> %s\n", _("(sending file...)"));
2912 while (fgets(buf, sizeof(buf), fp) != NULL) {
2914 if (sock_puts(SESSION(session)->sock, buf) < 0) {
2921 FILE_OP_ERROR(file, "fgets");
2926 sock_puts(SESSION(session)->sock, "");
2930 if (new_uid != NULL)
2933 if (new_uid != NULL && session->uidplus) {
2934 argbuf = g_ptr_array_new();
2936 ok = imap_cmd_ok(session, argbuf);
2937 if (ok != IMAP_SUCCESS)
2938 log_warning(_("can't append message to %s\n"),
2940 else if (argbuf->len > 0) {
2941 resp_str = g_ptr_array_index(argbuf, argbuf->len - 1);
2943 sscanf(resp_str, "%*u OK [APPENDUID %*u %u]",
2945 *new_uid = new_uid_;
2949 ptr_array_free_strings(argbuf);
2950 g_ptr_array_free(argbuf, TRUE);
2952 ok = imap_cmd_ok(session, NULL);
2957 static gint imap_cmd_copy(IMAPSession *session, const gchar *seq_set,
2958 const gchar *destfolder, GRelation *uid_mapping)
2964 g_return_val_if_fail(session != NULL, IMAP_ERROR);
2965 g_return_val_if_fail(seq_set != NULL, IMAP_ERROR);
2966 g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
2968 QUOTE_IF_REQUIRED(destfolder_, destfolder);
2969 imap_gen_send(session, "UID COPY %s %s", seq_set, destfolder_);
2971 reply = g_ptr_array_new();
2973 ok = imap_cmd_ok(session, reply);
2974 if (ok != IMAP_SUCCESS)
2975 log_warning(_("can't copy %s to %s\n"), seq_set, destfolder_);
2979 - split IMAPSets into uids
2980 - g_relation_insert(uid_mapping, olduid, newuid);
2982 else if (imap_has_capability(session, "UIDPLUS") && reply->len > 0)
2983 if ((okmsginfo = g_ptr_array_index(reply, reply->len - 1)) != NULL &&
2984 sscanf(okmsginfo, "%*u OK [COPYUID %*u %u %u]", &olduid, &newuid) == 2 &&
2988 ptr_array_free_strings(reply);
2989 g_ptr_array_free(reply, TRUE);
2993 gint imap_cmd_envelope(IMAPSession *session, IMAPSet set)
2995 static GString *header_fields = NULL;
2997 if (header_fields == NULL) {
2998 const HeaderEntry *headers, *elem;
3000 headers = procheader_get_headernames(FALSE);
3001 header_fields = g_string_new("");
3003 for (elem = headers; elem->name != NULL; ++elem) {
3004 gint namelen = strlen(elem->name);
3006 /* Header fields ending with space are not rfc822 headers */
3007 if (elem->name[namelen - 1] == ' ')
3010 /* strip : at the of header field */
3011 if(elem->name[namelen - 1] == ':')
3017 g_string_sprintfa(header_fields, "%s%.*s",
3018 header_fields->str[0] != '\0' ? " " : "",
3019 namelen, elem->name);
3024 (session, "UID FETCH %s (UID FLAGS RFC822.SIZE BODY.PEEK[HEADER.FIELDS (%s)])",
3025 set, header_fields->str);
3027 return IMAP_SUCCESS;
3030 static gint imap_cmd_store(IMAPSession *session, IMAPSet seq_set,
3035 imap_gen_send(session, "UID STORE %s %s", seq_set, sub_cmd);
3037 if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS) {
3038 log_warning(_("error while imap command: STORE %s %s\n"),
3043 return IMAP_SUCCESS;
3046 static gint imap_cmd_expunge(IMAPSession *session)
3050 imap_gen_send(session, "EXPUNGE");
3051 if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS) {
3052 log_warning(_("error while imap command: EXPUNGE\n"));
3056 return IMAP_SUCCESS;
3059 static gint imap_cmd_close(IMAPSession *session)
3063 imap_gen_send(session, "CLOSE");
3064 if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS)
3065 log_warning(_("error while imap command: CLOSE\n"));
3070 static gint imap_cmd_ok(IMAPSession *session, GPtrArray *argbuf)
3072 gint ok = IMAP_SUCCESS;
3077 while ((ok = imap_gen_recv(session, &buf))
3079 // make sure data is long enough for any substring of buf
3080 data = alloca(strlen(buf) + 1);
3082 // untagged line read
3083 if (buf[0] == '*' && buf[1] == ' ') {
3086 g_ptr_array_add(argbuf, g_strdup(buf + 2));
3088 if (sscanf(buf + 2, "%d %s", &num, data) >= 2) {
3089 if (!strcmp(data, "EXISTS")) {
3090 session->exists = num;
3091 session->folder_content_changed = TRUE;
3094 if(!strcmp(data, "EXPUNGE")) {
3096 session->folder_content_changed = TRUE;
3099 // tagged line with correct tag and OK response found
3100 } else if ((sscanf(buf, "%d %s", &cmd_num, data) >= 2) &&
3101 (cmd_num == session->cmd_count) &&
3102 !strcmp(data, "OK")) {
3104 g_ptr_array_add(argbuf, g_strdup(buf));
3118 static void imap_gen_send(IMAPSession *session, const gchar *format, ...)
3125 va_start(args, format);
3126 tmp = g_strdup_vprintf(format, args);
3129 session->cmd_count++;
3131 buf = g_strdup_printf("%d %s\r\n", session->cmd_count, tmp);
3132 if (!strncasecmp(tmp, "LOGIN ", 6) && (p = strchr(tmp + 6, ' '))) {
3134 log_print("IMAP4> %d %s ********\n", session->cmd_count, tmp);
3136 log_print("IMAP4> %d %s\n", session->cmd_count, tmp);
3138 sock_write_all(SESSION(session)->sock, buf, strlen(buf));
3143 static gint imap_gen_recv(IMAPSession *session, gchar **ret)
3145 if ((*ret = sock_getline(SESSION(session)->sock)) == NULL)
3150 log_print("IMAP4< %s\n", *ret);
3152 return IMAP_SUCCESS;
3156 /* misc utility functions */
3158 static gchar *strchr_cpy(const gchar *src, gchar ch, gchar *dest, gint len)
3163 tmp = strchr(src, ch);
3167 memcpy(dest, src, MIN(tmp - src, len - 1));
3168 dest[MIN(tmp - src, len - 1)] = '\0';
3173 static gchar *get_quoted(const gchar *src, gchar ch, gchar *dest, gint len)
3175 const gchar *p = src;
3178 g_return_val_if_fail(*p == ch, NULL);
3183 while (*p != '\0' && *p != ch) {
3185 if (*p == '\\' && *(p + 1) != '\0')
3194 return (gchar *)(*p == ch ? p + 1 : p);
3197 static gchar *search_array_contain_str(GPtrArray *array, const gchar *str)
3201 for (i = 0; i < array->len; i++) {
3204 tmp = g_ptr_array_index(array, i);
3205 if (strstr(tmp, str) != NULL)
3212 static gchar *search_array_str(GPtrArray *array, const gchar *str)
3219 for (i = 0; i < array->len; i++) {
3222 tmp = g_ptr_array_index(array, i);
3223 if (!strncmp(tmp, str, len))
3230 static void imap_path_separator_subst(gchar *str, gchar separator)
3233 gboolean in_escape = FALSE;
3235 if (!separator || separator == '/') return;
3237 for (p = str; *p != '\0'; p++) {
3238 if (*p == '/' && !in_escape)
3240 else if (*p == '&' && *(p + 1) != '-' && !in_escape)
3242 else if (*p == '-' && in_escape)
3247 static gchar *imap_modified_utf7_to_locale(const gchar *mutf7_str)
3250 const gchar *from_p;
3253 to = g_malloc(strlen(mutf7_str) + 1);
3256 for (from_p = mutf7_str; *from_p != '\0'; from_p++) {
3257 if (*from_p == '&' && *(from_p + 1) == '-') {
3267 static iconv_t cd = (iconv_t)-1;
3268 static gboolean iconv_ok = TRUE;
3271 size_t norm_utf7_len;
3273 gchar *to_str, *to_p;
3275 gboolean in_escape = FALSE;
3277 if (!iconv_ok) return g_strdup(mutf7_str);
3279 if (cd == (iconv_t)-1) {
3280 cd = iconv_open(conv_get_current_charset_str(), "UTF-7");
3281 if (cd == (iconv_t)-1) {
3282 g_warning("iconv cannot convert UTF-7 to %s\n",
3283 conv_get_current_charset_str());
3285 return g_strdup(mutf7_str);
3289 norm_utf7 = g_string_new(NULL);
3291 for (p = mutf7_str; *p != '\0'; p++) {
3292 /* replace: '&' -> '+',
3294 escaped ',' -> '/' */
3295 if (!in_escape && *p == '&') {
3296 if (*(p + 1) != '-') {
3297 g_string_append_c(norm_utf7, '+');
3300 g_string_append_c(norm_utf7, '&');
3303 } else if (in_escape && *p == ',') {
3304 g_string_append_c(norm_utf7, '/');
3305 } else if (in_escape && *p == '-') {
3306 g_string_append_c(norm_utf7, '-');
3309 g_string_append_c(norm_utf7, *p);
3313 norm_utf7_p = norm_utf7->str;
3314 norm_utf7_len = norm_utf7->len;
3315 to_len = strlen(mutf7_str) * 5;
3316 to_p = to_str = g_malloc(to_len + 1);
3318 if (iconv(cd, (ICONV_CONST gchar **)&norm_utf7_p, &norm_utf7_len,
3319 &to_p, &to_len) == -1) {
3320 g_warning(_("iconv cannot convert UTF-7 to %s\n"),
3321 conv_get_current_charset_str());
3322 g_string_free(norm_utf7, TRUE);
3324 return g_strdup(mutf7_str);
3327 /* second iconv() call for flushing */
3328 iconv(cd, NULL, NULL, &to_p, &to_len);
3329 g_string_free(norm_utf7, TRUE);
3333 #endif /* !HAVE_ICONV */
3336 static gchar *imap_locale_to_modified_utf7(const gchar *from)
3339 const gchar *from_p;
3342 to = g_malloc(strlen(from) * 2 + 1);
3345 for (from_p = from; *from_p != '\0'; from_p++) {
3346 if (*from_p == '&') {
3356 static iconv_t cd = (iconv_t)-1;
3357 static gboolean iconv_ok = TRUE;
3358 gchar *norm_utf7, *norm_utf7_p;
3359 size_t from_len, norm_utf7_len;
3361 gchar *from_tmp, *to, *p;
3362 gboolean in_escape = FALSE;
3364 if (!iconv_ok) return g_strdup(from);
3366 if (cd == (iconv_t)-1) {
3367 cd = iconv_open("UTF-7", conv_get_current_charset_str());
3368 if (cd == (iconv_t)-1) {
3369 g_warning("iconv cannot convert %s to UTF-7\n",
3370 conv_get_current_charset_str());
3372 return g_strdup(from);
3376 Xstrdup_a(from_tmp, from, return g_strdup(from));
3377 from_len = strlen(from);
3378 norm_utf7_len = from_len * 5;
3379 Xalloca(norm_utf7, norm_utf7_len + 1, return g_strdup(from));
3380 norm_utf7_p = norm_utf7;
3382 #define IS_PRINT(ch) (isprint(ch) && isascii(ch))
3384 while (from_len > 0) {
3385 if (*from_tmp == '+') {
3386 *norm_utf7_p++ = '+';
3387 *norm_utf7_p++ = '-';
3391 } else if (IS_PRINT(*from_tmp)) {
3392 /* printable ascii char */
3393 *norm_utf7_p = *from_tmp;
3399 size_t mb_len = 0, conv_len = 0;
3401 /* unprintable char: convert to UTF-7 */
3403 while (!IS_PRINT(*p) && conv_len < from_len) {
3404 mb_len = mblen(p, MB_LEN_MAX);
3406 g_warning("wrong multibyte sequence\n");
3407 return g_strdup(from);
3413 from_len -= conv_len;
3414 if (iconv(cd, (ICONV_CONST gchar **)&from_tmp,
3416 &norm_utf7_p, &norm_utf7_len) == -1) {
3417 g_warning("iconv cannot convert %s to UTF-7\n",
3418 conv_get_current_charset_str());
3419 return g_strdup(from);
3422 /* second iconv() call for flushing */
3423 iconv(cd, NULL, NULL, &norm_utf7_p, &norm_utf7_len);
3429 *norm_utf7_p = '\0';
3430 to_str = g_string_new(NULL);
3431 for (p = norm_utf7; p < norm_utf7_p; p++) {
3432 /* replace: '&' -> "&-",
3435 BASE64 '/' -> ',' */
3436 if (!in_escape && *p == '&') {
3437 g_string_append(to_str, "&-");
3438 } else if (!in_escape && *p == '+') {
3439 if (*(p + 1) == '-') {
3440 g_string_append_c(to_str, '+');
3443 g_string_append_c(to_str, '&');
3446 } else if (in_escape && *p == '/') {
3447 g_string_append_c(to_str, ',');
3448 } else if (in_escape && *p == '-') {
3449 g_string_append_c(to_str, '-');
3452 g_string_append_c(to_str, *p);
3458 g_string_append_c(to_str, '-');
3462 g_string_free(to_str, FALSE);
3465 #endif /* !HAVE_ICONV */
3468 static GSList *imap_get_seq_set_from_numlist(MsgNumberList *numlist)
3471 GSList *sorted_list, *cur;
3472 guint first, last, next;
3474 GSList *ret_list = NULL;
3476 if (numlist == NULL)
3479 str = g_string_sized_new(256);
3481 sorted_list = g_slist_copy(numlist);
3482 sorted_list = g_slist_sort(sorted_list, g_int_compare);
3484 first = GPOINTER_TO_INT(sorted_list->data);
3486 for (cur = sorted_list; cur != NULL; cur = g_slist_next(cur)) {
3487 last = GPOINTER_TO_INT(cur->data);
3489 next = GPOINTER_TO_INT(cur->next->data);
3493 if (last + 1 != next || next == 0) {
3495 g_string_append_c(str, ',');
3497 g_string_sprintfa(str, "%u", first);
3499 g_string_sprintfa(str, "%u:%u", first, last);
3503 if (str->len > IMAP_CMD_LIMIT) {
3504 ret_str = g_strdup(str->str);
3505 ret_list = g_slist_append(ret_list, ret_str);
3506 g_string_truncate(str, 0);
3512 ret_str = g_strdup(str->str);
3513 ret_list = g_slist_append(ret_list, ret_str);
3516 g_slist_free(sorted_list);
3517 g_string_free(str, TRUE);
3522 static GSList *imap_get_seq_set_from_msglist(MsgInfoList *msglist)
3524 MsgNumberList *numlist = NULL;
3528 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
3529 MsgInfo *msginfo = (MsgInfo *) cur->data;
3531 numlist = g_slist_append(numlist, GINT_TO_POINTER(msginfo->msgnum));
3533 seq_list = imap_get_seq_set_from_numlist(numlist);
3534 g_slist_free(numlist);
3539 static void imap_seq_set_free(GSList *seq_list)
3541 slist_free_strings(seq_list);
3542 g_slist_free(seq_list);
3546 static gboolean imap_rename_folder_func(GNode *node, gpointer data)
3548 FolderItem *item = node->data;
3549 gchar **paths = data;
3550 const gchar *oldpath = paths[0];
3551 const gchar *newpath = paths[1];
3553 gchar *new_itempath;
3556 oldpathlen = strlen(oldpath);
3557 if (strncmp(oldpath, item->path, oldpathlen) != 0) {
3558 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
3562 base = item->path + oldpathlen;
3563 while (*base == G_DIR_SEPARATOR) base++;
3565 new_itempath = g_strdup(newpath);
3567 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
3570 item->path = new_itempath;
3575 static gint get_list_of_uids(Folder *folder, IMAPFolderItem *item, GSList **msgnum_list)
3577 gint ok, nummsgs = 0, lastuid_old;
3578 IMAPSession *session;
3579 GSList *uidlist, *elem;
3582 session = imap_session_get(folder);
3583 g_return_val_if_fail(session != NULL, -1);
3585 ok = imap_select(session, IMAP_FOLDER(folder), item->item.path,
3586 NULL, NULL, NULL, NULL);
3587 if (ok != IMAP_SUCCESS)
3590 cmd_buf = g_strdup_printf("UID %d:*", item->lastuid + 1);
3591 ok = imap_cmd_search(session, cmd_buf, &uidlist);
3594 if (ok == IMAP_SOCKET) {
3595 session_destroy((Session *)session);
3596 ((RemoteFolder *)folder)->session = NULL;
3600 if (ok != IMAP_SUCCESS) {
3604 argbuf = g_ptr_array_new();
3606 cmd_buf = g_strdup_printf("UID FETCH %d:* (UID)", item->lastuid + 1);
3607 imap_gen_send(session, cmd_buf);
3609 ok = imap_cmd_ok(session, argbuf);
3610 if (ok != IMAP_SUCCESS) {
3611 ptr_array_free_strings(argbuf);
3612 g_ptr_array_free(argbuf, TRUE);
3616 for(i = 0; i < argbuf->len; i++) {
3619 if((ret = sscanf(g_ptr_array_index(argbuf, i),
3620 "%*d FETCH (UID %d)", &msgnum)) == 1)
3621 uidlist = g_slist_prepend(uidlist, GINT_TO_POINTER(msgnum));
3623 ptr_array_free_strings(argbuf);
3624 g_ptr_array_free(argbuf, TRUE);
3627 lastuid_old = item->lastuid;
3628 *msgnum_list = g_slist_copy(item->uid_list);
3629 nummsgs = g_slist_length(*msgnum_list);
3630 debug_print("Got %d uids from cache\n", g_slist_length(item->uid_list));
3632 for (elem = uidlist; elem != NULL; elem = g_slist_next(elem)) {
3635 msgnum = GPOINTER_TO_INT(elem->data);
3636 if (msgnum > lastuid_old) {
3637 *msgnum_list = g_slist_prepend(*msgnum_list, GINT_TO_POINTER(msgnum));
3638 item->uid_list = g_slist_prepend(item->uid_list, GINT_TO_POINTER(msgnum));
3641 if(msgnum > item->lastuid)
3642 item->lastuid = msgnum;
3645 g_slist_free(uidlist);
3650 gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list, gboolean *old_uids_valid)
3652 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3653 IMAPSession *session;
3654 gint ok, nummsgs = 0, exists, recent, uid_val, uid_next, unseen;
3657 gboolean selected_folder;
3659 g_return_val_if_fail(folder != NULL, -1);
3660 g_return_val_if_fail(item != NULL, -1);
3661 g_return_val_if_fail(item->item.path != NULL, -1);
3662 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3663 g_return_val_if_fail(folder->account != NULL, -1);
3665 session = imap_session_get(folder);
3666 g_return_val_if_fail(session != NULL, -1);
3668 selected_folder = (session->mbox != NULL) &&
3669 (!strcmp(session->mbox, item->item.path));
3670 if (selected_folder) {
3671 ok = imap_cmd_noop(session);
3672 if (ok != IMAP_SUCCESS)
3674 exists = session->exists;
3676 *old_uids_valid = TRUE;
3678 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
3679 &exists, &recent, &uid_next, &uid_val, &unseen);
3680 if (ok != IMAP_SUCCESS)
3683 if(item->item.mtime == uid_val)
3684 *old_uids_valid = TRUE;
3686 *old_uids_valid = FALSE;
3688 debug_print("Freeing imap uid cache\n");
3690 g_slist_free(item->uid_list);
3691 item->uid_list = NULL;
3693 item->item.mtime = uid_val;
3695 imap_delete_all_cached_messages((FolderItem *)item);
3699 if (!selected_folder)
3700 item->uid_next = uid_next;
3702 /* If old uid_next matches new uid_next we can be sure no message
3703 was added to the folder */
3704 if (( selected_folder && !session->folder_content_changed) ||
3705 (!selected_folder && uid_next == item->uid_next)) {
3706 nummsgs = g_slist_length(item->uid_list);
3708 /* If number of messages is still the same we
3709 know our caches message numbers are still valid,
3710 otherwise if the number of messages has decrease
3711 we discard our cache to start a new scan to find
3712 out which numbers have been removed */
3713 if (exists == nummsgs) {
3714 *msgnum_list = g_slist_copy(item->uid_list);
3716 } else if (exists < nummsgs) {
3717 debug_print("Freeing imap uid cache");
3719 g_slist_free(item->uid_list);
3720 item->uid_list = NULL;
3725 *msgnum_list = NULL;
3729 nummsgs = get_list_of_uids(folder, item, &uidlist);
3731 if (nummsgs != exists) {
3732 /* Cache contains more messages then folder, we have cached
3733 an old UID of a message that was removed and new messages
3734 have been added too, otherwise the uid_next check would
3736 debug_print("Freeing imap uid cache");
3738 g_slist_free(item->uid_list);
3739 item->uid_list = NULL;
3741 g_slist_free(*msgnum_list);
3743 nummsgs = get_list_of_uids(folder, item, &uidlist);
3746 *msgnum_list = uidlist;
3748 dir = folder_item_get_path((FolderItem *)item);
3749 debug_print("removing old messages from %s\n", dir);
3750 remove_numbered_files_not_in_list(dir, *msgnum_list);
3756 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item)
3761 flags.perm_flags = MSG_NEW|MSG_UNREAD;
3762 flags.tmp_flags = 0;
3764 g_return_val_if_fail(item != NULL, NULL);
3765 g_return_val_if_fail(file != NULL, NULL);
3767 if (item->stype == F_QUEUE) {
3768 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
3769 } else if (item->stype == F_DRAFT) {
3770 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
3773 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3774 if (!msginfo) return NULL;
3776 msginfo->folder = item;
3781 GSList *imap_get_msginfos(Folder *folder, FolderItem *item, GSList *msgnum_list)
3783 IMAPSession *session;
3784 MsgInfoList *ret = NULL;
3787 g_return_val_if_fail(folder != NULL, NULL);
3788 g_return_val_if_fail(item != NULL, NULL);
3789 g_return_val_if_fail(msgnum_list != NULL, NULL);
3791 session = imap_session_get(folder);
3792 g_return_val_if_fail(session != NULL, NULL);
3794 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3795 NULL, NULL, NULL, NULL);
3796 if (ok != IMAP_SUCCESS)
3799 if (!(item->stype == F_QUEUE || item->stype == F_DRAFT)) {
3800 ret = g_slist_concat(ret,
3801 imap_get_uncached_messages(
3802 session, item, msgnum_list));
3804 MsgNumberList *sorted_list, *elem;
3805 gint startnum, lastnum;
3807 sorted_list = g_slist_sort(g_slist_copy(msgnum_list), g_int_compare);
3809 startnum = lastnum = GPOINTER_TO_INT(sorted_list->data);
3811 for (elem = sorted_list;; elem = g_slist_next(elem)) {
3815 num = GPOINTER_TO_INT(elem->data);
3817 if (num > lastnum + 1 || elem == NULL) {
3819 for (i = startnum; i <= lastnum; ++i) {
3822 file = imap_fetch_msg(folder, item, i);
3824 MsgInfo *msginfo = imap_parse_msg(file, item);
3825 if (msginfo != NULL) {
3826 msginfo->msgnum = i;
3827 ret = g_slist_append(ret, msginfo);
3841 g_slist_free(sorted_list);
3847 MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid)
3849 MsgInfo *msginfo = NULL;
3850 MsgInfoList *msginfolist;
3851 MsgNumberList numlist;
3853 numlist.next = NULL;
3854 numlist.data = GINT_TO_POINTER(uid);
3856 msginfolist = imap_get_msginfos(folder, item, &numlist);
3857 if (msginfolist != NULL) {
3858 msginfo = msginfolist->data;
3859 g_slist_free(msginfolist);
3865 gboolean imap_scan_required(Folder *folder, FolderItem *_item)
3867 IMAPSession *session;
3868 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3869 gint ok, exists = 0, recent = 0, unseen = 0;
3870 guint32 uid_next, uid_val = 0;
3871 gboolean selected_folder;
3873 g_return_val_if_fail(folder != NULL, FALSE);
3874 g_return_val_if_fail(item != NULL, FALSE);
3875 g_return_val_if_fail(item->item.folder != NULL, FALSE);
3876 g_return_val_if_fail(FOLDER_CLASS(item->item.folder) == &imap_class, FALSE);
3878 if (item->item.path == NULL)
3881 session = imap_session_get(folder);
3882 g_return_val_if_fail(session != NULL, FALSE);
3884 selected_folder = (session->mbox != NULL) &&
3885 (!strcmp(session->mbox, item->item.path));
3886 if (selected_folder) {
3887 ok = imap_cmd_noop(session);
3888 if (ok != IMAP_SUCCESS)
3891 if (session->folder_content_changed)
3894 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
3895 &exists, &recent, &uid_next, &uid_val, &unseen);
3896 if (ok != IMAP_SUCCESS)
3899 if ((uid_next != item->uid_next) || (exists < item->item.total_msgs))
3906 void imap_change_flags(Folder *folder, FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
3908 IMAPSession *session;
3909 IMAPFlags flags_set = 0, flags_unset = 0;
3910 gint ok = IMAP_SUCCESS;
3911 MsgNumberList numlist;
3913 g_return_if_fail(folder != NULL);
3914 g_return_if_fail(folder->klass == &imap_class);
3915 g_return_if_fail(item != NULL);
3916 g_return_if_fail(item->folder == folder);
3917 g_return_if_fail(msginfo != NULL);
3918 g_return_if_fail(msginfo->folder == item);
3920 session = imap_session_get(folder);
3921 if (!session) return;
3923 if ((ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
3924 NULL, NULL, NULL, NULL)) != IMAP_SUCCESS)
3927 if (!MSG_IS_MARKED(msginfo->flags) && (newflags & MSG_MARKED))
3928 flags_set |= IMAP_FLAG_FLAGGED;
3929 if ( MSG_IS_MARKED(msginfo->flags) && !(newflags & MSG_MARKED))
3930 flags_unset |= IMAP_FLAG_FLAGGED;
3932 if (!MSG_IS_UNREAD(msginfo->flags) && (newflags & MSG_UNREAD))
3933 flags_unset |= IMAP_FLAG_SEEN;
3934 if ( MSG_IS_UNREAD(msginfo->flags) && !(newflags & MSG_UNREAD))
3935 flags_set |= IMAP_FLAG_SEEN;
3937 if (!MSG_IS_REPLIED(msginfo->flags) && (newflags & MSG_REPLIED))
3938 flags_set |= IMAP_FLAG_ANSWERED;
3939 if ( MSG_IS_REPLIED(msginfo->flags) && !(newflags & MSG_REPLIED))
3940 flags_set |= IMAP_FLAG_ANSWERED;
3942 numlist.next = NULL;
3943 numlist.data = GINT_TO_POINTER(msginfo->msgnum);
3946 ok = imap_set_message_flags(session, &numlist, flags_set, TRUE);
3947 if (ok != IMAP_SUCCESS) return;
3951 ok = imap_set_message_flags(session, &numlist, flags_unset, FALSE);
3952 if (ok != IMAP_SUCCESS) return;
3955 msginfo->flags.perm_flags = newflags;