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(IMAP_FOLDER(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_;
2474 guint32 uid_validity_;
2476 if (!exists || !recent || !unseen || !uid_validity) {
2477 if (session->mbox && strcmp(session->mbox, path) == 0)
2478 return IMAP_SUCCESS;
2482 uid_validity = &uid_validity_;
2485 g_free(session->mbox);
2486 session->mbox = NULL;
2488 real_path = imap_get_real_path(folder, path);
2489 ok = imap_cmd_select(session, real_path,
2490 exists, recent, unseen, uid_validity);
2491 if (ok != IMAP_SUCCESS)
2492 log_warning(_("can't select folder: %s\n"), real_path);
2494 session->mbox = g_strdup(path);
2495 session->folder_content_changed = FALSE;
2502 #define THROW(err) { ok = err; goto catch; }
2504 static gint imap_status(IMAPSession *session, IMAPFolder *folder,
2506 gint *messages, gint *recent,
2507 guint32 *uid_next, guint32 *uid_validity,
2513 GPtrArray *argbuf = NULL;
2516 if (messages && recent && uid_next && uid_validity && unseen) {
2517 *messages = *recent = *uid_next = *uid_validity = *unseen = 0;
2518 argbuf = g_ptr_array_new();
2521 real_path = imap_get_real_path(folder, path);
2522 QUOTE_IF_REQUIRED(real_path_, real_path);
2523 imap_gen_send(session, "STATUS %s "
2524 "(MESSAGES RECENT UIDNEXT UIDVALIDITY UNSEEN)",
2527 ok = imap_cmd_ok(session, argbuf);
2528 if (ok != IMAP_SUCCESS || !argbuf) THROW(ok);
2530 str = search_array_str(argbuf, "STATUS");
2531 if (!str) THROW(IMAP_ERROR);
2533 str = strchr(str, '(');
2534 if (!str) THROW(IMAP_ERROR);
2536 while (*str != '\0' && *str != ')') {
2537 while (*str == ' ') str++;
2539 if (!strncmp(str, "MESSAGES ", 9)) {
2541 *messages = strtol(str, &str, 10);
2542 } else if (!strncmp(str, "RECENT ", 7)) {
2544 *recent = strtol(str, &str, 10);
2545 } else if (!strncmp(str, "UIDNEXT ", 8)) {
2547 *uid_next = strtoul(str, &str, 10);
2548 } else if (!strncmp(str, "UIDVALIDITY ", 12)) {
2550 *uid_validity = strtoul(str, &str, 10);
2551 } else if (!strncmp(str, "UNSEEN ", 7)) {
2553 *unseen = strtol(str, &str, 10);
2555 g_warning("invalid STATUS response: %s\n", str);
2563 ptr_array_free_strings(argbuf);
2564 g_ptr_array_free(argbuf, TRUE);
2572 static gboolean imap_has_capability(IMAPSession *session, const gchar *cap)
2576 for (p = session->capability; *p != NULL; ++p) {
2577 if (!g_strcasecmp(*p, cap))
2584 static void imap_free_capabilities(IMAPSession *session)
2586 g_strfreev(session->capability);
2587 session->capability = NULL;
2590 /* low-level IMAP4rev1 commands */
2592 static gint imap_cmd_authenticate(IMAPSession *session, const gchar *user,
2593 const gchar *pass, IMAPAuthType type)
2600 gchar hexdigest[33];
2604 auth_type = "CRAM-MD5";
2606 imap_gen_send(session, "AUTHENTICATE %s", auth_type);
2607 ok = imap_gen_recv(session, &buf);
2608 if (ok != IMAP_SUCCESS || buf[0] != '+' || buf[1] != ' ') {
2613 challenge = g_malloc(strlen(buf + 2) + 1);
2614 challenge_len = base64_decode(challenge, buf + 2, -1);
2615 challenge[challenge_len] = '\0';
2617 log_print("IMAP< [Decoded: %s]\n", challenge);
2619 md5_hex_hmac(hexdigest, challenge, challenge_len, pass, strlen(pass));
2622 response = g_strdup_printf("%s %s", user, hexdigest);
2623 log_print("IMAP> [Encoded: %s]\n", response);
2624 response64 = g_malloc((strlen(response) + 3) * 2 + 1);
2625 base64_encode(response64, response, strlen(response));
2628 log_print("IMAP> %s\n", response64);
2629 sock_puts(SESSION(session)->sock, response64);
2630 ok = imap_cmd_ok(session, NULL);
2631 if (ok != IMAP_SUCCESS)
2632 log_warning(_("IMAP4 authentication failed.\n"));
2637 static gint imap_cmd_login(IMAPSession *session,
2638 const gchar *user, const gchar *pass)
2640 gchar *user_, *pass_;
2643 QUOTE_IF_REQUIRED(user_, user);
2644 QUOTE_IF_REQUIRED(pass_, pass);
2645 imap_gen_send(session, "LOGIN %s %s", user_, pass_);
2647 ok = imap_cmd_ok(session, NULL);
2648 if (ok != IMAP_SUCCESS)
2649 log_warning(_("IMAP4 login failed.\n"));
2654 static gint imap_cmd_logout(IMAPSession *session)
2656 imap_gen_send(session, "LOGOUT");
2657 return imap_cmd_ok(session, NULL);
2660 static gint imap_cmd_noop(IMAPSession *session)
2662 imap_gen_send(session, "NOOP");
2663 return imap_cmd_ok(session, NULL);
2666 static gint imap_cmd_starttls(IMAPSession *session)
2668 imap_gen_send(session, "STARTTLS");
2669 return imap_cmd_ok(session, NULL);
2672 #define THROW(err) { ok = err; goto catch; }
2674 static gint imap_cmd_namespace(IMAPSession *session, gchar **ns_str)
2680 argbuf = g_ptr_array_new();
2682 imap_gen_send(session, "NAMESPACE");
2683 if ((ok = imap_cmd_ok(session, argbuf)) != IMAP_SUCCESS) THROW(ok);
2685 str = search_array_str(argbuf, "NAMESPACE");
2686 if (!str) THROW(IMAP_ERROR);
2688 *ns_str = g_strdup(str);
2691 ptr_array_free_strings(argbuf);
2692 g_ptr_array_free(argbuf, TRUE);
2699 static gint imap_cmd_list(IMAPSession *session, const gchar *ref,
2700 const gchar *mailbox, GPtrArray *argbuf)
2702 gchar *ref_, *mailbox_;
2704 if (!ref) ref = "\"\"";
2705 if (!mailbox) mailbox = "\"\"";
2707 QUOTE_IF_REQUIRED(ref_, ref);
2708 QUOTE_IF_REQUIRED(mailbox_, mailbox);
2709 imap_gen_send(session, "LIST %s %s", ref_, mailbox_);
2711 return imap_cmd_ok(session, argbuf);
2714 #define THROW goto catch
2716 static gint imap_cmd_do_select(IMAPSession *session, const gchar *folder,
2718 gint *exists, gint *recent, gint *unseen,
2719 guint32 *uid_validity)
2726 unsigned int uid_validity_;
2728 *exists = *recent = *unseen = *uid_validity = 0;
2729 argbuf = g_ptr_array_new();
2732 select_cmd = "EXAMINE";
2734 select_cmd = "SELECT";
2736 QUOTE_IF_REQUIRED(folder_, folder);
2737 imap_gen_send(session, "%s %s", select_cmd, folder_);
2739 if ((ok = imap_cmd_ok(session, argbuf)) != IMAP_SUCCESS) THROW;
2741 resp_str = search_array_contain_str(argbuf, "EXISTS");
2743 if (sscanf(resp_str,"%d EXISTS", exists) != 1) {
2744 g_warning("imap_cmd_select(): invalid EXISTS line.\n");
2749 resp_str = search_array_contain_str(argbuf, "RECENT");
2751 if (sscanf(resp_str, "%d RECENT", recent) != 1) {
2752 g_warning("imap_cmd_select(): invalid RECENT line.\n");
2757 resp_str = search_array_contain_str(argbuf, "UIDVALIDITY");
2759 if (sscanf(resp_str, "OK [UIDVALIDITY %u] ", &uid_validity_)
2761 g_warning("imap_cmd_select(): invalid UIDVALIDITY line.\n");
2764 *uid_validity = uid_validity_;
2767 resp_str = search_array_contain_str(argbuf, "UNSEEN");
2769 if (sscanf(resp_str, "OK [UNSEEN %d] ", unseen) != 1) {
2770 g_warning("imap_cmd_select(): invalid UNSEEN line.\n");
2776 ptr_array_free_strings(argbuf);
2777 g_ptr_array_free(argbuf, TRUE);
2782 static gint imap_cmd_select(IMAPSession *session, const gchar *folder,
2783 gint *exists, gint *recent, gint *unseen,
2784 guint32 *uid_validity)
2786 return imap_cmd_do_select(session, folder, FALSE,
2787 exists, recent, unseen, uid_validity);
2790 static gint imap_cmd_examine(IMAPSession *session, const gchar *folder,
2791 gint *exists, gint *recent, gint *unseen,
2792 guint32 *uid_validity)
2794 return imap_cmd_do_select(session, folder, TRUE,
2795 exists, recent, unseen, uid_validity);
2800 static gint imap_cmd_create(IMAPSession *session, const gchar *folder)
2804 QUOTE_IF_REQUIRED(folder_, folder);
2805 imap_gen_send(session, "CREATE %s", folder_);
2807 return imap_cmd_ok(session, NULL);
2810 static gint imap_cmd_rename(IMAPSession *session, const gchar *old_folder,
2811 const gchar *new_folder)
2813 gchar *old_folder_, *new_folder_;
2815 QUOTE_IF_REQUIRED(old_folder_, old_folder);
2816 QUOTE_IF_REQUIRED(new_folder_, new_folder);
2817 imap_gen_send(session, "RENAME %s %s", old_folder_, new_folder_);
2819 return imap_cmd_ok(session, NULL);
2822 static gint imap_cmd_delete(IMAPSession *session, const gchar *folder)
2826 QUOTE_IF_REQUIRED(folder_, folder);
2827 imap_gen_send(session, "DELETE %s", folder_);
2829 return imap_cmd_ok(session, NULL);
2832 static gint imap_cmd_search(IMAPSession *session, const gchar *criteria,
2839 g_return_val_if_fail(criteria != NULL, IMAP_ERROR);
2840 g_return_val_if_fail(list != NULL, IMAP_ERROR);
2844 argbuf = g_ptr_array_new();
2845 imap_gen_send(session, "UID SEARCH %s", criteria);
2847 ok = imap_cmd_ok(session, argbuf);
2848 if (ok != IMAP_SUCCESS) {
2849 ptr_array_free_strings(argbuf);
2850 g_ptr_array_free(argbuf, TRUE);
2854 if ((uidlist = search_array_str(argbuf, "SEARCH ")) != NULL) {
2855 gchar **strlist, **p;
2857 strlist = g_strsplit(uidlist + 7, " ", 0);
2858 for (p = strlist; *p != NULL; ++p) {
2861 if (sscanf(*p, "%d", &msgnum) == 1)
2862 *list = g_slist_append(*list, GINT_TO_POINTER(msgnum));
2864 g_strfreev(strlist);
2866 ptr_array_free_strings(argbuf);
2867 g_ptr_array_free(argbuf, TRUE);
2869 return IMAP_SUCCESS;
2872 static gint imap_cmd_fetch(IMAPSession *session, guint32 uid,
2873 const gchar *filename)
2881 g_return_val_if_fail(filename != NULL, IMAP_ERROR);
2883 imap_gen_send(session, "UID FETCH %d BODY.PEEK[]", uid);
2885 while ((ok = imap_gen_recv(session, &buf)) == IMAP_SUCCESS) {
2886 if (buf[0] != '*' || buf[1] != ' ') {
2890 if (strstr(buf, "FETCH") != NULL) break;
2893 if (ok != IMAP_SUCCESS) {
2898 #define RETURN_ERROR_IF_FAIL(cond) \
2901 return IMAP_ERROR; \
2904 cur_pos = strchr(buf, '{');
2905 RETURN_ERROR_IF_FAIL(cur_pos != NULL);
2906 cur_pos = strchr_cpy(cur_pos + 1, '}', size_str, sizeof(size_str));
2907 RETURN_ERROR_IF_FAIL(cur_pos != NULL);
2908 size_num = atol(size_str);
2909 RETURN_ERROR_IF_FAIL(size_num >= 0);
2911 RETURN_ERROR_IF_FAIL(*cur_pos == '\0');
2913 #undef RETURN_ERROR_IF_FAIL
2917 if (recv_bytes_write_to_file(SESSION(session)->sock,
2918 size_num, filename) != 0)
2921 if (imap_gen_recv(session, &buf) != IMAP_SUCCESS) {
2926 if (buf[0] == '\0' || buf[strlen(buf) - 1] != ')') {
2932 ok = imap_cmd_ok(session, NULL);
2937 static gint imap_cmd_append(IMAPSession *session, const gchar *destfolder,
2938 const gchar *file, IMAPFlags flags,
2947 gchar buf[BUFFSIZE];
2952 g_return_val_if_fail(file != NULL, IMAP_ERROR);
2954 size = get_file_size_as_crlf(file);
2955 if ((fp = fopen(file, "rb")) == NULL) {
2956 FILE_OP_ERROR(file, "fopen");
2959 QUOTE_IF_REQUIRED(destfolder_, destfolder);
2960 flag_str = imap_get_flag_str(flags);
2961 imap_gen_send(session, "APPEND %s (%s) {%d}",
2962 destfolder_, flag_str, size);
2965 ok = imap_gen_recv(session, &ret);
2966 if (ok != IMAP_SUCCESS || ret[0] != '+' || ret[1] != ' ') {
2967 log_warning(_("can't append %s to %s\n"), file, destfolder_);
2974 log_print("IMAP4> %s\n", _("(sending file...)"));
2976 while (fgets(buf, sizeof(buf), fp) != NULL) {
2978 if (sock_puts(SESSION(session)->sock, buf) < 0) {
2985 FILE_OP_ERROR(file, "fgets");
2990 sock_puts(SESSION(session)->sock, "");
2994 if (new_uid != NULL)
2997 if (new_uid != NULL && session->uidplus) {
2998 argbuf = g_ptr_array_new();
3000 ok = imap_cmd_ok(session, argbuf);
3001 if ((ok == IMAP_SUCCESS) && (argbuf->len > 0)) {
3002 resp_str = g_ptr_array_index(argbuf, argbuf->len - 1);
3004 sscanf(resp_str, "%*u OK [APPENDUID %*u %u]",
3006 *new_uid = new_uid_;
3010 ptr_array_free_strings(argbuf);
3011 g_ptr_array_free(argbuf, TRUE);
3013 ok = imap_cmd_ok(session, NULL);
3015 if (ok != IMAP_SUCCESS)
3016 log_warning(_("can't append message to %s\n"),
3022 static MsgNumberList *imapset_to_numlist(IMAPSet imapset)
3024 gchar **ranges, **range;
3026 MsgNumberList *uids = NULL;
3028 ranges = g_strsplit(imapset, ",", 0);
3029 for (range = ranges; *range != NULL; range++) {
3030 printf("%s\n", *range);
3031 if(sscanf(*range, "%u:%u", &low, &high) == 1)
3032 uids = g_slist_prepend(uids, GINT_TO_POINTER(low));
3035 for (i = low; i <= high; i++)
3036 uids = g_slist_prepend(uids, GINT_TO_POINTER(i));
3039 uids = g_slist_reverse(uids);
3045 static gint imap_cmd_copy(IMAPSession *session, const gchar *seq_set,
3046 const gchar *destfolder, GRelation *uid_mapping)
3051 g_return_val_if_fail(session != NULL, IMAP_ERROR);
3052 g_return_val_if_fail(seq_set != NULL, IMAP_ERROR);
3053 g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
3055 QUOTE_IF_REQUIRED(destfolder_, destfolder);
3056 imap_gen_send(session, "UID COPY %s %s", seq_set, destfolder_);
3058 if (uid_mapping != NULL && session->uidplus) {
3060 gchar *resp_str = NULL, *olduids_str, *newuids_str;
3061 MsgNumberList *olduids, *old_cur, *newuids, *new_cur;
3063 reply = g_ptr_array_new();
3064 ok = imap_cmd_ok(session, reply);
3065 if ((ok == IMAP_SUCCESS) && (reply->len > 0)) {
3066 resp_str = g_ptr_array_index(reply, reply->len - 1);
3068 olduids_str = g_new0(gchar, strlen(resp_str));
3069 newuids_str = g_new0(gchar, strlen(resp_str));
3070 if (sscanf(resp_str, "%*s OK [COPYUID %*u %[0-9,:] %[0-9,:]]",
3071 olduids_str, newuids_str) == 2) {
3072 olduids = imapset_to_numlist(olduids_str);
3073 newuids = imapset_to_numlist(newuids_str);
3077 while(old_cur != NULL && new_cur != NULL) {
3078 g_relation_insert(uid_mapping,
3079 GPOINTER_TO_INT(old_cur->data),
3080 GPOINTER_TO_INT(new_cur->data));
3081 old_cur = g_slist_next(old_cur);
3082 new_cur = g_slist_next(new_cur);
3085 g_slist_free(olduids);
3086 g_slist_free(newuids);
3088 g_free(olduids_str);
3089 g_free(newuids_str);
3092 ptr_array_free_strings(reply);
3093 g_ptr_array_free(reply, TRUE);
3095 ok = imap_cmd_ok(session, NULL);
3097 if (ok != IMAP_SUCCESS)
3098 log_warning(_("can't copy %s to %s\n"), seq_set, destfolder_);
3103 gint imap_cmd_envelope(IMAPSession *session, IMAPSet set)
3105 static GString *header_fields = NULL;
3107 if (header_fields == NULL) {
3108 const HeaderEntry *headers, *elem;
3110 headers = procheader_get_headernames(FALSE);
3111 header_fields = g_string_new("");
3113 for (elem = headers; elem->name != NULL; ++elem) {
3114 gint namelen = strlen(elem->name);
3116 /* Header fields ending with space are not rfc822 headers */
3117 if (elem->name[namelen - 1] == ' ')
3120 /* strip : at the of header field */
3121 if(elem->name[namelen - 1] == ':')
3127 g_string_sprintfa(header_fields, "%s%.*s",
3128 header_fields->str[0] != '\0' ? " " : "",
3129 namelen, elem->name);
3134 (session, "UID FETCH %s (UID FLAGS RFC822.SIZE BODY.PEEK[HEADER.FIELDS (%s)])",
3135 set, header_fields->str);
3137 return IMAP_SUCCESS;
3140 static gint imap_cmd_store(IMAPSession *session, IMAPSet seq_set,
3145 imap_gen_send(session, "UID STORE %s %s", seq_set, sub_cmd);
3147 if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS) {
3148 log_warning(_("error while imap command: STORE %s %s\n"),
3153 return IMAP_SUCCESS;
3156 static gint imap_cmd_expunge(IMAPSession *session, IMAPSet seq_set)
3160 if (seq_set && session->uidplus)
3161 imap_gen_send(session, "UID EXPUNGE %s", seq_set);
3163 imap_gen_send(session, "EXPUNGE");
3164 if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS) {
3165 log_warning(_("error while imap command: EXPUNGE\n"));
3169 return IMAP_SUCCESS;
3172 static gint imap_cmd_close(IMAPSession *session)
3176 imap_gen_send(session, "CLOSE");
3177 if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS)
3178 log_warning(_("error while imap command: CLOSE\n"));
3183 static gint imap_cmd_ok(IMAPSession *session, GPtrArray *argbuf)
3185 gint ok = IMAP_SUCCESS;
3190 while ((ok = imap_gen_recv(session, &buf))
3192 // make sure data is long enough for any substring of buf
3193 data = alloca(strlen(buf) + 1);
3195 // untagged line read
3196 if (buf[0] == '*' && buf[1] == ' ') {
3199 g_ptr_array_add(argbuf, g_strdup(buf + 2));
3201 if (sscanf(buf + 2, "%d %s", &num, data) >= 2) {
3202 if (!strcmp(data, "EXISTS")) {
3203 session->exists = num;
3204 session->folder_content_changed = TRUE;
3207 if(!strcmp(data, "EXPUNGE")) {
3209 session->folder_content_changed = TRUE;
3212 // tagged line with correct tag and OK response found
3213 } else if ((sscanf(buf, "%d %s", &cmd_num, data) >= 2) &&
3214 (cmd_num == session->cmd_count) &&
3215 !strcmp(data, "OK")) {
3217 g_ptr_array_add(argbuf, g_strdup(buf));
3231 static void imap_gen_send(IMAPSession *session, const gchar *format, ...)
3238 va_start(args, format);
3239 tmp = g_strdup_vprintf(format, args);
3242 session->cmd_count++;
3244 buf = g_strdup_printf("%d %s\r\n", session->cmd_count, tmp);
3245 if (!strncasecmp(tmp, "LOGIN ", 6) && (p = strchr(tmp + 6, ' '))) {
3247 log_print("IMAP4> %d %s ********\n", session->cmd_count, tmp);
3249 log_print("IMAP4> %d %s\n", session->cmd_count, tmp);
3251 sock_write_all(SESSION(session)->sock, buf, strlen(buf));
3256 static gint imap_gen_recv(IMAPSession *session, gchar **ret)
3258 if ((*ret = sock_getline(SESSION(session)->sock)) == NULL)
3263 log_print("IMAP4< %s\n", *ret);
3265 return IMAP_SUCCESS;
3269 /* misc utility functions */
3271 static gchar *strchr_cpy(const gchar *src, gchar ch, gchar *dest, gint len)
3276 tmp = strchr(src, ch);
3280 memcpy(dest, src, MIN(tmp - src, len - 1));
3281 dest[MIN(tmp - src, len - 1)] = '\0';
3286 static gchar *get_quoted(const gchar *src, gchar ch, gchar *dest, gint len)
3288 const gchar *p = src;
3291 g_return_val_if_fail(*p == ch, NULL);
3296 while (*p != '\0' && *p != ch) {
3298 if (*p == '\\' && *(p + 1) != '\0')
3307 return (gchar *)(*p == ch ? p + 1 : p);
3310 static gchar *search_array_contain_str(GPtrArray *array, const gchar *str)
3314 for (i = 0; i < array->len; i++) {
3317 tmp = g_ptr_array_index(array, i);
3318 if (strstr(tmp, str) != NULL)
3325 static gchar *search_array_str(GPtrArray *array, const gchar *str)
3332 for (i = 0; i < array->len; i++) {
3335 tmp = g_ptr_array_index(array, i);
3336 if (!strncmp(tmp, str, len))
3343 static void imap_path_separator_subst(gchar *str, gchar separator)
3346 gboolean in_escape = FALSE;
3348 if (!separator || separator == '/') return;
3350 for (p = str; *p != '\0'; p++) {
3351 if (*p == '/' && !in_escape)
3353 else if (*p == '&' && *(p + 1) != '-' && !in_escape)
3355 else if (*p == '-' && in_escape)
3360 static gchar *imap_modified_utf7_to_locale(const gchar *mutf7_str)
3363 const gchar *from_p;
3366 to = g_malloc(strlen(mutf7_str) + 1);
3369 for (from_p = mutf7_str; *from_p != '\0'; from_p++) {
3370 if (*from_p == '&' && *(from_p + 1) == '-') {
3380 static iconv_t cd = (iconv_t)-1;
3381 static gboolean iconv_ok = TRUE;
3384 size_t norm_utf7_len;
3386 gchar *to_str, *to_p;
3388 gboolean in_escape = FALSE;
3390 if (!iconv_ok) return g_strdup(mutf7_str);
3392 if (cd == (iconv_t)-1) {
3393 cd = iconv_open(conv_get_current_charset_str(), "UTF-7");
3394 if (cd == (iconv_t)-1) {
3395 g_warning("iconv cannot convert UTF-7 to %s\n",
3396 conv_get_current_charset_str());
3398 return g_strdup(mutf7_str);
3402 norm_utf7 = g_string_new(NULL);
3404 for (p = mutf7_str; *p != '\0'; p++) {
3405 /* replace: '&' -> '+',
3407 escaped ',' -> '/' */
3408 if (!in_escape && *p == '&') {
3409 if (*(p + 1) != '-') {
3410 g_string_append_c(norm_utf7, '+');
3413 g_string_append_c(norm_utf7, '&');
3416 } else if (in_escape && *p == ',') {
3417 g_string_append_c(norm_utf7, '/');
3418 } else if (in_escape && *p == '-') {
3419 g_string_append_c(norm_utf7, '-');
3422 g_string_append_c(norm_utf7, *p);
3426 norm_utf7_p = norm_utf7->str;
3427 norm_utf7_len = norm_utf7->len;
3428 to_len = strlen(mutf7_str) * 5;
3429 to_p = to_str = g_malloc(to_len + 1);
3431 if (iconv(cd, (ICONV_CONST gchar **)&norm_utf7_p, &norm_utf7_len,
3432 &to_p, &to_len) == -1) {
3433 g_warning(_("iconv cannot convert UTF-7 to %s\n"),
3434 conv_get_current_charset_str());
3435 g_string_free(norm_utf7, TRUE);
3437 return g_strdup(mutf7_str);
3440 /* second iconv() call for flushing */
3441 iconv(cd, NULL, NULL, &to_p, &to_len);
3442 g_string_free(norm_utf7, TRUE);
3446 #endif /* !HAVE_ICONV */
3449 static gchar *imap_locale_to_modified_utf7(const gchar *from)
3452 const gchar *from_p;
3455 to = g_malloc(strlen(from) * 2 + 1);
3458 for (from_p = from; *from_p != '\0'; from_p++) {
3459 if (*from_p == '&') {
3469 static iconv_t cd = (iconv_t)-1;
3470 static gboolean iconv_ok = TRUE;
3471 gchar *norm_utf7, *norm_utf7_p;
3472 size_t from_len, norm_utf7_len;
3474 gchar *from_tmp, *to, *p;
3475 gboolean in_escape = FALSE;
3477 if (!iconv_ok) return g_strdup(from);
3479 if (cd == (iconv_t)-1) {
3480 cd = iconv_open("UTF-7", conv_get_current_charset_str());
3481 if (cd == (iconv_t)-1) {
3482 g_warning("iconv cannot convert %s to UTF-7\n",
3483 conv_get_current_charset_str());
3485 return g_strdup(from);
3489 Xstrdup_a(from_tmp, from, return g_strdup(from));
3490 from_len = strlen(from);
3491 norm_utf7_len = from_len * 5;
3492 Xalloca(norm_utf7, norm_utf7_len + 1, return g_strdup(from));
3493 norm_utf7_p = norm_utf7;
3495 #define IS_PRINT(ch) (isprint(ch) && isascii(ch))
3497 while (from_len > 0) {
3498 if (*from_tmp == '+') {
3499 *norm_utf7_p++ = '+';
3500 *norm_utf7_p++ = '-';
3504 } else if (IS_PRINT(*(guchar *)from_tmp)) {
3505 /* printable ascii char */
3506 *norm_utf7_p = *from_tmp;
3512 size_t mb_len = 0, conv_len = 0;
3514 /* unprintable char: convert to UTF-7 */
3516 while (!IS_PRINT(*(guchar *)p) && conv_len < from_len) {
3517 mb_len = mblen(p, MB_LEN_MAX);
3519 g_warning("wrong multibyte sequence\n");
3520 return g_strdup(from);
3526 from_len -= conv_len;
3527 if (iconv(cd, (ICONV_CONST gchar **)&from_tmp,
3529 &norm_utf7_p, &norm_utf7_len) == -1) {
3530 g_warning("iconv cannot convert %s to UTF-7\n",
3531 conv_get_current_charset_str());
3532 return g_strdup(from);
3535 /* second iconv() call for flushing */
3536 iconv(cd, NULL, NULL, &norm_utf7_p, &norm_utf7_len);
3542 *norm_utf7_p = '\0';
3543 to_str = g_string_new(NULL);
3544 for (p = norm_utf7; p < norm_utf7_p; p++) {
3545 /* replace: '&' -> "&-",
3548 BASE64 '/' -> ',' */
3549 if (!in_escape && *p == '&') {
3550 g_string_append(to_str, "&-");
3551 } else if (!in_escape && *p == '+') {
3552 if (*(p + 1) == '-') {
3553 g_string_append_c(to_str, '+');
3556 g_string_append_c(to_str, '&');
3559 } else if (in_escape && *p == '/') {
3560 g_string_append_c(to_str, ',');
3561 } else if (in_escape && *p == '-') {
3562 g_string_append_c(to_str, '-');
3565 g_string_append_c(to_str, *p);
3571 g_string_append_c(to_str, '-');
3575 g_string_free(to_str, FALSE);
3578 #endif /* !HAVE_ICONV */
3581 static GSList *imap_get_seq_set_from_numlist(MsgNumberList *numlist)
3584 GSList *sorted_list, *cur;
3585 guint first, last, next;
3587 GSList *ret_list = NULL;
3589 if (numlist == NULL)
3592 str = g_string_sized_new(256);
3594 sorted_list = g_slist_copy(numlist);
3595 sorted_list = g_slist_sort(sorted_list, g_int_compare);
3597 first = GPOINTER_TO_INT(sorted_list->data);
3599 for (cur = sorted_list; cur != NULL; cur = g_slist_next(cur)) {
3600 last = GPOINTER_TO_INT(cur->data);
3602 next = GPOINTER_TO_INT(cur->next->data);
3606 if (last + 1 != next || next == 0) {
3608 g_string_append_c(str, ',');
3610 g_string_sprintfa(str, "%u", first);
3612 g_string_sprintfa(str, "%u:%u", first, last);
3616 if (str->len > IMAP_CMD_LIMIT) {
3617 ret_str = g_strdup(str->str);
3618 ret_list = g_slist_append(ret_list, ret_str);
3619 g_string_truncate(str, 0);
3625 ret_str = g_strdup(str->str);
3626 ret_list = g_slist_append(ret_list, ret_str);
3629 g_slist_free(sorted_list);
3630 g_string_free(str, TRUE);
3635 static GSList *imap_get_seq_set_from_msglist(MsgInfoList *msglist)
3637 MsgNumberList *numlist = NULL;
3641 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
3642 MsgInfo *msginfo = (MsgInfo *) cur->data;
3644 numlist = g_slist_append(numlist, GINT_TO_POINTER(msginfo->msgnum));
3646 seq_list = imap_get_seq_set_from_numlist(numlist);
3647 g_slist_free(numlist);
3652 static void imap_seq_set_free(GSList *seq_list)
3654 slist_free_strings(seq_list);
3655 g_slist_free(seq_list);
3659 static gboolean imap_rename_folder_func(GNode *node, gpointer data)
3661 FolderItem *item = node->data;
3662 gchar **paths = data;
3663 const gchar *oldpath = paths[0];
3664 const gchar *newpath = paths[1];
3666 gchar *new_itempath;
3669 oldpathlen = strlen(oldpath);
3670 if (strncmp(oldpath, item->path, oldpathlen) != 0) {
3671 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
3675 base = item->path + oldpathlen;
3676 while (*base == G_DIR_SEPARATOR) base++;
3678 new_itempath = g_strdup(newpath);
3680 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
3683 item->path = new_itempath;
3688 static gint get_list_of_uids(Folder *folder, IMAPFolderItem *item, GSList **msgnum_list)
3690 gint ok, nummsgs = 0, lastuid_old;
3691 IMAPSession *session;
3692 GSList *uidlist, *elem;
3695 session = imap_session_get(folder);
3696 g_return_val_if_fail(session != NULL, -1);
3698 ok = imap_select(session, IMAP_FOLDER(folder), item->item.path,
3699 NULL, NULL, NULL, NULL);
3700 if (ok != IMAP_SUCCESS)
3703 cmd_buf = g_strdup_printf("UID %d:*", item->lastuid + 1);
3704 ok = imap_cmd_search(session, cmd_buf, &uidlist);
3707 if (ok == IMAP_SOCKET) {
3708 session_destroy((Session *)session);
3709 ((RemoteFolder *)folder)->session = NULL;
3713 if (ok != IMAP_SUCCESS) {
3717 argbuf = g_ptr_array_new();
3719 cmd_buf = g_strdup_printf("UID FETCH %d:* (UID)", item->lastuid + 1);
3720 imap_gen_send(session, cmd_buf);
3722 ok = imap_cmd_ok(session, argbuf);
3723 if (ok != IMAP_SUCCESS) {
3724 ptr_array_free_strings(argbuf);
3725 g_ptr_array_free(argbuf, TRUE);
3729 for(i = 0; i < argbuf->len; i++) {
3732 if((ret = sscanf(g_ptr_array_index(argbuf, i),
3733 "%*d FETCH (UID %d)", &msgnum)) == 1)
3734 uidlist = g_slist_prepend(uidlist, GINT_TO_POINTER(msgnum));
3736 ptr_array_free_strings(argbuf);
3737 g_ptr_array_free(argbuf, TRUE);
3740 lastuid_old = item->lastuid;
3741 *msgnum_list = g_slist_copy(item->uid_list);
3742 nummsgs = g_slist_length(*msgnum_list);
3743 debug_print("Got %d uids from cache\n", g_slist_length(item->uid_list));
3745 for (elem = uidlist; elem != NULL; elem = g_slist_next(elem)) {
3748 msgnum = GPOINTER_TO_INT(elem->data);
3749 if (msgnum > lastuid_old) {
3750 *msgnum_list = g_slist_prepend(*msgnum_list, GINT_TO_POINTER(msgnum));
3751 item->uid_list = g_slist_prepend(item->uid_list, GINT_TO_POINTER(msgnum));
3754 if(msgnum > item->lastuid)
3755 item->lastuid = msgnum;
3758 g_slist_free(uidlist);
3763 gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list, gboolean *old_uids_valid)
3765 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3766 IMAPSession *session;
3767 gint ok, nummsgs = 0, exists, recent, uid_val, uid_next, unseen;
3770 gboolean selected_folder;
3772 g_return_val_if_fail(folder != NULL, -1);
3773 g_return_val_if_fail(item != NULL, -1);
3774 g_return_val_if_fail(item->item.path != NULL, -1);
3775 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3776 g_return_val_if_fail(folder->account != NULL, -1);
3778 session = imap_session_get(folder);
3779 g_return_val_if_fail(session != NULL, -1);
3781 selected_folder = (session->mbox != NULL) &&
3782 (!strcmp(session->mbox, item->item.path));
3783 if (selected_folder) {
3784 ok = imap_cmd_noop(session);
3785 if (ok != IMAP_SUCCESS)
3787 exists = session->exists;
3789 *old_uids_valid = TRUE;
3791 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
3792 &exists, &recent, &uid_next, &uid_val, &unseen);
3793 if (ok != IMAP_SUCCESS)
3796 if(item->item.mtime == uid_val)
3797 *old_uids_valid = TRUE;
3799 *old_uids_valid = FALSE;
3801 debug_print("Freeing imap uid cache\n");
3803 g_slist_free(item->uid_list);
3804 item->uid_list = NULL;
3806 item->item.mtime = uid_val;
3808 imap_delete_all_cached_messages((FolderItem *)item);
3812 if (!selected_folder)
3813 item->uid_next = uid_next;
3815 /* If old uid_next matches new uid_next we can be sure no message
3816 was added to the folder */
3817 if (( selected_folder && !session->folder_content_changed) ||
3818 (!selected_folder && uid_next == item->uid_next)) {
3819 nummsgs = g_slist_length(item->uid_list);
3821 /* If number of messages is still the same we
3822 know our caches message numbers are still valid,
3823 otherwise if the number of messages has decrease
3824 we discard our cache to start a new scan to find
3825 out which numbers have been removed */
3826 if (exists == nummsgs) {
3827 *msgnum_list = g_slist_copy(item->uid_list);
3829 } else if (exists < nummsgs) {
3830 debug_print("Freeing imap uid cache");
3832 g_slist_free(item->uid_list);
3833 item->uid_list = NULL;
3838 *msgnum_list = NULL;
3842 nummsgs = get_list_of_uids(folder, item, &uidlist);
3844 if (nummsgs != exists) {
3845 /* Cache contains more messages then folder, we have cached
3846 an old UID of a message that was removed and new messages
3847 have been added too, otherwise the uid_next check would
3849 debug_print("Freeing imap uid cache");
3851 g_slist_free(item->uid_list);
3852 item->uid_list = NULL;
3854 g_slist_free(*msgnum_list);
3856 nummsgs = get_list_of_uids(folder, item, &uidlist);
3859 *msgnum_list = uidlist;
3861 dir = folder_item_get_path((FolderItem *)item);
3862 debug_print("removing old messages from %s\n", dir);
3863 remove_numbered_files_not_in_list(dir, *msgnum_list);
3869 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item)
3874 flags.perm_flags = MSG_NEW|MSG_UNREAD;
3875 flags.tmp_flags = 0;
3877 g_return_val_if_fail(item != NULL, NULL);
3878 g_return_val_if_fail(file != NULL, NULL);
3880 if (item->stype == F_QUEUE) {
3881 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
3882 } else if (item->stype == F_DRAFT) {
3883 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
3886 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3887 if (!msginfo) return NULL;
3889 msginfo->folder = item;
3894 GSList *imap_get_msginfos(Folder *folder, FolderItem *item, GSList *msgnum_list)
3896 IMAPSession *session;
3897 MsgInfoList *ret = NULL;
3900 g_return_val_if_fail(folder != NULL, NULL);
3901 g_return_val_if_fail(item != NULL, NULL);
3902 g_return_val_if_fail(msgnum_list != NULL, NULL);
3904 session = imap_session_get(folder);
3905 g_return_val_if_fail(session != NULL, NULL);
3907 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3908 NULL, NULL, NULL, NULL);
3909 if (ok != IMAP_SUCCESS)
3912 if (!(item->stype == F_QUEUE || item->stype == F_DRAFT)) {
3913 ret = g_slist_concat(ret,
3914 imap_get_uncached_messages(
3915 session, item, msgnum_list));
3917 MsgNumberList *sorted_list, *elem;
3918 gint startnum, lastnum;
3920 sorted_list = g_slist_sort(g_slist_copy(msgnum_list), g_int_compare);
3922 startnum = lastnum = GPOINTER_TO_INT(sorted_list->data);
3924 for (elem = sorted_list;; elem = g_slist_next(elem)) {
3928 num = GPOINTER_TO_INT(elem->data);
3930 if (num > lastnum + 1 || elem == NULL) {
3932 for (i = startnum; i <= lastnum; ++i) {
3935 file = imap_fetch_msg(folder, item, i);
3937 MsgInfo *msginfo = imap_parse_msg(file, item);
3938 if (msginfo != NULL) {
3939 msginfo->msgnum = i;
3940 ret = g_slist_append(ret, msginfo);
3954 g_slist_free(sorted_list);
3960 MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid)
3962 MsgInfo *msginfo = NULL;
3963 MsgInfoList *msginfolist;
3964 MsgNumberList numlist;
3966 numlist.next = NULL;
3967 numlist.data = GINT_TO_POINTER(uid);
3969 msginfolist = imap_get_msginfos(folder, item, &numlist);
3970 if (msginfolist != NULL) {
3971 msginfo = msginfolist->data;
3972 g_slist_free(msginfolist);
3978 gboolean imap_scan_required(Folder *folder, FolderItem *_item)
3980 IMAPSession *session;
3981 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3982 gint ok, exists = 0, recent = 0, unseen = 0;
3983 guint32 uid_next, uid_val = 0;
3984 gboolean selected_folder;
3986 g_return_val_if_fail(folder != NULL, FALSE);
3987 g_return_val_if_fail(item != NULL, FALSE);
3988 g_return_val_if_fail(item->item.folder != NULL, FALSE);
3989 g_return_val_if_fail(FOLDER_CLASS(item->item.folder) == &imap_class, FALSE);
3991 if (item->item.path == NULL)
3994 session = imap_session_get(folder);
3995 g_return_val_if_fail(session != NULL, FALSE);
3997 selected_folder = (session->mbox != NULL) &&
3998 (!strcmp(session->mbox, item->item.path));
3999 if (selected_folder) {
4000 ok = imap_cmd_noop(session);
4001 if (ok != IMAP_SUCCESS)
4004 if (session->folder_content_changed)
4007 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
4008 &exists, &recent, &uid_next, &uid_val, &unseen);
4009 if (ok != IMAP_SUCCESS)
4012 if ((uid_next != item->uid_next) || (exists < item->item.total_msgs))
4019 void imap_change_flags(Folder *folder, FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
4021 IMAPSession *session;
4022 IMAPFlags flags_set = 0, flags_unset = 0;
4023 gint ok = IMAP_SUCCESS;
4024 MsgNumberList numlist;
4026 g_return_if_fail(folder != NULL);
4027 g_return_if_fail(folder->klass == &imap_class);
4028 g_return_if_fail(item != NULL);
4029 g_return_if_fail(item->folder == folder);
4030 g_return_if_fail(msginfo != NULL);
4031 g_return_if_fail(msginfo->folder == item);
4033 session = imap_session_get(folder);
4034 if (!session) return;
4036 if ((ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
4037 NULL, NULL, NULL, NULL)) != IMAP_SUCCESS)
4040 if (!MSG_IS_MARKED(msginfo->flags) && (newflags & MSG_MARKED))
4041 flags_set |= IMAP_FLAG_FLAGGED;
4042 if ( MSG_IS_MARKED(msginfo->flags) && !(newflags & MSG_MARKED))
4043 flags_unset |= IMAP_FLAG_FLAGGED;
4045 if (!MSG_IS_UNREAD(msginfo->flags) && (newflags & MSG_UNREAD))
4046 flags_unset |= IMAP_FLAG_SEEN;
4047 if ( MSG_IS_UNREAD(msginfo->flags) && !(newflags & MSG_UNREAD))
4048 flags_set |= IMAP_FLAG_SEEN;
4050 if (!MSG_IS_REPLIED(msginfo->flags) && (newflags & MSG_REPLIED))
4051 flags_set |= IMAP_FLAG_ANSWERED;
4052 if ( MSG_IS_REPLIED(msginfo->flags) && !(newflags & MSG_REPLIED))
4053 flags_set |= IMAP_FLAG_ANSWERED;
4055 numlist.next = NULL;
4056 numlist.data = GINT_TO_POINTER(msginfo->msgnum);
4059 ok = imap_set_message_flags(session, &numlist, flags_set, TRUE);
4060 if (ok != IMAP_SUCCESS) return;
4064 ok = imap_set_message_flags(session, &numlist, flags_unset, FALSE);
4065 if (ok != IMAP_SUCCESS) return;
4068 msginfo->flags.perm_flags = newflags;
4073 static gint compare_msginfo(gconstpointer a, gconstpointer b)
4075 return ((MsgInfo *)a)->msgnum - ((MsgInfo *)b)->msgnum;
4078 static guint gslist_find_next_num(MsgNumberList **list, guint num)
4082 g_return_val_if_fail(list != NULL, -1);
4084 for (elem = *list; elem != NULL; elem = g_slist_next(elem))
4085 if (GPOINTER_TO_INT(elem->data) >= num)
4088 return elem != NULL ? GPOINTER_TO_INT(elem->data) : (gint)-1;
4092 * NEW and DELETED flags are not syncronized
4093 * - The NEW/RECENT flags in IMAP folders can not really be directly
4094 * modified by Sylpheed
4095 * - The DELETE/DELETED flag in IMAP and Sylpheed don't have the same
4096 * meaning, in IMAP it always removes the messages from the FolderItem
4097 * in Sylpheed it can mean to move the message to trash
4099 static gint imap_get_flags(Folder *folder, FolderItem *item,
4100 MsgInfoList *msginfo_list, GRelation *msgflags)
4102 IMAPSession *session;
4103 GSList *sorted_list;
4105 GSList *new = NULL, *p_new;
4107 GSList *unseen = NULL, *answered = NULL, *flagged = NULL, *deleted = NULL;
4108 GSList *p_unseen, *p_answered, *p_flagged, *p_deleted;
4110 GSList *seq_list, *cur;
4111 gboolean reverse_seen = FALSE;
4114 gint exists_cnt, recent_cnt, unseen_cnt, uid_next;
4115 guint32 uidvalidity;
4117 g_return_val_if_fail(folder != NULL, -1);
4118 g_return_val_if_fail(item != NULL, -1);
4119 g_return_val_if_fail(msginfo_list != NULL, -1);
4121 session = imap_session_get(folder);
4122 g_return_val_if_fail(session != NULL, -1);
4124 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
4125 NULL, NULL, NULL, NULL);
4126 if (ok != IMAP_SUCCESS)
4129 ok = imap_status(session, IMAP_FOLDER(folder), item->path,
4130 &exists_cnt, &recent_cnt, &uid_next, &uidvalidity, &unseen_cnt);
4132 if (unseen_cnt > exists_cnt / 2)
4133 reverse_seen = TRUE;
4135 cmd_buf = g_string_new(NULL);
4137 sorted_list = g_slist_sort(g_slist_copy(msginfo_list), compare_msginfo);
4139 seq_list = imap_get_seq_set_from_msglist(msginfo_list);
4141 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
4142 IMAPSet imapset = cur->data;
4144 g_string_sprintf(cmd_buf, "RECENT UID %s", imapset);
4145 imap_cmd_search(session, cmd_buf->str, &p_new);
4146 new = g_slist_concat(new, p_new);
4148 g_string_sprintf(cmd_buf, "%sSEEN UID %s", reverse_seen ? "" : "UN", imapset);
4149 imap_cmd_search(session, cmd_buf->str, &p_unseen);
4150 unseen = g_slist_concat(unseen, p_unseen);
4152 g_string_sprintf(cmd_buf, "ANSWERED UID %s", imapset);
4153 imap_cmd_search(session, cmd_buf->str, &p_answered);
4154 answered = g_slist_concat(answered, p_answered);
4156 g_string_sprintf(cmd_buf, "FLAGGED UID %s", imapset);
4157 imap_cmd_search(session, cmd_buf->str, &p_flagged);
4158 flagged = g_slist_concat(flagged, p_flagged);
4160 g_string_sprintf(cmd_buf, "DELETED UID %s", imapset);
4161 imap_cmd_search(session, cmd_buf->str, &p_deleted);
4162 deleted = g_slist_concat(deleted, p_deleted);
4170 p_answered = answered;
4171 p_flagged = flagged;
4172 p_deleted = deleted;
4174 for (elem = sorted_list; elem != NULL; elem = g_slist_next(elem)) {
4178 msginfo = (MsgInfo *) elem->data;
4179 flags = msginfo->flags.perm_flags;
4180 flags &= ~((reverse_seen ? 0 : MSG_UNREAD) | MSG_REPLIED | MSG_MARKED);
4182 flags |= MSG_UNREAD;
4184 if (gslist_find_next_num(&p_new, msginfo->msgnum) == msginfo->msgnum)
4187 if (gslist_find_next_num(&p_unseen, msginfo->msgnum) == msginfo->msgnum) {
4188 if (!reverse_seen) {
4189 flags |= MSG_UNREAD;
4191 flags &= ~(MSG_UNREAD | MSG_NEW);
4194 if (gslist_find_next_num(&p_answered, msginfo->msgnum) == msginfo->msgnum)
4195 flags |= MSG_REPLIED;
4196 if (gslist_find_next_num(&p_flagged, msginfo->msgnum) == msginfo->msgnum)
4197 flags |= MSG_MARKED;
4199 if (gslist_find_next_num(&p_deleted, msginfo->msgnum) == msginfo->msgnum)
4200 MSG_SET_PERM_FLAGS(msginfo->flags, MSG_DELETED);
4202 g_relation_insert(msgflags, msginfo, GINT_TO_POINTER(flags));
4205 imap_seq_set_free(seq_list);
4206 g_slist_free(deleted);
4207 g_slist_free(flagged);
4208 g_slist_free(answered);
4209 g_slist_free(unseen);
4210 /* new not freed in original patch ??? */
4211 g_slist_free(sorted_list);
4212 g_string_free(cmd_buf, TRUE);