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 time_t last_access_time;
96 gboolean folder_content_changed;
100 struct _IMAPNameSpace
106 #define IMAP_SUCCESS 0
107 #define IMAP_SOCKET 2
108 #define IMAP_AUTHFAIL 3
109 #define IMAP_PROTOCOL 4
110 #define IMAP_SYNTAX 5
114 #define IMAPBUFSIZE 8192
118 IMAP_FLAG_SEEN = 1 << 0,
119 IMAP_FLAG_ANSWERED = 1 << 1,
120 IMAP_FLAG_FLAGGED = 1 << 2,
121 IMAP_FLAG_DELETED = 1 << 3,
122 IMAP_FLAG_DRAFT = 1 << 4
125 #define IMAP_IS_SEEN(flags) ((flags & IMAP_FLAG_SEEN) != 0)
126 #define IMAP_IS_ANSWERED(flags) ((flags & IMAP_FLAG_ANSWERED) != 0)
127 #define IMAP_IS_FLAGGED(flags) ((flags & IMAP_FLAG_FLAGGED) != 0)
128 #define IMAP_IS_DELETED(flags) ((flags & IMAP_FLAG_DELETED) != 0)
129 #define IMAP_IS_DRAFT(flags) ((flags & IMAP_FLAG_DRAFT) != 0)
132 #define IMAP4_PORT 143
134 #define IMAPS_PORT 993
137 #define IMAP_CMD_LIMIT 1000
139 #define QUOTE_IF_REQUIRED(out, str) \
141 if (*str != '"' && strpbrk(str, " \t(){}%*") != NULL) { \
145 len = strlen(str) + 3; \
146 Xalloca(__tmp, len, return IMAP_ERROR); \
147 g_snprintf(__tmp, len, "\"%s\"", str); \
150 Xstrdup_a(out, str, return IMAP_ERROR); \
154 typedef gchar * IMAPSet;
156 struct _IMAPFolderItem
165 static void imap_folder_init (Folder *folder,
169 static Folder *imap_folder_new (const gchar *name,
171 static void imap_folder_destroy (Folder *folder);
173 static IMAPSession *imap_session_new (const PrefsAccount *account);
174 static void imap_session_authenticate(IMAPSession *session,
175 const PrefsAccount *account);
176 static void imap_session_destroy (Session *session);
178 static gchar *imap_fetch_msg (Folder *folder,
181 static gint imap_add_msg (Folder *folder,
185 static gint imap_add_msgs (Folder *folder,
188 GRelation *relation);
190 static gint imap_copy_msg (Folder *folder,
193 static gint imap_copy_msgs (Folder *folder,
195 MsgInfoList *msglist,
196 GRelation *relation);
198 static gint imap_remove_msg (Folder *folder,
201 static gint imap_remove_all_msg (Folder *folder,
204 static gboolean imap_is_msg_changed (Folder *folder,
208 static gint imap_close (Folder *folder,
211 static gint imap_scan_tree (Folder *folder);
213 static gint imap_create_tree (Folder *folder);
215 static FolderItem *imap_create_folder (Folder *folder,
218 static gint imap_rename_folder (Folder *folder,
221 static gint imap_remove_folder (Folder *folder,
224 static FolderItem *imap_folder_item_new (Folder *folder);
225 static void imap_folder_item_destroy (Folder *folder,
228 static IMAPSession *imap_session_get (Folder *folder);
230 static gint imap_greeting (IMAPSession *session);
231 static gint imap_auth (IMAPSession *session,
236 static gint imap_scan_tree_recursive (IMAPSession *session,
238 static GSList *imap_parse_list (IMAPFolder *folder,
239 IMAPSession *session,
240 const gchar *real_path,
243 static void imap_create_missing_folders (Folder *folder);
244 static FolderItem *imap_create_special_folder
246 SpecialFolderItemType stype,
249 static gint imap_do_copy_msgs (Folder *folder,
251 MsgInfoList *msglist,
252 GRelation *relation);
254 static void imap_delete_all_cached_messages (FolderItem *item);
257 static SockInfo *imap_open (const gchar *server,
261 static SockInfo *imap_open (const gchar *server,
266 static SockInfo *imap_open_tunnel(const gchar *server,
267 const gchar *tunnelcmd,
270 static SockInfo *imap_open_tunnel(const gchar *server,
271 const gchar *tunnelcmd);
275 static SockInfo *imap_init_sock(SockInfo *sock, SSLType ssl_type);
277 static SockInfo *imap_init_sock(SockInfo *sock);
280 static gchar *imap_get_flag_str (IMAPFlags flags);
281 static gint imap_set_message_flags (IMAPSession *session,
282 MsgNumberList *numlist,
285 static gint imap_select (IMAPSession *session,
291 guint32 *uid_validity);
292 static gint imap_status (IMAPSession *session,
298 guint32 *uid_validity,
301 static void imap_parse_namespace (IMAPSession *session,
303 static void imap_get_namespace_by_list (IMAPSession *session,
305 static IMAPNameSpace *imap_find_namespace (IMAPFolder *folder,
307 static gchar imap_get_path_separator (IMAPFolder *folder,
309 static gchar *imap_get_real_path (IMAPFolder *folder,
312 static gchar *imap_parse_atom (SockInfo *sock,
317 static MsgFlags imap_parse_flags (const gchar *flag_str);
318 static MsgInfo *imap_parse_envelope (SockInfo *sock,
322 static gboolean imap_has_capability (IMAPSession *session,
324 static void imap_free_capabilities (IMAPSession *session);
326 /* low-level IMAP4rev1 commands */
327 static gint imap_cmd_authenticate
328 (IMAPSession *session,
332 static gint imap_cmd_login (IMAPSession *session,
335 static gint imap_cmd_logout (IMAPSession *session);
336 static gint imap_cmd_noop (IMAPSession *session);
337 static gint imap_cmd_starttls (IMAPSession *session);
338 static gint imap_cmd_namespace (IMAPSession *session,
340 static gint imap_cmd_list (IMAPSession *session,
342 const gchar *mailbox,
344 static gint imap_cmd_do_select (IMAPSession *session,
350 guint32 *uid_validity);
351 static gint imap_cmd_select (IMAPSession *session,
356 guint32 *uid_validity);
357 static gint imap_cmd_examine (IMAPSession *session,
362 guint32 *uid_validity);
363 static gint imap_cmd_create (IMAPSession *sock,
364 const gchar *folder);
365 static gint imap_cmd_rename (IMAPSession *sock,
366 const gchar *oldfolder,
367 const gchar *newfolder);
368 static gint imap_cmd_delete (IMAPSession *session,
369 const gchar *folder);
370 static gint imap_cmd_envelope (IMAPSession *session,
372 static gint imap_cmd_fetch (IMAPSession *sock,
374 const gchar *filename);
375 static gint imap_cmd_append (IMAPSession *session,
376 const gchar *destfolder,
380 static gint imap_cmd_copy (IMAPSession *session,
381 const gchar *seq_set,
382 const gchar *destfolder,
383 GRelation *uid_mapping);
384 static gint imap_cmd_store (IMAPSession *session,
387 static gint imap_cmd_expunge (IMAPSession *session,
389 static gint imap_cmd_close (IMAPSession *session);
391 static gint imap_cmd_ok (IMAPSession *session,
393 static void imap_gen_send (IMAPSession *session,
394 const gchar *format, ...);
395 static gint imap_gen_recv (IMAPSession *session,
398 /* misc utility functions */
399 static gchar *strchr_cpy (const gchar *src,
403 static gchar *get_quoted (const gchar *src,
407 static gchar *search_array_contain_str (GPtrArray *array,
409 static gchar *search_array_str (GPtrArray *array,
411 static void imap_path_separator_subst (gchar *str,
414 static gchar *imap_modified_utf7_to_locale (const gchar *mutf7_str);
415 static gchar *imap_locale_to_modified_utf7 (const gchar *from);
417 static GSList *imap_get_seq_set_from_numlist (MsgNumberList *msglist);
418 static GSList *imap_get_seq_set_from_msglist (MsgInfoList *msglist);
419 static void imap_seq_set_free (GSList *seq_list);
421 static gboolean imap_rename_folder_func (GNode *node,
423 static gint imap_get_num_list (Folder *folder,
426 gboolean *old_uids_valid);
427 static GSList *imap_get_msginfos (Folder *folder,
429 GSList *msgnum_list);
430 static MsgInfo *imap_get_msginfo (Folder *folder,
433 static gboolean imap_scan_required (Folder *folder,
435 static void imap_change_flags (Folder *folder,
438 MsgPermFlags newflags);
439 static gint imap_get_flags (Folder *folder,
441 MsgInfoList *msglist,
442 GRelation *msgflags);
443 static gchar *imap_folder_get_path (Folder *folder);
444 static gchar *imap_item_get_path (Folder *folder,
447 static FolderClass imap_class;
449 FolderClass *imap_get_class(void)
451 if (imap_class.idstr == NULL) {
452 imap_class.type = F_IMAP;
453 imap_class.idstr = "imap";
454 imap_class.uistr = "IMAP4";
456 /* Folder functions */
457 imap_class.new_folder = imap_folder_new;
458 imap_class.destroy_folder = imap_folder_destroy;
459 imap_class.scan_tree = imap_scan_tree;
460 imap_class.create_tree = imap_create_tree;
462 /* FolderItem functions */
463 imap_class.item_new = imap_folder_item_new;
464 imap_class.item_destroy = imap_folder_item_destroy;
465 imap_class.item_get_path = imap_item_get_path;
466 imap_class.create_folder = imap_create_folder;
467 imap_class.rename_folder = imap_rename_folder;
468 imap_class.remove_folder = imap_remove_folder;
469 imap_class.close = imap_close;
470 imap_class.get_num_list = imap_get_num_list;
471 imap_class.scan_required = imap_scan_required;
473 /* Message functions */
474 imap_class.get_msginfo = imap_get_msginfo;
475 imap_class.get_msginfos = imap_get_msginfos;
476 imap_class.fetch_msg = imap_fetch_msg;
477 imap_class.add_msg = imap_add_msg;
478 imap_class.add_msgs = imap_add_msgs;
479 imap_class.copy_msg = imap_copy_msg;
480 imap_class.copy_msgs = imap_copy_msgs;
481 imap_class.remove_msg = imap_remove_msg;
482 imap_class.remove_all_msg = imap_remove_all_msg;
483 imap_class.is_msg_changed = imap_is_msg_changed;
484 imap_class.change_flags = imap_change_flags;
485 imap_class.get_flags = imap_get_flags;
491 static Folder *imap_folder_new(const gchar *name, const gchar *path)
495 folder = (Folder *)g_new0(IMAPFolder, 1);
496 folder->klass = &imap_class;
497 imap_folder_init(folder, name, path);
502 static void imap_folder_destroy(Folder *folder)
506 dir = imap_folder_get_path(folder);
507 if (is_dir_exist(dir))
508 remove_dir_recursive(dir);
511 folder_remote_folder_destroy(REMOTE_FOLDER(folder));
514 static void imap_folder_init(Folder *folder, const gchar *name,
517 folder_remote_folder_init((Folder *)folder, name, path);
520 static FolderItem *imap_folder_item_new(Folder *folder)
522 IMAPFolderItem *item;
524 item = g_new0(IMAPFolderItem, 1);
527 item->uid_list = NULL;
529 return (FolderItem *)item;
532 static void imap_folder_item_destroy(Folder *folder, FolderItem *_item)
534 IMAPFolderItem *item = (IMAPFolderItem *)_item;
536 g_return_if_fail(item != NULL);
537 g_slist_free(item->uid_list);
542 static gboolean imap_reset_uid_lists_func(GNode *node, gpointer data)
544 IMAPFolderItem *item = (IMAPFolderItem *)node->data;
548 g_slist_free(item->uid_list);
549 item->uid_list = NULL;
554 static void imap_reset_uid_lists(Folder *folder)
556 if(folder->node == NULL)
559 /* Destroy all uid lists and rest last uid */
560 g_node_traverse(folder->node, G_IN_ORDER, G_TRAVERSE_ALL, -1, imap_reset_uid_lists_func, NULL);
563 /* Send CAPABILITY, and examine the server's response to see whether this
564 * connection is pre-authenticated or not and build a list of CAPABILITIES. */
565 static gint imap_greeting(IMAPSession *session)
570 imap_gen_send(session, "CAPABILITY");
572 argbuf = g_ptr_array_new();
574 if (imap_cmd_ok(session, argbuf) != IMAP_SUCCESS ||
575 ((capstr = search_array_str(argbuf, "CAPABILITY ")) == NULL)) {
576 ptr_array_free_strings(argbuf);
577 g_ptr_array_free(argbuf, TRUE);
581 session->authenticated = search_array_str(argbuf, "PREAUTH") != NULL;
583 capstr += strlen("CAPABILITY ");
585 IMAP_SESSION(session)->capability = g_strsplit(capstr, " ", 0);
587 ptr_array_free_strings(argbuf);
588 g_ptr_array_free(argbuf, TRUE);
590 if (imap_has_capability(session, "UIDPLUS"))
591 session->uidplus = TRUE;
596 static gint imap_auth(IMAPSession *session, const gchar *user, const gchar *pass,
601 if (type == 0 || type == IMAP_AUTH_LOGIN)
602 ok = imap_cmd_login(session, user, pass);
604 ok = imap_cmd_authenticate(session, user, pass, type);
606 if (ok == IMAP_SUCCESS)
607 session->authenticated = TRUE;
612 static IMAPSession *imap_session_get(Folder *folder)
614 RemoteFolder *rfolder = REMOTE_FOLDER(folder);
615 IMAPSession *session = NULL;
617 g_return_val_if_fail(folder != NULL, NULL);
618 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, NULL);
619 g_return_val_if_fail(folder->account != NULL, NULL);
621 if (prefs_common.work_offline)
624 /* Make sure we have a session */
625 if (rfolder->session != NULL) {
626 session = IMAP_SESSION(rfolder->session);
628 imap_reset_uid_lists(folder);
629 session = imap_session_new(folder->account);
634 if (SESSION(session)->sock->state == CONN_DISCONNECTED) {
635 debug_print("IMAP server disconnected\n");
636 session_destroy(SESSION(session));
637 imap_reset_uid_lists(folder);
638 session = imap_session_new(folder->account);
641 /* Make sure session is authenticated */
642 if (!IMAP_SESSION(session)->authenticated)
643 imap_session_authenticate(IMAP_SESSION(session), folder->account);
644 if (!IMAP_SESSION(session)->authenticated) {
645 session_destroy(SESSION(session));
646 rfolder->session = NULL;
650 /* Make sure we have parsed the IMAP namespace */
651 imap_parse_namespace(IMAP_SESSION(session),
652 IMAP_FOLDER(folder));
654 /* I think the point of this code is to avoid sending a
655 * keepalive if we've used the session recently and therefore
656 * think it's still alive. Unfortunately, most of the code
657 * does not yet check for errors on the socket, and so if the
658 * connection drops we don't notice until the timeout expires.
659 * A better solution than sending a NOOP every time would be
660 * for every command to be prepared to retry until it is
661 * successfully sent. -- mbp */
662 if (time(NULL) - session->last_access_time > SESSION_TIMEOUT) {
663 /* verify that the session is still alive */
664 if (imap_cmd_noop(session) != IMAP_SUCCESS) {
665 /* Check if this is the first try to establish a
666 connection, if yes we don't try to reconnect */
667 if (rfolder->session == NULL) {
668 log_warning(_("Connecting %s failed"),
669 folder->account->recv_server);
670 session_destroy(SESSION(session));
673 log_warning(_("IMAP4 connection to %s has been"
674 " disconnected. Reconnecting...\n"),
675 folder->account->recv_server);
676 session_destroy(SESSION(session));
677 /* Clear folders session to make imap_session_get create
678 a new session, because of rfolder->session == NULL
679 it will not try to reconnect again and so avoid an
681 rfolder->session = NULL;
682 session = imap_session_get(folder);
687 rfolder->session = SESSION(session);
689 session->last_access_time = time(NULL);
691 return IMAP_SESSION(session);
694 static IMAPSession *imap_session_new(const PrefsAccount *account)
696 IMAPSession *session;
701 /* FIXME: IMAP over SSL only... */
704 port = account->set_imapport ? account->imapport
705 : account->ssl_imap == SSL_TUNNEL ? IMAPS_PORT : IMAP4_PORT;
706 ssl_type = account->ssl_imap;
708 port = account->set_imapport ? account->imapport
712 if (account->set_tunnelcmd) {
713 log_message(_("creating tunneled IMAP4 connection\n"));
715 if ((imap_sock = imap_open_tunnel(account->recv_server,
719 if ((imap_sock = imap_open_tunnel(account->recv_server,
720 account->tunnelcmd)) == NULL)
724 g_return_val_if_fail(account->recv_server != NULL, NULL);
726 log_message(_("creating IMAP4 connection to %s:%d ...\n"),
727 account->recv_server, port);
730 if ((imap_sock = imap_open(account->recv_server, port,
733 if ((imap_sock = imap_open(account->recv_server, port)) == NULL)
738 session = g_new0(IMAPSession, 1);
739 session_init(SESSION(session));
740 SESSION(session)->type = SESSION_IMAP;
741 SESSION(session)->server = g_strdup(account->recv_server);
742 SESSION(session)->sock = imap_sock;
744 SESSION(session)->destroy = imap_session_destroy;
746 session->capability = NULL;
748 session->authenticated = FALSE;
749 session->mbox = NULL;
750 session->cmd_count = 0;
752 /* Only need to log in if the connection was not PREAUTH */
753 if (imap_greeting(session) != IMAP_SUCCESS) {
754 session_destroy(SESSION(session));
759 if (account->ssl_imap == SSL_STARTTLS &&
760 imap_has_capability(session, "STARTTLS")) {
763 ok = imap_cmd_starttls(session);
764 if (ok != IMAP_SUCCESS) {
765 log_warning(_("Can't start TLS session.\n"));
766 session_destroy(SESSION(session));
769 if (!ssl_init_socket_with_method(SESSION(session)->sock,
771 session_destroy(SESSION(session));
775 imap_free_capabilities(session);
776 session->authenticated = FALSE;
777 session->uidplus = FALSE;
778 session->cmd_count = 1;
780 if (imap_greeting(session) != IMAP_SUCCESS) {
781 session_destroy(SESSION(session));
786 log_message("IMAP connection is %s-authenticated\n",
787 (session->authenticated) ? "pre" : "un");
792 static void imap_session_authenticate(IMAPSession *session,
793 const PrefsAccount *account)
797 g_return_if_fail(account->userid != NULL);
799 pass = account->passwd;
802 tmp_pass = input_dialog_query_password(account->recv_server, account->userid);
805 Xstrdup_a(pass, tmp_pass, {g_free(tmp_pass); return;});
809 if (imap_auth(session, account->userid, pass, account->imap_auth_type) != IMAP_SUCCESS) {
810 imap_cmd_logout(session);
814 session->authenticated = TRUE;
817 static void imap_session_destroy(Session *session)
819 imap_free_capabilities(IMAP_SESSION(session));
820 g_free(IMAP_SESSION(session)->mbox);
821 sock_close(session->sock);
822 session->sock = NULL;
825 static gchar *imap_fetch_msg(Folder *folder, FolderItem *item, gint uid)
827 gchar *path, *filename;
828 IMAPSession *session;
831 g_return_val_if_fail(folder != NULL, NULL);
832 g_return_val_if_fail(item != NULL, NULL);
834 path = folder_item_get_path(item);
835 if (!is_dir_exist(path))
837 filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(uid), NULL);
840 if (is_file_exist(filename)) {
841 debug_print("message %d has been already cached.\n", uid);
845 session = imap_session_get(folder);
851 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
852 NULL, NULL, NULL, NULL);
853 if (ok != IMAP_SUCCESS) {
854 g_warning("can't select mailbox %s\n", item->path);
859 debug_print("getting message %d...\n", uid);
860 ok = imap_cmd_fetch(session, (guint32)uid, filename);
862 if (ok != IMAP_SUCCESS) {
863 g_warning("can't fetch message %d\n", uid);
871 static gint imap_add_msg(Folder *folder, FolderItem *dest,
872 const gchar *file, MsgFlags *flags)
876 MsgFileInfo fileinfo;
878 g_return_val_if_fail(file != NULL, -1);
880 fileinfo.msginfo = NULL;
881 fileinfo.file = (gchar *)file;
882 fileinfo.flags = flags;
883 file_list.data = &fileinfo;
884 file_list.next = NULL;
886 ret = imap_add_msgs(folder, dest, &file_list, NULL);
890 static gint imap_add_msgs(Folder *folder, FolderItem *dest, GSList *file_list,
894 IMAPSession *session;
895 guint32 last_uid = 0;
897 MsgFileInfo *fileinfo;
900 g_return_val_if_fail(folder != NULL, -1);
901 g_return_val_if_fail(dest != NULL, -1);
902 g_return_val_if_fail(file_list != NULL, -1);
904 session = imap_session_get(folder);
905 if (!session) return -1;
907 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
909 for (cur = file_list; cur != NULL; cur = cur->next) {
910 IMAPFlags iflags = 0;
913 fileinfo = (MsgFileInfo *)cur->data;
915 if (fileinfo->flags) {
916 if (MSG_IS_MARKED(*fileinfo->flags))
917 iflags |= IMAP_FLAG_FLAGGED;
918 if (MSG_IS_REPLIED(*fileinfo->flags))
919 iflags |= IMAP_FLAG_ANSWERED;
920 if (!MSG_IS_UNREAD(*fileinfo->flags))
921 iflags |= IMAP_FLAG_SEEN;
924 if (dest->stype == F_OUTBOX ||
925 dest->stype == F_QUEUE ||
926 dest->stype == F_DRAFT ||
927 dest->stype == F_TRASH)
928 iflags |= IMAP_FLAG_SEEN;
930 ok = imap_cmd_append(session, destdir, fileinfo->file, iflags,
933 if (ok != IMAP_SUCCESS) {
934 g_warning("can't append message %s\n", fileinfo->file);
939 if (relation != NULL)
940 g_relation_insert(relation, fileinfo->msginfo != NULL ?
941 (gpointer) fileinfo->msginfo : (gpointer) fileinfo,
942 GINT_TO_POINTER(dest->last_num + 1));
943 if (last_uid < new_uid)
952 static gint imap_do_copy_msgs(Folder *folder, FolderItem *dest,
953 MsgInfoList *msglist, GRelation *relation)
957 GSList *seq_list, *cur;
959 IMAPSession *session;
960 gint ok = IMAP_SUCCESS;
961 GRelation *uid_mapping;
964 g_return_val_if_fail(folder != NULL, -1);
965 g_return_val_if_fail(dest != NULL, -1);
966 g_return_val_if_fail(msglist != NULL, -1);
968 session = imap_session_get(folder);
969 if (!session) return -1;
971 msginfo = (MsgInfo *)msglist->data;
973 src = msginfo->folder;
975 g_warning("the src folder is identical to the dest.\n");
979 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
980 NULL, NULL, NULL, NULL);
981 if (ok != IMAP_SUCCESS)
984 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
985 seq_list = imap_get_seq_set_from_msglist(msglist);
986 uid_mapping = g_relation_new(2);
987 g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
989 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
990 gchar *seq_set = (gchar *)cur->data;
992 debug_print("Copying message %s%c[%s] to %s ...\n",
993 src->path, G_DIR_SEPARATOR,
996 ok = imap_cmd_copy(session, seq_set, destdir, uid_mapping);
997 if (ok != IMAP_SUCCESS) {
998 g_relation_destroy(uid_mapping);
999 imap_seq_set_free(seq_list);
1004 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
1005 MsgInfo *msginfo = (MsgInfo *)cur->data;
1008 tuples = g_relation_select(uid_mapping,
1009 GINT_TO_POINTER(msginfo->msgnum),
1011 if (tuples->len > 0) {
1012 gint num = GPOINTER_TO_INT(g_tuples_index(tuples, 0, 1));
1013 g_relation_insert(relation, msginfo,
1014 GPOINTER_TO_INT(num));
1018 g_relation_insert(relation, msginfo,
1019 GPOINTER_TO_INT(0));
1020 g_tuples_destroy(tuples);
1023 g_relation_destroy(uid_mapping);
1024 imap_seq_set_free(seq_list);
1028 if (ok == IMAP_SUCCESS)
1034 static gint imap_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1038 g_return_val_if_fail(msginfo != NULL, -1);
1040 msglist.data = msginfo;
1041 msglist.next = NULL;
1043 return imap_copy_msgs(folder, dest, &msglist, NULL);
1046 static gint imap_copy_msgs(Folder *folder, FolderItem *dest,
1047 MsgInfoList *msglist, GRelation *relation)
1053 g_return_val_if_fail(folder != NULL, -1);
1054 g_return_val_if_fail(dest != NULL, -1);
1055 g_return_val_if_fail(msglist != NULL, -1);
1057 msginfo = (MsgInfo *)msglist->data;
1058 g_return_val_if_fail(msginfo->folder != NULL, -1);
1060 if (folder == msginfo->folder->folder)
1061 return imap_do_copy_msgs(folder, dest, msglist, relation);
1063 file_list = procmsg_get_message_file_list(msglist);
1064 g_return_val_if_fail(file_list != NULL, -1);
1066 ret = imap_add_msgs(folder, dest, file_list, relation);
1068 procmsg_message_file_list_free(file_list);
1073 static gint imap_remove_msg(Folder *folder, FolderItem *item, gint uid)
1076 IMAPSession *session;
1078 MsgNumberList numlist;
1080 g_return_val_if_fail(folder != NULL, -1);
1081 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
1082 g_return_val_if_fail(item != NULL, -1);
1084 session = imap_session_get(folder);
1085 if (!session) return -1;
1087 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
1088 NULL, NULL, NULL, NULL);
1089 if (ok != IMAP_SUCCESS)
1092 numlist.next = NULL;
1093 numlist.data = GINT_TO_POINTER(uid);
1095 ok = imap_set_message_flags
1096 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
1097 &numlist, IMAP_FLAG_DELETED, TRUE);
1098 if (ok != IMAP_SUCCESS) {
1099 log_warning(_("can't set deleted flags: %d\n"), uid);
1103 if (!session->uidplus) {
1104 ok = imap_cmd_expunge(session, NULL);
1108 uidstr = g_strdup_printf("%u", uid);
1109 ok = imap_cmd_expunge(session, uidstr);
1112 if (ok != IMAP_SUCCESS) {
1113 log_warning(_("can't expunge\n"));
1117 IMAP_FOLDER_ITEM(item)->uid_list = g_slist_remove(
1118 IMAP_FOLDER_ITEM(item)->uid_list, numlist.data);
1119 dir = folder_item_get_path(item);
1120 if (is_dir_exist(dir))
1121 remove_numbered_files(dir, uid, uid);
1124 return IMAP_SUCCESS;
1127 static gint imap_remove_all_msg(Folder *folder, FolderItem *item)
1130 IMAPSession *session;
1133 g_return_val_if_fail(folder != NULL, -1);
1134 g_return_val_if_fail(item != NULL, -1);
1136 session = imap_session_get(folder);
1137 if (!session) return -1;
1139 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
1140 NULL, NULL, NULL, NULL);
1141 if (ok != IMAP_SUCCESS)
1144 imap_gen_send(session, "STORE 1:* +FLAGS.SILENT (\\Deleted)");
1145 ok = imap_cmd_ok(session, NULL);
1146 if (ok != IMAP_SUCCESS) {
1147 log_warning(_("can't set deleted flags: 1:*\n"));
1151 ok = imap_cmd_expunge(session, NULL);
1152 if (ok != IMAP_SUCCESS) {
1153 log_warning(_("can't expunge\n"));
1157 dir = folder_item_get_path(item);
1158 if (is_dir_exist(dir))
1159 remove_all_numbered_files(dir);
1162 return IMAP_SUCCESS;
1165 static gboolean imap_is_msg_changed(Folder *folder, FolderItem *item,
1168 /* TODO: properly implement this method */
1172 static gint imap_close(Folder *folder, FolderItem *item)
1175 IMAPSession *session;
1177 g_return_val_if_fail(folder != NULL, -1);
1178 g_return_val_if_fail(item != NULL, -1);
1179 g_return_val_if_fail(item->path != NULL, -1);
1181 session = imap_session_get(folder);
1182 if (!session) return -1;
1184 if (session->mbox) {
1185 if (strcmp2(session->mbox, item->path) != 0) return -1;
1187 ok = imap_cmd_close(session);
1188 if (ok != IMAP_SUCCESS)
1189 log_warning(_("can't close folder\n"));
1191 g_free(session->mbox);
1192 session->mbox = NULL;
1200 static gint imap_scan_tree(Folder *folder)
1202 FolderItem *item = NULL;
1203 IMAPSession *session;
1204 gchar *root_folder = NULL;
1206 g_return_val_if_fail(folder != NULL, -1);
1207 g_return_val_if_fail(folder->account != NULL, -1);
1209 session = imap_session_get(folder);
1211 if (!folder->node) {
1212 folder_tree_destroy(folder);
1213 item = folder_item_new(folder, folder->name, NULL);
1214 item->folder = folder;
1215 folder->node = item->node = g_node_new(item);
1220 if (folder->account->imap_dir && *folder->account->imap_dir) {
1225 Xstrdup_a(root_folder, folder->account->imap_dir, return -1);
1226 extract_quote(root_folder, '"');
1227 subst_char(root_folder,
1228 imap_get_path_separator(IMAP_FOLDER(folder),
1231 strtailchomp(root_folder, '/');
1232 real_path = imap_get_real_path
1233 (IMAP_FOLDER(folder), root_folder);
1234 debug_print("IMAP root directory: %s\n", real_path);
1236 /* check if root directory exist */
1237 argbuf = g_ptr_array_new();
1238 ok = imap_cmd_list(session, NULL, real_path, argbuf);
1239 if (ok != IMAP_SUCCESS ||
1240 search_array_str(argbuf, "LIST ") == NULL) {
1241 log_warning(_("root folder %s does not exist\n"), real_path);
1242 g_ptr_array_free(argbuf, TRUE);
1245 if (!folder->node) {
1246 item = folder_item_new(folder, folder->name, NULL);
1247 item->folder = folder;
1248 folder->node = item->node = g_node_new(item);
1252 g_ptr_array_free(argbuf, TRUE);
1257 item = FOLDER_ITEM(folder->node->data);
1258 if (!item || ((item->path || root_folder) &&
1259 strcmp2(item->path, root_folder) != 0)) {
1260 folder_tree_destroy(folder);
1261 item = folder_item_new(folder, folder->name, root_folder);
1262 item->folder = folder;
1263 folder->node = item->node = g_node_new(item);
1266 imap_scan_tree_recursive(session, FOLDER_ITEM(folder->node->data));
1267 imap_create_missing_folders(folder);
1272 static gint imap_scan_tree_recursive(IMAPSession *session, FolderItem *item)
1275 IMAPFolder *imapfolder;
1276 FolderItem *new_item;
1277 GSList *item_list, *cur;
1280 gchar *wildcard_path;
1284 g_return_val_if_fail(item != NULL, -1);
1285 g_return_val_if_fail(item->folder != NULL, -1);
1286 g_return_val_if_fail(item->no_sub == FALSE, -1);
1288 folder = item->folder;
1289 imapfolder = IMAP_FOLDER(folder);
1291 separator = imap_get_path_separator(imapfolder, item->path);
1293 if (folder->ui_func)
1294 folder->ui_func(folder, item, folder->ui_func_data);
1297 wildcard[0] = separator;
1300 real_path = imap_get_real_path(imapfolder, item->path);
1304 real_path = g_strdup("");
1307 Xstrcat_a(wildcard_path, real_path, wildcard,
1308 {g_free(real_path); return IMAP_ERROR;});
1309 QUOTE_IF_REQUIRED(wildcard_path, wildcard_path);
1311 imap_gen_send(session, "LIST \"\" %s",
1314 strtailchomp(real_path, separator);
1315 item_list = imap_parse_list(imapfolder, session, real_path, NULL);
1318 node = item->node->children;
1319 while (node != NULL) {
1320 FolderItem *old_item = FOLDER_ITEM(node->data);
1321 GNode *next = node->next;
1324 for (cur = item_list; cur != NULL; cur = cur->next) {
1325 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1326 if (!strcmp2(old_item->path, cur_item->path)) {
1327 new_item = cur_item;
1332 debug_print("folder '%s' not found. removing...\n",
1334 folder_item_remove(old_item);
1336 old_item->no_sub = new_item->no_sub;
1337 old_item->no_select = new_item->no_select;
1338 if (old_item->no_sub == TRUE && node->children) {
1339 debug_print("folder '%s' doesn't have "
1340 "subfolders. removing...\n",
1342 folder_item_remove_children(old_item);
1349 for (cur = item_list; cur != NULL; cur = cur->next) {
1350 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1352 for (node = item->node->children; node != NULL;
1353 node = node->next) {
1354 if (!strcmp2(FOLDER_ITEM(node->data)->path,
1356 new_item = FOLDER_ITEM(node->data);
1357 folder_item_destroy(cur_item);
1363 new_item = cur_item;
1364 debug_print("new folder '%s' found.\n", new_item->path);
1365 folder_item_append(item, new_item);
1368 if (!strcmp(new_item->path, "INBOX")) {
1369 new_item->stype = F_INBOX;
1370 folder->inbox = new_item;
1371 } else if (!folder_item_parent(item) || item->stype == F_INBOX) {
1374 base = g_basename(new_item->path);
1376 if (!folder->outbox && !strcasecmp(base, "Sent")) {
1377 new_item->stype = F_OUTBOX;
1378 folder->outbox = new_item;
1379 } else if (!folder->draft && !strcasecmp(base, "Drafts")) {
1380 new_item->stype = F_DRAFT;
1381 folder->draft = new_item;
1382 } else if (!folder->queue && !strcasecmp(base, "Queue")) {
1383 new_item->stype = F_QUEUE;
1384 folder->queue = new_item;
1385 } else if (!folder->trash && !strcasecmp(base, "Trash")) {
1386 new_item->stype = F_TRASH;
1387 folder->trash = new_item;
1391 if (new_item->no_sub == FALSE)
1392 imap_scan_tree_recursive(session, new_item);
1395 g_slist_free(item_list);
1397 return IMAP_SUCCESS;
1400 static GSList *imap_parse_list(IMAPFolder *folder, IMAPSession *session,
1401 const gchar *real_path, gchar *separator)
1403 gchar buf[IMAPBUFSIZE];
1405 gchar separator_str[16];
1408 gchar *loc_name, *loc_path;
1409 GSList *item_list = NULL;
1411 FolderItem *new_item;
1413 debug_print("getting list of %s ...\n",
1414 *real_path ? real_path : "\"\"");
1416 str = g_string_new(NULL);
1419 if (sock_gets(SESSION(session)->sock, buf, sizeof(buf)) <= 0) {
1420 log_warning(_("error occurred while getting LIST.\n"));
1424 if (buf[0] != '*' || buf[1] != ' ') {
1425 log_print("IMAP4< %s\n", buf);
1426 if (sscanf(buf, "%*d %16s", buf) < 1 ||
1427 strcmp(buf, "OK") != 0)
1428 log_warning(_("error occurred while getting LIST.\n"));
1432 debug_print("IMAP4< %s\n", buf);
1434 g_string_assign(str, buf);
1436 if (strncmp(p, "LIST ", 5) != 0) continue;
1439 if (*p != '(') continue;
1441 p = strchr_cpy(p, ')', flags, sizeof(flags));
1443 while (*p == ' ') p++;
1445 p = strchr_cpy(p, ' ', separator_str, sizeof(separator_str));
1447 extract_quote(separator_str, '"');
1448 if (!strcmp(separator_str, "NIL"))
1449 separator_str[0] = '\0';
1451 *separator = separator_str[0];
1454 while (*p == ' ') p++;
1455 if (*p == '{' || *p == '"')
1456 p = imap_parse_atom(SESSION(session)->sock, p,
1457 buf, sizeof(buf), str);
1459 strncpy2(buf, p, sizeof(buf));
1460 strtailchomp(buf, separator_str[0]);
1461 if (buf[0] == '\0') continue;
1462 if (!strcmp(buf, real_path)) continue;
1464 if (separator_str[0] != '\0')
1465 subst_char(buf, separator_str[0], '/');
1466 name = g_basename(buf);
1467 if (name[0] == '.') continue;
1469 loc_name = imap_modified_utf7_to_locale(name);
1470 loc_path = imap_modified_utf7_to_locale(buf);
1471 new_item = folder_item_new(FOLDER(folder), loc_name, loc_path);
1472 if (strcasestr(flags, "\\Noinferiors") != NULL)
1473 new_item->no_sub = TRUE;
1474 if (strcmp(buf, "INBOX") != 0 &&
1475 strcasestr(flags, "\\Noselect") != NULL)
1476 new_item->no_select = TRUE;
1478 item_list = g_slist_append(item_list, new_item);
1480 debug_print("folder '%s' found.\n", loc_path);
1485 g_string_free(str, TRUE);
1490 static gint imap_create_tree(Folder *folder)
1492 g_return_val_if_fail(folder != NULL, -1);
1493 g_return_val_if_fail(folder->node != NULL, -1);
1494 g_return_val_if_fail(folder->node->data != NULL, -1);
1495 g_return_val_if_fail(folder->account != NULL, -1);
1497 imap_scan_tree(folder);
1498 imap_create_missing_folders(folder);
1503 static void imap_create_missing_folders(Folder *folder)
1505 g_return_if_fail(folder != NULL);
1508 folder->inbox = imap_create_special_folder
1509 (folder, F_INBOX, "INBOX");
1511 if (!folder->outbox)
1512 folder->outbox = imap_create_special_folder
1513 (folder, F_OUTBOX, "Sent");
1515 folder->draft = imap_create_special_folder
1516 (folder, F_DRAFT, "Drafts");
1518 folder->queue = imap_create_special_folder
1519 (folder, F_QUEUE, "Queue");
1522 folder->trash = imap_create_special_folder
1523 (folder, F_TRASH, "Trash");
1526 static FolderItem *imap_create_special_folder(Folder *folder,
1527 SpecialFolderItemType stype,
1531 FolderItem *new_item;
1533 g_return_val_if_fail(folder != NULL, NULL);
1534 g_return_val_if_fail(folder->node != NULL, NULL);
1535 g_return_val_if_fail(folder->node->data != NULL, NULL);
1536 g_return_val_if_fail(folder->account != NULL, NULL);
1537 g_return_val_if_fail(name != NULL, NULL);
1539 item = FOLDER_ITEM(folder->node->data);
1540 new_item = imap_create_folder(folder, item, name);
1543 g_warning("Can't create '%s'\n", name);
1544 if (!folder->inbox) return NULL;
1546 new_item = imap_create_folder(folder, folder->inbox, name);
1548 g_warning("Can't create '%s' under INBOX\n", name);
1550 new_item->stype = stype;
1552 new_item->stype = stype;
1557 static gchar *imap_folder_get_path(Folder *folder)
1561 g_return_val_if_fail(folder != NULL, NULL);
1562 g_return_val_if_fail(folder->account != NULL, NULL);
1564 folder_path = g_strconcat(get_imap_cache_dir(),
1566 folder->account->recv_server,
1568 folder->account->userid,
1574 static gchar *imap_item_get_path(Folder *folder, FolderItem *item)
1576 gchar *folder_path, *path;
1578 g_return_val_if_fail(folder != NULL, NULL);
1579 g_return_val_if_fail(item != NULL, NULL);
1580 folder_path = imap_folder_get_path(folder);
1582 g_return_val_if_fail(folder_path != NULL, NULL);
1583 if (folder_path[0] == G_DIR_SEPARATOR) {
1585 path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
1588 path = g_strdup(folder_path);
1591 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1592 folder_path, G_DIR_SEPARATOR_S,
1595 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1598 g_free(folder_path);
1603 static FolderItem *imap_create_folder(Folder *folder, FolderItem *parent,
1606 gchar *dirpath, *imap_path;
1607 IMAPSession *session;
1608 FolderItem *new_item;
1614 g_return_val_if_fail(folder != NULL, NULL);
1615 g_return_val_if_fail(folder->account != NULL, NULL);
1616 g_return_val_if_fail(parent != NULL, NULL);
1617 g_return_val_if_fail(name != NULL, NULL);
1619 session = imap_session_get(folder);
1620 if (!session) return NULL;
1622 if (!folder_item_parent(parent) && strcmp(name, "INBOX") == 0)
1623 dirpath = g_strdup(name);
1624 else if (parent->path)
1625 dirpath = g_strconcat(parent->path, "/", name, NULL);
1626 else if ((p = strchr(name, '/')) != NULL && *(p + 1) != '\0')
1627 dirpath = g_strdup(name);
1628 else if (folder->account->imap_dir && *folder->account->imap_dir) {
1631 Xstrdup_a(imap_dir, folder->account->imap_dir, return NULL);
1632 strtailchomp(imap_dir, '/');
1633 dirpath = g_strconcat(imap_dir, "/", name, NULL);
1635 dirpath = g_strdup(name);
1637 /* keep trailing directory separator to create a folder that contains
1639 imap_path = imap_locale_to_modified_utf7(dirpath);
1640 strtailchomp(dirpath, '/');
1641 Xstrdup_a(new_name, name, {g_free(dirpath); return NULL;});
1642 strtailchomp(new_name, '/');
1643 separator = imap_get_path_separator(IMAP_FOLDER(folder), imap_path);
1644 imap_path_separator_subst(imap_path, separator);
1645 subst_char(new_name, '/', separator);
1647 if (strcmp(name, "INBOX") != 0) {
1650 gboolean exist = FALSE;
1652 argbuf = g_ptr_array_new();
1653 ok = imap_cmd_list(session, NULL, imap_path,
1655 if (ok != IMAP_SUCCESS) {
1656 log_warning(_("can't create mailbox: LIST failed\n"));
1659 ptr_array_free_strings(argbuf);
1660 g_ptr_array_free(argbuf, TRUE);
1664 for (i = 0; i < argbuf->len; i++) {
1666 str = g_ptr_array_index(argbuf, i);
1667 if (!strncmp(str, "LIST ", 5)) {
1672 ptr_array_free_strings(argbuf);
1673 g_ptr_array_free(argbuf, TRUE);
1676 ok = imap_cmd_create(session, imap_path);
1677 if (ok != IMAP_SUCCESS) {
1678 log_warning(_("can't create mailbox\n"));
1686 new_item = folder_item_new(folder, new_name, dirpath);
1687 folder_item_append(parent, new_item);
1691 dirpath = folder_item_get_path(new_item);
1692 if (!is_dir_exist(dirpath))
1693 make_dir_hier(dirpath);
1699 static gint imap_rename_folder(Folder *folder, FolderItem *item,
1704 gchar *real_oldpath;
1705 gchar *real_newpath;
1707 gchar *old_cache_dir;
1708 gchar *new_cache_dir;
1709 IMAPSession *session;
1712 gint exists, recent, unseen;
1713 guint32 uid_validity;
1715 g_return_val_if_fail(folder != NULL, -1);
1716 g_return_val_if_fail(item != NULL, -1);
1717 g_return_val_if_fail(item->path != NULL, -1);
1718 g_return_val_if_fail(name != NULL, -1);
1720 if (strchr(name, imap_get_path_separator(folder, item->path)) != NULL) {
1721 g_warning(_("New folder name must not contain the namespace "
1726 session = imap_session_get(folder);
1727 if (!session) return -1;
1729 real_oldpath = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1731 g_free(session->mbox);
1732 session->mbox = NULL;
1733 ok = imap_cmd_examine(session, "INBOX",
1734 &exists, &recent, &unseen, &uid_validity);
1735 if (ok != IMAP_SUCCESS) {
1736 g_free(real_oldpath);
1740 separator = imap_get_path_separator(IMAP_FOLDER(folder), item->path);
1741 if (strchr(item->path, G_DIR_SEPARATOR)) {
1742 dirpath = g_dirname(item->path);
1743 newpath = g_strconcat(dirpath, G_DIR_SEPARATOR_S, name, NULL);
1746 newpath = g_strdup(name);
1748 real_newpath = imap_locale_to_modified_utf7(newpath);
1749 imap_path_separator_subst(real_newpath, separator);
1751 ok = imap_cmd_rename(session, real_oldpath, real_newpath);
1752 if (ok != IMAP_SUCCESS) {
1753 log_warning(_("can't rename mailbox: %s to %s\n"),
1754 real_oldpath, real_newpath);
1755 g_free(real_oldpath);
1757 g_free(real_newpath);
1762 item->name = g_strdup(name);
1764 old_cache_dir = folder_item_get_path(item);
1766 paths[0] = g_strdup(item->path);
1768 g_node_traverse(item->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1769 imap_rename_folder_func, paths);
1771 if (is_dir_exist(old_cache_dir)) {
1772 new_cache_dir = folder_item_get_path(item);
1773 if (rename(old_cache_dir, new_cache_dir) < 0) {
1774 FILE_OP_ERROR(old_cache_dir, "rename");
1776 g_free(new_cache_dir);
1779 g_free(old_cache_dir);
1782 g_free(real_oldpath);
1783 g_free(real_newpath);
1788 static gint imap_remove_folder(Folder *folder, FolderItem *item)
1791 IMAPSession *session;
1794 gint exists, recent, unseen;
1795 guint32 uid_validity;
1797 g_return_val_if_fail(folder != NULL, -1);
1798 g_return_val_if_fail(item != NULL, -1);
1799 g_return_val_if_fail(item->path != NULL, -1);
1801 session = imap_session_get(folder);
1802 if (!session) return -1;
1804 path = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1806 ok = imap_cmd_examine(session, "INBOX",
1807 &exists, &recent, &unseen, &uid_validity);
1808 if (ok != IMAP_SUCCESS) {
1813 ok = imap_cmd_delete(session, path);
1814 if (ok != IMAP_SUCCESS) {
1815 log_warning(_("can't delete mailbox\n"));
1821 cache_dir = folder_item_get_path(item);
1822 if (is_dir_exist(cache_dir) && remove_dir_recursive(cache_dir) < 0)
1823 g_warning("can't remove directory '%s'\n", cache_dir);
1825 folder_item_remove(item);
1830 static GSList *imap_get_uncached_messages(IMAPSession *session,
1832 MsgNumberList *numlist)
1835 GSList *newlist = NULL;
1836 GSList *llast = NULL;
1839 GSList *seq_list, *cur;
1842 g_return_val_if_fail(session != NULL, NULL);
1843 g_return_val_if_fail(item != NULL, NULL);
1844 g_return_val_if_fail(item->folder != NULL, NULL);
1845 g_return_val_if_fail(FOLDER_CLASS(item->folder) == &imap_class, NULL);
1847 seq_list = imap_get_seq_set_from_numlist(numlist);
1848 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
1849 imapset = cur->data;
1851 if (imap_cmd_envelope(session, imapset)
1853 log_warning(_("can't get envelope\n"));
1857 str = g_string_new(NULL);
1860 if ((tmp = sock_getline(SESSION(session)->sock)) == NULL) {
1861 log_warning(_("error occurred while getting envelope.\n"));
1862 g_string_free(str, TRUE);
1866 if (tmp[0] != '*' || tmp[1] != ' ') {
1867 log_print("IMAP4< %s\n", tmp);
1871 if (strstr(tmp, "FETCH") == NULL) {
1872 log_print("IMAP4< %s\n", tmp);
1876 log_print("IMAP4< %s\n", tmp);
1877 g_string_assign(str, tmp);
1880 msginfo = imap_parse_envelope
1881 (SESSION(session)->sock, item, str);
1883 log_warning(_("can't parse envelope: %s\n"), str->str);
1886 if (item->stype == F_QUEUE) {
1887 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_QUEUED);
1888 } else if (item->stype == F_DRAFT) {
1889 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_DRAFT);
1892 msginfo->folder = item;
1895 llast = newlist = g_slist_append(newlist, msginfo);
1897 llast = g_slist_append(llast, msginfo);
1898 llast = llast->next;
1902 g_string_free(str, TRUE);
1904 imap_seq_set_free(seq_list);
1909 static void imap_delete_all_cached_messages(FolderItem *item)
1913 g_return_if_fail(item != NULL);
1914 g_return_if_fail(item->folder != NULL);
1915 g_return_if_fail(FOLDER_CLASS(item->folder) == &imap_class);
1917 debug_print("Deleting all cached messages...\n");
1919 dir = folder_item_get_path(item);
1920 if (is_dir_exist(dir))
1921 remove_all_numbered_files(dir);
1924 debug_print("done.\n");
1928 static SockInfo *imap_open_tunnel(const gchar *server,
1929 const gchar *tunnelcmd,
1932 static SockInfo *imap_open_tunnel(const gchar *server,
1933 const gchar *tunnelcmd)
1938 if ((sock = sock_connect_cmd(server, tunnelcmd)) == NULL) {
1939 log_warning(_("Can't establish IMAP4 session with: %s\n"),
1944 return imap_init_sock(sock, ssl_type);
1946 return imap_init_sock(sock);
1952 static SockInfo *imap_open(const gchar *server, gushort port,
1955 static SockInfo *imap_open(const gchar *server, gushort port)
1960 if ((sock = sock_connect(server, port)) == NULL) {
1961 log_warning(_("Can't connect to IMAP4 server: %s:%d\n"),
1967 if (ssl_type == SSL_TUNNEL && !ssl_init_socket(sock)) {
1968 log_warning(_("Can't establish IMAP4 session with: %s:%d\n"),
1978 static SockInfo *imap_init_sock(SockInfo *sock, SSLType ssl_type)
1980 static SockInfo *imap_init_sock(SockInfo *sock)
1987 static GList *imap_parse_namespace_str(gchar *str)
1992 IMAPNameSpace *namespace;
1993 GList *ns_list = NULL;
1995 while (*p != '\0') {
1996 /* parse ("#foo" "/") */
1998 while (*p && *p != '(') p++;
1999 if (*p == '\0') break;
2002 while (*p && *p != '"') p++;
2003 if (*p == '\0') break;
2007 while (*p && *p != '"') p++;
2008 if (*p == '\0') break;
2012 while (*p && isspace(*p)) p++;
2013 if (*p == '\0') break;
2014 if (strncmp(p, "NIL", 3) == 0)
2016 else if (*p == '"') {
2019 while (*p && *p != '"') p++;
2020 if (*p == '\0') break;
2025 while (*p && *p != ')') p++;
2026 if (*p == '\0') break;
2029 namespace = g_new(IMAPNameSpace, 1);
2030 namespace->name = g_strdup(name);
2031 namespace->separator = separator ? separator[0] : '\0';
2032 ns_list = g_list_append(ns_list, namespace);
2038 static void imap_parse_namespace(IMAPSession *session, IMAPFolder *folder)
2043 g_return_if_fail(session != NULL);
2044 g_return_if_fail(folder != NULL);
2046 if (folder->ns_personal != NULL ||
2047 folder->ns_others != NULL ||
2048 folder->ns_shared != NULL)
2051 if (!imap_has_capability(session, "NAMESPACE")) {
2052 imap_get_namespace_by_list(session, folder);
2056 if (imap_cmd_namespace(session, &ns_str)
2058 log_warning(_("can't get namespace\n"));
2062 str_array = strsplit_parenthesis(ns_str, '(', ')', 3);
2063 if (str_array == NULL) {
2065 imap_get_namespace_by_list(session, folder);
2069 folder->ns_personal = imap_parse_namespace_str(str_array[0]);
2070 if (str_array[0] && str_array[1])
2071 folder->ns_others = imap_parse_namespace_str(str_array[1]);
2072 if (str_array[0] && str_array[1] && str_array[2])
2073 folder->ns_shared = imap_parse_namespace_str(str_array[2]);
2074 g_strfreev(str_array);
2078 static void imap_get_namespace_by_list(IMAPSession *session, IMAPFolder *folder)
2080 GSList *item_list, *cur;
2081 gchar separator = '\0';
2082 IMAPNameSpace *namespace;
2084 g_return_if_fail(session != NULL);
2085 g_return_if_fail(folder != NULL);
2087 if (folder->ns_personal != NULL ||
2088 folder->ns_others != NULL ||
2089 folder->ns_shared != NULL)
2092 imap_gen_send(session, "LIST \"\" \"\"");
2093 item_list = imap_parse_list(folder, session, "", &separator);
2094 for (cur = item_list; cur != NULL; cur = cur->next)
2095 folder_item_destroy(FOLDER_ITEM(cur->data));
2096 g_slist_free(item_list);
2098 namespace = g_new(IMAPNameSpace, 1);
2099 namespace->name = g_strdup("");
2100 namespace->separator = separator;
2101 folder->ns_personal = g_list_append(NULL, namespace);
2104 static IMAPNameSpace *imap_find_namespace_from_list(GList *ns_list,
2107 IMAPNameSpace *namespace = NULL;
2108 gchar *tmp_path, *name;
2110 if (!path) path = "";
2112 for (; ns_list != NULL; ns_list = ns_list->next) {
2113 IMAPNameSpace *tmp_ns = ns_list->data;
2115 Xstrcat_a(tmp_path, path, "/", return namespace);
2116 Xstrdup_a(name, tmp_ns->name, return namespace);
2117 if (tmp_ns->separator && tmp_ns->separator != '/') {
2118 subst_char(tmp_path, tmp_ns->separator, '/');
2119 subst_char(name, tmp_ns->separator, '/');
2121 if (strncmp(tmp_path, name, strlen(name)) == 0)
2128 static IMAPNameSpace *imap_find_namespace(IMAPFolder *folder,
2131 IMAPNameSpace *namespace;
2133 g_return_val_if_fail(folder != NULL, NULL);
2135 namespace = imap_find_namespace_from_list(folder->ns_personal, path);
2136 if (namespace) return namespace;
2137 namespace = imap_find_namespace_from_list(folder->ns_others, path);
2138 if (namespace) return namespace;
2139 namespace = imap_find_namespace_from_list(folder->ns_shared, path);
2140 if (namespace) return namespace;
2145 static gchar imap_get_path_separator(IMAPFolder *folder, const gchar *path)
2147 IMAPNameSpace *namespace;
2148 gchar separator = '/';
2150 namespace = imap_find_namespace(folder, path);
2151 if (namespace && namespace->separator)
2152 separator = namespace->separator;
2157 static gchar *imap_get_real_path(IMAPFolder *folder, const gchar *path)
2162 g_return_val_if_fail(folder != NULL, NULL);
2163 g_return_val_if_fail(path != NULL, NULL);
2165 real_path = imap_locale_to_modified_utf7(path);
2166 separator = imap_get_path_separator(folder, path);
2167 imap_path_separator_subst(real_path, separator);
2172 static gchar *imap_parse_atom(SockInfo *sock, gchar *src,
2173 gchar *dest, gint dest_len, GString *str)
2175 gchar *cur_pos = src;
2178 g_return_val_if_fail(str != NULL, cur_pos);
2180 /* read the next line if the current response buffer is empty */
2181 while (isspace(*(guchar *)cur_pos)) cur_pos++;
2182 while (*cur_pos == '\0') {
2183 if ((nextline = sock_getline(sock)) == NULL)
2185 g_string_assign(str, nextline);
2187 strretchomp(nextline);
2188 /* log_print("IMAP4< %s\n", nextline); */
2189 debug_print("IMAP4< %s\n", nextline);
2192 while (isspace(*(guchar *)cur_pos)) cur_pos++;
2195 if (!strncmp(cur_pos, "NIL", 3)) {
2198 } else if (*cur_pos == '\"') {
2201 p = get_quoted(cur_pos, '\"', dest, dest_len);
2202 cur_pos = p ? p : cur_pos + 2;
2203 } else if (*cur_pos == '{') {
2208 cur_pos = strchr_cpy(cur_pos + 1, '}', buf, sizeof(buf));
2210 g_return_val_if_fail(len >= 0, cur_pos);
2212 g_string_truncate(str, 0);
2216 if ((nextline = sock_getline(sock)) == NULL)
2218 line_len += strlen(nextline);
2219 g_string_append(str, nextline);
2221 strretchomp(nextline);
2222 /* log_print("IMAP4< %s\n", nextline); */
2223 debug_print("IMAP4< %s\n", nextline);
2225 } while (line_len < len);
2227 memcpy(dest, cur_pos, MIN(len, dest_len - 1));
2228 dest[MIN(len, dest_len - 1)] = '\0';
2235 static gchar *imap_get_header(SockInfo *sock, gchar *cur_pos, gchar **headers,
2245 g_return_val_if_fail(str != NULL, cur_pos);
2247 while (isspace(*(guchar *)cur_pos)) cur_pos++;
2249 g_return_val_if_fail(*cur_pos == '{', cur_pos);
2251 cur_pos = strchr_cpy(cur_pos + 1, '}', buf, sizeof(buf));
2253 g_return_val_if_fail(len >= 0, cur_pos);
2255 g_string_truncate(str, 0);
2259 if ((nextline = sock_getline(sock)) == NULL)
2261 block_len += strlen(nextline);
2262 g_string_append(str, nextline);
2264 strretchomp(nextline);
2265 /* debug_print("IMAP4< %s\n", nextline); */
2267 } while (block_len < len);
2269 debug_print("IMAP4< [contents of BODY.PEEK[HEADER.FIELDS (...)]]\n");
2271 *headers = g_strndup(cur_pos, len);
2274 while (isspace(*(guchar *)cur_pos)) cur_pos++;
2275 while (*cur_pos == '\0') {
2276 if ((nextline = sock_getline(sock)) == NULL)
2278 g_string_assign(str, nextline);
2280 strretchomp(nextline);
2281 debug_print("IMAP4< %s\n", nextline);
2284 while (isspace(*(guchar *)cur_pos)) cur_pos++;
2290 static MsgFlags imap_parse_flags(const gchar *flag_str)
2292 const gchar *p = flag_str;
2293 MsgFlags flags = {0, 0};
2295 flags.perm_flags = MSG_UNREAD;
2297 while ((p = strchr(p, '\\')) != NULL) {
2300 if (g_strncasecmp(p, "Recent", 6) == 0 && MSG_IS_UNREAD(flags)) {
2301 MSG_SET_PERM_FLAGS(flags, MSG_NEW);
2302 } else if (g_strncasecmp(p, "Seen", 4) == 0) {
2303 MSG_UNSET_PERM_FLAGS(flags, MSG_NEW|MSG_UNREAD);
2304 } else if (g_strncasecmp(p, "Deleted", 7) == 0) {
2305 MSG_SET_PERM_FLAGS(flags, MSG_DELETED);
2306 } else if (g_strncasecmp(p, "Flagged", 7) == 0) {
2307 MSG_SET_PERM_FLAGS(flags, MSG_MARKED);
2308 } else if (g_strncasecmp(p, "Answered", 8) == 0) {
2309 MSG_SET_PERM_FLAGS(flags, MSG_REPLIED);
2316 static MsgInfo *imap_parse_envelope(SockInfo *sock, FolderItem *item,
2319 gchar buf[IMAPBUFSIZE];
2320 MsgInfo *msginfo = NULL;
2325 MsgFlags flags = {0, 0}, imap_flags = {0, 0};
2327 g_return_val_if_fail(line_str != NULL, NULL);
2328 g_return_val_if_fail(line_str->str[0] == '*' &&
2329 line_str->str[1] == ' ', NULL);
2331 MSG_SET_TMP_FLAGS(flags, MSG_IMAP);
2332 if (item->stype == F_QUEUE) {
2333 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
2334 } else if (item->stype == F_DRAFT) {
2335 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
2338 cur_pos = line_str->str + 2;
2340 #define PARSE_ONE_ELEMENT(ch) \
2342 cur_pos = strchr_cpy(cur_pos, ch, buf, sizeof(buf)); \
2343 if (cur_pos == NULL) { \
2344 g_warning("cur_pos == NULL\n"); \
2345 procmsg_msginfo_free(msginfo); \
2350 PARSE_ONE_ELEMENT(' ');
2353 PARSE_ONE_ELEMENT(' ');
2354 g_return_val_if_fail(!strcmp(buf, "FETCH"), NULL);
2356 g_return_val_if_fail(*cur_pos == '(', NULL);
2359 while (*cur_pos != '\0' && *cur_pos != ')') {
2360 while (*cur_pos == ' ') cur_pos++;
2362 if (!strncmp(cur_pos, "UID ", 4)) {
2364 uid = strtoul(cur_pos, &cur_pos, 10);
2365 } else if (!strncmp(cur_pos, "FLAGS ", 6)) {
2367 if (*cur_pos != '(') {
2368 g_warning("*cur_pos != '('\n");
2369 procmsg_msginfo_free(msginfo);
2373 PARSE_ONE_ELEMENT(')');
2374 imap_flags = imap_parse_flags(buf);
2375 } else if (!strncmp(cur_pos, "RFC822.SIZE ", 12)) {
2377 size = strtol(cur_pos, &cur_pos, 10);
2378 } else if (!strncmp(cur_pos, "BODY[HEADER.FIELDS ", 19)) {
2382 if (*cur_pos != '(') {
2383 g_warning("*cur_pos != '('\n");
2384 procmsg_msginfo_free(msginfo);
2388 PARSE_ONE_ELEMENT(')');
2389 if (*cur_pos != ']') {
2390 g_warning("*cur_pos != ']'\n");
2391 procmsg_msginfo_free(msginfo);
2396 cur_pos = imap_get_header(sock, cur_pos, &headers,
2398 msginfo = procheader_parse_str(headers, flags, FALSE, FALSE);
2401 g_warning("invalid FETCH response: %s\n", cur_pos);
2407 msginfo->msgnum = uid;
2408 msginfo->size = size;
2409 msginfo->flags.tmp_flags |= imap_flags.tmp_flags;
2410 msginfo->flags.perm_flags = imap_flags.perm_flags;
2416 static gchar *imap_get_flag_str(IMAPFlags flags)
2421 str = g_string_new(NULL);
2423 if (IMAP_IS_SEEN(flags)) g_string_append(str, "\\Seen ");
2424 if (IMAP_IS_ANSWERED(flags)) g_string_append(str, "\\Answered ");
2425 if (IMAP_IS_FLAGGED(flags)) g_string_append(str, "\\Flagged ");
2426 if (IMAP_IS_DELETED(flags)) g_string_append(str, "\\Deleted ");
2427 if (IMAP_IS_DRAFT(flags)) g_string_append(str, "\\Draft");
2429 if (str->len > 0 && str->str[str->len - 1] == ' ')
2430 g_string_truncate(str, str->len - 1);
2433 g_string_free(str, FALSE);
2438 static gint imap_set_message_flags(IMAPSession *session,
2439 MsgNumberList *numlist,
2446 GSList *seq_list, *cur;
2449 flag_str = imap_get_flag_str(flags);
2450 cmd = g_strconcat(is_set ? "+FLAGS.SILENT (" : "-FLAGS.SILENT (",
2451 flag_str, ")", NULL);
2454 seq_list = imap_get_seq_set_from_numlist(numlist);
2455 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
2456 imapset = cur->data;
2458 ok = imap_cmd_store(session, imapset, cmd);
2460 imap_seq_set_free(seq_list);
2466 static gint imap_select(IMAPSession *session, IMAPFolder *folder,
2468 gint *exists, gint *recent, gint *unseen,
2469 guint32 *uid_validity)
2473 gint exists_, recent_, unseen_, 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)
2726 *exists = *recent = *unseen = *uid_validity = 0;
2727 argbuf = g_ptr_array_new();
2730 select_cmd = "EXAMINE";
2732 select_cmd = "SELECT";
2734 QUOTE_IF_REQUIRED(folder_, folder);
2735 imap_gen_send(session, "%s %s", select_cmd, folder_);
2737 if ((ok = imap_cmd_ok(session, argbuf)) != IMAP_SUCCESS) THROW;
2739 resp_str = search_array_contain_str(argbuf, "EXISTS");
2741 if (sscanf(resp_str,"%d EXISTS", exists) != 1) {
2742 g_warning("imap_cmd_select(): invalid EXISTS line.\n");
2747 resp_str = search_array_contain_str(argbuf, "RECENT");
2749 if (sscanf(resp_str, "%d RECENT", recent) != 1) {
2750 g_warning("imap_cmd_select(): invalid RECENT line.\n");
2755 resp_str = search_array_contain_str(argbuf, "UIDVALIDITY");
2757 if (sscanf(resp_str, "OK [UIDVALIDITY %u] ", uid_validity)
2759 g_warning("imap_cmd_select(): invalid UIDVALIDITY line.\n");
2764 resp_str = search_array_contain_str(argbuf, "UNSEEN");
2766 if (sscanf(resp_str, "OK [UNSEEN %d] ", unseen) != 1) {
2767 g_warning("imap_cmd_select(): invalid UNSEEN line.\n");
2773 ptr_array_free_strings(argbuf);
2774 g_ptr_array_free(argbuf, TRUE);
2779 static gint imap_cmd_select(IMAPSession *session, const gchar *folder,
2780 gint *exists, gint *recent, gint *unseen,
2781 guint32 *uid_validity)
2783 return imap_cmd_do_select(session, folder, FALSE,
2784 exists, recent, unseen, uid_validity);
2787 static gint imap_cmd_examine(IMAPSession *session, const gchar *folder,
2788 gint *exists, gint *recent, gint *unseen,
2789 guint32 *uid_validity)
2791 return imap_cmd_do_select(session, folder, TRUE,
2792 exists, recent, unseen, uid_validity);
2797 static gint imap_cmd_create(IMAPSession *session, const gchar *folder)
2801 QUOTE_IF_REQUIRED(folder_, folder);
2802 imap_gen_send(session, "CREATE %s", folder_);
2804 return imap_cmd_ok(session, NULL);
2807 static gint imap_cmd_rename(IMAPSession *session, const gchar *old_folder,
2808 const gchar *new_folder)
2810 gchar *old_folder_, *new_folder_;
2812 QUOTE_IF_REQUIRED(old_folder_, old_folder);
2813 QUOTE_IF_REQUIRED(new_folder_, new_folder);
2814 imap_gen_send(session, "RENAME %s %s", old_folder_, new_folder_);
2816 return imap_cmd_ok(session, NULL);
2819 static gint imap_cmd_delete(IMAPSession *session, const gchar *folder)
2823 QUOTE_IF_REQUIRED(folder_, folder);
2824 imap_gen_send(session, "DELETE %s", folder_);
2826 return imap_cmd_ok(session, NULL);
2829 static gint imap_cmd_search(IMAPSession *session, const gchar *criteria,
2836 g_return_val_if_fail(criteria != NULL, IMAP_ERROR);
2837 g_return_val_if_fail(list != NULL, IMAP_ERROR);
2841 argbuf = g_ptr_array_new();
2842 imap_gen_send(session, "UID SEARCH %s", criteria);
2844 ok = imap_cmd_ok(session, argbuf);
2845 if (ok != IMAP_SUCCESS) {
2846 ptr_array_free_strings(argbuf);
2847 g_ptr_array_free(argbuf, TRUE);
2851 if ((uidlist = search_array_str(argbuf, "SEARCH ")) != NULL) {
2852 gchar **strlist, **p;
2854 strlist = g_strsplit(uidlist + 7, " ", 0);
2855 for (p = strlist; *p != NULL; ++p) {
2858 if (sscanf(*p, "%d", &msgnum) == 1)
2859 *list = g_slist_append(*list, GINT_TO_POINTER(msgnum));
2861 g_strfreev(strlist);
2863 ptr_array_free_strings(argbuf);
2864 g_ptr_array_free(argbuf, TRUE);
2866 return IMAP_SUCCESS;
2869 static gint imap_cmd_fetch(IMAPSession *session, guint32 uid,
2870 const gchar *filename)
2878 g_return_val_if_fail(filename != NULL, IMAP_ERROR);
2880 imap_gen_send(session, "UID FETCH %d BODY.PEEK[]", uid);
2882 while ((ok = imap_gen_recv(session, &buf)) == IMAP_SUCCESS) {
2883 if (buf[0] != '*' || buf[1] != ' ') {
2887 if (strstr(buf, "FETCH") != NULL) break;
2890 if (ok != IMAP_SUCCESS) {
2895 #define RETURN_ERROR_IF_FAIL(cond) \
2898 return IMAP_ERROR; \
2901 cur_pos = strchr(buf, '{');
2902 RETURN_ERROR_IF_FAIL(cur_pos != NULL);
2903 cur_pos = strchr_cpy(cur_pos + 1, '}', size_str, sizeof(size_str));
2904 RETURN_ERROR_IF_FAIL(cur_pos != NULL);
2905 size_num = atol(size_str);
2906 RETURN_ERROR_IF_FAIL(size_num >= 0);
2908 RETURN_ERROR_IF_FAIL(*cur_pos == '\0');
2910 #undef RETURN_ERROR_IF_FAIL
2914 if (recv_bytes_write_to_file(SESSION(session)->sock,
2915 size_num, filename) != 0)
2918 if (imap_gen_recv(session, &buf) != IMAP_SUCCESS) {
2923 if (buf[0] == '\0' || buf[strlen(buf) - 1] != ')') {
2929 ok = imap_cmd_ok(session, NULL);
2934 static gint imap_cmd_append(IMAPSession *session, const gchar *destfolder,
2935 const gchar *file, IMAPFlags flags,
2944 gchar buf[BUFFSIZE];
2949 g_return_val_if_fail(file != NULL, IMAP_ERROR);
2951 size = get_file_size_as_crlf(file);
2952 if ((fp = fopen(file, "rb")) == NULL) {
2953 FILE_OP_ERROR(file, "fopen");
2956 QUOTE_IF_REQUIRED(destfolder_, destfolder);
2957 flag_str = imap_get_flag_str(flags);
2958 imap_gen_send(session, "APPEND %s (%s) {%d}",
2959 destfolder_, flag_str, size);
2962 ok = imap_gen_recv(session, &ret);
2963 if (ok != IMAP_SUCCESS || ret[0] != '+' || ret[1] != ' ') {
2964 log_warning(_("can't append %s to %s\n"), file, destfolder_);
2971 log_print("IMAP4> %s\n", _("(sending file...)"));
2973 while (fgets(buf, sizeof(buf), fp) != NULL) {
2975 if (sock_puts(SESSION(session)->sock, buf) < 0) {
2982 FILE_OP_ERROR(file, "fgets");
2987 sock_puts(SESSION(session)->sock, "");
2991 if (new_uid != NULL)
2994 if (new_uid != NULL && session->uidplus) {
2995 argbuf = g_ptr_array_new();
2997 ok = imap_cmd_ok(session, argbuf);
2998 if ((ok == IMAP_SUCCESS) && (argbuf->len > 0)) {
2999 resp_str = g_ptr_array_index(argbuf, argbuf->len - 1);
3001 sscanf(resp_str, "%*u OK [APPENDUID %*u %u]",
3003 *new_uid = new_uid_;
3007 ptr_array_free_strings(argbuf);
3008 g_ptr_array_free(argbuf, TRUE);
3010 ok = imap_cmd_ok(session, NULL);
3012 if (ok != IMAP_SUCCESS)
3013 log_warning(_("can't append message to %s\n"),
3019 static MsgNumberList *imapset_to_numlist(IMAPSet imapset)
3021 gchar **ranges, **range;
3023 MsgNumberList *uids = NULL;
3025 ranges = g_strsplit(imapset, ",", 0);
3026 for (range = ranges; *range != NULL; range++) {
3027 printf("%s\n", *range);
3028 if(sscanf(*range, "%u:%u", &low, &high) == 1)
3029 uids = g_slist_prepend(uids, GINT_TO_POINTER(low));
3032 for (i = low; i <= high; i++)
3033 uids = g_slist_prepend(uids, GINT_TO_POINTER(i));
3036 uids = g_slist_reverse(uids);
3042 static gint imap_cmd_copy(IMAPSession *session, const gchar *seq_set,
3043 const gchar *destfolder, GRelation *uid_mapping)
3048 g_return_val_if_fail(session != NULL, IMAP_ERROR);
3049 g_return_val_if_fail(seq_set != NULL, IMAP_ERROR);
3050 g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
3052 QUOTE_IF_REQUIRED(destfolder_, destfolder);
3053 imap_gen_send(session, "UID COPY %s %s", seq_set, destfolder_);
3055 if (uid_mapping != NULL && session->uidplus) {
3057 gchar *resp_str = NULL, *olduids_str, *newuids_str;
3058 MsgNumberList *olduids, *old_cur, *newuids, *new_cur;
3060 reply = g_ptr_array_new();
3061 ok = imap_cmd_ok(session, reply);
3062 if ((ok == IMAP_SUCCESS) && (reply->len > 0)) {
3063 resp_str = g_ptr_array_index(reply, reply->len - 1);
3065 olduids_str = g_new0(gchar, strlen(resp_str));
3066 newuids_str = g_new0(gchar, strlen(resp_str));
3067 if (sscanf(resp_str, "%*s OK [COPYUID %*u %[0-9,:] %[0-9,:]]",
3068 olduids_str, newuids_str) == 2) {
3069 olduids = imapset_to_numlist(olduids_str);
3070 newuids = imapset_to_numlist(newuids_str);
3074 while(old_cur != NULL && new_cur != NULL) {
3075 g_relation_insert(uid_mapping,
3076 GPOINTER_TO_INT(old_cur->data),
3077 GPOINTER_TO_INT(new_cur->data));
3078 old_cur = g_slist_next(old_cur);
3079 new_cur = g_slist_next(new_cur);
3082 g_slist_free(olduids);
3083 g_slist_free(newuids);
3085 g_free(olduids_str);
3086 g_free(newuids_str);
3089 ptr_array_free_strings(reply);
3090 g_ptr_array_free(reply, TRUE);
3092 ok = imap_cmd_ok(session, NULL);
3094 if (ok != IMAP_SUCCESS)
3095 log_warning(_("can't copy %s to %s\n"), seq_set, destfolder_);
3100 gint imap_cmd_envelope(IMAPSession *session, IMAPSet set)
3102 static GString *header_fields = NULL;
3104 if (header_fields == NULL) {
3105 const HeaderEntry *headers, *elem;
3107 headers = procheader_get_headernames(FALSE);
3108 header_fields = g_string_new("");
3110 for (elem = headers; elem->name != NULL; ++elem) {
3111 gint namelen = strlen(elem->name);
3113 /* Header fields ending with space are not rfc822 headers */
3114 if (elem->name[namelen - 1] == ' ')
3117 /* strip : at the of header field */
3118 if(elem->name[namelen - 1] == ':')
3124 g_string_sprintfa(header_fields, "%s%.*s",
3125 header_fields->str[0] != '\0' ? " " : "",
3126 namelen, elem->name);
3131 (session, "UID FETCH %s (UID FLAGS RFC822.SIZE BODY.PEEK[HEADER.FIELDS (%s)])",
3132 set, header_fields->str);
3134 return IMAP_SUCCESS;
3137 static gint imap_cmd_store(IMAPSession *session, IMAPSet seq_set,
3142 imap_gen_send(session, "UID STORE %s %s", seq_set, sub_cmd);
3144 if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS) {
3145 log_warning(_("error while imap command: STORE %s %s\n"),
3150 return IMAP_SUCCESS;
3153 static gint imap_cmd_expunge(IMAPSession *session, IMAPSet seq_set)
3157 if (seq_set && session->uidplus)
3158 imap_gen_send(session, "UID EXPUNGE %s", seq_set);
3160 imap_gen_send(session, "EXPUNGE");
3161 if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS) {
3162 log_warning(_("error while imap command: EXPUNGE\n"));
3166 return IMAP_SUCCESS;
3169 static gint imap_cmd_close(IMAPSession *session)
3173 imap_gen_send(session, "CLOSE");
3174 if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS)
3175 log_warning(_("error while imap command: CLOSE\n"));
3180 static gint imap_cmd_ok(IMAPSession *session, GPtrArray *argbuf)
3182 gint ok = IMAP_SUCCESS;
3187 while ((ok = imap_gen_recv(session, &buf))
3189 // make sure data is long enough for any substring of buf
3190 data = alloca(strlen(buf) + 1);
3192 // untagged line read
3193 if (buf[0] == '*' && buf[1] == ' ') {
3196 g_ptr_array_add(argbuf, g_strdup(buf + 2));
3198 if (sscanf(buf + 2, "%d %s", &num, data) >= 2) {
3199 if (!strcmp(data, "EXISTS")) {
3200 session->exists = num;
3201 session->folder_content_changed = TRUE;
3204 if(!strcmp(data, "EXPUNGE")) {
3206 session->folder_content_changed = TRUE;
3209 // tagged line with correct tag and OK response found
3210 } else if ((sscanf(buf, "%d %s", &cmd_num, data) >= 2) &&
3211 (cmd_num == session->cmd_count) &&
3212 !strcmp(data, "OK")) {
3214 g_ptr_array_add(argbuf, g_strdup(buf));
3228 static void imap_gen_send(IMAPSession *session, const gchar *format, ...)
3235 va_start(args, format);
3236 tmp = g_strdup_vprintf(format, args);
3239 session->cmd_count++;
3241 buf = g_strdup_printf("%d %s\r\n", session->cmd_count, tmp);
3242 if (!strncasecmp(tmp, "LOGIN ", 6) && (p = strchr(tmp + 6, ' '))) {
3244 log_print("IMAP4> %d %s ********\n", session->cmd_count, tmp);
3246 log_print("IMAP4> %d %s\n", session->cmd_count, tmp);
3248 sock_write_all(SESSION(session)->sock, buf, strlen(buf));
3253 static gint imap_gen_recv(IMAPSession *session, gchar **ret)
3255 if ((*ret = sock_getline(SESSION(session)->sock)) == NULL)
3260 log_print("IMAP4< %s\n", *ret);
3262 return IMAP_SUCCESS;
3266 /* misc utility functions */
3268 static gchar *strchr_cpy(const gchar *src, gchar ch, gchar *dest, gint len)
3273 tmp = strchr(src, ch);
3277 memcpy(dest, src, MIN(tmp - src, len - 1));
3278 dest[MIN(tmp - src, len - 1)] = '\0';
3283 static gchar *get_quoted(const gchar *src, gchar ch, gchar *dest, gint len)
3285 const gchar *p = src;
3288 g_return_val_if_fail(*p == ch, NULL);
3293 while (*p != '\0' && *p != ch) {
3295 if (*p == '\\' && *(p + 1) != '\0')
3304 return (gchar *)(*p == ch ? p + 1 : p);
3307 static gchar *search_array_contain_str(GPtrArray *array, const gchar *str)
3311 for (i = 0; i < array->len; i++) {
3314 tmp = g_ptr_array_index(array, i);
3315 if (strstr(tmp, str) != NULL)
3322 static gchar *search_array_str(GPtrArray *array, const gchar *str)
3329 for (i = 0; i < array->len; i++) {
3332 tmp = g_ptr_array_index(array, i);
3333 if (!strncmp(tmp, str, len))
3340 static void imap_path_separator_subst(gchar *str, gchar separator)
3343 gboolean in_escape = FALSE;
3345 if (!separator || separator == '/') return;
3347 for (p = str; *p != '\0'; p++) {
3348 if (*p == '/' && !in_escape)
3350 else if (*p == '&' && *(p + 1) != '-' && !in_escape)
3352 else if (*p == '-' && in_escape)
3357 static gchar *imap_modified_utf7_to_locale(const gchar *mutf7_str)
3360 const gchar *from_p;
3363 to = g_malloc(strlen(mutf7_str) + 1);
3366 for (from_p = mutf7_str; *from_p != '\0'; from_p++) {
3367 if (*from_p == '&' && *(from_p + 1) == '-') {
3377 static iconv_t cd = (iconv_t)-1;
3378 static gboolean iconv_ok = TRUE;
3381 size_t norm_utf7_len;
3383 gchar *to_str, *to_p;
3385 gboolean in_escape = FALSE;
3387 if (!iconv_ok) return g_strdup(mutf7_str);
3389 if (cd == (iconv_t)-1) {
3390 cd = iconv_open(conv_get_current_charset_str(), "UTF-7");
3391 if (cd == (iconv_t)-1) {
3392 g_warning("iconv cannot convert UTF-7 to %s\n",
3393 conv_get_current_charset_str());
3395 return g_strdup(mutf7_str);
3399 norm_utf7 = g_string_new(NULL);
3401 for (p = mutf7_str; *p != '\0'; p++) {
3402 /* replace: '&' -> '+',
3404 escaped ',' -> '/' */
3405 if (!in_escape && *p == '&') {
3406 if (*(p + 1) != '-') {
3407 g_string_append_c(norm_utf7, '+');
3410 g_string_append_c(norm_utf7, '&');
3413 } else if (in_escape && *p == ',') {
3414 g_string_append_c(norm_utf7, '/');
3415 } else if (in_escape && *p == '-') {
3416 g_string_append_c(norm_utf7, '-');
3419 g_string_append_c(norm_utf7, *p);
3423 norm_utf7_p = norm_utf7->str;
3424 norm_utf7_len = norm_utf7->len;
3425 to_len = strlen(mutf7_str) * 5;
3426 to_p = to_str = g_malloc(to_len + 1);
3428 if (iconv(cd, (ICONV_CONST gchar **)&norm_utf7_p, &norm_utf7_len,
3429 &to_p, &to_len) == -1) {
3430 g_warning(_("iconv cannot convert UTF-7 to %s\n"),
3431 conv_get_current_charset_str());
3432 g_string_free(norm_utf7, TRUE);
3434 return g_strdup(mutf7_str);
3437 /* second iconv() call for flushing */
3438 iconv(cd, NULL, NULL, &to_p, &to_len);
3439 g_string_free(norm_utf7, TRUE);
3443 #endif /* !HAVE_ICONV */
3446 static gchar *imap_locale_to_modified_utf7(const gchar *from)
3449 const gchar *from_p;
3452 to = g_malloc(strlen(from) * 2 + 1);
3455 for (from_p = from; *from_p != '\0'; from_p++) {
3456 if (*from_p == '&') {
3466 static iconv_t cd = (iconv_t)-1;
3467 static gboolean iconv_ok = TRUE;
3468 gchar *norm_utf7, *norm_utf7_p;
3469 size_t from_len, norm_utf7_len;
3471 gchar *from_tmp, *to, *p;
3472 gboolean in_escape = FALSE;
3474 if (!iconv_ok) return g_strdup(from);
3476 if (cd == (iconv_t)-1) {
3477 cd = iconv_open("UTF-7", conv_get_current_charset_str());
3478 if (cd == (iconv_t)-1) {
3479 g_warning("iconv cannot convert %s to UTF-7\n",
3480 conv_get_current_charset_str());
3482 return g_strdup(from);
3486 Xstrdup_a(from_tmp, from, return g_strdup(from));
3487 from_len = strlen(from);
3488 norm_utf7_len = from_len * 5;
3489 Xalloca(norm_utf7, norm_utf7_len + 1, return g_strdup(from));
3490 norm_utf7_p = norm_utf7;
3492 #define IS_PRINT(ch) (isprint(ch) && isascii(ch))
3494 while (from_len > 0) {
3495 if (*from_tmp == '+') {
3496 *norm_utf7_p++ = '+';
3497 *norm_utf7_p++ = '-';
3501 } else if (IS_PRINT(*(guchar *)from_tmp)) {
3502 /* printable ascii char */
3503 *norm_utf7_p = *from_tmp;
3509 size_t mb_len = 0, conv_len = 0;
3511 /* unprintable char: convert to UTF-7 */
3513 while (!IS_PRINT(*(guchar *)p) && conv_len < from_len) {
3514 mb_len = mblen(p, MB_LEN_MAX);
3516 g_warning("wrong multibyte sequence\n");
3517 return g_strdup(from);
3523 from_len -= conv_len;
3524 if (iconv(cd, (ICONV_CONST gchar **)&from_tmp,
3526 &norm_utf7_p, &norm_utf7_len) == -1) {
3527 g_warning("iconv cannot convert %s to UTF-7\n",
3528 conv_get_current_charset_str());
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;
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;
4104 GSList *unseen = NULL, *answered = NULL, *flagged = NULL, *deleted = NULL;
4105 GSList *p_unseen, *p_answered, *p_flagged, *p_deleted;
4107 GSList *seq_list, *cur;
4108 gboolean reverse_seen = FALSE;
4111 gint exists_cnt, recent_cnt, unseen_cnt, uid_next;
4112 guint32 uidvalidity;
4114 g_return_val_if_fail(folder != NULL, -1);
4115 g_return_val_if_fail(item != NULL, -1);
4116 g_return_val_if_fail(msginfo_list != NULL, -1);
4118 session = imap_session_get(folder);
4119 g_return_val_if_fail(session != NULL, -1);
4121 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
4122 NULL, NULL, NULL, NULL);
4123 if (ok != IMAP_SUCCESS)
4126 ok = imap_status(session, IMAP_FOLDER(folder), item->path,
4127 &exists_cnt, &recent_cnt, &uid_next, &uidvalidity, &unseen_cnt);
4129 if (unseen_cnt > exists_cnt / 2)
4130 reverse_seen = TRUE;
4132 cmd_buf = g_string_new(NULL);
4134 sorted_list = g_slist_sort(g_slist_copy(msginfo_list), compare_msginfo);
4136 seq_list = imap_get_seq_set_from_msglist(msginfo_list);
4138 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
4139 IMAPSet imapset = cur->data;
4141 g_string_sprintf(cmd_buf, "RECENT UID %s", imapset);
4142 imap_cmd_search(session, cmd_buf->str, &p_new);
4143 new = g_slist_concat(new, p_new);
4145 g_string_sprintf(cmd_buf, "%sSEEN UID %s", reverse_seen ? "" : "UN", imapset);
4146 imap_cmd_search(session, cmd_buf->str, &p_unseen);
4147 unseen = g_slist_concat(unseen, p_unseen);
4149 g_string_sprintf(cmd_buf, "ANSWERED UID %s", imapset);
4150 imap_cmd_search(session, cmd_buf->str, &p_answered);
4151 answered = g_slist_concat(answered, p_answered);
4153 g_string_sprintf(cmd_buf, "FLAGGED UID %s", imapset);
4154 imap_cmd_search(session, cmd_buf->str, &p_flagged);
4155 flagged = g_slist_concat(flagged, p_flagged);
4157 g_string_sprintf(cmd_buf, "DELETED UID %s", imapset);
4158 imap_cmd_search(session, cmd_buf->str, &p_deleted);
4159 deleted = g_slist_concat(deleted, p_deleted);
4167 p_answered = answered;
4168 p_flagged = flagged;
4169 p_deleted = deleted;
4171 for (elem = sorted_list; elem != NULL; elem = g_slist_next(elem)) {
4175 msginfo = (MsgInfo *) elem->data;
4176 flags = msginfo->flags.perm_flags;
4177 flags &= ~((reverse_seen ? 0 : MSG_UNREAD) | MSG_REPLIED | MSG_MARKED);
4179 flags |= MSG_UNREAD;
4181 if (gslist_find_next_num(&p_new, msginfo->msgnum) == msginfo->msgnum)
4184 if (gslist_find_next_num(&p_unseen, msginfo->msgnum) == msginfo->msgnum) {
4185 if (!reverse_seen) {
4186 flags |= MSG_UNREAD;
4188 flags &= ~(MSG_UNREAD | MSG_NEW);
4191 if (gslist_find_next_num(&p_answered, msginfo->msgnum) == msginfo->msgnum)
4192 flags |= MSG_REPLIED;
4193 if (gslist_find_next_num(&p_flagged, msginfo->msgnum) == msginfo->msgnum)
4194 flags |= MSG_MARKED;
4196 if (gslist_find_next_num(&p_deleted, msginfo->msgnum) == msginfo->msgnum)
4197 MSG_SET_PERM_FLAGS(msginfo->flags, MSG_DELETED);
4199 g_relation_insert(msgflags, msginfo, GINT_TO_POINTER(flags));
4202 imap_seq_set_free(seq_list);
4203 g_slist_free(deleted);
4204 g_slist_free(flagged);
4205 g_slist_free(answered);
4206 g_slist_free(unseen);
4207 /* new not freed in original patch ??? */
4208 g_slist_free(sorted_list);
4209 g_string_free(cmd_buf, TRUE);