2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2005 Hiroyuki Yamamoto
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27 #include <glib/gi18n.h>
50 #include "procheader.h"
51 #include "prefs_account.h"
56 #include "prefs_common.h"
57 #include "inputdialog.h"
59 #include "remotefolder.h"
60 #include "alertpanel.h"
62 #include "statusbar.h"
67 static pthread_mutex_t imap_mutex;
68 static const char *mutex_hold = NULL;
71 #define __FUNCTION__ __FILE__
74 #define MUTEX_TRYLOCK_OR_RETURN() { \
75 debug_print("%s: locking mutex\n", __FUNCTION__); \
76 if (pthread_mutex_trylock(&imap_mutex) == EBUSY) { \
77 g_warning("can't lock mutex (held by %s)\n", \
78 mutex_hold ? mutex_hold:"(nil)"); \
81 mutex_hold = __FUNCTION__; \
84 #define MUTEX_TRYLOCK_OR_RETURN_VAL(retval) { \
85 debug_print("%s: locking mutex\n", __FUNCTION__); \
86 if (pthread_mutex_trylock(&imap_mutex) == EBUSY) { \
87 g_warning("can't lock mutex (held by %s)\n", \
88 mutex_hold ? mutex_hold:"(nil)"); \
91 mutex_hold = __FUNCTION__; \
94 #define MUTEX_UNLOCK() { \
95 debug_print("%s: unlocking mutex\n", __FUNCTION__);\
96 pthread_mutex_unlock(&imap_mutex); \
101 #define MUTEX_TRYLOCK_OR_RETURN() do {} while(0)
102 #define MUTEX_TRYLOCK_OR_RETURN_VAL(retval) do {} while(0)
103 #define MUTEX_UNLOCK() do {} while(0)
106 typedef struct _IMAPFolder IMAPFolder;
107 typedef struct _IMAPSession IMAPSession;
108 typedef struct _IMAPNameSpace IMAPNameSpace;
109 typedef struct _IMAPFolderItem IMAPFolderItem;
111 #include "prefs_account.h"
113 #define IMAP_FOLDER(obj) ((IMAPFolder *)obj)
114 #define IMAP_FOLDER_ITEM(obj) ((IMAPFolderItem *)obj)
115 #define IMAP_SESSION(obj) ((IMAPSession *)obj)
119 RemoteFolder rfolder;
121 /* list of IMAPNameSpace */
131 gboolean authenticated;
140 gboolean folder_content_changed;
144 struct _IMAPNameSpace
150 #define IMAP_SUCCESS 0
151 #define IMAP_SOCKET 2
152 #define IMAP_AUTHFAIL 3
153 #define IMAP_PROTOCOL 4
154 #define IMAP_SYNTAX 5
158 #define IMAPBUFSIZE 8192
162 IMAP_FLAG_SEEN = 1 << 0,
163 IMAP_FLAG_ANSWERED = 1 << 1,
164 IMAP_FLAG_FLAGGED = 1 << 2,
165 IMAP_FLAG_DELETED = 1 << 3,
166 IMAP_FLAG_DRAFT = 1 << 4
169 #define IMAP_IS_SEEN(flags) ((flags & IMAP_FLAG_SEEN) != 0)
170 #define IMAP_IS_ANSWERED(flags) ((flags & IMAP_FLAG_ANSWERED) != 0)
171 #define IMAP_IS_FLAGGED(flags) ((flags & IMAP_FLAG_FLAGGED) != 0)
172 #define IMAP_IS_DELETED(flags) ((flags & IMAP_FLAG_DELETED) != 0)
173 #define IMAP_IS_DRAFT(flags) ((flags & IMAP_FLAG_DRAFT) != 0)
176 #define IMAP4_PORT 143
178 #define IMAPS_PORT 993
181 #define IMAP_CMD_LIMIT 1000
183 #define QUOTE_IF_REQUIRED(out, str) \
185 if (*str != '"' && strpbrk(str, " \t(){}[]%*\\") != NULL) { \
189 len = strlen(str) + 3; \
190 Xalloca(__tmp, len, return IMAP_ERROR); \
191 g_snprintf(__tmp, len, "\"%s\"", str); \
194 Xstrdup_a(out, str, return IMAP_ERROR); \
198 typedef gchar * IMAPSet;
200 struct _IMAPFolderItem
210 static void imap_folder_init (Folder *folder,
214 static Folder *imap_folder_new (const gchar *name,
216 static void imap_folder_destroy (Folder *folder);
218 static IMAPSession *imap_session_new (const PrefsAccount *account);
219 static void imap_session_authenticate(IMAPSession *session,
220 const PrefsAccount *account);
221 static void imap_session_destroy (Session *session);
223 static gchar *imap_fetch_msg (Folder *folder,
226 static gchar *imap_fetch_msg_full (Folder *folder,
231 static gint imap_add_msg (Folder *folder,
235 static gint imap_add_msgs (Folder *folder,
238 GRelation *relation);
240 static gint imap_copy_msg (Folder *folder,
243 static gint imap_copy_msgs (Folder *folder,
245 MsgInfoList *msglist,
246 GRelation *relation);
248 static gint imap_remove_msg (Folder *folder,
251 static gint imap_remove_msgs (Folder *folder,
253 MsgInfoList *msglist,
254 GRelation *relation);
255 static gint imap_remove_all_msg (Folder *folder,
258 static gboolean imap_is_msg_changed (Folder *folder,
262 static gint imap_close (Folder *folder,
265 static gint imap_scan_tree (Folder *folder);
267 static gint imap_create_tree (Folder *folder);
269 static FolderItem *imap_create_folder (Folder *folder,
272 static gint imap_rename_folder (Folder *folder,
275 static gint imap_remove_folder (Folder *folder,
278 static FolderItem *imap_folder_item_new (Folder *folder);
279 static void imap_folder_item_destroy (Folder *folder,
282 static IMAPSession *imap_session_get (Folder *folder);
284 static gint imap_greeting (IMAPSession *session);
285 static gint imap_auth (IMAPSession *session,
290 static gint imap_scan_tree_recursive (IMAPSession *session,
292 static GSList *imap_parse_list (IMAPFolder *folder,
293 IMAPSession *session,
294 const gchar *real_path,
297 static void imap_create_missing_folders (Folder *folder);
298 static FolderItem *imap_create_special_folder
300 SpecialFolderItemType stype,
303 static gint imap_do_copy_msgs (Folder *folder,
305 MsgInfoList *msglist,
306 GRelation *relation);
308 static void imap_delete_all_cached_messages (FolderItem *item);
309 static void imap_set_batch (Folder *folder,
313 static SockInfo *imap_open (const gchar *server,
317 static SockInfo *imap_open (const gchar *server,
322 static SockInfo *imap_open_tunnel(const gchar *server,
323 const gchar *tunnelcmd,
326 static SockInfo *imap_open_tunnel(const gchar *server,
327 const gchar *tunnelcmd);
331 static SockInfo *imap_init_sock(SockInfo *sock, SSLType ssl_type);
333 static SockInfo *imap_init_sock(SockInfo *sock);
336 static gchar *imap_get_flag_str (IMAPFlags flags);
337 static gint imap_set_message_flags (IMAPSession *session,
338 MsgNumberList *numlist,
341 static gint imap_select (IMAPSession *session,
347 guint32 *uid_validity,
349 static gint imap_status (IMAPSession *session,
355 guint32 *uid_validity,
359 static void imap_parse_namespace (IMAPSession *session,
361 static void imap_get_namespace_by_list (IMAPSession *session,
363 static IMAPNameSpace *imap_find_namespace (IMAPFolder *folder,
365 static gchar imap_get_path_separator (IMAPFolder *folder,
367 static gchar *imap_get_real_path (IMAPFolder *folder,
370 static gchar *imap_parse_atom (SockInfo *sock,
375 static MsgFlags imap_parse_flags (const gchar *flag_str);
376 static MsgInfo *imap_parse_envelope (SockInfo *sock,
380 static gboolean imap_has_capability (IMAPSession *session,
382 static void imap_free_capabilities (IMAPSession *session);
384 /* low-level IMAP4rev1 commands */
385 static gint imap_cmd_authenticate
386 (IMAPSession *session,
390 static gint imap_cmd_login (IMAPSession *session,
393 static gint imap_cmd_logout (IMAPSession *session);
394 static gint imap_cmd_noop (IMAPSession *session);
396 static gint imap_cmd_starttls (IMAPSession *session);
398 static gint imap_cmd_namespace (IMAPSession *session,
400 static gint imap_cmd_list (IMAPSession *session,
402 const gchar *mailbox,
404 static gint imap_cmd_do_select (IMAPSession *session,
410 guint32 *uid_validity,
412 static gint imap_cmd_select (IMAPSession *session,
417 guint32 *uid_validity,
419 static gint imap_cmd_examine (IMAPSession *session,
424 guint32 *uid_validity,
426 static gint imap_cmd_create (IMAPSession *sock,
427 const gchar *folder);
428 static gint imap_cmd_rename (IMAPSession *sock,
429 const gchar *oldfolder,
430 const gchar *newfolder);
431 static gint imap_cmd_delete (IMAPSession *session,
432 const gchar *folder);
433 static gint imap_cmd_envelope (IMAPSession *session,
435 static gint imap_cmd_fetch (IMAPSession *sock,
437 const gchar *filename,
440 static gint imap_cmd_append (IMAPSession *session,
441 const gchar *destfolder,
445 static gint imap_cmd_copy (IMAPSession *session,
446 const gchar *seq_set,
447 const gchar *destfolder,
448 GRelation *uid_mapping);
449 static gint imap_cmd_store (IMAPSession *session,
452 static gint imap_cmd_expunge (IMAPSession *session,
454 static gint imap_cmd_close (IMAPSession *session);
456 static gint imap_cmd_ok (IMAPSession *session,
458 static gint imap_cmd_ok_block (IMAPSession *session,
460 static gint imap_cmd_ok_with_block
461 (IMAPSession *session,
464 static void imap_gen_send (IMAPSession *session,
465 const gchar *format, ...);
466 static gint imap_gen_recv (IMAPSession *session,
468 static gint imap_gen_recv_block (IMAPSession *session,
470 static gint imap_gen_recv_with_block
471 (IMAPSession *session,
475 /* misc utility functions */
476 static gchar *strchr_cpy (const gchar *src,
480 static gchar *get_quoted (const gchar *src,
484 static gchar *search_array_contain_str (GPtrArray *array,
486 static gchar *search_array_str (GPtrArray *array,
488 static void imap_path_separator_subst (gchar *str,
491 static gchar *imap_utf8_to_modified_utf7 (const gchar *from);
492 static gchar *imap_modified_utf7_to_utf8 (const gchar *mutf7_str);
494 static GSList *imap_get_seq_set_from_numlist (MsgNumberList *msglist);
495 static GSList *imap_get_seq_set_from_msglist (MsgInfoList *msglist);
496 static void imap_seq_set_free (GSList *seq_list);
498 static gboolean imap_rename_folder_func (GNode *node,
500 static gint imap_get_num_list (Folder *folder,
503 gboolean *old_uids_valid);
504 static GSList *imap_get_msginfos (Folder *folder,
506 GSList *msgnum_list);
507 static MsgInfo *imap_get_msginfo (Folder *folder,
510 static gboolean imap_scan_required (Folder *folder,
512 static void imap_change_flags (Folder *folder,
515 MsgPermFlags newflags);
516 static gint imap_get_flags (Folder *folder,
518 MsgInfoList *msglist,
519 GRelation *msgflags);
520 static gchar *imap_folder_get_path (Folder *folder);
521 static gchar *imap_item_get_path (Folder *folder,
523 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item);
524 static GHashTable *flags_set_table = NULL;
525 static GHashTable *flags_unset_table = NULL;
526 typedef struct _hashtable_data {
527 IMAPSession *session;
531 static FolderClass imap_class;
533 typedef struct _thread_data {
544 void *imap_getline_thread(void *data)
546 thread_data *td = (thread_data *)data;
549 line = sock_getline(td->sock);
557 /* imap_getline just wraps sock_getline inside a thread,
558 * performing gtk updates so that the interface isn't frozen.
560 static gchar *imap_getline(SockInfo *sock)
562 #if (defined USE_PTHREAD && defined __GLIBC__ && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 3)))
563 thread_data *td = g_new0(thread_data, 1);
568 if (pthread_create(&pt, PTHREAD_CREATE_JOINABLE,
569 imap_getline_thread, td) != 0) {
571 return sock_getline(sock);
574 debug_print("+++waiting for imap_getline_thread...\n");
576 /* don't let the interface freeze while waiting */
579 debug_print("---imap_getline_thread done\n");
581 /* get the thread's return value and clean its resources */
582 pthread_join(pt, (void *)&line);
587 return sock_getline(sock);
591 FolderClass *imap_get_class(void)
593 if (imap_class.idstr == NULL) {
594 imap_class.type = F_IMAP;
595 imap_class.idstr = "imap";
596 imap_class.uistr = "IMAP4";
598 /* Folder functions */
599 imap_class.new_folder = imap_folder_new;
600 imap_class.destroy_folder = imap_folder_destroy;
601 imap_class.scan_tree = imap_scan_tree;
602 imap_class.create_tree = imap_create_tree;
604 /* FolderItem functions */
605 imap_class.item_new = imap_folder_item_new;
606 imap_class.item_destroy = imap_folder_item_destroy;
607 imap_class.item_get_path = imap_item_get_path;
608 imap_class.create_folder = imap_create_folder;
609 imap_class.rename_folder = imap_rename_folder;
610 imap_class.remove_folder = imap_remove_folder;
611 imap_class.close = imap_close;
612 imap_class.get_num_list = imap_get_num_list;
613 imap_class.scan_required = imap_scan_required;
615 /* Message functions */
616 imap_class.get_msginfo = imap_get_msginfo;
617 imap_class.get_msginfos = imap_get_msginfos;
618 imap_class.fetch_msg = imap_fetch_msg;
619 imap_class.fetch_msg_full = imap_fetch_msg_full;
620 imap_class.add_msg = imap_add_msg;
621 imap_class.add_msgs = imap_add_msgs;
622 imap_class.copy_msg = imap_copy_msg;
623 imap_class.copy_msgs = imap_copy_msgs;
624 imap_class.remove_msg = imap_remove_msg;
625 imap_class.remove_msgs = imap_remove_msgs;
626 imap_class.remove_all_msg = imap_remove_all_msg;
627 imap_class.is_msg_changed = imap_is_msg_changed;
628 imap_class.change_flags = imap_change_flags;
629 imap_class.get_flags = imap_get_flags;
630 imap_class.set_batch = imap_set_batch;
632 pthread_mutex_init(&imap_mutex, NULL);
639 static gchar *get_seq_set_from_seq_list(GSList *seq_list)
645 for (cur = seq_list; cur != NULL; cur = cur->next) {
646 tmp = val?g_strdup(val):NULL;
648 val = g_strconcat(tmp?tmp:"", tmp?",":"",(gchar *)cur->data,
656 static Folder *imap_folder_new(const gchar *name, const gchar *path)
660 folder = (Folder *)g_new0(IMAPFolder, 1);
661 folder->klass = &imap_class;
662 imap_folder_init(folder, name, path);
667 static void imap_folder_destroy(Folder *folder)
671 dir = imap_folder_get_path(folder);
672 if (is_dir_exist(dir))
673 remove_dir_recursive(dir);
676 folder_remote_folder_destroy(REMOTE_FOLDER(folder));
679 static void imap_folder_init(Folder *folder, const gchar *name,
682 folder_remote_folder_init((Folder *)folder, name, path);
685 static FolderItem *imap_folder_item_new(Folder *folder)
687 IMAPFolderItem *item;
689 item = g_new0(IMAPFolderItem, 1);
692 item->uid_list = NULL;
694 return (FolderItem *)item;
697 static void imap_folder_item_destroy(Folder *folder, FolderItem *_item)
699 IMAPFolderItem *item = (IMAPFolderItem *)_item;
701 g_return_if_fail(item != NULL);
702 g_slist_free(item->uid_list);
707 static gboolean imap_reset_uid_lists_func(GNode *node, gpointer data)
709 IMAPFolderItem *item = (IMAPFolderItem *)node->data;
713 g_slist_free(item->uid_list);
714 item->uid_list = NULL;
719 static void imap_reset_uid_lists(Folder *folder)
721 if(folder->node == NULL)
724 /* Destroy all uid lists and rest last uid */
725 g_node_traverse(folder->node, G_IN_ORDER, G_TRAVERSE_ALL, -1, imap_reset_uid_lists_func, NULL);
728 /* Send CAPABILITY, and examine the server's response to see whether this
729 * connection is pre-authenticated or not and build a list of CAPABILITIES. */
730 static gint imap_greeting(IMAPSession *session)
735 imap_gen_send(session, "CAPABILITY");
737 argbuf = g_ptr_array_new();
739 if (imap_cmd_ok(session, argbuf) != IMAP_SUCCESS ||
740 ((capstr = search_array_str(argbuf, "CAPABILITY ")) == NULL)) {
741 ptr_array_free_strings(argbuf);
742 g_ptr_array_free(argbuf, TRUE);
746 session->authenticated = search_array_str(argbuf, "PREAUTH") != NULL;
748 capstr += strlen("CAPABILITY ");
750 IMAP_SESSION(session)->capability = g_strsplit(capstr, " ", 0);
752 ptr_array_free_strings(argbuf);
753 g_ptr_array_free(argbuf, TRUE);
755 if (imap_has_capability(session, "UIDPLUS"))
756 session->uidplus = TRUE;
761 static gint imap_auth(IMAPSession *session, const gchar *user, const gchar *pass,
766 if (type == 0 || type == IMAP_AUTH_LOGIN)
767 ok = imap_cmd_login(session, user, pass);
769 ok = imap_cmd_authenticate(session, user, pass, type);
771 if (ok == IMAP_SUCCESS)
772 session->authenticated = TRUE;
777 static IMAPSession *imap_session_get(Folder *folder)
779 RemoteFolder *rfolder = REMOTE_FOLDER(folder);
780 IMAPSession *session = NULL;
782 g_return_val_if_fail(folder != NULL, NULL);
783 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, NULL);
784 g_return_val_if_fail(folder->account != NULL, NULL);
786 if (prefs_common.work_offline && !imap_gtk_should_override()) {
790 /* Make sure we have a session */
791 if (rfolder->session != NULL) {
792 session = IMAP_SESSION(rfolder->session);
794 imap_reset_uid_lists(folder);
795 session = imap_session_new(folder->account);
800 if (SESSION(session)->sock->state == CONN_DISCONNECTED) {
801 debug_print("IMAP server disconnected\n");
802 session_destroy(SESSION(session));
803 imap_reset_uid_lists(folder);
804 session = imap_session_new(folder->account);
807 /* Make sure session is authenticated */
808 if (!IMAP_SESSION(session)->authenticated)
809 imap_session_authenticate(IMAP_SESSION(session), folder->account);
810 if (!IMAP_SESSION(session)->authenticated) {
811 session_destroy(SESSION(session));
812 rfolder->session = NULL;
816 /* Make sure we have parsed the IMAP namespace */
817 imap_parse_namespace(IMAP_SESSION(session),
818 IMAP_FOLDER(folder));
820 /* I think the point of this code is to avoid sending a
821 * keepalive if we've used the session recently and therefore
822 * think it's still alive. Unfortunately, most of the code
823 * does not yet check for errors on the socket, and so if the
824 * connection drops we don't notice until the timeout expires.
825 * A better solution than sending a NOOP every time would be
826 * for every command to be prepared to retry until it is
827 * successfully sent. -- mbp */
828 if (time(NULL) - SESSION(session)->last_access_time > SESSION_TIMEOUT_INTERVAL) {
829 /* verify that the session is still alive */
830 if (imap_cmd_noop(session) != IMAP_SUCCESS) {
831 /* Check if this is the first try to establish a
832 connection, if yes we don't try to reconnect */
833 if (rfolder->session == NULL) {
834 log_warning(_("Connecting to %s failed"),
835 folder->account->recv_server);
836 session_destroy(SESSION(session));
839 log_warning(_("IMAP4 connection to %s has been"
840 " disconnected. Reconnecting...\n"),
841 folder->account->recv_server);
842 statusbar_print_all(_("IMAP4 connection to %s has been"
843 " disconnected. Reconnecting...\n"),
844 folder->account->recv_server);
845 session_destroy(SESSION(session));
846 /* Clear folders session to make imap_session_get create
847 a new session, because of rfolder->session == NULL
848 it will not try to reconnect again and so avoid an
850 rfolder->session = NULL;
851 session = imap_session_get(folder);
857 rfolder->session = SESSION(session);
859 return IMAP_SESSION(session);
862 static IMAPSession *imap_session_new(const PrefsAccount *account)
864 IMAPSession *session;
869 /* FIXME: IMAP over SSL only... */
872 port = account->set_imapport ? account->imapport
873 : account->ssl_imap == SSL_TUNNEL ? IMAPS_PORT : IMAP4_PORT;
874 ssl_type = account->ssl_imap;
876 port = account->set_imapport ? account->imapport
880 if (account->set_tunnelcmd) {
881 log_message(_("creating tunneled IMAP4 connection\n"));
883 if ((imap_sock = imap_open_tunnel(account->recv_server,
887 if ((imap_sock = imap_open_tunnel(account->recv_server,
888 account->tunnelcmd)) == NULL)
892 g_return_val_if_fail(account->recv_server != NULL, NULL);
894 log_message(_("creating IMAP4 connection to %s:%d ...\n"),
895 account->recv_server, port);
898 if ((imap_sock = imap_open(account->recv_server, port,
901 if ((imap_sock = imap_open(account->recv_server, port)) == NULL)
906 session = g_new0(IMAPSession, 1);
907 session_init(SESSION(session));
908 SESSION(session)->type = SESSION_IMAP;
909 SESSION(session)->server = g_strdup(account->recv_server);
910 SESSION(session)->sock = imap_sock;
912 SESSION(session)->destroy = imap_session_destroy;
914 session->capability = NULL;
916 session->authenticated = FALSE;
917 session->mbox = NULL;
918 session->cmd_count = 0;
920 /* Only need to log in if the connection was not PREAUTH */
921 if (imap_greeting(session) != IMAP_SUCCESS) {
922 session_destroy(SESSION(session));
927 if (account->ssl_imap == SSL_STARTTLS &&
928 imap_has_capability(session, "STARTTLS")) {
931 ok = imap_cmd_starttls(session);
932 if (ok != IMAP_SUCCESS) {
933 log_warning(_("Can't start TLS session.\n"));
934 session_destroy(SESSION(session));
937 if (!ssl_init_socket_with_method(SESSION(session)->sock,
939 session_destroy(SESSION(session));
943 imap_free_capabilities(session);
944 session->authenticated = FALSE;
945 session->uidplus = FALSE;
946 session->cmd_count = 1;
948 if (imap_greeting(session) != IMAP_SUCCESS) {
949 session_destroy(SESSION(session));
954 log_message("IMAP connection is %s-authenticated\n",
955 (session->authenticated) ? "pre" : "un");
960 static void imap_session_authenticate(IMAPSession *session,
961 const PrefsAccount *account)
965 g_return_if_fail(account->userid != NULL);
967 pass = account->passwd;
970 tmp_pass = input_dialog_query_password(account->recv_server, account->userid);
973 Xstrdup_a(pass, tmp_pass, {g_free(tmp_pass); return;});
976 statusbar_print_all(_("Connecting to IMAP4 server %s...\n"),
977 account->recv_server);
978 if (imap_auth(session, account->userid, pass, account->imap_auth_type) != IMAP_SUCCESS) {
979 imap_cmd_logout(session);
985 session->authenticated = TRUE;
988 static void imap_session_destroy(Session *session)
990 imap_free_capabilities(IMAP_SESSION(session));
991 g_free(IMAP_SESSION(session)->mbox);
992 sock_close(session->sock);
993 session->sock = NULL;
996 static gchar *imap_fetch_msg(Folder *folder, FolderItem *item, gint uid)
998 return imap_fetch_msg_full(folder, item, uid, TRUE, TRUE);
1001 static guint get_size_with_lfs(MsgInfo *info)
1010 fp = procmsg_open_message(info);
1014 while (fgets(buf, sizeof (buf), fp) != NULL) {
1016 if (!strstr(buf, "\r") && strstr(buf, "\n"))
1024 static gchar *imap_fetch_msg_full(Folder *folder, FolderItem *item, gint uid,
1025 gboolean headers, gboolean body)
1027 gchar *path, *filename;
1028 IMAPSession *session;
1031 g_return_val_if_fail(folder != NULL, NULL);
1032 g_return_val_if_fail(item != NULL, NULL);
1037 path = folder_item_get_path(item);
1038 if (!is_dir_exist(path))
1039 make_dir_hier(path);
1040 filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(uid), NULL);
1043 if (is_file_exist(filename)) {
1044 /* see whether the local file represents the whole message
1045 * or not. As the IMAP server reports size with \r chars,
1046 * we have to update the local file (UNIX \n only) size */
1047 MsgInfo *msginfo = imap_parse_msg(filename, item);
1048 MsgInfo *cached = msgcache_get_msg(item->cache,uid);
1049 guint have_size = get_size_with_lfs(msginfo);
1050 debug_print("message %d has been already %scached (%d/%d).\n", uid,
1051 have_size == cached->size ? "fully ":"",
1052 have_size, cached? (int)cached->size : -1);
1054 if (cached && (cached->size == have_size || !body)) {
1055 procmsg_msginfo_free(cached);
1056 procmsg_msginfo_free(msginfo);
1059 procmsg_msginfo_free(cached);
1060 procmsg_msginfo_free(msginfo);
1064 session = imap_session_get(folder);
1070 debug_print("IMAP fetching messages\n");
1071 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
1072 NULL, NULL, NULL, NULL, FALSE);
1073 if (ok != IMAP_SUCCESS) {
1074 g_warning("can't select mailbox %s\n", item->path);
1079 debug_print("getting message %d...\n", uid);
1080 ok = imap_cmd_fetch(session, (guint32)uid, filename, headers, body);
1082 if (ok != IMAP_SUCCESS) {
1083 g_warning("can't fetch message %d\n", uid);
1091 static gint imap_add_msg(Folder *folder, FolderItem *dest,
1092 const gchar *file, MsgFlags *flags)
1096 MsgFileInfo fileinfo;
1098 g_return_val_if_fail(file != NULL, -1);
1100 fileinfo.msginfo = NULL;
1101 fileinfo.file = (gchar *)file;
1102 fileinfo.flags = flags;
1103 file_list.data = &fileinfo;
1104 file_list.next = NULL;
1106 ret = imap_add_msgs(folder, dest, &file_list, NULL);
1110 static gint imap_add_msgs(Folder *folder, FolderItem *dest, GSList *file_list,
1111 GRelation *relation)
1114 IMAPSession *session;
1115 guint32 last_uid = 0;
1117 MsgFileInfo *fileinfo;
1121 g_return_val_if_fail(folder != NULL, -1);
1122 g_return_val_if_fail(dest != NULL, -1);
1123 g_return_val_if_fail(file_list != NULL, -1);
1125 MUTEX_TRYLOCK_OR_RETURN_VAL(-1);
1127 session = imap_session_get(folder);
1132 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
1134 for (cur = file_list; cur != NULL; cur = cur->next) {
1135 IMAPFlags iflags = 0;
1136 guint32 new_uid = 0;
1138 fileinfo = (MsgFileInfo *)cur->data;
1140 if (fileinfo->flags) {
1141 if (MSG_IS_MARKED(*fileinfo->flags))
1142 iflags |= IMAP_FLAG_FLAGGED;
1143 if (MSG_IS_REPLIED(*fileinfo->flags))
1144 iflags |= IMAP_FLAG_ANSWERED;
1145 if (!MSG_IS_UNREAD(*fileinfo->flags))
1146 iflags |= IMAP_FLAG_SEEN;
1149 if (dest->stype == F_OUTBOX ||
1150 dest->stype == F_QUEUE ||
1151 dest->stype == F_DRAFT ||
1152 dest->stype == F_TRASH)
1153 iflags |= IMAP_FLAG_SEEN;
1155 ok = imap_cmd_append(session, destdir, fileinfo->file, iflags,
1158 if (ok != IMAP_SUCCESS) {
1159 g_warning("can't append message %s\n", fileinfo->file);
1165 if (relation != NULL)
1166 g_relation_insert(relation, fileinfo->msginfo != NULL ?
1167 (gpointer) fileinfo->msginfo : (gpointer) fileinfo,
1168 GINT_TO_POINTER(dest->last_num + 1));
1169 if (last_uid < new_uid)
1179 static gint imap_do_copy_msgs(Folder *folder, FolderItem *dest,
1180 MsgInfoList *msglist, GRelation *relation)
1184 GSList *seq_list, *cur;
1186 IMAPSession *session;
1187 gint ok = IMAP_SUCCESS;
1188 GRelation *uid_mapping;
1191 g_return_val_if_fail(folder != NULL, -1);
1192 g_return_val_if_fail(dest != NULL, -1);
1193 g_return_val_if_fail(msglist != NULL, -1);
1195 MUTEX_TRYLOCK_OR_RETURN_VAL(-1);
1197 session = imap_session_get(folder);
1203 msginfo = (MsgInfo *)msglist->data;
1205 src = msginfo->folder;
1207 g_warning("the src folder is identical to the dest.\n");
1212 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
1213 NULL, NULL, NULL, NULL, FALSE);
1214 if (ok != IMAP_SUCCESS) {
1219 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
1220 seq_list = imap_get_seq_set_from_msglist(msglist);
1221 uid_mapping = g_relation_new(2);
1222 g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
1224 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
1225 gchar *seq_set = (gchar *)cur->data;
1227 debug_print("Copying message %s%c[%s] to %s ...\n",
1228 src->path, G_DIR_SEPARATOR,
1231 ok = imap_cmd_copy(session, seq_set, destdir, uid_mapping);
1232 if (ok != IMAP_SUCCESS) {
1233 g_relation_destroy(uid_mapping);
1234 imap_seq_set_free(seq_list);
1239 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
1240 MsgInfo *msginfo = (MsgInfo *)cur->data;
1243 tuples = g_relation_select(uid_mapping,
1244 GINT_TO_POINTER(msginfo->msgnum),
1246 if (tuples->len > 0) {
1247 gint num = GPOINTER_TO_INT(g_tuples_index(tuples, 0, 1));
1248 g_relation_insert(relation, msginfo,
1249 GPOINTER_TO_INT(num));
1253 g_relation_insert(relation, msginfo,
1254 GPOINTER_TO_INT(0));
1255 g_tuples_destroy(tuples);
1258 g_relation_destroy(uid_mapping);
1259 imap_seq_set_free(seq_list);
1263 IMAP_FOLDER_ITEM(dest)->lastuid = 0;
1264 IMAP_FOLDER_ITEM(dest)->uid_next = 0;
1265 g_slist_free(IMAP_FOLDER_ITEM(dest)->uid_list);
1266 IMAP_FOLDER_ITEM(dest)->uid_list = NULL;
1270 if (ok == IMAP_SUCCESS)
1276 static gint imap_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1280 g_return_val_if_fail(msginfo != NULL, -1);
1282 msglist.data = msginfo;
1283 msglist.next = NULL;
1285 return imap_copy_msgs(folder, dest, &msglist, NULL);
1288 static gint imap_copy_msgs(Folder *folder, FolderItem *dest,
1289 MsgInfoList *msglist, GRelation *relation)
1295 g_return_val_if_fail(folder != NULL, -1);
1296 g_return_val_if_fail(dest != NULL, -1);
1297 g_return_val_if_fail(msglist != NULL, -1);
1299 msginfo = (MsgInfo *)msglist->data;
1300 g_return_val_if_fail(msginfo->folder != NULL, -1);
1302 if (folder == msginfo->folder->folder) {
1303 ret = imap_do_copy_msgs(folder, dest, msglist, relation);
1307 file_list = procmsg_get_message_file_list(msglist);
1308 g_return_val_if_fail(file_list != NULL, -1);
1310 ret = imap_add_msgs(folder, dest, file_list, relation);
1312 procmsg_message_file_list_free(file_list);
1318 static gint imap_do_remove_msgs(Folder *folder, FolderItem *dest,
1319 MsgInfoList *msglist, GRelation *relation)
1322 GSList *seq_list = NULL, *cur;
1324 IMAPSession *session;
1325 gint ok = IMAP_SUCCESS;
1326 GRelation *uid_mapping;
1328 g_return_val_if_fail(folder != NULL, -1);
1329 g_return_val_if_fail(dest != NULL, -1);
1330 g_return_val_if_fail(msglist != NULL, -1);
1332 MUTEX_TRYLOCK_OR_RETURN_VAL(-1);
1334 session = imap_session_get(folder);
1339 msginfo = (MsgInfo *)msglist->data;
1341 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
1342 NULL, NULL, NULL, NULL, FALSE);
1343 if (ok != IMAP_SUCCESS) {
1348 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
1349 for (cur = msglist; cur; cur = cur->next) {
1350 msginfo = (MsgInfo *)cur->data;
1351 seq_list = g_slist_append(seq_list, GINT_TO_POINTER(msginfo->msgnum));
1354 uid_mapping = g_relation_new(2);
1355 g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
1357 ok = imap_set_message_flags
1358 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
1359 seq_list, IMAP_FLAG_DELETED, TRUE);
1360 if (ok != IMAP_SUCCESS) {
1361 log_warning(_("can't set deleted flags\n"));
1365 ok = imap_cmd_expunge(session, NULL);
1366 if (ok != IMAP_SUCCESS) {
1367 log_warning(_("can't expunge\n"));
1372 g_relation_destroy(uid_mapping);
1373 g_slist_free(seq_list);
1378 if (ok == IMAP_SUCCESS)
1384 static gint imap_remove_msgs(Folder *folder, FolderItem *dest,
1385 MsgInfoList *msglist, GRelation *relation)
1389 g_return_val_if_fail(folder != NULL, -1);
1390 g_return_val_if_fail(dest != NULL, -1);
1391 if (msglist == NULL)
1394 msginfo = (MsgInfo *)msglist->data;
1395 g_return_val_if_fail(msginfo->folder != NULL, -1);
1397 return imap_do_remove_msgs(folder, dest, msglist, relation);
1400 static gint imap_remove_all_msg(Folder *folder, FolderItem *item)
1402 GSList *list = folder_item_get_msg_list(item);
1403 gint res = imap_remove_msgs(folder, item, list, NULL);
1408 static gboolean imap_is_msg_changed(Folder *folder, FolderItem *item,
1411 /* TODO: properly implement this method */
1415 static gint imap_close(Folder *folder, FolderItem *item)
1418 IMAPSession *session;
1420 g_return_val_if_fail(folder != NULL, -1);
1421 g_return_val_if_fail(item != NULL, -1);
1422 g_return_val_if_fail(item->path != NULL, -1);
1424 session = imap_session_get(folder);
1425 if (!session) return -1;
1427 if (session->mbox) {
1428 if (strcmp2(session->mbox, item->path) != 0) return -1;
1430 ok = imap_cmd_close(session);
1431 if (ok != IMAP_SUCCESS)
1432 log_warning(_("can't close folder\n"));
1434 g_free(session->mbox);
1436 session->mbox = NULL;
1444 static gint imap_scan_tree(Folder *folder)
1446 FolderItem *item = NULL;
1447 IMAPSession *session;
1448 gchar *root_folder = NULL;
1450 g_return_val_if_fail(folder != NULL, -1);
1451 g_return_val_if_fail(folder->account != NULL, -1);
1453 MUTEX_TRYLOCK_OR_RETURN_VAL(-1);
1455 session = imap_session_get(folder);
1457 if (!folder->node) {
1458 folder_tree_destroy(folder);
1459 item = folder_item_new(folder, folder->name, NULL);
1460 item->folder = folder;
1461 folder->node = item->node = g_node_new(item);
1467 if (folder->account->imap_dir && *folder->account->imap_dir) {
1472 Xstrdup_a(root_folder, folder->account->imap_dir, {MUTEX_UNLOCK();return -1;});
1473 extract_quote(root_folder, '"');
1474 subst_char(root_folder,
1475 imap_get_path_separator(IMAP_FOLDER(folder),
1478 strtailchomp(root_folder, '/');
1479 real_path = imap_get_real_path
1480 (IMAP_FOLDER(folder), root_folder);
1481 debug_print("IMAP root directory: %s\n", real_path);
1483 /* check if root directory exist */
1484 argbuf = g_ptr_array_new();
1485 ok = imap_cmd_list(session, NULL, real_path, argbuf);
1486 if (ok != IMAP_SUCCESS ||
1487 search_array_str(argbuf, "LIST ") == NULL) {
1488 log_warning(_("root folder %s does not exist\n"), real_path);
1489 g_ptr_array_free(argbuf, TRUE);
1492 if (!folder->node) {
1493 item = folder_item_new(folder, folder->name, NULL);
1494 item->folder = folder;
1495 folder->node = item->node = g_node_new(item);
1500 g_ptr_array_free(argbuf, TRUE);
1505 item = FOLDER_ITEM(folder->node->data);
1506 if (!item || ((item->path || root_folder) &&
1507 strcmp2(item->path, root_folder) != 0)) {
1508 folder_tree_destroy(folder);
1509 item = folder_item_new(folder, folder->name, root_folder);
1510 item->folder = folder;
1511 folder->node = item->node = g_node_new(item);
1514 imap_scan_tree_recursive(session, FOLDER_ITEM(folder->node->data));
1515 imap_create_missing_folders(folder);
1521 static gint imap_scan_tree_recursive(IMAPSession *session, FolderItem *item)
1524 IMAPFolder *imapfolder;
1525 FolderItem *new_item;
1526 GSList *item_list, *cur;
1529 gchar *wildcard_path;
1533 g_return_val_if_fail(item != NULL, -1);
1534 g_return_val_if_fail(item->folder != NULL, -1);
1535 g_return_val_if_fail(item->no_sub == FALSE, -1);
1537 folder = item->folder;
1538 imapfolder = IMAP_FOLDER(folder);
1540 separator = imap_get_path_separator(imapfolder, item->path);
1542 if (folder->ui_func)
1543 folder->ui_func(folder, item, folder->ui_func_data);
1546 wildcard[0] = separator;
1549 real_path = imap_get_real_path(imapfolder, item->path);
1553 real_path = g_strdup("");
1556 Xstrcat_a(wildcard_path, real_path, wildcard,
1557 {g_free(real_path); return IMAP_ERROR;});
1558 QUOTE_IF_REQUIRED(wildcard_path, wildcard_path);
1560 imap_gen_send(session, "LIST \"\" %s",
1563 strtailchomp(real_path, separator);
1564 item_list = imap_parse_list(imapfolder, session, real_path, NULL);
1567 node = item->node->children;
1568 while (node != NULL) {
1569 FolderItem *old_item = FOLDER_ITEM(node->data);
1570 GNode *next = node->next;
1573 for (cur = item_list; cur != NULL; cur = cur->next) {
1574 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1575 if (!strcmp2(old_item->path, cur_item->path)) {
1576 new_item = cur_item;
1581 debug_print("folder '%s' not found. removing...\n",
1583 folder_item_remove(old_item);
1585 old_item->no_sub = new_item->no_sub;
1586 old_item->no_select = new_item->no_select;
1587 if (old_item->no_sub == TRUE && node->children) {
1588 debug_print("folder '%s' doesn't have "
1589 "subfolders. removing...\n",
1591 folder_item_remove_children(old_item);
1598 for (cur = item_list; cur != NULL; cur = cur->next) {
1599 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1601 for (node = item->node->children; node != NULL;
1602 node = node->next) {
1603 if (!strcmp2(FOLDER_ITEM(node->data)->path,
1605 new_item = FOLDER_ITEM(node->data);
1606 folder_item_destroy(cur_item);
1612 new_item = cur_item;
1613 debug_print("new folder '%s' found.\n", new_item->path);
1614 folder_item_append(item, new_item);
1617 if (!strcmp(new_item->path, "INBOX")) {
1618 new_item->stype = F_INBOX;
1619 folder->inbox = new_item;
1620 } else if (!folder_item_parent(item) || item->stype == F_INBOX) {
1623 base = g_path_get_basename(new_item->path);
1625 if (!folder->outbox && !g_ascii_strcasecmp(base, "Sent")) {
1626 new_item->stype = F_OUTBOX;
1627 folder->outbox = new_item;
1628 } else if (!folder->draft && !g_ascii_strcasecmp(base, "Drafts")) {
1629 new_item->stype = F_DRAFT;
1630 folder->draft = new_item;
1631 } else if (!folder->queue && !g_ascii_strcasecmp(base, "Queue")) {
1632 new_item->stype = F_QUEUE;
1633 folder->queue = new_item;
1634 } else if (!folder->trash && !g_ascii_strcasecmp(base, "Trash")) {
1635 new_item->stype = F_TRASH;
1636 folder->trash = new_item;
1641 if (new_item->no_sub == FALSE)
1642 imap_scan_tree_recursive(session, new_item);
1645 g_slist_free(item_list);
1647 return IMAP_SUCCESS;
1650 static GSList *imap_parse_list(IMAPFolder *folder, IMAPSession *session,
1651 const gchar *real_path, gchar *separator)
1653 gchar buf[IMAPBUFSIZE];
1655 gchar separator_str[16];
1658 gchar *loc_name, *loc_path;
1659 GSList *item_list = NULL;
1661 FolderItem *new_item;
1663 debug_print("getting list of %s ...\n",
1664 *real_path ? real_path : "\"\"");
1666 str = g_string_new(NULL);
1669 if (sock_gets(SESSION(session)->sock, buf, sizeof(buf)) <= 0) {
1670 log_warning(_("error occurred while getting LIST.\n"));
1674 if (buf[0] != '*' || buf[1] != ' ') {
1675 log_print("IMAP4< %s\n", buf);
1676 if (sscanf(buf, "%*d %16s", buf) < 1 ||
1677 strcmp(buf, "OK") != 0)
1678 log_warning(_("error occurred while getting LIST.\n"));
1682 debug_print("IMAP4< %s\n", buf);
1684 g_string_assign(str, buf);
1686 if (strncmp(p, "LIST ", 5) != 0) continue;
1689 if (*p != '(') continue;
1691 p = strchr_cpy(p, ')', flags, sizeof(flags));
1693 while (*p == ' ') p++;
1695 p = strchr_cpy(p, ' ', separator_str, sizeof(separator_str));
1697 extract_quote(separator_str, '"');
1698 if (!strcmp(separator_str, "NIL"))
1699 separator_str[0] = '\0';
1701 *separator = separator_str[0];
1704 while (*p == ' ') p++;
1705 if (*p == '{' || *p == '"')
1706 p = imap_parse_atom(SESSION(session)->sock, p,
1707 buf, sizeof(buf), str);
1709 strncpy2(buf, p, sizeof(buf));
1710 strtailchomp(buf, separator_str[0]);
1711 if (buf[0] == '\0') continue;
1712 if (!strcmp(buf, real_path)) continue;
1714 if (separator_str[0] != '\0')
1715 subst_char(buf, separator_str[0], '/');
1716 base = g_path_get_basename(buf);
1717 if (base[0] == '.') continue;
1719 loc_name = imap_modified_utf7_to_utf8(base);
1720 loc_path = imap_modified_utf7_to_utf8(buf);
1721 new_item = folder_item_new(FOLDER(folder), loc_name, loc_path);
1722 if (strcasestr(flags, "\\Noinferiors") != NULL)
1723 new_item->no_sub = TRUE;
1724 if (strcmp(buf, "INBOX") != 0 &&
1725 strcasestr(flags, "\\Noselect") != NULL)
1726 new_item->no_select = TRUE;
1728 item_list = g_slist_append(item_list, new_item);
1730 debug_print("folder '%s' found.\n", loc_path);
1736 g_string_free(str, TRUE);
1741 static gint imap_create_tree(Folder *folder)
1743 g_return_val_if_fail(folder != NULL, -1);
1744 g_return_val_if_fail(folder->node != NULL, -1);
1745 g_return_val_if_fail(folder->node->data != NULL, -1);
1746 g_return_val_if_fail(folder->account != NULL, -1);
1748 imap_scan_tree(folder);
1749 imap_create_missing_folders(folder);
1754 static void imap_create_missing_folders(Folder *folder)
1756 g_return_if_fail(folder != NULL);
1759 folder->inbox = imap_create_special_folder
1760 (folder, F_INBOX, "INBOX");
1762 if (!folder->outbox)
1763 folder->outbox = imap_create_special_folder
1764 (folder, F_OUTBOX, "Sent");
1766 folder->draft = imap_create_special_folder
1767 (folder, F_DRAFT, "Drafts");
1769 folder->queue = imap_create_special_folder
1770 (folder, F_QUEUE, "Queue");
1773 folder->trash = imap_create_special_folder
1774 (folder, F_TRASH, "Trash");
1777 static FolderItem *imap_create_special_folder(Folder *folder,
1778 SpecialFolderItemType stype,
1782 FolderItem *new_item;
1784 g_return_val_if_fail(folder != NULL, NULL);
1785 g_return_val_if_fail(folder->node != NULL, NULL);
1786 g_return_val_if_fail(folder->node->data != NULL, NULL);
1787 g_return_val_if_fail(folder->account != NULL, NULL);
1788 g_return_val_if_fail(name != NULL, NULL);
1790 item = FOLDER_ITEM(folder->node->data);
1791 new_item = imap_create_folder(folder, item, name);
1794 g_warning("Can't create '%s'\n", name);
1795 if (!folder->inbox) return NULL;
1797 new_item = imap_create_folder(folder, folder->inbox, name);
1799 g_warning("Can't create '%s' under INBOX\n", name);
1801 new_item->stype = stype;
1803 new_item->stype = stype;
1808 static gchar *imap_folder_get_path(Folder *folder)
1812 g_return_val_if_fail(folder != NULL, NULL);
1813 g_return_val_if_fail(folder->account != NULL, NULL);
1815 folder_path = g_strconcat(get_imap_cache_dir(),
1817 folder->account->recv_server,
1819 folder->account->userid,
1825 static gchar *imap_item_get_path(Folder *folder, FolderItem *item)
1827 gchar *folder_path, *path;
1829 g_return_val_if_fail(folder != NULL, NULL);
1830 g_return_val_if_fail(item != NULL, NULL);
1831 folder_path = imap_folder_get_path(folder);
1833 g_return_val_if_fail(folder_path != NULL, NULL);
1834 if (folder_path[0] == G_DIR_SEPARATOR) {
1836 path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
1839 path = g_strdup(folder_path);
1842 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1843 folder_path, G_DIR_SEPARATOR_S,
1846 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1849 g_free(folder_path);
1854 static FolderItem *imap_create_folder(Folder *folder, FolderItem *parent,
1857 gchar *dirpath, *imap_path;
1858 IMAPSession *session;
1859 FolderItem *new_item;
1865 g_return_val_if_fail(folder != NULL, NULL);
1866 g_return_val_if_fail(folder->account != NULL, NULL);
1867 g_return_val_if_fail(parent != NULL, NULL);
1868 g_return_val_if_fail(name != NULL, NULL);
1870 MUTEX_TRYLOCK_OR_RETURN_VAL(NULL);
1872 session = imap_session_get(folder);
1878 if (!folder_item_parent(parent) && strcmp(name, "INBOX") == 0)
1879 dirpath = g_strdup(name);
1880 else if (parent->path)
1881 dirpath = g_strconcat(parent->path, "/", name, NULL);
1882 else if ((p = strchr(name, '/')) != NULL && *(p + 1) != '\0')
1883 dirpath = g_strdup(name);
1884 else if (folder->account->imap_dir && *folder->account->imap_dir) {
1887 Xstrdup_a(imap_dir, folder->account->imap_dir, {MUTEX_UNLOCK();return NULL;});
1888 strtailchomp(imap_dir, '/');
1889 dirpath = g_strconcat(imap_dir, "/", name, NULL);
1891 dirpath = g_strdup(name);
1893 /* keep trailing directory separator to create a folder that contains
1895 imap_path = imap_utf8_to_modified_utf7(dirpath);
1896 strtailchomp(dirpath, '/');
1897 Xstrdup_a(new_name, name, {
1902 strtailchomp(new_name, '/');
1903 separator = imap_get_path_separator(IMAP_FOLDER(folder), imap_path);
1904 imap_path_separator_subst(imap_path, separator);
1905 subst_char(new_name, '/', separator);
1907 if (strcmp(name, "INBOX") != 0) {
1910 gboolean exist = FALSE;
1912 argbuf = g_ptr_array_new();
1913 ok = imap_cmd_list(session, NULL, imap_path,
1915 if (ok != IMAP_SUCCESS) {
1916 log_warning(_("can't create mailbox: LIST failed\n"));
1919 ptr_array_free_strings(argbuf);
1920 g_ptr_array_free(argbuf, TRUE);
1925 for (i = 0; i < argbuf->len; i++) {
1927 str = g_ptr_array_index(argbuf, i);
1928 if (!strncmp(str, "LIST ", 5)) {
1933 ptr_array_free_strings(argbuf);
1934 g_ptr_array_free(argbuf, TRUE);
1937 ok = imap_cmd_create(session, imap_path);
1938 if (ok != IMAP_SUCCESS) {
1939 log_warning(_("can't create mailbox\n"));
1948 new_item = folder_item_new(folder, new_name, dirpath);
1949 folder_item_append(parent, new_item);
1953 dirpath = folder_item_get_path(new_item);
1954 if (!is_dir_exist(dirpath))
1955 make_dir_hier(dirpath);
1962 static gint imap_rename_folder(Folder *folder, FolderItem *item,
1967 gchar *real_oldpath;
1968 gchar *real_newpath;
1970 gchar *old_cache_dir;
1971 gchar *new_cache_dir;
1972 IMAPSession *session;
1975 gint exists, recent, unseen;
1976 guint32 uid_validity;
1978 g_return_val_if_fail(folder != NULL, -1);
1979 g_return_val_if_fail(item != NULL, -1);
1980 g_return_val_if_fail(item->path != NULL, -1);
1981 g_return_val_if_fail(name != NULL, -1);
1983 MUTEX_TRYLOCK_OR_RETURN_VAL(-1);
1985 if (strchr(name, imap_get_path_separator(IMAP_FOLDER(folder), item->path)) != NULL) {
1986 g_warning(_("New folder name must not contain the namespace "
1992 session = imap_session_get(folder);
1997 real_oldpath = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1999 g_free(session->mbox);
2000 session->mbox = NULL;
2001 ok = imap_cmd_examine(session, "INBOX",
2002 &exists, &recent, &unseen, &uid_validity, FALSE);
2003 if (ok != IMAP_SUCCESS) {
2004 g_free(real_oldpath);
2009 separator = imap_get_path_separator(IMAP_FOLDER(folder), item->path);
2010 if (strchr(item->path, G_DIR_SEPARATOR)) {
2011 dirpath = g_path_get_dirname(item->path);
2012 newpath = g_strconcat(dirpath, G_DIR_SEPARATOR_S, name, NULL);
2015 newpath = g_strdup(name);
2017 real_newpath = imap_utf8_to_modified_utf7(newpath);
2018 imap_path_separator_subst(real_newpath, separator);
2020 ok = imap_cmd_rename(session, real_oldpath, real_newpath);
2021 if (ok != IMAP_SUCCESS) {
2022 log_warning(_("can't rename mailbox: %s to %s\n"),
2023 real_oldpath, real_newpath);
2024 g_free(real_oldpath);
2026 g_free(real_newpath);
2032 item->name = g_strdup(name);
2034 old_cache_dir = folder_item_get_path(item);
2036 paths[0] = g_strdup(item->path);
2038 g_node_traverse(item->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
2039 imap_rename_folder_func, paths);
2041 if (is_dir_exist(old_cache_dir)) {
2042 new_cache_dir = folder_item_get_path(item);
2043 if (rename(old_cache_dir, new_cache_dir) < 0) {
2044 FILE_OP_ERROR(old_cache_dir, "rename");
2046 g_free(new_cache_dir);
2049 g_free(old_cache_dir);
2052 g_free(real_oldpath);
2053 g_free(real_newpath);
2059 static gint imap_remove_folder_real(Folder *folder, FolderItem *item)
2062 IMAPSession *session;
2065 gint exists, recent, unseen;
2066 guint32 uid_validity;
2068 g_return_val_if_fail(folder != NULL, -1);
2069 g_return_val_if_fail(item != NULL, -1);
2070 g_return_val_if_fail(item->path != NULL, -1);
2072 MUTEX_TRYLOCK_OR_RETURN_VAL(-1);
2074 session = imap_session_get(folder);
2079 path = imap_get_real_path(IMAP_FOLDER(folder), item->path);
2081 ok = imap_cmd_examine(session, "INBOX",
2082 &exists, &recent, &unseen, &uid_validity, FALSE);
2083 if (ok != IMAP_SUCCESS) {
2089 ok = imap_cmd_delete(session, path);
2090 if (ok != IMAP_SUCCESS) {
2091 log_warning(_("can't delete mailbox\n"));
2098 cache_dir = folder_item_get_path(item);
2099 if (is_dir_exist(cache_dir) && remove_dir_recursive(cache_dir) < 0)
2100 g_warning("can't remove directory '%s'\n", cache_dir);
2102 folder_item_remove(item);
2108 static gint imap_remove_folder(Folder *folder, FolderItem *item)
2112 g_return_val_if_fail(item != NULL, -1);
2113 g_return_val_if_fail(item->folder != NULL, -1);
2114 g_return_val_if_fail(item->node != NULL, -1);
2115 g_return_val_if_fail(item->no_select == FALSE, -1);
2117 node = item->node->children;
2118 while (node != NULL) {
2120 if (imap_remove_folder(folder, FOLDER_ITEM(node->data)) < 0)
2124 debug_print("IMAP removing %s\n", item->path);
2126 if (imap_remove_all_msg(folder, item) < 0)
2128 return imap_remove_folder_real(folder, item);
2131 typedef struct _uncached_data {
2132 IMAPSession *session;
2134 MsgNumberList *numlist;
2140 static void *imap_get_uncached_messages_thread(void *data)
2142 uncached_data *stuff = (uncached_data *)data;
2143 IMAPSession *session = stuff->session;
2144 FolderItem *item = stuff->item;
2145 MsgNumberList *numlist = stuff->numlist;
2148 GSList *newlist = NULL;
2149 GSList *llast = NULL;
2150 GString *str = NULL;
2152 GSList *seq_list, *cur;
2155 stuff->total = g_slist_length(numlist);
2158 if (session == NULL || item == NULL || item->folder == NULL
2159 || FOLDER_CLASS(item->folder) != &imap_class) {
2164 seq_list = imap_get_seq_set_from_numlist(numlist);
2165 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
2166 imapset = cur->data;
2168 if (!imapset || strlen(imapset) == 0)
2171 if (imap_cmd_envelope(session, imapset)
2173 log_warning(_("can't get envelope\n"));
2177 str = g_string_new(NULL);
2180 if ((tmp =sock_getline(SESSION(session)->sock)) == NULL) {
2181 log_warning(_("error occurred while getting envelope.\n"));
2182 g_string_free(str, TRUE);
2187 if (tmp[0] != '*' || tmp[1] != ' ') {
2188 log_print("IMAP4< %s\n", tmp);
2192 if (strstr(tmp, "FETCH") == NULL) {
2193 log_print("IMAP4< %s\n", tmp);
2197 log_print("IMAP4< %s\n", tmp);
2198 g_string_assign(str, tmp);
2203 msginfo = imap_parse_envelope
2204 (SESSION(session)->sock, item, str);
2206 log_warning(_("can't parse envelope: %s\n"), str->str);
2209 if (item->stype == F_QUEUE) {
2210 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_QUEUED);
2211 } else if (item->stype == F_DRAFT) {
2212 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_DRAFT);
2215 msginfo->folder = item;
2218 llast = newlist = g_slist_append(newlist, msginfo);
2220 llast = g_slist_append(llast, msginfo);
2221 llast = llast->next;
2225 g_string_free(str, TRUE);
2227 imap_seq_set_free(seq_list);
2229 session_set_access_time(SESSION(session));
2235 static GSList *imap_get_uncached_messages(IMAPSession *session,
2237 MsgNumberList *numlist)
2239 uncached_data *data = g_new0(uncached_data, 1);
2240 GSList *result = NULL;
2246 data->session = session;
2248 data->numlist = numlist;
2252 if (prefs_common.work_offline && !imap_gtk_should_override()) {
2257 #if (defined USE_PTHREAD && defined __GLIBC__ && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 3)))
2258 MUTEX_TRYLOCK_OR_RETURN_VAL(NULL);
2260 if (pthread_create(&pt, PTHREAD_CREATE_JOINABLE,
2261 imap_get_uncached_messages_thread, data) != 0) {
2262 result = (GSList *)imap_get_uncached_messages_thread(data);
2267 debug_print("+++waiting for imap_get_uncached_messages_thread...\n");
2268 statusbar_print_all(_("IMAP4 Fetching uncached short headers..."));
2269 while(!data->done) {
2270 /* don't let the interface freeze while waiting */
2272 if (data->total != 0 && last_cur != data->cur && data->cur % 10 == 0) {
2274 g_snprintf(buf, sizeof(buf), "%d / %d",
2275 data->cur, data->total);
2276 gtk_progress_bar_set_text
2277 (GTK_PROGRESS_BAR(mainwindow_get_mainwindow()->progressbar), buf);
2278 gtk_progress_bar_set_fraction
2279 (GTK_PROGRESS_BAR(mainwindow_get_mainwindow()->progressbar),
2280 (gfloat)data->cur / (gfloat)data->total);
2281 last_cur = data->cur;
2284 gtk_progress_bar_set_fraction
2285 (GTK_PROGRESS_BAR(mainwindow_get_mainwindow()->progressbar), 0);
2286 gtk_progress_bar_set_text
2287 (GTK_PROGRESS_BAR(mainwindow_get_mainwindow()->progressbar), "");
2288 statusbar_pop_all();
2290 debug_print("---imap_get_uncached_messages_thread done\n");
2292 /* get the thread's return value and clean its resources */
2293 pthread_join(pt, (void *)&result);
2296 result = (GSList *)imap_get_uncached_messages_thread(data);
2302 static void imap_delete_all_cached_messages(FolderItem *item)
2306 g_return_if_fail(item != NULL);
2307 g_return_if_fail(item->folder != NULL);
2308 g_return_if_fail(FOLDER_CLASS(item->folder) == &imap_class);
2310 debug_print("Deleting all cached messages...\n");
2312 dir = folder_item_get_path(item);
2313 if (is_dir_exist(dir))
2314 remove_all_numbered_files(dir);
2317 debug_print("done.\n");
2321 static SockInfo *imap_open_tunnel(const gchar *server,
2322 const gchar *tunnelcmd,
2325 static SockInfo *imap_open_tunnel(const gchar *server,
2326 const gchar *tunnelcmd)
2331 if ((sock = sock_connect_cmd(server, tunnelcmd)) == NULL) {
2332 log_warning(_("Can't establish IMAP4 session with: %s\n"),
2337 return imap_init_sock(sock, ssl_type);
2339 return imap_init_sock(sock);
2343 void *imap_open_thread(void *data)
2345 SockInfo *sock = NULL;
2346 thread_data *td = (thread_data *)data;
2347 if ((sock = sock_connect(td->server, td->port)) == NULL) {
2348 log_warning(_("Can't connect to IMAP4 server: %s:%d\n"),
2349 td->server, td->port);
2359 static SockInfo *imap_open(const gchar *server, gushort port,
2362 static SockInfo *imap_open(const gchar *server, gushort port)
2365 thread_data *td = g_new0(thread_data, 1);
2369 SockInfo *sock = NULL;
2372 td->ssl_type = ssl_type;
2374 td->server = g_strdup(server);
2378 statusbar_print_all(_("Connecting to IMAP4 server: %s..."), server);
2380 #if (defined USE_PTHREAD && defined __GLIBC__ && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 3)))
2381 if (pthread_create(&pt, PTHREAD_CREATE_JOINABLE,
2382 imap_open_thread, td) != 0) {
2383 statusbar_pop_all();
2384 sock = imap_open_thread(td);
2386 debug_print("+++waiting for imap_open_thread...\n");
2388 /* don't let the interface freeze while waiting */
2392 /* get the thread's return value and clean its resources */
2393 pthread_join(pt, (void *)&sock);
2396 sock = imap_open_thread(td);
2399 if (sock && td->ssl_type == SSL_TUNNEL && !ssl_init_socket(sock)) {
2400 log_warning(_("Can't establish IMAP4 session with: %s:%d\n"),
2401 td->server, td->port);
2410 debug_print("---imap_open_thread returned %p\n", sock);
2411 statusbar_pop_all();
2413 if(!sock && !prefs_common.no_recv_err_panel) {
2414 alertpanel_error(_("Can't connect to IMAP4 server: %s:%d"),
2422 static SockInfo *imap_init_sock(SockInfo *sock, SSLType ssl_type)
2424 static SockInfo *imap_init_sock(SockInfo *sock)
2431 static GList *imap_parse_namespace_str(gchar *str)
2436 IMAPNameSpace *namespace;
2437 GList *ns_list = NULL;
2439 while (*p != '\0') {
2440 /* parse ("#foo" "/") */
2442 while (*p && *p != '(') p++;
2443 if (*p == '\0') break;
2446 while (*p && *p != '"') p++;
2447 if (*p == '\0') break;
2451 while (*p && *p != '"') p++;
2452 if (*p == '\0') break;
2456 while (*p && isspace(*p)) p++;
2457 if (*p == '\0') break;
2458 if (strncmp(p, "NIL", 3) == 0)
2460 else if (*p == '"') {
2463 while (*p && *p != '"') p++;
2464 if (*p == '\0') break;
2469 while (*p && *p != ')') p++;
2470 if (*p == '\0') break;
2473 namespace = g_new(IMAPNameSpace, 1);
2474 namespace->name = g_strdup(name);
2475 namespace->separator = separator ? separator[0] : '\0';
2476 ns_list = g_list_append(ns_list, namespace);
2482 static void imap_parse_namespace(IMAPSession *session, IMAPFolder *folder)
2487 g_return_if_fail(session != NULL);
2488 g_return_if_fail(folder != NULL);
2490 if (folder->ns_personal != NULL ||
2491 folder->ns_others != NULL ||
2492 folder->ns_shared != NULL)
2495 if (!imap_has_capability(session, "NAMESPACE")) {
2496 imap_get_namespace_by_list(session, folder);
2500 if (imap_cmd_namespace(session, &ns_str)
2502 log_warning(_("can't get namespace\n"));
2506 str_array = strsplit_parenthesis(ns_str, '(', ')', 3);
2507 if (str_array == NULL) {
2509 imap_get_namespace_by_list(session, folder);
2513 folder->ns_personal = imap_parse_namespace_str(str_array[0]);
2514 if (str_array[0] && str_array[1])
2515 folder->ns_others = imap_parse_namespace_str(str_array[1]);
2516 if (str_array[0] && str_array[1] && str_array[2])
2517 folder->ns_shared = imap_parse_namespace_str(str_array[2]);
2518 g_strfreev(str_array);
2522 static void imap_get_namespace_by_list(IMAPSession *session, IMAPFolder *folder)
2524 GSList *item_list, *cur;
2525 gchar separator = '\0';
2526 IMAPNameSpace *namespace;
2528 g_return_if_fail(session != NULL);
2529 g_return_if_fail(folder != NULL);
2531 if (folder->ns_personal != NULL ||
2532 folder->ns_others != NULL ||
2533 folder->ns_shared != NULL)
2536 imap_gen_send(session, "LIST \"\" \"\"");
2537 item_list = imap_parse_list(folder, session, "", &separator);
2538 for (cur = item_list; cur != NULL; cur = cur->next)
2539 folder_item_destroy(FOLDER_ITEM(cur->data));
2540 g_slist_free(item_list);
2542 namespace = g_new(IMAPNameSpace, 1);
2543 namespace->name = g_strdup("");
2544 namespace->separator = separator;
2545 folder->ns_personal = g_list_append(NULL, namespace);
2548 static IMAPNameSpace *imap_find_namespace_from_list(GList *ns_list,
2551 IMAPNameSpace *namespace = NULL;
2552 gchar *tmp_path, *name;
2554 if (!path) path = "";
2556 for (; ns_list != NULL; ns_list = ns_list->next) {
2557 IMAPNameSpace *tmp_ns = ns_list->data;
2559 Xstrcat_a(tmp_path, path, "/", return namespace);
2560 Xstrdup_a(name, tmp_ns->name, return namespace);
2561 if (tmp_ns->separator && tmp_ns->separator != '/') {
2562 subst_char(tmp_path, tmp_ns->separator, '/');
2563 subst_char(name, tmp_ns->separator, '/');
2565 if (strncmp(tmp_path, name, strlen(name)) == 0)
2572 static IMAPNameSpace *imap_find_namespace(IMAPFolder *folder,
2575 IMAPNameSpace *namespace;
2577 g_return_val_if_fail(folder != NULL, NULL);
2579 namespace = imap_find_namespace_from_list(folder->ns_personal, path);
2580 if (namespace) return namespace;
2581 namespace = imap_find_namespace_from_list(folder->ns_others, path);
2582 if (namespace) return namespace;
2583 namespace = imap_find_namespace_from_list(folder->ns_shared, path);
2584 if (namespace) return namespace;
2589 static gchar imap_get_path_separator(IMAPFolder *folder, const gchar *path)
2591 IMAPNameSpace *namespace;
2592 gchar separator = '/';
2594 namespace = imap_find_namespace(folder, path);
2595 if (namespace && namespace->separator)
2596 separator = namespace->separator;
2601 static gchar *imap_get_real_path(IMAPFolder *folder, const gchar *path)
2606 g_return_val_if_fail(folder != NULL, NULL);
2607 g_return_val_if_fail(path != NULL, NULL);
2609 real_path = imap_utf8_to_modified_utf7(path);
2610 separator = imap_get_path_separator(folder, path);
2611 imap_path_separator_subst(real_path, separator);
2616 static gchar *imap_parse_atom(SockInfo *sock, gchar *src,
2617 gchar *dest, gint dest_len, GString *str)
2619 gchar *cur_pos = src;
2622 g_return_val_if_fail(str != NULL, cur_pos);
2624 /* read the next line if the current response buffer is empty */
2625 while (isspace(*(guchar *)cur_pos)) cur_pos++;
2626 while (*cur_pos == '\0') {
2627 if ((nextline = imap_getline(sock)) == NULL)
2629 g_string_assign(str, nextline);
2631 strretchomp(nextline);
2632 /* log_print("IMAP4< %s\n", nextline); */
2633 debug_print("IMAP4< %s\n", nextline);
2636 while (isspace(*(guchar *)cur_pos)) cur_pos++;
2639 if (!strncmp(cur_pos, "NIL", 3)) {
2642 } else if (*cur_pos == '\"') {
2645 p = get_quoted(cur_pos, '\"', dest, dest_len);
2646 cur_pos = p ? p : cur_pos + 2;
2647 } else if (*cur_pos == '{') {
2652 cur_pos = strchr_cpy(cur_pos + 1, '}', buf, sizeof(buf));
2654 g_return_val_if_fail(len >= 0, cur_pos);
2656 g_string_truncate(str, 0);
2660 if ((nextline = imap_getline(sock)) == NULL)
2662 line_len += strlen(nextline);
2663 g_string_append(str, nextline);
2665 strretchomp(nextline);
2666 /* log_print("IMAP4< %s\n", nextline); */
2667 debug_print("IMAP4< %s\n", nextline);
2669 } while (line_len < len);
2671 memcpy(dest, cur_pos, MIN(len, dest_len - 1));
2672 dest[MIN(len, dest_len - 1)] = '\0';
2679 static gchar *imap_get_header(SockInfo *sock, gchar *cur_pos, gchar **headers,
2689 g_return_val_if_fail(str != NULL, cur_pos);
2691 while (isspace(*(guchar *)cur_pos)) cur_pos++;
2693 g_return_val_if_fail(*cur_pos == '{', cur_pos);
2695 cur_pos = strchr_cpy(cur_pos + 1, '}', buf, sizeof(buf));
2697 g_return_val_if_fail(len >= 0, cur_pos);
2699 g_string_truncate(str, 0);
2703 if ((nextline = sock_getline(sock)) == NULL) {
2707 block_len += strlen(nextline);
2708 g_string_append(str, nextline);
2710 strretchomp(nextline);
2711 /* debug_print("IMAP4< %s\n", nextline); */
2713 } while (block_len < len);
2715 debug_print("IMAP4< [contents of BODY.PEEK[HEADER_FIELDS (...)]\n");
2717 *headers = g_strndup(cur_pos, len);
2720 while (isspace(*(guchar *)cur_pos)) cur_pos++;
2721 while (*cur_pos == '\0') {
2722 if ((nextline = sock_getline(sock)) == NULL)
2724 g_string_assign(str, nextline);
2726 strretchomp(nextline);
2727 debug_print("IMAP4< %s\n", nextline);
2730 while (isspace(*(guchar *)cur_pos)) cur_pos++;
2736 static MsgFlags imap_parse_flags(const gchar *flag_str)
2738 const gchar *p = flag_str;
2739 MsgFlags flags = {0, 0};
2741 flags.perm_flags = MSG_UNREAD;
2743 while ((p = strchr(p, '\\')) != NULL) {
2746 if (g_ascii_strncasecmp(p, "Recent", 6) == 0 && MSG_IS_UNREAD(flags)) {
2747 MSG_SET_PERM_FLAGS(flags, MSG_NEW);
2748 } else if (g_ascii_strncasecmp(p, "Seen", 4) == 0) {
2749 MSG_UNSET_PERM_FLAGS(flags, MSG_NEW|MSG_UNREAD);
2750 } else if (g_ascii_strncasecmp(p, "Deleted", 7) == 0) {
2751 MSG_SET_PERM_FLAGS(flags, MSG_DELETED);
2752 } else if (g_ascii_strncasecmp(p, "Flagged", 7) == 0) {
2753 MSG_SET_PERM_FLAGS(flags, MSG_MARKED);
2754 } else if (g_ascii_strncasecmp(p, "Answered", 8) == 0) {
2755 MSG_SET_PERM_FLAGS(flags, MSG_REPLIED);
2762 static MsgInfo *imap_parse_envelope(SockInfo *sock, FolderItem *item,
2765 gchar buf[IMAPBUFSIZE];
2766 MsgInfo *msginfo = NULL;
2771 MsgFlags flags = {0, 0}, imap_flags = {0, 0};
2773 g_return_val_if_fail(line_str != NULL, NULL);
2774 g_return_val_if_fail(line_str->str[0] == '*' &&
2775 line_str->str[1] == ' ', NULL);
2777 MSG_SET_TMP_FLAGS(flags, MSG_IMAP);
2778 if (item->stype == F_QUEUE) {
2779 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
2780 } else if (item->stype == F_DRAFT) {
2781 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
2784 cur_pos = line_str->str + 2;
2786 #define PARSE_ONE_ELEMENT(ch) \
2788 cur_pos = strchr_cpy(cur_pos, ch, buf, sizeof(buf)); \
2789 if (cur_pos == NULL) { \
2790 g_warning("cur_pos == NULL\n"); \
2791 procmsg_msginfo_free(msginfo); \
2796 PARSE_ONE_ELEMENT(' ');
2799 PARSE_ONE_ELEMENT(' ');
2800 g_return_val_if_fail(!strcmp(buf, "FETCH"), NULL);
2802 g_return_val_if_fail(*cur_pos == '(', NULL);
2805 while (*cur_pos != '\0' && *cur_pos != ')') {
2806 while (*cur_pos == ' ') cur_pos++;
2808 if (!strncmp(cur_pos, "UID ", 4)) {
2810 uid = strtoul(cur_pos, &cur_pos, 10);
2811 } else if (!strncmp(cur_pos, "FLAGS ", 6)) {
2813 if (*cur_pos != '(') {
2814 g_warning("*cur_pos != '('\n");
2815 procmsg_msginfo_free(msginfo);
2819 PARSE_ONE_ELEMENT(')');
2820 imap_flags = imap_parse_flags(buf);
2821 } else if (!strncmp(cur_pos, "RFC822.SIZE ", 12)) {
2823 size = strtol(cur_pos, &cur_pos, 10);
2824 } else if (!strncmp(cur_pos, "BODY[HEADER.FIELDS ", 19)) {
2828 if (*cur_pos != '(') {
2829 g_warning("*cur_pos != '('\n");
2830 procmsg_msginfo_free(msginfo);
2834 PARSE_ONE_ELEMENT(')');
2835 if (*cur_pos != ']') {
2836 g_warning("*cur_pos != ']'\n");
2837 procmsg_msginfo_free(msginfo);
2841 cur_pos = imap_get_header(sock, cur_pos, &headers,
2843 msginfo = procheader_parse_str(headers, flags, FALSE, FALSE);
2846 g_warning("invalid FETCH response: %s\n", cur_pos);
2852 msginfo->msgnum = uid;
2853 msginfo->size = size;
2854 msginfo->flags.tmp_flags |= imap_flags.tmp_flags;
2855 msginfo->flags.perm_flags = imap_flags.perm_flags;
2861 static gchar *imap_get_flag_str(IMAPFlags flags)
2866 str = g_string_new(NULL);
2868 if (IMAP_IS_SEEN(flags)) g_string_append(str, "\\Seen ");
2869 if (IMAP_IS_ANSWERED(flags)) g_string_append(str, "\\Answered ");
2870 if (IMAP_IS_FLAGGED(flags)) g_string_append(str, "\\Flagged ");
2871 if (IMAP_IS_DELETED(flags)) g_string_append(str, "\\Deleted ");
2872 if (IMAP_IS_DRAFT(flags)) g_string_append(str, "\\Draft");
2874 if (str->len > 0 && str->str[str->len - 1] == ' ')
2875 g_string_truncate(str, str->len - 1);
2878 g_string_free(str, FALSE);
2883 static gint imap_set_message_flags(IMAPSession *session,
2884 MsgNumberList *numlist,
2894 flag_str = imap_get_flag_str(flags);
2895 cmd = g_strconcat(is_set ? "+FLAGS.SILENT (" : "-FLAGS.SILENT (",
2896 flag_str, ")", NULL);
2899 seq_list = imap_get_seq_set_from_numlist(numlist);
2900 imapset = get_seq_set_from_seq_list(seq_list);
2902 ok = imap_cmd_store(session, imapset, cmd);
2905 imap_seq_set_free(seq_list);
2911 typedef struct _select_data {
2912 IMAPSession *session;
2917 guint32 *uid_validity;
2921 static void *imap_select_thread(void *data)
2923 select_data *stuff = (select_data *)data;
2924 IMAPSession *session = stuff->session;
2925 gchar *real_path = stuff->real_path;
2926 gint *exists = stuff->exists;
2927 gint *recent = stuff->recent;
2928 gint *unseen = stuff->unseen;
2929 guint32 *uid_validity = stuff->uid_validity;
2932 ok = imap_cmd_select(session, real_path,
2933 exists, recent, unseen, uid_validity, TRUE);
2935 return GINT_TO_POINTER(ok);
2938 static gint imap_select(IMAPSession *session, IMAPFolder *folder,
2940 gint *exists, gint *recent, gint *unseen,
2941 guint32 *uid_validity, gboolean block)
2945 gint exists_, recent_, unseen_;
2946 guint32 uid_validity_;
2948 if (!exists || !recent || !unseen || !uid_validity) {
2949 if (session->mbox && strcmp(session->mbox, path) == 0)
2950 return IMAP_SUCCESS;
2954 uid_validity = &uid_validity_;
2957 g_free(session->mbox);
2958 session->mbox = NULL;
2960 real_path = imap_get_real_path(folder, path);
2962 #if (defined USE_PTHREAD && defined __GLIBC__ && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 3)))
2964 if (block == FALSE) {
2965 select_data *data = g_new0(select_data, 1);
2968 data->session = session;
2969 data->real_path = real_path;
2970 data->exists = exists;
2971 data->recent = recent;
2972 data->unseen = unseen;
2973 data->uid_validity = uid_validity;
2976 if (pthread_create(&pt, PTHREAD_CREATE_JOINABLE,
2977 imap_select_thread, data) != 0) {
2978 ok = GPOINTER_TO_INT(imap_select_thread(data));
2981 debug_print("+++waiting for imap_select_thread...\n");
2982 while(!data->done) {
2983 /* don't let the interface freeze while waiting */
2986 debug_print("---imap_select_thread done\n");
2988 /* get the thread's return value and clean its resources */
2989 pthread_join(pt, &tmp);
2990 ok = GPOINTER_TO_INT(tmp);
2994 ok = imap_cmd_select(session, real_path,
2995 exists, recent, unseen, uid_validity, block);
2998 ok = imap_cmd_select(session, real_path,
2999 exists, recent, unseen, uid_validity, block);
3001 if (ok != IMAP_SUCCESS)
3002 log_warning(_("can't select folder: %s\n"), real_path);
3004 session->mbox = g_strdup(path);
3005 session->folder_content_changed = FALSE;
3012 #define THROW(err) { ok = err; goto catch; }
3014 static gint imap_status(IMAPSession *session, IMAPFolder *folder,
3016 gint *messages, gint *recent,
3017 guint32 *uid_next, guint32 *uid_validity,
3018 gint *unseen, gboolean block)
3023 GPtrArray *argbuf = NULL;
3026 if (messages && recent && uid_next && uid_validity && unseen) {
3027 *messages = *recent = *uid_next = *uid_validity = *unseen = 0;
3028 argbuf = g_ptr_array_new();
3031 real_path = imap_get_real_path(folder, path);
3032 QUOTE_IF_REQUIRED(real_path_, real_path);
3033 imap_gen_send(session, "STATUS %s "
3034 "(MESSAGES RECENT UIDNEXT UIDVALIDITY UNSEEN)",
3037 ok = imap_cmd_ok_with_block(session, argbuf, block);
3038 if (ok != IMAP_SUCCESS || !argbuf) THROW(ok);
3040 str = search_array_str(argbuf, "STATUS");
3041 if (!str) THROW(IMAP_ERROR);
3043 str = strrchr(str, '(');
3044 if (!str) THROW(IMAP_ERROR);
3046 while (*str != '\0' && *str != ')') {
3047 while (*str == ' ') str++;
3049 if (!strncmp(str, "MESSAGES ", 9)) {
3051 *messages = strtol(str, &str, 10);
3052 } else if (!strncmp(str, "RECENT ", 7)) {
3054 *recent = strtol(str, &str, 10);
3055 } else if (!strncmp(str, "UIDNEXT ", 8)) {
3057 *uid_next = strtoul(str, &str, 10);
3058 } else if (!strncmp(str, "UIDVALIDITY ", 12)) {
3060 *uid_validity = strtoul(str, &str, 10);
3061 } else if (!strncmp(str, "UNSEEN ", 7)) {
3063 *unseen = strtol(str, &str, 10);
3065 g_warning("invalid STATUS response: %s\n", str);
3073 ptr_array_free_strings(argbuf);
3074 g_ptr_array_free(argbuf, TRUE);
3082 static gboolean imap_has_capability(IMAPSession *session, const gchar *cap)
3086 for (p = session->capability; *p != NULL; ++p) {
3087 if (!g_ascii_strcasecmp(*p, cap))
3094 static void imap_free_capabilities(IMAPSession *session)
3096 g_strfreev(session->capability);
3097 session->capability = NULL;
3100 /* low-level IMAP4rev1 commands */
3102 static gint imap_cmd_authenticate(IMAPSession *session, const gchar *user,
3103 const gchar *pass, IMAPAuthType type)
3110 gchar hexdigest[33];
3114 auth_type = "CRAM-MD5";
3116 imap_gen_send(session, "AUTHENTICATE %s", auth_type);
3117 ok = imap_gen_recv(session, &buf);
3118 if (ok != IMAP_SUCCESS || buf[0] != '+' || buf[1] != ' ') {
3123 challenge = g_malloc(strlen(buf + 2) + 1);
3124 challenge_len = base64_decode(challenge, buf + 2, -1);
3125 challenge[challenge_len] = '\0';
3127 log_print("IMAP< [Decoded: %s]\n", challenge);
3129 md5_hex_hmac(hexdigest, challenge, challenge_len, pass, strlen(pass));
3132 response = g_strdup_printf("%s %s", user, hexdigest);
3133 log_print("IMAP> [Encoded: %s]\n", response);
3134 response64 = g_malloc((strlen(response) + 3) * 2 + 1);
3135 base64_encode(response64, response, strlen(response));
3138 log_print("IMAP> %s\n", response64);
3139 sock_puts(SESSION(session)->sock, response64);
3140 ok = imap_cmd_ok(session, NULL);
3141 if (ok != IMAP_SUCCESS)
3142 log_warning(_("IMAP4 authentication failed.\n"));
3147 static gint imap_cmd_login(IMAPSession *session,
3148 const gchar *user, const gchar *pass)
3153 imap_gen_send(session, "LOGIN {%d}\r\n%s {%d}\r\n%s",
3155 strlen(pass), pass);
3157 ok = imap_gen_recv_with_block(session, &ans, TRUE);
3158 if (ok != IMAP_SUCCESS || ans[0] != '+' || ans[1] != ' ') {
3163 ok = imap_gen_recv_with_block(session, &ans, TRUE);
3164 if (ok != IMAP_SUCCESS || ans[0] != '+' || ans[1] != ' ') {
3169 ok = imap_cmd_ok(session, NULL);
3170 if (ok != IMAP_SUCCESS)
3171 log_warning(_("IMAP4 login failed.\n"));
3176 static gint imap_cmd_logout(IMAPSession *session)
3178 imap_gen_send(session, "LOGOUT");
3179 return imap_cmd_ok(session, NULL);
3182 static gint imap_cmd_noop(IMAPSession *session)
3184 imap_gen_send(session, "NOOP");
3185 return imap_cmd_ok(session, NULL);
3189 static gint imap_cmd_starttls(IMAPSession *session)
3191 imap_gen_send(session, "STARTTLS");
3192 return imap_cmd_ok(session, NULL);
3196 #define THROW(err) { ok = err; goto catch; }
3198 static gint imap_cmd_namespace(IMAPSession *session, gchar **ns_str)
3204 argbuf = g_ptr_array_new();
3206 imap_gen_send(session, "NAMESPACE");
3207 if ((ok = imap_cmd_ok(session, argbuf)) != IMAP_SUCCESS) THROW(ok);
3209 str = search_array_str(argbuf, "NAMESPACE");
3210 if (!str) THROW(IMAP_ERROR);
3212 *ns_str = g_strdup(str);
3215 ptr_array_free_strings(argbuf);
3216 g_ptr_array_free(argbuf, TRUE);
3223 static gint imap_cmd_list(IMAPSession *session, const gchar *ref,
3224 const gchar *mailbox, GPtrArray *argbuf)
3226 gchar *ref_, *mailbox_;
3228 if (!ref) ref = "\"\"";
3229 if (!mailbox) mailbox = "\"\"";
3231 QUOTE_IF_REQUIRED(ref_, ref);
3232 QUOTE_IF_REQUIRED(mailbox_, mailbox);
3233 imap_gen_send(session, "LIST %s %s", ref_, mailbox_);
3235 return imap_cmd_ok(session, argbuf);
3238 #define THROW goto catch
3240 static gint imap_cmd_do_select(IMAPSession *session, const gchar *folder,
3242 gint *exists, gint *recent, gint *unseen,
3243 guint32 *uid_validity, gboolean block)
3250 unsigned int uid_validity_;
3252 *exists = *recent = *unseen = *uid_validity = 0;
3253 argbuf = g_ptr_array_new();
3256 select_cmd = "EXAMINE";
3258 select_cmd = "SELECT";
3260 QUOTE_IF_REQUIRED(folder_, folder);
3261 imap_gen_send(session, "%s %s", select_cmd, folder_);
3263 if ((ok = imap_cmd_ok_with_block(session, argbuf, block)) != IMAP_SUCCESS) THROW;
3265 resp_str = search_array_contain_str(argbuf, "EXISTS");
3267 if (sscanf(resp_str,"%d EXISTS", exists) != 1) {
3268 g_warning("imap_cmd_select(): invalid EXISTS line.\n");
3273 resp_str = search_array_contain_str(argbuf, "RECENT");
3275 if (sscanf(resp_str, "%d RECENT", recent) != 1) {
3276 g_warning("imap_cmd_select(): invalid RECENT line.\n");
3281 resp_str = search_array_contain_str(argbuf, "UIDVALIDITY");
3283 if (sscanf(resp_str, "OK [UIDVALIDITY %u] ", &uid_validity_)
3285 g_warning("imap_cmd_select(): invalid UIDVALIDITY line.\n");
3288 *uid_validity = uid_validity_;
3291 resp_str = search_array_contain_str(argbuf, "UNSEEN");
3293 if (sscanf(resp_str, "OK [UNSEEN %d] ", unseen) != 1) {
3294 g_warning("imap_cmd_select(): invalid UNSEEN line.\n");
3300 ptr_array_free_strings(argbuf);
3301 g_ptr_array_free(argbuf, TRUE);
3306 static gint imap_cmd_select(IMAPSession *session, const gchar *folder,
3307 gint *exists, gint *recent, gint *unseen,
3308 guint32 *uid_validity, gboolean block)
3310 return imap_cmd_do_select(session, folder, FALSE,
3311 exists, recent, unseen, uid_validity, block);
3314 static gint imap_cmd_examine(IMAPSession *session, const gchar *folder,
3315 gint *exists, gint *recent, gint *unseen,
3316 guint32 *uid_validity, gboolean block)
3318 return imap_cmd_do_select(session, folder, TRUE,
3319 exists, recent, unseen, uid_validity, block);
3324 static gint imap_cmd_create(IMAPSession *session, const gchar *folder)
3328 QUOTE_IF_REQUIRED(folder_, folder);
3329 imap_gen_send(session, "CREATE %s", folder_);
3331 return imap_cmd_ok(session, NULL);
3334 static gint imap_cmd_rename(IMAPSession *session, const gchar *old_folder,
3335 const gchar *new_folder)
3337 gchar *old_folder_, *new_folder_;
3339 QUOTE_IF_REQUIRED(old_folder_, old_folder);
3340 QUOTE_IF_REQUIRED(new_folder_, new_folder);
3341 imap_gen_send(session, "RENAME %s %s", old_folder_, new_folder_);
3343 return imap_cmd_ok(session, NULL);
3346 static gint imap_cmd_delete(IMAPSession *session, const gchar *folder)
3350 QUOTE_IF_REQUIRED(folder_, folder);
3351 imap_gen_send(session, "DELETE %s", folder_);
3353 return imap_cmd_ok(session, NULL);
3356 static gint imap_cmd_search(IMAPSession *session, const gchar *criteria,
3357 GSList **list, gboolean block)
3363 g_return_val_if_fail(criteria != NULL, IMAP_ERROR);
3364 g_return_val_if_fail(list != NULL, IMAP_ERROR);
3368 argbuf = g_ptr_array_new();
3369 imap_gen_send(session, "UID SEARCH %s", criteria);
3371 ok = imap_cmd_ok_with_block(session, argbuf, block);
3372 if (ok != IMAP_SUCCESS) {
3373 ptr_array_free_strings(argbuf);
3374 g_ptr_array_free(argbuf, TRUE);
3378 if ((uidlist = search_array_str(argbuf, "SEARCH ")) != NULL) {
3379 gchar **strlist, **p;
3381 strlist = g_strsplit(uidlist + 7, " ", 0);
3382 for (p = strlist; *p != NULL; ++p) {
3385 if (sscanf(*p, "%u", &msgnum) == 1)
3386 *list = g_slist_append(*list, GINT_TO_POINTER(msgnum));
3388 g_strfreev(strlist);
3390 ptr_array_free_strings(argbuf);
3391 g_ptr_array_free(argbuf, TRUE);
3393 return IMAP_SUCCESS;
3396 typedef struct _fetch_data {
3397 IMAPSession *session;
3399 const gchar *filename;
3405 static void *imap_cmd_fetch_thread(void *data)
3407 fetch_data *stuff = (fetch_data *)data;
3408 IMAPSession *session = stuff->session;
3409 guint32 uid = stuff->uid;
3410 const gchar *filename = stuff->filename;
3418 if (filename == NULL) {
3420 return GINT_TO_POINTER(IMAP_ERROR);
3424 imap_gen_send(session, "UID FETCH %d BODY.PEEK[]", uid);
3426 imap_gen_send(session, "UID FETCH %d BODY.PEEK[HEADER]",
3428 while ((ok = imap_gen_recv_block(session, &buf)) == IMAP_SUCCESS) {
3429 if (buf[0] != '*' || buf[1] != ' ') {
3432 return GINT_TO_POINTER(IMAP_ERROR);
3434 if (strstr(buf, "FETCH") != NULL) break;
3437 if (ok != IMAP_SUCCESS) {
3440 return GINT_TO_POINTER(ok);
3443 #define RETURN_ERROR_IF_FAIL(cond) \
3446 stuff->done = TRUE; \
3447 return GINT_TO_POINTER(IMAP_ERROR); \
3450 cur_pos = strchr(buf, '{');
3451 RETURN_ERROR_IF_FAIL(cur_pos != NULL);
3452 cur_pos = strchr_cpy(cur_pos + 1, '}', size_str, sizeof(size_str));
3453 RETURN_ERROR_IF_FAIL(cur_pos != NULL);
3454 size_num = atol(size_str);
3455 RETURN_ERROR_IF_FAIL(size_num >= 0);
3457 RETURN_ERROR_IF_FAIL(*cur_pos == '\0');
3459 #undef RETURN_ERROR_IF_FAIL
3463 if (recv_bytes_write_to_file(SESSION(session)->sock,
3464 size_num, filename) != 0) {
3466 return GINT_TO_POINTER(IMAP_ERROR);
3468 if (imap_gen_recv_block(session, &buf) != IMAP_SUCCESS) {
3471 return GINT_TO_POINTER(IMAP_ERROR);
3474 if (buf[0] == '\0' || buf[strlen(buf) - 1] != ')') {
3477 return GINT_TO_POINTER(IMAP_ERROR);
3481 ok = imap_cmd_ok_block(session, NULL);
3484 return GINT_TO_POINTER(ok);
3487 static gint imap_cmd_fetch(IMAPSession *session, guint32 uid,
3488 const gchar *filename, gboolean headers,
3491 fetch_data *data = g_new0(fetch_data, 1);
3498 data->session = session;
3500 data->filename = filename;
3501 data->headers = headers;
3504 if (prefs_common.work_offline && !imap_gtk_should_override()) {
3509 #if (defined USE_PTHREAD && defined __GLIBC__ && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 3)))
3510 MUTEX_TRYLOCK_OR_RETURN_VAL(-1);
3511 if (pthread_create(&pt, PTHREAD_CREATE_JOINABLE,
3512 imap_cmd_fetch_thread, data) != 0) {
3513 result = GPOINTER_TO_INT(imap_cmd_fetch_thread(data));
3518 debug_print("+++waiting for imap_cmd_fetch_thread...\n");
3519 while(!data->done) {
3520 /* don't let the interface freeze while waiting */
3523 debug_print("---imap_cmd_fetch_thread done\n");
3525 /* get the thread's return value and clean its resources */
3526 pthread_join(pt, &tmp);
3527 result = GPOINTER_TO_INT(tmp);
3530 result = GPOINTER_TO_INT(imap_cmd_fetch_thread(data));
3536 static gint imap_cmd_append(IMAPSession *session, const gchar *destfolder,
3537 const gchar *file, IMAPFlags flags,
3544 unsigned int new_uid_;
3546 gchar buf[BUFFSIZE];
3551 g_return_val_if_fail(file != NULL, IMAP_ERROR);
3553 size = get_file_size_as_crlf(file);
3554 if ((fp = fopen(file, "rb")) == NULL) {
3555 FILE_OP_ERROR(file, "fopen");
3558 QUOTE_IF_REQUIRED(destfolder_, destfolder);
3559 flag_str = imap_get_flag_str(flags);
3560 imap_gen_send(session, "APPEND %s (%s) {%d}",
3561 destfolder_, flag_str, size);
3564 ok = imap_gen_recv(session, &ret);
3565 if (ok != IMAP_SUCCESS || ret[0] != '+' || ret[1] != ' ') {
3566 log_warning(_("can't append %s to %s\n"), file, destfolder_);
3573 log_print("IMAP4> %s\n", "(sending file...)");
3575 while (fgets(buf, sizeof(buf), fp) != NULL) {
3577 if (sock_puts(SESSION(session)->sock, buf) < 0) {
3584 FILE_OP_ERROR(file, "fgets");
3589 sock_puts(SESSION(session)->sock, "");
3593 if (new_uid != NULL)
3596 if (new_uid != NULL && session->uidplus) {
3597 argbuf = g_ptr_array_new();
3599 ok = imap_cmd_ok(session, argbuf);
3600 if ((ok == IMAP_SUCCESS) && (argbuf->len > 0)) {
3601 resp_str = g_ptr_array_index(argbuf, argbuf->len - 1);
3603 sscanf(resp_str, "%*u OK [APPENDUID %*u %u]",
3605 *new_uid = new_uid_;
3609 ptr_array_free_strings(argbuf);
3610 g_ptr_array_free(argbuf, TRUE);
3612 ok = imap_cmd_ok(session, NULL);
3614 if (ok != IMAP_SUCCESS)
3615 log_warning(_("can't append message to %s\n"),
3621 static MsgNumberList *imapset_to_numlist(IMAPSet imapset)
3623 gchar **ranges, **range;
3624 unsigned int low, high;
3625 MsgNumberList *uids = NULL;
3627 ranges = g_strsplit(imapset, ",", 0);
3628 for (range = ranges; *range != NULL; range++) {
3629 if (sscanf(*range, "%u:%u", &low, &high) == 1)
3630 uids = g_slist_prepend(uids, GINT_TO_POINTER(low));
3633 for (i = low; i <= high; i++)
3634 uids = g_slist_prepend(uids, GINT_TO_POINTER(i));
3637 uids = g_slist_reverse(uids);
3643 static gint imap_cmd_copy(IMAPSession *session, const gchar *seq_set,
3644 const gchar *destfolder, GRelation *uid_mapping)
3649 g_return_val_if_fail(session != NULL, IMAP_ERROR);
3650 g_return_val_if_fail(seq_set != NULL, IMAP_ERROR);
3651 g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
3653 QUOTE_IF_REQUIRED(destfolder_, destfolder);
3654 imap_gen_send(session, "UID COPY %s %s", seq_set, destfolder_);
3656 if (uid_mapping != NULL && session->uidplus) {
3658 gchar *resp_str = NULL, *olduids_str, *newuids_str;
3659 MsgNumberList *olduids, *old_cur, *newuids, *new_cur;
3661 reply = g_ptr_array_new();
3662 ok = imap_cmd_ok(session, reply);
3663 if ((ok == IMAP_SUCCESS) && (reply->len > 0)) {
3664 resp_str = g_ptr_array_index(reply, reply->len - 1);
3666 olduids_str = g_new0(gchar, strlen(resp_str));
3667 newuids_str = g_new0(gchar, strlen(resp_str));
3668 if (sscanf(resp_str, "%*s OK [COPYUID %*u %[0-9,:] %[0-9,:]]",
3669 olduids_str, newuids_str) == 2) {
3670 olduids = imapset_to_numlist(olduids_str);
3671 newuids = imapset_to_numlist(newuids_str);
3675 while(old_cur != NULL && new_cur != NULL) {
3676 g_relation_insert(uid_mapping,
3677 GPOINTER_TO_INT(old_cur->data),
3678 GPOINTER_TO_INT(new_cur->data));
3679 old_cur = g_slist_next(old_cur);
3680 new_cur = g_slist_next(new_cur);
3683 g_slist_free(olduids);
3684 g_slist_free(newuids);
3686 g_free(olduids_str);
3687 g_free(newuids_str);
3690 ptr_array_free_strings(reply);
3691 g_ptr_array_free(reply, TRUE);
3693 ok = imap_cmd_ok(session, NULL);
3695 if (ok != IMAP_SUCCESS)
3696 log_warning(_("can't copy %s to %s\n"), seq_set, destfolder_);
3701 gint imap_cmd_envelope(IMAPSession *session, IMAPSet set)
3703 static gchar *header_fields =
3704 "Date From To Cc Subject Message-ID References In-Reply-To" ;
3707 (session, "UID FETCH %s (UID FLAGS RFC822.SIZE BODY.PEEK[HEADER.FIELDS (%s)])",
3708 set, header_fields);
3710 return IMAP_SUCCESS;
3713 static gint imap_cmd_store(IMAPSession *session, IMAPSet seq_set,
3718 imap_gen_send(session, "UID STORE %s %s", seq_set, sub_cmd);
3720 if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS) {
3721 log_warning(_("error while imap command: STORE %s %s\n"),
3726 return IMAP_SUCCESS;
3729 typedef struct _expunge_data {
3730 IMAPSession *session;
3735 static void *imap_cmd_expunge_thread(void *data)
3737 expunge_data *stuff = (expunge_data *)data;
3738 IMAPSession *session = stuff->session;
3739 IMAPSet seq_set = stuff->seq_set;
3743 if (seq_set && session->uidplus)
3744 imap_gen_send(session, "UID EXPUNGE %s", seq_set);
3746 imap_gen_send(session, "EXPUNGE");
3747 if ((ok = imap_cmd_ok_with_block(session, NULL, TRUE)) != IMAP_SUCCESS) {
3748 log_warning(_("error while imap command: EXPUNGE\n"));
3750 return GINT_TO_POINTER(ok);
3754 return GINT_TO_POINTER(IMAP_SUCCESS);
3757 static gint imap_cmd_expunge(IMAPSession *session, IMAPSet seq_set)
3759 expunge_data *data = g_new0(expunge_data, 1);
3766 data->session = session;
3767 data->seq_set = seq_set;
3769 if (prefs_common.work_offline && !imap_gtk_should_override()) {
3774 #if (defined USE_PTHREAD && defined __GLIBC__ && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 3)))
3775 if (pthread_create(&pt, PTHREAD_CREATE_JOINABLE,
3776 imap_cmd_expunge_thread, data) != 0) {
3777 result = GPOINTER_TO_INT(imap_cmd_expunge_thread(data));
3781 debug_print("+++waiting for imap_cmd_expunge_thread...\n");
3782 while(!data->done) {
3783 /* don't let the interface freeze while waiting */
3786 debug_print("---imap_cmd_expunge_thread done\n");
3788 /* get the thread's return value and clean its resources */
3789 pthread_join(pt, &tmp);
3790 result = GPOINTER_TO_INT(tmp);
3792 result = GPOINTER_TO_INT(imap_cmd_expunge_thread(data));
3798 static gint imap_cmd_close(IMAPSession *session)
3802 imap_gen_send(session, "CLOSE");
3803 if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS)
3804 log_warning(_("error while imap command: CLOSE\n"));
3809 static gint imap_cmd_ok_with_block(IMAPSession *session, GPtrArray *argbuf, gboolean block)
3811 gint ok = IMAP_SUCCESS;
3816 while ((ok = imap_gen_recv_with_block(session, &buf, block))
3818 /* make sure data is long enough for any substring of buf */
3819 data = alloca(strlen(buf) + 1);
3821 /* untagged line read */
3822 if (buf[0] == '*' && buf[1] == ' ') {
3825 g_ptr_array_add(argbuf, g_strdup(buf + 2));
3827 if (sscanf(buf + 2, "%d %s", &num, data) >= 2) {
3828 if (!strcmp(data, "EXISTS")) {
3829 session->exists = num;
3830 session->folder_content_changed = TRUE;
3833 if(!strcmp(data, "EXPUNGE")) {
3835 session->folder_content_changed = TRUE;
3838 /* tagged line with correct tag and OK response found */
3839 } else if ((sscanf(buf, "%d %s", &cmd_num, data) >= 2) &&
3840 (cmd_num == session->cmd_count) &&
3841 !strcmp(data, "OK")) {
3843 g_ptr_array_add(argbuf, g_strdup(buf));
3845 /* everything else */
3856 static gint imap_cmd_ok(IMAPSession *session, GPtrArray *argbuf)
3858 return imap_cmd_ok_with_block(session, argbuf, FALSE);
3860 static gint imap_cmd_ok_block(IMAPSession *session, GPtrArray *argbuf)
3862 return imap_cmd_ok_with_block(session, argbuf, TRUE);
3864 static void imap_gen_send(IMAPSession *session, const gchar *format, ...)
3871 va_start(args, format);
3872 tmp = g_strdup_vprintf(format, args);
3875 session->cmd_count++;
3877 buf = g_strdup_printf("%d %s\r\n", session->cmd_count, tmp);
3878 if (!g_ascii_strncasecmp(tmp, "LOGIN ", 6) && (p = strchr(tmp + 6, ' '))) {
3880 log_print("IMAP4> %d %s ********\n", session->cmd_count, tmp);
3882 log_print("IMAP4> %d %s\n", session->cmd_count, tmp);
3884 sock_write_all(SESSION(session)->sock, buf, strlen(buf));
3889 static gint imap_gen_recv_with_block(IMAPSession *session, gchar **ret, gboolean block)
3892 if ((*ret = imap_getline(SESSION(session)->sock)) == NULL)
3895 if ((*ret = sock_getline(SESSION(session)->sock)) == NULL)
3900 log_print("IMAP4< %s\n", *ret);
3902 session_set_access_time(SESSION(session));
3904 return IMAP_SUCCESS;
3907 static gint imap_gen_recv_block(IMAPSession *session, gchar **ret)
3909 return imap_gen_recv_with_block(session, ret, TRUE);
3912 static gint imap_gen_recv(IMAPSession *session, gchar **ret)
3914 return imap_gen_recv_with_block(session, ret, FALSE);
3916 /* misc utility functions */
3918 static gchar *strchr_cpy(const gchar *src, gchar ch, gchar *dest, gint len)
3923 tmp = strchr(src, ch);
3927 memcpy(dest, src, MIN(tmp - src, len - 1));
3928 dest[MIN(tmp - src, len - 1)] = '\0';
3933 static gchar *get_quoted(const gchar *src, gchar ch, gchar *dest, gint len)
3935 const gchar *p = src;
3938 g_return_val_if_fail(*p == ch, NULL);
3943 while (*p != '\0' && *p != ch) {
3945 if (*p == '\\' && *(p + 1) != '\0')
3954 return (gchar *)(*p == ch ? p + 1 : p);
3957 static gchar *search_array_contain_str(GPtrArray *array, const gchar *str)
3961 for (i = 0; i < array->len; i++) {
3964 tmp = g_ptr_array_index(array, i);
3965 if (strstr(tmp, str) != NULL)
3972 static gchar *search_array_str(GPtrArray *array, const gchar *str)
3979 for (i = 0; i < array->len; i++) {
3982 tmp = g_ptr_array_index(array, i);
3983 if (!strncmp(tmp, str, len))
3990 static void imap_path_separator_subst(gchar *str, gchar separator)
3993 gboolean in_escape = FALSE;
3995 if (!separator || separator == '/') return;
3997 for (p = str; *p != '\0'; p++) {
3998 if (*p == '/' && !in_escape)
4000 else if (*p == '&' && *(p + 1) != '-' && !in_escape)
4002 else if (*p == '-' && in_escape)
4007 static gchar *imap_modified_utf7_to_utf8(const gchar *mutf7_str)
4009 static iconv_t cd = (iconv_t)-1;
4010 static gboolean iconv_ok = TRUE;
4013 size_t norm_utf7_len;
4015 gchar *to_str, *to_p;
4017 gboolean in_escape = FALSE;
4019 if (!iconv_ok) return g_strdup(mutf7_str);
4021 if (cd == (iconv_t)-1) {
4022 cd = iconv_open(CS_INTERNAL, CS_UTF_7);
4023 if (cd == (iconv_t)-1) {
4024 g_warning("iconv cannot convert UTF-7 to %s\n",
4027 return g_strdup(mutf7_str);
4031 /* modified UTF-7 to normal UTF-7 conversion */
4032 norm_utf7 = g_string_new(NULL);
4034 for (p = mutf7_str; *p != '\0'; p++) {
4035 /* replace: '&' -> '+',
4037 escaped ',' -> '/' */
4038 if (!in_escape && *p == '&') {
4039 if (*(p + 1) != '-') {
4040 g_string_append_c(norm_utf7, '+');
4043 g_string_append_c(norm_utf7, '&');
4046 } else if (in_escape && *p == ',') {
4047 g_string_append_c(norm_utf7, '/');
4048 } else if (in_escape && *p == '-') {
4049 g_string_append_c(norm_utf7, '-');
4052 g_string_append_c(norm_utf7, *p);
4056 norm_utf7_p = norm_utf7->str;
4057 norm_utf7_len = norm_utf7->len;
4058 to_len = strlen(mutf7_str) * 5;
4059 to_p = to_str = g_malloc(to_len + 1);
4061 if (iconv(cd, (ICONV_CONST gchar **)&norm_utf7_p, &norm_utf7_len,
4062 &to_p, &to_len) == -1) {
4063 g_warning(_("iconv cannot convert UTF-7 to %s\n"),
4064 conv_get_locale_charset_str());
4065 g_string_free(norm_utf7, TRUE);
4067 return g_strdup(mutf7_str);
4070 /* second iconv() call for flushing */
4071 iconv(cd, NULL, NULL, &to_p, &to_len);
4072 g_string_free(norm_utf7, TRUE);
4078 static gchar *imap_utf8_to_modified_utf7(const gchar *from)
4080 static iconv_t cd = (iconv_t)-1;
4081 static gboolean iconv_ok = TRUE;
4082 gchar *norm_utf7, *norm_utf7_p;
4083 size_t from_len, norm_utf7_len;
4085 gchar *from_tmp, *to, *p;
4086 gboolean in_escape = FALSE;
4088 if (!iconv_ok) return g_strdup(from);
4090 if (cd == (iconv_t)-1) {
4091 cd = iconv_open(CS_UTF_7, CS_INTERNAL);
4092 if (cd == (iconv_t)-1) {
4093 g_warning(_("iconv cannot convert %s to UTF-7\n"),
4096 return g_strdup(from);
4100 /* UTF-8 to normal UTF-7 conversion */
4101 Xstrdup_a(from_tmp, from, return g_strdup(from));
4102 from_len = strlen(from);
4103 norm_utf7_len = from_len * 5;
4104 Xalloca(norm_utf7, norm_utf7_len + 1, return g_strdup(from));
4105 norm_utf7_p = norm_utf7;
4107 #define IS_PRINT(ch) (isprint(ch) && IS_ASCII(ch))
4109 while (from_len > 0) {
4110 if (*from_tmp == '+') {
4111 *norm_utf7_p++ = '+';
4112 *norm_utf7_p++ = '-';
4116 } else if (IS_PRINT(*(guchar *)from_tmp)) {
4117 /* printable ascii char */
4118 *norm_utf7_p = *from_tmp;
4124 size_t conv_len = 0;
4126 /* unprintable char: convert to UTF-7 */
4128 while (!IS_PRINT(*(guchar *)p) && conv_len < from_len) {
4129 conv_len += g_utf8_skip[*(guchar *)p];
4130 p += g_utf8_skip[*(guchar *)p];
4133 from_len -= conv_len;
4134 if (iconv(cd, (ICONV_CONST gchar **)&from_tmp,
4136 &norm_utf7_p, &norm_utf7_len) == -1) {
4137 g_warning(_("iconv cannot convert UTF-8 to UTF-7\n"));
4138 return g_strdup(from);
4141 /* second iconv() call for flushing */
4142 iconv(cd, NULL, NULL, &norm_utf7_p, &norm_utf7_len);
4148 *norm_utf7_p = '\0';
4149 to_str = g_string_new(NULL);
4150 for (p = norm_utf7; p < norm_utf7_p; p++) {
4151 /* replace: '&' -> "&-",
4154 BASE64 '/' -> ',' */
4155 if (!in_escape && *p == '&') {
4156 g_string_append(to_str, "&-");
4157 } else if (!in_escape && *p == '+') {
4158 if (*(p + 1) == '-') {
4159 g_string_append_c(to_str, '+');
4162 g_string_append_c(to_str, '&');
4165 } else if (in_escape && *p == '/') {
4166 g_string_append_c(to_str, ',');
4167 } else if (in_escape && *p == '-') {
4168 g_string_append_c(to_str, '-');
4171 g_string_append_c(to_str, *p);
4177 g_string_append_c(to_str, '-');
4181 g_string_free(to_str, FALSE);
4186 static GSList *imap_get_seq_set_from_numlist(MsgNumberList *numlist)
4189 GSList *sorted_list, *cur;
4190 guint first, last, next;
4192 GSList *ret_list = NULL;
4194 if (numlist == NULL)
4197 str = g_string_sized_new(256);
4199 sorted_list = g_slist_copy(numlist);
4200 sorted_list = g_slist_sort(sorted_list, g_int_compare);
4202 first = GPOINTER_TO_INT(sorted_list->data);
4204 for (cur = sorted_list; cur != NULL; cur = g_slist_next(cur)) {
4205 if (GPOINTER_TO_INT(cur->data) == 0)
4208 last = GPOINTER_TO_INT(cur->data);
4210 next = GPOINTER_TO_INT(cur->next->data);
4214 if (last + 1 != next || next == 0) {
4216 g_string_append_c(str, ',');
4218 g_string_append_printf(str, "%u", first);
4220 g_string_append_printf(str, "%u:%u", first, last);
4224 if (str->len > IMAP_CMD_LIMIT) {
4225 ret_str = g_strdup(str->str);
4226 ret_list = g_slist_append(ret_list, ret_str);
4227 g_string_truncate(str, 0);
4233 ret_str = g_strdup(str->str);
4234 ret_list = g_slist_append(ret_list, ret_str);
4237 g_slist_free(sorted_list);
4238 g_string_free(str, TRUE);
4243 static GSList *imap_get_seq_set_from_msglist(MsgInfoList *msglist)
4245 MsgNumberList *numlist = NULL;
4249 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
4250 MsgInfo *msginfo = (MsgInfo *) cur->data;
4252 numlist = g_slist_append(numlist, GINT_TO_POINTER(msginfo->msgnum));
4254 seq_list = imap_get_seq_set_from_numlist(numlist);
4255 g_slist_free(numlist);
4260 static void imap_seq_set_free(GSList *seq_list)
4262 slist_free_strings(seq_list);
4263 g_slist_free(seq_list);
4267 static gboolean imap_rename_folder_func(GNode *node, gpointer data)
4269 FolderItem *item = node->data;
4270 gchar **paths = data;
4271 const gchar *oldpath = paths[0];
4272 const gchar *newpath = paths[1];
4274 gchar *new_itempath;
4277 oldpathlen = strlen(oldpath);
4278 if (strncmp(oldpath, item->path, oldpathlen) != 0) {
4279 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
4283 base = item->path + oldpathlen;
4284 while (*base == G_DIR_SEPARATOR) base++;
4286 new_itempath = g_strdup(newpath);
4288 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
4291 item->path = new_itempath;
4296 typedef struct _get_list_uid_data {
4298 IMAPFolderItem *item;
4299 GSList **msgnum_list;
4301 } get_list_uid_data;
4303 static void *get_list_of_uids_thread(void *data)
4305 get_list_uid_data *stuff = (get_list_uid_data *)data;
4306 Folder *folder = stuff->folder;
4307 IMAPFolderItem *item = stuff->item;
4308 GSList **msgnum_list = stuff->msgnum_list;
4309 gint ok, nummsgs = 0, lastuid_old;
4310 IMAPSession *session;
4311 GSList *uidlist, *elem;
4314 session = imap_session_get(folder);
4315 if (session == NULL) {
4317 return GINT_TO_POINTER(-1);
4320 ok = imap_select(session, IMAP_FOLDER(folder), item->item.path,
4321 NULL, NULL, NULL, NULL, TRUE);
4322 if (ok != IMAP_SUCCESS) {
4324 return GINT_TO_POINTER(-1);
4327 cmd_buf = g_strdup_printf("UID %d:*", item->lastuid + 1);
4328 ok = imap_cmd_search(session, cmd_buf, &uidlist, TRUE);
4331 if (ok == IMAP_SOCKET) {
4332 session_destroy((Session *)session);
4333 ((RemoteFolder *)folder)->session = NULL;
4335 return GINT_TO_POINTER(-1);
4338 if (ok != IMAP_SUCCESS) {
4342 argbuf = g_ptr_array_new();
4344 cmd_buf = g_strdup_printf("UID FETCH %d:* (UID)", item->lastuid + 1);
4345 imap_gen_send(session, cmd_buf);
4347 ok = imap_cmd_ok_block(session, argbuf);
4348 if (ok != IMAP_SUCCESS) {
4349 ptr_array_free_strings(argbuf);
4350 g_ptr_array_free(argbuf, TRUE);
4352 return GINT_TO_POINTER(-1);
4355 for(i = 0; i < argbuf->len; i++) {
4358 if((ret = sscanf(g_ptr_array_index(argbuf, i),
4359 "%*d FETCH (UID %d)", &msgnum)) == 1)
4360 uidlist = g_slist_prepend(uidlist, GINT_TO_POINTER(msgnum));
4362 ptr_array_free_strings(argbuf);
4363 g_ptr_array_free(argbuf, TRUE);
4366 lastuid_old = item->lastuid;
4367 *msgnum_list = g_slist_copy(item->uid_list);
4368 nummsgs = g_slist_length(*msgnum_list);
4369 debug_print("Got %d uids from cache\n", g_slist_length(item->uid_list));
4371 for (elem = uidlist; elem != NULL; elem = g_slist_next(elem)) {
4374 msgnum = GPOINTER_TO_INT(elem->data);
4375 if (msgnum > lastuid_old) {
4376 *msgnum_list = g_slist_prepend(*msgnum_list, GINT_TO_POINTER(msgnum));
4377 item->uid_list = g_slist_prepend(item->uid_list, GINT_TO_POINTER(msgnum));
4380 if(msgnum > item->lastuid)
4381 item->lastuid = msgnum;
4384 g_slist_free(uidlist);
4387 return GINT_TO_POINTER(nummsgs);
4390 static gint get_list_of_uids(Folder *folder, IMAPFolderItem *item, GSList **msgnum_list)
4393 get_list_uid_data *data = g_new0(get_list_uid_data, 1);
4399 data->folder = folder;
4401 data->msgnum_list = msgnum_list;
4403 if (prefs_common.work_offline && !imap_gtk_should_override()) {
4408 #if (defined USE_PTHREAD && defined __GLIBC__ && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 3)))
4409 if (pthread_create(&pt, PTHREAD_CREATE_JOINABLE,
4410 get_list_of_uids_thread, data) != 0) {
4411 result = GPOINTER_TO_INT(get_list_of_uids_thread(data));
4415 debug_print("+++waiting for get_list_of_uids_thread...\n");
4416 while(!data->done) {
4417 /* don't let the interface freeze while waiting */
4420 debug_print("---get_list_of_uids_thread done\n");
4422 /* get the thread's return value and clean its resources */
4423 pthread_join(pt, &tmp);
4424 result = GPOINTER_TO_INT(tmp);
4426 result = GPOINTER_TO_INT(get_list_of_uids_thread(data));
4433 gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list, gboolean *old_uids_valid)
4435 IMAPFolderItem *item = (IMAPFolderItem *)_item;
4436 IMAPSession *session;
4437 gint ok, nummsgs = 0, exists, recent, uid_val, uid_next, unseen;
4438 GSList *uidlist = NULL;
4440 gboolean selected_folder;
4442 g_return_val_if_fail(folder != NULL, -1);
4443 g_return_val_if_fail(item != NULL, -1);
4444 g_return_val_if_fail(item->item.path != NULL, -1);
4445 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
4446 g_return_val_if_fail(folder->account != NULL, -1);
4448 MUTEX_TRYLOCK_OR_RETURN_VAL(-1);
4450 session = imap_session_get(folder);
4451 g_return_val_if_fail(session != NULL, -1);
4453 selected_folder = (session->mbox != NULL) &&
4454 (!strcmp(session->mbox, item->item.path));
4455 if (selected_folder) {
4456 ok = imap_cmd_noop(session);
4457 if (ok != IMAP_SUCCESS) {
4461 exists = session->exists;
4463 *old_uids_valid = TRUE;
4465 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
4466 &exists, &recent, &uid_next, &uid_val, &unseen, FALSE);
4467 if (ok != IMAP_SUCCESS) {
4471 if(item->item.mtime == uid_val)
4472 *old_uids_valid = TRUE;
4474 *old_uids_valid = FALSE;
4476 debug_print("Freeing imap uid cache\n");
4478 g_slist_free(item->uid_list);
4479 item->uid_list = NULL;
4481 item->item.mtime = uid_val;
4483 imap_delete_all_cached_messages((FolderItem *)item);
4487 if (!selected_folder)
4488 item->uid_next = uid_next;
4490 /* If old uid_next matches new uid_next we can be sure no message
4491 was added to the folder */
4492 if (( selected_folder && !session->folder_content_changed) ||
4493 (!selected_folder && uid_next == item->uid_next)) {
4494 nummsgs = g_slist_length(item->uid_list);
4496 /* If number of messages is still the same we
4497 know our caches message numbers are still valid,
4498 otherwise if the number of messages has decrease
4499 we discard our cache to start a new scan to find
4500 out which numbers have been removed */
4501 if (exists == nummsgs) {
4502 *msgnum_list = g_slist_copy(item->uid_list);
4505 } else if (exists < nummsgs) {
4506 debug_print("Freeing imap uid cache");
4508 g_slist_free(item->uid_list);
4509 item->uid_list = NULL;
4514 *msgnum_list = NULL;
4519 nummsgs = get_list_of_uids(folder, item, &uidlist);
4526 if (nummsgs != exists) {
4527 /* Cache contains more messages then folder, we have cached
4528 an old UID of a message that was removed and new messages
4529 have been added too, otherwise the uid_next check would
4531 debug_print("Freeing imap uid cache");
4533 g_slist_free(item->uid_list);
4534 item->uid_list = NULL;
4536 g_slist_free(*msgnum_list);
4538 nummsgs = get_list_of_uids(folder, item, &uidlist);
4541 *msgnum_list = uidlist;
4543 dir = folder_item_get_path((FolderItem *)item);
4544 debug_print("removing old messages from %s\n", dir);
4545 remove_numbered_files_not_in_list(dir, *msgnum_list);
4552 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item)
4557 flags.perm_flags = MSG_NEW|MSG_UNREAD;
4558 flags.tmp_flags = 0;
4560 g_return_val_if_fail(item != NULL, NULL);
4561 g_return_val_if_fail(file != NULL, NULL);
4563 if (item->stype == F_QUEUE) {
4564 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
4565 } else if (item->stype == F_DRAFT) {
4566 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
4569 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
4570 if (!msginfo) return NULL;
4572 msginfo->plaintext_file = g_strdup(file);
4573 msginfo->folder = item;
4578 GSList *imap_get_msginfos(Folder *folder, FolderItem *item, GSList *msgnum_list)
4580 IMAPSession *session;
4581 MsgInfoList *ret = NULL;
4584 g_return_val_if_fail(folder != NULL, NULL);
4585 g_return_val_if_fail(item != NULL, NULL);
4586 g_return_val_if_fail(msgnum_list != NULL, NULL);
4588 session = imap_session_get(folder);
4589 g_return_val_if_fail(session != NULL, NULL);
4591 debug_print("IMAP getting msginfos\n");
4592 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
4593 NULL, NULL, NULL, NULL, FALSE);
4594 if (ok != IMAP_SUCCESS)
4597 if (!(item->stype == F_QUEUE || item->stype == F_DRAFT)) {
4598 ret = g_slist_concat(ret,
4599 imap_get_uncached_messages(
4600 session, item, msgnum_list));
4602 MsgNumberList *sorted_list, *elem;
4603 gint startnum, lastnum;
4605 sorted_list = g_slist_sort(g_slist_copy(msgnum_list), g_int_compare);
4607 startnum = lastnum = GPOINTER_TO_INT(sorted_list->data);
4609 for (elem = sorted_list;; elem = g_slist_next(elem)) {
4613 num = GPOINTER_TO_INT(elem->data);
4615 if (num > lastnum + 1 || elem == NULL) {
4617 for (i = startnum; i <= lastnum; ++i) {
4620 file = imap_fetch_msg(folder, item, i);
4622 MsgInfo *msginfo = imap_parse_msg(file, item);
4623 if (msginfo != NULL) {
4624 msginfo->msgnum = i;
4625 ret = g_slist_append(ret, msginfo);
4639 g_slist_free(sorted_list);
4645 MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid)
4647 MsgInfo *msginfo = NULL;
4648 MsgInfoList *msginfolist;
4649 MsgNumberList numlist;
4651 numlist.next = NULL;
4652 numlist.data = GINT_TO_POINTER(uid);
4654 msginfolist = imap_get_msginfos(folder, item, &numlist);
4655 if (msginfolist != NULL) {
4656 msginfo = msginfolist->data;
4657 g_slist_free(msginfolist);
4663 gboolean imap_scan_required(Folder *folder, FolderItem *_item)
4665 IMAPSession *session;
4666 IMAPFolderItem *item = (IMAPFolderItem *)_item;
4667 gint ok, exists = 0, recent = 0, unseen = 0;
4668 guint32 uid_next, uid_val = 0;
4669 gboolean selected_folder;
4671 g_return_val_if_fail(folder != NULL, FALSE);
4672 g_return_val_if_fail(item != NULL, FALSE);
4673 g_return_val_if_fail(item->item.folder != NULL, FALSE);
4674 g_return_val_if_fail(FOLDER_CLASS(item->item.folder) == &imap_class, FALSE);
4676 if (item->item.path == NULL)
4679 session = imap_session_get(folder);
4680 g_return_val_if_fail(session != NULL, FALSE);
4682 selected_folder = (session->mbox != NULL) &&
4683 (!strcmp(session->mbox, item->item.path));
4684 if (selected_folder) {
4685 ok = imap_cmd_noop(session);
4686 if (ok != IMAP_SUCCESS)
4689 if (session->folder_content_changed)
4692 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
4693 &exists, &recent, &uid_next, &uid_val, &unseen, FALSE);
4694 if (ok != IMAP_SUCCESS)
4697 if ((uid_next != item->uid_next) || (exists != item->item.total_msgs))
4704 void imap_change_flags(Folder *folder, FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
4706 IMAPSession *session;
4707 IMAPFlags flags_set = 0, flags_unset = 0;
4708 gint ok = IMAP_SUCCESS;
4709 MsgNumberList numlist;
4710 hashtable_data *ht_data = NULL;
4712 g_return_if_fail(folder != NULL);
4713 g_return_if_fail(folder->klass == &imap_class);
4714 g_return_if_fail(item != NULL);
4715 g_return_if_fail(item->folder == folder);
4716 g_return_if_fail(msginfo != NULL);
4717 g_return_if_fail(msginfo->folder == item);
4719 MUTEX_TRYLOCK_OR_RETURN();
4721 session = imap_session_get(folder);
4726 if ((ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
4727 NULL, NULL, NULL, NULL, FALSE)) != IMAP_SUCCESS) {
4732 if (!MSG_IS_MARKED(msginfo->flags) && (newflags & MSG_MARKED))
4733 flags_set |= IMAP_FLAG_FLAGGED;
4734 if ( MSG_IS_MARKED(msginfo->flags) && !(newflags & MSG_MARKED))
4735 flags_unset |= IMAP_FLAG_FLAGGED;
4737 if (!MSG_IS_UNREAD(msginfo->flags) && (newflags & MSG_UNREAD))
4738 flags_unset |= IMAP_FLAG_SEEN;
4739 if ( MSG_IS_UNREAD(msginfo->flags) && !(newflags & MSG_UNREAD))
4740 flags_set |= IMAP_FLAG_SEEN;
4742 if (!MSG_IS_REPLIED(msginfo->flags) && (newflags & MSG_REPLIED))
4743 flags_set |= IMAP_FLAG_ANSWERED;
4744 if ( MSG_IS_REPLIED(msginfo->flags) && !(newflags & MSG_REPLIED))
4745 flags_set |= IMAP_FLAG_ANSWERED;
4747 numlist.next = NULL;
4748 numlist.data = GINT_TO_POINTER(msginfo->msgnum);
4750 if (IMAP_FOLDER_ITEM(item)->batching) {
4751 /* instead of performing an UID STORE command for each message change,
4752 * as a lot of them can change "together", we just fill in hashtables
4753 * and defer the treatment so that we're able to send only one
4756 debug_print("IMAP batch mode on, deferring flags change\n");
4758 ht_data = g_hash_table_lookup(flags_set_table, GINT_TO_POINTER(flags_set));
4759 if (ht_data == NULL) {
4760 ht_data = g_new0(hashtable_data, 1);
4761 ht_data->session = session;
4762 g_hash_table_insert(flags_set_table, GINT_TO_POINTER(flags_set), ht_data);
4764 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
4765 ht_data->msglist = g_slist_prepend(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum));
4768 ht_data = g_hash_table_lookup(flags_unset_table, GINT_TO_POINTER(flags_unset));
4769 if (ht_data == NULL) {
4770 ht_data = g_new0(hashtable_data, 1);
4771 ht_data->session = session;
4772 g_hash_table_insert(flags_unset_table, GINT_TO_POINTER(flags_unset), ht_data);
4774 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
4775 ht_data->msglist = g_slist_prepend(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum));
4778 debug_print("IMAP changing flags\n");
4780 ok = imap_set_message_flags(session, &numlist, flags_set, TRUE);
4781 if (ok != IMAP_SUCCESS) {
4788 ok = imap_set_message_flags(session, &numlist, flags_unset, FALSE);
4789 if (ok != IMAP_SUCCESS) {
4795 msginfo->flags.perm_flags = newflags;
4801 static gint imap_remove_msg(Folder *folder, FolderItem *item, gint uid)
4804 IMAPSession *session;
4806 MsgNumberList numlist;
4808 g_return_val_if_fail(folder != NULL, -1);
4809 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
4810 g_return_val_if_fail(item != NULL, -1);
4812 session = imap_session_get(folder);
4813 if (!session) return -1;
4815 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
4816 NULL, NULL, NULL, NULL, FALSE);
4817 if (ok != IMAP_SUCCESS)
4820 numlist.next = NULL;
4821 numlist.data = GINT_TO_POINTER(uid);
4823 ok = imap_set_message_flags
4824 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
4825 &numlist, IMAP_FLAG_DELETED, TRUE);
4826 if (ok != IMAP_SUCCESS) {
4827 log_warning(_("can't set deleted flags: %d\n"), uid);
4831 if (!session->uidplus) {
4832 ok = imap_cmd_expunge(session, NULL);
4836 uidstr = g_strdup_printf("%u", uid);
4837 ok = imap_cmd_expunge(session, uidstr);
4840 if (ok != IMAP_SUCCESS) {
4841 log_warning(_("can't expunge\n"));
4845 IMAP_FOLDER_ITEM(item)->uid_list = g_slist_remove(
4846 IMAP_FOLDER_ITEM(item)->uid_list, numlist.data);
4847 dir = folder_item_get_path(item);
4848 if (is_dir_exist(dir))
4849 remove_numbered_files(dir, uid, uid);
4852 return IMAP_SUCCESS;
4855 static gint compare_msginfo(gconstpointer a, gconstpointer b)
4857 return ((MsgInfo *)a)->msgnum - ((MsgInfo *)b)->msgnum;
4860 static guint gslist_find_next_num(MsgNumberList **list, guint num)
4864 g_return_val_if_fail(list != NULL, -1);
4866 for (elem = *list; elem != NULL; elem = g_slist_next(elem))
4867 if (GPOINTER_TO_INT(elem->data) >= num)
4870 return elem != NULL ? GPOINTER_TO_INT(elem->data) : (gint)-1;
4874 * NEW and DELETED flags are not syncronized
4875 * - The NEW/RECENT flags in IMAP folders can not really be directly
4876 * modified by Sylpheed
4877 * - The DELETE/DELETED flag in IMAP and Sylpheed don't have the same
4878 * meaning, in IMAP it always removes the messages from the FolderItem
4879 * in Sylpheed it can mean to move the message to trash
4882 typedef struct _get_flags_data {
4885 MsgInfoList *msginfo_list;
4886 GRelation *msgflags;
4890 static /*gint*/ void *imap_get_flags_thread(void *data)
4892 get_flags_data *stuff = (get_flags_data *)data;
4893 Folder *folder = stuff->folder;
4894 FolderItem *item = stuff->item;
4895 MsgInfoList *msginfo_list = stuff->msginfo_list;
4896 GRelation *msgflags = stuff->msgflags;
4897 IMAPSession *session;
4898 GSList *sorted_list;
4899 GSList *unseen = NULL, *answered = NULL, *flagged = NULL;
4900 GSList *p_unseen, *p_answered, *p_flagged;
4902 GSList *seq_list, *cur;
4903 gboolean reverse_seen = FALSE;
4906 gint exists_cnt, recent_cnt, unseen_cnt, uid_next;
4907 guint32 uidvalidity;
4908 gboolean selected_folder;
4910 if (folder == NULL || item == NULL) {
4912 return GINT_TO_POINTER(-1);
4914 if (msginfo_list == NULL) {
4916 return GINT_TO_POINTER(0);
4919 session = imap_session_get(folder);
4920 if (session == NULL) {
4922 return GINT_TO_POINTER(-1);
4925 selected_folder = (session->mbox != NULL) &&
4926 (!strcmp(session->mbox, item->path));
4928 if (!selected_folder) {
4929 ok = imap_status(session, IMAP_FOLDER(folder), item->path,
4930 &exists_cnt, &recent_cnt, &uid_next, &uidvalidity, &unseen_cnt, TRUE);
4931 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
4932 NULL, NULL, NULL, NULL, TRUE);
4933 if (ok != IMAP_SUCCESS) {
4935 return GINT_TO_POINTER(-1);
4940 if (unseen_cnt > exists_cnt / 2)
4941 reverse_seen = TRUE;
4943 cmd_buf = g_string_new(NULL);
4945 sorted_list = g_slist_sort(g_slist_copy(msginfo_list), compare_msginfo);
4947 seq_list = imap_get_seq_set_from_msglist(msginfo_list);
4949 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
4950 IMAPSet imapset = cur->data;
4952 g_string_sprintf(cmd_buf, "%sSEEN UID %s", reverse_seen ? "" : "UN", imapset);
4953 imap_cmd_search(session, cmd_buf->str, &p_unseen, TRUE);
4954 unseen = g_slist_concat(unseen, p_unseen);
4956 g_string_sprintf(cmd_buf, "ANSWERED UID %s", imapset);
4957 imap_cmd_search(session, cmd_buf->str, &p_answered, TRUE);
4958 answered = g_slist_concat(answered, p_answered);
4960 g_string_sprintf(cmd_buf, "FLAGGED UID %s", imapset);
4961 imap_cmd_search(session, cmd_buf->str, &p_flagged, TRUE);
4962 flagged = g_slist_concat(flagged, p_flagged);
4966 p_answered = answered;
4967 p_flagged = flagged;
4969 for (elem = sorted_list; elem != NULL; elem = g_slist_next(elem)) {
4974 msginfo = (MsgInfo *) elem->data;
4975 flags = msginfo->flags.perm_flags;
4976 wasnew = (flags & MSG_NEW);
4977 flags &= ~((reverse_seen ? 0 : MSG_UNREAD | MSG_NEW) | MSG_REPLIED | MSG_MARKED);
4979 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
4980 if (gslist_find_next_num(&p_unseen, msginfo->msgnum) == msginfo->msgnum) {
4981 if (!reverse_seen) {
4982 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
4984 flags &= ~(MSG_UNREAD | MSG_NEW);
4987 if (gslist_find_next_num(&p_answered, msginfo->msgnum) == msginfo->msgnum)
4988 flags |= MSG_REPLIED;
4989 if (gslist_find_next_num(&p_flagged, msginfo->msgnum) == msginfo->msgnum)
4990 flags |= MSG_MARKED;
4991 g_relation_insert(msgflags, msginfo, GINT_TO_POINTER(flags));
4994 imap_seq_set_free(seq_list);
4995 g_slist_free(flagged);
4996 g_slist_free(answered);
4997 g_slist_free(unseen);
4998 g_slist_free(sorted_list);
4999 g_string_free(cmd_buf, TRUE);
5002 return GINT_TO_POINTER(0);
5005 static gint imap_get_flags(Folder *folder, FolderItem *item,
5006 MsgInfoList *msginfo_list, GRelation *msgflags)
5009 get_flags_data *data = g_new0(get_flags_data, 1);
5015 data->folder = folder;
5017 data->msginfo_list = msginfo_list;
5018 data->msgflags = msgflags;
5020 if (prefs_common.work_offline && !imap_gtk_should_override()) {
5025 #if (defined USE_PTHREAD && defined __GLIBC__ && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 3)))
5026 MUTEX_TRYLOCK_OR_RETURN_VAL(-1);
5027 if (pthread_create(&pt, PTHREAD_CREATE_JOINABLE,
5028 imap_get_flags_thread, data) != 0) {
5029 result = GPOINTER_TO_INT(imap_get_flags_thread(data));
5034 debug_print("+++waiting for imap_get_flags_thread...\n");
5035 while(!data->done) {
5036 /* don't let the interface freeze while waiting */
5039 debug_print("---imap_get_flags_thread done\n");
5041 /* get the thread's return value and clean its resources */
5042 pthread_join(pt, &tmp);
5043 result = GPOINTER_TO_INT(tmp);
5046 result = GPOINTER_TO_INT(imap_get_flags_thread(data));
5053 static gboolean process_flags(gpointer key, gpointer value, gpointer user_data)
5055 gboolean flags_set = GPOINTER_TO_INT(user_data);
5056 gint flags_value = GPOINTER_TO_INT(key);
5057 hashtable_data *data = (hashtable_data *)value;
5059 data->msglist = g_slist_reverse(data->msglist);
5061 debug_print("IMAP %ssetting flags to %d for %d messages\n",
5064 g_slist_length(data->msglist));
5065 imap_set_message_flags(data->session, data->msglist, flags_value, flags_set);
5067 g_slist_free(data->msglist);
5072 static void process_hashtable(void)
5074 MUTEX_TRYLOCK_OR_RETURN();
5075 if (flags_set_table) {
5076 g_hash_table_foreach_remove(flags_set_table, process_flags, GINT_TO_POINTER(TRUE));
5077 g_free(flags_set_table);
5078 flags_set_table = NULL;
5080 if (flags_unset_table) {
5081 g_hash_table_foreach_remove(flags_unset_table, process_flags, GINT_TO_POINTER(FALSE));
5082 g_free(flags_unset_table);
5083 flags_unset_table = NULL;
5088 static IMAPFolderItem *batching_item = NULL;
5090 static void imap_set_batch (Folder *folder, FolderItem *_item, gboolean batch)
5092 IMAPFolderItem *item = (IMAPFolderItem *)_item;
5094 g_return_if_fail(item != NULL);
5096 if (batch && batching_item != NULL) {
5097 g_warning("already batching on %s\n", batching_item->item.path);
5101 if (item->batching == batch)
5104 item->batching = batch;
5106 batching_item = batch?item:NULL;
5109 debug_print("IMAP switching to batch mode\n");
5110 if (flags_set_table) {
5111 g_warning("flags_set_table non-null but we just entered batch mode!\n");
5112 flags_set_table = NULL;
5114 if (flags_unset_table) {
5115 g_warning("flags_unset_table non-null but we just entered batch mode!\n");
5116 flags_unset_table = NULL;
5118 flags_set_table = g_hash_table_new(NULL, g_direct_equal);
5119 flags_unset_table = g_hash_table_new(NULL, g_direct_equal);
5121 debug_print("IMAP switching away from batch mode\n");
5123 process_hashtable();