2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2006 Hiroyuki Yamamoto and the Sylpheed-Claws 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 static gint imap_cmd_store (IMAPSession *session,
351 struct mailimap_set * set,
354 static gint imap_cmd_expunge (IMAPSession *session);
356 static void imap_path_separator_subst (gchar *str,
359 static gchar *imap_utf8_to_modified_utf7 (const gchar *from);
360 static gchar *imap_modified_utf7_to_utf8 (const gchar *mutf7_str);
362 static gboolean imap_rename_folder_func (GNode *node,
364 static gint imap_get_num_list (Folder *folder,
367 gboolean *old_uids_valid);
368 static GSList *imap_get_msginfos (Folder *folder,
370 GSList *msgnum_list);
371 static MsgInfo *imap_get_msginfo (Folder *folder,
374 static gboolean imap_scan_required (Folder *folder,
376 static void imap_change_flags (Folder *folder,
379 MsgPermFlags newflags);
380 static gint imap_get_flags (Folder *folder,
382 MsgInfoList *msglist,
383 GRelation *msgflags);
384 static gchar *imap_folder_get_path (Folder *folder);
385 static gchar *imap_item_get_path (Folder *folder,
387 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item);
390 /* data types conversion libetpan <-> sylpheed */
391 static GSList * imap_list_from_lep(IMAPFolder * folder,
392 clist * list, const gchar * real_path, gboolean all);
393 static GSList * imap_get_lep_set_from_numlist(MsgNumberList *numlist);
394 static GSList * imap_get_lep_set_from_msglist(MsgInfoList *msglist);
395 static GSList * imap_uid_list_from_lep(clist * list);
396 static GSList * imap_uid_list_from_lep_tab(carray * list);
397 static MsgInfo *imap_envelope_from_lep(struct imap_fetch_env_info * info,
399 static void imap_lep_set_free(GSList *seq_list);
400 static struct mailimap_flag_list * imap_flag_to_lep(IMAPFlags flags);
402 typedef struct _hashtable_data {
403 IMAPSession *session;
405 IMAPFolderItem *item;
408 static FolderClass imap_class;
410 typedef struct _thread_data {
420 FolderClass *imap_get_class(void)
422 if (imap_class.idstr == NULL) {
423 imap_class.type = F_IMAP;
424 imap_class.idstr = "imap";
425 imap_class.uistr = "IMAP4";
427 /* Folder functions */
428 imap_class.new_folder = imap_folder_new;
429 imap_class.destroy_folder = imap_folder_destroy;
430 imap_class.scan_tree = imap_scan_tree;
431 imap_class.create_tree = imap_create_tree;
433 /* FolderItem functions */
434 imap_class.item_new = imap_folder_item_new;
435 imap_class.item_destroy = imap_folder_item_destroy;
436 imap_class.item_get_path = imap_item_get_path;
437 imap_class.create_folder = imap_create_folder;
438 imap_class.rename_folder = imap_rename_folder;
439 imap_class.remove_folder = imap_remove_folder;
440 imap_class.close = imap_close;
441 imap_class.get_num_list = imap_get_num_list;
442 imap_class.scan_required = imap_scan_required;
443 imap_class.set_xml = folder_set_xml;
444 imap_class.get_xml = folder_get_xml;
445 imap_class.item_set_xml = imap_item_set_xml;
446 imap_class.item_get_xml = imap_item_get_xml;
448 /* Message functions */
449 imap_class.get_msginfo = imap_get_msginfo;
450 imap_class.get_msginfos = imap_get_msginfos;
451 imap_class.fetch_msg = imap_fetch_msg;
452 imap_class.fetch_msg_full = imap_fetch_msg_full;
453 imap_class.add_msg = imap_add_msg;
454 imap_class.add_msgs = imap_add_msgs;
455 imap_class.copy_msg = imap_copy_msg;
456 imap_class.copy_msgs = imap_copy_msgs;
457 imap_class.remove_msg = imap_remove_msg;
458 imap_class.remove_msgs = imap_remove_msgs;
459 imap_class.remove_all_msg = imap_remove_all_msg;
460 imap_class.is_msg_changed = imap_is_msg_changed;
461 imap_class.change_flags = imap_change_flags;
462 imap_class.get_flags = imap_get_flags;
463 imap_class.set_batch = imap_set_batch;
464 imap_class.synchronise = imap_synchronise;
466 pthread_mutex_init(&imap_mutex, NULL);
473 static Folder *imap_folder_new(const gchar *name, const gchar *path)
477 folder = (Folder *)g_new0(IMAPFolder, 1);
478 folder->klass = &imap_class;
479 imap_folder_init(folder, name, path);
484 static void imap_folder_destroy(Folder *folder)
486 while (imap_folder_get_refcnt(folder) > 0)
487 gtk_main_iteration();
489 folder_remote_folder_destroy(REMOTE_FOLDER(folder));
493 static void imap_folder_init(Folder *folder, const gchar *name,
496 folder_remote_folder_init((Folder *)folder, name, path);
499 static FolderItem *imap_folder_item_new(Folder *folder)
501 IMAPFolderItem *item;
503 item = g_new0(IMAPFolderItem, 1);
506 item->uid_list = NULL;
508 return (FolderItem *)item;
511 static void imap_folder_item_destroy(Folder *folder, FolderItem *_item)
513 IMAPFolderItem *item = (IMAPFolderItem *)_item;
515 g_return_if_fail(item != NULL);
516 g_slist_free(item->uid_list);
521 static gboolean imap_reset_uid_lists_func(GNode *node, gpointer data)
523 IMAPFolderItem *item = (IMAPFolderItem *)node->data;
526 g_slist_free(item->uid_list);
527 item->uid_list = NULL;
532 static void imap_reset_uid_lists(Folder *folder)
534 if(folder->node == NULL)
537 /* Destroy all uid lists and rest last uid */
538 g_node_traverse(folder->node, G_IN_ORDER, G_TRAVERSE_ALL, -1, imap_reset_uid_lists_func, NULL);
541 int imap_get_capabilities(IMAPSession *session)
543 struct mailimap_capability_data *capabilities = NULL;
547 if (session->capability != NULL)
548 return MAILIMAP_NO_ERROR;
550 capabilities = imap_threaded_capability(session->folder, &result);
552 if (result != MAILIMAP_NO_ERROR) {
553 return MAILIMAP_ERROR_CAPABILITY;
556 if (capabilities == NULL) {
557 return MAILIMAP_NO_ERROR;
560 for(cur = clist_begin(capabilities->cap_list) ; cur != NULL ;
561 cur = clist_next(cur)) {
562 struct mailimap_capability * cap =
564 if (!cap || cap->cap_data.cap_name == NULL)
566 session->capability = g_slist_append
567 (session->capability,
568 g_strdup(cap->cap_data.cap_name));
569 debug_print("got capa %s\n", cap->cap_data.cap_name);
571 mailimap_capability_data_free(capabilities);
572 return MAILIMAP_NO_ERROR;
575 gboolean imap_has_capability(IMAPSession *session, const gchar *cap)
578 for (cur = session->capability; cur; cur = cur->next) {
579 if (!g_ascii_strcasecmp(cur->data, cap))
585 static gint imap_auth(IMAPSession *session, const gchar *user, const gchar *pass,
588 gint ok = IMAP_ERROR;
589 static time_t last_login_err = 0;
590 gchar *ext_info = "";
592 if (imap_get_capabilities(session) != MAILIMAP_NO_ERROR)
597 ok = imap_cmd_login(session, user, pass, "ANONYMOUS");
599 case IMAP_AUTH_CRAM_MD5:
600 ok = imap_cmd_login(session, user, pass, "CRAM-MD5");
602 case IMAP_AUTH_LOGIN:
603 ok = imap_cmd_login(session, user, pass, "LOGIN");
606 debug_print("capabilities:\n"
610 imap_has_capability(session, "ANONYMOUS"),
611 imap_has_capability(session, "CRAM-MD5"),
612 imap_has_capability(session, "LOGIN"));
613 if (imap_has_capability(session, "CRAM-MD5"))
614 ok = imap_cmd_login(session, user, pass, "CRAM-MD5");
615 if (ok == IMAP_ERROR) /* we always try LOGIN before giving up */
616 ok = imap_cmd_login(session, user, pass, "LOGIN");
619 if (ok == IMAP_SUCCESS)
620 session->authenticated = TRUE;
622 if (type == IMAP_AUTH_CRAM_MD5) {
623 ext_info = _("\n\nCRAM-MD5 logins only work if libetpan has been "
624 "compiled with SASL support and the "
625 "CRAM-MD5 SASL plugin is installed.");
628 if (time(NULL) - last_login_err > 10) {
629 if (!prefs_common.no_recv_err_panel) {
630 alertpanel_error(_("Connection to %s failed: "
632 SESSION(session)->server, ext_info);
634 log_error(_("Connection to %s failed: "
635 "login refused.%s\n"),
636 SESSION(session)->server, ext_info);
639 last_login_err = time(NULL);
644 static IMAPSession *imap_reconnect_if_possible(Folder *folder, IMAPSession *session)
646 RemoteFolder *rfolder = REMOTE_FOLDER(folder);
647 /* Check if this is the first try to establish a
648 connection, if yes we don't try to reconnect */
649 debug_print("reconnecting\n");
650 if (rfolder->session == NULL) {
651 log_warning(_("Connecting to %s failed"),
652 folder->account->recv_server);
653 session_destroy(SESSION(session));
656 log_warning(_("IMAP4 connection to %s has been"
657 " disconnected. Reconnecting...\n"),
658 folder->account->recv_server);
659 statusbar_print_all(_("IMAP4 connection to %s has been"
660 " disconnected. Reconnecting...\n"),
661 folder->account->recv_server);
662 SESSION(session)->state = SESSION_DISCONNECTED;
663 session_destroy(SESSION(session));
664 /* Clear folders session to make imap_session_get create
665 a new session, because of rfolder->session == NULL
666 it will not try to reconnect again and so avoid an
668 rfolder->session = NULL;
669 session = imap_session_get(folder);
670 rfolder->session = SESSION(session);
676 #define lock_session() {\
677 debug_print("locking session\n"); \
678 session->busy = TRUE;\
681 #define unlock_session() {\
682 debug_print("unlocking session\n"); \
683 session->busy = FALSE;\
686 static IMAPSession *imap_session_get(Folder *folder)
688 RemoteFolder *rfolder = REMOTE_FOLDER(folder);
689 IMAPSession *session = NULL;
691 g_return_val_if_fail(folder != NULL, NULL);
692 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, NULL);
693 g_return_val_if_fail(folder->account != NULL, NULL);
695 if (prefs_common.work_offline &&
696 !inc_offline_should_override(
697 _("Sylpheed-Claws needs network access in order "
698 "to access the IMAP server."))) {
702 /* Make sure we have a session */
703 if (rfolder->session != NULL) {
704 session = IMAP_SESSION(rfolder->session);
705 /* don't do that yet...
710 imap_reset_uid_lists(folder);
711 if (time(NULL) - rfolder->last_failure <= 2)
713 session = imap_session_new(folder, folder->account);
715 if(session == NULL) {
716 rfolder->last_failure = time(NULL);
720 /* Make sure session is authenticated */
721 if (!IMAP_SESSION(session)->authenticated)
722 imap_session_authenticate(IMAP_SESSION(session), folder->account);
724 if (!IMAP_SESSION(session)->authenticated) {
725 imap_threaded_disconnect(session->folder);
726 SESSION(session)->state = SESSION_DISCONNECTED;
727 session_destroy(SESSION(session));
728 rfolder->session = NULL;
729 rfolder->last_failure = time(NULL);
733 /* I think the point of this code is to avoid sending a
734 * keepalive if we've used the session recently and therefore
735 * think it's still alive. Unfortunately, most of the code
736 * does not yet check for errors on the socket, and so if the
737 * connection drops we don't notice until the timeout expires.
738 * A better solution than sending a NOOP every time would be
739 * for every command to be prepared to retry until it is
740 * successfully sent. -- mbp */
741 if (time(NULL) - SESSION(session)->last_access_time > SESSION_TIMEOUT_INTERVAL) {
742 /* verify that the session is still alive */
743 if (imap_cmd_noop(session) != IMAP_SUCCESS) {
744 debug_print("disconnected!\n");
745 session = imap_reconnect_if_possible(folder, session);
749 rfolder->session = SESSION(session);
751 return IMAP_SESSION(session);
754 static IMAPSession *imap_session_new(Folder * folder,
755 const PrefsAccount *account)
757 IMAPSession *session;
760 int authenticated = FALSE;
763 /* FIXME: IMAP over SSL only... */
766 port = account->set_imapport ? account->imapport
767 : account->ssl_imap == SSL_TUNNEL ? IMAPS_PORT : IMAP4_PORT;
768 ssl_type = account->ssl_imap;
770 if (account->ssl_imap != SSL_NONE) {
771 if (alertpanel_full(_("Insecure connection"),
772 _("This connection is configured to be secured "
773 "using SSL, but SSL is not available in this "
774 "build of Sylpheed-Claws. \n\n"
775 "Do you want to continue connecting to this "
776 "server? The communication would not be "
778 GTK_STOCK_CANCEL, _("Con_tinue connecting"),
779 NULL, FALSE, NULL, ALERT_WARNING,
780 G_ALERTDEFAULT) != G_ALERTALTERNATE)
783 port = account->set_imapport ? account->imapport
788 statusbar_print_all(_("Connecting to IMAP4 server: %s..."), folder->account->recv_server);
789 if (account->set_tunnelcmd) {
790 r = imap_threaded_connect_cmd(folder,
792 account->recv_server,
797 if (ssl_type == SSL_TUNNEL) {
798 r = imap_threaded_connect_ssl(folder,
799 account->recv_server,
805 r = imap_threaded_connect(folder,
806 account->recv_server,
812 if (r == MAILIMAP_NO_ERROR_AUTHENTICATED) {
813 authenticated = TRUE;
815 else if (r == MAILIMAP_NO_ERROR_NON_AUTHENTICATED) {
816 authenticated = FALSE;
819 if(!prefs_common.no_recv_err_panel) {
820 alertpanel_error(_("Can't connect to IMAP4 server: %s:%d"),
821 account->recv_server, port);
823 log_error(_("Can't connect to IMAP4 server: %s:%d\n"),
824 account->recv_server, port);
830 session = g_new0(IMAPSession, 1);
831 session_init(SESSION(session));
832 SESSION(session)->type = SESSION_IMAP;
833 SESSION(session)->server = g_strdup(account->recv_server);
834 SESSION(session)->sock = NULL;
836 SESSION(session)->destroy = imap_session_destroy;
838 session->capability = NULL;
840 session->authenticated = authenticated;
841 session->mbox = NULL;
842 session->cmd_count = 0;
843 session->folder = folder;
844 IMAP_FOLDER(session->folder)->last_seen_separator = 0;
847 if (account->ssl_imap == SSL_STARTTLS) {
850 ok = imap_cmd_starttls(session);
851 if (ok != IMAP_SUCCESS) {
852 log_warning(_("Can't start TLS session.\n"));
853 session_destroy(SESSION(session));
857 imap_free_capabilities(session);
858 session->authenticated = FALSE;
859 session->uidplus = FALSE;
860 session->cmd_count = 1;
863 log_message("IMAP connection is %s-authenticated\n",
864 (session->authenticated) ? "pre" : "un");
869 static void imap_session_authenticate(IMAPSession *session,
870 const PrefsAccount *account)
872 gchar *pass, *acc_pass;
873 gboolean failed = FALSE;
875 g_return_if_fail(account->userid != NULL);
876 acc_pass = account->passwd;
879 if (!pass && account->imap_auth_type != IMAP_AUTH_ANON) {
881 tmp_pass = input_dialog_query_password(account->recv_server, account->userid);
884 Xstrdup_a(pass, tmp_pass, {g_free(tmp_pass); return;});
886 } else if (account->imap_auth_type == IMAP_AUTH_ANON) {
889 statusbar_print_all(_("Connecting to IMAP4 server %s...\n"),
890 account->recv_server);
891 if (imap_auth(session, account->userid, pass, account->imap_auth_type) != IMAP_SUCCESS) {
899 alertpanel_error(_("Couldn't login to IMAP server %s."), account->recv_server);
906 session->authenticated = TRUE;
910 static void imap_session_destroy(Session *session)
912 if (session->state != SESSION_DISCONNECTED)
913 imap_threaded_disconnect(IMAP_SESSION(session)->folder);
915 imap_free_capabilities(IMAP_SESSION(session));
916 g_free(IMAP_SESSION(session)->mbox);
917 sock_close(session->sock);
918 session->sock = NULL;
921 static gchar *imap_fetch_msg(Folder *folder, FolderItem *item, gint uid)
923 return imap_fetch_msg_full(folder, item, uid, TRUE, TRUE);
926 static guint get_size_with_crs(MsgInfo *info)
935 fp = procmsg_open_message(info);
939 while (fgets(buf, sizeof (buf), fp) != NULL) {
941 if (!strstr(buf, "\r") && strstr(buf, "\n"))
949 static gchar *imap_fetch_msg_full(Folder *folder, FolderItem *item, gint uid,
950 gboolean headers, gboolean body)
952 gchar *path, *filename;
953 IMAPSession *session;
956 g_return_val_if_fail(folder != NULL, NULL);
957 g_return_val_if_fail(item != NULL, NULL);
962 path = folder_item_get_path(item);
963 if (!is_dir_exist(path))
965 filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(uid), NULL);
967 debug_print("trying to fetch cached %s\n", filename);
968 if (is_file_exist(filename)) {
969 /* see whether the local file represents the whole message
970 * or not. As the IMAP server reports size with \r chars,
971 * we have to update the local file (UNIX \n only) size */
972 MsgInfo *msginfo = imap_parse_msg(filename, item);
973 MsgInfo *cached = msgcache_get_msg(item->cache,uid);
974 guint have_size = get_size_with_crs(msginfo);
977 debug_print("message %d has been already %scached (%d/%d).\n", uid,
978 have_size >= cached->size ? "fully ":"",
979 have_size, (int)cached->size);
981 if (cached && (cached->size <= have_size || !body)) {
982 procmsg_msginfo_free(cached);
983 procmsg_msginfo_free(msginfo);
984 file_strip_crs(filename);
986 } else if (!cached) {
987 debug_print("message not cached, considering file complete\n");
988 procmsg_msginfo_free(msginfo);
989 file_strip_crs(filename);
992 procmsg_msginfo_free(cached);
993 procmsg_msginfo_free(msginfo);
997 session = imap_session_get(folder);
1006 debug_print("IMAP fetching messages\n");
1007 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
1008 NULL, NULL, NULL, NULL, FALSE);
1009 if (ok != IMAP_SUCCESS) {
1010 g_warning("can't select mailbox %s\n", item->path);
1016 debug_print("getting message %d...\n", uid);
1017 ok = imap_cmd_fetch(session, (guint32)uid, filename, headers, body);
1019 if (ok != IMAP_SUCCESS) {
1020 g_warning("can't fetch message %d\n", uid);
1027 file_strip_crs(filename);
1031 static gint imap_add_msg(Folder *folder, FolderItem *dest,
1032 const gchar *file, MsgFlags *flags)
1036 MsgFileInfo fileinfo;
1038 g_return_val_if_fail(file != NULL, -1);
1040 fileinfo.msginfo = NULL;
1041 fileinfo.file = (gchar *)file;
1042 fileinfo.flags = flags;
1043 file_list.data = &fileinfo;
1044 file_list.next = NULL;
1046 ret = imap_add_msgs(folder, dest, &file_list, NULL);
1050 static gint imap_add_msgs(Folder *folder, FolderItem *dest, GSList *file_list,
1051 GRelation *relation)
1054 IMAPSession *session;
1055 guint32 last_uid = 0;
1057 MsgFileInfo *fileinfo;
1059 gint curnum = 0, total = 0;
1062 g_return_val_if_fail(folder != NULL, -1);
1063 g_return_val_if_fail(dest != NULL, -1);
1064 g_return_val_if_fail(file_list != NULL, -1);
1066 session = imap_session_get(folder);
1071 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
1073 statusbar_print_all(_("Adding messages..."));
1074 total = g_slist_length(file_list);
1075 for (cur = file_list; cur != NULL; cur = cur->next) {
1076 IMAPFlags iflags = 0;
1077 guint32 new_uid = 0;
1078 gchar *real_file = NULL;
1079 fileinfo = (MsgFileInfo *)cur->data;
1081 statusbar_progress_all(curnum, total, 1);
1084 if (fileinfo->flags) {
1085 if (MSG_IS_MARKED(*fileinfo->flags))
1086 iflags |= IMAP_FLAG_FLAGGED;
1087 if (MSG_IS_REPLIED(*fileinfo->flags))
1088 iflags |= IMAP_FLAG_ANSWERED;
1089 if (!MSG_IS_UNREAD(*fileinfo->flags))
1090 iflags |= IMAP_FLAG_SEEN;
1093 if (real_file == NULL)
1094 real_file = g_strdup(fileinfo->file);
1096 if (folder_has_parent_of_type(dest, F_QUEUE) ||
1097 folder_has_parent_of_type(dest, F_OUTBOX) ||
1098 folder_has_parent_of_type(dest, F_DRAFT) ||
1099 folder_has_parent_of_type(dest, F_TRASH))
1100 iflags |= IMAP_FLAG_SEEN;
1102 ok = imap_cmd_append(session, destdir, real_file, iflags,
1105 if (ok != IMAP_SUCCESS) {
1106 g_warning("can't append message %s\n", real_file);
1110 statusbar_progress_all(0,0,0);
1111 statusbar_pop_all();
1114 debug_print("appended new message as %d\n", new_uid);
1115 /* put the local file in the imapcache, so that we don't
1116 * have to fetch it back later. */
1118 gchar *cache_path = folder_item_get_path(dest);
1119 if (!is_dir_exist(cache_path))
1120 make_dir_hier(cache_path);
1121 if (is_dir_exist(cache_path)) {
1122 gchar *cache_file = g_strconcat(
1123 cache_path, G_DIR_SEPARATOR_S,
1124 itos(new_uid), NULL);
1125 copy_file(real_file, cache_file, TRUE);
1126 debug_print("copied to cache: %s\n", cache_file);
1133 if (relation != NULL)
1134 g_relation_insert(relation, fileinfo->msginfo != NULL ?
1135 (gpointer) fileinfo->msginfo : (gpointer) fileinfo,
1136 GINT_TO_POINTER(dest->last_num + 1));
1138 new_uid = dest->last_num+1;
1140 if (last_uid < new_uid) {
1146 statusbar_progress_all(0,0,0);
1147 statusbar_pop_all();
1149 imap_cmd_expunge(session);
1157 static gint imap_do_copy_msgs(Folder *folder, FolderItem *dest,
1158 MsgInfoList *msglist, GRelation *relation)
1162 GSList *seq_list, *cur;
1164 IMAPSession *session;
1165 gint ok = IMAP_SUCCESS;
1166 GRelation *uid_mapping;
1169 g_return_val_if_fail(folder != NULL, -1);
1170 g_return_val_if_fail(dest != NULL, -1);
1171 g_return_val_if_fail(msglist != NULL, -1);
1173 session = imap_session_get(folder);
1179 msginfo = (MsgInfo *)msglist->data;
1181 src = msginfo->folder;
1183 g_warning("the src folder is identical to the dest.\n");
1188 if (src->folder != dest->folder) {
1189 GSList *infolist = NULL, *cur;
1191 for (cur = msglist; cur; cur = cur->next) {
1192 msginfo = (MsgInfo *)cur->data;
1193 MsgFileInfo *fileinfo = g_new0(MsgFileInfo, 1);
1194 fileinfo->file = procmsg_get_message_file(msginfo);
1195 fileinfo->flags = &(msginfo->flags);
1196 infolist = g_slist_prepend(infolist, fileinfo);
1198 infolist = g_slist_reverse(infolist);
1199 res = folder_item_add_msgs(dest, infolist, FALSE);
1200 for (cur = infolist; cur; cur = cur->next) {
1201 MsgFileInfo *info = (MsgFileInfo *)cur->data;
1205 g_slist_free(infolist);
1209 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
1210 NULL, NULL, NULL, NULL, FALSE);
1211 if (ok != IMAP_SUCCESS) {
1216 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
1217 seq_list = imap_get_lep_set_from_msglist(msglist);
1218 uid_mapping = g_relation_new(2);
1219 g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
1221 statusbar_print_all(_("Copying messages..."));
1222 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
1223 struct mailimap_set * seq_set;
1224 seq_set = cur->data;
1226 debug_print("Copying messages from %s to %s ...\n",
1227 src->path, destdir);
1229 ok = imap_cmd_copy(session, seq_set, destdir, uid_mapping);
1230 if (ok != IMAP_SUCCESS) {
1231 g_relation_destroy(uid_mapping);
1232 imap_lep_set_free(seq_list);
1238 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
1239 MsgInfo *msginfo = (MsgInfo *)cur->data;
1242 tuples = g_relation_select(uid_mapping,
1243 GINT_TO_POINTER(msginfo->msgnum),
1245 if (tuples->len > 0) {
1246 gint num = GPOINTER_TO_INT(g_tuples_index(tuples, 0, 1));
1247 g_relation_insert(relation, msginfo,
1248 GPOINTER_TO_INT(num));
1252 g_relation_insert(relation, msginfo,
1253 GPOINTER_TO_INT(0));
1254 g_tuples_destroy(tuples);
1256 statusbar_pop_all();
1258 g_relation_destroy(uid_mapping);
1259 imap_lep_set_free(seq_list);
1263 IMAP_FOLDER_ITEM(dest)->lastuid = 0;
1264 IMAP_FOLDER_ITEM(dest)->uid_next = 0;
1265 g_slist_free(IMAP_FOLDER_ITEM(dest)->uid_list);
1266 IMAP_FOLDER_ITEM(dest)->uid_list = NULL;
1269 if (ok == IMAP_SUCCESS)
1275 static gint imap_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1279 g_return_val_if_fail(msginfo != NULL, -1);
1281 msglist.data = msginfo;
1282 msglist.next = NULL;
1284 return imap_copy_msgs(folder, dest, &msglist, NULL);
1287 static gint imap_copy_msgs(Folder *folder, FolderItem *dest,
1288 MsgInfoList *msglist, GRelation *relation)
1293 g_return_val_if_fail(folder != NULL, -1);
1294 g_return_val_if_fail(dest != NULL, -1);
1295 g_return_val_if_fail(msglist != NULL, -1);
1297 msginfo = (MsgInfo *)msglist->data;
1298 g_return_val_if_fail(msginfo->folder != NULL, -1);
1300 ret = imap_do_copy_msgs(folder, dest, msglist, relation);
1305 static gint imap_do_remove_msgs(Folder *folder, FolderItem *dest,
1306 MsgInfoList *msglist, GRelation *relation)
1308 gchar *destdir, *dir;
1309 GSList *numlist = NULL, *cur;
1311 IMAPSession *session;
1312 gint ok = IMAP_SUCCESS;
1313 GRelation *uid_mapping;
1315 g_return_val_if_fail(folder != NULL, -1);
1316 g_return_val_if_fail(dest != NULL, -1);
1317 g_return_val_if_fail(msglist != NULL, -1);
1319 session = imap_session_get(folder);
1324 msginfo = (MsgInfo *)msglist->data;
1326 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
1327 NULL, NULL, NULL, NULL, FALSE);
1328 if (ok != IMAP_SUCCESS) {
1333 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
1334 for (cur = msglist; cur; cur = cur->next) {
1335 msginfo = (MsgInfo *)cur->data;
1336 if (!MSG_IS_DELETED(msginfo->flags))
1337 numlist = g_slist_prepend(numlist, GINT_TO_POINTER(msginfo->msgnum));
1339 numlist = g_slist_reverse(numlist);
1341 uid_mapping = g_relation_new(2);
1342 g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
1344 ok = imap_set_message_flags
1345 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
1346 numlist, IMAP_FLAG_DELETED, TRUE);
1347 if (ok != IMAP_SUCCESS) {
1348 log_warning(_("can't set deleted flags\n"));
1352 ok = imap_cmd_expunge(session);
1353 if (ok != IMAP_SUCCESS) {
1354 log_warning(_("can't expunge\n"));
1359 dir = folder_item_get_path(msginfo->folder);
1360 if (is_dir_exist(dir)) {
1361 for (cur = msglist; cur; cur = cur->next) {
1362 msginfo = (MsgInfo *)cur->data;
1363 remove_numbered_files(dir, msginfo->msgnum, msginfo->msgnum);
1368 g_relation_destroy(uid_mapping);
1369 g_slist_free(numlist);
1373 if (ok == IMAP_SUCCESS)
1379 static gint imap_remove_msgs(Folder *folder, FolderItem *dest,
1380 MsgInfoList *msglist, GRelation *relation)
1384 g_return_val_if_fail(folder != NULL, -1);
1385 g_return_val_if_fail(dest != NULL, -1);
1386 if (msglist == NULL)
1389 msginfo = (MsgInfo *)msglist->data;
1390 g_return_val_if_fail(msginfo->folder != NULL, -1);
1392 return imap_do_remove_msgs(folder, dest, msglist, relation);
1395 static gint imap_remove_all_msg(Folder *folder, FolderItem *item)
1397 GSList *list = folder_item_get_msg_list(item);
1398 gint res = imap_remove_msgs(folder, item, list, NULL);
1399 procmsg_msg_list_free(list);
1403 static gboolean imap_is_msg_changed(Folder *folder, FolderItem *item,
1406 /* TODO: properly implement this method */
1410 static gint imap_close(Folder *folder, FolderItem *item)
1415 static gint imap_scan_tree(Folder *folder)
1417 FolderItem *item = NULL;
1418 IMAPSession *session;
1419 gchar *root_folder = NULL;
1421 g_return_val_if_fail(folder != NULL, -1);
1422 g_return_val_if_fail(folder->account != NULL, -1);
1424 session = imap_session_get(folder);
1426 if (!folder->node) {
1427 folder_tree_destroy(folder);
1428 item = folder_item_new(folder, folder->name, NULL);
1429 item->folder = folder;
1430 folder->node = item->node = g_node_new(item);
1436 if (folder->account->imap_dir && *folder->account->imap_dir) {
1441 Xstrdup_a(root_folder, folder->account->imap_dir, {unlock_session();return -1;});
1442 extract_quote(root_folder, '"');
1443 subst_char(root_folder,
1444 imap_get_path_separator(IMAP_FOLDER(folder),
1447 strtailchomp(root_folder, '/');
1448 real_path = imap_get_real_path
1449 (IMAP_FOLDER(folder), root_folder);
1450 debug_print("IMAP root directory: %s\n", real_path);
1452 /* check if root directory exist */
1454 r = imap_threaded_list(session->folder, "", real_path,
1456 if ((r != MAILIMAP_NO_ERROR) || (clist_count(lep_list) == 0)) {
1457 if (!folder->node) {
1458 item = folder_item_new(folder, folder->name, NULL);
1459 item->folder = folder;
1460 folder->node = item->node = g_node_new(item);
1465 mailimap_list_result_free(lep_list);
1471 item = FOLDER_ITEM(folder->node->data);
1472 if (!item || ((item->path || root_folder) &&
1473 strcmp2(item->path, root_folder) != 0)) {
1474 folder_tree_destroy(folder);
1475 item = folder_item_new(folder, folder->name, root_folder);
1476 item->folder = folder;
1477 folder->node = item->node = g_node_new(item);
1480 imap_scan_tree_recursive(session, FOLDER_ITEM(folder->node->data));
1481 imap_create_missing_folders(folder);
1487 static gint imap_scan_tree_recursive(IMAPSession *session, FolderItem *item)
1490 IMAPFolder *imapfolder;
1491 FolderItem *new_item;
1492 GSList *item_list, *cur;
1495 gchar *wildcard_path;
1501 g_return_val_if_fail(item != NULL, -1);
1502 g_return_val_if_fail(item->folder != NULL, -1);
1503 g_return_val_if_fail(item->no_sub == FALSE, -1);
1505 folder = item->folder;
1506 imapfolder = IMAP_FOLDER(folder);
1508 separator = imap_get_path_separator(imapfolder, item->path);
1510 if (folder->ui_func)
1511 folder->ui_func(folder, item, folder->ui_func_data);
1514 wildcard[0] = separator;
1517 real_path = imap_get_real_path(imapfolder, item->path);
1521 real_path = g_strdup("");
1524 Xstrcat_a(wildcard_path, real_path, wildcard,
1525 {g_free(real_path); return IMAP_ERROR;});
1527 r = imap_threaded_list(folder, "", wildcard_path, &lep_list);
1528 if (r != MAILIMAP_NO_ERROR) {
1532 item_list = imap_list_from_lep(imapfolder,
1533 lep_list, real_path, FALSE);
1534 mailimap_list_result_free(lep_list);
1539 node = item->node->children;
1540 while (node != NULL) {
1541 FolderItem *old_item = FOLDER_ITEM(node->data);
1542 GNode *next = node->next;
1545 for (cur = item_list; cur != NULL; cur = cur->next) {
1546 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1547 if (!strcmp2(old_item->path, cur_item->path)) {
1548 new_item = cur_item;
1553 debug_print("folder '%s' not found. removing...\n",
1555 folder_item_remove(old_item);
1557 old_item->no_sub = new_item->no_sub;
1558 old_item->no_select = new_item->no_select;
1559 if (old_item->no_sub == TRUE && node->children) {
1560 debug_print("folder '%s' doesn't have "
1561 "subfolders. removing...\n",
1563 folder_item_remove_children(old_item);
1570 for (cur = item_list; cur != NULL; cur = cur->next) {
1571 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1574 for (node = item->node->children; node != NULL;
1575 node = node->next) {
1576 if (!strcmp2(FOLDER_ITEM(node->data)->path,
1578 new_item = FOLDER_ITEM(node->data);
1579 folder_item_destroy(cur_item);
1585 new_item = cur_item;
1586 debug_print("new folder '%s' found.\n", new_item->path);
1587 folder_item_append(item, new_item);
1590 if (!strcmp(new_item->path, "INBOX")) {
1591 new_item->stype = F_INBOX;
1592 folder->inbox = new_item;
1593 } else if (!folder_item_parent(item) || item->stype == F_INBOX) {
1596 base = g_path_get_basename(new_item->path);
1598 if (!folder->outbox && !g_ascii_strcasecmp(base, "Sent")) {
1599 new_item->stype = F_OUTBOX;
1600 folder->outbox = new_item;
1601 } else if (!folder->draft && !g_ascii_strcasecmp(base, "Drafts")) {
1602 new_item->stype = F_DRAFT;
1603 folder->draft = new_item;
1604 } else if (!folder->queue && !g_ascii_strcasecmp(base, "Queue")) {
1605 new_item->stype = F_QUEUE;
1606 folder->queue = new_item;
1607 } else if (!folder->trash && !g_ascii_strcasecmp(base, "Trash")) {
1608 new_item->stype = F_TRASH;
1609 folder->trash = new_item;
1614 if (new_item->no_sub == FALSE)
1615 imap_scan_tree_recursive(session, new_item);
1618 g_slist_free(item_list);
1620 return IMAP_SUCCESS;
1623 static gint imap_create_tree(Folder *folder)
1625 g_return_val_if_fail(folder != NULL, -1);
1626 g_return_val_if_fail(folder->node != NULL, -1);
1627 g_return_val_if_fail(folder->node->data != NULL, -1);
1628 g_return_val_if_fail(folder->account != NULL, -1);
1630 imap_scan_tree(folder);
1631 imap_create_missing_folders(folder);
1636 static void imap_create_missing_folders(Folder *folder)
1638 g_return_if_fail(folder != NULL);
1641 folder->inbox = imap_create_special_folder
1642 (folder, F_INBOX, "INBOX");
1644 folder->trash = imap_create_special_folder
1645 (folder, F_TRASH, "Trash");
1647 folder->queue = imap_create_special_folder
1648 (folder, F_QUEUE, "Queue");
1649 if (!folder->outbox)
1650 folder->outbox = imap_create_special_folder
1651 (folder, F_OUTBOX, "Sent");
1653 folder->draft = imap_create_special_folder
1654 (folder, F_DRAFT, "Drafts");
1657 static FolderItem *imap_create_special_folder(Folder *folder,
1658 SpecialFolderItemType stype,
1662 FolderItem *new_item;
1664 g_return_val_if_fail(folder != NULL, NULL);
1665 g_return_val_if_fail(folder->node != NULL, NULL);
1666 g_return_val_if_fail(folder->node->data != NULL, NULL);
1667 g_return_val_if_fail(folder->account != NULL, NULL);
1668 g_return_val_if_fail(name != NULL, NULL);
1670 item = FOLDER_ITEM(folder->node->data);
1671 new_item = imap_create_folder(folder, item, name);
1674 g_warning("Can't create '%s'\n", name);
1675 if (!folder->inbox) return NULL;
1677 new_item = imap_create_folder(folder, folder->inbox, name);
1679 g_warning("Can't create '%s' under INBOX\n", name);
1681 new_item->stype = stype;
1683 new_item->stype = stype;
1688 static gchar *imap_folder_get_path(Folder *folder)
1692 g_return_val_if_fail(folder != NULL, NULL);
1693 g_return_val_if_fail(folder->account != NULL, NULL);
1695 folder_path = g_strconcat(get_imap_cache_dir(),
1697 folder->account->recv_server,
1699 folder->account->userid,
1705 static gchar *imap_item_get_path(Folder *folder, FolderItem *item)
1707 gchar *folder_path, *path;
1709 g_return_val_if_fail(folder != NULL, NULL);
1710 g_return_val_if_fail(item != NULL, NULL);
1711 folder_path = imap_folder_get_path(folder);
1713 g_return_val_if_fail(folder_path != NULL, NULL);
1714 if (folder_path[0] == G_DIR_SEPARATOR) {
1716 path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
1719 path = g_strdup(folder_path);
1722 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1723 folder_path, G_DIR_SEPARATOR_S,
1726 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1729 g_free(folder_path);
1734 static FolderItem *imap_create_folder(Folder *folder, FolderItem *parent,
1737 gchar *dirpath, *imap_path;
1738 IMAPSession *session;
1739 FolderItem *new_item;
1744 gboolean no_select = FALSE, no_sub = FALSE;
1746 g_return_val_if_fail(folder != NULL, NULL);
1747 g_return_val_if_fail(folder->account != NULL, NULL);
1748 g_return_val_if_fail(parent != NULL, NULL);
1749 g_return_val_if_fail(name != NULL, NULL);
1751 session = imap_session_get(folder);
1757 if (!folder_item_parent(parent) && strcmp(name, "INBOX") == 0) {
1758 dirpath = g_strdup(name);
1759 }else if (parent->path)
1760 dirpath = g_strconcat(parent->path, "/", name, NULL);
1761 else if ((p = strchr(name, '/')) != NULL && *(p + 1) != '\0')
1762 dirpath = g_strdup(name);
1763 else if (folder->account->imap_dir && *folder->account->imap_dir) {
1766 Xstrdup_a(imap_dir, folder->account->imap_dir, {unlock_session();return NULL;});
1767 strtailchomp(imap_dir, '/');
1768 dirpath = g_strconcat(imap_dir, "/", name, NULL);
1770 dirpath = g_strdup(name);
1774 /* keep trailing directory separator to create a folder that contains
1776 imap_path = imap_utf8_to_modified_utf7(dirpath);
1778 strtailchomp(dirpath, '/');
1779 Xstrdup_a(new_name, name, {
1784 separator = imap_get_path_separator(IMAP_FOLDER(folder), imap_path);
1785 imap_path_separator_subst(imap_path, separator);
1786 /* remove trailing / for display */
1787 strtailchomp(new_name, '/');
1789 if (strcmp(dirpath, "INBOX") != 0) {
1791 gboolean exist = FALSE;
1795 argbuf = g_ptr_array_new();
1796 r = imap_threaded_list(folder, "", imap_path, &lep_list);
1797 if (r != MAILIMAP_NO_ERROR) {
1798 log_warning(_("can't create mailbox: LIST failed\n"));
1801 ptr_array_free_strings(argbuf);
1802 g_ptr_array_free(argbuf, TRUE);
1807 if (clist_count(lep_list) > 0)
1809 mailimap_list_result_free(lep_list);
1812 ok = imap_cmd_create(session, imap_path);
1813 if (ok != IMAP_SUCCESS) {
1814 log_warning(_("can't create mailbox\n"));
1820 r = imap_threaded_list(folder, "", imap_path, &lep_list);
1821 if (r == MAILIMAP_NO_ERROR) {
1822 GSList *item_list = imap_list_from_lep(IMAP_FOLDER(folder),
1823 lep_list, dirpath, TRUE);
1825 FolderItem *cur_item = FOLDER_ITEM(item_list->data);
1826 no_select = cur_item->no_select;
1827 no_sub = cur_item->no_sub;
1828 g_slist_free(item_list);
1830 mailimap_list_result_free(lep_list);
1837 /* just get flags */
1838 r = imap_threaded_list(folder, "", "INBOX", &lep_list);
1839 if (r == MAILIMAP_NO_ERROR) {
1840 GSList *item_list = imap_list_from_lep(IMAP_FOLDER(folder),
1841 lep_list, dirpath, TRUE);
1843 FolderItem *cur_item = FOLDER_ITEM(item_list->data);
1844 no_select = cur_item->no_select;
1845 no_sub = cur_item->no_sub;
1846 g_slist_free(item_list);
1848 mailimap_list_result_free(lep_list);
1852 new_item = folder_item_new(folder, new_name, dirpath);
1853 new_item->no_select = no_select;
1854 new_item->no_sub = no_sub;
1855 folder_item_append(parent, new_item);
1859 dirpath = folder_item_get_path(new_item);
1860 if (!is_dir_exist(dirpath))
1861 make_dir_hier(dirpath);
1867 static gint imap_rename_folder(Folder *folder, FolderItem *item,
1872 gchar *real_oldpath;
1873 gchar *real_newpath;
1875 gchar *old_cache_dir;
1876 gchar *new_cache_dir;
1877 IMAPSession *session;
1880 gint exists, recent, unseen;
1881 guint32 uid_validity;
1883 g_return_val_if_fail(folder != NULL, -1);
1884 g_return_val_if_fail(item != NULL, -1);
1885 g_return_val_if_fail(item->path != NULL, -1);
1886 g_return_val_if_fail(name != NULL, -1);
1888 session = imap_session_get(folder);
1894 if (strchr(name, imap_get_path_separator(IMAP_FOLDER(folder), item->path)) != NULL) {
1895 g_warning(_("New folder name must not contain the namespace "
1901 real_oldpath = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1903 g_free(session->mbox);
1904 session->mbox = NULL;
1905 ok = imap_cmd_examine(session, "INBOX",
1906 &exists, &recent, &unseen, &uid_validity, FALSE);
1907 if (ok != IMAP_SUCCESS) {
1908 g_free(real_oldpath);
1913 separator = imap_get_path_separator(IMAP_FOLDER(folder), item->path);
1914 if (strchr(item->path, G_DIR_SEPARATOR)) {
1915 dirpath = g_path_get_dirname(item->path);
1916 newpath = g_strconcat(dirpath, G_DIR_SEPARATOR_S, name, NULL);
1919 newpath = g_strdup(name);
1921 real_newpath = imap_utf8_to_modified_utf7(newpath);
1922 imap_path_separator_subst(real_newpath, separator);
1924 ok = imap_cmd_rename(session, real_oldpath, real_newpath);
1925 if (ok != IMAP_SUCCESS) {
1926 log_warning(_("can't rename mailbox: %s to %s\n"),
1927 real_oldpath, real_newpath);
1928 g_free(real_oldpath);
1930 g_free(real_newpath);
1936 item->name = g_strdup(name);
1938 old_cache_dir = folder_item_get_path(item);
1940 paths[0] = g_strdup(item->path);
1942 g_node_traverse(item->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1943 imap_rename_folder_func, paths);
1945 if (is_dir_exist(old_cache_dir)) {
1946 new_cache_dir = folder_item_get_path(item);
1947 if (rename(old_cache_dir, new_cache_dir) < 0) {
1948 FILE_OP_ERROR(old_cache_dir, "rename");
1950 g_free(new_cache_dir);
1953 g_free(old_cache_dir);
1956 g_free(real_oldpath);
1957 g_free(real_newpath);
1962 static gint imap_remove_folder_real(Folder *folder, FolderItem *item)
1965 IMAPSession *session;
1969 g_return_val_if_fail(folder != NULL, -1);
1970 g_return_val_if_fail(item != NULL, -1);
1971 g_return_val_if_fail(item->path != NULL, -1);
1973 session = imap_session_get(folder);
1978 path = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1980 ok = imap_cmd_delete(session, path);
1981 if (ok != IMAP_SUCCESS) {
1982 gchar *tmp = g_strdup_printf("%s%c", path,
1983 imap_get_path_separator(IMAP_FOLDER(folder), path));
1986 ok = imap_cmd_delete(session, path);
1989 if (ok != IMAP_SUCCESS) {
1990 log_warning(_("can't delete mailbox\n"));
1997 cache_dir = folder_item_get_path(item);
1998 if (is_dir_exist(cache_dir) && remove_dir_recursive(cache_dir) < 0)
1999 g_warning("can't remove directory '%s'\n", cache_dir);
2001 folder_item_remove(item);
2006 static gint imap_remove_folder(Folder *folder, FolderItem *item)
2010 g_return_val_if_fail(item != NULL, -1);
2011 g_return_val_if_fail(item->folder != NULL, -1);
2012 g_return_val_if_fail(item->node != NULL, -1);
2014 node = item->node->children;
2015 while (node != NULL) {
2017 if (imap_remove_folder(folder, FOLDER_ITEM(node->data)) < 0)
2021 debug_print("IMAP removing %s\n", item->path);
2023 if (imap_remove_all_msg(folder, item) < 0)
2025 return imap_remove_folder_real(folder, item);
2028 typedef struct _uncached_data {
2029 IMAPSession *session;
2031 MsgNumberList *numlist;
2037 static void *imap_get_uncached_messages_thread(void *data)
2039 uncached_data *stuff = (uncached_data *)data;
2040 IMAPSession *session = stuff->session;
2041 FolderItem *item = stuff->item;
2042 MsgNumberList *numlist = stuff->numlist;
2044 GSList *newlist = NULL;
2045 GSList *llast = NULL;
2046 GSList *seq_list, *cur;
2048 debug_print("uncached_messages\n");
2050 if (session == NULL || item == NULL || item->folder == NULL
2051 || FOLDER_CLASS(item->folder) != &imap_class) {
2056 seq_list = imap_get_lep_set_from_numlist(numlist);
2057 debug_print("get msgs info\n");
2058 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
2059 struct mailimap_set * imapset;
2065 imapset = cur->data;
2067 r = imap_threaded_fetch_env(session->folder,
2068 imapset, &env_list);
2069 if (r != MAILIMAP_NO_ERROR)
2072 session_set_access_time(SESSION(session));
2075 for(i = 0 ; i < carray_count(env_list) ; i ++) {
2076 struct imap_fetch_env_info * info;
2079 info = carray_get(env_list, i);
2080 msginfo = imap_envelope_from_lep(info, item);
2081 if (msginfo == NULL)
2083 msginfo->folder = item;
2085 llast = newlist = g_slist_append(newlist, msginfo);
2087 llast = g_slist_append(llast, msginfo);
2088 llast = llast->next;
2093 imap_fetch_env_free(env_list);
2096 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
2097 struct mailimap_set * imapset;
2099 imapset = cur->data;
2100 mailimap_set_free(imapset);
2103 session_set_access_time(SESSION(session));
2108 #define MAX_MSG_NUM 50
2110 static GSList *imap_get_uncached_messages(IMAPSession *session,
2112 MsgNumberList *numlist)
2114 GSList *result = NULL;
2116 uncached_data *data = g_new0(uncached_data, 1);
2121 data->total = g_slist_length(numlist);
2122 debug_print("messages list : %i\n", data->total);
2124 while (cur != NULL) {
2125 GSList * partial_result;
2133 while (count < MAX_MSG_NUM) {
2138 if (newlist == NULL)
2139 llast = newlist = g_slist_append(newlist, p);
2141 llast = g_slist_append(llast, p);
2142 llast = llast->next;
2152 data->session = session;
2154 data->numlist = newlist;
2157 if (prefs_common.work_offline &&
2158 !inc_offline_should_override(
2159 _("Sylpheed-Claws needs network access in order "
2160 "to access the IMAP server."))) {
2166 (GSList *)imap_get_uncached_messages_thread(data);
2168 statusbar_progress_all(data->cur,data->total, 1);
2170 g_slist_free(newlist);
2172 result = g_slist_concat(result, partial_result);
2176 statusbar_progress_all(0,0,0);
2177 statusbar_pop_all();
2182 static void imap_delete_all_cached_messages(FolderItem *item)
2186 g_return_if_fail(item != NULL);
2187 g_return_if_fail(item->folder != NULL);
2188 g_return_if_fail(FOLDER_CLASS(item->folder) == &imap_class);
2190 debug_print("Deleting all cached messages...\n");
2192 dir = folder_item_get_path(item);
2193 if (is_dir_exist(dir))
2194 remove_all_numbered_files(dir);
2197 debug_print("done.\n");
2200 static IMAPNameSpace *imap_find_namespace_from_list(GList *ns_list,
2203 IMAPNameSpace *namespace = NULL;
2204 gchar *tmp_path, *name;
2206 if (!path) path = "";
2208 for (; ns_list != NULL; ns_list = ns_list->next) {
2209 IMAPNameSpace *tmp_ns = ns_list->data;
2211 Xstrcat_a(tmp_path, path, "/", return namespace);
2212 Xstrdup_a(name, tmp_ns->name, return namespace);
2213 if (tmp_ns->separator && tmp_ns->separator != '/') {
2214 subst_char(tmp_path, tmp_ns->separator, '/');
2215 subst_char(name, tmp_ns->separator, '/');
2217 if (strncmp(tmp_path, name, strlen(name)) == 0)
2224 static IMAPNameSpace *imap_find_namespace(IMAPFolder *folder,
2227 IMAPNameSpace *namespace;
2229 g_return_val_if_fail(folder != NULL, NULL);
2231 namespace = imap_find_namespace_from_list(folder->ns_personal, path);
2232 if (namespace) return namespace;
2233 namespace = imap_find_namespace_from_list(folder->ns_others, path);
2234 if (namespace) return namespace;
2235 namespace = imap_find_namespace_from_list(folder->ns_shared, path);
2236 if (namespace) return namespace;
2241 gchar imap_get_path_separator_for_item(FolderItem *item)
2243 Folder *folder = NULL;
2244 IMAPFolder *imap_folder = NULL;
2247 folder = item->folder;
2252 imap_folder = IMAP_FOLDER(folder);
2257 return imap_get_path_separator(imap_folder, item->path);
2260 static gchar imap_get_path_separator(IMAPFolder *folder, const gchar *path)
2262 IMAPNameSpace *namespace;
2263 gchar separator = '/';
2264 IMAPSession *session = imap_session_get(FOLDER(folder));
2265 g_return_val_if_fail(session != NULL, '/');
2267 if (folder->last_seen_separator == 0) {
2269 int r = imap_threaded_list((Folder *)folder, "", "", &lep_list);
2270 if (r != MAILIMAP_NO_ERROR) {
2271 log_warning(_("LIST failed\n"));
2275 if (clist_count(lep_list) > 0) {
2276 clistiter * iter = clist_begin(lep_list);
2277 struct mailimap_mailbox_list * mb;
2278 mb = clist_content(iter);
2280 folder->last_seen_separator = mb->mb_delimiter;
2281 debug_print("got separator: %c\n", folder->last_seen_separator);
2283 mailimap_list_result_free(lep_list);
2286 if (folder->last_seen_separator != 0) {
2287 debug_print("using separator: %c\n", folder->last_seen_separator);
2288 return folder->last_seen_separator;
2291 namespace = imap_find_namespace(folder, path);
2292 if (namespace && namespace->separator)
2293 separator = namespace->separator;
2298 static gchar *imap_get_real_path(IMAPFolder *folder, const gchar *path)
2303 g_return_val_if_fail(folder != NULL, NULL);
2304 g_return_val_if_fail(path != NULL, NULL);
2306 real_path = imap_utf8_to_modified_utf7(path);
2307 separator = imap_get_path_separator(folder, path);
2308 imap_path_separator_subst(real_path, separator);
2313 static gint imap_set_message_flags(IMAPSession *session,
2314 MsgNumberList *numlist,
2322 seq_list = imap_get_lep_set_from_numlist(numlist);
2324 for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) {
2325 struct mailimap_set * imapset;
2327 imapset = cur->data;
2329 ok = imap_cmd_store(session, imapset,
2333 imap_lep_set_free(seq_list);
2335 return IMAP_SUCCESS;
2338 typedef struct _select_data {
2339 IMAPSession *session;
2344 guint32 *uid_validity;
2348 static gint imap_select(IMAPSession *session, IMAPFolder *folder,
2350 gint *exists, gint *recent, gint *unseen,
2351 guint32 *uid_validity, gboolean block)
2355 gint exists_, recent_, unseen_;
2356 guint32 uid_validity_;
2358 if (!exists && !recent && !unseen && !uid_validity) {
2359 if (session->mbox && strcmp(session->mbox, path) == 0)
2360 return IMAP_SUCCESS;
2369 uid_validity = &uid_validity_;
2371 g_free(session->mbox);
2372 session->mbox = NULL;
2374 real_path = imap_get_real_path(folder, path);
2376 ok = imap_cmd_select(session, real_path,
2377 exists, recent, unseen, uid_validity, block);
2378 if (ok != IMAP_SUCCESS)
2379 log_warning(_("can't select folder: %s\n"), real_path);
2381 session->mbox = g_strdup(path);
2382 session->folder_content_changed = FALSE;
2389 static gint imap_status(IMAPSession *session, IMAPFolder *folder,
2390 const gchar *path, IMAPFolderItem *item,
2392 guint32 *uid_next, guint32 *uid_validity,
2393 gint *unseen, gboolean block)
2397 struct mailimap_mailbox_data_status * data_status;
2402 real_path = imap_get_real_path(folder, path);
2416 r = imap_threaded_status(FOLDER(folder), real_path,
2417 &data_status, mask);
2420 if (r != MAILIMAP_NO_ERROR) {
2421 debug_print("status err %d\n", r);
2425 if (data_status->st_info_list == NULL) {
2426 mailimap_mailbox_data_status_free(data_status);
2427 debug_print("status->st_info_list == NULL\n");
2432 for(iter = clist_begin(data_status->st_info_list) ; iter != NULL ;
2433 iter = clist_next(iter)) {
2434 struct mailimap_status_info * info;
2436 info = clist_content(iter);
2437 switch (info->st_att) {
2438 case MAILIMAP_STATUS_ATT_MESSAGES:
2439 * messages = info->st_value;
2440 got_values |= 1 << 0;
2443 case MAILIMAP_STATUS_ATT_UIDNEXT:
2444 * uid_next = info->st_value;
2445 got_values |= 1 << 2;
2448 case MAILIMAP_STATUS_ATT_UIDVALIDITY:
2449 * uid_validity = info->st_value;
2450 got_values |= 1 << 3;
2453 case MAILIMAP_STATUS_ATT_UNSEEN:
2454 * unseen = info->st_value;
2455 got_values |= 1 << 4;
2459 mailimap_mailbox_data_status_free(data_status);
2461 if (got_values != mask) {
2462 debug_print("status: incomplete values received (%d)\n", got_values);
2465 return IMAP_SUCCESS;
2468 static void imap_free_capabilities(IMAPSession *session)
2470 slist_free_strings(session->capability);
2471 g_slist_free(session->capability);
2472 session->capability = NULL;
2475 /* low-level IMAP4rev1 commands */
2477 static gint imap_cmd_login(IMAPSession *session,
2478 const gchar *user, const gchar *pass,
2484 if (!strcmp(type, "LOGIN") && imap_has_capability(session, "LOGINDISABLED")) {
2485 gint ok = IMAP_ERROR;
2486 if (imap_has_capability(session, "STARTTLS")) {
2488 log_warning(_("Server requires TLS to log in.\n"));
2489 ok = imap_cmd_starttls(session);
2490 if (ok != IMAP_SUCCESS) {
2491 log_warning(_("Can't start TLS session.\n"));
2495 imap_free_capabilities(session);
2496 if (imap_get_capabilities(session) != MAILIMAP_NO_ERROR) {
2497 log_warning(_("Can't refresh capabilities.\n"));
2502 log_error(_("Connection to %s failed: "
2503 "server requires TLS, but Sylpheed-Claws "
2504 "has been compiled without OpenSSL "
2506 SESSION(session)->server);
2510 log_error(_("Server logins are disabled.\n"));
2515 log_print("IMAP4> Logging %s to %s using %s\n",
2517 SESSION(session)->server,
2519 r = imap_threaded_login(session->folder, user, pass, type);
2520 if (r != MAILIMAP_NO_ERROR) {
2521 log_print("IMAP4< Error logging in to %s\n",
2522 SESSION(session)->server);
2525 log_print("IMAP4< Login to %s successful\n",
2526 SESSION(session)->server);
2532 static gint imap_cmd_noop(IMAPSession *session)
2535 unsigned int exists;
2537 r = imap_threaded_noop(session->folder, &exists);
2538 if (r != MAILIMAP_NO_ERROR) {
2539 debug_print("noop err %d\n", r);
2542 session->exists = exists;
2543 session_set_access_time(SESSION(session));
2545 return IMAP_SUCCESS;
2549 static gint imap_cmd_starttls(IMAPSession *session)
2553 r = imap_threaded_starttls(session->folder,
2554 SESSION(session)->server, SESSION(session)->port);
2555 if (r != MAILIMAP_NO_ERROR) {
2556 debug_print("starttls err %d\n", r);
2559 return IMAP_SUCCESS;
2563 static gint imap_cmd_select(IMAPSession *session, const gchar *folder,
2564 gint *exists, gint *recent, gint *unseen,
2565 guint32 *uid_validity, gboolean block)
2569 r = imap_threaded_select(session->folder, folder,
2570 exists, recent, unseen, uid_validity);
2571 if (r != MAILIMAP_NO_ERROR) {
2572 debug_print("select err %d\n", r);
2575 return IMAP_SUCCESS;
2578 static gint imap_cmd_examine(IMAPSession *session, const gchar *folder,
2579 gint *exists, gint *recent, gint *unseen,
2580 guint32 *uid_validity, gboolean block)
2584 r = imap_threaded_examine(session->folder, folder,
2585 exists, recent, unseen, uid_validity);
2586 if (r != MAILIMAP_NO_ERROR) {
2587 debug_print("examine err %d\n", r);
2591 return IMAP_SUCCESS;
2594 static gint imap_cmd_create(IMAPSession *session, const gchar *folder)
2598 r = imap_threaded_create(session->folder, folder);
2599 if (r != MAILIMAP_NO_ERROR) {
2604 return IMAP_SUCCESS;
2607 static gint imap_cmd_rename(IMAPSession *session, const gchar *old_folder,
2608 const gchar *new_folder)
2612 r = imap_threaded_rename(session->folder, old_folder,
2614 if (r != MAILIMAP_NO_ERROR) {
2619 return IMAP_SUCCESS;
2622 static gint imap_cmd_delete(IMAPSession *session, const gchar *folder)
2627 r = imap_threaded_delete(session->folder, folder);
2628 if (r != MAILIMAP_NO_ERROR) {
2633 return IMAP_SUCCESS;
2636 typedef struct _fetch_data {
2637 IMAPSession *session;
2639 const gchar *filename;
2645 static void *imap_cmd_fetch_thread(void *data)
2647 fetch_data *stuff = (fetch_data *)data;
2648 IMAPSession *session = stuff->session;
2649 guint32 uid = stuff->uid;
2650 const gchar *filename = stuff->filename;
2654 r = imap_threaded_fetch_content(session->folder,
2658 r = imap_threaded_fetch_content(session->folder,
2661 if (r != MAILIMAP_NO_ERROR) {
2662 debug_print("fetch err %d\n", r);
2663 return GINT_TO_POINTER(IMAP_ERROR);
2665 return GINT_TO_POINTER(IMAP_SUCCESS);
2668 static gint imap_cmd_fetch(IMAPSession *session, guint32 uid,
2669 const gchar *filename, gboolean headers,
2672 fetch_data *data = g_new0(fetch_data, 1);
2675 data->session = session;
2677 data->filename = filename;
2678 data->headers = headers;
2681 if (prefs_common.work_offline &&
2682 !inc_offline_should_override(
2683 _("Sylpheed-Claws needs network access in order "
2684 "to access the IMAP server."))) {
2688 statusbar_print_all(_("Fetching message..."));
2689 result = GPOINTER_TO_INT(imap_cmd_fetch_thread(data));
2690 statusbar_pop_all();
2696 static gint imap_cmd_append(IMAPSession *session, const gchar *destfolder,
2697 const gchar *file, IMAPFlags flags,
2700 struct mailimap_flag_list * flag_list;
2703 g_return_val_if_fail(file != NULL, IMAP_ERROR);
2705 flag_list = imap_flag_to_lep(flags);
2706 r = imap_threaded_append(session->folder, destfolder,
2707 file, flag_list, (int *)new_uid);
2708 mailimap_flag_list_free(flag_list);
2710 if (r != MAILIMAP_NO_ERROR) {
2711 debug_print("append err %d\n", r);
2714 return IMAP_SUCCESS;
2717 static gint imap_cmd_copy(IMAPSession *session, struct mailimap_set * set,
2718 const gchar *destfolder, GRelation *uid_mapping)
2722 g_return_val_if_fail(session != NULL, IMAP_ERROR);
2723 g_return_val_if_fail(set != NULL, IMAP_ERROR);
2724 g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
2726 r = imap_threaded_copy(session->folder, set, destfolder);
2727 if (r != MAILIMAP_NO_ERROR) {
2732 return IMAP_SUCCESS;
2735 static gint imap_cmd_store(IMAPSession *session, struct mailimap_set * set,
2736 IMAPFlags flags, int do_add)
2739 struct mailimap_flag_list * flag_list;
2740 struct mailimap_store_att_flags * store_att_flags;
2742 flag_list = imap_flag_to_lep(flags);
2746 mailimap_store_att_flags_new_add_flags_silent(flag_list);
2749 mailimap_store_att_flags_new_remove_flags_silent(flag_list);
2751 r = imap_threaded_store(session->folder, set, store_att_flags);
2752 mailimap_store_att_flags_free(store_att_flags);
2753 if (r != MAILIMAP_NO_ERROR) {
2758 return IMAP_SUCCESS;
2761 static gint imap_cmd_expunge(IMAPSession *session)
2765 if (prefs_common.work_offline &&
2766 !inc_offline_should_override(
2767 _("Sylpheed-Claws needs network access in order "
2768 "to access the IMAP server."))) {
2772 r = imap_threaded_expunge(session->folder);
2773 if (r != MAILIMAP_NO_ERROR) {
2778 return IMAP_SUCCESS;
2781 static void imap_path_separator_subst(gchar *str, gchar separator)
2784 gboolean in_escape = FALSE;
2786 if (!separator || separator == '/') return;
2788 for (p = str; *p != '\0'; p++) {
2789 if (*p == '/' && !in_escape)
2791 else if (*p == '&' && *(p + 1) != '-' && !in_escape)
2793 else if (*p == '-' && in_escape)
2798 static gchar *imap_modified_utf7_to_utf8(const gchar *mutf7_str)
2800 static iconv_t cd = (iconv_t)-1;
2801 static gboolean iconv_ok = TRUE;
2804 size_t norm_utf7_len;
2806 gchar *to_str, *to_p;
2808 gboolean in_escape = FALSE;
2810 if (!iconv_ok) return g_strdup(mutf7_str);
2812 if (cd == (iconv_t)-1) {
2813 cd = iconv_open(CS_INTERNAL, CS_UTF_7);
2814 if (cd == (iconv_t)-1) {
2815 g_warning("iconv cannot convert UTF-7 to %s\n",
2818 return g_strdup(mutf7_str);
2822 /* modified UTF-7 to normal UTF-7 conversion */
2823 norm_utf7 = g_string_new(NULL);
2825 for (p = mutf7_str; *p != '\0'; p++) {
2826 /* replace: '&' -> '+',
2828 escaped ',' -> '/' */
2829 if (!in_escape && *p == '&') {
2830 if (*(p + 1) != '-') {
2831 g_string_append_c(norm_utf7, '+');
2834 g_string_append_c(norm_utf7, '&');
2837 } else if (in_escape && *p == ',') {
2838 g_string_append_c(norm_utf7, '/');
2839 } else if (in_escape && *p == '-') {
2840 g_string_append_c(norm_utf7, '-');
2843 g_string_append_c(norm_utf7, *p);
2847 norm_utf7_p = norm_utf7->str;
2848 norm_utf7_len = norm_utf7->len;
2849 to_len = strlen(mutf7_str) * 5;
2850 to_p = to_str = g_malloc(to_len + 1);
2852 if (iconv(cd, (ICONV_CONST gchar **)&norm_utf7_p, &norm_utf7_len,
2853 &to_p, &to_len) == -1) {
2854 g_warning(_("iconv cannot convert UTF-7 to %s\n"),
2855 conv_get_locale_charset_str());
2856 g_string_free(norm_utf7, TRUE);
2858 return g_strdup(mutf7_str);
2861 /* second iconv() call for flushing */
2862 iconv(cd, NULL, NULL, &to_p, &to_len);
2863 g_string_free(norm_utf7, TRUE);
2869 static gchar *imap_utf8_to_modified_utf7(const gchar *from)
2871 static iconv_t cd = (iconv_t)-1;
2872 static gboolean iconv_ok = TRUE;
2873 gchar *norm_utf7, *norm_utf7_p;
2874 size_t from_len, norm_utf7_len;
2876 gchar *from_tmp, *to, *p;
2877 gboolean in_escape = FALSE;
2879 if (!iconv_ok) return g_strdup(from);
2881 if (cd == (iconv_t)-1) {
2882 cd = iconv_open(CS_UTF_7, CS_INTERNAL);
2883 if (cd == (iconv_t)-1) {
2884 g_warning(_("iconv cannot convert %s to UTF-7\n"),
2887 return g_strdup(from);
2891 /* UTF-8 to normal UTF-7 conversion */
2892 Xstrdup_a(from_tmp, from, return g_strdup(from));
2893 from_len = strlen(from);
2894 norm_utf7_len = from_len * 5;
2895 Xalloca(norm_utf7, norm_utf7_len + 1, return g_strdup(from));
2896 norm_utf7_p = norm_utf7;
2898 #define IS_PRINT(ch) (isprint(ch) && IS_ASCII(ch))
2900 while (from_len > 0) {
2901 if (*from_tmp == '+') {
2902 *norm_utf7_p++ = '+';
2903 *norm_utf7_p++ = '-';
2907 } else if (IS_PRINT(*(guchar *)from_tmp)) {
2908 /* printable ascii char */
2909 *norm_utf7_p = *from_tmp;
2915 size_t conv_len = 0;
2917 /* unprintable char: convert to UTF-7 */
2919 while (!IS_PRINT(*(guchar *)p) && conv_len < from_len) {
2920 conv_len += g_utf8_skip[*(guchar *)p];
2921 p += g_utf8_skip[*(guchar *)p];
2924 from_len -= conv_len;
2925 if (iconv(cd, (ICONV_CONST gchar **)&from_tmp,
2927 &norm_utf7_p, &norm_utf7_len) == -1) {
2928 g_warning(_("iconv cannot convert UTF-8 to UTF-7\n"));
2929 return g_strdup(from);
2932 /* second iconv() call for flushing */
2933 iconv(cd, NULL, NULL, &norm_utf7_p, &norm_utf7_len);
2939 *norm_utf7_p = '\0';
2940 to_str = g_string_new(NULL);
2941 for (p = norm_utf7; p < norm_utf7_p; p++) {
2942 /* replace: '&' -> "&-",
2945 BASE64 '/' -> ',' */
2946 if (!in_escape && *p == '&') {
2947 g_string_append(to_str, "&-");
2948 } else if (!in_escape && *p == '+') {
2949 if (*(p + 1) == '-') {
2950 g_string_append_c(to_str, '+');
2953 g_string_append_c(to_str, '&');
2956 } else if (in_escape && *p == '/') {
2957 g_string_append_c(to_str, ',');
2958 } else if (in_escape && *p == '-') {
2959 g_string_append_c(to_str, '-');
2962 g_string_append_c(to_str, *p);
2968 g_string_append_c(to_str, '-');
2972 g_string_free(to_str, FALSE);
2977 static gboolean imap_rename_folder_func(GNode *node, gpointer data)
2979 FolderItem *item = node->data;
2980 gchar **paths = data;
2981 const gchar *oldpath = paths[0];
2982 const gchar *newpath = paths[1];
2984 gchar *new_itempath;
2987 oldpathlen = strlen(oldpath);
2988 if (strncmp(oldpath, item->path, oldpathlen) != 0) {
2989 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
2993 base = item->path + oldpathlen;
2994 while (*base == G_DIR_SEPARATOR) base++;
2996 new_itempath = g_strdup(newpath);
2998 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
3001 item->path = new_itempath;
3006 typedef struct _get_list_uid_data {
3008 IMAPSession *session;
3009 IMAPFolderItem *item;
3010 GSList **msgnum_list;
3012 } get_list_uid_data;
3014 static void *get_list_of_uids_thread(void *data)
3016 get_list_uid_data *stuff = (get_list_uid_data *)data;
3017 Folder *folder = stuff->folder;
3018 IMAPFolderItem *item = stuff->item;
3019 GSList **msgnum_list = stuff->msgnum_list;
3020 gint ok, nummsgs = 0, lastuid_old;
3021 IMAPSession *session;
3022 GSList *uidlist, *elem;
3023 struct mailimap_set * set;
3024 clist * lep_uidlist;
3027 session = stuff->session;
3028 if (session == NULL) {
3030 return GINT_TO_POINTER(-1);
3032 /* no session locking here, it's already locked by caller */
3033 ok = imap_select(session, IMAP_FOLDER(folder), item->item.path,
3034 NULL, NULL, NULL, NULL, TRUE);
3035 if (ok != IMAP_SUCCESS) {
3037 return GINT_TO_POINTER(-1);
3042 set = mailimap_set_new_interval(item->lastuid + 1, 0);
3044 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SIMPLE, set,
3046 mailimap_set_free(set);
3048 if (r == MAILIMAP_NO_ERROR) {
3049 GSList * fetchuid_list;
3052 imap_uid_list_from_lep(lep_uidlist);
3053 mailimap_search_result_free(lep_uidlist);
3055 uidlist = g_slist_concat(fetchuid_list, uidlist);
3058 GSList * fetchuid_list;
3059 carray * lep_uidtab;
3061 r = imap_threaded_fetch_uid(folder, item->lastuid + 1,
3063 if (r == MAILIMAP_NO_ERROR) {
3065 imap_uid_list_from_lep_tab(lep_uidtab);
3066 imap_fetch_uid_list_free(lep_uidtab);
3067 uidlist = g_slist_concat(fetchuid_list, uidlist);
3071 lastuid_old = item->lastuid;
3072 *msgnum_list = g_slist_copy(item->uid_list);
3073 nummsgs = g_slist_length(*msgnum_list);
3074 debug_print("Got %d uids from cache\n", g_slist_length(item->uid_list));
3076 for (elem = uidlist; elem != NULL; elem = g_slist_next(elem)) {
3079 msgnum = GPOINTER_TO_INT(elem->data);
3080 if (msgnum > lastuid_old) {
3081 *msgnum_list = g_slist_prepend(*msgnum_list, GINT_TO_POINTER(msgnum));
3082 item->uid_list = g_slist_prepend(item->uid_list, GINT_TO_POINTER(msgnum));
3085 if(msgnum > item->lastuid)
3086 item->lastuid = msgnum;
3089 g_slist_free(uidlist);
3091 return GINT_TO_POINTER(nummsgs);
3094 static gint get_list_of_uids(IMAPSession *session, Folder *folder, IMAPFolderItem *item, GSList **msgnum_list)
3097 get_list_uid_data *data = g_new0(get_list_uid_data, 1);
3099 data->folder = folder;
3101 data->msgnum_list = msgnum_list;
3102 data->session = session;
3103 if (prefs_common.work_offline &&
3104 !inc_offline_should_override(
3105 _("Sylpheed-Claws needs network access in order "
3106 "to access the IMAP server."))) {
3111 result = GPOINTER_TO_INT(get_list_of_uids_thread(data));
3117 gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list, gboolean *old_uids_valid)
3119 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3120 IMAPSession *session;
3121 gint ok, nummsgs = 0, exists;
3122 guint32 uid_next = 0, uid_val = 0;
3123 GSList *uidlist = NULL;
3125 gboolean selected_folder;
3127 debug_print("get_num_list\n");
3129 g_return_val_if_fail(folder != NULL, -1);
3130 g_return_val_if_fail(item != NULL, -1);
3131 g_return_val_if_fail(item->item.path != NULL, -1);
3132 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3133 g_return_val_if_fail(folder->account != NULL, -1);
3135 session = imap_session_get(folder);
3136 g_return_val_if_fail(session != NULL, -1);
3139 if (FOLDER_ITEM(item)->path)
3140 statusbar_print_all(_("Scanning folder %s%c%s ..."),
3141 FOLDER_ITEM(item)->folder->name,
3143 FOLDER_ITEM(item)->path);
3145 statusbar_print_all(_("Scanning folder %s ..."),
3146 FOLDER_ITEM(item)->folder->name);
3148 selected_folder = (session->mbox != NULL) &&
3149 (!strcmp(session->mbox, item->item.path));
3150 if (selected_folder && time(NULL) - item->use_cache < 2) {
3151 ok = imap_cmd_noop(session);
3152 if (ok != IMAP_SUCCESS) {
3153 debug_print("disconnected!\n");
3154 session = imap_reconnect_if_possible(folder, session);
3155 if (session == NULL) {
3156 statusbar_pop_all();
3161 exists = session->exists;
3163 uid_next = item->c_uid_next;
3164 uid_val = item->c_uid_validity;
3165 *old_uids_valid = TRUE;
3167 if (item->use_cache && time(NULL) - item->use_cache < 2) {
3168 exists = item->c_messages;
3169 uid_next = item->c_uid_next;
3170 uid_val = item->c_uid_validity;
3172 debug_print("using cache %d %d %d\n", exists, uid_next, uid_val);
3174 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path, item,
3175 &exists, &uid_next, &uid_val, NULL, FALSE);
3177 item->item.last_num = uid_next - 1;
3179 item->use_cache = (time_t)0;
3180 if (ok != IMAP_SUCCESS) {
3181 statusbar_pop_all();
3185 if(item->item.mtime == uid_val)
3186 *old_uids_valid = TRUE;
3188 *old_uids_valid = FALSE;
3190 debug_print("Freeing imap uid cache (%d != %d)\n",
3191 (int)item->item.mtime, uid_val);
3193 g_slist_free(item->uid_list);
3194 item->uid_list = NULL;
3196 item->item.mtime = uid_val;
3198 imap_delete_all_cached_messages((FolderItem *)item);
3202 /* If old uid_next matches new uid_next we can be sure no message
3203 was added to the folder */
3204 debug_print("uid_next is %d and item->uid_next %d \n",
3205 uid_next, item->uid_next);
3206 if (uid_next == item->uid_next) {
3207 nummsgs = g_slist_length(item->uid_list);
3209 /* If number of messages is still the same we
3210 know our caches message numbers are still valid,
3211 otherwise if the number of messages has decrease
3212 we discard our cache to start a new scan to find
3213 out which numbers have been removed */
3214 if (exists == nummsgs) {
3215 debug_print("exists == nummsgs\n");
3216 *msgnum_list = g_slist_copy(item->uid_list);
3217 statusbar_pop_all();
3220 } else if (exists < nummsgs) {
3221 debug_print("Freeing imap uid cache");
3223 g_slist_free(item->uid_list);
3224 item->uid_list = NULL;
3229 *msgnum_list = NULL;
3230 statusbar_pop_all();
3235 nummsgs = get_list_of_uids(session, folder, item, &uidlist);
3238 statusbar_pop_all();
3243 if (nummsgs != exists) {
3244 /* Cache contains more messages then folder, we have cached
3245 an old UID of a message that was removed and new messages
3246 have been added too, otherwise the uid_next check would
3248 debug_print("Freeing imap uid cache");
3250 g_slist_free(item->uid_list);
3251 item->uid_list = NULL;
3253 g_slist_free(*msgnum_list);
3255 nummsgs = get_list_of_uids(session, folder, item, &uidlist);
3258 *msgnum_list = uidlist;
3260 dir = folder_item_get_path((FolderItem *)item);
3261 debug_print("removing old messages from %s\n", dir);
3262 remove_numbered_files_not_in_list(dir, *msgnum_list);
3265 item->uid_next = uid_next;
3267 debug_print("get_num_list - ok - %i\n", nummsgs);
3268 statusbar_pop_all();
3273 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item)
3278 flags.perm_flags = MSG_NEW|MSG_UNREAD;
3279 flags.tmp_flags = 0;
3281 g_return_val_if_fail(item != NULL, NULL);
3282 g_return_val_if_fail(file != NULL, NULL);
3284 if (folder_has_parent_of_type(item, F_QUEUE)) {
3285 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
3286 } else if (folder_has_parent_of_type(item, F_DRAFT)) {
3287 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
3290 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3291 if (!msginfo) return NULL;
3293 msginfo->plaintext_file = g_strdup(file);
3294 msginfo->folder = item;
3299 GSList *imap_get_msginfos(Folder *folder, FolderItem *item,
3300 GSList *msgnum_list)
3302 IMAPSession *session;
3303 MsgInfoList *ret = NULL;
3306 debug_print("get_msginfos\n");
3308 g_return_val_if_fail(folder != NULL, NULL);
3309 g_return_val_if_fail(item != NULL, NULL);
3310 g_return_val_if_fail(msgnum_list != NULL, NULL);
3312 session = imap_session_get(folder);
3313 g_return_val_if_fail(session != NULL, NULL);
3315 debug_print("IMAP getting msginfos\n");
3316 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3317 NULL, NULL, NULL, NULL, FALSE);
3318 if (ok != IMAP_SUCCESS) {
3322 if (!(folder_has_parent_of_type(item, F_DRAFT) ||
3323 folder_has_parent_of_type(item, F_QUEUE))) {
3324 ret = g_slist_concat(ret,
3325 imap_get_uncached_messages(session, item,
3328 MsgNumberList *sorted_list, *elem, *llast = NULL;
3329 gint startnum, lastnum;
3331 sorted_list = g_slist_sort(g_slist_copy(msgnum_list), g_int_compare);
3333 startnum = lastnum = GPOINTER_TO_INT(sorted_list->data);
3335 llast = g_slist_last(ret);
3336 for (elem = sorted_list;; elem = g_slist_next(elem)) {
3340 num = GPOINTER_TO_INT(elem->data);
3342 if (num > lastnum + 1 || elem == NULL) {
3344 for (i = startnum; i <= lastnum; ++i) {
3347 file = imap_fetch_msg(folder, item, i);
3349 MsgInfo *msginfo = imap_parse_msg(file, item);
3350 if (msginfo != NULL) {
3351 msginfo->msgnum = i;
3353 llast = ret = g_slist_append(ret, msginfo);
3355 llast = g_slist_append(llast, msginfo);
3356 llast = llast->next;
3361 session_set_access_time(SESSION(session));
3372 g_slist_free(sorted_list);
3378 MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid)
3380 MsgInfo *msginfo = NULL;
3381 MsgInfoList *msginfolist;
3382 MsgNumberList numlist;
3384 numlist.next = NULL;
3385 numlist.data = GINT_TO_POINTER(uid);
3387 msginfolist = imap_get_msginfos(folder, item, &numlist);
3388 if (msginfolist != NULL) {
3389 msginfo = msginfolist->data;
3390 g_slist_free(msginfolist);
3396 gboolean imap_scan_required(Folder *folder, FolderItem *_item)
3398 IMAPSession *session;
3399 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3400 gint ok, exists = 0, unseen = 0;
3401 guint32 uid_next, uid_val;
3402 gboolean selected_folder;
3404 g_return_val_if_fail(folder != NULL, FALSE);
3405 g_return_val_if_fail(item != NULL, FALSE);
3406 g_return_val_if_fail(item->item.folder != NULL, FALSE);
3407 g_return_val_if_fail(FOLDER_CLASS(item->item.folder) == &imap_class, FALSE);
3409 if (item->item.path == NULL)
3412 session = imap_session_get(folder);
3413 g_return_val_if_fail(session != NULL, FALSE);
3415 selected_folder = (session->mbox != NULL) &&
3416 (!strcmp(session->mbox, item->item.path));
3417 if (selected_folder && time(NULL) - item->use_cache < 2) {
3418 ok = imap_cmd_noop(session);
3419 if (ok != IMAP_SUCCESS) {
3420 debug_print("disconnected!\n");
3421 session = imap_reconnect_if_possible(folder, session);
3422 if (session == NULL)
3427 if (session->folder_content_changed
3428 || session->exists != item->item.total_msgs) {
3433 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path, IMAP_FOLDER_ITEM(item),
3434 &exists, &uid_next, &uid_val, &unseen, FALSE);
3435 if (ok != IMAP_SUCCESS) {
3440 item->use_cache = time(NULL);
3441 item->c_messages = exists;
3442 item->c_uid_next = uid_next;
3443 item->c_uid_validity = uid_val;
3444 item->c_unseen = unseen;
3445 item->item.last_num = uid_next - 1;
3446 debug_print("uidnext %d, item->uid_next %d, exists %d, item->item.total_msgs %d\n",
3447 uid_next, item->uid_next, exists, item->item.total_msgs);
3448 if ((uid_next != item->uid_next) || (exists != item->item.total_msgs)
3449 || unseen != item->item.unread_msgs || uid_val != item->item.mtime) {
3458 void imap_change_flags(Folder *folder, FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
3460 IMAPSession *session;
3461 IMAPFlags flags_set = 0, flags_unset = 0;
3462 gint ok = IMAP_SUCCESS;
3463 MsgNumberList numlist;
3464 hashtable_data *ht_data = NULL;
3466 g_return_if_fail(folder != NULL);
3467 g_return_if_fail(folder->klass == &imap_class);
3468 g_return_if_fail(item != NULL);
3469 g_return_if_fail(item->folder == folder);
3470 g_return_if_fail(msginfo != NULL);
3471 g_return_if_fail(msginfo->folder == item);
3473 session = imap_session_get(folder);
3478 if (!MSG_IS_MARKED(msginfo->flags) && (newflags & MSG_MARKED))
3479 flags_set |= IMAP_FLAG_FLAGGED;
3480 if ( MSG_IS_MARKED(msginfo->flags) && !(newflags & MSG_MARKED))
3481 flags_unset |= IMAP_FLAG_FLAGGED;
3483 if (!MSG_IS_UNREAD(msginfo->flags) && (newflags & MSG_UNREAD))
3484 flags_unset |= IMAP_FLAG_SEEN;
3485 if ( MSG_IS_UNREAD(msginfo->flags) && !(newflags & MSG_UNREAD))
3486 flags_set |= IMAP_FLAG_SEEN;
3488 if (!MSG_IS_REPLIED(msginfo->flags) && (newflags & MSG_REPLIED))
3489 flags_set |= IMAP_FLAG_ANSWERED;
3490 if ( MSG_IS_REPLIED(msginfo->flags) && !(newflags & MSG_REPLIED))
3491 flags_unset |= IMAP_FLAG_ANSWERED;
3493 if (!MSG_IS_DELETED(msginfo->flags) && (newflags & MSG_DELETED))
3494 flags_set |= IMAP_FLAG_DELETED;
3495 if ( MSG_IS_DELETED(msginfo->flags) && !(newflags & MSG_DELETED))
3496 flags_unset |= IMAP_FLAG_DELETED;
3498 if (!flags_set && !flags_unset) {
3499 /* the changed flags were not translatable to IMAP-speak.
3500 * like MSG_POSTFILTERED, so just apply. */
3501 msginfo->flags.perm_flags = newflags;
3506 if ((ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
3507 NULL, NULL, NULL, NULL, FALSE)) != IMAP_SUCCESS) {
3511 numlist.next = NULL;
3512 numlist.data = GINT_TO_POINTER(msginfo->msgnum);
3514 if (IMAP_FOLDER_ITEM(item)->batching) {
3515 /* instead of performing an UID STORE command for each message change,
3516 * as a lot of them can change "together", we just fill in hashtables
3517 * and defer the treatment so that we're able to send only one
3520 debug_print("IMAP batch mode on, deferring flags change\n");
3522 ht_data = g_hash_table_lookup(IMAP_FOLDER_ITEM(item)->flags_set_table,
3523 GINT_TO_POINTER(flags_set));
3524 if (ht_data == NULL) {
3525 ht_data = g_new0(hashtable_data, 1);
3526 ht_data->session = session;
3527 ht_data->item = IMAP_FOLDER_ITEM(item);
3528 g_hash_table_insert(IMAP_FOLDER_ITEM(item)->flags_set_table,
3529 GINT_TO_POINTER(flags_set), ht_data);
3531 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
3532 ht_data->msglist = g_slist_prepend(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum));
3535 ht_data = g_hash_table_lookup(IMAP_FOLDER_ITEM(item)->flags_unset_table,
3536 GINT_TO_POINTER(flags_unset));
3537 if (ht_data == NULL) {
3538 ht_data = g_new0(hashtable_data, 1);
3539 ht_data->session = session;
3540 ht_data->item = IMAP_FOLDER_ITEM(item);
3541 g_hash_table_insert(IMAP_FOLDER_ITEM(item)->flags_unset_table,
3542 GINT_TO_POINTER(flags_unset), ht_data);
3544 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
3545 ht_data->msglist = g_slist_prepend(ht_data->msglist,
3546 GINT_TO_POINTER(msginfo->msgnum));
3549 debug_print("IMAP changing flags\n");
3551 ok = imap_set_message_flags(session, &numlist, flags_set, TRUE);
3552 if (ok != IMAP_SUCCESS) {
3559 ok = imap_set_message_flags(session, &numlist, flags_unset, FALSE);
3560 if (ok != IMAP_SUCCESS) {
3566 msginfo->flags.perm_flags = newflags;
3571 static gint imap_remove_msg(Folder *folder, FolderItem *item, gint uid)
3574 IMAPSession *session;
3576 MsgNumberList numlist;
3578 g_return_val_if_fail(folder != NULL, -1);
3579 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3580 g_return_val_if_fail(item != NULL, -1);
3582 session = imap_session_get(folder);
3583 if (!session) return -1;
3585 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3586 NULL, NULL, NULL, NULL, FALSE);
3587 if (ok != IMAP_SUCCESS) {
3591 numlist.next = NULL;
3592 numlist.data = GINT_TO_POINTER(uid);
3594 ok = imap_set_message_flags
3595 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
3596 &numlist, IMAP_FLAG_DELETED, TRUE);
3597 if (ok != IMAP_SUCCESS) {
3598 log_warning(_("can't set deleted flags: %d\n"), uid);
3603 if (!session->uidplus) {
3604 ok = imap_cmd_expunge(session);
3608 uidstr = g_strdup_printf("%u", uid);
3609 ok = imap_cmd_expunge(session);
3612 if (ok != IMAP_SUCCESS) {
3613 log_warning(_("can't expunge\n"));
3618 IMAP_FOLDER_ITEM(item)->uid_list = g_slist_remove(
3619 IMAP_FOLDER_ITEM(item)->uid_list, numlist.data);
3620 dir = folder_item_get_path(item);
3621 if (is_dir_exist(dir))
3622 remove_numbered_files(dir, uid, uid);
3625 return IMAP_SUCCESS;
3628 static gint compare_msginfo(gconstpointer a, gconstpointer b)
3630 return ((MsgInfo *)a)->msgnum - ((MsgInfo *)b)->msgnum;
3633 static guint gslist_find_next_num(MsgNumberList **list, guint num)
3637 g_return_val_if_fail(list != NULL, -1);
3639 for (elem = *list; elem != NULL; elem = g_slist_next(elem))
3640 if (GPOINTER_TO_INT(elem->data) >= num)
3643 return elem != NULL ? GPOINTER_TO_INT(elem->data) : (gint)-1;
3647 * NEW and DELETED flags are not syncronized
3648 * - The NEW/RECENT flags in IMAP folders can not really be directly
3649 * modified by Sylpheed
3650 * - The DELETE/DELETED flag in IMAP and Sylpheed don't have the same
3651 * meaning, in IMAP it always removes the messages from the FolderItem
3652 * in Sylpheed it can mean to move the message to trash
3655 typedef struct _get_flags_data {
3658 MsgInfoList *msginfo_list;
3659 GRelation *msgflags;
3660 gboolean full_search;
3664 static /*gint*/ void *imap_get_flags_thread(void *data)
3666 get_flags_data *stuff = (get_flags_data *)data;
3667 Folder *folder = stuff->folder;
3668 FolderItem *item = stuff->item;
3669 MsgInfoList *msginfo_list = stuff->msginfo_list;
3670 GRelation *msgflags = stuff->msgflags;
3671 gboolean full_search = stuff->full_search;
3672 IMAPSession *session;
3673 GSList *sorted_list = NULL;
3674 GSList *unseen = NULL, *answered = NULL, *flagged = NULL, *deleted = NULL;
3675 GSList *p_unseen, *p_answered, *p_flagged, *p_deleted;
3677 GSList *seq_list, *cur;
3678 gboolean reverse_seen = FALSE;
3681 gint exists_cnt, unseen_cnt;
3682 gboolean selected_folder;
3684 if (folder == NULL || item == NULL) {
3686 return GINT_TO_POINTER(-1);
3689 session = imap_session_get(folder);
3690 if (session == NULL) {
3692 return GINT_TO_POINTER(-1);
3695 selected_folder = (session->mbox != NULL) &&
3696 (!strcmp(session->mbox, item->path));
3698 if (!selected_folder) {
3699 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3700 &exists_cnt, NULL, &unseen_cnt, NULL, TRUE);
3701 if (ok != IMAP_SUCCESS) {
3704 return GINT_TO_POINTER(-1);
3707 if (unseen_cnt > exists_cnt / 2)
3708 reverse_seen = TRUE;
3711 if (item->unread_msgs > item->total_msgs / 2)
3712 reverse_seen = TRUE;
3715 cmd_buf = g_string_new(NULL);
3717 sorted_list = g_slist_sort(g_slist_copy(msginfo_list), compare_msginfo);
3719 seq_list = imap_get_lep_set_from_msglist(msginfo_list);
3721 struct mailimap_set * set;
3722 set = mailimap_set_new_interval(1, 0);
3723 seq_list = g_slist_append(NULL, set);
3726 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
3727 struct mailimap_set * imapset;
3728 clist * lep_uidlist;
3731 imapset = cur->data;
3733 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SEEN,
3734 full_search ? NULL:imapset, &lep_uidlist);
3737 r = imap_threaded_search(folder,
3738 IMAP_SEARCH_TYPE_UNSEEN,
3739 full_search ? NULL:imapset, &lep_uidlist);
3741 if (r == MAILIMAP_NO_ERROR) {
3744 uidlist = imap_uid_list_from_lep(lep_uidlist);
3745 mailimap_search_result_free(lep_uidlist);
3747 unseen = g_slist_concat(unseen, uidlist);
3750 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_FLAGGED,
3751 full_search ? NULL:imapset, &lep_uidlist);
3752 if (r == MAILIMAP_NO_ERROR) {
3755 uidlist = imap_uid_list_from_lep(lep_uidlist);
3756 mailimap_search_result_free(lep_uidlist);
3758 flagged = g_slist_concat(flagged, uidlist);
3761 if (item->opened || item->processing_pending || item == folder->inbox) {
3762 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_ANSWERED,
3763 full_search ? NULL:imapset, &lep_uidlist);
3764 if (r == MAILIMAP_NO_ERROR) {
3767 uidlist = imap_uid_list_from_lep(lep_uidlist);
3768 mailimap_search_result_free(lep_uidlist);
3770 answered = g_slist_concat(answered, uidlist);
3773 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_DELETED,
3774 full_search ? NULL:imapset, &lep_uidlist);
3775 if (r == MAILIMAP_NO_ERROR) {
3778 uidlist = imap_uid_list_from_lep(lep_uidlist);
3779 mailimap_search_result_free(lep_uidlist);
3781 deleted = g_slist_concat(deleted, uidlist);
3787 p_answered = answered;
3788 p_flagged = flagged;
3789 p_deleted = deleted;
3791 for (elem = sorted_list; elem != NULL; elem = g_slist_next(elem)) {
3796 msginfo = (MsgInfo *) elem->data;
3797 flags = msginfo->flags.perm_flags;
3798 wasnew = (flags & MSG_NEW);
3799 if (item->opened || item->processing_pending || item == folder->inbox) {
3800 flags &= ~((reverse_seen ? 0 : MSG_UNREAD | MSG_NEW) | MSG_REPLIED | MSG_MARKED);
3802 flags &= ~((reverse_seen ? 0 : MSG_UNREAD | MSG_NEW | MSG_MARKED));
3805 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
3806 if (gslist_find_next_num(&p_unseen, msginfo->msgnum) == msginfo->msgnum) {
3807 if (!reverse_seen) {
3808 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
3810 flags &= ~(MSG_UNREAD | MSG_NEW);
3814 if (gslist_find_next_num(&p_flagged, msginfo->msgnum) == msginfo->msgnum)
3815 flags |= MSG_MARKED;
3817 flags &= ~MSG_MARKED;
3819 if (item->opened || item->processing_pending || item == folder->inbox) {
3820 if (gslist_find_next_num(&p_answered, msginfo->msgnum) == msginfo->msgnum)
3821 flags |= MSG_REPLIED;
3823 flags &= ~MSG_REPLIED;
3824 if (gslist_find_next_num(&p_deleted, msginfo->msgnum) == msginfo->msgnum)
3825 flags |= MSG_DELETED;
3827 flags &= ~MSG_DELETED;
3829 g_relation_insert(msgflags, msginfo, GINT_TO_POINTER(flags));
3832 imap_lep_set_free(seq_list);
3833 g_slist_free(flagged);
3834 g_slist_free(deleted);
3835 g_slist_free(answered);
3836 g_slist_free(unseen);
3837 g_slist_free(sorted_list);
3838 g_string_free(cmd_buf, TRUE);
3842 return GINT_TO_POINTER(0);
3845 static gint imap_get_flags(Folder *folder, FolderItem *item,
3846 MsgInfoList *msginfo_list, GRelation *msgflags)
3849 get_flags_data *data = g_new0(get_flags_data, 1);
3851 data->folder = folder;
3853 data->msginfo_list = msginfo_list;
3854 data->msgflags = msgflags;
3855 data->full_search = FALSE;
3857 GSList *tmp = NULL, *cur;
3859 if (prefs_common.work_offline &&
3860 !inc_offline_should_override(
3861 _("Sylpheed-Claws needs network access in order "
3862 "to access the IMAP server."))) {
3867 tmp = folder_item_get_msg_list(item);
3869 if (g_slist_length(tmp) <= g_slist_length(msginfo_list))
3870 data->full_search = TRUE;
3872 for (cur = tmp; cur; cur = cur->next)
3873 procmsg_msginfo_free((MsgInfo *)cur->data);
3877 result = GPOINTER_TO_INT(imap_get_flags_thread(data));
3884 static gboolean process_flags(gpointer key, gpointer value, gpointer user_data)
3886 gboolean flags_set = GPOINTER_TO_INT(user_data);
3887 gint flags_value = GPOINTER_TO_INT(key);
3888 hashtable_data *data = (hashtable_data *)value;
3889 IMAPFolderItem *_item = data->item;
3890 FolderItem *item = (FolderItem *)_item;
3891 gint ok = IMAP_ERROR;
3892 IMAPSession *session = imap_session_get(item->folder);
3894 data->msglist = g_slist_reverse(data->msglist);
3896 debug_print("IMAP %ssetting flags to %d for %d messages\n",
3899 g_slist_length(data->msglist));
3903 ok = imap_select(session, IMAP_FOLDER(item->folder), item->path,
3904 NULL, NULL, NULL, NULL, FALSE);
3906 if (ok == IMAP_SUCCESS) {
3907 imap_set_message_flags(data->session, data->msglist, flags_value, flags_set);
3909 g_warning("can't select mailbox %s\n", item->path);
3913 g_slist_free(data->msglist);
3918 static void process_hashtable(IMAPFolderItem *item)
3920 if (item->flags_set_table) {
3921 g_hash_table_foreach_remove(item->flags_set_table, process_flags, GINT_TO_POINTER(TRUE));
3922 g_hash_table_destroy(item->flags_set_table);
3923 item->flags_set_table = NULL;
3925 if (item->flags_unset_table) {
3926 g_hash_table_foreach_remove(item->flags_unset_table, process_flags, GINT_TO_POINTER(FALSE));
3927 g_hash_table_destroy(item->flags_unset_table);
3928 item->flags_unset_table = NULL;
3932 static void imap_set_batch (Folder *folder, FolderItem *_item, gboolean batch)
3934 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3936 g_return_if_fail(item != NULL);
3938 if (item->batching == batch)
3942 item->batching = TRUE;
3943 debug_print("IMAP switching to batch mode\n");
3944 if (!item->flags_set_table) {
3945 item->flags_set_table = g_hash_table_new(NULL, g_direct_equal);
3947 if (!item->flags_unset_table) {
3948 item->flags_unset_table = g_hash_table_new(NULL, g_direct_equal);
3951 debug_print("IMAP switching away from batch mode\n");
3953 process_hashtable(item);
3954 item->batching = FALSE;
3960 /* data types conversion libetpan <-> sylpheed */
3964 #define ETPAN_IMAP_MB_MARKED 1
3965 #define ETPAN_IMAP_MB_UNMARKED 2
3966 #define ETPAN_IMAP_MB_NOSELECT 4
3967 #define ETPAN_IMAP_MB_NOINFERIORS 8
3969 static int imap_flags_to_flags(struct mailimap_mbx_list_flags * imap_flags)
3975 if (imap_flags->mbf_type == MAILIMAP_MBX_LIST_FLAGS_SFLAG) {
3976 switch (imap_flags->mbf_sflag) {
3977 case MAILIMAP_MBX_LIST_SFLAG_MARKED:
3978 flags |= ETPAN_IMAP_MB_MARKED;
3980 case MAILIMAP_MBX_LIST_SFLAG_NOSELECT:
3981 flags |= ETPAN_IMAP_MB_NOSELECT;
3983 case MAILIMAP_MBX_LIST_SFLAG_UNMARKED:
3984 flags |= ETPAN_IMAP_MB_UNMARKED;
3989 for(cur = clist_begin(imap_flags->mbf_oflags) ; cur != NULL ;
3990 cur = clist_next(cur)) {
3991 struct mailimap_mbx_list_oflag * oflag;
3993 oflag = clist_content(cur);
3995 switch (oflag->of_type) {
3996 case MAILIMAP_MBX_LIST_OFLAG_NOINFERIORS:
3997 flags |= ETPAN_IMAP_MB_NOINFERIORS;
4005 static GSList * imap_list_from_lep(IMAPFolder * folder,
4006 clist * list, const gchar * real_path, gboolean all)
4009 GSList * item_list = NULL, *llast = NULL;
4011 for(iter = clist_begin(list) ; iter != NULL ;
4012 iter = clist_next(iter)) {
4013 struct mailimap_mailbox_list * mb;
4021 FolderItem *new_item;
4023 mb = clist_content(iter);
4029 if (mb->mb_flag != NULL)
4030 flags = imap_flags_to_flags(mb->mb_flag);
4032 delimiter = mb->mb_delimiter;
4035 dup_name = strdup(name);
4036 if (delimiter != '\0')
4037 subst_char(dup_name, delimiter, '/');
4039 base = g_path_get_basename(dup_name);
4040 if (base[0] == '.') {
4046 if (!all && strcmp(dup_name, real_path) == 0) {
4052 if (!all && dup_name[strlen(dup_name)-1] == '/') {
4058 loc_name = imap_modified_utf7_to_utf8(base);
4059 loc_path = imap_modified_utf7_to_utf8(dup_name);
4061 new_item = folder_item_new(FOLDER(folder), loc_name, loc_path);
4062 if ((flags & ETPAN_IMAP_MB_NOINFERIORS) != 0)
4063 new_item->no_sub = TRUE;
4064 if (strcmp(dup_name, "INBOX") != 0 &&
4065 ((flags & ETPAN_IMAP_MB_NOSELECT) != 0))
4066 new_item->no_select = TRUE;
4068 if (item_list == NULL)
4069 llast = item_list = g_slist_append(item_list, new_item);
4071 llast = g_slist_append(llast, new_item);
4072 llast = llast->next;
4074 debug_print("folder '%s' found.\n", loc_path);
4085 static GSList * imap_get_lep_set_from_numlist(MsgNumberList *numlist)
4087 GSList *sorted_list, *cur;
4088 guint first, last, next;
4089 GSList *ret_list = NULL, *llast = NULL;
4091 struct mailimap_set * current_set;
4092 unsigned int item_count;
4094 if (numlist == NULL)
4098 current_set = mailimap_set_new_empty();
4100 sorted_list = g_slist_copy(numlist);
4101 sorted_list = g_slist_sort(sorted_list, g_int_compare);
4103 first = GPOINTER_TO_INT(sorted_list->data);
4106 for (cur = sorted_list; cur != NULL; cur = g_slist_next(cur)) {
4107 if (GPOINTER_TO_INT(cur->data) == 0)
4112 last = GPOINTER_TO_INT(cur->data);
4114 next = GPOINTER_TO_INT(cur->next->data);
4118 if (last + 1 != next || next == 0) {
4120 struct mailimap_set_item * item;
4121 item = mailimap_set_item_new(first, last);
4122 mailimap_set_add(current_set, item);
4127 if (count >= IMAP_SET_MAX_COUNT) {
4128 if (ret_list == NULL)
4129 llast = ret_list = g_slist_append(ret_list,
4132 llast = g_slist_append(llast, current_set);
4133 llast = llast->next;
4135 current_set = mailimap_set_new_empty();
4142 if (clist_count(current_set->set_list) > 0) {
4143 ret_list = g_slist_append(ret_list,
4147 g_slist_free(sorted_list);
4152 static GSList * imap_get_lep_set_from_msglist(MsgInfoList *msglist)
4154 MsgNumberList *numlist = NULL;
4158 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
4159 MsgInfo *msginfo = (MsgInfo *) cur->data;
4161 numlist = g_slist_prepend(numlist, GINT_TO_POINTER(msginfo->msgnum));
4163 numlist = g_slist_reverse(numlist);
4164 seq_list = imap_get_lep_set_from_numlist(numlist);
4165 g_slist_free(numlist);
4170 static GSList * imap_uid_list_from_lep(clist * list)
4177 for(iter = clist_begin(list) ; iter != NULL ;
4178 iter = clist_next(iter)) {
4181 puid = clist_content(iter);
4182 result = g_slist_prepend(result, GINT_TO_POINTER(* puid));
4185 result = g_slist_reverse(result);
4189 static GSList * imap_uid_list_from_lep_tab(carray * list)
4196 for(i = 0 ; i < carray_count(list) ; i ++) {
4199 puid = carray_get(list, i);
4200 result = g_slist_prepend(result, GINT_TO_POINTER(* puid));
4202 result = g_slist_reverse(result);
4206 static MsgInfo *imap_envelope_from_lep(struct imap_fetch_env_info * info,
4209 MsgInfo *msginfo = NULL;
4212 MsgFlags flags = {0, 0};
4214 if (info->headers == NULL)
4217 MSG_SET_TMP_FLAGS(flags, MSG_IMAP);
4218 if (folder_has_parent_of_type(item, F_QUEUE)) {
4219 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
4220 } else if (folder_has_parent_of_type(item, F_DRAFT)) {
4221 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
4223 flags.perm_flags = info->flags;
4227 msginfo = procheader_parse_str(info->headers, flags, FALSE, FALSE);
4230 msginfo->msgnum = uid;
4231 msginfo->size = size;
4237 static void imap_lep_set_free(GSList *seq_list)
4241 for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) {
4242 struct mailimap_set * imapset;
4244 imapset = cur->data;
4245 mailimap_set_free(imapset);
4247 g_slist_free(seq_list);
4250 static struct mailimap_flag_list * imap_flag_to_lep(IMAPFlags flags)
4252 struct mailimap_flag_list * flag_list;
4254 flag_list = mailimap_flag_list_new_empty();
4256 if (IMAP_IS_SEEN(flags))
4257 mailimap_flag_list_add(flag_list,
4258 mailimap_flag_new_seen());
4259 if (IMAP_IS_ANSWERED(flags))
4260 mailimap_flag_list_add(flag_list,
4261 mailimap_flag_new_answered());
4262 if (IMAP_IS_FLAGGED(flags))
4263 mailimap_flag_list_add(flag_list,
4264 mailimap_flag_new_flagged());
4265 if (IMAP_IS_DELETED(flags))
4266 mailimap_flag_list_add(flag_list,
4267 mailimap_flag_new_deleted());
4268 if (IMAP_IS_DRAFT(flags))
4269 mailimap_flag_list_add(flag_list,
4270 mailimap_flag_new_draft());
4275 guint imap_folder_get_refcnt(Folder *folder)
4277 return ((IMAPFolder *)folder)->refcnt;
4280 void imap_folder_ref(Folder *folder)
4282 ((IMAPFolder *)folder)->refcnt++;
4285 void imap_disconnect_all(void)
4288 for (list = account_get_list(); list != NULL; list = list->next) {
4289 PrefsAccount *account = list->data;
4290 if (account->protocol == A_IMAP4) {
4291 RemoteFolder *folder = (RemoteFolder *)account->folder;
4292 if (folder && folder->session) {
4293 IMAPSession *session = (IMAPSession *)folder->session;
4294 imap_threaded_disconnect(FOLDER(folder));
4295 SESSION(session)->state = SESSION_DISCONNECTED;
4296 session_destroy(SESSION(session));
4297 folder->session = NULL;
4303 void imap_folder_unref(Folder *folder)
4305 if (((IMAPFolder *)folder)->refcnt > 0)
4306 ((IMAPFolder *)folder)->refcnt--;
4309 #else /* HAVE_LIBETPAN */
4311 static FolderClass imap_class;
4313 static XMLTag *imap_item_get_xml(Folder *folder, FolderItem *item);
4314 static void imap_item_set_xml(Folder *folder, FolderItem *item, XMLTag *tag);
4316 static Folder *imap_folder_new (const gchar *name,
4321 static gint imap_create_tree (Folder *folder)
4325 static FolderItem *imap_create_folder (Folder *folder,
4331 static gint imap_rename_folder (Folder *folder,
4338 gchar imap_get_path_separator_for_item(FolderItem *item)
4343 FolderClass *imap_get_class(void)
4345 if (imap_class.idstr == NULL) {
4346 imap_class.type = F_IMAP;
4347 imap_class.idstr = "imap";
4348 imap_class.uistr = "IMAP4";
4350 imap_class.new_folder = imap_folder_new;
4351 imap_class.create_tree = imap_create_tree;
4352 imap_class.create_folder = imap_create_folder;
4353 imap_class.rename_folder = imap_rename_folder;
4355 imap_class.set_xml = folder_set_xml;
4356 imap_class.get_xml = folder_get_xml;
4357 imap_class.item_set_xml = imap_item_set_xml;
4358 imap_class.item_get_xml = imap_item_get_xml;
4359 /* nothing implemented */
4365 void imap_disconnect_all(void)
4371 void imap_synchronise(FolderItem *item)
4373 imap_gtk_synchronise(item);
4376 static void imap_item_set_xml(Folder *folder, FolderItem *item, XMLTag *tag)
4378 #ifdef HAVE_LIBETPAN
4381 folder_item_set_xml(folder, item, tag);
4383 #ifdef HAVE_LIBETPAN
4384 for (cur = tag->attr; cur != NULL; cur = g_list_next(cur)) {
4385 XMLAttr *attr = (XMLAttr *) cur->data;
4387 if (!attr || !attr->name || !attr->value) continue;
4388 if (!strcmp(attr->name, "uidnext"))
4389 IMAP_FOLDER_ITEM(item)->uid_next = atoi(attr->value);
4394 static XMLTag *imap_item_get_xml(Folder *folder, FolderItem *item)
4398 tag = folder_item_get_xml(folder, item);
4400 #ifdef HAVE_LIBETPAN
4401 xml_tag_add_attr(tag, xml_attr_new_int("uidnext",
4402 IMAP_FOLDER_ITEM(item)->uid_next));