2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2007 Hiroyuki Yamamoto and the Claws Mail team
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
27 #include <glib/gi18n.h>
34 #include "alertpanel.h"
57 #include "procheader.h"
58 #include "prefs_account.h"
63 #include "prefs_common.h"
64 #include "inputdialog.h"
66 #include "remotefolder.h"
68 #include "statusbar.h"
70 #include "imap-thread.h"
73 typedef struct _IMAPFolder IMAPFolder;
74 typedef struct _IMAPSession IMAPSession;
75 typedef struct _IMAPNameSpace IMAPNameSpace;
76 typedef struct _IMAPFolderItem IMAPFolderItem;
78 #include "prefs_account.h"
80 #define IMAP_FOLDER(obj) ((IMAPFolder *)obj)
81 #define IMAP_FOLDER_ITEM(obj) ((IMAPFolderItem *)obj)
82 #define IMAP_SESSION(obj) ((IMAPSession *)obj)
88 /* list of IMAPNameSpace */
92 gchar last_seen_separator;
100 gboolean authenticated;
109 gboolean folder_content_changed;
115 struct _IMAPNameSpace
121 #define IMAP_SUCCESS 0
122 #define IMAP_SOCKET 2
123 #define IMAP_AUTHFAIL 3
124 #define IMAP_PROTOCOL 4
125 #define IMAP_SYNTAX 5
129 #define IMAPBUFSIZE 8192
133 IMAP_FLAG_SEEN = 1 << 0,
134 IMAP_FLAG_ANSWERED = 1 << 1,
135 IMAP_FLAG_FLAGGED = 1 << 2,
136 IMAP_FLAG_DELETED = 1 << 3,
137 IMAP_FLAG_DRAFT = 1 << 4
140 #define IMAP_IS_SEEN(flags) ((flags & IMAP_FLAG_SEEN) != 0)
141 #define IMAP_IS_ANSWERED(flags) ((flags & IMAP_FLAG_ANSWERED) != 0)
142 #define IMAP_IS_FLAGGED(flags) ((flags & IMAP_FLAG_FLAGGED) != 0)
143 #define IMAP_IS_DELETED(flags) ((flags & IMAP_FLAG_DELETED) != 0)
144 #define IMAP_IS_DRAFT(flags) ((flags & IMAP_FLAG_DRAFT) != 0)
147 #define IMAP4_PORT 143
149 #define IMAPS_PORT 993
152 #define IMAP_CMD_LIMIT 1000
154 struct _IMAPFolderItem
166 guint32 c_uid_validity;
169 GHashTable *flags_set_table;
170 GHashTable *flags_unset_table;
173 static XMLTag *imap_item_get_xml(Folder *folder, FolderItem *item);
174 static void imap_item_set_xml(Folder *folder, FolderItem *item, XMLTag *tag);
176 static void imap_folder_init (Folder *folder,
180 static Folder *imap_folder_new (const gchar *name,
182 static void imap_folder_destroy (Folder *folder);
184 static IMAPSession *imap_session_new (Folder *folder,
185 const PrefsAccount *account);
186 static void imap_session_authenticate(IMAPSession *session,
187 const PrefsAccount *account);
188 static void imap_session_destroy (Session *session);
190 static gchar *imap_fetch_msg (Folder *folder,
193 static gchar *imap_fetch_msg_full (Folder *folder,
198 static gint imap_add_msg (Folder *folder,
202 static gint imap_add_msgs (Folder *folder,
205 GRelation *relation);
207 static gint imap_copy_msg (Folder *folder,
210 static gint imap_copy_msgs (Folder *folder,
212 MsgInfoList *msglist,
213 GRelation *relation);
215 static gint imap_remove_msg (Folder *folder,
218 static gint imap_remove_msgs (Folder *folder,
220 MsgInfoList *msglist,
221 GRelation *relation);
222 static gint imap_remove_all_msg (Folder *folder,
225 static gboolean imap_is_msg_changed (Folder *folder,
229 static gint imap_close (Folder *folder,
232 static gint imap_scan_tree (Folder *folder);
234 static gint imap_create_tree (Folder *folder);
236 static FolderItem *imap_create_folder (Folder *folder,
239 static gint imap_rename_folder (Folder *folder,
242 static gint imap_remove_folder (Folder *folder,
245 static FolderItem *imap_folder_item_new (Folder *folder);
246 static void imap_folder_item_destroy (Folder *folder,
249 static IMAPSession *imap_session_get (Folder *folder);
251 static gint imap_auth (IMAPSession *session,
256 static gint imap_scan_tree_recursive (IMAPSession *session,
260 static void imap_create_missing_folders (Folder *folder);
261 static FolderItem *imap_create_special_folder
263 SpecialFolderItemType stype,
266 static gint imap_do_copy_msgs (Folder *folder,
268 MsgInfoList *msglist,
269 GRelation *relation);
271 static void imap_delete_all_cached_messages (FolderItem *item);
272 static void imap_set_batch (Folder *folder,
275 static gint imap_set_message_flags (IMAPSession *session,
276 MsgNumberList *numlist,
279 static gint imap_select (IMAPSession *session,
285 guint32 *uid_validity,
287 static gint imap_status (IMAPSession *session,
290 IMAPFolderItem *item,
293 guint32 *uid_validity,
297 static gchar imap_get_path_separator (IMAPSession *session,
300 static gchar *imap_get_real_path (IMAPSession *session,
303 static void imap_synchronise (FolderItem *item);
305 static void imap_free_capabilities (IMAPSession *session);
307 /* low-level IMAP4rev1 commands */
308 static gint imap_cmd_login (IMAPSession *session,
312 static gint imap_cmd_noop (IMAPSession *session);
314 static gint imap_cmd_starttls (IMAPSession *session);
316 static gint imap_cmd_select (IMAPSession *session,
321 guint32 *uid_validity,
323 static gint imap_cmd_examine (IMAPSession *session,
328 guint32 *uid_validity,
330 static gint imap_cmd_create (IMAPSession *sock,
331 const gchar *folder);
332 static gint imap_cmd_rename (IMAPSession *sock,
333 const gchar *oldfolder,
334 const gchar *newfolder);
335 static gint imap_cmd_delete (IMAPSession *session,
336 const gchar *folder);
337 static gint imap_cmd_fetch (IMAPSession *sock,
339 const gchar *filename,
342 static gint imap_cmd_append (IMAPSession *session,
343 const gchar *destfolder,
347 static gint imap_cmd_copy (IMAPSession *session,
348 struct mailimap_set * set,
349 const gchar *destfolder,
350 GRelation *uid_mapping,
351 struct mailimap_set ** source,
352 struct mailimap_set ** dest);
353 static gint imap_cmd_store (IMAPSession *session,
354 struct mailimap_set * set,
357 static gint imap_cmd_expunge (IMAPSession *session);
359 static void imap_path_separator_subst (gchar *str,
362 static gchar *imap_utf8_to_modified_utf7 (const gchar *from);
363 static gchar *imap_modified_utf7_to_utf8 (const gchar *mutf7_str);
365 static gboolean imap_rename_folder_func (GNode *node,
367 static gint imap_get_num_list (Folder *folder,
370 gboolean *old_uids_valid);
371 static GSList *imap_get_msginfos (Folder *folder,
373 GSList *msgnum_list);
374 static MsgInfo *imap_get_msginfo (Folder *folder,
377 static gboolean imap_scan_required (Folder *folder,
379 static void imap_change_flags (Folder *folder,
382 MsgPermFlags newflags);
383 static gint imap_get_flags (Folder *folder,
385 MsgInfoList *msglist,
386 GRelation *msgflags);
387 static gchar *imap_folder_get_path (Folder *folder);
388 static gchar *imap_item_get_path (Folder *folder,
390 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item);
393 /* data types conversion libetpan <-> claws */
394 static GSList * imap_list_from_lep(IMAPFolder * folder,
395 clist * list, const gchar * real_path, gboolean all);
396 static GSList * imap_get_lep_set_from_numlist(MsgNumberList *numlist);
397 static GSList * imap_get_lep_set_from_msglist(MsgInfoList *msglist);
398 static GSList * imap_uid_list_from_lep(clist * list);
399 static GSList * imap_uid_list_from_lep_tab(carray * list);
400 static MsgInfo *imap_envelope_from_lep(struct imap_fetch_env_info * info,
402 static void imap_lep_set_free(GSList *seq_list);
403 static struct mailimap_flag_list * imap_flag_to_lep(IMAPFlags flags);
405 typedef struct _hashtable_data {
406 IMAPSession *session;
408 IMAPFolderItem *item;
411 static FolderClass imap_class;
413 typedef struct _thread_data {
423 FolderClass *imap_get_class(void)
425 if (imap_class.idstr == NULL) {
426 imap_class.type = F_IMAP;
427 imap_class.idstr = "imap";
428 imap_class.uistr = "IMAP4";
430 /* Folder functions */
431 imap_class.new_folder = imap_folder_new;
432 imap_class.destroy_folder = imap_folder_destroy;
433 imap_class.scan_tree = imap_scan_tree;
434 imap_class.create_tree = imap_create_tree;
436 /* FolderItem functions */
437 imap_class.item_new = imap_folder_item_new;
438 imap_class.item_destroy = imap_folder_item_destroy;
439 imap_class.item_get_path = imap_item_get_path;
440 imap_class.create_folder = imap_create_folder;
441 imap_class.rename_folder = imap_rename_folder;
442 imap_class.remove_folder = imap_remove_folder;
443 imap_class.close = imap_close;
444 imap_class.get_num_list = imap_get_num_list;
445 imap_class.scan_required = imap_scan_required;
446 imap_class.set_xml = folder_set_xml;
447 imap_class.get_xml = folder_get_xml;
448 imap_class.item_set_xml = imap_item_set_xml;
449 imap_class.item_get_xml = imap_item_get_xml;
451 /* Message functions */
452 imap_class.get_msginfo = imap_get_msginfo;
453 imap_class.get_msginfos = imap_get_msginfos;
454 imap_class.fetch_msg = imap_fetch_msg;
455 imap_class.fetch_msg_full = imap_fetch_msg_full;
456 imap_class.add_msg = imap_add_msg;
457 imap_class.add_msgs = imap_add_msgs;
458 imap_class.copy_msg = imap_copy_msg;
459 imap_class.copy_msgs = imap_copy_msgs;
460 imap_class.remove_msg = imap_remove_msg;
461 imap_class.remove_msgs = imap_remove_msgs;
462 imap_class.remove_all_msg = imap_remove_all_msg;
463 imap_class.is_msg_changed = imap_is_msg_changed;
464 imap_class.change_flags = imap_change_flags;
465 imap_class.get_flags = imap_get_flags;
466 imap_class.set_batch = imap_set_batch;
467 imap_class.synchronise = imap_synchronise;
469 pthread_mutex_init(&imap_mutex, NULL);
476 static Folder *imap_folder_new(const gchar *name, const gchar *path)
480 folder = (Folder *)g_new0(IMAPFolder, 1);
481 folder->klass = &imap_class;
482 imap_folder_init(folder, name, path);
487 static void imap_folder_destroy(Folder *folder)
489 while (imap_folder_get_refcnt(folder) > 0)
490 gtk_main_iteration();
492 folder_remote_folder_destroy(REMOTE_FOLDER(folder));
496 static void imap_folder_init(Folder *folder, const gchar *name,
499 folder_remote_folder_init((Folder *)folder, name, path);
502 static FolderItem *imap_folder_item_new(Folder *folder)
504 IMAPFolderItem *item;
506 item = g_new0(IMAPFolderItem, 1);
509 item->uid_list = NULL;
511 return (FolderItem *)item;
514 static void imap_folder_item_destroy(Folder *folder, FolderItem *_item)
516 IMAPFolderItem *item = (IMAPFolderItem *)_item;
518 g_return_if_fail(item != NULL);
519 g_slist_free(item->uid_list);
524 static gboolean imap_reset_uid_lists_func(GNode *node, gpointer data)
526 IMAPFolderItem *item = (IMAPFolderItem *)node->data;
529 g_slist_free(item->uid_list);
530 item->uid_list = NULL;
535 static void imap_reset_uid_lists(Folder *folder)
537 if(folder->node == NULL)
540 /* Destroy all uid lists and rest last uid */
541 g_node_traverse(folder->node, G_IN_ORDER, G_TRAVERSE_ALL, -1, imap_reset_uid_lists_func, NULL);
544 static int imap_get_capabilities(IMAPSession *session)
546 struct mailimap_capability_data *capabilities = NULL;
550 if (session->capability != NULL)
551 return MAILIMAP_NO_ERROR;
553 capabilities = imap_threaded_capability(session->folder, &result);
555 if (result != MAILIMAP_NO_ERROR) {
556 return MAILIMAP_ERROR_CAPABILITY;
559 if (capabilities == NULL) {
560 return MAILIMAP_NO_ERROR;
563 for(cur = clist_begin(capabilities->cap_list) ; cur != NULL ;
564 cur = clist_next(cur)) {
565 struct mailimap_capability * cap =
567 if (!cap || cap->cap_data.cap_name == NULL)
569 session->capability = g_slist_append
570 (session->capability,
571 g_strdup(cap->cap_data.cap_name));
572 debug_print("got capa %s\n", cap->cap_data.cap_name);
574 mailimap_capability_data_free(capabilities);
575 return MAILIMAP_NO_ERROR;
578 static gboolean imap_has_capability(IMAPSession *session, const gchar *cap)
581 for (cur = session->capability; cur; cur = cur->next) {
582 if (!g_ascii_strcasecmp(cur->data, cap))
588 static gint imap_auth(IMAPSession *session, const gchar *user, const gchar *pass,
591 gint ok = IMAP_ERROR;
592 static time_t last_login_err = 0;
593 gchar *ext_info = "";
595 if (imap_get_capabilities(session) != MAILIMAP_NO_ERROR)
600 ok = imap_cmd_login(session, user, pass, "ANONYMOUS");
602 case IMAP_AUTH_CRAM_MD5:
603 ok = imap_cmd_login(session, user, pass, "CRAM-MD5");
605 case IMAP_AUTH_LOGIN:
606 ok = imap_cmd_login(session, user, pass, "LOGIN");
608 case IMAP_AUTH_GSSAPI:
609 ok = imap_cmd_login(session, user, pass, "GSSAPI");
612 debug_print("capabilities:\n"
617 imap_has_capability(session, "ANONYMOUS"),
618 imap_has_capability(session, "CRAM-MD5"),
619 imap_has_capability(session, "LOGIN"),
620 imap_has_capability(session, "GSSAPI"));
621 if (imap_has_capability(session, "CRAM-MD5"))
622 ok = imap_cmd_login(session, user, pass, "CRAM-MD5");
623 if (ok == IMAP_ERROR && imap_has_capability(session, "GSSAPI"))
624 ok = imap_cmd_login(session, user, pass, "GSSAPI");
625 if (ok == IMAP_ERROR) /* we always try LOGIN before giving up */
626 ok = imap_cmd_login(session, user, pass, "LOGIN");
629 if (ok == IMAP_SUCCESS)
630 session->authenticated = TRUE;
632 if (type == IMAP_AUTH_CRAM_MD5) {
633 ext_info = _("\n\nCRAM-MD5 logins only work if libetpan has been "
634 "compiled with SASL support and the "
635 "CRAM-MD5 SASL plugin is installed.");
638 if (time(NULL) - last_login_err > 10) {
639 if (!prefs_common.no_recv_err_panel) {
640 alertpanel_error(_("Connection to %s failed: "
642 SESSION(session)->server, ext_info);
644 log_error(LOG_PROTOCOL, _("Connection to %s failed: "
645 "login refused.%s\n"),
646 SESSION(session)->server, ext_info);
649 last_login_err = time(NULL);
654 static IMAPSession *imap_reconnect_if_possible(Folder *folder, IMAPSession *session)
656 RemoteFolder *rfolder = REMOTE_FOLDER(folder);
657 /* Check if this is the first try to establish a
658 connection, if yes we don't try to reconnect */
659 debug_print("reconnecting\n");
660 if (rfolder->session == NULL) {
661 log_warning(LOG_PROTOCOL, _("Connecting to %s failed"),
662 folder->account->recv_server);
663 session_destroy(SESSION(session));
666 log_warning(LOG_PROTOCOL, _("IMAP4 connection to %s has been"
667 " disconnected. Reconnecting...\n"),
668 folder->account->recv_server);
669 statusbar_print_all(_("IMAP4 connection to %s has been"
670 " disconnected. Reconnecting...\n"),
671 folder->account->recv_server);
672 SESSION(session)->state = SESSION_DISCONNECTED;
673 session_destroy(SESSION(session));
674 /* Clear folders session to make imap_session_get create
675 a new session, because of rfolder->session == NULL
676 it will not try to reconnect again and so avoid an
678 rfolder->session = NULL;
679 debug_print("getting session...\n");
680 session = imap_session_get(folder);
681 rfolder->session = SESSION(session);
687 #define lock_session() {\
689 debug_print("locking session %p (%d)\n", session, session->busy); \
691 debug_print(" SESSION WAS LOCKED !! \n"); \
692 session->busy = TRUE;\
694 debug_print("can't lock null session\n"); \
698 #define unlock_session() {\
700 debug_print("unlocking session %p\n", session); \
701 session->busy = FALSE;\
703 debug_print("can't unlock null session\n"); \
707 static IMAPSession *imap_session_get(Folder *folder)
709 RemoteFolder *rfolder = REMOTE_FOLDER(folder);
710 IMAPSession *session = NULL;
712 g_return_val_if_fail(folder != NULL, NULL);
713 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, NULL);
714 g_return_val_if_fail(folder->account != NULL, NULL);
716 if (prefs_common.work_offline &&
717 !inc_offline_should_override(FALSE,
718 _("Claws Mail needs network access in order "
719 "to access the IMAP server."))) {
723 /* Make sure we have a session */
724 if (rfolder->session != NULL) {
725 session = IMAP_SESSION(rfolder->session);
726 } else if (rfolder->connecting) {
727 debug_print("already connecting\n");
730 imap_reset_uid_lists(folder);
731 if (time(NULL) - rfolder->last_failure <= 2)
733 rfolder->connecting = TRUE;
734 session = imap_session_new(folder, folder->account);
736 if(session == NULL) {
737 rfolder->last_failure = time(NULL);
738 rfolder->connecting = FALSE;
742 /* Make sure session is authenticated */
743 if (!IMAP_SESSION(session)->authenticated)
744 imap_session_authenticate(IMAP_SESSION(session), folder->account);
746 if (!IMAP_SESSION(session)->authenticated) {
747 imap_threaded_disconnect(session->folder);
748 SESSION(session)->state = SESSION_DISCONNECTED;
749 session_destroy(SESSION(session));
750 rfolder->session = NULL;
751 rfolder->last_failure = time(NULL);
752 rfolder->connecting = FALSE;
758 /* I think the point of this code is to avoid sending a
759 * keepalive if we've used the session recently and therefore
760 * think it's still alive. Unfortunately, most of the code
761 * does not yet check for errors on the socket, and so if the
762 * connection drops we don't notice until the timeout expires.
763 * A better solution than sending a NOOP every time would be
764 * for every command to be prepared to retry until it is
765 * successfully sent. -- mbp */
766 if (time(NULL) - SESSION(session)->last_access_time > SESSION_TIMEOUT_INTERVAL) {
767 /* verify that the session is still alive */
768 if (imap_cmd_noop(session) != IMAP_SUCCESS) {
769 debug_print("disconnected!\n");
770 session = imap_reconnect_if_possible(folder, session);
774 rfolder->session = SESSION(session);
775 rfolder->connecting = FALSE;
777 return IMAP_SESSION(session);
780 static IMAPSession *imap_session_new(Folder * folder,
781 const PrefsAccount *account)
783 IMAPSession *session;
786 int authenticated = FALSE;
789 /* FIXME: IMAP over SSL only... */
792 port = account->set_imapport ? account->imapport
793 : account->ssl_imap == SSL_TUNNEL ? IMAPS_PORT : IMAP4_PORT;
794 ssl_type = account->ssl_imap;
796 if (account->ssl_imap != SSL_NONE) {
797 if (alertpanel_full(_("Insecure connection"),
798 _("This connection is configured to be secured "
799 "using SSL, but SSL is not available in this "
800 "build of Claws Mail. \n\n"
801 "Do you want to continue connecting to this "
802 "server? The communication would not be "
804 GTK_STOCK_CANCEL, _("Con_tinue connecting"),
805 NULL, FALSE, NULL, ALERT_WARNING,
806 G_ALERTDEFAULT) != G_ALERTALTERNATE)
809 port = account->set_imapport ? account->imapport
814 statusbar_print_all(_("Connecting to IMAP4 server: %s..."), folder->account->recv_server);
815 if (account->set_tunnelcmd) {
816 r = imap_threaded_connect_cmd(folder,
818 account->recv_server,
823 if (ssl_type == SSL_TUNNEL) {
824 r = imap_threaded_connect_ssl(folder,
825 account->recv_server,
831 r = imap_threaded_connect(folder,
832 account->recv_server,
838 if (r == MAILIMAP_NO_ERROR_AUTHENTICATED) {
839 authenticated = TRUE;
841 else if (r == MAILIMAP_NO_ERROR_NON_AUTHENTICATED) {
842 authenticated = FALSE;
845 #if (LIBETPAN_VERSION_MAJOR > 0 || LIBETPAN_VERSION_MINOR > 48)
847 if (r == MAILIMAP_ERROR_SSL)
848 log_error(LOG_PROTOCOL, _("SSL handshake failed\n"));
851 if(!prefs_common.no_recv_err_panel) {
852 alertpanel_error_log(_("Can't connect to IMAP4 server: %s:%d"),
853 account->recv_server, port);
855 log_error(LOG_PROTOCOL, _("Can't connect to IMAP4 server: %s:%d\n"),
856 account->recv_server, port);
862 session = g_new0(IMAPSession, 1);
863 session_init(SESSION(session));
864 SESSION(session)->type = SESSION_IMAP;
865 SESSION(session)->server = g_strdup(account->recv_server);
866 SESSION(session)->sock = NULL;
868 SESSION(session)->destroy = imap_session_destroy;
870 session->capability = NULL;
872 session->authenticated = authenticated;
873 session->mbox = NULL;
874 session->cmd_count = 0;
875 session->folder = folder;
876 IMAP_FOLDER(session->folder)->last_seen_separator = 0;
879 if (account->ssl_imap == SSL_STARTTLS) {
882 ok = imap_cmd_starttls(session);
883 if (ok != IMAP_SUCCESS) {
884 log_warning(LOG_PROTOCOL, _("Can't start TLS session.\n"));
885 session_destroy(SESSION(session));
889 imap_free_capabilities(session);
890 session->authenticated = FALSE;
891 session->uidplus = FALSE;
892 session->cmd_count = 1;
895 log_message(LOG_PROTOCOL, "IMAP connection is %s-authenticated\n",
896 (session->authenticated) ? "pre" : "un");
901 static void imap_session_authenticate(IMAPSession *session,
902 const PrefsAccount *account)
904 gchar *pass, *acc_pass;
905 gboolean failed = FALSE;
907 g_return_if_fail(account->userid != NULL);
908 acc_pass = account->passwd;
911 if (!pass && account->imap_auth_type != IMAP_AUTH_ANON) {
913 tmp_pass = input_dialog_query_password(account->recv_server, account->userid);
916 Xstrdup_a(pass, tmp_pass, {g_free(tmp_pass); return;});
918 } else if (account->imap_auth_type == IMAP_AUTH_ANON) {
921 statusbar_print_all(_("Connecting to IMAP4 server %s...\n"),
922 account->recv_server);
923 if (imap_auth(session, account->userid, pass, account->imap_auth_type) != IMAP_SUCCESS) {
931 if (prefs_common.no_recv_err_panel) {
932 log_error(LOG_PROTOCOL, _("Couldn't login to IMAP server %s."), account->recv_server);
933 mainwindow_show_error();
935 alertpanel_error_log(_("Couldn't login to IMAP server %s."), account->recv_server);
942 session->authenticated = TRUE;
946 static void imap_session_destroy(Session *session)
948 if (session->state != SESSION_DISCONNECTED)
949 imap_threaded_disconnect(IMAP_SESSION(session)->folder);
951 imap_free_capabilities(IMAP_SESSION(session));
952 g_free(IMAP_SESSION(session)->mbox);
953 sock_close(session->sock);
954 session->sock = NULL;
957 static gchar *imap_fetch_msg(Folder *folder, FolderItem *item, gint uid)
959 return imap_fetch_msg_full(folder, item, uid, TRUE, TRUE);
962 static guint get_file_size_with_crs(const gchar *filename)
968 if (filename == NULL)
971 fp = fopen(filename, "rb");
975 while (fgets(buf, sizeof (buf), fp) != NULL) {
977 if (!strstr(buf, "\r\n") && strstr(buf, "\n"))
985 static gchar *imap_fetch_msg_full(Folder *folder, FolderItem *item, gint uid,
986 gboolean headers, gboolean body)
988 gchar *path, *filename;
989 IMAPSession *session;
992 g_return_val_if_fail(folder != NULL, NULL);
993 g_return_val_if_fail(item != NULL, NULL);
998 path = folder_item_get_path(item);
999 if (!is_dir_exist(path))
1000 make_dir_hier(path);
1001 filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(uid), NULL);
1003 debug_print("trying to fetch cached %s\n", filename);
1004 if (is_file_exist(filename)) {
1005 /* see whether the local file represents the whole message
1006 * or not. As the IMAP server reports size with \r chars,
1007 * we have to update the local file (UNIX \n only) size */
1008 MsgInfo *cached = msgcache_get_msg(item->cache,uid);
1009 guint have_size = get_file_size_with_crs(filename);
1012 debug_print("message %d has been already %scached (%d/%d).\n", uid,
1013 have_size >= cached->size ? "fully ":"",
1014 have_size, (int)cached->size);
1016 if (cached && (cached->size <= have_size || !body)) {
1017 procmsg_msginfo_free(cached);
1018 file_strip_crs(filename);
1020 } else if (!cached && time(NULL) - get_file_mtime(filename) < 60) {
1021 debug_print("message not cached and file recent, considering file complete\n");
1022 file_strip_crs(filename);
1025 procmsg_msginfo_free(cached);
1029 debug_print("getting session...\n");
1030 session = imap_session_get(folder);
1037 debug_print("IMAP fetching messages\n");
1038 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
1039 NULL, NULL, NULL, NULL, FALSE);
1040 if (ok != IMAP_SUCCESS) {
1041 g_warning("can't select mailbox %s\n", item->path);
1047 debug_print("getting message %d...\n", uid);
1048 ok = imap_cmd_fetch(session, (guint32)uid, filename, headers, body);
1050 if (ok != IMAP_SUCCESS) {
1051 g_warning("can't fetch message %d\n", uid);
1058 file_strip_crs(filename);
1062 static gboolean imap_is_msg_fully_cached(Folder *folder, FolderItem *item, gint uid)
1064 gchar *path, *filename;
1066 MsgInfo *cached = msgcache_get_msg(item->cache,uid);
1071 path = folder_item_get_path(item);
1072 if (!is_dir_exist(path))
1075 filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(uid), NULL);
1077 if (is_file_exist(filename)) {
1078 if (cached && cached->total_size == cached->size) {
1083 size = get_file_size_with_crs(filename);
1086 if (cached && size >= cached->size) {
1087 cached->total_size = cached->size;
1088 procmsg_msginfo_free(cached);
1092 procmsg_msginfo_free(cached);
1096 void imap_cache_msg(FolderItem *item, gint msgnum)
1098 Folder *folder = NULL;
1102 folder = item->folder;
1104 if (!imap_is_msg_fully_cached(folder, item, msgnum)) {
1105 gchar *tmp = imap_fetch_msg_full(folder, item, msgnum, TRUE, TRUE);
1106 debug_print("fetched %s\n", tmp);
1111 static gint imap_add_msg(Folder *folder, FolderItem *dest,
1112 const gchar *file, MsgFlags *flags)
1116 MsgFileInfo fileinfo;
1118 g_return_val_if_fail(file != NULL, -1);
1120 fileinfo.msginfo = NULL;
1121 fileinfo.file = (gchar *)file;
1122 fileinfo.flags = flags;
1123 file_list.data = &fileinfo;
1124 file_list.next = NULL;
1126 ret = imap_add_msgs(folder, dest, &file_list, NULL);
1130 static gint imap_add_msgs(Folder *folder, FolderItem *dest, GSList *file_list,
1131 GRelation *relation)
1134 IMAPSession *session;
1135 guint32 last_uid = 0;
1137 MsgFileInfo *fileinfo;
1139 gint curnum = 0, total = 0;
1142 g_return_val_if_fail(folder != NULL, -1);
1143 g_return_val_if_fail(dest != NULL, -1);
1144 g_return_val_if_fail(file_list != NULL, -1);
1146 debug_print("getting session...\n");
1147 session = imap_session_get(folder);
1151 destdir = imap_get_real_path(session, IMAP_FOLDER(folder), dest->path);
1153 statusbar_print_all(_("Adding messages..."));
1154 total = g_slist_length(file_list);
1155 for (cur = file_list; cur != NULL; cur = cur->next) {
1156 IMAPFlags iflags = 0;
1157 guint32 new_uid = 0;
1158 gchar *real_file = NULL;
1159 fileinfo = (MsgFileInfo *)cur->data;
1161 statusbar_progress_all(curnum, total, total < 10 ? 1:10);
1164 if (fileinfo->flags) {
1165 if (MSG_IS_MARKED(*fileinfo->flags))
1166 iflags |= IMAP_FLAG_FLAGGED;
1167 if (MSG_IS_REPLIED(*fileinfo->flags))
1168 iflags |= IMAP_FLAG_ANSWERED;
1169 if (!MSG_IS_UNREAD(*fileinfo->flags))
1170 iflags |= IMAP_FLAG_SEEN;
1173 if (real_file == NULL)
1174 real_file = g_strdup(fileinfo->file);
1176 if (folder_has_parent_of_type(dest, F_QUEUE) ||
1177 folder_has_parent_of_type(dest, F_OUTBOX) ||
1178 folder_has_parent_of_type(dest, F_DRAFT) ||
1179 folder_has_parent_of_type(dest, F_TRASH))
1180 iflags |= IMAP_FLAG_SEEN;
1182 ok = imap_cmd_append(session, destdir, real_file, iflags,
1185 if (ok != IMAP_SUCCESS) {
1186 g_warning("can't append message %s\n", real_file);
1190 statusbar_progress_all(0,0,0);
1191 statusbar_pop_all();
1194 debug_print("appended new message as %d\n", new_uid);
1195 /* put the local file in the imapcache, so that we don't
1196 * have to fetch it back later. */
1198 gchar *cache_path = folder_item_get_path(dest);
1199 if (!is_dir_exist(cache_path))
1200 make_dir_hier(cache_path);
1201 if (is_dir_exist(cache_path)) {
1202 gchar *cache_file = g_strconcat(
1203 cache_path, G_DIR_SEPARATOR_S,
1204 itos(new_uid), NULL);
1205 copy_file(real_file, cache_file, TRUE);
1206 debug_print("copied to cache: %s\n", cache_file);
1213 if (relation != NULL)
1214 g_relation_insert(relation, fileinfo->msginfo != NULL ?
1215 (gpointer) fileinfo->msginfo : (gpointer) fileinfo,
1216 GINT_TO_POINTER(dest->last_num + 1));
1218 new_uid = dest->last_num+1;
1220 if (last_uid < new_uid) {
1226 statusbar_progress_all(0,0,0);
1227 statusbar_pop_all();
1229 imap_cmd_expunge(session);
1237 static gint imap_do_copy_msgs(Folder *folder, FolderItem *dest,
1238 MsgInfoList *msglist, GRelation *relation)
1242 GSList *seq_list, *cur;
1244 IMAPSession *session;
1245 gint ok = IMAP_SUCCESS;
1246 GRelation *uid_mapping;
1248 gboolean single = FALSE;
1250 g_return_val_if_fail(folder != NULL, -1);
1251 g_return_val_if_fail(dest != NULL, -1);
1252 g_return_val_if_fail(msglist != NULL, -1);
1254 debug_print("getting session...\n");
1255 session = imap_session_get(folder);
1261 msginfo = (MsgInfo *)msglist->data;
1262 if (msglist->next == NULL)
1264 src = msginfo->folder;
1266 g_warning("the src folder is identical to the dest.\n");
1271 if (src->folder != dest->folder) {
1272 GSList *infolist = NULL, *cur;
1274 for (cur = msglist; cur; cur = cur->next) {
1275 msginfo = (MsgInfo *)cur->data;
1276 MsgFileInfo *fileinfo = g_new0(MsgFileInfo, 1);
1277 fileinfo->file = procmsg_get_message_file(msginfo);
1278 fileinfo->flags = &(msginfo->flags);
1279 infolist = g_slist_prepend(infolist, fileinfo);
1281 infolist = g_slist_reverse(infolist);
1283 res = folder_item_add_msgs(dest, infolist, FALSE);
1284 for (cur = infolist; cur; cur = cur->next) {
1285 MsgFileInfo *info = (MsgFileInfo *)cur->data;
1289 g_slist_free(infolist);
1293 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
1294 NULL, NULL, NULL, NULL, FALSE);
1295 if (ok != IMAP_SUCCESS) {
1300 destdir = imap_get_real_path(session, IMAP_FOLDER(folder), dest->path);
1301 seq_list = imap_get_lep_set_from_msglist(msglist);
1302 uid_mapping = g_relation_new(2);
1303 g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
1305 statusbar_print_all(_("Copying messages..."));
1306 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
1307 struct mailimap_set * seq_set;
1308 struct mailimap_set * source = NULL;
1309 struct mailimap_set * dest = NULL;
1310 seq_set = cur->data;
1312 debug_print("Copying messages from %s to %s ...\n",
1313 src->path, destdir);
1315 ok = imap_cmd_copy(session, seq_set, destdir, uid_mapping,
1318 if (ok == IMAP_SUCCESS) {
1319 if (single && relation && source && dest) {
1320 clistiter *l = clist_begin(source->set_list);
1321 struct mailimap_set_item *i = (struct mailimap_set_item *)clist_content(l);
1322 int snum = i->set_first;
1324 l = clist_begin(dest->set_list);
1325 i = (struct mailimap_set_item *)clist_content(l);
1326 dnum = i->set_first;
1327 g_relation_insert(uid_mapping, GINT_TO_POINTER(snum),
1328 GINT_TO_POINTER(dnum));
1334 mailimap_set_free(source);
1336 mailimap_set_free(dest);
1338 if (ok != IMAP_SUCCESS) {
1339 g_relation_destroy(uid_mapping);
1340 imap_lep_set_free(seq_list);
1342 statusbar_pop_all();
1347 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
1348 MsgInfo *msginfo = (MsgInfo *)cur->data;
1351 tuples = g_relation_select(uid_mapping,
1352 GINT_TO_POINTER(msginfo->msgnum),
1354 if (tuples->len > 0) {
1355 gint num = GPOINTER_TO_INT(g_tuples_index(tuples, 0, 1));
1356 g_relation_insert(relation, msginfo,
1357 GINT_TO_POINTER(num));
1360 debug_print("copied new message as %d\n", num);
1361 /* put the local file in the imapcache, so that we don't
1362 * have to fetch it back later. */
1364 gchar *cache_path = folder_item_get_path(msginfo->folder);
1365 gchar *real_file = g_strconcat(
1366 cache_path, G_DIR_SEPARATOR_S,
1367 itos(msginfo->msgnum), NULL);
1368 gchar *cache_file = NULL;
1370 cache_path = folder_item_get_path(dest);
1371 cache_file = g_strconcat(
1372 cache_path, G_DIR_SEPARATOR_S,
1374 if (!is_dir_exist(cache_path))
1375 make_dir_hier(cache_path);
1376 if (is_file_exist(real_file) && is_dir_exist(cache_path)) {
1377 copy_file(real_file, cache_file, TRUE);
1378 debug_print("copied to cache: %s\n", cache_file);
1385 g_relation_insert(relation, msginfo,
1386 GINT_TO_POINTER(0));
1387 g_tuples_destroy(tuples);
1389 statusbar_pop_all();
1391 g_relation_destroy(uid_mapping);
1392 imap_lep_set_free(seq_list);
1396 IMAP_FOLDER_ITEM(dest)->lastuid = 0;
1397 IMAP_FOLDER_ITEM(dest)->uid_next = 0;
1398 g_slist_free(IMAP_FOLDER_ITEM(dest)->uid_list);
1399 IMAP_FOLDER_ITEM(dest)->uid_list = NULL;
1402 if (ok == IMAP_SUCCESS)
1408 static gint imap_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1412 g_return_val_if_fail(msginfo != NULL, -1);
1414 msglist.data = msginfo;
1415 msglist.next = NULL;
1417 return imap_copy_msgs(folder, dest, &msglist, NULL);
1420 static gint imap_copy_msgs(Folder *folder, FolderItem *dest,
1421 MsgInfoList *msglist, GRelation *relation)
1426 g_return_val_if_fail(folder != NULL, -1);
1427 g_return_val_if_fail(dest != NULL, -1);
1428 g_return_val_if_fail(msglist != NULL, -1);
1430 msginfo = (MsgInfo *)msglist->data;
1431 g_return_val_if_fail(msginfo->folder != NULL, -1);
1433 ret = imap_do_copy_msgs(folder, dest, msglist, relation);
1438 static gint imap_do_remove_msgs(Folder *folder, FolderItem *dest,
1439 MsgInfoList *msglist, GRelation *relation)
1441 gchar *destdir, *dir;
1442 GSList *numlist = NULL, *cur;
1444 IMAPSession *session;
1445 gint ok = IMAP_SUCCESS;
1446 GRelation *uid_mapping;
1448 g_return_val_if_fail(folder != NULL, -1);
1449 g_return_val_if_fail(dest != NULL, -1);
1450 g_return_val_if_fail(msglist != NULL, -1);
1452 debug_print("getting session...\n");
1453 session = imap_session_get(folder);
1458 msginfo = (MsgInfo *)msglist->data;
1460 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
1461 NULL, NULL, NULL, NULL, FALSE);
1462 if (ok != IMAP_SUCCESS) {
1467 destdir = imap_get_real_path(session, IMAP_FOLDER(folder), dest->path);
1468 for (cur = msglist; cur; cur = cur->next) {
1469 msginfo = (MsgInfo *)cur->data;
1470 if (!MSG_IS_DELETED(msginfo->flags))
1471 numlist = g_slist_prepend(numlist, GINT_TO_POINTER(msginfo->msgnum));
1473 numlist = g_slist_reverse(numlist);
1475 uid_mapping = g_relation_new(2);
1476 g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
1478 ok = imap_set_message_flags
1479 (session, numlist, IMAP_FLAG_DELETED, TRUE);
1480 if (ok != IMAP_SUCCESS) {
1481 log_warning(LOG_PROTOCOL, _("can't set deleted flags\n"));
1485 ok = imap_cmd_expunge(session);
1486 if (ok != IMAP_SUCCESS) {
1487 log_warning(LOG_PROTOCOL, _("can't expunge\n"));
1492 dir = folder_item_get_path(msginfo->folder);
1493 if (is_dir_exist(dir)) {
1494 for (cur = msglist; cur; cur = cur->next) {
1495 msginfo = (MsgInfo *)cur->data;
1496 remove_numbered_files(dir, msginfo->msgnum, msginfo->msgnum);
1501 g_relation_destroy(uid_mapping);
1502 g_slist_free(numlist);
1506 if (ok == IMAP_SUCCESS)
1512 static gint imap_remove_msgs(Folder *folder, FolderItem *dest,
1513 MsgInfoList *msglist, GRelation *relation)
1517 g_return_val_if_fail(folder != NULL, -1);
1518 g_return_val_if_fail(dest != NULL, -1);
1519 if (msglist == NULL)
1522 msginfo = (MsgInfo *)msglist->data;
1523 g_return_val_if_fail(msginfo->folder != NULL, -1);
1525 return imap_do_remove_msgs(folder, dest, msglist, relation);
1528 static gint imap_remove_all_msg(Folder *folder, FolderItem *item)
1530 GSList *list = folder_item_get_msg_list(item);
1531 gint res = imap_remove_msgs(folder, item, list, NULL);
1532 procmsg_msg_list_free(list);
1536 static gboolean imap_is_msg_changed(Folder *folder, FolderItem *item,
1539 /* TODO: properly implement this method */
1543 static gint imap_close(Folder *folder, FolderItem *item)
1548 gint imap_scan_tree_real(Folder *folder, gboolean subs_only)
1550 FolderItem *item = NULL;
1551 IMAPSession *session;
1552 gchar *root_folder = NULL;
1554 g_return_val_if_fail(folder != NULL, -1);
1555 g_return_val_if_fail(folder->account != NULL, -1);
1557 debug_print("getting session...\n");
1558 session = imap_session_get(folder);
1560 if (!folder->node) {
1561 folder_tree_destroy(folder);
1562 item = folder_item_new(folder, folder->name, NULL);
1563 item->folder = folder;
1564 folder->node = item->node = g_node_new(item);
1569 if (folder->account->imap_dir && *folder->account->imap_dir) {
1574 Xstrdup_a(root_folder, folder->account->imap_dir, {unlock_session();return -1;});
1575 extract_quote(root_folder, '"');
1576 subst_char(root_folder,
1577 imap_get_path_separator(session, IMAP_FOLDER(folder),
1580 strtailchomp(root_folder, '/');
1581 real_path = imap_get_real_path
1582 (session, IMAP_FOLDER(folder), root_folder);
1583 debug_print("IMAP root directory: %s\n", real_path);
1585 /* check if root directory exist */
1587 r = imap_threaded_list(session->folder, "", real_path,
1589 if ((r != MAILIMAP_NO_ERROR) || (clist_count(lep_list) == 0)) {
1590 if (!folder->node) {
1591 item = folder_item_new(folder, folder->name, NULL);
1592 item->folder = folder;
1593 folder->node = item->node = g_node_new(item);
1598 mailimap_list_result_free(lep_list);
1604 item = FOLDER_ITEM(folder->node->data);
1606 if (item && !item->path && root_folder) {
1607 item->path = g_strdup(root_folder);
1610 if (!item || ((item->path || root_folder) &&
1611 strcmp2(item->path, root_folder) != 0)) {
1612 folder_tree_destroy(folder);
1613 item = folder_item_new(folder, folder->name, root_folder);
1614 item->folder = folder;
1615 folder->node = item->node = g_node_new(item);
1618 imap_scan_tree_recursive(session, FOLDER_ITEM(folder->node->data), subs_only);
1619 imap_create_missing_folders(folder);
1625 static gint imap_scan_tree(Folder *folder)
1627 gboolean subs_only = FALSE;
1628 if (folder->account) {
1629 debug_print(" scanning only subs %d\n", folder->account->imap_subsonly);
1630 subs_only = folder->account->imap_subsonly;
1632 return imap_scan_tree_real(folder, subs_only);
1635 static gint imap_scan_tree_recursive(IMAPSession *session, FolderItem *item, gboolean subs_only)
1638 IMAPFolder *imapfolder;
1639 FolderItem *new_item;
1640 GSList *item_list, *cur;
1643 gchar *wildcard_path;
1649 g_return_val_if_fail(item != NULL, -1);
1650 g_return_val_if_fail(item->folder != NULL, -1);
1651 g_return_val_if_fail(item->no_sub == FALSE, -1);
1653 folder = item->folder;
1654 imapfolder = IMAP_FOLDER(folder);
1656 separator = imap_get_path_separator(session, imapfolder, item->path);
1658 if (folder->ui_func)
1659 folder->ui_func(folder, item, folder->ui_func_data);
1662 wildcard[0] = separator;
1665 real_path = imap_get_real_path(session, imapfolder, item->path);
1669 real_path = g_strdup("");
1672 Xstrcat_a(wildcard_path, real_path, wildcard,
1673 {g_free(real_path); return IMAP_ERROR;});
1677 r = imap_threaded_lsub(folder, "", wildcard_path, &lep_list);
1679 r = imap_threaded_list(folder, "", wildcard_path, &lep_list);
1681 if (r != MAILIMAP_NO_ERROR) {
1685 item_list = imap_list_from_lep(imapfolder,
1686 lep_list, real_path, FALSE);
1687 mailimap_list_result_free(lep_list);
1692 node = item->node->children;
1693 while (node != NULL) {
1694 FolderItem *old_item = FOLDER_ITEM(node->data);
1695 GNode *next = node->next;
1698 for (cur = item_list; cur != NULL; cur = cur->next) {
1699 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1700 if (!strcmp2(old_item->path, cur_item->path)) {
1701 new_item = cur_item;
1706 if (old_item && old_item->path && !strcmp(old_item->path, "INBOX")) {
1707 debug_print("not removing INBOX\n");
1709 debug_print("folder '%s' not found. removing...\n",
1711 folder_item_remove(old_item);
1714 old_item->no_sub = new_item->no_sub;
1715 old_item->no_select = new_item->no_select;
1716 if (old_item->no_sub == TRUE && node->children) {
1717 debug_print("folder '%s' doesn't have "
1718 "subfolders. removing...\n",
1720 folder_item_remove_children(old_item);
1727 for (cur = item_list; cur != NULL; cur = cur->next) {
1728 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1731 for (node = item->node->children; node != NULL;
1732 node = node->next) {
1733 if (!strcmp2(FOLDER_ITEM(node->data)->path,
1735 new_item = FOLDER_ITEM(node->data);
1736 folder_item_destroy(cur_item);
1742 new_item = cur_item;
1743 debug_print("new folder '%s' found.\n", new_item->path);
1744 folder_item_append(item, new_item);
1747 if (!strcmp(new_item->path, "INBOX")) {
1748 new_item->stype = F_INBOX;
1749 folder->inbox = new_item;
1750 } else if (!folder_item_parent(item) || item->stype == F_INBOX) {
1753 base = g_path_get_basename(new_item->path);
1755 if (!folder->outbox && !g_ascii_strcasecmp(base, "Sent")) {
1756 new_item->stype = F_OUTBOX;
1757 folder->outbox = new_item;
1758 } else if (!folder->draft && !g_ascii_strcasecmp(base, "Drafts")) {
1759 new_item->stype = F_DRAFT;
1760 folder->draft = new_item;
1761 } else if (!folder->queue && !g_ascii_strcasecmp(base, "Queue")) {
1762 new_item->stype = F_QUEUE;
1763 folder->queue = new_item;
1764 } else if (!folder->trash && !g_ascii_strcasecmp(base, "Trash")) {
1765 new_item->stype = F_TRASH;
1766 folder->trash = new_item;
1771 if (new_item->no_sub == FALSE)
1772 imap_scan_tree_recursive(session, new_item, subs_only);
1775 g_slist_free(item_list);
1777 return IMAP_SUCCESS;
1780 GList *imap_scan_subtree(Folder *folder, FolderItem *item, gboolean unsubs_only, gboolean recursive)
1782 IMAPSession *session = imap_session_get(folder);
1784 gchar *wildcard_path;
1788 GSList *item_list = NULL, *cur;
1789 GList *child_list = NULL, *tmplist = NULL;
1790 GSList *sub_list = NULL;
1796 separator = imap_get_path_separator(session, IMAP_FOLDER(folder), item->path);
1799 wildcard[0] = separator;
1802 real_path = imap_get_real_path(session, IMAP_FOLDER(folder), item->path);
1806 real_path = g_strdup("");
1809 Xstrcat_a(wildcard_path, real_path, wildcard,
1810 {g_free(real_path); return NULL;});
1814 statusbar_print_all(_("Looking for unsubscribed folders in %s..."),
1815 item->path?item->path:item->name);
1817 statusbar_print_all(_("Looking for subfolders of %s..."),
1818 item->path?item->path:item->name);
1820 r = imap_threaded_list(folder, "", wildcard_path, &lep_list);
1822 statusbar_pop_all();
1825 item_list = imap_list_from_lep(IMAP_FOLDER(folder),
1826 lep_list, real_path, FALSE);
1827 mailimap_list_result_free(lep_list);
1829 for (cur = item_list; cur != NULL; cur = cur->next) {
1830 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1832 tmplist = imap_scan_subtree(folder, cur_item,
1833 unsubs_only, recursive);
1835 child_list = g_list_concat(child_list, tmplist);
1837 child_list = g_list_prepend(child_list,
1838 imap_get_real_path(session,
1839 IMAP_FOLDER(folder), cur_item->path));
1841 folder_item_destroy(cur_item);
1843 child_list = g_list_reverse(child_list);
1844 g_slist_free(item_list);
1847 r = imap_threaded_lsub(folder, "", wildcard_path, &lep_list);
1849 statusbar_pop_all();
1852 sub_list = imap_list_from_lep(IMAP_FOLDER(folder),
1853 lep_list, real_path, FALSE);
1854 mailimap_list_result_free(lep_list);
1856 for (cur = sub_list; cur != NULL; cur = cur->next) {
1857 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1858 GList *oldlitem = NULL;
1859 gchar *tmp = imap_get_real_path(session,
1860 IMAP_FOLDER(folder), cur_item->path);
1861 folder_item_destroy(cur_item);
1862 oldlitem = g_list_find_custom(
1863 child_list, tmp, (GCompareFunc)strcmp2);
1865 child_list = g_list_remove_link(child_list, oldlitem);
1866 g_free(oldlitem->data);
1867 g_list_free(oldlitem);
1873 statusbar_pop_all();
1878 static gint imap_create_tree(Folder *folder)
1880 g_return_val_if_fail(folder != NULL, -1);
1881 g_return_val_if_fail(folder->node != NULL, -1);
1882 g_return_val_if_fail(folder->node->data != NULL, -1);
1883 g_return_val_if_fail(folder->account != NULL, -1);
1885 imap_scan_tree(folder);
1886 imap_create_missing_folders(folder);
1891 static void imap_create_missing_folders(Folder *folder)
1893 g_return_if_fail(folder != NULL);
1896 folder->inbox = imap_create_special_folder
1897 (folder, F_INBOX, "INBOX");
1899 folder->trash = imap_create_special_folder
1900 (folder, F_TRASH, "Trash");
1902 folder->queue = imap_create_special_folder
1903 (folder, F_QUEUE, "Queue");
1904 if (!folder->outbox)
1905 folder->outbox = imap_create_special_folder
1906 (folder, F_OUTBOX, "Sent");
1908 folder->draft = imap_create_special_folder
1909 (folder, F_DRAFT, "Drafts");
1912 static FolderItem *imap_create_special_folder(Folder *folder,
1913 SpecialFolderItemType stype,
1917 FolderItem *new_item;
1919 g_return_val_if_fail(folder != NULL, NULL);
1920 g_return_val_if_fail(folder->node != NULL, NULL);
1921 g_return_val_if_fail(folder->node->data != NULL, NULL);
1922 g_return_val_if_fail(folder->account != NULL, NULL);
1923 g_return_val_if_fail(name != NULL, NULL);
1925 item = FOLDER_ITEM(folder->node->data);
1926 new_item = imap_create_folder(folder, item, name);
1929 g_warning("Can't create '%s'\n", name);
1930 if (!folder->inbox) return NULL;
1932 new_item = imap_create_folder(folder, folder->inbox, name);
1934 g_warning("Can't create '%s' under INBOX\n", name);
1936 new_item->stype = stype;
1938 new_item->stype = stype;
1943 static gchar *imap_folder_get_path(Folder *folder)
1947 g_return_val_if_fail(folder != NULL, NULL);
1948 g_return_val_if_fail(folder->account != NULL, NULL);
1950 folder_path = g_strconcat(get_imap_cache_dir(),
1952 folder->account->recv_server,
1954 folder->account->userid,
1960 static gchar *imap_item_get_path(Folder *folder, FolderItem *item)
1962 gchar *folder_path, *path;
1964 g_return_val_if_fail(folder != NULL, NULL);
1965 g_return_val_if_fail(item != NULL, NULL);
1966 folder_path = imap_folder_get_path(folder);
1968 g_return_val_if_fail(folder_path != NULL, NULL);
1969 if (folder_path[0] == G_DIR_SEPARATOR) {
1971 path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
1974 path = g_strdup(folder_path);
1977 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1978 folder_path, G_DIR_SEPARATOR_S,
1981 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1984 g_free(folder_path);
1989 static FolderItem *imap_create_folder(Folder *folder, FolderItem *parent,
1992 gchar *dirpath, *imap_path;
1993 IMAPSession *session;
1994 FolderItem *new_item;
1999 gboolean no_select = FALSE, no_sub = FALSE;
2000 gboolean exist = FALSE;
2002 g_return_val_if_fail(folder != NULL, NULL);
2003 g_return_val_if_fail(folder->account != NULL, NULL);
2004 g_return_val_if_fail(parent != NULL, NULL);
2005 g_return_val_if_fail(name != NULL, NULL);
2007 debug_print("getting session...\n");
2008 session = imap_session_get(folder);
2013 if (!folder_item_parent(parent) && strcmp(name, "INBOX") == 0) {
2014 dirpath = g_strdup(name);
2015 }else if (parent->path)
2016 dirpath = g_strconcat(parent->path, "/", name, NULL);
2017 else if ((p = strchr(name, '/')) != NULL && *(p + 1) != '\0')
2018 dirpath = g_strdup(name);
2019 else if (folder->account->imap_dir && *folder->account->imap_dir) {
2022 Xstrdup_a(imap_dir, folder->account->imap_dir, {unlock_session();return NULL;});
2023 strtailchomp(imap_dir, '/');
2024 dirpath = g_strconcat(imap_dir, "/", name, NULL);
2026 dirpath = g_strdup(name);
2030 /* keep trailing directory separator to create a folder that contains
2032 imap_path = imap_utf8_to_modified_utf7(dirpath);
2034 strtailchomp(dirpath, '/');
2035 Xstrdup_a(new_name, name, {
2040 separator = imap_get_path_separator(session, IMAP_FOLDER(folder), imap_path);
2041 imap_path_separator_subst(imap_path, separator);
2042 /* remove trailing / for display */
2043 strtailchomp(new_name, '/');
2045 if (strcmp(dirpath, "INBOX") != 0) {
2050 argbuf = g_ptr_array_new();
2051 r = imap_threaded_list(folder, "", imap_path, &lep_list);
2052 if (r != MAILIMAP_NO_ERROR) {
2053 log_warning(LOG_PROTOCOL, _("can't create mailbox: LIST failed\n"));
2056 ptr_array_free_strings(argbuf);
2057 g_ptr_array_free(argbuf, TRUE);
2062 if (clist_count(lep_list) > 0)
2064 mailimap_list_result_free(lep_list);
2067 ok = imap_cmd_create(session, imap_path);
2068 if (ok != IMAP_SUCCESS) {
2069 log_warning(LOG_PROTOCOL, _("can't create mailbox\n"));
2075 r = imap_threaded_list(folder, "", imap_path, &lep_list);
2076 if (r == MAILIMAP_NO_ERROR) {
2077 GSList *item_list = imap_list_from_lep(IMAP_FOLDER(folder),
2078 lep_list, dirpath, TRUE);
2080 FolderItem *cur_item = FOLDER_ITEM(item_list->data);
2081 no_select = cur_item->no_select;
2082 no_sub = cur_item->no_sub;
2083 g_slist_free(item_list);
2085 mailimap_list_result_free(lep_list);
2088 imap_threaded_subscribe(folder, imap_path, TRUE);
2092 /* just get flags */
2093 r = imap_threaded_list(folder, "", "INBOX", &lep_list);
2094 if (r == MAILIMAP_NO_ERROR) {
2095 GSList *item_list = imap_list_from_lep(IMAP_FOLDER(folder),
2096 lep_list, dirpath, TRUE);
2098 FolderItem *cur_item = FOLDER_ITEM(item_list->data);
2099 no_select = cur_item->no_select;
2100 no_sub = cur_item->no_sub;
2101 g_slist_free(item_list);
2103 mailimap_list_result_free(lep_list);
2107 new_item = folder_item_new(folder, new_name, dirpath);
2108 new_item->no_select = no_select;
2109 new_item->no_sub = no_sub;
2110 folder_item_append(parent, new_item);
2114 dirpath = folder_item_get_path(new_item);
2115 if (!is_dir_exist(dirpath))
2116 make_dir_hier(dirpath);
2121 /* folder existed, scan it */
2122 folder_item_scan_full(new_item, FALSE);
2128 static gint imap_rename_folder(Folder *folder, FolderItem *item,
2133 gchar *real_oldpath;
2134 gchar *real_newpath;
2136 gchar *old_cache_dir;
2137 gchar *new_cache_dir;
2138 IMAPSession *session;
2141 gint exists, recent, unseen;
2142 guint32 uid_validity;
2144 g_return_val_if_fail(folder != NULL, -1);
2145 g_return_val_if_fail(item != NULL, -1);
2146 g_return_val_if_fail(item->path != NULL, -1);
2147 g_return_val_if_fail(name != NULL, -1);
2149 debug_print("getting session...\n");
2150 session = imap_session_get(folder);
2155 if (strchr(name, imap_get_path_separator(session, IMAP_FOLDER(folder), item->path)) != NULL) {
2156 g_warning(_("New folder name must not contain the namespace "
2162 real_oldpath = imap_get_real_path(session, IMAP_FOLDER(folder), item->path);
2164 g_free(session->mbox);
2165 session->mbox = NULL;
2166 ok = imap_cmd_examine(session, "INBOX",
2167 &exists, &recent, &unseen, &uid_validity, FALSE);
2168 if (ok != IMAP_SUCCESS) {
2169 g_free(real_oldpath);
2174 separator = imap_get_path_separator(session, IMAP_FOLDER(folder), item->path);
2175 if (strchr(item->path, G_DIR_SEPARATOR)) {
2176 dirpath = g_path_get_dirname(item->path);
2177 newpath = g_strconcat(dirpath, G_DIR_SEPARATOR_S, name, NULL);
2180 newpath = g_strdup(name);
2182 real_newpath = imap_utf8_to_modified_utf7(newpath);
2183 imap_path_separator_subst(real_newpath, separator);
2185 ok = imap_cmd_rename(session, real_oldpath, real_newpath);
2186 if (ok != IMAP_SUCCESS) {
2187 log_warning(LOG_PROTOCOL, _("can't rename mailbox: %s to %s\n"),
2188 real_oldpath, real_newpath);
2189 g_free(real_oldpath);
2191 g_free(real_newpath);
2196 item->name = g_strdup(name);
2198 old_cache_dir = folder_item_get_path(item);
2200 paths[0] = g_strdup(item->path);
2202 g_node_traverse(item->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
2203 imap_rename_folder_func, paths);
2205 if (is_dir_exist(old_cache_dir)) {
2206 new_cache_dir = folder_item_get_path(item);
2207 if (rename(old_cache_dir, new_cache_dir) < 0) {
2208 FILE_OP_ERROR(old_cache_dir, "rename");
2210 g_free(new_cache_dir);
2213 g_free(old_cache_dir);
2216 g_free(real_oldpath);
2217 g_free(real_newpath);
2222 gint imap_subscribe(Folder *folder, FolderItem *item, gchar *rpath, gboolean sub)
2226 IMAPSession *session;
2227 debug_print("getting session...\n");
2229 session = imap_session_get(folder);
2233 if (item && item->path) {
2234 path = imap_get_real_path(session, IMAP_FOLDER(folder), item->path);
2237 if (!strcmp(path, "INBOX") && sub == FALSE)
2239 debug_print("%ssubscribing %s\n", sub?"":"un", path);
2240 r = imap_threaded_subscribe(folder, path, sub);
2243 r = imap_threaded_subscribe(folder, rpath, sub);
2249 static gint imap_remove_folder_real(Folder *folder, FolderItem *item)
2252 IMAPSession *session;
2256 g_return_val_if_fail(folder != NULL, -1);
2257 g_return_val_if_fail(item != NULL, -1);
2258 g_return_val_if_fail(item->path != NULL, -1);
2260 debug_print("getting session...\n");
2261 session = imap_session_get(folder);
2265 path = imap_get_real_path(session, IMAP_FOLDER(folder), item->path);
2267 imap_threaded_subscribe(folder, path, FALSE);
2268 ok = imap_cmd_delete(session, path);
2269 if (ok != IMAP_SUCCESS) {
2270 gchar *tmp = g_strdup_printf("%s%c", path,
2271 imap_get_path_separator(session, IMAP_FOLDER(folder), path));
2274 ok = imap_cmd_delete(session, path);
2277 if (ok != IMAP_SUCCESS) {
2278 log_warning(LOG_PROTOCOL, _("can't delete mailbox\n"));
2285 cache_dir = folder_item_get_path(item);
2286 if (is_dir_exist(cache_dir) && remove_dir_recursive(cache_dir) < 0)
2287 g_warning("can't remove directory '%s'\n", cache_dir);
2289 folder_item_remove(item);
2294 static gint imap_remove_folder(Folder *folder, FolderItem *item)
2298 g_return_val_if_fail(item != NULL, -1);
2299 g_return_val_if_fail(item->folder != NULL, -1);
2300 g_return_val_if_fail(item->node != NULL, -1);
2302 node = item->node->children;
2303 while (node != NULL) {
2305 if (imap_remove_folder(folder, FOLDER_ITEM(node->data)) < 0)
2309 debug_print("IMAP removing %s\n", item->path);
2311 if (imap_remove_all_msg(folder, item) < 0)
2313 return imap_remove_folder_real(folder, item);
2316 typedef struct _uncached_data {
2317 IMAPSession *session;
2319 MsgNumberList *numlist;
2325 static void *imap_get_uncached_messages_thread(void *data)
2327 uncached_data *stuff = (uncached_data *)data;
2328 IMAPSession *session = stuff->session;
2329 FolderItem *item = stuff->item;
2330 MsgNumberList *numlist = stuff->numlist;
2332 GSList *newlist = NULL;
2333 GSList *llast = NULL;
2334 GSList *seq_list, *cur;
2336 debug_print("uncached_messages\n");
2338 if (session == NULL || item == NULL || item->folder == NULL
2339 || FOLDER_CLASS(item->folder) != &imap_class) {
2344 seq_list = imap_get_lep_set_from_numlist(numlist);
2345 debug_print("get msgs info\n");
2346 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
2347 struct mailimap_set * imapset;
2353 imapset = cur->data;
2355 r = imap_threaded_fetch_env(session->folder,
2356 imapset, &env_list);
2357 if (r != MAILIMAP_NO_ERROR)
2360 session_set_access_time(SESSION(session));
2363 for(i = 0 ; i < carray_count(env_list) ; i ++) {
2364 struct imap_fetch_env_info * info;
2367 info = carray_get(env_list, i);
2368 msginfo = imap_envelope_from_lep(info, item);
2369 if (msginfo == NULL)
2371 msginfo->folder = item;
2373 llast = newlist = g_slist_append(newlist, msginfo);
2375 llast = g_slist_append(llast, msginfo);
2376 llast = llast->next;
2381 imap_fetch_env_free(env_list);
2384 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
2385 struct mailimap_set * imapset;
2387 imapset = cur->data;
2388 mailimap_set_free(imapset);
2391 session_set_access_time(SESSION(session));
2396 #define MAX_MSG_NUM 50
2398 static GSList *imap_get_uncached_messages(IMAPSession *session,
2400 MsgNumberList *numlist)
2402 GSList *result = NULL;
2404 uncached_data *data = g_new0(uncached_data, 1);
2409 data->total = g_slist_length(numlist);
2410 debug_print("messages list : %i\n", data->total);
2412 while (cur != NULL) {
2413 GSList * partial_result;
2421 while (count < MAX_MSG_NUM) {
2426 if (newlist == NULL)
2427 llast = newlist = g_slist_append(newlist, p);
2429 llast = g_slist_append(llast, p);
2430 llast = llast->next;
2440 data->session = session;
2442 data->numlist = newlist;
2445 if (prefs_common.work_offline &&
2446 !inc_offline_should_override(FALSE,
2447 _("Claws Mail needs network access in order "
2448 "to access the IMAP server."))) {
2454 (GSList *)imap_get_uncached_messages_thread(data);
2456 statusbar_progress_all(data->cur,data->total, 1);
2458 g_slist_free(newlist);
2460 result = g_slist_concat(result, partial_result);
2464 statusbar_progress_all(0,0,0);
2465 statusbar_pop_all();
2470 static void imap_delete_all_cached_messages(FolderItem *item)
2474 g_return_if_fail(item != NULL);
2475 g_return_if_fail(item->folder != NULL);
2476 g_return_if_fail(FOLDER_CLASS(item->folder) == &imap_class);
2478 debug_print("Deleting all cached messages...\n");
2480 dir = folder_item_get_path(item);
2481 if (is_dir_exist(dir))
2482 remove_all_numbered_files(dir);
2485 debug_print("done.\n");
2488 gchar imap_get_path_separator_for_item(FolderItem *item)
2490 Folder *folder = NULL;
2491 IMAPFolder *imap_folder = NULL;
2492 IMAPSession *session = NULL;
2497 folder = item->folder;
2502 imap_folder = IMAP_FOLDER(folder);
2507 debug_print("getting session...");
2508 session = imap_session_get(FOLDER(folder));
2509 result = imap_get_path_separator(session, imap_folder, item->path);
2514 static gchar imap_refresh_path_separator(IMAPSession *session, IMAPFolder *folder, const gchar *subfolder)
2518 gchar separator = '\0';
2520 g_return_val_if_fail(session != NULL, '/');
2521 r = imap_threaded_list((Folder *)folder, "", subfolder, &lep_list);
2523 if (r != MAILIMAP_NO_ERROR) {
2524 log_warning(LOG_PROTOCOL, _("LIST failed\n"));
2528 if (clist_count(lep_list) > 0) {
2529 clistiter * iter = clist_begin(lep_list);
2530 struct mailimap_mailbox_list * mb;
2531 mb = clist_content(iter);
2533 separator = mb->mb_delimiter;
2534 debug_print("got separator: %c\n", folder->last_seen_separator);
2536 mailimap_list_result_free(lep_list);
2540 static gchar imap_get_path_separator(IMAPSession *session, IMAPFolder *folder, const gchar *path)
2542 gchar separator = '/';
2544 if (folder->last_seen_separator == 0) {
2545 folder->last_seen_separator = imap_refresh_path_separator(session, folder, "");
2548 if (folder->last_seen_separator == 0) {
2549 folder->last_seen_separator = imap_refresh_path_separator(session, folder, "INBOX");
2552 if (folder->last_seen_separator != 0) {
2553 debug_print("using separator: %c\n", folder->last_seen_separator);
2554 return folder->last_seen_separator;
2560 static gchar *imap_get_real_path(IMAPSession *session, IMAPFolder *folder, const gchar *path)
2565 g_return_val_if_fail(folder != NULL, NULL);
2566 g_return_val_if_fail(path != NULL, NULL);
2568 real_path = imap_utf8_to_modified_utf7(path);
2569 separator = imap_get_path_separator(session, folder, path);
2570 imap_path_separator_subst(real_path, separator);
2575 static gint imap_set_message_flags(IMAPSession *session,
2576 MsgNumberList *numlist,
2584 seq_list = imap_get_lep_set_from_numlist(numlist);
2586 for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) {
2587 struct mailimap_set * imapset;
2589 imapset = cur->data;
2591 ok = imap_cmd_store(session, imapset,
2595 imap_lep_set_free(seq_list);
2597 return IMAP_SUCCESS;
2600 typedef struct _select_data {
2601 IMAPSession *session;
2606 guint32 *uid_validity;
2610 static gint imap_select(IMAPSession *session, IMAPFolder *folder,
2612 gint *exists, gint *recent, gint *unseen,
2613 guint32 *uid_validity, gboolean block)
2617 gint exists_, recent_, unseen_;
2618 guint32 uid_validity_;
2620 if (!exists && !recent && !unseen && !uid_validity) {
2621 if (session->mbox && strcmp(session->mbox, path) == 0)
2622 return IMAP_SUCCESS;
2631 uid_validity = &uid_validity_;
2633 g_free(session->mbox);
2634 session->mbox = NULL;
2636 real_path = imap_get_real_path(session, folder, path);
2638 ok = imap_cmd_select(session, real_path,
2639 exists, recent, unseen, uid_validity, block);
2640 if (ok != IMAP_SUCCESS)
2641 log_warning(LOG_PROTOCOL, _("can't select folder: %s\n"), real_path);
2643 session->mbox = g_strdup(path);
2644 session->folder_content_changed = FALSE;
2651 static gint imap_status(IMAPSession *session, IMAPFolder *folder,
2652 const gchar *path, IMAPFolderItem *item,
2654 guint32 *uid_next, guint32 *uid_validity,
2655 gint *unseen, gboolean block)
2659 struct mailimap_mailbox_data_status * data_status;
2664 real_path = imap_get_real_path(session, folder, path);
2682 r = imap_threaded_status(FOLDER(folder), real_path,
2683 &data_status, mask);
2686 if (r != MAILIMAP_NO_ERROR) {
2687 debug_print("status err %d\n", r);
2691 if (data_status->st_info_list == NULL) {
2692 mailimap_mailbox_data_status_free(data_status);
2693 debug_print("status->st_info_list == NULL\n");
2698 for(iter = clist_begin(data_status->st_info_list) ; iter != NULL ;
2699 iter = clist_next(iter)) {
2700 struct mailimap_status_info * info;
2702 info = clist_content(iter);
2703 switch (info->st_att) {
2704 case MAILIMAP_STATUS_ATT_MESSAGES:
2706 * messages = info->st_value;
2707 got_values |= 1 << 0;
2711 case MAILIMAP_STATUS_ATT_UIDNEXT:
2713 * uid_next = info->st_value;
2714 got_values |= 1 << 2;
2718 case MAILIMAP_STATUS_ATT_UIDVALIDITY:
2720 * uid_validity = info->st_value;
2721 got_values |= 1 << 3;
2725 case MAILIMAP_STATUS_ATT_UNSEEN:
2727 * unseen = info->st_value;
2728 got_values |= 1 << 4;
2733 mailimap_mailbox_data_status_free(data_status);
2735 if (got_values != mask) {
2736 g_warning("status: incomplete values received (%d)\n", got_values);
2738 return IMAP_SUCCESS;
2741 static void imap_free_capabilities(IMAPSession *session)
2743 slist_free_strings(session->capability);
2744 g_slist_free(session->capability);
2745 session->capability = NULL;
2748 /* low-level IMAP4rev1 commands */
2750 static gint imap_cmd_login(IMAPSession *session,
2751 const gchar *user, const gchar *pass,
2757 if (!strcmp(type, "LOGIN") && imap_has_capability(session, "LOGINDISABLED")) {
2758 gint ok = IMAP_ERROR;
2759 if (imap_has_capability(session, "STARTTLS")) {
2761 log_warning(LOG_PROTOCOL, _("Server requires TLS to log in.\n"));
2762 ok = imap_cmd_starttls(session);
2763 if (ok != IMAP_SUCCESS) {
2764 log_warning(LOG_PROTOCOL, _("Can't start TLS session.\n"));
2768 imap_free_capabilities(session);
2769 if (imap_get_capabilities(session) != MAILIMAP_NO_ERROR) {
2770 log_warning(LOG_PROTOCOL, _("Can't refresh capabilities.\n"));
2775 log_error(LOG_PROTOCOL, _("Connection to %s failed: "
2776 "server requires TLS, but Claws Mail "
2777 "has been compiled without OpenSSL "
2779 SESSION(session)->server);
2783 log_error(LOG_PROTOCOL, _("Server logins are disabled.\n"));
2788 log_print(LOG_PROTOCOL, "IMAP4> Logging %s to %s using %s\n",
2790 SESSION(session)->server,
2792 r = imap_threaded_login(session->folder, user, pass, type);
2793 if (r != MAILIMAP_NO_ERROR) {
2794 log_print(LOG_PROTOCOL, "IMAP4< Error logging in to %s\n",
2795 SESSION(session)->server);
2798 log_print(LOG_PROTOCOL, "IMAP4< Login to %s successful\n",
2799 SESSION(session)->server);
2805 static gint imap_cmd_noop(IMAPSession *session)
2808 unsigned int exists;
2810 r = imap_threaded_noop(session->folder, &exists);
2811 if (r != MAILIMAP_NO_ERROR) {
2812 debug_print("noop err %d\n", r);
2815 session->exists = exists;
2816 session_set_access_time(SESSION(session));
2818 return IMAP_SUCCESS;
2822 static gint imap_cmd_starttls(IMAPSession *session)
2826 r = imap_threaded_starttls(session->folder,
2827 SESSION(session)->server, SESSION(session)->port);
2828 if (r != MAILIMAP_NO_ERROR) {
2829 debug_print("starttls err %d\n", r);
2832 return IMAP_SUCCESS;
2836 static gint imap_cmd_select(IMAPSession *session, const gchar *folder,
2837 gint *exists, gint *recent, gint *unseen,
2838 guint32 *uid_validity, gboolean block)
2842 r = imap_threaded_select(session->folder, folder,
2843 exists, recent, unseen, uid_validity);
2844 if (r != MAILIMAP_NO_ERROR) {
2845 debug_print("select err %d\n", r);
2848 return IMAP_SUCCESS;
2851 static gint imap_cmd_examine(IMAPSession *session, const gchar *folder,
2852 gint *exists, gint *recent, gint *unseen,
2853 guint32 *uid_validity, gboolean block)
2857 r = imap_threaded_examine(session->folder, folder,
2858 exists, recent, unseen, uid_validity);
2859 if (r != MAILIMAP_NO_ERROR) {
2860 debug_print("examine err %d\n", r);
2864 return IMAP_SUCCESS;
2867 static gint imap_cmd_create(IMAPSession *session, const gchar *folder)
2871 r = imap_threaded_create(session->folder, folder);
2872 if (r != MAILIMAP_NO_ERROR) {
2877 return IMAP_SUCCESS;
2880 static gint imap_cmd_rename(IMAPSession *session, const gchar *old_folder,
2881 const gchar *new_folder)
2885 r = imap_threaded_rename(session->folder, old_folder,
2887 if (r != MAILIMAP_NO_ERROR) {
2892 return IMAP_SUCCESS;
2895 static gint imap_cmd_delete(IMAPSession *session, const gchar *folder)
2900 r = imap_threaded_delete(session->folder, folder);
2901 if (r != MAILIMAP_NO_ERROR) {
2906 return IMAP_SUCCESS;
2909 typedef struct _fetch_data {
2910 IMAPSession *session;
2912 const gchar *filename;
2918 static void *imap_cmd_fetch_thread(void *data)
2920 fetch_data *stuff = (fetch_data *)data;
2921 IMAPSession *session = stuff->session;
2922 guint32 uid = stuff->uid;
2923 const gchar *filename = stuff->filename;
2927 r = imap_threaded_fetch_content(session->folder,
2931 r = imap_threaded_fetch_content(session->folder,
2934 if (r != MAILIMAP_NO_ERROR) {
2935 debug_print("fetch err %d\n", r);
2936 return GINT_TO_POINTER(IMAP_ERROR);
2938 return GINT_TO_POINTER(IMAP_SUCCESS);
2941 static gint imap_cmd_fetch(IMAPSession *session, guint32 uid,
2942 const gchar *filename, gboolean headers,
2945 fetch_data *data = g_new0(fetch_data, 1);
2948 data->session = session;
2950 data->filename = filename;
2951 data->headers = headers;
2954 if (prefs_common.work_offline &&
2955 !inc_offline_should_override(FALSE,
2956 _("Claws Mail needs network access in order "
2957 "to access the IMAP server."))) {
2961 statusbar_print_all(_("Fetching message..."));
2962 result = GPOINTER_TO_INT(imap_cmd_fetch_thread(data));
2963 statusbar_pop_all();
2969 static gint imap_cmd_append(IMAPSession *session, const gchar *destfolder,
2970 const gchar *file, IMAPFlags flags,
2973 struct mailimap_flag_list * flag_list;
2976 g_return_val_if_fail(file != NULL, IMAP_ERROR);
2978 flag_list = imap_flag_to_lep(flags);
2979 r = imap_threaded_append(session->folder, destfolder,
2980 file, flag_list, (int *)new_uid);
2981 mailimap_flag_list_free(flag_list);
2983 if (r != MAILIMAP_NO_ERROR) {
2984 debug_print("append err %d\n", r);
2987 return IMAP_SUCCESS;
2990 static gint imap_cmd_copy(IMAPSession *session, struct mailimap_set * set,
2991 const gchar *destfolder, GRelation *uid_mapping,
2992 struct mailimap_set **source, struct mailimap_set **dest)
2996 g_return_val_if_fail(session != NULL, IMAP_ERROR);
2997 g_return_val_if_fail(set != NULL, IMAP_ERROR);
2998 g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
3000 r = imap_threaded_copy(session->folder, set, destfolder, source, dest);
3001 if (r != MAILIMAP_NO_ERROR) {
3006 return IMAP_SUCCESS;
3009 static gint imap_cmd_store(IMAPSession *session, struct mailimap_set * set,
3010 IMAPFlags flags, int do_add)
3013 struct mailimap_flag_list * flag_list;
3014 struct mailimap_store_att_flags * store_att_flags;
3016 flag_list = imap_flag_to_lep(flags);
3020 mailimap_store_att_flags_new_add_flags_silent(flag_list);
3023 mailimap_store_att_flags_new_remove_flags_silent(flag_list);
3025 r = imap_threaded_store(session->folder, set, store_att_flags);
3026 mailimap_store_att_flags_free(store_att_flags);
3027 if (r != MAILIMAP_NO_ERROR) {
3032 return IMAP_SUCCESS;
3035 static gint imap_cmd_expunge(IMAPSession *session)
3039 if (prefs_common.work_offline &&
3040 !inc_offline_should_override(FALSE,
3041 _("Claws Mail needs network access in order "
3042 "to access the IMAP server."))) {
3046 r = imap_threaded_expunge(session->folder);
3047 if (r != MAILIMAP_NO_ERROR) {
3052 return IMAP_SUCCESS;
3055 static void imap_path_separator_subst(gchar *str, gchar separator)
3058 gboolean in_escape = FALSE;
3060 if (!separator || separator == '/') return;
3062 for (p = str; *p != '\0'; p++) {
3063 if (*p == '/' && !in_escape)
3065 else if (*p == '&' && *(p + 1) != '-' && !in_escape)
3067 else if (*p == '-' && in_escape)
3072 static gchar *imap_modified_utf7_to_utf8(const gchar *mutf7_str)
3074 static iconv_t cd = (iconv_t)-1;
3075 static gboolean iconv_ok = TRUE;
3078 size_t norm_utf7_len;
3080 gchar *to_str, *to_p;
3082 gboolean in_escape = FALSE;
3084 if (!iconv_ok) return g_strdup(mutf7_str);
3086 if (cd == (iconv_t)-1) {
3087 cd = iconv_open(CS_INTERNAL, CS_UTF_7);
3088 if (cd == (iconv_t)-1) {
3089 g_warning("iconv cannot convert UTF-7 to %s\n",
3092 return g_strdup(mutf7_str);
3096 /* modified UTF-7 to normal UTF-7 conversion */
3097 norm_utf7 = g_string_new(NULL);
3099 for (p = mutf7_str; *p != '\0'; p++) {
3100 /* replace: '&' -> '+',
3102 escaped ',' -> '/' */
3103 if (!in_escape && *p == '&') {
3104 if (*(p + 1) != '-') {
3105 g_string_append_c(norm_utf7, '+');
3108 g_string_append_c(norm_utf7, '&');
3111 } else if (in_escape && *p == ',') {
3112 g_string_append_c(norm_utf7, '/');
3113 } else if (in_escape && *p == '-') {
3114 g_string_append_c(norm_utf7, '-');
3117 g_string_append_c(norm_utf7, *p);
3121 norm_utf7_p = norm_utf7->str;
3122 norm_utf7_len = norm_utf7->len;
3123 to_len = strlen(mutf7_str) * 5;
3124 to_p = to_str = g_malloc(to_len + 1);
3126 if (iconv(cd, (ICONV_CONST gchar **)&norm_utf7_p, &norm_utf7_len,
3127 &to_p, &to_len) == -1) {
3128 g_warning(_("iconv cannot convert UTF-7 to %s\n"),
3129 conv_get_locale_charset_str());
3130 g_string_free(norm_utf7, TRUE);
3132 return g_strdup(mutf7_str);
3135 /* second iconv() call for flushing */
3136 iconv(cd, NULL, NULL, &to_p, &to_len);
3137 g_string_free(norm_utf7, TRUE);
3143 static gchar *imap_utf8_to_modified_utf7(const gchar *from)
3145 static iconv_t cd = (iconv_t)-1;
3146 static gboolean iconv_ok = TRUE;
3147 gchar *norm_utf7, *norm_utf7_p;
3148 size_t from_len, norm_utf7_len;
3150 gchar *from_tmp, *to, *p;
3151 gboolean in_escape = FALSE;
3153 if (!iconv_ok) return g_strdup(from);
3155 if (cd == (iconv_t)-1) {
3156 cd = iconv_open(CS_UTF_7, CS_INTERNAL);
3157 if (cd == (iconv_t)-1) {
3158 g_warning(_("iconv cannot convert %s to UTF-7\n"),
3161 return g_strdup(from);
3165 /* UTF-8 to normal UTF-7 conversion */
3166 Xstrdup_a(from_tmp, from, return g_strdup(from));
3167 from_len = strlen(from);
3168 norm_utf7_len = from_len * 5;
3169 Xalloca(norm_utf7, norm_utf7_len + 1, return g_strdup(from));
3170 norm_utf7_p = norm_utf7;
3172 #define IS_PRINT(ch) (isprint(ch) && IS_ASCII(ch))
3174 while (from_len > 0) {
3175 if (*from_tmp == '+') {
3176 *norm_utf7_p++ = '+';
3177 *norm_utf7_p++ = '-';
3181 } else if (IS_PRINT(*(guchar *)from_tmp)) {
3182 /* printable ascii char */
3183 *norm_utf7_p = *from_tmp;
3189 size_t conv_len = 0;
3191 /* unprintable char: convert to UTF-7 */
3193 while (!IS_PRINT(*(guchar *)p) && conv_len < from_len) {
3194 conv_len += g_utf8_skip[*(guchar *)p];
3195 p += g_utf8_skip[*(guchar *)p];
3198 from_len -= conv_len;
3199 if (iconv(cd, (ICONV_CONST gchar **)&from_tmp,
3201 &norm_utf7_p, &norm_utf7_len) == -1) {
3202 g_warning(_("iconv cannot convert UTF-8 to UTF-7\n"));
3203 return g_strdup(from);
3206 /* second iconv() call for flushing */
3207 iconv(cd, NULL, NULL, &norm_utf7_p, &norm_utf7_len);
3213 *norm_utf7_p = '\0';
3214 to_str = g_string_new(NULL);
3215 for (p = norm_utf7; p < norm_utf7_p; p++) {
3216 /* replace: '&' -> "&-",
3219 BASE64 '/' -> ',' */
3220 if (!in_escape && *p == '&') {
3221 g_string_append(to_str, "&-");
3222 } else if (!in_escape && *p == '+') {
3223 if (*(p + 1) == '-') {
3224 g_string_append_c(to_str, '+');
3227 g_string_append_c(to_str, '&');
3230 } else if (in_escape && *p == '/') {
3231 g_string_append_c(to_str, ',');
3232 } else if (in_escape && *p == '-') {
3233 g_string_append_c(to_str, '-');
3236 g_string_append_c(to_str, *p);
3242 g_string_append_c(to_str, '-');
3246 g_string_free(to_str, FALSE);
3251 static gboolean imap_rename_folder_func(GNode *node, gpointer data)
3253 FolderItem *item = node->data;
3254 gchar **paths = data;
3255 const gchar *oldpath = paths[0];
3256 const gchar *newpath = paths[1];
3257 gchar *real_oldpath, *real_newpath;
3259 gchar *new_itempath;
3261 IMAPSession *session = imap_session_get(item->folder);
3263 oldpathlen = strlen(oldpath);
3264 if (strncmp(oldpath, item->path, oldpathlen) != 0) {
3265 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
3269 base = item->path + oldpathlen;
3270 while (*base == G_DIR_SEPARATOR) base++;
3272 new_itempath = g_strdup(newpath);
3274 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
3277 real_oldpath = imap_get_real_path(session, IMAP_FOLDER(item->folder), item->path);
3279 item->path = new_itempath;
3281 real_newpath = imap_get_real_path(session, IMAP_FOLDER(item->folder), item->path);
3283 imap_threaded_subscribe(item->folder, real_oldpath, FALSE);
3284 imap_threaded_subscribe(item->folder, real_newpath, TRUE);
3286 g_free(real_oldpath);
3287 g_free(real_newpath);
3291 typedef struct _get_list_uid_data {
3293 IMAPSession *session;
3294 IMAPFolderItem *item;
3295 GSList **msgnum_list;
3297 } get_list_uid_data;
3299 static void *get_list_of_uids_thread(void *data)
3301 get_list_uid_data *stuff = (get_list_uid_data *)data;
3302 Folder *folder = stuff->folder;
3303 IMAPFolderItem *item = stuff->item;
3304 GSList **msgnum_list = stuff->msgnum_list;
3305 gint ok, nummsgs = 0, lastuid_old;
3306 IMAPSession *session;
3307 GSList *uidlist, *elem;
3308 clist * lep_uidlist;
3311 session = stuff->session;
3312 if (session == NULL) {
3314 return GINT_TO_POINTER(-1);
3316 /* no session locking here, it's already locked by caller */
3317 ok = imap_select(session, IMAP_FOLDER(folder), item->item.path,
3318 NULL, NULL, NULL, NULL, TRUE);
3319 if (ok != IMAP_SUCCESS) {
3321 return GINT_TO_POINTER(-1);
3326 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SIMPLE, NULL,
3329 if (r == MAILIMAP_NO_ERROR) {
3330 GSList * fetchuid_list;
3333 imap_uid_list_from_lep(lep_uidlist);
3334 mailimap_search_result_free(lep_uidlist);
3336 uidlist = g_slist_concat(fetchuid_list, uidlist);
3339 GSList * fetchuid_list;
3340 carray * lep_uidtab;
3342 r = imap_threaded_fetch_uid(folder, item->lastuid + 1,
3344 if (r == MAILIMAP_NO_ERROR) {
3346 imap_uid_list_from_lep_tab(lep_uidtab);
3347 imap_fetch_uid_list_free(lep_uidtab);
3348 uidlist = g_slist_concat(fetchuid_list, uidlist);
3352 lastuid_old = item->lastuid;
3353 *msgnum_list = g_slist_copy(item->uid_list);
3354 nummsgs = g_slist_length(*msgnum_list);
3355 debug_print("Got %d uids from cache\n", g_slist_length(item->uid_list));
3357 for (elem = uidlist; elem != NULL; elem = g_slist_next(elem)) {
3360 msgnum = GPOINTER_TO_INT(elem->data);
3361 if (msgnum > lastuid_old) {
3362 *msgnum_list = g_slist_prepend(*msgnum_list, GINT_TO_POINTER(msgnum));
3363 item->uid_list = g_slist_prepend(item->uid_list, GINT_TO_POINTER(msgnum));
3366 if(msgnum > item->lastuid)
3367 item->lastuid = msgnum;
3370 g_slist_free(uidlist);
3372 return GINT_TO_POINTER(nummsgs);
3375 static gint get_list_of_uids(IMAPSession *session, Folder *folder, IMAPFolderItem *item, GSList **msgnum_list)
3378 get_list_uid_data *data = g_new0(get_list_uid_data, 1);
3380 data->folder = folder;
3382 data->msgnum_list = msgnum_list;
3383 data->session = session;
3384 if (prefs_common.work_offline &&
3385 !inc_offline_should_override(FALSE,
3386 _("Claws Mail needs network access in order "
3387 "to access the IMAP server."))) {
3392 result = GPOINTER_TO_INT(get_list_of_uids_thread(data));
3398 gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list, gboolean *old_uids_valid)
3400 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3401 IMAPSession *session;
3402 gint ok, nummsgs = 0, exists;
3403 guint32 uid_next = 0, uid_val = 0;
3404 GSList *uidlist = NULL;
3406 gboolean selected_folder;
3407 debug_print("get_num_list\n");
3409 g_return_val_if_fail(folder != NULL, -1);
3410 g_return_val_if_fail(item != NULL, -1);
3411 g_return_val_if_fail(item->item.path != NULL, -1);
3412 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3413 g_return_val_if_fail(folder->account != NULL, -1);
3415 debug_print("getting session...\n");
3416 session = imap_session_get(folder);
3417 g_return_val_if_fail(session != NULL, -1);
3419 if (FOLDER_ITEM(item)->path)
3420 statusbar_print_all(_("Scanning folder %s%c%s ..."),
3421 FOLDER_ITEM(item)->folder->name,
3423 FOLDER_ITEM(item)->path);
3425 statusbar_print_all(_("Scanning folder %s ..."),
3426 FOLDER_ITEM(item)->folder->name);
3428 selected_folder = (session->mbox != NULL) &&
3429 (!strcmp(session->mbox, item->item.path));
3430 if (selected_folder && time(NULL) - item->use_cache < 2) {
3431 ok = imap_cmd_noop(session);
3432 if (ok != IMAP_SUCCESS) {
3433 debug_print("disconnected!\n");
3434 session = imap_reconnect_if_possible(folder, session);
3435 if (session == NULL) {
3436 statusbar_pop_all();
3441 exists = session->exists;
3443 uid_next = item->c_uid_next;
3444 uid_val = item->c_uid_validity;
3445 *old_uids_valid = TRUE;
3447 if (item->use_cache && time(NULL) - item->use_cache < 2) {
3448 exists = item->c_messages;
3449 uid_next = item->c_uid_next;
3450 uid_val = item->c_uid_validity;
3452 debug_print("using cache %d %d %d\n", exists, uid_next, uid_val);
3454 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path, item,
3455 &exists, &uid_next, &uid_val, NULL, FALSE);
3457 item->item.last_num = uid_next - 1;
3459 item->use_cache = (time_t)0;
3460 if (ok != IMAP_SUCCESS) {
3461 statusbar_pop_all();
3465 if(item->item.mtime == uid_val)
3466 *old_uids_valid = TRUE;
3468 *old_uids_valid = FALSE;
3470 debug_print("Freeing imap uid cache (%d != %d)\n",
3471 (int)item->item.mtime, uid_val);
3473 g_slist_free(item->uid_list);
3474 item->uid_list = NULL;
3476 item->item.mtime = uid_val;
3478 imap_delete_all_cached_messages((FolderItem *)item);
3482 /* If old uid_next matches new uid_next we can be sure no message
3483 was added to the folder */
3484 debug_print("uid_next is %d and item->uid_next %d \n",
3485 uid_next, item->uid_next);
3486 if (uid_next == item->uid_next) {
3487 nummsgs = g_slist_length(item->uid_list);
3489 /* If number of messages is still the same we
3490 know our caches message numbers are still valid,
3491 otherwise if the number of messages has decrease
3492 we discard our cache to start a new scan to find
3493 out which numbers have been removed */
3494 if (exists == nummsgs) {
3495 debug_print("exists == nummsgs\n");
3496 *msgnum_list = g_slist_copy(item->uid_list);
3497 statusbar_pop_all();
3500 } else if (exists < nummsgs) {
3501 debug_print("Freeing imap uid cache");
3503 g_slist_free(item->uid_list);
3504 item->uid_list = NULL;
3509 *msgnum_list = NULL;
3510 statusbar_pop_all();
3515 nummsgs = get_list_of_uids(session, folder, item, &uidlist);
3518 statusbar_pop_all();
3523 if (nummsgs != exists) {
3524 /* Cache contains more messages then folder, we have cached
3525 an old UID of a message that was removed and new messages
3526 have been added too, otherwise the uid_next check would
3528 debug_print("Freeing imap uid cache");
3530 g_slist_free(item->uid_list);
3531 item->uid_list = NULL;
3533 g_slist_free(*msgnum_list);
3535 nummsgs = get_list_of_uids(session, folder, item, &uidlist);
3538 *msgnum_list = uidlist;
3540 dir = folder_item_get_path((FolderItem *)item);
3541 debug_print("removing old messages from %s\n", dir);
3542 remove_numbered_files_not_in_list(dir, *msgnum_list);
3545 item->uid_next = uid_next;
3547 debug_print("get_num_list - ok - %i\n", nummsgs);
3548 statusbar_pop_all();
3553 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item)
3558 flags.perm_flags = MSG_NEW|MSG_UNREAD;
3559 flags.tmp_flags = 0;
3561 g_return_val_if_fail(item != NULL, NULL);
3562 g_return_val_if_fail(file != NULL, NULL);
3564 if (folder_has_parent_of_type(item, F_QUEUE)) {
3565 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
3566 } else if (folder_has_parent_of_type(item, F_DRAFT)) {
3567 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
3570 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3571 if (!msginfo) return NULL;
3573 msginfo->plaintext_file = g_strdup(file);
3574 msginfo->folder = item;
3579 GSList *imap_get_msginfos(Folder *folder, FolderItem *item,
3580 GSList *msgnum_list)
3582 IMAPSession *session;
3583 MsgInfoList *ret = NULL;
3586 debug_print("get_msginfos\n");
3588 g_return_val_if_fail(folder != NULL, NULL);
3589 g_return_val_if_fail(item != NULL, NULL);
3590 g_return_val_if_fail(msgnum_list != NULL, NULL);
3592 debug_print("getting session...\n");
3593 session = imap_session_get(folder);
3594 g_return_val_if_fail(session != NULL, NULL);
3596 debug_print("IMAP getting msginfos\n");
3597 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3598 NULL, NULL, NULL, NULL, FALSE);
3599 if (ok != IMAP_SUCCESS) {
3603 if (!(folder_has_parent_of_type(item, F_DRAFT) ||
3604 folder_has_parent_of_type(item, F_QUEUE))) {
3605 ret = g_slist_concat(ret,
3606 imap_get_uncached_messages(session, item,
3609 MsgNumberList *sorted_list, *elem, *llast = NULL;
3610 gint startnum, lastnum;
3612 sorted_list = g_slist_sort(g_slist_copy(msgnum_list), g_int_compare);
3614 startnum = lastnum = GPOINTER_TO_INT(sorted_list->data);
3616 llast = g_slist_last(ret);
3617 for (elem = sorted_list;; elem = g_slist_next(elem)) {
3621 num = GPOINTER_TO_INT(elem->data);
3623 if (num > lastnum + 1 || elem == NULL) {
3625 for (i = startnum; i <= lastnum; ++i) {
3628 file = imap_fetch_msg(folder, item, i);
3630 MsgInfo *msginfo = imap_parse_msg(file, item);
3631 if (msginfo != NULL) {
3632 msginfo->msgnum = i;
3634 llast = ret = g_slist_append(ret, msginfo);
3636 llast = g_slist_append(llast, msginfo);
3637 llast = llast->next;
3642 session_set_access_time(SESSION(session));
3653 g_slist_free(sorted_list);
3659 MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid)
3661 MsgInfo *msginfo = NULL;
3662 MsgInfoList *msginfolist;
3663 MsgNumberList numlist;
3665 numlist.next = NULL;
3666 numlist.data = GINT_TO_POINTER(uid);
3668 msginfolist = imap_get_msginfos(folder, item, &numlist);
3669 if (msginfolist != NULL) {
3670 msginfo = msginfolist->data;
3671 g_slist_free(msginfolist);
3677 gboolean imap_scan_required(Folder *folder, FolderItem *_item)
3679 IMAPSession *session;
3680 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3681 gint ok, exists = 0, unseen = 0;
3682 guint32 uid_next = 0, uid_val = 0;
3683 gboolean selected_folder;
3685 g_return_val_if_fail(folder != NULL, FALSE);
3686 g_return_val_if_fail(item != NULL, FALSE);
3687 g_return_val_if_fail(item->item.folder != NULL, FALSE);
3688 g_return_val_if_fail(FOLDER_CLASS(item->item.folder) == &imap_class, FALSE);
3690 if (item->item.path == NULL)
3693 debug_print("getting session...\n");
3694 session = imap_session_get(folder);
3695 g_return_val_if_fail(session != NULL, FALSE);
3697 selected_folder = (session->mbox != NULL) &&
3698 (!strcmp(session->mbox, item->item.path));
3699 if (selected_folder && time(NULL) - item->use_cache < 2) {
3700 ok = imap_cmd_noop(session);
3701 if (ok != IMAP_SUCCESS) {
3702 debug_print("disconnected!\n");
3703 session = imap_reconnect_if_possible(folder, session);
3704 if (session == NULL)
3708 if (session->folder_content_changed
3709 || session->exists != item->item.total_msgs) {
3714 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path, IMAP_FOLDER_ITEM(item),
3715 &exists, &uid_next, &uid_val, &unseen, FALSE);
3716 if (ok != IMAP_SUCCESS) {
3721 item->use_cache = time(NULL);
3722 item->c_messages = exists;
3723 item->c_uid_next = uid_next;
3724 item->c_uid_validity = uid_val;
3725 item->c_unseen = unseen;
3726 item->item.last_num = uid_next - 1;
3727 debug_print("uidnext %d, item->uid_next %d, exists %d, item->item.total_msgs %d\n",
3728 uid_next, item->uid_next, exists, item->item.total_msgs);
3729 if ((uid_next != item->uid_next) || (exists != item->item.total_msgs)
3730 || unseen != item->item.unread_msgs || uid_val != item->item.mtime) {
3739 void imap_change_flags(Folder *folder, FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
3741 IMAPSession *session;
3742 IMAPFlags flags_set = 0, flags_unset = 0;
3743 gint ok = IMAP_SUCCESS;
3744 MsgNumberList numlist;
3745 hashtable_data *ht_data = NULL;
3747 g_return_if_fail(folder != NULL);
3748 g_return_if_fail(folder->klass == &imap_class);
3749 g_return_if_fail(item != NULL);
3750 g_return_if_fail(item->folder == folder);
3751 g_return_if_fail(msginfo != NULL);
3752 g_return_if_fail(msginfo->folder == item);
3754 if (!MSG_IS_MARKED(msginfo->flags) && (newflags & MSG_MARKED))
3755 flags_set |= IMAP_FLAG_FLAGGED;
3756 if ( MSG_IS_MARKED(msginfo->flags) && !(newflags & MSG_MARKED))
3757 flags_unset |= IMAP_FLAG_FLAGGED;
3759 if (!MSG_IS_UNREAD(msginfo->flags) && (newflags & MSG_UNREAD))
3760 flags_unset |= IMAP_FLAG_SEEN;
3761 if ( MSG_IS_UNREAD(msginfo->flags) && !(newflags & MSG_UNREAD))
3762 flags_set |= IMAP_FLAG_SEEN;
3764 if (!MSG_IS_REPLIED(msginfo->flags) && (newflags & MSG_REPLIED))
3765 flags_set |= IMAP_FLAG_ANSWERED;
3766 if ( MSG_IS_REPLIED(msginfo->flags) && !(newflags & MSG_REPLIED))
3767 flags_unset |= IMAP_FLAG_ANSWERED;
3769 if (!MSG_IS_DELETED(msginfo->flags) && (newflags & MSG_DELETED))
3770 flags_set |= IMAP_FLAG_DELETED;
3771 if ( MSG_IS_DELETED(msginfo->flags) && !(newflags & MSG_DELETED))
3772 flags_unset |= IMAP_FLAG_DELETED;
3774 if (!flags_set && !flags_unset) {
3775 /* the changed flags were not translatable to IMAP-speak.
3776 * like MSG_POSTFILTERED, so just apply. */
3777 msginfo->flags.perm_flags = newflags;
3781 debug_print("getting session...\n");
3782 session = imap_session_get(folder);
3787 if ((ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
3788 NULL, NULL, NULL, NULL, FALSE)) != IMAP_SUCCESS) {
3792 numlist.next = NULL;
3793 numlist.data = GINT_TO_POINTER(msginfo->msgnum);
3795 if (IMAP_FOLDER_ITEM(item)->batching) {
3796 /* instead of performing an UID STORE command for each message change,
3797 * as a lot of them can change "together", we just fill in hashtables
3798 * and defer the treatment so that we're able to send only one
3801 debug_print("IMAP batch mode on, deferring flags change\n");
3803 ht_data = g_hash_table_lookup(IMAP_FOLDER_ITEM(item)->flags_set_table,
3804 GINT_TO_POINTER(flags_set));
3805 if (ht_data == NULL) {
3806 ht_data = g_new0(hashtable_data, 1);
3807 ht_data->session = session;
3808 ht_data->item = IMAP_FOLDER_ITEM(item);
3809 g_hash_table_insert(IMAP_FOLDER_ITEM(item)->flags_set_table,
3810 GINT_TO_POINTER(flags_set), ht_data);
3812 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
3813 ht_data->msglist = g_slist_prepend(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum));
3816 ht_data = g_hash_table_lookup(IMAP_FOLDER_ITEM(item)->flags_unset_table,
3817 GINT_TO_POINTER(flags_unset));
3818 if (ht_data == NULL) {
3819 ht_data = g_new0(hashtable_data, 1);
3820 ht_data->session = session;
3821 ht_data->item = IMAP_FOLDER_ITEM(item);
3822 g_hash_table_insert(IMAP_FOLDER_ITEM(item)->flags_unset_table,
3823 GINT_TO_POINTER(flags_unset), ht_data);
3825 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
3826 ht_data->msglist = g_slist_prepend(ht_data->msglist,
3827 GINT_TO_POINTER(msginfo->msgnum));
3830 debug_print("IMAP changing flags\n");
3832 ok = imap_set_message_flags(session, &numlist, flags_set, TRUE);
3833 if (ok != IMAP_SUCCESS) {
3840 ok = imap_set_message_flags(session, &numlist, flags_unset, FALSE);
3841 if (ok != IMAP_SUCCESS) {
3847 msginfo->flags.perm_flags = newflags;
3852 static gint imap_remove_msg(Folder *folder, FolderItem *item, gint uid)
3855 IMAPSession *session;
3857 MsgNumberList numlist;
3859 g_return_val_if_fail(folder != NULL, -1);
3860 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3861 g_return_val_if_fail(item != NULL, -1);
3863 debug_print("getting session...\n");
3864 session = imap_session_get(folder);
3865 if (!session) return -1;
3867 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3868 NULL, NULL, NULL, NULL, FALSE);
3869 if (ok != IMAP_SUCCESS) {
3873 numlist.next = NULL;
3874 numlist.data = GINT_TO_POINTER(uid);
3876 ok = imap_set_message_flags
3877 (session, &numlist, IMAP_FLAG_DELETED, TRUE);
3878 if (ok != IMAP_SUCCESS) {
3879 log_warning(LOG_PROTOCOL, _("can't set deleted flags: %d\n"), uid);
3884 if (!session->uidplus) {
3885 ok = imap_cmd_expunge(session);
3889 uidstr = g_strdup_printf("%u", uid);
3890 ok = imap_cmd_expunge(session);
3893 if (ok != IMAP_SUCCESS) {
3894 log_warning(LOG_PROTOCOL, _("can't expunge\n"));
3899 IMAP_FOLDER_ITEM(item)->uid_list = g_slist_remove(
3900 IMAP_FOLDER_ITEM(item)->uid_list, numlist.data);
3901 dir = folder_item_get_path(item);
3902 if (is_dir_exist(dir))
3903 remove_numbered_files(dir, uid, uid);
3906 return IMAP_SUCCESS;
3909 static gint compare_msginfo(gconstpointer a, gconstpointer b)
3911 return ((MsgInfo *)a)->msgnum - ((MsgInfo *)b)->msgnum;
3914 static guint gslist_find_next_num(MsgNumberList **list, guint num)
3918 g_return_val_if_fail(list != NULL, -1);
3920 for (elem = *list; elem != NULL; elem = g_slist_next(elem))
3921 if (GPOINTER_TO_INT(elem->data) >= num)
3924 return elem != NULL ? GPOINTER_TO_INT(elem->data) : (gint)-1;
3928 * NEW and DELETED flags are not syncronized
3929 * - The NEW/RECENT flags in IMAP folders can not really be directly
3930 * modified by Sylpheed
3931 * - The DELETE/DELETED flag in IMAP and Sylpheed don't have the same
3932 * meaning, in IMAP it always removes the messages from the FolderItem
3933 * in Sylpheed it can mean to move the message to trash
3936 typedef struct _get_flags_data {
3939 MsgInfoList *msginfo_list;
3940 GRelation *msgflags;
3941 gboolean full_search;
3945 static /*gint*/ void *imap_get_flags_thread(void *data)
3947 get_flags_data *stuff = (get_flags_data *)data;
3948 Folder *folder = stuff->folder;
3949 FolderItem *item = stuff->item;
3950 MsgInfoList *msginfo_list = stuff->msginfo_list;
3951 GRelation *msgflags = stuff->msgflags;
3952 gboolean full_search = stuff->full_search;
3953 IMAPSession *session;
3954 GSList *sorted_list = NULL;
3955 GSList *unseen = NULL, *answered = NULL, *flagged = NULL, *deleted = NULL;
3956 GSList *p_unseen, *p_answered, *p_flagged, *p_deleted;
3958 GSList *seq_list, *cur;
3959 gboolean reverse_seen = FALSE;
3962 gint exists_cnt, unseen_cnt;
3963 gboolean selected_folder;
3965 if (folder == NULL || item == NULL) {
3967 return GINT_TO_POINTER(-1);
3970 debug_print("getting session...\n");
3971 session = imap_session_get(folder);
3972 if (session == NULL) {
3974 return GINT_TO_POINTER(-1);
3977 selected_folder = (session->mbox != NULL) &&
3978 (!strcmp(session->mbox, item->path));
3980 if (!selected_folder) {
3981 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3982 &exists_cnt, NULL, &unseen_cnt, NULL, TRUE);
3983 if (ok != IMAP_SUCCESS) {
3986 return GINT_TO_POINTER(-1);
3989 if (unseen_cnt > exists_cnt / 2)
3990 reverse_seen = TRUE;
3993 if (item->unread_msgs > item->total_msgs / 2)
3994 reverse_seen = TRUE;
3997 cmd_buf = g_string_new(NULL);
3999 sorted_list = g_slist_sort(g_slist_copy(msginfo_list), compare_msginfo);
4001 seq_list = imap_get_lep_set_from_msglist(msginfo_list);
4003 struct mailimap_set * set;
4004 set = mailimap_set_new_interval(1, 0);
4005 seq_list = g_slist_append(NULL, set);
4008 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
4009 struct mailimap_set * imapset;
4010 clist * lep_uidlist;
4013 imapset = cur->data;
4015 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SEEN,
4016 full_search ? NULL:imapset, &lep_uidlist);
4019 r = imap_threaded_search(folder,
4020 IMAP_SEARCH_TYPE_UNSEEN,
4021 full_search ? NULL:imapset, &lep_uidlist);
4023 if (r == MAILIMAP_NO_ERROR) {
4026 uidlist = imap_uid_list_from_lep(lep_uidlist);
4027 mailimap_search_result_free(lep_uidlist);
4029 unseen = g_slist_concat(unseen, uidlist);
4032 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_FLAGGED,
4033 full_search ? NULL:imapset, &lep_uidlist);
4034 if (r == MAILIMAP_NO_ERROR) {
4037 uidlist = imap_uid_list_from_lep(lep_uidlist);
4038 mailimap_search_result_free(lep_uidlist);
4040 flagged = g_slist_concat(flagged, uidlist);
4043 if (item->opened || item->processing_pending || item == folder->inbox) {
4044 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_ANSWERED,
4045 full_search ? NULL:imapset, &lep_uidlist);
4046 if (r == MAILIMAP_NO_ERROR) {
4049 uidlist = imap_uid_list_from_lep(lep_uidlist);
4050 mailimap_search_result_free(lep_uidlist);
4052 answered = g_slist_concat(answered, uidlist);
4055 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_DELETED,
4056 full_search ? NULL:imapset, &lep_uidlist);
4057 if (r == MAILIMAP_NO_ERROR) {
4060 uidlist = imap_uid_list_from_lep(lep_uidlist);
4061 mailimap_search_result_free(lep_uidlist);
4063 deleted = g_slist_concat(deleted, uidlist);
4069 p_answered = answered;
4070 p_flagged = flagged;
4071 p_deleted = deleted;
4073 for (elem = sorted_list; elem != NULL; elem = g_slist_next(elem)) {
4078 msginfo = (MsgInfo *) elem->data;
4079 flags = msginfo->flags.perm_flags;
4080 wasnew = (flags & MSG_NEW);
4081 if (item->opened || item->processing_pending || item == folder->inbox) {
4082 flags &= ~((reverse_seen ? 0 : MSG_UNREAD | MSG_NEW) | MSG_REPLIED | MSG_MARKED);
4084 flags &= ~((reverse_seen ? 0 : MSG_UNREAD | MSG_NEW | MSG_MARKED));
4087 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
4088 if (gslist_find_next_num(&p_unseen, msginfo->msgnum) == msginfo->msgnum) {
4089 if (!reverse_seen) {
4090 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
4092 flags &= ~(MSG_UNREAD | MSG_NEW);
4096 if (gslist_find_next_num(&p_flagged, msginfo->msgnum) == msginfo->msgnum)
4097 flags |= MSG_MARKED;
4099 flags &= ~MSG_MARKED;
4101 if (item->opened || item->processing_pending || item == folder->inbox) {
4102 if (gslist_find_next_num(&p_answered, msginfo->msgnum) == msginfo->msgnum)
4103 flags |= MSG_REPLIED;
4105 flags &= ~MSG_REPLIED;
4106 if (gslist_find_next_num(&p_deleted, msginfo->msgnum) == msginfo->msgnum)
4107 flags |= MSG_DELETED;
4109 flags &= ~MSG_DELETED;
4111 g_relation_insert(msgflags, msginfo, GINT_TO_POINTER(flags));
4114 imap_lep_set_free(seq_list);
4115 g_slist_free(flagged);
4116 g_slist_free(deleted);
4117 g_slist_free(answered);
4118 g_slist_free(unseen);
4119 g_slist_free(sorted_list);
4120 g_string_free(cmd_buf, TRUE);
4124 return GINT_TO_POINTER(0);
4127 static gint imap_get_flags(Folder *folder, FolderItem *item,
4128 MsgInfoList *msginfo_list, GRelation *msgflags)
4131 get_flags_data *data = g_new0(get_flags_data, 1);
4133 data->folder = folder;
4135 data->msginfo_list = msginfo_list;
4136 data->msgflags = msgflags;
4137 data->full_search = FALSE;
4139 GSList *tmp = NULL, *cur;
4141 if (prefs_common.work_offline &&
4142 !inc_offline_should_override(FALSE,
4143 _("Claws Mail needs network access in order "
4144 "to access the IMAP server."))) {
4149 tmp = folder_item_get_msg_list(item);
4151 if (g_slist_length(tmp) <= g_slist_length(msginfo_list))
4152 data->full_search = TRUE;
4154 for (cur = tmp; cur; cur = cur->next)
4155 procmsg_msginfo_free((MsgInfo *)cur->data);
4159 result = GPOINTER_TO_INT(imap_get_flags_thread(data));
4166 static gboolean process_flags(gpointer key, gpointer value, gpointer user_data)
4168 gboolean flags_set = GPOINTER_TO_INT(user_data);
4169 gint flags_value = GPOINTER_TO_INT(key);
4170 hashtable_data *data = (hashtable_data *)value;
4171 IMAPFolderItem *_item = data->item;
4172 FolderItem *item = (FolderItem *)_item;
4173 gint ok = IMAP_ERROR;
4174 IMAPSession *session = NULL;
4176 debug_print("getting session...\n");
4177 session = imap_session_get(item->folder);
4179 data->msglist = g_slist_reverse(data->msglist);
4181 debug_print("IMAP %ssetting flags to %d for %d messages\n",
4184 g_slist_length(data->msglist));
4187 ok = imap_select(session, IMAP_FOLDER(item->folder), item->path,
4188 NULL, NULL, NULL, NULL, FALSE);
4190 if (ok == IMAP_SUCCESS) {
4191 imap_set_message_flags(data->session, data->msglist, flags_value, flags_set);
4193 g_warning("can't select mailbox %s\n", item->path);
4197 g_slist_free(data->msglist);
4202 static void process_hashtable(IMAPFolderItem *item)
4204 if (item->flags_set_table) {
4205 g_hash_table_foreach_remove(item->flags_set_table, process_flags, GINT_TO_POINTER(TRUE));
4206 g_hash_table_destroy(item->flags_set_table);
4207 item->flags_set_table = NULL;
4209 if (item->flags_unset_table) {
4210 g_hash_table_foreach_remove(item->flags_unset_table, process_flags, GINT_TO_POINTER(FALSE));
4211 g_hash_table_destroy(item->flags_unset_table);
4212 item->flags_unset_table = NULL;
4216 static void imap_set_batch (Folder *folder, FolderItem *_item, gboolean batch)
4218 IMAPFolderItem *item = (IMAPFolderItem *)_item;
4220 g_return_if_fail(item != NULL);
4222 if (item->batching == batch)
4226 item->batching = TRUE;
4227 debug_print("IMAP switching to batch mode\n");
4228 if (!item->flags_set_table) {
4229 item->flags_set_table = g_hash_table_new(NULL, g_direct_equal);
4231 if (!item->flags_unset_table) {
4232 item->flags_unset_table = g_hash_table_new(NULL, g_direct_equal);
4235 debug_print("IMAP switching away from batch mode\n");
4237 process_hashtable(item);
4238 item->batching = FALSE;
4244 /* data types conversion libetpan <-> claws */
4248 #define ETPAN_IMAP_MB_MARKED 1
4249 #define ETPAN_IMAP_MB_UNMARKED 2
4250 #define ETPAN_IMAP_MB_NOSELECT 4
4251 #define ETPAN_IMAP_MB_NOINFERIORS 8
4253 static int imap_flags_to_flags(struct mailimap_mbx_list_flags * imap_flags)
4259 if (imap_flags->mbf_type == MAILIMAP_MBX_LIST_FLAGS_SFLAG) {
4260 switch (imap_flags->mbf_sflag) {
4261 case MAILIMAP_MBX_LIST_SFLAG_MARKED:
4262 flags |= ETPAN_IMAP_MB_MARKED;
4264 case MAILIMAP_MBX_LIST_SFLAG_NOSELECT:
4265 flags |= ETPAN_IMAP_MB_NOSELECT;
4267 case MAILIMAP_MBX_LIST_SFLAG_UNMARKED:
4268 flags |= ETPAN_IMAP_MB_UNMARKED;
4273 for(cur = clist_begin(imap_flags->mbf_oflags) ; cur != NULL ;
4274 cur = clist_next(cur)) {
4275 struct mailimap_mbx_list_oflag * oflag;
4277 oflag = clist_content(cur);
4279 switch (oflag->of_type) {
4280 case MAILIMAP_MBX_LIST_OFLAG_NOINFERIORS:
4281 flags |= ETPAN_IMAP_MB_NOINFERIORS;
4289 static GSList * imap_list_from_lep(IMAPFolder * folder,
4290 clist * list, const gchar * real_path, gboolean all)
4293 GSList * item_list = NULL, *llast = NULL;
4295 for(iter = clist_begin(list) ; iter != NULL ;
4296 iter = clist_next(iter)) {
4297 struct mailimap_mailbox_list * mb;
4305 FolderItem *new_item;
4307 mb = clist_content(iter);
4313 if (mb->mb_flag != NULL)
4314 flags = imap_flags_to_flags(mb->mb_flag);
4316 delimiter = mb->mb_delimiter;
4319 dup_name = strdup(name);
4320 if (delimiter != '\0')
4321 subst_char(dup_name, delimiter, '/');
4323 base = g_path_get_basename(dup_name);
4324 if (base[0] == '.') {
4329 if (!all && path_cmp(name, real_path) == 0) {
4335 if (!all && dup_name[strlen(dup_name)-1] == '/') {
4336 dup_name[strlen(dup_name)-1] = '\0';
4339 loc_name = imap_modified_utf7_to_utf8(base);
4340 loc_path = imap_modified_utf7_to_utf8(dup_name);
4342 new_item = folder_item_new(FOLDER(folder), loc_name, loc_path);
4343 if ((flags & ETPAN_IMAP_MB_NOINFERIORS) != 0)
4344 new_item->no_sub = TRUE;
4345 if (strcmp(dup_name, "INBOX") != 0 &&
4346 ((flags & ETPAN_IMAP_MB_NOSELECT) != 0))
4347 new_item->no_select = TRUE;
4349 if (item_list == NULL)
4350 llast = item_list = g_slist_append(item_list, new_item);
4352 llast = g_slist_append(llast, new_item);
4353 llast = llast->next;
4355 debug_print("folder '%s' found.\n", loc_path);
4366 static GSList * imap_get_lep_set_from_numlist(MsgNumberList *numlist)
4368 GSList *sorted_list, *cur;
4369 guint first, last, next;
4370 GSList *ret_list = NULL, *llast = NULL;
4372 struct mailimap_set * current_set;
4373 unsigned int item_count;
4375 if (numlist == NULL)
4379 current_set = mailimap_set_new_empty();
4381 sorted_list = g_slist_copy(numlist);
4382 sorted_list = g_slist_sort(sorted_list, g_int_compare);
4384 first = GPOINTER_TO_INT(sorted_list->data);
4387 for (cur = sorted_list; cur != NULL; cur = g_slist_next(cur)) {
4388 if (GPOINTER_TO_INT(cur->data) == 0)
4393 last = GPOINTER_TO_INT(cur->data);
4395 next = GPOINTER_TO_INT(cur->next->data);
4399 if (last + 1 != next || next == 0) {
4401 struct mailimap_set_item * item;
4402 item = mailimap_set_item_new(first, last);
4403 mailimap_set_add(current_set, item);
4408 if (count >= IMAP_SET_MAX_COUNT) {
4409 if (ret_list == NULL)
4410 llast = ret_list = g_slist_append(ret_list,
4413 llast = g_slist_append(llast, current_set);
4414 llast = llast->next;
4416 current_set = mailimap_set_new_empty();
4423 if (clist_count(current_set->set_list) > 0) {
4424 ret_list = g_slist_append(ret_list,
4428 g_slist_free(sorted_list);
4433 static GSList * imap_get_lep_set_from_msglist(MsgInfoList *msglist)
4435 MsgNumberList *numlist = NULL;
4439 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
4440 MsgInfo *msginfo = (MsgInfo *) cur->data;
4442 numlist = g_slist_prepend(numlist, GINT_TO_POINTER(msginfo->msgnum));
4444 numlist = g_slist_reverse(numlist);
4445 seq_list = imap_get_lep_set_from_numlist(numlist);
4446 g_slist_free(numlist);
4451 static GSList * imap_uid_list_from_lep(clist * list)
4458 for(iter = clist_begin(list) ; iter != NULL ;
4459 iter = clist_next(iter)) {
4462 puid = clist_content(iter);
4463 result = g_slist_prepend(result, GINT_TO_POINTER(* puid));
4466 result = g_slist_reverse(result);
4470 static GSList * imap_uid_list_from_lep_tab(carray * list)
4477 for(i = 0 ; i < carray_count(list) ; i ++) {
4480 puid = carray_get(list, i);
4481 result = g_slist_prepend(result, GINT_TO_POINTER(* puid));
4483 result = g_slist_reverse(result);
4487 static MsgInfo *imap_envelope_from_lep(struct imap_fetch_env_info * info,
4490 MsgInfo *msginfo = NULL;
4493 MsgFlags flags = {0, 0};
4495 if (info->headers == NULL)
4498 MSG_SET_TMP_FLAGS(flags, MSG_IMAP);
4499 if (folder_has_parent_of_type(item, F_QUEUE)) {
4500 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
4501 } else if (folder_has_parent_of_type(item, F_DRAFT)) {
4502 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
4504 flags.perm_flags = info->flags;
4508 msginfo = procheader_parse_str(info->headers, flags, FALSE, FALSE);
4511 msginfo->msgnum = uid;
4512 msginfo->size = size;
4518 static void imap_lep_set_free(GSList *seq_list)
4522 for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) {
4523 struct mailimap_set * imapset;
4525 imapset = cur->data;
4526 mailimap_set_free(imapset);
4528 g_slist_free(seq_list);
4531 static struct mailimap_flag_list * imap_flag_to_lep(IMAPFlags flags)
4533 struct mailimap_flag_list * flag_list;
4535 flag_list = mailimap_flag_list_new_empty();
4537 if (IMAP_IS_SEEN(flags))
4538 mailimap_flag_list_add(flag_list,
4539 mailimap_flag_new_seen());
4540 if (IMAP_IS_ANSWERED(flags))
4541 mailimap_flag_list_add(flag_list,
4542 mailimap_flag_new_answered());
4543 if (IMAP_IS_FLAGGED(flags))
4544 mailimap_flag_list_add(flag_list,
4545 mailimap_flag_new_flagged());
4546 if (IMAP_IS_DELETED(flags))
4547 mailimap_flag_list_add(flag_list,
4548 mailimap_flag_new_deleted());
4549 if (IMAP_IS_DRAFT(flags))
4550 mailimap_flag_list_add(flag_list,
4551 mailimap_flag_new_draft());
4556 guint imap_folder_get_refcnt(Folder *folder)
4558 return ((IMAPFolder *)folder)->refcnt;
4561 void imap_folder_ref(Folder *folder)
4563 ((IMAPFolder *)folder)->refcnt++;
4566 void imap_disconnect_all(void)
4569 for (list = account_get_list(); list != NULL; list = list->next) {
4570 PrefsAccount *account = list->data;
4571 if (account->protocol == A_IMAP4) {
4572 RemoteFolder *folder = (RemoteFolder *)account->folder;
4573 if (folder && folder->session) {
4574 IMAPSession *session = (IMAPSession *)folder->session;
4575 imap_threaded_disconnect(FOLDER(folder));
4576 SESSION(session)->state = SESSION_DISCONNECTED;
4577 session_destroy(SESSION(session));
4578 folder->session = NULL;
4584 void imap_folder_unref(Folder *folder)
4586 if (((IMAPFolder *)folder)->refcnt > 0)
4587 ((IMAPFolder *)folder)->refcnt--;
4590 #else /* HAVE_LIBETPAN */
4592 static FolderClass imap_class;
4594 static XMLTag *imap_item_get_xml(Folder *folder, FolderItem *item);
4595 static void imap_item_set_xml(Folder *folder, FolderItem *item, XMLTag *tag);
4597 static Folder *imap_folder_new (const gchar *name,
4600 static gboolean missing_imap_warning = TRUE;
4601 if (missing_imap_warning) {
4602 missing_imap_warning = FALSE;
4604 _("You have one or more IMAP accounts "
4605 "defined. However this version of "
4606 "Claws Mail has been built without "
4607 "IMAP support; your IMAP account(s) are "
4609 "You probably need to "
4610 "install libetpan and recompile "
4615 static gint imap_create_tree (Folder *folder)
4619 static FolderItem *imap_create_folder (Folder *folder,
4625 static gint imap_rename_folder (Folder *folder,
4632 gchar imap_get_path_separator_for_item(FolderItem *item)
4637 FolderClass *imap_get_class(void)
4639 if (imap_class.idstr == NULL) {
4640 imap_class.type = F_IMAP;
4641 imap_class.idstr = "imap";
4642 imap_class.uistr = "IMAP4";
4644 imap_class.new_folder = imap_folder_new;
4645 imap_class.create_tree = imap_create_tree;
4646 imap_class.create_folder = imap_create_folder;
4647 imap_class.rename_folder = imap_rename_folder;
4649 imap_class.set_xml = folder_set_xml;
4650 imap_class.get_xml = folder_get_xml;
4651 imap_class.item_set_xml = imap_item_set_xml;
4652 imap_class.item_get_xml = imap_item_get_xml;
4653 /* nothing implemented */
4659 void imap_disconnect_all(void)
4663 gint imap_scan_tree_real(Folder *folder, gboolean subs_only)
4668 gint imap_subscribe(Folder *folder, FolderItem *item, gchar *rpath, gboolean sub)
4673 GList * imap_scan_subtree(Folder *folder, FolderItem *item, gboolean unsubs_only, gboolean recursive)
4678 void imap_cache_msg(FolderItem *item, gint msgnum)
4684 void imap_synchronise(FolderItem *item)
4686 imap_gtk_synchronise(item);
4689 static void imap_item_set_xml(Folder *folder, FolderItem *item, XMLTag *tag)
4691 #ifdef HAVE_LIBETPAN
4694 folder_item_set_xml(folder, item, tag);
4696 #ifdef HAVE_LIBETPAN
4697 for (cur = tag->attr; cur != NULL; cur = g_list_next(cur)) {
4698 XMLAttr *attr = (XMLAttr *) cur->data;
4700 if (!attr || !attr->name || !attr->value) continue;
4701 if (!strcmp(attr->name, "uidnext"))
4702 IMAP_FOLDER_ITEM(item)->uid_next = atoi(attr->value);
4707 static XMLTag *imap_item_get_xml(Folder *folder, FolderItem *item)
4711 tag = folder_item_get_xml(folder, item);
4713 #ifdef HAVE_LIBETPAN
4714 xml_tag_add_attr(tag, xml_attr_new_int("uidnext",
4715 IMAP_FOLDER_ITEM(item)->uid_next));