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_add_msgs(Folder *folder, FolderItem *dest, GSList *file_list,
1179 GRelation *relation)
1182 IMAPSession *session;
1183 guint32 last_uid = 0;
1185 MsgFileInfo *fileinfo;
1187 gint curnum = 0, total = 0;
1190 g_return_val_if_fail(folder != NULL, -1);
1191 g_return_val_if_fail(dest != NULL, -1);
1192 g_return_val_if_fail(file_list != NULL, -1);
1194 debug_print("getting session...\n");
1195 session = imap_session_get(folder);
1199 destdir = imap_get_real_path(session, IMAP_FOLDER(folder), dest->path);
1201 statusbar_print_all(_("Adding messages..."));
1202 total = g_slist_length(file_list);
1203 for (cur = file_list; cur != NULL; cur = cur->next) {
1204 IMAPFlags iflags = 0;
1205 guint32 new_uid = 0;
1206 gchar *real_file = NULL;
1207 fileinfo = (MsgFileInfo *)cur->data;
1209 statusbar_progress_all(curnum, total, total < 10 ? 1:10);
1212 if (fileinfo->flags) {
1213 if (MSG_IS_MARKED(*fileinfo->flags))
1214 iflags |= IMAP_FLAG_FLAGGED;
1215 if (MSG_IS_REPLIED(*fileinfo->flags))
1216 iflags |= IMAP_FLAG_ANSWERED;
1217 if (!MSG_IS_UNREAD(*fileinfo->flags))
1218 iflags |= IMAP_FLAG_SEEN;
1221 if (real_file == NULL)
1222 real_file = g_strdup(fileinfo->file);
1224 if (folder_has_parent_of_type(dest, F_QUEUE) ||
1225 folder_has_parent_of_type(dest, F_OUTBOX) ||
1226 folder_has_parent_of_type(dest, F_DRAFT) ||
1227 folder_has_parent_of_type(dest, F_TRASH))
1228 iflags |= IMAP_FLAG_SEEN;
1230 ok = imap_cmd_append(session, destdir, real_file, iflags,
1233 if (ok != IMAP_SUCCESS) {
1234 g_warning("can't append message %s\n", real_file);
1237 unlock_session(session);
1238 statusbar_progress_all(0,0,0);
1239 statusbar_pop_all();
1242 debug_print("appended new message as %d\n", new_uid);
1243 /* put the local file in the imapcache, so that we don't
1244 * have to fetch it back later. */
1246 gchar *cache_path = folder_item_get_path(dest);
1247 if (!is_dir_exist(cache_path))
1248 make_dir_hier(cache_path);
1249 if (is_dir_exist(cache_path)) {
1250 gchar *cache_file = g_strconcat(
1251 cache_path, G_DIR_SEPARATOR_S,
1252 itos(new_uid), NULL);
1253 copy_file(real_file, cache_file, TRUE);
1254 debug_print("copied to cache: %s\n", cache_file);
1261 if (relation != NULL)
1262 g_relation_insert(relation, fileinfo->msginfo != NULL ?
1263 (gpointer) fileinfo->msginfo : (gpointer) fileinfo,
1264 GINT_TO_POINTER(dest->last_num + 1));
1266 new_uid = dest->last_num+1;
1268 if (last_uid < new_uid) {
1274 statusbar_progress_all(0,0,0);
1275 statusbar_pop_all();
1277 imap_cmd_expunge(session);
1278 unlock_session(session);
1285 static gint imap_do_copy_msgs(Folder *folder, FolderItem *dest,
1286 MsgInfoList *msglist, GRelation *relation)
1290 GSList *seq_list, *cur;
1292 IMAPSession *session;
1293 gint ok = IMAP_SUCCESS;
1294 GRelation *uid_mapping;
1296 gboolean single = FALSE;
1298 g_return_val_if_fail(folder != NULL, -1);
1299 g_return_val_if_fail(dest != NULL, -1);
1300 g_return_val_if_fail(msglist != NULL, -1);
1302 debug_print("getting session...\n");
1303 session = imap_session_get(folder);
1309 msginfo = (MsgInfo *)msglist->data;
1310 if (msglist->next == NULL)
1312 src = msginfo->folder;
1314 g_warning("the src folder is identical to the dest.\n");
1315 unlock_session(session);
1319 if (src->folder != dest->folder) {
1320 GSList *infolist = NULL, *cur;
1322 for (cur = msglist; cur; cur = cur->next) {
1323 msginfo = (MsgInfo *)cur->data;
1324 MsgFileInfo *fileinfo = g_new0(MsgFileInfo, 1);
1325 fileinfo->file = procmsg_get_message_file(msginfo);
1326 fileinfo->flags = &(msginfo->flags);
1327 infolist = g_slist_prepend(infolist, fileinfo);
1329 infolist = g_slist_reverse(infolist);
1330 unlock_session(session);
1331 res = folder_item_add_msgs(dest, infolist, FALSE);
1332 for (cur = infolist; cur; cur = cur->next) {
1333 MsgFileInfo *info = (MsgFileInfo *)cur->data;
1337 g_slist_free(infolist);
1341 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
1342 NULL, NULL, NULL, NULL, FALSE);
1343 if (ok != IMAP_SUCCESS) {
1344 unlock_session(session);
1348 destdir = imap_get_real_path(session, IMAP_FOLDER(folder), dest->path);
1349 seq_list = imap_get_lep_set_from_msglist(msglist);
1350 uid_mapping = g_relation_new(2);
1351 g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
1353 statusbar_print_all(_("Copying messages..."));
1354 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
1355 struct mailimap_set * seq_set;
1356 struct mailimap_set * source = NULL;
1357 struct mailimap_set * dest = NULL;
1358 seq_set = cur->data;
1360 debug_print("Copying messages from %s to %s ...\n",
1361 src->path, destdir);
1363 ok = imap_cmd_copy(session, seq_set, destdir, uid_mapping,
1366 if (ok == IMAP_SUCCESS) {
1367 if (single && relation && source && dest) {
1368 clistiter *l = clist_begin(source->set_list);
1369 struct mailimap_set_item *i = (struct mailimap_set_item *)clist_content(l);
1370 int snum = i->set_first;
1372 l = clist_begin(dest->set_list);
1373 i = (struct mailimap_set_item *)clist_content(l);
1374 dnum = i->set_first;
1375 g_relation_insert(uid_mapping, GINT_TO_POINTER(snum),
1376 GINT_TO_POINTER(dnum));
1382 mailimap_set_free(source);
1384 mailimap_set_free(dest);
1386 if (ok != IMAP_SUCCESS) {
1387 g_relation_destroy(uid_mapping);
1388 imap_lep_set_free(seq_list);
1389 unlock_session(session);
1390 statusbar_pop_all();
1395 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
1396 MsgInfo *msginfo = (MsgInfo *)cur->data;
1399 tuples = g_relation_select(uid_mapping,
1400 GINT_TO_POINTER(msginfo->msgnum),
1402 if (tuples->len > 0) {
1403 gint num = GPOINTER_TO_INT(g_tuples_index(tuples, 0, 1));
1404 g_relation_insert(relation, msginfo,
1405 GINT_TO_POINTER(num));
1408 debug_print("copied new message as %d\n", num);
1409 /* put the local file in the imapcache, so that we don't
1410 * have to fetch it back later. */
1412 gchar *cache_path = folder_item_get_path(msginfo->folder);
1413 gchar *real_file = g_strconcat(
1414 cache_path, G_DIR_SEPARATOR_S,
1415 itos(msginfo->msgnum), NULL);
1416 gchar *cache_file = NULL;
1418 cache_path = folder_item_get_path(dest);
1419 cache_file = g_strconcat(
1420 cache_path, G_DIR_SEPARATOR_S,
1422 if (!is_dir_exist(cache_path))
1423 make_dir_hier(cache_path);
1424 if (is_file_exist(real_file) && is_dir_exist(cache_path)) {
1425 copy_file(real_file, cache_file, TRUE);
1426 debug_print("copied to cache: %s\n", cache_file);
1433 g_relation_insert(relation, msginfo,
1434 GINT_TO_POINTER(0));
1435 g_tuples_destroy(tuples);
1437 statusbar_pop_all();
1439 g_relation_destroy(uid_mapping);
1440 imap_lep_set_free(seq_list);
1444 IMAP_FOLDER_ITEM(dest)->lastuid = 0;
1445 IMAP_FOLDER_ITEM(dest)->uid_next = 0;
1446 g_slist_free(IMAP_FOLDER_ITEM(dest)->uid_list);
1447 IMAP_FOLDER_ITEM(dest)->uid_list = NULL;
1449 unlock_session(session);
1450 if (ok == IMAP_SUCCESS)
1456 static gint imap_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1460 g_return_val_if_fail(msginfo != NULL, -1);
1462 msglist.data = msginfo;
1463 msglist.next = NULL;
1465 return imap_copy_msgs(folder, dest, &msglist, NULL);
1468 static gint imap_copy_msgs(Folder *folder, FolderItem *dest,
1469 MsgInfoList *msglist, GRelation *relation)
1474 g_return_val_if_fail(folder != NULL, -1);
1475 g_return_val_if_fail(dest != NULL, -1);
1476 g_return_val_if_fail(msglist != NULL, -1);
1478 msginfo = (MsgInfo *)msglist->data;
1479 g_return_val_if_fail(msginfo->folder != NULL, -1);
1481 ret = imap_do_copy_msgs(folder, dest, msglist, relation);
1486 static gint imap_do_remove_msgs(Folder *folder, FolderItem *dest,
1487 MsgInfoList *msglist, GRelation *relation)
1489 gchar *destdir, *dir;
1490 GSList *numlist = NULL, *cur;
1492 IMAPSession *session;
1493 gint ok = IMAP_SUCCESS;
1494 GRelation *uid_mapping;
1496 g_return_val_if_fail(folder != NULL, -1);
1497 g_return_val_if_fail(dest != NULL, -1);
1498 g_return_val_if_fail(msglist != NULL, -1);
1500 debug_print("getting session...\n");
1501 session = imap_session_get(folder);
1506 msginfo = (MsgInfo *)msglist->data;
1508 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
1509 NULL, NULL, NULL, NULL, FALSE);
1510 if (ok != IMAP_SUCCESS) {
1511 unlock_session(session);
1515 destdir = imap_get_real_path(session, IMAP_FOLDER(folder), dest->path);
1516 for (cur = msglist; cur; cur = cur->next) {
1517 msginfo = (MsgInfo *)cur->data;
1518 if (!MSG_IS_DELETED(msginfo->flags))
1519 numlist = g_slist_prepend(numlist, GINT_TO_POINTER(msginfo->msgnum));
1521 numlist = g_slist_reverse(numlist);
1523 uid_mapping = g_relation_new(2);
1524 g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
1526 ok = imap_set_message_flags
1527 (session, numlist, IMAP_FLAG_DELETED, TRUE);
1528 if (ok != IMAP_SUCCESS) {
1529 log_warning(LOG_PROTOCOL, _("can't set deleted flags\n"));
1530 unlock_session(session);
1533 ok = imap_cmd_expunge(session);
1534 if (ok != IMAP_SUCCESS) {
1535 log_warning(LOG_PROTOCOL, _("can't expunge\n"));
1536 unlock_session(session);
1540 dir = folder_item_get_path(msginfo->folder);
1541 if (is_dir_exist(dir)) {
1542 for (cur = msglist; cur; cur = cur->next) {
1543 msginfo = (MsgInfo *)cur->data;
1544 remove_numbered_files(dir, msginfo->msgnum, msginfo->msgnum);
1549 g_relation_destroy(uid_mapping);
1550 g_slist_free(numlist);
1553 unlock_session(session);
1554 if (ok == IMAP_SUCCESS)
1560 static gint imap_remove_msgs(Folder *folder, FolderItem *dest,
1561 MsgInfoList *msglist, GRelation *relation)
1565 g_return_val_if_fail(folder != NULL, -1);
1566 g_return_val_if_fail(dest != NULL, -1);
1567 if (msglist == NULL)
1570 msginfo = (MsgInfo *)msglist->data;
1571 g_return_val_if_fail(msginfo->folder != NULL, -1);
1573 return imap_do_remove_msgs(folder, dest, msglist, relation);
1576 static gint imap_remove_all_msg(Folder *folder, FolderItem *item)
1578 GSList *list = folder_item_get_msg_list(item);
1579 gint res = imap_remove_msgs(folder, item, list, NULL);
1580 procmsg_msg_list_free(list);
1584 static gboolean imap_is_msg_changed(Folder *folder, FolderItem *item,
1587 /* TODO: properly implement this method */
1591 static gint imap_close(Folder *folder, FolderItem *item)
1596 gint imap_scan_tree_real(Folder *folder, gboolean subs_only)
1598 FolderItem *item = NULL;
1599 IMAPSession *session;
1600 gchar *root_folder = NULL;
1602 g_return_val_if_fail(folder != NULL, -1);
1603 g_return_val_if_fail(folder->account != NULL, -1);
1605 debug_print("getting session...\n");
1606 session = imap_session_get(folder);
1608 if (!folder->node) {
1609 folder_tree_destroy(folder);
1610 item = folder_item_new(folder, folder->name, NULL);
1611 item->folder = folder;
1612 folder->node = item->node = g_node_new(item);
1617 if (folder->account->imap_dir && *folder->account->imap_dir) {
1622 Xstrdup_a(root_folder, folder->account->imap_dir, {unlock_session(session);return -1;});
1623 extract_quote(root_folder, '"');
1624 subst_char(root_folder,
1625 imap_get_path_separator(session, IMAP_FOLDER(folder),
1628 strtailchomp(root_folder, '/');
1629 real_path = imap_get_real_path
1630 (session, IMAP_FOLDER(folder), root_folder);
1631 debug_print("IMAP root directory: %s\n", real_path);
1633 /* check if root directory exist */
1635 r = imap_threaded_list(session->folder, "", real_path,
1637 if ((r != MAILIMAP_NO_ERROR) || (clist_count(lep_list) == 0)) {
1638 if (!folder->node) {
1639 item = folder_item_new(folder, folder->name, NULL);
1640 item->folder = folder;
1641 folder->node = item->node = g_node_new(item);
1643 unlock_session(session);
1646 mailimap_list_result_free(lep_list);
1652 item = FOLDER_ITEM(folder->node->data);
1654 if (item && !item->path && root_folder) {
1655 item->path = g_strdup(root_folder);
1658 if (!item || ((item->path || root_folder) &&
1659 strcmp2(item->path, root_folder) != 0)) {
1660 folder_tree_destroy(folder);
1661 item = folder_item_new(folder, folder->name, root_folder);
1662 item->folder = folder;
1663 folder->node = item->node = g_node_new(item);
1666 imap_scan_tree_recursive(session, FOLDER_ITEM(folder->node->data), subs_only);
1667 imap_create_missing_folders(folder);
1668 unlock_session(session);
1673 static gint imap_scan_tree(Folder *folder)
1675 gboolean subs_only = FALSE;
1676 if (folder->account) {
1677 debug_print(" scanning only subs %d\n", folder->account->imap_subsonly);
1678 subs_only = folder->account->imap_subsonly;
1680 return imap_scan_tree_real(folder, subs_only);
1683 static gint imap_scan_tree_recursive(IMAPSession *session, FolderItem *item, gboolean subs_only)
1686 IMAPFolder *imapfolder;
1687 FolderItem *new_item;
1688 GSList *item_list, *cur;
1691 gchar *wildcard_path;
1697 g_return_val_if_fail(item != NULL, -1);
1698 g_return_val_if_fail(item->folder != NULL, -1);
1699 g_return_val_if_fail(item->no_sub == FALSE, -1);
1701 folder = item->folder;
1702 imapfolder = IMAP_FOLDER(folder);
1704 separator = imap_get_path_separator(session, imapfolder, item->path);
1706 if (folder->ui_func)
1707 folder->ui_func(folder, item, folder->ui_func_data);
1710 wildcard[0] = separator;
1713 real_path = imap_get_real_path(session, imapfolder, item->path);
1717 real_path = g_strdup("");
1720 Xstrcat_a(wildcard_path, real_path, wildcard,
1721 {g_free(real_path); return IMAP_ERROR;});
1725 r = imap_threaded_lsub(folder, "", wildcard_path, &lep_list);
1727 r = imap_threaded_list(folder, "", wildcard_path, &lep_list);
1729 if (r != MAILIMAP_NO_ERROR) {
1733 item_list = imap_list_from_lep(imapfolder,
1734 lep_list, real_path, FALSE);
1735 mailimap_list_result_free(lep_list);
1740 node = item->node->children;
1741 while (node != NULL) {
1742 FolderItem *old_item = FOLDER_ITEM(node->data);
1743 GNode *next = node->next;
1746 for (cur = item_list; cur != NULL; cur = cur->next) {
1747 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1748 if (!strcmp2(old_item->path, cur_item->path)) {
1749 new_item = cur_item;
1754 if (old_item && old_item->path && !strcmp(old_item->path, "INBOX")) {
1755 debug_print("not removing INBOX\n");
1757 debug_print("folder '%s' not found. removing...\n",
1759 folder_item_remove(old_item);
1762 old_item->no_sub = new_item->no_sub;
1763 old_item->no_select = new_item->no_select;
1764 if (old_item->no_sub == TRUE && node->children) {
1765 debug_print("folder '%s' doesn't have "
1766 "subfolders. removing...\n",
1768 folder_item_remove_children(old_item);
1775 for (cur = item_list; cur != NULL; cur = cur->next) {
1776 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1779 for (node = item->node->children; node != NULL;
1780 node = node->next) {
1781 if (!strcmp2(FOLDER_ITEM(node->data)->path,
1783 new_item = FOLDER_ITEM(node->data);
1784 folder_item_destroy(cur_item);
1790 new_item = cur_item;
1791 debug_print("new folder '%s' found.\n", new_item->path);
1792 folder_item_append(item, new_item);
1795 if (!strcmp(new_item->path, "INBOX")) {
1796 new_item->stype = F_INBOX;
1797 folder->inbox = new_item;
1798 } else if (!folder_item_parent(item) || item->stype == F_INBOX) {
1801 base = g_path_get_basename(new_item->path);
1803 if (!folder->outbox && !g_ascii_strcasecmp(base, "Sent")) {
1804 new_item->stype = F_OUTBOX;
1805 folder->outbox = new_item;
1806 } else if (!folder->draft && !g_ascii_strcasecmp(base, "Drafts")) {
1807 new_item->stype = F_DRAFT;
1808 folder->draft = new_item;
1809 } else if (!folder->queue && !g_ascii_strcasecmp(base, "Queue")) {
1810 new_item->stype = F_QUEUE;
1811 folder->queue = new_item;
1812 } else if (!folder->trash && !g_ascii_strcasecmp(base, "Trash")) {
1813 new_item->stype = F_TRASH;
1814 folder->trash = new_item;
1819 if (new_item->no_sub == FALSE)
1820 imap_scan_tree_recursive(session, new_item, subs_only);
1823 g_slist_free(item_list);
1825 return IMAP_SUCCESS;
1828 GList *imap_scan_subtree(Folder *folder, FolderItem *item, gboolean unsubs_only, gboolean recursive)
1830 IMAPSession *session = imap_session_get(folder);
1832 gchar *wildcard_path;
1836 GSList *item_list = NULL, *cur;
1837 GList *child_list = NULL, *tmplist = NULL;
1838 GSList *sub_list = NULL;
1844 separator = imap_get_path_separator(session, IMAP_FOLDER(folder), item->path);
1847 wildcard[0] = separator;
1850 real_path = imap_get_real_path(session, IMAP_FOLDER(folder), item->path);
1854 real_path = g_strdup("");
1857 Xstrcat_a(wildcard_path, real_path, wildcard,
1858 {g_free(real_path); return NULL;});
1862 statusbar_print_all(_("Looking for unsubscribed folders in %s..."),
1863 item->path?item->path:item->name);
1865 statusbar_print_all(_("Looking for subfolders of %s..."),
1866 item->path?item->path:item->name);
1868 r = imap_threaded_list(folder, "", wildcard_path, &lep_list);
1870 statusbar_pop_all();
1873 item_list = imap_list_from_lep(IMAP_FOLDER(folder),
1874 lep_list, real_path, FALSE);
1875 mailimap_list_result_free(lep_list);
1877 for (cur = item_list; cur != NULL; cur = cur->next) {
1878 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1880 tmplist = imap_scan_subtree(folder, cur_item,
1881 unsubs_only, recursive);
1883 child_list = g_list_concat(child_list, tmplist);
1885 child_list = g_list_prepend(child_list,
1886 imap_get_real_path(session,
1887 IMAP_FOLDER(folder), cur_item->path));
1889 folder_item_destroy(cur_item);
1891 child_list = g_list_reverse(child_list);
1892 g_slist_free(item_list);
1895 r = imap_threaded_lsub(folder, "", wildcard_path, &lep_list);
1897 statusbar_pop_all();
1900 sub_list = imap_list_from_lep(IMAP_FOLDER(folder),
1901 lep_list, real_path, FALSE);
1902 mailimap_list_result_free(lep_list);
1904 for (cur = sub_list; cur != NULL; cur = cur->next) {
1905 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1906 GList *oldlitem = NULL;
1907 gchar *tmp = imap_get_real_path(session,
1908 IMAP_FOLDER(folder), cur_item->path);
1909 folder_item_destroy(cur_item);
1910 oldlitem = g_list_find_custom(
1911 child_list, tmp, (GCompareFunc)strcmp2);
1913 child_list = g_list_remove_link(child_list, oldlitem);
1914 g_free(oldlitem->data);
1915 g_list_free(oldlitem);
1921 statusbar_pop_all();
1926 static gint imap_create_tree(Folder *folder)
1928 g_return_val_if_fail(folder != NULL, -1);
1929 g_return_val_if_fail(folder->node != NULL, -1);
1930 g_return_val_if_fail(folder->node->data != NULL, -1);
1931 g_return_val_if_fail(folder->account != NULL, -1);
1933 imap_scan_tree(folder);
1934 imap_create_missing_folders(folder);
1939 static void imap_create_missing_folders(Folder *folder)
1941 g_return_if_fail(folder != NULL);
1944 folder->inbox = imap_create_special_folder
1945 (folder, F_INBOX, "INBOX");
1947 folder->trash = imap_create_special_folder
1948 (folder, F_TRASH, "Trash");
1950 folder->queue = imap_create_special_folder
1951 (folder, F_QUEUE, "Queue");
1952 if (!folder->outbox)
1953 folder->outbox = imap_create_special_folder
1954 (folder, F_OUTBOX, "Sent");
1956 folder->draft = imap_create_special_folder
1957 (folder, F_DRAFT, "Drafts");
1960 static FolderItem *imap_create_special_folder(Folder *folder,
1961 SpecialFolderItemType stype,
1965 FolderItem *new_item;
1967 g_return_val_if_fail(folder != NULL, NULL);
1968 g_return_val_if_fail(folder->node != NULL, NULL);
1969 g_return_val_if_fail(folder->node->data != NULL, NULL);
1970 g_return_val_if_fail(folder->account != NULL, NULL);
1971 g_return_val_if_fail(name != NULL, NULL);
1973 item = FOLDER_ITEM(folder->node->data);
1974 new_item = imap_create_folder(folder, item, name);
1977 g_warning("Can't create '%s'\n", name);
1978 if (!folder->inbox) return NULL;
1980 new_item = imap_create_folder(folder, folder->inbox, name);
1982 g_warning("Can't create '%s' under INBOX\n", name);
1984 new_item->stype = stype;
1986 new_item->stype = stype;
1991 static gchar *imap_folder_get_path(Folder *folder)
1995 g_return_val_if_fail(folder != NULL, NULL);
1996 g_return_val_if_fail(folder->account != NULL, NULL);
1998 folder_path = g_strconcat(get_imap_cache_dir(),
2000 folder->account->recv_server,
2002 folder->account->userid,
2008 static gchar *imap_item_get_path(Folder *folder, FolderItem *item)
2010 gchar *folder_path, *path;
2012 g_return_val_if_fail(folder != NULL, NULL);
2013 g_return_val_if_fail(item != NULL, NULL);
2014 folder_path = imap_folder_get_path(folder);
2016 g_return_val_if_fail(folder_path != NULL, NULL);
2017 if (folder_path[0] == G_DIR_SEPARATOR) {
2019 path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
2022 path = g_strdup(folder_path);
2025 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
2026 folder_path, G_DIR_SEPARATOR_S,
2029 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
2032 g_free(folder_path);
2037 static FolderItem *imap_create_folder(Folder *folder, FolderItem *parent,
2040 gchar *dirpath, *imap_path;
2041 IMAPSession *session;
2042 FolderItem *new_item;
2047 gboolean no_select = FALSE, no_sub = FALSE;
2048 gboolean exist = FALSE;
2050 g_return_val_if_fail(folder != NULL, NULL);
2051 g_return_val_if_fail(folder->account != NULL, NULL);
2052 g_return_val_if_fail(parent != NULL, NULL);
2053 g_return_val_if_fail(name != NULL, NULL);
2055 debug_print("getting session...\n");
2056 session = imap_session_get(folder);
2061 if (!folder_item_parent(parent) && strcmp(name, "INBOX") == 0) {
2062 dirpath = g_strdup(name);
2063 }else if (parent->path)
2064 dirpath = g_strconcat(parent->path, "/", name, NULL);
2065 else if ((p = strchr(name, '/')) != NULL && *(p + 1) != '\0')
2066 dirpath = g_strdup(name);
2067 else if (folder->account->imap_dir && *folder->account->imap_dir) {
2070 Xstrdup_a(imap_dir, folder->account->imap_dir, {unlock_session(session);return NULL;});
2071 strtailchomp(imap_dir, '/');
2072 dirpath = g_strconcat(imap_dir, "/", name, NULL);
2074 dirpath = g_strdup(name);
2078 /* keep trailing directory separator to create a folder that contains
2080 imap_path = imap_utf8_to_modified_utf7(dirpath);
2082 strtailchomp(dirpath, '/');
2083 Xstrdup_a(new_name, name, {
2085 unlock_session(session);
2088 separator = imap_get_path_separator(session, IMAP_FOLDER(folder), imap_path);
2089 imap_path_separator_subst(imap_path, separator);
2090 /* remove trailing / for display */
2091 strtailchomp(new_name, '/');
2093 if (strcmp(dirpath, "INBOX") != 0) {
2098 argbuf = g_ptr_array_new();
2099 r = imap_threaded_list(folder, "", imap_path, &lep_list);
2100 if (r != MAILIMAP_NO_ERROR) {
2101 log_warning(LOG_PROTOCOL, _("can't create mailbox: LIST failed\n"));
2104 ptr_array_free_strings(argbuf);
2105 g_ptr_array_free(argbuf, TRUE);
2106 unlock_session(session);
2110 if (clist_count(lep_list) > 0)
2112 mailimap_list_result_free(lep_list);
2115 ok = imap_cmd_create(session, imap_path);
2116 if (ok != IMAP_SUCCESS) {
2117 log_warning(LOG_PROTOCOL, _("can't create mailbox\n"));
2120 unlock_session(session);
2123 r = imap_threaded_list(folder, "", imap_path, &lep_list);
2124 if (r == MAILIMAP_NO_ERROR) {
2125 GSList *item_list = imap_list_from_lep(IMAP_FOLDER(folder),
2126 lep_list, dirpath, TRUE);
2128 FolderItem *cur_item = FOLDER_ITEM(item_list->data);
2129 no_select = cur_item->no_select;
2130 no_sub = cur_item->no_sub;
2131 g_slist_free(item_list);
2133 mailimap_list_result_free(lep_list);
2136 imap_threaded_subscribe(folder, imap_path, TRUE);
2140 /* just get flags */
2141 r = imap_threaded_list(folder, "", "INBOX", &lep_list);
2142 if (r == MAILIMAP_NO_ERROR) {
2143 GSList *item_list = imap_list_from_lep(IMAP_FOLDER(folder),
2144 lep_list, dirpath, TRUE);
2146 FolderItem *cur_item = FOLDER_ITEM(item_list->data);
2147 no_select = cur_item->no_select;
2148 no_sub = cur_item->no_sub;
2149 g_slist_free(item_list);
2151 mailimap_list_result_free(lep_list);
2155 new_item = folder_item_new(folder, new_name, dirpath);
2156 new_item->no_select = no_select;
2157 new_item->no_sub = no_sub;
2158 folder_item_append(parent, new_item);
2162 dirpath = folder_item_get_path(new_item);
2163 if (!is_dir_exist(dirpath))
2164 make_dir_hier(dirpath);
2166 unlock_session(session);
2169 /* folder existed, scan it */
2170 folder_item_scan_full(new_item, FALSE);
2176 static gint imap_rename_folder(Folder *folder, FolderItem *item,
2181 gchar *real_oldpath;
2182 gchar *real_newpath;
2184 gchar *old_cache_dir;
2185 gchar *new_cache_dir;
2186 IMAPSession *session;
2189 gint exists, recent, unseen;
2190 guint32 uid_validity;
2192 g_return_val_if_fail(folder != NULL, -1);
2193 g_return_val_if_fail(item != NULL, -1);
2194 g_return_val_if_fail(item->path != NULL, -1);
2195 g_return_val_if_fail(name != NULL, -1);
2197 debug_print("getting session...\n");
2198 session = imap_session_get(folder);
2203 if (strchr(name, imap_get_path_separator(session, IMAP_FOLDER(folder), item->path)) != NULL) {
2204 g_warning(_("New folder name must not contain the namespace "
2206 unlock_session(session);
2210 real_oldpath = imap_get_real_path(session, IMAP_FOLDER(folder), item->path);
2212 g_free(session->mbox);
2213 session->mbox = NULL;
2214 ok = imap_cmd_examine(session, "INBOX",
2215 &exists, &recent, &unseen, &uid_validity, FALSE);
2216 if (ok != IMAP_SUCCESS) {
2217 g_free(real_oldpath);
2218 unlock_session(session);
2222 separator = imap_get_path_separator(session, IMAP_FOLDER(folder), item->path);
2223 if (strchr(item->path, G_DIR_SEPARATOR)) {
2224 dirpath = g_path_get_dirname(item->path);
2225 newpath = g_strconcat(dirpath, G_DIR_SEPARATOR_S, name, NULL);
2228 newpath = g_strdup(name);
2230 real_newpath = imap_utf8_to_modified_utf7(newpath);
2231 imap_path_separator_subst(real_newpath, separator);
2233 ok = imap_cmd_rename(session, real_oldpath, real_newpath);
2234 if (ok != IMAP_SUCCESS) {
2235 log_warning(LOG_PROTOCOL, _("can't rename mailbox: %s to %s\n"),
2236 real_oldpath, real_newpath);
2237 g_free(real_oldpath);
2239 g_free(real_newpath);
2240 unlock_session(session);
2244 item->name = g_strdup(name);
2246 old_cache_dir = folder_item_get_path(item);
2248 paths[0] = g_strdup(item->path);
2250 g_node_traverse(item->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
2251 imap_rename_folder_func, paths);
2253 if (is_dir_exist(old_cache_dir)) {
2254 new_cache_dir = folder_item_get_path(item);
2255 if (rename(old_cache_dir, new_cache_dir) < 0) {
2256 FILE_OP_ERROR(old_cache_dir, "rename");
2258 g_free(new_cache_dir);
2261 g_free(old_cache_dir);
2264 g_free(real_oldpath);
2265 g_free(real_newpath);
2266 unlock_session(session);
2270 gint imap_subscribe(Folder *folder, FolderItem *item, gchar *rpath, gboolean sub)
2274 IMAPSession *session;
2275 debug_print("getting session...\n");
2277 session = imap_session_get(folder);
2281 if (item && item->path) {
2282 path = imap_get_real_path(session, IMAP_FOLDER(folder), item->path);
2285 if (!strcmp(path, "INBOX") && sub == FALSE)
2287 debug_print("%ssubscribing %s\n", sub?"":"un", path);
2288 r = imap_threaded_subscribe(folder, path, sub);
2291 r = imap_threaded_subscribe(folder, rpath, sub);
2297 static gint imap_remove_folder_real(Folder *folder, FolderItem *item)
2300 IMAPSession *session;
2304 g_return_val_if_fail(folder != NULL, -1);
2305 g_return_val_if_fail(item != NULL, -1);
2306 g_return_val_if_fail(item->path != NULL, -1);
2308 debug_print("getting session...\n");
2309 session = imap_session_get(folder);
2313 path = imap_get_real_path(session, IMAP_FOLDER(folder), item->path);
2315 imap_threaded_subscribe(folder, path, FALSE);
2316 ok = imap_cmd_delete(session, path);
2317 if (ok != IMAP_SUCCESS) {
2318 gchar *tmp = g_strdup_printf("%s%c", path,
2319 imap_get_path_separator(session, IMAP_FOLDER(folder), path));
2322 ok = imap_cmd_delete(session, path);
2325 if (ok != IMAP_SUCCESS) {
2326 log_warning(LOG_PROTOCOL, _("can't delete mailbox\n"));
2328 unlock_session(session);
2333 cache_dir = folder_item_get_path(item);
2334 if (is_dir_exist(cache_dir) && remove_dir_recursive(cache_dir) < 0)
2335 g_warning("can't remove directory '%s'\n", cache_dir);
2337 folder_item_remove(item);
2338 unlock_session(session);
2342 static gint imap_remove_folder(Folder *folder, FolderItem *item)
2346 g_return_val_if_fail(item != NULL, -1);
2347 g_return_val_if_fail(item->folder != NULL, -1);
2348 g_return_val_if_fail(item->node != NULL, -1);
2350 node = item->node->children;
2351 while (node != NULL) {
2353 if (imap_remove_folder(folder, FOLDER_ITEM(node->data)) < 0)
2357 debug_print("IMAP removing %s\n", item->path);
2359 if (imap_remove_all_msg(folder, item) < 0)
2361 return imap_remove_folder_real(folder, item);
2364 typedef struct _uncached_data {
2365 IMAPSession *session;
2367 MsgNumberList *numlist;
2373 static void *imap_get_uncached_messages_thread(void *data)
2375 uncached_data *stuff = (uncached_data *)data;
2376 IMAPSession *session = stuff->session;
2377 FolderItem *item = stuff->item;
2378 MsgNumberList *numlist = stuff->numlist;
2380 GSList *newlist = NULL;
2381 GSList *llast = NULL;
2382 GSList *seq_list, *cur;
2384 debug_print("uncached_messages\n");
2386 if (session == NULL || item == NULL || item->folder == NULL
2387 || FOLDER_CLASS(item->folder) != &imap_class) {
2392 seq_list = imap_get_lep_set_from_numlist(numlist);
2393 debug_print("get msgs info\n");
2394 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
2395 struct mailimap_set * imapset;
2401 if (session->cancelled)
2404 imapset = cur->data;
2406 r = imap_threaded_fetch_env(session->folder,
2407 imapset, &env_list);
2408 if (r != MAILIMAP_NO_ERROR)
2411 session_set_access_time(SESSION(session));
2414 for(i = 0 ; i < carray_count(env_list) ; i ++) {
2415 struct imap_fetch_env_info * info;
2418 info = carray_get(env_list, i);
2419 msginfo = imap_envelope_from_lep(info, item);
2420 if (msginfo == NULL)
2422 msginfo->folder = item;
2424 llast = newlist = g_slist_append(newlist, msginfo);
2426 llast = g_slist_append(llast, msginfo);
2427 llast = llast->next;
2432 imap_fetch_env_free(env_list);
2435 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
2436 struct mailimap_set * imapset;
2438 imapset = cur->data;
2439 mailimap_set_free(imapset);
2442 session_set_access_time(SESSION(session));
2447 #define MAX_MSG_NUM 50
2449 static GSList *imap_get_uncached_messages(IMAPSession *session,
2451 MsgNumberList *numlist)
2453 GSList *result = NULL;
2455 uncached_data *data = g_new0(uncached_data, 1);
2460 data->total = g_slist_length(numlist);
2461 debug_print("messages list : %i\n", data->total);
2463 while (cur != NULL) {
2464 GSList * partial_result;
2472 while (count < MAX_MSG_NUM) {
2477 if (newlist == NULL)
2478 llast = newlist = g_slist_append(newlist, p);
2480 llast = g_slist_append(llast, p);
2481 llast = llast->next;
2491 data->session = session;
2493 data->numlist = newlist;
2496 if (prefs_common.work_offline &&
2497 !inc_offline_should_override(FALSE,
2498 _("Claws Mail needs network access in order "
2499 "to access the IMAP server."))) {
2505 (GSList *)imap_get_uncached_messages_thread(data);
2507 statusbar_progress_all(data->cur,data->total, 1);
2509 g_slist_free(newlist);
2511 result = g_slist_concat(result, partial_result);
2515 statusbar_progress_all(0,0,0);
2516 statusbar_pop_all();
2521 static void imap_delete_all_cached_messages(FolderItem *item)
2525 g_return_if_fail(item != NULL);
2526 g_return_if_fail(item->folder != NULL);
2527 g_return_if_fail(FOLDER_CLASS(item->folder) == &imap_class);
2529 debug_print("Deleting all cached messages...\n");
2531 dir = folder_item_get_path(item);
2532 if (is_dir_exist(dir))
2533 remove_all_numbered_files(dir);
2536 debug_print("done.\n");
2539 gchar imap_get_path_separator_for_item(FolderItem *item)
2541 Folder *folder = NULL;
2542 IMAPFolder *imap_folder = NULL;
2543 IMAPSession *session = NULL;
2548 folder = item->folder;
2553 imap_folder = IMAP_FOLDER(folder);
2558 debug_print("getting session...");
2559 session = imap_session_get(FOLDER(folder));
2560 result = imap_get_path_separator(session, imap_folder, item->path);
2561 unlock_session(session);
2565 static gchar imap_refresh_path_separator(IMAPSession *session, IMAPFolder *folder, const gchar *subfolder)
2569 gchar separator = '\0';
2571 g_return_val_if_fail(session != NULL, '/');
2572 r = imap_threaded_list((Folder *)folder, "", subfolder, &lep_list);
2574 if (r != MAILIMAP_NO_ERROR) {
2575 log_warning(LOG_PROTOCOL, _("LIST failed\n"));
2579 if (clist_count(lep_list) > 0) {
2580 clistiter * iter = clist_begin(lep_list);
2581 struct mailimap_mailbox_list * mb;
2582 mb = clist_content(iter);
2584 separator = mb->mb_delimiter;
2585 debug_print("got separator: %c\n", folder->last_seen_separator);
2587 mailimap_list_result_free(lep_list);
2591 static gchar imap_get_path_separator(IMAPSession *session, IMAPFolder *folder, const gchar *path)
2593 gchar separator = '/';
2595 if (folder->last_seen_separator == 0) {
2596 folder->last_seen_separator = imap_refresh_path_separator(session, folder, "");
2599 if (folder->last_seen_separator == 0) {
2600 folder->last_seen_separator = imap_refresh_path_separator(session, folder, "INBOX");
2603 if (folder->last_seen_separator != 0) {
2604 debug_print("using separator: %c\n", folder->last_seen_separator);
2605 return folder->last_seen_separator;
2611 static gchar *imap_get_real_path(IMAPSession *session, IMAPFolder *folder, const gchar *path)
2616 g_return_val_if_fail(folder != NULL, NULL);
2617 g_return_val_if_fail(path != NULL, NULL);
2619 real_path = imap_utf8_to_modified_utf7(path);
2620 separator = imap_get_path_separator(session, folder, path);
2621 imap_path_separator_subst(real_path, separator);
2626 static gint imap_set_message_flags(IMAPSession *session,
2627 MsgNumberList *numlist,
2635 seq_list = imap_get_lep_set_from_numlist(numlist);
2637 for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) {
2638 struct mailimap_set * imapset;
2640 imapset = cur->data;
2642 ok = imap_cmd_store(session, imapset,
2646 imap_lep_set_free(seq_list);
2648 return IMAP_SUCCESS;
2651 typedef struct _select_data {
2652 IMAPSession *session;
2657 guint32 *uid_validity;
2661 static gint imap_select(IMAPSession *session, IMAPFolder *folder,
2663 gint *exists, gint *recent, gint *unseen,
2664 guint32 *uid_validity, gboolean block)
2668 gint exists_, recent_, unseen_;
2669 guint32 uid_validity_;
2671 if (!exists && !recent && !unseen && !uid_validity) {
2672 if (session->mbox && strcmp(session->mbox, path) == 0)
2673 return IMAP_SUCCESS;
2682 uid_validity = &uid_validity_;
2684 g_free(session->mbox);
2685 session->mbox = NULL;
2687 real_path = imap_get_real_path(session, folder, path);
2689 ok = imap_cmd_select(session, real_path,
2690 exists, recent, unseen, uid_validity, block);
2691 if (ok != IMAP_SUCCESS)
2692 log_warning(LOG_PROTOCOL, _("can't select folder: %s\n"), real_path);
2694 session->mbox = g_strdup(path);
2695 session->folder_content_changed = FALSE;
2702 static gint imap_status(IMAPSession *session, IMAPFolder *folder,
2703 const gchar *path, IMAPFolderItem *item,
2705 guint32 *uid_next, guint32 *uid_validity,
2706 gint *unseen, gboolean block)
2710 struct mailimap_mailbox_data_status * data_status;
2715 real_path = imap_get_real_path(session, folder, path);
2733 r = imap_threaded_status(FOLDER(folder), real_path,
2734 &data_status, mask);
2737 if (r != MAILIMAP_NO_ERROR) {
2738 debug_print("status err %d\n", r);
2742 if (data_status->st_info_list == NULL) {
2743 mailimap_mailbox_data_status_free(data_status);
2744 debug_print("status->st_info_list == NULL\n");
2749 for(iter = clist_begin(data_status->st_info_list) ; iter != NULL ;
2750 iter = clist_next(iter)) {
2751 struct mailimap_status_info * info;
2753 info = clist_content(iter);
2754 switch (info->st_att) {
2755 case MAILIMAP_STATUS_ATT_MESSAGES:
2757 * messages = info->st_value;
2758 got_values |= 1 << 0;
2762 case MAILIMAP_STATUS_ATT_UIDNEXT:
2764 * uid_next = info->st_value;
2765 got_values |= 1 << 2;
2769 case MAILIMAP_STATUS_ATT_UIDVALIDITY:
2771 * uid_validity = info->st_value;
2772 got_values |= 1 << 3;
2776 case MAILIMAP_STATUS_ATT_UNSEEN:
2778 * unseen = info->st_value;
2779 got_values |= 1 << 4;
2784 mailimap_mailbox_data_status_free(data_status);
2786 if (got_values != mask) {
2787 g_warning("status: incomplete values received (%d)\n", got_values);
2789 return IMAP_SUCCESS;
2792 static void imap_free_capabilities(IMAPSession *session)
2794 slist_free_strings(session->capability);
2795 g_slist_free(session->capability);
2796 session->capability = NULL;
2799 /* low-level IMAP4rev1 commands */
2801 static gint imap_cmd_login(IMAPSession *session,
2802 const gchar *user, const gchar *pass,
2808 if (!strcmp(type, "LOGIN") && imap_has_capability(session, "LOGINDISABLED")) {
2809 gint ok = IMAP_ERROR;
2810 if (imap_has_capability(session, "STARTTLS")) {
2812 log_warning(LOG_PROTOCOL, _("Server requires TLS to log in.\n"));
2813 ok = imap_cmd_starttls(session);
2814 if (ok != IMAP_SUCCESS) {
2815 log_warning(LOG_PROTOCOL, _("Can't start TLS session.\n"));
2819 imap_free_capabilities(session);
2820 if (imap_get_capabilities(session) != MAILIMAP_NO_ERROR) {
2821 log_warning(LOG_PROTOCOL, _("Can't refresh capabilities.\n"));
2826 log_error(LOG_PROTOCOL, _("Connection to %s failed: "
2827 "server requires TLS, but Claws Mail "
2828 "has been compiled without OpenSSL "
2830 SESSION(session)->server);
2834 log_error(LOG_PROTOCOL, _("Server logins are disabled.\n"));
2839 log_print(LOG_PROTOCOL, "IMAP4> Logging %s to %s using %s\n",
2841 SESSION(session)->server,
2843 r = imap_threaded_login(session->folder, user, pass, type);
2844 if (r != MAILIMAP_NO_ERROR) {
2845 log_print(LOG_PROTOCOL, "IMAP4< Error logging in to %s\n",
2846 SESSION(session)->server);
2849 log_print(LOG_PROTOCOL, "IMAP4< Login to %s successful\n",
2850 SESSION(session)->server);
2856 static gint imap_cmd_noop(IMAPSession *session)
2859 unsigned int exists;
2861 r = imap_threaded_noop(session->folder, &exists);
2862 if (r != MAILIMAP_NO_ERROR) {
2863 debug_print("noop err %d\n", r);
2866 session->exists = exists;
2867 session_set_access_time(SESSION(session));
2869 return IMAP_SUCCESS;
2873 static gint imap_cmd_starttls(IMAPSession *session)
2877 r = imap_threaded_starttls(session->folder,
2878 SESSION(session)->server, SESSION(session)->port);
2879 if (r != MAILIMAP_NO_ERROR) {
2880 debug_print("starttls err %d\n", r);
2883 return IMAP_SUCCESS;
2887 static gint imap_cmd_select(IMAPSession *session, const gchar *folder,
2888 gint *exists, gint *recent, gint *unseen,
2889 guint32 *uid_validity, gboolean block)
2893 r = imap_threaded_select(session->folder, folder,
2894 exists, recent, unseen, uid_validity);
2895 if (r != MAILIMAP_NO_ERROR) {
2896 debug_print("select err %d\n", r);
2899 return IMAP_SUCCESS;
2902 static gint imap_cmd_examine(IMAPSession *session, const gchar *folder,
2903 gint *exists, gint *recent, gint *unseen,
2904 guint32 *uid_validity, gboolean block)
2908 r = imap_threaded_examine(session->folder, folder,
2909 exists, recent, unseen, uid_validity);
2910 if (r != MAILIMAP_NO_ERROR) {
2911 debug_print("examine err %d\n", r);
2915 return IMAP_SUCCESS;
2918 static gint imap_cmd_create(IMAPSession *session, const gchar *folder)
2922 r = imap_threaded_create(session->folder, folder);
2923 if (r != MAILIMAP_NO_ERROR) {
2928 return IMAP_SUCCESS;
2931 static gint imap_cmd_rename(IMAPSession *session, const gchar *old_folder,
2932 const gchar *new_folder)
2936 r = imap_threaded_rename(session->folder, old_folder,
2938 if (r != MAILIMAP_NO_ERROR) {
2943 return IMAP_SUCCESS;
2946 static gint imap_cmd_delete(IMAPSession *session, const gchar *folder)
2951 r = imap_threaded_delete(session->folder, folder);
2952 if (r != MAILIMAP_NO_ERROR) {
2957 return IMAP_SUCCESS;
2960 typedef struct _fetch_data {
2961 IMAPSession *session;
2963 const gchar *filename;
2969 static void *imap_cmd_fetch_thread(void *data)
2971 fetch_data *stuff = (fetch_data *)data;
2972 IMAPSession *session = stuff->session;
2973 guint32 uid = stuff->uid;
2974 const gchar *filename = stuff->filename;
2978 r = imap_threaded_fetch_content(session->folder,
2982 r = imap_threaded_fetch_content(session->folder,
2985 if (r != MAILIMAP_NO_ERROR) {
2986 debug_print("fetch err %d\n", r);
2987 return GINT_TO_POINTER(IMAP_ERROR);
2989 return GINT_TO_POINTER(IMAP_SUCCESS);
2992 static gint imap_cmd_fetch(IMAPSession *session, guint32 uid,
2993 const gchar *filename, gboolean headers,
2996 fetch_data *data = g_new0(fetch_data, 1);
2999 data->session = session;
3001 data->filename = filename;
3002 data->headers = headers;
3005 if (prefs_common.work_offline &&
3006 !inc_offline_should_override(FALSE,
3007 _("Claws Mail needs network access in order "
3008 "to access the IMAP server."))) {
3012 statusbar_print_all(_("Fetching message..."));
3013 result = GPOINTER_TO_INT(imap_cmd_fetch_thread(data));
3014 statusbar_pop_all();
3020 static gint imap_cmd_append(IMAPSession *session, const gchar *destfolder,
3021 const gchar *file, IMAPFlags flags,
3024 struct mailimap_flag_list * flag_list;
3027 g_return_val_if_fail(file != NULL, IMAP_ERROR);
3029 flag_list = imap_flag_to_lep(flags);
3030 r = imap_threaded_append(session->folder, destfolder,
3031 file, flag_list, (int *)new_uid);
3032 mailimap_flag_list_free(flag_list);
3034 if (r != MAILIMAP_NO_ERROR) {
3035 debug_print("append err %d\n", r);
3038 return IMAP_SUCCESS;
3041 static gint imap_cmd_copy(IMAPSession *session, struct mailimap_set * set,
3042 const gchar *destfolder, GRelation *uid_mapping,
3043 struct mailimap_set **source, struct mailimap_set **dest)
3047 g_return_val_if_fail(session != NULL, IMAP_ERROR);
3048 g_return_val_if_fail(set != NULL, IMAP_ERROR);
3049 g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
3051 r = imap_threaded_copy(session->folder, set, destfolder, source, dest);
3052 if (r != MAILIMAP_NO_ERROR) {
3057 return IMAP_SUCCESS;
3060 static gint imap_cmd_store(IMAPSession *session, struct mailimap_set * set,
3061 IMAPFlags flags, int do_add)
3064 struct mailimap_flag_list * flag_list;
3065 struct mailimap_store_att_flags * store_att_flags;
3067 flag_list = imap_flag_to_lep(flags);
3071 mailimap_store_att_flags_new_add_flags_silent(flag_list);
3074 mailimap_store_att_flags_new_remove_flags_silent(flag_list);
3076 r = imap_threaded_store(session->folder, set, store_att_flags);
3077 mailimap_store_att_flags_free(store_att_flags);
3078 if (r != MAILIMAP_NO_ERROR) {
3083 return IMAP_SUCCESS;
3086 static gint imap_cmd_expunge(IMAPSession *session)
3090 if (prefs_common.work_offline &&
3091 !inc_offline_should_override(FALSE,
3092 _("Claws Mail needs network access in order "
3093 "to access the IMAP server."))) {
3097 r = imap_threaded_expunge(session->folder);
3098 if (r != MAILIMAP_NO_ERROR) {
3103 return IMAP_SUCCESS;
3106 static void imap_path_separator_subst(gchar *str, gchar separator)
3109 gboolean in_escape = FALSE;
3111 if (!separator || separator == '/') return;
3113 for (p = str; *p != '\0'; p++) {
3114 if (*p == '/' && !in_escape)
3116 else if (*p == '&' && *(p + 1) != '-' && !in_escape)
3118 else if (*p == '-' && in_escape)
3123 static gchar *imap_modified_utf7_to_utf8(const gchar *mutf7_str)
3125 static iconv_t cd = (iconv_t)-1;
3126 static gboolean iconv_ok = TRUE;
3129 size_t norm_utf7_len;
3131 gchar *to_str, *to_p;
3133 gboolean in_escape = FALSE;
3135 if (!iconv_ok) return g_strdup(mutf7_str);
3137 if (cd == (iconv_t)-1) {
3138 cd = iconv_open(CS_INTERNAL, CS_UTF_7);
3139 if (cd == (iconv_t)-1) {
3140 g_warning("iconv cannot convert UTF-7 to %s\n",
3143 return g_strdup(mutf7_str);
3147 /* modified UTF-7 to normal UTF-7 conversion */
3148 norm_utf7 = g_string_new(NULL);
3150 for (p = mutf7_str; *p != '\0'; p++) {
3151 /* replace: '&' -> '+',
3153 escaped ',' -> '/' */
3154 if (!in_escape && *p == '&') {
3155 if (*(p + 1) != '-') {
3156 g_string_append_c(norm_utf7, '+');
3159 g_string_append_c(norm_utf7, '&');
3162 } else if (in_escape && *p == ',') {
3163 g_string_append_c(norm_utf7, '/');
3164 } else if (in_escape && *p == '-') {
3165 g_string_append_c(norm_utf7, '-');
3168 g_string_append_c(norm_utf7, *p);
3172 norm_utf7_p = norm_utf7->str;
3173 norm_utf7_len = norm_utf7->len;
3174 to_len = strlen(mutf7_str) * 5;
3175 to_p = to_str = g_malloc(to_len + 1);
3177 if (iconv(cd, (ICONV_CONST gchar **)&norm_utf7_p, &norm_utf7_len,
3178 &to_p, &to_len) == -1) {
3179 g_warning(_("iconv cannot convert UTF-7 to %s\n"),
3180 conv_get_locale_charset_str());
3181 g_string_free(norm_utf7, TRUE);
3183 return g_strdup(mutf7_str);
3186 /* second iconv() call for flushing */
3187 iconv(cd, NULL, NULL, &to_p, &to_len);
3188 g_string_free(norm_utf7, TRUE);
3194 static gchar *imap_utf8_to_modified_utf7(const gchar *from)
3196 static iconv_t cd = (iconv_t)-1;
3197 static gboolean iconv_ok = TRUE;
3198 gchar *norm_utf7, *norm_utf7_p;
3199 size_t from_len, norm_utf7_len;
3201 gchar *from_tmp, *to, *p;
3202 gboolean in_escape = FALSE;
3204 if (!iconv_ok) return g_strdup(from);
3206 if (cd == (iconv_t)-1) {
3207 cd = iconv_open(CS_UTF_7, CS_INTERNAL);
3208 if (cd == (iconv_t)-1) {
3209 g_warning(_("iconv cannot convert %s to UTF-7\n"),
3212 return g_strdup(from);
3216 /* UTF-8 to normal UTF-7 conversion */
3217 Xstrdup_a(from_tmp, from, return g_strdup(from));
3218 from_len = strlen(from);
3219 norm_utf7_len = from_len * 5;
3220 Xalloca(norm_utf7, norm_utf7_len + 1, return g_strdup(from));
3221 norm_utf7_p = norm_utf7;
3223 #define IS_PRINT(ch) (isprint(ch) && IS_ASCII(ch))
3225 while (from_len > 0) {
3226 if (*from_tmp == '+') {
3227 *norm_utf7_p++ = '+';
3228 *norm_utf7_p++ = '-';
3232 } else if (IS_PRINT(*(guchar *)from_tmp)) {
3233 /* printable ascii char */
3234 *norm_utf7_p = *from_tmp;
3240 size_t conv_len = 0;
3242 /* unprintable char: convert to UTF-7 */
3244 while (!IS_PRINT(*(guchar *)p) && conv_len < from_len) {
3245 conv_len += g_utf8_skip[*(guchar *)p];
3246 p += g_utf8_skip[*(guchar *)p];
3249 from_len -= conv_len;
3250 if (iconv(cd, (ICONV_CONST gchar **)&from_tmp,
3252 &norm_utf7_p, &norm_utf7_len) == -1) {
3253 g_warning(_("iconv cannot convert UTF-8 to UTF-7\n"));
3254 return g_strdup(from);
3257 /* second iconv() call for flushing */
3258 iconv(cd, NULL, NULL, &norm_utf7_p, &norm_utf7_len);
3264 *norm_utf7_p = '\0';
3265 to_str = g_string_new(NULL);
3266 for (p = norm_utf7; p < norm_utf7_p; p++) {
3267 /* replace: '&' -> "&-",
3270 BASE64 '/' -> ',' */
3271 if (!in_escape && *p == '&') {
3272 g_string_append(to_str, "&-");
3273 } else if (!in_escape && *p == '+') {
3274 if (*(p + 1) == '-') {
3275 g_string_append_c(to_str, '+');
3278 g_string_append_c(to_str, '&');
3281 } else if (in_escape && *p == '/') {
3282 g_string_append_c(to_str, ',');
3283 } else if (in_escape && *p == '-') {
3284 g_string_append_c(to_str, '-');
3287 g_string_append_c(to_str, *p);
3293 g_string_append_c(to_str, '-');
3297 g_string_free(to_str, FALSE);
3302 static gboolean imap_rename_folder_func(GNode *node, gpointer data)
3304 FolderItem *item = node->data;
3305 gchar **paths = data;
3306 const gchar *oldpath = paths[0];
3307 const gchar *newpath = paths[1];
3308 gchar *real_oldpath, *real_newpath;
3310 gchar *new_itempath;
3312 IMAPSession *session = imap_session_get(item->folder);
3314 oldpathlen = strlen(oldpath);
3315 if (strncmp(oldpath, item->path, oldpathlen) != 0) {
3316 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
3320 base = item->path + oldpathlen;
3321 while (*base == G_DIR_SEPARATOR) base++;
3323 new_itempath = g_strdup(newpath);
3325 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
3328 real_oldpath = imap_get_real_path(session, IMAP_FOLDER(item->folder), item->path);
3330 item->path = new_itempath;
3332 real_newpath = imap_get_real_path(session, IMAP_FOLDER(item->folder), item->path);
3334 imap_threaded_subscribe(item->folder, real_oldpath, FALSE);
3335 imap_threaded_subscribe(item->folder, real_newpath, TRUE);
3337 g_free(real_oldpath);
3338 g_free(real_newpath);
3342 typedef struct _get_list_uid_data {
3344 IMAPSession *session;
3345 IMAPFolderItem *item;
3346 GSList **msgnum_list;
3348 } get_list_uid_data;
3350 static void *get_list_of_uids_thread(void *data)
3352 get_list_uid_data *stuff = (get_list_uid_data *)data;
3353 Folder *folder = stuff->folder;
3354 IMAPFolderItem *item = stuff->item;
3355 GSList **msgnum_list = stuff->msgnum_list;
3356 gint ok, nummsgs = 0, lastuid_old;
3357 IMAPSession *session;
3358 GSList *uidlist, *elem;
3360 clist * lep_uidlist;
3362 session = stuff->session;
3363 if (session == NULL) {
3365 return GINT_TO_POINTER(-1);
3367 /* no session locking here, it's already locked by caller */
3368 ok = imap_select(session, IMAP_FOLDER(folder), item->item.path,
3369 NULL, NULL, NULL, NULL, TRUE);
3370 if (ok != IMAP_SUCCESS) {
3372 return GINT_TO_POINTER(-1);
3377 if (folder->account && folder->account->low_bandwidth) {
3378 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SIMPLE, NULL,
3382 if (r == MAILIMAP_NO_ERROR) {
3383 GSList * fetchuid_list =
3384 imap_uid_list_from_lep(lep_uidlist);
3385 mailimap_search_result_free(lep_uidlist);
3387 uidlist = g_slist_concat(fetchuid_list, uidlist);
3389 carray * lep_uidtab;
3390 r = imap_threaded_fetch_uid(folder, 1,
3392 if (r == MAILIMAP_NO_ERROR) {
3393 GSList * fetchuid_list =
3394 imap_uid_list_from_lep_tab(lep_uidtab);
3395 imap_fetch_uid_list_free(lep_uidtab);
3396 uidlist = g_slist_concat(fetchuid_list, uidlist);
3400 if (r != MAILIMAP_NO_ERROR) {
3402 return GINT_TO_POINTER(-1);
3405 lastuid_old = item->lastuid;
3406 *msgnum_list = g_slist_copy(item->uid_list);
3407 nummsgs = g_slist_length(*msgnum_list);
3408 debug_print("Got %d uids from cache\n", g_slist_length(item->uid_list));
3410 for (elem = uidlist; elem != NULL; elem = g_slist_next(elem)) {
3413 msgnum = GPOINTER_TO_INT(elem->data);
3414 if (msgnum > lastuid_old) {
3415 *msgnum_list = g_slist_prepend(*msgnum_list, GINT_TO_POINTER(msgnum));
3416 item->uid_list = g_slist_prepend(item->uid_list, GINT_TO_POINTER(msgnum));
3419 if(msgnum > item->lastuid)
3420 item->lastuid = msgnum;
3423 g_slist_free(uidlist);
3425 return GINT_TO_POINTER(nummsgs);
3428 static gint get_list_of_uids(IMAPSession *session, Folder *folder, IMAPFolderItem *item, GSList **msgnum_list)
3431 get_list_uid_data *data = g_new0(get_list_uid_data, 1);
3433 data->folder = folder;
3435 data->msgnum_list = msgnum_list;
3436 data->session = session;
3437 if (prefs_common.work_offline &&
3438 !inc_offline_should_override(FALSE,
3439 _("Claws Mail needs network access in order "
3440 "to access the IMAP server."))) {
3445 result = GPOINTER_TO_INT(get_list_of_uids_thread(data));
3451 gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list, gboolean *old_uids_valid)
3453 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3454 IMAPSession *session;
3455 gint ok, nummsgs = 0, exists;
3456 guint32 uid_next = 0, uid_val = 0;
3457 GSList *uidlist = NULL;
3459 gboolean selected_folder;
3460 debug_print("get_num_list\n");
3462 g_return_val_if_fail(folder != NULL, -1);
3463 g_return_val_if_fail(item != NULL, -1);
3464 g_return_val_if_fail(item->item.path != NULL, -1);
3465 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3466 g_return_val_if_fail(folder->account != NULL, -1);
3468 debug_print("getting session...\n");
3469 session = imap_session_get(folder);
3470 g_return_val_if_fail(session != NULL, -1);
3472 if (FOLDER_ITEM(item)->path)
3473 statusbar_print_all(_("Scanning folder %s%c%s ..."),
3474 FOLDER_ITEM(item)->folder->name,
3476 FOLDER_ITEM(item)->path);
3478 statusbar_print_all(_("Scanning folder %s ..."),
3479 FOLDER_ITEM(item)->folder->name);
3481 selected_folder = (session->mbox != NULL) &&
3482 (!strcmp(session->mbox, item->item.path));
3483 if (selected_folder && time(NULL) - item->use_cache < 2) {
3484 ok = imap_cmd_noop(session);
3485 if (ok != IMAP_SUCCESS) {
3486 debug_print("disconnected!\n");
3487 session = imap_reconnect_if_possible(folder, session);
3488 if (session == NULL) {
3489 statusbar_pop_all();
3490 unlock_session(session);
3494 exists = session->exists;
3496 uid_next = item->c_uid_next;
3497 uid_val = item->c_uid_validity;
3498 *old_uids_valid = TRUE;
3500 if (item->use_cache && time(NULL) - item->use_cache < 2) {
3501 exists = item->c_messages;
3502 uid_next = item->c_uid_next;
3503 uid_val = item->c_uid_validity;
3505 debug_print("using cache %d %d %d\n", exists, uid_next, uid_val);
3507 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path, item,
3508 &exists, &uid_next, &uid_val, NULL, FALSE);
3510 item->item.last_num = uid_next - 1;
3512 item->use_cache = (time_t)0;
3513 if (ok != IMAP_SUCCESS) {
3514 statusbar_pop_all();
3515 unlock_session(session);
3518 if(item->item.mtime == uid_val)
3519 *old_uids_valid = TRUE;
3521 *old_uids_valid = FALSE;
3523 debug_print("Freeing imap uid cache (%d != %d)\n",
3524 (int)item->item.mtime, uid_val);
3526 g_slist_free(item->uid_list);
3527 item->uid_list = NULL;
3529 item->item.mtime = uid_val;
3531 imap_delete_all_cached_messages((FolderItem *)item);
3535 /* If old uid_next matches new uid_next we can be sure no message
3536 was added to the folder */
3537 debug_print("uid_next is %d and item->uid_next %d \n",
3538 uid_next, item->uid_next);
3539 if (uid_next == item->uid_next) {
3540 nummsgs = g_slist_length(item->uid_list);
3542 /* If number of messages is still the same we
3543 know our caches message numbers are still valid,
3544 otherwise if the number of messages has decrease
3545 we discard our cache to start a new scan to find
3546 out which numbers have been removed */
3547 if (exists == nummsgs) {
3548 debug_print("exists == nummsgs\n");
3549 *msgnum_list = g_slist_copy(item->uid_list);
3550 statusbar_pop_all();
3551 unlock_session(session);
3553 } else if (exists < nummsgs) {
3554 debug_print("Freeing imap uid cache");
3556 g_slist_free(item->uid_list);
3557 item->uid_list = NULL;
3562 *msgnum_list = NULL;
3563 statusbar_pop_all();
3564 unlock_session(session);
3568 item->last_change = time(NULL);
3569 nummsgs = get_list_of_uids(session, folder, item, &uidlist);
3572 statusbar_pop_all();
3573 unlock_session(session);
3577 if (nummsgs != exists) {
3578 /* Cache contains more messages then folder, we have cached
3579 an old UID of a message that was removed and new messages
3580 have been added too, otherwise the uid_next check would
3582 debug_print("Freeing imap uid cache");
3584 g_slist_free(item->uid_list);
3585 item->uid_list = NULL;
3587 g_slist_free(*msgnum_list);
3589 nummsgs = get_list_of_uids(session, folder, item, &uidlist);
3592 *msgnum_list = uidlist;
3594 dir = folder_item_get_path((FolderItem *)item);
3595 debug_print("removing old messages from %s\n", dir);
3596 remove_numbered_files_not_in_list(dir, *msgnum_list);
3599 item->uid_next = uid_next;
3601 debug_print("get_num_list - ok - %i\n", nummsgs);
3602 statusbar_pop_all();
3603 unlock_session(session);
3607 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item)
3612 flags.perm_flags = MSG_NEW|MSG_UNREAD;
3613 flags.tmp_flags = 0;
3615 g_return_val_if_fail(item != NULL, NULL);
3616 g_return_val_if_fail(file != NULL, NULL);
3618 if (folder_has_parent_of_type(item, F_QUEUE)) {
3619 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
3620 } else if (folder_has_parent_of_type(item, F_DRAFT)) {
3621 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
3624 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3625 if (!msginfo) return NULL;
3627 msginfo->plaintext_file = g_strdup(file);
3628 msginfo->folder = item;
3633 GSList *imap_get_msginfos(Folder *folder, FolderItem *item,
3634 GSList *msgnum_list)
3636 IMAPSession *session;
3637 MsgInfoList *ret = NULL;
3640 debug_print("get_msginfos\n");
3642 g_return_val_if_fail(folder != NULL, NULL);
3643 g_return_val_if_fail(item != NULL, NULL);
3644 g_return_val_if_fail(msgnum_list != NULL, NULL);
3646 debug_print("getting session...\n");
3647 session = imap_session_get(folder);
3648 g_return_val_if_fail(session != NULL, NULL);
3650 debug_print("IMAP getting msginfos\n");
3651 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3652 NULL, NULL, NULL, NULL, FALSE);
3653 if (ok != IMAP_SUCCESS) {
3654 unlock_session(session);
3657 if (!(folder_has_parent_of_type(item, F_DRAFT) ||
3658 folder_has_parent_of_type(item, F_QUEUE))) {
3659 ret = g_slist_concat(ret,
3660 imap_get_uncached_messages(session, item,
3663 MsgNumberList *sorted_list, *elem, *llast = NULL;
3664 gint startnum, lastnum;
3666 sorted_list = g_slist_sort(g_slist_copy(msgnum_list), g_int_compare);
3668 startnum = lastnum = GPOINTER_TO_INT(sorted_list->data);
3670 llast = g_slist_last(ret);
3671 for (elem = sorted_list;; elem = g_slist_next(elem)) {
3675 num = GPOINTER_TO_INT(elem->data);
3677 if (num > lastnum + 1 || elem == NULL) {
3679 for (i = startnum; i <= lastnum; ++i) {
3682 file = imap_fetch_msg(folder, item, i);
3684 MsgInfo *msginfo = imap_parse_msg(file, item);
3685 if (msginfo != NULL) {
3686 msginfo->msgnum = i;
3688 llast = ret = g_slist_append(ret, msginfo);
3690 llast = g_slist_append(llast, msginfo);
3691 llast = llast->next;
3696 session_set_access_time(SESSION(session));
3707 g_slist_free(sorted_list);
3709 unlock_session(session);
3713 MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid)
3715 MsgInfo *msginfo = NULL;
3716 MsgInfoList *msginfolist;
3717 MsgNumberList numlist;
3719 numlist.next = NULL;
3720 numlist.data = GINT_TO_POINTER(uid);
3722 msginfolist = imap_get_msginfos(folder, item, &numlist);
3723 if (msginfolist != NULL) {
3724 msginfo = msginfolist->data;
3725 g_slist_free(msginfolist);
3731 gboolean imap_scan_required(Folder *folder, FolderItem *_item)
3733 IMAPSession *session;
3734 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3735 gint ok, exists = 0, unseen = 0;
3736 guint32 uid_next = 0, uid_val = 0;
3737 gboolean selected_folder;
3739 g_return_val_if_fail(folder != NULL, FALSE);
3740 g_return_val_if_fail(item != NULL, FALSE);
3741 g_return_val_if_fail(item->item.folder != NULL, FALSE);
3742 g_return_val_if_fail(FOLDER_CLASS(item->item.folder) == &imap_class, FALSE);
3744 if (item->item.path == NULL)
3747 debug_print("getting session...\n");
3748 session = imap_session_get(folder);
3749 g_return_val_if_fail(session != NULL, FALSE);
3751 selected_folder = (session->mbox != NULL) &&
3752 (!strcmp(session->mbox, item->item.path));
3753 if (selected_folder && time(NULL) - item->use_cache < 2) {
3754 ok = imap_cmd_noop(session);
3755 if (ok != IMAP_SUCCESS) {
3756 debug_print("disconnected!\n");
3757 session = imap_reconnect_if_possible(folder, session);
3758 if (session == NULL)
3762 if (session->folder_content_changed
3763 || session->exists != item->item.total_msgs) {
3764 unlock_session(session);
3768 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path, IMAP_FOLDER_ITEM(item),
3769 &exists, &uid_next, &uid_val, &unseen, FALSE);
3770 if (ok != IMAP_SUCCESS) {
3771 unlock_session(session);
3775 item->use_cache = time(NULL);
3776 item->c_messages = exists;
3777 item->c_uid_next = uid_next;
3778 item->c_uid_validity = uid_val;
3779 item->c_unseen = unseen;
3780 item->item.last_num = uid_next - 1;
3781 debug_print("uidnext %d, item->uid_next %d, exists %d, item->item.total_msgs %d\n",
3782 uid_next, item->uid_next, exists, item->item.total_msgs);
3783 if ((uid_next != item->uid_next) || (exists != item->item.total_msgs)
3784 || unseen != item->item.unread_msgs || uid_val != item->item.mtime) {
3785 unlock_session(session);
3786 item->last_change = time(NULL);
3790 unlock_session(session);
3794 void imap_change_flags(Folder *folder, FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
3796 IMAPSession *session;
3797 IMAPFlags flags_set = 0, flags_unset = 0;
3798 gint ok = IMAP_SUCCESS;
3799 MsgNumberList numlist;
3800 hashtable_data *ht_data = NULL;
3802 g_return_if_fail(folder != NULL);
3803 g_return_if_fail(folder->klass == &imap_class);
3804 g_return_if_fail(item != NULL);
3805 g_return_if_fail(item->folder == folder);
3806 g_return_if_fail(msginfo != NULL);
3807 g_return_if_fail(msginfo->folder == item);
3809 if (!MSG_IS_MARKED(msginfo->flags) && (newflags & MSG_MARKED))
3810 flags_set |= IMAP_FLAG_FLAGGED;
3811 if ( MSG_IS_MARKED(msginfo->flags) && !(newflags & MSG_MARKED))
3812 flags_unset |= IMAP_FLAG_FLAGGED;
3814 if (!MSG_IS_UNREAD(msginfo->flags) && (newflags & MSG_UNREAD))
3815 flags_unset |= IMAP_FLAG_SEEN;
3816 if ( MSG_IS_UNREAD(msginfo->flags) && !(newflags & MSG_UNREAD))
3817 flags_set |= IMAP_FLAG_SEEN;
3819 if (!MSG_IS_REPLIED(msginfo->flags) && (newflags & MSG_REPLIED))
3820 flags_set |= IMAP_FLAG_ANSWERED;
3821 if ( MSG_IS_REPLIED(msginfo->flags) && !(newflags & MSG_REPLIED))
3822 flags_unset |= IMAP_FLAG_ANSWERED;
3824 if (!MSG_IS_DELETED(msginfo->flags) && (newflags & MSG_DELETED))
3825 flags_set |= IMAP_FLAG_DELETED;
3826 if ( MSG_IS_DELETED(msginfo->flags) && !(newflags & MSG_DELETED))
3827 flags_unset |= IMAP_FLAG_DELETED;
3829 if (!flags_set && !flags_unset) {
3830 /* the changed flags were not translatable to IMAP-speak.
3831 * like MSG_POSTFILTERED, so just apply. */
3832 msginfo->flags.perm_flags = newflags;
3836 debug_print("getting session...\n");
3837 session = imap_session_get(folder);
3842 if ((ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
3843 NULL, NULL, NULL, NULL, FALSE)) != IMAP_SUCCESS) {
3844 unlock_session(session);
3847 numlist.next = NULL;
3848 numlist.data = GINT_TO_POINTER(msginfo->msgnum);
3850 if (IMAP_FOLDER_ITEM(item)->batching) {
3851 /* instead of performing an UID STORE command for each message change,
3852 * as a lot of them can change "together", we just fill in hashtables
3853 * and defer the treatment so that we're able to send only one
3856 debug_print("IMAP batch mode on, deferring flags change\n");
3858 ht_data = g_hash_table_lookup(IMAP_FOLDER_ITEM(item)->flags_set_table,
3859 GINT_TO_POINTER(flags_set));
3860 if (ht_data == NULL) {
3861 ht_data = g_new0(hashtable_data, 1);
3862 ht_data->session = session;
3863 ht_data->item = IMAP_FOLDER_ITEM(item);
3864 g_hash_table_insert(IMAP_FOLDER_ITEM(item)->flags_set_table,
3865 GINT_TO_POINTER(flags_set), ht_data);
3867 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
3868 ht_data->msglist = g_slist_prepend(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum));
3871 ht_data = g_hash_table_lookup(IMAP_FOLDER_ITEM(item)->flags_unset_table,
3872 GINT_TO_POINTER(flags_unset));
3873 if (ht_data == NULL) {
3874 ht_data = g_new0(hashtable_data, 1);
3875 ht_data->session = session;
3876 ht_data->item = IMAP_FOLDER_ITEM(item);
3877 g_hash_table_insert(IMAP_FOLDER_ITEM(item)->flags_unset_table,
3878 GINT_TO_POINTER(flags_unset), ht_data);
3880 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
3881 ht_data->msglist = g_slist_prepend(ht_data->msglist,
3882 GINT_TO_POINTER(msginfo->msgnum));
3885 debug_print("IMAP changing flags\n");
3887 ok = imap_set_message_flags(session, &numlist, flags_set, TRUE);
3888 if (ok != IMAP_SUCCESS) {
3889 unlock_session(session);
3895 ok = imap_set_message_flags(session, &numlist, flags_unset, FALSE);
3896 if (ok != IMAP_SUCCESS) {
3897 unlock_session(session);
3902 msginfo->flags.perm_flags = newflags;
3903 unlock_session(session);
3907 static gint imap_remove_msg(Folder *folder, FolderItem *item, gint uid)
3910 IMAPSession *session;
3912 MsgNumberList numlist;
3914 g_return_val_if_fail(folder != NULL, -1);
3915 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3916 g_return_val_if_fail(item != NULL, -1);
3918 debug_print("getting session...\n");
3919 session = imap_session_get(folder);
3920 if (!session) return -1;
3922 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3923 NULL, NULL, NULL, NULL, FALSE);
3924 if (ok != IMAP_SUCCESS) {
3925 unlock_session(session);
3928 numlist.next = NULL;
3929 numlist.data = GINT_TO_POINTER(uid);
3931 ok = imap_set_message_flags
3932 (session, &numlist, IMAP_FLAG_DELETED, TRUE);
3933 if (ok != IMAP_SUCCESS) {
3934 log_warning(LOG_PROTOCOL, _("can't set deleted flags: %d\n"), uid);
3935 unlock_session(session);
3939 if (!session->uidplus) {
3940 ok = imap_cmd_expunge(session);
3944 uidstr = g_strdup_printf("%u", uid);
3945 ok = imap_cmd_expunge(session);
3948 if (ok != IMAP_SUCCESS) {
3949 log_warning(LOG_PROTOCOL, _("can't expunge\n"));
3950 unlock_session(session);
3954 IMAP_FOLDER_ITEM(item)->uid_list = g_slist_remove(
3955 IMAP_FOLDER_ITEM(item)->uid_list, numlist.data);
3956 dir = folder_item_get_path(item);
3957 if (is_dir_exist(dir))
3958 remove_numbered_files(dir, uid, uid);
3960 unlock_session(session);
3961 return IMAP_SUCCESS;
3964 static gint compare_msginfo(gconstpointer a, gconstpointer b)
3966 return ((MsgInfo *)a)->msgnum - ((MsgInfo *)b)->msgnum;
3969 static guint gslist_find_next_num(MsgNumberList **list, guint num)
3973 g_return_val_if_fail(list != NULL, -1);
3975 for (elem = *list; elem != NULL; elem = g_slist_next(elem))
3976 if (GPOINTER_TO_INT(elem->data) >= num)
3979 return elem != NULL ? GPOINTER_TO_INT(elem->data) : (gint)-1;
3983 * NEW and DELETED flags are not syncronized
3984 * - The NEW/RECENT flags in IMAP folders can not really be directly
3985 * modified by Sylpheed
3986 * - The DELETE/DELETED flag in IMAP and Sylpheed don't have the same
3987 * meaning, in IMAP it always removes the messages from the FolderItem
3988 * in Sylpheed it can mean to move the message to trash
3991 typedef struct _get_flags_data {
3994 MsgInfoList *msginfo_list;
3995 GRelation *msgflags;
3996 gboolean full_search;
4000 static /*gint*/ void *imap_get_flags_thread(void *data)
4002 get_flags_data *stuff = (get_flags_data *)data;
4003 Folder *folder = stuff->folder;
4004 FolderItem *fitem = (FolderItem *) stuff->item;
4005 MsgInfoList *msginfo_list = stuff->msginfo_list;
4006 GRelation *msgflags = stuff->msgflags;
4008 carray * lep_uidtab;
4009 IMAPSession *session;
4012 GHashTable *flags_hash = NULL;
4013 gboolean full_search = stuff->full_search;
4014 GSList *sorted_list = NULL;
4015 GSList *unseen = NULL, *answered = NULL, *flagged = NULL, *deleted = NULL;
4016 GSList *p_unseen, *p_answered, *p_flagged, *p_deleted;
4017 GSList *seq_list, *cur;
4018 gboolean reverse_seen = FALSE;
4019 gboolean selected_folder;
4020 gint exists_cnt, unseen_cnt;
4022 session = imap_session_get(folder);
4023 if (session == NULL) {
4025 return GINT_TO_POINTER(-1);
4027 selected_folder = (session->mbox != NULL) &&
4028 (!strcmp(session->mbox, fitem->path));
4030 if (!selected_folder) {
4031 ok = imap_select(session, IMAP_FOLDER(folder), fitem->path,
4032 &exists_cnt, NULL, &unseen_cnt, NULL, TRUE);
4033 if (ok != IMAP_SUCCESS) {
4035 unlock_session(session);
4036 return GINT_TO_POINTER(-1);
4039 if (unseen_cnt > exists_cnt / 2)
4040 reverse_seen = TRUE;
4043 if (fitem->unread_msgs > fitem->total_msgs / 2)
4044 reverse_seen = TRUE;
4047 sorted_list = g_slist_sort(g_slist_copy(msginfo_list), compare_msginfo);
4049 seq_list = imap_get_lep_set_from_msglist(msginfo_list);
4051 struct mailimap_set * set;
4052 set = mailimap_set_new_interval(1, 0);
4053 seq_list = g_slist_append(NULL, set);
4056 if (folder->account && folder->account->low_bandwidth) {
4057 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
4058 struct mailimap_set * imapset;
4059 clist * lep_uidlist;
4062 imapset = cur->data;
4064 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SEEN,
4065 full_search ? NULL:imapset, &lep_uidlist);
4068 r = imap_threaded_search(folder,
4069 IMAP_SEARCH_TYPE_UNSEEN,
4070 full_search ? NULL:imapset, &lep_uidlist);
4072 if (r == MAILIMAP_NO_ERROR) {
4075 uidlist = imap_uid_list_from_lep(lep_uidlist);
4076 mailimap_search_result_free(lep_uidlist);
4078 unseen = g_slist_concat(unseen, uidlist);
4081 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_FLAGGED,
4082 full_search ? NULL:imapset, &lep_uidlist);
4083 if (r == MAILIMAP_NO_ERROR) {
4086 uidlist = imap_uid_list_from_lep(lep_uidlist);
4087 mailimap_search_result_free(lep_uidlist);
4089 flagged = g_slist_concat(flagged, uidlist);
4092 if (fitem->opened || fitem->processing_pending || fitem == folder->inbox) {
4093 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_ANSWERED,
4094 full_search ? NULL:imapset, &lep_uidlist);
4095 if (r == MAILIMAP_NO_ERROR) {
4098 uidlist = imap_uid_list_from_lep(lep_uidlist);
4099 mailimap_search_result_free(lep_uidlist);
4101 answered = g_slist_concat(answered, uidlist);
4104 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_DELETED,
4105 full_search ? NULL:imapset, &lep_uidlist);
4106 if (r == MAILIMAP_NO_ERROR) {
4109 uidlist = imap_uid_list_from_lep(lep_uidlist);
4110 mailimap_search_result_free(lep_uidlist);
4112 deleted = g_slist_concat(deleted, uidlist);
4117 p_answered = answered;
4118 p_flagged = flagged;
4119 p_deleted = deleted;
4122 r = imap_threaded_fetch_uid_flags(folder, 1, &lep_uidtab);
4123 if (r == MAILIMAP_NO_ERROR) {
4124 flags_hash = g_hash_table_new_full(g_int_hash, g_int_equal, free, NULL);
4125 imap_flags_hash_from_lep_uid_flags_tab(lep_uidtab, flags_hash);
4126 imap_fetch_uid_flags_list_free(lep_uidtab);
4129 for (elem = sorted_list; elem != NULL; elem = g_slist_next(elem)) {
4131 MsgPermFlags flags, oldflags;
4134 msginfo = (MsgInfo *) elem->data;
4135 flags = msginfo->flags.perm_flags;
4136 wasnew = (flags & MSG_NEW);
4137 oldflags = flags & ~(MSG_NEW|MSG_UNREAD|MSG_REPLIED|MSG_MARKED|MSG_DELETED);
4139 if (folder->account && folder->account->low_bandwidth) {
4140 if (fitem->opened || fitem->processing_pending || fitem == folder->inbox) {
4141 flags &= ~((reverse_seen ? 0 : MSG_UNREAD | MSG_NEW) | MSG_REPLIED | MSG_MARKED);
4143 flags &= ~((reverse_seen ? 0 : MSG_UNREAD | MSG_NEW | MSG_MARKED));
4146 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
4147 if (gslist_find_next_num(&p_unseen, msginfo->msgnum) == msginfo->msgnum) {
4148 if (!reverse_seen) {
4149 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
4151 flags &= ~(MSG_UNREAD | MSG_NEW);
4155 if (gslist_find_next_num(&p_flagged, msginfo->msgnum) == msginfo->msgnum)
4156 flags |= MSG_MARKED;
4158 flags &= ~MSG_MARKED;
4160 if (fitem->opened || fitem->processing_pending || fitem == folder->inbox) {
4161 if (gslist_find_next_num(&p_answered, msginfo->msgnum) == msginfo->msgnum)
4162 flags |= MSG_REPLIED;
4164 flags &= ~MSG_REPLIED;
4165 if (gslist_find_next_num(&p_deleted, msginfo->msgnum) == msginfo->msgnum)
4166 flags |= MSG_DELETED;
4168 flags &= ~MSG_DELETED;
4171 if (flags_hash != NULL) {
4174 puid = malloc(sizeof(* puid));
4175 * puid = msginfo->msgnum;
4177 flags = GPOINTER_TO_INT(g_hash_table_lookup(flags_hash, puid));
4181 if ((flags & MSG_UNREAD) == 0)
4188 g_relation_insert(msgflags, msginfo, GINT_TO_POINTER(flags));
4192 g_hash_table_destroy(flags_hash);
4194 imap_lep_set_free(seq_list);
4195 g_slist_free(flagged);
4196 g_slist_free(deleted);
4197 g_slist_free(answered);
4198 g_slist_free(unseen);
4199 g_slist_free(sorted_list);
4201 unlock_session(session);
4203 return GINT_TO_POINTER(0);
4206 static gint imap_get_flags(Folder *folder, FolderItem *item,
4207 MsgInfoList *msginfo_list, GRelation *msgflags)
4210 get_flags_data *data = g_new0(get_flags_data, 1);
4212 data->folder = folder;
4214 data->msginfo_list = msginfo_list;
4215 data->msgflags = msgflags;
4216 data->full_search = FALSE;
4218 GSList *tmp = NULL, *cur;
4220 if (prefs_common.work_offline &&
4221 !inc_offline_should_override(FALSE,
4222 _("Claws Mail needs network access in order "
4223 "to access the IMAP server."))) {
4228 tmp = folder_item_get_msg_list(item);
4230 if (g_slist_length(tmp) <= g_slist_length(msginfo_list))
4231 data->full_search = TRUE;
4233 for (cur = tmp; cur; cur = cur->next)
4234 procmsg_msginfo_free((MsgInfo *)cur->data);
4238 result = GPOINTER_TO_INT(imap_get_flags_thread(data));
4245 static gboolean process_flags(gpointer key, gpointer value, gpointer user_data)
4247 gboolean flags_set = GPOINTER_TO_INT(user_data);
4248 gint flags_value = GPOINTER_TO_INT(key);
4249 hashtable_data *data = (hashtable_data *)value;
4250 IMAPFolderItem *_item = data->item;
4251 FolderItem *item = (FolderItem *)_item;
4252 gint ok = IMAP_ERROR;
4253 IMAPSession *session = NULL;
4255 debug_print("getting session...\n");
4256 session = imap_session_get(item->folder);
4258 data->msglist = g_slist_reverse(data->msglist);
4260 debug_print("IMAP %ssetting flags to %d for %d messages\n",
4263 g_slist_length(data->msglist));
4266 ok = imap_select(session, IMAP_FOLDER(item->folder), item->path,
4267 NULL, NULL, NULL, NULL, FALSE);
4269 if (ok == IMAP_SUCCESS) {
4270 imap_set_message_flags(data->session, data->msglist, flags_value, flags_set);
4272 g_warning("can't select mailbox %s\n", item->path);
4275 unlock_session(session);
4276 g_slist_free(data->msglist);
4281 static void process_hashtable(IMAPFolderItem *item)
4283 if (item->flags_set_table) {
4284 g_hash_table_foreach_remove(item->flags_set_table, process_flags, GINT_TO_POINTER(TRUE));
4285 g_hash_table_destroy(item->flags_set_table);
4286 item->flags_set_table = NULL;
4288 if (item->flags_unset_table) {
4289 g_hash_table_foreach_remove(item->flags_unset_table, process_flags, GINT_TO_POINTER(FALSE));
4290 g_hash_table_destroy(item->flags_unset_table);
4291 item->flags_unset_table = NULL;
4295 static void imap_set_batch (Folder *folder, FolderItem *_item, gboolean batch)
4297 IMAPFolderItem *item = (IMAPFolderItem *)_item;
4299 g_return_if_fail(item != NULL);
4301 if (item->batching == batch)
4305 item->batching = TRUE;
4306 debug_print("IMAP switching to batch mode\n");
4307 if (!item->flags_set_table) {
4308 item->flags_set_table = g_hash_table_new(NULL, g_direct_equal);
4310 if (!item->flags_unset_table) {
4311 item->flags_unset_table = g_hash_table_new(NULL, g_direct_equal);
4314 debug_print("IMAP switching away from batch mode\n");
4316 process_hashtable(item);
4317 item->batching = FALSE;
4323 /* data types conversion libetpan <-> claws */
4327 #define ETPAN_IMAP_MB_MARKED 1
4328 #define ETPAN_IMAP_MB_UNMARKED 2
4329 #define ETPAN_IMAP_MB_NOSELECT 4
4330 #define ETPAN_IMAP_MB_NOINFERIORS 8
4332 static int imap_flags_to_flags(struct mailimap_mbx_list_flags * imap_flags)
4338 if (imap_flags->mbf_type == MAILIMAP_MBX_LIST_FLAGS_SFLAG) {
4339 switch (imap_flags->mbf_sflag) {
4340 case MAILIMAP_MBX_LIST_SFLAG_MARKED:
4341 flags |= ETPAN_IMAP_MB_MARKED;
4343 case MAILIMAP_MBX_LIST_SFLAG_NOSELECT:
4344 flags |= ETPAN_IMAP_MB_NOSELECT;
4346 case MAILIMAP_MBX_LIST_SFLAG_UNMARKED:
4347 flags |= ETPAN_IMAP_MB_UNMARKED;
4352 for(cur = clist_begin(imap_flags->mbf_oflags) ; cur != NULL ;
4353 cur = clist_next(cur)) {
4354 struct mailimap_mbx_list_oflag * oflag;
4356 oflag = clist_content(cur);
4358 switch (oflag->of_type) {
4359 case MAILIMAP_MBX_LIST_OFLAG_NOINFERIORS:
4360 flags |= ETPAN_IMAP_MB_NOINFERIORS;
4368 static GSList * imap_list_from_lep(IMAPFolder * folder,
4369 clist * list, const gchar * real_path, gboolean all)
4372 GSList * item_list = NULL, *llast = NULL;
4374 for(iter = clist_begin(list) ; iter != NULL ;
4375 iter = clist_next(iter)) {
4376 struct mailimap_mailbox_list * mb;
4384 FolderItem *new_item;
4386 mb = clist_content(iter);
4392 if (mb->mb_flag != NULL)
4393 flags = imap_flags_to_flags(mb->mb_flag);
4395 delimiter = mb->mb_delimiter;
4398 dup_name = strdup(name);
4399 if (delimiter != '\0')
4400 subst_char(dup_name, delimiter, '/');
4402 base = g_path_get_basename(dup_name);
4403 if (base[0] == '.') {
4408 if (!all && path_cmp(name, real_path) == 0) {
4414 if (!all && dup_name[strlen(dup_name)-1] == '/') {
4415 dup_name[strlen(dup_name)-1] = '\0';
4418 loc_name = imap_modified_utf7_to_utf8(base);
4419 loc_path = imap_modified_utf7_to_utf8(dup_name);
4421 new_item = folder_item_new(FOLDER(folder), loc_name, loc_path);
4422 if ((flags & ETPAN_IMAP_MB_NOINFERIORS) != 0)
4423 new_item->no_sub = TRUE;
4424 if (strcmp(dup_name, "INBOX") != 0 &&
4425 ((flags & ETPAN_IMAP_MB_NOSELECT) != 0))
4426 new_item->no_select = TRUE;
4428 if (item_list == NULL)
4429 llast = item_list = g_slist_append(item_list, new_item);
4431 llast = g_slist_append(llast, new_item);
4432 llast = llast->next;
4434 debug_print("folder '%s' found.\n", loc_path);
4445 static GSList * imap_get_lep_set_from_numlist(MsgNumberList *numlist)
4447 GSList *sorted_list, *cur;
4448 guint first, last, next;
4449 GSList *ret_list = NULL, *llast = NULL;
4451 struct mailimap_set * current_set;
4452 unsigned int item_count;
4454 if (numlist == NULL)
4458 current_set = mailimap_set_new_empty();
4460 sorted_list = g_slist_copy(numlist);
4461 sorted_list = g_slist_sort(sorted_list, g_int_compare);
4463 first = GPOINTER_TO_INT(sorted_list->data);
4466 for (cur = sorted_list; cur != NULL; cur = g_slist_next(cur)) {
4467 if (GPOINTER_TO_INT(cur->data) == 0)
4472 last = GPOINTER_TO_INT(cur->data);
4474 next = GPOINTER_TO_INT(cur->next->data);
4478 if (last + 1 != next || next == 0) {
4480 struct mailimap_set_item * item;
4481 item = mailimap_set_item_new(first, last);
4482 mailimap_set_add(current_set, item);
4487 if (count >= IMAP_SET_MAX_COUNT) {
4488 if (ret_list == NULL)
4489 llast = ret_list = g_slist_append(ret_list,
4492 llast = g_slist_append(llast, current_set);
4493 llast = llast->next;
4495 current_set = mailimap_set_new_empty();
4502 if (clist_count(current_set->set_list) > 0) {
4503 ret_list = g_slist_append(ret_list,
4507 g_slist_free(sorted_list);
4512 static GSList * imap_get_lep_set_from_msglist(MsgInfoList *msglist)
4514 MsgNumberList *numlist = NULL;
4518 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
4519 MsgInfo *msginfo = (MsgInfo *) cur->data;
4521 numlist = g_slist_prepend(numlist, GINT_TO_POINTER(msginfo->msgnum));
4523 numlist = g_slist_reverse(numlist);
4524 seq_list = imap_get_lep_set_from_numlist(numlist);
4525 g_slist_free(numlist);
4530 static GSList * imap_uid_list_from_lep(clist * list)
4537 for(iter = clist_begin(list) ; iter != NULL ;
4538 iter = clist_next(iter)) {
4541 puid = clist_content(iter);
4542 result = g_slist_prepend(result, GINT_TO_POINTER(* puid));
4545 result = g_slist_reverse(result);
4549 static GSList * imap_uid_list_from_lep_tab(carray * list)
4556 for(i = 0 ; i < carray_count(list) ; i ++) {
4559 puid = carray_get(list, i);
4560 result = g_slist_prepend(result, GINT_TO_POINTER(* puid));
4562 result = g_slist_reverse(result);
4566 static void imap_flags_hash_from_lep_uid_flags_tab(carray * list,
4574 for(i = 0 ; i < carray_count(list) ; i += 2) {
4579 puid = carray_get(list, i);
4580 pflags = carray_get(list, i + 1);
4581 pguid = malloc(sizeof(* pguid));
4584 g_hash_table_insert(hash, pguid, GINT_TO_POINTER(* pflags));
4588 static MsgInfo *imap_envelope_from_lep(struct imap_fetch_env_info * info,
4591 MsgInfo *msginfo = NULL;
4594 MsgFlags flags = {0, 0};
4596 if (info->headers == NULL)
4599 MSG_SET_TMP_FLAGS(flags, MSG_IMAP);
4600 if (folder_has_parent_of_type(item, F_QUEUE)) {
4601 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
4602 } else if (folder_has_parent_of_type(item, F_DRAFT)) {
4603 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
4605 flags.perm_flags = info->flags;
4609 msginfo = procheader_parse_str(info->headers, flags, FALSE, FALSE);
4612 msginfo->msgnum = uid;
4613 msginfo->size = size;
4619 static void imap_lep_set_free(GSList *seq_list)
4623 for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) {
4624 struct mailimap_set * imapset;
4626 imapset = cur->data;
4627 mailimap_set_free(imapset);
4629 g_slist_free(seq_list);
4632 static struct mailimap_flag_list * imap_flag_to_lep(IMAPFlags flags)
4634 struct mailimap_flag_list * flag_list;
4636 flag_list = mailimap_flag_list_new_empty();
4638 if (IMAP_IS_SEEN(flags))
4639 mailimap_flag_list_add(flag_list,
4640 mailimap_flag_new_seen());
4641 if (IMAP_IS_ANSWERED(flags))
4642 mailimap_flag_list_add(flag_list,
4643 mailimap_flag_new_answered());
4644 if (IMAP_IS_FLAGGED(flags))
4645 mailimap_flag_list_add(flag_list,
4646 mailimap_flag_new_flagged());
4647 if (IMAP_IS_DELETED(flags))
4648 mailimap_flag_list_add(flag_list,
4649 mailimap_flag_new_deleted());
4650 if (IMAP_IS_DRAFT(flags))
4651 mailimap_flag_list_add(flag_list,
4652 mailimap_flag_new_draft());
4657 guint imap_folder_get_refcnt(Folder *folder)
4659 return ((IMAPFolder *)folder)->refcnt;
4662 void imap_folder_ref(Folder *folder)
4664 ((IMAPFolder *)folder)->refcnt++;
4667 void imap_disconnect_all(void)
4670 for (list = account_get_list(); list != NULL; list = list->next) {
4671 PrefsAccount *account = list->data;
4672 if (account->protocol == A_IMAP4) {
4673 RemoteFolder *folder = (RemoteFolder *)account->folder;
4674 if (folder && folder->session) {
4675 IMAPSession *session = (IMAPSession *)folder->session;
4676 imap_threaded_disconnect(FOLDER(folder));
4677 SESSION(session)->state = SESSION_DISCONNECTED;
4678 session_destroy(SESSION(session));
4679 folder->session = NULL;
4685 void imap_folder_unref(Folder *folder)
4687 if (((IMAPFolder *)folder)->refcnt > 0)
4688 ((IMAPFolder *)folder)->refcnt--;
4691 void imap_cancel_all(void)
4696 folderlist = folder_get_list();
4697 for (cur = folderlist; cur != NULL; cur = g_list_next(cur)) {
4698 Folder *folder = (Folder *) cur->data;
4700 if (folder->klass == &imap_class) {
4701 if (imap_is_busy(folder)) {
4702 IMAPSession *imap_session;
4703 RemoteFolder *rfolder;
4705 fprintf(stderr, "cancelled\n");
4706 imap_threaded_cancel(folder);
4707 rfolder = (RemoteFolder *) folder;
4708 imap_session = (IMAPSession *) rfolder->session;
4710 imap_session->cancelled = 1;
4716 gboolean imap_cancel_all_enabled(void)
4721 folderlist = folder_get_list();
4722 for (cur = folderlist; cur != NULL; cur = g_list_next(cur)) {
4723 Folder *folder = (Folder *) cur->data;
4725 if (folder->klass == &imap_class) {
4726 if (imap_is_busy(folder)) {
4735 static gboolean imap_is_busy(Folder *folder)
4737 IMAPSession *imap_session;
4738 RemoteFolder *rfolder;
4740 rfolder = (RemoteFolder *) folder;
4741 imap_session = (IMAPSession *) rfolder->session;
4742 if (imap_session == NULL)
4745 return imap_session->busy;
4748 #else /* HAVE_LIBETPAN */
4750 static FolderClass imap_class;
4752 static XMLTag *imap_item_get_xml(Folder *folder, FolderItem *item);
4753 static void imap_item_set_xml(Folder *folder, FolderItem *item, XMLTag *tag);
4755 static Folder *imap_folder_new (const gchar *name,
4758 static gboolean missing_imap_warning = TRUE;
4759 if (missing_imap_warning) {
4760 missing_imap_warning = FALSE;
4762 _("You have one or more IMAP accounts "
4763 "defined. However this version of "
4764 "Claws Mail has been built without "
4765 "IMAP support; your IMAP account(s) are "
4767 "You probably need to "
4768 "install libetpan and recompile "
4773 static gint imap_create_tree (Folder *folder)
4777 static FolderItem *imap_create_folder (Folder *folder,
4783 static gint imap_rename_folder (Folder *folder,
4790 gchar imap_get_path_separator_for_item(FolderItem *item)
4795 FolderClass *imap_get_class(void)
4797 if (imap_class.idstr == NULL) {
4798 imap_class.type = F_IMAP;
4799 imap_class.idstr = "imap";
4800 imap_class.uistr = "IMAP4";
4802 imap_class.new_folder = imap_folder_new;
4803 imap_class.create_tree = imap_create_tree;
4804 imap_class.create_folder = imap_create_folder;
4805 imap_class.rename_folder = imap_rename_folder;
4807 imap_class.set_xml = folder_set_xml;
4808 imap_class.get_xml = folder_get_xml;
4809 imap_class.item_set_xml = imap_item_set_xml;
4810 imap_class.item_get_xml = imap_item_get_xml;
4811 /* nothing implemented */
4817 void imap_disconnect_all(void)
4821 gint imap_scan_tree_real(Folder *folder, gboolean subs_only)
4826 gint imap_subscribe(Folder *folder, FolderItem *item, gchar *rpath, gboolean sub)
4831 GList * imap_scan_subtree(Folder *folder, FolderItem *item, gboolean unsubs_only, gboolean recursive)
4836 void imap_cache_msg(FolderItem *item, gint msgnum)
4840 void imap_cancel_all(void)
4844 gboolean imap_cancel_all_enabled(void)
4851 void imap_synchronise(FolderItem *item, gint days)
4853 #ifdef HAVE_LIBETPAN
4854 if (IMAP_FOLDER_ITEM(item)->last_sync == IMAP_FOLDER_ITEM(item)->last_change) {
4855 debug_print("%s already synced\n", item->path?item->path:item->name);
4858 debug_print("syncing %s\n", item->path?item->path:item->name);
4859 imap_gtk_synchronise(item, days);
4860 IMAP_FOLDER_ITEM(item)->last_sync = IMAP_FOLDER_ITEM(item)->last_change;
4864 static void imap_item_set_xml(Folder *folder, FolderItem *item, XMLTag *tag)
4866 #ifdef HAVE_LIBETPAN
4869 folder_item_set_xml(folder, item, tag);
4871 #ifdef HAVE_LIBETPAN
4872 for (cur = tag->attr; cur != NULL; cur = g_list_next(cur)) {
4873 XMLAttr *attr = (XMLAttr *) cur->data;
4875 if (!attr || !attr->name || !attr->value) continue;
4876 if (!strcmp(attr->name, "uidnext"))
4877 IMAP_FOLDER_ITEM(item)->uid_next = atoi(attr->value);
4878 if (!strcmp(attr->name, "last_sync"))
4879 IMAP_FOLDER_ITEM(item)->last_sync = atoi(attr->value);
4880 if (!strcmp(attr->name, "last_change"))
4881 IMAP_FOLDER_ITEM(item)->last_change = atoi(attr->value);
4883 if (IMAP_FOLDER_ITEM(item)->last_change == 0)
4884 IMAP_FOLDER_ITEM(item)->last_change = time(NULL);
4888 static XMLTag *imap_item_get_xml(Folder *folder, FolderItem *item)
4892 tag = folder_item_get_xml(folder, item);
4894 #ifdef HAVE_LIBETPAN
4895 xml_tag_add_attr(tag, xml_attr_new_int("uidnext",
4896 IMAP_FOLDER_ITEM(item)->uid_next));
4897 xml_tag_add_attr(tag, xml_attr_new_int("last_sync",
4898 IMAP_FOLDER_ITEM(item)->last_sync));
4899 xml_tag_add_attr(tag, xml_attr_new_int("last_change",
4900 IMAP_FOLDER_ITEM(item)->last_change));