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(_("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(_("Connecting to %s failed"),
662 folder->account->recv_server);
663 session_destroy(SESSION(session));
666 log_warning(_("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(
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(_("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(_("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(_("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("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(_("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_size_with_crs(MsgInfo *info)
971 fp = procmsg_open_message(info);
975 while (fgets(buf, sizeof (buf), fp) != NULL) {
977 if (!strstr(buf, "\r") && 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 *msginfo = imap_parse_msg(filename, item);
1009 MsgInfo *cached = msgcache_get_msg(item->cache,uid);
1010 guint have_size = get_size_with_crs(msginfo);
1013 debug_print("message %d has been already %scached (%d/%d).\n", uid,
1014 have_size >= cached->size ? "fully ":"",
1015 have_size, (int)cached->size);
1017 if (cached && (cached->size <= have_size || !body)) {
1018 procmsg_msginfo_free(cached);
1019 procmsg_msginfo_free(msginfo);
1020 file_strip_crs(filename);
1022 } else if (!cached && time(NULL) - get_file_mtime(filename) < 60) {
1023 debug_print("message not cached and file recent, considering file complete\n");
1024 procmsg_msginfo_free(msginfo);
1025 file_strip_crs(filename);
1028 procmsg_msginfo_free(cached);
1029 procmsg_msginfo_free(msginfo);
1033 debug_print("getting session...\n");
1034 session = imap_session_get(folder);
1041 debug_print("IMAP fetching messages\n");
1042 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
1043 NULL, NULL, NULL, NULL, FALSE);
1044 if (ok != IMAP_SUCCESS) {
1045 g_warning("can't select mailbox %s\n", item->path);
1051 debug_print("getting message %d...\n", uid);
1052 ok = imap_cmd_fetch(session, (guint32)uid, filename, headers, body);
1054 if (ok != IMAP_SUCCESS) {
1055 g_warning("can't fetch message %d\n", uid);
1062 file_strip_crs(filename);
1066 static gint imap_add_msg(Folder *folder, FolderItem *dest,
1067 const gchar *file, MsgFlags *flags)
1071 MsgFileInfo fileinfo;
1073 g_return_val_if_fail(file != NULL, -1);
1075 fileinfo.msginfo = NULL;
1076 fileinfo.file = (gchar *)file;
1077 fileinfo.flags = flags;
1078 file_list.data = &fileinfo;
1079 file_list.next = NULL;
1081 ret = imap_add_msgs(folder, dest, &file_list, NULL);
1085 static gint imap_add_msgs(Folder *folder, FolderItem *dest, GSList *file_list,
1086 GRelation *relation)
1089 IMAPSession *session;
1090 guint32 last_uid = 0;
1092 MsgFileInfo *fileinfo;
1094 gint curnum = 0, total = 0;
1097 g_return_val_if_fail(folder != NULL, -1);
1098 g_return_val_if_fail(dest != NULL, -1);
1099 g_return_val_if_fail(file_list != NULL, -1);
1101 debug_print("getting session...\n");
1102 session = imap_session_get(folder);
1106 destdir = imap_get_real_path(session, IMAP_FOLDER(folder), dest->path);
1108 statusbar_print_all(_("Adding messages..."));
1109 total = g_slist_length(file_list);
1110 for (cur = file_list; cur != NULL; cur = cur->next) {
1111 IMAPFlags iflags = 0;
1112 guint32 new_uid = 0;
1113 gchar *real_file = NULL;
1114 fileinfo = (MsgFileInfo *)cur->data;
1116 statusbar_progress_all(curnum, total, total < 10 ? 1:10);
1119 if (fileinfo->flags) {
1120 if (MSG_IS_MARKED(*fileinfo->flags))
1121 iflags |= IMAP_FLAG_FLAGGED;
1122 if (MSG_IS_REPLIED(*fileinfo->flags))
1123 iflags |= IMAP_FLAG_ANSWERED;
1124 if (!MSG_IS_UNREAD(*fileinfo->flags))
1125 iflags |= IMAP_FLAG_SEEN;
1128 if (real_file == NULL)
1129 real_file = g_strdup(fileinfo->file);
1131 if (folder_has_parent_of_type(dest, F_QUEUE) ||
1132 folder_has_parent_of_type(dest, F_OUTBOX) ||
1133 folder_has_parent_of_type(dest, F_DRAFT) ||
1134 folder_has_parent_of_type(dest, F_TRASH))
1135 iflags |= IMAP_FLAG_SEEN;
1137 ok = imap_cmd_append(session, destdir, real_file, iflags,
1140 if (ok != IMAP_SUCCESS) {
1141 g_warning("can't append message %s\n", real_file);
1145 statusbar_progress_all(0,0,0);
1146 statusbar_pop_all();
1149 debug_print("appended new message as %d\n", new_uid);
1150 /* put the local file in the imapcache, so that we don't
1151 * have to fetch it back later. */
1153 gchar *cache_path = folder_item_get_path(dest);
1154 if (!is_dir_exist(cache_path))
1155 make_dir_hier(cache_path);
1156 if (is_dir_exist(cache_path)) {
1157 gchar *cache_file = g_strconcat(
1158 cache_path, G_DIR_SEPARATOR_S,
1159 itos(new_uid), NULL);
1160 copy_file(real_file, cache_file, TRUE);
1161 debug_print("copied to cache: %s\n", cache_file);
1168 if (relation != NULL)
1169 g_relation_insert(relation, fileinfo->msginfo != NULL ?
1170 (gpointer) fileinfo->msginfo : (gpointer) fileinfo,
1171 GINT_TO_POINTER(dest->last_num + 1));
1173 new_uid = dest->last_num+1;
1175 if (last_uid < new_uid) {
1181 statusbar_progress_all(0,0,0);
1182 statusbar_pop_all();
1184 imap_cmd_expunge(session);
1192 static gint imap_do_copy_msgs(Folder *folder, FolderItem *dest,
1193 MsgInfoList *msglist, GRelation *relation)
1197 GSList *seq_list, *cur;
1199 IMAPSession *session;
1200 gint ok = IMAP_SUCCESS;
1201 GRelation *uid_mapping;
1203 gboolean single = FALSE;
1205 g_return_val_if_fail(folder != NULL, -1);
1206 g_return_val_if_fail(dest != NULL, -1);
1207 g_return_val_if_fail(msglist != NULL, -1);
1209 debug_print("getting session...\n");
1210 session = imap_session_get(folder);
1216 msginfo = (MsgInfo *)msglist->data;
1217 if (msglist->next == NULL)
1219 src = msginfo->folder;
1221 g_warning("the src folder is identical to the dest.\n");
1226 if (src->folder != dest->folder) {
1227 GSList *infolist = NULL, *cur;
1229 for (cur = msglist; cur; cur = cur->next) {
1230 msginfo = (MsgInfo *)cur->data;
1231 MsgFileInfo *fileinfo = g_new0(MsgFileInfo, 1);
1232 fileinfo->file = procmsg_get_message_file(msginfo);
1233 fileinfo->flags = &(msginfo->flags);
1234 infolist = g_slist_prepend(infolist, fileinfo);
1236 infolist = g_slist_reverse(infolist);
1238 res = folder_item_add_msgs(dest, infolist, FALSE);
1239 for (cur = infolist; cur; cur = cur->next) {
1240 MsgFileInfo *info = (MsgFileInfo *)cur->data;
1244 g_slist_free(infolist);
1248 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
1249 NULL, NULL, NULL, NULL, FALSE);
1250 if (ok != IMAP_SUCCESS) {
1255 destdir = imap_get_real_path(session, IMAP_FOLDER(folder), dest->path);
1256 seq_list = imap_get_lep_set_from_msglist(msglist);
1257 uid_mapping = g_relation_new(2);
1258 g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
1260 statusbar_print_all(_("Copying messages..."));
1261 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
1262 struct mailimap_set * seq_set;
1263 struct mailimap_set * source = NULL;
1264 struct mailimap_set * dest = NULL;
1265 seq_set = cur->data;
1267 debug_print("Copying messages from %s to %s ...\n",
1268 src->path, destdir);
1270 ok = imap_cmd_copy(session, seq_set, destdir, uid_mapping,
1273 if (ok == IMAP_SUCCESS) {
1274 if (single && relation && source && dest) {
1275 clistiter *l = clist_begin(source->set_list);
1276 struct mailimap_set_item *i = (struct mailimap_set_item *)clist_content(l);
1277 int snum = i->set_first;
1279 l = clist_begin(dest->set_list);
1280 i = (struct mailimap_set_item *)clist_content(l);
1281 dnum = i->set_first;
1282 g_relation_insert(uid_mapping, GINT_TO_POINTER(snum),
1283 GINT_TO_POINTER(dnum));
1289 mailimap_set_free(source);
1291 mailimap_set_free(dest);
1293 if (ok != IMAP_SUCCESS) {
1294 g_relation_destroy(uid_mapping);
1295 imap_lep_set_free(seq_list);
1297 statusbar_pop_all();
1302 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
1303 MsgInfo *msginfo = (MsgInfo *)cur->data;
1306 tuples = g_relation_select(uid_mapping,
1307 GINT_TO_POINTER(msginfo->msgnum),
1309 if (tuples->len > 0) {
1310 gint num = GPOINTER_TO_INT(g_tuples_index(tuples, 0, 1));
1311 g_relation_insert(relation, msginfo,
1312 GINT_TO_POINTER(num));
1315 debug_print("copied new message as %d\n", num);
1316 /* put the local file in the imapcache, so that we don't
1317 * have to fetch it back later. */
1319 gchar *cache_path = folder_item_get_path(msginfo->folder);
1320 gchar *real_file = g_strconcat(
1321 cache_path, G_DIR_SEPARATOR_S,
1322 itos(msginfo->msgnum), NULL);
1323 gchar *cache_file = NULL;
1325 cache_path = folder_item_get_path(dest);
1326 cache_file = g_strconcat(
1327 cache_path, G_DIR_SEPARATOR_S,
1329 if (!is_dir_exist(cache_path))
1330 make_dir_hier(cache_path);
1331 if (is_file_exist(real_file) && is_dir_exist(cache_path)) {
1332 copy_file(real_file, cache_file, TRUE);
1333 debug_print("copied to cache: %s\n", cache_file);
1340 g_relation_insert(relation, msginfo,
1341 GINT_TO_POINTER(0));
1342 g_tuples_destroy(tuples);
1344 statusbar_pop_all();
1346 g_relation_destroy(uid_mapping);
1347 imap_lep_set_free(seq_list);
1351 IMAP_FOLDER_ITEM(dest)->lastuid = 0;
1352 IMAP_FOLDER_ITEM(dest)->uid_next = 0;
1353 g_slist_free(IMAP_FOLDER_ITEM(dest)->uid_list);
1354 IMAP_FOLDER_ITEM(dest)->uid_list = NULL;
1357 if (ok == IMAP_SUCCESS)
1363 static gint imap_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1367 g_return_val_if_fail(msginfo != NULL, -1);
1369 msglist.data = msginfo;
1370 msglist.next = NULL;
1372 return imap_copy_msgs(folder, dest, &msglist, NULL);
1375 static gint imap_copy_msgs(Folder *folder, FolderItem *dest,
1376 MsgInfoList *msglist, GRelation *relation)
1381 g_return_val_if_fail(folder != NULL, -1);
1382 g_return_val_if_fail(dest != NULL, -1);
1383 g_return_val_if_fail(msglist != NULL, -1);
1385 msginfo = (MsgInfo *)msglist->data;
1386 g_return_val_if_fail(msginfo->folder != NULL, -1);
1388 ret = imap_do_copy_msgs(folder, dest, msglist, relation);
1393 static gint imap_do_remove_msgs(Folder *folder, FolderItem *dest,
1394 MsgInfoList *msglist, GRelation *relation)
1396 gchar *destdir, *dir;
1397 GSList *numlist = NULL, *cur;
1399 IMAPSession *session;
1400 gint ok = IMAP_SUCCESS;
1401 GRelation *uid_mapping;
1403 g_return_val_if_fail(folder != NULL, -1);
1404 g_return_val_if_fail(dest != NULL, -1);
1405 g_return_val_if_fail(msglist != NULL, -1);
1407 debug_print("getting session...\n");
1408 session = imap_session_get(folder);
1413 msginfo = (MsgInfo *)msglist->data;
1415 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
1416 NULL, NULL, NULL, NULL, FALSE);
1417 if (ok != IMAP_SUCCESS) {
1422 destdir = imap_get_real_path(session, IMAP_FOLDER(folder), dest->path);
1423 for (cur = msglist; cur; cur = cur->next) {
1424 msginfo = (MsgInfo *)cur->data;
1425 if (!MSG_IS_DELETED(msginfo->flags))
1426 numlist = g_slist_prepend(numlist, GINT_TO_POINTER(msginfo->msgnum));
1428 numlist = g_slist_reverse(numlist);
1430 uid_mapping = g_relation_new(2);
1431 g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
1433 ok = imap_set_message_flags
1434 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
1435 numlist, IMAP_FLAG_DELETED, TRUE);
1436 if (ok != IMAP_SUCCESS) {
1437 log_warning(_("can't set deleted flags\n"));
1441 ok = imap_cmd_expunge(session);
1442 if (ok != IMAP_SUCCESS) {
1443 log_warning(_("can't expunge\n"));
1448 dir = folder_item_get_path(msginfo->folder);
1449 if (is_dir_exist(dir)) {
1450 for (cur = msglist; cur; cur = cur->next) {
1451 msginfo = (MsgInfo *)cur->data;
1452 remove_numbered_files(dir, msginfo->msgnum, msginfo->msgnum);
1457 g_relation_destroy(uid_mapping);
1458 g_slist_free(numlist);
1462 if (ok == IMAP_SUCCESS)
1468 static gint imap_remove_msgs(Folder *folder, FolderItem *dest,
1469 MsgInfoList *msglist, GRelation *relation)
1473 g_return_val_if_fail(folder != NULL, -1);
1474 g_return_val_if_fail(dest != NULL, -1);
1475 if (msglist == NULL)
1478 msginfo = (MsgInfo *)msglist->data;
1479 g_return_val_if_fail(msginfo->folder != NULL, -1);
1481 return imap_do_remove_msgs(folder, dest, msglist, relation);
1484 static gint imap_remove_all_msg(Folder *folder, FolderItem *item)
1486 GSList *list = folder_item_get_msg_list(item);
1487 gint res = imap_remove_msgs(folder, item, list, NULL);
1488 procmsg_msg_list_free(list);
1492 static gboolean imap_is_msg_changed(Folder *folder, FolderItem *item,
1495 /* TODO: properly implement this method */
1499 static gint imap_close(Folder *folder, FolderItem *item)
1504 gint imap_scan_tree_real(Folder *folder, gboolean subs_only)
1506 FolderItem *item = NULL;
1507 IMAPSession *session;
1508 gchar *root_folder = NULL;
1510 g_return_val_if_fail(folder != NULL, -1);
1511 g_return_val_if_fail(folder->account != NULL, -1);
1513 debug_print("getting session...\n");
1514 session = imap_session_get(folder);
1516 if (!folder->node) {
1517 folder_tree_destroy(folder);
1518 item = folder_item_new(folder, folder->name, NULL);
1519 item->folder = folder;
1520 folder->node = item->node = g_node_new(item);
1525 if (folder->account->imap_dir && *folder->account->imap_dir) {
1530 Xstrdup_a(root_folder, folder->account->imap_dir, {unlock_session();return -1;});
1531 extract_quote(root_folder, '"');
1532 subst_char(root_folder,
1533 imap_get_path_separator(session, IMAP_FOLDER(folder),
1536 strtailchomp(root_folder, '/');
1537 real_path = imap_get_real_path
1538 (session, IMAP_FOLDER(folder), root_folder);
1539 debug_print("IMAP root directory: %s\n", real_path);
1541 /* check if root directory exist */
1543 r = imap_threaded_list(session->folder, "", real_path,
1545 if ((r != MAILIMAP_NO_ERROR) || (clist_count(lep_list) == 0)) {
1546 if (!folder->node) {
1547 item = folder_item_new(folder, folder->name, NULL);
1548 item->folder = folder;
1549 folder->node = item->node = g_node_new(item);
1554 mailimap_list_result_free(lep_list);
1560 item = FOLDER_ITEM(folder->node->data);
1562 if (item && !item->path && root_folder) {
1563 item->path = g_strdup(root_folder);
1566 if (!item || ((item->path || root_folder) &&
1567 strcmp2(item->path, root_folder) != 0)) {
1568 folder_tree_destroy(folder);
1569 item = folder_item_new(folder, folder->name, root_folder);
1570 item->folder = folder;
1571 folder->node = item->node = g_node_new(item);
1574 imap_scan_tree_recursive(session, FOLDER_ITEM(folder->node->data), subs_only);
1575 imap_create_missing_folders(folder);
1581 static gint imap_scan_tree(Folder *folder)
1583 gboolean subs_only = FALSE;
1584 if (folder->account) {
1585 debug_print(" scanning only subs %d\n", folder->account->imap_subsonly);
1586 subs_only = folder->account->imap_subsonly;
1588 return imap_scan_tree_real(folder, subs_only);
1591 static gint imap_scan_tree_recursive(IMAPSession *session, FolderItem *item, gboolean subs_only)
1594 IMAPFolder *imapfolder;
1595 FolderItem *new_item;
1596 GSList *item_list, *cur;
1599 gchar *wildcard_path;
1605 g_return_val_if_fail(item != NULL, -1);
1606 g_return_val_if_fail(item->folder != NULL, -1);
1607 g_return_val_if_fail(item->no_sub == FALSE, -1);
1609 folder = item->folder;
1610 imapfolder = IMAP_FOLDER(folder);
1612 separator = imap_get_path_separator(session, imapfolder, item->path);
1614 if (folder->ui_func)
1615 folder->ui_func(folder, item, folder->ui_func_data);
1618 wildcard[0] = separator;
1621 real_path = imap_get_real_path(session, imapfolder, item->path);
1625 real_path = g_strdup("");
1628 Xstrcat_a(wildcard_path, real_path, wildcard,
1629 {g_free(real_path); return IMAP_ERROR;});
1633 r = imap_threaded_lsub(folder, "", wildcard_path, &lep_list);
1635 r = imap_threaded_list(folder, "", wildcard_path, &lep_list);
1637 if (r != MAILIMAP_NO_ERROR) {
1641 item_list = imap_list_from_lep(imapfolder,
1642 lep_list, real_path, FALSE);
1643 mailimap_list_result_free(lep_list);
1648 node = item->node->children;
1649 while (node != NULL) {
1650 FolderItem *old_item = FOLDER_ITEM(node->data);
1651 GNode *next = node->next;
1654 for (cur = item_list; cur != NULL; cur = cur->next) {
1655 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1656 if (!strcmp2(old_item->path, cur_item->path)) {
1657 new_item = cur_item;
1662 if (old_item && old_item->path && !strcmp(old_item->path, "INBOX")) {
1663 debug_print("not removing INBOX\n");
1665 debug_print("folder '%s' not found. removing...\n",
1667 folder_item_remove(old_item);
1670 old_item->no_sub = new_item->no_sub;
1671 old_item->no_select = new_item->no_select;
1672 if (old_item->no_sub == TRUE && node->children) {
1673 debug_print("folder '%s' doesn't have "
1674 "subfolders. removing...\n",
1676 folder_item_remove_children(old_item);
1683 for (cur = item_list; cur != NULL; cur = cur->next) {
1684 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1687 for (node = item->node->children; node != NULL;
1688 node = node->next) {
1689 if (!strcmp2(FOLDER_ITEM(node->data)->path,
1691 new_item = FOLDER_ITEM(node->data);
1692 folder_item_destroy(cur_item);
1698 new_item = cur_item;
1699 debug_print("new folder '%s' found.\n", new_item->path);
1700 folder_item_append(item, new_item);
1703 if (!strcmp(new_item->path, "INBOX")) {
1704 new_item->stype = F_INBOX;
1705 folder->inbox = new_item;
1706 } else if (!folder_item_parent(item) || item->stype == F_INBOX) {
1709 base = g_path_get_basename(new_item->path);
1711 if (!folder->outbox && !g_ascii_strcasecmp(base, "Sent")) {
1712 new_item->stype = F_OUTBOX;
1713 folder->outbox = new_item;
1714 } else if (!folder->draft && !g_ascii_strcasecmp(base, "Drafts")) {
1715 new_item->stype = F_DRAFT;
1716 folder->draft = new_item;
1717 } else if (!folder->queue && !g_ascii_strcasecmp(base, "Queue")) {
1718 new_item->stype = F_QUEUE;
1719 folder->queue = new_item;
1720 } else if (!folder->trash && !g_ascii_strcasecmp(base, "Trash")) {
1721 new_item->stype = F_TRASH;
1722 folder->trash = new_item;
1727 if (new_item->no_sub == FALSE)
1728 imap_scan_tree_recursive(session, new_item, subs_only);
1731 g_slist_free(item_list);
1733 return IMAP_SUCCESS;
1736 GList *imap_scan_subtree(Folder *folder, FolderItem *item, gboolean unsubs_only, gboolean recursive)
1738 IMAPSession *session = imap_session_get(folder);
1740 gchar *wildcard_path;
1744 GSList *item_list = NULL, *cur;
1745 GList *child_list = NULL, *tmplist = NULL;
1746 GSList *sub_list = NULL;
1752 separator = imap_get_path_separator(session, IMAP_FOLDER(folder), item->path);
1755 wildcard[0] = separator;
1758 real_path = imap_get_real_path(session, IMAP_FOLDER(folder), item->path);
1762 real_path = g_strdup("");
1765 Xstrcat_a(wildcard_path, real_path, wildcard,
1766 {g_free(real_path); return NULL;});
1770 statusbar_print_all(_("Looking for unsubscribed folders in %s..."),
1771 item->path?item->path:item->name);
1773 statusbar_print_all(_("Looking for subfolders of %s..."),
1774 item->path?item->path:item->name);
1776 r = imap_threaded_list(folder, "", wildcard_path, &lep_list);
1778 statusbar_pop_all();
1781 item_list = imap_list_from_lep(IMAP_FOLDER(folder),
1782 lep_list, real_path, FALSE);
1783 mailimap_list_result_free(lep_list);
1785 for (cur = item_list; cur != NULL; cur = cur->next) {
1786 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1788 tmplist = imap_scan_subtree(folder, cur_item,
1789 unsubs_only, recursive);
1791 child_list = g_list_concat(child_list, tmplist);
1793 child_list = g_list_prepend(child_list,
1794 imap_get_real_path(session,
1795 IMAP_FOLDER(folder), cur_item->path));
1797 folder_item_destroy(cur_item);
1799 child_list = g_list_reverse(child_list);
1800 g_slist_free(item_list);
1803 r = imap_threaded_lsub(folder, "", wildcard_path, &lep_list);
1805 statusbar_pop_all();
1808 sub_list = imap_list_from_lep(IMAP_FOLDER(folder),
1809 lep_list, real_path, FALSE);
1810 mailimap_list_result_free(lep_list);
1812 for (cur = sub_list; cur != NULL; cur = cur->next) {
1813 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1814 GList *oldlitem = NULL;
1815 gchar *tmp = imap_get_real_path(session,
1816 IMAP_FOLDER(folder), cur_item->path);
1817 folder_item_destroy(cur_item);
1818 oldlitem = g_list_find_custom(
1819 child_list, tmp, (GCompareFunc)strcmp2);
1821 child_list = g_list_remove_link(child_list, oldlitem);
1822 g_free(oldlitem->data);
1823 g_list_free(oldlitem);
1829 statusbar_pop_all();
1834 static gint imap_create_tree(Folder *folder)
1836 g_return_val_if_fail(folder != NULL, -1);
1837 g_return_val_if_fail(folder->node != NULL, -1);
1838 g_return_val_if_fail(folder->node->data != NULL, -1);
1839 g_return_val_if_fail(folder->account != NULL, -1);
1841 imap_scan_tree(folder);
1842 imap_create_missing_folders(folder);
1847 static void imap_create_missing_folders(Folder *folder)
1849 g_return_if_fail(folder != NULL);
1852 folder->inbox = imap_create_special_folder
1853 (folder, F_INBOX, "INBOX");
1855 folder->trash = imap_create_special_folder
1856 (folder, F_TRASH, "Trash");
1858 folder->queue = imap_create_special_folder
1859 (folder, F_QUEUE, "Queue");
1860 if (!folder->outbox)
1861 folder->outbox = imap_create_special_folder
1862 (folder, F_OUTBOX, "Sent");
1864 folder->draft = imap_create_special_folder
1865 (folder, F_DRAFT, "Drafts");
1868 static FolderItem *imap_create_special_folder(Folder *folder,
1869 SpecialFolderItemType stype,
1873 FolderItem *new_item;
1875 g_return_val_if_fail(folder != NULL, NULL);
1876 g_return_val_if_fail(folder->node != NULL, NULL);
1877 g_return_val_if_fail(folder->node->data != NULL, NULL);
1878 g_return_val_if_fail(folder->account != NULL, NULL);
1879 g_return_val_if_fail(name != NULL, NULL);
1881 item = FOLDER_ITEM(folder->node->data);
1882 new_item = imap_create_folder(folder, item, name);
1885 g_warning("Can't create '%s'\n", name);
1886 if (!folder->inbox) return NULL;
1888 new_item = imap_create_folder(folder, folder->inbox, name);
1890 g_warning("Can't create '%s' under INBOX\n", name);
1892 new_item->stype = stype;
1894 new_item->stype = stype;
1899 static gchar *imap_folder_get_path(Folder *folder)
1903 g_return_val_if_fail(folder != NULL, NULL);
1904 g_return_val_if_fail(folder->account != NULL, NULL);
1906 folder_path = g_strconcat(get_imap_cache_dir(),
1908 folder->account->recv_server,
1910 folder->account->userid,
1916 static gchar *imap_item_get_path(Folder *folder, FolderItem *item)
1918 gchar *folder_path, *path;
1920 g_return_val_if_fail(folder != NULL, NULL);
1921 g_return_val_if_fail(item != NULL, NULL);
1922 folder_path = imap_folder_get_path(folder);
1924 g_return_val_if_fail(folder_path != NULL, NULL);
1925 if (folder_path[0] == G_DIR_SEPARATOR) {
1927 path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
1930 path = g_strdup(folder_path);
1933 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1934 folder_path, G_DIR_SEPARATOR_S,
1937 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1940 g_free(folder_path);
1945 static FolderItem *imap_create_folder(Folder *folder, FolderItem *parent,
1948 gchar *dirpath, *imap_path;
1949 IMAPSession *session;
1950 FolderItem *new_item;
1955 gboolean no_select = FALSE, no_sub = FALSE;
1956 gboolean exist = FALSE;
1958 g_return_val_if_fail(folder != NULL, NULL);
1959 g_return_val_if_fail(folder->account != NULL, NULL);
1960 g_return_val_if_fail(parent != NULL, NULL);
1961 g_return_val_if_fail(name != NULL, NULL);
1963 debug_print("getting session...\n");
1964 session = imap_session_get(folder);
1969 if (!folder_item_parent(parent) && strcmp(name, "INBOX") == 0) {
1970 dirpath = g_strdup(name);
1971 }else if (parent->path)
1972 dirpath = g_strconcat(parent->path, "/", name, NULL);
1973 else if ((p = strchr(name, '/')) != NULL && *(p + 1) != '\0')
1974 dirpath = g_strdup(name);
1975 else if (folder->account->imap_dir && *folder->account->imap_dir) {
1978 Xstrdup_a(imap_dir, folder->account->imap_dir, {unlock_session();return NULL;});
1979 strtailchomp(imap_dir, '/');
1980 dirpath = g_strconcat(imap_dir, "/", name, NULL);
1982 dirpath = g_strdup(name);
1986 /* keep trailing directory separator to create a folder that contains
1988 imap_path = imap_utf8_to_modified_utf7(dirpath);
1990 strtailchomp(dirpath, '/');
1991 Xstrdup_a(new_name, name, {
1996 separator = imap_get_path_separator(session, IMAP_FOLDER(folder), imap_path);
1997 imap_path_separator_subst(imap_path, separator);
1998 /* remove trailing / for display */
1999 strtailchomp(new_name, '/');
2001 if (strcmp(dirpath, "INBOX") != 0) {
2006 argbuf = g_ptr_array_new();
2007 r = imap_threaded_list(folder, "", imap_path, &lep_list);
2008 if (r != MAILIMAP_NO_ERROR) {
2009 log_warning(_("can't create mailbox: LIST failed\n"));
2012 ptr_array_free_strings(argbuf);
2013 g_ptr_array_free(argbuf, TRUE);
2018 if (clist_count(lep_list) > 0)
2020 mailimap_list_result_free(lep_list);
2023 ok = imap_cmd_create(session, imap_path);
2024 if (ok != IMAP_SUCCESS) {
2025 log_warning(_("can't create mailbox\n"));
2031 r = imap_threaded_list(folder, "", imap_path, &lep_list);
2032 if (r == MAILIMAP_NO_ERROR) {
2033 GSList *item_list = imap_list_from_lep(IMAP_FOLDER(folder),
2034 lep_list, dirpath, TRUE);
2036 FolderItem *cur_item = FOLDER_ITEM(item_list->data);
2037 no_select = cur_item->no_select;
2038 no_sub = cur_item->no_sub;
2039 g_slist_free(item_list);
2041 mailimap_list_result_free(lep_list);
2044 imap_threaded_subscribe(folder, imap_path, TRUE);
2048 /* just get flags */
2049 r = imap_threaded_list(folder, "", "INBOX", &lep_list);
2050 if (r == MAILIMAP_NO_ERROR) {
2051 GSList *item_list = imap_list_from_lep(IMAP_FOLDER(folder),
2052 lep_list, dirpath, TRUE);
2054 FolderItem *cur_item = FOLDER_ITEM(item_list->data);
2055 no_select = cur_item->no_select;
2056 no_sub = cur_item->no_sub;
2057 g_slist_free(item_list);
2059 mailimap_list_result_free(lep_list);
2063 new_item = folder_item_new(folder, new_name, dirpath);
2064 new_item->no_select = no_select;
2065 new_item->no_sub = no_sub;
2066 folder_item_append(parent, new_item);
2070 dirpath = folder_item_get_path(new_item);
2071 if (!is_dir_exist(dirpath))
2072 make_dir_hier(dirpath);
2077 /* folder existed, scan it */
2078 folder_item_scan_full(new_item, FALSE);
2084 static gint imap_rename_folder(Folder *folder, FolderItem *item,
2089 gchar *real_oldpath;
2090 gchar *real_newpath;
2092 gchar *old_cache_dir;
2093 gchar *new_cache_dir;
2094 IMAPSession *session;
2097 gint exists, recent, unseen;
2098 guint32 uid_validity;
2100 g_return_val_if_fail(folder != NULL, -1);
2101 g_return_val_if_fail(item != NULL, -1);
2102 g_return_val_if_fail(item->path != NULL, -1);
2103 g_return_val_if_fail(name != NULL, -1);
2105 debug_print("getting session...\n");
2106 session = imap_session_get(folder);
2111 if (strchr(name, imap_get_path_separator(session, IMAP_FOLDER(folder), item->path)) != NULL) {
2112 g_warning(_("New folder name must not contain the namespace "
2118 real_oldpath = imap_get_real_path(session, IMAP_FOLDER(folder), item->path);
2120 g_free(session->mbox);
2121 session->mbox = NULL;
2122 ok = imap_cmd_examine(session, "INBOX",
2123 &exists, &recent, &unseen, &uid_validity, FALSE);
2124 if (ok != IMAP_SUCCESS) {
2125 g_free(real_oldpath);
2130 separator = imap_get_path_separator(session, IMAP_FOLDER(folder), item->path);
2131 if (strchr(item->path, G_DIR_SEPARATOR)) {
2132 dirpath = g_path_get_dirname(item->path);
2133 newpath = g_strconcat(dirpath, G_DIR_SEPARATOR_S, name, NULL);
2136 newpath = g_strdup(name);
2138 real_newpath = imap_utf8_to_modified_utf7(newpath);
2139 imap_path_separator_subst(real_newpath, separator);
2141 ok = imap_cmd_rename(session, real_oldpath, real_newpath);
2142 if (ok != IMAP_SUCCESS) {
2143 log_warning(_("can't rename mailbox: %s to %s\n"),
2144 real_oldpath, real_newpath);
2145 g_free(real_oldpath);
2147 g_free(real_newpath);
2153 item->name = g_strdup(name);
2155 old_cache_dir = folder_item_get_path(item);
2157 paths[0] = g_strdup(item->path);
2159 g_node_traverse(item->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
2160 imap_rename_folder_func, paths);
2162 if (is_dir_exist(old_cache_dir)) {
2163 new_cache_dir = folder_item_get_path(item);
2164 if (rename(old_cache_dir, new_cache_dir) < 0) {
2165 FILE_OP_ERROR(old_cache_dir, "rename");
2167 g_free(new_cache_dir);
2170 g_free(old_cache_dir);
2173 g_free(real_oldpath);
2174 g_free(real_newpath);
2179 gint imap_subscribe(Folder *folder, FolderItem *item, gchar *rpath, gboolean sub)
2183 IMAPSession *session;
2184 debug_print("getting session...\n");
2186 session = imap_session_get(folder);
2190 if (item && item->path) {
2191 path = imap_get_real_path(session, IMAP_FOLDER(folder), item->path);
2194 if (!strcmp(path, "INBOX") && sub == FALSE)
2196 debug_print("%ssubscribing %s\n", sub?"":"un", path);
2197 r = imap_threaded_subscribe(folder, path, sub);
2200 r = imap_threaded_subscribe(folder, rpath, sub);
2206 static gint imap_remove_folder_real(Folder *folder, FolderItem *item)
2209 IMAPSession *session;
2213 g_return_val_if_fail(folder != NULL, -1);
2214 g_return_val_if_fail(item != NULL, -1);
2215 g_return_val_if_fail(item->path != NULL, -1);
2217 debug_print("getting session...\n");
2218 session = imap_session_get(folder);
2222 path = imap_get_real_path(session, IMAP_FOLDER(folder), item->path);
2224 imap_threaded_subscribe(folder, path, FALSE);
2225 ok = imap_cmd_delete(session, path);
2226 if (ok != IMAP_SUCCESS) {
2227 gchar *tmp = g_strdup_printf("%s%c", path,
2228 imap_get_path_separator(session, IMAP_FOLDER(folder), path));
2231 ok = imap_cmd_delete(session, path);
2234 if (ok != IMAP_SUCCESS) {
2235 log_warning(_("can't delete mailbox\n"));
2242 cache_dir = folder_item_get_path(item);
2243 if (is_dir_exist(cache_dir) && remove_dir_recursive(cache_dir) < 0)
2244 g_warning("can't remove directory '%s'\n", cache_dir);
2246 folder_item_remove(item);
2251 static gint imap_remove_folder(Folder *folder, FolderItem *item)
2255 g_return_val_if_fail(item != NULL, -1);
2256 g_return_val_if_fail(item->folder != NULL, -1);
2257 g_return_val_if_fail(item->node != NULL, -1);
2259 node = item->node->children;
2260 while (node != NULL) {
2262 if (imap_remove_folder(folder, FOLDER_ITEM(node->data)) < 0)
2266 debug_print("IMAP removing %s\n", item->path);
2268 if (imap_remove_all_msg(folder, item) < 0)
2270 return imap_remove_folder_real(folder, item);
2273 typedef struct _uncached_data {
2274 IMAPSession *session;
2276 MsgNumberList *numlist;
2282 static void *imap_get_uncached_messages_thread(void *data)
2284 uncached_data *stuff = (uncached_data *)data;
2285 IMAPSession *session = stuff->session;
2286 FolderItem *item = stuff->item;
2287 MsgNumberList *numlist = stuff->numlist;
2289 GSList *newlist = NULL;
2290 GSList *llast = NULL;
2291 GSList *seq_list, *cur;
2293 debug_print("uncached_messages\n");
2295 if (session == NULL || item == NULL || item->folder == NULL
2296 || FOLDER_CLASS(item->folder) != &imap_class) {
2301 seq_list = imap_get_lep_set_from_numlist(numlist);
2302 debug_print("get msgs info\n");
2303 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
2304 struct mailimap_set * imapset;
2310 imapset = cur->data;
2312 r = imap_threaded_fetch_env(session->folder,
2313 imapset, &env_list);
2314 if (r != MAILIMAP_NO_ERROR)
2317 session_set_access_time(SESSION(session));
2320 for(i = 0 ; i < carray_count(env_list) ; i ++) {
2321 struct imap_fetch_env_info * info;
2324 info = carray_get(env_list, i);
2325 msginfo = imap_envelope_from_lep(info, item);
2326 if (msginfo == NULL)
2328 msginfo->folder = item;
2330 llast = newlist = g_slist_append(newlist, msginfo);
2332 llast = g_slist_append(llast, msginfo);
2333 llast = llast->next;
2338 imap_fetch_env_free(env_list);
2341 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
2342 struct mailimap_set * imapset;
2344 imapset = cur->data;
2345 mailimap_set_free(imapset);
2348 session_set_access_time(SESSION(session));
2353 #define MAX_MSG_NUM 50
2355 static GSList *imap_get_uncached_messages(IMAPSession *session,
2357 MsgNumberList *numlist)
2359 GSList *result = NULL;
2361 uncached_data *data = g_new0(uncached_data, 1);
2366 data->total = g_slist_length(numlist);
2367 debug_print("messages list : %i\n", data->total);
2369 while (cur != NULL) {
2370 GSList * partial_result;
2378 while (count < MAX_MSG_NUM) {
2383 if (newlist == NULL)
2384 llast = newlist = g_slist_append(newlist, p);
2386 llast = g_slist_append(llast, p);
2387 llast = llast->next;
2397 data->session = session;
2399 data->numlist = newlist;
2402 if (prefs_common.work_offline &&
2403 !inc_offline_should_override(
2404 _("Claws Mail needs network access in order "
2405 "to access the IMAP server."))) {
2411 (GSList *)imap_get_uncached_messages_thread(data);
2413 statusbar_progress_all(data->cur,data->total, 1);
2415 g_slist_free(newlist);
2417 result = g_slist_concat(result, partial_result);
2421 statusbar_progress_all(0,0,0);
2422 statusbar_pop_all();
2427 static void imap_delete_all_cached_messages(FolderItem *item)
2431 g_return_if_fail(item != NULL);
2432 g_return_if_fail(item->folder != NULL);
2433 g_return_if_fail(FOLDER_CLASS(item->folder) == &imap_class);
2435 debug_print("Deleting all cached messages...\n");
2437 dir = folder_item_get_path(item);
2438 if (is_dir_exist(dir))
2439 remove_all_numbered_files(dir);
2442 debug_print("done.\n");
2445 gchar imap_get_path_separator_for_item(FolderItem *item)
2447 Folder *folder = NULL;
2448 IMAPFolder *imap_folder = NULL;
2449 IMAPSession *session = NULL;
2454 folder = item->folder;
2459 imap_folder = IMAP_FOLDER(folder);
2464 debug_print("getting session...");
2465 session = imap_session_get(FOLDER(folder));
2466 result = imap_get_path_separator(session, imap_folder, item->path);
2471 static gchar imap_refresh_path_separator(IMAPSession *session, IMAPFolder *folder, const gchar *subfolder)
2475 gchar separator = '\0';
2477 g_return_val_if_fail(session != NULL, '/');
2478 r = imap_threaded_list((Folder *)folder, "", subfolder, &lep_list);
2480 if (r != MAILIMAP_NO_ERROR) {
2481 log_warning(_("LIST failed\n"));
2485 if (clist_count(lep_list) > 0) {
2486 clistiter * iter = clist_begin(lep_list);
2487 struct mailimap_mailbox_list * mb;
2488 mb = clist_content(iter);
2490 separator = mb->mb_delimiter;
2491 debug_print("got separator: %c\n", folder->last_seen_separator);
2493 mailimap_list_result_free(lep_list);
2497 static gchar imap_get_path_separator(IMAPSession *session, IMAPFolder *folder, const gchar *path)
2499 gchar separator = '/';
2501 if (folder->last_seen_separator == 0) {
2502 folder->last_seen_separator = imap_refresh_path_separator(session, folder, "");
2505 if (folder->last_seen_separator == 0) {
2506 folder->last_seen_separator = imap_refresh_path_separator(session, folder, "INBOX");
2509 if (folder->last_seen_separator != 0) {
2510 debug_print("using separator: %c\n", folder->last_seen_separator);
2511 return folder->last_seen_separator;
2517 static gchar *imap_get_real_path(IMAPSession *session, IMAPFolder *folder, const gchar *path)
2522 g_return_val_if_fail(folder != NULL, NULL);
2523 g_return_val_if_fail(path != NULL, NULL);
2525 real_path = imap_utf8_to_modified_utf7(path);
2526 separator = imap_get_path_separator(session, folder, path);
2527 imap_path_separator_subst(real_path, separator);
2532 static gint imap_set_message_flags(IMAPSession *session,
2533 MsgNumberList *numlist,
2541 seq_list = imap_get_lep_set_from_numlist(numlist);
2543 for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) {
2544 struct mailimap_set * imapset;
2546 imapset = cur->data;
2548 ok = imap_cmd_store(session, imapset,
2552 imap_lep_set_free(seq_list);
2554 return IMAP_SUCCESS;
2557 typedef struct _select_data {
2558 IMAPSession *session;
2563 guint32 *uid_validity;
2567 static gint imap_select(IMAPSession *session, IMAPFolder *folder,
2569 gint *exists, gint *recent, gint *unseen,
2570 guint32 *uid_validity, gboolean block)
2574 gint exists_, recent_, unseen_;
2575 guint32 uid_validity_;
2577 if (!exists && !recent && !unseen && !uid_validity) {
2578 if (session->mbox && strcmp(session->mbox, path) == 0)
2579 return IMAP_SUCCESS;
2588 uid_validity = &uid_validity_;
2590 g_free(session->mbox);
2591 session->mbox = NULL;
2593 real_path = imap_get_real_path(session, folder, path);
2595 ok = imap_cmd_select(session, real_path,
2596 exists, recent, unseen, uid_validity, block);
2597 if (ok != IMAP_SUCCESS)
2598 log_warning(_("can't select folder: %s\n"), real_path);
2600 session->mbox = g_strdup(path);
2601 session->folder_content_changed = FALSE;
2608 static gint imap_status(IMAPSession *session, IMAPFolder *folder,
2609 const gchar *path, IMAPFolderItem *item,
2611 guint32 *uid_next, guint32 *uid_validity,
2612 gint *unseen, gboolean block)
2616 struct mailimap_mailbox_data_status * data_status;
2621 real_path = imap_get_real_path(session, folder, path);
2635 r = imap_threaded_status(FOLDER(folder), real_path,
2636 &data_status, mask);
2639 if (r != MAILIMAP_NO_ERROR) {
2640 debug_print("status err %d\n", r);
2644 if (data_status->st_info_list == NULL) {
2645 mailimap_mailbox_data_status_free(data_status);
2646 debug_print("status->st_info_list == NULL\n");
2651 for(iter = clist_begin(data_status->st_info_list) ; iter != NULL ;
2652 iter = clist_next(iter)) {
2653 struct mailimap_status_info * info;
2655 info = clist_content(iter);
2656 switch (info->st_att) {
2657 case MAILIMAP_STATUS_ATT_MESSAGES:
2658 * messages = info->st_value;
2659 got_values |= 1 << 0;
2662 case MAILIMAP_STATUS_ATT_UIDNEXT:
2663 * uid_next = info->st_value;
2664 got_values |= 1 << 2;
2667 case MAILIMAP_STATUS_ATT_UIDVALIDITY:
2668 * uid_validity = info->st_value;
2669 got_values |= 1 << 3;
2672 case MAILIMAP_STATUS_ATT_UNSEEN:
2673 * unseen = info->st_value;
2674 got_values |= 1 << 4;
2678 mailimap_mailbox_data_status_free(data_status);
2680 if (got_values != mask) {
2681 debug_print("status: incomplete values received (%d)\n", got_values);
2684 return IMAP_SUCCESS;
2687 static void imap_free_capabilities(IMAPSession *session)
2689 slist_free_strings(session->capability);
2690 g_slist_free(session->capability);
2691 session->capability = NULL;
2694 /* low-level IMAP4rev1 commands */
2696 static gint imap_cmd_login(IMAPSession *session,
2697 const gchar *user, const gchar *pass,
2703 if (!strcmp(type, "LOGIN") && imap_has_capability(session, "LOGINDISABLED")) {
2704 gint ok = IMAP_ERROR;
2705 if (imap_has_capability(session, "STARTTLS")) {
2707 log_warning(_("Server requires TLS to log in.\n"));
2708 ok = imap_cmd_starttls(session);
2709 if (ok != IMAP_SUCCESS) {
2710 log_warning(_("Can't start TLS session.\n"));
2714 imap_free_capabilities(session);
2715 if (imap_get_capabilities(session) != MAILIMAP_NO_ERROR) {
2716 log_warning(_("Can't refresh capabilities.\n"));
2721 log_error(_("Connection to %s failed: "
2722 "server requires TLS, but Claws Mail "
2723 "has been compiled without OpenSSL "
2725 SESSION(session)->server);
2729 log_error(_("Server logins are disabled.\n"));
2734 log_print("IMAP4> Logging %s to %s using %s\n",
2736 SESSION(session)->server,
2738 r = imap_threaded_login(session->folder, user, pass, type);
2739 if (r != MAILIMAP_NO_ERROR) {
2740 log_print("IMAP4< Error logging in to %s\n",
2741 SESSION(session)->server);
2744 log_print("IMAP4< Login to %s successful\n",
2745 SESSION(session)->server);
2751 static gint imap_cmd_noop(IMAPSession *session)
2754 unsigned int exists;
2756 r = imap_threaded_noop(session->folder, &exists);
2757 if (r != MAILIMAP_NO_ERROR) {
2758 debug_print("noop err %d\n", r);
2761 session->exists = exists;
2762 session_set_access_time(SESSION(session));
2764 return IMAP_SUCCESS;
2768 static gint imap_cmd_starttls(IMAPSession *session)
2772 r = imap_threaded_starttls(session->folder,
2773 SESSION(session)->server, SESSION(session)->port);
2774 if (r != MAILIMAP_NO_ERROR) {
2775 debug_print("starttls err %d\n", r);
2778 return IMAP_SUCCESS;
2782 static gint imap_cmd_select(IMAPSession *session, const gchar *folder,
2783 gint *exists, gint *recent, gint *unseen,
2784 guint32 *uid_validity, gboolean block)
2788 r = imap_threaded_select(session->folder, folder,
2789 exists, recent, unseen, uid_validity);
2790 if (r != MAILIMAP_NO_ERROR) {
2791 debug_print("select err %d\n", r);
2794 return IMAP_SUCCESS;
2797 static gint imap_cmd_examine(IMAPSession *session, const gchar *folder,
2798 gint *exists, gint *recent, gint *unseen,
2799 guint32 *uid_validity, gboolean block)
2803 r = imap_threaded_examine(session->folder, folder,
2804 exists, recent, unseen, uid_validity);
2805 if (r != MAILIMAP_NO_ERROR) {
2806 debug_print("examine err %d\n", r);
2810 return IMAP_SUCCESS;
2813 static gint imap_cmd_create(IMAPSession *session, const gchar *folder)
2817 r = imap_threaded_create(session->folder, folder);
2818 if (r != MAILIMAP_NO_ERROR) {
2823 return IMAP_SUCCESS;
2826 static gint imap_cmd_rename(IMAPSession *session, const gchar *old_folder,
2827 const gchar *new_folder)
2831 r = imap_threaded_rename(session->folder, old_folder,
2833 if (r != MAILIMAP_NO_ERROR) {
2838 return IMAP_SUCCESS;
2841 static gint imap_cmd_delete(IMAPSession *session, const gchar *folder)
2846 r = imap_threaded_delete(session->folder, folder);
2847 if (r != MAILIMAP_NO_ERROR) {
2852 return IMAP_SUCCESS;
2855 typedef struct _fetch_data {
2856 IMAPSession *session;
2858 const gchar *filename;
2864 static void *imap_cmd_fetch_thread(void *data)
2866 fetch_data *stuff = (fetch_data *)data;
2867 IMAPSession *session = stuff->session;
2868 guint32 uid = stuff->uid;
2869 const gchar *filename = stuff->filename;
2873 r = imap_threaded_fetch_content(session->folder,
2877 r = imap_threaded_fetch_content(session->folder,
2880 if (r != MAILIMAP_NO_ERROR) {
2881 debug_print("fetch err %d\n", r);
2882 return GINT_TO_POINTER(IMAP_ERROR);
2884 return GINT_TO_POINTER(IMAP_SUCCESS);
2887 static gint imap_cmd_fetch(IMAPSession *session, guint32 uid,
2888 const gchar *filename, gboolean headers,
2891 fetch_data *data = g_new0(fetch_data, 1);
2894 data->session = session;
2896 data->filename = filename;
2897 data->headers = headers;
2900 if (prefs_common.work_offline &&
2901 !inc_offline_should_override(
2902 _("Claws Mail needs network access in order "
2903 "to access the IMAP server."))) {
2907 statusbar_print_all(_("Fetching message..."));
2908 result = GPOINTER_TO_INT(imap_cmd_fetch_thread(data));
2909 statusbar_pop_all();
2915 static gint imap_cmd_append(IMAPSession *session, const gchar *destfolder,
2916 const gchar *file, IMAPFlags flags,
2919 struct mailimap_flag_list * flag_list;
2922 g_return_val_if_fail(file != NULL, IMAP_ERROR);
2924 flag_list = imap_flag_to_lep(flags);
2925 r = imap_threaded_append(session->folder, destfolder,
2926 file, flag_list, (int *)new_uid);
2927 mailimap_flag_list_free(flag_list);
2929 if (r != MAILIMAP_NO_ERROR) {
2930 debug_print("append err %d\n", r);
2933 return IMAP_SUCCESS;
2936 static gint imap_cmd_copy(IMAPSession *session, struct mailimap_set * set,
2937 const gchar *destfolder, GRelation *uid_mapping,
2938 struct mailimap_set **source, struct mailimap_set **dest)
2942 g_return_val_if_fail(session != NULL, IMAP_ERROR);
2943 g_return_val_if_fail(set != NULL, IMAP_ERROR);
2944 g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
2946 r = imap_threaded_copy(session->folder, set, destfolder, source, dest);
2947 if (r != MAILIMAP_NO_ERROR) {
2952 return IMAP_SUCCESS;
2955 static gint imap_cmd_store(IMAPSession *session, struct mailimap_set * set,
2956 IMAPFlags flags, int do_add)
2959 struct mailimap_flag_list * flag_list;
2960 struct mailimap_store_att_flags * store_att_flags;
2962 flag_list = imap_flag_to_lep(flags);
2966 mailimap_store_att_flags_new_add_flags_silent(flag_list);
2969 mailimap_store_att_flags_new_remove_flags_silent(flag_list);
2971 r = imap_threaded_store(session->folder, set, store_att_flags);
2972 mailimap_store_att_flags_free(store_att_flags);
2973 if (r != MAILIMAP_NO_ERROR) {
2978 return IMAP_SUCCESS;
2981 static gint imap_cmd_expunge(IMAPSession *session)
2985 if (prefs_common.work_offline &&
2986 !inc_offline_should_override(
2987 _("Claws Mail needs network access in order "
2988 "to access the IMAP server."))) {
2992 r = imap_threaded_expunge(session->folder);
2993 if (r != MAILIMAP_NO_ERROR) {
2998 return IMAP_SUCCESS;
3001 static void imap_path_separator_subst(gchar *str, gchar separator)
3004 gboolean in_escape = FALSE;
3006 if (!separator || separator == '/') return;
3008 for (p = str; *p != '\0'; p++) {
3009 if (*p == '/' && !in_escape)
3011 else if (*p == '&' && *(p + 1) != '-' && !in_escape)
3013 else if (*p == '-' && in_escape)
3018 static gchar *imap_modified_utf7_to_utf8(const gchar *mutf7_str)
3020 static iconv_t cd = (iconv_t)-1;
3021 static gboolean iconv_ok = TRUE;
3024 size_t norm_utf7_len;
3026 gchar *to_str, *to_p;
3028 gboolean in_escape = FALSE;
3030 if (!iconv_ok) return g_strdup(mutf7_str);
3032 if (cd == (iconv_t)-1) {
3033 cd = iconv_open(CS_INTERNAL, CS_UTF_7);
3034 if (cd == (iconv_t)-1) {
3035 g_warning("iconv cannot convert UTF-7 to %s\n",
3038 return g_strdup(mutf7_str);
3042 /* modified UTF-7 to normal UTF-7 conversion */
3043 norm_utf7 = g_string_new(NULL);
3045 for (p = mutf7_str; *p != '\0'; p++) {
3046 /* replace: '&' -> '+',
3048 escaped ',' -> '/' */
3049 if (!in_escape && *p == '&') {
3050 if (*(p + 1) != '-') {
3051 g_string_append_c(norm_utf7, '+');
3054 g_string_append_c(norm_utf7, '&');
3057 } else if (in_escape && *p == ',') {
3058 g_string_append_c(norm_utf7, '/');
3059 } else if (in_escape && *p == '-') {
3060 g_string_append_c(norm_utf7, '-');
3063 g_string_append_c(norm_utf7, *p);
3067 norm_utf7_p = norm_utf7->str;
3068 norm_utf7_len = norm_utf7->len;
3069 to_len = strlen(mutf7_str) * 5;
3070 to_p = to_str = g_malloc(to_len + 1);
3072 if (iconv(cd, (ICONV_CONST gchar **)&norm_utf7_p, &norm_utf7_len,
3073 &to_p, &to_len) == -1) {
3074 g_warning(_("iconv cannot convert UTF-7 to %s\n"),
3075 conv_get_locale_charset_str());
3076 g_string_free(norm_utf7, TRUE);
3078 return g_strdup(mutf7_str);
3081 /* second iconv() call for flushing */
3082 iconv(cd, NULL, NULL, &to_p, &to_len);
3083 g_string_free(norm_utf7, TRUE);
3089 static gchar *imap_utf8_to_modified_utf7(const gchar *from)
3091 static iconv_t cd = (iconv_t)-1;
3092 static gboolean iconv_ok = TRUE;
3093 gchar *norm_utf7, *norm_utf7_p;
3094 size_t from_len, norm_utf7_len;
3096 gchar *from_tmp, *to, *p;
3097 gboolean in_escape = FALSE;
3099 if (!iconv_ok) return g_strdup(from);
3101 if (cd == (iconv_t)-1) {
3102 cd = iconv_open(CS_UTF_7, CS_INTERNAL);
3103 if (cd == (iconv_t)-1) {
3104 g_warning(_("iconv cannot convert %s to UTF-7\n"),
3107 return g_strdup(from);
3111 /* UTF-8 to normal UTF-7 conversion */
3112 Xstrdup_a(from_tmp, from, return g_strdup(from));
3113 from_len = strlen(from);
3114 norm_utf7_len = from_len * 5;
3115 Xalloca(norm_utf7, norm_utf7_len + 1, return g_strdup(from));
3116 norm_utf7_p = norm_utf7;
3118 #define IS_PRINT(ch) (isprint(ch) && IS_ASCII(ch))
3120 while (from_len > 0) {
3121 if (*from_tmp == '+') {
3122 *norm_utf7_p++ = '+';
3123 *norm_utf7_p++ = '-';
3127 } else if (IS_PRINT(*(guchar *)from_tmp)) {
3128 /* printable ascii char */
3129 *norm_utf7_p = *from_tmp;
3135 size_t conv_len = 0;
3137 /* unprintable char: convert to UTF-7 */
3139 while (!IS_PRINT(*(guchar *)p) && conv_len < from_len) {
3140 conv_len += g_utf8_skip[*(guchar *)p];
3141 p += g_utf8_skip[*(guchar *)p];
3144 from_len -= conv_len;
3145 if (iconv(cd, (ICONV_CONST gchar **)&from_tmp,
3147 &norm_utf7_p, &norm_utf7_len) == -1) {
3148 g_warning(_("iconv cannot convert UTF-8 to UTF-7\n"));
3149 return g_strdup(from);
3152 /* second iconv() call for flushing */
3153 iconv(cd, NULL, NULL, &norm_utf7_p, &norm_utf7_len);
3159 *norm_utf7_p = '\0';
3160 to_str = g_string_new(NULL);
3161 for (p = norm_utf7; p < norm_utf7_p; p++) {
3162 /* replace: '&' -> "&-",
3165 BASE64 '/' -> ',' */
3166 if (!in_escape && *p == '&') {
3167 g_string_append(to_str, "&-");
3168 } else if (!in_escape && *p == '+') {
3169 if (*(p + 1) == '-') {
3170 g_string_append_c(to_str, '+');
3173 g_string_append_c(to_str, '&');
3176 } else if (in_escape && *p == '/') {
3177 g_string_append_c(to_str, ',');
3178 } else if (in_escape && *p == '-') {
3179 g_string_append_c(to_str, '-');
3182 g_string_append_c(to_str, *p);
3188 g_string_append_c(to_str, '-');
3192 g_string_free(to_str, FALSE);
3197 static gboolean imap_rename_folder_func(GNode *node, gpointer data)
3199 FolderItem *item = node->data;
3200 gchar **paths = data;
3201 const gchar *oldpath = paths[0];
3202 const gchar *newpath = paths[1];
3204 gchar *new_itempath;
3207 oldpathlen = strlen(oldpath);
3208 if (strncmp(oldpath, item->path, oldpathlen) != 0) {
3209 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
3213 base = item->path + oldpathlen;
3214 while (*base == G_DIR_SEPARATOR) base++;
3216 new_itempath = g_strdup(newpath);
3218 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
3221 item->path = new_itempath;
3226 typedef struct _get_list_uid_data {
3228 IMAPSession *session;
3229 IMAPFolderItem *item;
3230 GSList **msgnum_list;
3232 } get_list_uid_data;
3234 static void *get_list_of_uids_thread(void *data)
3236 get_list_uid_data *stuff = (get_list_uid_data *)data;
3237 Folder *folder = stuff->folder;
3238 IMAPFolderItem *item = stuff->item;
3239 GSList **msgnum_list = stuff->msgnum_list;
3240 gint ok, nummsgs = 0, lastuid_old;
3241 IMAPSession *session;
3242 GSList *uidlist, *elem;
3243 clist * lep_uidlist;
3246 session = stuff->session;
3247 if (session == NULL) {
3249 return GINT_TO_POINTER(-1);
3251 /* no session locking here, it's already locked by caller */
3252 ok = imap_select(session, IMAP_FOLDER(folder), item->item.path,
3253 NULL, NULL, NULL, NULL, TRUE);
3254 if (ok != IMAP_SUCCESS) {
3256 return GINT_TO_POINTER(-1);
3261 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SIMPLE, NULL,
3264 if (r == MAILIMAP_NO_ERROR) {
3265 GSList * fetchuid_list;
3268 imap_uid_list_from_lep(lep_uidlist);
3269 mailimap_search_result_free(lep_uidlist);
3271 uidlist = g_slist_concat(fetchuid_list, uidlist);
3274 GSList * fetchuid_list;
3275 carray * lep_uidtab;
3277 r = imap_threaded_fetch_uid(folder, item->lastuid + 1,
3279 if (r == MAILIMAP_NO_ERROR) {
3281 imap_uid_list_from_lep_tab(lep_uidtab);
3282 imap_fetch_uid_list_free(lep_uidtab);
3283 uidlist = g_slist_concat(fetchuid_list, uidlist);
3287 lastuid_old = item->lastuid;
3288 *msgnum_list = g_slist_copy(item->uid_list);
3289 nummsgs = g_slist_length(*msgnum_list);
3290 debug_print("Got %d uids from cache\n", g_slist_length(item->uid_list));
3292 for (elem = uidlist; elem != NULL; elem = g_slist_next(elem)) {
3295 msgnum = GPOINTER_TO_INT(elem->data);
3296 if (msgnum > lastuid_old) {
3297 *msgnum_list = g_slist_prepend(*msgnum_list, GINT_TO_POINTER(msgnum));
3298 item->uid_list = g_slist_prepend(item->uid_list, GINT_TO_POINTER(msgnum));
3301 if(msgnum > item->lastuid)
3302 item->lastuid = msgnum;
3305 g_slist_free(uidlist);
3307 return GINT_TO_POINTER(nummsgs);
3310 static gint get_list_of_uids(IMAPSession *session, Folder *folder, IMAPFolderItem *item, GSList **msgnum_list)
3313 get_list_uid_data *data = g_new0(get_list_uid_data, 1);
3315 data->folder = folder;
3317 data->msgnum_list = msgnum_list;
3318 data->session = session;
3319 if (prefs_common.work_offline &&
3320 !inc_offline_should_override(
3321 _("Claws Mail needs network access in order "
3322 "to access the IMAP server."))) {
3327 result = GPOINTER_TO_INT(get_list_of_uids_thread(data));
3333 gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list, gboolean *old_uids_valid)
3335 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3336 IMAPSession *session;
3337 gint ok, nummsgs = 0, exists;
3338 guint32 uid_next = 0, uid_val = 0;
3339 GSList *uidlist = NULL;
3341 gboolean selected_folder;
3342 debug_print("get_num_list\n");
3344 g_return_val_if_fail(folder != NULL, -1);
3345 g_return_val_if_fail(item != NULL, -1);
3346 g_return_val_if_fail(item->item.path != NULL, -1);
3347 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3348 g_return_val_if_fail(folder->account != NULL, -1);
3350 debug_print("getting session...\n");
3351 session = imap_session_get(folder);
3352 g_return_val_if_fail(session != NULL, -1);
3354 if (FOLDER_ITEM(item)->path)
3355 statusbar_print_all(_("Scanning folder %s%c%s ..."),
3356 FOLDER_ITEM(item)->folder->name,
3358 FOLDER_ITEM(item)->path);
3360 statusbar_print_all(_("Scanning folder %s ..."),
3361 FOLDER_ITEM(item)->folder->name);
3363 selected_folder = (session->mbox != NULL) &&
3364 (!strcmp(session->mbox, item->item.path));
3365 if (selected_folder && time(NULL) - item->use_cache < 2) {
3366 ok = imap_cmd_noop(session);
3367 if (ok != IMAP_SUCCESS) {
3368 debug_print("disconnected!\n");
3369 session = imap_reconnect_if_possible(folder, session);
3370 if (session == NULL) {
3371 statusbar_pop_all();
3376 exists = session->exists;
3378 uid_next = item->c_uid_next;
3379 uid_val = item->c_uid_validity;
3380 *old_uids_valid = TRUE;
3382 if (item->use_cache && time(NULL) - item->use_cache < 2) {
3383 exists = item->c_messages;
3384 uid_next = item->c_uid_next;
3385 uid_val = item->c_uid_validity;
3387 debug_print("using cache %d %d %d\n", exists, uid_next, uid_val);
3389 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path, item,
3390 &exists, &uid_next, &uid_val, NULL, FALSE);
3392 item->item.last_num = uid_next - 1;
3394 item->use_cache = (time_t)0;
3395 if (ok != IMAP_SUCCESS) {
3396 statusbar_pop_all();
3400 if(item->item.mtime == uid_val)
3401 *old_uids_valid = TRUE;
3403 *old_uids_valid = FALSE;
3405 debug_print("Freeing imap uid cache (%d != %d)\n",
3406 (int)item->item.mtime, uid_val);
3408 g_slist_free(item->uid_list);
3409 item->uid_list = NULL;
3411 item->item.mtime = uid_val;
3413 imap_delete_all_cached_messages((FolderItem *)item);
3417 /* If old uid_next matches new uid_next we can be sure no message
3418 was added to the folder */
3419 debug_print("uid_next is %d and item->uid_next %d \n",
3420 uid_next, item->uid_next);
3421 if (uid_next == item->uid_next) {
3422 nummsgs = g_slist_length(item->uid_list);
3424 /* If number of messages is still the same we
3425 know our caches message numbers are still valid,
3426 otherwise if the number of messages has decrease
3427 we discard our cache to start a new scan to find
3428 out which numbers have been removed */
3429 if (exists == nummsgs) {
3430 debug_print("exists == nummsgs\n");
3431 *msgnum_list = g_slist_copy(item->uid_list);
3432 statusbar_pop_all();
3435 } else if (exists < nummsgs) {
3436 debug_print("Freeing imap uid cache");
3438 g_slist_free(item->uid_list);
3439 item->uid_list = NULL;
3444 *msgnum_list = NULL;
3445 statusbar_pop_all();
3450 nummsgs = get_list_of_uids(session, folder, item, &uidlist);
3453 statusbar_pop_all();
3458 if (nummsgs != exists) {
3459 /* Cache contains more messages then folder, we have cached
3460 an old UID of a message that was removed and new messages
3461 have been added too, otherwise the uid_next check would
3463 debug_print("Freeing imap uid cache");
3465 g_slist_free(item->uid_list);
3466 item->uid_list = NULL;
3468 g_slist_free(*msgnum_list);
3470 nummsgs = get_list_of_uids(session, folder, item, &uidlist);
3473 *msgnum_list = uidlist;
3475 dir = folder_item_get_path((FolderItem *)item);
3476 debug_print("removing old messages from %s\n", dir);
3477 remove_numbered_files_not_in_list(dir, *msgnum_list);
3480 item->uid_next = uid_next;
3482 debug_print("get_num_list - ok - %i\n", nummsgs);
3483 statusbar_pop_all();
3488 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item)
3493 flags.perm_flags = MSG_NEW|MSG_UNREAD;
3494 flags.tmp_flags = 0;
3496 g_return_val_if_fail(item != NULL, NULL);
3497 g_return_val_if_fail(file != NULL, NULL);
3499 if (folder_has_parent_of_type(item, F_QUEUE)) {
3500 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
3501 } else if (folder_has_parent_of_type(item, F_DRAFT)) {
3502 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
3505 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3506 if (!msginfo) return NULL;
3508 msginfo->plaintext_file = g_strdup(file);
3509 msginfo->folder = item;
3514 GSList *imap_get_msginfos(Folder *folder, FolderItem *item,
3515 GSList *msgnum_list)
3517 IMAPSession *session;
3518 MsgInfoList *ret = NULL;
3521 debug_print("get_msginfos\n");
3523 g_return_val_if_fail(folder != NULL, NULL);
3524 g_return_val_if_fail(item != NULL, NULL);
3525 g_return_val_if_fail(msgnum_list != NULL, NULL);
3527 debug_print("getting session...\n");
3528 session = imap_session_get(folder);
3529 g_return_val_if_fail(session != NULL, NULL);
3531 debug_print("IMAP getting msginfos\n");
3532 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3533 NULL, NULL, NULL, NULL, FALSE);
3534 if (ok != IMAP_SUCCESS) {
3538 if (!(folder_has_parent_of_type(item, F_DRAFT) ||
3539 folder_has_parent_of_type(item, F_QUEUE))) {
3540 ret = g_slist_concat(ret,
3541 imap_get_uncached_messages(session, item,
3544 MsgNumberList *sorted_list, *elem, *llast = NULL;
3545 gint startnum, lastnum;
3547 sorted_list = g_slist_sort(g_slist_copy(msgnum_list), g_int_compare);
3549 startnum = lastnum = GPOINTER_TO_INT(sorted_list->data);
3551 llast = g_slist_last(ret);
3552 for (elem = sorted_list;; elem = g_slist_next(elem)) {
3556 num = GPOINTER_TO_INT(elem->data);
3558 if (num > lastnum + 1 || elem == NULL) {
3560 for (i = startnum; i <= lastnum; ++i) {
3563 file = imap_fetch_msg(folder, item, i);
3565 MsgInfo *msginfo = imap_parse_msg(file, item);
3566 if (msginfo != NULL) {
3567 msginfo->msgnum = i;
3569 llast = ret = g_slist_append(ret, msginfo);
3571 llast = g_slist_append(llast, msginfo);
3572 llast = llast->next;
3577 session_set_access_time(SESSION(session));
3588 g_slist_free(sorted_list);
3594 MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid)
3596 MsgInfo *msginfo = NULL;
3597 MsgInfoList *msginfolist;
3598 MsgNumberList numlist;
3600 numlist.next = NULL;
3601 numlist.data = GINT_TO_POINTER(uid);
3603 msginfolist = imap_get_msginfos(folder, item, &numlist);
3604 if (msginfolist != NULL) {
3605 msginfo = msginfolist->data;
3606 g_slist_free(msginfolist);
3612 gboolean imap_scan_required(Folder *folder, FolderItem *_item)
3614 IMAPSession *session;
3615 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3616 gint ok, exists = 0, unseen = 0;
3617 guint32 uid_next = 0, uid_val = 0;
3618 gboolean selected_folder;
3620 g_return_val_if_fail(folder != NULL, FALSE);
3621 g_return_val_if_fail(item != NULL, FALSE);
3622 g_return_val_if_fail(item->item.folder != NULL, FALSE);
3623 g_return_val_if_fail(FOLDER_CLASS(item->item.folder) == &imap_class, FALSE);
3625 if (item->item.path == NULL)
3628 debug_print("getting session...\n");
3629 session = imap_session_get(folder);
3630 g_return_val_if_fail(session != NULL, FALSE);
3632 selected_folder = (session->mbox != NULL) &&
3633 (!strcmp(session->mbox, item->item.path));
3634 if (selected_folder && time(NULL) - item->use_cache < 2) {
3635 ok = imap_cmd_noop(session);
3636 if (ok != IMAP_SUCCESS) {
3637 debug_print("disconnected!\n");
3638 session = imap_reconnect_if_possible(folder, session);
3639 if (session == NULL)
3643 if (session->folder_content_changed
3644 || session->exists != item->item.total_msgs) {
3649 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path, IMAP_FOLDER_ITEM(item),
3650 &exists, &uid_next, &uid_val, &unseen, FALSE);
3651 if (ok != IMAP_SUCCESS) {
3656 item->use_cache = time(NULL);
3657 item->c_messages = exists;
3658 item->c_uid_next = uid_next;
3659 item->c_uid_validity = uid_val;
3660 item->c_unseen = unseen;
3661 item->item.last_num = uid_next - 1;
3662 debug_print("uidnext %d, item->uid_next %d, exists %d, item->item.total_msgs %d\n",
3663 uid_next, item->uid_next, exists, item->item.total_msgs);
3664 if ((uid_next != item->uid_next) || (exists != item->item.total_msgs)
3665 || unseen != item->item.unread_msgs || uid_val != item->item.mtime) {
3674 void imap_change_flags(Folder *folder, FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
3676 IMAPSession *session;
3677 IMAPFlags flags_set = 0, flags_unset = 0;
3678 gint ok = IMAP_SUCCESS;
3679 MsgNumberList numlist;
3680 hashtable_data *ht_data = NULL;
3682 g_return_if_fail(folder != NULL);
3683 g_return_if_fail(folder->klass == &imap_class);
3684 g_return_if_fail(item != NULL);
3685 g_return_if_fail(item->folder == folder);
3686 g_return_if_fail(msginfo != NULL);
3687 g_return_if_fail(msginfo->folder == item);
3689 if (!MSG_IS_MARKED(msginfo->flags) && (newflags & MSG_MARKED))
3690 flags_set |= IMAP_FLAG_FLAGGED;
3691 if ( MSG_IS_MARKED(msginfo->flags) && !(newflags & MSG_MARKED))
3692 flags_unset |= IMAP_FLAG_FLAGGED;
3694 if (!MSG_IS_UNREAD(msginfo->flags) && (newflags & MSG_UNREAD))
3695 flags_unset |= IMAP_FLAG_SEEN;
3696 if ( MSG_IS_UNREAD(msginfo->flags) && !(newflags & MSG_UNREAD))
3697 flags_set |= IMAP_FLAG_SEEN;
3699 if (!MSG_IS_REPLIED(msginfo->flags) && (newflags & MSG_REPLIED))
3700 flags_set |= IMAP_FLAG_ANSWERED;
3701 if ( MSG_IS_REPLIED(msginfo->flags) && !(newflags & MSG_REPLIED))
3702 flags_unset |= IMAP_FLAG_ANSWERED;
3704 if (!MSG_IS_DELETED(msginfo->flags) && (newflags & MSG_DELETED))
3705 flags_set |= IMAP_FLAG_DELETED;
3706 if ( MSG_IS_DELETED(msginfo->flags) && !(newflags & MSG_DELETED))
3707 flags_unset |= IMAP_FLAG_DELETED;
3709 if (!flags_set && !flags_unset) {
3710 /* the changed flags were not translatable to IMAP-speak.
3711 * like MSG_POSTFILTERED, so just apply. */
3712 msginfo->flags.perm_flags = newflags;
3716 debug_print("getting session...\n");
3717 session = imap_session_get(folder);
3722 if ((ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
3723 NULL, NULL, NULL, NULL, FALSE)) != IMAP_SUCCESS) {
3727 numlist.next = NULL;
3728 numlist.data = GINT_TO_POINTER(msginfo->msgnum);
3730 if (IMAP_FOLDER_ITEM(item)->batching) {
3731 /* instead of performing an UID STORE command for each message change,
3732 * as a lot of them can change "together", we just fill in hashtables
3733 * and defer the treatment so that we're able to send only one
3736 debug_print("IMAP batch mode on, deferring flags change\n");
3738 ht_data = g_hash_table_lookup(IMAP_FOLDER_ITEM(item)->flags_set_table,
3739 GINT_TO_POINTER(flags_set));
3740 if (ht_data == NULL) {
3741 ht_data = g_new0(hashtable_data, 1);
3742 ht_data->session = session;
3743 ht_data->item = IMAP_FOLDER_ITEM(item);
3744 g_hash_table_insert(IMAP_FOLDER_ITEM(item)->flags_set_table,
3745 GINT_TO_POINTER(flags_set), ht_data);
3747 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
3748 ht_data->msglist = g_slist_prepend(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum));
3751 ht_data = g_hash_table_lookup(IMAP_FOLDER_ITEM(item)->flags_unset_table,
3752 GINT_TO_POINTER(flags_unset));
3753 if (ht_data == NULL) {
3754 ht_data = g_new0(hashtable_data, 1);
3755 ht_data->session = session;
3756 ht_data->item = IMAP_FOLDER_ITEM(item);
3757 g_hash_table_insert(IMAP_FOLDER_ITEM(item)->flags_unset_table,
3758 GINT_TO_POINTER(flags_unset), ht_data);
3760 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
3761 ht_data->msglist = g_slist_prepend(ht_data->msglist,
3762 GINT_TO_POINTER(msginfo->msgnum));
3765 debug_print("IMAP changing flags\n");
3767 ok = imap_set_message_flags(session, &numlist, flags_set, TRUE);
3768 if (ok != IMAP_SUCCESS) {
3775 ok = imap_set_message_flags(session, &numlist, flags_unset, FALSE);
3776 if (ok != IMAP_SUCCESS) {
3782 msginfo->flags.perm_flags = newflags;
3787 static gint imap_remove_msg(Folder *folder, FolderItem *item, gint uid)
3790 IMAPSession *session;
3792 MsgNumberList numlist;
3794 g_return_val_if_fail(folder != NULL, -1);
3795 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3796 g_return_val_if_fail(item != NULL, -1);
3798 debug_print("getting session...\n");
3799 session = imap_session_get(folder);
3800 if (!session) return -1;
3802 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3803 NULL, NULL, NULL, NULL, FALSE);
3804 if (ok != IMAP_SUCCESS) {
3808 numlist.next = NULL;
3809 numlist.data = GINT_TO_POINTER(uid);
3811 ok = imap_set_message_flags
3812 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
3813 &numlist, IMAP_FLAG_DELETED, TRUE);
3814 if (ok != IMAP_SUCCESS) {
3815 log_warning(_("can't set deleted flags: %d\n"), uid);
3820 if (!session->uidplus) {
3821 ok = imap_cmd_expunge(session);
3825 uidstr = g_strdup_printf("%u", uid);
3826 ok = imap_cmd_expunge(session);
3829 if (ok != IMAP_SUCCESS) {
3830 log_warning(_("can't expunge\n"));
3835 IMAP_FOLDER_ITEM(item)->uid_list = g_slist_remove(
3836 IMAP_FOLDER_ITEM(item)->uid_list, numlist.data);
3837 dir = folder_item_get_path(item);
3838 if (is_dir_exist(dir))
3839 remove_numbered_files(dir, uid, uid);
3842 return IMAP_SUCCESS;
3845 static gint compare_msginfo(gconstpointer a, gconstpointer b)
3847 return ((MsgInfo *)a)->msgnum - ((MsgInfo *)b)->msgnum;
3850 static guint gslist_find_next_num(MsgNumberList **list, guint num)
3854 g_return_val_if_fail(list != NULL, -1);
3856 for (elem = *list; elem != NULL; elem = g_slist_next(elem))
3857 if (GPOINTER_TO_INT(elem->data) >= num)
3860 return elem != NULL ? GPOINTER_TO_INT(elem->data) : (gint)-1;
3864 * NEW and DELETED flags are not syncronized
3865 * - The NEW/RECENT flags in IMAP folders can not really be directly
3866 * modified by Sylpheed
3867 * - The DELETE/DELETED flag in IMAP and Sylpheed don't have the same
3868 * meaning, in IMAP it always removes the messages from the FolderItem
3869 * in Sylpheed it can mean to move the message to trash
3872 typedef struct _get_flags_data {
3875 MsgInfoList *msginfo_list;
3876 GRelation *msgflags;
3877 gboolean full_search;
3881 static /*gint*/ void *imap_get_flags_thread(void *data)
3883 get_flags_data *stuff = (get_flags_data *)data;
3884 Folder *folder = stuff->folder;
3885 FolderItem *item = stuff->item;
3886 MsgInfoList *msginfo_list = stuff->msginfo_list;
3887 GRelation *msgflags = stuff->msgflags;
3888 gboolean full_search = stuff->full_search;
3889 IMAPSession *session;
3890 GSList *sorted_list = NULL;
3891 GSList *unseen = NULL, *answered = NULL, *flagged = NULL, *deleted = NULL;
3892 GSList *p_unseen, *p_answered, *p_flagged, *p_deleted;
3894 GSList *seq_list, *cur;
3895 gboolean reverse_seen = FALSE;
3898 gint exists_cnt, unseen_cnt;
3899 gboolean selected_folder;
3901 if (folder == NULL || item == NULL) {
3903 return GINT_TO_POINTER(-1);
3906 debug_print("getting session...\n");
3907 session = imap_session_get(folder);
3908 if (session == NULL) {
3910 return GINT_TO_POINTER(-1);
3913 selected_folder = (session->mbox != NULL) &&
3914 (!strcmp(session->mbox, item->path));
3916 if (!selected_folder) {
3917 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3918 &exists_cnt, NULL, &unseen_cnt, NULL, TRUE);
3919 if (ok != IMAP_SUCCESS) {
3922 return GINT_TO_POINTER(-1);
3925 if (unseen_cnt > exists_cnt / 2)
3926 reverse_seen = TRUE;
3929 if (item->unread_msgs > item->total_msgs / 2)
3930 reverse_seen = TRUE;
3933 cmd_buf = g_string_new(NULL);
3935 sorted_list = g_slist_sort(g_slist_copy(msginfo_list), compare_msginfo);
3937 seq_list = imap_get_lep_set_from_msglist(msginfo_list);
3939 struct mailimap_set * set;
3940 set = mailimap_set_new_interval(1, 0);
3941 seq_list = g_slist_append(NULL, set);
3944 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
3945 struct mailimap_set * imapset;
3946 clist * lep_uidlist;
3949 imapset = cur->data;
3951 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SEEN,
3952 full_search ? NULL:imapset, &lep_uidlist);
3955 r = imap_threaded_search(folder,
3956 IMAP_SEARCH_TYPE_UNSEEN,
3957 full_search ? NULL:imapset, &lep_uidlist);
3959 if (r == MAILIMAP_NO_ERROR) {
3962 uidlist = imap_uid_list_from_lep(lep_uidlist);
3963 mailimap_search_result_free(lep_uidlist);
3965 unseen = g_slist_concat(unseen, uidlist);
3968 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_FLAGGED,
3969 full_search ? NULL:imapset, &lep_uidlist);
3970 if (r == MAILIMAP_NO_ERROR) {
3973 uidlist = imap_uid_list_from_lep(lep_uidlist);
3974 mailimap_search_result_free(lep_uidlist);
3976 flagged = g_slist_concat(flagged, uidlist);
3979 if (item->opened || item->processing_pending || item == folder->inbox) {
3980 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_ANSWERED,
3981 full_search ? NULL:imapset, &lep_uidlist);
3982 if (r == MAILIMAP_NO_ERROR) {
3985 uidlist = imap_uid_list_from_lep(lep_uidlist);
3986 mailimap_search_result_free(lep_uidlist);
3988 answered = g_slist_concat(answered, uidlist);
3991 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_DELETED,
3992 full_search ? NULL:imapset, &lep_uidlist);
3993 if (r == MAILIMAP_NO_ERROR) {
3996 uidlist = imap_uid_list_from_lep(lep_uidlist);
3997 mailimap_search_result_free(lep_uidlist);
3999 deleted = g_slist_concat(deleted, uidlist);
4005 p_answered = answered;
4006 p_flagged = flagged;
4007 p_deleted = deleted;
4009 for (elem = sorted_list; elem != NULL; elem = g_slist_next(elem)) {
4014 msginfo = (MsgInfo *) elem->data;
4015 flags = msginfo->flags.perm_flags;
4016 wasnew = (flags & MSG_NEW);
4017 if (item->opened || item->processing_pending || item == folder->inbox) {
4018 flags &= ~((reverse_seen ? 0 : MSG_UNREAD | MSG_NEW) | MSG_REPLIED | MSG_MARKED);
4020 flags &= ~((reverse_seen ? 0 : MSG_UNREAD | MSG_NEW | MSG_MARKED));
4023 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
4024 if (gslist_find_next_num(&p_unseen, msginfo->msgnum) == msginfo->msgnum) {
4025 if (!reverse_seen) {
4026 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
4028 flags &= ~(MSG_UNREAD | MSG_NEW);
4032 if (gslist_find_next_num(&p_flagged, msginfo->msgnum) == msginfo->msgnum)
4033 flags |= MSG_MARKED;
4035 flags &= ~MSG_MARKED;
4037 if (item->opened || item->processing_pending || item == folder->inbox) {
4038 if (gslist_find_next_num(&p_answered, msginfo->msgnum) == msginfo->msgnum)
4039 flags |= MSG_REPLIED;
4041 flags &= ~MSG_REPLIED;
4042 if (gslist_find_next_num(&p_deleted, msginfo->msgnum) == msginfo->msgnum)
4043 flags |= MSG_DELETED;
4045 flags &= ~MSG_DELETED;
4047 g_relation_insert(msgflags, msginfo, GINT_TO_POINTER(flags));
4050 imap_lep_set_free(seq_list);
4051 g_slist_free(flagged);
4052 g_slist_free(deleted);
4053 g_slist_free(answered);
4054 g_slist_free(unseen);
4055 g_slist_free(sorted_list);
4056 g_string_free(cmd_buf, TRUE);
4060 return GINT_TO_POINTER(0);
4063 static gint imap_get_flags(Folder *folder, FolderItem *item,
4064 MsgInfoList *msginfo_list, GRelation *msgflags)
4067 get_flags_data *data = g_new0(get_flags_data, 1);
4069 data->folder = folder;
4071 data->msginfo_list = msginfo_list;
4072 data->msgflags = msgflags;
4073 data->full_search = FALSE;
4075 GSList *tmp = NULL, *cur;
4077 if (prefs_common.work_offline &&
4078 !inc_offline_should_override(
4079 _("Claws Mail needs network access in order "
4080 "to access the IMAP server."))) {
4085 tmp = folder_item_get_msg_list(item);
4087 if (g_slist_length(tmp) <= g_slist_length(msginfo_list))
4088 data->full_search = TRUE;
4090 for (cur = tmp; cur; cur = cur->next)
4091 procmsg_msginfo_free((MsgInfo *)cur->data);
4095 result = GPOINTER_TO_INT(imap_get_flags_thread(data));
4102 static gboolean process_flags(gpointer key, gpointer value, gpointer user_data)
4104 gboolean flags_set = GPOINTER_TO_INT(user_data);
4105 gint flags_value = GPOINTER_TO_INT(key);
4106 hashtable_data *data = (hashtable_data *)value;
4107 IMAPFolderItem *_item = data->item;
4108 FolderItem *item = (FolderItem *)_item;
4109 gint ok = IMAP_ERROR;
4110 IMAPSession *session = NULL;
4112 debug_print("getting session...\n");
4113 session = imap_session_get(item->folder);
4115 data->msglist = g_slist_reverse(data->msglist);
4117 debug_print("IMAP %ssetting flags to %d for %d messages\n",
4120 g_slist_length(data->msglist));
4123 ok = imap_select(session, IMAP_FOLDER(item->folder), item->path,
4124 NULL, NULL, NULL, NULL, FALSE);
4126 if (ok == IMAP_SUCCESS) {
4127 imap_set_message_flags(data->session, data->msglist, flags_value, flags_set);
4129 g_warning("can't select mailbox %s\n", item->path);
4133 g_slist_free(data->msglist);
4138 static void process_hashtable(IMAPFolderItem *item)
4140 if (item->flags_set_table) {
4141 g_hash_table_foreach_remove(item->flags_set_table, process_flags, GINT_TO_POINTER(TRUE));
4142 g_hash_table_destroy(item->flags_set_table);
4143 item->flags_set_table = NULL;
4145 if (item->flags_unset_table) {
4146 g_hash_table_foreach_remove(item->flags_unset_table, process_flags, GINT_TO_POINTER(FALSE));
4147 g_hash_table_destroy(item->flags_unset_table);
4148 item->flags_unset_table = NULL;
4152 static void imap_set_batch (Folder *folder, FolderItem *_item, gboolean batch)
4154 IMAPFolderItem *item = (IMAPFolderItem *)_item;
4156 g_return_if_fail(item != NULL);
4158 if (item->batching == batch)
4162 item->batching = TRUE;
4163 debug_print("IMAP switching to batch mode\n");
4164 if (!item->flags_set_table) {
4165 item->flags_set_table = g_hash_table_new(NULL, g_direct_equal);
4167 if (!item->flags_unset_table) {
4168 item->flags_unset_table = g_hash_table_new(NULL, g_direct_equal);
4171 debug_print("IMAP switching away from batch mode\n");
4173 process_hashtable(item);
4174 item->batching = FALSE;
4180 /* data types conversion libetpan <-> claws */
4184 #define ETPAN_IMAP_MB_MARKED 1
4185 #define ETPAN_IMAP_MB_UNMARKED 2
4186 #define ETPAN_IMAP_MB_NOSELECT 4
4187 #define ETPAN_IMAP_MB_NOINFERIORS 8
4189 static int imap_flags_to_flags(struct mailimap_mbx_list_flags * imap_flags)
4195 if (imap_flags->mbf_type == MAILIMAP_MBX_LIST_FLAGS_SFLAG) {
4196 switch (imap_flags->mbf_sflag) {
4197 case MAILIMAP_MBX_LIST_SFLAG_MARKED:
4198 flags |= ETPAN_IMAP_MB_MARKED;
4200 case MAILIMAP_MBX_LIST_SFLAG_NOSELECT:
4201 flags |= ETPAN_IMAP_MB_NOSELECT;
4203 case MAILIMAP_MBX_LIST_SFLAG_UNMARKED:
4204 flags |= ETPAN_IMAP_MB_UNMARKED;
4209 for(cur = clist_begin(imap_flags->mbf_oflags) ; cur != NULL ;
4210 cur = clist_next(cur)) {
4211 struct mailimap_mbx_list_oflag * oflag;
4213 oflag = clist_content(cur);
4215 switch (oflag->of_type) {
4216 case MAILIMAP_MBX_LIST_OFLAG_NOINFERIORS:
4217 flags |= ETPAN_IMAP_MB_NOINFERIORS;
4225 static GSList * imap_list_from_lep(IMAPFolder * folder,
4226 clist * list, const gchar * real_path, gboolean all)
4229 GSList * item_list = NULL, *llast = NULL;
4231 for(iter = clist_begin(list) ; iter != NULL ;
4232 iter = clist_next(iter)) {
4233 struct mailimap_mailbox_list * mb;
4241 FolderItem *new_item;
4243 mb = clist_content(iter);
4249 if (mb->mb_flag != NULL)
4250 flags = imap_flags_to_flags(mb->mb_flag);
4252 delimiter = mb->mb_delimiter;
4255 dup_name = strdup(name);
4256 if (delimiter != '\0')
4257 subst_char(dup_name, delimiter, '/');
4259 base = g_path_get_basename(dup_name);
4260 if (base[0] == '.') {
4265 if (!all && path_cmp(name, real_path) == 0) {
4271 if (!all && dup_name[strlen(dup_name)-1] == '/') {
4272 dup_name[strlen(dup_name)-1] = '\0';
4275 loc_name = imap_modified_utf7_to_utf8(base);
4276 loc_path = imap_modified_utf7_to_utf8(dup_name);
4278 new_item = folder_item_new(FOLDER(folder), loc_name, loc_path);
4279 if ((flags & ETPAN_IMAP_MB_NOINFERIORS) != 0)
4280 new_item->no_sub = TRUE;
4281 if (strcmp(dup_name, "INBOX") != 0 &&
4282 ((flags & ETPAN_IMAP_MB_NOSELECT) != 0))
4283 new_item->no_select = TRUE;
4285 if (item_list == NULL)
4286 llast = item_list = g_slist_append(item_list, new_item);
4288 llast = g_slist_append(llast, new_item);
4289 llast = llast->next;
4291 debug_print("folder '%s' found.\n", loc_path);
4302 static GSList * imap_get_lep_set_from_numlist(MsgNumberList *numlist)
4304 GSList *sorted_list, *cur;
4305 guint first, last, next;
4306 GSList *ret_list = NULL, *llast = NULL;
4308 struct mailimap_set * current_set;
4309 unsigned int item_count;
4311 if (numlist == NULL)
4315 current_set = mailimap_set_new_empty();
4317 sorted_list = g_slist_copy(numlist);
4318 sorted_list = g_slist_sort(sorted_list, g_int_compare);
4320 first = GPOINTER_TO_INT(sorted_list->data);
4323 for (cur = sorted_list; cur != NULL; cur = g_slist_next(cur)) {
4324 if (GPOINTER_TO_INT(cur->data) == 0)
4329 last = GPOINTER_TO_INT(cur->data);
4331 next = GPOINTER_TO_INT(cur->next->data);
4335 if (last + 1 != next || next == 0) {
4337 struct mailimap_set_item * item;
4338 item = mailimap_set_item_new(first, last);
4339 mailimap_set_add(current_set, item);
4344 if (count >= IMAP_SET_MAX_COUNT) {
4345 if (ret_list == NULL)
4346 llast = ret_list = g_slist_append(ret_list,
4349 llast = g_slist_append(llast, current_set);
4350 llast = llast->next;
4352 current_set = mailimap_set_new_empty();
4359 if (clist_count(current_set->set_list) > 0) {
4360 ret_list = g_slist_append(ret_list,
4364 g_slist_free(sorted_list);
4369 static GSList * imap_get_lep_set_from_msglist(MsgInfoList *msglist)
4371 MsgNumberList *numlist = NULL;
4375 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
4376 MsgInfo *msginfo = (MsgInfo *) cur->data;
4378 numlist = g_slist_prepend(numlist, GINT_TO_POINTER(msginfo->msgnum));
4380 numlist = g_slist_reverse(numlist);
4381 seq_list = imap_get_lep_set_from_numlist(numlist);
4382 g_slist_free(numlist);
4387 static GSList * imap_uid_list_from_lep(clist * list)
4394 for(iter = clist_begin(list) ; iter != NULL ;
4395 iter = clist_next(iter)) {
4398 puid = clist_content(iter);
4399 result = g_slist_prepend(result, GINT_TO_POINTER(* puid));
4402 result = g_slist_reverse(result);
4406 static GSList * imap_uid_list_from_lep_tab(carray * list)
4413 for(i = 0 ; i < carray_count(list) ; i ++) {
4416 puid = carray_get(list, i);
4417 result = g_slist_prepend(result, GINT_TO_POINTER(* puid));
4419 result = g_slist_reverse(result);
4423 static MsgInfo *imap_envelope_from_lep(struct imap_fetch_env_info * info,
4426 MsgInfo *msginfo = NULL;
4429 MsgFlags flags = {0, 0};
4431 if (info->headers == NULL)
4434 MSG_SET_TMP_FLAGS(flags, MSG_IMAP);
4435 if (folder_has_parent_of_type(item, F_QUEUE)) {
4436 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
4437 } else if (folder_has_parent_of_type(item, F_DRAFT)) {
4438 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
4440 flags.perm_flags = info->flags;
4444 msginfo = procheader_parse_str(info->headers, flags, FALSE, FALSE);
4447 msginfo->msgnum = uid;
4448 msginfo->size = size;
4454 static void imap_lep_set_free(GSList *seq_list)
4458 for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) {
4459 struct mailimap_set * imapset;
4461 imapset = cur->data;
4462 mailimap_set_free(imapset);
4464 g_slist_free(seq_list);
4467 static struct mailimap_flag_list * imap_flag_to_lep(IMAPFlags flags)
4469 struct mailimap_flag_list * flag_list;
4471 flag_list = mailimap_flag_list_new_empty();
4473 if (IMAP_IS_SEEN(flags))
4474 mailimap_flag_list_add(flag_list,
4475 mailimap_flag_new_seen());
4476 if (IMAP_IS_ANSWERED(flags))
4477 mailimap_flag_list_add(flag_list,
4478 mailimap_flag_new_answered());
4479 if (IMAP_IS_FLAGGED(flags))
4480 mailimap_flag_list_add(flag_list,
4481 mailimap_flag_new_flagged());
4482 if (IMAP_IS_DELETED(flags))
4483 mailimap_flag_list_add(flag_list,
4484 mailimap_flag_new_deleted());
4485 if (IMAP_IS_DRAFT(flags))
4486 mailimap_flag_list_add(flag_list,
4487 mailimap_flag_new_draft());
4492 guint imap_folder_get_refcnt(Folder *folder)
4494 return ((IMAPFolder *)folder)->refcnt;
4497 void imap_folder_ref(Folder *folder)
4499 ((IMAPFolder *)folder)->refcnt++;
4502 void imap_disconnect_all(void)
4505 for (list = account_get_list(); list != NULL; list = list->next) {
4506 PrefsAccount *account = list->data;
4507 if (account->protocol == A_IMAP4) {
4508 RemoteFolder *folder = (RemoteFolder *)account->folder;
4509 if (folder && folder->session) {
4510 IMAPSession *session = (IMAPSession *)folder->session;
4511 imap_threaded_disconnect(FOLDER(folder));
4512 SESSION(session)->state = SESSION_DISCONNECTED;
4513 session_destroy(SESSION(session));
4514 folder->session = NULL;
4520 void imap_folder_unref(Folder *folder)
4522 if (((IMAPFolder *)folder)->refcnt > 0)
4523 ((IMAPFolder *)folder)->refcnt--;
4526 #else /* HAVE_LIBETPAN */
4528 static FolderClass imap_class;
4530 static XMLTag *imap_item_get_xml(Folder *folder, FolderItem *item);
4531 static void imap_item_set_xml(Folder *folder, FolderItem *item, XMLTag *tag);
4533 static Folder *imap_folder_new (const gchar *name,
4536 static gboolean missing_imap_warning = TRUE;
4537 if (missing_imap_warning) {
4538 missing_imap_warning = FALSE;
4540 _("You have one or more IMAP accounts "
4541 "defined. However this version of "
4542 "Claws Mail has been built without "
4543 "IMAP support; your IMAP account(s) are "
4545 "You probably need to "
4546 "install libetpan and recompile "
4551 static gint imap_create_tree (Folder *folder)
4555 static FolderItem *imap_create_folder (Folder *folder,
4561 static gint imap_rename_folder (Folder *folder,
4568 gchar imap_get_path_separator_for_item(FolderItem *item)
4573 FolderClass *imap_get_class(void)
4575 if (imap_class.idstr == NULL) {
4576 imap_class.type = F_IMAP;
4577 imap_class.idstr = "imap";
4578 imap_class.uistr = "IMAP4";
4580 imap_class.new_folder = imap_folder_new;
4581 imap_class.create_tree = imap_create_tree;
4582 imap_class.create_folder = imap_create_folder;
4583 imap_class.rename_folder = imap_rename_folder;
4585 imap_class.set_xml = folder_set_xml;
4586 imap_class.get_xml = folder_get_xml;
4587 imap_class.item_set_xml = imap_item_set_xml;
4588 imap_class.item_get_xml = imap_item_get_xml;
4589 /* nothing implemented */
4595 void imap_disconnect_all(void)
4599 gint imap_scan_tree_real(Folder *folder, gboolean subs_only)
4604 gint imap_subscribe(Folder *folder, FolderItem *item, gchar *rpath, gboolean sub)
4609 GList * imap_scan_subtree(Folder *folder, FolderItem *item, gboolean unsubs_only, gboolean recursive)
4615 void imap_synchronise(FolderItem *item)
4617 imap_gtk_synchronise(item);
4620 static void imap_item_set_xml(Folder *folder, FolderItem *item, XMLTag *tag)
4622 #ifdef HAVE_LIBETPAN
4625 folder_item_set_xml(folder, item, tag);
4627 #ifdef HAVE_LIBETPAN
4628 for (cur = tag->attr; cur != NULL; cur = g_list_next(cur)) {
4629 XMLAttr *attr = (XMLAttr *) cur->data;
4631 if (!attr || !attr->name || !attr->value) continue;
4632 if (!strcmp(attr->name, "uidnext"))
4633 IMAP_FOLDER_ITEM(item)->uid_next = atoi(attr->value);
4638 static XMLTag *imap_item_get_xml(Folder *folder, FolderItem *item)
4642 tag = folder_item_get_xml(folder, item);
4644 #ifdef HAVE_LIBETPAN
4645 xml_tag_add_attr(tag, xml_attr_new_int("uidnext",
4646 IMAP_FOLDER_ITEM(item)->uid_next));