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;
686 g_return_val_if_fail(folder != NULL, NULL);
687 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, NULL);
688 g_return_val_if_fail(folder->account != NULL, NULL);
690 if (prefs_common.work_offline &&
691 !inc_offline_should_override(
692 _("Sylpheed-Claws needs network access in order "
693 "to access the IMAP server."))) {
697 /* Make sure we have a session */
698 if (rfolder->session != NULL) {
699 session = IMAP_SESSION(rfolder->session);
700 /* don't do that yet...
705 imap_reset_uid_lists(folder);
706 if (time(NULL) - rfolder->last_failure <= 2)
708 session = imap_session_new(folder, folder->account);
710 if(session == NULL) {
711 rfolder->last_failure = time(NULL);
715 /* Make sure session is authenticated */
716 if (!IMAP_SESSION(session)->authenticated)
717 imap_session_authenticate(IMAP_SESSION(session), folder->account);
719 if (!IMAP_SESSION(session)->authenticated) {
720 session_destroy(SESSION(session));
721 rfolder->session = NULL;
722 rfolder->last_failure = time(NULL);
726 /* I think the point of this code is to avoid sending a
727 * keepalive if we've used the session recently and therefore
728 * think it's still alive. Unfortunately, most of the code
729 * does not yet check for errors on the socket, and so if the
730 * connection drops we don't notice until the timeout expires.
731 * A better solution than sending a NOOP every time would be
732 * for every command to be prepared to retry until it is
733 * successfully sent. -- mbp */
734 if (time(NULL) - SESSION(session)->last_access_time > SESSION_TIMEOUT_INTERVAL) {
735 /* verify that the session is still alive */
736 if (imap_cmd_noop(session) != IMAP_SUCCESS) {
737 debug_print("disconnected!\n");
738 session = imap_reconnect_if_possible(folder, session);
742 rfolder->session = SESSION(session);
744 return IMAP_SESSION(session);
747 static IMAPSession *imap_session_new(Folder * folder,
748 const PrefsAccount *account)
750 IMAPSession *session;
756 /* FIXME: IMAP over SSL only... */
759 port = account->set_imapport ? account->imapport
760 : account->ssl_imap == SSL_TUNNEL ? IMAPS_PORT : IMAP4_PORT;
761 ssl_type = account->ssl_imap;
763 if (account->ssl_imap != SSL_NONE) {
764 if (alertpanel_full(_("Insecure connection"),
765 _("This connection is configured to be secured "
766 "using SSL, but SSL is not available in this "
767 "build of Sylpheed-Claws. \n\n"
768 "Do you want to continue connecting to this "
769 "server? The communication would not be "
771 GTK_STOCK_CANCEL, _("Con_tinue connecting"),
772 NULL, FALSE, NULL, ALERT_WARNING,
773 G_ALERTDEFAULT) != G_ALERTALTERNATE)
776 port = account->set_imapport ? account->imapport
781 statusbar_print_all(_("Connecting to IMAP4 server: %s..."), folder->account->recv_server);
782 if (account->set_tunnelcmd) {
783 r = imap_threaded_connect_cmd(folder,
785 account->recv_server,
790 if (ssl_type == SSL_TUNNEL) {
791 r = imap_threaded_connect_ssl(folder,
792 account->recv_server,
798 r = imap_threaded_connect(folder,
799 account->recv_server,
805 if (r == MAILIMAP_NO_ERROR_AUTHENTICATED) {
806 authenticated = TRUE;
808 else if (r == MAILIMAP_NO_ERROR_NON_AUTHENTICATED) {
809 authenticated = FALSE;
812 if(!prefs_common.no_recv_err_panel) {
813 alertpanel_error(_("Can't connect to IMAP4 server: %s:%d"),
814 account->recv_server, port);
816 log_error(_("Can't connect to IMAP4 server: %s:%d\n"),
817 account->recv_server, port);
823 session = g_new0(IMAPSession, 1);
824 session_init(SESSION(session));
825 SESSION(session)->type = SESSION_IMAP;
826 SESSION(session)->server = g_strdup(account->recv_server);
827 SESSION(session)->sock = NULL;
829 SESSION(session)->destroy = imap_session_destroy;
831 session->capability = NULL;
833 session->authenticated = authenticated;
834 session->mbox = NULL;
835 session->cmd_count = 0;
836 session->folder = folder;
837 IMAP_FOLDER(session->folder)->last_seen_separator = 0;
840 if (account->ssl_imap == SSL_STARTTLS) {
843 ok = imap_cmd_starttls(session);
844 if (ok != IMAP_SUCCESS) {
845 log_warning(_("Can't start TLS session.\n"));
846 session_destroy(SESSION(session));
850 imap_free_capabilities(session);
851 session->authenticated = FALSE;
852 session->uidplus = FALSE;
853 session->cmd_count = 1;
856 log_message("IMAP connection is %s-authenticated\n",
857 (session->authenticated) ? "pre" : "un");
862 static void imap_session_authenticate(IMAPSession *session,
863 const PrefsAccount *account)
865 gchar *pass, *acc_pass;
866 gboolean failed = FALSE;
868 g_return_if_fail(account->userid != NULL);
869 acc_pass = account->passwd;
872 if (!pass && account->imap_auth_type != IMAP_AUTH_ANON) {
874 tmp_pass = input_dialog_query_password(account->recv_server, account->userid);
877 Xstrdup_a(pass, tmp_pass, {g_free(tmp_pass); return;});
879 } else if (account->imap_auth_type == IMAP_AUTH_ANON) {
882 statusbar_print_all(_("Connecting to IMAP4 server %s...\n"),
883 account->recv_server);
884 if (imap_auth(session, account->userid, pass, account->imap_auth_type) != IMAP_SUCCESS) {
892 imap_threaded_disconnect(session->folder);
893 imap_cmd_logout(session);
894 alertpanel_error(_("Couldn't login to IMAP server %s."), account->recv_server);
901 session->authenticated = TRUE;
904 static void imap_session_destroy(Session *session)
906 if (session->state != SESSION_DISCONNECTED)
907 imap_threaded_disconnect(IMAP_SESSION(session)->folder);
909 imap_free_capabilities(IMAP_SESSION(session));
910 g_free(IMAP_SESSION(session)->mbox);
911 sock_close(session->sock);
912 session->sock = NULL;
915 static gchar *imap_fetch_msg(Folder *folder, FolderItem *item, gint uid)
917 return imap_fetch_msg_full(folder, item, uid, TRUE, TRUE);
920 static guint get_size_with_crs(MsgInfo *info)
929 fp = procmsg_open_message(info);
933 while (fgets(buf, sizeof (buf), fp) != NULL) {
935 if (!strstr(buf, "\r") && strstr(buf, "\n"))
943 static gchar *imap_fetch_msg_full(Folder *folder, FolderItem *item, gint uid,
944 gboolean headers, gboolean body)
946 gchar *path, *filename;
947 IMAPSession *session;
950 g_return_val_if_fail(folder != NULL, NULL);
951 g_return_val_if_fail(item != NULL, NULL);
956 path = folder_item_get_path(item);
957 if (!is_dir_exist(path))
959 filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(uid), NULL);
961 debug_print("trying to fetch cached %s\n", filename);
962 if (is_file_exist(filename)) {
963 /* see whether the local file represents the whole message
964 * or not. As the IMAP server reports size with \r chars,
965 * we have to update the local file (UNIX \n only) size */
966 MsgInfo *msginfo = imap_parse_msg(filename, item);
967 MsgInfo *cached = msgcache_get_msg(item->cache,uid);
968 guint have_size = get_size_with_crs(msginfo);
971 debug_print("message %d has been already %scached (%d/%d).\n", uid,
972 have_size >= cached->size ? "fully ":"",
973 have_size, (int)cached->size);
975 if (cached && (cached->size <= have_size || !body)) {
976 procmsg_msginfo_free(cached);
977 procmsg_msginfo_free(msginfo);
978 file_strip_crs(filename);
980 } else if (!cached) {
981 debug_print("message not cached, considering file complete\n");
982 procmsg_msginfo_free(msginfo);
983 file_strip_crs(filename);
986 procmsg_msginfo_free(cached);
987 procmsg_msginfo_free(msginfo);
991 session = imap_session_get(folder);
1000 debug_print("IMAP fetching messages\n");
1001 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
1002 NULL, NULL, NULL, NULL, FALSE);
1003 if (ok != IMAP_SUCCESS) {
1004 g_warning("can't select mailbox %s\n", item->path);
1010 debug_print("getting message %d...\n", uid);
1011 ok = imap_cmd_fetch(session, (guint32)uid, filename, headers, body);
1013 if (ok != IMAP_SUCCESS) {
1014 g_warning("can't fetch message %d\n", uid);
1021 file_strip_crs(filename);
1025 static gint imap_add_msg(Folder *folder, FolderItem *dest,
1026 const gchar *file, MsgFlags *flags)
1030 MsgFileInfo fileinfo;
1032 g_return_val_if_fail(file != NULL, -1);
1034 fileinfo.msginfo = NULL;
1035 fileinfo.file = (gchar *)file;
1036 fileinfo.flags = flags;
1037 file_list.data = &fileinfo;
1038 file_list.next = NULL;
1040 ret = imap_add_msgs(folder, dest, &file_list, NULL);
1044 static gint imap_add_msgs(Folder *folder, FolderItem *dest, GSList *file_list,
1045 GRelation *relation)
1048 IMAPSession *session;
1049 guint32 last_uid = 0;
1051 MsgFileInfo *fileinfo;
1053 gint curnum = 0, total = 0;
1056 g_return_val_if_fail(folder != NULL, -1);
1057 g_return_val_if_fail(dest != NULL, -1);
1058 g_return_val_if_fail(file_list != NULL, -1);
1060 session = imap_session_get(folder);
1065 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
1067 statusbar_print_all(_("Adding messages..."));
1068 total = g_slist_length(file_list);
1069 for (cur = file_list; cur != NULL; cur = cur->next) {
1070 IMAPFlags iflags = 0;
1071 guint32 new_uid = 0;
1072 gchar *real_file = NULL;
1073 fileinfo = (MsgFileInfo *)cur->data;
1075 statusbar_progress_all(curnum, total, 1);
1078 if (fileinfo->flags) {
1079 if (MSG_IS_MARKED(*fileinfo->flags))
1080 iflags |= IMAP_FLAG_FLAGGED;
1081 if (MSG_IS_REPLIED(*fileinfo->flags))
1082 iflags |= IMAP_FLAG_ANSWERED;
1083 if (!MSG_IS_UNREAD(*fileinfo->flags))
1084 iflags |= IMAP_FLAG_SEEN;
1087 if (real_file == NULL)
1088 real_file = g_strdup(fileinfo->file);
1090 if (folder_has_parent_of_type(dest, F_QUEUE) ||
1091 folder_has_parent_of_type(dest, F_OUTBOX) ||
1092 folder_has_parent_of_type(dest, F_DRAFT) ||
1093 folder_has_parent_of_type(dest, F_TRASH))
1094 iflags |= IMAP_FLAG_SEEN;
1096 ok = imap_cmd_append(session, destdir, real_file, iflags,
1099 if (ok != IMAP_SUCCESS) {
1100 g_warning("can't append message %s\n", real_file);
1104 statusbar_progress_all(0,0,0);
1105 statusbar_pop_all();
1108 debug_print("appended new message as %d\n", new_uid);
1109 /* put the local file in the imapcache, so that we don't
1110 * have to fetch it back later. */
1112 gchar *cache_path = folder_item_get_path(dest);
1113 if (!is_dir_exist(cache_path))
1114 make_dir_hier(cache_path);
1115 if (is_dir_exist(cache_path)) {
1116 gchar *cache_file = g_strconcat(
1117 cache_path, G_DIR_SEPARATOR_S,
1118 itos(new_uid), NULL);
1119 copy_file(real_file, cache_file, TRUE);
1120 debug_print("copied to cache: %s\n", cache_file);
1127 if (relation != NULL)
1128 g_relation_insert(relation, fileinfo->msginfo != NULL ?
1129 (gpointer) fileinfo->msginfo : (gpointer) fileinfo,
1130 GINT_TO_POINTER(dest->last_num + 1));
1132 new_uid = dest->last_num+1;
1134 if (last_uid < new_uid) {
1140 statusbar_progress_all(0,0,0);
1141 statusbar_pop_all();
1143 imap_cmd_expunge(session);
1151 static gint imap_do_copy_msgs(Folder *folder, FolderItem *dest,
1152 MsgInfoList *msglist, GRelation *relation)
1156 GSList *seq_list, *cur;
1158 IMAPSession *session;
1159 gint ok = IMAP_SUCCESS;
1160 GRelation *uid_mapping;
1163 g_return_val_if_fail(folder != NULL, -1);
1164 g_return_val_if_fail(dest != NULL, -1);
1165 g_return_val_if_fail(msglist != NULL, -1);
1167 session = imap_session_get(folder);
1173 msginfo = (MsgInfo *)msglist->data;
1175 src = msginfo->folder;
1177 g_warning("the src folder is identical to the dest.\n");
1182 if (src->folder != dest->folder) {
1183 GSList *infolist = NULL, *cur;
1185 for (cur = msglist; cur; cur = cur->next) {
1186 msginfo = (MsgInfo *)cur->data;
1187 MsgFileInfo *fileinfo = g_new0(MsgFileInfo, 1);
1188 fileinfo->file = procmsg_get_message_file(msginfo);
1189 fileinfo->flags = &(msginfo->flags);
1190 infolist = g_slist_prepend(infolist, fileinfo);
1192 infolist = g_slist_reverse(infolist);
1193 res = folder_item_add_msgs(dest, infolist, FALSE);
1194 for (cur = infolist; cur; cur = cur->next) {
1195 MsgFileInfo *info = (MsgFileInfo *)cur->data;
1199 g_slist_free(infolist);
1203 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
1204 NULL, NULL, NULL, NULL, FALSE);
1205 if (ok != IMAP_SUCCESS) {
1210 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
1211 seq_list = imap_get_lep_set_from_msglist(msglist);
1212 uid_mapping = g_relation_new(2);
1213 g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
1215 statusbar_print_all(_("Copying messages..."));
1216 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
1217 struct mailimap_set * seq_set;
1218 seq_set = cur->data;
1220 debug_print("Copying messages from %s to %s ...\n",
1221 src->path, destdir);
1223 ok = imap_cmd_copy(session, seq_set, destdir, uid_mapping);
1224 if (ok != IMAP_SUCCESS) {
1225 g_relation_destroy(uid_mapping);
1226 imap_lep_set_free(seq_list);
1232 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
1233 MsgInfo *msginfo = (MsgInfo *)cur->data;
1236 tuples = g_relation_select(uid_mapping,
1237 GINT_TO_POINTER(msginfo->msgnum),
1239 if (tuples->len > 0) {
1240 gint num = GPOINTER_TO_INT(g_tuples_index(tuples, 0, 1));
1241 g_relation_insert(relation, msginfo,
1242 GPOINTER_TO_INT(num));
1246 g_relation_insert(relation, msginfo,
1247 GPOINTER_TO_INT(0));
1248 g_tuples_destroy(tuples);
1250 statusbar_pop_all();
1252 g_relation_destroy(uid_mapping);
1253 imap_lep_set_free(seq_list);
1257 IMAP_FOLDER_ITEM(dest)->lastuid = 0;
1258 IMAP_FOLDER_ITEM(dest)->uid_next = 0;
1259 g_slist_free(IMAP_FOLDER_ITEM(dest)->uid_list);
1260 IMAP_FOLDER_ITEM(dest)->uid_list = NULL;
1263 if (ok == IMAP_SUCCESS)
1269 static gint imap_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1273 g_return_val_if_fail(msginfo != NULL, -1);
1275 msglist.data = msginfo;
1276 msglist.next = NULL;
1278 return imap_copy_msgs(folder, dest, &msglist, NULL);
1281 static gint imap_copy_msgs(Folder *folder, FolderItem *dest,
1282 MsgInfoList *msglist, GRelation *relation)
1287 g_return_val_if_fail(folder != NULL, -1);
1288 g_return_val_if_fail(dest != NULL, -1);
1289 g_return_val_if_fail(msglist != NULL, -1);
1291 msginfo = (MsgInfo *)msglist->data;
1292 g_return_val_if_fail(msginfo->folder != NULL, -1);
1294 ret = imap_do_copy_msgs(folder, dest, msglist, relation);
1299 static gint imap_do_remove_msgs(Folder *folder, FolderItem *dest,
1300 MsgInfoList *msglist, GRelation *relation)
1302 gchar *destdir, *dir;
1303 GSList *numlist = NULL, *cur;
1305 IMAPSession *session;
1306 gint ok = IMAP_SUCCESS;
1307 GRelation *uid_mapping;
1309 g_return_val_if_fail(folder != NULL, -1);
1310 g_return_val_if_fail(dest != NULL, -1);
1311 g_return_val_if_fail(msglist != NULL, -1);
1313 session = imap_session_get(folder);
1318 msginfo = (MsgInfo *)msglist->data;
1320 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
1321 NULL, NULL, NULL, NULL, FALSE);
1322 if (ok != IMAP_SUCCESS) {
1327 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
1328 for (cur = msglist; cur; cur = cur->next) {
1329 msginfo = (MsgInfo *)cur->data;
1330 if (!MSG_IS_DELETED(msginfo->flags))
1331 numlist = g_slist_prepend(numlist, GINT_TO_POINTER(msginfo->msgnum));
1333 numlist = g_slist_reverse(numlist);
1335 uid_mapping = g_relation_new(2);
1336 g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
1338 ok = imap_set_message_flags
1339 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
1340 numlist, IMAP_FLAG_DELETED, TRUE);
1341 if (ok != IMAP_SUCCESS) {
1342 log_warning(_("can't set deleted flags\n"));
1346 ok = imap_cmd_expunge(session);
1347 if (ok != IMAP_SUCCESS) {
1348 log_warning(_("can't expunge\n"));
1353 dir = folder_item_get_path(msginfo->folder);
1354 if (is_dir_exist(dir)) {
1355 for (cur = msglist; cur; cur = cur->next) {
1356 msginfo = (MsgInfo *)cur->data;
1357 remove_numbered_files(dir, msginfo->msgnum, msginfo->msgnum);
1362 g_relation_destroy(uid_mapping);
1363 g_slist_free(numlist);
1367 if (ok == IMAP_SUCCESS)
1373 static gint imap_remove_msgs(Folder *folder, FolderItem *dest,
1374 MsgInfoList *msglist, GRelation *relation)
1378 g_return_val_if_fail(folder != NULL, -1);
1379 g_return_val_if_fail(dest != NULL, -1);
1380 if (msglist == NULL)
1383 msginfo = (MsgInfo *)msglist->data;
1384 g_return_val_if_fail(msginfo->folder != NULL, -1);
1386 return imap_do_remove_msgs(folder, dest, msglist, relation);
1389 static gint imap_remove_all_msg(Folder *folder, FolderItem *item)
1391 GSList *list = folder_item_get_msg_list(item);
1392 gint res = imap_remove_msgs(folder, item, list, NULL);
1393 procmsg_msg_list_free(list);
1397 static gboolean imap_is_msg_changed(Folder *folder, FolderItem *item,
1400 /* TODO: properly implement this method */
1404 static gint imap_close(Folder *folder, FolderItem *item)
1409 static gint imap_scan_tree(Folder *folder)
1411 FolderItem *item = NULL;
1412 IMAPSession *session;
1413 gchar *root_folder = NULL;
1415 g_return_val_if_fail(folder != NULL, -1);
1416 g_return_val_if_fail(folder->account != NULL, -1);
1418 session = imap_session_get(folder);
1420 if (!folder->node) {
1421 folder_tree_destroy(folder);
1422 item = folder_item_new(folder, folder->name, NULL);
1423 item->folder = folder;
1424 folder->node = item->node = g_node_new(item);
1430 if (folder->account->imap_dir && *folder->account->imap_dir) {
1435 Xstrdup_a(root_folder, folder->account->imap_dir, {unlock_session();return -1;});
1436 extract_quote(root_folder, '"');
1437 subst_char(root_folder,
1438 imap_get_path_separator(IMAP_FOLDER(folder),
1441 strtailchomp(root_folder, '/');
1442 real_path = imap_get_real_path
1443 (IMAP_FOLDER(folder), root_folder);
1444 debug_print("IMAP root directory: %s\n", real_path);
1446 /* check if root directory exist */
1448 r = imap_threaded_list(session->folder, "", real_path,
1450 if ((r != MAILIMAP_NO_ERROR) || (clist_count(lep_list) == 0)) {
1451 if (!folder->node) {
1452 item = folder_item_new(folder, folder->name, NULL);
1453 item->folder = folder;
1454 folder->node = item->node = g_node_new(item);
1459 mailimap_list_result_free(lep_list);
1465 item = FOLDER_ITEM(folder->node->data);
1466 if (!item || ((item->path || root_folder) &&
1467 strcmp2(item->path, root_folder) != 0)) {
1468 folder_tree_destroy(folder);
1469 item = folder_item_new(folder, folder->name, root_folder);
1470 item->folder = folder;
1471 folder->node = item->node = g_node_new(item);
1474 imap_scan_tree_recursive(session, FOLDER_ITEM(folder->node->data));
1475 imap_create_missing_folders(folder);
1481 static gint imap_scan_tree_recursive(IMAPSession *session, FolderItem *item)
1484 IMAPFolder *imapfolder;
1485 FolderItem *new_item;
1486 GSList *item_list, *cur;
1489 gchar *wildcard_path;
1495 g_return_val_if_fail(item != NULL, -1);
1496 g_return_val_if_fail(item->folder != NULL, -1);
1497 g_return_val_if_fail(item->no_sub == FALSE, -1);
1499 folder = item->folder;
1500 imapfolder = IMAP_FOLDER(folder);
1502 separator = imap_get_path_separator(imapfolder, item->path);
1504 if (folder->ui_func)
1505 folder->ui_func(folder, item, folder->ui_func_data);
1508 wildcard[0] = separator;
1511 real_path = imap_get_real_path(imapfolder, item->path);
1515 real_path = g_strdup("");
1518 Xstrcat_a(wildcard_path, real_path, wildcard,
1519 {g_free(real_path); return IMAP_ERROR;});
1521 r = imap_threaded_list(folder, "", wildcard_path, &lep_list);
1522 if (r != MAILIMAP_NO_ERROR) {
1526 item_list = imap_list_from_lep(imapfolder,
1527 lep_list, real_path, FALSE);
1528 mailimap_list_result_free(lep_list);
1533 node = item->node->children;
1534 while (node != NULL) {
1535 FolderItem *old_item = FOLDER_ITEM(node->data);
1536 GNode *next = node->next;
1539 for (cur = item_list; cur != NULL; cur = cur->next) {
1540 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1541 if (!strcmp2(old_item->path, cur_item->path)) {
1542 new_item = cur_item;
1547 debug_print("folder '%s' not found. removing...\n",
1549 folder_item_remove(old_item);
1551 old_item->no_sub = new_item->no_sub;
1552 old_item->no_select = new_item->no_select;
1553 if (old_item->no_sub == TRUE && node->children) {
1554 debug_print("folder '%s' doesn't have "
1555 "subfolders. removing...\n",
1557 folder_item_remove_children(old_item);
1564 for (cur = item_list; cur != NULL; cur = cur->next) {
1565 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1568 for (node = item->node->children; node != NULL;
1569 node = node->next) {
1570 if (!strcmp2(FOLDER_ITEM(node->data)->path,
1572 new_item = FOLDER_ITEM(node->data);
1573 folder_item_destroy(cur_item);
1579 new_item = cur_item;
1580 debug_print("new folder '%s' found.\n", new_item->path);
1581 folder_item_append(item, new_item);
1584 if (!strcmp(new_item->path, "INBOX")) {
1585 new_item->stype = F_INBOX;
1586 folder->inbox = new_item;
1587 } else if (!folder_item_parent(item) || item->stype == F_INBOX) {
1590 base = g_path_get_basename(new_item->path);
1592 if (!folder->outbox && !g_ascii_strcasecmp(base, "Sent")) {
1593 new_item->stype = F_OUTBOX;
1594 folder->outbox = new_item;
1595 } else if (!folder->draft && !g_ascii_strcasecmp(base, "Drafts")) {
1596 new_item->stype = F_DRAFT;
1597 folder->draft = new_item;
1598 } else if (!folder->queue && !g_ascii_strcasecmp(base, "Queue")) {
1599 new_item->stype = F_QUEUE;
1600 folder->queue = new_item;
1601 } else if (!folder->trash && !g_ascii_strcasecmp(base, "Trash")) {
1602 new_item->stype = F_TRASH;
1603 folder->trash = new_item;
1608 if (new_item->no_sub == FALSE)
1609 imap_scan_tree_recursive(session, new_item);
1612 g_slist_free(item_list);
1614 return IMAP_SUCCESS;
1617 static gint imap_create_tree(Folder *folder)
1619 g_return_val_if_fail(folder != NULL, -1);
1620 g_return_val_if_fail(folder->node != NULL, -1);
1621 g_return_val_if_fail(folder->node->data != NULL, -1);
1622 g_return_val_if_fail(folder->account != NULL, -1);
1624 imap_scan_tree(folder);
1625 imap_create_missing_folders(folder);
1630 static void imap_create_missing_folders(Folder *folder)
1632 g_return_if_fail(folder != NULL);
1635 folder->inbox = imap_create_special_folder
1636 (folder, F_INBOX, "INBOX");
1638 folder->trash = imap_create_special_folder
1639 (folder, F_TRASH, "Trash");
1641 folder->queue = imap_create_special_folder
1642 (folder, F_QUEUE, "Queue");
1643 if (!folder->outbox)
1644 folder->outbox = imap_create_special_folder
1645 (folder, F_OUTBOX, "Sent");
1647 folder->draft = imap_create_special_folder
1648 (folder, F_DRAFT, "Drafts");
1651 static FolderItem *imap_create_special_folder(Folder *folder,
1652 SpecialFolderItemType stype,
1656 FolderItem *new_item;
1658 g_return_val_if_fail(folder != NULL, NULL);
1659 g_return_val_if_fail(folder->node != NULL, NULL);
1660 g_return_val_if_fail(folder->node->data != NULL, NULL);
1661 g_return_val_if_fail(folder->account != NULL, NULL);
1662 g_return_val_if_fail(name != NULL, NULL);
1664 item = FOLDER_ITEM(folder->node->data);
1665 new_item = imap_create_folder(folder, item, name);
1668 g_warning("Can't create '%s'\n", name);
1669 if (!folder->inbox) return NULL;
1671 new_item = imap_create_folder(folder, folder->inbox, name);
1673 g_warning("Can't create '%s' under INBOX\n", name);
1675 new_item->stype = stype;
1677 new_item->stype = stype;
1682 static gchar *imap_folder_get_path(Folder *folder)
1686 g_return_val_if_fail(folder != NULL, NULL);
1687 g_return_val_if_fail(folder->account != NULL, NULL);
1689 folder_path = g_strconcat(get_imap_cache_dir(),
1691 folder->account->recv_server,
1693 folder->account->userid,
1699 static gchar *imap_item_get_path(Folder *folder, FolderItem *item)
1701 gchar *folder_path, *path;
1703 g_return_val_if_fail(folder != NULL, NULL);
1704 g_return_val_if_fail(item != NULL, NULL);
1705 folder_path = imap_folder_get_path(folder);
1707 g_return_val_if_fail(folder_path != NULL, NULL);
1708 if (folder_path[0] == G_DIR_SEPARATOR) {
1710 path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
1713 path = g_strdup(folder_path);
1716 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1717 folder_path, G_DIR_SEPARATOR_S,
1720 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1723 g_free(folder_path);
1728 static FolderItem *imap_create_folder(Folder *folder, FolderItem *parent,
1731 gchar *dirpath, *imap_path;
1732 IMAPSession *session;
1733 FolderItem *new_item;
1738 gboolean no_select = FALSE, no_sub = FALSE;
1740 g_return_val_if_fail(folder != NULL, NULL);
1741 g_return_val_if_fail(folder->account != NULL, NULL);
1742 g_return_val_if_fail(parent != NULL, NULL);
1743 g_return_val_if_fail(name != NULL, NULL);
1745 session = imap_session_get(folder);
1751 if (!folder_item_parent(parent) && strcmp(name, "INBOX") == 0) {
1752 dirpath = g_strdup(name);
1753 }else if (parent->path)
1754 dirpath = g_strconcat(parent->path, "/", name, NULL);
1755 else if ((p = strchr(name, '/')) != NULL && *(p + 1) != '\0')
1756 dirpath = g_strdup(name);
1757 else if (folder->account->imap_dir && *folder->account->imap_dir) {
1760 Xstrdup_a(imap_dir, folder->account->imap_dir, {unlock_session();return NULL;});
1761 strtailchomp(imap_dir, '/');
1762 dirpath = g_strconcat(imap_dir, "/", name, NULL);
1764 dirpath = g_strdup(name);
1768 /* keep trailing directory separator to create a folder that contains
1770 imap_path = imap_utf8_to_modified_utf7(dirpath);
1772 strtailchomp(dirpath, '/');
1773 Xstrdup_a(new_name, name, {
1778 separator = imap_get_path_separator(IMAP_FOLDER(folder), imap_path);
1779 imap_path_separator_subst(imap_path, separator);
1780 /* remove trailing / for display */
1781 strtailchomp(new_name, '/');
1783 if (strcmp(dirpath, "INBOX") != 0) {
1785 gboolean exist = FALSE;
1789 argbuf = g_ptr_array_new();
1790 r = imap_threaded_list(folder, "", imap_path, &lep_list);
1791 if (r != MAILIMAP_NO_ERROR) {
1792 log_warning(_("can't create mailbox: LIST failed\n"));
1795 ptr_array_free_strings(argbuf);
1796 g_ptr_array_free(argbuf, TRUE);
1801 if (clist_count(lep_list) > 0)
1803 mailimap_list_result_free(lep_list);
1806 ok = imap_cmd_create(session, imap_path);
1807 if (ok != IMAP_SUCCESS) {
1808 log_warning(_("can't create mailbox\n"));
1814 r = imap_threaded_list(folder, "", imap_path, &lep_list);
1815 if (r == MAILIMAP_NO_ERROR) {
1816 GSList *item_list = imap_list_from_lep(IMAP_FOLDER(folder),
1817 lep_list, dirpath, TRUE);
1819 FolderItem *cur_item = FOLDER_ITEM(item_list->data);
1820 no_select = cur_item->no_select;
1821 no_sub = cur_item->no_sub;
1822 g_slist_free(item_list);
1824 mailimap_list_result_free(lep_list);
1831 /* just get flags */
1832 r = imap_threaded_list(folder, "", "INBOX", &lep_list);
1833 if (r == MAILIMAP_NO_ERROR) {
1834 GSList *item_list = imap_list_from_lep(IMAP_FOLDER(folder),
1835 lep_list, dirpath, TRUE);
1837 FolderItem *cur_item = FOLDER_ITEM(item_list->data);
1838 no_select = cur_item->no_select;
1839 no_sub = cur_item->no_sub;
1840 g_slist_free(item_list);
1842 mailimap_list_result_free(lep_list);
1846 new_item = folder_item_new(folder, new_name, dirpath);
1847 new_item->no_select = no_select;
1848 new_item->no_sub = no_sub;
1849 folder_item_append(parent, new_item);
1853 dirpath = folder_item_get_path(new_item);
1854 if (!is_dir_exist(dirpath))
1855 make_dir_hier(dirpath);
1861 static gint imap_rename_folder(Folder *folder, FolderItem *item,
1866 gchar *real_oldpath;
1867 gchar *real_newpath;
1869 gchar *old_cache_dir;
1870 gchar *new_cache_dir;
1871 IMAPSession *session;
1874 gint exists, recent, unseen;
1875 guint32 uid_validity;
1877 g_return_val_if_fail(folder != NULL, -1);
1878 g_return_val_if_fail(item != NULL, -1);
1879 g_return_val_if_fail(item->path != NULL, -1);
1880 g_return_val_if_fail(name != NULL, -1);
1882 session = imap_session_get(folder);
1888 if (strchr(name, imap_get_path_separator(IMAP_FOLDER(folder), item->path)) != NULL) {
1889 g_warning(_("New folder name must not contain the namespace "
1895 real_oldpath = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1897 g_free(session->mbox);
1898 session->mbox = NULL;
1899 ok = imap_cmd_examine(session, "INBOX",
1900 &exists, &recent, &unseen, &uid_validity, FALSE);
1901 if (ok != IMAP_SUCCESS) {
1902 g_free(real_oldpath);
1907 separator = imap_get_path_separator(IMAP_FOLDER(folder), item->path);
1908 if (strchr(item->path, G_DIR_SEPARATOR)) {
1909 dirpath = g_path_get_dirname(item->path);
1910 newpath = g_strconcat(dirpath, G_DIR_SEPARATOR_S, name, NULL);
1913 newpath = g_strdup(name);
1915 real_newpath = imap_utf8_to_modified_utf7(newpath);
1916 imap_path_separator_subst(real_newpath, separator);
1918 ok = imap_cmd_rename(session, real_oldpath, real_newpath);
1919 if (ok != IMAP_SUCCESS) {
1920 log_warning(_("can't rename mailbox: %s to %s\n"),
1921 real_oldpath, real_newpath);
1922 g_free(real_oldpath);
1924 g_free(real_newpath);
1930 item->name = g_strdup(name);
1932 old_cache_dir = folder_item_get_path(item);
1934 paths[0] = g_strdup(item->path);
1936 g_node_traverse(item->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1937 imap_rename_folder_func, paths);
1939 if (is_dir_exist(old_cache_dir)) {
1940 new_cache_dir = folder_item_get_path(item);
1941 if (rename(old_cache_dir, new_cache_dir) < 0) {
1942 FILE_OP_ERROR(old_cache_dir, "rename");
1944 g_free(new_cache_dir);
1947 g_free(old_cache_dir);
1950 g_free(real_oldpath);
1951 g_free(real_newpath);
1956 static gint imap_remove_folder_real(Folder *folder, FolderItem *item)
1959 IMAPSession *session;
1963 g_return_val_if_fail(folder != NULL, -1);
1964 g_return_val_if_fail(item != NULL, -1);
1965 g_return_val_if_fail(item->path != NULL, -1);
1967 session = imap_session_get(folder);
1972 path = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1974 ok = imap_cmd_delete(session, path);
1975 if (ok != IMAP_SUCCESS) {
1976 gchar *tmp = g_strdup_printf("%s%c", path,
1977 imap_get_path_separator(IMAP_FOLDER(folder), path));
1980 ok = imap_cmd_delete(session, path);
1983 if (ok != IMAP_SUCCESS) {
1984 log_warning(_("can't delete mailbox\n"));
1991 cache_dir = folder_item_get_path(item);
1992 if (is_dir_exist(cache_dir) && remove_dir_recursive(cache_dir) < 0)
1993 g_warning("can't remove directory '%s'\n", cache_dir);
1995 folder_item_remove(item);
2000 static gint imap_remove_folder(Folder *folder, FolderItem *item)
2004 g_return_val_if_fail(item != NULL, -1);
2005 g_return_val_if_fail(item->folder != NULL, -1);
2006 g_return_val_if_fail(item->node != NULL, -1);
2008 node = item->node->children;
2009 while (node != NULL) {
2011 if (imap_remove_folder(folder, FOLDER_ITEM(node->data)) < 0)
2015 debug_print("IMAP removing %s\n", item->path);
2017 if (imap_remove_all_msg(folder, item) < 0)
2019 return imap_remove_folder_real(folder, item);
2022 typedef struct _uncached_data {
2023 IMAPSession *session;
2025 MsgNumberList *numlist;
2031 static void *imap_get_uncached_messages_thread(void *data)
2033 uncached_data *stuff = (uncached_data *)data;
2034 IMAPSession *session = stuff->session;
2035 FolderItem *item = stuff->item;
2036 MsgNumberList *numlist = stuff->numlist;
2038 GSList *newlist = NULL;
2039 GSList *llast = NULL;
2040 GSList *seq_list, *cur;
2042 debug_print("uncached_messages\n");
2044 if (session == NULL || item == NULL || item->folder == NULL
2045 || FOLDER_CLASS(item->folder) != &imap_class) {
2050 seq_list = imap_get_lep_set_from_numlist(numlist);
2051 debug_print("get msgs info\n");
2052 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
2053 struct mailimap_set * imapset;
2059 imapset = cur->data;
2061 r = imap_threaded_fetch_env(session->folder,
2062 imapset, &env_list);
2063 if (r != MAILIMAP_NO_ERROR)
2066 session_set_access_time(SESSION(session));
2069 for(i = 0 ; i < carray_count(env_list) ; i ++) {
2070 struct imap_fetch_env_info * info;
2073 info = carray_get(env_list, i);
2074 msginfo = imap_envelope_from_lep(info, item);
2075 if (msginfo == NULL)
2077 msginfo->folder = item;
2079 llast = newlist = g_slist_append(newlist, msginfo);
2081 llast = g_slist_append(llast, msginfo);
2082 llast = llast->next;
2087 imap_fetch_env_free(env_list);
2090 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
2091 struct mailimap_set * imapset;
2093 imapset = cur->data;
2094 mailimap_set_free(imapset);
2097 session_set_access_time(SESSION(session));
2102 #define MAX_MSG_NUM 50
2104 static GSList *imap_get_uncached_messages(IMAPSession *session,
2106 MsgNumberList *numlist)
2108 GSList *result = NULL;
2110 uncached_data *data = g_new0(uncached_data, 1);
2115 data->total = g_slist_length(numlist);
2116 debug_print("messages list : %i\n", data->total);
2118 while (cur != NULL) {
2119 GSList * partial_result;
2127 while (count < MAX_MSG_NUM) {
2132 if (newlist == NULL)
2133 llast = newlist = g_slist_append(newlist, p);
2135 llast = g_slist_append(llast, p);
2136 llast = llast->next;
2146 data->session = session;
2148 data->numlist = newlist;
2151 if (prefs_common.work_offline &&
2152 !inc_offline_should_override(
2153 _("Sylpheed-Claws needs network access in order "
2154 "to access the IMAP server."))) {
2160 (GSList *)imap_get_uncached_messages_thread(data);
2162 statusbar_progress_all(data->cur,data->total, 1);
2164 g_slist_free(newlist);
2166 result = g_slist_concat(result, partial_result);
2170 statusbar_progress_all(0,0,0);
2171 statusbar_pop_all();
2176 static void imap_delete_all_cached_messages(FolderItem *item)
2180 g_return_if_fail(item != NULL);
2181 g_return_if_fail(item->folder != NULL);
2182 g_return_if_fail(FOLDER_CLASS(item->folder) == &imap_class);
2184 debug_print("Deleting all cached messages...\n");
2186 dir = folder_item_get_path(item);
2187 if (is_dir_exist(dir))
2188 remove_all_numbered_files(dir);
2191 debug_print("done.\n");
2194 static IMAPNameSpace *imap_find_namespace_from_list(GList *ns_list,
2197 IMAPNameSpace *namespace = NULL;
2198 gchar *tmp_path, *name;
2200 if (!path) path = "";
2202 for (; ns_list != NULL; ns_list = ns_list->next) {
2203 IMAPNameSpace *tmp_ns = ns_list->data;
2205 Xstrcat_a(tmp_path, path, "/", return namespace);
2206 Xstrdup_a(name, tmp_ns->name, return namespace);
2207 if (tmp_ns->separator && tmp_ns->separator != '/') {
2208 subst_char(tmp_path, tmp_ns->separator, '/');
2209 subst_char(name, tmp_ns->separator, '/');
2211 if (strncmp(tmp_path, name, strlen(name)) == 0)
2218 static IMAPNameSpace *imap_find_namespace(IMAPFolder *folder,
2221 IMAPNameSpace *namespace;
2223 g_return_val_if_fail(folder != NULL, NULL);
2225 namespace = imap_find_namespace_from_list(folder->ns_personal, path);
2226 if (namespace) return namespace;
2227 namespace = imap_find_namespace_from_list(folder->ns_others, path);
2228 if (namespace) return namespace;
2229 namespace = imap_find_namespace_from_list(folder->ns_shared, path);
2230 if (namespace) return namespace;
2235 gchar imap_get_path_separator_for_item(FolderItem *item)
2237 Folder *folder = NULL;
2238 IMAPFolder *imap_folder = NULL;
2241 folder = item->folder;
2246 imap_folder = IMAP_FOLDER(folder);
2251 return imap_get_path_separator(imap_folder, item->path);
2254 static gchar imap_get_path_separator(IMAPFolder *folder, const gchar *path)
2256 IMAPNameSpace *namespace;
2257 gchar separator = '/';
2258 IMAPSession *session = imap_session_get(FOLDER(folder));
2259 g_return_val_if_fail(session != NULL, '/');
2261 if (folder->last_seen_separator == 0) {
2263 int r = imap_threaded_list((Folder *)folder, "", "", &lep_list);
2264 if (r != MAILIMAP_NO_ERROR) {
2265 log_warning(_("LIST failed\n"));
2269 if (clist_count(lep_list) > 0) {
2270 clistiter * iter = clist_begin(lep_list);
2271 struct mailimap_mailbox_list * mb;
2272 mb = clist_content(iter);
2274 folder->last_seen_separator = mb->mb_delimiter;
2275 debug_print("got separator: %c\n", folder->last_seen_separator);
2277 mailimap_list_result_free(lep_list);
2280 if (folder->last_seen_separator != 0) {
2281 debug_print("using separator: %c\n", folder->last_seen_separator);
2282 return folder->last_seen_separator;
2285 namespace = imap_find_namespace(folder, path);
2286 if (namespace && namespace->separator)
2287 separator = namespace->separator;
2292 static gchar *imap_get_real_path(IMAPFolder *folder, const gchar *path)
2297 g_return_val_if_fail(folder != NULL, NULL);
2298 g_return_val_if_fail(path != NULL, NULL);
2300 real_path = imap_utf8_to_modified_utf7(path);
2301 separator = imap_get_path_separator(folder, path);
2302 imap_path_separator_subst(real_path, separator);
2307 static gint imap_set_message_flags(IMAPSession *session,
2308 MsgNumberList *numlist,
2316 seq_list = imap_get_lep_set_from_numlist(numlist);
2318 for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) {
2319 struct mailimap_set * imapset;
2321 imapset = cur->data;
2323 ok = imap_cmd_store(session, imapset,
2327 imap_lep_set_free(seq_list);
2329 return IMAP_SUCCESS;
2332 typedef struct _select_data {
2333 IMAPSession *session;
2338 guint32 *uid_validity;
2342 static gint imap_select(IMAPSession *session, IMAPFolder *folder,
2344 gint *exists, gint *recent, gint *unseen,
2345 guint32 *uid_validity, gboolean block)
2349 gint exists_, recent_, unseen_;
2350 guint32 uid_validity_;
2352 if (!exists && !recent && !unseen && !uid_validity) {
2353 if (session->mbox && strcmp(session->mbox, path) == 0)
2354 return IMAP_SUCCESS;
2363 uid_validity = &uid_validity_;
2365 g_free(session->mbox);
2366 session->mbox = NULL;
2368 real_path = imap_get_real_path(folder, path);
2370 ok = imap_cmd_select(session, real_path,
2371 exists, recent, unseen, uid_validity, block);
2372 if (ok != IMAP_SUCCESS)
2373 log_warning(_("can't select folder: %s\n"), real_path);
2375 session->mbox = g_strdup(path);
2376 session->folder_content_changed = FALSE;
2383 static gint imap_status(IMAPSession *session, IMAPFolder *folder,
2384 const gchar *path, IMAPFolderItem *item,
2386 guint32 *uid_next, guint32 *uid_validity,
2387 gint *unseen, gboolean block)
2391 struct mailimap_mailbox_data_status * data_status;
2396 real_path = imap_get_real_path(folder, path);
2410 r = imap_threaded_status(FOLDER(folder), real_path,
2411 &data_status, mask);
2414 if (r != MAILIMAP_NO_ERROR) {
2415 debug_print("status err %d\n", r);
2419 if (data_status->st_info_list == NULL) {
2420 mailimap_mailbox_data_status_free(data_status);
2421 debug_print("status->st_info_list == NULL\n");
2426 for(iter = clist_begin(data_status->st_info_list) ; iter != NULL ;
2427 iter = clist_next(iter)) {
2428 struct mailimap_status_info * info;
2430 info = clist_content(iter);
2431 switch (info->st_att) {
2432 case MAILIMAP_STATUS_ATT_MESSAGES:
2433 * messages = info->st_value;
2434 got_values |= 1 << 0;
2437 case MAILIMAP_STATUS_ATT_UIDNEXT:
2438 * uid_next = info->st_value;
2439 got_values |= 1 << 2;
2442 case MAILIMAP_STATUS_ATT_UIDVALIDITY:
2443 * uid_validity = info->st_value;
2444 got_values |= 1 << 3;
2447 case MAILIMAP_STATUS_ATT_UNSEEN:
2448 * unseen = info->st_value;
2449 got_values |= 1 << 4;
2453 mailimap_mailbox_data_status_free(data_status);
2455 if (got_values != mask) {
2456 debug_print("status: incomplete values received (%d)\n", got_values);
2459 return IMAP_SUCCESS;
2462 static void imap_free_capabilities(IMAPSession *session)
2464 slist_free_strings(session->capability);
2465 g_slist_free(session->capability);
2466 session->capability = NULL;
2469 /* low-level IMAP4rev1 commands */
2471 static gint imap_cmd_login(IMAPSession *session,
2472 const gchar *user, const gchar *pass,
2478 log_print("IMAP4> Logging %s to %s using %s\n",
2480 SESSION(session)->server,
2482 r = imap_threaded_login(session->folder, user, pass, type);
2483 if (r != MAILIMAP_NO_ERROR) {
2484 log_print("IMAP4< Error logging in to %s\n",
2485 SESSION(session)->server);
2488 log_print("IMAP4< Login to %s successful\n",
2489 SESSION(session)->server);
2495 static gint imap_cmd_logout(IMAPSession *session)
2497 imap_threaded_disconnect(session->folder);
2499 return IMAP_SUCCESS;
2502 static gint imap_cmd_noop(IMAPSession *session)
2505 unsigned int exists;
2507 r = imap_threaded_noop(session->folder, &exists);
2508 if (r != MAILIMAP_NO_ERROR) {
2509 debug_print("noop err %d\n", r);
2512 session->exists = exists;
2513 session_set_access_time(SESSION(session));
2515 return IMAP_SUCCESS;
2519 static gint imap_cmd_starttls(IMAPSession *session)
2523 r = imap_threaded_starttls(session->folder,
2524 SESSION(session)->server, SESSION(session)->port);
2525 if (r != MAILIMAP_NO_ERROR) {
2526 debug_print("starttls err %d\n", r);
2529 return IMAP_SUCCESS;
2533 static gint imap_cmd_select(IMAPSession *session, const gchar *folder,
2534 gint *exists, gint *recent, gint *unseen,
2535 guint32 *uid_validity, gboolean block)
2539 r = imap_threaded_select(session->folder, folder,
2540 exists, recent, unseen, uid_validity);
2541 if (r != MAILIMAP_NO_ERROR) {
2542 debug_print("select err %d\n", r);
2545 return IMAP_SUCCESS;
2548 static gint imap_cmd_examine(IMAPSession *session, const gchar *folder,
2549 gint *exists, gint *recent, gint *unseen,
2550 guint32 *uid_validity, gboolean block)
2554 r = imap_threaded_examine(session->folder, folder,
2555 exists, recent, unseen, uid_validity);
2556 if (r != MAILIMAP_NO_ERROR) {
2557 debug_print("examine err %d\n", r);
2561 return IMAP_SUCCESS;
2564 static gint imap_cmd_create(IMAPSession *session, const gchar *folder)
2568 r = imap_threaded_create(session->folder, folder);
2569 if (r != MAILIMAP_NO_ERROR) {
2574 return IMAP_SUCCESS;
2577 static gint imap_cmd_rename(IMAPSession *session, const gchar *old_folder,
2578 const gchar *new_folder)
2582 r = imap_threaded_rename(session->folder, old_folder,
2584 if (r != MAILIMAP_NO_ERROR) {
2589 return IMAP_SUCCESS;
2592 static gint imap_cmd_delete(IMAPSession *session, const gchar *folder)
2597 r = imap_threaded_delete(session->folder, folder);
2598 if (r != MAILIMAP_NO_ERROR) {
2603 return IMAP_SUCCESS;
2606 typedef struct _fetch_data {
2607 IMAPSession *session;
2609 const gchar *filename;
2615 static void *imap_cmd_fetch_thread(void *data)
2617 fetch_data *stuff = (fetch_data *)data;
2618 IMAPSession *session = stuff->session;
2619 guint32 uid = stuff->uid;
2620 const gchar *filename = stuff->filename;
2624 r = imap_threaded_fetch_content(session->folder,
2628 r = imap_threaded_fetch_content(session->folder,
2631 if (r != MAILIMAP_NO_ERROR) {
2632 debug_print("fetch err %d\n", r);
2633 return GINT_TO_POINTER(IMAP_ERROR);
2635 return GINT_TO_POINTER(IMAP_SUCCESS);
2638 static gint imap_cmd_fetch(IMAPSession *session, guint32 uid,
2639 const gchar *filename, gboolean headers,
2642 fetch_data *data = g_new0(fetch_data, 1);
2645 data->session = session;
2647 data->filename = filename;
2648 data->headers = headers;
2651 if (prefs_common.work_offline &&
2652 !inc_offline_should_override(
2653 _("Sylpheed-Claws needs network access in order "
2654 "to access the IMAP server."))) {
2658 statusbar_print_all(_("Fetching message..."));
2659 result = GPOINTER_TO_INT(imap_cmd_fetch_thread(data));
2660 statusbar_pop_all();
2666 static gint imap_cmd_append(IMAPSession *session, const gchar *destfolder,
2667 const gchar *file, IMAPFlags flags,
2670 struct mailimap_flag_list * flag_list;
2673 g_return_val_if_fail(file != NULL, IMAP_ERROR);
2675 flag_list = imap_flag_to_lep(flags);
2676 r = imap_threaded_append(session->folder, destfolder,
2677 file, flag_list, (int *)new_uid);
2678 mailimap_flag_list_free(flag_list);
2680 if (r != MAILIMAP_NO_ERROR) {
2681 debug_print("append err %d\n", r);
2684 return IMAP_SUCCESS;
2687 static gint imap_cmd_copy(IMAPSession *session, struct mailimap_set * set,
2688 const gchar *destfolder, GRelation *uid_mapping)
2692 g_return_val_if_fail(session != NULL, IMAP_ERROR);
2693 g_return_val_if_fail(set != NULL, IMAP_ERROR);
2694 g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
2696 r = imap_threaded_copy(session->folder, set, destfolder);
2697 if (r != MAILIMAP_NO_ERROR) {
2702 return IMAP_SUCCESS;
2705 static gint imap_cmd_store(IMAPSession *session, struct mailimap_set * set,
2706 IMAPFlags flags, int do_add)
2709 struct mailimap_flag_list * flag_list;
2710 struct mailimap_store_att_flags * store_att_flags;
2712 flag_list = imap_flag_to_lep(flags);
2716 mailimap_store_att_flags_new_add_flags_silent(flag_list);
2719 mailimap_store_att_flags_new_remove_flags_silent(flag_list);
2721 r = imap_threaded_store(session->folder, set, store_att_flags);
2722 mailimap_store_att_flags_free(store_att_flags);
2723 if (r != MAILIMAP_NO_ERROR) {
2728 return IMAP_SUCCESS;
2731 static gint imap_cmd_expunge(IMAPSession *session)
2735 if (prefs_common.work_offline &&
2736 !inc_offline_should_override(
2737 _("Sylpheed-Claws needs network access in order "
2738 "to access the IMAP server."))) {
2742 r = imap_threaded_expunge(session->folder);
2743 if (r != MAILIMAP_NO_ERROR) {
2748 return IMAP_SUCCESS;
2751 static void imap_path_separator_subst(gchar *str, gchar separator)
2754 gboolean in_escape = FALSE;
2756 if (!separator || separator == '/') return;
2758 for (p = str; *p != '\0'; p++) {
2759 if (*p == '/' && !in_escape)
2761 else if (*p == '&' && *(p + 1) != '-' && !in_escape)
2763 else if (*p == '-' && in_escape)
2768 static gchar *imap_modified_utf7_to_utf8(const gchar *mutf7_str)
2770 static iconv_t cd = (iconv_t)-1;
2771 static gboolean iconv_ok = TRUE;
2774 size_t norm_utf7_len;
2776 gchar *to_str, *to_p;
2778 gboolean in_escape = FALSE;
2780 if (!iconv_ok) return g_strdup(mutf7_str);
2782 if (cd == (iconv_t)-1) {
2783 cd = iconv_open(CS_INTERNAL, CS_UTF_7);
2784 if (cd == (iconv_t)-1) {
2785 g_warning("iconv cannot convert UTF-7 to %s\n",
2788 return g_strdup(mutf7_str);
2792 /* modified UTF-7 to normal UTF-7 conversion */
2793 norm_utf7 = g_string_new(NULL);
2795 for (p = mutf7_str; *p != '\0'; p++) {
2796 /* replace: '&' -> '+',
2798 escaped ',' -> '/' */
2799 if (!in_escape && *p == '&') {
2800 if (*(p + 1) != '-') {
2801 g_string_append_c(norm_utf7, '+');
2804 g_string_append_c(norm_utf7, '&');
2807 } else if (in_escape && *p == ',') {
2808 g_string_append_c(norm_utf7, '/');
2809 } else if (in_escape && *p == '-') {
2810 g_string_append_c(norm_utf7, '-');
2813 g_string_append_c(norm_utf7, *p);
2817 norm_utf7_p = norm_utf7->str;
2818 norm_utf7_len = norm_utf7->len;
2819 to_len = strlen(mutf7_str) * 5;
2820 to_p = to_str = g_malloc(to_len + 1);
2822 if (iconv(cd, (ICONV_CONST gchar **)&norm_utf7_p, &norm_utf7_len,
2823 &to_p, &to_len) == -1) {
2824 g_warning(_("iconv cannot convert UTF-7 to %s\n"),
2825 conv_get_locale_charset_str());
2826 g_string_free(norm_utf7, TRUE);
2828 return g_strdup(mutf7_str);
2831 /* second iconv() call for flushing */
2832 iconv(cd, NULL, NULL, &to_p, &to_len);
2833 g_string_free(norm_utf7, TRUE);
2839 static gchar *imap_utf8_to_modified_utf7(const gchar *from)
2841 static iconv_t cd = (iconv_t)-1;
2842 static gboolean iconv_ok = TRUE;
2843 gchar *norm_utf7, *norm_utf7_p;
2844 size_t from_len, norm_utf7_len;
2846 gchar *from_tmp, *to, *p;
2847 gboolean in_escape = FALSE;
2849 if (!iconv_ok) return g_strdup(from);
2851 if (cd == (iconv_t)-1) {
2852 cd = iconv_open(CS_UTF_7, CS_INTERNAL);
2853 if (cd == (iconv_t)-1) {
2854 g_warning(_("iconv cannot convert %s to UTF-7\n"),
2857 return g_strdup(from);
2861 /* UTF-8 to normal UTF-7 conversion */
2862 Xstrdup_a(from_tmp, from, return g_strdup(from));
2863 from_len = strlen(from);
2864 norm_utf7_len = from_len * 5;
2865 Xalloca(norm_utf7, norm_utf7_len + 1, return g_strdup(from));
2866 norm_utf7_p = norm_utf7;
2868 #define IS_PRINT(ch) (isprint(ch) && IS_ASCII(ch))
2870 while (from_len > 0) {
2871 if (*from_tmp == '+') {
2872 *norm_utf7_p++ = '+';
2873 *norm_utf7_p++ = '-';
2877 } else if (IS_PRINT(*(guchar *)from_tmp)) {
2878 /* printable ascii char */
2879 *norm_utf7_p = *from_tmp;
2885 size_t conv_len = 0;
2887 /* unprintable char: convert to UTF-7 */
2889 while (!IS_PRINT(*(guchar *)p) && conv_len < from_len) {
2890 conv_len += g_utf8_skip[*(guchar *)p];
2891 p += g_utf8_skip[*(guchar *)p];
2894 from_len -= conv_len;
2895 if (iconv(cd, (ICONV_CONST gchar **)&from_tmp,
2897 &norm_utf7_p, &norm_utf7_len) == -1) {
2898 g_warning(_("iconv cannot convert UTF-8 to UTF-7\n"));
2899 return g_strdup(from);
2902 /* second iconv() call for flushing */
2903 iconv(cd, NULL, NULL, &norm_utf7_p, &norm_utf7_len);
2909 *norm_utf7_p = '\0';
2910 to_str = g_string_new(NULL);
2911 for (p = norm_utf7; p < norm_utf7_p; p++) {
2912 /* replace: '&' -> "&-",
2915 BASE64 '/' -> ',' */
2916 if (!in_escape && *p == '&') {
2917 g_string_append(to_str, "&-");
2918 } else if (!in_escape && *p == '+') {
2919 if (*(p + 1) == '-') {
2920 g_string_append_c(to_str, '+');
2923 g_string_append_c(to_str, '&');
2926 } else if (in_escape && *p == '/') {
2927 g_string_append_c(to_str, ',');
2928 } else if (in_escape && *p == '-') {
2929 g_string_append_c(to_str, '-');
2932 g_string_append_c(to_str, *p);
2938 g_string_append_c(to_str, '-');
2942 g_string_free(to_str, FALSE);
2947 static gboolean imap_rename_folder_func(GNode *node, gpointer data)
2949 FolderItem *item = node->data;
2950 gchar **paths = data;
2951 const gchar *oldpath = paths[0];
2952 const gchar *newpath = paths[1];
2954 gchar *new_itempath;
2957 oldpathlen = strlen(oldpath);
2958 if (strncmp(oldpath, item->path, oldpathlen) != 0) {
2959 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
2963 base = item->path + oldpathlen;
2964 while (*base == G_DIR_SEPARATOR) base++;
2966 new_itempath = g_strdup(newpath);
2968 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
2971 item->path = new_itempath;
2976 typedef struct _get_list_uid_data {
2978 IMAPSession *session;
2979 IMAPFolderItem *item;
2980 GSList **msgnum_list;
2982 } get_list_uid_data;
2984 static void *get_list_of_uids_thread(void *data)
2986 get_list_uid_data *stuff = (get_list_uid_data *)data;
2987 Folder *folder = stuff->folder;
2988 IMAPFolderItem *item = stuff->item;
2989 GSList **msgnum_list = stuff->msgnum_list;
2990 gint ok, nummsgs = 0, lastuid_old;
2991 IMAPSession *session;
2992 GSList *uidlist, *elem;
2993 struct mailimap_set * set;
2994 clist * lep_uidlist;
2997 session = stuff->session;
2998 if (session == NULL) {
3000 return GINT_TO_POINTER(-1);
3002 /* no session locking here, it's already locked by caller */
3003 ok = imap_select(session, IMAP_FOLDER(folder), item->item.path,
3004 NULL, NULL, NULL, NULL, TRUE);
3005 if (ok != IMAP_SUCCESS) {
3007 return GINT_TO_POINTER(-1);
3012 set = mailimap_set_new_interval(item->lastuid + 1, 0);
3014 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SIMPLE, set,
3016 mailimap_set_free(set);
3018 if (r == MAILIMAP_NO_ERROR) {
3019 GSList * fetchuid_list;
3022 imap_uid_list_from_lep(lep_uidlist);
3023 mailimap_search_result_free(lep_uidlist);
3025 uidlist = g_slist_concat(fetchuid_list, uidlist);
3028 GSList * fetchuid_list;
3029 carray * lep_uidtab;
3031 r = imap_threaded_fetch_uid(folder, item->lastuid + 1,
3033 if (r == MAILIMAP_NO_ERROR) {
3035 imap_uid_list_from_lep_tab(lep_uidtab);
3036 imap_fetch_uid_list_free(lep_uidtab);
3037 uidlist = g_slist_concat(fetchuid_list, uidlist);
3041 lastuid_old = item->lastuid;
3042 *msgnum_list = g_slist_copy(item->uid_list);
3043 nummsgs = g_slist_length(*msgnum_list);
3044 debug_print("Got %d uids from cache\n", g_slist_length(item->uid_list));
3046 for (elem = uidlist; elem != NULL; elem = g_slist_next(elem)) {
3049 msgnum = GPOINTER_TO_INT(elem->data);
3050 if (msgnum > lastuid_old) {
3051 *msgnum_list = g_slist_prepend(*msgnum_list, GINT_TO_POINTER(msgnum));
3052 item->uid_list = g_slist_prepend(item->uid_list, GINT_TO_POINTER(msgnum));
3055 if(msgnum > item->lastuid)
3056 item->lastuid = msgnum;
3059 g_slist_free(uidlist);
3061 return GINT_TO_POINTER(nummsgs);
3064 static gint get_list_of_uids(IMAPSession *session, Folder *folder, IMAPFolderItem *item, GSList **msgnum_list)
3067 get_list_uid_data *data = g_new0(get_list_uid_data, 1);
3069 data->folder = folder;
3071 data->msgnum_list = msgnum_list;
3072 data->session = session;
3073 if (prefs_common.work_offline &&
3074 !inc_offline_should_override(
3075 _("Sylpheed-Claws needs network access in order "
3076 "to access the IMAP server."))) {
3081 result = GPOINTER_TO_INT(get_list_of_uids_thread(data));
3087 gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list, gboolean *old_uids_valid)
3089 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3090 IMAPSession *session;
3091 gint ok, nummsgs = 0, exists;
3092 guint32 uid_next = 0, uid_val = 0;
3093 GSList *uidlist = NULL;
3095 gboolean selected_folder;
3097 debug_print("get_num_list\n");
3099 g_return_val_if_fail(folder != NULL, -1);
3100 g_return_val_if_fail(item != NULL, -1);
3101 g_return_val_if_fail(item->item.path != NULL, -1);
3102 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3103 g_return_val_if_fail(folder->account != NULL, -1);
3105 session = imap_session_get(folder);
3106 g_return_val_if_fail(session != NULL, -1);
3109 if (FOLDER_ITEM(item)->path)
3110 statusbar_print_all(_("Scanning folder %s%c%s ..."),
3111 FOLDER_ITEM(item)->folder->name,
3113 FOLDER_ITEM(item)->path);
3115 statusbar_print_all(_("Scanning folder %s ..."),
3116 FOLDER_ITEM(item)->folder->name);
3118 selected_folder = (session->mbox != NULL) &&
3119 (!strcmp(session->mbox, item->item.path));
3120 if (selected_folder && time(NULL) - item->use_cache < 2) {
3121 ok = imap_cmd_noop(session);
3122 if (ok != IMAP_SUCCESS) {
3123 debug_print("disconnected!\n");
3124 session = imap_reconnect_if_possible(folder, session);
3125 if (session == NULL) {
3126 statusbar_pop_all();
3131 exists = session->exists;
3133 uid_next = item->c_uid_next;
3134 uid_val = item->c_uid_validity;
3135 *old_uids_valid = TRUE;
3137 if (item->use_cache && time(NULL) - item->use_cache < 2) {
3138 exists = item->c_messages;
3139 uid_next = item->c_uid_next;
3140 uid_val = item->c_uid_validity;
3142 debug_print("using cache %d %d %d\n", exists, uid_next, uid_val);
3144 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path, item,
3145 &exists, &uid_next, &uid_val, NULL, FALSE);
3147 item->item.last_num = uid_next - 1;
3149 item->use_cache = (time_t)0;
3150 if (ok != IMAP_SUCCESS) {
3151 statusbar_pop_all();
3155 if(item->item.mtime == uid_val)
3156 *old_uids_valid = TRUE;
3158 *old_uids_valid = FALSE;
3160 debug_print("Freeing imap uid cache (%d != %d)\n",
3161 (int)item->item.mtime, uid_val);
3163 g_slist_free(item->uid_list);
3164 item->uid_list = NULL;
3166 item->item.mtime = uid_val;
3168 imap_delete_all_cached_messages((FolderItem *)item);
3172 /* If old uid_next matches new uid_next we can be sure no message
3173 was added to the folder */
3174 debug_print("uid_next is %d and item->uid_next %d \n",
3175 uid_next, item->uid_next);
3176 if (uid_next == item->uid_next) {
3177 nummsgs = g_slist_length(item->uid_list);
3179 /* If number of messages is still the same we
3180 know our caches message numbers are still valid,
3181 otherwise if the number of messages has decrease
3182 we discard our cache to start a new scan to find
3183 out which numbers have been removed */
3184 if (exists == nummsgs) {
3185 debug_print("exists == nummsgs\n");
3186 *msgnum_list = g_slist_copy(item->uid_list);
3187 statusbar_pop_all();
3190 } else if (exists < nummsgs) {
3191 debug_print("Freeing imap uid cache");
3193 g_slist_free(item->uid_list);
3194 item->uid_list = NULL;
3199 *msgnum_list = NULL;
3200 statusbar_pop_all();
3205 nummsgs = get_list_of_uids(session, folder, item, &uidlist);
3208 statusbar_pop_all();
3213 if (nummsgs != exists) {
3214 /* Cache contains more messages then folder, we have cached
3215 an old UID of a message that was removed and new messages
3216 have been added too, otherwise the uid_next check would
3218 debug_print("Freeing imap uid cache");
3220 g_slist_free(item->uid_list);
3221 item->uid_list = NULL;
3223 g_slist_free(*msgnum_list);
3225 nummsgs = get_list_of_uids(session, folder, item, &uidlist);
3228 *msgnum_list = uidlist;
3230 dir = folder_item_get_path((FolderItem *)item);
3231 debug_print("removing old messages from %s\n", dir);
3232 remove_numbered_files_not_in_list(dir, *msgnum_list);
3235 item->uid_next = uid_next;
3237 debug_print("get_num_list - ok - %i\n", nummsgs);
3238 statusbar_pop_all();
3243 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item)
3248 flags.perm_flags = MSG_NEW|MSG_UNREAD;
3249 flags.tmp_flags = 0;
3251 g_return_val_if_fail(item != NULL, NULL);
3252 g_return_val_if_fail(file != NULL, NULL);
3254 if (folder_has_parent_of_type(item, F_QUEUE)) {
3255 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
3256 } else if (folder_has_parent_of_type(item, F_DRAFT)) {
3257 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
3260 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3261 if (!msginfo) return NULL;
3263 msginfo->plaintext_file = g_strdup(file);
3264 msginfo->folder = item;
3269 GSList *imap_get_msginfos(Folder *folder, FolderItem *item,
3270 GSList *msgnum_list)
3272 IMAPSession *session;
3273 MsgInfoList *ret = NULL;
3276 debug_print("get_msginfos\n");
3278 g_return_val_if_fail(folder != NULL, NULL);
3279 g_return_val_if_fail(item != NULL, NULL);
3280 g_return_val_if_fail(msgnum_list != NULL, NULL);
3282 session = imap_session_get(folder);
3283 g_return_val_if_fail(session != NULL, NULL);
3285 debug_print("IMAP getting msginfos\n");
3286 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3287 NULL, NULL, NULL, NULL, FALSE);
3288 if (ok != IMAP_SUCCESS) {
3292 if (!(folder_has_parent_of_type(item, F_DRAFT) ||
3293 folder_has_parent_of_type(item, F_QUEUE))) {
3294 ret = g_slist_concat(ret,
3295 imap_get_uncached_messages(session, item,
3298 MsgNumberList *sorted_list, *elem, *llast = NULL;
3299 gint startnum, lastnum;
3301 sorted_list = g_slist_sort(g_slist_copy(msgnum_list), g_int_compare);
3303 startnum = lastnum = GPOINTER_TO_INT(sorted_list->data);
3305 llast = g_slist_last(ret);
3306 for (elem = sorted_list;; elem = g_slist_next(elem)) {
3310 num = GPOINTER_TO_INT(elem->data);
3312 if (num > lastnum + 1 || elem == NULL) {
3314 for (i = startnum; i <= lastnum; ++i) {
3317 file = imap_fetch_msg(folder, item, i);
3319 MsgInfo *msginfo = imap_parse_msg(file, item);
3320 if (msginfo != NULL) {
3321 msginfo->msgnum = i;
3323 llast = ret = g_slist_append(ret, msginfo);
3325 llast = g_slist_append(llast, msginfo);
3326 llast = llast->next;
3331 session_set_access_time(SESSION(session));
3342 g_slist_free(sorted_list);
3348 MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid)
3350 MsgInfo *msginfo = NULL;
3351 MsgInfoList *msginfolist;
3352 MsgNumberList numlist;
3354 numlist.next = NULL;
3355 numlist.data = GINT_TO_POINTER(uid);
3357 msginfolist = imap_get_msginfos(folder, item, &numlist);
3358 if (msginfolist != NULL) {
3359 msginfo = msginfolist->data;
3360 g_slist_free(msginfolist);
3366 gboolean imap_scan_required(Folder *folder, FolderItem *_item)
3368 IMAPSession *session;
3369 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3370 gint ok, exists = 0, unseen = 0;
3371 guint32 uid_next, uid_val;
3372 gboolean selected_folder;
3374 g_return_val_if_fail(folder != NULL, FALSE);
3375 g_return_val_if_fail(item != NULL, FALSE);
3376 g_return_val_if_fail(item->item.folder != NULL, FALSE);
3377 g_return_val_if_fail(FOLDER_CLASS(item->item.folder) == &imap_class, FALSE);
3379 if (item->item.path == NULL)
3382 session = imap_session_get(folder);
3383 g_return_val_if_fail(session != NULL, FALSE);
3385 selected_folder = (session->mbox != NULL) &&
3386 (!strcmp(session->mbox, item->item.path));
3387 if (selected_folder && time(NULL) - item->use_cache < 2) {
3388 ok = imap_cmd_noop(session);
3389 if (ok != IMAP_SUCCESS) {
3390 debug_print("disconnected!\n");
3391 session = imap_reconnect_if_possible(folder, session);
3392 if (session == NULL)
3397 if (session->folder_content_changed
3398 || session->exists != item->item.total_msgs) {
3403 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path, IMAP_FOLDER_ITEM(item),
3404 &exists, &uid_next, &uid_val, &unseen, FALSE);
3405 if (ok != IMAP_SUCCESS) {
3410 item->use_cache = time(NULL);
3411 item->c_messages = exists;
3412 item->c_uid_next = uid_next;
3413 item->c_uid_validity = uid_val;
3414 item->c_unseen = unseen;
3415 item->item.last_num = uid_next - 1;
3416 debug_print("uidnext %d, item->uid_next %d, exists %d, item->item.total_msgs %d\n",
3417 uid_next, item->uid_next, exists, item->item.total_msgs);
3418 if ((uid_next != item->uid_next) || (exists != item->item.total_msgs)
3419 || unseen != item->item.unread_msgs || uid_val != item->item.mtime) {
3428 void imap_change_flags(Folder *folder, FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
3430 IMAPSession *session;
3431 IMAPFlags flags_set = 0, flags_unset = 0;
3432 gint ok = IMAP_SUCCESS;
3433 MsgNumberList numlist;
3434 hashtable_data *ht_data = NULL;
3436 g_return_if_fail(folder != NULL);
3437 g_return_if_fail(folder->klass == &imap_class);
3438 g_return_if_fail(item != NULL);
3439 g_return_if_fail(item->folder == folder);
3440 g_return_if_fail(msginfo != NULL);
3441 g_return_if_fail(msginfo->folder == item);
3443 session = imap_session_get(folder);
3448 if (!MSG_IS_MARKED(msginfo->flags) && (newflags & MSG_MARKED))
3449 flags_set |= IMAP_FLAG_FLAGGED;
3450 if ( MSG_IS_MARKED(msginfo->flags) && !(newflags & MSG_MARKED))
3451 flags_unset |= IMAP_FLAG_FLAGGED;
3453 if (!MSG_IS_UNREAD(msginfo->flags) && (newflags & MSG_UNREAD))
3454 flags_unset |= IMAP_FLAG_SEEN;
3455 if ( MSG_IS_UNREAD(msginfo->flags) && !(newflags & MSG_UNREAD))
3456 flags_set |= IMAP_FLAG_SEEN;
3458 if (!MSG_IS_REPLIED(msginfo->flags) && (newflags & MSG_REPLIED))
3459 flags_set |= IMAP_FLAG_ANSWERED;
3460 if ( MSG_IS_REPLIED(msginfo->flags) && !(newflags & MSG_REPLIED))
3461 flags_unset |= IMAP_FLAG_ANSWERED;
3463 if (!MSG_IS_DELETED(msginfo->flags) && (newflags & MSG_DELETED))
3464 flags_set |= IMAP_FLAG_DELETED;
3465 if ( MSG_IS_DELETED(msginfo->flags) && !(newflags & MSG_DELETED))
3466 flags_unset |= IMAP_FLAG_DELETED;
3468 if (!flags_set && !flags_unset) {
3469 /* the changed flags were not translatable to IMAP-speak.
3470 * like MSG_POSTFILTERED, so just apply. */
3471 msginfo->flags.perm_flags = newflags;
3476 if ((ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
3477 NULL, NULL, NULL, NULL, FALSE)) != IMAP_SUCCESS) {
3481 numlist.next = NULL;
3482 numlist.data = GINT_TO_POINTER(msginfo->msgnum);
3484 if (IMAP_FOLDER_ITEM(item)->batching) {
3485 /* instead of performing an UID STORE command for each message change,
3486 * as a lot of them can change "together", we just fill in hashtables
3487 * and defer the treatment so that we're able to send only one
3490 debug_print("IMAP batch mode on, deferring flags change\n");
3492 ht_data = g_hash_table_lookup(IMAP_FOLDER_ITEM(item)->flags_set_table,
3493 GINT_TO_POINTER(flags_set));
3494 if (ht_data == NULL) {
3495 ht_data = g_new0(hashtable_data, 1);
3496 ht_data->session = session;
3497 ht_data->item = IMAP_FOLDER_ITEM(item);
3498 g_hash_table_insert(IMAP_FOLDER_ITEM(item)->flags_set_table,
3499 GINT_TO_POINTER(flags_set), ht_data);
3501 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
3502 ht_data->msglist = g_slist_prepend(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum));
3505 ht_data = g_hash_table_lookup(IMAP_FOLDER_ITEM(item)->flags_unset_table,
3506 GINT_TO_POINTER(flags_unset));
3507 if (ht_data == NULL) {
3508 ht_data = g_new0(hashtable_data, 1);
3509 ht_data->session = session;
3510 ht_data->item = IMAP_FOLDER_ITEM(item);
3511 g_hash_table_insert(IMAP_FOLDER_ITEM(item)->flags_unset_table,
3512 GINT_TO_POINTER(flags_unset), ht_data);
3514 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
3515 ht_data->msglist = g_slist_prepend(ht_data->msglist,
3516 GINT_TO_POINTER(msginfo->msgnum));
3519 debug_print("IMAP changing flags\n");
3521 ok = imap_set_message_flags(session, &numlist, flags_set, TRUE);
3522 if (ok != IMAP_SUCCESS) {
3529 ok = imap_set_message_flags(session, &numlist, flags_unset, FALSE);
3530 if (ok != IMAP_SUCCESS) {
3536 msginfo->flags.perm_flags = newflags;
3541 static gint imap_remove_msg(Folder *folder, FolderItem *item, gint uid)
3544 IMAPSession *session;
3546 MsgNumberList numlist;
3548 g_return_val_if_fail(folder != NULL, -1);
3549 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3550 g_return_val_if_fail(item != NULL, -1);
3552 session = imap_session_get(folder);
3553 if (!session) return -1;
3555 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3556 NULL, NULL, NULL, NULL, FALSE);
3557 if (ok != IMAP_SUCCESS) {
3561 numlist.next = NULL;
3562 numlist.data = GINT_TO_POINTER(uid);
3564 ok = imap_set_message_flags
3565 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
3566 &numlist, IMAP_FLAG_DELETED, TRUE);
3567 if (ok != IMAP_SUCCESS) {
3568 log_warning(_("can't set deleted flags: %d\n"), uid);
3573 if (!session->uidplus) {
3574 ok = imap_cmd_expunge(session);
3578 uidstr = g_strdup_printf("%u", uid);
3579 ok = imap_cmd_expunge(session);
3582 if (ok != IMAP_SUCCESS) {
3583 log_warning(_("can't expunge\n"));
3588 IMAP_FOLDER_ITEM(item)->uid_list = g_slist_remove(
3589 IMAP_FOLDER_ITEM(item)->uid_list, numlist.data);
3590 dir = folder_item_get_path(item);
3591 if (is_dir_exist(dir))
3592 remove_numbered_files(dir, uid, uid);
3595 return IMAP_SUCCESS;
3598 static gint compare_msginfo(gconstpointer a, gconstpointer b)
3600 return ((MsgInfo *)a)->msgnum - ((MsgInfo *)b)->msgnum;
3603 static guint gslist_find_next_num(MsgNumberList **list, guint num)
3607 g_return_val_if_fail(list != NULL, -1);
3609 for (elem = *list; elem != NULL; elem = g_slist_next(elem))
3610 if (GPOINTER_TO_INT(elem->data) >= num)
3613 return elem != NULL ? GPOINTER_TO_INT(elem->data) : (gint)-1;
3617 * NEW and DELETED flags are not syncronized
3618 * - The NEW/RECENT flags in IMAP folders can not really be directly
3619 * modified by Sylpheed
3620 * - The DELETE/DELETED flag in IMAP and Sylpheed don't have the same
3621 * meaning, in IMAP it always removes the messages from the FolderItem
3622 * in Sylpheed it can mean to move the message to trash
3625 typedef struct _get_flags_data {
3628 MsgInfoList *msginfo_list;
3629 GRelation *msgflags;
3630 gboolean full_search;
3634 static /*gint*/ void *imap_get_flags_thread(void *data)
3636 get_flags_data *stuff = (get_flags_data *)data;
3637 Folder *folder = stuff->folder;
3638 FolderItem *item = stuff->item;
3639 MsgInfoList *msginfo_list = stuff->msginfo_list;
3640 GRelation *msgflags = stuff->msgflags;
3641 gboolean full_search = stuff->full_search;
3642 IMAPSession *session;
3643 GSList *sorted_list = NULL;
3644 GSList *unseen = NULL, *answered = NULL, *flagged = NULL, *deleted = NULL;
3645 GSList *p_unseen, *p_answered, *p_flagged, *p_deleted;
3647 GSList *seq_list, *cur;
3648 gboolean reverse_seen = FALSE;
3651 gint exists_cnt, unseen_cnt;
3652 gboolean selected_folder;
3654 if (folder == NULL || item == NULL) {
3656 return GINT_TO_POINTER(-1);
3659 session = imap_session_get(folder);
3660 if (session == NULL) {
3662 return GINT_TO_POINTER(-1);
3665 selected_folder = (session->mbox != NULL) &&
3666 (!strcmp(session->mbox, item->path));
3668 if (!selected_folder) {
3669 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3670 &exists_cnt, NULL, &unseen_cnt, NULL, TRUE);
3671 if (ok != IMAP_SUCCESS) {
3674 return GINT_TO_POINTER(-1);
3677 if (unseen_cnt > exists_cnt / 2)
3678 reverse_seen = TRUE;
3681 if (item->unread_msgs > item->total_msgs / 2)
3682 reverse_seen = TRUE;
3685 cmd_buf = g_string_new(NULL);
3687 sorted_list = g_slist_sort(g_slist_copy(msginfo_list), compare_msginfo);
3689 seq_list = imap_get_lep_set_from_msglist(msginfo_list);
3691 struct mailimap_set * set;
3692 set = mailimap_set_new_interval(1, 0);
3693 seq_list = g_slist_append(NULL, set);
3696 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
3697 struct mailimap_set * imapset;
3698 clist * lep_uidlist;
3701 imapset = cur->data;
3703 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SEEN,
3704 full_search ? NULL:imapset, &lep_uidlist);
3707 r = imap_threaded_search(folder,
3708 IMAP_SEARCH_TYPE_UNSEEN,
3709 full_search ? NULL:imapset, &lep_uidlist);
3711 if (r == MAILIMAP_NO_ERROR) {
3714 uidlist = imap_uid_list_from_lep(lep_uidlist);
3715 mailimap_search_result_free(lep_uidlist);
3717 unseen = g_slist_concat(unseen, uidlist);
3720 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_FLAGGED,
3721 full_search ? NULL:imapset, &lep_uidlist);
3722 if (r == MAILIMAP_NO_ERROR) {
3725 uidlist = imap_uid_list_from_lep(lep_uidlist);
3726 mailimap_search_result_free(lep_uidlist);
3728 flagged = g_slist_concat(flagged, uidlist);
3731 if (item->opened || item->processing_pending || item == folder->inbox) {
3732 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_ANSWERED,
3733 full_search ? NULL:imapset, &lep_uidlist);
3734 if (r == MAILIMAP_NO_ERROR) {
3737 uidlist = imap_uid_list_from_lep(lep_uidlist);
3738 mailimap_search_result_free(lep_uidlist);
3740 answered = g_slist_concat(answered, uidlist);
3743 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_DELETED,
3744 full_search ? NULL:imapset, &lep_uidlist);
3745 if (r == MAILIMAP_NO_ERROR) {
3748 uidlist = imap_uid_list_from_lep(lep_uidlist);
3749 mailimap_search_result_free(lep_uidlist);
3751 deleted = g_slist_concat(deleted, uidlist);
3757 p_answered = answered;
3758 p_flagged = flagged;
3759 p_deleted = deleted;
3761 for (elem = sorted_list; elem != NULL; elem = g_slist_next(elem)) {
3766 msginfo = (MsgInfo *) elem->data;
3767 flags = msginfo->flags.perm_flags;
3768 wasnew = (flags & MSG_NEW);
3769 if (item->opened || item->processing_pending || item == folder->inbox) {
3770 flags &= ~((reverse_seen ? 0 : MSG_UNREAD | MSG_NEW) | MSG_REPLIED | MSG_MARKED);
3772 flags &= ~((reverse_seen ? 0 : MSG_UNREAD | MSG_NEW | MSG_MARKED));
3775 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
3776 if (gslist_find_next_num(&p_unseen, msginfo->msgnum) == msginfo->msgnum) {
3777 if (!reverse_seen) {
3778 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
3780 flags &= ~(MSG_UNREAD | MSG_NEW);
3784 if (gslist_find_next_num(&p_flagged, msginfo->msgnum) == msginfo->msgnum)
3785 flags |= MSG_MARKED;
3787 flags &= ~MSG_MARKED;
3789 if (item->opened || item->processing_pending || item == folder->inbox) {
3790 if (gslist_find_next_num(&p_answered, msginfo->msgnum) == msginfo->msgnum)
3791 flags |= MSG_REPLIED;
3793 flags &= ~MSG_REPLIED;
3794 if (gslist_find_next_num(&p_deleted, msginfo->msgnum) == msginfo->msgnum)
3795 flags |= MSG_DELETED;
3797 flags &= ~MSG_DELETED;
3799 g_relation_insert(msgflags, msginfo, GINT_TO_POINTER(flags));
3802 imap_lep_set_free(seq_list);
3803 g_slist_free(flagged);
3804 g_slist_free(deleted);
3805 g_slist_free(answered);
3806 g_slist_free(unseen);
3807 g_slist_free(sorted_list);
3808 g_string_free(cmd_buf, TRUE);
3812 return GINT_TO_POINTER(0);
3815 static gint imap_get_flags(Folder *folder, FolderItem *item,
3816 MsgInfoList *msginfo_list, GRelation *msgflags)
3819 get_flags_data *data = g_new0(get_flags_data, 1);
3821 data->folder = folder;
3823 data->msginfo_list = msginfo_list;
3824 data->msgflags = msgflags;
3825 data->full_search = FALSE;
3827 GSList *tmp = NULL, *cur;
3829 if (prefs_common.work_offline &&
3830 !inc_offline_should_override(
3831 _("Sylpheed-Claws needs network access in order "
3832 "to access the IMAP server."))) {
3837 tmp = folder_item_get_msg_list(item);
3839 if (g_slist_length(tmp) <= g_slist_length(msginfo_list))
3840 data->full_search = TRUE;
3842 for (cur = tmp; cur; cur = cur->next)
3843 procmsg_msginfo_free((MsgInfo *)cur->data);
3847 result = GPOINTER_TO_INT(imap_get_flags_thread(data));
3854 static gboolean process_flags(gpointer key, gpointer value, gpointer user_data)
3856 gboolean flags_set = GPOINTER_TO_INT(user_data);
3857 gint flags_value = GPOINTER_TO_INT(key);
3858 hashtable_data *data = (hashtable_data *)value;
3859 IMAPFolderItem *_item = data->item;
3860 FolderItem *item = (FolderItem *)_item;
3861 gint ok = IMAP_ERROR;
3862 IMAPSession *session = imap_session_get(item->folder);
3864 data->msglist = g_slist_reverse(data->msglist);
3866 debug_print("IMAP %ssetting flags to %d for %d messages\n",
3869 g_slist_length(data->msglist));
3873 ok = imap_select(session, IMAP_FOLDER(item->folder), item->path,
3874 NULL, NULL, NULL, NULL, FALSE);
3876 if (ok == IMAP_SUCCESS) {
3877 imap_set_message_flags(data->session, data->msglist, flags_value, flags_set);
3879 g_warning("can't select mailbox %s\n", item->path);
3883 g_slist_free(data->msglist);
3888 static void process_hashtable(IMAPFolderItem *item)
3890 if (item->flags_set_table) {
3891 g_hash_table_foreach_remove(item->flags_set_table, process_flags, GINT_TO_POINTER(TRUE));
3892 g_hash_table_destroy(item->flags_set_table);
3893 item->flags_set_table = NULL;
3895 if (item->flags_unset_table) {
3896 g_hash_table_foreach_remove(item->flags_unset_table, process_flags, GINT_TO_POINTER(FALSE));
3897 g_hash_table_destroy(item->flags_unset_table);
3898 item->flags_unset_table = NULL;
3902 static void imap_set_batch (Folder *folder, FolderItem *_item, gboolean batch)
3904 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3906 g_return_if_fail(item != NULL);
3908 if (item->batching == batch)
3912 item->batching = TRUE;
3913 debug_print("IMAP switching to batch mode\n");
3914 if (!item->flags_set_table) {
3915 item->flags_set_table = g_hash_table_new(NULL, g_direct_equal);
3917 if (!item->flags_unset_table) {
3918 item->flags_unset_table = g_hash_table_new(NULL, g_direct_equal);
3921 debug_print("IMAP switching away from batch mode\n");
3923 process_hashtable(item);
3924 item->batching = FALSE;
3930 /* data types conversion libetpan <-> sylpheed */
3934 #define ETPAN_IMAP_MB_MARKED 1
3935 #define ETPAN_IMAP_MB_UNMARKED 2
3936 #define ETPAN_IMAP_MB_NOSELECT 4
3937 #define ETPAN_IMAP_MB_NOINFERIORS 8
3939 static int imap_flags_to_flags(struct mailimap_mbx_list_flags * imap_flags)
3945 if (imap_flags->mbf_type == MAILIMAP_MBX_LIST_FLAGS_SFLAG) {
3946 switch (imap_flags->mbf_sflag) {
3947 case MAILIMAP_MBX_LIST_SFLAG_MARKED:
3948 flags |= ETPAN_IMAP_MB_MARKED;
3950 case MAILIMAP_MBX_LIST_SFLAG_NOSELECT:
3951 flags |= ETPAN_IMAP_MB_NOSELECT;
3953 case MAILIMAP_MBX_LIST_SFLAG_UNMARKED:
3954 flags |= ETPAN_IMAP_MB_UNMARKED;
3959 for(cur = clist_begin(imap_flags->mbf_oflags) ; cur != NULL ;
3960 cur = clist_next(cur)) {
3961 struct mailimap_mbx_list_oflag * oflag;
3963 oflag = clist_content(cur);
3965 switch (oflag->of_type) {
3966 case MAILIMAP_MBX_LIST_OFLAG_NOINFERIORS:
3967 flags |= ETPAN_IMAP_MB_NOINFERIORS;
3975 static GSList * imap_list_from_lep(IMAPFolder * folder,
3976 clist * list, const gchar * real_path, gboolean all)
3979 GSList * item_list = NULL, *llast = NULL;
3981 for(iter = clist_begin(list) ; iter != NULL ;
3982 iter = clist_next(iter)) {
3983 struct mailimap_mailbox_list * mb;
3991 FolderItem *new_item;
3993 mb = clist_content(iter);
3999 if (mb->mb_flag != NULL)
4000 flags = imap_flags_to_flags(mb->mb_flag);
4002 delimiter = mb->mb_delimiter;
4005 dup_name = strdup(name);
4006 if (delimiter != '\0')
4007 subst_char(dup_name, delimiter, '/');
4009 base = g_path_get_basename(dup_name);
4010 if (base[0] == '.') {
4016 if (!all && strcmp(dup_name, real_path) == 0) {
4022 if (!all && dup_name[strlen(dup_name)-1] == '/') {
4028 loc_name = imap_modified_utf7_to_utf8(base);
4029 loc_path = imap_modified_utf7_to_utf8(dup_name);
4031 new_item = folder_item_new(FOLDER(folder), loc_name, loc_path);
4032 if ((flags & ETPAN_IMAP_MB_NOINFERIORS) != 0)
4033 new_item->no_sub = TRUE;
4034 if (strcmp(dup_name, "INBOX") != 0 &&
4035 ((flags & ETPAN_IMAP_MB_NOSELECT) != 0))
4036 new_item->no_select = TRUE;
4038 if (item_list == NULL)
4039 llast = item_list = g_slist_append(item_list, new_item);
4041 llast = g_slist_append(llast, new_item);
4042 llast = llast->next;
4044 debug_print("folder '%s' found.\n", loc_path);
4055 static GSList * imap_get_lep_set_from_numlist(MsgNumberList *numlist)
4057 GSList *sorted_list, *cur;
4058 guint first, last, next;
4059 GSList *ret_list = NULL, *llast = NULL;
4061 struct mailimap_set * current_set;
4062 unsigned int item_count;
4064 if (numlist == NULL)
4068 current_set = mailimap_set_new_empty();
4070 sorted_list = g_slist_copy(numlist);
4071 sorted_list = g_slist_sort(sorted_list, g_int_compare);
4073 first = GPOINTER_TO_INT(sorted_list->data);
4076 for (cur = sorted_list; cur != NULL; cur = g_slist_next(cur)) {
4077 if (GPOINTER_TO_INT(cur->data) == 0)
4082 last = GPOINTER_TO_INT(cur->data);
4084 next = GPOINTER_TO_INT(cur->next->data);
4088 if (last + 1 != next || next == 0) {
4090 struct mailimap_set_item * item;
4091 item = mailimap_set_item_new(first, last);
4092 mailimap_set_add(current_set, item);
4097 if (count >= IMAP_SET_MAX_COUNT) {
4098 if (ret_list == NULL)
4099 llast = ret_list = g_slist_append(ret_list,
4102 llast = g_slist_append(llast, current_set);
4103 llast = llast->next;
4105 current_set = mailimap_set_new_empty();
4112 if (clist_count(current_set->set_list) > 0) {
4113 ret_list = g_slist_append(ret_list,
4117 g_slist_free(sorted_list);
4122 static GSList * imap_get_lep_set_from_msglist(MsgInfoList *msglist)
4124 MsgNumberList *numlist = NULL;
4128 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
4129 MsgInfo *msginfo = (MsgInfo *) cur->data;
4131 numlist = g_slist_prepend(numlist, GINT_TO_POINTER(msginfo->msgnum));
4133 numlist = g_slist_reverse(numlist);
4134 seq_list = imap_get_lep_set_from_numlist(numlist);
4135 g_slist_free(numlist);
4140 static GSList * imap_uid_list_from_lep(clist * list)
4147 for(iter = clist_begin(list) ; iter != NULL ;
4148 iter = clist_next(iter)) {
4151 puid = clist_content(iter);
4152 result = g_slist_prepend(result, GINT_TO_POINTER(* puid));
4155 result = g_slist_reverse(result);
4159 static GSList * imap_uid_list_from_lep_tab(carray * list)
4166 for(i = 0 ; i < carray_count(list) ; i ++) {
4169 puid = carray_get(list, i);
4170 result = g_slist_prepend(result, GINT_TO_POINTER(* puid));
4172 result = g_slist_reverse(result);
4176 static MsgInfo *imap_envelope_from_lep(struct imap_fetch_env_info * info,
4179 MsgInfo *msginfo = NULL;
4182 MsgFlags flags = {0, 0};
4184 if (info->headers == NULL)
4187 MSG_SET_TMP_FLAGS(flags, MSG_IMAP);
4188 if (folder_has_parent_of_type(item, F_QUEUE)) {
4189 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
4190 } else if (folder_has_parent_of_type(item, F_DRAFT)) {
4191 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
4193 flags.perm_flags = info->flags;
4197 msginfo = procheader_parse_str(info->headers, flags, FALSE, FALSE);
4200 msginfo->msgnum = uid;
4201 msginfo->size = size;
4207 static void imap_lep_set_free(GSList *seq_list)
4211 for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) {
4212 struct mailimap_set * imapset;
4214 imapset = cur->data;
4215 mailimap_set_free(imapset);
4217 g_slist_free(seq_list);
4220 static struct mailimap_flag_list * imap_flag_to_lep(IMAPFlags flags)
4222 struct mailimap_flag_list * flag_list;
4224 flag_list = mailimap_flag_list_new_empty();
4226 if (IMAP_IS_SEEN(flags))
4227 mailimap_flag_list_add(flag_list,
4228 mailimap_flag_new_seen());
4229 if (IMAP_IS_ANSWERED(flags))
4230 mailimap_flag_list_add(flag_list,
4231 mailimap_flag_new_answered());
4232 if (IMAP_IS_FLAGGED(flags))
4233 mailimap_flag_list_add(flag_list,
4234 mailimap_flag_new_flagged());
4235 if (IMAP_IS_DELETED(flags))
4236 mailimap_flag_list_add(flag_list,
4237 mailimap_flag_new_deleted());
4238 if (IMAP_IS_DRAFT(flags))
4239 mailimap_flag_list_add(flag_list,
4240 mailimap_flag_new_draft());
4245 guint imap_folder_get_refcnt(Folder *folder)
4247 return ((IMAPFolder *)folder)->refcnt;
4250 void imap_folder_ref(Folder *folder)
4252 ((IMAPFolder *)folder)->refcnt++;
4255 void imap_disconnect_all(void)
4258 for (list = account_get_list(); list != NULL; list = list->next) {
4259 PrefsAccount *account = list->data;
4260 if (account->protocol == A_IMAP4) {
4261 RemoteFolder *folder = (RemoteFolder *)account->folder;
4262 if (folder && folder->session) {
4263 IMAPSession *session = (IMAPSession *)folder->session;
4264 imap_threaded_disconnect(FOLDER(folder));
4265 session_destroy(SESSION(session));
4266 folder->session = NULL;
4272 void imap_folder_unref(Folder *folder)
4274 if (((IMAPFolder *)folder)->refcnt > 0)
4275 ((IMAPFolder *)folder)->refcnt--;
4278 #else /* HAVE_LIBETPAN */
4280 static FolderClass imap_class;
4282 static XMLTag *imap_item_get_xml(Folder *folder, FolderItem *item);
4283 static void imap_item_set_xml(Folder *folder, FolderItem *item, XMLTag *tag);
4285 static Folder *imap_folder_new (const gchar *name,
4290 static gint imap_create_tree (Folder *folder)
4294 static FolderItem *imap_create_folder (Folder *folder,
4300 static gint imap_rename_folder (Folder *folder,
4307 gchar imap_get_path_separator_for_item(FolderItem *item)
4312 FolderClass *imap_get_class(void)
4314 if (imap_class.idstr == NULL) {
4315 imap_class.type = F_IMAP;
4316 imap_class.idstr = "imap";
4317 imap_class.uistr = "IMAP4";
4319 imap_class.new_folder = imap_folder_new;
4320 imap_class.create_tree = imap_create_tree;
4321 imap_class.create_folder = imap_create_folder;
4322 imap_class.rename_folder = imap_rename_folder;
4324 imap_class.set_xml = folder_set_xml;
4325 imap_class.get_xml = folder_get_xml;
4326 imap_class.item_set_xml = imap_item_set_xml;
4327 imap_class.item_get_xml = imap_item_get_xml;
4328 /* nothing implemented */
4334 void imap_disconnect_all(void)
4340 void imap_synchronise(FolderItem *item)
4342 imap_gtk_synchronise(item);
4345 static void imap_item_set_xml(Folder *folder, FolderItem *item, XMLTag *tag)
4347 #ifdef HAVE_LIBETPAN
4350 folder_item_set_xml(folder, item, tag);
4352 #ifdef HAVE_LIBETPAN
4353 for (cur = tag->attr; cur != NULL; cur = g_list_next(cur)) {
4354 XMLAttr *attr = (XMLAttr *) cur->data;
4356 if (!attr || !attr->name || !attr->value) continue;
4357 if (!strcmp(attr->name, "uidnext"))
4358 IMAP_FOLDER_ITEM(item)->uid_next = atoi(attr->value);
4363 static XMLTag *imap_item_get_xml(Folder *folder, FolderItem *item)
4367 tag = folder_item_get_xml(folder, item);
4369 #ifdef HAVE_LIBETPAN
4370 xml_tag_add_attr(tag, xml_attr_new_int("uidnext",
4371 IMAP_FOLDER_ITEM(item)->uid_next));