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 session = imap_session_get(folder);
1721 if (!session) return -1;
1723 real_oldpath = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1725 g_free(session->mbox);
1726 session->mbox = NULL;
1727 ok = imap_cmd_examine(session, "INBOX",
1728 &exists, &recent, &unseen, &uid_validity);
1729 if (ok != IMAP_SUCCESS) {
1730 g_free(real_oldpath);
1734 separator = imap_get_path_separator(IMAP_FOLDER(folder), item->path);
1735 if (strchr(item->path, G_DIR_SEPARATOR)) {
1736 dirpath = g_dirname(item->path);
1737 newpath = g_strconcat(dirpath, G_DIR_SEPARATOR_S, name, NULL);
1740 newpath = g_strdup(name);
1742 real_newpath = imap_locale_to_modified_utf7(newpath);
1743 imap_path_separator_subst(real_newpath, separator);
1745 ok = imap_cmd_rename(session, real_oldpath, real_newpath);
1746 if (ok != IMAP_SUCCESS) {
1747 log_warning(_("can't rename mailbox: %s to %s\n"),
1748 real_oldpath, real_newpath);
1749 g_free(real_oldpath);
1751 g_free(real_newpath);
1756 item->name = g_strdup(name);
1758 old_cache_dir = folder_item_get_path(item);
1760 paths[0] = g_strdup(item->path);
1762 g_node_traverse(item->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1763 imap_rename_folder_func, paths);
1765 if (is_dir_exist(old_cache_dir)) {
1766 new_cache_dir = folder_item_get_path(item);
1767 if (rename(old_cache_dir, new_cache_dir) < 0) {
1768 FILE_OP_ERROR(old_cache_dir, "rename");
1770 g_free(new_cache_dir);
1773 g_free(old_cache_dir);
1776 g_free(real_oldpath);
1777 g_free(real_newpath);
1782 static gint imap_remove_folder(Folder *folder, FolderItem *item)
1785 IMAPSession *session;
1788 gint exists, recent, unseen;
1789 guint32 uid_validity;
1791 g_return_val_if_fail(folder != NULL, -1);
1792 g_return_val_if_fail(item != NULL, -1);
1793 g_return_val_if_fail(item->path != NULL, -1);
1795 session = imap_session_get(folder);
1796 if (!session) return -1;
1798 path = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1800 ok = imap_cmd_examine(session, "INBOX",
1801 &exists, &recent, &unseen, &uid_validity);
1802 if (ok != IMAP_SUCCESS) {
1807 ok = imap_cmd_delete(session, path);
1808 if (ok != IMAP_SUCCESS) {
1809 log_warning(_("can't delete mailbox\n"));
1815 cache_dir = folder_item_get_path(item);
1816 if (is_dir_exist(cache_dir) && remove_dir_recursive(cache_dir) < 0)
1817 g_warning("can't remove directory '%s'\n", cache_dir);
1819 folder_item_remove(item);
1824 static GSList *imap_get_uncached_messages(IMAPSession *session,
1826 MsgNumberList *numlist)
1829 GSList *newlist = NULL;
1830 GSList *llast = NULL;
1833 GSList *seq_list, *cur;
1836 g_return_val_if_fail(session != NULL, NULL);
1837 g_return_val_if_fail(item != NULL, NULL);
1838 g_return_val_if_fail(item->folder != NULL, NULL);
1839 g_return_val_if_fail(FOLDER_CLASS(item->folder) == &imap_class, NULL);
1841 seq_list = imap_get_seq_set_from_numlist(numlist);
1842 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
1843 imapset = cur->data;
1845 if (imap_cmd_envelope(session, imapset)
1847 log_warning(_("can't get envelope\n"));
1851 str = g_string_new(NULL);
1854 if ((tmp = sock_getline(SESSION(session)->sock)) == NULL) {
1855 log_warning(_("error occurred while getting envelope.\n"));
1856 g_string_free(str, TRUE);
1860 if (tmp[0] != '*' || tmp[1] != ' ') {
1861 log_print("IMAP4< %s\n", tmp);
1865 if (strstr(tmp, "FETCH") == NULL) {
1866 log_print("IMAP4< %s\n", tmp);
1870 log_print("IMAP4< %s\n", tmp);
1871 g_string_assign(str, tmp);
1874 msginfo = imap_parse_envelope
1875 (SESSION(session)->sock, item, str);
1877 log_warning(_("can't parse envelope: %s\n"), str->str);
1880 if (item->stype == F_QUEUE) {
1881 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_QUEUED);
1882 } else if (item->stype == F_DRAFT) {
1883 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_DRAFT);
1886 msginfo->folder = item;
1889 llast = newlist = g_slist_append(newlist, msginfo);
1891 llast = g_slist_append(llast, msginfo);
1892 llast = llast->next;
1896 g_string_free(str, TRUE);
1898 imap_seq_set_free(seq_list);
1903 static void imap_delete_all_cached_messages(FolderItem *item)
1907 g_return_if_fail(item != NULL);
1908 g_return_if_fail(item->folder != NULL);
1909 g_return_if_fail(FOLDER_CLASS(item->folder) == &imap_class);
1911 debug_print("Deleting all cached messages...\n");
1913 dir = folder_item_get_path(item);
1914 if (is_dir_exist(dir))
1915 remove_all_numbered_files(dir);
1918 debug_print("done.\n");
1922 static SockInfo *imap_open_tunnel(const gchar *server,
1923 const gchar *tunnelcmd,
1926 static SockInfo *imap_open_tunnel(const gchar *server,
1927 const gchar *tunnelcmd)
1932 if ((sock = sock_connect_cmd(server, tunnelcmd)) == NULL) {
1933 log_warning(_("Can't establish IMAP4 session with: %s\n"),
1938 return imap_init_sock(sock, ssl_type);
1940 return imap_init_sock(sock);
1946 static SockInfo *imap_open(const gchar *server, gushort port,
1949 static SockInfo *imap_open(const gchar *server, gushort port)
1954 if ((sock = sock_connect(server, port)) == NULL) {
1955 log_warning(_("Can't connect to IMAP4 server: %s:%d\n"),
1961 if (ssl_type == SSL_TUNNEL && !ssl_init_socket(sock)) {
1962 log_warning(_("Can't establish IMAP4 session with: %s:%d\n"),
1972 static SockInfo *imap_init_sock(SockInfo *sock, SSLType ssl_type)
1974 static SockInfo *imap_init_sock(SockInfo *sock)
1981 static GList *imap_parse_namespace_str(gchar *str)
1986 IMAPNameSpace *namespace;
1987 GList *ns_list = NULL;
1989 while (*p != '\0') {
1990 /* parse ("#foo" "/") */
1992 while (*p && *p != '(') p++;
1993 if (*p == '\0') break;
1996 while (*p && *p != '"') p++;
1997 if (*p == '\0') break;
2001 while (*p && *p != '"') p++;
2002 if (*p == '\0') break;
2006 while (*p && isspace(*p)) p++;
2007 if (*p == '\0') break;
2008 if (strncmp(p, "NIL", 3) == 0)
2010 else if (*p == '"') {
2013 while (*p && *p != '"') p++;
2014 if (*p == '\0') break;
2019 while (*p && *p != ')') p++;
2020 if (*p == '\0') break;
2023 namespace = g_new(IMAPNameSpace, 1);
2024 namespace->name = g_strdup(name);
2025 namespace->separator = separator ? separator[0] : '\0';
2026 ns_list = g_list_append(ns_list, namespace);
2032 static void imap_parse_namespace(IMAPSession *session, IMAPFolder *folder)
2037 g_return_if_fail(session != NULL);
2038 g_return_if_fail(folder != NULL);
2040 if (folder->ns_personal != NULL ||
2041 folder->ns_others != NULL ||
2042 folder->ns_shared != NULL)
2045 if (!imap_has_capability(session, "NAMESPACE")) {
2046 imap_get_namespace_by_list(session, folder);
2050 if (imap_cmd_namespace(session, &ns_str)
2052 log_warning(_("can't get namespace\n"));
2056 str_array = strsplit_parenthesis(ns_str, '(', ')', 3);
2057 if (str_array == NULL) {
2059 imap_get_namespace_by_list(session, folder);
2063 folder->ns_personal = imap_parse_namespace_str(str_array[0]);
2064 if (str_array[0] && str_array[1])
2065 folder->ns_others = imap_parse_namespace_str(str_array[1]);
2066 if (str_array[0] && str_array[1] && str_array[2])
2067 folder->ns_shared = imap_parse_namespace_str(str_array[2]);
2068 g_strfreev(str_array);
2072 static void imap_get_namespace_by_list(IMAPSession *session, IMAPFolder *folder)
2074 GSList *item_list, *cur;
2075 gchar separator = '\0';
2076 IMAPNameSpace *namespace;
2078 g_return_if_fail(session != NULL);
2079 g_return_if_fail(folder != NULL);
2081 if (folder->ns_personal != NULL ||
2082 folder->ns_others != NULL ||
2083 folder->ns_shared != NULL)
2086 imap_gen_send(session, "LIST \"\" \"\"");
2087 item_list = imap_parse_list(folder, session, "", &separator);
2088 for (cur = item_list; cur != NULL; cur = cur->next)
2089 folder_item_destroy(FOLDER_ITEM(cur->data));
2090 g_slist_free(item_list);
2092 namespace = g_new(IMAPNameSpace, 1);
2093 namespace->name = g_strdup("");
2094 namespace->separator = separator;
2095 folder->ns_personal = g_list_append(NULL, namespace);
2098 static IMAPNameSpace *imap_find_namespace_from_list(GList *ns_list,
2101 IMAPNameSpace *namespace = NULL;
2102 gchar *tmp_path, *name;
2104 if (!path) path = "";
2106 for (; ns_list != NULL; ns_list = ns_list->next) {
2107 IMAPNameSpace *tmp_ns = ns_list->data;
2109 Xstrcat_a(tmp_path, path, "/", return namespace);
2110 Xstrdup_a(name, tmp_ns->name, return namespace);
2111 if (tmp_ns->separator && tmp_ns->separator != '/') {
2112 subst_char(tmp_path, tmp_ns->separator, '/');
2113 subst_char(name, tmp_ns->separator, '/');
2115 if (strncmp(tmp_path, name, strlen(name)) == 0)
2122 static IMAPNameSpace *imap_find_namespace(IMAPFolder *folder,
2125 IMAPNameSpace *namespace;
2127 g_return_val_if_fail(folder != NULL, NULL);
2129 namespace = imap_find_namespace_from_list(folder->ns_personal, path);
2130 if (namespace) return namespace;
2131 namespace = imap_find_namespace_from_list(folder->ns_others, path);
2132 if (namespace) return namespace;
2133 namespace = imap_find_namespace_from_list(folder->ns_shared, path);
2134 if (namespace) return namespace;
2139 static gchar imap_get_path_separator(IMAPFolder *folder, const gchar *path)
2141 IMAPNameSpace *namespace;
2142 gchar separator = '/';
2144 namespace = imap_find_namespace(folder, path);
2145 if (namespace && namespace->separator)
2146 separator = namespace->separator;
2151 static gchar *imap_get_real_path(IMAPFolder *folder, const gchar *path)
2156 g_return_val_if_fail(folder != NULL, NULL);
2157 g_return_val_if_fail(path != NULL, NULL);
2159 real_path = imap_locale_to_modified_utf7(path);
2160 separator = imap_get_path_separator(folder, path);
2161 imap_path_separator_subst(real_path, separator);
2166 static gchar *imap_parse_atom(SockInfo *sock, gchar *src,
2167 gchar *dest, gint dest_len, GString *str)
2169 gchar *cur_pos = src;
2172 g_return_val_if_fail(str != NULL, cur_pos);
2174 /* read the next line if the current response buffer is empty */
2175 while (isspace(*(guchar *)cur_pos)) cur_pos++;
2176 while (*cur_pos == '\0') {
2177 if ((nextline = sock_getline(sock)) == NULL)
2179 g_string_assign(str, nextline);
2181 strretchomp(nextline);
2182 /* log_print("IMAP4< %s\n", nextline); */
2183 debug_print("IMAP4< %s\n", nextline);
2186 while (isspace(*(guchar *)cur_pos)) cur_pos++;
2189 if (!strncmp(cur_pos, "NIL", 3)) {
2192 } else if (*cur_pos == '\"') {
2195 p = get_quoted(cur_pos, '\"', dest, dest_len);
2196 cur_pos = p ? p : cur_pos + 2;
2197 } else if (*cur_pos == '{') {
2202 cur_pos = strchr_cpy(cur_pos + 1, '}', buf, sizeof(buf));
2204 g_return_val_if_fail(len >= 0, cur_pos);
2206 g_string_truncate(str, 0);
2210 if ((nextline = sock_getline(sock)) == NULL)
2212 line_len += strlen(nextline);
2213 g_string_append(str, nextline);
2215 strretchomp(nextline);
2216 /* log_print("IMAP4< %s\n", nextline); */
2217 debug_print("IMAP4< %s\n", nextline);
2219 } while (line_len < len);
2221 memcpy(dest, cur_pos, MIN(len, dest_len - 1));
2222 dest[MIN(len, dest_len - 1)] = '\0';
2229 static gchar *imap_get_header(SockInfo *sock, gchar *cur_pos, gchar **headers,
2239 g_return_val_if_fail(str != NULL, cur_pos);
2241 while (isspace(*(guchar *)cur_pos)) cur_pos++;
2243 g_return_val_if_fail(*cur_pos == '{', cur_pos);
2245 cur_pos = strchr_cpy(cur_pos + 1, '}', buf, sizeof(buf));
2247 g_return_val_if_fail(len >= 0, cur_pos);
2249 g_string_truncate(str, 0);
2253 if ((nextline = sock_getline(sock)) == NULL)
2255 block_len += strlen(nextline);
2256 g_string_append(str, nextline);
2258 strretchomp(nextline);
2259 /* debug_print("IMAP4< %s\n", nextline); */
2261 } while (block_len < len);
2263 debug_print("IMAP4< [contents of BODY.PEEK[HEADER.FIELDS (...)]]\n");
2265 *headers = g_strndup(cur_pos, len);
2268 while (isspace(*(guchar *)cur_pos)) cur_pos++;
2269 while (*cur_pos == '\0') {
2270 if ((nextline = sock_getline(sock)) == NULL)
2272 g_string_assign(str, nextline);
2274 strretchomp(nextline);
2275 debug_print("IMAP4< %s\n", nextline);
2278 while (isspace(*(guchar *)cur_pos)) cur_pos++;
2284 static MsgFlags imap_parse_flags(const gchar *flag_str)
2286 const gchar *p = flag_str;
2287 MsgFlags flags = {0, 0};
2289 flags.perm_flags = MSG_UNREAD;
2291 while ((p = strchr(p, '\\')) != NULL) {
2294 if (g_strncasecmp(p, "Recent", 6) == 0 && MSG_IS_UNREAD(flags)) {
2295 MSG_SET_PERM_FLAGS(flags, MSG_NEW);
2296 } else if (g_strncasecmp(p, "Seen", 4) == 0) {
2297 MSG_UNSET_PERM_FLAGS(flags, MSG_NEW|MSG_UNREAD);
2298 } else if (g_strncasecmp(p, "Deleted", 7) == 0) {
2299 MSG_SET_PERM_FLAGS(flags, MSG_DELETED);
2300 } else if (g_strncasecmp(p, "Flagged", 7) == 0) {
2301 MSG_SET_PERM_FLAGS(flags, MSG_MARKED);
2302 } else if (g_strncasecmp(p, "Answered", 8) == 0) {
2303 MSG_SET_PERM_FLAGS(flags, MSG_REPLIED);
2310 static MsgInfo *imap_parse_envelope(SockInfo *sock, FolderItem *item,
2313 gchar buf[IMAPBUFSIZE];
2314 MsgInfo *msginfo = NULL;
2319 MsgFlags flags = {0, 0}, imap_flags = {0, 0};
2321 g_return_val_if_fail(line_str != NULL, NULL);
2322 g_return_val_if_fail(line_str->str[0] == '*' &&
2323 line_str->str[1] == ' ', NULL);
2325 MSG_SET_TMP_FLAGS(flags, MSG_IMAP);
2326 if (item->stype == F_QUEUE) {
2327 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
2328 } else if (item->stype == F_DRAFT) {
2329 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
2332 cur_pos = line_str->str + 2;
2334 #define PARSE_ONE_ELEMENT(ch) \
2336 cur_pos = strchr_cpy(cur_pos, ch, buf, sizeof(buf)); \
2337 if (cur_pos == NULL) { \
2338 g_warning("cur_pos == NULL\n"); \
2339 procmsg_msginfo_free(msginfo); \
2344 PARSE_ONE_ELEMENT(' ');
2347 PARSE_ONE_ELEMENT(' ');
2348 g_return_val_if_fail(!strcmp(buf, "FETCH"), NULL);
2350 g_return_val_if_fail(*cur_pos == '(', NULL);
2353 while (*cur_pos != '\0' && *cur_pos != ')') {
2354 while (*cur_pos == ' ') cur_pos++;
2356 if (!strncmp(cur_pos, "UID ", 4)) {
2358 uid = strtoul(cur_pos, &cur_pos, 10);
2359 } else if (!strncmp(cur_pos, "FLAGS ", 6)) {
2361 if (*cur_pos != '(') {
2362 g_warning("*cur_pos != '('\n");
2363 procmsg_msginfo_free(msginfo);
2367 PARSE_ONE_ELEMENT(')');
2368 imap_flags = imap_parse_flags(buf);
2369 } else if (!strncmp(cur_pos, "RFC822.SIZE ", 12)) {
2371 size = strtol(cur_pos, &cur_pos, 10);
2372 } else if (!strncmp(cur_pos, "BODY[HEADER.FIELDS ", 19)) {
2376 if (*cur_pos != '(') {
2377 g_warning("*cur_pos != '('\n");
2378 procmsg_msginfo_free(msginfo);
2382 PARSE_ONE_ELEMENT(')');
2383 if (*cur_pos != ']') {
2384 g_warning("*cur_pos != ']'\n");
2385 procmsg_msginfo_free(msginfo);
2390 cur_pos = imap_get_header(sock, cur_pos, &headers,
2392 msginfo = procheader_parse_str(headers, flags, FALSE, FALSE);
2395 g_warning("invalid FETCH response: %s\n", cur_pos);
2401 msginfo->msgnum = uid;
2402 msginfo->size = size;
2403 msginfo->flags.tmp_flags |= imap_flags.tmp_flags;
2404 msginfo->flags.perm_flags = imap_flags.perm_flags;
2410 static gchar *imap_get_flag_str(IMAPFlags flags)
2415 str = g_string_new(NULL);
2417 if (IMAP_IS_SEEN(flags)) g_string_append(str, "\\Seen ");
2418 if (IMAP_IS_ANSWERED(flags)) g_string_append(str, "\\Answered ");
2419 if (IMAP_IS_FLAGGED(flags)) g_string_append(str, "\\Flagged ");
2420 if (IMAP_IS_DELETED(flags)) g_string_append(str, "\\Deleted ");
2421 if (IMAP_IS_DRAFT(flags)) g_string_append(str, "\\Draft");
2423 if (str->len > 0 && str->str[str->len - 1] == ' ')
2424 g_string_truncate(str, str->len - 1);
2427 g_string_free(str, FALSE);
2432 static gint imap_set_message_flags(IMAPSession *session,
2433 MsgNumberList *numlist,
2440 GSList *seq_list, *cur;
2443 flag_str = imap_get_flag_str(flags);
2444 cmd = g_strconcat(is_set ? "+FLAGS.SILENT (" : "-FLAGS.SILENT (",
2445 flag_str, ")", NULL);
2448 seq_list = imap_get_seq_set_from_numlist(numlist);
2449 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
2450 imapset = cur->data;
2452 ok = imap_cmd_store(session, imapset, cmd);
2454 imap_seq_set_free(seq_list);
2460 static gint imap_select(IMAPSession *session, IMAPFolder *folder,
2462 gint *exists, gint *recent, gint *unseen,
2463 guint32 *uid_validity)
2467 gint exists_, recent_, unseen_, uid_validity_;
2469 if (!exists || !recent || !unseen || !uid_validity) {
2470 if (session->mbox && strcmp(session->mbox, path) == 0)
2471 return IMAP_SUCCESS;
2475 uid_validity = &uid_validity_;
2478 g_free(session->mbox);
2479 session->mbox = NULL;
2481 real_path = imap_get_real_path(folder, path);
2482 ok = imap_cmd_select(session, real_path,
2483 exists, recent, unseen, uid_validity);
2484 if (ok != IMAP_SUCCESS)
2485 log_warning(_("can't select folder: %s\n"), real_path);
2487 session->mbox = g_strdup(path);
2488 session->folder_content_changed = FALSE;
2495 #define THROW(err) { ok = err; goto catch; }
2497 static gint imap_status(IMAPSession *session, IMAPFolder *folder,
2499 gint *messages, gint *recent,
2500 guint32 *uid_next, guint32 *uid_validity,
2506 GPtrArray *argbuf = NULL;
2509 if (messages && recent && uid_next && uid_validity && unseen) {
2510 *messages = *recent = *uid_next = *uid_validity = *unseen = 0;
2511 argbuf = g_ptr_array_new();
2514 real_path = imap_get_real_path(folder, path);
2515 QUOTE_IF_REQUIRED(real_path_, real_path);
2516 imap_gen_send(session, "STATUS %s "
2517 "(MESSAGES RECENT UIDNEXT UIDVALIDITY UNSEEN)",
2520 ok = imap_cmd_ok(session, argbuf);
2521 if (ok != IMAP_SUCCESS || !argbuf) THROW(ok);
2523 str = search_array_str(argbuf, "STATUS");
2524 if (!str) THROW(IMAP_ERROR);
2526 str = strchr(str, '(');
2527 if (!str) THROW(IMAP_ERROR);
2529 while (*str != '\0' && *str != ')') {
2530 while (*str == ' ') str++;
2532 if (!strncmp(str, "MESSAGES ", 9)) {
2534 *messages = strtol(str, &str, 10);
2535 } else if (!strncmp(str, "RECENT ", 7)) {
2537 *recent = strtol(str, &str, 10);
2538 } else if (!strncmp(str, "UIDNEXT ", 8)) {
2540 *uid_next = strtoul(str, &str, 10);
2541 } else if (!strncmp(str, "UIDVALIDITY ", 12)) {
2543 *uid_validity = strtoul(str, &str, 10);
2544 } else if (!strncmp(str, "UNSEEN ", 7)) {
2546 *unseen = strtol(str, &str, 10);
2548 g_warning("invalid STATUS response: %s\n", str);
2556 ptr_array_free_strings(argbuf);
2557 g_ptr_array_free(argbuf, TRUE);
2565 static gboolean imap_has_capability(IMAPSession *session, const gchar *cap)
2569 for (p = session->capability; *p != NULL; ++p) {
2570 if (!g_strcasecmp(*p, cap))
2577 static void imap_free_capabilities(IMAPSession *session)
2579 g_strfreev(session->capability);
2580 session->capability = NULL;
2583 /* low-level IMAP4rev1 commands */
2585 static gint imap_cmd_authenticate(IMAPSession *session, const gchar *user,
2586 const gchar *pass, IMAPAuthType type)
2593 gchar hexdigest[33];
2597 auth_type = "CRAM-MD5";
2599 imap_gen_send(session, "AUTHENTICATE %s", auth_type);
2600 ok = imap_gen_recv(session, &buf);
2601 if (ok != IMAP_SUCCESS || buf[0] != '+' || buf[1] != ' ') {
2606 challenge = g_malloc(strlen(buf + 2) + 1);
2607 challenge_len = base64_decode(challenge, buf + 2, -1);
2608 challenge[challenge_len] = '\0';
2610 log_print("IMAP< [Decoded: %s]\n", challenge);
2612 md5_hex_hmac(hexdigest, challenge, challenge_len, pass, strlen(pass));
2615 response = g_strdup_printf("%s %s", user, hexdigest);
2616 log_print("IMAP> [Encoded: %s]\n", response);
2617 response64 = g_malloc((strlen(response) + 3) * 2 + 1);
2618 base64_encode(response64, response, strlen(response));
2621 log_print("IMAP> %s\n", response64);
2622 sock_puts(SESSION(session)->sock, response64);
2623 ok = imap_cmd_ok(session, NULL);
2624 if (ok != IMAP_SUCCESS)
2625 log_warning(_("IMAP4 authentication failed.\n"));
2630 static gint imap_cmd_login(IMAPSession *session,
2631 const gchar *user, const gchar *pass)
2633 gchar *user_, *pass_;
2636 QUOTE_IF_REQUIRED(user_, user);
2637 QUOTE_IF_REQUIRED(pass_, pass);
2638 imap_gen_send(session, "LOGIN %s %s", user_, pass_);
2640 ok = imap_cmd_ok(session, NULL);
2641 if (ok != IMAP_SUCCESS)
2642 log_warning(_("IMAP4 login failed.\n"));
2647 static gint imap_cmd_logout(IMAPSession *session)
2649 imap_gen_send(session, "LOGOUT");
2650 return imap_cmd_ok(session, NULL);
2653 static gint imap_cmd_noop(IMAPSession *session)
2655 imap_gen_send(session, "NOOP");
2656 return imap_cmd_ok(session, NULL);
2659 static gint imap_cmd_starttls(IMAPSession *session)
2661 imap_gen_send(session, "STARTTLS");
2662 return imap_cmd_ok(session, NULL);
2665 #define THROW(err) { ok = err; goto catch; }
2667 static gint imap_cmd_namespace(IMAPSession *session, gchar **ns_str)
2673 argbuf = g_ptr_array_new();
2675 imap_gen_send(session, "NAMESPACE");
2676 if ((ok = imap_cmd_ok(session, argbuf)) != IMAP_SUCCESS) THROW(ok);
2678 str = search_array_str(argbuf, "NAMESPACE");
2679 if (!str) THROW(IMAP_ERROR);
2681 *ns_str = g_strdup(str);
2684 ptr_array_free_strings(argbuf);
2685 g_ptr_array_free(argbuf, TRUE);
2692 static gint imap_cmd_list(IMAPSession *session, const gchar *ref,
2693 const gchar *mailbox, GPtrArray *argbuf)
2695 gchar *ref_, *mailbox_;
2697 if (!ref) ref = "\"\"";
2698 if (!mailbox) mailbox = "\"\"";
2700 QUOTE_IF_REQUIRED(ref_, ref);
2701 QUOTE_IF_REQUIRED(mailbox_, mailbox);
2702 imap_gen_send(session, "LIST %s %s", ref_, mailbox_);
2704 return imap_cmd_ok(session, argbuf);
2707 #define THROW goto catch
2709 static gint imap_cmd_do_select(IMAPSession *session, const gchar *folder,
2711 gint *exists, gint *recent, gint *unseen,
2712 guint32 *uid_validity)
2720 *exists = *recent = *unseen = *uid_validity = 0;
2721 argbuf = g_ptr_array_new();
2724 select_cmd = "EXAMINE";
2726 select_cmd = "SELECT";
2728 QUOTE_IF_REQUIRED(folder_, folder);
2729 imap_gen_send(session, "%s %s", select_cmd, folder_);
2731 if ((ok = imap_cmd_ok(session, argbuf)) != IMAP_SUCCESS) THROW;
2733 resp_str = search_array_contain_str(argbuf, "EXISTS");
2735 if (sscanf(resp_str,"%d EXISTS", exists) != 1) {
2736 g_warning("imap_cmd_select(): invalid EXISTS line.\n");
2741 resp_str = search_array_contain_str(argbuf, "RECENT");
2743 if (sscanf(resp_str, "%d RECENT", recent) != 1) {
2744 g_warning("imap_cmd_select(): invalid RECENT line.\n");
2749 resp_str = search_array_contain_str(argbuf, "UIDVALIDITY");
2751 if (sscanf(resp_str, "OK [UIDVALIDITY %u] ", uid_validity)
2753 g_warning("imap_cmd_select(): invalid UIDVALIDITY line.\n");
2758 resp_str = search_array_contain_str(argbuf, "UNSEEN");
2760 if (sscanf(resp_str, "OK [UNSEEN %d] ", unseen) != 1) {
2761 g_warning("imap_cmd_select(): invalid UNSEEN line.\n");
2767 ptr_array_free_strings(argbuf);
2768 g_ptr_array_free(argbuf, TRUE);
2773 static gint imap_cmd_select(IMAPSession *session, const gchar *folder,
2774 gint *exists, gint *recent, gint *unseen,
2775 guint32 *uid_validity)
2777 return imap_cmd_do_select(session, folder, FALSE,
2778 exists, recent, unseen, uid_validity);
2781 static gint imap_cmd_examine(IMAPSession *session, const gchar *folder,
2782 gint *exists, gint *recent, gint *unseen,
2783 guint32 *uid_validity)
2785 return imap_cmd_do_select(session, folder, TRUE,
2786 exists, recent, unseen, uid_validity);
2791 static gint imap_cmd_create(IMAPSession *session, const gchar *folder)
2795 QUOTE_IF_REQUIRED(folder_, folder);
2796 imap_gen_send(session, "CREATE %s", folder_);
2798 return imap_cmd_ok(session, NULL);
2801 static gint imap_cmd_rename(IMAPSession *session, const gchar *old_folder,
2802 const gchar *new_folder)
2804 gchar *old_folder_, *new_folder_;
2806 QUOTE_IF_REQUIRED(old_folder_, old_folder);
2807 QUOTE_IF_REQUIRED(new_folder_, new_folder);
2808 imap_gen_send(session, "RENAME %s %s", old_folder_, new_folder_);
2810 return imap_cmd_ok(session, NULL);
2813 static gint imap_cmd_delete(IMAPSession *session, const gchar *folder)
2817 QUOTE_IF_REQUIRED(folder_, folder);
2818 imap_gen_send(session, "DELETE %s", folder_);
2820 return imap_cmd_ok(session, NULL);
2823 static gint imap_cmd_search(IMAPSession *session, const gchar *criteria,
2830 g_return_val_if_fail(criteria != NULL, IMAP_ERROR);
2831 g_return_val_if_fail(list != NULL, IMAP_ERROR);
2835 argbuf = g_ptr_array_new();
2836 imap_gen_send(session, "UID SEARCH %s", criteria);
2838 ok = imap_cmd_ok(session, argbuf);
2839 if (ok != IMAP_SUCCESS) {
2840 ptr_array_free_strings(argbuf);
2841 g_ptr_array_free(argbuf, TRUE);
2845 if ((uidlist = search_array_str(argbuf, "SEARCH ")) != NULL) {
2846 gchar **strlist, **p;
2848 strlist = g_strsplit(uidlist + 7, " ", 0);
2849 for (p = strlist; *p != NULL; ++p) {
2852 if (sscanf(*p, "%d", &msgnum) == 1)
2853 *list = g_slist_append(*list, GINT_TO_POINTER(msgnum));
2855 g_strfreev(strlist);
2857 ptr_array_free_strings(argbuf);
2858 g_ptr_array_free(argbuf, TRUE);
2860 return IMAP_SUCCESS;
2863 static gint imap_cmd_fetch(IMAPSession *session, guint32 uid,
2864 const gchar *filename)
2872 g_return_val_if_fail(filename != NULL, IMAP_ERROR);
2874 imap_gen_send(session, "UID FETCH %d BODY.PEEK[]", uid);
2876 while ((ok = imap_gen_recv(session, &buf)) == IMAP_SUCCESS) {
2877 if (buf[0] != '*' || buf[1] != ' ') {
2881 if (strstr(buf, "FETCH") != NULL) break;
2884 if (ok != IMAP_SUCCESS) {
2889 #define RETURN_ERROR_IF_FAIL(cond) \
2892 return IMAP_ERROR; \
2895 cur_pos = strchr(buf, '{');
2896 RETURN_ERROR_IF_FAIL(cur_pos != NULL);
2897 cur_pos = strchr_cpy(cur_pos + 1, '}', size_str, sizeof(size_str));
2898 RETURN_ERROR_IF_FAIL(cur_pos != NULL);
2899 size_num = atol(size_str);
2900 RETURN_ERROR_IF_FAIL(size_num >= 0);
2902 RETURN_ERROR_IF_FAIL(*cur_pos == '\0');
2904 #undef RETURN_ERROR_IF_FAIL
2908 if (recv_bytes_write_to_file(SESSION(session)->sock,
2909 size_num, filename) != 0)
2912 if (imap_gen_recv(session, &buf) != IMAP_SUCCESS) {
2917 if (buf[0] == '\0' || buf[strlen(buf) - 1] != ')') {
2923 ok = imap_cmd_ok(session, NULL);
2928 static gint imap_cmd_append(IMAPSession *session, const gchar *destfolder,
2929 const gchar *file, IMAPFlags flags,
2938 gchar buf[BUFFSIZE];
2943 g_return_val_if_fail(file != NULL, IMAP_ERROR);
2945 size = get_file_size_as_crlf(file);
2946 if ((fp = fopen(file, "rb")) == NULL) {
2947 FILE_OP_ERROR(file, "fopen");
2950 QUOTE_IF_REQUIRED(destfolder_, destfolder);
2951 flag_str = imap_get_flag_str(flags);
2952 imap_gen_send(session, "APPEND %s (%s) {%d}",
2953 destfolder_, flag_str, size);
2956 ok = imap_gen_recv(session, &ret);
2957 if (ok != IMAP_SUCCESS || ret[0] != '+' || ret[1] != ' ') {
2958 log_warning(_("can't append %s to %s\n"), file, destfolder_);
2965 log_print("IMAP4> %s\n", _("(sending file...)"));
2967 while (fgets(buf, sizeof(buf), fp) != NULL) {
2969 if (sock_puts(SESSION(session)->sock, buf) < 0) {
2976 FILE_OP_ERROR(file, "fgets");
2981 sock_puts(SESSION(session)->sock, "");
2985 if (new_uid != NULL)
2988 if (new_uid != NULL && session->uidplus) {
2989 argbuf = g_ptr_array_new();
2991 ok = imap_cmd_ok(session, argbuf);
2992 if ((ok == IMAP_SUCCESS) && (argbuf->len > 0)) {
2993 resp_str = g_ptr_array_index(argbuf, argbuf->len - 1);
2995 sscanf(resp_str, "%*u OK [APPENDUID %*u %u]",
2997 *new_uid = new_uid_;
3001 ptr_array_free_strings(argbuf);
3002 g_ptr_array_free(argbuf, TRUE);
3004 ok = imap_cmd_ok(session, NULL);
3006 if (ok != IMAP_SUCCESS)
3007 log_warning(_("can't append message to %s\n"),
3013 static MsgNumberList *imapset_to_numlist(IMAPSet imapset)
3015 gchar **ranges, **range;
3017 MsgNumberList *uids = NULL;
3019 ranges = g_strsplit(imapset, ",", 0);
3020 for (range = ranges; *range != NULL; range++) {
3021 printf("%s\n", *range);
3022 if(sscanf(*range, "%u:%u", &low, &high) == 1)
3023 uids = g_slist_prepend(uids, GINT_TO_POINTER(low));
3026 for (i = low; i <= high; i++)
3027 uids = g_slist_prepend(uids, GINT_TO_POINTER(i));
3030 uids = g_slist_reverse(uids);
3036 static gint imap_cmd_copy(IMAPSession *session, const gchar *seq_set,
3037 const gchar *destfolder, GRelation *uid_mapping)
3042 g_return_val_if_fail(session != NULL, IMAP_ERROR);
3043 g_return_val_if_fail(seq_set != NULL, IMAP_ERROR);
3044 g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
3046 QUOTE_IF_REQUIRED(destfolder_, destfolder);
3047 imap_gen_send(session, "UID COPY %s %s", seq_set, destfolder_);
3049 if (uid_mapping != NULL && session->uidplus) {
3051 gchar *resp_str = NULL, *olduids_str, *newuids_str;
3052 MsgNumberList *olduids, *old_cur, *newuids, *new_cur;
3054 reply = g_ptr_array_new();
3055 ok = imap_cmd_ok(session, reply);
3056 if ((ok == IMAP_SUCCESS) && (reply->len > 0)) {
3057 resp_str = g_ptr_array_index(reply, reply->len - 1);
3059 olduids_str = g_new0(gchar, strlen(resp_str));
3060 newuids_str = g_new0(gchar, strlen(resp_str));
3061 if (sscanf(resp_str, "%*s OK [COPYUID %*u %[0-9,:] %[0-9,:]]",
3062 olduids_str, newuids_str) == 2) {
3063 olduids = imapset_to_numlist(olduids_str);
3064 newuids = imapset_to_numlist(newuids_str);
3068 while(old_cur != NULL && new_cur != NULL) {
3069 g_relation_insert(uid_mapping,
3070 GPOINTER_TO_INT(old_cur->data),
3071 GPOINTER_TO_INT(new_cur->data));
3072 old_cur = g_slist_next(old_cur);
3073 new_cur = g_slist_next(new_cur);
3076 g_slist_free(olduids);
3077 g_slist_free(newuids);
3079 g_free(olduids_str);
3080 g_free(newuids_str);
3083 ptr_array_free_strings(reply);
3084 g_ptr_array_free(reply, TRUE);
3086 ok = imap_cmd_ok(session, NULL);
3088 if (ok != IMAP_SUCCESS)
3089 log_warning(_("can't copy %s to %s\n"), seq_set, destfolder_);
3094 gint imap_cmd_envelope(IMAPSession *session, IMAPSet set)
3096 static GString *header_fields = NULL;
3098 if (header_fields == NULL) {
3099 const HeaderEntry *headers, *elem;
3101 headers = procheader_get_headernames(FALSE);
3102 header_fields = g_string_new("");
3104 for (elem = headers; elem->name != NULL; ++elem) {
3105 gint namelen = strlen(elem->name);
3107 /* Header fields ending with space are not rfc822 headers */
3108 if (elem->name[namelen - 1] == ' ')
3111 /* strip : at the of header field */
3112 if(elem->name[namelen - 1] == ':')
3118 g_string_sprintfa(header_fields, "%s%.*s",
3119 header_fields->str[0] != '\0' ? " " : "",
3120 namelen, elem->name);
3125 (session, "UID FETCH %s (UID FLAGS RFC822.SIZE BODY.PEEK[HEADER.FIELDS (%s)])",
3126 set, header_fields->str);
3128 return IMAP_SUCCESS;
3131 static gint imap_cmd_store(IMAPSession *session, IMAPSet seq_set,
3136 imap_gen_send(session, "UID STORE %s %s", seq_set, sub_cmd);
3138 if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS) {
3139 log_warning(_("error while imap command: STORE %s %s\n"),
3144 return IMAP_SUCCESS;
3147 static gint imap_cmd_expunge(IMAPSession *session, IMAPSet seq_set)
3151 if (seq_set && session->uidplus)
3152 imap_gen_send(session, "UID EXPUNGE %s", seq_set);
3154 imap_gen_send(session, "EXPUNGE");
3155 if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS) {
3156 log_warning(_("error while imap command: EXPUNGE\n"));
3160 return IMAP_SUCCESS;
3163 static gint imap_cmd_close(IMAPSession *session)
3167 imap_gen_send(session, "CLOSE");
3168 if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS)
3169 log_warning(_("error while imap command: CLOSE\n"));
3174 static gint imap_cmd_ok(IMAPSession *session, GPtrArray *argbuf)
3176 gint ok = IMAP_SUCCESS;
3181 while ((ok = imap_gen_recv(session, &buf))
3183 // make sure data is long enough for any substring of buf
3184 data = alloca(strlen(buf) + 1);
3186 // untagged line read
3187 if (buf[0] == '*' && buf[1] == ' ') {
3190 g_ptr_array_add(argbuf, g_strdup(buf + 2));
3192 if (sscanf(buf + 2, "%d %s", &num, data) >= 2) {
3193 if (!strcmp(data, "EXISTS")) {
3194 session->exists = num;
3195 session->folder_content_changed = TRUE;
3198 if(!strcmp(data, "EXPUNGE")) {
3200 session->folder_content_changed = TRUE;
3203 // tagged line with correct tag and OK response found
3204 } else if ((sscanf(buf, "%d %s", &cmd_num, data) >= 2) &&
3205 (cmd_num == session->cmd_count) &&
3206 !strcmp(data, "OK")) {
3208 g_ptr_array_add(argbuf, g_strdup(buf));
3222 static void imap_gen_send(IMAPSession *session, const gchar *format, ...)
3229 va_start(args, format);
3230 tmp = g_strdup_vprintf(format, args);
3233 session->cmd_count++;
3235 buf = g_strdup_printf("%d %s\r\n", session->cmd_count, tmp);
3236 if (!strncasecmp(tmp, "LOGIN ", 6) && (p = strchr(tmp + 6, ' '))) {
3238 log_print("IMAP4> %d %s ********\n", session->cmd_count, tmp);
3240 log_print("IMAP4> %d %s\n", session->cmd_count, tmp);
3242 sock_write_all(SESSION(session)->sock, buf, strlen(buf));
3247 static gint imap_gen_recv(IMAPSession *session, gchar **ret)
3249 if ((*ret = sock_getline(SESSION(session)->sock)) == NULL)
3254 log_print("IMAP4< %s\n", *ret);
3256 return IMAP_SUCCESS;
3260 /* misc utility functions */
3262 static gchar *strchr_cpy(const gchar *src, gchar ch, gchar *dest, gint len)
3267 tmp = strchr(src, ch);
3271 memcpy(dest, src, MIN(tmp - src, len - 1));
3272 dest[MIN(tmp - src, len - 1)] = '\0';
3277 static gchar *get_quoted(const gchar *src, gchar ch, gchar *dest, gint len)
3279 const gchar *p = src;
3282 g_return_val_if_fail(*p == ch, NULL);
3287 while (*p != '\0' && *p != ch) {
3289 if (*p == '\\' && *(p + 1) != '\0')
3298 return (gchar *)(*p == ch ? p + 1 : p);
3301 static gchar *search_array_contain_str(GPtrArray *array, const gchar *str)
3305 for (i = 0; i < array->len; i++) {
3308 tmp = g_ptr_array_index(array, i);
3309 if (strstr(tmp, str) != NULL)
3316 static gchar *search_array_str(GPtrArray *array, const gchar *str)
3323 for (i = 0; i < array->len; i++) {
3326 tmp = g_ptr_array_index(array, i);
3327 if (!strncmp(tmp, str, len))
3334 static void imap_path_separator_subst(gchar *str, gchar separator)
3337 gboolean in_escape = FALSE;
3339 if (!separator || separator == '/') return;
3341 for (p = str; *p != '\0'; p++) {
3342 if (*p == '/' && !in_escape)
3344 else if (*p == '&' && *(p + 1) != '-' && !in_escape)
3346 else if (*p == '-' && in_escape)
3351 static gchar *imap_modified_utf7_to_locale(const gchar *mutf7_str)
3354 const gchar *from_p;
3357 to = g_malloc(strlen(mutf7_str) + 1);
3360 for (from_p = mutf7_str; *from_p != '\0'; from_p++) {
3361 if (*from_p == '&' && *(from_p + 1) == '-') {
3371 static iconv_t cd = (iconv_t)-1;
3372 static gboolean iconv_ok = TRUE;
3375 size_t norm_utf7_len;
3377 gchar *to_str, *to_p;
3379 gboolean in_escape = FALSE;
3381 if (!iconv_ok) return g_strdup(mutf7_str);
3383 if (cd == (iconv_t)-1) {
3384 cd = iconv_open(conv_get_current_charset_str(), "UTF-7");
3385 if (cd == (iconv_t)-1) {
3386 g_warning("iconv cannot convert UTF-7 to %s\n",
3387 conv_get_current_charset_str());
3389 return g_strdup(mutf7_str);
3393 norm_utf7 = g_string_new(NULL);
3395 for (p = mutf7_str; *p != '\0'; p++) {
3396 /* replace: '&' -> '+',
3398 escaped ',' -> '/' */
3399 if (!in_escape && *p == '&') {
3400 if (*(p + 1) != '-') {
3401 g_string_append_c(norm_utf7, '+');
3404 g_string_append_c(norm_utf7, '&');
3407 } else if (in_escape && *p == ',') {
3408 g_string_append_c(norm_utf7, '/');
3409 } else if (in_escape && *p == '-') {
3410 g_string_append_c(norm_utf7, '-');
3413 g_string_append_c(norm_utf7, *p);
3417 norm_utf7_p = norm_utf7->str;
3418 norm_utf7_len = norm_utf7->len;
3419 to_len = strlen(mutf7_str) * 5;
3420 to_p = to_str = g_malloc(to_len + 1);
3422 if (iconv(cd, (ICONV_CONST gchar **)&norm_utf7_p, &norm_utf7_len,
3423 &to_p, &to_len) == -1) {
3424 g_warning(_("iconv cannot convert UTF-7 to %s\n"),
3425 conv_get_current_charset_str());
3426 g_string_free(norm_utf7, TRUE);
3428 return g_strdup(mutf7_str);
3431 /* second iconv() call for flushing */
3432 iconv(cd, NULL, NULL, &to_p, &to_len);
3433 g_string_free(norm_utf7, TRUE);
3437 #endif /* !HAVE_ICONV */
3440 static gchar *imap_locale_to_modified_utf7(const gchar *from)
3443 const gchar *from_p;
3446 to = g_malloc(strlen(from) * 2 + 1);
3449 for (from_p = from; *from_p != '\0'; from_p++) {
3450 if (*from_p == '&') {
3460 static iconv_t cd = (iconv_t)-1;
3461 static gboolean iconv_ok = TRUE;
3462 gchar *norm_utf7, *norm_utf7_p;
3463 size_t from_len, norm_utf7_len;
3465 gchar *from_tmp, *to, *p;
3466 gboolean in_escape = FALSE;
3468 if (!iconv_ok) return g_strdup(from);
3470 if (cd == (iconv_t)-1) {
3471 cd = iconv_open("UTF-7", conv_get_current_charset_str());
3472 if (cd == (iconv_t)-1) {
3473 g_warning("iconv cannot convert %s to UTF-7\n",
3474 conv_get_current_charset_str());
3476 return g_strdup(from);
3480 Xstrdup_a(from_tmp, from, return g_strdup(from));
3481 from_len = strlen(from);
3482 norm_utf7_len = from_len * 5;
3483 Xalloca(norm_utf7, norm_utf7_len + 1, return g_strdup(from));
3484 norm_utf7_p = norm_utf7;
3486 #define IS_PRINT(ch) (isprint(ch) && isascii(ch))
3488 while (from_len > 0) {
3489 if (*from_tmp == '+') {
3490 *norm_utf7_p++ = '+';
3491 *norm_utf7_p++ = '-';
3495 } else if (IS_PRINT(*(guchar *)from_tmp)) {
3496 /* printable ascii char */
3497 *norm_utf7_p = *from_tmp;
3503 size_t mb_len = 0, conv_len = 0;
3505 /* unprintable char: convert to UTF-7 */
3507 while (!IS_PRINT(*(guchar *)p) && conv_len < from_len) {
3508 mb_len = mblen(p, MB_LEN_MAX);
3510 g_warning("wrong multibyte sequence\n");
3511 return g_strdup(from);
3517 from_len -= conv_len;
3518 if (iconv(cd, (ICONV_CONST gchar **)&from_tmp,
3520 &norm_utf7_p, &norm_utf7_len) == -1) {
3521 g_warning("iconv cannot convert %s to UTF-7\n",
3522 conv_get_current_charset_str());
3523 return g_strdup(from);
3526 /* second iconv() call for flushing */
3527 iconv(cd, NULL, NULL, &norm_utf7_p, &norm_utf7_len);
3533 *norm_utf7_p = '\0';
3534 to_str = g_string_new(NULL);
3535 for (p = norm_utf7; p < norm_utf7_p; p++) {
3536 /* replace: '&' -> "&-",
3539 BASE64 '/' -> ',' */
3540 if (!in_escape && *p == '&') {
3541 g_string_append(to_str, "&-");
3542 } else if (!in_escape && *p == '+') {
3543 if (*(p + 1) == '-') {
3544 g_string_append_c(to_str, '+');
3547 g_string_append_c(to_str, '&');
3550 } else if (in_escape && *p == '/') {
3551 g_string_append_c(to_str, ',');
3552 } else if (in_escape && *p == '-') {
3553 g_string_append_c(to_str, '-');
3556 g_string_append_c(to_str, *p);
3562 g_string_append_c(to_str, '-');
3566 g_string_free(to_str, FALSE);
3569 #endif /* !HAVE_ICONV */
3572 static GSList *imap_get_seq_set_from_numlist(MsgNumberList *numlist)
3575 GSList *sorted_list, *cur;
3576 guint first, last, next;
3578 GSList *ret_list = NULL;
3580 if (numlist == NULL)
3583 str = g_string_sized_new(256);
3585 sorted_list = g_slist_copy(numlist);
3586 sorted_list = g_slist_sort(sorted_list, g_int_compare);
3588 first = GPOINTER_TO_INT(sorted_list->data);
3590 for (cur = sorted_list; cur != NULL; cur = g_slist_next(cur)) {
3591 last = GPOINTER_TO_INT(cur->data);
3593 next = GPOINTER_TO_INT(cur->next->data);
3597 if (last + 1 != next || next == 0) {
3599 g_string_append_c(str, ',');
3601 g_string_sprintfa(str, "%u", first);
3603 g_string_sprintfa(str, "%u:%u", first, last);
3607 if (str->len > IMAP_CMD_LIMIT) {
3608 ret_str = g_strdup(str->str);
3609 ret_list = g_slist_append(ret_list, ret_str);
3610 g_string_truncate(str, 0);
3616 ret_str = g_strdup(str->str);
3617 ret_list = g_slist_append(ret_list, ret_str);
3620 g_slist_free(sorted_list);
3621 g_string_free(str, TRUE);
3626 static GSList *imap_get_seq_set_from_msglist(MsgInfoList *msglist)
3628 MsgNumberList *numlist = NULL;
3632 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
3633 MsgInfo *msginfo = (MsgInfo *) cur->data;
3635 numlist = g_slist_append(numlist, GINT_TO_POINTER(msginfo->msgnum));
3637 seq_list = imap_get_seq_set_from_numlist(numlist);
3638 g_slist_free(numlist);
3643 static void imap_seq_set_free(GSList *seq_list)
3645 slist_free_strings(seq_list);
3646 g_slist_free(seq_list);
3650 static gboolean imap_rename_folder_func(GNode *node, gpointer data)
3652 FolderItem *item = node->data;
3653 gchar **paths = data;
3654 const gchar *oldpath = paths[0];
3655 const gchar *newpath = paths[1];
3657 gchar *new_itempath;
3660 oldpathlen = strlen(oldpath);
3661 if (strncmp(oldpath, item->path, oldpathlen) != 0) {
3662 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
3666 base = item->path + oldpathlen;
3667 while (*base == G_DIR_SEPARATOR) base++;
3669 new_itempath = g_strdup(newpath);
3671 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
3674 item->path = new_itempath;
3679 static gint get_list_of_uids(Folder *folder, IMAPFolderItem *item, GSList **msgnum_list)
3681 gint ok, nummsgs = 0, lastuid_old;
3682 IMAPSession *session;
3683 GSList *uidlist, *elem;
3686 session = imap_session_get(folder);
3687 g_return_val_if_fail(session != NULL, -1);
3689 ok = imap_select(session, IMAP_FOLDER(folder), item->item.path,
3690 NULL, NULL, NULL, NULL);
3691 if (ok != IMAP_SUCCESS)
3694 cmd_buf = g_strdup_printf("UID %d:*", item->lastuid + 1);
3695 ok = imap_cmd_search(session, cmd_buf, &uidlist);
3698 if (ok == IMAP_SOCKET) {
3699 session_destroy((Session *)session);
3700 ((RemoteFolder *)folder)->session = NULL;
3704 if (ok != IMAP_SUCCESS) {
3708 argbuf = g_ptr_array_new();
3710 cmd_buf = g_strdup_printf("UID FETCH %d:* (UID)", item->lastuid + 1);
3711 imap_gen_send(session, cmd_buf);
3713 ok = imap_cmd_ok(session, argbuf);
3714 if (ok != IMAP_SUCCESS) {
3715 ptr_array_free_strings(argbuf);
3716 g_ptr_array_free(argbuf, TRUE);
3720 for(i = 0; i < argbuf->len; i++) {
3723 if((ret = sscanf(g_ptr_array_index(argbuf, i),
3724 "%*d FETCH (UID %d)", &msgnum)) == 1)
3725 uidlist = g_slist_prepend(uidlist, GINT_TO_POINTER(msgnum));
3727 ptr_array_free_strings(argbuf);
3728 g_ptr_array_free(argbuf, TRUE);
3731 lastuid_old = item->lastuid;
3732 *msgnum_list = g_slist_copy(item->uid_list);
3733 nummsgs = g_slist_length(*msgnum_list);
3734 debug_print("Got %d uids from cache\n", g_slist_length(item->uid_list));
3736 for (elem = uidlist; elem != NULL; elem = g_slist_next(elem)) {
3739 msgnum = GPOINTER_TO_INT(elem->data);
3740 if (msgnum > lastuid_old) {
3741 *msgnum_list = g_slist_prepend(*msgnum_list, GINT_TO_POINTER(msgnum));
3742 item->uid_list = g_slist_prepend(item->uid_list, GINT_TO_POINTER(msgnum));
3745 if(msgnum > item->lastuid)
3746 item->lastuid = msgnum;
3749 g_slist_free(uidlist);
3754 gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list, gboolean *old_uids_valid)
3756 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3757 IMAPSession *session;
3758 gint ok, nummsgs = 0, exists, recent, uid_val, uid_next, unseen;
3761 gboolean selected_folder;
3763 g_return_val_if_fail(folder != NULL, -1);
3764 g_return_val_if_fail(item != NULL, -1);
3765 g_return_val_if_fail(item->item.path != NULL, -1);
3766 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3767 g_return_val_if_fail(folder->account != NULL, -1);
3769 session = imap_session_get(folder);
3770 g_return_val_if_fail(session != NULL, -1);
3772 selected_folder = (session->mbox != NULL) &&
3773 (!strcmp(session->mbox, item->item.path));
3774 if (selected_folder) {
3775 ok = imap_cmd_noop(session);
3776 if (ok != IMAP_SUCCESS)
3778 exists = session->exists;
3780 *old_uids_valid = TRUE;
3782 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
3783 &exists, &recent, &uid_next, &uid_val, &unseen);
3784 if (ok != IMAP_SUCCESS)
3787 if(item->item.mtime == uid_val)
3788 *old_uids_valid = TRUE;
3790 *old_uids_valid = FALSE;
3792 debug_print("Freeing imap uid cache\n");
3794 g_slist_free(item->uid_list);
3795 item->uid_list = NULL;
3797 item->item.mtime = uid_val;
3799 imap_delete_all_cached_messages((FolderItem *)item);
3803 if (!selected_folder)
3804 item->uid_next = uid_next;
3806 /* If old uid_next matches new uid_next we can be sure no message
3807 was added to the folder */
3808 if (( selected_folder && !session->folder_content_changed) ||
3809 (!selected_folder && uid_next == item->uid_next)) {
3810 nummsgs = g_slist_length(item->uid_list);
3812 /* If number of messages is still the same we
3813 know our caches message numbers are still valid,
3814 otherwise if the number of messages has decrease
3815 we discard our cache to start a new scan to find
3816 out which numbers have been removed */
3817 if (exists == nummsgs) {
3818 *msgnum_list = g_slist_copy(item->uid_list);
3820 } else if (exists < nummsgs) {
3821 debug_print("Freeing imap uid cache");
3823 g_slist_free(item->uid_list);
3824 item->uid_list = NULL;
3829 *msgnum_list = NULL;
3833 nummsgs = get_list_of_uids(folder, item, &uidlist);
3835 if (nummsgs != exists) {
3836 /* Cache contains more messages then folder, we have cached
3837 an old UID of a message that was removed and new messages
3838 have been added too, otherwise the uid_next check would
3840 debug_print("Freeing imap uid cache");
3842 g_slist_free(item->uid_list);
3843 item->uid_list = NULL;
3845 g_slist_free(*msgnum_list);
3847 nummsgs = get_list_of_uids(folder, item, &uidlist);
3850 *msgnum_list = uidlist;
3852 dir = folder_item_get_path((FolderItem *)item);
3853 debug_print("removing old messages from %s\n", dir);
3854 remove_numbered_files_not_in_list(dir, *msgnum_list);
3860 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item)
3865 flags.perm_flags = MSG_NEW|MSG_UNREAD;
3866 flags.tmp_flags = 0;
3868 g_return_val_if_fail(item != NULL, NULL);
3869 g_return_val_if_fail(file != NULL, NULL);
3871 if (item->stype == F_QUEUE) {
3872 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
3873 } else if (item->stype == F_DRAFT) {
3874 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
3877 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3878 if (!msginfo) return NULL;
3880 msginfo->folder = item;
3885 GSList *imap_get_msginfos(Folder *folder, FolderItem *item, GSList *msgnum_list)
3887 IMAPSession *session;
3888 MsgInfoList *ret = NULL;
3891 g_return_val_if_fail(folder != NULL, NULL);
3892 g_return_val_if_fail(item != NULL, NULL);
3893 g_return_val_if_fail(msgnum_list != NULL, NULL);
3895 session = imap_session_get(folder);
3896 g_return_val_if_fail(session != NULL, NULL);
3898 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3899 NULL, NULL, NULL, NULL);
3900 if (ok != IMAP_SUCCESS)
3903 if (!(item->stype == F_QUEUE || item->stype == F_DRAFT)) {
3904 ret = g_slist_concat(ret,
3905 imap_get_uncached_messages(
3906 session, item, msgnum_list));
3908 MsgNumberList *sorted_list, *elem;
3909 gint startnum, lastnum;
3911 sorted_list = g_slist_sort(g_slist_copy(msgnum_list), g_int_compare);
3913 startnum = lastnum = GPOINTER_TO_INT(sorted_list->data);
3915 for (elem = sorted_list;; elem = g_slist_next(elem)) {
3919 num = GPOINTER_TO_INT(elem->data);
3921 if (num > lastnum + 1 || elem == NULL) {
3923 for (i = startnum; i <= lastnum; ++i) {
3926 file = imap_fetch_msg(folder, item, i);
3928 MsgInfo *msginfo = imap_parse_msg(file, item);
3929 if (msginfo != NULL) {
3930 msginfo->msgnum = i;
3931 ret = g_slist_append(ret, msginfo);
3945 g_slist_free(sorted_list);
3951 MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid)
3953 MsgInfo *msginfo = NULL;
3954 MsgInfoList *msginfolist;
3955 MsgNumberList numlist;
3957 numlist.next = NULL;
3958 numlist.data = GINT_TO_POINTER(uid);
3960 msginfolist = imap_get_msginfos(folder, item, &numlist);
3961 if (msginfolist != NULL) {
3962 msginfo = msginfolist->data;
3963 g_slist_free(msginfolist);
3969 gboolean imap_scan_required(Folder *folder, FolderItem *_item)
3971 IMAPSession *session;
3972 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3973 gint ok, exists = 0, recent = 0, unseen = 0;
3974 guint32 uid_next, uid_val = 0;
3975 gboolean selected_folder;
3977 g_return_val_if_fail(folder != NULL, FALSE);
3978 g_return_val_if_fail(item != NULL, FALSE);
3979 g_return_val_if_fail(item->item.folder != NULL, FALSE);
3980 g_return_val_if_fail(FOLDER_CLASS(item->item.folder) == &imap_class, FALSE);
3982 if (item->item.path == NULL)
3985 session = imap_session_get(folder);
3986 g_return_val_if_fail(session != NULL, FALSE);
3988 selected_folder = (session->mbox != NULL) &&
3989 (!strcmp(session->mbox, item->item.path));
3990 if (selected_folder) {
3991 ok = imap_cmd_noop(session);
3992 if (ok != IMAP_SUCCESS)
3995 if (session->folder_content_changed)
3998 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
3999 &exists, &recent, &uid_next, &uid_val, &unseen);
4000 if (ok != IMAP_SUCCESS)
4003 if ((uid_next != item->uid_next) || (exists < item->item.total_msgs))
4010 void imap_change_flags(Folder *folder, FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
4012 IMAPSession *session;
4013 IMAPFlags flags_set = 0, flags_unset = 0;
4014 gint ok = IMAP_SUCCESS;
4015 MsgNumberList numlist;
4017 g_return_if_fail(folder != NULL);
4018 g_return_if_fail(folder->klass == &imap_class);
4019 g_return_if_fail(item != NULL);
4020 g_return_if_fail(item->folder == folder);
4021 g_return_if_fail(msginfo != NULL);
4022 g_return_if_fail(msginfo->folder == item);
4024 session = imap_session_get(folder);
4025 if (!session) return;
4027 if ((ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
4028 NULL, NULL, NULL, NULL)) != IMAP_SUCCESS)
4031 if (!MSG_IS_MARKED(msginfo->flags) && (newflags & MSG_MARKED))
4032 flags_set |= IMAP_FLAG_FLAGGED;
4033 if ( MSG_IS_MARKED(msginfo->flags) && !(newflags & MSG_MARKED))
4034 flags_unset |= IMAP_FLAG_FLAGGED;
4036 if (!MSG_IS_UNREAD(msginfo->flags) && (newflags & MSG_UNREAD))
4037 flags_unset |= IMAP_FLAG_SEEN;
4038 if ( MSG_IS_UNREAD(msginfo->flags) && !(newflags & MSG_UNREAD))
4039 flags_set |= IMAP_FLAG_SEEN;
4041 if (!MSG_IS_REPLIED(msginfo->flags) && (newflags & MSG_REPLIED))
4042 flags_set |= IMAP_FLAG_ANSWERED;
4043 if ( MSG_IS_REPLIED(msginfo->flags) && !(newflags & MSG_REPLIED))
4044 flags_set |= IMAP_FLAG_ANSWERED;
4046 numlist.next = NULL;
4047 numlist.data = GINT_TO_POINTER(msginfo->msgnum);
4050 ok = imap_set_message_flags(session, &numlist, flags_set, TRUE);
4051 if (ok != IMAP_SUCCESS) return;
4055 ok = imap_set_message_flags(session, &numlist, flags_unset, FALSE);
4056 if (ok != IMAP_SUCCESS) return;
4059 msginfo->flags.perm_flags = newflags;
4064 static gint compare_msginfo(gconstpointer a, gconstpointer b)
4066 return ((MsgInfo *)a)->msgnum - ((MsgInfo *)b)->msgnum;
4069 static guint gslist_find_next_num(MsgNumberList **list, guint num)
4073 g_return_val_if_fail(list != NULL, -1);
4075 for (elem = *list; elem != NULL; elem = g_slist_next(elem))
4076 if (GPOINTER_TO_INT(elem->data) >= num)
4079 return elem != NULL ? GPOINTER_TO_INT(elem->data) : (gint)-1;
4083 * NEW and DELETED flags are not syncronized
4084 * - The NEW/RECENT flags in IMAP folders can not really be directly
4085 * modified by Sylpheed
4086 * - The DELETE/DELETED flag in IMAP and Sylpheed don't have the same
4087 * meaning, in IMAP it always removes the messages from the FolderItem
4088 * in Sylpheed it can mean to move the message to trash
4090 static gint imap_get_flags(Folder *folder, FolderItem *item,
4091 MsgInfoList *msginfo_list, GRelation *msgflags)
4093 IMAPSession *session;
4094 GSList *sorted_list;
4096 GSList *new = NULL, *p_new;
4098 GSList *unseen = NULL, *answered = NULL, *flagged = NULL, *deleted = NULL;
4099 GSList *p_unseen, *p_answered, *p_flagged, *p_deleted;
4101 GSList *seq_list, *cur;
4102 gboolean reverse_seen = FALSE;
4105 gint exists_cnt, recent_cnt, unseen_cnt, uid_next;
4106 guint32 uidvalidity;
4108 g_return_val_if_fail(folder != NULL, -1);
4109 g_return_val_if_fail(item != NULL, -1);
4110 g_return_val_if_fail(msginfo_list != NULL, -1);
4112 session = imap_session_get(folder);
4113 g_return_val_if_fail(session != NULL, -1);
4115 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
4116 NULL, NULL, NULL, NULL);
4117 if (ok != IMAP_SUCCESS)
4120 ok = imap_status(session, IMAP_FOLDER(folder), item->path,
4121 &exists_cnt, &recent_cnt, &uid_next, &uidvalidity, &unseen_cnt);
4123 if (unseen_cnt > exists_cnt / 2)
4124 reverse_seen = TRUE;
4126 cmd_buf = g_string_new(NULL);
4128 sorted_list = g_slist_sort(g_slist_copy(msginfo_list), compare_msginfo);
4130 seq_list = imap_get_seq_set_from_msglist(msginfo_list);
4132 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
4133 IMAPSet imapset = cur->data;
4135 g_string_sprintf(cmd_buf, "RECENT UID %s", imapset);
4136 imap_cmd_search(session, cmd_buf->str, &p_new);
4137 new = g_slist_concat(new, p_new);
4139 g_string_sprintf(cmd_buf, "%sSEEN UID %s", reverse_seen ? "" : "UN", imapset);
4140 imap_cmd_search(session, cmd_buf->str, &p_unseen);
4141 unseen = g_slist_concat(unseen, p_unseen);
4143 g_string_sprintf(cmd_buf, "ANSWERED UID %s", imapset);
4144 imap_cmd_search(session, cmd_buf->str, &p_answered);
4145 answered = g_slist_concat(answered, p_answered);
4147 g_string_sprintf(cmd_buf, "FLAGGED UID %s", imapset);
4148 imap_cmd_search(session, cmd_buf->str, &p_flagged);
4149 flagged = g_slist_concat(flagged, p_flagged);
4151 g_string_sprintf(cmd_buf, "DELETED UID %s", imapset);
4152 imap_cmd_search(session, cmd_buf->str, &p_deleted);
4153 deleted = g_slist_concat(deleted, p_deleted);
4161 p_answered = answered;
4162 p_flagged = flagged;
4163 p_deleted = deleted;
4165 for (elem = sorted_list; elem != NULL; elem = g_slist_next(elem)) {
4169 msginfo = (MsgInfo *) elem->data;
4170 flags = msginfo->flags.perm_flags;
4171 flags &= ~((reverse_seen ? 0 : MSG_UNREAD) | MSG_REPLIED | MSG_MARKED);
4173 flags |= MSG_UNREAD;
4175 if (gslist_find_next_num(&p_new, msginfo->msgnum) == msginfo->msgnum)
4178 if (gslist_find_next_num(&p_unseen, msginfo->msgnum) == msginfo->msgnum) {
4179 if (!reverse_seen) {
4180 flags |= MSG_UNREAD;
4182 flags &= ~(MSG_UNREAD | MSG_NEW);
4185 if (gslist_find_next_num(&p_answered, msginfo->msgnum) == msginfo->msgnum)
4186 flags |= MSG_REPLIED;
4187 if (gslist_find_next_num(&p_flagged, msginfo->msgnum) == msginfo->msgnum)
4188 flags |= MSG_MARKED;
4190 if (gslist_find_next_num(&p_deleted, msginfo->msgnum) == msginfo->msgnum)
4191 MSG_SET_PERM_FLAGS(msginfo->flags, MSG_DELETED);
4193 g_relation_insert(msgflags, msginfo, GINT_TO_POINTER(flags));
4196 imap_seq_set_free(seq_list);
4197 g_slist_free(deleted);
4198 g_slist_free(flagged);
4199 g_slist_free(answered);
4200 g_slist_free(unseen);
4201 /* new not freed in original patch ??? */
4202 g_slist_free(sorted_list);
4203 g_string_free(cmd_buf, TRUE);