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 if (( selected_folder && !session->folder_content_changed) ||
3122 (!selected_folder && uid_next == item->uid_next)) {
3123 nummsgs = g_slist_length(item->uid_list);
3125 /* If number of messages is still the same we
3126 know our caches message numbers are still valid,
3127 otherwise if the number of messages has decrease
3128 we discard our cache to start a new scan to find
3129 out which numbers have been removed */
3130 if (exists == nummsgs) {
3131 debug_print("exists == nummsgs\n");
3132 *msgnum_list = g_slist_copy(item->uid_list);
3133 statusbar_pop_all();
3136 } else if (exists < nummsgs) {
3137 debug_print("Freeing imap uid cache");
3139 g_slist_free(item->uid_list);
3140 item->uid_list = NULL;
3145 *msgnum_list = NULL;
3146 statusbar_pop_all();
3151 nummsgs = get_list_of_uids(session, folder, item, &uidlist);
3154 statusbar_pop_all();
3159 if (nummsgs != exists) {
3160 /* Cache contains more messages then folder, we have cached
3161 an old UID of a message that was removed and new messages
3162 have been added too, otherwise the uid_next check would
3164 debug_print("Freeing imap uid cache");
3166 g_slist_free(item->uid_list);
3167 item->uid_list = NULL;
3169 g_slist_free(*msgnum_list);
3171 nummsgs = get_list_of_uids(session, folder, item, &uidlist);
3174 *msgnum_list = uidlist;
3176 dir = folder_item_get_path((FolderItem *)item);
3177 debug_print("removing old messages from %s\n", dir);
3178 remove_numbered_files_not_in_list(dir, *msgnum_list);
3181 item->uid_next = uid_next;
3183 debug_print("get_num_list - ok - %i\n", nummsgs);
3184 statusbar_pop_all();
3189 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item)
3194 flags.perm_flags = MSG_NEW|MSG_UNREAD;
3195 flags.tmp_flags = 0;
3197 g_return_val_if_fail(item != NULL, NULL);
3198 g_return_val_if_fail(file != NULL, NULL);
3200 if (folder_has_parent_of_type(item, F_QUEUE)) {
3201 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
3202 } else if (folder_has_parent_of_type(item, F_DRAFT)) {
3203 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
3206 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3207 if (!msginfo) return NULL;
3209 msginfo->plaintext_file = g_strdup(file);
3210 msginfo->folder = item;
3215 GSList *imap_get_msginfos(Folder *folder, FolderItem *item,
3216 GSList *msgnum_list)
3218 IMAPSession *session;
3219 MsgInfoList *ret = NULL;
3222 debug_print("get_msginfos\n");
3224 g_return_val_if_fail(folder != NULL, NULL);
3225 g_return_val_if_fail(item != NULL, NULL);
3226 g_return_val_if_fail(msgnum_list != NULL, NULL);
3228 session = imap_session_get(folder);
3229 g_return_val_if_fail(session != NULL, NULL);
3231 debug_print("IMAP getting msginfos\n");
3232 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3233 NULL, NULL, NULL, NULL, FALSE);
3234 if (ok != IMAP_SUCCESS) {
3238 if (!(folder_has_parent_of_type(item, F_DRAFT) ||
3239 folder_has_parent_of_type(item, F_QUEUE))) {
3240 ret = g_slist_concat(ret,
3241 imap_get_uncached_messages(session, item,
3244 MsgNumberList *sorted_list, *elem;
3245 gint startnum, lastnum;
3247 sorted_list = g_slist_sort(g_slist_copy(msgnum_list), g_int_compare);
3249 startnum = lastnum = GPOINTER_TO_INT(sorted_list->data);
3251 for (elem = sorted_list;; elem = g_slist_next(elem)) {
3255 num = GPOINTER_TO_INT(elem->data);
3257 if (num > lastnum + 1 || elem == NULL) {
3259 for (i = startnum; i <= lastnum; ++i) {
3262 file = imap_fetch_msg(folder, item, i);
3264 MsgInfo *msginfo = imap_parse_msg(file, item);
3265 if (msginfo != NULL) {
3266 msginfo->msgnum = i;
3267 ret = g_slist_append(ret, msginfo);
3281 g_slist_free(sorted_list);
3287 MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid)
3289 MsgInfo *msginfo = NULL;
3290 MsgInfoList *msginfolist;
3291 MsgNumberList numlist;
3293 numlist.next = NULL;
3294 numlist.data = GINT_TO_POINTER(uid);
3296 msginfolist = imap_get_msginfos(folder, item, &numlist);
3297 if (msginfolist != NULL) {
3298 msginfo = msginfolist->data;
3299 g_slist_free(msginfolist);
3305 gboolean imap_scan_required(Folder *folder, FolderItem *_item)
3307 IMAPSession *session;
3308 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3309 gint ok, exists = 0, unseen = 0;
3310 guint32 uid_next, uid_val;
3311 gboolean selected_folder;
3313 g_return_val_if_fail(folder != NULL, FALSE);
3314 g_return_val_if_fail(item != NULL, FALSE);
3315 g_return_val_if_fail(item->item.folder != NULL, FALSE);
3316 g_return_val_if_fail(FOLDER_CLASS(item->item.folder) == &imap_class, FALSE);
3318 if (item->item.path == NULL)
3321 session = imap_session_get(folder);
3322 g_return_val_if_fail(session != NULL, FALSE);
3324 selected_folder = (session->mbox != NULL) &&
3325 (!strcmp(session->mbox, item->item.path));
3326 if (selected_folder && time(NULL) - item->use_cache < 2) {
3327 ok = imap_cmd_noop(session);
3328 if (ok != IMAP_SUCCESS) {
3329 debug_print("disconnected!\n");
3330 session = imap_reconnect_if_possible(folder, session);
3331 if (session == NULL)
3336 if (session->folder_content_changed
3337 || session->exists != item->item.total_msgs) {
3342 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path, IMAP_FOLDER_ITEM(item),
3343 &exists, &uid_next, &uid_val, &unseen, FALSE);
3344 if (ok != IMAP_SUCCESS) {
3349 item->use_cache = time(NULL);
3350 item->c_messages = exists;
3351 item->c_uid_next = uid_next;
3352 item->c_uid_validity = uid_val;
3353 item->c_unseen = unseen;
3354 debug_print("uidnext %d, item->uid_next %d, exists %d, item->item.total_msgs %d\n",
3355 uid_next, item->uid_next, exists, item->item.total_msgs);
3356 if ((uid_next != item->uid_next) || (exists != item->item.total_msgs)) {
3365 void imap_change_flags(Folder *folder, FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
3367 IMAPSession *session;
3368 IMAPFlags flags_set = 0, flags_unset = 0;
3369 gint ok = IMAP_SUCCESS;
3370 MsgNumberList numlist;
3371 hashtable_data *ht_data = NULL;
3373 g_return_if_fail(folder != NULL);
3374 g_return_if_fail(folder->klass == &imap_class);
3375 g_return_if_fail(item != NULL);
3376 g_return_if_fail(item->folder == folder);
3377 g_return_if_fail(msginfo != NULL);
3378 g_return_if_fail(msginfo->folder == item);
3380 session = imap_session_get(folder);
3385 if ((ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
3386 NULL, NULL, NULL, NULL, FALSE)) != IMAP_SUCCESS) {
3391 if (!MSG_IS_MARKED(msginfo->flags) && (newflags & MSG_MARKED))
3392 flags_set |= IMAP_FLAG_FLAGGED;
3393 if ( MSG_IS_MARKED(msginfo->flags) && !(newflags & MSG_MARKED))
3394 flags_unset |= IMAP_FLAG_FLAGGED;
3396 if (!MSG_IS_UNREAD(msginfo->flags) && (newflags & MSG_UNREAD))
3397 flags_unset |= IMAP_FLAG_SEEN;
3398 if ( MSG_IS_UNREAD(msginfo->flags) && !(newflags & MSG_UNREAD))
3399 flags_set |= IMAP_FLAG_SEEN;
3401 if (!MSG_IS_REPLIED(msginfo->flags) && (newflags & MSG_REPLIED))
3402 flags_set |= IMAP_FLAG_ANSWERED;
3403 if ( MSG_IS_REPLIED(msginfo->flags) && !(newflags & MSG_REPLIED))
3404 flags_unset |= IMAP_FLAG_ANSWERED;
3406 if (!MSG_IS_DELETED(msginfo->flags) && (newflags & MSG_DELETED))
3407 flags_set |= IMAP_FLAG_DELETED;
3408 if ( MSG_IS_DELETED(msginfo->flags) && !(newflags & MSG_DELETED))
3409 flags_unset |= IMAP_FLAG_DELETED;
3411 numlist.next = NULL;
3412 numlist.data = GINT_TO_POINTER(msginfo->msgnum);
3414 if (IMAP_FOLDER_ITEM(item)->batching) {
3415 /* instead of performing an UID STORE command for each message change,
3416 * as a lot of them can change "together", we just fill in hashtables
3417 * and defer the treatment so that we're able to send only one
3420 debug_print("IMAP batch mode on, deferring flags change\n");
3422 ht_data = g_hash_table_lookup(IMAP_FOLDER_ITEM(item)->flags_set_table,
3423 GINT_TO_POINTER(flags_set));
3424 if (ht_data == NULL) {
3425 ht_data = g_new0(hashtable_data, 1);
3426 ht_data->session = session;
3427 ht_data->item = IMAP_FOLDER_ITEM(item);
3428 g_hash_table_insert(IMAP_FOLDER_ITEM(item)->flags_set_table,
3429 GINT_TO_POINTER(flags_set), ht_data);
3431 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
3432 ht_data->msglist = g_slist_prepend(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum));
3435 ht_data = g_hash_table_lookup(IMAP_FOLDER_ITEM(item)->flags_unset_table,
3436 GINT_TO_POINTER(flags_unset));
3437 if (ht_data == NULL) {
3438 ht_data = g_new0(hashtable_data, 1);
3439 ht_data->session = session;
3440 ht_data->item = IMAP_FOLDER_ITEM(item);
3441 g_hash_table_insert(IMAP_FOLDER_ITEM(item)->flags_unset_table,
3442 GINT_TO_POINTER(flags_unset), ht_data);
3444 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
3445 ht_data->msglist = g_slist_prepend(ht_data->msglist,
3446 GINT_TO_POINTER(msginfo->msgnum));
3449 debug_print("IMAP changing flags\n");
3451 ok = imap_set_message_flags(session, &numlist, flags_set, TRUE);
3452 if (ok != IMAP_SUCCESS) {
3459 ok = imap_set_message_flags(session, &numlist, flags_unset, FALSE);
3460 if (ok != IMAP_SUCCESS) {
3466 msginfo->flags.perm_flags = newflags;
3471 static gint imap_remove_msg(Folder *folder, FolderItem *item, gint uid)
3474 IMAPSession *session;
3476 MsgNumberList numlist;
3478 g_return_val_if_fail(folder != NULL, -1);
3479 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3480 g_return_val_if_fail(item != NULL, -1);
3482 session = imap_session_get(folder);
3483 if (!session) return -1;
3485 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3486 NULL, NULL, NULL, NULL, FALSE);
3487 if (ok != IMAP_SUCCESS) {
3491 numlist.next = NULL;
3492 numlist.data = GINT_TO_POINTER(uid);
3494 ok = imap_set_message_flags
3495 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
3496 &numlist, IMAP_FLAG_DELETED, TRUE);
3497 if (ok != IMAP_SUCCESS) {
3498 log_warning(_("can't set deleted flags: %d\n"), uid);
3503 if (!session->uidplus) {
3504 ok = imap_cmd_expunge(session);
3508 uidstr = g_strdup_printf("%u", uid);
3509 ok = imap_cmd_expunge(session);
3512 if (ok != IMAP_SUCCESS) {
3513 log_warning(_("can't expunge\n"));
3518 IMAP_FOLDER_ITEM(item)->uid_list = g_slist_remove(
3519 IMAP_FOLDER_ITEM(item)->uid_list, numlist.data);
3520 dir = folder_item_get_path(item);
3521 if (is_dir_exist(dir))
3522 remove_numbered_files(dir, uid, uid);
3525 return IMAP_SUCCESS;
3528 static gint compare_msginfo(gconstpointer a, gconstpointer b)
3530 return ((MsgInfo *)a)->msgnum - ((MsgInfo *)b)->msgnum;
3533 static guint gslist_find_next_num(MsgNumberList **list, guint num)
3537 g_return_val_if_fail(list != NULL, -1);
3539 for (elem = *list; elem != NULL; elem = g_slist_next(elem))
3540 if (GPOINTER_TO_INT(elem->data) >= num)
3543 return elem != NULL ? GPOINTER_TO_INT(elem->data) : (gint)-1;
3547 * NEW and DELETED flags are not syncronized
3548 * - The NEW/RECENT flags in IMAP folders can not really be directly
3549 * modified by Sylpheed
3550 * - The DELETE/DELETED flag in IMAP and Sylpheed don't have the same
3551 * meaning, in IMAP it always removes the messages from the FolderItem
3552 * in Sylpheed it can mean to move the message to trash
3555 typedef struct _get_flags_data {
3558 MsgInfoList *msginfo_list;
3559 GRelation *msgflags;
3560 gboolean full_search;
3564 static /*gint*/ void *imap_get_flags_thread(void *data)
3566 get_flags_data *stuff = (get_flags_data *)data;
3567 Folder *folder = stuff->folder;
3568 FolderItem *item = stuff->item;
3569 MsgInfoList *msginfo_list = stuff->msginfo_list;
3570 GRelation *msgflags = stuff->msgflags;
3571 gboolean full_search = stuff->full_search;
3572 IMAPSession *session;
3573 GSList *sorted_list = NULL;
3574 GSList *unseen = NULL, *answered = NULL, *flagged = NULL, *deleted = NULL;
3575 GSList *p_unseen, *p_answered, *p_flagged, *p_deleted;
3577 GSList *seq_list, *cur;
3578 gboolean reverse_seen = FALSE;
3581 gint exists_cnt, unseen_cnt;
3582 gboolean selected_folder;
3584 if (folder == NULL || item == NULL) {
3586 return GINT_TO_POINTER(-1);
3589 session = imap_session_get(folder);
3590 if (session == NULL) {
3592 return GINT_TO_POINTER(-1);
3595 selected_folder = (session->mbox != NULL) &&
3596 (!strcmp(session->mbox, item->path));
3598 if (!selected_folder) {
3599 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3600 &exists_cnt, NULL, &unseen_cnt, NULL, TRUE);
3601 if (ok != IMAP_SUCCESS) {
3604 return GINT_TO_POINTER(-1);
3607 if (unseen_cnt > exists_cnt / 2)
3608 reverse_seen = TRUE;
3611 if (item->unread_msgs > item->total_msgs / 2)
3612 reverse_seen = TRUE;
3615 cmd_buf = g_string_new(NULL);
3617 sorted_list = g_slist_sort(g_slist_copy(msginfo_list), compare_msginfo);
3619 seq_list = imap_get_lep_set_from_msglist(msginfo_list);
3621 struct mailimap_set * set;
3622 set = mailimap_set_new_interval(1, 0);
3623 seq_list = g_slist_append(NULL, set);
3626 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
3627 struct mailimap_set * imapset;
3628 clist * lep_uidlist;
3631 imapset = cur->data;
3633 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SEEN,
3634 imapset, &lep_uidlist);
3637 r = imap_threaded_search(folder,
3638 IMAP_SEARCH_TYPE_UNSEEN,
3639 imapset, &lep_uidlist);
3641 if (r == MAILIMAP_NO_ERROR) {
3644 uidlist = imap_uid_list_from_lep(lep_uidlist);
3645 mailimap_search_result_free(lep_uidlist);
3647 unseen = g_slist_concat(unseen, uidlist);
3650 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_ANSWERED,
3651 imapset, &lep_uidlist);
3652 if (r == MAILIMAP_NO_ERROR) {
3655 uidlist = imap_uid_list_from_lep(lep_uidlist);
3656 mailimap_search_result_free(lep_uidlist);
3658 answered = g_slist_concat(answered, uidlist);
3661 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_FLAGGED,
3662 imapset, &lep_uidlist);
3663 if (r == MAILIMAP_NO_ERROR) {
3666 uidlist = imap_uid_list_from_lep(lep_uidlist);
3667 mailimap_search_result_free(lep_uidlist);
3669 flagged = g_slist_concat(flagged, uidlist);
3672 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_DELETED,
3673 imapset, &lep_uidlist);
3674 if (r == MAILIMAP_NO_ERROR) {
3677 uidlist = imap_uid_list_from_lep(lep_uidlist);
3678 mailimap_search_result_free(lep_uidlist);
3680 deleted = g_slist_concat(deleted, uidlist);
3685 p_answered = answered;
3686 p_flagged = flagged;
3687 p_deleted = deleted;
3689 for (elem = sorted_list; elem != NULL; elem = g_slist_next(elem)) {
3694 msginfo = (MsgInfo *) elem->data;
3695 flags = msginfo->flags.perm_flags;
3696 wasnew = (flags & MSG_NEW);
3697 flags &= ~((reverse_seen ? 0 : MSG_UNREAD | MSG_NEW) | MSG_REPLIED | MSG_MARKED);
3699 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
3700 if (gslist_find_next_num(&p_unseen, msginfo->msgnum) == msginfo->msgnum) {
3701 if (!reverse_seen) {
3702 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
3704 flags &= ~(MSG_UNREAD | MSG_NEW);
3707 if (gslist_find_next_num(&p_answered, msginfo->msgnum) == msginfo->msgnum)
3708 flags |= MSG_REPLIED;
3710 flags &= ~MSG_REPLIED;
3711 if (gslist_find_next_num(&p_flagged, msginfo->msgnum) == msginfo->msgnum)
3712 flags |= MSG_MARKED;
3714 flags &= ~MSG_MARKED;
3715 if (gslist_find_next_num(&p_deleted, msginfo->msgnum) == msginfo->msgnum)
3716 flags |= MSG_DELETED;
3718 flags &= ~MSG_DELETED;
3719 g_relation_insert(msgflags, msginfo, GINT_TO_POINTER(flags));
3722 imap_lep_set_free(seq_list);
3723 g_slist_free(flagged);
3724 g_slist_free(deleted);
3725 g_slist_free(answered);
3726 g_slist_free(unseen);
3727 g_slist_free(sorted_list);
3728 g_string_free(cmd_buf, TRUE);
3732 return GINT_TO_POINTER(0);
3735 static gint imap_get_flags(Folder *folder, FolderItem *item,
3736 MsgInfoList *msginfo_list, GRelation *msgflags)
3739 get_flags_data *data = g_new0(get_flags_data, 1);
3741 data->folder = folder;
3743 data->msginfo_list = msginfo_list;
3744 data->msgflags = msgflags;
3745 data->full_search = FALSE;
3747 GSList *tmp = NULL, *cur;
3749 if (prefs_common.work_offline && !inc_offline_should_override()) {
3754 tmp = folder_item_get_msg_list(item);
3756 if (g_slist_length(tmp) == g_slist_length(msginfo_list))
3757 data->full_search = TRUE;
3759 for (cur = tmp; cur; cur = cur->next)
3760 procmsg_msginfo_free((MsgInfo *)cur->data);
3764 result = GPOINTER_TO_INT(imap_get_flags_thread(data));
3771 static gboolean process_flags(gpointer key, gpointer value, gpointer user_data)
3773 gboolean flags_set = GPOINTER_TO_INT(user_data);
3774 gint flags_value = GPOINTER_TO_INT(key);
3775 hashtable_data *data = (hashtable_data *)value;
3776 IMAPFolderItem *_item = data->item;
3777 FolderItem *item = (FolderItem *)_item;
3778 gint ok = IMAP_ERROR;
3779 IMAPSession *session = imap_session_get(item->folder);
3781 data->msglist = g_slist_reverse(data->msglist);
3783 debug_print("IMAP %ssetting flags to %d for %d messages\n",
3786 g_slist_length(data->msglist));
3790 ok = imap_select(session, IMAP_FOLDER(item->folder), item->path,
3791 NULL, NULL, NULL, NULL, FALSE);
3793 if (ok == IMAP_SUCCESS) {
3794 imap_set_message_flags(data->session, data->msglist, flags_value, flags_set);
3796 g_warning("can't select mailbox %s\n", item->path);
3800 g_slist_free(data->msglist);
3805 static void process_hashtable(IMAPFolderItem *item)
3807 if (item->flags_set_table) {
3808 g_hash_table_foreach_remove(item->flags_set_table, process_flags, GINT_TO_POINTER(TRUE));
3809 g_hash_table_destroy(item->flags_set_table);
3810 item->flags_set_table = NULL;
3812 if (item->flags_unset_table) {
3813 g_hash_table_foreach_remove(item->flags_unset_table, process_flags, GINT_TO_POINTER(FALSE));
3814 g_hash_table_destroy(item->flags_unset_table);
3815 item->flags_unset_table = NULL;
3819 static void imap_set_batch (Folder *folder, FolderItem *_item, gboolean batch)
3821 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3823 g_return_if_fail(item != NULL);
3825 if (item->batching == batch)
3829 item->batching = TRUE;
3830 debug_print("IMAP switching to batch mode\n");
3831 if (!item->flags_set_table) {
3832 item->flags_set_table = g_hash_table_new(NULL, g_direct_equal);
3834 if (!item->flags_unset_table) {
3835 item->flags_unset_table = g_hash_table_new(NULL, g_direct_equal);
3838 debug_print("IMAP switching away from batch mode\n");
3840 process_hashtable(item);
3841 item->batching = FALSE;
3847 /* data types conversion libetpan <-> sylpheed */
3851 #define ETPAN_IMAP_MB_MARKED 1
3852 #define ETPAN_IMAP_MB_UNMARKED 2
3853 #define ETPAN_IMAP_MB_NOSELECT 4
3854 #define ETPAN_IMAP_MB_NOINFERIORS 8
3856 static int imap_flags_to_flags(struct mailimap_mbx_list_flags * imap_flags)
3862 if (imap_flags->mbf_type == MAILIMAP_MBX_LIST_FLAGS_SFLAG) {
3863 switch (imap_flags->mbf_sflag) {
3864 case MAILIMAP_MBX_LIST_SFLAG_MARKED:
3865 flags |= ETPAN_IMAP_MB_MARKED;
3867 case MAILIMAP_MBX_LIST_SFLAG_NOSELECT:
3868 flags |= ETPAN_IMAP_MB_NOSELECT;
3870 case MAILIMAP_MBX_LIST_SFLAG_UNMARKED:
3871 flags |= ETPAN_IMAP_MB_UNMARKED;
3876 for(cur = clist_begin(imap_flags->mbf_oflags) ; cur != NULL ;
3877 cur = clist_next(cur)) {
3878 struct mailimap_mbx_list_oflag * oflag;
3880 oflag = clist_content(cur);
3882 switch (oflag->of_type) {
3883 case MAILIMAP_MBX_LIST_OFLAG_NOINFERIORS:
3884 flags |= ETPAN_IMAP_MB_NOINFERIORS;
3892 static GSList * imap_list_from_lep(IMAPFolder * folder,
3893 clist * list, const gchar * real_path, gboolean all)
3900 for(iter = clist_begin(list) ; iter != NULL ;
3901 iter = clist_next(iter)) {
3902 struct mailimap_mailbox_list * mb;
3910 FolderItem *new_item;
3912 mb = clist_content(iter);
3918 if (mb->mb_flag != NULL)
3919 flags = imap_flags_to_flags(mb->mb_flag);
3921 delimiter = mb->mb_delimiter;
3924 dup_name = strdup(name);
3925 if (delimiter != '\0')
3926 subst_char(dup_name, delimiter, '/');
3928 base = g_path_get_basename(dup_name);
3929 if (base[0] == '.') {
3935 if (!all && strcmp(dup_name, real_path) == 0) {
3941 if (!all && dup_name[strlen(dup_name)-1] == '/') {
3947 loc_name = imap_modified_utf7_to_utf8(base);
3948 loc_path = imap_modified_utf7_to_utf8(dup_name);
3950 new_item = folder_item_new(FOLDER(folder), loc_name, loc_path);
3951 if ((flags & ETPAN_IMAP_MB_NOINFERIORS) != 0)
3952 new_item->no_sub = TRUE;
3953 if (strcmp(dup_name, "INBOX") != 0 &&
3954 ((flags & ETPAN_IMAP_MB_NOSELECT) != 0))
3955 new_item->no_select = TRUE;
3957 item_list = g_slist_append(item_list, new_item);
3959 debug_print("folder '%s' found.\n", loc_path);
3970 static GSList * imap_get_lep_set_from_numlist(MsgNumberList *numlist)
3972 GSList *sorted_list, *cur;
3973 guint first, last, next;
3974 GSList *ret_list = NULL;
3976 struct mailimap_set * current_set;
3977 unsigned int item_count;
3979 if (numlist == NULL)
3983 current_set = mailimap_set_new_empty();
3985 sorted_list = g_slist_copy(numlist);
3986 sorted_list = g_slist_sort(sorted_list, g_int_compare);
3988 first = GPOINTER_TO_INT(sorted_list->data);
3991 for (cur = sorted_list; cur != NULL; cur = g_slist_next(cur)) {
3992 if (GPOINTER_TO_INT(cur->data) == 0)
3997 last = GPOINTER_TO_INT(cur->data);
3999 next = GPOINTER_TO_INT(cur->next->data);
4003 if (last + 1 != next || next == 0) {
4005 struct mailimap_set_item * item;
4006 item = mailimap_set_item_new(first, last);
4007 mailimap_set_add(current_set, item);
4012 if (count >= IMAP_SET_MAX_COUNT) {
4013 ret_list = g_slist_append(ret_list,
4015 current_set = mailimap_set_new_empty();
4022 if (clist_count(current_set->set_list) > 0) {
4023 ret_list = g_slist_append(ret_list,
4027 g_slist_free(sorted_list);
4032 static GSList * imap_get_lep_set_from_msglist(MsgInfoList *msglist)
4034 MsgNumberList *numlist = NULL;
4038 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
4039 MsgInfo *msginfo = (MsgInfo *) cur->data;
4041 numlist = g_slist_append(numlist, GINT_TO_POINTER(msginfo->msgnum));
4043 seq_list = imap_get_lep_set_from_numlist(numlist);
4044 g_slist_free(numlist);
4049 static GSList * imap_uid_list_from_lep(clist * list)
4056 for(iter = clist_begin(list) ; iter != NULL ;
4057 iter = clist_next(iter)) {
4060 puid = clist_content(iter);
4061 result = g_slist_append(result, GINT_TO_POINTER(* puid));
4067 static GSList * imap_uid_list_from_lep_tab(carray * list)
4074 for(i = 0 ; i < carray_count(list) ; i ++) {
4077 puid = carray_get(list, i);
4078 result = g_slist_append(result, GINT_TO_POINTER(* puid));
4084 static MsgInfo *imap_envelope_from_lep(struct imap_fetch_env_info * info,
4087 MsgInfo *msginfo = NULL;
4090 MsgFlags flags = {0, 0};
4092 if (info->headers == NULL)
4095 MSG_SET_TMP_FLAGS(flags, MSG_IMAP);
4096 if (folder_has_parent_of_type(item, F_QUEUE)) {
4097 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
4098 } else if (folder_has_parent_of_type(item, F_DRAFT)) {
4099 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
4101 flags.perm_flags = info->flags;
4105 msginfo = procheader_parse_str(info->headers, flags, FALSE, FALSE);
4108 msginfo->msgnum = uid;
4109 msginfo->size = size;
4115 static void imap_lep_set_free(GSList *seq_list)
4119 for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) {
4120 struct mailimap_set * imapset;
4122 imapset = cur->data;
4123 mailimap_set_free(imapset);
4125 g_slist_free(seq_list);
4128 static struct mailimap_flag_list * imap_flag_to_lep(IMAPFlags flags)
4130 struct mailimap_flag_list * flag_list;
4132 flag_list = mailimap_flag_list_new_empty();
4134 if (IMAP_IS_SEEN(flags))
4135 mailimap_flag_list_add(flag_list,
4136 mailimap_flag_new_seen());
4137 if (IMAP_IS_ANSWERED(flags))
4138 mailimap_flag_list_add(flag_list,
4139 mailimap_flag_new_answered());
4140 if (IMAP_IS_FLAGGED(flags))
4141 mailimap_flag_list_add(flag_list,
4142 mailimap_flag_new_flagged());
4143 if (IMAP_IS_DELETED(flags))
4144 mailimap_flag_list_add(flag_list,
4145 mailimap_flag_new_deleted());
4146 if (IMAP_IS_DRAFT(flags))
4147 mailimap_flag_list_add(flag_list,
4148 mailimap_flag_new_draft());
4153 guint imap_folder_get_refcnt(Folder *folder)
4155 return ((IMAPFolder *)folder)->refcnt;
4158 void imap_folder_ref(Folder *folder)
4160 ((IMAPFolder *)folder)->refcnt++;
4163 void imap_folder_unref(Folder *folder)
4165 if (((IMAPFolder *)folder)->refcnt > 0)
4166 ((IMAPFolder *)folder)->refcnt--;
4169 #else /* HAVE_LIBETPAN */
4171 static FolderClass imap_class;
4173 static Folder *imap_folder_new (const gchar *name,
4178 static gint imap_create_tree (Folder *folder)
4182 static FolderItem *imap_create_folder (Folder *folder,
4188 static gint imap_rename_folder (Folder *folder,
4195 FolderClass *imap_get_class(void)
4197 if (imap_class.idstr == NULL) {
4198 imap_class.type = F_IMAP;
4199 imap_class.idstr = "imap";
4200 imap_class.uistr = "IMAP4";
4202 imap_class.new_folder = imap_folder_new;
4203 imap_class.create_tree = imap_create_tree;
4204 imap_class.create_folder = imap_create_folder;
4205 imap_class.rename_folder = imap_rename_folder;
4206 /* nothing implemented */
4213 void imap_synchronise(FolderItem *item)
4215 imap_gtk_synchronise(item);