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 3 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, see <http://www.gnu.org/licenses/>.
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 void imap_remove_cached_msg (Folder *folder,
204 static gint imap_add_msg (Folder *folder,
208 static gint imap_add_msgs (Folder *folder,
211 GRelation *relation);
213 static gint imap_copy_msg (Folder *folder,
216 static gint imap_copy_msgs (Folder *folder,
218 MsgInfoList *msglist,
219 GRelation *relation);
221 static gint imap_remove_msg (Folder *folder,
224 static gint imap_remove_msgs (Folder *folder,
226 MsgInfoList *msglist,
227 GRelation *relation);
228 static gint imap_remove_all_msg (Folder *folder,
231 static gboolean imap_is_msg_changed (Folder *folder,
235 static gint imap_close (Folder *folder,
238 static gint imap_scan_tree (Folder *folder);
240 static gint imap_create_tree (Folder *folder);
242 static FolderItem *imap_create_folder (Folder *folder,
245 static gint imap_rename_folder (Folder *folder,
248 static gint imap_remove_folder (Folder *folder,
251 static FolderItem *imap_folder_item_new (Folder *folder);
252 static void imap_folder_item_destroy (Folder *folder,
255 static IMAPSession *imap_session_get (Folder *folder);
257 static gint imap_auth (IMAPSession *session,
262 static gint imap_scan_tree_recursive (IMAPSession *session,
266 static void imap_create_missing_folders (Folder *folder);
267 static FolderItem *imap_create_special_folder
269 SpecialFolderItemType stype,
272 static gint imap_do_copy_msgs (Folder *folder,
274 MsgInfoList *msglist,
275 GRelation *relation);
277 static void imap_delete_all_cached_messages (FolderItem *item);
278 static void imap_set_batch (Folder *folder,
281 static gint imap_set_message_flags (IMAPSession *session,
282 MsgNumberList *numlist,
285 static gint imap_select (IMAPSession *session,
291 guint32 *uid_validity,
293 static gint imap_status (IMAPSession *session,
296 IMAPFolderItem *item,
299 guint32 *uid_validity,
303 static gchar imap_get_path_separator (IMAPSession *session,
306 static gchar *imap_get_real_path (IMAPSession *session,
309 static void imap_synchronise (FolderItem *item, gint days);
310 static gboolean imap_is_busy (Folder *folder);
312 static void imap_free_capabilities (IMAPSession *session);
314 /* low-level IMAP4rev1 commands */
315 static gint imap_cmd_login (IMAPSession *session,
319 static gint imap_cmd_noop (IMAPSession *session);
321 static gint imap_cmd_starttls (IMAPSession *session);
323 static gint imap_cmd_select (IMAPSession *session,
328 guint32 *uid_validity,
330 static gint imap_cmd_close (IMAPSession *session);
331 static gint imap_cmd_examine (IMAPSession *session,
336 guint32 *uid_validity,
338 static gint imap_cmd_create (IMAPSession *sock,
339 const gchar *folder);
340 static gint imap_cmd_rename (IMAPSession *sock,
341 const gchar *oldfolder,
342 const gchar *newfolder);
343 static gint imap_cmd_delete (IMAPSession *session,
344 const gchar *folder);
345 static gint imap_cmd_fetch (IMAPSession *sock,
347 const gchar *filename,
350 static gint imap_cmd_append (IMAPSession *session,
351 const gchar *destfolder,
355 static gint imap_cmd_copy (IMAPSession *session,
356 struct mailimap_set * set,
357 const gchar *destfolder,
358 GRelation *uid_mapping,
359 struct mailimap_set ** source,
360 struct mailimap_set ** dest);
361 static gint imap_cmd_store (IMAPSession *session,
362 struct mailimap_set * set,
365 static gint imap_cmd_expunge (IMAPSession *session);
367 static void imap_path_separator_subst (gchar *str,
370 static gchar *imap_utf8_to_modified_utf7 (const gchar *from);
371 static gchar *imap_modified_utf7_to_utf8 (const gchar *mutf7_str);
373 static gboolean imap_rename_folder_func (GNode *node,
375 static gint imap_get_num_list (Folder *folder,
378 gboolean *old_uids_valid);
379 static GSList *imap_get_msginfos (Folder *folder,
381 GSList *msgnum_list);
382 static MsgInfo *imap_get_msginfo (Folder *folder,
385 static gboolean imap_scan_required (Folder *folder,
387 static void imap_change_flags (Folder *folder,
390 MsgPermFlags newflags);
391 static gint imap_get_flags (Folder *folder,
393 MsgInfoList *msglist,
394 GRelation *msgflags);
395 static gchar *imap_folder_get_path (Folder *folder);
396 static gchar *imap_item_get_path (Folder *folder,
398 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item);
401 /* data types conversion libetpan <-> claws */
402 static GSList * imap_list_from_lep(IMAPFolder * folder,
403 clist * list, const gchar * real_path, gboolean all);
404 static GSList * imap_get_lep_set_from_numlist(MsgNumberList *numlist);
405 static GSList * imap_get_lep_set_from_msglist(MsgInfoList *msglist);
406 static GSList * imap_uid_list_from_lep(clist * list);
407 static GSList * imap_uid_list_from_lep_tab(carray * list);
408 static void imap_flags_hash_from_lep_uid_flags_tab(carray * list,
410 static MsgInfo *imap_envelope_from_lep(struct imap_fetch_env_info * info,
412 static void imap_lep_set_free(GSList *seq_list);
413 static struct mailimap_flag_list * imap_flag_to_lep(IMAPFlags flags);
415 typedef struct _hashtable_data {
416 IMAPSession *session;
418 IMAPFolderItem *item;
421 static FolderClass imap_class;
423 typedef struct _thread_data {
433 FolderClass *imap_get_class(void)
435 if (imap_class.idstr == NULL) {
436 imap_class.type = F_IMAP;
437 imap_class.idstr = "imap";
438 imap_class.uistr = "IMAP4";
440 /* Folder functions */
441 imap_class.new_folder = imap_folder_new;
442 imap_class.destroy_folder = imap_folder_destroy;
443 imap_class.scan_tree = imap_scan_tree;
444 imap_class.create_tree = imap_create_tree;
446 /* FolderItem functions */
447 imap_class.item_new = imap_folder_item_new;
448 imap_class.item_destroy = imap_folder_item_destroy;
449 imap_class.item_get_path = imap_item_get_path;
450 imap_class.create_folder = imap_create_folder;
451 imap_class.rename_folder = imap_rename_folder;
452 imap_class.remove_folder = imap_remove_folder;
453 imap_class.close = imap_close;
454 imap_class.get_num_list = imap_get_num_list;
455 imap_class.scan_required = imap_scan_required;
456 imap_class.set_xml = folder_set_xml;
457 imap_class.get_xml = folder_get_xml;
458 imap_class.item_set_xml = imap_item_set_xml;
459 imap_class.item_get_xml = imap_item_get_xml;
461 /* Message functions */
462 imap_class.get_msginfo = imap_get_msginfo;
463 imap_class.get_msginfos = imap_get_msginfos;
464 imap_class.fetch_msg = imap_fetch_msg;
465 imap_class.fetch_msg_full = imap_fetch_msg_full;
466 imap_class.add_msg = imap_add_msg;
467 imap_class.add_msgs = imap_add_msgs;
468 imap_class.copy_msg = imap_copy_msg;
469 imap_class.copy_msgs = imap_copy_msgs;
470 imap_class.remove_msg = imap_remove_msg;
471 imap_class.remove_msgs = imap_remove_msgs;
472 imap_class.remove_all_msg = imap_remove_all_msg;
473 imap_class.is_msg_changed = imap_is_msg_changed;
474 imap_class.change_flags = imap_change_flags;
475 imap_class.get_flags = imap_get_flags;
476 imap_class.set_batch = imap_set_batch;
477 imap_class.synchronise = imap_synchronise;
478 imap_class.remove_cached_msg = imap_remove_cached_msg;
480 pthread_mutex_init(&imap_mutex, NULL);
487 static Folder *imap_folder_new(const gchar *name, const gchar *path)
491 folder = (Folder *)g_new0(IMAPFolder, 1);
492 folder->klass = &imap_class;
493 imap_folder_init(folder, name, path);
498 static void imap_folder_destroy(Folder *folder)
500 while (imap_folder_get_refcnt(folder) > 0)
501 gtk_main_iteration();
503 folder_remote_folder_destroy(REMOTE_FOLDER(folder));
507 static void imap_folder_init(Folder *folder, const gchar *name,
510 folder_remote_folder_init((Folder *)folder, name, path);
513 static FolderItem *imap_folder_item_new(Folder *folder)
515 IMAPFolderItem *item;
517 item = g_new0(IMAPFolderItem, 1);
520 item->uid_list = NULL;
522 return (FolderItem *)item;
525 static void imap_folder_item_destroy(Folder *folder, FolderItem *_item)
527 IMAPFolderItem *item = (IMAPFolderItem *)_item;
529 g_return_if_fail(item != NULL);
530 g_slist_free(item->uid_list);
535 static gboolean imap_reset_uid_lists_func(GNode *node, gpointer data)
537 IMAPFolderItem *item = (IMAPFolderItem *)node->data;
540 g_slist_free(item->uid_list);
541 item->uid_list = NULL;
546 static void imap_reset_uid_lists(Folder *folder)
548 if(folder->node == NULL)
551 /* Destroy all uid lists and rest last uid */
552 g_node_traverse(folder->node, G_IN_ORDER, G_TRAVERSE_ALL, -1, imap_reset_uid_lists_func, NULL);
555 static int imap_get_capabilities(IMAPSession *session)
557 struct mailimap_capability_data *capabilities = NULL;
561 if (session->capability != NULL)
562 return MAILIMAP_NO_ERROR;
564 capabilities = imap_threaded_capability(session->folder, &result);
566 if (result != MAILIMAP_NO_ERROR) {
567 return MAILIMAP_ERROR_CAPABILITY;
570 if (capabilities == NULL) {
571 return MAILIMAP_NO_ERROR;
574 for(cur = clist_begin(capabilities->cap_list) ; cur != NULL ;
575 cur = clist_next(cur)) {
576 struct mailimap_capability * cap =
578 if (!cap || cap->cap_data.cap_name == NULL)
580 session->capability = g_slist_append
581 (session->capability,
582 g_strdup(cap->cap_data.cap_name));
583 debug_print("got capa %s\n", cap->cap_data.cap_name);
585 mailimap_capability_data_free(capabilities);
586 return MAILIMAP_NO_ERROR;
589 static gboolean imap_has_capability(IMAPSession *session, const gchar *cap)
592 for (cur = session->capability; cur; cur = cur->next) {
593 if (!g_ascii_strcasecmp(cur->data, cap))
599 static gint imap_auth(IMAPSession *session, const gchar *user, const gchar *pass,
602 gint ok = IMAP_ERROR;
603 static time_t last_login_err = 0;
604 gchar *ext_info = "";
606 if (imap_get_capabilities(session) != MAILIMAP_NO_ERROR)
611 ok = imap_cmd_login(session, user, pass, "ANONYMOUS");
613 case IMAP_AUTH_CRAM_MD5:
614 ok = imap_cmd_login(session, user, pass, "CRAM-MD5");
616 case IMAP_AUTH_LOGIN:
617 ok = imap_cmd_login(session, user, pass, "LOGIN");
619 case IMAP_AUTH_GSSAPI:
620 ok = imap_cmd_login(session, user, pass, "GSSAPI");
623 debug_print("capabilities:\n"
628 imap_has_capability(session, "ANONYMOUS"),
629 imap_has_capability(session, "CRAM-MD5"),
630 imap_has_capability(session, "LOGIN"),
631 imap_has_capability(session, "GSSAPI"));
632 if (imap_has_capability(session, "CRAM-MD5"))
633 ok = imap_cmd_login(session, user, pass, "CRAM-MD5");
634 if (ok == IMAP_ERROR && imap_has_capability(session, "GSSAPI"))
635 ok = imap_cmd_login(session, user, pass, "GSSAPI");
636 if (ok == IMAP_ERROR) /* we always try LOGIN before giving up */
637 ok = imap_cmd_login(session, user, pass, "LOGIN");
640 if (ok == IMAP_SUCCESS)
641 session->authenticated = TRUE;
643 if (type == IMAP_AUTH_CRAM_MD5) {
644 ext_info = _("\n\nCRAM-MD5 logins only work if libetpan has been "
645 "compiled with SASL support and the "
646 "CRAM-MD5 SASL plugin is installed.");
649 if (time(NULL) - last_login_err > 10) {
650 if (!prefs_common.no_recv_err_panel) {
651 alertpanel_error(_("Connection to %s failed: "
653 SESSION(session)->server, ext_info);
655 log_error(LOG_PROTOCOL, _("Connection to %s failed: "
656 "login refused.%s\n"),
657 SESSION(session)->server, ext_info);
660 last_login_err = time(NULL);
665 static IMAPSession *imap_reconnect_if_possible(Folder *folder, IMAPSession *session)
667 RemoteFolder *rfolder = REMOTE_FOLDER(folder);
668 /* Check if this is the first try to establish a
669 connection, if yes we don't try to reconnect */
670 debug_print("reconnecting\n");
671 if (rfolder->session == NULL) {
672 log_warning(LOG_PROTOCOL, _("Connecting to %s failed"),
673 folder->account->recv_server);
674 session_destroy(SESSION(session));
677 log_warning(LOG_PROTOCOL, _("IMAP4 connection to %s has been"
678 " disconnected. Reconnecting...\n"),
679 folder->account->recv_server);
680 statusbar_print_all(_("IMAP4 connection to %s has been"
681 " disconnected. Reconnecting...\n"),
682 folder->account->recv_server);
683 SESSION(session)->state = SESSION_DISCONNECTED;
684 session_destroy(SESSION(session));
685 /* Clear folders session to make imap_session_get create
686 a new session, because of rfolder->session == NULL
687 it will not try to reconnect again and so avoid an
689 rfolder->session = NULL;
690 debug_print("getting session...\n");
691 session = imap_session_get(folder);
692 rfolder->session = SESSION(session);
698 static void lock_session(IMAPSession *session)
703 debug_print("locking session %p (%d)\n", session, session->busy);
705 debug_print(" SESSION WAS LOCKED !! \n");
706 session->busy = TRUE;
707 mainwin = mainwindow_get_mainwindow();
709 toolbar_main_set_sensitive(mainwin);
710 main_window_set_menu_sensitive(mainwin);
713 debug_print("can't lock null session\n");
717 static void unlock_session(IMAPSession *session)
722 debug_print("unlocking session %p\n", session);
723 session->busy = FALSE;
724 mainwin = mainwindow_get_mainwindow();
726 toolbar_main_set_sensitive(mainwin);
727 main_window_set_menu_sensitive(mainwin);
730 debug_print("can't unlock null session\n");
734 static IMAPSession *imap_session_get(Folder *folder)
736 RemoteFolder *rfolder = REMOTE_FOLDER(folder);
737 IMAPSession *session = NULL;
739 g_return_val_if_fail(folder != NULL, NULL);
740 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, NULL);
741 g_return_val_if_fail(folder->account != NULL, NULL);
743 if (prefs_common.work_offline &&
744 !inc_offline_should_override(FALSE,
745 _("Claws Mail needs network access in order "
746 "to access the IMAP server."))) {
750 /* Make sure we have a session */
751 if (rfolder->session != NULL) {
752 session = IMAP_SESSION(rfolder->session);
753 } else if (rfolder->connecting) {
754 debug_print("already connecting\n");
757 imap_reset_uid_lists(folder);
758 if (time(NULL) - rfolder->last_failure <= 2)
760 rfolder->connecting = TRUE;
761 session = imap_session_new(folder, folder->account);
763 if(session == NULL) {
764 rfolder->last_failure = time(NULL);
765 rfolder->connecting = FALSE;
769 /* Make sure session is authenticated */
770 if (!IMAP_SESSION(session)->authenticated)
771 imap_session_authenticate(IMAP_SESSION(session), folder->account);
773 if (!IMAP_SESSION(session)->authenticated) {
774 imap_threaded_disconnect(session->folder);
775 SESSION(session)->state = SESSION_DISCONNECTED;
776 session_destroy(SESSION(session));
777 rfolder->session = NULL;
778 rfolder->last_failure = time(NULL);
779 rfolder->connecting = FALSE;
783 lock_session(session);
785 /* I think the point of this code is to avoid sending a
786 * keepalive if we've used the session recently and therefore
787 * think it's still alive. Unfortunately, most of the code
788 * does not yet check for errors on the socket, and so if the
789 * connection drops we don't notice until the timeout expires.
790 * A better solution than sending a NOOP every time would be
791 * for every command to be prepared to retry until it is
792 * successfully sent. -- mbp */
793 if ((time(NULL) - SESSION(session)->last_access_time > SESSION_TIMEOUT_INTERVAL) || session->cancelled) {
794 /* verify that the session is still alive */
795 if (imap_cmd_noop(session) != IMAP_SUCCESS) {
796 debug_print("disconnected!\n");
797 session = imap_reconnect_if_possible(folder, session);
800 session->cancelled = FALSE;
803 rfolder->session = SESSION(session);
804 rfolder->connecting = FALSE;
806 return IMAP_SESSION(session);
809 static IMAPSession *imap_session_new(Folder * folder,
810 const PrefsAccount *account)
812 IMAPSession *session;
815 int authenticated = FALSE;
818 /* FIXME: IMAP over SSL only... */
821 port = account->set_imapport ? account->imapport
822 : account->ssl_imap == SSL_TUNNEL ? IMAPS_PORT : IMAP4_PORT;
823 ssl_type = account->ssl_imap;
825 if (account->ssl_imap != SSL_NONE) {
826 if (alertpanel_full(_("Insecure connection"),
827 _("This connection is configured to be secured "
828 "using SSL, but SSL is not available in this "
829 "build of Claws Mail. \n\n"
830 "Do you want to continue connecting to this "
831 "server? The communication would not be "
833 GTK_STOCK_CANCEL, _("Con_tinue connecting"),
834 NULL, FALSE, NULL, ALERT_WARNING,
835 G_ALERTDEFAULT) != G_ALERTALTERNATE)
838 port = account->set_imapport ? account->imapport
843 statusbar_print_all(_("Connecting to IMAP4 server: %s..."), folder->account->recv_server);
844 if (account->set_tunnelcmd) {
845 r = imap_threaded_connect_cmd(folder,
847 account->recv_server,
852 if (ssl_type == SSL_TUNNEL) {
853 r = imap_threaded_connect_ssl(folder,
854 account->recv_server,
860 r = imap_threaded_connect(folder,
861 account->recv_server,
867 if (r == MAILIMAP_NO_ERROR_AUTHENTICATED) {
868 authenticated = TRUE;
870 else if (r == MAILIMAP_NO_ERROR_NON_AUTHENTICATED) {
871 authenticated = FALSE;
874 #if (LIBETPAN_VERSION_MAJOR > 0 || LIBETPAN_VERSION_MINOR > 48)
876 if (r == MAILIMAP_ERROR_SSL)
877 log_error(LOG_PROTOCOL, _("SSL handshake failed\n"));
880 if(!prefs_common.no_recv_err_panel) {
881 alertpanel_error_log(_("Can't connect to IMAP4 server: %s:%d"),
882 account->recv_server, port);
884 log_error(LOG_PROTOCOL, _("Can't connect to IMAP4 server: %s:%d\n"),
885 account->recv_server, port);
891 session = g_new0(IMAPSession, 1);
892 session_init(SESSION(session));
893 SESSION(session)->type = SESSION_IMAP;
894 SESSION(session)->server = g_strdup(account->recv_server);
895 SESSION(session)->sock = NULL;
897 SESSION(session)->destroy = imap_session_destroy;
899 session->capability = NULL;
901 session->authenticated = authenticated;
902 session->mbox = NULL;
903 session->cmd_count = 0;
904 session->folder = folder;
905 IMAP_FOLDER(session->folder)->last_seen_separator = 0;
908 if (account->ssl_imap == SSL_STARTTLS) {
911 ok = imap_cmd_starttls(session);
912 if (ok != IMAP_SUCCESS) {
913 log_warning(LOG_PROTOCOL, _("Can't start TLS session.\n"));
914 session_destroy(SESSION(session));
918 imap_free_capabilities(session);
919 session->authenticated = FALSE;
920 session->uidplus = FALSE;
921 session->cmd_count = 1;
924 log_message(LOG_PROTOCOL, "IMAP connection is %s-authenticated\n",
925 (session->authenticated) ? "pre" : "un");
930 static void imap_session_authenticate(IMAPSession *session,
931 const PrefsAccount *account)
933 gchar *pass, *acc_pass;
934 gboolean failed = FALSE;
936 g_return_if_fail(account->userid != NULL);
937 acc_pass = account->passwd;
940 if (!pass && account->imap_auth_type != IMAP_AUTH_ANON) {
942 tmp_pass = input_dialog_query_password(account->recv_server, account->userid);
945 Xstrdup_a(pass, tmp_pass, {g_free(tmp_pass); return;});
947 } else if (account->imap_auth_type == IMAP_AUTH_ANON) {
950 statusbar_print_all(_("Connecting to IMAP4 server %s...\n"),
951 account->recv_server);
952 if (imap_auth(session, account->userid, pass, account->imap_auth_type) != IMAP_SUCCESS) {
960 if (prefs_common.no_recv_err_panel) {
961 log_error(LOG_PROTOCOL, _("Couldn't login to IMAP server %s."), account->recv_server);
962 mainwindow_show_error();
964 alertpanel_error_log(_("Couldn't login to IMAP server %s."), account->recv_server);
971 session->authenticated = TRUE;
975 static void imap_session_destroy(Session *session)
977 if (session->state != SESSION_DISCONNECTED)
978 imap_threaded_disconnect(IMAP_SESSION(session)->folder);
980 imap_free_capabilities(IMAP_SESSION(session));
981 g_free(IMAP_SESSION(session)->mbox);
982 sock_close(session->sock);
983 session->sock = NULL;
986 static gchar *imap_fetch_msg(Folder *folder, FolderItem *item, gint uid)
988 return imap_fetch_msg_full(folder, item, uid, TRUE, TRUE);
991 static guint get_file_size_with_crs(const gchar *filename)
997 if (filename == NULL)
1000 fp = fopen(filename, "rb");
1004 while (fgets(buf, sizeof (buf), fp) != NULL) {
1006 if (!strstr(buf, "\r\n") && strstr(buf, "\n"))
1014 static void imap_remove_cached_msg(Folder *folder, FolderItem *item, MsgInfo *msginfo)
1016 gchar *path, *filename;
1018 path = folder_item_get_path(item);
1020 if (!is_dir_exist(path)) {
1025 filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(msginfo->msgnum), NULL);
1028 if (is_file_exist(filename)) {
1034 static gchar *imap_fetch_msg_full(Folder *folder, FolderItem *item, gint uid,
1035 gboolean headers, gboolean body)
1037 gchar *path, *filename;
1038 IMAPSession *session;
1041 g_return_val_if_fail(folder != NULL, NULL);
1042 g_return_val_if_fail(item != NULL, NULL);
1047 path = folder_item_get_path(item);
1048 if (!is_dir_exist(path))
1049 make_dir_hier(path);
1050 filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(uid), NULL);
1052 debug_print("trying to fetch cached %s\n", filename);
1053 if (is_file_exist(filename)) {
1054 /* see whether the local file represents the whole message
1055 * or not. As the IMAP server reports size with \r chars,
1056 * we have to update the local file (UNIX \n only) size */
1057 MsgInfo *cached = msgcache_get_msg(item->cache,uid);
1058 guint have_size = get_file_size_with_crs(filename);
1061 debug_print("message %d has been already %scached (%d/%d).\n", uid,
1062 have_size >= cached->size ? "fully ":"",
1063 have_size, (int)cached->size);
1065 if (cached && (cached->size <= have_size || !body)) {
1066 procmsg_msginfo_free(cached);
1067 file_strip_crs(filename);
1069 } else if (!cached && time(NULL) - get_file_mtime(filename) < 60) {
1070 debug_print("message not cached and file recent, considering file complete\n");
1071 file_strip_crs(filename);
1074 procmsg_msginfo_free(cached);
1078 debug_print("getting session...\n");
1079 session = imap_session_get(folder);
1086 debug_print("IMAP fetching messages\n");
1087 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
1088 NULL, NULL, NULL, NULL, FALSE);
1089 if (ok != IMAP_SUCCESS) {
1090 g_warning("can't select mailbox %s\n", item->path);
1092 unlock_session(session);
1096 debug_print("getting message %d...\n", uid);
1097 ok = imap_cmd_fetch(session, (guint32)uid, filename, headers, body);
1099 if (ok != IMAP_SUCCESS) {
1100 g_warning("can't fetch message %d\n", uid);
1102 unlock_session(session);
1106 unlock_session(session);
1107 file_strip_crs(filename);
1111 static gboolean imap_is_msg_fully_cached(Folder *folder, FolderItem *item, gint uid)
1113 gchar *path, *filename;
1115 MsgInfo *cached = msgcache_get_msg(item->cache,uid);
1120 path = folder_item_get_path(item);
1121 if (!is_dir_exist(path))
1124 filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(uid), NULL);
1126 if (is_file_exist(filename)) {
1127 if (cached && cached->total_size == cached->size) {
1132 size = get_file_size_with_crs(filename);
1135 if (cached && size >= cached->size) {
1136 cached->total_size = cached->size;
1137 procmsg_msginfo_free(cached);
1141 procmsg_msginfo_free(cached);
1145 void imap_cache_msg(FolderItem *item, gint msgnum)
1147 Folder *folder = NULL;
1151 folder = item->folder;
1153 if (!imap_is_msg_fully_cached(folder, item, msgnum)) {
1154 gchar *tmp = imap_fetch_msg_full(folder, item, msgnum, TRUE, TRUE);
1155 debug_print("fetched %s\n", tmp);
1160 static gint imap_add_msg(Folder *folder, FolderItem *dest,
1161 const gchar *file, MsgFlags *flags)
1165 MsgFileInfo fileinfo;
1167 g_return_val_if_fail(file != NULL, -1);
1169 fileinfo.msginfo = NULL;
1170 fileinfo.file = (gchar *)file;
1171 fileinfo.flags = flags;
1172 file_list.data = &fileinfo;
1173 file_list.next = NULL;
1175 ret = imap_add_msgs(folder, dest, &file_list, NULL);
1179 static gint imap_get_msg_from_local(Folder *folder, FolderItem *dest, const gchar *real_file)
1181 /* don't get session, already done. */
1183 MsgFlags flags = {0, 0};
1185 msginfo = procheader_parse_file(real_file, flags, FALSE, FALSE);
1187 if (msginfo && msginfo->msgid) {
1188 GSList *msglist = folder_item_get_msg_list(dest);
1192 /* gets last matching mail with msgid. Slower than by msgid but gets the
1193 * most recent one */
1194 for (cur = msglist ; cur != NULL ; cur = cur->next) {
1195 MsgInfo * r_msginfo;
1197 r_msginfo = (MsgInfo *) cur->data;
1199 if (r_msginfo->msgid && !strcmp(r_msginfo->msgid,msginfo->msgid)) {
1200 if (found_num < r_msginfo->msgnum) {
1201 found_num = r_msginfo->msgnum;
1204 procmsg_msginfo_free(r_msginfo);
1207 g_slist_free(msglist);
1210 procmsg_msginfo_free(msginfo);
1214 static gint imap_add_msgs(Folder *folder, FolderItem *dest, GSList *file_list,
1215 GRelation *relation)
1218 IMAPSession *session;
1219 guint32 last_uid = 0;
1221 MsgFileInfo *fileinfo;
1223 gint curnum = 0, total = 0;
1224 gboolean missing_uids = FALSE;
1226 g_return_val_if_fail(folder != NULL, -1);
1227 g_return_val_if_fail(dest != NULL, -1);
1228 g_return_val_if_fail(file_list != NULL, -1);
1230 debug_print("getting session...\n");
1231 session = imap_session_get(folder);
1235 destdir = imap_get_real_path(session, IMAP_FOLDER(folder), dest->path);
1237 statusbar_print_all(_("Adding messages..."));
1238 total = g_slist_length(file_list);
1239 for (cur = file_list; cur != NULL; cur = cur->next) {
1240 IMAPFlags iflags = 0;
1241 guint32 new_uid = 0;
1242 gchar *real_file = NULL;
1243 fileinfo = (MsgFileInfo *)cur->data;
1245 statusbar_progress_all(curnum, total, total < 10 ? 1:10);
1248 if (fileinfo->flags) {
1249 if (MSG_IS_MARKED(*fileinfo->flags))
1250 iflags |= IMAP_FLAG_FLAGGED;
1251 if (MSG_IS_REPLIED(*fileinfo->flags))
1252 iflags |= IMAP_FLAG_ANSWERED;
1253 if (!MSG_IS_UNREAD(*fileinfo->flags))
1254 iflags |= IMAP_FLAG_SEEN;
1257 if (real_file == NULL)
1258 real_file = g_strdup(fileinfo->file);
1260 if (folder_has_parent_of_type(dest, F_QUEUE) ||
1261 folder_has_parent_of_type(dest, F_OUTBOX) ||
1262 folder_has_parent_of_type(dest, F_DRAFT) ||
1263 folder_has_parent_of_type(dest, F_TRASH))
1264 iflags |= IMAP_FLAG_SEEN;
1266 ok = imap_cmd_append(session, destdir, real_file, iflags,
1269 if (ok != IMAP_SUCCESS) {
1270 g_warning("can't append message %s\n", real_file);
1273 unlock_session(session);
1274 statusbar_progress_all(0,0,0);
1275 statusbar_pop_all();
1278 debug_print("appended new message as %d\n", new_uid);
1279 /* put the local file in the imapcache, so that we don't
1280 * have to fetch it back later. */
1283 missing_uids = TRUE;
1284 debug_print("Missing UID (0)\n");
1287 gchar *cache_path = folder_item_get_path(dest);
1288 if (!is_dir_exist(cache_path))
1289 make_dir_hier(cache_path);
1290 if (is_dir_exist(cache_path)) {
1291 gchar *cache_file = g_strconcat(
1292 cache_path, G_DIR_SEPARATOR_S,
1293 itos(new_uid), NULL);
1294 copy_file(real_file, cache_file, TRUE);
1295 debug_print("got UID %d, copied to cache: %s\n", new_uid, cache_file);
1302 if (relation != NULL)
1303 g_relation_insert(relation, fileinfo->msginfo != NULL ?
1304 (gpointer) fileinfo->msginfo : (gpointer) fileinfo,
1305 GINT_TO_POINTER(new_uid));
1306 if (last_uid < new_uid) {
1314 unlock_session(IMAP_SESSION(REMOTE_FOLDER(folder)->session));
1315 folder_item_scan_full(dest, FALSE);
1316 lock_session(IMAP_SESSION(REMOTE_FOLDER(folder)->session));
1317 for (cur = file_list; cur != NULL; cur = cur->next) {
1318 guint32 new_uid = 0;
1319 fileinfo = (MsgFileInfo *)cur->data;
1321 if (!fileinfo->file)
1324 new_uid = imap_get_msg_from_local(folder, dest, fileinfo->file);
1325 debug_print("new uid %d from scanning\n", new_uid);
1327 gchar *cache_path = folder_item_get_path(dest);
1328 if (!is_dir_exist(cache_path))
1329 make_dir_hier(cache_path);
1330 if (is_dir_exist(cache_path)) {
1331 gchar *cache_file = g_strconcat(
1332 cache_path, G_DIR_SEPARATOR_S,
1333 itos(new_uid), NULL);
1334 copy_file(fileinfo->file, cache_file, TRUE);
1335 debug_print("copied to cache: %s\n", cache_file);
1339 g_relation_delete(relation, fileinfo->msginfo != NULL ?
1340 (gpointer) fileinfo->msginfo : (gpointer) fileinfo,
1343 g_relation_insert(relation, fileinfo->msginfo != NULL ?
1344 (gpointer) fileinfo->msginfo : (gpointer) fileinfo,
1345 GINT_TO_POINTER(new_uid));
1347 if (last_uid < new_uid) {
1352 statusbar_progress_all(0,0,0);
1353 statusbar_pop_all();
1355 unlock_session(session);
1362 static GSList *flatten_mailimap_set(struct mailimap_set * set)
1364 GSList *result = NULL;
1369 for (list = clist_begin(set->set_list); list; list = clist_next(list)) {
1370 struct mailimap_set_item *item = (struct mailimap_set_item *)clist_content(list);
1371 start = item->set_first;
1372 end = item->set_last;
1373 for (t = start; t <= end; t++) {
1374 result = g_slist_prepend(result, GINT_TO_POINTER(t));
1377 result = g_slist_reverse(result);
1378 if (debug_get_mode()) {
1379 debug_print("flat imap set: ");
1380 for (cur = result; cur; cur = cur->next) {
1381 debug_print("%d ", GPOINTER_TO_INT(cur->data));
1388 static gint imap_do_copy_msgs(Folder *folder, FolderItem *dest,
1389 MsgInfoList *msglist, GRelation *relation)
1393 GSList *seq_list, *cur;
1395 IMAPSession *session;
1396 gint ok = IMAP_SUCCESS;
1397 GRelation *uid_mapping;
1399 gboolean single = FALSE;
1401 g_return_val_if_fail(folder != NULL, -1);
1402 g_return_val_if_fail(dest != NULL, -1);
1403 g_return_val_if_fail(msglist != NULL, -1);
1405 debug_print("getting session...\n");
1406 session = imap_session_get(folder);
1412 msginfo = (MsgInfo *)msglist->data;
1413 if (msglist->next == NULL)
1415 src = msginfo->folder;
1417 g_warning("the src folder is identical to the dest.\n");
1418 unlock_session(session);
1422 if (src->folder != dest->folder) {
1423 GSList *infolist = NULL, *cur;
1425 for (cur = msglist; cur; cur = cur->next) {
1426 msginfo = (MsgInfo *)cur->data;
1427 MsgFileInfo *fileinfo = g_new0(MsgFileInfo, 1);
1428 fileinfo->file = procmsg_get_message_file(msginfo);
1429 fileinfo->flags = &(msginfo->flags);
1430 infolist = g_slist_prepend(infolist, fileinfo);
1432 infolist = g_slist_reverse(infolist);
1433 unlock_session(session);
1434 res = folder_item_add_msgs(dest, infolist, FALSE);
1435 for (cur = infolist; cur; cur = cur->next) {
1436 MsgFileInfo *info = (MsgFileInfo *)cur->data;
1440 g_slist_free(infolist);
1444 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
1445 NULL, NULL, NULL, NULL, FALSE);
1446 if (ok != IMAP_SUCCESS) {
1447 unlock_session(session);
1451 destdir = imap_get_real_path(session, IMAP_FOLDER(folder), dest->path);
1452 seq_list = imap_get_lep_set_from_msglist(msglist);
1453 uid_mapping = g_relation_new(2);
1454 g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
1456 statusbar_print_all(_("Copying messages..."));
1457 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
1458 struct mailimap_set * seq_set;
1459 struct mailimap_set * source = NULL;
1460 struct mailimap_set * dest = NULL;
1461 seq_set = cur->data;
1463 debug_print("Copying messages from %s to %s ...\n",
1464 src->path, destdir);
1466 ok = imap_cmd_copy(session, seq_set, destdir, uid_mapping,
1469 if (ok == IMAP_SUCCESS) {
1470 if (relation && source && dest) {
1471 GSList *s_list = flatten_mailimap_set(source);
1472 GSList *d_list = flatten_mailimap_set(dest);
1473 GSList *s_cur, *d_cur;
1474 if (g_slist_length(s_list) == g_slist_length(d_list)) {
1476 for (s_cur = s_list, d_cur = d_list;
1478 s_cur = s_cur->next, d_cur = d_cur->next) {
1479 g_relation_insert(uid_mapping, s_cur->data, d_cur->data);
1483 debug_print("hhhmm, source list length != dest list length.\n");
1485 g_slist_free(s_list);
1486 g_slist_free(d_list);
1492 mailimap_set_free(source);
1494 mailimap_set_free(dest);
1496 if (ok != IMAP_SUCCESS) {
1497 g_relation_destroy(uid_mapping);
1498 imap_lep_set_free(seq_list);
1499 unlock_session(session);
1500 statusbar_pop_all();
1505 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
1506 MsgInfo *msginfo = (MsgInfo *)cur->data;
1509 tuples = g_relation_select(uid_mapping,
1510 GINT_TO_POINTER(msginfo->msgnum),
1512 if (tuples->len > 0) {
1513 gint num = GPOINTER_TO_INT(g_tuples_index(tuples, 0, 1));
1514 g_relation_insert(relation, msginfo,
1515 GINT_TO_POINTER(num));
1518 debug_print("copied message %d as %d\n", msginfo->msgnum, num);
1519 /* put the local file in the imapcache, so that we don't
1520 * have to fetch it back later. */
1522 gchar *cache_path = folder_item_get_path(msginfo->folder);
1523 gchar *real_file = g_strconcat(
1524 cache_path, G_DIR_SEPARATOR_S,
1525 itos(msginfo->msgnum), NULL);
1526 gchar *cache_file = NULL;
1528 cache_path = folder_item_get_path(dest);
1529 cache_file = g_strconcat(
1530 cache_path, G_DIR_SEPARATOR_S,
1532 if (!is_dir_exist(cache_path))
1533 make_dir_hier(cache_path);
1534 if (is_file_exist(real_file) && is_dir_exist(cache_path)) {
1535 copy_file(real_file, cache_file, TRUE);
1536 debug_print("copied to cache: %s\n", cache_file);
1543 g_relation_insert(relation, msginfo,
1544 GINT_TO_POINTER(0));
1545 g_tuples_destroy(tuples);
1547 statusbar_pop_all();
1549 g_relation_destroy(uid_mapping);
1550 imap_lep_set_free(seq_list);
1554 IMAP_FOLDER_ITEM(dest)->lastuid = 0;
1555 IMAP_FOLDER_ITEM(dest)->uid_next = 0;
1556 g_slist_free(IMAP_FOLDER_ITEM(dest)->uid_list);
1557 IMAP_FOLDER_ITEM(dest)->uid_list = NULL;
1559 unlock_session(session);
1560 if (ok == IMAP_SUCCESS)
1566 static gint imap_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1570 g_return_val_if_fail(msginfo != NULL, -1);
1572 msglist.data = msginfo;
1573 msglist.next = NULL;
1575 return imap_copy_msgs(folder, dest, &msglist, NULL);
1578 static gint imap_copy_msgs(Folder *folder, FolderItem *dest,
1579 MsgInfoList *msglist, GRelation *relation)
1584 g_return_val_if_fail(folder != NULL, -1);
1585 g_return_val_if_fail(dest != NULL, -1);
1586 g_return_val_if_fail(msglist != NULL, -1);
1588 msginfo = (MsgInfo *)msglist->data;
1589 g_return_val_if_fail(msginfo->folder != NULL, -1);
1591 ret = imap_do_copy_msgs(folder, dest, msglist, relation);
1596 static gint imap_do_remove_msgs(Folder *folder, FolderItem *dest,
1597 MsgInfoList *msglist, GRelation *relation)
1599 gchar *destdir, *dir;
1600 GSList *numlist = NULL, *cur;
1602 IMAPSession *session;
1603 gint ok = IMAP_SUCCESS;
1604 GRelation *uid_mapping;
1606 g_return_val_if_fail(folder != NULL, -1);
1607 g_return_val_if_fail(dest != NULL, -1);
1608 g_return_val_if_fail(msglist != NULL, -1);
1610 debug_print("getting session...\n");
1611 session = imap_session_get(folder);
1616 msginfo = (MsgInfo *)msglist->data;
1618 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
1619 NULL, NULL, NULL, NULL, FALSE);
1620 if (ok != IMAP_SUCCESS) {
1621 unlock_session(session);
1625 destdir = imap_get_real_path(session, IMAP_FOLDER(folder), dest->path);
1626 for (cur = msglist; cur; cur = cur->next) {
1627 msginfo = (MsgInfo *)cur->data;
1628 if (!MSG_IS_DELETED(msginfo->flags))
1629 numlist = g_slist_prepend(numlist, GINT_TO_POINTER(msginfo->msgnum));
1631 numlist = g_slist_reverse(numlist);
1633 uid_mapping = g_relation_new(2);
1634 g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
1636 ok = imap_set_message_flags
1637 (session, numlist, IMAP_FLAG_DELETED, TRUE);
1638 if (ok != IMAP_SUCCESS) {
1639 log_warning(LOG_PROTOCOL, _("can't set deleted flags\n"));
1640 unlock_session(session);
1643 ok = imap_cmd_expunge(session);
1644 if (ok != IMAP_SUCCESS) {
1645 log_warning(LOG_PROTOCOL, _("can't expunge\n"));
1646 unlock_session(session);
1650 dir = folder_item_get_path(msginfo->folder);
1651 if (is_dir_exist(dir)) {
1652 for (cur = msglist; cur; cur = cur->next) {
1653 msginfo = (MsgInfo *)cur->data;
1654 remove_numbered_files(dir, msginfo->msgnum, msginfo->msgnum);
1659 g_relation_destroy(uid_mapping);
1660 g_slist_free(numlist);
1663 unlock_session(session);
1664 if (ok == IMAP_SUCCESS)
1670 static gint imap_remove_msgs(Folder *folder, FolderItem *dest,
1671 MsgInfoList *msglist, GRelation *relation)
1675 g_return_val_if_fail(folder != NULL, -1);
1676 g_return_val_if_fail(dest != NULL, -1);
1677 if (msglist == NULL)
1680 msginfo = (MsgInfo *)msglist->data;
1681 g_return_val_if_fail(msginfo->folder != NULL, -1);
1683 return imap_do_remove_msgs(folder, dest, msglist, relation);
1686 static gint imap_remove_all_msg(Folder *folder, FolderItem *item)
1688 GSList *list = folder_item_get_msg_list(item);
1689 gint res = imap_remove_msgs(folder, item, list, NULL);
1690 procmsg_msg_list_free(list);
1694 static gboolean imap_is_msg_changed(Folder *folder, FolderItem *item,
1697 /* TODO: properly implement this method */
1701 static gint imap_close(Folder *folder, FolderItem *item)
1706 gint imap_scan_tree_real(Folder *folder, gboolean subs_only)
1708 FolderItem *item = NULL;
1709 IMAPSession *session;
1710 gchar *root_folder = NULL;
1712 g_return_val_if_fail(folder != NULL, -1);
1713 g_return_val_if_fail(folder->account != NULL, -1);
1715 debug_print("getting session...\n");
1716 session = imap_session_get(folder);
1718 if (!folder->node) {
1719 folder_tree_destroy(folder);
1720 item = folder_item_new(folder, folder->name, NULL);
1721 item->folder = folder;
1722 folder->node = item->node = g_node_new(item);
1727 if (folder->account->imap_dir && *folder->account->imap_dir) {
1732 Xstrdup_a(root_folder, folder->account->imap_dir, {unlock_session(session);return -1;});
1733 extract_quote(root_folder, '"');
1734 subst_char(root_folder,
1735 imap_get_path_separator(session, IMAP_FOLDER(folder),
1738 strtailchomp(root_folder, '/');
1739 real_path = imap_get_real_path
1740 (session, IMAP_FOLDER(folder), root_folder);
1741 debug_print("IMAP root directory: %s\n", real_path);
1743 /* check if root directory exist */
1745 r = imap_threaded_list(session->folder, "", real_path,
1747 if ((r != MAILIMAP_NO_ERROR) || (clist_count(lep_list) == 0)) {
1748 if (!folder->node) {
1749 item = folder_item_new(folder, folder->name, NULL);
1750 item->folder = folder;
1751 folder->node = item->node = g_node_new(item);
1753 unlock_session(session);
1756 mailimap_list_result_free(lep_list);
1762 item = FOLDER_ITEM(folder->node->data);
1764 if (item && !item->path && root_folder) {
1765 item->path = g_strdup(root_folder);
1768 if (!item || ((item->path || root_folder) &&
1769 strcmp2(item->path, root_folder) != 0)) {
1770 folder_tree_destroy(folder);
1771 item = folder_item_new(folder, folder->name, root_folder);
1772 item->folder = folder;
1773 folder->node = item->node = g_node_new(item);
1776 imap_scan_tree_recursive(session, FOLDER_ITEM(folder->node->data), subs_only);
1777 imap_create_missing_folders(folder);
1778 unlock_session(session);
1783 static gint imap_scan_tree(Folder *folder)
1785 gboolean subs_only = FALSE;
1786 if (folder->account) {
1787 debug_print(" scanning only subs %d\n", folder->account->imap_subsonly);
1788 subs_only = folder->account->imap_subsonly;
1790 return imap_scan_tree_real(folder, subs_only);
1793 static gint imap_scan_tree_recursive(IMAPSession *session, FolderItem *item, gboolean subs_only)
1796 IMAPFolder *imapfolder;
1797 FolderItem *new_item;
1798 GSList *item_list, *cur;
1801 gchar *wildcard_path;
1807 g_return_val_if_fail(item != NULL, -1);
1808 g_return_val_if_fail(item->folder != NULL, -1);
1809 g_return_val_if_fail(item->no_sub == FALSE, -1);
1811 folder = item->folder;
1812 imapfolder = IMAP_FOLDER(folder);
1814 separator = imap_get_path_separator(session, imapfolder, item->path);
1816 if (folder->ui_func)
1817 folder->ui_func(folder, item, folder->ui_func_data);
1820 wildcard[0] = separator;
1823 real_path = imap_get_real_path(session, imapfolder, item->path);
1827 real_path = g_strdup("");
1830 Xstrcat_a(wildcard_path, real_path, wildcard,
1831 {g_free(real_path); return IMAP_ERROR;});
1835 r = imap_threaded_lsub(folder, "", wildcard_path, &lep_list);
1837 r = imap_threaded_list(folder, "", wildcard_path, &lep_list);
1839 if (r != MAILIMAP_NO_ERROR) {
1843 item_list = imap_list_from_lep(imapfolder,
1844 lep_list, real_path, FALSE);
1845 mailimap_list_result_free(lep_list);
1850 node = item->node->children;
1851 while (node != NULL) {
1852 FolderItem *old_item = FOLDER_ITEM(node->data);
1853 GNode *next = node->next;
1856 for (cur = item_list; cur != NULL; cur = cur->next) {
1857 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1858 if (!strcmp2(old_item->path, cur_item->path)) {
1859 new_item = cur_item;
1864 if (old_item && old_item->path && !strcmp(old_item->path, "INBOX")) {
1865 debug_print("not removing INBOX\n");
1867 debug_print("folder '%s' not found. removing...\n",
1869 folder_item_remove(old_item);
1872 old_item->no_sub = new_item->no_sub;
1873 old_item->no_select = new_item->no_select;
1874 if (old_item->no_sub == TRUE && node->children) {
1875 debug_print("folder '%s' doesn't have "
1876 "subfolders. removing...\n",
1878 folder_item_remove_children(old_item);
1885 for (cur = item_list; cur != NULL; cur = cur->next) {
1886 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1889 for (node = item->node->children; node != NULL;
1890 node = node->next) {
1891 if (!strcmp2(FOLDER_ITEM(node->data)->path,
1893 new_item = FOLDER_ITEM(node->data);
1894 folder_item_destroy(cur_item);
1900 new_item = cur_item;
1901 debug_print("new folder '%s' found.\n", new_item->path);
1902 folder_item_append(item, new_item);
1905 if (!strcmp(new_item->path, "INBOX")) {
1906 new_item->stype = F_INBOX;
1907 folder->inbox = new_item;
1908 } else if (!folder_item_parent(item) || item->stype == F_INBOX) {
1911 base = g_path_get_basename(new_item->path);
1913 if (!folder->outbox && !g_ascii_strcasecmp(base, "Sent")) {
1914 new_item->stype = F_OUTBOX;
1915 folder->outbox = new_item;
1916 } else if (!folder->draft && !g_ascii_strcasecmp(base, "Drafts")) {
1917 new_item->stype = F_DRAFT;
1918 folder->draft = new_item;
1919 } else if (!folder->queue && !g_ascii_strcasecmp(base, "Queue")) {
1920 new_item->stype = F_QUEUE;
1921 folder->queue = new_item;
1922 } else if (!folder->trash && !g_ascii_strcasecmp(base, "Trash")) {
1923 new_item->stype = F_TRASH;
1924 folder->trash = new_item;
1929 if (new_item->no_sub == FALSE)
1930 imap_scan_tree_recursive(session, new_item, subs_only);
1933 g_slist_free(item_list);
1935 return IMAP_SUCCESS;
1938 GList *imap_scan_subtree(Folder *folder, FolderItem *item, gboolean unsubs_only, gboolean recursive)
1940 IMAPSession *session = imap_session_get(folder);
1942 gchar *wildcard_path;
1946 GSList *item_list = NULL, *cur;
1947 GList *child_list = NULL, *tmplist = NULL;
1948 GSList *sub_list = NULL;
1954 separator = imap_get_path_separator(session, IMAP_FOLDER(folder), item->path);
1957 wildcard[0] = separator;
1960 real_path = imap_get_real_path(session, IMAP_FOLDER(folder), item->path);
1964 real_path = g_strdup("");
1967 Xstrcat_a(wildcard_path, real_path, wildcard,
1968 {g_free(real_path); return NULL;});
1972 statusbar_print_all(_("Looking for unsubscribed folders in %s..."),
1973 item->path?item->path:item->name);
1975 statusbar_print_all(_("Looking for subfolders of %s..."),
1976 item->path?item->path:item->name);
1978 r = imap_threaded_list(folder, "", wildcard_path, &lep_list);
1980 statusbar_pop_all();
1983 item_list = imap_list_from_lep(IMAP_FOLDER(folder),
1984 lep_list, real_path, FALSE);
1985 mailimap_list_result_free(lep_list);
1987 for (cur = item_list; cur != NULL; cur = cur->next) {
1988 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1990 tmplist = imap_scan_subtree(folder, cur_item,
1991 unsubs_only, recursive);
1993 child_list = g_list_concat(child_list, tmplist);
1995 child_list = g_list_prepend(child_list,
1996 imap_get_real_path(session,
1997 IMAP_FOLDER(folder), cur_item->path));
1999 folder_item_destroy(cur_item);
2001 child_list = g_list_reverse(child_list);
2002 g_slist_free(item_list);
2005 r = imap_threaded_lsub(folder, "", wildcard_path, &lep_list);
2007 statusbar_pop_all();
2010 sub_list = imap_list_from_lep(IMAP_FOLDER(folder),
2011 lep_list, real_path, FALSE);
2012 mailimap_list_result_free(lep_list);
2014 for (cur = sub_list; cur != NULL; cur = cur->next) {
2015 FolderItem *cur_item = FOLDER_ITEM(cur->data);
2016 GList *oldlitem = NULL;
2017 gchar *tmp = imap_get_real_path(session,
2018 IMAP_FOLDER(folder), cur_item->path);
2019 folder_item_destroy(cur_item);
2020 oldlitem = g_list_find_custom(
2021 child_list, tmp, (GCompareFunc)strcmp2);
2023 child_list = g_list_remove_link(child_list, oldlitem);
2024 g_free(oldlitem->data);
2025 g_list_free(oldlitem);
2031 statusbar_pop_all();
2036 static gint imap_create_tree(Folder *folder)
2038 g_return_val_if_fail(folder != NULL, -1);
2039 g_return_val_if_fail(folder->node != NULL, -1);
2040 g_return_val_if_fail(folder->node->data != NULL, -1);
2041 g_return_val_if_fail(folder->account != NULL, -1);
2043 imap_scan_tree(folder);
2044 imap_create_missing_folders(folder);
2049 static void imap_create_missing_folders(Folder *folder)
2051 g_return_if_fail(folder != NULL);
2054 folder->inbox = imap_create_special_folder
2055 (folder, F_INBOX, "INBOX");
2057 folder->trash = imap_create_special_folder
2058 (folder, F_TRASH, "Trash");
2060 folder->queue = imap_create_special_folder
2061 (folder, F_QUEUE, "Queue");
2062 if (!folder->outbox)
2063 folder->outbox = imap_create_special_folder
2064 (folder, F_OUTBOX, "Sent");
2066 folder->draft = imap_create_special_folder
2067 (folder, F_DRAFT, "Drafts");
2070 static FolderItem *imap_create_special_folder(Folder *folder,
2071 SpecialFolderItemType stype,
2075 FolderItem *new_item;
2077 g_return_val_if_fail(folder != NULL, NULL);
2078 g_return_val_if_fail(folder->node != NULL, NULL);
2079 g_return_val_if_fail(folder->node->data != NULL, NULL);
2080 g_return_val_if_fail(folder->account != NULL, NULL);
2081 g_return_val_if_fail(name != NULL, NULL);
2083 item = FOLDER_ITEM(folder->node->data);
2084 new_item = imap_create_folder(folder, item, name);
2087 g_warning("Can't create '%s'\n", name);
2088 if (!folder->inbox) return NULL;
2090 new_item = imap_create_folder(folder, folder->inbox, name);
2092 g_warning("Can't create '%s' under INBOX\n", name);
2094 new_item->stype = stype;
2096 new_item->stype = stype;
2101 static gchar *imap_folder_get_path(Folder *folder)
2105 g_return_val_if_fail(folder != NULL, NULL);
2106 g_return_val_if_fail(folder->account != NULL, NULL);
2108 folder_path = g_strconcat(get_imap_cache_dir(),
2110 folder->account->recv_server,
2112 folder->account->userid,
2118 static gchar *imap_item_get_path(Folder *folder, FolderItem *item)
2120 gchar *folder_path, *path;
2122 g_return_val_if_fail(folder != NULL, NULL);
2123 g_return_val_if_fail(item != NULL, NULL);
2124 folder_path = imap_folder_get_path(folder);
2126 g_return_val_if_fail(folder_path != NULL, NULL);
2127 if (folder_path[0] == G_DIR_SEPARATOR) {
2129 path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
2132 path = g_strdup(folder_path);
2135 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
2136 folder_path, G_DIR_SEPARATOR_S,
2139 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
2142 g_free(folder_path);
2147 static FolderItem *imap_create_folder(Folder *folder, FolderItem *parent,
2150 gchar *dirpath, *imap_path;
2151 IMAPSession *session;
2152 FolderItem *new_item;
2157 gboolean no_select = FALSE, no_sub = FALSE;
2158 gboolean exist = FALSE;
2160 g_return_val_if_fail(folder != NULL, NULL);
2161 g_return_val_if_fail(folder->account != NULL, NULL);
2162 g_return_val_if_fail(parent != NULL, NULL);
2163 g_return_val_if_fail(name != NULL, NULL);
2165 debug_print("getting session...\n");
2166 session = imap_session_get(folder);
2171 if (!folder_item_parent(parent) && strcmp(name, "INBOX") == 0) {
2172 dirpath = g_strdup(name);
2173 }else if (parent->path)
2174 dirpath = g_strconcat(parent->path, "/", name, NULL);
2175 else if ((p = strchr(name, '/')) != NULL && *(p + 1) != '\0')
2176 dirpath = g_strdup(name);
2177 else if (folder->account->imap_dir && *folder->account->imap_dir) {
2180 Xstrdup_a(imap_dir, folder->account->imap_dir, {unlock_session(session);return NULL;});
2181 strtailchomp(imap_dir, '/');
2182 dirpath = g_strconcat(imap_dir, "/", name, NULL);
2184 dirpath = g_strdup(name);
2188 /* keep trailing directory separator to create a folder that contains
2190 imap_path = imap_utf8_to_modified_utf7(dirpath);
2192 strtailchomp(dirpath, '/');
2193 Xstrdup_a(new_name, name, {
2195 unlock_session(session);
2198 separator = imap_get_path_separator(session, IMAP_FOLDER(folder), imap_path);
2199 imap_path_separator_subst(imap_path, separator);
2200 /* remove trailing / for display */
2201 strtailchomp(new_name, '/');
2203 if (strcmp(dirpath, "INBOX") != 0) {
2208 argbuf = g_ptr_array_new();
2209 r = imap_threaded_list(folder, "", imap_path, &lep_list);
2210 if (r != MAILIMAP_NO_ERROR) {
2211 log_warning(LOG_PROTOCOL, _("can't create mailbox: LIST failed\n"));
2214 ptr_array_free_strings(argbuf);
2215 g_ptr_array_free(argbuf, TRUE);
2216 unlock_session(session);
2220 if (clist_count(lep_list) > 0)
2222 mailimap_list_result_free(lep_list);
2225 ok = imap_cmd_create(session, imap_path);
2226 if (ok != IMAP_SUCCESS) {
2227 log_warning(LOG_PROTOCOL, _("can't create mailbox\n"));
2230 unlock_session(session);
2233 r = imap_threaded_list(folder, "", imap_path, &lep_list);
2234 if (r == MAILIMAP_NO_ERROR) {
2235 GSList *item_list = imap_list_from_lep(IMAP_FOLDER(folder),
2236 lep_list, dirpath, TRUE);
2238 FolderItem *cur_item = FOLDER_ITEM(item_list->data);
2239 no_select = cur_item->no_select;
2240 no_sub = cur_item->no_sub;
2241 g_slist_free(item_list);
2243 mailimap_list_result_free(lep_list);
2246 imap_threaded_subscribe(folder, imap_path, TRUE);
2250 /* just get flags */
2251 r = imap_threaded_list(folder, "", "INBOX", &lep_list);
2252 if (r == MAILIMAP_NO_ERROR) {
2253 GSList *item_list = imap_list_from_lep(IMAP_FOLDER(folder),
2254 lep_list, dirpath, TRUE);
2256 FolderItem *cur_item = FOLDER_ITEM(item_list->data);
2257 no_select = cur_item->no_select;
2258 no_sub = cur_item->no_sub;
2259 g_slist_free(item_list);
2261 mailimap_list_result_free(lep_list);
2265 new_item = folder_item_new(folder, new_name, dirpath);
2266 new_item->no_select = no_select;
2267 new_item->no_sub = no_sub;
2268 folder_item_append(parent, new_item);
2272 dirpath = folder_item_get_path(new_item);
2273 if (!is_dir_exist(dirpath))
2274 make_dir_hier(dirpath);
2276 unlock_session(session);
2279 /* folder existed, scan it */
2280 folder_item_scan_full(new_item, FALSE);
2286 static gint imap_rename_folder(Folder *folder, FolderItem *item,
2291 gchar *real_oldpath;
2292 gchar *real_newpath;
2294 gchar *old_cache_dir;
2295 gchar *new_cache_dir;
2296 IMAPSession *session;
2299 gint exists, recent, unseen;
2300 guint32 uid_validity;
2302 g_return_val_if_fail(folder != NULL, -1);
2303 g_return_val_if_fail(item != NULL, -1);
2304 g_return_val_if_fail(item->path != NULL, -1);
2305 g_return_val_if_fail(name != NULL, -1);
2307 debug_print("getting session...\n");
2308 session = imap_session_get(folder);
2313 if (strchr(name, imap_get_path_separator(session, IMAP_FOLDER(folder), item->path)) != NULL) {
2314 g_warning(_("New folder name must not contain the namespace "
2316 unlock_session(session);
2320 real_oldpath = imap_get_real_path(session, IMAP_FOLDER(folder), item->path);
2322 g_free(session->mbox);
2323 session->mbox = NULL;
2324 ok = imap_cmd_examine(session, "INBOX",
2325 &exists, &recent, &unseen, &uid_validity, FALSE);
2326 if (ok != IMAP_SUCCESS) {
2327 g_free(real_oldpath);
2328 unlock_session(session);
2332 separator = imap_get_path_separator(session, IMAP_FOLDER(folder), item->path);
2333 if (strchr(item->path, G_DIR_SEPARATOR)) {
2334 dirpath = g_path_get_dirname(item->path);
2335 newpath = g_strconcat(dirpath, G_DIR_SEPARATOR_S, name, NULL);
2338 newpath = g_strdup(name);
2340 real_newpath = imap_utf8_to_modified_utf7(newpath);
2341 imap_path_separator_subst(real_newpath, separator);
2343 ok = imap_cmd_rename(session, real_oldpath, real_newpath);
2344 if (ok != IMAP_SUCCESS) {
2345 log_warning(LOG_PROTOCOL, _("can't rename mailbox: %s to %s\n"),
2346 real_oldpath, real_newpath);
2347 g_free(real_oldpath);
2349 g_free(real_newpath);
2350 unlock_session(session);
2354 item->name = g_strdup(name);
2356 old_cache_dir = folder_item_get_path(item);
2358 paths[0] = g_strdup(item->path);
2360 g_node_traverse(item->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
2361 imap_rename_folder_func, paths);
2363 if (is_dir_exist(old_cache_dir)) {
2364 new_cache_dir = folder_item_get_path(item);
2365 if (rename(old_cache_dir, new_cache_dir) < 0) {
2366 FILE_OP_ERROR(old_cache_dir, "rename");
2368 g_free(new_cache_dir);
2371 g_free(old_cache_dir);
2374 g_free(real_oldpath);
2375 g_free(real_newpath);
2376 unlock_session(session);
2380 gint imap_subscribe(Folder *folder, FolderItem *item, gchar *rpath, gboolean sub)
2384 IMAPSession *session;
2385 debug_print("getting session...\n");
2387 session = imap_session_get(folder);
2391 if (item && item->path) {
2392 path = imap_get_real_path(session, IMAP_FOLDER(folder), item->path);
2395 if (!strcmp(path, "INBOX") && sub == FALSE)
2397 debug_print("%ssubscribing %s\n", sub?"":"un", path);
2398 r = imap_threaded_subscribe(folder, path, sub);
2401 r = imap_threaded_subscribe(folder, rpath, sub);
2407 static gint imap_remove_folder_real(Folder *folder, FolderItem *item)
2410 IMAPSession *session;
2414 g_return_val_if_fail(folder != NULL, -1);
2415 g_return_val_if_fail(item != NULL, -1);
2416 g_return_val_if_fail(item->path != NULL, -1);
2418 debug_print("getting session...\n");
2419 session = imap_session_get(folder);
2423 path = imap_get_real_path(session, IMAP_FOLDER(folder), item->path);
2425 imap_threaded_subscribe(folder, path, FALSE);
2426 ok = imap_cmd_delete(session, path);
2427 if (ok != IMAP_SUCCESS) {
2428 gchar *tmp = g_strdup_printf("%s%c", path,
2429 imap_get_path_separator(session, IMAP_FOLDER(folder), path));
2432 ok = imap_cmd_delete(session, path);
2435 if (ok != IMAP_SUCCESS) {
2436 log_warning(LOG_PROTOCOL, _("can't delete mailbox\n"));
2438 unlock_session(session);
2443 cache_dir = folder_item_get_path(item);
2444 if (is_dir_exist(cache_dir) && remove_dir_recursive(cache_dir) < 0)
2445 g_warning("can't remove directory '%s'\n", cache_dir);
2447 folder_item_remove(item);
2448 unlock_session(session);
2452 static gint imap_remove_folder(Folder *folder, FolderItem *item)
2456 g_return_val_if_fail(item != NULL, -1);
2457 g_return_val_if_fail(item->folder != NULL, -1);
2458 g_return_val_if_fail(item->node != NULL, -1);
2460 node = item->node->children;
2461 while (node != NULL) {
2463 if (imap_remove_folder(folder, FOLDER_ITEM(node->data)) < 0)
2467 debug_print("IMAP removing %s\n", item->path);
2469 if (imap_remove_all_msg(folder, item) < 0)
2471 return imap_remove_folder_real(folder, item);
2474 typedef struct _uncached_data {
2475 IMAPSession *session;
2477 MsgNumberList *numlist;
2483 static void *imap_get_uncached_messages_thread(void *data)
2485 uncached_data *stuff = (uncached_data *)data;
2486 IMAPSession *session = stuff->session;
2487 FolderItem *item = stuff->item;
2488 MsgNumberList *numlist = stuff->numlist;
2490 GSList *newlist = NULL;
2491 GSList *llast = NULL;
2492 GSList *seq_list, *cur;
2494 debug_print("uncached_messages\n");
2496 if (session == NULL || item == NULL || item->folder == NULL
2497 || FOLDER_CLASS(item->folder) != &imap_class) {
2502 seq_list = imap_get_lep_set_from_numlist(numlist);
2503 debug_print("get msgs info\n");
2504 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
2505 struct mailimap_set * imapset;
2511 if (session->cancelled)
2514 imapset = cur->data;
2516 r = imap_threaded_fetch_env(session->folder,
2517 imapset, &env_list);
2518 if (r != MAILIMAP_NO_ERROR)
2521 session_set_access_time(SESSION(session));
2524 for(i = 0 ; i < carray_count(env_list) ; i ++) {
2525 struct imap_fetch_env_info * info;
2528 info = carray_get(env_list, i);
2529 msginfo = imap_envelope_from_lep(info, item);
2530 if (msginfo == NULL)
2532 msginfo->folder = item;
2534 llast = newlist = g_slist_append(newlist, msginfo);
2536 llast = g_slist_append(llast, msginfo);
2537 llast = llast->next;
2542 imap_fetch_env_free(env_list);
2545 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
2546 struct mailimap_set * imapset;
2548 imapset = cur->data;
2549 mailimap_set_free(imapset);
2552 session_set_access_time(SESSION(session));
2557 #define MAX_MSG_NUM 50
2559 static GSList *imap_get_uncached_messages(IMAPSession *session,
2561 MsgNumberList *numlist)
2563 GSList *result = NULL;
2565 uncached_data *data = g_new0(uncached_data, 1);
2570 data->total = g_slist_length(numlist);
2571 debug_print("messages list : %i\n", data->total);
2573 while (cur != NULL) {
2574 GSList * partial_result;
2582 while (count < MAX_MSG_NUM) {
2587 if (newlist == NULL)
2588 llast = newlist = g_slist_append(newlist, p);
2590 llast = g_slist_append(llast, p);
2591 llast = llast->next;
2601 data->session = session;
2603 data->numlist = newlist;
2606 if (prefs_common.work_offline &&
2607 !inc_offline_should_override(FALSE,
2608 _("Claws Mail needs network access in order "
2609 "to access the IMAP server."))) {
2615 (GSList *)imap_get_uncached_messages_thread(data);
2617 statusbar_progress_all(data->cur,data->total, 1);
2619 g_slist_free(newlist);
2621 result = g_slist_concat(result, partial_result);
2625 statusbar_progress_all(0,0,0);
2626 statusbar_pop_all();
2631 static void imap_delete_all_cached_messages(FolderItem *item)
2635 g_return_if_fail(item != NULL);
2636 g_return_if_fail(item->folder != NULL);
2637 g_return_if_fail(FOLDER_CLASS(item->folder) == &imap_class);
2639 debug_print("Deleting all cached messages...\n");
2641 dir = folder_item_get_path(item);
2642 if (is_dir_exist(dir))
2643 remove_all_numbered_files(dir);
2646 debug_print("done.\n");
2649 gchar imap_get_path_separator_for_item(FolderItem *item)
2651 Folder *folder = NULL;
2652 IMAPFolder *imap_folder = NULL;
2653 IMAPSession *session = NULL;
2658 folder = item->folder;
2663 imap_folder = IMAP_FOLDER(folder);
2668 debug_print("getting session...");
2669 session = imap_session_get(FOLDER(folder));
2670 result = imap_get_path_separator(session, imap_folder, item->path);
2671 unlock_session(session);
2675 static gchar imap_refresh_path_separator(IMAPSession *session, IMAPFolder *folder, const gchar *subfolder)
2679 gchar separator = '\0';
2681 g_return_val_if_fail(session != NULL, '/');
2682 r = imap_threaded_list((Folder *)folder, "", subfolder, &lep_list);
2684 if (r != MAILIMAP_NO_ERROR) {
2685 log_warning(LOG_PROTOCOL, _("LIST failed\n"));
2689 if (clist_count(lep_list) > 0) {
2690 clistiter * iter = clist_begin(lep_list);
2691 struct mailimap_mailbox_list * mb;
2692 mb = clist_content(iter);
2694 separator = mb->mb_delimiter;
2695 debug_print("got separator: %c\n", folder->last_seen_separator);
2697 mailimap_list_result_free(lep_list);
2701 static gchar imap_get_path_separator(IMAPSession *session, IMAPFolder *folder, const gchar *path)
2703 gchar separator = '/';
2705 if (folder->last_seen_separator == 0) {
2706 folder->last_seen_separator = imap_refresh_path_separator(session, folder, "");
2709 if (folder->last_seen_separator == 0) {
2710 folder->last_seen_separator = imap_refresh_path_separator(session, folder, "INBOX");
2713 if (folder->last_seen_separator != 0) {
2714 debug_print("using separator: %c\n", folder->last_seen_separator);
2715 return folder->last_seen_separator;
2721 static gchar *imap_get_real_path(IMAPSession *session, IMAPFolder *folder, const gchar *path)
2726 g_return_val_if_fail(folder != NULL, NULL);
2727 g_return_val_if_fail(path != NULL, NULL);
2729 real_path = imap_utf8_to_modified_utf7(path);
2730 separator = imap_get_path_separator(session, folder, path);
2731 imap_path_separator_subst(real_path, separator);
2736 static gint imap_set_message_flags(IMAPSession *session,
2737 MsgNumberList *numlist,
2745 seq_list = imap_get_lep_set_from_numlist(numlist);
2747 for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) {
2748 struct mailimap_set * imapset;
2750 imapset = cur->data;
2752 ok = imap_cmd_store(session, imapset,
2756 imap_lep_set_free(seq_list);
2758 return IMAP_SUCCESS;
2761 typedef struct _select_data {
2762 IMAPSession *session;
2767 guint32 *uid_validity;
2771 static gint imap_select(IMAPSession *session, IMAPFolder *folder,
2773 gint *exists, gint *recent, gint *unseen,
2774 guint32 *uid_validity, gboolean block)
2778 gint exists_, recent_, unseen_;
2779 guint32 uid_validity_;
2781 if (!exists && !recent && !unseen && !uid_validity) {
2782 if (session->mbox && strcmp(session->mbox, path) == 0)
2783 return IMAP_SUCCESS;
2792 uid_validity = &uid_validity_;
2794 g_free(session->mbox);
2795 session->mbox = NULL;
2797 real_path = imap_get_real_path(session, folder, path);
2799 ok = imap_cmd_select(session, real_path,
2800 exists, recent, unseen, uid_validity, block);
2801 if (ok != IMAP_SUCCESS)
2802 log_warning(LOG_PROTOCOL, _("can't select folder: %s\n"), real_path);
2804 session->mbox = g_strdup(path);
2805 session->folder_content_changed = FALSE;
2812 static gint imap_status(IMAPSession *session, IMAPFolder *folder,
2813 const gchar *path, IMAPFolderItem *item,
2815 guint32 *uid_next, guint32 *uid_validity,
2816 gint *unseen, gboolean block)
2820 struct mailimap_mailbox_data_status * data_status;
2825 real_path = imap_get_real_path(session, folder, path);
2844 if (session->mbox != NULL &&
2845 !strcmp(session->mbox, item->item.path)) {
2846 r = imap_cmd_close(session);
2847 if (r != MAILIMAP_NO_ERROR) {
2848 debug_print("close err %d\n", r);
2853 r = imap_threaded_status(FOLDER(folder), real_path,
2854 &data_status, mask);
2857 if (r != MAILIMAP_NO_ERROR) {
2858 debug_print("status err %d\n", r);
2862 if (data_status->st_info_list == NULL) {
2863 mailimap_mailbox_data_status_free(data_status);
2864 debug_print("status->st_info_list == NULL\n");
2869 for(iter = clist_begin(data_status->st_info_list) ; iter != NULL ;
2870 iter = clist_next(iter)) {
2871 struct mailimap_status_info * info;
2873 info = clist_content(iter);
2874 switch (info->st_att) {
2875 case MAILIMAP_STATUS_ATT_MESSAGES:
2877 * messages = info->st_value;
2878 got_values |= 1 << 0;
2882 case MAILIMAP_STATUS_ATT_UIDNEXT:
2884 * uid_next = info->st_value;
2885 got_values |= 1 << 2;
2889 case MAILIMAP_STATUS_ATT_UIDVALIDITY:
2891 * uid_validity = info->st_value;
2892 got_values |= 1 << 3;
2896 case MAILIMAP_STATUS_ATT_UNSEEN:
2898 * unseen = info->st_value;
2899 got_values |= 1 << 4;
2904 mailimap_mailbox_data_status_free(data_status);
2906 if (got_values != mask) {
2907 g_warning("status: incomplete values received (%d)\n", got_values);
2909 return IMAP_SUCCESS;
2912 static void imap_free_capabilities(IMAPSession *session)
2914 slist_free_strings(session->capability);
2915 g_slist_free(session->capability);
2916 session->capability = NULL;
2919 /* low-level IMAP4rev1 commands */
2921 static gint imap_cmd_login(IMAPSession *session,
2922 const gchar *user, const gchar *pass,
2928 if (!strcmp(type, "LOGIN") && imap_has_capability(session, "LOGINDISABLED")) {
2929 gint ok = IMAP_ERROR;
2930 if (imap_has_capability(session, "STARTTLS")) {
2932 log_warning(LOG_PROTOCOL, _("Server requires TLS to log in.\n"));
2933 ok = imap_cmd_starttls(session);
2934 if (ok != IMAP_SUCCESS) {
2935 log_warning(LOG_PROTOCOL, _("Can't start TLS session.\n"));
2939 imap_free_capabilities(session);
2940 if (imap_get_capabilities(session) != MAILIMAP_NO_ERROR) {
2941 log_warning(LOG_PROTOCOL, _("Can't refresh capabilities.\n"));
2946 log_error(LOG_PROTOCOL, _("Connection to %s failed: "
2947 "server requires TLS, but Claws Mail "
2948 "has been compiled without OpenSSL "
2950 SESSION(session)->server);
2954 log_error(LOG_PROTOCOL, _("Server logins are disabled.\n"));
2959 log_print(LOG_PROTOCOL, "IMAP4> Logging %s to %s using %s\n",
2961 SESSION(session)->server,
2963 r = imap_threaded_login(session->folder, user, pass, type);
2964 if (r != MAILIMAP_NO_ERROR) {
2965 log_print(LOG_PROTOCOL, "IMAP4< Error logging in to %s\n",
2966 SESSION(session)->server);
2969 log_print(LOG_PROTOCOL, "IMAP4< Login to %s successful\n",
2970 SESSION(session)->server);
2976 static gint imap_cmd_noop(IMAPSession *session)
2979 unsigned int exists;
2981 r = imap_threaded_noop(session->folder, &exists);
2982 if (r != MAILIMAP_NO_ERROR) {
2983 debug_print("noop err %d\n", r);
2986 session->exists = exists;
2987 session_set_access_time(SESSION(session));
2989 return IMAP_SUCCESS;
2993 static gint imap_cmd_starttls(IMAPSession *session)
2997 r = imap_threaded_starttls(session->folder,
2998 SESSION(session)->server, SESSION(session)->port);
2999 if (r != MAILIMAP_NO_ERROR) {
3000 debug_print("starttls err %d\n", r);
3003 return IMAP_SUCCESS;
3007 static gint imap_cmd_select(IMAPSession *session, const gchar *folder,
3008 gint *exists, gint *recent, gint *unseen,
3009 guint32 *uid_validity, gboolean block)
3013 r = imap_threaded_select(session->folder, folder,
3014 exists, recent, unseen, uid_validity);
3015 if (r != MAILIMAP_NO_ERROR) {
3016 debug_print("select err %d\n", r);
3019 return IMAP_SUCCESS;
3022 static gint imap_cmd_close(IMAPSession *session)
3026 r = imap_threaded_close(session->folder);
3027 if (r != MAILIMAP_NO_ERROR) {
3028 debug_print("close err %d\n", r);
3031 g_free(session->mbox);
3032 session->mbox = NULL;
3033 return IMAP_SUCCESS;
3036 static gint imap_cmd_examine(IMAPSession *session, const gchar *folder,
3037 gint *exists, gint *recent, gint *unseen,
3038 guint32 *uid_validity, gboolean block)
3042 r = imap_threaded_examine(session->folder, folder,
3043 exists, recent, unseen, uid_validity);
3044 if (r != MAILIMAP_NO_ERROR) {
3045 debug_print("examine err %d\n", r);
3049 return IMAP_SUCCESS;
3052 static gint imap_cmd_create(IMAPSession *session, const gchar *folder)
3056 r = imap_threaded_create(session->folder, folder);
3057 if (r != MAILIMAP_NO_ERROR) {
3062 return IMAP_SUCCESS;
3065 static gint imap_cmd_rename(IMAPSession *session, const gchar *old_folder,
3066 const gchar *new_folder)
3070 r = imap_threaded_rename(session->folder, old_folder,
3072 if (r != MAILIMAP_NO_ERROR) {
3077 return IMAP_SUCCESS;
3080 static gint imap_cmd_delete(IMAPSession *session, const gchar *folder)
3085 r = imap_threaded_delete(session->folder, folder);
3086 if (r != MAILIMAP_NO_ERROR) {
3091 return IMAP_SUCCESS;
3094 typedef struct _fetch_data {
3095 IMAPSession *session;
3097 const gchar *filename;
3103 static void *imap_cmd_fetch_thread(void *data)
3105 fetch_data *stuff = (fetch_data *)data;
3106 IMAPSession *session = stuff->session;
3107 guint32 uid = stuff->uid;
3108 const gchar *filename = stuff->filename;
3112 r = imap_threaded_fetch_content(session->folder,
3116 r = imap_threaded_fetch_content(session->folder,
3119 if (r != MAILIMAP_NO_ERROR) {
3120 debug_print("fetch err %d\n", r);
3121 return GINT_TO_POINTER(IMAP_ERROR);
3123 return GINT_TO_POINTER(IMAP_SUCCESS);
3126 static gint imap_cmd_fetch(IMAPSession *session, guint32 uid,
3127 const gchar *filename, gboolean headers,
3130 fetch_data *data = g_new0(fetch_data, 1);
3133 data->session = session;
3135 data->filename = filename;
3136 data->headers = headers;
3139 if (prefs_common.work_offline &&
3140 !inc_offline_should_override(FALSE,
3141 _("Claws Mail needs network access in order "
3142 "to access the IMAP server."))) {
3146 statusbar_print_all(_("Fetching message..."));
3147 result = GPOINTER_TO_INT(imap_cmd_fetch_thread(data));
3148 statusbar_pop_all();
3154 static gint imap_cmd_append(IMAPSession *session, const gchar *destfolder,
3155 const gchar *file, IMAPFlags flags,
3158 struct mailimap_flag_list * flag_list;
3161 g_return_val_if_fail(file != NULL, IMAP_ERROR);
3163 flag_list = imap_flag_to_lep(flags);
3164 r = imap_threaded_append(session->folder, destfolder,
3165 file, flag_list, (int *)new_uid);
3166 mailimap_flag_list_free(flag_list);
3168 if (r != MAILIMAP_NO_ERROR) {
3169 debug_print("append err %d\n", r);
3172 return IMAP_SUCCESS;
3175 static gint imap_cmd_copy(IMAPSession *session, struct mailimap_set * set,
3176 const gchar *destfolder, GRelation *uid_mapping,
3177 struct mailimap_set **source, struct mailimap_set **dest)
3181 g_return_val_if_fail(session != NULL, IMAP_ERROR);
3182 g_return_val_if_fail(set != NULL, IMAP_ERROR);
3183 g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
3185 r = imap_threaded_copy(session->folder, set, destfolder, source, dest);
3186 if (r != MAILIMAP_NO_ERROR) {
3191 return IMAP_SUCCESS;
3194 static gint imap_cmd_store(IMAPSession *session, struct mailimap_set * set,
3195 IMAPFlags flags, int do_add)
3198 struct mailimap_flag_list * flag_list;
3199 struct mailimap_store_att_flags * store_att_flags;
3201 flag_list = imap_flag_to_lep(flags);
3205 mailimap_store_att_flags_new_add_flags_silent(flag_list);
3208 mailimap_store_att_flags_new_remove_flags_silent(flag_list);
3210 r = imap_threaded_store(session->folder, set, store_att_flags);
3211 mailimap_store_att_flags_free(store_att_flags);
3212 if (r != MAILIMAP_NO_ERROR) {
3217 return IMAP_SUCCESS;
3220 static gint imap_cmd_expunge(IMAPSession *session)
3224 if (prefs_common.work_offline &&
3225 !inc_offline_should_override(FALSE,
3226 _("Claws Mail needs network access in order "
3227 "to access the IMAP server."))) {
3231 r = imap_threaded_expunge(session->folder);
3232 if (r != MAILIMAP_NO_ERROR) {
3237 return IMAP_SUCCESS;
3240 static void imap_path_separator_subst(gchar *str, gchar separator)
3243 gboolean in_escape = FALSE;
3245 if (!separator || separator == '/') return;
3247 for (p = str; *p != '\0'; p++) {
3248 if (*p == '/' && !in_escape)
3250 else if (*p == '&' && *(p + 1) != '-' && !in_escape)
3252 else if (*p == '-' && in_escape)
3257 static gchar *imap_modified_utf7_to_utf8(const gchar *mutf7_str)
3259 static iconv_t cd = (iconv_t)-1;
3260 static gboolean iconv_ok = TRUE;
3263 size_t norm_utf7_len;
3265 gchar *to_str, *to_p;
3267 gboolean in_escape = FALSE;
3269 if (!iconv_ok) return g_strdup(mutf7_str);
3271 if (cd == (iconv_t)-1) {
3272 cd = iconv_open(CS_INTERNAL, CS_UTF_7);
3273 if (cd == (iconv_t)-1) {
3274 g_warning("iconv cannot convert UTF-7 to %s\n",
3277 return g_strdup(mutf7_str);
3281 /* modified UTF-7 to normal UTF-7 conversion */
3282 norm_utf7 = g_string_new(NULL);
3284 for (p = mutf7_str; *p != '\0'; p++) {
3285 /* replace: '&' -> '+',
3287 escaped ',' -> '/' */
3288 if (!in_escape && *p == '&') {
3289 if (*(p + 1) != '-') {
3290 g_string_append_c(norm_utf7, '+');
3293 g_string_append_c(norm_utf7, '&');
3296 } else if (in_escape && *p == ',') {
3297 g_string_append_c(norm_utf7, '/');
3298 } else if (in_escape && *p == '-') {
3299 g_string_append_c(norm_utf7, '-');
3302 g_string_append_c(norm_utf7, *p);
3306 norm_utf7_p = norm_utf7->str;
3307 norm_utf7_len = norm_utf7->len;
3308 to_len = strlen(mutf7_str) * 5;
3309 to_p = to_str = g_malloc(to_len + 1);
3311 if (iconv(cd, (ICONV_CONST gchar **)&norm_utf7_p, &norm_utf7_len,
3312 &to_p, &to_len) == -1) {
3313 g_warning(_("iconv cannot convert UTF-7 to %s\n"),
3314 conv_get_locale_charset_str());
3315 g_string_free(norm_utf7, TRUE);
3317 return g_strdup(mutf7_str);
3320 /* second iconv() call for flushing */
3321 iconv(cd, NULL, NULL, &to_p, &to_len);
3322 g_string_free(norm_utf7, TRUE);
3328 static gchar *imap_utf8_to_modified_utf7(const gchar *from)
3330 static iconv_t cd = (iconv_t)-1;
3331 static gboolean iconv_ok = TRUE;
3332 gchar *norm_utf7, *norm_utf7_p;
3333 size_t from_len, norm_utf7_len;
3335 gchar *from_tmp, *to, *p;
3336 gboolean in_escape = FALSE;
3338 if (!iconv_ok) return g_strdup(from);
3340 if (cd == (iconv_t)-1) {
3341 cd = iconv_open(CS_UTF_7, CS_INTERNAL);
3342 if (cd == (iconv_t)-1) {
3343 g_warning(_("iconv cannot convert %s to UTF-7\n"),
3346 return g_strdup(from);
3350 /* UTF-8 to normal UTF-7 conversion */
3351 Xstrdup_a(from_tmp, from, return g_strdup(from));
3352 from_len = strlen(from);
3353 norm_utf7_len = from_len * 5;
3354 Xalloca(norm_utf7, norm_utf7_len + 1, return g_strdup(from));
3355 norm_utf7_p = norm_utf7;
3357 #define IS_PRINT(ch) (isprint(ch) && IS_ASCII(ch))
3359 while (from_len > 0) {
3360 if (*from_tmp == '+') {
3361 *norm_utf7_p++ = '+';
3362 *norm_utf7_p++ = '-';
3366 } else if (IS_PRINT(*(guchar *)from_tmp)) {
3367 /* printable ascii char */
3368 *norm_utf7_p = *from_tmp;
3374 size_t conv_len = 0;
3376 /* unprintable char: convert to UTF-7 */
3378 while (!IS_PRINT(*(guchar *)p) && conv_len < from_len) {
3379 conv_len += g_utf8_skip[*(guchar *)p];
3380 p += g_utf8_skip[*(guchar *)p];
3383 from_len -= conv_len;
3384 if (iconv(cd, (ICONV_CONST gchar **)&from_tmp,
3386 &norm_utf7_p, &norm_utf7_len) == -1) {
3387 g_warning(_("iconv cannot convert UTF-8 to UTF-7\n"));
3388 return g_strdup(from);
3391 /* second iconv() call for flushing */
3392 iconv(cd, NULL, NULL, &norm_utf7_p, &norm_utf7_len);
3398 *norm_utf7_p = '\0';
3399 to_str = g_string_new(NULL);
3400 for (p = norm_utf7; p < norm_utf7_p; p++) {
3401 /* replace: '&' -> "&-",
3404 BASE64 '/' -> ',' */
3405 if (!in_escape && *p == '&') {
3406 g_string_append(to_str, "&-");
3407 } else if (!in_escape && *p == '+') {
3408 if (*(p + 1) == '-') {
3409 g_string_append_c(to_str, '+');
3412 g_string_append_c(to_str, '&');
3415 } else if (in_escape && *p == '/') {
3416 g_string_append_c(to_str, ',');
3417 } else if (in_escape && *p == '-') {
3418 g_string_append_c(to_str, '-');
3421 g_string_append_c(to_str, *p);
3427 g_string_append_c(to_str, '-');
3431 g_string_free(to_str, FALSE);
3436 static gboolean imap_rename_folder_func(GNode *node, gpointer data)
3438 FolderItem *item = node->data;
3439 gchar **paths = data;
3440 const gchar *oldpath = paths[0];
3441 const gchar *newpath = paths[1];
3442 gchar *real_oldpath, *real_newpath;
3444 gchar *new_itempath;
3446 IMAPSession *session = imap_session_get(item->folder);
3448 oldpathlen = strlen(oldpath);
3449 if (strncmp(oldpath, item->path, oldpathlen) != 0) {
3450 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
3454 base = item->path + oldpathlen;
3455 while (*base == G_DIR_SEPARATOR) base++;
3457 new_itempath = g_strdup(newpath);
3459 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
3462 real_oldpath = imap_get_real_path(session, IMAP_FOLDER(item->folder), item->path);
3464 item->path = new_itempath;
3466 real_newpath = imap_get_real_path(session, IMAP_FOLDER(item->folder), item->path);
3468 imap_threaded_subscribe(item->folder, real_oldpath, FALSE);
3469 imap_threaded_subscribe(item->folder, real_newpath, TRUE);
3471 g_free(real_oldpath);
3472 g_free(real_newpath);
3476 typedef struct _get_list_uid_data {
3478 IMAPSession *session;
3479 IMAPFolderItem *item;
3480 GSList **msgnum_list;
3482 } get_list_uid_data;
3484 static void *get_list_of_uids_thread(void *data)
3486 get_list_uid_data *stuff = (get_list_uid_data *)data;
3487 Folder *folder = stuff->folder;
3488 IMAPFolderItem *item = stuff->item;
3489 GSList **msgnum_list = stuff->msgnum_list;
3490 gint ok, nummsgs = 0, lastuid_old;
3491 IMAPSession *session;
3492 GSList *uidlist, *elem;
3494 clist * lep_uidlist;
3496 session = stuff->session;
3497 if (session == NULL) {
3499 return GINT_TO_POINTER(-1);
3501 /* no session locking here, it's already locked by caller */
3502 ok = imap_select(session, IMAP_FOLDER(folder), item->item.path,
3503 NULL, NULL, NULL, NULL, TRUE);
3504 if (ok != IMAP_SUCCESS) {
3506 return GINT_TO_POINTER(-1);
3511 if (folder->account && folder->account->low_bandwidth) {
3512 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SIMPLE, NULL,
3516 if (r == MAILIMAP_NO_ERROR) {
3517 GSList * fetchuid_list =
3518 imap_uid_list_from_lep(lep_uidlist);
3519 mailimap_search_result_free(lep_uidlist);
3521 uidlist = g_slist_concat(fetchuid_list, uidlist);
3523 carray * lep_uidtab;
3524 r = imap_threaded_fetch_uid(folder, 1,
3526 if (r == MAILIMAP_NO_ERROR) {
3527 GSList * fetchuid_list =
3528 imap_uid_list_from_lep_tab(lep_uidtab);
3529 imap_fetch_uid_list_free(lep_uidtab);
3530 uidlist = g_slist_concat(fetchuid_list, uidlist);
3534 if (r != MAILIMAP_NO_ERROR) {
3536 return GINT_TO_POINTER(-1);
3539 lastuid_old = item->lastuid;
3540 *msgnum_list = g_slist_copy(item->uid_list);
3541 nummsgs = g_slist_length(*msgnum_list);
3542 debug_print("Got %d uids from cache\n", g_slist_length(item->uid_list));
3544 for (elem = uidlist; elem != NULL; elem = g_slist_next(elem)) {
3547 msgnum = GPOINTER_TO_INT(elem->data);
3548 if (msgnum > lastuid_old) {
3549 *msgnum_list = g_slist_prepend(*msgnum_list, GINT_TO_POINTER(msgnum));
3550 item->uid_list = g_slist_prepend(item->uid_list, GINT_TO_POINTER(msgnum));
3553 if(msgnum > item->lastuid)
3554 item->lastuid = msgnum;
3557 g_slist_free(uidlist);
3559 return GINT_TO_POINTER(nummsgs);
3562 static gint get_list_of_uids(IMAPSession *session, Folder *folder, IMAPFolderItem *item, GSList **msgnum_list)
3565 get_list_uid_data *data = g_new0(get_list_uid_data, 1);
3567 data->folder = folder;
3569 data->msgnum_list = msgnum_list;
3570 data->session = session;
3571 if (prefs_common.work_offline &&
3572 !inc_offline_should_override(FALSE,
3573 _("Claws Mail needs network access in order "
3574 "to access the IMAP server."))) {
3579 result = GPOINTER_TO_INT(get_list_of_uids_thread(data));
3585 gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list, gboolean *old_uids_valid)
3587 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3588 IMAPSession *session;
3589 gint ok, nummsgs = 0, exists;
3590 guint32 uid_next = 0, uid_val = 0;
3591 GSList *uidlist = NULL;
3593 gboolean selected_folder;
3594 debug_print("get_num_list\n");
3596 g_return_val_if_fail(folder != NULL, -1);
3597 g_return_val_if_fail(item != NULL, -1);
3598 g_return_val_if_fail(item->item.path != NULL, -1);
3599 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3600 g_return_val_if_fail(folder->account != NULL, -1);
3602 debug_print("getting session...\n");
3603 session = imap_session_get(folder);
3604 g_return_val_if_fail(session != NULL, -1);
3606 if (FOLDER_ITEM(item)->path)
3607 statusbar_print_all(_("Scanning folder %s%c%s ..."),
3608 FOLDER_ITEM(item)->folder->name,
3610 FOLDER_ITEM(item)->path);
3612 statusbar_print_all(_("Scanning folder %s ..."),
3613 FOLDER_ITEM(item)->folder->name);
3615 selected_folder = (session->mbox != NULL) &&
3616 (!strcmp(session->mbox, item->item.path));
3617 if (selected_folder && time(NULL) - item->use_cache < 2) {
3618 ok = imap_cmd_noop(session);
3619 if (ok != IMAP_SUCCESS) {
3620 debug_print("disconnected!\n");
3621 session = imap_reconnect_if_possible(folder, session);
3622 if (session == NULL) {
3623 statusbar_pop_all();
3624 unlock_session(session);
3628 exists = session->exists;
3630 uid_next = item->c_uid_next;
3631 uid_val = item->c_uid_validity;
3632 *old_uids_valid = TRUE;
3634 if (item->use_cache && time(NULL) - item->use_cache < 2) {
3635 exists = item->c_messages;
3636 uid_next = item->c_uid_next;
3637 uid_val = item->c_uid_validity;
3639 debug_print("using cache %d %d %d\n", exists, uid_next, uid_val);
3641 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path, item,
3642 &exists, &uid_next, &uid_val, NULL, FALSE);
3644 item->item.last_num = uid_next - 1;
3646 item->use_cache = (time_t)0;
3647 if (ok != IMAP_SUCCESS) {
3648 statusbar_pop_all();
3649 unlock_session(session);
3652 if(item->item.mtime == uid_val)
3653 *old_uids_valid = TRUE;
3655 *old_uids_valid = FALSE;
3657 debug_print("Freeing imap uid cache (%d != %d)\n",
3658 (int)item->item.mtime, uid_val);
3660 g_slist_free(item->uid_list);
3661 item->uid_list = NULL;
3663 item->item.mtime = uid_val;
3665 imap_delete_all_cached_messages((FolderItem *)item);
3669 /* If old uid_next matches new uid_next we can be sure no message
3670 was added to the folder */
3671 debug_print("uid_next is %d and item->uid_next %d \n",
3672 uid_next, item->uid_next);
3673 if (uid_next == item->uid_next) {
3674 nummsgs = g_slist_length(item->uid_list);
3676 /* If number of messages is still the same we
3677 know our caches message numbers are still valid,
3678 otherwise if the number of messages has decrease
3679 we discard our cache to start a new scan to find
3680 out which numbers have been removed */
3681 if (exists == nummsgs) {
3682 debug_print("exists == nummsgs\n");
3683 *msgnum_list = g_slist_copy(item->uid_list);
3684 statusbar_pop_all();
3685 unlock_session(session);
3687 } else if (exists < nummsgs) {
3688 debug_print("Freeing imap uid cache");
3690 g_slist_free(item->uid_list);
3691 item->uid_list = NULL;
3696 *msgnum_list = NULL;
3697 statusbar_pop_all();
3698 unlock_session(session);
3702 item->last_change = time(NULL);
3703 nummsgs = get_list_of_uids(session, folder, item, &uidlist);
3706 statusbar_pop_all();
3707 unlock_session(session);
3711 if (nummsgs != exists) {
3712 /* Cache contains more messages then folder, we have cached
3713 an old UID of a message that was removed and new messages
3714 have been added too, otherwise the uid_next check would
3716 debug_print("Freeing imap uid cache");
3718 g_slist_free(item->uid_list);
3719 item->uid_list = NULL;
3721 g_slist_free(*msgnum_list);
3723 nummsgs = get_list_of_uids(session, folder, item, &uidlist);
3726 *msgnum_list = uidlist;
3728 dir = folder_item_get_path((FolderItem *)item);
3729 debug_print("removing old messages from %s\n", dir);
3730 remove_numbered_files_not_in_list(dir, *msgnum_list);
3733 item->uid_next = uid_next;
3735 debug_print("get_num_list - ok - %i\n", nummsgs);
3736 statusbar_pop_all();
3737 unlock_session(session);
3741 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item)
3746 flags.perm_flags = MSG_NEW|MSG_UNREAD;
3747 flags.tmp_flags = 0;
3749 g_return_val_if_fail(item != NULL, NULL);
3750 g_return_val_if_fail(file != NULL, NULL);
3752 if (folder_has_parent_of_type(item, F_QUEUE)) {
3753 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
3754 } else if (folder_has_parent_of_type(item, F_DRAFT)) {
3755 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
3758 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3759 if (!msginfo) return NULL;
3761 msginfo->plaintext_file = g_strdup(file);
3762 msginfo->folder = item;
3767 GSList *imap_get_msginfos(Folder *folder, FolderItem *item,
3768 GSList *msgnum_list)
3770 IMAPSession *session;
3771 MsgInfoList *ret = NULL;
3774 debug_print("get_msginfos\n");
3776 g_return_val_if_fail(folder != NULL, NULL);
3777 g_return_val_if_fail(item != NULL, NULL);
3778 g_return_val_if_fail(msgnum_list != NULL, NULL);
3780 debug_print("getting session...\n");
3781 session = imap_session_get(folder);
3782 g_return_val_if_fail(session != NULL, NULL);
3784 debug_print("IMAP getting msginfos\n");
3785 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3786 NULL, NULL, NULL, NULL, FALSE);
3787 if (ok != IMAP_SUCCESS) {
3788 unlock_session(session);
3791 if (!(folder_has_parent_of_type(item, F_DRAFT) ||
3792 folder_has_parent_of_type(item, F_QUEUE))) {
3793 ret = g_slist_concat(ret,
3794 imap_get_uncached_messages(session, item,
3797 MsgNumberList *sorted_list, *elem, *llast = NULL;
3798 gint startnum, lastnum;
3800 sorted_list = g_slist_sort(g_slist_copy(msgnum_list), g_int_compare);
3802 startnum = lastnum = GPOINTER_TO_INT(sorted_list->data);
3804 llast = g_slist_last(ret);
3805 for (elem = sorted_list;; elem = g_slist_next(elem)) {
3809 num = GPOINTER_TO_INT(elem->data);
3811 if (num > lastnum + 1 || elem == NULL) {
3813 for (i = startnum; i <= lastnum; ++i) {
3815 unlock_session(session);
3816 file = imap_fetch_msg(folder, item, i);
3817 lock_session(session);
3819 MsgInfo *msginfo = imap_parse_msg(file, item);
3820 if (msginfo != NULL) {
3821 msginfo->msgnum = i;
3823 llast = ret = g_slist_append(ret, msginfo);
3825 llast = g_slist_append(llast, msginfo);
3826 llast = llast->next;
3831 session_set_access_time(SESSION(session));
3842 g_slist_free(sorted_list);
3844 unlock_session(session);
3848 MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid)
3850 MsgInfo *msginfo = NULL;
3851 MsgInfoList *msginfolist;
3852 MsgNumberList numlist;
3854 numlist.next = NULL;
3855 numlist.data = GINT_TO_POINTER(uid);
3857 msginfolist = imap_get_msginfos(folder, item, &numlist);
3858 if (msginfolist != NULL) {
3859 msginfo = msginfolist->data;
3860 g_slist_free(msginfolist);
3866 gboolean imap_scan_required(Folder *folder, FolderItem *_item)
3868 IMAPSession *session;
3869 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3870 gint ok, exists = 0, unseen = 0;
3871 guint32 uid_next = 0, uid_val = 0;
3872 gboolean selected_folder;
3874 g_return_val_if_fail(folder != NULL, FALSE);
3875 g_return_val_if_fail(item != NULL, FALSE);
3876 g_return_val_if_fail(item->item.folder != NULL, FALSE);
3877 g_return_val_if_fail(FOLDER_CLASS(item->item.folder) == &imap_class, FALSE);
3879 if (item->item.path == NULL)
3882 debug_print("getting session...\n");
3883 session = imap_session_get(folder);
3884 g_return_val_if_fail(session != NULL, FALSE);
3886 selected_folder = (session->mbox != NULL) &&
3887 (!strcmp(session->mbox, item->item.path));
3888 if (selected_folder && time(NULL) - item->use_cache < 2) {
3889 ok = imap_cmd_noop(session);
3890 if (ok != IMAP_SUCCESS) {
3891 debug_print("disconnected!\n");
3892 session = imap_reconnect_if_possible(folder, session);
3893 if (session == NULL)
3897 if (session->folder_content_changed
3898 || session->exists != item->item.total_msgs) {
3899 unlock_session(session);
3903 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path, IMAP_FOLDER_ITEM(item),
3904 &exists, &uid_next, &uid_val, &unseen, FALSE);
3905 if (ok != IMAP_SUCCESS) {
3906 unlock_session(session);
3910 item->use_cache = time(NULL);
3911 item->c_messages = exists;
3912 item->c_uid_next = uid_next;
3913 item->c_uid_validity = uid_val;
3914 item->c_unseen = unseen;
3915 item->item.last_num = uid_next - 1;
3916 debug_print("uidnext %d, item->uid_next %d, exists %d, item->item.total_msgs %d\n",
3917 uid_next, item->uid_next, exists, item->item.total_msgs);
3918 if ((uid_next != item->uid_next) || (exists != item->item.total_msgs)
3919 || unseen != item->item.unread_msgs || uid_val != item->item.mtime) {
3920 unlock_session(session);
3921 item->last_change = time(NULL);
3925 unlock_session(session);
3929 void imap_change_flags(Folder *folder, FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
3931 IMAPSession *session;
3932 IMAPFlags flags_set = 0, flags_unset = 0;
3933 gint ok = IMAP_SUCCESS;
3934 MsgNumberList numlist;
3935 hashtable_data *ht_data = NULL;
3937 g_return_if_fail(folder != NULL);
3938 g_return_if_fail(folder->klass == &imap_class);
3939 g_return_if_fail(item != NULL);
3940 g_return_if_fail(item->folder == folder);
3941 g_return_if_fail(msginfo != NULL);
3942 g_return_if_fail(msginfo->folder == item);
3944 if (!MSG_IS_MARKED(msginfo->flags) && (newflags & MSG_MARKED))
3945 flags_set |= IMAP_FLAG_FLAGGED;
3946 if ( MSG_IS_MARKED(msginfo->flags) && !(newflags & MSG_MARKED))
3947 flags_unset |= IMAP_FLAG_FLAGGED;
3949 if (!MSG_IS_UNREAD(msginfo->flags) && (newflags & MSG_UNREAD))
3950 flags_unset |= IMAP_FLAG_SEEN;
3951 if ( MSG_IS_UNREAD(msginfo->flags) && !(newflags & MSG_UNREAD))
3952 flags_set |= IMAP_FLAG_SEEN;
3954 if (!MSG_IS_REPLIED(msginfo->flags) && (newflags & MSG_REPLIED))
3955 flags_set |= IMAP_FLAG_ANSWERED;
3956 if ( MSG_IS_REPLIED(msginfo->flags) && !(newflags & MSG_REPLIED))
3957 flags_unset |= IMAP_FLAG_ANSWERED;
3959 if (!MSG_IS_DELETED(msginfo->flags) && (newflags & MSG_DELETED))
3960 flags_set |= IMAP_FLAG_DELETED;
3961 if ( MSG_IS_DELETED(msginfo->flags) && !(newflags & MSG_DELETED))
3962 flags_unset |= IMAP_FLAG_DELETED;
3964 if (!flags_set && !flags_unset) {
3965 /* the changed flags were not translatable to IMAP-speak.
3966 * like MSG_POSTFILTERED, so just apply. */
3967 msginfo->flags.perm_flags = newflags;
3971 debug_print("getting session...\n");
3972 session = imap_session_get(folder);
3977 if ((ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
3978 NULL, NULL, NULL, NULL, FALSE)) != IMAP_SUCCESS) {
3979 unlock_session(session);
3982 numlist.next = NULL;
3983 numlist.data = GINT_TO_POINTER(msginfo->msgnum);
3985 if (IMAP_FOLDER_ITEM(item)->batching) {
3986 /* instead of performing an UID STORE command for each message change,
3987 * as a lot of them can change "together", we just fill in hashtables
3988 * and defer the treatment so that we're able to send only one
3991 debug_print("IMAP batch mode on, deferring flags change\n");
3993 ht_data = g_hash_table_lookup(IMAP_FOLDER_ITEM(item)->flags_set_table,
3994 GINT_TO_POINTER(flags_set));
3995 if (ht_data == NULL) {
3996 ht_data = g_new0(hashtable_data, 1);
3997 ht_data->session = session;
3998 ht_data->item = IMAP_FOLDER_ITEM(item);
3999 g_hash_table_insert(IMAP_FOLDER_ITEM(item)->flags_set_table,
4000 GINT_TO_POINTER(flags_set), ht_data);
4002 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
4003 ht_data->msglist = g_slist_prepend(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum));
4006 ht_data = g_hash_table_lookup(IMAP_FOLDER_ITEM(item)->flags_unset_table,
4007 GINT_TO_POINTER(flags_unset));
4008 if (ht_data == NULL) {
4009 ht_data = g_new0(hashtable_data, 1);
4010 ht_data->session = session;
4011 ht_data->item = IMAP_FOLDER_ITEM(item);
4012 g_hash_table_insert(IMAP_FOLDER_ITEM(item)->flags_unset_table,
4013 GINT_TO_POINTER(flags_unset), ht_data);
4015 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
4016 ht_data->msglist = g_slist_prepend(ht_data->msglist,
4017 GINT_TO_POINTER(msginfo->msgnum));
4020 debug_print("IMAP changing flags\n");
4022 ok = imap_set_message_flags(session, &numlist, flags_set, TRUE);
4023 if (ok != IMAP_SUCCESS) {
4024 unlock_session(session);
4030 ok = imap_set_message_flags(session, &numlist, flags_unset, FALSE);
4031 if (ok != IMAP_SUCCESS) {
4032 unlock_session(session);
4037 msginfo->flags.perm_flags = newflags;
4038 unlock_session(session);
4042 static gint imap_remove_msg(Folder *folder, FolderItem *item, gint uid)
4045 IMAPSession *session;
4047 MsgNumberList numlist;
4049 g_return_val_if_fail(folder != NULL, -1);
4050 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
4051 g_return_val_if_fail(item != NULL, -1);
4053 debug_print("getting session...\n");
4054 session = imap_session_get(folder);
4055 if (!session) return -1;
4057 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
4058 NULL, NULL, NULL, NULL, FALSE);
4059 if (ok != IMAP_SUCCESS) {
4060 unlock_session(session);
4063 numlist.next = NULL;
4064 numlist.data = GINT_TO_POINTER(uid);
4066 ok = imap_set_message_flags
4067 (session, &numlist, IMAP_FLAG_DELETED, TRUE);
4068 if (ok != IMAP_SUCCESS) {
4069 log_warning(LOG_PROTOCOL, _("can't set deleted flags: %d\n"), uid);
4070 unlock_session(session);
4074 if (!session->uidplus) {
4075 ok = imap_cmd_expunge(session);
4079 uidstr = g_strdup_printf("%u", uid);
4080 ok = imap_cmd_expunge(session);
4083 if (ok != IMAP_SUCCESS) {
4084 log_warning(LOG_PROTOCOL, _("can't expunge\n"));
4085 unlock_session(session);
4089 IMAP_FOLDER_ITEM(item)->uid_list = g_slist_remove(
4090 IMAP_FOLDER_ITEM(item)->uid_list, numlist.data);
4091 dir = folder_item_get_path(item);
4092 if (is_dir_exist(dir))
4093 remove_numbered_files(dir, uid, uid);
4095 unlock_session(session);
4096 return IMAP_SUCCESS;
4099 static gint compare_msginfo(gconstpointer a, gconstpointer b)
4101 return ((MsgInfo *)a)->msgnum - ((MsgInfo *)b)->msgnum;
4104 static guint gslist_find_next_num(MsgNumberList **list, guint num)
4108 g_return_val_if_fail(list != NULL, -1);
4110 for (elem = *list; elem != NULL; elem = g_slist_next(elem))
4111 if (GPOINTER_TO_INT(elem->data) >= num)
4114 return elem != NULL ? GPOINTER_TO_INT(elem->data) : (gint)-1;
4118 * NEW and DELETED flags are not syncronized
4119 * - The NEW/RECENT flags in IMAP folders can not really be directly
4120 * modified by Sylpheed
4121 * - The DELETE/DELETED flag in IMAP and Sylpheed don't have the same
4122 * meaning, in IMAP it always removes the messages from the FolderItem
4123 * in Sylpheed it can mean to move the message to trash
4126 typedef struct _get_flags_data {
4129 MsgInfoList *msginfo_list;
4130 GRelation *msgflags;
4131 gboolean full_search;
4135 static /*gint*/ void *imap_get_flags_thread(void *data)
4137 get_flags_data *stuff = (get_flags_data *)data;
4138 Folder *folder = stuff->folder;
4139 FolderItem *fitem = (FolderItem *) stuff->item;
4140 MsgInfoList *msginfo_list = stuff->msginfo_list;
4141 GRelation *msgflags = stuff->msgflags;
4143 carray * lep_uidtab;
4144 IMAPSession *session;
4147 GHashTable *flags_hash = NULL;
4148 gboolean full_search = stuff->full_search;
4149 GSList *sorted_list = NULL;
4150 GSList *unseen = NULL, *answered = NULL, *flagged = NULL, *deleted = NULL;
4151 GSList *p_unseen, *p_answered, *p_flagged, *p_deleted;
4152 GSList *seq_list, *cur;
4153 gboolean reverse_seen = FALSE;
4154 gboolean selected_folder;
4155 gint exists_cnt, unseen_cnt;
4157 session = imap_session_get(folder);
4158 if (session == NULL) {
4160 return GINT_TO_POINTER(-1);
4162 selected_folder = (session->mbox != NULL) &&
4163 (!strcmp(session->mbox, fitem->path));
4165 if (!selected_folder) {
4166 ok = imap_select(session, IMAP_FOLDER(folder), fitem->path,
4167 &exists_cnt, NULL, &unseen_cnt, NULL, TRUE);
4168 if (ok != IMAP_SUCCESS) {
4170 unlock_session(session);
4171 return GINT_TO_POINTER(-1);
4174 if (unseen_cnt > exists_cnt / 2)
4175 reverse_seen = TRUE;
4178 if (fitem->unread_msgs > fitem->total_msgs / 2)
4179 reverse_seen = TRUE;
4182 sorted_list = g_slist_sort(g_slist_copy(msginfo_list), compare_msginfo);
4184 seq_list = imap_get_lep_set_from_msglist(msginfo_list);
4186 struct mailimap_set * set;
4187 set = mailimap_set_new_interval(1, 0);
4188 seq_list = g_slist_append(NULL, set);
4191 if (folder->account && folder->account->low_bandwidth) {
4192 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
4193 struct mailimap_set * imapset;
4194 clist * lep_uidlist;
4197 imapset = cur->data;
4199 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SEEN,
4200 full_search ? NULL:imapset, &lep_uidlist);
4203 r = imap_threaded_search(folder,
4204 IMAP_SEARCH_TYPE_UNSEEN,
4205 full_search ? NULL:imapset, &lep_uidlist);
4207 if (r == MAILIMAP_NO_ERROR) {
4210 uidlist = imap_uid_list_from_lep(lep_uidlist);
4211 mailimap_search_result_free(lep_uidlist);
4213 unseen = g_slist_concat(unseen, uidlist);
4216 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_FLAGGED,
4217 full_search ? NULL:imapset, &lep_uidlist);
4218 if (r == MAILIMAP_NO_ERROR) {
4221 uidlist = imap_uid_list_from_lep(lep_uidlist);
4222 mailimap_search_result_free(lep_uidlist);
4224 flagged = g_slist_concat(flagged, uidlist);
4227 if (fitem->opened || fitem->processing_pending || fitem == folder->inbox) {
4228 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_ANSWERED,
4229 full_search ? NULL:imapset, &lep_uidlist);
4230 if (r == MAILIMAP_NO_ERROR) {
4233 uidlist = imap_uid_list_from_lep(lep_uidlist);
4234 mailimap_search_result_free(lep_uidlist);
4236 answered = g_slist_concat(answered, uidlist);
4239 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_DELETED,
4240 full_search ? NULL:imapset, &lep_uidlist);
4241 if (r == MAILIMAP_NO_ERROR) {
4244 uidlist = imap_uid_list_from_lep(lep_uidlist);
4245 mailimap_search_result_free(lep_uidlist);
4247 deleted = g_slist_concat(deleted, uidlist);
4252 p_answered = answered;
4253 p_flagged = flagged;
4254 p_deleted = deleted;
4257 r = imap_threaded_fetch_uid_flags(folder, 1, &lep_uidtab);
4258 if (r == MAILIMAP_NO_ERROR) {
4259 flags_hash = g_hash_table_new_full(g_int_hash, g_int_equal, free, NULL);
4260 imap_flags_hash_from_lep_uid_flags_tab(lep_uidtab, flags_hash);
4261 imap_fetch_uid_flags_list_free(lep_uidtab);
4264 for (elem = sorted_list; elem != NULL; elem = g_slist_next(elem)) {
4266 MsgPermFlags flags, oldflags;
4269 msginfo = (MsgInfo *) elem->data;
4270 flags = msginfo->flags.perm_flags;
4271 wasnew = (flags & MSG_NEW);
4272 oldflags = flags & ~(MSG_NEW|MSG_UNREAD|MSG_REPLIED|MSG_MARKED|MSG_DELETED);
4274 if (folder->account && folder->account->low_bandwidth) {
4275 if (fitem->opened || fitem->processing_pending || fitem == folder->inbox) {
4276 flags &= ~((reverse_seen ? 0 : MSG_UNREAD | MSG_NEW) | MSG_REPLIED | MSG_MARKED);
4278 flags &= ~((reverse_seen ? 0 : MSG_UNREAD | MSG_NEW | MSG_MARKED));
4281 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
4282 if (gslist_find_next_num(&p_unseen, msginfo->msgnum) == msginfo->msgnum) {
4283 if (!reverse_seen) {
4284 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
4286 flags &= ~(MSG_UNREAD | MSG_NEW);
4290 if (gslist_find_next_num(&p_flagged, msginfo->msgnum) == msginfo->msgnum)
4291 flags |= MSG_MARKED;
4293 flags &= ~MSG_MARKED;
4295 if (fitem->opened || fitem->processing_pending || fitem == folder->inbox) {
4296 if (gslist_find_next_num(&p_answered, msginfo->msgnum) == msginfo->msgnum)
4297 flags |= MSG_REPLIED;
4299 flags &= ~MSG_REPLIED;
4300 if (gslist_find_next_num(&p_deleted, msginfo->msgnum) == msginfo->msgnum)
4301 flags |= MSG_DELETED;
4303 flags &= ~MSG_DELETED;
4306 if (flags_hash != NULL) {
4309 puid = malloc(sizeof(* puid));
4310 * puid = msginfo->msgnum;
4312 flags = GPOINTER_TO_INT(g_hash_table_lookup(flags_hash, puid));
4316 if ((flags & MSG_UNREAD) == 0)
4323 g_relation_insert(msgflags, msginfo, GINT_TO_POINTER(flags));
4327 g_hash_table_destroy(flags_hash);
4329 imap_lep_set_free(seq_list);
4330 g_slist_free(flagged);
4331 g_slist_free(deleted);
4332 g_slist_free(answered);
4333 g_slist_free(unseen);
4334 g_slist_free(sorted_list);
4336 unlock_session(session);
4338 return GINT_TO_POINTER(0);
4341 static gint imap_get_flags(Folder *folder, FolderItem *item,
4342 MsgInfoList *msginfo_list, GRelation *msgflags)
4345 get_flags_data *data = g_new0(get_flags_data, 1);
4347 data->folder = folder;
4349 data->msginfo_list = msginfo_list;
4350 data->msgflags = msgflags;
4351 data->full_search = FALSE;
4353 GSList *tmp = NULL, *cur;
4355 if (prefs_common.work_offline &&
4356 !inc_offline_should_override(FALSE,
4357 _("Claws Mail needs network access in order "
4358 "to access the IMAP server."))) {
4363 tmp = folder_item_get_msg_list(item);
4365 if (g_slist_length(tmp) <= g_slist_length(msginfo_list))
4366 data->full_search = TRUE;
4368 for (cur = tmp; cur; cur = cur->next)
4369 procmsg_msginfo_free((MsgInfo *)cur->data);
4373 result = GPOINTER_TO_INT(imap_get_flags_thread(data));
4380 static gboolean process_flags(gpointer key, gpointer value, gpointer user_data)
4382 gboolean flags_set = GPOINTER_TO_INT(user_data);
4383 gint flags_value = GPOINTER_TO_INT(key);
4384 hashtable_data *data = (hashtable_data *)value;
4385 IMAPFolderItem *_item = data->item;
4386 FolderItem *item = (FolderItem *)_item;
4387 gint ok = IMAP_ERROR;
4388 IMAPSession *session = NULL;
4390 debug_print("getting session...\n");
4391 session = imap_session_get(item->folder);
4393 data->msglist = g_slist_reverse(data->msglist);
4395 debug_print("IMAP %ssetting flags to %d for %d messages\n",
4398 g_slist_length(data->msglist));
4401 ok = imap_select(session, IMAP_FOLDER(item->folder), item->path,
4402 NULL, NULL, NULL, NULL, FALSE);
4404 if (ok == IMAP_SUCCESS) {
4405 imap_set_message_flags(data->session, data->msglist, flags_value, flags_set);
4407 g_warning("can't select mailbox %s\n", item->path);
4410 unlock_session(session);
4411 g_slist_free(data->msglist);
4416 static void process_hashtable(IMAPFolderItem *item)
4418 if (item->flags_set_table) {
4419 g_hash_table_foreach_remove(item->flags_set_table, process_flags, GINT_TO_POINTER(TRUE));
4420 g_hash_table_destroy(item->flags_set_table);
4421 item->flags_set_table = NULL;
4423 if (item->flags_unset_table) {
4424 g_hash_table_foreach_remove(item->flags_unset_table, process_flags, GINT_TO_POINTER(FALSE));
4425 g_hash_table_destroy(item->flags_unset_table);
4426 item->flags_unset_table = NULL;
4430 static void imap_set_batch (Folder *folder, FolderItem *_item, gboolean batch)
4432 IMAPFolderItem *item = (IMAPFolderItem *)_item;
4434 g_return_if_fail(item != NULL);
4436 if (item->batching == batch)
4440 item->batching = TRUE;
4441 debug_print("IMAP switching to batch mode\n");
4442 if (!item->flags_set_table) {
4443 item->flags_set_table = g_hash_table_new(NULL, g_direct_equal);
4445 if (!item->flags_unset_table) {
4446 item->flags_unset_table = g_hash_table_new(NULL, g_direct_equal);
4449 debug_print("IMAP switching away from batch mode\n");
4451 process_hashtable(item);
4452 item->batching = FALSE;
4458 /* data types conversion libetpan <-> claws */
4462 #define ETPAN_IMAP_MB_MARKED 1
4463 #define ETPAN_IMAP_MB_UNMARKED 2
4464 #define ETPAN_IMAP_MB_NOSELECT 4
4465 #define ETPAN_IMAP_MB_NOINFERIORS 8
4467 static int imap_flags_to_flags(struct mailimap_mbx_list_flags * imap_flags)
4473 if (imap_flags->mbf_type == MAILIMAP_MBX_LIST_FLAGS_SFLAG) {
4474 switch (imap_flags->mbf_sflag) {
4475 case MAILIMAP_MBX_LIST_SFLAG_MARKED:
4476 flags |= ETPAN_IMAP_MB_MARKED;
4478 case MAILIMAP_MBX_LIST_SFLAG_NOSELECT:
4479 flags |= ETPAN_IMAP_MB_NOSELECT;
4481 case MAILIMAP_MBX_LIST_SFLAG_UNMARKED:
4482 flags |= ETPAN_IMAP_MB_UNMARKED;
4487 for(cur = clist_begin(imap_flags->mbf_oflags) ; cur != NULL ;
4488 cur = clist_next(cur)) {
4489 struct mailimap_mbx_list_oflag * oflag;
4491 oflag = clist_content(cur);
4493 switch (oflag->of_type) {
4494 case MAILIMAP_MBX_LIST_OFLAG_NOINFERIORS:
4495 flags |= ETPAN_IMAP_MB_NOINFERIORS;
4503 static GSList * imap_list_from_lep(IMAPFolder * folder,
4504 clist * list, const gchar * real_path, gboolean all)
4507 GSList * item_list = NULL, *llast = NULL;
4509 for(iter = clist_begin(list) ; iter != NULL ;
4510 iter = clist_next(iter)) {
4511 struct mailimap_mailbox_list * mb;
4519 FolderItem *new_item;
4521 mb = clist_content(iter);
4527 if (mb->mb_flag != NULL)
4528 flags = imap_flags_to_flags(mb->mb_flag);
4530 delimiter = mb->mb_delimiter;
4533 dup_name = strdup(name);
4534 if (delimiter != '\0')
4535 subst_char(dup_name, delimiter, '/');
4537 base = g_path_get_basename(dup_name);
4538 if (base[0] == '.') {
4543 if (!all && path_cmp(name, real_path) == 0) {
4549 if (!all && dup_name[strlen(dup_name)-1] == '/') {
4550 dup_name[strlen(dup_name)-1] = '\0';
4553 loc_name = imap_modified_utf7_to_utf8(base);
4554 loc_path = imap_modified_utf7_to_utf8(dup_name);
4556 new_item = folder_item_new(FOLDER(folder), loc_name, loc_path);
4557 if ((flags & ETPAN_IMAP_MB_NOINFERIORS) != 0)
4558 new_item->no_sub = TRUE;
4559 if (strcmp(dup_name, "INBOX") != 0 &&
4560 ((flags & ETPAN_IMAP_MB_NOSELECT) != 0))
4561 new_item->no_select = TRUE;
4563 if (item_list == NULL)
4564 llast = item_list = g_slist_append(item_list, new_item);
4566 llast = g_slist_append(llast, new_item);
4567 llast = llast->next;
4569 debug_print("folder '%s' found.\n", loc_path);
4580 static GSList * imap_get_lep_set_from_numlist(MsgNumberList *numlist)
4582 GSList *sorted_list, *cur;
4583 guint first, last, next;
4584 GSList *ret_list = NULL, *llast = NULL;
4586 struct mailimap_set * current_set;
4587 unsigned int item_count;
4589 if (numlist == NULL)
4593 current_set = mailimap_set_new_empty();
4595 sorted_list = g_slist_copy(numlist);
4596 sorted_list = g_slist_sort(sorted_list, g_int_compare);
4598 first = GPOINTER_TO_INT(sorted_list->data);
4601 for (cur = sorted_list; cur != NULL; cur = g_slist_next(cur)) {
4602 if (GPOINTER_TO_INT(cur->data) == 0)
4607 last = GPOINTER_TO_INT(cur->data);
4609 next = GPOINTER_TO_INT(cur->next->data);
4613 if (last + 1 != next || next == 0) {
4615 struct mailimap_set_item * item;
4616 item = mailimap_set_item_new(first, last);
4617 mailimap_set_add(current_set, item);
4622 if (count >= IMAP_SET_MAX_COUNT) {
4623 if (ret_list == NULL)
4624 llast = ret_list = g_slist_append(ret_list,
4627 llast = g_slist_append(llast, current_set);
4628 llast = llast->next;
4630 current_set = mailimap_set_new_empty();
4637 if (clist_count(current_set->set_list) > 0) {
4638 ret_list = g_slist_append(ret_list,
4642 g_slist_free(sorted_list);
4647 static GSList * imap_get_lep_set_from_msglist(MsgInfoList *msglist)
4649 MsgNumberList *numlist = NULL;
4653 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
4654 MsgInfo *msginfo = (MsgInfo *) cur->data;
4656 numlist = g_slist_prepend(numlist, GINT_TO_POINTER(msginfo->msgnum));
4658 numlist = g_slist_reverse(numlist);
4659 seq_list = imap_get_lep_set_from_numlist(numlist);
4660 g_slist_free(numlist);
4665 static GSList * imap_uid_list_from_lep(clist * list)
4672 for(iter = clist_begin(list) ; iter != NULL ;
4673 iter = clist_next(iter)) {
4676 puid = clist_content(iter);
4677 result = g_slist_prepend(result, GINT_TO_POINTER(* puid));
4680 result = g_slist_reverse(result);
4684 static GSList * imap_uid_list_from_lep_tab(carray * list)
4691 for(i = 0 ; i < carray_count(list) ; i ++) {
4694 puid = carray_get(list, i);
4695 result = g_slist_prepend(result, GINT_TO_POINTER(* puid));
4697 result = g_slist_reverse(result);
4701 static void imap_flags_hash_from_lep_uid_flags_tab(carray * list,
4709 for(i = 0 ; i < carray_count(list) ; i += 2) {
4714 puid = carray_get(list, i);
4715 pflags = carray_get(list, i + 1);
4716 pguid = malloc(sizeof(* pguid));
4719 g_hash_table_insert(hash, pguid, GINT_TO_POINTER(* pflags));
4723 static MsgInfo *imap_envelope_from_lep(struct imap_fetch_env_info * info,
4726 MsgInfo *msginfo = NULL;
4729 MsgFlags flags = {0, 0};
4731 if (info->headers == NULL)
4734 MSG_SET_TMP_FLAGS(flags, MSG_IMAP);
4735 if (folder_has_parent_of_type(item, F_QUEUE)) {
4736 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
4737 } else if (folder_has_parent_of_type(item, F_DRAFT)) {
4738 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
4740 flags.perm_flags = info->flags;
4744 msginfo = procheader_parse_str(info->headers, flags, FALSE, FALSE);
4747 msginfo->msgnum = uid;
4748 msginfo->size = size;
4754 static void imap_lep_set_free(GSList *seq_list)
4758 for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) {
4759 struct mailimap_set * imapset;
4761 imapset = cur->data;
4762 mailimap_set_free(imapset);
4764 g_slist_free(seq_list);
4767 static struct mailimap_flag_list * imap_flag_to_lep(IMAPFlags flags)
4769 struct mailimap_flag_list * flag_list;
4771 flag_list = mailimap_flag_list_new_empty();
4773 if (IMAP_IS_SEEN(flags))
4774 mailimap_flag_list_add(flag_list,
4775 mailimap_flag_new_seen());
4776 if (IMAP_IS_ANSWERED(flags))
4777 mailimap_flag_list_add(flag_list,
4778 mailimap_flag_new_answered());
4779 if (IMAP_IS_FLAGGED(flags))
4780 mailimap_flag_list_add(flag_list,
4781 mailimap_flag_new_flagged());
4782 if (IMAP_IS_DELETED(flags))
4783 mailimap_flag_list_add(flag_list,
4784 mailimap_flag_new_deleted());
4785 if (IMAP_IS_DRAFT(flags))
4786 mailimap_flag_list_add(flag_list,
4787 mailimap_flag_new_draft());
4792 guint imap_folder_get_refcnt(Folder *folder)
4794 return ((IMAPFolder *)folder)->refcnt;
4797 void imap_folder_ref(Folder *folder)
4799 ((IMAPFolder *)folder)->refcnt++;
4802 void imap_disconnect_all(void)
4805 for (list = account_get_list(); list != NULL; list = list->next) {
4806 PrefsAccount *account = list->data;
4807 if (account->protocol == A_IMAP4) {
4808 RemoteFolder *folder = (RemoteFolder *)account->folder;
4809 if (folder && folder->session) {
4810 IMAPSession *session = (IMAPSession *)folder->session;
4811 imap_threaded_disconnect(FOLDER(folder));
4812 SESSION(session)->state = SESSION_DISCONNECTED;
4813 session_destroy(SESSION(session));
4814 folder->session = NULL;
4820 void imap_folder_unref(Folder *folder)
4822 if (((IMAPFolder *)folder)->refcnt > 0)
4823 ((IMAPFolder *)folder)->refcnt--;
4826 void imap_cancel_all(void)
4831 folderlist = folder_get_list();
4832 for (cur = folderlist; cur != NULL; cur = g_list_next(cur)) {
4833 Folder *folder = (Folder *) cur->data;
4835 if (folder->klass == &imap_class) {
4836 if (imap_is_busy(folder)) {
4837 IMAPSession *imap_session;
4838 RemoteFolder *rfolder;
4840 fprintf(stderr, "cancelled\n");
4841 imap_threaded_cancel(folder);
4842 rfolder = (RemoteFolder *) folder;
4843 imap_session = (IMAPSession *) rfolder->session;
4845 imap_session->cancelled = 1;
4851 gboolean imap_cancel_all_enabled(void)
4856 folderlist = folder_get_list();
4857 for (cur = folderlist; cur != NULL; cur = g_list_next(cur)) {
4858 Folder *folder = (Folder *) cur->data;
4860 if (folder->klass == &imap_class) {
4861 if (imap_is_busy(folder)) {
4870 static gboolean imap_is_busy(Folder *folder)
4872 IMAPSession *imap_session;
4873 RemoteFolder *rfolder;
4875 rfolder = (RemoteFolder *) folder;
4876 imap_session = (IMAPSession *) rfolder->session;
4877 if (imap_session == NULL)
4880 return imap_session->busy;
4883 #else /* HAVE_LIBETPAN */
4885 static FolderClass imap_class;
4887 static XMLTag *imap_item_get_xml(Folder *folder, FolderItem *item);
4888 static void imap_item_set_xml(Folder *folder, FolderItem *item, XMLTag *tag);
4890 static Folder *imap_folder_new (const gchar *name,
4893 static gboolean missing_imap_warning = TRUE;
4894 if (missing_imap_warning) {
4895 missing_imap_warning = FALSE;
4897 _("You have one or more IMAP accounts "
4898 "defined. However this version of "
4899 "Claws Mail has been built without "
4900 "IMAP support; your IMAP account(s) are "
4902 "You probably need to "
4903 "install libetpan and recompile "
4908 static gint imap_create_tree (Folder *folder)
4912 static FolderItem *imap_create_folder (Folder *folder,
4918 static gint imap_rename_folder (Folder *folder,
4925 gchar imap_get_path_separator_for_item(FolderItem *item)
4930 FolderClass *imap_get_class(void)
4932 if (imap_class.idstr == NULL) {
4933 imap_class.type = F_IMAP;
4934 imap_class.idstr = "imap";
4935 imap_class.uistr = "IMAP4";
4937 imap_class.new_folder = imap_folder_new;
4938 imap_class.create_tree = imap_create_tree;
4939 imap_class.create_folder = imap_create_folder;
4940 imap_class.rename_folder = imap_rename_folder;
4942 imap_class.set_xml = folder_set_xml;
4943 imap_class.get_xml = folder_get_xml;
4944 imap_class.item_set_xml = imap_item_set_xml;
4945 imap_class.item_get_xml = imap_item_get_xml;
4946 /* nothing implemented */
4952 void imap_disconnect_all(void)
4956 gint imap_scan_tree_real(Folder *folder, gboolean subs_only)
4961 gint imap_subscribe(Folder *folder, FolderItem *item, gchar *rpath, gboolean sub)
4966 GList * imap_scan_subtree(Folder *folder, FolderItem *item, gboolean unsubs_only, gboolean recursive)
4971 void imap_cache_msg(FolderItem *item, gint msgnum)
4975 void imap_cancel_all(void)
4979 gboolean imap_cancel_all_enabled(void)
4986 void imap_synchronise(FolderItem *item, gint days)
4988 #ifdef HAVE_LIBETPAN
4989 if (IMAP_FOLDER_ITEM(item)->last_sync == IMAP_FOLDER_ITEM(item)->last_change) {
4990 debug_print("%s already synced\n", item->path?item->path:item->name);
4993 debug_print("syncing %s\n", item->path?item->path:item->name);
4994 imap_gtk_synchronise(item, days);
4995 IMAP_FOLDER_ITEM(item)->last_sync = IMAP_FOLDER_ITEM(item)->last_change;
4999 static void imap_item_set_xml(Folder *folder, FolderItem *item, XMLTag *tag)
5001 #ifdef HAVE_LIBETPAN
5004 folder_item_set_xml(folder, item, tag);
5006 #ifdef HAVE_LIBETPAN
5007 for (cur = tag->attr; cur != NULL; cur = g_list_next(cur)) {
5008 XMLAttr *attr = (XMLAttr *) cur->data;
5010 if (!attr || !attr->name || !attr->value) continue;
5011 if (!strcmp(attr->name, "uidnext"))
5012 IMAP_FOLDER_ITEM(item)->uid_next = atoi(attr->value);
5013 if (!strcmp(attr->name, "last_sync"))
5014 IMAP_FOLDER_ITEM(item)->last_sync = atoi(attr->value);
5015 if (!strcmp(attr->name, "last_change"))
5016 IMAP_FOLDER_ITEM(item)->last_change = atoi(attr->value);
5018 if (IMAP_FOLDER_ITEM(item)->last_change == 0)
5019 IMAP_FOLDER_ITEM(item)->last_change = time(NULL);
5023 static XMLTag *imap_item_get_xml(Folder *folder, FolderItem *item)
5027 tag = folder_item_get_xml(folder, item);
5029 #ifdef HAVE_LIBETPAN
5030 xml_tag_add_attr(tag, xml_attr_new_int("uidnext",
5031 IMAP_FOLDER_ITEM(item)->uid_next));
5032 xml_tag_add_attr(tag, xml_attr_new_int("last_sync",
5033 IMAP_FOLDER_ITEM(item)->last_sync));
5034 xml_tag_add_attr(tag, xml_attr_new_int("last_change",
5035 IMAP_FOLDER_ITEM(item)->last_change));