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;
680 g_return_val_if_fail(folder != NULL, NULL);
681 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, NULL);
682 g_return_val_if_fail(folder->account != NULL, NULL);
684 if (prefs_common.work_offline &&
685 !inc_offline_should_override(
686 _("Sylpheed-Claws needs network access in order "
687 "to access the IMAP server."))) {
691 /* Make sure we have a session */
692 if (rfolder->session != NULL) {
693 session = IMAP_SESSION(rfolder->session);
694 /* don't do that yet...
699 imap_reset_uid_lists(folder);
700 session = imap_session_new(folder, folder->account);
705 /* Make sure session is authenticated */
706 if (!IMAP_SESSION(session)->authenticated)
707 imap_session_authenticate(IMAP_SESSION(session), folder->account);
709 if (!IMAP_SESSION(session)->authenticated) {
710 session_destroy(SESSION(session));
711 rfolder->session = NULL;
715 /* I think the point of this code is to avoid sending a
716 * keepalive if we've used the session recently and therefore
717 * think it's still alive. Unfortunately, most of the code
718 * does not yet check for errors on the socket, and so if the
719 * connection drops we don't notice until the timeout expires.
720 * A better solution than sending a NOOP every time would be
721 * for every command to be prepared to retry until it is
722 * successfully sent. -- mbp */
723 if (time(NULL) - SESSION(session)->last_access_time > SESSION_TIMEOUT_INTERVAL) {
724 /* verify that the session is still alive */
725 if (imap_cmd_noop(session) != IMAP_SUCCESS) {
726 debug_print("disconnected!\n");
727 session = imap_reconnect_if_possible(folder, session);
731 rfolder->session = SESSION(session);
733 return IMAP_SESSION(session);
736 static IMAPSession *imap_session_new(Folder * folder,
737 const PrefsAccount *account)
739 IMAPSession *session;
745 /* FIXME: IMAP over SSL only... */
748 port = account->set_imapport ? account->imapport
749 : account->ssl_imap == SSL_TUNNEL ? IMAPS_PORT : IMAP4_PORT;
750 ssl_type = account->ssl_imap;
752 if (account->ssl_imap != SSL_NONE) {
753 if (alertpanel_full(_("Insecure connection"),
754 _("This connection is configured to be secured "
755 "using SSL, but SSL is not available in this "
756 "build of Sylpheed-Claws. \n\n"
757 "Do you want to continue connecting to this "
758 "server? The communication would not be "
760 _("Con_tinue connecting"),
761 GTK_STOCK_CANCEL, NULL,
762 FALSE, NULL, ALERT_WARNING,
763 G_ALERTALTERNATE) != G_ALERTDEFAULT)
766 port = account->set_imapport ? account->imapport
771 statusbar_print_all(_("Connecting to IMAP4 server: %s..."), folder->account->recv_server);
772 if (account->set_tunnelcmd) {
773 r = imap_threaded_connect_cmd(folder,
775 account->recv_server,
780 if (ssl_type == SSL_TUNNEL) {
781 r = imap_threaded_connect_ssl(folder,
782 account->recv_server,
788 r = imap_threaded_connect(folder,
789 account->recv_server,
795 if (r == MAILIMAP_NO_ERROR_AUTHENTICATED) {
796 authenticated = TRUE;
798 else if (r == MAILIMAP_NO_ERROR_NON_AUTHENTICATED) {
799 authenticated = FALSE;
802 if(!prefs_common.no_recv_err_panel) {
803 alertpanel_error(_("Can't connect to IMAP4 server: %s:%d"),
804 account->recv_server, port);
806 log_error(_("Can't connect to IMAP4 server: %s:%d\n"),
807 account->recv_server, port);
813 session = g_new0(IMAPSession, 1);
814 session_init(SESSION(session));
815 SESSION(session)->type = SESSION_IMAP;
816 SESSION(session)->server = g_strdup(account->recv_server);
817 SESSION(session)->sock = NULL;
819 SESSION(session)->destroy = imap_session_destroy;
821 session->capability = NULL;
823 session->authenticated = authenticated;
824 session->mbox = NULL;
825 session->cmd_count = 0;
826 session->folder = folder;
827 IMAP_FOLDER(session->folder)->last_seen_separator = 0;
830 if (account->ssl_imap == SSL_STARTTLS) {
833 ok = imap_cmd_starttls(session);
834 if (ok != IMAP_SUCCESS) {
835 log_warning(_("Can't start TLS session.\n"));
836 session_destroy(SESSION(session));
840 imap_free_capabilities(session);
841 session->authenticated = FALSE;
842 session->uidplus = FALSE;
843 session->cmd_count = 1;
846 log_message("IMAP connection is %s-authenticated\n",
847 (session->authenticated) ? "pre" : "un");
852 static void imap_session_authenticate(IMAPSession *session,
853 const PrefsAccount *account)
857 g_return_if_fail(account->userid != NULL);
859 pass = account->passwd;
862 tmp_pass = input_dialog_query_password(account->recv_server, account->userid);
864 tmp_pass = g_strdup(""); /* allow empty password */
865 Xstrdup_a(pass, tmp_pass, {g_free(tmp_pass); return;});
868 statusbar_print_all(_("Connecting to IMAP4 server %s...\n"),
869 account->recv_server);
870 if (imap_auth(session, account->userid, pass, account->imap_auth_type) != IMAP_SUCCESS) {
871 imap_threaded_disconnect(session->folder);
872 imap_cmd_logout(session);
878 session->authenticated = TRUE;
881 static void imap_session_destroy(Session *session)
883 if (session->state != SESSION_DISCONNECTED)
884 imap_threaded_disconnect(IMAP_SESSION(session)->folder);
886 imap_free_capabilities(IMAP_SESSION(session));
887 g_free(IMAP_SESSION(session)->mbox);
888 sock_close(session->sock);
889 session->sock = NULL;
892 static gchar *imap_fetch_msg(Folder *folder, FolderItem *item, gint uid)
894 return imap_fetch_msg_full(folder, item, uid, TRUE, TRUE);
897 static guint get_size_with_crs(MsgInfo *info)
906 fp = procmsg_open_message(info);
910 while (fgets(buf, sizeof (buf), fp) != NULL) {
912 if (!strstr(buf, "\r") && strstr(buf, "\n"))
920 static gchar *imap_fetch_msg_full(Folder *folder, FolderItem *item, gint uid,
921 gboolean headers, gboolean body)
923 gchar *path, *filename;
924 IMAPSession *session;
927 g_return_val_if_fail(folder != NULL, NULL);
928 g_return_val_if_fail(item != NULL, NULL);
933 path = folder_item_get_path(item);
934 if (!is_dir_exist(path))
936 filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(uid), NULL);
938 debug_print("trying to fetch cached %s\n", filename);
939 if (is_file_exist(filename)) {
940 /* see whether the local file represents the whole message
941 * or not. As the IMAP server reports size with \r chars,
942 * we have to update the local file (UNIX \n only) size */
943 MsgInfo *msginfo = imap_parse_msg(filename, item);
944 MsgInfo *cached = msgcache_get_msg(item->cache,uid);
945 guint have_size = get_size_with_crs(msginfo);
948 debug_print("message %d has been already %scached (%d/%d).\n", uid,
949 have_size == cached->size ? "fully ":"",
950 have_size, (int)cached->size);
952 if (cached && (cached->size == have_size || !body)) {
953 procmsg_msginfo_free(cached);
954 procmsg_msginfo_free(msginfo);
955 file_strip_crs(filename);
957 } else if (!cached) {
958 debug_print("message not cached, considering file complete\n");
959 procmsg_msginfo_free(msginfo);
960 file_strip_crs(filename);
963 procmsg_msginfo_free(cached);
964 procmsg_msginfo_free(msginfo);
968 session = imap_session_get(folder);
977 debug_print("IMAP fetching messages\n");
978 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
979 NULL, NULL, NULL, NULL, FALSE);
980 if (ok != IMAP_SUCCESS) {
981 g_warning("can't select mailbox %s\n", item->path);
987 debug_print("getting message %d...\n", uid);
988 ok = imap_cmd_fetch(session, (guint32)uid, filename, headers, body);
990 if (ok != IMAP_SUCCESS) {
991 g_warning("can't fetch message %d\n", uid);
998 file_strip_crs(filename);
1002 static gint imap_add_msg(Folder *folder, FolderItem *dest,
1003 const gchar *file, MsgFlags *flags)
1007 MsgFileInfo fileinfo;
1009 g_return_val_if_fail(file != NULL, -1);
1011 fileinfo.msginfo = NULL;
1012 fileinfo.file = (gchar *)file;
1013 fileinfo.flags = flags;
1014 file_list.data = &fileinfo;
1015 file_list.next = NULL;
1017 ret = imap_add_msgs(folder, dest, &file_list, NULL);
1021 static gint imap_add_msgs(Folder *folder, FolderItem *dest, GSList *file_list,
1022 GRelation *relation)
1025 IMAPSession *session;
1026 guint32 last_uid = 0;
1028 MsgFileInfo *fileinfo;
1030 gint curnum = 0, total = 0;
1033 g_return_val_if_fail(folder != NULL, -1);
1034 g_return_val_if_fail(dest != NULL, -1);
1035 g_return_val_if_fail(file_list != NULL, -1);
1037 session = imap_session_get(folder);
1042 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
1044 statusbar_print_all(_("Adding messages..."));
1045 total = g_slist_length(file_list);
1046 for (cur = file_list; cur != NULL; cur = cur->next) {
1047 IMAPFlags iflags = 0;
1048 guint32 new_uid = 0;
1049 gchar *real_file = NULL;
1050 gboolean file_is_tmp = FALSE;
1051 fileinfo = (MsgFileInfo *)cur->data;
1053 statusbar_progress_all(curnum, total, 1);
1056 if (fileinfo->flags) {
1057 if (MSG_IS_MARKED(*fileinfo->flags))
1058 iflags |= IMAP_FLAG_FLAGGED;
1059 if (MSG_IS_REPLIED(*fileinfo->flags))
1060 iflags |= IMAP_FLAG_ANSWERED;
1061 if (!MSG_IS_UNREAD(*fileinfo->flags))
1062 iflags |= IMAP_FLAG_SEEN;
1065 if (fileinfo->flags) {
1066 if ((MSG_IS_QUEUED(*fileinfo->flags)
1067 || MSG_IS_DRAFT(*fileinfo->flags))
1068 && !folder_has_parent_of_type(dest, F_QUEUE)
1069 && !folder_has_parent_of_type(dest, F_DRAFT)) {
1070 real_file = get_tmp_file();
1072 if (procmsg_remove_special_headers(
1080 } else if (!(MSG_IS_QUEUED(*fileinfo->flags)
1081 || MSG_IS_DRAFT(*fileinfo->flags))
1082 && (folder_has_parent_of_type(dest, F_QUEUE)
1083 || folder_has_parent_of_type(dest, F_DRAFT))) {
1087 if (real_file == NULL)
1088 real_file = g_strdup(fileinfo->file);
1090 if (folder_has_parent_of_type(dest, F_QUEUE) ||
1091 folder_has_parent_of_type(dest, F_OUTBOX) ||
1092 folder_has_parent_of_type(dest, F_DRAFT) ||
1093 folder_has_parent_of_type(dest, F_TRASH))
1094 iflags |= IMAP_FLAG_SEEN;
1096 ok = imap_cmd_append(session, destdir, real_file, iflags,
1099 if (ok != IMAP_SUCCESS) {
1100 g_warning("can't append message %s\n", real_file);
1102 g_unlink(real_file);
1106 statusbar_progress_all(0,0,0);
1107 statusbar_pop_all();
1110 debug_print("appended new message as %d\n", new_uid);
1111 /* put the local file in the imapcache, so that we don't
1112 * have to fetch it back later. */
1114 gchar *cache_path = folder_item_get_path(dest);
1115 if (!is_dir_exist(cache_path))
1116 make_dir_hier(cache_path);
1117 if (is_dir_exist(cache_path)) {
1118 gchar *cache_file = g_strconcat(
1119 cache_path, G_DIR_SEPARATOR_S,
1120 itos(new_uid), NULL);
1121 copy_file(real_file, cache_file, TRUE);
1122 debug_print("copied to cache: %s\n", cache_file);
1129 if (relation != NULL)
1130 g_relation_insert(relation, fileinfo->msginfo != NULL ?
1131 (gpointer) fileinfo->msginfo : (gpointer) fileinfo,
1132 GINT_TO_POINTER(dest->last_num + 1));
1134 new_uid = dest->last_num+1;
1136 if (last_uid < new_uid)
1139 g_unlink(real_file);
1143 statusbar_progress_all(0,0,0);
1144 statusbar_pop_all();
1146 imap_cmd_expunge(session);
1154 static gint imap_do_copy_msgs(Folder *folder, FolderItem *dest,
1155 MsgInfoList *msglist, GRelation *relation)
1159 GSList *seq_list, *cur;
1161 IMAPSession *session;
1162 gint ok = IMAP_SUCCESS;
1163 GRelation *uid_mapping;
1166 g_return_val_if_fail(folder != NULL, -1);
1167 g_return_val_if_fail(dest != NULL, -1);
1168 g_return_val_if_fail(msglist != NULL, -1);
1170 session = imap_session_get(folder);
1176 msginfo = (MsgInfo *)msglist->data;
1178 src = msginfo->folder;
1180 g_warning("the src folder is identical to the dest.\n");
1185 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
1186 NULL, NULL, NULL, NULL, FALSE);
1187 if (ok != IMAP_SUCCESS) {
1192 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
1193 seq_list = imap_get_lep_set_from_msglist(msglist);
1194 uid_mapping = g_relation_new(2);
1195 g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
1197 statusbar_print_all(_("Copying messages..."));
1198 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
1199 struct mailimap_set * seq_set;
1200 seq_set = cur->data;
1202 debug_print("Copying messages from %s to %s ...\n",
1203 src->path, destdir);
1205 ok = imap_cmd_copy(session, seq_set, destdir, uid_mapping);
1206 if (ok != IMAP_SUCCESS) {
1207 g_relation_destroy(uid_mapping);
1208 imap_lep_set_free(seq_list);
1214 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
1215 MsgInfo *msginfo = (MsgInfo *)cur->data;
1218 tuples = g_relation_select(uid_mapping,
1219 GINT_TO_POINTER(msginfo->msgnum),
1221 if (tuples->len > 0) {
1222 gint num = GPOINTER_TO_INT(g_tuples_index(tuples, 0, 1));
1223 g_relation_insert(relation, msginfo,
1224 GPOINTER_TO_INT(num));
1228 g_relation_insert(relation, msginfo,
1229 GPOINTER_TO_INT(0));
1230 g_tuples_destroy(tuples);
1232 statusbar_pop_all();
1234 g_relation_destroy(uid_mapping);
1235 imap_lep_set_free(seq_list);
1239 IMAP_FOLDER_ITEM(dest)->lastuid = 0;
1240 IMAP_FOLDER_ITEM(dest)->uid_next = 0;
1241 g_slist_free(IMAP_FOLDER_ITEM(dest)->uid_list);
1242 IMAP_FOLDER_ITEM(dest)->uid_list = NULL;
1245 if (ok == IMAP_SUCCESS)
1251 static gint imap_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1255 g_return_val_if_fail(msginfo != NULL, -1);
1257 msglist.data = msginfo;
1258 msglist.next = NULL;
1260 return imap_copy_msgs(folder, dest, &msglist, NULL);
1263 static gint imap_copy_msgs(Folder *folder, FolderItem *dest,
1264 MsgInfoList *msglist, GRelation *relation)
1270 g_return_val_if_fail(folder != NULL, -1);
1271 g_return_val_if_fail(dest != NULL, -1);
1272 g_return_val_if_fail(msglist != NULL, -1);
1274 msginfo = (MsgInfo *)msglist->data;
1275 g_return_val_if_fail(msginfo->folder != NULL, -1);
1277 /* if from/to are the same "type" (with or without extra headers),
1278 * copy them via imap */
1279 if (folder == msginfo->folder->folder &&
1280 !folder_has_parent_of_type(msginfo->folder, F_DRAFT) &&
1281 !folder_has_parent_of_type(msginfo->folder, F_QUEUE) &&
1282 !folder_has_parent_of_type(dest, F_DRAFT) &&
1283 !folder_has_parent_of_type(dest, F_QUEUE)) {
1284 ret = imap_do_copy_msgs(folder, dest, msglist, relation);
1286 } else if (folder == msginfo->folder->folder &&
1287 (folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
1288 folder_has_parent_of_type(msginfo->folder, F_QUEUE)) &&
1289 (folder_has_parent_of_type(dest, F_DRAFT) ||
1290 folder_has_parent_of_type(dest, F_QUEUE))) {
1291 ret = imap_do_copy_msgs(folder, dest, msglist, relation);
1294 /* else reupload them */
1295 file_list = procmsg_get_message_file_list(msglist);
1296 g_return_val_if_fail(file_list != NULL, -1);
1298 ret = imap_add_msgs(folder, dest, file_list, relation);
1299 procmsg_message_file_list_free(file_list);
1305 static gint imap_do_remove_msgs(Folder *folder, FolderItem *dest,
1306 MsgInfoList *msglist, GRelation *relation)
1309 GSList *numlist = NULL, *cur;
1311 IMAPSession *session;
1312 gint ok = IMAP_SUCCESS;
1313 GRelation *uid_mapping;
1315 g_return_val_if_fail(folder != NULL, -1);
1316 g_return_val_if_fail(dest != NULL, -1);
1317 g_return_val_if_fail(msglist != NULL, -1);
1319 session = imap_session_get(folder);
1324 msginfo = (MsgInfo *)msglist->data;
1326 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
1327 NULL, NULL, NULL, NULL, FALSE);
1328 if (ok != IMAP_SUCCESS) {
1333 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
1334 for (cur = msglist; cur; cur = cur->next) {
1335 msginfo = (MsgInfo *)cur->data;
1336 if (!MSG_IS_DELETED(msginfo->flags))
1337 numlist = g_slist_append(numlist, GINT_TO_POINTER(msginfo->msgnum));
1340 uid_mapping = g_relation_new(2);
1341 g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
1343 ok = imap_set_message_flags
1344 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
1345 numlist, IMAP_FLAG_DELETED, TRUE);
1346 if (ok != IMAP_SUCCESS) {
1347 log_warning(_("can't set deleted flags\n"));
1351 ok = imap_cmd_expunge(session);
1352 if (ok != IMAP_SUCCESS) {
1353 log_warning(_("can't expunge\n"));
1358 g_relation_destroy(uid_mapping);
1359 g_slist_free(numlist);
1363 if (ok == IMAP_SUCCESS)
1369 static gint imap_remove_msgs(Folder *folder, FolderItem *dest,
1370 MsgInfoList *msglist, GRelation *relation)
1374 g_return_val_if_fail(folder != NULL, -1);
1375 g_return_val_if_fail(dest != NULL, -1);
1376 if (msglist == NULL)
1379 msginfo = (MsgInfo *)msglist->data;
1380 g_return_val_if_fail(msginfo->folder != NULL, -1);
1382 return imap_do_remove_msgs(folder, dest, msglist, relation);
1385 static gint imap_remove_all_msg(Folder *folder, FolderItem *item)
1387 GSList *list = folder_item_get_msg_list(item);
1388 gint res = imap_remove_msgs(folder, item, list, NULL);
1389 procmsg_msg_list_free(list);
1393 static gboolean imap_is_msg_changed(Folder *folder, FolderItem *item,
1396 /* TODO: properly implement this method */
1400 static gint imap_close(Folder *folder, FolderItem *item)
1405 static gint imap_scan_tree(Folder *folder)
1407 FolderItem *item = NULL;
1408 IMAPSession *session;
1409 gchar *root_folder = NULL;
1411 g_return_val_if_fail(folder != NULL, -1);
1412 g_return_val_if_fail(folder->account != NULL, -1);
1414 session = imap_session_get(folder);
1416 if (!folder->node) {
1417 folder_tree_destroy(folder);
1418 item = folder_item_new(folder, folder->name, NULL);
1419 item->folder = folder;
1420 folder->node = item->node = g_node_new(item);
1426 if (folder->account->imap_dir && *folder->account->imap_dir) {
1431 Xstrdup_a(root_folder, folder->account->imap_dir, {unlock_session();return -1;});
1432 extract_quote(root_folder, '"');
1433 subst_char(root_folder,
1434 imap_get_path_separator(IMAP_FOLDER(folder),
1437 strtailchomp(root_folder, '/');
1438 real_path = imap_get_real_path
1439 (IMAP_FOLDER(folder), root_folder);
1440 debug_print("IMAP root directory: %s\n", real_path);
1442 /* check if root directory exist */
1444 r = imap_threaded_list(session->folder, "", real_path,
1446 if ((r != MAILIMAP_NO_ERROR) || (clist_count(lep_list) == 0)) {
1447 if (!folder->node) {
1448 item = folder_item_new(folder, folder->name, NULL);
1449 item->folder = folder;
1450 folder->node = item->node = g_node_new(item);
1455 mailimap_list_result_free(lep_list);
1461 item = FOLDER_ITEM(folder->node->data);
1462 if (!item || ((item->path || root_folder) &&
1463 strcmp2(item->path, root_folder) != 0)) {
1464 folder_tree_destroy(folder);
1465 item = folder_item_new(folder, folder->name, root_folder);
1466 item->folder = folder;
1467 folder->node = item->node = g_node_new(item);
1470 imap_scan_tree_recursive(session, FOLDER_ITEM(folder->node->data));
1471 imap_create_missing_folders(folder);
1477 static gint imap_scan_tree_recursive(IMAPSession *session, FolderItem *item)
1480 IMAPFolder *imapfolder;
1481 FolderItem *new_item;
1482 GSList *item_list, *cur;
1485 gchar *wildcard_path;
1491 g_return_val_if_fail(item != NULL, -1);
1492 g_return_val_if_fail(item->folder != NULL, -1);
1493 g_return_val_if_fail(item->no_sub == FALSE, -1);
1495 folder = item->folder;
1496 imapfolder = IMAP_FOLDER(folder);
1498 separator = imap_get_path_separator(imapfolder, item->path);
1500 if (folder->ui_func)
1501 folder->ui_func(folder, item, folder->ui_func_data);
1504 wildcard[0] = separator;
1507 real_path = imap_get_real_path(imapfolder, item->path);
1511 real_path = g_strdup("");
1514 Xstrcat_a(wildcard_path, real_path, wildcard,
1515 {g_free(real_path); return IMAP_ERROR;});
1517 r = imap_threaded_list(folder, "", wildcard_path, &lep_list);
1518 if (r != MAILIMAP_NO_ERROR) {
1522 item_list = imap_list_from_lep(imapfolder,
1523 lep_list, real_path, FALSE);
1524 mailimap_list_result_free(lep_list);
1529 node = item->node->children;
1530 while (node != NULL) {
1531 FolderItem *old_item = FOLDER_ITEM(node->data);
1532 GNode *next = node->next;
1535 for (cur = item_list; cur != NULL; cur = cur->next) {
1536 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1537 if (!strcmp2(old_item->path, cur_item->path)) {
1538 new_item = cur_item;
1543 debug_print("folder '%s' not found. removing...\n",
1545 folder_item_remove(old_item);
1547 old_item->no_sub = new_item->no_sub;
1548 old_item->no_select = new_item->no_select;
1549 if (old_item->no_sub == TRUE && node->children) {
1550 debug_print("folder '%s' doesn't have "
1551 "subfolders. removing...\n",
1553 folder_item_remove_children(old_item);
1560 for (cur = item_list; cur != NULL; cur = cur->next) {
1561 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1564 for (node = item->node->children; node != NULL;
1565 node = node->next) {
1566 if (!strcmp2(FOLDER_ITEM(node->data)->path,
1568 new_item = FOLDER_ITEM(node->data);
1569 folder_item_destroy(cur_item);
1575 new_item = cur_item;
1576 debug_print("new folder '%s' found.\n", new_item->path);
1577 folder_item_append(item, new_item);
1580 if (!strcmp(new_item->path, "INBOX")) {
1581 new_item->stype = F_INBOX;
1582 folder->inbox = new_item;
1583 } else if (!folder_item_parent(item) || item->stype == F_INBOX) {
1586 base = g_path_get_basename(new_item->path);
1588 if (!folder->outbox && !g_ascii_strcasecmp(base, "Sent")) {
1589 new_item->stype = F_OUTBOX;
1590 folder->outbox = new_item;
1591 } else if (!folder->draft && !g_ascii_strcasecmp(base, "Drafts")) {
1592 new_item->stype = F_DRAFT;
1593 folder->draft = new_item;
1594 } else if (!folder->queue && !g_ascii_strcasecmp(base, "Queue")) {
1595 new_item->stype = F_QUEUE;
1596 folder->queue = new_item;
1597 } else if (!folder->trash && !g_ascii_strcasecmp(base, "Trash")) {
1598 new_item->stype = F_TRASH;
1599 folder->trash = new_item;
1604 if (new_item->no_sub == FALSE)
1605 imap_scan_tree_recursive(session, new_item);
1608 g_slist_free(item_list);
1610 return IMAP_SUCCESS;
1613 static gint imap_create_tree(Folder *folder)
1615 g_return_val_if_fail(folder != NULL, -1);
1616 g_return_val_if_fail(folder->node != NULL, -1);
1617 g_return_val_if_fail(folder->node->data != NULL, -1);
1618 g_return_val_if_fail(folder->account != NULL, -1);
1620 imap_scan_tree(folder);
1621 imap_create_missing_folders(folder);
1626 static void imap_create_missing_folders(Folder *folder)
1628 g_return_if_fail(folder != NULL);
1631 folder->inbox = imap_create_special_folder
1632 (folder, F_INBOX, "INBOX");
1634 folder->trash = imap_create_special_folder
1635 (folder, F_TRASH, "Trash");
1637 folder->queue = imap_create_special_folder
1638 (folder, F_QUEUE, "Queue");
1639 if (!folder->outbox)
1640 folder->outbox = imap_create_special_folder
1641 (folder, F_OUTBOX, "Sent");
1643 folder->draft = imap_create_special_folder
1644 (folder, F_DRAFT, "Drafts");
1647 static FolderItem *imap_create_special_folder(Folder *folder,
1648 SpecialFolderItemType stype,
1652 FolderItem *new_item;
1654 g_return_val_if_fail(folder != NULL, NULL);
1655 g_return_val_if_fail(folder->node != NULL, NULL);
1656 g_return_val_if_fail(folder->node->data != NULL, NULL);
1657 g_return_val_if_fail(folder->account != NULL, NULL);
1658 g_return_val_if_fail(name != NULL, NULL);
1660 item = FOLDER_ITEM(folder->node->data);
1661 new_item = imap_create_folder(folder, item, name);
1664 g_warning("Can't create '%s'\n", name);
1665 if (!folder->inbox) return NULL;
1667 new_item = imap_create_folder(folder, folder->inbox, name);
1669 g_warning("Can't create '%s' under INBOX\n", name);
1671 new_item->stype = stype;
1673 new_item->stype = stype;
1678 static gchar *imap_folder_get_path(Folder *folder)
1682 g_return_val_if_fail(folder != NULL, NULL);
1683 g_return_val_if_fail(folder->account != NULL, NULL);
1685 folder_path = g_strconcat(get_imap_cache_dir(),
1687 folder->account->recv_server,
1689 folder->account->userid,
1695 static gchar *imap_item_get_path(Folder *folder, FolderItem *item)
1697 gchar *folder_path, *path;
1699 g_return_val_if_fail(folder != NULL, NULL);
1700 g_return_val_if_fail(item != NULL, NULL);
1701 folder_path = imap_folder_get_path(folder);
1703 g_return_val_if_fail(folder_path != NULL, NULL);
1704 if (folder_path[0] == G_DIR_SEPARATOR) {
1706 path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
1709 path = g_strdup(folder_path);
1712 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1713 folder_path, G_DIR_SEPARATOR_S,
1716 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1719 g_free(folder_path);
1724 static FolderItem *imap_create_folder(Folder *folder, FolderItem *parent,
1727 gchar *dirpath, *imap_path;
1728 IMAPSession *session;
1729 FolderItem *new_item;
1734 gboolean no_select = FALSE, no_sub = FALSE;
1736 g_return_val_if_fail(folder != NULL, NULL);
1737 g_return_val_if_fail(folder->account != NULL, NULL);
1738 g_return_val_if_fail(parent != NULL, NULL);
1739 g_return_val_if_fail(name != NULL, NULL);
1741 session = imap_session_get(folder);
1747 if (!folder_item_parent(parent) && strcmp(name, "INBOX") == 0) {
1748 dirpath = g_strdup(name);
1749 }else if (parent->path)
1750 dirpath = g_strconcat(parent->path, "/", name, NULL);
1751 else if ((p = strchr(name, '/')) != NULL && *(p + 1) != '\0')
1752 dirpath = g_strdup(name);
1753 else if (folder->account->imap_dir && *folder->account->imap_dir) {
1756 Xstrdup_a(imap_dir, folder->account->imap_dir, {unlock_session();return NULL;});
1757 strtailchomp(imap_dir, '/');
1758 dirpath = g_strconcat(imap_dir, "/", name, NULL);
1760 dirpath = g_strdup(name);
1764 /* keep trailing directory separator to create a folder that contains
1766 imap_path = imap_utf8_to_modified_utf7(dirpath);
1768 strtailchomp(dirpath, '/');
1769 Xstrdup_a(new_name, name, {
1774 separator = imap_get_path_separator(IMAP_FOLDER(folder), imap_path);
1775 imap_path_separator_subst(imap_path, separator);
1776 /* remove trailing / for display */
1777 strtailchomp(new_name, '/');
1779 if (strcmp(dirpath, "INBOX") != 0) {
1781 gboolean exist = FALSE;
1785 argbuf = g_ptr_array_new();
1786 r = imap_threaded_list(folder, "", imap_path, &lep_list);
1787 if (r != MAILIMAP_NO_ERROR) {
1788 log_warning(_("can't create mailbox: LIST failed\n"));
1791 ptr_array_free_strings(argbuf);
1792 g_ptr_array_free(argbuf, TRUE);
1797 if (clist_count(lep_list) > 0)
1799 mailimap_list_result_free(lep_list);
1802 ok = imap_cmd_create(session, imap_path);
1803 if (ok != IMAP_SUCCESS) {
1804 log_warning(_("can't create mailbox\n"));
1810 r = imap_threaded_list(folder, "", imap_path, &lep_list);
1811 if (r == MAILIMAP_NO_ERROR) {
1812 GSList *item_list = imap_list_from_lep(IMAP_FOLDER(folder),
1813 lep_list, dirpath, TRUE);
1815 FolderItem *cur_item = FOLDER_ITEM(item_list->data);
1816 no_select = cur_item->no_select;
1817 no_sub = cur_item->no_sub;
1818 g_slist_free(item_list);
1820 mailimap_list_result_free(lep_list);
1827 /* just get flags */
1828 r = imap_threaded_list(folder, "", "INBOX", &lep_list);
1829 if (r == MAILIMAP_NO_ERROR) {
1830 GSList *item_list = imap_list_from_lep(IMAP_FOLDER(folder),
1831 lep_list, dirpath, TRUE);
1833 FolderItem *cur_item = FOLDER_ITEM(item_list->data);
1834 no_select = cur_item->no_select;
1835 no_sub = cur_item->no_sub;
1836 g_slist_free(item_list);
1838 mailimap_list_result_free(lep_list);
1842 new_item = folder_item_new(folder, new_name, dirpath);
1843 new_item->no_select = no_select;
1844 new_item->no_sub = no_sub;
1845 folder_item_append(parent, new_item);
1849 dirpath = folder_item_get_path(new_item);
1850 if (!is_dir_exist(dirpath))
1851 make_dir_hier(dirpath);
1857 static gint imap_rename_folder(Folder *folder, FolderItem *item,
1862 gchar *real_oldpath;
1863 gchar *real_newpath;
1865 gchar *old_cache_dir;
1866 gchar *new_cache_dir;
1867 IMAPSession *session;
1870 gint exists, recent, unseen;
1871 guint32 uid_validity;
1873 g_return_val_if_fail(folder != NULL, -1);
1874 g_return_val_if_fail(item != NULL, -1);
1875 g_return_val_if_fail(item->path != NULL, -1);
1876 g_return_val_if_fail(name != NULL, -1);
1878 session = imap_session_get(folder);
1884 if (strchr(name, imap_get_path_separator(IMAP_FOLDER(folder), item->path)) != NULL) {
1885 g_warning(_("New folder name must not contain the namespace "
1891 real_oldpath = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1893 g_free(session->mbox);
1894 session->mbox = NULL;
1895 ok = imap_cmd_examine(session, "INBOX",
1896 &exists, &recent, &unseen, &uid_validity, FALSE);
1897 if (ok != IMAP_SUCCESS) {
1898 g_free(real_oldpath);
1903 separator = imap_get_path_separator(IMAP_FOLDER(folder), item->path);
1904 if (strchr(item->path, G_DIR_SEPARATOR)) {
1905 dirpath = g_path_get_dirname(item->path);
1906 newpath = g_strconcat(dirpath, G_DIR_SEPARATOR_S, name, NULL);
1909 newpath = g_strdup(name);
1911 real_newpath = imap_utf8_to_modified_utf7(newpath);
1912 imap_path_separator_subst(real_newpath, separator);
1914 ok = imap_cmd_rename(session, real_oldpath, real_newpath);
1915 if (ok != IMAP_SUCCESS) {
1916 log_warning(_("can't rename mailbox: %s to %s\n"),
1917 real_oldpath, real_newpath);
1918 g_free(real_oldpath);
1920 g_free(real_newpath);
1926 item->name = g_strdup(name);
1928 old_cache_dir = folder_item_get_path(item);
1930 paths[0] = g_strdup(item->path);
1932 g_node_traverse(item->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1933 imap_rename_folder_func, paths);
1935 if (is_dir_exist(old_cache_dir)) {
1936 new_cache_dir = folder_item_get_path(item);
1937 if (rename(old_cache_dir, new_cache_dir) < 0) {
1938 FILE_OP_ERROR(old_cache_dir, "rename");
1940 g_free(new_cache_dir);
1943 g_free(old_cache_dir);
1946 g_free(real_oldpath);
1947 g_free(real_newpath);
1952 static gint imap_remove_folder_real(Folder *folder, FolderItem *item)
1955 IMAPSession *session;
1959 g_return_val_if_fail(folder != NULL, -1);
1960 g_return_val_if_fail(item != NULL, -1);
1961 g_return_val_if_fail(item->path != NULL, -1);
1963 session = imap_session_get(folder);
1968 path = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1970 ok = imap_cmd_delete(session, path);
1971 if (ok != IMAP_SUCCESS) {
1972 gchar *tmp = g_strdup_printf("%s%c", path,
1973 imap_get_path_separator(IMAP_FOLDER(folder), path));
1976 ok = imap_cmd_delete(session, path);
1979 if (ok != IMAP_SUCCESS) {
1980 log_warning(_("can't delete mailbox\n"));
1987 cache_dir = folder_item_get_path(item);
1988 if (is_dir_exist(cache_dir) && remove_dir_recursive(cache_dir) < 0)
1989 g_warning("can't remove directory '%s'\n", cache_dir);
1991 folder_item_remove(item);
1996 static gint imap_remove_folder(Folder *folder, FolderItem *item)
2000 g_return_val_if_fail(item != NULL, -1);
2001 g_return_val_if_fail(item->folder != NULL, -1);
2002 g_return_val_if_fail(item->node != NULL, -1);
2004 node = item->node->children;
2005 while (node != NULL) {
2007 if (imap_remove_folder(folder, FOLDER_ITEM(node->data)) < 0)
2011 debug_print("IMAP removing %s\n", item->path);
2013 if (imap_remove_all_msg(folder, item) < 0)
2015 return imap_remove_folder_real(folder, item);
2018 typedef struct _uncached_data {
2019 IMAPSession *session;
2021 MsgNumberList *numlist;
2027 static void *imap_get_uncached_messages_thread(void *data)
2029 uncached_data *stuff = (uncached_data *)data;
2030 IMAPSession *session = stuff->session;
2031 FolderItem *item = stuff->item;
2032 MsgNumberList *numlist = stuff->numlist;
2034 GSList *newlist = NULL;
2035 GSList *llast = NULL;
2036 GSList *seq_list, *cur;
2038 debug_print("uncached_messages\n");
2040 if (session == NULL || item == NULL || item->folder == NULL
2041 || FOLDER_CLASS(item->folder) != &imap_class) {
2046 seq_list = imap_get_lep_set_from_numlist(numlist);
2047 debug_print("get msgs info\n");
2048 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
2049 struct mailimap_set * imapset;
2055 imapset = cur->data;
2057 r = imap_threaded_fetch_env(session->folder,
2058 imapset, &env_list);
2059 if (r != MAILIMAP_NO_ERROR)
2062 session_set_access_time(SESSION(session));
2065 for(i = 0 ; i < carray_count(env_list) ; i ++) {
2066 struct imap_fetch_env_info * info;
2069 info = carray_get(env_list, i);
2070 msginfo = imap_envelope_from_lep(info, item);
2071 if (msginfo == NULL)
2073 msginfo->folder = item;
2075 llast = newlist = g_slist_append(newlist, msginfo);
2077 llast = g_slist_append(llast, msginfo);
2078 llast = llast->next;
2083 imap_fetch_env_free(env_list);
2086 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
2087 struct mailimap_set * imapset;
2089 imapset = cur->data;
2090 mailimap_set_free(imapset);
2093 session_set_access_time(SESSION(session));
2098 #define MAX_MSG_NUM 50
2100 static GSList *imap_get_uncached_messages(IMAPSession *session,
2102 MsgNumberList *numlist)
2104 GSList *result = NULL;
2106 uncached_data *data = g_new0(uncached_data, 1);
2111 data->total = g_slist_length(numlist);
2112 debug_print("messages list : %i\n", data->total);
2114 while (cur != NULL) {
2115 GSList * partial_result;
2123 while (count < MAX_MSG_NUM) {
2128 if (newlist == NULL)
2129 llast = newlist = g_slist_append(newlist, p);
2131 llast = g_slist_append(llast, p);
2132 llast = llast->next;
2142 data->session = session;
2144 data->numlist = newlist;
2147 if (prefs_common.work_offline &&
2148 !inc_offline_should_override(
2149 _("Sylpheed-Claws needs network access in order "
2150 "to access the IMAP server."))) {
2156 (GSList *)imap_get_uncached_messages_thread(data);
2158 statusbar_progress_all(data->cur,data->total, 1);
2160 g_slist_free(newlist);
2162 result = g_slist_concat(result, partial_result);
2166 statusbar_progress_all(0,0,0);
2167 statusbar_pop_all();
2172 static void imap_delete_all_cached_messages(FolderItem *item)
2176 g_return_if_fail(item != NULL);
2177 g_return_if_fail(item->folder != NULL);
2178 g_return_if_fail(FOLDER_CLASS(item->folder) == &imap_class);
2180 debug_print("Deleting all cached messages...\n");
2182 dir = folder_item_get_path(item);
2183 if (is_dir_exist(dir))
2184 remove_all_numbered_files(dir);
2187 debug_print("done.\n");
2190 static IMAPNameSpace *imap_find_namespace_from_list(GList *ns_list,
2193 IMAPNameSpace *namespace = NULL;
2194 gchar *tmp_path, *name;
2196 if (!path) path = "";
2198 for (; ns_list != NULL; ns_list = ns_list->next) {
2199 IMAPNameSpace *tmp_ns = ns_list->data;
2201 Xstrcat_a(tmp_path, path, "/", return namespace);
2202 Xstrdup_a(name, tmp_ns->name, return namespace);
2203 if (tmp_ns->separator && tmp_ns->separator != '/') {
2204 subst_char(tmp_path, tmp_ns->separator, '/');
2205 subst_char(name, tmp_ns->separator, '/');
2207 if (strncmp(tmp_path, name, strlen(name)) == 0)
2214 static IMAPNameSpace *imap_find_namespace(IMAPFolder *folder,
2217 IMAPNameSpace *namespace;
2219 g_return_val_if_fail(folder != NULL, NULL);
2221 namespace = imap_find_namespace_from_list(folder->ns_personal, path);
2222 if (namespace) return namespace;
2223 namespace = imap_find_namespace_from_list(folder->ns_others, path);
2224 if (namespace) return namespace;
2225 namespace = imap_find_namespace_from_list(folder->ns_shared, path);
2226 if (namespace) return namespace;
2232 static gchar imap_get_path_separator(IMAPFolder *folder, const gchar *path)
2234 IMAPNameSpace *namespace;
2235 gchar separator = '/';
2237 if (folder->last_seen_separator == 0) {
2239 int r = imap_threaded_list((Folder *)folder, "", "", &lep_list);
2240 if (r != MAILIMAP_NO_ERROR) {
2241 log_warning(_("LIST failed\n"));
2245 if (clist_count(lep_list) > 0) {
2246 clistiter * iter = clist_begin(lep_list);
2247 struct mailimap_mailbox_list * mb;
2248 mb = clist_content(iter);
2250 folder->last_seen_separator = mb->mb_delimiter;
2251 debug_print("got separator: %c\n", folder->last_seen_separator);
2253 mailimap_list_result_free(lep_list);
2256 if (folder->last_seen_separator != 0) {
2257 debug_print("using separator: %c\n", folder->last_seen_separator);
2258 return folder->last_seen_separator;
2261 namespace = imap_find_namespace(folder, path);
2262 if (namespace && namespace->separator)
2263 separator = namespace->separator;
2268 static gchar *imap_get_real_path(IMAPFolder *folder, const gchar *path)
2273 g_return_val_if_fail(folder != NULL, NULL);
2274 g_return_val_if_fail(path != NULL, NULL);
2276 real_path = imap_utf8_to_modified_utf7(path);
2277 separator = imap_get_path_separator(folder, path);
2278 imap_path_separator_subst(real_path, separator);
2283 static gint imap_set_message_flags(IMAPSession *session,
2284 MsgNumberList *numlist,
2292 seq_list = imap_get_lep_set_from_numlist(numlist);
2294 for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) {
2295 struct mailimap_set * imapset;
2297 imapset = cur->data;
2299 ok = imap_cmd_store(session, imapset,
2303 imap_lep_set_free(seq_list);
2305 return IMAP_SUCCESS;
2308 typedef struct _select_data {
2309 IMAPSession *session;
2314 guint32 *uid_validity;
2318 static gint imap_select(IMAPSession *session, IMAPFolder *folder,
2320 gint *exists, gint *recent, gint *unseen,
2321 guint32 *uid_validity, gboolean block)
2325 gint exists_, recent_, unseen_;
2326 guint32 uid_validity_;
2328 if (!exists && !recent && !unseen && !uid_validity) {
2329 if (session->mbox && strcmp(session->mbox, path) == 0)
2330 return IMAP_SUCCESS;
2339 uid_validity = &uid_validity_;
2341 g_free(session->mbox);
2342 session->mbox = NULL;
2344 real_path = imap_get_real_path(folder, path);
2346 ok = imap_cmd_select(session, real_path,
2347 exists, recent, unseen, uid_validity, block);
2348 if (ok != IMAP_SUCCESS)
2349 log_warning(_("can't select folder: %s\n"), real_path);
2351 session->mbox = g_strdup(path);
2352 session->folder_content_changed = FALSE;
2359 static gint imap_status(IMAPSession *session, IMAPFolder *folder,
2360 const gchar *path, IMAPFolderItem *item,
2362 guint32 *uid_next, guint32 *uid_validity,
2363 gint *unseen, gboolean block)
2367 struct mailimap_mailbox_data_status * data_status;
2372 real_path = imap_get_real_path(folder, path);
2386 r = imap_threaded_status(FOLDER(folder), real_path,
2387 &data_status, mask);
2390 if (r != MAILIMAP_NO_ERROR) {
2391 debug_print("status err %d\n", r);
2395 if (data_status->st_info_list == NULL) {
2396 mailimap_mailbox_data_status_free(data_status);
2397 debug_print("status->st_info_list == NULL\n");
2402 for(iter = clist_begin(data_status->st_info_list) ; iter != NULL ;
2403 iter = clist_next(iter)) {
2404 struct mailimap_status_info * info;
2406 info = clist_content(iter);
2407 switch (info->st_att) {
2408 case MAILIMAP_STATUS_ATT_MESSAGES:
2409 * messages = info->st_value;
2410 got_values |= 1 << 0;
2413 case MAILIMAP_STATUS_ATT_UIDNEXT:
2414 * uid_next = info->st_value;
2415 got_values |= 1 << 2;
2418 case MAILIMAP_STATUS_ATT_UIDVALIDITY:
2419 * uid_validity = info->st_value;
2420 got_values |= 1 << 3;
2423 case MAILIMAP_STATUS_ATT_UNSEEN:
2424 * unseen = info->st_value;
2425 got_values |= 1 << 4;
2429 mailimap_mailbox_data_status_free(data_status);
2431 if (got_values != mask) {
2432 debug_print("status: incomplete values received (%d)\n", got_values);
2435 return IMAP_SUCCESS;
2438 static void imap_free_capabilities(IMAPSession *session)
2440 slist_free_strings(session->capability);
2441 g_slist_free(session->capability);
2442 session->capability = NULL;
2445 /* low-level IMAP4rev1 commands */
2447 static gint imap_cmd_login(IMAPSession *session,
2448 const gchar *user, const gchar *pass,
2454 log_print("IMAP4> Logging %s to %s using %s\n",
2456 SESSION(session)->server,
2458 r = imap_threaded_login(session->folder, user, pass, type);
2459 if (r != MAILIMAP_NO_ERROR) {
2460 log_error("IMAP4< Error logging in to %s\n",
2461 SESSION(session)->server);
2469 static gint imap_cmd_logout(IMAPSession *session)
2471 imap_threaded_disconnect(session->folder);
2473 return IMAP_SUCCESS;
2476 static gint imap_cmd_noop(IMAPSession *session)
2479 unsigned int exists;
2481 r = imap_threaded_noop(session->folder, &exists);
2482 if (r != MAILIMAP_NO_ERROR) {
2483 debug_print("noop err %d\n", r);
2486 session->exists = exists;
2487 session_set_access_time(SESSION(session));
2489 return IMAP_SUCCESS;
2493 static gint imap_cmd_starttls(IMAPSession *session)
2497 r = imap_threaded_starttls(session->folder,
2498 SESSION(session)->server, SESSION(session)->port);
2499 if (r != MAILIMAP_NO_ERROR) {
2500 debug_print("starttls err %d\n", r);
2503 return IMAP_SUCCESS;
2507 static gint imap_cmd_select(IMAPSession *session, const gchar *folder,
2508 gint *exists, gint *recent, gint *unseen,
2509 guint32 *uid_validity, gboolean block)
2513 r = imap_threaded_select(session->folder, folder,
2514 exists, recent, unseen, uid_validity);
2515 if (r != MAILIMAP_NO_ERROR) {
2516 debug_print("select err %d\n", r);
2519 return IMAP_SUCCESS;
2522 static gint imap_cmd_examine(IMAPSession *session, const gchar *folder,
2523 gint *exists, gint *recent, gint *unseen,
2524 guint32 *uid_validity, gboolean block)
2528 r = imap_threaded_examine(session->folder, folder,
2529 exists, recent, unseen, uid_validity);
2530 if (r != MAILIMAP_NO_ERROR) {
2531 debug_print("examine err %d\n", r);
2535 return IMAP_SUCCESS;
2538 static gint imap_cmd_create(IMAPSession *session, const gchar *folder)
2542 r = imap_threaded_create(session->folder, folder);
2543 if (r != MAILIMAP_NO_ERROR) {
2548 return IMAP_SUCCESS;
2551 static gint imap_cmd_rename(IMAPSession *session, const gchar *old_folder,
2552 const gchar *new_folder)
2556 r = imap_threaded_rename(session->folder, old_folder,
2558 if (r != MAILIMAP_NO_ERROR) {
2563 return IMAP_SUCCESS;
2566 static gint imap_cmd_delete(IMAPSession *session, const gchar *folder)
2571 r = imap_threaded_delete(session->folder, folder);
2572 if (r != MAILIMAP_NO_ERROR) {
2577 return IMAP_SUCCESS;
2580 typedef struct _fetch_data {
2581 IMAPSession *session;
2583 const gchar *filename;
2589 static void *imap_cmd_fetch_thread(void *data)
2591 fetch_data *stuff = (fetch_data *)data;
2592 IMAPSession *session = stuff->session;
2593 guint32 uid = stuff->uid;
2594 const gchar *filename = stuff->filename;
2598 r = imap_threaded_fetch_content(session->folder,
2602 r = imap_threaded_fetch_content(session->folder,
2605 if (r != MAILIMAP_NO_ERROR) {
2606 debug_print("fetch err %d\n", r);
2607 return GINT_TO_POINTER(IMAP_ERROR);
2609 return GINT_TO_POINTER(IMAP_SUCCESS);
2612 static gint imap_cmd_fetch(IMAPSession *session, guint32 uid,
2613 const gchar *filename, gboolean headers,
2616 fetch_data *data = g_new0(fetch_data, 1);
2619 data->session = session;
2621 data->filename = filename;
2622 data->headers = headers;
2625 if (prefs_common.work_offline &&
2626 !inc_offline_should_override(
2627 _("Sylpheed-Claws needs network access in order "
2628 "to access the IMAP server."))) {
2632 statusbar_print_all(_("Fetching message..."));
2633 result = GPOINTER_TO_INT(imap_cmd_fetch_thread(data));
2634 statusbar_pop_all();
2640 static gint imap_cmd_append(IMAPSession *session, const gchar *destfolder,
2641 const gchar *file, IMAPFlags flags,
2644 struct mailimap_flag_list * flag_list;
2647 g_return_val_if_fail(file != NULL, IMAP_ERROR);
2649 flag_list = imap_flag_to_lep(flags);
2650 r = imap_threaded_append(session->folder, destfolder,
2651 file, flag_list, new_uid);
2652 mailimap_flag_list_free(flag_list);
2654 if (r != MAILIMAP_NO_ERROR) {
2655 debug_print("append err %d\n", r);
2658 return IMAP_SUCCESS;
2661 static gint imap_cmd_copy(IMAPSession *session, struct mailimap_set * set,
2662 const gchar *destfolder, GRelation *uid_mapping)
2666 g_return_val_if_fail(session != NULL, IMAP_ERROR);
2667 g_return_val_if_fail(set != NULL, IMAP_ERROR);
2668 g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
2670 r = imap_threaded_copy(session->folder, set, destfolder);
2671 if (r != MAILIMAP_NO_ERROR) {
2676 return IMAP_SUCCESS;
2679 static gint imap_cmd_store(IMAPSession *session, struct mailimap_set * set,
2680 IMAPFlags flags, int do_add)
2683 struct mailimap_flag_list * flag_list;
2684 struct mailimap_store_att_flags * store_att_flags;
2686 flag_list = imap_flag_to_lep(flags);
2690 mailimap_store_att_flags_new_add_flags_silent(flag_list);
2693 mailimap_store_att_flags_new_remove_flags_silent(flag_list);
2695 r = imap_threaded_store(session->folder, set, store_att_flags);
2696 mailimap_store_att_flags_free(store_att_flags);
2697 if (r != MAILIMAP_NO_ERROR) {
2702 return IMAP_SUCCESS;
2705 static gint imap_cmd_expunge(IMAPSession *session)
2709 if (prefs_common.work_offline &&
2710 !inc_offline_should_override(
2711 _("Sylpheed-Claws needs network access in order "
2712 "to access the IMAP server."))) {
2716 r = imap_threaded_expunge(session->folder);
2717 if (r != MAILIMAP_NO_ERROR) {
2722 return IMAP_SUCCESS;
2725 static void imap_path_separator_subst(gchar *str, gchar separator)
2728 gboolean in_escape = FALSE;
2730 if (!separator || separator == '/') return;
2732 for (p = str; *p != '\0'; p++) {
2733 if (*p == '/' && !in_escape)
2735 else if (*p == '&' && *(p + 1) != '-' && !in_escape)
2737 else if (*p == '-' && in_escape)
2742 static gchar *imap_modified_utf7_to_utf8(const gchar *mutf7_str)
2744 static iconv_t cd = (iconv_t)-1;
2745 static gboolean iconv_ok = TRUE;
2748 size_t norm_utf7_len;
2750 gchar *to_str, *to_p;
2752 gboolean in_escape = FALSE;
2754 if (!iconv_ok) return g_strdup(mutf7_str);
2756 if (cd == (iconv_t)-1) {
2757 cd = iconv_open(CS_INTERNAL, CS_UTF_7);
2758 if (cd == (iconv_t)-1) {
2759 g_warning("iconv cannot convert UTF-7 to %s\n",
2762 return g_strdup(mutf7_str);
2766 /* modified UTF-7 to normal UTF-7 conversion */
2767 norm_utf7 = g_string_new(NULL);
2769 for (p = mutf7_str; *p != '\0'; p++) {
2770 /* replace: '&' -> '+',
2772 escaped ',' -> '/' */
2773 if (!in_escape && *p == '&') {
2774 if (*(p + 1) != '-') {
2775 g_string_append_c(norm_utf7, '+');
2778 g_string_append_c(norm_utf7, '&');
2781 } else if (in_escape && *p == ',') {
2782 g_string_append_c(norm_utf7, '/');
2783 } else if (in_escape && *p == '-') {
2784 g_string_append_c(norm_utf7, '-');
2787 g_string_append_c(norm_utf7, *p);
2791 norm_utf7_p = norm_utf7->str;
2792 norm_utf7_len = norm_utf7->len;
2793 to_len = strlen(mutf7_str) * 5;
2794 to_p = to_str = g_malloc(to_len + 1);
2796 if (iconv(cd, (ICONV_CONST gchar **)&norm_utf7_p, &norm_utf7_len,
2797 &to_p, &to_len) == -1) {
2798 g_warning(_("iconv cannot convert UTF-7 to %s\n"),
2799 conv_get_locale_charset_str());
2800 g_string_free(norm_utf7, TRUE);
2802 return g_strdup(mutf7_str);
2805 /* second iconv() call for flushing */
2806 iconv(cd, NULL, NULL, &to_p, &to_len);
2807 g_string_free(norm_utf7, TRUE);
2813 static gchar *imap_utf8_to_modified_utf7(const gchar *from)
2815 static iconv_t cd = (iconv_t)-1;
2816 static gboolean iconv_ok = TRUE;
2817 gchar *norm_utf7, *norm_utf7_p;
2818 size_t from_len, norm_utf7_len;
2820 gchar *from_tmp, *to, *p;
2821 gboolean in_escape = FALSE;
2823 if (!iconv_ok) return g_strdup(from);
2825 if (cd == (iconv_t)-1) {
2826 cd = iconv_open(CS_UTF_7, CS_INTERNAL);
2827 if (cd == (iconv_t)-1) {
2828 g_warning(_("iconv cannot convert %s to UTF-7\n"),
2831 return g_strdup(from);
2835 /* UTF-8 to normal UTF-7 conversion */
2836 Xstrdup_a(from_tmp, from, return g_strdup(from));
2837 from_len = strlen(from);
2838 norm_utf7_len = from_len * 5;
2839 Xalloca(norm_utf7, norm_utf7_len + 1, return g_strdup(from));
2840 norm_utf7_p = norm_utf7;
2842 #define IS_PRINT(ch) (isprint(ch) && IS_ASCII(ch))
2844 while (from_len > 0) {
2845 if (*from_tmp == '+') {
2846 *norm_utf7_p++ = '+';
2847 *norm_utf7_p++ = '-';
2851 } else if (IS_PRINT(*(guchar *)from_tmp)) {
2852 /* printable ascii char */
2853 *norm_utf7_p = *from_tmp;
2859 size_t conv_len = 0;
2861 /* unprintable char: convert to UTF-7 */
2863 while (!IS_PRINT(*(guchar *)p) && conv_len < from_len) {
2864 conv_len += g_utf8_skip[*(guchar *)p];
2865 p += g_utf8_skip[*(guchar *)p];
2868 from_len -= conv_len;
2869 if (iconv(cd, (ICONV_CONST gchar **)&from_tmp,
2871 &norm_utf7_p, &norm_utf7_len) == -1) {
2872 g_warning(_("iconv cannot convert UTF-8 to UTF-7\n"));
2873 return g_strdup(from);
2876 /* second iconv() call for flushing */
2877 iconv(cd, NULL, NULL, &norm_utf7_p, &norm_utf7_len);
2883 *norm_utf7_p = '\0';
2884 to_str = g_string_new(NULL);
2885 for (p = norm_utf7; p < norm_utf7_p; p++) {
2886 /* replace: '&' -> "&-",
2889 BASE64 '/' -> ',' */
2890 if (!in_escape && *p == '&') {
2891 g_string_append(to_str, "&-");
2892 } else if (!in_escape && *p == '+') {
2893 if (*(p + 1) == '-') {
2894 g_string_append_c(to_str, '+');
2897 g_string_append_c(to_str, '&');
2900 } else if (in_escape && *p == '/') {
2901 g_string_append_c(to_str, ',');
2902 } else if (in_escape && *p == '-') {
2903 g_string_append_c(to_str, '-');
2906 g_string_append_c(to_str, *p);
2912 g_string_append_c(to_str, '-');
2916 g_string_free(to_str, FALSE);
2921 static gboolean imap_rename_folder_func(GNode *node, gpointer data)
2923 FolderItem *item = node->data;
2924 gchar **paths = data;
2925 const gchar *oldpath = paths[0];
2926 const gchar *newpath = paths[1];
2928 gchar *new_itempath;
2931 oldpathlen = strlen(oldpath);
2932 if (strncmp(oldpath, item->path, oldpathlen) != 0) {
2933 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
2937 base = item->path + oldpathlen;
2938 while (*base == G_DIR_SEPARATOR) base++;
2940 new_itempath = g_strdup(newpath);
2942 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
2945 item->path = new_itempath;
2950 typedef struct _get_list_uid_data {
2952 IMAPSession *session;
2953 IMAPFolderItem *item;
2954 GSList **msgnum_list;
2956 } get_list_uid_data;
2958 static void *get_list_of_uids_thread(void *data)
2960 get_list_uid_data *stuff = (get_list_uid_data *)data;
2961 Folder *folder = stuff->folder;
2962 IMAPFolderItem *item = stuff->item;
2963 GSList **msgnum_list = stuff->msgnum_list;
2964 gint ok, nummsgs = 0, lastuid_old;
2965 IMAPSession *session;
2966 GSList *uidlist, *elem;
2967 struct mailimap_set * set;
2968 clist * lep_uidlist;
2971 session = stuff->session;
2972 if (session == NULL) {
2974 return GINT_TO_POINTER(-1);
2976 /* no session locking here, it's already locked by caller */
2977 ok = imap_select(session, IMAP_FOLDER(folder), item->item.path,
2978 NULL, NULL, NULL, NULL, TRUE);
2979 if (ok != IMAP_SUCCESS) {
2981 return GINT_TO_POINTER(-1);
2986 set = mailimap_set_new_interval(item->lastuid + 1, 0);
2988 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SIMPLE, set,
2990 mailimap_set_free(set);
2992 if (r == MAILIMAP_NO_ERROR) {
2993 GSList * fetchuid_list;
2996 imap_uid_list_from_lep(lep_uidlist);
2997 mailimap_search_result_free(lep_uidlist);
2999 uidlist = g_slist_concat(fetchuid_list, uidlist);
3002 GSList * fetchuid_list;
3003 carray * lep_uidtab;
3005 r = imap_threaded_fetch_uid(folder, item->lastuid + 1,
3007 if (r == MAILIMAP_NO_ERROR) {
3009 imap_uid_list_from_lep_tab(lep_uidtab);
3010 imap_fetch_uid_list_free(lep_uidtab);
3011 uidlist = g_slist_concat(fetchuid_list, uidlist);
3015 lastuid_old = item->lastuid;
3016 *msgnum_list = g_slist_copy(item->uid_list);
3017 nummsgs = g_slist_length(*msgnum_list);
3018 debug_print("Got %d uids from cache\n", g_slist_length(item->uid_list));
3020 for (elem = uidlist; elem != NULL; elem = g_slist_next(elem)) {
3023 msgnum = GPOINTER_TO_INT(elem->data);
3024 if (msgnum > lastuid_old) {
3025 *msgnum_list = g_slist_prepend(*msgnum_list, GINT_TO_POINTER(msgnum));
3026 item->uid_list = g_slist_prepend(item->uid_list, GINT_TO_POINTER(msgnum));
3029 if(msgnum > item->lastuid)
3030 item->lastuid = msgnum;
3033 g_slist_free(uidlist);
3035 return GINT_TO_POINTER(nummsgs);
3038 static gint get_list_of_uids(IMAPSession *session, Folder *folder, IMAPFolderItem *item, GSList **msgnum_list)
3041 get_list_uid_data *data = g_new0(get_list_uid_data, 1);
3043 data->folder = folder;
3045 data->msgnum_list = msgnum_list;
3046 data->session = session;
3047 if (prefs_common.work_offline &&
3048 !inc_offline_should_override(
3049 _("Sylpheed-Claws needs network access in order "
3050 "to access the IMAP server."))) {
3055 result = GPOINTER_TO_INT(get_list_of_uids_thread(data));
3061 gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list, gboolean *old_uids_valid)
3063 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3064 IMAPSession *session;
3065 gint ok, nummsgs = 0, exists, uid_val, uid_next;
3066 GSList *uidlist = NULL;
3068 gboolean selected_folder;
3070 debug_print("get_num_list\n");
3072 g_return_val_if_fail(folder != NULL, -1);
3073 g_return_val_if_fail(item != NULL, -1);
3074 g_return_val_if_fail(item->item.path != NULL, -1);
3075 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3076 g_return_val_if_fail(folder->account != NULL, -1);
3078 session = imap_session_get(folder);
3079 g_return_val_if_fail(session != NULL, -1);
3081 statusbar_print_all("Scanning %s...\n", FOLDER_ITEM(item)->path
3082 ? FOLDER_ITEM(item)->path:"");
3084 selected_folder = (session->mbox != NULL) &&
3085 (!strcmp(session->mbox, item->item.path));
3086 if (selected_folder && time(NULL) - item->use_cache < 2) {
3087 ok = imap_cmd_noop(session);
3088 if (ok != IMAP_SUCCESS) {
3089 debug_print("disconnected!\n");
3090 session = imap_reconnect_if_possible(folder, session);
3091 if (session == NULL) {
3092 statusbar_pop_all();
3097 exists = session->exists;
3099 *old_uids_valid = TRUE;
3101 if (item->use_cache && time(NULL) - item->use_cache < 2) {
3102 exists = item->c_messages;
3103 uid_next = item->c_uid_next;
3104 uid_val = item->c_uid_validity;
3106 debug_print("using cache %d %d %d\n", exists, uid_next, uid_val);
3108 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path, item,
3109 &exists, &uid_next, &uid_val, NULL, FALSE);
3111 item->item.last_num = uid_next - 1;
3113 item->use_cache = (time_t)0;
3114 if (ok != IMAP_SUCCESS) {
3115 statusbar_pop_all();
3119 if(item->item.mtime == uid_val)
3120 *old_uids_valid = TRUE;
3122 *old_uids_valid = FALSE;
3124 debug_print("Freeing imap uid cache\n");
3126 g_slist_free(item->uid_list);
3127 item->uid_list = NULL;
3129 item->item.mtime = uid_val;
3131 imap_delete_all_cached_messages((FolderItem *)item);
3135 /* If old uid_next matches new uid_next we can be sure no message
3136 was added to the folder */
3137 debug_print("uid_next is %d and item->uid_next %d \n",
3138 uid_next, item->uid_next);
3139 if (uid_next == item->uid_next) {
3140 nummsgs = g_slist_length(item->uid_list);
3142 /* If number of messages is still the same we
3143 know our caches message numbers are still valid,
3144 otherwise if the number of messages has decrease
3145 we discard our cache to start a new scan to find
3146 out which numbers have been removed */
3147 if (exists == nummsgs) {
3148 debug_print("exists == nummsgs\n");
3149 *msgnum_list = g_slist_copy(item->uid_list);
3150 statusbar_pop_all();
3153 } else if (exists < nummsgs) {
3154 debug_print("Freeing imap uid cache");
3156 g_slist_free(item->uid_list);
3157 item->uid_list = NULL;
3162 *msgnum_list = NULL;
3163 statusbar_pop_all();
3168 nummsgs = get_list_of_uids(session, folder, item, &uidlist);
3171 statusbar_pop_all();
3176 if (nummsgs != exists) {
3177 /* Cache contains more messages then folder, we have cached
3178 an old UID of a message that was removed and new messages
3179 have been added too, otherwise the uid_next check would
3181 debug_print("Freeing imap uid cache");
3183 g_slist_free(item->uid_list);
3184 item->uid_list = NULL;
3186 g_slist_free(*msgnum_list);
3188 nummsgs = get_list_of_uids(session, folder, item, &uidlist);
3191 *msgnum_list = uidlist;
3193 dir = folder_item_get_path((FolderItem *)item);
3194 debug_print("removing old messages from %s\n", dir);
3195 remove_numbered_files_not_in_list(dir, *msgnum_list);
3198 item->uid_next = uid_next;
3200 debug_print("get_num_list - ok - %i\n", nummsgs);
3201 statusbar_pop_all();
3206 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item)
3211 flags.perm_flags = MSG_NEW|MSG_UNREAD;
3212 flags.tmp_flags = 0;
3214 g_return_val_if_fail(item != NULL, NULL);
3215 g_return_val_if_fail(file != NULL, NULL);
3217 if (folder_has_parent_of_type(item, F_QUEUE)) {
3218 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
3219 } else if (folder_has_parent_of_type(item, F_DRAFT)) {
3220 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
3223 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3224 if (!msginfo) return NULL;
3226 msginfo->plaintext_file = g_strdup(file);
3227 msginfo->folder = item;
3232 GSList *imap_get_msginfos(Folder *folder, FolderItem *item,
3233 GSList *msgnum_list)
3235 IMAPSession *session;
3236 MsgInfoList *ret = NULL;
3239 debug_print("get_msginfos\n");
3241 g_return_val_if_fail(folder != NULL, NULL);
3242 g_return_val_if_fail(item != NULL, NULL);
3243 g_return_val_if_fail(msgnum_list != NULL, NULL);
3245 session = imap_session_get(folder);
3246 g_return_val_if_fail(session != NULL, NULL);
3248 debug_print("IMAP getting msginfos\n");
3249 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3250 NULL, NULL, NULL, NULL, FALSE);
3251 if (ok != IMAP_SUCCESS) {
3255 if (!(folder_has_parent_of_type(item, F_DRAFT) ||
3256 folder_has_parent_of_type(item, F_QUEUE))) {
3257 ret = g_slist_concat(ret,
3258 imap_get_uncached_messages(session, item,
3261 MsgNumberList *sorted_list, *elem;
3262 gint startnum, lastnum;
3264 sorted_list = g_slist_sort(g_slist_copy(msgnum_list), g_int_compare);
3266 startnum = lastnum = GPOINTER_TO_INT(sorted_list->data);
3268 for (elem = sorted_list;; elem = g_slist_next(elem)) {
3272 num = GPOINTER_TO_INT(elem->data);
3274 if (num > lastnum + 1 || elem == NULL) {
3276 for (i = startnum; i <= lastnum; ++i) {
3279 file = imap_fetch_msg(folder, item, i);
3281 MsgInfo *msginfo = imap_parse_msg(file, item);
3282 if (msginfo != NULL) {
3283 msginfo->msgnum = i;
3284 ret = g_slist_append(ret, msginfo);
3288 session_set_access_time(SESSION(session));
3299 g_slist_free(sorted_list);
3305 MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid)
3307 MsgInfo *msginfo = NULL;
3308 MsgInfoList *msginfolist;
3309 MsgNumberList numlist;
3311 numlist.next = NULL;
3312 numlist.data = GINT_TO_POINTER(uid);
3314 msginfolist = imap_get_msginfos(folder, item, &numlist);
3315 if (msginfolist != NULL) {
3316 msginfo = msginfolist->data;
3317 g_slist_free(msginfolist);
3323 gboolean imap_scan_required(Folder *folder, FolderItem *_item)
3325 IMAPSession *session;
3326 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3327 gint ok, exists = 0, unseen = 0;
3328 guint32 uid_next, uid_val;
3329 gboolean selected_folder;
3331 g_return_val_if_fail(folder != NULL, FALSE);
3332 g_return_val_if_fail(item != NULL, FALSE);
3333 g_return_val_if_fail(item->item.folder != NULL, FALSE);
3334 g_return_val_if_fail(FOLDER_CLASS(item->item.folder) == &imap_class, FALSE);
3336 if (item->item.path == NULL)
3339 session = imap_session_get(folder);
3340 g_return_val_if_fail(session != NULL, FALSE);
3342 selected_folder = (session->mbox != NULL) &&
3343 (!strcmp(session->mbox, item->item.path));
3344 if (selected_folder && time(NULL) - item->use_cache < 2) {
3345 ok = imap_cmd_noop(session);
3346 if (ok != IMAP_SUCCESS) {
3347 debug_print("disconnected!\n");
3348 session = imap_reconnect_if_possible(folder, session);
3349 if (session == NULL)
3354 if (session->folder_content_changed
3355 || session->exists != item->item.total_msgs) {
3360 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path, IMAP_FOLDER_ITEM(item),
3361 &exists, &uid_next, &uid_val, &unseen, FALSE);
3362 if (ok != IMAP_SUCCESS) {
3367 item->use_cache = time(NULL);
3368 item->c_messages = exists;
3369 item->c_uid_next = uid_next;
3370 item->c_uid_validity = uid_val;
3371 item->c_unseen = unseen;
3372 item->item.last_num = uid_next - 1;
3373 debug_print("uidnext %d, item->uid_next %d, exists %d, item->item.total_msgs %d\n",
3374 uid_next, item->uid_next, exists, item->item.total_msgs);
3375 if ((uid_next != item->uid_next) || (exists != item->item.total_msgs)) {
3384 void imap_change_flags(Folder *folder, FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
3386 IMAPSession *session;
3387 IMAPFlags flags_set = 0, flags_unset = 0;
3388 gint ok = IMAP_SUCCESS;
3389 MsgNumberList numlist;
3390 hashtable_data *ht_data = NULL;
3392 g_return_if_fail(folder != NULL);
3393 g_return_if_fail(folder->klass == &imap_class);
3394 g_return_if_fail(item != NULL);
3395 g_return_if_fail(item->folder == folder);
3396 g_return_if_fail(msginfo != NULL);
3397 g_return_if_fail(msginfo->folder == item);
3399 session = imap_session_get(folder);
3404 if ((ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
3405 NULL, NULL, NULL, NULL, FALSE)) != IMAP_SUCCESS) {
3410 if (!MSG_IS_MARKED(msginfo->flags) && (newflags & MSG_MARKED))
3411 flags_set |= IMAP_FLAG_FLAGGED;
3412 if ( MSG_IS_MARKED(msginfo->flags) && !(newflags & MSG_MARKED))
3413 flags_unset |= IMAP_FLAG_FLAGGED;
3415 if (!MSG_IS_UNREAD(msginfo->flags) && (newflags & MSG_UNREAD))
3416 flags_unset |= IMAP_FLAG_SEEN;
3417 if ( MSG_IS_UNREAD(msginfo->flags) && !(newflags & MSG_UNREAD))
3418 flags_set |= IMAP_FLAG_SEEN;
3420 if (!MSG_IS_REPLIED(msginfo->flags) && (newflags & MSG_REPLIED))
3421 flags_set |= IMAP_FLAG_ANSWERED;
3422 if ( MSG_IS_REPLIED(msginfo->flags) && !(newflags & MSG_REPLIED))
3423 flags_unset |= IMAP_FLAG_ANSWERED;
3425 if (!MSG_IS_DELETED(msginfo->flags) && (newflags & MSG_DELETED))
3426 flags_set |= IMAP_FLAG_DELETED;
3427 if ( MSG_IS_DELETED(msginfo->flags) && !(newflags & MSG_DELETED))
3428 flags_unset |= IMAP_FLAG_DELETED;
3430 numlist.next = NULL;
3431 numlist.data = GINT_TO_POINTER(msginfo->msgnum);
3433 if (IMAP_FOLDER_ITEM(item)->batching) {
3434 /* instead of performing an UID STORE command for each message change,
3435 * as a lot of them can change "together", we just fill in hashtables
3436 * and defer the treatment so that we're able to send only one
3439 debug_print("IMAP batch mode on, deferring flags change\n");
3441 ht_data = g_hash_table_lookup(IMAP_FOLDER_ITEM(item)->flags_set_table,
3442 GINT_TO_POINTER(flags_set));
3443 if (ht_data == NULL) {
3444 ht_data = g_new0(hashtable_data, 1);
3445 ht_data->session = session;
3446 ht_data->item = IMAP_FOLDER_ITEM(item);
3447 g_hash_table_insert(IMAP_FOLDER_ITEM(item)->flags_set_table,
3448 GINT_TO_POINTER(flags_set), ht_data);
3450 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
3451 ht_data->msglist = g_slist_prepend(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum));
3454 ht_data = g_hash_table_lookup(IMAP_FOLDER_ITEM(item)->flags_unset_table,
3455 GINT_TO_POINTER(flags_unset));
3456 if (ht_data == NULL) {
3457 ht_data = g_new0(hashtable_data, 1);
3458 ht_data->session = session;
3459 ht_data->item = IMAP_FOLDER_ITEM(item);
3460 g_hash_table_insert(IMAP_FOLDER_ITEM(item)->flags_unset_table,
3461 GINT_TO_POINTER(flags_unset), ht_data);
3463 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
3464 ht_data->msglist = g_slist_prepend(ht_data->msglist,
3465 GINT_TO_POINTER(msginfo->msgnum));
3468 debug_print("IMAP changing flags\n");
3470 ok = imap_set_message_flags(session, &numlist, flags_set, TRUE);
3471 if (ok != IMAP_SUCCESS) {
3478 ok = imap_set_message_flags(session, &numlist, flags_unset, FALSE);
3479 if (ok != IMAP_SUCCESS) {
3485 msginfo->flags.perm_flags = newflags;
3490 static gint imap_remove_msg(Folder *folder, FolderItem *item, gint uid)
3493 IMAPSession *session;
3495 MsgNumberList numlist;
3497 g_return_val_if_fail(folder != NULL, -1);
3498 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3499 g_return_val_if_fail(item != NULL, -1);
3501 session = imap_session_get(folder);
3502 if (!session) return -1;
3504 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3505 NULL, NULL, NULL, NULL, FALSE);
3506 if (ok != IMAP_SUCCESS) {
3510 numlist.next = NULL;
3511 numlist.data = GINT_TO_POINTER(uid);
3513 ok = imap_set_message_flags
3514 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
3515 &numlist, IMAP_FLAG_DELETED, TRUE);
3516 if (ok != IMAP_SUCCESS) {
3517 log_warning(_("can't set deleted flags: %d\n"), uid);
3522 if (!session->uidplus) {
3523 ok = imap_cmd_expunge(session);
3527 uidstr = g_strdup_printf("%u", uid);
3528 ok = imap_cmd_expunge(session);
3531 if (ok != IMAP_SUCCESS) {
3532 log_warning(_("can't expunge\n"));
3537 IMAP_FOLDER_ITEM(item)->uid_list = g_slist_remove(
3538 IMAP_FOLDER_ITEM(item)->uid_list, numlist.data);
3539 dir = folder_item_get_path(item);
3540 if (is_dir_exist(dir))
3541 remove_numbered_files(dir, uid, uid);
3544 return IMAP_SUCCESS;
3547 static gint compare_msginfo(gconstpointer a, gconstpointer b)
3549 return ((MsgInfo *)a)->msgnum - ((MsgInfo *)b)->msgnum;
3552 static guint gslist_find_next_num(MsgNumberList **list, guint num)
3556 g_return_val_if_fail(list != NULL, -1);
3558 for (elem = *list; elem != NULL; elem = g_slist_next(elem))
3559 if (GPOINTER_TO_INT(elem->data) >= num)
3562 return elem != NULL ? GPOINTER_TO_INT(elem->data) : (gint)-1;
3566 * NEW and DELETED flags are not syncronized
3567 * - The NEW/RECENT flags in IMAP folders can not really be directly
3568 * modified by Sylpheed
3569 * - The DELETE/DELETED flag in IMAP and Sylpheed don't have the same
3570 * meaning, in IMAP it always removes the messages from the FolderItem
3571 * in Sylpheed it can mean to move the message to trash
3574 typedef struct _get_flags_data {
3577 MsgInfoList *msginfo_list;
3578 GRelation *msgflags;
3579 gboolean full_search;
3583 static /*gint*/ void *imap_get_flags_thread(void *data)
3585 get_flags_data *stuff = (get_flags_data *)data;
3586 Folder *folder = stuff->folder;
3587 FolderItem *item = stuff->item;
3588 MsgInfoList *msginfo_list = stuff->msginfo_list;
3589 GRelation *msgflags = stuff->msgflags;
3590 gboolean full_search = stuff->full_search;
3591 IMAPSession *session;
3592 GSList *sorted_list = NULL;
3593 GSList *unseen = NULL, *answered = NULL, *flagged = NULL, *deleted = NULL;
3594 GSList *p_unseen, *p_answered, *p_flagged, *p_deleted;
3596 GSList *seq_list, *cur;
3597 gboolean reverse_seen = FALSE;
3600 gint exists_cnt, unseen_cnt;
3601 gboolean selected_folder;
3603 if (folder == NULL || item == NULL) {
3605 return GINT_TO_POINTER(-1);
3608 session = imap_session_get(folder);
3609 if (session == NULL) {
3611 return GINT_TO_POINTER(-1);
3614 selected_folder = (session->mbox != NULL) &&
3615 (!strcmp(session->mbox, item->path));
3617 if (!selected_folder) {
3618 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3619 &exists_cnt, NULL, &unseen_cnt, NULL, TRUE);
3620 if (ok != IMAP_SUCCESS) {
3623 return GINT_TO_POINTER(-1);
3626 if (unseen_cnt > exists_cnt / 2)
3627 reverse_seen = TRUE;
3630 if (item->unread_msgs > item->total_msgs / 2)
3631 reverse_seen = TRUE;
3634 cmd_buf = g_string_new(NULL);
3636 sorted_list = g_slist_sort(g_slist_copy(msginfo_list), compare_msginfo);
3638 seq_list = imap_get_lep_set_from_msglist(msginfo_list);
3640 struct mailimap_set * set;
3641 set = mailimap_set_new_interval(1, 0);
3642 seq_list = g_slist_append(NULL, set);
3645 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
3646 struct mailimap_set * imapset;
3647 clist * lep_uidlist;
3650 imapset = cur->data;
3652 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SEEN,
3653 imapset, &lep_uidlist);
3656 r = imap_threaded_search(folder,
3657 IMAP_SEARCH_TYPE_UNSEEN,
3658 imapset, &lep_uidlist);
3660 if (r == MAILIMAP_NO_ERROR) {
3663 uidlist = imap_uid_list_from_lep(lep_uidlist);
3664 mailimap_search_result_free(lep_uidlist);
3666 unseen = g_slist_concat(unseen, uidlist);
3669 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_ANSWERED,
3670 imapset, &lep_uidlist);
3671 if (r == MAILIMAP_NO_ERROR) {
3674 uidlist = imap_uid_list_from_lep(lep_uidlist);
3675 mailimap_search_result_free(lep_uidlist);
3677 answered = g_slist_concat(answered, uidlist);
3680 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_FLAGGED,
3681 imapset, &lep_uidlist);
3682 if (r == MAILIMAP_NO_ERROR) {
3685 uidlist = imap_uid_list_from_lep(lep_uidlist);
3686 mailimap_search_result_free(lep_uidlist);
3688 flagged = g_slist_concat(flagged, uidlist);
3691 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_DELETED,
3692 imapset, &lep_uidlist);
3693 if (r == MAILIMAP_NO_ERROR) {
3696 uidlist = imap_uid_list_from_lep(lep_uidlist);
3697 mailimap_search_result_free(lep_uidlist);
3699 deleted = g_slist_concat(deleted, uidlist);
3704 p_answered = answered;
3705 p_flagged = flagged;
3706 p_deleted = deleted;
3708 for (elem = sorted_list; elem != NULL; elem = g_slist_next(elem)) {
3713 msginfo = (MsgInfo *) elem->data;
3714 flags = msginfo->flags.perm_flags;
3715 wasnew = (flags & MSG_NEW);
3716 flags &= ~((reverse_seen ? 0 : MSG_UNREAD | MSG_NEW) | MSG_REPLIED | MSG_MARKED);
3718 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
3719 if (gslist_find_next_num(&p_unseen, msginfo->msgnum) == msginfo->msgnum) {
3720 if (!reverse_seen) {
3721 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
3723 flags &= ~(MSG_UNREAD | MSG_NEW);
3726 if (gslist_find_next_num(&p_answered, msginfo->msgnum) == msginfo->msgnum)
3727 flags |= MSG_REPLIED;
3729 flags &= ~MSG_REPLIED;
3730 if (gslist_find_next_num(&p_flagged, msginfo->msgnum) == msginfo->msgnum)
3731 flags |= MSG_MARKED;
3733 flags &= ~MSG_MARKED;
3734 if (gslist_find_next_num(&p_deleted, msginfo->msgnum) == msginfo->msgnum)
3735 flags |= MSG_DELETED;
3737 flags &= ~MSG_DELETED;
3738 g_relation_insert(msgflags, msginfo, GINT_TO_POINTER(flags));
3741 imap_lep_set_free(seq_list);
3742 g_slist_free(flagged);
3743 g_slist_free(deleted);
3744 g_slist_free(answered);
3745 g_slist_free(unseen);
3746 g_slist_free(sorted_list);
3747 g_string_free(cmd_buf, TRUE);
3751 return GINT_TO_POINTER(0);
3754 static gint imap_get_flags(Folder *folder, FolderItem *item,
3755 MsgInfoList *msginfo_list, GRelation *msgflags)
3758 get_flags_data *data = g_new0(get_flags_data, 1);
3760 data->folder = folder;
3762 data->msginfo_list = msginfo_list;
3763 data->msgflags = msgflags;
3764 data->full_search = FALSE;
3766 GSList *tmp = NULL, *cur;
3768 if (prefs_common.work_offline &&
3769 !inc_offline_should_override(
3770 _("Sylpheed-Claws needs network access in order "
3771 "to access the IMAP server."))) {
3776 tmp = folder_item_get_msg_list(item);
3778 if (g_slist_length(tmp) == g_slist_length(msginfo_list))
3779 data->full_search = TRUE;
3781 for (cur = tmp; cur; cur = cur->next)
3782 procmsg_msginfo_free((MsgInfo *)cur->data);
3786 result = GPOINTER_TO_INT(imap_get_flags_thread(data));
3793 static gboolean process_flags(gpointer key, gpointer value, gpointer user_data)
3795 gboolean flags_set = GPOINTER_TO_INT(user_data);
3796 gint flags_value = GPOINTER_TO_INT(key);
3797 hashtable_data *data = (hashtable_data *)value;
3798 IMAPFolderItem *_item = data->item;
3799 FolderItem *item = (FolderItem *)_item;
3800 gint ok = IMAP_ERROR;
3801 IMAPSession *session = imap_session_get(item->folder);
3803 data->msglist = g_slist_reverse(data->msglist);
3805 debug_print("IMAP %ssetting flags to %d for %d messages\n",
3808 g_slist_length(data->msglist));
3812 ok = imap_select(session, IMAP_FOLDER(item->folder), item->path,
3813 NULL, NULL, NULL, NULL, FALSE);
3815 if (ok == IMAP_SUCCESS) {
3816 imap_set_message_flags(data->session, data->msglist, flags_value, flags_set);
3818 g_warning("can't select mailbox %s\n", item->path);
3822 g_slist_free(data->msglist);
3827 static void process_hashtable(IMAPFolderItem *item)
3829 if (item->flags_set_table) {
3830 g_hash_table_foreach_remove(item->flags_set_table, process_flags, GINT_TO_POINTER(TRUE));
3831 g_hash_table_destroy(item->flags_set_table);
3832 item->flags_set_table = NULL;
3834 if (item->flags_unset_table) {
3835 g_hash_table_foreach_remove(item->flags_unset_table, process_flags, GINT_TO_POINTER(FALSE));
3836 g_hash_table_destroy(item->flags_unset_table);
3837 item->flags_unset_table = NULL;
3841 static void imap_set_batch (Folder *folder, FolderItem *_item, gboolean batch)
3843 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3845 g_return_if_fail(item != NULL);
3847 if (item->batching == batch)
3851 item->batching = TRUE;
3852 debug_print("IMAP switching to batch mode\n");
3853 if (!item->flags_set_table) {
3854 item->flags_set_table = g_hash_table_new(NULL, g_direct_equal);
3856 if (!item->flags_unset_table) {
3857 item->flags_unset_table = g_hash_table_new(NULL, g_direct_equal);
3860 debug_print("IMAP switching away from batch mode\n");
3862 process_hashtable(item);
3863 item->batching = FALSE;
3869 /* data types conversion libetpan <-> sylpheed */
3873 #define ETPAN_IMAP_MB_MARKED 1
3874 #define ETPAN_IMAP_MB_UNMARKED 2
3875 #define ETPAN_IMAP_MB_NOSELECT 4
3876 #define ETPAN_IMAP_MB_NOINFERIORS 8
3878 static int imap_flags_to_flags(struct mailimap_mbx_list_flags * imap_flags)
3884 if (imap_flags->mbf_type == MAILIMAP_MBX_LIST_FLAGS_SFLAG) {
3885 switch (imap_flags->mbf_sflag) {
3886 case MAILIMAP_MBX_LIST_SFLAG_MARKED:
3887 flags |= ETPAN_IMAP_MB_MARKED;
3889 case MAILIMAP_MBX_LIST_SFLAG_NOSELECT:
3890 flags |= ETPAN_IMAP_MB_NOSELECT;
3892 case MAILIMAP_MBX_LIST_SFLAG_UNMARKED:
3893 flags |= ETPAN_IMAP_MB_UNMARKED;
3898 for(cur = clist_begin(imap_flags->mbf_oflags) ; cur != NULL ;
3899 cur = clist_next(cur)) {
3900 struct mailimap_mbx_list_oflag * oflag;
3902 oflag = clist_content(cur);
3904 switch (oflag->of_type) {
3905 case MAILIMAP_MBX_LIST_OFLAG_NOINFERIORS:
3906 flags |= ETPAN_IMAP_MB_NOINFERIORS;
3914 static GSList * imap_list_from_lep(IMAPFolder * folder,
3915 clist * list, const gchar * real_path, gboolean all)
3922 for(iter = clist_begin(list) ; iter != NULL ;
3923 iter = clist_next(iter)) {
3924 struct mailimap_mailbox_list * mb;
3932 FolderItem *new_item;
3934 mb = clist_content(iter);
3940 if (mb->mb_flag != NULL)
3941 flags = imap_flags_to_flags(mb->mb_flag);
3943 delimiter = mb->mb_delimiter;
3946 dup_name = strdup(name);
3947 if (delimiter != '\0')
3948 subst_char(dup_name, delimiter, '/');
3950 base = g_path_get_basename(dup_name);
3951 if (base[0] == '.') {
3957 if (!all && strcmp(dup_name, real_path) == 0) {
3963 if (!all && dup_name[strlen(dup_name)-1] == '/') {
3969 loc_name = imap_modified_utf7_to_utf8(base);
3970 loc_path = imap_modified_utf7_to_utf8(dup_name);
3972 new_item = folder_item_new(FOLDER(folder), loc_name, loc_path);
3973 if ((flags & ETPAN_IMAP_MB_NOINFERIORS) != 0)
3974 new_item->no_sub = TRUE;
3975 if (strcmp(dup_name, "INBOX") != 0 &&
3976 ((flags & ETPAN_IMAP_MB_NOSELECT) != 0))
3977 new_item->no_select = TRUE;
3979 item_list = g_slist_append(item_list, new_item);
3981 debug_print("folder '%s' found.\n", loc_path);
3992 static GSList * imap_get_lep_set_from_numlist(MsgNumberList *numlist)
3994 GSList *sorted_list, *cur;
3995 guint first, last, next;
3996 GSList *ret_list = NULL;
3998 struct mailimap_set * current_set;
3999 unsigned int item_count;
4001 if (numlist == NULL)
4005 current_set = mailimap_set_new_empty();
4007 sorted_list = g_slist_copy(numlist);
4008 sorted_list = g_slist_sort(sorted_list, g_int_compare);
4010 first = GPOINTER_TO_INT(sorted_list->data);
4013 for (cur = sorted_list; cur != NULL; cur = g_slist_next(cur)) {
4014 if (GPOINTER_TO_INT(cur->data) == 0)
4019 last = GPOINTER_TO_INT(cur->data);
4021 next = GPOINTER_TO_INT(cur->next-&