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 MsgInfo *imap_envelope_from_lep(struct imap_fetch_env_info * info,
406 static void imap_lep_set_free(GSList *seq_list);
407 static struct mailimap_flag_list * imap_flag_to_lep(IMAPFlags flags);
409 typedef struct _hashtable_data {
410 IMAPSession *session;
412 IMAPFolderItem *item;
415 static FolderClass imap_class;
417 typedef struct _thread_data {
427 FolderClass *imap_get_class(void)
429 if (imap_class.idstr == NULL) {
430 imap_class.type = F_IMAP;
431 imap_class.idstr = "imap";
432 imap_class.uistr = "IMAP4";
434 /* Folder functions */
435 imap_class.new_folder = imap_folder_new;
436 imap_class.destroy_folder = imap_folder_destroy;
437 imap_class.scan_tree = imap_scan_tree;
438 imap_class.create_tree = imap_create_tree;
440 /* FolderItem functions */
441 imap_class.item_new = imap_folder_item_new;
442 imap_class.item_destroy = imap_folder_item_destroy;
443 imap_class.item_get_path = imap_item_get_path;
444 imap_class.create_folder = imap_create_folder;
445 imap_class.rename_folder = imap_rename_folder;
446 imap_class.remove_folder = imap_remove_folder;
447 imap_class.close = imap_close;
448 imap_class.get_num_list = imap_get_num_list;
449 imap_class.scan_required = imap_scan_required;
450 imap_class.set_xml = folder_set_xml;
451 imap_class.get_xml = folder_get_xml;
452 imap_class.item_set_xml = imap_item_set_xml;
453 imap_class.item_get_xml = imap_item_get_xml;
455 /* Message functions */
456 imap_class.get_msginfo = imap_get_msginfo;
457 imap_class.get_msginfos = imap_get_msginfos;
458 imap_class.fetch_msg = imap_fetch_msg;
459 imap_class.fetch_msg_full = imap_fetch_msg_full;
460 imap_class.add_msg = imap_add_msg;
461 imap_class.add_msgs = imap_add_msgs;
462 imap_class.copy_msg = imap_copy_msg;
463 imap_class.copy_msgs = imap_copy_msgs;
464 imap_class.remove_msg = imap_remove_msg;
465 imap_class.remove_msgs = imap_remove_msgs;
466 imap_class.remove_all_msg = imap_remove_all_msg;
467 imap_class.is_msg_changed = imap_is_msg_changed;
468 imap_class.change_flags = imap_change_flags;
469 imap_class.get_flags = imap_get_flags;
470 imap_class.set_batch = imap_set_batch;
471 imap_class.synchronise = imap_synchronise;
473 pthread_mutex_init(&imap_mutex, NULL);
480 static Folder *imap_folder_new(const gchar *name, const gchar *path)
484 folder = (Folder *)g_new0(IMAPFolder, 1);
485 folder->klass = &imap_class;
486 imap_folder_init(folder, name, path);
491 static void imap_folder_destroy(Folder *folder)
493 while (imap_folder_get_refcnt(folder) > 0)
494 gtk_main_iteration();
496 folder_remote_folder_destroy(REMOTE_FOLDER(folder));
500 static void imap_folder_init(Folder *folder, const gchar *name,
503 folder_remote_folder_init((Folder *)folder, name, path);
506 static FolderItem *imap_folder_item_new(Folder *folder)
508 IMAPFolderItem *item;
510 item = g_new0(IMAPFolderItem, 1);
513 item->uid_list = NULL;
515 return (FolderItem *)item;
518 static void imap_folder_item_destroy(Folder *folder, FolderItem *_item)
520 IMAPFolderItem *item = (IMAPFolderItem *)_item;
522 g_return_if_fail(item != NULL);
523 g_slist_free(item->uid_list);
528 static gboolean imap_reset_uid_lists_func(GNode *node, gpointer data)
530 IMAPFolderItem *item = (IMAPFolderItem *)node->data;
533 g_slist_free(item->uid_list);
534 item->uid_list = NULL;
539 static void imap_reset_uid_lists(Folder *folder)
541 if(folder->node == NULL)
544 /* Destroy all uid lists and rest last uid */
545 g_node_traverse(folder->node, G_IN_ORDER, G_TRAVERSE_ALL, -1, imap_reset_uid_lists_func, NULL);
548 static int imap_get_capabilities(IMAPSession *session)
550 struct mailimap_capability_data *capabilities = NULL;
554 if (session->capability != NULL)
555 return MAILIMAP_NO_ERROR;
557 capabilities = imap_threaded_capability(session->folder, &result);
559 if (result != MAILIMAP_NO_ERROR) {
560 return MAILIMAP_ERROR_CAPABILITY;
563 if (capabilities == NULL) {
564 return MAILIMAP_NO_ERROR;
567 for(cur = clist_begin(capabilities->cap_list) ; cur != NULL ;
568 cur = clist_next(cur)) {
569 struct mailimap_capability * cap =
571 if (!cap || cap->cap_data.cap_name == NULL)
573 session->capability = g_slist_append
574 (session->capability,
575 g_strdup(cap->cap_data.cap_name));
576 debug_print("got capa %s\n", cap->cap_data.cap_name);
578 mailimap_capability_data_free(capabilities);
579 return MAILIMAP_NO_ERROR;
582 static gboolean imap_has_capability(IMAPSession *session, const gchar *cap)
585 for (cur = session->capability; cur; cur = cur->next) {
586 if (!g_ascii_strcasecmp(cur->data, cap))
592 static gint imap_auth(IMAPSession *session, const gchar *user, const gchar *pass,
595 gint ok = IMAP_ERROR;
596 static time_t last_login_err = 0;
597 gchar *ext_info = "";
599 if (imap_get_capabilities(session) != MAILIMAP_NO_ERROR)
604 ok = imap_cmd_login(session, user, pass, "ANONYMOUS");
606 case IMAP_AUTH_CRAM_MD5:
607 ok = imap_cmd_login(session, user, pass, "CRAM-MD5");
609 case IMAP_AUTH_LOGIN:
610 ok = imap_cmd_login(session, user, pass, "LOGIN");
612 case IMAP_AUTH_GSSAPI:
613 ok = imap_cmd_login(session, user, pass, "GSSAPI");
616 debug_print("capabilities:\n"
621 imap_has_capability(session, "ANONYMOUS"),
622 imap_has_capability(session, "CRAM-MD5"),
623 imap_has_capability(session, "LOGIN"),
624 imap_has_capability(session, "GSSAPI"));
625 if (imap_has_capability(session, "CRAM-MD5"))
626 ok = imap_cmd_login(session, user, pass, "CRAM-MD5");
627 if (ok == IMAP_ERROR && imap_has_capability(session, "GSSAPI"))
628 ok = imap_cmd_login(session, user, pass, "GSSAPI");
629 if (ok == IMAP_ERROR) /* we always try LOGIN before giving up */
630 ok = imap_cmd_login(session, user, pass, "LOGIN");
633 if (ok == IMAP_SUCCESS)
634 session->authenticated = TRUE;
636 if (type == IMAP_AUTH_CRAM_MD5) {
637 ext_info = _("\n\nCRAM-MD5 logins only work if libetpan has been "
638 "compiled with SASL support and the "
639 "CRAM-MD5 SASL plugin is installed.");
642 if (time(NULL) - last_login_err > 10) {
643 if (!prefs_common.no_recv_err_panel) {
644 alertpanel_error(_("Connection to %s failed: "
646 SESSION(session)->server, ext_info);
648 log_error(LOG_PROTOCOL, _("Connection to %s failed: "
649 "login refused.%s\n"),
650 SESSION(session)->server, ext_info);
653 last_login_err = time(NULL);
658 static IMAPSession *imap_reconnect_if_possible(Folder *folder, IMAPSession *session)
660 RemoteFolder *rfolder = REMOTE_FOLDER(folder);
661 /* Check if this is the first try to establish a
662 connection, if yes we don't try to reconnect */
663 debug_print("reconnecting\n");
664 if (rfolder->session == NULL) {
665 log_warning(LOG_PROTOCOL, _("Connecting to %s failed"),
666 folder->account->recv_server);
667 session_destroy(SESSION(session));
670 log_warning(LOG_PROTOCOL, _("IMAP4 connection to %s has been"
671 " disconnected. Reconnecting...\n"),
672 folder->account->recv_server);
673 statusbar_print_all(_("IMAP4 connection to %s has been"
674 " disconnected. Reconnecting...\n"),
675 folder->account->recv_server);
676 SESSION(session)->state = SESSION_DISCONNECTED;
677 session_destroy(SESSION(session));
678 /* Clear folders session to make imap_session_get create
679 a new session, because of rfolder->session == NULL
680 it will not try to reconnect again and so avoid an
682 rfolder->session = NULL;
683 debug_print("getting session...\n");
684 session = imap_session_get(folder);
685 rfolder->session = SESSION(session);
691 static void lock_session(IMAPSession *session)
696 debug_print("locking session %p (%d)\n", session, session->busy);
698 debug_print(" SESSION WAS LOCKED !! \n");
699 session->busy = TRUE;
700 mainwin = mainwindow_get_mainwindow();
702 toolbar_main_set_sensitive(mainwin);
703 main_window_set_menu_sensitive(mainwin);
706 debug_print("can't lock null session\n");
710 static void unlock_session(IMAPSession *session)
715 debug_print("unlocking session %p\n", session);
716 session->busy = FALSE;
717 mainwin = mainwindow_get_mainwindow();
719 toolbar_main_set_sensitive(mainwin);
720 main_window_set_menu_sensitive(mainwin);
723 debug_print("can't unlock null session\n");
727 static IMAPSession *imap_session_get(Folder *folder)
729 RemoteFolder *rfolder = REMOTE_FOLDER(folder);
730 IMAPSession *session = NULL;
732 g_return_val_if_fail(folder != NULL, NULL);
733 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, NULL);
734 g_return_val_if_fail(folder->account != NULL, NULL);
736 if (prefs_common.work_offline &&
737 !inc_offline_should_override(FALSE,
738 _("Claws Mail needs network access in order "
739 "to access the IMAP server."))) {
743 /* Make sure we have a session */
744 if (rfolder->session != NULL) {
745 session = IMAP_SESSION(rfolder->session);
746 } else if (rfolder->connecting) {
747 debug_print("already connecting\n");
750 imap_reset_uid_lists(folder);
751 if (time(NULL) - rfolder->last_failure <= 2)
753 rfolder->connecting = TRUE;
754 session = imap_session_new(folder, folder->account);
756 if(session == NULL) {
757 rfolder->last_failure = time(NULL);
758 rfolder->connecting = FALSE;
762 /* Make sure session is authenticated */
763 if (!IMAP_SESSION(session)->authenticated)
764 imap_session_authenticate(IMAP_SESSION(session), folder->account);
766 if (!IMAP_SESSION(session)->authenticated) {
767 imap_threaded_disconnect(session->folder);
768 SESSION(session)->state = SESSION_DISCONNECTED;
769 session_destroy(SESSION(session));
770 rfolder->session = NULL;
771 rfolder->last_failure = time(NULL);
772 rfolder->connecting = FALSE;
776 lock_session(session);
778 /* I think the point of this code is to avoid sending a
779 * keepalive if we've used the session recently and therefore
780 * think it's still alive. Unfortunately, most of the code
781 * does not yet check for errors on the socket, and so if the
782 * connection drops we don't notice until the timeout expires.
783 * A better solution than sending a NOOP every time would be
784 * for every command to be prepared to retry until it is
785 * successfully sent. -- mbp */
786 if ((time(NULL) - SESSION(session)->last_access_time > SESSION_TIMEOUT_INTERVAL) || session->cancelled) {
787 /* verify that the session is still alive */
788 if (imap_cmd_noop(session) != IMAP_SUCCESS) {
789 debug_print("disconnected!\n");
790 session = imap_reconnect_if_possible(folder, session);
793 session->cancelled = FALSE;
796 rfolder->session = SESSION(session);
797 rfolder->connecting = FALSE;
799 return IMAP_SESSION(session);
802 static IMAPSession *imap_session_new(Folder * folder,
803 const PrefsAccount *account)
805 IMAPSession *session;
808 int authenticated = FALSE;
811 /* FIXME: IMAP over SSL only... */
814 port = account->set_imapport ? account->imapport
815 : account->ssl_imap == SSL_TUNNEL ? IMAPS_PORT : IMAP4_PORT;
816 ssl_type = account->ssl_imap;
818 if (account->ssl_imap != SSL_NONE) {
819 if (alertpanel_full(_("Insecure connection"),
820 _("This connection is configured to be secured "
821 "using SSL, but SSL is not available in this "
822 "build of Claws Mail. \n\n"
823 "Do you want to continue connecting to this "
824 "server? The communication would not be "
826 GTK_STOCK_CANCEL, _("Con_tinue connecting"),
827 NULL, FALSE, NULL, ALERT_WARNING,
828 G_ALERTDEFAULT) != G_ALERTALTERNATE)
831 port = account->set_imapport ? account->imapport
836 statusbar_print_all(_("Connecting to IMAP4 server: %s..."), folder->account->recv_server);
837 if (account->set_tunnelcmd) {
838 r = imap_threaded_connect_cmd(folder,
840 account->recv_server,
845 if (ssl_type == SSL_TUNNEL) {
846 r = imap_threaded_connect_ssl(folder,
847 account->recv_server,
853 r = imap_threaded_connect(folder,
854 account->recv_server,
860 if (r == MAILIMAP_NO_ERROR_AUTHENTICATED) {
861 authenticated = TRUE;
863 else if (r == MAILIMAP_NO_ERROR_NON_AUTHENTICATED) {
864 authenticated = FALSE;
867 #if (LIBETPAN_VERSION_MAJOR > 0 || LIBETPAN_VERSION_MINOR > 48)
869 if (r == MAILIMAP_ERROR_SSL)
870 log_error(LOG_PROTOCOL, _("SSL handshake failed\n"));
873 if(!prefs_common.no_recv_err_panel) {
874 alertpanel_error_log(_("Can't connect to IMAP4 server: %s:%d"),
875 account->recv_server, port);
877 log_error(LOG_PROTOCOL, _("Can't connect to IMAP4 server: %s:%d\n"),
878 account->recv_server, port);
884 session = g_new0(IMAPSession, 1);
885 session_init(SESSION(session));
886 SESSION(session)->type = SESSION_IMAP;
887 SESSION(session)->server = g_strdup(account->recv_server);
888 SESSION(session)->sock = NULL;
890 SESSION(session)->destroy = imap_session_destroy;
892 session->capability = NULL;
894 session->authenticated = authenticated;
895 session->mbox = NULL;
896 session->cmd_count = 0;
897 session->folder = folder;
898 IMAP_FOLDER(session->folder)->last_seen_separator = 0;
901 if (account->ssl_imap == SSL_STARTTLS) {
904 ok = imap_cmd_starttls(session);
905 if (ok != IMAP_SUCCESS) {
906 log_warning(LOG_PROTOCOL, _("Can't start TLS session.\n"));
907 session_destroy(SESSION(session));
911 imap_free_capabilities(session);
912 session->authenticated = FALSE;
913 session->uidplus = FALSE;
914 session->cmd_count = 1;
917 log_message(LOG_PROTOCOL, "IMAP connection is %s-authenticated\n",
918 (session->authenticated) ? "pre" : "un");
923 static void imap_session_authenticate(IMAPSession *session,
924 const PrefsAccount *account)
926 gchar *pass, *acc_pass;
927 gboolean failed = FALSE;
929 g_return_if_fail(account->userid != NULL);
930 acc_pass = account->passwd;
933 if (!pass && account->imap_auth_type != IMAP_AUTH_ANON) {
935 tmp_pass = input_dialog_query_password(account->recv_server, account->userid);
938 Xstrdup_a(pass, tmp_pass, {g_free(tmp_pass); return;});
940 } else if (account->imap_auth_type == IMAP_AUTH_ANON) {
943 statusbar_print_all(_("Connecting to IMAP4 server %s...\n"),
944 account->recv_server);
945 if (imap_auth(session, account->userid, pass, account->imap_auth_type) != IMAP_SUCCESS) {
953 if (prefs_common.no_recv_err_panel) {
954 log_error(LOG_PROTOCOL, _("Couldn't login to IMAP server %s."), account->recv_server);
955 mainwindow_show_error();
957 alertpanel_error_log(_("Couldn't login to IMAP server %s."), account->recv_server);
964 session->authenticated = TRUE;
968 static void imap_session_destroy(Session *session)
970 if (session->state != SESSION_DISCONNECTED)
971 imap_threaded_disconnect(IMAP_SESSION(session)->folder);
973 imap_free_capabilities(IMAP_SESSION(session));
974 g_free(IMAP_SESSION(session)->mbox);
975 sock_close(session->sock);
976 session->sock = NULL;
979 static gchar *imap_fetch_msg(Folder *folder, FolderItem *item, gint uid)
981 return imap_fetch_msg_full(folder, item, uid, TRUE, TRUE);
984 static guint get_file_size_with_crs(const gchar *filename)
990 if (filename == NULL)
993 fp = fopen(filename, "rb");
997 while (fgets(buf, sizeof (buf), fp) != NULL) {
999 if (!strstr(buf, "\r\n") && strstr(buf, "\n"))
1007 static gchar *imap_fetch_msg_full(Folder *folder, FolderItem *item, gint uid,
1008 gboolean headers, gboolean body)
1010 gchar *path, *filename;
1011 IMAPSession *session;
1014 g_return_val_if_fail(folder != NULL, NULL);
1015 g_return_val_if_fail(item != NULL, NULL);
1020 path = folder_item_get_path(item);
1021 if (!is_dir_exist(path))
1022 make_dir_hier(path);
1023 filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(uid), NULL);
1025 debug_print("trying to fetch cached %s\n", filename);
1026 if (is_file_exist(filename)) {
1027 /* see whether the local file represents the whole message
1028 * or not. As the IMAP server reports size with \r chars,
1029 * we have to update the local file (UNIX \n only) size */
1030 MsgInfo *cached = msgcache_get_msg(item->cache,uid);
1031 guint have_size = get_file_size_with_crs(filename);
1034 debug_print("message %d has been already %scached (%d/%d).\n", uid,
1035 have_size >= cached->size ? "fully ":"",
1036 have_size, (int)cached->size);
1038 if (cached && (cached->size <= have_size || !body)) {
1039 procmsg_msginfo_free(cached);
1040 file_strip_crs(filename);
1042 } else if (!cached && time(NULL) - get_file_mtime(filename) < 60) {
1043 debug_print("message not cached and file recent, considering file complete\n");
1044 file_strip_crs(filename);
1047 procmsg_msginfo_free(cached);
1051 debug_print("getting session...\n");
1052 session = imap_session_get(folder);
1059 debug_print("IMAP fetching messages\n");
1060 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
1061 NULL, NULL, NULL, NULL, FALSE);
1062 if (ok != IMAP_SUCCESS) {
1063 g_warning("can't select mailbox %s\n", item->path);
1065 unlock_session(session);
1069 debug_print("getting message %d...\n", uid);
1070 ok = imap_cmd_fetch(session, (guint32)uid, filename, headers, body);
1072 if (ok != IMAP_SUCCESS) {
1073 g_warning("can't fetch message %d\n", uid);
1075 unlock_session(session);
1079 unlock_session(session);
1080 file_strip_crs(filename);
1084 static gboolean imap_is_msg_fully_cached(Folder *folder, FolderItem *item, gint uid)
1086 gchar *path, *filename;
1088 MsgInfo *cached = msgcache_get_msg(item->cache,uid);
1093 path = folder_item_get_path(item);
1094 if (!is_dir_exist(path))
1097 filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(uid), NULL);
1099 if (is_file_exist(filename)) {
1100 if (cached && cached->total_size == cached->size) {
1105 size = get_file_size_with_crs(filename);
1108 if (cached && size >= cached->size) {
1109 cached->total_size = cached->size;
1110 procmsg_msginfo_free(cached);
1114 procmsg_msginfo_free(cached);
1118 void imap_cache_msg(FolderItem *item, gint msgnum)
1120 Folder *folder = NULL;
1124 folder = item->folder;
1126 if (!imap_is_msg_fully_cached(folder, item, msgnum)) {
1127 gchar *tmp = imap_fetch_msg_full(folder, item, msgnum, TRUE, TRUE);
1128 debug_print("fetched %s\n", tmp);
1133 static gint imap_add_msg(Folder *folder, FolderItem *dest,
1134 const gchar *file, MsgFlags *flags)
1138 MsgFileInfo fileinfo;
1140 g_return_val_if_fail(file != NULL, -1);
1142 fileinfo.msginfo = NULL;
1143 fileinfo.file = (gchar *)file;
1144 fileinfo.flags = flags;
1145 file_list.data = &fileinfo;
1146 file_list.next = NULL;
1148 ret = imap_add_msgs(folder, dest, &file_list, NULL);
1152 static gint imap_add_msgs(Folder *folder, FolderItem *dest, GSList *file_list,
1153 GRelation *relation)
1156 IMAPSession *session;
1157 guint32 last_uid = 0;
1159 MsgFileInfo *fileinfo;
1161 gint curnum = 0, total = 0;
1164 g_return_val_if_fail(folder != NULL, -1);
1165 g_return_val_if_fail(dest != NULL, -1);
1166 g_return_val_if_fail(file_list != NULL, -1);
1168 debug_print("getting session...\n");
1169 session = imap_session_get(folder);
1173 destdir = imap_get_real_path(session, IMAP_FOLDER(folder), dest->path);
1175 statusbar_print_all(_("Adding messages..."));
1176 total = g_slist_length(file_list);
1177 for (cur = file_list; cur != NULL; cur = cur->next) {
1178 IMAPFlags iflags = 0;
1179 guint32 new_uid = 0;
1180 gchar *real_file = NULL;
1181 fileinfo = (MsgFileInfo *)cur->data;
1183 statusbar_progress_all(curnum, total, total < 10 ? 1:10);
1186 if (fileinfo->flags) {
1187 if (MSG_IS_MARKED(*fileinfo->flags))
1188 iflags |= IMAP_FLAG_FLAGGED;
1189 if (MSG_IS_REPLIED(*fileinfo->flags))
1190 iflags |= IMAP_FLAG_ANSWERED;
1191 if (!MSG_IS_UNREAD(*fileinfo->flags))
1192 iflags |= IMAP_FLAG_SEEN;
1195 if (real_file == NULL)
1196 real_file = g_strdup(fileinfo->file);
1198 if (folder_has_parent_of_type(dest, F_QUEUE) ||
1199 folder_has_parent_of_type(dest, F_OUTBOX) ||
1200 folder_has_parent_of_type(dest, F_DRAFT) ||
1201 folder_has_parent_of_type(dest, F_TRASH))
1202 iflags |= IMAP_FLAG_SEEN;
1204 ok = imap_cmd_append(session, destdir, real_file, iflags,
1207 if (ok != IMAP_SUCCESS) {
1208 g_warning("can't append message %s\n", real_file);
1211 unlock_session(session);
1212 statusbar_progress_all(0,0,0);
1213 statusbar_pop_all();
1216 debug_print("appended new message as %d\n", new_uid);
1217 /* put the local file in the imapcache, so that we don't
1218 * have to fetch it back later. */
1220 gchar *cache_path = folder_item_get_path(dest);
1221 if (!is_dir_exist(cache_path))
1222 make_dir_hier(cache_path);
1223 if (is_dir_exist(cache_path)) {
1224 gchar *cache_file = g_strconcat(
1225 cache_path, G_DIR_SEPARATOR_S,
1226 itos(new_uid), NULL);
1227 copy_file(real_file, cache_file, TRUE);
1228 debug_print("copied to cache: %s\n", cache_file);
1235 if (relation != NULL)
1236 g_relation_insert(relation, fileinfo->msginfo != NULL ?
1237 (gpointer) fileinfo->msginfo : (gpointer) fileinfo,
1238 GINT_TO_POINTER(dest->last_num + 1));
1240 new_uid = dest->last_num+1;
1242 if (last_uid < new_uid) {
1248 statusbar_progress_all(0,0,0);
1249 statusbar_pop_all();
1251 imap_cmd_expunge(session);
1252 unlock_session(session);
1259 static gint imap_do_copy_msgs(Folder *folder, FolderItem *dest,
1260 MsgInfoList *msglist, GRelation *relation)
1264 GSList *seq_list, *cur;
1266 IMAPSession *session;
1267 gint ok = IMAP_SUCCESS;
1268 GRelation *uid_mapping;
1270 gboolean single = FALSE;
1272 g_return_val_if_fail(folder != NULL, -1);
1273 g_return_val_if_fail(dest != NULL, -1);
1274 g_return_val_if_fail(msglist != NULL, -1);
1276 debug_print("getting session...\n");
1277 session = imap_session_get(folder);
1283 msginfo = (MsgInfo *)msglist->data;
1284 if (msglist->next == NULL)
1286 src = msginfo->folder;
1288 g_warning("the src folder is identical to the dest.\n");
1289 unlock_session(session);
1293 if (src->folder != dest->folder) {
1294 GSList *infolist = NULL, *cur;
1296 for (cur = msglist; cur; cur = cur->next) {
1297 msginfo = (MsgInfo *)cur->data;
1298 MsgFileInfo *fileinfo = g_new0(MsgFileInfo, 1);
1299 fileinfo->file = procmsg_get_message_file(msginfo);
1300 fileinfo->flags = &(msginfo->flags);
1301 infolist = g_slist_prepend(infolist, fileinfo);
1303 infolist = g_slist_reverse(infolist);
1304 unlock_session(session);
1305 res = folder_item_add_msgs(dest, infolist, FALSE);
1306 for (cur = infolist; cur; cur = cur->next) {
1307 MsgFileInfo *info = (MsgFileInfo *)cur->data;
1311 g_slist_free(infolist);
1315 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
1316 NULL, NULL, NULL, NULL, FALSE);
1317 if (ok != IMAP_SUCCESS) {
1318 unlock_session(session);
1322 destdir = imap_get_real_path(session, IMAP_FOLDER(folder), dest->path);
1323 seq_list = imap_get_lep_set_from_msglist(msglist);
1324 uid_mapping = g_relation_new(2);
1325 g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
1327 statusbar_print_all(_("Copying messages..."));
1328 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
1329 struct mailimap_set * seq_set;
1330 struct mailimap_set * source = NULL;
1331 struct mailimap_set * dest = NULL;
1332 seq_set = cur->data;
1334 debug_print("Copying messages from %s to %s ...\n",
1335 src->path, destdir);
1337 ok = imap_cmd_copy(session, seq_set, destdir, uid_mapping,
1340 if (ok == IMAP_SUCCESS) {
1341 if (single && relation && source && dest) {
1342 clistiter *l = clist_begin(source->set_list);
1343 struct mailimap_set_item *i = (struct mailimap_set_item *)clist_content(l);
1344 int snum = i->set_first;
1346 l = clist_begin(dest->set_list);
1347 i = (struct mailimap_set_item *)clist_content(l);
1348 dnum = i->set_first;
1349 g_relation_insert(uid_mapping, GINT_TO_POINTER(snum),
1350 GINT_TO_POINTER(dnum));
1356 mailimap_set_free(source);
1358 mailimap_set_free(dest);
1360 if (ok != IMAP_SUCCESS) {
1361 g_relation_destroy(uid_mapping);
1362 imap_lep_set_free(seq_list);
1363 unlock_session(session);
1364 statusbar_pop_all();
1369 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
1370 MsgInfo *msginfo = (MsgInfo *)cur->data;
1373 tuples = g_relation_select(uid_mapping,
1374 GINT_TO_POINTER(msginfo->msgnum),
1376 if (tuples->len > 0) {
1377 gint num = GPOINTER_TO_INT(g_tuples_index(tuples, 0, 1));
1378 g_relation_insert(relation, msginfo,
1379 GINT_TO_POINTER(num));
1382 debug_print("copied new message as %d\n", num);
1383 /* put the local file in the imapcache, so that we don't
1384 * have to fetch it back later. */
1386 gchar *cache_path = folder_item_get_path(msginfo->folder);
1387 gchar *real_file = g_strconcat(
1388 cache_path, G_DIR_SEPARATOR_S,
1389 itos(msginfo->msgnum), NULL);
1390 gchar *cache_file = NULL;
1392 cache_path = folder_item_get_path(dest);
1393 cache_file = g_strconcat(
1394 cache_path, G_DIR_SEPARATOR_S,
1396 if (!is_dir_exist(cache_path))
1397 make_dir_hier(cache_path);
1398 if (is_file_exist(real_file) && is_dir_exist(cache_path)) {
1399 copy_file(real_file, cache_file, TRUE);
1400 debug_print("copied to cache: %s\n", cache_file);
1407 g_relation_insert(relation, msginfo,
1408 GINT_TO_POINTER(0));
1409 g_tuples_destroy(tuples);
1411 statusbar_pop_all();
1413 g_relation_destroy(uid_mapping);
1414 imap_lep_set_free(seq_list);
1418 IMAP_FOLDER_ITEM(dest)->lastuid = 0;
1419 IMAP_FOLDER_ITEM(dest)->uid_next = 0;
1420 g_slist_free(IMAP_FOLDER_ITEM(dest)->uid_list);
1421 IMAP_FOLDER_ITEM(dest)->uid_list = NULL;
1423 unlock_session(session);
1424 if (ok == IMAP_SUCCESS)
1430 static gint imap_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1434 g_return_val_if_fail(msginfo != NULL, -1);
1436 msglist.data = msginfo;
1437 msglist.next = NULL;
1439 return imap_copy_msgs(folder, dest, &msglist, NULL);
1442 static gint imap_copy_msgs(Folder *folder, FolderItem *dest,
1443 MsgInfoList *msglist, GRelation *relation)
1448 g_return_val_if_fail(folder != NULL, -1);
1449 g_return_val_if_fail(dest != NULL, -1);
1450 g_return_val_if_fail(msglist != NULL, -1);
1452 msginfo = (MsgInfo *)msglist->data;
1453 g_return_val_if_fail(msginfo->folder != NULL, -1);
1455 ret = imap_do_copy_msgs(folder, dest, msglist, relation);
1460 static gint imap_do_remove_msgs(Folder *folder, FolderItem *dest,
1461 MsgInfoList *msglist, GRelation *relation)
1463 gchar *destdir, *dir;
1464 GSList *numlist = NULL, *cur;
1466 IMAPSession *session;
1467 gint ok = IMAP_SUCCESS;
1468 GRelation *uid_mapping;
1470 g_return_val_if_fail(folder != NULL, -1);
1471 g_return_val_if_fail(dest != NULL, -1);
1472 g_return_val_if_fail(msglist != NULL, -1);
1474 debug_print("getting session...\n");
1475 session = imap_session_get(folder);
1480 msginfo = (MsgInfo *)msglist->data;
1482 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
1483 NULL, NULL, NULL, NULL, FALSE);
1484 if (ok != IMAP_SUCCESS) {
1485 unlock_session(session);
1489 destdir = imap_get_real_path(session, IMAP_FOLDER(folder), dest->path);
1490 for (cur = msglist; cur; cur = cur->next) {
1491 msginfo = (MsgInfo *)cur->data;
1492 if (!MSG_IS_DELETED(msginfo->flags))
1493 numlist = g_slist_prepend(numlist, GINT_TO_POINTER(msginfo->msgnum));
1495 numlist = g_slist_reverse(numlist);
1497 uid_mapping = g_relation_new(2);
1498 g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
1500 ok = imap_set_message_flags
1501 (session, numlist, IMAP_FLAG_DELETED, TRUE);
1502 if (ok != IMAP_SUCCESS) {
1503 log_warning(LOG_PROTOCOL, _("can't set deleted flags\n"));
1504 unlock_session(session);
1507 ok = imap_cmd_expunge(session);
1508 if (ok != IMAP_SUCCESS) {
1509 log_warning(LOG_PROTOCOL, _("can't expunge\n"));
1510 unlock_session(session);
1514 dir = folder_item_get_path(msginfo->folder);
1515 if (is_dir_exist(dir)) {
1516 for (cur = msglist; cur; cur = cur->next) {
1517 msginfo = (MsgInfo *)cur->data;
1518 remove_numbered_files(dir, msginfo->msgnum, msginfo->msgnum);
1523 g_relation_destroy(uid_mapping);
1524 g_slist_free(numlist);
1527 unlock_session(session);
1528 if (ok == IMAP_SUCCESS)
1534 static gint imap_remove_msgs(Folder *folder, FolderItem *dest,
1535 MsgInfoList *msglist, GRelation *relation)
1539 g_return_val_if_fail(folder != NULL, -1);
1540 g_return_val_if_fail(dest != NULL, -1);
1541 if (msglist == NULL)
1544 msginfo = (MsgInfo *)msglist->data;
1545 g_return_val_if_fail(msginfo->folder != NULL, -1);
1547 return imap_do_remove_msgs(folder, dest, msglist, relation);
1550 static gint imap_remove_all_msg(Folder *folder, FolderItem *item)
1552 GSList *list = folder_item_get_msg_list(item);
1553 gint res = imap_remove_msgs(folder, item, list, NULL);
1554 procmsg_msg_list_free(list);
1558 static gboolean imap_is_msg_changed(Folder *folder, FolderItem *item,
1561 /* TODO: properly implement this method */
1565 static gint imap_close(Folder *folder, FolderItem *item)
1570 gint imap_scan_tree_real(Folder *folder, gboolean subs_only)
1572 FolderItem *item = NULL;
1573 IMAPSession *session;
1574 gchar *root_folder = NULL;
1576 g_return_val_if_fail(folder != NULL, -1);
1577 g_return_val_if_fail(folder->account != NULL, -1);
1579 debug_print("getting session...\n");
1580 session = imap_session_get(folder);
1582 if (!folder->node) {
1583 folder_tree_destroy(folder);
1584 item = folder_item_new(folder, folder->name, NULL);
1585 item->folder = folder;
1586 folder->node = item->node = g_node_new(item);
1591 if (folder->account->imap_dir && *folder->account->imap_dir) {
1596 Xstrdup_a(root_folder, folder->account->imap_dir, {unlock_session(session);return -1;});
1597 extract_quote(root_folder, '"');
1598 subst_char(root_folder,
1599 imap_get_path_separator(session, IMAP_FOLDER(folder),
1602 strtailchomp(root_folder, '/');
1603 real_path = imap_get_real_path
1604 (session, IMAP_FOLDER(folder), root_folder);
1605 debug_print("IMAP root directory: %s\n", real_path);
1607 /* check if root directory exist */
1609 r = imap_threaded_list(session->folder, "", real_path,
1611 if ((r != MAILIMAP_NO_ERROR) || (clist_count(lep_list) == 0)) {
1612 if (!folder->node) {
1613 item = folder_item_new(folder, folder->name, NULL);
1614 item->folder = folder;
1615 folder->node = item->node = g_node_new(item);
1617 unlock_session(session);
1620 mailimap_list_result_free(lep_list);
1626 item = FOLDER_ITEM(folder->node->data);
1628 if (item && !item->path && root_folder) {
1629 item->path = g_strdup(root_folder);
1632 if (!item || ((item->path || root_folder) &&
1633 strcmp2(item->path, root_folder) != 0)) {
1634 folder_tree_destroy(folder);
1635 item = folder_item_new(folder, folder->name, root_folder);
1636 item->folder = folder;
1637 folder->node = item->node = g_node_new(item);
1640 imap_scan_tree_recursive(session, FOLDER_ITEM(folder->node->data), subs_only);
1641 imap_create_missing_folders(folder);
1642 unlock_session(session);
1647 static gint imap_scan_tree(Folder *folder)
1649 gboolean subs_only = FALSE;
1650 if (folder->account) {
1651 debug_print(" scanning only subs %d\n", folder->account->imap_subsonly);
1652 subs_only = folder->account->imap_subsonly;
1654 return imap_scan_tree_real(folder, subs_only);
1657 static gint imap_scan_tree_recursive(IMAPSession *session, FolderItem *item, gboolean subs_only)
1660 IMAPFolder *imapfolder;
1661 FolderItem *new_item;
1662 GSList *item_list, *cur;
1665 gchar *wildcard_path;
1671 g_return_val_if_fail(item != NULL, -1);
1672 g_return_val_if_fail(item->folder != NULL, -1);
1673 g_return_val_if_fail(item->no_sub == FALSE, -1);
1675 folder = item->folder;
1676 imapfolder = IMAP_FOLDER(folder);
1678 separator = imap_get_path_separator(session, imapfolder, item->path);
1680 if (folder->ui_func)
1681 folder->ui_func(folder, item, folder->ui_func_data);
1684 wildcard[0] = separator;
1687 real_path = imap_get_real_path(session, imapfolder, item->path);
1691 real_path = g_strdup("");
1694 Xstrcat_a(wildcard_path, real_path, wildcard,
1695 {g_free(real_path); return IMAP_ERROR;});
1699 r = imap_threaded_lsub(folder, "", wildcard_path, &lep_list);
1701 r = imap_threaded_list(folder, "", wildcard_path, &lep_list);
1703 if (r != MAILIMAP_NO_ERROR) {
1707 item_list = imap_list_from_lep(imapfolder,
1708 lep_list, real_path, FALSE);
1709 mailimap_list_result_free(lep_list);
1714 node = item->node->children;
1715 while (node != NULL) {
1716 FolderItem *old_item = FOLDER_ITEM(node->data);
1717 GNode *next = node->next;
1720 for (cur = item_list; cur != NULL; cur = cur->next) {
1721 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1722 if (!strcmp2(old_item->path, cur_item->path)) {
1723 new_item = cur_item;
1728 if (old_item && old_item->path && !strcmp(old_item->path, "INBOX")) {
1729 debug_print("not removing INBOX\n");
1731 debug_print("folder '%s' not found. removing...\n",
1733 folder_item_remove(old_item);
1736 old_item->no_sub = new_item->no_sub;
1737 old_item->no_select = new_item->no_select;
1738 if (old_item->no_sub == TRUE && node->children) {
1739 debug_print("folder '%s' doesn't have "
1740 "subfolders. removing...\n",
1742 folder_item_remove_children(old_item);
1749 for (cur = item_list; cur != NULL; cur = cur->next) {
1750 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1753 for (node = item->node->children; node != NULL;
1754 node = node->next) {
1755 if (!strcmp2(FOLDER_ITEM(node->data)->path,
1757 new_item = FOLDER_ITEM(node->data);
1758 folder_item_destroy(cur_item);
1764 new_item = cur_item;
1765 debug_print("new folder '%s' found.\n", new_item->path);
1766 folder_item_append(item, new_item);
1769 if (!strcmp(new_item->path, "INBOX")) {
1770 new_item->stype = F_INBOX;
1771 folder->inbox = new_item;
1772 } else if (!folder_item_parent(item) || item->stype == F_INBOX) {
1775 base = g_path_get_basename(new_item->path);
1777 if (!folder->outbox && !g_ascii_strcasecmp(base, "Sent")) {
1778 new_item->stype = F_OUTBOX;
1779 folder->outbox = new_item;
1780 } else if (!folder->draft && !g_ascii_strcasecmp(base, "Drafts")) {
1781 new_item->stype = F_DRAFT;
1782 folder->draft = new_item;
1783 } else if (!folder->queue && !g_ascii_strcasecmp(base, "Queue")) {
1784 new_item->stype = F_QUEUE;
1785 folder->queue = new_item;
1786 } else if (!folder->trash && !g_ascii_strcasecmp(base, "Trash")) {
1787 new_item->stype = F_TRASH;
1788 folder->trash = new_item;
1793 if (new_item->no_sub == FALSE)
1794 imap_scan_tree_recursive(session, new_item, subs_only);
1797 g_slist_free(item_list);
1799 return IMAP_SUCCESS;
1802 GList *imap_scan_subtree(Folder *folder, FolderItem *item, gboolean unsubs_only, gboolean recursive)
1804 IMAPSession *session = imap_session_get(folder);
1806 gchar *wildcard_path;
1810 GSList *item_list = NULL, *cur;
1811 GList *child_list = NULL, *tmplist = NULL;
1812 GSList *sub_list = NULL;
1818 separator = imap_get_path_separator(session, IMAP_FOLDER(folder), item->path);
1821 wildcard[0] = separator;
1824 real_path = imap_get_real_path(session, IMAP_FOLDER(folder), item->path);
1828 real_path = g_strdup("");
1831 Xstrcat_a(wildcard_path, real_path, wildcard,
1832 {g_free(real_path); return NULL;});
1836 statusbar_print_all(_("Looking for unsubscribed folders in %s..."),
1837 item->path?item->path:item->name);
1839 statusbar_print_all(_("Looking for subfolders of %s..."),
1840 item->path?item->path:item->name);
1842 r = imap_threaded_list(folder, "", wildcard_path, &lep_list);
1844 statusbar_pop_all();
1847 item_list = imap_list_from_lep(IMAP_FOLDER(folder),
1848 lep_list, real_path, FALSE);
1849 mailimap_list_result_free(lep_list);
1851 for (cur = item_list; cur != NULL; cur = cur->next) {
1852 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1854 tmplist = imap_scan_subtree(folder, cur_item,
1855 unsubs_only, recursive);
1857 child_list = g_list_concat(child_list, tmplist);
1859 child_list = g_list_prepend(child_list,
1860 imap_get_real_path(session,
1861 IMAP_FOLDER(folder), cur_item->path));
1863 folder_item_destroy(cur_item);
1865 child_list = g_list_reverse(child_list);
1866 g_slist_free(item_list);
1869 r = imap_threaded_lsub(folder, "", wildcard_path, &lep_list);
1871 statusbar_pop_all();
1874 sub_list = imap_list_from_lep(IMAP_FOLDER(folder),
1875 lep_list, real_path, FALSE);
1876 mailimap_list_result_free(lep_list);
1878 for (cur = sub_list; cur != NULL; cur = cur->next) {
1879 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1880 GList *oldlitem = NULL;
1881 gchar *tmp = imap_get_real_path(session,
1882 IMAP_FOLDER(folder), cur_item->path);
1883 folder_item_destroy(cur_item);
1884 oldlitem = g_list_find_custom(
1885 child_list, tmp, (GCompareFunc)strcmp2);
1887 child_list = g_list_remove_link(child_list, oldlitem);
1888 g_free(oldlitem->data);
1889 g_list_free(oldlitem);
1895 statusbar_pop_all();
1900 static gint imap_create_tree(Folder *folder)
1902 g_return_val_if_fail(folder != NULL, -1);
1903 g_return_val_if_fail(folder->node != NULL, -1);
1904 g_return_val_if_fail(folder->node->data != NULL, -1);
1905 g_return_val_if_fail(folder->account != NULL, -1);
1907 imap_scan_tree(folder);
1908 imap_create_missing_folders(folder);
1913 static void imap_create_missing_folders(Folder *folder)
1915 g_return_if_fail(folder != NULL);
1918 folder->inbox = imap_create_special_folder
1919 (folder, F_INBOX, "INBOX");
1921 folder->trash = imap_create_special_folder
1922 (folder, F_TRASH, "Trash");
1924 folder->queue = imap_create_special_folder
1925 (folder, F_QUEUE, "Queue");
1926 if (!folder->outbox)
1927 folder->outbox = imap_create_special_folder
1928 (folder, F_OUTBOX, "Sent");
1930 folder->draft = imap_create_special_folder
1931 (folder, F_DRAFT, "Drafts");
1934 static FolderItem *imap_create_special_folder(Folder *folder,
1935 SpecialFolderItemType stype,
1939 FolderItem *new_item;
1941 g_return_val_if_fail(folder != NULL, NULL);
1942 g_return_val_if_fail(folder->node != NULL, NULL);
1943 g_return_val_if_fail(folder->node->data != NULL, NULL);
1944 g_return_val_if_fail(folder->account != NULL, NULL);
1945 g_return_val_if_fail(name != NULL, NULL);
1947 item = FOLDER_ITEM(folder->node->data);
1948 new_item = imap_create_folder(folder, item, name);
1951 g_warning("Can't create '%s'\n", name);
1952 if (!folder->inbox) return NULL;
1954 new_item = imap_create_folder(folder, folder->inbox, name);
1956 g_warning("Can't create '%s' under INBOX\n", name);
1958 new_item->stype = stype;
1960 new_item->stype = stype;
1965 static gchar *imap_folder_get_path(Folder *folder)
1969 g_return_val_if_fail(folder != NULL, NULL);
1970 g_return_val_if_fail(folder->account != NULL, NULL);
1972 folder_path = g_strconcat(get_imap_cache_dir(),
1974 folder->account->recv_server,
1976 folder->account->userid,
1982 static gchar *imap_item_get_path(Folder *folder, FolderItem *item)
1984 gchar *folder_path, *path;
1986 g_return_val_if_fail(folder != NULL, NULL);
1987 g_return_val_if_fail(item != NULL, NULL);
1988 folder_path = imap_folder_get_path(folder);
1990 g_return_val_if_fail(folder_path != NULL, NULL);
1991 if (folder_path[0] == G_DIR_SEPARATOR) {
1993 path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
1996 path = g_strdup(folder_path);
1999 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
2000 folder_path, G_DIR_SEPARATOR_S,
2003 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
2006 g_free(folder_path);
2011 static FolderItem *imap_create_folder(Folder *folder, FolderItem *parent,
2014 gchar *dirpath, *imap_path;
2015 IMAPSession *session;
2016 FolderItem *new_item;
2021 gboolean no_select = FALSE, no_sub = FALSE;
2022 gboolean exist = FALSE;
2024 g_return_val_if_fail(folder != NULL, NULL);
2025 g_return_val_if_fail(folder->account != NULL, NULL);
2026 g_return_val_if_fail(parent != NULL, NULL);
2027 g_return_val_if_fail(name != NULL, NULL);
2029 debug_print("getting session...\n");
2030 session = imap_session_get(folder);
2035 if (!folder_item_parent(parent) && strcmp(name, "INBOX") == 0) {
2036 dirpath = g_strdup(name);
2037 }else if (parent->path)
2038 dirpath = g_strconcat(parent->path, "/", name, NULL);
2039 else if ((p = strchr(name, '/')) != NULL && *(p + 1) != '\0')
2040 dirpath = g_strdup(name);
2041 else if (folder->account->imap_dir && *folder->account->imap_dir) {
2044 Xstrdup_a(imap_dir, folder->account->imap_dir, {unlock_session(session);return NULL;});
2045 strtailchomp(imap_dir, '/');
2046 dirpath = g_strconcat(imap_dir, "/", name, NULL);
2048 dirpath = g_strdup(name);
2052 /* keep trailing directory separator to create a folder that contains
2054 imap_path = imap_utf8_to_modified_utf7(dirpath);
2056 strtailchomp(dirpath, '/');
2057 Xstrdup_a(new_name, name, {
2059 unlock_session(session);
2062 separator = imap_get_path_separator(session, IMAP_FOLDER(folder), imap_path);
2063 imap_path_separator_subst(imap_path, separator);
2064 /* remove trailing / for display */
2065 strtailchomp(new_name, '/');
2067 if (strcmp(dirpath, "INBOX") != 0) {
2072 argbuf = g_ptr_array_new();
2073 r = imap_threaded_list(folder, "", imap_path, &lep_list);
2074 if (r != MAILIMAP_NO_ERROR) {
2075 log_warning(LOG_PROTOCOL, _("can't create mailbox: LIST failed\n"));
2078 ptr_array_free_strings(argbuf);
2079 g_ptr_array_free(argbuf, TRUE);
2080 unlock_session(session);
2084 if (clist_count(lep_list) > 0)
2086 mailimap_list_result_free(lep_list);
2089 ok = imap_cmd_create(session, imap_path);
2090 if (ok != IMAP_SUCCESS) {
2091 log_warning(LOG_PROTOCOL, _("can't create mailbox\n"));
2094 unlock_session(session);
2097 r = imap_threaded_list(folder, "", imap_path, &lep_list);
2098 if (r == MAILIMAP_NO_ERROR) {
2099 GSList *item_list = imap_list_from_lep(IMAP_FOLDER(folder),
2100 lep_list, dirpath, TRUE);
2102 FolderItem *cur_item = FOLDER_ITEM(item_list->data);
2103 no_select = cur_item->no_select;
2104 no_sub = cur_item->no_sub;
2105 g_slist_free(item_list);
2107 mailimap_list_result_free(lep_list);
2110 imap_threaded_subscribe(folder, imap_path, TRUE);
2114 /* just get flags */
2115 r = imap_threaded_list(folder, "", "INBOX", &lep_list);
2116 if (r == MAILIMAP_NO_ERROR) {
2117 GSList *item_list = imap_list_from_lep(IMAP_FOLDER(folder),
2118 lep_list, dirpath, TRUE);
2120 FolderItem *cur_item = FOLDER_ITEM(item_list->data);
2121 no_select = cur_item->no_select;
2122 no_sub = cur_item->no_sub;
2123 g_slist_free(item_list);
2125 mailimap_list_result_free(lep_list);
2129 new_item = folder_item_new(folder, new_name, dirpath);
2130 new_item->no_select = no_select;
2131 new_item->no_sub = no_sub;
2132 folder_item_append(parent, new_item);
2136 dirpath = folder_item_get_path(new_item);
2137 if (!is_dir_exist(dirpath))
2138 make_dir_hier(dirpath);
2140 unlock_session(session);
2143 /* folder existed, scan it */
2144 folder_item_scan_full(new_item, FALSE);
2150 static gint imap_rename_folder(Folder *folder, FolderItem *item,
2155 gchar *real_oldpath;
2156 gchar *real_newpath;
2158 gchar *old_cache_dir;
2159 gchar *new_cache_dir;
2160 IMAPSession *session;
2163 gint exists, recent, unseen;
2164 guint32 uid_validity;
2166 g_return_val_if_fail(folder != NULL, -1);
2167 g_return_val_if_fail(item != NULL, -1);
2168 g_return_val_if_fail(item->path != NULL, -1);
2169 g_return_val_if_fail(name != NULL, -1);
2171 debug_print("getting session...\n");
2172 session = imap_session_get(folder);
2177 if (strchr(name, imap_get_path_separator(session, IMAP_FOLDER(folder), item->path)) != NULL) {
2178 g_warning(_("New folder name must not contain the namespace "
2180 unlock_session(session);
2184 real_oldpath = imap_get_real_path(session, IMAP_FOLDER(folder), item->path);
2186 g_free(session->mbox);
2187 session->mbox = NULL;
2188 ok = imap_cmd_examine(session, "INBOX",
2189 &exists, &recent, &unseen, &uid_validity, FALSE);
2190 if (ok != IMAP_SUCCESS) {
2191 g_free(real_oldpath);
2192 unlock_session(session);
2196 separator = imap_get_path_separator(session, IMAP_FOLDER(folder), item->path);
2197 if (strchr(item->path, G_DIR_SEPARATOR)) {
2198 dirpath = g_path_get_dirname(item->path);
2199 newpath = g_strconcat(dirpath, G_DIR_SEPARATOR_S, name, NULL);
2202 newpath = g_strdup(name);
2204 real_newpath = imap_utf8_to_modified_utf7(newpath);
2205 imap_path_separator_subst(real_newpath, separator);
2207 ok = imap_cmd_rename(session, real_oldpath, real_newpath);
2208 if (ok != IMAP_SUCCESS) {
2209 log_warning(LOG_PROTOCOL, _("can't rename mailbox: %s to %s\n"),
2210 real_oldpath, real_newpath);
2211 g_free(real_oldpath);
2213 g_free(real_newpath);
2214 unlock_session(session);
2218 item->name = g_strdup(name);
2220 old_cache_dir = folder_item_get_path(item);
2222 paths[0] = g_strdup(item->path);
2224 g_node_traverse(item->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
2225 imap_rename_folder_func, paths);
2227 if (is_dir_exist(old_cache_dir)) {
2228 new_cache_dir = folder_item_get_path(item);
2229 if (rename(old_cache_dir, new_cache_dir) < 0) {
2230 FILE_OP_ERROR(old_cache_dir, "rename");
2232 g_free(new_cache_dir);
2235 g_free(old_cache_dir);
2238 g_free(real_oldpath);
2239 g_free(real_newpath);
2240 unlock_session(session);
2244 gint imap_subscribe(Folder *folder, FolderItem *item, gchar *rpath, gboolean sub)
2248 IMAPSession *session;
2249 debug_print("getting session...\n");
2251 session = imap_session_get(folder);
2255 if (item && item->path) {
2256 path = imap_get_real_path(session, IMAP_FOLDER(folder), item->path);
2259 if (!strcmp(path, "INBOX") && sub == FALSE)
2261 debug_print("%ssubscribing %s\n", sub?"":"un", path);
2262 r = imap_threaded_subscribe(folder, path, sub);
2265 r = imap_threaded_subscribe(folder, rpath, sub);
2271 static gint imap_remove_folder_real(Folder *folder, FolderItem *item)
2274 IMAPSession *session;
2278 g_return_val_if_fail(folder != NULL, -1);
2279 g_return_val_if_fail(item != NULL, -1);
2280 g_return_val_if_fail(item->path != NULL, -1);
2282 debug_print("getting session...\n");
2283 session = imap_session_get(folder);
2287 path = imap_get_real_path(session, IMAP_FOLDER(folder), item->path);
2289 imap_threaded_subscribe(folder, path, FALSE);
2290 ok = imap_cmd_delete(session, path);
2291 if (ok != IMAP_SUCCESS) {
2292 gchar *tmp = g_strdup_printf("%s%c", path,
2293 imap_get_path_separator(session, IMAP_FOLDER(folder), path));
2296 ok = imap_cmd_delete(session, path);
2299 if (ok != IMAP_SUCCESS) {
2300 log_warning(LOG_PROTOCOL, _("can't delete mailbox\n"));
2302 unlock_session(session);
2307 cache_dir = folder_item_get_path(item);
2308 if (is_dir_exist(cache_dir) && remove_dir_recursive(cache_dir) < 0)
2309 g_warning("can't remove directory '%s'\n", cache_dir);
2311 folder_item_remove(item);
2312 unlock_session(session);
2316 static gint imap_remove_folder(Folder *folder, FolderItem *item)
2320 g_return_val_if_fail(item != NULL, -1);
2321 g_return_val_if_fail(item->folder != NULL, -1);
2322 g_return_val_if_fail(item->node != NULL, -1);
2324 node = item->node->children;
2325 while (node != NULL) {
2327 if (imap_remove_folder(folder, FOLDER_ITEM(node->data)) < 0)
2331 debug_print("IMAP removing %s\n", item->path);
2333 if (imap_remove_all_msg(folder, item) < 0)
2335 return imap_remove_folder_real(folder, item);
2338 typedef struct _uncached_data {
2339 IMAPSession *session;
2341 MsgNumberList *numlist;
2347 static void *imap_get_uncached_messages_thread(void *data)
2349 uncached_data *stuff = (uncached_data *)data;
2350 IMAPSession *session = stuff->session;
2351 FolderItem *item = stuff->item;
2352 MsgNumberList *numlist = stuff->numlist;
2354 GSList *newlist = NULL;
2355 GSList *llast = NULL;
2356 GSList *seq_list, *cur;
2358 debug_print("uncached_messages\n");
2360 if (session == NULL || item == NULL || item->folder == NULL
2361 || FOLDER_CLASS(item->folder) != &imap_class) {
2366 seq_list = imap_get_lep_set_from_numlist(numlist);
2367 debug_print("get msgs info\n");
2368 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
2369 struct mailimap_set * imapset;
2375 if (session->cancelled)
2378 imapset = cur->data;
2380 r = imap_threaded_fetch_env(session->folder,
2381 imapset, &env_list);
2382 if (r != MAILIMAP_NO_ERROR)
2385 session_set_access_time(SESSION(session));
2388 for(i = 0 ; i < carray_count(env_list) ; i ++) {
2389 struct imap_fetch_env_info * info;
2392 info = carray_get(env_list, i);
2393 msginfo = imap_envelope_from_lep(info, item);
2394 if (msginfo == NULL)
2396 msginfo->folder = item;
2398 llast = newlist = g_slist_append(newlist, msginfo);
2400 llast = g_slist_append(llast, msginfo);
2401 llast = llast->next;
2406 imap_fetch_env_free(env_list);
2409 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
2410 struct mailimap_set * imapset;
2412 imapset = cur->data;
2413 mailimap_set_free(imapset);
2416 session_set_access_time(SESSION(session));
2421 #define MAX_MSG_NUM 50
2423 static GSList *imap_get_uncached_messages(IMAPSession *session,
2425 MsgNumberList *numlist)
2427 GSList *result = NULL;
2429 uncached_data *data = g_new0(uncached_data, 1);
2434 data->total = g_slist_length(numlist);
2435 debug_print("messages list : %i\n", data->total);
2437 while (cur != NULL) {
2438 GSList * partial_result;
2446 while (count < MAX_MSG_NUM) {
2451 if (newlist == NULL)
2452 llast = newlist = g_slist_append(newlist, p);
2454 llast = g_slist_append(llast, p);
2455 llast = llast->next;
2465 data->session = session;
2467 data->numlist = newlist;
2470 if (prefs_common.work_offline &&
2471 !inc_offline_should_override(FALSE,
2472 _("Claws Mail needs network access in order "
2473 "to access the IMAP server."))) {
2479 (GSList *)imap_get_uncached_messages_thread(data);
2481 statusbar_progress_all(data->cur,data->total, 1);
2483 g_slist_free(newlist);
2485 result = g_slist_concat(result, partial_result);
2489 statusbar_progress_all(0,0,0);
2490 statusbar_pop_all();
2495 static void imap_delete_all_cached_messages(FolderItem *item)
2499 g_return_if_fail(item != NULL);
2500 g_return_if_fail(item->folder != NULL);
2501 g_return_if_fail(FOLDER_CLASS(item->folder) == &imap_class);
2503 debug_print("Deleting all cached messages...\n");
2505 dir = folder_item_get_path(item);
2506 if (is_dir_exist(dir))
2507 remove_all_numbered_files(dir);
2510 debug_print("done.\n");
2513 gchar imap_get_path_separator_for_item(FolderItem *item)
2515 Folder *folder = NULL;
2516 IMAPFolder *imap_folder = NULL;
2517 IMAPSession *session = NULL;
2522 folder = item->folder;
2527 imap_folder = IMAP_FOLDER(folder);
2532 debug_print("getting session...");
2533 session = imap_session_get(FOLDER(folder));
2534 result = imap_get_path_separator(session, imap_folder, item->path);
2535 unlock_session(session);
2539 static gchar imap_refresh_path_separator(IMAPSession *session, IMAPFolder *folder, const gchar *subfolder)
2543 gchar separator = '\0';
2545 g_return_val_if_fail(session != NULL, '/');
2546 r = imap_threaded_list((Folder *)folder, "", subfolder, &lep_list);
2548 if (r != MAILIMAP_NO_ERROR) {
2549 log_warning(LOG_PROTOCOL, _("LIST failed\n"));
2553 if (clist_count(lep_list) > 0) {
2554 clistiter * iter = clist_begin(lep_list);
2555 struct mailimap_mailbox_list * mb;
2556 mb = clist_content(iter);
2558 separator = mb->mb_delimiter;
2559 debug_print("got separator: %c\n", folder->last_seen_separator);
2561 mailimap_list_result_free(lep_list);
2565 static gchar imap_get_path_separator(IMAPSession *session, IMAPFolder *folder, const gchar *path)
2567 gchar separator = '/';
2569 if (folder->last_seen_separator == 0) {
2570 folder->last_seen_separator = imap_refresh_path_separator(session, folder, "");
2573 if (folder->last_seen_separator == 0) {
2574 folder->last_seen_separator = imap_refresh_path_separator(session, folder, "INBOX");
2577 if (folder->last_seen_separator != 0) {
2578 debug_print("using separator: %c\n", folder->last_seen_separator);
2579 return folder->last_seen_separator;
2585 static gchar *imap_get_real_path(IMAPSession *session, IMAPFolder *folder, const gchar *path)
2590 g_return_val_if_fail(folder != NULL, NULL);
2591 g_return_val_if_fail(path != NULL, NULL);
2593 real_path = imap_utf8_to_modified_utf7(path);
2594 separator = imap_get_path_separator(session, folder, path);
2595 imap_path_separator_subst(real_path, separator);
2600 static gint imap_set_message_flags(IMAPSession *session,
2601 MsgNumberList *numlist,
2609 seq_list = imap_get_lep_set_from_numlist(numlist);
2611 for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) {
2612 struct mailimap_set * imapset;
2614 imapset = cur->data;
2616 ok = imap_cmd_store(session, imapset,
2620 imap_lep_set_free(seq_list);
2622 return IMAP_SUCCESS;
2625 typedef struct _select_data {
2626 IMAPSession *session;
2631 guint32 *uid_validity;
2635 static gint imap_select(IMAPSession *session, IMAPFolder *folder,
2637 gint *exists, gint *recent, gint *unseen,
2638 guint32 *uid_validity, gboolean block)
2642 gint exists_, recent_, unseen_;
2643 guint32 uid_validity_;
2645 if (!exists && !recent && !unseen && !uid_validity) {
2646 if (session->mbox && strcmp(session->mbox, path) == 0)
2647 return IMAP_SUCCESS;
2656 uid_validity = &uid_validity_;
2658 g_free(session->mbox);
2659 session->mbox = NULL;
2661 real_path = imap_get_real_path(session, folder, path);
2663 ok = imap_cmd_select(session, real_path,
2664 exists, recent, unseen, uid_validity, block);
2665 if (ok != IMAP_SUCCESS)
2666 log_warning(LOG_PROTOCOL, _("can't select folder: %s\n"), real_path);
2668 session->mbox = g_strdup(path);
2669 session->folder_content_changed = FALSE;
2676 static gint imap_status(IMAPSession *session, IMAPFolder *folder,
2677 const gchar *path, IMAPFolderItem *item,
2679 guint32 *uid_next, guint32 *uid_validity,
2680 gint *unseen, gboolean block)
2684 struct mailimap_mailbox_data_status * data_status;
2689 real_path = imap_get_real_path(session, folder, path);
2707 r = imap_threaded_status(FOLDER(folder), real_path,
2708 &data_status, mask);
2711 if (r != MAILIMAP_NO_ERROR) {
2712 debug_print("status err %d\n", r);
2716 if (data_status->st_info_list == NULL) {
2717 mailimap_mailbox_data_status_free(data_status);
2718 debug_print("status->st_info_list == NULL\n");
2723 for(iter = clist_begin(data_status->st_info_list) ; iter != NULL ;
2724 iter = clist_next(iter)) {
2725 struct mailimap_status_info * info;
2727 info = clist_content(iter);
2728 switch (info->st_att) {
2729 case MAILIMAP_STATUS_ATT_MESSAGES:
2731 * messages = info->st_value;
2732 got_values |= 1 << 0;
2736 case MAILIMAP_STATUS_ATT_UIDNEXT:
2738 * uid_next = info->st_value;
2739 got_values |= 1 << 2;
2743 case MAILIMAP_STATUS_ATT_UIDVALIDITY:
2745 * uid_validity = info->st_value;
2746 got_values |= 1 << 3;
2750 case MAILIMAP_STATUS_ATT_UNSEEN:
2752 * unseen = info->st_value;
2753 got_values |= 1 << 4;
2758 mailimap_mailbox_data_status_free(data_status);
2760 if (got_values != mask) {
2761 g_warning("status: incomplete values received (%d)\n", got_values);
2763 return IMAP_SUCCESS;
2766 static void imap_free_capabilities(IMAPSession *session)
2768 slist_free_strings(session->capability);
2769 g_slist_free(session->capability);
2770 session->capability = NULL;
2773 /* low-level IMAP4rev1 commands */
2775 static gint imap_cmd_login(IMAPSession *session,
2776 const gchar *user, const gchar *pass,
2782 if (!strcmp(type, "LOGIN") && imap_has_capability(session, "LOGINDISABLED")) {
2783 gint ok = IMAP_ERROR;
2784 if (imap_has_capability(session, "STARTTLS")) {
2786 log_warning(LOG_PROTOCOL, _("Server requires TLS to log in.\n"));
2787 ok = imap_cmd_starttls(session);
2788 if (ok != IMAP_SUCCESS) {
2789 log_warning(LOG_PROTOCOL, _("Can't start TLS session.\n"));
2793 imap_free_capabilities(session);
2794 if (imap_get_capabilities(session) != MAILIMAP_NO_ERROR) {
2795 log_warning(LOG_PROTOCOL, _("Can't refresh capabilities.\n"));
2800 log_error(LOG_PROTOCOL, _("Connection to %s failed: "
2801 "server requires TLS, but Claws Mail "
2802 "has been compiled without OpenSSL "
2804 SESSION(session)->server);
2808 log_error(LOG_PROTOCOL, _("Server logins are disabled.\n"));
2813 log_print(LOG_PROTOCOL, "IMAP4> Logging %s to %s using %s\n",
2815 SESSION(session)->server,
2817 r = imap_threaded_login(session->folder, user, pass, type);
2818 if (r != MAILIMAP_NO_ERROR) {
2819 log_print(LOG_PROTOCOL, "IMAP4< Error logging in to %s\n",
2820 SESSION(session)->server);
2823 log_print(LOG_PROTOCOL, "IMAP4< Login to %s successful\n",
2824 SESSION(session)->server);
2830 static gint imap_cmd_noop(IMAPSession *session)
2833 unsigned int exists;
2835 r = imap_threaded_noop(session->folder, &exists);
2836 if (r != MAILIMAP_NO_ERROR) {
2837 debug_print("noop err %d\n", r);
2840 session->exists = exists;
2841 session_set_access_time(SESSION(session));
2843 return IMAP_SUCCESS;
2847 static gint imap_cmd_starttls(IMAPSession *session)
2851 r = imap_threaded_starttls(session->folder,
2852 SESSION(session)->server, SESSION(session)->port);
2853 if (r != MAILIMAP_NO_ERROR) {
2854 debug_print("starttls err %d\n", r);
2857 return IMAP_SUCCESS;
2861 static gint imap_cmd_select(IMAPSession *session, const gchar *folder,
2862 gint *exists, gint *recent, gint *unseen,
2863 guint32 *uid_validity, gboolean block)
2867 r = imap_threaded_select(session->folder, folder,
2868 exists, recent, unseen, uid_validity);
2869 if (r != MAILIMAP_NO_ERROR) {
2870 debug_print("select err %d\n", r);
2873 return IMAP_SUCCESS;
2876 static gint imap_cmd_examine(IMAPSession *session, const gchar *folder,
2877 gint *exists, gint *recent, gint *unseen,
2878 guint32 *uid_validity, gboolean block)
2882 r = imap_threaded_examine(session->folder, folder,
2883 exists, recent, unseen, uid_validity);
2884 if (r != MAILIMAP_NO_ERROR) {
2885 debug_print("examine err %d\n", r);
2889 return IMAP_SUCCESS;
2892 static gint imap_cmd_create(IMAPSession *session, const gchar *folder)
2896 r = imap_threaded_create(session->folder, folder);
2897 if (r != MAILIMAP_NO_ERROR) {
2902 return IMAP_SUCCESS;
2905 static gint imap_cmd_rename(IMAPSession *session, const gchar *old_folder,
2906 const gchar *new_folder)
2910 r = imap_threaded_rename(session->folder, old_folder,
2912 if (r != MAILIMAP_NO_ERROR) {
2917 return IMAP_SUCCESS;
2920 static gint imap_cmd_delete(IMAPSession *session, const gchar *folder)
2925 r = imap_threaded_delete(session->folder, folder);
2926 if (r != MAILIMAP_NO_ERROR) {
2931 return IMAP_SUCCESS;
2934 typedef struct _fetch_data {
2935 IMAPSession *session;
2937 const gchar *filename;
2943 static void *imap_cmd_fetch_thread(void *data)
2945 fetch_data *stuff = (fetch_data *)data;
2946 IMAPSession *session = stuff->session;
2947 guint32 uid = stuff->uid;
2948 const gchar *filename = stuff->filename;
2952 r = imap_threaded_fetch_content(session->folder,
2956 r = imap_threaded_fetch_content(session->folder,
2959 if (r != MAILIMAP_NO_ERROR) {
2960 debug_print("fetch err %d\n", r);
2961 return GINT_TO_POINTER(IMAP_ERROR);
2963 return GINT_TO_POINTER(IMAP_SUCCESS);
2966 static gint imap_cmd_fetch(IMAPSession *session, guint32 uid,
2967 const gchar *filename, gboolean headers,
2970 fetch_data *data = g_new0(fetch_data, 1);
2973 data->session = session;
2975 data->filename = filename;
2976 data->headers = headers;
2979 if (prefs_common.work_offline &&
2980 !inc_offline_should_override(FALSE,
2981 _("Claws Mail needs network access in order "
2982 "to access the IMAP server."))) {
2986 statusbar_print_all(_("Fetching message..."));
2987 result = GPOINTER_TO_INT(imap_cmd_fetch_thread(data));
2988 statusbar_pop_all();
2994 static gint imap_cmd_append(IMAPSession *session, const gchar *destfolder,
2995 const gchar *file, IMAPFlags flags,
2998 struct mailimap_flag_list * flag_list;
3001 g_return_val_if_fail(file != NULL, IMAP_ERROR);
3003 flag_list = imap_flag_to_lep(flags);
3004 r = imap_threaded_append(session->folder, destfolder,
3005 file, flag_list, (int *)new_uid);
3006 mailimap_flag_list_free(flag_list);
3008 if (r != MAILIMAP_NO_ERROR) {
3009 debug_print("append err %d\n", r);
3012 return IMAP_SUCCESS;
3015 static gint imap_cmd_copy(IMAPSession *session, struct mailimap_set * set,
3016 const gchar *destfolder, GRelation *uid_mapping,
3017 struct mailimap_set **source, struct mailimap_set **dest)
3021 g_return_val_if_fail(session != NULL, IMAP_ERROR);
3022 g_return_val_if_fail(set != NULL, IMAP_ERROR);
3023 g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
3025 r = imap_threaded_copy(session->folder, set, destfolder, source, dest);
3026 if (r != MAILIMAP_NO_ERROR) {
3031 return IMAP_SUCCESS;
3034 static gint imap_cmd_store(IMAPSession *session, struct mailimap_set * set,
3035 IMAPFlags flags, int do_add)
3038 struct mailimap_flag_list * flag_list;
3039 struct mailimap_store_att_flags * store_att_flags;
3041 flag_list = imap_flag_to_lep(flags);
3045 mailimap_store_att_flags_new_add_flags_silent(flag_list);
3048 mailimap_store_att_flags_new_remove_flags_silent(flag_list);
3050 r = imap_threaded_store(session->folder, set, store_att_flags);
3051 mailimap_store_att_flags_free(store_att_flags);
3052 if (r != MAILIMAP_NO_ERROR) {
3057 return IMAP_SUCCESS;
3060 static gint imap_cmd_expunge(IMAPSession *session)
3064 if (prefs_common.work_offline &&
3065 !inc_offline_should_override(FALSE,
3066 _("Claws Mail needs network access in order "
3067 "to access the IMAP server."))) {
3071 r = imap_threaded_expunge(session->folder);
3072 if (r != MAILIMAP_NO_ERROR) {
3077 return IMAP_SUCCESS;
3080 static void imap_path_separator_subst(gchar *str, gchar separator)
3083 gboolean in_escape = FALSE;
3085 if (!separator || separator == '/') return;
3087 for (p = str; *p != '\0'; p++) {
3088 if (*p == '/' && !in_escape)
3090 else if (*p == '&' && *(p + 1) != '-' && !in_escape)
3092 else if (*p == '-' && in_escape)
3097 static gchar *imap_modified_utf7_to_utf8(const gchar *mutf7_str)
3099 static iconv_t cd = (iconv_t)-1;
3100 static gboolean iconv_ok = TRUE;
3103 size_t norm_utf7_len;
3105 gchar *to_str, *to_p;
3107 gboolean in_escape = FALSE;
3109 if (!iconv_ok) return g_strdup(mutf7_str);
3111 if (cd == (iconv_t)-1) {
3112 cd = iconv_open(CS_INTERNAL, CS_UTF_7);
3113 if (cd == (iconv_t)-1) {
3114 g_warning("iconv cannot convert UTF-7 to %s\n",
3117 return g_strdup(mutf7_str);
3121 /* modified UTF-7 to normal UTF-7 conversion */
3122 norm_utf7 = g_string_new(NULL);
3124 for (p = mutf7_str; *p != '\0'; p++) {
3125 /* replace: '&' -> '+',
3127 escaped ',' -> '/' */
3128 if (!in_escape && *p == '&') {
3129 if (*(p + 1) != '-') {
3130 g_string_append_c(norm_utf7, '+');
3133 g_string_append_c(norm_utf7, '&');
3136 } else if (in_escape && *p == ',') {
3137 g_string_append_c(norm_utf7, '/');
3138 } else if (in_escape && *p == '-') {
3139 g_string_append_c(norm_utf7, '-');
3142 g_string_append_c(norm_utf7, *p);
3146 norm_utf7_p = norm_utf7->str;
3147 norm_utf7_len = norm_utf7->len;
3148 to_len = strlen(mutf7_str) * 5;
3149 to_p = to_str = g_malloc(to_len + 1);
3151 if (iconv(cd, (ICONV_CONST gchar **)&norm_utf7_p, &norm_utf7_len,
3152 &to_p, &to_len) == -1) {
3153 g_warning(_("iconv cannot convert UTF-7 to %s\n"),
3154 conv_get_locale_charset_str());
3155 g_string_free(norm_utf7, TRUE);
3157 return g_strdup(mutf7_str);
3160 /* second iconv() call for flushing */
3161 iconv(cd, NULL, NULL, &to_p, &to_len);
3162 g_string_free(norm_utf7, TRUE);
3168 static gchar *imap_utf8_to_modified_utf7(const gchar *from)
3170 static iconv_t cd = (iconv_t)-1;
3171 static gboolean iconv_ok = TRUE;
3172 gchar *norm_utf7, *norm_utf7_p;
3173 size_t from_len, norm_utf7_len;
3175 gchar *from_tmp, *to, *p;
3176 gboolean in_escape = FALSE;
3178 if (!iconv_ok) return g_strdup(from);
3180 if (cd == (iconv_t)-1) {
3181 cd = iconv_open(CS_UTF_7, CS_INTERNAL);
3182 if (cd == (iconv_t)-1) {
3183 g_warning(_("iconv cannot convert %s to UTF-7\n"),
3186 return g_strdup(from);
3190 /* UTF-8 to normal UTF-7 conversion */
3191 Xstrdup_a(from_tmp, from, return g_strdup(from));
3192 from_len = strlen(from);
3193 norm_utf7_len = from_len * 5;
3194 Xalloca(norm_utf7, norm_utf7_len + 1, return g_strdup(from));
3195 norm_utf7_p = norm_utf7;
3197 #define IS_PRINT(ch) (isprint(ch) && IS_ASCII(ch))
3199 while (from_len > 0) {
3200 if (*from_tmp == '+') {
3201 *norm_utf7_p++ = '+';
3202 *norm_utf7_p++ = '-';
3206 } else if (IS_PRINT(*(guchar *)from_tmp)) {
3207 /* printable ascii char */
3208 *norm_utf7_p = *from_tmp;
3214 size_t conv_len = 0;
3216 /* unprintable char: convert to UTF-7 */
3218 while (!IS_PRINT(*(guchar *)p) && conv_len < from_len) {
3219 conv_len += g_utf8_skip[*(guchar *)p];
3220 p += g_utf8_skip[*(guchar *)p];
3223 from_len -= conv_len;
3224 if (iconv(cd, (ICONV_CONST gchar **)&from_tmp,
3226 &norm_utf7_p, &norm_utf7_len) == -1) {
3227 g_warning(_("iconv cannot convert UTF-8 to UTF-7\n"));
3228 return g_strdup(from);
3231 /* second iconv() call for flushing */
3232 iconv(cd, NULL, NULL, &norm_utf7_p, &norm_utf7_len);
3238 *norm_utf7_p = '\0';
3239 to_str = g_string_new(NULL);
3240 for (p = norm_utf7; p < norm_utf7_p; p++) {
3241 /* replace: '&' -> "&-",
3244 BASE64 '/' -> ',' */
3245 if (!in_escape && *p == '&') {
3246 g_string_append(to_str, "&-");
3247 } else if (!in_escape && *p == '+') {
3248 if (*(p + 1) == '-') {
3249 g_string_append_c(to_str, '+');
3252 g_string_append_c(to_str, '&');
3255 } else if (in_escape && *p == '/') {
3256 g_string_append_c(to_str, ',');
3257 } else if (in_escape && *p == '-') {
3258 g_string_append_c(to_str, '-');
3261 g_string_append_c(to_str, *p);
3267 g_string_append_c(to_str, '-');
3271 g_string_free(to_str, FALSE);
3276 static gboolean imap_rename_folder_func(GNode *node, gpointer data)
3278 FolderItem *item = node->data;
3279 gchar **paths = data;
3280 const gchar *oldpath = paths[0];
3281 const gchar *newpath = paths[1];
3282 gchar *real_oldpath, *real_newpath;
3284 gchar *new_itempath;
3286 IMAPSession *session = imap_session_get(item->folder);
3288 oldpathlen = strlen(oldpath);
3289 if (strncmp(oldpath, item->path, oldpathlen) != 0) {
3290 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
3294 base = item->path + oldpathlen;
3295 while (*base == G_DIR_SEPARATOR) base++;
3297 new_itempath = g_strdup(newpath);
3299 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
3302 real_oldpath = imap_get_real_path(session, IMAP_FOLDER(item->folder), item->path);
3304 item->path = new_itempath;
3306 real_newpath = imap_get_real_path(session, IMAP_FOLDER(item->folder), item->path);
3308 imap_threaded_subscribe(item->folder, real_oldpath, FALSE);
3309 imap_threaded_subscribe(item->folder, real_newpath, TRUE);
3311 g_free(real_oldpath);
3312 g_free(real_newpath);
3316 typedef struct _get_list_uid_data {
3318 IMAPSession *session;
3319 IMAPFolderItem *item;
3320 GSList **msgnum_list;
3322 } get_list_uid_data;
3324 static void *get_list_of_uids_thread(void *data)
3326 get_list_uid_data *stuff = (get_list_uid_data *)data;
3327 Folder *folder = stuff->folder;
3328 IMAPFolderItem *item = stuff->item;
3329 GSList **msgnum_list = stuff->msgnum_list;
3330 gint ok, nummsgs = 0, lastuid_old;
3331 IMAPSession *session;
3332 GSList *uidlist, *elem;
3333 clist * lep_uidlist;
3336 session = stuff->session;
3337 if (session == NULL) {
3339 return GINT_TO_POINTER(-1);
3341 /* no session locking here, it's already locked by caller */
3342 ok = imap_select(session, IMAP_FOLDER(folder), item->item.path,
3343 NULL, NULL, NULL, NULL, TRUE);
3344 if (ok != IMAP_SUCCESS) {
3346 return GINT_TO_POINTER(-1);
3351 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SIMPLE, NULL,
3354 if (r == MAILIMAP_NO_ERROR) {
3355 GSList * fetchuid_list;
3358 imap_uid_list_from_lep(lep_uidlist);
3359 mailimap_search_result_free(lep_uidlist);
3361 uidlist = g_slist_concat(fetchuid_list, uidlist);
3364 GSList * fetchuid_list;
3365 carray * lep_uidtab;
3367 r = imap_threaded_fetch_uid(folder, item->lastuid + 1,
3369 if (r == MAILIMAP_NO_ERROR) {
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 lastuid_old = item->lastuid;
3378 *msgnum_list = g_slist_copy(item->uid_list);
3379 nummsgs = g_slist_length(*msgnum_list);
3380 debug_print("Got %d uids from cache\n", g_slist_length(item->uid_list));
3382 for (elem = uidlist; elem != NULL; elem = g_slist_next(elem)) {
3385 msgnum = GPOINTER_TO_INT(elem->data);
3386 if (msgnum > lastuid_old) {
3387 *msgnum_list = g_slist_prepend(*msgnum_list, GINT_TO_POINTER(msgnum));
3388 item->uid_list = g_slist_prepend(item->uid_list, GINT_TO_POINTER(msgnum));
3391 if(msgnum > item->lastuid)
3392 item->lastuid = msgnum;
3395 g_slist_free(uidlist);
3397 return GINT_TO_POINTER(nummsgs);
3400 static gint get_list_of_uids(IMAPSession *session, Folder *folder, IMAPFolderItem *item, GSList **msgnum_list)
3403 get_list_uid_data *data = g_new0(get_list_uid_data, 1);
3405 data->folder = folder;
3407 data->msgnum_list = msgnum_list;
3408 data->session = session;
3409 if (prefs_common.work_offline &&
3410 !inc_offline_should_override(FALSE,
3411 _("Claws Mail needs network access in order "
3412 "to access the IMAP server."))) {
3417 result = GPOINTER_TO_INT(get_list_of_uids_thread(data));
3423 gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list, gboolean *old_uids_valid)
3425 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3426 IMAPSession *session;
3427 gint ok, nummsgs = 0, exists;
3428 guint32 uid_next = 0, uid_val = 0;
3429 GSList *uidlist = NULL;
3431 gboolean selected_folder;
3432 debug_print("get_num_list\n");
3434 g_return_val_if_fail(folder != NULL, -1);
3435 g_return_val_if_fail(item != NULL, -1);
3436 g_return_val_if_fail(item->item.path != NULL, -1);
3437 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3438 g_return_val_if_fail(folder->account != NULL, -1);
3440 debug_print("getting session...\n");
3441 session = imap_session_get(folder);
3442 g_return_val_if_fail(session != NULL, -1);
3444 if (FOLDER_ITEM(item)->path)
3445 statusbar_print_all(_("Scanning folder %s%c%s ..."),
3446 FOLDER_ITEM(item)->folder->name,
3448 FOLDER_ITEM(item)->path);
3450 statusbar_print_all(_("Scanning folder %s ..."),
3451 FOLDER_ITEM(item)->folder->name);
3453 selected_folder = (session->mbox != NULL) &&
3454 (!strcmp(session->mbox, item->item.path));
3455 if (selected_folder && time(NULL) - item->use_cache < 2) {
3456 ok = imap_cmd_noop(session);
3457 if (ok != IMAP_SUCCESS) {
3458 debug_print("disconnected!\n");
3459 session = imap_reconnect_if_possible(folder, session);
3460 if (session == NULL) {
3461 statusbar_pop_all();
3462 unlock_session(session);
3466 exists = session->exists;
3468 uid_next = item->c_uid_next;
3469 uid_val = item->c_uid_validity;
3470 *old_uids_valid = TRUE;
3472 if (item->use_cache && time(NULL) - item->use_cache < 2) {
3473 exists = item->c_messages;
3474 uid_next = item->c_uid_next;
3475 uid_val = item->c_uid_validity;
3477 debug_print("using cache %d %d %d\n", exists, uid_next, uid_val);
3479 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path, item,
3480 &exists, &uid_next, &uid_val, NULL, FALSE);
3482 item->item.last_num = uid_next - 1;
3484 item->use_cache = (time_t)0;
3485 if (ok != IMAP_SUCCESS) {
3486 statusbar_pop_all();
3487 unlock_session(session);
3490 if(item->item.mtime == uid_val)
3491 *old_uids_valid = TRUE;
3493 *old_uids_valid = FALSE;
3495 debug_print("Freeing imap uid cache (%d != %d)\n",
3496 (int)item->item.mtime, uid_val);
3498 g_slist_free(item->uid_list);
3499 item->uid_list = NULL;
3501 item->item.mtime = uid_val;
3503 imap_delete_all_cached_messages((FolderItem *)item);
3507 /* If old uid_next matches new uid_next we can be sure no message
3508 was added to the folder */
3509 debug_print("uid_next is %d and item->uid_next %d \n",
3510 uid_next, item->uid_next);
3511 if (uid_next == item->uid_next) {
3512 nummsgs = g_slist_length(item->uid_list);
3514 /* If number of messages is still the same we
3515 know our caches message numbers are still valid,
3516 otherwise if the number of messages has decrease
3517 we discard our cache to start a new scan to find
3518 out which numbers have been removed */
3519 if (exists == nummsgs) {
3520 debug_print("exists == nummsgs\n");
3521 *msgnum_list = g_slist_copy(item->uid_list);
3522 statusbar_pop_all();
3523 unlock_session(session);
3525 } else if (exists < nummsgs) {
3526 debug_print("Freeing imap uid cache");
3528 g_slist_free(item->uid_list);
3529 item->uid_list = NULL;
3534 *msgnum_list = NULL;
3535 statusbar_pop_all();
3536 unlock_session(session);
3540 item->last_change = time(NULL);
3541 nummsgs = get_list_of_uids(session, folder, item, &uidlist);
3544 statusbar_pop_all();
3545 unlock_session(session);
3549 if (nummsgs != exists) {
3550 /* Cache contains more messages then folder, we have cached
3551 an old UID of a message that was removed and new messages
3552 have been added too, otherwise the uid_next check would
3554 debug_print("Freeing imap uid cache");
3556 g_slist_free(item->uid_list);
3557 item->uid_list = NULL;
3559 g_slist_free(*msgnum_list);
3561 nummsgs = get_list_of_uids(session, folder, item, &uidlist);
3564 *msgnum_list = uidlist;
3566 dir = folder_item_get_path((FolderItem *)item);
3567 debug_print("removing old messages from %s\n", dir);
3568 remove_numbered_files_not_in_list(dir, *msgnum_list);
3571 item->uid_next = uid_next;
3573 debug_print("get_num_list - ok - %i\n", nummsgs);
3574 statusbar_pop_all();
3575 unlock_session(session);
3579 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item)
3584 flags.perm_flags = MSG_NEW|MSG_UNREAD;
3585 flags.tmp_flags = 0;
3587 g_return_val_if_fail(item != NULL, NULL);
3588 g_return_val_if_fail(file != NULL, NULL);
3590 if (folder_has_parent_of_type(item, F_QUEUE)) {
3591 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
3592 } else if (folder_has_parent_of_type(item, F_DRAFT)) {
3593 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
3596 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3597 if (!msginfo) return NULL;
3599 msginfo->plaintext_file = g_strdup(file);
3600 msginfo->folder = item;
3605 GSList *imap_get_msginfos(Folder *folder, FolderItem *item,
3606 GSList *msgnum_list)
3608 IMAPSession *session;
3609 MsgInfoList *ret = NULL;
3612 debug_print("get_msginfos\n");
3614 g_return_val_if_fail(folder != NULL, NULL);
3615 g_return_val_if_fail(item != NULL, NULL);
3616 g_return_val_if_fail(msgnum_list != NULL, NULL);
3618 debug_print("getting session...\n");
3619 session = imap_session_get(folder);
3620 g_return_val_if_fail(session != NULL, NULL);
3622 debug_print("IMAP getting msginfos\n");
3623 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3624 NULL, NULL, NULL, NULL, FALSE);
3625 if (ok != IMAP_SUCCESS) {
3626 unlock_session(session);
3629 if (!(folder_has_parent_of_type(item, F_DRAFT) ||
3630 folder_has_parent_of_type(item, F_QUEUE))) {
3631 ret = g_slist_concat(ret,
3632 imap_get_uncached_messages(session, item,
3635 MsgNumberList *sorted_list, *elem, *llast = NULL;
3636 gint startnum, lastnum;
3638 sorted_list = g_slist_sort(g_slist_copy(msgnum_list), g_int_compare);
3640 startnum = lastnum = GPOINTER_TO_INT(sorted_list->data);
3642 llast = g_slist_last(ret);
3643 for (elem = sorted_list;; elem = g_slist_next(elem)) {
3647 num = GPOINTER_TO_INT(elem->data);
3649 if (num > lastnum + 1 || elem == NULL) {
3651 for (i = startnum; i <= lastnum; ++i) {
3654 file = imap_fetch_msg(folder, item, i);
3656 MsgInfo *msginfo = imap_parse_msg(file, item);
3657 if (msginfo != NULL) {
3658 msginfo->msgnum = i;
3660 llast = ret = g_slist_append(ret, msginfo);
3662 llast = g_slist_append(llast, msginfo);
3663 llast = llast->next;
3668 session_set_access_time(SESSION(session));
3679 g_slist_free(sorted_list);
3681 unlock_session(session);
3685 MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid)
3687 MsgInfo *msginfo = NULL;
3688 MsgInfoList *msginfolist;
3689 MsgNumberList numlist;
3691 numlist.next = NULL;
3692 numlist.data = GINT_TO_POINTER(uid);
3694 msginfolist = imap_get_msginfos(folder, item, &numlist);
3695 if (msginfolist != NULL) {
3696 msginfo = msginfolist->data;
3697 g_slist_free(msginfolist);
3703 gboolean imap_scan_required(Folder *folder, FolderItem *_item)
3705 IMAPSession *session;
3706 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3707 gint ok, exists = 0, unseen = 0;
3708 guint32 uid_next = 0, uid_val = 0;
3709 gboolean selected_folder;
3711 g_return_val_if_fail(folder != NULL, FALSE);
3712 g_return_val_if_fail(item != NULL, FALSE);
3713 g_return_val_if_fail(item->item.folder != NULL, FALSE);
3714 g_return_val_if_fail(FOLDER_CLASS(item->item.folder) == &imap_class, FALSE);
3716 if (item->item.path == NULL)
3719 debug_print("getting session...\n");
3720 session = imap_session_get(folder);
3721 g_return_val_if_fail(session != NULL, FALSE);
3723 selected_folder = (session->mbox != NULL) &&
3724 (!strcmp(session->mbox, item->item.path));
3725 if (selected_folder && time(NULL) - item->use_cache < 2) {
3726 ok = imap_cmd_noop(session);
3727 if (ok != IMAP_SUCCESS) {
3728 debug_print("disconnected!\n");
3729 session = imap_reconnect_if_possible(folder, session);
3730 if (session == NULL)
3734 if (session->folder_content_changed
3735 || session->exists != item->item.total_msgs) {
3736 unlock_session(session);
3740 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path, IMAP_FOLDER_ITEM(item),
3741 &exists, &uid_next, &uid_val, &unseen, FALSE);
3742 if (ok != IMAP_SUCCESS) {
3743 unlock_session(session);
3747 item->use_cache = time(NULL);
3748 item->c_messages = exists;
3749 item->c_uid_next = uid_next;
3750 item->c_uid_validity = uid_val;
3751 item->c_unseen = unseen;
3752 item->item.last_num = uid_next - 1;
3753 debug_print("uidnext %d, item->uid_next %d, exists %d, item->item.total_msgs %d\n",
3754 uid_next, item->uid_next, exists, item->item.total_msgs);
3755 if ((uid_next != item->uid_next) || (exists != item->item.total_msgs)
3756 || unseen != item->item.unread_msgs || uid_val != item->item.mtime) {
3757 unlock_session(session);
3758 item->last_change = time(NULL);
3762 unlock_session(session);
3766 void imap_change_flags(Folder *folder, FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
3768 IMAPSession *session;
3769 IMAPFlags flags_set = 0, flags_unset = 0;
3770 gint ok = IMAP_SUCCESS;
3771 MsgNumberList numlist;
3772 hashtable_data *ht_data = NULL;
3774 g_return_if_fail(folder != NULL);
3775 g_return_if_fail(folder->klass == &imap_class);
3776 g_return_if_fail(item != NULL);
3777 g_return_if_fail(item->folder == folder);
3778 g_return_if_fail(msginfo != NULL);
3779 g_return_if_fail(msginfo->folder == item);
3781 if (!MSG_IS_MARKED(msginfo->flags) && (newflags & MSG_MARKED))
3782 flags_set |= IMAP_FLAG_FLAGGED;
3783 if ( MSG_IS_MARKED(msginfo->flags) && !(newflags & MSG_MARKED))
3784 flags_unset |= IMAP_FLAG_FLAGGED;
3786 if (!MSG_IS_UNREAD(msginfo->flags) && (newflags & MSG_UNREAD))
3787 flags_unset |= IMAP_FLAG_SEEN;
3788 if ( MSG_IS_UNREAD(msginfo->flags) && !(newflags & MSG_UNREAD))
3789 flags_set |= IMAP_FLAG_SEEN;
3791 if (!MSG_IS_REPLIED(msginfo->flags) && (newflags & MSG_REPLIED))
3792 flags_set |= IMAP_FLAG_ANSWERED;
3793 if ( MSG_IS_REPLIED(msginfo->flags) && !(newflags & MSG_REPLIED))
3794 flags_unset |= IMAP_FLAG_ANSWERED;
3796 if (!MSG_IS_DELETED(msginfo->flags) && (newflags & MSG_DELETED))
3797 flags_set |= IMAP_FLAG_DELETED;
3798 if ( MSG_IS_DELETED(msginfo->flags) && !(newflags & MSG_DELETED))
3799 flags_unset |= IMAP_FLAG_DELETED;
3801 if (!flags_set && !flags_unset) {
3802 /* the changed flags were not translatable to IMAP-speak.
3803 * like MSG_POSTFILTERED, so just apply. */
3804 msginfo->flags.perm_flags = newflags;
3808 debug_print("getting session...\n");
3809 session = imap_session_get(folder);
3814 if ((ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
3815 NULL, NULL, NULL, NULL, FALSE)) != IMAP_SUCCESS) {
3816 unlock_session(session);
3819 numlist.next = NULL;
3820 numlist.data = GINT_TO_POINTER(msginfo->msgnum);
3822 if (IMAP_FOLDER_ITEM(item)->batching) {
3823 /* instead of performing an UID STORE command for each message change,
3824 * as a lot of them can change "together", we just fill in hashtables
3825 * and defer the treatment so that we're able to send only one
3828 debug_print("IMAP batch mode on, deferring flags change\n");
3830 ht_data = g_hash_table_lookup(IMAP_FOLDER_ITEM(item)->flags_set_table,
3831 GINT_TO_POINTER(flags_set));
3832 if (ht_data == NULL) {
3833 ht_data = g_new0(hashtable_data, 1);
3834 ht_data->session = session;
3835 ht_data->item = IMAP_FOLDER_ITEM(item);
3836 g_hash_table_insert(IMAP_FOLDER_ITEM(item)->flags_set_table,
3837 GINT_TO_POINTER(flags_set), ht_data);
3839 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
3840 ht_data->msglist = g_slist_prepend(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum));
3843 ht_data = g_hash_table_lookup(IMAP_FOLDER_ITEM(item)->flags_unset_table,
3844 GINT_TO_POINTER(flags_unset));
3845 if (ht_data == NULL) {
3846 ht_data = g_new0(hashtable_data, 1);
3847 ht_data->session = session;
3848 ht_data->item = IMAP_FOLDER_ITEM(item);
3849 g_hash_table_insert(IMAP_FOLDER_ITEM(item)->flags_unset_table,
3850 GINT_TO_POINTER(flags_unset), ht_data);
3852 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
3853 ht_data->msglist = g_slist_prepend(ht_data->msglist,
3854 GINT_TO_POINTER(msginfo->msgnum));
3857 debug_print("IMAP changing flags\n");
3859 ok = imap_set_message_flags(session, &numlist, flags_set, TRUE);
3860 if (ok != IMAP_SUCCESS) {
3861 unlock_session(session);
3867 ok = imap_set_message_flags(session, &numlist, flags_unset, FALSE);
3868 if (ok != IMAP_SUCCESS) {
3869 unlock_session(session);
3874 msginfo->flags.perm_flags = newflags;
3875 unlock_session(session);
3879 static gint imap_remove_msg(Folder *folder, FolderItem *item, gint uid)
3882 IMAPSession *session;
3884 MsgNumberList numlist;
3886 g_return_val_if_fail(folder != NULL, -1);
3887 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3888 g_return_val_if_fail(item != NULL, -1);
3890 debug_print("getting session...\n");
3891 session = imap_session_get(folder);
3892 if (!session) return -1;
3894 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3895 NULL, NULL, NULL, NULL, FALSE);
3896 if (ok != IMAP_SUCCESS) {
3897 unlock_session(session);
3900 numlist.next = NULL;
3901 numlist.data = GINT_TO_POINTER(uid);
3903 ok = imap_set_message_flags
3904 (session, &numlist, IMAP_FLAG_DELETED, TRUE);
3905 if (ok != IMAP_SUCCESS) {
3906 log_warning(LOG_PROTOCOL, _("can't set deleted flags: %d\n"), uid);
3907 unlock_session(session);
3911 if (!session->uidplus) {
3912 ok = imap_cmd_expunge(session);
3916 uidstr = g_strdup_printf("%u", uid);
3917 ok = imap_cmd_expunge(session);
3920 if (ok != IMAP_SUCCESS) {
3921 log_warning(LOG_PROTOCOL, _("can't expunge\n"));
3922 unlock_session(session);
3926 IMAP_FOLDER_ITEM(item)->uid_list = g_slist_remove(
3927 IMAP_FOLDER_ITEM(item)->uid_list, numlist.data);
3928 dir = folder_item_get_path(item);
3929 if (is_dir_exist(dir))
3930 remove_numbered_files(dir, uid, uid);
3932 unlock_session(session);
3933 return IMAP_SUCCESS;
3936 static gint compare_msginfo(gconstpointer a, gconstpointer b)
3938 return ((MsgInfo *)a)->msgnum - ((MsgInfo *)b)->msgnum;
3941 static guint gslist_find_next_num(MsgNumberList **list, guint num)
3945 g_return_val_if_fail(list != NULL, -1);
3947 for (elem = *list; elem != NULL; elem = g_slist_next(elem))
3948 if (GPOINTER_TO_INT(elem->data) >= num)
3951 return elem != NULL ? GPOINTER_TO_INT(elem->data) : (gint)-1;
3955 * NEW and DELETED flags are not syncronized
3956 * - The NEW/RECENT flags in IMAP folders can not really be directly
3957 * modified by Sylpheed
3958 * - The DELETE/DELETED flag in IMAP and Sylpheed don't have the same
3959 * meaning, in IMAP it always removes the messages from the FolderItem
3960 * in Sylpheed it can mean to move the message to trash
3963 typedef struct _get_flags_data {
3966 MsgInfoList *msginfo_list;
3967 GRelation *msgflags;
3968 gboolean full_search;
3972 static /*gint*/ void *imap_get_flags_thread(void *data)
3974 get_flags_data *stuff = (get_flags_data *)data;
3975 Folder *folder = stuff->folder;
3976 FolderItem *item = stuff->item;
3977 MsgInfoList *msginfo_list = stuff->msginfo_list;
3978 GRelation *msgflags = stuff->msgflags;
3979 gboolean full_search = stuff->full_search;
3980 IMAPSession *session;
3981 GSList *sorted_list = NULL;
3982 GSList *unseen = NULL, *answered = NULL, *flagged = NULL, *deleted = NULL;
3983 GSList *p_unseen, *p_answered, *p_flagged, *p_deleted;
3985 GSList *seq_list, *cur;
3986 gboolean reverse_seen = FALSE;
3989 gint exists_cnt, unseen_cnt;
3990 gboolean selected_folder;
3992 if (folder == NULL || item == NULL) {
3994 return GINT_TO_POINTER(-1);
3997 debug_print("getting session...\n");
3998 session = imap_session_get(folder);
3999 if (session == NULL) {
4001 return GINT_TO_POINTER(-1);
4004 selected_folder = (session->mbox != NULL) &&
4005 (!strcmp(session->mbox, item->path));
4007 if (!selected_folder) {
4008 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
4009 &exists_cnt, NULL, &unseen_cnt, NULL, TRUE);
4010 if (ok != IMAP_SUCCESS) {
4012 unlock_session(session);
4013 return GINT_TO_POINTER(-1);
4016 if (unseen_cnt > exists_cnt / 2)
4017 reverse_seen = TRUE;
4020 if (item->unread_msgs > item->total_msgs / 2)
4021 reverse_seen = TRUE;
4024 cmd_buf = g_string_new(NULL);
4026 sorted_list = g_slist_sort(g_slist_copy(msginfo_list), compare_msginfo);
4028 seq_list = imap_get_lep_set_from_msglist(msginfo_list);
4030 struct mailimap_set * set;
4031 set = mailimap_set_new_interval(1, 0);
4032 seq_list = g_slist_append(NULL, set);
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 (item->opened || item->processing_pending || item == 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);
4096 p_answered = answered;
4097 p_flagged = flagged;
4098 p_deleted = deleted;
4100 for (elem = sorted_list; elem != NULL; elem = g_slist_next(elem)) {
4105 msginfo = (MsgInfo *) elem->data;
4106 flags = msginfo->flags.perm_flags;
4107 wasnew = (flags & MSG_NEW);
4108 if (item->opened || item->processing_pending || item == folder->inbox) {
4109 flags &= ~((reverse_seen ? 0 : MSG_UNREAD | MSG_NEW) | MSG_REPLIED | MSG_MARKED);
4111 flags &= ~((reverse_seen ? 0 : MSG_UNREAD | MSG_NEW | MSG_MARKED));
4114 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
4115 if (gslist_find_next_num(&p_unseen, msginfo->msgnum) == msginfo->msgnum) {
4116 if (!reverse_seen) {
4117 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
4119 flags &= ~(MSG_UNREAD | MSG_NEW);
4123 if (gslist_find_next_num(&p_flagged, msginfo->msgnum) == msginfo->msgnum)
4124 flags |= MSG_MARKED;
4126 flags &= ~MSG_MARKED;
4128 if (item->opened || item->processing_pending || item == folder->inbox) {
4129 if (gslist_find_next_num(&p_answered, msginfo->msgnum) == msginfo->msgnum)
4130 flags |= MSG_REPLIED;
4132 flags &= ~MSG_REPLIED;
4133 if (gslist_find_next_num(&p_deleted, msginfo->msgnum) == msginfo->msgnum)
4134 flags |= MSG_DELETED;
4136 flags &= ~MSG_DELETED;
4138 g_relation_insert(msgflags, msginfo, GINT_TO_POINTER(flags));
4141 imap_lep_set_free(seq_list);
4142 g_slist_free(flagged);
4143 g_slist_free(deleted);
4144 g_slist_free(answered);
4145 g_slist_free(unseen);
4146 g_slist_free(sorted_list);
4147 g_string_free(cmd_buf, TRUE);
4150 unlock_session(session);
4151 return GINT_TO_POINTER(0);
4154 static gint imap_get_flags(Folder *folder, FolderItem *item,
4155 MsgInfoList *msginfo_list, GRelation *msgflags)
4158 get_flags_data *data = g_new0(get_flags_data, 1);
4160 data->folder = folder;
4162 data->msginfo_list = msginfo_list;
4163 data->msgflags = msgflags;
4164 data->full_search = FALSE;
4166 GSList *tmp = NULL, *cur;
4168 if (prefs_common.work_offline &&
4169 !inc_offline_should_override(FALSE,
4170 _("Claws Mail needs network access in order "
4171 "to access the IMAP server."))) {
4176 tmp = folder_item_get_msg_list(item);
4178 if (g_slist_length(tmp) <= g_slist_length(msginfo_list))
4179 data->full_search = TRUE;
4181 for (cur = tmp; cur; cur = cur->next)
4182 procmsg_msginfo_free((MsgInfo *)cur->data);
4186 result = GPOINTER_TO_INT(imap_get_flags_thread(data));
4193 static gboolean process_flags(gpointer key, gpointer value, gpointer user_data)
4195 gboolean flags_set = GPOINTER_TO_INT(user_data);
4196 gint flags_value = GPOINTER_TO_INT(key);
4197 hashtable_data *data = (hashtable_data *)value;
4198 IMAPFolderItem *_item = data->item;
4199 FolderItem *item = (FolderItem *)_item;
4200 gint ok = IMAP_ERROR;
4201 IMAPSession *session = NULL;
4203 debug_print("getting session...\n");
4204 session = imap_session_get(item->folder);
4206 data->msglist = g_slist_reverse(data->msglist);
4208 debug_print("IMAP %ssetting flags to %d for %d messages\n",
4211 g_slist_length(data->msglist));
4214 ok = imap_select(session, IMAP_FOLDER(item->folder), item->path,
4215 NULL, NULL, NULL, NULL, FALSE);
4217 if (ok == IMAP_SUCCESS) {
4218 imap_set_message_flags(data->session, data->msglist, flags_value, flags_set);
4220 g_warning("can't select mailbox %s\n", item->path);
4223 unlock_session(session);
4224 g_slist_free(data->msglist);
4229 static void process_hashtable(IMAPFolderItem *item)
4231 if (item->flags_set_table) {
4232 g_hash_table_foreach_remove(item->flags_set_table, process_flags, GINT_TO_POINTER(TRUE));
4233 g_hash_table_destroy(item->flags_set_table);
4234 item->flags_set_table = NULL;
4236 if (item->flags_unset_table) {
4237 g_hash_table_foreach_remove(item->flags_unset_table, process_flags, GINT_TO_POINTER(FALSE));
4238 g_hash_table_destroy(item->flags_unset_table);
4239 item->flags_unset_table = NULL;
4243 static void imap_set_batch (Folder *folder, FolderItem *_item, gboolean batch)
4245 IMAPFolderItem *item = (IMAPFolderItem *)_item;
4247 g_return_if_fail(item != NULL);
4249 if (item->batching == batch)
4253 item->batching = TRUE;
4254 debug_print("IMAP switching to batch mode\n");
4255 if (!item->flags_set_table) {
4256 item->flags_set_table = g_hash_table_new(NULL, g_direct_equal);
4258 if (!item->flags_unset_table) {
4259 item->flags_unset_table = g_hash_table_new(NULL, g_direct_equal);
4262 debug_print("IMAP switching away from batch mode\n");
4264 process_hashtable(item);
4265 item->batching = FALSE;
4271 /* data types conversion libetpan <-> claws */
4275 #define ETPAN_IMAP_MB_MARKED 1
4276 #define ETPAN_IMAP_MB_UNMARKED 2
4277 #define ETPAN_IMAP_MB_NOSELECT 4
4278 #define ETPAN_IMAP_MB_NOINFERIORS 8
4280 static int imap_flags_to_flags(struct mailimap_mbx_list_flags * imap_flags)
4286 if (imap_flags->mbf_type == MAILIMAP_MBX_LIST_FLAGS_SFLAG) {
4287 switch (imap_flags->mbf_sflag) {
4288 case MAILIMAP_MBX_LIST_SFLAG_MARKED:
4289 flags |= ETPAN_IMAP_MB_MARKED;
4291 case MAILIMAP_MBX_LIST_SFLAG_NOSELECT:
4292 flags |= ETPAN_IMAP_MB_NOSELECT;
4294 case MAILIMAP_MBX_LIST_SFLAG_UNMARKED:
4295 flags |= ETPAN_IMAP_MB_UNMARKED;
4300 for(cur = clist_begin(imap_flags->mbf_oflags) ; cur != NULL ;
4301 cur = clist_next(cur)) {
4302 struct mailimap_mbx_list_oflag * oflag;
4304 oflag = clist_content(cur);
4306 switch (oflag->of_type) {
4307 case MAILIMAP_MBX_LIST_OFLAG_NOINFERIORS:
4308 flags |= ETPAN_IMAP_MB_NOINFERIORS;
4316 static GSList * imap_list_from_lep(IMAPFolder * folder,
4317 clist * list, const gchar * real_path, gboolean all)
4320 GSList * item_list = NULL, *llast = NULL;
4322 for(iter = clist_begin(list) ; iter != NULL ;
4323 iter = clist_next(iter)) {
4324 struct mailimap_mailbox_list * mb;
4332 FolderItem *new_item;
4334 mb = clist_content(iter);
4340 if (mb->mb_flag != NULL)
4341 flags = imap_flags_to_flags(mb->mb_flag);
4343 delimiter = mb->mb_delimiter;
4346 dup_name = strdup(name);
4347 if (delimiter != '\0')
4348 subst_char(dup_name, delimiter, '/');
4350 base = g_path_get_basename(dup_name);
4351 if (base[0] == '.') {
4356 if (!all && path_cmp(name, real_path) == 0) {
4362 if (!all && dup_name[strlen(dup_name)-1] == '/') {
4363 dup_name[strlen(dup_name)-1] = '\0';
4366 loc_name = imap_modified_utf7_to_utf8(base);
4367 loc_path = imap_modified_utf7_to_utf8(dup_name);
4369 new_item = folder_item_new(FOLDER(folder), loc_name, loc_path);
4370 if ((flags & ETPAN_IMAP_MB_NOINFERIORS) != 0)
4371 new_item->no_sub = TRUE;
4372 if (strcmp(dup_name, "INBOX") != 0 &&
4373 ((flags & ETPAN_IMAP_MB_NOSELECT) != 0))
4374 new_item->no_select = TRUE;
4376 if (item_list == NULL)
4377 llast = item_list = g_slist_append(item_list, new_item);
4379 llast = g_slist_append(llast, new_item);
4380 llast = llast->next;
4382 debug_print("folder '%s' found.\n", loc_path);
4393 static GSList * imap_get_lep_set_from_numlist(MsgNumberList *numlist)
4395 GSList *sorted_list, *cur;
4396 guint first, last, next;
4397 GSList *ret_list = NULL, *llast = NULL;
4399 struct mailimap_set * current_set;
4400 unsigned int item_count;
4402 if (numlist == NULL)
4406 current_set = mailimap_set_new_empty();
4408 sorted_list = g_slist_copy(numlist);
4409 sorted_list = g_slist_sort(sorted_list, g_int_compare);
4411 first = GPOINTER_TO_INT(sorted_list->data);
4414 for (cur = sorted_list; cur != NULL; cur = g_slist_next(cur)) {
4415 if (GPOINTER_TO_INT(cur->data) == 0)
4420 last = GPOINTER_TO_INT(cur->data);
4422 next = GPOINTER_TO_INT(cur->next->data);
4426 if (last + 1 != next || next == 0) {
4428 struct mailimap_set_item * item;
4429 item = mailimap_set_item_new(first, last);
4430 mailimap_set_add(current_set, item);
4435 if (count >= IMAP_SET_MAX_COUNT) {
4436 if (ret_list == NULL)
4437 llast = ret_list = g_slist_append(ret_list,
4440 llast = g_slist_append(llast, current_set);
4441 llast = llast->next;
4443 current_set = mailimap_set_new_empty();
4450 if (clist_count(current_set->set_list) > 0) {
4451 ret_list = g_slist_append(ret_list,
4455 g_slist_free(sorted_list);
4460 static GSList * imap_get_lep_set_from_msglist(MsgInfoList *msglist)
4462 MsgNumberList *numlist = NULL;
4466 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
4467 MsgInfo *msginfo = (MsgInfo *) cur->data;
4469 numlist = g_slist_prepend(numlist, GINT_TO_POINTER(msginfo->msgnum));
4471 numlist = g_slist_reverse(numlist);
4472 seq_list = imap_get_lep_set_from_numlist(numlist);
4473 g_slist_free(numlist);
4478 static GSList * imap_uid_list_from_lep(clist * list)
4485 for(iter = clist_begin(list) ; iter != NULL ;
4486 iter = clist_next(iter)) {
4489 puid = clist_content(iter);
4490 result = g_slist_prepend(result, GINT_TO_POINTER(* puid));
4493 result = g_slist_reverse(result);
4497 static GSList * imap_uid_list_from_lep_tab(carray * list)
4504 for(i = 0 ; i < carray_count(list) ; i ++) {
4507 puid = carray_get(list, i);
4508 result = g_slist_prepend(result, GINT_TO_POINTER(* puid));
4510 result = g_slist_reverse(result);
4514 static MsgInfo *imap_envelope_from_lep(struct imap_fetch_env_info * info,
4517 MsgInfo *msginfo = NULL;
4520 MsgFlags flags = {0, 0};
4522 if (info->headers == NULL)
4525 MSG_SET_TMP_FLAGS(flags, MSG_IMAP);
4526 if (folder_has_parent_of_type(item, F_QUEUE)) {
4527 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
4528 } else if (folder_has_parent_of_type(item, F_DRAFT)) {
4529 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
4531 flags.perm_flags = info->flags;
4535 msginfo = procheader_parse_str(info->headers, flags, FALSE, FALSE);
4538 msginfo->msgnum = uid;
4539 msginfo->size = size;
4545 static void imap_lep_set_free(GSList *seq_list)
4549 for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) {
4550 struct mailimap_set * imapset;
4552 imapset = cur->data;
4553 mailimap_set_free(imapset);
4555 g_slist_free(seq_list);
4558 static struct mailimap_flag_list * imap_flag_to_lep(IMAPFlags flags)
4560 struct mailimap_flag_list * flag_list;
4562 flag_list = mailimap_flag_list_new_empty();
4564 if (IMAP_IS_SEEN(flags))
4565 mailimap_flag_list_add(flag_list,
4566 mailimap_flag_new_seen());
4567 if (IMAP_IS_ANSWERED(flags))
4568 mailimap_flag_list_add(flag_list,
4569 mailimap_flag_new_answered());
4570 if (IMAP_IS_FLAGGED(flags))
4571 mailimap_flag_list_add(flag_list,
4572 mailimap_flag_new_flagged());
4573 if (IMAP_IS_DELETED(flags))
4574 mailimap_flag_list_add(flag_list,
4575 mailimap_flag_new_deleted());
4576 if (IMAP_IS_DRAFT(flags))
4577 mailimap_flag_list_add(flag_list,
4578 mailimap_flag_new_draft());
4583 guint imap_folder_get_refcnt(Folder *folder)
4585 return ((IMAPFolder *)folder)->refcnt;
4588 void imap_folder_ref(Folder *folder)
4590 ((IMAPFolder *)folder)->refcnt++;
4593 void imap_disconnect_all(void)
4596 for (list = account_get_list(); list != NULL; list = list->next) {
4597 PrefsAccount *account = list->data;
4598 if (account->protocol == A_IMAP4) {
4599 RemoteFolder *folder = (RemoteFolder *)account->folder;
4600 if (folder && folder->session) {
4601 IMAPSession *session = (IMAPSession *)folder->session;
4602 imap_threaded_disconnect(FOLDER(folder));
4603 SESSION(session)->state = SESSION_DISCONNECTED;
4604 session_destroy(SESSION(session));
4605 folder->session = NULL;
4611 void imap_folder_unref(Folder *folder)
4613 if (((IMAPFolder *)folder)->refcnt > 0)
4614 ((IMAPFolder *)folder)->refcnt--;
4617 void imap_cancel_all(void)
4622 folderlist = folder_get_list();
4623 for (cur = folderlist; cur != NULL; cur = g_list_next(cur)) {
4624 Folder *folder = (Folder *) cur->data;
4626 if (folder->klass == &imap_class) {
4627 if (imap_is_busy(folder)) {
4628 IMAPSession *imap_session;
4629 RemoteFolder *rfolder;
4631 fprintf(stderr, "cancelled\n");
4632 imap_threaded_cancel(folder);
4633 rfolder = (RemoteFolder *) folder;
4634 imap_session = (IMAPSession *) rfolder->session;
4635 imap_session->cancelled = 1;
4641 gboolean imap_cancel_all_enabled(void)
4646 folderlist = folder_get_list();
4647 for (cur = folderlist; cur != NULL; cur = g_list_next(cur)) {
4648 Folder *folder = (Folder *) cur->data;
4650 if (folder->klass == &imap_class) {
4651 if (imap_is_busy(folder)) {
4660 static gboolean imap_is_busy(Folder *folder)
4662 IMAPSession *imap_session;
4663 RemoteFolder *rfolder;
4665 rfolder = (RemoteFolder *) folder;
4666 imap_session = (IMAPSession *) rfolder->session;
4667 if (imap_session == NULL)
4670 return imap_session->busy;
4673 #else /* HAVE_LIBETPAN */
4675 static FolderClass imap_class;
4677 static XMLTag *imap_item_get_xml(Folder *folder, FolderItem *item);
4678 static void imap_item_set_xml(Folder *folder, FolderItem *item, XMLTag *tag);
4680 static Folder *imap_folder_new (const gchar *name,
4683 static gboolean missing_imap_warning = TRUE;
4684 if (missing_imap_warning) {
4685 missing_imap_warning = FALSE;
4687 _("You have one or more IMAP accounts "
4688 "defined. However this version of "
4689 "Claws Mail has been built without "
4690 "IMAP support; your IMAP account(s) are "
4692 "You probably need to "
4693 "install libetpan and recompile "
4698 static gint imap_create_tree (Folder *folder)
4702 static FolderItem *imap_create_folder (Folder *folder,
4708 static gint imap_rename_folder (Folder *folder,
4715 gchar imap_get_path_separator_for_item(FolderItem *item)
4720 FolderClass *imap_get_class(void)
4722 if (imap_class.idstr == NULL) {
4723 imap_class.type = F_IMAP;
4724 imap_class.idstr = "imap";
4725 imap_class.uistr = "IMAP4";
4727 imap_class.new_folder = imap_folder_new;
4728 imap_class.create_tree = imap_create_tree;
4729 imap_class.create_folder = imap_create_folder;
4730 imap_class.rename_folder = imap_rename_folder;
4732 imap_class.set_xml = folder_set_xml;
4733 imap_class.get_xml = folder_get_xml;
4734 imap_class.item_set_xml = imap_item_set_xml;
4735 imap_class.item_get_xml = imap_item_get_xml;
4736 /* nothing implemented */
4742 void imap_disconnect_all(void)
4746 gint imap_scan_tree_real(Folder *folder, gboolean subs_only)
4751 gint imap_subscribe(Folder *folder, FolderItem *item, gchar *rpath, gboolean sub)
4756 GList * imap_scan_subtree(Folder *folder, FolderItem *item, gboolean unsubs_only, gboolean recursive)
4761 void imap_cache_msg(FolderItem *item, gint msgnum)
4765 void imap_cancel_all(void)
4769 gboolean imap_cancel_all_enabled(void)
4776 void imap_synchronise(FolderItem *item)
4778 #ifdef HAVE_LIBETPAN
4779 if (IMAP_FOLDER_ITEM(item)->last_sync == IMAP_FOLDER_ITEM(item)->last_change) {
4780 debug_print("%s already synced\n", item->path?item->path:item->name);
4783 debug_print("syncing %s\n", item->path?item->path:item->name);
4784 imap_gtk_synchronise(item);
4785 IMAP_FOLDER_ITEM(item)->last_sync = IMAP_FOLDER_ITEM(item)->last_change;
4789 static void imap_item_set_xml(Folder *folder, FolderItem *item, XMLTag *tag)
4791 #ifdef HAVE_LIBETPAN
4794 folder_item_set_xml(folder, item, tag);
4796 #ifdef HAVE_LIBETPAN
4797 for (cur = tag->attr; cur != NULL; cur = g_list_next(cur)) {
4798 XMLAttr *attr = (XMLAttr *) cur->data;
4800 if (!attr || !attr->name || !attr->value) continue;
4801 if (!strcmp(attr->name, "uidnext"))
4802 IMAP_FOLDER_ITEM(item)->uid_next = atoi(attr->value);
4803 if (!strcmp(attr->name, "last_sync"))
4804 IMAP_FOLDER_ITEM(item)->last_sync = atoi(attr->value);
4805 if (!strcmp(attr->name, "last_change"))
4806 IMAP_FOLDER_ITEM(item)->last_change = atoi(attr->value);
4808 if (IMAP_FOLDER_ITEM(item)->last_change == 0)
4809 IMAP_FOLDER_ITEM(item)->last_change = time(NULL);
4813 static XMLTag *imap_item_get_xml(Folder *folder, FolderItem *item)
4817 tag = folder_item_get_xml(folder, item);
4819 #ifdef HAVE_LIBETPAN
4820 xml_tag_add_attr(tag, xml_attr_new_int("uidnext",
4821 IMAP_FOLDER_ITEM(item)->uid_next));
4822 xml_tag_add_attr(tag, xml_attr_new_int("last_sync",
4823 IMAP_FOLDER_ITEM(item)->last_sync));
4824 xml_tag_add_attr(tag, xml_attr_new_int("last_change",
4825 IMAP_FOLDER_ITEM(item)->last_change));