2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2007 Hiroyuki Yamamoto and the Claws Mail team
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
27 #include <glib/gi18n.h>
34 #include "alertpanel.h"
57 #include "procheader.h"
58 #include "prefs_account.h"
63 #include "prefs_common.h"
64 #include "inputdialog.h"
66 #include "remotefolder.h"
68 #include "statusbar.h"
70 #include "imap-thread.h"
73 typedef struct _IMAPFolder IMAPFolder;
74 typedef struct _IMAPSession IMAPSession;
75 typedef struct _IMAPNameSpace IMAPNameSpace;
76 typedef struct _IMAPFolderItem IMAPFolderItem;
78 #include "prefs_account.h"
80 #define IMAP_FOLDER(obj) ((IMAPFolder *)obj)
81 #define IMAP_FOLDER_ITEM(obj) ((IMAPFolderItem *)obj)
82 #define IMAP_SESSION(obj) ((IMAPSession *)obj)
88 /* list of IMAPNameSpace */
92 gchar last_seen_separator;
100 gboolean authenticated;
109 gboolean folder_content_changed;
116 struct _IMAPNameSpace
122 #define IMAP_SUCCESS 0
123 #define IMAP_SOCKET 2
124 #define IMAP_AUTHFAIL 3
125 #define IMAP_PROTOCOL 4
126 #define IMAP_SYNTAX 5
130 #define IMAPBUFSIZE 8192
134 IMAP_FLAG_SEEN = 1 << 0,
135 IMAP_FLAG_ANSWERED = 1 << 1,
136 IMAP_FLAG_FLAGGED = 1 << 2,
137 IMAP_FLAG_DELETED = 1 << 3,
138 IMAP_FLAG_DRAFT = 1 << 4
141 #define IMAP_IS_SEEN(flags) ((flags & IMAP_FLAG_SEEN) != 0)
142 #define IMAP_IS_ANSWERED(flags) ((flags & IMAP_FLAG_ANSWERED) != 0)
143 #define IMAP_IS_FLAGGED(flags) ((flags & IMAP_FLAG_FLAGGED) != 0)
144 #define IMAP_IS_DELETED(flags) ((flags & IMAP_FLAG_DELETED) != 0)
145 #define IMAP_IS_DRAFT(flags) ((flags & IMAP_FLAG_DRAFT) != 0)
148 #define IMAP4_PORT 143
150 #define IMAPS_PORT 993
153 #define IMAP_CMD_LIMIT 1000
155 struct _IMAPFolderItem
167 guint32 c_uid_validity;
170 GHashTable *flags_set_table;
171 GHashTable *flags_unset_table;
176 static XMLTag *imap_item_get_xml(Folder *folder, FolderItem *item);
177 static void imap_item_set_xml(Folder *folder, FolderItem *item, XMLTag *tag);
179 static void imap_folder_init (Folder *folder,
183 static Folder *imap_folder_new (const gchar *name,
185 static void imap_folder_destroy (Folder *folder);
187 static IMAPSession *imap_session_new (Folder *folder,
188 const PrefsAccount *account);
189 static void imap_session_authenticate(IMAPSession *session,
190 const PrefsAccount *account);
191 static void imap_session_destroy (Session *session);
193 static gchar *imap_fetch_msg (Folder *folder,
196 static gchar *imap_fetch_msg_full (Folder *folder,
201 static void imap_remove_cached_msg (Folder *folder,
204 static gint imap_add_msg (Folder *folder,
208 static gint imap_add_msgs (Folder *folder,
211 GRelation *relation);
213 static gint imap_copy_msg (Folder *folder,
216 static gint imap_copy_msgs (Folder *folder,
218 MsgInfoList *msglist,
219 GRelation *relation);
221 static gint imap_remove_msg (Folder *folder,
224 static gint imap_remove_msgs (Folder *folder,
226 MsgInfoList *msglist,
227 GRelation *relation);
228 static gint imap_remove_all_msg (Folder *folder,
231 static gboolean imap_is_msg_changed (Folder *folder,
235 static gint imap_close (Folder *folder,
238 static gint imap_scan_tree (Folder *folder);
240 static gint imap_create_tree (Folder *folder);
242 static FolderItem *imap_create_folder (Folder *folder,
245 static gint imap_rename_folder (Folder *folder,
248 static gint imap_remove_folder (Folder *folder,
251 static FolderItem *imap_folder_item_new (Folder *folder);
252 static void imap_folder_item_destroy (Folder *folder,
255 static IMAPSession *imap_session_get (Folder *folder);
257 static gint imap_auth (IMAPSession *session,
262 static gint imap_scan_tree_recursive (IMAPSession *session,
266 static void imap_create_missing_folders (Folder *folder);
267 static FolderItem *imap_create_special_folder
269 SpecialFolderItemType stype,
272 static gint imap_do_copy_msgs (Folder *folder,
274 MsgInfoList *msglist,
275 GRelation *relation);
277 static void imap_delete_all_cached_messages (FolderItem *item);
278 static void imap_set_batch (Folder *folder,
281 static gint imap_set_message_flags (IMAPSession *session,
282 MsgNumberList *numlist,
285 static gint imap_select (IMAPSession *session,
291 guint32 *uid_validity,
293 static gint imap_status (IMAPSession *session,
296 IMAPFolderItem *item,
299 guint32 *uid_validity,
303 static gchar imap_get_path_separator (IMAPSession *session,
306 static gchar *imap_get_real_path (IMAPSession *session,
309 static void imap_synchronise (FolderItem *item, gint days);
310 static gboolean imap_is_busy (Folder *folder);
312 static void imap_free_capabilities (IMAPSession *session);
314 /* low-level IMAP4rev1 commands */
315 static gint imap_cmd_login (IMAPSession *session,
319 static gint imap_cmd_noop (IMAPSession *session);
321 static gint imap_cmd_starttls (IMAPSession *session);
323 static gint imap_cmd_select (IMAPSession *session,
328 guint32 *uid_validity,
330 static gint imap_cmd_examine (IMAPSession *session,
335 guint32 *uid_validity,
337 static gint imap_cmd_create (IMAPSession *sock,
338 const gchar *folder);
339 static gint imap_cmd_rename (IMAPSession *sock,
340 const gchar *oldfolder,
341 const gchar *newfolder);
342 static gint imap_cmd_delete (IMAPSession *session,
343 const gchar *folder);
344 static gint imap_cmd_fetch (IMAPSession *sock,
346 const gchar *filename,
349 static gint imap_cmd_append (IMAPSession *session,
350 const gchar *destfolder,
354 static gint imap_cmd_copy (IMAPSession *session,
355 struct mailimap_set * set,
356 const gchar *destfolder,
357 GRelation *uid_mapping,
358 struct mailimap_set ** source,
359 struct mailimap_set ** dest);
360 static gint imap_cmd_store (IMAPSession *session,
361 struct mailimap_set * set,
364 static gint imap_cmd_expunge (IMAPSession *session);
366 static void imap_path_separator_subst (gchar *str,
369 static gchar *imap_utf8_to_modified_utf7 (const gchar *from);
370 static gchar *imap_modified_utf7_to_utf8 (const gchar *mutf7_str);
372 static gboolean imap_rename_folder_func (GNode *node,
374 static gint imap_get_num_list (Folder *folder,
377 gboolean *old_uids_valid);
378 static GSList *imap_get_msginfos (Folder *folder,
380 GSList *msgnum_list);
381 static MsgInfo *imap_get_msginfo (Folder *folder,
384 static gboolean imap_scan_required (Folder *folder,
386 static void imap_change_flags (Folder *folder,
389 MsgPermFlags newflags);
390 static gint imap_get_flags (Folder *folder,
392 MsgInfoList *msglist,
393 GRelation *msgflags);
394 static gchar *imap_folder_get_path (Folder *folder);
395 static gchar *imap_item_get_path (Folder *folder,
397 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item);
400 /* data types conversion libetpan <-> claws */
401 static GSList * imap_list_from_lep(IMAPFolder * folder,
402 clist * list, const gchar * real_path, gboolean all);
403 static GSList * imap_get_lep_set_from_numlist(MsgNumberList *numlist);
404 static GSList * imap_get_lep_set_from_msglist(MsgInfoList *msglist);
405 static GSList * imap_uid_list_from_lep(clist * list);
406 static GSList * imap_uid_list_from_lep_tab(carray * list);
407 static void imap_flags_hash_from_lep_uid_flags_tab(carray * list,
409 static MsgInfo *imap_envelope_from_lep(struct imap_fetch_env_info * info,
411 static void imap_lep_set_free(GSList *seq_list);
412 static struct mailimap_flag_list * imap_flag_to_lep(IMAPFlags flags);
414 typedef struct _hashtable_data {
415 IMAPSession *session;
417 IMAPFolderItem *item;
420 static FolderClass imap_class;
422 typedef struct _thread_data {
432 FolderClass *imap_get_class(void)
434 if (imap_class.idstr == NULL) {
435 imap_class.type = F_IMAP;
436 imap_class.idstr = "imap";
437 imap_class.uistr = "IMAP4";
439 /* Folder functions */
440 imap_class.new_folder = imap_folder_new;
441 imap_class.destroy_folder = imap_folder_destroy;
442 imap_class.scan_tree = imap_scan_tree;
443 imap_class.create_tree = imap_create_tree;
445 /* FolderItem functions */
446 imap_class.item_new = imap_folder_item_new;
447 imap_class.item_destroy = imap_folder_item_destroy;
448 imap_class.item_get_path = imap_item_get_path;
449 imap_class.create_folder = imap_create_folder;
450 imap_class.rename_folder = imap_rename_folder;
451 imap_class.remove_folder = imap_remove_folder;
452 imap_class.close = imap_close;
453 imap_class.get_num_list = imap_get_num_list;
454 imap_class.scan_required = imap_scan_required;
455 imap_class.set_xml = folder_set_xml;
456 imap_class.get_xml = folder_get_xml;
457 imap_class.item_set_xml = imap_item_set_xml;
458 imap_class.item_get_xml = imap_item_get_xml;
460 /* Message functions */
461 imap_class.get_msginfo = imap_get_msginfo;
462 imap_class.get_msginfos = imap_get_msginfos;
463 imap_class.fetch_msg = imap_fetch_msg;
464 imap_class.fetch_msg_full = imap_fetch_msg_full;
465 imap_class.add_msg = imap_add_msg;
466 imap_class.add_msgs = imap_add_msgs;
467 imap_class.copy_msg = imap_copy_msg;
468 imap_class.copy_msgs = imap_copy_msgs;
469 imap_class.remove_msg = imap_remove_msg;
470 imap_class.remove_msgs = imap_remove_msgs;
471 imap_class.remove_all_msg = imap_remove_all_msg;
472 imap_class.is_msg_changed = imap_is_msg_changed;
473 imap_class.change_flags = imap_change_flags;
474 imap_class.get_flags = imap_get_flags;
475 imap_class.set_batch = imap_set_batch;
476 imap_class.synchronise = imap_synchronise;
477 imap_class.remove_cached_msg = imap_remove_cached_msg;
479 pthread_mutex_init(&imap_mutex, NULL);
486 static Folder *imap_folder_new(const gchar *name, const gchar *path)
490 folder = (Folder *)g_new0(IMAPFolder, 1);
491 folder->klass = &imap_class;
492 imap_folder_init(folder, name, path);
497 static void imap_folder_destroy(Folder *folder)
499 while (imap_folder_get_refcnt(folder) > 0)
500 gtk_main_iteration();
502 folder_remote_folder_destroy(REMOTE_FOLDER(folder));
506 static void imap_folder_init(Folder *folder, const gchar *name,
509 folder_remote_folder_init((Folder *)folder, name, path);
512 static FolderItem *imap_folder_item_new(Folder *folder)
514 IMAPFolderItem *item;
516 item = g_new0(IMAPFolderItem, 1);
519 item->uid_list = NULL;
521 return (FolderItem *)item;
524 static void imap_folder_item_destroy(Folder *folder, FolderItem *_item)
526 IMAPFolderItem *item = (IMAPFolderItem *)_item;
528 g_return_if_fail(item != NULL);
529 g_slist_free(item->uid_list);
534 static gboolean imap_reset_uid_lists_func(GNode *node, gpointer data)
536 IMAPFolderItem *item = (IMAPFolderItem *)node->data;
539 g_slist_free(item->uid_list);
540 item->uid_list = NULL;
545 static void imap_reset_uid_lists(Folder *folder)
547 if(folder->node == NULL)
550 /* Destroy all uid lists and rest last uid */
551 g_node_traverse(folder->node, G_IN_ORDER, G_TRAVERSE_ALL, -1, imap_reset_uid_lists_func, NULL);
554 static int imap_get_capabilities(IMAPSession *session)
556 struct mailimap_capability_data *capabilities = NULL;
560 if (session->capability != NULL)
561 return MAILIMAP_NO_ERROR;
563 capabilities = imap_threaded_capability(session->folder, &result);
565 if (result != MAILIMAP_NO_ERROR) {
566 return MAILIMAP_ERROR_CAPABILITY;
569 if (capabilities == NULL) {
570 return MAILIMAP_NO_ERROR;
573 for(cur = clist_begin(capabilities->cap_list) ; cur != NULL ;
574 cur = clist_next(cur)) {
575 struct mailimap_capability * cap =
577 if (!cap || cap->cap_data.cap_name == NULL)
579 session->capability = g_slist_append
580 (session->capability,
581 g_strdup(cap->cap_data.cap_name));
582 debug_print("got capa %s\n", cap->cap_data.cap_name);
584 mailimap_capability_data_free(capabilities);
585 return MAILIMAP_NO_ERROR;
588 static gboolean imap_has_capability(IMAPSession *session, const gchar *cap)
591 for (cur = session->capability; cur; cur = cur->next) {
592 if (!g_ascii_strcasecmp(cur->data, cap))
598 static gint imap_auth(IMAPSession *session, const gchar *user, const gchar *pass,
601 gint ok = IMAP_ERROR;
602 static time_t last_login_err = 0;
603 gchar *ext_info = "";
605 if (imap_get_capabilities(session) != MAILIMAP_NO_ERROR)
610 ok = imap_cmd_login(session, user, pass, "ANONYMOUS");
612 case IMAP_AUTH_CRAM_MD5:
613 ok = imap_cmd_login(session, user, pass, "CRAM-MD5");
615 case IMAP_AUTH_LOGIN:
616 ok = imap_cmd_login(session, user, pass, "LOGIN");
618 case IMAP_AUTH_GSSAPI:
619 ok = imap_cmd_login(session, user, pass, "GSSAPI");
622 debug_print("capabilities:\n"
627 imap_has_capability(session, "ANONYMOUS"),
628 imap_has_capability(session, "CRAM-MD5"),
629 imap_has_capability(session, "LOGIN"),
630 imap_has_capability(session, "GSSAPI"));
631 if (imap_has_capability(session, "CRAM-MD5"))
632 ok = imap_cmd_login(session, user, pass, "CRAM-MD5");
633 if (ok == IMAP_ERROR && imap_has_capability(session, "GSSAPI"))
634 ok = imap_cmd_login(session, user, pass, "GSSAPI");
635 if (ok == IMAP_ERROR) /* we always try LOGIN before giving up */
636 ok = imap_cmd_login(session, user, pass, "LOGIN");
639 if (ok == IMAP_SUCCESS)
640 session->authenticated = TRUE;
642 if (type == IMAP_AUTH_CRAM_MD5) {
643 ext_info = _("\n\nCRAM-MD5 logins only work if libetpan has been "
644 "compiled with SASL support and the "
645 "CRAM-MD5 SASL plugin is installed.");
648 if (time(NULL) - last_login_err > 10) {
649 if (!prefs_common.no_recv_err_panel) {
650 alertpanel_error(_("Connection to %s failed: "
652 SESSION(session)->server, ext_info);
654 log_error(LOG_PROTOCOL, _("Connection to %s failed: "
655 "login refused.%s\n"),
656 SESSION(session)->server, ext_info);
659 last_login_err = time(NULL);
664 static IMAPSession *imap_reconnect_if_possible(Folder *folder, IMAPSession *session)
666 RemoteFolder *rfolder = REMOTE_FOLDER(folder);
667 /* Check if this is the first try to establish a
668 connection, if yes we don't try to reconnect */
669 debug_print("reconnecting\n");
670 if (rfolder->session == NULL) {
671 log_warning(LOG_PROTOCOL, _("Connecting to %s failed"),
672 folder->account->recv_server);
673 session_destroy(SESSION(session));
676 log_warning(LOG_PROTOCOL, _("IMAP4 connection to %s has been"
677 " disconnected. Reconnecting...\n"),
678 folder->account->recv_server);
679 statusbar_print_all(_("IMAP4 connection to %s has been"
680 " disconnected. Reconnecting...\n"),
681 folder->account->recv_server);
682 SESSION(session)->state = SESSION_DISCONNECTED;
683 session_destroy(SESSION(session));
684 /* Clear folders session to make imap_session_get create
685 a new session, because of rfolder->session == NULL
686 it will not try to reconnect again and so avoid an
688 rfolder->session = NULL;
689 debug_print("getting session...\n");
690 session = imap_session_get(folder);
691 rfolder->session = SESSION(session);
697 static void lock_session(IMAPSession *session)
702 debug_print("locking session %p (%d)\n", session, session->busy);
704 debug_print(" SESSION WAS LOCKED !! \n");
705 session->busy = TRUE;
706 mainwin = mainwindow_get_mainwindow();
708 toolbar_main_set_sensitive(mainwin);
709 main_window_set_menu_sensitive(mainwin);
712 debug_print("can't lock null session\n");
716 static void unlock_session(IMAPSession *session)
721 debug_print("unlocking session %p\n", session);
722 session->busy = FALSE;
723 mainwin = mainwindow_get_mainwindow();
725 toolbar_main_set_sensitive(mainwin);
726 main_window_set_menu_sensitive(mainwin);
729 debug_print("can't unlock null session\n");
733 static IMAPSession *imap_session_get(Folder *folder)
735 RemoteFolder *rfolder = REMOTE_FOLDER(folder);
736 IMAPSession *session = NULL;
738 g_return_val_if_fail(folder != NULL, NULL);
739 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, NULL);
740 g_return_val_if_fail(folder->account != NULL, NULL);
742 if (prefs_common.work_offline &&
743 !inc_offline_should_override(FALSE,
744 _("Claws Mail needs network access in order "
745 "to access the IMAP server."))) {
749 /* Make sure we have a session */
750 if (rfolder->session != NULL) {
751 session = IMAP_SESSION(rfolder->session);
752 } else if (rfolder->connecting) {
753 debug_print("already connecting\n");
756 imap_reset_uid_lists(folder);
757 if (time(NULL) - rfolder->last_failure <= 2)
759 rfolder->connecting = TRUE;
760 session = imap_session_new(folder, folder->account);
762 if(session == NULL) {
763 rfolder->last_failure = time(NULL);
764 rfolder->connecting = FALSE;
768 /* Make sure session is authenticated */
769 if (!IMAP_SESSION(session)->authenticated)
770 imap_session_authenticate(IMAP_SESSION(session), folder->account);
772 if (!IMAP_SESSION(session)->authenticated) {
773 imap_threaded_disconnect(session->folder);
774 SESSION(session)->state = SESSION_DISCONNECTED;
775 session_destroy(SESSION(session));
776 rfolder->session = NULL;
777 rfolder->last_failure = time(NULL);
778 rfolder->connecting = FALSE;
782 lock_session(session);
784 /* I think the point of this code is to avoid sending a
785 * keepalive if we've used the session recently and therefore
786 * think it's still alive. Unfortunately, most of the code
787 * does not yet check for errors on the socket, and so if the
788 * connection drops we don't notice until the timeout expires.
789 * A better solution than sending a NOOP every time would be
790 * for every command to be prepared to retry until it is
791 * successfully sent. -- mbp */
792 if ((time(NULL) - SESSION(session)->last_access_time > SESSION_TIMEOUT_INTERVAL) || session->cancelled) {
793 /* verify that the session is still alive */
794 if (imap_cmd_noop(session) != IMAP_SUCCESS) {
795 debug_print("disconnected!\n");
796 session = imap_reconnect_if_possible(folder, session);
799 session->cancelled = FALSE;
802 rfolder->session = SESSION(session);
803 rfolder->connecting = FALSE;
805 return IMAP_SESSION(session);
808 static IMAPSession *imap_session_new(Folder * folder,
809 const PrefsAccount *account)
811 IMAPSession *session;
814 int authenticated = FALSE;
817 /* FIXME: IMAP over SSL only... */
820 port = account->set_imapport ? account->imapport
821 : account->ssl_imap == SSL_TUNNEL ? IMAPS_PORT : IMAP4_PORT;
822 ssl_type = account->ssl_imap;
824 if (account->ssl_imap != SSL_NONE) {
825 if (alertpanel_full(_("Insecure connection"),
826 _("This connection is configured to be secured "
827 "using SSL, but SSL is not available in this "
828 "build of Claws Mail. \n\n"
829 "Do you want to continue connecting to this "
830 "server? The communication would not be "
832 GTK_STOCK_CANCEL, _("Con_tinue connecting"),
833 NULL, FALSE, NULL, ALERT_WARNING,
834 G_ALERTDEFAULT) != G_ALERTALTERNATE)
837 port = account->set_imapport ? account->imapport
842 statusbar_print_all(_("Connecting to IMAP4 server: %s..."), folder->account->recv_server);
843 if (account->set_tunnelcmd) {
844 r = imap_threaded_connect_cmd(folder,
846 account->recv_server,
851 if (ssl_type == SSL_TUNNEL) {
852 r = imap_threaded_connect_ssl(folder,
853 account->recv_server,
859 r = imap_threaded_connect(folder,
860 account->recv_server,
866 if (r == MAILIMAP_NO_ERROR_AUTHENTICATED) {
867 authenticated = TRUE;
869 else if (r == MAILIMAP_NO_ERROR_NON_AUTHENTICATED) {
870 authenticated = FALSE;
873 #if (LIBETPAN_VERSION_MAJOR > 0 || LIBETPAN_VERSION_MINOR > 48)
875 if (r == MAILIMAP_ERROR_SSL)
876 log_error(LOG_PROTOCOL, _("SSL handshake failed\n"));
879 if(!prefs_common.no_recv_err_panel) {
880 alertpanel_error_log(_("Can't connect to IMAP4 server: %s:%d"),
881 account->recv_server, port);
883 log_error(LOG_PROTOCOL, _("Can't connect to IMAP4 server: %s:%d\n"),
884 account->recv_server, port);
890 session = g_new0(IMAPSession, 1);
891 session_init(SESSION(session));
892 SESSION(session)->type = SESSION_IMAP;
893 SESSION(session)->server = g_strdup(account->recv_server);
894 SESSION(session)->sock = NULL;
896 SESSION(session)->destroy = imap_session_destroy;
898 session->capability = NULL;
900 session->authenticated = authenticated;
901 session->mbox = NULL;
902 session->cmd_count = 0;
903 session->folder = folder;
904 IMAP_FOLDER(session->folder)->last_seen_separator = 0;
907 if (account->ssl_imap == SSL_STARTTLS) {
910 ok = imap_cmd_starttls(session);
911 if (ok != IMAP_SUCCESS) {
912 log_warning(LOG_PROTOCOL, _("Can't start TLS session.\n"));
913 session_destroy(SESSION(session));
917 imap_free_capabilities(session);
918 session->authenticated = FALSE;
919 session->uidplus = FALSE;
920 session->cmd_count = 1;
923 log_message(LOG_PROTOCOL, "IMAP connection is %s-authenticated\n",
924 (session->authenticated) ? "pre" : "un");
929 static void imap_session_authenticate(IMAPSession *session,
930 const PrefsAccount *account)
932 gchar *pass, *acc_pass;
933 gboolean failed = FALSE;
935 g_return_if_fail(account->userid != NULL);
936 acc_pass = account->passwd;
939 if (!pass && account->imap_auth_type != IMAP_AUTH_ANON) {
941 tmp_pass = input_dialog_query_password(account->recv_server, account->userid);
944 Xstrdup_a(pass, tmp_pass, {g_free(tmp_pass); return;});
946 } else if (account->imap_auth_type == IMAP_AUTH_ANON) {
949 statusbar_print_all(_("Connecting to IMAP4 server %s...\n"),
950 account->recv_server);
951 if (imap_auth(session, account->userid, pass, account->imap_auth_type) != IMAP_SUCCESS) {
959 if (prefs_common.no_recv_err_panel) {
960 log_error(LOG_PROTOCOL, _("Couldn't login to IMAP server %s."), account->recv_server);
961 mainwindow_show_error();
963 alertpanel_error_log(_("Couldn't login to IMAP server %s."), account->recv_server);
970 session->authenticated = TRUE;
974 static void imap_session_destroy(Session *session)
976 if (session->state != SESSION_DISCONNECTED)
977 imap_threaded_disconnect(IMAP_SESSION(session)->folder);
979 imap_free_capabilities(IMAP_SESSION(session));
980 g_free(IMAP_SESSION(session)->mbox);
981 sock_close(session->sock);
982 session->sock = NULL;
985 static gchar *imap_fetch_msg(Folder *folder, FolderItem *item, gint uid)
987 return imap_fetch_msg_full(folder, item, uid, TRUE, TRUE);
990 static guint get_file_size_with_crs(const gchar *filename)
996 if (filename == NULL)
999 fp = fopen(filename, "rb");
1003 while (fgets(buf, sizeof (buf), fp) != NULL) {
1005 if (!strstr(buf, "\r\n") && strstr(buf, "\n"))
1013 static void imap_remove_cached_msg(Folder *folder, FolderItem *item, MsgInfo *msginfo)
1015 gchar *path, *filename;
1017 path = folder_item_get_path(item);
1019 if (!is_dir_exist(path)) {
1024 filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(msginfo->msgnum), NULL);
1027 if (is_file_exist(filename)) {
1033 static gchar *imap_fetch_msg_full(Folder *folder, FolderItem *item, gint uid,
1034 gboolean headers, gboolean body)
1036 gchar *path, *filename;
1037 IMAPSession *session;
1040 g_return_val_if_fail(folder != NULL, NULL);
1041 g_return_val_if_fail(item != NULL, NULL);
1046 path = folder_item_get_path(item);
1047 if (!is_dir_exist(path))
1048 make_dir_hier(path);
1049 filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(uid), NULL);
1051 debug_print("trying to fetch cached %s\n", filename);
1052 if (is_file_exist(filename)) {
1053 /* see whether the local file represents the whole message
1054 * or not. As the IMAP server reports size with \r chars,
1055 * we have to update the local file (UNIX \n only) size */
1056 MsgInfo *cached = msgcache_get_msg(item->cache,uid);
1057 guint have_size = get_file_size_with_crs(filename);
1060 debug_print("message %d has been already %scached (%d/%d).\n", uid,
1061 have_size >= cached->size ? "fully ":"",
1062 have_size, (int)cached->size);
1064 if (cached && (cached->size <= have_size || !body)) {
1065 procmsg_msginfo_free(cached);
1066 file_strip_crs(filename);
1068 } else if (!cached && time(NULL) - get_file_mtime(filename) < 60) {
1069 debug_print("message not cached and file recent, considering file complete\n");
1070 file_strip_crs(filename);
1073 procmsg_msginfo_free(cached);
1077 debug_print("getting session...\n");
1078 session = imap_session_get(folder);
1085 debug_print("IMAP fetching messages\n");
1086 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
1087 NULL, NULL, NULL, NULL, FALSE);
1088 if (ok != IMAP_SUCCESS) {
1089 g_warning("can't select mailbox %s\n", item->path);
1091 unlock_session(session);
1095 debug_print("getting message %d...\n", uid);
1096 ok = imap_cmd_fetch(session, (guint32)uid, filename, headers, body);
1098 if (ok != IMAP_SUCCESS) {
1099 g_warning("can't fetch message %d\n", uid);
1101 unlock_session(session);
1105 unlock_session(session);
1106 file_strip_crs(filename);
1110 static gboolean imap_is_msg_fully_cached(Folder *folder, FolderItem *item, gint uid)
1112 gchar *path, *filename;
1114 MsgInfo *cached = msgcache_get_msg(item->cache,uid);
1119 path = folder_item_get_path(item);
1120 if (!is_dir_exist(path))
1123 filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(uid), NULL);
1125 if (is_file_exist(filename)) {
1126 if (cached && cached->total_size == cached->size) {
1131 size = get_file_size_with_crs(filename);
1134 if (cached && size >= cached->size) {
1135 cached->total_size = cached->size;
1136 procmsg_msginfo_free(cached);
1140 procmsg_msginfo_free(cached);
1144 void imap_cache_msg(FolderItem *item, gint msgnum)
1146 Folder *folder = NULL;
1150 folder = item->folder;
1152 if (!imap_is_msg_fully_cached(folder, item, msgnum)) {
1153 gchar *tmp = imap_fetch_msg_full(folder, item, msgnum, TRUE, TRUE);
1154 debug_print("fetched %s\n", tmp);
1159 static gint imap_add_msg(Folder *folder, FolderItem *dest,
1160 const gchar *file, MsgFlags *flags)
1164 MsgFileInfo fileinfo;
1166 g_return_val_if_fail(file != NULL, -1);
1168 fileinfo.msginfo = NULL;
1169 fileinfo.file = (gchar *)file;
1170 fileinfo.flags = flags;
1171 file_list.data = &fileinfo;
1172 file_list.next = NULL;
1174 ret = imap_add_msgs(folder, dest, &file_list, NULL);
1178 static gint imap_get_msg_from_local(Folder *folder, FolderItem *dest, const gchar *real_file)
1180 /* don't get session, already done. */
1182 MsgFlags flags = {0, 0};
1184 msginfo = procheader_parse_file(real_file, flags, FALSE, FALSE);
1186 if (msginfo && msginfo->msgid) {
1187 GSList *msglist = folder_item_get_msg_list(dest);
1191 /* gets last matching mail with msgid. Slower than by msgid but gets the
1192 * most recent one */
1193 for (cur = msglist ; cur != NULL ; cur = cur->next) {
1194 MsgInfo * r_msginfo;
1196 r_msginfo = (MsgInfo *) cur->data;
1198 if (r_msginfo->msgid && !strcmp(r_msginfo->msgid,msginfo->msgid)) {
1199 if (found_num < r_msginfo->msgnum) {
1200 found_num = r_msginfo->msgnum;
1203 procmsg_msginfo_free(r_msginfo);
1206 g_slist_free(msglist);
1209 procmsg_msginfo_free(msginfo);
1213 static gint imap_add_msgs(Folder *folder, FolderItem *dest, GSList *file_list,
1214 GRelation *relation)
1217 IMAPSession *session;
1218 guint32 last_uid = 0;
1220 MsgFileInfo *fileinfo;
1222 gint curnum = 0, total = 0;
1223 gboolean missing_uids = FALSE;
1225 g_return_val_if_fail(folder != NULL, -1);
1226 g_return_val_if_fail(dest != NULL, -1);
1227 g_return_val_if_fail(file_list != NULL, -1);
1229 debug_print("getting session...\n");
1230 session = imap_session_get(folder);
1234 destdir = imap_get_real_path(session, IMAP_FOLDER(folder), dest->path);
1236 statusbar_print_all(_("Adding messages..."));
1237 total = g_slist_length(file_list);
1238 for (cur = file_list; cur != NULL; cur = cur->next) {
1239 IMAPFlags iflags = 0;
1240 guint32 new_uid = 0;
1241 gchar *real_file = NULL;
1242 fileinfo = (MsgFileInfo *)cur->data;
1244 statusbar_progress_all(curnum, total, total < 10 ? 1:10);
1247 if (fileinfo->flags) {
1248 if (MSG_IS_MARKED(*fileinfo->flags))
1249 iflags |= IMAP_FLAG_FLAGGED;
1250 if (MSG_IS_REPLIED(*fileinfo->flags))
1251 iflags |= IMAP_FLAG_ANSWERED;
1252 if (!MSG_IS_UNREAD(*fileinfo->flags))
1253 iflags |= IMAP_FLAG_SEEN;
1256 if (real_file == NULL)
1257 real_file = g_strdup(fileinfo->file);
1259 if (folder_has_parent_of_type(dest, F_QUEUE) ||
1260 folder_has_parent_of_type(dest, F_OUTBOX) ||
1261 folder_has_parent_of_type(dest, F_DRAFT) ||
1262 folder_has_parent_of_type(dest, F_TRASH))
1263 iflags |= IMAP_FLAG_SEEN;
1265 ok = imap_cmd_append(session, destdir, real_file, iflags,
1268 if (ok != IMAP_SUCCESS) {
1269 g_warning("can't append message %s\n", real_file);
1272 unlock_session(session);
1273 statusbar_progress_all(0,0,0);
1274 statusbar_pop_all();
1277 debug_print("appended new message as %d\n", new_uid);
1278 /* put the local file in the imapcache, so that we don't
1279 * have to fetch it back later. */
1282 missing_uids = TRUE;
1283 debug_print("Missing UID (0)\n");
1286 gchar *cache_path = folder_item_get_path(dest);
1287 if (!is_dir_exist(cache_path))
1288 make_dir_hier(cache_path);
1289 if (is_dir_exist(cache_path)) {
1290 gchar *cache_file = g_strconcat(
1291 cache_path, G_DIR_SEPARATOR_S,
1292 itos(new_uid), NULL);
1293 copy_file(real_file, cache_file, TRUE);
1294 debug_print("got UID %d, copied to cache: %s\n", new_uid, cache_file);
1301 if (relation != NULL)
1302 g_relation_insert(relation, fileinfo->msginfo != NULL ?
1303 (gpointer) fileinfo->msginfo : (gpointer) fileinfo,
1304 GINT_TO_POINTER(new_uid));
1305 if (last_uid < new_uid) {
1313 unlock_session(IMAP_SESSION(REMOTE_FOLDER(folder)->session));
1314 folder_item_scan_full(dest, FALSE);
1315 lock_session(IMAP_SESSION(REMOTE_FOLDER(folder)->session));
1316 for (cur = file_list; cur != NULL; cur = cur->next) {
1317 guint32 new_uid = 0;
1318 fileinfo = (MsgFileInfo *)cur->data;
1320 if (!fileinfo->file)
1323 new_uid = imap_get_msg_from_local(folder, dest, fileinfo->file);
1324 debug_print("new uid %d from scanning\n", new_uid);
1326 gchar *cache_path = folder_item_get_path(dest);
1327 if (!is_dir_exist(cache_path))
1328 make_dir_hier(cache_path);
1329 if (is_dir_exist(cache_path)) {
1330 gchar *cache_file = g_strconcat(
1331 cache_path, G_DIR_SEPARATOR_S,
1332 itos(new_uid), NULL);
1333 copy_file(fileinfo->file, cache_file, TRUE);
1334 debug_print("copied to cache: %s\n", cache_file);
1338 g_relation_delete(relation, fileinfo->msginfo != NULL ?
1339 (gpointer) fileinfo->msginfo : (gpointer) fileinfo,
1342 g_relation_insert(relation, fileinfo->msginfo != NULL ?
1343 (gpointer) fileinfo->msginfo : (gpointer) fileinfo,
1344 GINT_TO_POINTER(new_uid));
1346 if (last_uid < new_uid) {
1351 statusbar_progress_all(0,0,0);
1352 statusbar_pop_all();
1354 unlock_session(session);
1361 static GSList *flatten_mailimap_set(struct mailimap_set * set)
1363 GSList *result = NULL;
1368 for (list = clist_begin(set->set_list); list; list = clist_next(list)) {
1369 struct mailimap_set_item *item = (struct mailimap_set_item *)clist_content(list);
1370 start = item->set_first;
1371 end = item->set_last;
1372 for (t = start; t <= end; t++) {
1373 result = g_slist_prepend(result, GINT_TO_POINTER(t));
1376 result = g_slist_reverse(result);
1377 if (debug_get_mode()) {
1378 debug_print("flat imap set: ");
1379 for (cur = result; cur; cur = cur->next) {
1380 debug_print("%d ", GPOINTER_TO_INT(cur->data));
1387 static gint imap_do_copy_msgs(Folder *folder, FolderItem *dest,
1388 MsgInfoList *msglist, GRelation *relation)
1392 GSList *seq_list, *cur;
1394 IMAPSession *session;
1395 gint ok = IMAP_SUCCESS;
1396 GRelation *uid_mapping;
1398 gboolean single = FALSE;
1400 g_return_val_if_fail(folder != NULL, -1);
1401 g_return_val_if_fail(dest != NULL, -1);
1402 g_return_val_if_fail(msglist != NULL, -1);
1404 debug_print("getting session...\n");
1405 session = imap_session_get(folder);
1411 msginfo = (MsgInfo *)msglist->data;
1412 if (msglist->next == NULL)
1414 src = msginfo->folder;
1416 g_warning("the src folder is identical to the dest.\n");
1417 unlock_session(session);
1421 if (src->folder != dest->folder) {
1422 GSList *infolist = NULL, *cur;
1424 for (cur = msglist; cur; cur = cur->next) {
1425 msginfo = (MsgInfo *)cur->data;
1426 MsgFileInfo *fileinfo = g_new0(MsgFileInfo, 1);
1427 fileinfo->file = procmsg_get_message_file(msginfo);
1428 fileinfo->flags = &(msginfo->flags);
1429 infolist = g_slist_prepend(infolist, fileinfo);
1431 infolist = g_slist_reverse(infolist);
1432 unlock_session(session);
1433 res = folder_item_add_msgs(dest, infolist, FALSE);
1434 for (cur = infolist; cur; cur = cur->next) {
1435 MsgFileInfo *info = (MsgFileInfo *)cur->data;
1439 g_slist_free(infolist);
1443 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
1444 NULL, NULL, NULL, NULL, FALSE);
1445 if (ok != IMAP_SUCCESS) {
1446 unlock_session(session);
1450 destdir = imap_get_real_path(session, IMAP_FOLDER(folder), dest->path);
1451 seq_list = imap_get_lep_set_from_msglist(msglist);
1452 uid_mapping = g_relation_new(2);
1453 g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
1455 statusbar_print_all(_("Copying messages..."));
1456 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
1457 struct mailimap_set * seq_set;
1458 struct mailimap_set * source = NULL;
1459 struct mailimap_set * dest = NULL;
1460 seq_set = cur->data;
1462 debug_print("Copying messages from %s to %s ...\n",
1463 src->path, destdir);
1465 ok = imap_cmd_copy(session, seq_set, destdir, uid_mapping,
1468 if (ok == IMAP_SUCCESS) {
1469 if (relation && source && dest) {
1470 GSList *s_list = flatten_mailimap_set(source);
1471 GSList *d_list = flatten_mailimap_set(dest);
1472 GSList *s_cur, *d_cur;
1473 if (g_slist_length(s_list) == g_slist_length(d_list)) {
1475 for (s_cur = s_list, d_cur = d_list;
1477 s_cur = s_cur->next, d_cur = d_cur->next) {
1478 g_relation_insert(uid_mapping, s_cur->data, d_cur->data);
1482 debug_print("hhhmm, source list length != dest list length.\n");
1484 g_slist_free(s_list);
1485 g_slist_free(d_list);
1491 mailimap_set_free(source);
1493 mailimap_set_free(dest);
1495 if (ok != IMAP_SUCCESS) {
1496 g_relation_destroy(uid_mapping);
1497 imap_lep_set_free(seq_list);
1498 unlock_session(session);
1499 statusbar_pop_all();
1504 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
1505 MsgInfo *msginfo = (MsgInfo *)cur->data;
1508 tuples = g_relation_select(uid_mapping,
1509 GINT_TO_POINTER(msginfo->msgnum),
1511 if (tuples->len > 0) {
1512 gint num = GPOINTER_TO_INT(g_tuples_index(tuples, 0, 1));
1513 g_relation_insert(relation, msginfo,
1514 GINT_TO_POINTER(num));
1517 debug_print("copied message %d as %d\n", msginfo->msgnum, num);
1518 /* put the local file in the imapcache, so that we don't
1519 * have to fetch it back later. */
1521 gchar *cache_path = folder_item_get_path(msginfo->folder);
1522 gchar *real_file = g_strconcat(
1523 cache_path, G_DIR_SEPARATOR_S,
1524 itos(msginfo->msgnum), NULL);
1525 gchar *cache_file = NULL;
1527 cache_path = folder_item_get_path(dest);
1528 cache_file = g_strconcat(
1529 cache_path, G_DIR_SEPARATOR_S,
1531 if (!is_dir_exist(cache_path))
1532 make_dir_hier(cache_path);
1533 if (is_file_exist(real_file) && is_dir_exist(cache_path)) {
1534 copy_file(real_file, cache_file, TRUE);
1535 debug_print("copied to cache: %s\n", cache_file);
1542 g_relation_insert(relation, msginfo,
1543 GINT_TO_POINTER(0));
1544 g_tuples_destroy(tuples);
1546 statusbar_pop_all();
1548 g_relation_destroy(uid_mapping);
1549 imap_lep_set_free(seq_list);
1553 IMAP_FOLDER_ITEM(dest)->lastuid = 0;
1554 IMAP_FOLDER_ITEM(dest)->uid_next = 0;
1555 g_slist_free(IMAP_FOLDER_ITEM(dest)->uid_list);
1556 IMAP_FOLDER_ITEM(dest)->uid_list = NULL;
1558 unlock_session(session);
1559 if (ok == IMAP_SUCCESS)
1565 static gint imap_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1569 g_return_val_if_fail(msginfo != NULL, -1);
1571 msglist.data = msginfo;
1572 msglist.next = NULL;
1574 return imap_copy_msgs(folder, dest, &msglist, NULL);
1577 static gint imap_copy_msgs(Folder *folder, FolderItem *dest,
1578 MsgInfoList *msglist, GRelation *relation)
1583 g_return_val_if_fail(folder != NULL, -1);
1584 g_return_val_if_fail(dest != NULL, -1);
1585 g_return_val_if_fail(msglist != NULL, -1);
1587 msginfo = (MsgInfo *)msglist->data;
1588 g_return_val_if_fail(msginfo->folder != NULL, -1);
1590 ret = imap_do_copy_msgs(folder, dest, msglist, relation);
1595 static gint imap_do_remove_msgs(Folder *folder, FolderItem *dest,
1596 MsgInfoList *msglist, GRelation *relation)
1598 gchar *destdir, *dir;
1599 GSList *numlist = NULL, *cur;
1601 IMAPSession *session;
1602 gint ok = IMAP_SUCCESS;
1603 GRelation *uid_mapping;
1605 g_return_val_if_fail(folder != NULL, -1);
1606 g_return_val_if_fail(dest != NULL, -1);
1607 g_return_val_if_fail(msglist != NULL, -1);
1609 debug_print("getting session...\n");
1610 session = imap_session_get(folder);
1615 msginfo = (MsgInfo *)msglist->data;
1617 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
1618 NULL, NULL, NULL, NULL, FALSE);
1619 if (ok != IMAP_SUCCESS) {
1620 unlock_session(session);
1624 destdir = imap_get_real_path(session, IMAP_FOLDER(folder), dest->path);
1625 for (cur = msglist; cur; cur = cur->next) {
1626 msginfo = (MsgInfo *)cur->data;
1627 if (!MSG_IS_DELETED(msginfo->flags))
1628 numlist = g_slist_prepend(numlist, GINT_TO_POINTER(msginfo->msgnum));
1630 numlist = g_slist_reverse(numlist);
1632 uid_mapping = g_relation_new(2);
1633 g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
1635 ok = imap_set_message_flags
1636 (session, numlist, IMAP_FLAG_DELETED, TRUE);
1637 if (ok != IMAP_SUCCESS) {
1638 log_warning(LOG_PROTOCOL, _("can't set deleted flags\n"));
1639 unlock_session(session);
1642 ok = imap_cmd_expunge(session);
1643 if (ok != IMAP_SUCCESS) {
1644 log_warning(LOG_PROTOCOL, _("can't expunge\n"));
1645 unlock_session(session);
1649 dir = folder_item_get_path(msginfo->folder);
1650 if (is_dir_exist(dir)) {
1651 for (cur = msglist; cur; cur = cur->next) {
1652 msginfo = (MsgInfo *)cur->data;
1653 remove_numbered_files(dir, msginfo->msgnum, msginfo->msgnum);
1658 g_relation_destroy(uid_mapping);
1659 g_slist_free(numlist);
1662 unlock_session(session);
1663 if (ok == IMAP_SUCCESS)
1669 static gint imap_remove_msgs(Folder *folder, FolderItem *dest,
1670 MsgInfoList *msglist, GRelation *relation)
1674 g_return_val_if_fail(folder != NULL, -1);
1675 g_return_val_if_fail(dest != NULL, -1);
1676 if (msglist == NULL)
1679 msginfo = (MsgInfo *)msglist->data;
1680 g_return_val_if_fail(msginfo->folder != NULL, -1);
1682 return imap_do_remove_msgs(folder, dest, msglist, relation);
1685 static gint imap_remove_all_msg(Folder *folder, FolderItem *item)
1687 GSList *list = folder_item_get_msg_list(item);
1688 gint res = imap_remove_msgs(folder, item, list, NULL);
1689 procmsg_msg_list_free(list);
1693 static gboolean imap_is_msg_changed(Folder *folder, FolderItem *item,
1696 /* TODO: properly implement this method */
1700 static gint imap_close(Folder *folder, FolderItem *item)
1705 gint imap_scan_tree_real(Folder *folder, gboolean subs_only)
1707 FolderItem *item = NULL;
1708 IMAPSession *session;
1709 gchar *root_folder = NULL;
1711 g_return_val_if_fail(folder != NULL, -1);
1712 g_return_val_if_fail(folder->account != NULL, -1);
1714 debug_print("getting session...\n");
1715 session = imap_session_get(folder);
1717 if (!folder->node) {
1718 folder_tree_destroy(folder);
1719 item = folder_item_new(folder, folder->name, NULL);
1720 item->folder = folder;
1721 folder->node = item->node = g_node_new(item);
1726 if (folder->account->imap_dir && *folder->account->imap_dir) {
1731 Xstrdup_a(root_folder, folder->account->imap_dir, {unlock_session(session);return -1;});
1732 extract_quote(root_folder, '"');
1733 subst_char(root_folder,
1734 imap_get_path_separator(session, IMAP_FOLDER(folder),
1737 strtailchomp(root_folder, '/');
1738 real_path = imap_get_real_path
1739 (session, IMAP_FOLDER(folder), root_folder);
1740 debug_print("IMAP root directory: %s\n", real_path);
1742 /* check if root directory exist */
1744 r = imap_threaded_list(session->folder, "", real_path,
1746 if ((r != MAILIMAP_NO_ERROR) || (clist_count(lep_list) == 0)) {
1747 if (!folder->node) {
1748 item = folder_item_new(folder, folder->name, NULL);
1749 item->folder = folder;
1750 folder->node = item->node = g_node_new(item);
1752 unlock_session(session);
1755 mailimap_list_result_free(lep_list);
1761 item = FOLDER_ITEM(folder->node->data);
1763 if (item && !item->path && root_folder) {
1764 item->path = g_strdup(root_folder);
1767 if (!item || ((item->path || root_folder) &&
1768 strcmp2(item->path, root_folder) != 0)) {
1769 folder_tree_destroy(folder);
1770 item = folder_item_new(folder, folder->name, root_folder);
1771 item->folder = folder;
1772 folder->node = item->node = g_node_new(item);
1775 imap_scan_tree_recursive(session, FOLDER_ITEM(folder->node->data), subs_only);
1776 imap_create_missing_folders(folder);
1777 unlock_session(session);
1782 static gint imap_scan_tree(Folder *folder)
1784 gboolean subs_only = FALSE;
1785 if (folder->account) {
1786 debug_print(" scanning only subs %d\n", folder->account->imap_subsonly);
1787 subs_only = folder->account->imap_subsonly;
1789 return imap_scan_tree_real(folder, subs_only);
1792 static gint imap_scan_tree_recursive(IMAPSession *session, FolderItem *item, gboolean subs_only)
1795 IMAPFolder *imapfolder;
1796 FolderItem *new_item;
1797 GSList *item_list, *cur;
1800 gchar *wildcard_path;
1806 g_return_val_if_fail(item != NULL, -1);
1807 g_return_val_if_fail(item->folder != NULL, -1);
1808 g_return_val_if_fail(item->no_sub == FALSE, -1);
1810 folder = item->folder;
1811 imapfolder = IMAP_FOLDER(folder);
1813 separator = imap_get_path_separator(session, imapfolder, item->path);
1815 if (folder->ui_func)
1816 folder->ui_func(folder, item, folder->ui_func_data);
1819 wildcard[0] = separator;
1822 real_path = imap_get_real_path(session, imapfolder, item->path);
1826 real_path = g_strdup("");
1829 Xstrcat_a(wildcard_path, real_path, wildcard,
1830 {g_free(real_path); return IMAP_ERROR;});
1834 r = imap_threaded_lsub(folder, "", wildcard_path, &lep_list);
1836 r = imap_threaded_list(folder, "", wildcard_path, &lep_list);
1838 if (r != MAILIMAP_NO_ERROR) {
1842 item_list = imap_list_from_lep(imapfolder,
1843 lep_list, real_path, FALSE);
1844 mailimap_list_result_free(lep_list);
1849 node = item->node->children;
1850 while (node != NULL) {
1851 FolderItem *old_item = FOLDER_ITEM(node->data);
1852 GNode *next = node->next;
1855 for (cur = item_list; cur != NULL; cur = cur->next) {
1856 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1857 if (!strcmp2(old_item->path, cur_item->path)) {
1858 new_item = cur_item;
1863 if (old_item && old_item->path && !strcmp(old_item->path, "INBOX")) {
1864 debug_print("not removing INBOX\n");
1866 debug_print("folder '%s' not found. removing...\n",
1868 folder_item_remove(old_item);
1871 old_item->no_sub = new_item->no_sub;
1872 old_item->no_select = new_item->no_select;
1873 if (old_item->no_sub == TRUE && node->children) {
1874 debug_print("folder '%s' doesn't have "
1875 "subfolders. removing...\n",
1877 folder_item_remove_children(old_item);
1884 for (cur = item_list; cur != NULL; cur = cur->next) {
1885 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1888 for (node = item->node->children; node != NULL;
1889 node = node->next) {
1890 if (!strcmp2(FOLDER_ITEM(node->data)->path,
1892 new_item = FOLDER_ITEM(node->data);
1893 folder_item_destroy(cur_item);
1899 new_item = cur_item;
1900 debug_print("new folder '%s' found.\n", new_item->path);
1901 folder_item_append(item, new_item);
1904 if (!strcmp(new_item->path, "INBOX")) {
1905 new_item->stype = F_INBOX;
1906 folder->inbox = new_item;
1907 } else if (!folder_item_parent(item) || item->stype == F_INBOX) {
1910 base = g_path_get_basename(new_item->path);
1912 if (!folder->outbox && !g_ascii_strcasecmp(base, "Sent")) {
1913 new_item->stype = F_OUTBOX;
1914 folder->outbox = new_item;
1915 } else if (!folder->draft && !g_ascii_strcasecmp(base, "Drafts")) {
1916 new_item->stype = F_DRAFT;
1917 folder->draft = new_item;
1918 } else if (!folder->queue && !g_ascii_strcasecmp(base, "Queue")) {
1919 new_item->stype = F_QUEUE;
1920 folder->queue = new_item;
1921 } else if (!folder->trash && !g_ascii_strcasecmp(base, "Trash")) {
1922 new_item->stype = F_TRASH;
1923 folder->trash = new_item;
1928 if (new_item->no_sub == FALSE)
1929 imap_scan_tree_recursive(session, new_item, subs_only);
1932 g_slist_free(item_list);
1934 return IMAP_SUCCESS;
1937 GList *imap_scan_subtree(Folder *folder, FolderItem *item, gboolean unsubs_only, gboolean recursive)
1939 IMAPSession *session = imap_session_get(folder);
1941 gchar *wildcard_path;
1945 GSList *item_list = NULL, *cur;
1946 GList *child_list = NULL, *tmplist = NULL;
1947 GSList *sub_list = NULL;
1953 separator = imap_get_path_separator(session, IMAP_FOLDER(folder), item->path);
1956 wildcard[0] = separator;
1959 real_path = imap_get_real_path(session, IMAP_FOLDER(folder), item->path);
1963 real_path = g_strdup("");
1966 Xstrcat_a(wildcard_path, real_path, wildcard,
1967 {g_free(real_path); return NULL;});
1971 statusbar_print_all(_("Looking for unsubscribed folders in %s..."),
1972 item->path?item->path:item->name);
1974 statusbar_print_all(_("Looking for subfolders of %s..."),
1975 item->path?item->path:item->name);
1977 r = imap_threaded_list(folder, "", wildcard_path, &lep_list);
1979 statusbar_pop_all();
1982 item_list = imap_list_from_lep(IMAP_FOLDER(folder),
1983 lep_list, real_path, FALSE);
1984 mailimap_list_result_free(lep_list);
1986 for (cur = item_list; cur != NULL; cur = cur->next) {
1987 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1989 tmplist = imap_scan_subtree(folder, cur_item,
1990 unsubs_only, recursive);
1992 child_list = g_list_concat(child_list, tmplist);
1994 child_list = g_list_prepend(child_list,
1995 imap_get_real_path(session,
1996 IMAP_FOLDER(folder), cur_item->path));
1998 folder_item_destroy(cur_item);
2000 child_list = g_list_reverse(child_list);
2001 g_slist_free(item_list);
2004 r = imap_threaded_lsub(folder, "", wildcard_path, &lep_list);
2006 statusbar_pop_all();
2009 sub_list = imap_list_from_lep(IMAP_FOLDER(folder),
2010 lep_list, real_path, FALSE);
2011 mailimap_list_result_free(lep_list);
2013 for (cur = sub_list; cur != NULL; cur = cur->next) {
2014 FolderItem *cur_item = FOLDER_ITEM(cur->data);
2015 GList *oldlitem = NULL;
2016 gchar *tmp = imap_get_real_path(session,
2017 IMAP_FOLDER(folder), cur_item->path);
2018 folder_item_destroy(cur_item);
2019 oldlitem = g_list_find_custom(
2020 child_list, tmp, (GCompareFunc)strcmp2);
2022 child_list = g_list_remove_link(child_list, oldlitem);
2023 g_free(oldlitem->data);
2024 g_list_free(oldlitem);
2030 statusbar_pop_all();
2035 static gint imap_create_tree(Folder *folder)
2037 g_return_val_if_fail(folder != NULL, -1);
2038 g_return_val_if_fail(folder->node != NULL, -1);
2039 g_return_val_if_fail(folder->node->data != NULL, -1);
2040 g_return_val_if_fail(folder->account != NULL, -1);
2042 imap_scan_tree(folder);
2043 imap_create_missing_folders(folder);
2048 static void imap_create_missing_folders(Folder *folder)
2050 g_return_if_fail(folder != NULL);
2053 folder->inbox = imap_create_special_folder
2054 (folder, F_INBOX, "INBOX");
2056 folder->trash = imap_create_special_folder
2057 (folder, F_TRASH, "Trash");
2059 folder->queue = imap_create_special_folder
2060 (folder, F_QUEUE, "Queue");
2061 if (!folder->outbox)
2062 folder->outbox = imap_create_special_folder
2063 (folder, F_OUTBOX, "Sent");
2065 folder->draft = imap_create_special_folder
2066 (folder, F_DRAFT, "Drafts");
2069 static FolderItem *imap_create_special_folder(Folder *folder,
2070 SpecialFolderItemType stype,
2074 FolderItem *new_item;
2076 g_return_val_if_fail(folder != NULL, NULL);
2077 g_return_val_if_fail(folder->node != NULL, NULL);
2078 g_return_val_if_fail(folder->node->data != NULL, NULL);
2079 g_return_val_if_fail(folder->account != NULL, NULL);
2080 g_return_val_if_fail(name != NULL, NULL);
2082 item = FOLDER_ITEM(folder->node->data);
2083 new_item = imap_create_folder(folder, item, name);
2086 g_warning("Can't create '%s'\n", name);
2087 if (!folder->inbox) return NULL;
2089 new_item = imap_create_folder(folder, folder->inbox, name);
2091 g_warning("Can't create '%s' under INBOX\n", name);
2093 new_item->stype = stype;
2095 new_item->stype = stype;
2100 static gchar *imap_folder_get_path(Folder *folder)
2104 g_return_val_if_fail(folder != NULL, NULL);
2105 g_return_val_if_fail(folder->account != NULL, NULL);
2107 folder_path = g_strconcat(get_imap_cache_dir(),
2109 folder->account->recv_server,
2111 folder->account->userid,
2117 static gchar *imap_item_get_path(Folder *folder, FolderItem *item)
2119 gchar *folder_path, *path;
2121 g_return_val_if_fail(folder != NULL, NULL);
2122 g_return_val_if_fail(item != NULL, NULL);
2123 folder_path = imap_folder_get_path(folder);
2125 g_return_val_if_fail(folder_path != NULL, NULL);
2126 if (folder_path[0] == G_DIR_SEPARATOR) {
2128 path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
2131 path = g_strdup(folder_path);
2134 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
2135 folder_path, G_DIR_SEPARATOR_S,
2138 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
2141 g_free(folder_path);
2146 static FolderItem *imap_create_folder(Folder *folder, FolderItem *parent,
2149 gchar *dirpath, *imap_path;
2150 IMAPSession *session;
2151 FolderItem *new_item;
2156 gboolean no_select = FALSE, no_sub = FALSE;
2157 gboolean exist = FALSE;
2159 g_return_val_if_fail(folder != NULL, NULL);
2160 g_return_val_if_fail(folder->account != NULL, NULL);
2161 g_return_val_if_fail(parent != NULL, NULL);
2162 g_return_val_if_fail(name != NULL, NULL);
2164 debug_print("getting session...\n");
2165 session = imap_session_get(folder);
2170 if (!folder_item_parent(parent) && strcmp(name, "INBOX") == 0) {
2171 dirpath = g_strdup(name);
2172 }else if (parent->path)
2173 dirpath = g_strconcat(parent->path, "/", name, NULL);
2174 else if ((p = strchr(name, '/')) != NULL && *(p + 1) != '\0')
2175 dirpath = g_strdup(name);
2176 else if (folder->account->imap_dir && *folder->account->imap_dir) {
2179 Xstrdup_a(imap_dir, folder->account->imap_dir, {unlock_session(session);return NULL;});
2180 strtailchomp(imap_dir, '/');
2181 dirpath = g_strconcat(imap_dir, "/", name, NULL);
2183 dirpath = g_strdup(name);
2187 /* keep trailing directory separator to create a folder that contains
2189 imap_path = imap_utf8_to_modified_utf7(dirpath);
2191 strtailchomp(dirpath, '/');
2192 Xstrdup_a(new_name, name, {
2194 unlock_session(session);
2197 separator = imap_get_path_separator(session, IMAP_FOLDER(folder), imap_path);
2198 imap_path_separator_subst(imap_path, separator);
2199 /* remove trailing / for display */
2200 strtailchomp(new_name, '/');
2202 if (strcmp(dirpath, "INBOX") != 0) {
2207 argbuf = g_ptr_array_new();
2208 r = imap_threaded_list(folder, "", imap_path, &lep_list);
2209 if (r != MAILIMAP_NO_ERROR) {
2210 log_warning(LOG_PROTOCOL, _("can't create mailbox: LIST failed\n"));
2213 ptr_array_free_strings(argbuf);
2214 g_ptr_array_free(argbuf, TRUE);
2215 unlock_session(session);
2219 if (clist_count(lep_list) > 0)
2221 mailimap_list_result_free(lep_list);
2224 ok = imap_cmd_create(session, imap_path);
2225 if (ok != IMAP_SUCCESS) {
2226 log_warning(LOG_PROTOCOL, _("can't create mailbox\n"));
2229 unlock_session(session);
2232 r = imap_threaded_list(folder, "", imap_path, &lep_list);
2233 if (r == MAILIMAP_NO_ERROR) {
2234 GSList *item_list = imap_list_from_lep(IMAP_FOLDER(folder),
2235 lep_list, dirpath, TRUE);
2237 FolderItem *cur_item = FOLDER_ITEM(item_list->data);
2238 no_select = cur_item->no_select;
2239 no_sub = cur_item->no_sub;
2240 g_slist_free(item_list);
2242 mailimap_list_result_free(lep_list);
2245 imap_threaded_subscribe(folder, imap_path, TRUE);
2249 /* just get flags */
2250 r = imap_threaded_list(folder, "", "INBOX", &lep_list);
2251 if (r == MAILIMAP_NO_ERROR) {
2252 GSList *item_list = imap_list_from_lep(IMAP_FOLDER(folder),
2253 lep_list, dirpath, TRUE);
2255 FolderItem *cur_item = FOLDER_ITEM(item_list->data);
2256 no_select = cur_item->no_select;
2257 no_sub = cur_item->no_sub;
2258 g_slist_free(item_list);
2260 mailimap_list_result_free(lep_list);
2264 new_item = folder_item_new(folder, new_name, dirpath);
2265 new_item->no_select = no_select;
2266 new_item->no_sub = no_sub;
2267 folder_item_append(parent, new_item);
2271 dirpath = folder_item_get_path(new_item);
2272 if (!is_dir_exist(dirpath))
2273 make_dir_hier(dirpath);
2275 unlock_session(session);
2278 /* folder existed, scan it */
2279 folder_item_scan_full(new_item, FALSE);
2285 static gint imap_rename_folder(Folder *folder, FolderItem *item,
2290 gchar *real_oldpath;
2291 gchar *real_newpath;
2293 gchar *old_cache_dir;
2294 gchar *new_cache_dir;
2295 IMAPSession *session;
2298 gint exists, recent, unseen;
2299 guint32 uid_validity;
2301 g_return_val_if_fail(folder != NULL, -1);
2302 g_return_val_if_fail(item != NULL, -1);
2303 g_return_val_if_fail(item->path != NULL, -1);
2304 g_return_val_if_fail(name != NULL, -1);
2306 debug_print("getting session...\n");
2307 session = imap_session_get(folder);
2312 if (strchr(name, imap_get_path_separator(session, IMAP_FOLDER(folder), item->path)) != NULL) {
2313 g_warning(_("New folder name must not contain the namespace "
2315 unlock_session(session);
2319 real_oldpath = imap_get_real_path(session, IMAP_FOLDER(folder), item->path);
2321 g_free(session->mbox);
2322 session->mbox = NULL;
2323 ok = imap_cmd_examine(session, "INBOX",
2324 &exists, &recent, &unseen, &uid_validity, FALSE);
2325 if (ok != IMAP_SUCCESS) {
2326 g_free(real_oldpath);
2327 unlock_session(session);
2331 separator = imap_get_path_separator(session, IMAP_FOLDER(folder), item->path);
2332 if (strchr(item->path, G_DIR_SEPARATOR)) {
2333 dirpath = g_path_get_dirname(item->path);
2334 newpath = g_strconcat(dirpath, G_DIR_SEPARATOR_S, name, NULL);
2337 newpath = g_strdup(name);
2339 real_newpath = imap_utf8_to_modified_utf7(newpath);
2340 imap_path_separator_subst(real_newpath, separator);
2342 ok = imap_cmd_rename(session, real_oldpath, real_newpath);
2343 if (ok != IMAP_SUCCESS) {
2344 log_warning(LOG_PROTOCOL, _("can't rename mailbox: %s to %s\n"),
2345 real_oldpath, real_newpath);
2346 g_free(real_oldpath);
2348 g_free(real_newpath);
2349 unlock_session(session);
2353 item->name = g_strdup(name);
2355 old_cache_dir = folder_item_get_path(item);
2357 paths[0] = g_strdup(item->path);
2359 g_node_traverse(item->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
2360 imap_rename_folder_func, paths);
2362 if (is_dir_exist(old_cache_dir)) {
2363 new_cache_dir = folder_item_get_path(item);
2364 if (rename(old_cache_dir, new_cache_dir) < 0) {
2365 FILE_OP_ERROR(old_cache_dir, "rename");
2367 g_free(new_cache_dir);
2370 g_free(old_cache_dir);
2373 g_free(real_oldpath);
2374 g_free(real_newpath);
2375 unlock_session(session);
2379 gint imap_subscribe(Folder *folder, FolderItem *item, gchar *rpath, gboolean sub)
2383 IMAPSession *session;
2384 debug_print("getting session...\n");
2386 session = imap_session_get(folder);
2390 if (item && item->path) {
2391 path = imap_get_real_path(session, IMAP_FOLDER(folder), item->path);
2394 if (!strcmp(path, "INBOX") && sub == FALSE)
2396 debug_print("%ssubscribing %s\n", sub?"":"un", path);
2397 r = imap_threaded_subscribe(folder, path, sub);
2400 r = imap_threaded_subscribe(folder, rpath, sub);
2406 static gint imap_remove_folder_real(Folder *folder, FolderItem *item)
2409 IMAPSession *session;
2413 g_return_val_if_fail(folder != NULL, -1);
2414 g_return_val_if_fail(item != NULL, -1);
2415 g_return_val_if_fail(item->path != NULL, -1);
2417 debug_print("getting session...\n");
2418 session = imap_session_get(folder);
2422 path = imap_get_real_path(session, IMAP_FOLDER(folder), item->path);
2424 imap_threaded_subscribe(folder, path, FALSE);
2425 ok = imap_cmd_delete(session, path);
2426 if (ok != IMAP_SUCCESS) {
2427 gchar *tmp = g_strdup_printf("%s%c", path,
2428 imap_get_path_separator(session, IMAP_FOLDER(folder), path));
2431 ok = imap_cmd_delete(session, path);
2434 if (ok != IMAP_SUCCESS) {
2435 log_warning(LOG_PROTOCOL, _("can't delete mailbox\n"));
2437 unlock_session(session);
2442 cache_dir = folder_item_get_path(item);
2443 if (is_dir_exist(cache_dir) && remove_dir_recursive(cache_dir) < 0)
2444 g_warning("can't remove directory '%s'\n", cache_dir);
2446 folder_item_remove(item);
2447 unlock_session(session);
2451 static gint imap_remove_folder(Folder *folder, FolderItem *item)
2455 g_return_val_if_fail(item != NULL, -1);
2456 g_return_val_if_fail(item->folder != NULL, -1);
2457 g_return_val_if_fail(item->node != NULL, -1);
2459 node = item->node->children;
2460 while (node != NULL) {
2462 if (imap_remove_folder(folder, FOLDER_ITEM(node->data)) < 0)
2466 debug_print("IMAP removing %s\n", item->path);
2468 if (imap_remove_all_msg(folder, item) < 0)
2470 return imap_remove_folder_real(folder, item);
2473 typedef struct _uncached_data {
2474 IMAPSession *session;
2476 MsgNumberList *numlist;
2482 static void *imap_get_uncached_messages_thread(void *data)
2484 uncached_data *stuff = (uncached_data *)data;
2485 IMAPSession *session = stuff->session;
2486 FolderItem *item = stuff->item;
2487 MsgNumberList *numlist = stuff->numlist;
2489 GSList *newlist = NULL;
2490 GSList *llast = NULL;
2491 GSList *seq_list, *cur;
2493 debug_print("uncached_messages\n");
2495 if (session == NULL || item == NULL || item->folder == NULL
2496 || FOLDER_CLASS(item->folder) != &imap_class) {
2501 seq_list = imap_get_lep_set_from_numlist(numlist);
2502 debug_print("get msgs info\n");
2503 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
2504 struct mailimap_set * imapset;
2510 if (session->cancelled)
2513 imapset = cur->data;
2515 r = imap_threaded_fetch_env(session->folder,
2516 imapset, &env_list);
2517 if (r != MAILIMAP_NO_ERROR)
2520 session_set_access_time(SESSION(session));
2523 for(i = 0 ; i < carray_count(env_list) ; i ++) {
2524 struct imap_fetch_env_info * info;
2527 info = carray_get(env_list, i);
2528 msginfo = imap_envelope_from_lep(info, item);
2529 if (msginfo == NULL)
2531 msginfo->folder = item;
2533 llast = newlist = g_slist_append(newlist, msginfo);
2535 llast = g_slist_append(llast, msginfo);
2536 llast = llast->next;
2541 imap_fetch_env_free(env_list);
2544 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
2545 struct mailimap_set * imapset;
2547 imapset = cur->data;
2548 mailimap_set_free(imapset);
2551 session_set_access_time(SESSION(session));
2556 #define MAX_MSG_NUM 50
2558 static GSList *imap_get_uncached_messages(IMAPSession *session,
2560 MsgNumberList *numlist)
2562 GSList *result = NULL;
2564 uncached_data *data = g_new0(uncached_data, 1);
2569 data->total = g_slist_length(numlist);
2570 debug_print("messages list : %i\n", data->total);
2572 while (cur != NULL) {
2573 GSList * partial_result;
2581 while (count < MAX_MSG_NUM) {
2586 if (newlist == NULL)
2587 llast = newlist = g_slist_append(newlist, p);
2589 llast = g_slist_append(llast, p);
2590 llast = llast->next;
2600 data->session = session;
2602 data->numlist = newlist;
2605 if (prefs_common.work_offline &&
2606 !inc_offline_should_override(FALSE,
2607 _("Claws Mail needs network access in order "
2608 "to access the IMAP server."))) {
2614 (GSList *)imap_get_uncached_messages_thread(data);
2616 statusbar_progress_all(data->cur,data->total, 1);
2618 g_slist_free(newlist);
2620 result = g_slist_concat(result, partial_result);
2624 statusbar_progress_all(0,0,0);
2625 statusbar_pop_all();
2630 static void imap_delete_all_cached_messages(FolderItem *item)
2634 g_return_if_fail(item != NULL);
2635 g_return_if_fail(item->folder != NULL);
2636 g_return_if_fail(FOLDER_CLASS(item->folder) == &imap_class);
2638 debug_print("Deleting all cached messages...\n");
2640 dir = folder_item_get_path(item);
2641 if (is_dir_exist(dir))
2642 remove_all_numbered_files(dir);
2645 debug_print("done.\n");
2648 gchar imap_get_path_separator_for_item(FolderItem *item)
2650 Folder *folder = NULL;
2651 IMAPFolder *imap_folder = NULL;
2652 IMAPSession *session = NULL;
2657 folder = item->folder;
2662 imap_folder = IMAP_FOLDER(folder);
2667 debug_print("getting session...");
2668 session = imap_session_get(FOLDER(folder));
2669 result = imap_get_path_separator(session, imap_folder, item->path);
2670 unlock_session(session);
2674 static gchar imap_refresh_path_separator(IMAPSession *session, IMAPFolder *folder, const gchar *subfolder)
2678 gchar separator = '\0';
2680 g_return_val_if_fail(session != NULL, '/');
2681 r = imap_threaded_list((Folder *)folder, "", subfolder, &lep_list);
2683 if (r != MAILIMAP_NO_ERROR) {
2684 log_warning(LOG_PROTOCOL, _("LIST failed\n"));
2688 if (clist_count(lep_list) > 0) {
2689 clistiter * iter = clist_begin(lep_list);
2690 struct mailimap_mailbox_list * mb;
2691 mb = clist_content(iter);
2693 separator = mb->mb_delimiter;
2694 debug_print("got separator: %c\n", folder->last_seen_separator);
2696 mailimap_list_result_free(lep_list);
2700 static gchar imap_get_path_separator(IMAPSession *session, IMAPFolder *folder, const gchar *path)
2702 gchar separator = '/';
2704 if (folder->last_seen_separator == 0) {
2705 folder->last_seen_separator = imap_refresh_path_separator(session, folder, "");
2708 if (folder->last_seen_separator == 0) {
2709 folder->last_seen_separator = imap_refresh_path_separator(session, folder, "INBOX");
2712 if (folder->last_seen_separator != 0) {
2713 debug_print("using separator: %c\n", folder->last_seen_separator);
2714 return folder->last_seen_separator;
2720 static gchar *imap_get_real_path(IMAPSession *session, IMAPFolder *folder, const gchar *path)
2725 g_return_val_if_fail(folder != NULL, NULL);
2726 g_return_val_if_fail(path != NULL, NULL);
2728 real_path = imap_utf8_to_modified_utf7(path);
2729 separator = imap_get_path_separator(session, folder, path);
2730 imap_path_separator_subst(real_path, separator);
2735 static gint imap_set_message_flags(IMAPSession *session,
2736 MsgNumberList *numlist,
2744 seq_list = imap_get_lep_set_from_numlist(numlist);
2746 for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) {
2747 struct mailimap_set * imapset;
2749 imapset = cur->data;
2751 ok = imap_cmd_store(session, imapset,
2755 imap_lep_set_free(seq_list);
2757 return IMAP_SUCCESS;
2760 typedef struct _select_data {
2761 IMAPSession *session;
2766 guint32 *uid_validity;
2770 static gint imap_select(IMAPSession *session, IMAPFolder *folder,
2772 gint *exists, gint *recent, gint *unseen,
2773 guint32 *uid_validity, gboolean block)
2777 gint exists_, recent_, unseen_;
2778 guint32 uid_validity_;
2780 if (!exists && !recent && !unseen && !uid_validity) {
2781 if (session->mbox && strcmp(session->mbox, path) == 0)
2782 return IMAP_SUCCESS;
2791 uid_validity = &uid_validity_;
2793 g_free(session->mbox);
2794 session->mbox = NULL;
2796 real_path = imap_get_real_path(session, folder, path);
2798 ok = imap_cmd_select(session, real_path,
2799 exists, recent, unseen, uid_validity, block);
2800 if (ok != IMAP_SUCCESS)
2801 log_warning(LOG_PROTOCOL, _("can't select folder: %s\n"), real_path);
2803 session->mbox = g_strdup(path);
2804 session->folder_content_changed = FALSE;
2811 static gint imap_status(IMAPSession *session, IMAPFolder *folder,
2812 const gchar *path, IMAPFolderItem *item,
2814 guint32 *uid_next, guint32 *uid_validity,
2815 gint *unseen, gboolean block)
2819 struct mailimap_mailbox_data_status * data_status;
2824 real_path = imap_get_real_path(session, folder, path);
2842 r = imap_threaded_status(FOLDER(folder), real_path,
2843 &data_status, mask);
2846 if (r != MAILIMAP_NO_ERROR) {
2847 debug_print("status err %d\n", r);
2851 if (data_status->st_info_list == NULL) {
2852 mailimap_mailbox_data_status_free(data_status);
2853 debug_print("status->st_info_list == NULL\n");
2858 for(iter = clist_begin(data_status->st_info_list) ; iter != NULL ;
2859 iter = clist_next(iter)) {
2860 struct mailimap_status_info * info;
2862 info = clist_content(iter);
2863 switch (info->st_att) {
2864 case MAILIMAP_STATUS_ATT_MESSAGES:
2866 * messages = info->st_value;
2867 got_values |= 1 << 0;
2871 case MAILIMAP_STATUS_ATT_UIDNEXT:
2873 * uid_next = info->st_value;
2874 got_values |= 1 << 2;
2878 case MAILIMAP_STATUS_ATT_UIDVALIDITY:
2880 * uid_validity = info->st_value;
2881 got_values |= 1 << 3;
2885 case MAILIMAP_STATUS_ATT_UNSEEN:
2887 * unseen = info->st_value;
2888 got_values |= 1 << 4;
2893 mailimap_mailbox_data_status_free(data_status);
2895 if (got_values != mask) {
2896 g_warning("status: incomplete values received (%d)\n", got_values);
2898 return IMAP_SUCCESS;
2901 static void imap_free_capabilities(IMAPSession *session)
2903 slist_free_strings(session->capability);
2904 g_slist_free(session->capability);
2905 session->capability = NULL;
2908 /* low-level IMAP4rev1 commands */
2910 static gint imap_cmd_login(IMAPSession *session,
2911 const gchar *user, const gchar *pass,
2917 if (!strcmp(type, "LOGIN") && imap_has_capability(session, "LOGINDISABLED")) {
2918 gint ok = IMAP_ERROR;
2919 if (imap_has_capability(session, "STARTTLS")) {
2921 log_warning(LOG_PROTOCOL, _("Server requires TLS to log in.\n"));
2922 ok = imap_cmd_starttls(session);
2923 if (ok != IMAP_SUCCESS) {
2924 log_warning(LOG_PROTOCOL, _("Can't start TLS session.\n"));
2928 imap_free_capabilities(session);
2929 if (imap_get_capabilities(session) != MAILIMAP_NO_ERROR) {
2930 log_warning(LOG_PROTOCOL, _("Can't refresh capabilities.\n"));
2935 log_error(LOG_PROTOCOL, _("Connection to %s failed: "
2936 "server requires TLS, but Claws Mail "
2937 "has been compiled without OpenSSL "
2939 SESSION(session)->server);
2943 log_error(LOG_PROTOCOL, _("Server logins are disabled.\n"));
2948 log_print(LOG_PROTOCOL, "IMAP4> Logging %s to %s using %s\n",
2950 SESSION(session)->server,
2952 r = imap_threaded_login(session->folder, user, pass, type);
2953 if (r != MAILIMAP_NO_ERROR) {
2954 log_print(LOG_PROTOCOL, "IMAP4< Error logging in to %s\n",
2955 SESSION(session)->server);
2958 log_print(LOG_PROTOCOL, "IMAP4< Login to %s successful\n",
2959 SESSION(session)->server);
2965 static gint imap_cmd_noop(IMAPSession *session)
2968 unsigned int exists;
2970 r = imap_threaded_noop(session->folder, &exists);
2971 if (r != MAILIMAP_NO_ERROR) {
2972 debug_print("noop err %d\n", r);
2975 session->exists = exists;
2976 session_set_access_time(SESSION(session));
2978 return IMAP_SUCCESS;
2982 static gint imap_cmd_starttls(IMAPSession *session)
2986 r = imap_threaded_starttls(session->folder,
2987 SESSION(session)->server, SESSION(session)->port);
2988 if (r != MAILIMAP_NO_ERROR) {
2989 debug_print("starttls err %d\n", r);
2992 return IMAP_SUCCESS;
2996 static gint imap_cmd_select(IMAPSession *session, const gchar *folder,
2997 gint *exists, gint *recent, gint *unseen,
2998 guint32 *uid_validity, gboolean block)
3002 r = imap_threaded_select(session->folder, folder,
3003 exists, recent, unseen, uid_validity);
3004 if (r != MAILIMAP_NO_ERROR) {
3005 debug_print("select err %d\n", r);
3008 return IMAP_SUCCESS;
3011 static gint imap_cmd_examine(IMAPSession *session, const gchar *folder,
3012 gint *exists, gint *recent, gint *unseen,
3013 guint32 *uid_validity, gboolean block)
3017 r = imap_threaded_examine(session->folder, folder,
3018 exists, recent, unseen, uid_validity);
3019 if (r != MAILIMAP_NO_ERROR) {
3020 debug_print("examine err %d\n", r);
3024 return IMAP_SUCCESS;
3027 static gint imap_cmd_create(IMAPSession *session, const gchar *folder)
3031 r = imap_threaded_create(session->folder, folder);
3032 if (r != MAILIMAP_NO_ERROR) {
3037 return IMAP_SUCCESS;
3040 static gint imap_cmd_rename(IMAPSession *session, const gchar *old_folder,
3041 const gchar *new_folder)
3045 r = imap_threaded_rename(session->folder, old_folder,
3047 if (r != MAILIMAP_NO_ERROR) {
3052 return IMAP_SUCCESS;
3055 static gint imap_cmd_delete(IMAPSession *session, const gchar *folder)
3060 r = imap_threaded_delete(session->folder, folder);
3061 if (r != MAILIMAP_NO_ERROR) {
3066 return IMAP_SUCCESS;
3069 typedef struct _fetch_data {
3070 IMAPSession *session;
3072 const gchar *filename;
3078 static void *imap_cmd_fetch_thread(void *data)
3080 fetch_data *stuff = (fetch_data *)data;
3081 IMAPSession *session = stuff->session;
3082 guint32 uid = stuff->uid;
3083 const gchar *filename = stuff->filename;
3087 r = imap_threaded_fetch_content(session->folder,
3091 r = imap_threaded_fetch_content(session->folder,
3094 if (r != MAILIMAP_NO_ERROR) {
3095 debug_print("fetch err %d\n", r);
3096 return GINT_TO_POINTER(IMAP_ERROR);
3098 return GINT_TO_POINTER(IMAP_SUCCESS);
3101 static gint imap_cmd_fetch(IMAPSession *session, guint32 uid,
3102 const gchar *filename, gboolean headers,
3105 fetch_data *data = g_new0(fetch_data, 1);
3108 data->session = session;
3110 data->filename = filename;
3111 data->headers = headers;
3114 if (prefs_common.work_offline &&
3115 !inc_offline_should_override(FALSE,
3116 _("Claws Mail needs network access in order "
3117 "to access the IMAP server."))) {
3121 statusbar_print_all(_("Fetching message..."));
3122 result = GPOINTER_TO_INT(imap_cmd_fetch_thread(data));
3123 statusbar_pop_all();
3129 static gint imap_cmd_append(IMAPSession *session, const gchar *destfolder,
3130 const gchar *file, IMAPFlags flags,
3133 struct mailimap_flag_list * flag_list;
3136 g_return_val_if_fail(file != NULL, IMAP_ERROR);
3138 flag_list = imap_flag_to_lep(flags);
3139 r = imap_threaded_append(session->folder, destfolder,
3140 file, flag_list, (int *)new_uid);
3141 mailimap_flag_list_free(flag_list);
3143 if (r != MAILIMAP_NO_ERROR) {
3144 debug_print("append err %d\n", r);
3147 return IMAP_SUCCESS;
3150 static gint imap_cmd_copy(IMAPSession *session, struct mailimap_set * set,
3151 const gchar *destfolder, GRelation *uid_mapping,
3152 struct mailimap_set **source, struct mailimap_set **dest)
3156 g_return_val_if_fail(session != NULL, IMAP_ERROR);
3157 g_return_val_if_fail(set != NULL, IMAP_ERROR);
3158 g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
3160 r = imap_threaded_copy(session->folder, set, destfolder, source, dest);
3161 if (r != MAILIMAP_NO_ERROR) {
3166 return IMAP_SUCCESS;
3169 static gint imap_cmd_store(IMAPSession *session, struct mailimap_set * set,
3170 IMAPFlags flags, int do_add)
3173 struct mailimap_flag_list * flag_list;
3174 struct mailimap_store_att_flags * store_att_flags;
3176 flag_list = imap_flag_to_lep(flags);
3180 mailimap_store_att_flags_new_add_flags_silent(flag_list);
3183 mailimap_store_att_flags_new_remove_flags_silent(flag_list);
3185 r = imap_threaded_store(session->folder, set, store_att_flags);
3186 mailimap_store_att_flags_free(store_att_flags);
3187 if (r != MAILIMAP_NO_ERROR) {
3192 return IMAP_SUCCESS;
3195 static gint imap_cmd_expunge(IMAPSession *session)
3199 if (prefs_common.work_offline &&
3200 !inc_offline_should_override(FALSE,
3201 _("Claws Mail needs network access in order "
3202 "to access the IMAP server."))) {
3206 r = imap_threaded_expunge(session->folder);
3207 if (r != MAILIMAP_NO_ERROR) {
3212 return IMAP_SUCCESS;
3215 static void imap_path_separator_subst(gchar *str, gchar separator)
3218 gboolean in_escape = FALSE;
3220 if (!separator || separator == '/') return;
3222 for (p = str; *p != '\0'; p++) {
3223 if (*p == '/' && !in_escape)
3225 else if (*p == '&' && *(p + 1) != '-' && !in_escape)
3227 else if (*p == '-' && in_escape)
3232 static gchar *imap_modified_utf7_to_utf8(const gchar *mutf7_str)
3234 static iconv_t cd = (iconv_t)-1;
3235 static gboolean iconv_ok = TRUE;
3238 size_t norm_utf7_len;
3240 gchar *to_str, *to_p;
3242 gboolean in_escape = FALSE;
3244 if (!iconv_ok) return g_strdup(mutf7_str);
3246 if (cd == (iconv_t)-1) {
3247 cd = iconv_open(CS_INTERNAL, CS_UTF_7);
3248 if (cd == (iconv_t)-1) {
3249 g_warning("iconv cannot convert UTF-7 to %s\n",
3252 return g_strdup(mutf7_str);
3256 /* modified UTF-7 to normal UTF-7 conversion */
3257 norm_utf7 = g_string_new(NULL);
3259 for (p = mutf7_str; *p != '\0'; p++) {
3260 /* replace: '&' -> '+',
3262 escaped ',' -> '/' */
3263 if (!in_escape && *p == '&') {
3264 if (*(p + 1) != '-') {
3265 g_string_append_c(norm_utf7, '+');
3268 g_string_append_c(norm_utf7, '&');
3271 } else if (in_escape && *p == ',') {
3272 g_string_append_c(norm_utf7, '/');
3273 } else if (in_escape && *p == '-') {
3274 g_string_append_c(norm_utf7, '-');
3277 g_string_append_c(norm_utf7, *p);
3281 norm_utf7_p = norm_utf7->str;
3282 norm_utf7_len = norm_utf7->len;
3283 to_len = strlen(mutf7_str) * 5;
3284 to_p = to_str = g_malloc(to_len + 1);
3286 if (iconv(cd, (ICONV_CONST gchar **)&norm_utf7_p, &norm_utf7_len,
3287 &to_p, &to_len) == -1) {
3288 g_warning(_("iconv cannot convert UTF-7 to %s\n"),
3289 conv_get_locale_charset_str());
3290 g_string_free(norm_utf7, TRUE);
3292 return g_strdup(mutf7_str);
3295 /* second iconv() call for flushing */
3296 iconv(cd, NULL, NULL, &to_p, &to_len);
3297 g_string_free(norm_utf7, TRUE);
3303 static gchar *imap_utf8_to_modified_utf7(const gchar *from)
3305 static iconv_t cd = (iconv_t)-1;
3306 static gboolean iconv_ok = TRUE;
3307 gchar *norm_utf7, *norm_utf7_p;
3308 size_t from_len, norm_utf7_len;
3310 gchar *from_tmp, *to, *p;
3311 gboolean in_escape = FALSE;
3313 if (!iconv_ok) return g_strdup(from);
3315 if (cd == (iconv_t)-1) {
3316 cd = iconv_open(CS_UTF_7, CS_INTERNAL);
3317 if (cd == (iconv_t)-1) {
3318 g_warning(_("iconv cannot convert %s to UTF-7\n"),
3321 return g_strdup(from);
3325 /* UTF-8 to normal UTF-7 conversion */
3326 Xstrdup_a(from_tmp, from, return g_strdup(from));
3327 from_len = strlen(from);
3328 norm_utf7_len = from_len * 5;
3329 Xalloca(norm_utf7, norm_utf7_len + 1, return g_strdup(from));
3330 norm_utf7_p = norm_utf7;
3332 #define IS_PRINT(ch) (isprint(ch) && IS_ASCII(ch))
3334 while (from_len > 0) {
3335 if (*from_tmp == '+') {
3336 *norm_utf7_p++ = '+';
3337 *norm_utf7_p++ = '-';
3341 } else if (IS_PRINT(*(guchar *)from_tmp)) {
3342 /* printable ascii char */
3343 *norm_utf7_p = *from_tmp;
3349 size_t conv_len = 0;
3351 /* unprintable char: convert to UTF-7 */
3353 while (!IS_PRINT(*(guchar *)p) && conv_len < from_len) {
3354 conv_len += g_utf8_skip[*(guchar *)p];
3355 p += g_utf8_skip[*(guchar *)p];
3358 from_len -= conv_len;
3359 if (iconv(cd, (ICONV_CONST gchar **)&from_tmp,
3361 &norm_utf7_p, &norm_utf7_len) == -1) {
3362 g_warning(_("iconv cannot convert UTF-8 to UTF-7\n"));
3363 return g_strdup(from);
3366 /* second iconv() call for flushing */
3367 iconv(cd, NULL, NULL, &norm_utf7_p, &norm_utf7_len);
3373 *norm_utf7_p = '\0';
3374 to_str = g_string_new(NULL);
3375 for (p = norm_utf7; p < norm_utf7_p; p++) {
3376 /* replace: '&' -> "&-",
3379 BASE64 '/' -> ',' */
3380 if (!in_escape && *p == '&') {
3381 g_string_append(to_str, "&-");
3382 } else if (!in_escape && *p == '+') {
3383 if (*(p + 1) == '-') {
3384 g_string_append_c(to_str, '+');
3387 g_string_append_c(to_str, '&');
3390 } else if (in_escape && *p == '/') {
3391 g_string_append_c(to_str, ',');
3392 } else if (in_escape && *p == '-') {
3393 g_string_append_c(to_str, '-');
3396 g_string_append_c(to_str, *p);
3402 g_string_append_c(to_str, '-');
3406 g_string_free(to_str, FALSE);
3411 static gboolean imap_rename_folder_func(GNode *node, gpointer data)
3413 FolderItem *item = node->data;
3414 gchar **paths = data;
3415 const gchar *oldpath = paths[0];
3416 const gchar *newpath = paths[1];
3417 gchar *real_oldpath, *real_newpath;
3419 gchar *new_itempath;
3421 IMAPSession *session = imap_session_get(item->folder);
3423 oldpathlen = strlen(oldpath);
3424 if (strncmp(oldpath, item->path, oldpathlen) != 0) {
3425 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
3429 base = item->path + oldpathlen;
3430 while (*base == G_DIR_SEPARATOR) base++;
3432 new_itempath = g_strdup(newpath);
3434 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
3437 real_oldpath = imap_get_real_path(session, IMAP_FOLDER(item->folder), item->path);
3439 item->path = new_itempath;
3441 real_newpath = imap_get_real_path(session, IMAP_FOLDER(item->folder), item->path);
3443 imap_threaded_subscribe(item->folder, real_oldpath, FALSE);
3444 imap_threaded_subscribe(item->folder, real_newpath, TRUE);
3446 g_free(real_oldpath);
3447 g_free(real_newpath);
3451 typedef struct _get_list_uid_data {
3453 IMAPSession *session;
3454 IMAPFolderItem *item;
3455 GSList **msgnum_list;
3457 } get_list_uid_data;
3459 static void *get_list_of_uids_thread(void *data)
3461 get_list_uid_data *stuff = (get_list_uid_data *)data;
3462 Folder *folder = stuff->folder;
3463 IMAPFolderItem *item = stuff->item;
3464 GSList **msgnum_list = stuff->msgnum_list;
3465 gint ok, nummsgs = 0, lastuid_old;
3466 IMAPSession *session;
3467 GSList *uidlist, *elem;
3469 clist * lep_uidlist;
3471 session = stuff->session;
3472 if (session == NULL) {
3474 return GINT_TO_POINTER(-1);
3476 /* no session locking here, it's already locked by caller */
3477 ok = imap_select(session, IMAP_FOLDER(folder), item->item.path,
3478 NULL, NULL, NULL, NULL, TRUE);
3479 if (ok != IMAP_SUCCESS) {
3481 return GINT_TO_POINTER(-1);
3486 if (folder->account && folder->account->low_bandwidth) {
3487 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SIMPLE, NULL,
3491 if (r == MAILIMAP_NO_ERROR) {
3492 GSList * fetchuid_list =
3493 imap_uid_list_from_lep(lep_uidlist);
3494 mailimap_search_result_free(lep_uidlist);
3496 uidlist = g_slist_concat(fetchuid_list, uidlist);
3498 carray * lep_uidtab;
3499 r = imap_threaded_fetch_uid(folder, 1,
3501 if (r == MAILIMAP_NO_ERROR) {
3502 GSList * fetchuid_list =
3503 imap_uid_list_from_lep_tab(lep_uidtab);
3504 imap_fetch_uid_list_free(lep_uidtab);
3505 uidlist = g_slist_concat(fetchuid_list, uidlist);
3509 if (r != MAILIMAP_NO_ERROR) {
3511 return GINT_TO_POINTER(-1);
3514 lastuid_old = item->lastuid;
3515 *msgnum_list = g_slist_copy(item->uid_list);
3516 nummsgs = g_slist_length(*msgnum_list);
3517 debug_print("Got %d uids from cache\n", g_slist_length(item->uid_list));
3519 for (elem = uidlist; elem != NULL; elem = g_slist_next(elem)) {
3522 msgnum = GPOINTER_TO_INT(elem->data);
3523 if (msgnum > lastuid_old) {
3524 *msgnum_list = g_slist_prepend(*msgnum_list, GINT_TO_POINTER(msgnum));
3525 item->uid_list = g_slist_prepend(item->uid_list, GINT_TO_POINTER(msgnum));
3528 if(msgnum > item->lastuid)
3529 item->lastuid = msgnum;
3532 g_slist_free(uidlist);
3534 return GINT_TO_POINTER(nummsgs);
3537 static gint get_list_of_uids(IMAPSession *session, Folder *folder, IMAPFolderItem *item, GSList **msgnum_list)
3540 get_list_uid_data *data = g_new0(get_list_uid_data, 1);
3542 data->folder = folder;
3544 data->msgnum_list = msgnum_list;
3545 data->session = session;
3546 if (prefs_common.work_offline &&
3547 !inc_offline_should_override(FALSE,
3548 _("Claws Mail needs network access in order "
3549 "to access the IMAP server."))) {
3554 result = GPOINTER_TO_INT(get_list_of_uids_thread(data));
3560 gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list, gboolean *old_uids_valid)
3562 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3563 IMAPSession *session;
3564 gint ok, nummsgs = 0, exists;
3565 guint32 uid_next = 0, uid_val = 0;
3566 GSList *uidlist = NULL;
3568 gboolean selected_folder;
3569 debug_print("get_num_list\n");
3571 g_return_val_if_fail(folder != NULL, -1);
3572 g_return_val_if_fail(item != NULL, -1);
3573 g_return_val_if_fail(item->item.path != NULL, -1);
3574 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3575 g_return_val_if_fail(folder->account != NULL, -1);
3577 debug_print("getting session...\n");
3578 session = imap_session_get(folder);
3579 g_return_val_if_fail(session != NULL, -1);
3581 if (FOLDER_ITEM(item)->path)
3582 statusbar_print_all(_("Scanning folder %s%c%s ..."),
3583 FOLDER_ITEM(item)->folder->name,
3585 FOLDER_ITEM(item)->path);
3587 statusbar_print_all(_("Scanning folder %s ..."),
3588 FOLDER_ITEM(item)->folder->name);
3590 selected_folder = (session->mbox != NULL) &&
3591 (!strcmp(session->mbox, item->item.path));
3592 if (selected_folder && time(NULL) - item->use_cache < 2) {
3593 ok = imap_cmd_noop(session);
3594 if (ok != IMAP_SUCCESS) {
3595 debug_print("disconnected!\n");
3596 session = imap_reconnect_if_possible(folder, session);
3597 if (session == NULL) {
3598 statusbar_pop_all();
3599 unlock_session(session);
3603 exists = session->exists;
3605 uid_next = item->c_uid_next;
3606 uid_val = item->c_uid_validity;
3607 *old_uids_valid = TRUE;
3609 if (item->use_cache && time(NULL) - item->use_cache < 2) {
3610 exists = item->c_messages;
3611 uid_next = item->c_uid_next;
3612 uid_val = item->c_uid_validity;
3614 debug_print("using cache %d %d %d\n", exists, uid_next, uid_val);
3616 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path, item,
3617 &exists, &uid_next, &uid_val, NULL, FALSE);
3619 item->item.last_num = uid_next - 1;
3621 item->use_cache = (time_t)0;
3622 if (ok != IMAP_SUCCESS) {
3623 statusbar_pop_all();
3624 unlock_session(session);
3627 if(item->item.mtime == uid_val)
3628 *old_uids_valid = TRUE;
3630 *old_uids_valid = FALSE;
3632 debug_print("Freeing imap uid cache (%d != %d)\n",
3633 (int)item->item.mtime, uid_val);
3635 g_slist_free(item->uid_list);
3636 item->uid_list = NULL;
3638 item->item.mtime = uid_val;
3640 imap_delete_all_cached_messages((FolderItem *)item);
3644 /* If old uid_next matches new uid_next we can be sure no message
3645 was added to the folder */
3646 debug_print("uid_next is %d and item->uid_next %d \n",
3647 uid_next, item->uid_next);
3648 if (uid_next == item->uid_next) {
3649 nummsgs = g_slist_length(item->uid_list);
3651 /* If number of messages is still the same we
3652 know our caches message numbers are still valid,
3653 otherwise if the number of messages has decrease
3654 we discard our cache to start a new scan to find
3655 out which numbers have been removed */
3656 if (exists == nummsgs) {
3657 debug_print("exists == nummsgs\n");
3658 *msgnum_list = g_slist_copy(item->uid_list);
3659 statusbar_pop_all();
3660 unlock_session(session);
3662 } else if (exists < nummsgs) {
3663 debug_print("Freeing imap uid cache");
3665 g_slist_free(item->uid_list);
3666 item->uid_list = NULL;
3671 *msgnum_list = NULL;
3672 statusbar_pop_all();
3673 unlock_session(session);
3677 item->last_change = time(NULL);
3678 nummsgs = get_list_of_uids(session, folder, item, &uidlist);
3681 statusbar_pop_all();
3682 unlock_session(session);
3686 if (nummsgs != exists) {
3687 /* Cache contains more messages then folder, we have cached
3688 an old UID of a message that was removed and new messages
3689 have been added too, otherwise the uid_next check would
3691 debug_print("Freeing imap uid cache");
3693 g_slist_free(item->uid_list);
3694 item->uid_list = NULL;
3696 g_slist_free(*msgnum_list);
3698 nummsgs = get_list_of_uids(session, folder, item, &uidlist);
3701 *msgnum_list = uidlist;
3703 dir = folder_item_get_path((FolderItem *)item);
3704 debug_print("removing old messages from %s\n", dir);
3705 remove_numbered_files_not_in_list(dir, *msgnum_list);
3708 item->uid_next = uid_next;
3710 debug_print("get_num_list - ok - %i\n", nummsgs);
3711 statusbar_pop_all();
3712 unlock_session(session);
3716 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item)
3721 flags.perm_flags = MSG_NEW|MSG_UNREAD;
3722 flags.tmp_flags = 0;
3724 g_return_val_if_fail(item != NULL, NULL);
3725 g_return_val_if_fail(file != NULL, NULL);
3727 if (folder_has_parent_of_type(item, F_QUEUE)) {
3728 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
3729 } else if (folder_has_parent_of_type(item, F_DRAFT)) {
3730 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
3733 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3734 if (!msginfo) return NULL;
3736 msginfo->plaintext_file = g_strdup(file);
3737 msginfo->folder = item;
3742 GSList *imap_get_msginfos(Folder *folder, FolderItem *item,
3743 GSList *msgnum_list)
3745 IMAPSession *session;
3746 MsgInfoList *ret = NULL;
3749 debug_print("get_msginfos\n");
3751 g_return_val_if_fail(folder != NULL, NULL);
3752 g_return_val_if_fail(item != NULL, NULL);
3753 g_return_val_if_fail(msgnum_list != NULL, NULL);
3755 debug_print("getting session...\n");
3756 session = imap_session_get(folder);
3757 g_return_val_if_fail(session != NULL, NULL);
3759 debug_print("IMAP getting msginfos\n");
3760 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3761 NULL, NULL, NULL, NULL, FALSE);
3762 if (ok != IMAP_SUCCESS) {
3763 unlock_session(session);
3766 if (!(folder_has_parent_of_type(item, F_DRAFT) ||
3767 folder_has_parent_of_type(item, F_QUEUE))) {
3768 ret = g_slist_concat(ret,
3769 imap_get_uncached_messages(session, item,
3772 MsgNumberList *sorted_list, *elem, *llast = NULL;
3773 gint startnum, lastnum;
3775 sorted_list = g_slist_sort(g_slist_copy(msgnum_list), g_int_compare);
3777 startnum = lastnum = GPOINTER_TO_INT(sorted_list->data);
3779 llast = g_slist_last(ret);
3780 for (elem = sorted_list;; elem = g_slist_next(elem)) {
3784 num = GPOINTER_TO_INT(elem->data);
3786 if (num > lastnum + 1 || elem == NULL) {
3788 for (i = startnum; i <= lastnum; ++i) {
3790 unlock_session(session);
3791 file = imap_fetch_msg(folder, item, i);
3792 lock_session(session);
3794 MsgInfo *msginfo = imap_parse_msg(file, item);
3795 if (msginfo != NULL) {
3796 msginfo->msgnum = i;
3798 llast = ret = g_slist_append(ret, msginfo);
3800 llast = g_slist_append(llast, msginfo);
3801 llast = llast->next;
3806 session_set_access_time(SESSION(session));
3817 g_slist_free(sorted_list);
3819 unlock_session(session);
3823 MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid)
3825 MsgInfo *msginfo = NULL;
3826 MsgInfoList *msginfolist;
3827 MsgNumberList numlist;
3829 numlist.next = NULL;
3830 numlist.data = GINT_TO_POINTER(uid);
3832 msginfolist = imap_get_msginfos(folder, item, &numlist);
3833 if (msginfolist != NULL) {
3834 msginfo = msginfolist->data;
3835 g_slist_free(msginfolist);
3841 gboolean imap_scan_required(Folder *folder, FolderItem *_item)
3843 IMAPSession *session;
3844 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3845 gint ok, exists = 0, unseen = 0;
3846 guint32 uid_next = 0, uid_val = 0;
3847 gboolean selected_folder;
3849 g_return_val_if_fail(folder != NULL, FALSE);
3850 g_return_val_if_fail(item != NULL, FALSE);
3851 g_return_val_if_fail(item->item.folder != NULL, FALSE);
3852 g_return_val_if_fail(FOLDER_CLASS(item->item.folder) == &imap_class, FALSE);
3854 if (item->item.path == NULL)
3857 debug_print("getting session...\n");
3858 session = imap_session_get(folder);
3859 g_return_val_if_fail(session != NULL, FALSE);
3861 selected_folder = (session->mbox != NULL) &&
3862 (!strcmp(session->mbox, item->item.path));
3863 if (selected_folder && time(NULL) - item->use_cache < 2) {
3864 ok = imap_cmd_noop(session);
3865 if (ok != IMAP_SUCCESS) {
3866 debug_print("disconnected!\n");
3867 session = imap_reconnect_if_possible(folder, session);
3868 if (session == NULL)
3872 if (session->folder_content_changed
3873 || session->exists != item->item.total_msgs) {
3874 unlock_session(session);
3878 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path, IMAP_FOLDER_ITEM(item),
3879 &exists, &uid_next, &uid_val, &unseen, FALSE);
3880 if (ok != IMAP_SUCCESS) {
3881 unlock_session(session);
3885 item->use_cache = time(NULL);
3886 item->c_messages = exists;
3887 item->c_uid_next = uid_next;
3888 item->c_uid_validity = uid_val;
3889 item->c_unseen = unseen;
3890 item->item.last_num = uid_next - 1;
3891 debug_print("uidnext %d, item->uid_next %d, exists %d, item->item.total_msgs %d\n",
3892 uid_next, item->uid_next, exists, item->item.total_msgs);
3893 if ((uid_next != item->uid_next) || (exists != item->item.total_msgs)
3894 || unseen != item->item.unread_msgs || uid_val != item->item.mtime) {
3895 unlock_session(session);
3896 item->last_change = time(NULL);
3900 unlock_session(session);
3904 void imap_change_flags(Folder *folder, FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
3906 IMAPSession *session;
3907 IMAPFlags flags_set = 0, flags_unset = 0;
3908 gint ok = IMAP_SUCCESS;
3909 MsgNumberList numlist;
3910 hashtable_data *ht_data = NULL;
3912 g_return_if_fail(folder != NULL);
3913 g_return_if_fail(folder->klass == &imap_class);
3914 g_return_if_fail(item != NULL);
3915 g_return_if_fail(item->folder == folder);
3916 g_return_if_fail(msginfo != NULL);
3917 g_return_if_fail(msginfo->folder == item);
3919 if (!MSG_IS_MARKED(msginfo->flags) && (newflags & MSG_MARKED))
3920 flags_set |= IMAP_FLAG_FLAGGED;
3921 if ( MSG_IS_MARKED(msginfo->flags) && !(newflags & MSG_MARKED))
3922 flags_unset |= IMAP_FLAG_FLAGGED;
3924 if (!MSG_IS_UNREAD(msginfo->flags) && (newflags & MSG_UNREAD))
3925 flags_unset |= IMAP_FLAG_SEEN;
3926 if ( MSG_IS_UNREAD(msginfo->flags) && !(newflags & MSG_UNREAD))
3927 flags_set |= IMAP_FLAG_SEEN;
3929 if (!MSG_IS_REPLIED(msginfo->flags) && (newflags & MSG_REPLIED))
3930 flags_set |= IMAP_FLAG_ANSWERED;
3931 if ( MSG_IS_REPLIED(msginfo->flags) && !(newflags & MSG_REPLIED))
3932 flags_unset |= IMAP_FLAG_ANSWERED;
3934 if (!MSG_IS_DELETED(msginfo->flags) && (newflags & MSG_DELETED))
3935 flags_set |= IMAP_FLAG_DELETED;
3936 if ( MSG_IS_DELETED(msginfo->flags) && !(newflags & MSG_DELETED))
3937 flags_unset |= IMAP_FLAG_DELETED;
3939 if (!flags_set && !flags_unset) {
3940 /* the changed flags were not translatable to IMAP-speak.
3941 * like MSG_POSTFILTERED, so just apply. */
3942 msginfo->flags.perm_flags = newflags;
3946 debug_print("getting session...\n");
3947 session = imap_session_get(folder);
3952 if ((ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
3953 NULL, NULL, NULL, NULL, FALSE)) != IMAP_SUCCESS) {
3954 unlock_session(session);
3957 numlist.next = NULL;
3958 numlist.data = GINT_TO_POINTER(msginfo->msgnum);
3960 if (IMAP_FOLDER_ITEM(item)->batching) {
3961 /* instead of performing an UID STORE command for each message change,
3962 * as a lot of them can change "together", we just fill in hashtables
3963 * and defer the treatment so that we're able to send only one
3966 debug_print("IMAP batch mode on, deferring flags change\n");
3968 ht_data = g_hash_table_lookup(IMAP_FOLDER_ITEM(item)->flags_set_table,
3969 GINT_TO_POINTER(flags_set));
3970 if (ht_data == NULL) {
3971 ht_data = g_new0(hashtable_data, 1);
3972 ht_data->session = session;
3973 ht_data->item = IMAP_FOLDER_ITEM(item);
3974 g_hash_table_insert(IMAP_FOLDER_ITEM(item)->flags_set_table,
3975 GINT_TO_POINTER(flags_set), ht_data);
3977 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
3978 ht_data->msglist = g_slist_prepend(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum));
3981 ht_data = g_hash_table_lookup(IMAP_FOLDER_ITEM(item)->flags_unset_table,
3982 GINT_TO_POINTER(flags_unset));
3983 if (ht_data == NULL) {
3984 ht_data = g_new0(hashtable_data, 1);
3985 ht_data->session = session;
3986 ht_data->item = IMAP_FOLDER_ITEM(item);
3987 g_hash_table_insert(IMAP_FOLDER_ITEM(item)->flags_unset_table,
3988 GINT_TO_POINTER(flags_unset), ht_data);
3990 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
3991 ht_data->msglist = g_slist_prepend(ht_data->msglist,
3992 GINT_TO_POINTER(msginfo->msgnum));
3995 debug_print("IMAP changing flags\n");
3997 ok = imap_set_message_flags(session, &numlist, flags_set, TRUE);
3998 if (ok != IMAP_SUCCESS) {
3999 unlock_session(session);
4005 ok = imap_set_message_flags(session, &numlist, flags_unset, FALSE);
4006 if (ok != IMAP_SUCCESS) {
4007 unlock_session(session);
4012 msginfo->flags.perm_flags = newflags;
4013 unlock_session(session);
4017 static gint imap_remove_msg(Folder *folder, FolderItem *item, gint uid)
4020 IMAPSession *session;
4022 MsgNumberList numlist;
4024 g_return_val_if_fail(folder != NULL, -1);
4025 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
4026 g_return_val_if_fail(item != NULL, -1);
4028 debug_print("getting session...\n");
4029 session = imap_session_get(folder);
4030 if (!session) return -1;
4032 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
4033 NULL, NULL, NULL, NULL, FALSE);
4034 if (ok != IMAP_SUCCESS) {
4035 unlock_session(session);
4038 numlist.next = NULL;
4039 numlist.data = GINT_TO_POINTER(uid);
4041 ok = imap_set_message_flags
4042 (session, &numlist, IMAP_FLAG_DELETED, TRUE);
4043 if (ok != IMAP_SUCCESS) {
4044 log_warning(LOG_PROTOCOL, _("can't set deleted flags: %d\n"), uid);
4045 unlock_session(session);
4049 if (!session->uidplus) {
4050 ok = imap_cmd_expunge(session);
4054 uidstr = g_strdup_printf("%u", uid);
4055 ok = imap_cmd_expunge(session);
4058 if (ok != IMAP_SUCCESS) {
4059 log_warning(LOG_PROTOCOL, _("can't expunge\n"));
4060 unlock_session(session);
4064 IMAP_FOLDER_ITEM(item)->uid_list = g_slist_remove(
4065 IMAP_FOLDER_ITEM(item)->uid_list, numlist.data);
4066 dir = folder_item_get_path(item);
4067 if (is_dir_exist(dir))
4068 remove_numbered_files(dir, uid, uid);
4070 unlock_session(session);
4071 return IMAP_SUCCESS;
4074 static gint compare_msginfo(gconstpointer a, gconstpointer b)
4076 return ((MsgInfo *)a)->msgnum - ((MsgInfo *)b)->msgnum;
4079 static guint gslist_find_next_num(MsgNumberList **list, guint num)
4083 g_return_val_if_fail(list != NULL, -1);
4085 for (elem = *list; elem != NULL; elem = g_slist_next(elem))
4086 if (GPOINTER_TO_INT(elem->data) >= num)
4089 return elem != NULL ? GPOINTER_TO_INT(elem->data) : (gint)-1;
4093 * NEW and DELETED flags are not syncronized
4094 * - The NEW/RECENT flags in IMAP folders can not really be directly
4095 * modified by Sylpheed
4096 * - The DELETE/DELETED flag in IMAP and Sylpheed don't have the same
4097 * meaning, in IMAP it always removes the messages from the FolderItem
4098 * in Sylpheed it can mean to move the message to trash
4101 typedef struct _get_flags_data {
4104 MsgInfoList *msginfo_list;
4105 GRelation *msgflags;
4106 gboolean full_search;
4110 static /*gint*/ void *imap_get_flags_thread(void *data)
4112 get_flags_data *stuff = (get_flags_data *)data;
4113 Folder *folder = stuff->folder;
4114 FolderItem *fitem = (FolderItem *) stuff->item;
4115 MsgInfoList *msginfo_list = stuff->msginfo_list;
4116 GRelation *msgflags = stuff->msgflags;
4118 carray * lep_uidtab;
4119 IMAPSession *session;
4122 GHashTable *flags_hash = NULL;
4123 gboolean full_search = stuff->full_search;
4124 GSList *sorted_list = NULL;
4125 GSList *unseen = NULL, *answered = NULL, *flagged = NULL, *deleted = NULL;
4126 GSList *p_unseen, *p_answered, *p_flagged, *p_deleted;
4127 GSList *seq_list, *cur;
4128 gboolean reverse_seen = FALSE;
4129 gboolean selected_folder;
4130 gint exists_cnt, unseen_cnt;
4132 session = imap_session_get(folder);
4133 if (session == NULL) {
4135 return GINT_TO_POINTER(-1);
4137 selected_folder = (session->mbox != NULL) &&
4138 (!strcmp(session->mbox, fitem->path));
4140 if (!selected_folder) {
4141 ok = imap_select(session, IMAP_FOLDER(folder), fitem->path,
4142 &exists_cnt, NULL, &unseen_cnt, NULL, TRUE);
4143 if (ok != IMAP_SUCCESS) {
4145 unlock_session(session);
4146 return GINT_TO_POINTER(-1);
4149 if (unseen_cnt > exists_cnt / 2)
4150 reverse_seen = TRUE;
4153 if (fitem->unread_msgs > fitem->total_msgs / 2)
4154 reverse_seen = TRUE;
4157 sorted_list = g_slist_sort(g_slist_copy(msginfo_list), compare_msginfo);
4159 seq_list = imap_get_lep_set_from_msglist(msginfo_list);
4161 struct mailimap_set * set;
4162 set = mailimap_set_new_interval(1, 0);
4163 seq_list = g_slist_append(NULL, set);
4166 if (folder->account && folder->account->low_bandwidth) {
4167 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
4168 struct mailimap_set * imapset;
4169 clist * lep_uidlist;
4172 imapset = cur->data;
4174 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SEEN,
4175 full_search ? NULL:imapset, &lep_uidlist);
4178 r = imap_threaded_search(folder,
4179 IMAP_SEARCH_TYPE_UNSEEN,
4180 full_search ? NULL:imapset, &lep_uidlist);
4182 if (r == MAILIMAP_NO_ERROR) {
4185 uidlist = imap_uid_list_from_lep(lep_uidlist);
4186 mailimap_search_result_free(lep_uidlist);
4188 unseen = g_slist_concat(unseen, uidlist);
4191 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_FLAGGED,
4192 full_search ? NULL:imapset, &lep_uidlist);
4193 if (r == MAILIMAP_NO_ERROR) {
4196 uidlist = imap_uid_list_from_lep(lep_uidlist);
4197 mailimap_search_result_free(lep_uidlist);
4199 flagged = g_slist_concat(flagged, uidlist);
4202 if (fitem->opened || fitem->processing_pending || fitem == folder->inbox) {
4203 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_ANSWERED,
4204 full_search ? NULL:imapset, &lep_uidlist);
4205 if (r == MAILIMAP_NO_ERROR) {
4208 uidlist = imap_uid_list_from_lep(lep_uidlist);
4209 mailimap_search_result_free(lep_uidlist);
4211 answered = g_slist_concat(answered, uidlist);
4214 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_DELETED,
4215 full_search ? NULL:imapset, &lep_uidlist);
4216 if (r == MAILIMAP_NO_ERROR) {
4219 uidlist = imap_uid_list_from_lep(lep_uidlist);
4220 mailimap_search_result_free(lep_uidlist);
4222 deleted = g_slist_concat(deleted, uidlist);
4227 p_answered = answered;
4228 p_flagged = flagged;
4229 p_deleted = deleted;
4232 r = imap_threaded_fetch_uid_flags(folder, 1, &lep_uidtab);
4233 if (r == MAILIMAP_NO_ERROR) {
4234 flags_hash = g_hash_table_new_full(g_int_hash, g_int_equal, free, NULL);
4235 imap_flags_hash_from_lep_uid_flags_tab(lep_uidtab, flags_hash);
4236 imap_fetch_uid_flags_list_free(lep_uidtab);
4239 for (elem = sorted_list; elem != NULL; elem = g_slist_next(elem)) {
4241 MsgPermFlags flags, oldflags;
4244 msginfo = (MsgInfo *) elem->data;
4245 flags = msginfo->flags.perm_flags;
4246 wasnew = (flags & MSG_NEW);
4247 oldflags = flags & ~(MSG_NEW|MSG_UNREAD|MSG_REPLIED|MSG_MARKED|MSG_DELETED);
4249 if (folder->account && folder->account->low_bandwidth) {
4250 if (fitem->opened || fitem->processing_pending || fitem == folder->inbox) {
4251 flags &= ~((reverse_seen ? 0 : MSG_UNREAD | MSG_NEW) | MSG_REPLIED | MSG_MARKED);
4253 flags &= ~((reverse_seen ? 0 : MSG_UNREAD | MSG_NEW | MSG_MARKED));
4256 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
4257 if (gslist_find_next_num(&p_unseen, msginfo->msgnum) == msginfo->msgnum) {
4258 if (!reverse_seen) {
4259 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
4261 flags &= ~(MSG_UNREAD | MSG_NEW);
4265 if (gslist_find_next_num(&p_flagged, msginfo->msgnum) == msginfo->msgnum)
4266 flags |= MSG_MARKED;
4268 flags &= ~MSG_MARKED;
4270 if (fitem->opened || fitem->processing_pending || fitem == folder->inbox) {
4271 if (gslist_find_next_num(&p_answered, msginfo->msgnum) == msginfo->msgnum)
4272 flags |= MSG_REPLIED;
4274 flags &= ~MSG_REPLIED;
4275 if (gslist_find_next_num(&p_deleted, msginfo->msgnum) == msginfo->msgnum)
4276 flags |= MSG_DELETED;
4278 flags &= ~MSG_DELETED;
4281 if (flags_hash != NULL) {
4284 puid = malloc(sizeof(* puid));
4285 * puid = msginfo->msgnum;
4287 flags = GPOINTER_TO_INT(g_hash_table_lookup(flags_hash, puid));
4291 if ((flags & MSG_UNREAD) == 0)
4298 g_relation_insert(msgflags, msginfo, GINT_TO_POINTER(flags));
4302 g_hash_table_destroy(flags_hash);
4304 imap_lep_set_free(seq_list);
4305 g_slist_free(flagged);
4306 g_slist_free(deleted);
4307 g_slist_free(answered);
4308 g_slist_free(unseen);
4309 g_slist_free(sorted_list);
4311 unlock_session(session);
4313 return GINT_TO_POINTER(0);
4316 static gint imap_get_flags(Folder *folder, FolderItem *item,
4317 MsgInfoList *msginfo_list, GRelation *msgflags)
4320 get_flags_data *data = g_new0(get_flags_data, 1);
4322 data->folder = folder;
4324 data->msginfo_list = msginfo_list;
4325 data->msgflags = msgflags;
4326 data->full_search = FALSE;
4328 GSList *tmp = NULL, *cur;
4330 if (prefs_common.work_offline &&
4331 !inc_offline_should_override(FALSE,
4332 _("Claws Mail needs network access in order "
4333 "to access the IMAP server."))) {
4338 tmp = folder_item_get_msg_list(item);
4340 if (g_slist_length(tmp) <= g_slist_length(msginfo_list))
4341 data->full_search = TRUE;
4343 for (cur = tmp; cur; cur = cur->next)
4344 procmsg_msginfo_free((MsgInfo *)cur->data);
4348 result = GPOINTER_TO_INT(imap_get_flags_thread(data));
4355 static gboolean process_flags(gpointer key, gpointer value, gpointer user_data)
4357 gboolean flags_set = GPOINTER_TO_INT(user_data);
4358 gint flags_value = GPOINTER_TO_INT(key);
4359 hashtable_data *data = (hashtable_data *)value;
4360 IMAPFolderItem *_item = data->item;
4361 FolderItem *item = (FolderItem *)_item;
4362 gint ok = IMAP_ERROR;
4363 IMAPSession *session = NULL;
4365 debug_print("getting session...\n");
4366 session = imap_session_get(item->folder);
4368 data->msglist = g_slist_reverse(data->msglist);
4370 debug_print("IMAP %ssetting flags to %d for %d messages\n",
4373 g_slist_length(data->msglist));
4376 ok = imap_select(session, IMAP_FOLDER(item->folder), item->path,
4377 NULL, NULL, NULL, NULL, FALSE);
4379 if (ok == IMAP_SUCCESS) {
4380 imap_set_message_flags(data->session, data->msglist, flags_value, flags_set);
4382 g_warning("can't select mailbox %s\n", item->path);
4385 unlock_session(session);
4386 g_slist_free(data->msglist);
4391 static void process_hashtable(IMAPFolderItem *item)
4393 if (item->flags_set_table) {
4394 g_hash_table_foreach_remove(item->flags_set_table, process_flags, GINT_TO_POINTER(TRUE));
4395 g_hash_table_destroy(item->flags_set_table);
4396 item->flags_set_table = NULL;
4398 if (item->flags_unset_table) {
4399 g_hash_table_foreach_remove(item->flags_unset_table, process_flags, GINT_TO_POINTER(FALSE));
4400 g_hash_table_destroy(item->flags_unset_table);
4401 item->flags_unset_table = NULL;
4405 static void imap_set_batch (Folder *folder, FolderItem *_item, gboolean batch)
4407 IMAPFolderItem *item = (IMAPFolderItem *)_item;
4409 g_return_if_fail(item != NULL);
4411 if (item->batching == batch)
4415 item->batching = TRUE;
4416 debug_print("IMAP switching to batch mode\n");
4417 if (!item->flags_set_table) {
4418 item->flags_set_table = g_hash_table_new(NULL, g_direct_equal);
4420 if (!item->flags_unset_table) {
4421 item->flags_unset_table = g_hash_table_new(NULL, g_direct_equal);
4424 debug_print("IMAP switching away from batch mode\n");
4426 process_hashtable(item);
4427 item->batching = FALSE;
4433 /* data types conversion libetpan <-> claws */
4437 #define ETPAN_IMAP_MB_MARKED 1
4438 #define ETPAN_IMAP_MB_UNMARKED 2
4439 #define ETPAN_IMAP_MB_NOSELECT 4
4440 #define ETPAN_IMAP_MB_NOINFERIORS 8
4442 static int imap_flags_to_flags(struct mailimap_mbx_list_flags * imap_flags)
4448 if (imap_flags->mbf_type == MAILIMAP_MBX_LIST_FLAGS_SFLAG) {
4449 switch (imap_flags->mbf_sflag) {
4450 case MAILIMAP_MBX_LIST_SFLAG_MARKED:
4451 flags |= ETPAN_IMAP_MB_MARKED;
4453 case MAILIMAP_MBX_LIST_SFLAG_NOSELECT:
4454 flags |= ETPAN_IMAP_MB_NOSELECT;
4456 case MAILIMAP_MBX_LIST_SFLAG_UNMARKED:
4457 flags |= ETPAN_IMAP_MB_UNMARKED;
4462 for(cur = clist_begin(imap_flags->mbf_oflags) ; cur != NULL ;
4463 cur = clist_next(cur)) {
4464 struct mailimap_mbx_list_oflag * oflag;
4466 oflag = clist_content(cur);
4468 switch (oflag->of_type) {
4469 case MAILIMAP_MBX_LIST_OFLAG_NOINFERIORS:
4470 flags |= ETPAN_IMAP_MB_NOINFERIORS;
4478 static GSList * imap_list_from_lep(IMAPFolder * folder,
4479 clist * list, const gchar * real_path, gboolean all)
4482 GSList * item_list = NULL, *llast = NULL;
4484 for(iter = clist_begin(list) ; iter != NULL ;
4485 iter = clist_next(iter)) {
4486 struct mailimap_mailbox_list * mb;
4494 FolderItem *new_item;
4496 mb = clist_content(iter);
4502 if (mb->mb_flag != NULL)
4503 flags = imap_flags_to_flags(mb->mb_flag);
4505 delimiter = mb->mb_delimiter;
4508 dup_name = strdup(name);
4509 if (delimiter != '\0')
4510 subst_char(dup_name, delimiter, '/');
4512 base = g_path_get_basename(dup_name);
4513 if (base[0] == '.') {
4518 if (!all && path_cmp(name, real_path) == 0) {
4524 if (!all && dup_name[strlen(dup_name)-1] == '/') {
4525 dup_name[strlen(dup_name)-1] = '\0';
4528 loc_name = imap_modified_utf7_to_utf8(base);
4529 loc_path = imap_modified_utf7_to_utf8(dup_name);
4531 new_item = folder_item_new(FOLDER(folder), loc_name, loc_path);
4532 if ((flags & ETPAN_IMAP_MB_NOINFERIORS) != 0)
4533 new_item->no_sub = TRUE;
4534 if (strcmp(dup_name, "INBOX") != 0 &&
4535 ((flags & ETPAN_IMAP_MB_NOSELECT) != 0))
4536 new_item->no_select = TRUE;
4538 if (item_list == NULL)
4539 llast = item_list = g_slist_append(item_list, new_item);
4541 llast = g_slist_append(llast, new_item);
4542 llast = llast->next;
4544 debug_print("folder '%s' found.\n", loc_path);
4555 static GSList * imap_get_lep_set_from_numlist(MsgNumberList *numlist)
4557 GSList *sorted_list, *cur;
4558 guint first, last, next;
4559 GSList *ret_list = NULL, *llast = NULL;
4561 struct mailimap_set * current_set;
4562 unsigned int item_count;
4564 if (numlist == NULL)
4568 current_set = mailimap_set_new_empty();
4570 sorted_list = g_slist_copy(numlist);
4571 sorted_list = g_slist_sort(sorted_list, g_int_compare);
4573 first = GPOINTER_TO_INT(sorted_list->data);
4576 for (cur = sorted_list; cur != NULL; cur = g_slist_next(cur)) {
4577 if (GPOINTER_TO_INT(cur->data) == 0)
4582 last = GPOINTER_TO_INT(cur->data);
4584 next = GPOINTER_TO_INT(cur->next->data);
4588 if (last + 1 != next || next == 0) {
4590 struct mailimap_set_item * item;
4591 item = mailimap_set_item_new(first, last);
4592 mailimap_set_add(current_set, item);
4597 if (count >= IMAP_SET_MAX_COUNT) {
4598 if (ret_list == NULL)
4599 llast = ret_list = g_slist_append(ret_list,
4602 llast = g_slist_append(llast, current_set);
4603 llast = llast->next;
4605 current_set = mailimap_set_new_empty();
4612 if (clist_count(current_set->set_list) > 0) {
4613 ret_list = g_slist_append(ret_list,
4617 g_slist_free(sorted_list);
4622 static GSList * imap_get_lep_set_from_msglist(MsgInfoList *msglist)
4624 MsgNumberList *numlist = NULL;
4628 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
4629 MsgInfo *msginfo = (MsgInfo *) cur->data;
4631 numlist = g_slist_prepend(numlist, GINT_TO_POINTER(msginfo->msgnum));
4633 numlist = g_slist_reverse(numlist);
4634 seq_list = imap_get_lep_set_from_numlist(numlist);
4635 g_slist_free(numlist);
4640 static GSList * imap_uid_list_from_lep(clist * list)
4647 for(iter = clist_begin(list) ; iter != NULL ;
4648 iter = clist_next(iter)) {
4651 puid = clist_content(iter);
4652 result = g_slist_prepend(result, GINT_TO_POINTER(* puid));
4655 result = g_slist_reverse(result);
4659 static GSList * imap_uid_list_from_lep_tab(carray * list)
4666 for(i = 0 ; i < carray_count(list) ; i ++) {
4669 puid = carray_get(list, i);
4670 result = g_slist_prepend(result, GINT_TO_POINTER(* puid));
4672 result = g_slist_reverse(result);
4676 static void imap_flags_hash_from_lep_uid_flags_tab(carray * list,
4684 for(i = 0 ; i < carray_count(list) ; i += 2) {
4689 puid = carray_get(list, i);
4690 pflags = carray_get(list, i + 1);
4691 pguid = malloc(sizeof(* pguid));
4694 g_hash_table_insert(hash, pguid, GINT_TO_POINTER(* pflags));
4698 static MsgInfo *imap_envelope_from_lep(struct imap_fetch_env_info * info,
4701 MsgInfo *msginfo = NULL;
4704 MsgFlags flags = {0, 0};
4706 if (info->headers == NULL)
4709 MSG_SET_TMP_FLAGS(flags, MSG_IMAP);
4710 if (folder_has_parent_of_type(item, F_QUEUE)) {
4711 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
4712 } else if (folder_has_parent_of_type(item, F_DRAFT)) {
4713 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
4715 flags.perm_flags = info->flags;
4719 msginfo = procheader_parse_str(info->headers, flags, FALSE, FALSE);
4722 msginfo->msgnum = uid;
4723 msginfo->size = size;
4729 static void imap_lep_set_free(GSList *seq_list)
4733 for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) {
4734 struct mailimap_set * imapset;
4736 imapset = cur->data;
4737 mailimap_set_free(imapset);
4739 g_slist_free(seq_list);
4742 static struct mailimap_flag_list * imap_flag_to_lep(IMAPFlags flags)
4744 struct mailimap_flag_list * flag_list;
4746 flag_list = mailimap_flag_list_new_empty();
4748 if (IMAP_IS_SEEN(flags))
4749 mailimap_flag_list_add(flag_list,
4750 mailimap_flag_new_seen());
4751 if (IMAP_IS_ANSWERED(flags))
4752 mailimap_flag_list_add(flag_list,
4753 mailimap_flag_new_answered());
4754 if (IMAP_IS_FLAGGED(flags))
4755 mailimap_flag_list_add(flag_list,
4756 mailimap_flag_new_flagged());
4757 if (IMAP_IS_DELETED(flags))
4758 mailimap_flag_list_add(flag_list,
4759 mailimap_flag_new_deleted());
4760 if (IMAP_IS_DRAFT(flags))
4761 mailimap_flag_list_add(flag_list,
4762 mailimap_flag_new_draft());
4767 guint imap_folder_get_refcnt(Folder *folder)
4769 return ((IMAPFolder *)folder)->refcnt;
4772 void imap_folder_ref(Folder *folder)
4774 ((IMAPFolder *)folder)->refcnt++;
4777 void imap_disconnect_all(void)
4780 for (list = account_get_list(); list != NULL; list = list->next) {
4781 PrefsAccount *account = list->data;
4782 if (account->protocol == A_IMAP4) {
4783 RemoteFolder *folder = (RemoteFolder *)account->folder;
4784 if (folder && folder->session) {
4785 IMAPSession *session = (IMAPSession *)folder->session;
4786 imap_threaded_disconnect(FOLDER(folder));
4787 SESSION(session)->state = SESSION_DISCONNECTED;
4788 session_destroy(SESSION(session));
4789 folder->session = NULL;
4795 void imap_folder_unref(Folder *folder)
4797 if (((IMAPFolder *)folder)->refcnt > 0)
4798 ((IMAPFolder *)folder)->refcnt--;
4801 void imap_cancel_all(void)
4806 folderlist = folder_get_list();
4807 for (cur = folderlist; cur != NULL; cur = g_list_next(cur)) {
4808 Folder *folder = (Folder *) cur->data;
4810 if (folder->klass == &imap_class) {
4811 if (imap_is_busy(folder)) {
4812 IMAPSession *imap_session;
4813 RemoteFolder *rfolder;
4815 fprintf(stderr, "cancelled\n");
4816 imap_threaded_cancel(folder);
4817 rfolder = (RemoteFolder *) folder;
4818 imap_session = (IMAPSession *) rfolder->session;
4820 imap_session->cancelled = 1;
4826 gboolean imap_cancel_all_enabled(void)
4831 folderlist = folder_get_list();
4832 for (cur = folderlist; cur != NULL; cur = g_list_next(cur)) {
4833 Folder *folder = (Folder *) cur->data;
4835 if (folder->klass == &imap_class) {
4836 if (imap_is_busy(folder)) {
4845 static gboolean imap_is_busy(Folder *folder)
4847 IMAPSession *imap_session;
4848 RemoteFolder *rfolder;
4850 rfolder = (RemoteFolder *) folder;
4851 imap_session = (IMAPSession *) rfolder->session;
4852 if (imap_session == NULL)
4855 return imap_session->busy;
4858 #else /* HAVE_LIBETPAN */
4860 static FolderClass imap_class;
4862 static XMLTag *imap_item_get_xml(Folder *folder, FolderItem *item);
4863 static void imap_item_set_xml(Folder *folder, FolderItem *item, XMLTag *tag);
4865 static Folder *imap_folder_new (const gchar *name,
4868 static gboolean missing_imap_warning = TRUE;
4869 if (missing_imap_warning) {
4870 missing_imap_warning = FALSE;
4872 _("You have one or more IMAP accounts "
4873 "defined. However this version of "
4874 "Claws Mail has been built without "
4875 "IMAP support; your IMAP account(s) are "
4877 "You probably need to "
4878 "install libetpan and recompile "
4883 static gint imap_create_tree (Folder *folder)
4887 static FolderItem *imap_create_folder (Folder *folder,
4893 static gint imap_rename_folder (Folder *folder,
4900 gchar imap_get_path_separator_for_item(FolderItem *item)
4905 FolderClass *imap_get_class(void)
4907 if (imap_class.idstr == NULL) {
4908 imap_class.type = F_IMAP;
4909 imap_class.idstr = "imap";
4910 imap_class.uistr = "IMAP4";
4912 imap_class.new_folder = imap_folder_new;
4913 imap_class.create_tree = imap_create_tree;
4914 imap_class.create_folder = imap_create_folder;
4915 imap_class.rename_folder = imap_rename_folder;
4917 imap_class.set_xml = folder_set_xml;
4918 imap_class.get_xml = folder_get_xml;
4919 imap_class.item_set_xml = imap_item_set_xml;
4920 imap_class.item_get_xml = imap_item_get_xml;
4921 /* nothing implemented */
4927 void imap_disconnect_all(void)
4931 gint imap_scan_tree_real(Folder *folder, gboolean subs_only)
4936 gint imap_subscribe(Folder *folder, FolderItem *item, gchar *rpath, gboolean sub)
4941 GList * imap_scan_subtree(Folder *folder, FolderItem *item, gboolean unsubs_only, gboolean recursive)
4946 void imap_cache_msg(FolderItem *item, gint msgnum)
4950 void imap_cancel_all(void)
4954 gboolean imap_cancel_all_enabled(void)
4961 void imap_synchronise(FolderItem *item, gint days)
4963 #ifdef HAVE_LIBETPAN
4964 if (IMAP_FOLDER_ITEM(item)->last_sync == IMAP_FOLDER_ITEM(item)->last_change) {
4965 debug_print("%s already synced\n", item->path?item->path:item->name);
4968 debug_print("syncing %s\n", item->path?item->path:item->name);
4969 imap_gtk_synchronise(item, days);
4970 IMAP_FOLDER_ITEM(item)->last_sync = IMAP_FOLDER_ITEM(item)->last_change;
4974 static void imap_item_set_xml(Folder *folder, FolderItem *item, XMLTag *tag)
4976 #ifdef HAVE_LIBETPAN
4979 folder_item_set_xml(folder, item, tag);
4981 #ifdef HAVE_LIBETPAN
4982 for (cur = tag->attr; cur != NULL; cur = g_list_next(cur)) {
4983 XMLAttr *attr = (XMLAttr *) cur->data;
4985 if (!attr || !attr->name || !attr->value) continue;
4986 if (!strcmp(attr->name, "uidnext"))
4987 IMAP_FOLDER_ITEM(item)->uid_next = atoi(attr->value);
4988 if (!strcmp(attr->name, "last_sync"))
4989 IMAP_FOLDER_ITEM(item)->last_sync = atoi(attr->value);
4990 if (!strcmp(attr->name, "last_change"))
4991 IMAP_FOLDER_ITEM(item)->last_change = atoi(attr->value);
4993 if (IMAP_FOLDER_ITEM(item)->last_change == 0)
4994 IMAP_FOLDER_ITEM(item)->last_change = time(NULL);
4998 static XMLTag *imap_item_get_xml(Folder *folder, FolderItem *item)
5002 tag = folder_item_get_xml(folder, item);
5004 #ifdef HAVE_LIBETPAN
5005 xml_tag_add_attr(tag, xml_attr_new_int("uidnext",
5006 IMAP_FOLDER_ITEM(item)->uid_next));
5007 xml_tag_add_attr(tag, xml_attr_new_int("last_sync",
5008 IMAP_FOLDER_ITEM(item)->last_sync));
5009 xml_tag_add_attr(tag, xml_attr_new_int("last_change",
5010 IMAP_FOLDER_ITEM(item)->last_change));