2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2005 Hiroyuki Yamamoto
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>
55 #include "procheader.h"
56 #include "prefs_account.h"
61 #include "prefs_common.h"
62 #include "inputdialog.h"
64 #include "remotefolder.h"
65 #include "alertpanel.h"
67 #include "statusbar.h"
69 #include "imap-thread.h"
71 typedef struct _IMAPFolder IMAPFolder;
72 typedef struct _IMAPSession IMAPSession;
73 typedef struct _IMAPNameSpace IMAPNameSpace;
74 typedef struct _IMAPFolderItem IMAPFolderItem;
76 #include "prefs_account.h"
78 #define IMAP_FOLDER(obj) ((IMAPFolder *)obj)
79 #define IMAP_FOLDER_ITEM(obj) ((IMAPFolderItem *)obj)
80 #define IMAP_SESSION(obj) ((IMAPSession *)obj)
86 /* list of IMAPNameSpace */
90 gchar last_seen_separator;
98 gboolean authenticated;
107 gboolean folder_content_changed;
112 struct _IMAPNameSpace
118 #define IMAP_SUCCESS 0
119 #define IMAP_SOCKET 2
120 #define IMAP_AUTHFAIL 3
121 #define IMAP_PROTOCOL 4
122 #define IMAP_SYNTAX 5
126 #define IMAPBUFSIZE 8192
130 IMAP_FLAG_SEEN = 1 << 0,
131 IMAP_FLAG_ANSWERED = 1 << 1,
132 IMAP_FLAG_FLAGGED = 1 << 2,
133 IMAP_FLAG_DELETED = 1 << 3,
134 IMAP_FLAG_DRAFT = 1 << 4
137 #define IMAP_IS_SEEN(flags) ((flags & IMAP_FLAG_SEEN) != 0)
138 #define IMAP_IS_ANSWERED(flags) ((flags & IMAP_FLAG_ANSWERED) != 0)
139 #define IMAP_IS_FLAGGED(flags) ((flags & IMAP_FLAG_FLAGGED) != 0)
140 #define IMAP_IS_DELETED(flags) ((flags & IMAP_FLAG_DELETED) != 0)
141 #define IMAP_IS_DRAFT(flags) ((flags & IMAP_FLAG_DRAFT) != 0)
144 #define IMAP4_PORT 143
146 #define IMAPS_PORT 993
149 #define IMAP_CMD_LIMIT 1000
151 struct _IMAPFolderItem
163 guint32 c_uid_validity;
167 static void imap_folder_init (Folder *folder,
171 static Folder *imap_folder_new (const gchar *name,
173 static void imap_folder_destroy (Folder *folder);
175 static IMAPSession *imap_session_new (Folder *folder,
176 const PrefsAccount *account);
177 static void imap_session_authenticate(IMAPSession *session,
178 const PrefsAccount *account);
179 static void imap_session_destroy (Session *session);
181 static gchar *imap_fetch_msg (Folder *folder,
184 static gchar *imap_fetch_msg_full (Folder *folder,
189 static gint imap_add_msg (Folder *folder,
193 static gint imap_add_msgs (Folder *folder,
196 GRelation *relation);
198 static gint imap_copy_msg (Folder *folder,
201 static gint imap_copy_msgs (Folder *folder,
203 MsgInfoList *msglist,
204 GRelation *relation);
206 static gint imap_remove_msg (Folder *folder,
209 static gint imap_remove_msgs (Folder *folder,
211 MsgInfoList *msglist,
212 GRelation *relation);
213 static gint imap_remove_all_msg (Folder *folder,
216 static gboolean imap_is_msg_changed (Folder *folder,
220 static gint imap_close (Folder *folder,
223 static gint imap_scan_tree (Folder *folder);
225 static gint imap_create_tree (Folder *folder);
227 static FolderItem *imap_create_folder (Folder *folder,
230 static gint imap_rename_folder (Folder *folder,
233 static gint imap_remove_folder (Folder *folder,
236 static FolderItem *imap_folder_item_new (Folder *folder);
237 static void imap_folder_item_destroy (Folder *folder,
240 static IMAPSession *imap_session_get (Folder *folder);
242 static gint imap_auth (IMAPSession *session,
247 static gint imap_scan_tree_recursive (IMAPSession *session,
250 static void imap_create_missing_folders (Folder *folder);
251 static FolderItem *imap_create_special_folder
253 SpecialFolderItemType stype,
256 static gint imap_do_copy_msgs (Folder *folder,
258 MsgInfoList *msglist,
259 GRelation *relation);
261 static void imap_delete_all_cached_messages (FolderItem *item);
262 static void imap_set_batch (Folder *folder,
265 static gint imap_set_message_flags (IMAPSession *session,
266 MsgNumberList *numlist,
269 static gint imap_select (IMAPSession *session,
275 guint32 *uid_validity,
277 static gint imap_status (IMAPSession *session,
280 IMAPFolderItem *item,
283 guint32 *uid_validity,
287 static IMAPNameSpace *imap_find_namespace (IMAPFolder *folder,
289 static gchar imap_get_path_separator (IMAPFolder *folder,
291 static gchar *imap_get_real_path (IMAPFolder *folder,
293 static void imap_synchronise (FolderItem *item);
295 static void imap_free_capabilities (IMAPSession *session);
297 /* low-level IMAP4rev1 commands */
298 static gint imap_cmd_login (IMAPSession *session,
302 static gint imap_cmd_logout (IMAPSession *session);
303 static gint imap_cmd_noop (IMAPSession *session);
305 static gint imap_cmd_starttls (IMAPSession *session);
307 static gint imap_cmd_select (IMAPSession *session,
312 guint32 *uid_validity,
314 static gint imap_cmd_examine (IMAPSession *session,
319 guint32 *uid_validity,
321 static gint imap_cmd_create (IMAPSession *sock,
322 const gchar *folder);
323 static gint imap_cmd_rename (IMAPSession *sock,
324 const gchar *oldfolder,
325 const gchar *newfolder);
326 static gint imap_cmd_delete (IMAPSession *session,
327 const gchar *folder);
328 static gint imap_cmd_fetch (IMAPSession *sock,
330 const gchar *filename,
333 static gint imap_cmd_append (IMAPSession *session,
334 const gchar *destfolder,
338 static gint imap_cmd_copy (IMAPSession *session,
339 struct mailimap_set * set,
340 const gchar *destfolder,
341 GRelation *uid_mapping);
342 static gint imap_cmd_store (IMAPSession *session,
343 struct mailimap_set * set,
346 static gint imap_cmd_expunge (IMAPSession *session);
348 static void imap_path_separator_subst (gchar *str,
351 static gchar *imap_utf8_to_modified_utf7 (const gchar *from);
352 static gchar *imap_modified_utf7_to_utf8 (const gchar *mutf7_str);
354 static gboolean imap_rename_folder_func (GNode *node,
356 static gint imap_get_num_list (Folder *folder,
359 gboolean *old_uids_valid);
360 static GSList *imap_get_msginfos (Folder *folder,
362 GSList *msgnum_list);
363 static MsgInfo *imap_get_msginfo (Folder *folder,
366 static gboolean imap_scan_required (Folder *folder,
368 static void imap_change_flags (Folder *folder,
371 MsgPermFlags newflags);
372 static gint imap_get_flags (Folder *folder,
374 MsgInfoList *msglist,
375 GRelation *msgflags);
376 static gchar *imap_folder_get_path (Folder *folder);
377 static gchar *imap_item_get_path (Folder *folder,
379 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item);
382 /* data types conversion libetpan <-> sylpheed */
383 static GSList * imap_list_from_lep(IMAPFolder * folder,
384 clist * list, const gchar * real_path);
385 static GSList * imap_get_lep_set_from_numlist(MsgNumberList *numlist);
386 static GSList * imap_get_lep_set_from_msglist(MsgInfoList *msglist);
387 static GSList * imap_uid_list_from_lep(clist * list);
388 static GSList * imap_uid_list_from_lep_tab(carray * list);
389 static MsgInfo *imap_envelope_from_lep(struct imap_fetch_env_info * info,
391 static void imap_lep_set_free(GSList *seq_list);
392 static struct mailimap_flag_list * imap_flag_to_lep(IMAPFlags flags);
395 static GHashTable *flags_set_table = NULL;
396 static GHashTable *flags_unset_table = NULL;
397 typedef struct _hashtable_data {
398 IMAPSession *session;
402 static FolderClass imap_class;
404 typedef struct _thread_data {
414 FolderClass *imap_get_class(void)
416 if (imap_class.idstr == NULL) {
417 imap_class.type = F_IMAP;
418 imap_class.idstr = "imap";
419 imap_class.uistr = "IMAP4";
421 /* Folder functions */
422 imap_class.new_folder = imap_folder_new;
423 imap_class.destroy_folder = imap_folder_destroy;
424 imap_class.scan_tree = imap_scan_tree;
425 imap_class.create_tree = imap_create_tree;
427 /* FolderItem functions */
428 imap_class.item_new = imap_folder_item_new;
429 imap_class.item_destroy = imap_folder_item_destroy;
430 imap_class.item_get_path = imap_item_get_path;
431 imap_class.create_folder = imap_create_folder;
432 imap_class.rename_folder = imap_rename_folder;
433 imap_class.remove_folder = imap_remove_folder;
434 imap_class.close = imap_close;
435 imap_class.get_num_list = imap_get_num_list;
436 imap_class.scan_required = imap_scan_required;
438 /* Message functions */
439 imap_class.get_msginfo = imap_get_msginfo;
440 imap_class.get_msginfos = imap_get_msginfos;
441 imap_class.fetch_msg = imap_fetch_msg;
442 imap_class.fetch_msg_full = imap_fetch_msg_full;
443 imap_class.add_msg = imap_add_msg;
444 imap_class.add_msgs = imap_add_msgs;
445 imap_class.copy_msg = imap_copy_msg;
446 imap_class.copy_msgs = imap_copy_msgs;
447 imap_class.remove_msg = imap_remove_msg;
448 imap_class.remove_msgs = imap_remove_msgs;
449 imap_class.remove_all_msg = imap_remove_all_msg;
450 imap_class.is_msg_changed = imap_is_msg_changed;
451 imap_class.change_flags = imap_change_flags;
452 imap_class.get_flags = imap_get_flags;
453 imap_class.set_batch = imap_set_batch;
454 imap_class.synchronise = imap_synchronise;
456 pthread_mutex_init(&imap_mutex, NULL);
463 static Folder *imap_folder_new(const gchar *name, const gchar *path)
467 folder = (Folder *)g_new0(IMAPFolder, 1);
468 folder->klass = &imap_class;
469 imap_folder_init(folder, name, path);
474 static void imap_folder_destroy(Folder *folder)
478 while (imap_folder_get_refcnt(folder) > 0)
479 gtk_main_iteration();
481 dir = imap_folder_get_path(folder);
482 if (is_dir_exist(dir))
483 remove_dir_recursive(dir);
486 folder_remote_folder_destroy(REMOTE_FOLDER(folder));
490 static void imap_folder_init(Folder *folder, const gchar *name,
493 folder_remote_folder_init((Folder *)folder, name, path);
496 static FolderItem *imap_folder_item_new(Folder *folder)
498 IMAPFolderItem *item;
500 item = g_new0(IMAPFolderItem, 1);
503 item->uid_list = NULL;
505 return (FolderItem *)item;
508 static void imap_folder_item_destroy(Folder *folder, FolderItem *_item)
510 IMAPFolderItem *item = (IMAPFolderItem *)_item;
512 g_return_if_fail(item != NULL);
513 g_slist_free(item->uid_list);
518 static gboolean imap_reset_uid_lists_func(GNode *node, gpointer data)
520 IMAPFolderItem *item = (IMAPFolderItem *)node->data;
524 g_slist_free(item->uid_list);
525 item->uid_list = NULL;
530 static void imap_reset_uid_lists(Folder *folder)
532 if(folder->node == NULL)
535 /* Destroy all uid lists and rest last uid */
536 g_node_traverse(folder->node, G_IN_ORDER, G_TRAVERSE_ALL, -1, imap_reset_uid_lists_func, NULL);
539 void imap_get_capabilities(IMAPSession *session)
541 struct mailimap_capability_data *capabilities = NULL;
544 if (session->capability != NULL)
547 capabilities = imap_threaded_capability(session->folder);
549 if (capabilities == NULL)
552 for(cur = clist_begin(capabilities->cap_list) ; cur != NULL ;
553 cur = clist_next(cur)) {
554 struct mailimap_capability * cap =
556 if (!cap || cap->cap_data.cap_name == NULL)
558 session->capability = g_slist_append
559 (session->capability,
560 g_strdup(cap->cap_data.cap_name));
561 debug_print("got capa %s\n", cap->cap_data.cap_name);
563 mailimap_capability_data_free(capabilities);
566 gboolean imap_has_capability(IMAPSession *session, const gchar *cap)
569 for (cur = session->capability; cur; cur = cur->next) {
570 if (!g_ascii_strcasecmp(cur->data, cap))
576 static gint imap_auth(IMAPSession *session, const gchar *user, const gchar *pass,
579 gint ok = IMAP_ERROR;
580 static time_t last_login_err = 0;
582 imap_get_capabilities(session);
585 case IMAP_AUTH_CRAM_MD5:
586 ok = imap_cmd_login(session, user, pass, "CRAM-MD5");
588 case IMAP_AUTH_LOGIN:
589 ok = imap_cmd_login(session, user, pass, "LOGIN");
592 debug_print("capabilities:\n"
595 imap_has_capability(session, "CRAM-MD5"),
596 imap_has_capability(session, "LOGIN"));
597 if (imap_has_capability(session, "CRAM-MD5"))
598 ok = imap_cmd_login(session, user, pass, "CRAM-MD5");
599 if (ok == IMAP_ERROR) /* we always try LOGIN before giving up */
600 ok = imap_cmd_login(session, user, pass, "LOGIN");
602 if (ok == IMAP_SUCCESS)
603 session->authenticated = TRUE;
605 gchar *ext_info = NULL;
607 if (type == IMAP_AUTH_CRAM_MD5) {
608 ext_info = _("\n\nCRAM-MD5 logins only work if libetpan has been "
609 "compiled with SASL support and the "
610 "CRAM-MD5 SASL plugin is installed.");
615 if (time(NULL) - last_login_err > 10) {
616 if (!prefs_common.no_recv_err_panel) {
617 alertpanel_error(_("Connection to %s failed: "
619 SESSION(session)->server, ext_info);
621 log_error(_("Connection to %s failed: "
622 "login refused.%s\n"),
623 SESSION(session)->server, ext_info);
626 last_login_err = time(NULL);
631 static IMAPSession *imap_reconnect_if_possible(Folder *folder, IMAPSession *session)
633 RemoteFolder *rfolder = REMOTE_FOLDER(folder);
634 /* Check if this is the first try to establish a
635 connection, if yes we don't try to reconnect */
636 debug_print("reconnecting\n");
637 if (rfolder->session == NULL) {
638 log_warning(_("Connecting to %s failed"),
639 folder->account->recv_server);
640 session_destroy(SESSION(session));
643 log_warning(_("IMAP4 connection to %s has been"
644 " disconnected. Reconnecting...\n"),
645 folder->account->recv_server);
646 statusbar_print_all(_("IMAP4 connection to %s has been"
647 " disconnected. Reconnecting...\n"),
648 folder->account->recv_server);
649 SESSION(session)->state = SESSION_DISCONNECTED;
650 session_destroy(SESSION(session));
651 /* Clear folders session to make imap_session_get create
652 a new session, because of rfolder->session == NULL
653 it will not try to reconnect again and so avoid an
655 rfolder->session = NULL;
656 session = imap_session_get(folder);
657 rfolder->session = SESSION(session);
663 static IMAPSession *imap_session_get(Folder *folder)
665 RemoteFolder *rfolder = REMOTE_FOLDER(folder);
666 IMAPSession *session = NULL;
668 g_return_val_if_fail(folder != NULL, NULL);
669 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, NULL);
670 g_return_val_if_fail(folder->account != NULL, NULL);
672 if (prefs_common.work_offline && !inc_offline_should_override()) {
676 /* Make sure we have a session */
677 if (rfolder->session != NULL) {
678 session = IMAP_SESSION(rfolder->session);
680 imap_reset_uid_lists(folder);
681 session = imap_session_new(folder, folder->account);
686 /* Make sure session is authenticated */
687 if (!IMAP_SESSION(session)->authenticated)
688 imap_session_authenticate(IMAP_SESSION(session), folder->account);
690 if (!IMAP_SESSION(session)->authenticated) {
691 session_destroy(SESSION(session));
692 rfolder->session = NULL;
697 /* Make sure we have parsed the IMAP namespace */
698 imap_parse_namespace(IMAP_SESSION(session),
699 IMAP_FOLDER(folder));
702 /* I think the point of this code is to avoid sending a
703 * keepalive if we've used the session recently and therefore
704 * think it's still alive. Unfortunately, most of the code
705 * does not yet check for errors on the socket, and so if the
706 * connection drops we don't notice until the timeout expires.
707 * A better solution than sending a NOOP every time would be
708 * for every command to be prepared to retry until it is
709 * successfully sent. -- mbp */
710 if (time(NULL) - SESSION(session)->last_access_time > SESSION_TIMEOUT_INTERVAL) {
711 /* verify that the session is still alive */
712 if (imap_cmd_noop(session) != IMAP_SUCCESS) {
713 debug_print("disconnected!\n");
714 session = imap_reconnect_if_possible(folder, session);
718 rfolder->session = SESSION(session);
720 return IMAP_SESSION(session);
723 static IMAPSession *imap_session_new(Folder * folder,
724 const PrefsAccount *account)
726 IMAPSession *session;
732 /* FIXME: IMAP over SSL only... */
735 port = account->set_imapport ? account->imapport
736 : account->ssl_imap == SSL_TUNNEL ? IMAPS_PORT : IMAP4_PORT;
737 ssl_type = account->ssl_imap;
739 if (account->ssl_imap != SSL_NONE) {
740 if (alertpanel_full(_("Insecure connection"),
741 _("This connection is configured to be secured "
742 "using SSL, but SSL is not available in this "
743 "build of Sylpheed-Claws. \n\n"
744 "Do you want to continue connecting to this "
745 "server? The communication would not be "
747 _("Con_tinue connecting"),
748 GTK_STOCK_CANCEL, NULL,
749 FALSE, NULL, ALERT_WARNING,
750 G_ALERTALTERNATE) != G_ALERTDEFAULT)
753 port = account->set_imapport ? account->imapport
758 statusbar_print_all(_("Connecting to IMAP4 server: %s..."), folder->account->recv_server);
759 if (account->set_tunnelcmd) {
760 r = imap_threaded_connect_cmd(folder,
762 account->recv_server,
767 if (ssl_type == SSL_TUNNEL) {
768 r = imap_threaded_connect_ssl(folder,
769 account->recv_server,
775 r = imap_threaded_connect(folder,
776 account->recv_server,
782 if (r == MAILIMAP_NO_ERROR_AUTHENTICATED) {
783 authenticated = TRUE;
785 else if (r == MAILIMAP_NO_ERROR_NON_AUTHENTICATED) {
786 authenticated = FALSE;
789 if(!prefs_common.no_recv_err_panel) {
790 alertpanel_error(_("Can't connect to IMAP4 server: %s:%d"),
791 account->recv_server, port);
793 log_error(_("Can't connect to IMAP4 server: %s:%d\n"),
794 account->recv_server, port);
800 session = g_new0(IMAPSession, 1);
801 session_init(SESSION(session));
802 SESSION(session)->type = SESSION_IMAP;
803 SESSION(session)->server = g_strdup(account->recv_server);
804 SESSION(session)->sock = NULL;
806 SESSION(session)->destroy = imap_session_destroy;
808 session->capability = NULL;
810 session->authenticated = authenticated;
811 session->mbox = NULL;
812 session->cmd_count = 0;
813 session->folder = folder;
814 IMAP_FOLDER(session->folder)->last_seen_separator = 0;
817 if (account->ssl_imap == SSL_STARTTLS) {
820 ok = imap_cmd_starttls(session);
821 if (ok != IMAP_SUCCESS) {
822 log_warning(_("Can't start TLS session.\n"));
823 session_destroy(SESSION(session));
827 imap_free_capabilities(session);
828 session->authenticated = FALSE;
829 session->uidplus = FALSE;
830 session->cmd_count = 1;
833 log_message("IMAP connection is %s-authenticated\n",
834 (session->authenticated) ? "pre" : "un");
839 static void imap_session_authenticate(IMAPSession *session,
840 const PrefsAccount *account)
844 g_return_if_fail(account->userid != NULL);
846 pass = account->passwd;
849 tmp_pass = input_dialog_query_password(account->recv_server, account->userid);
851 tmp_pass = g_strdup(""); /* allow empty password */
852 Xstrdup_a(pass, tmp_pass, {g_free(tmp_pass); return;});
855 statusbar_print_all(_("Connecting to IMAP4 server %s...\n"),
856 account->recv_server);
857 if (imap_auth(session, account->userid, pass, account->imap_auth_type) != IMAP_SUCCESS) {
858 imap_threaded_disconnect(session->folder);
859 imap_cmd_logout(session);
865 session->authenticated = TRUE;
868 static void imap_session_destroy(Session *session)
870 if (session->state != SESSION_DISCONNECTED)
871 imap_threaded_disconnect(IMAP_SESSION(session)->folder);
873 imap_free_capabilities(IMAP_SESSION(session));
874 g_free(IMAP_SESSION(session)->mbox);
875 sock_close(session->sock);
876 session->sock = NULL;
879 static gchar *imap_fetch_msg(Folder *folder, FolderItem *item, gint uid)
881 return imap_fetch_msg_full(folder, item, uid, TRUE, TRUE);
884 static guint get_size_with_crs(MsgInfo *info)
893 fp = procmsg_open_message(info);
897 while (fgets(buf, sizeof (buf), fp) != NULL) {
899 if (!strstr(buf, "\r") && strstr(buf, "\n"))
907 static gchar *imap_fetch_msg_full(Folder *folder, FolderItem *item, gint uid,
908 gboolean headers, gboolean body)
910 gchar *path, *filename;
911 IMAPSession *session;
914 g_return_val_if_fail(folder != NULL, NULL);
915 g_return_val_if_fail(item != NULL, NULL);
920 path = folder_item_get_path(item);
921 if (!is_dir_exist(path))
923 filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(uid), NULL);
926 if (is_file_exist(filename)) {
927 /* see whether the local file represents the whole message
928 * or not. As the IMAP server reports size with \r chars,
929 * we have to update the local file (UNIX \n only) size */
930 MsgInfo *msginfo = imap_parse_msg(filename, item);
931 MsgInfo *cached = msgcache_get_msg(item->cache,uid);
932 guint have_size = get_size_with_crs(msginfo);
935 debug_print("message %d has been already %scached (%d/%d).\n", uid,
936 have_size == cached->size ? "fully ":"",
937 have_size, (int)cached->size);
939 if (cached && (cached->size == have_size || !body)) {
940 procmsg_msginfo_free(cached);
941 procmsg_msginfo_free(msginfo);
942 file_strip_crs(filename);
945 procmsg_msginfo_free(cached);
946 procmsg_msginfo_free(msginfo);
950 session = imap_session_get(folder);
956 debug_print("IMAP fetching messages\n");
957 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
958 NULL, NULL, NULL, NULL, FALSE);
959 if (ok != IMAP_SUCCESS) {
960 g_warning("can't select mailbox %s\n", item->path);
965 debug_print("getting message %d...\n", uid);
966 ok = imap_cmd_fetch(session, (guint32)uid, filename, headers, body);
968 if (ok != IMAP_SUCCESS) {
969 g_warning("can't fetch message %d\n", uid);
974 file_strip_crs(filename);
978 static gint imap_add_msg(Folder *folder, FolderItem *dest,
979 const gchar *file, MsgFlags *flags)
983 MsgFileInfo fileinfo;
985 g_return_val_if_fail(file != NULL, -1);
987 fileinfo.msginfo = NULL;
988 fileinfo.file = (gchar *)file;
989 fileinfo.flags = flags;
990 file_list.data = &fileinfo;
991 file_list.next = NULL;
993 ret = imap_add_msgs(folder, dest, &file_list, NULL);
997 static gint imap_add_msgs(Folder *folder, FolderItem *dest, GSList *file_list,
1001 IMAPSession *session;
1002 guint32 last_uid = 0;
1004 MsgFileInfo *fileinfo;
1006 gint curnum = 0, total = 0;
1009 g_return_val_if_fail(folder != NULL, -1);
1010 g_return_val_if_fail(dest != NULL, -1);
1011 g_return_val_if_fail(file_list != NULL, -1);
1013 session = imap_session_get(folder);
1017 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
1019 statusbar_print_all(_("Adding messages..."));
1020 total = g_slist_length(file_list);
1021 for (cur = file_list; cur != NULL; cur = cur->next) {
1022 IMAPFlags iflags = 0;
1023 guint32 new_uid = 0;
1024 gchar *real_file = NULL;
1025 gboolean file_is_tmp = FALSE;
1026 fileinfo = (MsgFileInfo *)cur->data;
1028 statusbar_progress_all(curnum, total, 1);
1031 if (fileinfo->flags) {
1032 if (MSG_IS_MARKED(*fileinfo->flags))
1033 iflags |= IMAP_FLAG_FLAGGED;
1034 if (MSG_IS_REPLIED(*fileinfo->flags))
1035 iflags |= IMAP_FLAG_ANSWERED;
1036 if (!MSG_IS_UNREAD(*fileinfo->flags))
1037 iflags |= IMAP_FLAG_SEEN;
1040 if (fileinfo->flags) {
1041 if ((MSG_IS_QUEUED(*fileinfo->flags)
1042 || MSG_IS_DRAFT(*fileinfo->flags))
1043 && !folder_has_parent_of_type(dest, F_QUEUE)
1044 && !folder_has_parent_of_type(dest, F_DRAFT)) {
1045 real_file = get_tmp_file();
1047 if (procmsg_remove_special_headers(
1056 if (real_file == NULL)
1057 real_file = g_strdup(fileinfo->file);
1059 if (folder_has_parent_of_type(dest, F_QUEUE) ||
1060 folder_has_parent_of_type(dest, F_OUTBOX) ||
1061 folder_has_parent_of_type(dest, F_DRAFT) ||
1062 folder_has_parent_of_type(dest, F_TRASH))
1063 iflags |= IMAP_FLAG_SEEN;
1065 ok = imap_cmd_append(session, destdir, real_file, iflags,
1068 if (ok != IMAP_SUCCESS) {
1069 g_warning("can't append message %s\n", real_file);
1071 g_unlink(real_file);
1077 if (relation != NULL)
1078 g_relation_insert(relation, fileinfo->msginfo != NULL ?
1079 (gpointer) fileinfo->msginfo : (gpointer) fileinfo,
1080 GINT_TO_POINTER(dest->last_num + 1));
1081 if (last_uid < new_uid)
1084 g_unlink(real_file);
1086 statusbar_progress_all(0,0,0);
1087 statusbar_pop_all();
1094 static gint imap_do_copy_msgs(Folder *folder, FolderItem *dest,
1095 MsgInfoList *msglist, GRelation *relation)
1099 GSList *seq_list, *cur;
1101 IMAPSession *session;
1102 gint ok = IMAP_SUCCESS;
1103 GRelation *uid_mapping;
1106 g_return_val_if_fail(folder != NULL, -1);
1107 g_return_val_if_fail(dest != NULL, -1);
1108 g_return_val_if_fail(msglist != NULL, -1);
1110 session = imap_session_get(folder);
1115 msginfo = (MsgInfo *)msglist->data;
1117 src = msginfo->folder;
1119 g_warning("the src folder is identical to the dest.\n");
1123 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
1124 NULL, NULL, NULL, NULL, FALSE);
1125 if (ok != IMAP_SUCCESS) {
1129 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
1130 seq_list = imap_get_lep_set_from_msglist(msglist);
1131 uid_mapping = g_relation_new(2);
1132 g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
1134 statusbar_print_all(_("Copying messages..."));
1135 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
1136 struct mailimap_set * seq_set;
1137 seq_set = cur->data;
1139 debug_print("Copying messages from %s to %s ...\n",
1140 src->path, destdir);
1142 ok = imap_cmd_copy(session, seq_set, destdir, uid_mapping);
1143 if (ok != IMAP_SUCCESS) {
1144 g_relation_destroy(uid_mapping);
1145 imap_lep_set_free(seq_list);
1150 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
1151 MsgInfo *msginfo = (MsgInfo *)cur->data;
1154 tuples = g_relation_select(uid_mapping,
1155 GINT_TO_POINTER(msginfo->msgnum),
1157 if (tuples->len > 0) {
1158 gint num = GPOINTER_TO_INT(g_tuples_index(tuples, 0, 1));
1159 g_relation_insert(relation, msginfo,
1160 GPOINTER_TO_INT(num));
1164 g_relation_insert(relation, msginfo,
1165 GPOINTER_TO_INT(0));
1166 g_tuples_destroy(tuples);
1168 statusbar_pop_all();
1170 g_relation_destroy(uid_mapping);
1171 imap_lep_set_free(seq_list);
1175 IMAP_FOLDER_ITEM(dest)->lastuid = 0;
1176 IMAP_FOLDER_ITEM(dest)->uid_next = 0;
1177 g_slist_free(IMAP_FOLDER_ITEM(dest)->uid_list);
1178 IMAP_FOLDER_ITEM(dest)->uid_list = NULL;
1180 if (ok == IMAP_SUCCESS)
1186 static gint imap_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1190 g_return_val_if_fail(msginfo != NULL, -1);
1192 msglist.data = msginfo;
1193 msglist.next = NULL;
1195 return imap_copy_msgs(folder, dest, &msglist, NULL);
1198 static gint imap_copy_msgs(Folder *folder, FolderItem *dest,
1199 MsgInfoList *msglist, GRelation *relation)
1205 g_return_val_if_fail(folder != NULL, -1);
1206 g_return_val_if_fail(dest != NULL, -1);
1207 g_return_val_if_fail(msglist != NULL, -1);
1209 msginfo = (MsgInfo *)msglist->data;
1210 g_return_val_if_fail(msginfo->folder != NULL, -1);
1212 if (folder == msginfo->folder->folder &&
1213 !folder_has_parent_of_type(msginfo->folder, F_DRAFT) &&
1214 !folder_has_parent_of_type(msginfo->folder, F_QUEUE)) {
1215 ret = imap_do_copy_msgs(folder, dest, msglist, relation);
1219 file_list = procmsg_get_message_file_list(msglist);
1220 g_return_val_if_fail(file_list != NULL, -1);
1222 ret = imap_add_msgs(folder, dest, file_list, relation);
1224 procmsg_message_file_list_free(file_list);
1230 static gint imap_do_remove_msgs(Folder *folder, FolderItem *dest,
1231 MsgInfoList *msglist, GRelation *relation)
1234 GSList *seq_list = NULL, *cur;
1236 IMAPSession *session;
1237 gint ok = IMAP_SUCCESS;
1238 GRelation *uid_mapping;
1240 g_return_val_if_fail(folder != NULL, -1);
1241 g_return_val_if_fail(dest != NULL, -1);
1242 g_return_val_if_fail(msglist != NULL, -1);
1244 session = imap_session_get(folder);
1248 msginfo = (MsgInfo *)msglist->data;
1250 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
1251 NULL, NULL, NULL, NULL, FALSE);
1252 if (ok != IMAP_SUCCESS) {
1256 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
1257 for (cur = msglist; cur; cur = cur->next) {
1258 msginfo = (MsgInfo *)cur->data;
1259 seq_list = g_slist_append(seq_list, GINT_TO_POINTER(msginfo->msgnum));
1262 uid_mapping = g_relation_new(2);
1263 g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
1265 ok = imap_set_message_flags
1266 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
1267 seq_list, IMAP_FLAG_DELETED, TRUE);
1268 if (ok != IMAP_SUCCESS) {
1269 log_warning(_("can't set deleted flags\n"));
1272 ok = imap_cmd_expunge(session);
1273 if (ok != IMAP_SUCCESS) {
1274 log_warning(_("can't expunge\n"));
1278 g_relation_destroy(uid_mapping);
1279 g_slist_free(seq_list);
1283 if (ok == IMAP_SUCCESS)
1289 static gint imap_remove_msgs(Folder *folder, FolderItem *dest,
1290 MsgInfoList *msglist, GRelation *relation)
1294 g_return_val_if_fail(folder != NULL, -1);
1295 g_return_val_if_fail(dest != NULL, -1);
1296 if (msglist == NULL)
1299 msginfo = (MsgInfo *)msglist->data;
1300 g_return_val_if_fail(msginfo->folder != NULL, -1);
1302 return imap_do_remove_msgs(folder, dest, msglist, relation);
1305 static gint imap_remove_all_msg(Folder *folder, FolderItem *item)
1307 GSList *list = folder_item_get_msg_list(item);
1308 gint res = imap_remove_msgs(folder, item, list, NULL);
1309 procmsg_msg_list_free(list);
1313 static gboolean imap_is_msg_changed(Folder *folder, FolderItem *item,
1316 /* TODO: properly implement this method */
1320 static gint imap_close(Folder *folder, FolderItem *item)
1325 static gint imap_scan_tree(Folder *folder)
1327 FolderItem *item = NULL;
1328 IMAPSession *session;
1329 gchar *root_folder = NULL;
1331 g_return_val_if_fail(folder != NULL, -1);
1332 g_return_val_if_fail(folder->account != NULL, -1);
1334 session = imap_session_get(folder);
1336 if (!folder->node) {
1337 folder_tree_destroy(folder);
1338 item = folder_item_new(folder, folder->name, NULL);
1339 item->folder = folder;
1340 folder->node = item->node = g_node_new(item);
1345 if (folder->account->imap_dir && *folder->account->imap_dir) {
1350 Xstrdup_a(root_folder, folder->account->imap_dir, {return -1;});
1351 extract_quote(root_folder, '"');
1352 subst_char(root_folder,
1353 imap_get_path_separator(IMAP_FOLDER(folder),
1356 strtailchomp(root_folder, '/');
1357 real_path = imap_get_real_path
1358 (IMAP_FOLDER(folder), root_folder);
1359 debug_print("IMAP root directory: %s\n", real_path);
1361 /* check if root directory exist */
1363 r = imap_threaded_list(session->folder, "", real_path,
1365 if ((r != MAILIMAP_NO_ERROR) || (clist_count(lep_list) == 0)) {
1366 if (!folder->node) {
1367 item = folder_item_new(folder, folder->name, NULL);
1368 item->folder = folder;
1369 folder->node = item->node = g_node_new(item);
1374 mailimap_list_result_free(lep_list);
1380 item = FOLDER_ITEM(folder->node->data);
1381 if (!item || ((item->path || root_folder) &&
1382 strcmp2(item->path, root_folder) != 0)) {
1383 folder_tree_destroy(folder);
1384 item = folder_item_new(folder, folder->name, root_folder);
1385 item->folder = folder;
1386 folder->node = item->node = g_node_new(item);
1389 imap_scan_tree_recursive(session, FOLDER_ITEM(folder->node->data));
1390 imap_create_missing_folders(folder);
1395 static gint imap_scan_tree_recursive(IMAPSession *session, FolderItem *item)
1398 IMAPFolder *imapfolder;
1399 FolderItem *new_item;
1400 GSList *item_list, *cur;
1403 gchar *wildcard_path;
1409 g_return_val_if_fail(item != NULL, -1);
1410 g_return_val_if_fail(item->folder != NULL, -1);
1411 g_return_val_if_fail(item->no_sub == FALSE, -1);
1413 folder = item->folder;
1414 imapfolder = IMAP_FOLDER(folder);
1416 separator = imap_get_path_separator(imapfolder, item->path);
1418 if (folder->ui_func)
1419 folder->ui_func(folder, item, folder->ui_func_data);
1422 wildcard[0] = separator;
1425 real_path = imap_get_real_path(imapfolder, item->path);
1429 real_path = g_strdup("");
1432 Xstrcat_a(wildcard_path, real_path, wildcard,
1433 {g_free(real_path); return IMAP_ERROR;});
1435 r = imap_threaded_list(folder, "", wildcard_path, &lep_list);
1436 if (r != MAILIMAP_NO_ERROR) {
1440 item_list = imap_list_from_lep(imapfolder,
1441 lep_list, real_path);
1442 mailimap_list_result_free(lep_list);
1447 node = item->node->children;
1448 while (node != NULL) {
1449 FolderItem *old_item = FOLDER_ITEM(node->data);
1450 GNode *next = node->next;
1453 for (cur = item_list; cur != NULL; cur = cur->next) {
1454 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1455 if (!strcmp2(old_item->path, cur_item->path)) {
1456 new_item = cur_item;
1461 debug_print("folder '%s' not found. removing...\n",
1463 folder_item_remove(old_item);
1465 old_item->no_sub = new_item->no_sub;
1466 old_item->no_select = new_item->no_select;
1467 if (old_item->no_sub == TRUE && node->children) {
1468 debug_print("folder '%s' doesn't have "
1469 "subfolders. removing...\n",
1471 folder_item_remove_children(old_item);
1478 for (cur = item_list; cur != NULL; cur = cur->next) {
1479 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1481 for (node = item->node->children; node != NULL;
1482 node = node->next) {
1483 if (!strcmp2(FOLDER_ITEM(node->data)->path,
1485 new_item = FOLDER_ITEM(node->data);
1486 folder_item_destroy(cur_item);
1492 new_item = cur_item;
1493 debug_print("new folder '%s' found.\n", new_item->path);
1494 folder_item_append(item, new_item);
1497 if (!strcmp(new_item->path, "INBOX")) {
1498 new_item->stype = F_INBOX;
1499 folder->inbox = new_item;
1500 } else if (!folder_item_parent(item) || item->stype == F_INBOX) {
1503 base = g_path_get_basename(new_item->path);
1505 if (!folder->outbox && !g_ascii_strcasecmp(base, "Sent")) {
1506 new_item->stype = F_OUTBOX;
1507 folder->outbox = new_item;
1508 } else if (!folder->draft && !g_ascii_strcasecmp(base, "Drafts")) {
1509 new_item->stype = F_DRAFT;
1510 folder->draft = new_item;
1511 } else if (!folder->queue && !g_ascii_strcasecmp(base, "Queue")) {
1512 new_item->stype = F_QUEUE;
1513 folder->queue = new_item;
1514 } else if (!folder->trash && !g_ascii_strcasecmp(base, "Trash")) {
1515 new_item->stype = F_TRASH;
1516 folder->trash = new_item;
1521 if (new_item->no_sub == FALSE)
1522 imap_scan_tree_recursive(session, new_item);
1525 g_slist_free(item_list);
1527 return IMAP_SUCCESS;
1530 static gint imap_create_tree(Folder *folder)
1532 g_return_val_if_fail(folder != NULL, -1);
1533 g_return_val_if_fail(folder->node != NULL, -1);
1534 g_return_val_if_fail(folder->node->data != NULL, -1);
1535 g_return_val_if_fail(folder->account != NULL, -1);
1537 imap_scan_tree(folder);
1538 imap_create_missing_folders(folder);
1543 static void imap_create_missing_folders(Folder *folder)
1545 g_return_if_fail(folder != NULL);
1548 folder->inbox = imap_create_special_folder
1549 (folder, F_INBOX, "INBOX");
1551 folder->trash = imap_create_special_folder
1552 (folder, F_TRASH, "Trash");
1554 folder->queue = imap_create_special_folder
1555 (folder, F_QUEUE, "Queue");
1556 if (!folder->outbox)
1557 folder->outbox = imap_create_special_folder
1558 (folder, F_OUTBOX, "Sent");
1560 folder->draft = imap_create_special_folder
1561 (folder, F_DRAFT, "Drafts");
1564 static FolderItem *imap_create_special_folder(Folder *folder,
1565 SpecialFolderItemType stype,
1569 FolderItem *new_item;
1571 g_return_val_if_fail(folder != NULL, NULL);
1572 g_return_val_if_fail(folder->node != NULL, NULL);
1573 g_return_val_if_fail(folder->node->data != NULL, NULL);
1574 g_return_val_if_fail(folder->account != NULL, NULL);
1575 g_return_val_if_fail(name != NULL, NULL);
1577 item = FOLDER_ITEM(folder->node->data);
1578 new_item = imap_create_folder(folder, item, name);
1581 g_warning("Can't create '%s'\n", name);
1582 if (!folder->inbox) return NULL;
1584 new_item = imap_create_folder(folder, folder->inbox, name);
1586 g_warning("Can't create '%s' under INBOX\n", name);
1588 new_item->stype = stype;
1590 new_item->stype = stype;
1595 static gchar *imap_folder_get_path(Folder *folder)
1599 g_return_val_if_fail(folder != NULL, NULL);
1600 g_return_val_if_fail(folder->account != NULL, NULL);
1602 folder_path = g_strconcat(get_imap_cache_dir(),
1604 folder->account->recv_server,
1606 folder->account->userid,
1612 static gchar *imap_item_get_path(Folder *folder, FolderItem *item)
1614 gchar *folder_path, *path;
1616 g_return_val_if_fail(folder != NULL, NULL);
1617 g_return_val_if_fail(item != NULL, NULL);
1618 folder_path = imap_folder_get_path(folder);
1620 g_return_val_if_fail(folder_path != NULL, NULL);
1621 if (folder_path[0] == G_DIR_SEPARATOR) {
1623 path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
1626 path = g_strdup(folder_path);
1629 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1630 folder_path, G_DIR_SEPARATOR_S,
1633 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1636 g_free(folder_path);
1641 static FolderItem *imap_create_folder(Folder *folder, FolderItem *parent,
1644 gchar *dirpath, *imap_path;
1645 IMAPSession *session;
1646 FolderItem *new_item;
1652 g_return_val_if_fail(folder != NULL, NULL);
1653 g_return_val_if_fail(folder->account != NULL, NULL);
1654 g_return_val_if_fail(parent != NULL, NULL);
1655 g_return_val_if_fail(name != NULL, NULL);
1657 session = imap_session_get(folder);
1662 if (!folder_item_parent(parent) && strcmp(name, "INBOX") == 0)
1663 dirpath = g_strdup(name);
1664 else if (parent->path)
1665 dirpath = g_strconcat(parent->path, "/", name, NULL);
1666 else if ((p = strchr(name, '/')) != NULL && *(p + 1) != '\0')
1667 dirpath = g_strdup(name);
1668 else if (folder->account->imap_dir && *folder->account->imap_dir) {
1671 Xstrdup_a(imap_dir, folder->account->imap_dir, {return NULL;});
1672 strtailchomp(imap_dir, '/');
1673 dirpath = g_strconcat(imap_dir, "/", name, NULL);
1675 dirpath = g_strdup(name);
1677 /* keep trailing directory separator to create a folder that contains
1679 imap_path = imap_utf8_to_modified_utf7(dirpath);
1680 strtailchomp(dirpath, '/');
1681 Xstrdup_a(new_name, name, {
1685 strtailchomp(new_name, '/');
1686 separator = imap_get_path_separator(IMAP_FOLDER(folder), imap_path);
1687 imap_path_separator_subst(imap_path, separator);
1688 subst_char(new_name, '/', separator);
1690 if (strcmp(name, "INBOX") != 0) {
1692 gboolean exist = FALSE;
1696 argbuf = g_ptr_array_new();
1697 r = imap_threaded_list(folder, "", imap_path, &lep_list);
1698 if (r != MAILIMAP_NO_ERROR) {
1699 log_warning(_("can't create mailbox: LIST failed\n"));
1702 ptr_array_free_strings(argbuf);
1703 g_ptr_array_free(argbuf, TRUE);
1707 if (clist_count(lep_list) > 0)
1711 ok = imap_cmd_create(session, imap_path);
1712 if (ok != IMAP_SUCCESS) {
1713 log_warning(_("can't create mailbox\n"));
1721 new_item = folder_item_new(folder, new_name, dirpath);
1722 folder_item_append(parent, new_item);
1726 dirpath = folder_item_get_path(new_item);
1727 if (!is_dir_exist(dirpath))
1728 make_dir_hier(dirpath);
1734 static gint imap_rename_folder(Folder *folder, FolderItem *item,
1739 gchar *real_oldpath;
1740 gchar *real_newpath;
1742 gchar *old_cache_dir;
1743 gchar *new_cache_dir;
1744 IMAPSession *session;
1747 gint exists, recent, unseen;
1748 guint32 uid_validity;
1750 g_return_val_if_fail(folder != NULL, -1);
1751 g_return_val_if_fail(item != NULL, -1);
1752 g_return_val_if_fail(item->path != NULL, -1);
1753 g_return_val_if_fail(name != NULL, -1);
1755 if (strchr(name, imap_get_path_separator(IMAP_FOLDER(folder), item->path)) != NULL) {
1756 g_warning(_("New folder name must not contain the namespace "
1761 session = imap_session_get(folder);
1765 real_oldpath = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1767 g_free(session->mbox);
1768 session->mbox = NULL;
1769 ok = imap_cmd_examine(session, "INBOX",
1770 &exists, &recent, &unseen, &uid_validity, FALSE);
1771 if (ok != IMAP_SUCCESS) {
1772 g_free(real_oldpath);
1776 separator = imap_get_path_separator(IMAP_FOLDER(folder), item->path);
1777 if (strchr(item->path, G_DIR_SEPARATOR)) {
1778 dirpath = g_path_get_dirname(item->path);
1779 newpath = g_strconcat(dirpath, G_DIR_SEPARATOR_S, name, NULL);
1782 newpath = g_strdup(name);
1784 real_newpath = imap_utf8_to_modified_utf7(newpath);
1785 imap_path_separator_subst(real_newpath, separator);
1787 ok = imap_cmd_rename(session, real_oldpath, real_newpath);
1788 if (ok != IMAP_SUCCESS) {
1789 log_warning(_("can't rename mailbox: %s to %s\n"),
1790 real_oldpath, real_newpath);
1791 g_free(real_oldpath);
1793 g_free(real_newpath);
1798 item->name = g_strdup(name);
1800 old_cache_dir = folder_item_get_path(item);
1802 paths[0] = g_strdup(item->path);
1804 g_node_traverse(item->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1805 imap_rename_folder_func, paths);
1807 if (is_dir_exist(old_cache_dir)) {
1808 new_cache_dir = folder_item_get_path(item);
1809 if (rename(old_cache_dir, new_cache_dir) < 0) {
1810 FILE_OP_ERROR(old_cache_dir, "rename");
1812 g_free(new_cache_dir);
1815 g_free(old_cache_dir);
1818 g_free(real_oldpath);
1819 g_free(real_newpath);
1824 static gint imap_remove_folder_real(Folder *folder, FolderItem *item)
1827 IMAPSession *session;
1830 gint exists, recent, unseen;
1831 guint32 uid_validity;
1833 g_return_val_if_fail(folder != NULL, -1);
1834 g_return_val_if_fail(item != NULL, -1);
1835 g_return_val_if_fail(item->path != NULL, -1);
1837 session = imap_session_get(folder);
1841 path = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1843 ok = imap_cmd_examine(session, "INBOX",
1844 &exists, &recent, &unseen, &uid_validity, FALSE);
1845 if (ok != IMAP_SUCCESS) {
1850 ok = imap_cmd_delete(session, path);
1851 if (ok != IMAP_SUCCESS) {
1852 log_warning(_("can't delete mailbox\n"));
1858 cache_dir = folder_item_get_path(item);
1859 if (is_dir_exist(cache_dir) && remove_dir_recursive(cache_dir) < 0)
1860 g_warning("can't remove directory '%s'\n", cache_dir);
1862 folder_item_remove(item);
1867 static gint imap_remove_folder(Folder *folder, FolderItem *item)
1871 g_return_val_if_fail(item != NULL, -1);
1872 g_return_val_if_fail(item->folder != NULL, -1);
1873 g_return_val_if_fail(item->node != NULL, -1);
1875 node = item->node->children;
1876 while (node != NULL) {
1878 if (imap_remove_folder(folder, FOLDER_ITEM(node->data)) < 0)
1882 debug_print("IMAP removing %s\n", item->path);
1884 if (imap_remove_all_msg(folder, item) < 0)
1886 return imap_remove_folder_real(folder, item);
1889 typedef struct _uncached_data {
1890 IMAPSession *session;
1892 MsgNumberList *numlist;
1898 static void *imap_get_uncached_messages_thread(void *data)
1900 uncached_data *stuff = (uncached_data *)data;
1901 IMAPSession *session = stuff->session;
1902 FolderItem *item = stuff->item;
1903 MsgNumberList *numlist = stuff->numlist;
1905 GSList *newlist = NULL;
1906 GSList *llast = NULL;
1907 GSList *seq_list, *cur;
1909 debug_print("uncached_messages\n");
1911 if (session == NULL || item == NULL || item->folder == NULL
1912 || FOLDER_CLASS(item->folder) != &imap_class) {
1917 seq_list = imap_get_lep_set_from_numlist(numlist);
1918 debug_print("get msgs info\n");
1919 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
1920 struct mailimap_set * imapset;
1926 imapset = cur->data;
1928 r = imap_threaded_fetch_env(session->folder,
1929 imapset, &env_list);
1930 if (r != MAILIMAP_NO_ERROR)
1934 for(i = 0 ; i < carray_count(env_list) ; i ++) {
1935 struct imap_fetch_env_info * info;
1938 info = carray_get(env_list, i);
1939 msginfo = imap_envelope_from_lep(info, item);
1940 msginfo->folder = item;
1942 llast = newlist = g_slist_append(newlist, msginfo);
1944 llast = g_slist_append(llast, msginfo);
1945 llast = llast->next;
1950 imap_fetch_env_free(env_list);
1953 session_set_access_time(SESSION(session));
1958 #define MAX_MSG_NUM 50
1960 static GSList *imap_get_uncached_messages(IMAPSession *session,
1962 MsgNumberList *numlist)
1964 GSList *result = NULL;
1966 uncached_data *data = g_new0(uncached_data, 1);
1971 data->total = g_slist_length(numlist);
1972 debug_print("messages list : %i\n", data->total);
1974 while (cur != NULL) {
1975 GSList * partial_result;
1983 while (count < MAX_MSG_NUM) {
1988 if (newlist == NULL)
1989 llast = newlist = g_slist_append(newlist, p);
1991 llast = g_slist_append(llast, p);
1992 llast = llast->next;
2002 data->session = session;
2004 data->numlist = newlist;
2007 if (prefs_common.work_offline && !inc_offline_should_override()) {
2013 (GSList *)imap_get_uncached_messages_thread(data);
2015 statusbar_progress_all(data->cur,data->total, 1);
2017 g_slist_free(newlist);
2019 result = g_slist_concat(result, partial_result);
2023 statusbar_progress_all(0,0,0);
2024 statusbar_pop_all();
2029 static void imap_delete_all_cached_messages(FolderItem *item)
2033 g_return_if_fail(item != NULL);
2034 g_return_if_fail(item->folder != NULL);
2035 g_return_if_fail(FOLDER_CLASS(item->folder) == &imap_class);
2037 debug_print("Deleting all cached messages...\n");
2039 dir = folder_item_get_path(item);
2040 if (is_dir_exist(dir))
2041 remove_all_numbered_files(dir);
2044 debug_print("done.\n");
2047 static IMAPNameSpace *imap_find_namespace_from_list(GList *ns_list,
2050 IMAPNameSpace *namespace = NULL;
2051 gchar *tmp_path, *name;
2053 if (!path) path = "";
2055 for (; ns_list != NULL; ns_list = ns_list->next) {
2056 IMAPNameSpace *tmp_ns = ns_list->data;
2058 Xstrcat_a(tmp_path, path, "/", return namespace);
2059 Xstrdup_a(name, tmp_ns->name, return namespace);
2060 if (tmp_ns->separator && tmp_ns->separator != '/') {
2061 subst_char(tmp_path, tmp_ns->separator, '/');
2062 subst_char(name, tmp_ns->separator, '/');
2064 if (strncmp(tmp_path, name, strlen(name)) == 0)
2071 static IMAPNameSpace *imap_find_namespace(IMAPFolder *folder,
2074 IMAPNameSpace *namespace;
2076 g_return_val_if_fail(folder != NULL, NULL);
2078 namespace = imap_find_namespace_from_list(folder->ns_personal, path);
2079 if (namespace) return namespace;
2080 namespace = imap_find_namespace_from_list(folder->ns_others, path);
2081 if (namespace) return namespace;
2082 namespace = imap_find_namespace_from_list(folder->ns_shared, path);
2083 if (namespace) return namespace;
2089 static gchar imap_get_path_separator(IMAPFolder *folder, const gchar *path)
2091 IMAPNameSpace *namespace;
2092 gchar separator = '/';
2094 if (folder->last_seen_separator == 0) {
2096 int r = imap_threaded_list((Folder *)folder, "", "", &lep_list);
2097 if (r != MAILIMAP_NO_ERROR) {
2098 log_warning(_("LIST failed\n"));
2102 if (clist_count(lep_list) > 0) {
2103 clistiter * iter = clist_begin(lep_list);
2104 struct mailimap_mailbox_list * mb;
2105 mb = clist_content(iter);
2107 folder->last_seen_separator = mb->mb_delimiter;
2108 debug_print("got separator: %c\n", folder->last_seen_separator);
2110 mailimap_list_result_free(lep_list);
2113 if (folder->last_seen_separator != 0) {
2114 debug_print("using separator: %c\n", folder->last_seen_separator);
2115 return folder->last_seen_separator;
2118 namespace = imap_find_namespace(folder, path);
2119 if (namespace && namespace->separator)
2120 separator = namespace->separator;
2125 static gchar *imap_get_real_path(IMAPFolder *folder, const gchar *path)
2130 g_return_val_if_fail(folder != NULL, NULL);
2131 g_return_val_if_fail(path != NULL, NULL);
2133 real_path = imap_utf8_to_modified_utf7(path);
2134 separator = imap_get_path_separator(folder, path);
2135 imap_path_separator_subst(real_path, separator);
2140 static gint imap_set_message_flags(IMAPSession *session,
2141 MsgNumberList *numlist,
2149 seq_list = imap_get_lep_set_from_numlist(numlist);
2151 for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) {
2152 struct mailimap_set * imapset;
2154 imapset = cur->data;
2156 ok = imap_cmd_store(session, imapset,
2160 imap_lep_set_free(seq_list);
2162 return IMAP_SUCCESS;
2165 typedef struct _select_data {
2166 IMAPSession *session;
2171 guint32 *uid_validity;
2175 static gint imap_select(IMAPSession *session, IMAPFolder *folder,
2177 gint *exists, gint *recent, gint *unseen,
2178 guint32 *uid_validity, gboolean block)
2182 gint exists_, recent_, unseen_;
2183 guint32 uid_validity_;
2185 if (!exists || !recent || !unseen || !uid_validity) {
2186 if (session->mbox && strcmp(session->mbox, path) == 0)
2187 return IMAP_SUCCESS;
2191 uid_validity = &uid_validity_;
2194 g_free(session->mbox);
2195 session->mbox = NULL;
2197 real_path = imap_get_real_path(folder, path);
2199 ok = imap_cmd_select(session, real_path,
2200 exists, recent, unseen, uid_validity, block);
2201 if (ok != IMAP_SUCCESS)
2202 log_warning(_("can't select folder: %s\n"), real_path);
2204 session->mbox = g_strdup(path);
2205 session->folder_content_changed = FALSE;
2212 static gint imap_status(IMAPSession *session, IMAPFolder *folder,
2213 const gchar *path, IMAPFolderItem *item,
2215 guint32 *uid_next, guint32 *uid_validity,
2216 gint *unseen, gboolean block)
2220 struct mailimap_mailbox_data_status * data_status;
2225 real_path = imap_get_real_path(folder, path);
2228 if (time(NULL) - item->last_update >= 5 && item->last_update != 1) {
2229 /* do the full stuff */
2230 item->last_update = 1; /* force update */
2231 debug_print("updating everything\n");
2232 r = imap_status(session, folder, path, item,
2233 &item->c_messages, &item->c_uid_next,
2234 &item->c_uid_validity, &item->c_unseen, block);
2235 if (r != MAILIMAP_NO_ERROR) {
2236 debug_print("status err %d\n", r);
2239 item->last_update = time(NULL);
2241 *messages = item->c_messages;
2243 *uid_next = item->c_uid_next;
2245 *uid_validity = item->c_uid_validity;
2247 *unseen = item->c_unseen;
2249 } else if (time(NULL) - item->last_update < 5) {
2250 /* return cached stuff */
2251 debug_print("using cache\n");
2253 *messages = item->c_messages;
2255 *uid_next = item->c_uid_next;
2257 *uid_validity = item->c_uid_validity;
2259 *unseen = item->c_unseen;
2264 /* if we get there, we're updating cache */
2278 r = imap_threaded_status(FOLDER(folder), real_path,
2279 &data_status, mask);
2282 if (r != MAILIMAP_NO_ERROR) {
2283 debug_print("status err %d\n", r);
2287 if (data_status->st_info_list == NULL) {
2288 mailimap_mailbox_data_status_free(data_status);
2289 debug_print("status->st_info_list == NULL\n");
2294 for(iter = clist_begin(data_status->st_info_list) ; iter != NULL ;
2295 iter = clist_next(iter)) {
2296 struct mailimap_status_info * info;
2298 info = clist_content(iter);
2299 switch (info->st_att) {
2300 case MAILIMAP_STATUS_ATT_MESSAGES:
2301 * messages = info->st_value;
2302 got_values |= 1 << 0;
2305 case MAILIMAP_STATUS_ATT_UIDNEXT:
2306 * uid_next = info->st_value;
2307 got_values |= 1 << 2;
2310 case MAILIMAP_STATUS_ATT_UIDVALIDITY:
2311 * uid_validity = info->st_value;
2312 got_values |= 1 << 3;
2315 case MAILIMAP_STATUS_ATT_UNSEEN:
2316 * unseen = info->st_value;
2317 got_values |= 1 << 4;
2321 mailimap_mailbox_data_status_free(data_status);
2323 if (got_values != mask) {
2324 debug_print("status: incomplete values received (%d)\n", got_values);
2327 return IMAP_SUCCESS;
2330 static void imap_free_capabilities(IMAPSession *session)
2332 slist_free_strings(session->capability);
2333 g_slist_free(session->capability);
2334 session->capability = NULL;
2337 /* low-level IMAP4rev1 commands */
2340 static gint imap_cmd_authenticate(IMAPSession *session, const gchar *user,
2341 const gchar *pass, IMAPAuthType type)
2348 gchar hexdigest[33];
2352 auth_type = "CRAM-MD5";
2354 imap_gen_send(session, "AUTHENTICATE %s", auth_type);
2355 ok = imap_gen_recv(session, &buf);
2356 if (ok != IMAP_SUCCESS || buf[0] != '+' || buf[1] != ' ') {
2361 challenge = g_malloc(strlen(buf + 2) + 1);
2362 challenge_len = base64_decode(challenge, buf + 2, -1);
2363 challenge[challenge_len] = '\0';
2366 md5_hex_hmac(hexdigest, challenge, challenge_len, pass, strlen(pass));
2369 response = g_strdup_printf("%s %s", user, hexdigest);
2370 response64 = g_malloc((strlen(response) + 3) * 2 + 1);
2371 base64_encode(response64, response, strlen(response));
2374 sock_puts(SESSION(session)->sock, response64);
2375 ok = imap_cmd_ok(session, NULL);
2376 if (ok != IMAP_SUCCESS)
2377 log_warning(_("IMAP4 authentication failed.\n"));
2383 static gint imap_cmd_login(IMAPSession *session,
2384 const gchar *user, const gchar *pass,
2390 log_print("IMAP4> Logging %s to %s using %s\n",
2392 SESSION(session)->server,
2394 r = imap_threaded_login(session->folder, user, pass, type);
2395 if (r != MAILIMAP_NO_ERROR) {
2396 log_error("IMAP4< Error logging in to %s\n",
2397 SESSION(session)->server);
2405 static gint imap_cmd_logout(IMAPSession *session)
2407 imap_threaded_disconnect(session->folder);
2409 return IMAP_SUCCESS;
2412 static gint imap_cmd_noop(IMAPSession *session)
2415 unsigned int exists;
2417 r = imap_threaded_noop(session->folder, &exists);
2418 if (r != MAILIMAP_NO_ERROR) {
2419 debug_print("noop err %d\n", r);
2422 session->exists = exists;
2423 session_set_access_time(SESSION(session));
2425 return IMAP_SUCCESS;
2429 static gint imap_cmd_starttls(IMAPSession *session)
2433 r = imap_threaded_starttls(session->folder);
2434 if (r != MAILIMAP_NO_ERROR) {
2435 debug_print("starttls err %d\n", r);
2438 return IMAP_SUCCESS;
2442 static gint imap_cmd_select(IMAPSession *session, const gchar *folder,
2443 gint *exists, gint *recent, gint *unseen,
2444 guint32 *uid_validity, gboolean block)
2448 r = imap_threaded_select(session->folder, folder,
2449 exists, recent, unseen, uid_validity);
2450 if (r != MAILIMAP_NO_ERROR) {
2451 debug_print("select err %d\n", r);
2454 return IMAP_SUCCESS;
2457 static gint imap_cmd_examine(IMAPSession *session, const gchar *folder,
2458 gint *exists, gint *recent, gint *unseen,
2459 guint32 *uid_validity, gboolean block)
2463 r = imap_threaded_examine(session->folder, folder,
2464 exists, recent, unseen, uid_validity);
2465 if (r != MAILIMAP_NO_ERROR) {
2466 debug_print("examine err %d\n", r);
2470 return IMAP_SUCCESS;
2473 static gint imap_cmd_create(IMAPSession *session, const gchar *folder)
2477 r = imap_threaded_create(session->folder, folder);
2478 if (r != MAILIMAP_NO_ERROR) {
2483 return IMAP_SUCCESS;
2486 static gint imap_cmd_rename(IMAPSession *session, const gchar *old_folder,
2487 const gchar *new_folder)
2491 r = imap_threaded_rename(session->folder, old_folder,
2493 if (r != MAILIMAP_NO_ERROR) {
2498 return IMAP_SUCCESS;
2501 static gint imap_cmd_delete(IMAPSession *session, const gchar *folder)
2506 r = imap_threaded_delete(session->folder, folder);
2507 if (r != MAILIMAP_NO_ERROR) {
2512 return IMAP_SUCCESS;
2515 typedef struct _fetch_data {
2516 IMAPSession *session;
2518 const gchar *filename;
2524 static void *imap_cmd_fetch_thread(void *data)
2526 fetch_data *stuff = (fetch_data *)data;
2527 IMAPSession *session = stuff->session;
2528 guint32 uid = stuff->uid;
2529 const gchar *filename = stuff->filename;
2533 r = imap_threaded_fetch_content(session->folder,
2537 r = imap_threaded_fetch_content(session->folder,
2540 if (r != MAILIMAP_NO_ERROR) {
2541 debug_print("fetch err %d\n", r);
2542 return GINT_TO_POINTER(IMAP_ERROR);
2544 return GINT_TO_POINTER(IMAP_SUCCESS);
2547 static gint imap_cmd_fetch(IMAPSession *session, guint32 uid,
2548 const gchar *filename, gboolean headers,
2551 fetch_data *data = g_new0(fetch_data, 1);
2554 data->session = session;
2556 data->filename = filename;
2557 data->headers = headers;
2560 if (prefs_common.work_offline && !inc_offline_should_override()) {
2564 statusbar_print_all(_("Fetching message..."));
2565 result = GPOINTER_TO_INT(imap_cmd_fetch_thread(data));
2566 statusbar_pop_all();
2572 static gint imap_cmd_append(IMAPSession *session, const gchar *destfolder,
2573 const gchar *file, IMAPFlags flags,
2576 struct mailimap_flag_list * flag_list;
2579 g_return_val_if_fail(file != NULL, IMAP_ERROR);
2581 flag_list = imap_flag_to_lep(flags);
2582 r = imap_threaded_append(session->folder, destfolder,
2584 if (new_uid != NULL)
2587 if (r != MAILIMAP_NO_ERROR) {
2588 debug_print("append err %d\n", r);
2591 return IMAP_SUCCESS;
2594 static gint imap_cmd_copy(IMAPSession *session, struct mailimap_set * set,
2595 const gchar *destfolder, GRelation *uid_mapping)
2599 g_return_val_if_fail(session != NULL, IMAP_ERROR);
2600 g_return_val_if_fail(set != NULL, IMAP_ERROR);
2601 g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
2603 r = imap_threaded_copy(session->folder, set, destfolder);
2604 if (r != MAILIMAP_NO_ERROR) {
2609 return IMAP_SUCCESS;
2612 static gint imap_cmd_store(IMAPSession *session, struct mailimap_set * set,
2613 IMAPFlags flags, int do_add)
2616 struct mailimap_flag_list * flag_list;
2617 struct mailimap_store_att_flags * store_att_flags;
2619 flag_list = imap_flag_to_lep(flags);
2623 mailimap_store_att_flags_new_add_flags_silent(flag_list);
2626 mailimap_store_att_flags_new_remove_flags_silent(flag_list);
2628 r = imap_threaded_store(session->folder, set, store_att_flags);
2629 if (r != MAILIMAP_NO_ERROR) {
2634 return IMAP_SUCCESS;
2637 static gint imap_cmd_expunge(IMAPSession *session)
2641 if (prefs_common.work_offline && !inc_offline_should_override()) {
2645 r = imap_threaded_expunge(session->folder);
2646 if (r != MAILIMAP_NO_ERROR) {
2651 return IMAP_SUCCESS;
2654 static void imap_path_separator_subst(gchar *str, gchar separator)
2657 gboolean in_escape = FALSE;
2659 if (!separator || separator == '/') return;
2661 for (p = str; *p != '\0'; p++) {
2662 if (*p == '/' && !in_escape)
2664 else if (*p == '&' && *(p + 1) != '-' && !in_escape)
2666 else if (*p == '-' && in_escape)
2671 static gchar *imap_modified_utf7_to_utf8(const gchar *mutf7_str)
2673 static iconv_t cd = (iconv_t)-1;
2674 static gboolean iconv_ok = TRUE;
2677 size_t norm_utf7_len;
2679 gchar *to_str, *to_p;
2681 gboolean in_escape = FALSE;
2683 if (!iconv_ok) return g_strdup(mutf7_str);
2685 if (cd == (iconv_t)-1) {
2686 cd = iconv_open(CS_INTERNAL, CS_UTF_7);
2687 if (cd == (iconv_t)-1) {
2688 g_warning("iconv cannot convert UTF-7 to %s\n",
2691 return g_strdup(mutf7_str);
2695 /* modified UTF-7 to normal UTF-7 conversion */
2696 norm_utf7 = g_string_new(NULL);
2698 for (p = mutf7_str; *p != '\0'; p++) {
2699 /* replace: '&' -> '+',
2701 escaped ',' -> '/' */
2702 if (!in_escape && *p == '&') {
2703 if (*(p + 1) != '-') {
2704 g_string_append_c(norm_utf7, '+');
2707 g_string_append_c(norm_utf7, '&');
2710 } else if (in_escape && *p == ',') {
2711 g_string_append_c(norm_utf7, '/');
2712 } else if (in_escape && *p == '-') {
2713 g_string_append_c(norm_utf7, '-');
2716 g_string_append_c(norm_utf7, *p);
2720 norm_utf7_p = norm_utf7->str;
2721 norm_utf7_len = norm_utf7->len;
2722 to_len = strlen(mutf7_str) * 5;
2723 to_p = to_str = g_malloc(to_len + 1);
2725 if (iconv(cd, (ICONV_CONST gchar **)&norm_utf7_p, &norm_utf7_len,
2726 &to_p, &to_len) == -1) {
2727 g_warning(_("iconv cannot convert UTF-7 to %s\n"),
2728 conv_get_locale_charset_str());
2729 g_string_free(norm_utf7, TRUE);
2731 return g_strdup(mutf7_str);
2734 /* second iconv() call for flushing */
2735 iconv(cd, NULL, NULL, &to_p, &to_len);
2736 g_string_free(norm_utf7, TRUE);
2742 static gchar *imap_utf8_to_modified_utf7(const gchar *from)
2744 static iconv_t cd = (iconv_t)-1;
2745 static gboolean iconv_ok = TRUE;
2746 gchar *norm_utf7, *norm_utf7_p;
2747 size_t from_len, norm_utf7_len;
2749 gchar *from_tmp, *to, *p;
2750 gboolean in_escape = FALSE;
2752 if (!iconv_ok) return g_strdup(from);
2754 if (cd == (iconv_t)-1) {
2755 cd = iconv_open(CS_UTF_7, CS_INTERNAL);
2756 if (cd == (iconv_t)-1) {
2757 g_warning(_("iconv cannot convert %s to UTF-7\n"),
2760 return g_strdup(from);
2764 /* UTF-8 to normal UTF-7 conversion */
2765 Xstrdup_a(from_tmp, from, return g_strdup(from));
2766 from_len = strlen(from);
2767 norm_utf7_len = from_len * 5;
2768 Xalloca(norm_utf7, norm_utf7_len + 1, return g_strdup(from));
2769 norm_utf7_p = norm_utf7;
2771 #define IS_PRINT(ch) (isprint(ch) && IS_ASCII(ch))
2773 while (from_len > 0) {
2774 if (*from_tmp == '+') {
2775 *norm_utf7_p++ = '+';
2776 *norm_utf7_p++ = '-';
2780 } else if (IS_PRINT(*(guchar *)from_tmp)) {
2781 /* printable ascii char */
2782 *norm_utf7_p = *from_tmp;
2788 size_t conv_len = 0;
2790 /* unprintable char: convert to UTF-7 */
2792 while (!IS_PRINT(*(guchar *)p) && conv_len < from_len) {
2793 conv_len += g_utf8_skip[*(guchar *)p];
2794 p += g_utf8_skip[*(guchar *)p];
2797 from_len -= conv_len;
2798 if (iconv(cd, (ICONV_CONST gchar **)&from_tmp,
2800 &norm_utf7_p, &norm_utf7_len) == -1) {
2801 g_warning(_("iconv cannot convert UTF-8 to UTF-7\n"));
2802 return g_strdup(from);
2805 /* second iconv() call for flushing */
2806 iconv(cd, NULL, NULL, &norm_utf7_p, &norm_utf7_len);
2812 *norm_utf7_p = '\0';
2813 to_str = g_string_new(NULL);
2814 for (p = norm_utf7; p < norm_utf7_p; p++) {
2815 /* replace: '&' -> "&-",
2818 BASE64 '/' -> ',' */
2819 if (!in_escape && *p == '&') {
2820 g_string_append(to_str, "&-");
2821 } else if (!in_escape && *p == '+') {
2822 if (*(p + 1) == '-') {
2823 g_string_append_c(to_str, '+');
2826 g_string_append_c(to_str, '&');
2829 } else if (in_escape && *p == '/') {
2830 g_string_append_c(to_str, ',');
2831 } else if (in_escape && *p == '-') {
2832 g_string_append_c(to_str, '-');
2835 g_string_append_c(to_str, *p);
2841 g_string_append_c(to_str, '-');
2845 g_string_free(to_str, FALSE);
2850 static gboolean imap_rename_folder_func(GNode *node, gpointer data)
2852 FolderItem *item = node->data;
2853 gchar **paths = data;
2854 const gchar *oldpath = paths[0];
2855 const gchar *newpath = paths[1];
2857 gchar *new_itempath;
2860 oldpathlen = strlen(oldpath);
2861 if (strncmp(oldpath, item->path, oldpathlen) != 0) {
2862 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
2866 base = item->path + oldpathlen;
2867 while (*base == G_DIR_SEPARATOR) base++;
2869 new_itempath = g_strdup(newpath);
2871 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
2874 item->path = new_itempath;
2879 typedef struct _get_list_uid_data {
2881 IMAPFolderItem *item;
2882 GSList **msgnum_list;
2884 } get_list_uid_data;
2886 static void *get_list_of_uids_thread(void *data)
2888 get_list_uid_data *stuff = (get_list_uid_data *)data;
2889 Folder *folder = stuff->folder;
2890 IMAPFolderItem *item = stuff->item;
2891 GSList **msgnum_list = stuff->msgnum_list;
2892 gint ok, nummsgs = 0, lastuid_old;
2893 IMAPSession *session;
2894 GSList *uidlist, *elem;
2895 struct mailimap_set * set;
2896 clist * lep_uidlist;
2899 session = imap_session_get(folder);
2900 if (session == NULL) {
2902 return GINT_TO_POINTER(-1);
2905 ok = imap_select(session, IMAP_FOLDER(folder), item->item.path,
2906 NULL, NULL, NULL, NULL, TRUE);
2907 if (ok != IMAP_SUCCESS) {
2909 return GINT_TO_POINTER(-1);
2914 set = mailimap_set_new_interval(item->lastuid + 1, 0);
2916 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SIMPLE, set,
2918 if (r == MAILIMAP_NO_ERROR) {
2919 GSList * fetchuid_list;
2922 imap_uid_list_from_lep(lep_uidlist);
2923 uidlist = g_slist_concat(fetchuid_list, uidlist);
2926 GSList * fetchuid_list;
2927 carray * lep_uidtab;
2929 r = imap_threaded_fetch_uid(folder, item->lastuid + 1,
2931 if (r == MAILIMAP_NO_ERROR) {
2933 imap_uid_list_from_lep_tab(lep_uidtab);
2934 uidlist = g_slist_concat(fetchuid_list, uidlist);
2938 lastuid_old = item->lastuid;
2939 *msgnum_list = g_slist_copy(item->uid_list);
2940 nummsgs = g_slist_length(*msgnum_list);
2941 debug_print("Got %d uids from cache\n", g_slist_length(item->uid_list));
2943 for (elem = uidlist; elem != NULL; elem = g_slist_next(elem)) {
2946 msgnum = GPOINTER_TO_INT(elem->data);
2947 if (msgnum > lastuid_old) {
2948 *msgnum_list = g_slist_prepend(*msgnum_list, GINT_TO_POINTER(msgnum));
2949 item->uid_list = g_slist_prepend(item->uid_list, GINT_TO_POINTER(msgnum));
2952 if(msgnum > item->lastuid)
2953 item->lastuid = msgnum;
2956 g_slist_free(uidlist);
2959 return GINT_TO_POINTER(nummsgs);
2962 static gint get_list_of_uids(Folder *folder, IMAPFolderItem *item, GSList **msgnum_list)
2965 get_list_uid_data *data = g_new0(get_list_uid_data, 1);
2967 data->folder = folder;
2969 data->msgnum_list = msgnum_list;
2971 if (prefs_common.work_offline && !inc_offline_should_override()) {
2976 result = GPOINTER_TO_INT(get_list_of_uids_thread(data));
2982 gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list, gboolean *old_uids_valid)
2984 IMAPFolderItem *item = (IMAPFolderItem *)_item;
2985 IMAPSession *session;
2986 gint ok, nummsgs = 0, exists, uid_val, uid_next;
2987 GSList *uidlist = NULL;
2989 gboolean selected_folder;
2991 debug_print("get_num_list\n");
2993 g_return_val_if_fail(folder != NULL, -1);
2994 g_return_val_if_fail(item != NULL, -1);
2995 g_return_val_if_fail(item->item.path != NULL, -1);
2996 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
2997 g_return_val_if_fail(folder->account != NULL, -1);
2999 session = imap_session_get(folder);
3000 g_return_val_if_fail(session != NULL, -1);
3002 statusbar_print_all("Scanning %s...\n", FOLDER_ITEM(item)->path
3003 ? FOLDER_ITEM(item)->path:"");
3005 selected_folder = (session->mbox != NULL) &&
3006 (!strcmp(session->mbox, item->item.path));
3007 if (selected_folder) {
3008 ok = imap_cmd_noop(session);
3009 if (ok != IMAP_SUCCESS) {
3010 debug_print("disconnected!\n");
3011 session = imap_reconnect_if_possible(folder, session);
3012 if (session == NULL) {
3013 statusbar_pop_all();
3017 exists = session->exists;
3019 *old_uids_valid = TRUE;
3021 if (item->use_cache && time(NULL) - item->use_cache < 2) {
3022 exists = item->c_messages;
3023 uid_next = item->c_uid_next;
3024 uid_val = item->c_uid_validity;
3026 debug_print("using cache %d %d %d\n", exists, uid_next, uid_val);
3028 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path, item,
3029 &exists, &uid_next, &uid_val, NULL, FALSE);
3031 item->use_cache = (time_t)0;
3032 if (ok != IMAP_SUCCESS) {
3033 statusbar_pop_all();
3036 if(item->item.mtime == uid_val)
3037 *old_uids_valid = TRUE;
3039 *old_uids_valid = FALSE;
3041 debug_print("Freeing imap uid cache\n");
3043 g_slist_free(item->uid_list);
3044 item->uid_list = NULL;
3046 item->item.mtime = uid_val;
3048 imap_delete_all_cached_messages((FolderItem *)item);
3052 if (!selected_folder)
3053 item->uid_next = uid_next;
3055 /* If old uid_next matches new uid_next we can be sure no message
3056 was added to the folder */
3057 if (( selected_folder && !session->folder_content_changed) ||
3058 (!selected_folder && uid_next == item->uid_next)) {
3059 nummsgs = g_slist_length(item->uid_list);
3061 /* If number of messages is still the same we
3062 know our caches message numbers are still valid,
3063 otherwise if the number of messages has decrease
3064 we discard our cache to start a new scan to find
3065 out which numbers have been removed */
3066 if (exists == nummsgs) {
3067 *msgnum_list = g_slist_copy(item->uid_list);
3068 statusbar_pop_all();
3070 } else if (exists < nummsgs) {
3071 debug_print("Freeing imap uid cache");
3073 g_slist_free(item->uid_list);
3074 item->uid_list = NULL;
3079 *msgnum_list = NULL;
3080 statusbar_pop_all();
3084 nummsgs = get_list_of_uids(folder, item, &uidlist);
3087 statusbar_pop_all();
3091 if (nummsgs != exists) {
3092 /* Cache contains more messages then folder, we have cached
3093 an old UID of a message that was removed and new messages
3094 have been added too, otherwise the uid_next check would
3096 debug_print("Freeing imap uid cache");
3098 g_slist_free(item->uid_list);
3099 item->uid_list = NULL;
3101 g_slist_free(*msgnum_list);
3103 nummsgs = get_list_of_uids(folder, item, &uidlist);
3106 *msgnum_list = uidlist;
3108 dir = folder_item_get_path((FolderItem *)item);
3109 debug_print("removing old messages from %s\n", dir);
3110 remove_numbered_files_not_in_list(dir, *msgnum_list);
3113 debug_print("get_num_list - ok - %i\n", nummsgs);
3114 statusbar_pop_all();
3118 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item)
3123 flags.perm_flags = MSG_NEW|MSG_UNREAD;
3124 flags.tmp_flags = 0;
3126 g_return_val_if_fail(item != NULL, NULL);
3127 g_return_val_if_fail(file != NULL, NULL);
3129 if (folder_has_parent_of_type(item, F_QUEUE)) {
3130 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
3131 } else if (folder_has_parent_of_type(item, F_DRAFT)) {
3132 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
3135 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3136 if (!msginfo) return NULL;
3138 msginfo->plaintext_file = g_strdup(file);
3139 msginfo->folder = item;
3144 GSList *imap_get_msginfos(Folder *folder, FolderItem *item,
3145 GSList *msgnum_list)
3147 IMAPSession *session;
3148 MsgInfoList *ret = NULL;
3151 debug_print("get_msginfos\n");
3153 g_return_val_if_fail(folder != NULL, NULL);
3154 g_return_val_if_fail(item != NULL, NULL);
3155 g_return_val_if_fail(msgnum_list != NULL, NULL);
3157 session = imap_session_get(folder);
3158 g_return_val_if_fail(session != NULL, NULL);
3160 debug_print("IMAP getting msginfos\n");
3161 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3162 NULL, NULL, NULL, NULL, FALSE);
3163 if (ok != IMAP_SUCCESS)
3166 if (!(folder_has_parent_of_type(item, F_DRAFT) ||
3167 folder_has_parent_of_type(item, F_QUEUE))) {
3168 ret = g_slist_concat(ret,
3169 imap_get_uncached_messages(session, item,
3172 MsgNumberList *sorted_list, *elem;
3173 gint startnum, lastnum;
3175 sorted_list = g_slist_sort(g_slist_copy(msgnum_list), g_int_compare);
3177 startnum = lastnum = GPOINTER_TO_INT(sorted_list->data);
3179 for (elem = sorted_list;; elem = g_slist_next(elem)) {
3183 num = GPOINTER_TO_INT(elem->data);
3185 if (num > lastnum + 1 || elem == NULL) {
3187 for (i = startnum; i <= lastnum; ++i) {
3190 file = imap_fetch_msg(folder, item, i);
3192 MsgInfo *msginfo = imap_parse_msg(file, item);
3193 if (msginfo != NULL) {
3194 msginfo->msgnum = i;
3195 ret = g_slist_append(ret, msginfo);
3209 g_slist_free(sorted_list);
3215 MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid)
3217 MsgInfo *msginfo = NULL;
3218 MsgInfoList *msginfolist;
3219 MsgNumberList numlist;
3221 numlist.next = NULL;
3222 numlist.data = GINT_TO_POINTER(uid);
3224 msginfolist = imap_get_msginfos(folder, item, &numlist);
3225 if (msginfolist != NULL) {
3226 msginfo = msginfolist->data;
3227 g_slist_free(msginfolist);
3233 gboolean imap_scan_required(Folder *folder, FolderItem *_item)
3235 IMAPSession *session;
3236 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3237 gint ok, exists = 0, unseen = 0;
3238 guint32 uid_next, uid_val;
3239 gboolean selected_folder;
3241 g_return_val_if_fail(folder != NULL, FALSE);
3242 g_return_val_if_fail(item != NULL, FALSE);
3243 g_return_val_if_fail(item->item.folder != NULL, FALSE);
3244 g_return_val_if_fail(FOLDER_CLASS(item->item.folder) == &imap_class, FALSE);
3246 if (item->item.path == NULL)
3249 session = imap_session_get(folder);
3250 g_return_val_if_fail(session != NULL, FALSE);
3252 selected_folder = (session->mbox != NULL) &&
3253 (!strcmp(session->mbox, item->item.path));
3254 if (selected_folder) {
3255 ok = imap_cmd_noop(session);
3256 if (ok != IMAP_SUCCESS) {
3257 debug_print("disconnected!\n");
3258 session = imap_reconnect_if_possible(folder, session);
3259 if (session == NULL)
3263 if (session->folder_content_changed
3264 || session->exists != item->item.total_msgs)
3267 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path, IMAP_FOLDER_ITEM(item),
3268 &exists, &uid_next, &uid_val, &unseen, FALSE);
3269 if (ok != IMAP_SUCCESS)
3272 item->use_cache = time(NULL);
3273 item->c_messages = exists;
3274 item->c_uid_next = uid_next;
3275 item->c_uid_validity = uid_val;
3276 item->c_unseen = unseen;
3278 if ((uid_next != item->uid_next) || (exists != item->item.total_msgs))
3285 void imap_change_flags(Folder *folder, FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
3287 IMAPSession *session;
3288 IMAPFlags flags_set = 0, flags_unset = 0;
3289 gint ok = IMAP_SUCCESS;
3290 MsgNumberList numlist;
3291 hashtable_data *ht_data = NULL;
3293 g_return_if_fail(folder != NULL);
3294 g_return_if_fail(folder->klass == &imap_class);
3295 g_return_if_fail(item != NULL);
3296 g_return_if_fail(item->folder == folder);
3297 g_return_if_fail(msginfo != NULL);
3298 g_return_if_fail(msginfo->folder == item);
3300 session = imap_session_get(folder);
3304 if ((ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
3305 NULL, NULL, NULL, NULL, FALSE)) != IMAP_SUCCESS) {
3309 if (!MSG_IS_MARKED(msginfo->flags) && (newflags & MSG_MARKED))
3310 flags_set |= IMAP_FLAG_FLAGGED;
3311 if ( MSG_IS_MARKED(msginfo->flags) && !(newflags & MSG_MARKED))
3312 flags_unset |= IMAP_FLAG_FLAGGED;
3314 if (!MSG_IS_UNREAD(msginfo->flags) && (newflags & MSG_UNREAD))
3315 flags_unset |= IMAP_FLAG_SEEN;
3316 if ( MSG_IS_UNREAD(msginfo->flags) && !(newflags & MSG_UNREAD))
3317 flags_set |= IMAP_FLAG_SEEN;
3319 if (!MSG_IS_REPLIED(msginfo->flags) && (newflags & MSG_REPLIED))
3320 flags_set |= IMAP_FLAG_ANSWERED;
3321 if ( MSG_IS_REPLIED(msginfo->flags) && !(newflags & MSG_REPLIED))
3322 flags_unset |= IMAP_FLAG_ANSWERED;
3324 if (!MSG_IS_DELETED(msginfo->flags) && (newflags & MSG_DELETED))
3325 flags_set |= IMAP_FLAG_DELETED;
3326 if ( MSG_IS_DELETED(msginfo->flags) && !(newflags & MSG_DELETED))
3327 flags_unset |= IMAP_FLAG_DELETED;
3329 numlist.next = NULL;
3330 numlist.data = GINT_TO_POINTER(msginfo->msgnum);
3332 if (IMAP_FOLDER_ITEM(item)->batching) {
3333 /* instead of performing an UID STORE command for each message change,
3334 * as a lot of them can change "together", we just fill in hashtables
3335 * and defer the treatment so that we're able to send only one
3338 debug_print("IMAP batch mode on, deferring flags change\n");
3340 ht_data = g_hash_table_lookup(flags_set_table, GINT_TO_POINTER(flags_set));
3341 if (ht_data == NULL) {
3342 ht_data = g_new0(hashtable_data, 1);
3343 ht_data->session = session;
3344 g_hash_table_insert(flags_set_table, GINT_TO_POINTER(flags_set), ht_data);
3346 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
3347 ht_data->msglist = g_slist_prepend(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum));
3350 ht_data = g_hash_table_lookup(flags_unset_table, GINT_TO_POINTER(flags_unset));
3351 if (ht_data == NULL) {
3352 ht_data = g_new0(hashtable_data, 1);
3353 ht_data->session = session;
3354 g_hash_table_insert(flags_unset_table, GINT_TO_POINTER(flags_unset), ht_data);
3356 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
3357 ht_data->msglist = g_slist_prepend(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum));
3360 debug_print("IMAP changing flags\n");
3362 ok = imap_set_message_flags(session, &numlist, flags_set, TRUE);
3363 if (ok != IMAP_SUCCESS) {
3369 ok = imap_set_message_flags(session, &numlist, flags_unset, FALSE);
3370 if (ok != IMAP_SUCCESS) {
3375 msginfo->flags.perm_flags = newflags;
3380 static gint imap_remove_msg(Folder *folder, FolderItem *item, gint uid)
3383 IMAPSession *session;
3385 MsgNumberList numlist;
3387 g_return_val_if_fail(folder != NULL, -1);
3388 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3389 g_return_val_if_fail(item != NULL, -1);
3391 session = imap_session_get(folder);
3392 if (!session) return -1;
3394 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3395 NULL, NULL, NULL, NULL, FALSE);
3396 if (ok != IMAP_SUCCESS)
3399 numlist.next = NULL;
3400 numlist.data = GINT_TO_POINTER(uid);
3402 ok = imap_set_message_flags
3403 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
3404 &numlist, IMAP_FLAG_DELETED, TRUE);
3405 if (ok != IMAP_SUCCESS) {
3406 log_warning(_("can't set deleted flags: %d\n"), uid);
3410 if (!session->uidplus) {
3411 ok = imap_cmd_expunge(session);
3415 uidstr = g_strdup_printf("%u", uid);
3416 ok = imap_cmd_expunge(session);
3419 if (ok != IMAP_SUCCESS) {
3420 log_warning(_("can't expunge\n"));
3424 IMAP_FOLDER_ITEM(item)->uid_list = g_slist_remove(
3425 IMAP_FOLDER_ITEM(item)->uid_list, numlist.data);
3426 dir = folder_item_get_path(item);
3427 if (is_dir_exist(dir))
3428 remove_numbered_files(dir, uid, uid);
3431 return IMAP_SUCCESS;
3434 static gint compare_msginfo(gconstpointer a, gconstpointer b)
3436 return ((MsgInfo *)a)->msgnum - ((MsgInfo *)b)->msgnum;
3439 static guint gslist_find_next_num(MsgNumberList **list, guint num)
3443 g_return_val_if_fail(list != NULL, -1);
3445 for (elem = *list; elem != NULL; elem = g_slist_next(elem))
3446 if (GPOINTER_TO_INT(elem->data) >= num)
3449 return elem != NULL ? GPOINTER_TO_INT(elem->data) : (gint)-1;
3453 * NEW and DELETED flags are not syncronized
3454 * - The NEW/RECENT flags in IMAP folders can not really be directly
3455 * modified by Sylpheed
3456 * - The DELETE/DELETED flag in IMAP and Sylpheed don't have the same
3457 * meaning, in IMAP it always removes the messages from the FolderItem
3458 * in Sylpheed it can mean to move the message to trash
3461 typedef struct _get_flags_data {
3464 MsgInfoList *msginfo_list;
3465 GRelation *msgflags;
3466 gboolean full_search;