2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2006 Hiroyuki Yamamoto and the Sylpheed-Claws team
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;
679 static time_t last_failure = 0;
681 g_return_val_if_fail(folder != NULL, NULL);
682 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, NULL);
683 g_return_val_if_fail(folder->account != NULL, NULL);
685 if (prefs_common.work_offline &&
686 !inc_offline_should_override(
687 _("Sylpheed-Claws needs network access in order "
688 "to access the IMAP server."))) {
692 /* Make sure we have a session */
693 if (rfolder->session != NULL) {
694 session = IMAP_SESSION(rfolder->session);
695 /* don't do that yet...
700 imap_reset_uid_lists(folder);
701 if (time(NULL) - last_failure <= 2)
703 session = imap_session_new(folder, folder->account);
705 if(session == NULL) {
706 last_failure = time(NULL);
710 /* Make sure session is authenticated */
711 if (!IMAP_SESSION(session)->authenticated)
712 imap_session_authenticate(IMAP_SESSION(session), folder->account);
714 if (!IMAP_SESSION(session)->authenticated) {
715 session_destroy(SESSION(session));
716 rfolder->session = NULL;
717 last_failure = time(NULL);
721 /* I think the point of this code is to avoid sending a
722 * keepalive if we've used the session recently and therefore
723 * think it's still alive. Unfortunately, most of the code
724 * does not yet check for errors on the socket, and so if the
725 * connection drops we don't notice until the timeout expires.
726 * A better solution than sending a NOOP every time would be
727 * for every command to be prepared to retry until it is
728 * successfully sent. -- mbp */
729 if (time(NULL) - SESSION(session)->last_access_time > SESSION_TIMEOUT_INTERVAL) {
730 /* verify that the session is still alive */
731 if (imap_cmd_noop(session) != IMAP_SUCCESS) {
732 debug_print("disconnected!\n");
733 session = imap_reconnect_if_possible(folder, session);
737 rfolder->session = SESSION(session);
739 return IMAP_SESSION(session);
742 static IMAPSession *imap_session_new(Folder * folder,
743 const PrefsAccount *account)
745 IMAPSession *session;
751 /* FIXME: IMAP over SSL only... */
754 port = account->set_imapport ? account->imapport
755 : account->ssl_imap == SSL_TUNNEL ? IMAPS_PORT : IMAP4_PORT;
756 ssl_type = account->ssl_imap;
758 if (account->ssl_imap != SSL_NONE) {
759 if (alertpanel_full(_("Insecure connection"),
760 _("This connection is configured to be secured "
761 "using SSL, but SSL is not available in this "
762 "build of Sylpheed-Claws. \n\n"
763 "Do you want to continue connecting to this "
764 "server? The communication would not be "
766 _("Con_tinue connecting"),
767 GTK_STOCK_CANCEL, NULL,
768 FALSE, NULL, ALERT_WARNING,
769 G_ALERTALTERNATE) != G_ALERTDEFAULT)
772 port = account->set_imapport ? account->imapport
777 statusbar_print_all(_("Connecting to IMAP4 server: %s..."), folder->account->recv_server);
778 if (account->set_tunnelcmd) {
779 r = imap_threaded_connect_cmd(folder,
781 account->recv_server,
786 if (ssl_type == SSL_TUNNEL) {
787 r = imap_threaded_connect_ssl(folder,
788 account->recv_server,
794 r = imap_threaded_connect(folder,
795 account->recv_server,
801 if (r == MAILIMAP_NO_ERROR_AUTHENTICATED) {
802 authenticated = TRUE;
804 else if (r == MAILIMAP_NO_ERROR_NON_AUTHENTICATED) {
805 authenticated = FALSE;
808 if(!prefs_common.no_recv_err_panel) {
809 alertpanel_error(_("Can't connect to IMAP4 server: %s:%d"),
810 account->recv_server, port);
812 log_error(_("Can't connect to IMAP4 server: %s:%d\n"),
813 account->recv_server, port);
819 session = g_new0(IMAPSession, 1);
820 session_init(SESSION(session));
821 SESSION(session)->type = SESSION_IMAP;
822 SESSION(session)->server = g_strdup(account->recv_server);
823 SESSION(session)->sock = NULL;
825 SESSION(session)->destroy = imap_session_destroy;
827 session->capability = NULL;
829 session->authenticated = authenticated;
830 session->mbox = NULL;
831 session->cmd_count = 0;
832 session->folder = folder;
833 IMAP_FOLDER(session->folder)->last_seen_separator = 0;
836 if (account->ssl_imap == SSL_STARTTLS) {
839 ok = imap_cmd_starttls(session);
840 if (ok != IMAP_SUCCESS) {
841 log_warning(_("Can't start TLS session.\n"));
842 session_destroy(SESSION(session));
846 imap_free_capabilities(session);
847 session->authenticated = FALSE;
848 session->uidplus = FALSE;
849 session->cmd_count = 1;
852 log_message("IMAP connection is %s-authenticated\n",
853 (session->authenticated) ? "pre" : "un");
858 static void imap_session_authenticate(IMAPSession *session,
859 const PrefsAccount *account)
863 g_return_if_fail(account->userid != NULL);
865 pass = account->passwd;
868 tmp_pass = input_dialog_query_password(account->recv_server, account->userid);
871 Xstrdup_a(pass, tmp_pass, {g_free(tmp_pass); return;});
874 statusbar_print_all(_("Connecting to IMAP4 server %s...\n"),
875 account->recv_server);
876 if (imap_auth(session, account->userid, pass, account->imap_auth_type) != IMAP_SUCCESS) {
877 imap_threaded_disconnect(session->folder);
878 imap_cmd_logout(session);
884 session->authenticated = TRUE;
887 static void imap_session_destroy(Session *session)
889 if (session->state != SESSION_DISCONNECTED)
890 imap_threaded_disconnect(IMAP_SESSION(session)->folder);
892 imap_free_capabilities(IMAP_SESSION(session));
893 g_free(IMAP_SESSION(session)->mbox);
894 sock_close(session->sock);
895 session->sock = NULL;
898 static gchar *imap_fetch_msg(Folder *folder, FolderItem *item, gint uid)
900 return imap_fetch_msg_full(folder, item, uid, TRUE, TRUE);
903 static guint get_size_with_crs(MsgInfo *info)
912 fp = procmsg_open_message(info);
916 while (fgets(buf, sizeof (buf), fp) != NULL) {
918 if (!strstr(buf, "\r") && strstr(buf, "\n"))
926 static gchar *imap_fetch_msg_full(Folder *folder, FolderItem *item, gint uid,
927 gboolean headers, gboolean body)
929 gchar *path, *filename;
930 IMAPSession *session;
933 g_return_val_if_fail(folder != NULL, NULL);
934 g_return_val_if_fail(item != NULL, NULL);
939 path = folder_item_get_path(item);
940 if (!is_dir_exist(path))
942 filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(uid), NULL);
944 debug_print("trying to fetch cached %s\n", filename);
945 if (is_file_exist(filename)) {
946 /* see whether the local file represents the whole message
947 * or not. As the IMAP server reports size with \r chars,
948 * we have to update the local file (UNIX \n only) size */
949 MsgInfo *msginfo = imap_parse_msg(filename, item);
950 MsgInfo *cached = msgcache_get_msg(item->cache,uid);
951 guint have_size = get_size_with_crs(msginfo);
954 debug_print("message %d has been already %scached (%d/%d).\n", uid,
955 have_size == cached->size ? "fully ":"",
956 have_size, (int)cached->size);
958 if (cached && (cached->size == have_size || !body)) {
959 procmsg_msginfo_free(cached);
960 procmsg_msginfo_free(msginfo);
961 file_strip_crs(filename);
963 } else if (!cached) {
964 debug_print("message not cached, considering file complete\n");
965 procmsg_msginfo_free(msginfo);
966 file_strip_crs(filename);
969 procmsg_msginfo_free(cached);
970 procmsg_msginfo_free(msginfo);
974 session = imap_session_get(folder);
983 debug_print("IMAP fetching messages\n");
984 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
985 NULL, NULL, NULL, NULL, FALSE);
986 if (ok != IMAP_SUCCESS) {
987 g_warning("can't select mailbox %s\n", item->path);
993 debug_print("getting message %d...\n", uid);
994 ok = imap_cmd_fetch(session, (guint32)uid, filename, headers, body);
996 if (ok != IMAP_SUCCESS) {
997 g_warning("can't fetch message %d\n", uid);
1004 file_strip_crs(filename);
1008 static gint imap_add_msg(Folder *folder, FolderItem *dest,
1009 const gchar *file, MsgFlags *flags)
1013 MsgFileInfo fileinfo;
1015 g_return_val_if_fail(file != NULL, -1);
1017 fileinfo.msginfo = NULL;
1018 fileinfo.file = (gchar *)file;
1019 fileinfo.flags = flags;
1020 file_list.data = &fileinfo;
1021 file_list.next = NULL;
1023 ret = imap_add_msgs(folder, dest, &file_list, NULL);
1027 static gint imap_add_msgs(Folder *folder, FolderItem *dest, GSList *file_list,
1028 GRelation *relation)
1031 IMAPSession *session;
1032 guint32 last_uid = 0;
1034 MsgFileInfo *fileinfo;
1036 gint curnum = 0, total = 0;
1039 g_return_val_if_fail(folder != NULL, -1);
1040 g_return_val_if_fail(dest != NULL, -1);
1041 g_return_val_if_fail(file_list != NULL, -1);
1043 session = imap_session_get(folder);
1048 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
1050 statusbar_print_all(_("Adding messages..."));
1051 total = g_slist_length(file_list);
1052 for (cur = file_list; cur != NULL; cur = cur->next) {
1053 IMAPFlags iflags = 0;
1054 guint32 new_uid = 0;
1055 gchar *real_file = NULL;
1056 gboolean file_is_tmp = FALSE;
1057 fileinfo = (MsgFileInfo *)cur->data;
1059 statusbar_progress_all(curnum, total, 1);
1062 if (fileinfo->flags) {
1063 if (MSG_IS_MARKED(*fileinfo->flags))
1064 iflags |= IMAP_FLAG_FLAGGED;
1065 if (MSG_IS_REPLIED(*fileinfo->flags))
1066 iflags |= IMAP_FLAG_ANSWERED;
1067 if (!MSG_IS_UNREAD(*fileinfo->flags))
1068 iflags |= IMAP_FLAG_SEEN;
1071 if (fileinfo->flags) {
1072 if ((MSG_IS_QUEUED(*fileinfo->flags)
1073 || MSG_IS_DRAFT(*fileinfo->flags))
1074 && !folder_has_parent_of_type(dest, F_QUEUE)
1075 && !folder_has_parent_of_type(dest, F_DRAFT)) {
1076 real_file = get_tmp_file();
1078 if (procmsg_remove_special_headers(
1086 } else if (!(MSG_IS_QUEUED(*fileinfo->flags)
1087 || MSG_IS_DRAFT(*fileinfo->flags))
1088 && (folder_has_parent_of_type(dest, F_QUEUE)
1089 || folder_has_parent_of_type(dest, F_DRAFT))) {
1093 if (real_file == NULL)
1094 real_file = g_strdup(fileinfo->file);
1096 if (folder_has_parent_of_type(dest, F_QUEUE) ||
1097 folder_has_parent_of_type(dest, F_OUTBOX) ||
1098 folder_has_parent_of_type(dest, F_DRAFT) ||
1099 folder_has_parent_of_type(dest, F_TRASH))
1100 iflags |= IMAP_FLAG_SEEN;
1102 ok = imap_cmd_append(session, destdir, real_file, iflags,
1105 if (ok != IMAP_SUCCESS) {
1106 g_warning("can't append message %s\n", real_file);
1108 g_unlink(real_file);
1112 statusbar_progress_all(0,0,0);
1113 statusbar_pop_all();
1116 debug_print("appended new message as %d\n", new_uid);
1117 /* put the local file in the imapcache, so that we don't
1118 * have to fetch it back later. */
1120 gchar *cache_path = folder_item_get_path(dest);
1121 if (!is_dir_exist(cache_path))
1122 make_dir_hier(cache_path);
1123 if (is_dir_exist(cache_path)) {
1124 gchar *cache_file = g_strconcat(
1125 cache_path, G_DIR_SEPARATOR_S,
1126 itos(new_uid), NULL);
1127 copy_file(real_file, cache_file, TRUE);
1128 debug_print("copied to cache: %s\n", cache_file);
1135 if (relation != NULL)
1136 g_relation_insert(relation, fileinfo->msginfo != NULL ?
1137 (gpointer) fileinfo->msginfo : (gpointer) fileinfo,
1138 GINT_TO_POINTER(dest->last_num + 1));
1140 new_uid = dest->last_num+1;
1142 if (last_uid < new_uid)
1145 g_unlink(real_file);
1149 statusbar_progress_all(0,0,0);
1150 statusbar_pop_all();
1152 imap_cmd_expunge(session);
1160 static gint imap_do_copy_msgs(Folder *folder, FolderItem *dest,
1161 MsgInfoList *msglist, GRelation *relation)
1165 GSList *seq_list, *cur;
1167 IMAPSession *session;
1168 gint ok = IMAP_SUCCESS;
1169 GRelation *uid_mapping;
1172 g_return_val_if_fail(folder != NULL, -1);
1173 g_return_val_if_fail(dest != NULL, -1);
1174 g_return_val_if_fail(msglist != NULL, -1);
1176 session = imap_session_get(folder);
1182 msginfo = (MsgInfo *)msglist->data;
1184 src = msginfo->folder;
1186 g_warning("the src folder is identical to the dest.\n");
1191 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
1192 NULL, NULL, NULL, NULL, FALSE);
1193 if (ok != IMAP_SUCCESS) {
1198 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
1199 seq_list = imap_get_lep_set_from_msglist(msglist);
1200 uid_mapping = g_relation_new(2);
1201 g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
1203 statusbar_print_all(_("Copying messages..."));
1204 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
1205 struct mailimap_set * seq_set;
1206 seq_set = cur->data;
1208 debug_print("Copying messages from %s to %s ...\n",
1209 src->path, destdir);
1211 ok = imap_cmd_copy(session, seq_set, destdir, uid_mapping);
1212 if (ok != IMAP_SUCCESS) {
1213 g_relation_destroy(uid_mapping);
1214 imap_lep_set_free(seq_list);
1220 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
1221 MsgInfo *msginfo = (MsgInfo *)cur->data;
1224 tuples = g_relation_select(uid_mapping,
1225 GINT_TO_POINTER(msginfo->msgnum),
1227 if (tuples->len > 0) {
1228 gint num = GPOINTER_TO_INT(g_tuples_index(tuples, 0, 1));
1229 g_relation_insert(relation, msginfo,
1230 GPOINTER_TO_INT(num));
1234 g_relation_insert(relation, msginfo,
1235 GPOINTER_TO_INT(0));
1236 g_tuples_destroy(tuples);
1238 statusbar_pop_all();
1240 g_relation_destroy(uid_mapping);
1241 imap_lep_set_free(seq_list);
1245 IMAP_FOLDER_ITEM(dest)->lastuid = 0;
1246 IMAP_FOLDER_ITEM(dest)->uid_next = 0;
1247 g_slist_free(IMAP_FOLDER_ITEM(dest)->uid_list);
1248 IMAP_FOLDER_ITEM(dest)->uid_list = NULL;
1251 if (ok == IMAP_SUCCESS)
1257 static gint imap_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1261 g_return_val_if_fail(msginfo != NULL, -1);
1263 msglist.data = msginfo;
1264 msglist.next = NULL;
1266 return imap_copy_msgs(folder, dest, &msglist, NULL);
1269 static gint imap_copy_msgs(Folder *folder, FolderItem *dest,
1270 MsgInfoList *msglist, GRelation *relation)
1276 g_return_val_if_fail(folder != NULL, -1);
1277 g_return_val_if_fail(dest != NULL, -1);
1278 g_return_val_if_fail(msglist != NULL, -1);
1280 msginfo = (MsgInfo *)msglist->data;
1281 g_return_val_if_fail(msginfo->folder != NULL, -1);
1283 /* if from/to are the same "type" (with or without extra headers),
1284 * copy them via imap */
1285 if (folder == msginfo->folder->folder &&
1286 !folder_has_parent_of_type(msginfo->folder, F_DRAFT) &&
1287 !folder_has_parent_of_type(msginfo->folder, F_QUEUE) &&
1288 !folder_has_parent_of_type(dest, F_DRAFT) &&
1289 !folder_has_parent_of_type(dest, F_QUEUE)) {
1290 ret = imap_do_copy_msgs(folder, dest, msglist, relation);
1292 } else if (folder == msginfo->folder->folder &&
1293 (folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
1294 folder_has_parent_of_type(msginfo->folder, F_QUEUE)) &&
1295 (folder_has_parent_of_type(dest, F_DRAFT) ||
1296 folder_has_parent_of_type(dest, F_QUEUE))) {
1297 ret = imap_do_copy_msgs(folder, dest, msglist, relation);
1300 /* else reupload them */
1301 file_list = procmsg_get_message_file_list(msglist);
1302 g_return_val_if_fail(file_list != NULL, -1);
1304 ret = imap_add_msgs(folder, dest, file_list, relation);
1305 procmsg_message_file_list_free(file_list);
1311 static gint imap_do_remove_msgs(Folder *folder, FolderItem *dest,
1312 MsgInfoList *msglist, GRelation *relation)
1315 GSList *numlist = NULL, *cur;
1317 IMAPSession *session;
1318 gint ok = IMAP_SUCCESS;
1319 GRelation *uid_mapping;
1321 g_return_val_if_fail(folder != NULL, -1);
1322 g_return_val_if_fail(dest != NULL, -1);
1323 g_return_val_if_fail(msglist != NULL, -1);
1325 session = imap_session_get(folder);
1330 msginfo = (MsgInfo *)msglist->data;
1332 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
1333 NULL, NULL, NULL, NULL, FALSE);
1334 if (ok != IMAP_SUCCESS) {
1339 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
1340 for (cur = msglist; cur; cur = cur->next) {
1341 msginfo = (MsgInfo *)cur->data;
1342 if (!MSG_IS_DELETED(msginfo->flags))
1343 numlist = g_slist_append(numlist, GINT_TO_POINTER(msginfo->msgnum));
1346 uid_mapping = g_relation_new(2);
1347 g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
1349 ok = imap_set_message_flags
1350 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
1351 numlist, IMAP_FLAG_DELETED, TRUE);
1352 if (ok != IMAP_SUCCESS) {
1353 log_warning(_("can't set deleted flags\n"));
1357 ok = imap_cmd_expunge(session);
1358 if (ok != IMAP_SUCCESS) {
1359 log_warning(_("can't expunge\n"));
1364 g_relation_destroy(uid_mapping);
1365 g_slist_free(numlist);
1369 if (ok == IMAP_SUCCESS)
1375 static gint imap_remove_msgs(Folder *folder, FolderItem *dest,
1376 MsgInfoList *msglist, GRelation *relation)
1380 g_return_val_if_fail(folder != NULL, -1);
1381 g_return_val_if_fail(dest != NULL, -1);
1382 if (msglist == NULL)
1385 msginfo = (MsgInfo *)msglist->data;
1386 g_return_val_if_fail(msginfo->folder != NULL, -1);
1388 return imap_do_remove_msgs(folder, dest, msglist, relation);
1391 static gint imap_remove_all_msg(Folder *folder, FolderItem *item)
1393 GSList *list = folder_item_get_msg_list(item);
1394 gint res = imap_remove_msgs(folder, item, list, NULL);
1395 procmsg_msg_list_free(list);
1399 static gboolean imap_is_msg_changed(Folder *folder, FolderItem *item,
1402 /* TODO: properly implement this method */
1406 static gint imap_close(Folder *folder, FolderItem *item)
1411 static gint imap_scan_tree(Folder *folder)
1413 FolderItem *item = NULL;
1414 IMAPSession *session;
1415 gchar *root_folder = NULL;
1417 g_return_val_if_fail(folder != NULL, -1);
1418 g_return_val_if_fail(folder->account != NULL, -1);
1420 session = imap_session_get(folder);
1422 if (!folder->node) {
1423 folder_tree_destroy(folder);
1424 item = folder_item_new(folder, folder->name, NULL);
1425 item->folder = folder;
1426 folder->node = item->node = g_node_new(item);
1432 if (folder->account->imap_dir && *folder->account->imap_dir) {
1437 Xstrdup_a(root_folder, folder->account->imap_dir, {unlock_session();return -1;});
1438 extract_quote(root_folder, '"');
1439 subst_char(root_folder,
1440 imap_get_path_separator(IMAP_FOLDER(folder),
1443 strtailchomp(root_folder, '/');
1444 real_path = imap_get_real_path
1445 (IMAP_FOLDER(folder), root_folder);
1446 debug_print("IMAP root directory: %s\n", real_path);
1448 /* check if root directory exist */
1450 r = imap_threaded_list(session->folder, "", real_path,
1452 if ((r != MAILIMAP_NO_ERROR) || (clist_count(lep_list) == 0)) {
1453 if (!folder->node) {
1454 item = folder_item_new(folder, folder->name, NULL);
1455 item->folder = folder;
1456 folder->node = item->node = g_node_new(item);
1461 mailimap_list_result_free(lep_list);
1467 item = FOLDER_ITEM(folder->node->data);
1468 if (!item || ((item->path || root_folder) &&
1469 strcmp2(item->path, root_folder) != 0)) {
1470 folder_tree_destroy(folder);
1471 item = folder_item_new(folder, folder->name, root_folder);
1472 item->folder = folder;
1473 folder->node = item->node = g_node_new(item);
1476 imap_scan_tree_recursive(session, FOLDER_ITEM(folder->node->data));
1477 imap_create_missing_folders(folder);
1483 static gint imap_scan_tree_recursive(IMAPSession *session, FolderItem *item)
1486 IMAPFolder *imapfolder;
1487 FolderItem *new_item;
1488 GSList *item_list, *cur;
1491 gchar *wildcard_path;
1497 g_return_val_if_fail(item != NULL, -1);
1498 g_return_val_if_fail(item->folder != NULL, -1);
1499 g_return_val_if_fail(item->no_sub == FALSE, -1);
1501 folder = item->folder;
1502 imapfolder = IMAP_FOLDER(folder);
1504 separator = imap_get_path_separator(imapfolder, item->path);
1506 if (folder->ui_func)
1507 folder->ui_func(folder, item, folder->ui_func_data);
1510 wildcard[0] = separator;
1513 real_path = imap_get_real_path(imapfolder, item->path);
1517 real_path = g_strdup("");
1520 Xstrcat_a(wildcard_path, real_path, wildcard,
1521 {g_free(real_path); return IMAP_ERROR;});
1523 r = imap_threaded_list(folder, "", wildcard_path, &lep_list);
1524 if (r != MAILIMAP_NO_ERROR) {
1528 item_list = imap_list_from_lep(imapfolder,
1529 lep_list, real_path, FALSE);
1530 mailimap_list_result_free(lep_list);
1535 node = item->node->children;
1536 while (node != NULL) {
1537 FolderItem *old_item = FOLDER_ITEM(node->data);
1538 GNode *next = node->next;
1541 for (cur = item_list; cur != NULL; cur = cur->next) {
1542 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1543 if (!strcmp2(old_item->path, cur_item->path)) {
1544 new_item = cur_item;
1549 debug_print("folder '%s' not found. removing...\n",
1551 folder_item_remove(old_item);
1553 old_item->no_sub = new_item->no_sub;
1554 old_item->no_select = new_item->no_select;
1555 if (old_item->no_sub == TRUE && node->children) {
1556 debug_print("folder '%s' doesn't have "
1557 "subfolders. removing...\n",
1559 folder_item_remove_children(old_item);
1566 for (cur = item_list; cur != NULL; cur = cur->next) {
1567 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1570 for (node = item->node->children; node != NULL;
1571 node = node->next) {
1572 if (!strcmp2(FOLDER_ITEM(node->data)->path,
1574 new_item = FOLDER_ITEM(node->data);
1575 folder_item_destroy(cur_item);
1581 new_item = cur_item;
1582 debug_print("new folder '%s' found.\n", new_item->path);
1583 folder_item_append(item, new_item);
1586 if (!strcmp(new_item->path, "INBOX")) {
1587 new_item->stype = F_INBOX;
1588 folder->inbox = new_item;
1589 } else if (!folder_item_parent(item) || item->stype == F_INBOX) {
1592 base = g_path_get_basename(new_item->path);
1594 if (!folder->outbox && !g_ascii_strcasecmp(base, "Sent")) {
1595 new_item->stype = F_OUTBOX;
1596 folder->outbox = new_item;
1597 } else if (!folder->draft && !g_ascii_strcasecmp(base, "Drafts")) {
1598 new_item->stype = F_DRAFT;
1599 folder->draft = new_item;
1600 } else if (!folder->queue && !g_ascii_strcasecmp(base, "Queue")) {
1601 new_item->stype = F_QUEUE;
1602 folder->queue = new_item;
1603 } else if (!folder->trash && !g_ascii_strcasecmp(base, "Trash")) {
1604 new_item->stype = F_TRASH;
1605 folder->trash = new_item;
1610 if (new_item->no_sub == FALSE)
1611 imap_scan_tree_recursive(session, new_item);
1614 g_slist_free(item_list);
1616 return IMAP_SUCCESS;
1619 static gint imap_create_tree(Folder *folder)
1621 g_return_val_if_fail(folder != NULL, -1);
1622 g_return_val_if_fail(folder->node != NULL, -1);
1623 g_return_val_if_fail(folder->node->data != NULL, -1);
1624 g_return_val_if_fail(folder->account != NULL, -1);
1626 imap_scan_tree(folder);
1627 imap_create_missing_folders(folder);
1632 static void imap_create_missing_folders(Folder *folder)
1634 g_return_if_fail(folder != NULL);
1637 folder->inbox = imap_create_special_folder
1638 (folder, F_INBOX, "INBOX");
1640 folder->trash = imap_create_special_folder
1641 (folder, F_TRASH, "Trash");
1643 folder->queue = imap_create_special_folder
1644 (folder, F_QUEUE, "Queue");
1645 if (!folder->outbox)
1646 folder->outbox = imap_create_special_folder
1647 (folder, F_OUTBOX, "Sent");
1649 folder->draft = imap_create_special_folder
1650 (folder, F_DRAFT, "Drafts");
1653 static FolderItem *imap_create_special_folder(Folder *folder,
1654 SpecialFolderItemType stype,
1658 FolderItem *new_item;
1660 g_return_val_if_fail(folder != NULL, NULL);
1661 g_return_val_if_fail(folder->node != NULL, NULL);
1662 g_return_val_if_fail(folder->node->data != NULL, NULL);
1663 g_return_val_if_fail(folder->account != NULL, NULL);
1664 g_return_val_if_fail(name != NULL, NULL);
1666 item = FOLDER_ITEM(folder->node->data);
1667 new_item = imap_create_folder(folder, item, name);
1670 g_warning("Can't create '%s'\n", name);
1671 if (!folder->inbox) return NULL;
1673 new_item = imap_create_folder(folder, folder->inbox, name);
1675 g_warning("Can't create '%s' under INBOX\n", name);
1677 new_item->stype = stype;
1679 new_item->stype = stype;
1684 static gchar *imap_folder_get_path(Folder *folder)
1688 g_return_val_if_fail(folder != NULL, NULL);
1689 g_return_val_if_fail(folder->account != NULL, NULL);
1691 folder_path = g_strconcat(get_imap_cache_dir(),
1693 folder->account->recv_server,
1695 folder->account->userid,
1701 static gchar *imap_item_get_path(Folder *folder, FolderItem *item)
1703 gchar *folder_path, *path;
1705 g_return_val_if_fail(folder != NULL, NULL);
1706 g_return_val_if_fail(item != NULL, NULL);
1707 folder_path = imap_folder_get_path(folder);
1709 g_return_val_if_fail(folder_path != NULL, NULL);
1710 if (folder_path[0] == G_DIR_SEPARATOR) {
1712 path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
1715 path = g_strdup(folder_path);
1718 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1719 folder_path, G_DIR_SEPARATOR_S,
1722 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1725 g_free(folder_path);
1730 static FolderItem *imap_create_folder(Folder *folder, FolderItem *parent,
1733 gchar *dirpath, *imap_path;
1734 IMAPSession *session;
1735 FolderItem *new_item;
1740 gboolean no_select = FALSE, no_sub = FALSE;
1742 g_return_val_if_fail(folder != NULL, NULL);
1743 g_return_val_if_fail(folder->account != NULL, NULL);
1744 g_return_val_if_fail(parent != NULL, NULL);
1745 g_return_val_if_fail(name != NULL, NULL);
1747 session = imap_session_get(folder);
1753 if (!folder_item_parent(parent) && strcmp(name, "INBOX") == 0) {
1754 dirpath = g_strdup(name);
1755 }else if (parent->path)
1756 dirpath = g_strconcat(parent->path, "/", name, NULL);
1757 else if ((p = strchr(name, '/')) != NULL && *(p + 1) != '\0')
1758 dirpath = g_strdup(name);
1759 else if (folder->account->imap_dir && *folder->account->imap_dir) {
1762 Xstrdup_a(imap_dir, folder->account->imap_dir, {unlock_session();return NULL;});
1763 strtailchomp(imap_dir, '/');
1764 dirpath = g_strconcat(imap_dir, "/", name, NULL);
1766 dirpath = g_strdup(name);
1770 /* keep trailing directory separator to create a folder that contains
1772 imap_path = imap_utf8_to_modified_utf7(dirpath);
1774 strtailchomp(dirpath, '/');
1775 Xstrdup_a(new_name, name, {
1780 separator = imap_get_path_separator(IMAP_FOLDER(folder), imap_path);
1781 imap_path_separator_subst(imap_path, separator);
1782 /* remove trailing / for display */
1783 strtailchomp(new_name, '/');
1785 if (strcmp(dirpath, "INBOX") != 0) {
1787 gboolean exist = FALSE;
1791 argbuf = g_ptr_array_new();
1792 r = imap_threaded_list(folder, "", imap_path, &lep_list);
1793 if (r != MAILIMAP_NO_ERROR) {
1794 log_warning(_("can't create mailbox: LIST failed\n"));
1797 ptr_array_free_strings(argbuf);
1798 g_ptr_array_free(argbuf, TRUE);
1803 if (clist_count(lep_list) > 0)
1805 mailimap_list_result_free(lep_list);
1808 ok = imap_cmd_create(session, imap_path);
1809 if (ok != IMAP_SUCCESS) {
1810 log_warning(_("can't create mailbox\n"));
1816 r = imap_threaded_list(folder, "", imap_path, &lep_list);
1817 if (r == MAILIMAP_NO_ERROR) {
1818 GSList *item_list = imap_list_from_lep(IMAP_FOLDER(folder),
1819 lep_list, dirpath, TRUE);
1821 FolderItem *cur_item = FOLDER_ITEM(item_list->data);
1822 no_select = cur_item->no_select;
1823 no_sub = cur_item->no_sub;
1824 g_slist_free(item_list);
1826 mailimap_list_result_free(lep_list);
1833 /* just get flags */
1834 r = imap_threaded_list(folder, "", "INBOX", &lep_list);
1835 if (r == MAILIMAP_NO_ERROR) {
1836 GSList *item_list = imap_list_from_lep(IMAP_FOLDER(folder),
1837 lep_list, dirpath, TRUE);
1839 FolderItem *cur_item = FOLDER_ITEM(item_list->data);
1840 no_select = cur_item->no_select;
1841 no_sub = cur_item->no_sub;
1842 g_slist_free(item_list);
1844 mailimap_list_result_free(lep_list);
1848 new_item = folder_item_new(folder, new_name, dirpath);
1849 new_item->no_select = no_select;
1850 new_item->no_sub = no_sub;
1851 folder_item_append(parent, new_item);
1855 dirpath = folder_item_get_path(new_item);
1856 if (!is_dir_exist(dirpath))
1857 make_dir_hier(dirpath);
1863 static gint imap_rename_folder(Folder *folder, FolderItem *item,
1868 gchar *real_oldpath;
1869 gchar *real_newpath;
1871 gchar *old_cache_dir;
1872 gchar *new_cache_dir;
1873 IMAPSession *session;
1876 gint exists, recent, unseen;
1877 guint32 uid_validity;
1879 g_return_val_if_fail(folder != NULL, -1);
1880 g_return_val_if_fail(item != NULL, -1);
1881 g_return_val_if_fail(item->path != NULL, -1);
1882 g_return_val_if_fail(name != NULL, -1);
1884 session = imap_session_get(folder);
1890 if (strchr(name, imap_get_path_separator(IMAP_FOLDER(folder), item->path)) != NULL) {
1891 g_warning(_("New folder name must not contain the namespace "
1897 real_oldpath = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1899 g_free(session->mbox);
1900 session->mbox = NULL;
1901 ok = imap_cmd_examine(session, "INBOX",
1902 &exists, &recent, &unseen, &uid_validity, FALSE);
1903 if (ok != IMAP_SUCCESS) {
1904 g_free(real_oldpath);
1909 separator = imap_get_path_separator(IMAP_FOLDER(folder), item->path);
1910 if (strchr(item->path, G_DIR_SEPARATOR)) {
1911 dirpath = g_path_get_dirname(item->path);
1912 newpath = g_strconcat(dirpath, G_DIR_SEPARATOR_S, name, NULL);
1915 newpath = g_strdup(name);
1917 real_newpath = imap_utf8_to_modified_utf7(newpath);
1918 imap_path_separator_subst(real_newpath, separator);
1920 ok = imap_cmd_rename(session, real_oldpath, real_newpath);
1921 if (ok != IMAP_SUCCESS) {
1922 log_warning(_("can't rename mailbox: %s to %s\n"),
1923 real_oldpath, real_newpath);
1924 g_free(real_oldpath);
1926 g_free(real_newpath);
1932 item->name = g_strdup(name);
1934 old_cache_dir = folder_item_get_path(item);
1936 paths[0] = g_strdup(item->path);
1938 g_node_traverse(item->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1939 imap_rename_folder_func, paths);
1941 if (is_dir_exist(old_cache_dir)) {
1942 new_cache_dir = folder_item_get_path(item);
1943 if (rename(old_cache_dir, new_cache_dir) < 0) {
1944 FILE_OP_ERROR(old_cache_dir, "rename");
1946 g_free(new_cache_dir);
1949 g_free(old_cache_dir);
1952 g_free(real_oldpath);
1953 g_free(real_newpath);
1958 static gint imap_remove_folder_real(Folder *folder, FolderItem *item)
1961 IMAPSession *session;
1965 g_return_val_if_fail(folder != NULL, -1);
1966 g_return_val_if_fail(item != NULL, -1);
1967 g_return_val_if_fail(item->path != NULL, -1);
1969 session = imap_session_get(folder);
1974 path = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1976 ok = imap_cmd_delete(session, path);
1977 if (ok != IMAP_SUCCESS) {
1978 gchar *tmp = g_strdup_printf("%s%c", path,
1979 imap_get_path_separator(IMAP_FOLDER(folder), path));
1982 ok = imap_cmd_delete(session, path);
1985 if (ok != IMAP_SUCCESS) {
1986 log_warning(_("can't delete mailbox\n"));
1993 cache_dir = folder_item_get_path(item);
1994 if (is_dir_exist(cache_dir) && remove_dir_recursive(cache_dir) < 0)
1995 g_warning("can't remove directory '%s'\n", cache_dir);
1997 folder_item_remove(item);
2002 static gint imap_remove_folder(Folder *folder, FolderItem *item)
2006 g_return_val_if_fail(item != NULL, -1);
2007 g_return_val_if_fail(item->folder != NULL, -1);
2008 g_return_val_if_fail(item->node != NULL, -1);
2010 node = item->node->children;
2011 while (node != NULL) {
2013 if (imap_remove_folder(folder, FOLDER_ITEM(node->data)) < 0)
2017 debug_print("IMAP removing %s\n", item->path);
2019 if (imap_remove_all_msg(folder, item) < 0)
2021 return imap_remove_folder_real(folder, item);
2024 typedef struct _uncached_data {
2025 IMAPSession *session;
2027 MsgNumberList *numlist;
2033 static void *imap_get_uncached_messages_thread(void *data)
2035 uncached_data *stuff = (uncached_data *)data;
2036 IMAPSession *session = stuff->session;
2037 FolderItem *item = stuff->item;
2038 MsgNumberList *numlist = stuff->numlist;
2040 GSList *newlist = NULL;
2041 GSList *llast = NULL;
2042 GSList *seq_list, *cur;
2044 debug_print("uncached_messages\n");
2046 if (session == NULL || item == NULL || item->folder == NULL
2047 || FOLDER_CLASS(item->folder) != &imap_class) {
2052 seq_list = imap_get_lep_set_from_numlist(numlist);
2053 debug_print("get msgs info\n");
2054 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
2055 struct mailimap_set * imapset;
2061 imapset = cur->data;
2063 r = imap_threaded_fetch_env(session->folder,
2064 imapset, &env_list);
2065 if (r != MAILIMAP_NO_ERROR)
2068 session_set_access_time(SESSION(session));
2071 for(i = 0 ; i < carray_count(env_list) ; i ++) {
2072 struct imap_fetch_env_info * info;
2075 info = carray_get(env_list, i);
2076 msginfo = imap_envelope_from_lep(info, item);
2077 if (msginfo == NULL)
2079 msginfo->folder = item;
2081 llast = newlist = g_slist_append(newlist, msginfo);
2083 llast = g_slist_append(llast, msginfo);
2084 llast = llast->next;
2089 imap_fetch_env_free(env_list);
2092 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
2093 struct mailimap_set * imapset;
2095 imapset = cur->data;
2096 mailimap_set_free(imapset);
2099 session_set_access_time(SESSION(session));
2104 #define MAX_MSG_NUM 50
2106 static GSList *imap_get_uncached_messages(IMAPSession *session,
2108 MsgNumberList *numlist)
2110 GSList *result = NULL;
2112 uncached_data *data = g_new0(uncached_data, 1);
2117 data->total = g_slist_length(numlist);
2118 debug_print("messages list : %i\n", data->total);
2120 while (cur != NULL) {
2121 GSList * partial_result;
2129 while (count < MAX_MSG_NUM) {
2134 if (newlist == NULL)
2135 llast = newlist = g_slist_append(newlist, p);
2137 llast = g_slist_append(llast, p);
2138 llast = llast->next;
2148 data->session = session;
2150 data->numlist = newlist;
2153 if (prefs_common.work_offline &&
2154 !inc_offline_should_override(
2155 _("Sylpheed-Claws needs network access in order "
2156 "to access the IMAP server."))) {
2162 (GSList *)imap_get_uncached_messages_thread(data);
2164 statusbar_progress_all(data->cur,data->total, 1);
2166 g_slist_free(newlist);
2168 result = g_slist_concat(result, partial_result);
2172 statusbar_progress_all(0,0,0);
2173 statusbar_pop_all();
2178 static void imap_delete_all_cached_messages(FolderItem *item)
2182 g_return_if_fail(item != NULL);
2183 g_return_if_fail(item->folder != NULL);
2184 g_return_if_fail(FOLDER_CLASS(item->folder) == &imap_class);
2186 debug_print("Deleting all cached messages...\n");
2188 dir = folder_item_get_path(item);
2189 if (is_dir_exist(dir))
2190 remove_all_numbered_files(dir);
2193 debug_print("done.\n");
2196 static IMAPNameSpace *imap_find_namespace_from_list(GList *ns_list,
2199 IMAPNameSpace *namespace = NULL;
2200 gchar *tmp_path, *name;
2202 if (!path) path = "";
2204 for (; ns_list != NULL; ns_list = ns_list->next) {
2205 IMAPNameSpace *tmp_ns = ns_list->data;
2207 Xstrcat_a(tmp_path, path, "/", return namespace);
2208 Xstrdup_a(name, tmp_ns->name, return namespace);
2209 if (tmp_ns->separator && tmp_ns->separator != '/') {
2210 subst_char(tmp_path, tmp_ns->separator, '/');
2211 subst_char(name, tmp_ns->separator, '/');
2213 if (strncmp(tmp_path, name, strlen(name)) == 0)
2220 static IMAPNameSpace *imap_find_namespace(IMAPFolder *folder,
2223 IMAPNameSpace *namespace;
2225 g_return_val_if_fail(folder != NULL, NULL);
2227 namespace = imap_find_namespace_from_list(folder->ns_personal, path);
2228 if (namespace) return namespace;
2229 namespace = imap_find_namespace_from_list(folder->ns_others, path);
2230 if (namespace) return namespace;
2231 namespace = imap_find_namespace_from_list(folder->ns_shared, path);
2232 if (namespace) return namespace;
2238 static gchar imap_get_path_separator(IMAPFolder *folder, const gchar *path)
2240 IMAPNameSpace *namespace;
2241 gchar separator = '/';
2243 if (folder->last_seen_separator == 0) {
2245 int r = imap_threaded_list((Folder *)folder, "", "", &lep_list);
2246 if (r != MAILIMAP_NO_ERROR) {
2247 log_warning(_("LIST failed\n"));
2251 if (clist_count(lep_list) > 0) {
2252 clistiter * iter = clist_begin(lep_list);
2253 struct mailimap_mailbox_list * mb;
2254 mb = clist_content(iter);
2256 folder->last_seen_separator = mb->mb_delimiter;
2257 debug_print("got separator: %c\n", folder->last_seen_separator);
2259 mailimap_list_result_free(lep_list);
2262 if (folder->last_seen_separator != 0) {
2263 debug_print("using separator: %c\n", folder->last_seen_separator);
2264 return folder->last_seen_separator;
2267 namespace = imap_find_namespace(folder, path);
2268 if (namespace && namespace->separator)
2269 separator = namespace->separator;
2274 static gchar *imap_get_real_path(IMAPFolder *folder, const gchar *path)
2279 g_return_val_if_fail(folder != NULL, NULL);
2280 g_return_val_if_fail(path != NULL, NULL);
2282 real_path = imap_utf8_to_modified_utf7(path);
2283 separator = imap_get_path_separator(folder, path);
2284 imap_path_separator_subst(real_path, separator);
2289 static gint imap_set_message_flags(IMAPSession *session,
2290 MsgNumberList *numlist,
2298 seq_list = imap_get_lep_set_from_numlist(numlist);
2300 for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) {
2301 struct mailimap_set * imapset;
2303 imapset = cur->data;
2305 ok = imap_cmd_store(session, imapset,
2309 imap_lep_set_free(seq_list);
2311 return IMAP_SUCCESS;
2314 typedef struct _select_data {
2315 IMAPSession *session;
2320 guint32 *uid_validity;
2324 static gint imap_select(IMAPSession *session, IMAPFolder *folder,
2326 gint *exists, gint *recent, gint *unseen,
2327 guint32 *uid_validity, gboolean block)
2331 gint exists_, recent_, unseen_;
2332 guint32 uid_validity_;
2334 if (!exists && !recent && !unseen && !uid_validity) {
2335 if (session->mbox && strcmp(session->mbox, path) == 0)
2336 return IMAP_SUCCESS;
2345 uid_validity = &uid_validity_;
2347 g_free(session->mbox);
2348 session->mbox = NULL;
2350 real_path = imap_get_real_path(folder, path);
2352 ok = imap_cmd_select(session, real_path,
2353 exists, recent, unseen, uid_validity, block);
2354 if (ok != IMAP_SUCCESS)
2355 log_warning(_("can't select folder: %s\n"), real_path);
2357 session->mbox = g_strdup(path);
2358 session->folder_content_changed = FALSE;
2365 static gint imap_status(IMAPSession *session, IMAPFolder *folder,
2366 const gchar *path, IMAPFolderItem *item,
2368 guint32 *uid_next, guint32 *uid_validity,
2369 gint *unseen, gboolean block)
2373 struct mailimap_mailbox_data_status * data_status;
2378 real_path = imap_get_real_path(folder, path);
2392 r = imap_threaded_status(FOLDER(folder), real_path,
2393 &data_status, mask);
2396 if (r != MAILIMAP_NO_ERROR) {
2397 debug_print("status err %d\n", r);
2401 if (data_status->st_info_list == NULL) {
2402 mailimap_mailbox_data_status_free(data_status);
2403 debug_print("status->st_info_list == NULL\n");
2408 for(iter = clist_begin(data_status->st_info_list) ; iter != NULL ;
2409 iter = clist_next(iter)) {
2410 struct mailimap_status_info * info;
2412 info = clist_content(iter);
2413 switch (info->st_att) {
2414 case MAILIMAP_STATUS_ATT_MESSAGES:
2415 * messages = info->st_value;
2416 got_values |= 1 << 0;
2419 case MAILIMAP_STATUS_ATT_UIDNEXT:
2420 * uid_next = info->st_value;
2421 got_values |= 1 << 2;
2424 case MAILIMAP_STATUS_ATT_UIDVALIDITY:
2425 * uid_validity = info->st_value;
2426 got_values |= 1 << 3;
2429 case MAILIMAP_STATUS_ATT_UNSEEN:
2430 * unseen = info->st_value;
2431 got_values |= 1 << 4;
2435 mailimap_mailbox_data_status_free(data_status);
2437 if (got_values != mask) {
2438 debug_print("status: incomplete values received (%d)\n", got_values);
2441 return IMAP_SUCCESS;
2444 static void imap_free_capabilities(IMAPSession *session)
2446 slist_free_strings(session->capability);
2447 g_slist_free(session->capability);
2448 session->capability = NULL;
2451 /* low-level IMAP4rev1 commands */
2453 static gint imap_cmd_login(IMAPSession *session,
2454 const gchar *user, const gchar *pass,
2460 log_print("IMAP4> Logging %s to %s using %s\n",
2462 SESSION(session)->server,
2464 r = imap_threaded_login(session->folder, user, pass, type);
2465 if (r != MAILIMAP_NO_ERROR) {
2466 log_error("IMAP4< Error logging in to %s\n",
2467 SESSION(session)->server);
2475 static gint imap_cmd_logout(IMAPSession *session)
2477 imap_threaded_disconnect(session->folder);
2479 return IMAP_SUCCESS;
2482 static gint imap_cmd_noop(IMAPSession *session)
2485 unsigned int exists;
2487 r = imap_threaded_noop(session->folder, &exists);
2488 if (r != MAILIMAP_NO_ERROR) {
2489 debug_print("noop err %d\n", r);
2492 session->exists = exists;
2493 session_set_access_time(SESSION(session));
2495 return IMAP_SUCCESS;
2499 static gint imap_cmd_starttls(IMAPSession *session)
2503 r = imap_threaded_starttls(session->folder,
2504 SESSION(session)->server, SESSION(session)->port);
2505 if (r != MAILIMAP_NO_ERROR) {
2506 debug_print("starttls err %d\n", r);
2509 return IMAP_SUCCESS;
2513 static gint imap_cmd_select(IMAPSession *session, const gchar *folder,
2514 gint *exists, gint *recent, gint *unseen,
2515 guint32 *uid_validity, gboolean block)
2519 r = imap_threaded_select(session->folder, folder,
2520 exists, recent, unseen, uid_validity);
2521 if (r != MAILIMAP_NO_ERROR) {
2522 debug_print("select err %d\n", r);
2525 return IMAP_SUCCESS;
2528 static gint imap_cmd_examine(IMAPSession *session, const gchar *folder,
2529 gint *exists, gint *recent, gint *unseen,
2530 guint32 *uid_validity, gboolean block)
2534 r = imap_threaded_examine(session->folder, folder,
2535 exists, recent, unseen, uid_validity);
2536 if (r != MAILIMAP_NO_ERROR) {
2537 debug_print("examine err %d\n", r);
2541 return IMAP_SUCCESS;
2544 static gint imap_cmd_create(IMAPSession *session, const gchar *folder)
2548 r = imap_threaded_create(session->folder, folder);
2549 if (r != MAILIMAP_NO_ERROR) {
2554 return IMAP_SUCCESS;
2557 static gint imap_cmd_rename(IMAPSession *session, const gchar *old_folder,
2558 const gchar *new_folder)
2562 r = imap_threaded_rename(session->folder, old_folder,
2564 if (r != MAILIMAP_NO_ERROR) {
2569 return IMAP_SUCCESS;
2572 static gint imap_cmd_delete(IMAPSession *session, const gchar *folder)
2577 r = imap_threaded_delete(session->folder, folder);
2578 if (r != MAILIMAP_NO_ERROR) {
2583 return IMAP_SUCCESS;
2586 typedef struct _fetch_data {
2587 IMAPSession *session;
2589 const gchar *filename;
2595 static void *imap_cmd_fetch_thread(void *data)
2597 fetch_data *stuff = (fetch_data *)data;
2598 IMAPSession *session = stuff->session;
2599 guint32 uid = stuff->uid;
2600 const gchar *filename = stuff->filename;
2604 r = imap_threaded_fetch_content(session->folder,
2608 r = imap_threaded_fetch_content(session->folder,
2611 if (r != MAILIMAP_NO_ERROR) {
2612 debug_print("fetch err %d\n", r);
2613 return GINT_TO_POINTER(IMAP_ERROR);
2615 return GINT_TO_POINTER(IMAP_SUCCESS);
2618 static gint imap_cmd_fetch(IMAPSession *session, guint32 uid,
2619 const gchar *filename, gboolean headers,
2622 fetch_data *data = g_new0(fetch_data, 1);
2625 data->session = session;
2627 data->filename = filename;
2628 data->headers = headers;
2631 if (prefs_common.work_offline &&
2632 !inc_offline_should_override(
2633 _("Sylpheed-Claws needs network access in order "
2634 "to access the IMAP server."))) {
2638 statusbar_print_all(_("Fetching message..."));
2639 result = GPOINTER_TO_INT(imap_cmd_fetch_thread(data));
2640 statusbar_pop_all();
2646 static gint imap_cmd_append(IMAPSession *session, const gchar *destfolder,
2647 const gchar *file, IMAPFlags flags,
2650 struct mailimap_flag_list * flag_list;
2653 g_return_val_if_fail(file != NULL, IMAP_ERROR);
2655 flag_list = imap_flag_to_lep(flags);
2656 r = imap_threaded_append(session->folder, destfolder,
2657 file, flag_list, new_uid);
2658 mailimap_flag_list_free(flag_list);
2660 if (r != MAILIMAP_NO_ERROR) {
2661 debug_print("append err %d\n", r);
2664 return IMAP_SUCCESS;
2667 static gint imap_cmd_copy(IMAPSession *session, struct mailimap_set * set,
2668 const gchar *destfolder, GRelation *uid_mapping)
2672 g_return_val_if_fail(session != NULL, IMAP_ERROR);
2673 g_return_val_if_fail(set != NULL, IMAP_ERROR);
2674 g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
2676 r = imap_threaded_copy(session->folder, set, destfolder);
2677 if (r != MAILIMAP_NO_ERROR) {
2682 return IMAP_SUCCESS;
2685 static gint imap_cmd_store(IMAPSession *session, struct mailimap_set * set,
2686 IMAPFlags flags, int do_add)
2689 struct mailimap_flag_list * flag_list;
2690 struct mailimap_store_att_flags * store_att_flags;
2692 flag_list = imap_flag_to_lep(flags);
2696 mailimap_store_att_flags_new_add_flags_silent(flag_list);
2699 mailimap_store_att_flags_new_remove_flags_silent(flag_list);
2701 r = imap_threaded_store(session->folder, set, store_att_flags);
2702 mailimap_store_att_flags_free(store_att_flags);
2703 if (r != MAILIMAP_NO_ERROR) {
2708 return IMAP_SUCCESS;
2711 static gint imap_cmd_expunge(IMAPSession *session)
2715 if (prefs_common.work_offline &&
2716 !inc_offline_should_override(
2717 _("Sylpheed-Claws needs network access in order "
2718 "to access the IMAP server."))) {
2722 r = imap_threaded_expunge(session->folder);
2723 if (r != MAILIMAP_NO_ERROR) {
2728 return IMAP_SUCCESS;
2731 static void imap_path_separator_subst(gchar *str, gchar separator)
2734 gboolean in_escape = FALSE;
2736 if (!separator || separator == '/') return;
2738 for (p = str; *p != '\0'; p++) {
2739 if (*p == '/' && !in_escape)
2741 else if (*p == '&' && *(p + 1) != '-' && !in_escape)
2743 else if (*p == '-' && in_escape)
2748 static gchar *imap_modified_utf7_to_utf8(const gchar *mutf7_str)
2750 static iconv_t cd = (iconv_t)-1;
2751 static gboolean iconv_ok = TRUE;
2754 size_t norm_utf7_len;
2756 gchar *to_str, *to_p;
2758 gboolean in_escape = FALSE;
2760 if (!iconv_ok) return g_strdup(mutf7_str);
2762 if (cd == (iconv_t)-1) {
2763 cd = iconv_open(CS_INTERNAL, CS_UTF_7);
2764 if (cd == (iconv_t)-1) {
2765 g_warning("iconv cannot convert UTF-7 to %s\n",
2768 return g_strdup(mutf7_str);
2772 /* modified UTF-7 to normal UTF-7 conversion */
2773 norm_utf7 = g_string_new(NULL);
2775 for (p = mutf7_str; *p != '\0'; p++) {
2776 /* replace: '&' -> '+',
2778 escaped ',' -> '/' */
2779 if (!in_escape && *p == '&') {
2780 if (*(p + 1) != '-') {
2781 g_string_append_c(norm_utf7, '+');
2784 g_string_append_c(norm_utf7, '&');
2787 } else if (in_escape && *p == ',') {
2788 g_string_append_c(norm_utf7, '/');
2789 } else if (in_escape && *p == '-') {
2790 g_string_append_c(norm_utf7, '-');
2793 g_string_append_c(norm_utf7, *p);
2797 norm_utf7_p = norm_utf7->str;
2798 norm_utf7_len = norm_utf7->len;
2799 to_len = strlen(mutf7_str) * 5;
2800 to_p = to_str = g_malloc(to_len + 1);
2802 if (iconv(cd, (ICONV_CONST gchar **)&norm_utf7_p, &norm_utf7_len,
2803 &to_p, &to_len) == -1) {
2804 g_warning(_("iconv cannot convert UTF-7 to %s\n"),
2805 conv_get_locale_charset_str());
2806 g_string_free(norm_utf7, TRUE);
2808 return g_strdup(mutf7_str);
2811 /* second iconv() call for flushing */
2812 iconv(cd, NULL, NULL, &to_p, &to_len);
2813 g_string_free(norm_utf7, TRUE);
2819 static gchar *imap_utf8_to_modified_utf7(const gchar *from)
2821 static iconv_t cd = (iconv_t)-1;
2822 static gboolean iconv_ok = TRUE;
2823 gchar *norm_utf7, *norm_utf7_p;
2824 size_t from_len, norm_utf7_len;
2826 gchar *from_tmp, *to, *p;
2827 gboolean in_escape = FALSE;
2829 if (!iconv_ok) return g_strdup(from);
2831 if (cd == (iconv_t)-1) {
2832 cd = iconv_open(CS_UTF_7, CS_INTERNAL);
2833 if (cd == (iconv_t)-1) {
2834 g_warning(_("iconv cannot convert %s to UTF-7\n"),
2837 return g_strdup(from);
2841 /* UTF-8 to normal UTF-7 conversion */
2842 Xstrdup_a(from_tmp, from, return g_strdup(from));
2843 from_len = strlen(from);
2844 norm_utf7_len = from_len * 5;
2845 Xalloca(norm_utf7, norm_utf7_len + 1, return g_strdup(from));
2846 norm_utf7_p = norm_utf7;
2848 #define IS_PRINT(ch) (isprint(ch) && IS_ASCII(ch))
2850 while (from_len > 0) {
2851 if (*from_tmp == '+') {
2852 *norm_utf7_p++ = '+';
2853 *norm_utf7_p++ = '-';
2857 } else if (IS_PRINT(*(guchar *)from_tmp)) {
2858 /* printable ascii char */
2859 *norm_utf7_p = *from_tmp;
2865 size_t conv_len = 0;
2867 /* unprintable char: convert to UTF-7 */
2869 while (!IS_PRINT(*(guchar *)p) && conv_len < from_len) {
2870 conv_len += g_utf8_skip[*(guchar *)p];
2871 p += g_utf8_skip[*(guchar *)p];
2874 from_len -= conv_len;
2875 if (iconv(cd, (ICONV_CONST gchar **)&from_tmp,
2877 &norm_utf7_p, &norm_utf7_len) == -1) {
2878 g_warning(_("iconv cannot convert UTF-8 to UTF-7\n"));
2879 return g_strdup(from);
2882 /* second iconv() call for flushing */
2883 iconv(cd, NULL, NULL, &norm_utf7_p, &norm_utf7_len);
2889 *norm_utf7_p = '\0';
2890 to_str = g_string_new(NULL);
2891 for (p = norm_utf7; p < norm_utf7_p; p++) {
2892 /* replace: '&' -> "&-",
2895 BASE64 '/' -> ',' */
2896 if (!in_escape && *p == '&') {
2897 g_string_append(to_str, "&-");
2898 } else if (!in_escape && *p == '+') {
2899 if (*(p + 1) == '-') {
2900 g_string_append_c(to_str, '+');
2903 g_string_append_c(to_str, '&');
2906 } else if (in_escape && *p == '/') {
2907 g_string_append_c(to_str, ',');
2908 } else if (in_escape && *p == '-') {
2909 g_string_append_c(to_str, '-');
2912 g_string_append_c(to_str, *p);
2918 g_string_append_c(to_str, '-');
2922 g_string_free(to_str, FALSE);
2927 static gboolean imap_rename_folder_func(GNode *node, gpointer data)
2929 FolderItem *item = node->data;
2930 gchar **paths = data;
2931 const gchar *oldpath = paths[0];
2932 const gchar *newpath = paths[1];
2934 gchar *new_itempath;
2937 oldpathlen = strlen(oldpath);
2938 if (strncmp(oldpath, item->path, oldpathlen) != 0) {
2939 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
2943 base = item->path + oldpathlen;
2944 while (*base == G_DIR_SEPARATOR) base++;
2946 new_itempath = g_strdup(newpath);
2948 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
2951 item->path = new_itempath;
2956 typedef struct _get_list_uid_data {
2958 IMAPSession *session;
2959 IMAPFolderItem *item;
2960 GSList **msgnum_list;
2962 } get_list_uid_data;
2964 static void *get_list_of_uids_thread(void *data)
2966 get_list_uid_data *stuff = (get_list_uid_data *)data;
2967 Folder *folder = stuff->folder;
2968 IMAPFolderItem *item = stuff->item;
2969 GSList **msgnum_list = stuff->msgnum_list;
2970 gint ok, nummsgs = 0, lastuid_old;
2971 IMAPSession *session;
2972 GSList *uidlist, *elem;
2973 struct mailimap_set * set;
2974 clist * lep_uidlist;
2977 session = stuff->session;
2978 if (session == NULL) {
2980 return GINT_TO_POINTER(-1);
2982 /* no session locking here, it's already locked by caller */
2983 ok = imap_select(session, IMAP_FOLDER(folder), item->item.path,
2984 NULL, NULL, NULL, NULL, TRUE);
2985 if (ok != IMAP_SUCCESS) {
2987 return GINT_TO_POINTER(-1);
2992 set = mailimap_set_new_interval(item->lastuid + 1, 0);
2994 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SIMPLE, set,
2996 mailimap_set_free(set);
2998 if (r == MAILIMAP_NO_ERROR) {
2999 GSList * fetchuid_list;
3002 imap_uid_list_from_lep(lep_uidlist);
3003 mailimap_search_result_free(lep_uidlist);
3005 uidlist = g_slist_concat(fetchuid_list, uidlist);
3008 GSList * fetchuid_list;
3009 carray * lep_uidtab;
3011 r = imap_threaded_fetch_uid(folder, item->lastuid + 1,
3013 if (r == MAILIMAP_NO_ERROR) {
3015 imap_uid_list_from_lep_tab(lep_uidtab);
3016 imap_fetch_uid_list_free(lep_uidtab);
3017 uidlist = g_slist_concat(fetchuid_list, uidlist);
3021 lastuid_old = item->lastuid;
3022 *msgnum_list = g_slist_copy(item->uid_list);
3023 nummsgs = g_slist_length(*msgnum_list);
3024 debug_print("Got %d uids from cache\n", g_slist_length(item->uid_list));
3026 for (elem = uidlist; elem != NULL; elem = g_slist_next(elem)) {
3029 msgnum = GPOINTER_TO_INT(elem->data);
3030 if (msgnum > lastuid_old) {
3031 *msgnum_list = g_slist_prepend(*msgnum_list, GINT_TO_POINTER(msgnum));
3032 item->uid_list = g_slist_prepend(item->uid_list, GINT_TO_POINTER(msgnum));
3035 if(msgnum > item->lastuid)
3036 item->lastuid = msgnum;
3039 g_slist_free(uidlist);
3041 return GINT_TO_POINTER(nummsgs);
3044 static gint get_list_of_uids(IMAPSession *session, Folder *folder, IMAPFolderItem *item, GSList **msgnum_list)
3047 get_list_uid_data *data = g_new0(get_list_uid_data, 1);
3049 data->folder = folder;
3051 data->msgnum_list = msgnum_list;
3052 data->session = session;
3053 if (prefs_common.work_offline &&
3054 !inc_offline_should_override(
3055 _("Sylpheed-Claws needs network access in order "
3056 "to access the IMAP server."))) {
3061 result = GPOINTER_TO_INT(get_list_of_uids_thread(data));
3067 gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list, gboolean *old_uids_valid)
3069 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3070 IMAPSession *session;
3071 gint ok, nummsgs = 0, exists, uid_val, uid_next;
3072 GSList *uidlist = NULL;
3074 gboolean selected_folder;
3076 debug_print("get_num_list\n");
3078 g_return_val_if_fail(folder != NULL, -1);
3079 g_return_val_if_fail(item != NULL, -1);
3080 g_return_val_if_fail(item->item.path != NULL, -1);
3081 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3082 g_return_val_if_fail(folder->account != NULL, -1);
3084 session = imap_session_get(folder);
3085 g_return_val_if_fail(session != NULL, -1);
3087 statusbar_print_all("Scanning %s...\n", FOLDER_ITEM(item)->path
3088 ? FOLDER_ITEM(item)->path:"");
3090 selected_folder = (session->mbox != NULL) &&
3091 (!strcmp(session->mbox, item->item.path));
3092 if (selected_folder && time(NULL) - item->use_cache < 2) {
3093 ok = imap_cmd_noop(session);
3094 if (ok != IMAP_SUCCESS) {
3095 debug_print("disconnected!\n");
3096 session = imap_reconnect_if_possible(folder, session);
3097 if (session == NULL) {
3098 statusbar_pop_all();
3103 exists = session->exists;
3105 *old_uids_valid = TRUE;
3107 if (item->use_cache && time(NULL) - item->use_cache < 2) {
3108 exists = item->c_messages;
3109 uid_next = item->c_uid_next;
3110 uid_val = item->c_uid_validity;
3112 debug_print("using cache %d %d %d\n", exists, uid_next, uid_val);
3114 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path, item,
3115 &exists, &uid_next, &uid_val, NULL, FALSE);
3117 item->item.last_num = uid_next - 1;
3119 item->use_cache = (time_t)0;
3120 if (ok != IMAP_SUCCESS) {
3121 statusbar_pop_all();
3125 if(item->item.mtime == uid_val)
3126 *old_uids_valid = TRUE;
3128 *old_uids_valid = FALSE;
3130 debug_print("Freeing imap uid cache\n");
3132 g_slist_free(item->uid_list);
3133 item->uid_list = NULL;
3135 item->item.mtime = uid_val;
3137 imap_delete_all_cached_messages((FolderItem *)item);
3141 /* If old uid_next matches new uid_next we can be sure no message
3142 was added to the folder */
3143 debug_print("uid_next is %d and item->uid_next %d \n",
3144 uid_next, item->uid_next);
3145 if (uid_next == item->uid_next) {
3146 nummsgs = g_slist_length(item->uid_list);
3148 /* If number of messages is still the same we
3149 know our caches message numbers are still valid,
3150 otherwise if the number of messages has decrease
3151 we discard our cache to start a new scan to find
3152 out which numbers have been removed */
3153 if (exists == nummsgs) {
3154 debug_print("exists == nummsgs\n");
3155 *msgnum_list = g_slist_copy(item->uid_list);
3156 statusbar_pop_all();
3159 } else if (exists < nummsgs) {
3160 debug_print("Freeing imap uid cache");
3162 g_slist_free(item->uid_list);
3163 item->uid_list = NULL;
3168 *msgnum_list = NULL;
3169 statusbar_pop_all();
3174 nummsgs = get_list_of_uids(session, folder, item, &uidlist);
3177 statusbar_pop_all();
3182 if (nummsgs != exists) {
3183 /* Cache contains more messages then folder, we have cached
3184 an old UID of a message that was removed and new messages
3185 have been added too, otherwise the uid_next check would
3187 debug_print("Freeing imap uid cache");
3189 g_slist_free(item->uid_list);
3190 item->uid_list = NULL;
3192 g_slist_free(*msgnum_list);
3194 nummsgs = get_list_of_uids(session, folder, item, &uidlist);
3197 *msgnum_list = uidlist;
3199 dir = folder_item_get_path((FolderItem *)item);
3200 debug_print("removing old messages from %s\n", dir);
3201 remove_numbered_files_not_in_list(dir, *msgnum_list);
3204 item->uid_next = uid_next;
3206 debug_print("get_num_list - ok - %i\n", nummsgs);
3207 statusbar_pop_all();
3212 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item)
3217 flags.perm_flags = MSG_NEW|MSG_UNREAD;
3218 flags.tmp_flags = 0;
3220 g_return_val_if_fail(item != NULL, NULL);
3221 g_return_val_if_fail(file != NULL, NULL);
3223 if (folder_has_parent_of_type(item, F_QUEUE)) {
3224 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
3225 } else if (folder_has_parent_of_type(item, F_DRAFT)) {
3226 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
3229 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3230 if (!msginfo) return NULL;
3232 msginfo->plaintext_file = g_strdup(file);
3233 msginfo->folder = item;
3238 GSList *imap_get_msginfos(Folder *folder, FolderItem *item,
3239 GSList *msgnum_list)
3241 IMAPSession *session;
3242 MsgInfoList *ret = NULL;
3245 debug_print("get_msginfos\n");
3247 g_return_val_if_fail(folder != NULL, NULL);
3248 g_return_val_if_fail(item != NULL, NULL);
3249 g_return_val_if_fail(msgnum_list != NULL, NULL);
3251 session = imap_session_get(folder);
3252 g_return_val_if_fail(session != NULL, NULL);
3254 debug_print("IMAP getting msginfos\n");
3255 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3256 NULL, NULL, NULL, NULL, FALSE);
3257 if (ok != IMAP_SUCCESS) {
3261 if (!(folder_has_parent_of_type(item, F_DRAFT) ||
3262 folder_has_parent_of_type(item, F_QUEUE))) {
3263 ret = g_slist_concat(ret,
3264 imap_get_uncached_messages(session, item,
3267 MsgNumberList *sorted_list, *elem;
3268 gint startnum, lastnum;
3270 sorted_list = g_slist_sort(g_slist_copy(msgnum_list), g_int_compare);
3272 startnum = lastnum = GPOINTER_TO_INT(sorted_list->data);
3274 for (elem = sorted_list;; elem = g_slist_next(elem)) {
3278 num = GPOINTER_TO_INT(elem->data);
3280 if (num > lastnum + 1 || elem == NULL) {
3282 for (i = startnum; i <= lastnum; ++i) {
3285 file = imap_fetch_msg(folder, item, i);
3287 MsgInfo *msginfo = imap_parse_msg(file, item);
3288 if (msginfo != NULL) {
3289 msginfo->msgnum = i;
3290 ret = g_slist_append(ret, msginfo);
3294 session_set_access_time(SESSION(session));
3305 g_slist_free(sorted_list);
3311 MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid)
3313 MsgInfo *msginfo = NULL;
3314 MsgInfoList *msginfolist;
3315 MsgNumberList numlist;
3317 numlist.next = NULL;
3318 numlist.data = GINT_TO_POINTER(uid);
3320 msginfolist = imap_get_msginfos(folder, item, &numlist);
3321 if (msginfolist != NULL) {
3322 msginfo = msginfolist->data;
3323 g_slist_free(msginfolist);
3329 gboolean imap_scan_required(Folder *folder, FolderItem *_item)
3331 IMAPSession *session;
3332 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3333 gint ok, exists = 0, unseen = 0;
3334 guint32 uid_next, uid_val;
3335 gboolean selected_folder;
3337 g_return_val_if_fail(folder != NULL, FALSE);
3338 g_return_val_if_fail(item != NULL, FALSE);
3339 g_return_val_if_fail(item->item.folder != NULL, FALSE);
3340 g_return_val_if_fail(FOLDER_CLASS(item->item.folder) == &imap_class, FALSE);
3342 if (item->item.path == NULL)
3345 session = imap_session_get(folder);
3346 g_return_val_if_fail(session != NULL, FALSE);
3348 selected_folder = (session->mbox != NULL) &&
3349 (!strcmp(session->mbox, item->item.path));
3350 if (selected_folder && time(NULL) - item->use_cache < 2) {
3351 ok = imap_cmd_noop(session);
3352 if (ok != IMAP_SUCCESS) {
3353 debug_print("disconnected!\n");
3354 session = imap_reconnect_if_possible(folder, session);
3355 if (session == NULL)
3360 if (session->folder_content_changed
3361 || session->exists != item->item.total_msgs) {
3366 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path, IMAP_FOLDER_ITEM(item),
3367 &exists, &uid_next, &uid_val, &unseen, FALSE);
3368 if (ok != IMAP_SUCCESS) {
3373 item->use_cache = time(NULL);
3374 item->c_messages = exists;
3375 item->c_uid_next = uid_next;
3376 item->c_uid_validity = uid_val;
3377 item->c_unseen = unseen;
3378 item->item.last_num = uid_next - 1;
3379 debug_print("uidnext %d, item->uid_next %d, exists %d, item->item.total_msgs %d\n",
3380 uid_next, item->uid_next, exists, item->item.total_msgs);
3381 if ((uid_next != item->uid_next) || (exists != item->item.total_msgs)) {
3390 void imap_change_flags(Folder *folder, FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
3392 IMAPSession *session;
3393 IMAPFlags flags_set = 0, flags_unset = 0;
3394 gint ok = IMAP_SUCCESS;
3395 MsgNumberList numlist;
3396 hashtable_data *ht_data = NULL;
3398 g_return_if_fail(folder != NULL);
3399 g_return_if_fail(folder->klass == &imap_class);
3400 g_return_if_fail(item != NULL);
3401 g_return_if_fail(item->folder == folder);
3402 g_return_if_fail(msginfo != NULL);
3403 g_return_if_fail(msginfo->folder == item);
3405 session = imap_session_get(folder);
3410 if ((ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
3411 NULL, NULL, NULL, NULL, FALSE)) != IMAP_SUCCESS) {
3416 if (!MSG_IS_MARKED(msginfo->flags) && (newflags & MSG_MARKED))
3417 flags_set |= IMAP_FLAG_FLAGGED;
3418 if ( MSG_IS_MARKED(msginfo->flags) && !(newflags & MSG_MARKED))
3419 flags_unset |= IMAP_FLAG_FLAGGED;
3421 if (!MSG_IS_UNREAD(msginfo->flags) && (newflags & MSG_UNREAD))
3422 flags_unset |= IMAP_FLAG_SEEN;
3423 if ( MSG_IS_UNREAD(msginfo->flags) && !(newflags & MSG_UNREAD))
3424 flags_set |= IMAP_FLAG_SEEN;
3426 if (!MSG_IS_REPLIED(msginfo->flags) && (newflags & MSG_REPLIED))
3427 flags_set |= IMAP_FLAG_ANSWERED;
3428 if ( MSG_IS_REPLIED(msginfo->flags) && !(newflags & MSG_REPLIED))
3429 flags_unset |= IMAP_FLAG_ANSWERED;
3431 if (!MSG_IS_DELETED(msginfo->flags) && (newflags & MSG_DELETED))
3432 flags_set |= IMAP_FLAG_DELETED;
3433 if ( MSG_IS_DELETED(msginfo->flags) && !(newflags & MSG_DELETED))
3434 flags_unset |= IMAP_FLAG_DELETED;
3436 numlist.next = NULL;
3437 numlist.data = GINT_TO_POINTER(msginfo->msgnum);
3439 if (IMAP_FOLDER_ITEM(item)->batching) {
3440 /* instead of performing an UID STORE command for each message change,
3441 * as a lot of them can change "together", we just fill in hashtables
3442 * and defer the treatment so that we're able to send only one
3445 debug_print("IMAP batch mode on, deferring flags change\n");
3447 ht_data = g_hash_table_lookup(IMAP_FOLDER_ITEM(item)->flags_set_table,
3448 GINT_TO_POINTER(flags_set));
3449 if (ht_data == NULL) {
3450 ht_data = g_new0(hashtable_data, 1);
3451 ht_data->session = session;
3452 ht_data->item = IMAP_FOLDER_ITEM(item);
3453 g_hash_table_insert(IMAP_FOLDER_ITEM(item)->flags_set_table,
3454 GINT_TO_POINTER(flags_set), ht_data);
3456 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
3457 ht_data->msglist = g_slist_prepend(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum));
3460 ht_data = g_hash_table_lookup(IMAP_FOLDER_ITEM(item)->flags_unset_table,
3461 GINT_TO_POINTER(flags_unset));
3462 if (ht_data == NULL) {
3463 ht_data = g_new0(hashtable_data, 1);
3464 ht_data->session = session;
3465 ht_data->item = IMAP_FOLDER_ITEM(item);
3466 g_hash_table_insert(IMAP_FOLDER_ITEM(item)->flags_unset_table,
3467 GINT_TO_POINTER(flags_unset), ht_data);
3469 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
3470 ht_data->msglist = g_slist_prepend(ht_data->msglist,
3471 GINT_TO_POINTER(msginfo->msgnum));
3474 debug_print("IMAP changing flags\n");
3476 ok = imap_set_message_flags(session, &numlist, flags_set, TRUE);
3477 if (ok != IMAP_SUCCESS) {
3484 ok = imap_set_message_flags(session, &numlist, flags_unset, FALSE);
3485 if (ok != IMAP_SUCCESS) {
3491 msginfo->flags.perm_flags = newflags;
3496 static gint imap_remove_msg(Folder *folder, FolderItem *item, gint uid)
3499 IMAPSession *session;
3501 MsgNumberList numlist;
3503 g_return_val_if_fail(folder != NULL, -1);
3504 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3505 g_return_val_if_fail(item != NULL, -1);
3507 session = imap_session_get(folder);
3508 if (!session) return -1;
3510 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3511 NULL, NULL, NULL, NULL, FALSE);
3512 if (ok != IMAP_SUCCESS) {
3516 numlist.next = NULL;
3517 numlist.data = GINT_TO_POINTER(uid);
3519 ok = imap_set_message_flags
3520 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
3521 &numlist, IMAP_FLAG_DELETED, TRUE);
3522 if (ok != IMAP_SUCCESS) {
3523 log_warning(_("can't set deleted flags: %d\n"), uid);
3528 if (!session->uidplus) {
3529 ok = imap_cmd_expunge(session);
3533 uidstr = g_strdup_printf("%u", uid);
3534 ok = imap_cmd_expunge(session);
3537 if (ok != IMAP_SUCCESS) {
3538 log_warning(_("can't expunge\n"));
3543 IMAP_FOLDER_ITEM(item)->uid_list = g_slist_remove(
3544 IMAP_FOLDER_ITEM(item)->uid_list, numlist.data);
3545 dir = folder_item_get_path(item);
3546 if (is_dir_exist(dir))
3547 remove_numbered_files(dir, uid, uid);
3550 return IMAP_SUCCESS;
3553 static gint compare_msginfo(gconstpointer a, gconstpointer b)
3555 return ((MsgInfo *)a)->msgnum - ((MsgInfo *)b)->msgnum;
3558 static guint gslist_find_next_num(MsgNumberList **list, guint num)
3562 g_return_val_if_fail(list != NULL, -1);
3564 for (elem = *list; elem != NULL; elem = g_slist_next(elem))
3565 if (GPOINTER_TO_INT(elem->data) >= num)
3568 return elem != NULL ? GPOINTER_TO_INT(elem->data) : (gint)-1;
3572 * NEW and DELETED flags are not syncronized
3573 * - The NEW/RECENT flags in IMAP folders can not really be directly
3574 * modified by Sylpheed
3575 * - The DELETE/DELETED flag in IMAP and Sylpheed don't have the same
3576 * meaning, in IMAP it always removes the messages from the FolderItem
3577 * in Sylpheed it can mean to move the message to trash
3580 typedef struct _get_flags_data {
3583 MsgInfoList *msginfo_list;
3584 GRelation *msgflags;
3585 gboolean full_search;
3589 static /*gint*/ void *imap_get_flags_thread(void *data)
3591 get_flags_data *stuff = (get_flags_data *)data;
3592 Folder *folder = stuff->folder;
3593 FolderItem *item = stuff->item;
3594 MsgInfoList *msginfo_list = stuff->msginfo_list;
3595 GRelation *msgflags = stuff->msgflags;
3596 gboolean full_search = stuff->full_search;
3597 IMAPSession *session;
3598 GSList *sorted_list = NULL;
3599 GSList *unseen = NULL, *answered = NULL, *flagged = NULL, *deleted = NULL;
3600 GSList *p_unseen, *p_answered, *p_flagged, *p_deleted;
3602 GSList *seq_list, *cur;
3603 gboolean reverse_seen = FALSE;
3606 gint exists_cnt, unseen_cnt;
3607 gboolean selected_folder;
3609 if (folder == NULL || item == NULL) {
3611 return GINT_TO_POINTER(-1);
3614 session = imap_session_get(folder);
3615 if (session == NULL) {
3617 return GINT_TO_POINTER(-1);
3620 selected_folder = (session->mbox != NULL) &&
3621 (!strcmp(session->mbox, item->path));
3623 if (!selected_folder) {
3624 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3625 &exists_cnt, NULL, &unseen_cnt, NULL, TRUE);
3626 if (ok != IMAP_SUCCESS) {
3629 return GINT_TO_POINTER(-1);
3632 if (unseen_cnt > exists_cnt / 2)
3633 reverse_seen = TRUE;
3636 if (item->unread_msgs > item->total_msgs / 2)
3637 reverse_seen = TRUE;
3640 cmd_buf = g_string_new(NULL);
3642 sorted_list = g_slist_sort(g_slist_copy(msginfo_list), compare_msginfo);
3644 seq_list = imap_get_lep_set_from_msglist(msginfo_list);
3646 struct mailimap_set * set;
3647 set = mailimap_set_new_interval(1, 0);
3648 seq_list = g_slist_append(NULL, set);
3651 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
3652 struct mailimap_set * imapset;
3653 clist * lep_uidlist;
3656 imapset = cur->data;
3658 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SEEN,
3659 imapset, &lep_uidlist);
3662 r = imap_threaded_search(folder,
3663 IMAP_SEARCH_TYPE_UNSEEN,
3664 imapset, &lep_uidlist);
3666 if (r == MAILIMAP_NO_ERROR) {
3669 uidlist = imap_uid_list_from_lep(lep_uidlist);
3670 mailimap_search_result_free(lep_uidlist);
3672 unseen = g_slist_concat(unseen, uidlist);
3675 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_ANSWERED,
3676 imapset, &lep_uidlist);
3677 if (r == MAILIMAP_NO_ERROR) {
3680 uidlist = imap_uid_list_from_lep(lep_uidlist);
3681 mailimap_search_result_free(lep_uidlist);
3683 answered = g_slist_concat(answered, uidlist);
3686 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_FLAGGED,
3687 imapset, &lep_uidlist);
3688 if (r == MAILIMAP_NO_ERROR) {
3691 uidlist = imap_uid_list_from_lep(lep_uidlist);
3692 mailimap_search_result_free(lep_uidlist);
3694 flagged = g_slist_concat(flagged, uidlist);
3697 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_DELETED,
3698 imapset, &lep_uidlist);
3699 if (r == MAILIMAP_NO_ERROR) {
3702 uidlist = imap_uid_list_from_lep(lep_uidlist);
3703 mailimap_search_result_free(lep_uidlist);
3705 deleted = g_slist_concat(deleted, uidlist);
3710 p_answered = answered;
3711 p_flagged = flagged;
3712 p_deleted = deleted;
3714 for (elem = sorted_list; elem != NULL; elem = g_slist_next(elem)) {
3719 msginfo = (MsgInfo *) elem->data;
3720 flags = msginfo->flags.perm_flags;
3721 wasnew = (flags & MSG_NEW);
3722 flags &= ~((reverse_seen ? 0 : MSG_UNREAD | MSG_NEW) | MSG_REPLIED | MSG_MARKED);
3724 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
3725 if (gslist_find_next_num(&p_unseen, msginfo->msgnum) == msginfo->msgnum) {
3726 if (!reverse_seen) {
3727 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
3729 flags &= ~(MSG_UNREAD | MSG_NEW);
3732 if (gslist_find_next_num(&p_answered, msginfo->msgnum) == msginfo->msgnum)
3733 flags |= MSG_REPLIED;
3735 flags &= ~MSG_REPLIED;
3736 if (gslist_find_next_num(&p_flagged, msginfo->msgnum) == msginfo->msgnum)
3737 flags |= MSG_MARKED;
3739 flags &= ~MSG_MARKED;
3740 if (gslist_find_next_num(&p_deleted, msginfo->msgnum) == msginfo->msgnum)
3741 flags |= MSG_DELETED;
3743 flags &= ~MSG_DELETED;
3744 g_relation_insert(msgflags, msginfo, GINT_TO_POINTER(flags));
3747 imap_lep_set_free(seq_list);
3748 g_slist_free(flagged);
3749 g_slist_free(deleted);
3750 g_slist_free(answered);
3751 g_slist_free(unseen);
3752 g_slist_free(sorted_list);
3753 g_string_free(cmd_buf, TRUE);
3757 return GINT_TO_POINTER(0);
3760 static gint imap_get_flags(Folder *folder, FolderItem *item,
3761 MsgInfoList *msginfo_list, GRelation *msgflags)
3764 get_flags_data *data = g_new0(get_flags_data, 1);
3766 data->folder = folder;
3768 data->msginfo_list = msginfo_list;
3769 data->msgflags = msgflags;
3770 data->full_search = FALSE;
3772 GSList *tmp = NULL, *cur;
3774 if (prefs_common.work_offline &&
3775 !inc_offline_should_override(
3776 _("Sylpheed-Claws needs network access in order "
3777 "to access the IMAP server."))) {
3782 tmp = folder_item_get_msg_list(item);
3784 if (g_slist_length(tmp) == g_slist_length(msginfo_list))
3785 data->full_search = TRUE;
3787 for (cur = tmp; cur; cur = cur->next)
3788 procmsg_msginfo_free((MsgInfo *)cur->data);
3792 result = GPOINTER_TO_INT(imap_get_flags_thread(data));
3799 static gboolean process_flags(gpointer key, gpointer value, gpointer user_data)
3801 gboolean flags_set = GPOINTER_TO_INT(user_data);
3802 gint flags_value = GPOINTER_TO_INT(key);
3803 hashtable_data *data = (hashtable_data *)value;
3804 IMAPFolderItem *_item = data->item;
3805 FolderItem *item = (FolderItem *)_item;
3806 gint ok = IMAP_ERROR;
3807 IMAPSession *session = imap_session_get(item->folder);
3809 data->msglist = g_slist_reverse(data->msglist);
3811 debug_print("IMAP %ssetting flags to %d for %d messages\n",
3814 g_slist_length(data->msglist));
3818 ok = imap_select(session, IMAP_FOLDER(item->folder), item->path,
3819 NULL, NULL, NULL, NULL, FALSE);
3821 if (ok == IMAP_SUCCESS) {
3822 imap_set_message_flags(data->session, data->msglist, flags_value, flags_set);
3824 g_warning("can't select mailbox %s\n", item->path);
3828 g_slist_free(data->msglist);
3833 static void process_hashtable(IMAPFolderItem *item)
3835 if (item->flags_set_table) {
3836 g_hash_table_foreach_remove(item->flags_set_table, process_flags, GINT_TO_POINTER(TRUE));
3837 g_hash_table_destroy(item->flags_set_table);
3838 item->flags_set_table = NULL;
3840 if (item->flags_unset_table) {
3841 g_hash_table_foreach_remove(item->flags_unset_table, process_flags, GINT_TO_POINTER(FALSE));
3842 g_hash_table_destroy(item->flags_unset_table);
3843 item->flags_unset_table = NULL;
3847 static void imap_set_batch (Folder *folder, FolderItem *_item, gboolean batch)
3849 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3851 g_return_if_fail(item != NULL);
3853 if (item->batching == batch)
3857 item->batching = TRUE;
3858 debug_print("IMAP switching to batch mode\n");
3859 if (!item->flags_set_table) {
3860 item->flags_set_table = g_hash_table_new(NULL, g_direct_equal);
3862 if (!item->flags_unset_table) {
3863 item->flags_unset_table = g_hash_table_new(NULL, g_direct_equal);
3866 debug_print("IMAP switching away from batch mode\n");
3868 process_hashtable(item);
3869 item->batching = FALSE;
3875 /* data types conversion libetpan <-> sylpheed */
3879 #define ETPAN_IMAP_MB_MARKED 1
3880 #define ETPAN_IMAP_MB_UNMARKED 2
3881 #define ETPAN_IMAP_MB_NOSELECT 4
3882 #define ETPAN_IMAP_MB_NOINFERIORS 8
3884 static int imap_flags_to_flags(struct mailimap_mbx_list_flags * imap_flags)
3890 if (imap_flags->mbf_type == MAILIMAP_MBX_LIST_FLAGS_SFLAG) {
3891 switch (imap_flags->mbf_sflag) {
3892 case MAILIMAP_MBX_LIST_SFLAG_MARKED:
3893 flags |= ETPAN_IMAP_MB_MARKED;
3895 case MAILIMAP_MBX_LIST_SFLAG_NOSELECT:
3896 flags |= ETPAN_IMAP_MB_NOSELECT;
3898 case MAILIMAP_MBX_LIST_SFLAG_UNMARKED:
3899 flags |= ETPAN_IMAP_MB_UNMARKED;
3904 for(cur = clist_begin(imap_flags->mbf_oflags) ; cur != NULL ;
3905 cur = clist_next(cur)) {
3906 struct mailimap_mbx_list_oflag * oflag;
3908 oflag = clist_content(cur);
3910 switch (oflag->of_type) {
3911 case MAILIMAP_MBX_LIST_OFLAG_NOINFERIORS:
3912 flags |= ETPAN_IMAP_MB_NOINFERIORS;
3920 static GSList * imap_list_from_lep(IMAPFolder * folder,
3921 clist * list, const gchar * real_path, gboolean all)
3928 for(iter = clist_begin(list) ; iter != NULL ;
3929 iter = clist_next(iter)) {
3930 struct mailimap_mailbox_list * mb;
3938 FolderItem *new_item;
3940 mb = clist_content(iter);
3946 if (mb->mb_flag != NULL)
3947 flags = imap_flags_to_flags(mb->mb_flag);
3949 delimiter = mb->mb_delimiter;
3952 dup_name = strdup(name);
3953 if (delimiter != '\0')
3954 subst_char(dup_name, delimiter, '/');
3956 base = g_path_get_basename(dup_name);
3957 if (base[0] == '.') {
3963 if (!all && strcmp(dup_name, real_path) == 0) {
3969 if (!all && dup_name[strlen(dup_name)-1] == '/') {
3975 loc_name = imap_modified_utf7_to_utf8(base);
3976 loc_path = imap_modified_utf7_to_utf8(dup_name);
3978 new_item = folder_item_new(FOLDER(folder), loc_name, loc_path);
3979 if ((flags & ETPAN_IMAP_MB_NOINFERIORS) != 0)
3980 new_item->no_sub = TRUE;
3981 if (strcmp(dup_name, "INBOX") != 0 &&
3982 ((flags & ETPAN_IMAP_MB_NOSELECT) != 0))
3983 new_item->no_select = TRUE;
3985 item_list = g_slist_append(item_list, new_item);
3987 debug_print("folder '%s' found.\n", loc_path);
3998 static GSList * imap_get_lep_set_from_numlist(MsgNumberList *numlist)
4000 GSList *sorted_list, *cur;
4001 guint first, last, next;
4002 GSList *ret_list = NULL;
4004 struct mailimap_set * current_set;
4005 unsigned int item_count;
4007 if (numlist == NULL)
4011 current_set = mailimap_set_new_empty();
4013 sorted_list = g_slist_copy(numlist);
4014 sorted_list = g_slist_sort(sorted_list, g_int_compare);
4016 first = GPOINTER_TO_INT(sorted_list->data);
4019 for (cur = sorted_list; cur != NULL; cur = g_slist_next(cur)) {
4020 if (GPOINTER_TO_INT(cur->data) == 0)
4025 last = GPOINTER_TO_INT(cur->data);
4027 next = GPOINTER_TO_INT(cur->next->data);
4031 if (last + 1 != next || next == 0) {
4033 struct mailimap_set_item * item;
4034 item = mailimap_set_item_new(first, last);
4035 mailimap_set_add(current_set, item);
4040 if (count >= IMAP_SET_MAX_COUNT) {
4041 ret_list = g_slist_append(ret_list,
4043 current_set = mailimap_set_new_empty();
4050 if (clist_count(current_set->set_list) > 0) {
4051 ret_list = g_slist_append(ret_list,
4055 g_slist_free(sorted_list);
4060 static GSList * imap_get_lep_set_from_msglist(MsgInfoList *msglist)
4062 MsgNumberList *numlist = NULL;
4066 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
4067 MsgInfo *msginfo = (MsgInfo *) cur->data;
4069 numlist = g_slist_append(numlist, GINT_TO_POINTER(msginfo->msgnum));
4071 seq_list = imap_get_lep_set_from_numlist(numlist);
4072 g_slist_free(numlist);
4077 static GSList * imap_uid_list_from_lep(clist * list)
4084 for(iter = clist_begin(list) ; iter != NULL ;
4085 iter = clist_next(iter)) {
4088 puid = clist_content(iter);
4089 result = g_slist_append(result, GINT_TO_POINTER(* puid));
4095 static GSList * imap_uid_list_from_lep_tab(carray * list)
4102 for(i = 0 ; i < carray_count(list) ; i ++) {
4105 puid = carray_get(list, i);
4106 result = g_slist_append(result, GINT_TO_POINTER(* puid));
4112 static MsgInfo *imap_envelope_from_lep(struct imap_fetch_env_info * info,
4115 MsgInfo *msginfo = NULL;
4118 MsgFlags flags = {0, 0};
4120 if (info->headers == NULL)
4123 MSG_SET_TMP_FLAGS(flags, MSG_IMAP);
4124 if (folder_has_parent_of_type(item, F_QUEUE)) {
4125 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
4126 } else if (folder_has_parent_of_type(item, F_DRAFT)) {
4127 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
4129 flags.perm_flags = info->flags;
4133 msginfo = procheader_parse_str(info->headers, flags, FALSE, FALSE);
4136 msginfo->msgnum = uid;
4137 msginfo->size = size;
4143 static void imap_lep_set_free(GSList *seq_list)
4147 for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) {
4148 struct mailimap_set * imapset;
4150 imapset = cur->data;
4151 mailimap_set_free(imapset);
4153 g_slist_free(seq_list);
4156 static struct mailimap_flag_list * imap_flag_to_lep(IMAPFlags flags)
4158 struct mailimap_flag_list * flag_list;
4160 flag_list = mailimap_flag_list_new_empty();
4162 if (IMAP_IS_SEEN(flags))
4163 mailimap_flag_list_add(flag_list,
4164 mailimap_flag_new_seen());
4165 if (IMAP_IS_ANSWERED(flags))
4166 mailimap_flag_list_add(flag_list,
4167 mailimap_flag_new_answered());
4168 if (IMAP_IS_FLAGGED(flags))
4169 mailimap_flag_list_add(flag_list,
4170 mailimap_flag_new_flagged());
4171 if (IMAP_IS_DELETED(flags))
4172 mailimap_flag_list_add(flag_list,
4173 mailimap_flag_new_deleted());
4174 if (IMAP_IS_DRAFT(flags))
4175 mailimap_flag_list_add(flag_list,
4176 mailimap_flag_new_draft());
4181 guint imap_folder_get_refcnt(Folder *folder)
4183 return ((IMAPFolder *)folder)->refcnt;
4186 void imap_folder_ref(Folder *folder)
4188 ((IMAPFolder *)folder)->refcnt++;
4191 void imap_folder_unref(Folder *folder)
4193 if (((IMAPFolder *)folder)->refcnt > 0)
4194 ((IMAPFolder *)folder)->refcnt--;
4197 #else /* HAVE_LIBETPAN */
4199 static FolderClass imap_class;
4201 static Folder *imap_folder_new (const gchar *name,
4206 static gint imap_create_tree (Folder *folder)
4210 static FolderItem *imap_create_folder (Folder *folder,
4216 static gint imap_rename_folder (Folder *folder,
4223 FolderClass *imap_get_class(void)
4225 if (imap_class.idstr == NULL) {
4226 imap_class.type = F_IMAP;
4227 imap_class.idstr = "imap";
4228 imap_class.uistr = "IMAP4";
4230 imap_class.new_folder = imap_folder_new;
4231 imap_class.create_tree = imap_create_tree;
4232 imap_class.create_folder = imap_create_folder;
4233 imap_class.rename_folder = imap_rename_folder;
4234 /* nothing implemented */
4241 void imap_synchronise(FolderItem *item)
4243 imap_gtk_synchronise(item);