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);
727 imap_reset_uid_lists(folder);
728 if (time(NULL) - rfolder->last_failure <= 2)
730 session = imap_session_new(folder, folder->account);
732 if(session == NULL) {
733 rfolder->last_failure = time(NULL);
737 /* Make sure session is authenticated */
738 if (!IMAP_SESSION(session)->authenticated)
739 imap_session_authenticate(IMAP_SESSION(session), folder->account);
741 if (!IMAP_SESSION(session)->authenticated) {
742 imap_threaded_disconnect(session->folder);
743 SESSION(session)->state = SESSION_DISCONNECTED;
744 session_destroy(SESSION(session));
745 rfolder->session = NULL;
746 rfolder->last_failure = time(NULL);
752 /* I think the point of this code is to avoid sending a
753 * keepalive if we've used the session recently and therefore
754 * think it's still alive. Unfortunately, most of the code
755 * does not yet check for errors on the socket, and so if the
756 * connection drops we don't notice until the timeout expires.
757 * A better solution than sending a NOOP every time would be
758 * for every command to be prepared to retry until it is
759 * successfully sent. -- mbp */
760 if (time(NULL) - SESSION(session)->last_access_time > SESSION_TIMEOUT_INTERVAL) {
761 /* verify that the session is still alive */
762 if (imap_cmd_noop(session) != IMAP_SUCCESS) {
763 debug_print("disconnected!\n");
764 session = imap_reconnect_if_possible(folder, session);
768 rfolder->session = SESSION(session);
770 return IMAP_SESSION(session);
773 static IMAPSession *imap_session_new(Folder * folder,
774 const PrefsAccount *account)
776 IMAPSession *session;
779 int authenticated = FALSE;
782 /* FIXME: IMAP over SSL only... */
785 port = account->set_imapport ? account->imapport
786 : account->ssl_imap == SSL_TUNNEL ? IMAPS_PORT : IMAP4_PORT;
787 ssl_type = account->ssl_imap;
789 if (account->ssl_imap != SSL_NONE) {
790 if (alertpanel_full(_("Insecure connection"),
791 _("This connection is configured to be secured "
792 "using SSL, but SSL is not available in this "
793 "build of Claws Mail. \n\n"
794 "Do you want to continue connecting to this "
795 "server? The communication would not be "
797 GTK_STOCK_CANCEL, _("Con_tinue connecting"),
798 NULL, FALSE, NULL, ALERT_WARNING,
799 G_ALERTDEFAULT) != G_ALERTALTERNATE)
802 port = account->set_imapport ? account->imapport
807 statusbar_print_all(_("Connecting to IMAP4 server: %s..."), folder->account->recv_server);
808 if (account->set_tunnelcmd) {
809 r = imap_threaded_connect_cmd(folder,
811 account->recv_server,
816 if (ssl_type == SSL_TUNNEL) {
817 r = imap_threaded_connect_ssl(folder,
818 account->recv_server,
824 r = imap_threaded_connect(folder,
825 account->recv_server,
831 if (r == MAILIMAP_NO_ERROR_AUTHENTICATED) {
832 authenticated = TRUE;
834 else if (r == MAILIMAP_NO_ERROR_NON_AUTHENTICATED) {
835 authenticated = FALSE;
838 #if (LIBETPAN_VERSION_MAJOR > 0 || LIBETPAN_VERSION_MINOR > 48)
840 if (r == MAILIMAP_ERROR_SSL)
841 log_error(_("SSL handshake failed\n"));
844 if(!prefs_common.no_recv_err_panel) {
845 alertpanel_error_log(_("Can't connect to IMAP4 server: %s:%d"),
846 account->recv_server, port);
848 log_error(_("Can't connect to IMAP4 server: %s:%d\n"),
849 account->recv_server, port);
855 session = g_new0(IMAPSession, 1);
856 session_init(SESSION(session));
857 SESSION(session)->type = SESSION_IMAP;
858 SESSION(session)->server = g_strdup(account->recv_server);
859 SESSION(session)->sock = NULL;
861 SESSION(session)->destroy = imap_session_destroy;
863 session->capability = NULL;
865 session->authenticated = authenticated;
866 session->mbox = NULL;
867 session->cmd_count = 0;
868 session->folder = folder;
869 IMAP_FOLDER(session->folder)->last_seen_separator = 0;
872 if (account->ssl_imap == SSL_STARTTLS) {
875 ok = imap_cmd_starttls(session);
876 if (ok != IMAP_SUCCESS) {
877 log_warning(_("Can't start TLS session.\n"));
878 session_destroy(SESSION(session));
882 imap_free_capabilities(session);
883 session->authenticated = FALSE;
884 session->uidplus = FALSE;
885 session->cmd_count = 1;
888 log_message("IMAP connection is %s-authenticated\n",
889 (session->authenticated) ? "pre" : "un");
894 static void imap_session_authenticate(IMAPSession *session,
895 const PrefsAccount *account)
897 gchar *pass, *acc_pass;
898 gboolean failed = FALSE;
900 g_return_if_fail(account->userid != NULL);
901 acc_pass = account->passwd;
904 if (!pass && account->imap_auth_type != IMAP_AUTH_ANON) {
906 tmp_pass = input_dialog_query_password(account->recv_server, account->userid);
909 Xstrdup_a(pass, tmp_pass, {g_free(tmp_pass); return;});
911 } else if (account->imap_auth_type == IMAP_AUTH_ANON) {
914 statusbar_print_all(_("Connecting to IMAP4 server %s...\n"),
915 account->recv_server);
916 if (imap_auth(session, account->userid, pass, account->imap_auth_type) != IMAP_SUCCESS) {
924 if (prefs_common.no_recv_err_panel) {
925 log_error(_("Couldn't login to IMAP server %s."), account->recv_server);
926 mainwindow_show_error();
928 alertpanel_error_log(_("Couldn't login to IMAP server %s."), account->recv_server);
935 session->authenticated = TRUE;
939 static void imap_session_destroy(Session *session)
941 if (session->state != SESSION_DISCONNECTED)
942 imap_threaded_disconnect(IMAP_SESSION(session)->folder);
944 imap_free_capabilities(IMAP_SESSION(session));
945 g_free(IMAP_SESSION(session)->mbox);
946 sock_close(session->sock);
947 session->sock = NULL;
950 static gchar *imap_fetch_msg(Folder *folder, FolderItem *item, gint uid)
952 return imap_fetch_msg_full(folder, item, uid, TRUE, TRUE);
955 static guint get_size_with_crs(MsgInfo *info)
964 fp = procmsg_open_message(info);
968 while (fgets(buf, sizeof (buf), fp) != NULL) {
970 if (!strstr(buf, "\r") && strstr(buf, "\n"))
978 static gchar *imap_fetch_msg_full(Folder *folder, FolderItem *item, gint uid,
979 gboolean headers, gboolean body)
981 gchar *path, *filename;
982 IMAPSession *session;
985 g_return_val_if_fail(folder != NULL, NULL);
986 g_return_val_if_fail(item != NULL, NULL);
991 path = folder_item_get_path(item);
992 if (!is_dir_exist(path))
994 filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(uid), NULL);
996 debug_print("trying to fetch cached %s\n", filename);
997 if (is_file_exist(filename)) {
998 /* see whether the local file represents the whole message
999 * or not. As the IMAP server reports size with \r chars,
1000 * we have to update the local file (UNIX \n only) size */
1001 MsgInfo *msginfo = imap_parse_msg(filename, item);
1002 MsgInfo *cached = msgcache_get_msg(item->cache,uid);
1003 guint have_size = get_size_with_crs(msginfo);
1006 debug_print("message %d has been already %scached (%d/%d).\n", uid,
1007 have_size >= cached->size ? "fully ":"",
1008 have_size, (int)cached->size);
1010 if (cached && (cached->size <= have_size || !body)) {
1011 procmsg_msginfo_free(cached);
1012 procmsg_msginfo_free(msginfo);
1013 file_strip_crs(filename);
1015 } else if (!cached && time(NULL) - get_file_mtime(filename) < 60) {
1016 debug_print("message not cached and file recent, considering file complete\n");
1017 procmsg_msginfo_free(msginfo);
1018 file_strip_crs(filename);
1021 procmsg_msginfo_free(cached);
1022 procmsg_msginfo_free(msginfo);
1026 debug_print("getting session...\n");
1027 session = imap_session_get(folder);
1034 debug_print("IMAP fetching messages\n");
1035 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
1036 NULL, NULL, NULL, NULL, FALSE);
1037 if (ok != IMAP_SUCCESS) {
1038 g_warning("can't select mailbox %s\n", item->path);
1044 debug_print("getting message %d...\n", uid);
1045 ok = imap_cmd_fetch(session, (guint32)uid, filename, headers, body);
1047 if (ok != IMAP_SUCCESS) {
1048 g_warning("can't fetch message %d\n", uid);
1055 file_strip_crs(filename);
1059 static gint imap_add_msg(Folder *folder, FolderItem *dest,
1060 const gchar *file, MsgFlags *flags)
1064 MsgFileInfo fileinfo;
1066 g_return_val_if_fail(file != NULL, -1);
1068 fileinfo.msginfo = NULL;
1069 fileinfo.file = (gchar *)file;
1070 fileinfo.flags = flags;
1071 file_list.data = &fileinfo;
1072 file_list.next = NULL;
1074 ret = imap_add_msgs(folder, dest, &file_list, NULL);
1078 static gint imap_add_msgs(Folder *folder, FolderItem *dest, GSList *file_list,
1079 GRelation *relation)
1082 IMAPSession *session;
1083 guint32 last_uid = 0;
1085 MsgFileInfo *fileinfo;
1087 gint curnum = 0, total = 0;
1090 g_return_val_if_fail(folder != NULL, -1);
1091 g_return_val_if_fail(dest != NULL, -1);
1092 g_return_val_if_fail(file_list != NULL, -1);
1094 debug_print("getting session...\n");
1095 session = imap_session_get(folder);
1099 destdir = imap_get_real_path(session, IMAP_FOLDER(folder), dest->path);
1101 statusbar_print_all(_("Adding messages..."));
1102 total = g_slist_length(file_list);
1103 for (cur = file_list; cur != NULL; cur = cur->next) {
1104 IMAPFlags iflags = 0;
1105 guint32 new_uid = 0;
1106 gchar *real_file = NULL;
1107 fileinfo = (MsgFileInfo *)cur->data;
1109 statusbar_progress_all(curnum, total, total < 10 ? 1:10);
1112 if (fileinfo->flags) {
1113 if (MSG_IS_MARKED(*fileinfo->flags))
1114 iflags |= IMAP_FLAG_FLAGGED;
1115 if (MSG_IS_REPLIED(*fileinfo->flags))
1116 iflags |= IMAP_FLAG_ANSWERED;
1117 if (!MSG_IS_UNREAD(*fileinfo->flags))
1118 iflags |= IMAP_FLAG_SEEN;
1121 if (real_file == NULL)
1122 real_file = g_strdup(fileinfo->file);
1124 if (folder_has_parent_of_type(dest, F_QUEUE) ||
1125 folder_has_parent_of_type(dest, F_OUTBOX) ||
1126 folder_has_parent_of_type(dest, F_DRAFT) ||
1127 folder_has_parent_of_type(dest, F_TRASH))
1128 iflags |= IMAP_FLAG_SEEN;
1130 ok = imap_cmd_append(session, destdir, real_file, iflags,
1133 if (ok != IMAP_SUCCESS) {
1134 g_warning("can't append message %s\n", real_file);
1138 statusbar_progress_all(0,0,0);
1139 statusbar_pop_all();
1142 debug_print("appended new message as %d\n", new_uid);
1143 /* put the local file in the imapcache, so that we don't
1144 * have to fetch it back later. */
1146 gchar *cache_path = folder_item_get_path(dest);
1147 if (!is_dir_exist(cache_path))
1148 make_dir_hier(cache_path);
1149 if (is_dir_exist(cache_path)) {
1150 gchar *cache_file = g_strconcat(
1151 cache_path, G_DIR_SEPARATOR_S,
1152 itos(new_uid), NULL);
1153 copy_file(real_file, cache_file, TRUE);
1154 debug_print("copied to cache: %s\n", cache_file);
1161 if (relation != NULL)
1162 g_relation_insert(relation, fileinfo->msginfo != NULL ?
1163 (gpointer) fileinfo->msginfo : (gpointer) fileinfo,
1164 GINT_TO_POINTER(dest->last_num + 1));
1166 new_uid = dest->last_num+1;
1168 if (last_uid < new_uid) {
1174 statusbar_progress_all(0,0,0);
1175 statusbar_pop_all();
1177 imap_cmd_expunge(session);
1185 static gint imap_do_copy_msgs(Folder *folder, FolderItem *dest,
1186 MsgInfoList *msglist, GRelation *relation)
1190 GSList *seq_list, *cur;
1192 IMAPSession *session;
1193 gint ok = IMAP_SUCCESS;
1194 GRelation *uid_mapping;
1196 gboolean single = FALSE;
1198 g_return_val_if_fail(folder != NULL, -1);
1199 g_return_val_if_fail(dest != NULL, -1);
1200 g_return_val_if_fail(msglist != NULL, -1);
1202 debug_print("getting session...\n");
1203 session = imap_session_get(folder);
1209 msginfo = (MsgInfo *)msglist->data;
1210 if (msglist->next == NULL)
1212 src = msginfo->folder;
1214 g_warning("the src folder is identical to the dest.\n");
1219 if (src->folder != dest->folder) {
1220 GSList *infolist = NULL, *cur;
1222 for (cur = msglist; cur; cur = cur->next) {
1223 msginfo = (MsgInfo *)cur->data;
1224 MsgFileInfo *fileinfo = g_new0(MsgFileInfo, 1);
1225 fileinfo->file = procmsg_get_message_file(msginfo);
1226 fileinfo->flags = &(msginfo->flags);
1227 infolist = g_slist_prepend(infolist, fileinfo);
1229 infolist = g_slist_reverse(infolist);
1231 res = folder_item_add_msgs(dest, infolist, FALSE);
1232 for (cur = infolist; cur; cur = cur->next) {
1233 MsgFileInfo *info = (MsgFileInfo *)cur->data;
1237 g_slist_free(infolist);
1241 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
1242 NULL, NULL, NULL, NULL, FALSE);
1243 if (ok != IMAP_SUCCESS) {
1248 destdir = imap_get_real_path(session, IMAP_FOLDER(folder), dest->path);
1249 seq_list = imap_get_lep_set_from_msglist(msglist);
1250 uid_mapping = g_relation_new(2);
1251 g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
1253 statusbar_print_all(_("Copying messages..."));
1254 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
1255 struct mailimap_set * seq_set;
1256 struct mailimap_set * source = NULL;
1257 struct mailimap_set * dest = NULL;
1258 seq_set = cur->data;
1260 debug_print("Copying messages from %s to %s ...\n",
1261 src->path, destdir);
1263 ok = imap_cmd_copy(session, seq_set, destdir, uid_mapping,
1266 if (ok == IMAP_SUCCESS) {
1267 if (single && relation && source && dest) {
1268 clistiter *l = clist_begin(source->set_list);
1269 struct mailimap_set_item *i = (struct mailimap_set_item *)clist_content(l);
1270 int snum = i->set_first;
1272 l = clist_begin(dest->set_list);
1273 i = (struct mailimap_set_item *)clist_content(l);
1274 dnum = i->set_first;
1275 g_relation_insert(uid_mapping, GINT_TO_POINTER(snum),
1276 GINT_TO_POINTER(dnum));
1282 mailimap_set_free(source);
1284 mailimap_set_free(dest);
1286 if (ok != IMAP_SUCCESS) {
1287 g_relation_destroy(uid_mapping);
1288 imap_lep_set_free(seq_list);
1290 statusbar_pop_all();
1295 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
1296 MsgInfo *msginfo = (MsgInfo *)cur->data;
1299 tuples = g_relation_select(uid_mapping,
1300 GINT_TO_POINTER(msginfo->msgnum),
1302 if (tuples->len > 0) {
1303 gint num = GPOINTER_TO_INT(g_tuples_index(tuples, 0, 1));
1304 g_relation_insert(relation, msginfo,
1305 GINT_TO_POINTER(num));
1308 debug_print("copied new message as %d\n", num);
1309 /* put the local file in the imapcache, so that we don't
1310 * have to fetch it back later. */
1312 gchar *cache_path = folder_item_get_path(msginfo->folder);
1313 gchar *real_file = g_strconcat(
1314 cache_path, G_DIR_SEPARATOR_S,
1315 itos(msginfo->msgnum), NULL);
1316 gchar *cache_file = NULL;
1318 cache_path = folder_item_get_path(dest);
1319 cache_file = g_strconcat(
1320 cache_path, G_DIR_SEPARATOR_S,
1322 if (!is_dir_exist(cache_path))
1323 make_dir_hier(cache_path);
1324 if (is_file_exist(real_file) && is_dir_exist(cache_path)) {
1325 copy_file(real_file, cache_file, TRUE);
1326 debug_print("copied to cache: %s\n", cache_file);
1333 g_relation_insert(relation, msginfo,
1334 GINT_TO_POINTER(0));
1335 g_tuples_destroy(tuples);
1337 statusbar_pop_all();
1339 g_relation_destroy(uid_mapping);
1340 imap_lep_set_free(seq_list);
1344 IMAP_FOLDER_ITEM(dest)->lastuid = 0;
1345 IMAP_FOLDER_ITEM(dest)->uid_next = 0;
1346 g_slist_free(IMAP_FOLDER_ITEM(dest)->uid_list);
1347 IMAP_FOLDER_ITEM(dest)->uid_list = NULL;
1350 if (ok == IMAP_SUCCESS)
1356 static gint imap_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1360 g_return_val_if_fail(msginfo != NULL, -1);
1362 msglist.data = msginfo;
1363 msglist.next = NULL;
1365 return imap_copy_msgs(folder, dest, &msglist, NULL);
1368 static gint imap_copy_msgs(Folder *folder, FolderItem *dest,
1369 MsgInfoList *msglist, GRelation *relation)
1374 g_return_val_if_fail(folder != NULL, -1);
1375 g_return_val_if_fail(dest != NULL, -1);
1376 g_return_val_if_fail(msglist != NULL, -1);
1378 msginfo = (MsgInfo *)msglist->data;
1379 g_return_val_if_fail(msginfo->folder != NULL, -1);
1381 ret = imap_do_copy_msgs(folder, dest, msglist, relation);
1386 static gint imap_do_remove_msgs(Folder *folder, FolderItem *dest,
1387 MsgInfoList *msglist, GRelation *relation)
1389 gchar *destdir, *dir;
1390 GSList *numlist = NULL, *cur;
1392 IMAPSession *session;
1393 gint ok = IMAP_SUCCESS;
1394 GRelation *uid_mapping;
1396 g_return_val_if_fail(folder != NULL, -1);
1397 g_return_val_if_fail(dest != NULL, -1);
1398 g_return_val_if_fail(msglist != NULL, -1);
1400 debug_print("getting session...\n");
1401 session = imap_session_get(folder);
1406 msginfo = (MsgInfo *)msglist->data;
1408 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
1409 NULL, NULL, NULL, NULL, FALSE);
1410 if (ok != IMAP_SUCCESS) {
1415 destdir = imap_get_real_path(session, IMAP_FOLDER(folder), dest->path);
1416 for (cur = msglist; cur; cur = cur->next) {
1417 msginfo = (MsgInfo *)cur->data;
1418 if (!MSG_IS_DELETED(msginfo->flags))
1419 numlist = g_slist_prepend(numlist, GINT_TO_POINTER(msginfo->msgnum));
1421 numlist = g_slist_reverse(numlist);
1423 uid_mapping = g_relation_new(2);
1424 g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
1426 ok = imap_set_message_flags
1427 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
1428 numlist, IMAP_FLAG_DELETED, TRUE);
1429 if (ok != IMAP_SUCCESS) {
1430 log_warning(_("can't set deleted flags\n"));
1434 ok = imap_cmd_expunge(session);
1435 if (ok != IMAP_SUCCESS) {
1436 log_warning(_("can't expunge\n"));
1441 dir = folder_item_get_path(msginfo->folder);
1442 if (is_dir_exist(dir)) {
1443 for (cur = msglist; cur; cur = cur->next) {
1444 msginfo = (MsgInfo *)cur->data;
1445 remove_numbered_files(dir, msginfo->msgnum, msginfo->msgnum);
1450 g_relation_destroy(uid_mapping);
1451 g_slist_free(numlist);
1455 if (ok == IMAP_SUCCESS)
1461 static gint imap_remove_msgs(Folder *folder, FolderItem *dest,
1462 MsgInfoList *msglist, GRelation *relation)
1466 g_return_val_if_fail(folder != NULL, -1);
1467 g_return_val_if_fail(dest != NULL, -1);
1468 if (msglist == NULL)
1471 msginfo = (MsgInfo *)msglist->data;
1472 g_return_val_if_fail(msginfo->folder != NULL, -1);
1474 return imap_do_remove_msgs(folder, dest, msglist, relation);
1477 static gint imap_remove_all_msg(Folder *folder, FolderItem *item)
1479 GSList *list = folder_item_get_msg_list(item);
1480 gint res = imap_remove_msgs(folder, item, list, NULL);
1481 procmsg_msg_list_free(list);
1485 static gboolean imap_is_msg_changed(Folder *folder, FolderItem *item,
1488 /* TODO: properly implement this method */
1492 static gint imap_close(Folder *folder, FolderItem *item)
1497 gint imap_scan_tree_real(Folder *folder, gboolean subs_only)
1499 FolderItem *item = NULL;
1500 IMAPSession *session;
1501 gchar *root_folder = NULL;
1503 g_return_val_if_fail(folder != NULL, -1);
1504 g_return_val_if_fail(folder->account != NULL, -1);
1506 debug_print("getting session...\n");
1507 session = imap_session_get(folder);
1509 if (!folder->node) {
1510 folder_tree_destroy(folder);
1511 item = folder_item_new(folder, folder->name, NULL);
1512 item->folder = folder;
1513 folder->node = item->node = g_node_new(item);
1518 if (folder->account->imap_dir && *folder->account->imap_dir) {
1523 Xstrdup_a(root_folder, folder->account->imap_dir, {unlock_session();return -1;});
1524 extract_quote(root_folder, '"');
1525 subst_char(root_folder,
1526 imap_get_path_separator(session, IMAP_FOLDER(folder),
1529 strtailchomp(root_folder, '/');
1530 real_path = imap_get_real_path
1531 (session, IMAP_FOLDER(folder), root_folder);
1532 debug_print("IMAP root directory: %s\n", real_path);
1534 /* check if root directory exist */
1536 r = imap_threaded_list(session->folder, "", real_path,
1538 if ((r != MAILIMAP_NO_ERROR) || (clist_count(lep_list) == 0)) {
1539 if (!folder->node) {
1540 item = folder_item_new(folder, folder->name, NULL);
1541 item->folder = folder;
1542 folder->node = item->node = g_node_new(item);
1547 mailimap_list_result_free(lep_list);
1553 item = FOLDER_ITEM(folder->node->data);
1555 if (item && !item->path && root_folder) {
1556 item->path = g_strdup(root_folder);
1559 if (!item || ((item->path || root_folder) &&
1560 strcmp2(item->path, root_folder) != 0)) {
1561 folder_tree_destroy(folder);
1562 item = folder_item_new(folder, folder->name, root_folder);
1563 item->folder = folder;
1564 folder->node = item->node = g_node_new(item);
1567 imap_scan_tree_recursive(session, FOLDER_ITEM(folder->node->data), subs_only);
1568 imap_create_missing_folders(folder);
1574 static gint imap_scan_tree(Folder *folder)
1576 gboolean subs_only = FALSE;
1577 if (folder->account) {
1578 debug_print(" scanning only subs %d\n", folder->account->imap_subsonly);
1579 subs_only = folder->account->imap_subsonly;
1581 return imap_scan_tree_real(folder, subs_only);
1584 static gint imap_scan_tree_recursive(IMAPSession *session, FolderItem *item, gboolean subs_only)
1587 IMAPFolder *imapfolder;
1588 FolderItem *new_item;
1589 GSList *item_list, *cur;
1592 gchar *wildcard_path;
1598 g_return_val_if_fail(item != NULL, -1);
1599 g_return_val_if_fail(item->folder != NULL, -1);
1600 g_return_val_if_fail(item->no_sub == FALSE, -1);
1602 folder = item->folder;
1603 imapfolder = IMAP_FOLDER(folder);
1605 separator = imap_get_path_separator(session, imapfolder, item->path);
1607 if (folder->ui_func)
1608 folder->ui_func(folder, item, folder->ui_func_data);
1611 wildcard[0] = separator;
1614 real_path = imap_get_real_path(session, imapfolder, item->path);
1618 real_path = g_strdup("");
1621 Xstrcat_a(wildcard_path, real_path, wildcard,
1622 {g_free(real_path); return IMAP_ERROR;});
1626 r = imap_threaded_lsub(folder, "", wildcard_path, &lep_list);
1628 r = imap_threaded_list(folder, "", wildcard_path, &lep_list);
1630 if (r != MAILIMAP_NO_ERROR) {
1634 item_list = imap_list_from_lep(imapfolder,
1635 lep_list, real_path, FALSE);
1636 mailimap_list_result_free(lep_list);
1641 node = item->node->children;
1642 while (node != NULL) {
1643 FolderItem *old_item = FOLDER_ITEM(node->data);
1644 GNode *next = node->next;
1647 for (cur = item_list; cur != NULL; cur = cur->next) {
1648 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1649 if (!strcmp2(old_item->path, cur_item->path)) {
1650 new_item = cur_item;
1655 if (old_item && old_item->path && !strcmp(old_item->path, "INBOX")) {
1656 debug_print("not removing INBOX\n");
1658 debug_print("folder '%s' not found. removing...\n",
1660 folder_item_remove(old_item);
1663 old_item->no_sub = new_item->no_sub;
1664 old_item->no_select = new_item->no_select;
1665 if (old_item->no_sub == TRUE && node->children) {
1666 debug_print("folder '%s' doesn't have "
1667 "subfolders. removing...\n",
1669 folder_item_remove_children(old_item);
1676 for (cur = item_list; cur != NULL; cur = cur->next) {
1677 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1680 for (node = item->node->children; node != NULL;
1681 node = node->next) {
1682 if (!strcmp2(FOLDER_ITEM(node->data)->path,
1684 new_item = FOLDER_ITEM(node->data);
1685 folder_item_destroy(cur_item);
1691 new_item = cur_item;
1692 debug_print("new folder '%s' found.\n", new_item->path);
1693 folder_item_append(item, new_item);
1696 if (!strcmp(new_item->path, "INBOX")) {
1697 new_item->stype = F_INBOX;
1698 folder->inbox = new_item;
1699 } else if (!folder_item_parent(item) || item->stype == F_INBOX) {
1702 base = g_path_get_basename(new_item->path);
1704 if (!folder->outbox && !g_ascii_strcasecmp(base, "Sent")) {
1705 new_item->stype = F_OUTBOX;
1706 folder->outbox = new_item;
1707 } else if (!folder->draft && !g_ascii_strcasecmp(base, "Drafts")) {
1708 new_item->stype = F_DRAFT;
1709 folder->draft = new_item;
1710 } else if (!folder->queue && !g_ascii_strcasecmp(base, "Queue")) {
1711 new_item->stype = F_QUEUE;
1712 folder->queue = new_item;
1713 } else if (!folder->trash && !g_ascii_strcasecmp(base, "Trash")) {
1714 new_item->stype = F_TRASH;
1715 folder->trash = new_item;
1720 if (new_item->no_sub == FALSE)
1721 imap_scan_tree_recursive(session, new_item, subs_only);
1724 g_slist_free(item_list);
1726 return IMAP_SUCCESS;
1729 GList *imap_scan_subtree(Folder *folder, FolderItem *item, gboolean unsubs_only, gboolean recursive)
1731 IMAPSession *session = imap_session_get(folder);
1733 gchar *wildcard_path;
1737 GSList *item_list = NULL, *cur;
1738 GList *child_list = NULL, *tmplist = NULL;
1739 GSList *sub_list = NULL;
1745 separator = imap_get_path_separator(session, IMAP_FOLDER(folder), item->path);
1748 wildcard[0] = separator;
1751 real_path = imap_get_real_path(session, IMAP_FOLDER(folder), item->path);
1755 real_path = g_strdup("");
1758 Xstrcat_a(wildcard_path, real_path, wildcard,
1759 {g_free(real_path); return NULL;});
1763 statusbar_print_all(_("Looking for unsubscribed folders in %s..."),
1764 item->path?item->path:item->name);
1766 statusbar_print_all(_("Looking for subfolders of %s..."),
1767 item->path?item->path:item->name);
1769 r = imap_threaded_list(folder, "", wildcard_path, &lep_list);
1771 statusbar_pop_all();
1774 item_list = imap_list_from_lep(IMAP_FOLDER(folder),
1775 lep_list, real_path, FALSE);
1776 mailimap_list_result_free(lep_list);
1778 for (cur = item_list; cur != NULL; cur = cur->next) {
1779 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1781 tmplist = imap_scan_subtree(folder, cur_item,
1782 unsubs_only, recursive);
1784 child_list = g_list_concat(child_list, tmplist);
1786 child_list = g_list_prepend(child_list,
1787 imap_get_real_path(session,
1788 IMAP_FOLDER(folder), cur_item->path));
1790 folder_item_destroy(cur_item);
1792 child_list = g_list_reverse(child_list);
1793 g_slist_free(item_list);
1796 r = imap_threaded_lsub(folder, "", wildcard_path, &lep_list);
1798 statusbar_pop_all();
1801 sub_list = imap_list_from_lep(IMAP_FOLDER(folder),
1802 lep_list, real_path, FALSE);
1803 mailimap_list_result_free(lep_list);
1805 for (cur = sub_list; cur != NULL; cur = cur->next) {
1806 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1807 GList *oldlitem = NULL;
1808 gchar *tmp = imap_get_real_path(session,
1809 IMAP_FOLDER(folder), cur_item->path);
1810 folder_item_destroy(cur_item);
1811 oldlitem = g_list_find_custom(
1812 child_list, tmp, (GCompareFunc)strcmp2);
1814 child_list = g_list_remove_link(child_list, oldlitem);
1815 g_free(oldlitem->data);
1816 g_list_free(oldlitem);
1822 statusbar_pop_all();
1827 static gint imap_create_tree(Folder *folder)
1829 g_return_val_if_fail(folder != NULL, -1);
1830 g_return_val_if_fail(folder->node != NULL, -1);
1831 g_return_val_if_fail(folder->node->data != NULL, -1);
1832 g_return_val_if_fail(folder->account != NULL, -1);
1834 imap_scan_tree(folder);
1835 imap_create_missing_folders(folder);
1840 static void imap_create_missing_folders(Folder *folder)
1842 g_return_if_fail(folder != NULL);
1845 folder->inbox = imap_create_special_folder
1846 (folder, F_INBOX, "INBOX");
1848 folder->trash = imap_create_special_folder
1849 (folder, F_TRASH, "Trash");
1851 folder->queue = imap_create_special_folder
1852 (folder, F_QUEUE, "Queue");
1853 if (!folder->outbox)
1854 folder->outbox = imap_create_special_folder
1855 (folder, F_OUTBOX, "Sent");
1857 folder->draft = imap_create_special_folder
1858 (folder, F_DRAFT, "Drafts");
1861 static FolderItem *imap_create_special_folder(Folder *folder,
1862 SpecialFolderItemType stype,
1866 FolderItem *new_item;
1868 g_return_val_if_fail(folder != NULL, NULL);
1869 g_return_val_if_fail(folder->node != NULL, NULL);
1870 g_return_val_if_fail(folder->node->data != NULL, NULL);
1871 g_return_val_if_fail(folder->account != NULL, NULL);
1872 g_return_val_if_fail(name != NULL, NULL);
1874 item = FOLDER_ITEM(folder->node->data);
1875 new_item = imap_create_folder(folder, item, name);
1878 g_warning("Can't create '%s'\n", name);
1879 if (!folder->inbox) return NULL;
1881 new_item = imap_create_folder(folder, folder->inbox, name);
1883 g_warning("Can't create '%s' under INBOX\n", name);
1885 new_item->stype = stype;
1887 new_item->stype = stype;
1892 static gchar *imap_folder_get_path(Folder *folder)
1896 g_return_val_if_fail(folder != NULL, NULL);
1897 g_return_val_if_fail(folder->account != NULL, NULL);
1899 folder_path = g_strconcat(get_imap_cache_dir(),
1901 folder->account->recv_server,
1903 folder->account->userid,
1909 static gchar *imap_item_get_path(Folder *folder, FolderItem *item)
1911 gchar *folder_path, *path;
1913 g_return_val_if_fail(folder != NULL, NULL);
1914 g_return_val_if_fail(item != NULL, NULL);
1915 folder_path = imap_folder_get_path(folder);
1917 g_return_val_if_fail(folder_path != NULL, NULL);
1918 if (folder_path[0] == G_DIR_SEPARATOR) {
1920 path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
1923 path = g_strdup(folder_path);
1926 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1927 folder_path, G_DIR_SEPARATOR_S,
1930 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1933 g_free(folder_path);
1938 static FolderItem *imap_create_folder(Folder *folder, FolderItem *parent,
1941 gchar *dirpath, *imap_path;
1942 IMAPSession *session;
1943 FolderItem *new_item;
1948 gboolean no_select = FALSE, no_sub = FALSE;
1949 gboolean exist = FALSE;
1951 g_return_val_if_fail(folder != NULL, NULL);
1952 g_return_val_if_fail(folder->account != NULL, NULL);
1953 g_return_val_if_fail(parent != NULL, NULL);
1954 g_return_val_if_fail(name != NULL, NULL);
1956 debug_print("getting session...\n");
1957 session = imap_session_get(folder);
1962 if (!folder_item_parent(parent) && strcmp(name, "INBOX") == 0) {
1963 dirpath = g_strdup(name);
1964 }else if (parent->path)
1965 dirpath = g_strconcat(parent->path, "/", name, NULL);
1966 else if ((p = strchr(name, '/')) != NULL && *(p + 1) != '\0')
1967 dirpath = g_strdup(name);
1968 else if (folder->account->imap_dir && *folder->account->imap_dir) {
1971 Xstrdup_a(imap_dir, folder->account->imap_dir, {unlock_session();return NULL;});
1972 strtailchomp(imap_dir, '/');
1973 dirpath = g_strconcat(imap_dir, "/", name, NULL);
1975 dirpath = g_strdup(name);
1979 /* keep trailing directory separator to create a folder that contains
1981 imap_path = imap_utf8_to_modified_utf7(dirpath);
1983 strtailchomp(dirpath, '/');
1984 Xstrdup_a(new_name, name, {
1989 separator = imap_get_path_separator(session, IMAP_FOLDER(folder), imap_path);
1990 imap_path_separator_subst(imap_path, separator);
1991 /* remove trailing / for display */
1992 strtailchomp(new_name, '/');
1994 if (strcmp(dirpath, "INBOX") != 0) {
1999 argbuf = g_ptr_array_new();
2000 r = imap_threaded_list(folder, "", imap_path, &lep_list);
2001 if (r != MAILIMAP_NO_ERROR) {
2002 log_warning(_("can't create mailbox: LIST failed\n"));
2005 ptr_array_free_strings(argbuf);
2006 g_ptr_array_free(argbuf, TRUE);
2011 if (clist_count(lep_list) > 0)
2013 mailimap_list_result_free(lep_list);
2016 ok = imap_cmd_create(session, imap_path);
2017 if (ok != IMAP_SUCCESS) {
2018 log_warning(_("can't create mailbox\n"));
2024 r = imap_threaded_list(folder, "", imap_path, &lep_list);
2025 if (r == MAILIMAP_NO_ERROR) {
2026 GSList *item_list = imap_list_from_lep(IMAP_FOLDER(folder),
2027 lep_list, dirpath, TRUE);
2029 FolderItem *cur_item = FOLDER_ITEM(item_list->data);
2030 no_select = cur_item->no_select;
2031 no_sub = cur_item->no_sub;
2032 g_slist_free(item_list);
2034 mailimap_list_result_free(lep_list);
2037 imap_threaded_subscribe(folder, imap_path, TRUE);
2041 /* just get flags */
2042 r = imap_threaded_list(folder, "", "INBOX", &lep_list);
2043 if (r == MAILIMAP_NO_ERROR) {
2044 GSList *item_list = imap_list_from_lep(IMAP_FOLDER(folder),
2045 lep_list, dirpath, TRUE);
2047 FolderItem *cur_item = FOLDER_ITEM(item_list->data);
2048 no_select = cur_item->no_select;
2049 no_sub = cur_item->no_sub;
2050 g_slist_free(item_list);
2052 mailimap_list_result_free(lep_list);
2056 new_item = folder_item_new(folder, new_name, dirpath);
2057 new_item->no_select = no_select;
2058 new_item->no_sub = no_sub;
2059 folder_item_append(parent, new_item);
2063 dirpath = folder_item_get_path(new_item);
2064 if (!is_dir_exist(dirpath))
2065 make_dir_hier(dirpath);
2070 /* folder existed, scan it */
2071 folder_item_scan_full(new_item, FALSE);
2077 static gint imap_rename_folder(Folder *folder, FolderItem *item,
2082 gchar *real_oldpath;
2083 gchar *real_newpath;
2085 gchar *old_cache_dir;
2086 gchar *new_cache_dir;
2087 IMAPSession *session;
2090 gint exists, recent, unseen;
2091 guint32 uid_validity;
2093 g_return_val_if_fail(folder != NULL, -1);
2094 g_return_val_if_fail(item != NULL, -1);
2095 g_return_val_if_fail(item->path != NULL, -1);
2096 g_return_val_if_fail(name != NULL, -1);
2098 debug_print("getting session...\n");
2099 session = imap_session_get(folder);
2104 if (strchr(name, imap_get_path_separator(session, IMAP_FOLDER(folder), item->path)) != NULL) {
2105 g_warning(_("New folder name must not contain the namespace "
2111 real_oldpath = imap_get_real_path(session, IMAP_FOLDER(folder), item->path);
2113 g_free(session->mbox);
2114 session->mbox = NULL;
2115 ok = imap_cmd_examine(session, "INBOX",
2116 &exists, &recent, &unseen, &uid_validity, FALSE);
2117 if (ok != IMAP_SUCCESS) {
2118 g_free(real_oldpath);
2123 separator = imap_get_path_separator(session, IMAP_FOLDER(folder), item->path);
2124 if (strchr(item->path, G_DIR_SEPARATOR)) {
2125 dirpath = g_path_get_dirname(item->path);
2126 newpath = g_strconcat(dirpath, G_DIR_SEPARATOR_S, name, NULL);
2129 newpath = g_strdup(name);
2131 real_newpath = imap_utf8_to_modified_utf7(newpath);
2132 imap_path_separator_subst(real_newpath, separator);
2134 ok = imap_cmd_rename(session, real_oldpath, real_newpath);
2135 if (ok != IMAP_SUCCESS) {
2136 log_warning(_("can't rename mailbox: %s to %s\n"),
2137 real_oldpath, real_newpath);
2138 g_free(real_oldpath);
2140 g_free(real_newpath);
2146 item->name = g_strdup(name);
2148 old_cache_dir = folder_item_get_path(item);
2150 paths[0] = g_strdup(item->path);
2152 g_node_traverse(item->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
2153 imap_rename_folder_func, paths);
2155 if (is_dir_exist(old_cache_dir)) {
2156 new_cache_dir = folder_item_get_path(item);
2157 if (rename(old_cache_dir, new_cache_dir) < 0) {
2158 FILE_OP_ERROR(old_cache_dir, "rename");
2160 g_free(new_cache_dir);
2163 g_free(old_cache_dir);
2166 g_free(real_oldpath);
2167 g_free(real_newpath);
2172 gint imap_subscribe(Folder *folder, FolderItem *item, gchar *rpath, gboolean sub)
2176 IMAPSession *session;
2177 debug_print("getting session...\n");
2179 session = imap_session_get(folder);
2183 if (item && item->path) {
2184 path = imap_get_real_path(session, IMAP_FOLDER(folder), item->path);
2187 if (!strcmp(path, "INBOX") && sub == FALSE)
2189 debug_print("%ssubscribing %s\n", sub?"":"un", path);
2190 r = imap_threaded_subscribe(folder, path, sub);
2193 r = imap_threaded_subscribe(folder, rpath, sub);
2199 static gint imap_remove_folder_real(Folder *folder, FolderItem *item)
2202 IMAPSession *session;
2206 g_return_val_if_fail(folder != NULL, -1);
2207 g_return_val_if_fail(item != NULL, -1);
2208 g_return_val_if_fail(item->path != NULL, -1);
2210 debug_print("getting session...\n");
2211 session = imap_session_get(folder);
2215 path = imap_get_real_path(session, IMAP_FOLDER(folder), item->path);
2217 imap_threaded_subscribe(folder, path, FALSE);
2218 ok = imap_cmd_delete(session, path);
2219 if (ok != IMAP_SUCCESS) {
2220 gchar *tmp = g_strdup_printf("%s%c", path,
2221 imap_get_path_separator(session, IMAP_FOLDER(folder), path));
2224 ok = imap_cmd_delete(session, path);
2227 if (ok != IMAP_SUCCESS) {
2228 log_warning(_("can't delete mailbox\n"));
2235 cache_dir = folder_item_get_path(item);
2236 if (is_dir_exist(cache_dir) && remove_dir_recursive(cache_dir) < 0)
2237 g_warning("can't remove directory '%s'\n", cache_dir);
2239 folder_item_remove(item);
2244 static gint imap_remove_folder(Folder *folder, FolderItem *item)
2248 g_return_val_if_fail(item != NULL, -1);
2249 g_return_val_if_fail(item->folder != NULL, -1);
2250 g_return_val_if_fail(item->node != NULL, -1);
2252 node = item->node->children;
2253 while (node != NULL) {
2255 if (imap_remove_folder(folder, FOLDER_ITEM(node->data)) < 0)
2259 debug_print("IMAP removing %s\n", item->path);
2261 if (imap_remove_all_msg(folder, item) < 0)
2263 return imap_remove_folder_real(folder, item);
2266 typedef struct _uncached_data {
2267 IMAPSession *session;
2269 MsgNumberList *numlist;
2275 static void *imap_get_uncached_messages_thread(void *data)
2277 uncached_data *stuff = (uncached_data *)data;
2278 IMAPSession *session = stuff->session;
2279 FolderItem *item = stuff->item;
2280 MsgNumberList *numlist = stuff->numlist;
2282 GSList *newlist = NULL;
2283 GSList *llast = NULL;
2284 GSList *seq_list, *cur;
2286 debug_print("uncached_messages\n");
2288 if (session == NULL || item == NULL || item->folder == NULL
2289 || FOLDER_CLASS(item->folder) != &imap_class) {
2294 seq_list = imap_get_lep_set_from_numlist(numlist);
2295 debug_print("get msgs info\n");
2296 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
2297 struct mailimap_set * imapset;
2303 imapset = cur->data;
2305 r = imap_threaded_fetch_env(session->folder,
2306 imapset, &env_list);
2307 if (r != MAILIMAP_NO_ERROR)
2310 session_set_access_time(SESSION(session));
2313 for(i = 0 ; i < carray_count(env_list) ; i ++) {
2314 struct imap_fetch_env_info * info;
2317 info = carray_get(env_list, i);
2318 msginfo = imap_envelope_from_lep(info, item);
2319 if (msginfo == NULL)
2321 msginfo->folder = item;
2323 llast = newlist = g_slist_append(newlist, msginfo);
2325 llast = g_slist_append(llast, msginfo);
2326 llast = llast->next;
2331 imap_fetch_env_free(env_list);
2334 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
2335 struct mailimap_set * imapset;
2337 imapset = cur->data;
2338 mailimap_set_free(imapset);
2341 session_set_access_time(SESSION(session));
2346 #define MAX_MSG_NUM 50
2348 static GSList *imap_get_uncached_messages(IMAPSession *session,
2350 MsgNumberList *numlist)
2352 GSList *result = NULL;
2354 uncached_data *data = g_new0(uncached_data, 1);
2359 data->total = g_slist_length(numlist);
2360 debug_print("messages list : %i\n", data->total);
2362 while (cur != NULL) {
2363 GSList * partial_result;
2371 while (count < MAX_MSG_NUM) {
2376 if (newlist == NULL)
2377 llast = newlist = g_slist_append(newlist, p);
2379 llast = g_slist_append(llast, p);
2380 llast = llast->next;
2390 data->session = session;
2392 data->numlist = newlist;
2395 if (prefs_common.work_offline &&
2396 !inc_offline_should_override(
2397 _("Claws Mail needs network access in order "
2398 "to access the IMAP server."))) {
2404 (GSList *)imap_get_uncached_messages_thread(data);
2406 statusbar_progress_all(data->cur,data->total, 1);
2408 g_slist_free(newlist);
2410 result = g_slist_concat(result, partial_result);
2414 statusbar_progress_all(0,0,0);
2415 statusbar_pop_all();
2420 static void imap_delete_all_cached_messages(FolderItem *item)
2424 g_return_if_fail(item != NULL);
2425 g_return_if_fail(item->folder != NULL);
2426 g_return_if_fail(FOLDER_CLASS(item->folder) == &imap_class);
2428 debug_print("Deleting all cached messages...\n");
2430 dir = folder_item_get_path(item);
2431 if (is_dir_exist(dir))
2432 remove_all_numbered_files(dir);
2435 debug_print("done.\n");
2438 gchar imap_get_path_separator_for_item(FolderItem *item)
2440 Folder *folder = NULL;
2441 IMAPFolder *imap_folder = NULL;
2442 IMAPSession *session = NULL;
2447 folder = item->folder;
2452 imap_folder = IMAP_FOLDER(folder);
2457 debug_print("getting session...");
2458 session = imap_session_get(FOLDER(folder));
2459 result = imap_get_path_separator(session, imap_folder, item->path);
2464 static gchar imap_refresh_path_separator(IMAPSession *session, IMAPFolder *folder, const gchar *subfolder)
2468 gchar separator = '\0';
2470 g_return_val_if_fail(session != NULL, '/');
2471 r = imap_threaded_list((Folder *)folder, "", subfolder, &lep_list);
2473 if (r != MAILIMAP_NO_ERROR) {
2474 log_warning(_("LIST failed\n"));
2478 if (clist_count(lep_list) > 0) {
2479 clistiter * iter = clist_begin(lep_list);
2480 struct mailimap_mailbox_list * mb;
2481 mb = clist_content(iter);
2483 separator = mb->mb_delimiter;
2484 debug_print("got separator: %c\n", folder->last_seen_separator);
2486 mailimap_list_result_free(lep_list);
2490 static gchar imap_get_path_separator(IMAPSession *session, IMAPFolder *folder, const gchar *path)
2492 gchar separator = '/';
2494 if (folder->last_seen_separator == 0) {
2495 folder->last_seen_separator = imap_refresh_path_separator(session, folder, "");
2498 if (folder->last_seen_separator == 0) {
2499 folder->last_seen_separator = imap_refresh_path_separator(session, folder, "INBOX");
2502 if (folder->last_seen_separator != 0) {
2503 debug_print("using separator: %c\n", folder->last_seen_separator);
2504 return folder->last_seen_separator;
2510 static gchar *imap_get_real_path(IMAPSession *session, IMAPFolder *folder, const gchar *path)
2515 g_return_val_if_fail(folder != NULL, NULL);
2516 g_return_val_if_fail(path != NULL, NULL);
2518 real_path = imap_utf8_to_modified_utf7(path);
2519 separator = imap_get_path_separator(session, folder, path);
2520 imap_path_separator_subst(real_path, separator);
2525 static gint imap_set_message_flags(IMAPSession *session,
2526 MsgNumberList *numlist,
2534 seq_list = imap_get_lep_set_from_numlist(numlist);
2536 for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) {
2537 struct mailimap_set * imapset;
2539 imapset = cur->data;
2541 ok = imap_cmd_store(session, imapset,
2545 imap_lep_set_free(seq_list);
2547 return IMAP_SUCCESS;
2550 typedef struct _select_data {
2551 IMAPSession *session;
2556 guint32 *uid_validity;
2560 static gint imap_select(IMAPSession *session, IMAPFolder *folder,
2562 gint *exists, gint *recent, gint *unseen,
2563 guint32 *uid_validity, gboolean block)
2567 gint exists_, recent_, unseen_;
2568 guint32 uid_validity_;
2570 if (!exists && !recent && !unseen && !uid_validity) {
2571 if (session->mbox && strcmp(session->mbox, path) == 0)
2572 return IMAP_SUCCESS;
2581 uid_validity = &uid_validity_;
2583 g_free(session->mbox);
2584 session->mbox = NULL;
2586 real_path = imap_get_real_path(session, folder, path);
2588 ok = imap_cmd_select(session, real_path,
2589 exists, recent, unseen, uid_validity, block);
2590 if (ok != IMAP_SUCCESS)
2591 log_warning(_("can't select folder: %s\n"), real_path);
2593 session->mbox = g_strdup(path);
2594 session->folder_content_changed = FALSE;
2601 static gint imap_status(IMAPSession *session, IMAPFolder *folder,
2602 const gchar *path, IMAPFolderItem *item,
2604 guint32 *uid_next, guint32 *uid_validity,
2605 gint *unseen, gboolean block)
2609 struct mailimap_mailbox_data_status * data_status;
2614 real_path = imap_get_real_path(session, folder, path);
2628 r = imap_threaded_status(FOLDER(folder), real_path,
2629 &data_status, mask);
2632 if (r != MAILIMAP_NO_ERROR) {
2633 debug_print("status err %d\n", r);
2637 if (data_status->st_info_list == NULL) {
2638 mailimap_mailbox_data_status_free(data_status);
2639 debug_print("status->st_info_list == NULL\n");
2644 for(iter = clist_begin(data_status->st_info_list) ; iter != NULL ;
2645 iter = clist_next(iter)) {
2646 struct mailimap_status_info * info;
2648 info = clist_content(iter);
2649 switch (info->st_att) {
2650 case MAILIMAP_STATUS_ATT_MESSAGES:
2651 * messages = info->st_value;
2652 got_values |= 1 << 0;
2655 case MAILIMAP_STATUS_ATT_UIDNEXT:
2656 * uid_next = info->st_value;
2657 got_values |= 1 << 2;
2660 case MAILIMAP_STATUS_ATT_UIDVALIDITY:
2661 * uid_validity = info->st_value;
2662 got_values |= 1 << 3;
2665 case MAILIMAP_STATUS_ATT_UNSEEN:
2666 * unseen = info->st_value;
2667 got_values |= 1 << 4;
2671 mailimap_mailbox_data_status_free(data_status);
2673 if (got_values != mask) {
2674 debug_print("status: incomplete values received (%d)\n", got_values);
2677 return IMAP_SUCCESS;
2680 static void imap_free_capabilities(IMAPSession *session)
2682 slist_free_strings(session->capability);
2683 g_slist_free(session->capability);
2684 session->capability = NULL;
2687 /* low-level IMAP4rev1 commands */
2689 static gint imap_cmd_login(IMAPSession *session,
2690 const gchar *user, const gchar *pass,
2696 if (!strcmp(type, "LOGIN") && imap_has_capability(session, "LOGINDISABLED")) {
2697 gint ok = IMAP_ERROR;
2698 if (imap_has_capability(session, "STARTTLS")) {
2700 log_warning(_("Server requires TLS to log in.\n"));
2701 ok = imap_cmd_starttls(session);
2702 if (ok != IMAP_SUCCESS) {
2703 log_warning(_("Can't start TLS session.\n"));
2707 imap_free_capabilities(session);
2708 if (imap_get_capabilities(session) != MAILIMAP_NO_ERROR) {
2709 log_warning(_("Can't refresh capabilities.\n"));
2714 log_error(_("Connection to %s failed: "
2715 "server requires TLS, but Claws Mail "
2716 "has been compiled without OpenSSL "
2718 SESSION(session)->server);
2722 log_error(_("Server logins are disabled.\n"));
2727 log_print("IMAP4> Logging %s to %s using %s\n",
2729 SESSION(session)->server,
2731 r = imap_threaded_login(session->folder, user, pass, type);
2732 if (r != MAILIMAP_NO_ERROR) {
2733 log_print("IMAP4< Error logging in to %s\n",
2734 SESSION(session)->server);
2737 log_print("IMAP4< Login to %s successful\n",
2738 SESSION(session)->server);
2744 static gint imap_cmd_noop(IMAPSession *session)
2747 unsigned int exists;
2749 r = imap_threaded_noop(session->folder, &exists);
2750 if (r != MAILIMAP_NO_ERROR) {
2751 debug_print("noop err %d\n", r);
2754 session->exists = exists;
2755 session_set_access_time(SESSION(session));
2757 return IMAP_SUCCESS;
2761 static gint imap_cmd_starttls(IMAPSession *session)
2765 r = imap_threaded_starttls(session->folder,
2766 SESSION(session)->server, SESSION(session)->port);
2767 if (r != MAILIMAP_NO_ERROR) {
2768 debug_print("starttls err %d\n", r);
2771 return IMAP_SUCCESS;
2775 static gint imap_cmd_select(IMAPSession *session, const gchar *folder,
2776 gint *exists, gint *recent, gint *unseen,
2777 guint32 *uid_validity, gboolean block)
2781 r = imap_threaded_select(session->folder, folder,
2782 exists, recent, unseen, uid_validity);
2783 if (r != MAILIMAP_NO_ERROR) {
2784 debug_print("select err %d\n", r);
2787 return IMAP_SUCCESS;
2790 static gint imap_cmd_examine(IMAPSession *session, const gchar *folder,
2791 gint *exists, gint *recent, gint *unseen,
2792 guint32 *uid_validity, gboolean block)
2796 r = imap_threaded_examine(session->folder, folder,
2797 exists, recent, unseen, uid_validity);
2798 if (r != MAILIMAP_NO_ERROR) {
2799 debug_print("examine err %d\n", r);
2803 return IMAP_SUCCESS;
2806 static gint imap_cmd_create(IMAPSession *session, const gchar *folder)
2810 r = imap_threaded_create(session->folder, folder);
2811 if (r != MAILIMAP_NO_ERROR) {
2816 return IMAP_SUCCESS;
2819 static gint imap_cmd_rename(IMAPSession *session, const gchar *old_folder,
2820 const gchar *new_folder)
2824 r = imap_threaded_rename(session->folder, old_folder,
2826 if (r != MAILIMAP_NO_ERROR) {
2831 return IMAP_SUCCESS;
2834 static gint imap_cmd_delete(IMAPSession *session, const gchar *folder)
2839 r = imap_threaded_delete(session->folder, folder);
2840 if (r != MAILIMAP_NO_ERROR) {
2845 return IMAP_SUCCESS;
2848 typedef struct _fetch_data {
2849 IMAPSession *session;
2851 const gchar *filename;
2857 static void *imap_cmd_fetch_thread(void *data)
2859 fetch_data *stuff = (fetch_data *)data;
2860 IMAPSession *session = stuff->session;
2861 guint32 uid = stuff->uid;
2862 const gchar *filename = stuff->filename;
2866 r = imap_threaded_fetch_content(session->folder,
2870 r = imap_threaded_fetch_content(session->folder,
2873 if (r != MAILIMAP_NO_ERROR) {
2874 debug_print("fetch err %d\n", r);
2875 return GINT_TO_POINTER(IMAP_ERROR);
2877 return GINT_TO_POINTER(IMAP_SUCCESS);
2880 static gint imap_cmd_fetch(IMAPSession *session, guint32 uid,
2881 const gchar *filename, gboolean headers,
2884 fetch_data *data = g_new0(fetch_data, 1);
2887 data->session = session;
2889 data->filename = filename;
2890 data->headers = headers;
2893 if (prefs_common.work_offline &&
2894 !inc_offline_should_override(
2895 _("Claws Mail needs network access in order "
2896 "to access the IMAP server."))) {
2900 statusbar_print_all(_("Fetching message..."));
2901 result = GPOINTER_TO_INT(imap_cmd_fetch_thread(data));
2902 statusbar_pop_all();
2908 static gint imap_cmd_append(IMAPSession *session, const gchar *destfolder,
2909 const gchar *file, IMAPFlags flags,
2912 struct mailimap_flag_list * flag_list;
2915 g_return_val_if_fail(file != NULL, IMAP_ERROR);
2917 flag_list = imap_flag_to_lep(flags);
2918 r = imap_threaded_append(session->folder, destfolder,
2919 file, flag_list, (int *)new_uid);
2920 mailimap_flag_list_free(flag_list);
2922 if (r != MAILIMAP_NO_ERROR) {
2923 debug_print("append err %d\n", r);
2926 return IMAP_SUCCESS;
2929 static gint imap_cmd_copy(IMAPSession *session, struct mailimap_set * set,
2930 const gchar *destfolder, GRelation *uid_mapping,
2931 struct mailimap_set **source, struct mailimap_set **dest)
2935 g_return_val_if_fail(session != NULL, IMAP_ERROR);
2936 g_return_val_if_fail(set != NULL, IMAP_ERROR);
2937 g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
2939 r = imap_threaded_copy(session->folder, set, destfolder, source, dest);
2940 if (r != MAILIMAP_NO_ERROR) {
2945 return IMAP_SUCCESS;
2948 static gint imap_cmd_store(IMAPSession *session, struct mailimap_set * set,
2949 IMAPFlags flags, int do_add)
2952 struct mailimap_flag_list * flag_list;
2953 struct mailimap_store_att_flags * store_att_flags;
2955 flag_list = imap_flag_to_lep(flags);
2959 mailimap_store_att_flags_new_add_flags_silent(flag_list);
2962 mailimap_store_att_flags_new_remove_flags_silent(flag_list);
2964 r = imap_threaded_store(session->folder, set, store_att_flags);
2965 mailimap_store_att_flags_free(store_att_flags);
2966 if (r != MAILIMAP_NO_ERROR) {
2971 return IMAP_SUCCESS;
2974 static gint imap_cmd_expunge(IMAPSession *session)
2978 if (prefs_common.work_offline &&
2979 !inc_offline_should_override(
2980 _("Claws Mail needs network access in order "
2981 "to access the IMAP server."))) {
2985 r = imap_threaded_expunge(session->folder);
2986 if (r != MAILIMAP_NO_ERROR) {
2991 return IMAP_SUCCESS;
2994 static void imap_path_separator_subst(gchar *str, gchar separator)
2997 gboolean in_escape = FALSE;
2999 if (!separator || separator == '/') return;
3001 for (p = str; *p != '\0'; p++) {
3002 if (*p == '/' && !in_escape)
3004 else if (*p == '&' && *(p + 1) != '-' && !in_escape)
3006 else if (*p == '-' && in_escape)
3011 static gchar *imap_modified_utf7_to_utf8(const gchar *mutf7_str)
3013 static iconv_t cd = (iconv_t)-1;
3014 static gboolean iconv_ok = TRUE;
3017 size_t norm_utf7_len;
3019 gchar *to_str, *to_p;
3021 gboolean in_escape = FALSE;
3023 if (!iconv_ok) return g_strdup(mutf7_str);
3025 if (cd == (iconv_t)-1) {
3026 cd = iconv_open(CS_INTERNAL, CS_UTF_7);
3027 if (cd == (iconv_t)-1) {
3028 g_warning("iconv cannot convert UTF-7 to %s\n",
3031 return g_strdup(mutf7_str);
3035 /* modified UTF-7 to normal UTF-7 conversion */
3036 norm_utf7 = g_string_new(NULL);
3038 for (p = mutf7_str; *p != '\0'; p++) {
3039 /* replace: '&' -> '+',
3041 escaped ',' -> '/' */
3042 if (!in_escape && *p == '&') {
3043 if (*(p + 1) != '-') {
3044 g_string_append_c(norm_utf7, '+');
3047 g_string_append_c(norm_utf7, '&');
3050 } else if (in_escape && *p == ',') {
3051 g_string_append_c(norm_utf7, '/');
3052 } else if (in_escape && *p == '-') {
3053 g_string_append_c(norm_utf7, '-');
3056 g_string_append_c(norm_utf7, *p);
3060 norm_utf7_p = norm_utf7->str;
3061 norm_utf7_len = norm_utf7->len;
3062 to_len = strlen(mutf7_str) * 5;
3063 to_p = to_str = g_malloc(to_len + 1);
3065 if (iconv(cd, (ICONV_CONST gchar **)&norm_utf7_p, &norm_utf7_len,
3066 &to_p, &to_len) == -1) {
3067 g_warning(_("iconv cannot convert UTF-7 to %s\n"),
3068 conv_get_locale_charset_str());
3069 g_string_free(norm_utf7, TRUE);
3071 return g_strdup(mutf7_str);
3074 /* second iconv() call for flushing */
3075 iconv(cd, NULL, NULL, &to_p, &to_len);
3076 g_string_free(norm_utf7, TRUE);
3082 static gchar *imap_utf8_to_modified_utf7(const gchar *from)
3084 static iconv_t cd = (iconv_t)-1;
3085 static gboolean iconv_ok = TRUE;
3086 gchar *norm_utf7, *norm_utf7_p;
3087 size_t from_len, norm_utf7_len;
3089 gchar *from_tmp, *to, *p;
3090 gboolean in_escape = FALSE;
3092 if (!iconv_ok) return g_strdup(from);
3094 if (cd == (iconv_t)-1) {
3095 cd = iconv_open(CS_UTF_7, CS_INTERNAL);
3096 if (cd == (iconv_t)-1) {
3097 g_warning(_("iconv cannot convert %s to UTF-7\n"),
3100 return g_strdup(from);
3104 /* UTF-8 to normal UTF-7 conversion */
3105 Xstrdup_a(from_tmp, from, return g_strdup(from));
3106 from_len = strlen(from);
3107 norm_utf7_len = from_len * 5;
3108 Xalloca(norm_utf7, norm_utf7_len + 1, return g_strdup(from));
3109 norm_utf7_p = norm_utf7;
3111 #define IS_PRINT(ch) (isprint(ch) && IS_ASCII(ch))
3113 while (from_len > 0) {
3114 if (*from_tmp == '+') {
3115 *norm_utf7_p++ = '+';
3116 *norm_utf7_p++ = '-';
3120 } else if (IS_PRINT(*(guchar *)from_tmp)) {
3121 /* printable ascii char */
3122 *norm_utf7_p = *from_tmp;
3128 size_t conv_len = 0;
3130 /* unprintable char: convert to UTF-7 */
3132 while (!IS_PRINT(*(guchar *)p) && conv_len < from_len) {
3133 conv_len += g_utf8_skip[*(guchar *)p];
3134 p += g_utf8_skip[*(guchar *)p];
3137 from_len -= conv_len;
3138 if (iconv(cd, (ICONV_CONST gchar **)&from_tmp,
3140 &norm_utf7_p, &norm_utf7_len) == -1) {
3141 g_warning(_("iconv cannot convert UTF-8 to UTF-7\n"));
3142 return g_strdup(from);
3145 /* second iconv() call for flushing */
3146 iconv(cd, NULL, NULL, &norm_utf7_p, &norm_utf7_len);
3152 *norm_utf7_p = '\0';
3153 to_str = g_string_new(NULL);
3154 for (p = norm_utf7; p < norm_utf7_p; p++) {
3155 /* replace: '&' -> "&-",
3158 BASE64 '/' -> ',' */
3159 if (!in_escape && *p == '&') {
3160 g_string_append(to_str, "&-");
3161 } else if (!in_escape && *p == '+') {
3162 if (*(p + 1) == '-') {
3163 g_string_append_c(to_str, '+');
3166 g_string_append_c(to_str, '&');
3169 } else if (in_escape && *p == '/') {
3170 g_string_append_c(to_str, ',');
3171 } else if (in_escape && *p == '-') {
3172 g_string_append_c(to_str, '-');
3175 g_string_append_c(to_str, *p);
3181 g_string_append_c(to_str, '-');
3185 g_string_free(to_str, FALSE);
3190 static gboolean imap_rename_folder_func(GNode *node, gpointer data)
3192 FolderItem *item = node->data;
3193 gchar **paths = data;
3194 const gchar *oldpath = paths[0];
3195 const gchar *newpath = paths[1];
3197 gchar *new_itempath;
3200 oldpathlen = strlen(oldpath);
3201 if (strncmp(oldpath, item->path, oldpathlen) != 0) {
3202 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
3206 base = item->path + oldpathlen;
3207 while (*base == G_DIR_SEPARATOR) base++;
3209 new_itempath = g_strdup(newpath);
3211 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
3214 item->path = new_itempath;
3219 typedef struct _get_list_uid_data {
3221 IMAPSession *session;
3222 IMAPFolderItem *item;
3223 GSList **msgnum_list;
3225 } get_list_uid_data;
3227 static void *get_list_of_uids_thread(void *data)
3229 get_list_uid_data *stuff = (get_list_uid_data *)data;
3230 Folder *folder = stuff->folder;
3231 IMAPFolderItem *item = stuff->item;
3232 GSList **msgnum_list = stuff->msgnum_list;
3233 gint ok, nummsgs = 0, lastuid_old;
3234 IMAPSession *session;
3235 GSList *uidlist, *elem;
3236 clist * lep_uidlist;
3239 session = stuff->session;
3240 if (session == NULL) {
3242 return GINT_TO_POINTER(-1);
3244 /* no session locking here, it's already locked by caller */
3245 ok = imap_select(session, IMAP_FOLDER(folder), item->item.path,
3246 NULL, NULL, NULL, NULL, TRUE);
3247 if (ok != IMAP_SUCCESS) {
3249 return GINT_TO_POINTER(-1);
3254 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SIMPLE, NULL,
3257 if (r == MAILIMAP_NO_ERROR) {
3258 GSList * fetchuid_list;
3261 imap_uid_list_from_lep(lep_uidlist);
3262 mailimap_search_result_free(lep_uidlist);
3264 uidlist = g_slist_concat(fetchuid_list, uidlist);
3267 GSList * fetchuid_list;
3268 carray * lep_uidtab;
3270 r = imap_threaded_fetch_uid(folder, item->lastuid + 1,
3272 if (r == MAILIMAP_NO_ERROR) {
3274 imap_uid_list_from_lep_tab(lep_uidtab);
3275 imap_fetch_uid_list_free(lep_uidtab);
3276 uidlist = g_slist_concat(fetchuid_list, uidlist);
3280 lastuid_old = item->lastuid;
3281 *msgnum_list = g_slist_copy(item->uid_list);
3282 nummsgs = g_slist_length(*msgnum_list);
3283 debug_print("Got %d uids from cache\n", g_slist_length(item->uid_list));
3285 for (elem = uidlist; elem != NULL; elem = g_slist_next(elem)) {
3288 msgnum = GPOINTER_TO_INT(elem->data);
3289 if (msgnum > lastuid_old) {
3290 *msgnum_list = g_slist_prepend(*msgnum_list, GINT_TO_POINTER(msgnum));
3291 item->uid_list = g_slist_prepend(item->uid_list, GINT_TO_POINTER(msgnum));
3294 if(msgnum > item->lastuid)
3295 item->lastuid = msgnum;
3298 g_slist_free(uidlist);
3300 return GINT_TO_POINTER(nummsgs);
3303 static gint get_list_of_uids(IMAPSession *session, Folder *folder, IMAPFolderItem *item, GSList **msgnum_list)
3306 get_list_uid_data *data = g_new0(get_list_uid_data, 1);
3308 data->folder = folder;
3310 data->msgnum_list = msgnum_list;
3311 data->session = session;
3312 if (prefs_common.work_offline &&
3313 !inc_offline_should_override(
3314 _("Claws Mail needs network access in order "
3315 "to access the IMAP server."))) {
3320 result = GPOINTER_TO_INT(get_list_of_uids_thread(data));
3326 gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list, gboolean *old_uids_valid)
3328 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3329 IMAPSession *session;
3330 gint ok, nummsgs = 0, exists;
3331 guint32 uid_next = 0, uid_val = 0;
3332 GSList *uidlist = NULL;
3334 gboolean selected_folder;
3335 debug_print("get_num_list\n");
3337 g_return_val_if_fail(folder != NULL, -1);
3338 g_return_val_if_fail(item != NULL, -1);
3339 g_return_val_if_fail(item->item.path != NULL, -1);
3340 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3341 g_return_val_if_fail(folder->account != NULL, -1);
3343 debug_print("getting session...\n");
3344 session = imap_session_get(folder);
3345 g_return_val_if_fail(session != NULL, -1);
3347 if (FOLDER_ITEM(item)->path)
3348 statusbar_print_all(_("Scanning folder %s%c%s ..."),
3349 FOLDER_ITEM(item)->folder->name,
3351 FOLDER_ITEM(item)->path);
3353 statusbar_print_all(_("Scanning folder %s ..."),
3354 FOLDER_ITEM(item)->folder->name);
3356 selected_folder = (session->mbox != NULL) &&
3357 (!strcmp(session->mbox, item->item.path));
3358 if (selected_folder && time(NULL) - item->use_cache < 2) {
3359 ok = imap_cmd_noop(session);
3360 if (ok != IMAP_SUCCESS) {
3361 debug_print("disconnected!\n");
3362 session = imap_reconnect_if_possible(folder, session);
3363 if (session == NULL) {
3364 statusbar_pop_all();
3369 exists = session->exists;
3371 uid_next = item->c_uid_next;
3372 uid_val = item->c_uid_validity;
3373 *old_uids_valid = TRUE;
3375 if (item->use_cache && time(NULL) - item->use_cache < 2) {
3376 exists = item->c_messages;
3377 uid_next = item->c_uid_next;
3378 uid_val = item->c_uid_validity;
3380 debug_print("using cache %d %d %d\n", exists, uid_next, uid_val);
3382 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path, item,
3383 &exists, &uid_next, &uid_val, NULL, FALSE);
3385 item->item.last_num = uid_next - 1;
3387 item->use_cache = (time_t)0;
3388 if (ok != IMAP_SUCCESS) {
3389 statusbar_pop_all();
3393 if(item->item.mtime == uid_val)
3394 *old_uids_valid = TRUE;
3396 *old_uids_valid = FALSE;
3398 debug_print("Freeing imap uid cache (%d != %d)\n",
3399 (int)item->item.mtime, uid_val);
3401 g_slist_free(item->uid_list);
3402 item->uid_list = NULL;
3404 item->item.mtime = uid_val;
3406 imap_delete_all_cached_messages((FolderItem *)item);
3410 /* If old uid_next matches new uid_next we can be sure no message
3411 was added to the folder */
3412 debug_print("uid_next is %d and item->uid_next %d \n",
3413 uid_next, item->uid_next);
3414 if (uid_next == item->uid_next) {
3415 nummsgs = g_slist_length(item->uid_list);
3417 /* If number of messages is still the same we
3418 know our caches message numbers are still valid,
3419 otherwise if the number of messages has decrease
3420 we discard our cache to start a new scan to find
3421 out which numbers have been removed */
3422 if (exists == nummsgs) {
3423 debug_print("exists == nummsgs\n");
3424 *msgnum_list = g_slist_copy(item->uid_list);
3425 statusbar_pop_all();
3428 } else if (exists < nummsgs) {
3429 debug_print("Freeing imap uid cache");
3431 g_slist_free(item->uid_list);
3432 item->uid_list = NULL;
3437 *msgnum_list = NULL;
3438 statusbar_pop_all();
3443 nummsgs = get_list_of_uids(session, folder, item, &uidlist);
3446 statusbar_pop_all();
3451 if (nummsgs != exists) {
3452 /* Cache contains more messages then folder, we have cached
3453 an old UID of a message that was removed and new messages
3454 have been added too, otherwise the uid_next check would
3456 debug_print("Freeing imap uid cache");
3458 g_slist_free(item->uid_list);
3459 item->uid_list = NULL;
3461 g_slist_free(*msgnum_list);
3463 nummsgs = get_list_of_uids(session, folder, item, &uidlist);
3466 *msgnum_list = uidlist;
3468 dir = folder_item_get_path((FolderItem *)item);
3469 debug_print("removing old messages from %s\n", dir);
3470 remove_numbered_files_not_in_list(dir, *msgnum_list);
3473 item->uid_next = uid_next;
3475 debug_print("get_num_list - ok - %i\n", nummsgs);
3476 statusbar_pop_all();
3481 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item)
3486 flags.perm_flags = MSG_NEW|MSG_UNREAD;
3487 flags.tmp_flags = 0;
3489 g_return_val_if_fail(item != NULL, NULL);
3490 g_return_val_if_fail(file != NULL, NULL);
3492 if (folder_has_parent_of_type(item, F_QUEUE)) {
3493 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
3494 } else if (folder_has_parent_of_type(item, F_DRAFT)) {
3495 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
3498 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3499 if (!msginfo) return NULL;
3501 msginfo->plaintext_file = g_strdup(file);
3502 msginfo->folder = item;
3507 GSList *imap_get_msginfos(Folder *folder, FolderItem *item,
3508 GSList *msgnum_list)
3510 IMAPSession *session;
3511 MsgInfoList *ret = NULL;
3514 debug_print("get_msginfos\n");
3516 g_return_val_if_fail(folder != NULL, NULL);
3517 g_return_val_if_fail(item != NULL, NULL);
3518 g_return_val_if_fail(msgnum_list != NULL, NULL);
3520 debug_print("getting session...\n");
3521 session = imap_session_get(folder);
3522 g_return_val_if_fail(session != NULL, NULL);
3524 debug_print("IMAP getting msginfos\n");
3525 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3526 NULL, NULL, NULL, NULL, FALSE);
3527 if (ok != IMAP_SUCCESS) {
3531 if (!(folder_has_parent_of_type(item, F_DRAFT) ||
3532 folder_has_parent_of_type(item, F_QUEUE))) {
3533 ret = g_slist_concat(ret,
3534 imap_get_uncached_messages(session, item,
3537 MsgNumberList *sorted_list, *elem, *llast = NULL;
3538 gint startnum, lastnum;
3540 sorted_list = g_slist_sort(g_slist_copy(msgnum_list), g_int_compare);
3542 startnum = lastnum = GPOINTER_TO_INT(sorted_list->data);
3544 llast = g_slist_last(ret);
3545 for (elem = sorted_list;; elem = g_slist_next(elem)) {
3549 num = GPOINTER_TO_INT(elem->data);
3551 if (num > lastnum + 1 || elem == NULL) {
3553 for (i = startnum; i <= lastnum; ++i) {
3556 file = imap_fetch_msg(folder, item, i);
3558 MsgInfo *msginfo = imap_parse_msg(file, item);
3559 if (msginfo != NULL) {
3560 msginfo->msgnum = i;
3562 llast = ret = g_slist_append(ret, msginfo);
3564 llast = g_slist_append(llast, msginfo);
3565 llast = llast->next;
3570 session_set_access_time(SESSION(session));
3581 g_slist_free(sorted_list);
3587 MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid)
3589 MsgInfo *msginfo = NULL;
3590 MsgInfoList *msginfolist;
3591 MsgNumberList numlist;
3593 numlist.next = NULL;
3594 numlist.data = GINT_TO_POINTER(uid);
3596 msginfolist = imap_get_msginfos(folder, item, &numlist);
3597 if (msginfolist != NULL) {
3598 msginfo = msginfolist->data;
3599 g_slist_free(msginfolist);
3605 gboolean imap_scan_required(Folder *folder, FolderItem *_item)
3607 IMAPSession *session;
3608 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3609 gint ok, exists = 0, unseen = 0;
3610 guint32 uid_next = 0, uid_val = 0;
3611 gboolean selected_folder;
3613 g_return_val_if_fail(folder != NULL, FALSE);
3614 g_return_val_if_fail(item != NULL, FALSE);
3615 g_return_val_if_fail(item->item.folder != NULL, FALSE);
3616 g_return_val_if_fail(FOLDER_CLASS(item->item.folder) == &imap_class, FALSE);
3618 if (item->item.path == NULL)
3621 debug_print("getting session...\n");
3622 session = imap_session_get(folder);
3623 g_return_val_if_fail(session != NULL, FALSE);
3625 selected_folder = (session->mbox != NULL) &&
3626 (!strcmp(session->mbox, item->item.path));
3627 if (selected_folder && time(NULL) - item->use_cache < 2) {
3628 ok = imap_cmd_noop(session);
3629 if (ok != IMAP_SUCCESS) {
3630 debug_print("disconnected!\n");
3631 session = imap_reconnect_if_possible(folder, session);
3632 if (session == NULL)
3636 if (session->folder_content_changed
3637 || session->exists != item->item.total_msgs) {
3642 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path, IMAP_FOLDER_ITEM(item),
3643 &exists, &uid_next, &uid_val, &unseen, FALSE);
3644 if (ok != IMAP_SUCCESS) {
3649 item->use_cache = time(NULL);
3650 item->c_messages = exists;
3651 item->c_uid_next = uid_next;
3652 item->c_uid_validity = uid_val;
3653 item->c_unseen = unseen;
3654 item->item.last_num = uid_next - 1;
3655 debug_print("uidnext %d, item->uid_next %d, exists %d, item->item.total_msgs %d\n",
3656 uid_next, item->uid_next, exists, item->item.total_msgs);
3657 if ((uid_next != item->uid_next) || (exists != item->item.total_msgs)
3658 || unseen != item->item.unread_msgs || uid_val != item->item.mtime) {
3667 void imap_change_flags(Folder *folder, FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
3669 IMAPSession *session;
3670 IMAPFlags flags_set = 0, flags_unset = 0;
3671 gint ok = IMAP_SUCCESS;
3672 MsgNumberList numlist;
3673 hashtable_data *ht_data = NULL;
3675 g_return_if_fail(folder != NULL);
3676 g_return_if_fail(folder->klass == &imap_class);
3677 g_return_if_fail(item != NULL);
3678 g_return_if_fail(item->folder == folder);
3679 g_return_if_fail(msginfo != NULL);
3680 g_return_if_fail(msginfo->folder == item);
3682 if (!MSG_IS_MARKED(msginfo->flags) && (newflags & MSG_MARKED))
3683 flags_set |= IMAP_FLAG_FLAGGED;
3684 if ( MSG_IS_MARKED(msginfo->flags) && !(newflags & MSG_MARKED))
3685 flags_unset |= IMAP_FLAG_FLAGGED;
3687 if (!MSG_IS_UNREAD(msginfo->flags) && (newflags & MSG_UNREAD))
3688 flags_unset |= IMAP_FLAG_SEEN;
3689 if ( MSG_IS_UNREAD(msginfo->flags) && !(newflags & MSG_UNREAD))
3690 flags_set |= IMAP_FLAG_SEEN;
3692 if (!MSG_IS_REPLIED(msginfo->flags) && (newflags & MSG_REPLIED))
3693 flags_set |= IMAP_FLAG_ANSWERED;
3694 if ( MSG_IS_REPLIED(msginfo->flags) && !(newflags & MSG_REPLIED))
3695 flags_unset |= IMAP_FLAG_ANSWERED;
3697 if (!MSG_IS_DELETED(msginfo->flags) && (newflags & MSG_DELETED))
3698 flags_set |= IMAP_FLAG_DELETED;
3699 if ( MSG_IS_DELETED(msginfo->flags) && !(newflags & MSG_DELETED))
3700 flags_unset |= IMAP_FLAG_DELETED;
3702 if (!flags_set && !flags_unset) {
3703 /* the changed flags were not translatable to IMAP-speak.
3704 * like MSG_POSTFILTERED, so just apply. */
3705 msginfo->flags.perm_flags = newflags;
3709 debug_print("getting session...\n");
3710 session = imap_session_get(folder);
3715 if ((ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
3716 NULL, NULL, NULL, NULL, FALSE)) != IMAP_SUCCESS) {
3720 numlist.next = NULL;
3721 numlist.data = GINT_TO_POINTER(msginfo->msgnum);
3723 if (IMAP_FOLDER_ITEM(item)->batching) {
3724 /* instead of performing an UID STORE command for each message change,
3725 * as a lot of them can change "together", we just fill in hashtables
3726 * and defer the treatment so that we're able to send only one
3729 debug_print("IMAP batch mode on, deferring flags change\n");
3731 ht_data = g_hash_table_lookup(IMAP_FOLDER_ITEM(item)->flags_set_table,
3732 GINT_TO_POINTER(flags_set));
3733 if (ht_data == NULL) {
3734 ht_data = g_new0(hashtable_data, 1);
3735 ht_data->session = session;
3736 ht_data->item = IMAP_FOLDER_ITEM(item);
3737 g_hash_table_insert(IMAP_FOLDER_ITEM(item)->flags_set_table,
3738 GINT_TO_POINTER(flags_set), ht_data);
3740 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
3741 ht_data->msglist = g_slist_prepend(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum));
3744 ht_data = g_hash_table_lookup(IMAP_FOLDER_ITEM(item)->flags_unset_table,
3745 GINT_TO_POINTER(flags_unset));
3746 if (ht_data == NULL) {
3747 ht_data = g_new0(hashtable_data, 1);
3748 ht_data->session = session;
3749 ht_data->item = IMAP_FOLDER_ITEM(item);
3750 g_hash_table_insert(IMAP_FOLDER_ITEM(item)->flags_unset_table,
3751 GINT_TO_POINTER(flags_unset), ht_data);
3753 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
3754 ht_data->msglist = g_slist_prepend(ht_data->msglist,
3755 GINT_TO_POINTER(msginfo->msgnum));
3758 debug_print("IMAP changing flags\n");
3760 ok = imap_set_message_flags(session, &numlist, flags_set, TRUE);
3761 if (ok != IMAP_SUCCESS) {
3768 ok = imap_set_message_flags(session, &numlist, flags_unset, FALSE);
3769 if (ok != IMAP_SUCCESS) {
3775 msginfo->flags.perm_flags = newflags;
3780 static gint imap_remove_msg(Folder *folder, FolderItem *item, gint uid)
3783 IMAPSession *session;
3785 MsgNumberList numlist;
3787 g_return_val_if_fail(folder != NULL, -1);
3788 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3789 g_return_val_if_fail(item != NULL, -1);
3791 debug_print("getting session...\n");
3792 session = imap_session_get(folder);
3793 if (!session) return -1;
3795 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3796 NULL, NULL, NULL, NULL, FALSE);
3797 if (ok != IMAP_SUCCESS) {
3801 numlist.next = NULL;
3802 numlist.data = GINT_TO_POINTER(uid);
3804 ok = imap_set_message_flags
3805 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
3806 &numlist, IMAP_FLAG_DELETED, TRUE);
3807 if (ok != IMAP_SUCCESS) {
3808 log_warning(_("can't set deleted flags: %d\n"), uid);
3813 if (!session->uidplus) {
3814 ok = imap_cmd_expunge(session);
3818 uidstr = g_strdup_printf("%u", uid);
3819 ok = imap_cmd_expunge(session);
3822 if (ok != IMAP_SUCCESS) {
3823 log_warning(_("can't expunge\n"));
3828 IMAP_FOLDER_ITEM(item)->uid_list = g_slist_remove(
3829 IMAP_FOLDER_ITEM(item)->uid_list, numlist.data);
3830 dir = folder_item_get_path(item);
3831 if (is_dir_exist(dir))
3832 remove_numbered_files(dir, uid, uid);
3835 return IMAP_SUCCESS;
3838 static gint compare_msginfo(gconstpointer a, gconstpointer b)
3840 return ((MsgInfo *)a)->msgnum - ((MsgInfo *)b)->msgnum;
3843 static guint gslist_find_next_num(MsgNumberList **list, guint num)
3847 g_return_val_if_fail(list != NULL, -1);
3849 for (elem = *list; elem != NULL; elem = g_slist_next(elem))
3850 if (GPOINTER_TO_INT(elem->data) >= num)
3853 return elem != NULL ? GPOINTER_TO_INT(elem->data) : (gint)-1;
3857 * NEW and DELETED flags are not syncronized
3858 * - The NEW/RECENT flags in IMAP folders can not really be directly
3859 * modified by Sylpheed
3860 * - The DELETE/DELETED flag in IMAP and Sylpheed don't have the same
3861 * meaning, in IMAP it always removes the messages from the FolderItem
3862 * in Sylpheed it can mean to move the message to trash
3865 typedef struct _get_flags_data {
3868 MsgInfoList *msginfo_list;
3869 GRelation *msgflags;
3870 gboolean full_search;
3874 static /*gint*/ void *imap_get_flags_thread(void *data)
3876 get_flags_data *stuff = (get_flags_data *)data;
3877 Folder *folder = stuff->folder;
3878 FolderItem *item = stuff->item;
3879 MsgInfoList *msginfo_list = stuff->msginfo_list;
3880 GRelation *msgflags = stuff->msgflags;
3881 gboolean full_search = stuff->full_search;
3882 IMAPSession *session;
3883 GSList *sorted_list = NULL;
3884 GSList *unseen = NULL, *answered = NULL, *flagged = NULL, *deleted = NULL;
3885 GSList *p_unseen, *p_answered, *p_flagged, *p_deleted;
3887 GSList *seq_list, *cur;
3888 gboolean reverse_seen = FALSE;
3891 gint exists_cnt, unseen_cnt;
3892 gboolean selected_folder;
3894 if (folder == NULL || item == NULL) {
3896 return GINT_TO_POINTER(-1);
3899 debug_print("getting session...\n");
3900 session = imap_session_get(folder);
3901 if (session == NULL) {
3903 return GINT_TO_POINTER(-1);
3906 selected_folder = (session->mbox != NULL) &&
3907 (!strcmp(session->mbox, item->path));
3909 if (!selected_folder) {
3910 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3911 &exists_cnt, NULL, &unseen_cnt, NULL, TRUE);
3912 if (ok != IMAP_SUCCESS) {
3915 return GINT_TO_POINTER(-1);
3918 if (unseen_cnt > exists_cnt / 2)
3919 reverse_seen = TRUE;
3922 if (item->unread_msgs > item->total_msgs / 2)
3923 reverse_seen = TRUE;
3926 cmd_buf = g_string_new(NULL);
3928 sorted_list = g_slist_sort(g_slist_copy(msginfo_list), compare_msginfo);
3930 seq_list = imap_get_lep_set_from_msglist(msginfo_list);
3932 struct mailimap_set * set;
3933 set = mailimap_set_new_interval(1, 0);
3934 seq_list = g_slist_append(NULL, set);
3937 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
3938 struct mailimap_set * imapset;
3939 clist * lep_uidlist;
3942 imapset = cur->data;
3944 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SEEN,
3945 full_search ? NULL:imapset, &lep_uidlist);
3948 r = imap_threaded_search(folder,
3949 IMAP_SEARCH_TYPE_UNSEEN,
3950 full_search ? NULL:imapset, &lep_uidlist);
3952 if (r == MAILIMAP_NO_ERROR) {
3955 uidlist = imap_uid_list_from_lep(lep_uidlist);
3956 mailimap_search_result_free(lep_uidlist);
3958 unseen = g_slist_concat(unseen, uidlist);
3961 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_FLAGGED,
3962 full_search ? NULL:imapset, &lep_uidlist);
3963 if (r == MAILIMAP_NO_ERROR) {
3966 uidlist = imap_uid_list_from_lep(lep_uidlist);
3967 mailimap_search_result_free(lep_uidlist);
3969 flagged = g_slist_concat(flagged, uidlist);
3972 if (item->opened || item->processing_pending || item == folder->inbox) {
3973 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_ANSWERED,
3974 full_search ? NULL:imapset, &lep_uidlist);
3975 if (r == MAILIMAP_NO_ERROR) {
3978 uidlist = imap_uid_list_from_lep(lep_uidlist);
3979 mailimap_search_result_free(lep_uidlist);
3981 answered = g_slist_concat(answered, uidlist);
3984 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_DELETED,
3985 full_search ? NULL:imapset, &lep_uidlist);
3986 if (r == MAILIMAP_NO_ERROR) {
3989 uidlist = imap_uid_list_from_lep(lep_uidlist);
3990 mailimap_search_result_free(lep_uidlist);
3992 deleted = g_slist_concat(deleted, uidlist);
3998 p_answered = answered;
3999 p_flagged = flagged;
4000 p_deleted = deleted;
4002 for (elem = sorted_list; elem != NULL; elem = g_slist_next(elem)) {
4007 msginfo = (MsgInfo *) elem->data;
4008 flags = msginfo->flags.perm_flags;
4009 wasnew = (flags & MSG_NEW);
4010 if (item->opened || item->processing_pending || item == folder->inbox) {
4011 flags &= ~((reverse_seen ? 0 : MSG_UNREAD | MSG_NEW) | MSG_REPLIED | MSG_MARKED);
4013 flags &= ~((reverse_seen ? 0 : MSG_UNREAD | MSG_NEW | MSG_MARKED));
4016 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
4017 if (gslist_find_next_num(&p_unseen, msginfo->msgnum) == msginfo->msgnum) {
4018 if (!reverse_seen) {
4019 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
4021 flags &= ~(MSG_UNREAD | MSG_NEW);
4025 if (gslist_find_next_num(&p_flagged, msginfo->msgnum) == msginfo->msgnum)
4026 flags |= MSG_MARKED;
4028 flags &= ~MSG_MARKED;
4030 if (item->opened || item->processing_pending || item == folder->inbox) {
4031 if (gslist_find_next_num(&p_answered, msginfo->msgnum) == msginfo->msgnum)
4032 flags |= MSG_REPLIED;
4034 flags &= ~MSG_REPLIED;
4035 if (gslist_find_next_num(&p_deleted, msginfo->msgnum) == msginfo->msgnum)
4036 flags |= MSG_DELETED;
4038 flags &= ~MSG_DELETED;
4040 g_relation_insert(msgflags, msginfo, GINT_TO_POINTER(flags));
4043 imap_lep_set_free(seq_list);
4044 g_slist_free(flagged);
4045 g_slist_free(deleted);
4046 g_slist_free(answered);
4047 g_slist_free(unseen);
4048 g_slist_free(sorted_list);
4049 g_string_free(cmd_buf, TRUE);
4053 return GINT_TO_POINTER(0);
4056 static gint imap_get_flags(Folder *folder, FolderItem *item,
4057 MsgInfoList *msginfo_list, GRelation *msgflags)
4060 get_flags_data *data = g_new0(get_flags_data, 1);
4062 data->folder = folder;
4064 data->msginfo_list = msginfo_list;
4065 data->msgflags = msgflags;
4066 data->full_search = FALSE;
4068 GSList *tmp = NULL, *cur;
4070 if (prefs_common.work_offline &&
4071 !inc_offline_should_override(
4072 _("Claws Mail needs network access in order "
4073 "to access the IMAP server."))) {
4078 tmp = folder_item_get_msg_list(item);
4080 if (g_slist_length(tmp) <= g_slist_length(msginfo_list))
4081 data->full_search = TRUE;
4083 for (cur = tmp; cur; cur = cur->next)
4084 procmsg_msginfo_free((MsgInfo *)cur->data);
4088 result = GPOINTER_TO_INT(imap_get_flags_thread(data));
4095 static gboolean process_flags(gpointer key, gpointer value, gpointer user_data)
4097 gboolean flags_set = GPOINTER_TO_INT(user_data);
4098 gint flags_value = GPOINTER_TO_INT(key);
4099 hashtable_data *data = (hashtable_data *)value;
4100 IMAPFolderItem *_item = data->item;
4101 FolderItem *item = (FolderItem *)_item;
4102 gint ok = IMAP_ERROR;
4103 IMAPSession *session = NULL;
4105 debug_print("getting session...\n");
4106 session = imap_session_get(item->folder);
4108 data->msglist = g_slist_reverse(data->msglist);
4110 debug_print("IMAP %ssetting flags to %d for %d messages\n",
4113 g_slist_length(data->msglist));
4116 ok = imap_select(session, IMAP_FOLDER(item->folder), item->path,
4117 NULL, NULL, NULL, NULL, FALSE);
4119 if (ok == IMAP_SUCCESS) {
4120 imap_set_message_flags(data->session, data->msglist, flags_value, flags_set);
4122 g_warning("can't select mailbox %s\n", item->path);
4126 g_slist_free(data->msglist);
4131 static void process_hashtable(IMAPFolderItem *item)
4133 if (item->flags_set_table) {
4134 g_hash_table_foreach_remove(item->flags_set_table, process_flags, GINT_TO_POINTER(TRUE));
4135 g_hash_table_destroy(item->flags_set_table);
4136 item->flags_set_table = NULL;
4138 if (item->flags_unset_table) {
4139 g_hash_table_foreach_remove(item->flags_unset_table, process_flags, GINT_TO_POINTER(FALSE));
4140 g_hash_table_destroy(item->flags_unset_table);
4141 item->flags_unset_table = NULL;
4145 static void imap_set_batch (Folder *folder, FolderItem *_item, gboolean batch)
4147 IMAPFolderItem *item = (IMAPFolderItem *)_item;
4149 g_return_if_fail(item != NULL);
4151 if (item->batching == batch)
4155 item->batching = TRUE;
4156 debug_print("IMAP switching to batch mode\n");
4157 if (!item->flags_set_table) {
4158 item->flags_set_table = g_hash_table_new(NULL, g_direct_equal);
4160 if (!item->flags_unset_table) {
4161 item->flags_unset_table = g_hash_table_new(NULL, g_direct_equal);
4164 debug_print("IMAP switching away from batch mode\n");
4166 process_hashtable(item);
4167 item->batching = FALSE;
4173 /* data types conversion libetpan <-> claws */
4177 #define ETPAN_IMAP_MB_MARKED 1
4178 #define ETPAN_IMAP_MB_UNMARKED 2
4179 #define ETPAN_IMAP_MB_NOSELECT 4
4180 #define ETPAN_IMAP_MB_NOINFERIORS 8
4182 static int imap_flags_to_flags(struct mailimap_mbx_list_flags * imap_flags)
4188 if (imap_flags->mbf_type == MAILIMAP_MBX_LIST_FLAGS_SFLAG) {
4189 switch (imap_flags->mbf_sflag) {
4190 case MAILIMAP_MBX_LIST_SFLAG_MARKED:
4191 flags |= ETPAN_IMAP_MB_MARKED;
4193 case MAILIMAP_MBX_LIST_SFLAG_NOSELECT:
4194 flags |= ETPAN_IMAP_MB_NOSELECT;
4196 case MAILIMAP_MBX_LIST_SFLAG_UNMARKED:
4197 flags |= ETPAN_IMAP_MB_UNMARKED;
4202 for(cur = clist_begin(imap_flags->mbf_oflags) ; cur != NULL ;
4203 cur = clist_next(cur)) {
4204 struct mailimap_mbx_list_oflag * oflag;
4206 oflag = clist_content(cur);
4208 switch (oflag->of_type) {
4209 case MAILIMAP_MBX_LIST_OFLAG_NOINFERIORS:
4210 flags |= ETPAN_IMAP_MB_NOINFERIORS;
4218 static GSList * imap_list_from_lep(IMAPFolder * folder,
4219 clist * list, const gchar * real_path, gboolean all)
4222 GSList * item_list = NULL, *llast = NULL;
4224 for(iter = clist_begin(list) ; iter != NULL ;
4225 iter = clist_next(iter)) {
4226 struct mailimap_mailbox_list * mb;
4234 FolderItem *new_item;
4236 mb = clist_content(iter);
4242 if (mb->mb_flag != NULL)
4243 flags = imap_flags_to_flags(mb->mb_flag);
4245 delimiter = mb->mb_delimiter;
4248 dup_name = strdup(name);
4249 if (delimiter != '\0')
4250 subst_char(dup_name, delimiter, '/');
4252 base = g_path_get_basename(dup_name);
4253 if (base[0] == '.') {
4258 if (!all && path_cmp(name, real_path) == 0) {
4264 if (!all && dup_name[strlen(dup_name)-1] == '/') {
4265 dup_name[strlen(dup_name)-1] = '\0';
4268 loc_name = imap_modified_utf7_to_utf8(base);
4269 loc_path = imap_modified_utf7_to_utf8(dup_name);
4271 new_item = folder_item_new(FOLDER(folder), loc_name, loc_path);
4272 if ((flags & ETPAN_IMAP_MB_NOINFERIORS) != 0)
4273 new_item->no_sub = TRUE;
4274 if (strcmp(dup_name, "INBOX") != 0 &&
4275 ((flags & ETPAN_IMAP_MB_NOSELECT) != 0))
4276 new_item->no_select = TRUE;
4278 if (item_list == NULL)
4279 llast = item_list = g_slist_append(item_list, new_item);
4281 llast = g_slist_append(llast, new_item);
4282 llast = llast->next;
4284 debug_print("folder '%s' found.\n", loc_path);
4295 static GSList * imap_get_lep_set_from_numlist(MsgNumberList *numlist)
4297 GSList *sorted_list, *cur;
4298 guint first, last, next;
4299 GSList *ret_list = NULL, *llast = NULL;
4301 struct mailimap_set * current_set;
4302 unsigned int item_count;
4304 if (numlist == NULL)
4308 current_set = mailimap_set_new_empty();
4310 sorted_list = g_slist_copy(numlist);
4311 sorted_list = g_slist_sort(sorted_list, g_int_compare);
4313 first = GPOINTER_TO_INT(sorted_list->data);
4316 for (cur = sorted_list; cur != NULL; cur = g_slist_next(cur)) {
4317 if (GPOINTER_TO_INT(cur->data) == 0)
4322 last = GPOINTER_TO_INT(cur->data);
4324 next = GPOINTER_TO_INT(cur->next->data);
4328 if (last + 1 != next || next == 0) {
4330 struct mailimap_set_item * item;
4331 item = mailimap_set_item_new(first, last);
4332 mailimap_set_add(current_set, item);
4337 if (count >= IMAP_SET_MAX_COUNT) {
4338 if (ret_list == NULL)
4339 llast = ret_list = g_slist_append(ret_list,
4342 llast = g_slist_append(llast, current_set);
4343 llast = llast->next;
4345 current_set = mailimap_set_new_empty();
4352 if (clist_count(current_set->set_list) > 0) {
4353 ret_list = g_slist_append(ret_list,
4357 g_slist_free(sorted_list);
4362 static GSList * imap_get_lep_set_from_msglist(MsgInfoList *msglist)
4364 MsgNumberList *numlist = NULL;
4368 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
4369 MsgInfo *msginfo = (MsgInfo *) cur->data;
4371 numlist = g_slist_prepend(numlist, GINT_TO_POINTER(msginfo->msgnum));
4373 numlist = g_slist_reverse(numlist);
4374 seq_list = imap_get_lep_set_from_numlist(numlist);
4375 g_slist_free(numlist);
4380 static GSList * imap_uid_list_from_lep(clist * list)
4387 for(iter = clist_begin(list) ; iter != NULL ;
4388 iter = clist_next(iter)) {
4391 puid = clist_content(iter);
4392 result = g_slist_prepend(result, GINT_TO_POINTER(* puid));
4395 result = g_slist_reverse(result);
4399 static GSList * imap_uid_list_from_lep_tab(carray * list)
4406 for(i = 0 ; i < carray_count(list) ; i ++) {
4409 puid = carray_get(list, i);
4410 result = g_slist_prepend(result, GINT_TO_POINTER(* puid));
4412 result = g_slist_reverse(result);
4416 static MsgInfo *imap_envelope_from_lep(struct imap_fetch_env_info * info,
4419 MsgInfo *msginfo = NULL;
4422 MsgFlags flags = {0, 0};
4424 if (info->headers == NULL)
4427 MSG_SET_TMP_FLAGS(flags, MSG_IMAP);
4428 if (folder_has_parent_of_type(item, F_QUEUE)) {
4429 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
4430 } else if (folder_has_parent_of_type(item, F_DRAFT)) {
4431 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
4433 flags.perm_flags = info->flags;
4437 msginfo = procheader_parse_str(info->headers, flags, FALSE, FALSE);
4440 msginfo->msgnum = uid;
4441 msginfo->size = size;
4447 static void imap_lep_set_free(GSList *seq_list)
4451 for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) {
4452 struct mailimap_set * imapset;
4454 imapset = cur->data;
4455 mailimap_set_free(imapset);
4457 g_slist_free(seq_list);
4460 static struct mailimap_flag_list * imap_flag_to_lep(IMAPFlags flags)
4462 struct mailimap_flag_list * flag_list;
4464 flag_list = mailimap_flag_list_new_empty();
4466 if (IMAP_IS_SEEN(flags))
4467 mailimap_flag_list_add(flag_list,
4468 mailimap_flag_new_seen());
4469 if (IMAP_IS_ANSWERED(flags))
4470 mailimap_flag_list_add(flag_list,
4471 mailimap_flag_new_answered());
4472 if (IMAP_IS_FLAGGED(flags))
4473 mailimap_flag_list_add(flag_list,
4474 mailimap_flag_new_flagged());
4475 if (IMAP_IS_DELETED(flags))
4476 mailimap_flag_list_add(flag_list,
4477 mailimap_flag_new_deleted());
4478 if (IMAP_IS_DRAFT(flags))
4479 mailimap_flag_list_add(flag_list,
4480 mailimap_flag_new_draft());
4485 guint imap_folder_get_refcnt(Folder *folder)
4487 return ((IMAPFolder *)folder)->refcnt;
4490 void imap_folder_ref(Folder *folder)
4492 ((IMAPFolder *)folder)->refcnt++;
4495 void imap_disconnect_all(void)
4498 for (list = account_get_list(); list != NULL; list = list->next) {
4499 PrefsAccount *account = list->data;
4500 if (account->protocol == A_IMAP4) {
4501 RemoteFolder *folder = (RemoteFolder *)account->folder;
4502 if (folder && folder->session) {
4503 IMAPSession *session = (IMAPSession *)folder->session;
4504 imap_threaded_disconnect(FOLDER(folder));
4505 SESSION(session)->state = SESSION_DISCONNECTED;
4506 session_destroy(SESSION(session));
4507 folder->session = NULL;
4513 void imap_folder_unref(Folder *folder)
4515 if (((IMAPFolder *)folder)->refcnt > 0)
4516 ((IMAPFolder *)folder)->refcnt--;
4519 #else /* HAVE_LIBETPAN */
4521 static FolderClass imap_class;
4523 static XMLTag *imap_item_get_xml(Folder *folder, FolderItem *item);
4524 static void imap_item_set_xml(Folder *folder, FolderItem *item, XMLTag *tag);
4526 static Folder *imap_folder_new (const gchar *name,
4529 static gboolean missing_imap_warning = TRUE;
4530 if (missing_imap_warning) {
4531 missing_imap_warning = FALSE;
4533 _("You have one or more IMAP accounts "
4534 "defined. However this version of "
4535 "Claws Mail has been built without "
4536 "IMAP support; your IMAP account(s) are "
4538 "You probably need to "
4539 "install libetpan and recompile "
4544 static gint imap_create_tree (Folder *folder)
4548 static FolderItem *imap_create_folder (Folder *folder,
4554 static gint imap_rename_folder (Folder *folder,
4561 gchar imap_get_path_separator_for_item(FolderItem *item)
4566 FolderClass *imap_get_class(void)
4568 if (imap_class.idstr == NULL) {
4569 imap_class.type = F_IMAP;
4570 imap_class.idstr = "imap";
4571 imap_class.uistr = "IMAP4";
4573 imap_class.new_folder = imap_folder_new;
4574 imap_class.create_tree = imap_create_tree;
4575 imap_class.create_folder = imap_create_folder;
4576 imap_class.rename_folder = imap_rename_folder;
4578 imap_class.set_xml = folder_set_xml;
4579 imap_class.get_xml = folder_get_xml;
4580 imap_class.item_set_xml = imap_item_set_xml;
4581 imap_class.item_get_xml = imap_item_get_xml;
4582 /* nothing implemented */
4588 void imap_disconnect_all(void)
4592 gint imap_scan_tree_real(Folder *folder, gboolean subs_only)
4597 gint imap_subscribe(Folder *folder, FolderItem *item, gchar *rpath, gboolean sub)
4602 GList * imap_scan_subtree(Folder *folder, FolderItem *item, gboolean unsubs_only, gboolean recursive)
4608 void imap_synchronise(FolderItem *item)
4610 imap_gtk_synchronise(item);
4613 static void imap_item_set_xml(Folder *folder, FolderItem *item, XMLTag *tag)
4615 #ifdef HAVE_LIBETPAN
4618 folder_item_set_xml(folder, item, tag);
4620 #ifdef HAVE_LIBETPAN
4621 for (cur = tag->attr; cur != NULL; cur = g_list_next(cur)) {
4622 XMLAttr *attr = (XMLAttr *) cur->data;
4624 if (!attr || !attr->name || !attr->value) continue;
4625 if (!strcmp(attr->name, "uidnext"))
4626 IMAP_FOLDER_ITEM(item)->uid_next = atoi(attr->value);
4631 static XMLTag *imap_item_get_xml(Folder *folder, FolderItem *item)
4635 tag = folder_item_get_xml(folder, item);
4637 #ifdef HAVE_LIBETPAN
4638 xml_tag_add_attr(tag, xml_attr_new_int("uidnext",
4639 IMAP_FOLDER_ITEM(item)->uid_next));