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 void imap_folder_init (Folder *folder,
166 static Folder *imap_folder_new (const gchar *name,
168 static void imap_folder_destroy (Folder *folder);
170 static IMAPSession *imap_session_new (const PrefsAccount *account);
171 static void imap_session_authenticate(IMAPSession *session,
172 const PrefsAccount *account);
173 static void imap_session_destroy (Session *session);
175 static gchar *imap_fetch_msg (Folder *folder,
178 static gint imap_add_msg (Folder *folder,
182 static gint imap_add_msgs (Folder *folder,
185 GRelation *relation);
187 static gint imap_copy_msg (Folder *folder,
190 static gint imap_copy_msgs (Folder *folder,
192 MsgInfoList *msglist,
193 GRelation *relation);
195 static gint imap_remove_msg (Folder *folder,
198 static gint imap_remove_all_msg (Folder *folder,
201 static gboolean imap_is_msg_changed (Folder *folder,
205 static gint imap_close (Folder *folder,
208 static gint imap_scan_tree (Folder *folder);
210 static gint imap_create_tree (Folder *folder);
212 static FolderItem *imap_create_folder (Folder *folder,
215 static gint imap_rename_folder (Folder *folder,
218 static gint imap_remove_folder (Folder *folder,
221 static FolderItem *imap_folder_item_new (Folder *folder);
222 static void imap_folder_item_destroy (Folder *folder,
225 static IMAPSession *imap_session_get (Folder *folder);
227 static gint imap_greeting (IMAPSession *session);
228 static gint imap_auth (IMAPSession *session,
233 static gint imap_scan_tree_recursive (IMAPSession *session,
235 static GSList *imap_parse_list (IMAPFolder *folder,
236 IMAPSession *session,
237 const gchar *real_path,
240 static void imap_create_missing_folders (Folder *folder);
241 static FolderItem *imap_create_special_folder
243 SpecialFolderItemType stype,
246 static gint imap_do_copy_msgs (Folder *folder,
248 MsgInfoList *msglist,
249 GRelation *relation);
251 static void imap_delete_all_cached_messages (FolderItem *item);
254 static SockInfo *imap_open (const gchar *server,
258 static SockInfo *imap_open (const gchar *server,
263 static SockInfo *imap_open_tunnel(const gchar *server,
264 const gchar *tunnelcmd,
267 static SockInfo *imap_open_tunnel(const gchar *server,
268 const gchar *tunnelcmd);
272 static SockInfo *imap_init_sock(SockInfo *sock, SSLType ssl_type);
274 static SockInfo *imap_init_sock(SockInfo *sock);
277 static gchar *imap_get_flag_str (IMAPFlags flags);
278 static gint imap_set_message_flags (IMAPSession *session,
279 MsgNumberList *numlist,
282 static gint imap_select (IMAPSession *session,
288 guint32 *uid_validity);
289 static gint imap_status (IMAPSession *session,
295 guint32 *uid_validity,
298 static void imap_parse_namespace (IMAPSession *session,
300 static void imap_get_namespace_by_list (IMAPSession *session,
302 static IMAPNameSpace *imap_find_namespace (IMAPFolder *folder,
304 static gchar imap_get_path_separator (IMAPFolder *folder,
306 static gchar *imap_get_real_path (IMAPFolder *folder,
309 static gchar *imap_parse_atom (SockInfo *sock,
314 static MsgFlags imap_parse_flags (const gchar *flag_str);
315 static MsgInfo *imap_parse_envelope (SockInfo *sock,
319 static gboolean imap_has_capability (IMAPSession *session,
321 static void imap_free_capabilities (IMAPSession *session);
323 /* low-level IMAP4rev1 commands */
324 static gint imap_cmd_authenticate
325 (IMAPSession *session,
329 static gint imap_cmd_login (IMAPSession *session,
332 static gint imap_cmd_logout (IMAPSession *session);
333 static gint imap_cmd_noop (IMAPSession *session);
334 static gint imap_cmd_starttls (IMAPSession *session);
335 static gint imap_cmd_namespace (IMAPSession *session,
337 static gint imap_cmd_list (IMAPSession *session,
339 const gchar *mailbox,
341 static gint imap_cmd_do_select (IMAPSession *session,
347 guint32 *uid_validity);
348 static gint imap_cmd_select (IMAPSession *session,
353 guint32 *uid_validity);
354 static gint imap_cmd_examine (IMAPSession *session,
359 guint32 *uid_validity);
360 static gint imap_cmd_create (IMAPSession *sock,
361 const gchar *folder);
362 static gint imap_cmd_rename (IMAPSession *sock,
363 const gchar *oldfolder,
364 const gchar *newfolder);
365 static gint imap_cmd_delete (IMAPSession *session,
366 const gchar *folder);
367 static gint imap_cmd_envelope (IMAPSession *session,
369 static gint imap_cmd_fetch (IMAPSession *sock,
371 const gchar *filename);
372 static gint imap_cmd_append (IMAPSession *session,
373 const gchar *destfolder,
377 static gint imap_cmd_copy (IMAPSession *session,
378 const gchar *seq_set,
379 const gchar *destfolder,
380 GRelation *uid_mapping);
381 static gint imap_cmd_store (IMAPSession *session,
384 static gint imap_cmd_expunge (IMAPSession *session,
386 static gint imap_cmd_close (IMAPSession *session);
388 static gint imap_cmd_ok (IMAPSession *session,
390 static void imap_gen_send (IMAPSession *session,
391 const gchar *format, ...);
392 static gint imap_gen_recv (IMAPSession *session,
395 /* misc utility functions */
396 static gchar *strchr_cpy (const gchar *src,
400 static gchar *get_quoted (const gchar *src,
404 static gchar *search_array_contain_str (GPtrArray *array,
406 static gchar *search_array_str (GPtrArray *array,
408 static void imap_path_separator_subst (gchar *str,
411 static gchar *imap_modified_utf7_to_locale (const gchar *mutf7_str);
412 static gchar *imap_locale_to_modified_utf7 (const gchar *from);
414 static GSList *imap_get_seq_set_from_numlist (MsgNumberList *msglist);
415 static GSList *imap_get_seq_set_from_msglist (MsgInfoList *msglist);
416 static void imap_seq_set_free (GSList *seq_list);
418 static gboolean imap_rename_folder_func (GNode *node,
420 static gint imap_get_num_list (Folder *folder,
423 gboolean *old_uids_valid);
424 static GSList *imap_get_msginfos (Folder *folder,
426 GSList *msgnum_list);
427 static MsgInfo *imap_get_msginfo (Folder *folder,
430 static gboolean imap_scan_required (Folder *folder,
432 static void imap_change_flags (Folder *folder,
435 MsgPermFlags newflags);
436 static gchar *imap_folder_get_path (Folder *folder);
437 static gchar *imap_item_get_path (Folder *folder,
440 static FolderClass imap_class =
446 /* Folder functions */
452 /* FolderItem functions */
453 imap_folder_item_new,
454 imap_folder_item_destroy,
466 /* Message functions */
480 FolderClass *imap_get_class(void)
485 static Folder *imap_folder_new(const gchar *name, const gchar *path)
489 folder = (Folder *)g_new0(IMAPFolder, 1);
490 folder->klass = &imap_class;
491 imap_folder_init(folder, name, path);
496 static void imap_folder_destroy(Folder *folder)
500 dir = imap_folder_get_path(folder);
501 if (is_dir_exist(dir))
502 remove_dir_recursive(dir);
505 folder_remote_folder_destroy(REMOTE_FOLDER(folder));
508 static void imap_folder_init(Folder *folder, const gchar *name,
511 folder_remote_folder_init((Folder *)folder, name, path);
514 static FolderItem *imap_folder_item_new(Folder *folder)
516 IMAPFolderItem *item;
518 item = g_new0(IMAPFolderItem, 1);
521 item->uid_list = NULL;
523 return (FolderItem *)item;
526 static void imap_folder_item_destroy(Folder *folder, FolderItem *_item)
528 IMAPFolderItem *item = (IMAPFolderItem *)_item;
530 g_return_if_fail(item != NULL);
531 g_slist_free(item->uid_list);
536 static gboolean imap_reset_uid_lists_func(GNode *node, gpointer data)
538 IMAPFolderItem *item = (IMAPFolderItem *)node->data;
542 g_slist_free(item->uid_list);
543 item->uid_list = NULL;
548 static void imap_reset_uid_lists(Folder *folder)
550 if(folder->node == NULL)
553 /* Destroy all uid lists and rest last uid */
554 g_node_traverse(folder->node, G_IN_ORDER, G_TRAVERSE_ALL, -1, imap_reset_uid_lists_func, NULL);
557 /* Send CAPABILITY, and examine the server's response to see whether this
558 * connection is pre-authenticated or not and build a list of CAPABILITIES. */
559 static gint imap_greeting(IMAPSession *session)
564 imap_gen_send(session, "CAPABILITY");
566 argbuf = g_ptr_array_new();
568 if (imap_cmd_ok(session, argbuf) != IMAP_SUCCESS ||
569 ((capstr = search_array_str(argbuf, "CAPABILITY ")) == NULL)) {
570 ptr_array_free_strings(argbuf);
571 g_ptr_array_free(argbuf, TRUE);
575 session->authenticated = search_array_str(argbuf, "PREAUTH") != NULL;
577 capstr += strlen("CAPABILITY ");
579 IMAP_SESSION(session)->capability = g_strsplit(capstr, " ", 0);
581 ptr_array_free_strings(argbuf);
582 g_ptr_array_free(argbuf, TRUE);
584 if (imap_has_capability(session, "UIDPLUS"))
585 session->uidplus = TRUE;
590 static gint imap_auth(IMAPSession *session, const gchar *user, const gchar *pass,
595 if (type == 0 || type == IMAP_AUTH_LOGIN)
596 ok = imap_cmd_login(session, user, pass);
598 ok = imap_cmd_authenticate(session, user, pass, type);
600 if (ok == IMAP_SUCCESS)
601 session->authenticated = TRUE;
606 static IMAPSession *imap_session_get(Folder *folder)
608 RemoteFolder *rfolder = REMOTE_FOLDER(folder);
609 IMAPSession *session = NULL;
611 g_return_val_if_fail(folder != NULL, NULL);
612 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, NULL);
613 g_return_val_if_fail(folder->account != NULL, NULL);
615 /* Make sure we have a session */
616 if (rfolder->session != NULL) {
617 session = IMAP_SESSION(rfolder->session);
619 imap_reset_uid_lists(folder);
620 session = imap_session_new(folder->account);
625 if (SESSION(session)->sock->state == CONN_DISCONNECTED) {
626 debug_print("IMAP server disconnected\n");
627 session_destroy(SESSION(session));
628 imap_reset_uid_lists(folder);
629 session = imap_session_new(folder->account);
632 /* Make sure session is authenticated */
633 if (!IMAP_SESSION(session)->authenticated)
634 imap_session_authenticate(IMAP_SESSION(session), folder->account);
635 if (!IMAP_SESSION(session)->authenticated) {
636 session_destroy(SESSION(session));
637 rfolder->session = NULL;
641 /* Make sure we have parsed the IMAP namespace */
642 imap_parse_namespace(IMAP_SESSION(session),
643 IMAP_FOLDER(folder));
645 /* I think the point of this code is to avoid sending a
646 * keepalive if we've used the session recently and therefore
647 * think it's still alive. Unfortunately, most of the code
648 * does not yet check for errors on the socket, and so if the
649 * connection drops we don't notice until the timeout expires.
650 * A better solution than sending a NOOP every time would be
651 * for every command to be prepared to retry until it is
652 * successfully sent. -- mbp */
653 if (time(NULL) - session->last_access_time > SESSION_TIMEOUT) {
654 /* verify that the session is still alive */
655 if (imap_cmd_noop(session) != IMAP_SUCCESS) {
656 /* Check if this is the first try to establish a
657 connection, if yes we don't try to reconnect */
658 if (rfolder->session == NULL) {
659 log_warning(_("Connecting %s failed"),
660 folder->account->recv_server);
661 session_destroy(SESSION(session));
664 log_warning(_("IMAP4 connection to %s has been"
665 " disconnected. Reconnecting...\n"),
666 folder->account->recv_server);
667 session_destroy(SESSION(session));
668 /* Clear folders session to make imap_session_get create
669 a new session, because of rfolder->session == NULL
670 it will not try to reconnect again and so avoid an
672 rfolder->session = NULL;
673 session = imap_session_get(folder);
678 rfolder->session = SESSION(session);
680 session->last_access_time = time(NULL);
682 return IMAP_SESSION(session);
685 static IMAPSession *imap_session_new(const PrefsAccount *account)
687 IMAPSession *session;
692 /* FIXME: IMAP over SSL only... */
695 port = account->set_imapport ? account->imapport
696 : account->ssl_imap == SSL_TUNNEL ? IMAPS_PORT : IMAP4_PORT;
697 ssl_type = account->ssl_imap;
699 port = account->set_imapport ? account->imapport
703 if (account->set_tunnelcmd) {
704 log_message(_("creating tunneled IMAP4 connection\n"));
706 if ((imap_sock = imap_open_tunnel(account->recv_server,
710 if ((imap_sock = imap_open_tunnel(account->recv_server,
711 account->tunnelcmd)) == NULL)
715 g_return_val_if_fail(account->recv_server != NULL, NULL);
717 log_message(_("creating IMAP4 connection to %s:%d ...\n"),
718 account->recv_server, port);
721 if ((imap_sock = imap_open(account->recv_server, port,
724 if ((imap_sock = imap_open(account->recv_server, port)) == NULL)
729 session = g_new0(IMAPSession, 1);
730 session_init(SESSION(session));
731 SESSION(session)->type = SESSION_IMAP;
732 SESSION(session)->server = g_strdup(account->recv_server);
733 SESSION(session)->sock = imap_sock;
735 SESSION(session)->destroy = imap_session_destroy;
737 session->capability = NULL;
739 session->authenticated = FALSE;
740 session->mbox = NULL;
741 session->cmd_count = 0;
743 /* Only need to log in if the connection was not PREAUTH */
744 if (imap_greeting(session) != IMAP_SUCCESS) {
745 session_destroy(SESSION(session));
750 if (account->ssl_imap == SSL_STARTTLS &&
751 imap_has_capability(session, "STARTTLS")) {
754 ok = imap_cmd_starttls(session);
755 if (ok != IMAP_SUCCESS) {
756 log_warning(_("Can't start TLS session.\n"));
757 session_destroy(SESSION(session));
760 if (!ssl_init_socket_with_method(SESSION(session)->sock,
762 session_destroy(SESSION(session));
766 imap_free_capabilities(session);
767 session->authenticated = FALSE;
768 session->uidplus = FALSE;
769 session->cmd_count = 1;
771 if (imap_greeting(session) != IMAP_SUCCESS) {
772 session_destroy(SESSION(session));
777 log_message("IMAP connection is %s-authenticated\n",
778 (session->authenticated) ? "pre" : "un");
783 static void imap_session_authenticate(IMAPSession *session,
784 const PrefsAccount *account)
788 g_return_if_fail(account->userid != NULL);
790 pass = account->passwd;
793 tmp_pass = input_dialog_query_password(account->recv_server, account->userid);
796 Xstrdup_a(pass, tmp_pass, {g_free(tmp_pass); return;});
800 if (imap_auth(session, account->userid, pass, account->imap_auth_type) != IMAP_SUCCESS) {
801 imap_cmd_logout(session);
805 session->authenticated = TRUE;
808 static void imap_session_destroy(Session *session)
810 imap_free_capabilities(IMAP_SESSION(session));
811 g_free(IMAP_SESSION(session)->mbox);
812 sock_close(session->sock);
813 session->sock = NULL;
816 static gchar *imap_fetch_msg(Folder *folder, FolderItem *item, gint uid)
818 gchar *path, *filename;
819 IMAPSession *session;
822 g_return_val_if_fail(folder != NULL, NULL);
823 g_return_val_if_fail(item != NULL, NULL);
825 path = folder_item_get_path(item);
826 if (!is_dir_exist(path))
828 filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(uid), NULL);
831 if (is_file_exist(filename)) {
832 debug_print("message %d has been already cached.\n", uid);
836 session = imap_session_get(folder);
842 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
843 NULL, NULL, NULL, NULL);
844 if (ok != IMAP_SUCCESS) {
845 g_warning("can't select mailbox %s\n", item->path);
850 debug_print("getting message %d...\n", uid);
851 ok = imap_cmd_fetch(session, (guint32)uid, filename);
853 if (ok != IMAP_SUCCESS) {
854 g_warning("can't fetch message %d\n", uid);
862 static gint imap_add_msg(Folder *folder, FolderItem *dest,
863 const gchar *file, MsgFlags *flags)
867 MsgFileInfo fileinfo;
869 g_return_val_if_fail(file != NULL, -1);
871 fileinfo.msginfo = NULL;
872 fileinfo.file = (gchar *)file;
873 fileinfo.flags = flags;
874 file_list.data = &fileinfo;
875 file_list.next = NULL;
877 ret = imap_add_msgs(folder, dest, &file_list, NULL);
881 static gint imap_add_msgs(Folder *folder, FolderItem *dest, GSList *file_list,
885 IMAPSession *session;
886 guint32 last_uid = 0;
888 MsgFileInfo *fileinfo;
891 g_return_val_if_fail(folder != NULL, -1);
892 g_return_val_if_fail(dest != NULL, -1);
893 g_return_val_if_fail(file_list != NULL, -1);
895 session = imap_session_get(folder);
896 if (!session) return -1;
898 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
900 for (cur = file_list; cur != NULL; cur = cur->next) {
901 IMAPFlags iflags = 0;
904 fileinfo = (MsgFileInfo *)cur->data;
906 if (fileinfo->flags) {
907 if (MSG_IS_MARKED(*fileinfo->flags))
908 iflags |= IMAP_FLAG_FLAGGED;
909 if (MSG_IS_REPLIED(*fileinfo->flags))
910 iflags |= IMAP_FLAG_ANSWERED;
911 if (!MSG_IS_UNREAD(*fileinfo->flags))
912 iflags |= IMAP_FLAG_SEEN;
915 if (dest->stype == F_OUTBOX ||
916 dest->stype == F_QUEUE ||
917 dest->stype == F_DRAFT ||
918 dest->stype == F_TRASH)
919 iflags |= IMAP_FLAG_SEEN;
921 ok = imap_cmd_append(session, destdir, fileinfo->file, iflags,
924 if (ok != IMAP_SUCCESS) {
925 g_warning("can't append message %s\n", fileinfo->file);
930 if (relation != NULL)
931 g_relation_insert(relation, fileinfo->msginfo != NULL ?
932 (gpointer) fileinfo->msginfo : (gpointer) fileinfo,
933 GINT_TO_POINTER(dest->last_num + 1));
934 if (last_uid < new_uid)
943 static gint imap_do_copy_msgs(Folder *folder, FolderItem *dest,
944 MsgInfoList *msglist, GRelation *relation)
948 GSList *seq_list, *cur;
950 IMAPSession *session;
951 gint ok = IMAP_SUCCESS;
952 GRelation *uid_mapping;
955 g_return_val_if_fail(folder != NULL, -1);
956 g_return_val_if_fail(dest != NULL, -1);
957 g_return_val_if_fail(msglist != NULL, -1);
959 session = imap_session_get(folder);
960 if (!session) return -1;
962 msginfo = (MsgInfo *)msglist->data;
964 src = msginfo->folder;
966 g_warning("the src folder is identical to the dest.\n");
970 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
971 NULL, NULL, NULL, NULL);
972 if (ok != IMAP_SUCCESS)
975 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
976 seq_list = imap_get_seq_set_from_msglist(msglist);
977 uid_mapping = g_relation_new(2);
978 g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
980 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
981 gchar *seq_set = (gchar *)cur->data;
983 debug_print("Copying message %s%c[%s] to %s ...\n",
984 src->path, G_DIR_SEPARATOR,
987 ok = imap_cmd_copy(session, seq_set, destdir, uid_mapping);
988 if (ok != IMAP_SUCCESS) {
989 g_relation_destroy(uid_mapping);
990 imap_seq_set_free(seq_list);
995 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
996 MsgInfo *msginfo = (MsgInfo *)cur->data;
999 tuples = g_relation_select(uid_mapping,
1000 GINT_TO_POINTER(msginfo->msgnum),
1002 if (tuples->len > 0) {
1003 gint num = GPOINTER_TO_INT(g_tuples_index(tuples, 0, 1));
1004 g_relation_insert(relation, msginfo,
1005 GPOINTER_TO_INT(num));
1009 g_relation_insert(relation, msginfo,
1010 GPOINTER_TO_INT(0));
1011 g_tuples_destroy(tuples);
1014 imap_seq_set_free(seq_list);
1018 if (ok == IMAP_SUCCESS)
1024 static gint imap_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1028 g_return_val_if_fail(msginfo != NULL, -1);
1030 msglist.data = msginfo;
1031 msglist.next = NULL;
1033 return imap_copy_msgs(folder, dest, &msglist, NULL);
1036 static gint imap_copy_msgs(Folder *folder, FolderItem *dest,
1037 MsgInfoList *msglist, GRelation *relation)
1043 g_return_val_if_fail(folder != NULL, -1);
1044 g_return_val_if_fail(dest != NULL, -1);
1045 g_return_val_if_fail(msglist != NULL, -1);
1047 msginfo = (MsgInfo *)msglist->data;
1048 g_return_val_if_fail(msginfo->folder != NULL, -1);
1050 if (folder == msginfo->folder->folder)
1051 return imap_do_copy_msgs(folder, dest, msglist, relation);
1053 file_list = procmsg_get_message_file_list(msglist);
1054 g_return_val_if_fail(file_list != NULL, -1);
1056 ret = imap_add_msgs(folder, dest, file_list, relation);
1058 procmsg_message_file_list_free(file_list);
1063 static gint imap_remove_msg(Folder *folder, FolderItem *item, gint uid)
1066 IMAPSession *session;
1068 MsgNumberList numlist;
1070 g_return_val_if_fail(folder != NULL, -1);
1071 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
1072 g_return_val_if_fail(item != NULL, -1);
1074 session = imap_session_get(folder);
1075 if (!session) return -1;
1077 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
1078 NULL, NULL, NULL, NULL);
1079 if (ok != IMAP_SUCCESS)
1082 numlist.next = NULL;
1083 numlist.data = GINT_TO_POINTER(uid);
1085 ok = imap_set_message_flags
1086 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
1087 &numlist, IMAP_FLAG_DELETED, TRUE);
1088 if (ok != IMAP_SUCCESS) {
1089 log_warning(_("can't set deleted flags: %d\n"), uid);
1093 if (!session->uidplus) {
1094 ok = imap_cmd_expunge(session, NULL);
1098 uidstr = g_strdup_printf("%u", uid);
1099 ok = imap_cmd_expunge(session, uidstr);
1102 if (ok != IMAP_SUCCESS) {
1103 log_warning(_("can't expunge\n"));
1107 IMAP_FOLDER_ITEM(item)->uid_list = g_slist_remove(
1108 IMAP_FOLDER_ITEM(item)->uid_list, numlist.data);
1109 dir = folder_item_get_path(item);
1110 if (is_dir_exist(dir))
1111 remove_numbered_files(dir, uid, uid);
1114 return IMAP_SUCCESS;
1117 static gint imap_remove_all_msg(Folder *folder, FolderItem *item)
1120 IMAPSession *session;
1123 g_return_val_if_fail(folder != NULL, -1);
1124 g_return_val_if_fail(item != NULL, -1);
1126 session = imap_session_get(folder);
1127 if (!session) return -1;
1129 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
1130 NULL, NULL, NULL, NULL);
1131 if (ok != IMAP_SUCCESS)
1134 imap_gen_send(session, "STORE 1:* +FLAGS.SILENT (\\Deleted)");
1135 ok = imap_cmd_ok(session, NULL);
1136 if (ok != IMAP_SUCCESS) {
1137 log_warning(_("can't set deleted flags: 1:*\n"));
1141 ok = imap_cmd_expunge(session, NULL);
1142 if (ok != IMAP_SUCCESS) {
1143 log_warning(_("can't expunge\n"));
1147 dir = folder_item_get_path(item);
1148 if (is_dir_exist(dir))
1149 remove_all_numbered_files(dir);
1152 return IMAP_SUCCESS;
1155 static gboolean imap_is_msg_changed(Folder *folder, FolderItem *item,
1158 /* TODO: properly implement this method */
1162 static gint imap_close(Folder *folder, FolderItem *item)
1165 IMAPSession *session;
1167 g_return_val_if_fail(folder != NULL, -1);
1168 g_return_val_if_fail(item != NULL, -1);
1169 g_return_val_if_fail(item->path != NULL, -1);
1171 session = imap_session_get(folder);
1172 if (!session) return -1;
1174 if (session->mbox) {
1175 if (strcmp2(session->mbox, item->path) != 0) return -1;
1177 ok = imap_cmd_close(session);
1178 if (ok != IMAP_SUCCESS)
1179 log_warning(_("can't close folder\n"));
1181 g_free(session->mbox);
1182 session->mbox = NULL;
1190 static gint imap_scan_tree(Folder *folder)
1192 FolderItem *item = NULL;
1193 IMAPSession *session;
1194 gchar *root_folder = NULL;
1196 g_return_val_if_fail(folder != NULL, -1);
1197 g_return_val_if_fail(folder->account != NULL, -1);
1199 session = imap_session_get(folder);
1201 if (!folder->node) {
1202 folder_tree_destroy(folder);
1203 item = folder_item_new(folder, folder->name, NULL);
1204 item->folder = folder;
1205 folder->node = item->node = g_node_new(item);
1210 if (folder->account->imap_dir && *folder->account->imap_dir) {
1215 Xstrdup_a(root_folder, folder->account->imap_dir, return -1);
1216 extract_quote(root_folder, '"');
1217 subst_char(root_folder,
1218 imap_get_path_separator(IMAP_FOLDER(folder),
1221 strtailchomp(root_folder, '/');
1222 real_path = imap_get_real_path
1223 (IMAP_FOLDER(folder), root_folder);
1224 debug_print("IMAP root directory: %s\n", real_path);
1226 /* check if root directory exist */
1227 argbuf = g_ptr_array_new();
1228 ok = imap_cmd_list(session, NULL, real_path, argbuf);
1229 if (ok != IMAP_SUCCESS ||
1230 search_array_str(argbuf, "LIST ") == NULL) {
1231 log_warning(_("root folder %s does not exist\n"), real_path);
1232 g_ptr_array_free(argbuf, TRUE);
1236 g_ptr_array_free(argbuf, TRUE);
1241 item = FOLDER_ITEM(folder->node->data);
1242 if (!item || ((item->path || root_folder) &&
1243 strcmp2(item->path, root_folder) != 0)) {
1244 folder_tree_destroy(folder);
1245 item = folder_item_new(folder, folder->name, root_folder);
1246 item->folder = folder;
1247 folder->node = item->node = g_node_new(item);
1250 imap_scan_tree_recursive(session, FOLDER_ITEM(folder->node->data));
1251 imap_create_missing_folders(folder);
1256 static gint imap_scan_tree_recursive(IMAPSession *session, FolderItem *item)
1259 IMAPFolder *imapfolder;
1260 FolderItem *new_item;
1261 GSList *item_list, *cur;
1264 gchar *wildcard_path;
1268 g_return_val_if_fail(item != NULL, -1);
1269 g_return_val_if_fail(item->folder != NULL, -1);
1270 g_return_val_if_fail(item->no_sub == FALSE, -1);
1272 folder = item->folder;
1273 imapfolder = IMAP_FOLDER(folder);
1275 separator = imap_get_path_separator(imapfolder, item->path);
1277 if (folder->ui_func)
1278 folder->ui_func(folder, item, folder->ui_func_data);
1281 wildcard[0] = separator;
1284 real_path = imap_get_real_path(imapfolder, item->path);
1288 real_path = g_strdup("");
1291 Xstrcat_a(wildcard_path, real_path, wildcard,
1292 {g_free(real_path); return IMAP_ERROR;});
1293 QUOTE_IF_REQUIRED(wildcard_path, wildcard_path);
1295 imap_gen_send(session, "LIST \"\" %s",
1298 strtailchomp(real_path, separator);
1299 item_list = imap_parse_list(imapfolder, session, real_path, NULL);
1302 node = item->node->children;
1303 while (node != NULL) {
1304 FolderItem *old_item = FOLDER_ITEM(node->data);
1305 GNode *next = node->next;
1308 for (cur = item_list; cur != NULL; cur = cur->next) {
1309 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1310 if (!strcmp2(old_item->path, cur_item->path)) {
1311 new_item = cur_item;
1316 debug_print("folder '%s' not found. removing...\n",
1318 folder_item_remove(old_item);
1320 old_item->no_sub = new_item->no_sub;
1321 old_item->no_select = new_item->no_select;
1322 if (old_item->no_sub == TRUE && node->children) {
1323 debug_print("folder '%s' doesn't have "
1324 "subfolders. removing...\n",
1326 folder_item_remove_children(old_item);
1333 for (cur = item_list; cur != NULL; cur = cur->next) {
1334 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1336 for (node = item->node->children; node != NULL;
1337 node = node->next) {
1338 if (!strcmp2(FOLDER_ITEM(node->data)->path,
1340 new_item = FOLDER_ITEM(node->data);
1341 folder_item_destroy(cur_item);
1347 new_item = cur_item;
1348 debug_print("new folder '%s' found.\n", new_item->path);
1349 folder_item_append(item, new_item);
1352 if (!strcmp(new_item->path, "INBOX")) {
1353 new_item->stype = F_INBOX;
1354 folder->inbox = new_item;
1355 } else if (!item->parent || item->stype == F_INBOX) {
1358 base = g_basename(new_item->path);
1360 if (!folder->outbox && !strcasecmp(base, "Sent")) {
1361 new_item->stype = F_OUTBOX;
1362 folder->outbox = new_item;
1363 } else if (!folder->draft && !strcasecmp(base, "Drafts")) {
1364 new_item->stype = F_DRAFT;
1365 folder->draft = new_item;
1366 } else if (!folder->queue && !strcasecmp(base, "Queue")) {
1367 new_item->stype = F_QUEUE;
1368 folder->queue = new_item;
1369 } else if (!folder->trash && !strcasecmp(base, "Trash")) {
1370 new_item->stype = F_TRASH;
1371 folder->trash = new_item;
1375 if (new_item->no_sub == FALSE)
1376 imap_scan_tree_recursive(session, new_item);
1379 g_slist_free(item_list);
1381 return IMAP_SUCCESS;
1384 static GSList *imap_parse_list(IMAPFolder *folder, IMAPSession *session,
1385 const gchar *real_path, gchar *separator)
1387 gchar buf[IMAPBUFSIZE];
1389 gchar separator_str[16];
1392 gchar *loc_name, *loc_path;
1393 GSList *item_list = NULL;
1395 FolderItem *new_item;
1397 debug_print("getting list of %s ...\n",
1398 *real_path ? real_path : "\"\"");
1400 str = g_string_new(NULL);
1403 if (sock_gets(SESSION(session)->sock, buf, sizeof(buf)) <= 0) {
1404 log_warning(_("error occurred while getting LIST.\n"));
1408 if (buf[0] != '*' || buf[1] != ' ') {
1409 log_print("IMAP4< %s\n", buf);
1410 if (sscanf(buf, "%*d %16s", buf) < 1 ||
1411 strcmp(buf, "OK") != 0)
1412 log_warning(_("error occurred while getting LIST.\n"));
1416 debug_print("IMAP4< %s\n", buf);
1418 g_string_assign(str, buf);
1420 if (strncmp(p, "LIST ", 5) != 0) continue;
1423 if (*p != '(') continue;
1425 p = strchr_cpy(p, ')', flags, sizeof(flags));
1427 while (*p == ' ') p++;
1429 p = strchr_cpy(p, ' ', separator_str, sizeof(separator_str));
1431 extract_quote(separator_str, '"');
1432 if (!strcmp(separator_str, "NIL"))
1433 separator_str[0] = '\0';
1435 *separator = separator_str[0];
1438 while (*p == ' ') p++;
1439 if (*p == '{' || *p == '"')
1440 p = imap_parse_atom(SESSION(session)->sock, p,
1441 buf, sizeof(buf), str);
1443 strncpy2(buf, p, sizeof(buf));
1444 strtailchomp(buf, separator_str[0]);
1445 if (buf[0] == '\0') continue;
1446 if (!strcmp(buf, real_path)) continue;
1448 if (separator_str[0] != '\0')
1449 subst_char(buf, separator_str[0], '/');
1450 name = g_basename(buf);
1451 if (name[0] == '.') continue;
1453 loc_name = imap_modified_utf7_to_locale(name);
1454 loc_path = imap_modified_utf7_to_locale(buf);
1455 new_item = folder_item_new(FOLDER(folder), loc_name, loc_path);
1456 if (strcasestr(flags, "\\Noinferiors") != NULL)
1457 new_item->no_sub = TRUE;
1458 if (strcmp(buf, "INBOX") != 0 &&
1459 strcasestr(flags, "\\Noselect") != NULL)
1460 new_item->no_select = TRUE;
1462 item_list = g_slist_append(item_list, new_item);
1464 debug_print("folder '%s' found.\n", loc_path);
1469 g_string_free(str, TRUE);
1474 static gint imap_create_tree(Folder *folder)
1476 g_return_val_if_fail(folder != NULL, -1);
1477 g_return_val_if_fail(folder->node != NULL, -1);
1478 g_return_val_if_fail(folder->node->data != NULL, -1);
1479 g_return_val_if_fail(folder->account != NULL, -1);
1481 imap_scan_tree(folder);
1482 imap_create_missing_folders(folder);
1487 static void imap_create_missing_folders(Folder *folder)
1489 g_return_if_fail(folder != NULL);
1492 folder->inbox = imap_create_special_folder
1493 (folder, F_INBOX, "INBOX");
1495 if (!folder->outbox)
1496 folder->outbox = imap_create_special_folder
1497 (folder, F_OUTBOX, "Sent");
1499 folder->draft = imap_create_special_folder
1500 (folder, F_DRAFT, "Drafts");
1502 folder->queue = imap_create_special_folder
1503 (folder, F_QUEUE, "Queue");
1506 folder->trash = imap_create_special_folder
1507 (folder, F_TRASH, "Trash");
1510 static FolderItem *imap_create_special_folder(Folder *folder,
1511 SpecialFolderItemType stype,
1515 FolderItem *new_item;
1517 g_return_val_if_fail(folder != NULL, NULL);
1518 g_return_val_if_fail(folder->node != NULL, NULL);
1519 g_return_val_if_fail(folder->node->data != NULL, NULL);
1520 g_return_val_if_fail(folder->account != NULL, NULL);
1521 g_return_val_if_fail(name != NULL, NULL);
1523 item = FOLDER_ITEM(folder->node->data);
1524 new_item = imap_create_folder(folder, item, name);
1527 g_warning("Can't create '%s'\n", name);
1528 if (!folder->inbox) return NULL;
1530 new_item = imap_create_folder(folder, folder->inbox, name);
1532 g_warning("Can't create '%s' under INBOX\n", name);
1534 new_item->stype = stype;
1536 new_item->stype = stype;
1541 static gchar *imap_folder_get_path(Folder *folder)
1545 g_return_val_if_fail(folder != NULL, NULL);
1546 g_return_val_if_fail(folder->account != NULL, NULL);
1548 folder_path = g_strconcat(get_imap_cache_dir(),
1550 folder->account->recv_server,
1552 folder->account->userid,
1558 static gchar *imap_item_get_path(Folder *folder, FolderItem *item)
1560 gchar *folder_path, *path;
1562 g_return_val_if_fail(folder != NULL, NULL);
1563 g_return_val_if_fail(item != NULL, NULL);
1564 folder_path = imap_folder_get_path(folder);
1566 g_return_val_if_fail(folder_path != NULL, NULL);
1567 if (folder_path[0] == G_DIR_SEPARATOR) {
1569 path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
1572 path = g_strdup(folder_path);
1575 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1576 folder_path, G_DIR_SEPARATOR_S,
1579 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1582 g_free(folder_path);
1587 static FolderItem *imap_create_folder(Folder *folder, FolderItem *parent,
1590 gchar *dirpath, *imap_path;
1591 IMAPSession *session;
1592 FolderItem *new_item;
1598 g_return_val_if_fail(folder != NULL, NULL);
1599 g_return_val_if_fail(folder->account != NULL, NULL);
1600 g_return_val_if_fail(parent != NULL, NULL);
1601 g_return_val_if_fail(name != NULL, NULL);
1603 session = imap_session_get(folder);
1604 if (!session) return NULL;
1606 if (!parent->parent && strcmp(name, "INBOX") == 0)
1607 dirpath = g_strdup(name);
1608 else if (parent->path)
1609 dirpath = g_strconcat(parent->path, "/", name, NULL);
1610 else if ((p = strchr(name, '/')) != NULL && *(p + 1) != '\0')
1611 dirpath = g_strdup(name);
1612 else if (folder->account->imap_dir && *folder->account->imap_dir) {
1615 Xstrdup_a(imap_dir, folder->account->imap_dir, return NULL);
1616 strtailchomp(imap_dir, '/');
1617 dirpath = g_strconcat(imap_dir, "/", name, NULL);
1619 dirpath = g_strdup(name);
1621 /* keep trailing directory separator to create a folder that contains
1623 imap_path = imap_locale_to_modified_utf7(dirpath);
1624 strtailchomp(dirpath, '/');
1625 Xstrdup_a(new_name, name, {g_free(dirpath); return NULL;});
1626 strtailchomp(new_name, '/');
1627 separator = imap_get_path_separator(IMAP_FOLDER(folder), imap_path);
1628 imap_path_separator_subst(imap_path, separator);
1629 subst_char(new_name, '/', separator);
1631 if (strcmp(name, "INBOX") != 0) {
1634 gboolean exist = FALSE;
1636 argbuf = g_ptr_array_new();
1637 ok = imap_cmd_list(session, NULL, imap_path,
1639 if (ok != IMAP_SUCCESS) {
1640 log_warning(_("can't create mailbox: LIST failed\n"));
1643 ptr_array_free_strings(argbuf);
1644 g_ptr_array_free(argbuf, TRUE);
1648 for (i = 0; i < argbuf->len; i++) {
1650 str = g_ptr_array_index(argbuf, i);
1651 if (!strncmp(str, "LIST ", 5)) {
1656 ptr_array_free_strings(argbuf);
1657 g_ptr_array_free(argbuf, TRUE);
1660 ok = imap_cmd_create(session, imap_path);
1661 if (ok != IMAP_SUCCESS) {
1662 log_warning(_("can't create mailbox\n"));
1670 new_item = folder_item_new(folder, new_name, dirpath);
1671 folder_item_append(parent, new_item);
1675 dirpath = folder_item_get_path(new_item);
1676 if (!is_dir_exist(dirpath))
1677 make_dir_hier(dirpath);
1683 static gint imap_rename_folder(Folder *folder, FolderItem *item,
1688 gchar *real_oldpath;
1689 gchar *real_newpath;
1691 gchar *old_cache_dir;
1692 gchar *new_cache_dir;
1693 IMAPSession *session;
1696 gint exists, recent, unseen;
1697 guint32 uid_validity;
1699 g_return_val_if_fail(folder != NULL, -1);
1700 g_return_val_if_fail(item != NULL, -1);
1701 g_return_val_if_fail(item->path != NULL, -1);
1702 g_return_val_if_fail(name != NULL, -1);
1704 session = imap_session_get(folder);
1705 if (!session) return -1;
1707 real_oldpath = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1709 g_free(session->mbox);
1710 session->mbox = NULL;
1711 ok = imap_cmd_examine(session, "INBOX",
1712 &exists, &recent, &unseen, &uid_validity);
1713 if (ok != IMAP_SUCCESS) {
1714 g_free(real_oldpath);
1718 separator = imap_get_path_separator(IMAP_FOLDER(folder), item->path);
1719 if (strchr(item->path, G_DIR_SEPARATOR)) {
1720 dirpath = g_dirname(item->path);
1721 newpath = g_strconcat(dirpath, G_DIR_SEPARATOR_S, name, NULL);
1724 newpath = g_strdup(name);
1726 real_newpath = imap_locale_to_modified_utf7(newpath);
1727 imap_path_separator_subst(real_newpath, separator);
1729 ok = imap_cmd_rename(session, real_oldpath, real_newpath);
1730 if (ok != IMAP_SUCCESS) {
1731 log_warning(_("can't rename mailbox: %s to %s\n"),
1732 real_oldpath, real_newpath);
1733 g_free(real_oldpath);
1735 g_free(real_newpath);
1740 item->name = g_strdup(name);
1742 old_cache_dir = folder_item_get_path(item);
1744 paths[0] = g_strdup(item->path);
1746 g_node_traverse(item->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1747 imap_rename_folder_func, paths);
1749 if (is_dir_exist(old_cache_dir)) {
1750 new_cache_dir = folder_item_get_path(item);
1751 if (rename(old_cache_dir, new_cache_dir) < 0) {
1752 FILE_OP_ERROR(old_cache_dir, "rename");
1754 g_free(new_cache_dir);
1757 g_free(old_cache_dir);
1760 g_free(real_oldpath);
1761 g_free(real_newpath);
1766 static gint imap_remove_folder(Folder *folder, FolderItem *item)
1769 IMAPSession *session;
1772 gint exists, recent, unseen;
1773 guint32 uid_validity;
1775 g_return_val_if_fail(folder != NULL, -1);
1776 g_return_val_if_fail(item != NULL, -1);
1777 g_return_val_if_fail(item->path != NULL, -1);
1779 session = imap_session_get(folder);
1780 if (!session) return -1;
1782 path = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1784 ok = imap_cmd_examine(session, "INBOX",
1785 &exists, &recent, &unseen, &uid_validity);
1786 if (ok != IMAP_SUCCESS) {
1791 ok = imap_cmd_delete(session, path);
1792 if (ok != IMAP_SUCCESS) {
1793 log_warning(_("can't delete mailbox\n"));
1799 cache_dir = folder_item_get_path(item);
1800 if (is_dir_exist(cache_dir) && remove_dir_recursive(cache_dir) < 0)
1801 g_warning("can't remove directory '%s'\n", cache_dir);
1803 folder_item_remove(item);
1808 static GSList *imap_get_uncached_messages(IMAPSession *session,
1810 MsgNumberList *numlist)
1813 GSList *newlist = NULL;
1814 GSList *llast = NULL;
1817 GSList *seq_list, *cur;
1820 g_return_val_if_fail(session != NULL, NULL);
1821 g_return_val_if_fail(item != NULL, NULL);
1822 g_return_val_if_fail(item->folder != NULL, NULL);
1823 g_return_val_if_fail(FOLDER_CLASS(item->folder) == &imap_class, NULL);
1825 seq_list = imap_get_seq_set_from_numlist(numlist);
1826 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
1827 imapset = cur->data;
1829 if (imap_cmd_envelope(session, imapset)
1831 log_warning(_("can't get envelope\n"));
1835 str = g_string_new(NULL);
1838 if ((tmp = sock_getline(SESSION(session)->sock)) == NULL) {
1839 log_warning(_("error occurred while getting envelope.\n"));
1840 g_string_free(str, TRUE);
1844 if (tmp[0] != '*' || tmp[1] != ' ') {
1845 log_print("IMAP4< %s\n", tmp);
1849 if (strstr(tmp, "FETCH") == NULL) {
1850 log_print("IMAP4< %s\n", tmp);
1854 log_print("IMAP4< %s\n", tmp);
1855 g_string_assign(str, tmp);
1858 msginfo = imap_parse_envelope
1859 (SESSION(session)->sock, item, str);
1861 log_warning(_("can't parse envelope: %s\n"), str->str);
1864 if (item->stype == F_QUEUE) {
1865 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_QUEUED);
1866 } else if (item->stype == F_DRAFT) {
1867 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_DRAFT);
1870 msginfo->folder = item;
1873 llast = newlist = g_slist_append(newlist, msginfo);
1875 llast = g_slist_append(llast, msginfo);
1876 llast = llast->next;
1880 g_string_free(str, TRUE);
1882 imap_seq_set_free(seq_list);
1887 static void imap_delete_all_cached_messages(FolderItem *item)
1891 g_return_if_fail(item != NULL);
1892 g_return_if_fail(item->folder != NULL);
1893 g_return_if_fail(FOLDER_CLASS(item->folder) == &imap_class);
1895 debug_print("Deleting all cached messages...\n");
1897 dir = folder_item_get_path(item);
1898 if (is_dir_exist(dir))
1899 remove_all_numbered_files(dir);
1902 debug_print("done.\n");
1906 static SockInfo *imap_open_tunnel(const gchar *server,
1907 const gchar *tunnelcmd,
1910 static SockInfo *imap_open_tunnel(const gchar *server,
1911 const gchar *tunnelcmd)
1916 if ((sock = sock_connect_cmd(server, tunnelcmd)) == NULL) {
1917 log_warning(_("Can't establish IMAP4 session with: %s\n"),
1922 return imap_init_sock(sock, ssl_type);
1924 return imap_init_sock(sock);
1930 static SockInfo *imap_open(const gchar *server, gushort port,
1933 static SockInfo *imap_open(const gchar *server, gushort port)
1938 if ((sock = sock_connect(server, port)) == NULL) {
1939 log_warning(_("Can't connect to IMAP4 server: %s:%d\n"),
1945 if (ssl_type == SSL_TUNNEL && !ssl_init_socket(sock)) {
1946 log_warning(_("Can't establish IMAP4 session with: %s:%d\n"),
1956 static SockInfo *imap_init_sock(SockInfo *sock, SSLType ssl_type)
1958 static SockInfo *imap_init_sock(SockInfo *sock)
1965 static GList *imap_parse_namespace_str(gchar *str)
1970 IMAPNameSpace *namespace;
1971 GList *ns_list = NULL;
1973 while (*p != '\0') {
1974 /* parse ("#foo" "/") */
1976 while (*p && *p != '(') p++;
1977 if (*p == '\0') break;
1980 while (*p && *p != '"') p++;
1981 if (*p == '\0') break;
1985 while (*p && *p != '"') p++;
1986 if (*p == '\0') break;
1990 while (*p && isspace(*p)) p++;
1991 if (*p == '\0') break;
1992 if (strncmp(p, "NIL", 3) == 0)
1994 else if (*p == '"') {
1997 while (*p && *p != '"') p++;
1998 if (*p == '\0') break;
2003 while (*p && *p != ')') p++;
2004 if (*p == '\0') break;
2007 namespace = g_new(IMAPNameSpace, 1);
2008 namespace->name = g_strdup(name);
2009 namespace->separator = separator ? separator[0] : '\0';
2010 ns_list = g_list_append(ns_list, namespace);
2016 static void imap_parse_namespace(IMAPSession *session, IMAPFolder *folder)
2021 g_return_if_fail(session != NULL);
2022 g_return_if_fail(folder != NULL);
2024 if (folder->ns_personal != NULL ||
2025 folder->ns_others != NULL ||
2026 folder->ns_shared != NULL)
2029 if (!imap_has_capability(session, "NAMESPACE")) {
2030 imap_get_namespace_by_list(session, folder);
2034 if (imap_cmd_namespace(session, &ns_str)
2036 log_warning(_("can't get namespace\n"));
2040 str_array = strsplit_parenthesis(ns_str, '(', ')', 3);
2041 if (str_array == NULL) {
2043 imap_get_namespace_by_list(session, folder);
2047 folder->ns_personal = imap_parse_namespace_str(str_array[0]);
2048 if (str_array[0] && str_array[1])
2049 folder->ns_others = imap_parse_namespace_str(str_array[1]);
2050 if (str_array[0] && str_array[1] && str_array[2])
2051 folder->ns_shared = imap_parse_namespace_str(str_array[2]);
2052 g_strfreev(str_array);
2056 static void imap_get_namespace_by_list(IMAPSession *session, IMAPFolder *folder)
2058 GSList *item_list, *cur;
2059 gchar separator = '\0';
2060 IMAPNameSpace *namespace;
2062 g_return_if_fail(session != NULL);
2063 g_return_if_fail(folder != NULL);
2065 if (folder->ns_personal != NULL ||
2066 folder->ns_others != NULL ||
2067 folder->ns_shared != NULL)
2070 imap_gen_send(session, "LIST \"\" \"\"");
2071 item_list = imap_parse_list(folder, session, "", &separator);
2072 for (cur = item_list; cur != NULL; cur = cur->next)
2073 folder_item_destroy(FOLDER_ITEM(cur->data));
2074 g_slist_free(item_list);
2076 namespace = g_new(IMAPNameSpace, 1);
2077 namespace->name = g_strdup("");
2078 namespace->separator = separator;
2079 folder->ns_personal = g_list_append(NULL, namespace);
2082 static IMAPNameSpace *imap_find_namespace_from_list(GList *ns_list,
2085 IMAPNameSpace *namespace = NULL;
2086 gchar *tmp_path, *name;
2088 if (!path) path = "";
2090 for (; ns_list != NULL; ns_list = ns_list->next) {
2091 IMAPNameSpace *tmp_ns = ns_list->data;
2093 Xstrcat_a(tmp_path, path, "/", return namespace);
2094 Xstrdup_a(name, tmp_ns->name, return namespace);
2095 if (tmp_ns->separator && tmp_ns->separator != '/') {
2096 subst_char(tmp_path, tmp_ns->separator, '/');
2097 subst_char(name, tmp_ns->separator, '/');
2099 if (strncmp(tmp_path, name, strlen(name)) == 0)
2106 static IMAPNameSpace *imap_find_namespace(IMAPFolder *folder,
2109 IMAPNameSpace *namespace;
2111 g_return_val_if_fail(folder != NULL, NULL);
2113 namespace = imap_find_namespace_from_list(folder->ns_personal, path);
2114 if (namespace) return namespace;
2115 namespace = imap_find_namespace_from_list(folder->ns_others, path);
2116 if (namespace) return namespace;
2117 namespace = imap_find_namespace_from_list(folder->ns_shared, path);
2118 if (namespace) return namespace;
2123 static gchar imap_get_path_separator(IMAPFolder *folder, const gchar *path)
2125 IMAPNameSpace *namespace;
2126 gchar separator = '/';
2128 namespace = imap_find_namespace(folder, path);
2129 if (namespace && namespace->separator)
2130 separator = namespace->separator;
2135 static gchar *imap_get_real_path(IMAPFolder *folder, const gchar *path)
2140 g_return_val_if_fail(folder != NULL, NULL);
2141 g_return_val_if_fail(path != NULL, NULL);
2143 real_path = imap_locale_to_modified_utf7(path);
2144 separator = imap_get_path_separator(folder, path);
2145 imap_path_separator_subst(real_path, separator);
2150 static gchar *imap_parse_atom(SockInfo *sock, gchar *src,
2151 gchar *dest, gint dest_len, GString *str)
2153 gchar *cur_pos = src;
2156 g_return_val_if_fail(str != NULL, cur_pos);
2158 /* read the next line if the current response buffer is empty */
2159 while (isspace(*cur_pos)) cur_pos++;
2160 while (*cur_pos == '\0') {
2161 if ((nextline = sock_getline(sock)) == NULL)
2163 g_string_assign(str, nextline);
2165 strretchomp(nextline);
2166 /* log_print("IMAP4< %s\n", nextline); */
2167 debug_print("IMAP4< %s\n", nextline);
2170 while (isspace(*cur_pos)) cur_pos++;
2173 if (!strncmp(cur_pos, "NIL", 3)) {
2176 } else if (*cur_pos == '\"') {
2179 p = get_quoted(cur_pos, '\"', dest, dest_len);
2180 cur_pos = p ? p : cur_pos + 2;
2181 } else if (*cur_pos == '{') {
2186 cur_pos = strchr_cpy(cur_pos + 1, '}', buf, sizeof(buf));
2188 g_return_val_if_fail(len >= 0, cur_pos);
2190 g_string_truncate(str, 0);
2194 if ((nextline = sock_getline(sock)) == NULL)
2196 line_len += strlen(nextline);
2197 g_string_append(str, nextline);
2199 strretchomp(nextline);
2200 /* log_print("IMAP4< %s\n", nextline); */
2201 debug_print("IMAP4< %s\n", nextline);
2203 } while (line_len < len);
2205 memcpy(dest, cur_pos, MIN(len, dest_len - 1));
2206 dest[MIN(len, dest_len - 1)] = '\0';
2213 static gchar *imap_get_header(SockInfo *sock, gchar *cur_pos, gchar **headers,
2223 g_return_val_if_fail(str != NULL, cur_pos);
2225 while (isspace(*cur_pos)) cur_pos++;
2227 g_return_val_if_fail(*cur_pos == '{', cur_pos);
2229 cur_pos = strchr_cpy(cur_pos + 1, '}', buf, sizeof(buf));
2231 g_return_val_if_fail(len >= 0, cur_pos);
2233 g_string_truncate(str, 0);
2237 if ((nextline = sock_getline(sock)) == NULL)
2239 block_len += strlen(nextline);
2240 g_string_append(str, nextline);
2242 strretchomp(nextline);
2243 /* debug_print("IMAP4< %s\n", nextline); */
2245 } while (block_len < len);
2247 debug_print("IMAP4< [contents of BODY.PEEK[HEADER.FIELDS (...)]]\n");
2249 *headers = g_strndup(cur_pos, len);
2252 while (isspace(*cur_pos)) cur_pos++;
2253 while (*cur_pos == '\0') {
2254 if ((nextline = sock_getline(sock)) == NULL)
2256 g_string_assign(str, nextline);
2258 strretchomp(nextline);
2259 debug_print("IMAP4< %s\n", nextline);
2262 while (isspace(*cur_pos)) cur_pos++;
2268 static MsgFlags imap_parse_flags(const gchar *flag_str)
2270 const gchar *p = flag_str;
2271 MsgFlags flags = {0, 0};
2273 flags.perm_flags = MSG_UNREAD;
2275 while ((p = strchr(p, '\\')) != NULL) {
2278 if (g_strncasecmp(p, "Recent", 6) == 0 && MSG_IS_UNREAD(flags)) {
2279 MSG_SET_PERM_FLAGS(flags, MSG_NEW);
2280 } else if (g_strncasecmp(p, "Seen", 4) == 0) {
2281 MSG_UNSET_PERM_FLAGS(flags, MSG_NEW|MSG_UNREAD);
2282 } else if (g_strncasecmp(p, "Deleted", 7) == 0) {
2283 MSG_SET_PERM_FLAGS(flags, MSG_DELETED);
2284 } else if (g_strncasecmp(p, "Flagged", 7) == 0) {
2285 MSG_SET_PERM_FLAGS(flags, MSG_MARKED);
2286 } else if (g_strncasecmp(p, "Answered", 8) == 0) {
2287 MSG_SET_PERM_FLAGS(flags, MSG_REPLIED);
2294 static MsgInfo *imap_parse_envelope(SockInfo *sock, FolderItem *item,
2297 gchar buf[IMAPBUFSIZE];
2298 MsgInfo *msginfo = NULL;
2303 MsgFlags flags = {0, 0}, imap_flags = {0, 0};
2305 g_return_val_if_fail(line_str != NULL, NULL);
2306 g_return_val_if_fail(line_str->str[0] == '*' &&
2307 line_str->str[1] == ' ', NULL);
2309 MSG_SET_TMP_FLAGS(flags, MSG_IMAP);
2310 if (item->stype == F_QUEUE) {
2311 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
2312 } else if (item->stype == F_DRAFT) {
2313 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
2316 cur_pos = line_str->str + 2;
2318 #define PARSE_ONE_ELEMENT(ch) \
2320 cur_pos = strchr_cpy(cur_pos, ch, buf, sizeof(buf)); \
2321 if (cur_pos == NULL) { \
2322 g_warning("cur_pos == NULL\n"); \
2323 procmsg_msginfo_free(msginfo); \
2328 PARSE_ONE_ELEMENT(' ');
2331 PARSE_ONE_ELEMENT(' ');
2332 g_return_val_if_fail(!strcmp(buf, "FETCH"), NULL);
2334 g_return_val_if_fail(*cur_pos == '(', NULL);
2337 while (*cur_pos != '\0' && *cur_pos != ')') {
2338 while (*cur_pos == ' ') cur_pos++;
2340 if (!strncmp(cur_pos, "UID ", 4)) {
2342 uid = strtoul(cur_pos, &cur_pos, 10);
2343 } else if (!strncmp(cur_pos, "FLAGS ", 6)) {
2345 if (*cur_pos != '(') {
2346 g_warning("*cur_pos != '('\n");
2347 procmsg_msginfo_free(msginfo);
2351 PARSE_ONE_ELEMENT(')');
2352 imap_flags = imap_parse_flags(buf);
2353 } else if (!strncmp(cur_pos, "RFC822.SIZE ", 12)) {
2355 size = strtol(cur_pos, &cur_pos, 10);
2356 } else if (!strncmp(cur_pos, "BODY[HEADER.FIELDS ", 19)) {
2360 if (*cur_pos != '(') {
2361 g_warning("*cur_pos != '('\n");
2362 procmsg_msginfo_free(msginfo);
2366 PARSE_ONE_ELEMENT(')');
2367 if (*cur_pos != ']') {
2368 g_warning("*cur_pos != ']'\n");
2369 procmsg_msginfo_free(msginfo);
2374 cur_pos = imap_get_header(sock, cur_pos, &headers,
2376 msginfo = procheader_parse_str(headers, flags, FALSE, FALSE);
2379 g_warning("invalid FETCH response: %s\n", cur_pos);
2385 msginfo->msgnum = uid;
2386 msginfo->size = size;
2387 msginfo->flags.tmp_flags |= imap_flags.tmp_flags;
2388 msginfo->flags.perm_flags = imap_flags.perm_flags;
2394 static gchar *imap_get_flag_str(IMAPFlags flags)
2399 str = g_string_new(NULL);
2401 if (IMAP_IS_SEEN(flags)) g_string_append(str, "\\Seen ");
2402 if (IMAP_IS_ANSWERED(flags)) g_string_append(str, "\\Answered ");
2403 if (IMAP_IS_FLAGGED(flags)) g_string_append(str, "\\Flagged ");
2404 if (IMAP_IS_DELETED(flags)) g_string_append(str, "\\Deleted ");
2405 if (IMAP_IS_DRAFT(flags)) g_string_append(str, "\\Draft");
2407 if (str->len > 0 && str->str[str->len - 1] == ' ')
2408 g_string_truncate(str, str->len - 1);
2411 g_string_free(str, FALSE);
2416 static gint imap_set_message_flags(IMAPSession *session,
2417 MsgNumberList *numlist,
2424 GSList *seq_list, *cur;
2427 flag_str = imap_get_flag_str(flags);
2428 cmd = g_strconcat(is_set ? "+FLAGS.SILENT (" : "-FLAGS.SILENT (",
2429 flag_str, ")", NULL);
2432 seq_list = imap_get_seq_set_from_numlist(numlist);
2433 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
2434 imapset = cur->data;
2436 ok = imap_cmd_store(session, imapset, cmd);
2438 imap_seq_set_free(seq_list);
2444 static gint imap_select(IMAPSession *session, IMAPFolder *folder,
2446 gint *exists, gint *recent, gint *unseen,
2447 guint32 *uid_validity)
2451 gint exists_, recent_, unseen_, uid_validity_;
2453 if (!exists || !recent || !unseen || !uid_validity) {
2454 if (session->mbox && strcmp(session->mbox, path) == 0)
2455 return IMAP_SUCCESS;
2459 uid_validity = &uid_validity_;
2462 g_free(session->mbox);
2463 session->mbox = NULL;
2465 real_path = imap_get_real_path(folder, path);
2466 ok = imap_cmd_select(session, real_path,
2467 exists, recent, unseen, uid_validity);
2468 if (ok != IMAP_SUCCESS)
2469 log_warning(_("can't select folder: %s\n"), real_path);
2471 session->mbox = g_strdup(path);
2472 session->folder_content_changed = FALSE;
2479 #define THROW(err) { ok = err; goto catch; }
2481 static gint imap_status(IMAPSession *session, IMAPFolder *folder,
2483 gint *messages, gint *recent,
2484 guint32 *uid_next, guint32 *uid_validity,
2490 GPtrArray *argbuf = NULL;
2493 if (messages && recent && uid_next && uid_validity && unseen) {
2494 *messages = *recent = *uid_next = *uid_validity = *unseen = 0;
2495 argbuf = g_ptr_array_new();
2498 real_path = imap_get_real_path(folder, path);
2499 QUOTE_IF_REQUIRED(real_path_, real_path);
2500 imap_gen_send(session, "STATUS %s "
2501 "(MESSAGES RECENT UIDNEXT UIDVALIDITY UNSEEN)",
2504 ok = imap_cmd_ok(session, argbuf);
2505 if (ok != IMAP_SUCCESS || !argbuf) THROW(ok);
2507 str = search_array_str(argbuf, "STATUS");
2508 if (!str) THROW(IMAP_ERROR);
2510 str = strchr(str, '(');
2511 if (!str) THROW(IMAP_ERROR);
2513 while (*str != '\0' && *str != ')') {
2514 while (*str == ' ') str++;
2516 if (!strncmp(str, "MESSAGES ", 9)) {
2518 *messages = strtol(str, &str, 10);
2519 } else if (!strncmp(str, "RECENT ", 7)) {
2521 *recent = strtol(str, &str, 10);
2522 } else if (!strncmp(str, "UIDNEXT ", 8)) {
2524 *uid_next = strtoul(str, &str, 10);
2525 } else if (!strncmp(str, "UIDVALIDITY ", 12)) {
2527 *uid_validity = strtoul(str, &str, 10);
2528 } else if (!strncmp(str, "UNSEEN ", 7)) {
2530 *unseen = strtol(str, &str, 10);
2532 g_warning("invalid STATUS response: %s\n", str);
2540 ptr_array_free_strings(argbuf);
2541 g_ptr_array_free(argbuf, TRUE);
2549 static gboolean imap_has_capability(IMAPSession *session, const gchar *cap)
2553 for (p = session->capability; *p != NULL; ++p) {
2554 if (!g_strcasecmp(*p, cap))
2561 static void imap_free_capabilities(IMAPSession *session)
2563 g_strfreev(session->capability);
2564 session->capability = NULL;
2567 /* low-level IMAP4rev1 commands */
2569 static gint imap_cmd_authenticate(IMAPSession *session, const gchar *user,
2570 const gchar *pass, IMAPAuthType type)
2577 gchar hexdigest[33];
2581 auth_type = "CRAM-MD5";
2583 imap_gen_send(session, "AUTHENTICATE %s", auth_type);
2584 ok = imap_gen_recv(session, &buf);
2585 if (ok != IMAP_SUCCESS || buf[0] != '+' || buf[1] != ' ') {
2590 challenge = g_malloc(strlen(buf + 2) + 1);
2591 challenge_len = base64_decode(challenge, buf + 2, -1);
2592 challenge[challenge_len] = '\0';
2594 log_print("IMAP< [Decoded: %s]\n", challenge);
2596 md5_hex_hmac(hexdigest, challenge, challenge_len, pass, strlen(pass));
2599 response = g_strdup_printf("%s %s", user, hexdigest);
2600 log_print("IMAP> [Encoded: %s]\n", response);
2601 response64 = g_malloc((strlen(response) + 3) * 2 + 1);
2602 base64_encode(response64, response, strlen(response));
2605 log_print("IMAP> %s\n", response64);
2606 sock_puts(SESSION(session)->sock, response64);
2607 ok = imap_cmd_ok(session, NULL);
2608 if (ok != IMAP_SUCCESS)
2609 log_warning(_("IMAP4 authentication failed.\n"));
2614 static gint imap_cmd_login(IMAPSession *session,
2615 const gchar *user, const gchar *pass)
2617 gchar *user_, *pass_;
2620 QUOTE_IF_REQUIRED(user_, user);
2621 QUOTE_IF_REQUIRED(pass_, pass);
2622 imap_gen_send(session, "LOGIN %s %s", user_, pass_);
2624 ok = imap_cmd_ok(session, NULL);
2625 if (ok != IMAP_SUCCESS)
2626 log_warning(_("IMAP4 login failed.\n"));
2631 static gint imap_cmd_logout(IMAPSession *session)
2633 imap_gen_send(session, "LOGOUT");
2634 return imap_cmd_ok(session, NULL);
2637 static gint imap_cmd_noop(IMAPSession *session)
2639 imap_gen_send(session, "NOOP");
2640 return imap_cmd_ok(session, NULL);
2643 static gint imap_cmd_starttls(IMAPSession *session)
2645 imap_gen_send(session, "STARTTLS");
2646 return imap_cmd_ok(session, NULL);
2649 #define THROW(err) { ok = err; goto catch; }
2651 static gint imap_cmd_namespace(IMAPSession *session, gchar **ns_str)
2657 argbuf = g_ptr_array_new();
2659 imap_gen_send(session, "NAMESPACE");
2660 if ((ok = imap_cmd_ok(session, argbuf)) != IMAP_SUCCESS) THROW(ok);
2662 str = search_array_str(argbuf, "NAMESPACE");
2663 if (!str) THROW(IMAP_ERROR);
2665 *ns_str = g_strdup(str);
2668 ptr_array_free_strings(argbuf);
2669 g_ptr_array_free(argbuf, TRUE);
2676 static gint imap_cmd_list(IMAPSession *session, const gchar *ref,
2677 const gchar *mailbox, GPtrArray *argbuf)
2679 gchar *ref_, *mailbox_;
2681 if (!ref) ref = "\"\"";
2682 if (!mailbox) mailbox = "\"\"";
2684 QUOTE_IF_REQUIRED(ref_, ref);
2685 QUOTE_IF_REQUIRED(mailbox_, mailbox);
2686 imap_gen_send(session, "LIST %s %s", ref_, mailbox_);
2688 return imap_cmd_ok(session, argbuf);
2691 #define THROW goto catch
2693 static gint imap_cmd_do_select(IMAPSession *session, const gchar *folder,
2695 gint *exists, gint *recent, gint *unseen,
2696 guint32 *uid_validity)
2704 *exists = *recent = *unseen = *uid_validity = 0;
2705 argbuf = g_ptr_array_new();
2708 select_cmd = "EXAMINE";
2710 select_cmd = "SELECT";
2712 QUOTE_IF_REQUIRED(folder_, folder);
2713 imap_gen_send(session, "%s %s", select_cmd, folder_);
2715 if ((ok = imap_cmd_ok(session, argbuf)) != IMAP_SUCCESS) THROW;
2717 resp_str = search_array_contain_str(argbuf, "EXISTS");
2719 if (sscanf(resp_str,"%d EXISTS", exists) != 1) {
2720 g_warning("imap_cmd_select(): invalid EXISTS line.\n");
2725 resp_str = search_array_contain_str(argbuf, "RECENT");
2727 if (sscanf(resp_str, "%d RECENT", recent) != 1) {
2728 g_warning("imap_cmd_select(): invalid RECENT line.\n");
2733 resp_str = search_array_contain_str(argbuf, "UIDVALIDITY");
2735 if (sscanf(resp_str, "OK [UIDVALIDITY %u] ", uid_validity)
2737 g_warning("imap_cmd_select(): invalid UIDVALIDITY line.\n");
2742 resp_str = search_array_contain_str(argbuf, "UNSEEN");
2744 if (sscanf(resp_str, "OK [UNSEEN %d] ", unseen) != 1) {
2745 g_warning("imap_cmd_select(): invalid UNSEEN line.\n");
2751 ptr_array_free_strings(argbuf);
2752 g_ptr_array_free(argbuf, TRUE);
2757 static gint imap_cmd_select(IMAPSession *session, const gchar *folder,
2758 gint *exists, gint *recent, gint *unseen,
2759 guint32 *uid_validity)
2761 return imap_cmd_do_select(session, folder, FALSE,
2762 exists, recent, unseen, uid_validity);
2765 static gint imap_cmd_examine(IMAPSession *session, const gchar *folder,
2766 gint *exists, gint *recent, gint *unseen,
2767 guint32 *uid_validity)
2769 return imap_cmd_do_select(session, folder, TRUE,
2770 exists, recent, unseen, uid_validity);
2775 static gint imap_cmd_create(IMAPSession *session, const gchar *folder)
2779 QUOTE_IF_REQUIRED(folder_, folder);
2780 imap_gen_send(session, "CREATE %s", folder_);
2782 return imap_cmd_ok(session, NULL);
2785 static gint imap_cmd_rename(IMAPSession *session, const gchar *old_folder,
2786 const gchar *new_folder)
2788 gchar *old_folder_, *new_folder_;
2790 QUOTE_IF_REQUIRED(old_folder_, old_folder);
2791 QUOTE_IF_REQUIRED(new_folder_, new_folder);
2792 imap_gen_send(session, "RENAME %s %s", old_folder_, new_folder_);
2794 return imap_cmd_ok(session, NULL);
2797 static gint imap_cmd_delete(IMAPSession *session, const gchar *folder)
2801 QUOTE_IF_REQUIRED(folder_, folder);
2802 imap_gen_send(session, "DELETE %s", folder_);
2804 return imap_cmd_ok(session, NULL);
2807 static gint imap_cmd_search(IMAPSession *session, const gchar *criteria,
2814 g_return_val_if_fail(criteria != NULL, IMAP_ERROR);
2815 g_return_val_if_fail(list != NULL, IMAP_ERROR);
2819 argbuf = g_ptr_array_new();
2820 imap_gen_send(session, "UID SEARCH %s", criteria);
2822 ok = imap_cmd_ok(session, argbuf);
2823 if (ok != IMAP_SUCCESS) {
2824 ptr_array_free_strings(argbuf);
2825 g_ptr_array_free(argbuf, TRUE);
2829 if ((uidlist = search_array_str(argbuf, "SEARCH ")) != NULL) {
2830 gchar **strlist, **p;
2832 strlist = g_strsplit(uidlist + 7, " ", 0);
2833 for (p = strlist; *p != NULL; ++p) {
2836 if (sscanf(*p, "%d", &msgnum) == 1)
2837 *list = g_slist_append(*list, GINT_TO_POINTER(msgnum));
2839 g_strfreev(strlist);
2841 ptr_array_free_strings(argbuf);
2842 g_ptr_array_free(argbuf, TRUE);
2844 return IMAP_SUCCESS;
2847 static gint imap_cmd_fetch(IMAPSession *session, guint32 uid,
2848 const gchar *filename)
2856 g_return_val_if_fail(filename != NULL, IMAP_ERROR);
2858 imap_gen_send(session, "UID FETCH %d BODY.PEEK[]", uid);
2860 while ((ok = imap_gen_recv(session, &buf)) == IMAP_SUCCESS) {
2861 if (buf[0] != '*' || buf[1] != ' ') {
2865 if (strstr(buf, "FETCH") != NULL) break;
2868 if (ok != IMAP_SUCCESS) {
2873 #define RETURN_ERROR_IF_FAIL(cond) \
2876 return IMAP_ERROR; \
2879 cur_pos = strchr(buf, '{');
2880 RETURN_ERROR_IF_FAIL(cur_pos != NULL);
2881 cur_pos = strchr_cpy(cur_pos + 1, '}', size_str, sizeof(size_str));
2882 RETURN_ERROR_IF_FAIL(cur_pos != NULL);
2883 size_num = atol(size_str);
2884 RETURN_ERROR_IF_FAIL(size_num >= 0);
2886 RETURN_ERROR_IF_FAIL(*cur_pos == '\0');
2888 #undef RETURN_ERROR_IF_FAIL
2892 if (recv_bytes_write_to_file(SESSION(session)->sock,
2893 size_num, filename) != 0)
2896 if (imap_gen_recv(session, &buf) != IMAP_SUCCESS) {
2901 if (buf[0] == '\0' || buf[strlen(buf) - 1] != ')') {
2907 ok = imap_cmd_ok(session, NULL);
2912 static gint imap_cmd_append(IMAPSession *session, const gchar *destfolder,
2913 const gchar *file, IMAPFlags flags,
2922 gchar buf[BUFFSIZE];
2927 g_return_val_if_fail(file != NULL, IMAP_ERROR);
2929 size = get_file_size_as_crlf(file);
2930 if ((fp = fopen(file, "rb")) == NULL) {
2931 FILE_OP_ERROR(file, "fopen");
2934 QUOTE_IF_REQUIRED(destfolder_, destfolder);
2935 flag_str = imap_get_flag_str(flags);
2936 imap_gen_send(session, "APPEND %s (%s) {%d}",
2937 destfolder_, flag_str, size);
2940 ok = imap_gen_recv(session, &ret);
2941 if (ok != IMAP_SUCCESS || ret[0] != '+' || ret[1] != ' ') {
2942 log_warning(_("can't append %s to %s\n"), file, destfolder_);
2949 log_print("IMAP4> %s\n", _("(sending file...)"));
2951 while (fgets(buf, sizeof(buf), fp) != NULL) {
2953 if (sock_puts(SESSION(session)->sock, buf) < 0) {
2960 FILE_OP_ERROR(file, "fgets");
2965 sock_puts(SESSION(session)->sock, "");
2969 if (new_uid != NULL)
2972 if (new_uid != NULL && session->uidplus) {
2973 argbuf = g_ptr_array_new();
2975 ok = imap_cmd_ok(session, argbuf);
2976 if ((ok == IMAP_SUCCESS) && (argbuf->len > 0)) {
2977 resp_str = g_ptr_array_index(argbuf, argbuf->len - 1);
2979 sscanf(resp_str, "%*u OK [APPENDUID %*u %u]",
2981 *new_uid = new_uid_;
2985 ptr_array_free_strings(argbuf);
2986 g_ptr_array_free(argbuf, TRUE);
2988 ok = imap_cmd_ok(session, NULL);
2990 if (ok != IMAP_SUCCESS)
2991 log_warning(_("can't append message to %s\n"),
2997 static MsgNumberList *imapset_to_numlist(IMAPSet imapset)
2999 gchar **ranges, **range;
3001 MsgNumberList *uids = NULL;
3003 ranges = g_strsplit(imapset, ",", 0);
3004 for (range = ranges; *range != NULL; range++) {
3005 printf("%s\n", *range);
3006 if(sscanf(*range, "%u:%u", &low, &high) == 1)
3007 uids = g_slist_prepend(uids, GINT_TO_POINTER(low));
3010 for (i = low; i <= high; i++)
3011 uids = g_slist_prepend(uids, GINT_TO_POINTER(i));
3014 uids = g_slist_reverse(uids);
3020 static gint imap_cmd_copy(IMAPSession *session, const gchar *seq_set,
3021 const gchar *destfolder, GRelation *uid_mapping)
3026 g_return_val_if_fail(session != NULL, IMAP_ERROR);
3027 g_return_val_if_fail(seq_set != NULL, IMAP_ERROR);
3028 g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
3030 QUOTE_IF_REQUIRED(destfolder_, destfolder);
3031 imap_gen_send(session, "UID COPY %s %s", seq_set, destfolder_);
3033 if (uid_mapping != NULL && session->uidplus) {
3035 gchar *resp_str = NULL, *olduids_str, *newuids_str;
3036 MsgNumberList *olduids, *old_cur, *newuids, *new_cur;
3038 reply = g_ptr_array_new();
3039 ok = imap_cmd_ok(session, reply);
3040 if ((ok == IMAP_SUCCESS) && (reply->len > 0)) {
3041 resp_str = g_ptr_array_index(reply, reply->len - 1);
3043 olduids_str = g_new0(gchar, strlen(resp_str));
3044 newuids_str = g_new0(gchar, strlen(resp_str));
3045 if (sscanf(resp_str, "%*s OK [COPYUID %*u %[0-9,:] %[0-9,:]]",
3046 olduids_str, newuids_str) == 2) {
3047 olduids = imapset_to_numlist(olduids_str);
3048 newuids = imapset_to_numlist(newuids_str);
3052 while(old_cur != NULL && new_cur != NULL) {
3053 g_relation_insert(uid_mapping,
3054 GPOINTER_TO_INT(old_cur->data),
3055 GPOINTER_TO_INT(new_cur->data));
3056 old_cur = g_slist_next(old_cur);
3057 new_cur = g_slist_next(new_cur);
3060 g_slist_free(olduids);
3061 g_slist_free(newuids);
3063 g_free(olduids_str);
3064 g_free(newuids_str);
3067 ptr_array_free_strings(reply);
3068 g_ptr_array_free(reply, TRUE);
3070 ok = imap_cmd_ok(session, NULL);
3072 if (ok != IMAP_SUCCESS)
3073 log_warning(_("can't copy %s to %s\n"), seq_set, destfolder_);
3078 gint imap_cmd_envelope(IMAPSession *session, IMAPSet set)
3080 static GString *header_fields = NULL;
3082 if (header_fields == NULL) {
3083 const HeaderEntry *headers, *elem;
3085 headers = procheader_get_headernames(FALSE);
3086 header_fields = g_string_new("");
3088 for (elem = headers; elem->name != NULL; ++elem) {
3089 gint namelen = strlen(elem->name);
3091 /* Header fields ending with space are not rfc822 headers */
3092 if (elem->name[namelen - 1] == ' ')
3095 /* strip : at the of header field */
3096 if(elem->name[namelen - 1] == ':')
3102 g_string_sprintfa(header_fields, "%s%.*s",
3103 header_fields->str[0] != '\0' ? " " : "",
3104 namelen, elem->name);
3109 (session, "UID FETCH %s (UID FLAGS RFC822.SIZE BODY.PEEK[HEADER.FIELDS (%s)])",
3110 set, header_fields->str);
3112 return IMAP_SUCCESS;
3115 static gint imap_cmd_store(IMAPSession *session, IMAPSet seq_set,
3120 imap_gen_send(session, "UID STORE %s %s", seq_set, sub_cmd);
3122 if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS) {
3123 log_warning(_("error while imap command: STORE %s %s\n"),
3128 return IMAP_SUCCESS;
3131 static gint imap_cmd_expunge(IMAPSession *session, IMAPSet seq_set)
3135 if (seq_set && session->uidplus)
3136 imap_gen_send(session, "UID EXPUNGE %s", seq_set);
3138 imap_gen_send(session, "EXPUNGE");
3139 if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS) {
3140 log_warning(_("error while imap command: EXPUNGE\n"));
3144 return IMAP_SUCCESS;
3147 static gint imap_cmd_close(IMAPSession *session)
3151 imap_gen_send(session, "CLOSE");
3152 if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS)
3153 log_warning(_("error while imap command: CLOSE\n"));
3158 static gint imap_cmd_ok(IMAPSession *session, GPtrArray *argbuf)
3160 gint ok = IMAP_SUCCESS;
3165 while ((ok = imap_gen_recv(session, &buf))
3167 // make sure data is long enough for any substring of buf
3168 data = alloca(strlen(buf) + 1);
3170 // untagged line read
3171 if (buf[0] == '*' && buf[1] == ' ') {
3174 g_ptr_array_add(argbuf, g_strdup(buf + 2));
3176 if (sscanf(buf + 2, "%d %s", &num, data) >= 2) {
3177 if (!strcmp(data, "EXISTS")) {
3178 session->exists = num;
3179 session->folder_content_changed = TRUE;
3182 if(!strcmp(data, "EXPUNGE")) {
3184 session->folder_content_changed = TRUE;
3187 // tagged line with correct tag and OK response found
3188 } else if ((sscanf(buf, "%d %s", &cmd_num, data) >= 2) &&
3189 (cmd_num == session->cmd_count) &&
3190 !strcmp(data, "OK")) {
3192 g_ptr_array_add(argbuf, g_strdup(buf));
3206 static void imap_gen_send(IMAPSession *session, const gchar *format, ...)
3213 va_start(args, format);
3214 tmp = g_strdup_vprintf(format, args);
3217 session->cmd_count++;
3219 buf = g_strdup_printf("%d %s\r\n", session->cmd_count, tmp);
3220 if (!strncasecmp(tmp, "LOGIN ", 6) && (p = strchr(tmp + 6, ' '))) {
3222 log_print("IMAP4> %d %s ********\n", session->cmd_count, tmp);
3224 log_print("IMAP4> %d %s\n", session->cmd_count, tmp);
3226 sock_write_all(SESSION(session)->sock, buf, strlen(buf));
3231 static gint imap_gen_recv(IMAPSession *session, gchar **ret)
3233 if ((*ret = sock_getline(SESSION(session)->sock)) == NULL)
3238 log_print("IMAP4< %s\n", *ret);
3240 return IMAP_SUCCESS;
3244 /* misc utility functions */
3246 static gchar *strchr_cpy(const gchar *src, gchar ch, gchar *dest, gint len)
3251 tmp = strchr(src, ch);
3255 memcpy(dest, src, MIN(tmp - src, len - 1));
3256 dest[MIN(tmp - src, len - 1)] = '\0';
3261 static gchar *get_quoted(const gchar *src, gchar ch, gchar *dest, gint len)
3263 const gchar *p = src;
3266 g_return_val_if_fail(*p == ch, NULL);
3271 while (*p != '\0' && *p != ch) {
3273 if (*p == '\\' && *(p + 1) != '\0')
3282 return (gchar *)(*p == ch ? p + 1 : p);
3285 static gchar *search_array_contain_str(GPtrArray *array, const gchar *str)
3289 for (i = 0; i < array->len; i++) {
3292 tmp = g_ptr_array_index(array, i);
3293 if (strstr(tmp, str) != NULL)
3300 static gchar *search_array_str(GPtrArray *array, const gchar *str)
3307 for (i = 0; i < array->len; i++) {
3310 tmp = g_ptr_array_index(array, i);
3311 if (!strncmp(tmp, str, len))
3318 static void imap_path_separator_subst(gchar *str, gchar separator)
3321 gboolean in_escape = FALSE;
3323 if (!separator || separator == '/') return;
3325 for (p = str; *p != '\0'; p++) {
3326 if (*p == '/' && !in_escape)
3328 else if (*p == '&' && *(p + 1) != '-' && !in_escape)
3330 else if (*p == '-' && in_escape)
3335 static gchar *imap_modified_utf7_to_locale(const gchar *mutf7_str)
3338 const gchar *from_p;
3341 to = g_malloc(strlen(mutf7_str) + 1);
3344 for (from_p = mutf7_str; *from_p != '\0'; from_p++) {
3345 if (*from_p == '&' && *(from_p + 1) == '-') {
3355 static iconv_t cd = (iconv_t)-1;
3356 static gboolean iconv_ok = TRUE;
3359 size_t norm_utf7_len;
3361 gchar *to_str, *to_p;
3363 gboolean in_escape = FALSE;
3365 if (!iconv_ok) return g_strdup(mutf7_str);
3367 if (cd == (iconv_t)-1) {
3368 cd = iconv_open(conv_get_current_charset_str(), "UTF-7");
3369 if (cd == (iconv_t)-1) {
3370 g_warning("iconv cannot convert UTF-7 to %s\n",
3371 conv_get_current_charset_str());
3373 return g_strdup(mutf7_str);
3377 norm_utf7 = g_string_new(NULL);
3379 for (p = mutf7_str; *p != '\0'; p++) {
3380 /* replace: '&' -> '+',
3382 escaped ',' -> '/' */
3383 if (!in_escape && *p == '&') {
3384 if (*(p + 1) != '-') {
3385 g_string_append_c(norm_utf7, '+');
3388 g_string_append_c(norm_utf7, '&');
3391 } else if (in_escape && *p == ',') {
3392 g_string_append_c(norm_utf7, '/');
3393 } else if (in_escape && *p == '-') {
3394 g_string_append_c(norm_utf7, '-');
3397 g_string_append_c(norm_utf7, *p);
3401 norm_utf7_p = norm_utf7->str;
3402 norm_utf7_len = norm_utf7->len;
3403 to_len = strlen(mutf7_str) * 5;
3404 to_p = to_str = g_malloc(to_len + 1);
3406 if (iconv(cd, (ICONV_CONST gchar **)&norm_utf7_p, &norm_utf7_len,
3407 &to_p, &to_len) == -1) {
3408 g_warning(_("iconv cannot convert UTF-7 to %s\n"),
3409 conv_get_current_charset_str());
3410 g_string_free(norm_utf7, TRUE);
3412 return g_strdup(mutf7_str);
3415 /* second iconv() call for flushing */
3416 iconv(cd, NULL, NULL, &to_p, &to_len);
3417 g_string_free(norm_utf7, TRUE);
3421 #endif /* !HAVE_ICONV */
3424 static gchar *imap_locale_to_modified_utf7(const gchar *from)
3427 const gchar *from_p;
3430 to = g_malloc(strlen(from) * 2 + 1);
3433 for (from_p = from; *from_p != '\0'; from_p++) {
3434 if (*from_p == '&') {
3444 static iconv_t cd = (iconv_t)-1;
3445 static gboolean iconv_ok = TRUE;
3446 gchar *norm_utf7, *norm_utf7_p;
3447 size_t from_len, norm_utf7_len;
3449 gchar *from_tmp, *to, *p;
3450 gboolean in_escape = FALSE;
3452 if (!iconv_ok) return g_strdup(from);
3454 if (cd == (iconv_t)-1) {
3455 cd = iconv_open("UTF-7", conv_get_current_charset_str());
3456 if (cd == (iconv_t)-1) {
3457 g_warning("iconv cannot convert %s to UTF-7\n",
3458 conv_get_current_charset_str());
3460 return g_strdup(from);
3464 Xstrdup_a(from_tmp, from, return g_strdup(from));
3465 from_len = strlen(from);
3466 norm_utf7_len = from_len * 5;
3467 Xalloca(norm_utf7, norm_utf7_len + 1, return g_strdup(from));
3468 norm_utf7_p = norm_utf7;
3470 #define IS_PRINT(ch) (isprint(ch) && isascii(ch))
3472 while (from_len > 0) {
3473 if (*from_tmp == '+') {
3474 *norm_utf7_p++ = '+';
3475 *norm_utf7_p++ = '-';
3479 } else if (IS_PRINT(*from_tmp)) {
3480 /* printable ascii char */
3481 *norm_utf7_p = *from_tmp;
3487 size_t mb_len = 0, conv_len = 0;
3489 /* unprintable char: convert to UTF-7 */
3491 while (!IS_PRINT(*p) && conv_len < from_len) {
3492 mb_len = mblen(p, MB_LEN_MAX);
3494 g_warning("wrong multibyte sequence\n");
3495 return g_strdup(from);
3501 from_len -= conv_len;
3502 if (iconv(cd, (ICONV_CONST gchar **)&from_tmp,
3504 &norm_utf7_p, &norm_utf7_len) == -1) {
3505 g_warning("iconv cannot convert %s to UTF-7\n",
3506 conv_get_current_charset_str());
3507 return g_strdup(from);
3510 /* second iconv() call for flushing */
3511 iconv(cd, NULL, NULL, &norm_utf7_p, &norm_utf7_len);
3517 *norm_utf7_p = '\0';
3518 to_str = g_string_new(NULL);
3519 for (p = norm_utf7; p < norm_utf7_p; p++) {
3520 /* replace: '&' -> "&-",
3523 BASE64 '/' -> ',' */
3524 if (!in_escape && *p == '&') {
3525 g_string_append(to_str, "&-");
3526 } else if (!in_escape && *p == '+') {
3527 if (*(p + 1) == '-') {
3528 g_string_append_c(to_str, '+');
3531 g_string_append_c(to_str, '&');
3534 } else if (in_escape && *p == '/') {
3535 g_string_append_c(to_str, ',');
3536 } else if (in_escape && *p == '-') {
3537 g_string_append_c(to_str, '-');
3540 g_string_append_c(to_str, *p);
3546 g_string_append_c(to_str, '-');
3550 g_string_free(to_str, FALSE);
3553 #endif /* !HAVE_ICONV */
3556 static GSList *imap_get_seq_set_from_numlist(MsgNumberList *numlist)
3559 GSList *sorted_list, *cur;
3560 guint first, last, next;
3562 GSList *ret_list = NULL;
3564 if (numlist == NULL)
3567 str = g_string_sized_new(256);
3569 sorted_list = g_slist_copy(numlist);
3570 sorted_list = g_slist_sort(sorted_list, g_int_compare);
3572 first = GPOINTER_TO_INT(sorted_list->data);
3574 for (cur = sorted_list; cur != NULL; cur = g_slist_next(cur)) {
3575 last = GPOINTER_TO_INT(cur->data);
3577 next = GPOINTER_TO_INT(cur->next->data);
3581 if (last + 1 != next || next == 0) {
3583 g_string_append_c(str, ',');
3585 g_string_sprintfa(str, "%u", first);
3587 g_string_sprintfa(str, "%u:%u", first, last);
3591 if (str->len > IMAP_CMD_LIMIT) {
3592 ret_str = g_strdup(str->str);
3593 ret_list = g_slist_append(ret_list, ret_str);
3594 g_string_truncate(str, 0);
3600 ret_str = g_strdup(str->str);
3601 ret_list = g_slist_append(ret_list, ret_str);
3604 g_slist_free(sorted_list);
3605 g_string_free(str, TRUE);
3610 static GSList *imap_get_seq_set_from_msglist(MsgInfoList *msglist)
3612 MsgNumberList *numlist = NULL;
3616 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
3617 MsgInfo *msginfo = (MsgInfo *) cur->data;
3619 numlist = g_slist_append(numlist, GINT_TO_POINTER(msginfo->msgnum));
3621 seq_list = imap_get_seq_set_from_numlist(numlist);
3622 g_slist_free(numlist);
3627 static void imap_seq_set_free(GSList *seq_list)
3629 slist_free_strings(seq_list);
3630 g_slist_free(seq_list);
3634 static gboolean imap_rename_folder_func(GNode *node, gpointer data)
3636 FolderItem *item = node->data;
3637 gchar **paths = data;
3638 const gchar *oldpath = paths[0];
3639 const gchar *newpath = paths[1];
3641 gchar *new_itempath;
3644 oldpathlen = strlen(oldpath);
3645 if (strncmp(oldpath, item->path, oldpathlen) != 0) {
3646 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
3650 base = item->path + oldpathlen;
3651 while (*base == G_DIR_SEPARATOR) base++;
3653 new_itempath = g_strdup(newpath);
3655 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
3658 item->path = new_itempath;
3663 static gint get_list_of_uids(Folder *folder, IMAPFolderItem *item, GSList **msgnum_list)
3665 gint ok, nummsgs = 0, lastuid_old;
3666 IMAPSession *session;
3667 GSList *uidlist, *elem;
3670 session = imap_session_get(folder);
3671 g_return_val_if_fail(session != NULL, -1);
3673 ok = imap_select(session, IMAP_FOLDER(folder), item->item.path,
3674 NULL, NULL, NULL, NULL);
3675 if (ok != IMAP_SUCCESS)
3678 cmd_buf = g_strdup_printf("UID %d:*", item->lastuid + 1);
3679 ok = imap_cmd_search(session, cmd_buf, &uidlist);
3682 if (ok == IMAP_SOCKET) {
3683 session_destroy((Session *)session);
3684 ((RemoteFolder *)folder)->session = NULL;
3688 if (ok != IMAP_SUCCESS) {
3692 argbuf = g_ptr_array_new();
3694 cmd_buf = g_strdup_printf("UID FETCH %d:* (UID)", item->lastuid + 1);
3695 imap_gen_send(session, cmd_buf);
3697 ok = imap_cmd_ok(session, argbuf);
3698 if (ok != IMAP_SUCCESS) {
3699 ptr_array_free_strings(argbuf);
3700 g_ptr_array_free(argbuf, TRUE);
3704 for(i = 0; i < argbuf->len; i++) {
3707 if((ret = sscanf(g_ptr_array_index(argbuf, i),
3708 "%*d FETCH (UID %d)", &msgnum)) == 1)
3709 uidlist = g_slist_prepend(uidlist, GINT_TO_POINTER(msgnum));
3711 ptr_array_free_strings(argbuf);
3712 g_ptr_array_free(argbuf, TRUE);
3715 lastuid_old = item->lastuid;
3716 *msgnum_list = g_slist_copy(item->uid_list);
3717 nummsgs = g_slist_length(*msgnum_list);
3718 debug_print("Got %d uids from cache\n", g_slist_length(item->uid_list));
3720 for (elem = uidlist; elem != NULL; elem = g_slist_next(elem)) {
3723 msgnum = GPOINTER_TO_INT(elem->data);
3724 if (msgnum > lastuid_old) {
3725 *msgnum_list = g_slist_prepend(*msgnum_list, GINT_TO_POINTER(msgnum));
3726 item->uid_list = g_slist_prepend(item->uid_list, GINT_TO_POINTER(msgnum));
3729 if(msgnum > item->lastuid)
3730 item->lastuid = msgnum;
3733 g_slist_free(uidlist);
3738 gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list, gboolean *old_uids_valid)
3740 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3741 IMAPSession *session;
3742 gint ok, nummsgs = 0, exists, recent, uid_val, uid_next, unseen;
3745 gboolean selected_folder;
3747 g_return_val_if_fail(folder != NULL, -1);
3748 g_return_val_if_fail(item != NULL, -1);
3749 g_return_val_if_fail(item->item.path != NULL, -1);
3750 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3751 g_return_val_if_fail(folder->account != NULL, -1);
3753 session = imap_session_get(folder);
3754 g_return_val_if_fail(session != NULL, -1);
3756 selected_folder = (session->mbox != NULL) &&
3757 (!strcmp(session->mbox, item->item.path));
3758 if (selected_folder) {
3759 ok = imap_cmd_noop(session);
3760 if (ok != IMAP_SUCCESS)
3762 exists = session->exists;
3764 *old_uids_valid = TRUE;
3766 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
3767 &exists, &recent, &uid_next, &uid_val, &unseen);
3768 if (ok != IMAP_SUCCESS)
3771 if(item->item.mtime == uid_val)
3772 *old_uids_valid = TRUE;
3774 *old_uids_valid = FALSE;
3776 debug_print("Freeing imap uid cache\n");
3778 g_slist_free(item->uid_list);
3779 item->uid_list = NULL;
3781 item->item.mtime = uid_val;
3783 imap_delete_all_cached_messages((FolderItem *)item);
3787 if (!selected_folder)
3788 item->uid_next = uid_next;
3790 /* If old uid_next matches new uid_next we can be sure no message
3791 was added to the folder */
3792 if (( selected_folder && !session->folder_content_changed) ||
3793 (!selected_folder && uid_next == item->uid_next)) {
3794 nummsgs = g_slist_length(item->uid_list);
3796 /* If number of messages is still the same we
3797 know our caches message numbers are still valid,
3798 otherwise if the number of messages has decrease
3799 we discard our cache to start a new scan to find
3800 out which numbers have been removed */
3801 if (exists == nummsgs) {
3802 *msgnum_list = g_slist_copy(item->uid_list);
3804 } else if (exists < nummsgs) {
3805 debug_print("Freeing imap uid cache");
3807 g_slist_free(item->uid_list);
3808 item->uid_list = NULL;
3813 *msgnum_list = NULL;
3817 nummsgs = get_list_of_uids(folder, item, &uidlist);
3819 if (nummsgs != exists) {
3820 /* Cache contains more messages then folder, we have cached
3821 an old UID of a message that was removed and new messages
3822 have been added too, otherwise the uid_next check would
3824 debug_print("Freeing imap uid cache");
3826 g_slist_free(item->uid_list);
3827 item->uid_list = NULL;
3829 g_slist_free(*msgnum_list);
3831 nummsgs = get_list_of_uids(folder, item, &uidlist);
3834 *msgnum_list = uidlist;
3836 dir = folder_item_get_path((FolderItem *)item);
3837 debug_print("removing old messages from %s\n", dir);
3838 remove_numbered_files_not_in_list(dir, *msgnum_list);
3844 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item)
3849 flags.perm_flags = MSG_NEW|MSG_UNREAD;
3850 flags.tmp_flags = 0;
3852 g_return_val_if_fail(item != NULL, NULL);
3853 g_return_val_if_fail(file != NULL, NULL);
3855 if (item->stype == F_QUEUE) {
3856 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
3857 } else if (item->stype == F_DRAFT) {
3858 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
3861 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3862 if (!msginfo) return NULL;
3864 msginfo->folder = item;
3869 GSList *imap_get_msginfos(Folder *folder, FolderItem *item, GSList *msgnum_list)
3871 IMAPSession *session;
3872 MsgInfoList *ret = NULL;
3875 g_return_val_if_fail(folder != NULL, NULL);
3876 g_return_val_if_fail(item != NULL, NULL);
3877 g_return_val_if_fail(msgnum_list != NULL, NULL);
3879 session = imap_session_get(folder);
3880 g_return_val_if_fail(session != NULL, NULL);
3882 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3883 NULL, NULL, NULL, NULL);
3884 if (ok != IMAP_SUCCESS)
3887 if (!(item->stype == F_QUEUE || item->stype == F_DRAFT)) {
3888 ret = g_slist_concat(ret,
3889 imap_get_uncached_messages(
3890 session, item, msgnum_list));
3892 MsgNumberList *sorted_list, *elem;
3893 gint startnum, lastnum;
3895 sorted_list = g_slist_sort(g_slist_copy(msgnum_list), g_int_compare);
3897 startnum = lastnum = GPOINTER_TO_INT(sorted_list->data);
3899 for (elem = sorted_list;; elem = g_slist_next(elem)) {
3903 num = GPOINTER_TO_INT(elem->data);
3905 if (num > lastnum + 1 || elem == NULL) {
3907 for (i = startnum; i <= lastnum; ++i) {
3910 file = imap_fetch_msg(folder, item, i);
3912 MsgInfo *msginfo = imap_parse_msg(file, item);
3913 if (msginfo != NULL) {
3914 msginfo->msgnum = i;
3915 ret = g_slist_append(ret, msginfo);
3929 g_slist_free(sorted_list);
3935 MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid)
3937 MsgInfo *msginfo = NULL;
3938 MsgInfoList *msginfolist;
3939 MsgNumberList numlist;
3941 numlist.next = NULL;
3942 numlist.data = GINT_TO_POINTER(uid);
3944 msginfolist = imap_get_msginfos(folder, item, &numlist);
3945 if (msginfolist != NULL) {
3946 msginfo = msginfolist->data;
3947 g_slist_free(msginfolist);
3953 gboolean imap_scan_required(Folder *folder, FolderItem *_item)
3955 IMAPSession *session;
3956 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3957 gint ok, exists = 0, recent = 0, unseen = 0;
3958 guint32 uid_next, uid_val = 0;
3959 gboolean selected_folder;
3961 g_return_val_if_fail(folder != NULL, FALSE);
3962 g_return_val_if_fail(item != NULL, FALSE);
3963 g_return_val_if_fail(item->item.folder != NULL, FALSE);
3964 g_return_val_if_fail(FOLDER_CLASS(item->item.folder) == &imap_class, FALSE);
3966 if (item->item.path == NULL)
3969 session = imap_session_get(folder);
3970 g_return_val_if_fail(session != NULL, FALSE);
3972 selected_folder = (session->mbox != NULL) &&
3973 (!strcmp(session->mbox, item->item.path));
3974 if (selected_folder) {
3975 ok = imap_cmd_noop(session);
3976 if (ok != IMAP_SUCCESS)
3979 if (session->folder_content_changed)
3982 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
3983 &exists, &recent, &uid_next, &uid_val, &unseen);
3984 if (ok != IMAP_SUCCESS)
3987 if ((uid_next != item->uid_next) || (exists < item->item.total_msgs))
3994 void imap_change_flags(Folder *folder, FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
3996 IMAPSession *session;
3997 IMAPFlags flags_set = 0, flags_unset = 0;
3998 gint ok = IMAP_SUCCESS;
3999 MsgNumberList numlist;
4001 g_return_if_fail(folder != NULL);
4002 g_return_if_fail(folder->klass == &imap_class);
4003 g_return_if_fail(item != NULL);
4004 g_return_if_fail(item->folder == folder);
4005 g_return_if_fail(msginfo != NULL);
4006 g_return_if_fail(msginfo->folder == item);
4008 session = imap_session_get(folder);
4009 if (!session) return;
4011 if ((ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
4012 NULL, NULL, NULL, NULL)) != IMAP_SUCCESS)
4015 if (!MSG_IS_MARKED(msginfo->flags) && (newflags & MSG_MARKED))
4016 flags_set |= IMAP_FLAG_FLAGGED;
4017 if ( MSG_IS_MARKED(msginfo->flags) && !(newflags & MSG_MARKED))
4018 flags_unset |= IMAP_FLAG_FLAGGED;
4020 if (!MSG_IS_UNREAD(msginfo->flags) && (newflags & MSG_UNREAD))
4021 flags_unset |= IMAP_FLAG_SEEN;
4022 if ( MSG_IS_UNREAD(msginfo->flags) && !(newflags & MSG_UNREAD))
4023 flags_set |= IMAP_FLAG_SEEN;
4025 if (!MSG_IS_REPLIED(msginfo->flags) && (newflags & MSG_REPLIED))
4026 flags_set |= IMAP_FLAG_ANSWERED;
4027 if ( MSG_IS_REPLIED(msginfo->flags) && !(newflags & MSG_REPLIED))
4028 flags_set |= IMAP_FLAG_ANSWERED;
4030 numlist.next = NULL;
4031 numlist.data = GINT_TO_POINTER(msginfo->msgnum);
4034 ok = imap_set_message_flags(session, &numlist, flags_set, TRUE);
4035 if (ok != IMAP_SUCCESS) return;
4039 ok = imap_set_message_flags(session, &numlist, flags_unset, FALSE);
4040 if (ok != IMAP_SUCCESS) return;
4043 msginfo->flags.perm_flags = newflags;