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>
51 #include "procheader.h"
52 #include "prefs_account.h"
57 #include "prefs_common.h"
58 #include "inputdialog.h"
60 #include "remotefolder.h"
61 #include "alertpanel.h"
63 #include "statusbar.h"
68 static pthread_mutex_t imap_mutex;
69 static const char *mutex_hold = NULL;
72 #define __FUNCTION__ __FILE__
75 #define MUTEX_TRYLOCK_OR_RETURN() { \
76 debug_print("%s: locking mutex\n", __FUNCTION__); \
77 if (pthread_mutex_trylock(&imap_mutex) == EBUSY) { \
78 g_warning("can't lock mutex (held by %s)\n", \
79 mutex_hold ? mutex_hold:"(nil)"); \
82 mutex_hold = __FUNCTION__; \
85 #define MUTEX_TRYLOCK_OR_RETURN_VAL(retval) { \
86 debug_print("%s: locking mutex\n", __FUNCTION__); \
87 if (pthread_mutex_trylock(&imap_mutex) == EBUSY) { \
88 g_warning("can't lock mutex (held by %s)\n", \
89 mutex_hold ? mutex_hold:"(nil)"); \
92 mutex_hold = __FUNCTION__; \
95 #define MUTEX_UNLOCK() { \
96 debug_print("%s: unlocking mutex\n", __FUNCTION__);\
97 pthread_mutex_unlock(&imap_mutex); \
102 #define MUTEX_TRYLOCK_OR_RETURN() do {} while(0)
103 #define MUTEX_TRYLOCK_OR_RETURN_VAL(retval) do {} while(0)
104 #define MUTEX_UNLOCK() do {} while(0)
107 typedef struct _IMAPFolder IMAPFolder;
108 typedef struct _IMAPSession IMAPSession;
109 typedef struct _IMAPNameSpace IMAPNameSpace;
110 typedef struct _IMAPFolderItem IMAPFolderItem;
112 #include "prefs_account.h"
114 #define IMAP_FOLDER(obj) ((IMAPFolder *)obj)
115 #define IMAP_FOLDER_ITEM(obj) ((IMAPFolderItem *)obj)
116 #define IMAP_SESSION(obj) ((IMAPSession *)obj)
120 RemoteFolder rfolder;
122 /* list of IMAPNameSpace */
132 gboolean authenticated;
141 gboolean folder_content_changed;
145 struct _IMAPNameSpace
151 #define IMAP_SUCCESS 0
152 #define IMAP_SOCKET 2
153 #define IMAP_AUTHFAIL 3
154 #define IMAP_PROTOCOL 4
155 #define IMAP_SYNTAX 5
159 #define IMAPBUFSIZE 8192
163 IMAP_FLAG_SEEN = 1 << 0,
164 IMAP_FLAG_ANSWERED = 1 << 1,
165 IMAP_FLAG_FLAGGED = 1 << 2,
166 IMAP_FLAG_DELETED = 1 << 3,
167 IMAP_FLAG_DRAFT = 1 << 4
170 #define IMAP_IS_SEEN(flags) ((flags & IMAP_FLAG_SEEN) != 0)
171 #define IMAP_IS_ANSWERED(flags) ((flags & IMAP_FLAG_ANSWERED) != 0)
172 #define IMAP_IS_FLAGGED(flags) ((flags & IMAP_FLAG_FLAGGED) != 0)
173 #define IMAP_IS_DELETED(flags) ((flags & IMAP_FLAG_DELETED) != 0)
174 #define IMAP_IS_DRAFT(flags) ((flags & IMAP_FLAG_DRAFT) != 0)
177 #define IMAP4_PORT 143
179 #define IMAPS_PORT 993
182 #define IMAP_CMD_LIMIT 1000
184 #define QUOTE_IF_REQUIRED(out, str) \
186 if (*str != '"' && strpbrk(str, " \t(){}[]%*\\") != NULL) { \
190 len = strlen(str) + 3; \
191 Xalloca(__tmp, len, return IMAP_ERROR); \
192 g_snprintf(__tmp, len, "\"%s\"", str); \
195 Xstrdup_a(out, str, return IMAP_ERROR); \
199 typedef gchar * IMAPSet;
201 struct _IMAPFolderItem
211 static void imap_folder_init (Folder *folder,
215 static Folder *imap_folder_new (const gchar *name,
217 static void imap_folder_destroy (Folder *folder);
219 static IMAPSession *imap_session_new (const PrefsAccount *account);
220 static void imap_session_authenticate(IMAPSession *session,
221 const PrefsAccount *account);
222 static void imap_session_destroy (Session *session);
224 static gchar *imap_fetch_msg (Folder *folder,
227 static gchar *imap_fetch_msg_full (Folder *folder,
232 static gint imap_add_msg (Folder *folder,
236 static gint imap_add_msgs (Folder *folder,
239 GRelation *relation);
241 static gint imap_copy_msg (Folder *folder,
244 static gint imap_copy_msgs (Folder *folder,
246 MsgInfoList *msglist,
247 GRelation *relation);
249 static gint imap_remove_msg (Folder *folder,
252 static gint imap_remove_msgs (Folder *folder,
254 MsgInfoList *msglist,
255 GRelation *relation);
256 static gint imap_remove_all_msg (Folder *folder,
259 static gboolean imap_is_msg_changed (Folder *folder,
263 static gint imap_close (Folder *folder,
266 static gint imap_scan_tree (Folder *folder);
268 static gint imap_create_tree (Folder *folder);
270 static FolderItem *imap_create_folder (Folder *folder,
273 static gint imap_rename_folder (Folder *folder,
276 static gint imap_remove_folder (Folder *folder,
279 static FolderItem *imap_folder_item_new (Folder *folder);
280 static void imap_folder_item_destroy (Folder *folder,
283 static IMAPSession *imap_session_get (Folder *folder);
285 static gint imap_greeting (IMAPSession *session);
286 static gint imap_auth (IMAPSession *session,
291 static gint imap_scan_tree_recursive (IMAPSession *session,
293 static GSList *imap_parse_list (IMAPFolder *folder,
294 IMAPSession *session,
295 const gchar *real_path,
298 static void imap_create_missing_folders (Folder *folder);
299 static FolderItem *imap_create_special_folder
301 SpecialFolderItemType stype,
304 static gint imap_do_copy_msgs (Folder *folder,
306 MsgInfoList *msglist,
307 GRelation *relation);
309 static void imap_delete_all_cached_messages (FolderItem *item);
310 static void imap_set_batch (Folder *folder,
314 static SockInfo *imap_open (const gchar *server,
318 static SockInfo *imap_open (const gchar *server,
323 static SockInfo *imap_open_tunnel(const gchar *server,
324 const gchar *tunnelcmd,
327 static SockInfo *imap_open_tunnel(const gchar *server,
328 const gchar *tunnelcmd);
332 static SockInfo *imap_init_sock(SockInfo *sock, SSLType ssl_type);
334 static SockInfo *imap_init_sock(SockInfo *sock);
337 static gchar *imap_get_flag_str (IMAPFlags flags);
338 static gint imap_set_message_flags (IMAPSession *session,
339 MsgNumberList *numlist,
342 static gint imap_select (IMAPSession *session,
348 guint32 *uid_validity,
350 static gint imap_status (IMAPSession *session,
356 guint32 *uid_validity,
360 static void imap_parse_namespace (IMAPSession *session,
362 static void imap_get_namespace_by_list (IMAPSession *session,
364 static IMAPNameSpace *imap_find_namespace (IMAPFolder *folder,
366 static gchar imap_get_path_separator (IMAPFolder *folder,
368 static gchar *imap_get_real_path (IMAPFolder *folder,
371 static gchar *imap_parse_atom (SockInfo *sock,
376 static MsgFlags imap_parse_flags (const gchar *flag_str);
377 static MsgInfo *imap_parse_envelope (SockInfo *sock,
381 static gboolean imap_has_capability (IMAPSession *session,
383 static void imap_free_capabilities (IMAPSession *session);
385 /* low-level IMAP4rev1 commands */
386 static gint imap_cmd_authenticate
387 (IMAPSession *session,
391 static gint imap_cmd_login (IMAPSession *session,
394 static gint imap_cmd_logout (IMAPSession *session);
395 static gint imap_cmd_noop (IMAPSession *session);
397 static gint imap_cmd_starttls (IMAPSession *session);
399 static gint imap_cmd_namespace (IMAPSession *session,
401 static gint imap_cmd_list (IMAPSession *session,
403 const gchar *mailbox,
405 static gint imap_cmd_do_select (IMAPSession *session,
411 guint32 *uid_validity,
413 static gint imap_cmd_select (IMAPSession *session,
418 guint32 *uid_validity,
420 static gint imap_cmd_examine (IMAPSession *session,
425 guint32 *uid_validity,
427 static gint imap_cmd_create (IMAPSession *sock,
428 const gchar *folder);
429 static gint imap_cmd_rename (IMAPSession *sock,
430 const gchar *oldfolder,
431 const gchar *newfolder);
432 static gint imap_cmd_delete (IMAPSession *session,
433 const gchar *folder);
434 static gint imap_cmd_envelope (IMAPSession *session,
436 static gint imap_cmd_fetch (IMAPSession *sock,
438 const gchar *filename,
441 static gint imap_cmd_append (IMAPSession *session,
442 const gchar *destfolder,
446 static gint imap_cmd_copy (IMAPSession *session,
447 const gchar *seq_set,
448 const gchar *destfolder,
449 GRelation *uid_mapping);
450 static gint imap_cmd_store (IMAPSession *session,
453 static gint imap_cmd_expunge (IMAPSession *session,
455 static gint imap_cmd_close (IMAPSession *session);
457 static gint imap_cmd_ok (IMAPSession *session,
459 static gint imap_cmd_ok_block (IMAPSession *session,
461 static gint imap_cmd_ok_with_block
462 (IMAPSession *session,
465 static void imap_gen_send (IMAPSession *session,
466 const gchar *format, ...);
467 static gint imap_gen_recv (IMAPSession *session,
469 static gint imap_gen_recv_block (IMAPSession *session,
471 static gint imap_gen_recv_with_block
472 (IMAPSession *session,
476 /* misc utility functions */
477 static gchar *strchr_cpy (const gchar *src,
481 static gchar *get_quoted (const gchar *src,
485 static gchar *search_array_contain_str (GPtrArray *array,
487 static gchar *search_array_str (GPtrArray *array,
489 static void imap_path_separator_subst (gchar *str,
492 static gchar *imap_utf8_to_modified_utf7 (const gchar *from);
493 static gchar *imap_modified_utf7_to_utf8 (const gchar *mutf7_str);
495 static GSList *imap_get_seq_set_from_numlist (MsgNumberList *msglist);
496 static GSList *imap_get_seq_set_from_msglist (MsgInfoList *msglist);
497 static void imap_seq_set_free (GSList *seq_list);
499 static gboolean imap_rename_folder_func (GNode *node,
501 static gint imap_get_num_list (Folder *folder,
504 gboolean *old_uids_valid);
505 static GSList *imap_get_msginfos (Folder *folder,
507 GSList *msgnum_list);
508 static MsgInfo *imap_get_msginfo (Folder *folder,
511 static gboolean imap_scan_required (Folder *folder,
513 static void imap_change_flags (Folder *folder,
516 MsgPermFlags newflags);
517 static gint imap_get_flags (Folder *folder,
519 MsgInfoList *msglist,
520 GRelation *msgflags);
521 static gchar *imap_folder_get_path (Folder *folder);
522 static gchar *imap_item_get_path (Folder *folder,
524 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item);
525 static GHashTable *flags_set_table = NULL;
526 static GHashTable *flags_unset_table = NULL;
527 typedef struct _hashtable_data {
528 IMAPSession *session;
532 static FolderClass imap_class;
534 typedef struct _thread_data {
545 void *imap_getline_thread(void *data)
547 thread_data *td = (thread_data *)data;
550 line = sock_getline(td->sock);
558 /* imap_getline just wraps sock_getline inside a thread,
559 * performing gtk updates so that the interface isn't frozen.
561 static gchar *imap_getline(SockInfo *sock)
563 #if (defined USE_PTHREAD && defined __GLIBC__ && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 3)))
564 thread_data *td = g_new0(thread_data, 1);
569 if (pthread_create(&pt, PTHREAD_CREATE_JOINABLE,
570 imap_getline_thread, td) != 0) {
572 return sock_getline(sock);
575 debug_print("+++waiting for imap_getline_thread...\n");
577 /* don't let the interface freeze while waiting */
580 debug_print("---imap_getline_thread done\n");
582 /* get the thread's return value and clean its resources */
583 pthread_join(pt, (void *)&line);
588 return sock_getline(sock);
592 FolderClass *imap_get_class(void)
594 if (imap_class.idstr == NULL) {
595 imap_class.type = F_IMAP;
596 imap_class.idstr = "imap";
597 imap_class.uistr = "IMAP4";
599 /* Folder functions */
600 imap_class.new_folder = imap_folder_new;
601 imap_class.destroy_folder = imap_folder_destroy;
602 imap_class.scan_tree = imap_scan_tree;
603 imap_class.create_tree = imap_create_tree;
605 /* FolderItem functions */
606 imap_class.item_new = imap_folder_item_new;
607 imap_class.item_destroy = imap_folder_item_destroy;
608 imap_class.item_get_path = imap_item_get_path;
609 imap_class.create_folder = imap_create_folder;
610 imap_class.rename_folder = imap_rename_folder;
611 imap_class.remove_folder = imap_remove_folder;
612 imap_class.close = imap_close;
613 imap_class.get_num_list = imap_get_num_list;
614 imap_class.scan_required = imap_scan_required;
616 /* Message functions */
617 imap_class.get_msginfo = imap_get_msginfo;
618 imap_class.get_msginfos = imap_get_msginfos;
619 imap_class.fetch_msg = imap_fetch_msg;
620 imap_class.fetch_msg_full = imap_fetch_msg_full;
621 imap_class.add_msg = imap_add_msg;
622 imap_class.add_msgs = imap_add_msgs;
623 imap_class.copy_msg = imap_copy_msg;
624 imap_class.copy_msgs = imap_copy_msgs;
625 imap_class.remove_msg = imap_remove_msg;
626 imap_class.remove_msgs = imap_remove_msgs;
627 imap_class.remove_all_msg = imap_remove_all_msg;
628 imap_class.is_msg_changed = imap_is_msg_changed;
629 imap_class.change_flags = imap_change_flags;
630 imap_class.get_flags = imap_get_flags;
631 imap_class.set_batch = imap_set_batch;
633 pthread_mutex_init(&imap_mutex, NULL);
640 static gchar *get_seq_set_from_seq_list(GSList *seq_list)
646 if (seq_list != NULL) {
648 val = g_strdup((gchar *)(cur->data));
650 while (cur != NULL) {
652 val = g_strconcat(tmp, ",", (gchar *)(cur->data),
662 static Folder *imap_folder_new(const gchar *name, const gchar *path)
666 folder = (Folder *)g_new0(IMAPFolder, 1);
667 folder->klass = &imap_class;
668 imap_folder_init(folder, name, path);
673 static void imap_folder_destroy(Folder *folder)
677 dir = imap_folder_get_path(folder);
678 if (is_dir_exist(dir))
679 remove_dir_recursive(dir);
682 folder_remote_folder_destroy(REMOTE_FOLDER(folder));
685 static void imap_folder_init(Folder *folder, const gchar *name,
688 folder_remote_folder_init((Folder *)folder, name, path);
691 static FolderItem *imap_folder_item_new(Folder *folder)
693 IMAPFolderItem *item;
695 item = g_new0(IMAPFolderItem, 1);
698 item->uid_list = NULL;
700 return (FolderItem *)item;
703 static void imap_folder_item_destroy(Folder *folder, FolderItem *_item)
705 IMAPFolderItem *item = (IMAPFolderItem *)_item;
707 g_return_if_fail(item != NULL);
708 g_slist_free(item->uid_list);
713 static gboolean imap_reset_uid_lists_func(GNode *node, gpointer data)
715 IMAPFolderItem *item = (IMAPFolderItem *)node->data;
719 g_slist_free(item->uid_list);
720 item->uid_list = NULL;
725 static void imap_reset_uid_lists(Folder *folder)
727 if(folder->node == NULL)
730 /* Destroy all uid lists and rest last uid */
731 g_node_traverse(folder->node, G_IN_ORDER, G_TRAVERSE_ALL, -1, imap_reset_uid_lists_func, NULL);
734 /* Send CAPABILITY, and examine the server's response to see whether this
735 * connection is pre-authenticated or not and build a list of CAPABILITIES. */
736 static gint imap_greeting(IMAPSession *session)
741 imap_gen_send(session, "CAPABILITY");
743 argbuf = g_ptr_array_new();
745 if (imap_cmd_ok(session, argbuf) != IMAP_SUCCESS ||
746 ((capstr = search_array_str(argbuf, "CAPABILITY ")) == NULL)) {
747 ptr_array_free_strings(argbuf);
748 g_ptr_array_free(argbuf, TRUE);
752 session->authenticated = search_array_str(argbuf, "PREAUTH") != NULL;
754 capstr += strlen("CAPABILITY ");
756 IMAP_SESSION(session)->capability = g_strsplit(capstr, " ", 0);
758 ptr_array_free_strings(argbuf);
759 g_ptr_array_free(argbuf, TRUE);
761 if (imap_has_capability(session, "UIDPLUS"))
762 session->uidplus = TRUE;
767 static gint imap_auth(IMAPSession *session, const gchar *user, const gchar *pass,
772 if (type == 0 || type == IMAP_AUTH_LOGIN)
773 ok = imap_cmd_login(session, user, pass);
775 ok = imap_cmd_authenticate(session, user, pass, type);
777 if (ok == IMAP_SUCCESS)
778 session->authenticated = TRUE;
783 static IMAPSession *imap_session_get(Folder *folder)
785 RemoteFolder *rfolder = REMOTE_FOLDER(folder);
786 IMAPSession *session = NULL;
788 g_return_val_if_fail(folder != NULL, NULL);
789 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, NULL);
790 g_return_val_if_fail(folder->account != NULL, NULL);
792 if (prefs_common.work_offline && !imap_gtk_should_override()) {
796 /* Make sure we have a session */
797 if (rfolder->session != NULL) {
798 session = IMAP_SESSION(rfolder->session);
800 imap_reset_uid_lists(folder);
801 session = imap_session_new(folder->account);
806 if (SESSION(session)->sock->state == CONN_DISCONNECTED) {
807 debug_print("IMAP server disconnected\n");
808 session_destroy(SESSION(session));
809 imap_reset_uid_lists(folder);
810 session = imap_session_new(folder->account);
813 /* Make sure session is authenticated */
814 if (!IMAP_SESSION(session)->authenticated)
815 imap_session_authenticate(IMAP_SESSION(session), folder->account);
816 if (!IMAP_SESSION(session)->authenticated) {
817 session_destroy(SESSION(session));
818 rfolder->session = NULL;
822 /* Make sure we have parsed the IMAP namespace */
823 imap_parse_namespace(IMAP_SESSION(session),
824 IMAP_FOLDER(folder));
826 /* I think the point of this code is to avoid sending a
827 * keepalive if we've used the session recently and therefore
828 * think it's still alive. Unfortunately, most of the code
829 * does not yet check for errors on the socket, and so if the
830 * connection drops we don't notice until the timeout expires.
831 * A better solution than sending a NOOP every time would be
832 * for every command to be prepared to retry until it is
833 * successfully sent. -- mbp */
834 if (time(NULL) - SESSION(session)->last_access_time > SESSION_TIMEOUT_INTERVAL) {
835 /* verify that the session is still alive */
836 if (imap_cmd_noop(session) != IMAP_SUCCESS) {
837 /* Check if this is the first try to establish a
838 connection, if yes we don't try to reconnect */
839 if (rfolder->session == NULL) {
840 log_warning(_("Connecting to %s failed"),
841 folder->account->recv_server);
842 session_destroy(SESSION(session));
845 log_warning(_("IMAP4 connection to %s has been"
846 " disconnected. Reconnecting...\n"),
847 folder->account->recv_server);
848 statusbar_print_all(_("IMAP4 connection to %s has been"
849 " disconnected. Reconnecting...\n"),
850 folder->account->recv_server);
851 session_destroy(SESSION(session));
852 /* Clear folders session to make imap_session_get create
853 a new session, because of rfolder->session == NULL
854 it will not try to reconnect again and so avoid an
856 rfolder->session = NULL;
857 session = imap_session_get(folder);
863 rfolder->session = SESSION(session);
865 return IMAP_SESSION(session);
868 static IMAPSession *imap_session_new(const PrefsAccount *account)
870 IMAPSession *session;
875 /* FIXME: IMAP over SSL only... */
878 port = account->set_imapport ? account->imapport
879 : account->ssl_imap == SSL_TUNNEL ? IMAPS_PORT : IMAP4_PORT;
880 ssl_type = account->ssl_imap;
882 port = account->set_imapport ? account->imapport
886 if (account->set_tunnelcmd) {
887 log_message(_("creating tunneled IMAP4 connection\n"));
889 if ((imap_sock = imap_open_tunnel(account->recv_server,
893 if ((imap_sock = imap_open_tunnel(account->recv_server,
894 account->tunnelcmd)) == NULL)
898 g_return_val_if_fail(account->recv_server != NULL, NULL);
900 log_message(_("creating IMAP4 connection to %s:%d ...\n"),
901 account->recv_server, port);
904 if ((imap_sock = imap_open(account->recv_server, port,
907 if ((imap_sock = imap_open(account->recv_server, port)) == NULL)
912 session = g_new0(IMAPSession, 1);
913 session_init(SESSION(session));
914 SESSION(session)->type = SESSION_IMAP;
915 SESSION(session)->server = g_strdup(account->recv_server);
916 SESSION(session)->sock = imap_sock;
918 SESSION(session)->destroy = imap_session_destroy;
920 session->capability = NULL;
922 session->authenticated = FALSE;
923 session->mbox = NULL;
924 session->cmd_count = 0;
926 /* Only need to log in if the connection was not PREAUTH */
927 if (imap_greeting(session) != IMAP_SUCCESS) {
928 session_destroy(SESSION(session));
933 if (account->ssl_imap == SSL_STARTTLS &&
934 imap_has_capability(session, "STARTTLS")) {
937 ok = imap_cmd_starttls(session);
938 if (ok != IMAP_SUCCESS) {
939 log_warning(_("Can't start TLS session.\n"));
940 session_destroy(SESSION(session));
943 if (!ssl_init_socket_with_method(SESSION(session)->sock,
945 session_destroy(SESSION(session));
949 imap_free_capabilities(session);
950 session->authenticated = FALSE;
951 session->uidplus = FALSE;
952 session->cmd_count = 1;
954 if (imap_greeting(session) != IMAP_SUCCESS) {
955 session_destroy(SESSION(session));
960 log_message("IMAP connection is %s-authenticated\n",
961 (session->authenticated) ? "pre" : "un");
966 static void imap_session_authenticate(IMAPSession *session,
967 const PrefsAccount *account)
971 g_return_if_fail(account->userid != NULL);
973 pass = account->passwd;
976 tmp_pass = input_dialog_query_password(account->recv_server, account->userid);
979 Xstrdup_a(pass, tmp_pass, {g_free(tmp_pass); return;});
982 statusbar_print_all(_("Connecting to IMAP4 server %s...\n"),
983 account->recv_server);
984 if (imap_auth(session, account->userid, pass, account->imap_auth_type) != IMAP_SUCCESS) {
985 imap_cmd_logout(session);
991 session->authenticated = TRUE;
994 static void imap_session_destroy(Session *session)
996 imap_free_capabilities(IMAP_SESSION(session));
997 g_free(IMAP_SESSION(session)->mbox);
998 sock_close(session->sock);
999 session->sock = NULL;
1002 static gchar *imap_fetch_msg(Folder *folder, FolderItem *item, gint uid)
1004 return imap_fetch_msg_full(folder, item, uid, TRUE, TRUE);
1007 static guint get_size_with_lfs(MsgInfo *info)
1016 fp = procmsg_open_message(info);
1020 while (fgets(buf, sizeof (buf), fp) != NULL) {
1022 if (!strstr(buf, "\r") && strstr(buf, "\n"))
1030 static gchar *imap_fetch_msg_full(Folder *folder, FolderItem *item, gint uid,
1031 gboolean headers, gboolean body)
1033 gchar *path, *filename;
1034 IMAPSession *session;
1037 g_return_val_if_fail(folder != NULL, NULL);
1038 g_return_val_if_fail(item != NULL, NULL);
1043 path = folder_item_get_path(item);
1044 if (!is_dir_exist(path))
1045 make_dir_hier(path);
1046 filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(uid), NULL);
1049 if (is_file_exist(filename)) {
1050 /* see whether the local file represents the whole message
1051 * or not. As the IMAP server reports size with \r chars,
1052 * we have to update the local file (UNIX \n only) size */
1053 MsgInfo *msginfo = imap_parse_msg(filename, item);
1054 MsgInfo *cached = msgcache_get_msg(item->cache,uid);
1055 guint have_size = get_size_with_lfs(msginfo);
1056 debug_print("message %d has been already %scached (%d/%d).\n", uid,
1057 have_size == cached->size ? "fully ":"",
1058 have_size, cached? (int)cached->size : -1);
1060 if (cached && (cached->size == have_size || !body)) {
1061 procmsg_msginfo_free(cached);
1062 procmsg_msginfo_free(msginfo);
1065 procmsg_msginfo_free(cached);
1066 procmsg_msginfo_free(msginfo);
1070 session = imap_session_get(folder);
1076 debug_print("IMAP fetching messages\n");
1077 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
1078 NULL, NULL, NULL, NULL, FALSE);
1079 if (ok != IMAP_SUCCESS) {
1080 g_warning("can't select mailbox %s\n", item->path);
1085 debug_print("getting message %d...\n", uid);
1086 ok = imap_cmd_fetch(session, (guint32)uid, filename, headers, body);
1088 if (ok != IMAP_SUCCESS) {
1089 g_warning("can't fetch message %d\n", uid);
1097 static gint imap_add_msg(Folder *folder, FolderItem *dest,
1098 const gchar *file, MsgFlags *flags)
1102 MsgFileInfo fileinfo;
1104 g_return_val_if_fail(file != NULL, -1);
1106 fileinfo.msginfo = NULL;
1107 fileinfo.file = (gchar *)file;
1108 fileinfo.flags = flags;
1109 file_list.data = &fileinfo;
1110 file_list.next = NULL;
1112 ret = imap_add_msgs(folder, dest, &file_list, NULL);
1116 static gint imap_add_msgs(Folder *folder, FolderItem *dest, GSList *file_list,
1117 GRelation *relation)
1120 IMAPSession *session;
1121 guint32 last_uid = 0;
1123 MsgFileInfo *fileinfo;
1127 g_return_val_if_fail(folder != NULL, -1);
1128 g_return_val_if_fail(dest != NULL, -1);
1129 g_return_val_if_fail(file_list != NULL, -1);
1131 MUTEX_TRYLOCK_OR_RETURN_VAL(-1);
1133 session = imap_session_get(folder);
1138 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
1140 for (cur = file_list; cur != NULL; cur = cur->next) {
1141 IMAPFlags iflags = 0;
1142 guint32 new_uid = 0;
1144 fileinfo = (MsgFileInfo *)cur->data;
1146 if (fileinfo->flags) {
1147 if (MSG_IS_MARKED(*fileinfo->flags))
1148 iflags |= IMAP_FLAG_FLAGGED;
1149 if (MSG_IS_REPLIED(*fileinfo->flags))
1150 iflags |= IMAP_FLAG_ANSWERED;
1151 if (!MSG_IS_UNREAD(*fileinfo->flags))
1152 iflags |= IMAP_FLAG_SEEN;
1155 if (dest->stype == F_OUTBOX ||
1156 dest->stype == F_QUEUE ||
1157 dest->stype == F_DRAFT ||
1158 dest->stype == F_TRASH)
1159 iflags |= IMAP_FLAG_SEEN;
1161 ok = imap_cmd_append(session, destdir, fileinfo->file, iflags,
1164 if (ok != IMAP_SUCCESS) {
1165 g_warning("can't append message %s\n", fileinfo->file);
1171 if (relation != NULL)
1172 g_relation_insert(relation, fileinfo->msginfo != NULL ?
1173 (gpointer) fileinfo->msginfo : (gpointer) fileinfo,
1174 GINT_TO_POINTER(dest->last_num + 1));
1175 if (last_uid < new_uid)
1185 static gint imap_do_copy_msgs(Folder *folder, FolderItem *dest,
1186 MsgInfoList *msglist, GRelation *relation)
1190 GSList *seq_list, *cur;
1192 IMAPSession *session;
1193 gint ok = IMAP_SUCCESS;
1194 GRelation *uid_mapping;
1197 g_return_val_if_fail(folder != NULL, -1);
1198 g_return_val_if_fail(dest != NULL, -1);
1199 g_return_val_if_fail(msglist != NULL, -1);
1201 MUTEX_TRYLOCK_OR_RETURN_VAL(-1);
1203 session = imap_session_get(folder);
1209 msginfo = (MsgInfo *)msglist->data;
1211 src = msginfo->folder;
1213 g_warning("the src folder is identical to the dest.\n");
1218 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
1219 NULL, NULL, NULL, NULL, FALSE);
1220 if (ok != IMAP_SUCCESS) {
1225 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
1226 seq_list = imap_get_seq_set_from_msglist(msglist);
1227 uid_mapping = g_relation_new(2);
1228 g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
1230 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
1231 gchar *seq_set = (gchar *)cur->data;
1233 debug_print("Copying message %s%c[%s] to %s ...\n",
1234 src->path, G_DIR_SEPARATOR,
1237 ok = imap_cmd_copy(session, seq_set, destdir, uid_mapping);
1238 if (ok != IMAP_SUCCESS) {
1239 g_relation_destroy(uid_mapping);
1240 imap_seq_set_free(seq_list);
1245 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
1246 MsgInfo *msginfo = (MsgInfo *)cur->data;
1249 tuples = g_relation_select(uid_mapping,
1250 GINT_TO_POINTER(msginfo->msgnum),
1252 if (tuples->len > 0) {
1253 gint num = GPOINTER_TO_INT(g_tuples_index(tuples, 0, 1));
1254 g_relation_insert(relation, msginfo,
1255 GPOINTER_TO_INT(num));
1259 g_relation_insert(relation, msginfo,
1260 GPOINTER_TO_INT(0));
1261 g_tuples_destroy(tuples);
1264 g_relation_destroy(uid_mapping);
1265 imap_seq_set_free(seq_list);
1269 IMAP_FOLDER_ITEM(dest)->lastuid = 0;
1270 IMAP_FOLDER_ITEM(dest)->uid_next = 0;
1271 g_slist_free(IMAP_FOLDER_ITEM(dest)->uid_list);
1272 IMAP_FOLDER_ITEM(dest)->uid_list = NULL;
1276 if (ok == IMAP_SUCCESS)
1282 static gint imap_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1286 g_return_val_if_fail(msginfo != NULL, -1);
1288 msglist.data = msginfo;
1289 msglist.next = NULL;
1291 return imap_copy_msgs(folder, dest, &msglist, NULL);
1294 static gint imap_copy_msgs(Folder *folder, FolderItem *dest,
1295 MsgInfoList *msglist, GRelation *relation)
1301 g_return_val_if_fail(folder != NULL, -1);
1302 g_return_val_if_fail(dest != NULL, -1);
1303 g_return_val_if_fail(msglist != NULL, -1);
1305 msginfo = (MsgInfo *)msglist->data;
1306 g_return_val_if_fail(msginfo->folder != NULL, -1);
1308 if (folder == msginfo->folder->folder) {
1309 ret = imap_do_copy_msgs(folder, dest, msglist, relation);
1313 file_list = procmsg_get_message_file_list(msglist);
1314 g_return_val_if_fail(file_list != NULL, -1);
1316 ret = imap_add_msgs(folder, dest, file_list, relation);
1318 procmsg_message_file_list_free(file_list);
1324 static gint imap_do_remove_msgs(Folder *folder, FolderItem *dest,
1325 MsgInfoList *msglist, GRelation *relation)
1328 GSList *seq_list = NULL, *cur;
1330 IMAPSession *session;
1331 gint ok = IMAP_SUCCESS;
1332 GRelation *uid_mapping;
1334 g_return_val_if_fail(folder != NULL, -1);
1335 g_return_val_if_fail(dest != NULL, -1);
1336 g_return_val_if_fail(msglist != NULL, -1);
1338 MUTEX_TRYLOCK_OR_RETURN_VAL(-1);
1340 session = imap_session_get(folder);
1345 msginfo = (MsgInfo *)msglist->data;
1347 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
1348 NULL, NULL, NULL, NULL, FALSE);
1349 if (ok != IMAP_SUCCESS) {
1354 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
1355 for (cur = msglist; cur; cur = cur->next) {
1356 msginfo = (MsgInfo *)cur->data;
1357 seq_list = g_slist_append(seq_list, GINT_TO_POINTER(msginfo->msgnum));
1360 uid_mapping = g_relation_new(2);
1361 g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
1363 ok = imap_set_message_flags
1364 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
1365 seq_list, IMAP_FLAG_DELETED, TRUE);
1366 if (ok != IMAP_SUCCESS) {
1367 log_warning(_("can't set deleted flags\n"));
1371 ok = imap_cmd_expunge(session, NULL);
1372 if (ok != IMAP_SUCCESS) {
1373 log_warning(_("can't expunge\n"));
1378 g_relation_destroy(uid_mapping);
1379 g_slist_free(seq_list);
1384 if (ok == IMAP_SUCCESS)
1390 static gint imap_remove_msgs(Folder *folder, FolderItem *dest,
1391 MsgInfoList *msglist, GRelation *relation)
1395 g_return_val_if_fail(folder != NULL, -1);
1396 g_return_val_if_fail(dest != NULL, -1);
1397 if (msglist == NULL)
1400 msginfo = (MsgInfo *)msglist->data;
1401 g_return_val_if_fail(msginfo->folder != NULL, -1);
1403 return imap_do_remove_msgs(folder, dest, msglist, relation);
1406 static gint imap_remove_all_msg(Folder *folder, FolderItem *item)
1408 GSList *list = folder_item_get_msg_list(item);
1409 gint res = imap_remove_msgs(folder, item, list, NULL);
1414 static gboolean imap_is_msg_changed(Folder *folder, FolderItem *item,
1417 /* TODO: properly implement this method */
1421 static gint imap_close(Folder *folder, FolderItem *item)
1424 IMAPSession *session;
1426 g_return_val_if_fail(folder != NULL, -1);
1427 g_return_val_if_fail(item != NULL, -1);
1428 g_return_val_if_fail(item->path != NULL, -1);
1430 session = imap_session_get(folder);
1431 if (!session) return -1;
1433 if (session->mbox) {
1434 if (strcmp2(session->mbox, item->path) != 0) return -1;
1436 ok = imap_cmd_close(session);
1437 if (ok != IMAP_SUCCESS)
1438 log_warning(_("can't close folder\n"));
1440 g_free(session->mbox);
1442 session->mbox = NULL;
1450 static gint imap_scan_tree(Folder *folder)
1452 FolderItem *item = NULL;
1453 IMAPSession *session;
1454 gchar *root_folder = NULL;
1456 g_return_val_if_fail(folder != NULL, -1);
1457 g_return_val_if_fail(folder->account != NULL, -1);
1459 MUTEX_TRYLOCK_OR_RETURN_VAL(-1);
1461 session = imap_session_get(folder);
1463 if (!folder->node) {
1464 folder_tree_destroy(folder);
1465 item = folder_item_new(folder, folder->name, NULL);
1466 item->folder = folder;
1467 folder->node = item->node = g_node_new(item);
1473 if (folder->account->imap_dir && *folder->account->imap_dir) {
1478 Xstrdup_a(root_folder, folder->account->imap_dir, {MUTEX_UNLOCK();return -1;});
1479 extract_quote(root_folder, '"');
1480 subst_char(root_folder,
1481 imap_get_path_separator(IMAP_FOLDER(folder),
1484 strtailchomp(root_folder, '/');
1485 real_path = imap_get_real_path
1486 (IMAP_FOLDER(folder), root_folder);
1487 debug_print("IMAP root directory: %s\n", real_path);
1489 /* check if root directory exist */
1490 argbuf = g_ptr_array_new();
1491 ok = imap_cmd_list(session, NULL, real_path, argbuf);
1492 if (ok != IMAP_SUCCESS ||
1493 search_array_str(argbuf, "LIST ") == NULL) {
1494 log_warning(_("root folder %s does not exist\n"), real_path);
1495 g_ptr_array_free(argbuf, TRUE);
1498 if (!folder->node) {
1499 item = folder_item_new(folder, folder->name, NULL);
1500 item->folder = folder;
1501 folder->node = item->node = g_node_new(item);
1506 g_ptr_array_free(argbuf, TRUE);
1511 item = FOLDER_ITEM(folder->node->data);
1512 if (!item || ((item->path || root_folder) &&
1513 strcmp2(item->path, root_folder) != 0)) {
1514 folder_tree_destroy(folder);
1515 item = folder_item_new(folder, folder->name, root_folder);
1516 item->folder = folder;
1517 folder->node = item->node = g_node_new(item);
1520 imap_scan_tree_recursive(session, FOLDER_ITEM(folder->node->data));
1521 imap_create_missing_folders(folder);
1527 static gint imap_scan_tree_recursive(IMAPSession *session, FolderItem *item)
1530 IMAPFolder *imapfolder;
1531 FolderItem *new_item;
1532 GSList *item_list, *cur;
1535 gchar *wildcard_path;
1539 g_return_val_if_fail(item != NULL, -1);
1540 g_return_val_if_fail(item->folder != NULL, -1);
1541 g_return_val_if_fail(item->no_sub == FALSE, -1);
1543 folder = item->folder;
1544 imapfolder = IMAP_FOLDER(folder);
1546 separator = imap_get_path_separator(imapfolder, item->path);
1548 if (folder->ui_func)
1549 folder->ui_func(folder, item, folder->ui_func_data);
1552 wildcard[0] = separator;
1555 real_path = imap_get_real_path(imapfolder, item->path);
1559 real_path = g_strdup("");
1562 Xstrcat_a(wildcard_path, real_path, wildcard,
1563 {g_free(real_path); return IMAP_ERROR;});
1564 QUOTE_IF_REQUIRED(wildcard_path, wildcard_path);
1566 imap_gen_send(session, "LIST \"\" %s",
1569 strtailchomp(real_path, separator);
1570 item_list = imap_parse_list(imapfolder, session, real_path, NULL);
1573 node = item->node->children;
1574 while (node != NULL) {
1575 FolderItem *old_item = FOLDER_ITEM(node->data);
1576 GNode *next = node->next;
1579 for (cur = item_list; cur != NULL; cur = cur->next) {
1580 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1581 if (!strcmp2(old_item->path, cur_item->path)) {
1582 new_item = cur_item;
1587 debug_print("folder '%s' not found. removing...\n",
1589 folder_item_remove(old_item);
1591 old_item->no_sub = new_item->no_sub;
1592 old_item->no_select = new_item->no_select;
1593 if (old_item->no_sub == TRUE && node->children) {
1594 debug_print("folder '%s' doesn't have "
1595 "subfolders. removing...\n",
1597 folder_item_remove_children(old_item);
1604 for (cur = item_list; cur != NULL; cur = cur->next) {
1605 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1607 for (node = item->node->children; node != NULL;
1608 node = node->next) {
1609 if (!strcmp2(FOLDER_ITEM(node->data)->path,
1611 new_item = FOLDER_ITEM(node->data);
1612 folder_item_destroy(cur_item);
1618 new_item = cur_item;
1619 debug_print("new folder '%s' found.\n", new_item->path);
1620 folder_item_append(item, new_item);
1623 if (!strcmp(new_item->path, "INBOX")) {
1624 new_item->stype = F_INBOX;
1625 folder->inbox = new_item;
1626 } else if (!folder_item_parent(item) || item->stype == F_INBOX) {
1629 base = g_path_get_basename(new_item->path);
1631 if (!folder->outbox && !g_ascii_strcasecmp(base, "Sent")) {
1632 new_item->stype = F_OUTBOX;
1633 folder->outbox = new_item;
1634 } else if (!folder->draft && !g_ascii_strcasecmp(base, "Drafts")) {
1635 new_item->stype = F_DRAFT;
1636 folder->draft = new_item;
1637 } else if (!folder->queue && !g_ascii_strcasecmp(base, "Queue")) {
1638 new_item->stype = F_QUEUE;
1639 folder->queue = new_item;
1640 } else if (!folder->trash && !g_ascii_strcasecmp(base, "Trash")) {
1641 new_item->stype = F_TRASH;
1642 folder->trash = new_item;
1647 if (new_item->no_sub == FALSE)
1648 imap_scan_tree_recursive(session, new_item);
1651 g_slist_free(item_list);
1653 return IMAP_SUCCESS;
1656 static GSList *imap_parse_list(IMAPFolder *folder, IMAPSession *session,
1657 const gchar *real_path, gchar *separator)
1659 gchar buf[IMAPBUFSIZE];
1661 gchar separator_str[16];
1664 gchar *loc_name, *loc_path;
1665 GSList *item_list = NULL;
1667 FolderItem *new_item;
1669 debug_print("getting list of %s ...\n",
1670 *real_path ? real_path : "\"\"");
1672 str = g_string_new(NULL);
1675 if (sock_gets(SESSION(session)->sock, buf, sizeof(buf)) <= 0) {
1676 log_warning(_("error occurred while getting LIST.\n"));
1680 if (buf[0] != '*' || buf[1] != ' ') {
1681 log_print("IMAP4< %s\n", buf);
1682 if (sscanf(buf, "%*d %16s", buf) < 1 ||
1683 strcmp(buf, "OK") != 0)
1684 log_warning(_("error occurred while getting LIST.\n"));
1688 debug_print("IMAP4< %s\n", buf);
1690 g_string_assign(str, buf);
1692 if (strncmp(p, "LIST ", 5) != 0) continue;
1695 if (*p != '(') continue;
1697 p = strchr_cpy(p, ')', flags, sizeof(flags));
1699 while (*p == ' ') p++;
1701 p = strchr_cpy(p, ' ', separator_str, sizeof(separator_str));
1703 extract_quote(separator_str, '"');
1704 if (!strcmp(separator_str, "NIL"))
1705 separator_str[0] = '\0';
1707 *separator = separator_str[0];
1710 while (*p == ' ') p++;
1711 if (*p == '{' || *p == '"')
1712 p = imap_parse_atom(SESSION(session)->sock, p,
1713 buf, sizeof(buf), str);
1715 strncpy2(buf, p, sizeof(buf));
1716 strtailchomp(buf, separator_str[0]);
1717 if (buf[0] == '\0') continue;
1718 if (!strcmp(buf, real_path)) continue;
1720 if (separator_str[0] != '\0')
1721 subst_char(buf, separator_str[0], '/');
1722 base = g_path_get_basename(buf);
1723 if (base[0] == '.') continue;
1725 loc_name = imap_modified_utf7_to_utf8(base);
1726 loc_path = imap_modified_utf7_to_utf8(buf);
1727 new_item = folder_item_new(FOLDER(folder), loc_name, loc_path);
1728 if (strcasestr(flags, "\\Noinferiors") != NULL)
1729 new_item->no_sub = TRUE;
1730 if (strcmp(buf, "INBOX") != 0 &&
1731 strcasestr(flags, "\\Noselect") != NULL)
1732 new_item->no_select = TRUE;
1734 item_list = g_slist_append(item_list, new_item);
1736 debug_print("folder '%s' found.\n", loc_path);
1742 g_string_free(str, TRUE);
1747 static gint imap_create_tree(Folder *folder)
1749 g_return_val_if_fail(folder != NULL, -1);
1750 g_return_val_if_fail(folder->node != NULL, -1);
1751 g_return_val_if_fail(folder->node->data != NULL, -1);
1752 g_return_val_if_fail(folder->account != NULL, -1);
1754 imap_scan_tree(folder);
1755 imap_create_missing_folders(folder);
1760 static void imap_create_missing_folders(Folder *folder)
1762 g_return_if_fail(folder != NULL);
1765 folder->inbox = imap_create_special_folder
1766 (folder, F_INBOX, "INBOX");
1768 if (!folder->outbox)
1769 folder->outbox = imap_create_special_folder
1770 (folder, F_OUTBOX, "Sent");
1772 folder->draft = imap_create_special_folder
1773 (folder, F_DRAFT, "Drafts");
1775 folder->queue = imap_create_special_folder
1776 (folder, F_QUEUE, "Queue");
1779 folder->trash = imap_create_special_folder
1780 (folder, F_TRASH, "Trash");
1783 static FolderItem *imap_create_special_folder(Folder *folder,
1784 SpecialFolderItemType stype,
1788 FolderItem *new_item;
1790 g_return_val_if_fail(folder != NULL, NULL);
1791 g_return_val_if_fail(folder->node != NULL, NULL);
1792 g_return_val_if_fail(folder->node->data != NULL, NULL);
1793 g_return_val_if_fail(folder->account != NULL, NULL);
1794 g_return_val_if_fail(name != NULL, NULL);
1796 item = FOLDER_ITEM(folder->node->data);
1797 new_item = imap_create_folder(folder, item, name);
1800 g_warning("Can't create '%s'\n", name);
1801 if (!folder->inbox) return NULL;
1803 new_item = imap_create_folder(folder, folder->inbox, name);
1805 g_warning("Can't create '%s' under INBOX\n", name);
1807 new_item->stype = stype;
1809 new_item->stype = stype;
1814 static gchar *imap_folder_get_path(Folder *folder)
1818 g_return_val_if_fail(folder != NULL, NULL);
1819 g_return_val_if_fail(folder->account != NULL, NULL);
1821 folder_path = g_strconcat(get_imap_cache_dir(),
1823 folder->account->recv_server,
1825 folder->account->userid,
1831 static gchar *imap_item_get_path(Folder *folder, FolderItem *item)
1833 gchar *folder_path, *path;
1835 g_return_val_if_fail(folder != NULL, NULL);
1836 g_return_val_if_fail(item != NULL, NULL);
1837 folder_path = imap_folder_get_path(folder);
1839 g_return_val_if_fail(folder_path != NULL, NULL);
1840 if (folder_path[0] == G_DIR_SEPARATOR) {
1842 path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
1845 path = g_strdup(folder_path);
1848 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1849 folder_path, G_DIR_SEPARATOR_S,
1852 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1855 g_free(folder_path);
1860 static FolderItem *imap_create_folder(Folder *folder, FolderItem *parent,
1863 gchar *dirpath, *imap_path;
1864 IMAPSession *session;
1865 FolderItem *new_item;
1871 g_return_val_if_fail(folder != NULL, NULL);
1872 g_return_val_if_fail(folder->account != NULL, NULL);
1873 g_return_val_if_fail(parent != NULL, NULL);
1874 g_return_val_if_fail(name != NULL, NULL);
1876 MUTEX_TRYLOCK_OR_RETURN_VAL(NULL);
1878 session = imap_session_get(folder);
1884 if (!folder_item_parent(parent) && strcmp(name, "INBOX") == 0)
1885 dirpath = g_strdup(name);
1886 else if (parent->path)
1887 dirpath = g_strconcat(parent->path, "/", name, NULL);
1888 else if ((p = strchr(name, '/')) != NULL && *(p + 1) != '\0')
1889 dirpath = g_strdup(name);
1890 else if (folder->account->imap_dir && *folder->account->imap_dir) {
1893 Xstrdup_a(imap_dir, folder->account->imap_dir, {MUTEX_UNLOCK();return NULL;});
1894 strtailchomp(imap_dir, '/');
1895 dirpath = g_strconcat(imap_dir, "/", name, NULL);
1897 dirpath = g_strdup(name);
1899 /* keep trailing directory separator to create a folder that contains
1901 imap_path = imap_utf8_to_modified_utf7(dirpath);
1902 strtailchomp(dirpath, '/');
1903 Xstrdup_a(new_name, name, {
1908 strtailchomp(new_name, '/');
1909 separator = imap_get_path_separator(IMAP_FOLDER(folder), imap_path);
1910 imap_path_separator_subst(imap_path, separator);
1911 subst_char(new_name, '/', separator);
1913 if (strcmp(name, "INBOX") != 0) {
1916 gboolean exist = FALSE;
1918 argbuf = g_ptr_array_new();
1919 ok = imap_cmd_list(session, NULL, imap_path,
1921 if (ok != IMAP_SUCCESS) {
1922 log_warning(_("can't create mailbox: LIST failed\n"));
1925 ptr_array_free_strings(argbuf);
1926 g_ptr_array_free(argbuf, TRUE);
1931 for (i = 0; i < argbuf->len; i++) {
1933 str = g_ptr_array_index(argbuf, i);
1934 if (!strncmp(str, "LIST ", 5)) {
1939 ptr_array_free_strings(argbuf);
1940 g_ptr_array_free(argbuf, TRUE);
1943 ok = imap_cmd_create(session, imap_path);
1944 if (ok != IMAP_SUCCESS) {
1945 log_warning(_("can't create mailbox\n"));
1954 new_item = folder_item_new(folder, new_name, dirpath);
1955 folder_item_append(parent, new_item);
1959 dirpath = folder_item_get_path(new_item);
1960 if (!is_dir_exist(dirpath))
1961 make_dir_hier(dirpath);
1968 static gint imap_rename_folder(Folder *folder, FolderItem *item,
1973 gchar *real_oldpath;
1974 gchar *real_newpath;
1976 gchar *old_cache_dir;
1977 gchar *new_cache_dir;
1978 IMAPSession *session;
1981 gint exists, recent, unseen;
1982 guint32 uid_validity;
1984 g_return_val_if_fail(folder != NULL, -1);
1985 g_return_val_if_fail(item != NULL, -1);
1986 g_return_val_if_fail(item->path != NULL, -1);
1987 g_return_val_if_fail(name != NULL, -1);
1989 MUTEX_TRYLOCK_OR_RETURN_VAL(-1);
1991 if (strchr(name, imap_get_path_separator(IMAP_FOLDER(folder), item->path)) != NULL) {
1992 g_warning(_("New folder name must not contain the namespace "
1998 session = imap_session_get(folder);
2003 real_oldpath = imap_get_real_path(IMAP_FOLDER(folder), item->path);
2005 g_free(session->mbox);
2006 session->mbox = NULL;
2007 ok = imap_cmd_examine(session, "INBOX",
2008 &exists, &recent, &unseen, &uid_validity, FALSE);
2009 if (ok != IMAP_SUCCESS) {
2010 g_free(real_oldpath);
2015 separator = imap_get_path_separator(IMAP_FOLDER(folder), item->path);
2016 if (strchr(item->path, G_DIR_SEPARATOR)) {
2017 dirpath = g_path_get_dirname(item->path);
2018 newpath = g_strconcat(dirpath, G_DIR_SEPARATOR_S, name, NULL);
2021 newpath = g_strdup(name);
2023 real_newpath = imap_utf8_to_modified_utf7(newpath);
2024 imap_path_separator_subst(real_newpath, separator);
2026 ok = imap_cmd_rename(session, real_oldpath, real_newpath);
2027 if (ok != IMAP_SUCCESS) {
2028 log_warning(_("can't rename mailbox: %s to %s\n"),
2029 real_oldpath, real_newpath);
2030 g_free(real_oldpath);
2032 g_free(real_newpath);
2038 item->name = g_strdup(name);
2040 old_cache_dir = folder_item_get_path(item);
2042 paths[0] = g_strdup(item->path);
2044 g_node_traverse(item->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
2045 imap_rename_folder_func, paths);
2047 if (is_dir_exist(old_cache_dir)) {
2048 new_cache_dir = folder_item_get_path(item);
2049 if (rename(old_cache_dir, new_cache_dir) < 0) {
2050 FILE_OP_ERROR(old_cache_dir, "rename");
2052 g_free(new_cache_dir);
2055 g_free(old_cache_dir);
2058 g_free(real_oldpath);
2059 g_free(real_newpath);
2065 static gint imap_remove_folder_real(Folder *folder, FolderItem *item)
2068 IMAPSession *session;
2071 gint exists, recent, unseen;
2072 guint32 uid_validity;
2074 g_return_val_if_fail(folder != NULL, -1);
2075 g_return_val_if_fail(item != NULL, -1);
2076 g_return_val_if_fail(item->path != NULL, -1);
2078 MUTEX_TRYLOCK_OR_RETURN_VAL(-1);
2080 session = imap_session_get(folder);
2085 path = imap_get_real_path(IMAP_FOLDER(folder), item->path);
2087 ok = imap_cmd_examine(session, "INBOX",
2088 &exists, &recent, &unseen, &uid_validity, FALSE);
2089 if (ok != IMAP_SUCCESS) {
2095 ok = imap_cmd_delete(session, path);
2096 if (ok != IMAP_SUCCESS) {
2097 log_warning(_("can't delete mailbox\n"));
2104 cache_dir = folder_item_get_path(item);
2105 if (is_dir_exist(cache_dir) && remove_dir_recursive(cache_dir) < 0)
2106 g_warning("can't remove directory '%s'\n", cache_dir);
2108 folder_item_remove(item);
2114 static gint imap_remove_folder(Folder *folder, FolderItem *item)
2118 g_return_val_if_fail(item != NULL, -1);
2119 g_return_val_if_fail(item->folder != NULL, -1);
2120 g_return_val_if_fail(item->node != NULL, -1);
2122 node = item->node->children;
2123 while (node != NULL) {
2125 if (imap_remove_folder(folder, FOLDER_ITEM(node->data)) < 0)
2129 debug_print("IMAP removing %s\n", item->path);
2131 if (imap_remove_all_msg(folder, item) < 0)
2133 return imap_remove_folder_real(folder, item);
2136 typedef struct _uncached_data {
2137 IMAPSession *session;
2139 MsgNumberList *numlist;
2145 static void *imap_get_uncached_messages_thread(void *data)
2147 uncached_data *stuff = (uncached_data *)data;
2148 IMAPSession *session = stuff->session;
2149 FolderItem *item = stuff->item;
2150 MsgNumberList *numlist = stuff->numlist;
2153 GSList *newlist = NULL;
2154 GSList *llast = NULL;
2155 GString *str = NULL;
2157 GSList *seq_list, *cur;
2160 stuff->total = g_slist_length(numlist);
2163 if (session == NULL || item == NULL || item->folder == NULL
2164 || FOLDER_CLASS(item->folder) != &imap_class) {
2169 seq_list = imap_get_seq_set_from_numlist(numlist);
2170 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
2171 imapset = cur->data;
2173 if (!imapset || strlen(imapset) == 0)
2176 if (imap_cmd_envelope(session, imapset)
2178 log_warning(_("can't get envelope\n"));
2182 str = g_string_new(NULL);
2185 if ((tmp =sock_getline(SESSION(session)->sock)) == NULL) {
2186 log_warning(_("error occurred while getting envelope.\n"));
2187 g_string_free(str, TRUE);
2192 if (tmp[0] != '*' || tmp[1] != ' ') {
2193 log_print("IMAP4< %s\n", tmp);
2197 if (strstr(tmp, "FETCH") == NULL) {
2198 log_print("IMAP4< %s\n", tmp);
2202 log_print("IMAP4< %s\n", tmp);
2203 g_string_assign(str, tmp);
2208 msginfo = imap_parse_envelope
2209 (SESSION(session)->sock, item, str);
2211 log_warning(_("can't parse envelope: %s\n"), str->str);
2214 if (item->stype == F_QUEUE) {
2215 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_QUEUED);
2216 } else if (item->stype == F_DRAFT) {
2217 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_DRAFT);
2220 msginfo->folder = item;
2223 llast = newlist = g_slist_append(newlist, msginfo);
2225 llast = g_slist_append(llast, msginfo);
2226 llast = llast->next;
2230 g_string_free(str, TRUE);
2232 imap_seq_set_free(seq_list);
2234 session_set_access_time(SESSION(session));
2240 static GSList *imap_get_uncached_messages(IMAPSession *session,
2242 MsgNumberList *numlist)
2244 uncached_data *data = g_new0(uncached_data, 1);
2245 GSList *result = NULL;
2251 data->session = session;
2253 data->numlist = numlist;
2257 if (prefs_common.work_offline && !imap_gtk_should_override()) {
2262 #if (defined USE_PTHREAD && defined __GLIBC__ && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 3)))
2263 MUTEX_TRYLOCK_OR_RETURN_VAL(NULL);
2265 if (pthread_create(&pt, PTHREAD_CREATE_JOINABLE,
2266 imap_get_uncached_messages_thread, data) != 0) {
2267 result = (GSList *)imap_get_uncached_messages_thread(data);
2272 debug_print("+++waiting for imap_get_uncached_messages_thread...\n");
2273 statusbar_print_all(_("IMAP4 Fetching uncached short headers..."));
2274 while(!data->done) {
2275 /* don't let the interface freeze while waiting */
2277 if (data->total != 0 && last_cur != data->cur && data->cur % 10 == 0) {
2279 g_snprintf(buf, sizeof(buf), "%d / %d",
2280 data->cur, data->total);
2281 gtk_progress_bar_set_text
2282 (GTK_PROGRESS_BAR(mainwindow_get_mainwindow()->progressbar), buf);
2283 gtk_progress_bar_set_fraction
2284 (GTK_PROGRESS_BAR(mainwindow_get_mainwindow()->progressbar),
2285 (gfloat)data->cur / (gfloat)data->total);
2286 last_cur = data->cur;
2289 gtk_progress_bar_set_fraction
2290 (GTK_PROGRESS_BAR(mainwindow_get_mainwindow()->progressbar), 0);
2291 gtk_progress_bar_set_text
2292 (GTK_PROGRESS_BAR(mainwindow_get_mainwindow()->progressbar), "");
2293 statusbar_pop_all();
2295 debug_print("---imap_get_uncached_messages_thread done\n");
2297 /* get the thread's return value and clean its resources */
2298 pthread_join(pt, (void *)&result);
2301 result = (GSList *)imap_get_uncached_messages_thread(data);
2307 static void imap_delete_all_cached_messages(FolderItem *item)
2311 g_return_if_fail(item != NULL);
2312 g_return_if_fail(item->folder != NULL);
2313 g_return_if_fail(FOLDER_CLASS(item->folder) == &imap_class);
2315 debug_print("Deleting all cached messages...\n");
2317 dir = folder_item_get_path(item);
2318 if (is_dir_exist(dir))
2319 remove_all_numbered_files(dir);
2322 debug_print("done.\n");
2326 static SockInfo *imap_open_tunnel(const gchar *server,
2327 const gchar *tunnelcmd,
2330 static SockInfo *imap_open_tunnel(const gchar *server,
2331 const gchar *tunnelcmd)
2336 if ((sock = sock_connect_cmd(server, tunnelcmd)) == NULL) {
2337 log_warning(_("Can't establish IMAP4 session with: %s\n"),
2342 return imap_init_sock(sock, ssl_type);
2344 return imap_init_sock(sock);
2348 void *imap_open_thread(void *data)
2350 SockInfo *sock = NULL;
2351 thread_data *td = (thread_data *)data;
2352 if ((sock = sock_connect(td->server, td->port)) == NULL) {
2353 log_warning(_("Can't connect to IMAP4 server: %s:%d\n"),
2354 td->server, td->port);
2364 static SockInfo *imap_open(const gchar *server, gushort port,
2367 static SockInfo *imap_open(const gchar *server, gushort port)
2370 thread_data *td = g_new0(thread_data, 1);
2374 SockInfo *sock = NULL;
2377 td->ssl_type = ssl_type;
2379 td->server = g_strdup(server);
2383 statusbar_print_all(_("Connecting to IMAP4 server: %s..."), server);
2385 #if (defined USE_PTHREAD && defined __GLIBC__ && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 3)))
2386 if (pthread_create(&pt, PTHREAD_CREATE_JOINABLE,
2387 imap_open_thread, td) != 0) {
2388 statusbar_pop_all();
2389 sock = imap_open_thread(td);
2391 debug_print("+++waiting for imap_open_thread...\n");
2393 /* don't let the interface freeze while waiting */
2397 /* get the thread's return value and clean its resources */
2398 pthread_join(pt, (void *)&sock);
2401 sock = imap_open_thread(td);
2404 if (sock && td->ssl_type == SSL_TUNNEL && !ssl_init_socket(sock)) {
2405 log_warning(_("Can't establish IMAP4 session with: %s:%d\n"),
2406 td->server, td->port);
2415 debug_print("---imap_open_thread returned %p\n", sock);
2416 statusbar_pop_all();
2418 if(!sock && !prefs_common.no_recv_err_panel) {
2419 alertpanel_error(_("Can't connect to IMAP4 server: %s:%d"),
2427 static SockInfo *imap_init_sock(SockInfo *sock, SSLType ssl_type)
2429 static SockInfo *imap_init_sock(SockInfo *sock)
2436 static GList *imap_parse_namespace_str(gchar *str)
2441 IMAPNameSpace *namespace;
2442 GList *ns_list = NULL;
2444 while (*p != '\0') {
2445 /* parse ("#foo" "/") */
2447 while (*p && *p != '(') p++;
2448 if (*p == '\0') break;
2451 while (*p && *p != '"') p++;
2452 if (*p == '\0') break;
2456 while (*p && *p != '"') p++;
2457 if (*p == '\0') break;
2461 while (*p && isspace(*p)) p++;
2462 if (*p == '\0') break;
2463 if (strncmp(p, "NIL", 3) == 0)
2465 else if (*p == '"') {
2468 while (*p && *p != '"') p++;
2469 if (*p == '\0') break;
2474 while (*p && *p != ')') p++;
2475 if (*p == '\0') break;
2478 namespace = g_new(IMAPNameSpace, 1);
2479 namespace->name = g_strdup(name);
2480 namespace->separator = separator ? separator[0] : '\0';
2481 ns_list = g_list_append(ns_list, namespace);
2487 static void imap_parse_namespace(IMAPSession *session, IMAPFolder *folder)
2492 g_return_if_fail(session != NULL);
2493 g_return_if_fail(folder != NULL);
2495 if (folder->ns_personal != NULL ||
2496 folder->ns_others != NULL ||
2497 folder->ns_shared != NULL)
2500 if (!imap_has_capability(session, "NAMESPACE")) {
2501 imap_get_namespace_by_list(session, folder);
2505 if (imap_cmd_namespace(session, &ns_str)
2507 log_warning(_("can't get namespace\n"));
2511 str_array = strsplit_parenthesis(ns_str, '(', ')', 3);
2512 if (str_array == NULL) {
2514 imap_get_namespace_by_list(session, folder);
2518 folder->ns_personal = imap_parse_namespace_str(str_array[0]);
2519 if (str_array[0] && str_array[1])
2520 folder->ns_others = imap_parse_namespace_str(str_array[1]);
2521 if (str_array[0] && str_array[1] && str_array[2])
2522 folder->ns_shared = imap_parse_namespace_str(str_array[2]);
2523 g_strfreev(str_array);
2527 static void imap_get_namespace_by_list(IMAPSession *session, IMAPFolder *folder)
2529 GSList *item_list, *cur;
2530 gchar separator = '\0';
2531 IMAPNameSpace *namespace;
2533 g_return_if_fail(session != NULL);
2534 g_return_if_fail(folder != NULL);
2536 if (folder->ns_personal != NULL ||
2537 folder->ns_others != NULL ||
2538 folder->ns_shared != NULL)
2541 imap_gen_send(session, "LIST \"\" \"\"");
2542 item_list = imap_parse_list(folder, session, "", &separator);
2543 for (cur = item_list; cur != NULL; cur = cur->next)
2544 folder_item_destroy(FOLDER_ITEM(cur->data));
2545 g_slist_free(item_list);
2547 namespace = g_new(IMAPNameSpace, 1);
2548 namespace->name = g_strdup("");
2549 namespace->separator = separator;
2550 folder->ns_personal = g_list_append(NULL, namespace);
2553 static IMAPNameSpace *imap_find_namespace_from_list(GList *ns_list,
2556 IMAPNameSpace *namespace = NULL;
2557 gchar *tmp_path, *name;
2559 if (!path) path = "";
2561 for (; ns_list != NULL; ns_list = ns_list->next) {
2562 IMAPNameSpace *tmp_ns = ns_list->data;
2564 Xstrcat_a(tmp_path, path, "/", return namespace);
2565 Xstrdup_a(name, tmp_ns->name, return namespace);
2566 if (tmp_ns->separator && tmp_ns->separator != '/') {
2567 subst_char(tmp_path, tmp_ns->separator, '/');
2568 subst_char(name, tmp_ns->separator, '/');
2570 if (strncmp(tmp_path, name, strlen(name)) == 0)
2577 static IMAPNameSpace *imap_find_namespace(IMAPFolder *folder,
2580 IMAPNameSpace *namespace;
2582 g_return_val_if_fail(folder != NULL, NULL);
2584 namespace = imap_find_namespace_from_list(folder->ns_personal, path);
2585 if (namespace) return namespace;
2586 namespace = imap_find_namespace_from_list(folder->ns_others, path);
2587 if (namespace) return namespace;
2588 namespace = imap_find_namespace_from_list(folder->ns_shared, path);
2589 if (namespace) return namespace;
2594 static gchar imap_get_path_separator(IMAPFolder *folder, const gchar *path)
2596 IMAPNameSpace *namespace;
2597 gchar separator = '/';
2599 namespace = imap_find_namespace(folder, path);
2600 if (namespace && namespace->separator)
2601 separator = namespace->separator;
2606 static gchar *imap_get_real_path(IMAPFolder *folder, const gchar *path)
2611 g_return_val_if_fail(folder != NULL, NULL);
2612 g_return_val_if_fail(path != NULL, NULL);
2614 real_path = imap_utf8_to_modified_utf7(path);
2615 separator = imap_get_path_separator(folder, path);
2616 imap_path_separator_subst(real_path, separator);
2621 static gchar *imap_parse_atom(SockInfo *sock, gchar *src,
2622 gchar *dest, gint dest_len, GString *str)
2624 gchar *cur_pos = src;
2627 g_return_val_if_fail(str != NULL, cur_pos);
2629 /* read the next line if the current response buffer is empty */
2630 while (isspace(*(guchar *)cur_pos)) cur_pos++;
2631 while (*cur_pos == '\0') {
2632 if ((nextline = imap_getline(sock)) == NULL)
2634 g_string_assign(str, nextline);
2636 strretchomp(nextline);
2637 /* log_print("IMAP4< %s\n", nextline); */
2638 debug_print("IMAP4< %s\n", nextline);
2641 while (isspace(*(guchar *)cur_pos)) cur_pos++;
2644 if (!strncmp(cur_pos, "NIL", 3)) {
2647 } else if (*cur_pos == '\"') {
2650 p = get_quoted(cur_pos, '\"', dest, dest_len);
2651 cur_pos = p ? p : cur_pos + 2;
2652 } else if (*cur_pos == '{') {
2657 cur_pos = strchr_cpy(cur_pos + 1, '}', buf, sizeof(buf));
2659 g_return_val_if_fail(len >= 0, cur_pos);
2661 g_string_truncate(str, 0);
2665 if ((nextline = imap_getline(sock)) == NULL)
2667 line_len += strlen(nextline);
2668 g_string_append(str, nextline);
2670 strretchomp(nextline);
2671 /* log_print("IMAP4< %s\n", nextline); */
2672 debug_print("IMAP4< %s\n", nextline);
2674 } while (line_len < len);
2676 memcpy(dest, cur_pos, MIN(len, dest_len - 1));
2677 dest[MIN(len, dest_len - 1)] = '\0';
2684 static gchar *imap_get_header(SockInfo *sock, gchar *cur_pos, gchar **headers,
2694 g_return_val_if_fail(str != NULL, cur_pos);
2696 while (isspace(*(guchar *)cur_pos)) cur_pos++;
2698 g_return_val_if_fail(*cur_pos == '{', cur_pos);
2700 cur_pos = strchr_cpy(cur_pos + 1, '}', buf, sizeof(buf));
2702 g_return_val_if_fail(len >= 0, cur_pos);
2704 g_string_truncate(str, 0);
2708 if ((nextline = sock_getline(sock)) == NULL) {
2712 block_len += strlen(nextline);
2713 g_string_append(str, nextline);
2715 strretchomp(nextline);
2716 /* debug_print("IMAP4< %s\n", nextline); */
2718 } while (block_len < len);
2720 debug_print("IMAP4< [contents of BODY.PEEK[HEADER_FIELDS (...)]\n");
2722 *headers = g_strndup(cur_pos, len);
2725 while (isspace(*(guchar *)cur_pos)) cur_pos++;
2726 while (*cur_pos == '\0') {
2727 if ((nextline = sock_getline(sock)) == NULL)
2729 g_string_assign(str, nextline);
2731 strretchomp(nextline);
2732 debug_print("IMAP4< %s\n", nextline);
2735 while (isspace(*(guchar *)cur_pos)) cur_pos++;
2741 static MsgFlags imap_parse_flags(const gchar *flag_str)
2743 const gchar *p = flag_str;
2744 MsgFlags flags = {0, 0};
2746 flags.perm_flags = MSG_UNREAD;
2748 while ((p = strchr(p, '\\')) != NULL) {
2751 if (g_ascii_strncasecmp(p, "Recent", 6) == 0 && MSG_IS_UNREAD(flags)) {
2752 MSG_SET_PERM_FLAGS(flags, MSG_NEW);
2753 } else if (g_ascii_strncasecmp(p, "Seen", 4) == 0) {
2754 MSG_UNSET_PERM_FLAGS(flags, MSG_NEW|MSG_UNREAD);
2755 } else if (g_ascii_strncasecmp(p, "Deleted", 7) == 0) {
2756 MSG_SET_PERM_FLAGS(flags, MSG_DELETED);
2757 } else if (g_ascii_strncasecmp(p, "Flagged", 7) == 0) {
2758 MSG_SET_PERM_FLAGS(flags, MSG_MARKED);
2759 } else if (g_ascii_strncasecmp(p, "Answered", 8) == 0) {
2760 MSG_SET_PERM_FLAGS(flags, MSG_REPLIED);
2767 static MsgInfo *imap_parse_envelope(SockInfo *sock, FolderItem *item,
2770 gchar buf[IMAPBUFSIZE];
2771 MsgInfo *msginfo = NULL;
2776 MsgFlags flags = {0, 0}, imap_flags = {0, 0};
2778 g_return_val_if_fail(line_str != NULL, NULL);
2779 g_return_val_if_fail(line_str->str[0] == '*' &&
2780 line_str->str[1] == ' ', NULL);
2782 MSG_SET_TMP_FLAGS(flags, MSG_IMAP);
2783 if (item->stype == F_QUEUE) {
2784 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
2785 } else if (item->stype == F_DRAFT) {
2786 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
2789 cur_pos = line_str->str + 2;
2791 #define PARSE_ONE_ELEMENT(ch) \
2793 cur_pos = strchr_cpy(cur_pos, ch, buf, sizeof(buf)); \
2794 if (cur_pos == NULL) { \
2795 g_warning("cur_pos == NULL\n"); \
2796 procmsg_msginfo_free(msginfo); \
2801 PARSE_ONE_ELEMENT(' ');
2804 PARSE_ONE_ELEMENT(' ');
2805 g_return_val_if_fail(!strcmp(buf, "FETCH"), NULL);
2807 g_return_val_if_fail(*cur_pos == '(', NULL);
2810 while (*cur_pos != '\0' && *cur_pos != ')') {
2811 while (*cur_pos == ' ') cur_pos++;
2813 if (!strncmp(cur_pos, "UID ", 4)) {
2815 uid = strtoul(cur_pos, &cur_pos, 10);
2816 } else if (!strncmp(cur_pos, "FLAGS ", 6)) {
2818 if (*cur_pos != '(') {
2819 g_warning("*cur_pos != '('\n");
2820 procmsg_msginfo_free(msginfo);
2824 PARSE_ONE_ELEMENT(')');
2825 imap_flags = imap_parse_flags(buf);
2826 } else if (!strncmp(cur_pos, "RFC822.SIZE ", 12)) {
2828 size = strtol(cur_pos, &cur_pos, 10);
2829 } else if (!strncmp(cur_pos, "BODY[HEADER.FIELDS ", 19)) {
2833 if (*cur_pos != '(') {
2834 g_warning("*cur_pos != '('\n");
2835 procmsg_msginfo_free(msginfo);
2839 PARSE_ONE_ELEMENT(')');
2840 if (*cur_pos != ']') {
2841 g_warning("*cur_pos != ']'\n");
2842 procmsg_msginfo_free(msginfo);
2846 cur_pos = imap_get_header(sock, cur_pos, &headers,
2848 msginfo = procheader_parse_str(headers, flags, FALSE, FALSE);
2851 g_warning("invalid FETCH response: %s\n", cur_pos);
2857 msginfo->msgnum = uid;
2858 msginfo->size = size;
2859 msginfo->flags.tmp_flags |= imap_flags.tmp_flags;
2860 msginfo->flags.perm_flags = imap_flags.perm_flags;
2866 static gchar *imap_get_flag_str(IMAPFlags flags)
2871 str = g_string_new(NULL);
2873 if (IMAP_IS_SEEN(flags)) g_string_append(str, "\\Seen ");
2874 if (IMAP_IS_ANSWERED(flags)) g_string_append(str, "\\Answered ");
2875 if (IMAP_IS_FLAGGED(flags)) g_string_append(str, "\\Flagged ");
2876 if (IMAP_IS_DELETED(flags)) g_string_append(str, "\\Deleted ");
2877 if (IMAP_IS_DRAFT(flags)) g_string_append(str, "\\Draft");
2879 if (str->len > 0 && str->str[str->len - 1] == ' ')
2880 g_string_truncate(str, str->len - 1);
2883 g_string_free(str, FALSE);
2888 static gint imap_set_message_flags(IMAPSession *session,
2889 MsgNumberList *numlist,
2899 flag_str = imap_get_flag_str(flags);
2900 cmd = g_strconcat(is_set ? "+FLAGS.SILENT (" : "-FLAGS.SILENT (",
2901 flag_str, ")", NULL);
2904 seq_list = imap_get_seq_set_from_numlist(numlist);
2905 imapset = get_seq_set_from_seq_list(seq_list);
2907 ok = imap_cmd_store(session, imapset, cmd);
2910 imap_seq_set_free(seq_list);
2916 typedef struct _select_data {
2917 IMAPSession *session;
2922 guint32 *uid_validity;
2926 static void *imap_select_thread(void *data)
2928 select_data *stuff = (select_data *)data;
2929 IMAPSession *session = stuff->session;
2930 gchar *real_path = stuff->real_path;
2931 gint *exists = stuff->exists;
2932 gint *recent = stuff->recent;
2933 gint *unseen = stuff->unseen;
2934 guint32 *uid_validity = stuff->uid_validity;
2937 ok = imap_cmd_select(session, real_path,
2938 exists, recent, unseen, uid_validity, TRUE);
2940 return GINT_TO_POINTER(ok);
2943 static gint imap_select(IMAPSession *session, IMAPFolder *folder,
2945 gint *exists, gint *recent, gint *unseen,
2946 guint32 *uid_validity, gboolean block)
2950 gint exists_, recent_, unseen_;
2951 guint32 uid_validity_;
2953 if (!exists || !recent || !unseen || !uid_validity) {
2954 if (session->mbox && strcmp(session->mbox, path) == 0)
2955 return IMAP_SUCCESS;
2959 uid_validity = &uid_validity_;
2962 g_free(session->mbox);
2963 session->mbox = NULL;
2965 real_path = imap_get_real_path(folder, path);
2967 #if (defined USE_PTHREAD && defined __GLIBC__ && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 3)))
2969 if (block == FALSE) {
2970 select_data *data = g_new0(select_data, 1);
2973 data->session = session;
2974 data->real_path = real_path;
2975 data->exists = exists;
2976 data->recent = recent;
2977 data->unseen = unseen;
2978 data->uid_validity = uid_validity;
2981 if (pthread_create(&pt, PTHREAD_CREATE_JOINABLE,
2982 imap_select_thread, data) != 0) {
2983 ok = GPOINTER_TO_INT(imap_select_thread(data));
2986 debug_print("+++waiting for imap_select_thread...\n");
2987 while(!data->done) {
2988 /* don't let the interface freeze while waiting */
2991 debug_print("---imap_select_thread done\n");
2993 /* get the thread's return value and clean its resources */
2994 pthread_join(pt, &tmp);
2995 ok = GPOINTER_TO_INT(tmp);
2999 ok = imap_cmd_select(session, real_path,
3000 exists, recent, unseen, uid_validity, block);
3003 ok = imap_cmd_select(session, real_path,
3004 exists, recent, unseen, uid_validity, block);
3006 if (ok != IMAP_SUCCESS)
3007 log_warning(_("can't select folder: %s\n"), real_path);
3009 session->mbox = g_strdup(path);
3010 session->folder_content_changed = FALSE;
3017 #define THROW(err) { ok = err; goto catch; }
3019 static gint imap_status(IMAPSession *session, IMAPFolder *folder,
3021 gint *messages, gint *recent,
3022 guint32 *uid_next, guint32 *uid_validity,
3023 gint *unseen, gboolean block)
3028 GPtrArray *argbuf = NULL;
3031 if (messages && recent && uid_next && uid_validity && unseen) {
3032 *messages = *recent = *uid_next = *uid_validity = *unseen = 0;
3033 argbuf = g_ptr_array_new();
3036 real_path = imap_get_real_path(folder, path);
3037 QUOTE_IF_REQUIRED(real_path_, real_path);
3038 imap_gen_send(session, "STATUS %s "
3039 "(MESSAGES RECENT UIDNEXT UIDVALIDITY UNSEEN)",
3042 ok = imap_cmd_ok_with_block(session, argbuf, block);
3043 if (ok != IMAP_SUCCESS || !argbuf) THROW(ok);
3045 str = search_array_str(argbuf, "STATUS");
3046 if (!str) THROW(IMAP_ERROR);
3048 str = strrchr(str, '(');
3049 if (!str) THROW(IMAP_ERROR);
3051 while (*str != '\0' && *str != ')') {
3052 while (*str == ' ') str++;
3054 if (!strncmp(str, "MESSAGES ", 9)) {
3056 *messages = strtol(str, &str, 10);
3057 } else if (!strncmp(str, "RECENT ", 7)) {
3059 *recent = strtol(str, &str, 10);
3060 } else if (!strncmp(str, "UIDNEXT ", 8)) {
3062 *uid_next = strtoul(str, &str, 10);
3063 } else if (!strncmp(str, "UIDVALIDITY ", 12)) {
3065 *uid_validity = strtoul(str, &str, 10);
3066 } else if (!strncmp(str, "UNSEEN ", 7)) {
3068 *unseen = strtol(str, &str, 10);
3070 g_warning("invalid STATUS response: %s\n", str);
3078 ptr_array_free_strings(argbuf);
3079 g_ptr_array_free(argbuf, TRUE);
3087 static gboolean imap_has_capability(IMAPSession *session, const gchar *cap)
3091 for (p = session->capability; *p != NULL; ++p) {
3092 if (!g_ascii_strcasecmp(*p, cap))
3099 static void imap_free_capabilities(IMAPSession *session)
3101 g_strfreev(session->capability);
3102 session->capability = NULL;
3105 /* low-level IMAP4rev1 commands */
3107 static gint imap_cmd_authenticate(IMAPSession *session, const gchar *user,
3108 const gchar *pass, IMAPAuthType type)
3115 gchar hexdigest[33];
3119 auth_type = "CRAM-MD5";
3121 imap_gen_send(session, "AUTHENTICATE %s", auth_type);
3122 ok = imap_gen_recv(session, &buf);
3123 if (ok != IMAP_SUCCESS || buf[0] != '+' || buf[1] != ' ') {
3128 challenge = g_malloc(strlen(buf + 2) + 1);
3129 challenge_len = base64_decode(challenge, buf + 2, -1);
3130 challenge[challenge_len] = '\0';
3132 log_print("IMAP< [Decoded: %s]\n", challenge);
3134 md5_hex_hmac(hexdigest, challenge, challenge_len, pass, strlen(pass));
3137 response = g_strdup_printf("%s %s", user, hexdigest);
3138 log_print("IMAP> [Encoded: %s]\n", response);
3139 response64 = g_malloc((strlen(response) + 3) * 2 + 1);
3140 base64_encode(response64, response, strlen(response));
3143 log_print("IMAP> %s\n", response64);
3144 sock_puts(SESSION(session)->sock, response64);
3145 ok = imap_cmd_ok(session, NULL);
3146 if (ok != IMAP_SUCCESS)
3147 log_warning(_("IMAP4 authentication failed.\n"));
3152 static gint imap_cmd_login(IMAPSession *session,
3153 const gchar *user, const gchar *pass)
3158 imap_gen_send(session, "LOGIN {%d}\r\n%s {%d}\r\n%s",
3160 strlen(pass), pass);
3162 ok = imap_gen_recv_with_block(session, &ans, TRUE);
3163 if (ok != IMAP_SUCCESS || ans[0] != '+' || ans[1] != ' ') {
3168 ok = imap_gen_recv_with_block(session, &ans, TRUE);
3169 if (ok != IMAP_SUCCESS || ans[0] != '+' || ans[1] != ' ') {
3174 ok = imap_cmd_ok(session, NULL);
3175 if (ok != IMAP_SUCCESS)
3176 log_warning(_("IMAP4 login failed.\n"));
3181 static gint imap_cmd_logout(IMAPSession *session)
3183 imap_gen_send(session, "LOGOUT");
3184 return imap_cmd_ok(session, NULL);
3187 static gint imap_cmd_noop(IMAPSession *session)
3189 imap_gen_send(session, "NOOP");
3190 return imap_cmd_ok(session, NULL);
3194 static gint imap_cmd_starttls(IMAPSession *session)
3196 imap_gen_send(session, "STARTTLS");
3197 return imap_cmd_ok(session, NULL);
3201 #define THROW(err) { ok = err; goto catch; }
3203 static gint imap_cmd_namespace(IMAPSession *session, gchar **ns_str)
3209 argbuf = g_ptr_array_new();
3211 imap_gen_send(session, "NAMESPACE");
3212 if ((ok = imap_cmd_ok(session, argbuf)) != IMAP_SUCCESS) THROW(ok);
3214 str = search_array_str(argbuf, "NAMESPACE");
3215 if (!str) THROW(IMAP_ERROR);
3217 *ns_str = g_strdup(str);
3220 ptr_array_free_strings(argbuf);
3221 g_ptr_array_free(argbuf, TRUE);
3228 static gint imap_cmd_list(IMAPSession *session, const gchar *ref,
3229 const gchar *mailbox, GPtrArray *argbuf)
3231 gchar *ref_, *mailbox_;
3233 if (!ref) ref = "\"\"";
3234 if (!mailbox) mailbox = "\"\"";
3236 QUOTE_IF_REQUIRED(ref_, ref);
3237 QUOTE_IF_REQUIRED(mailbox_, mailbox);
3238 imap_gen_send(session, "LIST %s %s", ref_, mailbox_);
3240 return imap_cmd_ok(session, argbuf);
3243 #define THROW goto catch
3245 static gint imap_cmd_do_select(IMAPSession *session, const gchar *folder,
3247 gint *exists, gint *recent, gint *unseen,
3248 guint32 *uid_validity, gboolean block)
3255 unsigned int uid_validity_;
3257 *exists = *recent = *unseen = *uid_validity = 0;
3258 argbuf = g_ptr_array_new();
3261 select_cmd = "EXAMINE";
3263 select_cmd = "SELECT";
3265 QUOTE_IF_REQUIRED(folder_, folder);
3266 imap_gen_send(session, "%s %s", select_cmd, folder_);
3268 if ((ok = imap_cmd_ok_with_block(session, argbuf, block)) != IMAP_SUCCESS) THROW;
3270 resp_str = search_array_contain_str(argbuf, "EXISTS");
3272 if (sscanf(resp_str,"%d EXISTS", exists) != 1) {
3273 g_warning("imap_cmd_select(): invalid EXISTS line.\n");
3278 resp_str = search_array_contain_str(argbuf, "RECENT");
3280 if (sscanf(resp_str, "%d RECENT", recent) != 1) {
3281 g_warning("imap_cmd_select(): invalid RECENT line.\n");
3286 resp_str = search_array_contain_str(argbuf, "UIDVALIDITY");
3288 if (sscanf(resp_str, "OK [UIDVALIDITY %u] ", &uid_validity_)
3290 g_warning("imap_cmd_select(): invalid UIDVALIDITY line.\n");
3293 *uid_validity = uid_validity_;
3296 resp_str = search_array_contain_str(argbuf, "UNSEEN");
3298 if (sscanf(resp_str, "OK [UNSEEN %d] ", unseen) != 1) {
3299 g_warning("imap_cmd_select(): invalid UNSEEN line.\n");
3305 ptr_array_free_strings(argbuf);
3306 g_ptr_array_free(argbuf, TRUE);
3311 static gint imap_cmd_select(IMAPSession *session, const gchar *folder,
3312 gint *exists, gint *recent, gint *unseen,
3313 guint32 *uid_validity, gboolean block)
3315 return imap_cmd_do_select(session, folder, FALSE,
3316 exists, recent, unseen, uid_validity, block);
3319 static gint imap_cmd_examine(IMAPSession *session, const gchar *folder,
3320 gint *exists, gint *recent, gint *unseen,
3321 guint32 *uid_validity, gboolean block)
3323 return imap_cmd_do_select(session, folder, TRUE,
3324 exists, recent, unseen, uid_validity, block);
3329 static gint imap_cmd_create(IMAPSession *session, const gchar *folder)
3333 QUOTE_IF_REQUIRED(folder_, folder);
3334 imap_gen_send(session, "CREATE %s", folder_);
3336 return imap_cmd_ok(session, NULL);
3339 static gint imap_cmd_rename(IMAPSession *session, const gchar *old_folder,
3340 const gchar *new_folder)
3342 gchar *old_folder_, *new_folder_;
3344 QUOTE_IF_REQUIRED(old_folder_, old_folder);
3345 QUOTE_IF_REQUIRED(new_folder_, new_folder);
3346 imap_gen_send(session, "RENAME %s %s", old_folder_, new_folder_);
3348 return imap_cmd_ok(session, NULL);
3351 static gint imap_cmd_delete(IMAPSession *session, const gchar *folder)
3355 QUOTE_IF_REQUIRED(folder_, folder);
3356 imap_gen_send(session, "DELETE %s", folder_);
3358 return imap_cmd_ok(session, NULL);
3361 static gint imap_cmd_search(IMAPSession *session, const gchar *criteria,
3362 GSList **list, gboolean block)
3368 g_return_val_if_fail(criteria != NULL, IMAP_ERROR);
3369 g_return_val_if_fail(list != NULL, IMAP_ERROR);
3373 argbuf = g_ptr_array_new();
3374 imap_gen_send(session, "UID SEARCH %s", criteria);
3376 ok = imap_cmd_ok_with_block(session, argbuf, block);
3377 if (ok != IMAP_SUCCESS) {
3378 ptr_array_free_strings(argbuf);
3379 g_ptr_array_free(argbuf, TRUE);
3383 if ((uidlist = search_array_str(argbuf, "SEARCH ")) != NULL) {
3384 gchar **strlist, **p;
3386 strlist = g_strsplit(uidlist + 7, " ", 0);
3387 for (p = strlist; *p != NULL; ++p) {
3390 if (sscanf(*p, "%u", &msgnum) == 1)
3391 *list = g_slist_append(*list, GINT_TO_POINTER(msgnum));
3393 g_strfreev(strlist);
3395 ptr_array_free_strings(argbuf);
3396 g_ptr_array_free(argbuf, TRUE);
3398 return IMAP_SUCCESS;
3401 typedef struct _fetch_data {
3402 IMAPSession *session;
3404 const gchar *filename;
3410 static void *imap_cmd_fetch_thread(void *data)
3412 fetch_data *stuff = (fetch_data *)data;
3413 IMAPSession *session = stuff->session;
3414 guint32 uid = stuff->uid;
3415 const gchar *filename = stuff->filename;
3423 if (filename == NULL) {
3425 return GINT_TO_POINTER(IMAP_ERROR);
3429 imap_gen_send(session, "UID FETCH %d BODY.PEEK[]", uid);
3431 imap_gen_send(session, "UID FETCH %d BODY.PEEK[HEADER]",
3433 while ((ok = imap_gen_recv_block(session, &buf)) == IMAP_SUCCESS) {
3434 if (buf[0] != '*' || buf[1] != ' ') {
3437 return GINT_TO_POINTER(IMAP_ERROR);
3439 if (strstr(buf, "FETCH") != NULL) break;
3442 if (ok != IMAP_SUCCESS) {
3445 return GINT_TO_POINTER(ok);
3448 #define RETURN_ERROR_IF_FAIL(cond) \
3451 stuff->done = TRUE; \
3452 return GINT_TO_POINTER(IMAP_ERROR); \
3455 cur_pos = strchr(buf, '{');
3456 RETURN_ERROR_IF_FAIL(cur_pos != NULL);
3457 cur_pos = strchr_cpy(cur_pos + 1, '}', size_str, sizeof(size_str));
3458 RETURN_ERROR_IF_FAIL(cur_pos != NULL);
3459 size_num = atol(size_str);
3460 RETURN_ERROR_IF_FAIL(size_num >= 0);
3462 RETURN_ERROR_IF_FAIL(*cur_pos == '\0');
3464 #undef RETURN_ERROR_IF_FAIL
3468 if (recv_bytes_write_to_file(SESSION(session)->sock,
3469 size_num, filename) != 0) {
3471 return GINT_TO_POINTER(IMAP_ERROR);
3473 if (imap_gen_recv_block(session, &buf) != IMAP_SUCCESS) {
3476 return GINT_TO_POINTER(IMAP_ERROR);
3479 if (buf[0] == '\0' || buf[strlen(buf) - 1] != ')') {
3482 return GINT_TO_POINTER(IMAP_ERROR);
3486 ok = imap_cmd_ok_block(session, NULL);
3489 return GINT_TO_POINTER(ok);
3492 static gint imap_cmd_fetch(IMAPSession *session, guint32 uid,
3493 const gchar *filename, gboolean headers,
3496 fetch_data *data = g_new0(fetch_data, 1);
3503 data->session = session;
3505 data->filename = filename;
3506 data->headers = headers;
3509 if (prefs_common.work_offline && !imap_gtk_should_override()) {
3514 #if (defined USE_PTHREAD && defined __GLIBC__ && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 3)))
3515 MUTEX_TRYLOCK_OR_RETURN_VAL(-1);
3516 if (pthread_create(&pt, PTHREAD_CREATE_JOINABLE,
3517 imap_cmd_fetch_thread, data) != 0) {
3518 result = GPOINTER_TO_INT(imap_cmd_fetch_thread(data));
3523 debug_print("+++waiting for imap_cmd_fetch_thread...\n");
3524 while(!data->done) {
3525 /* don't let the interface freeze while waiting */
3528 debug_print("---imap_cmd_fetch_thread done\n");
3530 /* get the thread's return value and clean its resources */
3531 pthread_join(pt, &tmp);
3532 result = GPOINTER_TO_INT(tmp);
3535 result = GPOINTER_TO_INT(imap_cmd_fetch_thread(data));
3541 static gint imap_cmd_append(IMAPSession *session, const gchar *destfolder,
3542 const gchar *file, IMAPFlags flags,
3549 unsigned int new_uid_;
3551 gchar buf[BUFFSIZE];
3556 g_return_val_if_fail(file != NULL, IMAP_ERROR);
3558 size = get_file_size_as_crlf(file);
3559 if ((fp = fopen(file, "rb")) == NULL) {
3560 FILE_OP_ERROR(file, "fopen");
3563 QUOTE_IF_REQUIRED(destfolder_, destfolder);
3564 flag_str = imap_get_flag_str(flags);
3565 imap_gen_send(session, "APPEND %s (%s) {%d}",
3566 destfolder_, flag_str, size);
3569 ok = imap_gen_recv(session, &ret);
3570 if (ok != IMAP_SUCCESS || ret[0] != '+' || ret[1] != ' ') {
3571 log_warning(_("can't append %s to %s\n"), file, destfolder_);
3578 log_print("IMAP4> %s\n", "(sending file...)");
3580 while (fgets(buf, sizeof(buf), fp) != NULL) {
3582 if (sock_puts(SESSION(session)->sock, buf) < 0) {
3589 FILE_OP_ERROR(file, "fgets");
3594 sock_puts(SESSION(session)->sock, "");
3598 if (new_uid != NULL)
3601 if (new_uid != NULL && session->uidplus) {
3602 argbuf = g_ptr_array_new();
3604 ok = imap_cmd_ok(session, argbuf);
3605 if ((ok == IMAP_SUCCESS) && (argbuf->len > 0)) {
3606 resp_str = g_ptr_array_index(argbuf, argbuf->len - 1);
3608 sscanf(resp_str, "%*u OK [APPENDUID %*u %u]",
3610 *new_uid = new_uid_;
3614 ptr_array_free_strings(argbuf);
3615 g_ptr_array_free(argbuf, TRUE);
3617 ok = imap_cmd_ok(session, NULL);
3619 if (ok != IMAP_SUCCESS)
3620 log_warning(_("can't append message to %s\n"),
3626 static MsgNumberList *imapset_to_numlist(IMAPSet imapset)
3628 gchar **ranges, **range;
3629 unsigned int low, high;
3630 MsgNumberList *uids = NULL;
3632 ranges = g_strsplit(imapset, ",", 0);
3633 for (range = ranges; *range != NULL; range++) {
3634 if (sscanf(*range, "%u:%u", &low, &high) == 1)
3635 uids = g_slist_prepend(uids, GINT_TO_POINTER(low));
3638 for (i = low; i <= high; i++)
3639 uids = g_slist_prepend(uids, GINT_TO_POINTER(i));
3642 uids = g_slist_reverse(uids);
3648 static gint imap_cmd_copy(IMAPSession *session, const gchar *seq_set,
3649 const gchar *destfolder, GRelation *uid_mapping)
3654 g_return_val_if_fail(session != NULL, IMAP_ERROR);
3655 g_return_val_if_fail(seq_set != NULL, IMAP_ERROR);
3656 g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
3658 QUOTE_IF_REQUIRED(destfolder_, destfolder);
3659 imap_gen_send(session, "UID COPY %s %s", seq_set, destfolder_);
3661 if (uid_mapping != NULL && session->uidplus) {
3663 gchar *resp_str = NULL, *olduids_str, *newuids_str;
3664 MsgNumberList *olduids, *old_cur, *newuids, *new_cur;
3666 reply = g_ptr_array_new();
3667 ok = imap_cmd_ok(session, reply);
3668 if ((ok == IMAP_SUCCESS) && (reply->len > 0)) {
3669 resp_str = g_ptr_array_index(reply, reply->len - 1);
3671 olduids_str = g_new0(gchar, strlen(resp_str));
3672 newuids_str = g_new0(gchar, strlen(resp_str));
3673 if (sscanf(resp_str, "%*s OK [COPYUID %*u %[0-9,:] %[0-9,:]]",
3674 olduids_str, newuids_str) == 2) {
3675 olduids = imapset_to_numlist(olduids_str);
3676 newuids = imapset_to_numlist(newuids_str);
3680 while(old_cur != NULL && new_cur != NULL) {
3681 g_relation_insert(uid_mapping,
3682 GPOINTER_TO_INT(old_cur->data),
3683 GPOINTER_TO_INT(new_cur->data));
3684 old_cur = g_slist_next(old_cur);
3685 new_cur = g_slist_next(new_cur);
3688 g_slist_free(olduids);
3689 g_slist_free(newuids);
3691 g_free(olduids_str);
3692 g_free(newuids_str);
3695 ptr_array_free_strings(reply);
3696 g_ptr_array_free(reply, TRUE);
3698 ok = imap_cmd_ok(session, NULL);
3700 if (ok != IMAP_SUCCESS)
3701 log_warning(_("can't copy %s to %s\n"), seq_set, destfolder_);
3706 gint imap_cmd_envelope(IMAPSession *session, IMAPSet set)
3708 static gchar *header_fields =
3709 "Date From To Cc Subject Message-ID References In-Reply-To" ;
3712 (session, "UID FETCH %s (UID FLAGS RFC822.SIZE BODY.PEEK[HEADER.FIELDS (%s)])",
3713 set, header_fields);
3715 return IMAP_SUCCESS;
3718 static gint imap_cmd_store(IMAPSession *session, IMAPSet seq_set,
3723 imap_gen_send(session, "UID STORE %s %s", seq_set, sub_cmd);
3725 if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS) {
3726 log_warning(_("error while imap command: STORE %s %s\n"),
3731 return IMAP_SUCCESS;
3734 typedef struct _expunge_data {
3735 IMAPSession *session;
3740 static void *imap_cmd_expunge_thread(void *data)
3742 expunge_data *stuff = (expunge_data *)data;
3743 IMAPSession *session = stuff->session;
3744 IMAPSet seq_set = stuff->seq_set;
3748 if (seq_set && session->uidplus)
3749 imap_gen_send(session, "UID EXPUNGE %s", seq_set);
3751 imap_gen_send(session, "EXPUNGE");
3752 if ((ok = imap_cmd_ok_with_block(session, NULL, TRUE)) != IMAP_SUCCESS) {
3753 log_warning(_("error while imap command: EXPUNGE\n"));
3755 return GINT_TO_POINTER(ok);
3759 return GINT_TO_POINTER(IMAP_SUCCESS);
3762 static gint imap_cmd_expunge(IMAPSession *session, IMAPSet seq_set)
3764 expunge_data *data = g_new0(expunge_data, 1);
3771 data->session = session;
3772 data->seq_set = seq_set;
3774 if (prefs_common.work_offline && !imap_gtk_should_override()) {
3779 #if (defined USE_PTHREAD && defined __GLIBC__ && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 3)))
3780 if (pthread_create(&pt, PTHREAD_CREATE_JOINABLE,
3781 imap_cmd_expunge_thread, data) != 0) {
3782 result = GPOINTER_TO_INT(imap_cmd_expunge_thread(data));
3786 debug_print("+++waiting for imap_cmd_expunge_thread...\n");
3787 while(!data->done) {
3788 /* don't let the interface freeze while waiting */
3791 debug_print("---imap_cmd_expunge_thread done\n");
3793 /* get the thread's return value and clean its resources */
3794 pthread_join(pt, &tmp);
3795 result = GPOINTER_TO_INT(tmp);
3797 result = GPOINTER_TO_INT(imap_cmd_expunge_thread(data));
3803 static gint imap_cmd_close(IMAPSession *session)
3807 imap_gen_send(session, "CLOSE");
3808 if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS)
3809 log_warning(_("error while imap command: CLOSE\n"));
3814 static gint imap_cmd_ok_with_block(IMAPSession *session, GPtrArray *argbuf, gboolean block)
3816 gint ok = IMAP_SUCCESS;
3821 while ((ok = imap_gen_recv_with_block(session, &buf, block))
3823 /* make sure data is long enough for any substring of buf */
3824 data = alloca(strlen(buf) + 1);
3826 /* untagged line read */
3827 if (buf[0] == '*' && buf[1] == ' ') {
3830 g_ptr_array_add(argbuf, g_strdup(buf + 2));
3832 if (sscanf(buf + 2, "%d %s", &num, data) >= 2) {
3833 if (!strcmp(data, "EXISTS")) {
3834 session->exists = num;
3835 session->folder_content_changed = TRUE;
3838 if(!strcmp(data, "EXPUNGE")) {
3840 session->folder_content_changed = TRUE;
3843 /* tagged line with correct tag and OK response found */
3844 } else if ((sscanf(buf, "%d %s", &cmd_num, data) >= 2) &&
3845 (cmd_num == session->cmd_count) &&
3846 !strcmp(data, "OK")) {
3848 g_ptr_array_add(argbuf, g_strdup(buf));
3850 /* everything else */
3861 static gint imap_cmd_ok(IMAPSession *session, GPtrArray *argbuf)
3863 return imap_cmd_ok_with_block(session, argbuf, FALSE);
3865 static gint imap_cmd_ok_block(IMAPSession *session, GPtrArray *argbuf)
3867 return imap_cmd_ok_with_block(session, argbuf, TRUE);
3869 static void imap_gen_send(IMAPSession *session, const gchar *format, ...)
3876 va_start(args, format);
3877 tmp = g_strdup_vprintf(format, args);
3880 session->cmd_count++;
3882 buf = g_strdup_printf("%d %s\r\n", session->cmd_count, tmp);
3883 if (!g_ascii_strncasecmp(tmp, "LOGIN ", 6) && (p = strchr(tmp + 6, ' '))) {
3885 log_print("IMAP4> %d %s ********\n", session->cmd_count, tmp);
3887 log_print("IMAP4> %d %s\n", session->cmd_count, tmp);
3889 sock_write_all(SESSION(session)->sock, buf, strlen(buf));
3894 static gint imap_gen_recv_with_block(IMAPSession *session, gchar **ret, gboolean block)
3897 if ((*ret = imap_getline(SESSION(session)->sock)) == NULL)
3900 if ((*ret = sock_getline(SESSION(session)->sock)) == NULL)
3905 log_print("IMAP4< %s\n", *ret);
3907 session_set_access_time(SESSION(session));
3909 return IMAP_SUCCESS;
3912 static gint imap_gen_recv_block(IMAPSession *session, gchar **ret)
3914 return imap_gen_recv_with_block(session, ret, TRUE);
3917 static gint imap_gen_recv(IMAPSession *session, gchar **ret)
3919 return imap_gen_recv_with_block(session, ret, FALSE);
3921 /* misc utility functions */
3923 static gchar *strchr_cpy(const gchar *src, gchar ch, gchar *dest, gint len)
3928 tmp = strchr(src, ch);
3932 memcpy(dest, src, MIN(tmp - src, len - 1));
3933 dest[MIN(tmp - src, len - 1)] = '\0';
3938 static gchar *get_quoted(const gchar *src, gchar ch, gchar *dest, gint len)
3940 const gchar *p = src;
3943 g_return_val_if_fail(*p == ch, NULL);
3948 while (*p != '\0' && *p != ch) {
3950 if (*p == '\\' && *(p + 1) != '\0')
3959 return (gchar *)(*p == ch ? p + 1 : p);
3962 static gchar *search_array_contain_str(GPtrArray *array, const gchar *str)
3966 for (i = 0; i < array->len; i++) {
3969 tmp = g_ptr_array_index(array, i);
3970 if (strstr(tmp, str) != NULL)
3977 static gchar *search_array_str(GPtrArray *array, const gchar *str)
3984 for (i = 0; i < array->len; i++) {
3987 tmp = g_ptr_array_index(array, i);
3988 if (!strncmp(tmp, str, len))
3995 static void imap_path_separator_subst(gchar *str, gchar separator)
3998 gboolean in_escape = FALSE;
4000 if (!separator || separator == '/') return;
4002 for (p = str; *p != '\0'; p++) {
4003 if (*p == '/' && !in_escape)
4005 else if (*p == '&' && *(p + 1) != '-' && !in_escape)
4007 else if (*p == '-' && in_escape)
4012 static gchar *imap_modified_utf7_to_utf8(const gchar *mutf7_str)
4014 static iconv_t cd = (iconv_t)-1;
4015 static gboolean iconv_ok = TRUE;
4018 size_t norm_utf7_len;
4020 gchar *to_str, *to_p;
4022 gboolean in_escape = FALSE;
4024 if (!iconv_ok) return g_strdup(mutf7_str);
4026 if (cd == (iconv_t)-1) {
4027 cd = iconv_open(CS_INTERNAL, CS_UTF_7);
4028 if (cd == (iconv_t)-1) {
4029 g_warning("iconv cannot convert UTF-7 to %s\n",
4032 return g_strdup(mutf7_str);
4036 /* modified UTF-7 to normal UTF-7 conversion */
4037 norm_utf7 = g_string_new(NULL);
4039 for (p = mutf7_str; *p != '\0'; p++) {
4040 /* replace: '&' -> '+',
4042 escaped ',' -> '/' */
4043 if (!in_escape && *p == '&') {
4044 if (*(p + 1) != '-') {
4045 g_string_append_c(norm_utf7, '+');
4048 g_string_append_c(norm_utf7, '&');
4051 } else if (in_escape && *p == ',') {
4052 g_string_append_c(norm_utf7, '/');
4053 } else if (in_escape && *p == '-') {
4054 g_string_append_c(norm_utf7, '-');
4057 g_string_append_c(norm_utf7, *p);
4061 norm_utf7_p = norm_utf7->str;
4062 norm_utf7_len = norm_utf7->len;
4063 to_len = strlen(mutf7_str) * 5;
4064 to_p = to_str = g_malloc(to_len + 1);
4066 if (iconv(cd, (ICONV_CONST gchar **)&norm_utf7_p, &norm_utf7_len,
4067 &to_p, &to_len) == -1) {
4068 g_warning(_("iconv cannot convert UTF-7 to %s\n"),
4069 conv_get_locale_charset_str());
4070 g_string_free(norm_utf7, TRUE);
4072 return g_strdup(mutf7_str);
4075 /* second iconv() call for flushing */
4076 iconv(cd, NULL, NULL, &to_p, &to_len);
4077 g_string_free(norm_utf7, TRUE);
4083 static gchar *imap_utf8_to_modified_utf7(const gchar *from)
4085 static iconv_t cd = (iconv_t)-1;
4086 static gboolean iconv_ok = TRUE;
4087 gchar *norm_utf7, *norm_utf7_p;
4088 size_t from_len, norm_utf7_len;
4090 gchar *from_tmp, *to, *p;
4091 gboolean in_escape = FALSE;
4093 if (!iconv_ok) return g_strdup(from);
4095 if (cd == (iconv_t)-1) {
4096 cd = iconv_open(CS_UTF_7, CS_INTERNAL);
4097 if (cd == (iconv_t)-1) {
4098 g_warning(_("iconv cannot convert %s to UTF-7\n"),
4101 return g_strdup(from);
4105 /* UTF-8 to normal UTF-7 conversion */
4106 Xstrdup_a(from_tmp, from, return g_strdup(from));
4107 from_len = strlen(from);
4108 norm_utf7_len = from_len * 5;
4109 Xalloca(norm_utf7, norm_utf7_len + 1, return g_strdup(from));
4110 norm_utf7_p = norm_utf7;
4112 #define IS_PRINT(ch) (isprint(ch) && IS_ASCII(ch))
4114 while (from_len > 0) {
4115 if (*from_tmp == '+') {
4116 *norm_utf7_p++ = '+';
4117 *norm_utf7_p++ = '-';
4121 } else if (IS_PRINT(*(guchar *)from_tmp)) {
4122 /* printable ascii char */
4123 *norm_utf7_p = *from_tmp;
4129 size_t conv_len = 0;
4131 /* unprintable char: convert to UTF-7 */
4133 while (!IS_PRINT(*(guchar *)p) && conv_len < from_len) {
4134 conv_len += g_utf8_skip[*(guchar *)p];
4135 p += g_utf8_skip[*(guchar *)p];
4138 from_len -= conv_len;
4139 if (iconv(cd, (ICONV_CONST gchar **)&from_tmp,
4141 &norm_utf7_p, &norm_utf7_len) == -1) {
4142 g_warning(_("iconv cannot convert UTF-8 to UTF-7\n"));
4143 return g_strdup(from);
4146 /* second iconv() call for flushing */
4147 iconv(cd, NULL, NULL, &norm_utf7_p, &norm_utf7_len);
4153 *norm_utf7_p = '\0';
4154 to_str = g_string_new(NULL);
4155 for (p = norm_utf7; p < norm_utf7_p; p++) {
4156 /* replace: '&' -> "&-",
4159 BASE64 '/' -> ',' */
4160 if (!in_escape && *p == '&') {
4161 g_string_append(to_str, "&-");
4162 } else if (!in_escape && *p == '+') {
4163 if (*(p + 1) == '-') {
4164 g_string_append_c(to_str, '+');
4167 g_string_append_c(to_str, '&');
4170 } else if (in_escape && *p == '/') {
4171 g_string_append_c(to_str, ',');
4172 } else if (in_escape && *p == '-') {
4173 g_string_append_c(to_str, '-');
4176 g_string_append_c(to_str, *p);
4182 g_string_append_c(to_str, '-');
4186 g_string_free(to_str, FALSE);
4191 static GSList *imap_get_seq_set_from_numlist(MsgNumberList *numlist)
4194 GSList *sorted_list, *cur;
4195 guint first, last, next;
4197 GSList *ret_list = NULL;
4199 if (numlist == NULL)
4202 str = g_string_sized_new(256);
4204 sorted_list = g_slist_copy(numlist);
4205 sorted_list = g_slist_sort(sorted_list, g_int_compare);
4207 first = GPOINTER_TO_INT(sorted_list->data);
4209 for (cur = sorted_list; cur != NULL; cur = g_slist_next(cur)) {
4210 if (GPOINTER_TO_INT(cur->data) == 0)
4213 last = GPOINTER_TO_INT(cur->data);
4215 next = GPOINTER_TO_INT(cur->next->data);
4219 if (last + 1 != next || next == 0) {
4221 g_string_append_c(str, ',');
4223 g_string_append_printf(str, "%u", first);
4225 g_string_append_printf(str, "%u:%u", first, last);
4229 if (str->len > IMAP_CMD_LIMIT) {
4230 ret_str = g_strdup(str->str);
4231 ret_list = g_slist_append(ret_list, ret_str);
4232 g_string_truncate(str, 0);
4238 ret_str = g_strdup(str->str);
4239 ret_list = g_slist_append(ret_list, ret_str);
4242 g_slist_free(sorted_list);
4243 g_string_free(str, TRUE);
4248 static GSList *imap_get_seq_set_from_msglist(MsgInfoList *msglist)
4250 MsgNumberList *numlist = NULL;
4254 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
4255 MsgInfo *msginfo = (MsgInfo *) cur->data;
4257 numlist = g_slist_append(numlist, GINT_TO_POINTER(msginfo->msgnum));
4259 seq_list = imap_get_seq_set_from_numlist(numlist);
4260 g_slist_free(numlist);
4265 static void imap_seq_set_free(GSList *seq_list)
4267 slist_free_strings(seq_list);
4268 g_slist_free(seq_list);
4272 static gboolean imap_rename_folder_func(GNode *node, gpointer data)
4274 FolderItem *item = node->data;
4275 gchar **paths = data;
4276 const gchar *oldpath = paths[0];
4277 const gchar *newpath = paths[1];
4279 gchar *new_itempath;
4282 oldpathlen = strlen(oldpath);
4283 if (strncmp(oldpath, item->path, oldpathlen) != 0) {
4284 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
4288 base = item->path + oldpathlen;
4289 while (*base == G_DIR_SEPARATOR) base++;
4291 new_itempath = g_strdup(newpath);
4293 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
4296 item->path = new_itempath;
4301 typedef struct _get_list_uid_data {
4303 IMAPFolderItem *item;
4304 GSList **msgnum_list;
4306 } get_list_uid_data;
4308 static void *get_list_of_uids_thread(void *data)
4310 get_list_uid_data *stuff = (get_list_uid_data *)data;
4311 Folder *folder = stuff->folder;
4312 IMAPFolderItem *item = stuff->item;
4313 GSList **msgnum_list = stuff->msgnum_list;
4314 gint ok, nummsgs = 0, lastuid_old;
4315 IMAPSession *session;
4316 GSList *uidlist, *elem;
4319 session = imap_session_get(folder);
4320 if (session == NULL) {
4322 return GINT_TO_POINTER(-1);
4325 ok = imap_select(session, IMAP_FOLDER(folder), item->item.path,
4326 NULL, NULL, NULL, NULL, TRUE);
4327 if (ok != IMAP_SUCCESS) {
4329 return GINT_TO_POINTER(-1);
4332 cmd_buf = g_strdup_printf("UID %d:*", item->lastuid + 1);
4333 ok = imap_cmd_search(session, cmd_buf, &uidlist, TRUE);
4336 if (ok == IMAP_SOCKET) {
4337 session_destroy((Session *)session);
4338 ((RemoteFolder *)folder)->session = NULL;
4340 return GINT_TO_POINTER(-1);
4343 if (ok != IMAP_SUCCESS) {
4347 argbuf = g_ptr_array_new();
4349 cmd_buf = g_strdup_printf("UID FETCH %d:* (UID)", item->lastuid + 1);
4350 imap_gen_send(session, cmd_buf);
4352 ok = imap_cmd_ok_block(session, argbuf);
4353 if (ok != IMAP_SUCCESS) {
4354 ptr_array_free_strings(argbuf);
4355 g_ptr_array_free(argbuf, TRUE);
4357 return GINT_TO_POINTER(-1);
4360 for(i = 0; i < argbuf->len; i++) {
4363 if((ret = sscanf(g_ptr_array_index(argbuf, i),
4364 "%*d FETCH (UID %d)", &msgnum)) == 1)
4365 uidlist = g_slist_prepend(uidlist, GINT_TO_POINTER(msgnum));
4367 ptr_array_free_strings(argbuf);
4368 g_ptr_array_free(argbuf, TRUE);
4371 lastuid_old = item->lastuid;
4372 *msgnum_list = g_slist_copy(item->uid_list);
4373 nummsgs = g_slist_length(*msgnum_list);
4374 debug_print("Got %d uids from cache\n", g_slist_length(item->uid_list));
4376 for (elem = uidlist; elem != NULL; elem = g_slist_next(elem)) {
4379 msgnum = GPOINTER_TO_INT(elem->data);
4380 if (msgnum > lastuid_old) {
4381 *msgnum_list = g_slist_prepend(*msgnum_list, GINT_TO_POINTER(msgnum));
4382 item->uid_list = g_slist_prepend(item->uid_list, GINT_TO_POINTER(msgnum));
4385 if(msgnum > item->lastuid)
4386 item->lastuid = msgnum;
4389 g_slist_free(uidlist);
4392 return GINT_TO_POINTER(nummsgs);
4395 static gint get_list_of_uids(Folder *folder, IMAPFolderItem *item, GSList **msgnum_list)
4398 get_list_uid_data *data = g_new0(get_list_uid_data, 1);
4404 data->folder = folder;
4406 data->msgnum_list = msgnum_list;
4408 if (prefs_common.work_offline && !imap_gtk_should_override()) {
4413 #if (defined USE_PTHREAD && defined __GLIBC__ && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 3)))
4414 if (pthread_create(&pt, PTHREAD_CREATE_JOINABLE,
4415 get_list_of_uids_thread, data) != 0) {
4416 result = GPOINTER_TO_INT(get_list_of_uids_thread(data));
4420 debug_print("+++waiting for get_list_of_uids_thread...\n");
4421 while(!data->done) {
4422 /* don't let the interface freeze while waiting */
4425 debug_print("---get_list_of_uids_thread done\n");
4427 /* get the thread's return value and clean its resources */
4428 pthread_join(pt, &tmp);
4429 result = GPOINTER_TO_INT(tmp);
4431 result = GPOINTER_TO_INT(get_list_of_uids_thread(data));
4438 gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list, gboolean *old_uids_valid)
4440 IMAPFolderItem *item = (IMAPFolderItem *)_item;
4441 IMAPSession *session;
4442 gint ok, nummsgs = 0, exists, recent, uid_val, uid_next, unseen;
4443 GSList *uidlist = NULL;
4445 gboolean selected_folder;
4447 g_return_val_if_fail(folder != NULL, -1);
4448 g_return_val_if_fail(item != NULL, -1);
4449 g_return_val_if_fail(item->item.path != NULL, -1);
4450 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
4451 g_return_val_if_fail(folder->account != NULL, -1);
4453 MUTEX_TRYLOCK_OR_RETURN_VAL(-1);
4455 session = imap_session_get(folder);
4456 g_return_val_if_fail(session != NULL, -1);
4458 selected_folder = (session->mbox != NULL) &&
4459 (!strcmp(session->mbox, item->item.path));
4460 if (selected_folder) {
4461 ok = imap_cmd_noop(session);
4462 if (ok != IMAP_SUCCESS) {
4466 exists = session->exists;
4468 *old_uids_valid = TRUE;
4470 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
4471 &exists, &recent, &uid_next, &uid_val, &unseen, FALSE);
4472 if (ok != IMAP_SUCCESS) {
4476 if(item->item.mtime == uid_val)
4477 *old_uids_valid = TRUE;
4479 *old_uids_valid = FALSE;
4481 debug_print("Freeing imap uid cache\n");
4483 g_slist_free(item->uid_list);
4484 item->uid_list = NULL;
4486 item->item.mtime = uid_val;
4488 imap_delete_all_cached_messages((FolderItem *)item);
4492 if (!selected_folder)
4493 item->uid_next = uid_next;
4495 /* If old uid_next matches new uid_next we can be sure no message
4496 was added to the folder */
4497 if (( selected_folder && !session->folder_content_changed) ||
4498 (!selected_folder && uid_next == item->uid_next)) {
4499 nummsgs = g_slist_length(item->uid_list);
4501 /* If number of messages is still the same we
4502 know our caches message numbers are still valid,
4503 otherwise if the number of messages has decrease
4504 we discard our cache to start a new scan to find
4505 out which numbers have been removed */
4506 if (exists == nummsgs) {
4507 *msgnum_list = g_slist_copy(item->uid_list);
4510 } else if (exists < nummsgs) {
4511 debug_print("Freeing imap uid cache");
4513 g_slist_free(item->uid_list);
4514 item->uid_list = NULL;
4519 *msgnum_list = NULL;
4524 nummsgs = get_list_of_uids(folder, item, &uidlist);
4531 if (nummsgs != exists) {
4532 /* Cache contains more messages then folder, we have cached
4533 an old UID of a message that was removed and new messages
4534 have been added too, otherwise the uid_next check would
4536 debug_print("Freeing imap uid cache");
4538 g_slist_free(item->uid_list);
4539 item->uid_list = NULL;
4541 g_slist_free(*msgnum_list);
4543 nummsgs = get_list_of_uids(folder, item, &uidlist);
4546 *msgnum_list = uidlist;
4548 dir = folder_item_get_path((FolderItem *)item);
4549 debug_print("removing old messages from %s\n", dir);
4550 remove_numbered_files_not_in_list(dir, *msgnum_list);
4557 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item)
4562 flags.perm_flags = MSG_NEW|MSG_UNREAD;
4563 flags.tmp_flags = 0;
4565 g_return_val_if_fail(item != NULL, NULL);
4566 g_return_val_if_fail(file != NULL, NULL);
4568 if (item->stype == F_QUEUE) {
4569 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
4570 } else if (item->stype == F_DRAFT) {
4571 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
4574 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
4575 if (!msginfo) return NULL;
4577 msginfo->plaintext_file = g_strdup(file);
4578 msginfo->folder = item;
4583 GSList *imap_get_msginfos(Folder *folder, FolderItem *item, GSList *msgnum_list)
4585 IMAPSession *session;
4586 MsgInfoList *ret = NULL;
4589 g_return_val_if_fail(folder != NULL, NULL);
4590 g_return_val_if_fail(item != NULL, NULL);
4591 g_return_val_if_fail(msgnum_list != NULL, NULL);
4593 session = imap_session_get(folder);
4594 g_return_val_if_fail(session != NULL, NULL);
4596 debug_print("IMAP getting msginfos\n");
4597 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
4598 NULL, NULL, NULL, NULL, FALSE);
4599 if (ok != IMAP_SUCCESS)
4602 if (!(item->stype == F_QUEUE || item->stype == F_DRAFT)) {
4603 ret = g_slist_concat(ret,
4604 imap_get_uncached_messages(
4605 session, item, msgnum_list));
4607 MsgNumberList *sorted_list, *elem;
4608 gint startnum, lastnum;
4610 sorted_list = g_slist_sort(g_slist_copy(msgnum_list), g_int_compare);
4612 startnum = lastnum = GPOINTER_TO_INT(sorted_list->data);
4614 for (elem = sorted_list;; elem = g_slist_next(elem)) {
4618 num = GPOINTER_TO_INT(elem->data);
4620 if (num > lastnum + 1 || elem == NULL) {
4622 for (i = startnum; i <= lastnum; ++i) {
4625 file = imap_fetch_msg(folder, item, i);
4627 MsgInfo *msginfo = imap_parse_msg(file, item);
4628 if (msginfo != NULL) {
4629 msginfo->msgnum = i;
4630 ret = g_slist_append(ret, msginfo);
4644 g_slist_free(sorted_list);
4650 MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid)
4652 MsgInfo *msginfo = NULL;
4653 MsgInfoList *msginfolist;
4654 MsgNumberList numlist;
4656 numlist.next = NULL;
4657 numlist.data = GINT_TO_POINTER(uid);
4659 msginfolist = imap_get_msginfos(folder, item, &numlist);
4660 if (msginfolist != NULL) {
4661 msginfo = msginfolist->data;
4662 g_slist_free(msginfolist);
4668 gboolean imap_scan_required(Folder *folder, FolderItem *_item)
4670 IMAPSession *session;
4671 IMAPFolderItem *item = (IMAPFolderItem *)_item;
4672 gint ok, exists = 0, recent = 0, unseen = 0;
4673 guint32 uid_next, uid_val = 0;
4674 gboolean selected_folder;
4676 g_return_val_if_fail(folder != NULL, FALSE);
4677 g_return_val_if_fail(item != NULL, FALSE);
4678 g_return_val_if_fail(item->item.folder != NULL, FALSE);
4679 g_return_val_if_fail(FOLDER_CLASS(item->item.folder) == &imap_class, FALSE);
4681 if (item->item.path == NULL)
4684 session = imap_session_get(folder);
4685 g_return_val_if_fail(session != NULL, FALSE);
4687 selected_folder = (session->mbox != NULL) &&
4688 (!strcmp(session->mbox, item->item.path));
4689 if (selected_folder) {
4690 ok = imap_cmd_noop(session);
4691 if (ok != IMAP_SUCCESS)
4694 if (session->folder_content_changed)
4697 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
4698 &exists, &recent, &uid_next, &uid_val, &unseen, FALSE);
4699 if (ok != IMAP_SUCCESS)
4702 if ((uid_next != item->uid_next) || (exists != item->item.total_msgs))
4709 void imap_change_flags(Folder *folder, FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
4711 IMAPSession *session;
4712 IMAPFlags flags_set = 0, flags_unset = 0;
4713 gint ok = IMAP_SUCCESS;
4714 MsgNumberList numlist;
4715 hashtable_data *ht_data = NULL;
4717 g_return_if_fail(folder != NULL);
4718 g_return_if_fail(folder->klass == &imap_class);
4719 g_return_if_fail(item != NULL);
4720 g_return_if_fail(item->folder == folder);
4721 g_return_if_fail(msginfo != NULL);
4722 g_return_if_fail(msginfo->folder == item);
4724 MUTEX_TRYLOCK_OR_RETURN();
4726 session = imap_session_get(folder);
4731 if ((ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
4732 NULL, NULL, NULL, NULL, FALSE)) != IMAP_SUCCESS) {
4737 if (!MSG_IS_MARKED(msginfo->flags) && (newflags & MSG_MARKED))
4738 flags_set |= IMAP_FLAG_FLAGGED;
4739 if ( MSG_IS_MARKED(msginfo->flags) && !(newflags & MSG_MARKED))
4740 flags_unset |= IMAP_FLAG_FLAGGED;
4742 if (!MSG_IS_UNREAD(msginfo->flags) && (newflags & MSG_UNREAD))
4743 flags_unset |= IMAP_FLAG_SEEN;
4744 if ( MSG_IS_UNREAD(msginfo->flags) && !(newflags & MSG_UNREAD))
4745 flags_set |= IMAP_FLAG_SEEN;
4747 if (!MSG_IS_REPLIED(msginfo->flags) && (newflags & MSG_REPLIED))
4748 flags_set |= IMAP_FLAG_ANSWERED;
4749 if ( MSG_IS_REPLIED(msginfo->flags) && !(newflags & MSG_REPLIED))
4750 flags_set |= IMAP_FLAG_ANSWERED;
4752 numlist.next = NULL;
4753 numlist.data = GINT_TO_POINTER(msginfo->msgnum);
4755 if (IMAP_FOLDER_ITEM(item)->batching) {
4756 /* instead of performing an UID STORE command for each message change,
4757 * as a lot of them can change "together", we just fill in hashtables
4758 * and defer the treatment so that we're able to send only one
4761 debug_print("IMAP batch mode on, deferring flags change\n");
4763 ht_data = g_hash_table_lookup(flags_set_table, GINT_TO_POINTER(flags_set));
4764 if (ht_data == NULL) {
4765 ht_data = g_new0(hashtable_data, 1);
4766 ht_data->session = session;
4767 g_hash_table_insert(flags_set_table, GINT_TO_POINTER(flags_set), ht_data);
4769 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
4770 ht_data->msglist = g_slist_prepend(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum));
4773 ht_data = g_hash_table_lookup(flags_unset_table, GINT_TO_POINTER(flags_unset));
4774 if (ht_data == NULL) {
4775 ht_data = g_new0(hashtable_data, 1);
4776 ht_data->session = session;
4777 g_hash_table_insert(flags_unset_table, GINT_TO_POINTER(flags_unset), ht_data);
4779 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
4780 ht_data->msglist = g_slist_prepend(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum));
4783 debug_print("IMAP changing flags\n");
4785 ok = imap_set_message_flags(session, &numlist, flags_set, TRUE);
4786 if (ok != IMAP_SUCCESS) {
4793 ok = imap_set_message_flags(session, &numlist, flags_unset, FALSE);
4794 if (ok != IMAP_SUCCESS) {
4800 msginfo->flags.perm_flags = newflags;
4806 static gint imap_remove_msg(Folder *folder, FolderItem *item, gint uid)
4809 IMAPSession *session;
4811 MsgNumberList numlist;
4813 g_return_val_if_fail(folder != NULL, -1);
4814 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
4815 g_return_val_if_fail(item != NULL, -1);
4817 session = imap_session_get(folder);
4818 if (!session) return -1;
4820 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
4821 NULL, NULL, NULL, NULL, FALSE);
4822 if (ok != IMAP_SUCCESS)
4825 numlist.next = NULL;
4826 numlist.data = GINT_TO_POINTER(uid);
4828 ok = imap_set_message_flags
4829 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
4830 &numlist, IMAP_FLAG_DELETED, TRUE);
4831 if (ok != IMAP_SUCCESS) {
4832 log_warning(_("can't set deleted flags: %d\n"), uid);
4836 if (!session->uidplus) {
4837 ok = imap_cmd_expunge(session, NULL);
4841 uidstr = g_strdup_printf("%u", uid);
4842 ok = imap_cmd_expunge(session, uidstr);
4845 if (ok != IMAP_SUCCESS) {
4846 log_warning(_("can't expunge\n"));
4850 IMAP_FOLDER_ITEM(item)->uid_list = g_slist_remove(
4851 IMAP_FOLDER_ITEM(item)->uid_list, numlist.data);
4852 dir = folder_item_get_path(item);
4853 if (is_dir_exist(dir))
4854 remove_numbered_files(dir, uid, uid);
4857 return IMAP_SUCCESS;
4860 static gint compare_msginfo(gconstpointer a, gconstpointer b)
4862 return ((MsgInfo *)a)->msgnum - ((MsgInfo *)b)->msgnum;
4865 static guint gslist_find_next_num(MsgNumberList **list, guint num)
4869 g_return_val_if_fail(list != NULL, -1);
4871 for (elem = *list; elem != NULL; elem = g_slist_next(elem))
4872 if (GPOINTER_TO_INT(elem->data) >= num)
4875 return elem != NULL ? GPOINTER_TO_INT(elem->data) : (gint)-1;
4879 * NEW and DELETED flags are not syncronized
4880 * - The NEW/RECENT flags in IMAP folders can not really be directly
4881 * modified by Sylpheed
4882 * - The DELETE/DELETED flag in IMAP and Sylpheed don't have the same
4883 * meaning, in IMAP it always removes the messages from the FolderItem
4884 * in Sylpheed it can mean to move the message to trash
4887 typedef struct _get_flags_data {
4890 MsgInfoList *msginfo_list;
4891 GRelation *msgflags;
4895 static /*gint*/ void *imap_get_flags_thread(void *data)
4897 get_flags_data *stuff = (get_flags_data *)data;
4898 Folder *folder = stuff->folder;
4899 FolderItem *item = stuff->item;
4900 MsgInfoList *msginfo_list = stuff->msginfo_list;
4901 GRelation *msgflags = stuff->msgflags;
4902 IMAPSession *session;
4903 GSList *sorted_list;
4904 GSList *unseen = NULL, *answered = NULL, *flagged = NULL;
4905 GSList *p_unseen, *p_answered, *p_flagged;
4907 GSList *seq_list, *cur;
4908 gboolean reverse_seen = FALSE;
4911 gint exists_cnt, recent_cnt, unseen_cnt, uid_next;
4912 guint32 uidvalidity;
4913 gboolean selected_folder;
4915 if (folder == NULL || item == NULL) {
4917 return GINT_TO_POINTER(-1);
4919 if (msginfo_list == NULL) {
4921 return GINT_TO_POINTER(0);
4924 session = imap_session_get(folder);
4925 if (session == NULL) {
4927 return GINT_TO_POINTER(-1);
4930 selected_folder = (session->mbox != NULL) &&
4931 (!strcmp(session->mbox, item->path));
4933 if (!selected_folder) {
4934 ok = imap_status(session, IMAP_FOLDER(folder), item->path,
4935 &exists_cnt, &recent_cnt, &uid_next, &uidvalidity, &unseen_cnt, TRUE);
4936 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
4937 NULL, NULL, NULL, NULL, TRUE);
4938 if (ok != IMAP_SUCCESS) {
4940 return GINT_TO_POINTER(-1);
4945 if (unseen_cnt > exists_cnt / 2)
4946 reverse_seen = TRUE;
4948 cmd_buf = g_string_new(NULL);
4950 sorted_list = g_slist_sort(g_slist_copy(msginfo_list), compare_msginfo);
4952 seq_list = imap_get_seq_set_from_msglist(msginfo_list);
4954 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
4955 IMAPSet imapset = cur->data;
4957 g_string_sprintf(cmd_buf, "%sSEEN UID %s", reverse_seen ? "" : "UN", imapset);
4958 imap_cmd_search(session, cmd_buf->str, &p_unseen, TRUE);
4959 unseen = g_slist_concat(unseen, p_unseen);
4961 g_string_sprintf(cmd_buf, "ANSWERED UID %s", imapset);
4962 imap_cmd_search(session, cmd_buf->str, &p_answered, TRUE);
4963 answered = g_slist_concat(answered, p_answered);
4965 g_string_sprintf(cmd_buf, "FLAGGED UID %s", imapset);
4966 imap_cmd_search(session, cmd_buf->str, &p_flagged, TRUE);
4967 flagged = g_slist_concat(flagged, p_flagged);
4971 p_answered = answered;
4972 p_flagged = flagged;
4974 for (elem = sorted_list; elem != NULL; elem = g_slist_next(elem)) {
4979 msginfo = (MsgInfo *) elem->data;
4980 flags = msginfo->flags.perm_flags;
4981 wasnew = (flags & MSG_NEW);
4982 flags &= ~((reverse_seen ? 0 : MSG_UNREAD | MSG_NEW) | MSG_REPLIED | MSG_MARKED);
4984 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
4985 if (gslist_find_next_num(&p_unseen, msginfo->msgnum) == msginfo->msgnum) {
4986 if (!reverse_seen) {
4987 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
4989 flags &= ~(MSG_UNREAD | MSG_NEW);
4992 if (gslist_find_next_num(&p_answered, msginfo->msgnum) == msginfo->msgnum)
4993 flags |= MSG_REPLIED;
4994 if (gslist_find_next_num(&p_flagged, msginfo->msgnum) == msginfo->msgnum)
4995 flags |= MSG_MARKED;
4996 g_relation_insert(msgflags, msginfo, GINT_TO_POINTER(flags));
4999 imap_seq_set_free(seq_list);
5000 g_slist_free(flagged);
5001 g_slist_free(answered);
5002 g_slist_free(unseen);
5003 g_slist_free(sorted_list);
5004 g_string_free(cmd_buf, TRUE);
5007 return GINT_TO_POINTER(0);
5010 static gint imap_get_flags(Folder *folder, FolderItem *item,
5011 MsgInfoList *msginfo_list, GRelation *msgflags)
5014 get_flags_data *data = g_new0(get_flags_data, 1);
5020 data->folder = folder;
5022 data->msginfo_list = msginfo_list;
5023 data->msgflags = msgflags;
5025 if (prefs_common.work_offline && !imap_gtk_should_override()) {
5030 #if (defined USE_PTHREAD && defined __GLIBC__ && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 3)))
5031 MUTEX_TRYLOCK_OR_RETURN_VAL(-1);
5032 if (pthread_create(&pt, PTHREAD_CREATE_JOINABLE,
5033 imap_get_flags_thread, data) != 0) {
5034 result = GPOINTER_TO_INT(imap_get_flags_thread(data));
5039 debug_print("+++waiting for imap_get_flags_thread...\n");
5040 while(!data->done) {
5041 /* don't let the interface freeze while waiting */
5044 debug_print("---imap_get_flags_thread done\n");
5046 /* get the thread's return value and clean its resources */
5047 pthread_join(pt, &tmp);
5048 result = GPOINTER_TO_INT(tmp);
5051 result = GPOINTER_TO_INT(imap_get_flags_thread(data));
5058 static gboolean process_flags(gpointer key, gpointer value, gpointer user_data)
5060 gboolean flags_set = GPOINTER_TO_INT(user_data);
5061 gint flags_value = GPOINTER_TO_INT(key);
5062 hashtable_data *data = (hashtable_data *)value;
5064 data->msglist = g_slist_reverse(data->msglist);
5066 debug_print("IMAP %ssetting flags to %d for %d messages\n",
5069 g_slist_length(data->msglist));
5070 imap_set_message_flags(data->session, data->msglist, flags_value, flags_set);
5072 g_slist_free(data->msglist);
5077 static void process_hashtable(void)
5079 MUTEX_TRYLOCK_OR_RETURN();
5080 if (flags_set_table) {
5081 g_hash_table_foreach_remove(flags_set_table, process_flags, GINT_TO_POINTER(TRUE));
5082 g_free(flags_set_table);
5083 flags_set_table = NULL;
5085 if (flags_unset_table) {
5086 g_hash_table_foreach_remove(flags_unset_table, process_flags, GINT_TO_POINTER(FALSE));
5087 g_free(flags_unset_table);
5088 flags_unset_table = NULL;
5093 static IMAPFolderItem *batching_item = NULL;
5095 static void imap_set_batch (Folder *folder, FolderItem *_item, gboolean batch)
5097 IMAPFolderItem *item = (IMAPFolderItem *)_item;
5099 g_return_if_fail(item != NULL);
5101 if (batch && batching_item != NULL) {
5102 g_warning("already batching on %s\n", batching_item->item.path);
5106 if (item->batching == batch)
5109 item->batching = batch;
5111 batching_item = batch?item:NULL;
5114 debug_print("IMAP switching to batch mode\n");
5115 if (flags_set_table) {
5116 g_warning("flags_set_table non-null but we just entered batch mode!\n");
5117 flags_set_table = NULL;
5119 if (flags_unset_table) {
5120 g_warning("flags_unset_table non-null but we just entered batch mode!\n");
5121 flags_unset_table = NULL;
5123 flags_set_table = g_hash_table_new(NULL, g_direct_equal);
5124 flags_unset_table = g_hash_table_new(NULL, g_direct_equal);
5126 debug_print("IMAP switching away from batch mode\n");
5128 process_hashtable();