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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
27 #include <glib/gi18n.h>
55 #include "procheader.h"
56 #include "prefs_account.h"
61 #include "prefs_common.h"
62 #include "inputdialog.h"
64 #include "remotefolder.h"
65 #include "alertpanel.h"
67 #include "statusbar.h"
69 #include "imap-thread.h"
71 typedef struct _IMAPFolder IMAPFolder;
72 typedef struct _IMAPSession IMAPSession;
73 typedef struct _IMAPNameSpace IMAPNameSpace;
74 typedef struct _IMAPFolderItem IMAPFolderItem;
76 #include "prefs_account.h"
78 #define IMAP_FOLDER(obj) ((IMAPFolder *)obj)
79 #define IMAP_FOLDER_ITEM(obj) ((IMAPFolderItem *)obj)
80 #define IMAP_SESSION(obj) ((IMAPSession *)obj)
86 /* list of IMAPNameSpace */
90 gchar last_seen_separator;
98 gboolean authenticated;
107 gboolean folder_content_changed;
113 struct _IMAPNameSpace
119 #define IMAP_SUCCESS 0
120 #define IMAP_SOCKET 2
121 #define IMAP_AUTHFAIL 3
122 #define IMAP_PROTOCOL 4
123 #define IMAP_SYNTAX 5
127 #define IMAPBUFSIZE 8192
131 IMAP_FLAG_SEEN = 1 << 0,
132 IMAP_FLAG_ANSWERED = 1 << 1,
133 IMAP_FLAG_FLAGGED = 1 << 2,
134 IMAP_FLAG_DELETED = 1 << 3,
135 IMAP_FLAG_DRAFT = 1 << 4
138 #define IMAP_IS_SEEN(flags) ((flags & IMAP_FLAG_SEEN) != 0)
139 #define IMAP_IS_ANSWERED(flags) ((flags & IMAP_FLAG_ANSWERED) != 0)
140 #define IMAP_IS_FLAGGED(flags) ((flags & IMAP_FLAG_FLAGGED) != 0)
141 #define IMAP_IS_DELETED(flags) ((flags & IMAP_FLAG_DELETED) != 0)
142 #define IMAP_IS_DRAFT(flags) ((flags & IMAP_FLAG_DRAFT) != 0)
145 #define IMAP4_PORT 143
147 #define IMAPS_PORT 993
150 #define IMAP_CMD_LIMIT 1000
152 struct _IMAPFolderItem
164 guint32 c_uid_validity;
167 GHashTable *flags_set_table;
168 GHashTable *flags_unset_table;
171 static void imap_folder_init (Folder *folder,
175 static Folder *imap_folder_new (const gchar *name,
177 static void imap_folder_destroy (Folder *folder);
179 static IMAPSession *imap_session_new (Folder *folder,
180 const PrefsAccount *account);
181 static void imap_session_authenticate(IMAPSession *session,
182 const PrefsAccount *account);
183 static void imap_session_destroy (Session *session);
185 static gchar *imap_fetch_msg (Folder *folder,
188 static gchar *imap_fetch_msg_full (Folder *folder,
193 static gint imap_add_msg (Folder *folder,
197 static gint imap_add_msgs (Folder *folder,
200 GRelation *relation);
202 static gint imap_copy_msg (Folder *folder,
205 static gint imap_copy_msgs (Folder *folder,
207 MsgInfoList *msglist,
208 GRelation *relation);
210 static gint imap_remove_msg (Folder *folder,
213 static gint imap_remove_msgs (Folder *folder,
215 MsgInfoList *msglist,
216 GRelation *relation);
217 static gint imap_remove_all_msg (Folder *folder,
220 static gboolean imap_is_msg_changed (Folder *folder,
224 static gint imap_close (Folder *folder,
227 static gint imap_scan_tree (Folder *folder);
229 static gint imap_create_tree (Folder *folder);
231 static FolderItem *imap_create_folder (Folder *folder,
234 static gint imap_rename_folder (Folder *folder,
237 static gint imap_remove_folder (Folder *folder,
240 static FolderItem *imap_folder_item_new (Folder *folder);
241 static void imap_folder_item_destroy (Folder *folder,
244 static IMAPSession *imap_session_get (Folder *folder);
246 static gint imap_auth (IMAPSession *session,
251 static gint imap_scan_tree_recursive (IMAPSession *session,
254 static void imap_create_missing_folders (Folder *folder);
255 static FolderItem *imap_create_special_folder
257 SpecialFolderItemType stype,
260 static gint imap_do_copy_msgs (Folder *folder,
262 MsgInfoList *msglist,
263 GRelation *relation);
265 static void imap_delete_all_cached_messages (FolderItem *item);
266 static void imap_set_batch (Folder *folder,
269 static gint imap_set_message_flags (IMAPSession *session,
270 MsgNumberList *numlist,
273 static gint imap_select (IMAPSession *session,
279 guint32 *uid_validity,
281 static gint imap_status (IMAPSession *session,
284 IMAPFolderItem *item,
287 guint32 *uid_validity,
291 static IMAPNameSpace *imap_find_namespace (IMAPFolder *folder,
293 static gchar imap_get_path_separator (IMAPFolder *folder,
295 static gchar *imap_get_real_path (IMAPFolder *folder,
297 static void imap_synchronise (FolderItem *item);
299 static void imap_free_capabilities (IMAPSession *session);
301 /* low-level IMAP4rev1 commands */
302 static gint imap_cmd_login (IMAPSession *session,
306 static gint imap_cmd_logout (IMAPSession *session);
307 static gint imap_cmd_noop (IMAPSession *session);
309 static gint imap_cmd_starttls (IMAPSession *session);
311 static gint imap_cmd_select (IMAPSession *session,
316 guint32 *uid_validity,
318 static gint imap_cmd_examine (IMAPSession *session,
323 guint32 *uid_validity,
325 static gint imap_cmd_create (IMAPSession *sock,
326 const gchar *folder);
327 static gint imap_cmd_rename (IMAPSession *sock,
328 const gchar *oldfolder,
329 const gchar *newfolder);
330 static gint imap_cmd_delete (IMAPSession *session,
331 const gchar *folder);
332 static gint imap_cmd_fetch (IMAPSession *sock,
334 const gchar *filename,
337 static gint imap_cmd_append (IMAPSession *session,
338 const gchar *destfolder,
342 static gint imap_cmd_copy (IMAPSession *session,
343 struct mailimap_set * set,
344 const gchar *destfolder,
345 GRelation *uid_mapping);
346 static gint imap_cmd_store (IMAPSession *session,
347 struct mailimap_set * set,
350 static gint imap_cmd_expunge (IMAPSession *session);
352 static void imap_path_separator_subst (gchar *str,
355 static gchar *imap_utf8_to_modified_utf7 (const gchar *from);
356 static gchar *imap_modified_utf7_to_utf8 (const gchar *mutf7_str);
358 static gboolean imap_rename_folder_func (GNode *node,
360 static gint imap_get_num_list (Folder *folder,
363 gboolean *old_uids_valid);
364 static GSList *imap_get_msginfos (Folder *folder,
366 GSList *msgnum_list);
367 static MsgInfo *imap_get_msginfo (Folder *folder,
370 static gboolean imap_scan_required (Folder *folder,
372 static void imap_change_flags (Folder *folder,
375 MsgPermFlags newflags);
376 static gint imap_get_flags (Folder *folder,
378 MsgInfoList *msglist,
379 GRelation *msgflags);
380 static gchar *imap_folder_get_path (Folder *folder);
381 static gchar *imap_item_get_path (Folder *folder,
383 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item);
386 /* data types conversion libetpan <-> sylpheed */
387 static GSList * imap_list_from_lep(IMAPFolder * folder,
388 clist * list, const gchar * real_path, gboolean all);
389 static GSList * imap_get_lep_set_from_numlist(MsgNumberList *numlist);
390 static GSList * imap_get_lep_set_from_msglist(MsgInfoList *msglist);
391 static GSList * imap_uid_list_from_lep(clist * list);
392 static GSList * imap_uid_list_from_lep_tab(carray * list);
393 static MsgInfo *imap_envelope_from_lep(struct imap_fetch_env_info * info,
395 static void imap_lep_set_free(GSList *seq_list);
396 static struct mailimap_flag_list * imap_flag_to_lep(IMAPFlags flags);
398 typedef struct _hashtable_data {
399 IMAPSession *session;
401 IMAPFolderItem *item;
404 static FolderClass imap_class;
406 typedef struct _thread_data {
416 FolderClass *imap_get_class(void)
418 if (imap_class.idstr == NULL) {
419 imap_class.type = F_IMAP;
420 imap_class.idstr = "imap";
421 imap_class.uistr = "IMAP4";
423 /* Folder functions */
424 imap_class.new_folder = imap_folder_new;
425 imap_class.destroy_folder = imap_folder_destroy;
426 imap_class.scan_tree = imap_scan_tree;
427 imap_class.create_tree = imap_create_tree;
429 /* FolderItem functions */
430 imap_class.item_new = imap_folder_item_new;
431 imap_class.item_destroy = imap_folder_item_destroy;
432 imap_class.item_get_path = imap_item_get_path;
433 imap_class.create_folder = imap_create_folder;
434 imap_class.rename_folder = imap_rename_folder;
435 imap_class.remove_folder = imap_remove_folder;
436 imap_class.close = imap_close;
437 imap_class.get_num_list = imap_get_num_list;
438 imap_class.scan_required = imap_scan_required;
440 /* Message functions */
441 imap_class.get_msginfo = imap_get_msginfo;
442 imap_class.get_msginfos = imap_get_msginfos;
443 imap_class.fetch_msg = imap_fetch_msg;
444 imap_class.fetch_msg_full = imap_fetch_msg_full;
445 imap_class.add_msg = imap_add_msg;
446 imap_class.add_msgs = imap_add_msgs;
447 imap_class.copy_msg = imap_copy_msg;
448 imap_class.copy_msgs = imap_copy_msgs;
449 imap_class.remove_msg = imap_remove_msg;
450 imap_class.remove_msgs = imap_remove_msgs;
451 imap_class.remove_all_msg = imap_remove_all_msg;
452 imap_class.is_msg_changed = imap_is_msg_changed;
453 imap_class.change_flags = imap_change_flags;
454 imap_class.get_flags = imap_get_flags;
455 imap_class.set_batch = imap_set_batch;
456 imap_class.synchronise = imap_synchronise;
458 pthread_mutex_init(&imap_mutex, NULL);
465 static Folder *imap_folder_new(const gchar *name, const gchar *path)
469 folder = (Folder *)g_new0(IMAPFolder, 1);
470 folder->klass = &imap_class;
471 imap_folder_init(folder, name, path);
476 static void imap_folder_destroy(Folder *folder)
480 while (imap_folder_get_refcnt(folder) > 0)
481 gtk_main_iteration();
483 dir = imap_folder_get_path(folder);
484 if (is_dir_exist(dir))
485 remove_dir_recursive(dir);
488 folder_remote_folder_destroy(REMOTE_FOLDER(folder));
492 static void imap_folder_init(Folder *folder, const gchar *name,
495 folder_remote_folder_init((Folder *)folder, name, path);
498 static FolderItem *imap_folder_item_new(Folder *folder)
500 IMAPFolderItem *item;
502 item = g_new0(IMAPFolderItem, 1);
505 item->uid_list = NULL;
507 return (FolderItem *)item;
510 static void imap_folder_item_destroy(Folder *folder, FolderItem *_item)
512 IMAPFolderItem *item = (IMAPFolderItem *)_item;
514 g_return_if_fail(item != NULL);
515 g_slist_free(item->uid_list);
520 static gboolean imap_reset_uid_lists_func(GNode *node, gpointer data)
522 IMAPFolderItem *item = (IMAPFolderItem *)node->data;
526 g_slist_free(item->uid_list);
527 item->uid_list = NULL;
532 static void imap_reset_uid_lists(Folder *folder)
534 if(folder->node == NULL)
537 /* Destroy all uid lists and rest last uid */
538 g_node_traverse(folder->node, G_IN_ORDER, G_TRAVERSE_ALL, -1, imap_reset_uid_lists_func, NULL);
541 void imap_get_capabilities(IMAPSession *session)
543 struct mailimap_capability_data *capabilities = NULL;
546 if (session->capability != NULL)
549 capabilities = imap_threaded_capability(session->folder);
551 if (capabilities == NULL)
554 for(cur = clist_begin(capabilities->cap_list) ; cur != NULL ;
555 cur = clist_next(cur)) {
556 struct mailimap_capability * cap =
558 if (!cap || cap->cap_data.cap_name == NULL)
560 session->capability = g_slist_append
561 (session->capability,
562 g_strdup(cap->cap_data.cap_name));
563 debug_print("got capa %s\n", cap->cap_data.cap_name);
565 mailimap_capability_data_free(capabilities);
568 gboolean imap_has_capability(IMAPSession *session, const gchar *cap)
571 for (cur = session->capability; cur; cur = cur->next) {
572 if (!g_ascii_strcasecmp(cur->data, cap))
578 static gint imap_auth(IMAPSession *session, const gchar *user, const gchar *pass,
581 gint ok = IMAP_ERROR;
582 static time_t last_login_err = 0;
584 imap_get_capabilities(session);
587 case IMAP_AUTH_CRAM_MD5:
588 ok = imap_cmd_login(session, user, pass, "CRAM-MD5");
590 case IMAP_AUTH_LOGIN:
591 ok = imap_cmd_login(session, user, pass, "LOGIN");
594 debug_print("capabilities:\n"
597 imap_has_capability(session, "CRAM-MD5"),
598 imap_has_capability(session, "LOGIN"));
599 if (imap_has_capability(session, "CRAM-MD5"))
600 ok = imap_cmd_login(session, user, pass, "CRAM-MD5");
601 if (ok == IMAP_ERROR) /* we always try LOGIN before giving up */
602 ok = imap_cmd_login(session, user, pass, "LOGIN");
604 if (ok == IMAP_SUCCESS)
605 session->authenticated = TRUE;
607 gchar *ext_info = NULL;
609 if (type == IMAP_AUTH_CRAM_MD5) {
610 ext_info = _("\n\nCRAM-MD5 logins only work if libetpan has been "
611 "compiled with SASL support and the "
612 "CRAM-MD5 SASL plugin is installed.");
617 if (time(NULL) - last_login_err > 10) {
618 if (!prefs_common.no_recv_err_panel) {
619 alertpanel_error(_("Connection to %s failed: "
621 SESSION(session)->server, ext_info);
623 log_error(_("Connection to %s failed: "
624 "login refused.%s\n"),
625 SESSION(session)->server, ext_info);
628 last_login_err = time(NULL);
633 static IMAPSession *imap_reconnect_if_possible(Folder *folder, IMAPSession *session)
635 RemoteFolder *rfolder = REMOTE_FOLDER(folder);
636 /* Check if this is the first try to establish a
637 connection, if yes we don't try to reconnect */
638 debug_print("reconnecting\n");
639 if (rfolder->session == NULL) {
640 log_warning(_("Connecting to %s failed"),
641 folder->account->recv_server);
642 session_destroy(SESSION(session));
645 log_warning(_("IMAP4 connection to %s has been"
646 " disconnected. Reconnecting...\n"),
647 folder->account->recv_server);
648 statusbar_print_all(_("IMAP4 connection to %s has been"
649 " disconnected. Reconnecting...\n"),
650 folder->account->recv_server);
651 SESSION(session)->state = SESSION_DISCONNECTED;
652 session_destroy(SESSION(session));
653 /* Clear folders session to make imap_session_get create
654 a new session, because of rfolder->session == NULL
655 it will not try to reconnect again and so avoid an
657 rfolder->session = NULL;
658 session = imap_session_get(folder);
659 rfolder->session = SESSION(session);
665 #define lock_session() {\
666 debug_print("locking session\n"); \
667 session->busy = TRUE;\
670 #define unlock_session() {\
671 debug_print("unlocking session\n"); \
672 session->busy = FALSE;\
675 static IMAPSession *imap_session_get(Folder *folder)
677 RemoteFolder *rfolder = REMOTE_FOLDER(folder);
678 IMAPSession *session = NULL;
680 g_return_val_if_fail(folder != NULL, NULL);
681 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, NULL);
682 g_return_val_if_fail(folder->account != NULL, NULL);
684 if (prefs_common.work_offline && !inc_offline_should_override()) {
688 /* Make sure we have a session */
689 if (rfolder->session != NULL) {
690 session = IMAP_SESSION(rfolder->session);
691 /* don't do that yet...
696 imap_reset_uid_lists(folder);
697 session = imap_session_new(folder, folder->account);
702 /* Make sure session is authenticated */
703 if (!IMAP_SESSION(session)->authenticated)
704 imap_session_authenticate(IMAP_SESSION(session), folder->account);
706 if (!IMAP_SESSION(session)->authenticated) {
707 session_destroy(SESSION(session));
708 rfolder->session = NULL;
712 /* I think the point of this code is to avoid sending a
713 * keepalive if we've used the session recently and therefore
714 * think it's still alive. Unfortunately, most of the code
715 * does not yet check for errors on the socket, and so if the
716 * connection drops we don't notice until the timeout expires.
717 * A better solution than sending a NOOP every time would be
718 * for every command to be prepared to retry until it is
719 * successfully sent. -- mbp */
720 if (time(NULL) - SESSION(session)->last_access_time > SESSION_TIMEOUT_INTERVAL) {
721 /* verify that the session is still alive */
722 if (imap_cmd_noop(session) != IMAP_SUCCESS) {
723 debug_print("disconnected!\n");
724 session = imap_reconnect_if_possible(folder, session);
728 rfolder->session = SESSION(session);
730 return IMAP_SESSION(session);
733 static IMAPSession *imap_session_new(Folder * folder,
734 const PrefsAccount *account)
736 IMAPSession *session;
742 /* FIXME: IMAP over SSL only... */
745 port = account->set_imapport ? account->imapport
746 : account->ssl_imap == SSL_TUNNEL ? IMAPS_PORT : IMAP4_PORT;
747 ssl_type = account->ssl_imap;
749 if (account->ssl_imap != SSL_NONE) {
750 if (alertpanel_full(_("Insecure connection"),
751 _("This connection is configured to be secured "
752 "using SSL, but SSL is not available in this "
753 "build of Sylpheed-Claws. \n\n"
754 "Do you want to continue connecting to this "
755 "server? The communication would not be "
757 _("Con_tinue connecting"),
758 GTK_STOCK_CANCEL, NULL,
759 FALSE, NULL, ALERT_WARNING,
760 G_ALERTALTERNATE) != G_ALERTDEFAULT)
763 port = account->set_imapport ? account->imapport
768 statusbar_print_all(_("Connecting to IMAP4 server: %s..."), folder->account->recv_server);
769 if (account->set_tunnelcmd) {
770 r = imap_threaded_connect_cmd(folder,
772 account->recv_server,
777 if (ssl_type == SSL_TUNNEL) {
778 r = imap_threaded_connect_ssl(folder,
779 account->recv_server,
785 r = imap_threaded_connect(folder,
786 account->recv_server,
792 if (r == MAILIMAP_NO_ERROR_AUTHENTICATED) {
793 authenticated = TRUE;
795 else if (r == MAILIMAP_NO_ERROR_NON_AUTHENTICATED) {
796 authenticated = FALSE;
799 if(!prefs_common.no_recv_err_panel) {
800 alertpanel_error(_("Can't connect to IMAP4 server: %s:%d"),
801 account->recv_server, port);
803 log_error(_("Can't connect to IMAP4 server: %s:%d\n"),
804 account->recv_server, port);
810 session = g_new0(IMAPSession, 1);
811 session_init(SESSION(session));
812 SESSION(session)->type = SESSION_IMAP;
813 SESSION(session)->server = g_strdup(account->recv_server);
814 SESSION(session)->sock = NULL;
816 SESSION(session)->destroy = imap_session_destroy;
818 session->capability = NULL;
820 session->authenticated = authenticated;
821 session->mbox = NULL;
822 session->cmd_count = 0;
823 session->folder = folder;
824 IMAP_FOLDER(session->folder)->last_seen_separator = 0;
827 if (account->ssl_imap == SSL_STARTTLS) {
830 ok = imap_cmd_starttls(session);
831 if (ok != IMAP_SUCCESS) {
832 log_warning(_("Can't start TLS session.\n"));
833 session_destroy(SESSION(session));
837 imap_free_capabilities(session);
838 session->authenticated = FALSE;
839 session->uidplus = FALSE;
840 session->cmd_count = 1;
843 log_message("IMAP connection is %s-authenticated\n",
844 (session->authenticated) ? "pre" : "un");
849 static void imap_session_authenticate(IMAPSession *session,
850 const PrefsAccount *account)
854 g_return_if_fail(account->userid != NULL);
856 pass = account->passwd;
859 tmp_pass = input_dialog_query_password(account->recv_server, account->userid);
861 tmp_pass = g_strdup(""); /* allow empty password */
862 Xstrdup_a(pass, tmp_pass, {g_free(tmp_pass); return;});
865 statusbar_print_all(_("Connecting to IMAP4 server %s...\n"),
866 account->recv_server);
867 if (imap_auth(session, account->userid, pass, account->imap_auth_type) != IMAP_SUCCESS) {
868 imap_threaded_disconnect(session->folder);
869 imap_cmd_logout(session);
875 session->authenticated = TRUE;
878 static void imap_session_destroy(Session *session)
880 if (session->state != SESSION_DISCONNECTED)
881 imap_threaded_disconnect(IMAP_SESSION(session)->folder);
883 imap_free_capabilities(IMAP_SESSION(session));
884 g_free(IMAP_SESSION(session)->mbox);
885 sock_close(session->sock);
886 session->sock = NULL;
889 static gchar *imap_fetch_msg(Folder *folder, FolderItem *item, gint uid)
891 return imap_fetch_msg_full(folder, item, uid, TRUE, TRUE);
894 static guint get_size_with_crs(MsgInfo *info)
903 fp = procmsg_open_message(info);
907 while (fgets(buf, sizeof (buf), fp) != NULL) {
909 if (!strstr(buf, "\r") && strstr(buf, "\n"))
917 static gchar *imap_fetch_msg_full(Folder *folder, FolderItem *item, gint uid,
918 gboolean headers, gboolean body)
920 gchar *path, *filename;
921 IMAPSession *session;
924 g_return_val_if_fail(folder != NULL, NULL);
925 g_return_val_if_fail(item != NULL, NULL);
930 path = folder_item_get_path(item);
931 if (!is_dir_exist(path))
933 filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(uid), NULL);
936 if (is_file_exist(filename)) {
937 /* see whether the local file represents the whole message
938 * or not. As the IMAP server reports size with \r chars,
939 * we have to update the local file (UNIX \n only) size */
940 MsgInfo *msginfo = imap_parse_msg(filename, item);
941 MsgInfo *cached = msgcache_get_msg(item->cache,uid);
942 guint have_size = get_size_with_crs(msginfo);
945 debug_print("message %d has been already %scached (%d/%d).\n", uid,
946 have_size == cached->size ? "fully ":"",
947 have_size, (int)cached->size);
949 if (cached && (cached->size == have_size || !body)) {
950 procmsg_msginfo_free(cached);
951 procmsg_msginfo_free(msginfo);
952 file_strip_crs(filename);
955 procmsg_msginfo_free(cached);
956 procmsg_msginfo_free(msginfo);
960 session = imap_session_get(folder);
969 debug_print("IMAP fetching messages\n");
970 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
971 NULL, NULL, NULL, NULL, FALSE);
972 if (ok != IMAP_SUCCESS) {
973 g_warning("can't select mailbox %s\n", item->path);
979 debug_print("getting message %d...\n", uid);
980 ok = imap_cmd_fetch(session, (guint32)uid, filename, headers, body);
982 if (ok != IMAP_SUCCESS) {
983 g_warning("can't fetch message %d\n", uid);
990 file_strip_crs(filename);
994 static gint imap_add_msg(Folder *folder, FolderItem *dest,
995 const gchar *file, MsgFlags *flags)
999 MsgFileInfo fileinfo;
1001 g_return_val_if_fail(file != NULL, -1);
1003 fileinfo.msginfo = NULL;
1004 fileinfo.file = (gchar *)file;
1005 fileinfo.flags = flags;
1006 file_list.data = &fileinfo;
1007 file_list.next = NULL;
1009 ret = imap_add_msgs(folder, dest, &file_list, NULL);
1013 static gint imap_add_msgs(Folder *folder, FolderItem *dest, GSList *file_list,
1014 GRelation *relation)
1017 IMAPSession *session;
1018 guint32 last_uid = 0;
1020 MsgFileInfo *fileinfo;
1022 gint curnum = 0, total = 0;
1025 g_return_val_if_fail(folder != NULL, -1);
1026 g_return_val_if_fail(dest != NULL, -1);
1027 g_return_val_if_fail(file_list != NULL, -1);
1029 session = imap_session_get(folder);
1034 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
1036 statusbar_print_all(_("Adding messages..."));
1037 total = g_slist_length(file_list);
1038 for (cur = file_list; cur != NULL; cur = cur->next) {
1039 IMAPFlags iflags = 0;
1040 guint32 new_uid = 0;
1041 gchar *real_file = NULL;
1042 gboolean file_is_tmp = FALSE;
1043 fileinfo = (MsgFileInfo *)cur->data;
1045 statusbar_progress_all(curnum, total, 1);
1048 if (fileinfo->flags) {
1049 if (MSG_IS_MARKED(*fileinfo->flags))
1050 iflags |= IMAP_FLAG_FLAGGED;
1051 if (MSG_IS_REPLIED(*fileinfo->flags))
1052 iflags |= IMAP_FLAG_ANSWERED;
1053 if (!MSG_IS_UNREAD(*fileinfo->flags))
1054 iflags |= IMAP_FLAG_SEEN;
1057 if (fileinfo->flags) {
1058 if ((MSG_IS_QUEUED(*fileinfo->flags)
1059 || MSG_IS_DRAFT(*fileinfo->flags))
1060 && !folder_has_parent_of_type(dest, F_QUEUE)
1061 && !folder_has_parent_of_type(dest, F_DRAFT)) {
1062 real_file = get_tmp_file();
1064 if (procmsg_remove_special_headers(
1074 if (real_file == NULL)
1075 real_file = g_strdup(fileinfo->file);
1077 if (folder_has_parent_of_type(dest, F_QUEUE) ||
1078 folder_has_parent_of_type(dest, F_OUTBOX) ||
1079 folder_has_parent_of_type(dest, F_DRAFT) ||
1080 folder_has_parent_of_type(dest, F_TRASH))
1081 iflags |= IMAP_FLAG_SEEN;
1083 ok = imap_cmd_append(session, destdir, real_file, iflags,
1086 if (ok != IMAP_SUCCESS) {
1087 g_warning("can't append message %s\n", real_file);
1089 g_unlink(real_file);
1093 statusbar_progress_all(0,0,0);
1094 statusbar_pop_all();
1098 if (relation != NULL)
1099 g_relation_insert(relation, fileinfo->msginfo != NULL ?
1100 (gpointer) fileinfo->msginfo : (gpointer) fileinfo,
1101 GINT_TO_POINTER(dest->last_num + 1));
1102 if (last_uid < new_uid)
1105 g_unlink(real_file);
1109 statusbar_progress_all(0,0,0);
1110 statusbar_pop_all();
1119 static gint imap_do_copy_msgs(Folder *folder, FolderItem *dest,
1120 MsgInfoList *msglist, GRelation *relation)
1124 GSList *seq_list, *cur;
1126 IMAPSession *session;
1127 gint ok = IMAP_SUCCESS;
1128 GRelation *uid_mapping;
1131 g_return_val_if_fail(folder != NULL, -1);
1132 g_return_val_if_fail(dest != NULL, -1);
1133 g_return_val_if_fail(msglist != NULL, -1);
1135 session = imap_session_get(folder);
1141 msginfo = (MsgInfo *)msglist->data;
1143 src = msginfo->folder;
1145 g_warning("the src folder is identical to the dest.\n");
1150 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
1151 NULL, NULL, NULL, NULL, FALSE);
1152 if (ok != IMAP_SUCCESS) {
1157 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
1158 seq_list = imap_get_lep_set_from_msglist(msglist);
1159 uid_mapping = g_relation_new(2);
1160 g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
1162 statusbar_print_all(_("Copying messages..."));
1163 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
1164 struct mailimap_set * seq_set;
1165 seq_set = cur->data;
1167 debug_print("Copying messages from %s to %s ...\n",
1168 src->path, destdir);
1170 ok = imap_cmd_copy(session, seq_set, destdir, uid_mapping);
1171 if (ok != IMAP_SUCCESS) {
1172 g_relation_destroy(uid_mapping);
1173 imap_lep_set_free(seq_list);
1179 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
1180 MsgInfo *msginfo = (MsgInfo *)cur->data;
1183 tuples = g_relation_select(uid_mapping,
1184 GINT_TO_POINTER(msginfo->msgnum),
1186 if (tuples->len > 0) {
1187 gint num = GPOINTER_TO_INT(g_tuples_index(tuples, 0, 1));
1188 g_relation_insert(relation, msginfo,
1189 GPOINTER_TO_INT(num));
1193 g_relation_insert(relation, msginfo,
1194 GPOINTER_TO_INT(0));
1195 g_tuples_destroy(tuples);
1197 statusbar_pop_all();
1199 g_relation_destroy(uid_mapping);
1200 imap_lep_set_free(seq_list);
1204 IMAP_FOLDER_ITEM(dest)->lastuid = 0;
1205 IMAP_FOLDER_ITEM(dest)->uid_next = 0;
1206 g_slist_free(IMAP_FOLDER_ITEM(dest)->uid_list);
1207 IMAP_FOLDER_ITEM(dest)->uid_list = NULL;
1210 if (ok == IMAP_SUCCESS)
1216 static gint imap_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1220 g_return_val_if_fail(msginfo != NULL, -1);
1222 msglist.data = msginfo;
1223 msglist.next = NULL;
1225 return imap_copy_msgs(folder, dest, &msglist, NULL);
1228 static gint imap_copy_msgs(Folder *folder, FolderItem *dest,
1229 MsgInfoList *msglist, GRelation *relation)
1235 g_return_val_if_fail(folder != NULL, -1);
1236 g_return_val_if_fail(dest != NULL, -1);
1237 g_return_val_if_fail(msglist != NULL, -1);
1239 msginfo = (MsgInfo *)msglist->data;
1240 g_return_val_if_fail(msginfo->folder != NULL, -1);
1242 if (folder == msginfo->folder->folder &&
1243 !folder_has_parent_of_type(msginfo->folder, F_DRAFT) &&
1244 !folder_has_parent_of_type(msginfo->folder, F_QUEUE)) {
1245 ret = imap_do_copy_msgs(folder, dest, msglist, relation);
1249 file_list = procmsg_get_message_file_list(msglist);
1250 g_return_val_if_fail(file_list != NULL, -1);
1252 ret = imap_add_msgs(folder, dest, file_list, relation);
1254 procmsg_message_file_list_free(file_list);
1260 static gint imap_do_remove_msgs(Folder *folder, FolderItem *dest,
1261 MsgInfoList *msglist, GRelation *relation)
1264 GSList *numlist = NULL, *cur;
1266 IMAPSession *session;
1267 gint ok = IMAP_SUCCESS;
1268 GRelation *uid_mapping;
1270 g_return_val_if_fail(folder != NULL, -1);
1271 g_return_val_if_fail(dest != NULL, -1);
1272 g_return_val_if_fail(msglist != NULL, -1);
1274 session = imap_session_get(folder);
1279 msginfo = (MsgInfo *)msglist->data;
1281 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
1282 NULL, NULL, NULL, NULL, FALSE);
1283 if (ok != IMAP_SUCCESS) {
1288 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
1289 for (cur = msglist; cur; cur = cur->next) {
1290 msginfo = (MsgInfo *)cur->data;
1291 if (!MSG_IS_DELETED(msginfo->flags))
1292 numlist = g_slist_append(numlist, GINT_TO_POINTER(msginfo->msgnum));
1295 uid_mapping = g_relation_new(2);
1296 g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
1298 ok = imap_set_message_flags
1299 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
1300 numlist, IMAP_FLAG_DELETED, TRUE);
1301 if (ok != IMAP_SUCCESS) {
1302 log_warning(_("can't set deleted flags\n"));
1306 ok = imap_cmd_expunge(session);
1307 if (ok != IMAP_SUCCESS) {
1308 log_warning(_("can't expunge\n"));
1313 g_relation_destroy(uid_mapping);
1314 g_slist_free(numlist);
1318 if (ok == IMAP_SUCCESS)
1324 static gint imap_remove_msgs(Folder *folder, FolderItem *dest,
1325 MsgInfoList *msglist, GRelation *relation)
1329 g_return_val_if_fail(folder != NULL, -1);
1330 g_return_val_if_fail(dest != NULL, -1);
1331 if (msglist == NULL)
1334 msginfo = (MsgInfo *)msglist->data;
1335 g_return_val_if_fail(msginfo->folder != NULL, -1);
1337 return imap_do_remove_msgs(folder, dest, msglist, relation);
1340 static gint imap_remove_all_msg(Folder *folder, FolderItem *item)
1342 GSList *list = folder_item_get_msg_list(item);
1343 gint res = imap_remove_msgs(folder, item, list, NULL);
1344 procmsg_msg_list_free(list);
1348 static gboolean imap_is_msg_changed(Folder *folder, FolderItem *item,
1351 /* TODO: properly implement this method */
1355 static gint imap_close(Folder *folder, FolderItem *item)
1360 static gint imap_scan_tree(Folder *folder)
1362 FolderItem *item = NULL;
1363 IMAPSession *session;
1364 gchar *root_folder = NULL;
1366 g_return_val_if_fail(folder != NULL, -1);
1367 g_return_val_if_fail(folder->account != NULL, -1);
1369 session = imap_session_get(folder);
1371 if (!folder->node) {
1372 folder_tree_destroy(folder);
1373 item = folder_item_new(folder, folder->name, NULL);
1374 item->folder = folder;
1375 folder->node = item->node = g_node_new(item);
1381 if (folder->account->imap_dir && *folder->account->imap_dir) {
1386 Xstrdup_a(root_folder, folder->account->imap_dir, {unlock_session();return -1;});
1387 extract_quote(root_folder, '"');
1388 subst_char(root_folder,
1389 imap_get_path_separator(IMAP_FOLDER(folder),
1392 strtailchomp(root_folder, '/');
1393 real_path = imap_get_real_path
1394 (IMAP_FOLDER(folder), root_folder);
1395 debug_print("IMAP root directory: %s\n", real_path);
1397 /* check if root directory exist */
1399 r = imap_threaded_list(session->folder, "", real_path,
1401 if ((r != MAILIMAP_NO_ERROR) || (clist_count(lep_list) == 0)) {
1402 if (!folder->node) {
1403 item = folder_item_new(folder, folder->name, NULL);
1404 item->folder = folder;
1405 folder->node = item->node = g_node_new(item);
1410 mailimap_list_result_free(lep_list);
1416 item = FOLDER_ITEM(folder->node->data);
1417 if (!item || ((item->path || root_folder) &&
1418 strcmp2(item->path, root_folder) != 0)) {
1419 folder_tree_destroy(folder);
1420 item = folder_item_new(folder, folder->name, root_folder);
1421 item->folder = folder;
1422 folder->node = item->node = g_node_new(item);
1425 imap_scan_tree_recursive(session, FOLDER_ITEM(folder->node->data));
1426 imap_create_missing_folders(folder);
1432 static gint imap_scan_tree_recursive(IMAPSession *session, FolderItem *item)
1435 IMAPFolder *imapfolder;
1436 FolderItem *new_item;
1437 GSList *item_list, *cur;
1440 gchar *wildcard_path;
1446 g_return_val_if_fail(item != NULL, -1);
1447 g_return_val_if_fail(item->folder != NULL, -1);
1448 g_return_val_if_fail(item->no_sub == FALSE, -1);
1450 folder = item->folder;
1451 imapfolder = IMAP_FOLDER(folder);
1453 separator = imap_get_path_separator(imapfolder, item->path);
1455 if (folder->ui_func)
1456 folder->ui_func(folder, item, folder->ui_func_data);
1459 wildcard[0] = separator;
1462 real_path = imap_get_real_path(imapfolder, item->path);
1466 real_path = g_strdup("");
1469 Xstrcat_a(wildcard_path, real_path, wildcard,
1470 {g_free(real_path); return IMAP_ERROR;});
1472 r = imap_threaded_list(folder, "", wildcard_path, &lep_list);
1473 if (r != MAILIMAP_NO_ERROR) {
1477 item_list = imap_list_from_lep(imapfolder,
1478 lep_list, real_path, FALSE);
1479 mailimap_list_result_free(lep_list);
1484 node = item->node->children;
1485 while (node != NULL) {
1486 FolderItem *old_item = FOLDER_ITEM(node->data);
1487 GNode *next = node->next;
1490 for (cur = item_list; cur != NULL; cur = cur->next) {
1491 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1492 if (!strcmp2(old_item->path, cur_item->path)) {
1493 new_item = cur_item;
1498 debug_print("folder '%s' not found. removing...\n",
1500 folder_item_remove(old_item);
1502 old_item->no_sub = new_item->no_sub;
1503 old_item->no_select = new_item->no_select;
1504 if (old_item->no_sub == TRUE && node->children) {
1505 debug_print("folder '%s' doesn't have "
1506 "subfolders. removing...\n",
1508 folder_item_remove_children(old_item);
1515 for (cur = item_list; cur != NULL; cur = cur->next) {
1516 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1519 for (node = item->node->children; node != NULL;
1520 node = node->next) {
1521 if (!strcmp2(FOLDER_ITEM(node->data)->path,
1523 new_item = FOLDER_ITEM(node->data);
1524 folder_item_destroy(cur_item);
1530 new_item = cur_item;
1531 debug_print("new folder '%s' found.\n", new_item->path);
1532 folder_item_append(item, new_item);
1535 if (!strcmp(new_item->path, "INBOX")) {
1536 new_item->stype = F_INBOX;
1537 folder->inbox = new_item;
1538 } else if (!folder_item_parent(item) || item->stype == F_INBOX) {
1541 base = g_path_get_basename(new_item->path);
1543 if (!folder->outbox && !g_ascii_strcasecmp(base, "Sent")) {
1544 new_item->stype = F_OUTBOX;
1545 folder->outbox = new_item;
1546 } else if (!folder->draft && !g_ascii_strcasecmp(base, "Drafts")) {
1547 new_item->stype = F_DRAFT;
1548 folder->draft = new_item;
1549 } else if (!folder->queue && !g_ascii_strcasecmp(base, "Queue")) {
1550 new_item->stype = F_QUEUE;
1551 folder->queue = new_item;
1552 } else if (!folder->trash && !g_ascii_strcasecmp(base, "Trash")) {
1553 new_item->stype = F_TRASH;
1554 folder->trash = new_item;
1559 if (new_item->no_sub == FALSE)
1560 imap_scan_tree_recursive(session, new_item);
1563 g_slist_free(item_list);
1565 return IMAP_SUCCESS;
1568 static gint imap_create_tree(Folder *folder)
1570 g_return_val_if_fail(folder != NULL, -1);
1571 g_return_val_if_fail(folder->node != NULL, -1);
1572 g_return_val_if_fail(folder->node->data != NULL, -1);
1573 g_return_val_if_fail(folder->account != NULL, -1);
1575 imap_scan_tree(folder);
1576 imap_create_missing_folders(folder);
1581 static void imap_create_missing_folders(Folder *folder)
1583 g_return_if_fail(folder != NULL);
1586 folder->inbox = imap_create_special_folder
1587 (folder, F_INBOX, "INBOX");
1589 folder->trash = imap_create_special_folder
1590 (folder, F_TRASH, "Trash");
1592 folder->queue = imap_create_special_folder
1593 (folder, F_QUEUE, "Queue");
1594 if (!folder->outbox)
1595 folder->outbox = imap_create_special_folder
1596 (folder, F_OUTBOX, "Sent");
1598 folder->draft = imap_create_special_folder
1599 (folder, F_DRAFT, "Drafts");
1602 static FolderItem *imap_create_special_folder(Folder *folder,
1603 SpecialFolderItemType stype,
1607 FolderItem *new_item;
1609 g_return_val_if_fail(folder != NULL, NULL);
1610 g_return_val_if_fail(folder->node != NULL, NULL);
1611 g_return_val_if_fail(folder->node->data != NULL, NULL);
1612 g_return_val_if_fail(folder->account != NULL, NULL);
1613 g_return_val_if_fail(name != NULL, NULL);
1615 item = FOLDER_ITEM(folder->node->data);
1616 new_item = imap_create_folder(folder, item, name);
1619 g_warning("Can't create '%s'\n", name);
1620 if (!folder->inbox) return NULL;
1622 new_item = imap_create_folder(folder, folder->inbox, name);
1624 g_warning("Can't create '%s' under INBOX\n", name);
1626 new_item->stype = stype;
1628 new_item->stype = stype;
1633 static gchar *imap_folder_get_path(Folder *folder)
1637 g_return_val_if_fail(folder != NULL, NULL);
1638 g_return_val_if_fail(folder->account != NULL, NULL);
1640 folder_path = g_strconcat(get_imap_cache_dir(),
1642 folder->account->recv_server,
1644 folder->account->userid,
1650 static gchar *imap_item_get_path(Folder *folder, FolderItem *item)
1652 gchar *folder_path, *path;
1654 g_return_val_if_fail(folder != NULL, NULL);
1655 g_return_val_if_fail(item != NULL, NULL);
1656 folder_path = imap_folder_get_path(folder);
1658 g_return_val_if_fail(folder_path != NULL, NULL);
1659 if (folder_path[0] == G_DIR_SEPARATOR) {
1661 path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
1664 path = g_strdup(folder_path);
1667 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1668 folder_path, G_DIR_SEPARATOR_S,
1671 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1674 g_free(folder_path);
1679 static FolderItem *imap_create_folder(Folder *folder, FolderItem *parent,
1682 gchar *dirpath, *imap_path;
1683 IMAPSession *session;
1684 FolderItem *new_item;
1689 gboolean no_select = FALSE, no_sub = FALSE;
1691 g_return_val_if_fail(folder != NULL, NULL);
1692 g_return_val_if_fail(folder->account != NULL, NULL);
1693 g_return_val_if_fail(parent != NULL, NULL);
1694 g_return_val_if_fail(name != NULL, NULL);
1696 session = imap_session_get(folder);
1702 if (!folder_item_parent(parent) && strcmp(name, "INBOX") == 0) {
1703 dirpath = g_strdup(name);
1704 }else if (parent->path)
1705 dirpath = g_strconcat(parent->path, "/", name, NULL);
1706 else if ((p = strchr(name, '/')) != NULL && *(p + 1) != '\0')
1707 dirpath = g_strdup(name);
1708 else if (folder->account->imap_dir && *folder->account->imap_dir) {
1711 Xstrdup_a(imap_dir, folder->account->imap_dir, {unlock_session();return NULL;});
1712 strtailchomp(imap_dir, '/');
1713 dirpath = g_strconcat(imap_dir, "/", name, NULL);
1715 dirpath = g_strdup(name);
1719 /* keep trailing directory separator to create a folder that contains
1721 imap_path = imap_utf8_to_modified_utf7(dirpath);
1723 strtailchomp(dirpath, '/');
1724 Xstrdup_a(new_name, name, {
1729 separator = imap_get_path_separator(IMAP_FOLDER(folder), imap_path);
1730 imap_path_separator_subst(imap_path, separator);
1731 /* remove trailing / for display */
1732 strtailchomp(new_name, '/');
1734 if (strcmp(dirpath, "INBOX") != 0) {
1736 gboolean exist = FALSE;
1740 argbuf = g_ptr_array_new();
1741 r = imap_threaded_list(folder, "", imap_path, &lep_list);
1742 if (r != MAILIMAP_NO_ERROR) {
1743 log_warning(_("can't create mailbox: LIST failed\n"));
1746 ptr_array_free_strings(argbuf);
1747 g_ptr_array_free(argbuf, TRUE);
1752 if (clist_count(lep_list) > 0)
1754 mailimap_list_result_free(lep_list);
1757 ok = imap_cmd_create(session, imap_path);
1758 if (ok != IMAP_SUCCESS) {
1759 log_warning(_("can't create mailbox\n"));
1765 r = imap_threaded_list(folder, "", imap_path, &lep_list);
1766 if (r == MAILIMAP_NO_ERROR) {
1767 GSList *item_list = imap_list_from_lep(IMAP_FOLDER(folder),
1768 lep_list, dirpath, TRUE);
1770 FolderItem *cur_item = FOLDER_ITEM(item_list->data);
1771 no_select = cur_item->no_select;
1772 no_sub = cur_item->no_sub;
1773 g_slist_free(item_list);
1775 mailimap_list_result_free(lep_list);
1782 /* just get flags */
1783 r = imap_threaded_list(folder, "", "INBOX", &lep_list);
1784 if (r == MAILIMAP_NO_ERROR) {
1785 GSList *item_list = imap_list_from_lep(IMAP_FOLDER(folder),
1786 lep_list, dirpath, TRUE);
1788 FolderItem *cur_item = FOLDER_ITEM(item_list->data);
1789 no_select = cur_item->no_select;
1790 no_sub = cur_item->no_sub;
1791 g_slist_free(item_list);
1793 mailimap_list_result_free(lep_list);
1797 new_item = folder_item_new(folder, new_name, dirpath);
1798 new_item->no_select = no_select;
1799 new_item->no_sub = no_sub;
1800 folder_item_append(parent, new_item);
1804 dirpath = folder_item_get_path(new_item);
1805 if (!is_dir_exist(dirpath))
1806 make_dir_hier(dirpath);
1812 static gint imap_rename_folder(Folder *folder, FolderItem *item,
1817 gchar *real_oldpath;
1818 gchar *real_newpath;
1820 gchar *old_cache_dir;
1821 gchar *new_cache_dir;
1822 IMAPSession *session;
1825 gint exists, recent, unseen;
1826 guint32 uid_validity;
1828 g_return_val_if_fail(folder != NULL, -1);
1829 g_return_val_if_fail(item != NULL, -1);
1830 g_return_val_if_fail(item->path != NULL, -1);
1831 g_return_val_if_fail(name != NULL, -1);
1833 session = imap_session_get(folder);
1839 if (strchr(name, imap_get_path_separator(IMAP_FOLDER(folder), item->path)) != NULL) {
1840 g_warning(_("New folder name must not contain the namespace "
1846 real_oldpath = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1848 g_free(session->mbox);
1849 session->mbox = NULL;
1850 ok = imap_cmd_examine(session, "INBOX",
1851 &exists, &recent, &unseen, &uid_validity, FALSE);
1852 if (ok != IMAP_SUCCESS) {
1853 g_free(real_oldpath);
1858 separator = imap_get_path_separator(IMAP_FOLDER(folder), item->path);
1859 if (strchr(item->path, G_DIR_SEPARATOR)) {
1860 dirpath = g_path_get_dirname(item->path);
1861 newpath = g_strconcat(dirpath, G_DIR_SEPARATOR_S, name, NULL);
1864 newpath = g_strdup(name);
1866 real_newpath = imap_utf8_to_modified_utf7(newpath);
1867 imap_path_separator_subst(real_newpath, separator);
1869 ok = imap_cmd_rename(session, real_oldpath, real_newpath);
1870 if (ok != IMAP_SUCCESS) {
1871 log_warning(_("can't rename mailbox: %s to %s\n"),
1872 real_oldpath, real_newpath);
1873 g_free(real_oldpath);
1875 g_free(real_newpath);
1881 item->name = g_strdup(name);
1883 old_cache_dir = folder_item_get_path(item);
1885 paths[0] = g_strdup(item->path);
1887 g_node_traverse(item->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1888 imap_rename_folder_func, paths);
1890 if (is_dir_exist(old_cache_dir)) {
1891 new_cache_dir = folder_item_get_path(item);
1892 if (rename(old_cache_dir, new_cache_dir) < 0) {
1893 FILE_OP_ERROR(old_cache_dir, "rename");
1895 g_free(new_cache_dir);
1898 g_free(old_cache_dir);
1901 g_free(real_oldpath);
1902 g_free(real_newpath);
1907 static gint imap_remove_folder_real(Folder *folder, FolderItem *item)
1910 IMAPSession *session;
1914 g_return_val_if_fail(folder != NULL, -1);
1915 g_return_val_if_fail(item != NULL, -1);
1916 g_return_val_if_fail(item->path != NULL, -1);
1918 session = imap_session_get(folder);
1923 path = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1925 ok = imap_cmd_delete(session, path);
1926 if (ok != IMAP_SUCCESS) {
1927 gchar *tmp = g_strdup_printf("%s%c", path,
1928 imap_get_path_separator(IMAP_FOLDER(folder), path));
1931 ok = imap_cmd_delete(session, path);
1934 if (ok != IMAP_SUCCESS) {
1935 log_warning(_("can't delete mailbox\n"));
1942 cache_dir = folder_item_get_path(item);
1943 if (is_dir_exist(cache_dir) && remove_dir_recursive(cache_dir) < 0)
1944 g_warning("can't remove directory '%s'\n", cache_dir);
1946 folder_item_remove(item);
1951 static gint imap_remove_folder(Folder *folder, FolderItem *item)
1955 g_return_val_if_fail(item != NULL, -1);
1956 g_return_val_if_fail(item->folder != NULL, -1);
1957 g_return_val_if_fail(item->node != NULL, -1);
1959 node = item->node->children;
1960 while (node != NULL) {
1962 if (imap_remove_folder(folder, FOLDER_ITEM(node->data)) < 0)
1966 debug_print("IMAP removing %s\n", item->path);
1968 if (imap_remove_all_msg(folder, item) < 0)
1970 return imap_remove_folder_real(folder, item);
1973 typedef struct _uncached_data {
1974 IMAPSession *session;
1976 MsgNumberList *numlist;
1982 static void *imap_get_uncached_messages_thread(void *data)
1984 uncached_data *stuff = (uncached_data *)data;
1985 IMAPSession *session = stuff->session;
1986 FolderItem *item = stuff->item;
1987 MsgNumberList *numlist = stuff->numlist;
1989 GSList *newlist = NULL;
1990 GSList *llast = NULL;
1991 GSList *seq_list, *cur;
1993 debug_print("uncached_messages\n");
1995 if (session == NULL || item == NULL || item->folder == NULL
1996 || FOLDER_CLASS(item->folder) != &imap_class) {
2001 seq_list = imap_get_lep_set_from_numlist(numlist);
2002 debug_print("get msgs info\n");
2003 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
2004 struct mailimap_set * imapset;
2010 imapset = cur->data;
2012 r = imap_threaded_fetch_env(session->folder,
2013 imapset, &env_list);
2014 if (r != MAILIMAP_NO_ERROR)
2018 for(i = 0 ; i < carray_count(env_list) ; i ++) {
2019 struct imap_fetch_env_info * info;
2022 info = carray_get(env_list, i);
2023 msginfo = imap_envelope_from_lep(info, item);
2024 if (msginfo == NULL)
2026 msginfo->folder = item;
2028 llast = newlist = g_slist_append(newlist, msginfo);
2030 llast = g_slist_append(llast, msginfo);
2031 llast = llast->next;
2036 imap_fetch_env_free(env_list);
2039 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
2040 struct mailimap_set * imapset;
2042 imapset = cur->data;
2043 mailimap_set_free(imapset);
2046 session_set_access_time(SESSION(session));
2051 #define MAX_MSG_NUM 50
2053 static GSList *imap_get_uncached_messages(IMAPSession *session,
2055 MsgNumberList *numlist)
2057 GSList *result = NULL;
2059 uncached_data *data = g_new0(uncached_data, 1);
2064 data->total = g_slist_length(numlist);
2065 debug_print("messages list : %i\n", data->total);
2067 while (cur != NULL) {
2068 GSList * partial_result;
2076 while (count < MAX_MSG_NUM) {
2081 if (newlist == NULL)
2082 llast = newlist = g_slist_append(newlist, p);
2084 llast = g_slist_append(llast, p);
2085 llast = llast->next;
2095 data->session = session;
2097 data->numlist = newlist;
2100 if (prefs_common.work_offline && !inc_offline_should_override()) {
2106 (GSList *)imap_get_uncached_messages_thread(data);
2108 statusbar_progress_all(data->cur,data->total, 1);
2110 g_slist_free(newlist);
2112 result = g_slist_concat(result, partial_result);
2116 statusbar_progress_all(0,0,0);
2117 statusbar_pop_all();
2122 static void imap_delete_all_cached_messages(FolderItem *item)
2126 g_return_if_fail(item != NULL);
2127 g_return_if_fail(item->folder != NULL);
2128 g_return_if_fail(FOLDER_CLASS(item->folder) == &imap_class);
2130 debug_print("Deleting all cached messages...\n");
2132 dir = folder_item_get_path(item);
2133 if (is_dir_exist(dir))
2134 remove_all_numbered_files(dir);
2137 debug_print("done.\n");
2140 static IMAPNameSpace *imap_find_namespace_from_list(GList *ns_list,
2143 IMAPNameSpace *namespace = NULL;
2144 gchar *tmp_path, *name;
2146 if (!path) path = "";
2148 for (; ns_list != NULL; ns_list = ns_list->next) {
2149 IMAPNameSpace *tmp_ns = ns_list->data;
2151 Xstrcat_a(tmp_path, path, "/", return namespace);
2152 Xstrdup_a(name, tmp_ns->name, return namespace);
2153 if (tmp_ns->separator && tmp_ns->separator != '/') {
2154 subst_char(tmp_path, tmp_ns->separator, '/');
2155 subst_char(name, tmp_ns->separator, '/');
2157 if (strncmp(tmp_path, name, strlen(name)) == 0)
2164 static IMAPNameSpace *imap_find_namespace(IMAPFolder *folder,
2167 IMAPNameSpace *namespace;
2169 g_return_val_if_fail(folder != NULL, NULL);
2171 namespace = imap_find_namespace_from_list(folder->ns_personal, path);
2172 if (namespace) return namespace;
2173 namespace = imap_find_namespace_from_list(folder->ns_others, path);
2174 if (namespace) return namespace;
2175 namespace = imap_find_namespace_from_list(folder->ns_shared, path);
2176 if (namespace) return namespace;
2182 static gchar imap_get_path_separator(IMAPFolder *folder, const gchar *path)
2184 IMAPNameSpace *namespace;
2185 gchar separator = '/';
2187 if (folder->last_seen_separator == 0) {
2189 int r = imap_threaded_list((Folder *)folder, "", "", &lep_list);
2190 if (r != MAILIMAP_NO_ERROR) {
2191 log_warning(_("LIST failed\n"));
2195 if (clist_count(lep_list) > 0) {
2196 clistiter * iter = clist_begin(lep_list);
2197 struct mailimap_mailbox_list * mb;
2198 mb = clist_content(iter);
2200 folder->last_seen_separator = mb->mb_delimiter;
2201 debug_print("got separator: %c\n", folder->last_seen_separator);
2203 mailimap_list_result_free(lep_list);
2206 if (folder->last_seen_separator != 0) {
2207 debug_print("using separator: %c\n", folder->last_seen_separator);
2208 return folder->last_seen_separator;
2211 namespace = imap_find_namespace(folder, path);
2212 if (namespace && namespace->separator)
2213 separator = namespace->separator;
2218 static gchar *imap_get_real_path(IMAPFolder *folder, const gchar *path)
2223 g_return_val_if_fail(folder != NULL, NULL);
2224 g_return_val_if_fail(path != NULL, NULL);
2226 real_path = imap_utf8_to_modified_utf7(path);
2227 separator = imap_get_path_separator(folder, path);
2228 imap_path_separator_subst(real_path, separator);
2233 static gint imap_set_message_flags(IMAPSession *session,
2234 MsgNumberList *numlist,
2242 seq_list = imap_get_lep_set_from_numlist(numlist);
2244 for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) {
2245 struct mailimap_set * imapset;
2247 imapset = cur->data;
2249 ok = imap_cmd_store(session, imapset,
2253 imap_lep_set_free(seq_list);
2255 return IMAP_SUCCESS;
2258 typedef struct _select_data {
2259 IMAPSession *session;
2264 guint32 *uid_validity;
2268 static gint imap_select(IMAPSession *session, IMAPFolder *folder,
2270 gint *exists, gint *recent, gint *unseen,
2271 guint32 *uid_validity, gboolean block)
2275 gint exists_, recent_, unseen_;
2276 guint32 uid_validity_;
2278 if (!exists && !recent && !unseen && !uid_validity) {
2279 if (session->mbox && strcmp(session->mbox, path) == 0)
2280 return IMAP_SUCCESS;
2289 uid_validity = &uid_validity_;
2291 g_free(session->mbox);
2292 session->mbox = NULL;
2294 real_path = imap_get_real_path(folder, path);
2296 ok = imap_cmd_select(session, real_path,
2297 exists, recent, unseen, uid_validity, block);
2298 if (ok != IMAP_SUCCESS)
2299 log_warning(_("can't select folder: %s\n"), real_path);
2301 session->mbox = g_strdup(path);
2302 session->folder_content_changed = FALSE;
2309 static gint imap_status(IMAPSession *session, IMAPFolder *folder,
2310 const gchar *path, IMAPFolderItem *item,
2312 guint32 *uid_next, guint32 *uid_validity,
2313 gint *unseen, gboolean block)
2317 struct mailimap_mailbox_data_status * data_status;
2322 real_path = imap_get_real_path(folder, path);
2336 r = imap_threaded_status(FOLDER(folder), real_path,
2337 &data_status, mask);
2340 if (r != MAILIMAP_NO_ERROR) {
2341 debug_print("status err %d\n", r);
2345 if (data_status->st_info_list == NULL) {
2346 mailimap_mailbox_data_status_free(data_status);
2347 debug_print("status->st_info_list == NULL\n");
2352 for(iter = clist_begin(data_status->st_info_list) ; iter != NULL ;
2353 iter = clist_next(iter)) {
2354 struct mailimap_status_info * info;
2356 info = clist_content(iter);
2357 switch (info->st_att) {
2358 case MAILIMAP_STATUS_ATT_MESSAGES:
2359 * messages = info->st_value;
2360 got_values |= 1 << 0;
2363 case MAILIMAP_STATUS_ATT_UIDNEXT:
2364 * uid_next = info->st_value;
2365 got_values |= 1 << 2;
2368 case MAILIMAP_STATUS_ATT_UIDVALIDITY:
2369 * uid_validity = info->st_value;
2370 got_values |= 1 << 3;
2373 case MAILIMAP_STATUS_ATT_UNSEEN:
2374 * unseen = info->st_value;
2375 got_values |= 1 << 4;
2379 mailimap_mailbox_data_status_free(data_status);
2381 if (got_values != mask) {
2382 debug_print("status: incomplete values received (%d)\n", got_values);
2385 return IMAP_SUCCESS;
2388 static void imap_free_capabilities(IMAPSession *session)
2390 slist_free_strings(session->capability);
2391 g_slist_free(session->capability);
2392 session->capability = NULL;
2395 /* low-level IMAP4rev1 commands */
2398 static gint imap_cmd_authenticate(IMAPSession *session, const gchar *user,
2399 const gchar *pass, IMAPAuthType type)
2406 gchar hexdigest[33];
2410 auth_type = "CRAM-MD5";
2412 imap_gen_send(session, "AUTHENTICATE %s", auth_type);
2413 ok = imap_gen_recv(session, &buf);
2414 if (ok != IMAP_SUCCESS || buf[0] != '+' || buf[1] != ' ') {
2419 challenge = g_malloc(strlen(buf + 2) + 1);
2420 challenge_len = base64_decode(challenge, buf + 2, -1);
2421 challenge[challenge_len] = '\0';
2424 md5_hex_hmac(hexdigest, challenge, challenge_len, pass, strlen(pass));
2427 response = g_strdup_printf("%s %s", user, hexdigest);
2428 response64 = g_malloc((strlen(response) + 3) * 2 + 1);
2429 base64_encode(response64, response, strlen(response));
2432 sock_puts(SESSION(session)->sock, response64);
2433 ok = imap_cmd_ok(session, NULL);
2434 if (ok != IMAP_SUCCESS)
2435 log_warning(_("IMAP4 authentication failed.\n"));
2441 static gint imap_cmd_login(IMAPSession *session,
2442 const gchar *user, const gchar *pass,
2448 log_print("IMAP4> Logging %s to %s using %s\n",
2450 SESSION(session)->server,
2452 r = imap_threaded_login(session->folder, user, pass, type);
2453 if (r != MAILIMAP_NO_ERROR) {
2454 log_error("IMAP4< Error logging in to %s\n",
2455 SESSION(session)->server);
2463 static gint imap_cmd_logout(IMAPSession *session)
2465 imap_threaded_disconnect(session->folder);
2467 return IMAP_SUCCESS;
2470 static gint imap_cmd_noop(IMAPSession *session)
2473 unsigned int exists;
2475 r = imap_threaded_noop(session->folder, &exists);
2476 if (r != MAILIMAP_NO_ERROR) {
2477 debug_print("noop err %d\n", r);
2480 session->exists = exists;
2481 session_set_access_time(SESSION(session));
2483 return IMAP_SUCCESS;
2487 static gint imap_cmd_starttls(IMAPSession *session)
2491 r = imap_threaded_starttls(session->folder);
2492 if (r != MAILIMAP_NO_ERROR) {
2493 debug_print("starttls err %d\n", r);
2496 return IMAP_SUCCESS;
2500 static gint imap_cmd_select(IMAPSession *session, const gchar *folder,
2501 gint *exists, gint *recent, gint *unseen,
2502 guint32 *uid_validity, gboolean block)
2506 r = imap_threaded_select(session->folder, folder,
2507 exists, recent, unseen, uid_validity);
2508 if (r != MAILIMAP_NO_ERROR) {
2509 debug_print("select err %d\n", r);
2512 return IMAP_SUCCESS;
2515 static gint imap_cmd_examine(IMAPSession *session, const gchar *folder,
2516 gint *exists, gint *recent, gint *unseen,
2517 guint32 *uid_validity, gboolean block)
2521 r = imap_threaded_examine(session->folder, folder,
2522 exists, recent, unseen, uid_validity);
2523 if (r != MAILIMAP_NO_ERROR) {
2524 debug_print("examine err %d\n", r);
2528 return IMAP_SUCCESS;
2531 static gint imap_cmd_create(IMAPSession *session, const gchar *folder)
2535 r = imap_threaded_create(session->folder, folder);
2536 if (r != MAILIMAP_NO_ERROR) {
2541 return IMAP_SUCCESS;
2544 static gint imap_cmd_rename(IMAPSession *session, const gchar *old_folder,
2545 const gchar *new_folder)
2549 r = imap_threaded_rename(session->folder, old_folder,
2551 if (r != MAILIMAP_NO_ERROR) {
2556 return IMAP_SUCCESS;
2559 static gint imap_cmd_delete(IMAPSession *session, const gchar *folder)
2564 r = imap_threaded_delete(session->folder, folder);
2565 if (r != MAILIMAP_NO_ERROR) {
2570 return IMAP_SUCCESS;
2573 typedef struct _fetch_data {
2574 IMAPSession *session;
2576 const gchar *filename;
2582 static void *imap_cmd_fetch_thread(void *data)
2584 fetch_data *stuff = (fetch_data *)data;
2585 IMAPSession *session = stuff->session;
2586 guint32 uid = stuff->uid;
2587 const gchar *filename = stuff->filename;
2591 r = imap_threaded_fetch_content(session->folder,
2595 r = imap_threaded_fetch_content(session->folder,
2598 if (r != MAILIMAP_NO_ERROR) {
2599 debug_print("fetch err %d\n", r);
2600 return GINT_TO_POINTER(IMAP_ERROR);
2602 return GINT_TO_POINTER(IMAP_SUCCESS);
2605 static gint imap_cmd_fetch(IMAPSession *session, guint32 uid,
2606 const gchar *filename, gboolean headers,
2609 fetch_data *data = g_new0(fetch_data, 1);
2612 data->session = session;
2614 data->filename = filename;
2615 data->headers = headers;
2618 if (prefs_common.work_offline && !inc_offline_should_override()) {
2622 statusbar_print_all(_("Fetching message..."));
2623 result = GPOINTER_TO_INT(imap_cmd_fetch_thread(data));
2624 statusbar_pop_all();
2630 static gint imap_cmd_append(IMAPSession *session, const gchar *destfolder,
2631 const gchar *file, IMAPFlags flags,
2634 struct mailimap_flag_list * flag_list;
2637 g_return_val_if_fail(file != NULL, IMAP_ERROR);
2639 flag_list = imap_flag_to_lep(flags);
2640 r = imap_threaded_append(session->folder, destfolder,
2642 mailimap_flag_list_free(flag_list);
2643 if (new_uid != NULL)
2646 if (r != MAILIMAP_NO_ERROR) {
2647 debug_print("append err %d\n", r);
2650 return IMAP_SUCCESS;
2653 static gint imap_cmd_copy(IMAPSession *session, struct mailimap_set * set,
2654 const gchar *destfolder, GRelation *uid_mapping)
2658 g_return_val_if_fail(session != NULL, IMAP_ERROR);
2659 g_return_val_if_fail(set != NULL, IMAP_ERROR);
2660 g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
2662 r = imap_threaded_copy(session->folder, set, destfolder);
2663 if (r != MAILIMAP_NO_ERROR) {
2668 return IMAP_SUCCESS;
2671 static gint imap_cmd_store(IMAPSession *session, struct mailimap_set * set,
2672 IMAPFlags flags, int do_add)
2675 struct mailimap_flag_list * flag_list;
2676 struct mailimap_store_att_flags * store_att_flags;
2678 flag_list = imap_flag_to_lep(flags);
2682 mailimap_store_att_flags_new_add_flags_silent(flag_list);
2685 mailimap_store_att_flags_new_remove_flags_silent(flag_list);
2687 r = imap_threaded_store(session->folder, set, store_att_flags);
2688 mailimap_store_att_flags_free(store_att_flags);
2689 if (r != MAILIMAP_NO_ERROR) {
2694 return IMAP_SUCCESS;
2697 static gint imap_cmd_expunge(IMAPSession *session)
2701 if (prefs_common.work_offline && !inc_offline_should_override()) {
2705 r = imap_threaded_expunge(session->folder);
2706 if (r != MAILIMAP_NO_ERROR) {
2711 return IMAP_SUCCESS;
2714 static void imap_path_separator_subst(gchar *str, gchar separator)
2717 gboolean in_escape = FALSE;
2719 if (!separator || separator == '/') return;
2721 for (p = str; *p != '\0'; p++) {
2722 if (*p == '/' && !in_escape)
2724 else if (*p == '&' && *(p + 1) != '-' && !in_escape)
2726 else if (*p == '-' && in_escape)
2731 static gchar *imap_modified_utf7_to_utf8(const gchar *mutf7_str)
2733 static iconv_t cd = (iconv_t)-1;
2734 static gboolean iconv_ok = TRUE;
2737 size_t norm_utf7_len;
2739 gchar *to_str, *to_p;
2741 gboolean in_escape = FALSE;
2743 if (!iconv_ok) return g_strdup(mutf7_str);
2745 if (cd == (iconv_t)-1) {
2746 cd = iconv_open(CS_INTERNAL, CS_UTF_7);
2747 if (cd == (iconv_t)-1) {
2748 g_warning("iconv cannot convert UTF-7 to %s\n",
2751 return g_strdup(mutf7_str);
2755 /* modified UTF-7 to normal UTF-7 conversion */
2756 norm_utf7 = g_string_new(NULL);
2758 for (p = mutf7_str; *p != '\0'; p++) {
2759 /* replace: '&' -> '+',
2761 escaped ',' -> '/' */
2762 if (!in_escape && *p == '&') {
2763 if (*(p + 1) != '-') {
2764 g_string_append_c(norm_utf7, '+');
2767 g_string_append_c(norm_utf7, '&');
2770 } else if (in_escape && *p == ',') {
2771 g_string_append_c(norm_utf7, '/');
2772 } else if (in_escape && *p == '-') {
2773 g_string_append_c(norm_utf7, '-');
2776 g_string_append_c(norm_utf7, *p);
2780 norm_utf7_p = norm_utf7->str;
2781 norm_utf7_len = norm_utf7->len;
2782 to_len = strlen(mutf7_str) * 5;
2783 to_p = to_str = g_malloc(to_len + 1);
2785 if (iconv(cd, (ICONV_CONST gchar **)&norm_utf7_p, &norm_utf7_len,
2786 &to_p, &to_len) == -1) {
2787 g_warning(_("iconv cannot convert UTF-7 to %s\n"),
2788 conv_get_locale_charset_str());
2789 g_string_free(norm_utf7, TRUE);
2791 return g_strdup(mutf7_str);
2794 /* second iconv() call for flushing */
2795 iconv(cd, NULL, NULL, &to_p, &to_len);
2796 g_string_free(norm_utf7, TRUE);
2802 static gchar *imap_utf8_to_modified_utf7(const gchar *from)
2804 static iconv_t cd = (iconv_t)-1;
2805 static gboolean iconv_ok = TRUE;
2806 gchar *norm_utf7, *norm_utf7_p;
2807 size_t from_len, norm_utf7_len;
2809 gchar *from_tmp, *to, *p;
2810 gboolean in_escape = FALSE;
2812 if (!iconv_ok) return g_strdup(from);
2814 if (cd == (iconv_t)-1) {
2815 cd = iconv_open(CS_UTF_7, CS_INTERNAL);
2816 if (cd == (iconv_t)-1) {
2817 g_warning(_("iconv cannot convert %s to UTF-7\n"),
2820 return g_strdup(from);
2824 /* UTF-8 to normal UTF-7 conversion */
2825 Xstrdup_a(from_tmp, from, return g_strdup(from));
2826 from_len = strlen(from);
2827 norm_utf7_len = from_len * 5;
2828 Xalloca(norm_utf7, norm_utf7_len + 1, return g_strdup(from));
2829 norm_utf7_p = norm_utf7;
2831 #define IS_PRINT(ch) (isprint(ch) && IS_ASCII(ch))
2833 while (from_len > 0) {
2834 if (*from_tmp == '+') {
2835 *norm_utf7_p++ = '+';
2836 *norm_utf7_p++ = '-';
2840 } else if (IS_PRINT(*(guchar *)from_tmp)) {
2841 /* printable ascii char */
2842 *norm_utf7_p = *from_tmp;
2848 size_t conv_len = 0;
2850 /* unprintable char: convert to UTF-7 */
2852 while (!IS_PRINT(*(guchar *)p) && conv_len < from_len) {
2853 conv_len += g_utf8_skip[*(guchar *)p];
2854 p += g_utf8_skip[*(guchar *)p];
2857 from_len -= conv_len;
2858 if (iconv(cd, (ICONV_CONST gchar **)&from_tmp,
2860 &norm_utf7_p, &norm_utf7_len) == -1) {
2861 g_warning(_("iconv cannot convert UTF-8 to UTF-7\n"));
2862 return g_strdup(from);
2865 /* second iconv() call for flushing */
2866 iconv(cd, NULL, NULL, &norm_utf7_p, &norm_utf7_len);
2872 *norm_utf7_p = '\0';
2873 to_str = g_string_new(NULL);
2874 for (p = norm_utf7; p < norm_utf7_p; p++) {
2875 /* replace: '&' -> "&-",
2878 BASE64 '/' -> ',' */
2879 if (!in_escape && *p == '&') {
2880 g_string_append(to_str, "&-");
2881 } else if (!in_escape && *p == '+') {
2882 if (*(p + 1) == '-') {
2883 g_string_append_c(to_str, '+');
2886 g_string_append_c(to_str, '&');
2889 } else if (in_escape && *p == '/') {
2890 g_string_append_c(to_str, ',');
2891 } else if (in_escape && *p == '-') {
2892 g_string_append_c(to_str, '-');
2895 g_string_append_c(to_str, *p);
2901 g_string_append_c(to_str, '-');
2905 g_string_free(to_str, FALSE);
2910 static gboolean imap_rename_folder_func(GNode *node, gpointer data)
2912 FolderItem *item = node->data;
2913 gchar **paths = data;
2914 const gchar *oldpath = paths[0];
2915 const gchar *newpath = paths[1];
2917 gchar *new_itempath;
2920 oldpathlen = strlen(oldpath);
2921 if (strncmp(oldpath, item->path, oldpathlen) != 0) {
2922 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
2926 base = item->path + oldpathlen;
2927 while (*base == G_DIR_SEPARATOR) base++;
2929 new_itempath = g_strdup(newpath);
2931 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
2934 item->path = new_itempath;
2939 typedef struct _get_list_uid_data {
2941 IMAPSession *session;
2942 IMAPFolderItem *item;
2943 GSList **msgnum_list;
2945 } get_list_uid_data;
2947 static void *get_list_of_uids_thread(void *data)
2949 get_list_uid_data *stuff = (get_list_uid_data *)data;
2950 Folder *folder = stuff->folder;
2951 IMAPFolderItem *item = stuff->item;
2952 GSList **msgnum_list = stuff->msgnum_list;
2953 gint ok, nummsgs = 0, lastuid_old;
2954 IMAPSession *session;
2955 GSList *uidlist, *elem;
2956 struct mailimap_set * set;
2957 clist * lep_uidlist;
2960 session = stuff->session;
2961 if (session == NULL) {
2963 return GINT_TO_POINTER(-1);
2965 /* no session locking here, it's already locked by caller */
2966 ok = imap_select(session, IMAP_FOLDER(folder), item->item.path,
2967 NULL, NULL, NULL, NULL, TRUE);
2968 if (ok != IMAP_SUCCESS) {
2970 return GINT_TO_POINTER(-1);
2975 set = mailimap_set_new_interval(item->lastuid + 1, 0);
2977 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SIMPLE, set,
2979 mailimap_set_free(set);
2981 if (r == MAILIMAP_NO_ERROR) {
2982 GSList * fetchuid_list;
2985 imap_uid_list_from_lep(lep_uidlist);
2986 mailimap_search_result_free(lep_uidlist);
2988 uidlist = g_slist_concat(fetchuid_list, uidlist);
2991 GSList * fetchuid_list;
2992 carray * lep_uidtab;
2994 r = imap_threaded_fetch_uid(folder, item->lastuid + 1,
2996 if (r == MAILIMAP_NO_ERROR) {
2998 imap_uid_list_from_lep_tab(lep_uidtab);
2999 imap_fetch_uid_list_free(lep_uidtab);
3000 uidlist = g_slist_concat(fetchuid_list, uidlist);
3004 lastuid_old = item->lastuid;
3005 *msgnum_list = g_slist_copy(item->uid_list);
3006 nummsgs = g_slist_length(*msgnum_list);
3007 debug_print("Got %d uids from cache\n", g_slist_length(item->uid_list));
3009 for (elem = uidlist; elem != NULL; elem = g_slist_next(elem)) {
3012 msgnum = GPOINTER_TO_INT(elem->data);
3013 if (msgnum > lastuid_old) {
3014 *msgnum_list = g_slist_prepend(*msgnum_list, GINT_TO_POINTER(msgnum));
3015 item->uid_list = g_slist_prepend(item->uid_list, GINT_TO_POINTER(msgnum));
3018 if(msgnum > item->lastuid)
3019 item->lastuid = msgnum;
3022 g_slist_free(uidlist);
3024 return GINT_TO_POINTER(nummsgs);
3027 static gint get_list_of_uids(IMAPSession *session, Folder *folder, IMAPFolderItem *item, GSList **msgnum_list)
3030 get_list_uid_data *data = g_new0(get_list_uid_data, 1);
3032 data->folder = folder;
3034 data->msgnum_list = msgnum_list;
3035 data->session = session;
3036 if (prefs_common.work_offline && !inc_offline_should_override()) {
3041 result = GPOINTER_TO_INT(get_list_of_uids_thread(data));
3047 gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list, gboolean *old_uids_valid)
3049 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3050 IMAPSession *session;
3051 gint ok, nummsgs = 0, exists, uid_val, uid_next;
3052 GSList *uidlist = NULL;
3054 gboolean selected_folder;
3056 debug_print("get_num_list\n");
3058 g_return_val_if_fail(folder != NULL, -1);
3059 g_return_val_if_fail(item != NULL, -1);
3060 g_return_val_if_fail(item->item.path != NULL, -1);
3061 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3062 g_return_val_if_fail(folder->account != NULL, -1);
3064 session = imap_session_get(folder);
3065 g_return_val_if_fail(session != NULL, -1);
3067 statusbar_print_all("Scanning %s...\n", FOLDER_ITEM(item)->path
3068 ? FOLDER_ITEM(item)->path:"");
3070 selected_folder = (session->mbox != NULL) &&
3071 (!strcmp(session->mbox, item->item.path));
3072 if (selected_folder && time(NULL) - item->use_cache < 2) {
3073 ok = imap_cmd_noop(session);
3074 if (ok != IMAP_SUCCESS) {
3075 debug_print("disconnected!\n");
3076 session = imap_reconnect_if_possible(folder, session);
3077 if (session == NULL) {
3078 statusbar_pop_all();
3083 exists = session->exists;
3085 *old_uids_valid = TRUE;
3087 if (item->use_cache && time(NULL) - item->use_cache < 2) {
3088 exists = item->c_messages;
3089 uid_next = item->c_uid_next;
3090 uid_val = item->c_uid_validity;
3092 debug_print("using cache %d %d %d\n", exists, uid_next, uid_val);
3094 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path, item,
3095 &exists, &uid_next, &uid_val, NULL, FALSE);
3097 item->use_cache = (time_t)0;
3098 if (ok != IMAP_SUCCESS) {
3099 statusbar_pop_all();
3103 if(item->item.mtime == uid_val)
3104 *old_uids_valid = TRUE;
3106 *old_uids_valid = FALSE;
3108 debug_print("Freeing imap uid cache\n");
3110 g_slist_free(item->uid_list);
3111 item->uid_list = NULL;
3113 item->item.mtime = uid_val;
3115 imap_delete_all_cached_messages((FolderItem *)item);
3119 /* If old uid_next matches new uid_next we can be sure no message
3120 was added to the folder */
3121 debug_print("uid_next is %d and item->uid_next %d \n",
3122 uid_next, item->uid_next);
3123 if (uid_next == item->uid_next) {
3124 nummsgs = g_slist_length(item->uid_list);
3126 /* If number of messages is still the same we
3127 know our caches message numbers are still valid,
3128 otherwise if the number of messages has decrease
3129 we discard our cache to start a new scan to find
3130 out which numbers have been removed */
3131 if (exists == nummsgs) {
3132 debug_print("exists == nummsgs\n");
3133 *msgnum_list = g_slist_copy(item->uid_list);
3134 statusbar_pop_all();
3137 } else if (exists < nummsgs) {
3138 debug_print("Freeing imap uid cache");
3140 g_slist_free(item->uid_list);
3141 item->uid_list = NULL;
3146 *msgnum_list = NULL;
3147 statusbar_pop_all();
3152 nummsgs = get_list_of_uids(session, folder, item, &uidlist);
3155 statusbar_pop_all();
3160 if (nummsgs != exists) {
3161 /* Cache contains more messages then folder, we have cached
3162 an old UID of a message that was removed and new messages
3163 have been added too, otherwise the uid_next check would
3165 debug_print("Freeing imap uid cache");
3167 g_slist_free(item->uid_list);
3168 item->uid_list = NULL;
3170 g_slist_free(*msgnum_list);
3172 nummsgs = get_list_of_uids(session, folder, item, &uidlist);
3175 *msgnum_list = uidlist;
3177 dir = folder_item_get_path((FolderItem *)item);
3178 debug_print("removing old messages from %s\n", dir);
3179 remove_numbered_files_not_in_list(dir, *msgnum_list);
3182 item->uid_next = uid_next;
3184 debug_print("get_num_list - ok - %i\n", nummsgs);
3185 statusbar_pop_all();
3190 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item)
3195 flags.perm_flags = MSG_NEW|MSG_UNREAD;
3196 flags.tmp_flags = 0;
3198 g_return_val_if_fail(item != NULL, NULL);
3199 g_return_val_if_fail(file != NULL, NULL);
3201 if (folder_has_parent_of_type(item, F_QUEUE)) {
3202 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
3203 } else if (folder_has_parent_of_type(item, F_DRAFT)) {
3204 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
3207 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3208 if (!msginfo) return NULL;
3210 msginfo->plaintext_file = g_strdup(file);
3211 msginfo->folder = item;
3216 GSList *imap_get_msginfos(Folder *folder, FolderItem *item,
3217 GSList *msgnum_list)
3219 IMAPSession *session;
3220 MsgInfoList *ret = NULL;
3223 debug_print("get_msginfos\n");
3225 g_return_val_if_fail(folder != NULL, NULL);
3226 g_return_val_if_fail(item != NULL, NULL);
3227 g_return_val_if_fail(msgnum_list != NULL, NULL);
3229 session = imap_session_get(folder);
3230 g_return_val_if_fail(session != NULL, NULL);
3232 debug_print("IMAP getting msginfos\n");
3233 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3234 NULL, NULL, NULL, NULL, FALSE);
3235 if (ok != IMAP_SUCCESS) {
3239 if (!(folder_has_parent_of_type(item, F_DRAFT) ||
3240 folder_has_parent_of_type(item, F_QUEUE))) {
3241 ret = g_slist_concat(ret,
3242 imap_get_uncached_messages(session, item,
3245 MsgNumberList *sorted_list, *elem;
3246 gint startnum, lastnum;
3248 sorted_list = g_slist_sort(g_slist_copy(msgnum_list), g_int_compare);
3250 startnum = lastnum = GPOINTER_TO_INT(sorted_list->data);
3252 for (elem = sorted_list;; elem = g_slist_next(elem)) {
3256 num = GPOINTER_TO_INT(elem->data);
3258 if (num > lastnum + 1 || elem == NULL) {
3260 for (i = startnum; i <= lastnum; ++i) {
3263 file = imap_fetch_msg(folder, item, i);
3265 MsgInfo *msginfo = imap_parse_msg(file, item);
3266 if (msginfo != NULL) {
3267 msginfo->msgnum = i;
3268 ret = g_slist_append(ret, msginfo);
3282 g_slist_free(sorted_list);
3288 MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid)
3290 MsgInfo *msginfo = NULL;
3291 MsgInfoList *msginfolist;
3292 MsgNumberList numlist;
3294 numlist.next = NULL;
3295 numlist.data = GINT_TO_POINTER(uid);
3297 msginfolist = imap_get_msginfos(folder, item, &numlist);
3298 if (msginfolist != NULL) {
3299 msginfo = msginfolist->data;
3300 g_slist_free(msginfolist);
3306 gboolean imap_scan_required(Folder *folder, FolderItem *_item)
3308 IMAPSession *session;
3309 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3310 gint ok, exists = 0, unseen = 0;
3311 guint32 uid_next, uid_val;
3312 gboolean selected_folder;
3314 g_return_val_if_fail(folder != NULL, FALSE);
3315 g_return_val_if_fail(item != NULL, FALSE);
3316 g_return_val_if_fail(item->item.folder != NULL, FALSE);
3317 g_return_val_if_fail(FOLDER_CLASS(item->item.folder) == &imap_class, FALSE);
3319 if (item->item.path == NULL)
3322 session = imap_session_get(folder);
3323 g_return_val_if_fail(session != NULL, FALSE);
3325 selected_folder = (session->mbox != NULL) &&
3326 (!strcmp(session->mbox, item->item.path));
3327 if (selected_folder && time(NULL) - item->use_cache < 2) {
3328 ok = imap_cmd_noop(session);
3329 if (ok != IMAP_SUCCESS) {
3330 debug_print("disconnected!\n");
3331 session = imap_reconnect_if_possible(folder, session);
3332 if (session == NULL)
3337 if (session->folder_content_changed
3338 || session->exists != item->item.total_msgs) {
3343 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path, IMAP_FOLDER_ITEM(item),
3344 &exists, &uid_next, &uid_val, &unseen, FALSE);
3345 if (ok != IMAP_SUCCESS) {
3350 item->use_cache = time(NULL);
3351 item->c_messages = exists;
3352 item->c_uid_next = uid_next;
3353 item->c_uid_validity = uid_val;
3354 item->c_unseen = unseen;
3355 debug_print("uidnext %d, item->uid_next %d, exists %d, item->item.total_msgs %d\n",
3356 uid_next, item->uid_next, exists, item->item.total_msgs);
3357 if ((uid_next != item->uid_next) || (exists != item->item.total_msgs)) {
3366 void imap_change_flags(Folder *folder, FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
3368 IMAPSession *session;
3369 IMAPFlags flags_set = 0, flags_unset = 0;
3370 gint ok = IMAP_SUCCESS;
3371 MsgNumberList numlist;
3372 hashtable_data *ht_data = NULL;
3374 g_return_if_fail(folder != NULL);
3375 g_return_if_fail(folder->klass == &imap_class);
3376 g_return_if_fail(item != NULL);
3377 g_return_if_fail(item->folder == folder);
3378 g_return_if_fail(msginfo != NULL);
3379 g_return_if_fail(msginfo->folder == item);
3381 session = imap_session_get(folder);
3386 if ((ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
3387 NULL, NULL, NULL, NULL, FALSE)) != IMAP_SUCCESS) {
3392 if (!MSG_IS_MARKED(msginfo->flags) && (newflags & MSG_MARKED))
3393 flags_set |= IMAP_FLAG_FLAGGED;
3394 if ( MSG_IS_MARKED(msginfo->flags) && !(newflags & MSG_MARKED))
3395 flags_unset |= IMAP_FLAG_FLAGGED;
3397 if (!MSG_IS_UNREAD(msginfo->flags) && (newflags & MSG_UNREAD))
3398 flags_unset |= IMAP_FLAG_SEEN;
3399 if ( MSG_IS_UNREAD(msginfo->flags) && !(newflags & MSG_UNREAD))
3400 flags_set |= IMAP_FLAG_SEEN;
3402 if (!MSG_IS_REPLIED(msginfo->flags) && (newflags & MSG_REPLIED))
3403 flags_set |= IMAP_FLAG_ANSWERED;
3404 if ( MSG_IS_REPLIED(msginfo->flags) && !(newflags & MSG_REPLIED))
3405 flags_unset |= IMAP_FLAG_ANSWERED;
3407 if (!MSG_IS_DELETED(msginfo->flags) && (newflags & MSG_DELETED))
3408 flags_set |= IMAP_FLAG_DELETED;
3409 if ( MSG_IS_DELETED(msginfo->flags) && !(newflags & MSG_DELETED))
3410 flags_unset |= IMAP_FLAG_DELETED;
3412 numlist.next = NULL;
3413 numlist.data = GINT_TO_POINTER(msginfo->msgnum);
3415 if (IMAP_FOLDER_ITEM(item)->batching) {
3416 /* instead of performing an UID STORE command for each message change,
3417 * as a lot of them can change "together", we just fill in hashtables
3418 * and defer the treatment so that we're able to send only one
3421 debug_print("IMAP batch mode on, deferring flags change\n");
3423 ht_data = g_hash_table_lookup(IMAP_FOLDER_ITEM(item)->flags_set_table,
3424 GINT_TO_POINTER(flags_set));
3425 if (ht_data == NULL) {
3426 ht_data = g_new0(hashtable_data, 1);
3427 ht_data->session = session;
3428 ht_data->item = IMAP_FOLDER_ITEM(item);
3429 g_hash_table_insert(IMAP_FOLDER_ITEM(item)->flags_set_table,
3430 GINT_TO_POINTER(flags_set), ht_data);
3432 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
3433 ht_data->msglist = g_slist_prepend(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum));
3436 ht_data = g_hash_table_lookup(IMAP_FOLDER_ITEM(item)->flags_unset_table,
3437 GINT_TO_POINTER(flags_unset));
3438 if (ht_data == NULL) {
3439 ht_data = g_new0(hashtable_data, 1);
3440 ht_data->session = session;
3441 ht_data->item = IMAP_FOLDER_ITEM(item);
3442 g_hash_table_insert(IMAP_FOLDER_ITEM(item)->flags_unset_table,
3443 GINT_TO_POINTER(flags_unset), ht_data);
3445 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
3446 ht_data->msglist = g_slist_prepend(ht_data->msglist,
3447 GINT_TO_POINTER(msginfo->msgnum));
3450 debug_print("IMAP changing flags\n");
3452 ok = imap_set_message_flags(session, &numlist, flags_set, TRUE);
3453 if (ok != IMAP_SUCCESS) {
3460 ok = imap_set_message_flags(session, &numlist, flags_unset, FALSE);
3461 if (ok != IMAP_SUCCESS) {
3467 msginfo->flags.perm_flags = newflags;
3472 static gint imap_remove_msg(Folder *folder, FolderItem *item, gint uid)
3475 IMAPSession *session;
3477 MsgNumberList numlist;
3479 g_return_val_if_fail(folder != NULL, -1);
3480 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3481 g_return_val_if_fail(item != NULL, -1);
3483 session = imap_session_get(folder);
3484 if (!session) return -1;
3486 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3487 NULL, NULL, NULL, NULL, FALSE);
3488 if (ok != IMAP_SUCCESS) {
3492 numlist.next = NULL;
3493 numlist.data = GINT_TO_POINTER(uid);
3495 ok = imap_set_message_flags
3496 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
3497 &numlist, IMAP_FLAG_DELETED, TRUE);
3498 if (ok != IMAP_SUCCESS) {
3499 log_warning(_("can't set deleted flags: %d\n"), uid);
3504 if (!session->uidplus) {
3505 ok = imap_cmd_expunge(session);
3509 uidstr = g_strdup_printf("%u", uid);
3510 ok = imap_cmd_expunge(session);
3513 if (ok != IMAP_SUCCESS) {
3514 log_warning(_("can't expunge\n"));
3519 IMAP_FOLDER_ITEM(item)->uid_list = g_slist_remove(
3520 IMAP_FOLDER_ITEM(item)->uid_list, numlist.data);
3521 dir = folder_item_get_path(item);
3522 if (is_dir_exist(dir))
3523 remove_numbered_files(dir, uid, uid);
3526 return IMAP_SUCCESS;
3529 static gint compare_msginfo(gconstpointer a, gconstpointer b)
3531 return ((MsgInfo *)a)->msgnum - ((MsgInfo *)b)->msgnum;
3534 static guint gslist_find_next_num(MsgNumberList **list, guint num)
3538 g_return_val_if_fail(list != NULL, -1);
3540 for (elem = *list; elem != NULL; elem = g_slist_next(elem))
3541 if (GPOINTER_TO_INT(elem->data) >= num)
3544 return elem != NULL ? GPOINTER_TO_INT(elem->data) : (gint)-1;
3548 * NEW and DELETED flags are not syncronized
3549 * - The NEW/RECENT flags in IMAP folders can not really be directly
3550 * modified by Sylpheed
3551 * - The DELETE/DELETED flag in IMAP and Sylpheed don't have the same
3552 * meaning, in IMAP it always removes the messages from the FolderItem
3553 * in Sylpheed it can mean to move the message to trash
3556 typedef struct _get_flags_data {
3559 MsgInfoList *msginfo_list;
3560 GRelation *msgflags;
3561 gboolean full_search;
3565 static /*gint*/ void *imap_get_flags_thread(void *data)
3567 get_flags_data *stuff = (get_flags_data *)data;
3568 Folder *folder = stuff->folder;
3569 FolderItem *item = stuff->item;
3570 MsgInfoList *msginfo_list = stuff->msginfo_list;
3571 GRelation *msgflags = stuff->msgflags;
3572 gboolean full_search = stuff->full_search;
3573 IMAPSession *session;
3574 GSList *sorted_list = NULL;
3575 GSList *unseen = NULL, *answered = NULL, *flagged = NULL, *deleted = NULL;
3576 GSList *p_unseen, *p_answered, *p_flagged, *p_deleted;
3578 GSList *seq_list, *cur;
3579 gboolean reverse_seen = FALSE;
3582 gint exists_cnt, unseen_cnt;
3583 gboolean selected_folder;
3585 if (folder == NULL || item == NULL) {
3587 return GINT_TO_POINTER(-1);
3590 session = imap_session_get(folder);
3591 if (session == NULL) {
3593 return GINT_TO_POINTER(-1);
3596 selected_folder = (session->mbox != NULL) &&
3597 (!strcmp(session->mbox, item->path));
3599 if (!selected_folder) {
3600 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3601 &exists_cnt, NULL, &unseen_cnt, NULL, TRUE);
3602 if (ok != IMAP_SUCCESS) {
3605 return GINT_TO_POINTER(-1);
3608 if (unseen_cnt > exists_cnt / 2)
3609 reverse_seen = TRUE;
3612 if (item->unread_msgs > item->total_msgs / 2)
3613 reverse_seen = TRUE;
3616 cmd_buf = g_string_new(NULL);
3618 sorted_list = g_slist_sort(g_slist_copy(msginfo_list), compare_msginfo);
3620 seq_list = imap_get_lep_set_from_msglist(msginfo_list);
3622 struct mailimap_set * set;
3623 set = mailimap_set_new_interval(1, 0);
3624 seq_list = g_slist_append(NULL, set);
3627 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
3628 struct mailimap_set * imapset;
3629 clist * lep_uidlist;
3632 imapset = cur->data;
3634 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SEEN,
3635 imapset, &lep_uidlist);
3638 r = imap_threaded_search(folder,
3639 IMAP_SEARCH_TYPE_UNSEEN,
3640 imapset, &lep_uidlist);
3642 if (r == MAILIMAP_NO_ERROR) {
3645 uidlist = imap_uid_list_from_lep(lep_uidlist);
3646 mailimap_search_result_free(lep_uidlist);
3648 unseen = g_slist_concat(unseen, uidlist);
3651 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_ANSWERED,
3652 imapset, &lep_uidlist);
3653 if (r == MAILIMAP_NO_ERROR) {
3656 uidlist = imap_uid_list_from_lep(lep_uidlist);
3657 mailimap_search_result_free(lep_uidlist);
3659 answered = g_slist_concat(answered, uidlist);
3662 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_FLAGGED,
3663 imapset, &lep_uidlist);
3664 if (r == MAILIMAP_NO_ERROR) {
3667 uidlist = imap_uid_list_from_lep(lep_uidlist);
3668 mailimap_search_result_free(lep_uidlist);
3670 flagged = g_slist_concat(flagged, uidlist);
3673 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_DELETED,
3674 imapset, &lep_uidlist);
3675 if (r == MAILIMAP_NO_ERROR) {
3678 uidlist = imap_uid_list_from_lep(lep_uidlist);
3679 mailimap_search_result_free(lep_uidlist);
3681 deleted = g_slist_concat(deleted, uidlist);
3686 p_answered = answered;
3687 p_flagged = flagged;
3688 p_deleted = deleted;
3690 for (elem = sorted_list; elem != NULL; elem = g_slist_next(elem)) {
3695 msginfo = (MsgInfo *) elem->data;
3696 flags = msginfo->flags.perm_flags;
3697 wasnew = (flags & MSG_NEW);
3698 flags &= ~((reverse_seen ? 0 : MSG_UNREAD | MSG_NEW) | MSG_REPLIED | MSG_MARKED);
3700 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
3701 if (gslist_find_next_num(&p_unseen, msginfo->msgnum) == msginfo->msgnum) {
3702 if (!reverse_seen) {
3703 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
3705 flags &= ~(MSG_UNREAD | MSG_NEW);
3708 if (gslist_find_next_num(&p_answered, msginfo->msgnum) == msginfo->msgnum)
3709 flags |= MSG_REPLIED;
3711 flags &= ~MSG_REPLIED;
3712 if (gslist_find_next_num(&p_flagged, msginfo->msgnum) == msginfo->msgnum)
3713 flags |= MSG_MARKED;
3715 flags &= ~MSG_MARKED;
3716 if (gslist_find_next_num(&p_deleted, msginfo->msgnum) == msginfo->msgnum)
3717 flags |= MSG_DELETED;
3719 flags &= ~MSG_DELETED;
3720 g_relation_insert(msgflags, msginfo, GINT_TO_POINTER(flags));
3723 imap_lep_set_free(seq_list);
3724 g_slist_free(flagged);
3725 g_slist_free(deleted);
3726 g_slist_free(answered);
3727 g_slist_free(unseen);
3728 g_slist_free(sorted_list);
3729 g_string_free(cmd_buf, TRUE);
3733 return GINT_TO_POINTER(0);
3736 static gint imap_get_flags(Folder *folder, FolderItem *item,
3737 MsgInfoList *msginfo_list, GRelation *msgflags)
3740 get_flags_data *data = g_new0(get_flags_data, 1);
3742 data->folder = folder;
3744 data->msginfo_list = msginfo_list;
3745 data->msgflags = msgflags;
3746 data->full_search = FALSE;
3748 GSList *tmp = NULL, *cur;
3750 if (prefs_common.work_offline && !inc_offline_should_override()) {
3755 tmp = folder_item_get_msg_list(item);
3757 if (g_slist_length(tmp) == g_slist_length(msginfo_list))
3758 data->full_search = TRUE;
3760 for (cur = tmp; cur; cur = cur->next)
3761 procmsg_msginfo_free((MsgInfo *)cur->data);
3765 result = GPOINTER_TO_INT(imap_get_flags_thread(data));
3772 static gboolean process_flags(gpointer key, gpointer value, gpointer user_data)
3774 gboolean flags_set = GPOINTER_TO_INT(user_data);
3775 gint flags_value = GPOINTER_TO_INT(key);
3776 hashtable_data *data = (hashtable_data *)value;
3777 IMAPFolderItem *_item = data->item;
3778 FolderItem *item = (FolderItem *)_item;
3779 gint ok = IMAP_ERROR;
3780 IMAPSession *session = imap_session_get(item->folder);
3782 data->msglist = g_slist_reverse(data->msglist);
3784 debug_print("IMAP %ssetting flags to %d for %d messages\n",
3787 g_slist_length(data->msglist));
3791 ok = imap_select(session, IMAP_FOLDER(item->folder), item->path,
3792 NULL, NULL, NULL, NULL, FALSE);
3794 if (ok == IMAP_SUCCESS) {
3795 imap_set_message_flags(data->session, data->msglist, flags_value, flags_set);
3797 g_warning("can't select mailbox %s\n", item->path);
3801 g_slist_free(data->msglist);
3806 static void process_hashtable(IMAPFolderItem *item)
3808 if (item->flags_set_table) {
3809 g_hash_table_foreach_remove(item->flags_set_table, process_flags, GINT_TO_POINTER(TRUE));
3810 g_hash_table_destroy(item->flags_set_table);
3811 item->flags_set_table = NULL;
3813 if (item->flags_unset_table) {
3814 g_hash_table_foreach_remove(item->flags_unset_table, process_flags, GINT_TO_POINTER(FALSE));
3815 g_hash_table_destroy(item->flags_unset_table);
3816 item->flags_unset_table = NULL;
3820 static void imap_set_batch (Folder *folder, FolderItem *_item, gboolean batch)
3822 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3824 g_return_if_fail(item != NULL);
3826 if (item->batching == batch)
3830 item->batching = TRUE;
3831 debug_print("IMAP switching to batch mode\n");
3832 if (!item->flags_set_table) {
3833 item->flags_set_table = g_hash_table_new(NULL, g_direct_equal);
3835 if (!item->flags_unset_table) {
3836 item->flags_unset_table = g_hash_table_new(NULL, g_direct_equal);
3839 debug_print("IMAP switching away from batch mode\n");
3841 process_hashtable(item);
3842 item->batching = FALSE;
3848 /* data types conversion libetpan <-> sylpheed */
3852 #define ETPAN_IMAP_MB_MARKED 1
3853 #define ETPAN_IMAP_MB_UNMARKED 2
3854 #define ETPAN_IMAP_MB_NOSELECT 4
3855 #define ETPAN_IMAP_MB_NOINFERIORS 8
3857 static int imap_flags_to_flags(struct mailimap_mbx_list_flags * imap_flags)
3863 if (imap_flags->mbf_type == MAILIMAP_MBX_LIST_FLAGS_SFLAG) {
3864 switch (imap_flags->mbf_sflag) {
3865 case MAILIMAP_MBX_LIST_SFLAG_MARKED:
3866 flags |= ETPAN_IMAP_MB_MARKED;
3868 case MAILIMAP_MBX_LIST_SFLAG_NOSELECT:
3869 flags |= ETPAN_IMAP_MB_NOSELECT;
3871 case MAILIMAP_MBX_LIST_SFLAG_UNMARKED:
3872 flags |= ETPAN_IMAP_MB_UNMARKED;
3877 for(cur = clist_begin(imap_flags->mbf_oflags) ; cur != NULL ;
3878 cur = clist_next(cur)) {
3879 struct mailimap_mbx_list_oflag * oflag;
3881 oflag = clist_content(cur);
3883 switch (oflag->of_type) {
3884 case MAILIMAP_MBX_LIST_OFLAG_NOINFERIORS:
3885 flags |= ETPAN_IMAP_MB_NOINFERIORS;
3893 static GSList * imap_list_from_lep(IMAPFolder * folder,
3894 clist * list, const gchar * real_path, gboolean all)
3901 for(iter = clist_begin(list) ; iter != NULL ;
3902 iter = clist_next(iter)) {
3903 struct mailimap_mailbox_list * mb;
3911 FolderItem *new_item;
3913 mb = clist_content(iter);
3919 if (mb->mb_flag != NULL)
3920 flags = imap_flags_to_flags(mb->mb_flag);
3922 delimiter = mb->mb_delimiter;
3925 dup_name = strdup(name);
3926 if (delimiter != '\0')
3927 subst_char(dup_name, delimiter, '/');
3929 base = g_path_get_basename(dup_name);
3930 if (base[0] == '.') {
3936 if (!all && strcmp(dup_name, real_path) == 0) {
3942 if (!all && dup_name[strlen(dup_name)-1] == '/') {
3948 loc_name = imap_modified_utf7_to_utf8(base);
3949 loc_path = imap_modified_utf7_to_utf8(dup_name);
3951 new_item = folder_item_new(FOLDER(folder), loc_name, loc_path);
3952 if ((flags & ETPAN_IMAP_MB_NOINFERIORS) != 0)
3953 new_item->no_sub = TRUE;
3954 if (strcmp(dup_name, "INBOX") != 0 &&
3955 ((flags & ETPAN_IMAP_MB_NOSELECT) != 0))
3956 new_item->no_select = TRUE;
3958 item_list = g_slist_append(item_list, new_item);
3960 debug_print("folder '%s' found.\n", loc_path);
3971 static GSList * imap_get_lep_set_from_numlist(MsgNumberList *numlist)
3973 GSList *sorted_list, *cur;
3974 guint first, last, next;
3975 GSList *ret_list = NULL;
3977 struct mailimap_set * current_set;
3978 unsigned int item_count;
3980 if (numlist == NULL)
3984 current_set = mailimap_set_new_empty();
3986 sorted_list = g_slist_copy(numlist);
3987 sorted_list = g_slist_sort(sorted_list, g_int_compare);
3989 first = GPOINTER_TO_INT(sorted_list->data);
3992 for (cur = sorted_list; cur != NULL; cur = g_slist_next(cur)) {
3993 if (GPOINTER_TO_INT(cur->data) == 0)
3998 last = GPOINTER_TO_INT(cur->data);
4000 next = GPOINTER_TO_INT(cur->next->data);
4004 if (last + 1 != next || next == 0) {
4006 struct mailimap_set_item * item;
4007 item = mailimap_set_item_new(first, last);
4008 mailimap_set_add(current_set, item);
4013 if (count >= IMAP_SET_MAX_COUNT) {
4014 ret_list = g_slist_append(ret_list,
4016 current_set = mailimap_set_new_empty();
4023 if (clist_count(current_set->set_list) > 0) {
4024 ret_list = g_slist_append(ret_list,
4028 g_slist_free(sorted_list);
4033 static GSList * imap_get_lep_set_from_msglist(MsgInfoList *msglist)
4035 MsgNumberList *numlist = NULL;
4039 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
4040 MsgInfo *msginfo = (MsgInfo *) cur->data;
4042 numlist = g_slist_append(numlist, GINT_TO_POINTER(msginfo->msgnum));
4044 seq_list = imap_get_lep_set_from_numlist(numlist);
4045 g_slist_free(numlist);