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 *numlist = 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 if (!MSG_IS_DELETED(msginfo->flags))
1260 numlist = g_slist_append(numlist, GINT_TO_POINTER(msginfo->msgnum));
1263 uid_mapping = g_relation_new(2);
1264 g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
1266 ok = imap_set_message_flags
1267 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
1268 numlist, IMAP_FLAG_DELETED, TRUE);
1269 if (ok != IMAP_SUCCESS) {
1270 log_warning(_("can't set deleted flags\n"));
1273 ok = imap_cmd_expunge(session);
1274 if (ok != IMAP_SUCCESS) {
1275 log_warning(_("can't expunge\n"));
1279 g_relation_destroy(uid_mapping);
1280 g_slist_free(numlist);
1284 if (ok == IMAP_SUCCESS)
1290 static gint imap_remove_msgs(Folder *folder, FolderItem *dest,
1291 MsgInfoList *msglist, GRelation *relation)
1295 g_return_val_if_fail(folder != NULL, -1);
1296 g_return_val_if_fail(dest != NULL, -1);
1297 if (msglist == NULL)
1300 msginfo = (MsgInfo *)msglist->data;
1301 g_return_val_if_fail(msginfo->folder != NULL, -1);
1303 return imap_do_remove_msgs(folder, dest, msglist, relation);
1306 static gint imap_remove_all_msg(Folder *folder, FolderItem *item)
1308 GSList *list = folder_item_get_msg_list(item);
1309 gint res = imap_remove_msgs(folder, item, list, NULL);
1310 procmsg_msg_list_free(list);
1314 static gboolean imap_is_msg_changed(Folder *folder, FolderItem *item,
1317 /* TODO: properly implement this method */
1321 static gint imap_close(Folder *folder, FolderItem *item)
1326 static gint imap_scan_tree(Folder *folder)
1328 FolderItem *item = NULL;
1329 IMAPSession *session;
1330 gchar *root_folder = NULL;
1332 g_return_val_if_fail(folder != NULL, -1);
1333 g_return_val_if_fail(folder->account != NULL, -1);
1335 session = imap_session_get(folder);
1337 if (!folder->node) {
1338 folder_tree_destroy(folder);
1339 item = folder_item_new(folder, folder->name, NULL);
1340 item->folder = folder;
1341 folder->node = item->node = g_node_new(item);
1346 if (folder->account->imap_dir && *folder->account->imap_dir) {
1351 Xstrdup_a(root_folder, folder->account->imap_dir, {return -1;});
1352 extract_quote(root_folder, '"');
1353 subst_char(root_folder,
1354 imap_get_path_separator(IMAP_FOLDER(folder),
1357 strtailchomp(root_folder, '/');
1358 real_path = imap_get_real_path
1359 (IMAP_FOLDER(folder), root_folder);
1360 debug_print("IMAP root directory: %s\n", real_path);
1362 /* check if root directory exist */
1364 r = imap_threaded_list(session->folder, "", real_path,
1366 if ((r != MAILIMAP_NO_ERROR) || (clist_count(lep_list) == 0)) {
1367 if (!folder->node) {
1368 item = folder_item_new(folder, folder->name, NULL);
1369 item->folder = folder;
1370 folder->node = item->node = g_node_new(item);
1375 mailimap_list_result_free(lep_list);
1381 item = FOLDER_ITEM(folder->node->data);
1382 if (!item || ((item->path || root_folder) &&
1383 strcmp2(item->path, root_folder) != 0)) {
1384 folder_tree_destroy(folder);
1385 item = folder_item_new(folder, folder->name, root_folder);
1386 item->folder = folder;
1387 folder->node = item->node = g_node_new(item);
1390 imap_scan_tree_recursive(session, FOLDER_ITEM(folder->node->data));
1391 imap_create_missing_folders(folder);
1396 static gint imap_scan_tree_recursive(IMAPSession *session, FolderItem *item)
1399 IMAPFolder *imapfolder;
1400 FolderItem *new_item;
1401 GSList *item_list, *cur;
1404 gchar *wildcard_path;
1410 g_return_val_if_fail(item != NULL, -1);
1411 g_return_val_if_fail(item->folder != NULL, -1);
1412 g_return_val_if_fail(item->no_sub == FALSE, -1);
1414 folder = item->folder;
1415 imapfolder = IMAP_FOLDER(folder);
1417 separator = imap_get_path_separator(imapfolder, item->path);
1419 if (folder->ui_func)
1420 folder->ui_func(folder, item, folder->ui_func_data);
1423 wildcard[0] = separator;
1426 real_path = imap_get_real_path(imapfolder, item->path);
1430 real_path = g_strdup("");
1433 Xstrcat_a(wildcard_path, real_path, wildcard,
1434 {g_free(real_path); return IMAP_ERROR;});
1436 r = imap_threaded_list(folder, "", wildcard_path, &lep_list);
1437 if (r != MAILIMAP_NO_ERROR) {
1441 item_list = imap_list_from_lep(imapfolder,
1442 lep_list, real_path);
1443 mailimap_list_result_free(lep_list);
1448 node = item->node->children;
1449 while (node != NULL) {
1450 FolderItem *old_item = FOLDER_ITEM(node->data);
1451 GNode *next = node->next;
1454 for (cur = item_list; cur != NULL; cur = cur->next) {
1455 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1456 if (!strcmp2(old_item->path, cur_item->path)) {
1457 new_item = cur_item;
1462 debug_print("folder '%s' not found. removing...\n",
1464 folder_item_remove(old_item);
1466 old_item->no_sub = new_item->no_sub;
1467 old_item->no_select = new_item->no_select;
1468 if (old_item->no_sub == TRUE && node->children) {
1469 debug_print("folder '%s' doesn't have "
1470 "subfolders. removing...\n",
1472 folder_item_remove_children(old_item);
1479 for (cur = item_list; cur != NULL; cur = cur->next) {
1480 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1482 for (node = item->node->children; node != NULL;
1483 node = node->next) {
1484 if (!strcmp2(FOLDER_ITEM(node->data)->path,
1486 new_item = FOLDER_ITEM(node->data);
1487 folder_item_destroy(cur_item);
1493 new_item = cur_item;
1494 debug_print("new folder '%s' found.\n", new_item->path);
1495 folder_item_append(item, new_item);
1498 if (!strcmp(new_item->path, "INBOX")) {
1499 new_item->stype = F_INBOX;
1500 folder->inbox = new_item;
1501 } else if (!folder_item_parent(item) || item->stype == F_INBOX) {
1504 base = g_path_get_basename(new_item->path);
1506 if (!folder->outbox && !g_ascii_strcasecmp(base, "Sent")) {
1507 new_item->stype = F_OUTBOX;
1508 folder->outbox = new_item;
1509 } else if (!folder->draft && !g_ascii_strcasecmp(base, "Drafts")) {
1510 new_item->stype = F_DRAFT;
1511 folder->draft = new_item;
1512 } else if (!folder->queue && !g_ascii_strcasecmp(base, "Queue")) {
1513 new_item->stype = F_QUEUE;
1514 folder->queue = new_item;
1515 } else if (!folder->trash && !g_ascii_strcasecmp(base, "Trash")) {
1516 new_item->stype = F_TRASH;
1517 folder->trash = new_item;
1522 if (new_item->no_sub == FALSE)
1523 imap_scan_tree_recursive(session, new_item);
1526 g_slist_free(item_list);
1528 return IMAP_SUCCESS;
1531 static gint imap_create_tree(Folder *folder)
1533 g_return_val_if_fail(folder != NULL, -1);
1534 g_return_val_if_fail(folder->node != NULL, -1);
1535 g_return_val_if_fail(folder->node->data != NULL, -1);
1536 g_return_val_if_fail(folder->account != NULL, -1);
1538 imap_scan_tree(folder);
1539 imap_create_missing_folders(folder);
1544 static void imap_create_missing_folders(Folder *folder)
1546 g_return_if_fail(folder != NULL);
1549 folder->inbox = imap_create_special_folder
1550 (folder, F_INBOX, "INBOX");
1552 folder->trash = imap_create_special_folder
1553 (folder, F_TRASH, "Trash");
1555 folder->queue = imap_create_special_folder
1556 (folder, F_QUEUE, "Queue");
1557 if (!folder->outbox)
1558 folder->outbox = imap_create_special_folder
1559 (folder, F_OUTBOX, "Sent");
1561 folder->draft = imap_create_special_folder
1562 (folder, F_DRAFT, "Drafts");
1565 static FolderItem *imap_create_special_folder(Folder *folder,
1566 SpecialFolderItemType stype,
1570 FolderItem *new_item;
1572 g_return_val_if_fail(folder != NULL, NULL);
1573 g_return_val_if_fail(folder->node != NULL, NULL);
1574 g_return_val_if_fail(folder->node->data != NULL, NULL);
1575 g_return_val_if_fail(folder->account != NULL, NULL);
1576 g_return_val_if_fail(name != NULL, NULL);
1578 item = FOLDER_ITEM(folder->node->data);
1579 new_item = imap_create_folder(folder, item, name);
1582 g_warning("Can't create '%s'\n", name);
1583 if (!folder->inbox) return NULL;
1585 new_item = imap_create_folder(folder, folder->inbox, name);
1587 g_warning("Can't create '%s' under INBOX\n", name);
1589 new_item->stype = stype;
1591 new_item->stype = stype;
1596 static gchar *imap_folder_get_path(Folder *folder)
1600 g_return_val_if_fail(folder != NULL, NULL);
1601 g_return_val_if_fail(folder->account != NULL, NULL);
1603 folder_path = g_strconcat(get_imap_cache_dir(),
1605 folder->account->recv_server,
1607 folder->account->userid,
1613 static gchar *imap_item_get_path(Folder *folder, FolderItem *item)
1615 gchar *folder_path, *path;
1617 g_return_val_if_fail(folder != NULL, NULL);
1618 g_return_val_if_fail(item != NULL, NULL);
1619 folder_path = imap_folder_get_path(folder);
1621 g_return_val_if_fail(folder_path != NULL, NULL);
1622 if (folder_path[0] == G_DIR_SEPARATOR) {
1624 path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
1627 path = g_strdup(folder_path);
1630 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1631 folder_path, G_DIR_SEPARATOR_S,
1634 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1637 g_free(folder_path);
1642 static FolderItem *imap_create_folder(Folder *folder, FolderItem *parent,
1645 gchar *dirpath, *imap_path;
1646 IMAPSession *session;
1647 FolderItem *new_item;
1653 g_return_val_if_fail(folder != NULL, NULL);
1654 g_return_val_if_fail(folder->account != NULL, NULL);
1655 g_return_val_if_fail(parent != NULL, NULL);
1656 g_return_val_if_fail(name != NULL, NULL);
1658 session = imap_session_get(folder);
1663 if (!folder_item_parent(parent) && strcmp(name, "INBOX") == 0)
1664 dirpath = g_strdup(name);
1665 else if (parent->path)
1666 dirpath = g_strconcat(parent->path, "/", name, NULL);
1667 else if ((p = strchr(name, '/')) != NULL && *(p + 1) != '\0')
1668 dirpath = g_strdup(name);
1669 else if (folder->account->imap_dir && *folder->account->imap_dir) {
1672 Xstrdup_a(imap_dir, folder->account->imap_dir, {return NULL;});
1673 strtailchomp(imap_dir, '/');
1674 dirpath = g_strconcat(imap_dir, "/", name, NULL);
1676 dirpath = g_strdup(name);
1678 /* keep trailing directory separator to create a folder that contains
1680 imap_path = imap_utf8_to_modified_utf7(dirpath);
1681 strtailchomp(dirpath, '/');
1682 Xstrdup_a(new_name, name, {
1686 strtailchomp(new_name, '/');
1687 separator = imap_get_path_separator(IMAP_FOLDER(folder), imap_path);
1688 imap_path_separator_subst(imap_path, separator);
1689 subst_char(new_name, '/', separator);
1691 if (strcmp(name, "INBOX") != 0) {
1693 gboolean exist = FALSE;
1697 argbuf = g_ptr_array_new();
1698 r = imap_threaded_list(folder, "", imap_path, &lep_list);
1699 if (r != MAILIMAP_NO_ERROR) {
1700 log_warning(_("can't create mailbox: LIST failed\n"));
1703 ptr_array_free_strings(argbuf);
1704 g_ptr_array_free(argbuf, TRUE);
1708 if (clist_count(lep_list) > 0)
1712 ok = imap_cmd_create(session, imap_path);
1713 if (ok != IMAP_SUCCESS) {
1714 log_warning(_("can't create mailbox\n"));
1722 new_item = folder_item_new(folder, new_name, dirpath);
1723 folder_item_append(parent, new_item);
1727 dirpath = folder_item_get_path(new_item);
1728 if (!is_dir_exist(dirpath))
1729 make_dir_hier(dirpath);
1735 static gint imap_rename_folder(Folder *folder, FolderItem *item,
1740 gchar *real_oldpath;
1741 gchar *real_newpath;
1743 gchar *old_cache_dir;
1744 gchar *new_cache_dir;
1745 IMAPSession *session;
1748 gint exists, recent, unseen;
1749 guint32 uid_validity;
1751 g_return_val_if_fail(folder != NULL, -1);
1752 g_return_val_if_fail(item != NULL, -1);
1753 g_return_val_if_fail(item->path != NULL, -1);
1754 g_return_val_if_fail(name != NULL, -1);
1756 if (strchr(name, imap_get_path_separator(IMAP_FOLDER(folder), item->path)) != NULL) {
1757 g_warning(_("New folder name must not contain the namespace "
1762 session = imap_session_get(folder);
1766 real_oldpath = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1768 g_free(session->mbox);
1769 session->mbox = NULL;
1770 ok = imap_cmd_examine(session, "INBOX",
1771 &exists, &recent, &unseen, &uid_validity, FALSE);
1772 if (ok != IMAP_SUCCESS) {
1773 g_free(real_oldpath);
1777 separator = imap_get_path_separator(IMAP_FOLDER(folder), item->path);
1778 if (strchr(item->path, G_DIR_SEPARATOR)) {
1779 dirpath = g_path_get_dirname(item->path);
1780 newpath = g_strconcat(dirpath, G_DIR_SEPARATOR_S, name, NULL);
1783 newpath = g_strdup(name);
1785 real_newpath = imap_utf8_to_modified_utf7(newpath);
1786 imap_path_separator_subst(real_newpath, separator);
1788 ok = imap_cmd_rename(session, real_oldpath, real_newpath);
1789 if (ok != IMAP_SUCCESS) {
1790 log_warning(_("can't rename mailbox: %s to %s\n"),
1791 real_oldpath, real_newpath);
1792 g_free(real_oldpath);
1794 g_free(real_newpath);
1799 item->name = g_strdup(name);
1801 old_cache_dir = folder_item_get_path(item);
1803 paths[0] = g_strdup(item->path);
1805 g_node_traverse(item->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1806 imap_rename_folder_func, paths);
1808 if (is_dir_exist(old_cache_dir)) {
1809 new_cache_dir = folder_item_get_path(item);
1810 if (rename(old_cache_dir, new_cache_dir) < 0) {
1811 FILE_OP_ERROR(old_cache_dir, "rename");
1813 g_free(new_cache_dir);
1816 g_free(old_cache_dir);
1819 g_free(real_oldpath);
1820 g_free(real_newpath);
1825 static gint imap_remove_folder_real(Folder *folder, FolderItem *item)
1828 IMAPSession *session;
1831 gint exists, recent, unseen;
1832 guint32 uid_validity;
1834 g_return_val_if_fail(folder != NULL, -1);
1835 g_return_val_if_fail(item != NULL, -1);
1836 g_return_val_if_fail(item->path != NULL, -1);
1838 session = imap_session_get(folder);
1842 path = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1844 ok = imap_cmd_examine(session, "INBOX",
1845 &exists, &recent, &unseen, &uid_validity, FALSE);
1846 if (ok != IMAP_SUCCESS) {
1851 ok = imap_cmd_delete(session, path);
1852 if (ok != IMAP_SUCCESS) {
1853 log_warning(_("can't delete mailbox\n"));
1859 cache_dir = folder_item_get_path(item);
1860 if (is_dir_exist(cache_dir) && remove_dir_recursive(cache_dir) < 0)
1861 g_warning("can't remove directory '%s'\n", cache_dir);
1863 folder_item_remove(item);
1868 static gint imap_remove_folder(Folder *folder, FolderItem *item)
1872 g_return_val_if_fail(item != NULL, -1);
1873 g_return_val_if_fail(item->folder != NULL, -1);
1874 g_return_val_if_fail(item->node != NULL, -1);
1876 node = item->node->children;
1877 while (node != NULL) {
1879 if (imap_remove_folder(folder, FOLDER_ITEM(node->data)) < 0)
1883 debug_print("IMAP removing %s\n", item->path);
1885 if (imap_remove_all_msg(folder, item) < 0)
1887 return imap_remove_folder_real(folder, item);
1890 typedef struct _uncached_data {
1891 IMAPSession *session;
1893 MsgNumberList *numlist;
1899 static void *imap_get_uncached_messages_thread(void *data)
1901 uncached_data *stuff = (uncached_data *)data;
1902 IMAPSession *session = stuff->session;
1903 FolderItem *item = stuff->item;
1904 MsgNumberList *numlist = stuff->numlist;
1906 GSList *newlist = NULL;
1907 GSList *llast = NULL;
1908 GSList *seq_list, *cur;
1910 debug_print("uncached_messages\n");
1912 if (session == NULL || item == NULL || item->folder == NULL
1913 || FOLDER_CLASS(item->folder) != &imap_class) {
1918 seq_list = imap_get_lep_set_from_numlist(numlist);
1919 debug_print("get msgs info\n");
1920 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
1921 struct mailimap_set * imapset;
1927 imapset = cur->data;
1929 r = imap_threaded_fetch_env(session->folder,
1930 imapset, &env_list);
1931 if (r != MAILIMAP_NO_ERROR)
1935 for(i = 0 ; i < carray_count(env_list) ; i ++) {
1936 struct imap_fetch_env_info * info;
1939 info = carray_get(env_list, i);
1940 msginfo = imap_envelope_from_lep(info, item);
1941 msginfo->folder = item;
1943 llast = newlist = g_slist_append(newlist, msginfo);
1945 llast = g_slist_append(llast, msginfo);
1946 llast = llast->next;
1951 imap_fetch_env_free(env_list);
1954 session_set_access_time(SESSION(session));
1959 #define MAX_MSG_NUM 50
1961 static GSList *imap_get_uncached_messages(IMAPSession *session,
1963 MsgNumberList *numlist)
1965 GSList *result = NULL;
1967 uncached_data *data = g_new0(uncached_data, 1);
1972 data->total = g_slist_length(numlist);
1973 debug_print("messages list : %i\n", data->total);
1975 while (cur != NULL) {
1976 GSList * partial_result;
1984 while (count < MAX_MSG_NUM) {
1989 if (newlist == NULL)
1990 llast = newlist = g_slist_append(newlist, p);
1992 llast = g_slist_append(llast, p);
1993 llast = llast->next;
2003 data->session = session;
2005 data->numlist = newlist;
2008 if (prefs_common.work_offline && !inc_offline_should_override()) {
2014 (GSList *)imap_get_uncached_messages_thread(data);
2016 statusbar_progress_all(data->cur,data->total, 1);
2018 g_slist_free(newlist);
2020 result = g_slist_concat(result, partial_result);
2024 statusbar_progress_all(0,0,0);
2025 statusbar_pop_all();
2030 static void imap_delete_all_cached_messages(FolderItem *item)
2034 g_return_if_fail(item != NULL);
2035 g_return_if_fail(item->folder != NULL);
2036 g_return_if_fail(FOLDER_CLASS(item->folder) == &imap_class);
2038 debug_print("Deleting all cached messages...\n");
2040 dir = folder_item_get_path(item);
2041 if (is_dir_exist(dir))
2042 remove_all_numbered_files(dir);
2045 debug_print("done.\n");
2048 static IMAPNameSpace *imap_find_namespace_from_list(GList *ns_list,
2051 IMAPNameSpace *namespace = NULL;
2052 gchar *tmp_path, *name;
2054 if (!path) path = "";
2056 for (; ns_list != NULL; ns_list = ns_list->next) {
2057 IMAPNameSpace *tmp_ns = ns_list->data;
2059 Xstrcat_a(tmp_path, path, "/", return namespace);
2060 Xstrdup_a(name, tmp_ns->name, return namespace);
2061 if (tmp_ns->separator && tmp_ns->separator != '/') {
2062 subst_char(tmp_path, tmp_ns->separator, '/');
2063 subst_char(name, tmp_ns->separator, '/');
2065 if (strncmp(tmp_path, name, strlen(name)) == 0)
2072 static IMAPNameSpace *imap_find_namespace(IMAPFolder *folder,
2075 IMAPNameSpace *namespace;
2077 g_return_val_if_fail(folder != NULL, NULL);
2079 namespace = imap_find_namespace_from_list(folder->ns_personal, path);
2080 if (namespace) return namespace;
2081 namespace = imap_find_namespace_from_list(folder->ns_others, path);
2082 if (namespace) return namespace;
2083 namespace = imap_find_namespace_from_list(folder->ns_shared, path);
2084 if (namespace) return namespace;
2090 static gchar imap_get_path_separator(IMAPFolder *folder, const gchar *path)
2092 IMAPNameSpace *namespace;
2093 gchar separator = '/';
2095 if (folder->last_seen_separator == 0) {
2097 int r = imap_threaded_list((Folder *)folder, "", "", &lep_list);
2098 if (r != MAILIMAP_NO_ERROR) {
2099 log_warning(_("LIST failed\n"));
2103 if (clist_count(lep_list) > 0) {
2104 clistiter * iter = clist_begin(lep_list);
2105 struct mailimap_mailbox_list * mb;
2106 mb = clist_content(iter);
2108 folder->last_seen_separator = mb->mb_delimiter;
2109 debug_print("got separator: %c\n", folder->last_seen_separator);
2111 mailimap_list_result_free(lep_list);
2114 if (folder->last_seen_separator != 0) {
2115 debug_print("using separator: %c\n", folder->last_seen_separator);
2116 return folder->last_seen_separator;
2119 namespace = imap_find_namespace(folder, path);
2120 if (namespace && namespace->separator)
2121 separator = namespace->separator;
2126 static gchar *imap_get_real_path(IMAPFolder *folder, const gchar *path)
2131 g_return_val_if_fail(folder != NULL, NULL);
2132 g_return_val_if_fail(path != NULL, NULL);
2134 real_path = imap_utf8_to_modified_utf7(path);
2135 separator = imap_get_path_separator(folder, path);
2136 imap_path_separator_subst(real_path, separator);
2141 static gint imap_set_message_flags(IMAPSession *session,
2142 MsgNumberList *numlist,
2150 seq_list = imap_get_lep_set_from_numlist(numlist);
2152 for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) {
2153 struct mailimap_set * imapset;
2155 imapset = cur->data;
2157 ok = imap_cmd_store(session, imapset,
2161 imap_lep_set_free(seq_list);
2163 return IMAP_SUCCESS;
2166 typedef struct _select_data {
2167 IMAPSession *session;
2172 guint32 *uid_validity;
2176 static gint imap_select(IMAPSession *session, IMAPFolder *folder,
2178 gint *exists, gint *recent, gint *unseen,
2179 guint32 *uid_validity, gboolean block)
2183 gint exists_, recent_, unseen_;
2184 guint32 uid_validity_;
2186 if (!exists || !recent || !unseen || !uid_validity) {
2187 if (session->mbox && strcmp(session->mbox, path) == 0)
2188 return IMAP_SUCCESS;
2192 uid_validity = &uid_validity_;
2195 g_free(session->mbox);
2196 session->mbox = NULL;
2198 real_path = imap_get_real_path(folder, path);
2200 ok = imap_cmd_select(session, real_path,
2201 exists, recent, unseen, uid_validity, block);
2202 if (ok != IMAP_SUCCESS)
2203 log_warning(_("can't select folder: %s\n"), real_path);
2205 session->mbox = g_strdup(path);
2206 session->folder_content_changed = FALSE;
2213 static gint imap_status(IMAPSession *session, IMAPFolder *folder,
2214 const gchar *path, IMAPFolderItem *item,
2216 guint32 *uid_next, guint32 *uid_validity,
2217 gint *unseen, gboolean block)
2221 struct mailimap_mailbox_data_status * data_status;
2226 real_path = imap_get_real_path(folder, path);
2229 if (time(NULL) - item->last_update >= 5 && item->last_update != 1) {
2230 /* do the full stuff */
2231 item->last_update = 1; /* force update */
2232 debug_print("updating everything\n");
2233 r = imap_status(session, folder, path, item,
2234 &item->c_messages, &item->c_uid_next,
2235 &item->c_uid_validity, &item->c_unseen, block);
2236 if (r != MAILIMAP_NO_ERROR) {
2237 debug_print("status err %d\n", r);
2240 item->last_update = time(NULL);
2242 *messages = item->c_messages;
2244 *uid_next = item->c_uid_next;
2246 *uid_validity = item->c_uid_validity;
2248 *unseen = item->c_unseen;
2250 } else if (time(NULL) - item->last_update < 5) {
2251 /* return cached stuff */
2252 debug_print("using cache\n");
2254 *messages = item->c_messages;
2256 *uid_next = item->c_uid_next;
2258 *uid_validity = item->c_uid_validity;
2260 *unseen = item->c_unseen;
2265 /* if we get there, we're updating cache */
2279 r = imap_threaded_status(FOLDER(folder), real_path,
2280 &data_status, mask);
2283 if (r != MAILIMAP_NO_ERROR) {
2284 debug_print("status err %d\n", r);
2288 if (data_status->st_info_list == NULL) {
2289 mailimap_mailbox_data_status_free(data_status);
2290 debug_print("status->st_info_list == NULL\n");
2295 for(iter = clist_begin(data_status->st_info_list) ; iter != NULL ;
2296 iter = clist_next(iter)) {
2297 struct mailimap_status_info * info;
2299 info = clist_content(iter);
2300 switch (info->st_att) {
2301 case MAILIMAP_STATUS_ATT_MESSAGES:
2302 * messages = info->st_value;
2303 got_values |= 1 << 0;
2306 case MAILIMAP_STATUS_ATT_UIDNEXT:
2307 * uid_next = info->st_value;
2308 got_values |= 1 << 2;
2311 case MAILIMAP_STATUS_ATT_UIDVALIDITY:
2312 * uid_validity = info->st_value;
2313 got_values |= 1 << 3;
2316 case MAILIMAP_STATUS_ATT_UNSEEN:
2317 * unseen = info->st_value;
2318 got_values |= 1 << 4;
2322 mailimap_mailbox_data_status_free(data_status);
2324 if (got_values != mask) {
2325 debug_print("status: incomplete values received (%d)\n", got_values);
2328 return IMAP_SUCCESS;
2331 static void imap_free_capabilities(IMAPSession *session)
2333 slist_free_strings(session->capability);
2334 g_slist_free(session->capability);
2335 session->capability = NULL;
2338 /* low-level IMAP4rev1 commands */
2341 static gint imap_cmd_authenticate(IMAPSession *session, const gchar *user,
2342 const gchar *pass, IMAPAuthType type)
2349 gchar hexdigest[33];
2353 auth_type = "CRAM-MD5";
2355 imap_gen_send(session, "AUTHENTICATE %s", auth_type);
2356 ok = imap_gen_recv(session, &buf);
2357 if (ok != IMAP_SUCCESS || buf[0] != '+' || buf[1] != ' ') {
2362 challenge = g_malloc(strlen(buf + 2) + 1);
2363 challenge_len = base64_decode(challenge, buf + 2, -1);
2364 challenge[challenge_len] = '\0';
2367 md5_hex_hmac(hexdigest, challenge, challenge_len, pass, strlen(pass));
2370 response = g_strdup_printf("%s %s", user, hexdigest);
2371 response64 = g_malloc((strlen(response) + 3) * 2 + 1);
2372 base64_encode(response64, response, strlen(response));
2375 sock_puts(SESSION(session)->sock, response64);
2376 ok = imap_cmd_ok(session, NULL);
2377 if (ok != IMAP_SUCCESS)
2378 log_warning(_("IMAP4 authentication failed.\n"));
2384 static gint imap_cmd_login(IMAPSession *session,
2385 const gchar *user, const gchar *pass,
2391 log_print("IMAP4> Logging %s to %s using %s\n",
2393 SESSION(session)->server,
2395 r = imap_threaded_login(session->folder, user, pass, type);
2396 if (r != MAILIMAP_NO_ERROR) {
2397 log_error("IMAP4< Error logging in to %s\n",
2398 SESSION(session)->server);
2406 static gint imap_cmd_logout(IMAPSession *session)
2408 imap_threaded_disconnect(session->folder);
2410 return IMAP_SUCCESS;
2413 static gint imap_cmd_noop(IMAPSession *session)
2416 unsigned int exists;
2418 r = imap_threaded_noop(session->folder, &exists);
2419 if (r != MAILIMAP_NO_ERROR) {
2420 debug_print("noop err %d\n", r);
2423 session->exists = exists;
2424 session_set_access_time(SESSION(session));
2426 return IMAP_SUCCESS;
2430 static gint imap_cmd_starttls(IMAPSession *session)
2434 r = imap_threaded_starttls(session->folder);
2435 if (r != MAILIMAP_NO_ERROR) {
2436 debug_print("starttls err %d\n", r);
2439 return IMAP_SUCCESS;
2443 static gint imap_cmd_select(IMAPSession *session, const gchar *folder,
2444 gint *exists, gint *recent, gint *unseen,
2445 guint32 *uid_validity, gboolean block)
2449 r = imap_threaded_select(session->folder, folder,
2450 exists, recent, unseen, uid_validity);
2451 if (r != MAILIMAP_NO_ERROR) {
2452 debug_print("select err %d\n", r);
2455 return IMAP_SUCCESS;
2458 static gint imap_cmd_examine(IMAPSession *session, const gchar *folder,
2459 gint *exists, gint *recent, gint *unseen,
2460 guint32 *uid_validity, gboolean block)
2464 r = imap_threaded_examine(session->folder, folder,
2465 exists, recent, unseen, uid_validity);
2466 if (r != MAILIMAP_NO_ERROR) {
2467 debug_print("examine err %d\n", r);
2471 return IMAP_SUCCESS;
2474 static gint imap_cmd_create(IMAPSession *session, const gchar *folder)
2478 r = imap_threaded_create(session->folder, folder);
2479 if (r != MAILIMAP_NO_ERROR) {
2484 return IMAP_SUCCESS;
2487 static gint imap_cmd_rename(IMAPSession *session, const gchar *old_folder,
2488 const gchar *new_folder)
2492 r = imap_threaded_rename(session->folder, old_folder,
2494 if (r != MAILIMAP_NO_ERROR) {
2499 return IMAP_SUCCESS;
2502 static gint imap_cmd_delete(IMAPSession *session, const gchar *folder)
2507 r = imap_threaded_delete(session->folder, folder);
2508 if (r != MAILIMAP_NO_ERROR) {
2513 return IMAP_SUCCESS;
2516 typedef struct _fetch_data {
2517 IMAPSession *session;
2519 const gchar *filename;
2525 static void *imap_cmd_fetch_thread(void *data)
2527 fetch_data *stuff = (fetch_data *)data;
2528 IMAPSession *session = stuff->session;
2529 guint32 uid = stuff->uid;
2530 const gchar *filename = stuff->filename;
2534 r = imap_threaded_fetch_content(session->folder,
2538 r = imap_threaded_fetch_content(session->folder,
2541 if (r != MAILIMAP_NO_ERROR) {
2542 debug_print("fetch err %d\n", r);
2543 return GINT_TO_POINTER(IMAP_ERROR);
2545 return GINT_TO_POINTER(IMAP_SUCCESS);
2548 static gint imap_cmd_fetch(IMAPSession *session, guint32 uid,
2549 const gchar *filename, gboolean headers,
2552 fetch_data *data = g_new0(fetch_data, 1);
2555 data->session = session;
2557 data->filename = filename;
2558 data->headers = headers;
2561 if (prefs_common.work_offline && !inc_offline_should_override()) {
2565 statusbar_print_all(_("Fetching message..."));
2566 result = GPOINTER_TO_INT(imap_cmd_fetch_thread(data));
2567 statusbar_pop_all();
2573 static gint imap_cmd_append(IMAPSession *session, const gchar *destfolder,
2574 const gchar *file, IMAPFlags flags,
2577 struct mailimap_flag_list * flag_list;
2580 g_return_val_if_fail(file != NULL, IMAP_ERROR);
2582 flag_list = imap_flag_to_lep(flags);
2583 r = imap_threaded_append(session->folder, destfolder,
2585 if (new_uid != NULL)
2588 if (r != MAILIMAP_NO_ERROR) {
2589 debug_print("append err %d\n", r);
2592 return IMAP_SUCCESS;
2595 static gint imap_cmd_copy(IMAPSession *session, struct mailimap_set * set,
2596 const gchar *destfolder, GRelation *uid_mapping)
2600 g_return_val_if_fail(session != NULL, IMAP_ERROR);
2601 g_return_val_if_fail(set != NULL, IMAP_ERROR);
2602 g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
2604 r = imap_threaded_copy(session->folder, set, destfolder);
2605 if (r != MAILIMAP_NO_ERROR) {
2610 return IMAP_SUCCESS;
2613 static gint imap_cmd_store(IMAPSession *session, struct mailimap_set * set,
2614 IMAPFlags flags, int do_add)
2617 struct mailimap_flag_list * flag_list;
2618 struct mailimap_store_att_flags * store_att_flags;
2620 flag_list = imap_flag_to_lep(flags);
2624 mailimap_store_att_flags_new_add_flags_silent(flag_list);
2627 mailimap_store_att_flags_new_remove_flags_silent(flag_list);
2629 r = imap_threaded_store(session->folder, set, store_att_flags);
2630 if (r != MAILIMAP_NO_ERROR) {
2635 return IMAP_SUCCESS;
2638 static gint imap_cmd_expunge(IMAPSession *session)
2642 if (prefs_common.work_offline && !inc_offline_should_override()) {
2646 r = imap_threaded_expunge(session->folder);
2647 if (r != MAILIMAP_NO_ERROR) {
2652 return IMAP_SUCCESS;
2655 static void imap_path_separator_subst(gchar *str, gchar separator)
2658 gboolean in_escape = FALSE;
2660 if (!separator || separator == '/') return;
2662 for (p = str; *p != '\0'; p++) {
2663 if (*p == '/' && !in_escape)
2665 else if (*p == '&' && *(p + 1) != '-' && !in_escape)
2667 else if (*p == '-' && in_escape)
2672 static gchar *imap_modified_utf7_to_utf8(const gchar *mutf7_str)
2674 static iconv_t cd = (iconv_t)-1;
2675 static gboolean iconv_ok = TRUE;
2678 size_t norm_utf7_len;
2680 gchar *to_str, *to_p;
2682 gboolean in_escape = FALSE;
2684 if (!iconv_ok) return g_strdup(mutf7_str);
2686 if (cd == (iconv_t)-1) {
2687 cd = iconv_open(CS_INTERNAL, CS_UTF_7);
2688 if (cd == (iconv_t)-1) {
2689 g_warning("iconv cannot convert UTF-7 to %s\n",
2692 return g_strdup(mutf7_str);
2696 /* modified UTF-7 to normal UTF-7 conversion */
2697 norm_utf7 = g_string_new(NULL);
2699 for (p = mutf7_str; *p != '\0'; p++) {
2700 /* replace: '&' -> '+',
2702 escaped ',' -> '/' */
2703 if (!in_escape && *p == '&') {
2704 if (*(p + 1) != '-') {
2705 g_string_append_c(norm_utf7, '+');
2708 g_string_append_c(norm_utf7, '&');
2711 } else if (in_escape && *p == ',') {
2712 g_string_append_c(norm_utf7, '/');
2713 } else if (in_escape && *p == '-') {
2714 g_string_append_c(norm_utf7, '-');
2717 g_string_append_c(norm_utf7, *p);
2721 norm_utf7_p = norm_utf7->str;
2722 norm_utf7_len = norm_utf7->len;
2723 to_len = strlen(mutf7_str) * 5;
2724 to_p = to_str = g_malloc(to_len + 1);
2726 if (iconv(cd, (ICONV_CONST gchar **)&norm_utf7_p, &norm_utf7_len,
2727 &to_p, &to_len) == -1) {
2728 g_warning(_("iconv cannot convert UTF-7 to %s\n"),
2729 conv_get_locale_charset_str());
2730 g_string_free(norm_utf7, TRUE);
2732 return g_strdup(mutf7_str);
2735 /* second iconv() call for flushing */
2736 iconv(cd, NULL, NULL, &to_p, &to_len);
2737 g_string_free(norm_utf7, TRUE);
2743 static gchar *imap_utf8_to_modified_utf7(const gchar *from)
2745 static iconv_t cd = (iconv_t)-1;
2746 static gboolean iconv_ok = TRUE;
2747 gchar *norm_utf7, *norm_utf7_p;
2748 size_t from_len, norm_utf7_len;
2750 gchar *from_tmp, *to, *p;
2751 gboolean in_escape = FALSE;
2753 if (!iconv_ok) return g_strdup(from);
2755 if (cd == (iconv_t)-1) {
2756 cd = iconv_open(CS_UTF_7, CS_INTERNAL);
2757 if (cd == (iconv_t)-1) {
2758 g_warning(_("iconv cannot convert %s to UTF-7\n"),
2761 return g_strdup(from);
2765 /* UTF-8 to normal UTF-7 conversion */
2766 Xstrdup_a(from_tmp, from, return g_strdup(from));
2767 from_len = strlen(from);
2768 norm_utf7_len = from_len * 5;
2769 Xalloca(norm_utf7, norm_utf7_len + 1, return g_strdup(from));
2770 norm_utf7_p = norm_utf7;
2772 #define IS_PRINT(ch) (isprint(ch) && IS_ASCII(ch))
2774 while (from_len > 0) {
2775 if (*from_tmp == '+') {
2776 *norm_utf7_p++ = '+';
2777 *norm_utf7_p++ = '-';
2781 } else if (IS_PRINT(*(guchar *)from_tmp)) {
2782 /* printable ascii char */
2783 *norm_utf7_p = *from_tmp;
2789 size_t conv_len = 0;
2791 /* unprintable char: convert to UTF-7 */
2793 while (!IS_PRINT(*(guchar *)p) && conv_len < from_len) {
2794 conv_len += g_utf8_skip[*(guchar *)p];
2795 p += g_utf8_skip[*(guchar *)p];
2798 from_len -= conv_len;
2799 if (iconv(cd, (ICONV_CONST gchar **)&from_tmp,
2801 &norm_utf7_p, &norm_utf7_len) == -1) {
2802 g_warning(_("iconv cannot convert UTF-8 to UTF-7\n"));
2803 return g_strdup(from);
2806 /* second iconv() call for flushing */
2807 iconv(cd, NULL, NULL, &norm_utf7_p, &norm_utf7_len);
2813 *norm_utf7_p = '\0';
2814 to_str = g_string_new(NULL);
2815 for (p = norm_utf7; p < norm_utf7_p; p++) {
2816 /* replace: '&' -> "&-",
2819 BASE64 '/' -> ',' */
2820 if (!in_escape && *p == '&') {
2821 g_string_append(to_str, "&-");
2822 } else if (!in_escape && *p == '+') {
2823 if (*(p + 1) == '-') {
2824 g_string_append_c(to_str, '+');
2827 g_string_append_c(to_str, '&');
2830 } else if (in_escape && *p == '/') {
2831 g_string_append_c(to_str, ',');
2832 } else if (in_escape && *p == '-') {
2833 g_string_append_c(to_str, '-');
2836 g_string_append_c(to_str, *p);
2842 g_string_append_c(to_str, '-');
2846 g_string_free(to_str, FALSE);
2851 static gboolean imap_rename_folder_func(GNode *node, gpointer data)
2853 FolderItem *item = node->data;
2854 gchar **paths = data;
2855 const gchar *oldpath = paths[0];
2856 const gchar *newpath = paths[1];
2858 gchar *new_itempath;
2861 oldpathlen = strlen(oldpath);
2862 if (strncmp(oldpath, item->path, oldpathlen) != 0) {
2863 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
2867 base = item->path + oldpathlen;
2868 while (*base == G_DIR_SEPARATOR) base++;
2870 new_itempath = g_strdup(newpath);
2872 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
2875 item->path = new_itempath;
2880 typedef struct _get_list_uid_data {
2882 IMAPFolderItem *item;
2883 GSList **msgnum_list;
2885 } get_list_uid_data;
2887 static void *get_list_of_uids_thread(void *data)
2889 get_list_uid_data *stuff = (get_list_uid_data *)data;
2890 Folder *folder = stuff->folder;
2891 IMAPFolderItem *item = stuff->item;
2892 GSList **msgnum_list = stuff->msgnum_list;
2893 gint ok, nummsgs = 0, lastuid_old;
2894 IMAPSession *session;
2895 GSList *uidlist, *elem;
2896 struct mailimap_set * set;
2897 clist * lep_uidlist;
2900 session = imap_session_get(folder);
2901 if (session == NULL) {
2903 return GINT_TO_POINTER(-1);
2906 ok = imap_select(session, IMAP_FOLDER(folder), item->item.path,
2907 NULL, NULL, NULL, NULL, TRUE);
2908 if (ok != IMAP_SUCCESS) {
2910 return GINT_TO_POINTER(-1);
2915 set = mailimap_set_new_interval(item->lastuid + 1, 0);
2917 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SIMPLE, set,
2919 if (r == MAILIMAP_NO_ERROR) {
2920 GSList * fetchuid_list;
2923 imap_uid_list_from_lep(lep_uidlist);
2924 uidlist = g_slist_concat(fetchuid_list, uidlist);
2927 GSList * fetchuid_list;
2928 carray * lep_uidtab;
2930 r = imap_threaded_fetch_uid(folder, item->lastuid + 1,
2932 if (r == MAILIMAP_NO_ERROR) {
2934 imap_uid_list_from_lep_tab(lep_uidtab);
2935 uidlist = g_slist_concat(fetchuid_list, uidlist);
2939 lastuid_old = item->lastuid;
2940 *msgnum_list = g_slist_copy(item->uid_list);
2941 nummsgs = g_slist_length(*msgnum_list);
2942 debug_print("Got %d uids from cache\n", g_slist_length(item->uid_list));
2944 for (elem = uidlist; elem != NULL; elem = g_slist_next(elem)) {
2947 msgnum = GPOINTER_TO_INT(elem->data);
2948 if (msgnum > lastuid_old) {
2949 *msgnum_list = g_slist_prepend(*msgnum_list, GINT_TO_POINTER(msgnum));
2950 item->uid_list = g_slist_prepend(item->uid_list, GINT_TO_POINTER(msgnum));
2953 if(msgnum > item->lastuid)
2954 item->lastuid = msgnum;
2957 g_slist_free(uidlist);
2960 return GINT_TO_POINTER(nummsgs);
2963 static gint get_list_of_uids(Folder *folder, IMAPFolderItem *item, GSList **msgnum_list)
2966 get_list_uid_data *data = g_new0(get_list_uid_data, 1);
2968 data->folder = folder;
2970 data->msgnum_list = msgnum_list;
2972 if (prefs_common.work_offline && !inc_offline_should_override()) {
2977 result = GPOINTER_TO_INT(get_list_of_uids_thread(data));
2983 gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list, gboolean *old_uids_valid)
2985 IMAPFolderItem *item = (IMAPFolderItem *)_item;
2986 IMAPSession *session;
2987 gint ok, nummsgs = 0, exists, uid_val, uid_next;
2988 GSList *uidlist = NULL;
2990 gboolean selected_folder;
2992 debug_print("get_num_list\n");
2994 g_return_val_if_fail(folder != NULL, -1);
2995 g_return_val_if_fail(item != NULL, -1);
2996 g_return_val_if_fail(item->item.path != NULL, -1);
2997 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
2998 g_return_val_if_fail(folder->account != NULL, -1);
3000 session = imap_session_get(folder);
3001 g_return_val_if_fail(session != NULL, -1);
3003 statusbar_print_all("Scanning %s...\n", FOLDER_ITEM(item)->path
3004 ? FOLDER_ITEM(item)->path:"");
3006 selected_folder = (session->mbox != NULL) &&
3007 (!strcmp(session->mbox, item->item.path));
3008 if (selected_folder) {
3009 ok = imap_cmd_noop(session);
3010 if (ok != IMAP_SUCCESS) {
3011 debug_print("disconnected!\n");
3012 session = imap_reconnect_if_possible(folder, session);
3013 if (session == NULL) {
3014 statusbar_pop_all();
3018 exists = session->exists;
3020 *old_uids_valid = TRUE;
3022 if (item->use_cache && time(NULL) - item->use_cache < 2) {
3023 exists = item->c_messages;
3024 uid_next = item->c_uid_next;
3025 uid_val = item->c_uid_validity;
3027 debug_print("using cache %d %d %d\n", exists, uid_next, uid_val);
3029 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path, item,
3030 &exists, &uid_next, &uid_val, NULL, FALSE);
3032 item->use_cache = (time_t)0;
3033 if (ok != IMAP_SUCCESS) {
3034 statusbar_pop_all();
3037 if(item->item.mtime == uid_val)
3038 *old_uids_valid = TRUE;
3040 *old_uids_valid = FALSE;
3042 debug_print("Freeing imap uid cache\n");
3044 g_slist_free(item->uid_list);
3045 item->uid_list = NULL;
3047 item->item.mtime = uid_val;
3049 imap_delete_all_cached_messages((FolderItem *)item);
3053 if (!selected_folder)
3054 item->uid_next = uid_next;
3056 /* If old uid_next matches new uid_next we can be sure no message
3057 was added to the folder */
3058 if (( selected_folder && !session->folder_content_changed) ||
3059 (!selected_folder && uid_next == item->uid_next)) {
3060 nummsgs = g_slist_length(item->uid_list);
3062 /* If number of messages is still the same we
3063 know our caches message numbers are still valid,
3064 otherwise if the number of messages has decrease
3065 we discard our cache to start a new scan to find
3066 out which numbers have been removed */
3067 if (exists == nummsgs) {
3068 *msgnum_list = g_slist_copy(item->uid_list);
3069 statusbar_pop_all();
3071 } else if (exists < nummsgs) {
3072 debug_print("Freeing imap uid cache");
3074 g_slist_free(item->uid_list);
3075 item->uid_list = NULL;
3080 *msgnum_list = NULL;
3081 statusbar_pop_all();
3085 nummsgs = get_list_of_uids(folder, item, &uidlist);
3088 statusbar_pop_all();
3092 if (nummsgs != exists) {
3093 /* Cache contains more messages then folder, we have cached
3094 an old UID of a message that was removed and new messages
3095 have been added too, otherwise the uid_next check would
3097 debug_print("Freeing imap uid cache");
3099 g_slist_free(item->uid_list);
3100 item->uid_list = NULL;
3102 g_slist_free(*msgnum_list);
3104 nummsgs = get_list_of_uids(folder, item, &uidlist);
3107 *msgnum_list = uidlist;
3109 dir = folder_item_get_path((FolderItem *)item);
3110 debug_print("removing old messages from %s\n", dir);
3111 remove_numbered_files_not_in_list(dir, *msgnum_list);
3114 debug_print("get_num_list - ok - %i\n", nummsgs);
3115 statusbar_pop_all();
3119 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item)
3124 flags.perm_flags = MSG_NEW|MSG_UNREAD;
3125 flags.tmp_flags = 0;
3127 g_return_val_if_fail(item != NULL, NULL);
3128 g_return_val_if_fail(file != NULL, NULL);
3130 if (folder_has_parent_of_type(item, F_QUEUE)) {
3131 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
3132 } else if (folder_has_parent_of_type(item, F_DRAFT)) {
3133 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
3136 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3137 if (!msginfo) return NULL;
3139 msginfo->plaintext_file = g_strdup(file);
3140 msginfo->folder = item;
3145 GSList *imap_get_msginfos(Folder *folder, FolderItem *item,
3146 GSList *msgnum_list)
3148 IMAPSession *session;
3149 MsgInfoList *ret = NULL;
3152 debug_print("get_msginfos\n");
3154 g_return_val_if_fail(folder != NULL, NULL);
3155 g_return_val_if_fail(item != NULL, NULL);
3156 g_return_val_if_fail(msgnum_list != NULL, NULL);
3158 session = imap_session_get(folder);
3159 g_return_val_if_fail(session != NULL, NULL);
3161 debug_print("IMAP getting msginfos\n");
3162 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3163 NULL, NULL, NULL, NULL, FALSE);
3164 if (ok != IMAP_SUCCESS)
3167 if (!(folder_has_parent_of_type(item, F_DRAFT) ||
3168 folder_has_parent_of_type(item, F_QUEUE))) {
3169 ret = g_slist_concat(ret,
3170 imap_get_uncached_messages(session, item,
3173 MsgNumberList *sorted_list, *elem;
3174 gint startnum, lastnum;
3176 sorted_list = g_slist_sort(g_slist_copy(msgnum_list), g_int_compare);
3178 startnum = lastnum = GPOINTER_TO_INT(sorted_list->data);
3180 for (elem = sorted_list;; elem = g_slist_next(elem)) {
3184 num = GPOINTER_TO_INT(elem->data);
3186 if (num > lastnum + 1 || elem == NULL) {
3188 for (i = startnum; i <= lastnum; ++i) {
3191 file = imap_fetch_msg(folder, item, i);
3193 MsgInfo *msginfo = imap_parse_msg(file, item);
3194 if (msginfo != NULL) {
3195 msginfo->msgnum = i;
3196 ret = g_slist_append(ret, msginfo);
3210 g_slist_free(sorted_list);
3216 MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid)
3218 MsgInfo *msginfo = NULL;
3219 MsgInfoList *msginfolist;
3220 MsgNumberList numlist;
3222 numlist.next = NULL;
3223 numlist.data = GINT_TO_POINTER(uid);
3225 msginfolist = imap_get_msginfos(folder, item, &numlist);
3226 if (msginfolist != NULL) {
3227 msginfo = msginfolist->data;
3228 g_slist_free(msginfolist);
3234 gboolean imap_scan_required(Folder *folder, FolderItem *_item)
3236 IMAPSession *session;
3237 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3238 gint ok, exists = 0, unseen = 0;
3239 guint32 uid_next, uid_val;
3240 gboolean selected_folder;
3242 g_return_val_if_fail(folder != NULL, FALSE);
3243 g_return_val_if_fail(item != NULL, FALSE);
3244 g_return_val_if_fail(item->item.folder != NULL, FALSE);
3245 g_return_val_if_fail(FOLDER_CLASS(item->item.folder) == &imap_class, FALSE);
3247 if (item->item.path == NULL)
3250 session = imap_session_get(folder);
3251 g_return_val_if_fail(session != NULL, FALSE);
3253 selected_folder = (session->mbox != NULL) &&
3254 (!strcmp(session->mbox, item->item.path));
3255 if (selected_folder) {
3256 ok = imap_cmd_noop(session);
3257 if (ok != IMAP_SUCCESS) {
3258 debug_print("disconnected!\n");
3259 session = imap_reconnect_if_possible(folder, session);
3260 if (session == NULL)
3264 if (session->folder_content_changed
3265 || session->exists != item->item.total_msgs)
3268 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path, IMAP_FOLDER_ITEM(item),
3269 &exists, &uid_next, &uid_val, &unseen, FALSE);
3270 if (ok != IMAP_SUCCESS)
3273 item->use_cache = time(NULL);
3274 item->c_messages = exists;
3275 item->c_uid_next = uid_next;
3276 item->c_uid_validity = uid_val;
3277 item->c_unseen = unseen;
3279 if ((uid_next != item->uid_next) || (exists != item->item.total_msgs))
3286 void imap_change_flags(Folder *folder, FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
3288 IMAPSession *session;
3289 IMAPFlags flags_set = 0, flags_unset = 0;
3290 gint ok = IMAP_SUCCESS;
3291 MsgNumberList numlist;
3292 hashtable_data *ht_data = NULL;
3294 g_return_if_fail(folder != NULL);
3295 g_return_if_fail(folder->klass == &imap_class);
3296 g_return_if_fail(item != NULL);
3297 g_return_if_fail(item->folder == folder);
3298 g_return_if_fail(msginfo != NULL);
3299 g_return_if_fail(msginfo->folder == item);
3301 session = imap_session_get(folder);
3305 if ((ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
3306 NULL, NULL, NULL, NULL, FALSE)) != IMAP_SUCCESS) {
3310 if (!MSG_IS_MARKED(msginfo->flags) && (newflags & MSG_MARKED))
3311 flags_set |= IMAP_FLAG_FLAGGED;
3312 if ( MSG_IS_MARKED(msginfo->flags) && !(newflags & MSG_MARKED))
3313 flags_unset |= IMAP_FLAG_FLAGGED;
3315 if (!MSG_IS_UNREAD(msginfo->flags) && (newflags & MSG_UNREAD))
3316 flags_unset |= IMAP_FLAG_SEEN;
3317 if ( MSG_IS_UNREAD(msginfo->flags) && !(newflags & MSG_UNREAD))
3318 flags_set |= IMAP_FLAG_SEEN;
3320 if (!MSG_IS_REPLIED(msginfo->flags) && (newflags & MSG_REPLIED))
3321 flags_set |= IMAP_FLAG_ANSWERED;
3322 if ( MSG_IS_REPLIED(msginfo->flags) && !(newflags & MSG_REPLIED))
3323 flags_unset |= IMAP_FLAG_ANSWERED;
3325 if (!MSG_IS_DELETED(msginfo->flags) && (newflags & MSG_DELETED))
3326 flags_set |= IMAP_FLAG_DELETED;
3327 if ( MSG_IS_DELETED(msginfo->flags) && !(newflags & MSG_DELETED))
3328 flags_unset |= IMAP_FLAG_DELETED;
3330 numlist.next = NULL;
3331 numlist.data = GINT_TO_POINTER(msginfo->msgnum);
3333 if (IMAP_FOLDER_ITEM(item)->batching) {
3334 /* instead of performing an UID STORE command for each message change,
3335 * as a lot of them can change "together", we just fill in hashtables
3336 * and defer the treatment so that we're able to send only one
3339 debug_print("IMAP batch mode on, deferring flags change\n");
3341 ht_data = g_hash_table_lookup(flags_set_table, GINT_TO_POINTER(flags_set));
3342 if (ht_data == NULL) {
3343 ht_data = g_new0(hashtable_data, 1);
3344 ht_data->session = session;
3345 g_hash_table_insert(flags_set_table, GINT_TO_POINTER(flags_set), ht_data);
3347 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
3348 ht_data->msglist = g_slist_prepend(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum));
3351 ht_data = g_hash_table_lookup(flags_unset_table, GINT_TO_POINTER(flags_unset));
3352 if (ht_data == NULL) {
3353 ht_data = g_new0(hashtable_data, 1);
3354 ht_data->session = session;
3355 g_hash_table_insert(flags_unset_table, GINT_TO_POINTER(flags_unset), ht_data);
3357 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
3358 ht_data->msglist = g_slist_prepend(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum));
3361 debug_print("IMAP changing flags\n");
3363 ok = imap_set_message_flags(session, &numlist, flags_set, TRUE);
3364 if (ok != IMAP_SUCCESS) {
3370 ok = imap_set_message_flags(session, &numlist, flags_unset, FALSE);
3371 if (ok != IMAP_SUCCESS) {
3376 msginfo->flags.perm_flags = newflags;
3381 static gint imap_remove_msg(Folder *folder, FolderItem *item, gint uid)
3384 IMAPSession *session;
3386 MsgNumberList numlist;
3388 g_return_val_if_fail(folder != NULL, -1);
3389 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3390 g_return_val_if_fail(item != NULL, -1);
3392 session = imap_session_get(folder);
3393 if (!session) return -1;
3395 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3396 NULL, NULL, NULL, NULL, FALSE);
3397 if (ok != IMAP_SUCCESS)
3400 numlist.next = NULL;
3401 numlist.data = GINT_TO_POINTER(uid);
3403 ok = imap_set_message_flags
3404 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
3405 &numlist, IMAP_FLAG_DELETED, TRUE);
3406 if (ok != IMAP_SUCCESS) {
3407 log_warning(_("can't set deleted flags: %d\n"), uid);
3411 if (!session->uidplus) {
3412 ok = imap_cmd_expunge(session);
3416 uidstr = g_strdup_printf("%u", uid);
3417 ok = imap_cmd_expunge(session);
3420 if (ok != IMAP_SUCCESS) {
3421 log_warning(_("can't expunge\n"));
3425 IMAP_FOLDER_ITEM(item)->uid_list = g_slist_remove(
3426 IMAP_FOLDER_ITEM(item)->uid_list, numlist.data);
3427 dir = folder_item_get_path(item);
3428 if (is_dir_exist(dir))
3429 remove_numbered_files(dir, uid, uid);
3432 return IMAP_SUCCESS;
3435 static gint compare_msginfo(gconstpointer a, gconstpointer b)
3437 return ((MsgInfo *)a)->msgnum - ((MsgInfo *)b)->msgnum;
3440 static guint gslist_find_next_num(MsgNumberList **list, guint num)
3444 g_return_val_if_fail(list != NULL, -1);
3446 for (elem = *list; elem != NULL; elem = g_slist_next(elem))
3447 if (GPOINTER_TO_INT(elem->data) >= num)
3450 return elem != NULL ? GPOINTER_TO_INT(elem->data) : (gint)-1;
3454 * NEW and DELETED flags are not syncronized
3455 * - The NEW/RECENT flags in IMAP folders can not really be directly
3456 * modified by Sylpheed
3457 * - The DELETE/DELETED flag in IMAP and Sylpheed don't have the same
3458 * meaning, in IMAP it always removes the messages from the FolderItem
3459 * in Sylpheed it can mean to move the message to trash
3462 typedef struct _get_flags_data {
3465 MsgInfoList *msginfo_list;
3466 GRelation *msgflags;
3467 gboolean full_search;
3471 static /*gint*/ void *imap_get_flags_thread(void *data)
3473 get_flags_data *stuff = (get_flags_data *)data;
3474 Folder *folder = stuff->folder;
3475 FolderItem *item = stuff->item;
3476 MsgInfoList *msginfo_list = stuff->msginfo_list;
3477 GRelation *msgflags = stuff->msgflags;
3478 gboolean full_search = stuff->full_search;
3479 IMAPSession *session;
3480 GSList *sorted_list = NULL;
3481 GSList *unseen = NULL, *answered = NULL, *flagged = NULL, *deleted = NULL;
3482 GSList *p_unseen, *p_answered, *p_flagged, *p_deleted;
3484 GSList *seq_list, *cur;
3485 gboolean reverse_seen = FALSE;
3488 gint exists_cnt, unseen_cnt;
3489 gboolean selected_folder;
3491 if (folder == NULL || item == NULL) {
3493 return GINT_TO_POINTER(-1);
3496 session = imap_session_get(folder);
3497 if (session == NULL) {
3499 return GINT_TO_POINTER(-1);
3502 selected_folder = (session->mbox != NULL) &&
3503 (!strcmp(session->mbox, item->path));
3505 if (!selected_folder) {
3506 ok = imap_status(session, IMAP_FOLDER(folder), item->path, IMAP_FOLDER_ITEM(item),
3507 &exists_cnt, NULL, NULL, &unseen_cnt, TRUE);
3508 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3509 NULL, NULL, NULL, NULL, TRUE);
3510 if (ok != IMAP_SUCCESS) {
3512 return GINT_TO_POINTER(-1);
3515 if (unseen_cnt > exists_cnt / 2)
3516 reverse_seen = TRUE;
3519 if (item->unread_msgs > item->total_msgs / 2)
3520 reverse_seen = TRUE;
3523 cmd_buf = g_string_new(NULL);
3525 sorted_list = g_slist_sort(g_slist_copy(msginfo_list), compare_msginfo);
3527 seq_list = imap_get_lep_set_from_msglist(msginfo_list);
3529 struct mailimap_set * set;
3530 set = mailimap_set_new_interval(1, 0);
3531 seq_list = g_slist_append(NULL, set);
3534 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
3535 struct mailimap_set * imapset;
3536 clist * lep_uidlist;
3539 imapset = cur->data;
3541 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SEEN,
3542 imapset, &lep_uidlist);
3545 r = imap_threaded_search(folder,
3546 IMAP_SEARCH_TYPE_UNSEEN,
3547 imapset, &lep_uidlist);
3549 if (r == MAILIMAP_NO_ERROR) {
3552 uidlist = imap_uid_list_from_lep(lep_uidlist);
3553 mailimap_search_result_free(lep_uidlist);
3555 unseen = g_slist_concat(unseen, uidlist);
3558 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_ANSWERED,
3559 imapset, &lep_uidlist);
3560 if (r == MAILIMAP_NO_ERROR) {
3563 uidlist = imap_uid_list_from_lep(lep_uidlist);
3564 mailimap_search_result_free(lep_uidlist);
3566 answered = g_slist_concat(answered, uidlist);
3569 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_FLAGGED,
3570 imapset, &lep_uidlist);
3571 if (r == MAILIMAP_NO_ERROR) {
3574 uidlist = imap_uid_list_from_lep(lep_uidlist);
3575 mailimap_search_result_free(lep_uidlist);
3577 flagged = g_slist_concat(flagged, uidlist);
3580 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_DELETED,
3581 imapset, &lep_uidlist);
3582 if (r == MAILIMAP_NO_ERROR) {
3585 uidlist = imap_uid_list_from_lep(lep_uidlist);
3586 mailimap_search_result_free(lep_uidlist);
3588 deleted = g_slist_concat(deleted, uidlist);
3593 p_answered = answered;
3594 p_flagged = flagged;
3595 p_deleted = deleted;
3597 for (elem = sorted_list; elem != NULL; elem = g_slist_next(elem)) {
3602 msginfo = (MsgInfo *) elem->data;
3603 flags = msginfo->flags.perm_flags;
3604 wasnew = (flags & MSG_NEW);
3605 flags &= ~((reverse_seen ? 0 : MSG_UNREAD | MSG_NEW) | MSG_REPLIED | MSG_MARKED);
3607 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
3608 if (gslist_find_next_num(&p_unseen, msginfo->msgnum) == msginfo->msgnum) {
3609 if (!reverse_seen) {
3610 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
3612 flags &= ~(MSG_UNREAD | MSG_NEW);
3615 if (gslist_find_next_num(&p_answered, msginfo->msgnum) == msginfo->msgnum)
3616 flags |= MSG_REPLIED;
3618 flags &= ~MSG_REPLIED;
3619 if (gslist_find_next_num(&p_flagged, msginfo->msgnum) == msginfo->msgnum)
3620 flags |= MSG_MARKED;
3622 flags &= ~MSG_MARKED;
3623 if (gslist_find_next_num(&p_deleted, msginfo->msgnum) == msginfo->msgnum)
3624 flags |= MSG_DELETED;
3626 flags &= ~MSG_DELETED;
3627 g_relation_insert(msgflags, msginfo, GINT_TO_POINTER(flags));
3630 imap_lep_set_free(seq_list);
3631 g_slist_free(flagged);
3632 g_slist_free(deleted);
3633 g_slist_free(answered);
3634 g_slist_free(unseen);
3635 g_slist_free(sorted_list);
3636 g_string_free(cmd_buf, TRUE);
3639 return GINT_TO_POINTER(0);
3642 static gint imap_get_flags(Folder *folder, FolderItem *item,
3643 MsgInfoList *msginfo_list, GRelation *msgflags)
3646 get_flags_data *data = g_new0(get_flags_data, 1);
3648 data->folder = folder;
3650 data->msginfo_list = msginfo_list;
3651 data->msgflags = msgflags;
3652 data->full_search = FALSE;
3654 GSList *tmp = NULL, *cur;
3656 if (prefs_common.work_offline && !inc_offline_should_override()) {
3661 tmp = folder_item_get_msg_list(item);
3663 if (g_slist_length(tmp) == g_slist_length(msginfo_list))
3664 data->full_search = TRUE;
3666 for (cur = tmp; cur; cur = cur->next)
3667 procmsg_msginfo_free((MsgInfo *)cur->data);
3671 result = GPOINTER_TO_INT(imap_get_flags_thread(data));
3678 static gboolean process_flags(gpointer key, gpointer value, gpointer user_data)
3680 gboolean flags_set = GPOINTER_TO_INT(user_data);
3681 gint flags_value = GPOINTER_TO_INT(key);
3682 hashtable_data *data = (hashtable_data *)value;
3684 data->msglist = g_slist_reverse(data->msglist);
3686 debug_print("IMAP %ssetting flags to %d for %d messages\n",
3689 g_slist_length(data->msglist));
3690 imap_set_message_flags(data->session, data->msglist, flags_value, flags_set);
3692 g_slist_free(data->msglist);
3697 static void process_hashtable(void)
3699 if (flags_set_table) {
3700 g_hash_table_foreach_remove(flags_set_table, process_flags, GINT_TO_POINTER(TRUE));
3701 g_free(flags_set_table);
3702 flags_set_table = NULL;
3704 if (flags_unset_table) {
3705 g_hash_table_foreach_remove(flags_unset_table, process_flags, GINT_TO_POINTER(FALSE));
3706 g_free(flags_unset_table);
3707 flags_unset_table = NULL;
3711 static IMAPFolderItem *batching_item = NULL;
3713 static void imap_set_batch (Folder *folder, FolderItem *_item, gboolean batch)
3715 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3717 g_return_if_fail(item != NULL);
3719 if (batch && batching_item != NULL) {
3720 g_warning("already batching on %s\n", batching_item->item.path);
3724 if (item->batching == batch)
3727 item->batching = batch;
3729 batching_item = batch?item:NULL;
3732 debug_print("IMAP switching to batch mode\n");
3733 if (flags_set_table) {
3734 g_warning("flags_set_table non-null but we just entered batch mode!\n");
3735 flags_set_table = NULL;
3737 if (flags_unset_table) {
3738 g_warning("flags_unset_table non-null but we just entered batch mode!\n");
3739 flags_unset_table = NULL;
3741 flags_set_table = g_hash_table_new(NULL, g_direct_equal);
3742 flags_unset_table = g_hash_table_new(NULL, g_direct_equal);
3744 debug_print("IMAP switching away from batch mode\n");
3746 process_hashtable();
3752 /* data types conversion libetpan <-> sylpheed */
3756 #define ETPAN_IMAP_MB_MARKED 1
3757 #define ETPAN_IMAP_MB_UNMARKED 2
3758 #define ETPAN_IMAP_MB_NOSELECT 4
3759 #define ETPAN_IMAP_MB_NOINFERIORS 8
3761 static int imap_flags_to_flags(struct mailimap_mbx_list_flags * imap_flags)
3767 if (imap_flags->mbf_type == MAILIMAP_MBX_LIST_FLAGS_SFLAG) {
3768 switch (imap_flags->mbf_sflag) {
3769 case MAILIMAP_MBX_LIST_SFLAG_MARKED:
3770 flags |= ETPAN_IMAP_MB_MARKED;
3772 case MAILIMAP_MBX_LIST_SFLAG_NOSELECT:
3773 flags |= ETPAN_IMAP_MB_NOSELECT;
3775 case MAILIMAP_MBX_LIST_SFLAG_UNMARKED:
3776 flags |= ETPAN_IMAP_MB_UNMARKED;
3781 for(cur = clist_begin(imap_flags->mbf_oflags) ; cur != NULL ;
3782 cur = clist_next(cur)) {
3783 struct mailimap_mbx_list_oflag * oflag;
3785 oflag = clist_content(cur);
3787 switch (oflag->of_type) {
3788 case MAILIMAP_MBX_LIST_OFLAG_NOINFERIORS:
3789 flags |= ETPAN_IMAP_MB_NOINFERIORS;
3797 static GSList * imap_list_from_lep(IMAPFolder * folder,
3798 clist * list, const gchar * real_path)
3805 for(iter = clist_begin(list) ; iter != NULL ;
3806 iter = clist_next(iter)) {
3807 struct mailimap_mailbox_list * mb;
3815 FolderItem *new_item;
3817 mb = clist_content(iter);
3820 if (mb->mb_flag != NULL)
3821 flags = imap_flags_to_flags(mb->mb_flag);
3823 delimiter = mb->mb_delimiter;
3826 dup_name = strdup(name);
3827 if (delimiter != '\0')
3828 subst_char(dup_name, delimiter, '/');
3830 base = g_path_get_basename(dup_name);
3831 if (base[0] == '.') {
3837 if (strcmp(dup_name, real_path) == 0) {
3843 if (dup_name[strlen(dup_name)-1] == '/') {
3849 loc_name = imap_modified_utf7_to_utf8(base);
3850 loc_path = imap_modified_utf7_to_utf8(dup_name);
3852 new_item = folder_item_new(FOLDER(folder), loc_name, loc_path);
3853 if ((flags & ETPAN_IMAP_MB_NOINFERIORS) != 0)
3854 new_item->no_sub = TRUE;
3855 if (strcmp(dup_name, "INBOX") != 0 &&
3856 ((flags & ETPAN_IMAP_MB_NOSELECT) != 0))
3857 new_item->no_select = TRUE;
3859 item_list = g_slist_append(item_list, new_item);
3861 debug_print("folder '%s' found.\n", loc_path);
3872 static GSList * imap_get_lep_set_from_numlist(MsgNumberList *numlist)
3874 GSList *sorted_list, *cur;
3875 guint first, last, next;
3876 GSList *ret_list = NULL;
3878 struct mailimap_set * current_set;
3879 unsigned int item_count;
3881 if (numlist == NULL)
3885 current_set = mailimap_set_new_empty();
3887 sorted_list = g_slist_copy(numlist);
3888 sorted_list = g_slist_sort(sorted_list, g_int_compare);
3890 first = GPOINTER_TO_INT(sorted_list->data);
3893 for (cur = sorted_list; cur != NULL; cur = g_slist_next(cur)) {
3894 if (GPOINTER_TO_INT(cur->data) == 0)
3899 last = GPOINTER_TO_INT(cur->data);
3901 next = GPOINTER_TO_INT(cur->next->data);
3905 if (last + 1 != next || next == 0) {
3907 struct mailimap_set_item * item;
3908 item = mailimap_set_item_new(first, last);
3909 mailimap_set_add(current_set, item);
3914 if (count >= IMAP_SET_MAX_COUNT) {
3915 ret_list = g_slist_append(ret_list,
3917 current_set = mailimap_set_new_empty();
3924 if (clist_count(current_set->set_list) > 0) {
3925 ret_list = g_slist_append(ret_list,
3929 g_slist_free(sorted_list);
3934 static GSList * imap_get_lep_set_from_msglist(MsgInfoList *msglist)
3936 MsgNumberList *numlist = NULL;
3940 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
3941 MsgInfo *msginfo = (MsgInfo *) cur->data;
3943 numlist = g_slist_append(numlist, GINT_TO_POINTER(msginfo->msgnum));
3945 seq_list = imap_get_lep_set_from_numlist(numlist);
3946 g_slist_free(numlist);
3951 static GSList * imap_uid_list_from_lep(clist * list)
3958 for(iter = clist_begin(list) ; iter != NULL ;
3959 iter = clist_next(iter)) {
3962 puid = clist_content(iter);
3963 result = g_slist_append(result, GINT_TO_POINTER(* puid));
3969 static GSList * imap_uid_list_from_lep_tab(carray * list)
3976 for(i = 0 ; i < carray_count(list) ; i ++) {
3979 puid = carray_get(list, i);
3980 result = g_slist_append(result, GINT_TO_POINTER(* puid));
3986 static MsgInfo *imap_envelope_from_lep(struct imap_fetch_env_info * info,
3989 MsgInfo *msginfo = NULL;
3992 MsgFlags flags = {0, 0};
3994 MSG_SET_TMP_FLAGS(flags, MSG_IMAP);
3995 if (folder_has_parent_of_type(item, F_QUEUE)) {
3996 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
3997 } else if (folder_has_parent_of_type(item, F_DRAFT)) {
3998 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
4000 flags.perm_flags = info->flags;
4004 msginfo = procheader_parse_str(info->headers, flags, FALSE, FALSE);
4007 msginfo->msgnum = uid;
4008 msginfo->size = size;
4014 static void imap_lep_set_free(GSList *seq_list)
4018 for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) {
4019 struct mailimap_set * imapset;
4021 imapset = cur->data;
4022 mailimap_set_free(imapset);
4024 g_slist_free(seq_list);
4027 static struct mailimap_flag_list * imap_flag_to_lep(IMAPFlags flags)
4029 struct mailimap_flag_list * flag_list;
4031 flag_list = mailimap_flag_list_new_empty();
4033 if (IMAP_IS_SEEN(flags))
4034 mailimap_flag_list_add(flag_list,
4035 mailimap_flag_new_seen());
4036 if (IMAP_IS_ANSWERED(flags))
4037 mailimap_flag_list_add(flag_list,
4038 mailimap_flag_new_answered());
4039 if (IMAP_IS_FLAGGED(flags))
4040 mailimap_flag_list_add(flag_list,
4041 mailimap_flag_new_flagged());
4042 if (IMAP_IS_DELETED(flags))
4043 mailimap_flag_list_add(flag_list,
4044 mailimap_flag_new_deleted());
4045 if (IMAP_IS_DRAFT(flags))
4046 mailimap_flag_list_add(flag_list,
4047 mailimap_flag_new_draft());
4052 guint imap_folder_get_refcnt(Folder *folder)
4054 return ((IMAPFolder *)folder)->refcnt;
4057 void imap_folder_ref(Folder *folder)
4059 ((IMAPFolder *)folder)->refcnt++;
4062 void imap_folder_unref(Folder *folder)
4064 if (((IMAPFolder *)folder)->refcnt > 0)
4065 ((IMAPFolder *)folder)->refcnt--;
4068 #else /* HAVE_LIBETPAN */
4070 static FolderClass imap_class;
4072 static Folder *imap_folder_new (const gchar *name,
4077 static gint imap_create_tree (Folder *folder)
4081 static FolderItem *imap_create_folder (Folder *folder,
4087 static gint imap_rename_folder (Folder *folder,
4094 FolderClass *imap_get_class(void)
4096 if (imap_class.idstr == NULL) {
4097 imap_class.type = F_IMAP;
4098 imap_class.idstr = "imap";
4099 imap_class.uistr = "IMAP4";
4101 imap_class.new_folder = imap_folder_new;
4102 imap_class.create_tree = imap_create_tree;
4103 imap_class.create_folder = imap_create_folder;
4104 imap_class.rename_folder = imap_rename_folder;
4105 /* nothing implemented */
4112 void imap_synchronise(FolderItem *item)
4114 imap_gtk_synchronise(item);