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 GSList * imap_uid_list_from_lep_uid_flags_tab(carray * list);
405 static void imap_flags_hash_from_lep_uid_flags_tab(carray * list,
407 static MsgInfo *imap_envelope_from_lep(struct imap_fetch_env_info * info,
409 static void imap_lep_set_free(GSList *seq_list);
410 static struct mailimap_flag_list * imap_flag_to_lep(IMAPFlags flags);
412 typedef struct _hashtable_data {
413 IMAPSession *session;
415 IMAPFolderItem *item;
418 static FolderClass imap_class;
420 typedef struct _thread_data {
430 FolderClass *imap_get_class(void)
432 if (imap_class.idstr == NULL) {
433 imap_class.type = F_IMAP;
434 imap_class.idstr = "imap";
435 imap_class.uistr = "IMAP4";
437 /* Folder functions */
438 imap_class.new_folder = imap_folder_new;
439 imap_class.destroy_folder = imap_folder_destroy;
440 imap_class.scan_tree = imap_scan_tree;
441 imap_class.create_tree = imap_create_tree;
443 /* FolderItem functions */
444 imap_class.item_new = imap_folder_item_new;
445 imap_class.item_destroy = imap_folder_item_destroy;
446 imap_class.item_get_path = imap_item_get_path;
447 imap_class.create_folder = imap_create_folder;
448 imap_class.rename_folder = imap_rename_folder;
449 imap_class.remove_folder = imap_remove_folder;
450 imap_class.close = imap_close;
451 imap_class.get_num_list = imap_get_num_list;
452 imap_class.scan_required = imap_scan_required;
453 imap_class.set_xml = folder_set_xml;
454 imap_class.get_xml = folder_get_xml;
455 imap_class.item_set_xml = imap_item_set_xml;
456 imap_class.item_get_xml = imap_item_get_xml;
458 /* Message functions */
459 imap_class.get_msginfo = imap_get_msginfo;
460 imap_class.get_msginfos = imap_get_msginfos;
461 imap_class.fetch_msg = imap_fetch_msg;
462 imap_class.fetch_msg_full = imap_fetch_msg_full;
463 imap_class.add_msg = imap_add_msg;
464 imap_class.add_msgs = imap_add_msgs;
465 imap_class.copy_msg = imap_copy_msg;
466 imap_class.copy_msgs = imap_copy_msgs;
467 imap_class.remove_msg = imap_remove_msg;
468 imap_class.remove_msgs = imap_remove_msgs;
469 imap_class.remove_all_msg = imap_remove_all_msg;
470 imap_class.is_msg_changed = imap_is_msg_changed;
471 imap_class.change_flags = imap_change_flags;
472 imap_class.get_flags = imap_get_flags;
473 imap_class.set_batch = imap_set_batch;
474 imap_class.synchronise = imap_synchronise;
476 pthread_mutex_init(&imap_mutex, NULL);
483 static Folder *imap_folder_new(const gchar *name, const gchar *path)
487 folder = (Folder *)g_new0(IMAPFolder, 1);
488 folder->klass = &imap_class;
489 imap_folder_init(folder, name, path);
494 static void imap_folder_destroy(Folder *folder)
496 while (imap_folder_get_refcnt(folder) > 0)
497 gtk_main_iteration();
499 folder_remote_folder_destroy(REMOTE_FOLDER(folder));
503 static void imap_folder_init(Folder *folder, const gchar *name,
506 folder_remote_folder_init((Folder *)folder, name, path);
509 static FolderItem *imap_folder_item_new(Folder *folder)
511 IMAPFolderItem *item;
513 item = g_new0(IMAPFolderItem, 1);
516 item->uid_list = NULL;
518 return (FolderItem *)item;
521 static void imap_folder_item_destroy(Folder *folder, FolderItem *_item)
523 IMAPFolderItem *item = (IMAPFolderItem *)_item;
525 g_return_if_fail(item != NULL);
526 g_slist_free(item->uid_list);
531 static gboolean imap_reset_uid_lists_func(GNode *node, gpointer data)
533 IMAPFolderItem *item = (IMAPFolderItem *)node->data;
536 g_slist_free(item->uid_list);
537 item->uid_list = NULL;
542 static void imap_reset_uid_lists(Folder *folder)
544 if(folder->node == NULL)
547 /* Destroy all uid lists and rest last uid */
548 g_node_traverse(folder->node, G_IN_ORDER, G_TRAVERSE_ALL, -1, imap_reset_uid_lists_func, NULL);
551 static int imap_get_capabilities(IMAPSession *session)
553 struct mailimap_capability_data *capabilities = NULL;
557 if (session->capability != NULL)
558 return MAILIMAP_NO_ERROR;
560 capabilities = imap_threaded_capability(session->folder, &result);
562 if (result != MAILIMAP_NO_ERROR) {
563 return MAILIMAP_ERROR_CAPABILITY;
566 if (capabilities == NULL) {
567 return MAILIMAP_NO_ERROR;
570 for(cur = clist_begin(capabilities->cap_list) ; cur != NULL ;
571 cur = clist_next(cur)) {
572 struct mailimap_capability * cap =
574 if (!cap || cap->cap_data.cap_name == NULL)
576 session->capability = g_slist_append
577 (session->capability,
578 g_strdup(cap->cap_data.cap_name));
579 debug_print("got capa %s\n", cap->cap_data.cap_name);
581 mailimap_capability_data_free(capabilities);
582 return MAILIMAP_NO_ERROR;
585 static gboolean imap_has_capability(IMAPSession *session, const gchar *cap)
588 for (cur = session->capability; cur; cur = cur->next) {
589 if (!g_ascii_strcasecmp(cur->data, cap))
595 static gint imap_auth(IMAPSession *session, const gchar *user, const gchar *pass,
598 gint ok = IMAP_ERROR;
599 static time_t last_login_err = 0;
600 gchar *ext_info = "";
602 if (imap_get_capabilities(session) != MAILIMAP_NO_ERROR)
607 ok = imap_cmd_login(session, user, pass, "ANONYMOUS");
609 case IMAP_AUTH_CRAM_MD5:
610 ok = imap_cmd_login(session, user, pass, "CRAM-MD5");
612 case IMAP_AUTH_LOGIN:
613 ok = imap_cmd_login(session, user, pass, "LOGIN");
615 case IMAP_AUTH_GSSAPI:
616 ok = imap_cmd_login(session, user, pass, "GSSAPI");
619 debug_print("capabilities:\n"
624 imap_has_capability(session, "ANONYMOUS"),
625 imap_has_capability(session, "CRAM-MD5"),
626 imap_has_capability(session, "LOGIN"),
627 imap_has_capability(session, "GSSAPI"));
628 if (imap_has_capability(session, "CRAM-MD5"))
629 ok = imap_cmd_login(session, user, pass, "CRAM-MD5");
630 if (ok == IMAP_ERROR && imap_has_capability(session, "GSSAPI"))
631 ok = imap_cmd_login(session, user, pass, "GSSAPI");
632 if (ok == IMAP_ERROR) /* we always try LOGIN before giving up */
633 ok = imap_cmd_login(session, user, pass, "LOGIN");
636 if (ok == IMAP_SUCCESS)
637 session->authenticated = TRUE;
639 if (type == IMAP_AUTH_CRAM_MD5) {
640 ext_info = _("\n\nCRAM-MD5 logins only work if libetpan has been "
641 "compiled with SASL support and the "
642 "CRAM-MD5 SASL plugin is installed.");
645 if (time(NULL) - last_login_err > 10) {
646 if (!prefs_common.no_recv_err_panel) {
647 alertpanel_error(_("Connection to %s failed: "
649 SESSION(session)->server, ext_info);
651 log_error(LOG_PROTOCOL, _("Connection to %s failed: "
652 "login refused.%s\n"),
653 SESSION(session)->server, ext_info);
656 last_login_err = time(NULL);
661 static IMAPSession *imap_reconnect_if_possible(Folder *folder, IMAPSession *session)
663 RemoteFolder *rfolder = REMOTE_FOLDER(folder);
664 /* Check if this is the first try to establish a
665 connection, if yes we don't try to reconnect */
666 debug_print("reconnecting\n");
667 if (rfolder->session == NULL) {
668 log_warning(LOG_PROTOCOL, _("Connecting to %s failed"),
669 folder->account->recv_server);
670 session_destroy(SESSION(session));
673 log_warning(LOG_PROTOCOL, _("IMAP4 connection to %s has been"
674 " disconnected. Reconnecting...\n"),
675 folder->account->recv_server);
676 statusbar_print_all(_("IMAP4 connection to %s has been"
677 " disconnected. Reconnecting...\n"),
678 folder->account->recv_server);
679 SESSION(session)->state = SESSION_DISCONNECTED;
680 session_destroy(SESSION(session));
681 /* Clear folders session to make imap_session_get create
682 a new session, because of rfolder->session == NULL
683 it will not try to reconnect again and so avoid an
685 rfolder->session = NULL;
686 debug_print("getting session...\n");
687 session = imap_session_get(folder);
688 rfolder->session = SESSION(session);
694 static void lock_session(IMAPSession *session)
699 debug_print("locking session %p (%d)\n", session, session->busy);
701 debug_print(" SESSION WAS LOCKED !! \n");
702 session->busy = TRUE;
703 mainwin = mainwindow_get_mainwindow();
705 toolbar_main_set_sensitive(mainwin);
706 main_window_set_menu_sensitive(mainwin);
709 debug_print("can't lock null session\n");
713 static void unlock_session(IMAPSession *session)
718 debug_print("unlocking session %p\n", session);
719 session->busy = FALSE;
720 mainwin = mainwindow_get_mainwindow();
722 toolbar_main_set_sensitive(mainwin);
723 main_window_set_menu_sensitive(mainwin);
726 debug_print("can't unlock null session\n");
730 static IMAPSession *imap_session_get(Folder *folder)
732 RemoteFolder *rfolder = REMOTE_FOLDER(folder);
733 IMAPSession *session = NULL;
735 g_return_val_if_fail(folder != NULL, NULL);
736 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, NULL);
737 g_return_val_if_fail(folder->account != NULL, NULL);
739 if (prefs_common.work_offline &&
740 !inc_offline_should_override(FALSE,
741 _("Claws Mail needs network access in order "
742 "to access the IMAP server."))) {
746 /* Make sure we have a session */
747 if (rfolder->session != NULL) {
748 session = IMAP_SESSION(rfolder->session);
749 } else if (rfolder->connecting) {
750 debug_print("already connecting\n");
753 imap_reset_uid_lists(folder);
754 if (time(NULL) - rfolder->last_failure <= 2)
756 rfolder->connecting = TRUE;
757 session = imap_session_new(folder, folder->account);
759 if(session == NULL) {
760 rfolder->last_failure = time(NULL);
761 rfolder->connecting = FALSE;
765 /* Make sure session is authenticated */
766 if (!IMAP_SESSION(session)->authenticated)
767 imap_session_authenticate(IMAP_SESSION(session), folder->account);
769 if (!IMAP_SESSION(session)->authenticated) {
770 imap_threaded_disconnect(session->folder);
771 SESSION(session)->state = SESSION_DISCONNECTED;
772 session_destroy(SESSION(session));
773 rfolder->session = NULL;
774 rfolder->last_failure = time(NULL);
775 rfolder->connecting = FALSE;
779 lock_session(session);
781 /* I think the point of this code is to avoid sending a
782 * keepalive if we've used the session recently and therefore
783 * think it's still alive. Unfortunately, most of the code
784 * does not yet check for errors on the socket, and so if the
785 * connection drops we don't notice until the timeout expires.
786 * A better solution than sending a NOOP every time would be
787 * for every command to be prepared to retry until it is
788 * successfully sent. -- mbp */
789 if ((time(NULL) - SESSION(session)->last_access_time > SESSION_TIMEOUT_INTERVAL) || session->cancelled) {
790 /* verify that the session is still alive */
791 if (imap_cmd_noop(session) != IMAP_SUCCESS) {
792 debug_print("disconnected!\n");
793 session = imap_reconnect_if_possible(folder, session);
796 session->cancelled = FALSE;
799 rfolder->session = SESSION(session);
800 rfolder->connecting = FALSE;
802 return IMAP_SESSION(session);
805 static IMAPSession *imap_session_new(Folder * folder,
806 const PrefsAccount *account)
808 IMAPSession *session;
811 int authenticated = FALSE;
814 /* FIXME: IMAP over SSL only... */
817 port = account->set_imapport ? account->imapport
818 : account->ssl_imap == SSL_TUNNEL ? IMAPS_PORT : IMAP4_PORT;
819 ssl_type = account->ssl_imap;
821 if (account->ssl_imap != SSL_NONE) {
822 if (alertpanel_full(_("Insecure connection"),
823 _("This connection is configured to be secured "
824 "using SSL, but SSL is not available in this "
825 "build of Claws Mail. \n\n"
826 "Do you want to continue connecting to this "
827 "server? The communication would not be "
829 GTK_STOCK_CANCEL, _("Con_tinue connecting"),
830 NULL, FALSE, NULL, ALERT_WARNING,
831 G_ALERTDEFAULT) != G_ALERTALTERNATE)
834 port = account->set_imapport ? account->imapport
839 statusbar_print_all(_("Connecting to IMAP4 server: %s..."), folder->account->recv_server);
840 if (account->set_tunnelcmd) {
841 r = imap_threaded_connect_cmd(folder,
843 account->recv_server,
848 if (ssl_type == SSL_TUNNEL) {
849 r = imap_threaded_connect_ssl(folder,
850 account->recv_server,
856 r = imap_threaded_connect(folder,
857 account->recv_server,
863 if (r == MAILIMAP_NO_ERROR_AUTHENTICATED) {
864 authenticated = TRUE;
866 else if (r == MAILIMAP_NO_ERROR_NON_AUTHENTICATED) {
867 authenticated = FALSE;
870 #if (LIBETPAN_VERSION_MAJOR > 0 || LIBETPAN_VERSION_MINOR > 48)
872 if (r == MAILIMAP_ERROR_SSL)
873 log_error(LOG_PROTOCOL, _("SSL handshake failed\n"));
876 if(!prefs_common.no_recv_err_panel) {
877 alertpanel_error_log(_("Can't connect to IMAP4 server: %s:%d"),
878 account->recv_server, port);
880 log_error(LOG_PROTOCOL, _("Can't connect to IMAP4 server: %s:%d\n"),
881 account->recv_server, port);
887 session = g_new0(IMAPSession, 1);
888 session_init(SESSION(session));
889 SESSION(session)->type = SESSION_IMAP;
890 SESSION(session)->server = g_strdup(account->recv_server);
891 SESSION(session)->sock = NULL;
893 SESSION(session)->destroy = imap_session_destroy;
895 session->capability = NULL;
897 session->authenticated = authenticated;
898 session->mbox = NULL;
899 session->cmd_count = 0;
900 session->folder = folder;
901 IMAP_FOLDER(session->folder)->last_seen_separator = 0;
904 if (account->ssl_imap == SSL_STARTTLS) {
907 ok = imap_cmd_starttls(session);
908 if (ok != IMAP_SUCCESS) {
909 log_warning(LOG_PROTOCOL, _("Can't start TLS session.\n"));
910 session_destroy(SESSION(session));
914 imap_free_capabilities(session);
915 session->authenticated = FALSE;
916 session->uidplus = FALSE;
917 session->cmd_count = 1;
920 log_message(LOG_PROTOCOL, "IMAP connection is %s-authenticated\n",
921 (session->authenticated) ? "pre" : "un");
926 static void imap_session_authenticate(IMAPSession *session,
927 const PrefsAccount *account)
929 gchar *pass, *acc_pass;
930 gboolean failed = FALSE;
932 g_return_if_fail(account->userid != NULL);
933 acc_pass = account->passwd;
936 if (!pass && account->imap_auth_type != IMAP_AUTH_ANON) {
938 tmp_pass = input_dialog_query_password(account->recv_server, account->userid);
941 Xstrdup_a(pass, tmp_pass, {g_free(tmp_pass); return;});
943 } else if (account->imap_auth_type == IMAP_AUTH_ANON) {
946 statusbar_print_all(_("Connecting to IMAP4 server %s...\n"),
947 account->recv_server);
948 if (imap_auth(session, account->userid, pass, account->imap_auth_type) != IMAP_SUCCESS) {
956 if (prefs_common.no_recv_err_panel) {
957 log_error(LOG_PROTOCOL, _("Couldn't login to IMAP server %s."), account->recv_server);
958 mainwindow_show_error();
960 alertpanel_error_log(_("Couldn't login to IMAP server %s."), account->recv_server);
967 session->authenticated = TRUE;
971 static void imap_session_destroy(Session *session)
973 if (session->state != SESSION_DISCONNECTED)
974 imap_threaded_disconnect(IMAP_SESSION(session)->folder);
976 imap_free_capabilities(IMAP_SESSION(session));
977 g_free(IMAP_SESSION(session)->mbox);
978 sock_close(session->sock);
979 session->sock = NULL;
982 static gchar *imap_fetch_msg(Folder *folder, FolderItem *item, gint uid)
984 return imap_fetch_msg_full(folder, item, uid, TRUE, TRUE);
987 static guint get_file_size_with_crs(const gchar *filename)
993 if (filename == NULL)
996 fp = fopen(filename, "rb");
1000 while (fgets(buf, sizeof (buf), fp) != NULL) {
1002 if (!strstr(buf, "\r\n") && strstr(buf, "\n"))
1010 static gchar *imap_fetch_msg_full(Folder *folder, FolderItem *item, gint uid,
1011 gboolean headers, gboolean body)
1013 gchar *path, *filename;
1014 IMAPSession *session;
1017 g_return_val_if_fail(folder != NULL, NULL);
1018 g_return_val_if_fail(item != NULL, NULL);
1023 path = folder_item_get_path(item);
1024 if (!is_dir_exist(path))
1025 make_dir_hier(path);
1026 filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(uid), NULL);
1028 debug_print("trying to fetch cached %s\n", filename);
1029 if (is_file_exist(filename)) {
1030 /* see whether the local file represents the whole message
1031 * or not. As the IMAP server reports size with \r chars,
1032 * we have to update the local file (UNIX \n only) size */
1033 MsgInfo *cached = msgcache_get_msg(item->cache,uid);
1034 guint have_size = get_file_size_with_crs(filename);
1037 debug_print("message %d has been already %scached (%d/%d).\n", uid,
1038 have_size >= cached->size ? "fully ":"",
1039 have_size, (int)cached->size);
1041 if (cached && (cached->size <= have_size || !body)) {
1042 procmsg_msginfo_free(cached);
1043 file_strip_crs(filename);
1045 } else if (!cached && time(NULL) - get_file_mtime(filename) < 60) {
1046 debug_print("message not cached and file recent, considering file complete\n");
1047 file_strip_crs(filename);
1050 procmsg_msginfo_free(cached);
1054 debug_print("getting session...\n");
1055 session = imap_session_get(folder);
1062 debug_print("IMAP fetching messages\n");
1063 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
1064 NULL, NULL, NULL, NULL, FALSE);
1065 if (ok != IMAP_SUCCESS) {
1066 g_warning("can't select mailbox %s\n", item->path);
1068 unlock_session(session);
1072 debug_print("getting message %d...\n", uid);
1073 ok = imap_cmd_fetch(session, (guint32)uid, filename, headers, body);
1075 if (ok != IMAP_SUCCESS) {
1076 g_warning("can't fetch message %d\n", uid);
1078 unlock_session(session);
1082 unlock_session(session);
1083 file_strip_crs(filename);
1087 static gboolean imap_is_msg_fully_cached(Folder *folder, FolderItem *item, gint uid)
1089 gchar *path, *filename;
1091 MsgInfo *cached = msgcache_get_msg(item->cache,uid);
1096 path = folder_item_get_path(item);
1097 if (!is_dir_exist(path))
1100 filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(uid), NULL);
1102 if (is_file_exist(filename)) {
1103 if (cached && cached->total_size == cached->size) {
1108 size = get_file_size_with_crs(filename);
1111 if (cached && size >= cached->size) {
1112 cached->total_size = cached->size;
1113 procmsg_msginfo_free(cached);
1117 procmsg_msginfo_free(cached);
1121 void imap_cache_msg(FolderItem *item, gint msgnum)
1123 Folder *folder = NULL;
1127 folder = item->folder;
1129 if (!imap_is_msg_fully_cached(folder, item, msgnum)) {
1130 gchar *tmp = imap_fetch_msg_full(folder, item, msgnum, TRUE, TRUE);
1131 debug_print("fetched %s\n", tmp);
1136 static gint imap_add_msg(Folder *folder, FolderItem *dest,
1137 const gchar *file, MsgFlags *flags)
1141 MsgFileInfo fileinfo;
1143 g_return_val_if_fail(file != NULL, -1);
1145 fileinfo.msginfo = NULL;
1146 fileinfo.file = (gchar *)file;
1147 fileinfo.flags = flags;
1148 file_list.data = &fileinfo;
1149 file_list.next = NULL;
1151 ret = imap_add_msgs(folder, dest, &file_list, NULL);
1155 static gint imap_add_msgs(Folder *folder, FolderItem *dest, GSList *file_list,
1156 GRelation *relation)
1159 IMAPSession *session;
1160 guint32 last_uid = 0;
1162 MsgFileInfo *fileinfo;
1164 gint curnum = 0, total = 0;
1167 g_return_val_if_fail(folder != NULL, -1);
1168 g_return_val_if_fail(dest != NULL, -1);
1169 g_return_val_if_fail(file_list != NULL, -1);
1171 debug_print("getting session...\n");
1172 session = imap_session_get(folder);
1176 destdir = imap_get_real_path(session, IMAP_FOLDER(folder), dest->path);
1178 statusbar_print_all(_("Adding messages..."));
1179 total = g_slist_length(file_list);
1180 for (cur = file_list; cur != NULL; cur = cur->next) {
1181 IMAPFlags iflags = 0;
1182 guint32 new_uid = 0;
1183 gchar *real_file = NULL;
1184 fileinfo = (MsgFileInfo *)cur->data;
1186 statusbar_progress_all(curnum, total, total < 10 ? 1:10);
1189 if (fileinfo->flags) {
1190 if (MSG_IS_MARKED(*fileinfo->flags))
1191 iflags |= IMAP_FLAG_FLAGGED;
1192 if (MSG_IS_REPLIED(*fileinfo->flags))
1193 iflags |= IMAP_FLAG_ANSWERED;
1194 if (!MSG_IS_UNREAD(*fileinfo->flags))
1195 iflags |= IMAP_FLAG_SEEN;
1198 if (real_file == NULL)
1199 real_file = g_strdup(fileinfo->file);
1201 if (folder_has_parent_of_type(dest, F_QUEUE) ||
1202 folder_has_parent_of_type(dest, F_OUTBOX) ||
1203 folder_has_parent_of_type(dest, F_DRAFT) ||
1204 folder_has_parent_of_type(dest, F_TRASH))
1205 iflags |= IMAP_FLAG_SEEN;
1207 ok = imap_cmd_append(session, destdir, real_file, iflags,
1210 if (ok != IMAP_SUCCESS) {
1211 g_warning("can't append message %s\n", real_file);
1214 unlock_session(session);
1215 statusbar_progress_all(0,0,0);
1216 statusbar_pop_all();
1219 debug_print("appended new message as %d\n", new_uid);
1220 /* put the local file in the imapcache, so that we don't
1221 * have to fetch it back later. */
1223 gchar *cache_path = folder_item_get_path(dest);
1224 if (!is_dir_exist(cache_path))
1225 make_dir_hier(cache_path);
1226 if (is_dir_exist(cache_path)) {
1227 gchar *cache_file = g_strconcat(
1228 cache_path, G_DIR_SEPARATOR_S,
1229 itos(new_uid), NULL);
1230 copy_file(real_file, cache_file, TRUE);
1231 debug_print("copied to cache: %s\n", cache_file);
1238 if (relation != NULL)
1239 g_relation_insert(relation, fileinfo->msginfo != NULL ?
1240 (gpointer) fileinfo->msginfo : (gpointer) fileinfo,
1241 GINT_TO_POINTER(dest->last_num + 1));
1243 new_uid = dest->last_num+1;
1245 if (last_uid < new_uid) {
1251 statusbar_progress_all(0,0,0);
1252 statusbar_pop_all();
1254 imap_cmd_expunge(session);
1255 unlock_session(session);
1262 static gint imap_do_copy_msgs(Folder *folder, FolderItem *dest,
1263 MsgInfoList *msglist, GRelation *relation)
1267 GSList *seq_list, *cur;
1269 IMAPSession *session;
1270 gint ok = IMAP_SUCCESS;
1271 GRelation *uid_mapping;
1273 gboolean single = FALSE;
1275 g_return_val_if_fail(folder != NULL, -1);
1276 g_return_val_if_fail(dest != NULL, -1);
1277 g_return_val_if_fail(msglist != NULL, -1);
1279 debug_print("getting session...\n");
1280 session = imap_session_get(folder);
1286 msginfo = (MsgInfo *)msglist->data;
1287 if (msglist->next == NULL)
1289 src = msginfo->folder;
1291 g_warning("the src folder is identical to the dest.\n");
1292 unlock_session(session);
1296 if (src->folder != dest->folder) {
1297 GSList *infolist = NULL, *cur;
1299 for (cur = msglist; cur; cur = cur->next) {
1300 msginfo = (MsgInfo *)cur->data;
1301 MsgFileInfo *fileinfo = g_new0(MsgFileInfo, 1);
1302 fileinfo->file = procmsg_get_message_file(msginfo);
1303 fileinfo->flags = &(msginfo->flags);
1304 infolist = g_slist_prepend(infolist, fileinfo);
1306 infolist = g_slist_reverse(infolist);
1307 unlock_session(session);
1308 res = folder_item_add_msgs(dest, infolist, FALSE);
1309 for (cur = infolist; cur; cur = cur->next) {
1310 MsgFileInfo *info = (MsgFileInfo *)cur->data;
1314 g_slist_free(infolist);
1318 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
1319 NULL, NULL, NULL, NULL, FALSE);
1320 if (ok != IMAP_SUCCESS) {
1321 unlock_session(session);
1325 destdir = imap_get_real_path(session, IMAP_FOLDER(folder), dest->path);
1326 seq_list = imap_get_lep_set_from_msglist(msglist);
1327 uid_mapping = g_relation_new(2);
1328 g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
1330 statusbar_print_all(_("Copying messages..."));
1331 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
1332 struct mailimap_set * seq_set;
1333 struct mailimap_set * source = NULL;
1334 struct mailimap_set * dest = NULL;
1335 seq_set = cur->data;
1337 debug_print("Copying messages from %s to %s ...\n",
1338 src->path, destdir);
1340 ok = imap_cmd_copy(session, seq_set, destdir, uid_mapping,
1343 if (ok == IMAP_SUCCESS) {
1344 if (single && relation && source && dest) {
1345 clistiter *l = clist_begin(source->set_list);
1346 struct mailimap_set_item *i = (struct mailimap_set_item *)clist_content(l);
1347 int snum = i->set_first;
1349 l = clist_begin(dest->set_list);
1350 i = (struct mailimap_set_item *)clist_content(l);
1351 dnum = i->set_first;
1352 g_relation_insert(uid_mapping, GINT_TO_POINTER(snum),
1353 GINT_TO_POINTER(dnum));
1359 mailimap_set_free(source);
1361 mailimap_set_free(dest);
1363 if (ok != IMAP_SUCCESS) {
1364 g_relation_destroy(uid_mapping);
1365 imap_lep_set_free(seq_list);
1366 unlock_session(session);
1367 statusbar_pop_all();
1372 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
1373 MsgInfo *msginfo = (MsgInfo *)cur->data;
1376 tuples = g_relation_select(uid_mapping,
1377 GINT_TO_POINTER(msginfo->msgnum),
1379 if (tuples->len > 0) {
1380 gint num = GPOINTER_TO_INT(g_tuples_index(tuples, 0, 1));
1381 g_relation_insert(relation, msginfo,
1382 GINT_TO_POINTER(num));
1385 debug_print("copied new message as %d\n", num);
1386 /* put the local file in the imapcache, so that we don't
1387 * have to fetch it back later. */
1389 gchar *cache_path = folder_item_get_path(msginfo->folder);
1390 gchar *real_file = g_strconcat(
1391 cache_path, G_DIR_SEPARATOR_S,
1392 itos(msginfo->msgnum), NULL);
1393 gchar *cache_file = NULL;
1395 cache_path = folder_item_get_path(dest);
1396 cache_file = g_strconcat(
1397 cache_path, G_DIR_SEPARATOR_S,
1399 if (!is_dir_exist(cache_path))
1400 make_dir_hier(cache_path);
1401 if (is_file_exist(real_file) && is_dir_exist(cache_path)) {
1402 copy_file(real_file, cache_file, TRUE);
1403 debug_print("copied to cache: %s\n", cache_file);
1410 g_relation_insert(relation, msginfo,
1411 GINT_TO_POINTER(0));
1412 g_tuples_destroy(tuples);
1414 statusbar_pop_all();
1416 g_relation_destroy(uid_mapping);
1417 imap_lep_set_free(seq_list);
1421 IMAP_FOLDER_ITEM(dest)->lastuid = 0;
1422 IMAP_FOLDER_ITEM(dest)->uid_next = 0;
1423 g_slist_free(IMAP_FOLDER_ITEM(dest)->uid_list);
1424 IMAP_FOLDER_ITEM(dest)->uid_list = NULL;
1426 unlock_session(session);
1427 if (ok == IMAP_SUCCESS)
1433 static gint imap_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1437 g_return_val_if_fail(msginfo != NULL, -1);
1439 msglist.data = msginfo;
1440 msglist.next = NULL;
1442 return imap_copy_msgs(folder, dest, &msglist, NULL);
1445 static gint imap_copy_msgs(Folder *folder, FolderItem *dest,
1446 MsgInfoList *msglist, GRelation *relation)
1451 g_return_val_if_fail(folder != NULL, -1);
1452 g_return_val_if_fail(dest != NULL, -1);
1453 g_return_val_if_fail(msglist != NULL, -1);
1455 msginfo = (MsgInfo *)msglist->data;
1456 g_return_val_if_fail(msginfo->folder != NULL, -1);
1458 ret = imap_do_copy_msgs(folder, dest, msglist, relation);
1463 static gint imap_do_remove_msgs(Folder *folder, FolderItem *dest,
1464 MsgInfoList *msglist, GRelation *relation)
1466 gchar *destdir, *dir;
1467 GSList *numlist = NULL, *cur;
1469 IMAPSession *session;
1470 gint ok = IMAP_SUCCESS;
1471 GRelation *uid_mapping;
1473 g_return_val_if_fail(folder != NULL, -1);
1474 g_return_val_if_fail(dest != NULL, -1);
1475 g_return_val_if_fail(msglist != NULL, -1);
1477 debug_print("getting session...\n");
1478 session = imap_session_get(folder);
1483 msginfo = (MsgInfo *)msglist->data;
1485 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
1486 NULL, NULL, NULL, NULL, FALSE);
1487 if (ok != IMAP_SUCCESS) {
1488 unlock_session(session);
1492 destdir = imap_get_real_path(session, IMAP_FOLDER(folder), dest->path);
1493 for (cur = msglist; cur; cur = cur->next) {
1494 msginfo = (MsgInfo *)cur->data;
1495 if (!MSG_IS_DELETED(msginfo->flags))
1496 numlist = g_slist_prepend(numlist, GINT_TO_POINTER(msginfo->msgnum));
1498 numlist = g_slist_reverse(numlist);
1500 uid_mapping = g_relation_new(2);
1501 g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
1503 ok = imap_set_message_flags
1504 (session, numlist, IMAP_FLAG_DELETED, TRUE);
1505 if (ok != IMAP_SUCCESS) {
1506 log_warning(LOG_PROTOCOL, _("can't set deleted flags\n"));
1507 unlock_session(session);
1510 ok = imap_cmd_expunge(session);
1511 if (ok != IMAP_SUCCESS) {
1512 log_warning(LOG_PROTOCOL, _("can't expunge\n"));
1513 unlock_session(session);
1517 dir = folder_item_get_path(msginfo->folder);
1518 if (is_dir_exist(dir)) {
1519 for (cur = msglist; cur; cur = cur->next) {
1520 msginfo = (MsgInfo *)cur->data;
1521 remove_numbered_files(dir, msginfo->msgnum, msginfo->msgnum);
1526 g_relation_destroy(uid_mapping);
1527 g_slist_free(numlist);
1530 unlock_session(session);
1531 if (ok == IMAP_SUCCESS)
1537 static gint imap_remove_msgs(Folder *folder, FolderItem *dest,
1538 MsgInfoList *msglist, GRelation *relation)
1542 g_return_val_if_fail(folder != NULL, -1);
1543 g_return_val_if_fail(dest != NULL, -1);
1544 if (msglist == NULL)
1547 msginfo = (MsgInfo *)msglist->data;
1548 g_return_val_if_fail(msginfo->folder != NULL, -1);
1550 return imap_do_remove_msgs(folder, dest, msglist, relation);
1553 static gint imap_remove_all_msg(Folder *folder, FolderItem *item)
1555 GSList *list = folder_item_get_msg_list(item);
1556 gint res = imap_remove_msgs(folder, item, list, NULL);
1557 procmsg_msg_list_free(list);
1561 static gboolean imap_is_msg_changed(Folder *folder, FolderItem *item,
1564 /* TODO: properly implement this method */
1568 static gint imap_close(Folder *folder, FolderItem *item)
1573 gint imap_scan_tree_real(Folder *folder, gboolean subs_only)
1575 FolderItem *item = NULL;
1576 IMAPSession *session;
1577 gchar *root_folder = NULL;
1579 g_return_val_if_fail(folder != NULL, -1);
1580 g_return_val_if_fail(folder->account != NULL, -1);
1582 debug_print("getting session...\n");
1583 session = imap_session_get(folder);
1585 if (!folder->node) {
1586 folder_tree_destroy(folder);
1587 item = folder_item_new(folder, folder->name, NULL);
1588 item->folder = folder;
1589 folder->node = item->node = g_node_new(item);
1594 if (folder->account->imap_dir && *folder->account->imap_dir) {
1599 Xstrdup_a(root_folder, folder->account->imap_dir, {unlock_session(session);return -1;});
1600 extract_quote(root_folder, '"');
1601 subst_char(root_folder,
1602 imap_get_path_separator(session, IMAP_FOLDER(folder),
1605 strtailchomp(root_folder, '/');
1606 real_path = imap_get_real_path
1607 (session, IMAP_FOLDER(folder), root_folder);
1608 debug_print("IMAP root directory: %s\n", real_path);
1610 /* check if root directory exist */
1612 r = imap_threaded_list(session->folder, "", real_path,
1614 if ((r != MAILIMAP_NO_ERROR) || (clist_count(lep_list) == 0)) {
1615 if (!folder->node) {
1616 item = folder_item_new(folder, folder->name, NULL);
1617 item->folder = folder;
1618 folder->node = item->node = g_node_new(item);
1620 unlock_session(session);
1623 mailimap_list_result_free(lep_list);
1629 item = FOLDER_ITEM(folder->node->data);
1631 if (item && !item->path && root_folder) {
1632 item->path = g_strdup(root_folder);
1635 if (!item || ((item->path || root_folder) &&
1636 strcmp2(item->path, root_folder) != 0)) {
1637 folder_tree_destroy(folder);
1638 item = folder_item_new(folder, folder->name, root_folder);
1639 item->folder = folder;
1640 folder->node = item->node = g_node_new(item);
1643 imap_scan_tree_recursive(session, FOLDER_ITEM(folder->node->data), subs_only);
1644 imap_create_missing_folders(folder);
1645 unlock_session(session);
1650 static gint imap_scan_tree(Folder *folder)
1652 gboolean subs_only = FALSE;
1653 if (folder->account) {
1654 debug_print(" scanning only subs %d\n", folder->account->imap_subsonly);
1655 subs_only = folder->account->imap_subsonly;
1657 return imap_scan_tree_real(folder, subs_only);
1660 static gint imap_scan_tree_recursive(IMAPSession *session, FolderItem *item, gboolean subs_only)
1663 IMAPFolder *imapfolder;
1664 FolderItem *new_item;
1665 GSList *item_list, *cur;
1668 gchar *wildcard_path;
1674 g_return_val_if_fail(item != NULL, -1);
1675 g_return_val_if_fail(item->folder != NULL, -1);
1676 g_return_val_if_fail(item->no_sub == FALSE, -1);
1678 folder = item->folder;
1679 imapfolder = IMAP_FOLDER(folder);
1681 separator = imap_get_path_separator(session, imapfolder, item->path);
1683 if (folder->ui_func)
1684 folder->ui_func(folder, item, folder->ui_func_data);
1687 wildcard[0] = separator;
1690 real_path = imap_get_real_path(session, imapfolder, item->path);
1694 real_path = g_strdup("");
1697 Xstrcat_a(wildcard_path, real_path, wildcard,
1698 {g_free(real_path); return IMAP_ERROR;});
1702 r = imap_threaded_lsub(folder, "", wildcard_path, &lep_list);
1704 r = imap_threaded_list(folder, "", wildcard_path, &lep_list);
1706 if (r != MAILIMAP_NO_ERROR) {
1710 item_list = imap_list_from_lep(imapfolder,
1711 lep_list, real_path, FALSE);
1712 mailimap_list_result_free(lep_list);
1717 node = item->node->children;
1718 while (node != NULL) {
1719 FolderItem *old_item = FOLDER_ITEM(node->data);
1720 GNode *next = node->next;
1723 for (cur = item_list; cur != NULL; cur = cur->next) {
1724 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1725 if (!strcmp2(old_item->path, cur_item->path)) {
1726 new_item = cur_item;
1731 if (old_item && old_item->path && !strcmp(old_item->path, "INBOX")) {
1732 debug_print("not removing INBOX\n");
1734 debug_print("folder '%s' not found. removing...\n",
1736 folder_item_remove(old_item);
1739 old_item->no_sub = new_item->no_sub;
1740 old_item->no_select = new_item->no_select;
1741 if (old_item->no_sub == TRUE && node->children) {
1742 debug_print("folder '%s' doesn't have "
1743 "subfolders. removing...\n",
1745 folder_item_remove_children(old_item);
1752 for (cur = item_list; cur != NULL; cur = cur->next) {
1753 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1756 for (node = item->node->children; node != NULL;
1757 node = node->next) {
1758 if (!strcmp2(FOLDER_ITEM(node->data)->path,
1760 new_item = FOLDER_ITEM(node->data);
1761 folder_item_destroy(cur_item);
1767 new_item = cur_item;
1768 debug_print("new folder '%s' found.\n", new_item->path);
1769 folder_item_append(item, new_item);
1772 if (!strcmp(new_item->path, "INBOX")) {
1773 new_item->stype = F_INBOX;
1774 folder->inbox = new_item;
1775 } else if (!folder_item_parent(item) || item->stype == F_INBOX) {
1778 base = g_path_get_basename(new_item->path);
1780 if (!folder->outbox && !g_ascii_strcasecmp(base, "Sent")) {
1781 new_item->stype = F_OUTBOX;
1782 folder->outbox = new_item;
1783 } else if (!folder->draft && !g_ascii_strcasecmp(base, "Drafts")) {
1784 new_item->stype = F_DRAFT;
1785 folder->draft = new_item;
1786 } else if (!folder->queue && !g_ascii_strcasecmp(base, "Queue")) {
1787 new_item->stype = F_QUEUE;
1788 folder->queue = new_item;
1789 } else if (!folder->trash && !g_ascii_strcasecmp(base, "Trash")) {
1790 new_item->stype = F_TRASH;
1791 folder->trash = new_item;
1796 if (new_item->no_sub == FALSE)
1797 imap_scan_tree_recursive(session, new_item, subs_only);
1800 g_slist_free(item_list);
1802 return IMAP_SUCCESS;
1805 GList *imap_scan_subtree(Folder *folder, FolderItem *item, gboolean unsubs_only, gboolean recursive)
1807 IMAPSession *session = imap_session_get(folder);
1809 gchar *wildcard_path;
1813 GSList *item_list = NULL, *cur;
1814 GList *child_list = NULL, *tmplist = NULL;
1815 GSList *sub_list = NULL;
1821 separator = imap_get_path_separator(session, IMAP_FOLDER(folder), item->path);
1824 wildcard[0] = separator;
1827 real_path = imap_get_real_path(session, IMAP_FOLDER(folder), item->path);
1831 real_path = g_strdup("");
1834 Xstrcat_a(wildcard_path, real_path, wildcard,
1835 {g_free(real_path); return NULL;});
1839 statusbar_print_all(_("Looking for unsubscribed folders in %s..."),
1840 item->path?item->path:item->name);
1842 statusbar_print_all(_("Looking for subfolders of %s..."),
1843 item->path?item->path:item->name);
1845 r = imap_threaded_list(folder, "", wildcard_path, &lep_list);
1847 statusbar_pop_all();
1850 item_list = imap_list_from_lep(IMAP_FOLDER(folder),
1851 lep_list, real_path, FALSE);
1852 mailimap_list_result_free(lep_list);
1854 for (cur = item_list; cur != NULL; cur = cur->next) {
1855 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1857 tmplist = imap_scan_subtree(folder, cur_item,
1858 unsubs_only, recursive);
1860 child_list = g_list_concat(child_list, tmplist);
1862 child_list = g_list_prepend(child_list,
1863 imap_get_real_path(session,
1864 IMAP_FOLDER(folder), cur_item->path));
1866 folder_item_destroy(cur_item);
1868 child_list = g_list_reverse(child_list);
1869 g_slist_free(item_list);
1872 r = imap_threaded_lsub(folder, "", wildcard_path, &lep_list);
1874 statusbar_pop_all();
1877 sub_list = imap_list_from_lep(IMAP_FOLDER(folder),
1878 lep_list, real_path, FALSE);
1879 mailimap_list_result_free(lep_list);
1881 for (cur = sub_list; cur != NULL; cur = cur->next) {
1882 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1883 GList *oldlitem = NULL;
1884 gchar *tmp = imap_get_real_path(session,
1885 IMAP_FOLDER(folder), cur_item->path);
1886 folder_item_destroy(cur_item);
1887 oldlitem = g_list_find_custom(
1888 child_list, tmp, (GCompareFunc)strcmp2);
1890 child_list = g_list_remove_link(child_list, oldlitem);
1891 g_free(oldlitem->data);
1892 g_list_free(oldlitem);
1898 statusbar_pop_all();
1903 static gint imap_create_tree(Folder *folder)
1905 g_return_val_if_fail(folder != NULL, -1);
1906 g_return_val_if_fail(folder->node != NULL, -1);
1907 g_return_val_if_fail(folder->node->data != NULL, -1);
1908 g_return_val_if_fail(folder->account != NULL, -1);
1910 imap_scan_tree(folder);
1911 imap_create_missing_folders(folder);
1916 static void imap_create_missing_folders(Folder *folder)
1918 g_return_if_fail(folder != NULL);
1921 folder->inbox = imap_create_special_folder
1922 (folder, F_INBOX, "INBOX");
1924 folder->trash = imap_create_special_folder
1925 (folder, F_TRASH, "Trash");
1927 folder->queue = imap_create_special_folder
1928 (folder, F_QUEUE, "Queue");
1929 if (!folder->outbox)
1930 folder->outbox = imap_create_special_folder
1931 (folder, F_OUTBOX, "Sent");
1933 folder->draft = imap_create_special_folder
1934 (folder, F_DRAFT, "Drafts");
1937 static FolderItem *imap_create_special_folder(Folder *folder,
1938 SpecialFolderItemType stype,
1942 FolderItem *new_item;
1944 g_return_val_if_fail(folder != NULL, NULL);
1945 g_return_val_if_fail(folder->node != NULL, NULL);
1946 g_return_val_if_fail(folder->node->data != NULL, NULL);
1947 g_return_val_if_fail(folder->account != NULL, NULL);
1948 g_return_val_if_fail(name != NULL, NULL);
1950 item = FOLDER_ITEM(folder->node->data);
1951 new_item = imap_create_folder(folder, item, name);
1954 g_warning("Can't create '%s'\n", name);
1955 if (!folder->inbox) return NULL;
1957 new_item = imap_create_folder(folder, folder->inbox, name);
1959 g_warning("Can't create '%s' under INBOX\n", name);
1961 new_item->stype = stype;
1963 new_item->stype = stype;
1968 static gchar *imap_folder_get_path(Folder *folder)
1972 g_return_val_if_fail(folder != NULL, NULL);
1973 g_return_val_if_fail(folder->account != NULL, NULL);
1975 folder_path = g_strconcat(get_imap_cache_dir(),
1977 folder->account->recv_server,
1979 folder->account->userid,
1985 static gchar *imap_item_get_path(Folder *folder, FolderItem *item)
1987 gchar *folder_path, *path;
1989 g_return_val_if_fail(folder != NULL, NULL);
1990 g_return_val_if_fail(item != NULL, NULL);
1991 folder_path = imap_folder_get_path(folder);
1993 g_return_val_if_fail(folder_path != NULL, NULL);
1994 if (folder_path[0] == G_DIR_SEPARATOR) {
1996 path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
1999 path = g_strdup(folder_path);
2002 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
2003 folder_path, G_DIR_SEPARATOR_S,
2006 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
2009 g_free(folder_path);
2014 static FolderItem *imap_create_folder(Folder *folder, FolderItem *parent,
2017 gchar *dirpath, *imap_path;
2018 IMAPSession *session;
2019 FolderItem *new_item;
2024 gboolean no_select = FALSE, no_sub = FALSE;
2025 gboolean exist = FALSE;
2027 g_return_val_if_fail(folder != NULL, NULL);
2028 g_return_val_if_fail(folder->account != NULL, NULL);
2029 g_return_val_if_fail(parent != NULL, NULL);
2030 g_return_val_if_fail(name != NULL, NULL);
2032 debug_print("getting session...\n");
2033 session = imap_session_get(folder);
2038 if (!folder_item_parent(parent) && strcmp(name, "INBOX") == 0) {
2039 dirpath = g_strdup(name);
2040 }else if (parent->path)
2041 dirpath = g_strconcat(parent->path, "/", name, NULL);
2042 else if ((p = strchr(name, '/')) != NULL && *(p + 1) != '\0')
2043 dirpath = g_strdup(name);
2044 else if (folder->account->imap_dir && *folder->account->imap_dir) {
2047 Xstrdup_a(imap_dir, folder->account->imap_dir, {unlock_session(session);return NULL;});
2048 strtailchomp(imap_dir, '/');
2049 dirpath = g_strconcat(imap_dir, "/", name, NULL);
2051 dirpath = g_strdup(name);
2055 /* keep trailing directory separator to create a folder that contains
2057 imap_path = imap_utf8_to_modified_utf7(dirpath);
2059 strtailchomp(dirpath, '/');
2060 Xstrdup_a(new_name, name, {
2062 unlock_session(session);
2065 separator = imap_get_path_separator(session, IMAP_FOLDER(folder), imap_path);
2066 imap_path_separator_subst(imap_path, separator);
2067 /* remove trailing / for display */
2068 strtailchomp(new_name, '/');
2070 if (strcmp(dirpath, "INBOX") != 0) {
2075 argbuf = g_ptr_array_new();
2076 r = imap_threaded_list(folder, "", imap_path, &lep_list);
2077 if (r != MAILIMAP_NO_ERROR) {
2078 log_warning(LOG_PROTOCOL, _("can't create mailbox: LIST failed\n"));
2081 ptr_array_free_strings(argbuf);
2082 g_ptr_array_free(argbuf, TRUE);
2083 unlock_session(session);
2087 if (clist_count(lep_list) > 0)
2089 mailimap_list_result_free(lep_list);
2092 ok = imap_cmd_create(session, imap_path);
2093 if (ok != IMAP_SUCCESS) {
2094 log_warning(LOG_PROTOCOL, _("can't create mailbox\n"));
2097 unlock_session(session);
2100 r = imap_threaded_list(folder, "", imap_path, &lep_list);
2101 if (r == MAILIMAP_NO_ERROR) {
2102 GSList *item_list = imap_list_from_lep(IMAP_FOLDER(folder),
2103 lep_list, dirpath, TRUE);
2105 FolderItem *cur_item = FOLDER_ITEM(item_list->data);
2106 no_select = cur_item->no_select;
2107 no_sub = cur_item->no_sub;
2108 g_slist_free(item_list);
2110 mailimap_list_result_free(lep_list);
2113 imap_threaded_subscribe(folder, imap_path, TRUE);
2117 /* just get flags */
2118 r = imap_threaded_list(folder, "", "INBOX", &lep_list);
2119 if (r == MAILIMAP_NO_ERROR) {
2120 GSList *item_list = imap_list_from_lep(IMAP_FOLDER(folder),
2121 lep_list, dirpath, TRUE);
2123 FolderItem *cur_item = FOLDER_ITEM(item_list->data);
2124 no_select = cur_item->no_select;
2125 no_sub = cur_item->no_sub;
2126 g_slist_free(item_list);
2128 mailimap_list_result_free(lep_list);
2132 new_item = folder_item_new(folder, new_name, dirpath);
2133 new_item->no_select = no_select;
2134 new_item->no_sub = no_sub;
2135 folder_item_append(parent, new_item);
2139 dirpath = folder_item_get_path(new_item);
2140 if (!is_dir_exist(dirpath))
2141 make_dir_hier(dirpath);
2143 unlock_session(session);
2146 /* folder existed, scan it */
2147 folder_item_scan_full(new_item, FALSE);
2153 static gint imap_rename_folder(Folder *folder, FolderItem *item,
2158 gchar *real_oldpath;
2159 gchar *real_newpath;
2161 gchar *old_cache_dir;
2162 gchar *new_cache_dir;
2163 IMAPSession *session;
2166 gint exists, recent, unseen;
2167 guint32 uid_validity;
2169 g_return_val_if_fail(folder != NULL, -1);
2170 g_return_val_if_fail(item != NULL, -1);
2171 g_return_val_if_fail(item->path != NULL, -1);
2172 g_return_val_if_fail(name != NULL, -1);
2174 debug_print("getting session...\n");
2175 session = imap_session_get(folder);
2180 if (strchr(name, imap_get_path_separator(session, IMAP_FOLDER(folder), item->path)) != NULL) {
2181 g_warning(_("New folder name must not contain the namespace "
2183 unlock_session(session);
2187 real_oldpath = imap_get_real_path(session, IMAP_FOLDER(folder), item->path);
2189 g_free(session->mbox);
2190 session->mbox = NULL;
2191 ok = imap_cmd_examine(session, "INBOX",
2192 &exists, &recent, &unseen, &uid_validity, FALSE);
2193 if (ok != IMAP_SUCCESS) {
2194 g_free(real_oldpath);
2195 unlock_session(session);
2199 separator = imap_get_path_separator(session, IMAP_FOLDER(folder), item->path);
2200 if (strchr(item->path, G_DIR_SEPARATOR)) {
2201 dirpath = g_path_get_dirname(item->path);
2202 newpath = g_strconcat(dirpath, G_DIR_SEPARATOR_S, name, NULL);
2205 newpath = g_strdup(name);
2207 real_newpath = imap_utf8_to_modified_utf7(newpath);
2208 imap_path_separator_subst(real_newpath, separator);
2210 ok = imap_cmd_rename(session, real_oldpath, real_newpath);
2211 if (ok != IMAP_SUCCESS) {
2212 log_warning(LOG_PROTOCOL, _("can't rename mailbox: %s to %s\n"),
2213 real_oldpath, real_newpath);
2214 g_free(real_oldpath);
2216 g_free(real_newpath);
2217 unlock_session(session);
2221 item->name = g_strdup(name);
2223 old_cache_dir = folder_item_get_path(item);
2225 paths[0] = g_strdup(item->path);
2227 g_node_traverse(item->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
2228 imap_rename_folder_func, paths);
2230 if (is_dir_exist(old_cache_dir)) {
2231 new_cache_dir = folder_item_get_path(item);
2232 if (rename(old_cache_dir, new_cache_dir) < 0) {
2233 FILE_OP_ERROR(old_cache_dir, "rename");
2235 g_free(new_cache_dir);
2238 g_free(old_cache_dir);
2241 g_free(real_oldpath);
2242 g_free(real_newpath);
2243 unlock_session(session);
2247 gint imap_subscribe(Folder *folder, FolderItem *item, gchar *rpath, gboolean sub)
2251 IMAPSession *session;
2252 debug_print("getting session...\n");
2254 session = imap_session_get(folder);
2258 if (item && item->path) {
2259 path = imap_get_real_path(session, IMAP_FOLDER(folder), item->path);
2262 if (!strcmp(path, "INBOX") && sub == FALSE)
2264 debug_print("%ssubscribing %s\n", sub?"":"un", path);
2265 r = imap_threaded_subscribe(folder, path, sub);
2268 r = imap_threaded_subscribe(folder, rpath, sub);
2274 static gint imap_remove_folder_real(Folder *folder, FolderItem *item)
2277 IMAPSession *session;
2281 g_return_val_if_fail(folder != NULL, -1);
2282 g_return_val_if_fail(item != NULL, -1);
2283 g_return_val_if_fail(item->path != NULL, -1);
2285 debug_print("getting session...\n");
2286 session = imap_session_get(folder);
2290 path = imap_get_real_path(session, IMAP_FOLDER(folder), item->path);
2292 imap_threaded_subscribe(folder, path, FALSE);
2293 ok = imap_cmd_delete(session, path);
2294 if (ok != IMAP_SUCCESS) {
2295 gchar *tmp = g_strdup_printf("%s%c", path,
2296 imap_get_path_separator(session, IMAP_FOLDER(folder), path));
2299 ok = imap_cmd_delete(session, path);
2302 if (ok != IMAP_SUCCESS) {
2303 log_warning(LOG_PROTOCOL, _("can't delete mailbox\n"));
2305 unlock_session(session);
2310 cache_dir = folder_item_get_path(item);
2311 if (is_dir_exist(cache_dir) && remove_dir_recursive(cache_dir) < 0)
2312 g_warning("can't remove directory '%s'\n", cache_dir);
2314 folder_item_remove(item);
2315 unlock_session(session);
2319 static gint imap_remove_folder(Folder *folder, FolderItem *item)
2323 g_return_val_if_fail(item != NULL, -1);
2324 g_return_val_if_fail(item->folder != NULL, -1);
2325 g_return_val_if_fail(item->node != NULL, -1);
2327 node = item->node->children;
2328 while (node != NULL) {
2330 if (imap_remove_folder(folder, FOLDER_ITEM(node->data)) < 0)
2334 debug_print("IMAP removing %s\n", item->path);
2336 if (imap_remove_all_msg(folder, item) < 0)
2338 return imap_remove_folder_real(folder, item);
2341 typedef struct _uncached_data {
2342 IMAPSession *session;
2344 MsgNumberList *numlist;
2350 static void *imap_get_uncached_messages_thread(void *data)
2352 uncached_data *stuff = (uncached_data *)data;
2353 IMAPSession *session = stuff->session;
2354 FolderItem *item = stuff->item;
2355 MsgNumberList *numlist = stuff->numlist;
2357 GSList *newlist = NULL;
2358 GSList *llast = NULL;
2359 GSList *seq_list, *cur;
2361 debug_print("uncached_messages\n");
2363 if (session == NULL || item == NULL || item->folder == NULL
2364 || FOLDER_CLASS(item->folder) != &imap_class) {
2369 seq_list = imap_get_lep_set_from_numlist(numlist);
2370 debug_print("get msgs info\n");
2371 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
2372 struct mailimap_set * imapset;
2378 if (session->cancelled)
2381 imapset = cur->data;
2383 r = imap_threaded_fetch_env(session->folder,
2384 imapset, &env_list);
2385 if (r != MAILIMAP_NO_ERROR)
2388 session_set_access_time(SESSION(session));
2391 for(i = 0 ; i < carray_count(env_list) ; i ++) {
2392 struct imap_fetch_env_info * info;
2395 info = carray_get(env_list, i);
2396 msginfo = imap_envelope_from_lep(info, item);
2397 if (msginfo == NULL)
2399 msginfo->folder = item;
2401 llast = newlist = g_slist_append(newlist, msginfo);
2403 llast = g_slist_append(llast, msginfo);
2404 llast = llast->next;
2409 imap_fetch_env_free(env_list);
2412 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
2413 struct mailimap_set * imapset;
2415 imapset = cur->data;
2416 mailimap_set_free(imapset);
2419 session_set_access_time(SESSION(session));
2424 #define MAX_MSG_NUM 50
2426 static GSList *imap_get_uncached_messages(IMAPSession *session,
2428 MsgNumberList *numlist)
2430 GSList *result = NULL;
2432 uncached_data *data = g_new0(uncached_data, 1);
2437 data->total = g_slist_length(numlist);
2438 debug_print("messages list : %i\n", data->total);
2440 while (cur != NULL) {
2441 GSList * partial_result;
2449 while (count < MAX_MSG_NUM) {
2454 if (newlist == NULL)
2455 llast = newlist = g_slist_append(newlist, p);
2457 llast = g_slist_append(llast, p);
2458 llast = llast->next;
2468 data->session = session;
2470 data->numlist = newlist;
2473 if (prefs_common.work_offline &&
2474 !inc_offline_should_override(FALSE,
2475 _("Claws Mail needs network access in order "
2476 "to access the IMAP server."))) {
2482 (GSList *)imap_get_uncached_messages_thread(data);
2484 statusbar_progress_all(data->cur,data->total, 1);
2486 g_slist_free(newlist);
2488 result = g_slist_concat(result, partial_result);
2492 statusbar_progress_all(0,0,0);
2493 statusbar_pop_all();
2498 static void imap_delete_all_cached_messages(FolderItem *item)
2502 g_return_if_fail(item != NULL);
2503 g_return_if_fail(item->folder != NULL);
2504 g_return_if_fail(FOLDER_CLASS(item->folder) == &imap_class);
2506 debug_print("Deleting all cached messages...\n");
2508 dir = folder_item_get_path(item);
2509 if (is_dir_exist(dir))
2510 remove_all_numbered_files(dir);
2513 debug_print("done.\n");
2516 gchar imap_get_path_separator_for_item(FolderItem *item)
2518 Folder *folder = NULL;
2519 IMAPFolder *imap_folder = NULL;
2520 IMAPSession *session = NULL;
2525 folder = item->folder;
2530 imap_folder = IMAP_FOLDER(folder);
2535 debug_print("getting session...");
2536 session = imap_session_get(FOLDER(folder));
2537 result = imap_get_path_separator(session, imap_folder, item->path);
2538 unlock_session(session);
2542 static gchar imap_refresh_path_separator(IMAPSession *session, IMAPFolder *folder, const gchar *subfolder)
2546 gchar separator = '\0';
2548 g_return_val_if_fail(session != NULL, '/');
2549 r = imap_threaded_list((Folder *)folder, "", subfolder, &lep_list);
2551 if (r != MAILIMAP_NO_ERROR) {
2552 log_warning(LOG_PROTOCOL, _("LIST failed\n"));
2556 if (clist_count(lep_list) > 0) {
2557 clistiter * iter = clist_begin(lep_list);
2558 struct mailimap_mailbox_list * mb;
2559 mb = clist_content(iter);
2561 separator = mb->mb_delimiter;
2562 debug_print("got separator: %c\n", folder->last_seen_separator);
2564 mailimap_list_result_free(lep_list);
2568 static gchar imap_get_path_separator(IMAPSession *session, IMAPFolder *folder, const gchar *path)
2570 gchar separator = '/';
2572 if (folder->last_seen_separator == 0) {
2573 folder->last_seen_separator = imap_refresh_path_separator(session, folder, "");
2576 if (folder->last_seen_separator == 0) {
2577 folder->last_seen_separator = imap_refresh_path_separator(session, folder, "INBOX");
2580 if (folder->last_seen_separator != 0) {
2581 debug_print("using separator: %c\n", folder->last_seen_separator);
2582 return folder->last_seen_separator;
2588 static gchar *imap_get_real_path(IMAPSession *session, IMAPFolder *folder, const gchar *path)
2593 g_return_val_if_fail(folder != NULL, NULL);
2594 g_return_val_if_fail(path != NULL, NULL);
2596 real_path = imap_utf8_to_modified_utf7(path);
2597 separator = imap_get_path_separator(session, folder, path);
2598 imap_path_separator_subst(real_path, separator);
2603 static gint imap_set_message_flags(IMAPSession *session,
2604 MsgNumberList *numlist,
2612 seq_list = imap_get_lep_set_from_numlist(numlist);
2614 for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) {
2615 struct mailimap_set * imapset;
2617 imapset = cur->data;
2619 ok = imap_cmd_store(session, imapset,
2623 imap_lep_set_free(seq_list);
2625 return IMAP_SUCCESS;
2628 typedef struct _select_data {
2629 IMAPSession *session;
2634 guint32 *uid_validity;
2638 static gint imap_select(IMAPSession *session, IMAPFolder *folder,
2640 gint *exists, gint *recent, gint *unseen,
2641 guint32 *uid_validity, gboolean block)
2645 gint exists_, recent_, unseen_;
2646 guint32 uid_validity_;
2648 if (!exists && !recent && !unseen && !uid_validity) {
2649 if (session->mbox && strcmp(session->mbox, path) == 0)
2650 return IMAP_SUCCESS;
2659 uid_validity = &uid_validity_;
2661 g_free(session->mbox);
2662 session->mbox = NULL;
2664 real_path = imap_get_real_path(session, folder, path);
2666 ok = imap_cmd_select(session, real_path,
2667 exists, recent, unseen, uid_validity, block);
2668 if (ok != IMAP_SUCCESS)
2669 log_warning(LOG_PROTOCOL, _("can't select folder: %s\n"), real_path);
2671 session->mbox = g_strdup(path);
2672 session->folder_content_changed = FALSE;
2679 static gint imap_status(IMAPSession *session, IMAPFolder *folder,
2680 const gchar *path, IMAPFolderItem *item,
2682 guint32 *uid_next, guint32 *uid_validity,
2683 gint *unseen, gboolean block)
2687 struct mailimap_mailbox_data_status * data_status;
2692 real_path = imap_get_real_path(session, folder, path);
2710 r = imap_threaded_status(FOLDER(folder), real_path,
2711 &data_status, mask);
2714 if (r != MAILIMAP_NO_ERROR) {
2715 debug_print("status err %d\n", r);
2719 if (data_status->st_info_list == NULL) {
2720 mailimap_mailbox_data_status_free(data_status);
2721 debug_print("status->st_info_list == NULL\n");
2726 for(iter = clist_begin(data_status->st_info_list) ; iter != NULL ;
2727 iter = clist_next(iter)) {
2728 struct mailimap_status_info * info;
2730 info = clist_content(iter);
2731 switch (info->st_att) {
2732 case MAILIMAP_STATUS_ATT_MESSAGES:
2734 * messages = info->st_value;
2735 got_values |= 1 << 0;
2739 case MAILIMAP_STATUS_ATT_UIDNEXT:
2741 * uid_next = info->st_value;
2742 got_values |= 1 << 2;
2746 case MAILIMAP_STATUS_ATT_UIDVALIDITY:
2748 * uid_validity = info->st_value;
2749 got_values |= 1 << 3;
2753 case MAILIMAP_STATUS_ATT_UNSEEN:
2755 * unseen = info->st_value;
2756 got_values |= 1 << 4;
2761 mailimap_mailbox_data_status_free(data_status);
2763 if (got_values != mask) {
2764 g_warning("status: incomplete values received (%d)\n", got_values);
2766 return IMAP_SUCCESS;
2769 static void imap_free_capabilities(IMAPSession *session)
2771 slist_free_strings(session->capability);
2772 g_slist_free(session->capability);
2773 session->capability = NULL;
2776 /* low-level IMAP4rev1 commands */
2778 static gint imap_cmd_login(IMAPSession *session,
2779 const gchar *user, const gchar *pass,
2785 if (!strcmp(type, "LOGIN") && imap_has_capability(session, "LOGINDISABLED")) {
2786 gint ok = IMAP_ERROR;
2787 if (imap_has_capability(session, "STARTTLS")) {
2789 log_warning(LOG_PROTOCOL, _("Server requires TLS to log in.\n"));
2790 ok = imap_cmd_starttls(session);
2791 if (ok != IMAP_SUCCESS) {
2792 log_warning(LOG_PROTOCOL, _("Can't start TLS session.\n"));
2796 imap_free_capabilities(session);
2797 if (imap_get_capabilities(session) != MAILIMAP_NO_ERROR) {
2798 log_warning(LOG_PROTOCOL, _("Can't refresh capabilities.\n"));
2803 log_error(LOG_PROTOCOL, _("Connection to %s failed: "
2804 "server requires TLS, but Claws Mail "
2805 "has been compiled without OpenSSL "
2807 SESSION(session)->server);
2811 log_error(LOG_PROTOCOL, _("Server logins are disabled.\n"));
2816 log_print(LOG_PROTOCOL, "IMAP4> Logging %s to %s using %s\n",
2818 SESSION(session)->server,
2820 r = imap_threaded_login(session->folder, user, pass, type);
2821 if (r != MAILIMAP_NO_ERROR) {
2822 log_print(LOG_PROTOCOL, "IMAP4< Error logging in to %s\n",
2823 SESSION(session)->server);
2826 log_print(LOG_PROTOCOL, "IMAP4< Login to %s successful\n",
2827 SESSION(session)->server);
2833 static gint imap_cmd_noop(IMAPSession *session)
2836 unsigned int exists;
2838 r = imap_threaded_noop(session->folder, &exists);
2839 if (r != MAILIMAP_NO_ERROR) {
2840 debug_print("noop err %d\n", r);
2843 session->exists = exists;
2844 session_set_access_time(SESSION(session));
2846 return IMAP_SUCCESS;
2850 static gint imap_cmd_starttls(IMAPSession *session)
2854 r = imap_threaded_starttls(session->folder,
2855 SESSION(session)->server, SESSION(session)->port);
2856 if (r != MAILIMAP_NO_ERROR) {
2857 debug_print("starttls err %d\n", r);
2860 return IMAP_SUCCESS;
2864 static gint imap_cmd_select(IMAPSession *session, const gchar *folder,
2865 gint *exists, gint *recent, gint *unseen,
2866 guint32 *uid_validity, gboolean block)
2870 r = imap_threaded_select(session->folder, folder,
2871 exists, recent, unseen, uid_validity);
2872 if (r != MAILIMAP_NO_ERROR) {
2873 debug_print("select err %d\n", r);
2876 return IMAP_SUCCESS;
2879 static gint imap_cmd_examine(IMAPSession *session, const gchar *folder,
2880 gint *exists, gint *recent, gint *unseen,
2881 guint32 *uid_validity, gboolean block)
2885 r = imap_threaded_examine(session->folder, folder,
2886 exists, recent, unseen, uid_validity);
2887 if (r != MAILIMAP_NO_ERROR) {
2888 debug_print("examine err %d\n", r);
2892 return IMAP_SUCCESS;
2895 static gint imap_cmd_create(IMAPSession *session, const gchar *folder)
2899 r = imap_threaded_create(session->folder, folder);
2900 if (r != MAILIMAP_NO_ERROR) {
2905 return IMAP_SUCCESS;
2908 static gint imap_cmd_rename(IMAPSession *session, const gchar *old_folder,
2909 const gchar *new_folder)
2913 r = imap_threaded_rename(session->folder, old_folder,
2915 if (r != MAILIMAP_NO_ERROR) {
2920 return IMAP_SUCCESS;
2923 static gint imap_cmd_delete(IMAPSession *session, const gchar *folder)
2928 r = imap_threaded_delete(session->folder, folder);
2929 if (r != MAILIMAP_NO_ERROR) {
2934 return IMAP_SUCCESS;
2937 typedef struct _fetch_data {
2938 IMAPSession *session;
2940 const gchar *filename;
2946 static void *imap_cmd_fetch_thread(void *data)
2948 fetch_data *stuff = (fetch_data *)data;
2949 IMAPSession *session = stuff->session;
2950 guint32 uid = stuff->uid;
2951 const gchar *filename = stuff->filename;
2955 r = imap_threaded_fetch_content(session->folder,
2959 r = imap_threaded_fetch_content(session->folder,
2962 if (r != MAILIMAP_NO_ERROR) {
2963 debug_print("fetch err %d\n", r);
2964 return GINT_TO_POINTER(IMAP_ERROR);
2966 return GINT_TO_POINTER(IMAP_SUCCESS);
2969 static gint imap_cmd_fetch(IMAPSession *session, guint32 uid,
2970 const gchar *filename, gboolean headers,
2973 fetch_data *data = g_new0(fetch_data, 1);
2976 data->session = session;
2978 data->filename = filename;
2979 data->headers = headers;
2982 if (prefs_common.work_offline &&
2983 !inc_offline_should_override(FALSE,
2984 _("Claws Mail needs network access in order "
2985 "to access the IMAP server."))) {
2989 statusbar_print_all(_("Fetching message..."));
2990 result = GPOINTER_TO_INT(imap_cmd_fetch_thread(data));
2991 statusbar_pop_all();
2997 static gint imap_cmd_append(IMAPSession *session, const gchar *destfolder,
2998 const gchar *file, IMAPFlags flags,
3001 struct mailimap_flag_list * flag_list;
3004 g_return_val_if_fail(file != NULL, IMAP_ERROR);
3006 flag_list = imap_flag_to_lep(flags);
3007 r = imap_threaded_append(session->folder, destfolder,
3008 file, flag_list, (int *)new_uid);
3009 mailimap_flag_list_free(flag_list);
3011 if (r != MAILIMAP_NO_ERROR) {
3012 debug_print("append err %d\n", r);
3015 return IMAP_SUCCESS;
3018 static gint imap_cmd_copy(IMAPSession *session, struct mailimap_set * set,
3019 const gchar *destfolder, GRelation *uid_mapping,
3020 struct mailimap_set **source, struct mailimap_set **dest)
3024 g_return_val_if_fail(session != NULL, IMAP_ERROR);
3025 g_return_val_if_fail(set != NULL, IMAP_ERROR);
3026 g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
3028 r = imap_threaded_copy(session->folder, set, destfolder, source, dest);
3029 if (r != MAILIMAP_NO_ERROR) {
3034 return IMAP_SUCCESS;
3037 static gint imap_cmd_store(IMAPSession *session, struct mailimap_set * set,
3038 IMAPFlags flags, int do_add)
3041 struct mailimap_flag_list * flag_list;
3042 struct mailimap_store_att_flags * store_att_flags;
3044 flag_list = imap_flag_to_lep(flags);
3048 mailimap_store_att_flags_new_add_flags_silent(flag_list);
3051 mailimap_store_att_flags_new_remove_flags_silent(flag_list);
3053 r = imap_threaded_store(session->folder, set, store_att_flags);
3054 mailimap_store_att_flags_free(store_att_flags);
3055 if (r != MAILIMAP_NO_ERROR) {
3060 return IMAP_SUCCESS;
3063 static gint imap_cmd_expunge(IMAPSession *session)
3067 if (prefs_common.work_offline &&
3068 !inc_offline_should_override(FALSE,
3069 _("Claws Mail needs network access in order "
3070 "to access the IMAP server."))) {
3074 r = imap_threaded_expunge(session->folder);
3075 if (r != MAILIMAP_NO_ERROR) {
3080 return IMAP_SUCCESS;
3083 static void imap_path_separator_subst(gchar *str, gchar separator)
3086 gboolean in_escape = FALSE;
3088 if (!separator || separator == '/') return;
3090 for (p = str; *p != '\0'; p++) {
3091 if (*p == '/' && !in_escape)
3093 else if (*p == '&' && *(p + 1) != '-' && !in_escape)
3095 else if (*p == '-' && in_escape)
3100 static gchar *imap_modified_utf7_to_utf8(const gchar *mutf7_str)
3102 static iconv_t cd = (iconv_t)-1;
3103 static gboolean iconv_ok = TRUE;
3106 size_t norm_utf7_len;
3108 gchar *to_str, *to_p;
3110 gboolean in_escape = FALSE;
3112 if (!iconv_ok) return g_strdup(mutf7_str);
3114 if (cd == (iconv_t)-1) {
3115 cd = iconv_open(CS_INTERNAL, CS_UTF_7);
3116 if (cd == (iconv_t)-1) {
3117 g_warning("iconv cannot convert UTF-7 to %s\n",
3120 return g_strdup(mutf7_str);
3124 /* modified UTF-7 to normal UTF-7 conversion */
3125 norm_utf7 = g_string_new(NULL);
3127 for (p = mutf7_str; *p != '\0'; p++) {
3128 /* replace: '&' -> '+',
3130 escaped ',' -> '/' */
3131 if (!in_escape && *p == '&') {
3132 if (*(p + 1) != '-') {
3133 g_string_append_c(norm_utf7, '+');
3136 g_string_append_c(norm_utf7, '&');
3139 } else if (in_escape && *p == ',') {
3140 g_string_append_c(norm_utf7, '/');
3141 } else if (in_escape && *p == '-') {
3142 g_string_append_c(norm_utf7, '-');
3145 g_string_append_c(norm_utf7, *p);
3149 norm_utf7_p = norm_utf7->str;
3150 norm_utf7_len = norm_utf7->len;
3151 to_len = strlen(mutf7_str) * 5;
3152 to_p = to_str = g_malloc(to_len + 1);
3154 if (iconv(cd, (ICONV_CONST gchar **)&norm_utf7_p, &norm_utf7_len,
3155 &to_p, &to_len) == -1) {
3156 g_warning(_("iconv cannot convert UTF-7 to %s\n"),
3157 conv_get_locale_charset_str());
3158 g_string_free(norm_utf7, TRUE);
3160 return g_strdup(mutf7_str);
3163 /* second iconv() call for flushing */
3164 iconv(cd, NULL, NULL, &to_p, &to_len);
3165 g_string_free(norm_utf7, TRUE);
3171 static gchar *imap_utf8_to_modified_utf7(const gchar *from)
3173 static iconv_t cd = (iconv_t)-1;
3174 static gboolean iconv_ok = TRUE;
3175 gchar *norm_utf7, *norm_utf7_p;
3176 size_t from_len, norm_utf7_len;
3178 gchar *from_tmp, *to, *p;
3179 gboolean in_escape = FALSE;
3181 if (!iconv_ok) return g_strdup(from);
3183 if (cd == (iconv_t)-1) {
3184 cd = iconv_open(CS_UTF_7, CS_INTERNAL);
3185 if (cd == (iconv_t)-1) {
3186 g_warning(_("iconv cannot convert %s to UTF-7\n"),
3189 return g_strdup(from);
3193 /* UTF-8 to normal UTF-7 conversion */
3194 Xstrdup_a(from_tmp, from, return g_strdup(from));
3195 from_len = strlen(from);
3196 norm_utf7_len = from_len * 5;
3197 Xalloca(norm_utf7, norm_utf7_len + 1, return g_strdup(from));
3198 norm_utf7_p = norm_utf7;
3200 #define IS_PRINT(ch) (isprint(ch) && IS_ASCII(ch))
3202 while (from_len > 0) {
3203 if (*from_tmp == '+') {
3204 *norm_utf7_p++ = '+';
3205 *norm_utf7_p++ = '-';
3209 } else if (IS_PRINT(*(guchar *)from_tmp)) {
3210 /* printable ascii char */
3211 *norm_utf7_p = *from_tmp;
3217 size_t conv_len = 0;
3219 /* unprintable char: convert to UTF-7 */
3221 while (!IS_PRINT(*(guchar *)p) && conv_len < from_len) {
3222 conv_len += g_utf8_skip[*(guchar *)p];
3223 p += g_utf8_skip[*(guchar *)p];
3226 from_len -= conv_len;
3227 if (iconv(cd, (ICONV_CONST gchar **)&from_tmp,
3229 &norm_utf7_p, &norm_utf7_len) == -1) {
3230 g_warning(_("iconv cannot convert UTF-8 to UTF-7\n"));
3231 return g_strdup(from);
3234 /* second iconv() call for flushing */
3235 iconv(cd, NULL, NULL, &norm_utf7_p, &norm_utf7_len);
3241 *norm_utf7_p = '\0';
3242 to_str = g_string_new(NULL);
3243 for (p = norm_utf7; p < norm_utf7_p; p++) {
3244 /* replace: '&' -> "&-",
3247 BASE64 '/' -> ',' */
3248 if (!in_escape && *p == '&') {
3249 g_string_append(to_str, "&-");
3250 } else if (!in_escape && *p == '+') {
3251 if (*(p + 1) == '-') {
3252 g_string_append_c(to_str, '+');
3255 g_string_append_c(to_str, '&');
3258 } else if (in_escape && *p == '/') {
3259 g_string_append_c(to_str, ',');
3260 } else if (in_escape && *p == '-') {
3261 g_string_append_c(to_str, '-');
3264 g_string_append_c(to_str, *p);
3270 g_string_append_c(to_str, '-');
3274 g_string_free(to_str, FALSE);
3279 static gboolean imap_rename_folder_func(GNode *node, gpointer data)
3281 FolderItem *item = node->data;
3282 gchar **paths = data;
3283 const gchar *oldpath = paths[0];
3284 const gchar *newpath = paths[1];
3285 gchar *real_oldpath, *real_newpath;
3287 gchar *new_itempath;
3289 IMAPSession *session = imap_session_get(item->folder);
3291 oldpathlen = strlen(oldpath);
3292 if (strncmp(oldpath, item->path, oldpathlen) != 0) {
3293 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
3297 base = item->path + oldpathlen;
3298 while (*base == G_DIR_SEPARATOR) base++;
3300 new_itempath = g_strdup(newpath);
3302 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
3305 real_oldpath = imap_get_real_path(session, IMAP_FOLDER(item->folder), item->path);
3307 item->path = new_itempath;
3309 real_newpath = imap_get_real_path(session, IMAP_FOLDER(item->folder), item->path);
3311 imap_threaded_subscribe(item->folder, real_oldpath, FALSE);
3312 imap_threaded_subscribe(item->folder, real_newpath, TRUE);
3314 g_free(real_oldpath);
3315 g_free(real_newpath);
3319 typedef struct _get_list_uid_data {
3321 IMAPSession *session;
3322 IMAPFolderItem *item;
3323 GSList **msgnum_list;
3325 } get_list_uid_data;
3327 static void *get_list_of_uids_thread(void *data)
3329 get_list_uid_data *stuff = (get_list_uid_data *)data;
3330 Folder *folder = stuff->folder;
3331 IMAPFolderItem *item = stuff->item;
3332 GSList **msgnum_list = stuff->msgnum_list;
3333 gint ok, nummsgs = 0, lastuid_old;
3334 IMAPSession *session;
3335 GSList *uidlist, *elem;
3337 clist * lep_uidlist;
3339 session = stuff->session;
3340 if (session == NULL) {
3342 return GINT_TO_POINTER(-1);
3344 /* no session locking here, it's already locked by caller */
3345 ok = imap_select(session, IMAP_FOLDER(folder), item->item.path,
3346 NULL, NULL, NULL, NULL, TRUE);
3347 if (ok != IMAP_SUCCESS) {
3349 return GINT_TO_POINTER(-1);
3354 if (folder->account && folder->account->low_bandwidth) {
3355 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SIMPLE, NULL,
3359 if (r == MAILIMAP_NO_ERROR) {
3360 GSList * fetchuid_list =
3361 imap_uid_list_from_lep(lep_uidlist);
3362 mailimap_search_result_free(lep_uidlist);
3364 uidlist = g_slist_concat(fetchuid_list, uidlist);
3366 carray * lep_uidtab;
3367 r = imap_threaded_fetch_uid(folder, 1,
3369 if (r == MAILIMAP_NO_ERROR) {
3370 GSList * fetchuid_list =
3371 imap_uid_list_from_lep_tab(lep_uidtab);
3372 imap_fetch_uid_list_free(lep_uidtab);
3373 uidlist = g_slist_concat(fetchuid_list, uidlist);
3377 if (r != MAILIMAP_NO_ERROR) {
3379 return GINT_TO_POINTER(-1);
3382 lastuid_old = item->lastuid;
3383 *msgnum_list = g_slist_copy(item->uid_list);
3384 nummsgs = g_slist_length(*msgnum_list);
3385 debug_print("Got %d uids from cache\n", g_slist_length(item->uid_list));
3387 for (elem = uidlist; elem != NULL; elem = g_slist_next(elem)) {
3390 msgnum = GPOINTER_TO_INT(elem->data);
3391 if (msgnum > lastuid_old) {
3392 *msgnum_list = g_slist_prepend(*msgnum_list, GINT_TO_POINTER(msgnum));
3393 item->uid_list = g_slist_prepend(item->uid_list, GINT_TO_POINTER(msgnum));
3396 if(msgnum > item->lastuid)
3397 item->lastuid = msgnum;
3400 g_slist_free(uidlist);
3402 return GINT_TO_POINTER(nummsgs);
3405 static gint get_list_of_uids(IMAPSession *session, Folder *folder, IMAPFolderItem *item, GSList **msgnum_list)
3408 get_list_uid_data *data = g_new0(get_list_uid_data, 1);
3410 data->folder = folder;
3412 data->msgnum_list = msgnum_list;
3413 data->session = session;
3414 if (prefs_common.work_offline &&
3415 !inc_offline_should_override(FALSE,
3416 _("Claws Mail needs network access in order "
3417 "to access the IMAP server."))) {
3422 result = GPOINTER_TO_INT(get_list_of_uids_thread(data));
3428 gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list, gboolean *old_uids_valid)
3430 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3431 IMAPSession *session;
3432 gint ok, nummsgs = 0, exists;
3433 guint32 uid_next = 0, uid_val = 0;
3434 GSList *uidlist = NULL;
3436 gboolean selected_folder;
3437 debug_print("get_num_list\n");
3439 g_return_val_if_fail(folder != NULL, -1);
3440 g_return_val_if_fail(item != NULL, -1);
3441 g_return_val_if_fail(item->item.path != NULL, -1);
3442 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3443 g_return_val_if_fail(folder->account != NULL, -1);
3445 debug_print("getting session...\n");
3446 session = imap_session_get(folder);
3447 g_return_val_if_fail(session != NULL, -1);
3449 if (FOLDER_ITEM(item)->path)
3450 statusbar_print_all(_("Scanning folder %s%c%s ..."),
3451 FOLDER_ITEM(item)->folder->name,
3453 FOLDER_ITEM(item)->path);
3455 statusbar_print_all(_("Scanning folder %s ..."),
3456 FOLDER_ITEM(item)->folder->name);
3458 selected_folder = (session->mbox != NULL) &&
3459 (!strcmp(session->mbox, item->item.path));
3460 if (selected_folder && time(NULL) - item->use_cache < 2) {
3461 ok = imap_cmd_noop(session);
3462 if (ok != IMAP_SUCCESS) {
3463 debug_print("disconnected!\n");
3464 session = imap_reconnect_if_possible(folder, session);
3465 if (session == NULL) {
3466 statusbar_pop_all();
3467 unlock_session(session);
3471 exists = session->exists;
3473 uid_next = item->c_uid_next;
3474 uid_val = item->c_uid_validity;
3475 *old_uids_valid = TRUE;
3477 if (item->use_cache && time(NULL) - item->use_cache < 2) {
3478 exists = item->c_messages;
3479 uid_next = item->c_uid_next;
3480 uid_val = item->c_uid_validity;
3482 debug_print("using cache %d %d %d\n", exists, uid_next, uid_val);
3484 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path, item,
3485 &exists, &uid_next, &uid_val, NULL, FALSE);
3487 item->item.last_num = uid_next - 1;
3489 item->use_cache = (time_t)0;
3490 if (ok != IMAP_SUCCESS) {
3491 statusbar_pop_all();
3492 unlock_session(session);
3495 if(item->item.mtime == uid_val)
3496 *old_uids_valid = TRUE;
3498 *old_uids_valid = FALSE;
3500 debug_print("Freeing imap uid cache (%d != %d)\n",
3501 (int)item->item.mtime, uid_val);
3503 g_slist_free(item->uid_list);
3504 item->uid_list = NULL;
3506 item->item.mtime = uid_val;
3508 imap_delete_all_cached_messages((FolderItem *)item);
3512 /* If old uid_next matches new uid_next we can be sure no message
3513 was added to the folder */
3514 debug_print("uid_next is %d and item->uid_next %d \n",
3515 uid_next, item->uid_next);
3516 if (uid_next == item->uid_next) {
3517 nummsgs = g_slist_length(item->uid_list);
3519 /* If number of messages is still the same we
3520 know our caches message numbers are still valid,
3521 otherwise if the number of messages has decrease
3522 we discard our cache to start a new scan to find
3523 out which numbers have been removed */
3524 if (exists == nummsgs) {
3525 debug_print("exists == nummsgs\n");
3526 *msgnum_list = g_slist_copy(item->uid_list);
3527 statusbar_pop_all();
3528 unlock_session(session);
3530 } else if (exists < nummsgs) {
3531 debug_print("Freeing imap uid cache");
3533 g_slist_free(item->uid_list);
3534 item->uid_list = NULL;
3539 *msgnum_list = NULL;
3540 statusbar_pop_all();
3541 unlock_session(session);
3545 item->last_change = time(NULL);
3546 nummsgs = get_list_of_uids(session, folder, item, &uidlist);
3549 statusbar_pop_all();
3550 unlock_session(session);
3554 if (nummsgs != exists) {
3555 /* Cache contains more messages then folder, we have cached
3556 an old UID of a message that was removed and new messages
3557 have been added too, otherwise the uid_next check would
3559 debug_print("Freeing imap uid cache");
3561 g_slist_free(item->uid_list);
3562 item->uid_list = NULL;
3564 g_slist_free(*msgnum_list);
3566 nummsgs = get_list_of_uids(session, folder, item, &uidlist);
3569 *msgnum_list = uidlist;
3571 dir = folder_item_get_path((FolderItem *)item);
3572 debug_print("removing old messages from %s\n", dir);
3573 remove_numbered_files_not_in_list(dir, *msgnum_list);
3576 item->uid_next = uid_next;
3578 debug_print("get_num_list - ok - %i\n", nummsgs);
3579 statusbar_pop_all();
3580 unlock_session(session);
3584 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item)
3589 flags.perm_flags = MSG_NEW|MSG_UNREAD;
3590 flags.tmp_flags = 0;
3592 g_return_val_if_fail(item != NULL, NULL);
3593 g_return_val_if_fail(file != NULL, NULL);
3595 if (folder_has_parent_of_type(item, F_QUEUE)) {
3596 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
3597 } else if (folder_has_parent_of_type(item, F_DRAFT)) {
3598 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
3601 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3602 if (!msginfo) return NULL;
3604 msginfo->plaintext_file = g_strdup(file);
3605 msginfo->folder = item;
3610 GSList *imap_get_msginfos(Folder *folder, FolderItem *item,
3611 GSList *msgnum_list)
3613 IMAPSession *session;
3614 MsgInfoList *ret = NULL;
3617 debug_print("get_msginfos\n");
3619 g_return_val_if_fail(folder != NULL, NULL);
3620 g_return_val_if_fail(item != NULL, NULL);
3621 g_return_val_if_fail(msgnum_list != NULL, NULL);
3623 debug_print("getting session...\n");
3624 session = imap_session_get(folder);
3625 g_return_val_if_fail(session != NULL, NULL);
3627 debug_print("IMAP getting msginfos\n");
3628 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3629 NULL, NULL, NULL, NULL, FALSE);
3630 if (ok != IMAP_SUCCESS) {
3631 unlock_session(session);
3634 if (!(folder_has_parent_of_type(item, F_DRAFT) ||
3635 folder_has_parent_of_type(item, F_QUEUE))) {
3636 ret = g_slist_concat(ret,
3637 imap_get_uncached_messages(session, item,
3640 MsgNumberList *sorted_list, *elem, *llast = NULL;
3641 gint startnum, lastnum;
3643 sorted_list = g_slist_sort(g_slist_copy(msgnum_list), g_int_compare);
3645 startnum = lastnum = GPOINTER_TO_INT(sorted_list->data);
3647 llast = g_slist_last(ret);
3648 for (elem = sorted_list;; elem = g_slist_next(elem)) {
3652 num = GPOINTER_TO_INT(elem->data);
3654 if (num > lastnum + 1 || elem == NULL) {
3656 for (i = startnum; i <= lastnum; ++i) {
3659 file = imap_fetch_msg(folder, item, i);
3661 MsgInfo *msginfo = imap_parse_msg(file, item);
3662 if (msginfo != NULL) {
3663 msginfo->msgnum = i;
3665 llast = ret = g_slist_append(ret, msginfo);
3667 llast = g_slist_append(llast, msginfo);
3668 llast = llast->next;
3673 session_set_access_time(SESSION(session));
3684 g_slist_free(sorted_list);
3686 unlock_session(session);
3690 MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid)
3692 MsgInfo *msginfo = NULL;
3693 MsgInfoList *msginfolist;
3694 MsgNumberList numlist;
3696 numlist.next = NULL;
3697 numlist.data = GINT_TO_POINTER(uid);
3699 msginfolist = imap_get_msginfos(folder, item, &numlist);
3700 if (msginfolist != NULL) {
3701 msginfo = msginfolist->data;
3702 g_slist_free(msginfolist);
3708 gboolean imap_scan_required(Folder *folder, FolderItem *_item)
3710 IMAPSession *session;
3711 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3712 gint ok, exists = 0, unseen = 0;
3713 guint32 uid_next = 0, uid_val = 0;
3714 gboolean selected_folder;
3716 g_return_val_if_fail(folder != NULL, FALSE);
3717 g_return_val_if_fail(item != NULL, FALSE);
3718 g_return_val_if_fail(item->item.folder != NULL, FALSE);
3719 g_return_val_if_fail(FOLDER_CLASS(item->item.folder) == &imap_class, FALSE);
3721 if (item->item.path == NULL)
3724 debug_print("getting session...\n");
3725 session = imap_session_get(folder);
3726 g_return_val_if_fail(session != NULL, FALSE);
3728 selected_folder = (session->mbox != NULL) &&
3729 (!strcmp(session->mbox, item->item.path));
3730 if (selected_folder && time(NULL) - item->use_cache < 2) {
3731 ok = imap_cmd_noop(session);
3732 if (ok != IMAP_SUCCESS) {
3733 debug_print("disconnected!\n");
3734 session = imap_reconnect_if_possible(folder, session);
3735 if (session == NULL)
3739 if (session->folder_content_changed
3740 || session->exists != item->item.total_msgs) {
3741 unlock_session(session);
3745 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path, IMAP_FOLDER_ITEM(item),
3746 &exists, &uid_next, &uid_val, &unseen, FALSE);
3747 if (ok != IMAP_SUCCESS) {
3748 unlock_session(session);
3752 item->use_cache = time(NULL);
3753 item->c_messages = exists;
3754 item->c_uid_next = uid_next;
3755 item->c_uid_validity = uid_val;
3756 item->c_unseen = unseen;
3757 item->item.last_num = uid_next - 1;
3758 debug_print("uidnext %d, item->uid_next %d, exists %d, item->item.total_msgs %d\n",
3759 uid_next, item->uid_next, exists, item->item.total_msgs);
3760 if ((uid_next != item->uid_next) || (exists != item->item.total_msgs)
3761 || unseen != item->item.unread_msgs || uid_val != item->item.mtime) {
3762 unlock_session(session);
3763 item->last_change = time(NULL);
3767 unlock_session(session);
3771 void imap_change_flags(Folder *folder, FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
3773 IMAPSession *session;
3774 IMAPFlags flags_set = 0, flags_unset = 0;
3775 gint ok = IMAP_SUCCESS;
3776 MsgNumberList numlist;
3777 hashtable_data *ht_data = NULL;
3779 g_return_if_fail(folder != NULL);
3780 g_return_if_fail(folder->klass == &imap_class);
3781 g_return_if_fail(item != NULL);
3782 g_return_if_fail(item->folder == folder);
3783 g_return_if_fail(msginfo != NULL);
3784 g_return_if_fail(msginfo->folder == item);
3786 if (!MSG_IS_MARKED(msginfo->flags) && (newflags & MSG_MARKED))
3787 flags_set |= IMAP_FLAG_FLAGGED;
3788 if ( MSG_IS_MARKED(msginfo->flags) && !(newflags & MSG_MARKED))
3789 flags_unset |= IMAP_FLAG_FLAGGED;
3791 if (!MSG_IS_UNREAD(msginfo->flags) && (newflags & MSG_UNREAD))
3792 flags_unset |= IMAP_FLAG_SEEN;
3793 if ( MSG_IS_UNREAD(msginfo->flags) && !(newflags & MSG_UNREAD))
3794 flags_set |= IMAP_FLAG_SEEN;
3796 if (!MSG_IS_REPLIED(msginfo->flags) && (newflags & MSG_REPLIED))
3797 flags_set |= IMAP_FLAG_ANSWERED;
3798 if ( MSG_IS_REPLIED(msginfo->flags) && !(newflags & MSG_REPLIED))
3799 flags_unset |= IMAP_FLAG_ANSWERED;
3801 if (!MSG_IS_DELETED(msginfo->flags) && (newflags & MSG_DELETED))
3802 flags_set |= IMAP_FLAG_DELETED;
3803 if ( MSG_IS_DELETED(msginfo->flags) && !(newflags & MSG_DELETED))
3804 flags_unset |= IMAP_FLAG_DELETED;
3806 if (!flags_set && !flags_unset) {
3807 /* the changed flags were not translatable to IMAP-speak.
3808 * like MSG_POSTFILTERED, so just apply. */
3809 msginfo->flags.perm_flags = newflags;
3813 debug_print("getting session...\n");
3814 session = imap_session_get(folder);
3819 if ((ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
3820 NULL, NULL, NULL, NULL, FALSE)) != IMAP_SUCCESS) {
3821 unlock_session(session);
3824 numlist.next = NULL;
3825 numlist.data = GINT_TO_POINTER(msginfo->msgnum);
3827 if (IMAP_FOLDER_ITEM(item)->batching) {
3828 /* instead of performing an UID STORE command for each message change,
3829 * as a lot of them can change "together", we just fill in hashtables
3830 * and defer the treatment so that we're able to send only one
3833 debug_print("IMAP batch mode on, deferring flags change\n");
3835 ht_data = g_hash_table_lookup(IMAP_FOLDER_ITEM(item)->flags_set_table,
3836 GINT_TO_POINTER(flags_set));
3837 if (ht_data == NULL) {
3838 ht_data = g_new0(hashtable_data, 1);
3839 ht_data->session = session;
3840 ht_data->item = IMAP_FOLDER_ITEM(item);
3841 g_hash_table_insert(IMAP_FOLDER_ITEM(item)->flags_set_table,
3842 GINT_TO_POINTER(flags_set), ht_data);
3844 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
3845 ht_data->msglist = g_slist_prepend(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum));
3848 ht_data = g_hash_table_lookup(IMAP_FOLDER_ITEM(item)->flags_unset_table,
3849 GINT_TO_POINTER(flags_unset));
3850 if (ht_data == NULL) {
3851 ht_data = g_new0(hashtable_data, 1);
3852 ht_data->session = session;
3853 ht_data->item = IMAP_FOLDER_ITEM(item);
3854 g_hash_table_insert(IMAP_FOLDER_ITEM(item)->flags_unset_table,
3855 GINT_TO_POINTER(flags_unset), ht_data);
3857 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
3858 ht_data->msglist = g_slist_prepend(ht_data->msglist,
3859 GINT_TO_POINTER(msginfo->msgnum));
3862 debug_print("IMAP changing flags\n");
3864 ok = imap_set_message_flags(session, &numlist, flags_set, TRUE);
3865 if (ok != IMAP_SUCCESS) {
3866 unlock_session(session);
3872 ok = imap_set_message_flags(session, &numlist, flags_unset, FALSE);
3873 if (ok != IMAP_SUCCESS) {
3874 unlock_session(session);
3879 msginfo->flags.perm_flags = newflags;
3880 unlock_session(session);
3884 static gint imap_remove_msg(Folder *folder, FolderItem *item, gint uid)
3887 IMAPSession *session;
3889 MsgNumberList numlist;
3891 g_return_val_if_fail(folder != NULL, -1);
3892 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3893 g_return_val_if_fail(item != NULL, -1);
3895 debug_print("getting session...\n");
3896 session = imap_session_get(folder);
3897 if (!session) return -1;
3899 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3900 NULL, NULL, NULL, NULL, FALSE);
3901 if (ok != IMAP_SUCCESS) {
3902 unlock_session(session);
3905 numlist.next = NULL;
3906 numlist.data = GINT_TO_POINTER(uid);
3908 ok = imap_set_message_flags
3909 (session, &numlist, IMAP_FLAG_DELETED, TRUE);
3910 if (ok != IMAP_SUCCESS) {
3911 log_warning(LOG_PROTOCOL, _("can't set deleted flags: %d\n"), uid);
3912 unlock_session(session);
3916 if (!session->uidplus) {
3917 ok = imap_cmd_expunge(session);
3921 uidstr = g_strdup_printf("%u", uid);
3922 ok = imap_cmd_expunge(session);
3925 if (ok != IMAP_SUCCESS) {
3926 log_warning(LOG_PROTOCOL, _("can't expunge\n"));
3927 unlock_session(session);
3931 IMAP_FOLDER_ITEM(item)->uid_list = g_slist_remove(
3932 IMAP_FOLDER_ITEM(item)->uid_list, numlist.data);
3933 dir = folder_item_get_path(item);
3934 if (is_dir_exist(dir))
3935 remove_numbered_files(dir, uid, uid);
3937 unlock_session(session);
3938 return IMAP_SUCCESS;
3941 static gint compare_msginfo(gconstpointer a, gconstpointer b)
3943 return ((MsgInfo *)a)->msgnum - ((MsgInfo *)b)->msgnum;
3946 static guint gslist_find_next_num(MsgNumberList **list, guint num)
3950 g_return_val_if_fail(list != NULL, -1);
3952 for (elem = *list; elem != NULL; elem = g_slist_next(elem))
3953 if (GPOINTER_TO_INT(elem->data) >= num)
3956 return elem != NULL ? GPOINTER_TO_INT(elem->data) : (gint)-1;
3960 * NEW and DELETED flags are not syncronized
3961 * - The NEW/RECENT flags in IMAP folders can not really be directly
3962 * modified by Sylpheed
3963 * - The DELETE/DELETED flag in IMAP and Sylpheed don't have the same
3964 * meaning, in IMAP it always removes the messages from the FolderItem
3965 * in Sylpheed it can mean to move the message to trash
3968 typedef struct _get_flags_data {
3971 MsgInfoList *msginfo_list;
3972 GRelation *msgflags;
3973 gboolean full_search;
3977 static /*gint*/ void *imap_get_flags_thread(void *data)
3979 get_flags_data *stuff = (get_flags_data *)data;
3980 Folder *folder = stuff->folder;
3981 FolderItem *fitem = (FolderItem *) stuff->item;
3982 MsgInfoList *msginfo_list = stuff->msginfo_list;
3983 GRelation *msgflags = stuff->msgflags;
3985 GSList * fetchuid_list;
3986 carray * lep_uidtab;
3987 IMAPSession *session;
3990 GHashTable *flags_hash = NULL;
3991 gboolean full_search = stuff->full_search;
3992 GSList *sorted_list = NULL;
3993 GSList *unseen = NULL, *answered = NULL, *flagged = NULL, *deleted = NULL;
3994 GSList *p_unseen, *p_answered, *p_flagged, *p_deleted;
3995 GSList *seq_list, *cur;
3996 gboolean reverse_seen = FALSE;
3997 gboolean selected_folder;
3998 gint exists_cnt, unseen_cnt;
4000 session = imap_session_get(folder);
4001 if (session == NULL) {
4003 return GINT_TO_POINTER(-1);
4005 selected_folder = (session->mbox != NULL) &&
4006 (!strcmp(session->mbox, fitem->path));
4008 if (!selected_folder) {
4009 ok = imap_select(session, IMAP_FOLDER(folder), fitem->path,
4010 &exists_cnt, NULL, &unseen_cnt, NULL, TRUE);
4011 if (ok != IMAP_SUCCESS) {
4013 unlock_session(session);
4014 return GINT_TO_POINTER(-1);
4017 if (unseen_cnt > exists_cnt / 2)
4018 reverse_seen = TRUE;
4021 if (fitem->unread_msgs > fitem->total_msgs / 2)
4022 reverse_seen = TRUE;
4025 sorted_list = g_slist_sort(g_slist_copy(msginfo_list), compare_msginfo);
4027 seq_list = imap_get_lep_set_from_msglist(msginfo_list);
4029 struct mailimap_set * set;
4030 set = mailimap_set_new_interval(1, 0);
4031 seq_list = g_slist_append(NULL, set);
4034 if (folder->account && folder->account->low_bandwidth) {
4035 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
4036 struct mailimap_set * imapset;
4037 clist * lep_uidlist;
4040 imapset = cur->data;
4042 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SEEN,
4043 full_search ? NULL:imapset, &lep_uidlist);
4046 r = imap_threaded_search(folder,
4047 IMAP_SEARCH_TYPE_UNSEEN,
4048 full_search ? NULL:imapset, &lep_uidlist);
4050 if (r == MAILIMAP_NO_ERROR) {
4053 uidlist = imap_uid_list_from_lep(lep_uidlist);
4054 mailimap_search_result_free(lep_uidlist);
4056 unseen = g_slist_concat(unseen, uidlist);
4059 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_FLAGGED,
4060 full_search ? NULL:imapset, &lep_uidlist);
4061 if (r == MAILIMAP_NO_ERROR) {
4064 uidlist = imap_uid_list_from_lep(lep_uidlist);
4065 mailimap_search_result_free(lep_uidlist);
4067 flagged = g_slist_concat(flagged, uidlist);
4070 if (fitem->opened || fitem->processing_pending || fitem == folder->inbox) {
4071 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_ANSWERED,
4072 full_search ? NULL:imapset, &lep_uidlist);
4073 if (r == MAILIMAP_NO_ERROR) {
4076 uidlist = imap_uid_list_from_lep(lep_uidlist);
4077 mailimap_search_result_free(lep_uidlist);
4079 answered = g_slist_concat(answered, uidlist);
4082 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_DELETED,
4083 full_search ? NULL:imapset, &lep_uidlist);
4084 if (r == MAILIMAP_NO_ERROR) {
4087 uidlist = imap_uid_list_from_lep(lep_uidlist);
4088 mailimap_search_result_free(lep_uidlist);
4090 deleted = g_slist_concat(deleted, uidlist);
4095 p_answered = answered;
4096 p_flagged = flagged;
4097 p_deleted = deleted;
4100 r = imap_threaded_fetch_uid_flags(folder, 1, &lep_uidtab);
4101 if (r == MAILIMAP_NO_ERROR) {
4103 imap_uid_list_from_lep_uid_flags_tab(lep_uidtab);
4105 flags_hash = g_hash_table_new_full(g_int_hash, g_int_equal, free, NULL);
4106 imap_flags_hash_from_lep_uid_flags_tab(lep_uidtab, flags_hash);
4107 imap_fetch_uid_flags_list_free(lep_uidtab);
4110 for (elem = sorted_list; elem != NULL; elem = g_slist_next(elem)) {
4113 gboolean wasnew, waspostfiltered;
4115 msginfo = (MsgInfo *) elem->data;
4116 flags = msginfo->flags.perm_flags;
4117 wasnew = (flags & MSG_NEW);
4118 waspostfiltered = (flags & MSG_POSTFILTERED);
4120 if (folder->account && folder->account->low_bandwidth) {
4121 if (fitem->opened || fitem->processing_pending || fitem == folder->inbox) {
4122 flags &= ~((reverse_seen ? 0 : MSG_UNREAD | MSG_NEW) | MSG_REPLIED | MSG_MARKED);
4124 flags &= ~((reverse_seen ? 0 : MSG_UNREAD | MSG_NEW | MSG_MARKED));
4127 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
4128 if (gslist_find_next_num(&p_unseen, msginfo->msgnum) == msginfo->msgnum) {
4129 if (!reverse_seen) {
4130 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
4132 flags &= ~(MSG_UNREAD | MSG_NEW);
4136 if (gslist_find_next_num(&p_flagged, msginfo->msgnum) == msginfo->msgnum)
4137 flags |= MSG_MARKED;
4139 flags &= ~MSG_MARKED;
4141 if (fitem->opened || fitem->processing_pending || fitem == folder->inbox) {
4142 if (gslist_find_next_num(&p_answered, msginfo->msgnum) == msginfo->msgnum)
4143 flags |= MSG_REPLIED;
4145 flags &= ~MSG_REPLIED;
4146 if (gslist_find_next_num(&p_deleted, msginfo->msgnum) == msginfo->msgnum)
4147 flags |= MSG_DELETED;
4149 flags &= ~MSG_DELETED;
4152 if (flags_hash != NULL) {
4155 puid = malloc(sizeof(* puid));
4156 * puid = msginfo->msgnum;
4158 flags = GPOINTER_TO_INT(g_hash_table_lookup(flags_hash, puid));
4162 if ((flags & MSG_UNREAD) == 0)
4167 if (waspostfiltered)
4168 flags |= MSG_POSTFILTERED;
4169 g_relation_insert(msgflags, msginfo, GINT_TO_POINTER(flags));
4173 g_hash_table_destroy(flags_hash);
4175 imap_lep_set_free(seq_list);
4176 g_slist_free(flagged);
4177 g_slist_free(deleted);
4178 g_slist_free(answered);
4179 g_slist_free(unseen);
4180 g_slist_free(sorted_list);
4182 unlock_session(session);
4184 return GINT_TO_POINTER(0);
4187 static gint imap_get_flags(Folder *folder, FolderItem *item,
4188 MsgInfoList *msginfo_list, GRelation *msgflags)
4191 get_flags_data *data = g_new0(get_flags_data, 1);
4193 data->folder = folder;
4195 data->msginfo_list = msginfo_list;
4196 data->msgflags = msgflags;
4197 data->full_search = FALSE;
4199 GSList *tmp = NULL, *cur;
4201 if (prefs_common.work_offline &&
4202 !inc_offline_should_override(FALSE,
4203 _("Claws Mail needs network access in order "
4204 "to access the IMAP server."))) {
4209 tmp = folder_item_get_msg_list(item);
4211 if (g_slist_length(tmp) <= g_slist_length(msginfo_list))
4212 data->full_search = TRUE;
4214 for (cur = tmp; cur; cur = cur->next)
4215 procmsg_msginfo_free((MsgInfo *)cur->data);
4219 result = GPOINTER_TO_INT(imap_get_flags_thread(data));
4226 static gboolean process_flags(gpointer key, gpointer value, gpointer user_data)
4228 gboolean flags_set = GPOINTER_TO_INT(user_data);
4229 gint flags_value = GPOINTER_TO_INT(key);
4230 hashtable_data *data = (hashtable_data *)value;
4231 IMAPFolderItem *_item = data->item;
4232 FolderItem *item = (FolderItem *)_item;
4233 gint ok = IMAP_ERROR;
4234 IMAPSession *session = NULL;
4236 debug_print("getting session...\n");
4237 session = imap_session_get(item->folder);
4239 data->msglist = g_slist_reverse(data->msglist);
4241 debug_print("IMAP %ssetting flags to %d for %d messages\n",
4244 g_slist_length(data->msglist));
4247 ok = imap_select(session, IMAP_FOLDER(item->folder), item->path,
4248 NULL, NULL, NULL, NULL, FALSE);
4250 if (ok == IMAP_SUCCESS) {
4251 imap_set_message_flags(data->session, data->msglist, flags_value, flags_set);
4253 g_warning("can't select mailbox %s\n", item->path);
4256 unlock_session(session);
4257 g_slist_free(data->msglist);
4262 static void process_hashtable(IMAPFolderItem *item)
4264 if (item->flags_set_table) {
4265 g_hash_table_foreach_remove(item->flags_set_table, process_flags, GINT_TO_POINTER(TRUE));
4266 g_hash_table_destroy(item->flags_set_table);
4267 item->flags_set_table = NULL;
4269 if (item->flags_unset_table) {
4270 g_hash_table_foreach_remove(item->flags_unset_table, process_flags, GINT_TO_POINTER(FALSE));
4271 g_hash_table_destroy(item->flags_unset_table);
4272 item->flags_unset_table = NULL;
4276 static void imap_set_batch (Folder *folder, FolderItem *_item, gboolean batch)
4278 IMAPFolderItem *item = (IMAPFolderItem *)_item;
4280 g_return_if_fail(item != NULL);
4282 if (item->batching == batch)
4286 item->batching = TRUE;
4287 debug_print("IMAP switching to batch mode\n");
4288 if (!item->flags_set_table) {
4289 item->flags_set_table = g_hash_table_new(NULL, g_direct_equal);
4291 if (!item->flags_unset_table) {
4292 item->flags_unset_table = g_hash_table_new(NULL, g_direct_equal);
4295 debug_print("IMAP switching away from batch mode\n");
4297 process_hashtable(item);
4298 item->batching = FALSE;
4304 /* data types conversion libetpan <-> claws */
4308 #define ETPAN_IMAP_MB_MARKED 1
4309 #define ETPAN_IMAP_MB_UNMARKED 2
4310 #define ETPAN_IMAP_MB_NOSELECT 4
4311 #define ETPAN_IMAP_MB_NOINFERIORS 8
4313 static int imap_flags_to_flags(struct mailimap_mbx_list_flags * imap_flags)
4319 if (imap_flags->mbf_type == MAILIMAP_MBX_LIST_FLAGS_SFLAG) {
4320 switch (imap_flags->mbf_sflag) {
4321 case MAILIMAP_MBX_LIST_SFLAG_MARKED:
4322 flags |= ETPAN_IMAP_MB_MARKED;
4324 case MAILIMAP_MBX_LIST_SFLAG_NOSELECT:
4325 flags |= ETPAN_IMAP_MB_NOSELECT;
4327 case MAILIMAP_MBX_LIST_SFLAG_UNMARKED:
4328 flags |= ETPAN_IMAP_MB_UNMARKED;
4333 for(cur = clist_begin(imap_flags->mbf_oflags) ; cur != NULL ;
4334 cur = clist_next(cur)) {
4335 struct mailimap_mbx_list_oflag * oflag;
4337 oflag = clist_content(cur);
4339 switch (oflag->of_type) {
4340 case MAILIMAP_MBX_LIST_OFLAG_NOINFERIORS:
4341 flags |= ETPAN_IMAP_MB_NOINFERIORS;
4349 static GSList * imap_list_from_lep(IMAPFolder * folder,
4350 clist * list, const gchar * real_path, gboolean all)
4353 GSList * item_list = NULL, *llast = NULL;
4355 for(iter = clist_begin(list) ; iter != NULL ;
4356 iter = clist_next(iter)) {
4357 struct mailimap_mailbox_list * mb;
4365 FolderItem *new_item;
4367 mb = clist_content(iter);
4373 if (mb->mb_flag != NULL)
4374 flags = imap_flags_to_flags(mb->mb_flag);
4376 delimiter = mb->mb_delimiter;
4379 dup_name = strdup(name);
4380 if (delimiter != '\0')
4381 subst_char(dup_name, delimiter, '/');
4383 base = g_path_get_basename(dup_name);
4384 if (base[0] == '.') {
4389 if (!all && path_cmp(name, real_path) == 0) {
4395 if (!all && dup_name[strlen(dup_name)-1] == '/') {
4396 dup_name[strlen(dup_name)-1] = '\0';
4399 loc_name = imap_modified_utf7_to_utf8(base);
4400 loc_path = imap_modified_utf7_to_utf8(dup_name);
4402 new_item = folder_item_new(FOLDER(folder), loc_name, loc_path);
4403 if ((flags & ETPAN_IMAP_MB_NOINFERIORS) != 0)
4404 new_item->no_sub = TRUE;
4405 if (strcmp(dup_name, "INBOX") != 0 &&
4406 ((flags & ETPAN_IMAP_MB_NOSELECT) != 0))
4407 new_item->no_select = TRUE;
4409 if (item_list == NULL)
4410 llast = item_list = g_slist_append(item_list, new_item);
4412 llast = g_slist_append(llast, new_item);
4413 llast = llast->next;
4415 debug_print("folder '%s' found.\n", loc_path);
4426 static GSList * imap_get_lep_set_from_numlist(MsgNumberList *numlist)
4428 GSList *sorted_list, *cur;
4429 guint first, last, next;
4430 GSList *ret_list = NULL, *llast = NULL;
4432 struct mailimap_set * current_set;
4433 unsigned int item_count;
4435 if (numlist == NULL)
4439 current_set = mailimap_set_new_empty();
4441 sorted_list = g_slist_copy(numlist);
4442 sorted_list = g_slist_sort(sorted_list, g_int_compare);
4444 first = GPOINTER_TO_INT(sorted_list->data);
4447 for (cur = sorted_list; cur != NULL; cur = g_slist_next(cur)) {
4448 if (GPOINTER_TO_INT(cur->data) == 0)
4453 last = GPOINTER_TO_INT(cur->data);
4455 next = GPOINTER_TO_INT(cur->next->data);
4459 if (last + 1 != next || next == 0) {
4461 struct mailimap_set_item * item;
4462 item = mailimap_set_item_new(first, last);
4463 mailimap_set_add(current_set, item);
4468 if (count >= IMAP_SET_MAX_COUNT) {
4469 if (ret_list == NULL)
4470 llast = ret_list = g_slist_append(ret_list,
4473 llast = g_slist_append(llast, current_set);
4474 llast = llast->next;
4476 current_set = mailimap_set_new_empty();
4483 if (clist_count(current_set->set_list) > 0) {
4484 ret_list = g_slist_append(ret_list,
4488 g_slist_free(sorted_list);
4493 static GSList * imap_get_lep_set_from_msglist(MsgInfoList *msglist)
4495 MsgNumberList *numlist = NULL;
4499 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
4500 MsgInfo *msginfo = (MsgInfo *) cur->data;
4502 numlist = g_slist_prepend(numlist, GINT_TO_POINTER(msginfo->msgnum));
4504 numlist = g_slist_reverse(numlist);
4505 seq_list = imap_get_lep_set_from_numlist(numlist);
4506 g_slist_free(numlist);
4511 static GSList * imap_uid_list_from_lep(clist * list)
4518 for(iter = clist_begin(list) ; iter != NULL ;
4519 iter = clist_next(iter)) {
4522 puid = clist_content(iter);
4523 result = g_slist_prepend(result, GINT_TO_POINTER(* puid));
4526 result = g_slist_reverse(result);
4530 static GSList * imap_uid_list_from_lep_tab(carray * list)
4537 for(i = 0 ; i < carray_count(list) ; i ++) {
4540 puid = carray_get(list, i);
4541 result = g_slist_prepend(result, GINT_TO_POINTER(* puid));
4543 result = g_slist_reverse(result);
4547 static GSList * imap_uid_list_from_lep_uid_flags_tab(carray * list)
4554 for(i = 0 ; i < carray_count(list) ; i += 2) {
4557 puid = carray_get(list, i);
4558 result = g_slist_prepend(result, GINT_TO_POINTER(* puid));
4560 result = g_slist_reverse(result);
4564 static void imap_flags_hash_from_lep_uid_flags_tab(carray * list,
4572 for(i = 0 ; i < carray_count(list) ; i += 2) {
4577 puid = carray_get(list, i);
4578 pflags = carray_get(list, i + 1);
4579 pguid = malloc(sizeof(* pguid));
4582 g_hash_table_insert(hash, pguid, GINT_TO_POINTER(* pflags));
4586 static MsgInfo *imap_envelope_from_lep(struct imap_fetch_env_info * info,
4589 MsgInfo *msginfo = NULL;
4592 MsgFlags flags = {0, 0};
4594 if (info->headers == NULL)
4597 MSG_SET_TMP_FLAGS(flags, MSG_IMAP);
4598 if (folder_has_parent_of_type(item, F_QUEUE)) {
4599 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
4600 } else if (folder_has_parent_of_type(item, F_DRAFT)) {
4601 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
4603 flags.perm_flags = info->flags;
4607 msginfo = procheader_parse_str(info->headers, flags, FALSE, FALSE);
4610 msginfo->msgnum = uid;
4611 msginfo->size = size;
4617 static void imap_lep_set_free(GSList *seq_list)
4621 for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) {
4622 struct mailimap_set * imapset;
4624 imapset = cur->data;
4625 mailimap_set_free(imapset);
4627 g_slist_free(seq_list);
4630 static struct mailimap_flag_list * imap_flag_to_lep(IMAPFlags flags)
4632 struct mailimap_flag_list * flag_list;
4634 flag_list = mailimap_flag_list_new_empty();
4636 if (IMAP_IS_SEEN(flags))
4637 mailimap_flag_list_add(flag_list,
4638 mailimap_flag_new_seen());
4639 if (IMAP_IS_ANSWERED(flags))
4640 mailimap_flag_list_add(flag_list,
4641 mailimap_flag_new_answered());
4642 if (IMAP_IS_FLAGGED(flags))
4643 mailimap_flag_list_add(flag_list,
4644 mailimap_flag_new_flagged());
4645 if (IMAP_IS_DELETED(flags))
4646 mailimap_flag_list_add(flag_list,
4647 mailimap_flag_new_deleted());
4648 if (IMAP_IS_DRAFT(flags))
4649 mailimap_flag_list_add(flag_list,
4650 mailimap_flag_new_draft());
4655 guint imap_folder_get_refcnt(Folder *folder)
4657 return ((IMAPFolder *)folder)->refcnt;
4660 void imap_folder_ref(Folder *folder)
4662 ((IMAPFolder *)folder)->refcnt++;
4665 void imap_disconnect_all(void)
4668 for (list = account_get_list(); list != NULL; list = list->next) {
4669 PrefsAccount *account = list->data;
4670 if (account->protocol == A_IMAP4) {
4671 RemoteFolder *folder = (RemoteFolder *)account->folder;
4672 if (folder && folder->session) {
4673 IMAPSession *session = (IMAPSession *)folder->session;
4674 imap_threaded_disconnect(FOLDER(folder));
4675 SESSION(session)->state = SESSION_DISCONNECTED;
4676 session_destroy(SESSION(session));
4677 folder->session = NULL;
4683 void imap_folder_unref(Folder *folder)
4685 if (((IMAPFolder *)folder)->refcnt > 0)
4686 ((IMAPFolder *)folder)->refcnt--;
4689 void imap_cancel_all(void)
4694 folderlist = folder_get_list();
4695 for (cur = folderlist; cur != NULL; cur = g_list_next(cur)) {
4696 Folder *folder = (Folder *) cur->data;
4698 if (folder->klass == &imap_class) {
4699 if (imap_is_busy(folder)) {
4700 IMAPSession *imap_session;
4701 RemoteFolder *rfolder;
4703 fprintf(stderr, "cancelled\n");
4704 imap_threaded_cancel(folder);
4705 rfolder = (RemoteFolder *) folder;
4706 imap_session = (IMAPSession *) rfolder->session;
4708 imap_session->cancelled = 1;
4714 gboolean imap_cancel_all_enabled(void)
4719 folderlist = folder_get_list();
4720 for (cur = folderlist; cur != NULL; cur = g_list_next(cur)) {
4721 Folder *folder = (Folder *) cur->data;
4723 if (folder->klass == &imap_class) {
4724 if (imap_is_busy(folder)) {
4733 static gboolean imap_is_busy(Folder *folder)
4735 IMAPSession *imap_session;
4736 RemoteFolder *rfolder;
4738 rfolder = (RemoteFolder *) folder;
4739 imap_session = (IMAPSession *) rfolder->session;
4740 if (imap_session == NULL)
4743 return imap_session->busy;
4746 #else /* HAVE_LIBETPAN */
4748 static FolderClass imap_class;
4750 static XMLTag *imap_item_get_xml(Folder *folder, FolderItem *item);
4751 static void imap_item_set_xml(Folder *folder, FolderItem *item, XMLTag *tag);
4753 static Folder *imap_folder_new (const gchar *name,
4756 static gboolean missing_imap_warning = TRUE;
4757 if (missing_imap_warning) {
4758 missing_imap_warning = FALSE;
4760 _("You have one or more IMAP accounts "
4761 "defined. However this version of "
4762 "Claws Mail has been built without "
4763 "IMAP support; your IMAP account(s) are "
4765 "You probably need to "
4766 "install libetpan and recompile "
4771 static gint imap_create_tree (Folder *folder)
4775 static FolderItem *imap_create_folder (Folder *folder,
4781 static gint imap_rename_folder (Folder *folder,
4788 gchar imap_get_path_separator_for_item(FolderItem *item)
4793 FolderClass *imap_get_class(void)
4795 if (imap_class.idstr == NULL) {
4796 imap_class.type = F_IMAP;
4797 imap_class.idstr = "imap";
4798 imap_class.uistr = "IMAP4";
4800 imap_class.new_folder = imap_folder_new;
4801 imap_class.create_tree = imap_create_tree;
4802 imap_class.create_folder = imap_create_folder;
4803 imap_class.rename_folder = imap_rename_folder;
4805 imap_class.set_xml = folder_set_xml;
4806 imap_class.get_xml = folder_get_xml;
4807 imap_class.item_set_xml = imap_item_set_xml;
4808 imap_class.item_get_xml = imap_item_get_xml;
4809 /* nothing implemented */
4815 void imap_disconnect_all(void)
4819 gint imap_scan_tree_real(Folder *folder, gboolean subs_only)
4824 gint imap_subscribe(Folder *folder, FolderItem *item, gchar *rpath, gboolean sub)
4829 GList * imap_scan_subtree(Folder *folder, FolderItem *item, gboolean unsubs_only, gboolean recursive)
4834 void imap_cache_msg(FolderItem *item, gint msgnum)
4838 void imap_cancel_all(void)
4842 gboolean imap_cancel_all_enabled(void)
4849 void imap_synchronise(FolderItem *item)
4851 #ifdef HAVE_LIBETPAN
4852 if (IMAP_FOLDER_ITEM(item)->last_sync == IMAP_FOLDER_ITEM(item)->last_change) {
4853 debug_print("%s already synced\n", item->path?item->path:item->name);
4856 debug_print("syncing %s\n", item->path?item->path:item->name);
4857 imap_gtk_synchronise(item);
4858 IMAP_FOLDER_ITEM(item)->last_sync = IMAP_FOLDER_ITEM(item)->last_change;
4862 static void imap_item_set_xml(Folder *folder, FolderItem *item, XMLTag *tag)
4864 #ifdef HAVE_LIBETPAN
4867 folder_item_set_xml(folder, item, tag);
4869 #ifdef HAVE_LIBETPAN
4870 for (cur = tag->attr; cur != NULL; cur = g_list_next(cur)) {
4871 XMLAttr *attr = (XMLAttr *) cur->data;
4873 if (!attr || !attr->name || !attr->value) continue;
4874 if (!strcmp(attr->name, "uidnext"))
4875 IMAP_FOLDER_ITEM(item)->uid_next = atoi(attr->value);
4876 if (!strcmp(attr->name, "last_sync"))
4877 IMAP_FOLDER_ITEM(item)->last_sync = atoi(attr->value);
4878 if (!strcmp(attr->name, "last_change"))
4879 IMAP_FOLDER_ITEM(item)->last_change = atoi(attr->value);
4881 if (IMAP_FOLDER_ITEM(item)->last_change == 0)
4882 IMAP_FOLDER_ITEM(item)->last_change = time(NULL);
4886 static XMLTag *imap_item_get_xml(Folder *folder, FolderItem *item)
4890 tag = folder_item_get_xml(folder, item);
4892 #ifdef HAVE_LIBETPAN
4893 xml_tag_add_attr(tag, xml_attr_new_int("uidnext",
4894 IMAP_FOLDER_ITEM(item)->uid_next));
4895 xml_tag_add_attr(tag, xml_attr_new_int("last_sync",
4896 IMAP_FOLDER_ITEM(item)->last_sync));
4897 xml_tag_add_attr(tag, xml_attr_new_int("last_change",
4898 IMAP_FOLDER_ITEM(item)->last_change));