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;
116 struct _IMAPNameSpace
122 #define IMAP_SUCCESS 0
123 #define IMAP_SOCKET 2
124 #define IMAP_AUTHFAIL 3
125 #define IMAP_PROTOCOL 4
126 #define IMAP_SYNTAX 5
130 #define IMAPBUFSIZE 8192
134 IMAP_FLAG_SEEN = 1 << 0,
135 IMAP_FLAG_ANSWERED = 1 << 1,
136 IMAP_FLAG_FLAGGED = 1 << 2,
137 IMAP_FLAG_DELETED = 1 << 3,
138 IMAP_FLAG_DRAFT = 1 << 4
141 #define IMAP_IS_SEEN(flags) ((flags & IMAP_FLAG_SEEN) != 0)
142 #define IMAP_IS_ANSWERED(flags) ((flags & IMAP_FLAG_ANSWERED) != 0)
143 #define IMAP_IS_FLAGGED(flags) ((flags & IMAP_FLAG_FLAGGED) != 0)
144 #define IMAP_IS_DELETED(flags) ((flags & IMAP_FLAG_DELETED) != 0)
145 #define IMAP_IS_DRAFT(flags) ((flags & IMAP_FLAG_DRAFT) != 0)
148 #define IMAP4_PORT 143
150 #define IMAPS_PORT 993
153 #define IMAP_CMD_LIMIT 1000
155 struct _IMAPFolderItem
167 guint32 c_uid_validity;
170 GHashTable *flags_set_table;
171 GHashTable *flags_unset_table;
176 static XMLTag *imap_item_get_xml(Folder *folder, FolderItem *item);
177 static void imap_item_set_xml(Folder *folder, FolderItem *item, XMLTag *tag);
179 static void imap_folder_init (Folder *folder,
183 static Folder *imap_folder_new (const gchar *name,
185 static void imap_folder_destroy (Folder *folder);
187 static IMAPSession *imap_session_new (Folder *folder,
188 const PrefsAccount *account);
189 static void imap_session_authenticate(IMAPSession *session,
190 const PrefsAccount *account);
191 static void imap_session_destroy (Session *session);
193 static gchar *imap_fetch_msg (Folder *folder,
196 static gchar *imap_fetch_msg_full (Folder *folder,
201 static gint imap_add_msg (Folder *folder,
205 static gint imap_add_msgs (Folder *folder,
208 GRelation *relation);
210 static gint imap_copy_msg (Folder *folder,
213 static gint imap_copy_msgs (Folder *folder,
215 MsgInfoList *msglist,
216 GRelation *relation);
218 static gint imap_remove_msg (Folder *folder,
221 static gint imap_remove_msgs (Folder *folder,
223 MsgInfoList *msglist,
224 GRelation *relation);
225 static gint imap_remove_all_msg (Folder *folder,
228 static gboolean imap_is_msg_changed (Folder *folder,
232 static gint imap_close (Folder *folder,
235 static gint imap_scan_tree (Folder *folder);
237 static gint imap_create_tree (Folder *folder);
239 static FolderItem *imap_create_folder (Folder *folder,
242 static gint imap_rename_folder (Folder *folder,
245 static gint imap_remove_folder (Folder *folder,
248 static FolderItem *imap_folder_item_new (Folder *folder);
249 static void imap_folder_item_destroy (Folder *folder,
252 static IMAPSession *imap_session_get (Folder *folder);
254 static gint imap_auth (IMAPSession *session,
259 static gint imap_scan_tree_recursive (IMAPSession *session,
263 static void imap_create_missing_folders (Folder *folder);
264 static FolderItem *imap_create_special_folder
266 SpecialFolderItemType stype,
269 static gint imap_do_copy_msgs (Folder *folder,
271 MsgInfoList *msglist,
272 GRelation *relation);
274 static void imap_delete_all_cached_messages (FolderItem *item);
275 static void imap_set_batch (Folder *folder,
278 static gint imap_set_message_flags (IMAPSession *session,
279 MsgNumberList *numlist,
282 static gint imap_select (IMAPSession *session,
288 guint32 *uid_validity,
290 static gint imap_status (IMAPSession *session,
293 IMAPFolderItem *item,
296 guint32 *uid_validity,
300 static gchar imap_get_path_separator (IMAPSession *session,
303 static gchar *imap_get_real_path (IMAPSession *session,
306 static void imap_synchronise (FolderItem *item);
308 static void imap_free_capabilities (IMAPSession *session);
310 /* low-level IMAP4rev1 commands */
311 static gint imap_cmd_login (IMAPSession *session,
315 static gint imap_cmd_noop (IMAPSession *session);
317 static gint imap_cmd_starttls (IMAPSession *session);
319 static gint imap_cmd_select (IMAPSession *session,
324 guint32 *uid_validity,
326 static gint imap_cmd_examine (IMAPSession *session,
331 guint32 *uid_validity,
333 static gint imap_cmd_create (IMAPSession *sock,
334 const gchar *folder);
335 static gint imap_cmd_rename (IMAPSession *sock,
336 const gchar *oldfolder,
337 const gchar *newfolder);
338 static gint imap_cmd_delete (IMAPSession *session,
339 const gchar *folder);
340 static gint imap_cmd_fetch (IMAPSession *sock,
342 const gchar *filename,
345 static gint imap_cmd_append (IMAPSession *session,
346 const gchar *destfolder,
350 static gint imap_cmd_copy (IMAPSession *session,
351 struct mailimap_set * set,
352 const gchar *destfolder,
353 GRelation *uid_mapping,
354 struct mailimap_set ** source,
355 struct mailimap_set ** dest);
356 static gint imap_cmd_store (IMAPSession *session,
357 struct mailimap_set * set,
360 static gint imap_cmd_expunge (IMAPSession *session);
362 static void imap_path_separator_subst (gchar *str,
365 static gchar *imap_utf8_to_modified_utf7 (const gchar *from);
366 static gchar *imap_modified_utf7_to_utf8 (const gchar *mutf7_str);
368 static gboolean imap_rename_folder_func (GNode *node,
370 static gint imap_get_num_list (Folder *folder,
373 gboolean *old_uids_valid);
374 static GSList *imap_get_msginfos (Folder *folder,
376 GSList *msgnum_list);
377 static MsgInfo *imap_get_msginfo (Folder *folder,
380 static gboolean imap_scan_required (Folder *folder,
382 static void imap_change_flags (Folder *folder,
385 MsgPermFlags newflags);
386 static gint imap_get_flags (Folder *folder,
388 MsgInfoList *msglist,
389 GRelation *msgflags);
390 static gchar *imap_folder_get_path (Folder *folder);
391 static gchar *imap_item_get_path (Folder *folder,
393 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item);
396 /* data types conversion libetpan <-> claws */
397 static GSList * imap_list_from_lep(IMAPFolder * folder,
398 clist * list, const gchar * real_path, gboolean all);
399 static GSList * imap_get_lep_set_from_numlist(MsgNumberList *numlist);
400 static GSList * imap_get_lep_set_from_msglist(MsgInfoList *msglist);
401 static GSList * imap_uid_list_from_lep(clist * list);
402 static GSList * imap_uid_list_from_lep_tab(carray * list);
403 static MsgInfo *imap_envelope_from_lep(struct imap_fetch_env_info * info,
405 static void imap_lep_set_free(GSList *seq_list);
406 static struct mailimap_flag_list * imap_flag_to_lep(IMAPFlags flags);
408 typedef struct _hashtable_data {
409 IMAPSession *session;
411 IMAPFolderItem *item;
414 static FolderClass imap_class;
416 typedef struct _thread_data {
426 FolderClass *imap_get_class(void)
428 if (imap_class.idstr == NULL) {
429 imap_class.type = F_IMAP;
430 imap_class.idstr = "imap";
431 imap_class.uistr = "IMAP4";
433 /* Folder functions */
434 imap_class.new_folder = imap_folder_new;
435 imap_class.destroy_folder = imap_folder_destroy;
436 imap_class.scan_tree = imap_scan_tree;
437 imap_class.create_tree = imap_create_tree;
439 /* FolderItem functions */
440 imap_class.item_new = imap_folder_item_new;
441 imap_class.item_destroy = imap_folder_item_destroy;
442 imap_class.item_get_path = imap_item_get_path;
443 imap_class.create_folder = imap_create_folder;
444 imap_class.rename_folder = imap_rename_folder;
445 imap_class.remove_folder = imap_remove_folder;
446 imap_class.close = imap_close;
447 imap_class.get_num_list = imap_get_num_list;
448 imap_class.scan_required = imap_scan_required;
449 imap_class.set_xml = folder_set_xml;
450 imap_class.get_xml = folder_get_xml;
451 imap_class.item_set_xml = imap_item_set_xml;
452 imap_class.item_get_xml = imap_item_get_xml;
454 /* Message functions */
455 imap_class.get_msginfo = imap_get_msginfo;
456 imap_class.get_msginfos = imap_get_msginfos;
457 imap_class.fetch_msg = imap_fetch_msg;
458 imap_class.fetch_msg_full = imap_fetch_msg_full;
459 imap_class.add_msg = imap_add_msg;
460 imap_class.add_msgs = imap_add_msgs;
461 imap_class.copy_msg = imap_copy_msg;
462 imap_class.copy_msgs = imap_copy_msgs;
463 imap_class.remove_msg = imap_remove_msg;
464 imap_class.remove_msgs = imap_remove_msgs;
465 imap_class.remove_all_msg = imap_remove_all_msg;
466 imap_class.is_msg_changed = imap_is_msg_changed;
467 imap_class.change_flags = imap_change_flags;
468 imap_class.get_flags = imap_get_flags;
469 imap_class.set_batch = imap_set_batch;
470 imap_class.synchronise = imap_synchronise;
472 pthread_mutex_init(&imap_mutex, NULL);
479 static Folder *imap_folder_new(const gchar *name, const gchar *path)
483 folder = (Folder *)g_new0(IMAPFolder, 1);
484 folder->klass = &imap_class;
485 imap_folder_init(folder, name, path);
490 static void imap_folder_destroy(Folder *folder)
492 while (imap_folder_get_refcnt(folder) > 0)
493 gtk_main_iteration();
495 folder_remote_folder_destroy(REMOTE_FOLDER(folder));
499 static void imap_folder_init(Folder *folder, const gchar *name,
502 folder_remote_folder_init((Folder *)folder, name, path);
505 static FolderItem *imap_folder_item_new(Folder *folder)
507 IMAPFolderItem *item;
509 item = g_new0(IMAPFolderItem, 1);
512 item->uid_list = NULL;
514 return (FolderItem *)item;
517 static void imap_folder_item_destroy(Folder *folder, FolderItem *_item)
519 IMAPFolderItem *item = (IMAPFolderItem *)_item;
521 g_return_if_fail(item != NULL);
522 g_slist_free(item->uid_list);
527 static gboolean imap_reset_uid_lists_func(GNode *node, gpointer data)
529 IMAPFolderItem *item = (IMAPFolderItem *)node->data;
532 g_slist_free(item->uid_list);
533 item->uid_list = NULL;
538 static void imap_reset_uid_lists(Folder *folder)
540 if(folder->node == NULL)
543 /* Destroy all uid lists and rest last uid */
544 g_node_traverse(folder->node, G_IN_ORDER, G_TRAVERSE_ALL, -1, imap_reset_uid_lists_func, NULL);
547 static int imap_get_capabilities(IMAPSession *session)
549 struct mailimap_capability_data *capabilities = NULL;
553 if (session->capability != NULL)
554 return MAILIMAP_NO_ERROR;
556 capabilities = imap_threaded_capability(session->folder, &result);
558 if (result != MAILIMAP_NO_ERROR) {
559 return MAILIMAP_ERROR_CAPABILITY;
562 if (capabilities == NULL) {
563 return MAILIMAP_NO_ERROR;
566 for(cur = clist_begin(capabilities->cap_list) ; cur != NULL ;
567 cur = clist_next(cur)) {
568 struct mailimap_capability * cap =
570 if (!cap || cap->cap_data.cap_name == NULL)
572 session->capability = g_slist_append
573 (session->capability,
574 g_strdup(cap->cap_data.cap_name));
575 debug_print("got capa %s\n", cap->cap_data.cap_name);
577 mailimap_capability_data_free(capabilities);
578 return MAILIMAP_NO_ERROR;
581 static gboolean imap_has_capability(IMAPSession *session, const gchar *cap)
584 for (cur = session->capability; cur; cur = cur->next) {
585 if (!g_ascii_strcasecmp(cur->data, cap))
591 static gint imap_auth(IMAPSession *session, const gchar *user, const gchar *pass,
594 gint ok = IMAP_ERROR;
595 static time_t last_login_err = 0;
596 gchar *ext_info = "";
598 if (imap_get_capabilities(session) != MAILIMAP_NO_ERROR)
603 ok = imap_cmd_login(session, user, pass, "ANONYMOUS");
605 case IMAP_AUTH_CRAM_MD5:
606 ok = imap_cmd_login(session, user, pass, "CRAM-MD5");
608 case IMAP_AUTH_LOGIN:
609 ok = imap_cmd_login(session, user, pass, "LOGIN");
611 case IMAP_AUTH_GSSAPI:
612 ok = imap_cmd_login(session, user, pass, "GSSAPI");
615 debug_print("capabilities:\n"
620 imap_has_capability(session, "ANONYMOUS"),
621 imap_has_capability(session, "CRAM-MD5"),
622 imap_has_capability(session, "LOGIN"),
623 imap_has_capability(session, "GSSAPI"));
624 if (imap_has_capability(session, "CRAM-MD5"))
625 ok = imap_cmd_login(session, user, pass, "CRAM-MD5");
626 if (ok == IMAP_ERROR && imap_has_capability(session, "GSSAPI"))
627 ok = imap_cmd_login(session, user, pass, "GSSAPI");
628 if (ok == IMAP_ERROR) /* we always try LOGIN before giving up */
629 ok = imap_cmd_login(session, user, pass, "LOGIN");
632 if (ok == IMAP_SUCCESS)
633 session->authenticated = TRUE;
635 if (type == IMAP_AUTH_CRAM_MD5) {
636 ext_info = _("\n\nCRAM-MD5 logins only work if libetpan has been "
637 "compiled with SASL support and the "
638 "CRAM-MD5 SASL plugin is installed.");
641 if (time(NULL) - last_login_err > 10) {
642 if (!prefs_common.no_recv_err_panel) {
643 alertpanel_error(_("Connection to %s failed: "
645 SESSION(session)->server, ext_info);
647 log_error(LOG_PROTOCOL, _("Connection to %s failed: "
648 "login refused.%s\n"),
649 SESSION(session)->server, ext_info);
652 last_login_err = time(NULL);
657 static IMAPSession *imap_reconnect_if_possible(Folder *folder, IMAPSession *session)
659 RemoteFolder *rfolder = REMOTE_FOLDER(folder);
660 /* Check if this is the first try to establish a
661 connection, if yes we don't try to reconnect */
662 debug_print("reconnecting\n");
663 if (rfolder->session == NULL) {
664 log_warning(LOG_PROTOCOL, _("Connecting to %s failed"),
665 folder->account->recv_server);
666 session_destroy(SESSION(session));
669 log_warning(LOG_PROTOCOL, _("IMAP4 connection to %s has been"
670 " disconnected. Reconnecting...\n"),
671 folder->account->recv_server);
672 statusbar_print_all(_("IMAP4 connection to %s has been"
673 " disconnected. Reconnecting...\n"),
674 folder->account->recv_server);
675 SESSION(session)->state = SESSION_DISCONNECTED;
676 session_destroy(SESSION(session));
677 /* Clear folders session to make imap_session_get create
678 a new session, because of rfolder->session == NULL
679 it will not try to reconnect again and so avoid an
681 rfolder->session = NULL;
682 debug_print("getting session...\n");
683 session = imap_session_get(folder);
684 rfolder->session = SESSION(session);
690 static void lock_session(IMAPSession *session)
695 debug_print("locking session %p (%d)\n", session, session->busy);
697 debug_print(" SESSION WAS LOCKED !! \n");
698 session->busy = TRUE;
699 mainwin = mainwindow_get_mainwindow();
701 toolbar_main_set_sensitive(mainwin);
702 main_window_set_menu_sensitive(mainwin);
705 debug_print("can't lock null session\n");
709 static void unlock_session(IMAPSession *session)
714 debug_print("unlocking session %p\n", session);
715 session->busy = FALSE;
716 mainwin = mainwindow_get_mainwindow();
718 toolbar_main_set_sensitive(mainwin);
719 main_window_set_menu_sensitive(mainwin);
722 debug_print("can't unlock null session\n");
726 static IMAPSession *imap_session_get(Folder *folder)
728 RemoteFolder *rfolder = REMOTE_FOLDER(folder);
729 IMAPSession *session = NULL;
731 g_return_val_if_fail(folder != NULL, NULL);
732 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, NULL);
733 g_return_val_if_fail(folder->account != NULL, NULL);
735 if (prefs_common.work_offline &&
736 !inc_offline_should_override(FALSE,
737 _("Claws Mail needs network access in order "
738 "to access the IMAP server."))) {
742 /* Make sure we have a session */
743 if (rfolder->session != NULL) {
744 session = IMAP_SESSION(rfolder->session);
745 } else if (rfolder->connecting) {
746 debug_print("already connecting\n");
749 imap_reset_uid_lists(folder);
750 if (time(NULL) - rfolder->last_failure <= 2)
752 rfolder->connecting = TRUE;
753 session = imap_session_new(folder, folder->account);
755 if(session == NULL) {
756 rfolder->last_failure = time(NULL);
757 rfolder->connecting = FALSE;
761 /* Make sure session is authenticated */
762 if (!IMAP_SESSION(session)->authenticated)
763 imap_session_authenticate(IMAP_SESSION(session), folder->account);
765 if (!IMAP_SESSION(session)->authenticated) {
766 imap_threaded_disconnect(session->folder);
767 SESSION(session)->state = SESSION_DISCONNECTED;
768 session_destroy(SESSION(session));
769 rfolder->session = NULL;
770 rfolder->last_failure = time(NULL);
771 rfolder->connecting = FALSE;
775 lock_session(session);
777 /* I think the point of this code is to avoid sending a
778 * keepalive if we've used the session recently and therefore
779 * think it's still alive. Unfortunately, most of the code
780 * does not yet check for errors on the socket, and so if the
781 * connection drops we don't notice until the timeout expires.
782 * A better solution than sending a NOOP every time would be
783 * for every command to be prepared to retry until it is
784 * successfully sent. -- mbp */
785 if ((time(NULL) - SESSION(session)->last_access_time > SESSION_TIMEOUT_INTERVAL) || session->cancelled) {
786 /* verify that the session is still alive */
787 if (imap_cmd_noop(session) != IMAP_SUCCESS) {
788 debug_print("disconnected!\n");
789 session = imap_reconnect_if_possible(folder, session);
792 session->cancelled = FALSE;
795 rfolder->session = SESSION(session);
796 rfolder->connecting = FALSE;
798 return IMAP_SESSION(session);
801 static IMAPSession *imap_session_new(Folder * folder,
802 const PrefsAccount *account)
804 IMAPSession *session;
807 int authenticated = FALSE;
810 /* FIXME: IMAP over SSL only... */
813 port = account->set_imapport ? account->imapport
814 : account->ssl_imap == SSL_TUNNEL ? IMAPS_PORT : IMAP4_PORT;
815 ssl_type = account->ssl_imap;
817 if (account->ssl_imap != SSL_NONE) {
818 if (alertpanel_full(_("Insecure connection"),
819 _("This connection is configured to be secured "
820 "using SSL, but SSL is not available in this "
821 "build of Claws Mail. \n\n"
822 "Do you want to continue connecting to this "
823 "server? The communication would not be "
825 GTK_STOCK_CANCEL, _("Con_tinue connecting"),
826 NULL, FALSE, NULL, ALERT_WARNING,
827 G_ALERTDEFAULT) != G_ALERTALTERNATE)
830 port = account->set_imapport ? account->imapport
835 statusbar_print_all(_("Connecting to IMAP4 server: %s..."), folder->account->recv_server);
836 if (account->set_tunnelcmd) {
837 r = imap_threaded_connect_cmd(folder,
839 account->recv_server,
844 if (ssl_type == SSL_TUNNEL) {
845 r = imap_threaded_connect_ssl(folder,
846 account->recv_server,
852 r = imap_threaded_connect(folder,
853 account->recv_server,
859 if (r == MAILIMAP_NO_ERROR_AUTHENTICATED) {
860 authenticated = TRUE;
862 else if (r == MAILIMAP_NO_ERROR_NON_AUTHENTICATED) {
863 authenticated = FALSE;
866 #if (LIBETPAN_VERSION_MAJOR > 0 || LIBETPAN_VERSION_MINOR > 48)
868 if (r == MAILIMAP_ERROR_SSL)
869 log_error(LOG_PROTOCOL, _("SSL handshake failed\n"));
872 if(!prefs_common.no_recv_err_panel) {
873 alertpanel_error_log(_("Can't connect to IMAP4 server: %s:%d"),
874 account->recv_server, port);
876 log_error(LOG_PROTOCOL, _("Can't connect to IMAP4 server: %s:%d\n"),
877 account->recv_server, port);
883 session = g_new0(IMAPSession, 1);
884 session_init(SESSION(session));
885 SESSION(session)->type = SESSION_IMAP;
886 SESSION(session)->server = g_strdup(account->recv_server);
887 SESSION(session)->sock = NULL;
889 SESSION(session)->destroy = imap_session_destroy;
891 session->capability = NULL;
893 session->authenticated = authenticated;
894 session->mbox = NULL;
895 session->cmd_count = 0;
896 session->folder = folder;
897 IMAP_FOLDER(session->folder)->last_seen_separator = 0;
900 if (account->ssl_imap == SSL_STARTTLS) {
903 ok = imap_cmd_starttls(session);
904 if (ok != IMAP_SUCCESS) {
905 log_warning(LOG_PROTOCOL, _("Can't start TLS session.\n"));
906 session_destroy(SESSION(session));
910 imap_free_capabilities(session);
911 session->authenticated = FALSE;
912 session->uidplus = FALSE;
913 session->cmd_count = 1;
916 log_message(LOG_PROTOCOL, "IMAP connection is %s-authenticated\n",
917 (session->authenticated) ? "pre" : "un");
922 static void imap_session_authenticate(IMAPSession *session,
923 const PrefsAccount *account)
925 gchar *pass, *acc_pass;
926 gboolean failed = FALSE;
928 g_return_if_fail(account->userid != NULL);
929 acc_pass = account->passwd;
932 if (!pass && account->imap_auth_type != IMAP_AUTH_ANON) {
934 tmp_pass = input_dialog_query_password(account->recv_server, account->userid);
937 Xstrdup_a(pass, tmp_pass, {g_free(tmp_pass); return;});
939 } else if (account->imap_auth_type == IMAP_AUTH_ANON) {
942 statusbar_print_all(_("Connecting to IMAP4 server %s...\n"),
943 account->recv_server);
944 if (imap_auth(session, account->userid, pass, account->imap_auth_type) != IMAP_SUCCESS) {
952 if (prefs_common.no_recv_err_panel) {
953 log_error(LOG_PROTOCOL, _("Couldn't login to IMAP server %s."), account->recv_server);
954 mainwindow_show_error();
956 alertpanel_error_log(_("Couldn't login to IMAP server %s."), account->recv_server);
963 session->authenticated = TRUE;
967 static void imap_session_destroy(Session *session)
969 if (session->state != SESSION_DISCONNECTED)
970 imap_threaded_disconnect(IMAP_SESSION(session)->folder);
972 imap_free_capabilities(IMAP_SESSION(session));
973 g_free(IMAP_SESSION(session)->mbox);
974 sock_close(session->sock);
975 session->sock = NULL;
978 static gchar *imap_fetch_msg(Folder *folder, FolderItem *item, gint uid)
980 return imap_fetch_msg_full(folder, item, uid, TRUE, TRUE);
983 static guint get_file_size_with_crs(const gchar *filename)
989 if (filename == NULL)
992 fp = fopen(filename, "rb");
996 while (fgets(buf, sizeof (buf), fp) != NULL) {
998 if (!strstr(buf, "\r\n") && strstr(buf, "\n"))
1006 static gchar *imap_fetch_msg_full(Folder *folder, FolderItem *item, gint uid,
1007 gboolean headers, gboolean body)
1009 gchar *path, *filename;
1010 IMAPSession *session;
1013 g_return_val_if_fail(folder != NULL, NULL);
1014 g_return_val_if_fail(item != NULL, NULL);
1019 path = folder_item_get_path(item);
1020 if (!is_dir_exist(path))
1021 make_dir_hier(path);
1022 filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(uid), NULL);
1024 debug_print("trying to fetch cached %s\n", filename);
1025 if (is_file_exist(filename)) {
1026 /* see whether the local file represents the whole message
1027 * or not. As the IMAP server reports size with \r chars,
1028 * we have to update the local file (UNIX \n only) size */
1029 MsgInfo *cached = msgcache_get_msg(item->cache,uid);
1030 guint have_size = get_file_size_with_crs(filename);
1033 debug_print("message %d has been already %scached (%d/%d).\n", uid,
1034 have_size >= cached->size ? "fully ":"",
1035 have_size, (int)cached->size);
1037 if (cached && (cached->size <= have_size || !body)) {
1038 procmsg_msginfo_free(cached);
1039 file_strip_crs(filename);
1041 } else if (!cached && time(NULL) - get_file_mtime(filename) < 60) {
1042 debug_print("message not cached and file recent, considering file complete\n");
1043 file_strip_crs(filename);
1046 procmsg_msginfo_free(cached);
1050 debug_print("getting session...\n");
1051 session = imap_session_get(folder);
1058 debug_print("IMAP fetching messages\n");
1059 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
1060 NULL, NULL, NULL, NULL, FALSE);
1061 if (ok != IMAP_SUCCESS) {
1062 g_warning("can't select mailbox %s\n", item->path);
1064 unlock_session(session);
1068 debug_print("getting message %d...\n", uid);
1069 ok = imap_cmd_fetch(session, (guint32)uid, filename, headers, body);
1071 if (ok != IMAP_SUCCESS) {
1072 g_warning("can't fetch message %d\n", uid);
1074 unlock_session(session);
1078 unlock_session(session);
1079 file_strip_crs(filename);
1083 static gboolean imap_is_msg_fully_cached(Folder *folder, FolderItem *item, gint uid)
1085 gchar *path, *filename;
1087 MsgInfo *cached = msgcache_get_msg(item->cache,uid);
1092 path = folder_item_get_path(item);
1093 if (!is_dir_exist(path))
1096 filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(uid), NULL);
1098 if (is_file_exist(filename)) {
1099 if (cached && cached->total_size == cached->size) {
1104 size = get_file_size_with_crs(filename);
1107 if (cached && size >= cached->size) {
1108 cached->total_size = cached->size;
1109 procmsg_msginfo_free(cached);
1113 procmsg_msginfo_free(cached);
1117 void imap_cache_msg(FolderItem *item, gint msgnum)
1119 Folder *folder = NULL;
1123 folder = item->folder;
1125 if (!imap_is_msg_fully_cached(folder, item, msgnum)) {
1126 gchar *tmp = imap_fetch_msg_full(folder, item, msgnum, TRUE, TRUE);
1127 debug_print("fetched %s\n", tmp);
1132 static gint imap_add_msg(Folder *folder, FolderItem *dest,
1133 const gchar *file, MsgFlags *flags)
1137 MsgFileInfo fileinfo;
1139 g_return_val_if_fail(file != NULL, -1);
1141 fileinfo.msginfo = NULL;
1142 fileinfo.file = (gchar *)file;
1143 fileinfo.flags = flags;
1144 file_list.data = &fileinfo;
1145 file_list.next = NULL;
1147 ret = imap_add_msgs(folder, dest, &file_list, NULL);
1151 static gint imap_add_msgs(Folder *folder, FolderItem *dest, GSList *file_list,
1152 GRelation *relation)
1155 IMAPSession *session;
1156 guint32 last_uid = 0;
1158 MsgFileInfo *fileinfo;
1160 gint curnum = 0, total = 0;
1163 g_return_val_if_fail(folder != NULL, -1);
1164 g_return_val_if_fail(dest != NULL, -1);
1165 g_return_val_if_fail(file_list != NULL, -1);
1167 debug_print("getting session...\n");
1168 session = imap_session_get(folder);
1172 destdir = imap_get_real_path(session, IMAP_FOLDER(folder), dest->path);
1174 statusbar_print_all(_("Adding messages..."));
1175 total = g_slist_length(file_list);
1176 for (cur = file_list; cur != NULL; cur = cur->next) {
1177 IMAPFlags iflags = 0;
1178 guint32 new_uid = 0;
1179 gchar *real_file = NULL;
1180 fileinfo = (MsgFileInfo *)cur->data;
1182 statusbar_progress_all(curnum, total, total < 10 ? 1:10);
1185 if (fileinfo->flags) {
1186 if (MSG_IS_MARKED(*fileinfo->flags))
1187 iflags |= IMAP_FLAG_FLAGGED;
1188 if (MSG_IS_REPLIED(*fileinfo->flags))
1189 iflags |= IMAP_FLAG_ANSWERED;
1190 if (!MSG_IS_UNREAD(*fileinfo->flags))
1191 iflags |= IMAP_FLAG_SEEN;
1194 if (real_file == NULL)
1195 real_file = g_strdup(fileinfo->file);
1197 if (folder_has_parent_of_type(dest, F_QUEUE) ||
1198 folder_has_parent_of_type(dest, F_OUTBOX) ||
1199 folder_has_parent_of_type(dest, F_DRAFT) ||
1200 folder_has_parent_of_type(dest, F_TRASH))
1201 iflags |= IMAP_FLAG_SEEN;
1203 ok = imap_cmd_append(session, destdir, real_file, iflags,
1206 if (ok != IMAP_SUCCESS) {
1207 g_warning("can't append message %s\n", real_file);
1210 unlock_session(session);
1211 statusbar_progress_all(0,0,0);
1212 statusbar_pop_all();
1215 debug_print("appended new message as %d\n", new_uid);
1216 /* put the local file in the imapcache, so that we don't
1217 * have to fetch it back later. */
1219 gchar *cache_path = folder_item_get_path(dest);
1220 if (!is_dir_exist(cache_path))
1221 make_dir_hier(cache_path);
1222 if (is_dir_exist(cache_path)) {
1223 gchar *cache_file = g_strconcat(
1224 cache_path, G_DIR_SEPARATOR_S,
1225 itos(new_uid), NULL);
1226 copy_file(real_file, cache_file, TRUE);
1227 debug_print("copied to cache: %s\n", cache_file);
1234 if (relation != NULL)
1235 g_relation_insert(relation, fileinfo->msginfo != NULL ?
1236 (gpointer) fileinfo->msginfo : (gpointer) fileinfo,
1237 GINT_TO_POINTER(dest->last_num + 1));
1239 new_uid = dest->last_num+1;
1241 if (last_uid < new_uid) {
1247 statusbar_progress_all(0,0,0);
1248 statusbar_pop_all();
1250 imap_cmd_expunge(session);
1251 unlock_session(session);
1258 static gint imap_do_copy_msgs(Folder *folder, FolderItem *dest,
1259 MsgInfoList *msglist, GRelation *relation)
1263 GSList *seq_list, *cur;
1265 IMAPSession *session;
1266 gint ok = IMAP_SUCCESS;
1267 GRelation *uid_mapping;
1269 gboolean single = FALSE;
1271 g_return_val_if_fail(folder != NULL, -1);
1272 g_return_val_if_fail(dest != NULL, -1);
1273 g_return_val_if_fail(msglist != NULL, -1);
1275 debug_print("getting session...\n");
1276 session = imap_session_get(folder);
1282 msginfo = (MsgInfo *)msglist->data;
1283 if (msglist->next == NULL)
1285 src = msginfo->folder;
1287 g_warning("the src folder is identical to the dest.\n");
1288 unlock_session(session);
1292 if (src->folder != dest->folder) {
1293 GSList *infolist = NULL, *cur;
1295 for (cur = msglist; cur; cur = cur->next) {
1296 msginfo = (MsgInfo *)cur->data;
1297 MsgFileInfo *fileinfo = g_new0(MsgFileInfo, 1);
1298 fileinfo->file = procmsg_get_message_file(msginfo);
1299 fileinfo->flags = &(msginfo->flags);
1300 infolist = g_slist_prepend(infolist, fileinfo);
1302 infolist = g_slist_reverse(infolist);
1303 unlock_session(session);
1304 res = folder_item_add_msgs(dest, infolist, FALSE);
1305 for (cur = infolist; cur; cur = cur->next) {
1306 MsgFileInfo *info = (MsgFileInfo *)cur->data;
1310 g_slist_free(infolist);
1314 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
1315 NULL, NULL, NULL, NULL, FALSE);
1316 if (ok != IMAP_SUCCESS) {
1317 unlock_session(session);
1321 destdir = imap_get_real_path(session, IMAP_FOLDER(folder), dest->path);
1322 seq_list = imap_get_lep_set_from_msglist(msglist);
1323 uid_mapping = g_relation_new(2);
1324 g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
1326 statusbar_print_all(_("Copying messages..."));
1327 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
1328 struct mailimap_set * seq_set;
1329 struct mailimap_set * source = NULL;
1330 struct mailimap_set * dest = NULL;
1331 seq_set = cur->data;
1333 debug_print("Copying messages from %s to %s ...\n",
1334 src->path, destdir);
1336 ok = imap_cmd_copy(session, seq_set, destdir, uid_mapping,
1339 if (ok == IMAP_SUCCESS) {
1340 if (single && relation && source && dest) {
1341 clistiter *l = clist_begin(source->set_list);
1342 struct mailimap_set_item *i = (struct mailimap_set_item *)clist_content(l);
1343 int snum = i->set_first;
1345 l = clist_begin(dest->set_list);
1346 i = (struct mailimap_set_item *)clist_content(l);
1347 dnum = i->set_first;
1348 g_relation_insert(uid_mapping, GINT_TO_POINTER(snum),
1349 GINT_TO_POINTER(dnum));
1355 mailimap_set_free(source);
1357 mailimap_set_free(dest);
1359 if (ok != IMAP_SUCCESS) {
1360 g_relation_destroy(uid_mapping);
1361 imap_lep_set_free(seq_list);
1362 unlock_session(session);
1363 statusbar_pop_all();
1368 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
1369 MsgInfo *msginfo = (MsgInfo *)cur->data;
1372 tuples = g_relation_select(uid_mapping,
1373 GINT_TO_POINTER(msginfo->msgnum),
1375 if (tuples->len > 0) {
1376 gint num = GPOINTER_TO_INT(g_tuples_index(tuples, 0, 1));
1377 g_relation_insert(relation, msginfo,
1378 GINT_TO_POINTER(num));
1381 debug_print("copied new message as %d\n", num);
1382 /* put the local file in the imapcache, so that we don't
1383 * have to fetch it back later. */
1385 gchar *cache_path = folder_item_get_path(msginfo->folder);
1386 gchar *real_file = g_strconcat(
1387 cache_path, G_DIR_SEPARATOR_S,
1388 itos(msginfo->msgnum), NULL);
1389 gchar *cache_file = NULL;
1391 cache_path = folder_item_get_path(dest);
1392 cache_file = g_strconcat(
1393 cache_path, G_DIR_SEPARATOR_S,
1395 if (!is_dir_exist(cache_path))
1396 make_dir_hier(cache_path);
1397 if (is_file_exist(real_file) && is_dir_exist(cache_path)) {
1398 copy_file(real_file, cache_file, TRUE);
1399 debug_print("copied to cache: %s\n", cache_file);
1406 g_relation_insert(relation, msginfo,
1407 GINT_TO_POINTER(0));
1408 g_tuples_destroy(tuples);
1410 statusbar_pop_all();
1412 g_relation_destroy(uid_mapping);
1413 imap_lep_set_free(seq_list);
1417 IMAP_FOLDER_ITEM(dest)->lastuid = 0;
1418 IMAP_FOLDER_ITEM(dest)->uid_next = 0;
1419 g_slist_free(IMAP_FOLDER_ITEM(dest)->uid_list);
1420 IMAP_FOLDER_ITEM(dest)->uid_list = NULL;
1422 unlock_session(session);
1423 if (ok == IMAP_SUCCESS)
1429 static gint imap_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1433 g_return_val_if_fail(msginfo != NULL, -1);
1435 msglist.data = msginfo;
1436 msglist.next = NULL;
1438 return imap_copy_msgs(folder, dest, &msglist, NULL);
1441 static gint imap_copy_msgs(Folder *folder, FolderItem *dest,
1442 MsgInfoList *msglist, GRelation *relation)
1447 g_return_val_if_fail(folder != NULL, -1);
1448 g_return_val_if_fail(dest != NULL, -1);
1449 g_return_val_if_fail(msglist != NULL, -1);
1451 msginfo = (MsgInfo *)msglist->data;
1452 g_return_val_if_fail(msginfo->folder != NULL, -1);
1454 ret = imap_do_copy_msgs(folder, dest, msglist, relation);
1459 static gint imap_do_remove_msgs(Folder *folder, FolderItem *dest,
1460 MsgInfoList *msglist, GRelation *relation)
1462 gchar *destdir, *dir;
1463 GSList *numlist = NULL, *cur;
1465 IMAPSession *session;
1466 gint ok = IMAP_SUCCESS;
1467 GRelation *uid_mapping;
1469 g_return_val_if_fail(folder != NULL, -1);
1470 g_return_val_if_fail(dest != NULL, -1);
1471 g_return_val_if_fail(msglist != NULL, -1);
1473 debug_print("getting session...\n");
1474 session = imap_session_get(folder);
1479 msginfo = (MsgInfo *)msglist->data;
1481 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
1482 NULL, NULL, NULL, NULL, FALSE);
1483 if (ok != IMAP_SUCCESS) {
1484 unlock_session(session);
1488 destdir = imap_get_real_path(session, IMAP_FOLDER(folder), dest->path);
1489 for (cur = msglist; cur; cur = cur->next) {
1490 msginfo = (MsgInfo *)cur->data;
1491 if (!MSG_IS_DELETED(msginfo->flags))
1492 numlist = g_slist_prepend(numlist, GINT_TO_POINTER(msginfo->msgnum));
1494 numlist = g_slist_reverse(numlist);
1496 uid_mapping = g_relation_new(2);
1497 g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
1499 ok = imap_set_message_flags
1500 (session, numlist, IMAP_FLAG_DELETED, TRUE);
1501 if (ok != IMAP_SUCCESS) {
1502 log_warning(LOG_PROTOCOL, _("can't set deleted flags\n"));
1503 unlock_session(session);
1506 ok = imap_cmd_expunge(session);
1507 if (ok != IMAP_SUCCESS) {
1508 log_warning(LOG_PROTOCOL, _("can't expunge\n"));
1509 unlock_session(session);
1513 dir = folder_item_get_path(msginfo->folder);
1514 if (is_dir_exist(dir)) {
1515 for (cur = msglist; cur; cur = cur->next) {
1516 msginfo = (MsgInfo *)cur->data;
1517 remove_numbered_files(dir, msginfo->msgnum, msginfo->msgnum);
1522 g_relation_destroy(uid_mapping);
1523 g_slist_free(numlist);
1526 unlock_session(session);
1527 if (ok == IMAP_SUCCESS)
1533 static gint imap_remove_msgs(Folder *folder, FolderItem *dest,
1534 MsgInfoList *msglist, GRelation *relation)
1538 g_return_val_if_fail(folder != NULL, -1);
1539 g_return_val_if_fail(dest != NULL, -1);
1540 if (msglist == NULL)
1543 msginfo = (MsgInfo *)msglist->data;
1544 g_return_val_if_fail(msginfo->folder != NULL, -1);
1546 return imap_do_remove_msgs(folder, dest, msglist, relation);
1549 static gint imap_remove_all_msg(Folder *folder, FolderItem *item)
1551 GSList *list = folder_item_get_msg_list(item);
1552 gint res = imap_remove_msgs(folder, item, list, NULL);
1553 procmsg_msg_list_free(list);
1557 static gboolean imap_is_msg_changed(Folder *folder, FolderItem *item,
1560 /* TODO: properly implement this method */
1564 static gint imap_close(Folder *folder, FolderItem *item)
1569 gint imap_scan_tree_real(Folder *folder, gboolean subs_only)
1571 FolderItem *item = NULL;
1572 IMAPSession *session;
1573 gchar *root_folder = NULL;
1575 g_return_val_if_fail(folder != NULL, -1);
1576 g_return_val_if_fail(folder->account != NULL, -1);
1578 debug_print("getting session...\n");
1579 session = imap_session_get(folder);
1581 if (!folder->node) {
1582 folder_tree_destroy(folder);
1583 item = folder_item_new(folder, folder->name, NULL);
1584 item->folder = folder;
1585 folder->node = item->node = g_node_new(item);
1590 if (folder->account->imap_dir && *folder->account->imap_dir) {
1595 Xstrdup_a(root_folder, folder->account->imap_dir, {unlock_session(session);return -1;});
1596 extract_quote(root_folder, '"');
1597 subst_char(root_folder,
1598 imap_get_path_separator(session, IMAP_FOLDER(folder),
1601 strtailchomp(root_folder, '/');
1602 real_path = imap_get_real_path
1603 (session, IMAP_FOLDER(folder), root_folder);
1604 debug_print("IMAP root directory: %s\n", real_path);
1606 /* check if root directory exist */
1608 r = imap_threaded_list(session->folder, "", real_path,
1610 if ((r != MAILIMAP_NO_ERROR) || (clist_count(lep_list) == 0)) {
1611 if (!folder->node) {
1612 item = folder_item_new(folder, folder->name, NULL);
1613 item->folder = folder;
1614 folder->node = item->node = g_node_new(item);
1616 unlock_session(session);
1619 mailimap_list_result_free(lep_list);
1625 item = FOLDER_ITEM(folder->node->data);
1627 if (item && !item->path && root_folder) {
1628 item->path = g_strdup(root_folder);
1631 if (!item || ((item->path || root_folder) &&
1632 strcmp2(item->path, root_folder) != 0)) {
1633 folder_tree_destroy(folder);
1634 item = folder_item_new(folder, folder->name, root_folder);
1635 item->folder = folder;
1636 folder->node = item->node = g_node_new(item);
1639 imap_scan_tree_recursive(session, FOLDER_ITEM(folder->node->data), subs_only);
1640 imap_create_missing_folders(folder);
1641 unlock_session(session);
1646 static gint imap_scan_tree(Folder *folder)
1648 gboolean subs_only = FALSE;
1649 if (folder->account) {
1650 debug_print(" scanning only subs %d\n", folder->account->imap_subsonly);
1651 subs_only = folder->account->imap_subsonly;
1653 return imap_scan_tree_real(folder, subs_only);
1656 static gint imap_scan_tree_recursive(IMAPSession *session, FolderItem *item, gboolean subs_only)
1659 IMAPFolder *imapfolder;
1660 FolderItem *new_item;
1661 GSList *item_list, *cur;
1664 gchar *wildcard_path;
1670 g_return_val_if_fail(item != NULL, -1);
1671 g_return_val_if_fail(item->folder != NULL, -1);
1672 g_return_val_if_fail(item->no_sub == FALSE, -1);
1674 folder = item->folder;
1675 imapfolder = IMAP_FOLDER(folder);
1677 separator = imap_get_path_separator(session, imapfolder, item->path);
1679 if (folder->ui_func)
1680 folder->ui_func(folder, item, folder->ui_func_data);
1683 wildcard[0] = separator;
1686 real_path = imap_get_real_path(session, imapfolder, item->path);
1690 real_path = g_strdup("");
1693 Xstrcat_a(wildcard_path, real_path, wildcard,
1694 {g_free(real_path); return IMAP_ERROR;});
1698 r = imap_threaded_lsub(folder, "", wildcard_path, &lep_list);
1700 r = imap_threaded_list(folder, "", wildcard_path, &lep_list);
1702 if (r != MAILIMAP_NO_ERROR) {
1706 item_list = imap_list_from_lep(imapfolder,
1707 lep_list, real_path, FALSE);
1708 mailimap_list_result_free(lep_list);
1713 node = item->node->children;
1714 while (node != NULL) {
1715 FolderItem *old_item = FOLDER_ITEM(node->data);
1716 GNode *next = node->next;
1719 for (cur = item_list; cur != NULL; cur = cur->next) {
1720 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1721 if (!strcmp2(old_item->path, cur_item->path)) {
1722 new_item = cur_item;
1727 if (old_item && old_item->path && !strcmp(old_item->path, "INBOX")) {
1728 debug_print("not removing INBOX\n");
1730 debug_print("folder '%s' not found. removing...\n",
1732 folder_item_remove(old_item);
1735 old_item->no_sub = new_item->no_sub;
1736 old_item->no_select = new_item->no_select;
1737 if (old_item->no_sub == TRUE && node->children) {
1738 debug_print("folder '%s' doesn't have "
1739 "subfolders. removing...\n",
1741 folder_item_remove_children(old_item);
1748 for (cur = item_list; cur != NULL; cur = cur->next) {
1749 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1752 for (node = item->node->children; node != NULL;
1753 node = node->next) {
1754 if (!strcmp2(FOLDER_ITEM(node->data)->path,
1756 new_item = FOLDER_ITEM(node->data);
1757 folder_item_destroy(cur_item);
1763 new_item = cur_item;
1764 debug_print("new folder '%s' found.\n", new_item->path);
1765 folder_item_append(item, new_item);
1768 if (!strcmp(new_item->path, "INBOX")) {
1769 new_item->stype = F_INBOX;
1770 folder->inbox = new_item;
1771 } else if (!folder_item_parent(item) || item->stype == F_INBOX) {
1774 base = g_path_get_basename(new_item->path);
1776 if (!folder->outbox && !g_ascii_strcasecmp(base, "Sent")) {
1777 new_item->stype = F_OUTBOX;
1778 folder->outbox = new_item;
1779 } else if (!folder->draft && !g_ascii_strcasecmp(base, "Drafts")) {
1780 new_item->stype = F_DRAFT;
1781 folder->draft = new_item;
1782 } else if (!folder->queue && !g_ascii_strcasecmp(base, "Queue")) {
1783 new_item->stype = F_QUEUE;
1784 folder->queue = new_item;
1785 } else if (!folder->trash && !g_ascii_strcasecmp(base, "Trash")) {
1786 new_item->stype = F_TRASH;
1787 folder->trash = new_item;
1792 if (new_item->no_sub == FALSE)
1793 imap_scan_tree_recursive(session, new_item, subs_only);
1796 g_slist_free(item_list);
1798 return IMAP_SUCCESS;
1801 GList *imap_scan_subtree(Folder *folder, FolderItem *item, gboolean unsubs_only, gboolean recursive)
1803 IMAPSession *session = imap_session_get(folder);
1805 gchar *wildcard_path;
1809 GSList *item_list = NULL, *cur;
1810 GList *child_list = NULL, *tmplist = NULL;
1811 GSList *sub_list = NULL;
1817 separator = imap_get_path_separator(session, IMAP_FOLDER(folder), item->path);
1820 wildcard[0] = separator;
1823 real_path = imap_get_real_path(session, IMAP_FOLDER(folder), item->path);
1827 real_path = g_strdup("");
1830 Xstrcat_a(wildcard_path, real_path, wildcard,
1831 {g_free(real_path); return NULL;});
1835 statusbar_print_all(_("Looking for unsubscribed folders in %s..."),
1836 item->path?item->path:item->name);
1838 statusbar_print_all(_("Looking for subfolders of %s..."),
1839 item->path?item->path:item->name);
1841 r = imap_threaded_list(folder, "", wildcard_path, &lep_list);
1843 statusbar_pop_all();
1846 item_list = imap_list_from_lep(IMAP_FOLDER(folder),
1847 lep_list, real_path, FALSE);
1848 mailimap_list_result_free(lep_list);
1850 for (cur = item_list; cur != NULL; cur = cur->next) {
1851 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1853 tmplist = imap_scan_subtree(folder, cur_item,
1854 unsubs_only, recursive);
1856 child_list = g_list_concat(child_list, tmplist);
1858 child_list = g_list_prepend(child_list,
1859 imap_get_real_path(session,
1860 IMAP_FOLDER(folder), cur_item->path));
1862 folder_item_destroy(cur_item);
1864 child_list = g_list_reverse(child_list);
1865 g_slist_free(item_list);
1868 r = imap_threaded_lsub(folder, "", wildcard_path, &lep_list);
1870 statusbar_pop_all();
1873 sub_list = imap_list_from_lep(IMAP_FOLDER(folder),
1874 lep_list, real_path, FALSE);
1875 mailimap_list_result_free(lep_list);
1877 for (cur = sub_list; cur != NULL; cur = cur->next) {
1878 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1879 GList *oldlitem = NULL;
1880 gchar *tmp = imap_get_real_path(session,
1881 IMAP_FOLDER(folder), cur_item->path);
1882 folder_item_destroy(cur_item);
1883 oldlitem = g_list_find_custom(
1884 child_list, tmp, (GCompareFunc)strcmp2);
1886 child_list = g_list_remove_link(child_list, oldlitem);
1887 g_free(oldlitem->data);
1888 g_list_free(oldlitem);
1894 statusbar_pop_all();
1899 static gint imap_create_tree(Folder *folder)
1901 g_return_val_if_fail(folder != NULL, -1);
1902 g_return_val_if_fail(folder->node != NULL, -1);
1903 g_return_val_if_fail(folder->node->data != NULL, -1);
1904 g_return_val_if_fail(folder->account != NULL, -1);
1906 imap_scan_tree(folder);
1907 imap_create_missing_folders(folder);
1912 static void imap_create_missing_folders(Folder *folder)
1914 g_return_if_fail(folder != NULL);
1917 folder->inbox = imap_create_special_folder
1918 (folder, F_INBOX, "INBOX");
1920 folder->trash = imap_create_special_folder
1921 (folder, F_TRASH, "Trash");
1923 folder->queue = imap_create_special_folder
1924 (folder, F_QUEUE, "Queue");
1925 if (!folder->outbox)
1926 folder->outbox = imap_create_special_folder
1927 (folder, F_OUTBOX, "Sent");
1929 folder->draft = imap_create_special_folder
1930 (folder, F_DRAFT, "Drafts");
1933 static FolderItem *imap_create_special_folder(Folder *folder,
1934 SpecialFolderItemType stype,
1938 FolderItem *new_item;
1940 g_return_val_if_fail(folder != NULL, NULL);
1941 g_return_val_if_fail(folder->node != NULL, NULL);
1942 g_return_val_if_fail(folder->node->data != NULL, NULL);
1943 g_return_val_if_fail(folder->account != NULL, NULL);
1944 g_return_val_if_fail(name != NULL, NULL);
1946 item = FOLDER_ITEM(folder->node->data);
1947 new_item = imap_create_folder(folder, item, name);
1950 g_warning("Can't create '%s'\n", name);
1951 if (!folder->inbox) return NULL;
1953 new_item = imap_create_folder(folder, folder->inbox, name);
1955 g_warning("Can't create '%s' under INBOX\n", name);
1957 new_item->stype = stype;
1959 new_item->stype = stype;
1964 static gchar *imap_folder_get_path(Folder *folder)
1968 g_return_val_if_fail(folder != NULL, NULL);
1969 g_return_val_if_fail(folder->account != NULL, NULL);
1971 folder_path = g_strconcat(get_imap_cache_dir(),
1973 folder->account->recv_server,
1975 folder->account->userid,
1981 static gchar *imap_item_get_path(Folder *folder, FolderItem *item)
1983 gchar *folder_path, *path;
1985 g_return_val_if_fail(folder != NULL, NULL);
1986 g_return_val_if_fail(item != NULL, NULL);
1987 folder_path = imap_folder_get_path(folder);
1989 g_return_val_if_fail(folder_path != NULL, NULL);
1990 if (folder_path[0] == G_DIR_SEPARATOR) {
1992 path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
1995 path = g_strdup(folder_path);
1998 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1999 folder_path, G_DIR_SEPARATOR_S,
2002 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
2005 g_free(folder_path);
2010 static FolderItem *imap_create_folder(Folder *folder, FolderItem *parent,
2013 gchar *dirpath, *imap_path;
2014 IMAPSession *session;
2015 FolderItem *new_item;
2020 gboolean no_select = FALSE, no_sub = FALSE;
2021 gboolean exist = FALSE;
2023 g_return_val_if_fail(folder != NULL, NULL);
2024 g_return_val_if_fail(folder->account != NULL, NULL);
2025 g_return_val_if_fail(parent != NULL, NULL);
2026 g_return_val_if_fail(name != NULL, NULL);
2028 debug_print("getting session...\n");
2029 session = imap_session_get(folder);
2034 if (!folder_item_parent(parent) && strcmp(name, "INBOX") == 0) {
2035 dirpath = g_strdup(name);
2036 }else if (parent->path)
2037 dirpath = g_strconcat(parent->path, "/", name, NULL);
2038 else if ((p = strchr(name, '/')) != NULL && *(p + 1) != '\0')
2039 dirpath = g_strdup(name);
2040 else if (folder->account->imap_dir && *folder->account->imap_dir) {
2043 Xstrdup_a(imap_dir, folder->account->imap_dir, {unlock_session(session);return NULL;});
2044 strtailchomp(imap_dir, '/');
2045 dirpath = g_strconcat(imap_dir, "/", name, NULL);
2047 dirpath = g_strdup(name);
2051 /* keep trailing directory separator to create a folder that contains
2053 imap_path = imap_utf8_to_modified_utf7(dirpath);
2055 strtailchomp(dirpath, '/');
2056 Xstrdup_a(new_name, name, {
2058 unlock_session(session);
2061 separator = imap_get_path_separator(session, IMAP_FOLDER(folder), imap_path);
2062 imap_path_separator_subst(imap_path, separator);
2063 /* remove trailing / for display */
2064 strtailchomp(new_name, '/');
2066 if (strcmp(dirpath, "INBOX") != 0) {
2071 argbuf = g_ptr_array_new();
2072 r = imap_threaded_list(folder, "", imap_path, &lep_list);
2073 if (r != MAILIMAP_NO_ERROR) {
2074 log_warning(LOG_PROTOCOL, _("can't create mailbox: LIST failed\n"));
2077 ptr_array_free_strings(argbuf);
2078 g_ptr_array_free(argbuf, TRUE);
2079 unlock_session(session);
2083 if (clist_count(lep_list) > 0)
2085 mailimap_list_result_free(lep_list);
2088 ok = imap_cmd_create(session, imap_path);
2089 if (ok != IMAP_SUCCESS) {
2090 log_warning(LOG_PROTOCOL, _("can't create mailbox\n"));
2093 unlock_session(session);
2096 r = imap_threaded_list(folder, "", imap_path, &lep_list);
2097 if (r == MAILIMAP_NO_ERROR) {
2098 GSList *item_list = imap_list_from_lep(IMAP_FOLDER(folder),
2099 lep_list, dirpath, TRUE);
2101 FolderItem *cur_item = FOLDER_ITEM(item_list->data);
2102 no_select = cur_item->no_select;
2103 no_sub = cur_item->no_sub;
2104 g_slist_free(item_list);
2106 mailimap_list_result_free(lep_list);
2109 imap_threaded_subscribe(folder, imap_path, TRUE);
2113 /* just get flags */
2114 r = imap_threaded_list(folder, "", "INBOX", &lep_list);
2115 if (r == MAILIMAP_NO_ERROR) {
2116 GSList *item_list = imap_list_from_lep(IMAP_FOLDER(folder),
2117 lep_list, dirpath, TRUE);
2119 FolderItem *cur_item = FOLDER_ITEM(item_list->data);
2120 no_select = cur_item->no_select;
2121 no_sub = cur_item->no_sub;
2122 g_slist_free(item_list);
2124 mailimap_list_result_free(lep_list);
2128 new_item = folder_item_new(folder, new_name, dirpath);
2129 new_item->no_select = no_select;
2130 new_item->no_sub = no_sub;
2131 folder_item_append(parent, new_item);
2135 dirpath = folder_item_get_path(new_item);
2136 if (!is_dir_exist(dirpath))
2137 make_dir_hier(dirpath);
2139 unlock_session(session);
2142 /* folder existed, scan it */
2143 folder_item_scan_full(new_item, FALSE);
2149 static gint imap_rename_folder(Folder *folder, FolderItem *item,
2154 gchar *real_oldpath;
2155 gchar *real_newpath;
2157 gchar *old_cache_dir;
2158 gchar *new_cache_dir;
2159 IMAPSession *session;
2162 gint exists, recent, unseen;
2163 guint32 uid_validity;
2165 g_return_val_if_fail(folder != NULL, -1);
2166 g_return_val_if_fail(item != NULL, -1);
2167 g_return_val_if_fail(item->path != NULL, -1);
2168 g_return_val_if_fail(name != NULL, -1);
2170 debug_print("getting session...\n");
2171 session = imap_session_get(folder);
2176 if (strchr(name, imap_get_path_separator(session, IMAP_FOLDER(folder), item->path)) != NULL) {
2177 g_warning(_("New folder name must not contain the namespace "
2179 unlock_session(session);
2183 real_oldpath = imap_get_real_path(session, IMAP_FOLDER(folder), item->path);
2185 g_free(session->mbox);
2186 session->mbox = NULL;
2187 ok = imap_cmd_examine(session, "INBOX",
2188 &exists, &recent, &unseen, &uid_validity, FALSE);
2189 if (ok != IMAP_SUCCESS) {
2190 g_free(real_oldpath);
2191 unlock_session(session);
2195 separator = imap_get_path_separator(session, IMAP_FOLDER(folder), item->path);
2196 if (strchr(item->path, G_DIR_SEPARATOR)) {
2197 dirpath = g_path_get_dirname(item->path);
2198 newpath = g_strconcat(dirpath, G_DIR_SEPARATOR_S, name, NULL);
2201 newpath = g_strdup(name);
2203 real_newpath = imap_utf8_to_modified_utf7(newpath);
2204 imap_path_separator_subst(real_newpath, separator);
2206 ok = imap_cmd_rename(session, real_oldpath, real_newpath);
2207 if (ok != IMAP_SUCCESS) {
2208 log_warning(LOG_PROTOCOL, _("can't rename mailbox: %s to %s\n"),
2209 real_oldpath, real_newpath);
2210 g_free(real_oldpath);
2212 g_free(real_newpath);
2213 unlock_session(session);
2217 item->name = g_strdup(name);
2219 old_cache_dir = folder_item_get_path(item);
2221 paths[0] = g_strdup(item->path);
2223 g_node_traverse(item->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
2224 imap_rename_folder_func, paths);
2226 if (is_dir_exist(old_cache_dir)) {
2227 new_cache_dir = folder_item_get_path(item);
2228 if (rename(old_cache_dir, new_cache_dir) < 0) {
2229 FILE_OP_ERROR(old_cache_dir, "rename");
2231 g_free(new_cache_dir);
2234 g_free(old_cache_dir);
2237 g_free(real_oldpath);
2238 g_free(real_newpath);
2239 unlock_session(session);
2243 gint imap_subscribe(Folder *folder, FolderItem *item, gchar *rpath, gboolean sub)
2247 IMAPSession *session;
2248 debug_print("getting session...\n");
2250 session = imap_session_get(folder);
2254 if (item && item->path) {
2255 path = imap_get_real_path(session, IMAP_FOLDER(folder), item->path);
2258 if (!strcmp(path, "INBOX") && sub == FALSE)
2260 debug_print("%ssubscribing %s\n", sub?"":"un", path);
2261 r = imap_threaded_subscribe(folder, path, sub);
2264 r = imap_threaded_subscribe(folder, rpath, sub);
2270 static gint imap_remove_folder_real(Folder *folder, FolderItem *item)
2273 IMAPSession *session;
2277 g_return_val_if_fail(folder != NULL, -1);
2278 g_return_val_if_fail(item != NULL, -1);
2279 g_return_val_if_fail(item->path != NULL, -1);
2281 debug_print("getting session...\n");
2282 session = imap_session_get(folder);
2286 path = imap_get_real_path(session, IMAP_FOLDER(folder), item->path);
2288 imap_threaded_subscribe(folder, path, FALSE);
2289 ok = imap_cmd_delete(session, path);
2290 if (ok != IMAP_SUCCESS) {
2291 gchar *tmp = g_strdup_printf("%s%c", path,
2292 imap_get_path_separator(session, IMAP_FOLDER(folder), path));
2295 ok = imap_cmd_delete(session, path);
2298 if (ok != IMAP_SUCCESS) {
2299 log_warning(LOG_PROTOCOL, _("can't delete mailbox\n"));
2301 unlock_session(session);
2306 cache_dir = folder_item_get_path(item);
2307 if (is_dir_exist(cache_dir) && remove_dir_recursive(cache_dir) < 0)
2308 g_warning("can't remove directory '%s'\n", cache_dir);
2310 folder_item_remove(item);
2311 unlock_session(session);
2315 static gint imap_remove_folder(Folder *folder, FolderItem *item)
2319 g_return_val_if_fail(item != NULL, -1);
2320 g_return_val_if_fail(item->folder != NULL, -1);
2321 g_return_val_if_fail(item->node != NULL, -1);
2323 node = item->node->children;
2324 while (node != NULL) {
2326 if (imap_remove_folder(folder, FOLDER_ITEM(node->data)) < 0)
2330 debug_print("IMAP removing %s\n", item->path);
2332 if (imap_remove_all_msg(folder, item) < 0)
2334 return imap_remove_folder_real(folder, item);
2337 typedef struct _uncached_data {
2338 IMAPSession *session;
2340 MsgNumberList *numlist;
2346 static void *imap_get_uncached_messages_thread(void *data)
2348 uncached_data *stuff = (uncached_data *)data;
2349 IMAPSession *session = stuff->session;
2350 FolderItem *item = stuff->item;
2351 MsgNumberList *numlist = stuff->numlist;
2353 GSList *newlist = NULL;
2354 GSList *llast = NULL;
2355 GSList *seq_list, *cur;
2357 debug_print("uncached_messages\n");
2359 if (session == NULL || item == NULL || item->folder == NULL
2360 || FOLDER_CLASS(item->folder) != &imap_class) {
2365 seq_list = imap_get_lep_set_from_numlist(numlist);
2366 debug_print("get msgs info\n");
2367 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
2368 struct mailimap_set * imapset;
2374 if (session->cancelled)
2377 imapset = cur->data;
2379 r = imap_threaded_fetch_env(session->folder,
2380 imapset, &env_list);
2381 if (r != MAILIMAP_NO_ERROR)
2384 session_set_access_time(SESSION(session));
2387 for(i = 0 ; i < carray_count(env_list) ; i ++) {
2388 struct imap_fetch_env_info * info;
2391 info = carray_get(env_list, i);
2392 msginfo = imap_envelope_from_lep(info, item);
2393 if (msginfo == NULL)
2395 msginfo->folder = item;
2397 llast = newlist = g_slist_append(newlist, msginfo);
2399 llast = g_slist_append(llast, msginfo);
2400 llast = llast->next;
2405 imap_fetch_env_free(env_list);
2408 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
2409 struct mailimap_set * imapset;
2411 imapset = cur->data;
2412 mailimap_set_free(imapset);
2415 session_set_access_time(SESSION(session));
2420 #define MAX_MSG_NUM 50
2422 static GSList *imap_get_uncached_messages(IMAPSession *session,
2424 MsgNumberList *numlist)
2426 GSList *result = NULL;
2428 uncached_data *data = g_new0(uncached_data, 1);
2433 data->total = g_slist_length(numlist);
2434 debug_print("messages list : %i\n", data->total);
2436 while (cur != NULL) {
2437 GSList * partial_result;
2445 while (count < MAX_MSG_NUM) {
2450 if (newlist == NULL)
2451 llast = newlist = g_slist_append(newlist, p);
2453 llast = g_slist_append(llast, p);
2454 llast = llast->next;
2464 data->session = session;
2466 data->numlist = newlist;
2469 if (prefs_common.work_offline &&
2470 !inc_offline_should_override(FALSE,
2471 _("Claws Mail needs network access in order "
2472 "to access the IMAP server."))) {
2478 (GSList *)imap_get_uncached_messages_thread(data);
2480 statusbar_progress_all(data->cur,data->total, 1);
2482 g_slist_free(newlist);
2484 result = g_slist_concat(result, partial_result);
2488 statusbar_progress_all(0,0,0);
2489 statusbar_pop_all();
2494 static void imap_delete_all_cached_messages(FolderItem *item)
2498 g_return_if_fail(item != NULL);
2499 g_return_if_fail(item->folder != NULL);
2500 g_return_if_fail(FOLDER_CLASS(item->folder) == &imap_class);
2502 debug_print("Deleting all cached messages...\n");
2504 dir = folder_item_get_path(item);
2505 if (is_dir_exist(dir))
2506 remove_all_numbered_files(dir);
2509 debug_print("done.\n");
2512 gchar imap_get_path_separator_for_item(FolderItem *item)
2514 Folder *folder = NULL;
2515 IMAPFolder *imap_folder = NULL;
2516 IMAPSession *session = NULL;
2521 folder = item->folder;
2526 imap_folder = IMAP_FOLDER(folder);
2531 debug_print("getting session...");
2532 session = imap_session_get(FOLDER(folder));
2533 result = imap_get_path_separator(session, imap_folder, item->path);
2534 unlock_session(session);
2538 static gchar imap_refresh_path_separator(IMAPSession *session, IMAPFolder *folder, const gchar *subfolder)
2542 gchar separator = '\0';
2544 g_return_val_if_fail(session != NULL, '/');
2545 r = imap_threaded_list((Folder *)folder, "", subfolder, &lep_list);
2547 if (r != MAILIMAP_NO_ERROR) {
2548 log_warning(LOG_PROTOCOL, _("LIST failed\n"));
2552 if (clist_count(lep_list) > 0) {
2553 clistiter * iter = clist_begin(lep_list);
2554 struct mailimap_mailbox_list * mb;
2555 mb = clist_content(iter);
2557 separator = mb->mb_delimiter;
2558 debug_print("got separator: %c\n", folder->last_seen_separator);
2560 mailimap_list_result_free(lep_list);
2564 static gchar imap_get_path_separator(IMAPSession *session, IMAPFolder *folder, const gchar *path)
2566 gchar separator = '/';
2568 if (folder->last_seen_separator == 0) {
2569 folder->last_seen_separator = imap_refresh_path_separator(session, folder, "");
2572 if (folder->last_seen_separator == 0) {
2573 folder->last_seen_separator = imap_refresh_path_separator(session, folder, "INBOX");
2576 if (folder->last_seen_separator != 0) {
2577 debug_print("using separator: %c\n", folder->last_seen_separator);
2578 return folder->last_seen_separator;
2584 static gchar *imap_get_real_path(IMAPSession *session, IMAPFolder *folder, const gchar *path)
2589 g_return_val_if_fail(folder != NULL, NULL);
2590 g_return_val_if_fail(path != NULL, NULL);
2592 real_path = imap_utf8_to_modified_utf7(path);
2593 separator = imap_get_path_separator(session, folder, path);
2594 imap_path_separator_subst(real_path, separator);
2599 static gint imap_set_message_flags(IMAPSession *session,
2600 MsgNumberList *numlist,
2608 seq_list = imap_get_lep_set_from_numlist(numlist);
2610 for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) {
2611 struct mailimap_set * imapset;
2613 imapset = cur->data;
2615 ok = imap_cmd_store(session, imapset,
2619 imap_lep_set_free(seq_list);
2621 return IMAP_SUCCESS;
2624 typedef struct _select_data {
2625 IMAPSession *session;
2630 guint32 *uid_validity;
2634 static gint imap_select(IMAPSession *session, IMAPFolder *folder,
2636 gint *exists, gint *recent, gint *unseen,
2637 guint32 *uid_validity, gboolean block)
2641 gint exists_, recent_, unseen_;
2642 guint32 uid_validity_;
2644 if (!exists && !recent && !unseen && !uid_validity) {
2645 if (session->mbox && strcmp(session->mbox, path) == 0)
2646 return IMAP_SUCCESS;
2655 uid_validity = &uid_validity_;
2657 g_free(session->mbox);
2658 session->mbox = NULL;
2660 real_path = imap_get_real_path(session, folder, path);
2662 ok = imap_cmd_select(session, real_path,
2663 exists, recent, unseen, uid_validity, block);
2664 if (ok != IMAP_SUCCESS)
2665 log_warning(LOG_PROTOCOL, _("can't select folder: %s\n"), real_path);
2667 session->mbox = g_strdup(path);
2668 session->folder_content_changed = FALSE;
2675 static gint imap_status(IMAPSession *session, IMAPFolder *folder,
2676 const gchar *path, IMAPFolderItem *item,
2678 guint32 *uid_next, guint32 *uid_validity,
2679 gint *unseen, gboolean block)
2683 struct mailimap_mailbox_data_status * data_status;
2688 real_path = imap_get_real_path(session, folder, path);
2706 r = imap_threaded_status(FOLDER(folder), real_path,
2707 &data_status, mask);
2710 if (r != MAILIMAP_NO_ERROR) {
2711 debug_print("status err %d\n", r);
2715 if (data_status->st_info_list == NULL) {
2716 mailimap_mailbox_data_status_free(data_status);
2717 debug_print("status->st_info_list == NULL\n");
2722 for(iter = clist_begin(data_status->st_info_list) ; iter != NULL ;
2723 iter = clist_next(iter)) {
2724 struct mailimap_status_info * info;
2726 info = clist_content(iter);
2727 switch (info->st_att) {
2728 case MAILIMAP_STATUS_ATT_MESSAGES:
2730 * messages = info->st_value;
2731 got_values |= 1 << 0;
2735 case MAILIMAP_STATUS_ATT_UIDNEXT:
2737 * uid_next = info->st_value;
2738 got_values |= 1 << 2;
2742 case MAILIMAP_STATUS_ATT_UIDVALIDITY:
2744 * uid_validity = info->st_value;
2745 got_values |= 1 << 3;
2749 case MAILIMAP_STATUS_ATT_UNSEEN:
2751 * unseen = info->st_value;
2752 got_values |= 1 << 4;
2757 mailimap_mailbox_data_status_free(data_status);
2759 if (got_values != mask) {
2760 g_warning("status: incomplete values received (%d)\n", got_values);
2762 return IMAP_SUCCESS;
2765 static void imap_free_capabilities(IMAPSession *session)
2767 slist_free_strings(session->capability);
2768 g_slist_free(session->capability);
2769 session->capability = NULL;
2772 /* low-level IMAP4rev1 commands */
2774 static gint imap_cmd_login(IMAPSession *session,
2775 const gchar *user, const gchar *pass,
2781 if (!strcmp(type, "LOGIN") && imap_has_capability(session, "LOGINDISABLED")) {
2782 gint ok = IMAP_ERROR;
2783 if (imap_has_capability(session, "STARTTLS")) {
2785 log_warning(LOG_PROTOCOL, _("Server requires TLS to log in.\n"));
2786 ok = imap_cmd_starttls(session);
2787 if (ok != IMAP_SUCCESS) {
2788 log_warning(LOG_PROTOCOL, _("Can't start TLS session.\n"));
2792 imap_free_capabilities(session);
2793 if (imap_get_capabilities(session) != MAILIMAP_NO_ERROR) {
2794 log_warning(LOG_PROTOCOL, _("Can't refresh capabilities.\n"));
2799 log_error(LOG_PROTOCOL, _("Connection to %s failed: "
2800 "server requires TLS, but Claws Mail "
2801 "has been compiled without OpenSSL "
2803 SESSION(session)->server);
2807 log_error(LOG_PROTOCOL, _("Server logins are disabled.\n"));
2812 log_print(LOG_PROTOCOL, "IMAP4> Logging %s to %s using %s\n",
2814 SESSION(session)->server,
2816 r = imap_threaded_login(session->folder, user, pass, type);
2817 if (r != MAILIMAP_NO_ERROR) {
2818 log_print(LOG_PROTOCOL, "IMAP4< Error logging in to %s\n",
2819 SESSION(session)->server);
2822 log_print(LOG_PROTOCOL, "IMAP4< Login to %s successful\n",
2823 SESSION(session)->server);
2829 static gint imap_cmd_noop(IMAPSession *session)
2832 unsigned int exists;
2834 r = imap_threaded_noop(session->folder, &exists);
2835 if (r != MAILIMAP_NO_ERROR) {
2836 debug_print("noop err %d\n", r);
2839 session->exists = exists;
2840 session_set_access_time(SESSION(session));
2842 return IMAP_SUCCESS;
2846 static gint imap_cmd_starttls(IMAPSession *session)
2850 r = imap_threaded_starttls(session->folder,
2851 SESSION(session)->server, SESSION(session)->port);
2852 if (r != MAILIMAP_NO_ERROR) {
2853 debug_print("starttls err %d\n", r);
2856 return IMAP_SUCCESS;
2860 static gint imap_cmd_select(IMAPSession *session, const gchar *folder,
2861 gint *exists, gint *recent, gint *unseen,
2862 guint32 *uid_validity, gboolean block)
2866 r = imap_threaded_select(session->folder, folder,
2867 exists, recent, unseen, uid_validity);
2868 if (r != MAILIMAP_NO_ERROR) {
2869 debug_print("select err %d\n", r);
2872 return IMAP_SUCCESS;
2875 static gint imap_cmd_examine(IMAPSession *session, const gchar *folder,
2876 gint *exists, gint *recent, gint *unseen,
2877 guint32 *uid_validity, gboolean block)
2881 r = imap_threaded_examine(session->folder, folder,
2882 exists, recent, unseen, uid_validity);
2883 if (r != MAILIMAP_NO_ERROR) {
2884 debug_print("examine err %d\n", r);
2888 return IMAP_SUCCESS;
2891 static gint imap_cmd_create(IMAPSession *session, const gchar *folder)
2895 r = imap_threaded_create(session->folder, folder);
2896 if (r != MAILIMAP_NO_ERROR) {
2901 return IMAP_SUCCESS;
2904 static gint imap_cmd_rename(IMAPSession *session, const gchar *old_folder,
2905 const gchar *new_folder)
2909 r = imap_threaded_rename(session->folder, old_folder,
2911 if (r != MAILIMAP_NO_ERROR) {
2916 return IMAP_SUCCESS;
2919 static gint imap_cmd_delete(IMAPSession *session, const gchar *folder)
2924 r = imap_threaded_delete(session->folder, folder);
2925 if (r != MAILIMAP_NO_ERROR) {
2930 return IMAP_SUCCESS;
2933 typedef struct _fetch_data {
2934 IMAPSession *session;
2936 const gchar *filename;
2942 static void *imap_cmd_fetch_thread(void *data)
2944 fetch_data *stuff = (fetch_data *)data;
2945 IMAPSession *session = stuff->session;
2946 guint32 uid = stuff->uid;
2947 const gchar *filename = stuff->filename;
2951 r = imap_threaded_fetch_content(session->folder,
2955 r = imap_threaded_fetch_content(session->folder,
2958 if (r != MAILIMAP_NO_ERROR) {
2959 debug_print("fetch err %d\n", r);
2960 return GINT_TO_POINTER(IMAP_ERROR);
2962 return GINT_TO_POINTER(IMAP_SUCCESS);
2965 static gint imap_cmd_fetch(IMAPSession *session, guint32 uid,
2966 const gchar *filename, gboolean headers,
2969 fetch_data *data = g_new0(fetch_data, 1);
2972 data->session = session;
2974 data->filename = filename;
2975 data->headers = headers;
2978 if (prefs_common.work_offline &&
2979 !inc_offline_should_override(FALSE,
2980 _("Claws Mail needs network access in order "
2981 "to access the IMAP server."))) {
2985 statusbar_print_all(_("Fetching message..."));
2986 result = GPOINTER_TO_INT(imap_cmd_fetch_thread(data));
2987 statusbar_pop_all();
2993 static gint imap_cmd_append(IMAPSession *session, const gchar *destfolder,
2994 const gchar *file, IMAPFlags flags,
2997 struct mailimap_flag_list * flag_list;
3000 g_return_val_if_fail(file != NULL, IMAP_ERROR);
3002 flag_list = imap_flag_to_lep(flags);
3003 r = imap_threaded_append(session->folder, destfolder,
3004 file, flag_list, (int *)new_uid);
3005 mailimap_flag_list_free(flag_list);
3007 if (r != MAILIMAP_NO_ERROR) {
3008 debug_print("append err %d\n", r);
3011 return IMAP_SUCCESS;
3014 static gint imap_cmd_copy(IMAPSession *session, struct mailimap_set * set,
3015 const gchar *destfolder, GRelation *uid_mapping,
3016 struct mailimap_set **source, struct mailimap_set **dest)
3020 g_return_val_if_fail(session != NULL, IMAP_ERROR);
3021 g_return_val_if_fail(set != NULL, IMAP_ERROR);
3022 g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
3024 r = imap_threaded_copy(session->folder, set, destfolder, source, dest);
3025 if (r != MAILIMAP_NO_ERROR) {
3030 return IMAP_SUCCESS;
3033 static gint imap_cmd_store(IMAPSession *session, struct mailimap_set * set,
3034 IMAPFlags flags, int do_add)
3037 struct mailimap_flag_list * flag_list;
3038 struct mailimap_store_att_flags * store_att_flags;
3040 flag_list = imap_flag_to_lep(flags);
3044 mailimap_store_att_flags_new_add_flags_silent(flag_list);
3047 mailimap_store_att_flags_new_remove_flags_silent(flag_list);
3049 r = imap_threaded_store(session->folder, set, store_att_flags);
3050 mailimap_store_att_flags_free(store_att_flags);
3051 if (r != MAILIMAP_NO_ERROR) {
3056 return IMAP_SUCCESS;
3059 static gint imap_cmd_expunge(IMAPSession *session)
3063 if (prefs_common.work_offline &&
3064 !inc_offline_should_override(FALSE,
3065 _("Claws Mail needs network access in order "
3066 "to access the IMAP server."))) {
3070 r = imap_threaded_expunge(session->folder);
3071 if (r != MAILIMAP_NO_ERROR) {
3076 return IMAP_SUCCESS;
3079 static void imap_path_separator_subst(gchar *str, gchar separator)
3082 gboolean in_escape = FALSE;
3084 if (!separator || separator == '/') return;
3086 for (p = str; *p != '\0'; p++) {
3087 if (*p == '/' && !in_escape)
3089 else if (*p == '&' && *(p + 1) != '-' && !in_escape)
3091 else if (*p == '-' && in_escape)
3096 static gchar *imap_modified_utf7_to_utf8(const gchar *mutf7_str)
3098 static iconv_t cd = (iconv_t)-1;
3099 static gboolean iconv_ok = TRUE;
3102 size_t norm_utf7_len;
3104 gchar *to_str, *to_p;
3106 gboolean in_escape = FALSE;
3108 if (!iconv_ok) return g_strdup(mutf7_str);
3110 if (cd == (iconv_t)-1) {
3111 cd = iconv_open(CS_INTERNAL, CS_UTF_7);
3112 if (cd == (iconv_t)-1) {
3113 g_warning("iconv cannot convert UTF-7 to %s\n",
3116 return g_strdup(mutf7_str);
3120 /* modified UTF-7 to normal UTF-7 conversion */
3121 norm_utf7 = g_string_new(NULL);
3123 for (p = mutf7_str; *p != '\0'; p++) {
3124 /* replace: '&' -> '+',
3126 escaped ',' -> '/' */
3127 if (!in_escape && *p == '&') {
3128 if (*(p + 1) != '-') {
3129 g_string_append_c(norm_utf7, '+');
3132 g_string_append_c(norm_utf7, '&');
3135 } else if (in_escape && *p == ',') {
3136 g_string_append_c(norm_utf7, '/');
3137 } else if (in_escape && *p == '-') {
3138 g_string_append_c(norm_utf7, '-');
3141 g_string_append_c(norm_utf7, *p);
3145 norm_utf7_p = norm_utf7->str;
3146 norm_utf7_len = norm_utf7->len;
3147 to_len = strlen(mutf7_str) * 5;
3148 to_p = to_str = g_malloc(to_len + 1);
3150 if (iconv(cd, (ICONV_CONST gchar **)&norm_utf7_p, &norm_utf7_len,
3151 &to_p, &to_len) == -1) {
3152 g_warning(_("iconv cannot convert UTF-7 to %s\n"),
3153 conv_get_locale_charset_str());
3154 g_string_free(norm_utf7, TRUE);
3156 return g_strdup(mutf7_str);
3159 /* second iconv() call for flushing */
3160 iconv(cd, NULL, NULL, &to_p, &to_len);
3161 g_string_free(norm_utf7, TRUE);
3167 static gchar *imap_utf8_to_modified_utf7(const gchar *from)
3169 static iconv_t cd = (iconv_t)-1;
3170 static gboolean iconv_ok = TRUE;
3171 gchar *norm_utf7, *norm_utf7_p;
3172 size_t from_len, norm_utf7_len;
3174 gchar *from_tmp, *to, *p;
3175 gboolean in_escape = FALSE;
3177 if (!iconv_ok) return g_strdup(from);
3179 if (cd == (iconv_t)-1) {
3180 cd = iconv_open(CS_UTF_7, CS_INTERNAL);
3181 if (cd == (iconv_t)-1) {
3182 g_warning(_("iconv cannot convert %s to UTF-7\n"),
3185 return g_strdup(from);
3189 /* UTF-8 to normal UTF-7 conversion */
3190 Xstrdup_a(from_tmp, from, return g_strdup(from));
3191 from_len = strlen(from);
3192 norm_utf7_len = from_len * 5;
3193 Xalloca(norm_utf7, norm_utf7_len + 1, return g_strdup(from));
3194 norm_utf7_p = norm_utf7;
3196 #define IS_PRINT(ch) (isprint(ch) && IS_ASCII(ch))
3198 while (from_len > 0) {
3199 if (*from_tmp == '+') {
3200 *norm_utf7_p++ = '+';
3201 *norm_utf7_p++ = '-';
3205 } else if (IS_PRINT(*(guchar *)from_tmp)) {
3206 /* printable ascii char */
3207 *norm_utf7_p = *from_tmp;
3213 size_t conv_len = 0;
3215 /* unprintable char: convert to UTF-7 */
3217 while (!IS_PRINT(*(guchar *)p) && conv_len < from_len) {
3218 conv_len += g_utf8_skip[*(guchar *)p];
3219 p += g_utf8_skip[*(guchar *)p];
3222 from_len -= conv_len;
3223 if (iconv(cd, (ICONV_CONST gchar **)&from_tmp,
3225 &norm_utf7_p, &norm_utf7_len) == -1) {
3226 g_warning(_("iconv cannot convert UTF-8 to UTF-7\n"));
3227 return g_strdup(from);
3230 /* second iconv() call for flushing */
3231 iconv(cd, NULL, NULL, &norm_utf7_p, &norm_utf7_len);
3237 *norm_utf7_p = '\0';
3238 to_str = g_string_new(NULL);
3239 for (p = norm_utf7; p < norm_utf7_p; p++) {
3240 /* replace: '&' -> "&-",
3243 BASE64 '/' -> ',' */
3244 if (!in_escape && *p == '&') {
3245 g_string_append(to_str, "&-");
3246 } else if (!in_escape && *p == '+') {
3247 if (*(p + 1) == '-') {
3248 g_string_append_c(to_str, '+');
3251 g_string_append_c(to_str, '&');
3254 } else if (in_escape && *p == '/') {
3255 g_string_append_c(to_str, ',');
3256 } else if (in_escape && *p == '-') {
3257 g_string_append_c(to_str, '-');
3260 g_string_append_c(to_str, *p);
3266 g_string_append_c(to_str, '-');
3270 g_string_free(to_str, FALSE);
3275 static gboolean imap_rename_folder_func(GNode *node, gpointer data)
3277 FolderItem *item = node->data;
3278 gchar **paths = data;
3279 const gchar *oldpath = paths[0];
3280 const gchar *newpath = paths[1];
3281 gchar *real_oldpath, *real_newpath;
3283 gchar *new_itempath;
3285 IMAPSession *session = imap_session_get(item->folder);
3287 oldpathlen = strlen(oldpath);
3288 if (strncmp(oldpath, item->path, oldpathlen) != 0) {
3289 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
3293 base = item->path + oldpathlen;
3294 while (*base == G_DIR_SEPARATOR) base++;
3296 new_itempath = g_strdup(newpath);
3298 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
3301 real_oldpath = imap_get_real_path(session, IMAP_FOLDER(item->folder), item->path);
3303 item->path = new_itempath;
3305 real_newpath = imap_get_real_path(session, IMAP_FOLDER(item->folder), item->path);
3307 imap_threaded_subscribe(item->folder, real_oldpath, FALSE);
3308 imap_threaded_subscribe(item->folder, real_newpath, TRUE);
3310 g_free(real_oldpath);
3311 g_free(real_newpath);
3315 typedef struct _get_list_uid_data {
3317 IMAPSession *session;
3318 IMAPFolderItem *item;
3319 GSList **msgnum_list;
3321 } get_list_uid_data;
3323 static void *get_list_of_uids_thread(void *data)
3325 get_list_uid_data *stuff = (get_list_uid_data *)data;
3326 Folder *folder = stuff->folder;
3327 IMAPFolderItem *item = stuff->item;
3328 GSList **msgnum_list = stuff->msgnum_list;
3329 gint ok, nummsgs = 0, lastuid_old;
3330 IMAPSession *session;
3331 GSList *uidlist, *elem;
3332 clist * lep_uidlist;
3335 session = stuff->session;
3336 if (session == NULL) {
3338 return GINT_TO_POINTER(-1);
3340 /* no session locking here, it's already locked by caller */
3341 ok = imap_select(session, IMAP_FOLDER(folder), item->item.path,
3342 NULL, NULL, NULL, NULL, TRUE);
3343 if (ok != IMAP_SUCCESS) {
3345 return GINT_TO_POINTER(-1);
3350 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SIMPLE, NULL,
3353 if (r == MAILIMAP_NO_ERROR) {
3354 GSList * fetchuid_list;
3357 imap_uid_list_from_lep(lep_uidlist);
3358 mailimap_search_result_free(lep_uidlist);
3360 uidlist = g_slist_concat(fetchuid_list, uidlist);
3363 GSList * fetchuid_list;
3364 carray * lep_uidtab;
3366 r = imap_threaded_fetch_uid(folder, item->lastuid + 1,
3368 if (r == MAILIMAP_NO_ERROR) {
3370 imap_uid_list_from_lep_tab(lep_uidtab);
3371 imap_fetch_uid_list_free(lep_uidtab);
3372 uidlist = g_slist_concat(fetchuid_list, uidlist);
3376 lastuid_old = item->lastuid;
3377 *msgnum_list = g_slist_copy(item->uid_list);
3378 nummsgs = g_slist_length(*msgnum_list);
3379 debug_print("Got %d uids from cache\n", g_slist_length(item->uid_list));
3381 for (elem = uidlist; elem != NULL; elem = g_slist_next(elem)) {
3384 msgnum = GPOINTER_TO_INT(elem->data);
3385 if (msgnum > lastuid_old) {
3386 *msgnum_list = g_slist_prepend(*msgnum_list, GINT_TO_POINTER(msgnum));
3387 item->uid_list = g_slist_prepend(item->uid_list, GINT_TO_POINTER(msgnum));
3390 if(msgnum > item->lastuid)
3391 item->lastuid = msgnum;
3394 g_slist_free(uidlist);
3396 return GINT_TO_POINTER(nummsgs);
3399 static gint get_list_of_uids(IMAPSession *session, Folder *folder, IMAPFolderItem *item, GSList **msgnum_list)
3402 get_list_uid_data *data = g_new0(get_list_uid_data, 1);
3404 data->folder = folder;
3406 data->msgnum_list = msgnum_list;
3407 data->session = session;
3408 if (prefs_common.work_offline &&
3409 !inc_offline_should_override(FALSE,
3410 _("Claws Mail needs network access in order "
3411 "to access the IMAP server."))) {
3416 result = GPOINTER_TO_INT(get_list_of_uids_thread(data));
3422 gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list, gboolean *old_uids_valid)
3424 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3425 IMAPSession *session;
3426 gint ok, nummsgs = 0, exists;
3427 guint32 uid_next = 0, uid_val = 0;
3428 GSList *uidlist = NULL;
3430 gboolean selected_folder;
3431 debug_print("get_num_list\n");
3433 g_return_val_if_fail(folder != NULL, -1);
3434 g_return_val_if_fail(item != NULL, -1);
3435 g_return_val_if_fail(item->item.path != NULL, -1);
3436 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3437 g_return_val_if_fail(folder->account != NULL, -1);
3439 debug_print("getting session...\n");
3440 session = imap_session_get(folder);
3441 g_return_val_if_fail(session != NULL, -1);
3443 if (FOLDER_ITEM(item)->path)
3444 statusbar_print_all(_("Scanning folder %s%c%s ..."),
3445 FOLDER_ITEM(item)->folder->name,
3447 FOLDER_ITEM(item)->path);
3449 statusbar_print_all(_("Scanning folder %s ..."),
3450 FOLDER_ITEM(item)->folder->name);
3452 selected_folder = (session->mbox != NULL) &&
3453 (!strcmp(session->mbox, item->item.path));
3454 if (selected_folder && time(NULL) - item->use_cache < 2) {
3455 ok = imap_cmd_noop(session);
3456 if (ok != IMAP_SUCCESS) {
3457 debug_print("disconnected!\n");
3458 session = imap_reconnect_if_possible(folder, session);
3459 if (session == NULL) {
3460 statusbar_pop_all();
3461 unlock_session(session);
3465 exists = session->exists;
3467 uid_next = item->c_uid_next;
3468 uid_val = item->c_uid_validity;
3469 *old_uids_valid = TRUE;
3471 if (item->use_cache && time(NULL) - item->use_cache < 2) {
3472 exists = item->c_messages;
3473 uid_next = item->c_uid_next;
3474 uid_val = item->c_uid_validity;
3476 debug_print("using cache %d %d %d\n", exists, uid_next, uid_val);
3478 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path, item,
3479 &exists, &uid_next, &uid_val, NULL, FALSE);
3481 item->item.last_num = uid_next - 1;
3483 item->use_cache = (time_t)0;
3484 if (ok != IMAP_SUCCESS) {
3485 statusbar_pop_all();
3486 unlock_session(session);
3489 if(item->item.mtime == uid_val)
3490 *old_uids_valid = TRUE;
3492 *old_uids_valid = FALSE;
3494 debug_print("Freeing imap uid cache (%d != %d)\n",
3495 (int)item->item.mtime, uid_val);
3497 g_slist_free(item->uid_list);
3498 item->uid_list = NULL;
3500 item->item.mtime = uid_val;
3502 imap_delete_all_cached_messages((FolderItem *)item);
3506 /* If old uid_next matches new uid_next we can be sure no message
3507 was added to the folder */
3508 debug_print("uid_next is %d and item->uid_next %d \n",
3509 uid_next, item->uid_next);
3510 if (uid_next == item->uid_next) {
3511 nummsgs = g_slist_length(item->uid_list);
3513 /* If number of messages is still the same we
3514 know our caches message numbers are still valid,
3515 otherwise if the number of messages has decrease
3516 we discard our cache to start a new scan to find
3517 out which numbers have been removed */
3518 if (exists == nummsgs) {
3519 debug_print("exists == nummsgs\n");
3520 *msgnum_list = g_slist_copy(item->uid_list);
3521 statusbar_pop_all();
3522 unlock_session(session);
3524 } else if (exists < nummsgs) {
3525 debug_print("Freeing imap uid cache");
3527 g_slist_free(item->uid_list);
3528 item->uid_list = NULL;
3533 *msgnum_list = NULL;
3534 statusbar_pop_all();
3535 unlock_session(session);
3539 item->last_change = time(NULL);
3540 nummsgs = get_list_of_uids(session, folder, item, &uidlist);
3543 statusbar_pop_all();
3544 unlock_session(session);
3548 if (nummsgs != exists) {
3549 /* Cache contains more messages then folder, we have cached
3550 an old UID of a message that was removed and new messages
3551 have been added too, otherwise the uid_next check would
3553 debug_print("Freeing imap uid cache");
3555 g_slist_free(item->uid_list);
3556 item->uid_list = NULL;
3558 g_slist_free(*msgnum_list);
3560 nummsgs = get_list_of_uids(session, folder, item, &uidlist);
3563 *msgnum_list = uidlist;
3565 dir = folder_item_get_path((FolderItem *)item);
3566 debug_print("removing old messages from %s\n", dir);
3567 remove_numbered_files_not_in_list(dir, *msgnum_list);
3570 item->uid_next = uid_next;
3572 debug_print("get_num_list - ok - %i\n", nummsgs);
3573 statusbar_pop_all();
3574 unlock_session(session);
3578 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item)
3583 flags.perm_flags = MSG_NEW|MSG_UNREAD;
3584 flags.tmp_flags = 0;
3586 g_return_val_if_fail(item != NULL, NULL);
3587 g_return_val_if_fail(file != NULL, NULL);
3589 if (folder_has_parent_of_type(item, F_QUEUE)) {
3590 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
3591 } else if (folder_has_parent_of_type(item, F_DRAFT)) {
3592 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
3595 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3596 if (!msginfo) return NULL;
3598 msginfo->plaintext_file = g_strdup(file);
3599 msginfo->folder = item;
3604 GSList *imap_get_msginfos(Folder *folder, FolderItem *item,
3605 GSList *msgnum_list)
3607 IMAPSession *session;
3608 MsgInfoList *ret = NULL;
3611 debug_print("get_msginfos\n");
3613 g_return_val_if_fail(folder != NULL, NULL);
3614 g_return_val_if_fail(item != NULL, NULL);
3615 g_return_val_if_fail(msgnum_list != NULL, NULL);
3617 debug_print("getting session...\n");
3618 session = imap_session_get(folder);
3619 g_return_val_if_fail(session != NULL, NULL);
3621 debug_print("IMAP getting msginfos\n");
3622 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3623 NULL, NULL, NULL, NULL, FALSE);
3624 if (ok != IMAP_SUCCESS) {
3625 unlock_session(session);
3628 if (!(folder_has_parent_of_type(item, F_DRAFT) ||
3629 folder_has_parent_of_type(item, F_QUEUE))) {
3630 ret = g_slist_concat(ret,
3631 imap_get_uncached_messages(session, item,
3634 MsgNumberList *sorted_list, *elem, *llast = NULL;
3635 gint startnum, lastnum;
3637 sorted_list = g_slist_sort(g_slist_copy(msgnum_list), g_int_compare);
3639 startnum = lastnum = GPOINTER_TO_INT(sorted_list->data);
3641 llast = g_slist_last(ret);
3642 for (elem = sorted_list;; elem = g_slist_next(elem)) {
3646 num = GPOINTER_TO_INT(elem->data);
3648 if (num > lastnum + 1 || elem == NULL) {
3650 for (i = startnum; i <= lastnum; ++i) {
3653 file = imap_fetch_msg(folder, item, i);
3655 MsgInfo *msginfo = imap_parse_msg(file, item);
3656 if (msginfo != NULL) {
3657 msginfo->msgnum = i;
3659 llast = ret = g_slist_append(ret, msginfo);
3661 llast = g_slist_append(llast, msginfo);
3662 llast = llast->next;
3667 session_set_access_time(SESSION(session));
3678 g_slist_free(sorted_list);
3680 unlock_session(session);
3684 MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid)
3686 MsgInfo *msginfo = NULL;
3687 MsgInfoList *msginfolist;
3688 MsgNumberList numlist;
3690 numlist.next = NULL;
3691 numlist.data = GINT_TO_POINTER(uid);
3693 msginfolist = imap_get_msginfos(folder, item, &numlist);
3694 if (msginfolist != NULL) {
3695 msginfo = msginfolist->data;
3696 g_slist_free(msginfolist);
3702 gboolean imap_scan_required(Folder *folder, FolderItem *_item)
3704 IMAPSession *session;
3705 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3706 gint ok, exists = 0, unseen = 0;
3707 guint32 uid_next = 0, uid_val = 0;
3708 gboolean selected_folder;
3710 g_return_val_if_fail(folder != NULL, FALSE);
3711 g_return_val_if_fail(item != NULL, FALSE);
3712 g_return_val_if_fail(item->item.folder != NULL, FALSE);
3713 g_return_val_if_fail(FOLDER_CLASS(item->item.folder) == &imap_class, FALSE);
3715 if (item->item.path == NULL)
3718 debug_print("getting session...\n");
3719 session = imap_session_get(folder);
3720 g_return_val_if_fail(session != NULL, FALSE);
3722 selected_folder = (session->mbox != NULL) &&
3723 (!strcmp(session->mbox, item->item.path));
3724 if (selected_folder && time(NULL) - item->use_cache < 2) {
3725 ok = imap_cmd_noop(session);
3726 if (ok != IMAP_SUCCESS) {
3727 debug_print("disconnected!\n");
3728 session = imap_reconnect_if_possible(folder, session);
3729 if (session == NULL)
3733 if (session->folder_content_changed
3734 || session->exists != item->item.total_msgs) {
3735 unlock_session(session);
3739 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path, IMAP_FOLDER_ITEM(item),
3740 &exists, &uid_next, &uid_val, &unseen, FALSE);
3741 if (ok != IMAP_SUCCESS) {
3742 unlock_session(session);
3746 item->use_cache = time(NULL);
3747 item->c_messages = exists;
3748 item->c_uid_next = uid_next;
3749 item->c_uid_validity = uid_val;
3750 item->c_unseen = unseen;
3751 item->item.last_num = uid_next - 1;
3752 debug_print("uidnext %d, item->uid_next %d, exists %d, item->item.total_msgs %d\n",
3753 uid_next, item->uid_next, exists, item->item.total_msgs);
3754 if ((uid_next != item->uid_next) || (exists != item->item.total_msgs)
3755 || unseen != item->item.unread_msgs || uid_val != item->item.mtime) {
3756 unlock_session(session);
3757 item->last_change = time(NULL);
3761 unlock_session(session);
3765 void imap_change_flags(Folder *folder, FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
3767 IMAPSession *session;
3768 IMAPFlags flags_set = 0, flags_unset = 0;
3769 gint ok = IMAP_SUCCESS;
3770 MsgNumberList numlist;
3771 hashtable_data *ht_data = NULL;
3773 g_return_if_fail(folder != NULL);
3774 g_return_if_fail(folder->klass == &imap_class);
3775 g_return_if_fail(item != NULL);
3776 g_return_if_fail(item->folder == folder);
3777 g_return_if_fail(msginfo != NULL);
3778 g_return_if_fail(msginfo->folder == item);
3780 if (!MSG_IS_MARKED(msginfo->flags) && (newflags & MSG_MARKED))
3781 flags_set |= IMAP_FLAG_FLAGGED;
3782 if ( MSG_IS_MARKED(msginfo->flags) && !(newflags & MSG_MARKED))
3783 flags_unset |= IMAP_FLAG_FLAGGED;
3785 if (!MSG_IS_UNREAD(msginfo->flags) && (newflags & MSG_UNREAD))
3786 flags_unset |= IMAP_FLAG_SEEN;
3787 if ( MSG_IS_UNREAD(msginfo->flags) && !(newflags & MSG_UNREAD))
3788 flags_set |= IMAP_FLAG_SEEN;
3790 if (!MSG_IS_REPLIED(msginfo->flags) && (newflags & MSG_REPLIED))
3791 flags_set |= IMAP_FLAG_ANSWERED;
3792 if ( MSG_IS_REPLIED(msginfo->flags) && !(newflags & MSG_REPLIED))
3793 flags_unset |= IMAP_FLAG_ANSWERED;
3795 if (!MSG_IS_DELETED(msginfo->flags) && (newflags & MSG_DELETED))
3796 flags_set |= IMAP_FLAG_DELETED;
3797 if ( MSG_IS_DELETED(msginfo->flags) && !(newflags & MSG_DELETED))
3798 flags_unset |= IMAP_FLAG_DELETED;
3800 if (!flags_set && !flags_unset) {
3801 /* the changed flags were not translatable to IMAP-speak.
3802 * like MSG_POSTFILTERED, so just apply. */
3803 msginfo->flags.perm_flags = newflags;
3807 debug_print("getting session...\n");
3808 session = imap_session_get(folder);
3813 if ((ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
3814 NULL, NULL, NULL, NULL, FALSE)) != IMAP_SUCCESS) {
3815 unlock_session(session);
3818 numlist.next = NULL;
3819 numlist.data = GINT_TO_POINTER(msginfo->msgnum);
3821 if (IMAP_FOLDER_ITEM(item)->batching) {
3822 /* instead of performing an UID STORE command for each message change,
3823 * as a lot of them can change "together", we just fill in hashtables
3824 * and defer the treatment so that we're able to send only one
3827 debug_print("IMAP batch mode on, deferring flags change\n");
3829 ht_data = g_hash_table_lookup(IMAP_FOLDER_ITEM(item)->flags_set_table,
3830 GINT_TO_POINTER(flags_set));
3831 if (ht_data == NULL) {
3832 ht_data = g_new0(hashtable_data, 1);
3833 ht_data->session = session;
3834 ht_data->item = IMAP_FOLDER_ITEM(item);
3835 g_hash_table_insert(IMAP_FOLDER_ITEM(item)->flags_set_table,
3836 GINT_TO_POINTER(flags_set), ht_data);
3838 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
3839 ht_data->msglist = g_slist_prepend(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum));
3842 ht_data = g_hash_table_lookup(IMAP_FOLDER_ITEM(item)->flags_unset_table,
3843 GINT_TO_POINTER(flags_unset));
3844 if (ht_data == NULL) {
3845 ht_data = g_new0(hashtable_data, 1);
3846 ht_data->session = session;
3847 ht_data->item = IMAP_FOLDER_ITEM(item);
3848 g_hash_table_insert(IMAP_FOLDER_ITEM(item)->flags_unset_table,
3849 GINT_TO_POINTER(flags_unset), ht_data);
3851 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
3852 ht_data->msglist = g_slist_prepend(ht_data->msglist,
3853 GINT_TO_POINTER(msginfo->msgnum));
3856 debug_print("IMAP changing flags\n");
3858 ok = imap_set_message_flags(session, &numlist, flags_set, TRUE);
3859 if (ok != IMAP_SUCCESS) {
3860 unlock_session(session);
3866 ok = imap_set_message_flags(session, &numlist, flags_unset, FALSE);
3867 if (ok != IMAP_SUCCESS) {
3868 unlock_session(session);
3873 msginfo->flags.perm_flags = newflags;
3874 unlock_session(session);
3878 static gint imap_remove_msg(Folder *folder, FolderItem *item, gint uid)
3881 IMAPSession *session;
3883 MsgNumberList numlist;
3885 g_return_val_if_fail(folder != NULL, -1);
3886 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3887 g_return_val_if_fail(item != NULL, -1);
3889 debug_print("getting session...\n");
3890 session = imap_session_get(folder);
3891 if (!session) return -1;
3893 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3894 NULL, NULL, NULL, NULL, FALSE);
3895 if (ok != IMAP_SUCCESS) {
3896 unlock_session(session);
3899 numlist.next = NULL;
3900 numlist.data = GINT_TO_POINTER(uid);
3902 ok = imap_set_message_flags
3903 (session, &numlist, IMAP_FLAG_DELETED, TRUE);
3904 if (ok != IMAP_SUCCESS) {
3905 log_warning(LOG_PROTOCOL, _("can't set deleted flags: %d\n"), uid);
3906 unlock_session(session);
3910 if (!session->uidplus) {
3911 ok = imap_cmd_expunge(session);
3915 uidstr = g_strdup_printf("%u", uid);
3916 ok = imap_cmd_expunge(session);
3919 if (ok != IMAP_SUCCESS) {
3920 log_warning(LOG_PROTOCOL, _("can't expunge\n"));
3921 unlock_session(session);
3925 IMAP_FOLDER_ITEM(item)->uid_list = g_slist_remove(
3926 IMAP_FOLDER_ITEM(item)->uid_list, numlist.data);
3927 dir = folder_item_get_path(item);
3928 if (is_dir_exist(dir))
3929 remove_numbered_files(dir, uid, uid);
3931 unlock_session(session);
3932 return IMAP_SUCCESS;
3935 static gint compare_msginfo(gconstpointer a, gconstpointer b)
3937 return ((MsgInfo *)a)->msgnum - ((MsgInfo *)b)->msgnum;
3940 static guint gslist_find_next_num(MsgNumberList **list, guint num)
3944 g_return_val_if_fail(list != NULL, -1);
3946 for (elem = *list; elem != NULL; elem = g_slist_next(elem))
3947 if (GPOINTER_TO_INT(elem->data) >= num)
3950 return elem != NULL ? GPOINTER_TO_INT(elem->data) : (gint)-1;
3954 * NEW and DELETED flags are not syncronized
3955 * - The NEW/RECENT flags in IMAP folders can not really be directly
3956 * modified by Sylpheed
3957 * - The DELETE/DELETED flag in IMAP and Sylpheed don't have the same
3958 * meaning, in IMAP it always removes the messages from the FolderItem
3959 * in Sylpheed it can mean to move the message to trash
3962 typedef struct _get_flags_data {
3965 MsgInfoList *msginfo_list;
3966 GRelation *msgflags;
3967 gboolean full_search;
3971 static /*gint*/ void *imap_get_flags_thread(void *data)
3973 get_flags_data *stuff = (get_flags_data *)data;
3974 Folder *folder = stuff->folder;