2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2007 Hiroyuki Yamamoto and the Claws Mail team
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
27 #include <glib/gi18n.h>
34 #include "alertpanel.h"
57 #include "procheader.h"
58 #include "prefs_account.h"
63 #include "prefs_common.h"
64 #include "inputdialog.h"
66 #include "remotefolder.h"
68 #include "statusbar.h"
70 #include "imap-thread.h"
73 typedef struct _IMAPFolder IMAPFolder;
74 typedef struct _IMAPSession IMAPSession;
75 typedef struct _IMAPNameSpace IMAPNameSpace;
76 typedef struct _IMAPFolderItem IMAPFolderItem;
78 #include "prefs_account.h"
80 #define IMAP_FOLDER(obj) ((IMAPFolder *)obj)
81 #define IMAP_FOLDER_ITEM(obj) ((IMAPFolderItem *)obj)
82 #define IMAP_SESSION(obj) ((IMAPSession *)obj)
88 /* list of IMAPNameSpace */
92 gchar last_seen_separator;
100 gboolean authenticated;
109 gboolean folder_content_changed;
116 struct _IMAPNameSpace
122 #define IMAP_SUCCESS 0
123 #define IMAP_SOCKET 2
124 #define IMAP_AUTHFAIL 3
125 #define IMAP_PROTOCOL 4
126 #define IMAP_SYNTAX 5
130 #define IMAPBUFSIZE 8192
134 IMAP_FLAG_SEEN = 1 << 0,
135 IMAP_FLAG_ANSWERED = 1 << 1,
136 IMAP_FLAG_FLAGGED = 1 << 2,
137 IMAP_FLAG_DELETED = 1 << 3,
138 IMAP_FLAG_DRAFT = 1 << 4
141 #define IMAP_IS_SEEN(flags) ((flags & IMAP_FLAG_SEEN) != 0)
142 #define IMAP_IS_ANSWERED(flags) ((flags & IMAP_FLAG_ANSWERED) != 0)
143 #define IMAP_IS_FLAGGED(flags) ((flags & IMAP_FLAG_FLAGGED) != 0)
144 #define IMAP_IS_DELETED(flags) ((flags & IMAP_FLAG_DELETED) != 0)
145 #define IMAP_IS_DRAFT(flags) ((flags & IMAP_FLAG_DRAFT) != 0)
148 #define IMAP4_PORT 143
150 #define IMAPS_PORT 993
153 #define IMAP_CMD_LIMIT 1000
155 struct _IMAPFolderItem
167 guint32 c_uid_validity;
170 GHashTable *flags_set_table;
171 GHashTable *flags_unset_table;
176 static XMLTag *imap_item_get_xml(Folder *folder, FolderItem *item);
177 static void imap_item_set_xml(Folder *folder, FolderItem *item, XMLTag *tag);
179 static void imap_folder_init (Folder *folder,
183 static Folder *imap_folder_new (const gchar *name,
185 static void imap_folder_destroy (Folder *folder);
187 static IMAPSession *imap_session_new (Folder *folder,
188 const PrefsAccount *account);
189 static void imap_session_authenticate(IMAPSession *session,
190 const PrefsAccount *account);
191 static void imap_session_destroy (Session *session);
193 static gchar *imap_fetch_msg (Folder *folder,
196 static gchar *imap_fetch_msg_full (Folder *folder,
201 static gint imap_add_msg (Folder *folder,
205 static gint imap_add_msgs (Folder *folder,
208 GRelation *relation);
210 static gint imap_copy_msg (Folder *folder,
213 static gint imap_copy_msgs (Folder *folder,
215 MsgInfoList *msglist,
216 GRelation *relation);
218 static gint imap_remove_msg (Folder *folder,
221 static gint imap_remove_msgs (Folder *folder,
223 MsgInfoList *msglist,
224 GRelation *relation);
225 static gint imap_remove_all_msg (Folder *folder,
228 static gboolean imap_is_msg_changed (Folder *folder,
232 static gint imap_close (Folder *folder,
235 static gint imap_scan_tree (Folder *folder);
237 static gint imap_create_tree (Folder *folder);
239 static FolderItem *imap_create_folder (Folder *folder,
242 static gint imap_rename_folder (Folder *folder,
245 static gint imap_remove_folder (Folder *folder,
248 static FolderItem *imap_folder_item_new (Folder *folder);
249 static void imap_folder_item_destroy (Folder *folder,
252 static IMAPSession *imap_session_get (Folder *folder);
254 static gint imap_auth (IMAPSession *session,
259 static gint imap_scan_tree_recursive (IMAPSession *session,
263 static void imap_create_missing_folders (Folder *folder);
264 static FolderItem *imap_create_special_folder
266 SpecialFolderItemType stype,
269 static gint imap_do_copy_msgs (Folder *folder,
271 MsgInfoList *msglist,
272 GRelation *relation);
274 static void imap_delete_all_cached_messages (FolderItem *item);
275 static void imap_set_batch (Folder *folder,
278 static gint imap_set_message_flags (IMAPSession *session,
279 MsgNumberList *numlist,
282 static gint imap_select (IMAPSession *session,
288 guint32 *uid_validity,
290 static gint imap_status (IMAPSession *session,
293 IMAPFolderItem *item,
296 guint32 *uid_validity,
300 static gchar imap_get_path_separator (IMAPSession *session,
303 static gchar *imap_get_real_path (IMAPSession *session,
306 static void imap_synchronise (FolderItem *item);
307 static gboolean imap_is_busy (Folder *folder);
309 static void imap_free_capabilities (IMAPSession *session);
311 /* low-level IMAP4rev1 commands */
312 static gint imap_cmd_login (IMAPSession *session,
316 static gint imap_cmd_noop (IMAPSession *session);
318 static gint imap_cmd_starttls (IMAPSession *session);
320 static gint imap_cmd_select (IMAPSession *session,
325 guint32 *uid_validity,
327 static gint imap_cmd_examine (IMAPSession *session,
332 guint32 *uid_validity,
334 static gint imap_cmd_create (IMAPSession *sock,
335 const gchar *folder);
336 static gint imap_cmd_rename (IMAPSession *sock,
337 const gchar *oldfolder,
338 const gchar *newfolder);
339 static gint imap_cmd_delete (IMAPSession *session,
340 const gchar *folder);
341 static gint imap_cmd_fetch (IMAPSession *sock,
343 const gchar *filename,
346 static gint imap_cmd_append (IMAPSession *session,
347 const gchar *destfolder,
351 static gint imap_cmd_copy (IMAPSession *session,
352 struct mailimap_set * set,
353 const gchar *destfolder,
354 GRelation *uid_mapping,
355 struct mailimap_set ** source,
356 struct mailimap_set ** dest);
357 static gint imap_cmd_store (IMAPSession *session,
358 struct mailimap_set * set,
361 static gint imap_cmd_expunge (IMAPSession *session);
363 static void imap_path_separator_subst (gchar *str,
366 static gchar *imap_utf8_to_modified_utf7 (const gchar *from);
367 static gchar *imap_modified_utf7_to_utf8 (const gchar *mutf7_str);
369 static gboolean imap_rename_folder_func (GNode *node,
371 static gint imap_get_num_list (Folder *folder,
374 gboolean *old_uids_valid);
375 static GSList *imap_get_msginfos (Folder *folder,
377 GSList *msgnum_list);
378 static MsgInfo *imap_get_msginfo (Folder *folder,
381 static gboolean imap_scan_required (Folder *folder,
383 static void imap_change_flags (Folder *folder,
386 MsgPermFlags newflags);
387 static gint imap_get_flags (Folder *folder,
389 MsgInfoList *msglist,
390 GRelation *msgflags);
391 static gchar *imap_folder_get_path (Folder *folder);
392 static gchar *imap_item_get_path (Folder *folder,
394 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item);
397 /* data types conversion libetpan <-> claws */
398 static GSList * imap_list_from_lep(IMAPFolder * folder,
399 clist * list, const gchar * real_path, gboolean all);
400 static GSList * imap_get_lep_set_from_numlist(MsgNumberList *numlist);
401 static GSList * imap_get_lep_set_from_msglist(MsgInfoList *msglist);
402 static GSList * imap_uid_list_from_lep(clist * list);
403 static GSList * imap_uid_list_from_lep_tab(carray * list);
404 static void imap_flags_hash_from_lep_uid_flags_tab(carray * list,
406 static MsgInfo *imap_envelope_from_lep(struct imap_fetch_env_info * info,
408 static void imap_lep_set_free(GSList *seq_list);
409 static struct mailimap_flag_list * imap_flag_to_lep(IMAPFlags flags);
411 typedef struct _hashtable_data {
412 IMAPSession *session;
414 IMAPFolderItem *item;
417 static FolderClass imap_class;
419 typedef struct _thread_data {
429 FolderClass *imap_get_class(void)
431 if (imap_class.idstr == NULL) {
432 imap_class.type = F_IMAP;
433 imap_class.idstr = "imap";
434 imap_class.uistr = "IMAP4";
436 /* Folder functions */
437 imap_class.new_folder = imap_folder_new;
438 imap_class.destroy_folder = imap_folder_destroy;
439 imap_class.scan_tree = imap_scan_tree;
440 imap_class.create_tree = imap_create_tree;
442 /* FolderItem functions */
443 imap_class.item_new = imap_folder_item_new;
444 imap_class.item_destroy = imap_folder_item_destroy;
445 imap_class.item_get_path = imap_item_get_path;
446 imap_class.create_folder = imap_create_folder;
447 imap_class.rename_folder = imap_rename_folder;
448 imap_class.remove_folder = imap_remove_folder;
449 imap_class.close = imap_close;
450 imap_class.get_num_list = imap_get_num_list;
451 imap_class.scan_required = imap_scan_required;
452 imap_class.set_xml = folder_set_xml;
453 imap_class.get_xml = folder_get_xml;
454 imap_class.item_set_xml = imap_item_set_xml;
455 imap_class.item_get_xml = imap_item_get_xml;
457 /* Message functions */
458 imap_class.get_msginfo = imap_get_msginfo;
459 imap_class.get_msginfos = imap_get_msginfos;
460 imap_class.fetch_msg = imap_fetch_msg;
461 imap_class.fetch_msg_full = imap_fetch_msg_full;
462 imap_class.add_msg = imap_add_msg;
463 imap_class.add_msgs = imap_add_msgs;
464 imap_class.copy_msg = imap_copy_msg;
465 imap_class.copy_msgs = imap_copy_msgs;
466 imap_class.remove_msg = imap_remove_msg;
467 imap_class.remove_msgs = imap_remove_msgs;
468 imap_class.remove_all_msg = imap_remove_all_msg;
469 imap_class.is_msg_changed = imap_is_msg_changed;
470 imap_class.change_flags = imap_change_flags;
471 imap_class.get_flags = imap_get_flags;
472 imap_class.set_batch = imap_set_batch;
473 imap_class.synchronise = imap_synchronise;
475 pthread_mutex_init(&imap_mutex, NULL);
482 static Folder *imap_folder_new(const gchar *name, const gchar *path)
486 folder = (Folder *)g_new0(IMAPFolder, 1);
487 folder->klass = &imap_class;
488 imap_folder_init(folder, name, path);
493 static void imap_folder_destroy(Folder *folder)
495 while (imap_folder_get_refcnt(folder) > 0)
496 gtk_main_iteration();
498 folder_remote_folder_destroy(REMOTE_FOLDER(folder));
502 static void imap_folder_init(Folder *folder, const gchar *name,
505 folder_remote_folder_init((Folder *)folder, name, path);
508 static FolderItem *imap_folder_item_new(Folder *folder)
510 IMAPFolderItem *item;
512 item = g_new0(IMAPFolderItem, 1);
515 item->uid_list = NULL;
517 return (FolderItem *)item;
520 static void imap_folder_item_destroy(Folder *folder, FolderItem *_item)
522 IMAPFolderItem *item = (IMAPFolderItem *)_item;
524 g_return_if_fail(item != NULL);
525 g_slist_free(item->uid_list);
530 static gboolean imap_reset_uid_lists_func(GNode *node, gpointer data)
532 IMAPFolderItem *item = (IMAPFolderItem *)node->data;
535 g_slist_free(item->uid_list);
536 item->uid_list = NULL;
541 static void imap_reset_uid_lists(Folder *folder)
543 if(folder->node == NULL)
546 /* Destroy all uid lists and rest last uid */
547 g_node_traverse(folder->node, G_IN_ORDER, G_TRAVERSE_ALL, -1, imap_reset_uid_lists_func, NULL);
550 static int imap_get_capabilities(IMAPSession *session)
552 struct mailimap_capability_data *capabilities = NULL;
556 if (session->capability != NULL)
557 return MAILIMAP_NO_ERROR;
559 capabilities = imap_threaded_capability(session->folder, &result);
561 if (result != MAILIMAP_NO_ERROR) {
562 return MAILIMAP_ERROR_CAPABILITY;
565 if (capabilities == NULL) {
566 return MAILIMAP_NO_ERROR;
569 for(cur = clist_begin(capabilities->cap_list) ; cur != NULL ;
570 cur = clist_next(cur)) {
571 struct mailimap_capability * cap =
573 if (!cap || cap->cap_data.cap_name == NULL)
575 session->capability = g_slist_append
576 (session->capability,
577 g_strdup(cap->cap_data.cap_name));
578 debug_print("got capa %s\n", cap->cap_data.cap_name);
580 mailimap_capability_data_free(capabilities);
581 return MAILIMAP_NO_ERROR;
584 static gboolean imap_has_capability(IMAPSession *session, const gchar *cap)
587 for (cur = session->capability; cur; cur = cur->next) {
588 if (!g_ascii_strcasecmp(cur->data, cap))
594 static gint imap_auth(IMAPSession *session, const gchar *user, const gchar *pass,
597 gint ok = IMAP_ERROR;
598 static time_t last_login_err = 0;
599 gchar *ext_info = "";
601 if (imap_get_capabilities(session) != MAILIMAP_NO_ERROR)
606 ok = imap_cmd_login(session, user, pass, "ANONYMOUS");
608 case IMAP_AUTH_CRAM_MD5:
609 ok = imap_cmd_login(session, user, pass, "CRAM-MD5");
611 case IMAP_AUTH_LOGIN:
612 ok = imap_cmd_login(session, user, pass, "LOGIN");
614 case IMAP_AUTH_GSSAPI:
615 ok = imap_cmd_login(session, user, pass, "GSSAPI");
618 debug_print("capabilities:\n"
623 imap_has_capability(session, "ANONYMOUS"),
624 imap_has_capability(session, "CRAM-MD5"),
625 imap_has_capability(session, "LOGIN"),
626 imap_has_capability(session, "GSSAPI"));
627 if (imap_has_capability(session, "CRAM-MD5"))
628 ok = imap_cmd_login(session, user, pass, "CRAM-MD5");
629 if (ok == IMAP_ERROR && imap_has_capability(session, "GSSAPI"))
630 ok = imap_cmd_login(session, user, pass, "GSSAPI");
631 if (ok == IMAP_ERROR) /* we always try LOGIN before giving up */
632 ok = imap_cmd_login(session, user, pass, "LOGIN");
635 if (ok == IMAP_SUCCESS)
636 session->authenticated = TRUE;
638 if (type == IMAP_AUTH_CRAM_MD5) {
639 ext_info = _("\n\nCRAM-MD5 logins only work if libetpan has been "
640 "compiled with SASL support and the "
641 "CRAM-MD5 SASL plugin is installed.");
644 if (time(NULL) - last_login_err > 10) {
645 if (!prefs_common.no_recv_err_panel) {
646 alertpanel_error(_("Connection to %s failed: "
648 SESSION(session)->server, ext_info);
650 log_error(LOG_PROTOCOL, _("Connection to %s failed: "
651 "login refused.%s\n"),
652 SESSION(session)->server, ext_info);
655 last_login_err = time(NULL);
660 static IMAPSession *imap_reconnect_if_possible(Folder *folder, IMAPSession *session)
662 RemoteFolder *rfolder = REMOTE_FOLDER(folder);
663 /* Check if this is the first try to establish a
664 connection, if yes we don't try to reconnect */
665 debug_print("reconnecting\n");
666 if (rfolder->session == NULL) {
667 log_warning(LOG_PROTOCOL, _("Connecting to %s failed"),
668 folder->account->recv_server);
669 session_destroy(SESSION(session));
672 log_warning(LOG_PROTOCOL, _("IMAP4 connection to %s has been"
673 " disconnected. Reconnecting...\n"),
674 folder->account->recv_server);
675 statusbar_print_all(_("IMAP4 connection to %s has been"
676 " disconnected. Reconnecting...\n"),
677 folder->account->recv_server);
678 SESSION(session)->state = SESSION_DISCONNECTED;
679 session_destroy(SESSION(session));
680 /* Clear folders session to make imap_session_get create
681 a new session, because of rfolder->session == NULL
682 it will not try to reconnect again and so avoid an
684 rfolder->session = NULL;
685 debug_print("getting session...\n");
686 session = imap_session_get(folder);
687 rfolder->session = SESSION(session);
693 static void lock_session(IMAPSession *session)
698 debug_print("locking session %p (%d)\n", session, session->busy);
700 debug_print(" SESSION WAS LOCKED !! \n");
701 session->busy = TRUE;
702 mainwin = mainwindow_get_mainwindow();
704 toolbar_main_set_sensitive(mainwin);
705 main_window_set_menu_sensitive(mainwin);
708 debug_print("can't lock null session\n");
712 static void unlock_session(IMAPSession *session)
717 debug_print("unlocking session %p\n", session);
718 session->busy = FALSE;
719 mainwin = mainwindow_get_mainwindow();
721 toolbar_main_set_sensitive(mainwin);
722 main_window_set_menu_sensitive(mainwin);
725 debug_print("can't unlock null session\n");
729 static IMAPSession *imap_session_get(Folder *folder)
731 RemoteFolder *rfolder = REMOTE_FOLDER(folder);
732 IMAPSession *session = NULL;
734 g_return_val_if_fail(folder != NULL, NULL);
735 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, NULL);
736 g_return_val_if_fail(folder->account != NULL, NULL);
738 if (prefs_common.work_offline &&
739 !inc_offline_should_override(FALSE,
740 _("Claws Mail needs network access in order "
741 "to access the IMAP server."))) {
745 /* Make sure we have a session */
746 if (rfolder->session != NULL) {
747 session = IMAP_SESSION(rfolder->session);
748 } else if (rfolder->connecting) {
749 debug_print("already connecting\n");
752 imap_reset_uid_lists(folder);
753 if (time(NULL) - rfolder->last_failure <= 2)
755 rfolder->connecting = TRUE;
756 session = imap_session_new(folder, folder->account);
758 if(session == NULL) {
759 rfolder->last_failure = time(NULL);
760 rfolder->connecting = FALSE;
764 /* Make sure session is authenticated */
765 if (!IMAP_SESSION(session)->authenticated)
766 imap_session_authenticate(IMAP_SESSION(session), folder->account);
768 if (!IMAP_SESSION(session)->authenticated) {
769 imap_threaded_disconnect(session->folder);
770 SESSION(session)->state = SESSION_DISCONNECTED;
771 session_destroy(SESSION(session));
772 rfolder->session = NULL;
773 rfolder->last_failure = time(NULL);
774 rfolder->connecting = FALSE;
778 lock_session(session);
780 /* I think the point of this code is to avoid sending a
781 * keepalive if we've used the session recently and therefore
782 * think it's still alive. Unfortunately, most of the code
783 * does not yet check for errors on the socket, and so if the
784 * connection drops we don't notice until the timeout expires.
785 * A better solution than sending a NOOP every time would be
786 * for every command to be prepared to retry until it is
787 * successfully sent. -- mbp */
788 if ((time(NULL) - SESSION(session)->last_access_time > SESSION_TIMEOUT_INTERVAL) || session->cancelled) {
789 /* verify that the session is still alive */
790 if (imap_cmd_noop(session) != IMAP_SUCCESS) {
791 debug_print("disconnected!\n");
792 session = imap_reconnect_if_possible(folder, session);
795 session->cancelled = FALSE;
798 rfolder->session = SESSION(session);
799 rfolder->connecting = FALSE;
801 return IMAP_SESSION(session);
804 static IMAPSession *imap_session_new(Folder * folder,
805 const PrefsAccount *account)
807 IMAPSession *session;
810 int authenticated = FALSE;
813 /* FIXME: IMAP over SSL only... */
816 port = account->set_imapport ? account->imapport
817 : account->ssl_imap == SSL_TUNNEL ? IMAPS_PORT : IMAP4_PORT;
818 ssl_type = account->ssl_imap;
820 if (account->ssl_imap != SSL_NONE) {
821 if (alertpanel_full(_("Insecure connection"),
822 _("This connection is configured to be secured "
823 "using SSL, but SSL is not available in this "
824 "build of Claws Mail. \n\n"
825 "Do you want to continue connecting to this "
826 "server? The communication would not be "
828 GTK_STOCK_CANCEL, _("Con_tinue connecting"),
829 NULL, FALSE, NULL, ALERT_WARNING,
830 G_ALERTDEFAULT) != G_ALERTALTERNATE)
833 port = account->set_imapport ? account->imapport
838 statusbar_print_all(_("Connecting to IMAP4 server: %s..."), folder->account->recv_server);
839 if (account->set_tunnelcmd) {
840 r = imap_threaded_connect_cmd(folder,
842 account->recv_server,
847 if (ssl_type == SSL_TUNNEL) {
848 r = imap_threaded_connect_ssl(folder,
849 account->recv_server,
855 r = imap_threaded_connect(folder,
856 account->recv_server,
862 if (r == MAILIMAP_NO_ERROR_AUTHENTICATED) {
863 authenticated = TRUE;
865 else if (r == MAILIMAP_NO_ERROR_NON_AUTHENTICATED) {
866 authenticated = FALSE;
869 #if (LIBETPAN_VERSION_MAJOR > 0 || LIBETPAN_VERSION_MINOR > 48)
871 if (r == MAILIMAP_ERROR_SSL)
872 log_error(LOG_PROTOCOL, _("SSL handshake failed\n"));
875 if(!prefs_common.no_recv_err_panel) {
876 alertpanel_error_log(_("Can't connect to IMAP4 server: %s:%d"),
877 account->recv_server, port);
879 log_error(LOG_PROTOCOL, _("Can't connect to IMAP4 server: %s:%d\n"),
880 account->recv_server, port);
886 session = g_new0(IMAPSession, 1);
887 session_init(SESSION(session));
888 SESSION(session)->type = SESSION_IMAP;
889 SESSION(session)->server = g_strdup(account->recv_server);
890 SESSION(session)->sock = NULL;
892 SESSION(session)->destroy = imap_session_destroy;
894 session->capability = NULL;
896 session->authenticated = authenticated;
897 session->mbox = NULL;
898 session->cmd_count = 0;
899 session->folder = folder;
900 IMAP_FOLDER(session->folder)->last_seen_separator = 0;
903 if (account->ssl_imap == SSL_STARTTLS) {
906 ok = imap_cmd_starttls(session);
907 if (ok != IMAP_SUCCESS) {
908 log_warning(LOG_PROTOCOL, _("Can't start TLS session.\n"));
909 session_destroy(SESSION(session));
913 imap_free_capabilities(session);
914 session->authenticated = FALSE;
915 session->uidplus = FALSE;
916 session->cmd_count = 1;
919 log_message(LOG_PROTOCOL, "IMAP connection is %s-authenticated\n",
920 (session->authenticated) ? "pre" : "un");
925 static void imap_session_authenticate(IMAPSession *session,
926 const PrefsAccount *account)
928 gchar *pass, *acc_pass;
929 gboolean failed = FALSE;
931 g_return_if_fail(account->userid != NULL);
932 acc_pass = account->passwd;
935 if (!pass && account->imap_auth_type != IMAP_AUTH_ANON) {
937 tmp_pass = input_dialog_query_password(account->recv_server, account->userid);
940 Xstrdup_a(pass, tmp_pass, {g_free(tmp_pass); return;});
942 } else if (account->imap_auth_type == IMAP_AUTH_ANON) {
945 statusbar_print_all(_("Connecting to IMAP4 server %s...\n"),
946 account->recv_server);
947 if (imap_auth(session, account->userid, pass, account->imap_auth_type) != IMAP_SUCCESS) {
955 if (prefs_common.no_recv_err_panel) {
956 log_error(LOG_PROTOCOL, _("Couldn't login to IMAP server %s."), account->recv_server);
957 mainwindow_show_error();
959 alertpanel_error_log(_("Couldn't login to IMAP server %s."), account->recv_server);
966 session->authenticated = TRUE;
970 static void imap_session_destroy(Session *session)
972 if (session->state != SESSION_DISCONNECTED)
973 imap_threaded_disconnect(IMAP_SESSION(session)->folder);
975 imap_free_capabilities(IMAP_SESSION(session));
976 g_free(IMAP_SESSION(session)->mbox);
977 sock_close(session->sock);
978 session->sock = NULL;
981 static gchar *imap_fetch_msg(Folder *folder, FolderItem *item, gint uid)
983 return imap_fetch_msg_full(folder, item, uid, TRUE, TRUE);
986 static guint get_file_size_with_crs(const gchar *filename)
992 if (filename == NULL)
995 fp = fopen(filename, "rb");
999 while (fgets(buf, sizeof (buf), fp) != NULL) {
1001 if (!strstr(buf, "\r\n") && strstr(buf, "\n"))
1009 static gchar *imap_fetch_msg_full(Folder *folder, FolderItem *item, gint uid,
1010 gboolean headers, gboolean body)
1012 gchar *path, *filename;
1013 IMAPSession *session;
1016 g_return_val_if_fail(folder != NULL, NULL);
1017 g_return_val_if_fail(item != NULL, NULL);
1022 path = folder_item_get_path(item);
1023 if (!is_dir_exist(path))
1024 make_dir_hier(path);
1025 filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(uid), NULL);
1027 debug_print("trying to fetch cached %s\n", filename);
1028 if (is_file_exist(filename)) {
1029 /* see whether the local file represents the whole message
1030 * or not. As the IMAP server reports size with \r chars,
1031 * we have to update the local file (UNIX \n only) size */
1032 MsgInfo *cached = msgcache_get_msg(item->cache,uid);
1033 guint have_size = get_file_size_with_crs(filename);
1036 debug_print("message %d has been already %scached (%d/%d).\n", uid,
1037 have_size >= cached->size ? "fully ":"",
1038 have_size, (int)cached->size);
1040 if (cached && (cached->size <= have_size || !body)) {
1041 procmsg_msginfo_free(cached);
1042 file_strip_crs(filename);
1044 } else if (!cached && time(NULL) - get_file_mtime(filename) < 60) {
1045 debug_print("message not cached and file recent, considering file complete\n");
1046 file_strip_crs(filename);
1049 procmsg_msginfo_free(cached);
1053 debug_print("getting session...\n");
1054 session = imap_session_get(folder);
1061 debug_print("IMAP fetching messages\n");
1062 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
1063 NULL, NULL, NULL, NULL, FALSE);
1064 if (ok != IMAP_SUCCESS) {
1065 g_warning("can't select mailbox %s\n", item->path);
1067 unlock_session(session);
1071 debug_print("getting message %d...\n", uid);
1072 ok = imap_cmd_fetch(session, (guint32)uid, filename, headers, body);
1074 if (ok != IMAP_SUCCESS) {
1075 g_warning("can't fetch message %d\n", uid);
1077 unlock_session(session);
1081 unlock_session(session);
1082 file_strip_crs(filename);
1086 static gboolean imap_is_msg_fully_cached(Folder *folder, FolderItem *item, gint uid)
1088 gchar *path, *filename;
1090 MsgInfo *cached = msgcache_get_msg(item->cache,uid);
1095 path = folder_item_get_path(item);
1096 if (!is_dir_exist(path))
1099 filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(uid), NULL);
1101 if (is_file_exist(filename)) {
1102 if (cached && cached->total_size == cached->size) {
1107 size = get_file_size_with_crs(filename);
1110 if (cached && size >= cached->size) {
1111 cached->total_size = cached->size;
1112 procmsg_msginfo_free(cached);
1116 procmsg_msginfo_free(cached);
1120 void imap_cache_msg(FolderItem *item, gint msgnum)
1122 Folder *folder = NULL;
1126 folder = item->folder;
1128 if (!imap_is_msg_fully_cached(folder, item, msgnum)) {
1129 gchar *tmp = imap_fetch_msg_full(folder, item, msgnum, TRUE, TRUE);
1130 debug_print("fetched %s\n", tmp);
1135 static gint imap_add_msg(Folder *folder, FolderItem *dest,
1136 const gchar *file, MsgFlags *flags)
1140 MsgFileInfo fileinfo;
1142 g_return_val_if_fail(file != NULL, -1);
1144 fileinfo.msginfo = NULL;
1145 fileinfo.file = (gchar *)file;
1146 fileinfo.flags = flags;
1147 file_list.data = &fileinfo;
1148 file_list.next = NULL;
1150 ret = imap_add_msgs(folder, dest, &file_list, NULL);
1154 static gint imap_add_msgs(Folder *folder, FolderItem *dest, GSList *file_list,
1155 GRelation *relation)
1158 IMAPSession *session;
1159 guint32 last_uid = 0;
1161 MsgFileInfo *fileinfo;
1163 gint curnum = 0, total = 0;
1166 g_return_val_if_fail(folder != NULL, -1);
1167 g_return_val_if_fail(dest != NULL, -1);
1168 g_return_val_if_fail(file_list != NULL, -1);
1170 debug_print("getting session...\n");
1171 session = imap_session_get(folder);
1175 destdir = imap_get_real_path(session, IMAP_FOLDER(folder), dest->path);
1177 statusbar_print_all(_("Adding messages..."));
1178 total = g_slist_length(file_list);
1179 for (cur = file_list; cur != NULL; cur = cur->next) {
1180 IMAPFlags iflags = 0;
1181 guint32 new_uid = 0;
1182 gchar *real_file = NULL;
1183 fileinfo = (MsgFileInfo *)cur->data;
1185 statusbar_progress_all(curnum, total, total < 10 ? 1:10);
1188 if (fileinfo->flags) {
1189 if (MSG_IS_MARKED(*fileinfo->flags))
1190 iflags |= IMAP_FLAG_FLAGGED;
1191 if (MSG_IS_REPLIED(*fileinfo->flags))
1192 iflags |= IMAP_FLAG_ANSWERED;
1193 if (!MSG_IS_UNREAD(*fileinfo->flags))
1194 iflags |= IMAP_FLAG_SEEN;
1197 if (real_file == NULL)
1198 real_file = g_strdup(fileinfo->file);
1200 if (folder_has_parent_of_type(dest, F_QUEUE) ||
1201 folder_has_parent_of_type(dest, F_OUTBOX) ||
1202 folder_has_parent_of_type(dest, F_DRAFT) ||
1203 folder_has_parent_of_type(dest, F_TRASH))
1204 iflags |= IMAP_FLAG_SEEN;
1206 ok = imap_cmd_append(session, destdir, real_file, iflags,
1209 if (ok != IMAP_SUCCESS) {
1210 g_warning("can't append message %s\n", real_file);
1213 unlock_session(session);
1214 statusbar_progress_all(0,0,0);
1215 statusbar_pop_all();
1218 debug_print("appended new message as %d\n", new_uid);
1219 /* put the local file in the imapcache, so that we don't
1220 * have to fetch it back later. */
1222 gchar *cache_path = folder_item_get_path(dest);
1223 if (!is_dir_exist(cache_path))
1224 make_dir_hier(cache_path);
1225 if (is_dir_exist(cache_path)) {
1226 gchar *cache_file = g_strconcat(
1227 cache_path, G_DIR_SEPARATOR_S,
1228 itos(new_uid), NULL);
1229 copy_file(real_file, cache_file, TRUE);
1230 debug_print("copied to cache: %s\n", cache_file);
1237 if (relation != NULL)
1238 g_relation_insert(relation, fileinfo->msginfo != NULL ?
1239 (gpointer) fileinfo->msginfo : (gpointer) fileinfo,
1240 GINT_TO_POINTER(dest->last_num + 1));
1242 new_uid = dest->last_num+1;
1244 if (last_uid < new_uid) {
1250 statusbar_progress_all(0,0,0);
1251 statusbar_pop_all();
1253 imap_cmd_expunge(session);
1254 unlock_session(session);
1261 static gint imap_do_copy_msgs(Folder *folder, FolderItem *dest,
1262 MsgInfoList *msglist, GRelation *relation)
1266 GSList *seq_list, *cur;
1268 IMAPSession *session;
1269 gint ok = IMAP_SUCCESS;
1270 GRelation *uid_mapping;
1272 gboolean single = FALSE;
1274 g_return_val_if_fail(folder != NULL, -1);
1275 g_return_val_if_fail(dest != NULL, -1);
1276 g_return_val_if_fail(msglist != NULL, -1);
1278 debug_print("getting session...\n");
1279 session = imap_session_get(folder);
1285 msginfo = (MsgInfo *)msglist->data;
1286 if (msglist->next == NULL)
1288 src = msginfo->folder;
1290 g_warning("the src folder is identical to the dest.\n");
1291 unlock_session(session);
1295 if (src->folder != dest->folder) {
1296 GSList *infolist = NULL, *cur;
1298 for (cur = msglist; cur; cur = cur->next) {
1299 msginfo = (MsgInfo *)cur->data;
1300 MsgFileInfo *fileinfo = g_new0(MsgFileInfo, 1);
1301 fileinfo->file = procmsg_get_message_file(msginfo);
1302 fileinfo->flags = &(msginfo->flags);
1303 infolist = g_slist_prepend(infolist, fileinfo);
1305 infolist = g_slist_reverse(infolist);
1306 unlock_session(session);
1307 res = folder_item_add_msgs(dest, infolist, FALSE);
1308 for (cur = infolist; cur; cur = cur->next) {
1309 MsgFileInfo *info = (MsgFileInfo *)cur->data;
1313 g_slist_free(infolist);
1317 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
1318 NULL, NULL, NULL, NULL, FALSE);
1319 if (ok != IMAP_SUCCESS) {
1320 unlock_session(session);
1324 destdir = imap_get_real_path(session, IMAP_FOLDER(folder), dest->path);
1325 seq_list = imap_get_lep_set_from_msglist(msglist);
1326 uid_mapping = g_relation_new(2);
1327 g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
1329 statusbar_print_all(_("Copying messages..."));
1330 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
1331 struct mailimap_set * seq_set;
1332 struct mailimap_set * source = NULL;
1333 struct mailimap_set * dest = NULL;
1334 seq_set = cur->data;
1336 debug_print("Copying messages from %s to %s ...\n",
1337 src->path, destdir);
1339 ok = imap_cmd_copy(session, seq_set, destdir, uid_mapping,
1342 if (ok == IMAP_SUCCESS) {
1343 if (single && relation && source && dest) {
1344 clistiter *l = clist_begin(source->set_list);
1345 struct mailimap_set_item *i = (struct mailimap_set_item *)clist_content(l);
1346 int snum = i->set_first;
1348 l = clist_begin(dest->set_list);
1349 i = (struct mailimap_set_item *)clist_content(l);
1350 dnum = i->set_first;
1351 g_relation_insert(uid_mapping, GINT_TO_POINTER(snum),
1352 GINT_TO_POINTER(dnum));
1358 mailimap_set_free(source);
1360 mailimap_set_free(dest);
1362 if (ok != IMAP_SUCCESS) {
1363 g_relation_destroy(uid_mapping);
1364 imap_lep_set_free(seq_list);
1365 unlock_session(session);
1366 statusbar_pop_all();
1371 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
1372 MsgInfo *msginfo = (MsgInfo *)cur->data;
1375 tuples = g_relation_select(uid_mapping,
1376 GINT_TO_POINTER(msginfo->msgnum),
1378 if (tuples->len > 0) {
1379 gint num = GPOINTER_TO_INT(g_tuples_index(tuples, 0, 1));
1380 g_relation_insert(relation, msginfo,
1381 GINT_TO_POINTER(num));
1384 debug_print("copied new message as %d\n", num);
1385 /* put the local file in the imapcache, so that we don't
1386 * have to fetch it back later. */
1388 gchar *cache_path = folder_item_get_path(msginfo->folder);
1389 gchar *real_file = g_strconcat(
1390 cache_path, G_DIR_SEPARATOR_S,
1391 itos(msginfo->msgnum), NULL);
1392 gchar *cache_file = NULL;
1394 cache_path = folder_item_get_path(dest);
1395 cache_file = g_strconcat(
1396 cache_path, G_DIR_SEPARATOR_S,
1398 if (!is_dir_exist(cache_path))
1399 make_dir_hier(cache_path);
1400 if (is_file_exist(real_file) && is_dir_exist(cache_path)) {
1401 copy_file(real_file, cache_file, TRUE);
1402 debug_print("copied to cache: %s\n", cache_file);
1409 g_relation_insert(relation, msginfo,
1410 GINT_TO_POINTER(0));
1411 g_tuples_destroy(tuples);
1413 statusbar_pop_all();
1415 g_relation_destroy(uid_mapping);
1416 imap_lep_set_free(seq_list);
1420 IMAP_FOLDER_ITEM(dest)->lastuid = 0;
1421 IMAP_FOLDER_ITEM(dest)->uid_next = 0;
1422 g_slist_free(IMAP_FOLDER_ITEM(dest)->uid_list);
1423 IMAP_FOLDER_ITEM(dest)->uid_list = NULL;
1425 unlock_session(session);
1426 if (ok == IMAP_SUCCESS)
1432 static gint imap_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1436 g_return_val_if_fail(msginfo != NULL, -1);
1438 msglist.data = msginfo;
1439 msglist.next = NULL;
1441 return imap_copy_msgs(folder, dest, &msglist, NULL);
1444 static gint imap_copy_msgs(Folder *folder, FolderItem *dest,
1445 MsgInfoList *msglist, GRelation *relation)
1450 g_return_val_if_fail(folder != NULL, -1);
1451 g_return_val_if_fail(dest != NULL, -1);
1452 g_return_val_if_fail(msglist != NULL, -1);
1454 msginfo = (MsgInfo *)msglist->data;
1455 g_return_val_if_fail(msginfo->folder != NULL, -1);
1457 ret = imap_do_copy_msgs(folder, dest, msglist, relation);
1462 static gint imap_do_remove_msgs(Folder *folder, FolderItem *dest,
1463 MsgInfoList *msglist, GRelation *relation)
1465 gchar *destdir, *dir;
1466 GSList *numlist = NULL, *cur;
1468 IMAPSession *session;
1469 gint ok = IMAP_SUCCESS;
1470 GRelation *uid_mapping;
1472 g_return_val_if_fail(folder != NULL, -1);
1473 g_return_val_if_fail(dest != NULL, -1);
1474 g_return_val_if_fail(msglist != NULL, -1);
1476 debug_print("getting session...\n");
1477 session = imap_session_get(folder);
1482 msginfo = (MsgInfo *)msglist->data;
1484 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
1485 NULL, NULL, NULL, NULL, FALSE);
1486 if (ok != IMAP_SUCCESS) {
1487 unlock_session(session);
1491 destdir = imap_get_real_path(session, IMAP_FOLDER(folder), dest->path);
1492 for (cur = msglist; cur; cur = cur->next) {
1493 msginfo = (MsgInfo *)cur->data;
1494 if (!MSG_IS_DELETED(msginfo->flags))
1495 numlist = g_slist_prepend(numlist, GINT_TO_POINTER(msginfo->msgnum));
1497 numlist = g_slist_reverse(numlist);
1499 uid_mapping = g_relation_new(2);
1500 g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
1502 ok = imap_set_message_flags
1503 (session, numlist, IMAP_FLAG_DELETED, TRUE);
1504 if (ok != IMAP_SUCCESS) {
1505 log_warning(LOG_PROTOCOL, _("can't set deleted flags\n"));
1506 unlock_session(session);
1509 ok = imap_cmd_expunge(session);
1510 if (ok != IMAP_SUCCESS) {
1511 log_warning(LOG_PROTOCOL, _("can't expunge\n"));
1512 unlock_session(session);
1516 dir = folder_item_get_path(msginfo->folder);
1517 if (is_dir_exist(dir)) {
1518 for (cur = msglist; cur; cur = cur->next) {
1519 msginfo = (MsgInfo *)cur->data;
1520 remove_numbered_files(dir, msginfo->msgnum, msginfo->msgnum);
1525 g_relation_destroy(uid_mapping);
1526 g_slist_free(numlist);
1529 unlock_session(session);
1530 if (ok == IMAP_SUCCESS)
1536 static gint imap_remove_msgs(Folder *folder, FolderItem *dest,
1537 MsgInfoList *msglist, GRelation *relation)
1541 g_return_val_if_fail(folder != NULL, -1);
1542 g_return_val_if_fail(dest != NULL, -1);
1543 if (msglist == NULL)
1546 msginfo = (MsgInfo *)msglist->data;
1547 g_return_val_if_fail(msginfo->folder != NULL, -1);
1549 return imap_do_remove_msgs(folder, dest, msglist, relation);
1552 static gint imap_remove_all_msg(Folder *folder, FolderItem *item)
1554 GSList *list = folder_item_get_msg_list(item);
1555 gint res = imap_remove_msgs(folder, item, list, NULL);
1556 procmsg_msg_list_free(list);
1560 static gboolean imap_is_msg_changed(Folder *folder, FolderItem *item,
1563 /* TODO: properly implement this method */
1567 static gint imap_close(Folder *folder, FolderItem *item)
1572 gint imap_scan_tree_real(Folder *folder, gboolean subs_only)
1574 FolderItem *item = NULL;
1575 IMAPSession *session;
1576 gchar *root_folder = NULL;
1578 g_return_val_if_fail(folder != NULL, -1);
1579 g_return_val_if_fail(folder->account != NULL, -1);
1581 debug_print("getting session...\n");
1582 session = imap_session_get(folder);
1584 if (!folder->node) {
1585 folder_tree_destroy(folder);
1586 item = folder_item_new(folder, folder->name, NULL);
1587 item->folder = folder;
1588 folder->node = item->node = g_node_new(item);
1593 if (folder->account->imap_dir && *folder->account->imap_dir) {
1598 Xstrdup_a(root_folder, folder->account->imap_dir, {unlock_session(session);return -1;});
1599 extract_quote(root_folder, '"');
1600 subst_char(root_folder,
1601 imap_get_path_separator(session, IMAP_FOLDER(folder),
1604 strtailchomp(root_folder, '/');
1605 real_path = imap_get_real_path
1606 (session, IMAP_FOLDER(folder), root_folder);
1607 debug_print("IMAP root directory: %s\n", real_path);
1609 /* check if root directory exist */
1611 r = imap_threaded_list(session->folder, "", real_path,
1613 if ((r != MAILIMAP_NO_ERROR) || (clist_count(lep_list) == 0)) {
1614 if (!folder->node) {
1615 item = folder_item_new(folder, folder->name, NULL);
1616 item->folder = folder;
1617 folder->node = item->node = g_node_new(item);
1619 unlock_session(session);
1622 mailimap_list_result_free(lep_list);
1628 item = FOLDER_ITEM(folder->node->data);
1630 if (item && !item->path && root_folder) {
1631 item->path = g_strdup(root_folder);
1634 if (!item || ((item->path || root_folder) &&
1635 strcmp2(item->path, root_folder) != 0)) {
1636 folder_tree_destroy(folder);
1637 item = folder_item_new(folder, folder->name, root_folder);
1638 item->folder = folder;
1639 folder->node = item->node = g_node_new(item);
1642 imap_scan_tree_recursive(session, FOLDER_ITEM(folder->node->data), subs_only);
1643 imap_create_missing_folders(folder);
1644 unlock_session(session);
1649 static gint imap_scan_tree(Folder *folder)
1651 gboolean subs_only = FALSE;
1652 if (folder->account) {
1653 debug_print(" scanning only subs %d\n", folder->account->imap_subsonly);
1654 subs_only = folder->account->imap_subsonly;
1656 return imap_scan_tree_real(folder, subs_only);
1659 static gint imap_scan_tree_recursive(IMAPSession *session, FolderItem *item, gboolean subs_only)
1662 IMAPFolder *imapfolder;
1663 FolderItem *new_item;
1664 GSList *item_list, *cur;
1667 gchar *wildcard_path;
1673 g_return_val_if_fail(item != NULL, -1);
1674 g_return_val_if_fail(item->folder != NULL, -1);
1675 g_return_val_if_fail(item->no_sub == FALSE, -1);
1677 folder = item->folder;
1678 imapfolder = IMAP_FOLDER(folder);
1680 separator = imap_get_path_separator(session, imapfolder, item->path);
1682 if (folder->ui_func)
1683 folder->ui_func(folder, item, folder->ui_func_data);
1686 wildcard[0] = separator;
1689 real_path = imap_get_real_path(session, imapfolder, item->path);
1693 real_path = g_strdup("");
1696 Xstrcat_a(wildcard_path, real_path, wildcard,
1697 {g_free(real_path); return IMAP_ERROR;});
1701 r = imap_threaded_lsub(folder, "", wildcard_path, &lep_list);
1703 r = imap_threaded_list(folder, "", wildcard_path, &lep_list);
1705 if (r != MAILIMAP_NO_ERROR) {
1709 item_list = imap_list_from_lep(imapfolder,
1710 lep_list, real_path, FALSE);
1711 mailimap_list_result_free(lep_list);
1716 node = item->node->children;
1717 while (node != NULL) {
1718 FolderItem *old_item = FOLDER_ITEM(node->data);
1719 GNode *next = node->next;
1722 for (cur = item_list; cur != NULL; cur = cur->next) {
1723 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1724 if (!strcmp2(old_item->path, cur_item->path)) {
1725 new_item = cur_item;
1730 if (old_item && old_item->path && !strcmp(old_item->path, "INBOX")) {
1731 debug_print("not removing INBOX\n");
1733 debug_print("folder '%s' not found. removing...\n",
1735 folder_item_remove(old_item);
1738 old_item->no_sub = new_item->no_sub;
1739 old_item->no_select = new_item->no_select;
1740 if (old_item->no_sub == TRUE && node->children) {
1741 debug_print("folder '%s' doesn't have "
1742 "subfolders. removing...\n",
1744 folder_item_remove_children(old_item);
1751 for (cur = item_list; cur != NULL; cur = cur->next) {
1752 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1755 for (node = item->node->children; node != NULL;
1756 node = node->next) {
1757 if (!strcmp2(FOLDER_ITEM(node->data)->path,
1759 new_item = FOLDER_ITEM(node->data);
1760 folder_item_destroy(cur_item);
1766 new_item = cur_item;
1767 debug_print("new folder '%s' found.\n", new_item->path);
1768 folder_item_append(item, new_item);
1771 if (!strcmp(new_item->path, "INBOX")) {
1772 new_item->stype = F_INBOX;
1773 folder->inbox = new_item;
1774 } else if (!folder_item_parent(item) || item->stype == F_INBOX) {
1777 base = g_path_get_basename(new_item->path);
1779 if (!folder->outbox && !g_ascii_strcasecmp(base, "Sent")) {
1780 new_item->stype = F_OUTBOX;
1781 folder->outbox = new_item;
1782 } else if (!folder->draft && !g_ascii_strcasecmp(base, "Drafts")) {
1783 new_item->stype = F_DRAFT;
1784 folder->draft = new_item;
1785 } else if (!folder->queue && !g_ascii_strcasecmp(base, "Queue")) {
1786 new_item->stype = F_QUEUE;
1787 folder->queue = new_item;
1788 } else if (!folder->trash && !g_ascii_strcasecmp(base, "Trash")) {
1789 new_item->stype = F_TRASH;
1790 folder->trash = new_item;
1795 if (new_item->no_sub == FALSE)
1796 imap_scan_tree_recursive(session, new_item, subs_only);
1799 g_slist_free(item_list);
1801 return IMAP_SUCCESS;
1804 GList *imap_scan_subtree(Folder *folder, FolderItem *item, gboolean unsubs_only, gboolean recursive)
1806 IMAPSession *session = imap_session_get(folder);
1808 gchar *wildcard_path;
1812 GSList *item_list = NULL, *cur;
1813 GList *child_list = NULL, *tmplist = NULL;
1814 GSList *sub_list = NULL;
1820 separator = imap_get_path_separator(session, IMAP_FOLDER(folder), item->path);
1823 wildcard[0] = separator;
1826 real_path = imap_get_real_path(session, IMAP_FOLDER(folder), item->path);
1830 real_path = g_strdup("");
1833 Xstrcat_a(wildcard_path, real_path, wildcard,
1834 {g_free(real_path); return NULL;});
1838 statusbar_print_all(_("Looking for unsubscribed folders in %s..."),
1839 item->path?item->path:item->name);
1841 statusbar_print_all(_("Looking for subfolders of %s..."),
1842 item->path?item->path:item->name);
1844 r = imap_threaded_list(folder, "", wildcard_path, &lep_list);
1846 statusbar_pop_all();
1849 item_list = imap_list_from_lep(IMAP_FOLDER(folder),
1850 lep_list, real_path, FALSE);
1851 mailimap_list_result_free(lep_list);
1853 for (cur = item_list; cur != NULL; cur = cur->next) {
1854 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1856 tmplist = imap_scan_subtree(folder, cur_item,
1857 unsubs_only, recursive);
1859 child_list = g_list_concat(child_list, tmplist);
1861 child_list = g_list_prepend(child_list,
1862 imap_get_real_path(session,
1863 IMAP_FOLDER(folder), cur_item->path));
1865 folder_item_destroy(cur_item);
1867 child_list = g_list_reverse(child_list);
1868 g_slist_free(item_list);
1871 r = imap_threaded_lsub(folder, "", wildcard_path, &lep_list);
1873 statusbar_pop_all();
1876 sub_list = imap_list_from_lep(IMAP_FOLDER(folder),
1877 lep_list, real_path, FALSE);
1878 mailimap_list_result_free(lep_list);
1880 for (cur = sub_list; cur != NULL; cur = cur->next) {
1881 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1882 GList *oldlitem = NULL;
1883 gchar *tmp = imap_get_real_path(session,
1884 IMAP_FOLDER(folder), cur_item->path);
1885 folder_item_destroy(cur_item);
1886 oldlitem = g_list_find_custom(
1887 child_list, tmp, (GCompareFunc)strcmp2);
1889 child_list = g_list_remove_link(child_list, oldlitem);
1890 g_free(oldlitem->data);
1891 g_list_free(oldlitem);
1897 statusbar_pop_all();
1902 static gint imap_create_tree(Folder *folder)
1904 g_return_val_if_fail(folder != NULL, -1);
1905 g_return_val_if_fail(folder->node != NULL, -1);
1906 g_return_val_if_fail(folder->node->data != NULL, -1);
1907 g_return_val_if_fail(folder->account != NULL, -1);
1909 imap_scan_tree(folder);
1910 imap_create_missing_folders(folder);
1915 static void imap_create_missing_folders(Folder *folder)
1917 g_return_if_fail(folder != NULL);
1920 folder->inbox = imap_create_special_folder
1921 (folder, F_INBOX, "INBOX");
1923 folder->trash = imap_create_special_folder
1924 (folder, F_TRASH, "Trash");
1926 folder->queue = imap_create_special_folder
1927 (folder, F_QUEUE, "Queue");
1928 if (!folder->outbox)
1929 folder->outbox = imap_create_special_folder
1930 (folder, F_OUTBOX, "Sent");
1932 folder->draft = imap_create_special_folder
1933 (folder, F_DRAFT, "Drafts");
1936 static FolderItem *imap_create_special_folder(Folder *folder,
1937 SpecialFolderItemType stype,
1941 FolderItem *new_item;
1943 g_return_val_if_fail(folder != NULL, NULL);
1944 g_return_val_if_fail(folder->node != NULL, NULL);
1945 g_return_val_if_fail(folder->node->data != NULL, NULL);
1946 g_return_val_if_fail(folder->account != NULL, NULL);
1947 g_return_val_if_fail(name != NULL, NULL);
1949 item = FOLDER_ITEM(folder->node->data);
1950 new_item = imap_create_folder(folder, item, name);
1953 g_warning("Can't create '%s'\n", name);
1954 if (!folder->inbox) return NULL;
1956 new_item = imap_create_folder(folder, folder->inbox, name);
1958 g_warning("Can't create '%s' under INBOX\n", name);
1960 new_item->stype = stype;
1962 new_item->stype = stype;
1967 static gchar *imap_folder_get_path(Folder *folder)
1971 g_return_val_if_fail(folder != NULL, NULL);
1972 g_return_val_if_fail(folder->account != NULL, NULL);
1974 folder_path = g_strconcat(get_imap_cache_dir(),
1976 folder->account->recv_server,
1978 folder->account->userid,
1984 static gchar *imap_item_get_path(Folder *folder, FolderItem *item)
1986 gchar *folder_path, *path;
1988 g_return_val_if_fail(folder != NULL, NULL);
1989 g_return_val_if_fail(item != NULL, NULL);
1990 folder_path = imap_folder_get_path(folder);
1992 g_return_val_if_fail(folder_path != NULL, NULL);
1993 if (folder_path[0] == G_DIR_SEPARATOR) {
1995 path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
1998 path = g_strdup(folder_path);
2001 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
2002 folder_path, G_DIR_SEPARATOR_S,
2005 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
2008 g_free(folder_path);
2013 static FolderItem *imap_create_folder(Folder *folder, FolderItem *parent,
2016 gchar *dirpath, *imap_path;
2017 IMAPSession *session;
2018 FolderItem *new_item;
2023 gboolean no_select = FALSE, no_sub = FALSE;
2024 gboolean exist = FALSE;
2026 g_return_val_if_fail(folder != NULL, NULL);
2027 g_return_val_if_fail(folder->account != NULL, NULL);
2028 g_return_val_if_fail(parent != NULL, NULL);
2029 g_return_val_if_fail(name != NULL, NULL);
2031 debug_print("getting session...\n");
2032 session = imap_session_get(folder);
2037 if (!folder_item_parent(parent) && strcmp(name, "INBOX") == 0) {
2038 dirpath = g_strdup(name);
2039 }else if (parent->path)
2040 dirpath = g_strconcat(parent->path, "/", name, NULL);
2041 else if ((p = strchr(name, '/')) != NULL && *(p + 1) != '\0')
2042 dirpath = g_strdup(name);
2043 else if (folder->account->imap_dir && *folder->account->imap_dir) {
2046 Xstrdup_a(imap_dir, folder->account->imap_dir, {unlock_session(session);return NULL;});
2047 strtailchomp(imap_dir, '/');
2048 dirpath = g_strconcat(imap_dir, "/", name, NULL);
2050 dirpath = g_strdup(name);
2054 /* keep trailing directory separator to create a folder that contains
2056 imap_path = imap_utf8_to_modified_utf7(dirpath);
2058 strtailchomp(dirpath, '/');
2059 Xstrdup_a(new_name, name, {
2061 unlock_session(session);
2064 separator = imap_get_path_separator(session, IMAP_FOLDER(folder), imap_path);
2065 imap_path_separator_subst(imap_path, separator);
2066 /* remove trailing / for display */
2067 strtailchomp(new_name, '/');
2069 if (strcmp(dirpath, "INBOX") != 0) {
2074 argbuf = g_ptr_array_new();
2075 r = imap_threaded_list(folder, "", imap_path, &lep_list);
2076 if (r != MAILIMAP_NO_ERROR) {
2077 log_warning(LOG_PROTOCOL, _("can't create mailbox: LIST failed\n"));
2080 ptr_array_free_strings(argbuf);
2081 g_ptr_array_free(argbuf, TRUE);
2082 unlock_session(session);
2086 if (clist_count(lep_list) > 0)
2088 mailimap_list_result_free(lep_list);
2091 ok = imap_cmd_create(session, imap_path);
2092 if (ok != IMAP_SUCCESS) {
2093 log_warning(LOG_PROTOCOL, _("can't create mailbox\n"));
2096 unlock_session(session);
2099 r = imap_threaded_list(folder, "", imap_path, &lep_list);
2100 if (r == MAILIMAP_NO_ERROR) {
2101 GSList *item_list = imap_list_from_lep(IMAP_FOLDER(folder),
2102 lep_list, dirpath, TRUE);
2104 FolderItem *cur_item = FOLDER_ITEM(item_list->data);
2105 no_select = cur_item->no_select;
2106 no_sub = cur_item->no_sub;
2107 g_slist_free(item_list);
2109 mailimap_list_result_free(lep_list);
2112 imap_threaded_subscribe(folder, imap_path, TRUE);
2116 /* just get flags */
2117 r = imap_threaded_list(folder, "", "INBOX", &lep_list);
2118 if (r == MAILIMAP_NO_ERROR) {
2119 GSList *item_list = imap_list_from_lep(IMAP_FOLDER(folder),
2120 lep_list, dirpath, TRUE);
2122 FolderItem *cur_item = FOLDER_ITEM(item_list->data);
2123 no_select = cur_item->no_select;
2124 no_sub = cur_item->no_sub;
2125 g_slist_free(item_list);
2127 mailimap_list_result_free(lep_list);
2131 new_item = folder_item_new(folder, new_name, dirpath);
2132 new_item->no_select = no_select;
2133 new_item->no_sub = no_sub;
2134 folder_item_append(parent, new_item);
2138 dirpath = folder_item_get_path(new_item);
2139 if (!is_dir_exist(dirpath))
2140 make_dir_hier(dirpath);
2142 unlock_session(session);
2145 /* folder existed, scan it */
2146 folder_item_scan_full(new_item, FALSE);
2152 static gint imap_rename_folder(Folder *folder, FolderItem *item,
2157 gchar *real_oldpath;
2158 gchar *real_newpath;
2160 gchar *old_cache_dir;
2161 gchar *new_cache_dir;
2162 IMAPSession *session;
2165 gint exists, recent, unseen;
2166 guint32 uid_validity;
2168 g_return_val_if_fail(folder != NULL, -1);
2169 g_return_val_if_fail(item != NULL, -1);
2170 g_return_val_if_fail(item->path != NULL, -1);
2171 g_return_val_if_fail(name != NULL, -1);
2173 debug_print("getting session...\n");
2174 session = imap_session_get(folder);
2179 if (strchr(name, imap_get_path_separator(session, IMAP_FOLDER(folder), item->path)) != NULL) {
2180 g_warning(_("New folder name must not contain the namespace "
2182 unlock_session(session);
2186 real_oldpath = imap_get_real_path(session, IMAP_FOLDER(folder), item->path);
2188 g_free(session->mbox);
2189 session->mbox = NULL;
2190 ok = imap_cmd_examine(session, "INBOX",
2191 &exists, &recent, &unseen, &uid_validity, FALSE);
2192 if (ok != IMAP_SUCCESS) {
2193 g_free(real_oldpath);
2194 unlock_session(session);
2198 separator = imap_get_path_separator(session, IMAP_FOLDER(folder), item->path);
2199 if (strchr(item->path, G_DIR_SEPARATOR)) {
2200 dirpath = g_path_get_dirname(item->path);
2201 newpath = g_strconcat(dirpath, G_DIR_SEPARATOR_S, name, NULL);
2204 newpath = g_strdup(name);
2206 real_newpath = imap_utf8_to_modified_utf7(newpath);
2207 imap_path_separator_subst(real_newpath, separator);
2209 ok = imap_cmd_rename(session, real_oldpath, real_newpath);
2210 if (ok != IMAP_SUCCESS) {
2211 log_warning(LOG_PROTOCOL, _("can't rename mailbox: %s to %s\n"),
2212 real_oldpath, real_newpath);
2213 g_free(real_oldpath);
2215 g_free(real_newpath);
2216 unlock_session(session);
2220 item->name = g_strdup(name);
2222 old_cache_dir = folder_item_get_path(item);
2224 paths[0] = g_strdup(item->path);
2226 g_node_traverse(item->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
2227 imap_rename_folder_func, paths);
2229 if (is_dir_exist(old_cache_dir)) {
2230 new_cache_dir = folder_item_get_path(item);
2231 if (rename(old_cache_dir, new_cache_dir) < 0) {
2232 FILE_OP_ERROR(old_cache_dir, "rename");
2234 g_free(new_cache_dir);
2237 g_free(old_cache_dir);
2240 g_free(real_oldpath);
2241 g_free(real_newpath);
2242 unlock_session(session);
2246 gint imap_subscribe(Folder *folder, FolderItem *item, gchar *rpath, gboolean sub)
2250 IMAPSession *session;
2251 debug_print("getting session...\n");
2253 session = imap_session_get(folder);
2257 if (item && item->path) {
2258 path = imap_get_real_path(session, IMAP_FOLDER(folder), item->path);
2261 if (!strcmp(path, "INBOX") && sub == FALSE)
2263 debug_print("%ssubscribing %s\n", sub?"":"un", path);
2264 r = imap_threaded_subscribe(folder, path, sub);
2267 r = imap_threaded_subscribe(folder, rpath, sub);
2273 static gint imap_remove_folder_real(Folder *folder, FolderItem *item)
2276 IMAPSession *session;
2280 g_return_val_if_fail(folder != NULL, -1);
2281 g_return_val_if_fail(item != NULL, -1);
2282 g_return_val_if_fail(item->path != NULL, -1);
2284 debug_print("getting session...\n");
2285 session = imap_session_get(folder);
2289 path = imap_get_real_path(session, IMAP_FOLDER(folder), item->path);
2291 imap_threaded_subscribe(folder, path, FALSE);
2292 ok = imap_cmd_delete(session, path);
2293 if (ok != IMAP_SUCCESS) {
2294 gchar *tmp = g_strdup_printf("%s%c", path,
2295 imap_get_path_separator(session, IMAP_FOLDER(folder), path));
2298 ok = imap_cmd_delete(session, path);
2301 if (ok != IMAP_SUCCESS) {
2302 log_warning(LOG_PROTOCOL, _("can't delete mailbox\n"));
2304 unlock_session(session);
2309 cache_dir = folder_item_get_path(item);
2310 if (is_dir_exist(cache_dir) && remove_dir_recursive(cache_dir) < 0)
2311 g_warning("can't remove directory '%s'\n", cache_dir);
2313 folder_item_remove(item);
2314 unlock_session(session);
2318 static gint imap_remove_folder(Folder *folder, FolderItem *item)
2322 g_return_val_if_fail(item != NULL, -1);
2323 g_return_val_if_fail(item->folder != NULL, -1);
2324 g_return_val_if_fail(item->node != NULL, -1);
2326 node = item->node->children;
2327 while (node != NULL) {
2329 if (imap_remove_folder(folder, FOLDER_ITEM(node->data)) < 0)
2333 debug_print("IMAP removing %s\n", item->path);
2335 if (imap_remove_all_msg(folder, item) < 0)
2337 return imap_remove_folder_real(folder, item);
2340 typedef struct _uncached_data {
2341 IMAPSession *session;
2343 MsgNumberList *numlist;
2349 static void *imap_get_uncached_messages_thread(void *data)
2351 uncached_data *stuff = (uncached_data *)data;
2352 IMAPSession *session = stuff->session;
2353 FolderItem *item = stuff->item;
2354 MsgNumberList *numlist = stuff->numlist;
2356 GSList *newlist = NULL;
2357 GSList *llast = NULL;
2358 GSList *seq_list, *cur;
2360 debug_print("uncached_messages\n");
2362 if (session == NULL || item == NULL || item->folder == NULL
2363 || FOLDER_CLASS(item->folder) != &imap_class) {
2368 seq_list = imap_get_lep_set_from_numlist(numlist);
2369 debug_print("get msgs info\n");
2370 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
2371 struct mailimap_set * imapset;
2377 if (session->cancelled)
2380 imapset = cur->data;
2382 r = imap_threaded_fetch_env(session->folder,
2383 imapset, &env_list);
2384 if (r != MAILIMAP_NO_ERROR)
2387 session_set_access_time(SESSION(session));
2390 for(i = 0 ; i < carray_count(env_list) ; i ++) {
2391 struct imap_fetch_env_info * info;
2394 info = carray_get(env_list, i);
2395 msginfo = imap_envelope_from_lep(info, item);
2396 if (msginfo == NULL)
2398 msginfo->folder = item;
2400 llast = newlist = g_slist_append(newlist, msginfo);
2402 llast = g_slist_append(llast, msginfo);
2403 llast = llast->next;
2408 imap_fetch_env_free(env_list);
2411 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
2412 struct mailimap_set * imapset;
2414 imapset = cur->data;
2415 mailimap_set_free(imapset);
2418 session_set_access_time(SESSION(session));
2423 #define MAX_MSG_NUM 50
2425 static GSList *imap_get_uncached_messages(IMAPSession *session,
2427 MsgNumberList *numlist)
2429 GSList *result = NULL;
2431 uncached_data *data = g_new0(uncached_data, 1);
2436 data->total = g_slist_length(numlist);
2437 debug_print("messages list : %i\n", data->total);
2439 while (cur != NULL) {
2440 GSList * partial_result;
2448 while (count < MAX_MSG_NUM) {
2453 if (newlist == NULL)
2454 llast = newlist = g_slist_append(newlist, p);
2456 llast = g_slist_append(llast, p);
2457 llast = llast->next;
2467 data->session = session;
2469 data->numlist = newlist;
2472 if (prefs_common.work_offline &&
2473 !inc_offline_should_override(FALSE,
2474 _("Claws Mail needs network access in order "
2475 "to access the IMAP server."))) {
2481 (GSList *)imap_get_uncached_messages_thread(data);
2483 statusbar_progress_all(data->cur,data->total, 1);
2485 g_slist_free(newlist);
2487 result = g_slist_concat(result, partial_result);
2491 statusbar_progress_all(0,0,0);
2492 statusbar_pop_all();
2497 static void imap_delete_all_cached_messages(FolderItem *item)
2501 g_return_if_fail(item != NULL);
2502 g_return_if_fail(item->folder != NULL);
2503 g_return_if_fail(FOLDER_CLASS(item->folder) == &imap_class);
2505 debug_print("Deleting all cached messages...\n");
2507 dir = folder_item_get_path(item);
2508 if (is_dir_exist(dir))
2509 remove_all_numbered_files(dir);
2512 debug_print("done.\n");
2515 gchar imap_get_path_separator_for_item(FolderItem *item)
2517 Folder *folder = NULL;
2518 IMAPFolder *imap_folder = NULL;
2519 IMAPSession *session = NULL;
2524 folder = item->folder;
2529 imap_folder = IMAP_FOLDER(folder);
2534 debug_print("getting session...");
2535 session = imap_session_get(FOLDER(folder));
2536 result = imap_get_path_separator(session, imap_folder, item->path);
2537 unlock_session(session);
2541 static gchar imap_refresh_path_separator(IMAPSession *session, IMAPFolder *folder, const gchar *subfolder)
2545 gchar separator = '\0';
2547 g_return_val_if_fail(session != NULL, '/');
2548 r = imap_threaded_list((Folder *)folder, "", subfolder, &lep_list);
2550 if (r != MAILIMAP_NO_ERROR) {
2551 log_warning(LOG_PROTOCOL, _("LIST failed\n"));
2555 if (clist_count(lep_list) > 0) {
2556 clistiter * iter = clist_begin(lep_list);
2557 struct mailimap_mailbox_list * mb;
2558 mb = clist_content(iter);
2560 separator = mb->mb_delimiter;
2561 debug_print("got separator: %c\n", folder->last_seen_separator);
2563 mailimap_list_result_free(lep_list);
2567 static gchar imap_get_path_separator(IMAPSession *session, IMAPFolder *folder, const gchar *path)
2569 gchar separator = '/';
2571 if (folder->last_seen_separator == 0) {
2572 folder->last_seen_separator = imap_refresh_path_separator(session, folder, "");
2575 if (folder->last_seen_separator == 0) {
2576 folder->last_seen_separator = imap_refresh_path_separator(session, folder, "INBOX");
2579 if (folder->last_seen_separator != 0) {
2580 debug_print("using separator: %c\n", folder->last_seen_separator);
2581 return folder->last_seen_separator;
2587 static gchar *imap_get_real_path(IMAPSession *session, IMAPFolder *folder, const gchar *path)
2592 g_return_val_if_fail(folder != NULL, NULL);
2593 g_return_val_if_fail(path != NULL, NULL);
2595 real_path = imap_utf8_to_modified_utf7(path);
2596 separator = imap_get_path_separator(session, folder, path);
2597 imap_path_separator_subst(real_path, separator);
2602 static gint imap_set_message_flags(IMAPSession *session,
2603 MsgNumberList *numlist,
2611 seq_list = imap_get_lep_set_from_numlist(numlist);
2613 for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) {
2614 struct mailimap_set * imapset;
2616 imapset = cur->data;
2618 ok = imap_cmd_store(session, imapset,
2622 imap_lep_set_free(seq_list);
2624 return IMAP_SUCCESS;
2627 typedef struct _select_data {
2628 IMAPSession *session;
2633 guint32 *uid_validity;
2637 static gint imap_select(IMAPSession *session, IMAPFolder *folder,
2639 gint *exists, gint *recent, gint *unseen,
2640 guint32 *uid_validity, gboolean block)
2644 gint exists_, recent_, unseen_;
2645 guint32 uid_validity_;
2647 if (!exists && !recent && !unseen && !uid_validity) {
2648 if (session->mbox && strcmp(session->mbox, path) == 0)
2649 return IMAP_SUCCESS;
2658 uid_validity = &uid_validity_;
2660 g_free(session->mbox);
2661 session->mbox = NULL;
2663 real_path = imap_get_real_path(session, folder, path);
2665 ok = imap_cmd_select(session, real_path,
2666 exists, recent, unseen, uid_validity, block);
2667 if (ok != IMAP_SUCCESS)
2668 log_warning(LOG_PROTOCOL, _("can't select folder: %s\n"), real_path);
2670 session->mbox = g_strdup(path);
2671 session->folder_content_changed = FALSE;
2678 static gint imap_status(IMAPSession *session, IMAPFolder *folder,
2679 const gchar *path, IMAPFolderItem *item,
2681 guint32 *uid_next, guint32 *uid_validity,
2682 gint *unseen, gboolean block)
2686 struct mailimap_mailbox_data_status * data_status;
2691 real_path = imap_get_real_path(session, folder, path);
2709 r = imap_threaded_status(FOLDER(folder), real_path,
2710 &data_status, mask);
2713 if (r != MAILIMAP_NO_ERROR) {
2714 debug_print("status err %d\n", r);
2718 if (data_status->st_info_list == NULL) {
2719 mailimap_mailbox_data_status_free(data_status);
2720 debug_print("status->st_info_list == NULL\n");
2725 for(iter = clist_begin(data_status->st_info_list) ; iter != NULL ;
2726 iter = clist_next(iter)) {
2727 struct mailimap_status_info * info;
2729 info = clist_content(iter);
2730 switch (info->st_att) {
2731 case MAILIMAP_STATUS_ATT_MESSAGES:
2733 * messages = info->st_value;
2734 got_values |= 1 << 0;
2738 case MAILIMAP_STATUS_ATT_UIDNEXT:
2740 * uid_next = info->st_value;
2741 got_values |= 1 << 2;
2745 case MAILIMAP_STATUS_ATT_UIDVALIDITY:
2747 * uid_validity = info->st_value;
2748 got_values |= 1 << 3;
2752 case MAILIMAP_STATUS_ATT_UNSEEN:
2754 * unseen = info->st_value;
2755 got_values |= 1 << 4;
2760 mailimap_mailbox_data_status_free(data_status);
2762 if (got_values != mask) {
2763 g_warning("status: incomplete values received (%d)\n", got_values);
2765 return IMAP_SUCCESS;
2768 static void imap_free_capabilities(IMAPSession *session)
2770 slist_free_strings(session->capability);
2771 g_slist_free(session->capability);
2772 session->capability = NULL;
2775 /* low-level IMAP4rev1 commands */
2777 static gint imap_cmd_login(IMAPSession *session,
2778 const gchar *user, const gchar *pass,
2784 if (!strcmp(type, "LOGIN") && imap_has_capability(session, "LOGINDISABLED")) {
2785 gint ok = IMAP_ERROR;
2786 if (imap_has_capability(session, "STARTTLS")) {
2788 log_warning(LOG_PROTOCOL, _("Server requires TLS to log in.\n"));
2789 ok = imap_cmd_starttls(session);
2790 if (ok != IMAP_SUCCESS) {
2791 log_warning(LOG_PROTOCOL, _("Can't start TLS session.\n"));
2795 imap_free_capabilities(session);
2796 if (imap_get_capabilities(session) != MAILIMAP_NO_ERROR) {
2797 log_warning(LOG_PROTOCOL, _("Can't refresh capabilities.\n"));
2802 log_error(LOG_PROTOCOL, _("Connection to %s failed: "
2803 "server requires TLS, but Claws Mail "
2804 "has been compiled without OpenSSL "
2806 SESSION(session)->server);
2810 log_error(LOG_PROTOCOL, _("Server logins are disabled.\n"));
2815 log_print(LOG_PROTOCOL, "IMAP4> Logging %s to %s using %s\n",
2817 SESSION(session)->server,
2819 r = imap_threaded_login(session->folder, user, pass, type);
2820 if (r != MAILIMAP_NO_ERROR) {
2821 log_print(LOG_PROTOCOL, "IMAP4< Error logging in to %s\n",
2822 SESSION(session)->server);
2825 log_print(LOG_PROTOCOL, "IMAP4< Login to %s successful\n",
2826 SESSION(session)->server);
2832 static gint imap_cmd_noop(IMAPSession *session)
2835 unsigned int exists;
2837 r = imap_threaded_noop(session->folder, &exists);
2838 if (r != MAILIMAP_NO_ERROR) {
2839 debug_print("noop err %d\n", r);
2842 session->exists = exists;
2843 session_set_access_time(SESSION(session));
2845 return IMAP_SUCCESS;
2849 static gint imap_cmd_starttls(IMAPSession *session)
2853 r = imap_threaded_starttls(session->folder,
2854 SESSION(session)->server, SESSION(session)->port);
2855 if (r != MAILIMAP_NO_ERROR) {
2856 debug_print("starttls err %d\n", r);
2859 return IMAP_SUCCESS;
2863 static gint imap_cmd_select(IMAPSession *session, const gchar *folder,
2864 gint *exists, gint *recent, gint *unseen,
2865 guint32 *uid_validity, gboolean block)
2869 r = imap_threaded_select(session->folder, folder,
2870 exists, recent, unseen, uid_validity);
2871 if (r != MAILIMAP_NO_ERROR) {
2872 debug_print("select err %d\n", r);
2875 return IMAP_SUCCESS;
2878 static gint imap_cmd_examine(IMAPSession *session, const gchar *folder,
2879 gint *exists, gint *recent, gint *unseen,
2880 guint32 *uid_validity, gboolean block)
2884 r = imap_threaded_examine(session->folder, folder,
2885 exists, recent, unseen, uid_validity);
2886 if (r != MAILIMAP_NO_ERROR) {
2887 debug_print("examine err %d\n", r);
2891 return IMAP_SUCCESS;
2894 static gint imap_cmd_create(IMAPSession *session, const gchar *folder)
2898 r = imap_threaded_create(session->folder, folder);
2899 if (r != MAILIMAP_NO_ERROR) {
2904 return IMAP_SUCCESS;
2907 static gint imap_cmd_rename(IMAPSession *session, const gchar *old_folder,
2908 const gchar *new_folder)
2912 r = imap_threaded_rename(session->folder, old_folder,
2914 if (r != MAILIMAP_NO_ERROR) {
2919 return IMAP_SUCCESS;
2922 static gint imap_cmd_delete(IMAPSession *session, const gchar *folder)
2927 r = imap_threaded_delete(session->folder, folder);
2928 if (r != MAILIMAP_NO_ERROR) {
2933 return IMAP_SUCCESS;
2936 typedef struct _fetch_data {
2937 IMAPSession *session;
2939 const gchar *filename;
2945 static void *imap_cmd_fetch_thread(void *data)
2947 fetch_data *stuff = (fetch_data *)data;
2948 IMAPSession *session = stuff->session;
2949 guint32 uid = stuff->uid;
2950 const gchar *filename = stuff->filename;
2954 r = imap_threaded_fetch_content(session->folder,
2958 r = imap_threaded_fetch_content(session->folder,
2961 if (r != MAILIMAP_NO_ERROR) {
2962 debug_print("fetch err %d\n", r);
2963 return GINT_TO_POINTER(IMAP_ERROR);
2965 return GINT_TO_POINTER(IMAP_SUCCESS);
2968 static gint imap_cmd_fetch(IMAPSession *session, guint32 uid,
2969 const gchar *filename, gboolean headers,
2972 fetch_data *data = g_new0(fetch_data, 1);
2975 data->session = session;
2977 data->filename = filename;
2978 data->headers = headers;
2981 if (prefs_common.work_offline &&
2982 !inc_offline_should_override(FALSE,
2983 _("Claws Mail needs network access in order "
2984 "to access the IMAP server."))) {
2988 statusbar_print_all(_("Fetching message..."));
2989 result = GPOINTER_TO_INT(imap_cmd_fetch_thread(data));
2990 statusbar_pop_all();
2996 static gint imap_cmd_append(IMAPSession *session, const gchar *destfolder,
2997 const gchar *file, IMAPFlags flags,
3000 struct mailimap_flag_list * flag_list;
3003 g_return_val_if_fail(file != NULL, IMAP_ERROR);
3005 flag_list = imap_flag_to_lep(flags);
3006 r = imap_threaded_append(session->folder, destfolder,
3007 file, flag_list, (int *)new_uid);
3008 mailimap_flag_list_free(flag_list);
3010 if (r != MAILIMAP_NO_ERROR) {
3011 debug_print("append err %d\n", r);
3014 return IMAP_SUCCESS;
3017 static gint imap_cmd_copy(IMAPSession *session, struct mailimap_set * set,
3018 const gchar *destfolder, GRelation *uid_mapping,
3019 struct mailimap_set **source, struct mailimap_set **dest)
3023 g_return_val_if_fail(session != NULL, IMAP_ERROR);
3024 g_return_val_if_fail(set != NULL, IMAP_ERROR);
3025 g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
3027 r = imap_threaded_copy(session->folder, set, destfolder, source, dest);
3028 if (r != MAILIMAP_NO_ERROR) {
3033 return IMAP_SUCCESS;
3036 static gint imap_cmd_store(IMAPSession *session, struct mailimap_set * set,
3037 IMAPFlags flags, int do_add)
3040 struct mailimap_flag_list * flag_list;
3041 struct mailimap_store_att_flags * store_att_flags;
3043 flag_list = imap_flag_to_lep(flags);
3047 mailimap_store_att_flags_new_add_flags_silent(flag_list);
3050 mailimap_store_att_flags_new_remove_flags_silent(flag_list);
3052 r = imap_threaded_store(session->folder, set, store_att_flags);
3053 mailimap_store_att_flags_free(store_att_flags);
3054 if (r != MAILIMAP_NO_ERROR) {
3059 return IMAP_SUCCESS;
3062 static gint imap_cmd_expunge(IMAPSession *session)
3066 if (prefs_common.work_offline &&
3067 !inc_offline_should_override(FALSE,
3068 _("Claws Mail needs network access in order "
3069 "to access the IMAP server."))) {
3073 r = imap_threaded_expunge(session->folder);
3074 if (r != MAILIMAP_NO_ERROR) {
3079 return IMAP_SUCCESS;
3082 static void imap_path_separator_subst(gchar *str, gchar separator)
3085 gboolean in_escape = FALSE;
3087 if (!separator || separator == '/') return;
3089 for (p = str; *p != '\0'; p++) {
3090 if (*p == '/' && !in_escape)
3092 else if (*p == '&' && *(p + 1) != '-' && !in_escape)
3094 else if (*p == '-' && in_escape)
3099 static gchar *imap_modified_utf7_to_utf8(const gchar *mutf7_str)
3101 static iconv_t cd = (iconv_t)-1;
3102 static gboolean iconv_ok = TRUE;
3105 size_t norm_utf7_len;
3107 gchar *to_str, *to_p;
3109 gboolean in_escape = FALSE;
3111 if (!iconv_ok) return g_strdup(mutf7_str);
3113 if (cd == (iconv_t)-1) {
3114 cd = iconv_open(CS_INTERNAL, CS_UTF_7);
3115 if (cd == (iconv_t)-1) {
3116 g_warning("iconv cannot convert UTF-7 to %s\n",
3119 return g_strdup(mutf7_str);
3123 /* modified UTF-7 to normal UTF-7 conversion */
3124 norm_utf7 = g_string_new(NULL);
3126 for (p = mutf7_str; *p != '\0'; p++) {
3127 /* replace: '&' -> '+',
3129 escaped ',' -> '/' */
3130 if (!in_escape && *p == '&') {
3131 if (*(p + 1) != '-') {
3132 g_string_append_c(norm_utf7, '+');
3135 g_string_append_c(norm_utf7, '&');
3138 } else if (in_escape && *p == ',') {
3139 g_string_append_c(norm_utf7, '/');
3140 } else if (in_escape && *p == '-') {
3141 g_string_append_c(norm_utf7, '-');
3144 g_string_append_c(norm_utf7, *p);
3148 norm_utf7_p = norm_utf7->str;
3149 norm_utf7_len = norm_utf7->len;
3150 to_len = strlen(mutf7_str) * 5;
3151 to_p = to_str = g_malloc(to_len + 1);
3153 if (iconv(cd, (ICONV_CONST gchar **)&norm_utf7_p, &norm_utf7_len,
3154 &to_p, &to_len) == -1) {
3155 g_warning(_("iconv cannot convert UTF-7 to %s\n"),
3156 conv_get_locale_charset_str());
3157 g_string_free(norm_utf7, TRUE);
3159 return g_strdup(mutf7_str);
3162 /* second iconv() call for flushing */
3163 iconv(cd, NULL, NULL, &to_p, &to_len);
3164 g_string_free(norm_utf7, TRUE);
3170 static gchar *imap_utf8_to_modified_utf7(const gchar *from)
3172 static iconv_t cd = (iconv_t)-1;
3173 static gboolean iconv_ok = TRUE;
3174 gchar *norm_utf7, *norm_utf7_p;
3175 size_t from_len, norm_utf7_len;
3177 gchar *from_tmp, *to, *p;
3178 gboolean in_escape = FALSE;
3180 if (!iconv_ok) return g_strdup(from);
3182 if (cd == (iconv_t)-1) {
3183 cd = iconv_open(CS_UTF_7, CS_INTERNAL);
3184 if (cd == (iconv_t)-1) {
3185 g_warning(_("iconv cannot convert %s to UTF-7\n"),
3188 return g_strdup(from);
3192 /* UTF-8 to normal UTF-7 conversion */
3193 Xstrdup_a(from_tmp, from, return g_strdup(from));
3194 from_len = strlen(from);
3195 norm_utf7_len = from_len * 5;
3196 Xalloca(norm_utf7, norm_utf7_len + 1, return g_strdup(from));
3197 norm_utf7_p = norm_utf7;
3199 #define IS_PRINT(ch) (isprint(ch) && IS_ASCII(ch))
3201 while (from_len > 0) {
3202 if (*from_tmp == '+') {
3203 *norm_utf7_p++ = '+';
3204 *norm_utf7_p++ = '-';
3208 } else if (IS_PRINT(*(guchar *)from_tmp)) {
3209 /* printable ascii char */
3210 *norm_utf7_p = *from_tmp;
3216 size_t conv_len = 0;
3218 /* unprintable char: convert to UTF-7 */
3220 while (!IS_PRINT(*(guchar *)p) && conv_len < from_len) {
3221 conv_len += g_utf8_skip[*(guchar *)p];
3222 p += g_utf8_skip[*(guchar *)p];
3225 from_len -= conv_len;
3226 if (iconv(cd, (ICONV_CONST gchar **)&from_tmp,
3228 &norm_utf7_p, &norm_utf7_len) == -1) {
3229 g_warning(_("iconv cannot convert UTF-8 to UTF-7\n"));
3230 return g_strdup(from);
3233 /* second iconv() call for flushing */
3234 iconv(cd, NULL, NULL, &norm_utf7_p, &norm_utf7_len);
3240 *norm_utf7_p = '\0';
3241 to_str = g_string_new(NULL);
3242 for (p = norm_utf7; p < norm_utf7_p; p++) {
3243 /* replace: '&' -> "&-",
3246 BASE64 '/' -> ',' */
3247 if (!in_escape && *p == '&') {
3248 g_string_append(to_str, "&-");
3249 } else if (!in_escape && *p == '+') {
3250 if (*(p + 1) == '-') {
3251 g_string_append_c(to_str, '+');
3254 g_string_append_c(to_str, '&');
3257 } else if (in_escape && *p == '/') {
3258 g_string_append_c(to_str, ',');
3259 } else if (in_escape && *p == '-') {
3260 g_string_append_c(to_str, '-');
3263 g_string_append_c(to_str, *p);
3269 g_string_append_c(to_str, '-');
3273 g_string_free(to_str, FALSE);
3278 static gboolean imap_rename_folder_func(GNode *node, gpointer data)
3280 FolderItem *item = node->data;
3281 gchar **paths = data;
3282 const gchar *oldpath = paths[0];
3283 const gchar *newpath = paths[1];
3284 gchar *real_oldpath, *real_newpath;
3286 gchar *new_itempath;
3288 IMAPSession *session = imap_session_get(item->folder);
3290 oldpathlen = strlen(oldpath);
3291 if (strncmp(oldpath, item->path, oldpathlen) != 0) {
3292 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
3296 base = item->path + oldpathlen;
3297 while (*base == G_DIR_SEPARATOR) base++;
3299 new_itempath = g_strdup(newpath);
3301 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
3304 real_oldpath = imap_get_real_path(session, IMAP_FOLDER(item->folder), item->path);
3306 item->path = new_itempath;
3308 real_newpath = imap_get_real_path(session, IMAP_FOLDER(item->folder), item->path);
3310 imap_threaded_subscribe(item->folder, real_oldpath, FALSE);
3311 imap_threaded_subscribe(item->folder, real_newpath, TRUE);
3313 g_free(real_oldpath);
3314 g_free(real_newpath);
3318 typedef struct _get_list_uid_data {
3320 IMAPSession *session;
3321 IMAPFolderItem *item;
3322 GSList **msgnum_list;
3324 } get_list_uid_data;
3326 static void *get_list_of_uids_thread(void *data)
3328 get_list_uid_data *stuff = (get_list_uid_data *)data;
3329 Folder *folder = stuff->folder;
3330 IMAPFolderItem *item = stuff->item;
3331 GSList **msgnum_list = stuff->msgnum_list;
3332 gint ok, nummsgs = 0, lastuid_old;
3333 IMAPSession *session;
3334 GSList *uidlist, *elem;
3336 clist * lep_uidlist;
3338 session = stuff->session;
3339 if (session == NULL) {
3341 return GINT_TO_POINTER(-1);
3343 /* no session locking here, it's already locked by caller */
3344 ok = imap_select(session, IMAP_FOLDER(folder), item->item.path,
3345 NULL, NULL, NULL, NULL, TRUE);
3346 if (ok != IMAP_SUCCESS) {
3348 return GINT_TO_POINTER(-1);
3353 if (folder->account && folder->account->low_bandwidth) {
3354 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SIMPLE, NULL,
3358 if (r == MAILIMAP_NO_ERROR) {
3359 GSList * fetchuid_list =
3360 imap_uid_list_from_lep(lep_uidlist);
3361 mailimap_search_result_free(lep_uidlist);
3363 uidlist = g_slist_concat(fetchuid_list, uidlist);
3365 carray * lep_uidtab;
3366 r = imap_threaded_fetch_uid(folder, 1,
3368 if (r == MAILIMAP_NO_ERROR) {
3369 GSList * fetchuid_list =
3370 imap_uid_list_from_lep_tab(lep_uidtab);
3371 imap_fetch_uid_list_free(lep_uidtab);
3372 uidlist = g_slist_concat(fetchuid_list, uidlist);
3376 if (r != MAILIMAP_NO_ERROR) {
3378 return GINT_TO_POINTER(-1);
3381 lastuid_old = item->lastuid;
3382 *msgnum_list = g_slist_copy(item->uid_list);
3383 nummsgs = g_slist_length(*msgnum_list);
3384 debug_print("Got %d uids from cache\n", g_slist_length(item->uid_list));
3386 for (elem = uidlist; elem != NULL; elem = g_slist_next(elem)) {
3389 msgnum = GPOINTER_TO_INT(elem->data);
3390 if (msgnum > lastuid_old) {
3391 *msgnum_list = g_slist_prepend(*msgnum_list, GINT_TO_POINTER(msgnum));
3392 item->uid_list = g_slist_prepend(item->uid_list, GINT_TO_POINTER(msgnum));
3395 if(msgnum > item->lastuid)
3396 item->lastuid = msgnum;
3399 g_slist_free(uidlist);
3401 return GINT_TO_POINTER(nummsgs);
3404 static gint get_list_of_uids(IMAPSession *session, Folder *folder, IMAPFolderItem *item, GSList **msgnum_list)
3407 get_list_uid_data *data = g_new0(get_list_uid_data, 1);
3409 data->folder = folder;
3411 data->msgnum_list = msgnum_list;
3412 data->session = session;
3413 if (prefs_common.work_offline &&
3414 !inc_offline_should_override(FALSE,
3415 _("Claws Mail needs network access in order "
3416 "to access the IMAP server."))) {
3421 result = GPOINTER_TO_INT(get_list_of_uids_thread(data));
3427 gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list, gboolean *old_uids_valid)
3429 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3430 IMAPSession *session;
3431 gint ok, nummsgs = 0, exists;
3432 guint32 uid_next = 0, uid_val = 0;
3433 GSList *uidlist = NULL;
3435 gboolean selected_folder;
3436 debug_print("get_num_list\n");
3438 g_return_val_if_fail(folder != NULL, -1);
3439 g_return_val_if_fail(item != NULL, -1);
3440 g_return_val_if_fail(item->item.path != NULL, -1);
3441 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3442 g_return_val_if_fail(folder->account != NULL, -1);
3444 debug_print("getting session...\n");
3445 session = imap_session_get(folder);
3446 g_return_val_if_fail(session != NULL, -1);
3448 if (FOLDER_ITEM(item)->path)
3449 statusbar_print_all(_("Scanning folder %s%c%s ..."),
3450 FOLDER_ITEM(item)->folder->name,
3452 FOLDER_ITEM(item)->path);
3454 statusbar_print_all(_("Scanning folder %s ..."),
3455 FOLDER_ITEM(item)->folder->name);
3457 selected_folder = (session->mbox != NULL) &&
3458 (!strcmp(session->mbox, item->item.path));
3459 if (selected_folder && time(NULL) - item->use_cache < 2) {
3460 ok = imap_cmd_noop(session);
3461 if (ok != IMAP_SUCCESS) {
3462 debug_print("disconnected!\n");
3463 session = imap_reconnect_if_possible(folder, session);
3464 if (session == NULL) {
3465 statusbar_pop_all();
3466 unlock_session(session);
3470 exists = session->exists;
3472 uid_next = item->c_uid_next;
3473 uid_val = item->c_uid_validity;
3474 *old_uids_valid = TRUE;
3476 if (item->use_cache && time(NULL) - item->use_cache < 2) {
3477 exists = item->c_messages;
3478 uid_next = item->c_uid_next;
3479 uid_val = item->c_uid_validity;
3481 debug_print("using cache %d %d %d\n", exists, uid_next, uid_val);
3483 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path, item,
3484 &exists, &uid_next, &uid_val, NULL, FALSE);
3486 item->item.last_num = uid_next - 1;
3488 item->use_cache = (time_t)0;
3489 if (ok != IMAP_SUCCESS) {
3490 statusbar_pop_all();
3491 unlock_session(session);
3494 if(item->item.mtime == uid_val)
3495 *old_uids_valid = TRUE;
3497 *old_uids_valid = FALSE;
3499 debug_print("Freeing imap uid cache (%d != %d)\n",
3500 (int)item->item.mtime, uid_val);
3502 g_slist_free(item->uid_list);
3503 item->uid_list = NULL;
3505 item->item.mtime = uid_val;
3507 imap_delete_all_cached_messages((FolderItem *)item);
3511 /* If old uid_next matches new uid_next we can be sure no message
3512 was added to the folder */
3513 debug_print("uid_next is %d and item->uid_next %d \n",
3514 uid_next, item->uid_next);
3515 if (uid_next == item->uid_next) {
3516 nummsgs = g_slist_length(item->uid_list);
3518 /* If number of messages is still the same we
3519 know our caches message numbers are still valid,
3520 otherwise if the number of messages has decrease
3521 we discard our cache to start a new scan to find
3522 out which numbers have been removed */
3523 if (exists == nummsgs) {
3524 debug_print("exists == nummsgs\n");
3525 *msgnum_list = g_slist_copy(item->uid_list);
3526 statusbar_pop_all();
3527 unlock_session(session);
3529 } else if (exists < nummsgs) {
3530 debug_print("Freeing imap uid cache");
3532 g_slist_free(item->uid_list);
3533 item->uid_list = NULL;
3538 *msgnum_list = NULL;
3539 statusbar_pop_all();
3540 unlock_session(session);
3544 item->last_change = time(NULL);
3545 nummsgs = get_list_of_uids(session, folder, item, &uidlist);
3548 statusbar_pop_all();
3549 unlock_session(session);
3553 if (nummsgs != exists) {
3554 /* Cache contains more messages then folder, we have cached
3555 an old UID of a message that was removed and new messages
3556 have been added too, otherwise the uid_next check would
3558 debug_print("Freeing imap uid cache");
3560 g_slist_free(item->uid_list);
3561 item->uid_list = NULL;
3563 g_slist_free(*msgnum_list);
3565 nummsgs = get_list_of_uids(session, folder, item, &uidlist);
3568 *msgnum_list = uidlist;
3570 dir = folder_item_get_path((FolderItem *)item);
3571 debug_print("removing old messages from %s\n", dir);
3572 remove_numbered_files_not_in_list(dir, *msgnum_list);
3575 item->uid_next = uid_next;
3577 debug_print("get_num_list - ok - %i\n", nummsgs);
3578 statusbar_pop_all();
3579 unlock_session(session);
3583 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item)
3588 flags.perm_flags = MSG_NEW|MSG_UNREAD;
3589 flags.tmp_flags = 0;
3591 g_return_val_if_fail(item != NULL, NULL);
3592 g_return_val_if_fail(file != NULL, NULL);
3594 if (folder_has_parent_of_type(item, F_QUEUE)) {
3595 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
3596 } else if (folder_has_parent_of_type(item, F_DRAFT)) {
3597 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
3600 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3601 if (!msginfo) return NULL;
3603 msginfo->plaintext_file = g_strdup(file);
3604 msginfo->folder = item;
3609 GSList *imap_get_msginfos(Folder *folder, FolderItem *item,
3610 GSList *msgnum_list)
3612 IMAPSession *session;
3613 MsgInfoList *ret = NULL;
3616 debug_print("get_msginfos\n");
3618 g_return_val_if_fail(folder != NULL, NULL);
3619 g_return_val_if_fail(item != NULL, NULL);
3620 g_return_val_if_fail(msgnum_list != NULL, NULL);
3622 debug_print("getting session...\n");
3623 session = imap_session_get(folder);
3624 g_return_val_if_fail(session != NULL, NULL);
3626 debug_print("IMAP getting msginfos\n");
3627 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3628 NULL, NULL, NULL, NULL, FALSE);
3629 if (ok != IMAP_SUCCESS) {
3630 unlock_session(session);
3633 if (!(folder_has_parent_of_type(item, F_DRAFT) ||
3634 folder_has_parent_of_type(item, F_QUEUE))) {
3635 ret = g_slist_concat(ret,
3636 imap_get_uncached_messages(session, item,
3639 MsgNumberList *sorted_list, *elem, *llast = NULL;
3640 gint startnum, lastnum;
3642 sorted_list = g_slist_sort(g_slist_copy(msgnum_list), g_int_compare);
3644 startnum = lastnum = GPOINTER_TO_INT(sorted_list->data);
3646 llast = g_slist_last(ret);
3647 for (elem = sorted_list;; elem = g_slist_next(elem)) {
3651 num = GPOINTER_TO_INT(elem->data);
3653 if (num > lastnum + 1 || elem == NULL) {
3655 for (i = startnum; i <= lastnum; ++i) {
3658 file = imap_fetch_msg(folder, item, i);
3660 MsgInfo *msginfo = imap_parse_msg(file, item);
3661 if (msginfo != NULL) {
3662 msginfo->msgnum = i;
3664 llast = ret = g_slist_append(ret, msginfo);
3666 llast = g_slist_append(llast, msginfo);
3667 llast = llast->next;
3672 session_set_access_time(SESSION(session));
3683 g_slist_free(sorted_list);
3685 unlock_session(session);
3689 MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid)
3691 MsgInfo *msginfo = NULL;
3692 MsgInfoList *msginfolist;
3693 MsgNumberList numlist;
3695 numlist.next = NULL;
3696 numlist.data = GINT_TO_POINTER(uid);
3698 msginfolist = imap_get_msginfos(folder, item, &numlist);
3699 if (msginfolist != NULL) {
3700 msginfo = msginfolist->data;
3701 g_slist_free(msginfolist);
3707 gboolean imap_scan_required(Folder *folder, FolderItem *_item)
3709 IMAPSession *session;
3710 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3711 gint ok, exists = 0, unseen = 0;
3712 guint32 uid_next = 0, uid_val = 0;
3713 gboolean selected_folder;
3715 g_return_val_if_fail(folder != NULL, FALSE);
3716 g_return_val_if_fail(item != NULL, FALSE);
3717 g_return_val_if_fail(item->item.folder != NULL, FALSE);
3718 g_return_val_if_fail(FOLDER_CLASS(item->item.folder) == &imap_class, FALSE);
3720 if (item->item.path == NULL)
3723 debug_print("getting session...\n");
3724 session = imap_session_get(folder);
3725 g_return_val_if_fail(session != NULL, FALSE);
3727 selected_folder = (session->mbox != NULL) &&
3728 (!strcmp(session->mbox, item->item.path));
3729 if (selected_folder && time(NULL) - item->use_cache < 2) {
3730 ok = imap_cmd_noop(session);
3731 if (ok != IMAP_SUCCESS) {
3732 debug_print("disconnected!\n");
3733 session = imap_reconnect_if_possible(folder, session);
3734 if (session == NULL)
3738 if (session->folder_content_changed
3739 || session->exists != item->item.total_msgs) {
3740 unlock_session(session);
3744 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path, IMAP_FOLDER_ITEM(item),
3745 &exists, &uid_next, &uid_val, &unseen, FALSE);
3746 if (ok != IMAP_SUCCESS) {
3747 unlock_session(session);
3751 item->use_cache = time(NULL);
3752 item->c_messages = exists;
3753 item->c_uid_next = uid_next;
3754 item->c_uid_validity = uid_val;
3755 item->c_unseen = unseen;
3756 item->item.last_num = uid_next - 1;
3757 debug_print("uidnext %d, item->uid_next %d, exists %d, item->item.total_msgs %d\n",
3758 uid_next, item->uid_next, exists, item->item.total_msgs);
3759 if ((uid_next != item->uid_next) || (exists != item->item.total_msgs)
3760 || unseen != item->item.unread_msgs || uid_val != item->item.mtime) {
3761 unlock_session(session);
3762 item->last_change = time(NULL);
3766 unlock_session(session);
3770 void imap_change_flags(Folder *folder, FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
3772 IMAPSession *session;
3773 IMAPFlags flags_set = 0, flags_unset = 0;
3774 gint ok = IMAP_SUCCESS;
3775 MsgNumberList numlist;
3776 hashtable_data *ht_data = NULL;
3778 g_return_if_fail(folder != NULL);
3779 g_return_if_fail(folder->klass == &imap_class);
3780 g_return_if_fail(item != NULL);
3781 g_return_if_fail(item->folder == folder);
3782 g_return_if_fail(msginfo != NULL);
3783 g_return_if_fail(msginfo->folder == item);
3785 if (!MSG_IS_MARKED(msginfo->flags) && (newflags & MSG_MARKED))
3786 flags_set |= IMAP_FLAG_FLAGGED;
3787 if ( MSG_IS_MARKED(msginfo->flags) && !(newflags & MSG_MARKED))
3788 flags_unset |= IMAP_FLAG_FLAGGED;
3790 if (!MSG_IS_UNREAD(msginfo->flags) && (newflags & MSG_UNREAD))
3791 flags_unset |= IMAP_FLAG_SEEN;
3792 if ( MSG_IS_UNREAD(msginfo->flags) && !(newflags & MSG_UNREAD))
3793 flags_set |= IMAP_FLAG_SEEN;
3795 if (!MSG_IS_REPLIED(msginfo->flags) && (newflags & MSG_REPLIED))
3796 flags_set |= IMAP_FLAG_ANSWERED;
3797 if ( MSG_IS_REPLIED(msginfo->flags) && !(newflags & MSG_REPLIED))
3798 flags_unset |= IMAP_FLAG_ANSWERED;
3800 if (!MSG_IS_DELETED(msginfo->flags) && (newflags & MSG_DELETED))
3801 flags_set |= IMAP_FLAG_DELETED;
3802 if ( MSG_IS_DELETED(msginfo->flags) && !(newflags & MSG_DELETED))
3803 flags_unset |= IMAP_FLAG_DELETED;
3805 if (!flags_set && !flags_unset) {
3806 /* the changed flags were not translatable to IMAP-speak.
3807 * like MSG_POSTFILTERED, so just apply. */
3808 msginfo->flags.perm_flags = newflags;
3812 debug_print("getting session...\n");
3813 session = imap_session_get(folder);
3818 if ((ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
3819 NULL, NULL, NULL, NULL, FALSE)) != IMAP_SUCCESS) {
3820 unlock_session(session);
3823 numlist.next = NULL;
3824 numlist.data = GINT_TO_POINTER(msginfo->msgnum);
3826 if (IMAP_FOLDER_ITEM(item)->batching) {
3827 /* instead of performing an UID STORE command for each message change,
3828 * as a lot of them can change "together", we just fill in hashtables
3829 * and defer the treatment so that we're able to send only one
3832 debug_print("IMAP batch mode on, deferring flags change\n");
3834 ht_data = g_hash_table_lookup(IMAP_FOLDER_ITEM(item)->flags_set_table,
3835 GINT_TO_POINTER(flags_set));
3836 if (ht_data == NULL) {
3837 ht_data = g_new0(hashtable_data, 1);
3838 ht_data->session = session;
3839 ht_data->item = IMAP_FOLDER_ITEM(item);
3840 g_hash_table_insert(IMAP_FOLDER_ITEM(item)->flags_set_table,
3841 GINT_TO_POINTER(flags_set), ht_data);
3843 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
3844 ht_data->msglist = g_slist_prepend(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum));
3847 ht_data = g_hash_table_lookup(IMAP_FOLDER_ITEM(item)->flags_unset_table,
3848 GINT_TO_POINTER(flags_unset));
3849 if (ht_data == NULL) {
3850 ht_data = g_new0(hashtable_data, 1);
3851 ht_data->session = session;
3852 ht_data->item = IMAP_FOLDER_ITEM(item);
3853 g_hash_table_insert(IMAP_FOLDER_ITEM(item)->flags_unset_table,
3854 GINT_TO_POINTER(flags_unset), ht_data);
3856 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
3857 ht_data->msglist = g_slist_prepend(ht_data->msglist,
3858 GINT_TO_POINTER(msginfo->msgnum));
3861 debug_print("IMAP changing flags\n");
3863 ok = imap_set_message_flags(session, &numlist, flags_set, TRUE);
3864 if (ok != IMAP_SUCCESS) {
3865 unlock_session(session);
3871 ok = imap_set_message_flags(session, &numlist, flags_unset, FALSE);
3872 if (ok != IMAP_SUCCESS) {
3873 unlock_session(session);
3878 msginfo->flags.perm_flags = newflags;
3879 unlock_session(session);
3883 static gint imap_remove_msg(Folder *folder, FolderItem *item, gint uid)
3886 IMAPSession *session;
3888 MsgNumberList numlist;
3890 g_return_val_if_fail(folder != NULL, -1);
3891 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3892 g_return_val_if_fail(item != NULL, -1);
3894 debug_print("getting session...\n");
3895 session = imap_session_get(folder);
3896 if (!session) return -1;
3898 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3899 NULL, NULL, NULL, NULL, FALSE);
3900 if (ok != IMAP_SUCCESS) {
3901 unlock_session(session);
3904 numlist.next = NULL;
3905 numlist.data = GINT_TO_POINTER(uid);
3907 ok = imap_set_message_flags
3908 (session, &numlist, IMAP_FLAG_DELETED, TRUE);
3909 if (ok != IMAP_SUCCESS) {
3910 log_warning(LOG_PROTOCOL, _("can't set deleted flags: %d\n"), uid);
3911 unlock_session(session);
3915 if (!session->uidplus) {
3916 ok = imap_cmd_expunge(session);
3920 uidstr = g_strdup_printf("%u", uid);
3921 ok = imap_cmd_expunge(session);
3924 if (ok != IMAP_SUCCESS) {
3925 log_warning(LOG_PROTOCOL, _("can't expunge\n"));
3926 unlock_session(session);
3930 IMAP_FOLDER_ITEM(item)->uid_list = g_slist_remove(
3931 IMAP_FOLDER_ITEM(item)->uid_list, numlist.data);
3932 dir = folder_item_get_path(item);
3933 if (is_dir_exist(dir))
3934 remove_numbered_files(dir, uid, uid);
3936 unlock_session(session);
3937 return IMAP_SUCCESS;
3940 static gint compare_msginfo(gconstpointer a, gconstpointer b)
3942 return ((MsgInfo *)a)->msgnum - ((MsgInfo *)b)->msgnum;
3945 static guint gslist_find_next_num(MsgNumberList **list, guint num)
3949 g_return_val_if_fail(list != NULL, -1);
3951 for (elem = *list; elem != NULL; elem = g_slist_next(elem))
3952 if (GPOINTER_TO_INT(elem->data) >= num)
3955 return elem != NULL ? GPOINTER_TO_INT(elem->data) : (gint)-1;
3959 * NEW and DELETED flags are not syncronized
3960 * - The NEW/RECENT flags in IMAP folders can not really be directly
3961 * modified by Sylpheed
3962 * - The DELETE/DELETED flag in IMAP and Sylpheed don't have the same
3963 * meaning, in IMAP it always removes the messages from the FolderItem
3964 * in Sylpheed it can mean to move the message to trash
3967 typedef struct _get_flags_data {
3970 MsgInfoList *msginfo_list;
3971 GRelation *msgflags;
3972 gboolean full_search;
3976 static /*gint*/ void *imap_get_flags_thread(void *data)
3978 get_flags_data *stuff = (get_flags_data *)data;
3979 Folder *folder = stuff->folder;
3980 FolderItem *fitem = (FolderItem *) stuff->item;
3981 MsgInfoList *msginfo_list = stuff->msginfo_list;
3982 GRelation *msgflags = stuff->msgflags;
3984 carray * lep_uidtab;
3985 IMAPSession *session;
3988 GHashTable *flags_hash = NULL;
3989 gboolean full_search = stuff->full_search;
3990 GSList *sorted_list = NULL;
3991 GSList *unseen = NULL, *answered = NULL, *flagged = NULL, *deleted = NULL;
3992 GSList *p_unseen, *p_answered, *p_flagged, *p_deleted;
3993 GSList *seq_list, *cur;
3994 gboolean reverse_seen = FALSE;
3995 gboolean selected_folder;
3996 gint exists_cnt, unseen_cnt;
3998 session = imap_session_get(folder);
3999 if (session == NULL) {
4001 return GINT_TO_POINTER(-1);
4003 selected_folder = (session->mbox != NULL) &&
4004 (!strcmp(session->mbox, fitem->path));
4006 if (!selected_folder) {
4007 ok = imap_select(session, IMAP_FOLDER(folder), fitem->path,
4008 &exists_cnt, NULL, &unseen_cnt, NULL, TRUE);
4009 if (ok != IMAP_SUCCESS) {
4011 unlock_session(session);
4012 return GINT_TO_POINTER(-1);
4015 if (unseen_cnt > exists_cnt / 2)
4016 reverse_seen = TRUE;
4019 if (fitem->unread_msgs > fitem->total_msgs / 2)
4020 reverse_seen = TRUE;
4023 sorted_list = g_slist_sort(g_slist_copy(msginfo_list), compare_msginfo);
4025 seq_list = imap_get_lep_set_from_msglist(msginfo_list);
4027 struct mailimap_set * set;
4028 set = mailimap_set_new_interval(1, 0);
4029 seq_list = g_slist_append(NULL, set);
4032 if (folder->account && folder->account->low_bandwidth) {
4033 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
4034 struct mailimap_set * imapset;
4035 clist * lep_uidlist;
4038 imapset = cur->data;
4040 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SEEN,
4041 full_search ? NULL:imapset, &lep_uidlist);
4044 r = imap_threaded_search(folder,
4045 IMAP_SEARCH_TYPE_UNSEEN,
4046 full_search ? NULL:imapset, &lep_uidlist);
4048 if (r == MAILIMAP_NO_ERROR) {
4051 uidlist = imap_uid_list_from_lep(lep_uidlist);
4052 mailimap_search_result_free(lep_uidlist);
4054 unseen = g_slist_concat(unseen, uidlist);
4057 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_FLAGGED,
4058 full_search ? NULL:imapset, &lep_uidlist);
4059 if (r == MAILIMAP_NO_ERROR) {
4062 uidlist = imap_uid_list_from_lep(lep_uidlist);
4063 mailimap_search_result_free(lep_uidlist);
4065 flagged = g_slist_concat(flagged, uidlist);
4068 if (fitem->opened || fitem->processing_pending || fitem == folder->inbox) {
4069 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_ANSWERED,
4070 full_search ? NULL:imapset, &lep_uidlist);
4071 if (r == MAILIMAP_NO_ERROR) {
4074 uidlist = imap_uid_list_from_lep(lep_uidlist);
4075 mailimap_search_result_free(lep_uidlist);
4077 answered = g_slist_concat(answered, uidlist);
4080 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_DELETED,
4081 full_search ? NULL:imapset, &lep_uidlist);
4082 if (r == MAILIMAP_NO_ERROR) {
4085 uidlist = imap_uid_list_from_lep(lep_uidlist);
4086 mailimap_search_result_free(lep_uidlist);
4088 deleted = g_slist_concat(deleted, uidlist);
4093 p_answered = answered;
4094 p_flagged = flagged;
4095 p_deleted = deleted;
4098 r = imap_threaded_fetch_uid_flags(folder, 1, &lep_uidtab);
4099 if (r == MAILIMAP_NO_ERROR) {
4100 flags_hash = g_hash_table_new_full(g_int_hash, g_int_equal, free, NULL);
4101 imap_flags_hash_from_lep_uid_flags_tab(lep_uidtab, flags_hash);
4102 imap_fetch_uid_flags_list_free(lep_uidtab);
4105 for (elem = sorted_list; elem != NULL; elem = g_slist_next(elem)) {
4107 MsgPermFlags flags, oldflags;
4110 msginfo = (MsgInfo *) elem->data;
4111 flags = msginfo->flags.perm_flags;
4112 wasnew = (flags & MSG_NEW);
4113 oldflags = flags & ~(MSG_NEW|MSG_UNREAD|MSG_REPLIED|MSG_MARKED|MSG_DELETED);
4115 if (folder->account && folder->account->low_bandwidth) {
4116 if (fitem->opened || fitem->processing_pending || fitem == folder->inbox) {
4117 flags &= ~((reverse_seen ? 0 : MSG_UNREAD | MSG_NEW) | MSG_REPLIED | MSG_MARKED);
4119 flags &= ~((reverse_seen ? 0 : MSG_UNREAD | MSG_NEW | MSG_MARKED));
4122 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
4123 if (gslist_find_next_num(&p_unseen, msginfo->msgnum) == msginfo->msgnum) {
4124 if (!reverse_seen) {
4125 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
4127 flags &= ~(MSG_UNREAD | MSG_NEW);
4131 if (gslist_find_next_num(&p_flagged, msginfo->msgnum) == msginfo->msgnum)
4132 flags |= MSG_MARKED;
4134 flags &= ~MSG_MARKED;
4136 if (fitem->opened || fitem->processing_pending || fitem == folder->inbox) {
4137 if (gslist_find_next_num(&p_answered, msginfo->msgnum) == msginfo->msgnum)
4138 flags |= MSG_REPLIED;
4140 flags &= ~MSG_REPLIED;
4141 if (gslist_find_next_num(&p_deleted, msginfo->msgnum) == msginfo->msgnum)
4142 flags |= MSG_DELETED;
4144 flags &= ~MSG_DELETED;
4147 if (flags_hash != NULL) {
4150 puid = malloc(sizeof(* puid));
4151 * puid = msginfo->msgnum;
4153 flags = GPOINTER_TO_INT(g_hash_table_lookup(flags_hash, puid));
4157 if ((flags & MSG_UNREAD) == 0)
4164 g_relation_insert(msgflags, msginfo, GINT_TO_POINTER(flags));
4168 g_hash_table_destroy(flags_hash);
4170 imap_lep_set_free(seq_list);
4171 g_slist_free(flagged);
4172 g_slist_free(deleted);
4173 g_slist_free(answered);
4174 g_slist_free(unseen);
4175 g_slist_free(sorted_list);
4177 unlock_session(session);
4179 return GINT_TO_POINTER(0);
4182 static gint imap_get_flags(Folder *folder, FolderItem *item,
4183 MsgInfoList *msginfo_list, GRelation *msgflags)
4186 get_flags_data *data = g_new0(get_flags_data, 1);
4188 data->folder = folder;
4190 data->msginfo_list = msginfo_list;
4191 data->msgflags = msgflags;
4192 data->full_search = FALSE;
4194 GSList *tmp = NULL, *cur;
4196 if (prefs_common.work_offline &&
4197 !inc_offline_should_override(FALSE,
4198 _("Claws Mail needs network access in order "
4199 "to access the IMAP server."))) {
4204 tmp = folder_item_get_msg_list(item);
4206 if (g_slist_length(tmp) <= g_slist_length(msginfo_list))
4207 data->full_search = TRUE;
4209 for (cur = tmp; cur; cur = cur->next)
4210 procmsg_msginfo_free((MsgInfo *)cur->data);
4214 result = GPOINTER_TO_INT(imap_get_flags_thread(data));
4221 static gboolean process_flags(gpointer key, gpointer value, gpointer user_data)
4223 gboolean flags_set = GPOINTER_TO_INT(user_data);
4224 gint flags_value = GPOINTER_TO_INT(key);
4225 hashtable_data *data = (hashtable_data *)value;
4226 IMAPFolderItem *_item = data->item;
4227 FolderItem *item = (FolderItem *)_item;
4228 gint ok = IMAP_ERROR;
4229 IMAPSession *session = NULL;
4231 debug_print("getting session...\n");
4232 session = imap_session_get(item->folder);
4234 data->msglist = g_slist_reverse(data->msglist);
4236 debug_print("IMAP %ssetting flags to %d for %d messages\n",
4239 g_slist_length(data->msglist));
4242 ok = imap_select(session, IMAP_FOLDER(item->folder), item->path,
4243 NULL, NULL, NULL, NULL, FALSE);
4245 if (ok == IMAP_SUCCESS) {
4246 imap_set_message_flags(data->session, data->msglist, flags_value, flags_set);
4248 g_warning("can't select mailbox %s\n", item->path);
4251 unlock_session(session);
4252 g_slist_free(data->msglist);
4257 static void process_hashtable(IMAPFolderItem *item)
4259 if (item->flags_set_table) {
4260 g_hash_table_foreach_remove(item->flags_set_table, process_flags, GINT_TO_POINTER(TRUE));
4261 g_hash_table_destroy(item->flags_set_table);
4262 item->flags_set_table = NULL;
4264 if (item->flags_unset_table) {
4265 g_hash_table_foreach_remove(item->flags_unset_table, process_flags, GINT_TO_POINTER(FALSE));
4266 g_hash_table_destroy(item->flags_unset_table);
4267 item->flags_unset_table = NULL;
4271 static void imap_set_batch (Folder *folder, FolderItem *_item, gboolean batch)
4273 IMAPFolderItem *item = (IMAPFolderItem *)_item;
4275 g_return_if_fail(item != NULL);
4277 if (item->batching == batch)
4281 item->batching = TRUE;
4282 debug_print("IMAP switching to batch mode\n");
4283 if (!item->flags_set_table) {
4284 item->flags_set_table = g_hash_table_new(NULL, g_direct_equal);
4286 if (!item->flags_unset_table) {
4287 item->flags_unset_table = g_hash_table_new(NULL, g_direct_equal);
4290 debug_print("IMAP switching away from batch mode\n");
4292 process_hashtable(item);
4293 item->batching = FALSE;
4299 /* data types conversion libetpan <-> claws */
4303 #define ETPAN_IMAP_MB_MARKED 1
4304 #define ETPAN_IMAP_MB_UNMARKED 2
4305 #define ETPAN_IMAP_MB_NOSELECT 4
4306 #define ETPAN_IMAP_MB_NOINFERIORS 8
4308 static int imap_flags_to_flags(struct mailimap_mbx_list_flags * imap_flags)
4314 if (imap_flags->mbf_type == MAILIMAP_MBX_LIST_FLAGS_SFLAG) {
4315 switch (imap_flags->mbf_sflag) {
4316 case MAILIMAP_MBX_LIST_SFLAG_MARKED:
4317 flags |= ETPAN_IMAP_MB_MARKED;
4319 case MAILIMAP_MBX_LIST_SFLAG_NOSELECT:
4320 flags |= ETPAN_IMAP_MB_NOSELECT;
4322 case MAILIMAP_MBX_LIST_SFLAG_UNMARKED:
4323 flags |= ETPAN_IMAP_MB_UNMARKED;
4328 for(cur = clist_begin(imap_flags->mbf_oflags) ; cur != NULL ;
4329 cur = clist_next(cur)) {
4330 struct mailimap_mbx_list_oflag * oflag;
4332 oflag = clist_content(cur);
4334 switch (oflag->of_type) {
4335 case MAILIMAP_MBX_LIST_OFLAG_NOINFERIORS:
4336 flags |= ETPAN_IMAP_MB_NOINFERIORS;
4344 static GSList * imap_list_from_lep(IMAPFolder * folder,
4345 clist * list, const gchar * real_path, gboolean all)
4348 GSList * item_list = NULL, *llast = NULL;
4350 for(iter = clist_begin(list) ; iter != NULL ;
4351 iter = clist_next(iter)) {
4352 struct mailimap_mailbox_list * mb;
4360 FolderItem *new_item;
4362 mb = clist_content(iter);
4368 if (mb->mb_flag != NULL)
4369 flags = imap_flags_to_flags(mb->mb_flag);
4371 delimiter = mb->mb_delimiter;
4374 dup_name = strdup(name);
4375 if (delimiter != '\0')
4376 subst_char(dup_name, delimiter, '/');
4378 base = g_path_get_basename(dup_name);
4379 if (base[0] == '.') {
4384 if (!all && path_cmp(name, real_path) == 0) {
4390 if (!all && dup_name[strlen(dup_name)-1] == '/') {
4391 dup_name[strlen(dup_name)-1] = '\0';
4394 loc_name = imap_modified_utf7_to_utf8(base);
4395 loc_path = imap_modified_utf7_to_utf8(dup_name);
4397 new_item = folder_item_new(FOLDER(folder), loc_name, loc_path);
4398 if ((flags & ETPAN_IMAP_MB_NOINFERIORS) != 0)
4399 new_item->no_sub = TRUE;
4400 if (strcmp(dup_name, "INBOX") != 0 &&
4401 ((flags & ETPAN_IMAP_MB_NOSELECT) != 0))
4402 new_item->no_select = TRUE;
4404 if (item_list == NULL)
4405 llast = item_list = g_slist_append(item_list, new_item);
4407 llast = g_slist_append(llast, new_item);
4408 llast = llast->next;
4410 debug_print("folder '%s' found.\n", loc_path);
4421 static GSList * imap_get_lep_set_from_numlist(MsgNumberList *numlist)
4423 GSList *sorted_list, *cur;
4424 guint first, last, next;
4425 GSList *ret_list = NULL, *llast = NULL;
4427 struct mailimap_set * current_set;
4428 unsigned int item_count;
4430 if (numlist == NULL)
4434 current_set = mailimap_set_new_empty();
4436 sorted_list = g_slist_copy(numlist);
4437 sorted_list = g_slist_sort(sorted_list, g_int_compare);
4439 first = GPOINTER_TO_INT(sorted_list->data);
4442 for (cur = sorted_list; cur != NULL; cur = g_slist_next(cur)) {
4443 if (GPOINTER_TO_INT(cur->data) == 0)
4448 last = GPOINTER_TO_INT(cur->data);
4450 next = GPOINTER_TO_INT(cur->next->data);
4454 if (last + 1 != next || next == 0) {
4456 struct mailimap_set_item * item;
4457 item = mailimap_set_item_new(first, last);
4458 mailimap_set_add(current_set, item);
4463 if (count >= IMAP_SET_MAX_COUNT) {
4464 if (ret_list == NULL)
4465 llast = ret_list = g_slist_append(ret_list,
4468 llast = g_slist_append(llast, current_set);
4469 llast = llast->next;
4471 current_set = mailimap_set_new_empty();
4478 if (clist_count(current_set->set_list) > 0) {
4479 ret_list = g_slist_append(ret_list,
4483 g_slist_free(sorted_list);
4488 static GSList * imap_get_lep_set_from_msglist(MsgInfoList *msglist)
4490 MsgNumberList *numlist = NULL;
4494 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
4495 MsgInfo *msginfo = (MsgInfo *) cur->data;
4497 numlist = g_slist_prepend(numlist, GINT_TO_POINTER(msginfo->msgnum));
4499 numlist = g_slist_reverse(numlist);
4500 seq_list = imap_get_lep_set_from_numlist(numlist);
4501 g_slist_free(numlist);
4506 static GSList * imap_uid_list_from_lep(clist * list)
4513 for(iter = clist_begin(list) ; iter != NULL ;
4514 iter = clist_next(iter)) {
4517 puid = clist_content(iter);
4518 result = g_slist_prepend(result, GINT_TO_POINTER(* puid));
4521 result = g_slist_reverse(result);
4525 static GSList * imap_uid_list_from_lep_tab(carray * list)
4532 for(i = 0 ; i < carray_count(list) ; i ++) {
4535 puid = carray_get(list, i);
4536 result = g_slist_prepend(result, GINT_TO_POINTER(* puid));
4538 result = g_slist_reverse(result);
4542 static void imap_flags_hash_from_lep_uid_flags_tab(carray * list,
4550 for(i = 0 ; i < carray_count(list) ; i += 2) {
4555 puid = carray_get(list, i);
4556 pflags = carray_get(list, i + 1);
4557 pguid = malloc(sizeof(* pguid));
4560 g_hash_table_insert(hash, pguid, GINT_TO_POINTER(* pflags));
4564 static MsgInfo *imap_envelope_from_lep(struct imap_fetch_env_info * info,
4567 MsgInfo *msginfo = NULL;
4570 MsgFlags flags = {0, 0};
4572 if (info->headers == NULL)
4575 MSG_SET_TMP_FLAGS(flags, MSG_IMAP);
4576 if (folder_has_parent_of_type(item, F_QUEUE)) {
4577 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
4578 } else if (folder_has_parent_of_type(item, F_DRAFT)) {
4579 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
4581 flags.perm_flags = info->flags;
4585 msginfo = procheader_parse_str(info->headers, flags, FALSE, FALSE);
4588 msginfo->msgnum = uid;
4589 msginfo->size = size;
4595 static void imap_lep_set_free(GSList *seq_list)
4599 for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) {
4600 struct mailimap_set * imapset;
4602 imapset = cur->data;
4603 mailimap_set_free(imapset);
4605 g_slist_free(seq_list);
4608 static struct mailimap_flag_list * imap_flag_to_lep(IMAPFlags flags)
4610 struct mailimap_flag_list * flag_list;
4612 flag_list = mailimap_flag_list_new_empty();
4614 if (IMAP_IS_SEEN(flags))
4615 mailimap_flag_list_add(flag_list,
4616 mailimap_flag_new_seen());
4617 if (IMAP_IS_ANSWERED(flags))
4618 mailimap_flag_list_add(flag_list,
4619 mailimap_flag_new_answered());
4620 if (IMAP_IS_FLAGGED(flags))
4621 mailimap_flag_list_add(flag_list,
4622 mailimap_flag_new_flagged());
4623 if (IMAP_IS_DELETED(flags))
4624 mailimap_flag_list_add(flag_list,
4625 mailimap_flag_new_deleted());
4626 if (IMAP_IS_DRAFT(flags))
4627 mailimap_flag_list_add(flag_list,
4628 mailimap_flag_new_draft());
4633 guint imap_folder_get_refcnt(Folder *folder)
4635 return ((IMAPFolder *)folder)->refcnt;
4638 void imap_folder_ref(Folder *folder)
4640 ((IMAPFolder *)folder)->refcnt++;
4643 void imap_disconnect_all(void)
4646 for (list = account_get_list(); list != NULL; list = list->next) {
4647 PrefsAccount *account = list->data;
4648 if (account->protocol == A_IMAP4) {
4649 RemoteFolder *folder = (RemoteFolder *)account->folder;
4650 if (folder && folder->session) {
4651 IMAPSession *session = (IMAPSession *)folder->session;
4652 imap_threaded_disconnect(FOLDER(folder));
4653 SESSION(session)->state = SESSION_DISCONNECTED;
4654 session_destroy(SESSION(session));
4655 folder->session = NULL;
4661 void imap_folder_unref(Folder *folder)
4663 if (((IMAPFolder *)folder)->refcnt > 0)
4664 ((IMAPFolder *)folder)->refcnt--;
4667 void imap_cancel_all(void)
4672 folderlist = folder_get_list();
4673 for (cur = folderlist; cur != NULL; cur = g_list_next(cur)) {
4674 Folder *folder = (Folder *) cur->data;
4676 if (folder->klass == &imap_class) {
4677 if (imap_is_busy(folder)) {
4678 IMAPSession *imap_session;
4679 RemoteFolder *rfolder;
4681 fprintf(stderr, "cancelled\n");
4682 imap_threaded_cancel(folder);
4683 rfolder = (RemoteFolder *) folder;
4684 imap_session = (IMAPSession *) rfolder->session;
4686 imap_session->cancelled = 1;
4692 gboolean imap_cancel_all_enabled(void)
4697 folderlist = folder_get_list();
4698 for (cur = folderlist; cur != NULL; cur = g_list_next(cur)) {
4699 Folder *folder = (Folder *) cur->data;
4701 if (folder->klass == &imap_class) {
4702 if (imap_is_busy(folder)) {
4711 static gboolean imap_is_busy(Folder *folder)
4713 IMAPSession *imap_session;
4714 RemoteFolder *rfolder;
4716 rfolder = (RemoteFolder *) folder;
4717 imap_session = (IMAPSession *) rfolder->session;
4718 if (imap_session == NULL)
4721 return imap_session->busy;
4724 #else /* HAVE_LIBETPAN */
4726 static FolderClass imap_class;
4728 static XMLTag *imap_item_get_xml(Folder *folder, FolderItem *item);
4729 static void imap_item_set_xml(Folder *folder, FolderItem *item, XMLTag *tag);
4731 static Folder *imap_folder_new (const gchar *name,
4734 static gboolean missing_imap_warning = TRUE;
4735 if (missing_imap_warning) {
4736 missing_imap_warning = FALSE;
4738 _("You have one or more IMAP accounts "
4739 "defined. However this version of "
4740 "Claws Mail has been built without "
4741 "IMAP support; your IMAP account(s) are "
4743 "You probably need to "
4744 "install libetpan and recompile "
4749 static gint imap_create_tree (Folder *folder)
4753 static FolderItem *imap_create_folder (Folder *folder,
4759 static gint imap_rename_folder (Folder *folder,
4766 gchar imap_get_path_separator_for_item(FolderItem *item)
4771 FolderClass *imap_get_class(void)
4773 if (imap_class.idstr == NULL) {
4774 imap_class.type = F_IMAP;
4775 imap_class.idstr = "imap";
4776 imap_class.uistr = "IMAP4";
4778 imap_class.new_folder = imap_folder_new;
4779 imap_class.create_tree = imap_create_tree;
4780 imap_class.create_folder = imap_create_folder;
4781 imap_class.rename_folder = imap_rename_folder;
4783 imap_class.set_xml = folder_set_xml;
4784 imap_class.get_xml = folder_get_xml;
4785 imap_class.item_set_xml = imap_item_set_xml;
4786 imap_class.item_get_xml = imap_item_get_xml;
4787 /* nothing implemented */
4793 void imap_disconnect_all(void)
4797 gint imap_scan_tree_real(Folder *folder, gboolean subs_only)
4802 gint imap_subscribe(Folder *folder, FolderItem *item, gchar *rpath, gboolean sub)
4807 GList * imap_scan_subtree(Folder *folder, FolderItem *item, gboolean unsubs_only, gboolean recursive)
4812 void imap_cache_msg(FolderItem *item, gint msgnum)
4816 void imap_cancel_all(void)
4820 gboolean imap_cancel_all_enabled(void)
4827 void imap_synchronise(FolderItem *item)
4829 #ifdef HAVE_LIBETPAN
4830 if (IMAP_FOLDER_ITEM(item)->last_sync == IMAP_FOLDER_ITEM(item)->last_change) {
4831 debug_print("%s already synced\n", item->path?item->path:item->name);
4834 debug_print("syncing %s\n", item->path?item->path:item->name);
4835 imap_gtk_synchronise(item);
4836 IMAP_FOLDER_ITEM(item)->last_sync = IMAP_FOLDER_ITEM(item)->last_change;
4840 static void imap_item_set_xml(Folder *folder, FolderItem *item, XMLTag *tag)
4842 #ifdef HAVE_LIBETPAN
4845 folder_item_set_xml(folder, item, tag);
4847 #ifdef HAVE_LIBETPAN
4848 for (cur = tag->attr; cur != NULL; cur = g_list_next(cur)) {
4849 XMLAttr *attr = (XMLAttr *) cur->data;
4851 if (!attr || !attr->name || !attr->value) continue;
4852 if (!strcmp(attr->name, "uidnext"))
4853 IMAP_FOLDER_ITEM(item)->uid_next = atoi(attr->value);
4854 if (!strcmp(attr->name, "last_sync"))
4855 IMAP_FOLDER_ITEM(item)->last_sync = atoi(attr->value);
4856 if (!strcmp(attr->name, "last_change"))
4857 IMAP_FOLDER_ITEM(item)->last_change = atoi(attr->value);
4859 if (IMAP_FOLDER_ITEM(item)->last_change == 0)
4860 IMAP_FOLDER_ITEM(item)->last_change = time(NULL);
4864 static XMLTag *imap_item_get_xml(Folder *folder, FolderItem *item)
4868 tag = folder_item_get_xml(folder, item);
4870 #ifdef HAVE_LIBETPAN
4871 xml_tag_add_attr(tag, xml_attr_new_int("uidnext",
4872 IMAP_FOLDER_ITEM(item)->uid_next));
4873 xml_tag_add_attr(tag, xml_attr_new_int("last_sync",
4874 IMAP_FOLDER_ITEM(item)->last_sync));
4875 xml_tag_add_attr(tag, xml_attr_new_int("last_change",
4876 IMAP_FOLDER_ITEM(item)->last_change));