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>
56 #include "procheader.h"
57 #include "prefs_account.h"
62 #include "prefs_common.h"
63 #include "inputdialog.h"
65 #include "remotefolder.h"
66 #include "alertpanel.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 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 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);
1294 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
1295 MsgInfo *msginfo = (MsgInfo *)cur->data;
1298 tuples = g_relation_select(uid_mapping,
1299 GINT_TO_POINTER(msginfo->msgnum),
1301 if (tuples->len > 0) {
1302 gint num = GPOINTER_TO_INT(g_tuples_index(tuples, 0, 1));
1303 g_relation_insert(relation, msginfo,
1304 GINT_TO_POINTER(num));
1307 debug_print("copied new message as %d\n", num);
1308 /* put the local file in the imapcache, so that we don't
1309 * have to fetch it back later. */
1311 gchar *cache_path = folder_item_get_path(msginfo->folder);
1312 gchar *real_file = g_strconcat(
1313 cache_path, G_DIR_SEPARATOR_S,
1314 itos(msginfo->msgnum), NULL);
1315 gchar *cache_file = NULL;
1317 cache_path = folder_item_get_path(dest);
1318 cache_file = g_strconcat(
1319 cache_path, G_DIR_SEPARATOR_S,
1321 if (!is_dir_exist(cache_path))
1322 make_dir_hier(cache_path);
1323 if (is_file_exist(real_file) && is_dir_exist(cache_path)) {
1324 copy_file(real_file, cache_file, TRUE);
1325 debug_print("copied to cache: %s\n", cache_file);
1332 g_relation_insert(relation, msginfo,
1333 GINT_TO_POINTER(0));
1334 g_tuples_destroy(tuples);
1336 statusbar_pop_all();
1338 g_relation_destroy(uid_mapping);
1339 imap_lep_set_free(seq_list);
1343 IMAP_FOLDER_ITEM(dest)->lastuid = 0;
1344 IMAP_FOLDER_ITEM(dest)->uid_next = 0;
1345 g_slist_free(IMAP_FOLDER_ITEM(dest)->uid_list);
1346 IMAP_FOLDER_ITEM(dest)->uid_list = NULL;
1349 if (ok == IMAP_SUCCESS)
1355 static gint imap_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1359 g_return_val_if_fail(msginfo != NULL, -1);
1361 msglist.data = msginfo;
1362 msglist.next = NULL;
1364 return imap_copy_msgs(folder, dest, &msglist, NULL);
1367 static gint imap_copy_msgs(Folder *folder, FolderItem *dest,
1368 MsgInfoList *msglist, GRelation *relation)
1373 g_return_val_if_fail(folder != NULL, -1);
1374 g_return_val_if_fail(dest != NULL, -1);
1375 g_return_val_if_fail(msglist != NULL, -1);
1377 msginfo = (MsgInfo *)msglist->data;
1378 g_return_val_if_fail(msginfo->folder != NULL, -1);
1380 ret = imap_do_copy_msgs(folder, dest, msglist, relation);
1385 static gint imap_do_remove_msgs(Folder *folder, FolderItem *dest,
1386 MsgInfoList *msglist, GRelation *relation)
1388 gchar *destdir, *dir;
1389 GSList *numlist = NULL, *cur;
1391 IMAPSession *session;
1392 gint ok = IMAP_SUCCESS;
1393 GRelation *uid_mapping;
1395 g_return_val_if_fail(folder != NULL, -1);
1396 g_return_val_if_fail(dest != NULL, -1);
1397 g_return_val_if_fail(msglist != NULL, -1);
1399 debug_print("getting session...\n");
1400 session = imap_session_get(folder);
1405 msginfo = (MsgInfo *)msglist->data;
1407 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
1408 NULL, NULL, NULL, NULL, FALSE);
1409 if (ok != IMAP_SUCCESS) {
1414 destdir = imap_get_real_path(session, IMAP_FOLDER(folder), dest->path);
1415 for (cur = msglist; cur; cur = cur->next) {
1416 msginfo = (MsgInfo *)cur->data;
1417 if (!MSG_IS_DELETED(msginfo->flags))
1418 numlist = g_slist_prepend(numlist, GINT_TO_POINTER(msginfo->msgnum));
1420 numlist = g_slist_reverse(numlist);
1422 uid_mapping = g_relation_new(2);
1423 g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
1425 ok = imap_set_message_flags
1426 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
1427 numlist, IMAP_FLAG_DELETED, TRUE);
1428 if (ok != IMAP_SUCCESS) {
1429 log_warning(_("can't set deleted flags\n"));
1433 ok = imap_cmd_expunge(session);
1434 if (ok != IMAP_SUCCESS) {
1435 log_warning(_("can't expunge\n"));
1440 dir = folder_item_get_path(msginfo->folder);
1441 if (is_dir_exist(dir)) {
1442 for (cur = msglist; cur; cur = cur->next) {
1443 msginfo = (MsgInfo *)cur->data;
1444 remove_numbered_files(dir, msginfo->msgnum, msginfo->msgnum);
1449 g_relation_destroy(uid_mapping);
1450 g_slist_free(numlist);
1454 if (ok == IMAP_SUCCESS)
1460 static gint imap_remove_msgs(Folder *folder, FolderItem *dest,
1461 MsgInfoList *msglist, GRelation *relation)
1465 g_return_val_if_fail(folder != NULL, -1);
1466 g_return_val_if_fail(dest != NULL, -1);
1467 if (msglist == NULL)
1470 msginfo = (MsgInfo *)msglist->data;
1471 g_return_val_if_fail(msginfo->folder != NULL, -1);
1473 return imap_do_remove_msgs(folder, dest, msglist, relation);
1476 static gint imap_remove_all_msg(Folder *folder, FolderItem *item)
1478 GSList *list = folder_item_get_msg_list(item);
1479 gint res = imap_remove_msgs(folder, item, list, NULL);
1480 procmsg_msg_list_free(list);
1484 static gboolean imap_is_msg_changed(Folder *folder, FolderItem *item,
1487 /* TODO: properly implement this method */
1491 static gint imap_close(Folder *folder, FolderItem *item)
1496 gint imap_scan_tree_real(Folder *folder, gboolean subs_only)
1498 FolderItem *item = NULL;
1499 IMAPSession *session;
1500 gchar *root_folder = NULL;
1502 g_return_val_if_fail(folder != NULL, -1);
1503 g_return_val_if_fail(folder->account != NULL, -1);
1505 debug_print("getting session...\n");
1506 session = imap_session_get(folder);
1508 if (!folder->node) {
1509 folder_tree_destroy(folder);
1510 item = folder_item_new(folder, folder->name, NULL);
1511 item->folder = folder;
1512 folder->node = item->node = g_node_new(item);
1517 if (folder->account->imap_dir && *folder->account->imap_dir) {
1522 Xstrdup_a(root_folder, folder->account->imap_dir, {unlock_session();return -1;});
1523 extract_quote(root_folder, '"');
1524 subst_char(root_folder,
1525 imap_get_path_separator(session, IMAP_FOLDER(folder),
1528 strtailchomp(root_folder, '/');
1529 real_path = imap_get_real_path
1530 (session, IMAP_FOLDER(folder), root_folder);
1531 debug_print("IMAP root directory: %s\n", real_path);
1533 /* check if root directory exist */
1535 r = imap_threaded_list(session->folder, "", real_path,
1537 if ((r != MAILIMAP_NO_ERROR) || (clist_count(lep_list) == 0)) {
1538 if (!folder->node) {
1539 item = folder_item_new(folder, folder->name, NULL);
1540 item->folder = folder;
1541 folder->node = item->node = g_node_new(item);
1546 mailimap_list_result_free(lep_list);
1552 item = FOLDER_ITEM(folder->node->data);
1554 if (item && !item->path && root_folder) {
1555 item->path = g_strdup(root_folder);
1558 if (!item || ((item->path || root_folder) &&
1559 strcmp2(item->path, root_folder) != 0)) {
1560 folder_tree_destroy(folder);
1561 item = folder_item_new(folder, folder->name, root_folder);
1562 item->folder = folder;
1563 folder->node = item->node = g_node_new(item);
1566 imap_scan_tree_recursive(session, FOLDER_ITEM(folder->node->data), subs_only);
1567 imap_create_missing_folders(folder);
1573 static gint imap_scan_tree(Folder *folder)
1575 gboolean subs_only = FALSE;
1576 if (folder->account) {
1577 printf(" scanning only subs %d\n", folder->account->imap_subsonly);
1578 subs_only = folder->account->imap_subsonly;
1580 return imap_scan_tree_real(folder, subs_only);
1583 static gint imap_scan_tree_recursive(IMAPSession *session, FolderItem *item, gboolean subs_only)
1586 IMAPFolder *imapfolder;
1587 FolderItem *new_item;
1588 GSList *item_list, *cur;
1591 gchar *wildcard_path;
1597 g_return_val_if_fail(item != NULL, -1);
1598 g_return_val_if_fail(item->folder != NULL, -1);
1599 g_return_val_if_fail(item->no_sub == FALSE, -1);
1601 folder = item->folder;
1602 imapfolder = IMAP_FOLDER(folder);
1604 separator = imap_get_path_separator(session, imapfolder, item->path);
1606 if (folder->ui_func)
1607 folder->ui_func(folder, item, folder->ui_func_data);
1610 wildcard[0] = separator;
1613 real_path = imap_get_real_path(session, imapfolder, item->path);
1617 real_path = g_strdup("");
1620 Xstrcat_a(wildcard_path, real_path, wildcard,
1621 {g_free(real_path); return IMAP_ERROR;});
1625 r = imap_threaded_lsub(folder, "", wildcard_path, &lep_list);
1627 r = imap_threaded_list(folder, "", wildcard_path, &lep_list);
1629 if (r != MAILIMAP_NO_ERROR) {
1633 item_list = imap_list_from_lep(imapfolder,
1634 lep_list, real_path, FALSE);
1635 mailimap_list_result_free(lep_list);
1640 node = item->node->children;
1641 while (node != NULL) {
1642 FolderItem *old_item = FOLDER_ITEM(node->data);
1643 GNode *next = node->next;
1646 for (cur = item_list; cur != NULL; cur = cur->next) {
1647 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1648 if (!strcmp2(old_item->path, cur_item->path)) {
1649 new_item = cur_item;
1654 if (old_item && old_item->path && !strcmp(old_item->path, "INBOX")) {
1655 debug_print("not removing INBOX\n");
1657 debug_print("folder '%s' not found. removing...\n",
1659 folder_item_remove(old_item);
1662 old_item->no_sub = new_item->no_sub;
1663 old_item->no_select = new_item->no_select;
1664 if (old_item->no_sub == TRUE && node->children) {
1665 debug_print("folder '%s' doesn't have "
1666 "subfolders. removing...\n",
1668 folder_item_remove_children(old_item);
1675 for (cur = item_list; cur != NULL; cur = cur->next) {
1676 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1679 for (node = item->node->children; node != NULL;
1680 node = node->next) {
1681 if (!strcmp2(FOLDER_ITEM(node->data)->path,
1683 new_item = FOLDER_ITEM(node->data);
1684 folder_item_destroy(cur_item);
1690 new_item = cur_item;
1691 debug_print("new folder '%s' found.\n", new_item->path);
1692 folder_item_append(item, new_item);
1695 if (!strcmp(new_item->path, "INBOX")) {
1696 new_item->stype = F_INBOX;
1697 folder->inbox = new_item;
1698 } else if (!folder_item_parent(item) || item->stype == F_INBOX) {
1701 base = g_path_get_basename(new_item->path);
1703 if (!folder->outbox && !g_ascii_strcasecmp(base, "Sent")) {
1704 new_item->stype = F_OUTBOX;
1705 folder->outbox = new_item;
1706 } else if (!folder->draft && !g_ascii_strcasecmp(base, "Drafts")) {
1707 new_item->stype = F_DRAFT;
1708 folder->draft = new_item;
1709 } else if (!folder->queue && !g_ascii_strcasecmp(base, "Queue")) {
1710 new_item->stype = F_QUEUE;
1711 folder->queue = new_item;
1712 } else if (!folder->trash && !g_ascii_strcasecmp(base, "Trash")) {
1713 new_item->stype = F_TRASH;
1714 folder->trash = new_item;
1719 if (new_item->no_sub == FALSE)
1720 imap_scan_tree_recursive(session, new_item, subs_only);
1723 g_slist_free(item_list);
1725 return IMAP_SUCCESS;
1728 gint imap_scan_subtree(Folder *folder, FolderItem *item, gboolean subs_only)
1730 IMAPSession *session = imap_session_get(folder);
1733 return imap_scan_tree_recursive(session, item, subs_only);
1736 static gint imap_create_tree(Folder *folder)
1738 g_return_val_if_fail(folder != NULL, -1);
1739 g_return_val_if_fail(folder->node != NULL, -1);
1740 g_return_val_if_fail(folder->node->data != NULL, -1);
1741 g_return_val_if_fail(folder->account != NULL, -1);
1743 imap_scan_tree(folder);
1744 imap_create_missing_folders(folder);
1749 static void imap_create_missing_folders(Folder *folder)
1751 g_return_if_fail(folder != NULL);
1754 folder->inbox = imap_create_special_folder
1755 (folder, F_INBOX, "INBOX");
1757 folder->trash = imap_create_special_folder
1758 (folder, F_TRASH, "Trash");
1760 folder->queue = imap_create_special_folder
1761 (folder, F_QUEUE, "Queue");
1762 if (!folder->outbox)
1763 folder->outbox = imap_create_special_folder
1764 (folder, F_OUTBOX, "Sent");
1766 folder->draft = imap_create_special_folder
1767 (folder, F_DRAFT, "Drafts");
1770 static FolderItem *imap_create_special_folder(Folder *folder,
1771 SpecialFolderItemType stype,
1775 FolderItem *new_item;
1777 g_return_val_if_fail(folder != NULL, NULL);
1778 g_return_val_if_fail(folder->node != NULL, NULL);
1779 g_return_val_if_fail(folder->node->data != NULL, NULL);
1780 g_return_val_if_fail(folder->account != NULL, NULL);
1781 g_return_val_if_fail(name != NULL, NULL);
1783 item = FOLDER_ITEM(folder->node->data);
1784 new_item = imap_create_folder(folder, item, name);
1787 g_warning("Can't create '%s'\n", name);
1788 if (!folder->inbox) return NULL;
1790 new_item = imap_create_folder(folder, folder->inbox, name);
1792 g_warning("Can't create '%s' under INBOX\n", name);
1794 new_item->stype = stype;
1796 new_item->stype = stype;
1801 static gchar *imap_folder_get_path(Folder *folder)
1805 g_return_val_if_fail(folder != NULL, NULL);
1806 g_return_val_if_fail(folder->account != NULL, NULL);
1808 folder_path = g_strconcat(get_imap_cache_dir(),
1810 folder->account->recv_server,
1812 folder->account->userid,
1818 static gchar *imap_item_get_path(Folder *folder, FolderItem *item)
1820 gchar *folder_path, *path;
1822 g_return_val_if_fail(folder != NULL, NULL);
1823 g_return_val_if_fail(item != NULL, NULL);
1824 folder_path = imap_folder_get_path(folder);
1826 g_return_val_if_fail(folder_path != NULL, NULL);
1827 if (folder_path[0] == G_DIR_SEPARATOR) {
1829 path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
1832 path = g_strdup(folder_path);
1835 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1836 folder_path, G_DIR_SEPARATOR_S,
1839 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1842 g_free(folder_path);
1847 static FolderItem *imap_create_folder(Folder *folder, FolderItem *parent,
1850 gchar *dirpath, *imap_path;
1851 IMAPSession *session;
1852 FolderItem *new_item;
1857 gboolean no_select = FALSE, no_sub = FALSE;
1858 gboolean exist = FALSE;
1860 g_return_val_if_fail(folder != NULL, NULL);
1861 g_return_val_if_fail(folder->account != NULL, NULL);
1862 g_return_val_if_fail(parent != NULL, NULL);
1863 g_return_val_if_fail(name != NULL, NULL);
1865 debug_print("getting session...\n");
1866 session = imap_session_get(folder);
1871 if (!folder_item_parent(parent) && strcmp(name, "INBOX") == 0) {
1872 dirpath = g_strdup(name);
1873 }else if (parent->path)
1874 dirpath = g_strconcat(parent->path, "/", name, NULL);
1875 else if ((p = strchr(name, '/')) != NULL && *(p + 1) != '\0')
1876 dirpath = g_strdup(name);
1877 else if (folder->account->imap_dir && *folder->account->imap_dir) {
1880 Xstrdup_a(imap_dir, folder->account->imap_dir, {unlock_session();return NULL;});
1881 strtailchomp(imap_dir, '/');
1882 dirpath = g_strconcat(imap_dir, "/", name, NULL);
1884 dirpath = g_strdup(name);
1888 /* keep trailing directory separator to create a folder that contains
1890 imap_path = imap_utf8_to_modified_utf7(dirpath);
1892 strtailchomp(dirpath, '/');
1893 Xstrdup_a(new_name, name, {
1898 separator = imap_get_path_separator(session, IMAP_FOLDER(folder), imap_path);
1899 imap_path_separator_subst(imap_path, separator);
1900 /* remove trailing / for display */
1901 strtailchomp(new_name, '/');
1903 if (strcmp(dirpath, "INBOX") != 0) {
1908 argbuf = g_ptr_array_new();
1909 r = imap_threaded_list(folder, "", imap_path, &lep_list);
1910 if (r != MAILIMAP_NO_ERROR) {
1911 log_warning(_("can't create mailbox: LIST failed\n"));
1914 ptr_array_free_strings(argbuf);
1915 g_ptr_array_free(argbuf, TRUE);
1920 if (clist_count(lep_list) > 0)
1922 mailimap_list_result_free(lep_list);
1925 ok = imap_cmd_create(session, imap_path);
1926 if (ok != IMAP_SUCCESS) {
1927 log_warning(_("can't create mailbox\n"));
1933 r = imap_threaded_list(folder, "", imap_path, &lep_list);
1934 if (r == MAILIMAP_NO_ERROR) {
1935 GSList *item_list = imap_list_from_lep(IMAP_FOLDER(folder),
1936 lep_list, dirpath, TRUE);
1938 FolderItem *cur_item = FOLDER_ITEM(item_list->data);
1939 no_select = cur_item->no_select;
1940 no_sub = cur_item->no_sub;
1941 g_slist_free(item_list);
1943 mailimap_list_result_free(lep_list);
1946 imap_threaded_subscribe(folder, imap_path, TRUE);
1950 /* just get flags */
1951 r = imap_threaded_list(folder, "", "INBOX", &lep_list);
1952 if (r == MAILIMAP_NO_ERROR) {
1953 GSList *item_list = imap_list_from_lep(IMAP_FOLDER(folder),
1954 lep_list, dirpath, TRUE);
1956 FolderItem *cur_item = FOLDER_ITEM(item_list->data);
1957 no_select = cur_item->no_select;
1958 no_sub = cur_item->no_sub;
1959 g_slist_free(item_list);
1961 mailimap_list_result_free(lep_list);
1965 new_item = folder_item_new(folder, new_name, dirpath);
1966 new_item->no_select = no_select;
1967 new_item->no_sub = no_sub;
1968 folder_item_append(parent, new_item);
1972 dirpath = folder_item_get_path(new_item);
1973 if (!is_dir_exist(dirpath))
1974 make_dir_hier(dirpath);
1979 /* folder existed, scan it */
1980 folder_item_scan_full(new_item, FALSE);
1986 static gint imap_rename_folder(Folder *folder, FolderItem *item,
1991 gchar *real_oldpath;
1992 gchar *real_newpath;
1994 gchar *old_cache_dir;
1995 gchar *new_cache_dir;
1996 IMAPSession *session;
1999 gint exists, recent, unseen;
2000 guint32 uid_validity;
2002 g_return_val_if_fail(folder != NULL, -1);
2003 g_return_val_if_fail(item != NULL, -1);
2004 g_return_val_if_fail(item->path != NULL, -1);
2005 g_return_val_if_fail(name != NULL, -1);
2007 debug_print("getting session...\n");
2008 session = imap_session_get(folder);
2013 if (strchr(name, imap_get_path_separator(session, IMAP_FOLDER(folder), item->path)) != NULL) {
2014 g_warning(_("New folder name must not contain the namespace "
2020 real_oldpath = imap_get_real_path(session, IMAP_FOLDER(folder), item->path);
2022 g_free(session->mbox);
2023 session->mbox = NULL;
2024 ok = imap_cmd_examine(session, "INBOX",
2025 &exists, &recent, &unseen, &uid_validity, FALSE);
2026 if (ok != IMAP_SUCCESS) {
2027 g_free(real_oldpath);
2032 separator = imap_get_path_separator(session, IMAP_FOLDER(folder), item->path);
2033 if (strchr(item->path, G_DIR_SEPARATOR)) {
2034 dirpath = g_path_get_dirname(item->path);
2035 newpath = g_strconcat(dirpath, G_DIR_SEPARATOR_S, name, NULL);
2038 newpath = g_strdup(name);
2040 real_newpath = imap_utf8_to_modified_utf7(newpath);
2041 imap_path_separator_subst(real_newpath, separator);
2043 ok = imap_cmd_rename(session, real_oldpath, real_newpath);
2044 if (ok != IMAP_SUCCESS) {
2045 log_warning(_("can't rename mailbox: %s to %s\n"),
2046 real_oldpath, real_newpath);
2047 g_free(real_oldpath);
2049 g_free(real_newpath);
2055 item->name = g_strdup(name);
2057 old_cache_dir = folder_item_get_path(item);
2059 paths[0] = g_strdup(item->path);
2061 g_node_traverse(item->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
2062 imap_rename_folder_func, paths);
2064 if (is_dir_exist(old_cache_dir)) {
2065 new_cache_dir = folder_item_get_path(item);
2066 if (rename(old_cache_dir, new_cache_dir) < 0) {
2067 FILE_OP_ERROR(old_cache_dir, "rename");
2069 g_free(new_cache_dir);
2072 g_free(old_cache_dir);
2075 g_free(real_oldpath);
2076 g_free(real_newpath);
2081 gint imap_subscribe(Folder *folder, FolderItem *item, gboolean sub)
2085 IMAPSession *session;
2086 debug_print("getting session...\n");
2088 session = imap_session_get(folder);
2089 if (!session || !item->path) {
2093 path = imap_get_real_path(session, IMAP_FOLDER(folder), item->path);
2096 if (!strcmp(path, "INBOX") && sub == FALSE)
2098 debug_print("%ssubscribing %s\n", sub?"":"un", path);
2099 r = imap_threaded_subscribe(folder, path, sub);
2104 static gint imap_remove_folder_real(Folder *folder, FolderItem *item)
2107 IMAPSession *session;
2111 g_return_val_if_fail(folder != NULL, -1);
2112 g_return_val_if_fail(item != NULL, -1);
2113 g_return_val_if_fail(item->path != NULL, -1);
2115 debug_print("getting session...\n");
2116 session = imap_session_get(folder);
2120 path = imap_get_real_path(session, IMAP_FOLDER(folder), item->path);
2122 imap_threaded_subscribe(folder, path, FALSE);
2123 ok = imap_cmd_delete(session, path);
2124 if (ok != IMAP_SUCCESS) {
2125 gchar *tmp = g_strdup_printf("%s%c", path,
2126 imap_get_path_separator(session, IMAP_FOLDER(folder), path));
2129 ok = imap_cmd_delete(session, path);
2132 if (ok != IMAP_SUCCESS) {
2133 log_warning(_("can't delete mailbox\n"));
2140 cache_dir = folder_item_get_path(item);
2141 if (is_dir_exist(cache_dir) && remove_dir_recursive(cache_dir) < 0)
2142 g_warning("can't remove directory '%s'\n", cache_dir);
2144 folder_item_remove(item);
2149 static gint imap_remove_folder(Folder *folder, FolderItem *item)
2153 g_return_val_if_fail(item != NULL, -1);
2154 g_return_val_if_fail(item->folder != NULL, -1);
2155 g_return_val_if_fail(item->node != NULL, -1);
2157 node = item->node->children;
2158 while (node != NULL) {
2160 if (imap_remove_folder(folder, FOLDER_ITEM(node->data)) < 0)
2164 debug_print("IMAP removing %s\n", item->path);
2166 if (imap_remove_all_msg(folder, item) < 0)
2168 return imap_remove_folder_real(folder, item);
2171 typedef struct _uncached_data {
2172 IMAPSession *session;
2174 MsgNumberList *numlist;
2180 static void *imap_get_uncached_messages_thread(void *data)
2182 uncached_data *stuff = (uncached_data *)data;
2183 IMAPSession *session = stuff->session;
2184 FolderItem *item = stuff->item;
2185 MsgNumberList *numlist = stuff->numlist;
2187 GSList *newlist = NULL;
2188 GSList *llast = NULL;
2189 GSList *seq_list, *cur;
2191 debug_print("uncached_messages\n");
2193 if (session == NULL || item == NULL || item->folder == NULL
2194 || FOLDER_CLASS(item->folder) != &imap_class) {
2199 seq_list = imap_get_lep_set_from_numlist(numlist);
2200 debug_print("get msgs info\n");
2201 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
2202 struct mailimap_set * imapset;
2208 imapset = cur->data;
2210 r = imap_threaded_fetch_env(session->folder,
2211 imapset, &env_list);
2212 if (r != MAILIMAP_NO_ERROR)
2215 session_set_access_time(SESSION(session));
2218 for(i = 0 ; i < carray_count(env_list) ; i ++) {
2219 struct imap_fetch_env_info * info;
2222 info = carray_get(env_list, i);
2223 msginfo = imap_envelope_from_lep(info, item);
2224 if (msginfo == NULL)
2226 msginfo->folder = item;
2228 llast = newlist = g_slist_append(newlist, msginfo);
2230 llast = g_slist_append(llast, msginfo);
2231 llast = llast->next;
2236 imap_fetch_env_free(env_list);
2239 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
2240 struct mailimap_set * imapset;
2242 imapset = cur->data;
2243 mailimap_set_free(imapset);
2246 session_set_access_time(SESSION(session));
2251 #define MAX_MSG_NUM 50
2253 static GSList *imap_get_uncached_messages(IMAPSession *session,
2255 MsgNumberList *numlist)
2257 GSList *result = NULL;
2259 uncached_data *data = g_new0(uncached_data, 1);
2264 data->total = g_slist_length(numlist);
2265 debug_print("messages list : %i\n", data->total);
2267 while (cur != NULL) {
2268 GSList * partial_result;
2276 while (count < MAX_MSG_NUM) {
2281 if (newlist == NULL)
2282 llast = newlist = g_slist_append(newlist, p);
2284 llast = g_slist_append(llast, p);
2285 llast = llast->next;
2295 data->session = session;
2297 data->numlist = newlist;
2300 if (prefs_common.work_offline &&
2301 !inc_offline_should_override(
2302 _("Claws Mail needs network access in order "
2303 "to access the IMAP server."))) {
2309 (GSList *)imap_get_uncached_messages_thread(data);
2311 statusbar_progress_all(data->cur,data->total, 1);
2313 g_slist_free(newlist);
2315 result = g_slist_concat(result, partial_result);
2319 statusbar_progress_all(0,0,0);
2320 statusbar_pop_all();
2325 static void imap_delete_all_cached_messages(FolderItem *item)
2329 g_return_if_fail(item != NULL);
2330 g_return_if_fail(item->folder != NULL);
2331 g_return_if_fail(FOLDER_CLASS(item->folder) == &imap_class);
2333 debug_print("Deleting all cached messages...\n");
2335 dir = folder_item_get_path(item);
2336 if (is_dir_exist(dir))
2337 remove_all_numbered_files(dir);
2340 debug_print("done.\n");
2343 gchar imap_get_path_separator_for_item(FolderItem *item)
2345 Folder *folder = NULL;
2346 IMAPFolder *imap_folder = NULL;
2347 IMAPSession *session = NULL;
2352 folder = item->folder;
2357 imap_folder = IMAP_FOLDER(folder);
2362 debug_print("getting session...");
2363 session = imap_session_get(FOLDER(folder));
2364 result = imap_get_path_separator(session, imap_folder, item->path);
2369 static gchar imap_refresh_path_separator(IMAPSession *session, IMAPFolder *folder, const gchar *subfolder)
2373 gchar separator = '\0';
2375 g_return_val_if_fail(session != NULL, '/');
2376 r = imap_threaded_list((Folder *)folder, "", subfolder, &lep_list);
2378 if (r != MAILIMAP_NO_ERROR) {
2379 log_warning(_("LIST failed\n"));
2383 if (clist_count(lep_list) > 0) {
2384 clistiter * iter = clist_begin(lep_list);
2385 struct mailimap_mailbox_list * mb;
2386 mb = clist_content(iter);
2388 separator = mb->mb_delimiter;
2389 debug_print("got separator: %c\n", folder->last_seen_separator);
2391 mailimap_list_result_free(lep_list);
2395 static gchar imap_get_path_separator(IMAPSession *session, IMAPFolder *folder, const gchar *path)
2397 gchar separator = '/';
2399 if (folder->last_seen_separator == 0) {
2400 folder->last_seen_separator = imap_refresh_path_separator(session, folder, "");
2403 if (folder->last_seen_separator == 0) {
2404 folder->last_seen_separator = imap_refresh_path_separator(session, folder, "INBOX");
2407 if (folder->last_seen_separator != 0) {
2408 debug_print("using separator: %c\n", folder->last_seen_separator);
2409 return folder->last_seen_separator;
2415 static gchar *imap_get_real_path(IMAPSession *session, IMAPFolder *folder, const gchar *path)
2420 g_return_val_if_fail(folder != NULL, NULL);
2421 g_return_val_if_fail(path != NULL, NULL);
2423 real_path = imap_utf8_to_modified_utf7(path);
2424 separator = imap_get_path_separator(session, folder, path);
2425 imap_path_separator_subst(real_path, separator);
2430 static gint imap_set_message_flags(IMAPSession *session,
2431 MsgNumberList *numlist,
2439 seq_list = imap_get_lep_set_from_numlist(numlist);
2441 for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) {
2442 struct mailimap_set * imapset;
2444 imapset = cur->data;
2446 ok = imap_cmd_store(session, imapset,
2450 imap_lep_set_free(seq_list);
2452 return IMAP_SUCCESS;
2455 typedef struct _select_data {
2456 IMAPSession *session;
2461 guint32 *uid_validity;
2465 static gint imap_select(IMAPSession *session, IMAPFolder *folder,
2467 gint *exists, gint *recent, gint *unseen,
2468 guint32 *uid_validity, gboolean block)
2472 gint exists_, recent_, unseen_;
2473 guint32 uid_validity_;
2475 if (!exists && !recent && !unseen && !uid_validity) {
2476 if (session->mbox && strcmp(session->mbox, path) == 0)
2477 return IMAP_SUCCESS;
2486 uid_validity = &uid_validity_;
2488 g_free(session->mbox);
2489 session->mbox = NULL;
2491 real_path = imap_get_real_path(session, folder, path);
2493 ok = imap_cmd_select(session, real_path,
2494 exists, recent, unseen, uid_validity, block);
2495 if (ok != IMAP_SUCCESS)
2496 log_warning(_("can't select folder: %s\n"), real_path);
2498 session->mbox = g_strdup(path);
2499 session->folder_content_changed = FALSE;
2506 static gint imap_status(IMAPSession *session, IMAPFolder *folder,
2507 const gchar *path, IMAPFolderItem *item,
2509 guint32 *uid_next, guint32 *uid_validity,
2510 gint *unseen, gboolean block)
2514 struct mailimap_mailbox_data_status * data_status;
2519 real_path = imap_get_real_path(session, folder, path);
2533 r = imap_threaded_status(FOLDER(folder), real_path,
2534 &data_status, mask);
2537 if (r != MAILIMAP_NO_ERROR) {
2538 debug_print("status err %d\n", r);
2542 if (data_status->st_info_list == NULL) {
2543 mailimap_mailbox_data_status_free(data_status);
2544 debug_print("status->st_info_list == NULL\n");
2549 for(iter = clist_begin(data_status->st_info_list) ; iter != NULL ;
2550 iter = clist_next(iter)) {
2551 struct mailimap_status_info * info;
2553 info = clist_content(iter);
2554 switch (info->st_att) {
2555 case MAILIMAP_STATUS_ATT_MESSAGES:
2556 * messages = info->st_value;
2557 got_values |= 1 << 0;
2560 case MAILIMAP_STATUS_ATT_UIDNEXT:
2561 * uid_next = info->st_value;
2562 got_values |= 1 << 2;
2565 case MAILIMAP_STATUS_ATT_UIDVALIDITY:
2566 * uid_validity = info->st_value;
2567 got_values |= 1 << 3;
2570 case MAILIMAP_STATUS_ATT_UNSEEN:
2571 * unseen = info->st_value;
2572 got_values |= 1 << 4;
2576 mailimap_mailbox_data_status_free(data_status);
2578 if (got_values != mask) {
2579 debug_print("status: incomplete values received (%d)\n", got_values);
2582 return IMAP_SUCCESS;
2585 static void imap_free_capabilities(IMAPSession *session)
2587 slist_free_strings(session->capability);
2588 g_slist_free(session->capability);
2589 session->capability = NULL;
2592 /* low-level IMAP4rev1 commands */
2594 static gint imap_cmd_login(IMAPSession *session,
2595 const gchar *user, const gchar *pass,
2601 if (!strcmp(type, "LOGIN") && imap_has_capability(session, "LOGINDISABLED")) {
2602 gint ok = IMAP_ERROR;
2603 if (imap_has_capability(session, "STARTTLS")) {
2605 log_warning(_("Server requires TLS to log in.\n"));
2606 ok = imap_cmd_starttls(session);
2607 if (ok != IMAP_SUCCESS) {
2608 log_warning(_("Can't start TLS session.\n"));
2612 imap_free_capabilities(session);
2613 if (imap_get_capabilities(session) != MAILIMAP_NO_ERROR) {
2614 log_warning(_("Can't refresh capabilities.\n"));
2619 log_error(_("Connection to %s failed: "
2620 "server requires TLS, but Claws Mail "
2621 "has been compiled without OpenSSL "
2623 SESSION(session)->server);
2627 log_error(_("Server logins are disabled.\n"));
2632 log_print("IMAP4> Logging %s to %s using %s\n",
2634 SESSION(session)->server,
2636 r = imap_threaded_login(session->folder, user, pass, type);
2637 if (r != MAILIMAP_NO_ERROR) {
2638 log_print("IMAP4< Error logging in to %s\n",
2639 SESSION(session)->server);
2642 log_print("IMAP4< Login to %s successful\n",
2643 SESSION(session)->server);
2649 static gint imap_cmd_noop(IMAPSession *session)
2652 unsigned int exists;
2654 r = imap_threaded_noop(session->folder, &exists);
2655 if (r != MAILIMAP_NO_ERROR) {
2656 debug_print("noop err %d\n", r);
2659 session->exists = exists;
2660 session_set_access_time(SESSION(session));
2662 return IMAP_SUCCESS;
2666 static gint imap_cmd_starttls(IMAPSession *session)
2670 r = imap_threaded_starttls(session->folder,
2671 SESSION(session)->server, SESSION(session)->port);
2672 if (r != MAILIMAP_NO_ERROR) {
2673 debug_print("starttls err %d\n", r);
2676 return IMAP_SUCCESS;
2680 static gint imap_cmd_select(IMAPSession *session, const gchar *folder,
2681 gint *exists, gint *recent, gint *unseen,
2682 guint32 *uid_validity, gboolean block)
2686 r = imap_threaded_select(session->folder, folder,
2687 exists, recent, unseen, uid_validity);
2688 if (r != MAILIMAP_NO_ERROR) {
2689 debug_print("select err %d\n", r);
2692 return IMAP_SUCCESS;
2695 static gint imap_cmd_examine(IMAPSession *session, const gchar *folder,
2696 gint *exists, gint *recent, gint *unseen,
2697 guint32 *uid_validity, gboolean block)
2701 r = imap_threaded_examine(session->folder, folder,
2702 exists, recent, unseen, uid_validity);
2703 if (r != MAILIMAP_NO_ERROR) {
2704 debug_print("examine err %d\n", r);
2708 return IMAP_SUCCESS;
2711 static gint imap_cmd_create(IMAPSession *session, const gchar *folder)
2715 r = imap_threaded_create(session->folder, folder);
2716 if (r != MAILIMAP_NO_ERROR) {
2721 return IMAP_SUCCESS;
2724 static gint imap_cmd_rename(IMAPSession *session, const gchar *old_folder,
2725 const gchar *new_folder)
2729 r = imap_threaded_rename(session->folder, old_folder,
2731 if (r != MAILIMAP_NO_ERROR) {
2736 return IMAP_SUCCESS;
2739 static gint imap_cmd_delete(IMAPSession *session, const gchar *folder)
2744 r = imap_threaded_delete(session->folder, folder);
2745 if (r != MAILIMAP_NO_ERROR) {
2750 return IMAP_SUCCESS;
2753 typedef struct _fetch_data {
2754 IMAPSession *session;
2756 const gchar *filename;
2762 static void *imap_cmd_fetch_thread(void *data)
2764 fetch_data *stuff = (fetch_data *)data;
2765 IMAPSession *session = stuff->session;
2766 guint32 uid = stuff->uid;
2767 const gchar *filename = stuff->filename;
2771 r = imap_threaded_fetch_content(session->folder,
2775 r = imap_threaded_fetch_content(session->folder,
2778 if (r != MAILIMAP_NO_ERROR) {
2779 debug_print("fetch err %d\n", r);
2780 return GINT_TO_POINTER(IMAP_ERROR);
2782 return GINT_TO_POINTER(IMAP_SUCCESS);
2785 static gint imap_cmd_fetch(IMAPSession *session, guint32 uid,
2786 const gchar *filename, gboolean headers,
2789 fetch_data *data = g_new0(fetch_data, 1);
2792 data->session = session;
2794 data->filename = filename;
2795 data->headers = headers;
2798 if (prefs_common.work_offline &&
2799 !inc_offline_should_override(
2800 _("Claws Mail needs network access in order "
2801 "to access the IMAP server."))) {
2805 statusbar_print_all(_("Fetching message..."));
2806 result = GPOINTER_TO_INT(imap_cmd_fetch_thread(data));
2807 statusbar_pop_all();
2813 static gint imap_cmd_append(IMAPSession *session, const gchar *destfolder,
2814 const gchar *file, IMAPFlags flags,
2817 struct mailimap_flag_list * flag_list;
2820 g_return_val_if_fail(file != NULL, IMAP_ERROR);
2822 flag_list = imap_flag_to_lep(flags);
2823 r = imap_threaded_append(session->folder, destfolder,
2824 file, flag_list, (int *)new_uid);
2825 mailimap_flag_list_free(flag_list);
2827 if (r != MAILIMAP_NO_ERROR) {
2828 debug_print("append err %d\n", r);
2831 return IMAP_SUCCESS;
2834 static gint imap_cmd_copy(IMAPSession *session, struct mailimap_set * set,
2835 const gchar *destfolder, GRelation *uid_mapping,
2836 struct mailimap_set **source, struct mailimap_set **dest)
2840 g_return_val_if_fail(session != NULL, IMAP_ERROR);
2841 g_return_val_if_fail(set != NULL, IMAP_ERROR);
2842 g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
2844 r = imap_threaded_copy(session->folder, set, destfolder, source, dest);
2845 if (r != MAILIMAP_NO_ERROR) {
2850 return IMAP_SUCCESS;
2853 static gint imap_cmd_store(IMAPSession *session, struct mailimap_set * set,
2854 IMAPFlags flags, int do_add)
2857 struct mailimap_flag_list * flag_list;
2858 struct mailimap_store_att_flags * store_att_flags;
2860 flag_list = imap_flag_to_lep(flags);
2864 mailimap_store_att_flags_new_add_flags_silent(flag_list);
2867 mailimap_store_att_flags_new_remove_flags_silent(flag_list);
2869 r = imap_threaded_store(session->folder, set, store_att_flags);
2870 mailimap_store_att_flags_free(store_att_flags);
2871 if (r != MAILIMAP_NO_ERROR) {
2876 return IMAP_SUCCESS;
2879 static gint imap_cmd_expunge(IMAPSession *session)
2883 if (prefs_common.work_offline &&
2884 !inc_offline_should_override(
2885 _("Claws Mail needs network access in order "
2886 "to access the IMAP server."))) {
2890 r = imap_threaded_expunge(session->folder);
2891 if (r != MAILIMAP_NO_ERROR) {
2896 return IMAP_SUCCESS;
2899 static void imap_path_separator_subst(gchar *str, gchar separator)
2902 gboolean in_escape = FALSE;
2904 if (!separator || separator == '/') return;
2906 for (p = str; *p != '\0'; p++) {
2907 if (*p == '/' && !in_escape)
2909 else if (*p == '&' && *(p + 1) != '-' && !in_escape)
2911 else if (*p == '-' && in_escape)
2916 static gchar *imap_modified_utf7_to_utf8(const gchar *mutf7_str)
2918 static iconv_t cd = (iconv_t)-1;
2919 static gboolean iconv_ok = TRUE;
2922 size_t norm_utf7_len;
2924 gchar *to_str, *to_p;
2926 gboolean in_escape = FALSE;
2928 if (!iconv_ok) return g_strdup(mutf7_str);
2930 if (cd == (iconv_t)-1) {
2931 cd = iconv_open(CS_INTERNAL, CS_UTF_7);
2932 if (cd == (iconv_t)-1) {
2933 g_warning("iconv cannot convert UTF-7 to %s\n",
2936 return g_strdup(mutf7_str);
2940 /* modified UTF-7 to normal UTF-7 conversion */
2941 norm_utf7 = g_string_new(NULL);
2943 for (p = mutf7_str; *p != '\0'; p++) {
2944 /* replace: '&' -> '+',
2946 escaped ',' -> '/' */
2947 if (!in_escape && *p == '&') {
2948 if (*(p + 1) != '-') {
2949 g_string_append_c(norm_utf7, '+');
2952 g_string_append_c(norm_utf7, '&');
2955 } else if (in_escape && *p == ',') {
2956 g_string_append_c(norm_utf7, '/');
2957 } else if (in_escape && *p == '-') {
2958 g_string_append_c(norm_utf7, '-');
2961 g_string_append_c(norm_utf7, *p);
2965 norm_utf7_p = norm_utf7->str;
2966 norm_utf7_len = norm_utf7->len;
2967 to_len = strlen(mutf7_str) * 5;
2968 to_p = to_str = g_malloc(to_len + 1);
2970 if (iconv(cd, (ICONV_CONST gchar **)&norm_utf7_p, &norm_utf7_len,
2971 &to_p, &to_len) == -1) {
2972 g_warning(_("iconv cannot convert UTF-7 to %s\n"),
2973 conv_get_locale_charset_str());
2974 g_string_free(norm_utf7, TRUE);
2976 return g_strdup(mutf7_str);
2979 /* second iconv() call for flushing */
2980 iconv(cd, NULL, NULL, &to_p, &to_len);
2981 g_string_free(norm_utf7, TRUE);
2987 static gchar *imap_utf8_to_modified_utf7(const gchar *from)
2989 static iconv_t cd = (iconv_t)-1;
2990 static gboolean iconv_ok = TRUE;
2991 gchar *norm_utf7, *norm_utf7_p;
2992 size_t from_len, norm_utf7_len;
2994 gchar *from_tmp, *to, *p;
2995 gboolean in_escape = FALSE;
2997 if (!iconv_ok) return g_strdup(from);
2999 if (cd == (iconv_t)-1) {
3000 cd = iconv_open(CS_UTF_7, CS_INTERNAL);
3001 if (cd == (iconv_t)-1) {
3002 g_warning(_("iconv cannot convert %s to UTF-7\n"),
3005 return g_strdup(from);
3009 /* UTF-8 to normal UTF-7 conversion */
3010 Xstrdup_a(from_tmp, from, return g_strdup(from));
3011 from_len = strlen(from);
3012 norm_utf7_len = from_len * 5;
3013 Xalloca(norm_utf7, norm_utf7_len + 1, return g_strdup(from));
3014 norm_utf7_p = norm_utf7;
3016 #define IS_PRINT(ch) (isprint(ch) && IS_ASCII(ch))
3018 while (from_len > 0) {
3019 if (*from_tmp == '+') {
3020 *norm_utf7_p++ = '+';
3021 *norm_utf7_p++ = '-';
3025 } else if (IS_PRINT(*(guchar *)from_tmp)) {
3026 /* printable ascii char */
3027 *norm_utf7_p = *from_tmp;
3033 size_t conv_len = 0;
3035 /* unprintable char: convert to UTF-7 */
3037 while (!IS_PRINT(*(guchar *)p) && conv_len < from_len) {
3038 conv_len += g_utf8_skip[*(guchar *)p];
3039 p += g_utf8_skip[*(guchar *)p];
3042 from_len -= conv_len;
3043 if (iconv(cd, (ICONV_CONST gchar **)&from_tmp,
3045 &norm_utf7_p, &norm_utf7_len) == -1) {
3046 g_warning(_("iconv cannot convert UTF-8 to UTF-7\n"));
3047 return g_strdup(from);
3050 /* second iconv() call for flushing */
3051 iconv(cd, NULL, NULL, &norm_utf7_p, &norm_utf7_len);
3057 *norm_utf7_p = '\0';
3058 to_str = g_string_new(NULL);
3059 for (p = norm_utf7; p < norm_utf7_p; p++) {
3060 /* replace: '&' -> "&-",
3063 BASE64 '/' -> ',' */
3064 if (!in_escape && *p == '&') {
3065 g_string_append(to_str, "&-");
3066 } else if (!in_escape && *p == '+') {
3067 if (*(p + 1) == '-') {
3068 g_string_append_c(to_str, '+');
3071 g_string_append_c(to_str, '&');
3074 } else if (in_escape && *p == '/') {
3075 g_string_append_c(to_str, ',');
3076 } else if (in_escape && *p == '-') {
3077 g_string_append_c(to_str, '-');
3080 g_string_append_c(to_str, *p);
3086 g_string_append_c(to_str, '-');
3090 g_string_free(to_str, FALSE);
3095 static gboolean imap_rename_folder_func(GNode *node, gpointer data)
3097 FolderItem *item = node->data;
3098 gchar **paths = data;
3099 const gchar *oldpath = paths[0];
3100 const gchar *newpath = paths[1];
3102 gchar *new_itempath;
3105 oldpathlen = strlen(oldpath);
3106 if (strncmp(oldpath, item->path, oldpathlen) != 0) {
3107 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
3111 base = item->path + oldpathlen;
3112 while (*base == G_DIR_SEPARATOR) base++;
3114 new_itempath = g_strdup(newpath);
3116 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
3119 item->path = new_itempath;
3124 typedef struct _get_list_uid_data {
3126 IMAPSession *session;
3127 IMAPFolderItem *item;
3128 GSList **msgnum_list;
3130 } get_list_uid_data;
3132 static void *get_list_of_uids_thread(void *data)
3134 get_list_uid_data *stuff = (get_list_uid_data *)data;
3135 Folder *folder = stuff->folder;
3136 IMAPFolderItem *item = stuff->item;
3137 GSList **msgnum_list = stuff->msgnum_list;
3138 gint ok, nummsgs = 0, lastuid_old;
3139 IMAPSession *session;
3140 GSList *uidlist, *elem;
3141 clist * lep_uidlist;
3144 session = stuff->session;
3145 if (session == NULL) {
3147 return GINT_TO_POINTER(-1);
3149 /* no session locking here, it's already locked by caller */
3150 ok = imap_select(session, IMAP_FOLDER(folder), item->item.path,
3151 NULL, NULL, NULL, NULL, TRUE);
3152 if (ok != IMAP_SUCCESS) {
3154 return GINT_TO_POINTER(-1);
3159 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SIMPLE, NULL,
3162 if (r == MAILIMAP_NO_ERROR) {
3163 GSList * fetchuid_list;
3166 imap_uid_list_from_lep(lep_uidlist);
3167 mailimap_search_result_free(lep_uidlist);
3169 uidlist = g_slist_concat(fetchuid_list, uidlist);
3172 GSList * fetchuid_list;
3173 carray * lep_uidtab;
3175 r = imap_threaded_fetch_uid(folder, item->lastuid + 1,
3177 if (r == MAILIMAP_NO_ERROR) {
3179 imap_uid_list_from_lep_tab(lep_uidtab);
3180 imap_fetch_uid_list_free(lep_uidtab);
3181 uidlist = g_slist_concat(fetchuid_list, uidlist);
3185 lastuid_old = item->lastuid;
3186 *msgnum_list = g_slist_copy(item->uid_list);
3187 nummsgs = g_slist_length(*msgnum_list);
3188 debug_print("Got %d uids from cache\n", g_slist_length(item->uid_list));
3190 for (elem = uidlist; elem != NULL; elem = g_slist_next(elem)) {
3193 msgnum = GPOINTER_TO_INT(elem->data);
3194 if (msgnum > lastuid_old) {
3195 *msgnum_list = g_slist_prepend(*msgnum_list, GINT_TO_POINTER(msgnum));
3196 item->uid_list = g_slist_prepend(item->uid_list, GINT_TO_POINTER(msgnum));
3199 if(msgnum > item->lastuid)
3200 item->lastuid = msgnum;
3203 g_slist_free(uidlist);
3205 return GINT_TO_POINTER(nummsgs);
3208 static gint get_list_of_uids(IMAPSession *session, Folder *folder, IMAPFolderItem *item, GSList **msgnum_list)
3211 get_list_uid_data *data = g_new0(get_list_uid_data, 1);
3213 data->folder = folder;
3215 data->msgnum_list = msgnum_list;
3216 data->session = session;
3217 if (prefs_common.work_offline &&
3218 !inc_offline_should_override(
3219 _("Claws Mail needs network access in order "
3220 "to access the IMAP server."))) {
3225 result = GPOINTER_TO_INT(get_list_of_uids_thread(data));
3231 gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list, gboolean *old_uids_valid)
3233 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3234 IMAPSession *session;
3235 gint ok, nummsgs = 0, exists;
3236 guint32 uid_next = 0, uid_val = 0;
3237 GSList *uidlist = NULL;
3239 gboolean selected_folder;
3240 debug_print("get_num_list\n");
3242 g_return_val_if_fail(folder != NULL, -1);
3243 g_return_val_if_fail(item != NULL, -1);
3244 g_return_val_if_fail(item->item.path != NULL, -1);
3245 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3246 g_return_val_if_fail(folder->account != NULL, -1);
3248 debug_print("getting session...\n");
3249 session = imap_session_get(folder);
3250 g_return_val_if_fail(session != NULL, -1);
3252 if (FOLDER_ITEM(item)->path)
3253 statusbar_print_all(_("Scanning folder %s%c%s ..."),
3254 FOLDER_ITEM(item)->folder->name,
3256 FOLDER_ITEM(item)->path);
3258 statusbar_print_all(_("Scanning folder %s ..."),
3259 FOLDER_ITEM(item)->folder->name);
3261 selected_folder = (session->mbox != NULL) &&
3262 (!strcmp(session->mbox, item->item.path));
3263 if (selected_folder && time(NULL) - item->use_cache < 2) {
3264 ok = imap_cmd_noop(session);
3265 if (ok != IMAP_SUCCESS) {
3266 debug_print("disconnected!\n");
3267 session = imap_reconnect_if_possible(folder, session);
3268 if (session == NULL) {
3269 statusbar_pop_all();
3274 exists = session->exists;
3276 uid_next = item->c_uid_next;
3277 uid_val = item->c_uid_validity;
3278 *old_uids_valid = TRUE;
3280 if (item->use_cache && time(NULL) - item->use_cache < 2) {
3281 exists = item->c_messages;
3282 uid_next = item->c_uid_next;
3283 uid_val = item->c_uid_validity;
3285 debug_print("using cache %d %d %d\n", exists, uid_next, uid_val);
3287 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path, item,
3288 &exists, &uid_next, &uid_val, NULL, FALSE);
3290 item->item.last_num = uid_next - 1;
3292 item->use_cache = (time_t)0;
3293 if (ok != IMAP_SUCCESS) {
3294 statusbar_pop_all();
3298 if(item->item.mtime == uid_val)
3299 *old_uids_valid = TRUE;
3301 *old_uids_valid = FALSE;
3303 debug_print("Freeing imap uid cache (%d != %d)\n",
3304 (int)item->item.mtime, uid_val);
3306 g_slist_free(item->uid_list);
3307 item->uid_list = NULL;
3309 item->item.mtime = uid_val;
3311 imap_delete_all_cached_messages((FolderItem *)item);
3315 /* If old uid_next matches new uid_next we can be sure no message
3316 was added to the folder */
3317 debug_print("uid_next is %d and item->uid_next %d \n",
3318 uid_next, item->uid_next);
3319 if (uid_next == item->uid_next) {
3320 nummsgs = g_slist_length(item->uid_list);
3322 /* If number of messages is still the same we
3323 know our caches message numbers are still valid,
3324 otherwise if the number of messages has decrease
3325 we discard our cache to start a new scan to find
3326 out which numbers have been removed */
3327 if (exists == nummsgs) {
3328 debug_print("exists == nummsgs\n");
3329 *msgnum_list = g_slist_copy(item->uid_list);
3330 statusbar_pop_all();
3333 } else if (exists < nummsgs) {
3334 debug_print("Freeing imap uid cache");
3336 g_slist_free(item->uid_list);
3337 item->uid_list = NULL;
3342 *msgnum_list = NULL;
3343 statusbar_pop_all();
3348 nummsgs = get_list_of_uids(session, folder, item, &uidlist);
3351 statusbar_pop_all();
3356 if (nummsgs != exists) {
3357 /* Cache contains more messages then folder, we have cached
3358 an old UID of a message that was removed and new messages
3359 have been added too, otherwise the uid_next check would
3361 debug_print("Freeing imap uid cache");
3363 g_slist_free(item->uid_list);
3364 item->uid_list = NULL;
3366 g_slist_free(*msgnum_list);
3368 nummsgs = get_list_of_uids(session, folder, item, &uidlist);
3371 *msgnum_list = uidlist;
3373 dir = folder_item_get_path((FolderItem *)item);
3374 debug_print("removing old messages from %s\n", dir);
3375 remove_numbered_files_not_in_list(dir, *msgnum_list);
3378 item->uid_next = uid_next;
3380 debug_print("get_num_list - ok - %i\n", nummsgs);
3381 statusbar_pop_all();
3386 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item)
3391 flags.perm_flags = MSG_NEW|MSG_UNREAD;
3392 flags.tmp_flags = 0;
3394 g_return_val_if_fail(item != NULL, NULL);
3395 g_return_val_if_fail(file != NULL, NULL);
3397 if (folder_has_parent_of_type(item, F_QUEUE)) {
3398 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
3399 } else if (folder_has_parent_of_type(item, F_DRAFT)) {
3400 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
3403 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3404 if (!msginfo) return NULL;
3406 msginfo->plaintext_file = g_strdup(file);
3407 msginfo->folder = item;
3412 GSList *imap_get_msginfos(Folder *folder, FolderItem *item,
3413 GSList *msgnum_list)
3415 IMAPSession *session;
3416 MsgInfoList *ret = NULL;
3419 debug_print("get_msginfos\n");
3421 g_return_val_if_fail(folder != NULL, NULL);
3422 g_return_val_if_fail(item != NULL, NULL);
3423 g_return_val_if_fail(msgnum_list != NULL, NULL);
3425 debug_print("getting session...\n");
3426 session = imap_session_get(folder);
3427 g_return_val_if_fail(session != NULL, NULL);
3429 debug_print("IMAP getting msginfos\n");
3430 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3431 NULL, NULL, NULL, NULL, FALSE);
3432 if (ok != IMAP_SUCCESS) {
3436 if (!(folder_has_parent_of_type(item, F_DRAFT) ||
3437 folder_has_parent_of_type(item, F_QUEUE))) {
3438 ret = g_slist_concat(ret,
3439 imap_get_uncached_messages(session, item,
3442 MsgNumberList *sorted_list, *elem, *llast = NULL;
3443 gint startnum, lastnum;
3445 sorted_list = g_slist_sort(g_slist_copy(msgnum_list), g_int_compare);
3447 startnum = lastnum = GPOINTER_TO_INT(sorted_list->data);
3449 llast = g_slist_last(ret);
3450 for (elem = sorted_list;; elem = g_slist_next(elem)) {
3454 num = GPOINTER_TO_INT(elem->data);
3456 if (num > lastnum + 1 || elem == NULL) {
3458 for (i = startnum; i <= lastnum; ++i) {
3461 file = imap_fetch_msg(folder, item, i);
3463 MsgInfo *msginfo = imap_parse_msg(file, item);
3464 if (msginfo != NULL) {
3465 msginfo->msgnum = i;
3467 llast = ret = g_slist_append(ret, msginfo);
3469 llast = g_slist_append(llast, msginfo);
3470 llast = llast->next;
3475 session_set_access_time(SESSION(session));
3486 g_slist_free(sorted_list);
3492 MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid)
3494 MsgInfo *msginfo = NULL;
3495 MsgInfoList *msginfolist;
3496 MsgNumberList numlist;
3498 numlist.next = NULL;
3499 numlist.data = GINT_TO_POINTER(uid);
3501 msginfolist = imap_get_msginfos(folder, item, &numlist);
3502 if (msginfolist != NULL) {
3503 msginfo = msginfolist->data;
3504 g_slist_free(msginfolist);
3510 gboolean imap_scan_required(Folder *folder, FolderItem *_item)
3512 IMAPSession *session;
3513 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3514 gint ok, exists = 0, unseen = 0;
3515 guint32 uid_next = 0, uid_val = 0;
3516 gboolean selected_folder;
3518 g_return_val_if_fail(folder != NULL, FALSE);
3519 g_return_val_if_fail(item != NULL, FALSE);
3520 g_return_val_if_fail(item->item.folder != NULL, FALSE);
3521 g_return_val_if_fail(FOLDER_CLASS(item->item.folder) == &imap_class, FALSE);
3523 if (item->item.path == NULL)
3526 debug_print("getting session...\n");
3527 session = imap_session_get(folder);
3528 g_return_val_if_fail(session != NULL, FALSE);
3530 selected_folder = (session->mbox != NULL) &&
3531 (!strcmp(session->mbox, item->item.path));
3532 if (selected_folder && time(NULL) - item->use_cache < 2) {
3533 ok = imap_cmd_noop(session);
3534 if (ok != IMAP_SUCCESS) {
3535 debug_print("disconnected!\n");
3536 session = imap_reconnect_if_possible(folder, session);
3537 if (session == NULL)
3541 if (session->folder_content_changed
3542 || session->exists != item->item.total_msgs) {
3547 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path, IMAP_FOLDER_ITEM(item),
3548 &exists, &uid_next, &uid_val, &unseen, FALSE);
3549 if (ok != IMAP_SUCCESS) {
3554 item->use_cache = time(NULL);
3555 item->c_messages = exists;
3556 item->c_uid_next = uid_next;
3557 item->c_uid_validity = uid_val;
3558 item->c_unseen = unseen;
3559 item->item.last_num = uid_next - 1;
3560 debug_print("uidnext %d, item->uid_next %d, exists %d, item->item.total_msgs %d\n",
3561 uid_next, item->uid_next, exists, item->item.total_msgs);
3562 if ((uid_next != item->uid_next) || (exists != item->item.total_msgs)
3563 || unseen != item->item.unread_msgs || uid_val != item->item.mtime) {
3572 void imap_change_flags(Folder *folder, FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
3574 IMAPSession *session;
3575 IMAPFlags flags_set = 0, flags_unset = 0;
3576 gint ok = IMAP_SUCCESS;
3577 MsgNumberList numlist;
3578 hashtable_data *ht_data = NULL;
3580 g_return_if_fail(folder != NULL);
3581 g_return_if_fail(folder->klass == &imap_class);
3582 g_return_if_fail(item != NULL);
3583 g_return_if_fail(item->folder == folder);
3584 g_return_if_fail(msginfo != NULL);
3585 g_return_if_fail(msginfo->folder == item);
3587 if (!MSG_IS_MARKED(msginfo->flags) && (newflags & MSG_MARKED))
3588 flags_set |= IMAP_FLAG_FLAGGED;
3589 if ( MSG_IS_MARKED(msginfo->flags) && !(newflags & MSG_MARKED))
3590 flags_unset |= IMAP_FLAG_FLAGGED;
3592 if (!MSG_IS_UNREAD(msginfo->flags) && (newflags & MSG_UNREAD))
3593 flags_unset |= IMAP_FLAG_SEEN;
3594 if ( MSG_IS_UNREAD(msginfo->flags) && !(newflags & MSG_UNREAD))
3595 flags_set |= IMAP_FLAG_SEEN;
3597 if (!MSG_IS_REPLIED(msginfo->flags) && (newflags & MSG_REPLIED))
3598 flags_set |= IMAP_FLAG_ANSWERED;
3599 if ( MSG_IS_REPLIED(msginfo->flags) && !(newflags & MSG_REPLIED))
3600 flags_unset |= IMAP_FLAG_ANSWERED;
3602 if (!MSG_IS_DELETED(msginfo->flags) && (newflags & MSG_DELETED))
3603 flags_set |= IMAP_FLAG_DELETED;
3604 if ( MSG_IS_DELETED(msginfo->flags) && !(newflags & MSG_DELETED))
3605 flags_unset |= IMAP_FLAG_DELETED;
3607 if (!flags_set && !flags_unset) {
3608 /* the changed flags were not translatable to IMAP-speak.
3609 * like MSG_POSTFILTERED, so just apply. */
3610 msginfo->flags.perm_flags = newflags;
3614 debug_print("getting session...\n");
3615 session = imap_session_get(folder);
3620 if ((ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
3621 NULL, NULL, NULL, NULL, FALSE)) != IMAP_SUCCESS) {
3625 numlist.next = NULL;
3626 numlist.data = GINT_TO_POINTER(msginfo->msgnum);
3628 if (IMAP_FOLDER_ITEM(item)->batching) {
3629 /* instead of performing an UID STORE command for each message change,
3630 * as a lot of them can change "together", we just fill in hashtables
3631 * and defer the treatment so that we're able to send only one
3634 debug_print("IMAP batch mode on, deferring flags change\n");
3636 ht_data = g_hash_table_lookup(IMAP_FOLDER_ITEM(item)->flags_set_table,
3637 GINT_TO_POINTER(flags_set));
3638 if (ht_data == NULL) {
3639 ht_data = g_new0(hashtable_data, 1);
3640 ht_data->session = session;
3641 ht_data->item = IMAP_FOLDER_ITEM(item);
3642 g_hash_table_insert(IMAP_FOLDER_ITEM(item)->flags_set_table,
3643 GINT_TO_POINTER(flags_set), ht_data);
3645 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
3646 ht_data->msglist = g_slist_prepend(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum));
3649 ht_data = g_hash_table_lookup(IMAP_FOLDER_ITEM(item)->flags_unset_table,
3650 GINT_TO_POINTER(flags_unset));
3651 if (ht_data == NULL) {
3652 ht_data = g_new0(hashtable_data, 1);
3653 ht_data->session = session;
3654 ht_data->item = IMAP_FOLDER_ITEM(item);
3655 g_hash_table_insert(IMAP_FOLDER_ITEM(item)->flags_unset_table,
3656 GINT_TO_POINTER(flags_unset), ht_data);
3658 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
3659 ht_data->msglist = g_slist_prepend(ht_data->msglist,
3660 GINT_TO_POINTER(msginfo->msgnum));
3663 debug_print("IMAP changing flags\n");
3665 ok = imap_set_message_flags(session, &numlist, flags_set, TRUE);
3666 if (ok != IMAP_SUCCESS) {
3673 ok = imap_set_message_flags(session, &numlist, flags_unset, FALSE);
3674 if (ok != IMAP_SUCCESS) {
3680 msginfo->flags.perm_flags = newflags;
3685 static gint imap_remove_msg(Folder *folder, FolderItem *item, gint uid)
3688 IMAPSession *session;
3690 MsgNumberList numlist;
3692 g_return_val_if_fail(folder != NULL, -1);
3693 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3694 g_return_val_if_fail(item != NULL, -1);
3696 debug_print("getting session...\n");
3697 session = imap_session_get(folder);
3698 if (!session) return -1;
3700 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3701 NULL, NULL, NULL, NULL, FALSE);
3702 if (ok != IMAP_SUCCESS) {
3706 numlist.next = NULL;
3707 numlist.data = GINT_TO_POINTER(uid);
3709 ok = imap_set_message_flags
3710 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
3711 &numlist, IMAP_FLAG_DELETED, TRUE);
3712 if (ok != IMAP_SUCCESS) {
3713 log_warning(_("can't set deleted flags: %d\n"), uid);
3718 if (!session->uidplus) {
3719 ok = imap_cmd_expunge(session);
3723 uidstr = g_strdup_printf("%u", uid);
3724 ok = imap_cmd_expunge(session);
3727 if (ok != IMAP_SUCCESS) {
3728 log_warning(_("can't expunge\n"));
3733 IMAP_FOLDER_ITEM(item)->uid_list = g_slist_remove(
3734 IMAP_FOLDER_ITEM(item)->uid_list, numlist.data);
3735 dir = folder_item_get_path(item);
3736 if (is_dir_exist(dir))
3737 remove_numbered_files(dir, uid, uid);
3740 return IMAP_SUCCESS;
3743 static gint compare_msginfo(gconstpointer a, gconstpointer b)
3745 return ((MsgInfo *)a)->msgnum - ((MsgInfo *)b)->msgnum;
3748 static guint gslist_find_next_num(MsgNumberList **list, guint num)
3752 g_return_val_if_fail(list != NULL, -1);
3754 for (elem = *list; elem != NULL; elem = g_slist_next(elem))
3755 if (GPOINTER_TO_INT(elem->data) >= num)
3758 return elem != NULL ? GPOINTER_TO_INT(elem->data) : (gint)-1;
3762 * NEW and DELETED flags are not syncronized
3763 * - The NEW/RECENT flags in IMAP folders can not really be directly
3764 * modified by Sylpheed
3765 * - The DELETE/DELETED flag in IMAP and Sylpheed don't have the same
3766 * meaning, in IMAP it always removes the messages from the FolderItem
3767 * in Sylpheed it can mean to move the message to trash
3770 typedef struct _get_flags_data {
3773 MsgInfoList *msginfo_list;
3774 GRelation *msgflags;
3775 gboolean full_search;
3779 static /*gint*/ void *imap_get_flags_thread(void *data)
3781 get_flags_data *stuff = (get_flags_data *)data;
3782 Folder *folder = stuff->folder;
3783 FolderItem *item = stuff->item;
3784 MsgInfoList *msginfo_list = stuff->msginfo_list;
3785 GRelation *msgflags = stuff->msgflags;
3786 gboolean full_search = stuff->full_search;
3787 IMAPSession *session;
3788 GSList *sorted_list = NULL;
3789 GSList *unseen = NULL, *answered = NULL, *flagged = NULL, *deleted = NULL;
3790 GSList *p_unseen, *p_answered, *p_flagged, *p_deleted;
3792 GSList *seq_list, *cur;
3793 gboolean reverse_seen = FALSE;
3796 gint exists_cnt, unseen_cnt;
3797 gboolean selected_folder;
3799 if (folder == NULL || item == NULL) {
3801 return GINT_TO_POINTER(-1);
3804 debug_print("getting session...\n");
3805 session = imap_session_get(folder);
3806 if (session == NULL) {
3808 return GINT_TO_POINTER(-1);
3811 selected_folder = (session->mbox != NULL) &&
3812 (!strcmp(session->mbox, item->path));
3814 if (!selected_folder) {
3815 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3816 &exists_cnt, NULL, &unseen_cnt, NULL, TRUE);
3817 if (ok != IMAP_SUCCESS) {
3820 return GINT_TO_POINTER(-1);
3823 if (unseen_cnt > exists_cnt / 2)
3824 reverse_seen = TRUE;
3827 if (item->unread_msgs > item->total_msgs / 2)
3828 reverse_seen = TRUE;
3831 cmd_buf = g_string_new(NULL);
3833 sorted_list = g_slist_sort(g_slist_copy(msginfo_list), compare_msginfo);
3835 seq_list = imap_get_lep_set_from_msglist(msginfo_list);
3837 struct mailimap_set * set;
3838 set = mailimap_set_new_interval(1, 0);
3839 seq_list = g_slist_append(NULL, set);
3842 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
3843 struct mailimap_set * imapset;
3844 clist * lep_uidlist;
3847 imapset = cur->data;
3849 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SEEN,
3850 full_search ? NULL:imapset, &lep_uidlist);
3853 r = imap_threaded_search(folder,
3854 IMAP_SEARCH_TYPE_UNSEEN,
3855 full_search ? NULL:imapset, &lep_uidlist);
3857 if (r == MAILIMAP_NO_ERROR) {
3860 uidlist = imap_uid_list_from_lep(lep_uidlist);
3861 mailimap_search_result_free(lep_uidlist);
3863 unseen = g_slist_concat(unseen, uidlist);
3866 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_FLAGGED,
3867 full_search ? NULL:imapset, &lep_uidlist);
3868 if (r == MAILIMAP_NO_ERROR) {
3871 uidlist = imap_uid_list_from_lep(lep_uidlist);
3872 mailimap_search_result_free(lep_uidlist);
3874 flagged = g_slist_concat(flagged, uidlist);
3877 if (item->opened || item->processing_pending || item == folder->inbox) {
3878 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_ANSWERED,
3879 full_search ? NULL:imapset, &lep_uidlist);
3880 if (r == MAILIMAP_NO_ERROR) {
3883 uidlist = imap_uid_list_from_lep(lep_uidlist);
3884 mailimap_search_result_free(lep_uidlist);
3886 answered = g_slist_concat(answered, uidlist);
3889 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_DELETED,
3890 full_search ? NULL:imapset, &lep_uidlist);
3891 if (r == MAILIMAP_NO_ERROR) {
3894 uidlist = imap_uid_list_from_lep(lep_uidlist);
3895 mailimap_search_result_free(lep_uidlist);
3897 deleted = g_slist_concat(deleted, uidlist);
3903 p_answered = answered;
3904 p_flagged = flagged;
3905 p_deleted = deleted;
3907 for (elem = sorted_list; elem != NULL; elem = g_slist_next(elem)) {
3912 msginfo = (MsgInfo *) elem->data;
3913 flags = msginfo->flags.perm_flags;
3914 wasnew = (flags & MSG_NEW);
3915 if (item->opened || item->processing_pending || item == folder->inbox) {
3916 flags &= ~((reverse_seen ? 0 : MSG_UNREAD | MSG_NEW) | MSG_REPLIED | MSG_MARKED);
3918 flags &= ~((reverse_seen ? 0 : MSG_UNREAD | MSG_NEW | MSG_MARKED));
3921 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
3922 if (gslist_find_next_num(&p_unseen, msginfo->msgnum) == msginfo->msgnum) {
3923 if (!reverse_seen) {
3924 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
3926 flags &= ~(MSG_UNREAD | MSG_NEW);
3930 if (gslist_find_next_num(&p_flagged, msginfo->msgnum) == msginfo->msgnum)
3931 flags |= MSG_MARKED;
3933 flags &= ~MSG_MARKED;
3935 if (item->opened || item->processing_pending || item == folder->inbox) {
3936 if (gslist_find_next_num(&p_answered, msginfo->msgnum) == msginfo->msgnum)
3937 flags |= MSG_REPLIED;
3939 flags &= ~MSG_REPLIED;
3940 if (gslist_find_next_num(&p_deleted, msginfo->msgnum) == msginfo->msgnum)
3941 flags |= MSG_DELETED;
3943 flags &= ~MSG_DELETED;
3945 g_relation_insert(msgflags, msginfo, GINT_TO_POINTER(flags));
3948 imap_lep_set_free(seq_list);
3949 g_slist_free(flagged);
3950 g_slist_free(deleted);
3951 g_slist_free(answered);
3952 g_slist_free(unseen);
3953 g_slist_free(sorted_list);
3954 g_string_free(cmd_buf, TRUE);
3958 return GINT_TO_POINTER(0);
3961 static gint imap_get_flags(Folder *folder, FolderItem *item,
3962 MsgInfoList *msginfo_list, GRelation *msgflags)
3965 get_flags_data *data = g_new0(get_flags_data, 1);
3967 data->folder = folder;
3969 data->msginfo_list = msginfo_list;
3970 data->msgflags = msgflags;
3971 data->full_search = FALSE;
3973 GSList *tmp = NULL, *cur;
3975 if (prefs_common.work_offline &&
3976 !inc_offline_should_override(
3977 _("Claws Mail needs network access in order "
3978 "to access the IMAP server."))) {
3983 tmp = folder_item_get_msg_list(item);
3985 if (g_slist_length(tmp) <= g_slist_length(msginfo_list))
3986 data->full_search = TRUE;
3988 for (cur = tmp; cur; cur = cur->next)
3989 procmsg_msginfo_free((MsgInfo *)cur->data);
3993 result = GPOINTER_TO_INT(imap_get_flags_thread(data));
4000 static gboolean process_flags(gpointer key, gpointer value, gpointer user_data)
4002 gboolean flags_set = GPOINTER_TO_INT(user_data);
4003 gint flags_value = GPOINTER_TO_INT(key);
4004 hashtable_data *data = (hashtable_data *)value;
4005 IMAPFolderItem *_item = data->item;
4006 FolderItem *item = (FolderItem *)_item;
4007 gint ok = IMAP_ERROR;
4008 IMAPSession *session = NULL;
4010 debug_print("getting session...\n");
4011 session = imap_session_get(item->folder);
4013 data->msglist = g_slist_reverse(data->msglist);
4015 debug_print("IMAP %ssetting flags to %d for %d messages\n",
4018 g_slist_length(data->msglist));
4021 ok = imap_select(session, IMAP_FOLDER(item->folder), item->path,
4022 NULL, NULL, NULL, NULL, FALSE);
4024 if (ok == IMAP_SUCCESS) {
4025 imap_set_message_flags(data->session, data->msglist, flags_value, flags_set);
4027 g_warning("can't select mailbox %s\n", item->path);
4031 g_slist_free(data->msglist);
4036 static void process_hashtable(IMAPFolderItem *item)
4038 if (item->flags_set_table) {
4039 g_hash_table_foreach_remove(item->flags_set_table, process_flags, GINT_TO_POINTER(TRUE));
4040 g_hash_table_destroy(item->flags_set_table);
4041 item->flags_set_table = NULL;
4043 if (item->flags_unset_table) {
4044 g_hash_table_foreach_remove(item->flags_unset_table, process_flags, GINT_TO_POINTER(FALSE));
4045 g_hash_table_destroy(item->flags_unset_table);
4046 item->flags_unset_table = NULL;
4050 static void imap_set_batch (Folder *folder, FolderItem *_item, gboolean batch)
4052 IMAPFolderItem *item = (IMAPFolderItem *)_item;
4054 g_return_if_fail(item != NULL);
4056 if (item->batching == batch)
4060 item->batching = TRUE;
4061 debug_print("IMAP switching to batch mode\n");
4062 if (!item->flags_set_table) {
4063 item->flags_set_table = g_hash_table_new(NULL, g_direct_equal);
4065 if (!item->flags_unset_table) {
4066 item->flags_unset_table = g_hash_table_new(NULL, g_direct_equal);
4069 debug_print("IMAP switching away from batch mode\n");
4071 process_hashtable(item);
4072 item->batching = FALSE;
4078 /* data types conversion libetpan <-> claws */
4082 #define ETPAN_IMAP_MB_MARKED 1
4083 #define ETPAN_IMAP_MB_UNMARKED 2
4084 #define ETPAN_IMAP_MB_NOSELECT 4
4085 #define ETPAN_IMAP_MB_NOINFERIORS 8
4087 static int imap_flags_to_flags(struct mailimap_mbx_list_flags * imap_flags)
4093 if (imap_flags->mbf_type == MAILIMAP_MBX_LIST_FLAGS_SFLAG) {
4094 switch (imap_flags->mbf_sflag) {
4095 case MAILIMAP_MBX_LIST_SFLAG_MARKED:
4096 flags |= ETPAN_IMAP_MB_MARKED;
4098 case MAILIMAP_MBX_LIST_SFLAG_NOSELECT:
4099 flags |= ETPAN_IMAP_MB_NOSELECT;
4101 case MAILIMAP_MBX_LIST_SFLAG_UNMARKED:
4102 flags |= ETPAN_IMAP_MB_UNMARKED;
4107 for(cur = clist_begin(imap_flags->mbf_oflags) ; cur != NULL ;
4108 cur = clist_next(cur)) {
4109 struct mailimap_mbx_list_oflag * oflag;
4111 oflag = clist_content(cur);
4113 switch (oflag->of_type) {
4114 case MAILIMAP_MBX_LIST_OFLAG_NOINFERIORS:
4115 flags |= ETPAN_IMAP_MB_NOINFERIORS;
4123 static GSList * imap_list_from_lep(IMAPFolder * folder,
4124 clist * list, const gchar * real_path, gboolean all)
4127 GSList * item_list = NULL, *llast = NULL;
4129 for(iter = clist_begin(list) ; iter != NULL ;
4130 iter = clist_next(iter)) {
4131 struct mailimap_mailbox_list * mb;
4139 FolderItem *new_item;
4141 mb = clist_content(iter);
4147 if (mb->mb_flag != NULL)
4148 flags = imap_flags_to_flags(mb->mb_flag);
4150 delimiter = mb->mb_delimiter;
4153 dup_name = strdup(name);
4154 if (delimiter != '\0')
4155 subst_char(dup_name, delimiter, '/');
4157 base = g_path_get_basename(dup_name);
4158 if (base[0] == '.') {
4164 if (!all && path_cmp(name, real_path) == 0) {
4170 if (!all && dup_name[strlen(dup_name)-1] == '/') {
4171 dup_name[strlen(dup_name)-1] = '\0';
4174 loc_name = imap_modified_utf7_to_utf8(base);
4175 loc_path = imap_modified_utf7_to_utf8(dup_name);
4177 new_item = folder_item_new(FOLDER(folder), loc_name, loc_path);
4178 if ((flags & ETPAN_IMAP_MB_NOINFERIORS) != 0)
4179 new_item->no_sub = TRUE;
4180 if (strcmp(dup_name, "INBOX") != 0 &&
4181 ((flags & ETPAN_IMAP_MB_NOSELECT) != 0))
4182 new_item->no_select = TRUE;
4184 if (item_list == NULL)
4185 llast = item_list = g_slist_append(item_list, new_item);
4187 llast = g_slist_append(llast, new_item);
4188 llast = llast->next;
4190 debug_print("folder '%s' found.\n", loc_path);
4201 static GSList * imap_get_lep_set_from_numlist(MsgNumberList *numlist)
4203 GSList *sorted_list, *cur;
4204 guint first, last, next;
4205 GSList *ret_list = NULL, *llast = NULL;
4207 struct mailimap_set * current_set;
4208 unsigned int item_count;
4210 if (numlist == NULL)
4214 current_set = mailimap_set_new_empty();
4216 sorted_list = g_slist_copy(numlist);
4217 sorted_list = g_slist_sort(sorted_list, g_int_compare);
4219 first = GPOINTER_TO_INT(sorted_list->data);
4222 for (cur = sorted_list; cur != NULL; cur = g_slist_next(cur)) {
4223 if (GPOINTER_TO_INT(cur->data) == 0)
4228 last = GPOINTER_TO_INT(cur->data);
4230 next = GPOINTER_TO_INT(cur->next->data);
4234 if (last + 1 != next || next == 0) {
4236 struct mailimap_set_item * item;
4237 item = mailimap_set_item_new(first, last);
4238 mailimap_set_add(current_set, item);
4243 if (count >= IMAP_SET_MAX_COUNT) {
4244 if (ret_list == NULL)
4245 llast = ret_list = g_slist_append(ret_list,
4248 llast = g_slist_append(llast, current_set);
4249 llast = llast->next;
4251 current_set = mailimap_set_new_empty();
4258 if (clist_count(current_set->set_list) > 0) {
4259 ret_list = g_slist_append(ret_list,
4263 g_slist_free(sorted_list);
4268 static GSList * imap_get_lep_set_from_msglist(MsgInfoList *msglist)
4270 MsgNumberList *numlist = NULL;
4274 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
4275 MsgInfo *msginfo = (MsgInfo *) cur->data;
4277 numlist = g_slist_prepend(numlist, GINT_TO_POINTER(msginfo->msgnum));
4279 numlist = g_slist_reverse(numlist);
4280 seq_list = imap_get_lep_set_from_numlist(numlist);
4281 g_slist_free(numlist);
4286 static GSList * imap_uid_list_from_lep(clist * list)
4293 for(iter = clist_begin(list) ; iter != NULL ;
4294 iter = clist_next(iter)) {
4297 puid = clist_content(iter);
4298 result = g_slist_prepend(result, GINT_TO_POINTER(* puid));
4301 result = g_slist_reverse(result);
4305 static GSList * imap_uid_list_from_lep_tab(carray * list)
4312 for(i = 0 ; i < carray_count(list) ; i ++) {
4315 puid = carray_get(list, i);
4316 result = g_slist_prepend(result, GINT_TO_POINTER(* puid));
4318 result = g_slist_reverse(result);
4322 static MsgInfo *imap_envelope_from_lep(struct imap_fetch_env_info * info,
4325 MsgInfo *msginfo = NULL;
4328 MsgFlags flags = {0, 0};
4330 if (info->headers == NULL)
4333 MSG_SET_TMP_FLAGS(flags, MSG_IMAP);
4334 if (folder_has_parent_of_type(item, F_QUEUE)) {
4335 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
4336 } else if (folder_has_parent_of_type(item, F_DRAFT)) {
4337 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
4339 flags.perm_flags = info->flags;
4343 msginfo = procheader_parse_str(info->headers, flags, FALSE, FALSE);
4346 msginfo->msgnum = uid;
4347 msginfo->size = size;
4353 static void imap_lep_set_free(GSList *seq_list)
4357 for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) {
4358 struct mailimap_set * imapset;
4360 imapset = cur->data;
4361 mailimap_set_free(imapset);
4363 g_slist_free(seq_list);
4366 static struct mailimap_flag_list * imap_flag_to_lep(IMAPFlags flags)
4368 struct mailimap_flag_list * flag_list;
4370 flag_list = mailimap_flag_list_new_empty();
4372 if (IMAP_IS_SEEN(flags))
4373 mailimap_flag_list_add(flag_list,
4374 mailimap_flag_new_seen());
4375 if (IMAP_IS_ANSWERED(flags))
4376 mailimap_flag_list_add(flag_list,
4377 mailimap_flag_new_answered());
4378 if (IMAP_IS_FLAGGED(flags))
4379 mailimap_flag_list_add(flag_list,
4380 mailimap_flag_new_flagged());
4381 if (IMAP_IS_DELETED(flags))
4382 mailimap_flag_list_add(flag_list,
4383 mailimap_flag_new_deleted());
4384 if (IMAP_IS_DRAFT(flags))
4385 mailimap_flag_list_add(flag_list,
4386 mailimap_flag_new_draft());
4391 guint imap_folder_get_refcnt(Folder *folder)
4393 return ((IMAPFolder *)folder)->refcnt;
4396 void imap_folder_ref(Folder *folder)
4398 ((IMAPFolder *)folder)->refcnt++;
4401 void imap_disconnect_all(void)
4404 for (list = account_get_list(); list != NULL; list = list->next) {
4405 PrefsAccount *account = list->data;
4406 if (account->protocol == A_IMAP4) {
4407 RemoteFolder *folder = (RemoteFolder *)account->folder;
4408 if (folder && folder->session) {
4409 IMAPSession *session = (IMAPSession *)folder->session;
4410 imap_threaded_disconnect(FOLDER(folder));
4411 SESSION(session)->state = SESSION_DISCONNECTED;
4412 session_destroy(SESSION(session));
4413 folder->session = NULL;
4419 void imap_folder_unref(Folder *folder)
4421 if (((IMAPFolder *)folder)->refcnt > 0)
4422 ((IMAPFolder *)folder)->refcnt--;
4425 #else /* HAVE_LIBETPAN */
4427 static FolderClass imap_class;
4429 static XMLTag *imap_item_get_xml(Folder *folder, FolderItem *item);
4430 static void imap_item_set_xml(Folder *folder, FolderItem *item, XMLTag *tag);
4432 static Folder *imap_folder_new (const gchar *name,
4435 static gboolean missing_imap_warning = TRUE;
4436 if (missing_imap_warning) {
4437 missing_imap_warning = FALSE;
4439 _("You have one or more IMAP accounts "
4440 "defined. However this version of "
4441 "Claws Mail has been built without "
4442 "IMAP support; your IMAP account(s) are "
4444 "You probably need to "
4445 "install libetpan and recompile "
4450 static gint imap_create_tree (Folder *folder)
4454 static FolderItem *imap_create_folder (Folder *folder,
4460 static gint imap_rename_folder (Folder *folder,
4467 gchar imap_get_path_separator_for_item(FolderItem *item)
4472 FolderClass *imap_get_class(void)
4474 if (imap_class.idstr == NULL) {
4475 imap_class.type = F_IMAP;
4476 imap_class.idstr = "imap";
4477 imap_class.uistr = "IMAP4";
4479 imap_class.new_folder = imap_folder_new;
4480 imap_class.create_tree = imap_create_tree;
4481 imap_class.create_folder = imap_create_folder;
4482 imap_class.rename_folder = imap_rename_folder;
4484 imap_class.set_xml = folder_set_xml;
4485 imap_class.get_xml = folder_get_xml;
4486 imap_class.item_set_xml = imap_item_set_xml;
4487 imap_class.item_get_xml = imap_item_get_xml;
4488 /* nothing implemented */
4494 void imap_disconnect_all(void)
4498 gint imap_scan_tree_real(Folder *folder, gboolean subs_only)
4503 gint imap_subscribe(Folder *folder, FolderItem *item, gboolean sub)
4508 gint imap_scan_subtree(Folder *folder, FolderItem *item, gboolean subs_only)
4514 void imap_synchronise(FolderItem *item)
4516 imap_gtk_synchronise(item);
4519 static void imap_item_set_xml(Folder *folder, FolderItem *item, XMLTag *tag)
4521 #ifdef HAVE_LIBETPAN
4524 folder_item_set_xml(folder, item, tag);
4526 #ifdef HAVE_LIBETPAN
4527 for (cur = tag->attr; cur != NULL; cur = g_list_next(cur)) {
4528 XMLAttr *attr = (XMLAttr *) cur->data;
4530 if (!attr || !attr->name || !attr->value) continue;
4531 if (!strcmp(attr->name, "uidnext"))
4532 IMAP_FOLDER_ITEM(item)->uid_next = atoi(attr->value);
4537 static XMLTag *imap_item_get_xml(Folder *folder, FolderItem *item)
4541 tag = folder_item_get_xml(folder, item);
4543 #ifdef HAVE_LIBETPAN
4544 xml_tag_add_attr(tag, xml_attr_new_int("uidnext",
4545 IMAP_FOLDER_ITEM(item)->uid_next));