2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2005 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.
27 #include <glib/gi18n.h>
50 #include "procheader.h"
51 #include "prefs_account.h"
56 #include "prefs_common.h"
57 #include "inputdialog.h"
59 #include "remotefolder.h"
60 #include "alertpanel.h"
62 #include "statusbar.h"
68 typedef struct _IMAPFolder IMAPFolder;
69 typedef struct _IMAPSession IMAPSession;
70 typedef struct _IMAPNameSpace IMAPNameSpace;
71 typedef struct _IMAPFolderItem IMAPFolderItem;
73 #include "prefs_account.h"
75 #define IMAP_FOLDER(obj) ((IMAPFolder *)obj)
76 #define IMAP_FOLDER_ITEM(obj) ((IMAPFolderItem *)obj)
77 #define IMAP_SESSION(obj) ((IMAPSession *)obj)
83 /* list of IMAPNameSpace */
93 gboolean authenticated;
102 gboolean folder_content_changed;
106 struct _IMAPNameSpace
112 #define IMAP_SUCCESS 0
113 #define IMAP_SOCKET 2
114 #define IMAP_AUTHFAIL 3
115 #define IMAP_PROTOCOL 4
116 #define IMAP_SYNTAX 5
120 #define IMAPBUFSIZE 8192
124 IMAP_FLAG_SEEN = 1 << 0,
125 IMAP_FLAG_ANSWERED = 1 << 1,
126 IMAP_FLAG_FLAGGED = 1 << 2,
127 IMAP_FLAG_DELETED = 1 << 3,
128 IMAP_FLAG_DRAFT = 1 << 4
131 #define IMAP_IS_SEEN(flags) ((flags & IMAP_FLAG_SEEN) != 0)
132 #define IMAP_IS_ANSWERED(flags) ((flags & IMAP_FLAG_ANSWERED) != 0)
133 #define IMAP_IS_FLAGGED(flags) ((flags & IMAP_FLAG_FLAGGED) != 0)
134 #define IMAP_IS_DELETED(flags) ((flags & IMAP_FLAG_DELETED) != 0)
135 #define IMAP_IS_DRAFT(flags) ((flags & IMAP_FLAG_DRAFT) != 0)
138 #define IMAP4_PORT 143
140 #define IMAPS_PORT 993
143 #define IMAP_CMD_LIMIT 1000
145 #define QUOTE_IF_REQUIRED(out, str) \
147 if (*str != '"' && strpbrk(str, " \t(){}[]%*") != NULL) { \
151 len = strlen(str) + 3; \
152 Xalloca(__tmp, len, return IMAP_ERROR); \
153 g_snprintf(__tmp, len, "\"%s\"", str); \
156 Xstrdup_a(out, str, return IMAP_ERROR); \
160 typedef gchar * IMAPSet;
162 struct _IMAPFolderItem
171 static void imap_folder_init (Folder *folder,
175 static Folder *imap_folder_new (const gchar *name,
177 static void imap_folder_destroy (Folder *folder);
179 static IMAPSession *imap_session_new (const PrefsAccount *account);
180 static void imap_session_authenticate(IMAPSession *session,
181 const PrefsAccount *account);
182 static void imap_session_destroy (Session *session);
184 static gchar *imap_fetch_msg (Folder *folder,
187 static gint imap_add_msg (Folder *folder,
191 static gint imap_add_msgs (Folder *folder,
194 GRelation *relation);
196 static gint imap_copy_msg (Folder *folder,
199 static gint imap_copy_msgs (Folder *folder,
201 MsgInfoList *msglist,
202 GRelation *relation);
204 static gint imap_remove_msg (Folder *folder,
207 static gint imap_remove_msgs (Folder *folder,
209 MsgInfoList *msglist,
210 GRelation *relation);
211 static gint imap_remove_all_msg (Folder *folder,
214 static gboolean imap_is_msg_changed (Folder *folder,
218 static gint imap_close (Folder *folder,
221 static gint imap_scan_tree (Folder *folder);
223 static gint imap_create_tree (Folder *folder);
225 static FolderItem *imap_create_folder (Folder *folder,
228 static gint imap_rename_folder (Folder *folder,
231 static gint imap_remove_folder (Folder *folder,
234 static FolderItem *imap_folder_item_new (Folder *folder);
235 static void imap_folder_item_destroy (Folder *folder,
238 static IMAPSession *imap_session_get (Folder *folder);
240 static gint imap_greeting (IMAPSession *session);
241 static gint imap_auth (IMAPSession *session,
246 static gint imap_scan_tree_recursive (IMAPSession *session,
248 static GSList *imap_parse_list (IMAPFolder *folder,
249 IMAPSession *session,
250 const gchar *real_path,
253 static void imap_create_missing_folders (Folder *folder);
254 static FolderItem *imap_create_special_folder
256 SpecialFolderItemType stype,
259 static gint imap_do_copy_msgs (Folder *folder,
261 MsgInfoList *msglist,
262 GRelation *relation);
264 static void imap_delete_all_cached_messages (FolderItem *item);
267 static SockInfo *imap_open (const gchar *server,
271 static SockInfo *imap_open (const gchar *server,
276 static SockInfo *imap_open_tunnel(const gchar *server,
277 const gchar *tunnelcmd,
280 static SockInfo *imap_open_tunnel(const gchar *server,
281 const gchar *tunnelcmd);
285 static SockInfo *imap_init_sock(SockInfo *sock, SSLType ssl_type);
287 static SockInfo *imap_init_sock(SockInfo *sock);
290 static gchar *imap_get_flag_str (IMAPFlags flags);
291 static gint imap_set_message_flags (IMAPSession *session,
292 MsgNumberList *numlist,
295 static gint imap_select (IMAPSession *session,
301 guint32 *uid_validity,
303 static gint imap_status (IMAPSession *session,
309 guint32 *uid_validity,
313 static void imap_parse_namespace (IMAPSession *session,
315 static void imap_get_namespace_by_list (IMAPSession *session,
317 static IMAPNameSpace *imap_find_namespace (IMAPFolder *folder,
319 static gchar imap_get_path_separator (IMAPFolder *folder,
321 static gchar *imap_get_real_path (IMAPFolder *folder,
324 static gchar *imap_parse_atom (SockInfo *sock,
329 static MsgFlags imap_parse_flags (const gchar *flag_str);
330 static MsgInfo *imap_parse_envelope (SockInfo *sock,
334 static gboolean imap_has_capability (IMAPSession *session,
336 static void imap_free_capabilities (IMAPSession *session);
338 /* low-level IMAP4rev1 commands */
339 static gint imap_cmd_authenticate
340 (IMAPSession *session,
344 static gint imap_cmd_login (IMAPSession *session,
347 static gint imap_cmd_logout (IMAPSession *session);
348 static gint imap_cmd_noop (IMAPSession *session);
350 static gint imap_cmd_starttls (IMAPSession *session);
352 static gint imap_cmd_namespace (IMAPSession *session,
354 static gint imap_cmd_list (IMAPSession *session,
356 const gchar *mailbox,
358 static gint imap_cmd_do_select (IMAPSession *session,
364 guint32 *uid_validity,
366 static gint imap_cmd_select (IMAPSession *session,
371 guint32 *uid_validity,
373 static gint imap_cmd_examine (IMAPSession *session,
378 guint32 *uid_validity,
380 static gint imap_cmd_create (IMAPSession *sock,
381 const gchar *folder);
382 static gint imap_cmd_rename (IMAPSession *sock,
383 const gchar *oldfolder,
384 const gchar *newfolder);
385 static gint imap_cmd_delete (IMAPSession *session,
386 const gchar *folder);
387 static gint imap_cmd_envelope (IMAPSession *session,
389 static gint imap_cmd_fetch (IMAPSession *sock,
391 const gchar *filename);
392 static gint imap_cmd_append (IMAPSession *session,
393 const gchar *destfolder,
397 static gint imap_cmd_copy (IMAPSession *session,
398 const gchar *seq_set,
399 const gchar *destfolder,
400 GRelation *uid_mapping);
401 static gint imap_cmd_store (IMAPSession *session,
404 static gint imap_cmd_expunge (IMAPSession *session,
406 static gint imap_cmd_close (IMAPSession *session);
408 static gint imap_cmd_ok (IMAPSession *session,
410 static gint imap_cmd_ok_block (IMAPSession *session,
412 static gint imap_cmd_ok_with_block
413 (IMAPSession *session,
416 static void imap_gen_send (IMAPSession *session,
417 const gchar *format, ...);
418 static gint imap_gen_recv (IMAPSession *session,
420 static gint imap_gen_recv_block (IMAPSession *session,
422 static gint imap_gen_recv_with_block
423 (IMAPSession *session,
427 /* misc utility functions */
428 static gchar *strchr_cpy (const gchar *src,
432 static gchar *get_quoted (const gchar *src,
436 static gchar *search_array_contain_str (GPtrArray *array,
438 static gchar *search_array_str (GPtrArray *array,
440 static void imap_path_separator_subst (gchar *str,
443 static gchar *imap_utf8_to_modified_utf7 (const gchar *from);
444 static gchar *imap_modified_utf7_to_utf8 (const gchar *mutf7_str);
446 static GSList *imap_get_seq_set_from_numlist (MsgNumberList *msglist);
447 static GSList *imap_get_seq_set_from_msglist (MsgInfoList *msglist);
448 static void imap_seq_set_free (GSList *seq_list);
450 static gboolean imap_rename_folder_func (GNode *node,
452 static gint imap_get_num_list (Folder *folder,
455 gboolean *old_uids_valid);
456 static GSList *imap_get_msginfos (Folder *folder,
458 GSList *msgnum_list);
459 static MsgInfo *imap_get_msginfo (Folder *folder,
462 static gboolean imap_scan_required (Folder *folder,
464 static void imap_change_flags (Folder *folder,
467 MsgPermFlags newflags);
468 static gint imap_get_flags (Folder *folder,
470 MsgInfoList *msglist,
471 GRelation *msgflags);
472 static gchar *imap_folder_get_path (Folder *folder);
473 static gchar *imap_item_get_path (Folder *folder,
476 static FolderClass imap_class;
478 typedef struct _thread_data {
489 void *imap_getline_thread(void *data)
491 thread_data *td = (thread_data *)data;
494 line = sock_getline(td->sock);
502 /* imap_getline just wraps sock_getline inside a thread,
503 * performing gtk updates so that the interface isn't frozen.
505 static gchar *imap_getline(SockInfo *sock)
507 #if (defined USE_PTHREAD && defined __GLIBC__ && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 3)))
508 thread_data *td = g_new0(thread_data, 1);
514 if (pthread_create(&pt, PTHREAD_CREATE_JOINABLE,
515 imap_getline_thread, td) != 0) {
517 return sock_getline(sock);
520 debug_print("+++waiting for imap_getline_thread...\n");
522 /* don't let the interface freeze while waiting */
525 debug_print("---imap_getline_thread done\n");
527 /* get the thread's return value and clean its resources */
528 pthread_join(pt, (void *)&line);
533 return sock_getline(sock);
537 FolderClass *imap_get_class(void)
539 if (imap_class.idstr == NULL) {
540 imap_class.type = F_IMAP;
541 imap_class.idstr = "imap";
542 imap_class.uistr = "IMAP4";
544 /* Folder functions */
545 imap_class.new_folder = imap_folder_new;
546 imap_class.destroy_folder = imap_folder_destroy;
547 imap_class.scan_tree = imap_scan_tree;
548 imap_class.create_tree = imap_create_tree;
550 /* FolderItem functions */
551 imap_class.item_new = imap_folder_item_new;
552 imap_class.item_destroy = imap_folder_item_destroy;
553 imap_class.item_get_path = imap_item_get_path;
554 imap_class.create_folder = imap_create_folder;
555 imap_class.rename_folder = imap_rename_folder;
556 imap_class.remove_folder = imap_remove_folder;
557 imap_class.close = imap_close;
558 imap_class.get_num_list = imap_get_num_list;
559 imap_class.scan_required = imap_scan_required;
561 /* Message functions */
562 imap_class.get_msginfo = imap_get_msginfo;
563 imap_class.get_msginfos = imap_get_msginfos;
564 imap_class.fetch_msg = imap_fetch_msg;
565 imap_class.add_msg = imap_add_msg;
566 imap_class.add_msgs = imap_add_msgs;
567 imap_class.copy_msg = imap_copy_msg;
568 imap_class.copy_msgs = imap_copy_msgs;
569 imap_class.remove_msg = imap_remove_msg;
570 imap_class.remove_msgs = imap_remove_msgs;
571 imap_class.remove_all_msg = imap_remove_all_msg;
572 imap_class.is_msg_changed = imap_is_msg_changed;
573 imap_class.change_flags = imap_change_flags;
574 imap_class.get_flags = imap_get_flags;
581 static gchar *get_seq_set_from_seq_list(GSList *seq_list)
587 for (cur = seq_list; cur != NULL; cur = cur->next) {
588 tmp = val?g_strdup(val):NULL;
590 val = g_strconcat(tmp?tmp:"", tmp?",":"",(gchar *)cur->data,
598 static Folder *imap_folder_new(const gchar *name, const gchar *path)
602 folder = (Folder *)g_new0(IMAPFolder, 1);
603 folder->klass = &imap_class;
604 imap_folder_init(folder, name, path);
609 static void imap_folder_destroy(Folder *folder)
613 dir = imap_folder_get_path(folder);
614 if (is_dir_exist(dir))
615 remove_dir_recursive(dir);
618 folder_remote_folder_destroy(REMOTE_FOLDER(folder));
621 static void imap_folder_init(Folder *folder, const gchar *name,
624 folder_remote_folder_init((Folder *)folder, name, path);
627 static FolderItem *imap_folder_item_new(Folder *folder)
629 IMAPFolderItem *item;
631 item = g_new0(IMAPFolderItem, 1);
634 item->uid_list = NULL;
636 return (FolderItem *)item;
639 static void imap_folder_item_destroy(Folder *folder, FolderItem *_item)
641 IMAPFolderItem *item = (IMAPFolderItem *)_item;
643 g_return_if_fail(item != NULL);
644 g_slist_free(item->uid_list);
649 static gboolean imap_reset_uid_lists_func(GNode *node, gpointer data)
651 IMAPFolderItem *item = (IMAPFolderItem *)node->data;
655 g_slist_free(item->uid_list);
656 item->uid_list = NULL;
661 static void imap_reset_uid_lists(Folder *folder)
663 if(folder->node == NULL)
666 /* Destroy all uid lists and rest last uid */
667 g_node_traverse(folder->node, G_IN_ORDER, G_TRAVERSE_ALL, -1, imap_reset_uid_lists_func, NULL);
670 /* Send CAPABILITY, and examine the server's response to see whether this
671 * connection is pre-authenticated or not and build a list of CAPABILITIES. */
672 static gint imap_greeting(IMAPSession *session)
677 imap_gen_send(session, "CAPABILITY");
679 argbuf = g_ptr_array_new();
681 if (imap_cmd_ok(session, argbuf) != IMAP_SUCCESS ||
682 ((capstr = search_array_str(argbuf, "CAPABILITY ")) == NULL)) {
683 ptr_array_free_strings(argbuf);
684 g_ptr_array_free(argbuf, TRUE);
688 session->authenticated = search_array_str(argbuf, "PREAUTH") != NULL;
690 capstr += strlen("CAPABILITY ");
692 IMAP_SESSION(session)->capability = g_strsplit(capstr, " ", 0);
694 ptr_array_free_strings(argbuf);
695 g_ptr_array_free(argbuf, TRUE);
697 if (imap_has_capability(session, "UIDPLUS"))
698 session->uidplus = TRUE;
703 static gint imap_auth(IMAPSession *session, const gchar *user, const gchar *pass,
708 if (type == 0 || type == IMAP_AUTH_LOGIN)
709 ok = imap_cmd_login(session, user, pass);
711 ok = imap_cmd_authenticate(session, user, pass, type);
713 if (ok == IMAP_SUCCESS)
714 session->authenticated = TRUE;
719 static IMAPSession *imap_session_get(Folder *folder)
721 RemoteFolder *rfolder = REMOTE_FOLDER(folder);
722 IMAPSession *session = NULL;
724 g_return_val_if_fail(folder != NULL, NULL);
725 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, NULL);
726 g_return_val_if_fail(folder->account != NULL, NULL);
728 if (prefs_common.work_offline && !imap_gtk_should_override()) {
732 /* Make sure we have a session */
733 if (rfolder->session != NULL) {
734 session = IMAP_SESSION(rfolder->session);
736 imap_reset_uid_lists(folder);
737 session = imap_session_new(folder->account);
742 if (SESSION(session)->sock->state == CONN_DISCONNECTED) {
743 debug_print("IMAP server disconnected\n");
744 session_destroy(SESSION(session));
745 imap_reset_uid_lists(folder);
746 session = imap_session_new(folder->account);
749 /* Make sure session is authenticated */
750 if (!IMAP_SESSION(session)->authenticated)
751 imap_session_authenticate(IMAP_SESSION(session), folder->account);
752 if (!IMAP_SESSION(session)->authenticated) {
753 session_destroy(SESSION(session));
754 rfolder->session = NULL;
758 /* Make sure we have parsed the IMAP namespace */
759 imap_parse_namespace(IMAP_SESSION(session),
760 IMAP_FOLDER(folder));
762 /* I think the point of this code is to avoid sending a
763 * keepalive if we've used the session recently and therefore
764 * think it's still alive. Unfortunately, most of the code
765 * does not yet check for errors on the socket, and so if the
766 * connection drops we don't notice until the timeout expires.
767 * A better solution than sending a NOOP every time would be
768 * for every command to be prepared to retry until it is
769 * successfully sent. -- mbp */
770 if (time(NULL) - SESSION(session)->last_access_time > SESSION_TIMEOUT_INTERVAL) {
771 /* verify that the session is still alive */
772 if (imap_cmd_noop(session) != IMAP_SUCCESS) {
773 /* Check if this is the first try to establish a
774 connection, if yes we don't try to reconnect */
775 if (rfolder->session == NULL) {
776 log_warning(_("Connecting to %s failed"),
777 folder->account->recv_server);
778 session_destroy(SESSION(session));
781 log_warning(_("IMAP4 connection to %s has been"
782 " disconnected. Reconnecting...\n"),
783 folder->account->recv_server);
784 statusbar_print_all(_("IMAP4 connection to %s has been"
785 " disconnected. Reconnecting...\n"),
786 folder->account->recv_server);
787 session_destroy(SESSION(session));
788 /* Clear folders session to make imap_session_get create
789 a new session, because of rfolder->session == NULL
790 it will not try to reconnect again and so avoid an
792 rfolder->session = NULL;
793 session = imap_session_get(folder);
799 rfolder->session = SESSION(session);
801 return IMAP_SESSION(session);
804 static IMAPSession *imap_session_new(const PrefsAccount *account)
806 IMAPSession *session;
811 /* FIXME: IMAP over SSL only... */
814 port = account->set_imapport ? account->imapport
815 : account->ssl_imap == SSL_TUNNEL ? IMAPS_PORT : IMAP4_PORT;
816 ssl_type = account->ssl_imap;
818 port = account->set_imapport ? account->imapport
822 if (account->set_tunnelcmd) {
823 log_message(_("creating tunneled IMAP4 connection\n"));
825 if ((imap_sock = imap_open_tunnel(account->recv_server,
829 if ((imap_sock = imap_open_tunnel(account->recv_server,
830 account->tunnelcmd)) == NULL)
834 g_return_val_if_fail(account->recv_server != NULL, NULL);
836 log_message(_("creating IMAP4 connection to %s:%d ...\n"),
837 account->recv_server, port);
840 if ((imap_sock = imap_open(account->recv_server, port,
843 if ((imap_sock = imap_open(account->recv_server, port)) == NULL)
848 session = g_new0(IMAPSession, 1);
849 session_init(SESSION(session));
850 SESSION(session)->type = SESSION_IMAP;
851 SESSION(session)->server = g_strdup(account->recv_server);
852 SESSION(session)->sock = imap_sock;
854 SESSION(session)->destroy = imap_session_destroy;
856 session->capability = NULL;
858 session->authenticated = FALSE;
859 session->mbox = NULL;
860 session->cmd_count = 0;
862 /* Only need to log in if the connection was not PREAUTH */
863 if (imap_greeting(session) != IMAP_SUCCESS) {
864 session_destroy(SESSION(session));
869 if (account->ssl_imap == SSL_STARTTLS &&
870 imap_has_capability(session, "STARTTLS")) {
873 ok = imap_cmd_starttls(session);
874 if (ok != IMAP_SUCCESS) {
875 log_warning(_("Can't start TLS session.\n"));
876 session_destroy(SESSION(session));
879 if (!ssl_init_socket_with_method(SESSION(session)->sock,
881 session_destroy(SESSION(session));
885 imap_free_capabilities(session);
886 session->authenticated = FALSE;
887 session->uidplus = FALSE;
888 session->cmd_count = 1;
890 if (imap_greeting(session) != IMAP_SUCCESS) {
891 session_destroy(SESSION(session));
896 log_message("IMAP connection is %s-authenticated\n",
897 (session->authenticated) ? "pre" : "un");
902 static void imap_session_authenticate(IMAPSession *session,
903 const PrefsAccount *account)
907 g_return_if_fail(account->userid != NULL);
909 pass = account->passwd;
912 tmp_pass = input_dialog_query_password(account->recv_server, account->userid);
915 Xstrdup_a(pass, tmp_pass, {g_free(tmp_pass); return;});
918 statusbar_print_all(_("Connecting to IMAP4 server %s...\n"),
919 account->recv_server);
920 if (imap_auth(session, account->userid, pass, account->imap_auth_type) != IMAP_SUCCESS) {
921 imap_cmd_logout(session);
923 alertpanel_error(_("Can't authenticate on IMAP4 server: %s"),
924 account->recv_server);
929 session->authenticated = TRUE;
932 static void imap_session_destroy(Session *session)
934 imap_free_capabilities(IMAP_SESSION(session));
935 g_free(IMAP_SESSION(session)->mbox);
936 sock_close(session->sock);
937 session->sock = NULL;
940 static gchar *imap_fetch_msg(Folder *folder, FolderItem *item, gint uid)
942 gchar *path, *filename;
943 IMAPSession *session;
946 g_return_val_if_fail(folder != NULL, NULL);
947 g_return_val_if_fail(item != NULL, NULL);
949 path = folder_item_get_path(item);
950 if (!is_dir_exist(path))
952 filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(uid), NULL);
955 if (is_file_exist(filename)) {
956 debug_print("message %d has been already cached.\n", uid);
960 session = imap_session_get(folder);
966 debug_print("-> fetching messages\n");
967 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
968 NULL, NULL, NULL, NULL, FALSE);
969 if (ok != IMAP_SUCCESS) {
970 g_warning("can't select mailbox %s\n", item->path);
975 debug_print("getting message %d...\n", uid);
976 ok = imap_cmd_fetch(session, (guint32)uid, filename);
978 if (ok != IMAP_SUCCESS) {
979 g_warning("can't fetch message %d\n", uid);
987 static gint imap_add_msg(Folder *folder, FolderItem *dest,
988 const gchar *file, MsgFlags *flags)
992 MsgFileInfo fileinfo;
994 g_return_val_if_fail(file != NULL, -1);
996 fileinfo.msginfo = NULL;
997 fileinfo.file = (gchar *)file;
998 fileinfo.flags = flags;
999 file_list.data = &fileinfo;
1000 file_list.next = NULL;
1002 ret = imap_add_msgs(folder, dest, &file_list, NULL);
1006 static gint imap_add_msgs(Folder *folder, FolderItem *dest, GSList *file_list,
1007 GRelation *relation)
1010 IMAPSession *session;
1011 guint32 last_uid = 0;
1013 MsgFileInfo *fileinfo;
1016 g_return_val_if_fail(folder != NULL, -1);
1017 g_return_val_if_fail(dest != NULL, -1);
1018 g_return_val_if_fail(file_list != NULL, -1);
1020 session = imap_session_get(folder);
1021 if (!session) return -1;
1023 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
1025 for (cur = file_list; cur != NULL; cur = cur->next) {
1026 IMAPFlags iflags = 0;
1027 guint32 new_uid = 0;
1029 fileinfo = (MsgFileInfo *)cur->data;
1031 if (fileinfo->flags) {
1032 if (MSG_IS_MARKED(*fileinfo->flags))
1033 iflags |= IMAP_FLAG_FLAGGED;
1034 if (MSG_IS_REPLIED(*fileinfo->flags))
1035 iflags |= IMAP_FLAG_ANSWERED;
1036 if (!MSG_IS_UNREAD(*fileinfo->flags))
1037 iflags |= IMAP_FLAG_SEEN;
1040 if (dest->stype == F_OUTBOX ||
1041 dest->stype == F_QUEUE ||
1042 dest->stype == F_DRAFT ||
1043 dest->stype == F_TRASH)
1044 iflags |= IMAP_FLAG_SEEN;
1046 ok = imap_cmd_append(session, destdir, fileinfo->file, iflags,
1049 if (ok != IMAP_SUCCESS) {
1050 g_warning("can't append message %s\n", fileinfo->file);
1055 if (relation != NULL)
1056 g_relation_insert(relation, fileinfo->msginfo != NULL ?
1057 (gpointer) fileinfo->msginfo : (gpointer) fileinfo,
1058 GINT_TO_POINTER(dest->last_num + 1));
1059 if (last_uid < new_uid)
1068 static gint imap_do_copy_msgs(Folder *folder, FolderItem *dest,
1069 MsgInfoList *msglist, GRelation *relation)
1073 GSList *seq_list, *cur;
1075 IMAPSession *session;
1076 gint ok = IMAP_SUCCESS;
1077 GRelation *uid_mapping;
1080 g_return_val_if_fail(folder != NULL, -1);
1081 g_return_val_if_fail(dest != NULL, -1);
1082 g_return_val_if_fail(msglist != NULL, -1);
1084 session = imap_session_get(folder);
1088 msginfo = (MsgInfo *)msglist->data;
1090 src = msginfo->folder;
1092 g_warning("the src folder is identical to the dest.\n");
1096 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
1097 NULL, NULL, NULL, NULL, FALSE);
1098 if (ok != IMAP_SUCCESS) {
1102 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
1103 seq_list = imap_get_seq_set_from_msglist(msglist);
1104 uid_mapping = g_relation_new(2);
1105 g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
1107 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
1108 gchar *seq_set = (gchar *)cur->data;
1110 debug_print("Copying message %s%c[%s] to %s ...\n",
1111 src->path, G_DIR_SEPARATOR,
1114 ok = imap_cmd_copy(session, seq_set, destdir, uid_mapping);
1115 if (ok != IMAP_SUCCESS) {
1116 g_relation_destroy(uid_mapping);
1117 imap_seq_set_free(seq_list);
1122 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
1123 MsgInfo *msginfo = (MsgInfo *)cur->data;
1126 tuples = g_relation_select(uid_mapping,
1127 GINT_TO_POINTER(msginfo->msgnum),
1129 if (tuples->len > 0) {
1130 gint num = GPOINTER_TO_INT(g_tuples_index(tuples, 0, 1));
1131 g_relation_insert(relation, msginfo,
1132 GPOINTER_TO_INT(num));
1136 g_relation_insert(relation, msginfo,
1137 GPOINTER_TO_INT(0));
1138 g_tuples_destroy(tuples);
1141 g_relation_destroy(uid_mapping);
1142 imap_seq_set_free(seq_list);
1146 if (ok == IMAP_SUCCESS)
1152 static gint imap_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1156 g_return_val_if_fail(msginfo != NULL, -1);
1158 msglist.data = msginfo;
1159 msglist.next = NULL;
1161 return imap_copy_msgs(folder, dest, &msglist, NULL);
1164 static gint imap_copy_msgs(Folder *folder, FolderItem *dest,
1165 MsgInfoList *msglist, GRelation *relation)
1171 g_return_val_if_fail(folder != NULL, -1);
1172 g_return_val_if_fail(dest != NULL, -1);
1173 g_return_val_if_fail(msglist != NULL, -1);
1175 msginfo = (MsgInfo *)msglist->data;
1176 g_return_val_if_fail(msginfo->folder != NULL, -1);
1178 if (folder == msginfo->folder->folder)
1179 return imap_do_copy_msgs(folder, dest, msglist, relation);
1181 file_list = procmsg_get_message_file_list(msglist);
1182 g_return_val_if_fail(file_list != NULL, -1);
1184 ret = imap_add_msgs(folder, dest, file_list, relation);
1186 procmsg_message_file_list_free(file_list);
1191 static gint imap_do_remove_msgs(Folder *folder, FolderItem *dest,
1192 MsgInfoList *msglist, GRelation *relation)
1195 GSList *seq_list = NULL, *cur;
1197 IMAPSession *session;
1198 gint ok = IMAP_SUCCESS;
1199 GRelation *uid_mapping;
1201 g_return_val_if_fail(folder != NULL, -1);
1202 g_return_val_if_fail(dest != NULL, -1);
1203 g_return_val_if_fail(msglist != NULL, -1);
1205 session = imap_session_get(folder);
1209 msginfo = (MsgInfo *)msglist->data;
1211 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
1212 NULL, NULL, NULL, NULL, FALSE);
1213 if (ok != IMAP_SUCCESS) {
1217 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
1218 for (cur = msglist; cur; cur = cur->next) {
1219 msginfo = (MsgInfo *)cur->data;
1220 seq_list = g_slist_append(seq_list, GINT_TO_POINTER(msginfo->msgnum));
1223 uid_mapping = g_relation_new(2);
1224 g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
1226 ok = imap_set_message_flags
1227 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
1228 seq_list, IMAP_FLAG_DELETED, TRUE);
1229 if (ok != IMAP_SUCCESS) {
1230 log_warning(_("can't set deleted flags\n"));
1233 ok = imap_cmd_expunge(session, NULL);
1234 if (ok != IMAP_SUCCESS) {
1235 log_warning(_("can't expunge\n"));
1239 g_relation_destroy(uid_mapping);
1240 g_list_free(seq_list);
1244 if (ok == IMAP_SUCCESS)
1250 static gint imap_remove_msgs(Folder *folder, FolderItem *dest,
1251 MsgInfoList *msglist, GRelation *relation)
1254 printf("removing %d messages\n", g_slist_length(msglist));
1255 g_return_val_if_fail(folder != NULL, -1);
1256 g_return_val_if_fail(dest != NULL, -1);
1257 g_return_val_if_fail(msglist != NULL, -1);
1259 msginfo = (MsgInfo *)msglist->data;
1260 g_return_val_if_fail(msginfo->folder != NULL, -1);
1262 return imap_do_remove_msgs(folder, dest, msglist, relation);
1265 static gint imap_remove_all_msg(Folder *folder, FolderItem *item)
1268 IMAPSession *session;
1271 g_return_val_if_fail(folder != NULL, -1);
1272 g_return_val_if_fail(item != NULL, -1);
1274 session = imap_session_get(folder);
1278 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
1279 NULL, NULL, NULL, NULL, FALSE);
1280 if (ok != IMAP_SUCCESS) {
1283 imap_gen_send(session, "STORE 1:* +FLAGS.SILENT (\\Deleted)");
1284 ok = imap_cmd_ok(session, NULL);
1285 if (ok != IMAP_SUCCESS) {
1286 log_warning(_("can't set deleted flags: 1:*\n"));
1290 ok = imap_cmd_expunge(session, NULL);
1291 if (ok != IMAP_SUCCESS) {
1292 log_warning(_("can't expunge\n"));
1296 dir = folder_item_get_path(item);
1297 if (is_dir_exist(dir))
1298 remove_all_numbered_files(dir);
1301 return IMAP_SUCCESS;
1304 static gboolean imap_is_msg_changed(Folder *folder, FolderItem *item,
1307 /* TODO: properly implement this method */
1311 static gint imap_close(Folder *folder, FolderItem *item)
1314 IMAPSession *session;
1316 g_return_val_if_fail(folder != NULL, -1);
1317 g_return_val_if_fail(item != NULL, -1);
1318 g_return_val_if_fail(item->path != NULL, -1);
1320 session = imap_session_get(folder);
1321 if (!session) return -1;
1323 if (session->mbox) {
1324 if (strcmp2(session->mbox, item->path) != 0) return -1;
1326 ok = imap_cmd_close(session);
1327 if (ok != IMAP_SUCCESS)
1328 log_warning(_("can't close folder\n"));
1330 g_free(session->mbox);
1332 session->mbox = NULL;
1340 static gint imap_scan_tree(Folder *folder)
1342 FolderItem *item = NULL;
1343 IMAPSession *session;
1344 gchar *root_folder = NULL;
1346 g_return_val_if_fail(folder != NULL, -1);
1347 g_return_val_if_fail(folder->account != NULL, -1);
1349 session = imap_session_get(folder);
1351 if (!folder->node) {
1352 folder_tree_destroy(folder);
1353 item = folder_item_new(folder, folder->name, NULL);
1354 item->folder = folder;
1355 folder->node = item->node = g_node_new(item);
1360 if (folder->account->imap_dir && *folder->account->imap_dir) {
1365 Xstrdup_a(root_folder, folder->account->imap_dir, return -1);
1366 extract_quote(root_folder, '"');
1367 subst_char(root_folder,
1368 imap_get_path_separator(IMAP_FOLDER(folder),
1371 strtailchomp(root_folder, '/');
1372 real_path = imap_get_real_path
1373 (IMAP_FOLDER(folder), root_folder);
1374 debug_print("IMAP root directory: %s\n", real_path);
1376 /* check if root directory exist */
1377 argbuf = g_ptr_array_new();
1378 ok = imap_cmd_list(session, NULL, real_path, argbuf);
1379 if (ok != IMAP_SUCCESS ||
1380 search_array_str(argbuf, "LIST ") == NULL) {
1381 log_warning(_("root folder %s does not exist\n"), real_path);
1382 g_ptr_array_free(argbuf, TRUE);
1385 if (!folder->node) {
1386 item = folder_item_new(folder, folder->name, NULL);
1387 item->folder = folder;
1388 folder->node = item->node = g_node_new(item);
1392 g_ptr_array_free(argbuf, TRUE);
1397 item = FOLDER_ITEM(folder->node->data);
1398 if (!item || ((item->path || root_folder) &&
1399 strcmp2(item->path, root_folder) != 0)) {
1400 folder_tree_destroy(folder);
1401 item = folder_item_new(folder, folder->name, root_folder);
1402 item->folder = folder;
1403 folder->node = item->node = g_node_new(item);
1406 imap_scan_tree_recursive(session, FOLDER_ITEM(folder->node->data));
1407 imap_create_missing_folders(folder);
1412 static gint imap_scan_tree_recursive(IMAPSession *session, FolderItem *item)
1415 IMAPFolder *imapfolder;
1416 FolderItem *new_item;
1417 GSList *item_list, *cur;
1420 gchar *wildcard_path;
1424 g_return_val_if_fail(item != NULL, -1);
1425 g_return_val_if_fail(item->folder != NULL, -1);
1426 g_return_val_if_fail(item->no_sub == FALSE, -1);
1428 folder = item->folder;
1429 imapfolder = IMAP_FOLDER(folder);
1431 separator = imap_get_path_separator(imapfolder, item->path);
1433 if (folder->ui_func)
1434 folder->ui_func(folder, item, folder->ui_func_data);
1437 wildcard[0] = separator;
1440 real_path = imap_get_real_path(imapfolder, item->path);
1444 real_path = g_strdup("");
1447 Xstrcat_a(wildcard_path, real_path, wildcard,
1448 {g_free(real_path); return IMAP_ERROR;});
1449 QUOTE_IF_REQUIRED(wildcard_path, wildcard_path);
1451 imap_gen_send(session, "LIST \"\" %s",
1454 strtailchomp(real_path, separator);
1455 item_list = imap_parse_list(imapfolder, session, real_path, NULL);
1458 node = item->node->children;
1459 while (node != NULL) {
1460 FolderItem *old_item = FOLDER_ITEM(node->data);
1461 GNode *next = node->next;
1464 for (cur = item_list; cur != NULL; cur = cur->next) {
1465 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1466 if (!strcmp2(old_item->path, cur_item->path)) {
1467 new_item = cur_item;
1472 debug_print("folder '%s' not found. removing...\n",
1474 folder_item_remove(old_item);
1476 old_item->no_sub = new_item->no_sub;
1477 old_item->no_select = new_item->no_select;
1478 if (old_item->no_sub == TRUE && node->children) {
1479 debug_print("folder '%s' doesn't have "
1480 "subfolders. removing...\n",
1482 folder_item_remove_children(old_item);
1489 for (cur = item_list; cur != NULL; cur = cur->next) {
1490 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1492 for (node = item->node->children; node != NULL;
1493 node = node->next) {
1494 if (!strcmp2(FOLDER_ITEM(node->data)->path,
1496 new_item = FOLDER_ITEM(node->data);
1497 folder_item_destroy(cur_item);
1503 new_item = cur_item;
1504 debug_print("new folder '%s' found.\n", new_item->path);
1505 folder_item_append(item, new_item);
1508 if (!strcmp(new_item->path, "INBOX")) {
1509 new_item->stype = F_INBOX;
1510 folder->inbox = new_item;
1511 } else if (!folder_item_parent(item) || item->stype == F_INBOX) {
1514 base = g_path_get_basename(new_item->path);
1516 if (!folder->outbox && !g_ascii_strcasecmp(base, "Sent")) {
1517 new_item->stype = F_OUTBOX;
1518 folder->outbox = new_item;
1519 } else if (!folder->draft && !g_ascii_strcasecmp(base, "Drafts")) {
1520 new_item->stype = F_DRAFT;
1521 folder->draft = new_item;
1522 } else if (!folder->queue && !g_ascii_strcasecmp(base, "Queue")) {
1523 new_item->stype = F_QUEUE;
1524 folder->queue = new_item;
1525 } else if (!folder->trash && !g_ascii_strcasecmp(base, "Trash")) {
1526 new_item->stype = F_TRASH;
1527 folder->trash = new_item;
1532 if (new_item->no_sub == FALSE)
1533 imap_scan_tree_recursive(session, new_item);
1536 g_slist_free(item_list);
1538 return IMAP_SUCCESS;
1541 static GSList *imap_parse_list(IMAPFolder *folder, IMAPSession *session,
1542 const gchar *real_path, gchar *separator)
1544 gchar buf[IMAPBUFSIZE];
1546 gchar separator_str[16];
1549 gchar *loc_name, *loc_path;
1550 GSList *item_list = NULL;
1552 FolderItem *new_item;
1554 debug_print("getting list of %s ...\n",
1555 *real_path ? real_path : "\"\"");
1557 str = g_string_new(NULL);
1560 if (sock_gets(SESSION(session)->sock, buf, sizeof(buf)) <= 0) {
1561 log_warning(_("error occurred while getting LIST.\n"));
1565 if (buf[0] != '*' || buf[1] != ' ') {
1566 log_print("IMAP4< %s\n", buf);
1567 if (sscanf(buf, "%*d %16s", buf) < 1 ||
1568 strcmp(buf, "OK") != 0)
1569 log_warning(_("error occurred while getting LIST.\n"));
1573 debug_print("IMAP4< %s\n", buf);
1575 g_string_assign(str, buf);
1577 if (strncmp(p, "LIST ", 5) != 0) continue;
1580 if (*p != '(') continue;
1582 p = strchr_cpy(p, ')', flags, sizeof(flags));
1584 while (*p == ' ') p++;
1586 p = strchr_cpy(p, ' ', separator_str, sizeof(separator_str));
1588 extract_quote(separator_str, '"');
1589 if (!strcmp(separator_str, "NIL"))
1590 separator_str[0] = '\0';
1592 *separator = separator_str[0];
1595 while (*p == ' ') p++;
1596 if (*p == '{' || *p == '"')
1597 p = imap_parse_atom(SESSION(session)->sock, p,
1598 buf, sizeof(buf), str);
1600 strncpy2(buf, p, sizeof(buf));
1601 strtailchomp(buf, separator_str[0]);
1602 if (buf[0] == '\0') continue;
1603 if (!strcmp(buf, real_path)) continue;
1605 if (separator_str[0] != '\0')
1606 subst_char(buf, separator_str[0], '/');
1607 base = g_path_get_basename(buf);
1608 if (base[0] == '.') continue;
1610 loc_name = imap_modified_utf7_to_utf8(base);
1611 loc_path = imap_modified_utf7_to_utf8(buf);
1612 new_item = folder_item_new(FOLDER(folder), loc_name, loc_path);
1613 if (strcasestr(flags, "\\Noinferiors") != NULL)
1614 new_item->no_sub = TRUE;
1615 if (strcmp(buf, "INBOX") != 0 &&
1616 strcasestr(flags, "\\Noselect") != NULL)
1617 new_item->no_select = TRUE;
1619 item_list = g_slist_append(item_list, new_item);
1621 debug_print("folder '%s' found.\n", loc_path);
1627 g_string_free(str, TRUE);
1632 static gint imap_create_tree(Folder *folder)
1634 g_return_val_if_fail(folder != NULL, -1);
1635 g_return_val_if_fail(folder->node != NULL, -1);
1636 g_return_val_if_fail(folder->node->data != NULL, -1);
1637 g_return_val_if_fail(folder->account != NULL, -1);
1639 imap_scan_tree(folder);
1640 imap_create_missing_folders(folder);
1645 static void imap_create_missing_folders(Folder *folder)
1647 g_return_if_fail(folder != NULL);
1650 folder->inbox = imap_create_special_folder
1651 (folder, F_INBOX, "INBOX");
1653 if (!folder->outbox)
1654 folder->outbox = imap_create_special_folder
1655 (folder, F_OUTBOX, "Sent");
1657 folder->draft = imap_create_special_folder
1658 (folder, F_DRAFT, "Drafts");
1660 folder->queue = imap_create_special_folder
1661 (folder, F_QUEUE, "Queue");
1664 folder->trash = imap_create_special_folder
1665 (folder, F_TRASH, "Trash");
1668 static FolderItem *imap_create_special_folder(Folder *folder,
1669 SpecialFolderItemType stype,
1673 FolderItem *new_item;
1675 g_return_val_if_fail(folder != NULL, NULL);
1676 g_return_val_if_fail(folder->node != NULL, NULL);
1677 g_return_val_if_fail(folder->node->data != NULL, NULL);
1678 g_return_val_if_fail(folder->account != NULL, NULL);
1679 g_return_val_if_fail(name != NULL, NULL);
1681 item = FOLDER_ITEM(folder->node->data);
1682 new_item = imap_create_folder(folder, item, name);
1685 g_warning("Can't create '%s'\n", name);
1686 if (!folder->inbox) return NULL;
1688 new_item = imap_create_folder(folder, folder->inbox, name);
1690 g_warning("Can't create '%s' under INBOX\n", name);
1692 new_item->stype = stype;
1694 new_item->stype = stype;
1699 static gchar *imap_folder_get_path(Folder *folder)
1703 g_return_val_if_fail(folder != NULL, NULL);
1704 g_return_val_if_fail(folder->account != NULL, NULL);
1706 folder_path = g_strconcat(get_imap_cache_dir(),
1708 folder->account->recv_server,
1710 folder->account->userid,
1716 static gchar *imap_item_get_path(Folder *folder, FolderItem *item)
1718 gchar *folder_path, *path;
1720 g_return_val_if_fail(folder != NULL, NULL);
1721 g_return_val_if_fail(item != NULL, NULL);
1722 folder_path = imap_folder_get_path(folder);
1724 g_return_val_if_fail(folder_path != NULL, NULL);
1725 if (folder_path[0] == G_DIR_SEPARATOR) {
1727 path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
1730 path = g_strdup(folder_path);
1733 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1734 folder_path, G_DIR_SEPARATOR_S,
1737 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1740 g_free(folder_path);
1745 static FolderItem *imap_create_folder(Folder *folder, FolderItem *parent,
1748 gchar *dirpath, *imap_path;
1749 IMAPSession *session;
1750 FolderItem *new_item;
1756 g_return_val_if_fail(folder != NULL, NULL);
1757 g_return_val_if_fail(folder->account != NULL, NULL);
1758 g_return_val_if_fail(parent != NULL, NULL);
1759 g_return_val_if_fail(name != NULL, NULL);
1761 session = imap_session_get(folder);
1762 if (!session) return NULL;
1764 if (!folder_item_parent(parent) && strcmp(name, "INBOX") == 0)
1765 dirpath = g_strdup(name);
1766 else if (parent->path)
1767 dirpath = g_strconcat(parent->path, "/", name, NULL);
1768 else if ((p = strchr(name, '/')) != NULL && *(p + 1) != '\0')
1769 dirpath = g_strdup(name);
1770 else if (folder->account->imap_dir && *folder->account->imap_dir) {
1773 Xstrdup_a(imap_dir, folder->account->imap_dir, return NULL);
1774 strtailchomp(imap_dir, '/');
1775 dirpath = g_strconcat(imap_dir, "/", name, NULL);
1777 dirpath = g_strdup(name);
1779 /* keep trailing directory separator to create a folder that contains
1781 imap_path = imap_utf8_to_modified_utf7(dirpath);
1782 strtailchomp(dirpath, '/');
1783 Xstrdup_a(new_name, name, {g_free(dirpath); return NULL;});
1784 strtailchomp(new_name, '/');
1785 separator = imap_get_path_separator(IMAP_FOLDER(folder), imap_path);
1786 imap_path_separator_subst(imap_path, separator);
1787 subst_char(new_name, '/', separator);
1789 if (strcmp(name, "INBOX") != 0) {
1792 gboolean exist = FALSE;
1794 argbuf = g_ptr_array_new();
1795 ok = imap_cmd_list(session, NULL, imap_path,
1797 if (ok != IMAP_SUCCESS) {
1798 log_warning(_("can't create mailbox: LIST failed\n"));
1801 ptr_array_free_strings(argbuf);
1802 g_ptr_array_free(argbuf, TRUE);
1806 for (i = 0; i < argbuf->len; i++) {
1808 str = g_ptr_array_index(argbuf, i);
1809 if (!strncmp(str, "LIST ", 5)) {
1814 ptr_array_free_strings(argbuf);
1815 g_ptr_array_free(argbuf, TRUE);
1818 ok = imap_cmd_create(session, imap_path);
1819 if (ok != IMAP_SUCCESS) {
1820 log_warning(_("can't create mailbox\n"));
1828 new_item = folder_item_new(folder, new_name, dirpath);
1829 folder_item_append(parent, new_item);
1833 dirpath = folder_item_get_path(new_item);
1834 if (!is_dir_exist(dirpath))
1835 make_dir_hier(dirpath);
1841 static gint imap_rename_folder(Folder *folder, FolderItem *item,
1846 gchar *real_oldpath;
1847 gchar *real_newpath;
1849 gchar *old_cache_dir;
1850 gchar *new_cache_dir;
1851 IMAPSession *session;
1854 gint exists, recent, unseen;
1855 guint32 uid_validity;
1857 g_return_val_if_fail(folder != NULL, -1);
1858 g_return_val_if_fail(item != NULL, -1);
1859 g_return_val_if_fail(item->path != NULL, -1);
1860 g_return_val_if_fail(name != NULL, -1);
1862 if (strchr(name, imap_get_path_separator(IMAP_FOLDER(folder), item->path)) != NULL) {
1863 g_warning(_("New folder name must not contain the namespace "
1868 session = imap_session_get(folder);
1869 if (!session) return -1;
1871 real_oldpath = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1873 g_free(session->mbox);
1874 session->mbox = NULL;
1875 ok = imap_cmd_examine(session, "INBOX",
1876 &exists, &recent, &unseen, &uid_validity, FALSE);
1877 if (ok != IMAP_SUCCESS) {
1878 g_free(real_oldpath);
1882 separator = imap_get_path_separator(IMAP_FOLDER(folder), item->path);
1883 if (strchr(item->path, G_DIR_SEPARATOR)) {
1884 dirpath = g_path_get_dirname(item->path);
1885 newpath = g_strconcat(dirpath, G_DIR_SEPARATOR_S, name, NULL);
1888 newpath = g_strdup(name);
1890 real_newpath = imap_utf8_to_modified_utf7(newpath);
1891 imap_path_separator_subst(real_newpath, separator);
1893 ok = imap_cmd_rename(session, real_oldpath, real_newpath);
1894 if (ok != IMAP_SUCCESS) {
1895 log_warning(_("can't rename mailbox: %s to %s\n"),
1896 real_oldpath, real_newpath);
1897 g_free(real_oldpath);
1899 g_free(real_newpath);
1904 item->name = g_strdup(name);
1906 old_cache_dir = folder_item_get_path(item);
1908 paths[0] = g_strdup(item->path);
1910 g_node_traverse(item->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1911 imap_rename_folder_func, paths);
1913 if (is_dir_exist(old_cache_dir)) {
1914 new_cache_dir = folder_item_get_path(item);
1915 if (rename(old_cache_dir, new_cache_dir) < 0) {
1916 FILE_OP_ERROR(old_cache_dir, "rename");
1918 g_free(new_cache_dir);
1921 g_free(old_cache_dir);
1924 g_free(real_oldpath);
1925 g_free(real_newpath);
1930 static gint imap_remove_folder(Folder *folder, FolderItem *item)
1933 IMAPSession *session;
1936 gint exists, recent, unseen;
1937 guint32 uid_validity;
1939 g_return_val_if_fail(folder != NULL, -1);
1940 g_return_val_if_fail(item != NULL, -1);
1941 g_return_val_if_fail(item->path != NULL, -1);
1943 session = imap_session_get(folder);
1944 if (!session) return -1;
1946 path = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1948 ok = imap_cmd_examine(session, "INBOX",
1949 &exists, &recent, &unseen, &uid_validity, FALSE);
1950 if (ok != IMAP_SUCCESS) {
1955 ok = imap_cmd_delete(session, path);
1956 if (ok != IMAP_SUCCESS) {
1957 log_warning(_("can't delete mailbox\n"));
1963 cache_dir = folder_item_get_path(item);
1964 if (is_dir_exist(cache_dir) && remove_dir_recursive(cache_dir) < 0)
1965 g_warning("can't remove directory '%s'\n", cache_dir);
1967 folder_item_remove(item);
1972 typedef struct _uncached_data {
1973 IMAPSession *session;
1975 MsgNumberList *numlist;
1979 static void *imap_get_uncached_messages_thread(void *data)
1981 uncached_data *stuff = (uncached_data *)data;
1982 IMAPSession *session = stuff->session;
1983 FolderItem *item = stuff->item;
1984 MsgNumberList *numlist = stuff->numlist;
1987 GSList *newlist = NULL;
1988 GSList *llast = NULL;
1991 GSList *seq_list, *cur;
1994 if (session == NULL || item == NULL || item->folder == NULL
1995 || FOLDER_CLASS(item->folder) != &imap_class) {
2000 seq_list = imap_get_seq_set_from_numlist(numlist);
2001 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
2002 imapset = cur->data;
2004 if (imap_cmd_envelope(session, imapset)
2006 log_warning(_("can't get envelope\n"));
2010 str = g_string_new(NULL);
2013 if ((tmp =sock_getline(SESSION(session)->sock)) == NULL) {
2014 log_warning(_("error occurred while getting envelope.\n"));
2015 g_string_free(str, TRUE);
2019 if (tmp[0] != '*' || tmp[1] != ' ') {
2020 log_print("IMAP4< %s\n", tmp);
2024 if (strstr(tmp, "FETCH") == NULL) {
2025 log_print("IMAP4< %s\n", tmp);
2029 log_print("IMAP4< %s\n", tmp);
2030 g_string_assign(str, tmp);
2033 msginfo = imap_parse_envelope
2034 (SESSION(session)->sock, item, str);
2036 log_warning(_("can't parse envelope: %s\n"), str->str);
2039 if (item->stype == F_QUEUE) {
2040 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_QUEUED);
2041 } else if (item->stype == F_DRAFT) {
2042 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_DRAFT);
2045 msginfo->folder = item;
2048 llast = newlist = g_slist_append(newlist, msginfo);
2050 llast = g_slist_append(llast, msginfo);
2051 llast = llast->next;
2055 g_string_free(str, TRUE);
2057 imap_seq_set_free(seq_list);
2059 session_set_access_time(SESSION(session));
2065 static GSList *imap_get_uncached_messages(IMAPSession *session,
2067 MsgNumberList *numlist)
2069 uncached_data *data = g_new0(uncached_data, 1);
2070 GSList *result = NULL;
2075 data->session = session;
2077 data->numlist = numlist;
2079 if (prefs_common.work_offline && !imap_gtk_should_override()) {
2084 #if (defined USE_PTHREAD && defined __GLIBC__ && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 3)))
2085 if (pthread_create(&pt, PTHREAD_CREATE_JOINABLE,
2086 imap_get_uncached_messages_thread, data) != 0) {
2087 result = (GSList *)imap_get_uncached_messages_thread(data);
2091 debug_print("+++waiting for imap_get_uncached_messages_thread...\n");
2092 while(!data->done) {
2093 /* don't let the interface freeze while waiting */
2096 debug_print("---imap_get_uncached_messages_thread done\n");
2098 /* get the thread's return value and clean its resources */
2099 pthread_join(pt, (void *)&result);
2101 result = (GSList *)imap_get_uncached_messages_thread(data);
2107 static void imap_delete_all_cached_messages(FolderItem *item)
2111 g_return_if_fail(item != NULL);
2112 g_return_if_fail(item->folder != NULL);
2113 g_return_if_fail(FOLDER_CLASS(item->folder) == &imap_class);
2115 debug_print("Deleting all cached messages...\n");
2117 dir = folder_item_get_path(item);
2118 if (is_dir_exist(dir))
2119 remove_all_numbered_files(dir);
2122 debug_print("done.\n");
2126 static SockInfo *imap_open_tunnel(const gchar *server,
2127 const gchar *tunnelcmd,
2130 static SockInfo *imap_open_tunnel(const gchar *server,
2131 const gchar *tunnelcmd)
2136 if ((sock = sock_connect_cmd(server, tunnelcmd)) == NULL) {
2137 log_warning(_("Can't establish IMAP4 session with: %s\n"),
2142 return imap_init_sock(sock, ssl_type);
2144 return imap_init_sock(sock);
2148 void *imap_open_thread(void *data)
2150 SockInfo *sock = NULL;
2151 thread_data *td = (thread_data *)data;
2152 if ((sock = sock_connect(td->server, td->port)) == NULL) {
2153 log_warning(_("Can't connect to IMAP4 server: %s:%d\n"),
2154 td->server, td->port);
2164 static SockInfo *imap_open(const gchar *server, gushort port,
2167 static SockInfo *imap_open(const gchar *server, gushort port)
2170 thread_data *td = g_new0(thread_data, 1);
2174 SockInfo *sock = NULL;
2177 td->ssl_type = ssl_type;
2179 td->server = g_strdup(server);
2183 statusbar_print_all(_("Connecting to IMAP4 server: %s..."), server);
2185 #if (defined USE_PTHREAD && defined __GLIBC__ && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 3)))
2186 if (pthread_create(&pt, PTHREAD_CREATE_JOINABLE,
2187 imap_open_thread, td) != 0) {
2188 statusbar_pop_all();
2189 sock = imap_open_thread(td);
2191 debug_print("+++waiting for imap_open_thread...\n");
2193 /* don't let the interface freeze while waiting */
2197 /* get the thread's return value and clean its resources */
2198 pthread_join(pt, (void *)&sock);
2201 sock = imap_open_thread(td);
2204 if (sock && td->ssl_type == SSL_TUNNEL && !ssl_init_socket(sock)) {
2205 log_warning(_("Can't establish IMAP4 session with: %s:%d\n"),
2206 td->server, td->port);
2215 debug_print("---imap_open_thread returned %p\n", sock);
2216 statusbar_pop_all();
2218 if(!sock && !prefs_common.no_recv_err_panel) {
2219 alertpanel_error(_("Can't connect to IMAP4 server: %s:%d"),
2227 static SockInfo *imap_init_sock(SockInfo *sock, SSLType ssl_type)
2229 static SockInfo *imap_init_sock(SockInfo *sock)
2236 static GList *imap_parse_namespace_str(gchar *str)
2241 IMAPNameSpace *namespace;
2242 GList *ns_list = NULL;
2244 while (*p != '\0') {
2245 /* parse ("#foo" "/") */
2247 while (*p && *p != '(') p++;
2248 if (*p == '\0') break;
2251 while (*p && *p != '"') p++;
2252 if (*p == '\0') break;
2256 while (*p && *p != '"') p++;
2257 if (*p == '\0') break;
2261 while (*p && isspace(*p)) p++;
2262 if (*p == '\0') break;
2263 if (strncmp(p, "NIL", 3) == 0)
2265 else if (*p == '"') {
2268 while (*p && *p != '"') p++;
2269 if (*p == '\0') break;
2274 while (*p && *p != ')') p++;
2275 if (*p == '\0') break;
2278 namespace = g_new(IMAPNameSpace, 1);
2279 namespace->name = g_strdup(name);
2280 namespace->separator = separator ? separator[0] : '\0';
2281 ns_list = g_list_append(ns_list, namespace);
2287 static void imap_parse_namespace(IMAPSession *session, IMAPFolder *folder)
2292 g_return_if_fail(session != NULL);
2293 g_return_if_fail(folder != NULL);
2295 if (folder->ns_personal != NULL ||
2296 folder->ns_others != NULL ||
2297 folder->ns_shared != NULL)
2300 if (!imap_has_capability(session, "NAMESPACE")) {
2301 imap_get_namespace_by_list(session, folder);
2305 if (imap_cmd_namespace(session, &ns_str)
2307 log_warning(_("can't get namespace\n"));
2311 str_array = strsplit_parenthesis(ns_str, '(', ')', 3);
2312 if (str_array == NULL) {
2314 imap_get_namespace_by_list(session, folder);
2318 folder->ns_personal = imap_parse_namespace_str(str_array[0]);
2319 if (str_array[0] && str_array[1])
2320 folder->ns_others = imap_parse_namespace_str(str_array[1]);
2321 if (str_array[0] && str_array[1] && str_array[2])
2322 folder->ns_shared = imap_parse_namespace_str(str_array[2]);
2323 g_strfreev(str_array);
2327 static void imap_get_namespace_by_list(IMAPSession *session, IMAPFolder *folder)
2329 GSList *item_list, *cur;
2330 gchar separator = '\0';
2331 IMAPNameSpace *namespace;
2333 g_return_if_fail(session != NULL);
2334 g_return_if_fail(folder != NULL);
2336 if (folder->ns_personal != NULL ||
2337 folder->ns_others != NULL ||
2338 folder->ns_shared != NULL)
2341 imap_gen_send(session, "LIST \"\" \"\"");
2342 item_list = imap_parse_list(folder, session, "", &separator);
2343 for (cur = item_list; cur != NULL; cur = cur->next)
2344 folder_item_destroy(FOLDER_ITEM(cur->data));
2345 g_slist_free(item_list);
2347 namespace = g_new(IMAPNameSpace, 1);
2348 namespace->name = g_strdup("");
2349 namespace->separator = separator;
2350 folder->ns_personal = g_list_append(NULL, namespace);
2353 static IMAPNameSpace *imap_find_namespace_from_list(GList *ns_list,
2356 IMAPNameSpace *namespace = NULL;
2357 gchar *tmp_path, *name;
2359 if (!path) path = "";
2361 for (; ns_list != NULL; ns_list = ns_list->next) {
2362 IMAPNameSpace *tmp_ns = ns_list->data;
2364 Xstrcat_a(tmp_path, path, "/", return namespace);
2365 Xstrdup_a(name, tmp_ns->name, return namespace);
2366 if (tmp_ns->separator && tmp_ns->separator != '/') {
2367 subst_char(tmp_path, tmp_ns->separator, '/');
2368 subst_char(name, tmp_ns->separator, '/');
2370 if (strncmp(tmp_path, name, strlen(name)) == 0)
2377 static IMAPNameSpace *imap_find_namespace(IMAPFolder *folder,
2380 IMAPNameSpace *namespace;
2382 g_return_val_if_fail(folder != NULL, NULL);
2384 namespace = imap_find_namespace_from_list(folder->ns_personal, path);
2385 if (namespace) return namespace;
2386 namespace = imap_find_namespace_from_list(folder->ns_others, path);
2387 if (namespace) return namespace;
2388 namespace = imap_find_namespace_from_list(folder->ns_shared, path);
2389 if (namespace) return namespace;
2394 static gchar imap_get_path_separator(IMAPFolder *folder, const gchar *path)
2396 IMAPNameSpace *namespace;
2397 gchar separator = '/';
2399 namespace = imap_find_namespace(folder, path);
2400 if (namespace && namespace->separator)
2401 separator = namespace->separator;
2406 static gchar *imap_get_real_path(IMAPFolder *folder, const gchar *path)
2411 g_return_val_if_fail(folder != NULL, NULL);
2412 g_return_val_if_fail(path != NULL, NULL);
2414 real_path = imap_utf8_to_modified_utf7(path);
2415 separator = imap_get_path_separator(folder, path);
2416 imap_path_separator_subst(real_path, separator);
2421 static gchar *imap_parse_atom(SockInfo *sock, gchar *src,
2422 gchar *dest, gint dest_len, GString *str)
2424 gchar *cur_pos = src;
2427 g_return_val_if_fail(str != NULL, cur_pos);
2429 /* read the next line if the current response buffer is empty */
2430 while (isspace(*(guchar *)cur_pos)) cur_pos++;
2431 while (*cur_pos == '\0') {
2432 if ((nextline = imap_getline(sock)) == NULL)
2434 g_string_assign(str, nextline);
2436 strretchomp(nextline);
2437 /* log_print("IMAP4< %s\n", nextline); */
2438 debug_print("IMAP4< %s\n", nextline);
2441 while (isspace(*(guchar *)cur_pos)) cur_pos++;
2444 if (!strncmp(cur_pos, "NIL", 3)) {
2447 } else if (*cur_pos == '\"') {
2450 p = get_quoted(cur_pos, '\"', dest, dest_len);
2451 cur_pos = p ? p : cur_pos + 2;
2452 } else if (*cur_pos == '{') {
2457 cur_pos = strchr_cpy(cur_pos + 1, '}', buf, sizeof(buf));
2459 g_return_val_if_fail(len >= 0, cur_pos);
2461 g_string_truncate(str, 0);
2465 if ((nextline = imap_getline(sock)) == NULL)
2467 line_len += strlen(nextline);
2468 g_string_append(str, nextline);
2470 strretchomp(nextline);
2471 /* log_print("IMAP4< %s\n", nextline); */
2472 debug_print("IMAP4< %s\n", nextline);
2474 } while (line_len < len);
2476 memcpy(dest, cur_pos, MIN(len, dest_len - 1));
2477 dest[MIN(len, dest_len - 1)] = '\0';
2484 static gchar *imap_get_header(SockInfo *sock, gchar *cur_pos, gchar **headers,
2494 g_return_val_if_fail(str != NULL, cur_pos);
2496 while (isspace(*(guchar *)cur_pos)) cur_pos++;
2498 g_return_val_if_fail(*cur_pos == '{', cur_pos);
2500 cur_pos = strchr_cpy(cur_pos + 1, '}', buf, sizeof(buf));
2502 g_return_val_if_fail(len >= 0, cur_pos);
2504 g_string_truncate(str, 0);
2508 if ((nextline = sock_getline(sock)) == NULL)
2510 block_len += strlen(nextline);
2511 g_string_append(str, nextline);
2513 strretchomp(nextline);
2514 /* debug_print("IMAP4< %s\n", nextline); */
2516 } while (block_len < len);
2518 debug_print("IMAP4< [contents of BODY.PEEK[HEADER_FIELDS (...)]\n");
2520 *headers = g_strndup(cur_pos, len);
2523 while (isspace(*(guchar *)cur_pos)) cur_pos++;
2524 while (*cur_pos == '\0') {
2525 if ((nextline = sock_getline(sock)) == NULL)
2527 g_string_assign(str, nextline);
2529 strretchomp(nextline);
2530 debug_print("IMAP4< %s\n", nextline);
2533 while (isspace(*(guchar *)cur_pos)) cur_pos++;
2539 static MsgFlags imap_parse_flags(const gchar *flag_str)
2541 const gchar *p = flag_str;
2542 MsgFlags flags = {0, 0};
2544 flags.perm_flags = MSG_UNREAD;
2546 while ((p = strchr(p, '\\')) != NULL) {
2549 if (g_ascii_strncasecmp(p, "Recent", 6) == 0 && MSG_IS_UNREAD(flags)) {
2550 MSG_SET_PERM_FLAGS(flags, MSG_NEW);
2551 } else if (g_ascii_strncasecmp(p, "Seen", 4) == 0) {
2552 MSG_UNSET_PERM_FLAGS(flags, MSG_NEW|MSG_UNREAD);
2553 } else if (g_ascii_strncasecmp(p, "Deleted", 7) == 0) {
2554 MSG_SET_PERM_FLAGS(flags, MSG_DELETED);
2555 } else if (g_ascii_strncasecmp(p, "Flagged", 7) == 0) {
2556 MSG_SET_PERM_FLAGS(flags, MSG_MARKED);
2557 } else if (g_ascii_strncasecmp(p, "Answered", 8) == 0) {
2558 MSG_SET_PERM_FLAGS(flags, MSG_REPLIED);
2565 static MsgInfo *imap_parse_envelope(SockInfo *sock, FolderItem *item,
2568 gchar buf[IMAPBUFSIZE];
2569 MsgInfo *msginfo = NULL;
2574 MsgFlags flags = {0, 0}, imap_flags = {0, 0};
2576 g_return_val_if_fail(line_str != NULL, NULL);
2577 g_return_val_if_fail(line_str->str[0] == '*' &&
2578 line_str->str[1] == ' ', NULL);
2580 MSG_SET_TMP_FLAGS(flags, MSG_IMAP);
2581 if (item->stype == F_QUEUE) {
2582 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
2583 } else if (item->stype == F_DRAFT) {
2584 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
2587 cur_pos = line_str->str + 2;
2589 #define PARSE_ONE_ELEMENT(ch) \
2591 cur_pos = strchr_cpy(cur_pos, ch, buf, sizeof(buf)); \
2592 if (cur_pos == NULL) { \
2593 g_warning("cur_pos == NULL\n"); \
2594 procmsg_msginfo_free(msginfo); \
2599 PARSE_ONE_ELEMENT(' ');
2602 PARSE_ONE_ELEMENT(' ');
2603 g_return_val_if_fail(!strcmp(buf, "FETCH"), NULL);
2605 g_return_val_if_fail(*cur_pos == '(', NULL);
2608 while (*cur_pos != '\0' && *cur_pos != ')') {
2609 while (*cur_pos == ' ') cur_pos++;
2611 if (!strncmp(cur_pos, "UID ", 4)) {
2613 uid = strtoul(cur_pos, &cur_pos, 10);
2614 } else if (!strncmp(cur_pos, "FLAGS ", 6)) {
2616 if (*cur_pos != '(') {
2617 g_warning("*cur_pos != '('\n");
2618 procmsg_msginfo_free(msginfo);
2622 PARSE_ONE_ELEMENT(')');
2623 imap_flags = imap_parse_flags(buf);
2624 } else if (!strncmp(cur_pos, "RFC822.SIZE ", 12)) {
2626 size = strtol(cur_pos, &cur_pos, 10);
2627 } else if (!strncmp(cur_pos, "BODY[HEADER.FIELDS ", 19)) {
2631 if (*cur_pos != '(') {
2632 g_warning("*cur_pos != '('\n");
2633 procmsg_msginfo_free(msginfo);
2637 PARSE_ONE_ELEMENT(')');
2638 if (*cur_pos != ']') {
2639 g_warning("*cur_pos != ']'\n");
2640 procmsg_msginfo_free(msginfo);
2644 cur_pos = imap_get_header(sock, cur_pos, &headers,
2646 msginfo = procheader_parse_str(headers, flags, FALSE, FALSE);
2649 g_warning("invalid FETCH response: %s\n", cur_pos);
2655 msginfo->msgnum = uid;
2656 msginfo->size = size;
2657 msginfo->flags.tmp_flags |= imap_flags.tmp_flags;
2658 msginfo->flags.perm_flags = imap_flags.perm_flags;
2664 static gchar *imap_get_flag_str(IMAPFlags flags)
2669 str = g_string_new(NULL);
2671 if (IMAP_IS_SEEN(flags)) g_string_append(str, "\\Seen ");
2672 if (IMAP_IS_ANSWERED(flags)) g_string_append(str, "\\Answered ");
2673 if (IMAP_IS_FLAGGED(flags)) g_string_append(str, "\\Flagged ");
2674 if (IMAP_IS_DELETED(flags)) g_string_append(str, "\\Deleted ");
2675 if (IMAP_IS_DRAFT(flags)) g_string_append(str, "\\Draft");
2677 if (str->len > 0 && str->str[str->len - 1] == ' ')
2678 g_string_truncate(str, str->len - 1);
2681 g_string_free(str, FALSE);
2686 static gint imap_set_message_flags(IMAPSession *session,
2687 MsgNumberList *numlist,
2697 flag_str = imap_get_flag_str(flags);
2698 cmd = g_strconcat(is_set ? "+FLAGS.SILENT (" : "-FLAGS.SILENT (",
2699 flag_str, ")", NULL);
2702 seq_list = imap_get_seq_set_from_numlist(numlist);
2703 imapset = get_seq_set_from_seq_list(seq_list);
2705 ok = imap_cmd_store(session, imapset, cmd);
2708 imap_seq_set_free(seq_list);
2714 typedef struct _select_data {
2715 IMAPSession *session;
2720 guint32 *uid_validity;
2724 static void *imap_select_thread(void *data)
2726 select_data *stuff = (select_data *)data;
2727 IMAPSession *session = stuff->session;
2728 gchar *real_path = stuff->real_path;
2729 gint *exists = stuff->exists;
2730 gint *recent = stuff->recent;
2731 gint *unseen = stuff->unseen;
2732 guint32 *uid_validity = stuff->uid_validity;
2735 ok = imap_cmd_select(session, real_path,
2736 exists, recent, unseen, uid_validity, TRUE);
2738 return GINT_TO_POINTER(ok);
2741 static gint imap_select(IMAPSession *session, IMAPFolder *folder,
2743 gint *exists, gint *recent, gint *unseen,
2744 guint32 *uid_validity, gboolean block)
2748 gint exists_, recent_, unseen_;
2749 guint32 uid_validity_;
2751 if (!exists || !recent || !unseen || !uid_validity) {
2752 if (session->mbox && strcmp(session->mbox, path) == 0)
2753 return IMAP_SUCCESS;
2757 uid_validity = &uid_validity_;
2760 g_free(session->mbox);
2761 session->mbox = NULL;
2763 real_path = imap_get_real_path(folder, path);
2765 #if (defined USE_PTHREAD && defined __GLIBC__ && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 3)))
2766 if (block == FALSE) {
2767 select_data *data = g_new0(select_data, 1);
2770 data->session = session;
2771 data->real_path = real_path;
2772 data->exists = exists;
2773 data->recent = recent;
2774 data->unseen = unseen;
2775 data->uid_validity = uid_validity;
2778 if (pthread_create(&pt, PTHREAD_CREATE_JOINABLE,
2779 imap_select_thread, data) != 0) {
2780 ok = GPOINTER_TO_INT(imap_select_thread(data));
2783 debug_print("+++waiting for imap_select_thread...\n");
2784 while(!data->done) {
2785 /* don't let the interface freeze while waiting */
2788 debug_print("---imap_select_thread done\n");
2790 /* get the thread's return value and clean its resources */
2791 pthread_join(pt, &tmp);
2792 ok = GPOINTER_TO_INT(tmp);
2796 ok = imap_cmd_select(session, real_path,
2797 exists, recent, unseen, uid_validity, block);
2800 ok = imap_cmd_select(session, real_path,
2801 exists, recent, unseen, uid_validity, block);
2803 if (ok != IMAP_SUCCESS)
2804 log_warning(_("can't select folder: %s\n"), real_path);
2806 session->mbox = g_strdup(path);
2807 session->folder_content_changed = FALSE;
2814 #define THROW(err) { ok = err; goto catch; }
2816 static gint imap_status(IMAPSession *session, IMAPFolder *folder,
2818 gint *messages, gint *recent,
2819 guint32 *uid_next, guint32 *uid_validity,
2820 gint *unseen, gboolean block)
2825 GPtrArray *argbuf = NULL;
2828 if (messages && recent && uid_next && uid_validity && unseen) {
2829 *messages = *recent = *uid_next = *uid_validity = *unseen = 0;
2830 argbuf = g_ptr_array_new();
2833 real_path = imap_get_real_path(folder, path);
2834 QUOTE_IF_REQUIRED(real_path_, real_path);
2835 imap_gen_send(session, "STATUS %s "
2836 "(MESSAGES RECENT UIDNEXT UIDVALIDITY UNSEEN)",
2839 ok = imap_cmd_ok_with_block(session, argbuf, block);
2840 if (ok != IMAP_SUCCESS || !argbuf) THROW(ok);
2842 str = search_array_str(argbuf, "STATUS");
2843 if (!str) THROW(IMAP_ERROR);
2845 str = strchr(str, '(');
2846 if (!str) THROW(IMAP_ERROR);
2848 while (*str != '\0' && *str != ')') {
2849 while (*str == ' ') str++;
2851 if (!strncmp(str, "MESSAGES ", 9)) {
2853 *messages = strtol(str, &str, 10);
2854 } else if (!strncmp(str, "RECENT ", 7)) {
2856 *recent = strtol(str, &str, 10);
2857 } else if (!strncmp(str, "UIDNEXT ", 8)) {
2859 *uid_next = strtoul(str, &str, 10);
2860 } else if (!strncmp(str, "UIDVALIDITY ", 12)) {
2862 *uid_validity = strtoul(str, &str, 10);
2863 } else if (!strncmp(str, "UNSEEN ", 7)) {
2865 *unseen = strtol(str, &str, 10);
2867 g_warning("invalid STATUS response: %s\n", str);
2875 ptr_array_free_strings(argbuf);
2876 g_ptr_array_free(argbuf, TRUE);
2884 static gboolean imap_has_capability(IMAPSession *session, const gchar *cap)
2888 for (p = session->capability; *p != NULL; ++p) {
2889 if (!g_ascii_strcasecmp(*p, cap))
2896 static void imap_free_capabilities(IMAPSession *session)
2898 g_strfreev(session->capability);
2899 session->capability = NULL;
2902 /* low-level IMAP4rev1 commands */
2904 static gint imap_cmd_authenticate(IMAPSession *session, const gchar *user,
2905 const gchar *pass, IMAPAuthType type)
2912 gchar hexdigest[33];
2916 auth_type = "CRAM-MD5";
2918 imap_gen_send(session, "AUTHENTICATE %s", auth_type);
2919 ok = imap_gen_recv(session, &buf);
2920 if (ok != IMAP_SUCCESS || buf[0] != '+' || buf[1] != ' ') {
2925 challenge = g_malloc(strlen(buf + 2) + 1);
2926 challenge_len = base64_decode(challenge, buf + 2, -1);
2927 challenge[challenge_len] = '\0';
2929 log_print("IMAP< [Decoded: %s]\n", challenge);
2931 md5_hex_hmac(hexdigest, challenge, challenge_len, pass, strlen(pass));
2934 response = g_strdup_printf("%s %s", user, hexdigest);
2935 log_print("IMAP> [Encoded: %s]\n", response);
2936 response64 = g_malloc((strlen(response) + 3) * 2 + 1);
2937 base64_encode(response64, response, strlen(response));
2940 log_print("IMAP> %s\n", response64);
2941 sock_puts(SESSION(session)->sock, response64);
2942 ok = imap_cmd_ok(session, NULL);
2943 if (ok != IMAP_SUCCESS)
2944 log_warning(_("IMAP4 authentication failed.\n"));
2949 static gint imap_cmd_login(IMAPSession *session,
2950 const gchar *user, const gchar *pass)
2952 gchar *user_, *pass_;
2955 QUOTE_IF_REQUIRED(user_, user);
2956 QUOTE_IF_REQUIRED(pass_, pass);
2957 imap_gen_send(session, "LOGIN %s %s", user_, pass_);
2959 ok = imap_cmd_ok(session, NULL);
2960 if (ok != IMAP_SUCCESS)
2961 log_warning(_("IMAP4 login failed.\n"));
2966 static gint imap_cmd_logout(IMAPSession *session)
2968 imap_gen_send(session, "LOGOUT");
2969 return imap_cmd_ok(session, NULL);
2972 static gint imap_cmd_noop(IMAPSession *session)
2974 imap_gen_send(session, "NOOP");
2975 return imap_cmd_ok(session, NULL);
2979 static gint imap_cmd_starttls(IMAPSession *session)
2981 imap_gen_send(session, "STARTTLS");
2982 return imap_cmd_ok(session, NULL);
2986 #define THROW(err) { ok = err; goto catch; }
2988 static gint imap_cmd_namespace(IMAPSession *session, gchar **ns_str)
2994 argbuf = g_ptr_array_new();
2996 imap_gen_send(session, "NAMESPACE");
2997 if ((ok = imap_cmd_ok(session, argbuf)) != IMAP_SUCCESS) THROW(ok);
2999 str = search_array_str(argbuf, "NAMESPACE");
3000 if (!str) THROW(IMAP_ERROR);
3002 *ns_str = g_strdup(str);
3005 ptr_array_free_strings(argbuf);
3006 g_ptr_array_free(argbuf, TRUE);
3013 static gint imap_cmd_list(IMAPSession *session, const gchar *ref,
3014 const gchar *mailbox, GPtrArray *argbuf)
3016 gchar *ref_, *mailbox_;
3018 if (!ref) ref = "\"\"";
3019 if (!mailbox) mailbox = "\"\"";
3021 QUOTE_IF_REQUIRED(ref_, ref);
3022 QUOTE_IF_REQUIRED(mailbox_, mailbox);
3023 imap_gen_send(session, "LIST %s %s", ref_, mailbox_);
3025 return imap_cmd_ok(session, argbuf);
3028 #define THROW goto catch
3030 static gint imap_cmd_do_select(IMAPSession *session, const gchar *folder,
3032 gint *exists, gint *recent, gint *unseen,
3033 guint32 *uid_validity, gboolean block)
3040 unsigned int uid_validity_;
3042 *exists = *recent = *unseen = *uid_validity = 0;
3043 argbuf = g_ptr_array_new();
3046 select_cmd = "EXAMINE";
3048 select_cmd = "SELECT";
3050 QUOTE_IF_REQUIRED(folder_, folder);
3051 imap_gen_send(session, "%s %s", select_cmd, folder_);
3053 if ((ok = imap_cmd_ok_with_block(session, argbuf, block)) != IMAP_SUCCESS) THROW;
3055 resp_str = search_array_contain_str(argbuf, "EXISTS");
3057 if (sscanf(resp_str,"%d EXISTS", exists) != 1) {
3058 g_warning("imap_cmd_select(): invalid EXISTS line.\n");
3063 resp_str = search_array_contain_str(argbuf, "RECENT");
3065 if (sscanf(resp_str, "%d RECENT", recent) != 1) {
3066 g_warning("imap_cmd_select(): invalid RECENT line.\n");
3071 resp_str = search_array_contain_str(argbuf, "UIDVALIDITY");
3073 if (sscanf(resp_str, "OK [UIDVALIDITY %u] ", &uid_validity_)
3075 g_warning("imap_cmd_select(): invalid UIDVALIDITY line.\n");
3078 *uid_validity = uid_validity_;
3081 resp_str = search_array_contain_str(argbuf, "UNSEEN");
3083 if (sscanf(resp_str, "OK [UNSEEN %d] ", unseen) != 1) {
3084 g_warning("imap_cmd_select(): invalid UNSEEN line.\n");
3090 ptr_array_free_strings(argbuf);
3091 g_ptr_array_free(argbuf, TRUE);
3096 static gint imap_cmd_select(IMAPSession *session, const gchar *folder,
3097 gint *exists, gint *recent, gint *unseen,
3098 guint32 *uid_validity, gboolean block)
3100 return imap_cmd_do_select(session, folder, FALSE,
3101 exists, recent, unseen, uid_validity, block);
3104 static gint imap_cmd_examine(IMAPSession *session, const gchar *folder,
3105 gint *exists, gint *recent, gint *unseen,
3106 guint32 *uid_validity, gboolean block)
3108 return imap_cmd_do_select(session, folder, TRUE,
3109 exists, recent, unseen, uid_validity, block);
3114 static gint imap_cmd_create(IMAPSession *session, const gchar *folder)
3118 QUOTE_IF_REQUIRED(folder_, folder);
3119 imap_gen_send(session, "CREATE %s", folder_);
3121 return imap_cmd_ok(session, NULL);
3124 static gint imap_cmd_rename(IMAPSession *session, const gchar *old_folder,
3125 const gchar *new_folder)
3127 gchar *old_folder_, *new_folder_;
3129 QUOTE_IF_REQUIRED(old_folder_, old_folder);
3130 QUOTE_IF_REQUIRED(new_folder_, new_folder);
3131 imap_gen_send(session, "RENAME %s %s", old_folder_, new_folder_);
3133 return imap_cmd_ok(session, NULL);
3136 static gint imap_cmd_delete(IMAPSession *session, const gchar *folder)
3140 QUOTE_IF_REQUIRED(folder_, folder);
3141 imap_gen_send(session, "DELETE %s", folder_);
3143 return imap_cmd_ok(session, NULL);
3146 static gint imap_cmd_search(IMAPSession *session, const gchar *criteria,
3147 GSList **list, gboolean block)
3153 g_return_val_if_fail(criteria != NULL, IMAP_ERROR);
3154 g_return_val_if_fail(list != NULL, IMAP_ERROR);
3158 argbuf = g_ptr_array_new();
3159 imap_gen_send(session, "UID SEARCH %s", criteria);
3161 ok = imap_cmd_ok_with_block(session, argbuf, block);
3162 if (ok != IMAP_SUCCESS) {
3163 ptr_array_free_strings(argbuf);
3164 g_ptr_array_free(argbuf, TRUE);
3168 if ((uidlist = search_array_str(argbuf, "SEARCH ")) != NULL) {
3169 gchar **strlist, **p;
3171 strlist = g_strsplit(uidlist + 7, " ", 0);
3172 for (p = strlist; *p != NULL; ++p) {
3175 if (sscanf(*p, "%u", &msgnum) == 1)
3176 *list = g_slist_append(*list, GINT_TO_POINTER(msgnum));
3178 g_strfreev(strlist);
3180 ptr_array_free_strings(argbuf);
3181 g_ptr_array_free(argbuf, TRUE);
3183 return IMAP_SUCCESS;
3186 typedef struct _fetch_data {
3187 IMAPSession *session;
3189 const gchar *filename;
3193 static void *imap_cmd_fetch_thread(void *data)
3195 fetch_data *stuff = (fetch_data *)data;
3196 IMAPSession *session = stuff->session;
3197 guint32 uid = stuff->uid;
3198 const gchar *filename = stuff->filename;
3206 if (filename == NULL) {
3208 return GINT_TO_POINTER(IMAP_ERROR);
3211 imap_gen_send(session, "UID FETCH %d BODY.PEEK[]", uid);
3213 while ((ok = imap_gen_recv_block(session, &buf)) == IMAP_SUCCESS) {
3214 if (buf[0] != '*' || buf[1] != ' ') {
3217 return GINT_TO_POINTER(IMAP_ERROR);
3219 if (strstr(buf, "FETCH") != NULL) break;
3222 if (ok != IMAP_SUCCESS) {
3225 return GINT_TO_POINTER(ok);
3228 #define RETURN_ERROR_IF_FAIL(cond) \
3231 stuff->done = TRUE; \
3232 return GINT_TO_POINTER(IMAP_ERROR); \
3235 cur_pos = strchr(buf, '{');
3236 RETURN_ERROR_IF_FAIL(cur_pos != NULL);
3237 cur_pos = strchr_cpy(cur_pos + 1, '}', size_str, sizeof(size_str));
3238 RETURN_ERROR_IF_FAIL(cur_pos != NULL);
3239 size_num = atol(size_str);
3240 RETURN_ERROR_IF_FAIL(size_num >= 0);
3242 RETURN_ERROR_IF_FAIL(*cur_pos == '\0');
3244 #undef RETURN_ERROR_IF_FAIL
3248 if (recv_bytes_write_to_file(SESSION(session)->sock,
3249 size_num, filename) != 0) {
3251 return GINT_TO_POINTER(IMAP_ERROR);
3253 if (imap_gen_recv_block(session, &buf) != IMAP_SUCCESS) {
3256 return GINT_TO_POINTER(IMAP_ERROR);
3259 if (buf[0] == '\0' || buf[strlen(buf) - 1] != ')') {
3262 return GINT_TO_POINTER(IMAP_ERROR);
3266 ok = imap_cmd_ok_block(session, NULL);
3269 return GINT_TO_POINTER(ok);
3272 static gint imap_cmd_fetch(IMAPSession *session, guint32 uid,
3273 const gchar *filename)
3275 fetch_data *data = g_new0(fetch_data, 1);
3282 data->session = session;
3284 data->filename = filename;
3286 if (prefs_common.work_offline && !imap_gtk_should_override()) {
3291 #if (defined USE_PTHREAD && defined __GLIBC__ && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 3)))
3292 if (pthread_create(&pt, PTHREAD_CREATE_JOINABLE,
3293 imap_cmd_fetch_thread, data) != 0) {
3294 result = GPOINTER_TO_INT(imap_cmd_fetch_thread(data));
3298 debug_print("+++waiting for imap_cmd_fetch_thread...\n");
3299 while(!data->done) {
3300 /* don't let the interface freeze while waiting */
3303 debug_print("---imap_cmd_fetch_thread done\n");
3305 /* get the thread's return value and clean its resources */
3306 pthread_join(pt, &tmp);
3307 result = GPOINTER_TO_INT(tmp);
3309 result = GPOINTER_TO_INT(imap_cmd_fetch_thread(data));
3315 static gint imap_cmd_append(IMAPSession *session, const gchar *destfolder,
3316 const gchar *file, IMAPFlags flags,
3323 unsigned int new_uid_;
3325 gchar buf[BUFFSIZE];
3330 g_return_val_if_fail(file != NULL, IMAP_ERROR);
3332 size = get_file_size_as_crlf(file);
3333 if ((fp = fopen(file, "rb")) == NULL) {
3334 FILE_OP_ERROR(file, "fopen");
3337 QUOTE_IF_REQUIRED(destfolder_, destfolder);
3338 flag_str = imap_get_flag_str(flags);
3339 imap_gen_send(session, "APPEND %s (%s) {%d}",
3340 destfolder_, flag_str, size);
3343 ok = imap_gen_recv(session, &ret);
3344 if (ok != IMAP_SUCCESS || ret[0] != '+' || ret[1] != ' ') {
3345 log_warning(_("can't append %s to %s\n"), file, destfolder_);
3352 log_print("IMAP4> %s\n", "(sending file...)");
3354 while (fgets(buf, sizeof(buf), fp) != NULL) {
3356 if (sock_puts(SESSION(session)->sock, buf) < 0) {
3363 FILE_OP_ERROR(file, "fgets");
3368 sock_puts(SESSION(session)->sock, "");
3372 if (new_uid != NULL)
3375 if (new_uid != NULL && session->uidplus) {
3376 argbuf = g_ptr_array_new();
3378 ok = imap_cmd_ok(session, argbuf);
3379 if ((ok == IMAP_SUCCESS) && (argbuf->len > 0)) {
3380 resp_str = g_ptr_array_index(argbuf, argbuf->len - 1);
3382 sscanf(resp_str, "%*u OK [APPENDUID %*u %u]",
3384 *new_uid = new_uid_;
3388 ptr_array_free_strings(argbuf);
3389 g_ptr_array_free(argbuf, TRUE);
3391 ok = imap_cmd_ok(session, NULL);
3393 if (ok != IMAP_SUCCESS)
3394 log_warning(_("can't append message to %s\n"),
3400 static MsgNumberList *imapset_to_numlist(IMAPSet imapset)
3402 gchar **ranges, **range;
3403 unsigned int low, high;
3404 MsgNumberList *uids = NULL;
3406 ranges = g_strsplit(imapset, ",", 0);
3407 for (range = ranges; *range != NULL; range++) {
3408 if (sscanf(*range, "%u:%u", &low, &high) == 1)
3409 uids = g_slist_prepend(uids, GINT_TO_POINTER(low));
3412 for (i = low; i <= high; i++)
3413 uids = g_slist_prepend(uids, GINT_TO_POINTER(i));
3416 uids = g_slist_reverse(uids);
3422 static gint imap_cmd_copy(IMAPSession *session, const gchar *seq_set,
3423 const gchar *destfolder, GRelation *uid_mapping)
3428 g_return_val_if_fail(session != NULL, IMAP_ERROR);
3429 g_return_val_if_fail(seq_set != NULL, IMAP_ERROR);
3430 g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
3432 QUOTE_IF_REQUIRED(destfolder_, destfolder);
3433 imap_gen_send(session, "UID COPY %s %s", seq_set, destfolder_);
3435 if (uid_mapping != NULL && session->uidplus) {
3437 gchar *resp_str = NULL, *olduids_str, *newuids_str;
3438 MsgNumberList *olduids, *old_cur, *newuids, *new_cur;
3440 reply = g_ptr_array_new();
3441 ok = imap_cmd_ok(session, reply);
3442 if ((ok == IMAP_SUCCESS) && (reply->len > 0)) {
3443 resp_str = g_ptr_array_index(reply, reply->len - 1);
3445 olduids_str = g_new0(gchar, strlen(resp_str));
3446 newuids_str = g_new0(gchar, strlen(resp_str));
3447 if (sscanf(resp_str, "%*s OK [COPYUID %*u %[0-9,:] %[0-9,:]]",
3448 olduids_str, newuids_str) == 2) {
3449 olduids = imapset_to_numlist(olduids_str);
3450 newuids = imapset_to_numlist(newuids_str);
3454 while(old_cur != NULL && new_cur != NULL) {
3455 g_relation_insert(uid_mapping,
3456 GPOINTER_TO_INT(old_cur->data),
3457 GPOINTER_TO_INT(new_cur->data));
3458 old_cur = g_slist_next(old_cur);
3459 new_cur = g_slist_next(new_cur);
3462 g_slist_free(olduids);
3463 g_slist_free(newuids);
3465 g_free(olduids_str);
3466 g_free(newuids_str);
3469 ptr_array_free_strings(reply);
3470 g_ptr_array_free(reply, TRUE);
3472 ok = imap_cmd_ok(session, NULL);
3474 if (ok != IMAP_SUCCESS)
3475 log_warning(_("can't copy %s to %s\n"), seq_set, destfolder_);
3480 gint imap_cmd_envelope(IMAPSession *session, IMAPSet set)
3482 static gchar *header_fields =
3483 "Date From To Cc Subject Message-ID References In-Reply-To" ;
3486 (session, "UID FETCH %s (UID FLAGS RFC822.SIZE BODY.PEEK[HEADER.FIELDS (%s)])",
3487 set, header_fields);
3489 return IMAP_SUCCESS;
3492 static gint imap_cmd_store(IMAPSession *session, IMAPSet seq_set,
3497 imap_gen_send(session, "UID STORE %s %s", seq_set, sub_cmd);
3499 if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS) {
3500 log_warning(_("error while imap command: STORE %s %s\n"),
3505 return IMAP_SUCCESS;
3508 static gint imap_cmd_expunge(IMAPSession *session, IMAPSet seq_set)
3512 if (seq_set && session->uidplus)
3513 imap_gen_send(session, "UID EXPUNGE %s", seq_set);
3515 imap_gen_send(session, "EXPUNGE");
3516 if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS) {
3517 log_warning(_("error while imap command: EXPUNGE\n"));
3521 return IMAP_SUCCESS;
3524 static gint imap_cmd_close(IMAPSession *session)
3528 imap_gen_send(session, "CLOSE");
3529 if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS)
3530 log_warning(_("error while imap command: CLOSE\n"));
3535 static gint imap_cmd_ok_with_block(IMAPSession *session, GPtrArray *argbuf, gboolean block)
3537 gint ok = IMAP_SUCCESS;
3542 while ((ok = imap_gen_recv_with_block(session, &buf, block))
3544 /* make sure data is long enough for any substring of buf */
3545 data = alloca(strlen(buf) + 1);
3547 /* untagged line read */
3548 if (buf[0] == '*' && buf[1] == ' ') {
3551 g_ptr_array_add(argbuf, g_strdup(buf + 2));
3553 if (sscanf(buf + 2, "%d %s", &num, data) >= 2) {
3554 if (!strcmp(data, "EXISTS")) {
3555 session->exists = num;
3556 session->folder_content_changed = TRUE;
3559 if(!strcmp(data, "EXPUNGE")) {
3561 session->folder_content_changed = TRUE;
3564 /* tagged line with correct tag and OK response found */
3565 } else if ((sscanf(buf, "%d %s", &cmd_num, data) >= 2) &&
3566 (cmd_num == session->cmd_count) &&
3567 !strcmp(data, "OK")) {
3569 g_ptr_array_add(argbuf, g_strdup(buf));
3571 /* everything else */
3582 static gint imap_cmd_ok(IMAPSession *session, GPtrArray *argbuf)
3584 return imap_cmd_ok_with_block(session, argbuf, FALSE);
3586 static gint imap_cmd_ok_block(IMAPSession *session, GPtrArray *argbuf)
3588 return imap_cmd_ok_with_block(session, argbuf, TRUE);
3590 static void imap_gen_send(IMAPSession *session, const gchar *format, ...)
3597 va_start(args, format);
3598 tmp = g_strdup_vprintf(format, args);
3601 session->cmd_count++;
3603 buf = g_strdup_printf("%d %s\r\n", session->cmd_count, tmp);
3604 if (!g_ascii_strncasecmp(tmp, "LOGIN ", 6) && (p = strchr(tmp + 6, ' '))) {
3606 log_print("IMAP4> %d %s ********\n", session->cmd_count, tmp);
3608 log_print("IMAP4> %d %s\n", session->cmd_count, tmp);
3610 sock_write_all(SESSION(session)->sock, buf, strlen(buf));
3615 static gint imap_gen_recv_with_block(IMAPSession *session, gchar **ret, gboolean block)
3618 if ((*ret = imap_getline(SESSION(session)->sock)) == NULL)
3621 if ((*ret = sock_getline(SESSION(session)->sock)) == NULL)
3626 log_print("IMAP4< %s\n", *ret);
3628 session_set_access_time(SESSION(session));
3630 return IMAP_SUCCESS;
3633 static gint imap_gen_recv_block(IMAPSession *session, gchar **ret)
3635 return imap_gen_recv_with_block(session, ret, TRUE);
3638 static gint imap_gen_recv(IMAPSession *session, gchar **ret)
3640 return imap_gen_recv_with_block(session, ret, FALSE);
3642 /* misc utility functions */
3644 static gchar *strchr_cpy(const gchar *src, gchar ch, gchar *dest, gint len)
3649 tmp = strchr(src, ch);
3653 memcpy(dest, src, MIN(tmp - src, len - 1));
3654 dest[MIN(tmp - src, len - 1)] = '\0';
3659 static gchar *get_quoted(const gchar *src, gchar ch, gchar *dest, gint len)
3661 const gchar *p = src;
3664 g_return_val_if_fail(*p == ch, NULL);
3669 while (*p != '\0' && *p != ch) {
3671 if (*p == '\\' && *(p + 1) != '\0')
3680 return (gchar *)(*p == ch ? p + 1 : p);
3683 static gchar *search_array_contain_str(GPtrArray *array, const gchar *str)
3687 for (i = 0; i < array->len; i++) {
3690 tmp = g_ptr_array_index(array, i);
3691 if (strstr(tmp, str) != NULL)
3698 static gchar *search_array_str(GPtrArray *array, const gchar *str)
3705 for (i = 0; i < array->len; i++) {
3708 tmp = g_ptr_array_index(array, i);
3709 if (!strncmp(tmp, str, len))
3716 static void imap_path_separator_subst(gchar *str, gchar separator)
3719 gboolean in_escape = FALSE;
3721 if (!separator || separator == '/') return;
3723 for (p = str; *p != '\0'; p++) {
3724 if (*p == '/' && !in_escape)
3726 else if (*p == '&' && *(p + 1) != '-' && !in_escape)
3728 else if (*p == '-' && in_escape)
3733 static gchar *imap_modified_utf7_to_utf8(const gchar *mutf7_str)
3735 static iconv_t cd = (iconv_t)-1;
3736 static gboolean iconv_ok = TRUE;
3739 size_t norm_utf7_len;
3741 gchar *to_str, *to_p;
3743 gboolean in_escape = FALSE;
3745 if (!iconv_ok) return g_strdup(mutf7_str);
3747 if (cd == (iconv_t)-1) {
3748 cd = iconv_open(CS_INTERNAL, CS_UTF_7);
3749 if (cd == (iconv_t)-1) {
3750 g_warning("iconv cannot convert UTF-7 to %s\n",
3753 return g_strdup(mutf7_str);
3757 /* modified UTF-7 to normal UTF-7 conversion */
3758 norm_utf7 = g_string_new(NULL);
3760 for (p = mutf7_str; *p != '\0'; p++) {
3761 /* replace: '&' -> '+',
3763 escaped ',' -> '/' */
3764 if (!in_escape && *p == '&') {
3765 if (*(p + 1) != '-') {
3766 g_string_append_c(norm_utf7, '+');
3769 g_string_append_c(norm_utf7, '&');
3772 } else if (in_escape && *p == ',') {
3773 g_string_append_c(norm_utf7, '/');
3774 } else if (in_escape && *p == '-') {
3775 g_string_append_c(norm_utf7, '-');
3778 g_string_append_c(norm_utf7, *p);
3782 norm_utf7_p = norm_utf7->str;
3783 norm_utf7_len = norm_utf7->len;
3784 to_len = strlen(mutf7_str) * 5;
3785 to_p = to_str = g_malloc(to_len + 1);
3787 if (iconv(cd, (ICONV_CONST gchar **)&norm_utf7_p, &norm_utf7_len,
3788 &to_p, &to_len) == -1) {
3789 g_warning(_("iconv cannot convert UTF-7 to %s\n"),
3790 conv_get_locale_charset_str());
3791 g_string_free(norm_utf7, TRUE);
3793 return g_strdup(mutf7_str);
3796 /* second iconv() call for flushing */
3797 iconv(cd, NULL, NULL, &to_p, &to_len);
3798 g_string_free(norm_utf7, TRUE);
3804 static gchar *imap_utf8_to_modified_utf7(const gchar *from)
3806 static iconv_t cd = (iconv_t)-1;
3807 static gboolean iconv_ok = TRUE;
3808 gchar *norm_utf7, *norm_utf7_p;
3809 size_t from_len, norm_utf7_len;
3811 gchar *from_tmp, *to, *p;
3812 gboolean in_escape = FALSE;
3814 if (!iconv_ok) return g_strdup(from);
3816 if (cd == (iconv_t)-1) {
3817 cd = iconv_open(CS_UTF_7, CS_INTERNAL);
3818 if (cd == (iconv_t)-1) {
3819 g_warning(_("iconv cannot convert %s to UTF-7\n"),
3822 return g_strdup(from);
3826 /* UTF-8 to normal UTF-7 conversion */
3827 Xstrdup_a(from_tmp, from, return g_strdup(from));
3828 from_len = strlen(from);
3829 norm_utf7_len = from_len * 5;
3830 Xalloca(norm_utf7, norm_utf7_len + 1, return g_strdup(from));
3831 norm_utf7_p = norm_utf7;
3833 #define IS_PRINT(ch) (isprint(ch) && IS_ASCII(ch))
3835 while (from_len > 0) {
3836 if (*from_tmp == '+') {
3837 *norm_utf7_p++ = '+';
3838 *norm_utf7_p++ = '-';
3842 } else if (IS_PRINT(*(guchar *)from_tmp)) {
3843 /* printable ascii char */
3844 *norm_utf7_p = *from_tmp;
3850 size_t conv_len = 0;
3852 /* unprintable char: convert to UTF-7 */
3854 while (!IS_PRINT(*(guchar *)p) && conv_len < from_len) {
3855 conv_len += g_utf8_skip[*(guchar *)p];
3856 p += g_utf8_skip[*(guchar *)p];
3859 from_len -= conv_len;
3860 if (iconv(cd, (ICONV_CONST gchar **)&from_tmp,
3862 &norm_utf7_p, &norm_utf7_len) == -1) {
3863 g_warning(_("iconv cannot convert UTF-8 to UTF-7\n"));
3864 return g_strdup(from);
3867 /* second iconv() call for flushing */
3868 iconv(cd, NULL, NULL, &norm_utf7_p, &norm_utf7_len);
3874 *norm_utf7_p = '\0';
3875 to_str = g_string_new(NULL);
3876 for (p = norm_utf7; p < norm_utf7_p; p++) {
3877 /* replace: '&' -> "&-",
3880 BASE64 '/' -> ',' */
3881 if (!in_escape && *p == '&') {
3882 g_string_append(to_str, "&-");
3883 } else if (!in_escape && *p == '+') {
3884 if (*(p + 1) == '-') {
3885 g_string_append_c(to_str, '+');
3888 g_string_append_c(to_str, '&');
3891 } else if (in_escape && *p == '/') {
3892 g_string_append_c(to_str, ',');
3893 } else if (in_escape && *p == '-') {
3894 g_string_append_c(to_str, '-');
3897 g_string_append_c(to_str, *p);
3903 g_string_append_c(to_str, '-');
3907 g_string_free(to_str, FALSE);
3912 static GSList *imap_get_seq_set_from_numlist(MsgNumberList *numlist)
3915 GSList *sorted_list, *cur;
3916 guint first, last, next;
3918 GSList *ret_list = NULL;
3920 if (numlist == NULL)
3923 str = g_string_sized_new(256);
3925 sorted_list = g_slist_copy(numlist);
3926 sorted_list = g_slist_sort(sorted_list, g_int_compare);
3928 first = GPOINTER_TO_INT(sorted_list->data);
3930 for (cur = sorted_list; cur != NULL; cur = g_slist_next(cur)) {
3931 last = GPOINTER_TO_INT(cur->data);
3933 next = GPOINTER_TO_INT(cur->next->data);
3937 if (last + 1 != next || next == 0) {
3939 g_string_append_c(str, ',');
3941 g_string_append_printf(str, "%u", first);
3943 g_string_append_printf(str, "%u:%u", first, last);
3947 if (str->len > IMAP_CMD_LIMIT) {
3948 ret_str = g_strdup(str->str);
3949 ret_list = g_slist_append(ret_list, ret_str);
3950 g_string_truncate(str, 0);
3956 ret_str = g_strdup(str->str);
3957 ret_list = g_slist_append(ret_list, ret_str);
3960 g_slist_free(sorted_list);
3961 g_string_free(str, TRUE);
3966 static GSList *imap_get_seq_set_from_msglist(MsgInfoList *msglist)
3968 MsgNumberList *numlist = NULL;
3972 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
3973 MsgInfo *msginfo = (MsgInfo *) cur->data;
3975 numlist = g_slist_append(numlist, GINT_TO_POINTER(msginfo->msgnum));
3977 seq_list = imap_get_seq_set_from_numlist(numlist);
3978 g_slist_free(numlist);
3983 static void imap_seq_set_free(GSList *seq_list)
3985 slist_free_strings(seq_list);
3986 g_slist_free(seq_list);
3990 static gboolean imap_rename_folder_func(GNode *node, gpointer data)
3992 FolderItem *item = node->data;
3993 gchar **paths = data;
3994 const gchar *oldpath = paths[0];
3995 const gchar *newpath = paths[1];
3997 gchar *new_itempath;
4000 oldpathlen = strlen(oldpath);
4001 if (strncmp(oldpath, item->path, oldpathlen) != 0) {
4002 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
4006 base = item->path + oldpathlen;
4007 while (*base == G_DIR_SEPARATOR) base++;
4009 new_itempath = g_strdup(newpath);
4011 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
4014 item->path = new_itempath;
4019 typedef struct _get_list_uid_data {
4021 IMAPFolderItem *item;
4022 GSList **msgnum_list;
4024 } get_list_uid_data;
4026 static void *get_list_of_uids_thread(void *data)
4028 get_list_uid_data *stuff = (get_list_uid_data *)data;
4029 Folder *folder = stuff->folder;
4030 IMAPFolderItem *item = stuff->item;
4031 GSList **msgnum_list = stuff->msgnum_list;
4032 gint ok, nummsgs = 0, lastuid_old;
4033 IMAPSession *session;
4034 GSList *uidlist, *elem;
4037 session = imap_session_get(folder);
4038 if (session == NULL) {
4040 return GINT_TO_POINTER(-1);
4043 ok = imap_select(session, IMAP_FOLDER(folder), item->item.path,
4044 NULL, NULL, NULL, NULL, TRUE);
4045 if (ok != IMAP_SUCCESS) {
4047 return GINT_TO_POINTER(-1);
4050 cmd_buf = g_strdup_printf("UID %d:*", item->lastuid + 1);
4051 ok = imap_cmd_search(session, cmd_buf, &uidlist, TRUE);
4054 if (ok == IMAP_SOCKET) {
4055 session_destroy((Session *)session);
4056 ((RemoteFolder *)folder)->session = NULL;
4058 return GINT_TO_POINTER(-1);
4061 if (ok != IMAP_SUCCESS) {
4065 argbuf = g_ptr_array_new();
4067 cmd_buf = g_strdup_printf("UID FETCH %d:* (UID)", item->lastuid + 1);
4068 imap_gen_send(session, cmd_buf);
4070 ok = imap_cmd_ok_block(session, argbuf);
4071 if (ok != IMAP_SUCCESS) {
4072 ptr_array_free_strings(argbuf);
4073 g_ptr_array_free(argbuf, TRUE);
4075 return GINT_TO_POINTER(-1);
4078 for(i = 0; i < argbuf->len; i++) {
4081 if((ret = sscanf(g_ptr_array_index(argbuf, i),
4082 "%*d FETCH (UID %d)", &msgnum)) == 1)
4083 uidlist = g_slist_prepend(uidlist, GINT_TO_POINTER(msgnum));
4085 ptr_array_free_strings(argbuf);
4086 g_ptr_array_free(argbuf, TRUE);
4089 lastuid_old = item->lastuid;
4090 *msgnum_list = g_slist_copy(item->uid_list);
4091 nummsgs = g_slist_length(*msgnum_list);
4092 debug_print("Got %d uids from cache\n", g_slist_length(item->uid_list));
4094 for (elem = uidlist; elem != NULL; elem = g_slist_next(elem)) {
4097 msgnum = GPOINTER_TO_INT(elem->data);
4098 if (msgnum > lastuid_old) {
4099 *msgnum_list = g_slist_prepend(*msgnum_list, GINT_TO_POINTER(msgnum));
4100 item->uid_list = g_slist_prepend(item->uid_list, GINT_TO_POINTER(msgnum));
4103 if(msgnum > item->lastuid)
4104 item->lastuid = msgnum;
4107 g_slist_free(uidlist);
4110 return GINT_TO_POINTER(nummsgs);
4113 static gint get_list_of_uids(Folder *folder, IMAPFolderItem *item, GSList **msgnum_list)
4116 get_list_uid_data *data = g_new0(get_list_uid_data, 1);
4122 data->folder = folder;
4124 data->msgnum_list = msgnum_list;
4126 if (prefs_common.work_offline && !imap_gtk_should_override()) {
4131 #if (defined USE_PTHREAD && defined __GLIBC__ && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 3)))
4132 if (pthread_create(&pt, PTHREAD_CREATE_JOINABLE,
4133 get_list_of_uids_thread, data) != 0) {
4134 result = GPOINTER_TO_INT(get_list_of_uids_thread(data));
4138 debug_print("+++waiting for get_list_of_uids_thread...\n");
4139 while(!data->done) {
4140 /* don't let the interface freeze while waiting */
4143 debug_print("---get_list_of_uids_thread done\n");
4145 /* get the thread's return value and clean its resources */
4146 pthread_join(pt, &tmp);
4147 result = GPOINTER_TO_INT(tmp);
4149 result = GPOINTER_TO_INT(get_list_of_uids_thread(data));
4156 gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list, gboolean *old_uids_valid)
4158 IMAPFolderItem *item = (IMAPFolderItem *)_item;
4159 IMAPSession *session;
4160 gint ok, nummsgs = 0, exists, recent, uid_val, uid_next, unseen;
4161 GSList *uidlist = NULL;
4163 gboolean selected_folder;
4165 g_return_val_if_fail(folder != NULL, -1);
4166 g_return_val_if_fail(item != NULL, -1);
4167 g_return_val_if_fail(item->item.path != NULL, -1);
4168 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
4169 g_return_val_if_fail(folder->account != NULL, -1);
4171 session = imap_session_get(folder);
4172 g_return_val_if_fail(session != NULL, -1);
4174 selected_folder = (session->mbox != NULL) &&
4175 (!strcmp(session->mbox, item->item.path));
4176 if (selected_folder) {
4177 ok = imap_cmd_noop(session);
4178 if (ok != IMAP_SUCCESS)
4180 exists = session->exists;
4182 *old_uids_valid = TRUE;
4184 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
4185 &exists, &recent, &uid_next, &uid_val, &unseen, FALSE);
4186 if (ok != IMAP_SUCCESS)
4189 if(item->item.mtime == uid_val)
4190 *old_uids_valid = TRUE;
4192 *old_uids_valid = FALSE;
4194 debug_print("Freeing imap uid cache\n");
4196 g_slist_free(item->uid_list);
4197 item->uid_list = NULL;
4199 item->item.mtime = uid_val;
4201 imap_delete_all_cached_messages((FolderItem *)item);
4205 if (!selected_folder)
4206 item->uid_next = uid_next;
4208 /* If old uid_next matches new uid_next we can be sure no message
4209 was added to the folder */
4210 if (( selected_folder && !session->folder_content_changed) ||
4211 (!selected_folder && uid_next == item->uid_next)) {
4212 nummsgs = g_slist_length(item->uid_list);
4214 /* If number of messages is still the same we
4215 know our caches message numbers are still valid,
4216 otherwise if the number of messages has decrease
4217 we discard our cache to start a new scan to find
4218 out which numbers have been removed */
4219 if (exists == nummsgs) {
4220 *msgnum_list = g_slist_copy(item->uid_list);
4222 } else if (exists < nummsgs) {
4223 debug_print("Freeing imap uid cache");
4225 g_slist_free(item->uid_list);
4226 item->uid_list = NULL;
4231 *msgnum_list = NULL;
4235 nummsgs = get_list_of_uids(folder, item, &uidlist);
4237 if (nummsgs != exists) {
4238 /* Cache contains more messages then folder, we have cached
4239 an old UID of a message that was removed and new messages
4240 have been added too, otherwise the uid_next check would
4242 debug_print("Freeing imap uid cache");
4244 g_slist_free(item->uid_list);
4245 item->uid_list = NULL;
4247 g_slist_free(*msgnum_list);
4249 nummsgs = get_list_of_uids(folder, item, &uidlist);
4252 *msgnum_list = uidlist;
4254 dir = folder_item_get_path((FolderItem *)item);
4255 debug_print("removing old messages from %s\n", dir);
4256 remove_numbered_files_not_in_list(dir, *msgnum_list);
4262 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item)
4267 flags.perm_flags = MSG_NEW|MSG_UNREAD;
4268 flags.tmp_flags = 0;
4270 g_return_val_if_fail(item != NULL, NULL);
4271 g_return_val_if_fail(file != NULL, NULL);
4273 if (item->stype == F_QUEUE) {
4274 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
4275 } else if (item->stype == F_DRAFT) {
4276 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
4279 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
4280 if (!msginfo) return NULL;
4282 msginfo->folder = item;
4287 GSList *imap_get_msginfos(Folder *folder, FolderItem *item, GSList *msgnum_list)
4289 IMAPSession *session;
4290 MsgInfoList *ret = NULL;
4293 g_return_val_if_fail(folder != NULL, NULL);
4294 g_return_val_if_fail(item != NULL, NULL);
4295 g_return_val_if_fail(msgnum_list != NULL, NULL);
4297 session = imap_session_get(folder);
4298 g_return_val_if_fail(session != NULL, NULL);
4300 debug_print("-> getting msginfos\n");
4301 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
4302 NULL, NULL, NULL, NULL, FALSE);
4303 if (ok != IMAP_SUCCESS)
4306 if (!(item->stype == F_QUEUE || item->stype == F_DRAFT)) {
4307 ret = g_slist_concat(ret,
4308 imap_get_uncached_messages(
4309 session, item, msgnum_list));
4311 MsgNumberList *sorted_list, *elem;
4312 gint startnum, lastnum;
4314 sorted_list = g_slist_sort(g_slist_copy(msgnum_list), g_int_compare);
4316 startnum = lastnum = GPOINTER_TO_INT(sorted_list->data);
4318 for (elem = sorted_list;; elem = g_slist_next(elem)) {
4322 num = GPOINTER_TO_INT(elem->data);
4324 if (num > lastnum + 1 || elem == NULL) {
4326 for (i = startnum; i <= lastnum; ++i) {
4329 file = imap_fetch_msg(folder, item, i);
4331 MsgInfo *msginfo = imap_parse_msg(file, item);
4332 if (msginfo != NULL) {
4333 msginfo->msgnum = i;
4334 ret = g_slist_append(ret, msginfo);
4348 g_slist_free(sorted_list);
4354 MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid)
4356 MsgInfo *msginfo = NULL;
4357 MsgInfoList *msginfolist;
4358 MsgNumberList numlist;
4360 numlist.next = NULL;
4361 numlist.data = GINT_TO_POINTER(uid);
4363 msginfolist = imap_get_msginfos(folder, item, &numlist);
4364 if (msginfolist != NULL) {
4365 msginfo = msginfolist->data;
4366 g_slist_free(msginfolist);
4372 gboolean imap_scan_required(Folder *folder, FolderItem *_item)
4374 IMAPSession *session;
4375 IMAPFolderItem *item = (IMAPFolderItem *)_item;
4376 gint ok, exists = 0, recent = 0, unseen = 0;
4377 guint32 uid_next, uid_val = 0;
4378 gboolean selected_folder;
4380 g_return_val_if_fail(folder != NULL, FALSE);
4381 g_return_val_if_fail(item != NULL, FALSE);
4382 g_return_val_if_fail(item->item.folder != NULL, FALSE);
4383 g_return_val_if_fail(FOLDER_CLASS(item->item.folder) == &imap_class, FALSE);
4385 if (item->item.path == NULL)
4388 session = imap_session_get(folder);
4389 g_return_val_if_fail(session != NULL, FALSE);
4391 selected_folder = (session->mbox != NULL) &&
4392 (!strcmp(session->mbox, item->item.path));
4393 if (selected_folder) {
4394 ok = imap_cmd_noop(session);
4395 if (ok != IMAP_SUCCESS)
4398 if (session->folder_content_changed)
4401 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
4402 &exists, &recent, &uid_next, &uid_val, &unseen, FALSE);
4403 if (ok != IMAP_SUCCESS)
4406 if ((uid_next != item->uid_next) || (exists != item->item.total_msgs))
4413 void imap_change_flags(Folder *folder, FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
4415 IMAPSession *session;
4416 IMAPFlags flags_set = 0, flags_unset = 0;
4417 gint ok = IMAP_SUCCESS;
4418 MsgNumberList numlist;
4420 g_return_if_fail(folder != NULL);
4421 g_return_if_fail(folder->klass == &imap_class);
4422 g_return_if_fail(item != NULL);
4423 g_return_if_fail(item->folder == folder);
4424 g_return_if_fail(msginfo != NULL);
4425 g_return_if_fail(msginfo->folder == item);
4427 session = imap_session_get(folder);
4428 if (!session) return;
4430 debug_print("-> changing flags\n");
4432 if ((ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
4433 NULL, NULL, NULL, NULL, FALSE)) != IMAP_SUCCESS)
4436 if (!MSG_IS_MARKED(msginfo->flags) && (newflags & MSG_MARKED))
4437 flags_set |= IMAP_FLAG_FLAGGED;
4438 if ( MSG_IS_MARKED(msginfo->flags) && !(newflags & MSG_MARKED))
4439 flags_unset |= IMAP_FLAG_FLAGGED;
4441 if (!MSG_IS_UNREAD(msginfo->flags) && (newflags & MSG_UNREAD))
4442 flags_unset |= IMAP_FLAG_SEEN;
4443 if ( MSG_IS_UNREAD(msginfo->flags) && !(newflags & MSG_UNREAD))
4444 flags_set |= IMAP_FLAG_SEEN;
4446 if (!MSG_IS_REPLIED(msginfo->flags) && (newflags & MSG_REPLIED))
4447 flags_set |= IMAP_FLAG_ANSWERED;
4448 if ( MSG_IS_REPLIED(msginfo->flags) && !(newflags & MSG_REPLIED))
4449 flags_set |= IMAP_FLAG_ANSWERED;
4451 numlist.next = NULL;
4452 numlist.data = GINT_TO_POINTER(msginfo->msgnum);
4455 ok = imap_set_message_flags(session, &numlist, flags_set, TRUE);
4456 if (ok != IMAP_SUCCESS) return;
4460 ok = imap_set_message_flags(session, &numlist, flags_unset, FALSE);
4461 if (ok != IMAP_SUCCESS) return;
4463 msginfo->flags.perm_flags = newflags;
4468 static gint imap_remove_msg(Folder *folder, FolderItem *item, gint uid)
4471 IMAPSession *session;
4473 MsgNumberList numlist;
4475 g_return_val_if_fail(folder != NULL, -1);
4476 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
4477 g_return_val_if_fail(item != NULL, -1);
4479 session = imap_session_get(folder);
4480 if (!session) return -1;
4482 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
4483 NULL, NULL, NULL, NULL, FALSE);
4484 if (ok != IMAP_SUCCESS)
4487 numlist.next = NULL;
4488 numlist.data = GINT_TO_POINTER(uid);
4490 ok = imap_set_message_flags
4491 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
4492 &numlist, IMAP_FLAG_DELETED, TRUE);
4493 if (ok != IMAP_SUCCESS) {
4494 log_warning(_("can't set deleted flags: %d\n"), uid);
4498 if (!session->uidplus) {
4499 ok = imap_cmd_expunge(session, NULL);
4503 uidstr = g_strdup_printf("%u", uid);
4504 ok = imap_cmd_expunge(session, uidstr);
4507 if (ok != IMAP_SUCCESS) {
4508 log_warning(_("can't expunge\n"));
4512 IMAP_FOLDER_ITEM(item)->uid_list = g_slist_remove(
4513 IMAP_FOLDER_ITEM(item)->uid_list, numlist.data);
4514 dir = folder_item_get_path(item);
4515 if (is_dir_exist(dir))
4516 remove_numbered_files(dir, uid, uid);
4519 return IMAP_SUCCESS;
4522 static gint compare_msginfo(gconstpointer a, gconstpointer b)
4524 return ((MsgInfo *)a)->msgnum - ((MsgInfo *)b)->msgnum;
4527 static guint gslist_find_next_num(MsgNumberList **list, guint num)
4531 g_return_val_if_fail(list != NULL, -1);
4533 for (elem = *list; elem != NULL; elem = g_slist_next(elem))
4534 if (GPOINTER_TO_INT(elem->data) >= num)
4537 return elem != NULL ? GPOINTER_TO_INT(elem->data) : (gint)-1;
4541 * NEW and DELETED flags are not syncronized
4542 * - The NEW/RECENT flags in IMAP folders can not really be directly
4543 * modified by Sylpheed
4544 * - The DELETE/DELETED flag in IMAP and Sylpheed don't have the same
4545 * meaning, in IMAP it always removes the messages from the FolderItem
4546 * in Sylpheed it can mean to move the message to trash
4549 typedef struct _get_flags_data {
4552 MsgInfoList *msginfo_list;
4553 GRelation *msgflags;
4557 static /*gint*/ void *imap_get_flags_thread(void *data)
4559 get_flags_data *stuff = (get_flags_data *)data;
4560 Folder *folder = stuff->folder;
4561 FolderItem *item = stuff->item;
4562 MsgInfoList *msginfo_list = stuff->msginfo_list;
4563 GRelation *msgflags = stuff->msgflags;
4564 IMAPSession *session;
4565 GSList *sorted_list;
4566 GSList *unseen = NULL, *answered = NULL, *flagged = NULL;
4567 GSList *p_unseen, *p_answered, *p_flagged;
4569 GSList *seq_list, *cur;
4570 gboolean reverse_seen = FALSE;
4573 gint exists_cnt, recent_cnt, unseen_cnt, uid_next;
4574 guint32 uidvalidity;
4575 gboolean selected_folder;
4577 if (folder == NULL || item == NULL) {
4579 return GINT_TO_POINTER(-1);
4581 if (msginfo_list == NULL) {
4583 return GINT_TO_POINTER(0);
4586 session = imap_session_get(folder);
4587 if (session == NULL) {
4589 return GINT_TO_POINTER(-1);
4592 selected_folder = (session->mbox != NULL) &&
4593 (!strcmp(session->mbox, item->path));
4595 if (!selected_folder) {
4596 ok = imap_status(session, IMAP_FOLDER(folder), item->path,
4597 &exists_cnt, &recent_cnt, &uid_next, &uidvalidity, &unseen_cnt, TRUE);
4598 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
4599 NULL, NULL, NULL, NULL, TRUE);
4600 if (ok != IMAP_SUCCESS) {
4602 return GINT_TO_POINTER(-1);
4607 if (unseen_cnt > exists_cnt / 2)
4608 reverse_seen = TRUE;
4610 cmd_buf = g_string_new(NULL);
4612 sorted_list = g_slist_sort(g_slist_copy(msginfo_list), compare_msginfo);
4614 seq_list = imap_get_seq_set_from_msglist(msginfo_list);
4616 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
4617 IMAPSet imapset = cur->data;
4619 g_string_sprintf(cmd_buf, "%sSEEN UID %s", reverse_seen ? "" : "UN", imapset);
4620 imap_cmd_search(session, cmd_buf->str, &p_unseen, TRUE);
4621 unseen = g_slist_concat(unseen, p_unseen);
4623 g_string_sprintf(cmd_buf, "ANSWERED UID %s", imapset);
4624 imap_cmd_search(session, cmd_buf->str, &p_answered, TRUE);
4625 answered = g_slist_concat(answered, p_answered);
4627 g_string_sprintf(cmd_buf, "FLAGGED UID %s", imapset);
4628 imap_cmd_search(session, cmd_buf->str, &p_flagged, TRUE);
4629 flagged = g_slist_concat(flagged, p_flagged);
4633 p_answered = answered;
4634 p_flagged = flagged;
4636 for (elem = sorted_list; elem != NULL; elem = g_slist_next(elem)) {
4641 msginfo = (MsgInfo *) elem->data;
4642 flags = msginfo->flags.perm_flags;
4643 wasnew = (flags & MSG_NEW);
4644 flags &= ~((reverse_seen ? 0 : MSG_UNREAD | MSG_NEW) | MSG_REPLIED | MSG_MARKED);
4646 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
4647 if (gslist_find_next_num(&p_unseen, msginfo->msgnum) == msginfo->msgnum) {
4648 if (!reverse_seen) {
4649 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
4651 flags &= ~(MSG_UNREAD | MSG_NEW);
4654 if (gslist_find_next_num(&p_answered, msginfo->msgnum) == msginfo->msgnum)
4655 flags |= MSG_REPLIED;
4656 if (gslist_find_next_num(&p_flagged, msginfo->msgnum) == msginfo->msgnum)
4657 flags |= MSG_MARKED;
4658 g_relation_insert(msgflags, msginfo, GINT_TO_POINTER(flags));
4661 imap_seq_set_free(seq_list);
4662 g_slist_free(flagged);
4663 g_slist_free(answered);
4664 g_slist_free(unseen);
4665 g_slist_free(sorted_list);
4666 g_string_free(cmd_buf, TRUE);
4669 return GINT_TO_POINTER(0);
4672 static gint imap_get_flags(Folder *folder, FolderItem *item,
4673 MsgInfoList *msginfo_list, GRelation *msgflags)
4676 get_flags_data *data = g_new0(get_flags_data, 1);
4682 data->folder = folder;
4684 data->msginfo_list = msginfo_list;
4685 data->msgflags = msgflags;
4687 if (prefs_common.work_offline && !imap_gtk_should_override()) {
4692 #if (defined USE_PTHREAD && defined __GLIBC__ && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 3)))
4693 if (pthread_create(&pt, PTHREAD_CREATE_JOINABLE,
4694 imap_get_flags_thread, data) != 0) {
4695 result = GPOINTER_TO_INT(imap_get_flags_thread(data));
4699 debug_print("+++waiting for imap_get_flags_thread...\n");
4700 while(!data->done) {
4701 /* don't let the interface freeze while waiting */
4704 debug_print("---imap_get_flags_thread done\n");
4706 /* get the thread's return value and clean its resources */
4707 pthread_join(pt, &tmp);
4708 result = GPOINTER_TO_INT(tmp);
4710 result = GPOINTER_TO_INT(imap_get_flags_thread(data));