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_all_msg (Folder *folder,
210 static gboolean imap_is_msg_changed (Folder *folder,
214 static gint imap_close (Folder *folder,
217 static gint imap_scan_tree (Folder *folder);
219 static gint imap_create_tree (Folder *folder);
221 static FolderItem *imap_create_folder (Folder *folder,
224 static gint imap_rename_folder (Folder *folder,
227 static gint imap_remove_folder (Folder *folder,
230 static FolderItem *imap_folder_item_new (Folder *folder);
231 static void imap_folder_item_destroy (Folder *folder,
234 static IMAPSession *imap_session_get (Folder *folder);
236 static gint imap_greeting (IMAPSession *session);
237 static gint imap_auth (IMAPSession *session,
242 static gint imap_scan_tree_recursive (IMAPSession *session,
244 static GSList *imap_parse_list (IMAPFolder *folder,
245 IMAPSession *session,
246 const gchar *real_path,
249 static void imap_create_missing_folders (Folder *folder);
250 static FolderItem *imap_create_special_folder
252 SpecialFolderItemType stype,
255 static gint imap_do_copy_msgs (Folder *folder,
257 MsgInfoList *msglist,
258 GRelation *relation);
260 static void imap_delete_all_cached_messages (FolderItem *item);
263 static SockInfo *imap_open (const gchar *server,
267 static SockInfo *imap_open (const gchar *server,
272 static SockInfo *imap_open_tunnel(const gchar *server,
273 const gchar *tunnelcmd,
276 static SockInfo *imap_open_tunnel(const gchar *server,
277 const gchar *tunnelcmd);
281 static SockInfo *imap_init_sock(SockInfo *sock, SSLType ssl_type);
283 static SockInfo *imap_init_sock(SockInfo *sock);
286 static gchar *imap_get_flag_str (IMAPFlags flags);
287 static gint imap_set_message_flags (IMAPSession *session,
288 MsgNumberList *numlist,
291 static gint imap_select (IMAPSession *session,
297 guint32 *uid_validity,
299 static gint imap_status (IMAPSession *session,
305 guint32 *uid_validity,
309 static void imap_parse_namespace (IMAPSession *session,
311 static void imap_get_namespace_by_list (IMAPSession *session,
313 static IMAPNameSpace *imap_find_namespace (IMAPFolder *folder,
315 static gchar imap_get_path_separator (IMAPFolder *folder,
317 static gchar *imap_get_real_path (IMAPFolder *folder,
320 static gchar *imap_parse_atom (SockInfo *sock,
325 static MsgFlags imap_parse_flags (const gchar *flag_str);
326 static MsgInfo *imap_parse_envelope (SockInfo *sock,
330 static gboolean imap_has_capability (IMAPSession *session,
332 static void imap_free_capabilities (IMAPSession *session);
334 /* low-level IMAP4rev1 commands */
335 static gint imap_cmd_authenticate
336 (IMAPSession *session,
340 static gint imap_cmd_login (IMAPSession *session,
343 static gint imap_cmd_logout (IMAPSession *session);
344 static gint imap_cmd_noop (IMAPSession *session);
346 static gint imap_cmd_starttls (IMAPSession *session);
348 static gint imap_cmd_namespace (IMAPSession *session,
350 static gint imap_cmd_list (IMAPSession *session,
352 const gchar *mailbox,
354 static gint imap_cmd_do_select (IMAPSession *session,
360 guint32 *uid_validity,
362 static gint imap_cmd_select (IMAPSession *session,
367 guint32 *uid_validity,
369 static gint imap_cmd_examine (IMAPSession *session,
374 guint32 *uid_validity,
376 static gint imap_cmd_create (IMAPSession *sock,
377 const gchar *folder);
378 static gint imap_cmd_rename (IMAPSession *sock,
379 const gchar *oldfolder,
380 const gchar *newfolder);
381 static gint imap_cmd_delete (IMAPSession *session,
382 const gchar *folder);
383 static gint imap_cmd_envelope (IMAPSession *session,
385 static gint imap_cmd_fetch (IMAPSession *sock,
387 const gchar *filename);
388 static gint imap_cmd_append (IMAPSession *session,
389 const gchar *destfolder,
393 static gint imap_cmd_copy (IMAPSession *session,
394 const gchar *seq_set,
395 const gchar *destfolder,
396 GRelation *uid_mapping);
397 static gint imap_cmd_store (IMAPSession *session,
400 static gint imap_cmd_expunge (IMAPSession *session,
402 static gint imap_cmd_close (IMAPSession *session);
404 static gint imap_cmd_ok (IMAPSession *session,
406 static gint imap_cmd_ok_block (IMAPSession *session,
408 static gint imap_cmd_ok_with_block
409 (IMAPSession *session,
412 static void imap_gen_send (IMAPSession *session,
413 const gchar *format, ...);
414 static gint imap_gen_recv (IMAPSession *session,
416 static gint imap_gen_recv_block (IMAPSession *session,
418 static gint imap_gen_recv_with_block
419 (IMAPSession *session,
423 /* misc utility functions */
424 static gchar *strchr_cpy (const gchar *src,
428 static gchar *get_quoted (const gchar *src,
432 static gchar *search_array_contain_str (GPtrArray *array,
434 static gchar *search_array_str (GPtrArray *array,
436 static void imap_path_separator_subst (gchar *str,
439 static gchar *imap_utf8_to_modified_utf7 (const gchar *from);
440 static gchar *imap_modified_utf7_to_utf8 (const gchar *mutf7_str);
442 static GSList *imap_get_seq_set_from_numlist (MsgNumberList *msglist);
443 static GSList *imap_get_seq_set_from_msglist (MsgInfoList *msglist);
444 static void imap_seq_set_free (GSList *seq_list);
446 static gboolean imap_rename_folder_func (GNode *node,
448 static gint imap_get_num_list (Folder *folder,
451 gboolean *old_uids_valid);
452 static GSList *imap_get_msginfos (Folder *folder,
454 GSList *msgnum_list);
455 static MsgInfo *imap_get_msginfo (Folder *folder,
458 static gboolean imap_scan_required (Folder *folder,
460 static void imap_change_flags (Folder *folder,
463 MsgPermFlags newflags);
464 static gint imap_get_flags (Folder *folder,
466 MsgInfoList *msglist,
467 GRelation *msgflags);
468 static gchar *imap_folder_get_path (Folder *folder);
469 static gchar *imap_item_get_path (Folder *folder,
472 static FolderClass imap_class;
474 typedef struct _thread_data {
485 void *imap_getline_thread(void *data)
487 thread_data *td = (thread_data *)data;
490 line = sock_getline(td->sock);
498 /* imap_getline just wraps sock_getline inside a thread,
499 * performing gtk updates so that the interface isn't frozen.
501 static gchar *imap_getline(SockInfo *sock)
503 #if (defined USE_PTHREAD && defined __GLIBC__ && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 3)))
504 thread_data *td = g_new0(thread_data, 1);
510 if (pthread_create(&pt, PTHREAD_CREATE_JOINABLE,
511 imap_getline_thread, td) != 0) {
513 return sock_getline(sock);
516 debug_print("+++waiting for imap_getline_thread...\n");
518 /* don't let the interface freeze while waiting */
521 debug_print("---imap_getline_thread done\n");
523 /* get the thread's return value and clean its resources */
524 pthread_join(pt, (void *)&line);
529 return sock_getline(sock);
533 FolderClass *imap_get_class(void)
535 if (imap_class.idstr == NULL) {
536 imap_class.type = F_IMAP;
537 imap_class.idstr = "imap";
538 imap_class.uistr = "IMAP4";
540 /* Folder functions */
541 imap_class.new_folder = imap_folder_new;
542 imap_class.destroy_folder = imap_folder_destroy;
543 imap_class.scan_tree = imap_scan_tree;
544 imap_class.create_tree = imap_create_tree;
546 /* FolderItem functions */
547 imap_class.item_new = imap_folder_item_new;
548 imap_class.item_destroy = imap_folder_item_destroy;
549 imap_class.item_get_path = imap_item_get_path;
550 imap_class.create_folder = imap_create_folder;
551 imap_class.rename_folder = imap_rename_folder;
552 imap_class.remove_folder = imap_remove_folder;
553 imap_class.close = imap_close;
554 imap_class.get_num_list = imap_get_num_list;
555 imap_class.scan_required = imap_scan_required;
557 /* Message functions */
558 imap_class.get_msginfo = imap_get_msginfo;
559 imap_class.get_msginfos = imap_get_msginfos;
560 imap_class.fetch_msg = imap_fetch_msg;
561 imap_class.add_msg = imap_add_msg;
562 imap_class.add_msgs = imap_add_msgs;
563 imap_class.copy_msg = imap_copy_msg;
564 imap_class.copy_msgs = imap_copy_msgs;
565 imap_class.remove_msg = imap_remove_msg;
566 imap_class.remove_all_msg = imap_remove_all_msg;
567 imap_class.is_msg_changed = imap_is_msg_changed;
568 imap_class.change_flags = imap_change_flags;
569 imap_class.get_flags = imap_get_flags;
575 static gchar *get_seq_set_from_seq_list(GSList *seq_list)
581 for (cur = seq_list; cur != NULL; cur = cur->next) {
582 tmp = val?g_strdup(val):NULL;
584 val = g_strconcat(tmp?tmp:"", tmp?",":"",(gchar *)cur->data,
592 static Folder *imap_folder_new(const gchar *name, const gchar *path)
596 folder = (Folder *)g_new0(IMAPFolder, 1);
597 folder->klass = &imap_class;
598 imap_folder_init(folder, name, path);
603 static void imap_folder_destroy(Folder *folder)
607 dir = imap_folder_get_path(folder);
608 if (is_dir_exist(dir))
609 remove_dir_recursive(dir);
612 folder_remote_folder_destroy(REMOTE_FOLDER(folder));
615 static void imap_folder_init(Folder *folder, const gchar *name,
618 folder_remote_folder_init((Folder *)folder, name, path);
621 static FolderItem *imap_folder_item_new(Folder *folder)
623 IMAPFolderItem *item;
625 item = g_new0(IMAPFolderItem, 1);
628 item->uid_list = NULL;
630 return (FolderItem *)item;
633 static void imap_folder_item_destroy(Folder *folder, FolderItem *_item)
635 IMAPFolderItem *item = (IMAPFolderItem *)_item;
637 g_return_if_fail(item != NULL);
638 g_slist_free(item->uid_list);
643 static gboolean imap_reset_uid_lists_func(GNode *node, gpointer data)
645 IMAPFolderItem *item = (IMAPFolderItem *)node->data;
649 g_slist_free(item->uid_list);
650 item->uid_list = NULL;
655 static void imap_reset_uid_lists(Folder *folder)
657 if(folder->node == NULL)
660 /* Destroy all uid lists and rest last uid */
661 g_node_traverse(folder->node, G_IN_ORDER, G_TRAVERSE_ALL, -1, imap_reset_uid_lists_func, NULL);
664 /* Send CAPABILITY, and examine the server's response to see whether this
665 * connection is pre-authenticated or not and build a list of CAPABILITIES. */
666 static gint imap_greeting(IMAPSession *session)
671 imap_gen_send(session, "CAPABILITY");
673 argbuf = g_ptr_array_new();
675 if (imap_cmd_ok(session, argbuf) != IMAP_SUCCESS ||
676 ((capstr = search_array_str(argbuf, "CAPABILITY ")) == NULL)) {
677 ptr_array_free_strings(argbuf);
678 g_ptr_array_free(argbuf, TRUE);
682 session->authenticated = search_array_str(argbuf, "PREAUTH") != NULL;
684 capstr += strlen("CAPABILITY ");
686 IMAP_SESSION(session)->capability = g_strsplit(capstr, " ", 0);
688 ptr_array_free_strings(argbuf);
689 g_ptr_array_free(argbuf, TRUE);
691 if (imap_has_capability(session, "UIDPLUS"))
692 session->uidplus = TRUE;
697 static gint imap_auth(IMAPSession *session, const gchar *user, const gchar *pass,
702 if (type == 0 || type == IMAP_AUTH_LOGIN)
703 ok = imap_cmd_login(session, user, pass);
705 ok = imap_cmd_authenticate(session, user, pass, type);
707 if (ok == IMAP_SUCCESS)
708 session->authenticated = TRUE;
713 static IMAPSession *imap_session_get(Folder *folder)
715 RemoteFolder *rfolder = REMOTE_FOLDER(folder);
716 IMAPSession *session = NULL;
718 g_return_val_if_fail(folder != NULL, NULL);
719 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, NULL);
720 g_return_val_if_fail(folder->account != NULL, NULL);
722 if (prefs_common.work_offline && !imap_gtk_should_override()) {
726 /* Make sure we have a session */
727 if (rfolder->session != NULL) {
728 session = IMAP_SESSION(rfolder->session);
730 imap_reset_uid_lists(folder);
731 session = imap_session_new(folder->account);
736 if (SESSION(session)->sock->state == CONN_DISCONNECTED) {
737 debug_print("IMAP server disconnected\n");
738 session_destroy(SESSION(session));
739 imap_reset_uid_lists(folder);
740 session = imap_session_new(folder->account);
743 /* Make sure session is authenticated */
744 if (!IMAP_SESSION(session)->authenticated)
745 imap_session_authenticate(IMAP_SESSION(session), folder->account);
746 if (!IMAP_SESSION(session)->authenticated) {
747 session_destroy(SESSION(session));
748 rfolder->session = NULL;
752 /* Make sure we have parsed the IMAP namespace */
753 imap_parse_namespace(IMAP_SESSION(session),
754 IMAP_FOLDER(folder));
756 /* I think the point of this code is to avoid sending a
757 * keepalive if we've used the session recently and therefore
758 * think it's still alive. Unfortunately, most of the code
759 * does not yet check for errors on the socket, and so if the
760 * connection drops we don't notice until the timeout expires.
761 * A better solution than sending a NOOP every time would be
762 * for every command to be prepared to retry until it is
763 * successfully sent. -- mbp */
764 if (time(NULL) - SESSION(session)->last_access_time > SESSION_TIMEOUT_INTERVAL) {
765 /* verify that the session is still alive */
766 if (imap_cmd_noop(session) != IMAP_SUCCESS) {
767 /* Check if this is the first try to establish a
768 connection, if yes we don't try to reconnect */
769 if (rfolder->session == NULL) {
770 log_warning(_("Connecting to %s failed"),
771 folder->account->recv_server);
772 session_destroy(SESSION(session));
775 log_warning(_("IMAP4 connection to %s has been"
776 " disconnected. Reconnecting...\n"),
777 folder->account->recv_server);
778 statusbar_print_all(_("IMAP4 connection to %s has been"
779 " disconnected. Reconnecting...\n"),
780 folder->account->recv_server);
781 session_destroy(SESSION(session));
782 /* Clear folders session to make imap_session_get create
783 a new session, because of rfolder->session == NULL
784 it will not try to reconnect again and so avoid an
786 rfolder->session = NULL;
787 session = imap_session_get(folder);
793 rfolder->session = SESSION(session);
795 return IMAP_SESSION(session);
798 static IMAPSession *imap_session_new(const PrefsAccount *account)
800 IMAPSession *session;
805 /* FIXME: IMAP over SSL only... */
808 port = account->set_imapport ? account->imapport
809 : account->ssl_imap == SSL_TUNNEL ? IMAPS_PORT : IMAP4_PORT;
810 ssl_type = account->ssl_imap;
812 port = account->set_imapport ? account->imapport
816 if (account->set_tunnelcmd) {
817 log_message(_("creating tunneled IMAP4 connection\n"));
819 if ((imap_sock = imap_open_tunnel(account->recv_server,
823 if ((imap_sock = imap_open_tunnel(account->recv_server,
824 account->tunnelcmd)) == NULL)
828 g_return_val_if_fail(account->recv_server != NULL, NULL);
830 log_message(_("creating IMAP4 connection to %s:%d ...\n"),
831 account->recv_server, port);
834 if ((imap_sock = imap_open(account->recv_server, port,
837 if ((imap_sock = imap_open(account->recv_server, port)) == NULL)
842 session = g_new0(IMAPSession, 1);
843 session_init(SESSION(session));
844 SESSION(session)->type = SESSION_IMAP;
845 SESSION(session)->server = g_strdup(account->recv_server);
846 SESSION(session)->sock = imap_sock;
848 SESSION(session)->destroy = imap_session_destroy;
850 session->capability = NULL;
852 session->authenticated = FALSE;
853 session->mbox = NULL;
854 session->cmd_count = 0;
856 /* Only need to log in if the connection was not PREAUTH */
857 if (imap_greeting(session) != IMAP_SUCCESS) {
858 session_destroy(SESSION(session));
863 if (account->ssl_imap == SSL_STARTTLS &&
864 imap_has_capability(session, "STARTTLS")) {
867 ok = imap_cmd_starttls(session);
868 if (ok != IMAP_SUCCESS) {
869 log_warning(_("Can't start TLS session.\n"));
870 session_destroy(SESSION(session));
873 if (!ssl_init_socket_with_method(SESSION(session)->sock,
875 session_destroy(SESSION(session));
879 imap_free_capabilities(session);
880 session->authenticated = FALSE;
881 session->uidplus = FALSE;
882 session->cmd_count = 1;
884 if (imap_greeting(session) != IMAP_SUCCESS) {
885 session_destroy(SESSION(session));
890 log_message("IMAP connection is %s-authenticated\n",
891 (session->authenticated) ? "pre" : "un");
896 static void imap_session_authenticate(IMAPSession *session,
897 const PrefsAccount *account)
901 g_return_if_fail(account->userid != NULL);
903 pass = account->passwd;
906 tmp_pass = input_dialog_query_password(account->recv_server, account->userid);
909 Xstrdup_a(pass, tmp_pass, {g_free(tmp_pass); return;});
912 statusbar_print_all(_("Connecting to IMAP4 server %s...\n"),
913 account->recv_server);
914 if (imap_auth(session, account->userid, pass, account->imap_auth_type) != IMAP_SUCCESS) {
915 imap_cmd_logout(session);
917 alertpanel_error(_("Can't authenticate on IMAP4 server: %s"),
918 account->recv_server);
923 session->authenticated = TRUE;
926 static void imap_session_destroy(Session *session)
928 imap_free_capabilities(IMAP_SESSION(session));
929 g_free(IMAP_SESSION(session)->mbox);
930 sock_close(session->sock);
931 session->sock = NULL;
934 static gchar *imap_fetch_msg(Folder *folder, FolderItem *item, gint uid)
936 gchar *path, *filename;
937 IMAPSession *session;
940 g_return_val_if_fail(folder != NULL, NULL);
941 g_return_val_if_fail(item != NULL, NULL);
943 path = folder_item_get_path(item);
944 if (!is_dir_exist(path))
946 filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(uid), NULL);
949 if (is_file_exist(filename)) {
950 debug_print("message %d has been already cached.\n", uid);
954 session = imap_session_get(folder);
960 debug_print("-> fetching messages\n");
961 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
962 NULL, NULL, NULL, NULL, FALSE);
963 if (ok != IMAP_SUCCESS) {
964 g_warning("can't select mailbox %s\n", item->path);
969 debug_print("getting message %d...\n", uid);
970 ok = imap_cmd_fetch(session, (guint32)uid, filename);
972 if (ok != IMAP_SUCCESS) {
973 g_warning("can't fetch message %d\n", uid);
981 static gint imap_add_msg(Folder *folder, FolderItem *dest,
982 const gchar *file, MsgFlags *flags)
986 MsgFileInfo fileinfo;
988 g_return_val_if_fail(file != NULL, -1);
990 fileinfo.msginfo = NULL;
991 fileinfo.file = (gchar *)file;
992 fileinfo.flags = flags;
993 file_list.data = &fileinfo;
994 file_list.next = NULL;
996 ret = imap_add_msgs(folder, dest, &file_list, NULL);
1000 static gint imap_add_msgs(Folder *folder, FolderItem *dest, GSList *file_list,
1001 GRelation *relation)
1004 IMAPSession *session;
1005 guint32 last_uid = 0;
1007 MsgFileInfo *fileinfo;
1010 g_return_val_if_fail(folder != NULL, -1);
1011 g_return_val_if_fail(dest != NULL, -1);
1012 g_return_val_if_fail(file_list != NULL, -1);
1014 session = imap_session_get(folder);
1015 if (!session) return -1;
1017 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
1019 for (cur = file_list; cur != NULL; cur = cur->next) {
1020 IMAPFlags iflags = 0;
1021 guint32 new_uid = 0;
1023 fileinfo = (MsgFileInfo *)cur->data;
1025 if (fileinfo->flags) {
1026 if (MSG_IS_MARKED(*fileinfo->flags))
1027 iflags |= IMAP_FLAG_FLAGGED;
1028 if (MSG_IS_REPLIED(*fileinfo->flags))
1029 iflags |= IMAP_FLAG_ANSWERED;
1030 if (!MSG_IS_UNREAD(*fileinfo->flags))
1031 iflags |= IMAP_FLAG_SEEN;
1034 if (dest->stype == F_OUTBOX ||
1035 dest->stype == F_QUEUE ||
1036 dest->stype == F_DRAFT ||
1037 dest->stype == F_TRASH)
1038 iflags |= IMAP_FLAG_SEEN;
1040 ok = imap_cmd_append(session, destdir, fileinfo->file, iflags,
1043 if (ok != IMAP_SUCCESS) {
1044 g_warning("can't append message %s\n", fileinfo->file);
1049 if (relation != NULL)
1050 g_relation_insert(relation, fileinfo->msginfo != NULL ?
1051 (gpointer) fileinfo->msginfo : (gpointer) fileinfo,
1052 GINT_TO_POINTER(dest->last_num + 1));
1053 if (last_uid < new_uid)
1062 static gint imap_do_copy_msgs(Folder *folder, FolderItem *dest,
1063 MsgInfoList *msglist, GRelation *relation)
1067 GSList *seq_list, *cur;
1069 IMAPSession *session;
1070 gint ok = IMAP_SUCCESS;
1071 GRelation *uid_mapping;
1074 g_return_val_if_fail(folder != NULL, -1);
1075 g_return_val_if_fail(dest != NULL, -1);
1076 g_return_val_if_fail(msglist != NULL, -1);
1078 session = imap_session_get(folder);
1079 if (!session) return -1;
1081 msginfo = (MsgInfo *)msglist->data;
1083 src = msginfo->folder;
1085 g_warning("the src folder is identical to the dest.\n");
1089 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
1090 NULL, NULL, NULL, NULL, FALSE);
1091 if (ok != IMAP_SUCCESS)
1094 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
1095 seq_list = imap_get_seq_set_from_msglist(msglist);
1096 uid_mapping = g_relation_new(2);
1097 g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
1099 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
1100 gchar *seq_set = (gchar *)cur->data;
1102 debug_print("Copying message %s%c[%s] to %s ...\n",
1103 src->path, G_DIR_SEPARATOR,
1106 ok = imap_cmd_copy(session, seq_set, destdir, uid_mapping);
1107 if (ok != IMAP_SUCCESS) {
1108 g_relation_destroy(uid_mapping);
1109 imap_seq_set_free(seq_list);
1114 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
1115 MsgInfo *msginfo = (MsgInfo *)cur->data;
1118 tuples = g_relation_select(uid_mapping,
1119 GINT_TO_POINTER(msginfo->msgnum),
1121 if (tuples->len > 0) {
1122 gint num = GPOINTER_TO_INT(g_tuples_index(tuples, 0, 1));
1123 g_relation_insert(relation, msginfo,
1124 GPOINTER_TO_INT(num));
1128 g_relation_insert(relation, msginfo,
1129 GPOINTER_TO_INT(0));
1130 g_tuples_destroy(tuples);
1133 g_relation_destroy(uid_mapping);
1134 imap_seq_set_free(seq_list);
1138 if (ok == IMAP_SUCCESS)
1144 static gint imap_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1148 g_return_val_if_fail(msginfo != NULL, -1);
1150 msglist.data = msginfo;
1151 msglist.next = NULL;
1153 return imap_copy_msgs(folder, dest, &msglist, NULL);
1156 static gint imap_copy_msgs(Folder *folder, FolderItem *dest,
1157 MsgInfoList *msglist, GRelation *relation)
1163 g_return_val_if_fail(folder != NULL, -1);
1164 g_return_val_if_fail(dest != NULL, -1);
1165 g_return_val_if_fail(msglist != NULL, -1);
1167 msginfo = (MsgInfo *)msglist->data;
1168 g_return_val_if_fail(msginfo->folder != NULL, -1);
1170 if (folder == msginfo->folder->folder)
1171 return imap_do_copy_msgs(folder, dest, msglist, relation);
1173 file_list = procmsg_get_message_file_list(msglist);
1174 g_return_val_if_fail(file_list != NULL, -1);
1176 ret = imap_add_msgs(folder, dest, file_list, relation);
1178 procmsg_message_file_list_free(file_list);
1183 static gint imap_remove_all_msg(Folder *folder, FolderItem *item)
1186 IMAPSession *session;
1189 g_return_val_if_fail(folder != NULL, -1);
1190 g_return_val_if_fail(item != NULL, -1);
1192 session = imap_session_get(folder);
1193 if (!session) return -1;
1195 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
1196 NULL, NULL, NULL, NULL, FALSE);
1197 if (ok != IMAP_SUCCESS)
1200 imap_gen_send(session, "STORE 1:* +FLAGS.SILENT (\\Deleted)");
1201 ok = imap_cmd_ok(session, NULL);
1202 if (ok != IMAP_SUCCESS) {
1203 log_warning(_("can't set deleted flags: 1:*\n"));
1207 ok = imap_cmd_expunge(session, NULL);
1208 if (ok != IMAP_SUCCESS) {
1209 log_warning(_("can't expunge\n"));
1213 dir = folder_item_get_path(item);
1214 if (is_dir_exist(dir))
1215 remove_all_numbered_files(dir);
1218 return IMAP_SUCCESS;
1221 static gboolean imap_is_msg_changed(Folder *folder, FolderItem *item,
1224 /* TODO: properly implement this method */
1228 static gint imap_close(Folder *folder, FolderItem *item)
1231 IMAPSession *session;
1233 g_return_val_if_fail(folder != NULL, -1);
1234 g_return_val_if_fail(item != NULL, -1);
1235 g_return_val_if_fail(item->path != NULL, -1);
1237 session = imap_session_get(folder);
1238 if (!session) return -1;
1240 if (session->mbox) {
1241 if (strcmp2(session->mbox, item->path) != 0) return -1;
1243 ok = imap_cmd_close(session);
1244 if (ok != IMAP_SUCCESS)
1245 log_warning(_("can't close folder\n"));
1247 g_free(session->mbox);
1249 session->mbox = NULL;
1257 static gint imap_scan_tree(Folder *folder)
1259 FolderItem *item = NULL;
1260 IMAPSession *session;
1261 gchar *root_folder = NULL;
1263 g_return_val_if_fail(folder != NULL, -1);
1264 g_return_val_if_fail(folder->account != NULL, -1);
1266 session = imap_session_get(folder);
1268 if (!folder->node) {
1269 folder_tree_destroy(folder);
1270 item = folder_item_new(folder, folder->name, NULL);
1271 item->folder = folder;
1272 folder->node = item->node = g_node_new(item);
1277 if (folder->account->imap_dir && *folder->account->imap_dir) {
1282 Xstrdup_a(root_folder, folder->account->imap_dir, return -1);
1283 extract_quote(root_folder, '"');
1284 subst_char(root_folder,
1285 imap_get_path_separator(IMAP_FOLDER(folder),
1288 strtailchomp(root_folder, '/');
1289 real_path = imap_get_real_path
1290 (IMAP_FOLDER(folder), root_folder);
1291 debug_print("IMAP root directory: %s\n", real_path);
1293 /* check if root directory exist */
1294 argbuf = g_ptr_array_new();
1295 ok = imap_cmd_list(session, NULL, real_path, argbuf);
1296 if (ok != IMAP_SUCCESS ||
1297 search_array_str(argbuf, "LIST ") == NULL) {
1298 log_warning(_("root folder %s does not exist\n"), real_path);
1299 g_ptr_array_free(argbuf, TRUE);
1302 if (!folder->node) {
1303 item = folder_item_new(folder, folder->name, NULL);
1304 item->folder = folder;
1305 folder->node = item->node = g_node_new(item);
1309 g_ptr_array_free(argbuf, TRUE);
1314 item = FOLDER_ITEM(folder->node->data);
1315 if (!item || ((item->path || root_folder) &&
1316 strcmp2(item->path, root_folder) != 0)) {
1317 folder_tree_destroy(folder);
1318 item = folder_item_new(folder, folder->name, root_folder);
1319 item->folder = folder;
1320 folder->node = item->node = g_node_new(item);
1323 imap_scan_tree_recursive(session, FOLDER_ITEM(folder->node->data));
1324 imap_create_missing_folders(folder);
1329 static gint imap_scan_tree_recursive(IMAPSession *session, FolderItem *item)
1332 IMAPFolder *imapfolder;
1333 FolderItem *new_item;
1334 GSList *item_list, *cur;
1337 gchar *wildcard_path;
1341 g_return_val_if_fail(item != NULL, -1);
1342 g_return_val_if_fail(item->folder != NULL, -1);
1343 g_return_val_if_fail(item->no_sub == FALSE, -1);
1345 folder = item->folder;
1346 imapfolder = IMAP_FOLDER(folder);
1348 separator = imap_get_path_separator(imapfolder, item->path);
1350 if (folder->ui_func)
1351 folder->ui_func(folder, item, folder->ui_func_data);
1354 wildcard[0] = separator;
1357 real_path = imap_get_real_path(imapfolder, item->path);
1361 real_path = g_strdup("");
1364 Xstrcat_a(wildcard_path, real_path, wildcard,
1365 {g_free(real_path); return IMAP_ERROR;});
1366 QUOTE_IF_REQUIRED(wildcard_path, wildcard_path);
1368 imap_gen_send(session, "LIST \"\" %s",
1371 strtailchomp(real_path, separator);
1372 item_list = imap_parse_list(imapfolder, session, real_path, NULL);
1375 node = item->node->children;
1376 while (node != NULL) {
1377 FolderItem *old_item = FOLDER_ITEM(node->data);
1378 GNode *next = node->next;
1381 for (cur = item_list; cur != NULL; cur = cur->next) {
1382 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1383 if (!strcmp2(old_item->path, cur_item->path)) {
1384 new_item = cur_item;
1389 debug_print("folder '%s' not found. removing...\n",
1391 folder_item_remove(old_item);
1393 old_item->no_sub = new_item->no_sub;
1394 old_item->no_select = new_item->no_select;
1395 if (old_item->no_sub == TRUE && node->children) {
1396 debug_print("folder '%s' doesn't have "
1397 "subfolders. removing...\n",
1399 folder_item_remove_children(old_item);
1406 for (cur = item_list; cur != NULL; cur = cur->next) {
1407 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1409 for (node = item->node->children; node != NULL;
1410 node = node->next) {
1411 if (!strcmp2(FOLDER_ITEM(node->data)->path,
1413 new_item = FOLDER_ITEM(node->data);
1414 folder_item_destroy(cur_item);
1420 new_item = cur_item;
1421 debug_print("new folder '%s' found.\n", new_item->path);
1422 folder_item_append(item, new_item);
1425 if (!strcmp(new_item->path, "INBOX")) {
1426 new_item->stype = F_INBOX;
1427 folder->inbox = new_item;
1428 } else if (!folder_item_parent(item) || item->stype == F_INBOX) {
1431 base = g_path_get_basename(new_item->path);
1433 if (!folder->outbox && !g_ascii_strcasecmp(base, "Sent")) {
1434 new_item->stype = F_OUTBOX;
1435 folder->outbox = new_item;
1436 } else if (!folder->draft && !g_ascii_strcasecmp(base, "Drafts")) {
1437 new_item->stype = F_DRAFT;
1438 folder->draft = new_item;
1439 } else if (!folder->queue && !g_ascii_strcasecmp(base, "Queue")) {
1440 new_item->stype = F_QUEUE;
1441 folder->queue = new_item;
1442 } else if (!folder->trash && !g_ascii_strcasecmp(base, "Trash")) {
1443 new_item->stype = F_TRASH;
1444 folder->trash = new_item;
1449 if (new_item->no_sub == FALSE)
1450 imap_scan_tree_recursive(session, new_item);
1453 g_slist_free(item_list);
1455 return IMAP_SUCCESS;
1458 static GSList *imap_parse_list(IMAPFolder *folder, IMAPSession *session,
1459 const gchar *real_path, gchar *separator)
1461 gchar buf[IMAPBUFSIZE];
1463 gchar separator_str[16];
1466 gchar *loc_name, *loc_path;
1467 GSList *item_list = NULL;
1469 FolderItem *new_item;
1471 debug_print("getting list of %s ...\n",
1472 *real_path ? real_path : "\"\"");
1474 str = g_string_new(NULL);
1477 if (sock_gets(SESSION(session)->sock, buf, sizeof(buf)) <= 0) {
1478 log_warning(_("error occurred while getting LIST.\n"));
1482 if (buf[0] != '*' || buf[1] != ' ') {
1483 log_print("IMAP4< %s\n", buf);
1484 if (sscanf(buf, "%*d %16s", buf) < 1 ||
1485 strcmp(buf, "OK") != 0)
1486 log_warning(_("error occurred while getting LIST.\n"));
1490 debug_print("IMAP4< %s\n", buf);
1492 g_string_assign(str, buf);
1494 if (strncmp(p, "LIST ", 5) != 0) continue;
1497 if (*p != '(') continue;
1499 p = strchr_cpy(p, ')', flags, sizeof(flags));
1501 while (*p == ' ') p++;
1503 p = strchr_cpy(p, ' ', separator_str, sizeof(separator_str));
1505 extract_quote(separator_str, '"');
1506 if (!strcmp(separator_str, "NIL"))
1507 separator_str[0] = '\0';
1509 *separator = separator_str[0];
1512 while (*p == ' ') p++;
1513 if (*p == '{' || *p == '"')
1514 p = imap_parse_atom(SESSION(session)->sock, p,
1515 buf, sizeof(buf), str);
1517 strncpy2(buf, p, sizeof(buf));
1518 strtailchomp(buf, separator_str[0]);
1519 if (buf[0] == '\0') continue;
1520 if (!strcmp(buf, real_path)) continue;
1522 if (separator_str[0] != '\0')
1523 subst_char(buf, separator_str[0], '/');
1524 base = g_path_get_basename(buf);
1525 if (base[0] == '.') continue;
1527 loc_name = imap_modified_utf7_to_utf8(base);
1528 loc_path = imap_modified_utf7_to_utf8(buf);
1529 new_item = folder_item_new(FOLDER(folder), loc_name, loc_path);
1530 if (strcasestr(flags, "\\Noinferiors") != NULL)
1531 new_item->no_sub = TRUE;
1532 if (strcmp(buf, "INBOX") != 0 &&
1533 strcasestr(flags, "\\Noselect") != NULL)
1534 new_item->no_select = TRUE;
1536 item_list = g_slist_append(item_list, new_item);
1538 debug_print("folder '%s' found.\n", loc_path);
1544 g_string_free(str, TRUE);
1549 static gint imap_create_tree(Folder *folder)
1551 g_return_val_if_fail(folder != NULL, -1);
1552 g_return_val_if_fail(folder->node != NULL, -1);
1553 g_return_val_if_fail(folder->node->data != NULL, -1);
1554 g_return_val_if_fail(folder->account != NULL, -1);
1556 imap_scan_tree(folder);
1557 imap_create_missing_folders(folder);
1562 static void imap_create_missing_folders(Folder *folder)
1564 g_return_if_fail(folder != NULL);
1567 folder->inbox = imap_create_special_folder
1568 (folder, F_INBOX, "INBOX");
1570 if (!folder->outbox)
1571 folder->outbox = imap_create_special_folder
1572 (folder, F_OUTBOX, "Sent");
1574 folder->draft = imap_create_special_folder
1575 (folder, F_DRAFT, "Drafts");
1577 folder->queue = imap_create_special_folder
1578 (folder, F_QUEUE, "Queue");
1581 folder->trash = imap_create_special_folder
1582 (folder, F_TRASH, "Trash");
1585 static FolderItem *imap_create_special_folder(Folder *folder,
1586 SpecialFolderItemType stype,
1590 FolderItem *new_item;
1592 g_return_val_if_fail(folder != NULL, NULL);
1593 g_return_val_if_fail(folder->node != NULL, NULL);
1594 g_return_val_if_fail(folder->node->data != NULL, NULL);
1595 g_return_val_if_fail(folder->account != NULL, NULL);
1596 g_return_val_if_fail(name != NULL, NULL);
1598 item = FOLDER_ITEM(folder->node->data);
1599 new_item = imap_create_folder(folder, item, name);
1602 g_warning("Can't create '%s'\n", name);
1603 if (!folder->inbox) return NULL;
1605 new_item = imap_create_folder(folder, folder->inbox, name);
1607 g_warning("Can't create '%s' under INBOX\n", name);
1609 new_item->stype = stype;
1611 new_item->stype = stype;
1616 static gchar *imap_folder_get_path(Folder *folder)
1620 g_return_val_if_fail(folder != NULL, NULL);
1621 g_return_val_if_fail(folder->account != NULL, NULL);
1623 folder_path = g_strconcat(get_imap_cache_dir(),
1625 folder->account->recv_server,
1627 folder->account->userid,
1633 static gchar *imap_item_get_path(Folder *folder, FolderItem *item)
1635 gchar *folder_path, *path;
1637 g_return_val_if_fail(folder != NULL, NULL);
1638 g_return_val_if_fail(item != NULL, NULL);
1639 folder_path = imap_folder_get_path(folder);
1641 g_return_val_if_fail(folder_path != NULL, NULL);
1642 if (folder_path[0] == G_DIR_SEPARATOR) {
1644 path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
1647 path = g_strdup(folder_path);
1650 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1651 folder_path, G_DIR_SEPARATOR_S,
1654 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1657 g_free(folder_path);
1662 static FolderItem *imap_create_folder(Folder *folder, FolderItem *parent,
1665 gchar *dirpath, *imap_path;
1666 IMAPSession *session;
1667 FolderItem *new_item;
1673 g_return_val_if_fail(folder != NULL, NULL);
1674 g_return_val_if_fail(folder->account != NULL, NULL);
1675 g_return_val_if_fail(parent != NULL, NULL);
1676 g_return_val_if_fail(name != NULL, NULL);
1678 session = imap_session_get(folder);
1679 if (!session) return NULL;
1681 if (!folder_item_parent(parent) && strcmp(name, "INBOX") == 0)
1682 dirpath = g_strdup(name);
1683 else if (parent->path)
1684 dirpath = g_strconcat(parent->path, "/", name, NULL);
1685 else if ((p = strchr(name, '/')) != NULL && *(p + 1) != '\0')
1686 dirpath = g_strdup(name);
1687 else if (folder->account->imap_dir && *folder->account->imap_dir) {
1690 Xstrdup_a(imap_dir, folder->account->imap_dir, return NULL);
1691 strtailchomp(imap_dir, '/');
1692 dirpath = g_strconcat(imap_dir, "/", name, NULL);
1694 dirpath = g_strdup(name);
1696 /* keep trailing directory separator to create a folder that contains
1698 imap_path = imap_utf8_to_modified_utf7(dirpath);
1699 strtailchomp(dirpath, '/');
1700 Xstrdup_a(new_name, name, {g_free(dirpath); return NULL;});
1701 strtailchomp(new_name, '/');
1702 separator = imap_get_path_separator(IMAP_FOLDER(folder), imap_path);
1703 imap_path_separator_subst(imap_path, separator);
1704 subst_char(new_name, '/', separator);
1706 if (strcmp(name, "INBOX") != 0) {
1709 gboolean exist = FALSE;
1711 argbuf = g_ptr_array_new();
1712 ok = imap_cmd_list(session, NULL, imap_path,
1714 if (ok != IMAP_SUCCESS) {
1715 log_warning(_("can't create mailbox: LIST failed\n"));
1718 ptr_array_free_strings(argbuf);
1719 g_ptr_array_free(argbuf, TRUE);
1723 for (i = 0; i < argbuf->len; i++) {
1725 str = g_ptr_array_index(argbuf, i);
1726 if (!strncmp(str, "LIST ", 5)) {
1731 ptr_array_free_strings(argbuf);
1732 g_ptr_array_free(argbuf, TRUE);
1735 ok = imap_cmd_create(session, imap_path);
1736 if (ok != IMAP_SUCCESS) {
1737 log_warning(_("can't create mailbox\n"));
1745 new_item = folder_item_new(folder, new_name, dirpath);
1746 folder_item_append(parent, new_item);
1750 dirpath = folder_item_get_path(new_item);
1751 if (!is_dir_exist(dirpath))
1752 make_dir_hier(dirpath);
1758 static gint imap_rename_folder(Folder *folder, FolderItem *item,
1763 gchar *real_oldpath;
1764 gchar *real_newpath;
1766 gchar *old_cache_dir;
1767 gchar *new_cache_dir;
1768 IMAPSession *session;
1771 gint exists, recent, unseen;
1772 guint32 uid_validity;
1774 g_return_val_if_fail(folder != NULL, -1);
1775 g_return_val_if_fail(item != NULL, -1);
1776 g_return_val_if_fail(item->path != NULL, -1);
1777 g_return_val_if_fail(name != NULL, -1);
1779 if (strchr(name, imap_get_path_separator(IMAP_FOLDER(folder), item->path)) != NULL) {
1780 g_warning(_("New folder name must not contain the namespace "
1785 session = imap_session_get(folder);
1786 if (!session) return -1;
1788 real_oldpath = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1790 g_free(session->mbox);
1791 session->mbox = NULL;
1792 ok = imap_cmd_examine(session, "INBOX",
1793 &exists, &recent, &unseen, &uid_validity, FALSE);
1794 if (ok != IMAP_SUCCESS) {
1795 g_free(real_oldpath);
1799 separator = imap_get_path_separator(IMAP_FOLDER(folder), item->path);
1800 if (strchr(item->path, G_DIR_SEPARATOR)) {
1801 dirpath = g_path_get_dirname(item->path);
1802 newpath = g_strconcat(dirpath, G_DIR_SEPARATOR_S, name, NULL);
1805 newpath = g_strdup(name);
1807 real_newpath = imap_utf8_to_modified_utf7(newpath);
1808 imap_path_separator_subst(real_newpath, separator);
1810 ok = imap_cmd_rename(session, real_oldpath, real_newpath);
1811 if (ok != IMAP_SUCCESS) {
1812 log_warning(_("can't rename mailbox: %s to %s\n"),
1813 real_oldpath, real_newpath);
1814 g_free(real_oldpath);
1816 g_free(real_newpath);
1821 item->name = g_strdup(name);
1823 old_cache_dir = folder_item_get_path(item);
1825 paths[0] = g_strdup(item->path);
1827 g_node_traverse(item->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1828 imap_rename_folder_func, paths);
1830 if (is_dir_exist(old_cache_dir)) {
1831 new_cache_dir = folder_item_get_path(item);
1832 if (rename(old_cache_dir, new_cache_dir) < 0) {
1833 FILE_OP_ERROR(old_cache_dir, "rename");
1835 g_free(new_cache_dir);
1838 g_free(old_cache_dir);
1841 g_free(real_oldpath);
1842 g_free(real_newpath);
1847 static gint imap_remove_folder(Folder *folder, FolderItem *item)
1850 IMAPSession *session;
1853 gint exists, recent, unseen;
1854 guint32 uid_validity;
1856 g_return_val_if_fail(folder != NULL, -1);
1857 g_return_val_if_fail(item != NULL, -1);
1858 g_return_val_if_fail(item->path != NULL, -1);
1860 session = imap_session_get(folder);
1861 if (!session) return -1;
1863 path = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1865 ok = imap_cmd_examine(session, "INBOX",
1866 &exists, &recent, &unseen, &uid_validity, FALSE);
1867 if (ok != IMAP_SUCCESS) {
1872 ok = imap_cmd_delete(session, path);
1873 if (ok != IMAP_SUCCESS) {
1874 log_warning(_("can't delete mailbox\n"));
1880 cache_dir = folder_item_get_path(item);
1881 if (is_dir_exist(cache_dir) && remove_dir_recursive(cache_dir) < 0)
1882 g_warning("can't remove directory '%s'\n", cache_dir);
1884 folder_item_remove(item);
1889 typedef struct _uncached_data {
1890 IMAPSession *session;
1892 MsgNumberList *numlist;
1896 static void *imap_get_uncached_messages_thread(void *data)
1898 uncached_data *stuff = (uncached_data *)data;
1899 IMAPSession *session = stuff->session;
1900 FolderItem *item = stuff->item;
1901 MsgNumberList *numlist = stuff->numlist;
1904 GSList *newlist = NULL;
1905 GSList *llast = NULL;
1908 GSList *seq_list, *cur;
1911 if (session == NULL || item == NULL || item->folder == NULL
1912 || FOLDER_CLASS(item->folder) != &imap_class) {
1917 seq_list = imap_get_seq_set_from_numlist(numlist);
1918 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
1919 imapset = cur->data;
1921 if (imap_cmd_envelope(session, imapset)
1923 log_warning(_("can't get envelope\n"));
1927 str = g_string_new(NULL);
1930 if ((tmp =sock_getline(SESSION(session)->sock)) == NULL) {
1931 log_warning(_("error occurred while getting envelope.\n"));
1932 g_string_free(str, TRUE);
1936 if (tmp[0] != '*' || tmp[1] != ' ') {
1937 log_print("IMAP4< %s\n", tmp);
1941 if (strstr(tmp, "FETCH") == NULL) {
1942 log_print("IMAP4< %s\n", tmp);
1946 log_print("IMAP4< %s\n", tmp);
1947 g_string_assign(str, tmp);
1950 msginfo = imap_parse_envelope
1951 (SESSION(session)->sock, item, str);
1953 log_warning(_("can't parse envelope: %s\n"), str->str);
1956 if (item->stype == F_QUEUE) {
1957 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_QUEUED);
1958 } else if (item->stype == F_DRAFT) {
1959 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_DRAFT);
1962 msginfo->folder = item;
1965 llast = newlist = g_slist_append(newlist, msginfo);
1967 llast = g_slist_append(llast, msginfo);
1968 llast = llast->next;
1972 g_string_free(str, TRUE);
1974 imap_seq_set_free(seq_list);
1976 session_set_access_time(SESSION(session));
1982 static GSList *imap_get_uncached_messages(IMAPSession *session,
1984 MsgNumberList *numlist)
1986 uncached_data *data = g_new0(uncached_data, 1);
1987 GSList *result = NULL;
1992 data->session = session;
1994 data->numlist = numlist;
1996 if (prefs_common.work_offline && !imap_gtk_should_override()) {
2001 #if (defined USE_PTHREAD && defined __GLIBC__ && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 3)))
2002 if (pthread_create(&pt, PTHREAD_CREATE_JOINABLE,
2003 imap_get_uncached_messages_thread, data) != 0) {
2004 result = (GSList *)imap_get_uncached_messages_thread(data);
2008 debug_print("+++waiting for imap_get_uncached_messages_thread...\n");
2009 while(!data->done) {
2010 /* don't let the interface freeze while waiting */
2013 debug_print("---imap_get_uncached_messages_thread done\n");
2015 /* get the thread's return value and clean its resources */
2016 pthread_join(pt, (void *)&result);
2018 result = (GSList *)imap_get_uncached_messages_thread(data);
2024 static void imap_delete_all_cached_messages(FolderItem *item)
2028 g_return_if_fail(item != NULL);
2029 g_return_if_fail(item->folder != NULL);
2030 g_return_if_fail(FOLDER_CLASS(item->folder) == &imap_class);
2032 debug_print("Deleting all cached messages...\n");
2034 dir = folder_item_get_path(item);
2035 if (is_dir_exist(dir))
2036 remove_all_numbered_files(dir);
2039 debug_print("done.\n");
2043 static SockInfo *imap_open_tunnel(const gchar *server,
2044 const gchar *tunnelcmd,
2047 static SockInfo *imap_open_tunnel(const gchar *server,
2048 const gchar *tunnelcmd)
2053 if ((sock = sock_connect_cmd(server, tunnelcmd)) == NULL) {
2054 log_warning(_("Can't establish IMAP4 session with: %s\n"),
2059 return imap_init_sock(sock, ssl_type);
2061 return imap_init_sock(sock);
2065 void *imap_open_thread(void *data)
2067 SockInfo *sock = NULL;
2068 thread_data *td = (thread_data *)data;
2069 if ((sock = sock_connect(td->server, td->port)) == NULL) {
2070 log_warning(_("Can't connect to IMAP4 server: %s:%d\n"),
2071 td->server, td->port);
2081 static SockInfo *imap_open(const gchar *server, gushort port,
2084 static SockInfo *imap_open(const gchar *server, gushort port)
2087 thread_data *td = g_new0(thread_data, 1);
2091 SockInfo *sock = NULL;
2094 td->ssl_type = ssl_type;
2096 td->server = g_strdup(server);
2100 statusbar_print_all(_("Connecting to IMAP4 server: %s..."), server);
2102 #if (defined USE_PTHREAD && defined __GLIBC__ && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 3)))
2103 if (pthread_create(&pt, PTHREAD_CREATE_JOINABLE,
2104 imap_open_thread, td) != 0) {
2105 statusbar_pop_all();
2106 sock = imap_open_thread(td);
2108 debug_print("+++waiting for imap_open_thread...\n");
2110 /* don't let the interface freeze while waiting */
2114 /* get the thread's return value and clean its resources */
2115 pthread_join(pt, (void *)&sock);
2118 sock = imap_open_thread(td);
2121 if (sock && td->ssl_type == SSL_TUNNEL && !ssl_init_socket(sock)) {
2122 log_warning(_("Can't establish IMAP4 session with: %s:%d\n"),
2123 td->server, td->port);
2132 debug_print("---imap_open_thread returned %p\n", sock);
2133 statusbar_pop_all();
2135 if(!sock && !prefs_common.no_recv_err_panel) {
2136 alertpanel_error(_("Can't connect to IMAP4 server: %s:%d"),
2144 static SockInfo *imap_init_sock(SockInfo *sock, SSLType ssl_type)
2146 static SockInfo *imap_init_sock(SockInfo *sock)
2153 static GList *imap_parse_namespace_str(gchar *str)
2158 IMAPNameSpace *namespace;
2159 GList *ns_list = NULL;
2161 while (*p != '\0') {
2162 /* parse ("#foo" "/") */
2164 while (*p && *p != '(') p++;
2165 if (*p == '\0') break;
2168 while (*p && *p != '"') p++;
2169 if (*p == '\0') break;
2173 while (*p && *p != '"') p++;
2174 if (*p == '\0') break;
2178 while (*p && isspace(*p)) p++;
2179 if (*p == '\0') break;
2180 if (strncmp(p, "NIL", 3) == 0)
2182 else if (*p == '"') {
2185 while (*p && *p != '"') p++;
2186 if (*p == '\0') break;
2191 while (*p && *p != ')') p++;
2192 if (*p == '\0') break;
2195 namespace = g_new(IMAPNameSpace, 1);
2196 namespace->name = g_strdup(name);
2197 namespace->separator = separator ? separator[0] : '\0';
2198 ns_list = g_list_append(ns_list, namespace);
2204 static void imap_parse_namespace(IMAPSession *session, IMAPFolder *folder)
2209 g_return_if_fail(session != NULL);
2210 g_return_if_fail(folder != NULL);
2212 if (folder->ns_personal != NULL ||
2213 folder->ns_others != NULL ||
2214 folder->ns_shared != NULL)
2217 if (!imap_has_capability(session, "NAMESPACE")) {
2218 imap_get_namespace_by_list(session, folder);
2222 if (imap_cmd_namespace(session, &ns_str)
2224 log_warning(_("can't get namespace\n"));
2228 str_array = strsplit_parenthesis(ns_str, '(', ')', 3);
2229 if (str_array == NULL) {
2231 imap_get_namespace_by_list(session, folder);
2235 folder->ns_personal = imap_parse_namespace_str(str_array[0]);
2236 if (str_array[0] && str_array[1])
2237 folder->ns_others = imap_parse_namespace_str(str_array[1]);
2238 if (str_array[0] && str_array[1] && str_array[2])
2239 folder->ns_shared = imap_parse_namespace_str(str_array[2]);
2240 g_strfreev(str_array);
2244 static void imap_get_namespace_by_list(IMAPSession *session, IMAPFolder *folder)
2246 GSList *item_list, *cur;
2247 gchar separator = '\0';
2248 IMAPNameSpace *namespace;
2250 g_return_if_fail(session != NULL);
2251 g_return_if_fail(folder != NULL);
2253 if (folder->ns_personal != NULL ||
2254 folder->ns_others != NULL ||
2255 folder->ns_shared != NULL)
2258 imap_gen_send(session, "LIST \"\" \"\"");
2259 item_list = imap_parse_list(folder, session, "", &separator);
2260 for (cur = item_list; cur != NULL; cur = cur->next)
2261 folder_item_destroy(FOLDER_ITEM(cur->data));
2262 g_slist_free(item_list);
2264 namespace = g_new(IMAPNameSpace, 1);
2265 namespace->name = g_strdup("");
2266 namespace->separator = separator;
2267 folder->ns_personal = g_list_append(NULL, namespace);
2270 static IMAPNameSpace *imap_find_namespace_from_list(GList *ns_list,
2273 IMAPNameSpace *namespace = NULL;
2274 gchar *tmp_path, *name;
2276 if (!path) path = "";
2278 for (; ns_list != NULL; ns_list = ns_list->next) {
2279 IMAPNameSpace *tmp_ns = ns_list->data;
2281 Xstrcat_a(tmp_path, path, "/", return namespace);
2282 Xstrdup_a(name, tmp_ns->name, return namespace);
2283 if (tmp_ns->separator && tmp_ns->separator != '/') {
2284 subst_char(tmp_path, tmp_ns->separator, '/');
2285 subst_char(name, tmp_ns->separator, '/');
2287 if (strncmp(tmp_path, name, strlen(name)) == 0)
2294 static IMAPNameSpace *imap_find_namespace(IMAPFolder *folder,
2297 IMAPNameSpace *namespace;
2299 g_return_val_if_fail(folder != NULL, NULL);
2301 namespace = imap_find_namespace_from_list(folder->ns_personal, path);
2302 if (namespace) return namespace;
2303 namespace = imap_find_namespace_from_list(folder->ns_others, path);
2304 if (namespace) return namespace;
2305 namespace = imap_find_namespace_from_list(folder->ns_shared, path);
2306 if (namespace) return namespace;
2311 static gchar imap_get_path_separator(IMAPFolder *folder, const gchar *path)
2313 IMAPNameSpace *namespace;
2314 gchar separator = '/';
2316 namespace = imap_find_namespace(folder, path);
2317 if (namespace && namespace->separator)
2318 separator = namespace->separator;
2323 static gchar *imap_get_real_path(IMAPFolder *folder, const gchar *path)
2328 g_return_val_if_fail(folder != NULL, NULL);
2329 g_return_val_if_fail(path != NULL, NULL);
2331 real_path = imap_utf8_to_modified_utf7(path);
2332 separator = imap_get_path_separator(folder, path);
2333 imap_path_separator_subst(real_path, separator);
2338 static gchar *imap_parse_atom(SockInfo *sock, gchar *src,
2339 gchar *dest, gint dest_len, GString *str)
2341 gchar *cur_pos = src;
2344 g_return_val_if_fail(str != NULL, cur_pos);
2346 /* read the next line if the current response buffer is empty */
2347 while (isspace(*(guchar *)cur_pos)) cur_pos++;
2348 while (*cur_pos == '\0') {
2349 if ((nextline = imap_getline(sock)) == NULL)
2351 g_string_assign(str, nextline);
2353 strretchomp(nextline);
2354 /* log_print("IMAP4< %s\n", nextline); */
2355 debug_print("IMAP4< %s\n", nextline);
2358 while (isspace(*(guchar *)cur_pos)) cur_pos++;
2361 if (!strncmp(cur_pos, "NIL", 3)) {
2364 } else if (*cur_pos == '\"') {
2367 p = get_quoted(cur_pos, '\"', dest, dest_len);
2368 cur_pos = p ? p : cur_pos + 2;
2369 } else if (*cur_pos == '{') {
2374 cur_pos = strchr_cpy(cur_pos + 1, '}', buf, sizeof(buf));
2376 g_return_val_if_fail(len >= 0, cur_pos);
2378 g_string_truncate(str, 0);
2382 if ((nextline = imap_getline(sock)) == NULL)
2384 line_len += strlen(nextline);
2385 g_string_append(str, nextline);
2387 strretchomp(nextline);
2388 /* log_print("IMAP4< %s\n", nextline); */
2389 debug_print("IMAP4< %s\n", nextline);
2391 } while (line_len < len);
2393 memcpy(dest, cur_pos, MIN(len, dest_len - 1));
2394 dest[MIN(len, dest_len - 1)] = '\0';
2401 static gchar *imap_get_header(SockInfo *sock, gchar *cur_pos, gchar **headers,
2411 g_return_val_if_fail(str != NULL, cur_pos);
2413 while (isspace(*(guchar *)cur_pos)) cur_pos++;
2415 g_return_val_if_fail(*cur_pos == '{', cur_pos);
2417 cur_pos = strchr_cpy(cur_pos + 1, '}', buf, sizeof(buf));
2419 g_return_val_if_fail(len >= 0, cur_pos);
2421 g_string_truncate(str, 0);
2425 if ((nextline = sock_getline(sock)) == NULL)
2427 block_len += strlen(nextline);
2428 g_string_append(str, nextline);
2430 strretchomp(nextline);
2431 /* debug_print("IMAP4< %s\n", nextline); */
2433 } while (block_len < len);
2435 debug_print("IMAP4< [contents of RFC822.HEADER]\n");
2437 *headers = g_strndup(cur_pos, len);
2440 while (isspace(*(guchar *)cur_pos)) cur_pos++;
2441 while (*cur_pos == '\0') {
2442 if ((nextline = sock_getline(sock)) == NULL)
2444 g_string_assign(str, nextline);
2446 strretchomp(nextline);
2447 debug_print("IMAP4< %s\n", nextline);
2450 while (isspace(*(guchar *)cur_pos)) cur_pos++;
2456 static MsgFlags imap_parse_flags(const gchar *flag_str)
2458 const gchar *p = flag_str;
2459 MsgFlags flags = {0, 0};
2461 flags.perm_flags = MSG_UNREAD;
2463 while ((p = strchr(p, '\\')) != NULL) {
2466 if (g_ascii_strncasecmp(p, "Recent", 6) == 0 && MSG_IS_UNREAD(flags)) {
2467 MSG_SET_PERM_FLAGS(flags, MSG_NEW);
2468 } else if (g_ascii_strncasecmp(p, "Seen", 4) == 0) {
2469 MSG_UNSET_PERM_FLAGS(flags, MSG_NEW|MSG_UNREAD);
2470 } else if (g_ascii_strncasecmp(p, "Deleted", 7) == 0) {
2471 MSG_SET_PERM_FLAGS(flags, MSG_DELETED);
2472 } else if (g_ascii_strncasecmp(p, "Flagged", 7) == 0) {
2473 MSG_SET_PERM_FLAGS(flags, MSG_MARKED);
2474 } else if (g_ascii_strncasecmp(p, "Answered", 8) == 0) {
2475 MSG_SET_PERM_FLAGS(flags, MSG_REPLIED);
2482 static MsgInfo *imap_parse_envelope(SockInfo *sock, FolderItem *item,
2485 gchar buf[IMAPBUFSIZE];
2486 MsgInfo *msginfo = NULL;
2491 MsgFlags flags = {0, 0}, imap_flags = {0, 0};
2493 g_return_val_if_fail(line_str != NULL, NULL);
2494 g_return_val_if_fail(line_str->str[0] == '*' &&
2495 line_str->str[1] == ' ', NULL);
2497 MSG_SET_TMP_FLAGS(flags, MSG_IMAP);
2498 if (item->stype == F_QUEUE) {
2499 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
2500 } else if (item->stype == F_DRAFT) {
2501 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
2504 cur_pos = line_str->str + 2;
2506 #define PARSE_ONE_ELEMENT(ch) \
2508 cur_pos = strchr_cpy(cur_pos, ch, buf, sizeof(buf)); \
2509 if (cur_pos == NULL) { \
2510 g_warning("cur_pos == NULL\n"); \
2511 procmsg_msginfo_free(msginfo); \
2516 PARSE_ONE_ELEMENT(' ');
2519 PARSE_ONE_ELEMENT(' ');
2520 g_return_val_if_fail(!strcmp(buf, "FETCH"), NULL);
2522 g_return_val_if_fail(*cur_pos == '(', NULL);
2525 while (*cur_pos != '\0' && *cur_pos != ')') {
2526 while (*cur_pos == ' ') cur_pos++;
2528 if (!strncmp(cur_pos, "UID ", 4)) {
2530 uid = strtoul(cur_pos, &cur_pos, 10);
2531 } else if (!strncmp(cur_pos, "FLAGS ", 6)) {
2533 if (*cur_pos != '(') {
2534 g_warning("*cur_pos != '('\n");
2535 procmsg_msginfo_free(msginfo);
2539 PARSE_ONE_ELEMENT(')');
2540 imap_flags = imap_parse_flags(buf);
2541 } else if (!strncmp(cur_pos, "RFC822.SIZE ", 12)) {
2543 size = strtol(cur_pos, &cur_pos, 10);
2544 } else if (!strncmp(cur_pos, "RFC822.HEADER ", 14)) {
2548 cur_pos = imap_get_header(sock, cur_pos, &headers,
2550 msginfo = procheader_parse_str(headers, flags, FALSE, FALSE);
2553 g_warning("invalid FETCH response: %s\n", cur_pos);
2559 msginfo->msgnum = uid;
2560 msginfo->size = size;
2561 msginfo->flags.tmp_flags |= imap_flags.tmp_flags;
2562 msginfo->flags.perm_flags = imap_flags.perm_flags;
2568 static gchar *imap_get_flag_str(IMAPFlags flags)
2573 str = g_string_new(NULL);
2575 if (IMAP_IS_SEEN(flags)) g_string_append(str, "\\Seen ");
2576 if (IMAP_IS_ANSWERED(flags)) g_string_append(str, "\\Answered ");
2577 if (IMAP_IS_FLAGGED(flags)) g_string_append(str, "\\Flagged ");
2578 if (IMAP_IS_DELETED(flags)) g_string_append(str, "\\Deleted ");
2579 if (IMAP_IS_DRAFT(flags)) g_string_append(str, "\\Draft");
2581 if (str->len > 0 && str->str[str->len - 1] == ' ')
2582 g_string_truncate(str, str->len - 1);
2585 g_string_free(str, FALSE);
2590 static gint imap_set_message_flags(IMAPSession *session,
2591 MsgNumberList *numlist,
2601 flag_str = imap_get_flag_str(flags);
2602 cmd = g_strconcat(is_set ? "+FLAGS.SILENT (" : "-FLAGS.SILENT (",
2603 flag_str, ")", NULL);
2606 seq_list = imap_get_seq_set_from_numlist(numlist);
2607 imapset = get_seq_set_from_seq_list(seq_list);
2609 ok = imap_cmd_store(session, imapset, cmd);
2612 imap_seq_set_free(seq_list);
2618 typedef struct _select_data {
2619 IMAPSession *session;
2624 guint32 *uid_validity;
2628 static void *imap_select_thread(void *data)
2630 select_data *stuff = (select_data *)data;
2631 IMAPSession *session = stuff->session;
2632 gchar *real_path = stuff->real_path;
2633 gint *exists = stuff->exists;
2634 gint *recent = stuff->recent;
2635 gint *unseen = stuff->unseen;
2636 guint32 *uid_validity = stuff->uid_validity;
2639 ok = imap_cmd_select(session, real_path,
2640 exists, recent, unseen, uid_validity, TRUE);
2642 return GINT_TO_POINTER(ok);
2645 static gint imap_select(IMAPSession *session, IMAPFolder *folder,
2647 gint *exists, gint *recent, gint *unseen,
2648 guint32 *uid_validity, gboolean block)
2652 gint exists_, recent_, unseen_;
2653 guint32 uid_validity_;
2655 if (!exists || !recent || !unseen || !uid_validity) {
2656 if (session->mbox && strcmp(session->mbox, path) == 0)
2657 return IMAP_SUCCESS;
2661 uid_validity = &uid_validity_;
2664 g_free(session->mbox);
2665 session->mbox = NULL;
2667 real_path = imap_get_real_path(folder, path);
2669 #if (defined USE_PTHREAD && defined __GLIBC__ && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 3)))
2670 if (block == FALSE) {
2671 select_data *data = g_new0(select_data, 1);
2674 data->session = session;
2675 data->real_path = real_path;
2676 data->exists = exists;
2677 data->recent = recent;
2678 data->unseen = unseen;
2679 data->uid_validity = uid_validity;
2682 if (pthread_create(&pt, PTHREAD_CREATE_JOINABLE,
2683 imap_select_thread, data) != 0) {
2684 ok = GPOINTER_TO_INT(imap_select_thread(data));
2687 debug_print("+++waiting for imap_select_thread...\n");
2688 while(!data->done) {
2689 /* don't let the interface freeze while waiting */
2692 debug_print("---imap_select_thread done\n");
2694 /* get the thread's return value and clean its resources */
2695 pthread_join(pt, &tmp);
2696 ok = GPOINTER_TO_INT(tmp);
2700 ok = imap_cmd_select(session, real_path,
2701 exists, recent, unseen, uid_validity, block);
2704 ok = imap_cmd_select(session, real_path,
2705 exists, recent, unseen, uid_validity, block);
2707 if (ok != IMAP_SUCCESS)
2708 log_warning(_("can't select folder: %s\n"), real_path);
2710 session->mbox = g_strdup(path);
2711 session->folder_content_changed = FALSE;
2718 #define THROW(err) { ok = err; goto catch; }
2720 static gint imap_status(IMAPSession *session, IMAPFolder *folder,
2722 gint *messages, gint *recent,
2723 guint32 *uid_next, guint32 *uid_validity,
2724 gint *unseen, gboolean block)
2729 GPtrArray *argbuf = NULL;
2732 if (messages && recent && uid_next && uid_validity && unseen) {
2733 *messages = *recent = *uid_next = *uid_validity = *unseen = 0;
2734 argbuf = g_ptr_array_new();
2737 real_path = imap_get_real_path(folder, path);
2738 QUOTE_IF_REQUIRED(real_path_, real_path);
2739 imap_gen_send(session, "STATUS %s "
2740 "(MESSAGES RECENT UIDNEXT UIDVALIDITY UNSEEN)",
2743 ok = imap_cmd_ok_with_block(session, argbuf, block);
2744 if (ok != IMAP_SUCCESS || !argbuf) THROW(ok);
2746 str = search_array_str(argbuf, "STATUS");
2747 if (!str) THROW(IMAP_ERROR);
2749 str = strchr(str, '(');
2750 if (!str) THROW(IMAP_ERROR);
2752 while (*str != '\0' && *str != ')') {
2753 while (*str == ' ') str++;
2755 if (!strncmp(str, "MESSAGES ", 9)) {
2757 *messages = strtol(str, &str, 10);
2758 } else if (!strncmp(str, "RECENT ", 7)) {
2760 *recent = strtol(str, &str, 10);
2761 } else if (!strncmp(str, "UIDNEXT ", 8)) {
2763 *uid_next = strtoul(str, &str, 10);
2764 } else if (!strncmp(str, "UIDVALIDITY ", 12)) {
2766 *uid_validity = strtoul(str, &str, 10);
2767 } else if (!strncmp(str, "UNSEEN ", 7)) {
2769 *unseen = strtol(str, &str, 10);
2771 g_warning("invalid STATUS response: %s\n", str);
2779 ptr_array_free_strings(argbuf);
2780 g_ptr_array_free(argbuf, TRUE);
2788 static gboolean imap_has_capability(IMAPSession *session, const gchar *cap)
2792 for (p = session->capability; *p != NULL; ++p) {
2793 if (!g_ascii_strcasecmp(*p, cap))
2800 static void imap_free_capabilities(IMAPSession *session)
2802 g_strfreev(session->capability);
2803 session->capability = NULL;
2806 /* low-level IMAP4rev1 commands */
2808 static gint imap_cmd_authenticate(IMAPSession *session, const gchar *user,
2809 const gchar *pass, IMAPAuthType type)
2816 gchar hexdigest[33];
2820 auth_type = "CRAM-MD5";
2822 imap_gen_send(session, "AUTHENTICATE %s", auth_type);
2823 ok = imap_gen_recv(session, &buf);
2824 if (ok != IMAP_SUCCESS || buf[0] != '+' || buf[1] != ' ') {
2829 challenge = g_malloc(strlen(buf + 2) + 1);
2830 challenge_len = base64_decode(challenge, buf + 2, -1);
2831 challenge[challenge_len] = '\0';
2833 log_print("IMAP< [Decoded: %s]\n", challenge);
2835 md5_hex_hmac(hexdigest, challenge, challenge_len, pass, strlen(pass));
2838 response = g_strdup_printf("%s %s", user, hexdigest);
2839 log_print("IMAP> [Encoded: %s]\n", response);
2840 response64 = g_malloc((strlen(response) + 3) * 2 + 1);
2841 base64_encode(response64, response, strlen(response));
2844 log_print("IMAP> %s\n", response64);
2845 sock_puts(SESSION(session)->sock, response64);
2846 ok = imap_cmd_ok(session, NULL);
2847 if (ok != IMAP_SUCCESS)
2848 log_warning(_("IMAP4 authentication failed.\n"));
2853 static gint imap_cmd_login(IMAPSession *session,
2854 const gchar *user, const gchar *pass)
2856 gchar *user_, *pass_;
2859 QUOTE_IF_REQUIRED(user_, user);
2860 QUOTE_IF_REQUIRED(pass_, pass);
2861 imap_gen_send(session, "LOGIN %s %s", user_, pass_);
2863 ok = imap_cmd_ok(session, NULL);
2864 if (ok != IMAP_SUCCESS)
2865 log_warning(_("IMAP4 login failed.\n"));
2870 static gint imap_cmd_logout(IMAPSession *session)
2872 imap_gen_send(session, "LOGOUT");
2873 return imap_cmd_ok(session, NULL);
2876 static gint imap_cmd_noop(IMAPSession *session)
2878 imap_gen_send(session, "NOOP");
2879 return imap_cmd_ok(session, NULL);
2883 static gint imap_cmd_starttls(IMAPSession *session)
2885 imap_gen_send(session, "STARTTLS");
2886 return imap_cmd_ok(session, NULL);
2890 #define THROW(err) { ok = err; goto catch; }
2892 static gint imap_cmd_namespace(IMAPSession *session, gchar **ns_str)
2898 argbuf = g_ptr_array_new();
2900 imap_gen_send(session, "NAMESPACE");
2901 if ((ok = imap_cmd_ok(session, argbuf)) != IMAP_SUCCESS) THROW(ok);
2903 str = search_array_str(argbuf, "NAMESPACE");
2904 if (!str) THROW(IMAP_ERROR);
2906 *ns_str = g_strdup(str);
2909 ptr_array_free_strings(argbuf);
2910 g_ptr_array_free(argbuf, TRUE);
2917 static gint imap_cmd_list(IMAPSession *session, const gchar *ref,
2918 const gchar *mailbox, GPtrArray *argbuf)
2920 gchar *ref_, *mailbox_;
2922 if (!ref) ref = "\"\"";
2923 if (!mailbox) mailbox = "\"\"";
2925 QUOTE_IF_REQUIRED(ref_, ref);
2926 QUOTE_IF_REQUIRED(mailbox_, mailbox);
2927 imap_gen_send(session, "LIST %s %s", ref_, mailbox_);
2929 return imap_cmd_ok(session, argbuf);
2932 #define THROW goto catch
2934 static gint imap_cmd_do_select(IMAPSession *session, const gchar *folder,
2936 gint *exists, gint *recent, gint *unseen,
2937 guint32 *uid_validity, gboolean block)
2944 unsigned int uid_validity_;
2946 *exists = *recent = *unseen = *uid_validity = 0;
2947 argbuf = g_ptr_array_new();
2950 select_cmd = "EXAMINE";
2952 select_cmd = "SELECT";
2954 QUOTE_IF_REQUIRED(folder_, folder);
2955 imap_gen_send(session, "%s %s", select_cmd, folder_);
2957 if ((ok = imap_cmd_ok_with_block(session, argbuf, block)) != IMAP_SUCCESS) THROW;
2959 resp_str = search_array_contain_str(argbuf, "EXISTS");
2961 if (sscanf(resp_str,"%d EXISTS", exists) != 1) {
2962 g_warning("imap_cmd_select(): invalid EXISTS line.\n");
2967 resp_str = search_array_contain_str(argbuf, "RECENT");
2969 if (sscanf(resp_str, "%d RECENT", recent) != 1) {
2970 g_warning("imap_cmd_select(): invalid RECENT line.\n");
2975 resp_str = search_array_contain_str(argbuf, "UIDVALIDITY");
2977 if (sscanf(resp_str, "OK [UIDVALIDITY %u] ", &uid_validity_)
2979 g_warning("imap_cmd_select(): invalid UIDVALIDITY line.\n");
2982 *uid_validity = uid_validity_;
2985 resp_str = search_array_contain_str(argbuf, "UNSEEN");
2987 if (sscanf(resp_str, "OK [UNSEEN %d] ", unseen) != 1) {
2988 g_warning("imap_cmd_select(): invalid UNSEEN line.\n");
2994 ptr_array_free_strings(argbuf);
2995 g_ptr_array_free(argbuf, TRUE);
3000 static gint imap_cmd_select(IMAPSession *session, const gchar *folder,
3001 gint *exists, gint *recent, gint *unseen,
3002 guint32 *uid_validity, gboolean block)
3004 return imap_cmd_do_select(session, folder, FALSE,
3005 exists, recent, unseen, uid_validity, block);
3008 static gint imap_cmd_examine(IMAPSession *session, const gchar *folder,
3009 gint *exists, gint *recent, gint *unseen,
3010 guint32 *uid_validity, gboolean block)
3012 return imap_cmd_do_select(session, folder, TRUE,
3013 exists, recent, unseen, uid_validity, block);
3018 static gint imap_cmd_create(IMAPSession *session, const gchar *folder)
3022 QUOTE_IF_REQUIRED(folder_, folder);
3023 imap_gen_send(session, "CREATE %s", folder_);
3025 return imap_cmd_ok(session, NULL);
3028 static gint imap_cmd_rename(IMAPSession *session, const gchar *old_folder,
3029 const gchar *new_folder)
3031 gchar *old_folder_, *new_folder_;
3033 QUOTE_IF_REQUIRED(old_folder_, old_folder);
3034 QUOTE_IF_REQUIRED(new_folder_, new_folder);
3035 imap_gen_send(session, "RENAME %s %s", old_folder_, new_folder_);
3037 return imap_cmd_ok(session, NULL);
3040 static gint imap_cmd_delete(IMAPSession *session, const gchar *folder)
3044 QUOTE_IF_REQUIRED(folder_, folder);
3045 imap_gen_send(session, "DELETE %s", folder_);
3047 return imap_cmd_ok(session, NULL);
3050 static gint imap_cmd_search(IMAPSession *session, const gchar *criteria,
3051 GSList **list, gboolean block)
3057 g_return_val_if_fail(criteria != NULL, IMAP_ERROR);
3058 g_return_val_if_fail(list != NULL, IMAP_ERROR);
3062 argbuf = g_ptr_array_new();
3063 imap_gen_send(session, "UID SEARCH %s", criteria);
3065 ok = imap_cmd_ok_with_block(session, argbuf, block);
3066 if (ok != IMAP_SUCCESS) {
3067 ptr_array_free_strings(argbuf);
3068 g_ptr_array_free(argbuf, TRUE);
3072 if ((uidlist = search_array_str(argbuf, "SEARCH ")) != NULL) {
3073 gchar **strlist, **p;
3075 strlist = g_strsplit(uidlist + 7, " ", 0);
3076 for (p = strlist; *p != NULL; ++p) {
3079 if (sscanf(*p, "%u", &msgnum) == 1)
3080 *list = g_slist_append(*list, GINT_TO_POINTER(msgnum));
3082 g_strfreev(strlist);
3084 ptr_array_free_strings(argbuf);
3085 g_ptr_array_free(argbuf, TRUE);
3087 return IMAP_SUCCESS;
3090 typedef struct _fetch_data {
3091 IMAPSession *session;
3093 const gchar *filename;
3097 static void *imap_cmd_fetch_thread(void *data)
3099 fetch_data *stuff = (fetch_data *)data;
3100 IMAPSession *session = stuff->session;
3101 guint32 uid = stuff->uid;
3102 const gchar *filename = stuff->filename;
3110 if (filename == NULL) {
3112 return GINT_TO_POINTER(IMAP_ERROR);
3115 imap_gen_send(session, "UID FETCH %d BODY.PEEK[]", uid);
3117 while ((ok = imap_gen_recv_block(session, &buf)) == IMAP_SUCCESS) {
3118 if (buf[0] != '*' || buf[1] != ' ') {
3121 return GINT_TO_POINTER(IMAP_ERROR);
3123 if (strstr(buf, "FETCH") != NULL) break;
3126 if (ok != IMAP_SUCCESS) {
3129 return GINT_TO_POINTER(ok);
3132 #define RETURN_ERROR_IF_FAIL(cond) \
3135 stuff->done = TRUE; \
3136 return GINT_TO_POINTER(IMAP_ERROR); \
3139 cur_pos = strchr(buf, '{');
3140 RETURN_ERROR_IF_FAIL(cur_pos != NULL);
3141 cur_pos = strchr_cpy(cur_pos + 1, '}', size_str, sizeof(size_str));
3142 RETURN_ERROR_IF_FAIL(cur_pos != NULL);
3143 size_num = atol(size_str);
3144 RETURN_ERROR_IF_FAIL(size_num >= 0);
3146 RETURN_ERROR_IF_FAIL(*cur_pos == '\0');
3148 #undef RETURN_ERROR_IF_FAIL
3152 if (recv_bytes_write_to_file(SESSION(session)->sock,
3153 size_num, filename) != 0) {
3155 return GINT_TO_POINTER(IMAP_ERROR);
3157 if (imap_gen_recv_block(session, &buf) != IMAP_SUCCESS) {
3160 return GINT_TO_POINTER(IMAP_ERROR);
3163 if (buf[0] == '\0' || buf[strlen(buf) - 1] != ')') {
3166 return GINT_TO_POINTER(IMAP_ERROR);
3170 ok = imap_cmd_ok_block(session, NULL);
3173 return GINT_TO_POINTER(ok);
3176 static gint imap_cmd_fetch(IMAPSession *session, guint32 uid,
3177 const gchar *filename)
3179 fetch_data *data = g_new0(fetch_data, 1);
3186 data->session = session;
3188 data->filename = filename;
3190 if (prefs_common.work_offline && !imap_gtk_should_override()) {
3195 #if (defined USE_PTHREAD && defined __GLIBC__ && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 3)))
3196 if (pthread_create(&pt, PTHREAD_CREATE_JOINABLE,
3197 imap_cmd_fetch_thread, data) != 0) {
3198 result = GPOINTER_TO_INT(imap_cmd_fetch_thread(data));
3202 debug_print("+++waiting for imap_cmd_fetch_thread...\n");
3203 while(!data->done) {
3204 /* don't let the interface freeze while waiting */
3207 debug_print("---imap_cmd_fetch_thread done\n");
3209 /* get the thread's return value and clean its resources */
3210 pthread_join(pt, &tmp);
3211 result = GPOINTER_TO_INT(tmp);
3213 result = GPOINTER_TO_INT(imap_cmd_fetch_thread(data));
3219 static gint imap_cmd_append(IMAPSession *session, const gchar *destfolder,
3220 const gchar *file, IMAPFlags flags,
3227 unsigned int new_uid_;
3229 gchar buf[BUFFSIZE];
3234 g_return_val_if_fail(file != NULL, IMAP_ERROR);
3236 size = get_file_size_as_crlf(file);
3237 if ((fp = fopen(file, "rb")) == NULL) {
3238 FILE_OP_ERROR(file, "fopen");
3241 QUOTE_IF_REQUIRED(destfolder_, destfolder);
3242 flag_str = imap_get_flag_str(flags);
3243 imap_gen_send(session, "APPEND %s (%s) {%d}",
3244 destfolder_, flag_str, size);
3247 ok = imap_gen_recv(session, &ret);
3248 if (ok != IMAP_SUCCESS || ret[0] != '+' || ret[1] != ' ') {
3249 log_warning(_("can't append %s to %s\n"), file, destfolder_);
3256 log_print("IMAP4> %s\n", "(sending file...)");
3258 while (fgets(buf, sizeof(buf), fp) != NULL) {
3260 if (sock_puts(SESSION(session)->sock, buf) < 0) {
3267 FILE_OP_ERROR(file, "fgets");
3272 sock_puts(SESSION(session)->sock, "");
3276 if (new_uid != NULL)
3279 if (new_uid != NULL && session->uidplus) {
3280 argbuf = g_ptr_array_new();
3282 ok = imap_cmd_ok(session, argbuf);
3283 if ((ok == IMAP_SUCCESS) && (argbuf->len > 0)) {
3284 resp_str = g_ptr_array_index(argbuf, argbuf->len - 1);
3286 sscanf(resp_str, "%*u OK [APPENDUID %*u %u]",
3288 *new_uid = new_uid_;
3292 ptr_array_free_strings(argbuf);
3293 g_ptr_array_free(argbuf, TRUE);
3295 ok = imap_cmd_ok(session, NULL);
3297 if (ok != IMAP_SUCCESS)
3298 log_warning(_("can't append message to %s\n"),
3304 static MsgNumberList *imapset_to_numlist(IMAPSet imapset)
3306 gchar **ranges, **range;
3307 unsigned int low, high;
3308 MsgNumberList *uids = NULL;
3310 ranges = g_strsplit(imapset, ",", 0);
3311 for (range = ranges; *range != NULL; range++) {
3312 if (sscanf(*range, "%u:%u", &low, &high) == 1)
3313 uids = g_slist_prepend(uids, GINT_TO_POINTER(low));
3316 for (i = low; i <= high; i++)
3317 uids = g_slist_prepend(uids, GINT_TO_POINTER(i));
3320 uids = g_slist_reverse(uids);
3326 static gint imap_cmd_copy(IMAPSession *session, const gchar *seq_set,
3327 const gchar *destfolder, GRelation *uid_mapping)
3332 g_return_val_if_fail(session != NULL, IMAP_ERROR);
3333 g_return_val_if_fail(seq_set != NULL, IMAP_ERROR);
3334 g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
3336 QUOTE_IF_REQUIRED(destfolder_, destfolder);
3337 imap_gen_send(session, "UID COPY %s %s", seq_set, destfolder_);
3339 if (uid_mapping != NULL && session->uidplus) {
3341 gchar *resp_str = NULL, *olduids_str, *newuids_str;
3342 MsgNumberList *olduids, *old_cur, *newuids, *new_cur;
3344 reply = g_ptr_array_new();
3345 ok = imap_cmd_ok(session, reply);
3346 if ((ok == IMAP_SUCCESS) && (reply->len > 0)) {
3347 resp_str = g_ptr_array_index(reply, reply->len - 1);
3349 olduids_str = g_new0(gchar, strlen(resp_str));
3350 newuids_str = g_new0(gchar, strlen(resp_str));
3351 if (sscanf(resp_str, "%*s OK [COPYUID %*u %[0-9,:] %[0-9,:]]",
3352 olduids_str, newuids_str) == 2) {
3353 olduids = imapset_to_numlist(olduids_str);
3354 newuids = imapset_to_numlist(newuids_str);
3358 while(old_cur != NULL && new_cur != NULL) {
3359 g_relation_insert(uid_mapping,
3360 GPOINTER_TO_INT(old_cur->data),
3361 GPOINTER_TO_INT(new_cur->data));
3362 old_cur = g_slist_next(old_cur);
3363 new_cur = g_slist_next(new_cur);
3366 g_slist_free(olduids);
3367 g_slist_free(newuids);
3369 g_free(olduids_str);
3370 g_free(newuids_str);
3373 ptr_array_free_strings(reply);
3374 g_ptr_array_free(reply, TRUE);
3376 ok = imap_cmd_ok(session, NULL);
3378 if (ok != IMAP_SUCCESS)
3379 log_warning(_("can't copy %s to %s\n"), seq_set, destfolder_);
3384 gint imap_cmd_envelope(IMAPSession *session, IMAPSet set)
3387 (session, "UID FETCH %s (UID FLAGS RFC822.SIZE RFC822.HEADER)",
3390 return IMAP_SUCCESS;
3393 static gint imap_cmd_store(IMAPSession *session, IMAPSet seq_set,
3398 imap_gen_send(session, "UID STORE %s %s", seq_set, sub_cmd);
3400 if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS) {
3401 log_warning(_("error while imap command: STORE %s %s\n"),
3406 return IMAP_SUCCESS;
3409 static gint imap_cmd_expunge(IMAPSession *session, IMAPSet seq_set)
3413 if (seq_set && session->uidplus)
3414 imap_gen_send(session, "UID EXPUNGE %s", seq_set);
3416 imap_gen_send(session, "EXPUNGE");
3417 if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS) {
3418 log_warning(_("error while imap command: EXPUNGE\n"));
3422 return IMAP_SUCCESS;
3425 static gint imap_cmd_close(IMAPSession *session)
3429 imap_gen_send(session, "CLOSE");
3430 if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS)
3431 log_warning(_("error while imap command: CLOSE\n"));
3436 static gint imap_cmd_ok_with_block(IMAPSession *session, GPtrArray *argbuf, gboolean block)
3438 gint ok = IMAP_SUCCESS;
3443 while ((ok = imap_gen_recv_with_block(session, &buf, block))
3445 /* make sure data is long enough for any substring of buf */
3446 data = alloca(strlen(buf) + 1);
3448 /* untagged line read */
3449 if (buf[0] == '*' && buf[1] == ' ') {
3452 g_ptr_array_add(argbuf, g_strdup(buf + 2));
3454 if (sscanf(buf + 2, "%d %s", &num, data) >= 2) {
3455 if (!strcmp(data, "EXISTS")) {
3456 session->exists = num;
3457 session->folder_content_changed = TRUE;
3460 if(!strcmp(data, "EXPUNGE")) {
3462 session->folder_content_changed = TRUE;
3465 /* tagged line with correct tag and OK response found */
3466 } else if ((sscanf(buf, "%d %s", &cmd_num, data) >= 2) &&
3467 (cmd_num == session->cmd_count) &&
3468 !strcmp(data, "OK")) {
3470 g_ptr_array_add(argbuf, g_strdup(buf));
3472 /* everything else */
3483 static gint imap_cmd_ok(IMAPSession *session, GPtrArray *argbuf)
3485 return imap_cmd_ok_with_block(session, argbuf, FALSE);
3487 static gint imap_cmd_ok_block(IMAPSession *session, GPtrArray *argbuf)
3489 return imap_cmd_ok_with_block(session, argbuf, TRUE);
3491 static void imap_gen_send(IMAPSession *session, const gchar *format, ...)
3498 va_start(args, format);
3499 tmp = g_strdup_vprintf(format, args);
3502 session->cmd_count++;
3504 buf = g_strdup_printf("%d %s\r\n", session->cmd_count, tmp);
3505 if (!g_ascii_strncasecmp(tmp, "LOGIN ", 6) && (p = strchr(tmp + 6, ' '))) {
3507 log_print("IMAP4> %d %s ********\n", session->cmd_count, tmp);
3509 log_print("IMAP4> %d %s\n", session->cmd_count, tmp);
3511 sock_write_all(SESSION(session)->sock, buf, strlen(buf));
3516 static gint imap_gen_recv_with_block(IMAPSession *session, gchar **ret, gboolean block)
3519 if ((*ret = imap_getline(SESSION(session)->sock)) == NULL)
3522 if ((*ret = sock_getline(SESSION(session)->sock)) == NULL)
3527 log_print("IMAP4< %s\n", *ret);
3529 session_set_access_time(SESSION(session));
3531 return IMAP_SUCCESS;
3534 static gint imap_gen_recv_block(IMAPSession *session, gchar **ret)
3536 return imap_gen_recv_with_block(session, ret, TRUE);
3539 static gint imap_gen_recv(IMAPSession *session, gchar **ret)
3541 return imap_gen_recv_with_block(session, ret, FALSE);
3543 /* misc utility functions */
3545 static gchar *strchr_cpy(const gchar *src, gchar ch, gchar *dest, gint len)
3550 tmp = strchr(src, ch);
3554 memcpy(dest, src, MIN(tmp - src, len - 1));
3555 dest[MIN(tmp - src, len - 1)] = '\0';
3560 static gchar *get_quoted(const gchar *src, gchar ch, gchar *dest, gint len)
3562 const gchar *p = src;
3565 g_return_val_if_fail(*p == ch, NULL);
3570 while (*p != '\0' && *p != ch) {
3572 if (*p == '\\' && *(p + 1) != '\0')
3581 return (gchar *)(*p == ch ? p + 1 : p);
3584 static gchar *search_array_contain_str(GPtrArray *array, const gchar *str)
3588 for (i = 0; i < array->len; i++) {
3591 tmp = g_ptr_array_index(array, i);
3592 if (strstr(tmp, str) != NULL)
3599 static gchar *search_array_str(GPtrArray *array, const gchar *str)
3606 for (i = 0; i < array->len; i++) {
3609 tmp = g_ptr_array_index(array, i);
3610 if (!strncmp(tmp, str, len))
3617 static void imap_path_separator_subst(gchar *str, gchar separator)
3620 gboolean in_escape = FALSE;
3622 if (!separator || separator == '/') return;
3624 for (p = str; *p != '\0'; p++) {
3625 if (*p == '/' && !in_escape)
3627 else if (*p == '&' && *(p + 1) != '-' && !in_escape)
3629 else if (*p == '-' && in_escape)
3634 static gchar *imap_modified_utf7_to_utf8(const gchar *mutf7_str)
3636 static iconv_t cd = (iconv_t)-1;
3637 static gboolean iconv_ok = TRUE;
3640 size_t norm_utf7_len;
3642 gchar *to_str, *to_p;
3644 gboolean in_escape = FALSE;
3646 if (!iconv_ok) return g_strdup(mutf7_str);
3648 if (cd == (iconv_t)-1) {
3649 cd = iconv_open(CS_INTERNAL, CS_UTF_7);
3650 if (cd == (iconv_t)-1) {
3651 g_warning("iconv cannot convert UTF-7 to %s\n",
3654 return g_strdup(mutf7_str);
3658 /* modified UTF-7 to normal UTF-7 conversion */
3659 norm_utf7 = g_string_new(NULL);
3661 for (p = mutf7_str; *p != '\0'; p++) {
3662 /* replace: '&' -> '+',
3664 escaped ',' -> '/' */
3665 if (!in_escape && *p == '&') {
3666 if (*(p + 1) != '-') {
3667 g_string_append_c(norm_utf7, '+');
3670 g_string_append_c(norm_utf7, '&');
3673 } else if (in_escape && *p == ',') {
3674 g_string_append_c(norm_utf7, '/');
3675 } else if (in_escape && *p == '-') {
3676 g_string_append_c(norm_utf7, '-');
3679 g_string_append_c(norm_utf7, *p);
3683 norm_utf7_p = norm_utf7->str;
3684 norm_utf7_len = norm_utf7->len;
3685 to_len = strlen(mutf7_str) * 5;
3686 to_p = to_str = g_malloc(to_len + 1);
3688 if (iconv(cd, (ICONV_CONST gchar **)&norm_utf7_p, &norm_utf7_len,
3689 &to_p, &to_len) == -1) {
3690 g_warning(_("iconv cannot convert UTF-7 to %s\n"),
3691 conv_get_locale_charset_str());
3692 g_string_free(norm_utf7, TRUE);
3694 return g_strdup(mutf7_str);
3697 /* second iconv() call for flushing */
3698 iconv(cd, NULL, NULL, &to_p, &to_len);
3699 g_string_free(norm_utf7, TRUE);
3705 static gchar *imap_utf8_to_modified_utf7(const gchar *from)
3707 static iconv_t cd = (iconv_t)-1;
3708 static gboolean iconv_ok = TRUE;
3709 gchar *norm_utf7, *norm_utf7_p;
3710 size_t from_len, norm_utf7_len;
3712 gchar *from_tmp, *to, *p;
3713 gboolean in_escape = FALSE;
3715 if (!iconv_ok) return g_strdup(from);
3717 if (cd == (iconv_t)-1) {
3718 cd = iconv_open(CS_UTF_7, CS_INTERNAL);
3719 if (cd == (iconv_t)-1) {
3720 g_warning(_("iconv cannot convert %s to UTF-7\n"),
3723 return g_strdup(from);
3727 /* UTF-8 to normal UTF-7 conversion */
3728 Xstrdup_a(from_tmp, from, return g_strdup(from));
3729 from_len = strlen(from);
3730 norm_utf7_len = from_len * 5;
3731 Xalloca(norm_utf7, norm_utf7_len + 1, return g_strdup(from));
3732 norm_utf7_p = norm_utf7;
3734 #define IS_PRINT(ch) (isprint(ch) && IS_ASCII(ch))
3736 while (from_len > 0) {
3737 if (*from_tmp == '+') {
3738 *norm_utf7_p++ = '+';
3739 *norm_utf7_p++ = '-';
3743 } else if (IS_PRINT(*(guchar *)from_tmp)) {
3744 /* printable ascii char */
3745 *norm_utf7_p = *from_tmp;
3751 size_t conv_len = 0;
3753 /* unprintable char: convert to UTF-7 */
3755 while (!IS_PRINT(*(guchar *)p) && conv_len < from_len) {
3756 conv_len += g_utf8_skip[*(guchar *)p];
3757 p += g_utf8_skip[*(guchar *)p];
3760 from_len -= conv_len;
3761 if (iconv(cd, (ICONV_CONST gchar **)&from_tmp,
3763 &norm_utf7_p, &norm_utf7_len) == -1) {
3764 g_warning(_("iconv cannot convert UTF-8 to UTF-7\n"));
3765 return g_strdup(from);
3768 /* second iconv() call for flushing */
3769 iconv(cd, NULL, NULL, &norm_utf7_p, &norm_utf7_len);
3775 *norm_utf7_p = '\0';
3776 to_str = g_string_new(NULL);
3777 for (p = norm_utf7; p < norm_utf7_p; p++) {
3778 /* replace: '&' -> "&-",
3781 BASE64 '/' -> ',' */
3782 if (!in_escape && *p == '&') {
3783 g_string_append(to_str, "&-");
3784 } else if (!in_escape && *p == '+') {
3785 if (*(p + 1) == '-') {
3786 g_string_append_c(to_str, '+');
3789 g_string_append_c(to_str, '&');
3792 } else if (in_escape && *p == '/') {
3793 g_string_append_c(to_str, ',');
3794 } else if (in_escape && *p == '-') {
3795 g_string_append_c(to_str, '-');
3798 g_string_append_c(to_str, *p);
3804 g_string_append_c(to_str, '-');
3808 g_string_free(to_str, FALSE);
3813 static GSList *imap_get_seq_set_from_numlist(MsgNumberList *numlist)
3816 GSList *sorted_list, *cur;
3817 guint first, last, next;
3819 GSList *ret_list = NULL;
3821 if (numlist == NULL)
3824 str = g_string_sized_new(256);
3826 sorted_list = g_slist_copy(numlist);
3827 sorted_list = g_slist_sort(sorted_list, g_int_compare);
3829 first = GPOINTER_TO_INT(sorted_list->data);
3831 for (cur = sorted_list; cur != NULL; cur = g_slist_next(cur)) {
3832 last = GPOINTER_TO_INT(cur->data);
3834 next = GPOINTER_TO_INT(cur->next->data);
3838 if (last + 1 != next || next == 0) {
3840 g_string_append_c(str, ',');
3842 g_string_append_printf(str, "%u", first);
3844 g_string_append_printf(str, "%u:%u", first, last);
3848 if (str->len > IMAP_CMD_LIMIT) {
3849 ret_str = g_strdup(str->str);
3850 ret_list = g_slist_append(ret_list, ret_str);
3851 g_string_truncate(str, 0);
3857 ret_str = g_strdup(str->str);
3858 ret_list = g_slist_append(ret_list, ret_str);
3861 g_slist_free(sorted_list);
3862 g_string_free(str, TRUE);
3867 static GSList *imap_get_seq_set_from_msglist(MsgInfoList *msglist)
3869 MsgNumberList *numlist = NULL;
3873 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
3874 MsgInfo *msginfo = (MsgInfo *) cur->data;
3876 numlist = g_slist_append(numlist, GINT_TO_POINTER(msginfo->msgnum));
3878 seq_list = imap_get_seq_set_from_numlist(numlist);
3879 g_slist_free(numlist);
3884 static void imap_seq_set_free(GSList *seq_list)
3886 slist_free_strings(seq_list);
3887 g_slist_free(seq_list);
3891 static gboolean imap_rename_folder_func(GNode *node, gpointer data)
3893 FolderItem *item = node->data;
3894 gchar **paths = data;
3895 const gchar *oldpath = paths[0];
3896 const gchar *newpath = paths[1];
3898 gchar *new_itempath;
3901 oldpathlen = strlen(oldpath);
3902 if (strncmp(oldpath, item->path, oldpathlen) != 0) {
3903 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
3907 base = item->path + oldpathlen;
3908 while (*base == G_DIR_SEPARATOR) base++;
3910 new_itempath = g_strdup(newpath);
3912 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
3915 item->path = new_itempath;
3920 typedef struct _get_list_uid_data {
3922 IMAPFolderItem *item;
3923 GSList **msgnum_list;
3925 } get_list_uid_data;
3927 static void *get_list_of_uids_thread(void *data)
3929 get_list_uid_data *stuff = (get_list_uid_data *)data;
3930 Folder *folder = stuff->folder;
3931 IMAPFolderItem *item = stuff->item;
3932 GSList **msgnum_list = stuff->msgnum_list;
3933 gint ok, nummsgs = 0, lastuid_old;
3934 IMAPSession *session;
3935 GSList *uidlist, *elem;
3938 session = imap_session_get(folder);
3939 if (session == NULL) {
3941 return GINT_TO_POINTER(-1);
3944 ok = imap_select(session, IMAP_FOLDER(folder), item->item.path,
3945 NULL, NULL, NULL, NULL, TRUE);
3946 if (ok != IMAP_SUCCESS) {
3948 return GINT_TO_POINTER(-1);
3951 cmd_buf = g_strdup_printf("UID %d:*", item->lastuid + 1);
3952 ok = imap_cmd_search(session, cmd_buf, &uidlist, TRUE);
3955 if (ok == IMAP_SOCKET) {
3956 session_destroy((Session *)session);
3957 ((RemoteFolder *)folder)->session = NULL;
3959 return GINT_TO_POINTER(-1);
3962 if (ok != IMAP_SUCCESS) {
3966 argbuf = g_ptr_array_new();
3968 cmd_buf = g_strdup_printf("UID FETCH %d:* (UID)", item->lastuid + 1);
3969 imap_gen_send(session, cmd_buf);
3971 ok = imap_cmd_ok_block(session, argbuf);
3972 if (ok != IMAP_SUCCESS) {
3973 ptr_array_free_strings(argbuf);
3974 g_ptr_array_free(argbuf, TRUE);
3976 return GINT_TO_POINTER(-1);
3979 for(i = 0; i < argbuf->len; i++) {
3982 if((ret = sscanf(g_ptr_array_index(argbuf, i),
3983 "%*d FETCH (UID %d)", &msgnum)) == 1)
3984 uidlist = g_slist_prepend(uidlist, GINT_TO_POINTER(msgnum));
3986 ptr_array_free_strings(argbuf);
3987 g_ptr_array_free(argbuf, TRUE);
3990 lastuid_old = item->lastuid;
3991 *msgnum_list = g_slist_copy(item->uid_list);
3992 nummsgs = g_slist_length(*msgnum_list);
3993 debug_print("Got %d uids from cache\n", g_slist_length(item->uid_list));
3995 for (elem = uidlist; elem != NULL; elem = g_slist_next(elem)) {
3998 msgnum = GPOINTER_TO_INT(elem->data);
3999 if (msgnum > lastuid_old) {
4000 *msgnum_list = g_slist_prepend(*msgnum_list, GINT_TO_POINTER(msgnum));
4001 item->uid_list = g_slist_prepend(item->uid_list, GINT_TO_POINTER(msgnum));
4004 if(msgnum > item->lastuid)
4005 item->lastuid = msgnum;
4008 g_slist_free(uidlist);
4011 return GINT_TO_POINTER(nummsgs);
4014 static gint get_list_of_uids(Folder *folder, IMAPFolderItem *item, GSList **msgnum_list)
4017 get_list_uid_data *data = g_new0(get_list_uid_data, 1);
4023 data->folder = folder;
4025 data->msgnum_list = msgnum_list;
4027 if (prefs_common.work_offline && !imap_gtk_should_override()) {
4032 #if (defined USE_PTHREAD && defined __GLIBC__ && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 3)))
4033 if (pthread_create(&pt, PTHREAD_CREATE_JOINABLE,
4034 get_list_of_uids_thread, data) != 0) {
4035 result = GPOINTER_TO_INT(get_list_of_uids_thread(data));
4039 debug_print("+++waiting for get_list_of_uids_thread...\n");
4040 while(!data->done) {
4041 /* don't let the interface freeze while waiting */
4044 debug_print("---get_list_of_uids_thread done\n");
4046 /* get the thread's return value and clean its resources */
4047 pthread_join(pt, &tmp);
4048 result = GPOINTER_TO_INT(tmp);
4050 result = GPOINTER_TO_INT(get_list_of_uids_thread(data));
4057 gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list, gboolean *old_uids_valid)
4059 IMAPFolderItem *item = (IMAPFolderItem *)_item;
4060 IMAPSession *session;
4061 gint ok, nummsgs = 0, exists, recent, uid_val, uid_next, unseen;
4062 GSList *uidlist = NULL;
4064 gboolean selected_folder;
4066 g_return_val_if_fail(folder != NULL, -1);
4067 g_return_val_if_fail(item != NULL, -1);
4068 g_return_val_if_fail(item->item.path != NULL, -1);
4069 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
4070 g_return_val_if_fail(folder->account != NULL, -1);
4072 session = imap_session_get(folder);
4073 g_return_val_if_fail(session != NULL, -1);
4075 selected_folder = (session->mbox != NULL) &&
4076 (!strcmp(session->mbox, item->item.path));
4077 if (selected_folder) {
4078 ok = imap_cmd_noop(session);
4079 if (ok != IMAP_SUCCESS)
4081 exists = session->exists;
4083 *old_uids_valid = TRUE;
4085 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
4086 &exists, &recent, &uid_next, &uid_val, &unseen, FALSE);
4087 if (ok != IMAP_SUCCESS)
4090 if(item->item.mtime == uid_val)
4091 *old_uids_valid = TRUE;
4093 *old_uids_valid = FALSE;
4095 debug_print("Freeing imap uid cache\n");
4097 g_slist_free(item->uid_list);
4098 item->uid_list = NULL;
4100 item->item.mtime = uid_val;
4102 imap_delete_all_cached_messages((FolderItem *)item);
4106 if (!selected_folder)
4107 item->uid_next = uid_next;
4109 /* If old uid_next matches new uid_next we can be sure no message
4110 was added to the folder */
4111 if (( selected_folder && !session->folder_content_changed) ||
4112 (!selected_folder && uid_next == item->uid_next)) {
4113 nummsgs = g_slist_length(item->uid_list);
4115 /* If number of messages is still the same we
4116 know our caches message numbers are still valid,
4117 otherwise if the number of messages has decrease
4118 we discard our cache to start a new scan to find
4119 out which numbers have been removed */
4120 if (exists == nummsgs) {
4121 *msgnum_list = g_slist_copy(item->uid_list);
4123 } else if (exists < nummsgs) {
4124 debug_print("Freeing imap uid cache");
4126 g_slist_free(item->uid_list);
4127 item->uid_list = NULL;
4132 *msgnum_list = NULL;
4136 nummsgs = get_list_of_uids(folder, item, &uidlist);
4138 if (nummsgs != exists) {
4139 /* Cache contains more messages then folder, we have cached
4140 an old UID of a message that was removed and new messages
4141 have been added too, otherwise the uid_next check would
4143 debug_print("Freeing imap uid cache");
4145 g_slist_free(item->uid_list);
4146 item->uid_list = NULL;
4148 g_slist_free(*msgnum_list);
4150 nummsgs = get_list_of_uids(folder, item, &uidlist);
4153 *msgnum_list = uidlist;
4155 dir = folder_item_get_path((FolderItem *)item);
4156 debug_print("removing old messages from %s\n", dir);
4157 remove_numbered_files_not_in_list(dir, *msgnum_list);
4163 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item)
4168 flags.perm_flags = MSG_NEW|MSG_UNREAD;
4169 flags.tmp_flags = 0;
4171 g_return_val_if_fail(item != NULL, NULL);
4172 g_return_val_if_fail(file != NULL, NULL);
4174 if (item->stype == F_QUEUE) {
4175 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
4176 } else if (item->stype == F_DRAFT) {
4177 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
4180 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
4181 if (!msginfo) return NULL;
4183 msginfo->folder = item;
4188 GSList *imap_get_msginfos(Folder *folder, FolderItem *item, GSList *msgnum_list)
4190 IMAPSession *session;
4191 MsgInfoList *ret = NULL;
4194 g_return_val_if_fail(folder != NULL, NULL);
4195 g_return_val_if_fail(item != NULL, NULL);
4196 g_return_val_if_fail(msgnum_list != NULL, NULL);
4198 session = imap_session_get(folder);
4199 g_return_val_if_fail(session != NULL, NULL);
4201 debug_print("-> getting msginfos\n");
4202 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
4203 NULL, NULL, NULL, NULL, FALSE);
4204 if (ok != IMAP_SUCCESS)
4207 if (!(item->stype == F_QUEUE || item->stype == F_DRAFT)) {
4208 ret = g_slist_concat(ret,
4209 imap_get_uncached_messages(
4210 session, item, msgnum_list));
4212 MsgNumberList *sorted_list, *elem;
4213 gint startnum, lastnum;
4215 sorted_list = g_slist_sort(g_slist_copy(msgnum_list), g_int_compare);
4217 startnum = lastnum = GPOINTER_TO_INT(sorted_list->data);
4219 for (elem = sorted_list;; elem = g_slist_next(elem)) {
4223 num = GPOINTER_TO_INT(elem->data);
4225 if (num > lastnum + 1 || elem == NULL) {
4227 for (i = startnum; i <= lastnum; ++i) {
4230 file = imap_fetch_msg(folder, item, i);
4232 MsgInfo *msginfo = imap_parse_msg(file, item);
4233 if (msginfo != NULL) {
4234 msginfo->msgnum = i;
4235 ret = g_slist_append(ret, msginfo);
4249 g_slist_free(sorted_list);
4255 MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid)
4257 MsgInfo *msginfo = NULL;
4258 MsgInfoList *msginfolist;
4259 MsgNumberList numlist;
4261 numlist.next = NULL;
4262 numlist.data = GINT_TO_POINTER(uid);
4264 msginfolist = imap_get_msginfos(folder, item, &numlist);
4265 if (msginfolist != NULL) {
4266 msginfo = msginfolist->data;
4267 g_slist_free(msginfolist);
4273 gboolean imap_scan_required(Folder *folder, FolderItem *_item)
4275 IMAPSession *session;
4276 IMAPFolderItem *item = (IMAPFolderItem *)_item;
4277 gint ok, exists = 0, recent = 0, unseen = 0;
4278 guint32 uid_next, uid_val = 0;
4279 gboolean selected_folder;
4281 g_return_val_if_fail(folder != NULL, FALSE);
4282 g_return_val_if_fail(item != NULL, FALSE);
4283 g_return_val_if_fail(item->item.folder != NULL, FALSE);
4284 g_return_val_if_fail(FOLDER_CLASS(item->item.folder) == &imap_class, FALSE);
4286 if (item->item.path == NULL)
4289 session = imap_session_get(folder);
4290 g_return_val_if_fail(session != NULL, FALSE);
4292 selected_folder = (session->mbox != NULL) &&
4293 (!strcmp(session->mbox, item->item.path));
4294 if (selected_folder) {
4295 ok = imap_cmd_noop(session);
4296 if (ok != IMAP_SUCCESS)
4299 if (session->folder_content_changed)
4302 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
4303 &exists, &recent, &uid_next, &uid_val, &unseen, FALSE);
4304 if (ok != IMAP_SUCCESS)
4307 if ((uid_next != item->uid_next) || (exists != item->item.total_msgs))
4314 static GHashTable *flags_set_table = NULL;
4315 static GHashTable *flags_unset_table = NULL;
4316 static gint hashtable_process_tag = -1;
4318 typedef struct _hashtable_data {
4319 IMAPSession *session;
4324 static gboolean process_flags(gpointer key, gpointer value, gpointer user_data)
4326 gboolean flags_set = GPOINTER_TO_INT(user_data);
4327 gint flags_value = GPOINTER_TO_INT(key);
4328 hashtable_data *data = (hashtable_data *)value;
4330 data->msglist = g_slist_reverse(data->msglist);
4332 imap_set_message_flags(data->session, data->msglist, flags_value, flags_set);
4334 if (flags_value == IMAP_FLAG_DELETED && flags_set == TRUE) {
4335 if (!data->session->uidplus) {
4336 imap_cmd_expunge(data->session, NULL);
4340 seq_list = imap_get_seq_set_from_numlist(data->msglist);
4341 imapset = get_seq_set_from_seq_list(seq_list);
4343 imap_cmd_expunge(data->session, imapset);
4348 g_slist_free(data->msglist);
4353 static FolderItem *last_deferred_item = NULL;
4354 static gboolean process_hashtable(void *data)
4356 FolderItem *item = (FolderItem *)data;
4357 debug_print("processing flags change hashtables\n");
4358 if (flags_set_table) {
4359 g_hash_table_foreach_remove(flags_set_table, process_flags, GINT_TO_POINTER(TRUE));
4360 g_free(flags_set_table);
4361 flags_set_table = NULL;
4363 if (flags_unset_table) {
4364 g_hash_table_foreach_remove(flags_unset_table, process_flags, GINT_TO_POINTER(FALSE));
4365 g_free(flags_unset_table);
4366 flags_unset_table = NULL;
4369 folder_item_scan_full(item, FALSE);
4370 hashtable_process_tag = -1;
4374 void imap_change_flags(Folder *folder, FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
4376 IMAPSession *session;
4377 IMAPFlags flags_set = 0, flags_unset = 0;
4378 gint ok = IMAP_SUCCESS;
4379 hashtable_data *ht_data = NULL;
4381 g_return_if_fail(folder != NULL);
4382 g_return_if_fail(folder->klass == &imap_class);
4383 g_return_if_fail(item != NULL);
4384 g_return_if_fail(item->folder == folder);
4385 g_return_if_fail(msginfo != NULL);
4386 g_return_if_fail(msginfo->folder == item);
4388 session = imap_session_get(folder);
4389 if (!session) return;
4391 debug_print("-> changing flags\n");
4393 if (hashtable_process_tag != -1 && item != last_deferred_item
4394 && last_deferred_item != NULL) {
4395 debug_print("forcing flush for %s (!= %s)\n",
4396 last_deferred_item->path,
4398 gtk_timeout_remove(hashtable_process_tag);
4399 process_hashtable(last_deferred_item);
4400 hashtable_process_tag = -1;
4402 last_deferred_item = item;
4404 if ((ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
4405 NULL, NULL, NULL, NULL, FALSE)) != IMAP_SUCCESS)
4408 if (!flags_set_table) {
4409 flags_set_table = g_hash_table_new(NULL, g_direct_equal);
4411 if (!flags_unset_table) {
4412 flags_unset_table = g_hash_table_new(NULL, g_direct_equal);
4415 if (!MSG_IS_MARKED(msginfo->flags) && (newflags & MSG_MARKED))
4416 flags_set |= IMAP_FLAG_FLAGGED;
4417 if ( MSG_IS_MARKED(msginfo->flags) && !(newflags & MSG_MARKED))
4418 flags_unset |= IMAP_FLAG_FLAGGED;
4420 if (!MSG_IS_UNREAD(msginfo->flags) && (newflags & MSG_UNREAD))
4421 flags_unset |= IMAP_FLAG_SEEN;
4422 if ( MSG_IS_UNREAD(msginfo->flags) && !(newflags & MSG_UNREAD))
4423 flags_set |= IMAP_FLAG_SEEN;
4425 if (!MSG_IS_REPLIED(msginfo->flags) && (newflags & MSG_REPLIED))
4426 flags_set |= IMAP_FLAG_ANSWERED;
4427 if ( MSG_IS_REPLIED(msginfo->flags) && !(newflags & MSG_REPLIED))
4428 flags_set |= IMAP_FLAG_ANSWERED;
4430 /* instead of performing an UID STORE command for each message change,
4431 * as a lot of them can change "together", we just fill in hashtables
4432 * and defer the treatment 100ms so that we're able to send only one
4436 ht_data = g_hash_table_lookup(flags_set_table, GINT_TO_POINTER(flags_set));
4437 if (ht_data == NULL) {
4438 ht_data = g_new0(hashtable_data, 1);
4439 ht_data->session = session;
4440 g_hash_table_insert(flags_set_table, GINT_TO_POINTER(flags_set), ht_data);
4442 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
4443 ht_data->msglist = g_slist_prepend(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum));
4447 ht_data = g_hash_table_lookup(flags_unset_table, GINT_TO_POINTER(flags_unset));
4448 if (ht_data == NULL) {
4449 ht_data = g_new0(hashtable_data, 1);
4450 ht_data->session = session;
4451 g_hash_table_insert(flags_unset_table, GINT_TO_POINTER(flags_unset), ht_data);
4453 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
4454 ht_data->msglist = g_slist_prepend(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum));
4457 msginfo->flags.perm_flags = newflags;
4459 if (hashtable_process_tag != -1)
4460 gtk_timeout_remove(hashtable_process_tag);
4462 hashtable_process_tag = gtk_timeout_add(100, process_hashtable, item);
4467 static gint imap_remove_msg(Folder *folder, FolderItem *item, gint uid)
4470 IMAPSession *session;
4472 MsgNumberList numlist;
4473 hashtable_data *ht_data;
4475 if (!flags_set_table) {
4476 flags_set_table = g_hash_table_new(NULL, g_direct_equal);
4478 if (!flags_unset_table) {
4479 flags_unset_table = g_hash_table_new(NULL, g_direct_equal);
4482 g_return_val_if_fail(folder != NULL, -1);
4483 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
4484 g_return_val_if_fail(item != NULL, -1);
4486 session = imap_session_get(folder);
4487 if (!session) return -1;
4489 if (hashtable_process_tag != -1 && item != last_deferred_item
4490 && last_deferred_item != NULL) {
4491 debug_print("forcing flush for %s (!= %s)\n",
4492 last_deferred_item->path,
4494 gtk_timeout_remove(hashtable_process_tag);
4495 process_hashtable(last_deferred_item);
4496 hashtable_process_tag = -1;
4498 last_deferred_item = item;
4500 debug_print("-> removing messages\n");
4501 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
4502 NULL, NULL, NULL, NULL, FALSE);
4503 if (ok != IMAP_SUCCESS)
4506 ht_data = g_hash_table_lookup(flags_set_table, GINT_TO_POINTER(IMAP_FLAG_DELETED));
4507 if (ht_data == NULL) {
4508 ht_data = g_new0(hashtable_data, 1);
4509 ht_data->session = session;
4510 g_hash_table_insert(flags_set_table, GINT_TO_POINTER(IMAP_FLAG_DELETED), ht_data);
4512 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(uid)))
4513 ht_data->msglist = g_slist_prepend(ht_data->msglist,
4514 GINT_TO_POINTER(uid));
4516 if (hashtable_process_tag != -1)
4517 gtk_timeout_remove(hashtable_process_tag);
4519 hashtable_process_tag = gtk_timeout_add(100, process_hashtable, item);
4521 IMAP_FOLDER_ITEM(item)->uid_list = g_slist_remove(
4522 IMAP_FOLDER_ITEM(item)->uid_list, numlist.data);
4523 dir = folder_item_get_path(item);
4524 if (is_dir_exist(dir))
4525 remove_numbered_files(dir, uid, uid);
4528 return IMAP_SUCCESS;
4531 static gint compare_msginfo(gconstpointer a, gconstpointer b)
4533 return ((MsgInfo *)a)->msgnum - ((MsgInfo *)b)->msgnum;
4536 static guint gslist_find_next_num(MsgNumberList **list, guint num)
4540 g_return_val_if_fail(list != NULL, -1);
4542 for (elem = *list; elem != NULL; elem = g_slist_next(elem))
4543 if (GPOINTER_TO_INT(elem->data) >= num)
4546 return elem != NULL ? GPOINTER_TO_INT(elem->data) : (gint)-1;
4550 * NEW and DELETED flags are not syncronized
4551 * - The NEW/RECENT flags in IMAP folders can not really be directly
4552 * modified by Sylpheed
4553 * - The DELETE/DELETED flag in IMAP and Sylpheed don't have the same
4554 * meaning, in IMAP it always removes the messages from the FolderItem
4555 * in Sylpheed it can mean to move the message to trash
4558 typedef struct _get_flags_data {
4561 MsgInfoList *msginfo_list;
4562 GRelation *msgflags;
4566 static /*gint*/ void *imap_get_flags_thread(void *data)
4568 get_flags_data *stuff = (get_flags_data *)data;
4569 Folder *folder = stuff->folder;
4570 FolderItem *item = stuff->item;
4571 MsgInfoList *msginfo_list = stuff->msginfo_list;
4572 GRelation *msgflags = stuff->msgflags;
4573 IMAPSession *session;
4574 GSList *sorted_list;
4575 GSList *unseen = NULL, *answered = NULL, *flagged = NULL;
4576 GSList *p_unseen, *p_answered, *p_flagged;
4578 GSList *seq_list, *cur;
4579 gboolean reverse_seen = FALSE;
4582 gint exists_cnt, recent_cnt, unseen_cnt, uid_next;
4583 guint32 uidvalidity;
4584 gboolean selected_folder;
4586 if (folder == NULL || item == NULL) {
4588 return GINT_TO_POINTER(-1);
4590 if (msginfo_list == NULL) {
4592 return GINT_TO_POINTER(0);
4595 session = imap_session_get(folder);
4596 if (session == NULL) {
4598 return GINT_TO_POINTER(-1);
4601 selected_folder = (session->mbox != NULL) &&
4602 (!strcmp(session->mbox, item->path));
4604 if (!selected_folder) {
4605 ok = imap_status(session, IMAP_FOLDER(folder), item->path,
4606 &exists_cnt, &recent_cnt, &uid_next, &uidvalidity, &unseen_cnt, TRUE);
4607 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
4608 NULL, NULL, NULL, NULL, TRUE);
4609 if (ok != IMAP_SUCCESS) {
4611 return GINT_TO_POINTER(-1);
4616 if (unseen_cnt > exists_cnt / 2)
4617 reverse_seen = TRUE;
4619 cmd_buf = g_string_new(NULL);
4621 sorted_list = g_slist_sort(g_slist_copy(msginfo_list), compare_msginfo);
4623 seq_list = imap_get_seq_set_from_msglist(msginfo_list);
4625 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
4626 IMAPSet imapset = cur->data;
4628 g_string_sprintf(cmd_buf, "%sSEEN UID %s", reverse_seen ? "" : "UN", imapset);
4629 imap_cmd_search(session, cmd_buf->str, &p_unseen, TRUE);
4630 unseen = g_slist_concat(unseen, p_unseen);
4632 g_string_sprintf(cmd_buf, "ANSWERED UID %s", imapset);
4633 imap_cmd_search(session, cmd_buf->str, &p_answered, TRUE);
4634 answered = g_slist_concat(answered, p_answered);
4636 g_string_sprintf(cmd_buf, "FLAGGED UID %s", imapset);
4637 imap_cmd_search(session, cmd_buf->str, &p_flagged, TRUE);
4638 flagged = g_slist_concat(flagged, p_flagged);
4642 p_answered = answered;
4643 p_flagged = flagged;
4645 for (elem = sorted_list; elem != NULL; elem = g_slist_next(elem)) {
4650 msginfo = (MsgInfo *) elem->data;
4651 flags = msginfo->flags.perm_flags;
4652 wasnew = (flags & MSG_NEW);
4653 flags &= ~((reverse_seen ? 0 : MSG_UNREAD | MSG_NEW) | MSG_REPLIED | MSG_MARKED);
4655 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
4656 if (gslist_find_next_num(&p_unseen, msginfo->msgnum) == msginfo->msgnum) {
4657 if (!reverse_seen) {
4658 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
4660 flags &= ~(MSG_UNREAD | MSG_NEW);
4663 if (gslist_find_next_num(&p_answered, msginfo->msgnum) == msginfo->msgnum)
4664 flags |= MSG_REPLIED;
4665 if (gslist_find_next_num(&p_flagged, msginfo->msgnum) == msginfo->msgnum)
4666 flags |= MSG_MARKED;
4667 g_relation_insert(msgflags, msginfo, GINT_TO_POINTER(flags));
4670 imap_seq_set_free(seq_list);
4671 g_slist_free(flagged);
4672 g_slist_free(answered);
4673 g_slist_free(unseen);
4674 g_slist_free(sorted_list);
4675 g_string_free(cmd_buf, TRUE);
4678 return GINT_TO_POINTER(0);
4681 static gint imap_get_flags(Folder *folder, FolderItem *item,
4682 MsgInfoList *msginfo_list, GRelation *msgflags)
4685 get_flags_data *data = g_new0(get_flags_data, 1);
4691 data->folder = folder;
4693 data->msginfo_list = msginfo_list;
4694 data->msgflags = msgflags;
4696 if (prefs_common.work_offline && !imap_gtk_should_override()) {
4701 #if (defined USE_PTHREAD && defined __GLIBC__ && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 3)))
4702 if (pthread_create(&pt, PTHREAD_CREATE_JOINABLE,
4703 imap_get_flags_thread, data) != 0) {
4704 result = GPOINTER_TO_INT(imap_get_flags_thread(data));
4708 debug_print("+++waiting for imap_get_flags_thread...\n");
4709 while(!data->done) {
4710 /* don't let the interface freeze while waiting */
4713 debug_print("---imap_get_flags_thread done\n");
4715 /* get the thread's return value and clean its resources */
4716 pthread_join(pt, &tmp);
4717 result = GPOINTER_TO_INT(tmp);
4719 result = GPOINTER_TO_INT(imap_get_flags_thread(data));