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;
70 #define MUTEX_TRYLOCK_OR_RETURN() { \
71 debug_print("%s: locking mutex\n", __FUNCTION__); \
72 if (pthread_mutex_trylock(&imap_mutex) == EBUSY) { \
73 g_warning("can't lock mutex (held by %s)\n", \
74 mutex_hold ? mutex_hold:"(nil)"); \
77 mutex_hold = __FUNCTION__; \
80 #define MUTEX_TRYLOCK_OR_RETURN_VAL(retval) { \
81 debug_print("%s: locking mutex\n", __FUNCTION__); \
82 if (pthread_mutex_trylock(&imap_mutex) == EBUSY) { \
83 g_warning("can't lock mutex (held by %s)\n", \
84 mutex_hold ? mutex_hold:"(nil)"); \
87 mutex_hold = __FUNCTION__; \
90 #define MUTEX_UNLOCK() { \
91 debug_print("%s: unlocking mutex\n", __FUNCTION__);\
92 pthread_mutex_unlock(&imap_mutex); \
97 #define MUTEX_TRYLOCK_OR_RETURN() do {} while(0)
98 #define MUTEX_TRYLOCK_OR_RETURN_VAL(retval) do {} while(0)
99 #define MUTEX_UNLOCK() do {} while(0)
102 typedef struct _IMAPFolder IMAPFolder;
103 typedef struct _IMAPSession IMAPSession;
104 typedef struct _IMAPNameSpace IMAPNameSpace;
105 typedef struct _IMAPFolderItem IMAPFolderItem;
107 #include "prefs_account.h"
109 #define IMAP_FOLDER(obj) ((IMAPFolder *)obj)
110 #define IMAP_FOLDER_ITEM(obj) ((IMAPFolderItem *)obj)
111 #define IMAP_SESSION(obj) ((IMAPSession *)obj)
115 RemoteFolder rfolder;
117 /* list of IMAPNameSpace */
127 gboolean authenticated;
136 gboolean folder_content_changed;
140 struct _IMAPNameSpace
146 #define IMAP_SUCCESS 0
147 #define IMAP_SOCKET 2
148 #define IMAP_AUTHFAIL 3
149 #define IMAP_PROTOCOL 4
150 #define IMAP_SYNTAX 5
154 #define IMAPBUFSIZE 8192
158 IMAP_FLAG_SEEN = 1 << 0,
159 IMAP_FLAG_ANSWERED = 1 << 1,
160 IMAP_FLAG_FLAGGED = 1 << 2,
161 IMAP_FLAG_DELETED = 1 << 3,
162 IMAP_FLAG_DRAFT = 1 << 4
165 #define IMAP_IS_SEEN(flags) ((flags & IMAP_FLAG_SEEN) != 0)
166 #define IMAP_IS_ANSWERED(flags) ((flags & IMAP_FLAG_ANSWERED) != 0)
167 #define IMAP_IS_FLAGGED(flags) ((flags & IMAP_FLAG_FLAGGED) != 0)
168 #define IMAP_IS_DELETED(flags) ((flags & IMAP_FLAG_DELETED) != 0)
169 #define IMAP_IS_DRAFT(flags) ((flags & IMAP_FLAG_DRAFT) != 0)
172 #define IMAP4_PORT 143
174 #define IMAPS_PORT 993
177 #define IMAP_CMD_LIMIT 1000
179 #define QUOTE_IF_REQUIRED(out, str) \
181 if (*str != '"' && strpbrk(str, " \t(){}[]%*\\") != NULL) { \
185 len = strlen(str) + 3; \
186 Xalloca(__tmp, len, return IMAP_ERROR); \
187 g_snprintf(__tmp, len, "\"%s\"", str); \
190 Xstrdup_a(out, str, return IMAP_ERROR); \
194 typedef gchar * IMAPSet;
196 struct _IMAPFolderItem
206 static void imap_folder_init (Folder *folder,
210 static Folder *imap_folder_new (const gchar *name,
212 static void imap_folder_destroy (Folder *folder);
214 static IMAPSession *imap_session_new (const PrefsAccount *account);
215 static void imap_session_authenticate(IMAPSession *session,
216 const PrefsAccount *account);
217 static void imap_session_destroy (Session *session);
219 static gchar *imap_fetch_msg (Folder *folder,
222 static gchar *imap_fetch_msg_full (Folder *folder,
227 static gint imap_add_msg (Folder *folder,
231 static gint imap_add_msgs (Folder *folder,
234 GRelation *relation);
236 static gint imap_copy_msg (Folder *folder,
239 static gint imap_copy_msgs (Folder *folder,
241 MsgInfoList *msglist,
242 GRelation *relation);
244 static gint imap_remove_msg (Folder *folder,
247 static gint imap_remove_msgs (Folder *folder,
249 MsgInfoList *msglist,
250 GRelation *relation);
251 static gint imap_remove_all_msg (Folder *folder,
254 static gboolean imap_is_msg_changed (Folder *folder,
258 static gint imap_close (Folder *folder,
261 static gint imap_scan_tree (Folder *folder);
263 static gint imap_create_tree (Folder *folder);
265 static FolderItem *imap_create_folder (Folder *folder,
268 static gint imap_rename_folder (Folder *folder,
271 static gint imap_remove_folder (Folder *folder,
274 static FolderItem *imap_folder_item_new (Folder *folder);
275 static void imap_folder_item_destroy (Folder *folder,
278 static IMAPSession *imap_session_get (Folder *folder);
280 static gint imap_greeting (IMAPSession *session);
281 static gint imap_auth (IMAPSession *session,
286 static gint imap_scan_tree_recursive (IMAPSession *session,
288 static GSList *imap_parse_list (IMAPFolder *folder,
289 IMAPSession *session,
290 const gchar *real_path,
293 static void imap_create_missing_folders (Folder *folder);
294 static FolderItem *imap_create_special_folder
296 SpecialFolderItemType stype,
299 static gint imap_do_copy_msgs (Folder *folder,
301 MsgInfoList *msglist,
302 GRelation *relation);
304 static void imap_delete_all_cached_messages (FolderItem *item);
305 static void imap_set_batch (Folder *folder,
309 static SockInfo *imap_open (const gchar *server,
313 static SockInfo *imap_open (const gchar *server,
318 static SockInfo *imap_open_tunnel(const gchar *server,
319 const gchar *tunnelcmd,
322 static SockInfo *imap_open_tunnel(const gchar *server,
323 const gchar *tunnelcmd);
327 static SockInfo *imap_init_sock(SockInfo *sock, SSLType ssl_type);
329 static SockInfo *imap_init_sock(SockInfo *sock);
332 static gchar *imap_get_flag_str (IMAPFlags flags);
333 static gint imap_set_message_flags (IMAPSession *session,
334 MsgNumberList *numlist,
337 static gint imap_select (IMAPSession *session,
343 guint32 *uid_validity,
345 static gint imap_status (IMAPSession *session,
351 guint32 *uid_validity,
355 static void imap_parse_namespace (IMAPSession *session,
357 static void imap_get_namespace_by_list (IMAPSession *session,
359 static IMAPNameSpace *imap_find_namespace (IMAPFolder *folder,
361 static gchar imap_get_path_separator (IMAPFolder *folder,
363 static gchar *imap_get_real_path (IMAPFolder *folder,
366 static gchar *imap_parse_atom (SockInfo *sock,
371 static MsgFlags imap_parse_flags (const gchar *flag_str);
372 static MsgInfo *imap_parse_envelope (SockInfo *sock,
376 static gboolean imap_has_capability (IMAPSession *session,
378 static void imap_free_capabilities (IMAPSession *session);
380 /* low-level IMAP4rev1 commands */
381 static gint imap_cmd_authenticate
382 (IMAPSession *session,
386 static gint imap_cmd_login (IMAPSession *session,
389 static gint imap_cmd_logout (IMAPSession *session);
390 static gint imap_cmd_noop (IMAPSession *session);
392 static gint imap_cmd_starttls (IMAPSession *session);
394 static gint imap_cmd_namespace (IMAPSession *session,
396 static gint imap_cmd_list (IMAPSession *session,
398 const gchar *mailbox,
400 static gint imap_cmd_do_select (IMAPSession *session,
406 guint32 *uid_validity,
408 static gint imap_cmd_select (IMAPSession *session,
413 guint32 *uid_validity,
415 static gint imap_cmd_examine (IMAPSession *session,
420 guint32 *uid_validity,
422 static gint imap_cmd_create (IMAPSession *sock,
423 const gchar *folder);
424 static gint imap_cmd_rename (IMAPSession *sock,
425 const gchar *oldfolder,
426 const gchar *newfolder);
427 static gint imap_cmd_delete (IMAPSession *session,
428 const gchar *folder);
429 static gint imap_cmd_envelope (IMAPSession *session,
431 static gint imap_cmd_fetch (IMAPSession *sock,
433 const gchar *filename,
436 static gint imap_cmd_append (IMAPSession *session,
437 const gchar *destfolder,
441 static gint imap_cmd_copy (IMAPSession *session,
442 const gchar *seq_set,
443 const gchar *destfolder,
444 GRelation *uid_mapping);
445 static gint imap_cmd_store (IMAPSession *session,
448 static gint imap_cmd_expunge (IMAPSession *session,
450 static gint imap_cmd_close (IMAPSession *session);
452 static gint imap_cmd_ok (IMAPSession *session,
454 static gint imap_cmd_ok_block (IMAPSession *session,
456 static gint imap_cmd_ok_with_block
457 (IMAPSession *session,
460 static void imap_gen_send (IMAPSession *session,
461 const gchar *format, ...);
462 static gint imap_gen_recv (IMAPSession *session,
464 static gint imap_gen_recv_block (IMAPSession *session,
466 static gint imap_gen_recv_with_block
467 (IMAPSession *session,
471 /* misc utility functions */
472 static gchar *strchr_cpy (const gchar *src,
476 static gchar *get_quoted (const gchar *src,
480 static gchar *search_array_contain_str (GPtrArray *array,
482 static gchar *search_array_str (GPtrArray *array,
484 static void imap_path_separator_subst (gchar *str,
487 static gchar *imap_utf8_to_modified_utf7 (const gchar *from);
488 static gchar *imap_modified_utf7_to_utf8 (const gchar *mutf7_str);
490 static GSList *imap_get_seq_set_from_numlist (MsgNumberList *msglist);
491 static GSList *imap_get_seq_set_from_msglist (MsgInfoList *msglist);
492 static void imap_seq_set_free (GSList *seq_list);
494 static gboolean imap_rename_folder_func (GNode *node,
496 static gint imap_get_num_list (Folder *folder,
499 gboolean *old_uids_valid);
500 static GSList *imap_get_msginfos (Folder *folder,
502 GSList *msgnum_list);
503 static MsgInfo *imap_get_msginfo (Folder *folder,
506 static gboolean imap_scan_required (Folder *folder,
508 static void imap_change_flags (Folder *folder,
511 MsgPermFlags newflags);
512 static gint imap_get_flags (Folder *folder,
514 MsgInfoList *msglist,
515 GRelation *msgflags);
516 static gchar *imap_folder_get_path (Folder *folder);
517 static gchar *imap_item_get_path (Folder *folder,
519 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item);
520 static GHashTable *flags_set_table = NULL;
521 static GHashTable *flags_unset_table = NULL;
522 typedef struct _hashtable_data {
523 IMAPSession *session;
527 static FolderClass imap_class;
529 typedef struct _thread_data {
540 void *imap_getline_thread(void *data)
542 thread_data *td = (thread_data *)data;
545 line = sock_getline(td->sock);
553 /* imap_getline just wraps sock_getline inside a thread,
554 * performing gtk updates so that the interface isn't frozen.
556 static gchar *imap_getline(SockInfo *sock)
558 #if (defined USE_PTHREAD && defined __GLIBC__ && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 3)))
559 thread_data *td = g_new0(thread_data, 1);
564 if (pthread_create(&pt, PTHREAD_CREATE_JOINABLE,
565 imap_getline_thread, td) != 0) {
567 return sock_getline(sock);
570 debug_print("+++waiting for imap_getline_thread...\n");
572 /* don't let the interface freeze while waiting */
575 debug_print("---imap_getline_thread done\n");
577 /* get the thread's return value and clean its resources */
578 pthread_join(pt, (void *)&line);
583 return sock_getline(sock);
587 FolderClass *imap_get_class(void)
589 if (imap_class.idstr == NULL) {
590 imap_class.type = F_IMAP;
591 imap_class.idstr = "imap";
592 imap_class.uistr = "IMAP4";
594 /* Folder functions */
595 imap_class.new_folder = imap_folder_new;
596 imap_class.destroy_folder = imap_folder_destroy;
597 imap_class.scan_tree = imap_scan_tree;
598 imap_class.create_tree = imap_create_tree;
600 /* FolderItem functions */
601 imap_class.item_new = imap_folder_item_new;
602 imap_class.item_destroy = imap_folder_item_destroy;
603 imap_class.item_get_path = imap_item_get_path;
604 imap_class.create_folder = imap_create_folder;
605 imap_class.rename_folder = imap_rename_folder;
606 imap_class.remove_folder = imap_remove_folder;
607 imap_class.close = imap_close;
608 imap_class.get_num_list = imap_get_num_list;
609 imap_class.scan_required = imap_scan_required;
611 /* Message functions */
612 imap_class.get_msginfo = imap_get_msginfo;
613 imap_class.get_msginfos = imap_get_msginfos;
614 imap_class.fetch_msg = imap_fetch_msg;
615 imap_class.fetch_msg_full = imap_fetch_msg_full;
616 imap_class.add_msg = imap_add_msg;
617 imap_class.add_msgs = imap_add_msgs;
618 imap_class.copy_msg = imap_copy_msg;
619 imap_class.copy_msgs = imap_copy_msgs;
620 imap_class.remove_msg = imap_remove_msg;
621 imap_class.remove_msgs = imap_remove_msgs;
622 imap_class.remove_all_msg = imap_remove_all_msg;
623 imap_class.is_msg_changed = imap_is_msg_changed;
624 imap_class.change_flags = imap_change_flags;
625 imap_class.get_flags = imap_get_flags;
626 imap_class.set_batch = imap_set_batch;
628 pthread_mutex_init(&imap_mutex, NULL);
635 static gchar *get_seq_set_from_seq_list(GSList *seq_list)
641 for (cur = seq_list; cur != NULL; cur = cur->next) {
642 tmp = val?g_strdup(val):NULL;
644 val = g_strconcat(tmp?tmp:"", tmp?",":"",(gchar *)cur->data,
652 static Folder *imap_folder_new(const gchar *name, const gchar *path)
656 folder = (Folder *)g_new0(IMAPFolder, 1);
657 folder->klass = &imap_class;
658 imap_folder_init(folder, name, path);
663 static void imap_folder_destroy(Folder *folder)
667 dir = imap_folder_get_path(folder);
668 if (is_dir_exist(dir))
669 remove_dir_recursive(dir);
672 folder_remote_folder_destroy(REMOTE_FOLDER(folder));
675 static void imap_folder_init(Folder *folder, const gchar *name,
678 folder_remote_folder_init((Folder *)folder, name, path);
681 static FolderItem *imap_folder_item_new(Folder *folder)
683 IMAPFolderItem *item;
685 item = g_new0(IMAPFolderItem, 1);
688 item->uid_list = NULL;
690 return (FolderItem *)item;
693 static void imap_folder_item_destroy(Folder *folder, FolderItem *_item)
695 IMAPFolderItem *item = (IMAPFolderItem *)_item;
697 g_return_if_fail(item != NULL);
698 g_slist_free(item->uid_list);
703 static gboolean imap_reset_uid_lists_func(GNode *node, gpointer data)
705 IMAPFolderItem *item = (IMAPFolderItem *)node->data;
709 g_slist_free(item->uid_list);
710 item->uid_list = NULL;
715 static void imap_reset_uid_lists(Folder *folder)
717 if(folder->node == NULL)
720 /* Destroy all uid lists and rest last uid */
721 g_node_traverse(folder->node, G_IN_ORDER, G_TRAVERSE_ALL, -1, imap_reset_uid_lists_func, NULL);
724 /* Send CAPABILITY, and examine the server's response to see whether this
725 * connection is pre-authenticated or not and build a list of CAPABILITIES. */
726 static gint imap_greeting(IMAPSession *session)
731 imap_gen_send(session, "CAPABILITY");
733 argbuf = g_ptr_array_new();
735 if (imap_cmd_ok(session, argbuf) != IMAP_SUCCESS ||
736 ((capstr = search_array_str(argbuf, "CAPABILITY ")) == NULL)) {
737 ptr_array_free_strings(argbuf);
738 g_ptr_array_free(argbuf, TRUE);
742 session->authenticated = search_array_str(argbuf, "PREAUTH") != NULL;
744 capstr += strlen("CAPABILITY ");
746 IMAP_SESSION(session)->capability = g_strsplit(capstr, " ", 0);
748 ptr_array_free_strings(argbuf);
749 g_ptr_array_free(argbuf, TRUE);
751 if (imap_has_capability(session, "UIDPLUS"))
752 session->uidplus = TRUE;
757 static gint imap_auth(IMAPSession *session, const gchar *user, const gchar *pass,
762 if (type == 0 || type == IMAP_AUTH_LOGIN)
763 ok = imap_cmd_login(session, user, pass);
765 ok = imap_cmd_authenticate(session, user, pass, type);
767 if (ok == IMAP_SUCCESS)
768 session->authenticated = TRUE;
773 static IMAPSession *imap_session_get(Folder *folder)
775 RemoteFolder *rfolder = REMOTE_FOLDER(folder);
776 IMAPSession *session = NULL;
778 g_return_val_if_fail(folder != NULL, NULL);
779 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, NULL);
780 g_return_val_if_fail(folder->account != NULL, NULL);
782 if (prefs_common.work_offline && !imap_gtk_should_override()) {
786 /* Make sure we have a session */
787 if (rfolder->session != NULL) {
788 session = IMAP_SESSION(rfolder->session);
790 imap_reset_uid_lists(folder);
791 session = imap_session_new(folder->account);
796 if (SESSION(session)->sock->state == CONN_DISCONNECTED) {
797 debug_print("IMAP server disconnected\n");
798 session_destroy(SESSION(session));
799 imap_reset_uid_lists(folder);
800 session = imap_session_new(folder->account);
803 /* Make sure session is authenticated */
804 if (!IMAP_SESSION(session)->authenticated)
805 imap_session_authenticate(IMAP_SESSION(session), folder->account);
806 if (!IMAP_SESSION(session)->authenticated) {
807 session_destroy(SESSION(session));
808 rfolder->session = NULL;
812 /* Make sure we have parsed the IMAP namespace */
813 imap_parse_namespace(IMAP_SESSION(session),
814 IMAP_FOLDER(folder));
816 /* I think the point of this code is to avoid sending a
817 * keepalive if we've used the session recently and therefore
818 * think it's still alive. Unfortunately, most of the code
819 * does not yet check for errors on the socket, and so if the
820 * connection drops we don't notice until the timeout expires.
821 * A better solution than sending a NOOP every time would be
822 * for every command to be prepared to retry until it is
823 * successfully sent. -- mbp */
824 if (time(NULL) - SESSION(session)->last_access_time > SESSION_TIMEOUT_INTERVAL) {
825 /* verify that the session is still alive */
826 if (imap_cmd_noop(session) != IMAP_SUCCESS) {
827 /* Check if this is the first try to establish a
828 connection, if yes we don't try to reconnect */
829 if (rfolder->session == NULL) {
830 log_warning(_("Connecting to %s failed"),
831 folder->account->recv_server);
832 session_destroy(SESSION(session));
835 log_warning(_("IMAP4 connection to %s has been"
836 " disconnected. Reconnecting...\n"),
837 folder->account->recv_server);
838 statusbar_print_all(_("IMAP4 connection to %s has been"
839 " disconnected. Reconnecting...\n"),
840 folder->account->recv_server);
841 session_destroy(SESSION(session));
842 /* Clear folders session to make imap_session_get create
843 a new session, because of rfolder->session == NULL
844 it will not try to reconnect again and so avoid an
846 rfolder->session = NULL;
847 session = imap_session_get(folder);
853 rfolder->session = SESSION(session);
855 return IMAP_SESSION(session);
858 static IMAPSession *imap_session_new(const PrefsAccount *account)
860 IMAPSession *session;
865 /* FIXME: IMAP over SSL only... */
868 port = account->set_imapport ? account->imapport
869 : account->ssl_imap == SSL_TUNNEL ? IMAPS_PORT : IMAP4_PORT;
870 ssl_type = account->ssl_imap;
872 port = account->set_imapport ? account->imapport
876 if (account->set_tunnelcmd) {
877 log_message(_("creating tunneled IMAP4 connection\n"));
879 if ((imap_sock = imap_open_tunnel(account->recv_server,
883 if ((imap_sock = imap_open_tunnel(account->recv_server,
884 account->tunnelcmd)) == NULL)
888 g_return_val_if_fail(account->recv_server != NULL, NULL);
890 log_message(_("creating IMAP4 connection to %s:%d ...\n"),
891 account->recv_server, port);
894 if ((imap_sock = imap_open(account->recv_server, port,
897 if ((imap_sock = imap_open(account->recv_server, port)) == NULL)
902 session = g_new0(IMAPSession, 1);
903 session_init(SESSION(session));
904 SESSION(session)->type = SESSION_IMAP;
905 SESSION(session)->server = g_strdup(account->recv_server);
906 SESSION(session)->sock = imap_sock;
908 SESSION(session)->destroy = imap_session_destroy;
910 session->capability = NULL;
912 session->authenticated = FALSE;
913 session->mbox = NULL;
914 session->cmd_count = 0;
916 /* Only need to log in if the connection was not PREAUTH */
917 if (imap_greeting(session) != IMAP_SUCCESS) {
918 session_destroy(SESSION(session));
923 if (account->ssl_imap == SSL_STARTTLS &&
924 imap_has_capability(session, "STARTTLS")) {
927 ok = imap_cmd_starttls(session);
928 if (ok != IMAP_SUCCESS) {
929 log_warning(_("Can't start TLS session.\n"));
930 session_destroy(SESSION(session));
933 if (!ssl_init_socket_with_method(SESSION(session)->sock,
935 session_destroy(SESSION(session));
939 imap_free_capabilities(session);
940 session->authenticated = FALSE;
941 session->uidplus = FALSE;
942 session->cmd_count = 1;
944 if (imap_greeting(session) != IMAP_SUCCESS) {
945 session_destroy(SESSION(session));
950 log_message("IMAP connection is %s-authenticated\n",
951 (session->authenticated) ? "pre" : "un");
956 static void imap_session_authenticate(IMAPSession *session,
957 const PrefsAccount *account)
961 g_return_if_fail(account->userid != NULL);
963 pass = account->passwd;
966 tmp_pass = input_dialog_query_password(account->recv_server, account->userid);
969 Xstrdup_a(pass, tmp_pass, {g_free(tmp_pass); return;});
972 statusbar_print_all(_("Connecting to IMAP4 server %s...\n"),
973 account->recv_server);
974 if (imap_auth(session, account->userid, pass, account->imap_auth_type) != IMAP_SUCCESS) {
975 imap_cmd_logout(session);
981 session->authenticated = TRUE;
984 static void imap_session_destroy(Session *session)
986 imap_free_capabilities(IMAP_SESSION(session));
987 g_free(IMAP_SESSION(session)->mbox);
988 sock_close(session->sock);
989 session->sock = NULL;
992 static gchar *imap_fetch_msg(Folder *folder, FolderItem *item, gint uid)
994 return imap_fetch_msg_full(folder, item, uid, TRUE, TRUE);
997 static guint get_size_with_lfs(MsgInfo *info)
1006 fp = procmsg_open_message(info);
1010 while (fgets(buf, sizeof (buf), fp) != NULL) {
1012 if (!strstr(buf, "\r") && strstr(buf, "\n"))
1020 static gchar *imap_fetch_msg_full(Folder *folder, FolderItem *item, gint uid,
1021 gboolean headers, gboolean body)
1023 gchar *path, *filename;
1024 IMAPSession *session;
1027 g_return_val_if_fail(folder != NULL, NULL);
1028 g_return_val_if_fail(item != NULL, NULL);
1033 path = folder_item_get_path(item);
1034 if (!is_dir_exist(path))
1035 make_dir_hier(path);
1036 filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(uid), NULL);
1039 if (is_file_exist(filename)) {
1040 /* see whether the local file represents the whole message
1041 * or not. As the IMAP server reports size with \r chars,
1042 * we have to update the local file (UNIX \n only) size */
1043 MsgInfo *msginfo = imap_parse_msg(filename, item);
1044 MsgInfo *cached = msgcache_get_msg(item->cache,uid);
1045 guint have_size = get_size_with_lfs(msginfo);
1046 debug_print("message %d has been already %scached (%d/%d).\n", uid,
1047 have_size == cached->size ? "fully ":"",
1048 have_size, cached? (int)cached->size : -1);
1050 if (cached && (cached->size == have_size || !body)) {
1051 procmsg_msginfo_free(cached);
1052 procmsg_msginfo_free(msginfo);
1055 procmsg_msginfo_free(cached);
1056 procmsg_msginfo_free(msginfo);
1060 session = imap_session_get(folder);
1066 debug_print("IMAP fetching messages\n");
1067 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
1068 NULL, NULL, NULL, NULL, FALSE);
1069 if (ok != IMAP_SUCCESS) {
1070 g_warning("can't select mailbox %s\n", item->path);
1075 debug_print("getting message %d...\n", uid);
1076 ok = imap_cmd_fetch(session, (guint32)uid, filename, headers, body);
1078 if (ok != IMAP_SUCCESS) {
1079 g_warning("can't fetch message %d\n", uid);
1087 static gint imap_add_msg(Folder *folder, FolderItem *dest,
1088 const gchar *file, MsgFlags *flags)
1092 MsgFileInfo fileinfo;
1094 g_return_val_if_fail(file != NULL, -1);
1096 fileinfo.msginfo = NULL;
1097 fileinfo.file = (gchar *)file;
1098 fileinfo.flags = flags;
1099 file_list.data = &fileinfo;
1100 file_list.next = NULL;
1102 ret = imap_add_msgs(folder, dest, &file_list, NULL);
1106 static gint imap_add_msgs(Folder *folder, FolderItem *dest, GSList *file_list,
1107 GRelation *relation)
1110 IMAPSession *session;
1111 guint32 last_uid = 0;
1113 MsgFileInfo *fileinfo;
1117 g_return_val_if_fail(folder != NULL, -1);
1118 g_return_val_if_fail(dest != NULL, -1);
1119 g_return_val_if_fail(file_list != NULL, -1);
1121 MUTEX_TRYLOCK_OR_RETURN_VAL(-1);
1123 session = imap_session_get(folder);
1128 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
1130 for (cur = file_list; cur != NULL; cur = cur->next) {
1131 IMAPFlags iflags = 0;
1132 guint32 new_uid = 0;
1134 fileinfo = (MsgFileInfo *)cur->data;
1136 if (fileinfo->flags) {
1137 if (MSG_IS_MARKED(*fileinfo->flags))
1138 iflags |= IMAP_FLAG_FLAGGED;
1139 if (MSG_IS_REPLIED(*fileinfo->flags))
1140 iflags |= IMAP_FLAG_ANSWERED;
1141 if (!MSG_IS_UNREAD(*fileinfo->flags))
1142 iflags |= IMAP_FLAG_SEEN;
1145 if (dest->stype == F_OUTBOX ||
1146 dest->stype == F_QUEUE ||
1147 dest->stype == F_DRAFT ||
1148 dest->stype == F_TRASH)
1149 iflags |= IMAP_FLAG_SEEN;
1151 ok = imap_cmd_append(session, destdir, fileinfo->file, iflags,
1154 if (ok != IMAP_SUCCESS) {
1155 g_warning("can't append message %s\n", fileinfo->file);
1161 if (relation != NULL)
1162 g_relation_insert(relation, fileinfo->msginfo != NULL ?
1163 (gpointer) fileinfo->msginfo : (gpointer) fileinfo,
1164 GINT_TO_POINTER(dest->last_num + 1));
1165 if (last_uid < new_uid)
1175 static gint imap_do_copy_msgs(Folder *folder, FolderItem *dest,
1176 MsgInfoList *msglist, GRelation *relation)
1180 GSList *seq_list, *cur;
1182 IMAPSession *session;
1183 gint ok = IMAP_SUCCESS;
1184 GRelation *uid_mapping;
1187 g_return_val_if_fail(folder != NULL, -1);
1188 g_return_val_if_fail(dest != NULL, -1);
1189 g_return_val_if_fail(msglist != NULL, -1);
1191 session = imap_session_get(folder);
1195 msginfo = (MsgInfo *)msglist->data;
1197 src = msginfo->folder;
1199 g_warning("the src folder is identical to the dest.\n");
1203 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
1204 NULL, NULL, NULL, NULL, FALSE);
1205 if (ok != IMAP_SUCCESS) {
1209 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
1210 seq_list = imap_get_seq_set_from_msglist(msglist);
1211 uid_mapping = g_relation_new(2);
1212 g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
1214 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
1215 gchar *seq_set = (gchar *)cur->data;
1217 debug_print("Copying message %s%c[%s] to %s ...\n",
1218 src->path, G_DIR_SEPARATOR,
1221 ok = imap_cmd_copy(session, seq_set, destdir, uid_mapping);
1222 if (ok != IMAP_SUCCESS) {
1223 g_relation_destroy(uid_mapping);
1224 imap_seq_set_free(seq_list);
1229 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
1230 MsgInfo *msginfo = (MsgInfo *)cur->data;
1233 tuples = g_relation_select(uid_mapping,
1234 GINT_TO_POINTER(msginfo->msgnum),
1236 if (tuples->len > 0) {
1237 gint num = GPOINTER_TO_INT(g_tuples_index(tuples, 0, 1));
1238 g_relation_insert(relation, msginfo,
1239 GPOINTER_TO_INT(num));
1243 g_relation_insert(relation, msginfo,
1244 GPOINTER_TO_INT(0));
1245 g_tuples_destroy(tuples);
1248 g_relation_destroy(uid_mapping);
1249 imap_seq_set_free(seq_list);
1253 IMAP_FOLDER_ITEM(dest)->lastuid = 0;
1254 IMAP_FOLDER_ITEM(dest)->uid_next = 0;
1255 g_slist_free(IMAP_FOLDER_ITEM(dest)->uid_list);
1256 IMAP_FOLDER_ITEM(dest)->uid_list = NULL;
1258 if (ok == IMAP_SUCCESS)
1264 static gint imap_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1268 g_return_val_if_fail(msginfo != NULL, -1);
1270 msglist.data = msginfo;
1271 msglist.next = NULL;
1273 return imap_copy_msgs(folder, dest, &msglist, NULL);
1276 static gint imap_copy_msgs(Folder *folder, FolderItem *dest,
1277 MsgInfoList *msglist, GRelation *relation)
1283 g_return_val_if_fail(folder != NULL, -1);
1284 g_return_val_if_fail(dest != NULL, -1);
1285 g_return_val_if_fail(msglist != NULL, -1);
1287 MUTEX_TRYLOCK_OR_RETURN_VAL(-1);
1289 msginfo = (MsgInfo *)msglist->data;
1290 g_return_val_if_fail(msginfo->folder != NULL, -1);
1292 if (folder == msginfo->folder->folder) {
1293 ret = imap_do_copy_msgs(folder, dest, msglist, relation);
1298 file_list = procmsg_get_message_file_list(msglist);
1299 g_return_val_if_fail(file_list != NULL, -1);
1301 ret = imap_add_msgs(folder, dest, file_list, relation);
1303 procmsg_message_file_list_free(file_list);
1310 static gint imap_do_remove_msgs(Folder *folder, FolderItem *dest,
1311 MsgInfoList *msglist, GRelation *relation)
1314 GSList *seq_list = NULL, *cur;
1316 IMAPSession *session;
1317 gint ok = IMAP_SUCCESS;
1318 GRelation *uid_mapping;
1320 g_return_val_if_fail(folder != NULL, -1);
1321 g_return_val_if_fail(dest != NULL, -1);
1322 g_return_val_if_fail(msglist != NULL, -1);
1324 MUTEX_TRYLOCK_OR_RETURN_VAL(-1);
1326 session = imap_session_get(folder);
1331 msginfo = (MsgInfo *)msglist->data;
1333 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
1334 NULL, NULL, NULL, NULL, FALSE);
1335 if (ok != IMAP_SUCCESS) {
1340 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
1341 for (cur = msglist; cur; cur = cur->next) {
1342 msginfo = (MsgInfo *)cur->data;
1343 seq_list = g_slist_append(seq_list, GINT_TO_POINTER(msginfo->msgnum));
1346 uid_mapping = g_relation_new(2);
1347 g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
1349 ok = imap_set_message_flags
1350 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
1351 seq_list, IMAP_FLAG_DELETED, TRUE);
1352 if (ok != IMAP_SUCCESS) {
1353 log_warning(_("can't set deleted flags\n"));
1357 ok = imap_cmd_expunge(session, NULL);
1358 if (ok != IMAP_SUCCESS) {
1359 log_warning(_("can't expunge\n"));
1364 g_relation_destroy(uid_mapping);
1365 g_slist_free(seq_list);
1370 if (ok == IMAP_SUCCESS)
1376 static gint imap_remove_msgs(Folder *folder, FolderItem *dest,
1377 MsgInfoList *msglist, GRelation *relation)
1381 g_return_val_if_fail(folder != NULL, -1);
1382 g_return_val_if_fail(dest != NULL, -1);
1383 g_return_val_if_fail(msglist != NULL, -1);
1385 msginfo = (MsgInfo *)msglist->data;
1386 g_return_val_if_fail(msginfo->folder != NULL, -1);
1388 return imap_do_remove_msgs(folder, dest, msglist, relation);
1391 static gint imap_remove_all_msg(Folder *folder, FolderItem *item)
1393 GSList *list = folder_item_get_msg_list(item);
1394 gint res = imap_remove_msgs(folder, item, list, NULL);
1399 static gboolean imap_is_msg_changed(Folder *folder, FolderItem *item,
1402 /* TODO: properly implement this method */
1406 static gint imap_close(Folder *folder, FolderItem *item)
1409 IMAPSession *session;
1411 g_return_val_if_fail(folder != NULL, -1);
1412 g_return_val_if_fail(item != NULL, -1);
1413 g_return_val_if_fail(item->path != NULL, -1);
1415 session = imap_session_get(folder);
1416 if (!session) return -1;
1418 if (session->mbox) {
1419 if (strcmp2(session->mbox, item->path) != 0) return -1;
1421 ok = imap_cmd_close(session);
1422 if (ok != IMAP_SUCCESS)
1423 log_warning(_("can't close folder\n"));
1425 g_free(session->mbox);
1427 session->mbox = NULL;
1435 static gint imap_scan_tree(Folder *folder)
1437 FolderItem *item = NULL;
1438 IMAPSession *session;
1439 gchar *root_folder = NULL;
1441 g_return_val_if_fail(folder != NULL, -1);
1442 g_return_val_if_fail(folder->account != NULL, -1);
1444 MUTEX_TRYLOCK_OR_RETURN_VAL(-1);
1446 session = imap_session_get(folder);
1448 if (!folder->node) {
1449 folder_tree_destroy(folder);
1450 item = folder_item_new(folder, folder->name, NULL);
1451 item->folder = folder;
1452 folder->node = item->node = g_node_new(item);
1458 if (folder->account->imap_dir && *folder->account->imap_dir) {
1463 Xstrdup_a(root_folder, folder->account->imap_dir, {MUTEX_UNLOCK();return -1;});
1464 extract_quote(root_folder, '"');
1465 subst_char(root_folder,
1466 imap_get_path_separator(IMAP_FOLDER(folder),
1469 strtailchomp(root_folder, '/');
1470 real_path = imap_get_real_path
1471 (IMAP_FOLDER(folder), root_folder);
1472 debug_print("IMAP root directory: %s\n", real_path);
1474 /* check if root directory exist */
1475 argbuf = g_ptr_array_new();
1476 ok = imap_cmd_list(session, NULL, real_path, argbuf);
1477 if (ok != IMAP_SUCCESS ||
1478 search_array_str(argbuf, "LIST ") == NULL) {
1479 log_warning(_("root folder %s does not exist\n"), real_path);
1480 g_ptr_array_free(argbuf, TRUE);
1483 if (!folder->node) {
1484 item = folder_item_new(folder, folder->name, NULL);
1485 item->folder = folder;
1486 folder->node = item->node = g_node_new(item);
1491 g_ptr_array_free(argbuf, TRUE);
1496 item = FOLDER_ITEM(folder->node->data);
1497 if (!item || ((item->path || root_folder) &&
1498 strcmp2(item->path, root_folder) != 0)) {
1499 folder_tree_destroy(folder);
1500 item = folder_item_new(folder, folder->name, root_folder);
1501 item->folder = folder;
1502 folder->node = item->node = g_node_new(item);
1505 imap_scan_tree_recursive(session, FOLDER_ITEM(folder->node->data));
1506 imap_create_missing_folders(folder);
1512 static gint imap_scan_tree_recursive(IMAPSession *session, FolderItem *item)
1515 IMAPFolder *imapfolder;
1516 FolderItem *new_item;
1517 GSList *item_list, *cur;
1520 gchar *wildcard_path;
1524 g_return_val_if_fail(item != NULL, -1);
1525 g_return_val_if_fail(item->folder != NULL, -1);
1526 g_return_val_if_fail(item->no_sub == FALSE, -1);
1528 folder = item->folder;
1529 imapfolder = IMAP_FOLDER(folder);
1531 separator = imap_get_path_separator(imapfolder, item->path);
1533 if (folder->ui_func)
1534 folder->ui_func(folder, item, folder->ui_func_data);
1537 wildcard[0] = separator;
1540 real_path = imap_get_real_path(imapfolder, item->path);
1544 real_path = g_strdup("");
1547 Xstrcat_a(wildcard_path, real_path, wildcard,
1548 {g_free(real_path); return IMAP_ERROR;});
1549 QUOTE_IF_REQUIRED(wildcard_path, wildcard_path);
1551 imap_gen_send(session, "LIST \"\" %s",
1554 strtailchomp(real_path, separator);
1555 item_list = imap_parse_list(imapfolder, session, real_path, NULL);
1558 node = item->node->children;
1559 while (node != NULL) {
1560 FolderItem *old_item = FOLDER_ITEM(node->data);
1561 GNode *next = node->next;
1564 for (cur = item_list; cur != NULL; cur = cur->next) {
1565 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1566 if (!strcmp2(old_item->path, cur_item->path)) {
1567 new_item = cur_item;
1572 debug_print("folder '%s' not found. removing...\n",
1574 folder_item_remove(old_item);
1576 old_item->no_sub = new_item->no_sub;
1577 old_item->no_select = new_item->no_select;
1578 if (old_item->no_sub == TRUE && node->children) {
1579 debug_print("folder '%s' doesn't have "
1580 "subfolders. removing...\n",
1582 folder_item_remove_children(old_item);
1589 for (cur = item_list; cur != NULL; cur = cur->next) {
1590 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1592 for (node = item->node->children; node != NULL;
1593 node = node->next) {
1594 if (!strcmp2(FOLDER_ITEM(node->data)->path,
1596 new_item = FOLDER_ITEM(node->data);
1597 folder_item_destroy(cur_item);
1603 new_item = cur_item;
1604 debug_print("new folder '%s' found.\n", new_item->path);
1605 folder_item_append(item, new_item);
1608 if (!strcmp(new_item->path, "INBOX")) {
1609 new_item->stype = F_INBOX;
1610 folder->inbox = new_item;
1611 } else if (!folder_item_parent(item) || item->stype == F_INBOX) {
1614 base = g_path_get_basename(new_item->path);
1616 if (!folder->outbox && !g_ascii_strcasecmp(base, "Sent")) {
1617 new_item->stype = F_OUTBOX;
1618 folder->outbox = new_item;
1619 } else if (!folder->draft && !g_ascii_strcasecmp(base, "Drafts")) {
1620 new_item->stype = F_DRAFT;
1621 folder->draft = new_item;
1622 } else if (!folder->queue && !g_ascii_strcasecmp(base, "Queue")) {
1623 new_item->stype = F_QUEUE;
1624 folder->queue = new_item;
1625 } else if (!folder->trash && !g_ascii_strcasecmp(base, "Trash")) {
1626 new_item->stype = F_TRASH;
1627 folder->trash = new_item;
1632 if (new_item->no_sub == FALSE)
1633 imap_scan_tree_recursive(session, new_item);
1636 g_slist_free(item_list);
1638 return IMAP_SUCCESS;
1641 static GSList *imap_parse_list(IMAPFolder *folder, IMAPSession *session,
1642 const gchar *real_path, gchar *separator)
1644 gchar buf[IMAPBUFSIZE];
1646 gchar separator_str[16];
1649 gchar *loc_name, *loc_path;
1650 GSList *item_list = NULL;
1652 FolderItem *new_item;
1654 debug_print("getting list of %s ...\n",
1655 *real_path ? real_path : "\"\"");
1657 str = g_string_new(NULL);
1660 if (sock_gets(SESSION(session)->sock, buf, sizeof(buf)) <= 0) {
1661 log_warning(_("error occurred while getting LIST.\n"));
1665 if (buf[0] != '*' || buf[1] != ' ') {
1666 log_print("IMAP4< %s\n", buf);
1667 if (sscanf(buf, "%*d %16s", buf) < 1 ||
1668 strcmp(buf, "OK") != 0)
1669 log_warning(_("error occurred while getting LIST.\n"));
1673 debug_print("IMAP4< %s\n", buf);
1675 g_string_assign(str, buf);
1677 if (strncmp(p, "LIST ", 5) != 0) continue;
1680 if (*p != '(') continue;
1682 p = strchr_cpy(p, ')', flags, sizeof(flags));
1684 while (*p == ' ') p++;
1686 p = strchr_cpy(p, ' ', separator_str, sizeof(separator_str));
1688 extract_quote(separator_str, '"');
1689 if (!strcmp(separator_str, "NIL"))
1690 separator_str[0] = '\0';
1692 *separator = separator_str[0];
1695 while (*p == ' ') p++;
1696 if (*p == '{' || *p == '"')
1697 p = imap_parse_atom(SESSION(session)->sock, p,
1698 buf, sizeof(buf), str);
1700 strncpy2(buf, p, sizeof(buf));
1701 strtailchomp(buf, separator_str[0]);
1702 if (buf[0] == '\0') continue;
1703 if (!strcmp(buf, real_path)) continue;
1705 if (separator_str[0] != '\0')
1706 subst_char(buf, separator_str[0], '/');
1707 base = g_path_get_basename(buf);
1708 if (base[0] == '.') continue;
1710 loc_name = imap_modified_utf7_to_utf8(base);
1711 loc_path = imap_modified_utf7_to_utf8(buf);
1712 new_item = folder_item_new(FOLDER(folder), loc_name, loc_path);
1713 if (strcasestr(flags, "\\Noinferiors") != NULL)
1714 new_item->no_sub = TRUE;
1715 if (strcmp(buf, "INBOX") != 0 &&
1716 strcasestr(flags, "\\Noselect") != NULL)
1717 new_item->no_select = TRUE;
1719 item_list = g_slist_append(item_list, new_item);
1721 debug_print("folder '%s' found.\n", loc_path);
1727 g_string_free(str, TRUE);
1732 static gint imap_create_tree(Folder *folder)
1734 g_return_val_if_fail(folder != NULL, -1);
1735 g_return_val_if_fail(folder->node != NULL, -1);
1736 g_return_val_if_fail(folder->node->data != NULL, -1);
1737 g_return_val_if_fail(folder->account != NULL, -1);
1739 imap_scan_tree(folder);
1740 imap_create_missing_folders(folder);
1745 static void imap_create_missing_folders(Folder *folder)
1747 g_return_if_fail(folder != NULL);
1750 folder->inbox = imap_create_special_folder
1751 (folder, F_INBOX, "INBOX");
1753 if (!folder->outbox)
1754 folder->outbox = imap_create_special_folder
1755 (folder, F_OUTBOX, "Sent");
1757 folder->draft = imap_create_special_folder
1758 (folder, F_DRAFT, "Drafts");
1760 folder->queue = imap_create_special_folder
1761 (folder, F_QUEUE, "Queue");
1764 folder->trash = imap_create_special_folder
1765 (folder, F_TRASH, "Trash");
1768 static FolderItem *imap_create_special_folder(Folder *folder,
1769 SpecialFolderItemType stype,
1773 FolderItem *new_item;
1775 g_return_val_if_fail(folder != NULL, NULL);
1776 g_return_val_if_fail(folder->node != NULL, NULL);
1777 g_return_val_if_fail(folder->node->data != NULL, NULL);
1778 g_return_val_if_fail(folder->account != NULL, NULL);
1779 g_return_val_if_fail(name != NULL, NULL);
1781 item = FOLDER_ITEM(folder->node->data);
1782 new_item = imap_create_folder(folder, item, name);
1785 g_warning("Can't create '%s'\n", name);
1786 if (!folder->inbox) return NULL;
1788 new_item = imap_create_folder(folder, folder->inbox, name);
1790 g_warning("Can't create '%s' under INBOX\n", name);
1792 new_item->stype = stype;
1794 new_item->stype = stype;
1799 static gchar *imap_folder_get_path(Folder *folder)
1803 g_return_val_if_fail(folder != NULL, NULL);
1804 g_return_val_if_fail(folder->account != NULL, NULL);
1806 folder_path = g_strconcat(get_imap_cache_dir(),
1808 folder->account->recv_server,
1810 folder->account->userid,
1816 static gchar *imap_item_get_path(Folder *folder, FolderItem *item)
1818 gchar *folder_path, *path;
1820 g_return_val_if_fail(folder != NULL, NULL);
1821 g_return_val_if_fail(item != NULL, NULL);
1822 folder_path = imap_folder_get_path(folder);
1824 g_return_val_if_fail(folder_path != NULL, NULL);
1825 if (folder_path[0] == G_DIR_SEPARATOR) {
1827 path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
1830 path = g_strdup(folder_path);
1833 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1834 folder_path, G_DIR_SEPARATOR_S,
1837 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1840 g_free(folder_path);
1845 static FolderItem *imap_create_folder(Folder *folder, FolderItem *parent,
1848 gchar *dirpath, *imap_path;
1849 IMAPSession *session;
1850 FolderItem *new_item;
1856 g_return_val_if_fail(folder != NULL, NULL);
1857 g_return_val_if_fail(folder->account != NULL, NULL);
1858 g_return_val_if_fail(parent != NULL, NULL);
1859 g_return_val_if_fail(name != NULL, NULL);
1861 MUTEX_TRYLOCK_OR_RETURN_VAL(NULL);
1863 session = imap_session_get(folder);
1869 if (!folder_item_parent(parent) && strcmp(name, "INBOX") == 0)
1870 dirpath = g_strdup(name);
1871 else if (parent->path)
1872 dirpath = g_strconcat(parent->path, "/", name, NULL);
1873 else if ((p = strchr(name, '/')) != NULL && *(p + 1) != '\0')
1874 dirpath = g_strdup(name);
1875 else if (folder->account->imap_dir && *folder->account->imap_dir) {
1878 Xstrdup_a(imap_dir, folder->account->imap_dir, {MUTEX_UNLOCK();return NULL;});
1879 strtailchomp(imap_dir, '/');
1880 dirpath = g_strconcat(imap_dir, "/", name, NULL);
1882 dirpath = g_strdup(name);
1884 /* keep trailing directory separator to create a folder that contains
1886 imap_path = imap_utf8_to_modified_utf7(dirpath);
1887 strtailchomp(dirpath, '/');
1888 Xstrdup_a(new_name, name, {
1893 strtailchomp(new_name, '/');
1894 separator = imap_get_path_separator(IMAP_FOLDER(folder), imap_path);
1895 imap_path_separator_subst(imap_path, separator);
1896 subst_char(new_name, '/', separator);
1898 if (strcmp(name, "INBOX") != 0) {
1901 gboolean exist = FALSE;
1903 argbuf = g_ptr_array_new();
1904 ok = imap_cmd_list(session, NULL, imap_path,
1906 if (ok != IMAP_SUCCESS) {
1907 log_warning(_("can't create mailbox: LIST failed\n"));
1910 ptr_array_free_strings(argbuf);
1911 g_ptr_array_free(argbuf, TRUE);
1916 for (i = 0; i < argbuf->len; i++) {
1918 str = g_ptr_array_index(argbuf, i);
1919 if (!strncmp(str, "LIST ", 5)) {
1924 ptr_array_free_strings(argbuf);
1925 g_ptr_array_free(argbuf, TRUE);
1928 ok = imap_cmd_create(session, imap_path);
1929 if (ok != IMAP_SUCCESS) {
1930 log_warning(_("can't create mailbox\n"));
1939 new_item = folder_item_new(folder, new_name, dirpath);
1940 folder_item_append(parent, new_item);
1944 dirpath = folder_item_get_path(new_item);
1945 if (!is_dir_exist(dirpath))
1946 make_dir_hier(dirpath);
1953 static gint imap_rename_folder(Folder *folder, FolderItem *item,
1958 gchar *real_oldpath;
1959 gchar *real_newpath;
1961 gchar *old_cache_dir;
1962 gchar *new_cache_dir;
1963 IMAPSession *session;
1966 gint exists, recent, unseen;
1967 guint32 uid_validity;
1969 g_return_val_if_fail(folder != NULL, -1);
1970 g_return_val_if_fail(item != NULL, -1);
1971 g_return_val_if_fail(item->path != NULL, -1);
1972 g_return_val_if_fail(name != NULL, -1);
1974 MUTEX_TRYLOCK_OR_RETURN_VAL(-1);
1976 if (strchr(name, imap_get_path_separator(IMAP_FOLDER(folder), item->path)) != NULL) {
1977 g_warning(_("New folder name must not contain the namespace "
1983 session = imap_session_get(folder);
1988 real_oldpath = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1990 g_free(session->mbox);
1991 session->mbox = NULL;
1992 ok = imap_cmd_examine(session, "INBOX",
1993 &exists, &recent, &unseen, &uid_validity, FALSE);
1994 if (ok != IMAP_SUCCESS) {
1995 g_free(real_oldpath);
2000 separator = imap_get_path_separator(IMAP_FOLDER(folder), item->path);
2001 if (strchr(item->path, G_DIR_SEPARATOR)) {
2002 dirpath = g_path_get_dirname(item->path);
2003 newpath = g_strconcat(dirpath, G_DIR_SEPARATOR_S, name, NULL);
2006 newpath = g_strdup(name);
2008 real_newpath = imap_utf8_to_modified_utf7(newpath);
2009 imap_path_separator_subst(real_newpath, separator);
2011 ok = imap_cmd_rename(session, real_oldpath, real_newpath);
2012 if (ok != IMAP_SUCCESS) {
2013 log_warning(_("can't rename mailbox: %s to %s\n"),
2014 real_oldpath, real_newpath);
2015 g_free(real_oldpath);
2017 g_free(real_newpath);
2023 item->name = g_strdup(name);
2025 old_cache_dir = folder_item_get_path(item);
2027 paths[0] = g_strdup(item->path);
2029 g_node_traverse(item->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
2030 imap_rename_folder_func, paths);
2032 if (is_dir_exist(old_cache_dir)) {
2033 new_cache_dir = folder_item_get_path(item);
2034 if (rename(old_cache_dir, new_cache_dir) < 0) {
2035 FILE_OP_ERROR(old_cache_dir, "rename");
2037 g_free(new_cache_dir);
2040 g_free(old_cache_dir);
2043 g_free(real_oldpath);
2044 g_free(real_newpath);
2050 static gint imap_remove_folder(Folder *folder, FolderItem *item)
2053 IMAPSession *session;
2056 gint exists, recent, unseen;
2057 guint32 uid_validity;
2059 g_return_val_if_fail(folder != NULL, -1);
2060 g_return_val_if_fail(item != NULL, -1);
2061 g_return_val_if_fail(item->path != NULL, -1);
2063 MUTEX_TRYLOCK_OR_RETURN_VAL(-1);
2065 session = imap_session_get(folder);
2070 path = imap_get_real_path(IMAP_FOLDER(folder), item->path);
2072 ok = imap_cmd_examine(session, "INBOX",
2073 &exists, &recent, &unseen, &uid_validity, FALSE);
2074 if (ok != IMAP_SUCCESS) {
2080 ok = imap_cmd_delete(session, path);
2081 if (ok != IMAP_SUCCESS) {
2082 log_warning(_("can't delete mailbox\n"));
2089 cache_dir = folder_item_get_path(item);
2090 if (is_dir_exist(cache_dir) && remove_dir_recursive(cache_dir) < 0)
2091 g_warning("can't remove directory '%s'\n", cache_dir);
2093 folder_item_remove(item);
2099 typedef struct _uncached_data {
2100 IMAPSession *session;
2102 MsgNumberList *numlist;
2108 static void *imap_get_uncached_messages_thread(void *data)
2110 uncached_data *stuff = (uncached_data *)data;
2111 IMAPSession *session = stuff->session;
2112 FolderItem *item = stuff->item;
2113 MsgNumberList *numlist = stuff->numlist;
2116 GSList *newlist = NULL;
2117 GSList *llast = NULL;
2118 GString *str = NULL;
2120 GSList *seq_list, *cur;
2123 stuff->total = g_slist_length(numlist);
2126 if (session == NULL || item == NULL || item->folder == NULL
2127 || FOLDER_CLASS(item->folder) != &imap_class) {
2132 seq_list = imap_get_seq_set_from_numlist(numlist);
2133 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
2134 imapset = cur->data;
2136 if (!imapset || strlen(imapset) == 0)
2139 if (imap_cmd_envelope(session, imapset)
2141 log_warning(_("can't get envelope\n"));
2145 str = g_string_new(NULL);
2148 if ((tmp =sock_getline(SESSION(session)->sock)) == NULL) {
2149 log_warning(_("error occurred while getting envelope.\n"));
2150 g_string_free(str, TRUE);
2155 if (tmp[0] != '*' || tmp[1] != ' ') {
2156 log_print("IMAP4< %s\n", tmp);
2160 if (strstr(tmp, "FETCH") == NULL) {
2161 log_print("IMAP4< %s\n", tmp);
2165 log_print("IMAP4< %s\n", tmp);
2166 g_string_assign(str, tmp);
2171 msginfo = imap_parse_envelope
2172 (SESSION(session)->sock, item, str);
2174 log_warning(_("can't parse envelope: %s\n"), str->str);
2177 if (item->stype == F_QUEUE) {
2178 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_QUEUED);
2179 } else if (item->stype == F_DRAFT) {
2180 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_DRAFT);
2183 msginfo->folder = item;
2186 llast = newlist = g_slist_append(newlist, msginfo);
2188 llast = g_slist_append(llast, msginfo);
2189 llast = llast->next;
2193 g_string_free(str, TRUE);
2195 imap_seq_set_free(seq_list);
2197 session_set_access_time(SESSION(session));
2203 static GSList *imap_get_uncached_messages(IMAPSession *session,
2205 MsgNumberList *numlist)
2207 uncached_data *data = g_new0(uncached_data, 1);
2208 GSList *result = NULL;
2214 data->session = session;
2216 data->numlist = numlist;
2220 if (prefs_common.work_offline && !imap_gtk_should_override()) {
2225 #if (defined USE_PTHREAD && defined __GLIBC__ && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 3)))
2226 MUTEX_TRYLOCK_OR_RETURN_VAL(NULL);
2228 if (pthread_create(&pt, PTHREAD_CREATE_JOINABLE,
2229 imap_get_uncached_messages_thread, data) != 0) {
2230 result = (GSList *)imap_get_uncached_messages_thread(data);
2235 debug_print("+++waiting for imap_get_uncached_messages_thread...\n");
2236 statusbar_print_all(_("IMAP4 Fetching uncached short headers..."));
2237 while(!data->done) {
2238 /* don't let the interface freeze while waiting */
2240 if (data->total != 0 && last_cur != data->cur && data->cur % 10 == 0) {
2242 g_snprintf(buf, sizeof(buf), "%d / %d",
2243 data->cur, data->total);
2244 gtk_progress_bar_set_text
2245 (GTK_PROGRESS_BAR(mainwindow_get_mainwindow()->progressbar), buf);
2246 gtk_progress_bar_set_fraction
2247 (GTK_PROGRESS_BAR(mainwindow_get_mainwindow()->progressbar),
2248 (gfloat)data->cur / (gfloat)data->total);
2249 last_cur = data->cur;
2252 gtk_progress_bar_set_fraction
2253 (GTK_PROGRESS_BAR(mainwindow_get_mainwindow()->progressbar), 0);
2254 gtk_progress_bar_set_text
2255 (GTK_PROGRESS_BAR(mainwindow_get_mainwindow()->progressbar), "");
2256 statusbar_pop_all();
2258 debug_print("---imap_get_uncached_messages_thread done\n");
2260 /* get the thread's return value and clean its resources */
2261 pthread_join(pt, (void *)&result);
2264 result = (GSList *)imap_get_uncached_messages_thread(data);
2270 static void imap_delete_all_cached_messages(FolderItem *item)
2274 g_return_if_fail(item != NULL);
2275 g_return_if_fail(item->folder != NULL);
2276 g_return_if_fail(FOLDER_CLASS(item->folder) == &imap_class);
2278 debug_print("Deleting all cached messages...\n");
2280 dir = folder_item_get_path(item);
2281 if (is_dir_exist(dir))
2282 remove_all_numbered_files(dir);
2285 debug_print("done.\n");
2289 static SockInfo *imap_open_tunnel(const gchar *server,
2290 const gchar *tunnelcmd,
2293 static SockInfo *imap_open_tunnel(const gchar *server,
2294 const gchar *tunnelcmd)
2299 if ((sock = sock_connect_cmd(server, tunnelcmd)) == NULL) {
2300 log_warning(_("Can't establish IMAP4 session with: %s\n"),
2305 return imap_init_sock(sock, ssl_type);
2307 return imap_init_sock(sock);
2311 void *imap_open_thread(void *data)
2313 SockInfo *sock = NULL;
2314 thread_data *td = (thread_data *)data;
2315 if ((sock = sock_connect(td->server, td->port)) == NULL) {
2316 log_warning(_("Can't connect to IMAP4 server: %s:%d\n"),
2317 td->server, td->port);
2327 static SockInfo *imap_open(const gchar *server, gushort port,
2330 static SockInfo *imap_open(const gchar *server, gushort port)
2333 thread_data *td = g_new0(thread_data, 1);
2337 SockInfo *sock = NULL;
2340 td->ssl_type = ssl_type;
2342 td->server = g_strdup(server);
2346 statusbar_print_all(_("Connecting to IMAP4 server: %s..."), server);
2348 #if (defined USE_PTHREAD && defined __GLIBC__ && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 3)))
2349 if (pthread_create(&pt, PTHREAD_CREATE_JOINABLE,
2350 imap_open_thread, td) != 0) {
2351 statusbar_pop_all();
2352 sock = imap_open_thread(td);
2354 debug_print("+++waiting for imap_open_thread...\n");
2356 /* don't let the interface freeze while waiting */
2360 /* get the thread's return value and clean its resources */
2361 pthread_join(pt, (void *)&sock);
2364 sock = imap_open_thread(td);
2367 if (sock && td->ssl_type == SSL_TUNNEL && !ssl_init_socket(sock)) {
2368 log_warning(_("Can't establish IMAP4 session with: %s:%d\n"),
2369 td->server, td->port);
2378 debug_print("---imap_open_thread returned %p\n", sock);
2379 statusbar_pop_all();
2381 if(!sock && !prefs_common.no_recv_err_panel) {
2382 alertpanel_error(_("Can't connect to IMAP4 server: %s:%d"),
2390 static SockInfo *imap_init_sock(SockInfo *sock, SSLType ssl_type)
2392 static SockInfo *imap_init_sock(SockInfo *sock)
2399 static GList *imap_parse_namespace_str(gchar *str)
2404 IMAPNameSpace *namespace;
2405 GList *ns_list = NULL;
2407 while (*p != '\0') {
2408 /* parse ("#foo" "/") */
2410 while (*p && *p != '(') p++;
2411 if (*p == '\0') break;
2414 while (*p && *p != '"') p++;
2415 if (*p == '\0') break;
2419 while (*p && *p != '"') p++;
2420 if (*p == '\0') break;
2424 while (*p && isspace(*p)) p++;
2425 if (*p == '\0') break;
2426 if (strncmp(p, "NIL", 3) == 0)
2428 else if (*p == '"') {
2431 while (*p && *p != '"') p++;
2432 if (*p == '\0') break;
2437 while (*p && *p != ')') p++;
2438 if (*p == '\0') break;
2441 namespace = g_new(IMAPNameSpace, 1);
2442 namespace->name = g_strdup(name);
2443 namespace->separator = separator ? separator[0] : '\0';
2444 ns_list = g_list_append(ns_list, namespace);
2450 static void imap_parse_namespace(IMAPSession *session, IMAPFolder *folder)
2455 g_return_if_fail(session != NULL);
2456 g_return_if_fail(folder != NULL);
2458 if (folder->ns_personal != NULL ||
2459 folder->ns_others != NULL ||
2460 folder->ns_shared != NULL)
2463 if (!imap_has_capability(session, "NAMESPACE")) {
2464 imap_get_namespace_by_list(session, folder);
2468 if (imap_cmd_namespace(session, &ns_str)
2470 log_warning(_("can't get namespace\n"));
2474 str_array = strsplit_parenthesis(ns_str, '(', ')', 3);
2475 if (str_array == NULL) {
2477 imap_get_namespace_by_list(session, folder);
2481 folder->ns_personal = imap_parse_namespace_str(str_array[0]);
2482 if (str_array[0] && str_array[1])
2483 folder->ns_others = imap_parse_namespace_str(str_array[1]);
2484 if (str_array[0] && str_array[1] && str_array[2])
2485 folder->ns_shared = imap_parse_namespace_str(str_array[2]);
2486 g_strfreev(str_array);
2490 static void imap_get_namespace_by_list(IMAPSession *session, IMAPFolder *folder)
2492 GSList *item_list, *cur;
2493 gchar separator = '\0';
2494 IMAPNameSpace *namespace;
2496 g_return_if_fail(session != NULL);
2497 g_return_if_fail(folder != NULL);
2499 if (folder->ns_personal != NULL ||
2500 folder->ns_others != NULL ||
2501 folder->ns_shared != NULL)
2504 imap_gen_send(session, "LIST \"\" \"\"");
2505 item_list = imap_parse_list(folder, session, "", &separator);
2506 for (cur = item_list; cur != NULL; cur = cur->next)
2507 folder_item_destroy(FOLDER_ITEM(cur->data));
2508 g_slist_free(item_list);
2510 namespace = g_new(IMAPNameSpace, 1);
2511 namespace->name = g_strdup("");
2512 namespace->separator = separator;
2513 folder->ns_personal = g_list_append(NULL, namespace);
2516 static IMAPNameSpace *imap_find_namespace_from_list(GList *ns_list,
2519 IMAPNameSpace *namespace = NULL;
2520 gchar *tmp_path, *name;
2522 if (!path) path = "";
2524 for (; ns_list != NULL; ns_list = ns_list->next) {
2525 IMAPNameSpace *tmp_ns = ns_list->data;
2527 Xstrcat_a(tmp_path, path, "/", return namespace);
2528 Xstrdup_a(name, tmp_ns->name, return namespace);
2529 if (tmp_ns->separator && tmp_ns->separator != '/') {
2530 subst_char(tmp_path, tmp_ns->separator, '/');
2531 subst_char(name, tmp_ns->separator, '/');
2533 if (strncmp(tmp_path, name, strlen(name)) == 0)
2540 static IMAPNameSpace *imap_find_namespace(IMAPFolder *folder,
2543 IMAPNameSpace *namespace;
2545 g_return_val_if_fail(folder != NULL, NULL);
2547 namespace = imap_find_namespace_from_list(folder->ns_personal, path);
2548 if (namespace) return namespace;
2549 namespace = imap_find_namespace_from_list(folder->ns_others, path);
2550 if (namespace) return namespace;
2551 namespace = imap_find_namespace_from_list(folder->ns_shared, path);
2552 if (namespace) return namespace;
2557 static gchar imap_get_path_separator(IMAPFolder *folder, const gchar *path)
2559 IMAPNameSpace *namespace;
2560 gchar separator = '/';
2562 namespace = imap_find_namespace(folder, path);
2563 if (namespace && namespace->separator)
2564 separator = namespace->separator;
2569 static gchar *imap_get_real_path(IMAPFolder *folder, const gchar *path)
2574 g_return_val_if_fail(folder != NULL, NULL);
2575 g_return_val_if_fail(path != NULL, NULL);
2577 real_path = imap_utf8_to_modified_utf7(path);
2578 separator = imap_get_path_separator(folder, path);
2579 imap_path_separator_subst(real_path, separator);
2584 static gchar *imap_parse_atom(SockInfo *sock, gchar *src,
2585 gchar *dest, gint dest_len, GString *str)
2587 gchar *cur_pos = src;
2590 g_return_val_if_fail(str != NULL, cur_pos);
2592 /* read the next line if the current response buffer is empty */
2593 while (isspace(*(guchar *)cur_pos)) cur_pos++;
2594 while (*cur_pos == '\0') {
2595 if ((nextline = imap_getline(sock)) == NULL)
2597 g_string_assign(str, nextline);
2599 strretchomp(nextline);
2600 /* log_print("IMAP4< %s\n", nextline); */
2601 debug_print("IMAP4< %s\n", nextline);
2604 while (isspace(*(guchar *)cur_pos)) cur_pos++;
2607 if (!strncmp(cur_pos, "NIL", 3)) {
2610 } else if (*cur_pos == '\"') {
2613 p = get_quoted(cur_pos, '\"', dest, dest_len);
2614 cur_pos = p ? p : cur_pos + 2;
2615 } else if (*cur_pos == '{') {
2620 cur_pos = strchr_cpy(cur_pos + 1, '}', buf, sizeof(buf));
2622 g_return_val_if_fail(len >= 0, cur_pos);
2624 g_string_truncate(str, 0);
2628 if ((nextline = imap_getline(sock)) == NULL)
2630 line_len += strlen(nextline);
2631 g_string_append(str, nextline);
2633 strretchomp(nextline);
2634 /* log_print("IMAP4< %s\n", nextline); */
2635 debug_print("IMAP4< %s\n", nextline);
2637 } while (line_len < len);
2639 memcpy(dest, cur_pos, MIN(len, dest_len - 1));
2640 dest[MIN(len, dest_len - 1)] = '\0';
2647 static gchar *imap_get_header(SockInfo *sock, gchar *cur_pos, gchar **headers,
2657 g_return_val_if_fail(str != NULL, cur_pos);
2659 while (isspace(*(guchar *)cur_pos)) cur_pos++;
2661 g_return_val_if_fail(*cur_pos == '{', cur_pos);
2663 cur_pos = strchr_cpy(cur_pos + 1, '}', buf, sizeof(buf));
2665 g_return_val_if_fail(len >= 0, cur_pos);
2667 g_string_truncate(str, 0);
2671 if ((nextline = sock_getline(sock)) == NULL) {
2675 block_len += strlen(nextline);
2676 g_string_append(str, nextline);
2678 strretchomp(nextline);
2679 /* debug_print("IMAP4< %s\n", nextline); */
2681 } while (block_len < len);
2683 debug_print("IMAP4< [contents of BODY.PEEK[HEADER_FIELDS (...)]\n");
2685 *headers = g_strndup(cur_pos, len);
2688 while (isspace(*(guchar *)cur_pos)) cur_pos++;
2689 while (*cur_pos == '\0') {
2690 if ((nextline = sock_getline(sock)) == NULL)
2692 g_string_assign(str, nextline);
2694 strretchomp(nextline);
2695 debug_print("IMAP4< %s\n", nextline);
2698 while (isspace(*(guchar *)cur_pos)) cur_pos++;
2704 static MsgFlags imap_parse_flags(const gchar *flag_str)
2706 const gchar *p = flag_str;
2707 MsgFlags flags = {0, 0};
2709 flags.perm_flags = MSG_UNREAD;
2711 while ((p = strchr(p, '\\')) != NULL) {
2714 if (g_ascii_strncasecmp(p, "Recent", 6) == 0 && MSG_IS_UNREAD(flags)) {
2715 MSG_SET_PERM_FLAGS(flags, MSG_NEW);
2716 } else if (g_ascii_strncasecmp(p, "Seen", 4) == 0) {
2717 MSG_UNSET_PERM_FLAGS(flags, MSG_NEW|MSG_UNREAD);
2718 } else if (g_ascii_strncasecmp(p, "Deleted", 7) == 0) {
2719 MSG_SET_PERM_FLAGS(flags, MSG_DELETED);
2720 } else if (g_ascii_strncasecmp(p, "Flagged", 7) == 0) {
2721 MSG_SET_PERM_FLAGS(flags, MSG_MARKED);
2722 } else if (g_ascii_strncasecmp(p, "Answered", 8) == 0) {
2723 MSG_SET_PERM_FLAGS(flags, MSG_REPLIED);
2730 static MsgInfo *imap_parse_envelope(SockInfo *sock, FolderItem *item,
2733 gchar buf[IMAPBUFSIZE];
2734 MsgInfo *msginfo = NULL;
2739 MsgFlags flags = {0, 0}, imap_flags = {0, 0};
2741 g_return_val_if_fail(line_str != NULL, NULL);
2742 g_return_val_if_fail(line_str->str[0] == '*' &&
2743 line_str->str[1] == ' ', NULL);
2745 MSG_SET_TMP_FLAGS(flags, MSG_IMAP);
2746 if (item->stype == F_QUEUE) {
2747 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
2748 } else if (item->stype == F_DRAFT) {
2749 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
2752 cur_pos = line_str->str + 2;
2754 #define PARSE_ONE_ELEMENT(ch) \
2756 cur_pos = strchr_cpy(cur_pos, ch, buf, sizeof(buf)); \
2757 if (cur_pos == NULL) { \
2758 g_warning("cur_pos == NULL\n"); \
2759 procmsg_msginfo_free(msginfo); \
2764 PARSE_ONE_ELEMENT(' ');
2767 PARSE_ONE_ELEMENT(' ');
2768 g_return_val_if_fail(!strcmp(buf, "FETCH"), NULL);
2770 g_return_val_if_fail(*cur_pos == '(', NULL);
2773 while (*cur_pos != '\0' && *cur_pos != ')') {
2774 while (*cur_pos == ' ') cur_pos++;
2776 if (!strncmp(cur_pos, "UID ", 4)) {
2778 uid = strtoul(cur_pos, &cur_pos, 10);
2779 } else if (!strncmp(cur_pos, "FLAGS ", 6)) {
2781 if (*cur_pos != '(') {
2782 g_warning("*cur_pos != '('\n");
2783 procmsg_msginfo_free(msginfo);
2787 PARSE_ONE_ELEMENT(')');
2788 imap_flags = imap_parse_flags(buf);
2789 } else if (!strncmp(cur_pos, "RFC822.SIZE ", 12)) {
2791 size = strtol(cur_pos, &cur_pos, 10);
2792 } else if (!strncmp(cur_pos, "BODY[HEADER.FIELDS ", 19)) {
2796 if (*cur_pos != '(') {
2797 g_warning("*cur_pos != '('\n");
2798 procmsg_msginfo_free(msginfo);
2802 PARSE_ONE_ELEMENT(')');
2803 if (*cur_pos != ']') {
2804 g_warning("*cur_pos != ']'\n");
2805 procmsg_msginfo_free(msginfo);
2809 cur_pos = imap_get_header(sock, cur_pos, &headers,
2811 msginfo = procheader_parse_str(headers, flags, FALSE, FALSE);
2814 g_warning("invalid FETCH response: %s\n", cur_pos);
2820 msginfo->msgnum = uid;
2821 msginfo->size = size;
2822 msginfo->flags.tmp_flags |= imap_flags.tmp_flags;
2823 msginfo->flags.perm_flags = imap_flags.perm_flags;
2829 static gchar *imap_get_flag_str(IMAPFlags flags)
2834 str = g_string_new(NULL);
2836 if (IMAP_IS_SEEN(flags)) g_string_append(str, "\\Seen ");
2837 if (IMAP_IS_ANSWERED(flags)) g_string_append(str, "\\Answered ");
2838 if (IMAP_IS_FLAGGED(flags)) g_string_append(str, "\\Flagged ");
2839 if (IMAP_IS_DELETED(flags)) g_string_append(str, "\\Deleted ");
2840 if (IMAP_IS_DRAFT(flags)) g_string_append(str, "\\Draft");
2842 if (str->len > 0 && str->str[str->len - 1] == ' ')
2843 g_string_truncate(str, str->len - 1);
2846 g_string_free(str, FALSE);
2851 static gint imap_set_message_flags(IMAPSession *session,
2852 MsgNumberList *numlist,
2862 flag_str = imap_get_flag_str(flags);
2863 cmd = g_strconcat(is_set ? "+FLAGS.SILENT (" : "-FLAGS.SILENT (",
2864 flag_str, ")", NULL);
2867 seq_list = imap_get_seq_set_from_numlist(numlist);
2868 imapset = get_seq_set_from_seq_list(seq_list);
2870 ok = imap_cmd_store(session, imapset, cmd);
2873 imap_seq_set_free(seq_list);
2879 typedef struct _select_data {
2880 IMAPSession *session;
2885 guint32 *uid_validity;
2889 static void *imap_select_thread(void *data)
2891 select_data *stuff = (select_data *)data;
2892 IMAPSession *session = stuff->session;
2893 gchar *real_path = stuff->real_path;
2894 gint *exists = stuff->exists;
2895 gint *recent = stuff->recent;
2896 gint *unseen = stuff->unseen;
2897 guint32 *uid_validity = stuff->uid_validity;
2900 ok = imap_cmd_select(session, real_path,
2901 exists, recent, unseen, uid_validity, TRUE);
2903 return GINT_TO_POINTER(ok);
2906 static gint imap_select(IMAPSession *session, IMAPFolder *folder,
2908 gint *exists, gint *recent, gint *unseen,
2909 guint32 *uid_validity, gboolean block)
2913 gint exists_, recent_, unseen_;
2914 guint32 uid_validity_;
2916 if (!exists || !recent || !unseen || !uid_validity) {
2917 if (session->mbox && strcmp(session->mbox, path) == 0)
2918 return IMAP_SUCCESS;
2922 uid_validity = &uid_validity_;
2925 g_free(session->mbox);
2926 session->mbox = NULL;
2928 real_path = imap_get_real_path(folder, path);
2930 #if (defined USE_PTHREAD && defined __GLIBC__ && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 3)))
2932 if (block == FALSE) {
2933 select_data *data = g_new0(select_data, 1);
2936 data->session = session;
2937 data->real_path = real_path;
2938 data->exists = exists;
2939 data->recent = recent;
2940 data->unseen = unseen;
2941 data->uid_validity = uid_validity;
2944 if (pthread_create(&pt, PTHREAD_CREATE_JOINABLE,
2945 imap_select_thread, data) != 0) {
2946 ok = GPOINTER_TO_INT(imap_select_thread(data));
2949 debug_print("+++waiting for imap_select_thread...\n");
2950 while(!data->done) {
2951 /* don't let the interface freeze while waiting */
2954 debug_print("---imap_select_thread done\n");
2956 /* get the thread's return value and clean its resources */
2957 pthread_join(pt, &tmp);
2958 ok = GPOINTER_TO_INT(tmp);
2962 ok = imap_cmd_select(session, real_path,
2963 exists, recent, unseen, uid_validity, block);
2966 ok = imap_cmd_select(session, real_path,
2967 exists, recent, unseen, uid_validity, block);
2969 if (ok != IMAP_SUCCESS)
2970 log_warning(_("can't select folder: %s\n"), real_path);
2972 session->mbox = g_strdup(path);
2973 session->folder_content_changed = FALSE;
2980 #define THROW(err) { ok = err; goto catch; }
2982 static gint imap_status(IMAPSession *session, IMAPFolder *folder,
2984 gint *messages, gint *recent,
2985 guint32 *uid_next, guint32 *uid_validity,
2986 gint *unseen, gboolean block)
2991 GPtrArray *argbuf = NULL;
2994 if (messages && recent && uid_next && uid_validity && unseen) {
2995 *messages = *recent = *uid_next = *uid_validity = *unseen = 0;
2996 argbuf = g_ptr_array_new();
2999 real_path = imap_get_real_path(folder, path);
3000 QUOTE_IF_REQUIRED(real_path_, real_path);
3001 imap_gen_send(session, "STATUS %s "
3002 "(MESSAGES RECENT UIDNEXT UIDVALIDITY UNSEEN)",
3005 ok = imap_cmd_ok_with_block(session, argbuf, block);
3006 if (ok != IMAP_SUCCESS || !argbuf) THROW(ok);
3008 str = search_array_str(argbuf, "STATUS");
3009 if (!str) THROW(IMAP_ERROR);
3011 str = strrchr(str, '(');
3012 if (!str) THROW(IMAP_ERROR);
3014 while (*str != '\0' && *str != ')') {
3015 while (*str == ' ') str++;
3017 if (!strncmp(str, "MESSAGES ", 9)) {
3019 *messages = strtol(str, &str, 10);
3020 } else if (!strncmp(str, "RECENT ", 7)) {
3022 *recent = strtol(str, &str, 10);
3023 } else if (!strncmp(str, "UIDNEXT ", 8)) {
3025 *uid_next = strtoul(str, &str, 10);
3026 } else if (!strncmp(str, "UIDVALIDITY ", 12)) {
3028 *uid_validity = strtoul(str, &str, 10);
3029 } else if (!strncmp(str, "UNSEEN ", 7)) {
3031 *unseen = strtol(str, &str, 10);
3033 g_warning("invalid STATUS response: %s\n", str);
3041 ptr_array_free_strings(argbuf);
3042 g_ptr_array_free(argbuf, TRUE);
3050 static gboolean imap_has_capability(IMAPSession *session, const gchar *cap)
3054 for (p = session->capability; *p != NULL; ++p) {
3055 if (!g_ascii_strcasecmp(*p, cap))
3062 static void imap_free_capabilities(IMAPSession *session)
3064 g_strfreev(session->capability);
3065 session->capability = NULL;
3068 /* low-level IMAP4rev1 commands */
3070 static gint imap_cmd_authenticate(IMAPSession *session, const gchar *user,
3071 const gchar *pass, IMAPAuthType type)
3078 gchar hexdigest[33];
3082 auth_type = "CRAM-MD5";
3084 imap_gen_send(session, "AUTHENTICATE %s", auth_type);
3085 ok = imap_gen_recv(session, &buf);
3086 if (ok != IMAP_SUCCESS || buf[0] != '+' || buf[1] != ' ') {
3091 challenge = g_malloc(strlen(buf + 2) + 1);
3092 challenge_len = base64_decode(challenge, buf + 2, -1);
3093 challenge[challenge_len] = '\0';
3095 log_print("IMAP< [Decoded: %s]\n", challenge);
3097 md5_hex_hmac(hexdigest, challenge, challenge_len, pass, strlen(pass));
3100 response = g_strdup_printf("%s %s", user, hexdigest);
3101 log_print("IMAP> [Encoded: %s]\n", response);
3102 response64 = g_malloc((strlen(response) + 3) * 2 + 1);
3103 base64_encode(response64, response, strlen(response));
3106 log_print("IMAP> %s\n", response64);
3107 sock_puts(SESSION(session)->sock, response64);
3108 ok = imap_cmd_ok(session, NULL);
3109 if (ok != IMAP_SUCCESS)
3110 log_warning(_("IMAP4 authentication failed.\n"));
3115 static gint imap_cmd_login(IMAPSession *session,
3116 const gchar *user, const gchar *pass)
3121 imap_gen_send(session, "LOGIN {%d}\r\n%s {%d}\r\n%s",
3123 strlen(pass), pass);
3125 ok = imap_gen_recv_with_block(session, &ans, TRUE);
3126 if (ok != IMAP_SUCCESS || ans[0] != '+' || ans[1] != ' ') {
3131 ok = imap_gen_recv_with_block(session, &ans, TRUE);
3132 if (ok != IMAP_SUCCESS || ans[0] != '+' || ans[1] != ' ') {
3137 ok = imap_cmd_ok(session, NULL);
3138 if (ok != IMAP_SUCCESS)
3139 log_warning(_("IMAP4 login failed.\n"));
3144 static gint imap_cmd_logout(IMAPSession *session)
3146 imap_gen_send(session, "LOGOUT");
3147 return imap_cmd_ok(session, NULL);
3150 static gint imap_cmd_noop(IMAPSession *session)
3152 imap_gen_send(session, "NOOP");
3153 return imap_cmd_ok(session, NULL);
3157 static gint imap_cmd_starttls(IMAPSession *session)
3159 imap_gen_send(session, "STARTTLS");
3160 return imap_cmd_ok(session, NULL);
3164 #define THROW(err) { ok = err; goto catch; }
3166 static gint imap_cmd_namespace(IMAPSession *session, gchar **ns_str)
3172 argbuf = g_ptr_array_new();
3174 imap_gen_send(session, "NAMESPACE");
3175 if ((ok = imap_cmd_ok(session, argbuf)) != IMAP_SUCCESS) THROW(ok);
3177 str = search_array_str(argbuf, "NAMESPACE");
3178 if (!str) THROW(IMAP_ERROR);
3180 *ns_str = g_strdup(str);
3183 ptr_array_free_strings(argbuf);
3184 g_ptr_array_free(argbuf, TRUE);
3191 static gint imap_cmd_list(IMAPSession *session, const gchar *ref,
3192 const gchar *mailbox, GPtrArray *argbuf)
3194 gchar *ref_, *mailbox_;
3196 if (!ref) ref = "\"\"";
3197 if (!mailbox) mailbox = "\"\"";
3199 QUOTE_IF_REQUIRED(ref_, ref);
3200 QUOTE_IF_REQUIRED(mailbox_, mailbox);
3201 imap_gen_send(session, "LIST %s %s", ref_, mailbox_);
3203 return imap_cmd_ok(session, argbuf);
3206 #define THROW goto catch
3208 static gint imap_cmd_do_select(IMAPSession *session, const gchar *folder,
3210 gint *exists, gint *recent, gint *unseen,
3211 guint32 *uid_validity, gboolean block)
3218 unsigned int uid_validity_;
3220 *exists = *recent = *unseen = *uid_validity = 0;
3221 argbuf = g_ptr_array_new();
3224 select_cmd = "EXAMINE";
3226 select_cmd = "SELECT";
3228 QUOTE_IF_REQUIRED(folder_, folder);
3229 imap_gen_send(session, "%s %s", select_cmd, folder_);
3231 if ((ok = imap_cmd_ok_with_block(session, argbuf, block)) != IMAP_SUCCESS) THROW;
3233 resp_str = search_array_contain_str(argbuf, "EXISTS");
3235 if (sscanf(resp_str,"%d EXISTS", exists) != 1) {
3236 g_warning("imap_cmd_select(): invalid EXISTS line.\n");
3241 resp_str = search_array_contain_str(argbuf, "RECENT");
3243 if (sscanf(resp_str, "%d RECENT", recent) != 1) {
3244 g_warning("imap_cmd_select(): invalid RECENT line.\n");
3249 resp_str = search_array_contain_str(argbuf, "UIDVALIDITY");
3251 if (sscanf(resp_str, "OK [UIDVALIDITY %u] ", &uid_validity_)
3253 g_warning("imap_cmd_select(): invalid UIDVALIDITY line.\n");
3256 *uid_validity = uid_validity_;
3259 resp_str = search_array_contain_str(argbuf, "UNSEEN");
3261 if (sscanf(resp_str, "OK [UNSEEN %d] ", unseen) != 1) {
3262 g_warning("imap_cmd_select(): invalid UNSEEN line.\n");
3268 ptr_array_free_strings(argbuf);
3269 g_ptr_array_free(argbuf, TRUE);
3274 static gint imap_cmd_select(IMAPSession *session, const gchar *folder,
3275 gint *exists, gint *recent, gint *unseen,
3276 guint32 *uid_validity, gboolean block)
3278 return imap_cmd_do_select(session, folder, FALSE,
3279 exists, recent, unseen, uid_validity, block);
3282 static gint imap_cmd_examine(IMAPSession *session, const gchar *folder,
3283 gint *exists, gint *recent, gint *unseen,
3284 guint32 *uid_validity, gboolean block)
3286 return imap_cmd_do_select(session, folder, TRUE,
3287 exists, recent, unseen, uid_validity, block);
3292 static gint imap_cmd_create(IMAPSession *session, const gchar *folder)
3296 QUOTE_IF_REQUIRED(folder_, folder);
3297 imap_gen_send(session, "CREATE %s", folder_);
3299 return imap_cmd_ok(session, NULL);
3302 static gint imap_cmd_rename(IMAPSession *session, const gchar *old_folder,
3303 const gchar *new_folder)
3305 gchar *old_folder_, *new_folder_;
3307 QUOTE_IF_REQUIRED(old_folder_, old_folder);
3308 QUOTE_IF_REQUIRED(new_folder_, new_folder);
3309 imap_gen_send(session, "RENAME %s %s", old_folder_, new_folder_);
3311 return imap_cmd_ok(session, NULL);
3314 static gint imap_cmd_delete(IMAPSession *session, const gchar *folder)
3318 QUOTE_IF_REQUIRED(folder_, folder);
3319 imap_gen_send(session, "DELETE %s", folder_);
3321 return imap_cmd_ok(session, NULL);
3324 static gint imap_cmd_search(IMAPSession *session, const gchar *criteria,
3325 GSList **list, gboolean block)
3331 g_return_val_if_fail(criteria != NULL, IMAP_ERROR);
3332 g_return_val_if_fail(list != NULL, IMAP_ERROR);
3336 argbuf = g_ptr_array_new();
3337 imap_gen_send(session, "UID SEARCH %s", criteria);
3339 ok = imap_cmd_ok_with_block(session, argbuf, block);
3340 if (ok != IMAP_SUCCESS) {
3341 ptr_array_free_strings(argbuf);
3342 g_ptr_array_free(argbuf, TRUE);
3346 if ((uidlist = search_array_str(argbuf, "SEARCH ")) != NULL) {
3347 gchar **strlist, **p;
3349 strlist = g_strsplit(uidlist + 7, " ", 0);
3350 for (p = strlist; *p != NULL; ++p) {
3353 if (sscanf(*p, "%u", &msgnum) == 1)
3354 *list = g_slist_append(*list, GINT_TO_POINTER(msgnum));
3356 g_strfreev(strlist);
3358 ptr_array_free_strings(argbuf);
3359 g_ptr_array_free(argbuf, TRUE);
3361 return IMAP_SUCCESS;
3364 typedef struct _fetch_data {
3365 IMAPSession *session;
3367 const gchar *filename;
3373 static void *imap_cmd_fetch_thread(void *data)
3375 fetch_data *stuff = (fetch_data *)data;
3376 IMAPSession *session = stuff->session;
3377 guint32 uid = stuff->uid;
3378 const gchar *filename = stuff->filename;
3386 if (filename == NULL) {
3388 return GINT_TO_POINTER(IMAP_ERROR);
3392 imap_gen_send(session, "UID FETCH %d BODY.PEEK[]", uid);
3394 imap_gen_send(session, "UID FETCH %d BODY.PEEK[HEADER]",
3396 while ((ok = imap_gen_recv_block(session, &buf)) == IMAP_SUCCESS) {
3397 if (buf[0] != '*' || buf[1] != ' ') {
3400 return GINT_TO_POINTER(IMAP_ERROR);
3402 if (strstr(buf, "FETCH") != NULL) break;
3405 if (ok != IMAP_SUCCESS) {
3408 return GINT_TO_POINTER(ok);
3411 #define RETURN_ERROR_IF_FAIL(cond) \
3414 stuff->done = TRUE; \
3415 return GINT_TO_POINTER(IMAP_ERROR); \
3418 cur_pos = strchr(buf, '{');
3419 RETURN_ERROR_IF_FAIL(cur_pos != NULL);
3420 cur_pos = strchr_cpy(cur_pos + 1, '}', size_str, sizeof(size_str));
3421 RETURN_ERROR_IF_FAIL(cur_pos != NULL);
3422 size_num = atol(size_str);
3423 RETURN_ERROR_IF_FAIL(size_num >= 0);
3425 RETURN_ERROR_IF_FAIL(*cur_pos == '\0');
3427 #undef RETURN_ERROR_IF_FAIL
3431 if (recv_bytes_write_to_file(SESSION(session)->sock,
3432 size_num, filename) != 0) {
3434 return GINT_TO_POINTER(IMAP_ERROR);
3436 if (imap_gen_recv_block(session, &buf) != IMAP_SUCCESS) {
3439 return GINT_TO_POINTER(IMAP_ERROR);
3442 if (buf[0] == '\0' || buf[strlen(buf) - 1] != ')') {
3445 return GINT_TO_POINTER(IMAP_ERROR);
3449 ok = imap_cmd_ok_block(session, NULL);
3452 return GINT_TO_POINTER(ok);
3455 static gint imap_cmd_fetch(IMAPSession *session, guint32 uid,
3456 const gchar *filename, gboolean headers,
3459 fetch_data *data = g_new0(fetch_data, 1);
3466 data->session = session;
3468 data->filename = filename;
3469 data->headers = headers;
3472 if (prefs_common.work_offline && !imap_gtk_should_override()) {
3477 #if (defined USE_PTHREAD && defined __GLIBC__ && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 3)))
3478 MUTEX_TRYLOCK_OR_RETURN_VAL(-1);
3479 if (pthread_create(&pt, PTHREAD_CREATE_JOINABLE,
3480 imap_cmd_fetch_thread, data) != 0) {
3481 result = GPOINTER_TO_INT(imap_cmd_fetch_thread(data));
3486 debug_print("+++waiting for imap_cmd_fetch_thread...\n");
3487 while(!data->done) {
3488 /* don't let the interface freeze while waiting */
3491 debug_print("---imap_cmd_fetch_thread done\n");
3493 /* get the thread's return value and clean its resources */
3494 pthread_join(pt, &tmp);
3495 result = GPOINTER_TO_INT(tmp);
3498 result = GPOINTER_TO_INT(imap_cmd_fetch_thread(data));
3504 static gint imap_cmd_append(IMAPSession *session, const gchar *destfolder,
3505 const gchar *file, IMAPFlags flags,
3512 unsigned int new_uid_;
3514 gchar buf[BUFFSIZE];
3519 g_return_val_if_fail(file != NULL, IMAP_ERROR);
3521 size = get_file_size_as_crlf(file);
3522 if ((fp = fopen(file, "rb")) == NULL) {
3523 FILE_OP_ERROR(file, "fopen");
3526 QUOTE_IF_REQUIRED(destfolder_, destfolder);
3527 flag_str = imap_get_flag_str(flags);
3528 imap_gen_send(session, "APPEND %s (%s) {%d}",
3529 destfolder_, flag_str, size);
3532 ok = imap_gen_recv(session, &ret);
3533 if (ok != IMAP_SUCCESS || ret[0] != '+' || ret[1] != ' ') {
3534 log_warning(_("can't append %s to %s\n"), file, destfolder_);
3541 log_print("IMAP4> %s\n", "(sending file...)");
3543 while (fgets(buf, sizeof(buf), fp) != NULL) {
3545 if (sock_puts(SESSION(session)->sock, buf) < 0) {
3552 FILE_OP_ERROR(file, "fgets");
3557 sock_puts(SESSION(session)->sock, "");
3561 if (new_uid != NULL)
3564 if (new_uid != NULL && session->uidplus) {
3565 argbuf = g_ptr_array_new();
3567 ok = imap_cmd_ok(session, argbuf);
3568 if ((ok == IMAP_SUCCESS) && (argbuf->len > 0)) {
3569 resp_str = g_ptr_array_index(argbuf, argbuf->len - 1);
3571 sscanf(resp_str, "%*u OK [APPENDUID %*u %u]",
3573 *new_uid = new_uid_;
3577 ptr_array_free_strings(argbuf);
3578 g_ptr_array_free(argbuf, TRUE);
3580 ok = imap_cmd_ok(session, NULL);
3582 if (ok != IMAP_SUCCESS)
3583 log_warning(_("can't append message to %s\n"),
3589 static MsgNumberList *imapset_to_numlist(IMAPSet imapset)
3591 gchar **ranges, **range;
3592 unsigned int low, high;
3593 MsgNumberList *uids = NULL;
3595 ranges = g_strsplit(imapset, ",", 0);
3596 for (range = ranges; *range != NULL; range++) {
3597 if (sscanf(*range, "%u:%u", &low, &high) == 1)
3598 uids = g_slist_prepend(uids, GINT_TO_POINTER(low));
3601 for (i = low; i <= high; i++)
3602 uids = g_slist_prepend(uids, GINT_TO_POINTER(i));
3605 uids = g_slist_reverse(uids);
3611 static gint imap_cmd_copy(IMAPSession *session, const gchar *seq_set,
3612 const gchar *destfolder, GRelation *uid_mapping)
3617 g_return_val_if_fail(session != NULL, IMAP_ERROR);
3618 g_return_val_if_fail(seq_set != NULL, IMAP_ERROR);
3619 g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
3621 QUOTE_IF_REQUIRED(destfolder_, destfolder);
3622 imap_gen_send(session, "UID COPY %s %s", seq_set, destfolder_);
3624 if (uid_mapping != NULL && session->uidplus) {
3626 gchar *resp_str = NULL, *olduids_str, *newuids_str;
3627 MsgNumberList *olduids, *old_cur, *newuids, *new_cur;
3629 reply = g_ptr_array_new();
3630 ok = imap_cmd_ok(session, reply);
3631 if ((ok == IMAP_SUCCESS) && (reply->len > 0)) {
3632 resp_str = g_ptr_array_index(reply, reply->len - 1);
3634 olduids_str = g_new0(gchar, strlen(resp_str));
3635 newuids_str = g_new0(gchar, strlen(resp_str));
3636 if (sscanf(resp_str, "%*s OK [COPYUID %*u %[0-9,:] %[0-9,:]]",
3637 olduids_str, newuids_str) == 2) {
3638 olduids = imapset_to_numlist(olduids_str);
3639 newuids = imapset_to_numlist(newuids_str);
3643 while(old_cur != NULL && new_cur != NULL) {
3644 g_relation_insert(uid_mapping,
3645 GPOINTER_TO_INT(old_cur->data),
3646 GPOINTER_TO_INT(new_cur->data));
3647 old_cur = g_slist_next(old_cur);
3648 new_cur = g_slist_next(new_cur);
3651 g_slist_free(olduids);
3652 g_slist_free(newuids);
3654 g_free(olduids_str);
3655 g_free(newuids_str);
3658 ptr_array_free_strings(reply);
3659 g_ptr_array_free(reply, TRUE);
3661 ok = imap_cmd_ok(session, NULL);
3663 if (ok != IMAP_SUCCESS)
3664 log_warning(_("can't copy %s to %s\n"), seq_set, destfolder_);
3669 gint imap_cmd_envelope(IMAPSession *session, IMAPSet set)
3671 static gchar *header_fields =
3672 "Date From To Cc Subject Message-ID References In-Reply-To" ;
3675 (session, "UID FETCH %s (UID FLAGS RFC822.SIZE BODY.PEEK[HEADER.FIELDS (%s)])",
3676 set, header_fields);
3678 return IMAP_SUCCESS;
3681 static gint imap_cmd_store(IMAPSession *session, IMAPSet seq_set,
3686 imap_gen_send(session, "UID STORE %s %s", seq_set, sub_cmd);
3688 if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS) {
3689 log_warning(_("error while imap command: STORE %s %s\n"),
3694 return IMAP_SUCCESS;
3697 typedef struct _expunge_data {
3698 IMAPSession *session;
3703 static void *imap_cmd_expunge_thread(void *data)
3705 expunge_data *stuff = (expunge_data *)data;
3706 IMAPSession *session = stuff->session;
3707 IMAPSet seq_set = stuff->seq_set;
3711 if (seq_set && session->uidplus)
3712 imap_gen_send(session, "UID EXPUNGE %s", seq_set);
3714 imap_gen_send(session, "EXPUNGE");
3715 if ((ok = imap_cmd_ok_with_block(session, NULL, TRUE)) != IMAP_SUCCESS) {
3716 log_warning(_("error while imap command: EXPUNGE\n"));
3718 return GINT_TO_POINTER(ok);
3722 return GINT_TO_POINTER(IMAP_SUCCESS);
3725 static gint imap_cmd_expunge(IMAPSession *session, IMAPSet seq_set)
3727 expunge_data *data = g_new0(expunge_data, 1);
3734 data->session = session;
3735 data->seq_set = seq_set;
3737 if (prefs_common.work_offline && !imap_gtk_should_override()) {
3742 #if (defined USE_PTHREAD && defined __GLIBC__ && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 3)))
3743 if (pthread_create(&pt, PTHREAD_CREATE_JOINABLE,
3744 imap_cmd_expunge_thread, data) != 0) {
3745 result = GPOINTER_TO_INT(imap_cmd_expunge_thread(data));
3749 debug_print("+++waiting for imap_cmd_expunge_thread...\n");
3750 while(!data->done) {
3751 /* don't let the interface freeze while waiting */
3754 debug_print("---imap_cmd_expunge_thread done\n");
3756 /* get the thread's return value and clean its resources */
3757 pthread_join(pt, &tmp);
3758 result = GPOINTER_TO_INT(tmp);
3760 result = GPOINTER_TO_INT(imap_cmd_expunge_thread(data));
3766 static gint imap_cmd_close(IMAPSession *session)
3770 imap_gen_send(session, "CLOSE");
3771 if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS)
3772 log_warning(_("error while imap command: CLOSE\n"));
3777 static gint imap_cmd_ok_with_block(IMAPSession *session, GPtrArray *argbuf, gboolean block)
3779 gint ok = IMAP_SUCCESS;
3784 while ((ok = imap_gen_recv_with_block(session, &buf, block))
3786 /* make sure data is long enough for any substring of buf */
3787 data = alloca(strlen(buf) + 1);
3789 /* untagged line read */
3790 if (buf[0] == '*' && buf[1] == ' ') {
3793 g_ptr_array_add(argbuf, g_strdup(buf + 2));
3795 if (sscanf(buf + 2, "%d %s", &num, data) >= 2) {
3796 if (!strcmp(data, "EXISTS")) {
3797 session->exists = num;
3798 session->folder_content_changed = TRUE;
3801 if(!strcmp(data, "EXPUNGE")) {
3803 session->folder_content_changed = TRUE;
3806 /* tagged line with correct tag and OK response found */
3807 } else if ((sscanf(buf, "%d %s", &cmd_num, data) >= 2) &&
3808 (cmd_num == session->cmd_count) &&
3809 !strcmp(data, "OK")) {
3811 g_ptr_array_add(argbuf, g_strdup(buf));
3813 /* everything else */
3824 static gint imap_cmd_ok(IMAPSession *session, GPtrArray *argbuf)
3826 return imap_cmd_ok_with_block(session, argbuf, FALSE);
3828 static gint imap_cmd_ok_block(IMAPSession *session, GPtrArray *argbuf)
3830 return imap_cmd_ok_with_block(session, argbuf, TRUE);
3832 static void imap_gen_send(IMAPSession *session, const gchar *format, ...)
3839 va_start(args, format);
3840 tmp = g_strdup_vprintf(format, args);
3843 session->cmd_count++;
3845 buf = g_strdup_printf("%d %s\r\n", session->cmd_count, tmp);
3846 if (!g_ascii_strncasecmp(tmp, "LOGIN ", 6) && (p = strchr(tmp + 6, ' '))) {
3848 log_print("IMAP4> %d %s ********\n", session->cmd_count, tmp);
3850 log_print("IMAP4> %d %s\n", session->cmd_count, tmp);
3852 sock_write_all(SESSION(session)->sock, buf, strlen(buf));
3857 static gint imap_gen_recv_with_block(IMAPSession *session, gchar **ret, gboolean block)
3860 if ((*ret = imap_getline(SESSION(session)->sock)) == NULL)
3863 if ((*ret = sock_getline(SESSION(session)->sock)) == NULL)
3868 log_print("IMAP4< %s\n", *ret);
3870 session_set_access_time(SESSION(session));
3872 return IMAP_SUCCESS;
3875 static gint imap_gen_recv_block(IMAPSession *session, gchar **ret)
3877 return imap_gen_recv_with_block(session, ret, TRUE);
3880 static gint imap_gen_recv(IMAPSession *session, gchar **ret)
3882 return imap_gen_recv_with_block(session, ret, FALSE);
3884 /* misc utility functions */
3886 static gchar *strchr_cpy(const gchar *src, gchar ch, gchar *dest, gint len)
3891 tmp = strchr(src, ch);
3895 memcpy(dest, src, MIN(tmp - src, len - 1));
3896 dest[MIN(tmp - src, len - 1)] = '\0';
3901 static gchar *get_quoted(const gchar *src, gchar ch, gchar *dest, gint len)
3903 const gchar *p = src;
3906 g_return_val_if_fail(*p == ch, NULL);
3911 while (*p != '\0' && *p != ch) {
3913 if (*p == '\\' && *(p + 1) != '\0')
3922 return (gchar *)(*p == ch ? p + 1 : p);
3925 static gchar *search_array_contain_str(GPtrArray *array, const gchar *str)
3929 for (i = 0; i < array->len; i++) {
3932 tmp = g_ptr_array_index(array, i);
3933 if (strstr(tmp, str) != NULL)
3940 static gchar *search_array_str(GPtrArray *array, const gchar *str)
3947 for (i = 0; i < array->len; i++) {
3950 tmp = g_ptr_array_index(array, i);
3951 if (!strncmp(tmp, str, len))
3958 static void imap_path_separator_subst(gchar *str, gchar separator)
3961 gboolean in_escape = FALSE;
3963 if (!separator || separator == '/') return;
3965 for (p = str; *p != '\0'; p++) {
3966 if (*p == '/' && !in_escape)
3968 else if (*p == '&' && *(p + 1) != '-' && !in_escape)
3970 else if (*p == '-' && in_escape)
3975 static gchar *imap_modified_utf7_to_utf8(const gchar *mutf7_str)
3977 static iconv_t cd = (iconv_t)-1;
3978 static gboolean iconv_ok = TRUE;
3981 size_t norm_utf7_len;
3983 gchar *to_str, *to_p;
3985 gboolean in_escape = FALSE;
3987 if (!iconv_ok) return g_strdup(mutf7_str);
3989 if (cd == (iconv_t)-1) {
3990 cd = iconv_open(CS_INTERNAL, CS_UTF_7);
3991 if (cd == (iconv_t)-1) {
3992 g_warning("iconv cannot convert UTF-7 to %s\n",
3995 return g_strdup(mutf7_str);
3999 /* modified UTF-7 to normal UTF-7 conversion */
4000 norm_utf7 = g_string_new(NULL);
4002 for (p = mutf7_str; *p != '\0'; p++) {
4003 /* replace: '&' -> '+',
4005 escaped ',' -> '/' */
4006 if (!in_escape && *p == '&') {
4007 if (*(p + 1) != '-') {
4008 g_string_append_c(norm_utf7, '+');
4011 g_string_append_c(norm_utf7, '&');
4014 } else if (in_escape && *p == ',') {
4015 g_string_append_c(norm_utf7, '/');
4016 } else if (in_escape && *p == '-') {
4017 g_string_append_c(norm_utf7, '-');
4020 g_string_append_c(norm_utf7, *p);
4024 norm_utf7_p = norm_utf7->str;
4025 norm_utf7_len = norm_utf7->len;
4026 to_len = strlen(mutf7_str) * 5;
4027 to_p = to_str = g_malloc(to_len + 1);
4029 if (iconv(cd, (ICONV_CONST gchar **)&norm_utf7_p, &norm_utf7_len,
4030 &to_p, &to_len) == -1) {
4031 g_warning(_("iconv cannot convert UTF-7 to %s\n"),
4032 conv_get_locale_charset_str());
4033 g_string_free(norm_utf7, TRUE);
4035 return g_strdup(mutf7_str);
4038 /* second iconv() call for flushing */
4039 iconv(cd, NULL, NULL, &to_p, &to_len);
4040 g_string_free(norm_utf7, TRUE);
4046 static gchar *imap_utf8_to_modified_utf7(const gchar *from)
4048 static iconv_t cd = (iconv_t)-1;
4049 static gboolean iconv_ok = TRUE;
4050 gchar *norm_utf7, *norm_utf7_p;
4051 size_t from_len, norm_utf7_len;
4053 gchar *from_tmp, *to, *p;
4054 gboolean in_escape = FALSE;
4056 if (!iconv_ok) return g_strdup(from);
4058 if (cd == (iconv_t)-1) {
4059 cd = iconv_open(CS_UTF_7, CS_INTERNAL);
4060 if (cd == (iconv_t)-1) {
4061 g_warning(_("iconv cannot convert %s to UTF-7\n"),
4064 return g_strdup(from);
4068 /* UTF-8 to normal UTF-7 conversion */
4069 Xstrdup_a(from_tmp, from, return g_strdup(from));
4070 from_len = strlen(from);
4071 norm_utf7_len = from_len * 5;
4072 Xalloca(norm_utf7, norm_utf7_len + 1, return g_strdup(from));
4073 norm_utf7_p = norm_utf7;
4075 #define IS_PRINT(ch) (isprint(ch) && IS_ASCII(ch))
4077 while (from_len > 0) {
4078 if (*from_tmp == '+') {
4079 *norm_utf7_p++ = '+';
4080 *norm_utf7_p++ = '-';
4084 } else if (IS_PRINT(*(guchar *)from_tmp)) {
4085 /* printable ascii char */
4086 *norm_utf7_p = *from_tmp;
4092 size_t conv_len = 0;
4094 /* unprintable char: convert to UTF-7 */
4096 while (!IS_PRINT(*(guchar *)p) && conv_len < from_len) {
4097 conv_len += g_utf8_skip[*(guchar *)p];
4098 p += g_utf8_skip[*(guchar *)p];
4101 from_len -= conv_len;
4102 if (iconv(cd, (ICONV_CONST gchar **)&from_tmp,
4104 &norm_utf7_p, &norm_utf7_len) == -1) {
4105 g_warning(_("iconv cannot convert UTF-8 to UTF-7\n"));
4106 return g_strdup(from);
4109 /* second iconv() call for flushing */
4110 iconv(cd, NULL, NULL, &norm_utf7_p, &norm_utf7_len);
4116 *norm_utf7_p = '\0';
4117 to_str = g_string_new(NULL);
4118 for (p = norm_utf7; p < norm_utf7_p; p++) {
4119 /* replace: '&' -> "&-",
4122 BASE64 '/' -> ',' */
4123 if (!in_escape && *p == '&') {
4124 g_string_append(to_str, "&-");
4125 } else if (!in_escape && *p == '+') {
4126 if (*(p + 1) == '-') {
4127 g_string_append_c(to_str, '+');
4130 g_string_append_c(to_str, '&');
4133 } else if (in_escape && *p == '/') {
4134 g_string_append_c(to_str, ',');
4135 } else if (in_escape && *p == '-') {
4136 g_string_append_c(to_str, '-');
4139 g_string_append_c(to_str, *p);
4145 g_string_append_c(to_str, '-');
4149 g_string_free(to_str, FALSE);
4154 static GSList *imap_get_seq_set_from_numlist(MsgNumberList *numlist)
4157 GSList *sorted_list, *cur;
4158 guint first, last, next;
4160 GSList *ret_list = NULL;
4162 if (numlist == NULL)
4165 str = g_string_sized_new(256);
4167 sorted_list = g_slist_copy(numlist);
4168 sorted_list = g_slist_sort(sorted_list, g_int_compare);
4170 first = GPOINTER_TO_INT(sorted_list->data);
4172 for (cur = sorted_list; cur != NULL; cur = g_slist_next(cur)) {
4173 if (GPOINTER_TO_INT(cur->data) == 0)
4176 last = GPOINTER_TO_INT(cur->data);
4178 next = GPOINTER_TO_INT(cur->next->data);
4182 if (last + 1 != next || next == 0) {
4184 g_string_append_c(str, ',');
4186 g_string_append_printf(str, "%u", first);
4188 g_string_append_printf(str, "%u:%u", first, last);
4192 if (str->len > IMAP_CMD_LIMIT) {
4193 ret_str = g_strdup(str->str);
4194 ret_list = g_slist_append(ret_list, ret_str);
4195 g_string_truncate(str, 0);
4201 ret_str = g_strdup(str->str);
4202 ret_list = g_slist_append(ret_list, ret_str);
4205 g_slist_free(sorted_list);
4206 g_string_free(str, TRUE);
4211 static GSList *imap_get_seq_set_from_msglist(MsgInfoList *msglist)
4213 MsgNumberList *numlist = NULL;
4217 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
4218 MsgInfo *msginfo = (MsgInfo *) cur->data;
4220 numlist = g_slist_append(numlist, GINT_TO_POINTER(msginfo->msgnum));
4222 seq_list = imap_get_seq_set_from_numlist(numlist);
4223 g_slist_free(numlist);
4228 static void imap_seq_set_free(GSList *seq_list)
4230 slist_free_strings(seq_list);
4231 g_slist_free(seq_list);
4235 static gboolean imap_rename_folder_func(GNode *node, gpointer data)
4237 FolderItem *item = node->data;
4238 gchar **paths = data;
4239 const gchar *oldpath = paths[0];
4240 const gchar *newpath = paths[1];
4242 gchar *new_itempath;
4245 oldpathlen = strlen(oldpath);
4246 if (strncmp(oldpath, item->path, oldpathlen) != 0) {
4247 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
4251 base = item->path + oldpathlen;
4252 while (*base == G_DIR_SEPARATOR) base++;
4254 new_itempath = g_strdup(newpath);
4256 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
4259 item->path = new_itempath;
4264 typedef struct _get_list_uid_data {
4266 IMAPFolderItem *item;
4267 GSList **msgnum_list;
4269 } get_list_uid_data;
4271 static void *get_list_of_uids_thread(void *data)
4273 get_list_uid_data *stuff = (get_list_uid_data *)data;
4274 Folder *folder = stuff->folder;
4275 IMAPFolderItem *item = stuff->item;
4276 GSList **msgnum_list = stuff->msgnum_list;
4277 gint ok, nummsgs = 0, lastuid_old;
4278 IMAPSession *session;
4279 GSList *uidlist, *elem;
4282 session = imap_session_get(folder);
4283 if (session == NULL) {
4285 return GINT_TO_POINTER(-1);
4288 ok = imap_select(session, IMAP_FOLDER(folder), item->item.path,
4289 NULL, NULL, NULL, NULL, TRUE);
4290 if (ok != IMAP_SUCCESS) {
4292 return GINT_TO_POINTER(-1);
4295 cmd_buf = g_strdup_printf("UID %d:*", item->lastuid + 1);
4296 ok = imap_cmd_search(session, cmd_buf, &uidlist, TRUE);
4299 if (ok == IMAP_SOCKET) {
4300 session_destroy((Session *)session);
4301 ((RemoteFolder *)folder)->session = NULL;
4303 return GINT_TO_POINTER(-1);
4306 if (ok != IMAP_SUCCESS) {
4310 argbuf = g_ptr_array_new();
4312 cmd_buf = g_strdup_printf("UID FETCH %d:* (UID)", item->lastuid + 1);
4313 imap_gen_send(session, cmd_buf);
4315 ok = imap_cmd_ok_block(session, argbuf);
4316 if (ok != IMAP_SUCCESS) {
4317 ptr_array_free_strings(argbuf);
4318 g_ptr_array_free(argbuf, TRUE);
4320 return GINT_TO_POINTER(-1);
4323 for(i = 0; i < argbuf->len; i++) {
4326 if((ret = sscanf(g_ptr_array_index(argbuf, i),
4327 "%*d FETCH (UID %d)", &msgnum)) == 1)
4328 uidlist = g_slist_prepend(uidlist, GINT_TO_POINTER(msgnum));
4330 ptr_array_free_strings(argbuf);
4331 g_ptr_array_free(argbuf, TRUE);
4334 lastuid_old = item->lastuid;
4335 *msgnum_list = g_slist_copy(item->uid_list);
4336 nummsgs = g_slist_length(*msgnum_list);
4337 debug_print("Got %d uids from cache\n", g_slist_length(item->uid_list));
4339 for (elem = uidlist; elem != NULL; elem = g_slist_next(elem)) {
4342 msgnum = GPOINTER_TO_INT(elem->data);
4343 if (msgnum > lastuid_old) {
4344 *msgnum_list = g_slist_prepend(*msgnum_list, GINT_TO_POINTER(msgnum));
4345 item->uid_list = g_slist_prepend(item->uid_list, GINT_TO_POINTER(msgnum));
4348 if(msgnum > item->lastuid)
4349 item->lastuid = msgnum;
4352 g_slist_free(uidlist);
4355 return GINT_TO_POINTER(nummsgs);
4358 static gint get_list_of_uids(Folder *folder, IMAPFolderItem *item, GSList **msgnum_list)
4361 get_list_uid_data *data = g_new0(get_list_uid_data, 1);
4367 data->folder = folder;
4369 data->msgnum_list = msgnum_list;
4371 if (prefs_common.work_offline && !imap_gtk_should_override()) {
4376 #if (defined USE_PTHREAD && defined __GLIBC__ && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 3)))
4377 if (pthread_create(&pt, PTHREAD_CREATE_JOINABLE,
4378 get_list_of_uids_thread, data) != 0) {
4379 result = GPOINTER_TO_INT(get_list_of_uids_thread(data));
4383 debug_print("+++waiting for get_list_of_uids_thread...\n");
4384 while(!data->done) {
4385 /* don't let the interface freeze while waiting */
4388 debug_print("---get_list_of_uids_thread done\n");
4390 /* get the thread's return value and clean its resources */
4391 pthread_join(pt, &tmp);
4392 result = GPOINTER_TO_INT(tmp);
4394 result = GPOINTER_TO_INT(get_list_of_uids_thread(data));
4401 gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list, gboolean *old_uids_valid)
4403 IMAPFolderItem *item = (IMAPFolderItem *)_item;
4404 IMAPSession *session;
4405 gint ok, nummsgs = 0, exists, recent, uid_val, uid_next, unseen;
4406 GSList *uidlist = NULL;
4408 gboolean selected_folder;
4410 g_return_val_if_fail(folder != NULL, -1);
4411 g_return_val_if_fail(item != NULL, -1);
4412 g_return_val_if_fail(item->item.path != NULL, -1);
4413 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
4414 g_return_val_if_fail(folder->account != NULL, -1);
4416 MUTEX_TRYLOCK_OR_RETURN_VAL(-1);
4418 session = imap_session_get(folder);
4419 g_return_val_if_fail(session != NULL, -1);
4421 selected_folder = (session->mbox != NULL) &&
4422 (!strcmp(session->mbox, item->item.path));
4423 if (selected_folder) {
4424 ok = imap_cmd_noop(session);
4425 if (ok != IMAP_SUCCESS) {
4429 exists = session->exists;
4431 *old_uids_valid = TRUE;
4433 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
4434 &exists, &recent, &uid_next, &uid_val, &unseen, FALSE);
4435 if (ok != IMAP_SUCCESS) {
4439 if(item->item.mtime == uid_val)
4440 *old_uids_valid = TRUE;
4442 *old_uids_valid = FALSE;
4444 debug_print("Freeing imap uid cache\n");
4446 g_slist_free(item->uid_list);
4447 item->uid_list = NULL;
4449 item->item.mtime = uid_val;
4451 imap_delete_all_cached_messages((FolderItem *)item);
4455 if (!selected_folder)
4456 item->uid_next = uid_next;
4458 /* If old uid_next matches new uid_next we can be sure no message
4459 was added to the folder */
4460 if (( selected_folder && !session->folder_content_changed) ||
4461 (!selected_folder && uid_next == item->uid_next)) {
4462 nummsgs = g_slist_length(item->uid_list);
4464 /* If number of messages is still the same we
4465 know our caches message numbers are still valid,
4466 otherwise if the number of messages has decrease
4467 we discard our cache to start a new scan to find
4468 out which numbers have been removed */
4469 if (exists == nummsgs) {
4470 *msgnum_list = g_slist_copy(item->uid_list);
4473 } else if (exists < nummsgs) {
4474 debug_print("Freeing imap uid cache");
4476 g_slist_free(item->uid_list);
4477 item->uid_list = NULL;
4482 *msgnum_list = NULL;
4487 nummsgs = get_list_of_uids(folder, item, &uidlist);
4494 if (nummsgs != exists) {
4495 /* Cache contains more messages then folder, we have cached
4496 an old UID of a message that was removed and new messages
4497 have been added too, otherwise the uid_next check would
4499 debug_print("Freeing imap uid cache");
4501 g_slist_free(item->uid_list);
4502 item->uid_list = NULL;
4504 g_slist_free(*msgnum_list);
4506 nummsgs = get_list_of_uids(folder, item, &uidlist);
4509 *msgnum_list = uidlist;
4511 dir = folder_item_get_path((FolderItem *)item);
4512 debug_print("removing old messages from %s\n", dir);
4513 remove_numbered_files_not_in_list(dir, *msgnum_list);
4520 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item)
4525 flags.perm_flags = MSG_NEW|MSG_UNREAD;
4526 flags.tmp_flags = 0;
4528 g_return_val_if_fail(item != NULL, NULL);
4529 g_return_val_if_fail(file != NULL, NULL);
4531 if (item->stype == F_QUEUE) {
4532 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
4533 } else if (item->stype == F_DRAFT) {
4534 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
4537 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
4538 if (!msginfo) return NULL;
4540 msginfo->plaintext_file = g_strdup(file);
4541 msginfo->folder = item;
4546 GSList *imap_get_msginfos(Folder *folder, FolderItem *item, GSList *msgnum_list)
4548 IMAPSession *session;
4549 MsgInfoList *ret = NULL;
4552 g_return_val_if_fail(folder != NULL, NULL);
4553 g_return_val_if_fail(item != NULL, NULL);
4554 g_return_val_if_fail(msgnum_list != NULL, NULL);
4556 session = imap_session_get(folder);
4557 g_return_val_if_fail(session != NULL, NULL);
4559 debug_print("IMAP getting msginfos\n");
4560 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
4561 NULL, NULL, NULL, NULL, FALSE);
4562 if (ok != IMAP_SUCCESS)
4565 if (!(item->stype == F_QUEUE || item->stype == F_DRAFT)) {
4566 ret = g_slist_concat(ret,
4567 imap_get_uncached_messages(
4568 session, item, msgnum_list));
4570 MsgNumberList *sorted_list, *elem;
4571 gint startnum, lastnum;
4573 sorted_list = g_slist_sort(g_slist_copy(msgnum_list), g_int_compare);
4575 startnum = lastnum = GPOINTER_TO_INT(sorted_list->data);
4577 for (elem = sorted_list;; elem = g_slist_next(elem)) {
4581 num = GPOINTER_TO_INT(elem->data);
4583 if (num > lastnum + 1 || elem == NULL) {
4585 for (i = startnum; i <= lastnum; ++i) {
4588 file = imap_fetch_msg(folder, item, i);
4590 MsgInfo *msginfo = imap_parse_msg(file, item);
4591 if (msginfo != NULL) {
4592 msginfo->msgnum = i;
4593 ret = g_slist_append(ret, msginfo);
4607 g_slist_free(sorted_list);
4613 MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid)
4615 MsgInfo *msginfo = NULL;
4616 MsgInfoList *msginfolist;
4617 MsgNumberList numlist;
4619 numlist.next = NULL;
4620 numlist.data = GINT_TO_POINTER(uid);
4622 msginfolist = imap_get_msginfos(folder, item, &numlist);
4623 if (msginfolist != NULL) {
4624 msginfo = msginfolist->data;
4625 g_slist_free(msginfolist);
4631 gboolean imap_scan_required(Folder *folder, FolderItem *_item)
4633 IMAPSession *session;
4634 IMAPFolderItem *item = (IMAPFolderItem *)_item;
4635 gint ok, exists = 0, recent = 0, unseen = 0;
4636 guint32 uid_next, uid_val = 0;
4637 gboolean selected_folder;
4639 g_return_val_if_fail(folder != NULL, FALSE);
4640 g_return_val_if_fail(item != NULL, FALSE);
4641 g_return_val_if_fail(item->item.folder != NULL, FALSE);
4642 g_return_val_if_fail(FOLDER_CLASS(item->item.folder) == &imap_class, FALSE);
4644 if (item->item.path == NULL)
4647 session = imap_session_get(folder);
4648 g_return_val_if_fail(session != NULL, FALSE);
4650 selected_folder = (session->mbox != NULL) &&
4651 (!strcmp(session->mbox, item->item.path));
4652 if (selected_folder) {
4653 ok = imap_cmd_noop(session);
4654 if (ok != IMAP_SUCCESS)
4657 if (session->folder_content_changed)
4660 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
4661 &exists, &recent, &uid_next, &uid_val, &unseen, FALSE);
4662 if (ok != IMAP_SUCCESS)
4665 if ((uid_next != item->uid_next) || (exists != item->item.total_msgs))
4672 void imap_change_flags(Folder *folder, FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
4674 IMAPSession *session;
4675 IMAPFlags flags_set = 0, flags_unset = 0;
4676 gint ok = IMAP_SUCCESS;
4677 MsgNumberList numlist;
4678 hashtable_data *ht_data = NULL;
4680 g_return_if_fail(folder != NULL);
4681 g_return_if_fail(folder->klass == &imap_class);
4682 g_return_if_fail(item != NULL);
4683 g_return_if_fail(item->folder == folder);
4684 g_return_if_fail(msginfo != NULL);
4685 g_return_if_fail(msginfo->folder == item);
4687 MUTEX_TRYLOCK_OR_RETURN();
4689 session = imap_session_get(folder);
4694 if ((ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
4695 NULL, NULL, NULL, NULL, FALSE)) != IMAP_SUCCESS) {
4700 if (!MSG_IS_MARKED(msginfo->flags) && (newflags & MSG_MARKED))
4701 flags_set |= IMAP_FLAG_FLAGGED;
4702 if ( MSG_IS_MARKED(msginfo->flags) && !(newflags & MSG_MARKED))
4703 flags_unset |= IMAP_FLAG_FLAGGED;
4705 if (!MSG_IS_UNREAD(msginfo->flags) && (newflags & MSG_UNREAD))
4706 flags_unset |= IMAP_FLAG_SEEN;
4707 if ( MSG_IS_UNREAD(msginfo->flags) && !(newflags & MSG_UNREAD))
4708 flags_set |= IMAP_FLAG_SEEN;
4710 if (!MSG_IS_REPLIED(msginfo->flags) && (newflags & MSG_REPLIED))
4711 flags_set |= IMAP_FLAG_ANSWERED;
4712 if ( MSG_IS_REPLIED(msginfo->flags) && !(newflags & MSG_REPLIED))
4713 flags_set |= IMAP_FLAG_ANSWERED;
4715 numlist.next = NULL;
4716 numlist.data = GINT_TO_POINTER(msginfo->msgnum);
4718 if (IMAP_FOLDER_ITEM(item)->batching) {
4719 /* instead of performing an UID STORE command for each message change,
4720 * as a lot of them can change "together", we just fill in hashtables
4721 * and defer the treatment so that we're able to send only one
4724 debug_print("IMAP batch mode on, deferring flags change\n");
4726 ht_data = g_hash_table_lookup(flags_set_table, GINT_TO_POINTER(flags_set));
4727 if (ht_data == NULL) {
4728 ht_data = g_new0(hashtable_data, 1);
4729 ht_data->session = session;
4730 g_hash_table_insert(flags_set_table, GINT_TO_POINTER(flags_set), ht_data);
4732 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
4733 ht_data->msglist = g_slist_prepend(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum));
4736 ht_data = g_hash_table_lookup(flags_unset_table, GINT_TO_POINTER(flags_unset));
4737 if (ht_data == NULL) {
4738 ht_data = g_new0(hashtable_data, 1);
4739 ht_data->session = session;
4740 g_hash_table_insert(flags_unset_table, GINT_TO_POINTER(flags_unset), ht_data);
4742 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
4743 ht_data->msglist = g_slist_prepend(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum));
4746 debug_print("IMAP changing flags\n");
4748 ok = imap_set_message_flags(session, &numlist, flags_set, TRUE);
4749 if (ok != IMAP_SUCCESS) {
4756 ok = imap_set_message_flags(session, &numlist, flags_unset, FALSE);
4757 if (ok != IMAP_SUCCESS) {
4763 msginfo->flags.perm_flags = newflags;
4769 static gint imap_remove_msg(Folder *folder, FolderItem *item, gint uid)
4772 IMAPSession *session;
4774 MsgNumberList numlist;
4776 g_return_val_if_fail(folder != NULL, -1);
4777 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
4778 g_return_val_if_fail(item != NULL, -1);
4780 session = imap_session_get(folder);
4781 if (!session) return -1;
4783 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
4784 NULL, NULL, NULL, NULL, FALSE);
4785 if (ok != IMAP_SUCCESS)
4788 numlist.next = NULL;
4789 numlist.data = GINT_TO_POINTER(uid);
4791 ok = imap_set_message_flags
4792 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
4793 &numlist, IMAP_FLAG_DELETED, TRUE);
4794 if (ok != IMAP_SUCCESS) {
4795 log_warning(_("can't set deleted flags: %d\n"), uid);
4799 if (!session->uidplus) {
4800 ok = imap_cmd_expunge(session, NULL);
4804 uidstr = g_strdup_printf("%u", uid);
4805 ok = imap_cmd_expunge(session, uidstr);
4808 if (ok != IMAP_SUCCESS) {
4809 log_warning(_("can't expunge\n"));
4813 IMAP_FOLDER_ITEM(item)->uid_list = g_slist_remove(
4814 IMAP_FOLDER_ITEM(item)->uid_list, numlist.data);
4815 dir = folder_item_get_path(item);
4816 if (is_dir_exist(dir))
4817 remove_numbered_files(dir, uid, uid);
4820 return IMAP_SUCCESS;
4823 static gint compare_msginfo(gconstpointer a, gconstpointer b)
4825 return ((MsgInfo *)a)->msgnum - ((MsgInfo *)b)->msgnum;
4828 static guint gslist_find_next_num(MsgNumberList **list, guint num)
4832 g_return_val_if_fail(list != NULL, -1);
4834 for (elem = *list; elem != NULL; elem = g_slist_next(elem))
4835 if (GPOINTER_TO_INT(elem->data) >= num)
4838 return elem != NULL ? GPOINTER_TO_INT(elem->data) : (gint)-1;
4842 * NEW and DELETED flags are not syncronized
4843 * - The NEW/RECENT flags in IMAP folders can not really be directly
4844 * modified by Sylpheed
4845 * - The DELETE/DELETED flag in IMAP and Sylpheed don't have the same
4846 * meaning, in IMAP it always removes the messages from the FolderItem
4847 * in Sylpheed it can mean to move the message to trash
4850 typedef struct _get_flags_data {
4853 MsgInfoList *msginfo_list;
4854 GRelation *msgflags;
4858 static /*gint*/ void *imap_get_flags_thread(void *data)
4860 get_flags_data *stuff = (get_flags_data *)data;
4861 Folder *folder = stuff->folder;
4862 FolderItem *item = stuff->item;
4863 MsgInfoList *msginfo_list = stuff->msginfo_list;
4864 GRelation *msgflags = stuff->msgflags;
4865 IMAPSession *session;
4866 GSList *sorted_list;
4867 GSList *unseen = NULL, *answered = NULL, *flagged = NULL;
4868 GSList *p_unseen, *p_answered, *p_flagged;
4870 GSList *seq_list, *cur;
4871 gboolean reverse_seen = FALSE;
4874 gint exists_cnt, recent_cnt, unseen_cnt, uid_next;
4875 guint32 uidvalidity;
4876 gboolean selected_folder;
4878 if (folder == NULL || item == NULL) {
4880 return GINT_TO_POINTER(-1);
4882 if (msginfo_list == NULL) {
4884 return GINT_TO_POINTER(0);
4887 session = imap_session_get(folder);
4888 if (session == NULL) {
4890 return GINT_TO_POINTER(-1);
4893 selected_folder = (session->mbox != NULL) &&
4894 (!strcmp(session->mbox, item->path));
4896 if (!selected_folder) {
4897 ok = imap_status(session, IMAP_FOLDER(folder), item->path,
4898 &exists_cnt, &recent_cnt, &uid_next, &uidvalidity, &unseen_cnt, TRUE);
4899 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
4900 NULL, NULL, NULL, NULL, TRUE);
4901 if (ok != IMAP_SUCCESS) {
4903 return GINT_TO_POINTER(-1);
4908 if (unseen_cnt > exists_cnt / 2)
4909 reverse_seen = TRUE;
4911 cmd_buf = g_string_new(NULL);
4913 sorted_list = g_slist_sort(g_slist_copy(msginfo_list), compare_msginfo);
4915 seq_list = imap_get_seq_set_from_msglist(msginfo_list);
4917 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
4918 IMAPSet imapset = cur->data;
4920 g_string_sprintf(cmd_buf, "%sSEEN UID %s", reverse_seen ? "" : "UN", imapset);
4921 imap_cmd_search(session, cmd_buf->str, &p_unseen, TRUE);
4922 unseen = g_slist_concat(unseen, p_unseen);
4924 g_string_sprintf(cmd_buf, "ANSWERED UID %s", imapset);
4925 imap_cmd_search(session, cmd_buf->str, &p_answered, TRUE);
4926 answered = g_slist_concat(answered, p_answered);
4928 g_string_sprintf(cmd_buf, "FLAGGED UID %s", imapset);
4929 imap_cmd_search(session, cmd_buf->str, &p_flagged, TRUE);
4930 flagged = g_slist_concat(flagged, p_flagged);
4934 p_answered = answered;
4935 p_flagged = flagged;
4937 for (elem = sorted_list; elem != NULL; elem = g_slist_next(elem)) {
4942 msginfo = (MsgInfo *) elem->data;
4943 flags = msginfo->flags.perm_flags;
4944 wasnew = (flags & MSG_NEW);
4945 flags &= ~((reverse_seen ? 0 : MSG_UNREAD | MSG_NEW) | MSG_REPLIED | MSG_MARKED);
4947 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
4948 if (gslist_find_next_num(&p_unseen, msginfo->msgnum) == msginfo->msgnum) {
4949 if (!reverse_seen) {
4950 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
4952 flags &= ~(MSG_UNREAD | MSG_NEW);
4955 if (gslist_find_next_num(&p_answered, msginfo->msgnum) == msginfo->msgnum)
4956 flags |= MSG_REPLIED;
4957 if (gslist_find_next_num(&p_flagged, msginfo->msgnum) == msginfo->msgnum)
4958 flags |= MSG_MARKED;
4959 g_relation_insert(msgflags, msginfo, GINT_TO_POINTER(flags));
4962 imap_seq_set_free(seq_list);
4963 g_slist_free(flagged);
4964 g_slist_free(answered);
4965 g_slist_free(unseen);
4966 g_slist_free(sorted_list);
4967 g_string_free(cmd_buf, TRUE);
4970 return GINT_TO_POINTER(0);
4973 static gint imap_get_flags(Folder *folder, FolderItem *item,
4974 MsgInfoList *msginfo_list, GRelation *msgflags)
4977 get_flags_data *data = g_new0(get_flags_data, 1);
4983 data->folder = folder;
4985 data->msginfo_list = msginfo_list;
4986 data->msgflags = msgflags;
4988 if (prefs_common.work_offline && !imap_gtk_should_override()) {
4993 #if (defined USE_PTHREAD && defined __GLIBC__ && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 3)))
4994 MUTEX_TRYLOCK_OR_RETURN_VAL(-1);
4995 if (pthread_create(&pt, PTHREAD_CREATE_JOINABLE,
4996 imap_get_flags_thread, data) != 0) {
4997 result = GPOINTER_TO_INT(imap_get_flags_thread(data));
5002 debug_print("+++waiting for imap_get_flags_thread...\n");
5003 while(!data->done) {
5004 /* don't let the interface freeze while waiting */
5007 debug_print("---imap_get_flags_thread done\n");
5009 /* get the thread's return value and clean its resources */
5010 pthread_join(pt, &tmp);
5011 result = GPOINTER_TO_INT(tmp);
5014 result = GPOINTER_TO_INT(imap_get_flags_thread(data));
5021 static gboolean process_flags(gpointer key, gpointer value, gpointer user_data)
5023 gboolean flags_set = GPOINTER_TO_INT(user_data);
5024 gint flags_value = GPOINTER_TO_INT(key);
5025 hashtable_data *data = (hashtable_data *)value;
5027 data->msglist = g_slist_reverse(data->msglist);
5029 debug_print("IMAP %ssetting flags to %d for %d messages\n",
5032 g_slist_length(data->msglist));
5033 imap_set_message_flags(data->session, data->msglist, flags_value, flags_set);
5035 g_slist_free(data->msglist);
5040 static void process_hashtable(void)
5042 MUTEX_TRYLOCK_OR_RETURN();
5043 if (flags_set_table) {
5044 g_hash_table_foreach_remove(flags_set_table, process_flags, GINT_TO_POINTER(TRUE));
5045 g_free(flags_set_table);
5046 flags_set_table = NULL;
5048 if (flags_unset_table) {
5049 g_hash_table_foreach_remove(flags_unset_table, process_flags, GINT_TO_POINTER(FALSE));
5050 g_free(flags_unset_table);
5051 flags_unset_table = NULL;
5056 static IMAPFolderItem *batching_item = NULL;
5058 static void imap_set_batch (Folder *folder, FolderItem *_item, gboolean batch)
5060 IMAPFolderItem *item = (IMAPFolderItem *)_item;
5062 g_return_if_fail(item != NULL);
5064 if (batch && batching_item != NULL) {
5065 g_warning("already batching on %s\n", batching_item->item.path);
5069 if (item->batching == batch)
5072 item->batching = batch;
5074 batching_item = batch?item:NULL;
5077 debug_print("IMAP switching to batch mode\n");
5078 if (flags_set_table) {
5079 g_warning("flags_set_table non-null but we just entered batch mode!\n");
5080 flags_set_table = NULL;
5082 if (flags_unset_table) {
5083 g_warning("flags_unset_table non-null but we just entered batch mode!\n");
5084 flags_unset_table = NULL;
5086 flags_set_table = g_hash_table_new(NULL, g_direct_equal);
5087 flags_unset_table = g_hash_table_new(NULL, g_direct_equal);
5089 debug_print("IMAP switching away from batch mode\n");
5091 process_hashtable();