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>
56 #include "procheader.h"
57 #include "prefs_account.h"
62 #include "prefs_common.h"
63 #include "inputdialog.h"
65 #include "remotefolder.h"
66 #include "alertpanel.h"
68 #include "statusbar.h"
70 #include "imap-thread.h"
73 typedef struct _IMAPFolder IMAPFolder;
74 typedef struct _IMAPSession IMAPSession;
75 typedef struct _IMAPNameSpace IMAPNameSpace;
76 typedef struct _IMAPFolderItem IMAPFolderItem;
78 #include "prefs_account.h"
80 #define IMAP_FOLDER(obj) ((IMAPFolder *)obj)
81 #define IMAP_FOLDER_ITEM(obj) ((IMAPFolderItem *)obj)
82 #define IMAP_SESSION(obj) ((IMAPSession *)obj)
88 /* list of IMAPNameSpace */
92 gchar last_seen_separator;
100 gboolean authenticated;
109 gboolean folder_content_changed;
115 struct _IMAPNameSpace
121 #define IMAP_SUCCESS 0
122 #define IMAP_SOCKET 2
123 #define IMAP_AUTHFAIL 3
124 #define IMAP_PROTOCOL 4
125 #define IMAP_SYNTAX 5
129 #define IMAPBUFSIZE 8192
133 IMAP_FLAG_SEEN = 1 << 0,
134 IMAP_FLAG_ANSWERED = 1 << 1,
135 IMAP_FLAG_FLAGGED = 1 << 2,
136 IMAP_FLAG_DELETED = 1 << 3,
137 IMAP_FLAG_DRAFT = 1 << 4
140 #define IMAP_IS_SEEN(flags) ((flags & IMAP_FLAG_SEEN) != 0)
141 #define IMAP_IS_ANSWERED(flags) ((flags & IMAP_FLAG_ANSWERED) != 0)
142 #define IMAP_IS_FLAGGED(flags) ((flags & IMAP_FLAG_FLAGGED) != 0)
143 #define IMAP_IS_DELETED(flags) ((flags & IMAP_FLAG_DELETED) != 0)
144 #define IMAP_IS_DRAFT(flags) ((flags & IMAP_FLAG_DRAFT) != 0)
147 #define IMAP4_PORT 143
149 #define IMAPS_PORT 993
152 #define IMAP_CMD_LIMIT 1000
154 struct _IMAPFolderItem
166 guint32 c_uid_validity;
169 GHashTable *flags_set_table;
170 GHashTable *flags_unset_table;
173 static XMLTag *imap_item_get_xml(Folder *folder, FolderItem *item);
174 static void imap_item_set_xml(Folder *folder, FolderItem *item, XMLTag *tag);
176 static void imap_folder_init (Folder *folder,
180 static Folder *imap_folder_new (const gchar *name,
182 static void imap_folder_destroy (Folder *folder);
184 static IMAPSession *imap_session_new (Folder *folder,
185 const PrefsAccount *account);
186 static void imap_session_authenticate(IMAPSession *session,
187 const PrefsAccount *account);
188 static void imap_session_destroy (Session *session);
190 static gchar *imap_fetch_msg (Folder *folder,
193 static gchar *imap_fetch_msg_full (Folder *folder,
198 static gint imap_add_msg (Folder *folder,
202 static gint imap_add_msgs (Folder *folder,
205 GRelation *relation);
207 static gint imap_copy_msg (Folder *folder,
210 static gint imap_copy_msgs (Folder *folder,
212 MsgInfoList *msglist,
213 GRelation *relation);
215 static gint imap_remove_msg (Folder *folder,
218 static gint imap_remove_msgs (Folder *folder,
220 MsgInfoList *msglist,
221 GRelation *relation);
222 static gint imap_remove_all_msg (Folder *folder,
225 static gboolean imap_is_msg_changed (Folder *folder,
229 static gint imap_close (Folder *folder,
232 static gint imap_scan_tree (Folder *folder);
234 static gint imap_create_tree (Folder *folder);
236 static FolderItem *imap_create_folder (Folder *folder,
239 static gint imap_rename_folder (Folder *folder,
242 static gint imap_remove_folder (Folder *folder,
245 static FolderItem *imap_folder_item_new (Folder *folder);
246 static void imap_folder_item_destroy (Folder *folder,
249 static IMAPSession *imap_session_get (Folder *folder);
251 static gint imap_auth (IMAPSession *session,
256 static gint imap_scan_tree_recursive (IMAPSession *session,
259 static void imap_create_missing_folders (Folder *folder);
260 static FolderItem *imap_create_special_folder
262 SpecialFolderItemType stype,
265 static gint imap_do_copy_msgs (Folder *folder,
267 MsgInfoList *msglist,
268 GRelation *relation);
270 static void imap_delete_all_cached_messages (FolderItem *item);
271 static void imap_set_batch (Folder *folder,
274 static gint imap_set_message_flags (IMAPSession *session,
275 MsgNumberList *numlist,
278 static gint imap_select (IMAPSession *session,
284 guint32 *uid_validity,
286 static gint imap_status (IMAPSession *session,
289 IMAPFolderItem *item,
292 guint32 *uid_validity,
296 static IMAPNameSpace *imap_find_namespace (IMAPFolder *folder,
298 static gchar imap_get_path_separator (IMAPFolder *folder,
300 static gchar *imap_get_real_path (IMAPFolder *folder,
302 static void imap_synchronise (FolderItem *item);
304 static void imap_free_capabilities (IMAPSession *session);
306 /* low-level IMAP4rev1 commands */
307 static gint imap_cmd_login (IMAPSession *session,
311 static gint imap_cmd_logout (IMAPSession *session);
312 static gint imap_cmd_noop (IMAPSession *session);
314 static gint imap_cmd_starttls (IMAPSession *session);
316 static gint imap_cmd_select (IMAPSession *session,
321 guint32 *uid_validity,
323 static gint imap_cmd_examine (IMAPSession *session,
328 guint32 *uid_validity,
330 static gint imap_cmd_create (IMAPSession *sock,
331 const gchar *folder);
332 static gint imap_cmd_rename (IMAPSession *sock,
333 const gchar *oldfolder,
334 const gchar *newfolder);
335 static gint imap_cmd_delete (IMAPSession *session,
336 const gchar *folder);
337 static gint imap_cmd_fetch (IMAPSession *sock,
339 const gchar *filename,
342 static gint imap_cmd_append (IMAPSession *session,
343 const gchar *destfolder,
347 static gint imap_cmd_copy (IMAPSession *session,
348 struct mailimap_set * set,
349 const gchar *destfolder,
350 GRelation *uid_mapping);
351 static gint imap_cmd_store (IMAPSession *session,
352 struct mailimap_set * set,
355 static gint imap_cmd_expunge (IMAPSession *session);
357 static void imap_path_separator_subst (gchar *str,
360 static gchar *imap_utf8_to_modified_utf7 (const gchar *from);
361 static gchar *imap_modified_utf7_to_utf8 (const gchar *mutf7_str);
363 static gboolean imap_rename_folder_func (GNode *node,
365 static gint imap_get_num_list (Folder *folder,
368 gboolean *old_uids_valid);
369 static GSList *imap_get_msginfos (Folder *folder,
371 GSList *msgnum_list);
372 static MsgInfo *imap_get_msginfo (Folder *folder,
375 static gboolean imap_scan_required (Folder *folder,
377 static void imap_change_flags (Folder *folder,
380 MsgPermFlags newflags);
381 static gint imap_get_flags (Folder *folder,
383 MsgInfoList *msglist,
384 GRelation *msgflags);
385 static gchar *imap_folder_get_path (Folder *folder);
386 static gchar *imap_item_get_path (Folder *folder,
388 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item);
391 /* data types conversion libetpan <-> sylpheed */
392 static GSList * imap_list_from_lep(IMAPFolder * folder,
393 clist * list, const gchar * real_path, gboolean all);
394 static GSList * imap_get_lep_set_from_numlist(MsgNumberList *numlist);
395 static GSList * imap_get_lep_set_from_msglist(MsgInfoList *msglist);
396 static GSList * imap_uid_list_from_lep(clist * list);
397 static GSList * imap_uid_list_from_lep_tab(carray * list);
398 static MsgInfo *imap_envelope_from_lep(struct imap_fetch_env_info * info,
400 static void imap_lep_set_free(GSList *seq_list);
401 static struct mailimap_flag_list * imap_flag_to_lep(IMAPFlags flags);
403 typedef struct _hashtable_data {
404 IMAPSession *session;
406 IMAPFolderItem *item;
409 static FolderClass imap_class;
411 typedef struct _thread_data {
421 FolderClass *imap_get_class(void)
423 if (imap_class.idstr == NULL) {
424 imap_class.type = F_IMAP;
425 imap_class.idstr = "imap";
426 imap_class.uistr = "IMAP4";
428 /* Folder functions */
429 imap_class.new_folder = imap_folder_new;
430 imap_class.destroy_folder = imap_folder_destroy;
431 imap_class.scan_tree = imap_scan_tree;
432 imap_class.create_tree = imap_create_tree;
434 /* FolderItem functions */
435 imap_class.item_new = imap_folder_item_new;
436 imap_class.item_destroy = imap_folder_item_destroy;
437 imap_class.item_get_path = imap_item_get_path;
438 imap_class.create_folder = imap_create_folder;
439 imap_class.rename_folder = imap_rename_folder;
440 imap_class.remove_folder = imap_remove_folder;
441 imap_class.close = imap_close;
442 imap_class.get_num_list = imap_get_num_list;
443 imap_class.scan_required = imap_scan_required;
444 imap_class.set_xml = folder_set_xml;
445 imap_class.get_xml = folder_get_xml;
446 imap_class.item_set_xml = imap_item_set_xml;
447 imap_class.item_get_xml = imap_item_get_xml;
449 /* Message functions */
450 imap_class.get_msginfo = imap_get_msginfo;
451 imap_class.get_msginfos = imap_get_msginfos;
452 imap_class.fetch_msg = imap_fetch_msg;
453 imap_class.fetch_msg_full = imap_fetch_msg_full;
454 imap_class.add_msg = imap_add_msg;
455 imap_class.add_msgs = imap_add_msgs;
456 imap_class.copy_msg = imap_copy_msg;
457 imap_class.copy_msgs = imap_copy_msgs;
458 imap_class.remove_msg = imap_remove_msg;
459 imap_class.remove_msgs = imap_remove_msgs;
460 imap_class.remove_all_msg = imap_remove_all_msg;
461 imap_class.is_msg_changed = imap_is_msg_changed;
462 imap_class.change_flags = imap_change_flags;
463 imap_class.get_flags = imap_get_flags;
464 imap_class.set_batch = imap_set_batch;
465 imap_class.synchronise = imap_synchronise;
467 pthread_mutex_init(&imap_mutex, NULL);
474 static Folder *imap_folder_new(const gchar *name, const gchar *path)
478 folder = (Folder *)g_new0(IMAPFolder, 1);
479 folder->klass = &imap_class;
480 imap_folder_init(folder, name, path);
485 static void imap_folder_destroy(Folder *folder)
487 while (imap_folder_get_refcnt(folder) > 0)
488 gtk_main_iteration();
490 folder_remote_folder_destroy(REMOTE_FOLDER(folder));
494 static void imap_folder_init(Folder *folder, const gchar *name,
497 folder_remote_folder_init((Folder *)folder, name, path);
500 static FolderItem *imap_folder_item_new(Folder *folder)
502 IMAPFolderItem *item;
504 item = g_new0(IMAPFolderItem, 1);
507 item->uid_list = NULL;
509 return (FolderItem *)item;
512 static void imap_folder_item_destroy(Folder *folder, FolderItem *_item)
514 IMAPFolderItem *item = (IMAPFolderItem *)_item;
516 g_return_if_fail(item != NULL);
517 g_slist_free(item->uid_list);
522 static gboolean imap_reset_uid_lists_func(GNode *node, gpointer data)
524 IMAPFolderItem *item = (IMAPFolderItem *)node->data;
527 g_slist_free(item->uid_list);
528 item->uid_list = NULL;
533 static void imap_reset_uid_lists(Folder *folder)
535 if(folder->node == NULL)
538 /* Destroy all uid lists and rest last uid */
539 g_node_traverse(folder->node, G_IN_ORDER, G_TRAVERSE_ALL, -1, imap_reset_uid_lists_func, NULL);
542 void imap_get_capabilities(IMAPSession *session)
544 struct mailimap_capability_data *capabilities = NULL;
547 if (session->capability != NULL)
550 capabilities = imap_threaded_capability(session->folder);
552 if (capabilities == NULL)
555 for(cur = clist_begin(capabilities->cap_list) ; cur != NULL ;
556 cur = clist_next(cur)) {
557 struct mailimap_capability * cap =
559 if (!cap || cap->cap_data.cap_name == NULL)
561 session->capability = g_slist_append
562 (session->capability,
563 g_strdup(cap->cap_data.cap_name));
564 debug_print("got capa %s\n", cap->cap_data.cap_name);
566 mailimap_capability_data_free(capabilities);
569 gboolean imap_has_capability(IMAPSession *session, const gchar *cap)
572 for (cur = session->capability; cur; cur = cur->next) {
573 if (!g_ascii_strcasecmp(cur->data, cap))
579 static gint imap_auth(IMAPSession *session, const gchar *user, const gchar *pass,
582 gint ok = IMAP_ERROR;
583 static time_t last_login_err = 0;
585 imap_get_capabilities(session);
589 ok = imap_cmd_login(session, user, pass, "ANONYMOUS");
591 case IMAP_AUTH_CRAM_MD5:
592 ok = imap_cmd_login(session, user, pass, "CRAM-MD5");
594 case IMAP_AUTH_LOGIN:
595 ok = imap_cmd_login(session, user, pass, "LOGIN");
598 debug_print("capabilities:\n"
602 imap_has_capability(session, "ANONYMOUS"),
603 imap_has_capability(session, "CRAM-MD5"),
604 imap_has_capability(session, "LOGIN"));
605 if (imap_has_capability(session, "CRAM-MD5"))
606 ok = imap_cmd_login(session, user, pass, "CRAM-MD5");
607 if (ok == IMAP_ERROR) /* we always try LOGIN before giving up */
608 ok = imap_cmd_login(session, user, pass, "LOGIN");
610 if (ok == IMAP_SUCCESS)
611 session->authenticated = TRUE;
613 gchar *ext_info = NULL;
615 if (type == IMAP_AUTH_CRAM_MD5) {
616 ext_info = _("\n\nCRAM-MD5 logins only work if libetpan has been "
617 "compiled with SASL support and the "
618 "CRAM-MD5 SASL plugin is installed.");
623 if (time(NULL) - last_login_err > 10) {
624 if (!prefs_common.no_recv_err_panel) {
625 alertpanel_error(_("Connection to %s failed: "
627 SESSION(session)->server, ext_info);
629 log_error(_("Connection to %s failed: "
630 "login refused.%s\n"),
631 SESSION(session)->server, ext_info);
634 last_login_err = time(NULL);
639 static IMAPSession *imap_reconnect_if_possible(Folder *folder, IMAPSession *session)
641 RemoteFolder *rfolder = REMOTE_FOLDER(folder);
642 /* Check if this is the first try to establish a
643 connection, if yes we don't try to reconnect */
644 debug_print("reconnecting\n");
645 if (rfolder->session == NULL) {
646 log_warning(_("Connecting to %s failed"),
647 folder->account->recv_server);
648 session_destroy(SESSION(session));
651 log_warning(_("IMAP4 connection to %s has been"
652 " disconnected. Reconnecting...\n"),
653 folder->account->recv_server);
654 statusbar_print_all(_("IMAP4 connection to %s has been"
655 " disconnected. Reconnecting...\n"),
656 folder->account->recv_server);
657 SESSION(session)->state = SESSION_DISCONNECTED;
658 session_destroy(SESSION(session));
659 /* Clear folders session to make imap_session_get create
660 a new session, because of rfolder->session == NULL
661 it will not try to reconnect again and so avoid an
663 rfolder->session = NULL;
664 session = imap_session_get(folder);
665 rfolder->session = SESSION(session);
671 #define lock_session() {\
672 debug_print("locking session\n"); \
673 session->busy = TRUE;\
676 #define unlock_session() {\
677 debug_print("unlocking session\n"); \
678 session->busy = FALSE;\
681 static IMAPSession *imap_session_get(Folder *folder)
683 RemoteFolder *rfolder = REMOTE_FOLDER(folder);
684 IMAPSession *session = NULL;
685 static time_t last_failure = 0;
687 g_return_val_if_fail(folder != NULL, NULL);
688 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, NULL);
689 g_return_val_if_fail(folder->account != NULL, NULL);
691 if (prefs_common.work_offline &&
692 !inc_offline_should_override(
693 _("Sylpheed-Claws needs network access in order "
694 "to access the IMAP server."))) {
698 /* Make sure we have a session */
699 if (rfolder->session != NULL) {
700 session = IMAP_SESSION(rfolder->session);
701 /* don't do that yet...
706 imap_reset_uid_lists(folder);
707 if (time(NULL) - last_failure <= 2)
709 session = imap_session_new(folder, folder->account);
711 if(session == NULL) {
712 last_failure = time(NULL);
716 /* Make sure session is authenticated */
717 if (!IMAP_SESSION(session)->authenticated)
718 imap_session_authenticate(IMAP_SESSION(session), folder->account);
720 if (!IMAP_SESSION(session)->authenticated) {
721 session_destroy(SESSION(session));
722 rfolder->session = NULL;
723 last_failure = time(NULL);
727 /* I think the point of this code is to avoid sending a
728 * keepalive if we've used the session recently and therefore
729 * think it's still alive. Unfortunately, most of the code
730 * does not yet check for errors on the socket, and so if the
731 * connection drops we don't notice until the timeout expires.
732 * A better solution than sending a NOOP every time would be
733 * for every command to be prepared to retry until it is
734 * successfully sent. -- mbp */
735 if (time(NULL) - SESSION(session)->last_access_time > SESSION_TIMEOUT_INTERVAL) {
736 /* verify that the session is still alive */
737 if (imap_cmd_noop(session) != IMAP_SUCCESS) {
738 debug_print("disconnected!\n");
739 session = imap_reconnect_if_possible(folder, session);
743 rfolder->session = SESSION(session);
745 return IMAP_SESSION(session);
748 static IMAPSession *imap_session_new(Folder * folder,
749 const PrefsAccount *account)
751 IMAPSession *session;
757 /* FIXME: IMAP over SSL only... */
760 port = account->set_imapport ? account->imapport
761 : account->ssl_imap == SSL_TUNNEL ? IMAPS_PORT : IMAP4_PORT;
762 ssl_type = account->ssl_imap;
764 if (account->ssl_imap != SSL_NONE) {
765 if (alertpanel_full(_("Insecure connection"),
766 _("This connection is configured to be secured "
767 "using SSL, but SSL is not available in this "
768 "build of Sylpheed-Claws. \n\n"
769 "Do you want to continue connecting to this "
770 "server? The communication would not be "
772 GTK_STOCK_CANCEL, _("Con_tinue connecting"),
773 NULL, FALSE, NULL, ALERT_WARNING,
774 G_ALERTDEFAULT) != G_ALERTALTERNATE)
777 port = account->set_imapport ? account->imapport
782 statusbar_print_all(_("Connecting to IMAP4 server: %s..."), folder->account->recv_server);
783 if (account->set_tunnelcmd) {
784 r = imap_threaded_connect_cmd(folder,
786 account->recv_server,
791 if (ssl_type == SSL_TUNNEL) {
792 r = imap_threaded_connect_ssl(folder,
793 account->recv_server,
799 r = imap_threaded_connect(folder,
800 account->recv_server,
806 if (r == MAILIMAP_NO_ERROR_AUTHENTICATED) {
807 authenticated = TRUE;
809 else if (r == MAILIMAP_NO_ERROR_NON_AUTHENTICATED) {
810 authenticated = FALSE;
813 if(!prefs_common.no_recv_err_panel) {
814 alertpanel_error(_("Can't connect to IMAP4 server: %s:%d"),
815 account->recv_server, port);
817 log_error(_("Can't connect to IMAP4 server: %s:%d\n"),
818 account->recv_server, port);
824 session = g_new0(IMAPSession, 1);
825 session_init(SESSION(session));
826 SESSION(session)->type = SESSION_IMAP;
827 SESSION(session)->server = g_strdup(account->recv_server);
828 SESSION(session)->sock = NULL;
830 SESSION(session)->destroy = imap_session_destroy;
832 session->capability = NULL;
834 session->authenticated = authenticated;
835 session->mbox = NULL;
836 session->cmd_count = 0;
837 session->folder = folder;
838 IMAP_FOLDER(session->folder)->last_seen_separator = 0;
841 if (account->ssl_imap == SSL_STARTTLS) {
844 ok = imap_cmd_starttls(session);
845 if (ok != IMAP_SUCCESS) {
846 log_warning(_("Can't start TLS session.\n"));
847 session_destroy(SESSION(session));
851 imap_free_capabilities(session);
852 session->authenticated = FALSE;
853 session->uidplus = FALSE;
854 session->cmd_count = 1;
857 log_message("IMAP connection is %s-authenticated\n",
858 (session->authenticated) ? "pre" : "un");
863 static void imap_session_authenticate(IMAPSession *session,
864 const PrefsAccount *account)
866 gchar *pass, *acc_pass;
867 gboolean failed = FALSE;
869 g_return_if_fail(account->userid != NULL);
870 acc_pass = account->passwd;
873 if (!pass && account->imap_auth_type != IMAP_AUTH_ANON) {
875 tmp_pass = input_dialog_query_password(account->recv_server, account->userid);
878 Xstrdup_a(pass, tmp_pass, {g_free(tmp_pass); return;});
880 } else if (account->imap_auth_type == IMAP_AUTH_ANON) {
883 statusbar_print_all(_("Connecting to IMAP4 server %s...\n"),
884 account->recv_server);
885 if (imap_auth(session, account->userid, pass, account->imap_auth_type) != IMAP_SUCCESS) {
893 imap_threaded_disconnect(session->folder);
894 imap_cmd_logout(session);
895 alertpanel_error(_("Couldn't login to IMAP server %s."), account->recv_server);
902 session->authenticated = TRUE;
905 static void imap_session_destroy(Session *session)
907 if (session->state != SESSION_DISCONNECTED)
908 imap_threaded_disconnect(IMAP_SESSION(session)->folder);
910 imap_free_capabilities(IMAP_SESSION(session));
911 g_free(IMAP_SESSION(session)->mbox);
912 sock_close(session->sock);
913 session->sock = NULL;
916 static gchar *imap_fetch_msg(Folder *folder, FolderItem *item, gint uid)
918 return imap_fetch_msg_full(folder, item, uid, TRUE, TRUE);
921 static guint get_size_with_crs(MsgInfo *info)
930 fp = procmsg_open_message(info);
934 while (fgets(buf, sizeof (buf), fp) != NULL) {
936 if (!strstr(buf, "\r") && strstr(buf, "\n"))
944 static gchar *imap_fetch_msg_full(Folder *folder, FolderItem *item, gint uid,
945 gboolean headers, gboolean body)
947 gchar *path, *filename;
948 IMAPSession *session;
951 g_return_val_if_fail(folder != NULL, NULL);
952 g_return_val_if_fail(item != NULL, NULL);
957 path = folder_item_get_path(item);
958 if (!is_dir_exist(path))
960 filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(uid), NULL);
962 debug_print("trying to fetch cached %s\n", filename);
963 if (is_file_exist(filename)) {
964 /* see whether the local file represents the whole message
965 * or not. As the IMAP server reports size with \r chars,
966 * we have to update the local file (UNIX \n only) size */
967 MsgInfo *msginfo = imap_parse_msg(filename, item);
968 MsgInfo *cached = msgcache_get_msg(item->cache,uid);
969 guint have_size = get_size_with_crs(msginfo);
972 debug_print("message %d has been already %scached (%d/%d).\n", uid,
973 have_size >= cached->size ? "fully ":"",
974 have_size, (int)cached->size);
976 if (cached && (cached->size <= have_size || !body)) {
977 procmsg_msginfo_free(cached);
978 procmsg_msginfo_free(msginfo);
979 file_strip_crs(filename);
981 } else if (!cached) {
982 debug_print("message not cached, considering file complete\n");
983 procmsg_msginfo_free(msginfo);
984 file_strip_crs(filename);
987 procmsg_msginfo_free(cached);
988 procmsg_msginfo_free(msginfo);
992 session = imap_session_get(folder);
1001 debug_print("IMAP fetching messages\n");
1002 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
1003 NULL, NULL, NULL, NULL, FALSE);
1004 if (ok != IMAP_SUCCESS) {
1005 g_warning("can't select mailbox %s\n", item->path);
1011 debug_print("getting message %d...\n", uid);
1012 ok = imap_cmd_fetch(session, (guint32)uid, filename, headers, body);
1014 if (ok != IMAP_SUCCESS) {
1015 g_warning("can't fetch message %d\n", uid);
1022 file_strip_crs(filename);
1026 static gint imap_add_msg(Folder *folder, FolderItem *dest,
1027 const gchar *file, MsgFlags *flags)
1031 MsgFileInfo fileinfo;
1033 g_return_val_if_fail(file != NULL, -1);
1035 fileinfo.msginfo = NULL;
1036 fileinfo.file = (gchar *)file;
1037 fileinfo.flags = flags;
1038 file_list.data = &fileinfo;
1039 file_list.next = NULL;
1041 ret = imap_add_msgs(folder, dest, &file_list, NULL);
1045 static gint imap_add_msgs(Folder *folder, FolderItem *dest, GSList *file_list,
1046 GRelation *relation)
1049 IMAPSession *session;
1050 guint32 last_uid = 0;
1052 MsgFileInfo *fileinfo;
1054 gint curnum = 0, total = 0;
1057 g_return_val_if_fail(folder != NULL, -1);
1058 g_return_val_if_fail(dest != NULL, -1);
1059 g_return_val_if_fail(file_list != NULL, -1);
1061 session = imap_session_get(folder);
1066 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
1068 statusbar_print_all(_("Adding messages..."));
1069 total = g_slist_length(file_list);
1070 for (cur = file_list; cur != NULL; cur = cur->next) {
1071 IMAPFlags iflags = 0;
1072 guint32 new_uid = 0;
1073 gchar *real_file = NULL;
1074 fileinfo = (MsgFileInfo *)cur->data;
1076 statusbar_progress_all(curnum, total, 1);
1079 if (fileinfo->flags) {
1080 if (MSG_IS_MARKED(*fileinfo->flags))
1081 iflags |= IMAP_FLAG_FLAGGED;
1082 if (MSG_IS_REPLIED(*fileinfo->flags))
1083 iflags |= IMAP_FLAG_ANSWERED;
1084 if (!MSG_IS_UNREAD(*fileinfo->flags))
1085 iflags |= IMAP_FLAG_SEEN;
1088 if (real_file == NULL)
1089 real_file = g_strdup(fileinfo->file);
1091 if (folder_has_parent_of_type(dest, F_QUEUE) ||
1092 folder_has_parent_of_type(dest, F_OUTBOX) ||
1093 folder_has_parent_of_type(dest, F_DRAFT) ||
1094 folder_has_parent_of_type(dest, F_TRASH))
1095 iflags |= IMAP_FLAG_SEEN;
1097 ok = imap_cmd_append(session, destdir, real_file, iflags,
1100 if (ok != IMAP_SUCCESS) {
1101 g_warning("can't append message %s\n", real_file);
1105 statusbar_progress_all(0,0,0);
1106 statusbar_pop_all();
1109 debug_print("appended new message as %d\n", new_uid);
1110 /* put the local file in the imapcache, so that we don't
1111 * have to fetch it back later. */
1113 gchar *cache_path = folder_item_get_path(dest);
1114 if (!is_dir_exist(cache_path))
1115 make_dir_hier(cache_path);
1116 if (is_dir_exist(cache_path)) {
1117 gchar *cache_file = g_strconcat(
1118 cache_path, G_DIR_SEPARATOR_S,
1119 itos(new_uid), NULL);
1120 copy_file(real_file, cache_file, TRUE);
1121 debug_print("copied to cache: %s\n", cache_file);
1128 if (relation != NULL)
1129 g_relation_insert(relation, fileinfo->msginfo != NULL ?
1130 (gpointer) fileinfo->msginfo : (gpointer) fileinfo,
1131 GINT_TO_POINTER(dest->last_num + 1));
1133 new_uid = dest->last_num+1;
1135 if (last_uid < new_uid) {
1141 statusbar_progress_all(0,0,0);
1142 statusbar_pop_all();
1144 imap_cmd_expunge(session);
1152 static gint imap_do_copy_msgs(Folder *folder, FolderItem *dest,
1153 MsgInfoList *msglist, GRelation *relation)
1157 GSList *seq_list, *cur;
1159 IMAPSession *session;
1160 gint ok = IMAP_SUCCESS;
1161 GRelation *uid_mapping;
1164 g_return_val_if_fail(folder != NULL, -1);
1165 g_return_val_if_fail(dest != NULL, -1);
1166 g_return_val_if_fail(msglist != NULL, -1);
1168 session = imap_session_get(folder);
1174 msginfo = (MsgInfo *)msglist->data;
1176 src = msginfo->folder;
1178 g_warning("the src folder is identical to the dest.\n");
1183 if (src->folder != dest->folder) {
1184 GSList *infolist = NULL, *cur;
1186 for (cur = msglist; cur; cur = cur->next) {
1187 msginfo = (MsgInfo *)cur->data;
1188 MsgFileInfo *fileinfo = g_new0(MsgFileInfo, 1);
1189 fileinfo->file = procmsg_get_message_file(msginfo);
1190 fileinfo->flags = &(msginfo->flags);
1191 infolist = g_slist_prepend(infolist, fileinfo);
1193 infolist = g_slist_reverse(infolist);
1194 res = folder_item_add_msgs(dest, infolist, FALSE);
1195 for (cur = infolist; cur; cur = cur->next) {
1196 MsgFileInfo *info = (MsgFileInfo *)cur->data;
1200 g_slist_free(infolist);
1204 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
1205 NULL, NULL, NULL, NULL, FALSE);
1206 if (ok != IMAP_SUCCESS) {
1211 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
1212 seq_list = imap_get_lep_set_from_msglist(msglist);
1213 uid_mapping = g_relation_new(2);
1214 g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
1216 statusbar_print_all(_("Copying messages..."));
1217 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
1218 struct mailimap_set * seq_set;
1219 seq_set = cur->data;
1221 debug_print("Copying messages from %s to %s ...\n",
1222 src->path, destdir);
1224 ok = imap_cmd_copy(session, seq_set, destdir, uid_mapping);
1225 if (ok != IMAP_SUCCESS) {
1226 g_relation_destroy(uid_mapping);
1227 imap_lep_set_free(seq_list);
1233 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
1234 MsgInfo *msginfo = (MsgInfo *)cur->data;
1237 tuples = g_relation_select(uid_mapping,
1238 GINT_TO_POINTER(msginfo->msgnum),
1240 if (tuples->len > 0) {
1241 gint num = GPOINTER_TO_INT(g_tuples_index(tuples, 0, 1));
1242 g_relation_insert(relation, msginfo,
1243 GPOINTER_TO_INT(num));
1247 g_relation_insert(relation, msginfo,
1248 GPOINTER_TO_INT(0));
1249 g_tuples_destroy(tuples);
1251 statusbar_pop_all();
1253 g_relation_destroy(uid_mapping);
1254 imap_lep_set_free(seq_list);
1258 IMAP_FOLDER_ITEM(dest)->lastuid = 0;
1259 IMAP_FOLDER_ITEM(dest)->uid_next = 0;
1260 g_slist_free(IMAP_FOLDER_ITEM(dest)->uid_list);
1261 IMAP_FOLDER_ITEM(dest)->uid_list = NULL;
1264 if (ok == IMAP_SUCCESS)
1270 static gint imap_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1274 g_return_val_if_fail(msginfo != NULL, -1);
1276 msglist.data = msginfo;
1277 msglist.next = NULL;
1279 return imap_copy_msgs(folder, dest, &msglist, NULL);
1282 static gint imap_copy_msgs(Folder *folder, FolderItem *dest,
1283 MsgInfoList *msglist, GRelation *relation)
1288 g_return_val_if_fail(folder != NULL, -1);
1289 g_return_val_if_fail(dest != NULL, -1);
1290 g_return_val_if_fail(msglist != NULL, -1);
1292 msginfo = (MsgInfo *)msglist->data;
1293 g_return_val_if_fail(msginfo->folder != NULL, -1);
1295 ret = imap_do_copy_msgs(folder, dest, msglist, relation);
1300 static gint imap_do_remove_msgs(Folder *folder, FolderItem *dest,
1301 MsgInfoList *msglist, GRelation *relation)
1303 gchar *destdir, *dir;
1304 GSList *numlist = NULL, *cur;
1306 IMAPSession *session;
1307 gint ok = IMAP_SUCCESS;
1308 GRelation *uid_mapping;
1310 g_return_val_if_fail(folder != NULL, -1);
1311 g_return_val_if_fail(dest != NULL, -1);
1312 g_return_val_if_fail(msglist != NULL, -1);
1314 session = imap_session_get(folder);
1319 msginfo = (MsgInfo *)msglist->data;
1321 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
1322 NULL, NULL, NULL, NULL, FALSE);
1323 if (ok != IMAP_SUCCESS) {
1328 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
1329 for (cur = msglist; cur; cur = cur->next) {
1330 msginfo = (MsgInfo *)cur->data;
1331 if (!MSG_IS_DELETED(msginfo->flags))
1332 numlist = g_slist_prepend(numlist, GINT_TO_POINTER(msginfo->msgnum));
1334 numlist = g_slist_reverse(numlist);
1336 uid_mapping = g_relation_new(2);
1337 g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
1339 ok = imap_set_message_flags
1340 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
1341 numlist, IMAP_FLAG_DELETED, TRUE);
1342 if (ok != IMAP_SUCCESS) {
1343 log_warning(_("can't set deleted flags\n"));
1347 ok = imap_cmd_expunge(session);
1348 if (ok != IMAP_SUCCESS) {
1349 log_warning(_("can't expunge\n"));
1354 dir = folder_item_get_path(msginfo->folder);
1355 if (is_dir_exist(dir)) {
1356 for (cur = msglist; cur; cur = cur->next) {
1357 msginfo = (MsgInfo *)cur->data;
1358 remove_numbered_files(dir, msginfo->msgnum, msginfo->msgnum);
1363 g_relation_destroy(uid_mapping);
1364 g_slist_free(numlist);
1368 if (ok == IMAP_SUCCESS)
1374 static gint imap_remove_msgs(Folder *folder, FolderItem *dest,
1375 MsgInfoList *msglist, GRelation *relation)
1379 g_return_val_if_fail(folder != NULL, -1);
1380 g_return_val_if_fail(dest != NULL, -1);
1381 if (msglist == NULL)
1384 msginfo = (MsgInfo *)msglist->data;
1385 g_return_val_if_fail(msginfo->folder != NULL, -1);
1387 return imap_do_remove_msgs(folder, dest, msglist, relation);
1390 static gint imap_remove_all_msg(Folder *folder, FolderItem *item)
1392 GSList *list = folder_item_get_msg_list(item);
1393 gint res = imap_remove_msgs(folder, item, list, NULL);
1394 procmsg_msg_list_free(list);
1398 static gboolean imap_is_msg_changed(Folder *folder, FolderItem *item,
1401 /* TODO: properly implement this method */
1405 static gint imap_close(Folder *folder, FolderItem *item)
1410 static gint imap_scan_tree(Folder *folder)
1412 FolderItem *item = NULL;
1413 IMAPSession *session;
1414 gchar *root_folder = NULL;
1416 g_return_val_if_fail(folder != NULL, -1);
1417 g_return_val_if_fail(folder->account != NULL, -1);
1419 session = imap_session_get(folder);
1421 if (!folder->node) {
1422 folder_tree_destroy(folder);
1423 item = folder_item_new(folder, folder->name, NULL);
1424 item->folder = folder;
1425 folder->node = item->node = g_node_new(item);
1431 if (folder->account->imap_dir && *folder->account->imap_dir) {
1436 Xstrdup_a(root_folder, folder->account->imap_dir, {unlock_session();return -1;});
1437 extract_quote(root_folder, '"');
1438 subst_char(root_folder,
1439 imap_get_path_separator(IMAP_FOLDER(folder),
1442 strtailchomp(root_folder, '/');
1443 real_path = imap_get_real_path
1444 (IMAP_FOLDER(folder), root_folder);
1445 debug_print("IMAP root directory: %s\n", real_path);
1447 /* check if root directory exist */
1449 r = imap_threaded_list(session->folder, "", real_path,
1451 if ((r != MAILIMAP_NO_ERROR) || (clist_count(lep_list) == 0)) {
1452 if (!folder->node) {
1453 item = folder_item_new(folder, folder->name, NULL);
1454 item->folder = folder;
1455 folder->node = item->node = g_node_new(item);
1460 mailimap_list_result_free(lep_list);
1466 item = FOLDER_ITEM(folder->node->data);
1467 if (!item || ((item->path || root_folder) &&
1468 strcmp2(item->path, root_folder) != 0)) {
1469 folder_tree_destroy(folder);
1470 item = folder_item_new(folder, folder->name, root_folder);
1471 item->folder = folder;
1472 folder->node = item->node = g_node_new(item);
1475 imap_scan_tree_recursive(session, FOLDER_ITEM(folder->node->data));
1476 imap_create_missing_folders(folder);
1482 static gint imap_scan_tree_recursive(IMAPSession *session, FolderItem *item)
1485 IMAPFolder *imapfolder;
1486 FolderItem *new_item;
1487 GSList *item_list, *cur;
1490 gchar *wildcard_path;
1496 g_return_val_if_fail(item != NULL, -1);
1497 g_return_val_if_fail(item->folder != NULL, -1);
1498 g_return_val_if_fail(item->no_sub == FALSE, -1);
1500 folder = item->folder;
1501 imapfolder = IMAP_FOLDER(folder);
1503 separator = imap_get_path_separator(imapfolder, item->path);
1505 if (folder->ui_func)
1506 folder->ui_func(folder, item, folder->ui_func_data);
1509 wildcard[0] = separator;
1512 real_path = imap_get_real_path(imapfolder, item->path);
1516 real_path = g_strdup("");
1519 Xstrcat_a(wildcard_path, real_path, wildcard,
1520 {g_free(real_path); return IMAP_ERROR;});
1522 r = imap_threaded_list(folder, "", wildcard_path, &lep_list);
1523 if (r != MAILIMAP_NO_ERROR) {
1527 item_list = imap_list_from_lep(imapfolder,
1528 lep_list, real_path, FALSE);
1529 mailimap_list_result_free(lep_list);
1534 node = item->node->children;
1535 while (node != NULL) {
1536 FolderItem *old_item = FOLDER_ITEM(node->data);
1537 GNode *next = node->next;
1540 for (cur = item_list; cur != NULL; cur = cur->next) {
1541 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1542 if (!strcmp2(old_item->path, cur_item->path)) {
1543 new_item = cur_item;
1548 debug_print("folder '%s' not found. removing...\n",
1550 folder_item_remove(old_item);
1552 old_item->no_sub = new_item->no_sub;
1553 old_item->no_select = new_item->no_select;
1554 if (old_item->no_sub == TRUE && node->children) {
1555 debug_print("folder '%s' doesn't have "
1556 "subfolders. removing...\n",
1558 folder_item_remove_children(old_item);
1565 for (cur = item_list; cur != NULL; cur = cur->next) {
1566 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1569 for (node = item->node->children; node != NULL;
1570 node = node->next) {
1571 if (!strcmp2(FOLDER_ITEM(node->data)->path,
1573 new_item = FOLDER_ITEM(node->data);
1574 folder_item_destroy(cur_item);
1580 new_item = cur_item;
1581 debug_print("new folder '%s' found.\n", new_item->path);
1582 folder_item_append(item, new_item);
1585 if (!strcmp(new_item->path, "INBOX")) {
1586 new_item->stype = F_INBOX;
1587 folder->inbox = new_item;
1588 } else if (!folder_item_parent(item) || item->stype == F_INBOX) {
1591 base = g_path_get_basename(new_item->path);
1593 if (!folder->outbox && !g_ascii_strcasecmp(base, "Sent")) {
1594 new_item->stype = F_OUTBOX;
1595 folder->outbox = new_item;
1596 } else if (!folder->draft && !g_ascii_strcasecmp(base, "Drafts")) {
1597 new_item->stype = F_DRAFT;
1598 folder->draft = new_item;
1599 } else if (!folder->queue && !g_ascii_strcasecmp(base, "Queue")) {
1600 new_item->stype = F_QUEUE;
1601 folder->queue = new_item;
1602 } else if (!folder->trash && !g_ascii_strcasecmp(base, "Trash")) {
1603 new_item->stype = F_TRASH;
1604 folder->trash = new_item;
1609 if (new_item->no_sub == FALSE)
1610 imap_scan_tree_recursive(session, new_item);
1613 g_slist_free(item_list);
1615 return IMAP_SUCCESS;
1618 static gint imap_create_tree(Folder *folder)
1620 g_return_val_if_fail(folder != NULL, -1);
1621 g_return_val_if_fail(folder->node != NULL, -1);
1622 g_return_val_if_fail(folder->node->data != NULL, -1);
1623 g_return_val_if_fail(folder->account != NULL, -1);
1625 imap_scan_tree(folder);
1626 imap_create_missing_folders(folder);
1631 static void imap_create_missing_folders(Folder *folder)
1633 g_return_if_fail(folder != NULL);
1636 folder->inbox = imap_create_special_folder
1637 (folder, F_INBOX, "INBOX");
1639 folder->trash = imap_create_special_folder
1640 (folder, F_TRASH, "Trash");
1642 folder->queue = imap_create_special_folder
1643 (folder, F_QUEUE, "Queue");
1644 if (!folder->outbox)
1645 folder->outbox = imap_create_special_folder
1646 (folder, F_OUTBOX, "Sent");
1648 folder->draft = imap_create_special_folder
1649 (folder, F_DRAFT, "Drafts");
1652 static FolderItem *imap_create_special_folder(Folder *folder,
1653 SpecialFolderItemType stype,
1657 FolderItem *new_item;
1659 g_return_val_if_fail(folder != NULL, NULL);
1660 g_return_val_if_fail(folder->node != NULL, NULL);
1661 g_return_val_if_fail(folder->node->data != NULL, NULL);
1662 g_return_val_if_fail(folder->account != NULL, NULL);
1663 g_return_val_if_fail(name != NULL, NULL);
1665 item = FOLDER_ITEM(folder->node->data);
1666 new_item = imap_create_folder(folder, item, name);
1669 g_warning("Can't create '%s'\n", name);
1670 if (!folder->inbox) return NULL;
1672 new_item = imap_create_folder(folder, folder->inbox, name);
1674 g_warning("Can't create '%s' under INBOX\n", name);
1676 new_item->stype = stype;
1678 new_item->stype = stype;
1683 static gchar *imap_folder_get_path(Folder *folder)
1687 g_return_val_if_fail(folder != NULL, NULL);
1688 g_return_val_if_fail(folder->account != NULL, NULL);
1690 folder_path = g_strconcat(get_imap_cache_dir(),
1692 folder->account->recv_server,
1694 folder->account->userid,
1700 static gchar *imap_item_get_path(Folder *folder, FolderItem *item)
1702 gchar *folder_path, *path;
1704 g_return_val_if_fail(folder != NULL, NULL);
1705 g_return_val_if_fail(item != NULL, NULL);
1706 folder_path = imap_folder_get_path(folder);
1708 g_return_val_if_fail(folder_path != NULL, NULL);
1709 if (folder_path[0] == G_DIR_SEPARATOR) {
1711 path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
1714 path = g_strdup(folder_path);
1717 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1718 folder_path, G_DIR_SEPARATOR_S,
1721 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1724 g_free(folder_path);
1729 static FolderItem *imap_create_folder(Folder *folder, FolderItem *parent,
1732 gchar *dirpath, *imap_path;
1733 IMAPSession *session;
1734 FolderItem *new_item;
1739 gboolean no_select = FALSE, no_sub = FALSE;
1741 g_return_val_if_fail(folder != NULL, NULL);
1742 g_return_val_if_fail(folder->account != NULL, NULL);
1743 g_return_val_if_fail(parent != NULL, NULL);
1744 g_return_val_if_fail(name != NULL, NULL);
1746 session = imap_session_get(folder);
1752 if (!folder_item_parent(parent) && strcmp(name, "INBOX") == 0) {
1753 dirpath = g_strdup(name);
1754 }else if (parent->path)
1755 dirpath = g_strconcat(parent->path, "/", name, NULL);
1756 else if ((p = strchr(name, '/')) != NULL && *(p + 1) != '\0')
1757 dirpath = g_strdup(name);
1758 else if (folder->account->imap_dir && *folder->account->imap_dir) {
1761 Xstrdup_a(imap_dir, folder->account->imap_dir, {unlock_session();return NULL;});
1762 strtailchomp(imap_dir, '/');
1763 dirpath = g_strconcat(imap_dir, "/", name, NULL);
1765 dirpath = g_strdup(name);
1769 /* keep trailing directory separator to create a folder that contains
1771 imap_path = imap_utf8_to_modified_utf7(dirpath);
1773 strtailchomp(dirpath, '/');
1774 Xstrdup_a(new_name, name, {
1779 separator = imap_get_path_separator(IMAP_FOLDER(folder), imap_path);
1780 imap_path_separator_subst(imap_path, separator);
1781 /* remove trailing / for display */
1782 strtailchomp(new_name, '/');
1784 if (strcmp(dirpath, "INBOX") != 0) {
1786 gboolean exist = FALSE;
1790 argbuf = g_ptr_array_new();
1791 r = imap_threaded_list(folder, "", imap_path, &lep_list);
1792 if (r != MAILIMAP_NO_ERROR) {
1793 log_warning(_("can't create mailbox: LIST failed\n"));
1796 ptr_array_free_strings(argbuf);
1797 g_ptr_array_free(argbuf, TRUE);
1802 if (clist_count(lep_list) > 0)
1804 mailimap_list_result_free(lep_list);
1807 ok = imap_cmd_create(session, imap_path);
1808 if (ok != IMAP_SUCCESS) {
1809 log_warning(_("can't create mailbox\n"));
1815 r = imap_threaded_list(folder, "", imap_path, &lep_list);
1816 if (r == MAILIMAP_NO_ERROR) {
1817 GSList *item_list = imap_list_from_lep(IMAP_FOLDER(folder),
1818 lep_list, dirpath, TRUE);
1820 FolderItem *cur_item = FOLDER_ITEM(item_list->data);
1821 no_select = cur_item->no_select;
1822 no_sub = cur_item->no_sub;
1823 g_slist_free(item_list);
1825 mailimap_list_result_free(lep_list);
1832 /* just get flags */
1833 r = imap_threaded_list(folder, "", "INBOX", &lep_list);
1834 if (r == MAILIMAP_NO_ERROR) {
1835 GSList *item_list = imap_list_from_lep(IMAP_FOLDER(folder),
1836 lep_list, dirpath, TRUE);
1838 FolderItem *cur_item = FOLDER_ITEM(item_list->data);
1839 no_select = cur_item->no_select;
1840 no_sub = cur_item->no_sub;
1841 g_slist_free(item_list);
1843 mailimap_list_result_free(lep_list);
1847 new_item = folder_item_new(folder, new_name, dirpath);
1848 new_item->no_select = no_select;
1849 new_item->no_sub = no_sub;
1850 folder_item_append(parent, new_item);
1854 dirpath = folder_item_get_path(new_item);
1855 if (!is_dir_exist(dirpath))
1856 make_dir_hier(dirpath);
1862 static gint imap_rename_folder(Folder *folder, FolderItem *item,
1867 gchar *real_oldpath;
1868 gchar *real_newpath;
1870 gchar *old_cache_dir;
1871 gchar *new_cache_dir;
1872 IMAPSession *session;
1875 gint exists, recent, unseen;
1876 guint32 uid_validity;
1878 g_return_val_if_fail(folder != NULL, -1);
1879 g_return_val_if_fail(item != NULL, -1);
1880 g_return_val_if_fail(item->path != NULL, -1);
1881 g_return_val_if_fail(name != NULL, -1);
1883 session = imap_session_get(folder);
1889 if (strchr(name, imap_get_path_separator(IMAP_FOLDER(folder), item->path)) != NULL) {
1890 g_warning(_("New folder name must not contain the namespace "
1896 real_oldpath = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1898 g_free(session->mbox);
1899 session->mbox = NULL;
1900 ok = imap_cmd_examine(session, "INBOX",
1901 &exists, &recent, &unseen, &uid_validity, FALSE);
1902 if (ok != IMAP_SUCCESS) {
1903 g_free(real_oldpath);
1908 separator = imap_get_path_separator(IMAP_FOLDER(folder), item->path);
1909 if (strchr(item->path, G_DIR_SEPARATOR)) {
1910 dirpath = g_path_get_dirname(item->path);
1911 newpath = g_strconcat(dirpath, G_DIR_SEPARATOR_S, name, NULL);
1914 newpath = g_strdup(name);
1916 real_newpath = imap_utf8_to_modified_utf7(newpath);
1917 imap_path_separator_subst(real_newpath, separator);
1919 ok = imap_cmd_rename(session, real_oldpath, real_newpath);
1920 if (ok != IMAP_SUCCESS) {
1921 log_warning(_("can't rename mailbox: %s to %s\n"),
1922 real_oldpath, real_newpath);
1923 g_free(real_oldpath);
1925 g_free(real_newpath);
1931 item->name = g_strdup(name);
1933 old_cache_dir = folder_item_get_path(item);
1935 paths[0] = g_strdup(item->path);
1937 g_node_traverse(item->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1938 imap_rename_folder_func, paths);
1940 if (is_dir_exist(old_cache_dir)) {
1941 new_cache_dir = folder_item_get_path(item);
1942 if (rename(old_cache_dir, new_cache_dir) < 0) {
1943 FILE_OP_ERROR(old_cache_dir, "rename");
1945 g_free(new_cache_dir);
1948 g_free(old_cache_dir);
1951 g_free(real_oldpath);
1952 g_free(real_newpath);
1957 static gint imap_remove_folder_real(Folder *folder, FolderItem *item)
1960 IMAPSession *session;
1964 g_return_val_if_fail(folder != NULL, -1);
1965 g_return_val_if_fail(item != NULL, -1);
1966 g_return_val_if_fail(item->path != NULL, -1);
1968 session = imap_session_get(folder);
1973 path = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1975 ok = imap_cmd_delete(session, path);
1976 if (ok != IMAP_SUCCESS) {
1977 gchar *tmp = g_strdup_printf("%s%c", path,
1978 imap_get_path_separator(IMAP_FOLDER(folder), path));
1981 ok = imap_cmd_delete(session, path);
1984 if (ok != IMAP_SUCCESS) {
1985 log_warning(_("can't delete mailbox\n"));
1992 cache_dir = folder_item_get_path(item);
1993 if (is_dir_exist(cache_dir) && remove_dir_recursive(cache_dir) < 0)
1994 g_warning("can't remove directory '%s'\n", cache_dir);
1996 folder_item_remove(item);
2001 static gint imap_remove_folder(Folder *folder, FolderItem *item)
2005 g_return_val_if_fail(item != NULL, -1);
2006 g_return_val_if_fail(item->folder != NULL, -1);
2007 g_return_val_if_fail(item->node != NULL, -1);
2009 node = item->node->children;
2010 while (node != NULL) {
2012 if (imap_remove_folder(folder, FOLDER_ITEM(node->data)) < 0)
2016 debug_print("IMAP removing %s\n", item->path);
2018 if (imap_remove_all_msg(folder, item) < 0)
2020 return imap_remove_folder_real(folder, item);
2023 typedef struct _uncached_data {
2024 IMAPSession *session;
2026 MsgNumberList *numlist;
2032 static void *imap_get_uncached_messages_thread(void *data)
2034 uncached_data *stuff = (uncached_data *)data;
2035 IMAPSession *session = stuff->session;
2036 FolderItem *item = stuff->item;
2037 MsgNumberList *numlist = stuff->numlist;
2039 GSList *newlist = NULL;
2040 GSList *llast = NULL;
2041 GSList *seq_list, *cur;
2043 debug_print("uncached_messages\n");
2045 if (session == NULL || item == NULL || item->folder == NULL
2046 || FOLDER_CLASS(item->folder) != &imap_class) {
2051 seq_list = imap_get_lep_set_from_numlist(numlist);
2052 debug_print("get msgs info\n");
2053 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
2054 struct mailimap_set * imapset;
2060 imapset = cur->data;
2062 r = imap_threaded_fetch_env(session->folder,
2063 imapset, &env_list);
2064 if (r != MAILIMAP_NO_ERROR)
2067 session_set_access_time(SESSION(session));
2070 for(i = 0 ; i < carray_count(env_list) ; i ++) {
2071 struct imap_fetch_env_info * info;
2074 info = carray_get(env_list, i);
2075 msginfo = imap_envelope_from_lep(info, item);
2076 if (msginfo == NULL)
2078 msginfo->folder = item;
2080 llast = newlist = g_slist_append(newlist, msginfo);
2082 llast = g_slist_append(llast, msginfo);
2083 llast = llast->next;
2088 imap_fetch_env_free(env_list);
2091 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
2092 struct mailimap_set * imapset;
2094 imapset = cur->data;
2095 mailimap_set_free(imapset);
2098 session_set_access_time(SESSION(session));
2103 #define MAX_MSG_NUM 50
2105 static GSList *imap_get_uncached_messages(IMAPSession *session,
2107 MsgNumberList *numlist)
2109 GSList *result = NULL;
2111 uncached_data *data = g_new0(uncached_data, 1);
2116 data->total = g_slist_length(numlist);
2117 debug_print("messages list : %i\n", data->total);
2119 while (cur != NULL) {
2120 GSList * partial_result;
2128 while (count < MAX_MSG_NUM) {
2133 if (newlist == NULL)
2134 llast = newlist = g_slist_append(newlist, p);
2136 llast = g_slist_append(llast, p);
2137 llast = llast->next;
2147 data->session = session;
2149 data->numlist = newlist;
2152 if (prefs_common.work_offline &&
2153 !inc_offline_should_override(
2154 _("Sylpheed-Claws needs network access in order "
2155 "to access the IMAP server."))) {
2161 (GSList *)imap_get_uncached_messages_thread(data);
2163 statusbar_progress_all(data->cur,data->total, 1);
2165 g_slist_free(newlist);
2167 result = g_slist_concat(result, partial_result);
2171 statusbar_progress_all(0,0,0);
2172 statusbar_pop_all();
2177 static void imap_delete_all_cached_messages(FolderItem *item)
2181 g_return_if_fail(item != NULL);
2182 g_return_if_fail(item->folder != NULL);
2183 g_return_if_fail(FOLDER_CLASS(item->folder) == &imap_class);
2185 debug_print("Deleting all cached messages...\n");
2187 dir = folder_item_get_path(item);
2188 if (is_dir_exist(dir))
2189 remove_all_numbered_files(dir);
2192 debug_print("done.\n");
2195 static IMAPNameSpace *imap_find_namespace_from_list(GList *ns_list,
2198 IMAPNameSpace *namespace = NULL;
2199 gchar *tmp_path, *name;
2201 if (!path) path = "";
2203 for (; ns_list != NULL; ns_list = ns_list->next) {
2204 IMAPNameSpace *tmp_ns = ns_list->data;
2206 Xstrcat_a(tmp_path, path, "/", return namespace);
2207 Xstrdup_a(name, tmp_ns->name, return namespace);
2208 if (tmp_ns->separator && tmp_ns->separator != '/') {
2209 subst_char(tmp_path, tmp_ns->separator, '/');
2210 subst_char(name, tmp_ns->separator, '/');
2212 if (strncmp(tmp_path, name, strlen(name)) == 0)
2219 static IMAPNameSpace *imap_find_namespace(IMAPFolder *folder,
2222 IMAPNameSpace *namespace;
2224 g_return_val_if_fail(folder != NULL, NULL);
2226 namespace = imap_find_namespace_from_list(folder->ns_personal, path);
2227 if (namespace) return namespace;
2228 namespace = imap_find_namespace_from_list(folder->ns_others, path);
2229 if (namespace) return namespace;
2230 namespace = imap_find_namespace_from_list(folder->ns_shared, path);
2231 if (namespace) return namespace;
2236 gchar imap_get_path_separator_for_item(FolderItem *item)
2238 Folder *folder = NULL;
2239 IMAPFolder *imap_folder = NULL;
2242 folder = item->folder;
2247 imap_folder = IMAP_FOLDER(folder);
2252 return imap_get_path_separator(imap_folder, item->path);
2255 static gchar imap_get_path_separator(IMAPFolder *folder, const gchar *path)
2257 IMAPNameSpace *namespace;
2258 gchar separator = '/';
2259 IMAPSession *session = imap_session_get(FOLDER(folder));
2260 g_return_val_if_fail(session != NULL, '/');
2262 if (folder->last_seen_separator == 0) {
2264 int r = imap_threaded_list((Folder *)folder, "", "", &lep_list);
2265 if (r != MAILIMAP_NO_ERROR) {
2266 log_warning(_("LIST failed\n"));
2270 if (clist_count(lep_list) > 0) {
2271 clistiter * iter = clist_begin(lep_list);
2272 struct mailimap_mailbox_list * mb;
2273 mb = clist_content(iter);
2275 folder->last_seen_separator = mb->mb_delimiter;
2276 debug_print("got separator: %c\n", folder->last_seen_separator);
2278 mailimap_list_result_free(lep_list);
2281 if (folder->last_seen_separator != 0) {
2282 debug_print("using separator: %c\n", folder->last_seen_separator);
2283 return folder->last_seen_separator;
2286 namespace = imap_find_namespace(folder, path);
2287 if (namespace && namespace->separator)
2288 separator = namespace->separator;
2293 static gchar *imap_get_real_path(IMAPFolder *folder, const gchar *path)
2298 g_return_val_if_fail(folder != NULL, NULL);
2299 g_return_val_if_fail(path != NULL, NULL);
2301 real_path = imap_utf8_to_modified_utf7(path);
2302 separator = imap_get_path_separator(folder, path);
2303 imap_path_separator_subst(real_path, separator);
2308 static gint imap_set_message_flags(IMAPSession *session,
2309 MsgNumberList *numlist,
2317 seq_list = imap_get_lep_set_from_numlist(numlist);
2319 for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) {
2320 struct mailimap_set * imapset;
2322 imapset = cur->data;
2324 ok = imap_cmd_store(session, imapset,
2328 imap_lep_set_free(seq_list);
2330 return IMAP_SUCCESS;
2333 typedef struct _select_data {
2334 IMAPSession *session;
2339 guint32 *uid_validity;
2343 static gint imap_select(IMAPSession *session, IMAPFolder *folder,
2345 gint *exists, gint *recent, gint *unseen,
2346 guint32 *uid_validity, gboolean block)
2350 gint exists_, recent_, unseen_;
2351 guint32 uid_validity_;
2353 if (!exists && !recent && !unseen && !uid_validity) {
2354 if (session->mbox && strcmp(session->mbox, path) == 0)
2355 return IMAP_SUCCESS;
2364 uid_validity = &uid_validity_;
2366 g_free(session->mbox);
2367 session->mbox = NULL;
2369 real_path = imap_get_real_path(folder, path);
2371 ok = imap_cmd_select(session, real_path,
2372 exists, recent, unseen, uid_validity, block);
2373 if (ok != IMAP_SUCCESS)
2374 log_warning(_("can't select folder: %s\n"), real_path);
2376 session->mbox = g_strdup(path);
2377 session->folder_content_changed = FALSE;
2384 static gint imap_status(IMAPSession *session, IMAPFolder *folder,
2385 const gchar *path, IMAPFolderItem *item,
2387 guint32 *uid_next, guint32 *uid_validity,
2388 gint *unseen, gboolean block)
2392 struct mailimap_mailbox_data_status * data_status;
2397 real_path = imap_get_real_path(folder, path);
2411 r = imap_threaded_status(FOLDER(folder), real_path,
2412 &data_status, mask);
2415 if (r != MAILIMAP_NO_ERROR) {
2416 debug_print("status err %d\n", r);
2420 if (data_status->st_info_list == NULL) {
2421 mailimap_mailbox_data_status_free(data_status);
2422 debug_print("status->st_info_list == NULL\n");
2427 for(iter = clist_begin(data_status->st_info_list) ; iter != NULL ;
2428 iter = clist_next(iter)) {
2429 struct mailimap_status_info * info;
2431 info = clist_content(iter);
2432 switch (info->st_att) {
2433 case MAILIMAP_STATUS_ATT_MESSAGES:
2434 * messages = info->st_value;
2435 got_values |= 1 << 0;
2438 case MAILIMAP_STATUS_ATT_UIDNEXT:
2439 * uid_next = info->st_value;
2440 got_values |= 1 << 2;
2443 case MAILIMAP_STATUS_ATT_UIDVALIDITY:
2444 * uid_validity = info->st_value;
2445 got_values |= 1 << 3;
2448 case MAILIMAP_STATUS_ATT_UNSEEN:
2449 * unseen = info->st_value;
2450 got_values |= 1 << 4;
2454 mailimap_mailbox_data_status_free(data_status);
2456 if (got_values != mask) {
2457 debug_print("status: incomplete values received (%d)\n", got_values);
2460 return IMAP_SUCCESS;
2463 static void imap_free_capabilities(IMAPSession *session)
2465 slist_free_strings(session->capability);
2466 g_slist_free(session->capability);
2467 session->capability = NULL;
2470 /* low-level IMAP4rev1 commands */
2472 static gint imap_cmd_login(IMAPSession *session,
2473 const gchar *user, const gchar *pass,
2479 log_print("IMAP4> Logging %s to %s using %s\n",
2481 SESSION(session)->server,
2483 r = imap_threaded_login(session->folder, user, pass, type);
2484 if (r != MAILIMAP_NO_ERROR) {
2485 log_print("IMAP4< Error logging in to %s\n",
2486 SESSION(session)->server);
2489 log_print("IMAP4< Login to %s successful\n",
2490 SESSION(session)->server);
2496 static gint imap_cmd_logout(IMAPSession *session)
2498 imap_threaded_disconnect(session->folder);
2500 return IMAP_SUCCESS;
2503 static gint imap_cmd_noop(IMAPSession *session)
2506 unsigned int exists;
2508 r = imap_threaded_noop(session->folder, &exists);
2509 if (r != MAILIMAP_NO_ERROR) {
2510 debug_print("noop err %d\n", r);
2513 session->exists = exists;
2514 session_set_access_time(SESSION(session));
2516 return IMAP_SUCCESS;
2520 static gint imap_cmd_starttls(IMAPSession *session)
2524 r = imap_threaded_starttls(session->folder,
2525 SESSION(session)->server, SESSION(session)->port);
2526 if (r != MAILIMAP_NO_ERROR) {
2527 debug_print("starttls err %d\n", r);
2530 return IMAP_SUCCESS;
2534 static gint imap_cmd_select(IMAPSession *session, const gchar *folder,
2535 gint *exists, gint *recent, gint *unseen,
2536 guint32 *uid_validity, gboolean block)
2540 r = imap_threaded_select(session->folder, folder,
2541 exists, recent, unseen, uid_validity);
2542 if (r != MAILIMAP_NO_ERROR) {
2543 debug_print("select err %d\n", r);
2546 return IMAP_SUCCESS;
2549 static gint imap_cmd_examine(IMAPSession *session, const gchar *folder,
2550 gint *exists, gint *recent, gint *unseen,
2551 guint32 *uid_validity, gboolean block)
2555 r = imap_threaded_examine(session->folder, folder,
2556 exists, recent, unseen, uid_validity);
2557 if (r != MAILIMAP_NO_ERROR) {
2558 debug_print("examine err %d\n", r);
2562 return IMAP_SUCCESS;
2565 static gint imap_cmd_create(IMAPSession *session, const gchar *folder)
2569 r = imap_threaded_create(session->folder, folder);
2570 if (r != MAILIMAP_NO_ERROR) {
2575 return IMAP_SUCCESS;
2578 static gint imap_cmd_rename(IMAPSession *session, const gchar *old_folder,
2579 const gchar *new_folder)
2583 r = imap_threaded_rename(session->folder, old_folder,
2585 if (r != MAILIMAP_NO_ERROR) {
2590 return IMAP_SUCCESS;
2593 static gint imap_cmd_delete(IMAPSession *session, const gchar *folder)
2598 r = imap_threaded_delete(session->folder, folder);
2599 if (r != MAILIMAP_NO_ERROR) {
2604 return IMAP_SUCCESS;
2607 typedef struct _fetch_data {
2608 IMAPSession *session;
2610 const gchar *filename;
2616 static void *imap_cmd_fetch_thread(void *data)
2618 fetch_data *stuff = (fetch_data *)data;
2619 IMAPSession *session = stuff->session;
2620 guint32 uid = stuff->uid;
2621 const gchar *filename = stuff->filename;
2625 r = imap_threaded_fetch_content(session->folder,
2629 r = imap_threaded_fetch_content(session->folder,
2632 if (r != MAILIMAP_NO_ERROR) {
2633 debug_print("fetch err %d\n", r);
2634 return GINT_TO_POINTER(IMAP_ERROR);
2636 return GINT_TO_POINTER(IMAP_SUCCESS);
2639 static gint imap_cmd_fetch(IMAPSession *session, guint32 uid,
2640 const gchar *filename, gboolean headers,
2643 fetch_data *data = g_new0(fetch_data, 1);
2646 data->session = session;
2648 data->filename = filename;
2649 data->headers = headers;
2652 if (prefs_common.work_offline &&
2653 !inc_offline_should_override(
2654 _("Sylpheed-Claws needs network access in order "
2655 "to access the IMAP server."))) {
2659 statusbar_print_all(_("Fetching message..."));
2660 result = GPOINTER_TO_INT(imap_cmd_fetch_thread(data));
2661 statusbar_pop_all();
2667 static gint imap_cmd_append(IMAPSession *session, const gchar *destfolder,
2668 const gchar *file, IMAPFlags flags,
2671 struct mailimap_flag_list * flag_list;
2674 g_return_val_if_fail(file != NULL, IMAP_ERROR);
2676 flag_list = imap_flag_to_lep(flags);
2677 r = imap_threaded_append(session->folder, destfolder,
2678 file, flag_list, (int *)new_uid);
2679 mailimap_flag_list_free(flag_list);
2681 if (r != MAILIMAP_NO_ERROR) {
2682 debug_print("append err %d\n", r);
2685 return IMAP_SUCCESS;
2688 static gint imap_cmd_copy(IMAPSession *session, struct mailimap_set * set,
2689 const gchar *destfolder, GRelation *uid_mapping)
2693 g_return_val_if_fail(session != NULL, IMAP_ERROR);
2694 g_return_val_if_fail(set != NULL, IMAP_ERROR);
2695 g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
2697 r = imap_threaded_copy(session->folder, set, destfolder);
2698 if (r != MAILIMAP_NO_ERROR) {
2703 return IMAP_SUCCESS;
2706 static gint imap_cmd_store(IMAPSession *session, struct mailimap_set * set,
2707 IMAPFlags flags, int do_add)
2710 struct mailimap_flag_list * flag_list;
2711 struct mailimap_store_att_flags * store_att_flags;
2713 flag_list = imap_flag_to_lep(flags);
2717 mailimap_store_att_flags_new_add_flags_silent(flag_list);
2720 mailimap_store_att_flags_new_remove_flags_silent(flag_list);
2722 r = imap_threaded_store(session->folder, set, store_att_flags);
2723 mailimap_store_att_flags_free(store_att_flags);
2724 if (r != MAILIMAP_NO_ERROR) {
2729 return IMAP_SUCCESS;
2732 static gint imap_cmd_expunge(IMAPSession *session)
2736 if (prefs_common.work_offline &&
2737 !inc_offline_should_override(
2738 _("Sylpheed-Claws needs network access in order "
2739 "to access the IMAP server."))) {
2743 r = imap_threaded_expunge(session->folder);
2744 if (r != MAILIMAP_NO_ERROR) {
2749 return IMAP_SUCCESS;
2752 static void imap_path_separator_subst(gchar *str, gchar separator)
2755 gboolean in_escape = FALSE;
2757 if (!separator || separator == '/') return;
2759 for (p = str; *p != '\0'; p++) {
2760 if (*p == '/' && !in_escape)
2762 else if (*p == '&' && *(p + 1) != '-' && !in_escape)
2764 else if (*p == '-' && in_escape)
2769 static gchar *imap_modified_utf7_to_utf8(const gchar *mutf7_str)
2771 static iconv_t cd = (iconv_t)-1;
2772 static gboolean iconv_ok = TRUE;
2775 size_t norm_utf7_len;
2777 gchar *to_str, *to_p;
2779 gboolean in_escape = FALSE;
2781 if (!iconv_ok) return g_strdup(mutf7_str);
2783 if (cd == (iconv_t)-1) {
2784 cd = iconv_open(CS_INTERNAL, CS_UTF_7);
2785 if (cd == (iconv_t)-1) {
2786 g_warning("iconv cannot convert UTF-7 to %s\n",
2789 return g_strdup(mutf7_str);
2793 /* modified UTF-7 to normal UTF-7 conversion */
2794 norm_utf7 = g_string_new(NULL);
2796 for (p = mutf7_str; *p != '\0'; p++) {
2797 /* replace: '&' -> '+',
2799 escaped ',' -> '/' */
2800 if (!in_escape && *p == '&') {
2801 if (*(p + 1) != '-') {
2802 g_string_append_c(norm_utf7, '+');
2805 g_string_append_c(norm_utf7, '&');
2808 } else if (in_escape && *p == ',') {
2809 g_string_append_c(norm_utf7, '/');
2810 } else if (in_escape && *p == '-') {
2811 g_string_append_c(norm_utf7, '-');
2814 g_string_append_c(norm_utf7, *p);
2818 norm_utf7_p = norm_utf7->str;
2819 norm_utf7_len = norm_utf7->len;
2820 to_len = strlen(mutf7_str) * 5;
2821 to_p = to_str = g_malloc(to_len + 1);
2823 if (iconv(cd, (ICONV_CONST gchar **)&norm_utf7_p, &norm_utf7_len,
2824 &to_p, &to_len) == -1) {
2825 g_warning(_("iconv cannot convert UTF-7 to %s\n"),
2826 conv_get_locale_charset_str());
2827 g_string_free(norm_utf7, TRUE);
2829 return g_strdup(mutf7_str);
2832 /* second iconv() call for flushing */
2833 iconv(cd, NULL, NULL, &to_p, &to_len);
2834 g_string_free(norm_utf7, TRUE);
2840 static gchar *imap_utf8_to_modified_utf7(const gchar *from)
2842 static iconv_t cd = (iconv_t)-1;
2843 static gboolean iconv_ok = TRUE;
2844 gchar *norm_utf7, *norm_utf7_p;
2845 size_t from_len, norm_utf7_len;
2847 gchar *from_tmp, *to, *p;
2848 gboolean in_escape = FALSE;
2850 if (!iconv_ok) return g_strdup(from);
2852 if (cd == (iconv_t)-1) {
2853 cd = iconv_open(CS_UTF_7, CS_INTERNAL);
2854 if (cd == (iconv_t)-1) {
2855 g_warning(_("iconv cannot convert %s to UTF-7\n"),
2858 return g_strdup(from);
2862 /* UTF-8 to normal UTF-7 conversion */
2863 Xstrdup_a(from_tmp, from, return g_strdup(from));
2864 from_len = strlen(from);
2865 norm_utf7_len = from_len * 5;
2866 Xalloca(norm_utf7, norm_utf7_len + 1, return g_strdup(from));
2867 norm_utf7_p = norm_utf7;
2869 #define IS_PRINT(ch) (isprint(ch) && IS_ASCII(ch))
2871 while (from_len > 0) {
2872 if (*from_tmp == '+') {
2873 *norm_utf7_p++ = '+';
2874 *norm_utf7_p++ = '-';
2878 } else if (IS_PRINT(*(guchar *)from_tmp)) {
2879 /* printable ascii char */
2880 *norm_utf7_p = *from_tmp;
2886 size_t conv_len = 0;
2888 /* unprintable char: convert to UTF-7 */
2890 while (!IS_PRINT(*(guchar *)p) && conv_len < from_len) {
2891 conv_len += g_utf8_skip[*(guchar *)p];
2892 p += g_utf8_skip[*(guchar *)p];
2895 from_len -= conv_len;
2896 if (iconv(cd, (ICONV_CONST gchar **)&from_tmp,
2898 &norm_utf7_p, &norm_utf7_len) == -1) {
2899 g_warning(_("iconv cannot convert UTF-8 to UTF-7\n"));
2900 return g_strdup(from);
2903 /* second iconv() call for flushing */
2904 iconv(cd, NULL, NULL, &norm_utf7_p, &norm_utf7_len);
2910 *norm_utf7_p = '\0';
2911 to_str = g_string_new(NULL);
2912 for (p = norm_utf7; p < norm_utf7_p; p++) {
2913 /* replace: '&' -> "&-",
2916 BASE64 '/' -> ',' */
2917 if (!in_escape && *p == '&') {
2918 g_string_append(to_str, "&-");
2919 } else if (!in_escape && *p == '+') {
2920 if (*(p + 1) == '-') {
2921 g_string_append_c(to_str, '+');
2924 g_string_append_c(to_str, '&');
2927 } else if (in_escape && *p == '/') {
2928 g_string_append_c(to_str, ',');
2929 } else if (in_escape && *p == '-') {
2930 g_string_append_c(to_str, '-');
2933 g_string_append_c(to_str, *p);
2939 g_string_append_c(to_str, '-');
2943 g_string_free(to_str, FALSE);
2948 static gboolean imap_rename_folder_func(GNode *node, gpointer data)
2950 FolderItem *item = node->data;
2951 gchar **paths = data;
2952 const gchar *oldpath = paths[0];
2953 const gchar *newpath = paths[1];
2955 gchar *new_itempath;
2958 oldpathlen = strlen(oldpath);
2959 if (strncmp(oldpath, item->path, oldpathlen) != 0) {
2960 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
2964 base = item->path + oldpathlen;
2965 while (*base == G_DIR_SEPARATOR) base++;
2967 new_itempath = g_strdup(newpath);
2969 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
2972 item->path = new_itempath;
2977 typedef struct _get_list_uid_data {
2979 IMAPSession *session;
2980 IMAPFolderItem *item;
2981 GSList **msgnum_list;
2983 } get_list_uid_data;
2985 static void *get_list_of_uids_thread(void *data)
2987 get_list_uid_data *stuff = (get_list_uid_data *)data;
2988 Folder *folder = stuff->folder;
2989 IMAPFolderItem *item = stuff->item;
2990 GSList **msgnum_list = stuff->msgnum_list;
2991 gint ok, nummsgs = 0, lastuid_old;
2992 IMAPSession *session;
2993 GSList *uidlist, *elem;
2994 struct mailimap_set * set;
2995 clist * lep_uidlist;
2998 session = stuff->session;
2999 if (session == NULL) {
3001 return GINT_TO_POINTER(-1);
3003 /* no session locking here, it's already locked by caller */
3004 ok = imap_select(session, IMAP_FOLDER(folder), item->item.path,
3005 NULL, NULL, NULL, NULL, TRUE);
3006 if (ok != IMAP_SUCCESS) {
3008 return GINT_TO_POINTER(-1);
3013 set = mailimap_set_new_interval(item->lastuid + 1, 0);
3015 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SIMPLE, set,
3017 mailimap_set_free(set);
3019 if (r == MAILIMAP_NO_ERROR) {
3020 GSList * fetchuid_list;
3023 imap_uid_list_from_lep(lep_uidlist);
3024 mailimap_search_result_free(lep_uidlist);
3026 uidlist = g_slist_concat(fetchuid_list, uidlist);
3029 GSList * fetchuid_list;
3030 carray * lep_uidtab;
3032 r = imap_threaded_fetch_uid(folder, item->lastuid + 1,
3034 if (r == MAILIMAP_NO_ERROR) {
3036 imap_uid_list_from_lep_tab(lep_uidtab);
3037 imap_fetch_uid_list_free(lep_uidtab);
3038 uidlist = g_slist_concat(fetchuid_list, uidlist);
3042 lastuid_old = item->lastuid;
3043 *msgnum_list = g_slist_copy(item->uid_list);
3044 nummsgs = g_slist_length(*msgnum_list);
3045 debug_print("Got %d uids from cache\n", g_slist_length(item->uid_list));
3047 for (elem = uidlist; elem != NULL; elem = g_slist_next(elem)) {
3050 msgnum = GPOINTER_TO_INT(elem->data);
3051 if (msgnum > lastuid_old) {
3052 *msgnum_list = g_slist_prepend(*msgnum_list, GINT_TO_POINTER(msgnum));
3053 item->uid_list = g_slist_prepend(item->uid_list, GINT_TO_POINTER(msgnum));
3056 if(msgnum > item->lastuid)
3057 item->lastuid = msgnum;
3060 g_slist_free(uidlist);
3062 return GINT_TO_POINTER(nummsgs);
3065 static gint get_list_of_uids(IMAPSession *session, Folder *folder, IMAPFolderItem *item, GSList **msgnum_list)
3068 get_list_uid_data *data = g_new0(get_list_uid_data, 1);
3070 data->folder = folder;
3072 data->msgnum_list = msgnum_list;
3073 data->session = session;
3074 if (prefs_common.work_offline &&
3075 !inc_offline_should_override(
3076 _("Sylpheed-Claws needs network access in order "
3077 "to access the IMAP server."))) {
3082 result = GPOINTER_TO_INT(get_list_of_uids_thread(data));
3088 gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list, gboolean *old_uids_valid)
3090 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3091 IMAPSession *session;
3092 gint ok, nummsgs = 0, exists;
3093 guint32 uid_next = 0, uid_val = 0;
3094 GSList *uidlist = NULL;
3096 gboolean selected_folder;
3098 debug_print("get_num_list\n");
3100 g_return_val_if_fail(folder != NULL, -1);
3101 g_return_val_if_fail(item != NULL, -1);
3102 g_return_val_if_fail(item->item.path != NULL, -1);
3103 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3104 g_return_val_if_fail(folder->account != NULL, -1);
3106 session = imap_session_get(folder);
3107 g_return_val_if_fail(session != NULL, -1);
3110 if (FOLDER_ITEM(item)->path)
3111 statusbar_print_all(_("Scanning folder %s%c%s ..."),
3112 FOLDER_ITEM(item)->folder->name,
3114 FOLDER_ITEM(item)->path);
3116 statusbar_print_all(_("Scanning folder %s ..."),
3117 FOLDER_ITEM(item)->folder->name);
3119 selected_folder = (session->mbox != NULL) &&
3120 (!strcmp(session->mbox, item->item.path));
3121 if (selected_folder && time(NULL) - item->use_cache < 2) {
3122 ok = imap_cmd_noop(session);
3123 if (ok != IMAP_SUCCESS) {
3124 debug_print("disconnected!\n");
3125 session = imap_reconnect_if_possible(folder, session);
3126 if (session == NULL) {
3127 statusbar_pop_all();
3132 exists = session->exists;
3134 uid_next = item->c_uid_next;
3135 uid_val = item->c_uid_validity;
3136 *old_uids_valid = TRUE;
3138 if (item->use_cache && time(NULL) - item->use_cache < 2) {
3139 exists = item->c_messages;
3140 uid_next = item->c_uid_next;
3141 uid_val = item->c_uid_validity;
3143 debug_print("using cache %d %d %d\n", exists, uid_next, uid_val);
3145 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path, item,
3146 &exists, &uid_next, &uid_val, NULL, FALSE);
3148 item->item.last_num = uid_next - 1;
3150 item->use_cache = (time_t)0;
3151 if (ok != IMAP_SUCCESS) {
3152 statusbar_pop_all();
3156 if(item->item.mtime == uid_val)
3157 *old_uids_valid = TRUE;
3159 *old_uids_valid = FALSE;
3161 debug_print("Freeing imap uid cache (%d != %d)\n",
3162 (int)item->item.mtime, uid_val);
3164 g_slist_free(item->uid_list);
3165 item->uid_list = NULL;
3167 item->item.mtime = uid_val;
3169 imap_delete_all_cached_messages((FolderItem *)item);
3173 /* If old uid_next matches new uid_next we can be sure no message
3174 was added to the folder */
3175 debug_print("uid_next is %d and item->uid_next %d \n",
3176 uid_next, item->uid_next);
3177 if (uid_next == item->uid_next) {
3178 nummsgs = g_slist_length(item->uid_list);
3180 /* If number of messages is still the same we
3181 know our caches message numbers are still valid,
3182 otherwise if the number of messages has decrease
3183 we discard our cache to start a new scan to find
3184 out which numbers have been removed */
3185 if (exists == nummsgs) {
3186 debug_print("exists == nummsgs\n");
3187 *msgnum_list = g_slist_copy(item->uid_list);
3188 statusbar_pop_all();
3191 } else if (exists < nummsgs) {
3192 debug_print("Freeing imap uid cache");
3194 g_slist_free(item->uid_list);
3195 item->uid_list = NULL;
3200 *msgnum_list = NULL;
3201 statusbar_pop_all();
3206 nummsgs = get_list_of_uids(session, folder, item, &uidlist);
3209 statusbar_pop_all();
3214 if (nummsgs != exists) {
3215 /* Cache contains more messages then folder, we have cached
3216 an old UID of a message that was removed and new messages
3217 have been added too, otherwise the uid_next check would
3219 debug_print("Freeing imap uid cache");
3221 g_slist_free(item->uid_list);
3222 item->uid_list = NULL;
3224 g_slist_free(*msgnum_list);
3226 nummsgs = get_list_of_uids(session, folder, item, &uidlist);
3229 *msgnum_list = uidlist;
3231 dir = folder_item_get_path((FolderItem *)item);
3232 debug_print("removing old messages from %s\n", dir);
3233 remove_numbered_files_not_in_list(dir, *msgnum_list);
3236 item->uid_next = uid_next;
3238 debug_print("get_num_list - ok - %i\n", nummsgs);
3239 statusbar_pop_all();
3244 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item)
3249 flags.perm_flags = MSG_NEW|MSG_UNREAD;
3250 flags.tmp_flags = 0;
3252 g_return_val_if_fail(item != NULL, NULL);
3253 g_return_val_if_fail(file != NULL, NULL);
3255 if (folder_has_parent_of_type(item, F_QUEUE)) {
3256 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
3257 } else if (folder_has_parent_of_type(item, F_DRAFT)) {
3258 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
3261 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3262 if (!msginfo) return NULL;
3264 msginfo->plaintext_file = g_strdup(file);
3265 msginfo->folder = item;
3270 GSList *imap_get_msginfos(Folder *folder, FolderItem *item,
3271 GSList *msgnum_list)
3273 IMAPSession *session;
3274 MsgInfoList *ret = NULL;
3277 debug_print("get_msginfos\n");
3279 g_return_val_if_fail(folder != NULL, NULL);
3280 g_return_val_if_fail(item != NULL, NULL);
3281 g_return_val_if_fail(msgnum_list != NULL, NULL);
3283 session = imap_session_get(folder);
3284 g_return_val_if_fail(session != NULL, NULL);
3286 debug_print("IMAP getting msginfos\n");
3287 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3288 NULL, NULL, NULL, NULL, FALSE);
3289 if (ok != IMAP_SUCCESS) {
3293 if (!(folder_has_parent_of_type(item, F_DRAFT) ||
3294 folder_has_parent_of_type(item, F_QUEUE))) {
3295 ret = g_slist_concat(ret,
3296 imap_get_uncached_messages(session, item,
3299 MsgNumberList *sorted_list, *elem, *llast = NULL;
3300 gint startnum, lastnum;
3302 sorted_list = g_slist_sort(g_slist_copy(msgnum_list), g_int_compare);
3304 startnum = lastnum = GPOINTER_TO_INT(sorted_list->data);
3306 llast = g_slist_last(ret);
3307 for (elem = sorted_list;; elem = g_slist_next(elem)) {
3311 num = GPOINTER_TO_INT(elem->data);
3313 if (num > lastnum + 1 || elem == NULL) {
3315 for (i = startnum; i <= lastnum; ++i) {
3318 file = imap_fetch_msg(folder, item, i);
3320 MsgInfo *msginfo = imap_parse_msg(file, item);
3321 if (msginfo != NULL) {
3322 msginfo->msgnum = i;
3324 llast = ret = g_slist_append(ret, msginfo);
3326 llast = g_slist_append(llast, msginfo);
3327 llast = llast->next;
3332 session_set_access_time(SESSION(session));
3343 g_slist_free(sorted_list);
3349 MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid)
3351 MsgInfo *msginfo = NULL;
3352 MsgInfoList *msginfolist;
3353 MsgNumberList numlist;
3355 numlist.next = NULL;
3356 numlist.data = GINT_TO_POINTER(uid);
3358 msginfolist = imap_get_msginfos(folder, item, &numlist);
3359 if (msginfolist != NULL) {
3360 msginfo = msginfolist->data;
3361 g_slist_free(msginfolist);
3367 gboolean imap_scan_required(Folder *folder, FolderItem *_item)
3369 IMAPSession *session;
3370 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3371 gint ok, exists = 0, unseen = 0;
3372 guint32 uid_next, uid_val;
3373 gboolean selected_folder;
3375 g_return_val_if_fail(folder != NULL, FALSE);
3376 g_return_val_if_fail(item != NULL, FALSE);
3377 g_return_val_if_fail(item->item.folder != NULL, FALSE);
3378 g_return_val_if_fail(FOLDER_CLASS(item->item.folder) == &imap_class, FALSE);
3380 if (item->item.path == NULL)
3383 session = imap_session_get(folder);
3384 g_return_val_if_fail(session != NULL, FALSE);
3386 selected_folder = (session->mbox != NULL) &&
3387 (!strcmp(session->mbox, item->item.path));
3388 if (selected_folder && time(NULL) - item->use_cache < 2) {
3389 ok = imap_cmd_noop(session);
3390 if (ok != IMAP_SUCCESS) {
3391 debug_print("disconnected!\n");
3392 session = imap_reconnect_if_possible(folder, session);
3393 if (session == NULL)
3398 if (session->folder_content_changed
3399 || session->exists != item->item.total_msgs) {
3404 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path, IMAP_FOLDER_ITEM(item),
3405 &exists, &uid_next, &uid_val, &unseen, FALSE);
3406 if (ok != IMAP_SUCCESS) {
3411 item->use_cache = time(NULL);
3412 item->c_messages = exists;
3413 item->c_uid_next = uid_next;
3414 item->c_uid_validity = uid_val;
3415 item->c_unseen = unseen;
3416 item->item.last_num = uid_next - 1;
3417 debug_print("uidnext %d, item->uid_next %d, exists %d, item->item.total_msgs %d\n",
3418 uid_next, item->uid_next, exists, item->item.total_msgs);
3419 if ((uid_next != item->uid_next) || (exists != item->item.total_msgs)
3420 || unseen != item->item.unread_msgs || uid_val != item->item.mtime) {
3429 void imap_change_flags(Folder *folder, FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
3431 IMAPSession *session;
3432 IMAPFlags flags_set = 0, flags_unset = 0;
3433 gint ok = IMAP_SUCCESS;
3434 MsgNumberList numlist;
3435 hashtable_data *ht_data = NULL;
3437 g_return_if_fail(folder != NULL);
3438 g_return_if_fail(folder->klass == &imap_class);
3439 g_return_if_fail(item != NULL);
3440 g_return_if_fail(item->folder == folder);
3441 g_return_if_fail(msginfo != NULL);
3442 g_return_if_fail(msginfo->folder == item);
3444 session = imap_session_get(folder);
3449 if (!MSG_IS_MARKED(msginfo->flags) && (newflags & MSG_MARKED))
3450 flags_set |= IMAP_FLAG_FLAGGED;
3451 if ( MSG_IS_MARKED(msginfo->flags) && !(newflags & MSG_MARKED))
3452 flags_unset |= IMAP_FLAG_FLAGGED;
3454 if (!MSG_IS_UNREAD(msginfo->flags) && (newflags & MSG_UNREAD))
3455 flags_unset |= IMAP_FLAG_SEEN;
3456 if ( MSG_IS_UNREAD(msginfo->flags) && !(newflags & MSG_UNREAD))
3457 flags_set |= IMAP_FLAG_SEEN;
3459 if (!MSG_IS_REPLIED(msginfo->flags) && (newflags & MSG_REPLIED))
3460 flags_set |= IMAP_FLAG_ANSWERED;
3461 if ( MSG_IS_REPLIED(msginfo->flags) && !(newflags & MSG_REPLIED))
3462 flags_unset |= IMAP_FLAG_ANSWERED;
3464 if (!MSG_IS_DELETED(msginfo->flags) && (newflags & MSG_DELETED))
3465 flags_set |= IMAP_FLAG_DELETED;
3466 if ( MSG_IS_DELETED(msginfo->flags) && !(newflags & MSG_DELETED))
3467 flags_unset |= IMAP_FLAG_DELETED;
3469 if (!flags_set && !flags_unset) {
3470 /* the changed flags were not translatable to IMAP-speak.
3471 * like MSG_POSTFILTERED, so just apply. */
3472 msginfo->flags.perm_flags = newflags;
3477 if ((ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
3478 NULL, NULL, NULL, NULL, FALSE)) != IMAP_SUCCESS) {
3482 numlist.next = NULL;
3483 numlist.data = GINT_TO_POINTER(msginfo->msgnum);
3485 if (IMAP_FOLDER_ITEM(item)->batching) {
3486 /* instead of performing an UID STORE command for each message change,
3487 * as a lot of them can change "together", we just fill in hashtables
3488 * and defer the treatment so that we're able to send only one
3491 debug_print("IMAP batch mode on, deferring flags change\n");
3493 ht_data = g_hash_table_lookup(IMAP_FOLDER_ITEM(item)->flags_set_table,
3494 GINT_TO_POINTER(flags_set));
3495 if (ht_data == NULL) {
3496 ht_data = g_new0(hashtable_data, 1);
3497 ht_data->session = session;
3498 ht_data->item = IMAP_FOLDER_ITEM(item);
3499 g_hash_table_insert(IMAP_FOLDER_ITEM(item)->flags_set_table,
3500 GINT_TO_POINTER(flags_set), ht_data);
3502 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
3503 ht_data->msglist = g_slist_prepend(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum));
3506 ht_data = g_hash_table_lookup(IMAP_FOLDER_ITEM(item)->flags_unset_table,
3507 GINT_TO_POINTER(flags_unset));
3508 if (ht_data == NULL) {
3509 ht_data = g_new0(hashtable_data, 1);
3510 ht_data->session = session;
3511 ht_data->item = IMAP_FOLDER_ITEM(item);
3512 g_hash_table_insert(IMAP_FOLDER_ITEM(item)->flags_unset_table,
3513 GINT_TO_POINTER(flags_unset), ht_data);
3515 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
3516 ht_data->msglist = g_slist_prepend(ht_data->msglist,
3517 GINT_TO_POINTER(msginfo->msgnum));
3520 debug_print("IMAP changing flags\n");
3522 ok = imap_set_message_flags(session, &numlist, flags_set, TRUE);
3523 if (ok != IMAP_SUCCESS) {
3530 ok = imap_set_message_flags(session, &numlist, flags_unset, FALSE);
3531 if (ok != IMAP_SUCCESS) {
3537 msginfo->flags.perm_flags = newflags;
3542 static gint imap_remove_msg(Folder *folder, FolderItem *item, gint uid)
3545 IMAPSession *session;
3547 MsgNumberList numlist;
3549 g_return_val_if_fail(folder != NULL, -1);
3550 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3551 g_return_val_if_fail(item != NULL, -1);
3553 session = imap_session_get(folder);
3554 if (!session) return -1;
3556 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3557 NULL, NULL, NULL, NULL, FALSE);
3558 if (ok != IMAP_SUCCESS) {
3562 numlist.next = NULL;
3563 numlist.data = GINT_TO_POINTER(uid);
3565 ok = imap_set_message_flags
3566 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
3567 &numlist, IMAP_FLAG_DELETED, TRUE);
3568 if (ok != IMAP_SUCCESS) {
3569 log_warning(_("can't set deleted flags: %d\n"), uid);
3574 if (!session->uidplus) {
3575 ok = imap_cmd_expunge(session);
3579 uidstr = g_strdup_printf("%u", uid);
3580 ok = imap_cmd_expunge(session);
3583 if (ok != IMAP_SUCCESS) {
3584 log_warning(_("can't expunge\n"));
3589 IMAP_FOLDER_ITEM(item)->uid_list = g_slist_remove(
3590 IMAP_FOLDER_ITEM(item)->uid_list, numlist.data);
3591 dir = folder_item_get_path(item);
3592 if (is_dir_exist(dir))
3593 remove_numbered_files(dir, uid, uid);
3596 return IMAP_SUCCESS;
3599 static gint compare_msginfo(gconstpointer a, gconstpointer b)
3601 return ((MsgInfo *)a)->msgnum - ((MsgInfo *)b)->msgnum;
3604 static guint gslist_find_next_num(MsgNumberList **list, guint num)
3608 g_return_val_if_fail(list != NULL, -1);
3610 for (elem = *list; elem != NULL; elem = g_slist_next(elem))
3611 if (GPOINTER_TO_INT(elem->data) >= num)
3614 return elem != NULL ? GPOINTER_TO_INT(elem->data) : (gint)-1;
3618 * NEW and DELETED flags are not syncronized
3619 * - The NEW/RECENT flags in IMAP folders can not really be directly
3620 * modified by Sylpheed
3621 * - The DELETE/DELETED flag in IMAP and Sylpheed don't have the same
3622 * meaning, in IMAP it always removes the messages from the FolderItem
3623 * in Sylpheed it can mean to move the message to trash
3626 typedef struct _get_flags_data {
3629 MsgInfoList *msginfo_list;
3630 GRelation *msgflags;
3631 gboolean full_search;
3635 static /*gint*/ void *imap_get_flags_thread(void *data)
3637 get_flags_data *stuff = (get_flags_data *)data;
3638 Folder *folder = stuff->folder;
3639 FolderItem *item = stuff->item;
3640 MsgInfoList *msginfo_list = stuff->msginfo_list;
3641 GRelation *msgflags = stuff->msgflags;
3642 gboolean full_search = stuff->full_search;
3643 IMAPSession *session;
3644 GSList *sorted_list = NULL;
3645 GSList *unseen = NULL, *answered = NULL, *flagged = NULL, *deleted = NULL;
3646 GSList *p_unseen, *p_answered, *p_flagged, *p_deleted;
3648 GSList *seq_list, *cur;
3649 gboolean reverse_seen = FALSE;
3652 gint exists_cnt, unseen_cnt;
3653 gboolean selected_folder;
3655 if (folder == NULL || item == NULL) {
3657 return GINT_TO_POINTER(-1);
3660 session = imap_session_get(folder);
3661 if (session == NULL) {
3663 return GINT_TO_POINTER(-1);
3666 selected_folder = (session->mbox != NULL) &&
3667 (!strcmp(session->mbox, item->path));
3669 if (!selected_folder) {
3670 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3671 &exists_cnt, NULL, &unseen_cnt, NULL, TRUE);
3672 if (ok != IMAP_SUCCESS) {
3675 return GINT_TO_POINTER(-1);
3678 if (unseen_cnt > exists_cnt / 2)
3679 reverse_seen = TRUE;
3682 if (item->unread_msgs > item->total_msgs / 2)
3683 reverse_seen = TRUE;
3686 cmd_buf = g_string_new(NULL);
3688 sorted_list = g_slist_sort(g_slist_copy(msginfo_list), compare_msginfo);
3690 seq_list = imap_get_lep_set_from_msglist(msginfo_list);
3692 struct mailimap_set * set;
3693 set = mailimap_set_new_interval(1, 0);
3694 seq_list = g_slist_append(NULL, set);
3697 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
3698 struct mailimap_set * imapset;
3699 clist * lep_uidlist;
3702 imapset = cur->data;
3704 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SEEN,
3705 full_search ? NULL:imapset, &lep_uidlist);
3708 r = imap_threaded_search(folder,
3709 IMAP_SEARCH_TYPE_UNSEEN,
3710 full_search ? NULL:imapset, &lep_uidlist);
3712 if (r == MAILIMAP_NO_ERROR) {
3715 uidlist = imap_uid_list_from_lep(lep_uidlist);
3716 mailimap_search_result_free(lep_uidlist);
3718 unseen = g_slist_concat(unseen, uidlist);
3721 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_FLAGGED,
3722 full_search ? NULL:imapset, &lep_uidlist);
3723 if (r == MAILIMAP_NO_ERROR) {
3726 uidlist = imap_uid_list_from_lep(lep_uidlist);
3727 mailimap_search_result_free(lep_uidlist);
3729 flagged = g_slist_concat(flagged, uidlist);
3732 if (item->opened || item->processing_pending || item == folder->inbox) {
3733 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_ANSWERED,
3734 full_search ? NULL:imapset, &lep_uidlist);
3735 if (r == MAILIMAP_NO_ERROR) {
3738 uidlist = imap_uid_list_from_lep(lep_uidlist);
3739 mailimap_search_result_free(lep_uidlist);
3741 answered = g_slist_concat(answered, uidlist);
3744 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_DELETED,
3745 full_search ? NULL:imapset, &lep_uidlist);
3746 if (r == MAILIMAP_NO_ERROR) {
3749 uidlist = imap_uid_list_from_lep(lep_uidlist);
3750 mailimap_search_result_free(lep_uidlist);
3752 deleted = g_slist_concat(deleted, uidlist);
3758 p_answered = answered;
3759 p_flagged = flagged;
3760 p_deleted = deleted;
3762 for (elem = sorted_list; elem != NULL; elem = g_slist_next(elem)) {
3767 msginfo = (MsgInfo *) elem->data;
3768 flags = msginfo->flags.perm_flags;
3769 wasnew = (flags & MSG_NEW);
3770 if (item->opened || item->processing_pending || item == folder->inbox) {
3771 flags &= ~((reverse_seen ? 0 : MSG_UNREAD | MSG_NEW) | MSG_REPLIED | MSG_MARKED);
3773 flags &= ~((reverse_seen ? 0 : MSG_UNREAD | MSG_NEW | MSG_MARKED));
3776 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
3777 if (gslist_find_next_num(&p_unseen, msginfo->msgnum) == msginfo->msgnum) {
3778 if (!reverse_seen) {
3779 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
3781 flags &= ~(MSG_UNREAD | MSG_NEW);
3785 if (gslist_find_next_num(&p_flagged, msginfo->msgnum) == msginfo->msgnum)
3786 flags |= MSG_MARKED;
3788 flags &= ~MSG_MARKED;
3790 if (item->opened || item->processing_pending || item == folder->inbox) {
3791 if (gslist_find_next_num(&p_answered, msginfo->msgnum) == msginfo->msgnum)
3792 flags |= MSG_REPLIED;
3794 flags &= ~MSG_REPLIED;
3795 if (gslist_find_next_num(&p_deleted, msginfo->msgnum) == msginfo->msgnum)
3796 flags |= MSG_DELETED;
3798 flags &= ~MSG_DELETED;
3800 g_relation_insert(msgflags, msginfo, GINT_TO_POINTER(flags));
3803 imap_lep_set_free(seq_list);
3804 g_slist_free(flagged);
3805 g_slist_free(deleted);
3806 g_slist_free(answered);
3807 g_slist_free(unseen);
3808 g_slist_free(sorted_list);
3809 g_string_free(cmd_buf, TRUE);
3813 return GINT_TO_POINTER(0);
3816 static gint imap_get_flags(Folder *folder, FolderItem *item,
3817 MsgInfoList *msginfo_list, GRelation *msgflags)
3820 get_flags_data *data = g_new0(get_flags_data, 1);
3822 data->folder = folder;
3824 data->msginfo_list = msginfo_list;
3825 data->msgflags = msgflags;
3826 data->full_search = FALSE;
3828 GSList *tmp = NULL, *cur;
3830 if (prefs_common.work_offline &&
3831 !inc_offline_should_override(
3832 _("Sylpheed-Claws needs network access in order "
3833 "to access the IMAP server."))) {
3838 tmp = folder_item_get_msg_list(item);
3840 if (g_slist_length(tmp) <= g_slist_length(msginfo_list))
3841 data->full_search = TRUE;
3843 for (cur = tmp; cur; cur = cur->next)
3844 procmsg_msginfo_free((MsgInfo *)cur->data);
3848 result = GPOINTER_TO_INT(imap_get_flags_thread(data));
3855 static gboolean process_flags(gpointer key, gpointer value, gpointer user_data)
3857 gboolean flags_set = GPOINTER_TO_INT(user_data);
3858 gint flags_value = GPOINTER_TO_INT(key);
3859 hashtable_data *data = (hashtable_data *)value;
3860 IMAPFolderItem *_item = data->item;
3861 FolderItem *item = (FolderItem *)_item;
3862 gint ok = IMAP_ERROR;
3863 IMAPSession *session = imap_session_get(item->folder);
3865 data->msglist = g_slist_reverse(data->msglist);
3867 debug_print("IMAP %ssetting flags to %d for %d messages\n",
3870 g_slist_length(data->msglist));
3874 ok = imap_select(session, IMAP_FOLDER(item->folder), item->path,
3875 NULL, NULL, NULL, NULL, FALSE);
3877 if (ok == IMAP_SUCCESS) {
3878 imap_set_message_flags(data->session, data->msglist, flags_value, flags_set);
3880 g_warning("can't select mailbox %s\n", item->path);
3884 g_slist_free(data->msglist);
3889 static void process_hashtable(IMAPFolderItem *item)
3891 if (item->flags_set_table) {
3892 g_hash_table_foreach_remove(item->flags_set_table, process_flags, GINT_TO_POINTER(TRUE));
3893 g_hash_table_destroy(item->flags_set_table);
3894 item->flags_set_table = NULL;
3896 if (item->flags_unset_table) {
3897 g_hash_table_foreach_remove(item->flags_unset_table, process_flags, GINT_TO_POINTER(FALSE));
3898 g_hash_table_destroy(item->flags_unset_table);
3899 item->flags_unset_table = NULL;
3903 static void imap_set_batch (Folder *folder, FolderItem *_item, gboolean batch)
3905 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3907 g_return_if_fail(item != NULL);
3909 if (item->batching == batch)
3913 item->batching = TRUE;
3914 debug_print("IMAP switching to batch mode\n");
3915 if (!item->flags_set_table) {
3916 item->flags_set_table = g_hash_table_new(NULL, g_direct_equal);
3918 if (!item->flags_unset_table) {
3919 item->flags_unset_table = g_hash_table_new(NULL, g_direct_equal);
3922 debug_print("IMAP switching away from batch mode\n");
3924 process_hashtable(item);
3925 item->batching = FALSE;
3931 /* data types conversion libetpan <-> sylpheed */
3935 #define ETPAN_IMAP_MB_MARKED 1
3936 #define ETPAN_IMAP_MB_UNMARKED 2
3937 #define ETPAN_IMAP_MB_NOSELECT 4
3938 #define ETPAN_IMAP_MB_NOINFERIORS 8
3940 static int imap_flags_to_flags(struct mailimap_mbx_list_flags * imap_flags)
3946 if (imap_flags->mbf_type == MAILIMAP_MBX_LIST_FLAGS_SFLAG) {
3947 switch (imap_flags->mbf_sflag) {
3948 case MAILIMAP_MBX_LIST_SFLAG_MARKED:
3949 flags |= ETPAN_IMAP_MB_MARKED;
3951 case MAILIMAP_MBX_LIST_SFLAG_NOSELECT:
3952 flags |= ETPAN_IMAP_MB_NOSELECT;
3954 case MAILIMAP_MBX_LIST_SFLAG_UNMARKED:
3955 flags |= ETPAN_IMAP_MB_UNMARKED;
3960 for(cur = clist_begin(imap_flags->mbf_oflags) ; cur != NULL ;
3961 cur = clist_next(cur)) {
3962 struct mailimap_mbx_list_oflag * oflag;
3964 oflag = clist_content(cur);
3966 switch (oflag->of_type) {
3967 case MAILIMAP_MBX_LIST_OFLAG_NOINFERIORS:
3968 flags |= ETPAN_IMAP_MB_NOINFERIORS;
3976 static GSList * imap_list_from_lep(IMAPFolder * folder,
3977 clist * list, const gchar * real_path, gboolean all)
3980 GSList * item_list = NULL, *llast = NULL;
3982 for(iter = clist_begin(list) ; iter != NULL ;
3983 iter = clist_next(iter)) {
3984 struct mailimap_mailbox_list * mb;
3992 FolderItem *new_item;
3994 mb = clist_content(iter);
4000 if (mb->mb_flag != NULL)
4001 flags = imap_flags_to_flags(mb->mb_flag);
4003 delimiter = mb->mb_delimiter;
4006 dup_name = strdup(name);
4007 if (delimiter != '\0')
4008 subst_char(dup_name, delimiter, '/');
4010 base = g_path_get_basename(dup_name);
4011 if (base[0] == '.') {
4017 if (!all && strcmp(dup_name, real_path) == 0) {
4023 if (!all && dup_name[strlen(dup_name)-1] == '/') {
4029 loc_name = imap_modified_utf7_to_utf8(base);
4030 loc_path = imap_modified_utf7_to_utf8(dup_name);
4032 new_item = folder_item_new(FOLDER(folder), loc_name, loc_path);
4033 if ((flags & ETPAN_IMAP_MB_NOINFERIORS) != 0)
4034 new_item->no_sub = TRUE;
4035 if (strcmp(dup_name, "INBOX") != 0 &&
4036 ((flags & ETPAN_IMAP_MB_NOSELECT) != 0))
4037 new_item->no_select = TRUE;
4039 if (item_list == NULL)
4040 llast = item_list = g_slist_append(item_list, new_item);
4042 llast = g_slist_append(llast, new_item);
4043 llast = llast->next;
4045 debug_print("folder '%s' found.\n", loc_path);
4056 static GSList * imap_get_lep_set_from_numlist(MsgNumberList *numlist)
4058 GSList *sorted_list, *cur;
4059 guint first, last, next;
4060 GSList *ret_list = NULL, *llast = NULL;
4062 struct mailimap_set * current_set;
4063 unsigned int item_count;
4065 if (numlist == NULL)
4069 current_set = mailimap_set_new_empty();
4071 sorted_list = g_slist_copy(numlist);
4072 sorted_list = g_slist_sort(sorted_list, g_int_compare);
4074 first = GPOINTER_TO_INT(sorted_list->data);
4077 for (cur = sorted_list; cur != NULL; cur = g_slist_next(cur)) {
4078 if (GPOINTER_TO_INT(cur->data) == 0)
4083 last = GPOINTER_TO_INT(cur->data);
4085 next = GPOINTER_TO_INT(cur->next->data);
4089 if (last + 1 != next || next == 0) {
4091 struct mailimap_set_item * item;
4092 item = mailimap_set_item_new(first, last);
4093 mailimap_set_add(current_set, item);
4098 if (count >= IMAP_SET_MAX_COUNT) {
4099 if (ret_list == NULL)
4100 llast = ret_list = g_slist_append(ret_list,
4103 llast = g_slist_append(llast, current_set);
4104 llast = llast->next;
4106 current_set = mailimap_set_new_empty();
4113 if (clist_count(current_set->set_list) > 0) {
4114 ret_list = g_slist_append(ret_list,
4118 g_slist_free(sorted_list);
4123 static GSList * imap_get_lep_set_from_msglist(MsgInfoList *msglist)
4125 MsgNumberList *numlist = NULL;
4129 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
4130 MsgInfo *msginfo = (MsgInfo *) cur->data;
4132 numlist = g_slist_prepend(numlist, GINT_TO_POINTER(msginfo->msgnum));
4134 numlist = g_slist_reverse(numlist);
4135 seq_list = imap_get_lep_set_from_numlist(numlist);
4136 g_slist_free(numlist);
4141 static GSList * imap_uid_list_from_lep(clist * list)
4148 for(iter = clist_begin(list) ; iter != NULL ;
4149 iter = clist_next(iter)) {
4152 puid = clist_content(iter);
4153 result = g_slist_prepend(result, GINT_TO_POINTER(* puid));
4156 result = g_slist_reverse(result);
4160 static GSList * imap_uid_list_from_lep_tab(carray * list)
4167 for(i = 0 ; i < carray_count(list) ; i ++) {
4170 puid = carray_get(list, i);
4171 result = g_slist_prepend(result, GINT_TO_POINTER(* puid));
4173 result = g_slist_reverse(result);
4177 static MsgInfo *imap_envelope_from_lep(struct imap_fetch_env_info * info,
4180 MsgInfo *msginfo = NULL;
4183 MsgFlags flags = {0, 0};
4185 if (info->headers == NULL)
4188 MSG_SET_TMP_FLAGS(flags, MSG_IMAP);
4189 if (folder_has_parent_of_type(item, F_QUEUE)) {
4190 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
4191 } else if (folder_has_parent_of_type(item, F_DRAFT)) {
4192 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
4194 flags.perm_flags = info->flags;
4198 msginfo = procheader_parse_str(info->headers, flags, FALSE, FALSE);
4201 msginfo->msgnum = uid;
4202 msginfo->size = size;
4208 static void imap_lep_set_free(GSList *seq_list)
4212 for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) {
4213 struct mailimap_set * imapset;
4215 imapset = cur->data;
4216 mailimap_set_free(imapset);
4218 g_slist_free(seq_list);
4221 static struct mailimap_flag_list * imap_flag_to_lep(IMAPFlags flags)
4223 struct mailimap_flag_list * flag_list;
4225 flag_list = mailimap_flag_list_new_empty();
4227 if (IMAP_IS_SEEN(flags))
4228 mailimap_flag_list_add(flag_list,
4229 mailimap_flag_new_seen());
4230 if (IMAP_IS_ANSWERED(flags))
4231 mailimap_flag_list_add(flag_list,
4232 mailimap_flag_new_answered());
4233 if (IMAP_IS_FLAGGED(flags))
4234 mailimap_flag_list_add(flag_list,
4235 mailimap_flag_new_flagged());
4236 if (IMAP_IS_DELETED(flags))
4237 mailimap_flag_list_add(flag_list,
4238 mailimap_flag_new_deleted());
4239 if (IMAP_IS_DRAFT(flags))
4240 mailimap_flag_list_add(flag_list,
4241 mailimap_flag_new_draft());
4246 guint imap_folder_get_refcnt(Folder *folder)
4248 return ((IMAPFolder *)folder)->refcnt;
4251 void imap_folder_ref(Folder *folder)
4253 ((IMAPFolder *)folder)->refcnt++;
4256 void imap_disconnect_all(void)
4259 for (list = account_get_list(); list != NULL; list = list->next) {
4260 PrefsAccount *account = list->data;
4261 if (account->protocol == A_IMAP4) {
4262 RemoteFolder *folder = (RemoteFolder *)account->folder;
4263 if (folder && folder->session) {
4264 IMAPSession *session = (IMAPSession *)folder->session;
4265 imap_threaded_disconnect(FOLDER(folder));
4266 session_destroy(session);
4267 folder->session = NULL;
4273 void imap_folder_unref(Folder *folder)
4275 if (((IMAPFolder *)folder)->refcnt > 0)
4276 ((IMAPFolder *)folder)->refcnt--;
4279 #else /* HAVE_LIBETPAN */
4281 static FolderClass imap_class;
4283 static XMLTag *imap_item_get_xml(Folder *folder, FolderItem *item);
4284 static void imap_item_set_xml(Folder *folder, FolderItem *item, XMLTag *tag);
4286 static Folder *imap_folder_new (const gchar *name,
4291 static gint imap_create_tree (Folder *folder)
4295 static FolderItem *imap_create_folder (Folder *folder,
4301 static gint imap_rename_folder (Folder *folder,
4308 gchar imap_get_path_separator_for_item(FolderItem *item)
4313 FolderClass *imap_get_class(void)
4315 if (imap_class.idstr == NULL) {
4316 imap_class.type = F_IMAP;
4317 imap_class.idstr = "imap";
4318 imap_class.uistr = "IMAP4";
4320 imap_class.new_folder = imap_folder_new;
4321 imap_class.create_tree = imap_create_tree;
4322 imap_class.create_folder = imap_create_folder;
4323 imap_class.rename_folder = imap_rename_folder;
4325 imap_class.set_xml = folder_set_xml;
4326 imap_class.get_xml = folder_get_xml;
4327 imap_class.item_set_xml = imap_item_set_xml;
4328 imap_class.item_get_xml = imap_item_get_xml;
4329 /* nothing implemented */
4335 void imap_disconnect_all(void)
4341 void imap_synchronise(FolderItem *item)
4343 imap_gtk_synchronise(item);
4346 static void imap_item_set_xml(Folder *folder, FolderItem *item, XMLTag *tag)
4348 #ifdef HAVE_LIBETPAN
4351 folder_item_set_xml(folder, item, tag);
4353 #ifdef HAVE_LIBETPAN
4354 for (cur = tag->attr; cur != NULL; cur = g_list_next(cur)) {
4355 XMLAttr *attr = (XMLAttr *) cur->data;
4357 if (!attr || !attr->name || !attr->value) continue;
4358 if (!strcmp(attr->name, "uidnext"))
4359 IMAP_FOLDER_ITEM(item)->uid_next = atoi(attr->value);
4364 static XMLTag *imap_item_get_xml(Folder *folder, FolderItem *item)
4368 tag = folder_item_get_xml(folder, item);
4370 #ifdef HAVE_LIBETPAN
4371 xml_tag_add_attr(tag, xml_attr_new_int("uidnext",
4372 IMAP_FOLDER_ITEM(item)->uid_next));