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) {
1213 Xstrdup_a(root_folder, folder->account->imap_dir, return -1);
1214 strtailchomp(root_folder, '/');
1215 extract_quote(root_folder, '"');
1216 real_path = imap_get_real_path
1217 (IMAP_FOLDER(folder), root_folder);
1218 debug_print("IMAP root directory: %s\n", real_path);
1219 if (imap_status(session, IMAP_FOLDER(folder), root_folder,
1220 NULL, NULL, NULL, NULL, NULL)
1222 if (imap_cmd_create(session, real_path)
1224 log_warning(_("can't create root folder %s\n"),
1234 item = FOLDER_ITEM(folder->node->data);
1235 if (!item || ((item->path || root_folder) &&
1236 strcmp2(item->path, root_folder) != 0)) {
1237 folder_tree_destroy(folder);
1238 item = folder_item_new(folder, folder->name, root_folder);
1239 item->folder = folder;
1240 folder->node = item->node = g_node_new(item);
1243 imap_scan_tree_recursive(session, FOLDER_ITEM(folder->node->data));
1244 imap_create_missing_folders(folder);
1249 static gint imap_scan_tree_recursive(IMAPSession *session, FolderItem *item)
1252 IMAPFolder *imapfolder;
1253 FolderItem *new_item;
1254 GSList *item_list, *cur;
1257 gchar *wildcard_path;
1261 g_return_val_if_fail(item != NULL, -1);
1262 g_return_val_if_fail(item->folder != NULL, -1);
1263 g_return_val_if_fail(item->no_sub == FALSE, -1);
1265 folder = item->folder;
1266 imapfolder = IMAP_FOLDER(folder);
1268 separator = imap_get_path_separator(imapfolder, item->path);
1270 if (folder->ui_func)
1271 folder->ui_func(folder, item, folder->ui_func_data);
1274 wildcard[0] = separator;
1277 real_path = imap_get_real_path(imapfolder, item->path);
1281 real_path = g_strdup("");
1284 Xstrcat_a(wildcard_path, real_path, wildcard,
1285 {g_free(real_path); return IMAP_ERROR;});
1286 QUOTE_IF_REQUIRED(wildcard_path, wildcard_path);
1288 imap_gen_send(session, "LIST \"\" %s",
1291 strtailchomp(real_path, separator);
1292 item_list = imap_parse_list(imapfolder, session, real_path, NULL);
1295 node = item->node->children;
1296 while (node != NULL) {
1297 FolderItem *old_item = FOLDER_ITEM(node->data);
1298 GNode *next = node->next;
1301 for (cur = item_list; cur != NULL; cur = cur->next) {
1302 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1303 if (!strcmp2(old_item->path, cur_item->path)) {
1304 new_item = cur_item;
1309 debug_print("folder '%s' not found. removing...\n",
1311 folder_item_remove(old_item);
1313 old_item->no_sub = new_item->no_sub;
1314 old_item->no_select = new_item->no_select;
1315 if (old_item->no_sub == TRUE && node->children) {
1316 debug_print("folder '%s' doesn't have "
1317 "subfolders. removing...\n",
1319 folder_item_remove_children(old_item);
1326 for (cur = item_list; cur != NULL; cur = cur->next) {
1327 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1329 for (node = item->node->children; node != NULL;
1330 node = node->next) {
1331 if (!strcmp2(FOLDER_ITEM(node->data)->path,
1333 new_item = FOLDER_ITEM(node->data);
1334 folder_item_destroy(cur_item);
1340 new_item = cur_item;
1341 debug_print("new folder '%s' found.\n", new_item->path);
1342 folder_item_append(item, new_item);
1345 if (!strcmp(new_item->path, "INBOX")) {
1346 new_item->stype = F_INBOX;
1347 folder->inbox = new_item;
1348 } else if (!item->parent || item->stype == F_INBOX) {
1351 base = g_basename(new_item->path);
1353 if (!folder->outbox && !strcasecmp(base, "Sent")) {
1354 new_item->stype = F_OUTBOX;
1355 folder->outbox = new_item;
1356 } else if (!folder->draft && !strcasecmp(base, "Drafts")) {
1357 new_item->stype = F_DRAFT;
1358 folder->draft = new_item;
1359 } else if (!folder->queue && !strcasecmp(base, "Queue")) {
1360 new_item->stype = F_QUEUE;
1361 folder->queue = new_item;
1362 } else if (!folder->trash && !strcasecmp(base, "Trash")) {
1363 new_item->stype = F_TRASH;
1364 folder->trash = new_item;
1368 if (new_item->no_sub == FALSE)
1369 imap_scan_tree_recursive(session, new_item);
1372 g_slist_free(item_list);
1374 return IMAP_SUCCESS;
1377 static GSList *imap_parse_list(IMAPFolder *folder, IMAPSession *session,
1378 const gchar *real_path, gchar *separator)
1380 gchar buf[IMAPBUFSIZE];
1382 gchar separator_str[16];
1385 gchar *loc_name, *loc_path;
1386 GSList *item_list = NULL;
1388 FolderItem *new_item;
1390 debug_print("getting list of %s ...\n",
1391 *real_path ? real_path : "\"\"");
1393 str = g_string_new(NULL);
1396 if (sock_gets(SESSION(session)->sock, buf, sizeof(buf)) <= 0) {
1397 log_warning(_("error occurred while getting LIST.\n"));
1401 if (buf[0] != '*' || buf[1] != ' ') {
1402 log_print("IMAP4< %s\n", buf);
1403 if (sscanf(buf, "%*d %16s", buf) < 1 ||
1404 strcmp(buf, "OK") != 0)
1405 log_warning(_("error occurred while getting LIST.\n"));
1409 debug_print("IMAP4< %s\n", buf);
1411 g_string_assign(str, buf);
1413 if (strncmp(p, "LIST ", 5) != 0) continue;
1416 if (*p != '(') continue;
1418 p = strchr_cpy(p, ')', flags, sizeof(flags));
1420 while (*p == ' ') p++;
1422 p = strchr_cpy(p, ' ', separator_str, sizeof(separator_str));
1424 extract_quote(separator_str, '"');
1425 if (!strcmp(separator_str, "NIL"))
1426 separator_str[0] = '\0';
1428 *separator = separator_str[0];
1431 while (*p == ' ') p++;
1432 if (*p == '{' || *p == '"')
1433 p = imap_parse_atom(SESSION(session)->sock, p,
1434 buf, sizeof(buf), str);
1436 strncpy2(buf, p, sizeof(buf));
1437 strtailchomp(buf, separator_str[0]);
1438 if (buf[0] == '\0') continue;
1439 if (!strcmp(buf, real_path)) continue;
1441 if (separator_str[0] != '\0')
1442 subst_char(buf, separator_str[0], '/');
1443 name = g_basename(buf);
1444 if (name[0] == '.') continue;
1446 loc_name = imap_modified_utf7_to_locale(name);
1447 loc_path = imap_modified_utf7_to_locale(buf);
1448 new_item = folder_item_new(FOLDER(folder), loc_name, loc_path);
1449 if (strcasestr(flags, "\\Noinferiors") != NULL)
1450 new_item->no_sub = TRUE;
1451 if (strcmp(buf, "INBOX") != 0 &&
1452 strcasestr(flags, "\\Noselect") != NULL)
1453 new_item->no_select = TRUE;
1455 item_list = g_slist_append(item_list, new_item);
1457 debug_print("folder '%s' found.\n", loc_path);
1462 g_string_free(str, TRUE);
1467 static gint imap_create_tree(Folder *folder)
1469 g_return_val_if_fail(folder != NULL, -1);
1470 g_return_val_if_fail(folder->node != NULL, -1);
1471 g_return_val_if_fail(folder->node->data != NULL, -1);
1472 g_return_val_if_fail(folder->account != NULL, -1);
1474 imap_scan_tree(folder);
1475 imap_create_missing_folders(folder);
1480 static void imap_create_missing_folders(Folder *folder)
1482 g_return_if_fail(folder != NULL);
1485 folder->inbox = imap_create_special_folder
1486 (folder, F_INBOX, "INBOX");
1488 if (!folder->outbox)
1489 folder->outbox = imap_create_special_folder
1490 (folder, F_OUTBOX, "Sent");
1492 folder->draft = imap_create_special_folder
1493 (folder, F_DRAFT, "Drafts");
1495 folder->queue = imap_create_special_folder
1496 (folder, F_QUEUE, "Queue");
1499 folder->trash = imap_create_special_folder
1500 (folder, F_TRASH, "Trash");
1503 static FolderItem *imap_create_special_folder(Folder *folder,
1504 SpecialFolderItemType stype,
1508 FolderItem *new_item;
1510 g_return_val_if_fail(folder != NULL, NULL);
1511 g_return_val_if_fail(folder->node != NULL, NULL);
1512 g_return_val_if_fail(folder->node->data != NULL, NULL);
1513 g_return_val_if_fail(folder->account != NULL, NULL);
1514 g_return_val_if_fail(name != NULL, NULL);
1516 item = FOLDER_ITEM(folder->node->data);
1517 new_item = imap_create_folder(folder, item, name);
1520 g_warning("Can't create '%s'\n", name);
1521 if (!folder->inbox) return NULL;
1523 new_item = imap_create_folder(folder, folder->inbox, name);
1525 g_warning("Can't create '%s' under INBOX\n", name);
1527 new_item->stype = stype;
1529 new_item->stype = stype;
1534 static gchar *imap_folder_get_path(Folder *folder)
1538 g_return_val_if_fail(folder != NULL, NULL);
1539 g_return_val_if_fail(folder->account != NULL, NULL);
1541 folder_path = g_strconcat(get_imap_cache_dir(),
1543 folder->account->recv_server,
1545 folder->account->userid,
1551 static gchar *imap_item_get_path(Folder *folder, FolderItem *item)
1553 gchar *folder_path, *path;
1555 g_return_val_if_fail(folder != NULL, NULL);
1556 g_return_val_if_fail(item != NULL, NULL);
1557 folder_path = imap_folder_get_path(folder);
1559 g_return_val_if_fail(folder_path != NULL, NULL);
1560 if (folder_path[0] == G_DIR_SEPARATOR) {
1562 path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
1565 path = g_strdup(folder_path);
1568 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1569 folder_path, G_DIR_SEPARATOR_S,
1572 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1575 g_free(folder_path);
1580 static FolderItem *imap_create_folder(Folder *folder, FolderItem *parent,
1583 gchar *dirpath, *imap_path;
1584 IMAPSession *session;
1585 FolderItem *new_item;
1591 g_return_val_if_fail(folder != NULL, NULL);
1592 g_return_val_if_fail(folder->account != NULL, NULL);
1593 g_return_val_if_fail(parent != NULL, NULL);
1594 g_return_val_if_fail(name != NULL, NULL);
1596 session = imap_session_get(folder);
1597 if (!session) return NULL;
1599 if (!parent->parent && strcmp(name, "INBOX") == 0)
1600 dirpath = g_strdup(name);
1601 else if (parent->path)
1602 dirpath = g_strconcat(parent->path, "/", name, NULL);
1603 else if ((p = strchr(name, '/')) != NULL && *(p + 1) != '\0')
1604 dirpath = g_strdup(name);
1605 else if (folder->account->imap_dir && *folder->account->imap_dir) {
1608 Xstrdup_a(imap_dir, folder->account->imap_dir, return NULL);
1609 strtailchomp(imap_dir, '/');
1610 dirpath = g_strconcat(imap_dir, "/", name, NULL);
1612 dirpath = g_strdup(name);
1614 /* keep trailing directory separator to create a folder that contains
1616 imap_path = imap_locale_to_modified_utf7(dirpath);
1617 strtailchomp(dirpath, '/');
1618 Xstrdup_a(new_name, name, {g_free(dirpath); return NULL;});
1619 strtailchomp(new_name, '/');
1620 separator = imap_get_path_separator(IMAP_FOLDER(folder), imap_path);
1621 imap_path_separator_subst(imap_path, separator);
1622 subst_char(new_name, '/', separator);
1624 if (strcmp(name, "INBOX") != 0) {
1627 gboolean exist = FALSE;
1629 argbuf = g_ptr_array_new();
1630 ok = imap_cmd_list(session, NULL, imap_path,
1632 if (ok != IMAP_SUCCESS) {
1633 log_warning(_("can't create mailbox: LIST failed\n"));
1636 ptr_array_free_strings(argbuf);
1637 g_ptr_array_free(argbuf, TRUE);
1641 for (i = 0; i < argbuf->len; i++) {
1643 str = g_ptr_array_index(argbuf, i);
1644 if (!strncmp(str, "LIST ", 5)) {
1649 ptr_array_free_strings(argbuf);
1650 g_ptr_array_free(argbuf, TRUE);
1653 ok = imap_cmd_create(session, imap_path);
1654 if (ok != IMAP_SUCCESS) {
1655 log_warning(_("can't create mailbox\n"));
1663 new_item = folder_item_new(folder, new_name, dirpath);
1664 folder_item_append(parent, new_item);
1668 dirpath = folder_item_get_path(new_item);
1669 if (!is_dir_exist(dirpath))
1670 make_dir_hier(dirpath);
1676 static gint imap_rename_folder(Folder *folder, FolderItem *item,
1681 gchar *real_oldpath;
1682 gchar *real_newpath;
1684 gchar *old_cache_dir;
1685 gchar *new_cache_dir;
1686 IMAPSession *session;
1689 gint exists, recent, unseen;
1690 guint32 uid_validity;
1692 g_return_val_if_fail(folder != NULL, -1);
1693 g_return_val_if_fail(item != NULL, -1);
1694 g_return_val_if_fail(item->path != NULL, -1);
1695 g_return_val_if_fail(name != NULL, -1);
1697 session = imap_session_get(folder);
1698 if (!session) return -1;
1700 real_oldpath = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1702 g_free(session->mbox);
1703 session->mbox = NULL;
1704 ok = imap_cmd_examine(session, "INBOX",
1705 &exists, &recent, &unseen, &uid_validity);
1706 if (ok != IMAP_SUCCESS) {
1707 g_free(real_oldpath);
1711 separator = imap_get_path_separator(IMAP_FOLDER(folder), item->path);
1712 if (strchr(item->path, G_DIR_SEPARATOR)) {
1713 dirpath = g_dirname(item->path);
1714 newpath = g_strconcat(dirpath, G_DIR_SEPARATOR_S, name, NULL);
1717 newpath = g_strdup(name);
1719 real_newpath = imap_locale_to_modified_utf7(newpath);
1720 imap_path_separator_subst(real_newpath, separator);
1722 ok = imap_cmd_rename(session, real_oldpath, real_newpath);
1723 if (ok != IMAP_SUCCESS) {
1724 log_warning(_("can't rename mailbox: %s to %s\n"),
1725 real_oldpath, real_newpath);
1726 g_free(real_oldpath);
1728 g_free(real_newpath);
1733 item->name = g_strdup(name);
1735 old_cache_dir = folder_item_get_path(item);
1737 paths[0] = g_strdup(item->path);
1739 g_node_traverse(item->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1740 imap_rename_folder_func, paths);
1742 if (is_dir_exist(old_cache_dir)) {
1743 new_cache_dir = folder_item_get_path(item);
1744 if (rename(old_cache_dir, new_cache_dir) < 0) {
1745 FILE_OP_ERROR(old_cache_dir, "rename");
1747 g_free(new_cache_dir);
1750 g_free(old_cache_dir);
1753 g_free(real_oldpath);
1754 g_free(real_newpath);
1759 static gint imap_remove_folder(Folder *folder, FolderItem *item)
1762 IMAPSession *session;
1765 gint exists, recent, unseen;
1766 guint32 uid_validity;
1768 g_return_val_if_fail(folder != NULL, -1);
1769 g_return_val_if_fail(item != NULL, -1);
1770 g_return_val_if_fail(item->path != NULL, -1);
1772 session = imap_session_get(folder);
1773 if (!session) return -1;
1775 path = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1777 ok = imap_cmd_examine(session, "INBOX",
1778 &exists, &recent, &unseen, &uid_validity);
1779 if (ok != IMAP_SUCCESS) {
1784 ok = imap_cmd_delete(session, path);
1785 if (ok != IMAP_SUCCESS) {
1786 log_warning(_("can't delete mailbox\n"));
1792 cache_dir = folder_item_get_path(item);
1793 if (is_dir_exist(cache_dir) && remove_dir_recursive(cache_dir) < 0)
1794 g_warning("can't remove directory '%s'\n", cache_dir);
1796 folder_item_remove(item);
1801 static GSList *imap_get_uncached_messages(IMAPSession *session,
1803 MsgNumberList *numlist)
1806 GSList *newlist = NULL;
1807 GSList *llast = NULL;
1810 GSList *seq_list, *cur;
1813 g_return_val_if_fail(session != NULL, NULL);
1814 g_return_val_if_fail(item != NULL, NULL);
1815 g_return_val_if_fail(item->folder != NULL, NULL);
1816 g_return_val_if_fail(FOLDER_CLASS(item->folder) == &imap_class, NULL);
1818 seq_list = imap_get_seq_set_from_numlist(numlist);
1819 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
1820 imapset = cur->data;
1822 if (imap_cmd_envelope(session, imapset)
1824 log_warning(_("can't get envelope\n"));
1828 str = g_string_new(NULL);
1831 if ((tmp = sock_getline(SESSION(session)->sock)) == NULL) {
1832 log_warning(_("error occurred while getting envelope.\n"));
1833 g_string_free(str, TRUE);
1837 if (tmp[0] != '*' || tmp[1] != ' ') {
1838 log_print("IMAP4< %s\n", tmp);
1842 if (strstr(tmp, "FETCH") == NULL) {
1843 log_print("IMAP4< %s\n", tmp);
1847 log_print("IMAP4< %s\n", tmp);
1848 g_string_assign(str, tmp);
1851 msginfo = imap_parse_envelope
1852 (SESSION(session)->sock, item, str);
1854 log_warning(_("can't parse envelope: %s\n"), str->str);
1857 if (item->stype == F_QUEUE) {
1858 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_QUEUED);
1859 } else if (item->stype == F_DRAFT) {
1860 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_DRAFT);
1863 msginfo->folder = item;
1866 llast = newlist = g_slist_append(newlist, msginfo);
1868 llast = g_slist_append(llast, msginfo);
1869 llast = llast->next;
1873 g_string_free(str, TRUE);
1875 imap_seq_set_free(seq_list);
1880 static void imap_delete_all_cached_messages(FolderItem *item)
1884 g_return_if_fail(item != NULL);
1885 g_return_if_fail(item->folder != NULL);
1886 g_return_if_fail(FOLDER_CLASS(item->folder) == &imap_class);
1888 debug_print("Deleting all cached messages...\n");
1890 dir = folder_item_get_path(item);
1891 if (is_dir_exist(dir))
1892 remove_all_numbered_files(dir);
1895 debug_print("done.\n");
1899 static SockInfo *imap_open_tunnel(const gchar *server,
1900 const gchar *tunnelcmd,
1903 static SockInfo *imap_open_tunnel(const gchar *server,
1904 const gchar *tunnelcmd)
1909 if ((sock = sock_connect_cmd(server, tunnelcmd)) == NULL) {
1910 log_warning(_("Can't establish IMAP4 session with: %s\n"),
1915 return imap_init_sock(sock, ssl_type);
1917 return imap_init_sock(sock);
1923 static SockInfo *imap_open(const gchar *server, gushort port,
1926 static SockInfo *imap_open(const gchar *server, gushort port)
1931 if ((sock = sock_connect(server, port)) == NULL) {
1932 log_warning(_("Can't connect to IMAP4 server: %s:%d\n"),
1938 if (ssl_type == SSL_TUNNEL && !ssl_init_socket(sock)) {
1939 log_warning(_("Can't establish IMAP4 session with: %s:%d\n"),
1949 static SockInfo *imap_init_sock(SockInfo *sock, SSLType ssl_type)
1951 static SockInfo *imap_init_sock(SockInfo *sock)
1958 static GList *imap_parse_namespace_str(gchar *str)
1963 IMAPNameSpace *namespace;
1964 GList *ns_list = NULL;
1966 while (*p != '\0') {
1967 /* parse ("#foo" "/") */
1969 while (*p && *p != '(') p++;
1970 if (*p == '\0') break;
1973 while (*p && *p != '"') p++;
1974 if (*p == '\0') break;
1978 while (*p && *p != '"') p++;
1979 if (*p == '\0') break;
1983 while (*p && isspace(*p)) p++;
1984 if (*p == '\0') break;
1985 if (strncmp(p, "NIL", 3) == 0)
1987 else if (*p == '"') {
1990 while (*p && *p != '"') p++;
1991 if (*p == '\0') break;
1996 while (*p && *p != ')') p++;
1997 if (*p == '\0') break;
2000 namespace = g_new(IMAPNameSpace, 1);
2001 namespace->name = g_strdup(name);
2002 namespace->separator = separator ? separator[0] : '\0';
2003 ns_list = g_list_append(ns_list, namespace);
2009 static void imap_parse_namespace(IMAPSession *session, IMAPFolder *folder)
2014 g_return_if_fail(session != NULL);
2015 g_return_if_fail(folder != NULL);
2017 if (folder->ns_personal != NULL ||
2018 folder->ns_others != NULL ||
2019 folder->ns_shared != NULL)
2022 if (!imap_has_capability(session, "NAMESPACE")) {
2023 imap_get_namespace_by_list(session, folder);
2027 if (imap_cmd_namespace(session, &ns_str)
2029 log_warning(_("can't get namespace\n"));
2033 str_array = strsplit_parenthesis(ns_str, '(', ')', 3);
2034 if (str_array == NULL) {
2036 imap_get_namespace_by_list(session, folder);
2040 folder->ns_personal = imap_parse_namespace_str(str_array[0]);
2041 if (str_array[0] && str_array[1])
2042 folder->ns_others = imap_parse_namespace_str(str_array[1]);
2043 if (str_array[0] && str_array[1] && str_array[2])
2044 folder->ns_shared = imap_parse_namespace_str(str_array[2]);
2045 g_strfreev(str_array);
2049 static void imap_get_namespace_by_list(IMAPSession *session, IMAPFolder *folder)
2051 GSList *item_list, *cur;
2052 gchar separator = '\0';
2053 IMAPNameSpace *namespace;
2055 g_return_if_fail(session != NULL);
2056 g_return_if_fail(folder != NULL);
2058 if (folder->ns_personal != NULL ||
2059 folder->ns_others != NULL ||
2060 folder->ns_shared != NULL)
2063 imap_gen_send(session, "LIST \"\" \"\"");
2064 item_list = imap_parse_list(folder, session, "", &separator);
2065 for (cur = item_list; cur != NULL; cur = cur->next)
2066 folder_item_destroy(FOLDER_ITEM(cur->data));
2067 g_slist_free(item_list);
2069 namespace = g_new(IMAPNameSpace, 1);
2070 namespace->name = g_strdup("");
2071 namespace->separator = separator;
2072 folder->ns_personal = g_list_append(NULL, namespace);
2075 static IMAPNameSpace *imap_find_namespace_from_list(GList *ns_list,
2078 IMAPNameSpace *namespace = NULL;
2079 gchar *tmp_path, *name;
2081 if (!path) path = "";
2083 for (; ns_list != NULL; ns_list = ns_list->next) {
2084 IMAPNameSpace *tmp_ns = ns_list->data;
2086 Xstrcat_a(tmp_path, path, "/", return namespace);
2087 Xstrdup_a(name, tmp_ns->name, return namespace);
2088 if (tmp_ns->separator && tmp_ns->separator != '/') {
2089 subst_char(tmp_path, tmp_ns->separator, '/');
2090 subst_char(name, tmp_ns->separator, '/');
2092 if (strncmp(tmp_path, name, strlen(name)) == 0)
2099 static IMAPNameSpace *imap_find_namespace(IMAPFolder *folder,
2102 IMAPNameSpace *namespace;
2104 g_return_val_if_fail(folder != NULL, NULL);
2106 namespace = imap_find_namespace_from_list(folder->ns_personal, path);
2107 if (namespace) return namespace;
2108 namespace = imap_find_namespace_from_list(folder->ns_others, path);
2109 if (namespace) return namespace;
2110 namespace = imap_find_namespace_from_list(folder->ns_shared, path);
2111 if (namespace) return namespace;
2116 static gchar imap_get_path_separator(IMAPFolder *folder, const gchar *path)
2118 IMAPNameSpace *namespace;
2119 gchar separator = '/';
2121 namespace = imap_find_namespace(folder, path);
2122 if (namespace && namespace->separator)
2123 separator = namespace->separator;
2128 static gchar *imap_get_real_path(IMAPFolder *folder, const gchar *path)
2133 g_return_val_if_fail(folder != NULL, NULL);
2134 g_return_val_if_fail(path != NULL, NULL);
2136 real_path = imap_locale_to_modified_utf7(path);
2137 separator = imap_get_path_separator(folder, path);
2138 imap_path_separator_subst(real_path, separator);
2143 static gchar *imap_parse_atom(SockInfo *sock, gchar *src,
2144 gchar *dest, gint dest_len, GString *str)
2146 gchar *cur_pos = src;
2149 g_return_val_if_fail(str != NULL, cur_pos);
2151 /* read the next line if the current response buffer is empty */
2152 while (isspace(*cur_pos)) cur_pos++;
2153 while (*cur_pos == '\0') {
2154 if ((nextline = sock_getline(sock)) == NULL)
2156 g_string_assign(str, nextline);
2158 strretchomp(nextline);
2159 /* log_print("IMAP4< %s\n", nextline); */
2160 debug_print("IMAP4< %s\n", nextline);
2163 while (isspace(*cur_pos)) cur_pos++;
2166 if (!strncmp(cur_pos, "NIL", 3)) {
2169 } else if (*cur_pos == '\"') {
2172 p = get_quoted(cur_pos, '\"', dest, dest_len);
2173 cur_pos = p ? p : cur_pos + 2;
2174 } else if (*cur_pos == '{') {
2179 cur_pos = strchr_cpy(cur_pos + 1, '}', buf, sizeof(buf));
2181 g_return_val_if_fail(len >= 0, cur_pos);
2183 g_string_truncate(str, 0);
2187 if ((nextline = sock_getline(sock)) == NULL)
2189 line_len += strlen(nextline);
2190 g_string_append(str, nextline);
2192 strretchomp(nextline);
2193 /* log_print("IMAP4< %s\n", nextline); */
2194 debug_print("IMAP4< %s\n", nextline);
2196 } while (line_len < len);
2198 memcpy(dest, cur_pos, MIN(len, dest_len - 1));
2199 dest[MIN(len, dest_len - 1)] = '\0';
2206 static gchar *imap_get_header(SockInfo *sock, gchar *cur_pos, gchar **headers,
2216 g_return_val_if_fail(str != NULL, cur_pos);
2218 while (isspace(*cur_pos)) cur_pos++;
2220 g_return_val_if_fail(*cur_pos == '{', cur_pos);
2222 cur_pos = strchr_cpy(cur_pos + 1, '}', buf, sizeof(buf));
2224 g_return_val_if_fail(len >= 0, cur_pos);
2226 g_string_truncate(str, 0);
2230 if ((nextline = sock_getline(sock)) == NULL)
2232 block_len += strlen(nextline);
2233 g_string_append(str, nextline);
2235 strretchomp(nextline);
2236 /* debug_print("IMAP4< %s\n", nextline); */
2238 } while (block_len < len);
2240 debug_print("IMAP4< [contents of BODY.PEEK[HEADER.FIELDS (...)]]\n");
2242 *headers = g_strndup(cur_pos, len);
2245 while (isspace(*cur_pos)) cur_pos++;
2246 while (*cur_pos == '\0') {
2247 if ((nextline = sock_getline(sock)) == NULL)
2249 g_string_assign(str, nextline);
2251 strretchomp(nextline);
2252 debug_print("IMAP4< %s\n", nextline);
2255 while (isspace(*cur_pos)) cur_pos++;
2261 static MsgFlags imap_parse_flags(const gchar *flag_str)
2263 const gchar *p = flag_str;
2264 MsgFlags flags = {0, 0};
2266 flags.perm_flags = MSG_UNREAD;
2268 while ((p = strchr(p, '\\')) != NULL) {
2271 if (g_strncasecmp(p, "Recent", 6) == 0 && MSG_IS_UNREAD(flags)) {
2272 MSG_SET_PERM_FLAGS(flags, MSG_NEW);
2273 } else if (g_strncasecmp(p, "Seen", 4) == 0) {
2274 MSG_UNSET_PERM_FLAGS(flags, MSG_NEW|MSG_UNREAD);
2275 } else if (g_strncasecmp(p, "Deleted", 7) == 0) {
2276 MSG_SET_PERM_FLAGS(flags, MSG_DELETED);
2277 } else if (g_strncasecmp(p, "Flagged", 7) == 0) {
2278 MSG_SET_PERM_FLAGS(flags, MSG_MARKED);
2279 } else if (g_strncasecmp(p, "Answered", 8) == 0) {
2280 MSG_SET_PERM_FLAGS(flags, MSG_REPLIED);
2287 static MsgInfo *imap_parse_envelope(SockInfo *sock, FolderItem *item,
2290 gchar buf[IMAPBUFSIZE];
2291 MsgInfo *msginfo = NULL;
2296 MsgFlags flags = {0, 0}, imap_flags = {0, 0};
2298 g_return_val_if_fail(line_str != NULL, NULL);
2299 g_return_val_if_fail(line_str->str[0] == '*' &&
2300 line_str->str[1] == ' ', NULL);
2302 MSG_SET_TMP_FLAGS(flags, MSG_IMAP);
2303 if (item->stype == F_QUEUE) {
2304 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
2305 } else if (item->stype == F_DRAFT) {
2306 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
2309 cur_pos = line_str->str + 2;
2311 #define PARSE_ONE_ELEMENT(ch) \
2313 cur_pos = strchr_cpy(cur_pos, ch, buf, sizeof(buf)); \
2314 if (cur_pos == NULL) { \
2315 g_warning("cur_pos == NULL\n"); \
2316 procmsg_msginfo_free(msginfo); \
2321 PARSE_ONE_ELEMENT(' ');
2324 PARSE_ONE_ELEMENT(' ');
2325 g_return_val_if_fail(!strcmp(buf, "FETCH"), NULL);
2327 g_return_val_if_fail(*cur_pos == '(', NULL);
2330 while (*cur_pos != '\0' && *cur_pos != ')') {
2331 while (*cur_pos == ' ') cur_pos++;
2333 if (!strncmp(cur_pos, "UID ", 4)) {
2335 uid = strtoul(cur_pos, &cur_pos, 10);
2336 } else if (!strncmp(cur_pos, "FLAGS ", 6)) {
2338 if (*cur_pos != '(') {
2339 g_warning("*cur_pos != '('\n");
2340 procmsg_msginfo_free(msginfo);
2344 PARSE_ONE_ELEMENT(')');
2345 imap_flags = imap_parse_flags(buf);
2346 } else if (!strncmp(cur_pos, "RFC822.SIZE ", 12)) {
2348 size = strtol(cur_pos, &cur_pos, 10);
2349 } else if (!strncmp(cur_pos, "BODY[HEADER.FIELDS ", 19)) {
2353 if (*cur_pos != '(') {
2354 g_warning("*cur_pos != '('\n");
2355 procmsg_msginfo_free(msginfo);
2359 PARSE_ONE_ELEMENT(')');
2360 if (*cur_pos != ']') {
2361 g_warning("*cur_pos != ']'\n");
2362 procmsg_msginfo_free(msginfo);
2367 cur_pos = imap_get_header(sock, cur_pos, &headers,
2369 msginfo = procheader_parse_str(headers, flags, FALSE, FALSE);
2372 g_warning("invalid FETCH response: %s\n", cur_pos);
2378 msginfo->msgnum = uid;
2379 msginfo->size = size;
2380 msginfo->flags.tmp_flags |= imap_flags.tmp_flags;
2381 msginfo->flags.perm_flags = imap_flags.perm_flags;
2387 static gchar *imap_get_flag_str(IMAPFlags flags)
2392 str = g_string_new(NULL);
2394 if (IMAP_IS_SEEN(flags)) g_string_append(str, "\\Seen ");
2395 if (IMAP_IS_ANSWERED(flags)) g_string_append(str, "\\Answered ");
2396 if (IMAP_IS_FLAGGED(flags)) g_string_append(str, "\\Flagged ");
2397 if (IMAP_IS_DELETED(flags)) g_string_append(str, "\\Deleted ");
2398 if (IMAP_IS_DRAFT(flags)) g_string_append(str, "\\Draft");
2400 if (str->len > 0 && str->str[str->len - 1] == ' ')
2401 g_string_truncate(str, str->len - 1);
2404 g_string_free(str, FALSE);
2409 static gint imap_set_message_flags(IMAPSession *session,
2410 MsgNumberList *numlist,
2417 GSList *seq_list, *cur;
2420 flag_str = imap_get_flag_str(flags);
2421 cmd = g_strconcat(is_set ? "+FLAGS.SILENT (" : "-FLAGS.SILENT (",
2422 flag_str, ")", NULL);
2425 seq_list = imap_get_seq_set_from_numlist(numlist);
2426 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
2427 imapset = cur->data;
2429 ok = imap_cmd_store(session, imapset, cmd);
2431 imap_seq_set_free(seq_list);
2437 static gint imap_select(IMAPSession *session, IMAPFolder *folder,
2439 gint *exists, gint *recent, gint *unseen,
2440 guint32 *uid_validity)
2444 gint exists_, recent_, unseen_, uid_validity_;
2446 if (!exists || !recent || !unseen || !uid_validity) {
2447 if (session->mbox && strcmp(session->mbox, path) == 0)
2448 return IMAP_SUCCESS;
2452 uid_validity = &uid_validity_;
2455 g_free(session->mbox);
2456 session->mbox = NULL;
2458 real_path = imap_get_real_path(folder, path);
2459 ok = imap_cmd_select(session, real_path,
2460 exists, recent, unseen, uid_validity);
2461 if (ok != IMAP_SUCCESS)
2462 log_warning(_("can't select folder: %s\n"), real_path);
2464 session->mbox = g_strdup(path);
2465 session->folder_content_changed = FALSE;
2472 #define THROW(err) { ok = err; goto catch; }
2474 static gint imap_status(IMAPSession *session, IMAPFolder *folder,
2476 gint *messages, gint *recent,
2477 guint32 *uid_next, guint32 *uid_validity,
2483 GPtrArray *argbuf = NULL;
2486 if (messages && recent && uid_next && uid_validity && unseen) {
2487 *messages = *recent = *uid_next = *uid_validity = *unseen = 0;
2488 argbuf = g_ptr_array_new();
2491 real_path = imap_get_real_path(folder, path);
2492 QUOTE_IF_REQUIRED(real_path_, real_path);
2493 imap_gen_send(session, "STATUS %s "
2494 "(MESSAGES RECENT UIDNEXT UIDVALIDITY UNSEEN)",
2497 ok = imap_cmd_ok(session, argbuf);
2498 if (ok != IMAP_SUCCESS || !argbuf) THROW(ok);
2500 str = search_array_str(argbuf, "STATUS");
2501 if (!str) THROW(IMAP_ERROR);
2503 str = strchr(str, '(');
2504 if (!str) THROW(IMAP_ERROR);
2506 while (*str != '\0' && *str != ')') {
2507 while (*str == ' ') str++;
2509 if (!strncmp(str, "MESSAGES ", 9)) {
2511 *messages = strtol(str, &str, 10);
2512 } else if (!strncmp(str, "RECENT ", 7)) {
2514 *recent = strtol(str, &str, 10);
2515 } else if (!strncmp(str, "UIDNEXT ", 8)) {
2517 *uid_next = strtoul(str, &str, 10);
2518 } else if (!strncmp(str, "UIDVALIDITY ", 12)) {
2520 *uid_validity = strtoul(str, &str, 10);
2521 } else if (!strncmp(str, "UNSEEN ", 7)) {
2523 *unseen = strtol(str, &str, 10);
2525 g_warning("invalid STATUS response: %s\n", str);
2533 ptr_array_free_strings(argbuf);
2534 g_ptr_array_free(argbuf, TRUE);
2542 static gboolean imap_has_capability(IMAPSession *session, const gchar *cap)
2546 for (p = session->capability; *p != NULL; ++p) {
2547 if (!g_strcasecmp(*p, cap))
2554 static void imap_free_capabilities(IMAPSession *session)
2556 g_strfreev(session->capability);
2557 session->capability = NULL;
2560 /* low-level IMAP4rev1 commands */
2562 static gint imap_cmd_authenticate(IMAPSession *session, const gchar *user,
2563 const gchar *pass, IMAPAuthType type)
2570 gchar hexdigest[33];
2574 auth_type = "CRAM-MD5";
2576 imap_gen_send(session, "AUTHENTICATE %s", auth_type);
2577 ok = imap_gen_recv(session, &buf);
2578 if (ok != IMAP_SUCCESS || buf[0] != '+' || buf[1] != ' ') {
2583 challenge = g_malloc(strlen(buf + 2) + 1);
2584 challenge_len = base64_decode(challenge, buf + 2, -1);
2585 challenge[challenge_len] = '\0';
2587 log_print("IMAP< [Decoded: %s]\n", challenge);
2589 md5_hex_hmac(hexdigest, challenge, challenge_len, pass, strlen(pass));
2592 response = g_strdup_printf("%s %s", user, hexdigest);
2593 log_print("IMAP> [Encoded: %s]\n", response);
2594 response64 = g_malloc((strlen(response) + 3) * 2 + 1);
2595 base64_encode(response64, response, strlen(response));
2598 log_print("IMAP> %s\n", response64);
2599 sock_puts(SESSION(session)->sock, response64);
2600 ok = imap_cmd_ok(session, NULL);
2601 if (ok != IMAP_SUCCESS)
2602 log_warning(_("IMAP4 authentication failed.\n"));
2607 static gint imap_cmd_login(IMAPSession *session,
2608 const gchar *user, const gchar *pass)
2610 gchar *user_, *pass_;
2613 QUOTE_IF_REQUIRED(user_, user);
2614 QUOTE_IF_REQUIRED(pass_, pass);
2615 imap_gen_send(session, "LOGIN %s %s", user_, pass_);
2617 ok = imap_cmd_ok(session, NULL);
2618 if (ok != IMAP_SUCCESS)
2619 log_warning(_("IMAP4 login failed.\n"));
2624 static gint imap_cmd_logout(IMAPSession *session)
2626 imap_gen_send(session, "LOGOUT");
2627 return imap_cmd_ok(session, NULL);
2630 static gint imap_cmd_noop(IMAPSession *session)
2632 imap_gen_send(session, "NOOP");
2633 return imap_cmd_ok(session, NULL);
2636 static gint imap_cmd_starttls(IMAPSession *session)
2638 imap_gen_send(session, "STARTTLS");
2639 return imap_cmd_ok(session, NULL);
2642 #define THROW(err) { ok = err; goto catch; }
2644 static gint imap_cmd_namespace(IMAPSession *session, gchar **ns_str)
2650 argbuf = g_ptr_array_new();
2652 imap_gen_send(session, "NAMESPACE");
2653 if ((ok = imap_cmd_ok(session, argbuf)) != IMAP_SUCCESS) THROW(ok);
2655 str = search_array_str(argbuf, "NAMESPACE");
2656 if (!str) THROW(IMAP_ERROR);
2658 *ns_str = g_strdup(str);
2661 ptr_array_free_strings(argbuf);
2662 g_ptr_array_free(argbuf, TRUE);
2669 static gint imap_cmd_list(IMAPSession *session, const gchar *ref,
2670 const gchar *mailbox, GPtrArray *argbuf)
2672 gchar *ref_, *mailbox_;
2674 if (!ref) ref = "\"\"";
2675 if (!mailbox) mailbox = "\"\"";
2677 QUOTE_IF_REQUIRED(ref_, ref);
2678 QUOTE_IF_REQUIRED(mailbox_, mailbox);
2679 imap_gen_send(session, "LIST %s %s", ref_, mailbox_);
2681 return imap_cmd_ok(session, argbuf);
2684 #define THROW goto catch
2686 static gint imap_cmd_do_select(IMAPSession *session, const gchar *folder,
2688 gint *exists, gint *recent, gint *unseen,
2689 guint32 *uid_validity)
2697 *exists = *recent = *unseen = *uid_validity = 0;
2698 argbuf = g_ptr_array_new();
2701 select_cmd = "EXAMINE";
2703 select_cmd = "SELECT";
2705 QUOTE_IF_REQUIRED(folder_, folder);
2706 imap_gen_send(session, "%s %s", select_cmd, folder_);
2708 if ((ok = imap_cmd_ok(session, argbuf)) != IMAP_SUCCESS) THROW;
2710 resp_str = search_array_contain_str(argbuf, "EXISTS");
2712 if (sscanf(resp_str,"%d EXISTS", exists) != 1) {
2713 g_warning("imap_cmd_select(): invalid EXISTS line.\n");
2718 resp_str = search_array_contain_str(argbuf, "RECENT");
2720 if (sscanf(resp_str, "%d RECENT", recent) != 1) {
2721 g_warning("imap_cmd_select(): invalid RECENT line.\n");
2726 resp_str = search_array_contain_str(argbuf, "UIDVALIDITY");
2728 if (sscanf(resp_str, "OK [UIDVALIDITY %u] ", uid_validity)
2730 g_warning("imap_cmd_select(): invalid UIDVALIDITY line.\n");
2735 resp_str = search_array_contain_str(argbuf, "UNSEEN");
2737 if (sscanf(resp_str, "OK [UNSEEN %d] ", unseen) != 1) {
2738 g_warning("imap_cmd_select(): invalid UNSEEN line.\n");
2744 ptr_array_free_strings(argbuf);
2745 g_ptr_array_free(argbuf, TRUE);
2750 static gint imap_cmd_select(IMAPSession *session, const gchar *folder,
2751 gint *exists, gint *recent, gint *unseen,
2752 guint32 *uid_validity)
2754 return imap_cmd_do_select(session, folder, FALSE,
2755 exists, recent, unseen, uid_validity);
2758 static gint imap_cmd_examine(IMAPSession *session, const gchar *folder,
2759 gint *exists, gint *recent, gint *unseen,
2760 guint32 *uid_validity)
2762 return imap_cmd_do_select(session, folder, TRUE,
2763 exists, recent, unseen, uid_validity);
2768 static gint imap_cmd_create(IMAPSession *session, const gchar *folder)
2772 QUOTE_IF_REQUIRED(folder_, folder);
2773 imap_gen_send(session, "CREATE %s", folder_);
2775 return imap_cmd_ok(session, NULL);
2778 static gint imap_cmd_rename(IMAPSession *session, const gchar *old_folder,
2779 const gchar *new_folder)
2781 gchar *old_folder_, *new_folder_;
2783 QUOTE_IF_REQUIRED(old_folder_, old_folder);
2784 QUOTE_IF_REQUIRED(new_folder_, new_folder);
2785 imap_gen_send(session, "RENAME %s %s", old_folder_, new_folder_);
2787 return imap_cmd_ok(session, NULL);
2790 static gint imap_cmd_delete(IMAPSession *session, const gchar *folder)
2794 QUOTE_IF_REQUIRED(folder_, folder);
2795 imap_gen_send(session, "DELETE %s", folder_);
2797 return imap_cmd_ok(session, NULL);
2800 static gint imap_cmd_search(IMAPSession *session, const gchar *criteria,
2807 g_return_val_if_fail(criteria != NULL, IMAP_ERROR);
2808 g_return_val_if_fail(list != NULL, IMAP_ERROR);
2812 argbuf = g_ptr_array_new();
2813 imap_gen_send(session, "UID SEARCH %s", criteria);
2815 ok = imap_cmd_ok(session, argbuf);
2816 if (ok != IMAP_SUCCESS) {
2817 ptr_array_free_strings(argbuf);
2818 g_ptr_array_free(argbuf, TRUE);
2822 if ((uidlist = search_array_str(argbuf, "SEARCH ")) != NULL) {
2823 gchar **strlist, **p;
2825 strlist = g_strsplit(uidlist + 7, " ", 0);
2826 for (p = strlist; *p != NULL; ++p) {
2829 if (sscanf(*p, "%d", &msgnum) == 1)
2830 *list = g_slist_append(*list, GINT_TO_POINTER(msgnum));
2832 g_strfreev(strlist);
2834 ptr_array_free_strings(argbuf);
2835 g_ptr_array_free(argbuf, TRUE);
2837 return IMAP_SUCCESS;
2840 static gint imap_cmd_fetch(IMAPSession *session, guint32 uid,
2841 const gchar *filename)
2849 g_return_val_if_fail(filename != NULL, IMAP_ERROR);
2851 imap_gen_send(session, "UID FETCH %d BODY.PEEK[]", uid);
2853 while ((ok = imap_gen_recv(session, &buf)) == IMAP_SUCCESS) {
2854 if (buf[0] != '*' || buf[1] != ' ') {
2858 if (strstr(buf, "FETCH") != NULL) break;
2861 if (ok != IMAP_SUCCESS) {
2866 #define RETURN_ERROR_IF_FAIL(cond) \
2869 return IMAP_ERROR; \
2872 cur_pos = strchr(buf, '{');
2873 RETURN_ERROR_IF_FAIL(cur_pos != NULL);
2874 cur_pos = strchr_cpy(cur_pos + 1, '}', size_str, sizeof(size_str));
2875 RETURN_ERROR_IF_FAIL(cur_pos != NULL);
2876 size_num = atol(size_str);
2877 RETURN_ERROR_IF_FAIL(size_num >= 0);
2879 RETURN_ERROR_IF_FAIL(*cur_pos == '\0');
2881 #undef RETURN_ERROR_IF_FAIL
2885 if (recv_bytes_write_to_file(SESSION(session)->sock,
2886 size_num, filename) != 0)
2889 if (imap_gen_recv(session, &buf) != IMAP_SUCCESS) {
2894 if (buf[0] == '\0' || buf[strlen(buf) - 1] != ')') {
2900 ok = imap_cmd_ok(session, NULL);
2905 static gint imap_cmd_append(IMAPSession *session, const gchar *destfolder,
2906 const gchar *file, IMAPFlags flags,
2915 gchar buf[BUFFSIZE];
2920 g_return_val_if_fail(file != NULL, IMAP_ERROR);
2922 size = get_file_size_as_crlf(file);
2923 if ((fp = fopen(file, "rb")) == NULL) {
2924 FILE_OP_ERROR(file, "fopen");
2927 QUOTE_IF_REQUIRED(destfolder_, destfolder);
2928 flag_str = imap_get_flag_str(flags);
2929 imap_gen_send(session, "APPEND %s (%s) {%d}",
2930 destfolder_, flag_str, size);
2933 ok = imap_gen_recv(session, &ret);
2934 if (ok != IMAP_SUCCESS || ret[0] != '+' || ret[1] != ' ') {
2935 log_warning(_("can't append %s to %s\n"), file, destfolder_);
2942 log_print("IMAP4> %s\n", _("(sending file...)"));
2944 while (fgets(buf, sizeof(buf), fp) != NULL) {
2946 if (sock_puts(SESSION(session)->sock, buf) < 0) {
2953 FILE_OP_ERROR(file, "fgets");
2958 sock_puts(SESSION(session)->sock, "");
2962 if (new_uid != NULL)
2965 if (new_uid != NULL && session->uidplus) {
2966 argbuf = g_ptr_array_new();
2968 ok = imap_cmd_ok(session, argbuf);
2969 if ((ok == IMAP_SUCCESS) && (argbuf->len > 0)) {
2970 resp_str = g_ptr_array_index(argbuf, argbuf->len - 1);
2972 sscanf(resp_str, "%*u OK [APPENDUID %*u %u]",
2974 *new_uid = new_uid_;
2978 ptr_array_free_strings(argbuf);
2979 g_ptr_array_free(argbuf, TRUE);
2981 ok = imap_cmd_ok(session, NULL);
2983 if (ok != IMAP_SUCCESS)
2984 log_warning(_("can't append message to %s\n"),
2990 static MsgNumberList *imapset_to_numlist(IMAPSet imapset)
2992 gchar **ranges, **range;
2994 MsgNumberList *uids = NULL;
2996 ranges = g_strsplit(imapset, ",", 0);
2997 for (range = ranges; *range != NULL; range++) {
2998 printf("%s\n", *range);
2999 if(sscanf(*range, "%u:%u", &low, &high) == 1)
3000 uids = g_slist_prepend(uids, GINT_TO_POINTER(low));
3003 for (i = low; i <= high; i++)
3004 uids = g_slist_prepend(uids, GINT_TO_POINTER(i));
3007 uids = g_slist_reverse(uids);
3013 static gint imap_cmd_copy(IMAPSession *session, const gchar *seq_set,
3014 const gchar *destfolder, GRelation *uid_mapping)
3019 g_return_val_if_fail(session != NULL, IMAP_ERROR);
3020 g_return_val_if_fail(seq_set != NULL, IMAP_ERROR);
3021 g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
3023 QUOTE_IF_REQUIRED(destfolder_, destfolder);
3024 imap_gen_send(session, "UID COPY %s %s", seq_set, destfolder_);
3026 if (uid_mapping != NULL && session->uidplus) {
3028 gchar *resp_str = NULL, *olduids_str, *newuids_str;
3029 MsgNumberList *olduids, *old_cur, *newuids, *new_cur;
3031 reply = g_ptr_array_new();
3032 ok = imap_cmd_ok(session, reply);
3033 if ((ok == IMAP_SUCCESS) && (reply->len > 0)) {
3034 resp_str = g_ptr_array_index(reply, reply->len - 1);
3036 olduids_str = g_new0(gchar, strlen(resp_str));
3037 newuids_str = g_new0(gchar, strlen(resp_str));
3038 if (sscanf(resp_str, "%*s OK [COPYUID %*u %[0-9,:] %[0-9,:]]",
3039 olduids_str, newuids_str) == 2) {
3040 olduids = imapset_to_numlist(olduids_str);
3041 newuids = imapset_to_numlist(newuids_str);
3045 while(old_cur != NULL && new_cur != NULL) {
3046 g_relation_insert(uid_mapping,
3047 GPOINTER_TO_INT(old_cur->data),
3048 GPOINTER_TO_INT(new_cur->data));
3049 old_cur = g_slist_next(old_cur);
3050 new_cur = g_slist_next(new_cur);
3053 g_slist_free(olduids);
3054 g_slist_free(newuids);
3056 g_free(olduids_str);
3057 g_free(newuids_str);
3060 ptr_array_free_strings(reply);
3061 g_ptr_array_free(reply, TRUE);
3063 ok = imap_cmd_ok(session, NULL);
3065 if (ok != IMAP_SUCCESS)
3066 log_warning(_("can't copy %s to %s\n"), seq_set, destfolder_);
3071 gint imap_cmd_envelope(IMAPSession *session, IMAPSet set)
3073 static GString *header_fields = NULL;
3075 if (header_fields == NULL) {
3076 const HeaderEntry *headers, *elem;
3078 headers = procheader_get_headernames(FALSE);
3079 header_fields = g_string_new("");
3081 for (elem = headers; elem->name != NULL; ++elem) {
3082 gint namelen = strlen(elem->name);
3084 /* Header fields ending with space are not rfc822 headers */
3085 if (elem->name[namelen - 1] == ' ')
3088 /* strip : at the of header field */
3089 if(elem->name[namelen - 1] == ':')
3095 g_string_sprintfa(header_fields, "%s%.*s",
3096 header_fields->str[0] != '\0' ? " " : "",
3097 namelen, elem->name);
3102 (session, "UID FETCH %s (UID FLAGS RFC822.SIZE BODY.PEEK[HEADER.FIELDS (%s)])",
3103 set, header_fields->str);
3105 return IMAP_SUCCESS;
3108 static gint imap_cmd_store(IMAPSession *session, IMAPSet seq_set,
3113 imap_gen_send(session, "UID STORE %s %s", seq_set, sub_cmd);
3115 if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS) {
3116 log_warning(_("error while imap command: STORE %s %s\n"),
3121 return IMAP_SUCCESS;
3124 static gint imap_cmd_expunge(IMAPSession *session, IMAPSet seq_set)
3128 if (seq_set && session->uidplus)
3129 imap_gen_send(session, "UID EXPUNGE %s", seq_set);
3131 imap_gen_send(session, "EXPUNGE");
3132 if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS) {
3133 log_warning(_("error while imap command: EXPUNGE\n"));
3137 return IMAP_SUCCESS;
3140 static gint imap_cmd_close(IMAPSession *session)
3144 imap_gen_send(session, "CLOSE");
3145 if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS)
3146 log_warning(_("error while imap command: CLOSE\n"));
3151 static gint imap_cmd_ok(IMAPSession *session, GPtrArray *argbuf)
3153 gint ok = IMAP_SUCCESS;
3158 while ((ok = imap_gen_recv(session, &buf))
3160 // make sure data is long enough for any substring of buf
3161 data = alloca(strlen(buf) + 1);
3163 // untagged line read
3164 if (buf[0] == '*' && buf[1] == ' ') {
3167 g_ptr_array_add(argbuf, g_strdup(buf + 2));
3169 if (sscanf(buf + 2, "%d %s", &num, data) >= 2) {
3170 if (!strcmp(data, "EXISTS")) {
3171 session->exists = num;
3172 session->folder_content_changed = TRUE;
3175 if(!strcmp(data, "EXPUNGE")) {
3177 session->folder_content_changed = TRUE;
3180 // tagged line with correct tag and OK response found
3181 } else if ((sscanf(buf, "%d %s", &cmd_num, data) >= 2) &&
3182 (cmd_num == session->cmd_count) &&
3183 !strcmp(data, "OK")) {
3185 g_ptr_array_add(argbuf, g_strdup(buf));
3199 static void imap_gen_send(IMAPSession *session, const gchar *format, ...)
3206 va_start(args, format);
3207 tmp = g_strdup_vprintf(format, args);
3210 session->cmd_count++;
3212 buf = g_strdup_printf("%d %s\r\n", session->cmd_count, tmp);
3213 if (!strncasecmp(tmp, "LOGIN ", 6) && (p = strchr(tmp + 6, ' '))) {
3215 log_print("IMAP4> %d %s ********\n", session->cmd_count, tmp);
3217 log_print("IMAP4> %d %s\n", session->cmd_count, tmp);
3219 sock_write_all(SESSION(session)->sock, buf, strlen(buf));
3224 static gint imap_gen_recv(IMAPSession *session, gchar **ret)
3226 if ((*ret = sock_getline(SESSION(session)->sock)) == NULL)
3231 log_print("IMAP4< %s\n", *ret);
3233 return IMAP_SUCCESS;
3237 /* misc utility functions */
3239 static gchar *strchr_cpy(const gchar *src, gchar ch, gchar *dest, gint len)
3244 tmp = strchr(src, ch);
3248 memcpy(dest, src, MIN(tmp - src, len - 1));
3249 dest[MIN(tmp - src, len - 1)] = '\0';
3254 static gchar *get_quoted(const gchar *src, gchar ch, gchar *dest, gint len)
3256 const gchar *p = src;
3259 g_return_val_if_fail(*p == ch, NULL);
3264 while (*p != '\0' && *p != ch) {
3266 if (*p == '\\' && *(p + 1) != '\0')
3275 return (gchar *)(*p == ch ? p + 1 : p);
3278 static gchar *search_array_contain_str(GPtrArray *array, const gchar *str)
3282 for (i = 0; i < array->len; i++) {
3285 tmp = g_ptr_array_index(array, i);
3286 if (strstr(tmp, str) != NULL)
3293 static gchar *search_array_str(GPtrArray *array, const gchar *str)
3300 for (i = 0; i < array->len; i++) {
3303 tmp = g_ptr_array_index(array, i);
3304 if (!strncmp(tmp, str, len))
3311 static void imap_path_separator_subst(gchar *str, gchar separator)
3314 gboolean in_escape = FALSE;
3316 if (!separator || separator == '/') return;
3318 for (p = str; *p != '\0'; p++) {
3319 if (*p == '/' && !in_escape)
3321 else if (*p == '&' && *(p + 1) != '-' && !in_escape)
3323 else if (*p == '-' && in_escape)
3328 static gchar *imap_modified_utf7_to_locale(const gchar *mutf7_str)
3331 const gchar *from_p;
3334 to = g_malloc(strlen(mutf7_str) + 1);
3337 for (from_p = mutf7_str; *from_p != '\0'; from_p++) {
3338 if (*from_p == '&' && *(from_p + 1) == '-') {
3348 static iconv_t cd = (iconv_t)-1;
3349 static gboolean iconv_ok = TRUE;
3352 size_t norm_utf7_len;
3354 gchar *to_str, *to_p;
3356 gboolean in_escape = FALSE;
3358 if (!iconv_ok) return g_strdup(mutf7_str);
3360 if (cd == (iconv_t)-1) {
3361 cd = iconv_open(conv_get_current_charset_str(), "UTF-7");
3362 if (cd == (iconv_t)-1) {
3363 g_warning("iconv cannot convert UTF-7 to %s\n",
3364 conv_get_current_charset_str());
3366 return g_strdup(mutf7_str);
3370 norm_utf7 = g_string_new(NULL);
3372 for (p = mutf7_str; *p != '\0'; p++) {
3373 /* replace: '&' -> '+',
3375 escaped ',' -> '/' */
3376 if (!in_escape && *p == '&') {
3377 if (*(p + 1) != '-') {
3378 g_string_append_c(norm_utf7, '+');
3381 g_string_append_c(norm_utf7, '&');
3384 } else if (in_escape && *p == ',') {
3385 g_string_append_c(norm_utf7, '/');
3386 } else if (in_escape && *p == '-') {
3387 g_string_append_c(norm_utf7, '-');
3390 g_string_append_c(norm_utf7, *p);
3394 norm_utf7_p = norm_utf7->str;
3395 norm_utf7_len = norm_utf7->len;
3396 to_len = strlen(mutf7_str) * 5;
3397 to_p = to_str = g_malloc(to_len + 1);
3399 if (iconv(cd, (ICONV_CONST gchar **)&norm_utf7_p, &norm_utf7_len,
3400 &to_p, &to_len) == -1) {
3401 g_warning(_("iconv cannot convert UTF-7 to %s\n"),
3402 conv_get_current_charset_str());
3403 g_string_free(norm_utf7, TRUE);
3405 return g_strdup(mutf7_str);
3408 /* second iconv() call for flushing */
3409 iconv(cd, NULL, NULL, &to_p, &to_len);
3410 g_string_free(norm_utf7, TRUE);
3414 #endif /* !HAVE_ICONV */
3417 static gchar *imap_locale_to_modified_utf7(const gchar *from)
3420 const gchar *from_p;
3423 to = g_malloc(strlen(from) * 2 + 1);
3426 for (from_p = from; *from_p != '\0'; from_p++) {
3427 if (*from_p == '&') {
3437 static iconv_t cd = (iconv_t)-1;
3438 static gboolean iconv_ok = TRUE;
3439 gchar *norm_utf7, *norm_utf7_p;
3440 size_t from_len, norm_utf7_len;
3442 gchar *from_tmp, *to, *p;
3443 gboolean in_escape = FALSE;
3445 if (!iconv_ok) return g_strdup(from);
3447 if (cd == (iconv_t)-1) {
3448 cd = iconv_open("UTF-7", conv_get_current_charset_str());
3449 if (cd == (iconv_t)-1) {
3450 g_warning("iconv cannot convert %s to UTF-7\n",
3451 conv_get_current_charset_str());
3453 return g_strdup(from);
3457 Xstrdup_a(from_tmp, from, return g_strdup(from));
3458 from_len = strlen(from);
3459 norm_utf7_len = from_len * 5;
3460 Xalloca(norm_utf7, norm_utf7_len + 1, return g_strdup(from));
3461 norm_utf7_p = norm_utf7;
3463 #define IS_PRINT(ch) (isprint(ch) && isascii(ch))
3465 while (from_len > 0) {
3466 if (*from_tmp == '+') {
3467 *norm_utf7_p++ = '+';
3468 *norm_utf7_p++ = '-';
3472 } else if (IS_PRINT(*from_tmp)) {
3473 /* printable ascii char */
3474 *norm_utf7_p = *from_tmp;
3480 size_t mb_len = 0, conv_len = 0;
3482 /* unprintable char: convert to UTF-7 */
3484 while (!IS_PRINT(*p) && conv_len < from_len) {
3485 mb_len = mblen(p, MB_LEN_MAX);
3487 g_warning("wrong multibyte sequence\n");
3488 return g_strdup(from);
3494 from_len -= conv_len;
3495 if (iconv(cd, (ICONV_CONST gchar **)&from_tmp,
3497 &norm_utf7_p, &norm_utf7_len) == -1) {
3498 g_warning("iconv cannot convert %s to UTF-7\n",
3499 conv_get_current_charset_str());
3500 return g_strdup(from);
3503 /* second iconv() call for flushing */
3504 iconv(cd, NULL, NULL, &norm_utf7_p, &norm_utf7_len);
3510 *norm_utf7_p = '\0';
3511 to_str = g_string_new(NULL);
3512 for (p = norm_utf7; p < norm_utf7_p; p++) {
3513 /* replace: '&' -> "&-",
3516 BASE64 '/' -> ',' */
3517 if (!in_escape && *p == '&') {
3518 g_string_append(to_str, "&-");
3519 } else if (!in_escape && *p == '+') {
3520 if (*(p + 1) == '-') {
3521 g_string_append_c(to_str, '+');
3524 g_string_append_c(to_str, '&');
3527 } else if (in_escape && *p == '/') {
3528 g_string_append_c(to_str, ',');
3529 } else if (in_escape && *p == '-') {
3530 g_string_append_c(to_str, '-');
3533 g_string_append_c(to_str, *p);
3539 g_string_append_c(to_str, '-');
3543 g_string_free(to_str, FALSE);
3546 #endif /* !HAVE_ICONV */
3549 static GSList *imap_get_seq_set_from_numlist(MsgNumberList *numlist)
3552 GSList *sorted_list, *cur;
3553 guint first, last, next;
3555 GSList *ret_list = NULL;
3557 if (numlist == NULL)
3560 str = g_string_sized_new(256);
3562 sorted_list = g_slist_copy(numlist);
3563 sorted_list = g_slist_sort(sorted_list, g_int_compare);
3565 first = GPOINTER_TO_INT(sorted_list->data);
3567 for (cur = sorted_list; cur != NULL; cur = g_slist_next(cur)) {
3568 last = GPOINTER_TO_INT(cur->data);
3570 next = GPOINTER_TO_INT(cur->next->data);
3574 if (last + 1 != next || next == 0) {
3576 g_string_append_c(str, ',');
3578 g_string_sprintfa(str, "%u", first);
3580 g_string_sprintfa(str, "%u:%u", first, last);
3584 if (str->len > IMAP_CMD_LIMIT) {
3585 ret_str = g_strdup(str->str);
3586 ret_list = g_slist_append(ret_list, ret_str);
3587 g_string_truncate(str, 0);
3593 ret_str = g_strdup(str->str);
3594 ret_list = g_slist_append(ret_list, ret_str);
3597 g_slist_free(sorted_list);
3598 g_string_free(str, TRUE);
3603 static GSList *imap_get_seq_set_from_msglist(MsgInfoList *msglist)
3605 MsgNumberList *numlist = NULL;
3609 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
3610 MsgInfo *msginfo = (MsgInfo *) cur->data;
3612 numlist = g_slist_append(numlist, GINT_TO_POINTER(msginfo->msgnum));
3614 seq_list = imap_get_seq_set_from_numlist(numlist);
3615 g_slist_free(numlist);
3620 static void imap_seq_set_free(GSList *seq_list)
3622 slist_free_strings(seq_list);
3623 g_slist_free(seq_list);
3627 static gboolean imap_rename_folder_func(GNode *node, gpointer data)
3629 FolderItem *item = node->data;
3630 gchar **paths = data;
3631 const gchar *oldpath = paths[0];
3632 const gchar *newpath = paths[1];
3634 gchar *new_itempath;
3637 oldpathlen = strlen(oldpath);
3638 if (strncmp(oldpath, item->path, oldpathlen) != 0) {
3639 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
3643 base = item->path + oldpathlen;
3644 while (*base == G_DIR_SEPARATOR) base++;
3646 new_itempath = g_strdup(newpath);
3648 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
3651 item->path = new_itempath;
3656 static gint get_list_of_uids(Folder *folder, IMAPFolderItem *item, GSList **msgnum_list)
3658 gint ok, nummsgs = 0, lastuid_old;
3659 IMAPSession *session;
3660 GSList *uidlist, *elem;
3663 session = imap_session_get(folder);
3664 g_return_val_if_fail(session != NULL, -1);
3666 ok = imap_select(session, IMAP_FOLDER(folder), item->item.path,
3667 NULL, NULL, NULL, NULL);
3668 if (ok != IMAP_SUCCESS)
3671 cmd_buf = g_strdup_printf("UID %d:*", item->lastuid + 1);
3672 ok = imap_cmd_search(session, cmd_buf, &uidlist);
3675 if (ok == IMAP_SOCKET) {
3676 session_destroy((Session *)session);
3677 ((RemoteFolder *)folder)->session = NULL;
3681 if (ok != IMAP_SUCCESS) {
3685 argbuf = g_ptr_array_new();
3687 cmd_buf = g_strdup_printf("UID FETCH %d:* (UID)", item->lastuid + 1);
3688 imap_gen_send(session, cmd_buf);
3690 ok = imap_cmd_ok(session, argbuf);
3691 if (ok != IMAP_SUCCESS) {
3692 ptr_array_free_strings(argbuf);
3693 g_ptr_array_free(argbuf, TRUE);
3697 for(i = 0; i < argbuf->len; i++) {
3700 if((ret = sscanf(g_ptr_array_index(argbuf, i),
3701 "%*d FETCH (UID %d)", &msgnum)) == 1)
3702 uidlist = g_slist_prepend(uidlist, GINT_TO_POINTER(msgnum));
3704 ptr_array_free_strings(argbuf);
3705 g_ptr_array_free(argbuf, TRUE);
3708 lastuid_old = item->lastuid;
3709 *msgnum_list = g_slist_copy(item->uid_list);
3710 nummsgs = g_slist_length(*msgnum_list);
3711 debug_print("Got %d uids from cache\n", g_slist_length(item->uid_list));
3713 for (elem = uidlist; elem != NULL; elem = g_slist_next(elem)) {
3716 msgnum = GPOINTER_TO_INT(elem->data);
3717 if (msgnum > lastuid_old) {
3718 *msgnum_list = g_slist_prepend(*msgnum_list, GINT_TO_POINTER(msgnum));
3719 item->uid_list = g_slist_prepend(item->uid_list, GINT_TO_POINTER(msgnum));
3722 if(msgnum > item->lastuid)
3723 item->lastuid = msgnum;
3726 g_slist_free(uidlist);
3731 gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list, gboolean *old_uids_valid)
3733 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3734 IMAPSession *session;
3735 gint ok, nummsgs = 0, exists, recent, uid_val, uid_next, unseen;
3738 gboolean selected_folder;
3740 g_return_val_if_fail(folder != NULL, -1);
3741 g_return_val_if_fail(item != NULL, -1);
3742 g_return_val_if_fail(item->item.path != NULL, -1);
3743 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3744 g_return_val_if_fail(folder->account != NULL, -1);
3746 session = imap_session_get(folder);
3747 g_return_val_if_fail(session != NULL, -1);
3749 selected_folder = (session->mbox != NULL) &&
3750 (!strcmp(session->mbox, item->item.path));
3751 if (selected_folder) {
3752 ok = imap_cmd_noop(session);
3753 if (ok != IMAP_SUCCESS)
3755 exists = session->exists;
3757 *old_uids_valid = TRUE;
3759 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
3760 &exists, &recent, &uid_next, &uid_val, &unseen);
3761 if (ok != IMAP_SUCCESS)
3764 if(item->item.mtime == uid_val)
3765 *old_uids_valid = TRUE;
3767 *old_uids_valid = FALSE;
3769 debug_print("Freeing imap uid cache\n");
3771 g_slist_free(item->uid_list);
3772 item->uid_list = NULL;
3774 item->item.mtime = uid_val;
3776 imap_delete_all_cached_messages((FolderItem *)item);
3780 if (!selected_folder)
3781 item->uid_next = uid_next;
3783 /* If old uid_next matches new uid_next we can be sure no message
3784 was added to the folder */
3785 if (( selected_folder && !session->folder_content_changed) ||
3786 (!selected_folder && uid_next == item->uid_next)) {
3787 nummsgs = g_slist_length(item->uid_list);
3789 /* If number of messages is still the same we
3790 know our caches message numbers are still valid,
3791 otherwise if the number of messages has decrease
3792 we discard our cache to start a new scan to find
3793 out which numbers have been removed */
3794 if (exists == nummsgs) {
3795 *msgnum_list = g_slist_copy(item->uid_list);
3797 } else if (exists < nummsgs) {
3798 debug_print("Freeing imap uid cache");
3800 g_slist_free(item->uid_list);
3801 item->uid_list = NULL;
3806 *msgnum_list = NULL;
3810 nummsgs = get_list_of_uids(folder, item, &uidlist);
3812 if (nummsgs != exists) {
3813 /* Cache contains more messages then folder, we have cached
3814 an old UID of a message that was removed and new messages
3815 have been added too, otherwise the uid_next check would
3817 debug_print("Freeing imap uid cache");
3819 g_slist_free(item->uid_list);
3820 item->uid_list = NULL;
3822 g_slist_free(*msgnum_list);
3824 nummsgs = get_list_of_uids(folder, item, &uidlist);
3827 *msgnum_list = uidlist;
3829 dir = folder_item_get_path((FolderItem *)item);
3830 debug_print("removing old messages from %s\n", dir);
3831 remove_numbered_files_not_in_list(dir, *msgnum_list);
3837 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item)
3842 flags.perm_flags = MSG_NEW|MSG_UNREAD;
3843 flags.tmp_flags = 0;
3845 g_return_val_if_fail(item != NULL, NULL);
3846 g_return_val_if_fail(file != NULL, NULL);
3848 if (item->stype == F_QUEUE) {
3849 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
3850 } else if (item->stype == F_DRAFT) {
3851 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
3854 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3855 if (!msginfo) return NULL;
3857 msginfo->folder = item;
3862 GSList *imap_get_msginfos(Folder *folder, FolderItem *item, GSList *msgnum_list)
3864 IMAPSession *session;
3865 MsgInfoList *ret = NULL;
3868 g_return_val_if_fail(folder != NULL, NULL);
3869 g_return_val_if_fail(item != NULL, NULL);
3870 g_return_val_if_fail(msgnum_list != NULL, NULL);
3872 session = imap_session_get(folder);
3873 g_return_val_if_fail(session != NULL, NULL);
3875 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3876 NULL, NULL, NULL, NULL);
3877 if (ok != IMAP_SUCCESS)
3880 if (!(item->stype == F_QUEUE || item->stype == F_DRAFT)) {
3881 ret = g_slist_concat(ret,
3882 imap_get_uncached_messages(
3883 session, item, msgnum_list));
3885 MsgNumberList *sorted_list, *elem;
3886 gint startnum, lastnum;
3888 sorted_list = g_slist_sort(g_slist_copy(msgnum_list), g_int_compare);
3890 startnum = lastnum = GPOINTER_TO_INT(sorted_list->data);
3892 for (elem = sorted_list;; elem = g_slist_next(elem)) {
3896 num = GPOINTER_TO_INT(elem->data);
3898 if (num > lastnum + 1 || elem == NULL) {
3900 for (i = startnum; i <= lastnum; ++i) {
3903 file = imap_fetch_msg(folder, item, i);
3905 MsgInfo *msginfo = imap_parse_msg(file, item);
3906 if (msginfo != NULL) {
3907 msginfo->msgnum = i;
3908 ret = g_slist_append(ret, msginfo);
3922 g_slist_free(sorted_list);
3928 MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid)
3930 MsgInfo *msginfo = NULL;
3931 MsgInfoList *msginfolist;
3932 MsgNumberList numlist;
3934 numlist.next = NULL;
3935 numlist.data = GINT_TO_POINTER(uid);
3937 msginfolist = imap_get_msginfos(folder, item, &numlist);
3938 if (msginfolist != NULL) {
3939 msginfo = msginfolist->data;
3940 g_slist_free(msginfolist);
3946 gboolean imap_scan_required(Folder *folder, FolderItem *_item)
3948 IMAPSession *session;
3949 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3950 gint ok, exists = 0, recent = 0, unseen = 0;
3951 guint32 uid_next, uid_val = 0;
3952 gboolean selected_folder;
3954 g_return_val_if_fail(folder != NULL, FALSE);
3955 g_return_val_if_fail(item != NULL, FALSE);
3956 g_return_val_if_fail(item->item.folder != NULL, FALSE);
3957 g_return_val_if_fail(FOLDER_CLASS(item->item.folder) == &imap_class, FALSE);
3959 if (item->item.path == NULL)
3962 session = imap_session_get(folder);
3963 g_return_val_if_fail(session != NULL, FALSE);
3965 selected_folder = (session->mbox != NULL) &&
3966 (!strcmp(session->mbox, item->item.path));
3967 if (selected_folder) {
3968 ok = imap_cmd_noop(session);
3969 if (ok != IMAP_SUCCESS)
3972 if (session->folder_content_changed)
3975 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
3976 &exists, &recent, &uid_next, &uid_val, &unseen);
3977 if (ok != IMAP_SUCCESS)
3980 if ((uid_next != item->uid_next) || (exists < item->item.total_msgs))
3987 void imap_change_flags(Folder *folder, FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
3989 IMAPSession *session;
3990 IMAPFlags flags_set = 0, flags_unset = 0;
3991 gint ok = IMAP_SUCCESS;
3992 MsgNumberList numlist;
3994 g_return_if_fail(folder != NULL);
3995 g_return_if_fail(folder->klass == &imap_class);
3996 g_return_if_fail(item != NULL);
3997 g_return_if_fail(item->folder == folder);
3998 g_return_if_fail(msginfo != NULL);
3999 g_return_if_fail(msginfo->folder == item);
4001 session = imap_session_get(folder);
4002 if (!session) return;
4004 if ((ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
4005 NULL, NULL, NULL, NULL)) != IMAP_SUCCESS)
4008 if (!MSG_IS_MARKED(msginfo->flags) && (newflags & MSG_MARKED))
4009 flags_set |= IMAP_FLAG_FLAGGED;
4010 if ( MSG_IS_MARKED(msginfo->flags) && !(newflags & MSG_MARKED))
4011 flags_unset |= IMAP_FLAG_FLAGGED;
4013 if (!MSG_IS_UNREAD(msginfo->flags) && (newflags & MSG_UNREAD))
4014 flags_unset |= IMAP_FLAG_SEEN;
4015 if ( MSG_IS_UNREAD(msginfo->flags) && !(newflags & MSG_UNREAD))
4016 flags_set |= IMAP_FLAG_SEEN;
4018 if (!MSG_IS_REPLIED(msginfo->flags) && (newflags & MSG_REPLIED))
4019 flags_set |= IMAP_FLAG_ANSWERED;
4020 if ( MSG_IS_REPLIED(msginfo->flags) && !(newflags & MSG_REPLIED))
4021 flags_set |= IMAP_FLAG_ANSWERED;
4023 numlist.next = NULL;
4024 numlist.data = GINT_TO_POINTER(msginfo->msgnum);
4027 ok = imap_set_message_flags(session, &numlist, flags_set, TRUE);
4028 if (ok != IMAP_SUCCESS) return;
4032 ok = imap_set_message_flags(session, &numlist, flags_unset, FALSE);
4033 if (ok != IMAP_SUCCESS) return;
4036 msginfo->flags.perm_flags = newflags;