2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2006 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,
259 static void imap_create_missing_folders (Folder *folder);
260 static FolderItem *imap_create_special_folder
262 SpecialFolderItemType stype,
265 static gint imap_do_copy_msgs (Folder *folder,
267 MsgInfoList *msglist,
268 GRelation *relation);
270 static void imap_delete_all_cached_messages (FolderItem *item);
271 static void imap_set_batch (Folder *folder,
274 static gint imap_set_message_flags (IMAPSession *session,
275 MsgNumberList *numlist,
278 static gint imap_select (IMAPSession *session,
284 guint32 *uid_validity,
286 static gint imap_status (IMAPSession *session,
289 IMAPFolderItem *item,
292 guint32 *uid_validity,
296 static IMAPNameSpace *imap_find_namespace (IMAPFolder *folder,
298 static gchar imap_get_path_separator (IMAPFolder *folder,
300 static gchar *imap_get_real_path (IMAPFolder *folder,
302 static void imap_synchronise (FolderItem *item);
304 static void imap_free_capabilities (IMAPSession *session);
306 /* low-level IMAP4rev1 commands */
307 static gint imap_cmd_login (IMAPSession *session,
311 static gint imap_cmd_noop (IMAPSession *session);
313 static gint imap_cmd_starttls (IMAPSession *session);
315 static gint imap_cmd_select (IMAPSession *session,
320 guint32 *uid_validity,
322 static gint imap_cmd_examine (IMAPSession *session,
327 guint32 *uid_validity,
329 static gint imap_cmd_create (IMAPSession *sock,
330 const gchar *folder);
331 static gint imap_cmd_rename (IMAPSession *sock,
332 const gchar *oldfolder,
333 const gchar *newfolder);
334 static gint imap_cmd_delete (IMAPSession *session,
335 const gchar *folder);
336 static gint imap_cmd_fetch (IMAPSession *sock,
338 const gchar *filename,
341 static gint imap_cmd_append (IMAPSession *session,
342 const gchar *destfolder,
346 static gint imap_cmd_copy (IMAPSession *session,
347 struct mailimap_set * set,
348 const gchar *destfolder,
349 GRelation *uid_mapping,
350 struct mailimap_set ** source,
351 struct mailimap_set ** dest);
352 static gint imap_cmd_store (IMAPSession *session,
353 struct mailimap_set * set,
356 static gint imap_cmd_expunge (IMAPSession *session);
358 static void imap_path_separator_subst (gchar *str,
361 static gchar *imap_utf8_to_modified_utf7 (const gchar *from);
362 static gchar *imap_modified_utf7_to_utf8 (const gchar *mutf7_str);
364 static gboolean imap_rename_folder_func (GNode *node,
366 static gint imap_get_num_list (Folder *folder,
369 gboolean *old_uids_valid);
370 static GSList *imap_get_msginfos (Folder *folder,
372 GSList *msgnum_list);
373 static MsgInfo *imap_get_msginfo (Folder *folder,
376 static gboolean imap_scan_required (Folder *folder,
378 static void imap_change_flags (Folder *folder,
381 MsgPermFlags newflags);
382 static gint imap_get_flags (Folder *folder,
384 MsgInfoList *msglist,
385 GRelation *msgflags);
386 static gchar *imap_folder_get_path (Folder *folder);
387 static gchar *imap_item_get_path (Folder *folder,
389 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item);
392 /* data types conversion libetpan <-> sylpheed */
393 static GSList * imap_list_from_lep(IMAPFolder * folder,
394 clist * list, const gchar * real_path, gboolean all);
395 static GSList * imap_get_lep_set_from_numlist(MsgNumberList *numlist);
396 static GSList * imap_get_lep_set_from_msglist(MsgInfoList *msglist);
397 static GSList * imap_uid_list_from_lep(clist * list);
398 static GSList * imap_uid_list_from_lep_tab(carray * list);
399 static MsgInfo *imap_envelope_from_lep(struct imap_fetch_env_info * info,
401 static void imap_lep_set_free(GSList *seq_list);
402 static struct mailimap_flag_list * imap_flag_to_lep(IMAPFlags flags);
404 typedef struct _hashtable_data {
405 IMAPSession *session;
407 IMAPFolderItem *item;
410 static FolderClass imap_class;
412 typedef struct _thread_data {
422 FolderClass *imap_get_class(void)
424 if (imap_class.idstr == NULL) {
425 imap_class.type = F_IMAP;
426 imap_class.idstr = "imap";
427 imap_class.uistr = "IMAP4";
429 /* Folder functions */
430 imap_class.new_folder = imap_folder_new;
431 imap_class.destroy_folder = imap_folder_destroy;
432 imap_class.scan_tree = imap_scan_tree;
433 imap_class.create_tree = imap_create_tree;
435 /* FolderItem functions */
436 imap_class.item_new = imap_folder_item_new;
437 imap_class.item_destroy = imap_folder_item_destroy;
438 imap_class.item_get_path = imap_item_get_path;
439 imap_class.create_folder = imap_create_folder;
440 imap_class.rename_folder = imap_rename_folder;
441 imap_class.remove_folder = imap_remove_folder;
442 imap_class.close = imap_close;
443 imap_class.get_num_list = imap_get_num_list;
444 imap_class.scan_required = imap_scan_required;
445 imap_class.set_xml = folder_set_xml;
446 imap_class.get_xml = folder_get_xml;
447 imap_class.item_set_xml = imap_item_set_xml;
448 imap_class.item_get_xml = imap_item_get_xml;
450 /* Message functions */
451 imap_class.get_msginfo = imap_get_msginfo;
452 imap_class.get_msginfos = imap_get_msginfos;
453 imap_class.fetch_msg = imap_fetch_msg;
454 imap_class.fetch_msg_full = imap_fetch_msg_full;
455 imap_class.add_msg = imap_add_msg;
456 imap_class.add_msgs = imap_add_msgs;
457 imap_class.copy_msg = imap_copy_msg;
458 imap_class.copy_msgs = imap_copy_msgs;
459 imap_class.remove_msg = imap_remove_msg;
460 imap_class.remove_msgs = imap_remove_msgs;
461 imap_class.remove_all_msg = imap_remove_all_msg;
462 imap_class.is_msg_changed = imap_is_msg_changed;
463 imap_class.change_flags = imap_change_flags;
464 imap_class.get_flags = imap_get_flags;
465 imap_class.set_batch = imap_set_batch;
466 imap_class.synchronise = imap_synchronise;
468 pthread_mutex_init(&imap_mutex, NULL);
475 static Folder *imap_folder_new(const gchar *name, const gchar *path)
479 folder = (Folder *)g_new0(IMAPFolder, 1);
480 folder->klass = &imap_class;
481 imap_folder_init(folder, name, path);
486 static void imap_folder_destroy(Folder *folder)
488 while (imap_folder_get_refcnt(folder) > 0)
489 gtk_main_iteration();
491 folder_remote_folder_destroy(REMOTE_FOLDER(folder));
495 static void imap_folder_init(Folder *folder, const gchar *name,
498 folder_remote_folder_init((Folder *)folder, name, path);
501 static FolderItem *imap_folder_item_new(Folder *folder)
503 IMAPFolderItem *item;
505 item = g_new0(IMAPFolderItem, 1);
508 item->uid_list = NULL;
510 return (FolderItem *)item;
513 static void imap_folder_item_destroy(Folder *folder, FolderItem *_item)
515 IMAPFolderItem *item = (IMAPFolderItem *)_item;
517 g_return_if_fail(item != NULL);
518 g_slist_free(item->uid_list);
523 static gboolean imap_reset_uid_lists_func(GNode *node, gpointer data)
525 IMAPFolderItem *item = (IMAPFolderItem *)node->data;
528 g_slist_free(item->uid_list);
529 item->uid_list = NULL;
534 static void imap_reset_uid_lists(Folder *folder)
536 if(folder->node == NULL)
539 /* Destroy all uid lists and rest last uid */
540 g_node_traverse(folder->node, G_IN_ORDER, G_TRAVERSE_ALL, -1, imap_reset_uid_lists_func, NULL);
543 int imap_get_capabilities(IMAPSession *session)
545 struct mailimap_capability_data *capabilities = NULL;
549 if (session->capability != NULL)
550 return MAILIMAP_NO_ERROR;
552 capabilities = imap_threaded_capability(session->folder, &result);
554 if (result != MAILIMAP_NO_ERROR) {
555 return MAILIMAP_ERROR_CAPABILITY;
558 if (capabilities == NULL) {
559 return MAILIMAP_NO_ERROR;
562 for(cur = clist_begin(capabilities->cap_list) ; cur != NULL ;
563 cur = clist_next(cur)) {
564 struct mailimap_capability * cap =
566 if (!cap || cap->cap_data.cap_name == NULL)
568 session->capability = g_slist_append
569 (session->capability,
570 g_strdup(cap->cap_data.cap_name));
571 debug_print("got capa %s\n", cap->cap_data.cap_name);
573 mailimap_capability_data_free(capabilities);
574 return MAILIMAP_NO_ERROR;
577 gboolean imap_has_capability(IMAPSession *session, const gchar *cap)
580 for (cur = session->capability; cur; cur = cur->next) {
581 if (!g_ascii_strcasecmp(cur->data, cap))
587 static gint imap_auth(IMAPSession *session, const gchar *user, const gchar *pass,
590 gint ok = IMAP_ERROR;
591 static time_t last_login_err = 0;
592 gchar *ext_info = "";
594 if (imap_get_capabilities(session) != MAILIMAP_NO_ERROR)
599 ok = imap_cmd_login(session, user, pass, "ANONYMOUS");
601 case IMAP_AUTH_CRAM_MD5:
602 ok = imap_cmd_login(session, user, pass, "CRAM-MD5");
604 case IMAP_AUTH_LOGIN:
605 ok = imap_cmd_login(session, user, pass, "LOGIN");
607 case IMAP_AUTH_GSSAPI:
608 ok = imap_cmd_login(session, user, pass, "GSSAPI");
611 debug_print("capabilities:\n"
616 imap_has_capability(session, "ANONYMOUS"),
617 imap_has_capability(session, "CRAM-MD5"),
618 imap_has_capability(session, "LOGIN"),
619 imap_has_capability(session, "GSSAPI"));
620 if (imap_has_capability(session, "CRAM-MD5"))
621 ok = imap_cmd_login(session, user, pass, "CRAM-MD5");
622 if (ok == IMAP_ERROR && imap_has_capability(session, "GSSAPI"))
623 ok = imap_cmd_login(session, user, pass, "GSSAPI");
624 if (ok == IMAP_ERROR) /* we always try LOGIN before giving up */
625 ok = imap_cmd_login(session, user, pass, "LOGIN");
628 if (ok == IMAP_SUCCESS)
629 session->authenticated = TRUE;
631 if (type == IMAP_AUTH_CRAM_MD5) {
632 ext_info = _("\n\nCRAM-MD5 logins only work if libetpan has been "
633 "compiled with SASL support and the "
634 "CRAM-MD5 SASL plugin is installed.");
637 if (time(NULL) - last_login_err > 10) {
638 if (!prefs_common.no_recv_err_panel) {
639 alertpanel_error(_("Connection to %s failed: "
641 SESSION(session)->server, ext_info);
643 log_error(_("Connection to %s failed: "
644 "login refused.%s\n"),
645 SESSION(session)->server, ext_info);
648 last_login_err = time(NULL);
653 static IMAPSession *imap_reconnect_if_possible(Folder *folder, IMAPSession *session)
655 RemoteFolder *rfolder = REMOTE_FOLDER(folder);
656 /* Check if this is the first try to establish a
657 connection, if yes we don't try to reconnect */
658 debug_print("reconnecting\n");
659 if (rfolder->session == NULL) {
660 log_warning(_("Connecting to %s failed"),
661 folder->account->recv_server);
662 session_destroy(SESSION(session));
665 log_warning(_("IMAP4 connection to %s has been"
666 " disconnected. Reconnecting...\n"),
667 folder->account->recv_server);
668 statusbar_print_all(_("IMAP4 connection to %s has been"
669 " disconnected. Reconnecting...\n"),
670 folder->account->recv_server);
671 SESSION(session)->state = SESSION_DISCONNECTED;
672 session_destroy(SESSION(session));
673 /* Clear folders session to make imap_session_get create
674 a new session, because of rfolder->session == NULL
675 it will not try to reconnect again and so avoid an
677 rfolder->session = NULL;
678 session = imap_session_get(folder);
679 rfolder->session = SESSION(session);
685 #define lock_session() {\
686 debug_print("locking session\n"); \
687 session->busy = TRUE;\
690 #define unlock_session() {\
691 debug_print("unlocking session\n"); \
692 session->busy = FALSE;\
695 static IMAPSession *imap_session_get(Folder *folder)
697 RemoteFolder *rfolder = REMOTE_FOLDER(folder);
698 IMAPSession *session = NULL;
700 g_return_val_if_fail(folder != NULL, NULL);
701 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, NULL);
702 g_return_val_if_fail(folder->account != NULL, NULL);
704 if (prefs_common.work_offline &&
705 !inc_offline_should_override(
706 _("Claws Mail needs network access in order "
707 "to access the IMAP server."))) {
711 /* Make sure we have a session */
712 if (rfolder->session != NULL) {
713 session = IMAP_SESSION(rfolder->session);
714 /* don't do that yet...
719 imap_reset_uid_lists(folder);
720 if (time(NULL) - rfolder->last_failure <= 2)
722 session = imap_session_new(folder, folder->account);
724 if(session == NULL) {
725 rfolder->last_failure = time(NULL);
729 /* Make sure session is authenticated */
730 if (!IMAP_SESSION(session)->authenticated)
731 imap_session_authenticate(IMAP_SESSION(session), folder->account);
733 if (!IMAP_SESSION(session)->authenticated) {
734 imap_threaded_disconnect(session->folder);
735 SESSION(session)->state = SESSION_DISCONNECTED;
736 session_destroy(SESSION(session));
737 rfolder->session = NULL;
738 rfolder->last_failure = time(NULL);
742 /* I think the point of this code is to avoid sending a
743 * keepalive if we've used the session recently and therefore
744 * think it's still alive. Unfortunately, most of the code
745 * does not yet check for errors on the socket, and so if the
746 * connection drops we don't notice until the timeout expires.
747 * A better solution than sending a NOOP every time would be
748 * for every command to be prepared to retry until it is
749 * successfully sent. -- mbp */
750 if (time(NULL) - SESSION(session)->last_access_time > SESSION_TIMEOUT_INTERVAL) {
751 /* verify that the session is still alive */
752 if (imap_cmd_noop(session) != IMAP_SUCCESS) {
753 debug_print("disconnected!\n");
754 session = imap_reconnect_if_possible(folder, session);
758 rfolder->session = SESSION(session);
760 return IMAP_SESSION(session);
763 static IMAPSession *imap_session_new(Folder * folder,
764 const PrefsAccount *account)
766 IMAPSession *session;
769 int authenticated = FALSE;
772 /* FIXME: IMAP over SSL only... */
775 port = account->set_imapport ? account->imapport
776 : account->ssl_imap == SSL_TUNNEL ? IMAPS_PORT : IMAP4_PORT;
777 ssl_type = account->ssl_imap;
779 if (account->ssl_imap != SSL_NONE) {
780 if (alertpanel_full(_("Insecure connection"),
781 _("This connection is configured to be secured "
782 "using SSL, but SSL is not available in this "
783 "build of Claws Mail. \n\n"
784 "Do you want to continue connecting to this "
785 "server? The communication would not be "
787 GTK_STOCK_CANCEL, _("Con_tinue connecting"),
788 NULL, FALSE, NULL, ALERT_WARNING,
789 G_ALERTDEFAULT) != G_ALERTALTERNATE)
792 port = account->set_imapport ? account->imapport
797 statusbar_print_all(_("Connecting to IMAP4 server: %s..."), folder->account->recv_server);
798 if (account->set_tunnelcmd) {
799 r = imap_threaded_connect_cmd(folder,
801 account->recv_server,
806 if (ssl_type == SSL_TUNNEL) {
807 r = imap_threaded_connect_ssl(folder,
808 account->recv_server,
814 r = imap_threaded_connect(folder,
815 account->recv_server,
821 if (r == MAILIMAP_NO_ERROR_AUTHENTICATED) {
822 authenticated = TRUE;
824 else if (r == MAILIMAP_NO_ERROR_NON_AUTHENTICATED) {
825 authenticated = FALSE;
828 if(!prefs_common.no_recv_err_panel) {
829 alertpanel_error(_("Can't connect to IMAP4 server: %s:%d"),
830 account->recv_server, port);
832 log_error(_("Can't connect to IMAP4 server: %s:%d\n"),
833 account->recv_server, port);
839 session = g_new0(IMAPSession, 1);
840 session_init(SESSION(session));
841 SESSION(session)->type = SESSION_IMAP;
842 SESSION(session)->server = g_strdup(account->recv_server);
843 SESSION(session)->sock = NULL;
845 SESSION(session)->destroy = imap_session_destroy;
847 session->capability = NULL;
849 session->authenticated = authenticated;
850 session->mbox = NULL;
851 session->cmd_count = 0;
852 session->folder = folder;
853 IMAP_FOLDER(session->folder)->last_seen_separator = 0;
856 if (account->ssl_imap == SSL_STARTTLS) {
859 ok = imap_cmd_starttls(session);
860 if (ok != IMAP_SUCCESS) {
861 log_warning(_("Can't start TLS session.\n"));
862 session_destroy(SESSION(session));
866 imap_free_capabilities(session);
867 session->authenticated = FALSE;
868 session->uidplus = FALSE;
869 session->cmd_count = 1;
872 log_message("IMAP connection is %s-authenticated\n",
873 (session->authenticated) ? "pre" : "un");
878 static void imap_session_authenticate(IMAPSession *session,
879 const PrefsAccount *account)
881 gchar *pass, *acc_pass;
882 gboolean failed = FALSE;
884 g_return_if_fail(account->userid != NULL);
885 acc_pass = account->passwd;
888 if (!pass && account->imap_auth_type != IMAP_AUTH_ANON) {
890 tmp_pass = input_dialog_query_password(account->recv_server, account->userid);
893 Xstrdup_a(pass, tmp_pass, {g_free(tmp_pass); return;});
895 } else if (account->imap_auth_type == IMAP_AUTH_ANON) {
898 statusbar_print_all(_("Connecting to IMAP4 server %s...\n"),
899 account->recv_server);
900 if (imap_auth(session, account->userid, pass, account->imap_auth_type) != IMAP_SUCCESS) {
908 if (prefs_common.no_recv_err_panel) {
909 log_error(_("Couldn't login to IMAP server %s."), account->recv_server);
910 mainwindow_show_error();
912 alertpanel_error_log(_("Couldn't login to IMAP server %s."), account->recv_server);
919 session->authenticated = TRUE;
923 static void imap_session_destroy(Session *session)
925 if (session->state != SESSION_DISCONNECTED)
926 imap_threaded_disconnect(IMAP_SESSION(session)->folder);
928 imap_free_capabilities(IMAP_SESSION(session));
929 g_free(IMAP_SESSION(session)->mbox);
930 sock_close(session->sock);
931 session->sock = NULL;
934 static gchar *imap_fetch_msg(Folder *folder, FolderItem *item, gint uid)
936 return imap_fetch_msg_full(folder, item, uid, TRUE, TRUE);
939 static guint get_size_with_crs(MsgInfo *info)
948 fp = procmsg_open_message(info);
952 while (fgets(buf, sizeof (buf), fp) != NULL) {
954 if (!strstr(buf, "\r") && strstr(buf, "\n"))
962 static gchar *imap_fetch_msg_full(Folder *folder, FolderItem *item, gint uid,
963 gboolean headers, gboolean body)
965 gchar *path, *filename;
966 IMAPSession *session;
969 g_return_val_if_fail(folder != NULL, NULL);
970 g_return_val_if_fail(item != NULL, NULL);
975 path = folder_item_get_path(item);
976 if (!is_dir_exist(path))
978 filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(uid), NULL);
980 debug_print("trying to fetch cached %s\n", filename);
981 if (is_file_exist(filename)) {
982 /* see whether the local file represents the whole message
983 * or not. As the IMAP server reports size with \r chars,
984 * we have to update the local file (UNIX \n only) size */
985 MsgInfo *msginfo = imap_parse_msg(filename, item);
986 MsgInfo *cached = msgcache_get_msg(item->cache,uid);
987 guint have_size = get_size_with_crs(msginfo);
990 debug_print("message %d has been already %scached (%d/%d).\n", uid,
991 have_size >= cached->size ? "fully ":"",
992 have_size, (int)cached->size);
994 if (cached && (cached->size <= have_size || !body)) {
995 procmsg_msginfo_free(cached);
996 procmsg_msginfo_free(msginfo);
997 file_strip_crs(filename);
999 } else if (!cached && time(NULL) - get_file_mtime(filename) < 60) {
1000 debug_print("message not cached and file recent, considering file complete\n");
1001 procmsg_msginfo_free(msginfo);
1002 file_strip_crs(filename);
1005 procmsg_msginfo_free(cached);
1006 procmsg_msginfo_free(msginfo);
1010 session = imap_session_get(folder);
1019 debug_print("IMAP fetching messages\n");
1020 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
1021 NULL, NULL, NULL, NULL, FALSE);
1022 if (ok != IMAP_SUCCESS) {
1023 g_warning("can't select mailbox %s\n", item->path);
1029 debug_print("getting message %d...\n", uid);
1030 ok = imap_cmd_fetch(session, (guint32)uid, filename, headers, body);
1032 if (ok != IMAP_SUCCESS) {
1033 g_warning("can't fetch message %d\n", uid);
1040 file_strip_crs(filename);
1044 static gint imap_add_msg(Folder *folder, FolderItem *dest,
1045 const gchar *file, MsgFlags *flags)
1049 MsgFileInfo fileinfo;
1051 g_return_val_if_fail(file != NULL, -1);
1053 fileinfo.msginfo = NULL;
1054 fileinfo.file = (gchar *)file;
1055 fileinfo.flags = flags;
1056 file_list.data = &fileinfo;
1057 file_list.next = NULL;
1059 ret = imap_add_msgs(folder, dest, &file_list, NULL);
1063 static gint imap_add_msgs(Folder *folder, FolderItem *dest, GSList *file_list,
1064 GRelation *relation)
1067 IMAPSession *session;
1068 guint32 last_uid = 0;
1070 MsgFileInfo *fileinfo;
1072 gint curnum = 0, total = 0;
1075 g_return_val_if_fail(folder != NULL, -1);
1076 g_return_val_if_fail(dest != NULL, -1);
1077 g_return_val_if_fail(file_list != NULL, -1);
1079 session = imap_session_get(folder);
1084 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
1086 statusbar_print_all(_("Adding messages..."));
1087 total = g_slist_length(file_list);
1088 for (cur = file_list; cur != NULL; cur = cur->next) {
1089 IMAPFlags iflags = 0;
1090 guint32 new_uid = 0;
1091 gchar *real_file = NULL;
1092 fileinfo = (MsgFileInfo *)cur->data;
1094 statusbar_progress_all(curnum, total, 1);
1097 if (fileinfo->flags) {
1098 if (MSG_IS_MARKED(*fileinfo->flags))
1099 iflags |= IMAP_FLAG_FLAGGED;
1100 if (MSG_IS_REPLIED(*fileinfo->flags))
1101 iflags |= IMAP_FLAG_ANSWERED;
1102 if (!MSG_IS_UNREAD(*fileinfo->flags))
1103 iflags |= IMAP_FLAG_SEEN;
1106 if (real_file == NULL)
1107 real_file = g_strdup(fileinfo->file);
1109 if (folder_has_parent_of_type(dest, F_QUEUE) ||
1110 folder_has_parent_of_type(dest, F_OUTBOX) ||
1111 folder_has_parent_of_type(dest, F_DRAFT) ||
1112 folder_has_parent_of_type(dest, F_TRASH))
1113 iflags |= IMAP_FLAG_SEEN;
1115 ok = imap_cmd_append(session, destdir, real_file, iflags,
1118 if (ok != IMAP_SUCCESS) {
1119 g_warning("can't append message %s\n", real_file);
1123 statusbar_progress_all(0,0,0);
1124 statusbar_pop_all();
1127 debug_print("appended new message as %d\n", new_uid);
1128 /* put the local file in the imapcache, so that we don't
1129 * have to fetch it back later. */
1131 gchar *cache_path = folder_item_get_path(dest);
1132 if (!is_dir_exist(cache_path))
1133 make_dir_hier(cache_path);
1134 if (is_dir_exist(cache_path)) {
1135 gchar *cache_file = g_strconcat(
1136 cache_path, G_DIR_SEPARATOR_S,
1137 itos(new_uid), NULL);
1138 copy_file(real_file, cache_file, TRUE);
1139 debug_print("copied to cache: %s\n", cache_file);
1146 if (relation != NULL)
1147 g_relation_insert(relation, fileinfo->msginfo != NULL ?
1148 (gpointer) fileinfo->msginfo : (gpointer) fileinfo,
1149 GINT_TO_POINTER(dest->last_num + 1));
1151 new_uid = dest->last_num+1;
1153 if (last_uid < new_uid) {
1159 statusbar_progress_all(0,0,0);
1160 statusbar_pop_all();
1162 imap_cmd_expunge(session);
1170 static gint imap_do_copy_msgs(Folder *folder, FolderItem *dest,
1171 MsgInfoList *msglist, GRelation *relation)
1175 GSList *seq_list, *cur;
1177 IMAPSession *session;
1178 gint ok = IMAP_SUCCESS;
1179 GRelation *uid_mapping;
1181 gboolean single = FALSE;
1183 g_return_val_if_fail(folder != NULL, -1);
1184 g_return_val_if_fail(dest != NULL, -1);
1185 g_return_val_if_fail(msglist != NULL, -1);
1187 session = imap_session_get(folder);
1193 msginfo = (MsgInfo *)msglist->data;
1194 if (msglist->next == NULL)
1196 src = msginfo->folder;
1198 g_warning("the src folder is identical to the dest.\n");
1203 if (src->folder != dest->folder) {
1204 GSList *infolist = NULL, *cur;
1206 for (cur = msglist; cur; cur = cur->next) {
1207 msginfo = (MsgInfo *)cur->data;
1208 MsgFileInfo *fileinfo = g_new0(MsgFileInfo, 1);
1209 fileinfo->file = procmsg_get_message_file(msginfo);
1210 fileinfo->flags = &(msginfo->flags);
1211 infolist = g_slist_prepend(infolist, fileinfo);
1213 infolist = g_slist_reverse(infolist);
1214 res = folder_item_add_msgs(dest, infolist, FALSE);
1215 for (cur = infolist; cur; cur = cur->next) {
1216 MsgFileInfo *info = (MsgFileInfo *)cur->data;
1220 g_slist_free(infolist);
1224 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
1225 NULL, NULL, NULL, NULL, FALSE);
1226 if (ok != IMAP_SUCCESS) {
1231 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
1232 seq_list = imap_get_lep_set_from_msglist(msglist);
1233 uid_mapping = g_relation_new(2);
1234 g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
1236 statusbar_print_all(_("Copying messages..."));
1237 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
1238 struct mailimap_set * seq_set;
1239 struct mailimap_set * source = NULL;
1240 struct mailimap_set * dest = NULL;
1241 seq_set = cur->data;
1243 debug_print("Copying messages from %s to %s ...\n",
1244 src->path, destdir);
1246 ok = imap_cmd_copy(session, seq_set, destdir, uid_mapping,
1249 if (ok == IMAP_SUCCESS) {
1250 if (single && relation && source && dest) {
1251 clistiter *l = clist_begin(source->set_list);
1252 struct mailimap_set_item *i = (struct mailimap_set_item *)clist_content(l);
1253 int snum = i->set_first;
1255 l = clist_begin(dest->set_list);
1256 i = (struct mailimap_set_item *)clist_content(l);
1257 dnum = i->set_first;
1258 g_relation_insert(uid_mapping, GINT_TO_POINTER(snum),
1259 GINT_TO_POINTER(dnum));
1265 mailimap_set_free(source);
1267 mailimap_set_free(dest);
1269 if (ok != IMAP_SUCCESS) {
1270 g_relation_destroy(uid_mapping);
1271 imap_lep_set_free(seq_list);
1277 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
1278 MsgInfo *msginfo = (MsgInfo *)cur->data;
1281 tuples = g_relation_select(uid_mapping,
1282 GINT_TO_POINTER(msginfo->msgnum),
1284 if (tuples->len > 0) {
1285 gint num = GPOINTER_TO_INT(g_tuples_index(tuples, 0, 1));
1286 g_relation_insert(relation, msginfo,
1287 GINT_TO_POINTER(num));
1290 debug_print("copied new message as %d\n", num);
1291 /* put the local file in the imapcache, so that we don't
1292 * have to fetch it back later. */
1294 gchar *cache_path = folder_item_get_path(msginfo->folder);
1295 gchar *real_file = g_strconcat(
1296 cache_path, G_DIR_SEPARATOR_S,
1297 itos(msginfo->msgnum), NULL);
1298 gchar *cache_file = NULL;
1300 cache_path = folder_item_get_path(dest);
1301 cache_file = g_strconcat(
1302 cache_path, G_DIR_SEPARATOR_S,
1304 if (!is_dir_exist(cache_path))
1305 make_dir_hier(cache_path);
1306 if (is_file_exist(real_file) && is_dir_exist(cache_path)) {
1307 copy_file(real_file, cache_file, TRUE);
1308 debug_print("copied to cache: %s\n", cache_file);
1315 g_relation_insert(relation, msginfo,
1316 GINT_TO_POINTER(0));
1317 g_tuples_destroy(tuples);
1319 statusbar_pop_all();
1321 g_relation_destroy(uid_mapping);
1322 imap_lep_set_free(seq_list);
1326 IMAP_FOLDER_ITEM(dest)->lastuid = 0;
1327 IMAP_FOLDER_ITEM(dest)->uid_next = 0;
1328 g_slist_free(IMAP_FOLDER_ITEM(dest)->uid_list);
1329 IMAP_FOLDER_ITEM(dest)->uid_list = NULL;
1332 if (ok == IMAP_SUCCESS)
1338 static gint imap_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1342 g_return_val_if_fail(msginfo != NULL, -1);
1344 msglist.data = msginfo;
1345 msglist.next = NULL;
1347 return imap_copy_msgs(folder, dest, &msglist, NULL);
1350 static gint imap_copy_msgs(Folder *folder, FolderItem *dest,
1351 MsgInfoList *msglist, GRelation *relation)
1356 g_return_val_if_fail(folder != NULL, -1);
1357 g_return_val_if_fail(dest != NULL, -1);
1358 g_return_val_if_fail(msglist != NULL, -1);
1360 msginfo = (MsgInfo *)msglist->data;
1361 g_return_val_if_fail(msginfo->folder != NULL, -1);
1363 ret = imap_do_copy_msgs(folder, dest, msglist, relation);
1368 static gint imap_do_remove_msgs(Folder *folder, FolderItem *dest,
1369 MsgInfoList *msglist, GRelation *relation)
1371 gchar *destdir, *dir;
1372 GSList *numlist = NULL, *cur;
1374 IMAPSession *session;
1375 gint ok = IMAP_SUCCESS;
1376 GRelation *uid_mapping;
1378 g_return_val_if_fail(folder != NULL, -1);
1379 g_return_val_if_fail(dest != NULL, -1);
1380 g_return_val_if_fail(msglist != NULL, -1);
1382 session = imap_session_get(folder);
1387 msginfo = (MsgInfo *)msglist->data;
1389 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
1390 NULL, NULL, NULL, NULL, FALSE);
1391 if (ok != IMAP_SUCCESS) {
1396 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
1397 for (cur = msglist; cur; cur = cur->next) {
1398 msginfo = (MsgInfo *)cur->data;
1399 if (!MSG_IS_DELETED(msginfo->flags))
1400 numlist = g_slist_prepend(numlist, GINT_TO_POINTER(msginfo->msgnum));
1402 numlist = g_slist_reverse(numlist);
1404 uid_mapping = g_relation_new(2);
1405 g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
1407 ok = imap_set_message_flags
1408 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
1409 numlist, IMAP_FLAG_DELETED, TRUE);
1410 if (ok != IMAP_SUCCESS) {
1411 log_warning(_("can't set deleted flags\n"));
1415 ok = imap_cmd_expunge(session);
1416 if (ok != IMAP_SUCCESS) {
1417 log_warning(_("can't expunge\n"));
1422 dir = folder_item_get_path(msginfo->folder);
1423 if (is_dir_exist(dir)) {
1424 for (cur = msglist; cur; cur = cur->next) {
1425 msginfo = (MsgInfo *)cur->data;
1426 remove_numbered_files(dir, msginfo->msgnum, msginfo->msgnum);
1431 g_relation_destroy(uid_mapping);
1432 g_slist_free(numlist);
1436 if (ok == IMAP_SUCCESS)
1442 static gint imap_remove_msgs(Folder *folder, FolderItem *dest,
1443 MsgInfoList *msglist, GRelation *relation)
1447 g_return_val_if_fail(folder != NULL, -1);
1448 g_return_val_if_fail(dest != NULL, -1);
1449 if (msglist == NULL)
1452 msginfo = (MsgInfo *)msglist->data;
1453 g_return_val_if_fail(msginfo->folder != NULL, -1);
1455 return imap_do_remove_msgs(folder, dest, msglist, relation);
1458 static gint imap_remove_all_msg(Folder *folder, FolderItem *item)
1460 GSList *list = folder_item_get_msg_list(item);
1461 gint res = imap_remove_msgs(folder, item, list, NULL);
1462 procmsg_msg_list_free(list);
1466 static gboolean imap_is_msg_changed(Folder *folder, FolderItem *item,
1469 /* TODO: properly implement this method */
1473 static gint imap_close(Folder *folder, FolderItem *item)
1478 static gint imap_scan_tree(Folder *folder)
1480 FolderItem *item = NULL;
1481 IMAPSession *session;
1482 gchar *root_folder = NULL;
1484 g_return_val_if_fail(folder != NULL, -1);
1485 g_return_val_if_fail(folder->account != NULL, -1);
1487 session = imap_session_get(folder);
1489 if (!folder->node) {
1490 folder_tree_destroy(folder);
1491 item = folder_item_new(folder, folder->name, NULL);
1492 item->folder = folder;
1493 folder->node = item->node = g_node_new(item);
1499 if (folder->account->imap_dir && *folder->account->imap_dir) {
1504 Xstrdup_a(root_folder, folder->account->imap_dir, {unlock_session();return -1;});
1505 extract_quote(root_folder, '"');
1506 subst_char(root_folder,
1507 imap_get_path_separator(IMAP_FOLDER(folder),
1510 strtailchomp(root_folder, '/');
1511 real_path = imap_get_real_path
1512 (IMAP_FOLDER(folder), root_folder);
1513 debug_print("IMAP root directory: %s\n", real_path);
1515 /* check if root directory exist */
1517 r = imap_threaded_list(session->folder, "", real_path,
1519 if ((r != MAILIMAP_NO_ERROR) || (clist_count(lep_list) == 0)) {
1520 if (!folder->node) {
1521 item = folder_item_new(folder, folder->name, NULL);
1522 item->folder = folder;
1523 folder->node = item->node = g_node_new(item);
1528 mailimap_list_result_free(lep_list);
1534 item = FOLDER_ITEM(folder->node->data);
1535 if (!item || ((item->path || root_folder) &&
1536 strcmp2(item->path, root_folder) != 0)) {
1537 folder_tree_destroy(folder);
1538 item = folder_item_new(folder, folder->name, root_folder);
1539 item->folder = folder;
1540 folder->node = item->node = g_node_new(item);
1543 imap_scan_tree_recursive(session, FOLDER_ITEM(folder->node->data));
1544 imap_create_missing_folders(folder);
1550 static gint imap_scan_tree_recursive(IMAPSession *session, FolderItem *item)
1553 IMAPFolder *imapfolder;
1554 FolderItem *new_item;
1555 GSList *item_list, *cur;
1558 gchar *wildcard_path;
1564 g_return_val_if_fail(item != NULL, -1);
1565 g_return_val_if_fail(item->folder != NULL, -1);
1566 g_return_val_if_fail(item->no_sub == FALSE, -1);
1568 folder = item->folder;
1569 imapfolder = IMAP_FOLDER(folder);
1571 separator = imap_get_path_separator(imapfolder, item->path);
1573 if (folder->ui_func)
1574 folder->ui_func(folder, item, folder->ui_func_data);
1577 wildcard[0] = separator;
1580 real_path = imap_get_real_path(imapfolder, item->path);
1584 real_path = g_strdup("");
1587 Xstrcat_a(wildcard_path, real_path, wildcard,
1588 {g_free(real_path); return IMAP_ERROR;});
1590 r = imap_threaded_list(folder, "", wildcard_path, &lep_list);
1591 if (r != MAILIMAP_NO_ERROR) {
1595 item_list = imap_list_from_lep(imapfolder,
1596 lep_list, real_path, FALSE);
1597 mailimap_list_result_free(lep_list);
1602 node = item->node->children;
1603 while (node != NULL) {
1604 FolderItem *old_item = FOLDER_ITEM(node->data);
1605 GNode *next = node->next;
1608 for (cur = item_list; cur != NULL; cur = cur->next) {
1609 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1610 if (!strcmp2(old_item->path, cur_item->path)) {
1611 new_item = cur_item;
1616 debug_print("folder '%s' not found. removing...\n",
1618 folder_item_remove(old_item);
1620 old_item->no_sub = new_item->no_sub;
1621 old_item->no_select = new_item->no_select;
1622 if (old_item->no_sub == TRUE && node->children) {
1623 debug_print("folder '%s' doesn't have "
1624 "subfolders. removing...\n",
1626 folder_item_remove_children(old_item);
1633 for (cur = item_list; cur != NULL; cur = cur->next) {
1634 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1637 for (node = item->node->children; node != NULL;
1638 node = node->next) {
1639 if (!strcmp2(FOLDER_ITEM(node->data)->path,
1641 new_item = FOLDER_ITEM(node->data);
1642 folder_item_destroy(cur_item);
1648 new_item = cur_item;
1649 debug_print("new folder '%s' found.\n", new_item->path);
1650 folder_item_append(item, new_item);
1653 if (!strcmp(new_item->path, "INBOX")) {
1654 new_item->stype = F_INBOX;
1655 folder->inbox = new_item;
1656 } else if (!folder_item_parent(item) || item->stype == F_INBOX) {
1659 base = g_path_get_basename(new_item->path);
1661 if (!folder->outbox && !g_ascii_strcasecmp(base, "Sent")) {
1662 new_item->stype = F_OUTBOX;
1663 folder->outbox = new_item;
1664 } else if (!folder->draft && !g_ascii_strcasecmp(base, "Drafts")) {
1665 new_item->stype = F_DRAFT;
1666 folder->draft = new_item;
1667 } else if (!folder->queue && !g_ascii_strcasecmp(base, "Queue")) {
1668 new_item->stype = F_QUEUE;
1669 folder->queue = new_item;
1670 } else if (!folder->trash && !g_ascii_strcasecmp(base, "Trash")) {
1671 new_item->stype = F_TRASH;
1672 folder->trash = new_item;
1677 if (new_item->no_sub == FALSE)
1678 imap_scan_tree_recursive(session, new_item);
1681 g_slist_free(item_list);
1683 return IMAP_SUCCESS;
1686 static gint imap_create_tree(Folder *folder)
1688 g_return_val_if_fail(folder != NULL, -1);
1689 g_return_val_if_fail(folder->node != NULL, -1);
1690 g_return_val_if_fail(folder->node->data != NULL, -1);
1691 g_return_val_if_fail(folder->account != NULL, -1);
1693 imap_scan_tree(folder);
1694 imap_create_missing_folders(folder);
1699 static void imap_create_missing_folders(Folder *folder)
1701 g_return_if_fail(folder != NULL);
1704 folder->inbox = imap_create_special_folder
1705 (folder, F_INBOX, "INBOX");
1707 folder->trash = imap_create_special_folder
1708 (folder, F_TRASH, "Trash");
1710 folder->queue = imap_create_special_folder
1711 (folder, F_QUEUE, "Queue");
1712 if (!folder->outbox)
1713 folder->outbox = imap_create_special_folder
1714 (folder, F_OUTBOX, "Sent");
1716 folder->draft = imap_create_special_folder
1717 (folder, F_DRAFT, "Drafts");
1720 static FolderItem *imap_create_special_folder(Folder *folder,
1721 SpecialFolderItemType stype,
1725 FolderItem *new_item;
1727 g_return_val_if_fail(folder != NULL, NULL);
1728 g_return_val_if_fail(folder->node != NULL, NULL);
1729 g_return_val_if_fail(folder->node->data != NULL, NULL);
1730 g_return_val_if_fail(folder->account != NULL, NULL);
1731 g_return_val_if_fail(name != NULL, NULL);
1733 item = FOLDER_ITEM(folder->node->data);
1734 new_item = imap_create_folder(folder, item, name);
1737 g_warning("Can't create '%s'\n", name);
1738 if (!folder->inbox) return NULL;
1740 new_item = imap_create_folder(folder, folder->inbox, name);
1742 g_warning("Can't create '%s' under INBOX\n", name);
1744 new_item->stype = stype;
1746 new_item->stype = stype;
1751 static gchar *imap_folder_get_path(Folder *folder)
1755 g_return_val_if_fail(folder != NULL, NULL);
1756 g_return_val_if_fail(folder->account != NULL, NULL);
1758 folder_path = g_strconcat(get_imap_cache_dir(),
1760 folder->account->recv_server,
1762 folder->account->userid,
1768 static gchar *imap_item_get_path(Folder *folder, FolderItem *item)
1770 gchar *folder_path, *path;
1772 g_return_val_if_fail(folder != NULL, NULL);
1773 g_return_val_if_fail(item != NULL, NULL);
1774 folder_path = imap_folder_get_path(folder);
1776 g_return_val_if_fail(folder_path != NULL, NULL);
1777 if (folder_path[0] == G_DIR_SEPARATOR) {
1779 path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
1782 path = g_strdup(folder_path);
1785 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1786 folder_path, G_DIR_SEPARATOR_S,
1789 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1792 g_free(folder_path);
1797 static FolderItem *imap_create_folder(Folder *folder, FolderItem *parent,
1800 gchar *dirpath, *imap_path;
1801 IMAPSession *session;
1802 FolderItem *new_item;
1807 gboolean no_select = FALSE, no_sub = FALSE;
1809 g_return_val_if_fail(folder != NULL, NULL);
1810 g_return_val_if_fail(folder->account != NULL, NULL);
1811 g_return_val_if_fail(parent != NULL, NULL);
1812 g_return_val_if_fail(name != NULL, NULL);
1814 session = imap_session_get(folder);
1820 if (!folder_item_parent(parent) && strcmp(name, "INBOX") == 0) {
1821 dirpath = g_strdup(name);
1822 }else if (parent->path)
1823 dirpath = g_strconcat(parent->path, "/", name, NULL);
1824 else if ((p = strchr(name, '/')) != NULL && *(p + 1) != '\0')
1825 dirpath = g_strdup(name);
1826 else if (folder->account->imap_dir && *folder->account->imap_dir) {
1829 Xstrdup_a(imap_dir, folder->account->imap_dir, {unlock_session();return NULL;});
1830 strtailchomp(imap_dir, '/');
1831 dirpath = g_strconcat(imap_dir, "/", name, NULL);
1833 dirpath = g_strdup(name);
1837 /* keep trailing directory separator to create a folder that contains
1839 imap_path = imap_utf8_to_modified_utf7(dirpath);
1841 strtailchomp(dirpath, '/');
1842 Xstrdup_a(new_name, name, {
1847 separator = imap_get_path_separator(IMAP_FOLDER(folder), imap_path);
1848 imap_path_separator_subst(imap_path, separator);
1849 /* remove trailing / for display */
1850 strtailchomp(new_name, '/');
1852 if (strcmp(dirpath, "INBOX") != 0) {
1854 gboolean exist = FALSE;
1858 argbuf = g_ptr_array_new();
1859 r = imap_threaded_list(folder, "", imap_path, &lep_list);
1860 if (r != MAILIMAP_NO_ERROR) {
1861 log_warning(_("can't create mailbox: LIST failed\n"));
1864 ptr_array_free_strings(argbuf);
1865 g_ptr_array_free(argbuf, TRUE);
1870 if (clist_count(lep_list) > 0)
1872 mailimap_list_result_free(lep_list);
1875 ok = imap_cmd_create(session, imap_path);
1876 if (ok != IMAP_SUCCESS) {
1877 log_warning(_("can't create mailbox\n"));
1883 r = imap_threaded_list(folder, "", imap_path, &lep_list);
1884 if (r == MAILIMAP_NO_ERROR) {
1885 GSList *item_list = imap_list_from_lep(IMAP_FOLDER(folder),
1886 lep_list, dirpath, TRUE);
1888 FolderItem *cur_item = FOLDER_ITEM(item_list->data);
1889 no_select = cur_item->no_select;
1890 no_sub = cur_item->no_sub;
1891 g_slist_free(item_list);
1893 mailimap_list_result_free(lep_list);
1900 /* just get flags */
1901 r = imap_threaded_list(folder, "", "INBOX", &lep_list);
1902 if (r == MAILIMAP_NO_ERROR) {
1903 GSList *item_list = imap_list_from_lep(IMAP_FOLDER(folder),
1904 lep_list, dirpath, TRUE);
1906 FolderItem *cur_item = FOLDER_ITEM(item_list->data);
1907 no_select = cur_item->no_select;
1908 no_sub = cur_item->no_sub;
1909 g_slist_free(item_list);
1911 mailimap_list_result_free(lep_list);
1915 new_item = folder_item_new(folder, new_name, dirpath);
1916 new_item->no_select = no_select;
1917 new_item->no_sub = no_sub;
1918 folder_item_append(parent, new_item);
1922 dirpath = folder_item_get_path(new_item);
1923 if (!is_dir_exist(dirpath))
1924 make_dir_hier(dirpath);
1930 static gint imap_rename_folder(Folder *folder, FolderItem *item,
1935 gchar *real_oldpath;
1936 gchar *real_newpath;
1938 gchar *old_cache_dir;
1939 gchar *new_cache_dir;
1940 IMAPSession *session;
1943 gint exists, recent, unseen;
1944 guint32 uid_validity;
1946 g_return_val_if_fail(folder != NULL, -1);
1947 g_return_val_if_fail(item != NULL, -1);
1948 g_return_val_if_fail(item->path != NULL, -1);
1949 g_return_val_if_fail(name != NULL, -1);
1951 session = imap_session_get(folder);
1957 if (strchr(name, imap_get_path_separator(IMAP_FOLDER(folder), item->path)) != NULL) {
1958 g_warning(_("New folder name must not contain the namespace "
1964 real_oldpath = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1966 g_free(session->mbox);
1967 session->mbox = NULL;
1968 ok = imap_cmd_examine(session, "INBOX",
1969 &exists, &recent, &unseen, &uid_validity, FALSE);
1970 if (ok != IMAP_SUCCESS) {
1971 g_free(real_oldpath);
1976 separator = imap_get_path_separator(IMAP_FOLDER(folder), item->path);
1977 if (strchr(item->path, G_DIR_SEPARATOR)) {
1978 dirpath = g_path_get_dirname(item->path);
1979 newpath = g_strconcat(dirpath, G_DIR_SEPARATOR_S, name, NULL);
1982 newpath = g_strdup(name);
1984 real_newpath = imap_utf8_to_modified_utf7(newpath);
1985 imap_path_separator_subst(real_newpath, separator);
1987 ok = imap_cmd_rename(session, real_oldpath, real_newpath);
1988 if (ok != IMAP_SUCCESS) {
1989 log_warning(_("can't rename mailbox: %s to %s\n"),
1990 real_oldpath, real_newpath);
1991 g_free(real_oldpath);
1993 g_free(real_newpath);
1999 item->name = g_strdup(name);
2001 old_cache_dir = folder_item_get_path(item);
2003 paths[0] = g_strdup(item->path);
2005 g_node_traverse(item->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
2006 imap_rename_folder_func, paths);
2008 if (is_dir_exist(old_cache_dir)) {
2009 new_cache_dir = folder_item_get_path(item);
2010 if (rename(old_cache_dir, new_cache_dir) < 0) {
2011 FILE_OP_ERROR(old_cache_dir, "rename");
2013 g_free(new_cache_dir);
2016 g_free(old_cache_dir);
2019 g_free(real_oldpath);
2020 g_free(real_newpath);
2025 static gint imap_remove_folder_real(Folder *folder, FolderItem *item)
2028 IMAPSession *session;
2032 g_return_val_if_fail(folder != NULL, -1);
2033 g_return_val_if_fail(item != NULL, -1);
2034 g_return_val_if_fail(item->path != NULL, -1);
2036 session = imap_session_get(folder);
2041 path = imap_get_real_path(IMAP_FOLDER(folder), item->path);
2043 ok = imap_cmd_delete(session, path);
2044 if (ok != IMAP_SUCCESS) {
2045 gchar *tmp = g_strdup_printf("%s%c", path,
2046 imap_get_path_separator(IMAP_FOLDER(folder), path));
2049 ok = imap_cmd_delete(session, path);
2052 if (ok != IMAP_SUCCESS) {
2053 log_warning(_("can't delete mailbox\n"));
2060 cache_dir = folder_item_get_path(item);
2061 if (is_dir_exist(cache_dir) && remove_dir_recursive(cache_dir) < 0)
2062 g_warning("can't remove directory '%s'\n", cache_dir);
2064 folder_item_remove(item);
2069 static gint imap_remove_folder(Folder *folder, FolderItem *item)
2073 g_return_val_if_fail(item != NULL, -1);
2074 g_return_val_if_fail(item->folder != NULL, -1);
2075 g_return_val_if_fail(item->node != NULL, -1);
2077 node = item->node->children;
2078 while (node != NULL) {
2080 if (imap_remove_folder(folder, FOLDER_ITEM(node->data)) < 0)
2084 debug_print("IMAP removing %s\n", item->path);
2086 if (imap_remove_all_msg(folder, item) < 0)
2088 return imap_remove_folder_real(folder, item);
2091 typedef struct _uncached_data {
2092 IMAPSession *session;
2094 MsgNumberList *numlist;
2100 static void *imap_get_uncached_messages_thread(void *data)
2102 uncached_data *stuff = (uncached_data *)data;
2103 IMAPSession *session = stuff->session;
2104 FolderItem *item = stuff->item;
2105 MsgNumberList *numlist = stuff->numlist;
2107 GSList *newlist = NULL;
2108 GSList *llast = NULL;
2109 GSList *seq_list, *cur;
2111 debug_print("uncached_messages\n");
2113 if (session == NULL || item == NULL || item->folder == NULL
2114 || FOLDER_CLASS(item->folder) != &imap_class) {
2119 seq_list = imap_get_lep_set_from_numlist(numlist);
2120 debug_print("get msgs info\n");
2121 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
2122 struct mailimap_set * imapset;
2128 imapset = cur->data;
2130 r = imap_threaded_fetch_env(session->folder,
2131 imapset, &env_list);
2132 if (r != MAILIMAP_NO_ERROR)
2135 session_set_access_time(SESSION(session));
2138 for(i = 0 ; i < carray_count(env_list) ; i ++) {
2139 struct imap_fetch_env_info * info;
2142 info = carray_get(env_list, i);
2143 msginfo = imap_envelope_from_lep(info, item);
2144 if (msginfo == NULL)
2146 msginfo->folder = item;
2148 llast = newlist = g_slist_append(newlist, msginfo);
2150 llast = g_slist_append(llast, msginfo);
2151 llast = llast->next;
2156 imap_fetch_env_free(env_list);
2159 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
2160 struct mailimap_set * imapset;
2162 imapset = cur->data;
2163 mailimap_set_free(imapset);
2166 session_set_access_time(SESSION(session));
2171 #define MAX_MSG_NUM 50
2173 static GSList *imap_get_uncached_messages(IMAPSession *session,
2175 MsgNumberList *numlist)
2177 GSList *result = NULL;
2179 uncached_data *data = g_new0(uncached_data, 1);
2184 data->total = g_slist_length(numlist);
2185 debug_print("messages list : %i\n", data->total);
2187 while (cur != NULL) {
2188 GSList * partial_result;
2196 while (count < MAX_MSG_NUM) {
2201 if (newlist == NULL)
2202 llast = newlist = g_slist_append(newlist, p);
2204 llast = g_slist_append(llast, p);
2205 llast = llast->next;
2215 data->session = session;
2217 data->numlist = newlist;
2220 if (prefs_common.work_offline &&
2221 !inc_offline_should_override(
2222 _("Claws Mail needs network access in order "
2223 "to access the IMAP server."))) {
2229 (GSList *)imap_get_uncached_messages_thread(data);
2231 statusbar_progress_all(data->cur,data->total, 1);
2233 g_slist_free(newlist);
2235 result = g_slist_concat(result, partial_result);
2239 statusbar_progress_all(0,0,0);
2240 statusbar_pop_all();
2245 static void imap_delete_all_cached_messages(FolderItem *item)
2249 g_return_if_fail(item != NULL);
2250 g_return_if_fail(item->folder != NULL);
2251 g_return_if_fail(FOLDER_CLASS(item->folder) == &imap_class);
2253 debug_print("Deleting all cached messages...\n");
2255 dir = folder_item_get_path(item);
2256 if (is_dir_exist(dir))
2257 remove_all_numbered_files(dir);
2260 debug_print("done.\n");
2263 static IMAPNameSpace *imap_find_namespace_from_list(GList *ns_list,
2266 IMAPNameSpace *namespace = NULL;
2267 gchar *tmp_path, *name;
2269 if (!path) path = "";
2271 for (; ns_list != NULL; ns_list = ns_list->next) {
2272 IMAPNameSpace *tmp_ns = ns_list->data;
2274 Xstrcat_a(tmp_path, path, "/", return namespace);
2275 Xstrdup_a(name, tmp_ns->name, return namespace);
2276 if (tmp_ns->separator && tmp_ns->separator != '/') {
2277 subst_char(tmp_path, tmp_ns->separator, '/');
2278 subst_char(name, tmp_ns->separator, '/');
2280 if (strncmp(tmp_path, name, strlen(name)) == 0)
2287 static IMAPNameSpace *imap_find_namespace(IMAPFolder *folder,
2290 IMAPNameSpace *namespace;
2292 g_return_val_if_fail(folder != NULL, NULL);
2294 namespace = imap_find_namespace_from_list(folder->ns_personal, path);
2295 if (namespace) return namespace;
2296 namespace = imap_find_namespace_from_list(folder->ns_others, path);
2297 if (namespace) return namespace;
2298 namespace = imap_find_namespace_from_list(folder->ns_shared, path);
2299 if (namespace) return namespace;
2304 gchar imap_get_path_separator_for_item(FolderItem *item)
2306 Folder *folder = NULL;
2307 IMAPFolder *imap_folder = NULL;
2310 folder = item->folder;
2315 imap_folder = IMAP_FOLDER(folder);
2320 return imap_get_path_separator(imap_folder, item->path);
2323 static gchar imap_get_path_separator(IMAPFolder *folder, const gchar *path)
2325 IMAPNameSpace *namespace;
2326 gchar separator = '/';
2327 IMAPSession *session = imap_session_get(FOLDER(folder));
2328 g_return_val_if_fail(session != NULL, '/');
2330 if (folder->last_seen_separator == 0) {
2332 int r = imap_threaded_list((Folder *)folder, "", "", &lep_list);
2333 if (r != MAILIMAP_NO_ERROR) {
2334 log_warning(_("LIST failed\n"));
2338 if (clist_count(lep_list) > 0) {
2339 clistiter * iter = clist_begin(lep_list);
2340 struct mailimap_mailbox_list * mb;
2341 mb = clist_content(iter);
2343 folder->last_seen_separator = mb->mb_delimiter;
2344 debug_print("got separator: %c\n", folder->last_seen_separator);
2346 mailimap_list_result_free(lep_list);
2349 if (folder->last_seen_separator != 0) {
2350 debug_print("using separator: %c\n", folder->last_seen_separator);
2351 return folder->last_seen_separator;
2354 namespace = imap_find_namespace(folder, path);
2355 if (namespace && namespace->separator)
2356 separator = namespace->separator;
2361 static gchar *imap_get_real_path(IMAPFolder *folder, const gchar *path)
2366 g_return_val_if_fail(folder != NULL, NULL);
2367 g_return_val_if_fail(path != NULL, NULL);
2369 real_path = imap_utf8_to_modified_utf7(path);
2370 separator = imap_get_path_separator(folder, path);
2371 imap_path_separator_subst(real_path, separator);
2376 static gint imap_set_message_flags(IMAPSession *session,
2377 MsgNumberList *numlist,
2385 seq_list = imap_get_lep_set_from_numlist(numlist);
2387 for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) {
2388 struct mailimap_set * imapset;
2390 imapset = cur->data;
2392 ok = imap_cmd_store(session, imapset,
2396 imap_lep_set_free(seq_list);
2398 return IMAP_SUCCESS;
2401 typedef struct _select_data {
2402 IMAPSession *session;
2407 guint32 *uid_validity;
2411 static gint imap_select(IMAPSession *session, IMAPFolder *folder,
2413 gint *exists, gint *recent, gint *unseen,
2414 guint32 *uid_validity, gboolean block)
2418 gint exists_, recent_, unseen_;
2419 guint32 uid_validity_;
2421 if (!exists && !recent && !unseen && !uid_validity) {
2422 if (session->mbox && strcmp(session->mbox, path) == 0)
2423 return IMAP_SUCCESS;
2432 uid_validity = &uid_validity_;
2434 g_free(session->mbox);
2435 session->mbox = NULL;
2437 real_path = imap_get_real_path(folder, path);
2439 ok = imap_cmd_select(session, real_path,
2440 exists, recent, unseen, uid_validity, block);
2441 if (ok != IMAP_SUCCESS)
2442 log_warning(_("can't select folder: %s\n"), real_path);
2444 session->mbox = g_strdup(path);
2445 session->folder_content_changed = FALSE;
2452 static gint imap_status(IMAPSession *session, IMAPFolder *folder,
2453 const gchar *path, IMAPFolderItem *item,
2455 guint32 *uid_next, guint32 *uid_validity,
2456 gint *unseen, gboolean block)
2460 struct mailimap_mailbox_data_status * data_status;
2465 real_path = imap_get_real_path(folder, path);
2479 r = imap_threaded_status(FOLDER(folder), real_path,
2480 &data_status, mask);
2483 if (r != MAILIMAP_NO_ERROR) {
2484 debug_print("status err %d\n", r);
2488 if (data_status->st_info_list == NULL) {
2489 mailimap_mailbox_data_status_free(data_status);
2490 debug_print("status->st_info_list == NULL\n");
2495 for(iter = clist_begin(data_status->st_info_list) ; iter != NULL ;
2496 iter = clist_next(iter)) {
2497 struct mailimap_status_info * info;
2499 info = clist_content(iter);
2500 switch (info->st_att) {
2501 case MAILIMAP_STATUS_ATT_MESSAGES:
2502 * messages = info->st_value;
2503 got_values |= 1 << 0;
2506 case MAILIMAP_STATUS_ATT_UIDNEXT:
2507 * uid_next = info->st_value;
2508 got_values |= 1 << 2;
2511 case MAILIMAP_STATUS_ATT_UIDVALIDITY:
2512 * uid_validity = info->st_value;
2513 got_values |= 1 << 3;
2516 case MAILIMAP_STATUS_ATT_UNSEEN:
2517 * unseen = info->st_value;
2518 got_values |= 1 << 4;
2522 mailimap_mailbox_data_status_free(data_status);
2524 if (got_values != mask) {
2525 debug_print("status: incomplete values received (%d)\n", got_values);
2528 return IMAP_SUCCESS;
2531 static void imap_free_capabilities(IMAPSession *session)
2533 slist_free_strings(session->capability);
2534 g_slist_free(session->capability);
2535 session->capability = NULL;
2538 /* low-level IMAP4rev1 commands */
2540 static gint imap_cmd_login(IMAPSession *session,
2541 const gchar *user, const gchar *pass,
2547 if (!strcmp(type, "LOGIN") && imap_has_capability(session, "LOGINDISABLED")) {
2548 gint ok = IMAP_ERROR;
2549 if (imap_has_capability(session, "STARTTLS")) {
2551 log_warning(_("Server requires TLS to log in.\n"));
2552 ok = imap_cmd_starttls(session);
2553 if (ok != IMAP_SUCCESS) {
2554 log_warning(_("Can't start TLS session.\n"));
2558 imap_free_capabilities(session);
2559 if (imap_get_capabilities(session) != MAILIMAP_NO_ERROR) {
2560 log_warning(_("Can't refresh capabilities.\n"));
2565 log_error(_("Connection to %s failed: "
2566 "server requires TLS, but Claws Mail "
2567 "has been compiled without OpenSSL "
2569 SESSION(session)->server);
2573 log_error(_("Server logins are disabled.\n"));
2578 log_print("IMAP4> Logging %s to %s using %s\n",
2580 SESSION(session)->server,
2582 r = imap_threaded_login(session->folder, user, pass, type);
2583 if (r != MAILIMAP_NO_ERROR) {
2584 log_print("IMAP4< Error logging in to %s\n",
2585 SESSION(session)->server);
2588 log_print("IMAP4< Login to %s successful\n",
2589 SESSION(session)->server);
2595 static gint imap_cmd_noop(IMAPSession *session)
2598 unsigned int exists;
2600 r = imap_threaded_noop(session->folder, &exists);
2601 if (r != MAILIMAP_NO_ERROR) {
2602 debug_print("noop err %d\n", r);
2605 session->exists = exists;
2606 session_set_access_time(SESSION(session));
2608 return IMAP_SUCCESS;
2612 static gint imap_cmd_starttls(IMAPSession *session)
2616 r = imap_threaded_starttls(session->folder,
2617 SESSION(session)->server, SESSION(session)->port);
2618 if (r != MAILIMAP_NO_ERROR) {
2619 debug_print("starttls err %d\n", r);
2622 return IMAP_SUCCESS;
2626 static gint imap_cmd_select(IMAPSession *session, const gchar *folder,
2627 gint *exists, gint *recent, gint *unseen,
2628 guint32 *uid_validity, gboolean block)
2632 r = imap_threaded_select(session->folder, folder,
2633 exists, recent, unseen, uid_validity);
2634 if (r != MAILIMAP_NO_ERROR) {
2635 debug_print("select err %d\n", r);
2638 return IMAP_SUCCESS;
2641 static gint imap_cmd_examine(IMAPSession *session, const gchar *folder,
2642 gint *exists, gint *recent, gint *unseen,
2643 guint32 *uid_validity, gboolean block)
2647 r = imap_threaded_examine(session->folder, folder,
2648 exists, recent, unseen, uid_validity);
2649 if (r != MAILIMAP_NO_ERROR) {
2650 debug_print("examine err %d\n", r);
2654 return IMAP_SUCCESS;
2657 static gint imap_cmd_create(IMAPSession *session, const gchar *folder)
2661 r = imap_threaded_create(session->folder, folder);
2662 if (r != MAILIMAP_NO_ERROR) {
2667 return IMAP_SUCCESS;
2670 static gint imap_cmd_rename(IMAPSession *session, const gchar *old_folder,
2671 const gchar *new_folder)
2675 r = imap_threaded_rename(session->folder, old_folder,
2677 if (r != MAILIMAP_NO_ERROR) {
2682 return IMAP_SUCCESS;
2685 static gint imap_cmd_delete(IMAPSession *session, const gchar *folder)
2690 r = imap_threaded_delete(session->folder, folder);
2691 if (r != MAILIMAP_NO_ERROR) {
2696 return IMAP_SUCCESS;
2699 typedef struct _fetch_data {
2700 IMAPSession *session;
2702 const gchar *filename;
2708 static void *imap_cmd_fetch_thread(void *data)
2710 fetch_data *stuff = (fetch_data *)data;
2711 IMAPSession *session = stuff->session;
2712 guint32 uid = stuff->uid;
2713 const gchar *filename = stuff->filename;
2717 r = imap_threaded_fetch_content(session->folder,
2721 r = imap_threaded_fetch_content(session->folder,
2724 if (r != MAILIMAP_NO_ERROR) {
2725 debug_print("fetch err %d\n", r);
2726 return GINT_TO_POINTER(IMAP_ERROR);
2728 return GINT_TO_POINTER(IMAP_SUCCESS);
2731 static gint imap_cmd_fetch(IMAPSession *session, guint32 uid,
2732 const gchar *filename, gboolean headers,
2735 fetch_data *data = g_new0(fetch_data, 1);
2738 data->session = session;
2740 data->filename = filename;
2741 data->headers = headers;
2744 if (prefs_common.work_offline &&
2745 !inc_offline_should_override(
2746 _("Claws Mail needs network access in order "
2747 "to access the IMAP server."))) {
2751 statusbar_print_all(_("Fetching message..."));
2752 result = GPOINTER_TO_INT(imap_cmd_fetch_thread(data));
2753 statusbar_pop_all();
2759 static gint imap_cmd_append(IMAPSession *session, const gchar *destfolder,
2760 const gchar *file, IMAPFlags flags,
2763 struct mailimap_flag_list * flag_list;
2766 g_return_val_if_fail(file != NULL, IMAP_ERROR);
2768 flag_list = imap_flag_to_lep(flags);
2769 r = imap_threaded_append(session->folder, destfolder,
2770 file, flag_list, (int *)new_uid);
2771 mailimap_flag_list_free(flag_list);
2773 if (r != MAILIMAP_NO_ERROR) {
2774 debug_print("append err %d\n", r);
2777 return IMAP_SUCCESS;
2780 static gint imap_cmd_copy(IMAPSession *session, struct mailimap_set * set,
2781 const gchar *destfolder, GRelation *uid_mapping,
2782 struct mailimap_set **source, struct mailimap_set **dest)
2786 g_return_val_if_fail(session != NULL, IMAP_ERROR);
2787 g_return_val_if_fail(set != NULL, IMAP_ERROR);
2788 g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
2790 r = imap_threaded_copy(session->folder, set, destfolder, source, dest);
2791 if (r != MAILIMAP_NO_ERROR) {
2796 return IMAP_SUCCESS;
2799 static gint imap_cmd_store(IMAPSession *session, struct mailimap_set * set,
2800 IMAPFlags flags, int do_add)
2803 struct mailimap_flag_list * flag_list;
2804 struct mailimap_store_att_flags * store_att_flags;
2806 flag_list = imap_flag_to_lep(flags);
2810 mailimap_store_att_flags_new_add_flags_silent(flag_list);
2813 mailimap_store_att_flags_new_remove_flags_silent(flag_list);
2815 r = imap_threaded_store(session->folder, set, store_att_flags);
2816 mailimap_store_att_flags_free(store_att_flags);
2817 if (r != MAILIMAP_NO_ERROR) {
2822 return IMAP_SUCCESS;
2825 static gint imap_cmd_expunge(IMAPSession *session)
2829 if (prefs_common.work_offline &&
2830 !inc_offline_should_override(
2831 _("Claws Mail needs network access in order "
2832 "to access the IMAP server."))) {
2836 r = imap_threaded_expunge(session->folder);
2837 if (r != MAILIMAP_NO_ERROR) {
2842 return IMAP_SUCCESS;
2845 static void imap_path_separator_subst(gchar *str, gchar separator)
2848 gboolean in_escape = FALSE;
2850 if (!separator || separator == '/') return;
2852 for (p = str; *p != '\0'; p++) {
2853 if (*p == '/' && !in_escape)
2855 else if (*p == '&' && *(p + 1) != '-' && !in_escape)
2857 else if (*p == '-' && in_escape)
2862 static gchar *imap_modified_utf7_to_utf8(const gchar *mutf7_str)
2864 static iconv_t cd = (iconv_t)-1;
2865 static gboolean iconv_ok = TRUE;
2868 size_t norm_utf7_len;
2870 gchar *to_str, *to_p;
2872 gboolean in_escape = FALSE;
2874 if (!iconv_ok) return g_strdup(mutf7_str);
2876 if (cd == (iconv_t)-1) {
2877 cd = iconv_open(CS_INTERNAL, CS_UTF_7);
2878 if (cd == (iconv_t)-1) {
2879 g_warning("iconv cannot convert UTF-7 to %s\n",
2882 return g_strdup(mutf7_str);
2886 /* modified UTF-7 to normal UTF-7 conversion */
2887 norm_utf7 = g_string_new(NULL);
2889 for (p = mutf7_str; *p != '\0'; p++) {
2890 /* replace: '&' -> '+',
2892 escaped ',' -> '/' */
2893 if (!in_escape && *p == '&') {
2894 if (*(p + 1) != '-') {
2895 g_string_append_c(norm_utf7, '+');
2898 g_string_append_c(norm_utf7, '&');
2901 } else if (in_escape && *p == ',') {
2902 g_string_append_c(norm_utf7, '/');
2903 } else if (in_escape && *p == '-') {
2904 g_string_append_c(norm_utf7, '-');
2907 g_string_append_c(norm_utf7, *p);
2911 norm_utf7_p = norm_utf7->str;
2912 norm_utf7_len = norm_utf7->len;
2913 to_len = strlen(mutf7_str) * 5;
2914 to_p = to_str = g_malloc(to_len + 1);
2916 if (iconv(cd, (ICONV_CONST gchar **)&norm_utf7_p, &norm_utf7_len,
2917 &to_p, &to_len) == -1) {
2918 g_warning(_("iconv cannot convert UTF-7 to %s\n"),
2919 conv_get_locale_charset_str());
2920 g_string_free(norm_utf7, TRUE);
2922 return g_strdup(mutf7_str);
2925 /* second iconv() call for flushing */
2926 iconv(cd, NULL, NULL, &to_p, &to_len);
2927 g_string_free(norm_utf7, TRUE);
2933 static gchar *imap_utf8_to_modified_utf7(const gchar *from)
2935 static iconv_t cd = (iconv_t)-1;
2936 static gboolean iconv_ok = TRUE;
2937 gchar *norm_utf7, *norm_utf7_p;
2938 size_t from_len, norm_utf7_len;
2940 gchar *from_tmp, *to, *p;
2941 gboolean in_escape = FALSE;
2943 if (!iconv_ok) return g_strdup(from);
2945 if (cd == (iconv_t)-1) {
2946 cd = iconv_open(CS_UTF_7, CS_INTERNAL);
2947 if (cd == (iconv_t)-1) {
2948 g_warning(_("iconv cannot convert %s to UTF-7\n"),
2951 return g_strdup(from);
2955 /* UTF-8 to normal UTF-7 conversion */
2956 Xstrdup_a(from_tmp, from, return g_strdup(from));
2957 from_len = strlen(from);
2958 norm_utf7_len = from_len * 5;
2959 Xalloca(norm_utf7, norm_utf7_len + 1, return g_strdup(from));
2960 norm_utf7_p = norm_utf7;
2962 #define IS_PRINT(ch) (isprint(ch) && IS_ASCII(ch))
2964 while (from_len > 0) {
2965 if (*from_tmp == '+') {
2966 *norm_utf7_p++ = '+';
2967 *norm_utf7_p++ = '-';
2971 } else if (IS_PRINT(*(guchar *)from_tmp)) {
2972 /* printable ascii char */
2973 *norm_utf7_p = *from_tmp;
2979 size_t conv_len = 0;
2981 /* unprintable char: convert to UTF-7 */
2983 while (!IS_PRINT(*(guchar *)p) && conv_len < from_len) {
2984 conv_len += g_utf8_skip[*(guchar *)p];
2985 p += g_utf8_skip[*(guchar *)p];
2988 from_len -= conv_len;
2989 if (iconv(cd, (ICONV_CONST gchar **)&from_tmp,
2991 &norm_utf7_p, &norm_utf7_len) == -1) {
2992 g_warning(_("iconv cannot convert UTF-8 to UTF-7\n"));
2993 return g_strdup(from);
2996 /* second iconv() call for flushing */
2997 iconv(cd, NULL, NULL, &norm_utf7_p, &norm_utf7_len);
3003 *norm_utf7_p = '\0';
3004 to_str = g_string_new(NULL);
3005 for (p = norm_utf7; p < norm_utf7_p; p++) {
3006 /* replace: '&' -> "&-",
3009 BASE64 '/' -> ',' */
3010 if (!in_escape && *p == '&') {
3011 g_string_append(to_str, "&-");
3012 } else if (!in_escape && *p == '+') {
3013 if (*(p + 1) == '-') {
3014 g_string_append_c(to_str, '+');
3017 g_string_append_c(to_str, '&');
3020 } else if (in_escape && *p == '/') {
3021 g_string_append_c(to_str, ',');
3022 } else if (in_escape && *p == '-') {
3023 g_string_append_c(to_str, '-');
3026 g_string_append_c(to_str, *p);
3032 g_string_append_c(to_str, '-');
3036 g_string_free(to_str, FALSE);
3041 static gboolean imap_rename_folder_func(GNode *node, gpointer data)
3043 FolderItem *item = node->data;
3044 gchar **paths = data;
3045 const gchar *oldpath = paths[0];
3046 const gchar *newpath = paths[1];
3048 gchar *new_itempath;
3051 oldpathlen = strlen(oldpath);
3052 if (strncmp(oldpath, item->path, oldpathlen) != 0) {
3053 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
3057 base = item->path + oldpathlen;
3058 while (*base == G_DIR_SEPARATOR) base++;
3060 new_itempath = g_strdup(newpath);
3062 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
3065 item->path = new_itempath;
3070 typedef struct _get_list_uid_data {
3072 IMAPSession *session;
3073 IMAPFolderItem *item;
3074 GSList **msgnum_list;
3076 } get_list_uid_data;
3078 static void *get_list_of_uids_thread(void *data)
3080 get_list_uid_data *stuff = (get_list_uid_data *)data;
3081 Folder *folder = stuff->folder;
3082 IMAPFolderItem *item = stuff->item;
3083 GSList **msgnum_list = stuff->msgnum_list;
3084 gint ok, nummsgs = 0, lastuid_old;
3085 IMAPSession *session;
3086 GSList *uidlist, *elem;
3087 clist * lep_uidlist;
3090 session = stuff->session;
3091 if (session == NULL) {
3093 return GINT_TO_POINTER(-1);
3095 /* no session locking here, it's already locked by caller */
3096 ok = imap_select(session, IMAP_FOLDER(folder), item->item.path,
3097 NULL, NULL, NULL, NULL, TRUE);
3098 if (ok != IMAP_SUCCESS) {
3100 return GINT_TO_POINTER(-1);
3105 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SIMPLE, NULL,
3108 if (r == MAILIMAP_NO_ERROR) {
3109 GSList * fetchuid_list;
3112 imap_uid_list_from_lep(lep_uidlist);
3113 mailimap_search_result_free(lep_uidlist);
3115 uidlist = g_slist_concat(fetchuid_list, uidlist);
3118 GSList * fetchuid_list;
3119 carray * lep_uidtab;
3121 r = imap_threaded_fetch_uid(folder, item->lastuid + 1,
3123 if (r == MAILIMAP_NO_ERROR) {
3125 imap_uid_list_from_lep_tab(lep_uidtab);
3126 imap_fetch_uid_list_free(lep_uidtab);
3127 uidlist = g_slist_concat(fetchuid_list, uidlist);
3131 lastuid_old = item->lastuid;
3132 *msgnum_list = g_slist_copy(item->uid_list);
3133 nummsgs = g_slist_length(*msgnum_list);
3134 debug_print("Got %d uids from cache\n", g_slist_length(item->uid_list));
3136 for (elem = uidlist; elem != NULL; elem = g_slist_next(elem)) {
3139 msgnum = GPOINTER_TO_INT(elem->data);
3140 if (msgnum > lastuid_old) {
3141 *msgnum_list = g_slist_prepend(*msgnum_list, GINT_TO_POINTER(msgnum));
3142 item->uid_list = g_slist_prepend(item->uid_list, GINT_TO_POINTER(msgnum));
3145 if(msgnum > item->lastuid)
3146 item->lastuid = msgnum;
3149 g_slist_free(uidlist);
3151 return GINT_TO_POINTER(nummsgs);
3154 static gint get_list_of_uids(IMAPSession *session, Folder *folder, IMAPFolderItem *item, GSList **msgnum_list)
3157 get_list_uid_data *data = g_new0(get_list_uid_data, 1);
3159 data->folder = folder;
3161 data->msgnum_list = msgnum_list;
3162 data->session = session;
3163 if (prefs_common.work_offline &&
3164 !inc_offline_should_override(
3165 _("Claws Mail needs network access in order "
3166 "to access the IMAP server."))) {
3171 result = GPOINTER_TO_INT(get_list_of_uids_thread(data));
3177 gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list, gboolean *old_uids_valid)
3179 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3180 IMAPSession *session;
3181 gint ok, nummsgs = 0, exists;
3182 guint32 uid_next = 0, uid_val = 0;
3183 GSList *uidlist = NULL;
3185 gboolean selected_folder;
3186 debug_print("get_num_list\n");
3188 g_return_val_if_fail(folder != NULL, -1);
3189 g_return_val_if_fail(item != NULL, -1);
3190 g_return_val_if_fail(item->item.path != NULL, -1);
3191 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3192 g_return_val_if_fail(folder->account != NULL, -1);
3194 session = imap_session_get(folder);
3195 g_return_val_if_fail(session != NULL, -1);
3198 if (FOLDER_ITEM(item)->path)
3199 statusbar_print_all(_("Scanning folder %s%c%s ..."),
3200 FOLDER_ITEM(item)->folder->name,
3202 FOLDER_ITEM(item)->path);
3204 statusbar_print_all(_("Scanning folder %s ..."),
3205 FOLDER_ITEM(item)->folder->name);
3207 selected_folder = (session->mbox != NULL) &&
3208 (!strcmp(session->mbox, item->item.path));
3209 if (selected_folder && time(NULL) - item->use_cache < 2) {
3210 ok = imap_cmd_noop(session);
3211 if (ok != IMAP_SUCCESS) {
3212 debug_print("disconnected!\n");
3213 session = imap_reconnect_if_possible(folder, session);
3214 if (session == NULL) {
3215 statusbar_pop_all();
3220 exists = session->exists;
3222 uid_next = item->c_uid_next;
3223 uid_val = item->c_uid_validity;
3224 *old_uids_valid = TRUE;
3226 if (item->use_cache && time(NULL) - item->use_cache < 2) {
3227 exists = item->c_messages;
3228 uid_next = item->c_uid_next;
3229 uid_val = item->c_uid_validity;
3231 debug_print("using cache %d %d %d\n", exists, uid_next, uid_val);
3233 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path, item,
3234 &exists, &uid_next, &uid_val, NULL, FALSE);
3236 item->item.last_num = uid_next - 1;
3238 item->use_cache = (time_t)0;
3239 if (ok != IMAP_SUCCESS) {
3240 statusbar_pop_all();
3244 if(item->item.mtime == uid_val)
3245 *old_uids_valid = TRUE;
3247 *old_uids_valid = FALSE;
3249 debug_print("Freeing imap uid cache (%d != %d)\n",
3250 (int)item->item.mtime, uid_val);
3252 g_slist_free(item->uid_list);
3253 item->uid_list = NULL;
3255 item->item.mtime = uid_val;
3257 imap_delete_all_cached_messages((FolderItem *)item);
3261 /* If old uid_next matches new uid_next we can be sure no message
3262 was added to the folder */
3263 debug_print("uid_next is %d and item->uid_next %d \n",
3264 uid_next, item->uid_next);
3265 if (uid_next == item->uid_next) {
3266 nummsgs = g_slist_length(item->uid_list);
3268 /* If number of messages is still the same we
3269 know our caches message numbers are still valid,
3270 otherwise if the number of messages has decrease
3271 we discard our cache to start a new scan to find
3272 out which numbers have been removed */
3273 if (exists == nummsgs) {
3274 debug_print("exists == nummsgs\n");
3275 *msgnum_list = g_slist_copy(item->uid_list);
3276 statusbar_pop_all();
3279 } else if (exists < nummsgs) {
3280 debug_print("Freeing imap uid cache");
3282 g_slist_free(item->uid_list);
3283 item->uid_list = NULL;
3288 *msgnum_list = NULL;
3289 statusbar_pop_all();
3294 nummsgs = get_list_of_uids(session, folder, item, &uidlist);
3297 statusbar_pop_all();
3302 if (nummsgs != exists) {
3303 /* Cache contains more messages then folder, we have cached
3304 an old UID of a message that was removed and new messages
3305 have been added too, otherwise the uid_next check would
3307 debug_print("Freeing imap uid cache");
3309 g_slist_free(item->uid_list);
3310 item->uid_list = NULL;
3312 g_slist_free(*msgnum_list);
3314 nummsgs = get_list_of_uids(session, folder, item, &uidlist);
3317 *msgnum_list = uidlist;
3319 dir = folder_item_get_path((FolderItem *)item);
3320 debug_print("removing old messages from %s\n", dir);
3321 remove_numbered_files_not_in_list(dir, *msgnum_list);
3324 item->uid_next = uid_next;
3326 debug_print("get_num_list - ok - %i\n", nummsgs);
3327 statusbar_pop_all();
3332 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item)
3337 flags.perm_flags = MSG_NEW|MSG_UNREAD;
3338 flags.tmp_flags = 0;
3340 g_return_val_if_fail(item != NULL, NULL);
3341 g_return_val_if_fail(file != NULL, NULL);
3343 if (folder_has_parent_of_type(item, F_QUEUE)) {
3344 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
3345 } else if (folder_has_parent_of_type(item, F_DRAFT)) {
3346 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
3349 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3350 if (!msginfo) return NULL;
3352 msginfo->plaintext_file = g_strdup(file);
3353 msginfo->folder = item;
3358 GSList *imap_get_msginfos(Folder *folder, FolderItem *item,
3359 GSList *msgnum_list)
3361 IMAPSession *session;
3362 MsgInfoList *ret = NULL;
3365 debug_print("get_msginfos\n");
3367 g_return_val_if_fail(folder != NULL, NULL);
3368 g_return_val_if_fail(item != NULL, NULL);
3369 g_return_val_if_fail(msgnum_list != NULL, NULL);
3371 session = imap_session_get(folder);
3372 g_return_val_if_fail(session != NULL, NULL);
3374 debug_print("IMAP getting msginfos\n");
3375 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3376 NULL, NULL, NULL, NULL, FALSE);
3377 if (ok != IMAP_SUCCESS) {
3381 if (!(folder_has_parent_of_type(item, F_DRAFT) ||
3382 folder_has_parent_of_type(item, F_QUEUE))) {
3383 ret = g_slist_concat(ret,
3384 imap_get_uncached_messages(session, item,
3387 MsgNumberList *sorted_list, *elem, *llast = NULL;
3388 gint startnum, lastnum;
3390 sorted_list = g_slist_sort(g_slist_copy(msgnum_list), g_int_compare);
3392 startnum = lastnum = GPOINTER_TO_INT(sorted_list->data);
3394 llast = g_slist_last(ret);
3395 for (elem = sorted_list;; elem = g_slist_next(elem)) {
3399 num = GPOINTER_TO_INT(elem->data);
3401 if (num > lastnum + 1 || elem == NULL) {
3403 for (i = startnum; i <= lastnum; ++i) {
3406 file = imap_fetch_msg(folder, item, i);
3408 MsgInfo *msginfo = imap_parse_msg(file, item);
3409 if (msginfo != NULL) {
3410 msginfo->msgnum = i;
3412 llast = ret = g_slist_append(ret, msginfo);
3414 llast = g_slist_append(llast, msginfo);
3415 llast = llast->next;
3420 session_set_access_time(SESSION(session));
3431 g_slist_free(sorted_list);
3437 MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid)
3439 MsgInfo *msginfo = NULL;
3440 MsgInfoList *msginfolist;
3441 MsgNumberList numlist;
3443 numlist.next = NULL;
3444 numlist.data = GINT_TO_POINTER(uid);
3446 msginfolist = imap_get_msginfos(folder, item, &numlist);
3447 if (msginfolist != NULL) {
3448 msginfo = msginfolist->data;
3449 g_slist_free(msginfolist);
3455 gboolean imap_scan_required(Folder *folder, FolderItem *_item)
3457 IMAPSession *session;
3458 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3459 gint ok, exists = 0, unseen = 0;
3460 guint32 uid_next = 0, uid_val = 0;
3461 gboolean selected_folder;
3463 g_return_val_if_fail(folder != NULL, FALSE);
3464 g_return_val_if_fail(item != NULL, FALSE);
3465 g_return_val_if_fail(item->item.folder != NULL, FALSE);
3466 g_return_val_if_fail(FOLDER_CLASS(item->item.folder) == &imap_class, FALSE);
3468 if (item->item.path == NULL)
3471 session = imap_session_get(folder);
3472 g_return_val_if_fail(session != NULL, FALSE);
3474 selected_folder = (session->mbox != NULL) &&
3475 (!strcmp(session->mbox, item->item.path));
3476 if (selected_folder && time(NULL) - item->use_cache < 2) {
3477 ok = imap_cmd_noop(session);
3478 if (ok != IMAP_SUCCESS) {
3479 debug_print("disconnected!\n");
3480 session = imap_reconnect_if_possible(folder, session);
3481 if (session == NULL)
3486 if (session->folder_content_changed
3487 || session->exists != item->item.total_msgs) {
3492 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path, IMAP_FOLDER_ITEM(item),
3493 &exists, &uid_next, &uid_val, &unseen, FALSE);
3494 if (ok != IMAP_SUCCESS) {
3499 item->use_cache = time(NULL);
3500 item->c_messages = exists;
3501 item->c_uid_next = uid_next;
3502 item->c_uid_validity = uid_val;
3503 item->c_unseen = unseen;
3504 item->item.last_num = uid_next - 1;
3505 debug_print("uidnext %d, item->uid_next %d, exists %d, item->item.total_msgs %d\n",
3506 uid_next, item->uid_next, exists, item->item.total_msgs);
3507 if ((uid_next != item->uid_next) || (exists != item->item.total_msgs)
3508 || unseen != item->item.unread_msgs || uid_val != item->item.mtime) {
3517 void imap_change_flags(Folder *folder, FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
3519 IMAPSession *session;
3520 IMAPFlags flags_set = 0, flags_unset = 0;
3521 gint ok = IMAP_SUCCESS;
3522 MsgNumberList numlist;
3523 hashtable_data *ht_data = NULL;
3525 g_return_if_fail(folder != NULL);
3526 g_return_if_fail(folder->klass == &imap_class);
3527 g_return_if_fail(item != NULL);
3528 g_return_if_fail(item->folder == folder);
3529 g_return_if_fail(msginfo != NULL);
3530 g_return_if_fail(msginfo->folder == item);
3532 session = imap_session_get(folder);
3537 if (!MSG_IS_MARKED(msginfo->flags) && (newflags & MSG_MARKED))
3538 flags_set |= IMAP_FLAG_FLAGGED;
3539 if ( MSG_IS_MARKED(msginfo->flags) && !(newflags & MSG_MARKED))
3540 flags_unset |= IMAP_FLAG_FLAGGED;
3542 if (!MSG_IS_UNREAD(msginfo->flags) && (newflags & MSG_UNREAD))
3543 flags_unset |= IMAP_FLAG_SEEN;
3544 if ( MSG_IS_UNREAD(msginfo->flags) && !(newflags & MSG_UNREAD))
3545 flags_set |= IMAP_FLAG_SEEN;
3547 if (!MSG_IS_REPLIED(msginfo->flags) && (newflags & MSG_REPLIED))
3548 flags_set |= IMAP_FLAG_ANSWERED;
3549 if ( MSG_IS_REPLIED(msginfo->flags) && !(newflags & MSG_REPLIED))
3550 flags_unset |= IMAP_FLAG_ANSWERED;
3552 if (!MSG_IS_DELETED(msginfo->flags) && (newflags & MSG_DELETED))
3553 flags_set |= IMAP_FLAG_DELETED;
3554 if ( MSG_IS_DELETED(msginfo->flags) && !(newflags & MSG_DELETED))
3555 flags_unset |= IMAP_FLAG_DELETED;
3557 if (!flags_set && !flags_unset) {
3558 /* the changed flags were not translatable to IMAP-speak.
3559 * like MSG_POSTFILTERED, so just apply. */
3560 msginfo->flags.perm_flags = newflags;
3565 if ((ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
3566 NULL, NULL, NULL, NULL, FALSE)) != IMAP_SUCCESS) {
3570 numlist.next = NULL;
3571 numlist.data = GINT_TO_POINTER(msginfo->msgnum);
3573 if (IMAP_FOLDER_ITEM(item)->batching) {
3574 /* instead of performing an UID STORE command for each message change,
3575 * as a lot of them can change "together", we just fill in hashtables
3576 * and defer the treatment so that we're able to send only one
3579 debug_print("IMAP batch mode on, deferring flags change\n");
3581 ht_data = g_hash_table_lookup(IMAP_FOLDER_ITEM(item)->flags_set_table,
3582 GINT_TO_POINTER(flags_set));
3583 if (ht_data == NULL) {
3584 ht_data = g_new0(hashtable_data, 1);
3585 ht_data->session = session;
3586 ht_data->item = IMAP_FOLDER_ITEM(item);
3587 g_hash_table_insert(IMAP_FOLDER_ITEM(item)->flags_set_table,
3588 GINT_TO_POINTER(flags_set), ht_data);
3590 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
3591 ht_data->msglist = g_slist_prepend(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum));
3594 ht_data = g_hash_table_lookup(IMAP_FOLDER_ITEM(item)->flags_unset_table,
3595 GINT_TO_POINTER(flags_unset));
3596 if (ht_data == NULL) {
3597 ht_data = g_new0(hashtable_data, 1);
3598 ht_data->session = session;
3599 ht_data->item = IMAP_FOLDER_ITEM(item);
3600 g_hash_table_insert(IMAP_FOLDER_ITEM(item)->flags_unset_table,
3601 GINT_TO_POINTER(flags_unset), ht_data);
3603 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
3604 ht_data->msglist = g_slist_prepend(ht_data->msglist,
3605 GINT_TO_POINTER(msginfo->msgnum));
3608 debug_print("IMAP changing flags\n");
3610 ok = imap_set_message_flags(session, &numlist, flags_set, TRUE);
3611 if (ok != IMAP_SUCCESS) {
3618 ok = imap_set_message_flags(session, &numlist, flags_unset, FALSE);
3619 if (ok != IMAP_SUCCESS) {
3625 msginfo->flags.perm_flags = newflags;
3630 static gint imap_remove_msg(Folder *folder, FolderItem *item, gint uid)
3633 IMAPSession *session;
3635 MsgNumberList numlist;
3637 g_return_val_if_fail(folder != NULL, -1);
3638 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3639 g_return_val_if_fail(item != NULL, -1);
3641 session = imap_session_get(folder);
3642 if (!session) return -1;
3644 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3645 NULL, NULL, NULL, NULL, FALSE);
3646 if (ok != IMAP_SUCCESS) {
3650 numlist.next = NULL;
3651 numlist.data = GINT_TO_POINTER(uid);
3653 ok = imap_set_message_flags
3654 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
3655 &numlist, IMAP_FLAG_DELETED, TRUE);
3656 if (ok != IMAP_SUCCESS) {
3657 log_warning(_("can't set deleted flags: %d\n"), uid);
3662 if (!session->uidplus) {
3663 ok = imap_cmd_expunge(session);
3667 uidstr = g_strdup_printf("%u", uid);
3668 ok = imap_cmd_expunge(session);
3671 if (ok != IMAP_SUCCESS) {
3672 log_warning(_("can't expunge\n"));
3677 IMAP_FOLDER_ITEM(item)->uid_list = g_slist_remove(
3678 IMAP_FOLDER_ITEM(item)->uid_list, numlist.data);
3679 dir = folder_item_get_path(item);
3680 if (is_dir_exist(dir))
3681 remove_numbered_files(dir, uid, uid);
3684 return IMAP_SUCCESS;
3687 static gint compare_msginfo(gconstpointer a, gconstpointer b)
3689 return ((MsgInfo *)a)->msgnum - ((MsgInfo *)b)->msgnum;
3692 static guint gslist_find_next_num(MsgNumberList **list, guint num)
3696 g_return_val_if_fail(list != NULL, -1);
3698 for (elem = *list; elem != NULL; elem = g_slist_next(elem))
3699 if (GPOINTER_TO_INT(elem->data) >= num)
3702 return elem != NULL ? GPOINTER_TO_INT(elem->data) : (gint)-1;
3706 * NEW and DELETED flags are not syncronized
3707 * - The NEW/RECENT flags in IMAP folders can not really be directly
3708 * modified by Sylpheed
3709 * - The DELETE/DELETED flag in IMAP and Sylpheed don't have the same
3710 * meaning, in IMAP it always removes the messages from the FolderItem
3711 * in Sylpheed it can mean to move the message to trash
3714 typedef struct _get_flags_data {
3717 MsgInfoList *msginfo_list;
3718 GRelation *msgflags;
3719 gboolean full_search;
3723 static /*gint*/ void *imap_get_flags_thread(void *data)
3725 get_flags_data *stuff = (get_flags_data *)data;
3726 Folder *folder = stuff->folder;
3727 FolderItem *item = stuff->item;
3728 MsgInfoList *msginfo_list = stuff->msginfo_list;
3729 GRelation *msgflags = stuff->msgflags;
3730 gboolean full_search = stuff->full_search;
3731 IMAPSession *session;
3732 GSList *sorted_list = NULL;
3733 GSList *unseen = NULL, *answered = NULL, *flagged = NULL, *deleted = NULL;
3734 GSList *p_unseen, *p_answered, *p_flagged, *p_deleted;
3736 GSList *seq_list, *cur;
3737 gboolean reverse_seen = FALSE;
3740 gint exists_cnt, unseen_cnt;
3741 gboolean selected_folder;
3743 if (folder == NULL || item == NULL) {
3745 return GINT_TO_POINTER(-1);
3748 session = imap_session_get(folder);
3749 if (session == NULL) {
3751 return GINT_TO_POINTER(-1);
3754 selected_folder = (session->mbox != NULL) &&
3755 (!strcmp(session->mbox, item->path));
3757 if (!selected_folder) {
3758 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3759 &exists_cnt, NULL, &unseen_cnt, NULL, TRUE);
3760 if (ok != IMAP_SUCCESS) {
3763 return GINT_TO_POINTER(-1);
3766 if (unseen_cnt > exists_cnt / 2)
3767 reverse_seen = TRUE;
3770 if (item->unread_msgs > item->total_msgs / 2)
3771 reverse_seen = TRUE;
3774 cmd_buf = g_string_new(NULL);
3776 sorted_list = g_slist_sort(g_slist_copy(msginfo_list), compare_msginfo);
3778 seq_list = imap_get_lep_set_from_msglist(msginfo_list);
3780 struct mailimap_set * set;
3781 set = mailimap_set_new_interval(1, 0);
3782 seq_list = g_slist_append(NULL, set);
3785 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
3786 struct mailimap_set * imapset;
3787 clist * lep_uidlist;
3790 imapset = cur->data;
3792 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SEEN,
3793 full_search ? NULL:imapset, &lep_uidlist);
3796 r = imap_threaded_search(folder,
3797 IMAP_SEARCH_TYPE_UNSEEN,
3798 full_search ? NULL:imapset, &lep_uidlist);
3800 if (r == MAILIMAP_NO_ERROR) {
3803 uidlist = imap_uid_list_from_lep(lep_uidlist);
3804 mailimap_search_result_free(lep_uidlist);
3806 unseen = g_slist_concat(unseen, uidlist);
3809 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_FLAGGED,
3810 full_search ? NULL:imapset, &lep_uidlist);
3811 if (r == MAILIMAP_NO_ERROR) {
3814 uidlist = imap_uid_list_from_lep(lep_uidlist);
3815 mailimap_search_result_free(lep_uidlist);
3817 flagged = g_slist_concat(flagged, uidlist);
3820 if (item->opened || item->processing_pending || item == folder->inbox) {
3821 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_ANSWERED,
3822 full_search ? NULL:imapset, &lep_uidlist);
3823 if (r == MAILIMAP_NO_ERROR) {
3826 uidlist = imap_uid_list_from_lep(lep_uidlist);
3827 mailimap_search_result_free(lep_uidlist);
3829 answered = g_slist_concat(answered, uidlist);
3832 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_DELETED,
3833 full_search ? NULL:imapset, &lep_uidlist);
3834 if (r == MAILIMAP_NO_ERROR) {
3837 uidlist = imap_uid_list_from_lep(lep_uidlist);
3838 mailimap_search_result_free(lep_uidlist);
3840 deleted = g_slist_concat(deleted, uidlist);
3846 p_answered = answered;
3847 p_flagged = flagged;
3848 p_deleted = deleted;
3850 for (elem = sorted_list; elem != NULL; elem = g_slist_next(elem)) {
3855 msginfo = (MsgInfo *) elem->data;
3856 flags = msginfo->flags.perm_flags;
3857 wasnew = (flags & MSG_NEW);
3858 if (item->opened || item->processing_pending || item == folder->inbox) {
3859 flags &= ~((reverse_seen ? 0 : MSG_UNREAD | MSG_NEW) | MSG_REPLIED | MSG_MARKED);
3861 flags &= ~((reverse_seen ? 0 : MSG_UNREAD | MSG_NEW | MSG_MARKED));
3864 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
3865 if (gslist_find_next_num(&p_unseen, msginfo->msgnum) == msginfo->msgnum) {
3866 if (!reverse_seen) {
3867 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
3869 flags &= ~(MSG_UNREAD | MSG_NEW);
3873 if (gslist_find_next_num(&p_flagged, msginfo->msgnum) == msginfo->msgnum)
3874 flags |= MSG_MARKED;
3876 flags &= ~MSG_MARKED;
3878 if (item->opened || item->processing_pending || item == folder->inbox) {
3879 if (gslist_find_next_num(&p_answered, msginfo->msgnum) == msginfo->msgnum)
3880 flags |= MSG_REPLIED;
3882 flags &= ~MSG_REPLIED;
3883 if (gslist_find_next_num(&p_deleted, msginfo->msgnum) == msginfo->msgnum)
3884 flags |= MSG_DELETED;
3886 flags &= ~MSG_DELETED;
3888 g_relation_insert(msgflags, msginfo, GINT_TO_POINTER(flags));
3891 imap_lep_set_free(seq_list);
3892 g_slist_free(flagged);
3893 g_slist_free(deleted);
3894 g_slist_free(answered);
3895 g_slist_free(unseen);
3896 g_slist_free(sorted_list);
3897 g_string_free(cmd_buf, TRUE);
3901 return GINT_TO_POINTER(0);
3904 static gint imap_get_flags(Folder *folder, FolderItem *item,
3905 MsgInfoList *msginfo_list, GRelation *msgflags)
3908 get_flags_data *data = g_new0(get_flags_data, 1);
3910 data->folder = folder;
3912 data->msginfo_list = msginfo_list;
3913 data->msgflags = msgflags;
3914 data->full_search = FALSE;
3916 GSList *tmp = NULL, *cur;
3918 if (prefs_common.work_offline &&
3919 !inc_offline_should_override(
3920 _("Claws Mail needs network access in order "
3921 "to access the IMAP server."))) {
3926 tmp = folder_item_get_msg_list(item);
3928 if (g_slist_length(tmp) <= g_slist_length(msginfo_list))
3929 data->full_search = TRUE;
3931 for (cur = tmp; cur; cur = cur->next)
3932 procmsg_msginfo_free((MsgInfo *)cur->data);
3936 result = GPOINTER_TO_INT(imap_get_flags_thread(data));
3943 static gboolean process_flags(gpointer key, gpointer value, gpointer user_data)
3945 gboolean flags_set = GPOINTER_TO_INT(user_data);
3946 gint flags_value = GPOINTER_TO_INT(key);
3947 hashtable_data *data = (hashtable_data *)value;
3948 IMAPFolderItem *_item = data->item;
3949 FolderItem *item = (FolderItem *)_item;
3950 gint ok = IMAP_ERROR;
3951 IMAPSession *session = imap_session_get(item->folder);
3953 data->msglist = g_slist_reverse(data->msglist);
3955 debug_print("IMAP %ssetting flags to %d for %d messages\n",
3958 g_slist_length(data->msglist));
3962 ok = imap_select(session, IMAP_FOLDER(item->folder), item->path,
3963 NULL, NULL, NULL, NULL, FALSE);
3965 if (ok == IMAP_SUCCESS) {
3966 imap_set_message_flags(data->session, data->msglist, flags_value, flags_set);
3968 g_warning("can't select mailbox %s\n", item->path);
3972 g_slist_free(data->msglist);
3977 static void process_hashtable(IMAPFolderItem *item)
3979 if (item->flags_set_table) {
3980 g_hash_table_foreach_remove(item->flags_set_table, process_flags, GINT_TO_POINTER(TRUE));
3981 g_hash_table_destroy(item->flags_set_table);
3982 item->flags_set_table = NULL;
3984 if (item->flags_unset_table) {
3985 g_hash_table_foreach_remove(item->flags_unset_table, process_flags, GINT_TO_POINTER(FALSE));
3986 g_hash_table_destroy(item->flags_unset_table);
3987 item->flags_unset_table = NULL;
3991 static void imap_set_batch (Folder *folder, FolderItem *_item, gboolean batch)
3993 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3995 g_return_if_fail(item != NULL);
3997 if (item->batching == batch)
4001 item->batching = TRUE;
4002 debug_print("IMAP switching to batch mode\n");
4003 if (!item->flags_set_table) {
4004 item->flags_set_table = g_hash_table_new(NULL, g_direct_equal);
4006 if (!item->flags_unset_table) {
4007 item->flags_unset_table = g_hash_table_new(NULL, g_direct_equal);
4010 debug_print("IMAP switching away from batch mode\n");
4012 process_hashtable(item);
4013 item->batching = FALSE;
4019 /* data types conversion libetpan <-> sylpheed */
4023 #define ETPAN_IMAP_MB_MARKED 1
4024 #define ETPAN_IMAP_MB_UNMARKED 2
4025 #define ETPAN_IMAP_MB_NOSELECT 4
4026 #define ETPAN_IMAP_MB_NOINFERIORS 8
4028 static int imap_flags_to_flags(struct mailimap_mbx_list_flags * imap_flags)
4034 if (imap_flags->mbf_type == MAILIMAP_MBX_LIST_FLAGS_SFLAG) {
4035 switch (imap_flags->mbf_sflag) {
4036 case MAILIMAP_MBX_LIST_SFLAG_MARKED:
4037 flags |= ETPAN_IMAP_MB_MARKED;
4039 case MAILIMAP_MBX_LIST_SFLAG_NOSELECT:
4040 flags |= ETPAN_IMAP_MB_NOSELECT;
4042 case MAILIMAP_MBX_LIST_SFLAG_UNMARKED:
4043 flags |= ETPAN_IMAP_MB_UNMARKED;
4048 for(cur = clist_begin(imap_flags->mbf_oflags) ; cur != NULL ;
4049 cur = clist_next(cur)) {
4050 struct mailimap_mbx_list_oflag * oflag;
4052 oflag = clist_content(cur);
4054 switch (oflag->of_type) {
4055 case MAILIMAP_MBX_LIST_OFLAG_NOINFERIORS:
4056 flags |= ETPAN_IMAP_MB_NOINFERIORS;
4064 static GSList * imap_list_from_lep(IMAPFolder * folder,
4065 clist * list, const gchar * real_path, gboolean all)
4068 GSList * item_list = NULL, *llast = NULL;
4070 for(iter = clist_begin(list) ; iter != NULL ;
4071 iter = clist_next(iter)) {
4072 struct mailimap_mailbox_list * mb;
4080 FolderItem *new_item;
4082 mb = clist_content(iter);
4088 if (mb->mb_flag != NULL)
4089 flags = imap_flags_to_flags(mb->mb_flag);
4091 delimiter = mb->mb_delimiter;
4094 dup_name = strdup(name);
4095 if (delimiter != '\0')
4096 subst_char(dup_name, delimiter, '/');
4098 base = g_path_get_basename(dup_name);
4099 if (base[0] == '.') {
4105 if (!all && strcmp(dup_name, real_path) == 0) {
4111 if (!all && dup_name[strlen(dup_name)-1] == '/') {
4117 loc_name = imap_modified_utf7_to_utf8(base);
4118 loc_path = imap_modified_utf7_to_utf8(dup_name);
4120 new_item = folder_item_new(FOLDER(folder), loc_name, loc_path);
4121 if ((flags & ETPAN_IMAP_MB_NOINFERIORS) != 0)
4122 new_item->no_sub = TRUE;
4123 if (strcmp(dup_name, "INBOX") != 0 &&
4124 ((flags & ETPAN_IMAP_MB_NOSELECT) != 0))
4125 new_item->no_select = TRUE;
4127 if (item_list == NULL)
4128 llast = item_list = g_slist_append(item_list, new_item);
4130 llast = g_slist_append(llast, new_item);
4131 llast = llast->next;
4133 debug_print("folder '%s' found.\n", loc_path);
4144 static GSList * imap_get_lep_set_from_numlist(MsgNumberList *numlist)
4146 GSList *sorted_list, *cur;
4147 guint first, last, next;
4148 GSList *ret_list = NULL, *llast = NULL;
4150 struct mailimap_set * current_set;
4151 unsigned int item_count;
4153 if (numlist == NULL)
4157 current_set = mailimap_set_new_empty();
4159 sorted_list = g_slist_copy(numlist);
4160 sorted_list = g_slist_sort(sorted_list, g_int_compare);
4162 first = GPOINTER_TO_INT(sorted_list->data);
4165 for (cur = sorted_list; cur != NULL; cur = g_slist_next(cur)) {
4166 if (GPOINTER_TO_INT(cur->data) == 0)
4171 last = GPOINTER_TO_INT(cur->data);
4173 next = GPOINTER_TO_INT(cur->next->data);
4177 if (last + 1 != next || next == 0) {
4179 struct mailimap_set_item * item;
4180 item = mailimap_set_item_new(first, last);
4181 mailimap_set_add(current_set, item);
4186 if (count >= IMAP_SET_MAX_COUNT) {
4187 if (ret_list == NULL)
4188 llast = ret_list = g_slist_append(ret_list,
4191 llast = g_slist_append(llast, current_set);
4192 llast = llast->next;
4194 current_set = mailimap_set_new_empty();
4201 if (clist_count(current_set->set_list) > 0) {
4202 ret_list = g_slist_append(ret_list,
4206 g_slist_free(sorted_list);
4211 static GSList * imap_get_lep_set_from_msglist(MsgInfoList *msglist)
4213 MsgNumberList *numlist = NULL;
4217 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
4218 MsgInfo *msginfo = (MsgInfo *) cur->data;
4220 numlist = g_slist_prepend(numlist, GINT_TO_POINTER(msginfo->msgnum));
4222 numlist = g_slist_reverse(numlist);
4223 seq_list = imap_get_lep_set_from_numlist(numlist);
4224 g_slist_free(numlist);
4229 static GSList * imap_uid_list_from_lep(clist * list)
4236 for(iter = clist_begin(list) ; iter != NULL ;
4237 iter = clist_next(iter)) {
4240 puid = clist_content(iter);
4241 result = g_slist_prepend(result, GINT_TO_POINTER(* puid));
4244 result = g_slist_reverse(result);
4248 static GSList * imap_uid_list_from_lep_tab(carray * list)
4255 for(i = 0 ; i < carray_count(list) ; i ++) {
4258 puid = carray_get(list, i);
4259 result = g_slist_prepend(result, GINT_TO_POINTER(* puid));
4261 result = g_slist_reverse(result);
4265 static MsgInfo *imap_envelope_from_lep(struct imap_fetch_env_info * info,
4268 MsgInfo *msginfo = NULL;
4271 MsgFlags flags = {0, 0};
4273 if (info->headers == NULL)
4276 MSG_SET_TMP_FLAGS(flags, MSG_IMAP);
4277 if (folder_has_parent_of_type(item, F_QUEUE)) {
4278 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
4279 } else if (folder_has_parent_of_type(item, F_DRAFT)) {
4280 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
4282 flags.perm_flags = info->flags;
4286 msginfo = procheader_parse_str(info->headers, flags, FALSE, FALSE);
4289 msginfo->msgnum = uid;
4290 msginfo->size = size;
4296 static void imap_lep_set_free(GSList *seq_list)
4300 for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) {
4301 struct mailimap_set * imapset;
4303 imapset = cur->data;
4304 mailimap_set_free(imapset);
4306 g_slist_free(seq_list);
4309 static struct mailimap_flag_list * imap_flag_to_lep(IMAPFlags flags)
4311 struct mailimap_flag_list * flag_list;
4313 flag_list = mailimap_flag_list_new_empty();
4315 if (IMAP_IS_SEEN(flags))
4316 mailimap_flag_list_add(flag_list,
4317 mailimap_flag_new_seen());
4318 if (IMAP_IS_ANSWERED(flags))
4319 mailimap_flag_list_add(flag_list,
4320 mailimap_flag_new_answered());
4321 if (IMAP_IS_FLAGGED(flags))
4322 mailimap_flag_list_add(flag_list,
4323 mailimap_flag_new_flagged());
4324 if (IMAP_IS_DELETED(flags))
4325 mailimap_flag_list_add(flag_list,
4326 mailimap_flag_new_deleted());
4327 if (IMAP_IS_DRAFT(flags))
4328 mailimap_flag_list_add(flag_list,
4329 mailimap_flag_new_draft());
4334 guint imap_folder_get_refcnt(Folder *folder)
4336 return ((IMAPFolder *)folder)->refcnt;
4339 void imap_folder_ref(Folder *folder)
4341 ((IMAPFolder *)folder)->refcnt++;
4344 void imap_disconnect_all(void)
4347 for (list = account_get_list(); list != NULL; list = list->next) {
4348 PrefsAccount *account = list->data;
4349 if (account->protocol == A_IMAP4) {
4350 RemoteFolder *folder = (RemoteFolder *)account->folder;
4351 if (folder && folder->session) {
4352 IMAPSession *session = (IMAPSession *)folder->session;
4353 imap_threaded_disconnect(FOLDER(folder));
4354 SESSION(session)->state = SESSION_DISCONNECTED;
4355 session_destroy(SESSION(session));
4356 folder->session = NULL;
4362 void imap_folder_unref(Folder *folder)
4364 if (((IMAPFolder *)folder)->refcnt > 0)
4365 ((IMAPFolder *)folder)->refcnt--;
4368 #else /* HAVE_LIBETPAN */
4370 static FolderClass imap_class;
4372 static XMLTag *imap_item_get_xml(Folder *folder, FolderItem *item);
4373 static void imap_item_set_xml(Folder *folder, FolderItem *item, XMLTag *tag);
4375 static Folder *imap_folder_new (const gchar *name,
4378 static gboolean missing_imap_warning = TRUE;
4379 if (missing_imap_warning) {
4380 missing_imap_warning = FALSE;
4382 _("You have one or more IMAP accounts "
4383 "defined. However this version of "
4384 "Claws Mail has been built without "
4385 "IMAP support; your IMAP account(s) are "
4387 "You probably need to "
4388 "install libetpan and recompile "
4393 static gint imap_create_tree (Folder *folder)
4397 static FolderItem *imap_create_folder (Folder *folder,
4403 static gint imap_rename_folder (Folder *folder,
4410 gchar imap_get_path_separator_for_item(FolderItem *item)
4415 FolderClass *imap_get_class(void)
4417 if (imap_class.idstr == NULL) {
4418 imap_class.type = F_IMAP;
4419 imap_class.idstr = "imap";
4420 imap_class.uistr = "IMAP4";
4422 imap_class.new_folder = imap_folder_new;
4423 imap_class.create_tree = imap_create_tree;
4424 imap_class.create_folder = imap_create_folder;
4425 imap_class.rename_folder = imap_rename_folder;
4427 imap_class.set_xml = folder_set_xml;
4428 imap_class.get_xml = folder_get_xml;
4429 imap_class.item_set_xml = imap_item_set_xml;
4430 imap_class.item_get_xml = imap_item_get_xml;
4431 /* nothing implemented */
4437 void imap_disconnect_all(void)
4443 void imap_synchronise(FolderItem *item)
4445 imap_gtk_synchronise(item);
4448 static void imap_item_set_xml(Folder *folder, FolderItem *item, XMLTag *tag)
4450 #ifdef HAVE_LIBETPAN
4453 folder_item_set_xml(folder, item, tag);
4455 #ifdef HAVE_LIBETPAN
4456 for (cur = tag->attr; cur != NULL; cur = g_list_next(cur)) {
4457 XMLAttr *attr = (XMLAttr *) cur->data;
4459 if (!attr || !attr->name || !attr->value) continue;
4460 if (!strcmp(attr->name, "uidnext"))
4461 IMAP_FOLDER_ITEM(item)->uid_next = atoi(attr->value);
4466 static XMLTag *imap_item_get_xml(Folder *folder, FolderItem *item)
4470 tag = folder_item_get_xml(folder, item);
4472 #ifdef HAVE_LIBETPAN
4473 xml_tag_add_attr(tag, xml_attr_new_int("uidnext",
4474 IMAP_FOLDER_ITEM(item)->uid_next));