2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2007 Hiroyuki Yamamoto and the Claws Mail 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>
34 #include "alertpanel.h"
57 #include "procheader.h"
58 #include "prefs_account.h"
63 #include "prefs_common.h"
64 #include "inputdialog.h"
66 #include "remotefolder.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,
260 static void imap_create_missing_folders (Folder *folder);
261 static FolderItem *imap_create_special_folder
263 SpecialFolderItemType stype,
266 static gint imap_do_copy_msgs (Folder *folder,
268 MsgInfoList *msglist,
269 GRelation *relation);
271 static void imap_delete_all_cached_messages (FolderItem *item);
272 static void imap_set_batch (Folder *folder,
275 static gint imap_set_message_flags (IMAPSession *session,
276 MsgNumberList *numlist,
279 static gint imap_select (IMAPSession *session,
285 guint32 *uid_validity,
287 static gint imap_status (IMAPSession *session,
290 IMAPFolderItem *item,
293 guint32 *uid_validity,
297 static gchar imap_get_path_separator (IMAPSession *session,
300 static gchar *imap_get_real_path (IMAPSession *session,
303 static void imap_synchronise (FolderItem *item);
305 static void imap_free_capabilities (IMAPSession *session);
307 /* low-level IMAP4rev1 commands */
308 static gint imap_cmd_login (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 struct mailimap_set ** source,
352 struct mailimap_set ** dest);
353 static gint imap_cmd_store (IMAPSession *session,
354 struct mailimap_set * set,
357 static gint imap_cmd_expunge (IMAPSession *session);
359 static void imap_path_separator_subst (gchar *str,
362 static gchar *imap_utf8_to_modified_utf7 (const gchar *from);
363 static gchar *imap_modified_utf7_to_utf8 (const gchar *mutf7_str);
365 static gboolean imap_rename_folder_func (GNode *node,
367 static gint imap_get_num_list (Folder *folder,
370 gboolean *old_uids_valid);
371 static GSList *imap_get_msginfos (Folder *folder,
373 GSList *msgnum_list);
374 static MsgInfo *imap_get_msginfo (Folder *folder,
377 static gboolean imap_scan_required (Folder *folder,
379 static void imap_change_flags (Folder *folder,
382 MsgPermFlags newflags);
383 static gint imap_get_flags (Folder *folder,
385 MsgInfoList *msglist,
386 GRelation *msgflags);
387 static gchar *imap_folder_get_path (Folder *folder);
388 static gchar *imap_item_get_path (Folder *folder,
390 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item);
393 /* data types conversion libetpan <-> claws */
394 static GSList * imap_list_from_lep(IMAPFolder * folder,
395 clist * list, const gchar * real_path, gboolean all);
396 static GSList * imap_get_lep_set_from_numlist(MsgNumberList *numlist);
397 static GSList * imap_get_lep_set_from_msglist(MsgInfoList *msglist);
398 static GSList * imap_uid_list_from_lep(clist * list);
399 static GSList * imap_uid_list_from_lep_tab(carray * list);
400 static MsgInfo *imap_envelope_from_lep(struct imap_fetch_env_info * info,
402 static void imap_lep_set_free(GSList *seq_list);
403 static struct mailimap_flag_list * imap_flag_to_lep(IMAPFlags flags);
405 typedef struct _hashtable_data {
406 IMAPSession *session;
408 IMAPFolderItem *item;
411 static FolderClass imap_class;
413 typedef struct _thread_data {
423 FolderClass *imap_get_class(void)
425 if (imap_class.idstr == NULL) {
426 imap_class.type = F_IMAP;
427 imap_class.idstr = "imap";
428 imap_class.uistr = "IMAP4";
430 /* Folder functions */
431 imap_class.new_folder = imap_folder_new;
432 imap_class.destroy_folder = imap_folder_destroy;
433 imap_class.scan_tree = imap_scan_tree;
434 imap_class.create_tree = imap_create_tree;
436 /* FolderItem functions */
437 imap_class.item_new = imap_folder_item_new;
438 imap_class.item_destroy = imap_folder_item_destroy;
439 imap_class.item_get_path = imap_item_get_path;
440 imap_class.create_folder = imap_create_folder;
441 imap_class.rename_folder = imap_rename_folder;
442 imap_class.remove_folder = imap_remove_folder;
443 imap_class.close = imap_close;
444 imap_class.get_num_list = imap_get_num_list;
445 imap_class.scan_required = imap_scan_required;
446 imap_class.set_xml = folder_set_xml;
447 imap_class.get_xml = folder_get_xml;
448 imap_class.item_set_xml = imap_item_set_xml;
449 imap_class.item_get_xml = imap_item_get_xml;
451 /* Message functions */
452 imap_class.get_msginfo = imap_get_msginfo;
453 imap_class.get_msginfos = imap_get_msginfos;
454 imap_class.fetch_msg = imap_fetch_msg;
455 imap_class.fetch_msg_full = imap_fetch_msg_full;
456 imap_class.add_msg = imap_add_msg;
457 imap_class.add_msgs = imap_add_msgs;
458 imap_class.copy_msg = imap_copy_msg;
459 imap_class.copy_msgs = imap_copy_msgs;
460 imap_class.remove_msg = imap_remove_msg;
461 imap_class.remove_msgs = imap_remove_msgs;
462 imap_class.remove_all_msg = imap_remove_all_msg;
463 imap_class.is_msg_changed = imap_is_msg_changed;
464 imap_class.change_flags = imap_change_flags;
465 imap_class.get_flags = imap_get_flags;
466 imap_class.set_batch = imap_set_batch;
467 imap_class.synchronise = imap_synchronise;
469 pthread_mutex_init(&imap_mutex, NULL);
476 static Folder *imap_folder_new(const gchar *name, const gchar *path)
480 folder = (Folder *)g_new0(IMAPFolder, 1);
481 folder->klass = &imap_class;
482 imap_folder_init(folder, name, path);
487 static void imap_folder_destroy(Folder *folder)
489 while (imap_folder_get_refcnt(folder) > 0)
490 gtk_main_iteration();
492 folder_remote_folder_destroy(REMOTE_FOLDER(folder));
496 static void imap_folder_init(Folder *folder, const gchar *name,
499 folder_remote_folder_init((Folder *)folder, name, path);
502 static FolderItem *imap_folder_item_new(Folder *folder)
504 IMAPFolderItem *item;
506 item = g_new0(IMAPFolderItem, 1);
509 item->uid_list = NULL;
511 return (FolderItem *)item;
514 static void imap_folder_item_destroy(Folder *folder, FolderItem *_item)
516 IMAPFolderItem *item = (IMAPFolderItem *)_item;
518 g_return_if_fail(item != NULL);
519 g_slist_free(item->uid_list);
524 static gboolean imap_reset_uid_lists_func(GNode *node, gpointer data)
526 IMAPFolderItem *item = (IMAPFolderItem *)node->data;
529 g_slist_free(item->uid_list);
530 item->uid_list = NULL;
535 static void imap_reset_uid_lists(Folder *folder)
537 if(folder->node == NULL)
540 /* Destroy all uid lists and rest last uid */
541 g_node_traverse(folder->node, G_IN_ORDER, G_TRAVERSE_ALL, -1, imap_reset_uid_lists_func, NULL);
544 static int imap_get_capabilities(IMAPSession *session)
546 struct mailimap_capability_data *capabilities = NULL;
550 if (session->capability != NULL)
551 return MAILIMAP_NO_ERROR;
553 capabilities = imap_threaded_capability(session->folder, &result);
555 if (result != MAILIMAP_NO_ERROR) {
556 return MAILIMAP_ERROR_CAPABILITY;
559 if (capabilities == NULL) {
560 return MAILIMAP_NO_ERROR;
563 for(cur = clist_begin(capabilities->cap_list) ; cur != NULL ;
564 cur = clist_next(cur)) {
565 struct mailimap_capability * cap =
567 if (!cap || cap->cap_data.cap_name == NULL)
569 session->capability = g_slist_append
570 (session->capability,
571 g_strdup(cap->cap_data.cap_name));
572 debug_print("got capa %s\n", cap->cap_data.cap_name);
574 mailimap_capability_data_free(capabilities);
575 return MAILIMAP_NO_ERROR;
578 static gboolean imap_has_capability(IMAPSession *session, const gchar *cap)
581 for (cur = session->capability; cur; cur = cur->next) {
582 if (!g_ascii_strcasecmp(cur->data, cap))
588 static gint imap_auth(IMAPSession *session, const gchar *user, const gchar *pass,
591 gint ok = IMAP_ERROR;
592 static time_t last_login_err = 0;
593 gchar *ext_info = "";
595 if (imap_get_capabilities(session) != MAILIMAP_NO_ERROR)
600 ok = imap_cmd_login(session, user, pass, "ANONYMOUS");
602 case IMAP_AUTH_CRAM_MD5:
603 ok = imap_cmd_login(session, user, pass, "CRAM-MD5");
605 case IMAP_AUTH_LOGIN:
606 ok = imap_cmd_login(session, user, pass, "LOGIN");
608 case IMAP_AUTH_GSSAPI:
609 ok = imap_cmd_login(session, user, pass, "GSSAPI");
612 debug_print("capabilities:\n"
617 imap_has_capability(session, "ANONYMOUS"),
618 imap_has_capability(session, "CRAM-MD5"),
619 imap_has_capability(session, "LOGIN"),
620 imap_has_capability(session, "GSSAPI"));
621 if (imap_has_capability(session, "CRAM-MD5"))
622 ok = imap_cmd_login(session, user, pass, "CRAM-MD5");
623 if (ok == IMAP_ERROR && imap_has_capability(session, "GSSAPI"))
624 ok = imap_cmd_login(session, user, pass, "GSSAPI");
625 if (ok == IMAP_ERROR) /* we always try LOGIN before giving up */
626 ok = imap_cmd_login(session, user, pass, "LOGIN");
629 if (ok == IMAP_SUCCESS)
630 session->authenticated = TRUE;
632 if (type == IMAP_AUTH_CRAM_MD5) {
633 ext_info = _("\n\nCRAM-MD5 logins only work if libetpan has been "
634 "compiled with SASL support and the "
635 "CRAM-MD5 SASL plugin is installed.");
638 if (time(NULL) - last_login_err > 10) {
639 if (!prefs_common.no_recv_err_panel) {
640 alertpanel_error(_("Connection to %s failed: "
642 SESSION(session)->server, ext_info);
644 log_error(LOG_PROTOCOL, _("Connection to %s failed: "
645 "login refused.%s\n"),
646 SESSION(session)->server, ext_info);
649 last_login_err = time(NULL);
654 static IMAPSession *imap_reconnect_if_possible(Folder *folder, IMAPSession *session)
656 RemoteFolder *rfolder = REMOTE_FOLDER(folder);
657 /* Check if this is the first try to establish a
658 connection, if yes we don't try to reconnect */
659 debug_print("reconnecting\n");
660 if (rfolder->session == NULL) {
661 log_warning(LOG_PROTOCOL, _("Connecting to %s failed"),
662 folder->account->recv_server);
663 session_destroy(SESSION(session));
666 log_warning(LOG_PROTOCOL, _("IMAP4 connection to %s has been"
667 " disconnected. Reconnecting...\n"),
668 folder->account->recv_server);
669 statusbar_print_all(_("IMAP4 connection to %s has been"
670 " disconnected. Reconnecting...\n"),
671 folder->account->recv_server);
672 SESSION(session)->state = SESSION_DISCONNECTED;
673 session_destroy(SESSION(session));
674 /* Clear folders session to make imap_session_get create
675 a new session, because of rfolder->session == NULL
676 it will not try to reconnect again and so avoid an
678 rfolder->session = NULL;
679 debug_print("getting session...\n");
680 session = imap_session_get(folder);
681 rfolder->session = SESSION(session);
687 #define lock_session() {\
689 debug_print("locking session %p (%d)\n", session, session->busy); \
691 debug_print(" SESSION WAS LOCKED !! \n"); \
692 session->busy = TRUE;\
694 debug_print("can't lock null session\n"); \
698 #define unlock_session() {\
700 debug_print("unlocking session %p\n", session); \
701 session->busy = FALSE;\
703 debug_print("can't unlock null session\n"); \
707 static IMAPSession *imap_session_get(Folder *folder)
709 RemoteFolder *rfolder = REMOTE_FOLDER(folder);
710 IMAPSession *session = NULL;
712 g_return_val_if_fail(folder != NULL, NULL);
713 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, NULL);
714 g_return_val_if_fail(folder->account != NULL, NULL);
716 if (prefs_common.work_offline &&
717 !inc_offline_should_override(FALSE,
718 _("Claws Mail needs network access in order "
719 "to access the IMAP server."))) {
723 /* Make sure we have a session */
724 if (rfolder->session != NULL) {
725 session = IMAP_SESSION(rfolder->session);
726 } else if (rfolder->connecting) {
727 debug_print("already connecting\n");
730 imap_reset_uid_lists(folder);
731 if (time(NULL) - rfolder->last_failure <= 2)
733 rfolder->connecting = TRUE;
734 session = imap_session_new(folder, folder->account);
736 if(session == NULL) {
737 rfolder->last_failure = time(NULL);
738 rfolder->connecting = FALSE;
742 /* Make sure session is authenticated */
743 if (!IMAP_SESSION(session)->authenticated)
744 imap_session_authenticate(IMAP_SESSION(session), folder->account);
746 if (!IMAP_SESSION(session)->authenticated) {
747 imap_threaded_disconnect(session->folder);
748 SESSION(session)->state = SESSION_DISCONNECTED;
749 session_destroy(SESSION(session));
750 rfolder->session = NULL;
751 rfolder->last_failure = time(NULL);
752 rfolder->connecting = FALSE;
758 /* I think the point of this code is to avoid sending a
759 * keepalive if we've used the session recently and therefore
760 * think it's still alive. Unfortunately, most of the code
761 * does not yet check for errors on the socket, and so if the
762 * connection drops we don't notice until the timeout expires.
763 * A better solution than sending a NOOP every time would be
764 * for every command to be prepared to retry until it is
765 * successfully sent. -- mbp */
766 if (time(NULL) - SESSION(session)->last_access_time > SESSION_TIMEOUT_INTERVAL) {
767 /* verify that the session is still alive */
768 if (imap_cmd_noop(session) != IMAP_SUCCESS) {
769 debug_print("disconnected!\n");
770 session = imap_reconnect_if_possible(folder, session);
774 rfolder->session = SESSION(session);
775 rfolder->connecting = FALSE;
777 return IMAP_SESSION(session);
780 static IMAPSession *imap_session_new(Folder * folder,
781 const PrefsAccount *account)
783 IMAPSession *session;
786 int authenticated = FALSE;
789 /* FIXME: IMAP over SSL only... */
792 port = account->set_imapport ? account->imapport
793 : account->ssl_imap == SSL_TUNNEL ? IMAPS_PORT : IMAP4_PORT;
794 ssl_type = account->ssl_imap;
796 if (account->ssl_imap != SSL_NONE) {
797 if (alertpanel_full(_("Insecure connection"),
798 _("This connection is configured to be secured "
799 "using SSL, but SSL is not available in this "
800 "build of Claws Mail. \n\n"
801 "Do you want to continue connecting to this "
802 "server? The communication would not be "
804 GTK_STOCK_CANCEL, _("Con_tinue connecting"),
805 NULL, FALSE, NULL, ALERT_WARNING,
806 G_ALERTDEFAULT) != G_ALERTALTERNATE)
809 port = account->set_imapport ? account->imapport
814 statusbar_print_all(_("Connecting to IMAP4 server: %s..."), folder->account->recv_server);
815 if (account->set_tunnelcmd) {
816 r = imap_threaded_connect_cmd(folder,
818 account->recv_server,
823 if (ssl_type == SSL_TUNNEL) {
824 r = imap_threaded_connect_ssl(folder,
825 account->recv_server,
831 r = imap_threaded_connect(folder,
832 account->recv_server,
838 if (r == MAILIMAP_NO_ERROR_AUTHENTICATED) {
839 authenticated = TRUE;
841 else if (r == MAILIMAP_NO_ERROR_NON_AUTHENTICATED) {
842 authenticated = FALSE;
845 #if (LIBETPAN_VERSION_MAJOR > 0 || LIBETPAN_VERSION_MINOR > 48)
847 if (r == MAILIMAP_ERROR_SSL)
848 log_error(LOG_PROTOCOL, _("SSL handshake failed\n"));
851 if(!prefs_common.no_recv_err_panel) {
852 alertpanel_error_log(_("Can't connect to IMAP4 server: %s:%d"),
853 account->recv_server, port);
855 log_error(LOG_PROTOCOL, _("Can't connect to IMAP4 server: %s:%d\n"),
856 account->recv_server, port);
862 session = g_new0(IMAPSession, 1);
863 session_init(SESSION(session));
864 SESSION(session)->type = SESSION_IMAP;
865 SESSION(session)->server = g_strdup(account->recv_server);
866 SESSION(session)->sock = NULL;
868 SESSION(session)->destroy = imap_session_destroy;
870 session->capability = NULL;
872 session->authenticated = authenticated;
873 session->mbox = NULL;
874 session->cmd_count = 0;
875 session->folder = folder;
876 IMAP_FOLDER(session->folder)->last_seen_separator = 0;
879 if (account->ssl_imap == SSL_STARTTLS) {
882 ok = imap_cmd_starttls(session);
883 if (ok != IMAP_SUCCESS) {
884 log_warning(LOG_PROTOCOL, _("Can't start TLS session.\n"));
885 session_destroy(SESSION(session));
889 imap_free_capabilities(session);
890 session->authenticated = FALSE;
891 session->uidplus = FALSE;
892 session->cmd_count = 1;
895 log_message(LOG_PROTOCOL, "IMAP connection is %s-authenticated\n",
896 (session->authenticated) ? "pre" : "un");
901 static void imap_session_authenticate(IMAPSession *session,
902 const PrefsAccount *account)
904 gchar *pass, *acc_pass;
905 gboolean failed = FALSE;
907 g_return_if_fail(account->userid != NULL);
908 acc_pass = account->passwd;
911 if (!pass && account->imap_auth_type != IMAP_AUTH_ANON) {
913 tmp_pass = input_dialog_query_password(account->recv_server, account->userid);
916 Xstrdup_a(pass, tmp_pass, {g_free(tmp_pass); return;});
918 } else if (account->imap_auth_type == IMAP_AUTH_ANON) {
921 statusbar_print_all(_("Connecting to IMAP4 server %s...\n"),
922 account->recv_server);
923 if (imap_auth(session, account->userid, pass, account->imap_auth_type) != IMAP_SUCCESS) {
931 if (prefs_common.no_recv_err_panel) {
932 log_error(LOG_PROTOCOL, _("Couldn't login to IMAP server %s."), account->recv_server);
933 mainwindow_show_error();
935 alertpanel_error_log(_("Couldn't login to IMAP server %s."), account->recv_server);
942 session->authenticated = TRUE;
946 static void imap_session_destroy(Session *session)
948 if (session->state != SESSION_DISCONNECTED)
949 imap_threaded_disconnect(IMAP_SESSION(session)->folder);
951 imap_free_capabilities(IMAP_SESSION(session));
952 g_free(IMAP_SESSION(session)->mbox);
953 sock_close(session->sock);
954 session->sock = NULL;
957 static gchar *imap_fetch_msg(Folder *folder, FolderItem *item, gint uid)
959 return imap_fetch_msg_full(folder, item, uid, TRUE, TRUE);
962 static guint get_size_with_crs(MsgInfo *info)
971 fp = procmsg_open_message(info);
975 while (fgets(buf, sizeof (buf), fp) != NULL) {
977 if (!strstr(buf, "\r") && strstr(buf, "\n"))
985 static gchar *imap_fetch_msg_full(Folder *folder, FolderItem *item, gint uid,
986 gboolean headers, gboolean body)
988 gchar *path, *filename;
989 IMAPSession *session;
992 g_return_val_if_fail(folder != NULL, NULL);
993 g_return_val_if_fail(item != NULL, NULL);
998 path = folder_item_get_path(item);
999 if (!is_dir_exist(path))
1000 make_dir_hier(path);
1001 filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(uid), NULL);
1003 debug_print("trying to fetch cached %s\n", filename);
1004 if (is_file_exist(filename)) {
1005 /* see whether the local file represents the whole message
1006 * or not. As the IMAP server reports size with \r chars,
1007 * we have to update the local file (UNIX \n only) size */
1008 MsgInfo *msginfo = imap_parse_msg(filename, item);
1009 MsgInfo *cached = msgcache_get_msg(item->cache,uid);
1010 guint have_size = get_size_with_crs(msginfo);
1013 debug_print("message %d has been already %scached (%d/%d).\n", uid,
1014 have_size >= cached->size ? "fully ":"",
1015 have_size, (int)cached->size);
1017 if (cached && (cached->size <= have_size || !body)) {
1018 procmsg_msginfo_free(cached);
1019 procmsg_msginfo_free(msginfo);
1020 file_strip_crs(filename);
1022 } else if (!cached && time(NULL) - get_file_mtime(filename) < 60) {
1023 debug_print("message not cached and file recent, considering file complete\n");
1024 procmsg_msginfo_free(msginfo);
1025 file_strip_crs(filename);
1028 procmsg_msginfo_free(cached);
1029 procmsg_msginfo_free(msginfo);
1033 debug_print("getting session...\n");
1034 session = imap_session_get(folder);
1041 debug_print("IMAP fetching messages\n");
1042 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
1043 NULL, NULL, NULL, NULL, FALSE);
1044 if (ok != IMAP_SUCCESS) {
1045 g_warning("can't select mailbox %s\n", item->path);
1051 debug_print("getting message %d...\n", uid);
1052 ok = imap_cmd_fetch(session, (guint32)uid, filename, headers, body);
1054 if (ok != IMAP_SUCCESS) {
1055 g_warning("can't fetch message %d\n", uid);
1062 file_strip_crs(filename);
1066 static gint imap_add_msg(Folder *folder, FolderItem *dest,
1067 const gchar *file, MsgFlags *flags)
1071 MsgFileInfo fileinfo;
1073 g_return_val_if_fail(file != NULL, -1);
1075 fileinfo.msginfo = NULL;
1076 fileinfo.file = (gchar *)file;
1077 fileinfo.flags = flags;
1078 file_list.data = &fileinfo;
1079 file_list.next = NULL;
1081 ret = imap_add_msgs(folder, dest, &file_list, NULL);
1085 static gint imap_add_msgs(Folder *folder, FolderItem *dest, GSList *file_list,
1086 GRelation *relation)
1089 IMAPSession *session;
1090 guint32 last_uid = 0;
1092 MsgFileInfo *fileinfo;
1094 gint curnum = 0, total = 0;
1097 g_return_val_if_fail(folder != NULL, -1);
1098 g_return_val_if_fail(dest != NULL, -1);
1099 g_return_val_if_fail(file_list != NULL, -1);
1101 debug_print("getting session...\n");
1102 session = imap_session_get(folder);
1106 destdir = imap_get_real_path(session, IMAP_FOLDER(folder), dest->path);
1108 statusbar_print_all(_("Adding messages..."));
1109 total = g_slist_length(file_list);
1110 for (cur = file_list; cur != NULL; cur = cur->next) {
1111 IMAPFlags iflags = 0;
1112 guint32 new_uid = 0;
1113 gchar *real_file = NULL;
1114 fileinfo = (MsgFileInfo *)cur->data;
1116 statusbar_progress_all(curnum, total, total < 10 ? 1:10);
1119 if (fileinfo->flags) {
1120 if (MSG_IS_MARKED(*fileinfo->flags))
1121 iflags |= IMAP_FLAG_FLAGGED;
1122 if (MSG_IS_REPLIED(*fileinfo->flags))
1123 iflags |= IMAP_FLAG_ANSWERED;
1124 if (!MSG_IS_UNREAD(*fileinfo->flags))
1125 iflags |= IMAP_FLAG_SEEN;
1128 if (real_file == NULL)
1129 real_file = g_strdup(fileinfo->file);
1131 if (folder_has_parent_of_type(dest, F_QUEUE) ||
1132 folder_has_parent_of_type(dest, F_OUTBOX) ||
1133 folder_has_parent_of_type(dest, F_DRAFT) ||
1134 folder_has_parent_of_type(dest, F_TRASH))
1135 iflags |= IMAP_FLAG_SEEN;
1137 ok = imap_cmd_append(session, destdir, real_file, iflags,
1140 if (ok != IMAP_SUCCESS) {
1141 g_warning("can't append message %s\n", real_file);
1145 statusbar_progress_all(0,0,0);
1146 statusbar_pop_all();
1149 debug_print("appended new message as %d\n", new_uid);
1150 /* put the local file in the imapcache, so that we don't
1151 * have to fetch it back later. */
1153 gchar *cache_path = folder_item_get_path(dest);
1154 if (!is_dir_exist(cache_path))
1155 make_dir_hier(cache_path);
1156 if (is_dir_exist(cache_path)) {
1157 gchar *cache_file = g_strconcat(
1158 cache_path, G_DIR_SEPARATOR_S,
1159 itos(new_uid), NULL);
1160 copy_file(real_file, cache_file, TRUE);
1161 debug_print("copied to cache: %s\n", cache_file);
1168 if (relation != NULL)
1169 g_relation_insert(relation, fileinfo->msginfo != NULL ?
1170 (gpointer) fileinfo->msginfo : (gpointer) fileinfo,
1171 GINT_TO_POINTER(dest->last_num + 1));
1173 new_uid = dest->last_num+1;
1175 if (last_uid < new_uid) {
1181 statusbar_progress_all(0,0,0);
1182 statusbar_pop_all();
1184 imap_cmd_expunge(session);
1192 static gint imap_do_copy_msgs(Folder *folder, FolderItem *dest,
1193 MsgInfoList *msglist, GRelation *relation)
1197 GSList *seq_list, *cur;
1199 IMAPSession *session;
1200 gint ok = IMAP_SUCCESS;
1201 GRelation *uid_mapping;
1203 gboolean single = FALSE;
1205 g_return_val_if_fail(folder != NULL, -1);
1206 g_return_val_if_fail(dest != NULL, -1);
1207 g_return_val_if_fail(msglist != NULL, -1);
1209 debug_print("getting session...\n");
1210 session = imap_session_get(folder);
1216 msginfo = (MsgInfo *)msglist->data;
1217 if (msglist->next == NULL)
1219 src = msginfo->folder;
1221 g_warning("the src folder is identical to the dest.\n");
1226 if (src->folder != dest->folder) {
1227 GSList *infolist = NULL, *cur;
1229 for (cur = msglist; cur; cur = cur->next) {
1230 msginfo = (MsgInfo *)cur->data;
1231 MsgFileInfo *fileinfo = g_new0(MsgFileInfo, 1);
1232 fileinfo->file = procmsg_get_message_file(msginfo);
1233 fileinfo->flags = &(msginfo->flags);
1234 infolist = g_slist_prepend(infolist, fileinfo);
1236 infolist = g_slist_reverse(infolist);
1238 res = folder_item_add_msgs(dest, infolist, FALSE);
1239 for (cur = infolist; cur; cur = cur->next) {
1240 MsgFileInfo *info = (MsgFileInfo *)cur->data;
1244 g_slist_free(infolist);
1248 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
1249 NULL, NULL, NULL, NULL, FALSE);
1250 if (ok != IMAP_SUCCESS) {
1255 destdir = imap_get_real_path(session, IMAP_FOLDER(folder), dest->path);
1256 seq_list = imap_get_lep_set_from_msglist(msglist);
1257 uid_mapping = g_relation_new(2);
1258 g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
1260 statusbar_print_all(_("Copying messages..."));
1261 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
1262 struct mailimap_set * seq_set;
1263 struct mailimap_set * source = NULL;
1264 struct mailimap_set * dest = NULL;
1265 seq_set = cur->data;
1267 debug_print("Copying messages from %s to %s ...\n",
1268 src->path, destdir);
1270 ok = imap_cmd_copy(session, seq_set, destdir, uid_mapping,
1273 if (ok == IMAP_SUCCESS) {
1274 if (single && relation && source && dest) {
1275 clistiter *l = clist_begin(source->set_list);
1276 struct mailimap_set_item *i = (struct mailimap_set_item *)clist_content(l);
1277 int snum = i->set_first;
1279 l = clist_begin(dest->set_list);
1280 i = (struct mailimap_set_item *)clist_content(l);
1281 dnum = i->set_first;
1282 g_relation_insert(uid_mapping, GINT_TO_POINTER(snum),
1283 GINT_TO_POINTER(dnum));
1289 mailimap_set_free(source);
1291 mailimap_set_free(dest);
1293 if (ok != IMAP_SUCCESS) {
1294 g_relation_destroy(uid_mapping);
1295 imap_lep_set_free(seq_list);
1297 statusbar_pop_all();
1302 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
1303 MsgInfo *msginfo = (MsgInfo *)cur->data;
1306 tuples = g_relation_select(uid_mapping,
1307 GINT_TO_POINTER(msginfo->msgnum),
1309 if (tuples->len > 0) {
1310 gint num = GPOINTER_TO_INT(g_tuples_index(tuples, 0, 1));
1311 g_relation_insert(relation, msginfo,
1312 GINT_TO_POINTER(num));
1315 debug_print("copied new message as %d\n", num);
1316 /* put the local file in the imapcache, so that we don't
1317 * have to fetch it back later. */
1319 gchar *cache_path = folder_item_get_path(msginfo->folder);
1320 gchar *real_file = g_strconcat(
1321 cache_path, G_DIR_SEPARATOR_S,
1322 itos(msginfo->msgnum), NULL);
1323 gchar *cache_file = NULL;
1325 cache_path = folder_item_get_path(dest);
1326 cache_file = g_strconcat(
1327 cache_path, G_DIR_SEPARATOR_S,
1329 if (!is_dir_exist(cache_path))
1330 make_dir_hier(cache_path);
1331 if (is_file_exist(real_file) && is_dir_exist(cache_path)) {
1332 copy_file(real_file, cache_file, TRUE);
1333 debug_print("copied to cache: %s\n", cache_file);
1340 g_relation_insert(relation, msginfo,
1341 GINT_TO_POINTER(0));
1342 g_tuples_destroy(tuples);
1344 statusbar_pop_all();
1346 g_relation_destroy(uid_mapping);
1347 imap_lep_set_free(seq_list);
1351 IMAP_FOLDER_ITEM(dest)->lastuid = 0;
1352 IMAP_FOLDER_ITEM(dest)->uid_next = 0;
1353 g_slist_free(IMAP_FOLDER_ITEM(dest)->uid_list);
1354 IMAP_FOLDER_ITEM(dest)->uid_list = NULL;
1357 if (ok == IMAP_SUCCESS)
1363 static gint imap_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1367 g_return_val_if_fail(msginfo != NULL, -1);
1369 msglist.data = msginfo;
1370 msglist.next = NULL;
1372 return imap_copy_msgs(folder, dest, &msglist, NULL);
1375 static gint imap_copy_msgs(Folder *folder, FolderItem *dest,
1376 MsgInfoList *msglist, GRelation *relation)
1381 g_return_val_if_fail(folder != NULL, -1);
1382 g_return_val_if_fail(dest != NULL, -1);
1383 g_return_val_if_fail(msglist != NULL, -1);
1385 msginfo = (MsgInfo *)msglist->data;
1386 g_return_val_if_fail(msginfo->folder != NULL, -1);
1388 ret = imap_do_copy_msgs(folder, dest, msglist, relation);
1393 static gint imap_do_remove_msgs(Folder *folder, FolderItem *dest,
1394 MsgInfoList *msglist, GRelation *relation)
1396 gchar *destdir, *dir;
1397 GSList *numlist = NULL, *cur;
1399 IMAPSession *session;
1400 gint ok = IMAP_SUCCESS;
1401 GRelation *uid_mapping;
1403 g_return_val_if_fail(folder != NULL, -1);
1404 g_return_val_if_fail(dest != NULL, -1);
1405 g_return_val_if_fail(msglist != NULL, -1);
1407 debug_print("getting session...\n");
1408 session = imap_session_get(folder);
1413 msginfo = (MsgInfo *)msglist->data;
1415 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
1416 NULL, NULL, NULL, NULL, FALSE);
1417 if (ok != IMAP_SUCCESS) {
1422 destdir = imap_get_real_path(session, IMAP_FOLDER(folder), dest->path);
1423 for (cur = msglist; cur; cur = cur->next) {
1424 msginfo = (MsgInfo *)cur->data;
1425 if (!MSG_IS_DELETED(msginfo->flags))
1426 numlist = g_slist_prepend(numlist, GINT_TO_POINTER(msginfo->msgnum));
1428 numlist = g_slist_reverse(numlist);
1430 uid_mapping = g_relation_new(2);
1431 g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
1433 ok = imap_set_message_flags
1434 (session, numlist, IMAP_FLAG_DELETED, TRUE);
1435 if (ok != IMAP_SUCCESS) {
1436 log_warning(LOG_PROTOCOL, _("can't set deleted flags\n"));
1440 ok = imap_cmd_expunge(session);
1441 if (ok != IMAP_SUCCESS) {
1442 log_warning(LOG_PROTOCOL, _("can't expunge\n"));
1447 dir = folder_item_get_path(msginfo->folder);
1448 if (is_dir_exist(dir)) {
1449 for (cur = msglist; cur; cur = cur->next) {
1450 msginfo = (MsgInfo *)cur->data;
1451 remove_numbered_files(dir, msginfo->msgnum, msginfo->msgnum);
1456 g_relation_destroy(uid_mapping);
1457 g_slist_free(numlist);
1461 if (ok == IMAP_SUCCESS)
1467 static gint imap_remove_msgs(Folder *folder, FolderItem *dest,
1468 MsgInfoList *msglist, GRelation *relation)
1472 g_return_val_if_fail(folder != NULL, -1);
1473 g_return_val_if_fail(dest != NULL, -1);
1474 if (msglist == NULL)
1477 msginfo = (MsgInfo *)msglist->data;
1478 g_return_val_if_fail(msginfo->folder != NULL, -1);
1480 return imap_do_remove_msgs(folder, dest, msglist, relation);
1483 static gint imap_remove_all_msg(Folder *folder, FolderItem *item)
1485 GSList *list = folder_item_get_msg_list(item);
1486 gint res = imap_remove_msgs(folder, item, list, NULL);
1487 procmsg_msg_list_free(list);
1491 static gboolean imap_is_msg_changed(Folder *folder, FolderItem *item,
1494 /* TODO: properly implement this method */
1498 static gint imap_close(Folder *folder, FolderItem *item)
1503 gint imap_scan_tree_real(Folder *folder, gboolean subs_only)
1505 FolderItem *item = NULL;
1506 IMAPSession *session;
1507 gchar *root_folder = NULL;
1509 g_return_val_if_fail(folder != NULL, -1);
1510 g_return_val_if_fail(folder->account != NULL, -1);
1512 debug_print("getting session...\n");
1513 session = imap_session_get(folder);
1515 if (!folder->node) {
1516 folder_tree_destroy(folder);
1517 item = folder_item_new(folder, folder->name, NULL);
1518 item->folder = folder;
1519 folder->node = item->node = g_node_new(item);
1524 if (folder->account->imap_dir && *folder->account->imap_dir) {
1529 Xstrdup_a(root_folder, folder->account->imap_dir, {unlock_session();return -1;});
1530 extract_quote(root_folder, '"');
1531 subst_char(root_folder,
1532 imap_get_path_separator(session, IMAP_FOLDER(folder),
1535 strtailchomp(root_folder, '/');
1536 real_path = imap_get_real_path
1537 (session, IMAP_FOLDER(folder), root_folder);
1538 debug_print("IMAP root directory: %s\n", real_path);
1540 /* check if root directory exist */
1542 r = imap_threaded_list(session->folder, "", real_path,
1544 if ((r != MAILIMAP_NO_ERROR) || (clist_count(lep_list) == 0)) {
1545 if (!folder->node) {
1546 item = folder_item_new(folder, folder->name, NULL);
1547 item->folder = folder;
1548 folder->node = item->node = g_node_new(item);
1553 mailimap_list_result_free(lep_list);
1559 item = FOLDER_ITEM(folder->node->data);
1561 if (item && !item->path && root_folder) {
1562 item->path = g_strdup(root_folder);
1565 if (!item || ((item->path || root_folder) &&
1566 strcmp2(item->path, root_folder) != 0)) {
1567 folder_tree_destroy(folder);
1568 item = folder_item_new(folder, folder->name, root_folder);
1569 item->folder = folder;
1570 folder->node = item->node = g_node_new(item);
1573 imap_scan_tree_recursive(session, FOLDER_ITEM(folder->node->data), subs_only);
1574 imap_create_missing_folders(folder);
1580 static gint imap_scan_tree(Folder *folder)
1582 gboolean subs_only = FALSE;
1583 if (folder->account) {
1584 debug_print(" scanning only subs %d\n", folder->account->imap_subsonly);
1585 subs_only = folder->account->imap_subsonly;
1587 return imap_scan_tree_real(folder, subs_only);
1590 static gint imap_scan_tree_recursive(IMAPSession *session, FolderItem *item, gboolean subs_only)
1593 IMAPFolder *imapfolder;
1594 FolderItem *new_item;
1595 GSList *item_list, *cur;
1598 gchar *wildcard_path;
1604 g_return_val_if_fail(item != NULL, -1);
1605 g_return_val_if_fail(item->folder != NULL, -1);
1606 g_return_val_if_fail(item->no_sub == FALSE, -1);
1608 folder = item->folder;
1609 imapfolder = IMAP_FOLDER(folder);
1611 separator = imap_get_path_separator(session, imapfolder, item->path);
1613 if (folder->ui_func)
1614 folder->ui_func(folder, item, folder->ui_func_data);
1617 wildcard[0] = separator;
1620 real_path = imap_get_real_path(session, imapfolder, item->path);
1624 real_path = g_strdup("");
1627 Xstrcat_a(wildcard_path, real_path, wildcard,
1628 {g_free(real_path); return IMAP_ERROR;});
1632 r = imap_threaded_lsub(folder, "", wildcard_path, &lep_list);
1634 r = imap_threaded_list(folder, "", wildcard_path, &lep_list);
1636 if (r != MAILIMAP_NO_ERROR) {
1640 item_list = imap_list_from_lep(imapfolder,
1641 lep_list, real_path, FALSE);
1642 mailimap_list_result_free(lep_list);
1647 node = item->node->children;
1648 while (node != NULL) {
1649 FolderItem *old_item = FOLDER_ITEM(node->data);
1650 GNode *next = node->next;
1653 for (cur = item_list; cur != NULL; cur = cur->next) {
1654 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1655 if (!strcmp2(old_item->path, cur_item->path)) {
1656 new_item = cur_item;
1661 if (old_item && old_item->path && !strcmp(old_item->path, "INBOX")) {
1662 debug_print("not removing INBOX\n");
1664 debug_print("folder '%s' not found. removing...\n",
1666 folder_item_remove(old_item);
1669 old_item->no_sub = new_item->no_sub;
1670 old_item->no_select = new_item->no_select;
1671 if (old_item->no_sub == TRUE && node->children) {
1672 debug_print("folder '%s' doesn't have "
1673 "subfolders. removing...\n",
1675 folder_item_remove_children(old_item);
1682 for (cur = item_list; cur != NULL; cur = cur->next) {
1683 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1686 for (node = item->node->children; node != NULL;
1687 node = node->next) {
1688 if (!strcmp2(FOLDER_ITEM(node->data)->path,
1690 new_item = FOLDER_ITEM(node->data);
1691 folder_item_destroy(cur_item);
1697 new_item = cur_item;
1698 debug_print("new folder '%s' found.\n", new_item->path);
1699 folder_item_append(item, new_item);
1702 if (!strcmp(new_item->path, "INBOX")) {
1703 new_item->stype = F_INBOX;
1704 folder->inbox = new_item;
1705 } else if (!folder_item_parent(item) || item->stype == F_INBOX) {
1708 base = g_path_get_basename(new_item->path);
1710 if (!folder->outbox && !g_ascii_strcasecmp(base, "Sent")) {
1711 new_item->stype = F_OUTBOX;
1712 folder->outbox = new_item;
1713 } else if (!folder->draft && !g_ascii_strcasecmp(base, "Drafts")) {
1714 new_item->stype = F_DRAFT;
1715 folder->draft = new_item;
1716 } else if (!folder->queue && !g_ascii_strcasecmp(base, "Queue")) {
1717 new_item->stype = F_QUEUE;
1718 folder->queue = new_item;
1719 } else if (!folder->trash && !g_ascii_strcasecmp(base, "Trash")) {
1720 new_item->stype = F_TRASH;
1721 folder->trash = new_item;
1726 if (new_item->no_sub == FALSE)
1727 imap_scan_tree_recursive(session, new_item, subs_only);
1730 g_slist_free(item_list);
1732 return IMAP_SUCCESS;
1735 GList *imap_scan_subtree(Folder *folder, FolderItem *item, gboolean unsubs_only, gboolean recursive)
1737 IMAPSession *session = imap_session_get(folder);
1739 gchar *wildcard_path;
1743 GSList *item_list = NULL, *cur;
1744 GList *child_list = NULL, *tmplist = NULL;
1745 GSList *sub_list = NULL;
1751 separator = imap_get_path_separator(session, IMAP_FOLDER(folder), item->path);
1754 wildcard[0] = separator;
1757 real_path = imap_get_real_path(session, IMAP_FOLDER(folder), item->path);
1761 real_path = g_strdup("");
1764 Xstrcat_a(wildcard_path, real_path, wildcard,
1765 {g_free(real_path); return NULL;});
1769 statusbar_print_all(_("Looking for unsubscribed folders in %s..."),
1770 item->path?item->path:item->name);
1772 statusbar_print_all(_("Looking for subfolders of %s..."),
1773 item->path?item->path:item->name);
1775 r = imap_threaded_list(folder, "", wildcard_path, &lep_list);
1777 statusbar_pop_all();
1780 item_list = imap_list_from_lep(IMAP_FOLDER(folder),
1781 lep_list, real_path, FALSE);
1782 mailimap_list_result_free(lep_list);
1784 for (cur = item_list; cur != NULL; cur = cur->next) {
1785 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1787 tmplist = imap_scan_subtree(folder, cur_item,
1788 unsubs_only, recursive);
1790 child_list = g_list_concat(child_list, tmplist);
1792 child_list = g_list_prepend(child_list,
1793 imap_get_real_path(session,
1794 IMAP_FOLDER(folder), cur_item->path));
1796 folder_item_destroy(cur_item);
1798 child_list = g_list_reverse(child_list);
1799 g_slist_free(item_list);
1802 r = imap_threaded_lsub(folder, "", wildcard_path, &lep_list);
1804 statusbar_pop_all();
1807 sub_list = imap_list_from_lep(IMAP_FOLDER(folder),
1808 lep_list, real_path, FALSE);
1809 mailimap_list_result_free(lep_list);
1811 for (cur = sub_list; cur != NULL; cur = cur->next) {
1812 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1813 GList *oldlitem = NULL;
1814 gchar *tmp = imap_get_real_path(session,
1815 IMAP_FOLDER(folder), cur_item->path);
1816 folder_item_destroy(cur_item);
1817 oldlitem = g_list_find_custom(
1818 child_list, tmp, (GCompareFunc)strcmp2);
1820 child_list = g_list_remove_link(child_list, oldlitem);
1821 g_free(oldlitem->data);
1822 g_list_free(oldlitem);
1828 statusbar_pop_all();
1833 static gint imap_create_tree(Folder *folder)
1835 g_return_val_if_fail(folder != NULL, -1);
1836 g_return_val_if_fail(folder->node != NULL, -1);
1837 g_return_val_if_fail(folder->node->data != NULL, -1);
1838 g_return_val_if_fail(folder->account != NULL, -1);
1840 imap_scan_tree(folder);
1841 imap_create_missing_folders(folder);
1846 static void imap_create_missing_folders(Folder *folder)
1848 g_return_if_fail(folder != NULL);
1851 folder->inbox = imap_create_special_folder
1852 (folder, F_INBOX, "INBOX");
1854 folder->trash = imap_create_special_folder
1855 (folder, F_TRASH, "Trash");
1857 folder->queue = imap_create_special_folder
1858 (folder, F_QUEUE, "Queue");
1859 if (!folder->outbox)
1860 folder->outbox = imap_create_special_folder
1861 (folder, F_OUTBOX, "Sent");
1863 folder->draft = imap_create_special_folder
1864 (folder, F_DRAFT, "Drafts");
1867 static FolderItem *imap_create_special_folder(Folder *folder,
1868 SpecialFolderItemType stype,
1872 FolderItem *new_item;
1874 g_return_val_if_fail(folder != NULL, NULL);
1875 g_return_val_if_fail(folder->node != NULL, NULL);
1876 g_return_val_if_fail(folder->node->data != NULL, NULL);
1877 g_return_val_if_fail(folder->account != NULL, NULL);
1878 g_return_val_if_fail(name != NULL, NULL);
1880 item = FOLDER_ITEM(folder->node->data);
1881 new_item = imap_create_folder(folder, item, name);
1884 g_warning("Can't create '%s'\n", name);
1885 if (!folder->inbox) return NULL;
1887 new_item = imap_create_folder(folder, folder->inbox, name);
1889 g_warning("Can't create '%s' under INBOX\n", name);
1891 new_item->stype = stype;
1893 new_item->stype = stype;
1898 static gchar *imap_folder_get_path(Folder *folder)
1902 g_return_val_if_fail(folder != NULL, NULL);
1903 g_return_val_if_fail(folder->account != NULL, NULL);
1905 folder_path = g_strconcat(get_imap_cache_dir(),
1907 folder->account->recv_server,
1909 folder->account->userid,
1915 static gchar *imap_item_get_path(Folder *folder, FolderItem *item)
1917 gchar *folder_path, *path;
1919 g_return_val_if_fail(folder != NULL, NULL);
1920 g_return_val_if_fail(item != NULL, NULL);
1921 folder_path = imap_folder_get_path(folder);
1923 g_return_val_if_fail(folder_path != NULL, NULL);
1924 if (folder_path[0] == G_DIR_SEPARATOR) {
1926 path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
1929 path = g_strdup(folder_path);
1932 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1933 folder_path, G_DIR_SEPARATOR_S,
1936 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1939 g_free(folder_path);
1944 static FolderItem *imap_create_folder(Folder *folder, FolderItem *parent,
1947 gchar *dirpath, *imap_path;
1948 IMAPSession *session;
1949 FolderItem *new_item;
1954 gboolean no_select = FALSE, no_sub = FALSE;
1955 gboolean exist = FALSE;
1957 g_return_val_if_fail(folder != NULL, NULL);
1958 g_return_val_if_fail(folder->account != NULL, NULL);
1959 g_return_val_if_fail(parent != NULL, NULL);
1960 g_return_val_if_fail(name != NULL, NULL);
1962 debug_print("getting session...\n");
1963 session = imap_session_get(folder);
1968 if (!folder_item_parent(parent) && strcmp(name, "INBOX") == 0) {
1969 dirpath = g_strdup(name);
1970 }else if (parent->path)
1971 dirpath = g_strconcat(parent->path, "/", name, NULL);
1972 else if ((p = strchr(name, '/')) != NULL && *(p + 1) != '\0')
1973 dirpath = g_strdup(name);
1974 else if (folder->account->imap_dir && *folder->account->imap_dir) {
1977 Xstrdup_a(imap_dir, folder->account->imap_dir, {unlock_session();return NULL;});
1978 strtailchomp(imap_dir, '/');
1979 dirpath = g_strconcat(imap_dir, "/", name, NULL);
1981 dirpath = g_strdup(name);
1985 /* keep trailing directory separator to create a folder that contains
1987 imap_path = imap_utf8_to_modified_utf7(dirpath);
1989 strtailchomp(dirpath, '/');
1990 Xstrdup_a(new_name, name, {
1995 separator = imap_get_path_separator(session, IMAP_FOLDER(folder), imap_path);
1996 imap_path_separator_subst(imap_path, separator);
1997 /* remove trailing / for display */
1998 strtailchomp(new_name, '/');
2000 if (strcmp(dirpath, "INBOX") != 0) {
2005 argbuf = g_ptr_array_new();
2006 r = imap_threaded_list(folder, "", imap_path, &lep_list);
2007 if (r != MAILIMAP_NO_ERROR) {
2008 log_warning(LOG_PROTOCOL, _("can't create mailbox: LIST failed\n"));
2011 ptr_array_free_strings(argbuf);
2012 g_ptr_array_free(argbuf, TRUE);
2017 if (clist_count(lep_list) > 0)
2019 mailimap_list_result_free(lep_list);
2022 ok = imap_cmd_create(session, imap_path);
2023 if (ok != IMAP_SUCCESS) {
2024 log_warning(LOG_PROTOCOL, _("can't create mailbox\n"));
2030 r = imap_threaded_list(folder, "", imap_path, &lep_list);
2031 if (r == MAILIMAP_NO_ERROR) {
2032 GSList *item_list = imap_list_from_lep(IMAP_FOLDER(folder),
2033 lep_list, dirpath, TRUE);
2035 FolderItem *cur_item = FOLDER_ITEM(item_list->data);
2036 no_select = cur_item->no_select;
2037 no_sub = cur_item->no_sub;
2038 g_slist_free(item_list);
2040 mailimap_list_result_free(lep_list);
2043 imap_threaded_subscribe(folder, imap_path, TRUE);
2047 /* just get flags */
2048 r = imap_threaded_list(folder, "", "INBOX", &lep_list);
2049 if (r == MAILIMAP_NO_ERROR) {
2050 GSList *item_list = imap_list_from_lep(IMAP_FOLDER(folder),
2051 lep_list, dirpath, TRUE);
2053 FolderItem *cur_item = FOLDER_ITEM(item_list->data);
2054 no_select = cur_item->no_select;
2055 no_sub = cur_item->no_sub;
2056 g_slist_free(item_list);
2058 mailimap_list_result_free(lep_list);
2062 new_item = folder_item_new(folder, new_name, dirpath);
2063 new_item->no_select = no_select;
2064 new_item->no_sub = no_sub;
2065 folder_item_append(parent, new_item);
2069 dirpath = folder_item_get_path(new_item);
2070 if (!is_dir_exist(dirpath))
2071 make_dir_hier(dirpath);
2076 /* folder existed, scan it */
2077 folder_item_scan_full(new_item, FALSE);
2083 static gint imap_rename_folder(Folder *folder, FolderItem *item,
2088 gchar *real_oldpath;
2089 gchar *real_newpath;
2091 gchar *old_cache_dir;
2092 gchar *new_cache_dir;
2093 IMAPSession *session;
2096 gint exists, recent, unseen;
2097 guint32 uid_validity;
2099 g_return_val_if_fail(folder != NULL, -1);
2100 g_return_val_if_fail(item != NULL, -1);
2101 g_return_val_if_fail(item->path != NULL, -1);
2102 g_return_val_if_fail(name != NULL, -1);
2104 debug_print("getting session...\n");
2105 session = imap_session_get(folder);
2110 if (strchr(name, imap_get_path_separator(session, IMAP_FOLDER(folder), item->path)) != NULL) {
2111 g_warning(_("New folder name must not contain the namespace "
2117 real_oldpath = imap_get_real_path(session, IMAP_FOLDER(folder), item->path);
2119 g_free(session->mbox);
2120 session->mbox = NULL;
2121 ok = imap_cmd_examine(session, "INBOX",
2122 &exists, &recent, &unseen, &uid_validity, FALSE);
2123 if (ok != IMAP_SUCCESS) {
2124 g_free(real_oldpath);
2129 separator = imap_get_path_separator(session, IMAP_FOLDER(folder), item->path);
2130 if (strchr(item->path, G_DIR_SEPARATOR)) {
2131 dirpath = g_path_get_dirname(item->path);
2132 newpath = g_strconcat(dirpath, G_DIR_SEPARATOR_S, name, NULL);
2135 newpath = g_strdup(name);
2137 real_newpath = imap_utf8_to_modified_utf7(newpath);
2138 imap_path_separator_subst(real_newpath, separator);
2140 ok = imap_cmd_rename(session, real_oldpath, real_newpath);
2141 if (ok != IMAP_SUCCESS) {
2142 log_warning(LOG_PROTOCOL, _("can't rename mailbox: %s to %s\n"),
2143 real_oldpath, real_newpath);
2144 g_free(real_oldpath);
2146 g_free(real_newpath);
2151 item->name = g_strdup(name);
2153 old_cache_dir = folder_item_get_path(item);
2155 paths[0] = g_strdup(item->path);
2157 g_node_traverse(item->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
2158 imap_rename_folder_func, paths);
2160 if (is_dir_exist(old_cache_dir)) {
2161 new_cache_dir = folder_item_get_path(item);
2162 if (rename(old_cache_dir, new_cache_dir) < 0) {
2163 FILE_OP_ERROR(old_cache_dir, "rename");
2165 g_free(new_cache_dir);
2168 g_free(old_cache_dir);
2171 g_free(real_oldpath);
2172 g_free(real_newpath);
2177 gint imap_subscribe(Folder *folder, FolderItem *item, gchar *rpath, gboolean sub)
2181 IMAPSession *session;
2182 debug_print("getting session...\n");
2184 session = imap_session_get(folder);
2188 if (item && item->path) {
2189 path = imap_get_real_path(session, IMAP_FOLDER(folder), item->path);
2192 if (!strcmp(path, "INBOX") && sub == FALSE)
2194 debug_print("%ssubscribing %s\n", sub?"":"un", path);
2195 r = imap_threaded_subscribe(folder, path, sub);
2198 r = imap_threaded_subscribe(folder, rpath, sub);
2204 static gint imap_remove_folder_real(Folder *folder, FolderItem *item)
2207 IMAPSession *session;
2211 g_return_val_if_fail(folder != NULL, -1);
2212 g_return_val_if_fail(item != NULL, -1);
2213 g_return_val_if_fail(item->path != NULL, -1);
2215 debug_print("getting session...\n");
2216 session = imap_session_get(folder);
2220 path = imap_get_real_path(session, IMAP_FOLDER(folder), item->path);
2222 imap_threaded_subscribe(folder, path, FALSE);
2223 ok = imap_cmd_delete(session, path);
2224 if (ok != IMAP_SUCCESS) {
2225 gchar *tmp = g_strdup_printf("%s%c", path,
2226 imap_get_path_separator(session, IMAP_FOLDER(folder), path));
2229 ok = imap_cmd_delete(session, path);
2232 if (ok != IMAP_SUCCESS) {
2233 log_warning(LOG_PROTOCOL, _("can't delete mailbox\n"));
2240 cache_dir = folder_item_get_path(item);
2241 if (is_dir_exist(cache_dir) && remove_dir_recursive(cache_dir) < 0)
2242 g_warning("can't remove directory '%s'\n", cache_dir);
2244 folder_item_remove(item);
2249 static gint imap_remove_folder(Folder *folder, FolderItem *item)
2253 g_return_val_if_fail(item != NULL, -1);
2254 g_return_val_if_fail(item->folder != NULL, -1);
2255 g_return_val_if_fail(item->node != NULL, -1);
2257 node = item->node->children;
2258 while (node != NULL) {
2260 if (imap_remove_folder(folder, FOLDER_ITEM(node->data)) < 0)
2264 debug_print("IMAP removing %s\n", item->path);
2266 if (imap_remove_all_msg(folder, item) < 0)
2268 return imap_remove_folder_real(folder, item);
2271 typedef struct _uncached_data {
2272 IMAPSession *session;
2274 MsgNumberList *numlist;
2280 static void *imap_get_uncached_messages_thread(void *data)
2282 uncached_data *stuff = (uncached_data *)data;
2283 IMAPSession *session = stuff->session;
2284 FolderItem *item = stuff->item;
2285 MsgNumberList *numlist = stuff->numlist;
2287 GSList *newlist = NULL;
2288 GSList *llast = NULL;
2289 GSList *seq_list, *cur;
2291 debug_print("uncached_messages\n");
2293 if (session == NULL || item == NULL || item->folder == NULL
2294 || FOLDER_CLASS(item->folder) != &imap_class) {
2299 seq_list = imap_get_lep_set_from_numlist(numlist);
2300 debug_print("get msgs info\n");
2301 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
2302 struct mailimap_set * imapset;
2308 imapset = cur->data;
2310 r = imap_threaded_fetch_env(session->folder,
2311 imapset, &env_list);
2312 if (r != MAILIMAP_NO_ERROR)
2315 session_set_access_time(SESSION(session));
2318 for(i = 0 ; i < carray_count(env_list) ; i ++) {
2319 struct imap_fetch_env_info * info;
2322 info = carray_get(env_list, i);
2323 msginfo = imap_envelope_from_lep(info, item);
2324 if (msginfo == NULL)
2326 msginfo->folder = item;
2328 llast = newlist = g_slist_append(newlist, msginfo);
2330 llast = g_slist_append(llast, msginfo);
2331 llast = llast->next;
2336 imap_fetch_env_free(env_list);
2339 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
2340 struct mailimap_set * imapset;
2342 imapset = cur->data;
2343 mailimap_set_free(imapset);
2346 session_set_access_time(SESSION(session));
2351 #define MAX_MSG_NUM 50
2353 static GSList *imap_get_uncached_messages(IMAPSession *session,
2355 MsgNumberList *numlist)
2357 GSList *result = NULL;
2359 uncached_data *data = g_new0(uncached_data, 1);
2364 data->total = g_slist_length(numlist);
2365 debug_print("messages list : %i\n", data->total);
2367 while (cur != NULL) {
2368 GSList * partial_result;
2376 while (count < MAX_MSG_NUM) {
2381 if (newlist == NULL)
2382 llast = newlist = g_slist_append(newlist, p);
2384 llast = g_slist_append(llast, p);
2385 llast = llast->next;
2395 data->session = session;
2397 data->numlist = newlist;
2400 if (prefs_common.work_offline &&
2401 !inc_offline_should_override(FALSE,
2402 _("Claws Mail needs network access in order "
2403 "to access the IMAP server."))) {
2409 (GSList *)imap_get_uncached_messages_thread(data);
2411 statusbar_progress_all(data->cur,data->total, 1);
2413 g_slist_free(newlist);
2415 result = g_slist_concat(result, partial_result);
2419 statusbar_progress_all(0,0,0);
2420 statusbar_pop_all();
2425 static void imap_delete_all_cached_messages(FolderItem *item)
2429 g_return_if_fail(item != NULL);
2430 g_return_if_fail(item->folder != NULL);
2431 g_return_if_fail(FOLDER_CLASS(item->folder) == &imap_class);
2433 debug_print("Deleting all cached messages...\n");
2435 dir = folder_item_get_path(item);
2436 if (is_dir_exist(dir))
2437 remove_all_numbered_files(dir);
2440 debug_print("done.\n");
2443 gchar imap_get_path_separator_for_item(FolderItem *item)
2445 Folder *folder = NULL;
2446 IMAPFolder *imap_folder = NULL;
2447 IMAPSession *session = NULL;
2452 folder = item->folder;
2457 imap_folder = IMAP_FOLDER(folder);
2462 debug_print("getting session...");
2463 session = imap_session_get(FOLDER(folder));
2464 result = imap_get_path_separator(session, imap_folder, item->path);
2469 static gchar imap_refresh_path_separator(IMAPSession *session, IMAPFolder *folder, const gchar *subfolder)
2473 gchar separator = '\0';
2475 g_return_val_if_fail(session != NULL, '/');
2476 r = imap_threaded_list((Folder *)folder, "", subfolder, &lep_list);
2478 if (r != MAILIMAP_NO_ERROR) {
2479 log_warning(LOG_PROTOCOL, _("LIST failed\n"));
2483 if (clist_count(lep_list) > 0) {
2484 clistiter * iter = clist_begin(lep_list);
2485 struct mailimap_mailbox_list * mb;
2486 mb = clist_content(iter);
2488 separator = mb->mb_delimiter;
2489 debug_print("got separator: %c\n", folder->last_seen_separator);
2491 mailimap_list_result_free(lep_list);
2495 static gchar imap_get_path_separator(IMAPSession *session, IMAPFolder *folder, const gchar *path)
2497 gchar separator = '/';
2499 if (folder->last_seen_separator == 0) {
2500 folder->last_seen_separator = imap_refresh_path_separator(session, folder, "");
2503 if (folder->last_seen_separator == 0) {
2504 folder->last_seen_separator = imap_refresh_path_separator(session, folder, "INBOX");
2507 if (folder->last_seen_separator != 0) {
2508 debug_print("using separator: %c\n", folder->last_seen_separator);
2509 return folder->last_seen_separator;
2515 static gchar *imap_get_real_path(IMAPSession *session, IMAPFolder *folder, const gchar *path)
2520 g_return_val_if_fail(folder != NULL, NULL);
2521 g_return_val_if_fail(path != NULL, NULL);
2523 real_path = imap_utf8_to_modified_utf7(path);
2524 separator = imap_get_path_separator(session, folder, path);
2525 imap_path_separator_subst(real_path, separator);
2530 static gint imap_set_message_flags(IMAPSession *session,
2531 MsgNumberList *numlist,
2539 seq_list = imap_get_lep_set_from_numlist(numlist);
2541 for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) {
2542 struct mailimap_set * imapset;
2544 imapset = cur->data;
2546 ok = imap_cmd_store(session, imapset,
2550 imap_lep_set_free(seq_list);
2552 return IMAP_SUCCESS;
2555 typedef struct _select_data {
2556 IMAPSession *session;
2561 guint32 *uid_validity;
2565 static gint imap_select(IMAPSession *session, IMAPFolder *folder,
2567 gint *exists, gint *recent, gint *unseen,
2568 guint32 *uid_validity, gboolean block)
2572 gint exists_, recent_, unseen_;
2573 guint32 uid_validity_;
2575 if (!exists && !recent && !unseen && !uid_validity) {
2576 if (session->mbox && strcmp(session->mbox, path) == 0)
2577 return IMAP_SUCCESS;
2586 uid_validity = &uid_validity_;
2588 g_free(session->mbox);
2589 session->mbox = NULL;
2591 real_path = imap_get_real_path(session, folder, path);
2593 ok = imap_cmd_select(session, real_path,
2594 exists, recent, unseen, uid_validity, block);
2595 if (ok != IMAP_SUCCESS)
2596 log_warning(LOG_PROTOCOL, _("can't select folder: %s\n"), real_path);
2598 session->mbox = g_strdup(path);
2599 session->folder_content_changed = FALSE;
2606 static gint imap_status(IMAPSession *session, IMAPFolder *folder,
2607 const gchar *path, IMAPFolderItem *item,
2609 guint32 *uid_next, guint32 *uid_validity,
2610 gint *unseen, gboolean block)
2614 struct mailimap_mailbox_data_status * data_status;
2619 real_path = imap_get_real_path(session, folder, path);
2633 r = imap_threaded_status(FOLDER(folder), real_path,
2634 &data_status, mask);
2637 if (r != MAILIMAP_NO_ERROR) {
2638 debug_print("status err %d\n", r);
2642 if (data_status->st_info_list == NULL) {
2643 mailimap_mailbox_data_status_free(data_status);
2644 debug_print("status->st_info_list == NULL\n");
2649 for(iter = clist_begin(data_status->st_info_list) ; iter != NULL ;
2650 iter = clist_next(iter)) {
2651 struct mailimap_status_info * info;
2653 info = clist_content(iter);
2654 switch (info->st_att) {
2655 case MAILIMAP_STATUS_ATT_MESSAGES:
2656 * messages = info->st_value;
2657 got_values |= 1 << 0;
2660 case MAILIMAP_STATUS_ATT_UIDNEXT:
2661 * uid_next = info->st_value;
2662 got_values |= 1 << 2;
2665 case MAILIMAP_STATUS_ATT_UIDVALIDITY:
2666 * uid_validity = info->st_value;
2667 got_values |= 1 << 3;
2670 case MAILIMAP_STATUS_ATT_UNSEEN:
2671 * unseen = info->st_value;
2672 got_values |= 1 << 4;
2676 mailimap_mailbox_data_status_free(data_status);
2678 if (got_values != mask) {
2679 debug_print("status: incomplete values received (%d)\n", got_values);
2682 return IMAP_SUCCESS;
2685 static void imap_free_capabilities(IMAPSession *session)
2687 slist_free_strings(session->capability);
2688 g_slist_free(session->capability);
2689 session->capability = NULL;
2692 /* low-level IMAP4rev1 commands */
2694 static gint imap_cmd_login(IMAPSession *session,
2695 const gchar *user, const gchar *pass,
2701 if (!strcmp(type, "LOGIN") && imap_has_capability(session, "LOGINDISABLED")) {
2702 gint ok = IMAP_ERROR;
2703 if (imap_has_capability(session, "STARTTLS")) {
2705 log_warning(LOG_PROTOCOL, _("Server requires TLS to log in.\n"));
2706 ok = imap_cmd_starttls(session);
2707 if (ok != IMAP_SUCCESS) {
2708 log_warning(LOG_PROTOCOL, _("Can't start TLS session.\n"));
2712 imap_free_capabilities(session);
2713 if (imap_get_capabilities(session) != MAILIMAP_NO_ERROR) {
2714 log_warning(LOG_PROTOCOL, _("Can't refresh capabilities.\n"));
2719 log_error(LOG_PROTOCOL, _("Connection to %s failed: "
2720 "server requires TLS, but Claws Mail "
2721 "has been compiled without OpenSSL "
2723 SESSION(session)->server);
2727 log_error(LOG_PROTOCOL, _("Server logins are disabled.\n"));
2732 log_print(LOG_PROTOCOL, "IMAP4> Logging %s to %s using %s\n",
2734 SESSION(session)->server,
2736 r = imap_threaded_login(session->folder, user, pass, type);
2737 if (r != MAILIMAP_NO_ERROR) {
2738 log_print(LOG_PROTOCOL, "IMAP4< Error logging in to %s\n",
2739 SESSION(session)->server);
2742 log_print(LOG_PROTOCOL, "IMAP4< Login to %s successful\n",
2743 SESSION(session)->server);
2749 static gint imap_cmd_noop(IMAPSession *session)
2752 unsigned int exists;
2754 r = imap_threaded_noop(session->folder, &exists);
2755 if (r != MAILIMAP_NO_ERROR) {
2756 debug_print("noop err %d\n", r);
2759 session->exists = exists;
2760 session_set_access_time(SESSION(session));
2762 return IMAP_SUCCESS;
2766 static gint imap_cmd_starttls(IMAPSession *session)
2770 r = imap_threaded_starttls(session->folder,
2771 SESSION(session)->server, SESSION(session)->port);
2772 if (r != MAILIMAP_NO_ERROR) {
2773 debug_print("starttls err %d\n", r);
2776 return IMAP_SUCCESS;
2780 static gint imap_cmd_select(IMAPSession *session, const gchar *folder,
2781 gint *exists, gint *recent, gint *unseen,
2782 guint32 *uid_validity, gboolean block)
2786 r = imap_threaded_select(session->folder, folder,
2787 exists, recent, unseen, uid_validity);
2788 if (r != MAILIMAP_NO_ERROR) {
2789 debug_print("select err %d\n", r);
2792 return IMAP_SUCCESS;
2795 static gint imap_cmd_examine(IMAPSession *session, const gchar *folder,
2796 gint *exists, gint *recent, gint *unseen,
2797 guint32 *uid_validity, gboolean block)
2801 r = imap_threaded_examine(session->folder, folder,
2802 exists, recent, unseen, uid_validity);
2803 if (r != MAILIMAP_NO_ERROR) {
2804 debug_print("examine err %d\n", r);
2808 return IMAP_SUCCESS;
2811 static gint imap_cmd_create(IMAPSession *session, const gchar *folder)
2815 r = imap_threaded_create(session->folder, folder);
2816 if (r != MAILIMAP_NO_ERROR) {
2821 return IMAP_SUCCESS;
2824 static gint imap_cmd_rename(IMAPSession *session, const gchar *old_folder,
2825 const gchar *new_folder)
2829 r = imap_threaded_rename(session->folder, old_folder,
2831 if (r != MAILIMAP_NO_ERROR) {
2836 return IMAP_SUCCESS;
2839 static gint imap_cmd_delete(IMAPSession *session, const gchar *folder)
2844 r = imap_threaded_delete(session->folder, folder);
2845 if (r != MAILIMAP_NO_ERROR) {
2850 return IMAP_SUCCESS;
2853 typedef struct _fetch_data {
2854 IMAPSession *session;
2856 const gchar *filename;
2862 static void *imap_cmd_fetch_thread(void *data)
2864 fetch_data *stuff = (fetch_data *)data;
2865 IMAPSession *session = stuff->session;
2866 guint32 uid = stuff->uid;
2867 const gchar *filename = stuff->filename;
2871 r = imap_threaded_fetch_content(session->folder,
2875 r = imap_threaded_fetch_content(session->folder,
2878 if (r != MAILIMAP_NO_ERROR) {
2879 debug_print("fetch err %d\n", r);
2880 return GINT_TO_POINTER(IMAP_ERROR);
2882 return GINT_TO_POINTER(IMAP_SUCCESS);
2885 static gint imap_cmd_fetch(IMAPSession *session, guint32 uid,
2886 const gchar *filename, gboolean headers,
2889 fetch_data *data = g_new0(fetch_data, 1);
2892 data->session = session;
2894 data->filename = filename;
2895 data->headers = headers;
2898 if (prefs_common.work_offline &&
2899 !inc_offline_should_override(FALSE,
2900 _("Claws Mail needs network access in order "
2901 "to access the IMAP server."))) {
2905 statusbar_print_all(_("Fetching message..."));
2906 result = GPOINTER_TO_INT(imap_cmd_fetch_thread(data));
2907 statusbar_pop_all();
2913 static gint imap_cmd_append(IMAPSession *session, const gchar *destfolder,
2914 const gchar *file, IMAPFlags flags,
2917 struct mailimap_flag_list * flag_list;
2920 g_return_val_if_fail(file != NULL, IMAP_ERROR);
2922 flag_list = imap_flag_to_lep(flags);
2923 r = imap_threaded_append(session->folder, destfolder,
2924 file, flag_list, (int *)new_uid);
2925 mailimap_flag_list_free(flag_list);
2927 if (r != MAILIMAP_NO_ERROR) {
2928 debug_print("append err %d\n", r);
2931 return IMAP_SUCCESS;
2934 static gint imap_cmd_copy(IMAPSession *session, struct mailimap_set * set,
2935 const gchar *destfolder, GRelation *uid_mapping,
2936 struct mailimap_set **source, struct mailimap_set **dest)
2940 g_return_val_if_fail(session != NULL, IMAP_ERROR);
2941 g_return_val_if_fail(set != NULL, IMAP_ERROR);
2942 g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
2944 r = imap_threaded_copy(session->folder, set, destfolder, source, dest);
2945 if (r != MAILIMAP_NO_ERROR) {
2950 return IMAP_SUCCESS;
2953 static gint imap_cmd_store(IMAPSession *session, struct mailimap_set * set,
2954 IMAPFlags flags, int do_add)
2957 struct mailimap_flag_list * flag_list;
2958 struct mailimap_store_att_flags * store_att_flags;
2960 flag_list = imap_flag_to_lep(flags);
2964 mailimap_store_att_flags_new_add_flags_silent(flag_list);
2967 mailimap_store_att_flags_new_remove_flags_silent(flag_list);
2969 r = imap_threaded_store(session->folder, set, store_att_flags);
2970 mailimap_store_att_flags_free(store_att_flags);
2971 if (r != MAILIMAP_NO_ERROR) {
2976 return IMAP_SUCCESS;
2979 static gint imap_cmd_expunge(IMAPSession *session)
2983 if (prefs_common.work_offline &&
2984 !inc_offline_should_override(FALSE,
2985 _("Claws Mail needs network access in order "
2986 "to access the IMAP server."))) {
2990 r = imap_threaded_expunge(session->folder);
2991 if (r != MAILIMAP_NO_ERROR) {
2996 return IMAP_SUCCESS;
2999 static void imap_path_separator_subst(gchar *str, gchar separator)
3002 gboolean in_escape = FALSE;
3004 if (!separator || separator == '/') return;
3006 for (p = str; *p != '\0'; p++) {
3007 if (*p == '/' && !in_escape)
3009 else if (*p == '&' && *(p + 1) != '-' && !in_escape)
3011 else if (*p == '-' && in_escape)
3016 static gchar *imap_modified_utf7_to_utf8(const gchar *mutf7_str)
3018 static iconv_t cd = (iconv_t)-1;
3019 static gboolean iconv_ok = TRUE;
3022 size_t norm_utf7_len;
3024 gchar *to_str, *to_p;
3026 gboolean in_escape = FALSE;
3028 if (!iconv_ok) return g_strdup(mutf7_str);
3030 if (cd == (iconv_t)-1) {
3031 cd = iconv_open(CS_INTERNAL, CS_UTF_7);
3032 if (cd == (iconv_t)-1) {
3033 g_warning("iconv cannot convert UTF-7 to %s\n",
3036 return g_strdup(mutf7_str);
3040 /* modified UTF-7 to normal UTF-7 conversion */
3041 norm_utf7 = g_string_new(NULL);
3043 for (p = mutf7_str; *p != '\0'; p++) {
3044 /* replace: '&' -> '+',
3046 escaped ',' -> '/' */
3047 if (!in_escape && *p == '&') {
3048 if (*(p + 1) != '-') {
3049 g_string_append_c(norm_utf7, '+');
3052 g_string_append_c(norm_utf7, '&');
3055 } else if (in_escape && *p == ',') {
3056 g_string_append_c(norm_utf7, '/');
3057 } else if (in_escape && *p == '-') {
3058 g_string_append_c(norm_utf7, '-');
3061 g_string_append_c(norm_utf7, *p);
3065 norm_utf7_p = norm_utf7->str;
3066 norm_utf7_len = norm_utf7->len;
3067 to_len = strlen(mutf7_str) * 5;
3068 to_p = to_str = g_malloc(to_len + 1);
3070 if (iconv(cd, (ICONV_CONST gchar **)&norm_utf7_p, &norm_utf7_len,
3071 &to_p, &to_len) == -1) {
3072 g_warning(_("iconv cannot convert UTF-7 to %s\n"),
3073 conv_get_locale_charset_str());
3074 g_string_free(norm_utf7, TRUE);
3076 return g_strdup(mutf7_str);
3079 /* second iconv() call for flushing */
3080 iconv(cd, NULL, NULL, &to_p, &to_len);
3081 g_string_free(norm_utf7, TRUE);
3087 static gchar *imap_utf8_to_modified_utf7(const gchar *from)
3089 static iconv_t cd = (iconv_t)-1;
3090 static gboolean iconv_ok = TRUE;
3091 gchar *norm_utf7, *norm_utf7_p;
3092 size_t from_len, norm_utf7_len;
3094 gchar *from_tmp, *to, *p;
3095 gboolean in_escape = FALSE;
3097 if (!iconv_ok) return g_strdup(from);
3099 if (cd == (iconv_t)-1) {
3100 cd = iconv_open(CS_UTF_7, CS_INTERNAL);
3101 if (cd == (iconv_t)-1) {
3102 g_warning(_("iconv cannot convert %s to UTF-7\n"),
3105 return g_strdup(from);
3109 /* UTF-8 to normal UTF-7 conversion */
3110 Xstrdup_a(from_tmp, from, return g_strdup(from));
3111 from_len = strlen(from);
3112 norm_utf7_len = from_len * 5;
3113 Xalloca(norm_utf7, norm_utf7_len + 1, return g_strdup(from));
3114 norm_utf7_p = norm_utf7;
3116 #define IS_PRINT(ch) (isprint(ch) && IS_ASCII(ch))
3118 while (from_len > 0) {
3119 if (*from_tmp == '+') {
3120 *norm_utf7_p++ = '+';
3121 *norm_utf7_p++ = '-';
3125 } else if (IS_PRINT(*(guchar *)from_tmp)) {
3126 /* printable ascii char */
3127 *norm_utf7_p = *from_tmp;
3133 size_t conv_len = 0;
3135 /* unprintable char: convert to UTF-7 */
3137 while (!IS_PRINT(*(guchar *)p) && conv_len < from_len) {
3138 conv_len += g_utf8_skip[*(guchar *)p];
3139 p += g_utf8_skip[*(guchar *)p];
3142 from_len -= conv_len;
3143 if (iconv(cd, (ICONV_CONST gchar **)&from_tmp,
3145 &norm_utf7_p, &norm_utf7_len) == -1) {
3146 g_warning(_("iconv cannot convert UTF-8 to UTF-7\n"));
3147 return g_strdup(from);
3150 /* second iconv() call for flushing */
3151 iconv(cd, NULL, NULL, &norm_utf7_p, &norm_utf7_len);
3157 *norm_utf7_p = '\0';
3158 to_str = g_string_new(NULL);
3159 for (p = norm_utf7; p < norm_utf7_p; p++) {
3160 /* replace: '&' -> "&-",
3163 BASE64 '/' -> ',' */
3164 if (!in_escape && *p == '&') {
3165 g_string_append(to_str, "&-");
3166 } else if (!in_escape && *p == '+') {
3167 if (*(p + 1) == '-') {
3168 g_string_append_c(to_str, '+');
3171 g_string_append_c(to_str, '&');
3174 } else if (in_escape && *p == '/') {
3175 g_string_append_c(to_str, ',');
3176 } else if (in_escape && *p == '-') {
3177 g_string_append_c(to_str, '-');
3180 g_string_append_c(to_str, *p);
3186 g_string_append_c(to_str, '-');
3190 g_string_free(to_str, FALSE);
3195 static gboolean imap_rename_folder_func(GNode *node, gpointer data)
3197 FolderItem *item = node->data;
3198 gchar **paths = data;
3199 const gchar *oldpath = paths[0];
3200 const gchar *newpath = paths[1];
3201 gchar *real_oldpath, *real_newpath;
3203 gchar *new_itempath;
3205 IMAPSession *session = imap_session_get(item->folder);
3207 oldpathlen = strlen(oldpath);
3208 if (strncmp(oldpath, item->path, oldpathlen) != 0) {
3209 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
3213 base = item->path + oldpathlen;
3214 while (*base == G_DIR_SEPARATOR) base++;
3216 new_itempath = g_strdup(newpath);
3218 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
3221 real_oldpath = imap_get_real_path(session, IMAP_FOLDER(item->folder), item->path);
3223 item->path = new_itempath;
3225 real_newpath = imap_get_real_path(session, IMAP_FOLDER(item->folder), item->path);
3227 imap_threaded_subscribe(item->folder, real_oldpath, FALSE);
3228 imap_threaded_subscribe(item->folder, real_newpath, TRUE);
3230 g_free(real_oldpath);
3231 g_free(real_newpath);
3235 typedef struct _get_list_uid_data {
3237 IMAPSession *session;
3238 IMAPFolderItem *item;
3239 GSList **msgnum_list;
3241 } get_list_uid_data;
3243 static void *get_list_of_uids_thread(void *data)
3245 get_list_uid_data *stuff = (get_list_uid_data *)data;
3246 Folder *folder = stuff->folder;
3247 IMAPFolderItem *item = stuff->item;
3248 GSList **msgnum_list = stuff->msgnum_list;
3249 gint ok, nummsgs = 0, lastuid_old;
3250 IMAPSession *session;
3251 GSList *uidlist, *elem;
3252 clist * lep_uidlist;
3255 session = stuff->session;
3256 if (session == NULL) {
3258 return GINT_TO_POINTER(-1);
3260 /* no session locking here, it's already locked by caller */
3261 ok = imap_select(session, IMAP_FOLDER(folder), item->item.path,
3262 NULL, NULL, NULL, NULL, TRUE);
3263 if (ok != IMAP_SUCCESS) {
3265 return GINT_TO_POINTER(-1);
3270 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SIMPLE, NULL,
3273 if (r == MAILIMAP_NO_ERROR) {
3274 GSList * fetchuid_list;
3277 imap_uid_list_from_lep(lep_uidlist);
3278 mailimap_search_result_free(lep_uidlist);
3280 uidlist = g_slist_concat(fetchuid_list, uidlist);
3283 GSList * fetchuid_list;
3284 carray * lep_uidtab;
3286 r = imap_threaded_fetch_uid(folder, item->lastuid + 1,
3288 if (r == MAILIMAP_NO_ERROR) {
3290 imap_uid_list_from_lep_tab(lep_uidtab);
3291 imap_fetch_uid_list_free(lep_uidtab);
3292 uidlist = g_slist_concat(fetchuid_list, uidlist);
3296 lastuid_old = item->lastuid;
3297 *msgnum_list = g_slist_copy(item->uid_list);
3298 nummsgs = g_slist_length(*msgnum_list);
3299 debug_print("Got %d uids from cache\n", g_slist_length(item->uid_list));
3301 for (elem = uidlist; elem != NULL; elem = g_slist_next(elem)) {
3304 msgnum = GPOINTER_TO_INT(elem->data);
3305 if (msgnum > lastuid_old) {
3306 *msgnum_list = g_slist_prepend(*msgnum_list, GINT_TO_POINTER(msgnum));
3307 item->uid_list = g_slist_prepend(item->uid_list, GINT_TO_POINTER(msgnum));
3310 if(msgnum > item->lastuid)
3311 item->lastuid = msgnum;
3314 g_slist_free(uidlist);
3316 return GINT_TO_POINTER(nummsgs);
3319 static gint get_list_of_uids(IMAPSession *session, Folder *folder, IMAPFolderItem *item, GSList **msgnum_list)
3322 get_list_uid_data *data = g_new0(get_list_uid_data, 1);
3324 data->folder = folder;
3326 data->msgnum_list = msgnum_list;
3327 data->session = session;
3328 if (prefs_common.work_offline &&
3329 !inc_offline_should_override(FALSE,
3330 _("Claws Mail needs network access in order "
3331 "to access the IMAP server."))) {
3336 result = GPOINTER_TO_INT(get_list_of_uids_thread(data));
3342 gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list, gboolean *old_uids_valid)
3344 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3345 IMAPSession *session;
3346 gint ok, nummsgs = 0, exists;
3347 guint32 uid_next = 0, uid_val = 0;
3348 GSList *uidlist = NULL;
3350 gboolean selected_folder;
3351 debug_print("get_num_list\n");
3353 g_return_val_if_fail(folder != NULL, -1);
3354 g_return_val_if_fail(item != NULL, -1);
3355 g_return_val_if_fail(item->item.path != NULL, -1);
3356 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3357 g_return_val_if_fail(folder->account != NULL, -1);
3359 debug_print("getting session...\n");
3360 session = imap_session_get(folder);
3361 g_return_val_if_fail(session != NULL, -1);
3363 if (FOLDER_ITEM(item)->path)
3364 statusbar_print_all(_("Scanning folder %s%c%s ..."),
3365 FOLDER_ITEM(item)->folder->name,
3367 FOLDER_ITEM(item)->path);
3369 statusbar_print_all(_("Scanning folder %s ..."),
3370 FOLDER_ITEM(item)->folder->name);
3372 selected_folder = (session->mbox != NULL) &&
3373 (!strcmp(session->mbox, item->item.path));
3374 if (selected_folder && time(NULL) - item->use_cache < 2) {
3375 ok = imap_cmd_noop(session);
3376 if (ok != IMAP_SUCCESS) {
3377 debug_print("disconnected!\n");
3378 session = imap_reconnect_if_possible(folder, session);
3379 if (session == NULL) {
3380 statusbar_pop_all();
3385 exists = session->exists;
3387 uid_next = item->c_uid_next;
3388 uid_val = item->c_uid_validity;
3389 *old_uids_valid = TRUE;
3391 if (item->use_cache && time(NULL) - item->use_cache < 2) {
3392 exists = item->c_messages;
3393 uid_next = item->c_uid_next;
3394 uid_val = item->c_uid_validity;
3396 debug_print("using cache %d %d %d\n", exists, uid_next, uid_val);
3398 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path, item,
3399 &exists, &uid_next, &uid_val, NULL, FALSE);
3401 item->item.last_num = uid_next - 1;
3403 item->use_cache = (time_t)0;
3404 if (ok != IMAP_SUCCESS) {
3405 statusbar_pop_all();
3409 if(item->item.mtime == uid_val)
3410 *old_uids_valid = TRUE;
3412 *old_uids_valid = FALSE;
3414 debug_print("Freeing imap uid cache (%d != %d)\n",
3415 (int)item->item.mtime, uid_val);
3417 g_slist_free(item->uid_list);
3418 item->uid_list = NULL;
3420 item->item.mtime = uid_val;
3422 imap_delete_all_cached_messages((FolderItem *)item);
3426 /* If old uid_next matches new uid_next we can be sure no message
3427 was added to the folder */
3428 debug_print("uid_next is %d and item->uid_next %d \n",
3429 uid_next, item->uid_next);
3430 if (uid_next == item->uid_next) {
3431 nummsgs = g_slist_length(item->uid_list);
3433 /* If number of messages is still the same we
3434 know our caches message numbers are still valid,
3435 otherwise if the number of messages has decrease
3436 we discard our cache to start a new scan to find
3437 out which numbers have been removed */
3438 if (exists == nummsgs) {
3439 debug_print("exists == nummsgs\n");
3440 *msgnum_list = g_slist_copy(item->uid_list);
3441 statusbar_pop_all();
3444 } else if (exists < nummsgs) {
3445 debug_print("Freeing imap uid cache");
3447 g_slist_free(item->uid_list);
3448 item->uid_list = NULL;
3453 *msgnum_list = NULL;
3454 statusbar_pop_all();
3459 nummsgs = get_list_of_uids(session, folder, item, &uidlist);
3462 statusbar_pop_all();
3467 if (nummsgs != exists) {
3468 /* Cache contains more messages then folder, we have cached
3469 an old UID of a message that was removed and new messages
3470 have been added too, otherwise the uid_next check would
3472 debug_print("Freeing imap uid cache");
3474 g_slist_free(item->uid_list);
3475 item->uid_list = NULL;
3477 g_slist_free(*msgnum_list);
3479 nummsgs = get_list_of_uids(session, folder, item, &uidlist);
3482 *msgnum_list = uidlist;
3484 dir = folder_item_get_path((FolderItem *)item);
3485 debug_print("removing old messages from %s\n", dir);
3486 remove_numbered_files_not_in_list(dir, *msgnum_list);
3489 item->uid_next = uid_next;
3491 debug_print("get_num_list - ok - %i\n", nummsgs);
3492 statusbar_pop_all();
3497 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item)
3502 flags.perm_flags = MSG_NEW|MSG_UNREAD;
3503 flags.tmp_flags = 0;
3505 g_return_val_if_fail(item != NULL, NULL);
3506 g_return_val_if_fail(file != NULL, NULL);
3508 if (folder_has_parent_of_type(item, F_QUEUE)) {
3509 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
3510 } else if (folder_has_parent_of_type(item, F_DRAFT)) {
3511 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
3514 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3515 if (!msginfo) return NULL;
3517 msginfo->plaintext_file = g_strdup(file);
3518 msginfo->folder = item;
3523 GSList *imap_get_msginfos(Folder *folder, FolderItem *item,
3524 GSList *msgnum_list)
3526 IMAPSession *session;
3527 MsgInfoList *ret = NULL;
3530 debug_print("get_msginfos\n");
3532 g_return_val_if_fail(folder != NULL, NULL);
3533 g_return_val_if_fail(item != NULL, NULL);
3534 g_return_val_if_fail(msgnum_list != NULL, NULL);
3536 debug_print("getting session...\n");
3537 session = imap_session_get(folder);
3538 g_return_val_if_fail(session != NULL, NULL);
3540 debug_print("IMAP getting msginfos\n");
3541 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3542 NULL, NULL, NULL, NULL, FALSE);
3543 if (ok != IMAP_SUCCESS) {
3547 if (!(folder_has_parent_of_type(item, F_DRAFT) ||
3548 folder_has_parent_of_type(item, F_QUEUE))) {
3549 ret = g_slist_concat(ret,
3550 imap_get_uncached_messages(session, item,
3553 MsgNumberList *sorted_list, *elem, *llast = NULL;
3554 gint startnum, lastnum;
3556 sorted_list = g_slist_sort(g_slist_copy(msgnum_list), g_int_compare);
3558 startnum = lastnum = GPOINTER_TO_INT(sorted_list->data);
3560 llast = g_slist_last(ret);
3561 for (elem = sorted_list;; elem = g_slist_next(elem)) {
3565 num = GPOINTER_TO_INT(elem->data);
3567 if (num > lastnum + 1 || elem == NULL) {
3569 for (i = startnum; i <= lastnum; ++i) {
3572 file = imap_fetch_msg(folder, item, i);
3574 MsgInfo *msginfo = imap_parse_msg(file, item);
3575 if (msginfo != NULL) {
3576 msginfo->msgnum = i;
3578 llast = ret = g_slist_append(ret, msginfo);
3580 llast = g_slist_append(llast, msginfo);
3581 llast = llast->next;
3586 session_set_access_time(SESSION(session));
3597 g_slist_free(sorted_list);
3603 MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid)
3605 MsgInfo *msginfo = NULL;
3606 MsgInfoList *msginfolist;
3607 MsgNumberList numlist;
3609 numlist.next = NULL;
3610 numlist.data = GINT_TO_POINTER(uid);
3612 msginfolist = imap_get_msginfos(folder, item, &numlist);
3613 if (msginfolist != NULL) {
3614 msginfo = msginfolist->data;
3615 g_slist_free(msginfolist);
3621 gboolean imap_scan_required(Folder *folder, FolderItem *_item)
3623 IMAPSession *session;
3624 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3625 gint ok, exists = 0, unseen = 0;
3626 guint32 uid_next = 0, uid_val = 0;
3627 gboolean selected_folder;
3629 g_return_val_if_fail(folder != NULL, FALSE);
3630 g_return_val_if_fail(item != NULL, FALSE);
3631 g_return_val_if_fail(item->item.folder != NULL, FALSE);
3632 g_return_val_if_fail(FOLDER_CLASS(item->item.folder) == &imap_class, FALSE);
3634 if (item->item.path == NULL)
3637 debug_print("getting session...\n");
3638 session = imap_session_get(folder);
3639 g_return_val_if_fail(session != NULL, FALSE);
3641 selected_folder = (session->mbox != NULL) &&
3642 (!strcmp(session->mbox, item->item.path));
3643 if (selected_folder && time(NULL) - item->use_cache < 2) {
3644 ok = imap_cmd_noop(session);
3645 if (ok != IMAP_SUCCESS) {
3646 debug_print("disconnected!\n");
3647 session = imap_reconnect_if_possible(folder, session);
3648 if (session == NULL)
3652 if (session->folder_content_changed
3653 || session->exists != item->item.total_msgs) {
3658 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path, IMAP_FOLDER_ITEM(item),
3659 &exists, &uid_next, &uid_val, &unseen, FALSE);
3660 if (ok != IMAP_SUCCESS) {
3665 item->use_cache = time(NULL);
3666 item->c_messages = exists;
3667 item->c_uid_next = uid_next;
3668 item->c_uid_validity = uid_val;
3669 item->c_unseen = unseen;
3670 item->item.last_num = uid_next - 1;
3671 debug_print("uidnext %d, item->uid_next %d, exists %d, item->item.total_msgs %d\n",
3672 uid_next, item->uid_next, exists, item->item.total_msgs);
3673 if ((uid_next != item->uid_next) || (exists != item->item.total_msgs)
3674 || unseen != item->item.unread_msgs || uid_val != item->item.mtime) {
3683 void imap_change_flags(Folder *folder, FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
3685 IMAPSession *session;
3686 IMAPFlags flags_set = 0, flags_unset = 0;
3687 gint ok = IMAP_SUCCESS;
3688 MsgNumberList numlist;
3689 hashtable_data *ht_data = NULL;
3691 g_return_if_fail(folder != NULL);
3692 g_return_if_fail(folder->klass == &imap_class);
3693 g_return_if_fail(item != NULL);
3694 g_return_if_fail(item->folder == folder);
3695 g_return_if_fail(msginfo != NULL);
3696 g_return_if_fail(msginfo->folder == item);
3698 if (!MSG_IS_MARKED(msginfo->flags) && (newflags & MSG_MARKED))
3699 flags_set |= IMAP_FLAG_FLAGGED;
3700 if ( MSG_IS_MARKED(msginfo->flags) && !(newflags & MSG_MARKED))
3701 flags_unset |= IMAP_FLAG_FLAGGED;
3703 if (!MSG_IS_UNREAD(msginfo->flags) && (newflags & MSG_UNREAD))
3704 flags_unset |= IMAP_FLAG_SEEN;
3705 if ( MSG_IS_UNREAD(msginfo->flags) && !(newflags & MSG_UNREAD))
3706 flags_set |= IMAP_FLAG_SEEN;
3708 if (!MSG_IS_REPLIED(msginfo->flags) && (newflags & MSG_REPLIED))
3709 flags_set |= IMAP_FLAG_ANSWERED;
3710 if ( MSG_IS_REPLIED(msginfo->flags) && !(newflags & MSG_REPLIED))
3711 flags_unset |= IMAP_FLAG_ANSWERED;
3713 if (!MSG_IS_DELETED(msginfo->flags) && (newflags & MSG_DELETED))
3714 flags_set |= IMAP_FLAG_DELETED;
3715 if ( MSG_IS_DELETED(msginfo->flags) && !(newflags & MSG_DELETED))
3716 flags_unset |= IMAP_FLAG_DELETED;
3718 if (!flags_set && !flags_unset) {
3719 /* the changed flags were not translatable to IMAP-speak.
3720 * like MSG_POSTFILTERED, so just apply. */
3721 msginfo->flags.perm_flags = newflags;
3725 debug_print("getting session...\n");
3726 session = imap_session_get(folder);
3731 if ((ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
3732 NULL, NULL, NULL, NULL, FALSE)) != IMAP_SUCCESS) {
3736 numlist.next = NULL;
3737 numlist.data = GINT_TO_POINTER(msginfo->msgnum);
3739 if (IMAP_FOLDER_ITEM(item)->batching) {
3740 /* instead of performing an UID STORE command for each message change,
3741 * as a lot of them can change "together", we just fill in hashtables
3742 * and defer the treatment so that we're able to send only one
3745 debug_print("IMAP batch mode on, deferring flags change\n");
3747 ht_data = g_hash_table_lookup(IMAP_FOLDER_ITEM(item)->flags_set_table,
3748 GINT_TO_POINTER(flags_set));
3749 if (ht_data == NULL) {
3750 ht_data = g_new0(hashtable_data, 1);
3751 ht_data->session = session;
3752 ht_data->item = IMAP_FOLDER_ITEM(item);
3753 g_hash_table_insert(IMAP_FOLDER_ITEM(item)->flags_set_table,
3754 GINT_TO_POINTER(flags_set), ht_data);
3756 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
3757 ht_data->msglist = g_slist_prepend(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum));
3760 ht_data = g_hash_table_lookup(IMAP_FOLDER_ITEM(item)->flags_unset_table,
3761 GINT_TO_POINTER(flags_unset));
3762 if (ht_data == NULL) {
3763 ht_data = g_new0(hashtable_data, 1);
3764 ht_data->session = session;
3765 ht_data->item = IMAP_FOLDER_ITEM(item);
3766 g_hash_table_insert(IMAP_FOLDER_ITEM(item)->flags_unset_table,
3767 GINT_TO_POINTER(flags_unset), ht_data);
3769 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
3770 ht_data->msglist = g_slist_prepend(ht_data->msglist,
3771 GINT_TO_POINTER(msginfo->msgnum));
3774 debug_print("IMAP changing flags\n");
3776 ok = imap_set_message_flags(session, &numlist, flags_set, TRUE);
3777 if (ok != IMAP_SUCCESS) {
3784 ok = imap_set_message_flags(session, &numlist, flags_unset, FALSE);
3785 if (ok != IMAP_SUCCESS) {
3791 msginfo->flags.perm_flags = newflags;
3796 static gint imap_remove_msg(Folder *folder, FolderItem *item, gint uid)
3799 IMAPSession *session;
3801 MsgNumberList numlist;
3803 g_return_val_if_fail(folder != NULL, -1);
3804 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3805 g_return_val_if_fail(item != NULL, -1);
3807 debug_print("getting session...\n");
3808 session = imap_session_get(folder);
3809 if (!session) return -1;
3811 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3812 NULL, NULL, NULL, NULL, FALSE);
3813 if (ok != IMAP_SUCCESS) {
3817 numlist.next = NULL;
3818 numlist.data = GINT_TO_POINTER(uid);
3820 ok = imap_set_message_flags
3821 (session, &numlist, IMAP_FLAG_DELETED, TRUE);
3822 if (ok != IMAP_SUCCESS) {
3823 log_warning(LOG_PROTOCOL, _("can't set deleted flags: %d\n"), uid);
3828 if (!session->uidplus) {
3829 ok = imap_cmd_expunge(session);
3833 uidstr = g_strdup_printf("%u", uid);
3834 ok = imap_cmd_expunge(session);
3837 if (ok != IMAP_SUCCESS) {
3838 log_warning(LOG_PROTOCOL, _("can't expunge\n"));
3843 IMAP_FOLDER_ITEM(item)->uid_list = g_slist_remove(
3844 IMAP_FOLDER_ITEM(item)->uid_list, numlist.data);
3845 dir = folder_item_get_path(item);
3846 if (is_dir_exist(dir))
3847 remove_numbered_files(dir, uid, uid);
3850 return IMAP_SUCCESS;
3853 static gint compare_msginfo(gconstpointer a, gconstpointer b)
3855 return ((MsgInfo *)a)->msgnum - ((MsgInfo *)b)->msgnum;
3858 static guint gslist_find_next_num(MsgNumberList **list, guint num)
3862 g_return_val_if_fail(list != NULL, -1);
3864 for (elem = *list; elem != NULL; elem = g_slist_next(elem))
3865 if (GPOINTER_TO_INT(elem->data) >= num)
3868 return elem != NULL ? GPOINTER_TO_INT(elem->data) : (gint)-1;
3872 * NEW and DELETED flags are not syncronized
3873 * - The NEW/RECENT flags in IMAP folders can not really be directly
3874 * modified by Sylpheed
3875 * - The DELETE/DELETED flag in IMAP and Sylpheed don't have the same
3876 * meaning, in IMAP it always removes the messages from the FolderItem
3877 * in Sylpheed it can mean to move the message to trash
3880 typedef struct _get_flags_data {
3883 MsgInfoList *msginfo_list;
3884 GRelation *msgflags;
3885 gboolean full_search;
3889 static /*gint*/ void *imap_get_flags_thread(void *data)
3891 get_flags_data *stuff = (get_flags_data *)data;
3892 Folder *folder = stuff->folder;
3893 FolderItem *item = stuff->item;
3894 MsgInfoList *msginfo_list = stuff->msginfo_list;
3895 GRelation *msgflags = stuff->msgflags;
3896 gboolean full_search = stuff->full_search;
3897 IMAPSession *session;
3898 GSList *sorted_list = NULL;
3899 GSList *unseen = NULL, *answered = NULL, *flagged = NULL, *deleted = NULL;
3900 GSList *p_unseen, *p_answered, *p_flagged, *p_deleted;
3902 GSList *seq_list, *cur;
3903 gboolean reverse_seen = FALSE;
3906 gint exists_cnt, unseen_cnt;
3907 gboolean selected_folder;
3909 if (folder == NULL || item == NULL) {
3911 return GINT_TO_POINTER(-1);
3914 debug_print("getting session...\n");
3915 session = imap_session_get(folder);
3916 if (session == NULL) {
3918 return GINT_TO_POINTER(-1);
3921 selected_folder = (session->mbox != NULL) &&
3922 (!strcmp(session->mbox, item->path));
3924 if (!selected_folder) {
3925 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3926 &exists_cnt, NULL, &unseen_cnt, NULL, TRUE);
3927 if (ok != IMAP_SUCCESS) {
3930 return GINT_TO_POINTER(-1);
3933 if (unseen_cnt > exists_cnt / 2)
3934 reverse_seen = TRUE;
3937 if (item->unread_msgs > item->total_msgs / 2)
3938 reverse_seen = TRUE;
3941 cmd_buf = g_string_new(NULL);
3943 sorted_list = g_slist_sort(g_slist_copy(msginfo_list), compare_msginfo);
3945 seq_list = imap_get_lep_set_from_msglist(msginfo_list);
3947 struct mailimap_set * set;
3948 set = mailimap_set_new_interval(1, 0);
3949 seq_list = g_slist_append(NULL, set);
3952 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
3953 struct mailimap_set * imapset;
3954 clist * lep_uidlist;
3957 imapset = cur->data;
3959 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SEEN,
3960 full_search ? NULL:imapset, &lep_uidlist);
3963 r = imap_threaded_search(folder,
3964 IMAP_SEARCH_TYPE_UNSEEN,
3965 full_search ? NULL:imapset, &lep_uidlist);
3967 if (r == MAILIMAP_NO_ERROR) {
3970 uidlist = imap_uid_list_from_lep(lep_uidlist);
3971 mailimap_search_result_free(lep_uidlist);