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"
69 typedef struct _IMAPFolder IMAPFolder;
70 typedef struct _IMAPSession IMAPSession;
71 typedef struct _IMAPNameSpace IMAPNameSpace;
72 typedef struct _IMAPFolderItem IMAPFolderItem;
74 #include "prefs_account.h"
76 #define IMAP_FOLDER(obj) ((IMAPFolder *)obj)
77 #define IMAP_FOLDER_ITEM(obj) ((IMAPFolderItem *)obj)
78 #define IMAP_SESSION(obj) ((IMAPSession *)obj)
84 /* list of IMAPNameSpace */
94 gboolean authenticated;
103 gboolean folder_content_changed;
107 struct _IMAPNameSpace
113 #define IMAP_SUCCESS 0
114 #define IMAP_SOCKET 2
115 #define IMAP_AUTHFAIL 3
116 #define IMAP_PROTOCOL 4
117 #define IMAP_SYNTAX 5
121 #define IMAPBUFSIZE 8192
125 IMAP_FLAG_SEEN = 1 << 0,
126 IMAP_FLAG_ANSWERED = 1 << 1,
127 IMAP_FLAG_FLAGGED = 1 << 2,
128 IMAP_FLAG_DELETED = 1 << 3,
129 IMAP_FLAG_DRAFT = 1 << 4
132 #define IMAP_IS_SEEN(flags) ((flags & IMAP_FLAG_SEEN) != 0)
133 #define IMAP_IS_ANSWERED(flags) ((flags & IMAP_FLAG_ANSWERED) != 0)
134 #define IMAP_IS_FLAGGED(flags) ((flags & IMAP_FLAG_FLAGGED) != 0)
135 #define IMAP_IS_DELETED(flags) ((flags & IMAP_FLAG_DELETED) != 0)
136 #define IMAP_IS_DRAFT(flags) ((flags & IMAP_FLAG_DRAFT) != 0)
139 #define IMAP4_PORT 143
141 #define IMAPS_PORT 993
144 #define IMAP_CMD_LIMIT 1000
146 #define QUOTE_IF_REQUIRED(out, str) \
148 if (*str != '"' && strpbrk(str, " \t(){}[]%*\\") != NULL) { \
152 len = strlen(str) + 3; \
153 Xalloca(__tmp, len, return IMAP_ERROR); \
154 g_snprintf(__tmp, len, "\"%s\"", str); \
157 Xstrdup_a(out, str, return IMAP_ERROR); \
161 typedef gchar * IMAPSet;
163 struct _IMAPFolderItem
173 static void imap_folder_init (Folder *folder,
177 static Folder *imap_folder_new (const gchar *name,
179 static void imap_folder_destroy (Folder *folder);
181 static IMAPSession *imap_session_new (const PrefsAccount *account);
182 static void imap_session_authenticate(IMAPSession *session,
183 const PrefsAccount *account);
184 static void imap_session_destroy (Session *session);
186 static gchar *imap_fetch_msg (Folder *folder,
189 static gchar *imap_fetch_msg_full (Folder *folder,
194 static gint imap_add_msg (Folder *folder,
198 static gint imap_add_msgs (Folder *folder,
201 GRelation *relation);
203 static gint imap_copy_msg (Folder *folder,
206 static gint imap_copy_msgs (Folder *folder,
208 MsgInfoList *msglist,
209 GRelation *relation);
211 static gint imap_remove_msg (Folder *folder,
214 static gint imap_remove_msgs (Folder *folder,
216 MsgInfoList *msglist,
217 GRelation *relation);
218 static gint imap_remove_all_msg (Folder *folder,
221 static gboolean imap_is_msg_changed (Folder *folder,
225 static gint imap_close (Folder *folder,
228 static gint imap_scan_tree (Folder *folder);
230 static gint imap_create_tree (Folder *folder);
232 static FolderItem *imap_create_folder (Folder *folder,
235 static gint imap_rename_folder (Folder *folder,
238 static gint imap_remove_folder (Folder *folder,
241 static FolderItem *imap_folder_item_new (Folder *folder);
242 static void imap_folder_item_destroy (Folder *folder,
245 static IMAPSession *imap_session_get (Folder *folder);
247 static gint imap_greeting (IMAPSession *session);
248 static gint imap_auth (IMAPSession *session,
253 static gint imap_scan_tree_recursive (IMAPSession *session,
255 static GSList *imap_parse_list (IMAPFolder *folder,
256 IMAPSession *session,
257 const gchar *real_path,
260 static void imap_create_missing_folders (Folder *folder);
261 static FolderItem *imap_create_special_folder
263 SpecialFolderItemType stype,
266 static gint imap_do_copy_msgs (Folder *folder,
268 MsgInfoList *msglist,
269 GRelation *relation);
271 static void imap_delete_all_cached_messages (FolderItem *item);
272 static void imap_set_batch (Folder *folder,
276 static SockInfo *imap_open (const gchar *server,
280 static SockInfo *imap_open (const gchar *server,
285 static SockInfo *imap_open_tunnel(const gchar *server,
286 const gchar *tunnelcmd,
289 static SockInfo *imap_open_tunnel(const gchar *server,
290 const gchar *tunnelcmd);
294 static SockInfo *imap_init_sock(SockInfo *sock, SSLType ssl_type);
296 static SockInfo *imap_init_sock(SockInfo *sock);
299 static gchar *imap_get_flag_str (IMAPFlags flags);
300 static gint imap_set_message_flags (IMAPSession *session,
301 MsgNumberList *numlist,
304 static gint imap_select (IMAPSession *session,
310 guint32 *uid_validity,
312 static gint imap_status (IMAPSession *session,
318 guint32 *uid_validity,
322 static void imap_parse_namespace (IMAPSession *session,
324 static void imap_get_namespace_by_list (IMAPSession *session,
326 static IMAPNameSpace *imap_find_namespace (IMAPFolder *folder,
328 static gchar imap_get_path_separator (IMAPFolder *folder,
330 static gchar *imap_get_real_path (IMAPFolder *folder,
333 static gchar *imap_parse_atom (SockInfo *sock,
338 static MsgFlags imap_parse_flags (const gchar *flag_str);
339 static MsgInfo *imap_parse_envelope (SockInfo *sock,
343 static gboolean imap_has_capability (IMAPSession *session,
345 static void imap_free_capabilities (IMAPSession *session);
347 /* low-level IMAP4rev1 commands */
348 static gint imap_cmd_authenticate
349 (IMAPSession *session,
353 static gint imap_cmd_login (IMAPSession *session,
356 static gint imap_cmd_logout (IMAPSession *session);
357 static gint imap_cmd_noop (IMAPSession *session);
359 static gint imap_cmd_starttls (IMAPSession *session);
361 static gint imap_cmd_namespace (IMAPSession *session,
363 static gint imap_cmd_list (IMAPSession *session,
365 const gchar *mailbox,
367 static gint imap_cmd_do_select (IMAPSession *session,
373 guint32 *uid_validity,
375 static gint imap_cmd_select (IMAPSession *session,
380 guint32 *uid_validity,
382 static gint imap_cmd_examine (IMAPSession *session,
387 guint32 *uid_validity,
389 static gint imap_cmd_create (IMAPSession *sock,
390 const gchar *folder);
391 static gint imap_cmd_rename (IMAPSession *sock,
392 const gchar *oldfolder,
393 const gchar *newfolder);
394 static gint imap_cmd_delete (IMAPSession *session,
395 const gchar *folder);
396 static gint imap_cmd_envelope (IMAPSession *session,
398 static gint imap_cmd_fetch (IMAPSession *sock,
400 const gchar *filename,
403 static gint imap_cmd_append (IMAPSession *session,
404 const gchar *destfolder,
408 static gint imap_cmd_copy (IMAPSession *session,
409 const gchar *seq_set,
410 const gchar *destfolder,
411 GRelation *uid_mapping);
412 static gint imap_cmd_store (IMAPSession *session,
415 static gint imap_cmd_expunge (IMAPSession *session,
417 static gint imap_cmd_close (IMAPSession *session);
419 static gint imap_cmd_ok (IMAPSession *session,
421 static gint imap_cmd_ok_block (IMAPSession *session,
423 static gint imap_cmd_ok_with_block
424 (IMAPSession *session,
427 static void imap_gen_send (IMAPSession *session,
428 const gchar *format, ...);
429 static gint imap_gen_recv (IMAPSession *session,
431 static gint imap_gen_recv_block (IMAPSession *session,
433 static gint imap_gen_recv_with_block
434 (IMAPSession *session,
438 /* misc utility functions */
439 static gchar *strchr_cpy (const gchar *src,
443 static gchar *get_quoted (const gchar *src,
447 static gchar *search_array_contain_str (GPtrArray *array,
449 static gchar *search_array_str (GPtrArray *array,
451 static void imap_path_separator_subst (gchar *str,
454 static gchar *imap_utf8_to_modified_utf7 (const gchar *from);
455 static gchar *imap_modified_utf7_to_utf8 (const gchar *mutf7_str);
457 static GSList *imap_get_seq_set_from_numlist (MsgNumberList *msglist);
458 static GSList *imap_get_seq_set_from_msglist (MsgInfoList *msglist);
459 static void imap_seq_set_free (GSList *seq_list);
461 static gboolean imap_rename_folder_func (GNode *node,
463 static gint imap_get_num_list (Folder *folder,
466 gboolean *old_uids_valid);
467 static GSList *imap_get_msginfos (Folder *folder,
469 GSList *msgnum_list);
470 static MsgInfo *imap_get_msginfo (Folder *folder,
473 static gboolean imap_scan_required (Folder *folder,
475 static void imap_change_flags (Folder *folder,
478 MsgPermFlags newflags);
479 static gint imap_get_flags (Folder *folder,
481 MsgInfoList *msglist,
482 GRelation *msgflags);
483 static gchar *imap_folder_get_path (Folder *folder);
484 static gchar *imap_item_get_path (Folder *folder,
486 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item);
487 static GHashTable *flags_set_table = NULL;
488 static GHashTable *flags_unset_table = NULL;
489 typedef struct _hashtable_data {
490 IMAPSession *session;
494 static FolderClass imap_class;
496 typedef struct _thread_data {
507 void *imap_getline_thread(void *data)
509 thread_data *td = (thread_data *)data;
512 line = sock_getline(td->sock);
520 /* imap_getline just wraps sock_getline inside a thread,
521 * performing gtk updates so that the interface isn't frozen.
523 static gchar *imap_getline(SockInfo *sock)
525 #if (defined USE_PTHREAD && defined __GLIBC__ && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 3)))
526 thread_data *td = g_new0(thread_data, 1);
532 if (pthread_create(&pt, PTHREAD_CREATE_JOINABLE,
533 imap_getline_thread, td) != 0) {
535 return sock_getline(sock);
538 debug_print("+++waiting for imap_getline_thread...\n");
540 /* don't let the interface freeze while waiting */
543 debug_print("---imap_getline_thread done\n");
545 /* get the thread's return value and clean its resources */
546 pthread_join(pt, (void *)&line);
551 return sock_getline(sock);
555 FolderClass *imap_get_class(void)
557 if (imap_class.idstr == NULL) {
558 imap_class.type = F_IMAP;
559 imap_class.idstr = "imap";
560 imap_class.uistr = "IMAP4";
562 /* Folder functions */
563 imap_class.new_folder = imap_folder_new;
564 imap_class.destroy_folder = imap_folder_destroy;
565 imap_class.scan_tree = imap_scan_tree;
566 imap_class.create_tree = imap_create_tree;
568 /* FolderItem functions */
569 imap_class.item_new = imap_folder_item_new;
570 imap_class.item_destroy = imap_folder_item_destroy;
571 imap_class.item_get_path = imap_item_get_path;
572 imap_class.create_folder = imap_create_folder;
573 imap_class.rename_folder = imap_rename_folder;
574 imap_class.remove_folder = imap_remove_folder;
575 imap_class.close = imap_close;
576 imap_class.get_num_list = imap_get_num_list;
577 imap_class.scan_required = imap_scan_required;
579 /* Message functions */
580 imap_class.get_msginfo = imap_get_msginfo;
581 imap_class.get_msginfos = imap_get_msginfos;
582 imap_class.fetch_msg = imap_fetch_msg;
583 imap_class.fetch_msg_full = imap_fetch_msg_full;
584 imap_class.add_msg = imap_add_msg;
585 imap_class.add_msgs = imap_add_msgs;
586 imap_class.copy_msg = imap_copy_msg;
587 imap_class.copy_msgs = imap_copy_msgs;
588 imap_class.remove_msg = imap_remove_msg;
589 imap_class.remove_msgs = imap_remove_msgs;
590 imap_class.remove_all_msg = imap_remove_all_msg;
591 imap_class.is_msg_changed = imap_is_msg_changed;
592 imap_class.change_flags = imap_change_flags;
593 imap_class.get_flags = imap_get_flags;
594 imap_class.set_batch = imap_set_batch;
600 static gchar *get_seq_set_from_seq_list(GSList *seq_list)
606 for (cur = seq_list; cur != NULL; cur = cur->next) {
607 tmp = val?g_strdup(val):NULL;
609 val = g_strconcat(tmp?tmp:"", tmp?",":"",(gchar *)cur->data,
617 static Folder *imap_folder_new(const gchar *name, const gchar *path)
621 folder = (Folder *)g_new0(IMAPFolder, 1);
622 folder->klass = &imap_class;
623 imap_folder_init(folder, name, path);
628 static void imap_folder_destroy(Folder *folder)
632 dir = imap_folder_get_path(folder);
633 if (is_dir_exist(dir))
634 remove_dir_recursive(dir);
637 folder_remote_folder_destroy(REMOTE_FOLDER(folder));
640 static void imap_folder_init(Folder *folder, const gchar *name,
643 folder_remote_folder_init((Folder *)folder, name, path);
646 static FolderItem *imap_folder_item_new(Folder *folder)
648 IMAPFolderItem *item;
650 item = g_new0(IMAPFolderItem, 1);
653 item->uid_list = NULL;
655 return (FolderItem *)item;
658 static void imap_folder_item_destroy(Folder *folder, FolderItem *_item)
660 IMAPFolderItem *item = (IMAPFolderItem *)_item;
662 g_return_if_fail(item != NULL);
663 g_slist_free(item->uid_list);
668 static gboolean imap_reset_uid_lists_func(GNode *node, gpointer data)
670 IMAPFolderItem *item = (IMAPFolderItem *)node->data;
674 g_slist_free(item->uid_list);
675 item->uid_list = NULL;
680 static void imap_reset_uid_lists(Folder *folder)
682 if(folder->node == NULL)
685 /* Destroy all uid lists and rest last uid */
686 g_node_traverse(folder->node, G_IN_ORDER, G_TRAVERSE_ALL, -1, imap_reset_uid_lists_func, NULL);
689 /* Send CAPABILITY, and examine the server's response to see whether this
690 * connection is pre-authenticated or not and build a list of CAPABILITIES. */
691 static gint imap_greeting(IMAPSession *session)
696 imap_gen_send(session, "CAPABILITY");
698 argbuf = g_ptr_array_new();
700 if (imap_cmd_ok(session, argbuf) != IMAP_SUCCESS ||
701 ((capstr = search_array_str(argbuf, "CAPABILITY ")) == NULL)) {
702 ptr_array_free_strings(argbuf);
703 g_ptr_array_free(argbuf, TRUE);
707 session->authenticated = search_array_str(argbuf, "PREAUTH") != NULL;
709 capstr += strlen("CAPABILITY ");
711 IMAP_SESSION(session)->capability = g_strsplit(capstr, " ", 0);
713 ptr_array_free_strings(argbuf);
714 g_ptr_array_free(argbuf, TRUE);
716 if (imap_has_capability(session, "UIDPLUS"))
717 session->uidplus = TRUE;
722 static gint imap_auth(IMAPSession *session, const gchar *user, const gchar *pass,
727 if (type == 0 || type == IMAP_AUTH_LOGIN)
728 ok = imap_cmd_login(session, user, pass);
730 ok = imap_cmd_authenticate(session, user, pass, type);
732 if (ok == IMAP_SUCCESS)
733 session->authenticated = TRUE;
738 static IMAPSession *imap_session_get(Folder *folder)
740 RemoteFolder *rfolder = REMOTE_FOLDER(folder);
741 IMAPSession *session = NULL;
743 g_return_val_if_fail(folder != NULL, NULL);
744 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, NULL);
745 g_return_val_if_fail(folder->account != NULL, NULL);
747 if (prefs_common.work_offline && !imap_gtk_should_override()) {
751 /* Make sure we have a session */
752 if (rfolder->session != NULL) {
753 session = IMAP_SESSION(rfolder->session);
755 imap_reset_uid_lists(folder);
756 session = imap_session_new(folder->account);
761 if (SESSION(session)->sock->state == CONN_DISCONNECTED) {
762 debug_print("IMAP server disconnected\n");
763 session_destroy(SESSION(session));
764 imap_reset_uid_lists(folder);
765 session = imap_session_new(folder->account);
768 /* Make sure session is authenticated */
769 if (!IMAP_SESSION(session)->authenticated)
770 imap_session_authenticate(IMAP_SESSION(session), folder->account);
771 if (!IMAP_SESSION(session)->authenticated) {
772 session_destroy(SESSION(session));
773 rfolder->session = NULL;
777 /* Make sure we have parsed the IMAP namespace */
778 imap_parse_namespace(IMAP_SESSION(session),
779 IMAP_FOLDER(folder));
781 /* I think the point of this code is to avoid sending a
782 * keepalive if we've used the session recently and therefore
783 * think it's still alive. Unfortunately, most of the code
784 * does not yet check for errors on the socket, and so if the
785 * connection drops we don't notice until the timeout expires.
786 * A better solution than sending a NOOP every time would be
787 * for every command to be prepared to retry until it is
788 * successfully sent. -- mbp */
789 if (time(NULL) - SESSION(session)->last_access_time > SESSION_TIMEOUT_INTERVAL) {
790 /* verify that the session is still alive */
791 if (imap_cmd_noop(session) != IMAP_SUCCESS) {
792 /* Check if this is the first try to establish a
793 connection, if yes we don't try to reconnect */
794 if (rfolder->session == NULL) {
795 log_warning(_("Connecting to %s failed"),
796 folder->account->recv_server);
797 session_destroy(SESSION(session));
800 log_warning(_("IMAP4 connection to %s has been"
801 " disconnected. Reconnecting...\n"),
802 folder->account->recv_server);
803 statusbar_print_all(_("IMAP4 connection to %s has been"
804 " disconnected. Reconnecting...\n"),
805 folder->account->recv_server);
806 session_destroy(SESSION(session));
807 /* Clear folders session to make imap_session_get create
808 a new session, because of rfolder->session == NULL
809 it will not try to reconnect again and so avoid an
811 rfolder->session = NULL;
812 session = imap_session_get(folder);
818 rfolder->session = SESSION(session);
820 return IMAP_SESSION(session);
823 static IMAPSession *imap_session_new(const PrefsAccount *account)
825 IMAPSession *session;
830 /* FIXME: IMAP over SSL only... */
833 port = account->set_imapport ? account->imapport
834 : account->ssl_imap == SSL_TUNNEL ? IMAPS_PORT : IMAP4_PORT;
835 ssl_type = account->ssl_imap;
837 port = account->set_imapport ? account->imapport
841 if (account->set_tunnelcmd) {
842 log_message(_("creating tunneled IMAP4 connection\n"));
844 if ((imap_sock = imap_open_tunnel(account->recv_server,
848 if ((imap_sock = imap_open_tunnel(account->recv_server,
849 account->tunnelcmd)) == NULL)
853 g_return_val_if_fail(account->recv_server != NULL, NULL);
855 log_message(_("creating IMAP4 connection to %s:%d ...\n"),
856 account->recv_server, port);
859 if ((imap_sock = imap_open(account->recv_server, port,
862 if ((imap_sock = imap_open(account->recv_server, port)) == NULL)
867 session = g_new0(IMAPSession, 1);
868 session_init(SESSION(session));
869 SESSION(session)->type = SESSION_IMAP;
870 SESSION(session)->server = g_strdup(account->recv_server);
871 SESSION(session)->sock = imap_sock;
873 SESSION(session)->destroy = imap_session_destroy;
875 session->capability = NULL;
877 session->authenticated = FALSE;
878 session->mbox = NULL;
879 session->cmd_count = 0;
881 /* Only need to log in if the connection was not PREAUTH */
882 if (imap_greeting(session) != IMAP_SUCCESS) {
883 session_destroy(SESSION(session));
888 if (account->ssl_imap == SSL_STARTTLS &&
889 imap_has_capability(session, "STARTTLS")) {
892 ok = imap_cmd_starttls(session);
893 if (ok != IMAP_SUCCESS) {
894 log_warning(_("Can't start TLS session.\n"));
895 session_destroy(SESSION(session));
898 if (!ssl_init_socket_with_method(SESSION(session)->sock,
900 session_destroy(SESSION(session));
904 imap_free_capabilities(session);
905 session->authenticated = FALSE;
906 session->uidplus = FALSE;
907 session->cmd_count = 1;
909 if (imap_greeting(session) != IMAP_SUCCESS) {
910 session_destroy(SESSION(session));
915 log_message("IMAP connection is %s-authenticated\n",
916 (session->authenticated) ? "pre" : "un");
921 static void imap_session_authenticate(IMAPSession *session,
922 const PrefsAccount *account)
926 g_return_if_fail(account->userid != NULL);
928 pass = account->passwd;
931 tmp_pass = input_dialog_query_password(account->recv_server, account->userid);
934 Xstrdup_a(pass, tmp_pass, {g_free(tmp_pass); return;});
937 statusbar_print_all(_("Connecting to IMAP4 server %s...\n"),
938 account->recv_server);
939 if (imap_auth(session, account->userid, pass, account->imap_auth_type) != IMAP_SUCCESS) {
940 imap_cmd_logout(session);
946 session->authenticated = TRUE;
949 static void imap_session_destroy(Session *session)
951 imap_free_capabilities(IMAP_SESSION(session));
952 g_free(IMAP_SESSION(session)->mbox);
953 sock_close(session->sock);
954 session->sock = NULL;
957 static gchar *imap_fetch_msg(Folder *folder, FolderItem *item, gint uid)
959 return imap_fetch_msg_full(folder, item, uid, TRUE, TRUE);
962 static guint get_size_with_lfs(MsgInfo *info)
971 fp = procmsg_open_message(info);
975 while (fgets(buf, sizeof (buf), fp) != NULL) {
977 if (!strstr(buf, "\r") && strstr(buf, "\n"))
985 static gchar *imap_fetch_msg_full(Folder *folder, FolderItem *item, gint uid,
986 gboolean headers, gboolean body)
988 gchar *path, *filename;
989 IMAPSession *session;
992 g_return_val_if_fail(folder != NULL, NULL);
993 g_return_val_if_fail(item != NULL, NULL);
998 path = folder_item_get_path(item);
999 if (!is_dir_exist(path))
1000 make_dir_hier(path);
1001 filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(uid), NULL);
1004 if (is_file_exist(filename)) {
1005 /* see whether the local file represents the whole message
1006 * or not. As the IMAP server reports size with \r chars,
1007 * we have to update the local file (UNIX \n only) size */
1008 MsgInfo *msginfo = imap_parse_msg(filename, item);
1009 MsgInfo *cached = msgcache_get_msg(item->cache,uid);
1010 guint have_size = get_size_with_lfs(msginfo);
1011 debug_print("message %d has been already %scached (%d/%d).\n", uid,
1012 have_size == cached->size ? "fully ":"",
1013 have_size, cached? (int)cached->size : -1);
1015 if (cached && (cached->size == have_size || !body)) {
1016 procmsg_msginfo_free(cached);
1017 procmsg_msginfo_free(msginfo);
1020 procmsg_msginfo_free(cached);
1021 procmsg_msginfo_free(msginfo);
1025 session = imap_session_get(folder);
1031 debug_print("IMAP fetching messages\n");
1032 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
1033 NULL, NULL, NULL, NULL, FALSE);
1034 if (ok != IMAP_SUCCESS) {
1035 g_warning("can't select mailbox %s\n", item->path);
1040 debug_print("getting message %d...\n", uid);
1041 ok = imap_cmd_fetch(session, (guint32)uid, filename, headers, body);
1043 if (ok != IMAP_SUCCESS) {
1044 g_warning("can't fetch message %d\n", uid);
1052 static gint imap_add_msg(Folder *folder, FolderItem *dest,
1053 const gchar *file, MsgFlags *flags)
1057 MsgFileInfo fileinfo;
1059 g_return_val_if_fail(file != NULL, -1);
1061 fileinfo.msginfo = NULL;
1062 fileinfo.file = (gchar *)file;
1063 fileinfo.flags = flags;
1064 file_list.data = &fileinfo;
1065 file_list.next = NULL;
1067 ret = imap_add_msgs(folder, dest, &file_list, NULL);
1071 static gint imap_add_msgs(Folder *folder, FolderItem *dest, GSList *file_list,
1072 GRelation *relation)
1075 IMAPSession *session;
1076 guint32 last_uid = 0;
1078 MsgFileInfo *fileinfo;
1081 g_return_val_if_fail(folder != NULL, -1);
1082 g_return_val_if_fail(dest != NULL, -1);
1083 g_return_val_if_fail(file_list != NULL, -1);
1085 session = imap_session_get(folder);
1086 if (!session) return -1;
1088 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
1090 for (cur = file_list; cur != NULL; cur = cur->next) {
1091 IMAPFlags iflags = 0;
1092 guint32 new_uid = 0;
1094 fileinfo = (MsgFileInfo *)cur->data;
1096 if (fileinfo->flags) {
1097 if (MSG_IS_MARKED(*fileinfo->flags))
1098 iflags |= IMAP_FLAG_FLAGGED;
1099 if (MSG_IS_REPLIED(*fileinfo->flags))
1100 iflags |= IMAP_FLAG_ANSWERED;
1101 if (!MSG_IS_UNREAD(*fileinfo->flags))
1102 iflags |= IMAP_FLAG_SEEN;
1105 if (dest->stype == F_OUTBOX ||
1106 dest->stype == F_QUEUE ||
1107 dest->stype == F_DRAFT ||
1108 dest->stype == F_TRASH)
1109 iflags |= IMAP_FLAG_SEEN;
1111 ok = imap_cmd_append(session, destdir, fileinfo->file, iflags,
1114 if (ok != IMAP_SUCCESS) {
1115 g_warning("can't append message %s\n", fileinfo->file);
1120 if (relation != NULL)
1121 g_relation_insert(relation, fileinfo->msginfo != NULL ?
1122 (gpointer) fileinfo->msginfo : (gpointer) fileinfo,
1123 GINT_TO_POINTER(dest->last_num + 1));
1124 if (last_uid < new_uid)
1133 static gint imap_do_copy_msgs(Folder *folder, FolderItem *dest,
1134 MsgInfoList *msglist, GRelation *relation)
1138 GSList *seq_list, *cur;
1140 IMAPSession *session;
1141 gint ok = IMAP_SUCCESS;
1142 GRelation *uid_mapping;
1145 g_return_val_if_fail(folder != NULL, -1);
1146 g_return_val_if_fail(dest != NULL, -1);
1147 g_return_val_if_fail(msglist != NULL, -1);
1149 session = imap_session_get(folder);
1153 msginfo = (MsgInfo *)msglist->data;
1155 src = msginfo->folder;
1157 g_warning("the src folder is identical to the dest.\n");
1161 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
1162 NULL, NULL, NULL, NULL, FALSE);
1163 if (ok != IMAP_SUCCESS) {
1167 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
1168 seq_list = imap_get_seq_set_from_msglist(msglist);
1169 uid_mapping = g_relation_new(2);
1170 g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
1172 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
1173 gchar *seq_set = (gchar *)cur->data;
1175 debug_print("Copying message %s%c[%s] to %s ...\n",
1176 src->path, G_DIR_SEPARATOR,
1179 ok = imap_cmd_copy(session, seq_set, destdir, uid_mapping);
1180 if (ok != IMAP_SUCCESS) {
1181 g_relation_destroy(uid_mapping);
1182 imap_seq_set_free(seq_list);
1187 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
1188 MsgInfo *msginfo = (MsgInfo *)cur->data;
1191 tuples = g_relation_select(uid_mapping,
1192 GINT_TO_POINTER(msginfo->msgnum),
1194 if (tuples->len > 0) {
1195 gint num = GPOINTER_TO_INT(g_tuples_index(tuples, 0, 1));
1196 g_relation_insert(relation, msginfo,
1197 GPOINTER_TO_INT(num));
1201 g_relation_insert(relation, msginfo,
1202 GPOINTER_TO_INT(0));
1203 g_tuples_destroy(tuples);
1206 g_relation_destroy(uid_mapping);
1207 imap_seq_set_free(seq_list);
1211 IMAP_FOLDER_ITEM(dest)->lastuid = 0;
1212 IMAP_FOLDER_ITEM(dest)->uid_next = 0;
1213 g_slist_free(IMAP_FOLDER_ITEM(dest)->uid_list);
1214 IMAP_FOLDER_ITEM(dest)->uid_list = NULL;
1216 if (ok == IMAP_SUCCESS)
1222 static gint imap_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1226 g_return_val_if_fail(msginfo != NULL, -1);
1228 msglist.data = msginfo;
1229 msglist.next = NULL;
1231 return imap_copy_msgs(folder, dest, &msglist, NULL);
1234 static gint imap_copy_msgs(Folder *folder, FolderItem *dest,
1235 MsgInfoList *msglist, GRelation *relation)
1241 g_return_val_if_fail(folder != NULL, -1);
1242 g_return_val_if_fail(dest != NULL, -1);
1243 g_return_val_if_fail(msglist != NULL, -1);
1245 msginfo = (MsgInfo *)msglist->data;
1246 g_return_val_if_fail(msginfo->folder != NULL, -1);
1248 if (folder == msginfo->folder->folder)
1249 return imap_do_copy_msgs(folder, dest, msglist, relation);
1251 file_list = procmsg_get_message_file_list(msglist);
1252 g_return_val_if_fail(file_list != NULL, -1);
1254 ret = imap_add_msgs(folder, dest, file_list, relation);
1256 procmsg_message_file_list_free(file_list);
1262 static gint imap_do_remove_msgs(Folder *folder, FolderItem *dest,
1263 MsgInfoList *msglist, GRelation *relation)
1266 GSList *seq_list = NULL, *cur;
1268 IMAPSession *session;
1269 gint ok = IMAP_SUCCESS;
1270 GRelation *uid_mapping;
1272 g_return_val_if_fail(folder != NULL, -1);
1273 g_return_val_if_fail(dest != NULL, -1);
1274 g_return_val_if_fail(msglist != NULL, -1);
1276 session = imap_session_get(folder);
1280 msginfo = (MsgInfo *)msglist->data;
1282 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
1283 NULL, NULL, NULL, NULL, FALSE);
1284 if (ok != IMAP_SUCCESS) {
1288 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
1289 for (cur = msglist; cur; cur = cur->next) {
1290 msginfo = (MsgInfo *)cur->data;
1291 seq_list = g_slist_append(seq_list, GINT_TO_POINTER(msginfo->msgnum));
1294 uid_mapping = g_relation_new(2);
1295 g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
1297 ok = imap_set_message_flags
1298 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
1299 seq_list, IMAP_FLAG_DELETED, TRUE);
1300 if (ok != IMAP_SUCCESS) {
1301 log_warning(_("can't set deleted flags\n"));
1304 ok = imap_cmd_expunge(session, NULL);
1305 if (ok != IMAP_SUCCESS) {
1306 log_warning(_("can't expunge\n"));
1310 g_relation_destroy(uid_mapping);
1311 g_slist_free(seq_list);
1315 if (ok == IMAP_SUCCESS)
1321 static gint imap_remove_msgs(Folder *folder, FolderItem *dest,
1322 MsgInfoList *msglist, GRelation *relation)
1326 g_return_val_if_fail(folder != NULL, -1);
1327 g_return_val_if_fail(dest != NULL, -1);
1328 g_return_val_if_fail(msglist != NULL, -1);
1330 msginfo = (MsgInfo *)msglist->data;
1331 g_return_val_if_fail(msginfo->folder != NULL, -1);
1333 return imap_do_remove_msgs(folder, dest, msglist, relation);
1336 static gint imap_remove_all_msg(Folder *folder, FolderItem *item)
1338 GSList *list = folder_item_get_msg_list(item);
1339 gint res = imap_remove_msgs(folder, item, list, NULL);
1344 static gboolean imap_is_msg_changed(Folder *folder, FolderItem *item,
1347 /* TODO: properly implement this method */
1351 static gint imap_close(Folder *folder, FolderItem *item)
1354 IMAPSession *session;
1356 g_return_val_if_fail(folder != NULL, -1);
1357 g_return_val_if_fail(item != NULL, -1);
1358 g_return_val_if_fail(item->path != NULL, -1);
1360 session = imap_session_get(folder);
1361 if (!session) return -1;
1363 if (session->mbox) {
1364 if (strcmp2(session->mbox, item->path) != 0) return -1;
1366 ok = imap_cmd_close(session);
1367 if (ok != IMAP_SUCCESS)
1368 log_warning(_("can't close folder\n"));
1370 g_free(session->mbox);
1372 session->mbox = NULL;
1380 static gint imap_scan_tree(Folder *folder)
1382 FolderItem *item = NULL;
1383 IMAPSession *session;
1384 gchar *root_folder = NULL;
1386 g_return_val_if_fail(folder != NULL, -1);
1387 g_return_val_if_fail(folder->account != NULL, -1);
1389 session = imap_session_get(folder);
1391 if (!folder->node) {
1392 folder_tree_destroy(folder);
1393 item = folder_item_new(folder, folder->name, NULL);
1394 item->folder = folder;
1395 folder->node = item->node = g_node_new(item);
1400 if (folder->account->imap_dir && *folder->account->imap_dir) {
1405 Xstrdup_a(root_folder, folder->account->imap_dir, return -1);
1406 extract_quote(root_folder, '"');
1407 subst_char(root_folder,
1408 imap_get_path_separator(IMAP_FOLDER(folder),
1411 strtailchomp(root_folder, '/');
1412 real_path = imap_get_real_path
1413 (IMAP_FOLDER(folder), root_folder);
1414 debug_print("IMAP root directory: %s\n", real_path);
1416 /* check if root directory exist */
1417 argbuf = g_ptr_array_new();
1418 ok = imap_cmd_list(session, NULL, real_path, argbuf);
1419 if (ok != IMAP_SUCCESS ||
1420 search_array_str(argbuf, "LIST ") == NULL) {
1421 log_warning(_("root folder %s does not exist\n"), real_path);
1422 g_ptr_array_free(argbuf, TRUE);
1425 if (!folder->node) {
1426 item = folder_item_new(folder, folder->name, NULL);
1427 item->folder = folder;
1428 folder->node = item->node = g_node_new(item);
1432 g_ptr_array_free(argbuf, TRUE);
1437 item = FOLDER_ITEM(folder->node->data);
1438 if (!item || ((item->path || root_folder) &&
1439 strcmp2(item->path, root_folder) != 0)) {
1440 folder_tree_destroy(folder);
1441 item = folder_item_new(folder, folder->name, root_folder);
1442 item->folder = folder;
1443 folder->node = item->node = g_node_new(item);
1446 imap_scan_tree_recursive(session, FOLDER_ITEM(folder->node->data));
1447 imap_create_missing_folders(folder);
1452 static gint imap_scan_tree_recursive(IMAPSession *session, FolderItem *item)
1455 IMAPFolder *imapfolder;
1456 FolderItem *new_item;
1457 GSList *item_list, *cur;
1460 gchar *wildcard_path;
1464 g_return_val_if_fail(item != NULL, -1);
1465 g_return_val_if_fail(item->folder != NULL, -1);
1466 g_return_val_if_fail(item->no_sub == FALSE, -1);
1468 folder = item->folder;
1469 imapfolder = IMAP_FOLDER(folder);
1471 separator = imap_get_path_separator(imapfolder, item->path);
1473 if (folder->ui_func)
1474 folder->ui_func(folder, item, folder->ui_func_data);
1477 wildcard[0] = separator;
1480 real_path = imap_get_real_path(imapfolder, item->path);
1484 real_path = g_strdup("");
1487 Xstrcat_a(wildcard_path, real_path, wildcard,
1488 {g_free(real_path); return IMAP_ERROR;});
1489 QUOTE_IF_REQUIRED(wildcard_path, wildcard_path);
1491 imap_gen_send(session, "LIST \"\" %s",
1494 strtailchomp(real_path, separator);
1495 item_list = imap_parse_list(imapfolder, session, real_path, NULL);
1498 node = item->node->children;
1499 while (node != NULL) {
1500 FolderItem *old_item = FOLDER_ITEM(node->data);
1501 GNode *next = node->next;
1504 for (cur = item_list; cur != NULL; cur = cur->next) {
1505 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1506 if (!strcmp2(old_item->path, cur_item->path)) {
1507 new_item = cur_item;
1512 debug_print("folder '%s' not found. removing...\n",
1514 folder_item_remove(old_item);
1516 old_item->no_sub = new_item->no_sub;
1517 old_item->no_select = new_item->no_select;
1518 if (old_item->no_sub == TRUE && node->children) {
1519 debug_print("folder '%s' doesn't have "
1520 "subfolders. removing...\n",
1522 folder_item_remove_children(old_item);
1529 for (cur = item_list; cur != NULL; cur = cur->next) {
1530 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1532 for (node = item->node->children; node != NULL;
1533 node = node->next) {
1534 if (!strcmp2(FOLDER_ITEM(node->data)->path,
1536 new_item = FOLDER_ITEM(node->data);
1537 folder_item_destroy(cur_item);
1543 new_item = cur_item;
1544 debug_print("new folder '%s' found.\n", new_item->path);
1545 folder_item_append(item, new_item);
1548 if (!strcmp(new_item->path, "INBOX")) {
1549 new_item->stype = F_INBOX;
1550 folder->inbox = new_item;
1551 } else if (!folder_item_parent(item) || item->stype == F_INBOX) {
1554 base = g_path_get_basename(new_item->path);
1556 if (!folder->outbox && !g_ascii_strcasecmp(base, "Sent")) {
1557 new_item->stype = F_OUTBOX;
1558 folder->outbox = new_item;
1559 } else if (!folder->draft && !g_ascii_strcasecmp(base, "Drafts")) {
1560 new_item->stype = F_DRAFT;
1561 folder->draft = new_item;
1562 } else if (!folder->queue && !g_ascii_strcasecmp(base, "Queue")) {
1563 new_item->stype = F_QUEUE;
1564 folder->queue = new_item;
1565 } else if (!folder->trash && !g_ascii_strcasecmp(base, "Trash")) {
1566 new_item->stype = F_TRASH;
1567 folder->trash = new_item;
1572 if (new_item->no_sub == FALSE)
1573 imap_scan_tree_recursive(session, new_item);
1576 g_slist_free(item_list);
1578 return IMAP_SUCCESS;
1581 static GSList *imap_parse_list(IMAPFolder *folder, IMAPSession *session,
1582 const gchar *real_path, gchar *separator)
1584 gchar buf[IMAPBUFSIZE];
1586 gchar separator_str[16];
1589 gchar *loc_name, *loc_path;
1590 GSList *item_list = NULL;
1592 FolderItem *new_item;
1594 debug_print("getting list of %s ...\n",
1595 *real_path ? real_path : "\"\"");
1597 str = g_string_new(NULL);
1600 if (sock_gets(SESSION(session)->sock, buf, sizeof(buf)) <= 0) {
1601 log_warning(_("error occurred while getting LIST.\n"));
1605 if (buf[0] != '*' || buf[1] != ' ') {
1606 log_print("IMAP4< %s\n", buf);
1607 if (sscanf(buf, "%*d %16s", buf) < 1 ||
1608 strcmp(buf, "OK") != 0)
1609 log_warning(_("error occurred while getting LIST.\n"));
1613 debug_print("IMAP4< %s\n", buf);
1615 g_string_assign(str, buf);
1617 if (strncmp(p, "LIST ", 5) != 0) continue;
1620 if (*p != '(') continue;
1622 p = strchr_cpy(p, ')', flags, sizeof(flags));
1624 while (*p == ' ') p++;
1626 p = strchr_cpy(p, ' ', separator_str, sizeof(separator_str));
1628 extract_quote(separator_str, '"');
1629 if (!strcmp(separator_str, "NIL"))
1630 separator_str[0] = '\0';
1632 *separator = separator_str[0];
1635 while (*p == ' ') p++;
1636 if (*p == '{' || *p == '"')
1637 p = imap_parse_atom(SESSION(session)->sock, p,
1638 buf, sizeof(buf), str);
1640 strncpy2(buf, p, sizeof(buf));
1641 strtailchomp(buf, separator_str[0]);
1642 if (buf[0] == '\0') continue;
1643 if (!strcmp(buf, real_path)) continue;
1645 if (separator_str[0] != '\0')
1646 subst_char(buf, separator_str[0], '/');
1647 base = g_path_get_basename(buf);
1648 if (base[0] == '.') continue;
1650 loc_name = imap_modified_utf7_to_utf8(base);
1651 loc_path = imap_modified_utf7_to_utf8(buf);
1652 new_item = folder_item_new(FOLDER(folder), loc_name, loc_path);
1653 if (strcasestr(flags, "\\Noinferiors") != NULL)
1654 new_item->no_sub = TRUE;
1655 if (strcmp(buf, "INBOX") != 0 &&
1656 strcasestr(flags, "\\Noselect") != NULL)
1657 new_item->no_select = TRUE;
1659 item_list = g_slist_append(item_list, new_item);
1661 debug_print("folder '%s' found.\n", loc_path);
1667 g_string_free(str, TRUE);
1672 static gint imap_create_tree(Folder *folder)
1674 g_return_val_if_fail(folder != NULL, -1);
1675 g_return_val_if_fail(folder->node != NULL, -1);
1676 g_return_val_if_fail(folder->node->data != NULL, -1);
1677 g_return_val_if_fail(folder->account != NULL, -1);
1679 imap_scan_tree(folder);
1680 imap_create_missing_folders(folder);
1685 static void imap_create_missing_folders(Folder *folder)
1687 g_return_if_fail(folder != NULL);
1690 folder->inbox = imap_create_special_folder
1691 (folder, F_INBOX, "INBOX");
1693 if (!folder->outbox)
1694 folder->outbox = imap_create_special_folder
1695 (folder, F_OUTBOX, "Sent");
1697 folder->draft = imap_create_special_folder
1698 (folder, F_DRAFT, "Drafts");
1700 folder->queue = imap_create_special_folder
1701 (folder, F_QUEUE, "Queue");
1704 folder->trash = imap_create_special_folder
1705 (folder, F_TRASH, "Trash");
1708 static FolderItem *imap_create_special_folder(Folder *folder,
1709 SpecialFolderItemType stype,
1713 FolderItem *new_item;
1715 g_return_val_if_fail(folder != NULL, NULL);
1716 g_return_val_if_fail(folder->node != NULL, NULL);
1717 g_return_val_if_fail(folder->node->data != NULL, NULL);
1718 g_return_val_if_fail(folder->account != NULL, NULL);
1719 g_return_val_if_fail(name != NULL, NULL);
1721 item = FOLDER_ITEM(folder->node->data);
1722 new_item = imap_create_folder(folder, item, name);
1725 g_warning("Can't create '%s'\n", name);
1726 if (!folder->inbox) return NULL;
1728 new_item = imap_create_folder(folder, folder->inbox, name);
1730 g_warning("Can't create '%s' under INBOX\n", name);
1732 new_item->stype = stype;
1734 new_item->stype = stype;
1739 static gchar *imap_folder_get_path(Folder *folder)
1743 g_return_val_if_fail(folder != NULL, NULL);
1744 g_return_val_if_fail(folder->account != NULL, NULL);
1746 folder_path = g_strconcat(get_imap_cache_dir(),
1748 folder->account->recv_server,
1750 folder->account->userid,
1756 static gchar *imap_item_get_path(Folder *folder, FolderItem *item)
1758 gchar *folder_path, *path;
1760 g_return_val_if_fail(folder != NULL, NULL);
1761 g_return_val_if_fail(item != NULL, NULL);
1762 folder_path = imap_folder_get_path(folder);
1764 g_return_val_if_fail(folder_path != NULL, NULL);
1765 if (folder_path[0] == G_DIR_SEPARATOR) {
1767 path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
1770 path = g_strdup(folder_path);
1773 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1774 folder_path, G_DIR_SEPARATOR_S,
1777 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1780 g_free(folder_path);
1785 static FolderItem *imap_create_folder(Folder *folder, FolderItem *parent,
1788 gchar *dirpath, *imap_path;
1789 IMAPSession *session;
1790 FolderItem *new_item;
1796 g_return_val_if_fail(folder != NULL, NULL);
1797 g_return_val_if_fail(folder->account != NULL, NULL);
1798 g_return_val_if_fail(parent != NULL, NULL);
1799 g_return_val_if_fail(name != NULL, NULL);
1801 session = imap_session_get(folder);
1802 if (!session) return NULL;
1804 if (!folder_item_parent(parent) && strcmp(name, "INBOX") == 0)
1805 dirpath = g_strdup(name);
1806 else if (parent->path)
1807 dirpath = g_strconcat(parent->path, "/", name, NULL);
1808 else if ((p = strchr(name, '/')) != NULL && *(p + 1) != '\0')
1809 dirpath = g_strdup(name);
1810 else if (folder->account->imap_dir && *folder->account->imap_dir) {
1813 Xstrdup_a(imap_dir, folder->account->imap_dir, return NULL);
1814 strtailchomp(imap_dir, '/');
1815 dirpath = g_strconcat(imap_dir, "/", name, NULL);
1817 dirpath = g_strdup(name);
1819 /* keep trailing directory separator to create a folder that contains
1821 imap_path = imap_utf8_to_modified_utf7(dirpath);
1822 strtailchomp(dirpath, '/');
1823 Xstrdup_a(new_name, name, {g_free(dirpath); return NULL;});
1824 strtailchomp(new_name, '/');
1825 separator = imap_get_path_separator(IMAP_FOLDER(folder), imap_path);
1826 imap_path_separator_subst(imap_path, separator);
1827 subst_char(new_name, '/', separator);
1829 if (strcmp(name, "INBOX") != 0) {
1832 gboolean exist = FALSE;
1834 argbuf = g_ptr_array_new();
1835 ok = imap_cmd_list(session, NULL, imap_path,
1837 if (ok != IMAP_SUCCESS) {
1838 log_warning(_("can't create mailbox: LIST failed\n"));
1841 ptr_array_free_strings(argbuf);
1842 g_ptr_array_free(argbuf, TRUE);
1846 for (i = 0; i < argbuf->len; i++) {
1848 str = g_ptr_array_index(argbuf, i);
1849 if (!strncmp(str, "LIST ", 5)) {
1854 ptr_array_free_strings(argbuf);
1855 g_ptr_array_free(argbuf, TRUE);
1858 ok = imap_cmd_create(session, imap_path);
1859 if (ok != IMAP_SUCCESS) {
1860 log_warning(_("can't create mailbox\n"));
1868 new_item = folder_item_new(folder, new_name, dirpath);
1869 folder_item_append(parent, new_item);
1873 dirpath = folder_item_get_path(new_item);
1874 if (!is_dir_exist(dirpath))
1875 make_dir_hier(dirpath);
1881 static gint imap_rename_folder(Folder *folder, FolderItem *item,
1886 gchar *real_oldpath;
1887 gchar *real_newpath;
1889 gchar *old_cache_dir;
1890 gchar *new_cache_dir;
1891 IMAPSession *session;
1894 gint exists, recent, unseen;
1895 guint32 uid_validity;
1897 g_return_val_if_fail(folder != NULL, -1);
1898 g_return_val_if_fail(item != NULL, -1);
1899 g_return_val_if_fail(item->path != NULL, -1);
1900 g_return_val_if_fail(name != NULL, -1);
1902 if (strchr(name, imap_get_path_separator(IMAP_FOLDER(folder), item->path)) != NULL) {
1903 g_warning(_("New folder name must not contain the namespace "
1908 session = imap_session_get(folder);
1909 if (!session) return -1;
1911 real_oldpath = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1913 g_free(session->mbox);
1914 session->mbox = NULL;
1915 ok = imap_cmd_examine(session, "INBOX",
1916 &exists, &recent, &unseen, &uid_validity, FALSE);
1917 if (ok != IMAP_SUCCESS) {
1918 g_free(real_oldpath);
1922 separator = imap_get_path_separator(IMAP_FOLDER(folder), item->path);
1923 if (strchr(item->path, G_DIR_SEPARATOR)) {
1924 dirpath = g_path_get_dirname(item->path);
1925 newpath = g_strconcat(dirpath, G_DIR_SEPARATOR_S, name, NULL);
1928 newpath = g_strdup(name);
1930 real_newpath = imap_utf8_to_modified_utf7(newpath);
1931 imap_path_separator_subst(real_newpath, separator);
1933 ok = imap_cmd_rename(session, real_oldpath, real_newpath);
1934 if (ok != IMAP_SUCCESS) {
1935 log_warning(_("can't rename mailbox: %s to %s\n"),
1936 real_oldpath, real_newpath);
1937 g_free(real_oldpath);
1939 g_free(real_newpath);
1944 item->name = g_strdup(name);
1946 old_cache_dir = folder_item_get_path(item);
1948 paths[0] = g_strdup(item->path);
1950 g_node_traverse(item->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1951 imap_rename_folder_func, paths);
1953 if (is_dir_exist(old_cache_dir)) {
1954 new_cache_dir = folder_item_get_path(item);
1955 if (rename(old_cache_dir, new_cache_dir) < 0) {
1956 FILE_OP_ERROR(old_cache_dir, "rename");
1958 g_free(new_cache_dir);
1961 g_free(old_cache_dir);
1964 g_free(real_oldpath);
1965 g_free(real_newpath);
1970 static gint imap_remove_folder(Folder *folder, FolderItem *item)
1973 IMAPSession *session;
1976 gint exists, recent, unseen;
1977 guint32 uid_validity;
1979 g_return_val_if_fail(folder != NULL, -1);
1980 g_return_val_if_fail(item != NULL, -1);
1981 g_return_val_if_fail(item->path != NULL, -1);
1983 session = imap_session_get(folder);
1984 if (!session) return -1;
1986 path = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1988 ok = imap_cmd_examine(session, "INBOX",
1989 &exists, &recent, &unseen, &uid_validity, FALSE);
1990 if (ok != IMAP_SUCCESS) {
1995 ok = imap_cmd_delete(session, path);
1996 if (ok != IMAP_SUCCESS) {
1997 log_warning(_("can't delete mailbox\n"));
2003 cache_dir = folder_item_get_path(item);
2004 if (is_dir_exist(cache_dir) && remove_dir_recursive(cache_dir) < 0)
2005 g_warning("can't remove directory '%s'\n", cache_dir);
2007 folder_item_remove(item);
2012 typedef struct _uncached_data {
2013 IMAPSession *session;
2015 MsgNumberList *numlist;
2019 static void *imap_get_uncached_messages_thread(void *data)
2021 uncached_data *stuff = (uncached_data *)data;
2022 IMAPSession *session = stuff->session;
2023 FolderItem *item = stuff->item;
2024 MsgNumberList *numlist = stuff->numlist;
2027 GSList *newlist = NULL;
2028 GSList *llast = NULL;
2029 GString *str = NULL;
2031 GSList *seq_list, *cur;
2034 if (session == NULL || item == NULL || item->folder == NULL
2035 || FOLDER_CLASS(item->folder) != &imap_class) {
2040 seq_list = imap_get_seq_set_from_numlist(numlist);
2041 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
2042 imapset = cur->data;
2044 if (imap_cmd_envelope(session, imapset)
2046 log_warning(_("can't get envelope\n"));
2050 str = g_string_new(NULL);
2053 if ((tmp =sock_getline(SESSION(session)->sock)) == NULL) {
2054 log_warning(_("error occurred while getting envelope.\n"));
2055 g_string_free(str, TRUE);
2060 if (tmp[0] != '*' || tmp[1] != ' ') {
2061 log_print("IMAP4< %s\n", tmp);
2065 if (strstr(tmp, "FETCH") == NULL) {
2066 log_print("IMAP4< %s\n", tmp);
2070 log_print("IMAP4< %s\n", tmp);
2071 g_string_assign(str, tmp);
2074 msginfo = imap_parse_envelope
2075 (SESSION(session)->sock, item, str);
2077 log_warning(_("can't parse envelope: %s\n"), str->str);
2080 if (item->stype == F_QUEUE) {
2081 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_QUEUED);
2082 } else if (item->stype == F_DRAFT) {
2083 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_DRAFT);
2086 msginfo->folder = item;
2089 llast = newlist = g_slist_append(newlist, msginfo);
2091 llast = g_slist_append(llast, msginfo);
2092 llast = llast->next;
2096 g_string_free(str, TRUE);
2098 imap_seq_set_free(seq_list);
2100 session_set_access_time(SESSION(session));
2106 static GSList *imap_get_uncached_messages(IMAPSession *session,
2108 MsgNumberList *numlist)
2110 uncached_data *data = g_new0(uncached_data, 1);
2111 GSList *result = NULL;
2116 data->session = session;
2118 data->numlist = numlist;
2120 if (prefs_common.work_offline && !imap_gtk_should_override()) {
2125 #if (defined USE_PTHREAD && defined __GLIBC__ && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 3)))
2126 if (pthread_create(&pt, PTHREAD_CREATE_JOINABLE,
2127 imap_get_uncached_messages_thread, data) != 0) {
2128 result = (GSList *)imap_get_uncached_messages_thread(data);
2132 debug_print("+++waiting for imap_get_uncached_messages_thread...\n");
2133 while(!data->done) {
2134 /* don't let the interface freeze while waiting */
2137 debug_print("---imap_get_uncached_messages_thread done\n");
2139 /* get the thread's return value and clean its resources */
2140 pthread_join(pt, (void *)&result);
2142 result = (GSList *)imap_get_uncached_messages_thread(data);
2148 static void imap_delete_all_cached_messages(FolderItem *item)
2152 g_return_if_fail(item != NULL);
2153 g_return_if_fail(item->folder != NULL);
2154 g_return_if_fail(FOLDER_CLASS(item->folder) == &imap_class);
2156 debug_print("Deleting all cached messages...\n");
2158 dir = folder_item_get_path(item);
2159 if (is_dir_exist(dir))
2160 remove_all_numbered_files(dir);
2163 debug_print("done.\n");
2167 static SockInfo *imap_open_tunnel(const gchar *server,
2168 const gchar *tunnelcmd,
2171 static SockInfo *imap_open_tunnel(const gchar *server,
2172 const gchar *tunnelcmd)
2177 if ((sock = sock_connect_cmd(server, tunnelcmd)) == NULL) {
2178 log_warning(_("Can't establish IMAP4 session with: %s\n"),
2183 return imap_init_sock(sock, ssl_type);
2185 return imap_init_sock(sock);
2189 void *imap_open_thread(void *data)
2191 SockInfo *sock = NULL;
2192 thread_data *td = (thread_data *)data;
2193 if ((sock = sock_connect(td->server, td->port)) == NULL) {
2194 log_warning(_("Can't connect to IMAP4 server: %s:%d\n"),
2195 td->server, td->port);
2205 static SockInfo *imap_open(const gchar *server, gushort port,
2208 static SockInfo *imap_open(const gchar *server, gushort port)
2211 thread_data *td = g_new0(thread_data, 1);
2215 SockInfo *sock = NULL;
2218 td->ssl_type = ssl_type;
2220 td->server = g_strdup(server);
2224 statusbar_print_all(_("Connecting to IMAP4 server: %s..."), server);
2226 #if (defined USE_PTHREAD && defined __GLIBC__ && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 3)))
2227 if (pthread_create(&pt, PTHREAD_CREATE_JOINABLE,
2228 imap_open_thread, td) != 0) {
2229 statusbar_pop_all();
2230 sock = imap_open_thread(td);
2232 debug_print("+++waiting for imap_open_thread...\n");
2234 /* don't let the interface freeze while waiting */
2238 /* get the thread's return value and clean its resources */
2239 pthread_join(pt, (void *)&sock);
2242 sock = imap_open_thread(td);
2245 if (sock && td->ssl_type == SSL_TUNNEL && !ssl_init_socket(sock)) {
2246 log_warning(_("Can't establish IMAP4 session with: %s:%d\n"),
2247 td->server, td->port);
2256 debug_print("---imap_open_thread returned %p\n", sock);
2257 statusbar_pop_all();
2259 if(!sock && !prefs_common.no_recv_err_panel) {
2260 alertpanel_error(_("Can't connect to IMAP4 server: %s:%d"),
2268 static SockInfo *imap_init_sock(SockInfo *sock, SSLType ssl_type)
2270 static SockInfo *imap_init_sock(SockInfo *sock)
2277 static GList *imap_parse_namespace_str(gchar *str)
2282 IMAPNameSpace *namespace;
2283 GList *ns_list = NULL;
2285 while (*p != '\0') {
2286 /* parse ("#foo" "/") */
2288 while (*p && *p != '(') p++;
2289 if (*p == '\0') break;
2292 while (*p && *p != '"') p++;
2293 if (*p == '\0') break;
2297 while (*p && *p != '"') p++;
2298 if (*p == '\0') break;
2302 while (*p && isspace(*p)) p++;
2303 if (*p == '\0') break;
2304 if (strncmp(p, "NIL", 3) == 0)
2306 else if (*p == '"') {
2309 while (*p && *p != '"') p++;
2310 if (*p == '\0') break;
2315 while (*p && *p != ')') p++;
2316 if (*p == '\0') break;
2319 namespace = g_new(IMAPNameSpace, 1);
2320 namespace->name = g_strdup(name);
2321 namespace->separator = separator ? separator[0] : '\0';
2322 ns_list = g_list_append(ns_list, namespace);
2328 static void imap_parse_namespace(IMAPSession *session, IMAPFolder *folder)
2333 g_return_if_fail(session != NULL);
2334 g_return_if_fail(folder != NULL);
2336 if (folder->ns_personal != NULL ||
2337 folder->ns_others != NULL ||
2338 folder->ns_shared != NULL)
2341 if (!imap_has_capability(session, "NAMESPACE")) {
2342 imap_get_namespace_by_list(session, folder);
2346 if (imap_cmd_namespace(session, &ns_str)
2348 log_warning(_("can't get namespace\n"));
2352 str_array = strsplit_parenthesis(ns_str, '(', ')', 3);
2353 if (str_array == NULL) {
2355 imap_get_namespace_by_list(session, folder);
2359 folder->ns_personal = imap_parse_namespace_str(str_array[0]);
2360 if (str_array[0] && str_array[1])
2361 folder->ns_others = imap_parse_namespace_str(str_array[1]);
2362 if (str_array[0] && str_array[1] && str_array[2])
2363 folder->ns_shared = imap_parse_namespace_str(str_array[2]);
2364 g_strfreev(str_array);
2368 static void imap_get_namespace_by_list(IMAPSession *session, IMAPFolder *folder)
2370 GSList *item_list, *cur;
2371 gchar separator = '\0';
2372 IMAPNameSpace *namespace;
2374 g_return_if_fail(session != NULL);
2375 g_return_if_fail(folder != NULL);
2377 if (folder->ns_personal != NULL ||
2378 folder->ns_others != NULL ||
2379 folder->ns_shared != NULL)
2382 imap_gen_send(session, "LIST \"\" \"\"");
2383 item_list = imap_parse_list(folder, session, "", &separator);
2384 for (cur = item_list; cur != NULL; cur = cur->next)
2385 folder_item_destroy(FOLDER_ITEM(cur->data));
2386 g_slist_free(item_list);
2388 namespace = g_new(IMAPNameSpace, 1);
2389 namespace->name = g_strdup("");
2390 namespace->separator = separator;
2391 folder->ns_personal = g_list_append(NULL, namespace);
2394 static IMAPNameSpace *imap_find_namespace_from_list(GList *ns_list,
2397 IMAPNameSpace *namespace = NULL;
2398 gchar *tmp_path, *name;
2400 if (!path) path = "";
2402 for (; ns_list != NULL; ns_list = ns_list->next) {
2403 IMAPNameSpace *tmp_ns = ns_list->data;
2405 Xstrcat_a(tmp_path, path, "/", return namespace);
2406 Xstrdup_a(name, tmp_ns->name, return namespace);
2407 if (tmp_ns->separator && tmp_ns->separator != '/') {
2408 subst_char(tmp_path, tmp_ns->separator, '/');
2409 subst_char(name, tmp_ns->separator, '/');
2411 if (strncmp(tmp_path, name, strlen(name)) == 0)
2418 static IMAPNameSpace *imap_find_namespace(IMAPFolder *folder,
2421 IMAPNameSpace *namespace;
2423 g_return_val_if_fail(folder != NULL, NULL);
2425 namespace = imap_find_namespace_from_list(folder->ns_personal, path);
2426 if (namespace) return namespace;
2427 namespace = imap_find_namespace_from_list(folder->ns_others, path);
2428 if (namespace) return namespace;
2429 namespace = imap_find_namespace_from_list(folder->ns_shared, path);
2430 if (namespace) return namespace;
2435 static gchar imap_get_path_separator(IMAPFolder *folder, const gchar *path)
2437 IMAPNameSpace *namespace;
2438 gchar separator = '/';
2440 namespace = imap_find_namespace(folder, path);
2441 if (namespace && namespace->separator)
2442 separator = namespace->separator;
2447 static gchar *imap_get_real_path(IMAPFolder *folder, const gchar *path)
2452 g_return_val_if_fail(folder != NULL, NULL);
2453 g_return_val_if_fail(path != NULL, NULL);
2455 real_path = imap_utf8_to_modified_utf7(path);
2456 separator = imap_get_path_separator(folder, path);
2457 imap_path_separator_subst(real_path, separator);
2462 static gchar *imap_parse_atom(SockInfo *sock, gchar *src,
2463 gchar *dest, gint dest_len, GString *str)
2465 gchar *cur_pos = src;
2468 g_return_val_if_fail(str != NULL, cur_pos);
2470 /* read the next line if the current response buffer is empty */
2471 while (isspace(*(guchar *)cur_pos)) cur_pos++;
2472 while (*cur_pos == '\0') {
2473 if ((nextline = imap_getline(sock)) == NULL)
2475 g_string_assign(str, nextline);
2477 strretchomp(nextline);
2478 /* log_print("IMAP4< %s\n", nextline); */
2479 debug_print("IMAP4< %s\n", nextline);
2482 while (isspace(*(guchar *)cur_pos)) cur_pos++;
2485 if (!strncmp(cur_pos, "NIL", 3)) {
2488 } else if (*cur_pos == '\"') {
2491 p = get_quoted(cur_pos, '\"', dest, dest_len);
2492 cur_pos = p ? p : cur_pos + 2;
2493 } else if (*cur_pos == '{') {
2498 cur_pos = strchr_cpy(cur_pos + 1, '}', buf, sizeof(buf));
2500 g_return_val_if_fail(len >= 0, cur_pos);
2502 g_string_truncate(str, 0);
2506 if ((nextline = imap_getline(sock)) == NULL)
2508 line_len += strlen(nextline);
2509 g_string_append(str, nextline);
2511 strretchomp(nextline);
2512 /* log_print("IMAP4< %s\n", nextline); */
2513 debug_print("IMAP4< %s\n", nextline);
2515 } while (line_len < len);
2517 memcpy(dest, cur_pos, MIN(len, dest_len - 1));
2518 dest[MIN(len, dest_len - 1)] = '\0';
2525 static gchar *imap_get_header(SockInfo *sock, gchar *cur_pos, gchar **headers,
2535 g_return_val_if_fail(str != NULL, cur_pos);
2537 while (isspace(*(guchar *)cur_pos)) cur_pos++;
2539 g_return_val_if_fail(*cur_pos == '{', cur_pos);
2541 cur_pos = strchr_cpy(cur_pos + 1, '}', buf, sizeof(buf));
2543 g_return_val_if_fail(len >= 0, cur_pos);
2545 g_string_truncate(str, 0);
2549 if ((nextline = sock_getline(sock)) == NULL) {
2553 block_len += strlen(nextline);
2554 g_string_append(str, nextline);
2556 strretchomp(nextline);
2557 /* debug_print("IMAP4< %s\n", nextline); */
2559 } while (block_len < len);
2561 debug_print("IMAP4< [contents of BODY.PEEK[HEADER_FIELDS (...)]\n");
2563 *headers = g_strndup(cur_pos, len);
2566 while (isspace(*(guchar *)cur_pos)) cur_pos++;
2567 while (*cur_pos == '\0') {
2568 if ((nextline = sock_getline(sock)) == NULL)
2570 g_string_assign(str, nextline);
2572 strretchomp(nextline);
2573 debug_print("IMAP4< %s\n", nextline);
2576 while (isspace(*(guchar *)cur_pos)) cur_pos++;
2582 static MsgFlags imap_parse_flags(const gchar *flag_str)
2584 const gchar *p = flag_str;
2585 MsgFlags flags = {0, 0};
2587 flags.perm_flags = MSG_UNREAD;
2589 while ((p = strchr(p, '\\')) != NULL) {
2592 if (g_ascii_strncasecmp(p, "Recent", 6) == 0 && MSG_IS_UNREAD(flags)) {
2593 MSG_SET_PERM_FLAGS(flags, MSG_NEW);
2594 } else if (g_ascii_strncasecmp(p, "Seen", 4) == 0) {
2595 MSG_UNSET_PERM_FLAGS(flags, MSG_NEW|MSG_UNREAD);
2596 } else if (g_ascii_strncasecmp(p, "Deleted", 7) == 0) {
2597 MSG_SET_PERM_FLAGS(flags, MSG_DELETED);
2598 } else if (g_ascii_strncasecmp(p, "Flagged", 7) == 0) {
2599 MSG_SET_PERM_FLAGS(flags, MSG_MARKED);
2600 } else if (g_ascii_strncasecmp(p, "Answered", 8) == 0) {
2601 MSG_SET_PERM_FLAGS(flags, MSG_REPLIED);
2608 static MsgInfo *imap_parse_envelope(SockInfo *sock, FolderItem *item,
2611 gchar buf[IMAPBUFSIZE];
2612 MsgInfo *msginfo = NULL;
2617 MsgFlags flags = {0, 0}, imap_flags = {0, 0};
2619 g_return_val_if_fail(line_str != NULL, NULL);
2620 g_return_val_if_fail(line_str->str[0] == '*' &&
2621 line_str->str[1] == ' ', NULL);
2623 MSG_SET_TMP_FLAGS(flags, MSG_IMAP);
2624 if (item->stype == F_QUEUE) {
2625 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
2626 } else if (item->stype == F_DRAFT) {
2627 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
2630 cur_pos = line_str->str + 2;
2632 #define PARSE_ONE_ELEMENT(ch) \
2634 cur_pos = strchr_cpy(cur_pos, ch, buf, sizeof(buf)); \
2635 if (cur_pos == NULL) { \
2636 g_warning("cur_pos == NULL\n"); \
2637 procmsg_msginfo_free(msginfo); \
2642 PARSE_ONE_ELEMENT(' ');
2645 PARSE_ONE_ELEMENT(' ');
2646 g_return_val_if_fail(!strcmp(buf, "FETCH"), NULL);
2648 g_return_val_if_fail(*cur_pos == '(', NULL);
2651 while (*cur_pos != '\0' && *cur_pos != ')') {
2652 while (*cur_pos == ' ') cur_pos++;
2654 if (!strncmp(cur_pos, "UID ", 4)) {
2656 uid = strtoul(cur_pos, &cur_pos, 10);
2657 } else if (!strncmp(cur_pos, "FLAGS ", 6)) {
2659 if (*cur_pos != '(') {
2660 g_warning("*cur_pos != '('\n");
2661 procmsg_msginfo_free(msginfo);
2665 PARSE_ONE_ELEMENT(')');
2666 imap_flags = imap_parse_flags(buf);
2667 } else if (!strncmp(cur_pos, "RFC822.SIZE ", 12)) {
2669 size = strtol(cur_pos, &cur_pos, 10);
2670 } else if (!strncmp(cur_pos, "BODY[HEADER.FIELDS ", 19)) {
2674 if (*cur_pos != '(') {
2675 g_warning("*cur_pos != '('\n");
2676 procmsg_msginfo_free(msginfo);
2680 PARSE_ONE_ELEMENT(')');
2681 if (*cur_pos != ']') {
2682 g_warning("*cur_pos != ']'\n");
2683 procmsg_msginfo_free(msginfo);
2687 cur_pos = imap_get_header(sock, cur_pos, &headers,
2689 msginfo = procheader_parse_str(headers, flags, FALSE, FALSE);
2692 g_warning("invalid FETCH response: %s\n", cur_pos);
2698 msginfo->msgnum = uid;
2699 msginfo->size = size;
2700 msginfo->flags.tmp_flags |= imap_flags.tmp_flags;
2701 msginfo->flags.perm_flags = imap_flags.perm_flags;
2707 static gchar *imap_get_flag_str(IMAPFlags flags)
2712 str = g_string_new(NULL);
2714 if (IMAP_IS_SEEN(flags)) g_string_append(str, "\\Seen ");
2715 if (IMAP_IS_ANSWERED(flags)) g_string_append(str, "\\Answered ");
2716 if (IMAP_IS_FLAGGED(flags)) g_string_append(str, "\\Flagged ");
2717 if (IMAP_IS_DELETED(flags)) g_string_append(str, "\\Deleted ");
2718 if (IMAP_IS_DRAFT(flags)) g_string_append(str, "\\Draft");
2720 if (str->len > 0 && str->str[str->len - 1] == ' ')
2721 g_string_truncate(str, str->len - 1);
2724 g_string_free(str, FALSE);
2729 static gint imap_set_message_flags(IMAPSession *session,
2730 MsgNumberList *numlist,
2740 flag_str = imap_get_flag_str(flags);
2741 cmd = g_strconcat(is_set ? "+FLAGS.SILENT (" : "-FLAGS.SILENT (",
2742 flag_str, ")", NULL);
2745 seq_list = imap_get_seq_set_from_numlist(numlist);
2746 imapset = get_seq_set_from_seq_list(seq_list);
2748 ok = imap_cmd_store(session, imapset, cmd);
2751 imap_seq_set_free(seq_list);
2757 typedef struct _select_data {
2758 IMAPSession *session;
2763 guint32 *uid_validity;
2767 static void *imap_select_thread(void *data)
2769 select_data *stuff = (select_data *)data;
2770 IMAPSession *session = stuff->session;
2771 gchar *real_path = stuff->real_path;
2772 gint *exists = stuff->exists;
2773 gint *recent = stuff->recent;
2774 gint *unseen = stuff->unseen;
2775 guint32 *uid_validity = stuff->uid_validity;
2778 ok = imap_cmd_select(session, real_path,
2779 exists, recent, unseen, uid_validity, TRUE);
2781 return GINT_TO_POINTER(ok);
2784 static gint imap_select(IMAPSession *session, IMAPFolder *folder,
2786 gint *exists, gint *recent, gint *unseen,
2787 guint32 *uid_validity, gboolean block)
2791 gint exists_, recent_, unseen_;
2792 guint32 uid_validity_;
2794 if (!exists || !recent || !unseen || !uid_validity) {
2795 if (session->mbox && strcmp(session->mbox, path) == 0)
2796 return IMAP_SUCCESS;
2800 uid_validity = &uid_validity_;
2803 g_free(session->mbox);
2804 session->mbox = NULL;
2806 real_path = imap_get_real_path(folder, path);
2808 #if (defined USE_PTHREAD && defined __GLIBC__ && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 3)))
2809 if (block == FALSE) {
2810 select_data *data = g_new0(select_data, 1);
2813 data->session = session;
2814 data->real_path = real_path;
2815 data->exists = exists;
2816 data->recent = recent;
2817 data->unseen = unseen;
2818 data->uid_validity = uid_validity;
2821 if (pthread_create(&pt, PTHREAD_CREATE_JOINABLE,
2822 imap_select_thread, data) != 0) {
2823 ok = GPOINTER_TO_INT(imap_select_thread(data));
2826 debug_print("+++waiting for imap_select_thread...\n");
2827 while(!data->done) {
2828 /* don't let the interface freeze while waiting */
2831 debug_print("---imap_select_thread done\n");
2833 /* get the thread's return value and clean its resources */
2834 pthread_join(pt, &tmp);
2835 ok = GPOINTER_TO_INT(tmp);
2839 ok = imap_cmd_select(session, real_path,
2840 exists, recent, unseen, uid_validity, block);
2843 ok = imap_cmd_select(session, real_path,
2844 exists, recent, unseen, uid_validity, block);
2846 if (ok != IMAP_SUCCESS)
2847 log_warning(_("can't select folder: %s\n"), real_path);
2849 session->mbox = g_strdup(path);
2850 session->folder_content_changed = FALSE;
2857 #define THROW(err) { ok = err; goto catch; }
2859 static gint imap_status(IMAPSession *session, IMAPFolder *folder,
2861 gint *messages, gint *recent,
2862 guint32 *uid_next, guint32 *uid_validity,
2863 gint *unseen, gboolean block)
2868 GPtrArray *argbuf = NULL;
2871 if (messages && recent && uid_next && uid_validity && unseen) {
2872 *messages = *recent = *uid_next = *uid_validity = *unseen = 0;
2873 argbuf = g_ptr_array_new();
2876 real_path = imap_get_real_path(folder, path);
2877 QUOTE_IF_REQUIRED(real_path_, real_path);
2878 imap_gen_send(session, "STATUS %s "
2879 "(MESSAGES RECENT UIDNEXT UIDVALIDITY UNSEEN)",
2882 ok = imap_cmd_ok_with_block(session, argbuf, block);
2883 if (ok != IMAP_SUCCESS || !argbuf) THROW(ok);
2885 str = search_array_str(argbuf, "STATUS");
2886 if (!str) THROW(IMAP_ERROR);
2888 str = strchr(str, '(');
2889 if (!str) THROW(IMAP_ERROR);
2891 while (*str != '\0' && *str != ')') {
2892 while (*str == ' ') str++;
2894 if (!strncmp(str, "MESSAGES ", 9)) {
2896 *messages = strtol(str, &str, 10);
2897 } else if (!strncmp(str, "RECENT ", 7)) {
2899 *recent = strtol(str, &str, 10);
2900 } else if (!strncmp(str, "UIDNEXT ", 8)) {
2902 *uid_next = strtoul(str, &str, 10);
2903 } else if (!strncmp(str, "UIDVALIDITY ", 12)) {
2905 *uid_validity = strtoul(str, &str, 10);
2906 } else if (!strncmp(str, "UNSEEN ", 7)) {
2908 *unseen = strtol(str, &str, 10);
2910 g_warning("invalid STATUS response: %s\n", str);
2918 ptr_array_free_strings(argbuf);
2919 g_ptr_array_free(argbuf, TRUE);
2927 static gboolean imap_has_capability(IMAPSession *session, const gchar *cap)
2931 for (p = session->capability; *p != NULL; ++p) {
2932 if (!g_ascii_strcasecmp(*p, cap))
2939 static void imap_free_capabilities(IMAPSession *session)
2941 g_strfreev(session->capability);
2942 session->capability = NULL;
2945 /* low-level IMAP4rev1 commands */
2947 static gint imap_cmd_authenticate(IMAPSession *session, const gchar *user,
2948 const gchar *pass, IMAPAuthType type)
2955 gchar hexdigest[33];
2959 auth_type = "CRAM-MD5";
2961 imap_gen_send(session, "AUTHENTICATE %s", auth_type);
2962 ok = imap_gen_recv(session, &buf);
2963 if (ok != IMAP_SUCCESS || buf[0] != '+' || buf[1] != ' ') {
2968 challenge = g_malloc(strlen(buf + 2) + 1);
2969 challenge_len = base64_decode(challenge, buf + 2, -1);
2970 challenge[challenge_len] = '\0';
2972 log_print("IMAP< [Decoded: %s]\n", challenge);
2974 md5_hex_hmac(hexdigest, challenge, challenge_len, pass, strlen(pass));
2977 response = g_strdup_printf("%s %s", user, hexdigest);
2978 log_print("IMAP> [Encoded: %s]\n", response);
2979 response64 = g_malloc((strlen(response) + 3) * 2 + 1);
2980 base64_encode(response64, response, strlen(response));
2983 log_print("IMAP> %s\n", response64);
2984 sock_puts(SESSION(session)->sock, response64);
2985 ok = imap_cmd_ok(session, NULL);
2986 if (ok != IMAP_SUCCESS)
2987 log_warning(_("IMAP4 authentication failed.\n"));
2992 static gint imap_cmd_login(IMAPSession *session,
2993 const gchar *user, const gchar *pass)
2998 imap_gen_send(session, "LOGIN {%d}\r\n%s {%d}\r\n%s",
3000 strlen(pass), pass);
3002 ok = imap_gen_recv_with_block(session, &ans, TRUE);
3003 if (ok != IMAP_SUCCESS || ans[0] != '+' || ans[1] != ' ') {
3008 ok = imap_gen_recv_with_block(session, &ans, TRUE);
3009 if (ok != IMAP_SUCCESS || ans[0] != '+' || ans[1] != ' ') {
3014 ok = imap_cmd_ok(session, NULL);
3015 if (ok != IMAP_SUCCESS)
3016 log_warning(_("IMAP4 login failed.\n"));
3021 static gint imap_cmd_logout(IMAPSession *session)
3023 imap_gen_send(session, "LOGOUT");
3024 return imap_cmd_ok(session, NULL);
3027 static gint imap_cmd_noop(IMAPSession *session)
3029 imap_gen_send(session, "NOOP");
3030 return imap_cmd_ok(session, NULL);
3034 static gint imap_cmd_starttls(IMAPSession *session)
3036 imap_gen_send(session, "STARTTLS");
3037 return imap_cmd_ok(session, NULL);
3041 #define THROW(err) { ok = err; goto catch; }
3043 static gint imap_cmd_namespace(IMAPSession *session, gchar **ns_str)
3049 argbuf = g_ptr_array_new();
3051 imap_gen_send(session, "NAMESPACE");
3052 if ((ok = imap_cmd_ok(session, argbuf)) != IMAP_SUCCESS) THROW(ok);
3054 str = search_array_str(argbuf, "NAMESPACE");
3055 if (!str) THROW(IMAP_ERROR);
3057 *ns_str = g_strdup(str);
3060 ptr_array_free_strings(argbuf);
3061 g_ptr_array_free(argbuf, TRUE);
3068 static gint imap_cmd_list(IMAPSession *session, const gchar *ref,
3069 const gchar *mailbox, GPtrArray *argbuf)
3071 gchar *ref_, *mailbox_;
3073 if (!ref) ref = "\"\"";
3074 if (!mailbox) mailbox = "\"\"";
3076 QUOTE_IF_REQUIRED(ref_, ref);
3077 QUOTE_IF_REQUIRED(mailbox_, mailbox);
3078 imap_gen_send(session, "LIST %s %s", ref_, mailbox_);
3080 return imap_cmd_ok(session, argbuf);
3083 #define THROW goto catch
3085 static gint imap_cmd_do_select(IMAPSession *session, const gchar *folder,
3087 gint *exists, gint *recent, gint *unseen,
3088 guint32 *uid_validity, gboolean block)
3095 unsigned int uid_validity_;
3097 *exists = *recent = *unseen = *uid_validity = 0;
3098 argbuf = g_ptr_array_new();
3101 select_cmd = "EXAMINE";
3103 select_cmd = "SELECT";
3105 QUOTE_IF_REQUIRED(folder_, folder);
3106 imap_gen_send(session, "%s %s", select_cmd, folder_);
3108 if ((ok = imap_cmd_ok_with_block(session, argbuf, block)) != IMAP_SUCCESS) THROW;
3110 resp_str = search_array_contain_str(argbuf, "EXISTS");
3112 if (sscanf(resp_str,"%d EXISTS", exists) != 1) {
3113 g_warning("imap_cmd_select(): invalid EXISTS line.\n");
3118 resp_str = search_array_contain_str(argbuf, "RECENT");
3120 if (sscanf(resp_str, "%d RECENT", recent) != 1) {
3121 g_warning("imap_cmd_select(): invalid RECENT line.\n");
3126 resp_str = search_array_contain_str(argbuf, "UIDVALIDITY");
3128 if (sscanf(resp_str, "OK [UIDVALIDITY %u] ", &uid_validity_)
3130 g_warning("imap_cmd_select(): invalid UIDVALIDITY line.\n");
3133 *uid_validity = uid_validity_;
3136 resp_str = search_array_contain_str(argbuf, "UNSEEN");
3138 if (sscanf(resp_str, "OK [UNSEEN %d] ", unseen) != 1) {
3139 g_warning("imap_cmd_select(): invalid UNSEEN line.\n");
3145 ptr_array_free_strings(argbuf);
3146 g_ptr_array_free(argbuf, TRUE);
3151 static gint imap_cmd_select(IMAPSession *session, const gchar *folder,
3152 gint *exists, gint *recent, gint *unseen,
3153 guint32 *uid_validity, gboolean block)
3155 return imap_cmd_do_select(session, folder, FALSE,
3156 exists, recent, unseen, uid_validity, block);
3159 static gint imap_cmd_examine(IMAPSession *session, const gchar *folder,
3160 gint *exists, gint *recent, gint *unseen,
3161 guint32 *uid_validity, gboolean block)
3163 return imap_cmd_do_select(session, folder, TRUE,
3164 exists, recent, unseen, uid_validity, block);
3169 static gint imap_cmd_create(IMAPSession *session, const gchar *folder)
3173 QUOTE_IF_REQUIRED(folder_, folder);
3174 imap_gen_send(session, "CREATE %s", folder_);
3176 return imap_cmd_ok(session, NULL);
3179 static gint imap_cmd_rename(IMAPSession *session, const gchar *old_folder,
3180 const gchar *new_folder)
3182 gchar *old_folder_, *new_folder_;
3184 QUOTE_IF_REQUIRED(old_folder_, old_folder);
3185 QUOTE_IF_REQUIRED(new_folder_, new_folder);
3186 imap_gen_send(session, "RENAME %s %s", old_folder_, new_folder_);
3188 return imap_cmd_ok(session, NULL);
3191 static gint imap_cmd_delete(IMAPSession *session, const gchar *folder)
3195 QUOTE_IF_REQUIRED(folder_, folder);
3196 imap_gen_send(session, "DELETE %s", folder_);
3198 return imap_cmd_ok(session, NULL);
3201 static gint imap_cmd_search(IMAPSession *session, const gchar *criteria,
3202 GSList **list, gboolean block)
3208 g_return_val_if_fail(criteria != NULL, IMAP_ERROR);
3209 g_return_val_if_fail(list != NULL, IMAP_ERROR);
3213 argbuf = g_ptr_array_new();
3214 imap_gen_send(session, "UID SEARCH %s", criteria);
3216 ok = imap_cmd_ok_with_block(session, argbuf, block);
3217 if (ok != IMAP_SUCCESS) {
3218 ptr_array_free_strings(argbuf);
3219 g_ptr_array_free(argbuf, TRUE);
3223 if ((uidlist = search_array_str(argbuf, "SEARCH ")) != NULL) {
3224 gchar **strlist, **p;
3226 strlist = g_strsplit(uidlist + 7, " ", 0);
3227 for (p = strlist; *p != NULL; ++p) {
3230 if (sscanf(*p, "%u", &msgnum) == 1)
3231 *list = g_slist_append(*list, GINT_TO_POINTER(msgnum));
3233 g_strfreev(strlist);
3235 ptr_array_free_strings(argbuf);
3236 g_ptr_array_free(argbuf, TRUE);
3238 return IMAP_SUCCESS;
3241 typedef struct _fetch_data {
3242 IMAPSession *session;
3244 const gchar *filename;
3250 static void *imap_cmd_fetch_thread(void *data)
3252 fetch_data *stuff = (fetch_data *)data;
3253 IMAPSession *session = stuff->session;
3254 guint32 uid = stuff->uid;
3255 const gchar *filename = stuff->filename;
3263 if (filename == NULL) {
3265 return GINT_TO_POINTER(IMAP_ERROR);
3269 imap_gen_send(session, "UID FETCH %d BODY.PEEK[]", uid);
3271 imap_gen_send(session, "UID FETCH %d BODY.PEEK[HEADER]",
3273 while ((ok = imap_gen_recv_block(session, &buf)) == IMAP_SUCCESS) {
3274 if (buf[0] != '*' || buf[1] != ' ') {
3277 return GINT_TO_POINTER(IMAP_ERROR);
3279 if (strstr(buf, "FETCH") != NULL) break;
3282 if (ok != IMAP_SUCCESS) {
3285 return GINT_TO_POINTER(ok);
3288 #define RETURN_ERROR_IF_FAIL(cond) \
3291 stuff->done = TRUE; \
3292 return GINT_TO_POINTER(IMAP_ERROR); \
3295 cur_pos = strchr(buf, '{');
3296 RETURN_ERROR_IF_FAIL(cur_pos != NULL);
3297 cur_pos = strchr_cpy(cur_pos + 1, '}', size_str, sizeof(size_str));
3298 RETURN_ERROR_IF_FAIL(cur_pos != NULL);
3299 size_num = atol(size_str);
3300 RETURN_ERROR_IF_FAIL(size_num >= 0);
3302 RETURN_ERROR_IF_FAIL(*cur_pos == '\0');
3304 #undef RETURN_ERROR_IF_FAIL
3308 if (recv_bytes_write_to_file(SESSION(session)->sock,
3309 size_num, filename) != 0) {
3311 return GINT_TO_POINTER(IMAP_ERROR);
3313 if (imap_gen_recv_block(session, &buf) != IMAP_SUCCESS) {
3316 return GINT_TO_POINTER(IMAP_ERROR);
3319 if (buf[0] == '\0' || buf[strlen(buf) - 1] != ')') {
3322 return GINT_TO_POINTER(IMAP_ERROR);
3326 ok = imap_cmd_ok_block(session, NULL);
3329 return GINT_TO_POINTER(ok);
3332 static gint imap_cmd_fetch(IMAPSession *session, guint32 uid,
3333 const gchar *filename, gboolean headers,
3336 fetch_data *data = g_new0(fetch_data, 1);
3343 data->session = session;
3345 data->filename = filename;
3346 data->headers = headers;
3349 if (prefs_common.work_offline && !imap_gtk_should_override()) {
3354 #if (defined USE_PTHREAD && defined __GLIBC__ && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 3)))
3355 if (pthread_create(&pt, PTHREAD_CREATE_JOINABLE,
3356 imap_cmd_fetch_thread, data) != 0) {
3357 result = GPOINTER_TO_INT(imap_cmd_fetch_thread(data));
3361 debug_print("+++waiting for imap_cmd_fetch_thread...\n");
3362 while(!data->done) {
3363 /* don't let the interface freeze while waiting */
3366 debug_print("---imap_cmd_fetch_thread done\n");
3368 /* get the thread's return value and clean its resources */
3369 pthread_join(pt, &tmp);
3370 result = GPOINTER_TO_INT(tmp);
3372 result = GPOINTER_TO_INT(imap_cmd_fetch_thread(data));
3378 static gint imap_cmd_append(IMAPSession *session, const gchar *destfolder,
3379 const gchar *file, IMAPFlags flags,
3386 unsigned int new_uid_;
3388 gchar buf[BUFFSIZE];
3393 g_return_val_if_fail(file != NULL, IMAP_ERROR);
3395 size = get_file_size_as_crlf(file);
3396 if ((fp = fopen(file, "rb")) == NULL) {
3397 FILE_OP_ERROR(file, "fopen");
3400 QUOTE_IF_REQUIRED(destfolder_, destfolder);
3401 flag_str = imap_get_flag_str(flags);
3402 imap_gen_send(session, "APPEND %s (%s) {%d}",
3403 destfolder_, flag_str, size);
3406 ok = imap_gen_recv(session, &ret);
3407 if (ok != IMAP_SUCCESS || ret[0] != '+' || ret[1] != ' ') {
3408 log_warning(_("can't append %s to %s\n"), file, destfolder_);
3415 log_print("IMAP4> %s\n", "(sending file...)");
3417 while (fgets(buf, sizeof(buf), fp) != NULL) {
3419 if (sock_puts(SESSION(session)->sock, buf) < 0) {
3426 FILE_OP_ERROR(file, "fgets");
3431 sock_puts(SESSION(session)->sock, "");
3435 if (new_uid != NULL)
3438 if (new_uid != NULL && session->uidplus) {
3439 argbuf = g_ptr_array_new();
3441 ok = imap_cmd_ok(session, argbuf);
3442 if ((ok == IMAP_SUCCESS) && (argbuf->len > 0)) {
3443 resp_str = g_ptr_array_index(argbuf, argbuf->len - 1);
3445 sscanf(resp_str, "%*u OK [APPENDUID %*u %u]",
3447 *new_uid = new_uid_;
3451 ptr_array_free_strings(argbuf);
3452 g_ptr_array_free(argbuf, TRUE);
3454 ok = imap_cmd_ok(session, NULL);
3456 if (ok != IMAP_SUCCESS)
3457 log_warning(_("can't append message to %s\n"),
3463 static MsgNumberList *imapset_to_numlist(IMAPSet imapset)
3465 gchar **ranges, **range;
3466 unsigned int low, high;
3467 MsgNumberList *uids = NULL;
3469 ranges = g_strsplit(imapset, ",", 0);
3470 for (range = ranges; *range != NULL; range++) {
3471 if (sscanf(*range, "%u:%u", &low, &high) == 1)
3472 uids = g_slist_prepend(uids, GINT_TO_POINTER(low));
3475 for (i = low; i <= high; i++)
3476 uids = g_slist_prepend(uids, GINT_TO_POINTER(i));
3479 uids = g_slist_reverse(uids);
3485 static gint imap_cmd_copy(IMAPSession *session, const gchar *seq_set,
3486 const gchar *destfolder, GRelation *uid_mapping)
3491 g_return_val_if_fail(session != NULL, IMAP_ERROR);
3492 g_return_val_if_fail(seq_set != NULL, IMAP_ERROR);
3493 g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
3495 QUOTE_IF_REQUIRED(destfolder_, destfolder);
3496 imap_gen_send(session, "UID COPY %s %s", seq_set, destfolder_);
3498 if (uid_mapping != NULL && session->uidplus) {
3500 gchar *resp_str = NULL, *olduids_str, *newuids_str;
3501 MsgNumberList *olduids, *old_cur, *newuids, *new_cur;
3503 reply = g_ptr_array_new();
3504 ok = imap_cmd_ok(session, reply);
3505 if ((ok == IMAP_SUCCESS) && (reply->len > 0)) {
3506 resp_str = g_ptr_array_index(reply, reply->len - 1);
3508 olduids_str = g_new0(gchar, strlen(resp_str));
3509 newuids_str = g_new0(gchar, strlen(resp_str));
3510 if (sscanf(resp_str, "%*s OK [COPYUID %*u %[0-9,:] %[0-9,:]]",
3511 olduids_str, newuids_str) == 2) {
3512 olduids = imapset_to_numlist(olduids_str);
3513 newuids = imapset_to_numlist(newuids_str);
3517 while(old_cur != NULL && new_cur != NULL) {
3518 g_relation_insert(uid_mapping,
3519 GPOINTER_TO_INT(old_cur->data),
3520 GPOINTER_TO_INT(new_cur->data));
3521 old_cur = g_slist_next(old_cur);
3522 new_cur = g_slist_next(new_cur);
3525 g_slist_free(olduids);
3526 g_slist_free(newuids);
3528 g_free(olduids_str);
3529 g_free(newuids_str);
3532 ptr_array_free_strings(reply);
3533 g_ptr_array_free(reply, TRUE);
3535 ok = imap_cmd_ok(session, NULL);
3537 if (ok != IMAP_SUCCESS)
3538 log_warning(_("can't copy %s to %s\n"), seq_set, destfolder_);
3543 gint imap_cmd_envelope(IMAPSession *session, IMAPSet set)
3545 static gchar *header_fields =
3546 "Date From To Cc Subject Message-ID References In-Reply-To" ;
3549 (session, "UID FETCH %s (UID FLAGS RFC822.SIZE BODY.PEEK[HEADER.FIELDS (%s)])",
3550 set, header_fields);
3552 return IMAP_SUCCESS;
3555 static gint imap_cmd_store(IMAPSession *session, IMAPSet seq_set,
3560 imap_gen_send(session, "UID STORE %s %s", seq_set, sub_cmd);
3562 if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS) {
3563 log_warning(_("error while imap command: STORE %s %s\n"),
3568 return IMAP_SUCCESS;
3571 typedef struct _expunge_data {
3572 IMAPSession *session;
3577 static void *imap_cmd_expunge_thread(void *data)
3579 expunge_data *stuff = (expunge_data *)data;
3580 IMAPSession *session = stuff->session;
3581 IMAPSet seq_set = stuff->seq_set;
3585 if (seq_set && session->uidplus)
3586 imap_gen_send(session, "UID EXPUNGE %s", seq_set);
3588 imap_gen_send(session, "EXPUNGE");
3589 if ((ok = imap_cmd_ok_with_block(session, NULL, TRUE)) != IMAP_SUCCESS) {
3590 log_warning(_("error while imap command: EXPUNGE\n"));
3592 return GINT_TO_POINTER(ok);
3596 return GINT_TO_POINTER(IMAP_SUCCESS);
3599 static gint imap_cmd_expunge(IMAPSession *session, IMAPSet seq_set)
3601 expunge_data *data = g_new0(expunge_data, 1);
3608 data->session = session;
3609 data->seq_set = seq_set;
3611 if (prefs_common.work_offline && !imap_gtk_should_override()) {
3616 #if (defined USE_PTHREAD && defined __GLIBC__ && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 3)))
3617 if (pthread_create(&pt, PTHREAD_CREATE_JOINABLE,
3618 imap_cmd_expunge_thread, data) != 0) {
3619 result = GPOINTER_TO_INT(imap_cmd_expunge_thread(data));
3623 debug_print("+++waiting for imap_cmd_expunge_thread...\n");
3624 while(!data->done) {
3625 /* don't let the interface freeze while waiting */
3628 debug_print("---imap_cmd_expunge_thread done\n");
3630 /* get the thread's return value and clean its resources */
3631 pthread_join(pt, &tmp);
3632 result = GPOINTER_TO_INT(tmp);
3634 result = GPOINTER_TO_INT(imap_cmd_expunge_thread(data));
3640 static gint imap_cmd_close(IMAPSession *session)
3644 imap_gen_send(session, "CLOSE");
3645 if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS)
3646 log_warning(_("error while imap command: CLOSE\n"));
3651 static gint imap_cmd_ok_with_block(IMAPSession *session, GPtrArray *argbuf, gboolean block)
3653 gint ok = IMAP_SUCCESS;
3658 while ((ok = imap_gen_recv_with_block(session, &buf, block))
3660 /* make sure data is long enough for any substring of buf */
3661 data = alloca(strlen(buf) + 1);
3663 /* untagged line read */
3664 if (buf[0] == '*' && buf[1] == ' ') {
3667 g_ptr_array_add(argbuf, g_strdup(buf + 2));
3669 if (sscanf(buf + 2, "%d %s", &num, data) >= 2) {
3670 if (!strcmp(data, "EXISTS")) {
3671 session->exists = num;
3672 session->folder_content_changed = TRUE;
3675 if(!strcmp(data, "EXPUNGE")) {
3677 session->folder_content_changed = TRUE;
3680 /* tagged line with correct tag and OK response found */
3681 } else if ((sscanf(buf, "%d %s", &cmd_num, data) >= 2) &&
3682 (cmd_num == session->cmd_count) &&
3683 !strcmp(data, "OK")) {
3685 g_ptr_array_add(argbuf, g_strdup(buf));
3687 /* everything else */
3698 static gint imap_cmd_ok(IMAPSession *session, GPtrArray *argbuf)
3700 return imap_cmd_ok_with_block(session, argbuf, FALSE);
3702 static gint imap_cmd_ok_block(IMAPSession *session, GPtrArray *argbuf)
3704 return imap_cmd_ok_with_block(session, argbuf, TRUE);
3706 static void imap_gen_send(IMAPSession *session, const gchar *format, ...)
3713 va_start(args, format);
3714 tmp = g_strdup_vprintf(format, args);
3717 session->cmd_count++;
3719 buf = g_strdup_printf("%d %s\r\n", session->cmd_count, tmp);
3720 if (!g_ascii_strncasecmp(tmp, "LOGIN ", 6) && (p = strchr(tmp + 6, ' '))) {
3722 log_print("IMAP4> %d %s ********\n", session->cmd_count, tmp);
3724 log_print("IMAP4> %d %s\n", session->cmd_count, tmp);
3726 sock_write_all(SESSION(session)->sock, buf, strlen(buf));
3731 static gint imap_gen_recv_with_block(IMAPSession *session, gchar **ret, gboolean block)
3734 if ((*ret = imap_getline(SESSION(session)->sock)) == NULL)
3737 if ((*ret = sock_getline(SESSION(session)->sock)) == NULL)
3742 log_print("IMAP4< %s\n", *ret);
3744 session_set_access_time(SESSION(session));
3746 return IMAP_SUCCESS;
3749 static gint imap_gen_recv_block(IMAPSession *session, gchar **ret)
3751 return imap_gen_recv_with_block(session, ret, TRUE);
3754 static gint imap_gen_recv(IMAPSession *session, gchar **ret)
3756 return imap_gen_recv_with_block(session, ret, FALSE);
3758 /* misc utility functions */
3760 static gchar *strchr_cpy(const gchar *src, gchar ch, gchar *dest, gint len)
3765 tmp = strchr(src, ch);
3769 memcpy(dest, src, MIN(tmp - src, len - 1));
3770 dest[MIN(tmp - src, len - 1)] = '\0';
3775 static gchar *get_quoted(const gchar *src, gchar ch, gchar *dest, gint len)
3777 const gchar *p = src;
3780 g_return_val_if_fail(*p == ch, NULL);
3785 while (*p != '\0' && *p != ch) {
3787 if (*p == '\\' && *(p + 1) != '\0')
3796 return (gchar *)(*p == ch ? p + 1 : p);
3799 static gchar *search_array_contain_str(GPtrArray *array, const gchar *str)
3803 for (i = 0; i < array->len; i++) {
3806 tmp = g_ptr_array_index(array, i);
3807 if (strstr(tmp, str) != NULL)
3814 static gchar *search_array_str(GPtrArray *array, const gchar *str)
3821 for (i = 0; i < array->len; i++) {
3824 tmp = g_ptr_array_index(array, i);
3825 if (!strncmp(tmp, str, len))
3832 static void imap_path_separator_subst(gchar *str, gchar separator)
3835 gboolean in_escape = FALSE;
3837 if (!separator || separator == '/') return;
3839 for (p = str; *p != '\0'; p++) {
3840 if (*p == '/' && !in_escape)
3842 else if (*p == '&' && *(p + 1) != '-' && !in_escape)
3844 else if (*p == '-' && in_escape)
3849 static gchar *imap_modified_utf7_to_utf8(const gchar *mutf7_str)
3851 static iconv_t cd = (iconv_t)-1;
3852 static gboolean iconv_ok = TRUE;
3855 size_t norm_utf7_len;
3857 gchar *to_str, *to_p;
3859 gboolean in_escape = FALSE;
3861 if (!iconv_ok) return g_strdup(mutf7_str);
3863 if (cd == (iconv_t)-1) {
3864 cd = iconv_open(CS_INTERNAL, CS_UTF_7);
3865 if (cd == (iconv_t)-1) {
3866 g_warning("iconv cannot convert UTF-7 to %s\n",
3869 return g_strdup(mutf7_str);
3873 /* modified UTF-7 to normal UTF-7 conversion */
3874 norm_utf7 = g_string_new(NULL);
3876 for (p = mutf7_str; *p != '\0'; p++) {
3877 /* replace: '&' -> '+',
3879 escaped ',' -> '/' */
3880 if (!in_escape && *p == '&') {
3881 if (*(p + 1) != '-') {
3882 g_string_append_c(norm_utf7, '+');
3885 g_string_append_c(norm_utf7, '&');
3888 } else if (in_escape && *p == ',') {
3889 g_string_append_c(norm_utf7, '/');
3890 } else if (in_escape && *p == '-') {
3891 g_string_append_c(norm_utf7, '-');
3894 g_string_append_c(norm_utf7, *p);
3898 norm_utf7_p = norm_utf7->str;
3899 norm_utf7_len = norm_utf7->len;
3900 to_len = strlen(mutf7_str) * 5;
3901 to_p = to_str = g_malloc(to_len + 1);
3903 if (iconv(cd, (ICONV_CONST gchar **)&norm_utf7_p, &norm_utf7_len,
3904 &to_p, &to_len) == -1) {
3905 g_warning(_("iconv cannot convert UTF-7 to %s\n"),
3906 conv_get_locale_charset_str());
3907 g_string_free(norm_utf7, TRUE);
3909 return g_strdup(mutf7_str);
3912 /* second iconv() call for flushing */
3913 iconv(cd, NULL, NULL, &to_p, &to_len);
3914 g_string_free(norm_utf7, TRUE);
3920 static gchar *imap_utf8_to_modified_utf7(const gchar *from)
3922 static iconv_t cd = (iconv_t)-1;
3923 static gboolean iconv_ok = TRUE;
3924 gchar *norm_utf7, *norm_utf7_p;
3925 size_t from_len, norm_utf7_len;
3927 gchar *from_tmp, *to, *p;
3928 gboolean in_escape = FALSE;
3930 if (!iconv_ok) return g_strdup(from);
3932 if (cd == (iconv_t)-1) {
3933 cd = iconv_open(CS_UTF_7, CS_INTERNAL);
3934 if (cd == (iconv_t)-1) {
3935 g_warning(_("iconv cannot convert %s to UTF-7\n"),
3938 return g_strdup(from);
3942 /* UTF-8 to normal UTF-7 conversion */
3943 Xstrdup_a(from_tmp, from, return g_strdup(from));
3944 from_len = strlen(from);
3945 norm_utf7_len = from_len * 5;
3946 Xalloca(norm_utf7, norm_utf7_len + 1, return g_strdup(from));
3947 norm_utf7_p = norm_utf7;
3949 #define IS_PRINT(ch) (isprint(ch) && IS_ASCII(ch))
3951 while (from_len > 0) {
3952 if (*from_tmp == '+') {
3953 *norm_utf7_p++ = '+';
3954 *norm_utf7_p++ = '-';
3958 } else if (IS_PRINT(*(guchar *)from_tmp)) {
3959 /* printable ascii char */
3960 *norm_utf7_p = *from_tmp;
3966 size_t conv_len = 0;
3968 /* unprintable char: convert to UTF-7 */
3970 while (!IS_PRINT(*(guchar *)p) && conv_len < from_len) {
3971 conv_len += g_utf8_skip[*(guchar *)p];
3972 p += g_utf8_skip[*(guchar *)p];
3975 from_len -= conv_len;
3976 if (iconv(cd, (ICONV_CONST gchar **)&from_tmp,
3978 &norm_utf7_p, &norm_utf7_len) == -1) {
3979 g_warning(_("iconv cannot convert UTF-8 to UTF-7\n"));
3980 return g_strdup(from);
3983 /* second iconv() call for flushing */
3984 iconv(cd, NULL, NULL, &norm_utf7_p, &norm_utf7_len);
3990 *norm_utf7_p = '\0';
3991 to_str = g_string_new(NULL);
3992 for (p = norm_utf7; p < norm_utf7_p; p++) {
3993 /* replace: '&' -> "&-",
3996 BASE64 '/' -> ',' */
3997 if (!in_escape && *p == '&') {
3998 g_string_append(to_str, "&-");
3999 } else if (!in_escape && *p == '+') {
4000 if (*(p + 1) == '-') {
4001 g_string_append_c(to_str, '+');
4004 g_string_append_c(to_str, '&');
4007 } else if (in_escape && *p == '/') {
4008 g_string_append_c(to_str, ',');
4009 } else if (in_escape && *p == '-') {
4010 g_string_append_c(to_str, '-');
4013 g_string_append_c(to_str, *p);
4019 g_string_append_c(to_str, '-');
4023 g_string_free(to_str, FALSE);
4028 static GSList *imap_get_seq_set_from_numlist(MsgNumberList *numlist)
4031 GSList *sorted_list, *cur;
4032 guint first, last, next;
4034 GSList *ret_list = NULL;
4036 if (numlist == NULL)
4039 str = g_string_sized_new(256);
4041 sorted_list = g_slist_copy(numlist);
4042 sorted_list = g_slist_sort(sorted_list, g_int_compare);
4044 first = GPOINTER_TO_INT(sorted_list->data);
4046 for (cur = sorted_list; cur != NULL; cur = g_slist_next(cur)) {
4047 last = GPOINTER_TO_INT(cur->data);
4049 next = GPOINTER_TO_INT(cur->next->data);
4053 if (last + 1 != next || next == 0) {
4055 g_string_append_c(str, ',');
4057 g_string_append_printf(str, "%u", first);
4059 g_string_append_printf(str, "%u:%u", first, last);
4063 if (str->len > IMAP_CMD_LIMIT) {
4064 ret_str = g_strdup(str->str);
4065 ret_list = g_slist_append(ret_list, ret_str);
4066 g_string_truncate(str, 0);
4072 ret_str = g_strdup(str->str);
4073 ret_list = g_slist_append(ret_list, ret_str);
4076 g_slist_free(sorted_list);
4077 g_string_free(str, TRUE);
4082 static GSList *imap_get_seq_set_from_msglist(MsgInfoList *msglist)
4084 MsgNumberList *numlist = NULL;
4088 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
4089 MsgInfo *msginfo = (MsgInfo *) cur->data;
4091 numlist = g_slist_append(numlist, GINT_TO_POINTER(msginfo->msgnum));
4093 seq_list = imap_get_seq_set_from_numlist(numlist);
4094 g_slist_free(numlist);
4099 static void imap_seq_set_free(GSList *seq_list)
4101 slist_free_strings(seq_list);
4102 g_slist_free(seq_list);
4106 static gboolean imap_rename_folder_func(GNode *node, gpointer data)
4108 FolderItem *item = node->data;
4109 gchar **paths = data;
4110 const gchar *oldpath = paths[0];
4111 const gchar *newpath = paths[1];
4113 gchar *new_itempath;
4116 oldpathlen = strlen(oldpath);
4117 if (strncmp(oldpath, item->path, oldpathlen) != 0) {
4118 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
4122 base = item->path + oldpathlen;
4123 while (*base == G_DIR_SEPARATOR) base++;
4125 new_itempath = g_strdup(newpath);
4127 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
4130 item->path = new_itempath;
4135 typedef struct _get_list_uid_data {
4137 IMAPFolderItem *item;
4138 GSList **msgnum_list;
4140 } get_list_uid_data;
4142 static void *get_list_of_uids_thread(void *data)
4144 get_list_uid_data *stuff = (get_list_uid_data *)data;
4145 Folder *folder = stuff->folder;
4146 IMAPFolderItem *item = stuff->item;
4147 GSList **msgnum_list = stuff->msgnum_list;
4148 gint ok, nummsgs = 0, lastuid_old;
4149 IMAPSession *session;
4150 GSList *uidlist, *elem;
4153 session = imap_session_get(folder);
4154 if (session == NULL) {
4156 return GINT_TO_POINTER(-1);
4159 ok = imap_select(session, IMAP_FOLDER(folder), item->item.path,
4160 NULL, NULL, NULL, NULL, TRUE);
4161 if (ok != IMAP_SUCCESS) {
4163 return GINT_TO_POINTER(-1);
4166 cmd_buf = g_strdup_printf("UID %d:*", item->lastuid + 1);
4167 ok = imap_cmd_search(session, cmd_buf, &uidlist, TRUE);
4170 if (ok == IMAP_SOCKET) {
4171 session_destroy((Session *)session);
4172 ((RemoteFolder *)folder)->session = NULL;
4174 return GINT_TO_POINTER(-1);
4177 if (ok != IMAP_SUCCESS) {
4181 argbuf = g_ptr_array_new();
4183 cmd_buf = g_strdup_printf("UID FETCH %d:* (UID)", item->lastuid + 1);
4184 imap_gen_send(session, cmd_buf);
4186 ok = imap_cmd_ok_block(session, argbuf);
4187 if (ok != IMAP_SUCCESS) {
4188 ptr_array_free_strings(argbuf);
4189 g_ptr_array_free(argbuf, TRUE);
4191 return GINT_TO_POINTER(-1);
4194 for(i = 0; i < argbuf->len; i++) {
4197 if((ret = sscanf(g_ptr_array_index(argbuf, i),
4198 "%*d FETCH (UID %d)", &msgnum)) == 1)
4199 uidlist = g_slist_prepend(uidlist, GINT_TO_POINTER(msgnum));
4201 ptr_array_free_strings(argbuf);
4202 g_ptr_array_free(argbuf, TRUE);
4205 lastuid_old = item->lastuid;
4206 *msgnum_list = g_slist_copy(item->uid_list);
4207 nummsgs = g_slist_length(*msgnum_list);
4208 debug_print("Got %d uids from cache\n", g_slist_length(item->uid_list));
4210 for (elem = uidlist; elem != NULL; elem = g_slist_next(elem)) {
4213 msgnum = GPOINTER_TO_INT(elem->data);
4214 if (msgnum > lastuid_old) {
4215 *msgnum_list = g_slist_prepend(*msgnum_list, GINT_TO_POINTER(msgnum));
4216 item->uid_list = g_slist_prepend(item->uid_list, GINT_TO_POINTER(msgnum));
4219 if(msgnum > item->lastuid)
4220 item->lastuid = msgnum;
4223 g_slist_free(uidlist);
4226 return GINT_TO_POINTER(nummsgs);
4229 static gint get_list_of_uids(Folder *folder, IMAPFolderItem *item, GSList **msgnum_list)
4232 get_list_uid_data *data = g_new0(get_list_uid_data, 1);
4238 data->folder = folder;
4240 data->msgnum_list = msgnum_list;
4242 if (prefs_common.work_offline && !imap_gtk_should_override()) {
4247 #if (defined USE_PTHREAD && defined __GLIBC__ && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 3)))
4248 if (pthread_create(&pt, PTHREAD_CREATE_JOINABLE,
4249 get_list_of_uids_thread, data) != 0) {
4250 result = GPOINTER_TO_INT(get_list_of_uids_thread(data));
4254 debug_print("+++waiting for get_list_of_uids_thread...\n");
4255 while(!data->done) {
4256 /* don't let the interface freeze while waiting */
4259 debug_print("---get_list_of_uids_thread done\n");
4261 /* get the thread's return value and clean its resources */
4262 pthread_join(pt, &tmp);
4263 result = GPOINTER_TO_INT(tmp);
4265 result = GPOINTER_TO_INT(get_list_of_uids_thread(data));
4272 gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list, gboolean *old_uids_valid)
4274 IMAPFolderItem *item = (IMAPFolderItem *)_item;
4275 IMAPSession *session;
4276 gint ok, nummsgs = 0, exists, recent, uid_val, uid_next, unseen;
4277 GSList *uidlist = NULL;
4279 gboolean selected_folder;
4281 g_return_val_if_fail(folder != NULL, -1);
4282 g_return_val_if_fail(item != NULL, -1);
4283 g_return_val_if_fail(item->item.path != NULL, -1);
4284 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
4285 g_return_val_if_fail(folder->account != NULL, -1);
4287 session = imap_session_get(folder);
4288 g_return_val_if_fail(session != NULL, -1);
4290 selected_folder = (session->mbox != NULL) &&
4291 (!strcmp(session->mbox, item->item.path));
4292 if (selected_folder) {
4293 ok = imap_cmd_noop(session);
4294 if (ok != IMAP_SUCCESS)
4296 exists = session->exists;
4298 *old_uids_valid = TRUE;
4300 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
4301 &exists, &recent, &uid_next, &uid_val, &unseen, FALSE);
4302 if (ok != IMAP_SUCCESS)
4305 if(item->item.mtime == uid_val)
4306 *old_uids_valid = TRUE;
4308 *old_uids_valid = FALSE;
4310 debug_print("Freeing imap uid cache\n");
4312 g_slist_free(item->uid_list);
4313 item->uid_list = NULL;
4315 item->item.mtime = uid_val;
4317 imap_delete_all_cached_messages((FolderItem *)item);
4321 if (!selected_folder)
4322 item->uid_next = uid_next;
4324 /* If old uid_next matches new uid_next we can be sure no message
4325 was added to the folder */
4326 if (( selected_folder && !session->folder_content_changed) ||
4327 (!selected_folder && uid_next == item->uid_next)) {
4328 nummsgs = g_slist_length(item->uid_list);
4330 /* If number of messages is still the same we
4331 know our caches message numbers are still valid,
4332 otherwise if the number of messages has decrease
4333 we discard our cache to start a new scan to find
4334 out which numbers have been removed */
4335 if (exists == nummsgs) {
4336 *msgnum_list = g_slist_copy(item->uid_list);
4338 } else if (exists < nummsgs) {
4339 debug_print("Freeing imap uid cache");
4341 g_slist_free(item->uid_list);
4342 item->uid_list = NULL;
4347 *msgnum_list = NULL;
4351 nummsgs = get_list_of_uids(folder, item, &uidlist);
4353 if (nummsgs != exists) {
4354 /* Cache contains more messages then folder, we have cached
4355 an old UID of a message that was removed and new messages
4356 have been added too, otherwise the uid_next check would
4358 debug_print("Freeing imap uid cache");
4360 g_slist_free(item->uid_list);
4361 item->uid_list = NULL;
4363 g_slist_free(*msgnum_list);
4365 nummsgs = get_list_of_uids(folder, item, &uidlist);
4368 *msgnum_list = uidlist;
4370 dir = folder_item_get_path((FolderItem *)item);
4371 debug_print("removing old messages from %s\n", dir);
4372 remove_numbered_files_not_in_list(dir, *msgnum_list);
4378 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item)
4383 flags.perm_flags = MSG_NEW|MSG_UNREAD;
4384 flags.tmp_flags = 0;
4386 g_return_val_if_fail(item != NULL, NULL);
4387 g_return_val_if_fail(file != NULL, NULL);
4389 if (item->stype == F_QUEUE) {
4390 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
4391 } else if (item->stype == F_DRAFT) {
4392 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
4395 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
4396 if (!msginfo) return NULL;
4398 msginfo->plaintext_file = g_strdup(file);
4399 msginfo->folder = item;
4404 GSList *imap_get_msginfos(Folder *folder, FolderItem *item, GSList *msgnum_list)
4406 IMAPSession *session;
4407 MsgInfoList *ret = NULL;
4410 g_return_val_if_fail(folder != NULL, NULL);
4411 g_return_val_if_fail(item != NULL, NULL);
4412 g_return_val_if_fail(msgnum_list != NULL, NULL);
4414 session = imap_session_get(folder);
4415 g_return_val_if_fail(session != NULL, NULL);
4417 debug_print("IMAP getting msginfos\n");
4418 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
4419 NULL, NULL, NULL, NULL, FALSE);
4420 if (ok != IMAP_SUCCESS)
4423 if (!(item->stype == F_QUEUE || item->stype == F_DRAFT)) {
4424 ret = g_slist_concat(ret,
4425 imap_get_uncached_messages(
4426 session, item, msgnum_list));
4428 MsgNumberList *sorted_list, *elem;
4429 gint startnum, lastnum;
4431 sorted_list = g_slist_sort(g_slist_copy(msgnum_list), g_int_compare);
4433 startnum = lastnum = GPOINTER_TO_INT(sorted_list->data);
4435 for (elem = sorted_list;; elem = g_slist_next(elem)) {
4439 num = GPOINTER_TO_INT(elem->data);
4441 if (num > lastnum + 1 || elem == NULL) {
4443 for (i = startnum; i <= lastnum; ++i) {
4446 file = imap_fetch_msg(folder, item, i);
4448 MsgInfo *msginfo = imap_parse_msg(file, item);
4449 if (msginfo != NULL) {
4450 msginfo->msgnum = i;
4451 ret = g_slist_append(ret, msginfo);
4465 g_slist_free(sorted_list);
4471 MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid)
4473 MsgInfo *msginfo = NULL;
4474 MsgInfoList *msginfolist;
4475 MsgNumberList numlist;
4477 numlist.next = NULL;
4478 numlist.data = GINT_TO_POINTER(uid);
4480 msginfolist = imap_get_msginfos(folder, item, &numlist);
4481 if (msginfolist != NULL) {
4482 msginfo = msginfolist->data;
4483 g_slist_free(msginfolist);
4489 gboolean imap_scan_required(Folder *folder, FolderItem *_item)
4491 IMAPSession *session;
4492 IMAPFolderItem *item = (IMAPFolderItem *)_item;
4493 gint ok, exists = 0, recent = 0, unseen = 0;
4494 guint32 uid_next, uid_val = 0;
4495 gboolean selected_folder;
4497 g_return_val_if_fail(folder != NULL, FALSE);
4498 g_return_val_if_fail(item != NULL, FALSE);
4499 g_return_val_if_fail(item->item.folder != NULL, FALSE);
4500 g_return_val_if_fail(FOLDER_CLASS(item->item.folder) == &imap_class, FALSE);
4502 if (item->item.path == NULL)
4505 session = imap_session_get(folder);
4506 g_return_val_if_fail(session != NULL, FALSE);
4508 selected_folder = (session->mbox != NULL) &&
4509 (!strcmp(session->mbox, item->item.path));
4510 if (selected_folder) {
4511 ok = imap_cmd_noop(session);
4512 if (ok != IMAP_SUCCESS)
4515 if (session->folder_content_changed)
4518 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
4519 &exists, &recent, &uid_next, &uid_val, &unseen, FALSE);
4520 if (ok != IMAP_SUCCESS)
4523 if ((uid_next != item->uid_next) || (exists != item->item.total_msgs))
4530 void imap_change_flags(Folder *folder, FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
4532 IMAPSession *session;
4533 IMAPFlags flags_set = 0, flags_unset = 0;
4534 gint ok = IMAP_SUCCESS;
4535 MsgNumberList numlist;
4536 hashtable_data *ht_data = NULL;
4538 g_return_if_fail(folder != NULL);
4539 g_return_if_fail(folder->klass == &imap_class);
4540 g_return_if_fail(item != NULL);
4541 g_return_if_fail(item->folder == folder);
4542 g_return_if_fail(msginfo != NULL);
4543 g_return_if_fail(msginfo->folder == item);
4545 session = imap_session_get(folder);
4546 if (!session) return;
4548 if ((ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
4549 NULL, NULL, NULL, NULL, FALSE)) != IMAP_SUCCESS)
4552 if (!MSG_IS_MARKED(msginfo->flags) && (newflags & MSG_MARKED))
4553 flags_set |= IMAP_FLAG_FLAGGED;
4554 if ( MSG_IS_MARKED(msginfo->flags) && !(newflags & MSG_MARKED))
4555 flags_unset |= IMAP_FLAG_FLAGGED;
4557 if (!MSG_IS_UNREAD(msginfo->flags) && (newflags & MSG_UNREAD))
4558 flags_unset |= IMAP_FLAG_SEEN;
4559 if ( MSG_IS_UNREAD(msginfo->flags) && !(newflags & MSG_UNREAD))
4560 flags_set |= IMAP_FLAG_SEEN;
4562 if (!MSG_IS_REPLIED(msginfo->flags) && (newflags & MSG_REPLIED))
4563 flags_set |= IMAP_FLAG_ANSWERED;
4564 if ( MSG_IS_REPLIED(msginfo->flags) && !(newflags & MSG_REPLIED))
4565 flags_set |= IMAP_FLAG_ANSWERED;
4567 numlist.next = NULL;
4568 numlist.data = GINT_TO_POINTER(msginfo->msgnum);
4570 if (IMAP_FOLDER_ITEM(item)->batching) {
4571 /* instead of performing an UID STORE command for each message change,
4572 * as a lot of them can change "together", we just fill in hashtables
4573 * and defer the treatment so that we're able to send only one
4576 debug_print("IMAP batch mode on, deferring flags change\n");
4578 ht_data = g_hash_table_lookup(flags_set_table, GINT_TO_POINTER(flags_set));
4579 if (ht_data == NULL) {
4580 ht_data = g_new0(hashtable_data, 1);
4581 ht_data->session = session;
4582 g_hash_table_insert(flags_set_table, GINT_TO_POINTER(flags_set), ht_data);
4584 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
4585 ht_data->msglist = g_slist_prepend(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum));
4588 ht_data = g_hash_table_lookup(flags_unset_table, GINT_TO_POINTER(flags_unset));
4589 if (ht_data == NULL) {
4590 ht_data = g_new0(hashtable_data, 1);
4591 ht_data->session = session;
4592 g_hash_table_insert(flags_unset_table, GINT_TO_POINTER(flags_unset), ht_data);
4594 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
4595 ht_data->msglist = g_slist_prepend(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum));
4598 debug_print("IMAP changing flags\n");
4600 ok = imap_set_message_flags(session, &numlist, flags_set, TRUE);
4601 if (ok != IMAP_SUCCESS) return;
4605 ok = imap_set_message_flags(session, &numlist, flags_unset, FALSE);
4606 if (ok != IMAP_SUCCESS) return;
4609 msginfo->flags.perm_flags = newflags;
4614 static gint imap_remove_msg(Folder *folder, FolderItem *item, gint uid)
4617 IMAPSession *session;
4619 MsgNumberList numlist;
4621 g_return_val_if_fail(folder != NULL, -1);
4622 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
4623 g_return_val_if_fail(item != NULL, -1);
4625 session = imap_session_get(folder);
4626 if (!session) return -1;
4628 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
4629 NULL, NULL, NULL, NULL, FALSE);
4630 if (ok != IMAP_SUCCESS)
4633 numlist.next = NULL;
4634 numlist.data = GINT_TO_POINTER(uid);
4636 ok = imap_set_message_flags
4637 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
4638 &numlist, IMAP_FLAG_DELETED, TRUE);
4639 if (ok != IMAP_SUCCESS) {
4640 log_warning(_("can't set deleted flags: %d\n"), uid);
4644 if (!session->uidplus) {
4645 ok = imap_cmd_expunge(session, NULL);
4649 uidstr = g_strdup_printf("%u", uid);
4650 ok = imap_cmd_expunge(session, uidstr);
4653 if (ok != IMAP_SUCCESS) {
4654 log_warning(_("can't expunge\n"));
4658 IMAP_FOLDER_ITEM(item)->uid_list = g_slist_remove(
4659 IMAP_FOLDER_ITEM(item)->uid_list, numlist.data);
4660 dir = folder_item_get_path(item);
4661 if (is_dir_exist(dir))
4662 remove_numbered_files(dir, uid, uid);
4665 return IMAP_SUCCESS;
4668 static gint compare_msginfo(gconstpointer a, gconstpointer b)
4670 return ((MsgInfo *)a)->msgnum - ((MsgInfo *)b)->msgnum;
4673 static guint gslist_find_next_num(MsgNumberList **list, guint num)
4677 g_return_val_if_fail(list != NULL, -1);
4679 for (elem = *list; elem != NULL; elem = g_slist_next(elem))
4680 if (GPOINTER_TO_INT(elem->data) >= num)
4683 return elem != NULL ? GPOINTER_TO_INT(elem->data) : (gint)-1;
4687 * NEW and DELETED flags are not syncronized
4688 * - The NEW/RECENT flags in IMAP folders can not really be directly
4689 * modified by Sylpheed
4690 * - The DELETE/DELETED flag in IMAP and Sylpheed don't have the same
4691 * meaning, in IMAP it always removes the messages from the FolderItem
4692 * in Sylpheed it can mean to move the message to trash
4695 typedef struct _get_flags_data {
4698 MsgInfoList *msginfo_list;
4699 GRelation *msgflags;
4703 static /*gint*/ void *imap_get_flags_thread(void *data)
4705 get_flags_data *stuff = (get_flags_data *)data;
4706 Folder *folder = stuff->folder;
4707 FolderItem *item = stuff->item;
4708 MsgInfoList *msginfo_list = stuff->msginfo_list;
4709 GRelation *msgflags = stuff->msgflags;
4710 IMAPSession *session;
4711 GSList *sorted_list;
4712 GSList *unseen = NULL, *answered = NULL, *flagged = NULL;
4713 GSList *p_unseen, *p_answered, *p_flagged;
4715 GSList *seq_list, *cur;
4716 gboolean reverse_seen = FALSE;
4719 gint exists_cnt, recent_cnt, unseen_cnt, uid_next;
4720 guint32 uidvalidity;
4721 gboolean selected_folder;
4723 if (folder == NULL || item == NULL) {
4725 return GINT_TO_POINTER(-1);
4727 if (msginfo_list == NULL) {
4729 return GINT_TO_POINTER(0);
4732 session = imap_session_get(folder);
4733 if (session == NULL) {
4735 return GINT_TO_POINTER(-1);
4738 selected_folder = (session->mbox != NULL) &&
4739 (!strcmp(session->mbox, item->path));
4741 if (!selected_folder) {
4742 ok = imap_status(session, IMAP_FOLDER(folder), item->path,
4743 &exists_cnt, &recent_cnt, &uid_next, &uidvalidity, &unseen_cnt, TRUE);
4744 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
4745 NULL, NULL, NULL, NULL, TRUE);
4746 if (ok != IMAP_SUCCESS) {
4748 return GINT_TO_POINTER(-1);
4753 if (unseen_cnt > exists_cnt / 2)
4754 reverse_seen = TRUE;
4756 cmd_buf = g_string_new(NULL);
4758 sorted_list = g_slist_sort(g_slist_copy(msginfo_list), compare_msginfo);
4760 seq_list = imap_get_seq_set_from_msglist(msginfo_list);
4762 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
4763 IMAPSet imapset = cur->data;
4765 g_string_sprintf(cmd_buf, "%sSEEN UID %s", reverse_seen ? "" : "UN", imapset);
4766 imap_cmd_search(session, cmd_buf->str, &p_unseen, TRUE);
4767 unseen = g_slist_concat(unseen, p_unseen);
4769 g_string_sprintf(cmd_buf, "ANSWERED UID %s", imapset);
4770 imap_cmd_search(session, cmd_buf->str, &p_answered, TRUE);
4771 answered = g_slist_concat(answered, p_answered);
4773 g_string_sprintf(cmd_buf, "FLAGGED UID %s", imapset);
4774 imap_cmd_search(session, cmd_buf->str, &p_flagged, TRUE);
4775 flagged = g_slist_concat(flagged, p_flagged);
4779 p_answered = answered;
4780 p_flagged = flagged;
4782 for (elem = sorted_list; elem != NULL; elem = g_slist_next(elem)) {
4787 msginfo = (MsgInfo *) elem->data;
4788 flags = msginfo->flags.perm_flags;
4789 wasnew = (flags & MSG_NEW);
4790 flags &= ~((reverse_seen ? 0 : MSG_UNREAD | MSG_NEW) | MSG_REPLIED | MSG_MARKED);
4792 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
4793 if (gslist_find_next_num(&p_unseen, msginfo->msgnum) == msginfo->msgnum) {
4794 if (!reverse_seen) {
4795 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
4797 flags &= ~(MSG_UNREAD | MSG_NEW);
4800 if (gslist_find_next_num(&p_answered, msginfo->msgnum) == msginfo->msgnum)
4801 flags |= MSG_REPLIED;
4802 if (gslist_find_next_num(&p_flagged, msginfo->msgnum) == msginfo->msgnum)
4803 flags |= MSG_MARKED;
4804 g_relation_insert(msgflags, msginfo, GINT_TO_POINTER(flags));
4807 imap_seq_set_free(seq_list);
4808 g_slist_free(flagged);
4809 g_slist_free(answered);
4810 g_slist_free(unseen);
4811 g_slist_free(sorted_list);
4812 g_string_free(cmd_buf, TRUE);
4815 return GINT_TO_POINTER(0);
4818 static gint imap_get_flags(Folder *folder, FolderItem *item,
4819 MsgInfoList *msginfo_list, GRelation *msgflags)
4822 get_flags_data *data = g_new0(get_flags_data, 1);
4828 data->folder = folder;
4830 data->msginfo_list = msginfo_list;
4831 data->msgflags = msgflags;
4833 if (prefs_common.work_offline && !imap_gtk_should_override()) {
4838 #if (defined USE_PTHREAD && defined __GLIBC__ && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 3)))
4839 if (pthread_create(&pt, PTHREAD_CREATE_JOINABLE,
4840 imap_get_flags_thread, data) != 0) {
4841 result = GPOINTER_TO_INT(imap_get_flags_thread(data));
4845 debug_print("+++waiting for imap_get_flags_thread...\n");
4846 while(!data->done) {
4847 /* don't let the interface freeze while waiting */
4850 debug_print("---imap_get_flags_thread done\n");
4852 /* get the thread's return value and clean its resources */
4853 pthread_join(pt, &tmp);
4854 result = GPOINTER_TO_INT(tmp);
4856 result = GPOINTER_TO_INT(imap_get_flags_thread(data));
4863 static gboolean process_flags(gpointer key, gpointer value, gpointer user_data)
4865 gboolean flags_set = GPOINTER_TO_INT(user_data);
4866 gint flags_value = GPOINTER_TO_INT(key);
4867 hashtable_data *data = (hashtable_data *)value;
4869 data->msglist = g_slist_reverse(data->msglist);
4871 debug_print("IMAP %ssetting flags to %d for %d messages\n",
4874 g_slist_length(data->msglist));
4875 imap_set_message_flags(data->session, data->msglist, flags_value, flags_set);
4877 g_slist_free(data->msglist);
4882 static void process_hashtable(void)
4884 if (flags_set_table) {
4885 g_hash_table_foreach_remove(flags_set_table, process_flags, GINT_TO_POINTER(TRUE));
4886 g_free(flags_set_table);
4887 flags_set_table = NULL;
4889 if (flags_unset_table) {
4890 g_hash_table_foreach_remove(flags_unset_table, process_flags, GINT_TO_POINTER(FALSE));
4891 g_free(flags_unset_table);
4892 flags_unset_table = NULL;
4896 static IMAPFolderItem *batching_item = NULL;
4898 static void imap_set_batch (Folder *folder, FolderItem *_item, gboolean batch)
4900 IMAPFolderItem *item = (IMAPFolderItem *)_item;
4902 g_return_if_fail(item != NULL);
4904 if (batch && batching_item != NULL) {
4905 g_warning("already batching on %s\n", batching_item->item.path);
4909 if (item->batching == batch)
4912 item->batching = batch;
4914 batching_item = batch?item:NULL;
4917 debug_print("IMAP switching to batch mode\n");
4918 if (flags_set_table) {
4919 g_warning("flags_set_table non-null but we just entered batch mode!\n");
4920 flags_set_table = NULL;
4922 if (flags_unset_table) {
4923 g_warning("flags_unset_table non-null but we just entered batch mode!\n");
4924 flags_unset_table = NULL;
4926 flags_set_table = g_hash_table_new(NULL, g_direct_equal);
4927 flags_unset_table = g_hash_table_new(NULL, g_direct_equal);
4929 debug_print("IMAP switching away from batch mode\n");
4931 process_hashtable();