2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2005 Hiroyuki Yamamoto
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27 #include <glib/gi18n.h>
50 #include "procheader.h"
51 #include "prefs_account.h"
56 #include "prefs_common.h"
57 #include "inputdialog.h"
59 #include "remotefolder.h"
60 #include "alertpanel.h"
62 #include "statusbar.h"
68 typedef struct _thread_data {
80 typedef struct _IMAPFolder IMAPFolder;
81 typedef struct _IMAPSession IMAPSession;
82 typedef struct _IMAPNameSpace IMAPNameSpace;
83 typedef struct _IMAPFolderItem IMAPFolderItem;
85 #include "prefs_account.h"
87 #define IMAP_FOLDER(obj) ((IMAPFolder *)obj)
88 #define IMAP_FOLDER_ITEM(obj) ((IMAPFolderItem *)obj)
89 #define IMAP_SESSION(obj) ((IMAPSession *)obj)
95 /* list of IMAPNameSpace */
105 gboolean authenticated;
114 gboolean folder_content_changed;
118 struct _IMAPNameSpace
124 #define IMAP_SUCCESS 0
125 #define IMAP_SOCKET 2
126 #define IMAP_AUTHFAIL 3
127 #define IMAP_PROTOCOL 4
128 #define IMAP_SYNTAX 5
132 #define IMAPBUFSIZE 8192
136 IMAP_FLAG_SEEN = 1 << 0,
137 IMAP_FLAG_ANSWERED = 1 << 1,
138 IMAP_FLAG_FLAGGED = 1 << 2,
139 IMAP_FLAG_DELETED = 1 << 3,
140 IMAP_FLAG_DRAFT = 1 << 4
143 #define IMAP_IS_SEEN(flags) ((flags & IMAP_FLAG_SEEN) != 0)
144 #define IMAP_IS_ANSWERED(flags) ((flags & IMAP_FLAG_ANSWERED) != 0)
145 #define IMAP_IS_FLAGGED(flags) ((flags & IMAP_FLAG_FLAGGED) != 0)
146 #define IMAP_IS_DELETED(flags) ((flags & IMAP_FLAG_DELETED) != 0)
147 #define IMAP_IS_DRAFT(flags) ((flags & IMAP_FLAG_DRAFT) != 0)
150 #define IMAP4_PORT 143
152 #define IMAPS_PORT 993
155 #define IMAP_CMD_LIMIT 1000
157 #define QUOTE_IF_REQUIRED(out, str) \
159 if (*str != '"' && strpbrk(str, " \t(){}[]%*") != NULL) { \
163 len = strlen(str) + 3; \
164 Xalloca(__tmp, len, return IMAP_ERROR); \
165 g_snprintf(__tmp, len, "\"%s\"", str); \
168 Xstrdup_a(out, str, return IMAP_ERROR); \
172 typedef gchar * IMAPSet;
174 struct _IMAPFolderItem
183 static void imap_folder_init (Folder *folder,
187 static Folder *imap_folder_new (const gchar *name,
189 static void imap_folder_destroy (Folder *folder);
191 static IMAPSession *imap_session_new (const PrefsAccount *account);
192 static void imap_session_authenticate(IMAPSession *session,
193 const PrefsAccount *account);
194 static void imap_session_destroy (Session *session);
196 static gchar *imap_fetch_msg (Folder *folder,
199 static gint imap_add_msg (Folder *folder,
203 static gint imap_add_msgs (Folder *folder,
206 GRelation *relation);
208 static gint imap_copy_msg (Folder *folder,
211 static gint imap_copy_msgs (Folder *folder,
213 MsgInfoList *msglist,
214 GRelation *relation);
216 static gint imap_remove_msg (Folder *folder,
219 static gint imap_remove_all_msg (Folder *folder,
222 static gboolean imap_is_msg_changed (Folder *folder,
226 static gint imap_close (Folder *folder,
229 static gint imap_scan_tree (Folder *folder);
231 static gint imap_create_tree (Folder *folder);
233 static FolderItem *imap_create_folder (Folder *folder,
236 static gint imap_rename_folder (Folder *folder,
239 static gint imap_remove_folder (Folder *folder,
242 static FolderItem *imap_folder_item_new (Folder *folder);
243 static void imap_folder_item_destroy (Folder *folder,
246 static IMAPSession *imap_session_get (Folder *folder);
248 static gint imap_greeting (IMAPSession *session);
249 static gint imap_auth (IMAPSession *session,
254 static gint imap_scan_tree_recursive (IMAPSession *session,
256 static GSList *imap_parse_list (IMAPFolder *folder,
257 IMAPSession *session,
258 const gchar *real_path,
261 static void imap_create_missing_folders (Folder *folder);
262 static FolderItem *imap_create_special_folder
264 SpecialFolderItemType stype,
267 static gint imap_do_copy_msgs (Folder *folder,
269 MsgInfoList *msglist,
270 GRelation *relation);
272 static void imap_delete_all_cached_messages (FolderItem *item);
275 static SockInfo *imap_open (const gchar *server,
279 static SockInfo *imap_open (const gchar *server,
284 static SockInfo *imap_open_tunnel(const gchar *server,
285 const gchar *tunnelcmd,
288 static SockInfo *imap_open_tunnel(const gchar *server,
289 const gchar *tunnelcmd);
293 static SockInfo *imap_init_sock(SockInfo *sock, SSLType ssl_type);
295 static SockInfo *imap_init_sock(SockInfo *sock);
298 static gchar *imap_get_flag_str (IMAPFlags flags);
299 static gint imap_set_message_flags (IMAPSession *session,
300 MsgNumberList *numlist,
303 static gint imap_select (IMAPSession *session,
309 guint32 *uid_validity,
311 static gint imap_status (IMAPSession *session,
317 guint32 *uid_validity,
321 static void imap_parse_namespace (IMAPSession *session,
323 static void imap_get_namespace_by_list (IMAPSession *session,
325 static IMAPNameSpace *imap_find_namespace (IMAPFolder *folder,
327 static gchar imap_get_path_separator (IMAPFolder *folder,
329 static gchar *imap_get_real_path (IMAPFolder *folder,
332 static gchar *imap_parse_atom (SockInfo *sock,
337 static MsgFlags imap_parse_flags (const gchar *flag_str);
338 static MsgInfo *imap_parse_envelope (SockInfo *sock,
342 static gboolean imap_has_capability (IMAPSession *session,
344 static void imap_free_capabilities (IMAPSession *session);
346 /* low-level IMAP4rev1 commands */
347 static gint imap_cmd_authenticate
348 (IMAPSession *session,
352 static gint imap_cmd_login (IMAPSession *session,
355 static gint imap_cmd_logout (IMAPSession *session);
356 static gint imap_cmd_noop (IMAPSession *session);
358 static gint imap_cmd_starttls (IMAPSession *session);
360 static gint imap_cmd_namespace (IMAPSession *session,
362 static gint imap_cmd_list (IMAPSession *session,
364 const gchar *mailbox,
366 static gint imap_cmd_do_select (IMAPSession *session,
372 guint32 *uid_validity,
374 static gint imap_cmd_select (IMAPSession *session,
379 guint32 *uid_validity,
381 static gint imap_cmd_examine (IMAPSession *session,
386 guint32 *uid_validity,
388 static gint imap_cmd_create (IMAPSession *sock,
389 const gchar *folder);
390 static gint imap_cmd_rename (IMAPSession *sock,
391 const gchar *oldfolder,
392 const gchar *newfolder);
393 static gint imap_cmd_delete (IMAPSession *session,
394 const gchar *folder);
395 static gint imap_cmd_envelope (IMAPSession *session,
397 static gint imap_cmd_fetch (IMAPSession *sock,
399 const gchar *filename);
400 static gint imap_cmd_append (IMAPSession *session,
401 const gchar *destfolder,
405 static gint imap_cmd_copy (IMAPSession *session,
406 const gchar *seq_set,
407 const gchar *destfolder,
408 GRelation *uid_mapping);
409 static gint imap_cmd_store (IMAPSession *session,
412 static gint imap_cmd_expunge (IMAPSession *session,
414 static gint imap_cmd_close (IMAPSession *session);
416 static gint imap_cmd_ok (IMAPSession *session,
418 static gint imap_cmd_ok_block (IMAPSession *session,
420 static gint imap_cmd_ok_with_block
421 (IMAPSession *session,
424 static void imap_gen_send (IMAPSession *session,
425 const gchar *format, ...);
426 static gint imap_gen_recv (IMAPSession *session,
428 static gint imap_gen_recv_block (IMAPSession *session,
430 static gint imap_gen_recv_with_block
431 (IMAPSession *session,
435 /* misc utility functions */
436 static gchar *strchr_cpy (const gchar *src,
440 static gchar *get_quoted (const gchar *src,
444 static gchar *search_array_contain_str (GPtrArray *array,
446 static gchar *search_array_str (GPtrArray *array,
448 static void imap_path_separator_subst (gchar *str,
451 static gchar *imap_utf8_to_modified_utf7 (const gchar *from);
452 static gchar *imap_modified_utf7_to_utf8 (const gchar *mutf7_str);
454 static GSList *imap_get_seq_set_from_numlist (MsgNumberList *msglist);
455 static GSList *imap_get_seq_set_from_msglist (MsgInfoList *msglist);
456 static void imap_seq_set_free (GSList *seq_list);
458 static gboolean imap_rename_folder_func (GNode *node,
460 static gint imap_get_num_list (Folder *folder,
463 gboolean *old_uids_valid);
464 static GSList *imap_get_msginfos (Folder *folder,
466 GSList *msgnum_list);
467 static MsgInfo *imap_get_msginfo (Folder *folder,
470 static gboolean imap_scan_required (Folder *folder,
472 static void imap_change_flags (Folder *folder,
475 MsgPermFlags newflags);
476 static gint imap_get_flags (Folder *folder,
478 MsgInfoList *msglist,
479 GRelation *msgflags);
480 static gchar *imap_folder_get_path (Folder *folder);
481 static gchar *imap_item_get_path (Folder *folder,
484 static FolderClass imap_class;
488 void *imap_getline_thread(void *data)
490 thread_data *td = (thread_data *)data;
493 line = sock_getline(td->sock);
501 /* imap_getline just wraps sock_getline inside a thread,
502 * performing gtk updates so that the interface isn't frozen.
504 static gchar *imap_getline(SockInfo *sock)
506 #if (defined USE_PTHREAD && defined __GLIBC__ && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 3)))
507 thread_data *td = g_new0(thread_data, 1);
513 if (pthread_create(&pt, PTHREAD_CREATE_JOINABLE,
514 imap_getline_thread, td) != 0) {
516 return sock_getline(sock);
519 debug_print("+++waiting for imap_getline_thread...\n");
521 /* don't let the interface freeze while waiting */
524 debug_print("---imap_getline_thread done %s\n");
526 /* get the thread's return value and clean its resources */
527 pthread_join(pt, (void *)&line);
532 return sock_getline(sock);
536 FolderClass *imap_get_class(void)
538 if (imap_class.idstr == NULL) {
539 imap_class.type = F_IMAP;
540 imap_class.idstr = "imap";
541 imap_class.uistr = "IMAP4";
543 /* Folder functions */
544 imap_class.new_folder = imap_folder_new;
545 imap_class.destroy_folder = imap_folder_destroy;
546 imap_class.scan_tree = imap_scan_tree;
547 imap_class.create_tree = imap_create_tree;
549 /* FolderItem functions */
550 imap_class.item_new = imap_folder_item_new;
551 imap_class.item_destroy = imap_folder_item_destroy;
552 imap_class.item_get_path = imap_item_get_path;
553 imap_class.create_folder = imap_create_folder;
554 imap_class.rename_folder = imap_rename_folder;
555 imap_class.remove_folder = imap_remove_folder;
556 imap_class.close = imap_close;
557 imap_class.get_num_list = imap_get_num_list;
558 imap_class.scan_required = imap_scan_required;
560 /* Message functions */
561 imap_class.get_msginfo = imap_get_msginfo;
562 imap_class.get_msginfos = imap_get_msginfos;
563 imap_class.fetch_msg = imap_fetch_msg;
564 imap_class.add_msg = imap_add_msg;
565 imap_class.add_msgs = imap_add_msgs;
566 imap_class.copy_msg = imap_copy_msg;
567 imap_class.copy_msgs = imap_copy_msgs;
568 imap_class.remove_msg = imap_remove_msg;
569 imap_class.remove_all_msg = imap_remove_all_msg;
570 imap_class.is_msg_changed = imap_is_msg_changed;
571 imap_class.change_flags = imap_change_flags;
572 imap_class.get_flags = imap_get_flags;
578 static gchar *get_seq_set_from_seq_list(GSList *seq_list)
584 for (cur = seq_list; cur != NULL; cur = cur->next) {
585 tmp = val?g_strdup(val):NULL;
587 val = g_strconcat(tmp?tmp:"", tmp?",":"",(gchar *)cur->data,
595 static Folder *imap_folder_new(const gchar *name, const gchar *path)
599 folder = (Folder *)g_new0(IMAPFolder, 1);
600 folder->klass = &imap_class;
601 imap_folder_init(folder, name, path);
606 static void imap_folder_destroy(Folder *folder)
610 dir = imap_folder_get_path(folder);
611 if (is_dir_exist(dir))
612 remove_dir_recursive(dir);
615 folder_remote_folder_destroy(REMOTE_FOLDER(folder));
618 static void imap_folder_init(Folder *folder, const gchar *name,
621 folder_remote_folder_init((Folder *)folder, name, path);
624 static FolderItem *imap_folder_item_new(Folder *folder)
626 IMAPFolderItem *item;
628 item = g_new0(IMAPFolderItem, 1);
631 item->uid_list = NULL;
633 return (FolderItem *)item;
636 static void imap_folder_item_destroy(Folder *folder, FolderItem *_item)
638 IMAPFolderItem *item = (IMAPFolderItem *)_item;
640 g_return_if_fail(item != NULL);
641 g_slist_free(item->uid_list);
646 static gboolean imap_reset_uid_lists_func(GNode *node, gpointer data)
648 IMAPFolderItem *item = (IMAPFolderItem *)node->data;
652 g_slist_free(item->uid_list);
653 item->uid_list = NULL;
658 static void imap_reset_uid_lists(Folder *folder)
660 if(folder->node == NULL)
663 /* Destroy all uid lists and rest last uid */
664 g_node_traverse(folder->node, G_IN_ORDER, G_TRAVERSE_ALL, -1, imap_reset_uid_lists_func, NULL);
667 /* Send CAPABILITY, and examine the server's response to see whether this
668 * connection is pre-authenticated or not and build a list of CAPABILITIES. */
669 static gint imap_greeting(IMAPSession *session)
674 imap_gen_send(session, "CAPABILITY");
676 argbuf = g_ptr_array_new();
678 if (imap_cmd_ok(session, argbuf) != IMAP_SUCCESS ||
679 ((capstr = search_array_str(argbuf, "CAPABILITY ")) == NULL)) {
680 ptr_array_free_strings(argbuf);
681 g_ptr_array_free(argbuf, TRUE);
685 session->authenticated = search_array_str(argbuf, "PREAUTH") != NULL;
687 capstr += strlen("CAPABILITY ");
689 IMAP_SESSION(session)->capability = g_strsplit(capstr, " ", 0);
691 ptr_array_free_strings(argbuf);
692 g_ptr_array_free(argbuf, TRUE);
694 if (imap_has_capability(session, "UIDPLUS"))
695 session->uidplus = TRUE;
700 static gint imap_auth(IMAPSession *session, const gchar *user, const gchar *pass,
705 if (type == 0 || type == IMAP_AUTH_LOGIN)
706 ok = imap_cmd_login(session, user, pass);
708 ok = imap_cmd_authenticate(session, user, pass, type);
710 if (ok == IMAP_SUCCESS)
711 session->authenticated = TRUE;
716 static IMAPSession *imap_session_get(Folder *folder)
718 RemoteFolder *rfolder = REMOTE_FOLDER(folder);
719 IMAPSession *session = NULL;
721 g_return_val_if_fail(folder != NULL, NULL);
722 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, NULL);
723 g_return_val_if_fail(folder->account != NULL, NULL);
725 if (prefs_common.work_offline && !imap_gtk_should_override()) {
729 /* Make sure we have a session */
730 if (rfolder->session != NULL) {
731 session = IMAP_SESSION(rfolder->session);
733 imap_reset_uid_lists(folder);
734 session = imap_session_new(folder->account);
739 if (SESSION(session)->sock->state == CONN_DISCONNECTED) {
740 debug_print("IMAP server disconnected\n");
741 session_destroy(SESSION(session));
742 imap_reset_uid_lists(folder);
743 session = imap_session_new(folder->account);
746 /* Make sure session is authenticated */
747 if (!IMAP_SESSION(session)->authenticated)
748 imap_session_authenticate(IMAP_SESSION(session), folder->account);
749 if (!IMAP_SESSION(session)->authenticated) {
750 session_destroy(SESSION(session));
751 rfolder->session = NULL;
755 /* Make sure we have parsed the IMAP namespace */
756 imap_parse_namespace(IMAP_SESSION(session),
757 IMAP_FOLDER(folder));
759 /* I think the point of this code is to avoid sending a
760 * keepalive if we've used the session recently and therefore
761 * think it's still alive. Unfortunately, most of the code
762 * does not yet check for errors on the socket, and so if the
763 * connection drops we don't notice until the timeout expires.
764 * A better solution than sending a NOOP every time would be
765 * for every command to be prepared to retry until it is
766 * successfully sent. -- mbp */
767 if (time(NULL) - SESSION(session)->last_access_time > SESSION_TIMEOUT_INTERVAL) {
768 /* verify that the session is still alive */
769 if (imap_cmd_noop(session) != IMAP_SUCCESS) {
770 /* Check if this is the first try to establish a
771 connection, if yes we don't try to reconnect */
772 if (rfolder->session == NULL) {
773 log_warning(_("Connecting to %s failed"),
774 folder->account->recv_server);
775 session_destroy(SESSION(session));
778 log_warning(_("IMAP4 connection to %s has been"
779 " disconnected. Reconnecting...\n"),
780 folder->account->recv_server);
781 statusbar_print_all(_("IMAP4 connection to %s has been"
782 " disconnected. Reconnecting...\n"),
783 folder->account->recv_server);
784 session_destroy(SESSION(session));
785 /* Clear folders session to make imap_session_get create
786 a new session, because of rfolder->session == NULL
787 it will not try to reconnect again and so avoid an
789 rfolder->session = NULL;
790 session = imap_session_get(folder);
796 rfolder->session = SESSION(session);
798 return IMAP_SESSION(session);
801 static IMAPSession *imap_session_new(const PrefsAccount *account)
803 IMAPSession *session;
808 /* FIXME: IMAP over SSL only... */
811 port = account->set_imapport ? account->imapport
812 : account->ssl_imap == SSL_TUNNEL ? IMAPS_PORT : IMAP4_PORT;
813 ssl_type = account->ssl_imap;
815 port = account->set_imapport ? account->imapport
819 if (account->set_tunnelcmd) {
820 log_message(_("creating tunneled IMAP4 connection\n"));
822 if ((imap_sock = imap_open_tunnel(account->recv_server,
826 if ((imap_sock = imap_open_tunnel(account->recv_server,
827 account->tunnelcmd)) == NULL)
831 g_return_val_if_fail(account->recv_server != NULL, NULL);
833 log_message(_("creating IMAP4 connection to %s:%d ...\n"),
834 account->recv_server, port);
837 if ((imap_sock = imap_open(account->recv_server, port,
840 if ((imap_sock = imap_open(account->recv_server, port)) == NULL)
845 session = g_new0(IMAPSession, 1);
846 session_init(SESSION(session));
847 SESSION(session)->type = SESSION_IMAP;
848 SESSION(session)->server = g_strdup(account->recv_server);
849 SESSION(session)->sock = imap_sock;
851 SESSION(session)->destroy = imap_session_destroy;
853 session->capability = NULL;
855 session->authenticated = FALSE;
856 session->mbox = NULL;
857 session->cmd_count = 0;
859 /* Only need to log in if the connection was not PREAUTH */
860 if (imap_greeting(session) != IMAP_SUCCESS) {
861 session_destroy(SESSION(session));
866 if (account->ssl_imap == SSL_STARTTLS &&
867 imap_has_capability(session, "STARTTLS")) {
870 ok = imap_cmd_starttls(session);
871 if (ok != IMAP_SUCCESS) {
872 log_warning(_("Can't start TLS session.\n"));
873 session_destroy(SESSION(session));
876 if (!ssl_init_socket_with_method(SESSION(session)->sock,
878 session_destroy(SESSION(session));
882 imap_free_capabilities(session);
883 session->authenticated = FALSE;
884 session->uidplus = FALSE;
885 session->cmd_count = 1;
887 if (imap_greeting(session) != IMAP_SUCCESS) {
888 session_destroy(SESSION(session));
893 log_message("IMAP connection is %s-authenticated\n",
894 (session->authenticated) ? "pre" : "un");
899 static void imap_session_authenticate(IMAPSession *session,
900 const PrefsAccount *account)
904 g_return_if_fail(account->userid != NULL);
906 pass = account->passwd;
909 tmp_pass = input_dialog_query_password(account->recv_server, account->userid);
912 Xstrdup_a(pass, tmp_pass, {g_free(tmp_pass); return;});
915 statusbar_print_all(_("Connecting to IMAP4 server %s...\n"),
916 account->recv_server);
917 if (imap_auth(session, account->userid, pass, account->imap_auth_type) != IMAP_SUCCESS) {
918 imap_cmd_logout(session);
923 session->authenticated = TRUE;
926 static void imap_session_destroy(Session *session)
928 imap_free_capabilities(IMAP_SESSION(session));
929 g_free(IMAP_SESSION(session)->mbox);
930 sock_close(session->sock);
931 session->sock = NULL;
934 static gchar *imap_fetch_msg(Folder *folder, FolderItem *item, gint uid)
936 gchar *path, *filename;
937 IMAPSession *session;
940 g_return_val_if_fail(folder != NULL, NULL);
941 g_return_val_if_fail(item != NULL, NULL);
943 path = folder_item_get_path(item);
944 if (!is_dir_exist(path))
946 filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(uid), NULL);
949 if (is_file_exist(filename)) {
950 debug_print("message %d has been already cached.\n", uid);
954 session = imap_session_get(folder);
960 printf("fetching messages\n");
961 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
962 NULL, NULL, NULL, NULL, FALSE);
963 if (ok != IMAP_SUCCESS) {
964 g_warning("can't select mailbox %s\n", item->path);
969 debug_print("getting message %d...\n", uid);
970 ok = imap_cmd_fetch(session, (guint32)uid, filename);
972 if (ok != IMAP_SUCCESS) {
973 g_warning("can't fetch message %d\n", uid);
981 static gint imap_add_msg(Folder *folder, FolderItem *dest,
982 const gchar *file, MsgFlags *flags)
986 MsgFileInfo fileinfo;
988 g_return_val_if_fail(file != NULL, -1);
990 fileinfo.msginfo = NULL;
991 fileinfo.file = (gchar *)file;
992 fileinfo.flags = flags;
993 file_list.data = &fileinfo;
994 file_list.next = NULL;
996 ret = imap_add_msgs(folder, dest, &file_list, NULL);
1000 static gint imap_add_msgs(Folder *folder, FolderItem *dest, GSList *file_list,
1001 GRelation *relation)
1004 IMAPSession *session;
1005 guint32 last_uid = 0;
1007 MsgFileInfo *fileinfo;
1010 g_return_val_if_fail(folder != NULL, -1);
1011 g_return_val_if_fail(dest != NULL, -1);
1012 g_return_val_if_fail(file_list != NULL, -1);
1014 session = imap_session_get(folder);
1015 if (!session) return -1;
1017 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
1019 for (cur = file_list; cur != NULL; cur = cur->next) {
1020 IMAPFlags iflags = 0;
1021 guint32 new_uid = 0;
1023 fileinfo = (MsgFileInfo *)cur->data;
1025 if (fileinfo->flags) {
1026 if (MSG_IS_MARKED(*fileinfo->flags))
1027 iflags |= IMAP_FLAG_FLAGGED;
1028 if (MSG_IS_REPLIED(*fileinfo->flags))
1029 iflags |= IMAP_FLAG_ANSWERED;
1030 if (!MSG_IS_UNREAD(*fileinfo->flags))
1031 iflags |= IMAP_FLAG_SEEN;
1034 if (dest->stype == F_OUTBOX ||
1035 dest->stype == F_QUEUE ||
1036 dest->stype == F_DRAFT ||
1037 dest->stype == F_TRASH)
1038 iflags |= IMAP_FLAG_SEEN;
1040 ok = imap_cmd_append(session, destdir, fileinfo->file, iflags,
1043 if (ok != IMAP_SUCCESS) {
1044 g_warning("can't append message %s\n", fileinfo->file);
1049 if (relation != NULL)
1050 g_relation_insert(relation, fileinfo->msginfo != NULL ?
1051 (gpointer) fileinfo->msginfo : (gpointer) fileinfo,
1052 GINT_TO_POINTER(dest->last_num + 1));
1053 if (last_uid < new_uid)
1062 static gint imap_do_copy_msgs(Folder *folder, FolderItem *dest,
1063 MsgInfoList *msglist, GRelation *relation)
1067 GSList *seq_list, *cur;
1069 IMAPSession *session;
1070 gint ok = IMAP_SUCCESS;
1071 GRelation *uid_mapping;
1074 g_return_val_if_fail(folder != NULL, -1);
1075 g_return_val_if_fail(dest != NULL, -1);
1076 g_return_val_if_fail(msglist != NULL, -1);
1078 session = imap_session_get(folder);
1079 if (!session) return -1;
1081 msginfo = (MsgInfo *)msglist->data;
1083 src = msginfo->folder;
1085 g_warning("the src folder is identical to the dest.\n");
1089 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
1090 NULL, NULL, NULL, NULL, FALSE);
1091 if (ok != IMAP_SUCCESS)
1094 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
1095 seq_list = imap_get_seq_set_from_msglist(msglist);
1096 uid_mapping = g_relation_new(2);
1097 g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
1099 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
1100 gchar *seq_set = (gchar *)cur->data;
1102 debug_print("Copying message %s%c[%s] to %s ...\n",
1103 src->path, G_DIR_SEPARATOR,
1106 ok = imap_cmd_copy(session, seq_set, destdir, uid_mapping);
1107 if (ok != IMAP_SUCCESS) {
1108 g_relation_destroy(uid_mapping);
1109 imap_seq_set_free(seq_list);
1114 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
1115 MsgInfo *msginfo = (MsgInfo *)cur->data;
1118 tuples = g_relation_select(uid_mapping,
1119 GINT_TO_POINTER(msginfo->msgnum),
1121 if (tuples->len > 0) {
1122 gint num = GPOINTER_TO_INT(g_tuples_index(tuples, 0, 1));
1123 g_relation_insert(relation, msginfo,
1124 GPOINTER_TO_INT(num));
1128 g_relation_insert(relation, msginfo,
1129 GPOINTER_TO_INT(0));
1130 g_tuples_destroy(tuples);
1133 g_relation_destroy(uid_mapping);
1134 imap_seq_set_free(seq_list);
1138 if (ok == IMAP_SUCCESS)
1144 static gint imap_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1148 g_return_val_if_fail(msginfo != NULL, -1);
1150 msglist.data = msginfo;
1151 msglist.next = NULL;
1153 return imap_copy_msgs(folder, dest, &msglist, NULL);
1156 static gint imap_copy_msgs(Folder *folder, FolderItem *dest,
1157 MsgInfoList *msglist, GRelation *relation)
1163 g_return_val_if_fail(folder != NULL, -1);
1164 g_return_val_if_fail(dest != NULL, -1);
1165 g_return_val_if_fail(msglist != NULL, -1);
1167 msginfo = (MsgInfo *)msglist->data;
1168 g_return_val_if_fail(msginfo->folder != NULL, -1);
1170 if (folder == msginfo->folder->folder)
1171 return imap_do_copy_msgs(folder, dest, msglist, relation);
1173 file_list = procmsg_get_message_file_list(msglist);
1174 g_return_val_if_fail(file_list != NULL, -1);
1176 ret = imap_add_msgs(folder, dest, file_list, relation);
1178 procmsg_message_file_list_free(file_list);
1183 static gint imap_remove_all_msg(Folder *folder, FolderItem *item)
1186 IMAPSession *session;
1189 g_return_val_if_fail(folder != NULL, -1);
1190 g_return_val_if_fail(item != NULL, -1);
1192 session = imap_session_get(folder);
1193 if (!session) return -1;
1195 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
1196 NULL, NULL, NULL, NULL, FALSE);
1197 if (ok != IMAP_SUCCESS)
1200 imap_gen_send(session, "STORE 1:* +FLAGS.SILENT (\\Deleted)");
1201 ok = imap_cmd_ok(session, NULL);
1202 if (ok != IMAP_SUCCESS) {
1203 log_warning(_("can't set deleted flags: 1:*\n"));
1207 ok = imap_cmd_expunge(session, NULL);
1208 if (ok != IMAP_SUCCESS) {
1209 log_warning(_("can't expunge\n"));
1213 dir = folder_item_get_path(item);
1214 if (is_dir_exist(dir))
1215 remove_all_numbered_files(dir);
1218 return IMAP_SUCCESS;
1221 static gboolean imap_is_msg_changed(Folder *folder, FolderItem *item,
1224 /* TODO: properly implement this method */
1228 static gint imap_close(Folder *folder, FolderItem *item)
1231 IMAPSession *session;
1233 g_return_val_if_fail(folder != NULL, -1);
1234 g_return_val_if_fail(item != NULL, -1);
1235 g_return_val_if_fail(item->path != NULL, -1);
1237 session = imap_session_get(folder);
1238 if (!session) return -1;
1240 if (session->mbox) {
1241 if (strcmp2(session->mbox, item->path) != 0) return -1;
1243 ok = imap_cmd_close(session);
1244 if (ok != IMAP_SUCCESS)
1245 log_warning(_("can't close folder\n"));
1247 g_free(session->mbox);
1249 session->mbox = NULL;
1257 static gint imap_scan_tree(Folder *folder)
1259 FolderItem *item = NULL;
1260 IMAPSession *session;
1261 gchar *root_folder = NULL;
1263 g_return_val_if_fail(folder != NULL, -1);
1264 g_return_val_if_fail(folder->account != NULL, -1);
1266 session = imap_session_get(folder);
1268 if (!folder->node) {
1269 folder_tree_destroy(folder);
1270 item = folder_item_new(folder, folder->name, NULL);
1271 item->folder = folder;
1272 folder->node = item->node = g_node_new(item);
1277 if (folder->account->imap_dir && *folder->account->imap_dir) {
1282 Xstrdup_a(root_folder, folder->account->imap_dir, return -1);
1283 extract_quote(root_folder, '"');
1284 subst_char(root_folder,
1285 imap_get_path_separator(IMAP_FOLDER(folder),
1288 strtailchomp(root_folder, '/');
1289 real_path = imap_get_real_path
1290 (IMAP_FOLDER(folder), root_folder);
1291 debug_print("IMAP root directory: %s\n", real_path);
1293 /* check if root directory exist */
1294 argbuf = g_ptr_array_new();
1295 ok = imap_cmd_list(session, NULL, real_path, argbuf);
1296 if (ok != IMAP_SUCCESS ||
1297 search_array_str(argbuf, "LIST ") == NULL) {
1298 log_warning(_("root folder %s does not exist\n"), real_path);
1299 g_ptr_array_free(argbuf, TRUE);
1302 if (!folder->node) {
1303 item = folder_item_new(folder, folder->name, NULL);
1304 item->folder = folder;
1305 folder->node = item->node = g_node_new(item);
1309 g_ptr_array_free(argbuf, TRUE);
1314 item = FOLDER_ITEM(folder->node->data);
1315 if (!item || ((item->path || root_folder) &&
1316 strcmp2(item->path, root_folder) != 0)) {
1317 folder_tree_destroy(folder);
1318 item = folder_item_new(folder, folder->name, root_folder);
1319 item->folder = folder;
1320 folder->node = item->node = g_node_new(item);
1323 imap_scan_tree_recursive(session, FOLDER_ITEM(folder->node->data));
1324 imap_create_missing_folders(folder);
1329 static gint imap_scan_tree_recursive(IMAPSession *session, FolderItem *item)
1332 IMAPFolder *imapfolder;
1333 FolderItem *new_item;
1334 GSList *item_list, *cur;
1337 gchar *wildcard_path;
1341 g_return_val_if_fail(item != NULL, -1);
1342 g_return_val_if_fail(item->folder != NULL, -1);
1343 g_return_val_if_fail(item->no_sub == FALSE, -1);
1345 folder = item->folder;
1346 imapfolder = IMAP_FOLDER(folder);
1348 separator = imap_get_path_separator(imapfolder, item->path);
1350 if (folder->ui_func)
1351 folder->ui_func(folder, item, folder->ui_func_data);
1354 wildcard[0] = separator;
1357 real_path = imap_get_real_path(imapfolder, item->path);
1361 real_path = g_strdup("");
1364 Xstrcat_a(wildcard_path, real_path, wildcard,
1365 {g_free(real_path); return IMAP_ERROR;});
1366 QUOTE_IF_REQUIRED(wildcard_path, wildcard_path);
1368 imap_gen_send(session, "LIST \"\" %s",
1371 strtailchomp(real_path, separator);
1372 item_list = imap_parse_list(imapfolder, session, real_path, NULL);
1375 node = item->node->children;
1376 while (node != NULL) {
1377 FolderItem *old_item = FOLDER_ITEM(node->data);
1378 GNode *next = node->next;
1381 for (cur = item_list; cur != NULL; cur = cur->next) {
1382 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1383 if (!strcmp2(old_item->path, cur_item->path)) {
1384 new_item = cur_item;
1389 debug_print("folder '%s' not found. removing...\n",
1391 folder_item_remove(old_item);
1393 old_item->no_sub = new_item->no_sub;
1394 old_item->no_select = new_item->no_select;
1395 if (old_item->no_sub == TRUE && node->children) {
1396 debug_print("folder '%s' doesn't have "
1397 "subfolders. removing...\n",
1399 folder_item_remove_children(old_item);
1406 for (cur = item_list; cur != NULL; cur = cur->next) {
1407 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1409 for (node = item->node->children; node != NULL;
1410 node = node->next) {
1411 if (!strcmp2(FOLDER_ITEM(node->data)->path,
1413 new_item = FOLDER_ITEM(node->data);
1414 folder_item_destroy(cur_item);
1420 new_item = cur_item;
1421 debug_print("new folder '%s' found.\n", new_item->path);
1422 folder_item_append(item, new_item);
1425 if (!strcmp(new_item->path, "INBOX")) {
1426 new_item->stype = F_INBOX;
1427 folder->inbox = new_item;
1428 } else if (!folder_item_parent(item) || item->stype == F_INBOX) {
1431 base = g_path_get_basename(new_item->path);
1433 if (!folder->outbox && !g_ascii_strcasecmp(base, "Sent")) {
1434 new_item->stype = F_OUTBOX;
1435 folder->outbox = new_item;
1436 } else if (!folder->draft && !g_ascii_strcasecmp(base, "Drafts")) {
1437 new_item->stype = F_DRAFT;
1438 folder->draft = new_item;
1439 } else if (!folder->queue && !g_ascii_strcasecmp(base, "Queue")) {
1440 new_item->stype = F_QUEUE;
1441 folder->queue = new_item;
1442 } else if (!folder->trash && !g_ascii_strcasecmp(base, "Trash")) {
1443 new_item->stype = F_TRASH;
1444 folder->trash = new_item;
1449 if (new_item->no_sub == FALSE)
1450 imap_scan_tree_recursive(session, new_item);
1453 g_slist_free(item_list);
1455 return IMAP_SUCCESS;
1458 static GSList *imap_parse_list(IMAPFolder *folder, IMAPSession *session,
1459 const gchar *real_path, gchar *separator)
1461 gchar buf[IMAPBUFSIZE];
1463 gchar separator_str[16];
1466 gchar *loc_name, *loc_path;
1467 GSList *item_list = NULL;
1469 FolderItem *new_item;
1471 debug_print("getting list of %s ...\n",
1472 *real_path ? real_path : "\"\"");
1474 str = g_string_new(NULL);
1477 if (sock_gets(SESSION(session)->sock, buf, sizeof(buf)) <= 0) {
1478 log_warning(_("error occurred while getting LIST.\n"));
1482 if (buf[0] != '*' || buf[1] != ' ') {
1483 log_print("IMAP4< %s\n", buf);
1484 if (sscanf(buf, "%*d %16s", buf) < 1 ||
1485 strcmp(buf, "OK") != 0)
1486 log_warning(_("error occurred while getting LIST.\n"));
1490 debug_print("IMAP4< %s\n", buf);
1492 g_string_assign(str, buf);
1494 if (strncmp(p, "LIST ", 5) != 0) continue;
1497 if (*p != '(') continue;
1499 p = strchr_cpy(p, ')', flags, sizeof(flags));
1501 while (*p == ' ') p++;
1503 p = strchr_cpy(p, ' ', separator_str, sizeof(separator_str));
1505 extract_quote(separator_str, '"');
1506 if (!strcmp(separator_str, "NIL"))
1507 separator_str[0] = '\0';
1509 *separator = separator_str[0];
1512 while (*p == ' ') p++;
1513 if (*p == '{' || *p == '"')
1514 p = imap_parse_atom(SESSION(session)->sock, p,
1515 buf, sizeof(buf), str);
1517 strncpy2(buf, p, sizeof(buf));
1518 strtailchomp(buf, separator_str[0]);
1519 if (buf[0] == '\0') continue;
1520 if (!strcmp(buf, real_path)) continue;
1522 if (separator_str[0] != '\0')
1523 subst_char(buf, separator_str[0], '/');
1524 base = g_path_get_basename(buf);
1525 if (base[0] == '.') continue;
1527 loc_name = imap_modified_utf7_to_utf8(base);
1528 loc_path = imap_modified_utf7_to_utf8(buf);
1529 new_item = folder_item_new(FOLDER(folder), loc_name, loc_path);
1530 if (strcasestr(flags, "\\Noinferiors") != NULL)
1531 new_item->no_sub = TRUE;
1532 if (strcmp(buf, "INBOX") != 0 &&
1533 strcasestr(flags, "\\Noselect") != NULL)
1534 new_item->no_select = TRUE;
1536 item_list = g_slist_append(item_list, new_item);
1538 debug_print("folder '%s' found.\n", loc_path);
1544 g_string_free(str, TRUE);
1549 static gint imap_create_tree(Folder *folder)
1551 g_return_val_if_fail(folder != NULL, -1);
1552 g_return_val_if_fail(folder->node != NULL, -1);
1553 g_return_val_if_fail(folder->node->data != NULL, -1);
1554 g_return_val_if_fail(folder->account != NULL, -1);
1556 imap_scan_tree(folder);
1557 imap_create_missing_folders(folder);
1562 static void imap_create_missing_folders(Folder *folder)
1564 g_return_if_fail(folder != NULL);
1567 folder->inbox = imap_create_special_folder
1568 (folder, F_INBOX, "INBOX");
1570 if (!folder->outbox)
1571 folder->outbox = imap_create_special_folder
1572 (folder, F_OUTBOX, "Sent");
1574 folder->draft = imap_create_special_folder
1575 (folder, F_DRAFT, "Drafts");
1577 folder->queue = imap_create_special_folder
1578 (folder, F_QUEUE, "Queue");
1581 folder->trash = imap_create_special_folder
1582 (folder, F_TRASH, "Trash");
1585 static FolderItem *imap_create_special_folder(Folder *folder,
1586 SpecialFolderItemType stype,
1590 FolderItem *new_item;
1592 g_return_val_if_fail(folder != NULL, NULL);
1593 g_return_val_if_fail(folder->node != NULL, NULL);
1594 g_return_val_if_fail(folder->node->data != NULL, NULL);
1595 g_return_val_if_fail(folder->account != NULL, NULL);
1596 g_return_val_if_fail(name != NULL, NULL);
1598 item = FOLDER_ITEM(folder->node->data);
1599 new_item = imap_create_folder(folder, item, name);
1602 g_warning("Can't create '%s'\n", name);
1603 if (!folder->inbox) return NULL;
1605 new_item = imap_create_folder(folder, folder->inbox, name);
1607 g_warning("Can't create '%s' under INBOX\n", name);
1609 new_item->stype = stype;
1611 new_item->stype = stype;
1616 static gchar *imap_folder_get_path(Folder *folder)
1620 g_return_val_if_fail(folder != NULL, NULL);
1621 g_return_val_if_fail(folder->account != NULL, NULL);
1623 folder_path = g_strconcat(get_imap_cache_dir(),
1625 folder->account->recv_server,
1627 folder->account->userid,
1633 static gchar *imap_item_get_path(Folder *folder, FolderItem *item)
1635 gchar *folder_path, *path;
1637 g_return_val_if_fail(folder != NULL, NULL);
1638 g_return_val_if_fail(item != NULL, NULL);
1639 folder_path = imap_folder_get_path(folder);
1641 g_return_val_if_fail(folder_path != NULL, NULL);
1642 if (folder_path[0] == G_DIR_SEPARATOR) {
1644 path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
1647 path = g_strdup(folder_path);
1650 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1651 folder_path, G_DIR_SEPARATOR_S,
1654 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1657 g_free(folder_path);
1662 static FolderItem *imap_create_folder(Folder *folder, FolderItem *parent,
1665 gchar *dirpath, *imap_path;
1666 IMAPSession *session;
1667 FolderItem *new_item;
1673 g_return_val_if_fail(folder != NULL, NULL);
1674 g_return_val_if_fail(folder->account != NULL, NULL);
1675 g_return_val_if_fail(parent != NULL, NULL);
1676 g_return_val_if_fail(name != NULL, NULL);
1678 session = imap_session_get(folder);
1679 if (!session) return NULL;
1681 if (!folder_item_parent(parent) && strcmp(name, "INBOX") == 0)
1682 dirpath = g_strdup(name);
1683 else if (parent->path)
1684 dirpath = g_strconcat(parent->path, "/", name, NULL);
1685 else if ((p = strchr(name, '/')) != NULL && *(p + 1) != '\0')
1686 dirpath = g_strdup(name);
1687 else if (folder->account->imap_dir && *folder->account->imap_dir) {
1690 Xstrdup_a(imap_dir, folder->account->imap_dir, return NULL);
1691 strtailchomp(imap_dir, '/');
1692 dirpath = g_strconcat(imap_dir, "/", name, NULL);
1694 dirpath = g_strdup(name);
1696 /* keep trailing directory separator to create a folder that contains
1698 imap_path = imap_utf8_to_modified_utf7(dirpath);
1699 strtailchomp(dirpath, '/');
1700 Xstrdup_a(new_name, name, {g_free(dirpath); return NULL;});
1701 strtailchomp(new_name, '/');
1702 separator = imap_get_path_separator(IMAP_FOLDER(folder), imap_path);
1703 imap_path_separator_subst(imap_path, separator);
1704 subst_char(new_name, '/', separator);
1706 if (strcmp(name, "INBOX") != 0) {
1709 gboolean exist = FALSE;
1711 argbuf = g_ptr_array_new();
1712 ok = imap_cmd_list(session, NULL, imap_path,
1714 if (ok != IMAP_SUCCESS) {
1715 log_warning(_("can't create mailbox: LIST failed\n"));
1718 ptr_array_free_strings(argbuf);
1719 g_ptr_array_free(argbuf, TRUE);
1723 for (i = 0; i < argbuf->len; i++) {
1725 str = g_ptr_array_index(argbuf, i);
1726 if (!strncmp(str, "LIST ", 5)) {
1731 ptr_array_free_strings(argbuf);
1732 g_ptr_array_free(argbuf, TRUE);
1735 ok = imap_cmd_create(session, imap_path);
1736 if (ok != IMAP_SUCCESS) {
1737 log_warning(_("can't create mailbox\n"));
1745 new_item = folder_item_new(folder, new_name, dirpath);
1746 folder_item_append(parent, new_item);
1750 dirpath = folder_item_get_path(new_item);
1751 if (!is_dir_exist(dirpath))
1752 make_dir_hier(dirpath);
1758 static gint imap_rename_folder(Folder *folder, FolderItem *item,
1763 gchar *real_oldpath;
1764 gchar *real_newpath;
1766 gchar *old_cache_dir;
1767 gchar *new_cache_dir;
1768 IMAPSession *session;
1771 gint exists, recent, unseen;
1772 guint32 uid_validity;
1774 g_return_val_if_fail(folder != NULL, -1);
1775 g_return_val_if_fail(item != NULL, -1);
1776 g_return_val_if_fail(item->path != NULL, -1);
1777 g_return_val_if_fail(name != NULL, -1);
1779 if (strchr(name, imap_get_path_separator(IMAP_FOLDER(folder), item->path)) != NULL) {
1780 g_warning(_("New folder name must not contain the namespace "
1785 session = imap_session_get(folder);
1786 if (!session) return -1;
1788 real_oldpath = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1790 g_free(session->mbox);
1791 session->mbox = NULL;
1792 ok = imap_cmd_examine(session, "INBOX",
1793 &exists, &recent, &unseen, &uid_validity, FALSE);
1794 if (ok != IMAP_SUCCESS) {
1795 g_free(real_oldpath);
1799 separator = imap_get_path_separator(IMAP_FOLDER(folder), item->path);
1800 if (strchr(item->path, G_DIR_SEPARATOR)) {
1801 dirpath = g_path_get_dirname(item->path);
1802 newpath = g_strconcat(dirpath, G_DIR_SEPARATOR_S, name, NULL);
1805 newpath = g_strdup(name);
1807 real_newpath = imap_utf8_to_modified_utf7(newpath);
1808 imap_path_separator_subst(real_newpath, separator);
1810 ok = imap_cmd_rename(session, real_oldpath, real_newpath);
1811 if (ok != IMAP_SUCCESS) {
1812 log_warning(_("can't rename mailbox: %s to %s\n"),
1813 real_oldpath, real_newpath);
1814 g_free(real_oldpath);
1816 g_free(real_newpath);
1821 item->name = g_strdup(name);
1823 old_cache_dir = folder_item_get_path(item);
1825 paths[0] = g_strdup(item->path);
1827 g_node_traverse(item->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1828 imap_rename_folder_func, paths);
1830 if (is_dir_exist(old_cache_dir)) {
1831 new_cache_dir = folder_item_get_path(item);
1832 if (rename(old_cache_dir, new_cache_dir) < 0) {
1833 FILE_OP_ERROR(old_cache_dir, "rename");
1835 g_free(new_cache_dir);
1838 g_free(old_cache_dir);
1841 g_free(real_oldpath);
1842 g_free(real_newpath);
1847 static gint imap_remove_folder(Folder *folder, FolderItem *item)
1850 IMAPSession *session;
1853 gint exists, recent, unseen;
1854 guint32 uid_validity;
1856 g_return_val_if_fail(folder != NULL, -1);
1857 g_return_val_if_fail(item != NULL, -1);
1858 g_return_val_if_fail(item->path != NULL, -1);
1860 session = imap_session_get(folder);
1861 if (!session) return -1;
1863 path = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1865 ok = imap_cmd_examine(session, "INBOX",
1866 &exists, &recent, &unseen, &uid_validity, FALSE);
1867 if (ok != IMAP_SUCCESS) {
1872 ok = imap_cmd_delete(session, path);
1873 if (ok != IMAP_SUCCESS) {
1874 log_warning(_("can't delete mailbox\n"));
1880 cache_dir = folder_item_get_path(item);
1881 if (is_dir_exist(cache_dir) && remove_dir_recursive(cache_dir) < 0)
1882 g_warning("can't remove directory '%s'\n", cache_dir);
1884 folder_item_remove(item);
1889 typedef struct _uncached_data {
1890 IMAPSession *session;
1892 MsgNumberList *numlist;
1896 static void *imap_get_uncached_messages_thread(void *data)
1898 uncached_data *stuff = (uncached_data *)data;
1899 IMAPSession *session = stuff->session;
1900 FolderItem *item = stuff->item;
1901 MsgNumberList *numlist = stuff->numlist;
1904 GSList *newlist = NULL;
1905 GSList *llast = NULL;
1908 GSList *seq_list, *cur;
1911 if (session == NULL || item == NULL || item->folder == NULL
1912 || FOLDER_CLASS(item->folder) != &imap_class) {
1917 seq_list = imap_get_seq_set_from_numlist(numlist);
1918 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
1919 imapset = cur->data;
1921 if (imap_cmd_envelope(session, imapset)
1923 log_warning(_("can't get envelope\n"));
1927 str = g_string_new(NULL);
1930 if ((tmp =sock_getline(SESSION(session)->sock)) == NULL) {
1931 log_warning(_("error occurred while getting envelope.\n"));
1932 g_string_free(str, TRUE);
1936 if (tmp[0] != '*' || tmp[1] != ' ') {
1937 log_print("IMAP4< %s\n", tmp);
1941 if (strstr(tmp, "FETCH") == NULL) {
1942 log_print("IMAP4< %s\n", tmp);
1946 log_print("IMAP4< %s\n", tmp);
1947 g_string_assign(str, tmp);
1950 msginfo = imap_parse_envelope
1951 (SESSION(session)->sock, item, str);
1953 log_warning(_("can't parse envelope: %s\n"), str->str);
1956 if (item->stype == F_QUEUE) {
1957 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_QUEUED);
1958 } else if (item->stype == F_DRAFT) {
1959 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_DRAFT);
1962 msginfo->folder = item;
1965 llast = newlist = g_slist_append(newlist, msginfo);
1967 llast = g_slist_append(llast, msginfo);
1968 llast = llast->next;
1972 g_string_free(str, TRUE);
1974 imap_seq_set_free(seq_list);
1976 session_set_access_time(SESSION(session));
1982 static GSList *imap_get_uncached_messages(IMAPSession *session,
1984 MsgNumberList *numlist)
1986 uncached_data *data = g_new0(uncached_data, 1);
1987 GSList *result = NULL;
1992 data->session = session;
1994 data->numlist = numlist;
1996 if (prefs_common.work_offline && !imap_gtk_should_override()) {
2001 #if (defined USE_PTHREAD && defined __GLIBC__ && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 3)))
2002 if (pthread_create(&pt, PTHREAD_CREATE_JOINABLE,
2003 imap_get_uncached_messages_thread, data) != 0) {
2004 result = (GSList *)imap_get_uncached_messages_thread(data);
2008 debug_print("+++waiting for imap_get_uncached_messages_thread...\n");
2009 while(!data->done) {
2010 /* don't let the interface freeze while waiting */
2013 debug_print("---imap_get_uncached_messages_thread done\n");
2015 /* get the thread's return value and clean its resources */
2016 pthread_join(pt, (void *)&result);
2018 result = (GSList *)imap_get_uncached_messages_thread(data);
2024 static void imap_delete_all_cached_messages(FolderItem *item)
2028 g_return_if_fail(item != NULL);
2029 g_return_if_fail(item->folder != NULL);
2030 g_return_if_fail(FOLDER_CLASS(item->folder) == &imap_class);
2032 debug_print("Deleting all cached messages...\n");
2034 dir = folder_item_get_path(item);
2035 if (is_dir_exist(dir))
2036 remove_all_numbered_files(dir);
2039 debug_print("done.\n");
2043 static SockInfo *imap_open_tunnel(const gchar *server,
2044 const gchar *tunnelcmd,
2047 static SockInfo *imap_open_tunnel(const gchar *server,
2048 const gchar *tunnelcmd)
2053 if ((sock = sock_connect_cmd(server, tunnelcmd)) == NULL) {
2054 log_warning(_("Can't establish IMAP4 session with: %s\n"),
2059 return imap_init_sock(sock, ssl_type);
2061 return imap_init_sock(sock);
2067 void *imap_open_thread(void *data)
2069 SockInfo *sock = NULL;
2070 thread_data *td = (thread_data *)data;
2071 if ((sock = sock_connect(td->server, td->port)) == NULL) {
2072 log_warning(_("Can't connect to IMAP4 server: %s:%d\n"),
2073 td->server, td->port);
2079 if (td->ssl_type == SSL_TUNNEL && !ssl_init_socket(sock)) {
2080 log_warning(_("Can't establish IMAP4 session with: %s:%d\n"),
2081 td->server, td->port);
2094 static SockInfo *imap_open_blocking(const gchar *server, gushort port,
2097 static SockInfo *imap_open_blocking(const gchar *server, gushort port)
2101 if ((sock = sock_connect(server, port)) == NULL) {
2102 log_warning(_("Can't connect to IMAP4 server: %s:%d\n"),
2108 if (ssl_type == SSL_TUNNEL && !ssl_init_socket(sock)) {
2109 log_warning(_("Can't establish IMAP4 session with: %s:%d\n"),
2120 static SockInfo *imap_open(const gchar *server, gushort port,
2123 static SockInfo *imap_open(const gchar *server, gushort port)
2126 #if (defined USE_PTHREAD && defined __GLIBC__ && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 3)))
2127 /* non blocking stuff */
2128 thread_data *td = g_new0(thread_data, 1);
2130 SockInfo *sock = NULL;
2133 td->ssl_type = ssl_type;
2135 td->server = g_strdup(server);
2139 statusbar_print_all(_("Connecting to IMAP4 server: %s..."), server);
2141 if (pthread_create(&pt, PTHREAD_CREATE_JOINABLE,
2142 imap_open_thread, td) != 0) {
2143 statusbar_pop_all();
2147 return imap_open_blocking(server, port, ssl_type);
2149 return imap_open_blocking(server, port);
2153 debug_print("+++waiting for imap_open_thread...\n");
2155 /* don't let the interface freeze while waiting */
2159 /* get the thread's return value and clean its resources */
2160 pthread_join(pt, (void *)&sock);
2164 debug_print("---imap_open_thread returned %p\n", sock);
2165 statusbar_pop_all();
2169 return imap_open_blocking(server, port, ssl_type);
2171 return imap_open_blocking(server, port);
2177 static SockInfo *imap_init_sock(SockInfo *sock, SSLType ssl_type)
2179 static SockInfo *imap_init_sock(SockInfo *sock)
2186 static GList *imap_parse_namespace_str(gchar *str)
2191 IMAPNameSpace *namespace;
2192 GList *ns_list = NULL;
2194 while (*p != '\0') {
2195 /* parse ("#foo" "/") */
2197 while (*p && *p != '(') p++;
2198 if (*p == '\0') break;
2201 while (*p && *p != '"') p++;
2202 if (*p == '\0') break;
2206 while (*p && *p != '"') p++;
2207 if (*p == '\0') break;
2211 while (*p && isspace(*p)) p++;
2212 if (*p == '\0') break;
2213 if (strncmp(p, "NIL", 3) == 0)
2215 else if (*p == '"') {
2218 while (*p && *p != '"') p++;
2219 if (*p == '\0') break;
2224 while (*p && *p != ')') p++;
2225 if (*p == '\0') break;
2228 namespace = g_new(IMAPNameSpace, 1);
2229 namespace->name = g_strdup(name);
2230 namespace->separator = separator ? separator[0] : '\0';
2231 ns_list = g_list_append(ns_list, namespace);
2237 static void imap_parse_namespace(IMAPSession *session, IMAPFolder *folder)
2242 g_return_if_fail(session != NULL);
2243 g_return_if_fail(folder != NULL);
2245 if (folder->ns_personal != NULL ||
2246 folder->ns_others != NULL ||
2247 folder->ns_shared != NULL)
2250 if (!imap_has_capability(session, "NAMESPACE")) {
2251 imap_get_namespace_by_list(session, folder);
2255 if (imap_cmd_namespace(session, &ns_str)
2257 log_warning(_("can't get namespace\n"));
2261 str_array = strsplit_parenthesis(ns_str, '(', ')', 3);
2262 if (str_array == NULL) {
2264 imap_get_namespace_by_list(session, folder);
2268 folder->ns_personal = imap_parse_namespace_str(str_array[0]);
2269 if (str_array[0] && str_array[1])
2270 folder->ns_others = imap_parse_namespace_str(str_array[1]);
2271 if (str_array[0] && str_array[1] && str_array[2])
2272 folder->ns_shared = imap_parse_namespace_str(str_array[2]);
2273 g_strfreev(str_array);
2277 static void imap_get_namespace_by_list(IMAPSession *session, IMAPFolder *folder)
2279 GSList *item_list, *cur;
2280 gchar separator = '\0';
2281 IMAPNameSpace *namespace;
2283 g_return_if_fail(session != NULL);
2284 g_return_if_fail(folder != NULL);
2286 if (folder->ns_personal != NULL ||
2287 folder->ns_others != NULL ||
2288 folder->ns_shared != NULL)
2291 imap_gen_send(session, "LIST \"\" \"\"");
2292 item_list = imap_parse_list(folder, session, "", &separator);
2293 for (cur = item_list; cur != NULL; cur = cur->next)
2294 folder_item_destroy(FOLDER_ITEM(cur->data));
2295 g_slist_free(item_list);
2297 namespace = g_new(IMAPNameSpace, 1);
2298 namespace->name = g_strdup("");
2299 namespace->separator = separator;
2300 folder->ns_personal = g_list_append(NULL, namespace);
2303 static IMAPNameSpace *imap_find_namespace_from_list(GList *ns_list,
2306 IMAPNameSpace *namespace = NULL;
2307 gchar *tmp_path, *name;
2309 if (!path) path = "";
2311 for (; ns_list != NULL; ns_list = ns_list->next) {
2312 IMAPNameSpace *tmp_ns = ns_list->data;
2314 Xstrcat_a(tmp_path, path, "/", return namespace);
2315 Xstrdup_a(name, tmp_ns->name, return namespace);
2316 if (tmp_ns->separator && tmp_ns->separator != '/') {
2317 subst_char(tmp_path, tmp_ns->separator, '/');
2318 subst_char(name, tmp_ns->separator, '/');
2320 if (strncmp(tmp_path, name, strlen(name)) == 0)
2327 static IMAPNameSpace *imap_find_namespace(IMAPFolder *folder,
2330 IMAPNameSpace *namespace;
2332 g_return_val_if_fail(folder != NULL, NULL);
2334 namespace = imap_find_namespace_from_list(folder->ns_personal, path);
2335 if (namespace) return namespace;
2336 namespace = imap_find_namespace_from_list(folder->ns_others, path);
2337 if (namespace) return namespace;
2338 namespace = imap_find_namespace_from_list(folder->ns_shared, path);
2339 if (namespace) return namespace;
2344 static gchar imap_get_path_separator(IMAPFolder *folder, const gchar *path)
2346 IMAPNameSpace *namespace;
2347 gchar separator = '/';
2349 namespace = imap_find_namespace(folder, path);
2350 if (namespace && namespace->separator)
2351 separator = namespace->separator;
2356 static gchar *imap_get_real_path(IMAPFolder *folder, const gchar *path)
2361 g_return_val_if_fail(folder != NULL, NULL);
2362 g_return_val_if_fail(path != NULL, NULL);
2364 real_path = imap_utf8_to_modified_utf7(path);
2365 separator = imap_get_path_separator(folder, path);
2366 imap_path_separator_subst(real_path, separator);
2371 static gchar *imap_parse_atom(SockInfo *sock, gchar *src,
2372 gchar *dest, gint dest_len, GString *str)
2374 gchar *cur_pos = src;
2377 g_return_val_if_fail(str != NULL, cur_pos);
2379 /* read the next line if the current response buffer is empty */
2380 while (isspace(*(guchar *)cur_pos)) cur_pos++;
2381 while (*cur_pos == '\0') {
2382 if ((nextline = imap_getline(sock)) == NULL)
2384 g_string_assign(str, nextline);
2386 strretchomp(nextline);
2387 /* log_print("IMAP4< %s\n", nextline); */
2388 debug_print("IMAP4< %s\n", nextline);
2391 while (isspace(*(guchar *)cur_pos)) cur_pos++;
2394 if (!strncmp(cur_pos, "NIL", 3)) {
2397 } else if (*cur_pos == '\"') {
2400 p = get_quoted(cur_pos, '\"', dest, dest_len);
2401 cur_pos = p ? p : cur_pos + 2;
2402 } else if (*cur_pos == '{') {
2407 cur_pos = strchr_cpy(cur_pos + 1, '}', buf, sizeof(buf));
2409 g_return_val_if_fail(len >= 0, cur_pos);
2411 g_string_truncate(str, 0);
2415 if ((nextline = imap_getline(sock)) == NULL)
2417 line_len += strlen(nextline);
2418 g_string_append(str, nextline);
2420 strretchomp(nextline);
2421 /* log_print("IMAP4< %s\n", nextline); */
2422 debug_print("IMAP4< %s\n", nextline);
2424 } while (line_len < len);
2426 memcpy(dest, cur_pos, MIN(len, dest_len - 1));
2427 dest[MIN(len, dest_len - 1)] = '\0';
2434 static gchar *imap_get_header(SockInfo *sock, gchar *cur_pos, gchar **headers,
2444 g_return_val_if_fail(str != NULL, cur_pos);
2446 while (isspace(*(guchar *)cur_pos)) cur_pos++;
2448 g_return_val_if_fail(*cur_pos == '{', cur_pos);
2450 cur_pos = strchr_cpy(cur_pos + 1, '}', buf, sizeof(buf));
2452 g_return_val_if_fail(len >= 0, cur_pos);
2454 g_string_truncate(str, 0);
2458 if ((nextline = sock_getline(sock)) == NULL)
2460 block_len += strlen(nextline);
2461 g_string_append(str, nextline);
2463 strretchomp(nextline);
2464 /* debug_print("IMAP4< %s\n", nextline); */
2466 } while (block_len < len);
2468 debug_print("IMAP4< [contents of RFC822.HEADER]\n");
2470 *headers = g_strndup(cur_pos, len);
2473 while (isspace(*(guchar *)cur_pos)) cur_pos++;
2474 while (*cur_pos == '\0') {
2475 if ((nextline = sock_getline(sock)) == NULL)
2477 g_string_assign(str, nextline);
2479 strretchomp(nextline);
2480 debug_print("IMAP4< %s\n", nextline);
2483 while (isspace(*(guchar *)cur_pos)) cur_pos++;
2489 static MsgFlags imap_parse_flags(const gchar *flag_str)
2491 const gchar *p = flag_str;
2492 MsgFlags flags = {0, 0};
2494 flags.perm_flags = MSG_UNREAD;
2496 while ((p = strchr(p, '\\')) != NULL) {
2499 if (g_ascii_strncasecmp(p, "Recent", 6) == 0 && MSG_IS_UNREAD(flags)) {
2500 MSG_SET_PERM_FLAGS(flags, MSG_NEW);
2501 } else if (g_ascii_strncasecmp(p, "Seen", 4) == 0) {
2502 MSG_UNSET_PERM_FLAGS(flags, MSG_NEW|MSG_UNREAD);
2503 } else if (g_ascii_strncasecmp(p, "Deleted", 7) == 0) {
2504 MSG_SET_PERM_FLAGS(flags, MSG_DELETED);
2505 } else if (g_ascii_strncasecmp(p, "Flagged", 7) == 0) {
2506 MSG_SET_PERM_FLAGS(flags, MSG_MARKED);
2507 } else if (g_ascii_strncasecmp(p, "Answered", 8) == 0) {
2508 MSG_SET_PERM_FLAGS(flags, MSG_REPLIED);
2515 static MsgInfo *imap_parse_envelope(SockInfo *sock, FolderItem *item,
2518 gchar buf[IMAPBUFSIZE];
2519 MsgInfo *msginfo = NULL;
2524 MsgFlags flags = {0, 0}, imap_flags = {0, 0};
2526 g_return_val_if_fail(line_str != NULL, NULL);
2527 g_return_val_if_fail(line_str->str[0] == '*' &&
2528 line_str->str[1] == ' ', NULL);
2530 MSG_SET_TMP_FLAGS(flags, MSG_IMAP);
2531 if (item->stype == F_QUEUE) {
2532 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
2533 } else if (item->stype == F_DRAFT) {
2534 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
2537 cur_pos = line_str->str + 2;
2539 #define PARSE_ONE_ELEMENT(ch) \
2541 cur_pos = strchr_cpy(cur_pos, ch, buf, sizeof(buf)); \
2542 if (cur_pos == NULL) { \
2543 g_warning("cur_pos == NULL\n"); \
2544 procmsg_msginfo_free(msginfo); \
2549 PARSE_ONE_ELEMENT(' ');
2552 PARSE_ONE_ELEMENT(' ');
2553 g_return_val_if_fail(!strcmp(buf, "FETCH"), NULL);
2555 g_return_val_if_fail(*cur_pos == '(', NULL);
2558 while (*cur_pos != '\0' && *cur_pos != ')') {
2559 while (*cur_pos == ' ') cur_pos++;
2561 if (!strncmp(cur_pos, "UID ", 4)) {
2563 uid = strtoul(cur_pos, &cur_pos, 10);
2564 } else if (!strncmp(cur_pos, "FLAGS ", 6)) {
2566 if (*cur_pos != '(') {
2567 g_warning("*cur_pos != '('\n");
2568 procmsg_msginfo_free(msginfo);
2572 PARSE_ONE_ELEMENT(')');
2573 imap_flags = imap_parse_flags(buf);
2574 } else if (!strncmp(cur_pos, "RFC822.SIZE ", 12)) {
2576 size = strtol(cur_pos, &cur_pos, 10);
2577 } else if (!strncmp(cur_pos, "RFC822.HEADER ", 14)) {
2581 cur_pos = imap_get_header(sock, cur_pos, &headers,
2583 msginfo = procheader_parse_str(headers, flags, FALSE, FALSE);
2586 g_warning("invalid FETCH response: %s\n", cur_pos);
2592 msginfo->msgnum = uid;
2593 msginfo->size = size;
2594 msginfo->flags.tmp_flags |= imap_flags.tmp_flags;
2595 msginfo->flags.perm_flags = imap_flags.perm_flags;
2601 static gchar *imap_get_flag_str(IMAPFlags flags)
2606 str = g_string_new(NULL);
2608 if (IMAP_IS_SEEN(flags)) g_string_append(str, "\\Seen ");
2609 if (IMAP_IS_ANSWERED(flags)) g_string_append(str, "\\Answered ");
2610 if (IMAP_IS_FLAGGED(flags)) g_string_append(str, "\\Flagged ");
2611 if (IMAP_IS_DELETED(flags)) g_string_append(str, "\\Deleted ");
2612 if (IMAP_IS_DRAFT(flags)) g_string_append(str, "\\Draft");
2614 if (str->len > 0 && str->str[str->len - 1] == ' ')
2615 g_string_truncate(str, str->len - 1);
2618 g_string_free(str, FALSE);
2623 static gint imap_set_message_flags(IMAPSession *session,
2624 MsgNumberList *numlist,
2634 flag_str = imap_get_flag_str(flags);
2635 cmd = g_strconcat(is_set ? "+FLAGS.SILENT (" : "-FLAGS.SILENT (",
2636 flag_str, ")", NULL);
2639 seq_list = imap_get_seq_set_from_numlist(numlist);
2640 imapset = get_seq_set_from_seq_list(seq_list);
2642 ok = imap_cmd_store(session, imapset, cmd);
2645 imap_seq_set_free(seq_list);
2651 typedef struct _select_data {
2652 IMAPSession *session;
2657 guint32 *uid_validity;
2661 static void *imap_select_thread(void *data)
2663 select_data *stuff = (select_data *)data;
2664 IMAPSession *session = stuff->session;
2665 gchar *real_path = stuff->real_path;
2666 gint *exists = stuff->exists;
2667 gint *recent = stuff->recent;
2668 gint *unseen = stuff->unseen;
2669 guint32 *uid_validity = stuff->uid_validity;
2672 ok = imap_cmd_select(session, real_path,
2673 exists, recent, unseen, uid_validity, TRUE);
2675 return GINT_TO_POINTER(ok);
2678 static gint imap_select(IMAPSession *session, IMAPFolder *folder,
2680 gint *exists, gint *recent, gint *unseen,
2681 guint32 *uid_validity, gboolean block)
2685 gint exists_, recent_, unseen_;
2686 guint32 uid_validity_;
2688 if (!exists || !recent || !unseen || !uid_validity) {
2689 if (session->mbox && strcmp(session->mbox, path) == 0)
2690 return IMAP_SUCCESS;
2694 uid_validity = &uid_validity_;
2697 g_free(session->mbox);
2698 session->mbox = NULL;
2700 real_path = imap_get_real_path(folder, path);
2702 #if (defined USE_PTHREAD && defined __GLIBC__ && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 3)))
2703 if (block == FALSE) {
2704 select_data *data = g_new0(select_data, 1);
2707 data->session = session;
2708 data->real_path = real_path;
2709 data->exists = exists;
2710 data->recent = recent;
2711 data->unseen = unseen;
2712 data->uid_validity = uid_validity;
2715 if (pthread_create(&pt, PTHREAD_CREATE_JOINABLE,
2716 imap_select_thread, data) != 0) {
2717 ok = GPOINTER_TO_INT(imap_select_thread(data));
2720 debug_print("+++waiting for imap_select_thread...\n");
2721 while(!data->done) {
2722 /* don't let the interface freeze while waiting */
2725 debug_print("---imap_select_thread done\n");
2727 /* get the thread's return value and clean its resources */
2728 pthread_join(pt, &tmp);
2729 ok = GPOINTER_TO_INT(tmp);
2733 ok = imap_cmd_select(session, real_path,
2734 exists, recent, unseen, uid_validity, block);
2737 ok = imap_cmd_select(session, real_path,
2738 exists, recent, unseen, uid_validity, block);
2740 if (ok != IMAP_SUCCESS)
2741 log_warning(_("can't select folder: %s\n"), real_path);
2743 session->mbox = g_strdup(path);
2744 session->folder_content_changed = FALSE;
2751 #define THROW(err) { ok = err; goto catch; }
2753 static gint imap_status(IMAPSession *session, IMAPFolder *folder,
2755 gint *messages, gint *recent,
2756 guint32 *uid_next, guint32 *uid_validity,
2757 gint *unseen, gboolean block)
2762 GPtrArray *argbuf = NULL;
2765 if (messages && recent && uid_next && uid_validity && unseen) {
2766 *messages = *recent = *uid_next = *uid_validity = *unseen = 0;
2767 argbuf = g_ptr_array_new();
2770 real_path = imap_get_real_path(folder, path);
2771 QUOTE_IF_REQUIRED(real_path_, real_path);
2772 imap_gen_send(session, "STATUS %s "
2773 "(MESSAGES RECENT UIDNEXT UIDVALIDITY UNSEEN)",
2776 ok = imap_cmd_ok_with_block(session, argbuf, block);
2777 if (ok != IMAP_SUCCESS || !argbuf) THROW(ok);
2779 str = search_array_str(argbuf, "STATUS");
2780 if (!str) THROW(IMAP_ERROR);
2782 str = strchr(str, '(');
2783 if (!str) THROW(IMAP_ERROR);
2785 while (*str != '\0' && *str != ')') {
2786 while (*str == ' ') str++;
2788 if (!strncmp(str, "MESSAGES ", 9)) {
2790 *messages = strtol(str, &str, 10);
2791 } else if (!strncmp(str, "RECENT ", 7)) {
2793 *recent = strtol(str, &str, 10);
2794 } else if (!strncmp(str, "UIDNEXT ", 8)) {
2796 *uid_next = strtoul(str, &str, 10);
2797 } else if (!strncmp(str, "UIDVALIDITY ", 12)) {
2799 *uid_validity = strtoul(str, &str, 10);
2800 } else if (!strncmp(str, "UNSEEN ", 7)) {
2802 *unseen = strtol(str, &str, 10);
2804 g_warning("invalid STATUS response: %s\n", str);
2812 ptr_array_free_strings(argbuf);
2813 g_ptr_array_free(argbuf, TRUE);
2821 static gboolean imap_has_capability(IMAPSession *session, const gchar *cap)
2825 for (p = session->capability; *p != NULL; ++p) {
2826 if (!g_ascii_strcasecmp(*p, cap))
2833 static void imap_free_capabilities(IMAPSession *session)
2835 g_strfreev(session->capability);
2836 session->capability = NULL;
2839 /* low-level IMAP4rev1 commands */
2841 static gint imap_cmd_authenticate(IMAPSession *session, const gchar *user,
2842 const gchar *pass, IMAPAuthType type)
2849 gchar hexdigest[33];
2853 auth_type = "CRAM-MD5";
2855 imap_gen_send(session, "AUTHENTICATE %s", auth_type);
2856 ok = imap_gen_recv(session, &buf);
2857 if (ok != IMAP_SUCCESS || buf[0] != '+' || buf[1] != ' ') {
2862 challenge = g_malloc(strlen(buf + 2) + 1);
2863 challenge_len = base64_decode(challenge, buf + 2, -1);
2864 challenge[challenge_len] = '\0';
2866 log_print("IMAP< [Decoded: %s]\n", challenge);
2868 md5_hex_hmac(hexdigest, challenge, challenge_len, pass, strlen(pass));
2871 response = g_strdup_printf("%s %s", user, hexdigest);
2872 log_print("IMAP> [Encoded: %s]\n", response);
2873 response64 = g_malloc((strlen(response) + 3) * 2 + 1);
2874 base64_encode(response64, response, strlen(response));
2877 log_print("IMAP> %s\n", response64);
2878 sock_puts(SESSION(session)->sock, response64);
2879 ok = imap_cmd_ok(session, NULL);
2880 if (ok != IMAP_SUCCESS)
2881 log_warning(_("IMAP4 authentication failed.\n"));
2886 static gint imap_cmd_login(IMAPSession *session,
2887 const gchar *user, const gchar *pass)
2889 gchar *user_, *pass_;
2892 QUOTE_IF_REQUIRED(user_, user);
2893 QUOTE_IF_REQUIRED(pass_, pass);
2894 imap_gen_send(session, "LOGIN %s %s", user_, pass_);
2896 ok = imap_cmd_ok(session, NULL);
2897 if (ok != IMAP_SUCCESS)
2898 log_warning(_("IMAP4 login failed.\n"));
2903 static gint imap_cmd_logout(IMAPSession *session)
2905 imap_gen_send(session, "LOGOUT");
2906 return imap_cmd_ok(session, NULL);
2909 static gint imap_cmd_noop(IMAPSession *session)
2911 imap_gen_send(session, "NOOP");
2912 return imap_cmd_ok(session, NULL);
2916 static gint imap_cmd_starttls(IMAPSession *session)
2918 imap_gen_send(session, "STARTTLS");
2919 return imap_cmd_ok(session, NULL);
2923 #define THROW(err) { ok = err; goto catch; }
2925 static gint imap_cmd_namespace(IMAPSession *session, gchar **ns_str)
2931 argbuf = g_ptr_array_new();
2933 imap_gen_send(session, "NAMESPACE");
2934 if ((ok = imap_cmd_ok(session, argbuf)) != IMAP_SUCCESS) THROW(ok);
2936 str = search_array_str(argbuf, "NAMESPACE");
2937 if (!str) THROW(IMAP_ERROR);
2939 *ns_str = g_strdup(str);
2942 ptr_array_free_strings(argbuf);
2943 g_ptr_array_free(argbuf, TRUE);
2950 static gint imap_cmd_list(IMAPSession *session, const gchar *ref,
2951 const gchar *mailbox, GPtrArray *argbuf)
2953 gchar *ref_, *mailbox_;
2955 if (!ref) ref = "\"\"";
2956 if (!mailbox) mailbox = "\"\"";
2958 QUOTE_IF_REQUIRED(ref_, ref);
2959 QUOTE_IF_REQUIRED(mailbox_, mailbox);
2960 imap_gen_send(session, "LIST %s %s", ref_, mailbox_);
2962 return imap_cmd_ok(session, argbuf);
2965 #define THROW goto catch
2967 static gint imap_cmd_do_select(IMAPSession *session, const gchar *folder,
2969 gint *exists, gint *recent, gint *unseen,
2970 guint32 *uid_validity, gboolean block)
2977 unsigned int uid_validity_;
2979 *exists = *recent = *unseen = *uid_validity = 0;
2980 argbuf = g_ptr_array_new();
2983 select_cmd = "EXAMINE";
2985 select_cmd = "SELECT";
2987 QUOTE_IF_REQUIRED(folder_, folder);
2988 imap_gen_send(session, "%s %s", select_cmd, folder_);
2990 if ((ok = imap_cmd_ok_with_block(session, argbuf, block)) != IMAP_SUCCESS) THROW;
2992 resp_str = search_array_contain_str(argbuf, "EXISTS");
2994 if (sscanf(resp_str,"%d EXISTS", exists) != 1) {
2995 g_warning("imap_cmd_select(): invalid EXISTS line.\n");
3000 resp_str = search_array_contain_str(argbuf, "RECENT");
3002 if (sscanf(resp_str, "%d RECENT", recent) != 1) {
3003 g_warning("imap_cmd_select(): invalid RECENT line.\n");
3008 resp_str = search_array_contain_str(argbuf, "UIDVALIDITY");
3010 if (sscanf(resp_str, "OK [UIDVALIDITY %u] ", &uid_validity_)
3012 g_warning("imap_cmd_select(): invalid UIDVALIDITY line.\n");
3015 *uid_validity = uid_validity_;
3018 resp_str = search_array_contain_str(argbuf, "UNSEEN");
3020 if (sscanf(resp_str, "OK [UNSEEN %d] ", unseen) != 1) {
3021 g_warning("imap_cmd_select(): invalid UNSEEN line.\n");
3027 ptr_array_free_strings(argbuf);
3028 g_ptr_array_free(argbuf, TRUE);
3033 static gint imap_cmd_select(IMAPSession *session, const gchar *folder,
3034 gint *exists, gint *recent, gint *unseen,
3035 guint32 *uid_validity, gboolean block)
3037 return imap_cmd_do_select(session, folder, FALSE,
3038 exists, recent, unseen, uid_validity, block);
3041 static gint imap_cmd_examine(IMAPSession *session, const gchar *folder,
3042 gint *exists, gint *recent, gint *unseen,
3043 guint32 *uid_validity, gboolean block)
3045 return imap_cmd_do_select(session, folder, TRUE,
3046 exists, recent, unseen, uid_validity, block);
3051 static gint imap_cmd_create(IMAPSession *session, const gchar *folder)
3055 QUOTE_IF_REQUIRED(folder_, folder);
3056 imap_gen_send(session, "CREATE %s", folder_);
3058 return imap_cmd_ok(session, NULL);
3061 static gint imap_cmd_rename(IMAPSession *session, const gchar *old_folder,
3062 const gchar *new_folder)
3064 gchar *old_folder_, *new_folder_;
3066 QUOTE_IF_REQUIRED(old_folder_, old_folder);
3067 QUOTE_IF_REQUIRED(new_folder_, new_folder);
3068 imap_gen_send(session, "RENAME %s %s", old_folder_, new_folder_);
3070 return imap_cmd_ok(session, NULL);
3073 static gint imap_cmd_delete(IMAPSession *session, const gchar *folder)
3077 QUOTE_IF_REQUIRED(folder_, folder);
3078 imap_gen_send(session, "DELETE %s", folder_);
3080 return imap_cmd_ok(session, NULL);
3083 static gint imap_cmd_search(IMAPSession *session, const gchar *criteria,
3084 GSList **list, gboolean block)
3090 g_return_val_if_fail(criteria != NULL, IMAP_ERROR);
3091 g_return_val_if_fail(list != NULL, IMAP_ERROR);
3095 argbuf = g_ptr_array_new();
3096 imap_gen_send(session, "UID SEARCH %s", criteria);
3098 ok = imap_cmd_ok_with_block(session, argbuf, block);
3099 if (ok != IMAP_SUCCESS) {
3100 ptr_array_free_strings(argbuf);
3101 g_ptr_array_free(argbuf, TRUE);
3105 if ((uidlist = search_array_str(argbuf, "SEARCH ")) != NULL) {
3106 gchar **strlist, **p;
3108 strlist = g_strsplit(uidlist + 7, " ", 0);
3109 for (p = strlist; *p != NULL; ++p) {
3112 if (sscanf(*p, "%u", &msgnum) == 1)
3113 *list = g_slist_append(*list, GINT_TO_POINTER(msgnum));
3115 g_strfreev(strlist);
3117 ptr_array_free_strings(argbuf);
3118 g_ptr_array_free(argbuf, TRUE);
3120 return IMAP_SUCCESS;
3123 typedef struct _fetch_data {
3124 IMAPSession *session;
3126 const gchar *filename;
3130 static void *imap_cmd_fetch_thread(void *data)
3132 fetch_data *stuff = (fetch_data *)data;
3133 IMAPSession *session = stuff->session;
3134 guint32 uid = stuff->uid;
3135 const gchar *filename = stuff->filename;
3143 if (filename == NULL) {
3145 return GINT_TO_POINTER(IMAP_ERROR);
3148 imap_gen_send(session, "UID FETCH %d BODY.PEEK[]", uid);
3150 while ((ok = imap_gen_recv_block(session, &buf)) == IMAP_SUCCESS) {
3151 if (buf[0] != '*' || buf[1] != ' ') {
3154 return GINT_TO_POINTER(IMAP_ERROR);
3156 if (strstr(buf, "FETCH") != NULL) break;
3159 if (ok != IMAP_SUCCESS) {
3162 return GINT_TO_POINTER(ok);
3165 #define RETURN_ERROR_IF_FAIL(cond) \
3168 stuff->done = TRUE; \
3169 return GINT_TO_POINTER(IMAP_ERROR); \
3172 cur_pos = strchr(buf, '{');
3173 RETURN_ERROR_IF_FAIL(cur_pos != NULL);
3174 cur_pos = strchr_cpy(cur_pos + 1, '}', size_str, sizeof(size_str));
3175 RETURN_ERROR_IF_FAIL(cur_pos != NULL);
3176 size_num = atol(size_str);
3177 RETURN_ERROR_IF_FAIL(size_num >= 0);
3179 RETURN_ERROR_IF_FAIL(*cur_pos == '\0');
3181 #undef RETURN_ERROR_IF_FAIL
3185 if (recv_bytes_write_to_file(SESSION(session)->sock,
3186 size_num, filename) != 0) {
3188 return GINT_TO_POINTER(IMAP_ERROR);
3190 if (imap_gen_recv_block(session, &buf) != IMAP_SUCCESS) {
3193 return GINT_TO_POINTER(IMAP_ERROR);
3196 if (buf[0] == '\0' || buf[strlen(buf) - 1] != ')') {
3199 return GINT_TO_POINTER(IMAP_ERROR);
3203 ok = imap_cmd_ok_block(session, NULL);
3206 return GINT_TO_POINTER(ok);
3209 static gint imap_cmd_fetch(IMAPSession *session, guint32 uid,
3210 const gchar *filename)
3212 fetch_data *data = g_new0(fetch_data, 1);
3219 data->session = session;
3221 data->filename = filename;
3223 if (prefs_common.work_offline && !imap_gtk_should_override()) {
3228 #if (defined USE_PTHREAD && defined __GLIBC__ && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 3)))
3229 if (pthread_create(&pt, PTHREAD_CREATE_JOINABLE,
3230 imap_cmd_fetch_thread, data) != 0) {
3231 result = GPOINTER_TO_INT(imap_cmd_fetch_thread(data));
3235 debug_print("+++waiting for imap_cmd_fetch_thread...\n");
3236 while(!data->done) {
3237 /* don't let the interface freeze while waiting */
3240 debug_print("---imap_cmd_fetch_thread done\n");
3242 /* get the thread's return value and clean its resources */
3243 pthread_join(pt, &tmp);
3244 result = GPOINTER_TO_INT(tmp);
3246 result = GPOINTER_TO_INT(imap_cmd_fetch_thread(data));
3252 static gint imap_cmd_append(IMAPSession *session, const gchar *destfolder,
3253 const gchar *file, IMAPFlags flags,
3260 unsigned int new_uid_;
3262 gchar buf[BUFFSIZE];
3267 g_return_val_if_fail(file != NULL, IMAP_ERROR);
3269 size = get_file_size_as_crlf(file);
3270 if ((fp = fopen(file, "rb")) == NULL) {
3271 FILE_OP_ERROR(file, "fopen");
3274 QUOTE_IF_REQUIRED(destfolder_, destfolder);
3275 flag_str = imap_get_flag_str(flags);
3276 imap_gen_send(session, "APPEND %s (%s) {%d}",
3277 destfolder_, flag_str, size);
3280 ok = imap_gen_recv(session, &ret);
3281 if (ok != IMAP_SUCCESS || ret[0] != '+' || ret[1] != ' ') {
3282 log_warning(_("can't append %s to %s\n"), file, destfolder_);
3289 log_print("IMAP4> %s\n", "(sending file...)");
3291 while (fgets(buf, sizeof(buf), fp) != NULL) {
3293 if (sock_puts(SESSION(session)->sock, buf) < 0) {
3300 FILE_OP_ERROR(file, "fgets");
3305 sock_puts(SESSION(session)->sock, "");
3309 if (new_uid != NULL)
3312 if (new_uid != NULL && session->uidplus) {
3313 argbuf = g_ptr_array_new();
3315 ok = imap_cmd_ok(session, argbuf);
3316 if ((ok == IMAP_SUCCESS) && (argbuf->len > 0)) {
3317 resp_str = g_ptr_array_index(argbuf, argbuf->len - 1);
3319 sscanf(resp_str, "%*u OK [APPENDUID %*u %u]",
3321 *new_uid = new_uid_;
3325 ptr_array_free_strings(argbuf);
3326 g_ptr_array_free(argbuf, TRUE);
3328 ok = imap_cmd_ok(session, NULL);
3330 if (ok != IMAP_SUCCESS)
3331 log_warning(_("can't append message to %s\n"),
3337 static MsgNumberList *imapset_to_numlist(IMAPSet imapset)
3339 gchar **ranges, **range;
3340 unsigned int low, high;
3341 MsgNumberList *uids = NULL;
3343 ranges = g_strsplit(imapset, ",", 0);
3344 for (range = ranges; *range != NULL; range++) {
3345 if (sscanf(*range, "%u:%u", &low, &high) == 1)
3346 uids = g_slist_prepend(uids, GINT_TO_POINTER(low));
3349 for (i = low; i <= high; i++)
3350 uids = g_slist_prepend(uids, GINT_TO_POINTER(i));
3353 uids = g_slist_reverse(uids);
3359 static gint imap_cmd_copy(IMAPSession *session, const gchar *seq_set,
3360 const gchar *destfolder, GRelation *uid_mapping)
3365 g_return_val_if_fail(session != NULL, IMAP_ERROR);
3366 g_return_val_if_fail(seq_set != NULL, IMAP_ERROR);
3367 g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
3369 QUOTE_IF_REQUIRED(destfolder_, destfolder);
3370 imap_gen_send(session, "UID COPY %s %s", seq_set, destfolder_);
3372 if (uid_mapping != NULL && session->uidplus) {
3374 gchar *resp_str = NULL, *olduids_str, *newuids_str;
3375 MsgNumberList *olduids, *old_cur, *newuids, *new_cur;
3377 reply = g_ptr_array_new();
3378 ok = imap_cmd_ok(session, reply);
3379 if ((ok == IMAP_SUCCESS) && (reply->len > 0)) {
3380 resp_str = g_ptr_array_index(reply, reply->len - 1);
3382 olduids_str = g_new0(gchar, strlen(resp_str));
3383 newuids_str = g_new0(gchar, strlen(resp_str));
3384 if (sscanf(resp_str, "%*s OK [COPYUID %*u %[0-9,:] %[0-9,:]]",
3385 olduids_str, newuids_str) == 2) {
3386 olduids = imapset_to_numlist(olduids_str);
3387 newuids = imapset_to_numlist(newuids_str);
3391 while(old_cur != NULL && new_cur != NULL) {
3392 g_relation_insert(uid_mapping,
3393 GPOINTER_TO_INT(old_cur->data),
3394 GPOINTER_TO_INT(new_cur->data));
3395 old_cur = g_slist_next(old_cur);
3396 new_cur = g_slist_next(new_cur);
3399 g_slist_free(olduids);
3400 g_slist_free(newuids);
3402 g_free(olduids_str);
3403 g_free(newuids_str);
3406 ptr_array_free_strings(reply);
3407 g_ptr_array_free(reply, TRUE);
3409 ok = imap_cmd_ok(session, NULL);
3411 if (ok != IMAP_SUCCESS)
3412 log_warning(_("can't copy %s to %s\n"), seq_set, destfolder_);
3417 gint imap_cmd_envelope(IMAPSession *session, IMAPSet set)
3420 (session, "UID FETCH %s (UID FLAGS RFC822.SIZE RFC822.HEADER)",
3423 return IMAP_SUCCESS;
3426 static gint imap_cmd_store(IMAPSession *session, IMAPSet seq_set,
3431 imap_gen_send(session, "UID STORE %s %s", seq_set, sub_cmd);
3433 if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS) {
3434 log_warning(_("error while imap command: STORE %s %s\n"),
3439 return IMAP_SUCCESS;
3442 static gint imap_cmd_expunge(IMAPSession *session, IMAPSet seq_set)
3446 if (seq_set && session->uidplus)
3447 imap_gen_send(session, "UID EXPUNGE %s", seq_set);
3449 imap_gen_send(session, "EXPUNGE");
3450 if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS) {
3451 log_warning(_("error while imap command: EXPUNGE\n"));
3455 return IMAP_SUCCESS;
3458 static gint imap_cmd_close(IMAPSession *session)
3462 imap_gen_send(session, "CLOSE");
3463 if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS)
3464 log_warning(_("error while imap command: CLOSE\n"));
3469 static gint imap_cmd_ok_with_block(IMAPSession *session, GPtrArray *argbuf, gboolean block)
3471 gint ok = IMAP_SUCCESS;
3476 while ((ok = imap_gen_recv_with_block(session, &buf, block))
3478 /* make sure data is long enough for any substring of buf */
3479 data = alloca(strlen(buf) + 1);
3481 /* untagged line read */
3482 if (buf[0] == '*' && buf[1] == ' ') {
3485 g_ptr_array_add(argbuf, g_strdup(buf + 2));
3487 if (sscanf(buf + 2, "%d %s", &num, data) >= 2) {
3488 if (!strcmp(data, "EXISTS")) {
3489 session->exists = num;
3490 session->folder_content_changed = TRUE;
3493 if(!strcmp(data, "EXPUNGE")) {
3495 session->folder_content_changed = TRUE;
3498 /* tagged line with correct tag and OK response found */
3499 } else if ((sscanf(buf, "%d %s", &cmd_num, data) >= 2) &&
3500 (cmd_num == session->cmd_count) &&
3501 !strcmp(data, "OK")) {
3503 g_ptr_array_add(argbuf, g_strdup(buf));
3505 /* everything else */
3516 static gint imap_cmd_ok(IMAPSession *session, GPtrArray *argbuf)
3518 return imap_cmd_ok_with_block(session, argbuf, FALSE);
3520 static gint imap_cmd_ok_block(IMAPSession *session, GPtrArray *argbuf)
3522 return imap_cmd_ok_with_block(session, argbuf, TRUE);
3524 static void imap_gen_send(IMAPSession *session, const gchar *format, ...)
3531 va_start(args, format);
3532 tmp = g_strdup_vprintf(format, args);
3535 session->cmd_count++;
3537 buf = g_strdup_printf("%d %s\r\n", session->cmd_count, tmp);
3538 if (!g_ascii_strncasecmp(tmp, "LOGIN ", 6) && (p = strchr(tmp + 6, ' '))) {
3540 log_print("IMAP4> %d %s ********\n", session->cmd_count, tmp);
3542 log_print("IMAP4> %d %s\n", session->cmd_count, tmp);
3544 sock_write_all(SESSION(session)->sock, buf, strlen(buf));
3549 static gint imap_gen_recv_with_block(IMAPSession *session, gchar **ret, gboolean block)
3552 if ((*ret = imap_getline(SESSION(session)->sock)) == NULL)
3555 if ((*ret = sock_getline(SESSION(session)->sock)) == NULL)
3560 log_print("IMAP4< %s\n", *ret);
3562 session_set_access_time(SESSION(session));
3564 return IMAP_SUCCESS;
3567 static gint imap_gen_recv_block(IMAPSession *session, gchar **ret)
3569 return imap_gen_recv_with_block(session, ret, TRUE);
3572 static gint imap_gen_recv(IMAPSession *session, gchar **ret)
3574 return imap_gen_recv_with_block(session, ret, FALSE);
3576 /* misc utility functions */
3578 static gchar *strchr_cpy(const gchar *src, gchar ch, gchar *dest, gint len)
3583 tmp = strchr(src, ch);
3587 memcpy(dest, src, MIN(tmp - src, len - 1));
3588 dest[MIN(tmp - src, len - 1)] = '\0';
3593 static gchar *get_quoted(const gchar *src, gchar ch, gchar *dest, gint len)
3595 const gchar *p = src;
3598 g_return_val_if_fail(*p == ch, NULL);
3603 while (*p != '\0' && *p != ch) {
3605 if (*p == '\\' && *(p + 1) != '\0')
3614 return (gchar *)(*p == ch ? p + 1 : p);
3617 static gchar *search_array_contain_str(GPtrArray *array, const gchar *str)
3621 for (i = 0; i < array->len; i++) {
3624 tmp = g_ptr_array_index(array, i);
3625 if (strstr(tmp, str) != NULL)
3632 static gchar *search_array_str(GPtrArray *array, const gchar *str)
3639 for (i = 0; i < array->len; i++) {
3642 tmp = g_ptr_array_index(array, i);
3643 if (!strncmp(tmp, str, len))
3650 static void imap_path_separator_subst(gchar *str, gchar separator)
3653 gboolean in_escape = FALSE;
3655 if (!separator || separator == '/') return;
3657 for (p = str; *p != '\0'; p++) {
3658 if (*p == '/' && !in_escape)
3660 else if (*p == '&' && *(p + 1) != '-' && !in_escape)
3662 else if (*p == '-' && in_escape)
3667 static gchar *imap_modified_utf7_to_utf8(const gchar *mutf7_str)
3669 static iconv_t cd = (iconv_t)-1;
3670 static gboolean iconv_ok = TRUE;
3673 size_t norm_utf7_len;
3675 gchar *to_str, *to_p;
3677 gboolean in_escape = FALSE;
3679 if (!iconv_ok) return g_strdup(mutf7_str);
3681 if (cd == (iconv_t)-1) {
3682 cd = iconv_open(CS_INTERNAL, CS_UTF_7);
3683 if (cd == (iconv_t)-1) {
3684 g_warning("iconv cannot convert UTF-7 to %s\n",
3687 return g_strdup(mutf7_str);
3691 /* modified UTF-7 to normal UTF-7 conversion */
3692 norm_utf7 = g_string_new(NULL);
3694 for (p = mutf7_str; *p != '\0'; p++) {
3695 /* replace: '&' -> '+',
3697 escaped ',' -> '/' */
3698 if (!in_escape && *p == '&') {
3699 if (*(p + 1) != '-') {
3700 g_string_append_c(norm_utf7, '+');
3703 g_string_append_c(norm_utf7, '&');
3706 } else if (in_escape && *p == ',') {
3707 g_string_append_c(norm_utf7, '/');
3708 } else if (in_escape && *p == '-') {
3709 g_string_append_c(norm_utf7, '-');
3712 g_string_append_c(norm_utf7, *p);
3716 norm_utf7_p = norm_utf7->str;
3717 norm_utf7_len = norm_utf7->len;
3718 to_len = strlen(mutf7_str) * 5;
3719 to_p = to_str = g_malloc(to_len + 1);
3721 if (iconv(cd, (ICONV_CONST gchar **)&norm_utf7_p, &norm_utf7_len,
3722 &to_p, &to_len) == -1) {
3723 g_warning(_("iconv cannot convert UTF-7 to %s\n"),
3724 conv_get_locale_charset_str());
3725 g_string_free(norm_utf7, TRUE);
3727 return g_strdup(mutf7_str);
3730 /* second iconv() call for flushing */
3731 iconv(cd, NULL, NULL, &to_p, &to_len);
3732 g_string_free(norm_utf7, TRUE);
3738 static gchar *imap_utf8_to_modified_utf7(const gchar *from)
3740 static iconv_t cd = (iconv_t)-1;
3741 static gboolean iconv_ok = TRUE;
3742 gchar *norm_utf7, *norm_utf7_p;
3743 size_t from_len, norm_utf7_len;
3745 gchar *from_tmp, *to, *p;
3746 gboolean in_escape = FALSE;
3748 if (!iconv_ok) return g_strdup(from);
3750 if (cd == (iconv_t)-1) {
3751 cd = iconv_open(CS_UTF_7, CS_INTERNAL);
3752 if (cd == (iconv_t)-1) {
3753 g_warning(_("iconv cannot convert %s to UTF-7\n"),
3756 return g_strdup(from);
3760 /* UTF-8 to normal UTF-7 conversion */
3761 Xstrdup_a(from_tmp, from, return g_strdup(from));
3762 from_len = strlen(from);
3763 norm_utf7_len = from_len * 5;
3764 Xalloca(norm_utf7, norm_utf7_len + 1, return g_strdup(from));
3765 norm_utf7_p = norm_utf7;
3767 #define IS_PRINT(ch) (isprint(ch) && IS_ASCII(ch))
3769 while (from_len > 0) {
3770 if (*from_tmp == '+') {
3771 *norm_utf7_p++ = '+';
3772 *norm_utf7_p++ = '-';
3776 } else if (IS_PRINT(*(guchar *)from_tmp)) {
3777 /* printable ascii char */
3778 *norm_utf7_p = *from_tmp;
3784 size_t conv_len = 0;
3786 /* unprintable char: convert to UTF-7 */
3788 while (!IS_PRINT(*(guchar *)p) && conv_len < from_len) {
3789 conv_len += g_utf8_skip[*(guchar *)p];
3790 p += g_utf8_skip[*(guchar *)p];
3793 from_len -= conv_len;
3794 if (iconv(cd, (ICONV_CONST gchar **)&from_tmp,
3796 &norm_utf7_p, &norm_utf7_len) == -1) {
3797 g_warning(_("iconv cannot convert UTF-8 to UTF-7\n"));
3798 return g_strdup(from);
3801 /* second iconv() call for flushing */
3802 iconv(cd, NULL, NULL, &norm_utf7_p, &norm_utf7_len);
3808 *norm_utf7_p = '\0';
3809 to_str = g_string_new(NULL);
3810 for (p = norm_utf7; p < norm_utf7_p; p++) {
3811 /* replace: '&' -> "&-",
3814 BASE64 '/' -> ',' */
3815 if (!in_escape && *p == '&') {
3816 g_string_append(to_str, "&-");
3817 } else if (!in_escape && *p == '+') {
3818 if (*(p + 1) == '-') {
3819 g_string_append_c(to_str, '+');
3822 g_string_append_c(to_str, '&');
3825 } else if (in_escape && *p == '/') {
3826 g_string_append_c(to_str, ',');
3827 } else if (in_escape && *p == '-') {
3828 g_string_append_c(to_str, '-');
3831 g_string_append_c(to_str, *p);
3837 g_string_append_c(to_str, '-');
3841 g_string_free(to_str, FALSE);
3846 static GSList *imap_get_seq_set_from_numlist(MsgNumberList *numlist)
3849 GSList *sorted_list, *cur;
3850 guint first, last, next;
3852 GSList *ret_list = NULL;
3854 if (numlist == NULL)
3857 str = g_string_sized_new(256);
3859 sorted_list = g_slist_copy(numlist);
3860 sorted_list = g_slist_sort(sorted_list, g_int_compare);
3862 first = GPOINTER_TO_INT(sorted_list->data);
3864 for (cur = sorted_list; cur != NULL; cur = g_slist_next(cur)) {
3865 last = GPOINTER_TO_INT(cur->data);
3867 next = GPOINTER_TO_INT(cur->next->data);
3871 if (last + 1 != next || next == 0) {
3873 g_string_append_c(str, ',');
3875 g_string_append_printf(str, "%u", first);
3877 g_string_append_printf(str, "%u:%u", first, last);
3881 if (str->len > IMAP_CMD_LIMIT) {
3882 ret_str = g_strdup(str->str);
3883 ret_list = g_slist_append(ret_list, ret_str);
3884 g_string_truncate(str, 0);
3890 ret_str = g_strdup(str->str);
3891 ret_list = g_slist_append(ret_list, ret_str);
3894 g_slist_free(sorted_list);
3895 g_string_free(str, TRUE);
3900 static GSList *imap_get_seq_set_from_msglist(MsgInfoList *msglist)
3902 MsgNumberList *numlist = NULL;
3906 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
3907 MsgInfo *msginfo = (MsgInfo *) cur->data;
3909 numlist = g_slist_append(numlist, GINT_TO_POINTER(msginfo->msgnum));
3911 seq_list = imap_get_seq_set_from_numlist(numlist);
3912 g_slist_free(numlist);
3917 static void imap_seq_set_free(GSList *seq_list)
3919 slist_free_strings(seq_list);
3920 g_slist_free(seq_list);
3924 static gboolean imap_rename_folder_func(GNode *node, gpointer data)
3926 FolderItem *item = node->data;
3927 gchar **paths = data;
3928 const gchar *oldpath = paths[0];
3929 const gchar *newpath = paths[1];
3931 gchar *new_itempath;
3934 oldpathlen = strlen(oldpath);
3935 if (strncmp(oldpath, item->path, oldpathlen) != 0) {
3936 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
3940 base = item->path + oldpathlen;
3941 while (*base == G_DIR_SEPARATOR) base++;
3943 new_itempath = g_strdup(newpath);
3945 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
3948 item->path = new_itempath;
3953 typedef struct _get_list_uid_data {
3955 IMAPFolderItem *item;
3956 GSList **msgnum_list;
3958 } get_list_uid_data;
3960 static void *get_list_of_uids_thread(void *data)
3962 get_list_uid_data *stuff = (get_list_uid_data *)data;
3963 Folder *folder = stuff->folder;
3964 IMAPFolderItem *item = stuff->item;
3965 GSList **msgnum_list = stuff->msgnum_list;
3966 gint ok, nummsgs = 0, lastuid_old;
3967 IMAPSession *session;
3968 GSList *uidlist, *elem;
3971 session = imap_session_get(folder);
3972 if (session == NULL) {
3974 return GINT_TO_POINTER(-1);
3977 ok = imap_select(session, IMAP_FOLDER(folder), item->item.path,
3978 NULL, NULL, NULL, NULL, TRUE);
3979 if (ok != IMAP_SUCCESS) {
3981 return GINT_TO_POINTER(-1);
3984 cmd_buf = g_strdup_printf("UID %d:*", item->lastuid + 1);
3985 ok = imap_cmd_search(session, cmd_buf, &uidlist, TRUE);
3988 if (ok == IMAP_SOCKET) {
3989 session_destroy((Session *)session);
3990 ((RemoteFolder *)folder)->session = NULL;
3992 return GINT_TO_POINTER(-1);
3995 if (ok != IMAP_SUCCESS) {
3999 argbuf = g_ptr_array_new();
4001 cmd_buf = g_strdup_printf("UID FETCH %d:* (UID)", item->lastuid + 1);
4002 imap_gen_send(session, cmd_buf);
4004 ok = imap_cmd_ok_block(session, argbuf);
4005 if (ok != IMAP_SUCCESS) {
4006 ptr_array_free_strings(argbuf);
4007 g_ptr_array_free(argbuf, TRUE);
4009 return GINT_TO_POINTER(-1);
4012 for(i = 0; i < argbuf->len; i++) {
4015 if((ret = sscanf(g_ptr_array_index(argbuf, i),
4016 "%*d FETCH (UID %d)", &msgnum)) == 1)
4017 uidlist = g_slist_prepend(uidlist, GINT_TO_POINTER(msgnum));
4019 ptr_array_free_strings(argbuf);
4020 g_ptr_array_free(argbuf, TRUE);
4023 lastuid_old = item->lastuid;
4024 *msgnum_list = g_slist_copy(item->uid_list);
4025 nummsgs = g_slist_length(*msgnum_list);
4026 debug_print("Got %d uids from cache\n", g_slist_length(item->uid_list));
4028 for (elem = uidlist; elem != NULL; elem = g_slist_next(elem)) {
4031 msgnum = GPOINTER_TO_INT(elem->data);
4032 if (msgnum > lastuid_old) {
4033 *msgnum_list = g_slist_prepend(*msgnum_list, GINT_TO_POINTER(msgnum));
4034 item->uid_list = g_slist_prepend(item->uid_list, GINT_TO_POINTER(msgnum));
4037 if(msgnum > item->lastuid)
4038 item->lastuid = msgnum;
4041 g_slist_free(uidlist);
4044 return GINT_TO_POINTER(nummsgs);
4047 static gint get_list_of_uids(Folder *folder, IMAPFolderItem *item, GSList **msgnum_list)
4050 get_list_uid_data *data = g_new0(get_list_uid_data, 1);
4056 data->folder = folder;
4058 data->msgnum_list = msgnum_list;
4060 if (prefs_common.work_offline && !imap_gtk_should_override()) {
4065 #if (defined USE_PTHREAD && defined __GLIBC__ && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 3)))
4066 if (pthread_create(&pt, PTHREAD_CREATE_JOINABLE,
4067 get_list_of_uids_thread, data) != 0) {
4068 result = GPOINTER_TO_INT(get_list_of_uids_thread(data));
4072 debug_print("+++waiting for get_list_of_uids_thread...\n");
4073 while(!data->done) {
4074 /* don't let the interface freeze while waiting */
4077 debug_print("---get_list_of_uids_thread done\n");
4079 /* get the thread's return value and clean its resources */
4080 pthread_join(pt, &tmp);
4081 result = GPOINTER_TO_INT(tmp);
4083 result = GPOINTER_TO_INT(get_list_of_uids_thread(data));
4090 gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list, gboolean *old_uids_valid)
4092 IMAPFolderItem *item = (IMAPFolderItem *)_item;
4093 IMAPSession *session;
4094 gint ok, nummsgs = 0, exists, recent, uid_val, uid_next, unseen;
4095 GSList *uidlist = NULL;
4097 gboolean selected_folder;
4099 g_return_val_if_fail(folder != NULL, -1);
4100 g_return_val_if_fail(item != NULL, -1);
4101 g_return_val_if_fail(item->item.path != NULL, -1);
4102 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
4103 g_return_val_if_fail(folder->account != NULL, -1);
4105 session = imap_session_get(folder);
4106 g_return_val_if_fail(session != NULL, -1);
4108 selected_folder = (session->mbox != NULL) &&
4109 (!strcmp(session->mbox, item->item.path));
4110 if (selected_folder) {
4111 ok = imap_cmd_noop(session);
4112 if (ok != IMAP_SUCCESS)
4114 exists = session->exists;
4116 *old_uids_valid = TRUE;
4118 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
4119 &exists, &recent, &uid_next, &uid_val, &unseen, FALSE);
4120 if (ok != IMAP_SUCCESS)
4123 if(item->item.mtime == uid_val)
4124 *old_uids_valid = TRUE;
4126 *old_uids_valid = FALSE;
4128 debug_print("Freeing imap uid cache\n");
4130 g_slist_free(item->uid_list);
4131 item->uid_list = NULL;
4133 item->item.mtime = uid_val;
4135 imap_delete_all_cached_messages((FolderItem *)item);
4139 if (!selected_folder)
4140 item->uid_next = uid_next;
4142 /* If old uid_next matches new uid_next we can be sure no message
4143 was added to the folder */
4144 if (( selected_folder && !session->folder_content_changed) ||
4145 (!selected_folder && uid_next == item->uid_next)) {
4146 nummsgs = g_slist_length(item->uid_list);
4148 /* If number of messages is still the same we
4149 know our caches message numbers are still valid,
4150 otherwise if the number of messages has decrease
4151 we discard our cache to start a new scan to find
4152 out which numbers have been removed */
4153 if (exists == nummsgs) {
4154 *msgnum_list = g_slist_copy(item->uid_list);
4156 } else if (exists < nummsgs) {
4157 debug_print("Freeing imap uid cache");
4159 g_slist_free(item->uid_list);
4160 item->uid_list = NULL;
4165 *msgnum_list = NULL;
4169 nummsgs = get_list_of_uids(folder, item, &uidlist);
4171 if (nummsgs != exists) {
4172 /* Cache contains more messages then folder, we have cached
4173 an old UID of a message that was removed and new messages
4174 have been added too, otherwise the uid_next check would
4176 debug_print("Freeing imap uid cache");
4178 g_slist_free(item->uid_list);
4179 item->uid_list = NULL;
4181 g_slist_free(*msgnum_list);
4183 nummsgs = get_list_of_uids(folder, item, &uidlist);
4186 *msgnum_list = uidlist;
4188 dir = folder_item_get_path((FolderItem *)item);
4189 debug_print("removing old messages from %s\n", dir);
4190 remove_numbered_files_not_in_list(dir, *msgnum_list);
4196 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item)
4201 flags.perm_flags = MSG_NEW|MSG_UNREAD;
4202 flags.tmp_flags = 0;
4204 g_return_val_if_fail(item != NULL, NULL);
4205 g_return_val_if_fail(file != NULL, NULL);
4207 if (item->stype == F_QUEUE) {
4208 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
4209 } else if (item->stype == F_DRAFT) {
4210 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
4213 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
4214 if (!msginfo) return NULL;
4216 msginfo->folder = item;
4221 GSList *imap_get_msginfos(Folder *folder, FolderItem *item, GSList *msgnum_list)
4223 IMAPSession *session;
4224 MsgInfoList *ret = NULL;
4227 g_return_val_if_fail(folder != NULL, NULL);
4228 g_return_val_if_fail(item != NULL, NULL);
4229 g_return_val_if_fail(msgnum_list != NULL, NULL);
4231 session = imap_session_get(folder);
4232 g_return_val_if_fail(session != NULL, NULL);
4234 printf("getting msginfos\n");
4235 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
4236 NULL, NULL, NULL, NULL, FALSE);
4237 if (ok != IMAP_SUCCESS)
4240 if (!(item->stype == F_QUEUE || item->stype == F_DRAFT)) {
4241 ret = g_slist_concat(ret,
4242 imap_get_uncached_messages(
4243 session, item, msgnum_list));
4245 MsgNumberList *sorted_list, *elem;
4246 gint startnum, lastnum;
4248 sorted_list = g_slist_sort(g_slist_copy(msgnum_list), g_int_compare);
4250 startnum = lastnum = GPOINTER_TO_INT(sorted_list->data);
4252 for (elem = sorted_list;; elem = g_slist_next(elem)) {
4256 num = GPOINTER_TO_INT(elem->data);
4258 if (num > lastnum + 1 || elem == NULL) {
4260 for (i = startnum; i <= lastnum; ++i) {
4263 file = imap_fetch_msg(folder, item, i);
4265 MsgInfo *msginfo = imap_parse_msg(file, item);
4266 if (msginfo != NULL) {
4267 msginfo->msgnum = i;
4268 ret = g_slist_append(ret, msginfo);
4282 g_slist_free(sorted_list);
4288 MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid)
4290 MsgInfo *msginfo = NULL;
4291 MsgInfoList *msginfolist;
4292 MsgNumberList numlist;
4294 numlist.next = NULL;
4295 numlist.data = GINT_TO_POINTER(uid);
4297 msginfolist = imap_get_msginfos(folder, item, &numlist);
4298 if (msginfolist != NULL) {
4299 msginfo = msginfolist->data;
4300 g_slist_free(msginfolist);
4306 gboolean imap_scan_required(Folder *folder, FolderItem *_item)
4308 IMAPSession *session;
4309 IMAPFolderItem *item = (IMAPFolderItem *)_item;
4310 gint ok, exists = 0, recent = 0, unseen = 0;
4311 guint32 uid_next, uid_val = 0;
4312 gboolean selected_folder;
4314 g_return_val_if_fail(folder != NULL, FALSE);
4315 g_return_val_if_fail(item != NULL, FALSE);
4316 g_return_val_if_fail(item->item.folder != NULL, FALSE);
4317 g_return_val_if_fail(FOLDER_CLASS(item->item.folder) == &imap_class, FALSE);
4319 if (item->item.path == NULL)
4322 session = imap_session_get(folder);
4323 g_return_val_if_fail(session != NULL, FALSE);
4325 selected_folder = (session->mbox != NULL) &&
4326 (!strcmp(session->mbox, item->item.path));
4327 if (selected_folder) {
4328 ok = imap_cmd_noop(session);
4329 if (ok != IMAP_SUCCESS)
4332 if (session->folder_content_changed)
4335 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
4336 &exists, &recent, &uid_next, &uid_val, &unseen, FALSE);
4337 if (ok != IMAP_SUCCESS)
4340 if ((uid_next != item->uid_next) || (exists != item->item.total_msgs))
4347 static GHashTable *flags_set_table = NULL;
4348 static GHashTable *flags_unset_table = NULL;
4349 static gint hashtable_process_tag = -1;
4351 typedef struct _hashtable_data {
4352 IMAPSession *session;
4356 static gboolean process_flags(gpointer key, gpointer value, gpointer user_data)
4358 gboolean flags_set = GPOINTER_TO_INT(user_data);
4359 gint flags_value = GPOINTER_TO_INT(key);
4360 hashtable_data *data = (hashtable_data *)value;
4362 data->msglist = g_slist_reverse(data->msglist);
4364 imap_set_message_flags(data->session, data->msglist, flags_value, flags_set);
4366 if (flags_value == IMAP_FLAG_DELETED && flags_set == TRUE) {
4367 if (!data->session->uidplus) {
4368 imap_cmd_expunge(data->session, NULL);
4372 seq_list = imap_get_seq_set_from_numlist(data->msglist);
4373 imapset = get_seq_set_from_seq_list(seq_list);
4375 imap_cmd_expunge(data->session, imapset);
4380 g_slist_free(data->msglist);
4385 static gboolean process_hashtable(void *data)
4387 debug_print("processing flags change hashtables\n");
4388 if (flags_set_table) {
4389 g_hash_table_foreach_remove(flags_set_table, process_flags, GINT_TO_POINTER(TRUE));
4390 g_free(flags_set_table);
4391 flags_set_table = NULL;
4393 if (flags_unset_table) {
4394 g_hash_table_foreach_remove(flags_unset_table, process_flags, GINT_TO_POINTER(FALSE));
4395 g_free(flags_unset_table);
4396 flags_unset_table = NULL;
4402 void imap_change_flags(Folder *folder, FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
4404 IMAPSession *session;
4405 IMAPFlags flags_set = 0, flags_unset = 0;
4406 gint ok = IMAP_SUCCESS;
4407 hashtable_data *ht_data = NULL;
4409 g_return_if_fail(folder != NULL);
4410 g_return_if_fail(folder->klass == &imap_class);
4411 g_return_if_fail(item != NULL);
4412 g_return_if_fail(item->folder == folder);
4413 g_return_if_fail(msginfo != NULL);
4414 g_return_if_fail(msginfo->folder == item);
4416 session = imap_session_get(folder);
4417 if (!session) return;
4418 printf("changing flags\n");
4419 if ((ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
4420 NULL, NULL, NULL, NULL, FALSE)) != IMAP_SUCCESS)
4423 if (!flags_set_table) {
4424 flags_set_table = g_hash_table_new(NULL, g_direct_equal);
4426 if (!flags_unset_table) {
4427 flags_unset_table = g_hash_table_new(NULL, g_direct_equal);
4430 if (!MSG_IS_MARKED(msginfo->flags) && (newflags & MSG_MARKED))
4431 flags_set |= IMAP_FLAG_FLAGGED;
4432 if ( MSG_IS_MARKED(msginfo->flags) && !(newflags & MSG_MARKED))
4433 flags_unset |= IMAP_FLAG_FLAGGED;
4435 if (!MSG_IS_UNREAD(msginfo->flags) && (newflags & MSG_UNREAD))
4436 flags_unset |= IMAP_FLAG_SEEN;
4437 if ( MSG_IS_UNREAD(msginfo->flags) && !(newflags & MSG_UNREAD))
4438 flags_set |= IMAP_FLAG_SEEN;
4440 if (!MSG_IS_REPLIED(msginfo->flags) && (newflags & MSG_REPLIED))
4441 flags_set |= IMAP_FLAG_ANSWERED;
4442 if ( MSG_IS_REPLIED(msginfo->flags) && !(newflags & MSG_REPLIED))
4443 flags_set |= IMAP_FLAG_ANSWERED;
4445 /* instead of performing an UID STORE command for each message change,
4446 * as a lot of them can change "together", we just fill in hashtables
4447 * and defer the treatment 500ms so that we're able to send only one
4451 ht_data = g_hash_table_lookup(flags_set_table, GINT_TO_POINTER(flags_set));
4452 if (ht_data == NULL) {
4453 ht_data = g_new0(hashtable_data, 1);
4454 ht_data->session = session;
4455 g_hash_table_insert(flags_set_table, GINT_TO_POINTER(flags_set), ht_data);
4457 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
4458 ht_data->msglist = g_slist_prepend(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum));
4462 ht_data = g_hash_table_lookup(flags_unset_table, GINT_TO_POINTER(flags_unset));
4463 if (ht_data == NULL) {
4464 ht_data = g_new0(hashtable_data, 1);
4465 ht_data->session = session;
4466 g_hash_table_insert(flags_unset_table, GINT_TO_POINTER(flags_unset), ht_data);
4468 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
4469 ht_data->msglist = g_slist_prepend(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum));
4472 msginfo->flags.perm_flags = newflags;
4474 if (hashtable_process_tag != -1)
4475 gtk_timeout_remove(hashtable_process_tag);
4477 hashtable_process_tag = gtk_timeout_add(500, process_hashtable, NULL);
4482 static gint imap_remove_msg(Folder *folder, FolderItem *item, gint uid)
4485 IMAPSession *session;
4487 MsgNumberList numlist;
4488 hashtable_data *ht_data;
4490 if (!flags_set_table) {
4491 flags_set_table = g_hash_table_new(NULL, g_direct_equal);
4493 if (!flags_unset_table) {
4494 flags_unset_table = g_hash_table_new(NULL, g_direct_equal);
4497 g_return_val_if_fail(folder != NULL, -1);
4498 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
4499 g_return_val_if_fail(item != NULL, -1);
4501 session = imap_session_get(folder);
4502 if (!session) return -1;
4504 printf("removing messages\n");
4505 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
4506 NULL, NULL, NULL, NULL, FALSE);
4507 if (ok != IMAP_SUCCESS)
4510 ht_data = g_hash_table_lookup(flags_set_table, GINT_TO_POINTER(IMAP_FLAG_DELETED));
4511 if (ht_data == NULL) {
4512 ht_data = g_new0(hashtable_data, 1);
4513 ht_data->session = session;
4514 g_hash_table_insert(flags_set_table, GINT_TO_POINTER(IMAP_FLAG_DELETED), ht_data);
4516 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(uid)))
4517 ht_data->msglist = g_slist_prepend(ht_data->msglist,
4518 GINT_TO_POINTER(uid));
4520 if (hashtable_process_tag != -1)
4521 gtk_timeout_remove(hashtable_process_tag);
4523 hashtable_process_tag = gtk_timeout_add(500, process_hashtable, NULL);
4525 IMAP_FOLDER_ITEM(item)->uid_list = g_slist_remove(
4526 IMAP_FOLDER_ITEM(item)->uid_list, numlist.data);
4527 dir = folder_item_get_path(item);
4528 if (is_dir_exist(dir))
4529 remove_numbered_files(dir, uid, uid);
4532 return IMAP_SUCCESS;
4535 static gint compare_msginfo(gconstpointer a, gconstpointer b)
4537 return ((MsgInfo *)a)->msgnum - ((MsgInfo *)b)->msgnum;
4540 static guint gslist_find_next_num(MsgNumberList **list, guint num)
4544 g_return_val_if_fail(list != NULL, -1);
4546 for (elem = *list; elem != NULL; elem = g_slist_next(elem))
4547 if (GPOINTER_TO_INT(elem->data) >= num)
4550 return elem != NULL ? GPOINTER_TO_INT(elem->data) : (gint)-1;
4554 * NEW and DELETED flags are not syncronized
4555 * - The NEW/RECENT flags in IMAP folders can not really be directly
4556 * modified by Sylpheed
4557 * - The DELETE/DELETED flag in IMAP and Sylpheed don't have the same
4558 * meaning, in IMAP it always removes the messages from the FolderItem
4559 * in Sylpheed it can mean to move the message to trash
4562 typedef struct _get_flags_data {
4565 MsgInfoList *msginfo_list;
4566 GRelation *msgflags;
4570 static /*gint*/ void *imap_get_flags_thread(void *data)
4572 get_flags_data *stuff = (get_flags_data *)data;
4573 Folder *folder = stuff->folder;
4574 FolderItem *item = stuff->item;
4575 MsgInfoList *msginfo_list = stuff->msginfo_list;
4576 GRelation *msgflags = stuff->msgflags;
4577 IMAPSession *session;
4578 GSList *sorted_list;
4579 GSList *unseen = NULL, *answered = NULL, *flagged = NULL;
4580 GSList *p_unseen, *p_answered, *p_flagged;
4582 GSList *seq_list, *cur;
4583 gboolean reverse_seen = FALSE;
4586 gint exists_cnt, recent_cnt, unseen_cnt, uid_next;
4587 guint32 uidvalidity;
4588 gboolean selected_folder;
4590 if (folder == NULL || item == NULL) {
4592 return GINT_TO_POINTER(-1);
4594 if (msginfo_list == NULL) {
4596 return GINT_TO_POINTER(0);
4599 session = imap_session_get(folder);
4600 if (session == NULL) {
4602 return GINT_TO_POINTER(-1);
4605 selected_folder = (session->mbox != NULL) &&
4606 (!strcmp(session->mbox, item->path));
4608 if (!selected_folder) {
4609 ok = imap_status(session, IMAP_FOLDER(folder), item->path,
4610 &exists_cnt, &recent_cnt, &uid_next, &uidvalidity, &unseen_cnt, TRUE);
4611 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
4612 NULL, NULL, NULL, NULL, TRUE);
4613 if (ok != IMAP_SUCCESS) {
4615 return GINT_TO_POINTER(-1);
4620 if (unseen_cnt > exists_cnt / 2)
4621 reverse_seen = TRUE;
4623 cmd_buf = g_string_new(NULL);
4625 sorted_list = g_slist_sort(g_slist_copy(msginfo_list), compare_msginfo);
4627 seq_list = imap_get_seq_set_from_msglist(msginfo_list);
4629 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
4630 IMAPSet imapset = cur->data;
4632 g_string_sprintf(cmd_buf, "%sSEEN UID %s", reverse_seen ? "" : "UN", imapset);
4633 imap_cmd_search(session, cmd_buf->str, &p_unseen, TRUE);
4634 unseen = g_slist_concat(unseen, p_unseen);
4636 g_string_sprintf(cmd_buf, "ANSWERED UID %s", imapset);
4637 imap_cmd_search(session, cmd_buf->str, &p_answered, TRUE);
4638 answered = g_slist_concat(answered, p_answered);
4640 g_string_sprintf(cmd_buf, "FLAGGED UID %s", imapset);
4641 imap_cmd_search(session, cmd_buf->str, &p_flagged, TRUE);
4642 flagged = g_slist_concat(flagged, p_flagged);
4646 p_answered = answered;
4647 p_flagged = flagged;
4649 for (elem = sorted_list; elem != NULL; elem = g_slist_next(elem)) {
4654 msginfo = (MsgInfo *) elem->data;
4655 flags = msginfo->flags.perm_flags;
4656 wasnew = (flags & MSG_NEW);
4657 flags &= ~((reverse_seen ? 0 : MSG_UNREAD | MSG_NEW) | MSG_REPLIED | MSG_MARKED);
4659 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
4660 if (gslist_find_next_num(&p_unseen, msginfo->msgnum) == msginfo->msgnum) {
4661 if (!reverse_seen) {
4662 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
4664 flags &= ~(MSG_UNREAD | MSG_NEW);
4667 if (gslist_find_next_num(&p_answered, msginfo->msgnum) == msginfo->msgnum)
4668 flags |= MSG_REPLIED;
4669 if (gslist_find_next_num(&p_flagged, msginfo->msgnum) == msginfo->msgnum)
4670 flags |= MSG_MARKED;
4671 g_relation_insert(msgflags, msginfo, GINT_TO_POINTER(flags));
4674 imap_seq_set_free(seq_list);
4675 g_slist_free(flagged);
4676 g_slist_free(answered);
4677 g_slist_free(unseen);
4678 g_slist_free(sorted_list);
4679 g_string_free(cmd_buf, TRUE);
4682 return GINT_TO_POINTER(0);
4685 static gint imap_get_flags(Folder *folder, FolderItem *item,
4686 MsgInfoList *msginfo_list, GRelation *msgflags)
4689 get_flags_data *data = g_new0(get_flags_data, 1);
4695 data->folder = folder;
4697 data->msginfo_list = msginfo_list;
4698 data->msgflags = msgflags;
4700 if (prefs_common.work_offline && !imap_gtk_should_override()) {
4705 #if (defined USE_PTHREAD && defined __GLIBC__ && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 3)))
4706 if (pthread_create(&pt, PTHREAD_CREATE_JOINABLE,
4707 imap_get_flags_thread, data) != 0) {
4708 result = GPOINTER_TO_INT(imap_get_flags_thread(data));
4712 debug_print("+++waiting for imap_get_flags_thread...\n");
4713 while(!data->done) {
4714 /* don't let the interface freeze while waiting */
4717 debug_print("---imap_get_flags_thread done\n");
4719 /* get the thread's return value and clean its resources */
4720 pthread_join(pt, &tmp);
4721 result = GPOINTER_TO_INT(tmp);
4723 result = GPOINTER_TO_INT(imap_get_flags_thread(data));