2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2004 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 "prefs_common.h"
56 #include "inputdialog.h"
58 #include "remotefolder.h"
59 #include "alertpanel.h"
61 typedef struct _IMAPFolder IMAPFolder;
62 typedef struct _IMAPSession IMAPSession;
63 typedef struct _IMAPNameSpace IMAPNameSpace;
64 typedef struct _IMAPFolderItem IMAPFolderItem;
66 #include "prefs_account.h"
68 #define IMAP_FOLDER(obj) ((IMAPFolder *)obj)
69 #define IMAP_FOLDER_ITEM(obj) ((IMAPFolderItem *)obj)
70 #define IMAP_SESSION(obj) ((IMAPSession *)obj)
76 /* list of IMAPNameSpace */
86 gboolean authenticated;
95 gboolean folder_content_changed;
105 #define IMAP_SUCCESS 0
106 #define IMAP_SOCKET 2
107 #define IMAP_AUTHFAIL 3
108 #define IMAP_PROTOCOL 4
109 #define IMAP_SYNTAX 5
113 #define IMAPBUFSIZE 8192
117 IMAP_FLAG_SEEN = 1 << 0,
118 IMAP_FLAG_ANSWERED = 1 << 1,
119 IMAP_FLAG_FLAGGED = 1 << 2,
120 IMAP_FLAG_DELETED = 1 << 3,
121 IMAP_FLAG_DRAFT = 1 << 4
124 #define IMAP_IS_SEEN(flags) ((flags & IMAP_FLAG_SEEN) != 0)
125 #define IMAP_IS_ANSWERED(flags) ((flags & IMAP_FLAG_ANSWERED) != 0)
126 #define IMAP_IS_FLAGGED(flags) ((flags & IMAP_FLAG_FLAGGED) != 0)
127 #define IMAP_IS_DELETED(flags) ((flags & IMAP_FLAG_DELETED) != 0)
128 #define IMAP_IS_DRAFT(flags) ((flags & IMAP_FLAG_DRAFT) != 0)
131 #define IMAP4_PORT 143
133 #define IMAPS_PORT 993
136 #define IMAP_CMD_LIMIT 1000
138 #define QUOTE_IF_REQUIRED(out, str) \
140 if (*str != '"' && strpbrk(str, " \t(){}%*") != NULL) { \
144 len = strlen(str) + 3; \
145 Xalloca(__tmp, len, return IMAP_ERROR); \
146 g_snprintf(__tmp, len, "\"%s\"", str); \
149 Xstrdup_a(out, str, return IMAP_ERROR); \
153 typedef gchar * IMAPSet;
155 struct _IMAPFolderItem
164 static void imap_folder_init (Folder *folder,
168 static Folder *imap_folder_new (const gchar *name,
170 static void imap_folder_destroy (Folder *folder);
172 static IMAPSession *imap_session_new (const PrefsAccount *account);
173 static void imap_session_authenticate(IMAPSession *session,
174 const PrefsAccount *account);
175 static void imap_session_destroy (Session *session);
177 static gchar *imap_fetch_msg (Folder *folder,
180 static gint imap_add_msg (Folder *folder,
184 static gint imap_add_msgs (Folder *folder,
187 GRelation *relation);
189 static gint imap_copy_msg (Folder *folder,
192 static gint imap_copy_msgs (Folder *folder,
194 MsgInfoList *msglist,
195 GRelation *relation);
197 static gint imap_remove_msg (Folder *folder,
200 static gint imap_remove_all_msg (Folder *folder,
203 static gboolean imap_is_msg_changed (Folder *folder,
207 static gint imap_close (Folder *folder,
210 static gint imap_scan_tree (Folder *folder);
212 static gint imap_create_tree (Folder *folder);
214 static FolderItem *imap_create_folder (Folder *folder,
217 static gint imap_rename_folder (Folder *folder,
220 static gint imap_remove_folder (Folder *folder,
223 static FolderItem *imap_folder_item_new (Folder *folder);
224 static void imap_folder_item_destroy (Folder *folder,
227 static IMAPSession *imap_session_get (Folder *folder);
229 static gint imap_greeting (IMAPSession *session);
230 static gint imap_auth (IMAPSession *session,
235 static gint imap_scan_tree_recursive (IMAPSession *session,
237 static GSList *imap_parse_list (IMAPFolder *folder,
238 IMAPSession *session,
239 const gchar *real_path,
242 static void imap_create_missing_folders (Folder *folder);
243 static FolderItem *imap_create_special_folder
245 SpecialFolderItemType stype,
248 static gint imap_do_copy_msgs (Folder *folder,
250 MsgInfoList *msglist,
251 GRelation *relation);
253 static void imap_delete_all_cached_messages (FolderItem *item);
256 static SockInfo *imap_open (const gchar *server,
260 static SockInfo *imap_open (const gchar *server,
265 static SockInfo *imap_open_tunnel(const gchar *server,
266 const gchar *tunnelcmd,
269 static SockInfo *imap_open_tunnel(const gchar *server,
270 const gchar *tunnelcmd);
274 static SockInfo *imap_init_sock(SockInfo *sock, SSLType ssl_type);
276 static SockInfo *imap_init_sock(SockInfo *sock);
279 static gchar *imap_get_flag_str (IMAPFlags flags);
280 static gint imap_set_message_flags (IMAPSession *session,
281 MsgNumberList *numlist,
284 static gint imap_select (IMAPSession *session,
290 guint32 *uid_validity);
291 static gint imap_status (IMAPSession *session,
297 guint32 *uid_validity,
300 static void imap_parse_namespace (IMAPSession *session,
302 static void imap_get_namespace_by_list (IMAPSession *session,
304 static IMAPNameSpace *imap_find_namespace (IMAPFolder *folder,
306 static gchar imap_get_path_separator (IMAPFolder *folder,
308 static gchar *imap_get_real_path (IMAPFolder *folder,
311 static gchar *imap_parse_atom (SockInfo *sock,
316 static MsgFlags imap_parse_flags (const gchar *flag_str);
317 static MsgInfo *imap_parse_envelope (SockInfo *sock,
321 static gboolean imap_has_capability (IMAPSession *session,
323 static void imap_free_capabilities (IMAPSession *session);
325 /* low-level IMAP4rev1 commands */
326 static gint imap_cmd_authenticate
327 (IMAPSession *session,
331 static gint imap_cmd_login (IMAPSession *session,
334 static gint imap_cmd_logout (IMAPSession *session);
335 static gint imap_cmd_noop (IMAPSession *session);
336 static gint imap_cmd_starttls (IMAPSession *session);
337 static gint imap_cmd_namespace (IMAPSession *session,
339 static gint imap_cmd_list (IMAPSession *session,
341 const gchar *mailbox,
343 static gint imap_cmd_do_select (IMAPSession *session,
349 guint32 *uid_validity);
350 static gint imap_cmd_select (IMAPSession *session,
355 guint32 *uid_validity);
356 static gint imap_cmd_examine (IMAPSession *session,
361 guint32 *uid_validity);
362 static gint imap_cmd_create (IMAPSession *sock,
363 const gchar *folder);
364 static gint imap_cmd_rename (IMAPSession *sock,
365 const gchar *oldfolder,
366 const gchar *newfolder);
367 static gint imap_cmd_delete (IMAPSession *session,
368 const gchar *folder);
369 static gint imap_cmd_envelope (IMAPSession *session,
371 static gint imap_cmd_fetch (IMAPSession *sock,
373 const gchar *filename);
374 static gint imap_cmd_append (IMAPSession *session,
375 const gchar *destfolder,
379 static gint imap_cmd_copy (IMAPSession *session,
380 const gchar *seq_set,
381 const gchar *destfolder,
382 GRelation *uid_mapping);
383 static gint imap_cmd_store (IMAPSession *session,
386 static gint imap_cmd_expunge (IMAPSession *session,
388 static gint imap_cmd_close (IMAPSession *session);
390 static gint imap_cmd_ok (IMAPSession *session,
392 static void imap_gen_send (IMAPSession *session,
393 const gchar *format, ...);
394 static gint imap_gen_recv (IMAPSession *session,
397 /* misc utility functions */
398 static gchar *strchr_cpy (const gchar *src,
402 static gchar *get_quoted (const gchar *src,
406 static gchar *search_array_contain_str (GPtrArray *array,
408 static gchar *search_array_str (GPtrArray *array,
410 static void imap_path_separator_subst (gchar *str,
413 static gchar *imap_utf8_to_modified_utf7 (const gchar *from);
414 static gchar *imap_modified_utf7_to_utf8 (const gchar *mutf7_str);
416 static GSList *imap_get_seq_set_from_numlist (MsgNumberList *msglist);
417 static GSList *imap_get_seq_set_from_msglist (MsgInfoList *msglist);
418 static void imap_seq_set_free (GSList *seq_list);
420 static gboolean imap_rename_folder_func (GNode *node,
422 static gint imap_get_num_list (Folder *folder,
425 gboolean *old_uids_valid);
426 static GSList *imap_get_msginfos (Folder *folder,
428 GSList *msgnum_list);
429 static MsgInfo *imap_get_msginfo (Folder *folder,
432 static gboolean imap_scan_required (Folder *folder,
434 static void imap_change_flags (Folder *folder,
437 MsgPermFlags newflags);
438 static gint imap_get_flags (Folder *folder,
440 MsgInfoList *msglist,
441 GRelation *msgflags);
442 static gchar *imap_folder_get_path (Folder *folder);
443 static gchar *imap_item_get_path (Folder *folder,
446 static FolderClass imap_class;
448 FolderClass *imap_get_class(void)
450 if (imap_class.idstr == NULL) {
451 imap_class.type = F_IMAP;
452 imap_class.idstr = "imap";
453 imap_class.uistr = "IMAP4";
455 /* Folder functions */
456 imap_class.new_folder = imap_folder_new;
457 imap_class.destroy_folder = imap_folder_destroy;
458 imap_class.scan_tree = imap_scan_tree;
459 imap_class.create_tree = imap_create_tree;
461 /* FolderItem functions */
462 imap_class.item_new = imap_folder_item_new;
463 imap_class.item_destroy = imap_folder_item_destroy;
464 imap_class.item_get_path = imap_item_get_path;
465 imap_class.create_folder = imap_create_folder;
466 imap_class.rename_folder = imap_rename_folder;
467 imap_class.remove_folder = imap_remove_folder;
468 imap_class.close = imap_close;
469 imap_class.get_num_list = imap_get_num_list;
470 imap_class.scan_required = imap_scan_required;
472 /* Message functions */
473 imap_class.get_msginfo = imap_get_msginfo;
474 imap_class.get_msginfos = imap_get_msginfos;
475 imap_class.fetch_msg = imap_fetch_msg;
476 imap_class.add_msg = imap_add_msg;
477 imap_class.add_msgs = imap_add_msgs;
478 imap_class.copy_msg = imap_copy_msg;
479 imap_class.copy_msgs = imap_copy_msgs;
480 imap_class.remove_msg = imap_remove_msg;
481 imap_class.remove_all_msg = imap_remove_all_msg;
482 imap_class.is_msg_changed = imap_is_msg_changed;
483 imap_class.change_flags = imap_change_flags;
484 imap_class.get_flags = imap_get_flags;
490 static Folder *imap_folder_new(const gchar *name, const gchar *path)
494 folder = (Folder *)g_new0(IMAPFolder, 1);
495 folder->klass = &imap_class;
496 imap_folder_init(folder, name, path);
501 static void imap_folder_destroy(Folder *folder)
505 dir = imap_folder_get_path(folder);
506 if (is_dir_exist(dir))
507 remove_dir_recursive(dir);
510 folder_remote_folder_destroy(REMOTE_FOLDER(folder));
513 static void imap_folder_init(Folder *folder, const gchar *name,
516 folder_remote_folder_init((Folder *)folder, name, path);
519 static FolderItem *imap_folder_item_new(Folder *folder)
521 IMAPFolderItem *item;
523 item = g_new0(IMAPFolderItem, 1);
526 item->uid_list = NULL;
528 return (FolderItem *)item;
531 static void imap_folder_item_destroy(Folder *folder, FolderItem *_item)
533 IMAPFolderItem *item = (IMAPFolderItem *)_item;
535 g_return_if_fail(item != NULL);
536 g_slist_free(item->uid_list);
541 static gboolean imap_reset_uid_lists_func(GNode *node, gpointer data)
543 IMAPFolderItem *item = (IMAPFolderItem *)node->data;
547 g_slist_free(item->uid_list);
548 item->uid_list = NULL;
553 static void imap_reset_uid_lists(Folder *folder)
555 if(folder->node == NULL)
558 /* Destroy all uid lists and rest last uid */
559 g_node_traverse(folder->node, G_IN_ORDER, G_TRAVERSE_ALL, -1, imap_reset_uid_lists_func, NULL);
562 /* Send CAPABILITY, and examine the server's response to see whether this
563 * connection is pre-authenticated or not and build a list of CAPABILITIES. */
564 static gint imap_greeting(IMAPSession *session)
569 imap_gen_send(session, "CAPABILITY");
571 argbuf = g_ptr_array_new();
573 if (imap_cmd_ok(session, argbuf) != IMAP_SUCCESS ||
574 ((capstr = search_array_str(argbuf, "CAPABILITY ")) == NULL)) {
575 ptr_array_free_strings(argbuf);
576 g_ptr_array_free(argbuf, TRUE);
580 session->authenticated = search_array_str(argbuf, "PREAUTH") != NULL;
582 capstr += strlen("CAPABILITY ");
584 IMAP_SESSION(session)->capability = g_strsplit(capstr, " ", 0);
586 ptr_array_free_strings(argbuf);
587 g_ptr_array_free(argbuf, TRUE);
589 if (imap_has_capability(session, "UIDPLUS"))
590 session->uidplus = TRUE;
595 static gint imap_auth(IMAPSession *session, const gchar *user, const gchar *pass,
600 if (type == 0 || type == IMAP_AUTH_LOGIN)
601 ok = imap_cmd_login(session, user, pass);
603 ok = imap_cmd_authenticate(session, user, pass, type);
605 if (ok == IMAP_SUCCESS)
606 session->authenticated = TRUE;
611 static IMAPSession *imap_session_get(Folder *folder)
613 RemoteFolder *rfolder = REMOTE_FOLDER(folder);
614 IMAPSession *session = NULL;
616 g_return_val_if_fail(folder != NULL, NULL);
617 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, NULL);
618 g_return_val_if_fail(folder->account != NULL, NULL);
620 if (prefs_common.work_offline)
623 /* Make sure we have a session */
624 if (rfolder->session != NULL) {
625 session = IMAP_SESSION(rfolder->session);
627 imap_reset_uid_lists(folder);
628 session = imap_session_new(folder->account);
633 if (SESSION(session)->sock->state == CONN_DISCONNECTED) {
634 debug_print("IMAP server disconnected\n");
635 session_destroy(SESSION(session));
636 imap_reset_uid_lists(folder);
637 session = imap_session_new(folder->account);
640 /* Make sure session is authenticated */
641 if (!IMAP_SESSION(session)->authenticated)
642 imap_session_authenticate(IMAP_SESSION(session), folder->account);
643 if (!IMAP_SESSION(session)->authenticated) {
644 session_destroy(SESSION(session));
645 rfolder->session = NULL;
649 /* Make sure we have parsed the IMAP namespace */
650 imap_parse_namespace(IMAP_SESSION(session),
651 IMAP_FOLDER(folder));
653 /* I think the point of this code is to avoid sending a
654 * keepalive if we've used the session recently and therefore
655 * think it's still alive. Unfortunately, most of the code
656 * does not yet check for errors on the socket, and so if the
657 * connection drops we don't notice until the timeout expires.
658 * A better solution than sending a NOOP every time would be
659 * for every command to be prepared to retry until it is
660 * successfully sent. -- mbp */
661 if (time(NULL) - SESSION(session)->last_access_time > SESSION_TIMEOUT_INTERVAL) {
662 /* verify that the session is still alive */
663 if (imap_cmd_noop(session) != IMAP_SUCCESS) {
664 /* Check if this is the first try to establish a
665 connection, if yes we don't try to reconnect */
666 if (rfolder->session == NULL) {
667 log_warning(_("Connecting to %s failed"),
668 folder->account->recv_server);
669 session_destroy(SESSION(session));
672 log_warning(_("IMAP4 connection to %s has been"
673 " disconnected. Reconnecting...\n"),
674 folder->account->recv_server);
675 session_destroy(SESSION(session));
676 /* Clear folders session to make imap_session_get create
677 a new session, because of rfolder->session == NULL
678 it will not try to reconnect again and so avoid an
680 rfolder->session = NULL;
681 session = imap_session_get(folder);
686 rfolder->session = SESSION(session);
688 return IMAP_SESSION(session);
691 static IMAPSession *imap_session_new(const PrefsAccount *account)
693 IMAPSession *session;
698 /* FIXME: IMAP over SSL only... */
701 port = account->set_imapport ? account->imapport
702 : account->ssl_imap == SSL_TUNNEL ? IMAPS_PORT : IMAP4_PORT;
703 ssl_type = account->ssl_imap;
705 port = account->set_imapport ? account->imapport
709 if (account->set_tunnelcmd) {
710 log_message(_("creating tunneled IMAP4 connection\n"));
712 if ((imap_sock = imap_open_tunnel(account->recv_server,
716 if ((imap_sock = imap_open_tunnel(account->recv_server,
717 account->tunnelcmd)) == NULL)
721 g_return_val_if_fail(account->recv_server != NULL, NULL);
723 log_message(_("creating IMAP4 connection to %s:%d ...\n"),
724 account->recv_server, port);
727 if ((imap_sock = imap_open(account->recv_server, port,
730 if ((imap_sock = imap_open(account->recv_server, port)) == NULL)
735 session = g_new0(IMAPSession, 1);
736 session_init(SESSION(session));
737 SESSION(session)->type = SESSION_IMAP;
738 SESSION(session)->server = g_strdup(account->recv_server);
739 SESSION(session)->sock = imap_sock;
741 SESSION(session)->destroy = imap_session_destroy;
743 session->capability = NULL;
745 session->authenticated = FALSE;
746 session->mbox = NULL;
747 session->cmd_count = 0;
749 /* Only need to log in if the connection was not PREAUTH */
750 if (imap_greeting(session) != IMAP_SUCCESS) {
751 session_destroy(SESSION(session));
756 if (account->ssl_imap == SSL_STARTTLS &&
757 imap_has_capability(session, "STARTTLS")) {
760 ok = imap_cmd_starttls(session);
761 if (ok != IMAP_SUCCESS) {
762 log_warning(_("Can't start TLS session.\n"));
763 session_destroy(SESSION(session));
766 if (!ssl_init_socket_with_method(SESSION(session)->sock,
768 session_destroy(SESSION(session));
772 imap_free_capabilities(session);
773 session->authenticated = FALSE;
774 session->uidplus = FALSE;
775 session->cmd_count = 1;
777 if (imap_greeting(session) != IMAP_SUCCESS) {
778 session_destroy(SESSION(session));
783 log_message("IMAP connection is %s-authenticated\n",
784 (session->authenticated) ? "pre" : "un");
789 static void imap_session_authenticate(IMAPSession *session,
790 const PrefsAccount *account)
794 g_return_if_fail(account->userid != NULL);
796 pass = account->passwd;
799 tmp_pass = input_dialog_query_password(account->recv_server, account->userid);
802 Xstrdup_a(pass, tmp_pass, {g_free(tmp_pass); return;});
806 if (imap_auth(session, account->userid, pass, account->imap_auth_type) != IMAP_SUCCESS) {
807 imap_cmd_logout(session);
811 session->authenticated = TRUE;
814 static void imap_session_destroy(Session *session)
816 imap_free_capabilities(IMAP_SESSION(session));
817 g_free(IMAP_SESSION(session)->mbox);
818 sock_close(session->sock);
819 session->sock = NULL;
822 static gchar *imap_fetch_msg(Folder *folder, FolderItem *item, gint uid)
824 gchar *path, *filename;
825 IMAPSession *session;
828 g_return_val_if_fail(folder != NULL, NULL);
829 g_return_val_if_fail(item != NULL, NULL);
831 path = folder_item_get_path(item);
832 if (!is_dir_exist(path))
834 filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(uid), NULL);
837 if (is_file_exist(filename)) {
838 debug_print("message %d has been already cached.\n", uid);
842 session = imap_session_get(folder);
848 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
849 NULL, NULL, NULL, NULL);
850 if (ok != IMAP_SUCCESS) {
851 g_warning("can't select mailbox %s\n", item->path);
856 debug_print("getting message %d...\n", uid);
857 ok = imap_cmd_fetch(session, (guint32)uid, filename);
859 if (ok != IMAP_SUCCESS) {
860 g_warning("can't fetch message %d\n", uid);
868 static gint imap_add_msg(Folder *folder, FolderItem *dest,
869 const gchar *file, MsgFlags *flags)
873 MsgFileInfo fileinfo;
875 g_return_val_if_fail(file != NULL, -1);
877 fileinfo.msginfo = NULL;
878 fileinfo.file = (gchar *)file;
879 fileinfo.flags = flags;
880 file_list.data = &fileinfo;
881 file_list.next = NULL;
883 ret = imap_add_msgs(folder, dest, &file_list, NULL);
887 static gint imap_add_msgs(Folder *folder, FolderItem *dest, GSList *file_list,
891 IMAPSession *session;
892 guint32 last_uid = 0;
894 MsgFileInfo *fileinfo;
897 g_return_val_if_fail(folder != NULL, -1);
898 g_return_val_if_fail(dest != NULL, -1);
899 g_return_val_if_fail(file_list != NULL, -1);
901 session = imap_session_get(folder);
902 if (!session) return -1;
904 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
906 for (cur = file_list; cur != NULL; cur = cur->next) {
907 IMAPFlags iflags = 0;
910 fileinfo = (MsgFileInfo *)cur->data;
912 if (fileinfo->flags) {
913 if (MSG_IS_MARKED(*fileinfo->flags))
914 iflags |= IMAP_FLAG_FLAGGED;
915 if (MSG_IS_REPLIED(*fileinfo->flags))
916 iflags |= IMAP_FLAG_ANSWERED;
917 if (!MSG_IS_UNREAD(*fileinfo->flags))
918 iflags |= IMAP_FLAG_SEEN;
921 if (dest->stype == F_OUTBOX ||
922 dest->stype == F_QUEUE ||
923 dest->stype == F_DRAFT ||
924 dest->stype == F_TRASH)
925 iflags |= IMAP_FLAG_SEEN;
927 ok = imap_cmd_append(session, destdir, fileinfo->file, iflags,
930 if (ok != IMAP_SUCCESS) {
931 g_warning("can't append message %s\n", fileinfo->file);
936 if (relation != NULL)
937 g_relation_insert(relation, fileinfo->msginfo != NULL ?
938 (gpointer) fileinfo->msginfo : (gpointer) fileinfo,
939 GINT_TO_POINTER(dest->last_num + 1));
940 if (last_uid < new_uid)
949 static gint imap_do_copy_msgs(Folder *folder, FolderItem *dest,
950 MsgInfoList *msglist, GRelation *relation)
954 GSList *seq_list, *cur;
956 IMAPSession *session;
957 gint ok = IMAP_SUCCESS;
958 GRelation *uid_mapping;
961 g_return_val_if_fail(folder != NULL, -1);
962 g_return_val_if_fail(dest != NULL, -1);
963 g_return_val_if_fail(msglist != NULL, -1);
965 session = imap_session_get(folder);
966 if (!session) return -1;
968 msginfo = (MsgInfo *)msglist->data;
970 src = msginfo->folder;
972 g_warning("the src folder is identical to the dest.\n");
976 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
977 NULL, NULL, NULL, NULL);
978 if (ok != IMAP_SUCCESS)
981 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
982 seq_list = imap_get_seq_set_from_msglist(msglist);
983 uid_mapping = g_relation_new(2);
984 g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
986 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
987 gchar *seq_set = (gchar *)cur->data;
989 debug_print("Copying message %s%c[%s] to %s ...\n",
990 src->path, G_DIR_SEPARATOR,
993 ok = imap_cmd_copy(session, seq_set, destdir, uid_mapping);
994 if (ok != IMAP_SUCCESS) {
995 g_relation_destroy(uid_mapping);
996 imap_seq_set_free(seq_list);
1001 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
1002 MsgInfo *msginfo = (MsgInfo *)cur->data;
1005 tuples = g_relation_select(uid_mapping,
1006 GINT_TO_POINTER(msginfo->msgnum),
1008 if (tuples->len > 0) {
1009 gint num = GPOINTER_TO_INT(g_tuples_index(tuples, 0, 1));
1010 g_relation_insert(relation, msginfo,
1011 GPOINTER_TO_INT(num));
1015 g_relation_insert(relation, msginfo,
1016 GPOINTER_TO_INT(0));
1017 g_tuples_destroy(tuples);
1020 g_relation_destroy(uid_mapping);
1021 imap_seq_set_free(seq_list);
1025 if (ok == IMAP_SUCCESS)
1031 static gint imap_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1035 g_return_val_if_fail(msginfo != NULL, -1);
1037 msglist.data = msginfo;
1038 msglist.next = NULL;
1040 return imap_copy_msgs(folder, dest, &msglist, NULL);
1043 static gint imap_copy_msgs(Folder *folder, FolderItem *dest,
1044 MsgInfoList *msglist, GRelation *relation)
1050 g_return_val_if_fail(folder != NULL, -1);
1051 g_return_val_if_fail(dest != NULL, -1);
1052 g_return_val_if_fail(msglist != NULL, -1);
1054 msginfo = (MsgInfo *)msglist->data;
1055 g_return_val_if_fail(msginfo->folder != NULL, -1);
1057 if (folder == msginfo->folder->folder)
1058 return imap_do_copy_msgs(folder, dest, msglist, relation);
1060 file_list = procmsg_get_message_file_list(msglist);
1061 g_return_val_if_fail(file_list != NULL, -1);
1063 ret = imap_add_msgs(folder, dest, file_list, relation);
1065 procmsg_message_file_list_free(file_list);
1070 static gint imap_remove_msg(Folder *folder, FolderItem *item, gint uid)
1073 IMAPSession *session;
1075 MsgNumberList numlist;
1077 g_return_val_if_fail(folder != NULL, -1);
1078 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
1079 g_return_val_if_fail(item != NULL, -1);
1081 session = imap_session_get(folder);
1082 if (!session) return -1;
1084 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
1085 NULL, NULL, NULL, NULL);
1086 if (ok != IMAP_SUCCESS)
1089 numlist.next = NULL;
1090 numlist.data = GINT_TO_POINTER(uid);
1092 ok = imap_set_message_flags
1093 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
1094 &numlist, IMAP_FLAG_DELETED, TRUE);
1095 if (ok != IMAP_SUCCESS) {
1096 log_warning(_("can't set deleted flags: %d\n"), uid);
1100 if (!session->uidplus) {
1101 ok = imap_cmd_expunge(session, NULL);
1105 uidstr = g_strdup_printf("%u", uid);
1106 ok = imap_cmd_expunge(session, uidstr);
1109 if (ok != IMAP_SUCCESS) {
1110 log_warning(_("can't expunge\n"));
1114 IMAP_FOLDER_ITEM(item)->uid_list = g_slist_remove(
1115 IMAP_FOLDER_ITEM(item)->uid_list, numlist.data);
1116 dir = folder_item_get_path(item);
1117 if (is_dir_exist(dir))
1118 remove_numbered_files(dir, uid, uid);
1121 return IMAP_SUCCESS;
1124 static gint imap_remove_all_msg(Folder *folder, FolderItem *item)
1127 IMAPSession *session;
1130 g_return_val_if_fail(folder != NULL, -1);
1131 g_return_val_if_fail(item != NULL, -1);
1133 session = imap_session_get(folder);
1134 if (!session) return -1;
1136 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
1137 NULL, NULL, NULL, NULL);
1138 if (ok != IMAP_SUCCESS)
1141 imap_gen_send(session, "STORE 1:* +FLAGS.SILENT (\\Deleted)");
1142 ok = imap_cmd_ok(session, NULL);
1143 if (ok != IMAP_SUCCESS) {
1144 log_warning(_("can't set deleted flags: 1:*\n"));
1148 ok = imap_cmd_expunge(session, NULL);
1149 if (ok != IMAP_SUCCESS) {
1150 log_warning(_("can't expunge\n"));
1154 dir = folder_item_get_path(item);
1155 if (is_dir_exist(dir))
1156 remove_all_numbered_files(dir);
1159 return IMAP_SUCCESS;
1162 static gboolean imap_is_msg_changed(Folder *folder, FolderItem *item,
1165 /* TODO: properly implement this method */
1169 static gint imap_close(Folder *folder, FolderItem *item)
1172 IMAPSession *session;
1174 g_return_val_if_fail(folder != NULL, -1);
1175 g_return_val_if_fail(item != NULL, -1);
1176 g_return_val_if_fail(item->path != NULL, -1);
1178 session = imap_session_get(folder);
1179 if (!session) return -1;
1181 if (session->mbox) {
1182 if (strcmp2(session->mbox, item->path) != 0) return -1;
1184 ok = imap_cmd_close(session);
1185 if (ok != IMAP_SUCCESS)
1186 log_warning(_("can't close folder\n"));
1188 g_free(session->mbox);
1189 session->mbox = NULL;
1197 static gint imap_scan_tree(Folder *folder)
1199 FolderItem *item = NULL;
1200 IMAPSession *session;
1201 gchar *root_folder = NULL;
1203 g_return_val_if_fail(folder != NULL, -1);
1204 g_return_val_if_fail(folder->account != NULL, -1);
1206 session = imap_session_get(folder);
1208 if (!folder->node) {
1209 folder_tree_destroy(folder);
1210 item = folder_item_new(folder, folder->name, NULL);
1211 item->folder = folder;
1212 folder->node = item->node = g_node_new(item);
1217 if (folder->account->imap_dir && *folder->account->imap_dir) {
1222 Xstrdup_a(root_folder, folder->account->imap_dir, return -1);
1223 extract_quote(root_folder, '"');
1224 subst_char(root_folder,
1225 imap_get_path_separator(IMAP_FOLDER(folder),
1228 strtailchomp(root_folder, '/');
1229 real_path = imap_get_real_path
1230 (IMAP_FOLDER(folder), root_folder);
1231 debug_print("IMAP root directory: %s\n", real_path);
1233 /* check if root directory exist */
1234 argbuf = g_ptr_array_new();
1235 ok = imap_cmd_list(session, NULL, real_path, argbuf);
1236 if (ok != IMAP_SUCCESS ||
1237 search_array_str(argbuf, "LIST ") == NULL) {
1238 log_warning(_("root folder %s does not exist\n"), real_path);
1239 g_ptr_array_free(argbuf, TRUE);
1242 if (!folder->node) {
1243 item = folder_item_new(folder, folder->name, NULL);
1244 item->folder = folder;
1245 folder->node = item->node = g_node_new(item);
1249 g_ptr_array_free(argbuf, TRUE);
1254 item = FOLDER_ITEM(folder->node->data);
1255 if (!item || ((item->path || root_folder) &&
1256 strcmp2(item->path, root_folder) != 0)) {
1257 folder_tree_destroy(folder);
1258 item = folder_item_new(folder, folder->name, root_folder);
1259 item->folder = folder;
1260 folder->node = item->node = g_node_new(item);
1263 imap_scan_tree_recursive(session, FOLDER_ITEM(folder->node->data));
1264 imap_create_missing_folders(folder);
1269 static gint imap_scan_tree_recursive(IMAPSession *session, FolderItem *item)
1272 IMAPFolder *imapfolder;
1273 FolderItem *new_item;
1274 GSList *item_list, *cur;
1277 gchar *wildcard_path;
1281 g_return_val_if_fail(item != NULL, -1);
1282 g_return_val_if_fail(item->folder != NULL, -1);
1283 g_return_val_if_fail(item->no_sub == FALSE, -1);
1285 folder = item->folder;
1286 imapfolder = IMAP_FOLDER(folder);
1288 separator = imap_get_path_separator(imapfolder, item->path);
1290 if (folder->ui_func)
1291 folder->ui_func(folder, item, folder->ui_func_data);
1294 wildcard[0] = separator;
1297 real_path = imap_get_real_path(imapfolder, item->path);
1301 real_path = g_strdup("");
1304 Xstrcat_a(wildcard_path, real_path, wildcard,
1305 {g_free(real_path); return IMAP_ERROR;});
1306 QUOTE_IF_REQUIRED(wildcard_path, wildcard_path);
1308 imap_gen_send(session, "LIST \"\" %s",
1311 strtailchomp(real_path, separator);
1312 item_list = imap_parse_list(imapfolder, session, real_path, NULL);
1315 node = item->node->children;
1316 while (node != NULL) {
1317 FolderItem *old_item = FOLDER_ITEM(node->data);
1318 GNode *next = node->next;
1321 for (cur = item_list; cur != NULL; cur = cur->next) {
1322 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1323 if (!strcmp2(old_item->path, cur_item->path)) {
1324 new_item = cur_item;
1329 debug_print("folder '%s' not found. removing...\n",
1331 folder_item_remove(old_item);
1333 old_item->no_sub = new_item->no_sub;
1334 old_item->no_select = new_item->no_select;
1335 if (old_item->no_sub == TRUE && node->children) {
1336 debug_print("folder '%s' doesn't have "
1337 "subfolders. removing...\n",
1339 folder_item_remove_children(old_item);
1346 for (cur = item_list; cur != NULL; cur = cur->next) {
1347 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1349 for (node = item->node->children; node != NULL;
1350 node = node->next) {
1351 if (!strcmp2(FOLDER_ITEM(node->data)->path,
1353 new_item = FOLDER_ITEM(node->data);
1354 folder_item_destroy(cur_item);
1360 new_item = cur_item;
1361 debug_print("new folder '%s' found.\n", new_item->path);
1362 folder_item_append(item, new_item);
1365 if (!strcmp(new_item->path, "INBOX")) {
1366 new_item->stype = F_INBOX;
1367 folder->inbox = new_item;
1368 } else if (!folder_item_parent(item) || item->stype == F_INBOX) {
1371 base = g_basename(new_item->path);
1373 if (!folder->outbox && !strcasecmp(base, "Sent")) {
1374 new_item->stype = F_OUTBOX;
1375 folder->outbox = new_item;
1376 } else if (!folder->draft && !strcasecmp(base, "Drafts")) {
1377 new_item->stype = F_DRAFT;
1378 folder->draft = new_item;
1379 } else if (!folder->queue && !strcasecmp(base, "Queue")) {
1380 new_item->stype = F_QUEUE;
1381 folder->queue = new_item;
1382 } else if (!folder->trash && !strcasecmp(base, "Trash")) {
1383 new_item->stype = F_TRASH;
1384 folder->trash = new_item;
1388 if (new_item->no_sub == FALSE)
1389 imap_scan_tree_recursive(session, new_item);
1392 g_slist_free(item_list);
1394 return IMAP_SUCCESS;
1397 static GSList *imap_parse_list(IMAPFolder *folder, IMAPSession *session,
1398 const gchar *real_path, gchar *separator)
1400 gchar buf[IMAPBUFSIZE];
1402 gchar separator_str[16];
1405 gchar *loc_name, *loc_path;
1406 GSList *item_list = NULL;
1408 FolderItem *new_item;
1410 debug_print("getting list of %s ...\n",
1411 *real_path ? real_path : "\"\"");
1413 str = g_string_new(NULL);
1416 if (sock_gets(SESSION(session)->sock, buf, sizeof(buf)) <= 0) {
1417 log_warning(_("error occurred while getting LIST.\n"));
1421 if (buf[0] != '*' || buf[1] != ' ') {
1422 log_print("IMAP4< %s\n", buf);
1423 if (sscanf(buf, "%*d %16s", buf) < 1 ||
1424 strcmp(buf, "OK") != 0)
1425 log_warning(_("error occurred while getting LIST.\n"));
1429 debug_print("IMAP4< %s\n", buf);
1431 g_string_assign(str, buf);
1433 if (strncmp(p, "LIST ", 5) != 0) continue;
1436 if (*p != '(') continue;
1438 p = strchr_cpy(p, ')', flags, sizeof(flags));
1440 while (*p == ' ') p++;
1442 p = strchr_cpy(p, ' ', separator_str, sizeof(separator_str));
1444 extract_quote(separator_str, '"');
1445 if (!strcmp(separator_str, "NIL"))
1446 separator_str[0] = '\0';
1448 *separator = separator_str[0];
1451 while (*p == ' ') p++;
1452 if (*p == '{' || *p == '"')
1453 p = imap_parse_atom(SESSION(session)->sock, p,
1454 buf, sizeof(buf), str);
1456 strncpy2(buf, p, sizeof(buf));
1457 strtailchomp(buf, separator_str[0]);
1458 if (buf[0] == '\0') continue;
1459 if (!strcmp(buf, real_path)) continue;
1461 if (separator_str[0] != '\0')
1462 subst_char(buf, separator_str[0], '/');
1463 name = g_basename(buf);
1464 if (name[0] == '.') continue;
1466 loc_name = imap_modified_utf7_to_utf8(name);
1467 loc_path = imap_modified_utf7_to_utf8(buf);
1468 new_item = folder_item_new(FOLDER(folder), loc_name, loc_path);
1469 if (strcasestr(flags, "\\Noinferiors") != NULL)
1470 new_item->no_sub = TRUE;
1471 if (strcmp(buf, "INBOX") != 0 &&
1472 strcasestr(flags, "\\Noselect") != NULL)
1473 new_item->no_select = TRUE;
1475 item_list = g_slist_append(item_list, new_item);
1477 debug_print("folder '%s' found.\n", loc_path);
1482 g_string_free(str, TRUE);
1487 static gint imap_create_tree(Folder *folder)
1489 g_return_val_if_fail(folder != NULL, -1);
1490 g_return_val_if_fail(folder->node != NULL, -1);
1491 g_return_val_if_fail(folder->node->data != NULL, -1);
1492 g_return_val_if_fail(folder->account != NULL, -1);
1494 imap_scan_tree(folder);
1495 imap_create_missing_folders(folder);
1500 static void imap_create_missing_folders(Folder *folder)
1502 g_return_if_fail(folder != NULL);
1505 folder->inbox = imap_create_special_folder
1506 (folder, F_INBOX, "INBOX");
1508 if (!folder->outbox)
1509 folder->outbox = imap_create_special_folder
1510 (folder, F_OUTBOX, "Sent");
1512 folder->draft = imap_create_special_folder
1513 (folder, F_DRAFT, "Drafts");
1515 folder->queue = imap_create_special_folder
1516 (folder, F_QUEUE, "Queue");
1519 folder->trash = imap_create_special_folder
1520 (folder, F_TRASH, "Trash");
1523 static FolderItem *imap_create_special_folder(Folder *folder,
1524 SpecialFolderItemType stype,
1528 FolderItem *new_item;
1530 g_return_val_if_fail(folder != NULL, NULL);
1531 g_return_val_if_fail(folder->node != NULL, NULL);
1532 g_return_val_if_fail(folder->node->data != NULL, NULL);
1533 g_return_val_if_fail(folder->account != NULL, NULL);
1534 g_return_val_if_fail(name != NULL, NULL);
1536 item = FOLDER_ITEM(folder->node->data);
1537 new_item = imap_create_folder(folder, item, name);
1540 g_warning("Can't create '%s'\n", name);
1541 if (!folder->inbox) return NULL;
1543 new_item = imap_create_folder(folder, folder->inbox, name);
1545 g_warning("Can't create '%s' under INBOX\n", name);
1547 new_item->stype = stype;
1549 new_item->stype = stype;
1554 static gchar *imap_folder_get_path(Folder *folder)
1558 g_return_val_if_fail(folder != NULL, NULL);
1559 g_return_val_if_fail(folder->account != NULL, NULL);
1561 folder_path = g_strconcat(get_imap_cache_dir(),
1563 folder->account->recv_server,
1565 folder->account->userid,
1571 static gchar *imap_item_get_path(Folder *folder, FolderItem *item)
1573 gchar *folder_path, *path;
1575 g_return_val_if_fail(folder != NULL, NULL);
1576 g_return_val_if_fail(item != NULL, NULL);
1577 folder_path = imap_folder_get_path(folder);
1579 g_return_val_if_fail(folder_path != NULL, NULL);
1580 if (folder_path[0] == G_DIR_SEPARATOR) {
1582 path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
1585 path = g_strdup(folder_path);
1588 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1589 folder_path, G_DIR_SEPARATOR_S,
1592 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1595 g_free(folder_path);
1600 static FolderItem *imap_create_folder(Folder *folder, FolderItem *parent,
1603 gchar *dirpath, *imap_path;
1604 IMAPSession *session;
1605 FolderItem *new_item;
1611 g_return_val_if_fail(folder != NULL, NULL);
1612 g_return_val_if_fail(folder->account != NULL, NULL);
1613 g_return_val_if_fail(parent != NULL, NULL);
1614 g_return_val_if_fail(name != NULL, NULL);
1616 session = imap_session_get(folder);
1617 if (!session) return NULL;
1619 if (!folder_item_parent(parent) && strcmp(name, "INBOX") == 0)
1620 dirpath = g_strdup(name);
1621 else if (parent->path)
1622 dirpath = g_strconcat(parent->path, "/", name, NULL);
1623 else if ((p = strchr(name, '/')) != NULL && *(p + 1) != '\0')
1624 dirpath = g_strdup(name);
1625 else if (folder->account->imap_dir && *folder->account->imap_dir) {
1628 Xstrdup_a(imap_dir, folder->account->imap_dir, return NULL);
1629 strtailchomp(imap_dir, '/');
1630 dirpath = g_strconcat(imap_dir, "/", name, NULL);
1632 dirpath = g_strdup(name);
1634 /* keep trailing directory separator to create a folder that contains
1636 imap_path = imap_utf8_to_modified_utf7(dirpath);
1637 strtailchomp(dirpath, '/');
1638 Xstrdup_a(new_name, name, {g_free(dirpath); return NULL;});
1639 strtailchomp(new_name, '/');
1640 separator = imap_get_path_separator(IMAP_FOLDER(folder), imap_path);
1641 imap_path_separator_subst(imap_path, separator);
1642 subst_char(new_name, '/', separator);
1644 if (strcmp(name, "INBOX") != 0) {
1647 gboolean exist = FALSE;
1649 argbuf = g_ptr_array_new();
1650 ok = imap_cmd_list(session, NULL, imap_path,
1652 if (ok != IMAP_SUCCESS) {
1653 log_warning(_("can't create mailbox: LIST failed\n"));
1656 ptr_array_free_strings(argbuf);
1657 g_ptr_array_free(argbuf, TRUE);
1661 for (i = 0; i < argbuf->len; i++) {
1663 str = g_ptr_array_index(argbuf, i);
1664 if (!strncmp(str, "LIST ", 5)) {
1669 ptr_array_free_strings(argbuf);
1670 g_ptr_array_free(argbuf, TRUE);
1673 ok = imap_cmd_create(session, imap_path);
1674 if (ok != IMAP_SUCCESS) {
1675 log_warning(_("can't create mailbox\n"));
1683 new_item = folder_item_new(folder, new_name, dirpath);
1684 folder_item_append(parent, new_item);
1688 dirpath = folder_item_get_path(new_item);
1689 if (!is_dir_exist(dirpath))
1690 make_dir_hier(dirpath);
1696 static gint imap_rename_folder(Folder *folder, FolderItem *item,
1701 gchar *real_oldpath;
1702 gchar *real_newpath;
1704 gchar *old_cache_dir;
1705 gchar *new_cache_dir;
1706 IMAPSession *session;
1709 gint exists, recent, unseen;
1710 guint32 uid_validity;
1712 g_return_val_if_fail(folder != NULL, -1);
1713 g_return_val_if_fail(item != NULL, -1);
1714 g_return_val_if_fail(item->path != NULL, -1);
1715 g_return_val_if_fail(name != NULL, -1);
1717 if (strchr(name, imap_get_path_separator(IMAP_FOLDER(folder), item->path)) != NULL) {
1718 g_warning(_("New folder name must not contain the namespace "
1723 session = imap_session_get(folder);
1724 if (!session) return -1;
1726 real_oldpath = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1728 g_free(session->mbox);
1729 session->mbox = NULL;
1730 ok = imap_cmd_examine(session, "INBOX",
1731 &exists, &recent, &unseen, &uid_validity);
1732 if (ok != IMAP_SUCCESS) {
1733 g_free(real_oldpath);
1737 separator = imap_get_path_separator(IMAP_FOLDER(folder), item->path);
1738 if (strchr(item->path, G_DIR_SEPARATOR)) {
1739 dirpath = g_dirname(item->path);
1740 newpath = g_strconcat(dirpath, G_DIR_SEPARATOR_S, name, NULL);
1743 newpath = g_strdup(name);
1745 real_newpath = imap_utf8_to_modified_utf7(newpath);
1746 imap_path_separator_subst(real_newpath, separator);
1748 ok = imap_cmd_rename(session, real_oldpath, real_newpath);
1749 if (ok != IMAP_SUCCESS) {
1750 log_warning(_("can't rename mailbox: %s to %s\n"),
1751 real_oldpath, real_newpath);
1752 g_free(real_oldpath);
1754 g_free(real_newpath);
1759 item->name = g_strdup(name);
1761 old_cache_dir = folder_item_get_path(item);
1763 paths[0] = g_strdup(item->path);
1765 g_node_traverse(item->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1766 imap_rename_folder_func, paths);
1768 if (is_dir_exist(old_cache_dir)) {
1769 new_cache_dir = folder_item_get_path(item);
1770 if (rename(old_cache_dir, new_cache_dir) < 0) {
1771 FILE_OP_ERROR(old_cache_dir, "rename");
1773 g_free(new_cache_dir);
1776 g_free(old_cache_dir);
1779 g_free(real_oldpath);
1780 g_free(real_newpath);
1785 static gint imap_remove_folder(Folder *folder, FolderItem *item)
1788 IMAPSession *session;
1791 gint exists, recent, unseen;
1792 guint32 uid_validity;
1794 g_return_val_if_fail(folder != NULL, -1);
1795 g_return_val_if_fail(item != NULL, -1);
1796 g_return_val_if_fail(item->path != NULL, -1);
1798 session = imap_session_get(folder);
1799 if (!session) return -1;
1801 path = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1803 ok = imap_cmd_examine(session, "INBOX",
1804 &exists, &recent, &unseen, &uid_validity);
1805 if (ok != IMAP_SUCCESS) {
1810 ok = imap_cmd_delete(session, path);
1811 if (ok != IMAP_SUCCESS) {
1812 log_warning(_("can't delete mailbox\n"));
1818 cache_dir = folder_item_get_path(item);
1819 if (is_dir_exist(cache_dir) && remove_dir_recursive(cache_dir) < 0)
1820 g_warning("can't remove directory '%s'\n", cache_dir);
1822 folder_item_remove(item);
1827 static GSList *imap_get_uncached_messages(IMAPSession *session,
1829 MsgNumberList *numlist)
1832 GSList *newlist = NULL;
1833 GSList *llast = NULL;
1836 GSList *seq_list, *cur;
1839 g_return_val_if_fail(session != NULL, NULL);
1840 g_return_val_if_fail(item != NULL, NULL);
1841 g_return_val_if_fail(item->folder != NULL, NULL);
1842 g_return_val_if_fail(FOLDER_CLASS(item->folder) == &imap_class, NULL);
1844 seq_list = imap_get_seq_set_from_numlist(numlist);
1845 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
1846 imapset = cur->data;
1848 if (imap_cmd_envelope(session, imapset)
1850 log_warning(_("can't get envelope\n"));
1854 str = g_string_new(NULL);
1857 if ((tmp = sock_getline(SESSION(session)->sock)) == NULL) {
1858 log_warning(_("error occurred while getting envelope.\n"));
1859 g_string_free(str, TRUE);
1863 if (tmp[0] != '*' || tmp[1] != ' ') {
1864 log_print("IMAP4< %s\n", tmp);
1868 if (strstr(tmp, "FETCH") == NULL) {
1869 log_print("IMAP4< %s\n", tmp);
1873 log_print("IMAP4< %s\n", tmp);
1874 g_string_assign(str, tmp);
1877 msginfo = imap_parse_envelope
1878 (SESSION(session)->sock, item, str);
1880 log_warning(_("can't parse envelope: %s\n"), str->str);
1883 if (item->stype == F_QUEUE) {
1884 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_QUEUED);
1885 } else if (item->stype == F_DRAFT) {
1886 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_DRAFT);
1889 msginfo->folder = item;
1892 llast = newlist = g_slist_append(newlist, msginfo);
1894 llast = g_slist_append(llast, msginfo);
1895 llast = llast->next;
1899 g_string_free(str, TRUE);
1901 imap_seq_set_free(seq_list);
1903 session_set_access_time(SESSION(session));
1908 static void imap_delete_all_cached_messages(FolderItem *item)
1912 g_return_if_fail(item != NULL);
1913 g_return_if_fail(item->folder != NULL);
1914 g_return_if_fail(FOLDER_CLASS(item->folder) == &imap_class);
1916 debug_print("Deleting all cached messages...\n");
1918 dir = folder_item_get_path(item);
1919 if (is_dir_exist(dir))
1920 remove_all_numbered_files(dir);
1923 debug_print("done.\n");
1927 static SockInfo *imap_open_tunnel(const gchar *server,
1928 const gchar *tunnelcmd,
1931 static SockInfo *imap_open_tunnel(const gchar *server,
1932 const gchar *tunnelcmd)
1937 if ((sock = sock_connect_cmd(server, tunnelcmd)) == NULL) {
1938 log_warning(_("Can't establish IMAP4 session with: %s\n"),
1943 return imap_init_sock(sock, ssl_type);
1945 return imap_init_sock(sock);
1951 static SockInfo *imap_open(const gchar *server, gushort port,
1954 static SockInfo *imap_open(const gchar *server, gushort port)
1959 if ((sock = sock_connect(server, port)) == NULL) {
1960 log_warning(_("Can't connect to IMAP4 server: %s:%d\n"),
1966 if (ssl_type == SSL_TUNNEL && !ssl_init_socket(sock)) {
1967 log_warning(_("Can't establish IMAP4 session with: %s:%d\n"),
1977 static SockInfo *imap_init_sock(SockInfo *sock, SSLType ssl_type)
1979 static SockInfo *imap_init_sock(SockInfo *sock)
1986 static GList *imap_parse_namespace_str(gchar *str)
1991 IMAPNameSpace *namespace;
1992 GList *ns_list = NULL;
1994 while (*p != '\0') {
1995 /* parse ("#foo" "/") */
1997 while (*p && *p != '(') p++;
1998 if (*p == '\0') break;
2001 while (*p && *p != '"') p++;
2002 if (*p == '\0') break;
2006 while (*p && *p != '"') p++;
2007 if (*p == '\0') break;
2011 while (*p && isspace(*p)) p++;
2012 if (*p == '\0') break;
2013 if (strncmp(p, "NIL", 3) == 0)
2015 else if (*p == '"') {
2018 while (*p && *p != '"') p++;
2019 if (*p == '\0') break;
2024 while (*p && *p != ')') p++;
2025 if (*p == '\0') break;
2028 namespace = g_new(IMAPNameSpace, 1);
2029 namespace->name = g_strdup(name);
2030 namespace->separator = separator ? separator[0] : '\0';
2031 ns_list = g_list_append(ns_list, namespace);
2037 static void imap_parse_namespace(IMAPSession *session, IMAPFolder *folder)
2042 g_return_if_fail(session != NULL);
2043 g_return_if_fail(folder != NULL);
2045 if (folder->ns_personal != NULL ||
2046 folder->ns_others != NULL ||
2047 folder->ns_shared != NULL)
2050 if (!imap_has_capability(session, "NAMESPACE")) {
2051 imap_get_namespace_by_list(session, folder);
2055 if (imap_cmd_namespace(session, &ns_str)
2057 log_warning(_("can't get namespace\n"));
2061 str_array = strsplit_parenthesis(ns_str, '(', ')', 3);
2062 if (str_array == NULL) {
2064 imap_get_namespace_by_list(session, folder);
2068 folder->ns_personal = imap_parse_namespace_str(str_array[0]);
2069 if (str_array[0] && str_array[1])
2070 folder->ns_others = imap_parse_namespace_str(str_array[1]);
2071 if (str_array[0] && str_array[1] && str_array[2])
2072 folder->ns_shared = imap_parse_namespace_str(str_array[2]);
2073 g_strfreev(str_array);
2077 static void imap_get_namespace_by_list(IMAPSession *session, IMAPFolder *folder)
2079 GSList *item_list, *cur;
2080 gchar separator = '\0';
2081 IMAPNameSpace *namespace;
2083 g_return_if_fail(session != NULL);
2084 g_return_if_fail(folder != NULL);
2086 if (folder->ns_personal != NULL ||
2087 folder->ns_others != NULL ||
2088 folder->ns_shared != NULL)
2091 imap_gen_send(session, "LIST \"\" \"\"");
2092 item_list = imap_parse_list(folder, session, "", &separator);
2093 for (cur = item_list; cur != NULL; cur = cur->next)
2094 folder_item_destroy(FOLDER_ITEM(cur->data));
2095 g_slist_free(item_list);
2097 namespace = g_new(IMAPNameSpace, 1);
2098 namespace->name = g_strdup("");
2099 namespace->separator = separator;
2100 folder->ns_personal = g_list_append(NULL, namespace);
2103 static IMAPNameSpace *imap_find_namespace_from_list(GList *ns_list,
2106 IMAPNameSpace *namespace = NULL;
2107 gchar *tmp_path, *name;
2109 if (!path) path = "";
2111 for (; ns_list != NULL; ns_list = ns_list->next) {
2112 IMAPNameSpace *tmp_ns = ns_list->data;
2114 Xstrcat_a(tmp_path, path, "/", return namespace);
2115 Xstrdup_a(name, tmp_ns->name, return namespace);
2116 if (tmp_ns->separator && tmp_ns->separator != '/') {
2117 subst_char(tmp_path, tmp_ns->separator, '/');
2118 subst_char(name, tmp_ns->separator, '/');
2120 if (strncmp(tmp_path, name, strlen(name)) == 0)
2127 static IMAPNameSpace *imap_find_namespace(IMAPFolder *folder,
2130 IMAPNameSpace *namespace;
2132 g_return_val_if_fail(folder != NULL, NULL);
2134 namespace = imap_find_namespace_from_list(folder->ns_personal, path);
2135 if (namespace) return namespace;
2136 namespace = imap_find_namespace_from_list(folder->ns_others, path);
2137 if (namespace) return namespace;
2138 namespace = imap_find_namespace_from_list(folder->ns_shared, path);
2139 if (namespace) return namespace;
2144 static gchar imap_get_path_separator(IMAPFolder *folder, const gchar *path)
2146 IMAPNameSpace *namespace;
2147 gchar separator = '/';
2149 namespace = imap_find_namespace(folder, path);
2150 if (namespace && namespace->separator)
2151 separator = namespace->separator;
2156 static gchar *imap_get_real_path(IMAPFolder *folder, const gchar *path)
2161 g_return_val_if_fail(folder != NULL, NULL);
2162 g_return_val_if_fail(path != NULL, NULL);
2164 real_path = imap_utf8_to_modified_utf7(path);
2165 separator = imap_get_path_separator(folder, path);
2166 imap_path_separator_subst(real_path, separator);
2171 static gchar *imap_parse_atom(SockInfo *sock, gchar *src,
2172 gchar *dest, gint dest_len, GString *str)
2174 gchar *cur_pos = src;
2177 g_return_val_if_fail(str != NULL, cur_pos);
2179 /* read the next line if the current response buffer is empty */
2180 while (isspace(*(guchar *)cur_pos)) cur_pos++;
2181 while (*cur_pos == '\0') {
2182 if ((nextline = sock_getline(sock)) == NULL)
2184 g_string_assign(str, nextline);
2186 strretchomp(nextline);
2187 /* log_print("IMAP4< %s\n", nextline); */
2188 debug_print("IMAP4< %s\n", nextline);
2191 while (isspace(*(guchar *)cur_pos)) cur_pos++;
2194 if (!strncmp(cur_pos, "NIL", 3)) {
2197 } else if (*cur_pos == '\"') {
2200 p = get_quoted(cur_pos, '\"', dest, dest_len);
2201 cur_pos = p ? p : cur_pos + 2;
2202 } else if (*cur_pos == '{') {
2207 cur_pos = strchr_cpy(cur_pos + 1, '}', buf, sizeof(buf));
2209 g_return_val_if_fail(len >= 0, cur_pos);
2211 g_string_truncate(str, 0);
2215 if ((nextline = sock_getline(sock)) == NULL)
2217 line_len += strlen(nextline);
2218 g_string_append(str, nextline);
2220 strretchomp(nextline);
2221 /* log_print("IMAP4< %s\n", nextline); */
2222 debug_print("IMAP4< %s\n", nextline);
2224 } while (line_len < len);
2226 memcpy(dest, cur_pos, MIN(len, dest_len - 1));
2227 dest[MIN(len, dest_len - 1)] = '\0';
2234 static gchar *imap_get_header(SockInfo *sock, gchar *cur_pos, gchar **headers,
2244 g_return_val_if_fail(str != NULL, cur_pos);
2246 while (isspace(*(guchar *)cur_pos)) cur_pos++;
2248 g_return_val_if_fail(*cur_pos == '{', cur_pos);
2250 cur_pos = strchr_cpy(cur_pos + 1, '}', buf, sizeof(buf));
2252 g_return_val_if_fail(len >= 0, cur_pos);
2254 g_string_truncate(str, 0);
2258 if ((nextline = sock_getline(sock)) == NULL)
2260 block_len += strlen(nextline);
2261 g_string_append(str, nextline);
2263 strretchomp(nextline);
2264 /* debug_print("IMAP4< %s\n", nextline); */
2266 } while (block_len < len);
2268 debug_print("IMAP4< [contents of BODY.PEEK[HEADER.FIELDS (...)]]\n");
2270 *headers = g_strndup(cur_pos, len);
2273 while (isspace(*(guchar *)cur_pos)) cur_pos++;
2274 while (*cur_pos == '\0') {
2275 if ((nextline = sock_getline(sock)) == NULL)
2277 g_string_assign(str, nextline);
2279 strretchomp(nextline);
2280 debug_print("IMAP4< %s\n", nextline);
2283 while (isspace(*(guchar *)cur_pos)) cur_pos++;
2289 static MsgFlags imap_parse_flags(const gchar *flag_str)
2291 const gchar *p = flag_str;
2292 MsgFlags flags = {0, 0};
2294 flags.perm_flags = MSG_UNREAD;
2296 while ((p = strchr(p, '\\')) != NULL) {
2299 if (g_strncasecmp(p, "Recent", 6) == 0 && MSG_IS_UNREAD(flags)) {
2300 MSG_SET_PERM_FLAGS(flags, MSG_NEW);
2301 } else if (g_strncasecmp(p, "Seen", 4) == 0) {
2302 MSG_UNSET_PERM_FLAGS(flags, MSG_NEW|MSG_UNREAD);
2303 } else if (g_strncasecmp(p, "Deleted", 7) == 0) {
2304 MSG_SET_PERM_FLAGS(flags, MSG_DELETED);
2305 } else if (g_strncasecmp(p, "Flagged", 7) == 0) {
2306 MSG_SET_PERM_FLAGS(flags, MSG_MARKED);
2307 } else if (g_strncasecmp(p, "Answered", 8) == 0) {
2308 MSG_SET_PERM_FLAGS(flags, MSG_REPLIED);
2315 static MsgInfo *imap_parse_envelope(SockInfo *sock, FolderItem *item,
2318 gchar buf[IMAPBUFSIZE];
2319 MsgInfo *msginfo = NULL;
2324 MsgFlags flags = {0, 0}, imap_flags = {0, 0};
2326 g_return_val_if_fail(line_str != NULL, NULL);
2327 g_return_val_if_fail(line_str->str[0] == '*' &&
2328 line_str->str[1] == ' ', NULL);
2330 MSG_SET_TMP_FLAGS(flags, MSG_IMAP);
2331 if (item->stype == F_QUEUE) {
2332 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
2333 } else if (item->stype == F_DRAFT) {
2334 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
2337 cur_pos = line_str->str + 2;
2339 #define PARSE_ONE_ELEMENT(ch) \
2341 cur_pos = strchr_cpy(cur_pos, ch, buf, sizeof(buf)); \
2342 if (cur_pos == NULL) { \
2343 g_warning("cur_pos == NULL\n"); \
2344 procmsg_msginfo_free(msginfo); \
2349 PARSE_ONE_ELEMENT(' ');
2352 PARSE_ONE_ELEMENT(' ');
2353 g_return_val_if_fail(!strcmp(buf, "FETCH"), NULL);
2355 g_return_val_if_fail(*cur_pos == '(', NULL);
2358 while (*cur_pos != '\0' && *cur_pos != ')') {
2359 while (*cur_pos == ' ') cur_pos++;
2361 if (!strncmp(cur_pos, "UID ", 4)) {
2363 uid = strtoul(cur_pos, &cur_pos, 10);
2364 } else if (!strncmp(cur_pos, "FLAGS ", 6)) {
2366 if (*cur_pos != '(') {
2367 g_warning("*cur_pos != '('\n");
2368 procmsg_msginfo_free(msginfo);
2372 PARSE_ONE_ELEMENT(')');
2373 imap_flags = imap_parse_flags(buf);
2374 } else if (!strncmp(cur_pos, "RFC822.SIZE ", 12)) {
2376 size = strtol(cur_pos, &cur_pos, 10);
2377 } else if (!strncmp(cur_pos, "BODY[HEADER.FIELDS ", 19)) {
2381 if (*cur_pos != '(') {
2382 g_warning("*cur_pos != '('\n");
2383 procmsg_msginfo_free(msginfo);
2387 PARSE_ONE_ELEMENT(')');
2388 if (*cur_pos != ']') {
2389 g_warning("*cur_pos != ']'\n");
2390 procmsg_msginfo_free(msginfo);
2395 cur_pos = imap_get_header(sock, cur_pos, &headers,
2397 msginfo = procheader_parse_str(headers, flags, FALSE, FALSE);
2400 g_warning("invalid FETCH response: %s\n", cur_pos);
2406 msginfo->msgnum = uid;
2407 msginfo->size = size;
2408 msginfo->flags.tmp_flags |= imap_flags.tmp_flags;
2409 msginfo->flags.perm_flags = imap_flags.perm_flags;
2415 static gchar *imap_get_flag_str(IMAPFlags flags)
2420 str = g_string_new(NULL);
2422 if (IMAP_IS_SEEN(flags)) g_string_append(str, "\\Seen ");
2423 if (IMAP_IS_ANSWERED(flags)) g_string_append(str, "\\Answered ");
2424 if (IMAP_IS_FLAGGED(flags)) g_string_append(str, "\\Flagged ");
2425 if (IMAP_IS_DELETED(flags)) g_string_append(str, "\\Deleted ");
2426 if (IMAP_IS_DRAFT(flags)) g_string_append(str, "\\Draft");
2428 if (str->len > 0 && str->str[str->len - 1] == ' ')
2429 g_string_truncate(str, str->len - 1);
2432 g_string_free(str, FALSE);
2437 static gint imap_set_message_flags(IMAPSession *session,
2438 MsgNumberList *numlist,
2445 GSList *seq_list, *cur;
2448 flag_str = imap_get_flag_str(flags);
2449 cmd = g_strconcat(is_set ? "+FLAGS.SILENT (" : "-FLAGS.SILENT (",
2450 flag_str, ")", NULL);
2453 seq_list = imap_get_seq_set_from_numlist(numlist);
2454 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
2455 imapset = cur->data;
2457 ok = imap_cmd_store(session, imapset, cmd);
2459 imap_seq_set_free(seq_list);
2465 static gint imap_select(IMAPSession *session, IMAPFolder *folder,
2467 gint *exists, gint *recent, gint *unseen,
2468 guint32 *uid_validity)
2472 gint exists_, recent_, unseen_;
2473 guint32 uid_validity_;
2475 if (!exists || !recent || !unseen || !uid_validity) {
2476 if (session->mbox && strcmp(session->mbox, path) == 0)
2477 return IMAP_SUCCESS;
2481 uid_validity = &uid_validity_;
2484 g_free(session->mbox);
2485 session->mbox = NULL;
2487 real_path = imap_get_real_path(folder, path);
2488 ok = imap_cmd_select(session, real_path,
2489 exists, recent, unseen, uid_validity);
2490 if (ok != IMAP_SUCCESS)
2491 log_warning(_("can't select folder: %s\n"), real_path);
2493 session->mbox = g_strdup(path);
2494 session->folder_content_changed = FALSE;
2501 #define THROW(err) { ok = err; goto catch; }
2503 static gint imap_status(IMAPSession *session, IMAPFolder *folder,
2505 gint *messages, gint *recent,
2506 guint32 *uid_next, guint32 *uid_validity,
2512 GPtrArray *argbuf = NULL;
2515 if (messages && recent && uid_next && uid_validity && unseen) {
2516 *messages = *recent = *uid_next = *uid_validity = *unseen = 0;
2517 argbuf = g_ptr_array_new();
2520 real_path = imap_get_real_path(folder, path);
2521 QUOTE_IF_REQUIRED(real_path_, real_path);
2522 imap_gen_send(session, "STATUS %s "
2523 "(MESSAGES RECENT UIDNEXT UIDVALIDITY UNSEEN)",
2526 ok = imap_cmd_ok(session, argbuf);
2527 if (ok != IMAP_SUCCESS || !argbuf) THROW(ok);
2529 str = search_array_str(argbuf, "STATUS");
2530 if (!str) THROW(IMAP_ERROR);
2532 str = strchr(str, '(');
2533 if (!str) THROW(IMAP_ERROR);
2535 while (*str != '\0' && *str != ')') {
2536 while (*str == ' ') str++;
2538 if (!strncmp(str, "MESSAGES ", 9)) {
2540 *messages = strtol(str, &str, 10);
2541 } else if (!strncmp(str, "RECENT ", 7)) {
2543 *recent = strtol(str, &str, 10);
2544 } else if (!strncmp(str, "UIDNEXT ", 8)) {
2546 *uid_next = strtoul(str, &str, 10);
2547 } else if (!strncmp(str, "UIDVALIDITY ", 12)) {
2549 *uid_validity = strtoul(str, &str, 10);
2550 } else if (!strncmp(str, "UNSEEN ", 7)) {
2552 *unseen = strtol(str, &str, 10);
2554 g_warning("invalid STATUS response: %s\n", str);
2562 ptr_array_free_strings(argbuf);
2563 g_ptr_array_free(argbuf, TRUE);
2571 static gboolean imap_has_capability(IMAPSession *session, const gchar *cap)
2575 for (p = session->capability; *p != NULL; ++p) {
2576 if (!g_strcasecmp(*p, cap))
2583 static void imap_free_capabilities(IMAPSession *session)
2585 g_strfreev(session->capability);
2586 session->capability = NULL;
2589 /* low-level IMAP4rev1 commands */
2591 static gint imap_cmd_authenticate(IMAPSession *session, const gchar *user,
2592 const gchar *pass, IMAPAuthType type)
2599 gchar hexdigest[33];
2603 auth_type = "CRAM-MD5";
2605 imap_gen_send(session, "AUTHENTICATE %s", auth_type);
2606 ok = imap_gen_recv(session, &buf);
2607 if (ok != IMAP_SUCCESS || buf[0] != '+' || buf[1] != ' ') {
2612 challenge = g_malloc(strlen(buf + 2) + 1);
2613 challenge_len = base64_decode(challenge, buf + 2, -1);
2614 challenge[challenge_len] = '\0';
2616 log_print("IMAP< [Decoded: %s]\n", challenge);
2618 md5_hex_hmac(hexdigest, challenge, challenge_len, pass, strlen(pass));
2621 response = g_strdup_printf("%s %s", user, hexdigest);
2622 log_print("IMAP> [Encoded: %s]\n", response);
2623 response64 = g_malloc((strlen(response) + 3) * 2 + 1);
2624 base64_encode(response64, response, strlen(response));
2627 log_print("IMAP> %s\n", response64);
2628 sock_puts(SESSION(session)->sock, response64);
2629 ok = imap_cmd_ok(session, NULL);
2630 if (ok != IMAP_SUCCESS)
2631 log_warning(_("IMAP4 authentication failed.\n"));
2636 static gint imap_cmd_login(IMAPSession *session,
2637 const gchar *user, const gchar *pass)
2639 gchar *user_, *pass_;
2642 QUOTE_IF_REQUIRED(user_, user);
2643 QUOTE_IF_REQUIRED(pass_, pass);
2644 imap_gen_send(session, "LOGIN %s %s", user_, pass_);
2646 ok = imap_cmd_ok(session, NULL);
2647 if (ok != IMAP_SUCCESS)
2648 log_warning(_("IMAP4 login failed.\n"));
2653 static gint imap_cmd_logout(IMAPSession *session)
2655 imap_gen_send(session, "LOGOUT");
2656 return imap_cmd_ok(session, NULL);
2659 static gint imap_cmd_noop(IMAPSession *session)
2661 imap_gen_send(session, "NOOP");
2662 return imap_cmd_ok(session, NULL);
2665 static gint imap_cmd_starttls(IMAPSession *session)
2667 imap_gen_send(session, "STARTTLS");
2668 return imap_cmd_ok(session, NULL);
2671 #define THROW(err) { ok = err; goto catch; }
2673 static gint imap_cmd_namespace(IMAPSession *session, gchar **ns_str)
2679 argbuf = g_ptr_array_new();
2681 imap_gen_send(session, "NAMESPACE");
2682 if ((ok = imap_cmd_ok(session, argbuf)) != IMAP_SUCCESS) THROW(ok);
2684 str = search_array_str(argbuf, "NAMESPACE");
2685 if (!str) THROW(IMAP_ERROR);
2687 *ns_str = g_strdup(str);
2690 ptr_array_free_strings(argbuf);
2691 g_ptr_array_free(argbuf, TRUE);
2698 static gint imap_cmd_list(IMAPSession *session, const gchar *ref,
2699 const gchar *mailbox, GPtrArray *argbuf)
2701 gchar *ref_, *mailbox_;
2703 if (!ref) ref = "\"\"";
2704 if (!mailbox) mailbox = "\"\"";
2706 QUOTE_IF_REQUIRED(ref_, ref);
2707 QUOTE_IF_REQUIRED(mailbox_, mailbox);
2708 imap_gen_send(session, "LIST %s %s", ref_, mailbox_);
2710 return imap_cmd_ok(session, argbuf);
2713 #define THROW goto catch
2715 static gint imap_cmd_do_select(IMAPSession *session, const gchar *folder,
2717 gint *exists, gint *recent, gint *unseen,
2718 guint32 *uid_validity)
2725 unsigned int uid_validity_;
2727 *exists = *recent = *unseen = *uid_validity = 0;
2728 argbuf = g_ptr_array_new();
2731 select_cmd = "EXAMINE";
2733 select_cmd = "SELECT";
2735 QUOTE_IF_REQUIRED(folder_, folder);
2736 imap_gen_send(session, "%s %s", select_cmd, folder_);
2738 if ((ok = imap_cmd_ok(session, argbuf)) != IMAP_SUCCESS) THROW;
2740 resp_str = search_array_contain_str(argbuf, "EXISTS");
2742 if (sscanf(resp_str,"%d EXISTS", exists) != 1) {
2743 g_warning("imap_cmd_select(): invalid EXISTS line.\n");
2748 resp_str = search_array_contain_str(argbuf, "RECENT");
2750 if (sscanf(resp_str, "%d RECENT", recent) != 1) {
2751 g_warning("imap_cmd_select(): invalid RECENT line.\n");
2756 resp_str = search_array_contain_str(argbuf, "UIDVALIDITY");
2758 if (sscanf(resp_str, "OK [UIDVALIDITY %u] ", &uid_validity_)
2760 g_warning("imap_cmd_select(): invalid UIDVALIDITY line.\n");
2763 *uid_validity = uid_validity_;
2766 resp_str = search_array_contain_str(argbuf, "UNSEEN");
2768 if (sscanf(resp_str, "OK [UNSEEN %d] ", unseen) != 1) {
2769 g_warning("imap_cmd_select(): invalid UNSEEN line.\n");
2775 ptr_array_free_strings(argbuf);
2776 g_ptr_array_free(argbuf, TRUE);
2781 static gint imap_cmd_select(IMAPSession *session, const gchar *folder,
2782 gint *exists, gint *recent, gint *unseen,
2783 guint32 *uid_validity)
2785 return imap_cmd_do_select(session, folder, FALSE,
2786 exists, recent, unseen, uid_validity);
2789 static gint imap_cmd_examine(IMAPSession *session, const gchar *folder,
2790 gint *exists, gint *recent, gint *unseen,
2791 guint32 *uid_validity)
2793 return imap_cmd_do_select(session, folder, TRUE,
2794 exists, recent, unseen, uid_validity);
2799 static gint imap_cmd_create(IMAPSession *session, const gchar *folder)
2803 QUOTE_IF_REQUIRED(folder_, folder);
2804 imap_gen_send(session, "CREATE %s", folder_);
2806 return imap_cmd_ok(session, NULL);
2809 static gint imap_cmd_rename(IMAPSession *session, const gchar *old_folder,
2810 const gchar *new_folder)
2812 gchar *old_folder_, *new_folder_;
2814 QUOTE_IF_REQUIRED(old_folder_, old_folder);
2815 QUOTE_IF_REQUIRED(new_folder_, new_folder);
2816 imap_gen_send(session, "RENAME %s %s", old_folder_, new_folder_);
2818 return imap_cmd_ok(session, NULL);
2821 static gint imap_cmd_delete(IMAPSession *session, const gchar *folder)
2825 QUOTE_IF_REQUIRED(folder_, folder);
2826 imap_gen_send(session, "DELETE %s", folder_);
2828 return imap_cmd_ok(session, NULL);
2831 static gint imap_cmd_search(IMAPSession *session, const gchar *criteria,
2838 g_return_val_if_fail(criteria != NULL, IMAP_ERROR);
2839 g_return_val_if_fail(list != NULL, IMAP_ERROR);
2843 argbuf = g_ptr_array_new();
2844 imap_gen_send(session, "UID SEARCH %s", criteria);
2846 ok = imap_cmd_ok(session, argbuf);
2847 if (ok != IMAP_SUCCESS) {
2848 ptr_array_free_strings(argbuf);
2849 g_ptr_array_free(argbuf, TRUE);
2853 if ((uidlist = search_array_str(argbuf, "SEARCH ")) != NULL) {
2854 gchar **strlist, **p;
2856 strlist = g_strsplit(uidlist + 7, " ", 0);
2857 for (p = strlist; *p != NULL; ++p) {
2860 if (sscanf(*p, "%u", &msgnum) == 1)
2861 *list = g_slist_append(*list, GINT_TO_POINTER(msgnum));
2863 g_strfreev(strlist);
2865 ptr_array_free_strings(argbuf);
2866 g_ptr_array_free(argbuf, TRUE);
2868 return IMAP_SUCCESS;
2871 static gint imap_cmd_fetch(IMAPSession *session, guint32 uid,
2872 const gchar *filename)
2880 g_return_val_if_fail(filename != NULL, IMAP_ERROR);
2882 imap_gen_send(session, "UID FETCH %d BODY.PEEK[]", uid);
2884 while ((ok = imap_gen_recv(session, &buf)) == IMAP_SUCCESS) {
2885 if (buf[0] != '*' || buf[1] != ' ') {
2889 if (strstr(buf, "FETCH") != NULL) break;
2892 if (ok != IMAP_SUCCESS) {
2897 #define RETURN_ERROR_IF_FAIL(cond) \
2900 return IMAP_ERROR; \
2903 cur_pos = strchr(buf, '{');
2904 RETURN_ERROR_IF_FAIL(cur_pos != NULL);
2905 cur_pos = strchr_cpy(cur_pos + 1, '}', size_str, sizeof(size_str));
2906 RETURN_ERROR_IF_FAIL(cur_pos != NULL);
2907 size_num = atol(size_str);
2908 RETURN_ERROR_IF_FAIL(size_num >= 0);
2910 RETURN_ERROR_IF_FAIL(*cur_pos == '\0');
2912 #undef RETURN_ERROR_IF_FAIL
2916 if (recv_bytes_write_to_file(SESSION(session)->sock,
2917 size_num, filename) != 0)
2920 if (imap_gen_recv(session, &buf) != IMAP_SUCCESS) {
2925 if (buf[0] == '\0' || buf[strlen(buf) - 1] != ')') {
2931 ok = imap_cmd_ok(session, NULL);
2936 static gint imap_cmd_append(IMAPSession *session, const gchar *destfolder,
2937 const gchar *file, IMAPFlags flags,
2944 unsigned int new_uid_;
2946 gchar buf[BUFFSIZE];
2951 g_return_val_if_fail(file != NULL, IMAP_ERROR);
2953 size = get_file_size_as_crlf(file);
2954 if ((fp = fopen(file, "rb")) == NULL) {
2955 FILE_OP_ERROR(file, "fopen");
2958 QUOTE_IF_REQUIRED(destfolder_, destfolder);
2959 flag_str = imap_get_flag_str(flags);
2960 imap_gen_send(session, "APPEND %s (%s) {%d}",
2961 destfolder_, flag_str, size);
2964 ok = imap_gen_recv(session, &ret);
2965 if (ok != IMAP_SUCCESS || ret[0] != '+' || ret[1] != ' ') {
2966 log_warning(_("can't append %s to %s\n"), file, destfolder_);
2973 log_print("IMAP4> %s\n", "(sending file...)");
2975 while (fgets(buf, sizeof(buf), fp) != NULL) {
2977 if (sock_puts(SESSION(session)->sock, buf) < 0) {
2984 FILE_OP_ERROR(file, "fgets");
2989 sock_puts(SESSION(session)->sock, "");
2993 if (new_uid != NULL)
2996 if (new_uid != NULL && session->uidplus) {
2997 argbuf = g_ptr_array_new();
2999 ok = imap_cmd_ok(session, argbuf);
3000 if ((ok == IMAP_SUCCESS) && (argbuf->len > 0)) {
3001 resp_str = g_ptr_array_index(argbuf, argbuf->len - 1);
3003 sscanf(resp_str, "%*u OK [APPENDUID %*u %u]",
3005 *new_uid = new_uid_;
3009 ptr_array_free_strings(argbuf);
3010 g_ptr_array_free(argbuf, TRUE);
3012 ok = imap_cmd_ok(session, NULL);
3014 if (ok != IMAP_SUCCESS)
3015 log_warning(_("can't append message to %s\n"),
3021 static MsgNumberList *imapset_to_numlist(IMAPSet imapset)
3023 gchar **ranges, **range;
3024 unsigned int low, high;
3025 MsgNumberList *uids = NULL;
3027 ranges = g_strsplit(imapset, ",", 0);
3028 for (range = ranges; *range != NULL; range++) {
3029 printf("%s\n", *range);
3030 if (sscanf(*range, "%u:%u", &low, &high) == 1)
3031 uids = g_slist_prepend(uids, GINT_TO_POINTER(low));
3034 for (i = low; i <= high; i++)
3035 uids = g_slist_prepend(uids, GINT_TO_POINTER(i));
3038 uids = g_slist_reverse(uids);
3044 static gint imap_cmd_copy(IMAPSession *session, const gchar *seq_set,
3045 const gchar *destfolder, GRelation *uid_mapping)
3050 g_return_val_if_fail(session != NULL, IMAP_ERROR);
3051 g_return_val_if_fail(seq_set != NULL, IMAP_ERROR);
3052 g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
3054 QUOTE_IF_REQUIRED(destfolder_, destfolder);
3055 imap_gen_send(session, "UID COPY %s %s", seq_set, destfolder_);
3057 if (uid_mapping != NULL && session->uidplus) {
3059 gchar *resp_str = NULL, *olduids_str, *newuids_str;
3060 MsgNumberList *olduids, *old_cur, *newuids, *new_cur;
3062 reply = g_ptr_array_new();
3063 ok = imap_cmd_ok(session, reply);
3064 if ((ok == IMAP_SUCCESS) && (reply->len > 0)) {
3065 resp_str = g_ptr_array_index(reply, reply->len - 1);
3067 olduids_str = g_new0(gchar, strlen(resp_str));
3068 newuids_str = g_new0(gchar, strlen(resp_str));
3069 if (sscanf(resp_str, "%*s OK [COPYUID %*u %[0-9,:] %[0-9,:]]",
3070 olduids_str, newuids_str) == 2) {
3071 olduids = imapset_to_numlist(olduids_str);
3072 newuids = imapset_to_numlist(newuids_str);
3076 while(old_cur != NULL && new_cur != NULL) {
3077 g_relation_insert(uid_mapping,
3078 GPOINTER_TO_INT(old_cur->data),
3079 GPOINTER_TO_INT(new_cur->data));
3080 old_cur = g_slist_next(old_cur);
3081 new_cur = g_slist_next(new_cur);
3084 g_slist_free(olduids);
3085 g_slist_free(newuids);
3087 g_free(olduids_str);
3088 g_free(newuids_str);
3091 ptr_array_free_strings(reply);
3092 g_ptr_array_free(reply, TRUE);
3094 ok = imap_cmd_ok(session, NULL);
3096 if (ok != IMAP_SUCCESS)
3097 log_warning(_("can't copy %s to %s\n"), seq_set, destfolder_);
3102 gint imap_cmd_envelope(IMAPSession *session, IMAPSet set)
3104 static GString *header_fields = NULL;
3106 if (header_fields == NULL) {
3107 const HeaderEntry *headers, *elem;
3109 headers = procheader_get_headernames(FALSE);
3110 header_fields = g_string_new("");
3112 for (elem = headers; elem->name != NULL; ++elem) {
3113 gint namelen = strlen(elem->name);
3115 /* Header fields ending with space are not rfc822 headers */
3116 if (elem->name[namelen - 1] == ' ')
3119 /* strip : at the of header field */
3120 if(elem->name[namelen - 1] == ':')
3126 g_string_sprintfa(header_fields, "%s%.*s",
3127 header_fields->str[0] != '\0' ? " " : "",
3128 namelen, elem->name);
3133 (session, "UID FETCH %s (UID FLAGS RFC822.SIZE BODY.PEEK[HEADER.FIELDS (%s)])",
3134 set, header_fields->str);
3136 return IMAP_SUCCESS;
3139 static gint imap_cmd_store(IMAPSession *session, IMAPSet seq_set,
3144 imap_gen_send(session, "UID STORE %s %s", seq_set, sub_cmd);
3146 if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS) {
3147 log_warning(_("error while imap command: STORE %s %s\n"),
3152 return IMAP_SUCCESS;
3155 static gint imap_cmd_expunge(IMAPSession *session, IMAPSet seq_set)
3159 if (seq_set && session->uidplus)
3160 imap_gen_send(session, "UID EXPUNGE %s", seq_set);
3162 imap_gen_send(session, "EXPUNGE");
3163 if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS) {
3164 log_warning(_("error while imap command: EXPUNGE\n"));
3168 return IMAP_SUCCESS;
3171 static gint imap_cmd_close(IMAPSession *session)
3175 imap_gen_send(session, "CLOSE");
3176 if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS)
3177 log_warning(_("error while imap command: CLOSE\n"));
3182 static gint imap_cmd_ok(IMAPSession *session, GPtrArray *argbuf)
3184 gint ok = IMAP_SUCCESS;
3189 while ((ok = imap_gen_recv(session, &buf))
3191 // make sure data is long enough for any substring of buf
3192 data = alloca(strlen(buf) + 1);
3194 // untagged line read
3195 if (buf[0] == '*' && buf[1] == ' ') {
3198 g_ptr_array_add(argbuf, g_strdup(buf + 2));
3200 if (sscanf(buf + 2, "%d %s", &num, data) >= 2) {
3201 if (!strcmp(data, "EXISTS")) {
3202 session->exists = num;
3203 session->folder_content_changed = TRUE;
3206 if(!strcmp(data, "EXPUNGE")) {
3208 session->folder_content_changed = TRUE;
3211 // tagged line with correct tag and OK response found
3212 } else if ((sscanf(buf, "%d %s", &cmd_num, data) >= 2) &&
3213 (cmd_num == session->cmd_count) &&
3214 !strcmp(data, "OK")) {
3216 g_ptr_array_add(argbuf, g_strdup(buf));
3230 static void imap_gen_send(IMAPSession *session, const gchar *format, ...)
3237 va_start(args, format);
3238 tmp = g_strdup_vprintf(format, args);
3241 session->cmd_count++;
3243 buf = g_strdup_printf("%d %s\r\n", session->cmd_count, tmp);
3244 if (!strncasecmp(tmp, "LOGIN ", 6) && (p = strchr(tmp + 6, ' '))) {
3246 log_print("IMAP4> %d %s ********\n", session->cmd_count, tmp);
3248 log_print("IMAP4> %d %s\n", session->cmd_count, tmp);
3250 sock_write_all(SESSION(session)->sock, buf, strlen(buf));
3255 static gint imap_gen_recv(IMAPSession *session, gchar **ret)
3257 if ((*ret = sock_getline(SESSION(session)->sock)) == NULL)
3262 log_print("IMAP4< %s\n", *ret);
3264 session_set_access_time(SESSION(session));
3266 return IMAP_SUCCESS;
3270 /* misc utility functions */
3272 static gchar *strchr_cpy(const gchar *src, gchar ch, gchar *dest, gint len)
3277 tmp = strchr(src, ch);
3281 memcpy(dest, src, MIN(tmp - src, len - 1));
3282 dest[MIN(tmp - src, len - 1)] = '\0';
3287 static gchar *get_quoted(const gchar *src, gchar ch, gchar *dest, gint len)
3289 const gchar *p = src;
3292 g_return_val_if_fail(*p == ch, NULL);
3297 while (*p != '\0' && *p != ch) {
3299 if (*p == '\\' && *(p + 1) != '\0')
3308 return (gchar *)(*p == ch ? p + 1 : p);
3311 static gchar *search_array_contain_str(GPtrArray *array, const gchar *str)
3315 for (i = 0; i < array->len; i++) {
3318 tmp = g_ptr_array_index(array, i);
3319 if (strstr(tmp, str) != NULL)
3326 static gchar *search_array_str(GPtrArray *array, const gchar *str)
3333 for (i = 0; i < array->len; i++) {
3336 tmp = g_ptr_array_index(array, i);
3337 if (!strncmp(tmp, str, len))
3344 static void imap_path_separator_subst(gchar *str, gchar separator)
3347 gboolean in_escape = FALSE;
3349 if (!separator || separator == '/') return;
3351 for (p = str; *p != '\0'; p++) {
3352 if (*p == '/' && !in_escape)
3354 else if (*p == '&' && *(p + 1) != '-' && !in_escape)
3356 else if (*p == '-' && in_escape)
3361 static gchar *imap_modified_utf7_to_utf8(const gchar *mutf7_str)
3364 const gchar *from_p;
3367 to = g_malloc(strlen(mutf7_str) + 1);
3370 for (from_p = mutf7_str; *from_p != '\0'; from_p++) {
3371 if (*from_p == '&' && *(from_p + 1) == '-') {
3381 static iconv_t cd = (iconv_t)-1;
3382 static gboolean iconv_ok = TRUE;
3385 size_t norm_utf7_len;
3387 gchar *to_str, *to_p;
3389 gboolean in_escape = FALSE;
3391 if (!iconv_ok) return g_strdup(mutf7_str);
3393 if (cd == (iconv_t)-1) {
3394 cd = iconv_open(conv_get_current_charset_str(), CS_UTF_8);
3395 if (cd == (iconv_t)-1) {
3396 g_warning(_("iconv cannot convert UTF-7 to UTF-8\n"));
3398 return g_strdup(mutf7_str);
3402 norm_utf7 = g_string_new(NULL);
3404 for (p = mutf7_str; *p != '\0'; p++) {
3405 /* replace: '&' -> '+',
3407 escaped ',' -> '/' */
3408 if (!in_escape && *p == '&') {
3409 if (*(p + 1) != '-') {
3410 g_string_append_c(norm_utf7, '+');
3413 g_string_append_c(norm_utf7, '&');
3416 } else if (in_escape && *p == ',') {
3417 g_string_append_c(norm_utf7, '/');
3418 } else if (in_escape && *p == '-') {
3419 g_string_append_c(norm_utf7, '-');
3422 g_string_append_c(norm_utf7, *p);
3426 norm_utf7_p = norm_utf7->str;
3427 norm_utf7_len = norm_utf7->len;
3428 to_len = strlen(mutf7_str) * 5;
3429 to_p = to_str = g_malloc(to_len + 1);
3431 if (iconv(cd, (ICONV_CONST gchar **)&norm_utf7_p, &norm_utf7_len,
3432 &to_p, &to_len) == -1) {
3433 g_warning(_("iconv cannot convert UTF-7 to UTF-8\n"));
3434 g_string_free(norm_utf7, TRUE);
3436 return g_strdup(mutf7_str);
3439 /* second iconv() call for flushing */
3440 iconv(cd, NULL, NULL, &to_p, &to_len);
3441 g_string_free(norm_utf7, TRUE);
3445 #endif /* !HAVE_ICONV */
3448 static gchar *imap_utf8_to_modified_utf7(const gchar *from)
3451 const gchar *from_p;
3454 to = g_malloc(strlen(from) * 2 + 1);
3457 for (from_p = from; *from_p != '\0'; from_p++) {
3458 if (*from_p == '&') {
3468 static iconv_t cd = (iconv_t)-1;
3469 static gboolean iconv_ok = TRUE;
3470 gchar *norm_utf7, *norm_utf7_p;
3471 size_t from_len, norm_utf7_len;
3473 gchar *from_tmp, *to, *p;
3474 gboolean in_escape = FALSE;
3476 if (!iconv_ok) return g_strdup(from);
3478 if (cd == (iconv_t)-1) {
3479 cd = iconv_open("UTF-7", CS_UTF_8);
3480 if (cd == (iconv_t)-1) {
3481 g_warning("iconv cannot convert UTF-8 to UTF-7\n");
3483 return g_strdup(from);
3487 Xstrdup_a(from_tmp, from, return g_strdup(from));
3488 from_len = strlen(from);
3489 norm_utf7_len = from_len * 5;
3490 Xalloca(norm_utf7, norm_utf7_len + 1, return g_strdup(from));
3491 norm_utf7_p = norm_utf7;
3493 #define IS_PRINT(ch) (isprint(ch) && isascii(ch))
3495 while (from_len > 0) {
3496 if (*from_tmp == '+') {
3497 *norm_utf7_p++ = '+';
3498 *norm_utf7_p++ = '-';
3502 } else if (IS_PRINT(*(guchar *)from_tmp)) {
3503 /* printable ascii char */
3504 *norm_utf7_p = *from_tmp;
3510 size_t mb_len = 0, conv_len = 0;
3512 /* unprintable char: convert to UTF-7 */
3514 while (!IS_PRINT(*(guchar *)p) && conv_len < from_len) {
3515 mb_len = mblen(p, MB_LEN_MAX);
3517 g_warning("wrong multibyte sequence\n");
3518 return g_strdup(from);
3524 from_len -= conv_len;
3525 if (iconv(cd, (ICONV_CONST gchar **)&from_tmp,
3527 &norm_utf7_p, &norm_utf7_len) == -1) {
3528 g_warning(_("iconv cannot convert UTF-8 to UTF-7\n"));
3529 return g_strdup(from);
3532 /* second iconv() call for flushing */
3533 iconv(cd, NULL, NULL, &norm_utf7_p, &norm_utf7_len);
3539 *norm_utf7_p = '\0';
3540 to_str = g_string_new(NULL);
3541 for (p = norm_utf7; p < norm_utf7_p; p++) {
3542 /* replace: '&' -> "&-",
3545 BASE64 '/' -> ',' */
3546 if (!in_escape && *p == '&') {
3547 g_string_append(to_str, "&-");
3548 } else if (!in_escape && *p == '+') {
3549 if (*(p + 1) == '-') {
3550 g_string_append_c(to_str, '+');
3553 g_string_append_c(to_str, '&');
3556 } else if (in_escape && *p == '/') {
3557 g_string_append_c(to_str, ',');
3558 } else if (in_escape && *p == '-') {
3559 g_string_append_c(to_str, '-');
3562 g_string_append_c(to_str, *p);
3568 g_string_append_c(to_str, '-');
3572 g_string_free(to_str, FALSE);
3575 #endif /* !HAVE_ICONV */
3578 static GSList *imap_get_seq_set_from_numlist(MsgNumberList *numlist)
3581 GSList *sorted_list, *cur;
3582 guint first, last, next;
3584 GSList *ret_list = NULL;
3586 if (numlist == NULL)
3589 str = g_string_sized_new(256);
3591 sorted_list = g_slist_copy(numlist);
3592 sorted_list = g_slist_sort(sorted_list, g_int_compare);
3594 first = GPOINTER_TO_INT(sorted_list->data);
3596 for (cur = sorted_list; cur != NULL; cur = g_slist_next(cur)) {
3597 last = GPOINTER_TO_INT(cur->data);
3599 next = GPOINTER_TO_INT(cur->next->data);
3603 if (last + 1 != next || next == 0) {
3605 g_string_append_c(str, ',');
3607 g_string_sprintfa(str, "%u", first);
3609 g_string_sprintfa(str, "%u:%u", first, last);
3613 if (str->len > IMAP_CMD_LIMIT) {
3614 ret_str = g_strdup(str->str);
3615 ret_list = g_slist_append(ret_list, ret_str);
3616 g_string_truncate(str, 0);
3622 ret_str = g_strdup(str->str);
3623 ret_list = g_slist_append(ret_list, ret_str);
3626 g_slist_free(sorted_list);
3627 g_string_free(str, TRUE);
3632 static GSList *imap_get_seq_set_from_msglist(MsgInfoList *msglist)
3634 MsgNumberList *numlist = NULL;
3638 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
3639 MsgInfo *msginfo = (MsgInfo *) cur->data;
3641 numlist = g_slist_append(numlist, GINT_TO_POINTER(msginfo->msgnum));
3643 seq_list = imap_get_seq_set_from_numlist(numlist);
3644 g_slist_free(numlist);
3649 static void imap_seq_set_free(GSList *seq_list)
3651 slist_free_strings(seq_list);
3652 g_slist_free(seq_list);
3656 static gboolean imap_rename_folder_func(GNode *node, gpointer data)
3658 FolderItem *item = node->data;
3659 gchar **paths = data;
3660 const gchar *oldpath = paths[0];
3661 const gchar *newpath = paths[1];
3663 gchar *new_itempath;
3666 oldpathlen = strlen(oldpath);
3667 if (strncmp(oldpath, item->path, oldpathlen) != 0) {
3668 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
3672 base = item->path + oldpathlen;
3673 while (*base == G_DIR_SEPARATOR) base++;
3675 new_itempath = g_strdup(newpath);
3677 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
3680 item->path = new_itempath;
3685 static gint get_list_of_uids(Folder *folder, IMAPFolderItem *item, GSList **msgnum_list)
3687 gint ok, nummsgs = 0, lastuid_old;
3688 IMAPSession *session;
3689 GSList *uidlist, *elem;
3692 session = imap_session_get(folder);
3693 g_return_val_if_fail(session != NULL, -1);
3695 ok = imap_select(session, IMAP_FOLDER(folder), item->item.path,
3696 NULL, NULL, NULL, NULL);
3697 if (ok != IMAP_SUCCESS)
3700 cmd_buf = g_strdup_printf("UID %d:*", item->lastuid + 1);
3701 ok = imap_cmd_search(session, cmd_buf, &uidlist);
3704 if (ok == IMAP_SOCKET) {
3705 session_destroy((Session *)session);
3706 ((RemoteFolder *)folder)->session = NULL;
3710 if (ok != IMAP_SUCCESS) {
3714 argbuf = g_ptr_array_new();
3716 cmd_buf = g_strdup_printf("UID FETCH %d:* (UID)", item->lastuid + 1);
3717 imap_gen_send(session, cmd_buf);
3719 ok = imap_cmd_ok(session, argbuf);
3720 if (ok != IMAP_SUCCESS) {
3721 ptr_array_free_strings(argbuf);
3722 g_ptr_array_free(argbuf, TRUE);
3726 for(i = 0; i < argbuf->len; i++) {
3729 if((ret = sscanf(g_ptr_array_index(argbuf, i),
3730 "%*d FETCH (UID %d)", &msgnum)) == 1)
3731 uidlist = g_slist_prepend(uidlist, GINT_TO_POINTER(msgnum));
3733 ptr_array_free_strings(argbuf);
3734 g_ptr_array_free(argbuf, TRUE);
3737 lastuid_old = item->lastuid;
3738 *msgnum_list = g_slist_copy(item->uid_list);
3739 nummsgs = g_slist_length(*msgnum_list);
3740 debug_print("Got %d uids from cache\n", g_slist_length(item->uid_list));
3742 for (elem = uidlist; elem != NULL; elem = g_slist_next(elem)) {
3745 msgnum = GPOINTER_TO_INT(elem->data);
3746 if (msgnum > lastuid_old) {
3747 *msgnum_list = g_slist_prepend(*msgnum_list, GINT_TO_POINTER(msgnum));
3748 item->uid_list = g_slist_prepend(item->uid_list, GINT_TO_POINTER(msgnum));
3751 if(msgnum > item->lastuid)
3752 item->lastuid = msgnum;
3755 g_slist_free(uidlist);
3760 gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list, gboolean *old_uids_valid)
3762 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3763 IMAPSession *session;
3764 gint ok, nummsgs = 0, exists, recent, uid_val, uid_next, unseen;
3765 GSList *uidlist = NULL;
3767 gboolean selected_folder;
3769 g_return_val_if_fail(folder != NULL, -1);
3770 g_return_val_if_fail(item != NULL, -1);
3771 g_return_val_if_fail(item->item.path != NULL, -1);
3772 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3773 g_return_val_if_fail(folder->account != NULL, -1);
3775 session = imap_session_get(folder);
3776 g_return_val_if_fail(session != NULL, -1);
3778 selected_folder = (session->mbox != NULL) &&
3779 (!strcmp(session->mbox, item->item.path));
3780 if (selected_folder) {
3781 ok = imap_cmd_noop(session);
3782 if (ok != IMAP_SUCCESS)
3784 exists = session->exists;
3786 *old_uids_valid = TRUE;
3788 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
3789 &exists, &recent, &uid_next, &uid_val, &unseen);
3790 if (ok != IMAP_SUCCESS)
3793 if(item->item.mtime == uid_val)
3794 *old_uids_valid = TRUE;
3796 *old_uids_valid = FALSE;
3798 debug_print("Freeing imap uid cache\n");
3800 g_slist_free(item->uid_list);
3801 item->uid_list = NULL;
3803 item->item.mtime = uid_val;
3805 imap_delete_all_cached_messages((FolderItem *)item);
3809 if (!selected_folder)
3810 item->uid_next = uid_next;
3812 /* If old uid_next matches new uid_next we can be sure no message
3813 was added to the folder */
3814 if (( selected_folder && !session->folder_content_changed) ||
3815 (!selected_folder && uid_next == item->uid_next)) {
3816 nummsgs = g_slist_length(item->uid_list);
3818 /* If number of messages is still the same we
3819 know our caches message numbers are still valid,
3820 otherwise if the number of messages has decrease
3821 we discard our cache to start a new scan to find
3822 out which numbers have been removed */
3823 if (exists == nummsgs) {
3824 *msgnum_list = g_slist_copy(item->uid_list);
3826 } else if (exists < nummsgs) {
3827 debug_print("Freeing imap uid cache");
3829 g_slist_free(item->uid_list);
3830 item->uid_list = NULL;
3835 *msgnum_list = NULL;
3839 nummsgs = get_list_of_uids(folder, item, &uidlist);
3841 if (nummsgs != exists) {
3842 /* Cache contains more messages then folder, we have cached
3843 an old UID of a message that was removed and new messages
3844 have been added too, otherwise the uid_next check would
3846 debug_print("Freeing imap uid cache");
3848 g_slist_free(item->uid_list);
3849 item->uid_list = NULL;
3851 g_slist_free(*msgnum_list);
3853 nummsgs = get_list_of_uids(folder, item, &uidlist);
3856 *msgnum_list = uidlist;
3858 dir = folder_item_get_path((FolderItem *)item);
3859 debug_print("removing old messages from %s\n", dir);
3860 remove_numbered_files_not_in_list(dir, *msgnum_list);
3866 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item)
3871 flags.perm_flags = MSG_NEW|MSG_UNREAD;
3872 flags.tmp_flags = 0;
3874 g_return_val_if_fail(item != NULL, NULL);
3875 g_return_val_if_fail(file != NULL, NULL);
3877 if (item->stype == F_QUEUE) {
3878 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
3879 } else if (item->stype == F_DRAFT) {
3880 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
3883 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3884 if (!msginfo) return NULL;
3886 msginfo->folder = item;
3891 GSList *imap_get_msginfos(Folder *folder, FolderItem *item, GSList *msgnum_list)
3893 IMAPSession *session;
3894 MsgInfoList *ret = NULL;
3897 g_return_val_if_fail(folder != NULL, NULL);
3898 g_return_val_if_fail(item != NULL, NULL);
3899 g_return_val_if_fail(msgnum_list != NULL, NULL);
3901 session = imap_session_get(folder);
3902 g_return_val_if_fail(session != NULL, NULL);
3904 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3905 NULL, NULL, NULL, NULL);
3906 if (ok != IMAP_SUCCESS)
3909 if (!(item->stype == F_QUEUE || item->stype == F_DRAFT)) {
3910 ret = g_slist_concat(ret,
3911 imap_get_uncached_messages(
3912 session, item, msgnum_list));
3914 MsgNumberList *sorted_list, *elem;
3915 gint startnum, lastnum;
3917 sorted_list = g_slist_sort(g_slist_copy(msgnum_list), g_int_compare);
3919 startnum = lastnum = GPOINTER_TO_INT(sorted_list->data);
3921 for (elem = sorted_list;; elem = g_slist_next(elem)) {
3925 num = GPOINTER_TO_INT(elem->data);
3927 if (num > lastnum + 1 || elem == NULL) {
3929 for (i = startnum; i <= lastnum; ++i) {
3932 file = imap_fetch_msg(folder, item, i);
3934 MsgInfo *msginfo = imap_parse_msg(file, item);
3935 if (msginfo != NULL) {
3936 msginfo->msgnum = i;
3937 ret = g_slist_append(ret, msginfo);
3951 g_slist_free(sorted_list);
3957 MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid)
3959 MsgInfo *msginfo = NULL;
3960 MsgInfoList *msginfolist;
3961 MsgNumberList numlist;
3963 numlist.next = NULL;
3964 numlist.data = GINT_TO_POINTER(uid);
3966 msginfolist = imap_get_msginfos(folder, item, &numlist);
3967 if (msginfolist != NULL) {
3968 msginfo = msginfolist->data;
3969 g_slist_free(msginfolist);
3975 gboolean imap_scan_required(Folder *folder, FolderItem *_item)
3977 IMAPSession *session;
3978 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3979 gint ok, exists = 0, recent = 0, unseen = 0;
3980 guint32 uid_next, uid_val = 0;
3981 gboolean selected_folder;
3983 g_return_val_if_fail(folder != NULL, FALSE);
3984 g_return_val_if_fail(item != NULL, FALSE);
3985 g_return_val_if_fail(item->item.folder != NULL, FALSE);
3986 g_return_val_if_fail(FOLDER_CLASS(item->item.folder) == &imap_class, FALSE);
3988 if (item->item.path == NULL)
3991 session = imap_session_get(folder);
3992 g_return_val_if_fail(session != NULL, FALSE);
3994 selected_folder = (session->mbox != NULL) &&
3995 (!strcmp(session->mbox, item->item.path));
3996 if (selected_folder) {
3997 ok = imap_cmd_noop(session);
3998 if (ok != IMAP_SUCCESS)
4001 if (session->folder_content_changed)
4004 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
4005 &exists, &recent, &uid_next, &uid_val, &unseen);
4006 if (ok != IMAP_SUCCESS)
4009 if ((uid_next != item->uid_next) || (exists < item->item.total_msgs))
4016 void imap_change_flags(Folder *folder, FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
4018 IMAPSession *session;
4019 IMAPFlags flags_set = 0, flags_unset = 0;
4020 gint ok = IMAP_SUCCESS;
4021 MsgNumberList numlist;
4023 g_return_if_fail(folder != NULL);
4024 g_return_if_fail(folder->klass == &imap_class);
4025 g_return_if_fail(item != NULL);
4026 g_return_if_fail(item->folder == folder);
4027 g_return_if_fail(msginfo != NULL);
4028 g_return_if_fail(msginfo->folder == item);
4030 session = imap_session_get(folder);
4031 if (!session) return;
4033 if ((ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
4034 NULL, NULL, NULL, NULL)) != IMAP_SUCCESS)
4037 if (!MSG_IS_MARKED(msginfo->flags) && (newflags & MSG_MARKED))
4038 flags_set |= IMAP_FLAG_FLAGGED;
4039 if ( MSG_IS_MARKED(msginfo->flags) && !(newflags & MSG_MARKED))
4040 flags_unset |= IMAP_FLAG_FLAGGED;
4042 if (!MSG_IS_UNREAD(msginfo->flags) && (newflags & MSG_UNREAD))
4043 flags_unset |= IMAP_FLAG_SEEN;
4044 if ( MSG_IS_UNREAD(msginfo->flags) && !(newflags & MSG_UNREAD))
4045 flags_set |= IMAP_FLAG_SEEN;
4047 if (!MSG_IS_REPLIED(msginfo->flags) && (newflags & MSG_REPLIED))
4048 flags_set |= IMAP_FLAG_ANSWERED;
4049 if ( MSG_IS_REPLIED(msginfo->flags) && !(newflags & MSG_REPLIED))
4050 flags_set |= IMAP_FLAG_ANSWERED;
4052 numlist.next = NULL;
4053 numlist.data = GINT_TO_POINTER(msginfo->msgnum);
4056 ok = imap_set_message_flags(session, &numlist, flags_set, TRUE);
4057 if (ok != IMAP_SUCCESS) return;
4061 ok = imap_set_message_flags(session, &numlist, flags_unset, FALSE);
4062 if (ok != IMAP_SUCCESS) return;
4065 msginfo->flags.perm_flags = newflags;
4070 static gint compare_msginfo(gconstpointer a, gconstpointer b)
4072 return ((MsgInfo *)a)->msgnum - ((MsgInfo *)b)->msgnum;
4075 static guint gslist_find_next_num(MsgNumberList **list, guint num)
4079 g_return_val_if_fail(list != NULL, -1);
4081 for (elem = *list; elem != NULL; elem = g_slist_next(elem))
4082 if (GPOINTER_TO_INT(elem->data) >= num)
4085 return elem != NULL ? GPOINTER_TO_INT(elem->data) : (gint)-1;
4089 * NEW and DELETED flags are not syncronized
4090 * - The NEW/RECENT flags in IMAP folders can not really be directly
4091 * modified by Sylpheed
4092 * - The DELETE/DELETED flag in IMAP and Sylpheed don't have the same
4093 * meaning, in IMAP it always removes the messages from the FolderItem
4094 * in Sylpheed it can mean to move the message to trash
4096 static gint imap_get_flags(Folder *folder, FolderItem *item,
4097 MsgInfoList *msginfo_list, GRelation *msgflags)
4099 IMAPSession *session;
4100 GSList *sorted_list;
4102 GSList *new = NULL, *p_new;
4103 GSList *deleted = NULL, *p_deleted;
4105 GSList *unseen = NULL, *answered = NULL, *flagged = NULL;
4106 GSList *p_unseen, *p_answered, *p_flagged;
4108 GSList *seq_list, *cur;
4109 gboolean reverse_seen = FALSE;
4112 gint exists_cnt, recent_cnt, unseen_cnt, uid_next;
4113 guint32 uidvalidity;
4115 g_return_val_if_fail(folder != NULL, -1);
4116 g_return_val_if_fail(item != NULL, -1);
4117 if (msginfo_list == NULL)
4120 session = imap_session_get(folder);
4121 g_return_val_if_fail(session != NULL, -1);
4123 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
4124 NULL, NULL, NULL, NULL);
4125 if (ok != IMAP_SUCCESS)
4128 ok = imap_status(session, IMAP_FOLDER(folder), item->path,
4129 &exists_cnt, &recent_cnt, &uid_next, &uidvalidity, &unseen_cnt);
4131 if (unseen_cnt > exists_cnt / 2)
4132 reverse_seen = TRUE;
4134 cmd_buf = g_string_new(NULL);
4136 sorted_list = g_slist_sort(g_slist_copy(msginfo_list), compare_msginfo);
4138 seq_list = imap_get_seq_set_from_msglist(msginfo_list);
4140 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
4141 IMAPSet imapset = cur->data;
4143 g_string_sprintf(cmd_buf, "RECENT UID %s", imapset);
4144 imap_cmd_search(session, cmd_buf->str, &p_new);
4145 new = g_slist_concat(new, p_new);
4147 g_string_sprintf(cmd_buf, "%sSEEN UID %s", reverse_seen ? "" : "UN", imapset);
4148 imap_cmd_search(session, cmd_buf->str, &p_unseen);
4149 unseen = g_slist_concat(unseen, p_unseen);
4151 g_string_sprintf(cmd_buf, "ANSWERED UID %s", imapset);
4152 imap_cmd_search(session, cmd_buf->str, &p_answered);
4153 answered = g_slist_concat(answered, p_answered);
4155 g_string_sprintf(cmd_buf, "FLAGGED UID %s", imapset);
4156 imap_cmd_search(session, cmd_buf->str, &p_flagged);
4157 flagged = g_slist_concat(flagged, p_flagged);
4159 g_string_sprintf(cmd_buf, "DELETED UID %s", imapset);
4160 imap_cmd_search(session, cmd_buf->str, &p_deleted);
4161 deleted = g_slist_concat(deleted, p_deleted);
4169 p_answered = answered;
4170 p_flagged = flagged;
4172 p_deleted = deleted;
4174 for (elem = sorted_list; elem != NULL; elem = g_slist_next(elem)) {
4179 msginfo = (MsgInfo *) elem->data;
4180 flags = msginfo->flags.perm_flags;
4181 wasnew = (flags & MSG_NEW);
4182 flags &= ~((reverse_seen ? 0 : MSG_UNREAD | MSG_NEW) | MSG_REPLIED | MSG_MARKED);
4184 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
4186 if (gslist_find_next_num(&p_new, msginfo->msgnum) == msginfo->msgnum)
4189 if (gslist_find_next_num(&p_unseen, msginfo->msgnum) == msginfo->msgnum) {
4190 if (!reverse_seen) {
4191 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
4193 flags &= ~(MSG_UNREAD | MSG_NEW);
4196 if (gslist_find_next_num(&p_answered, msginfo->msgnum) == msginfo->msgnum)
4197 flags |= MSG_REPLIED;
4198 if (gslist_find_next_num(&p_flagged, msginfo->msgnum) == msginfo->msgnum)
4199 flags |= MSG_MARKED;
4201 if (gslist_find_next_num(&p_deleted, msginfo->msgnum) == msginfo->msgnum)
4202 MSG_SET_PERM_FLAGS(msginfo->flags, MSG_DELETED);
4204 g_relation_insert(msgflags, msginfo, GINT_TO_POINTER(flags));
4207 imap_seq_set_free(seq_list);
4208 /* g_slist_free(deleted); */
4209 g_slist_free(flagged);
4210 g_slist_free(answered);
4211 g_slist_free(unseen);
4212 /* new not freed in original patch ??? */
4213 g_slist_free(sorted_list);
4214 g_string_free(cmd_buf, TRUE);