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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27 #include <glib/gi18n.h>
54 #include "procheader.h"
55 #include "prefs_account.h"
60 #include "prefs_common.h"
61 #include "inputdialog.h"
63 #include "remotefolder.h"
64 #include "alertpanel.h"
66 #include "statusbar.h"
68 #include "imap-thread.h"
70 typedef struct _IMAPFolder IMAPFolder;
71 typedef struct _IMAPSession IMAPSession;
72 typedef struct _IMAPNameSpace IMAPNameSpace;
73 typedef struct _IMAPFolderItem IMAPFolderItem;
75 #include "prefs_account.h"
77 #define IMAP_FOLDER(obj) ((IMAPFolder *)obj)
78 #define IMAP_FOLDER_ITEM(obj) ((IMAPFolderItem *)obj)
79 #define IMAP_SESSION(obj) ((IMAPSession *)obj)
85 /* list of IMAPNameSpace */
89 gchar last_seen_separator;
97 gboolean authenticated;
106 gboolean folder_content_changed;
111 struct _IMAPNameSpace
117 #define IMAP_SUCCESS 0
118 #define IMAP_SOCKET 2
119 #define IMAP_AUTHFAIL 3
120 #define IMAP_PROTOCOL 4
121 #define IMAP_SYNTAX 5
125 #define IMAPBUFSIZE 8192
129 IMAP_FLAG_SEEN = 1 << 0,
130 IMAP_FLAG_ANSWERED = 1 << 1,
131 IMAP_FLAG_FLAGGED = 1 << 2,
132 IMAP_FLAG_DELETED = 1 << 3,
133 IMAP_FLAG_DRAFT = 1 << 4
136 #define IMAP_IS_SEEN(flags) ((flags & IMAP_FLAG_SEEN) != 0)
137 #define IMAP_IS_ANSWERED(flags) ((flags & IMAP_FLAG_ANSWERED) != 0)
138 #define IMAP_IS_FLAGGED(flags) ((flags & IMAP_FLAG_FLAGGED) != 0)
139 #define IMAP_IS_DELETED(flags) ((flags & IMAP_FLAG_DELETED) != 0)
140 #define IMAP_IS_DRAFT(flags) ((flags & IMAP_FLAG_DRAFT) != 0)
143 #define IMAP4_PORT 143
145 #define IMAPS_PORT 993
148 #define IMAP_CMD_LIMIT 1000
150 struct _IMAPFolderItem
160 static void imap_folder_init (Folder *folder,
164 static Folder *imap_folder_new (const gchar *name,
166 static void imap_folder_destroy (Folder *folder);
168 static IMAPSession *imap_session_new (Folder *folder,
169 const PrefsAccount *account);
170 static void imap_session_authenticate(IMAPSession *session,
171 const PrefsAccount *account);
172 static void imap_session_destroy (Session *session);
174 static gchar *imap_fetch_msg (Folder *folder,
177 static gchar *imap_fetch_msg_full (Folder *folder,
182 static gint imap_add_msg (Folder *folder,
186 static gint imap_add_msgs (Folder *folder,
189 GRelation *relation);
191 static gint imap_copy_msg (Folder *folder,
194 static gint imap_copy_msgs (Folder *folder,
196 MsgInfoList *msglist,
197 GRelation *relation);
199 static gint imap_remove_msg (Folder *folder,
202 static gint imap_remove_msgs (Folder *folder,
204 MsgInfoList *msglist,
205 GRelation *relation);
206 static gint imap_remove_all_msg (Folder *folder,
209 static gboolean imap_is_msg_changed (Folder *folder,
213 static gint imap_close (Folder *folder,
216 static gint imap_scan_tree (Folder *folder);
218 static gint imap_create_tree (Folder *folder);
220 static FolderItem *imap_create_folder (Folder *folder,
223 static gint imap_rename_folder (Folder *folder,
226 static gint imap_remove_folder (Folder *folder,
229 static FolderItem *imap_folder_item_new (Folder *folder);
230 static void imap_folder_item_destroy (Folder *folder,
233 static IMAPSession *imap_session_get (Folder *folder);
235 static gint imap_auth (IMAPSession *session,
240 static gint imap_scan_tree_recursive (IMAPSession *session,
243 static void imap_create_missing_folders (Folder *folder);
244 static FolderItem *imap_create_special_folder
246 SpecialFolderItemType stype,
249 static gint imap_do_copy_msgs (Folder *folder,
251 MsgInfoList *msglist,
252 GRelation *relation);
254 static void imap_delete_all_cached_messages (FolderItem *item);
255 static void imap_set_batch (Folder *folder,
258 static gint imap_set_message_flags (IMAPSession *session,
259 MsgNumberList *numlist,
262 static gint imap_select (IMAPSession *session,
268 guint32 *uid_validity,
270 static gint imap_status (IMAPSession *session,
276 guint32 *uid_validity,
280 static IMAPNameSpace *imap_find_namespace (IMAPFolder *folder,
282 static gchar imap_get_path_separator (IMAPFolder *folder,
284 static gchar *imap_get_real_path (IMAPFolder *folder,
286 static void imap_synchronise (FolderItem *item);
288 static void imap_free_capabilities (IMAPSession *session);
290 /* low-level IMAP4rev1 commands */
291 static gint imap_cmd_login (IMAPSession *session,
295 static gint imap_cmd_logout (IMAPSession *session);
296 static gint imap_cmd_noop (IMAPSession *session);
298 static gint imap_cmd_starttls (IMAPSession *session);
300 static gint imap_cmd_select (IMAPSession *session,
305 guint32 *uid_validity,
307 static gint imap_cmd_examine (IMAPSession *session,
312 guint32 *uid_validity,
314 static gint imap_cmd_create (IMAPSession *sock,
315 const gchar *folder);
316 static gint imap_cmd_rename (IMAPSession *sock,
317 const gchar *oldfolder,
318 const gchar *newfolder);
319 static gint imap_cmd_delete (IMAPSession *session,
320 const gchar *folder);
321 static gint imap_cmd_fetch (IMAPSession *sock,
323 const gchar *filename,
326 static gint imap_cmd_append (IMAPSession *session,
327 const gchar *destfolder,
331 static gint imap_cmd_copy (IMAPSession *session,
332 struct mailimap_set * set,
333 const gchar *destfolder,
334 GRelation *uid_mapping);
335 static gint imap_cmd_store (IMAPSession *session,
336 struct mailimap_set * set,
339 static gint imap_cmd_expunge (IMAPSession *session);
341 static void imap_path_separator_subst (gchar *str,
344 static gchar *imap_utf8_to_modified_utf7 (const gchar *from);
345 static gchar *imap_modified_utf7_to_utf8 (const gchar *mutf7_str);
347 static gboolean imap_rename_folder_func (GNode *node,
349 static gint imap_get_num_list (Folder *folder,
352 gboolean *old_uids_valid);
353 static GSList *imap_get_msginfos (Folder *folder,
355 GSList *msgnum_list);
356 static MsgInfo *imap_get_msginfo (Folder *folder,
359 static gboolean imap_scan_required (Folder *folder,
361 static void imap_change_flags (Folder *folder,
364 MsgPermFlags newflags);
365 static gint imap_get_flags (Folder *folder,
367 MsgInfoList *msglist,
368 GRelation *msgflags);
369 static gchar *imap_folder_get_path (Folder *folder);
370 static gchar *imap_item_get_path (Folder *folder,
372 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item);
375 /* data types conversion libetpan <-> sylpheed */
376 static GSList * imap_list_from_lep(IMAPFolder * folder,
377 clist * list, const gchar * real_path);
378 static GSList * imap_get_lep_set_from_numlist(MsgNumberList *numlist);
379 static GSList * imap_get_lep_set_from_msglist(MsgInfoList *msglist);
380 static GSList * imap_uid_list_from_lep(clist * list);
381 static GSList * imap_uid_list_from_lep_tab(carray * list);
382 static MsgInfo *imap_envelope_from_lep(struct imap_fetch_env_info * info,
384 static void imap_lep_set_free(GSList *seq_list);
385 static struct mailimap_flag_list * imap_flag_to_lep(IMAPFlags flags);
388 static GHashTable *flags_set_table = NULL;
389 static GHashTable *flags_unset_table = NULL;
390 typedef struct _hashtable_data {
391 IMAPSession *session;
395 static FolderClass imap_class;
397 typedef struct _thread_data {
407 FolderClass *imap_get_class(void)
409 if (imap_class.idstr == NULL) {
410 imap_class.type = F_IMAP;
411 imap_class.idstr = "imap";
412 imap_class.uistr = "IMAP4";
414 /* Folder functions */
415 imap_class.new_folder = imap_folder_new;
416 imap_class.destroy_folder = imap_folder_destroy;
417 imap_class.scan_tree = imap_scan_tree;
418 imap_class.create_tree = imap_create_tree;
420 /* FolderItem functions */
421 imap_class.item_new = imap_folder_item_new;
422 imap_class.item_destroy = imap_folder_item_destroy;
423 imap_class.item_get_path = imap_item_get_path;
424 imap_class.create_folder = imap_create_folder;
425 imap_class.rename_folder = imap_rename_folder;
426 imap_class.remove_folder = imap_remove_folder;
427 imap_class.close = imap_close;
428 imap_class.get_num_list = imap_get_num_list;
429 imap_class.scan_required = imap_scan_required;
431 /* Message functions */
432 imap_class.get_msginfo = imap_get_msginfo;
433 imap_class.get_msginfos = imap_get_msginfos;
434 imap_class.fetch_msg = imap_fetch_msg;
435 imap_class.fetch_msg_full = imap_fetch_msg_full;
436 imap_class.add_msg = imap_add_msg;
437 imap_class.add_msgs = imap_add_msgs;
438 imap_class.copy_msg = imap_copy_msg;
439 imap_class.copy_msgs = imap_copy_msgs;
440 imap_class.remove_msg = imap_remove_msg;
441 imap_class.remove_msgs = imap_remove_msgs;
442 imap_class.remove_all_msg = imap_remove_all_msg;
443 imap_class.is_msg_changed = imap_is_msg_changed;
444 imap_class.change_flags = imap_change_flags;
445 imap_class.get_flags = imap_get_flags;
446 imap_class.set_batch = imap_set_batch;
447 imap_class.synchronise = imap_synchronise;
449 pthread_mutex_init(&imap_mutex, NULL);
456 static Folder *imap_folder_new(const gchar *name, const gchar *path)
460 folder = (Folder *)g_new0(IMAPFolder, 1);
461 folder->klass = &imap_class;
462 imap_folder_init(folder, name, path);
467 static void imap_folder_destroy(Folder *folder)
471 while (imap_folder_get_refcnt(folder) > 0)
472 gtk_main_iteration();
474 dir = imap_folder_get_path(folder);
475 if (is_dir_exist(dir))
476 remove_dir_recursive(dir);
479 folder_remote_folder_destroy(REMOTE_FOLDER(folder));
483 static void imap_folder_init(Folder *folder, const gchar *name,
486 folder_remote_folder_init((Folder *)folder, name, path);
489 static FolderItem *imap_folder_item_new(Folder *folder)
491 IMAPFolderItem *item;
493 item = g_new0(IMAPFolderItem, 1);
496 item->uid_list = NULL;
498 return (FolderItem *)item;
501 static void imap_folder_item_destroy(Folder *folder, FolderItem *_item)
503 IMAPFolderItem *item = (IMAPFolderItem *)_item;
505 g_return_if_fail(item != NULL);
506 g_slist_free(item->uid_list);
511 static gboolean imap_reset_uid_lists_func(GNode *node, gpointer data)
513 IMAPFolderItem *item = (IMAPFolderItem *)node->data;
517 g_slist_free(item->uid_list);
518 item->uid_list = NULL;
523 static void imap_reset_uid_lists(Folder *folder)
525 if(folder->node == NULL)
528 /* Destroy all uid lists and rest last uid */
529 g_node_traverse(folder->node, G_IN_ORDER, G_TRAVERSE_ALL, -1, imap_reset_uid_lists_func, NULL);
532 void imap_get_capabilities(IMAPSession *session)
534 struct mailimap_capability_data *capabilities = NULL;
537 if (session->capability != NULL)
540 capabilities = imap_threaded_capability(session->folder);
542 if (capabilities == NULL)
545 for(cur = clist_begin(capabilities->cap_list) ; cur != NULL ;
546 cur = clist_next(cur)) {
547 struct mailimap_capability * cap =
549 if (!cap || cap->cap_data.cap_name == NULL)
551 session->capability = g_slist_append
552 (session->capability,
553 g_strdup(cap->cap_data.cap_name));
554 debug_print("got capa %s\n", cap->cap_data.cap_name);
556 mailimap_capability_data_free(capabilities);
559 gboolean imap_has_capability(IMAPSession *session, const gchar *cap)
562 for (cur = session->capability; cur; cur = cur->next) {
563 if (!g_ascii_strcasecmp(cur->data, cap))
569 static gint imap_auth(IMAPSession *session, const gchar *user, const gchar *pass,
572 gint ok = IMAP_ERROR;
573 static time_t last_login_err = 0;
575 imap_get_capabilities(session);
578 case IMAP_AUTH_CRAM_MD5:
579 ok = imap_cmd_login(session, user, pass, "CRAM-MD5");
581 case IMAP_AUTH_LOGIN:
582 ok = imap_cmd_login(session, user, pass, "LOGIN");
585 debug_print("capabilities:\n"
588 imap_has_capability(session, "CRAM-MD5"),
589 imap_has_capability(session, "LOGIN"));
590 if (imap_has_capability(session, "CRAM-MD5"))
591 ok = imap_cmd_login(session, user, pass, "CRAM-MD5");
592 if (ok == IMAP_ERROR) /* we always try LOGIN before giving up */
593 ok = imap_cmd_login(session, user, pass, "LOGIN");
595 if (ok == IMAP_SUCCESS)
596 session->authenticated = TRUE;
598 gchar *ext_info = NULL;
600 if (type == IMAP_AUTH_CRAM_MD5) {
601 ext_info = _("\n\nCRAM-MD5 logins only work if libetpan has been "
602 "compiled with SASL support and the "
603 "CRAM-MD5 SASL plugin is installed.");
608 if (time(NULL) - last_login_err > 10) {
609 alertpanel_error(_("Connection to %s failed: login refused.%s"),
610 SESSION(session)->server, ext_info);
612 last_login_err = time(NULL);
617 static IMAPSession *imap_reconnect_if_possible(Folder *folder, IMAPSession *session)
619 RemoteFolder *rfolder = REMOTE_FOLDER(folder);
620 /* Check if this is the first try to establish a
621 connection, if yes we don't try to reconnect */
622 debug_print("reconnecting\n");
623 if (rfolder->session == NULL) {
624 log_warning(_("Connecting to %s failed"),
625 folder->account->recv_server);
626 session_destroy(SESSION(session));
629 log_warning(_("IMAP4 connection to %s has been"
630 " disconnected. Reconnecting...\n"),
631 folder->account->recv_server);
632 statusbar_print_all(_("IMAP4 connection to %s has been"
633 " disconnected. Reconnecting...\n"),
634 folder->account->recv_server);
635 SESSION(session)->state = SESSION_DISCONNECTED;
636 session_destroy(SESSION(session));
637 /* Clear folders session to make imap_session_get create
638 a new session, because of rfolder->session == NULL
639 it will not try to reconnect again and so avoid an
641 rfolder->session = NULL;
642 session = imap_session_get(folder);
643 rfolder->session = SESSION(session);
649 static IMAPSession *imap_session_get(Folder *folder)
651 RemoteFolder *rfolder = REMOTE_FOLDER(folder);
652 IMAPSession *session = NULL;
654 g_return_val_if_fail(folder != NULL, NULL);
655 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, NULL);
656 g_return_val_if_fail(folder->account != NULL, NULL);
658 if (prefs_common.work_offline && !imap_gtk_should_override()) {
662 /* Make sure we have a session */
663 if (rfolder->session != NULL) {
664 session = IMAP_SESSION(rfolder->session);
666 imap_reset_uid_lists(folder);
667 session = imap_session_new(folder, folder->account);
672 /* Make sure session is authenticated */
673 if (!IMAP_SESSION(session)->authenticated)
674 imap_session_authenticate(IMAP_SESSION(session), folder->account);
676 if (!IMAP_SESSION(session)->authenticated) {
677 session_destroy(SESSION(session));
678 rfolder->session = NULL;
683 /* Make sure we have parsed the IMAP namespace */
684 imap_parse_namespace(IMAP_SESSION(session),
685 IMAP_FOLDER(folder));
688 /* I think the point of this code is to avoid sending a
689 * keepalive if we've used the session recently and therefore
690 * think it's still alive. Unfortunately, most of the code
691 * does not yet check for errors on the socket, and so if the
692 * connection drops we don't notice until the timeout expires.
693 * A better solution than sending a NOOP every time would be
694 * for every command to be prepared to retry until it is
695 * successfully sent. -- mbp */
696 if (time(NULL) - SESSION(session)->last_access_time > SESSION_TIMEOUT_INTERVAL) {
697 /* verify that the session is still alive */
698 if (imap_cmd_noop(session) != IMAP_SUCCESS) {
699 debug_print("disconnected!\n");
700 session = imap_reconnect_if_possible(folder, session);
704 rfolder->session = SESSION(session);
706 return IMAP_SESSION(session);
709 static IMAPSession *imap_session_new(Folder * folder,
710 const PrefsAccount *account)
712 IMAPSession *session;
718 /* FIXME: IMAP over SSL only... */
721 port = account->set_imapport ? account->imapport
722 : account->ssl_imap == SSL_TUNNEL ? IMAPS_PORT : IMAP4_PORT;
723 ssl_type = account->ssl_imap;
725 port = account->set_imapport ? account->imapport
730 statusbar_print_all(_("Connecting to IMAP4 server: %s..."), folder->account->recv_server);
731 if (account->set_tunnelcmd) {
732 r = imap_threaded_connect_cmd(folder,
734 account->recv_server,
739 if (ssl_type == SSL_TUNNEL) {
740 r = imap_threaded_connect_ssl(folder,
741 account->recv_server,
747 r = imap_threaded_connect(folder,
748 account->recv_server,
754 if (r == MAILIMAP_NO_ERROR_AUTHENTICATED) {
755 authenticated = TRUE;
757 else if (r == MAILIMAP_NO_ERROR_NON_AUTHENTICATED) {
758 authenticated = FALSE;
761 if(!prefs_common.no_recv_err_panel) {
762 alertpanel_error(_("Can't connect to IMAP4 server: %s:%d"),
763 account->recv_server, port);
765 log_error(_("Can't connect to IMAP4 server: %s:%d"),
766 account->recv_server, port);
772 session = g_new0(IMAPSession, 1);
773 session_init(SESSION(session));
774 SESSION(session)->type = SESSION_IMAP;
775 SESSION(session)->server = g_strdup(account->recv_server);
776 SESSION(session)->sock = NULL;
778 SESSION(session)->destroy = imap_session_destroy;
780 session->capability = NULL;
782 session->authenticated = authenticated;
783 session->mbox = NULL;
784 session->cmd_count = 0;
785 session->folder = folder;
786 IMAP_FOLDER(session->folder)->last_seen_separator = 0;
789 if (account->ssl_imap == SSL_STARTTLS) {
792 ok = imap_cmd_starttls(session);
793 if (ok != IMAP_SUCCESS) {
794 log_warning(_("Can't start TLS session.\n"));
795 session_destroy(SESSION(session));
799 imap_free_capabilities(session);
800 session->authenticated = FALSE;
801 session->uidplus = FALSE;
802 session->cmd_count = 1;
805 log_message("IMAP connection is %s-authenticated\n",
806 (session->authenticated) ? "pre" : "un");
811 static void imap_session_authenticate(IMAPSession *session,
812 const PrefsAccount *account)
816 g_return_if_fail(account->userid != NULL);
818 pass = account->passwd;
821 tmp_pass = input_dialog_query_password(account->recv_server, account->userid);
823 tmp_pass = g_strdup(""); /* allow empty password */
824 Xstrdup_a(pass, tmp_pass, {g_free(tmp_pass); return;});
827 statusbar_print_all(_("Connecting to IMAP4 server %s...\n"),
828 account->recv_server);
829 if (imap_auth(session, account->userid, pass, account->imap_auth_type) != IMAP_SUCCESS) {
830 imap_threaded_disconnect(session->folder);
831 imap_cmd_logout(session);
837 session->authenticated = TRUE;
840 static void imap_session_destroy(Session *session)
842 if (session->state != SESSION_DISCONNECTED)
843 imap_threaded_disconnect(IMAP_SESSION(session)->folder);
845 imap_free_capabilities(IMAP_SESSION(session));
846 g_free(IMAP_SESSION(session)->mbox);
847 sock_close(session->sock);
848 session->sock = NULL;
851 static gchar *imap_fetch_msg(Folder *folder, FolderItem *item, gint uid)
853 return imap_fetch_msg_full(folder, item, uid, TRUE, TRUE);
856 static guint get_size_with_crs(MsgInfo *info)
865 fp = procmsg_open_message(info);
869 while (fgets(buf, sizeof (buf), fp) != NULL) {
871 if (!strstr(buf, "\r") && strstr(buf, "\n"))
879 static gchar *imap_fetch_msg_full(Folder *folder, FolderItem *item, gint uid,
880 gboolean headers, gboolean body)
882 gchar *path, *filename;
883 IMAPSession *session;
886 g_return_val_if_fail(folder != NULL, NULL);
887 g_return_val_if_fail(item != NULL, NULL);
892 path = folder_item_get_path(item);
893 if (!is_dir_exist(path))
895 filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(uid), NULL);
898 if (is_file_exist(filename)) {
899 /* see whether the local file represents the whole message
900 * or not. As the IMAP server reports size with \r chars,
901 * we have to update the local file (UNIX \n only) size */
902 MsgInfo *msginfo = imap_parse_msg(filename, item);
903 MsgInfo *cached = msgcache_get_msg(item->cache,uid);
904 guint have_size = get_size_with_crs(msginfo);
907 debug_print("message %d has been already %scached (%d/%d).\n", uid,
908 have_size == cached->size ? "fully ":"",
909 have_size, (int)cached->size);
911 if (cached && (cached->size == have_size || !body)) {
912 procmsg_msginfo_free(cached);
913 procmsg_msginfo_free(msginfo);
914 file_strip_crs(filename);
917 procmsg_msginfo_free(cached);
918 procmsg_msginfo_free(msginfo);
922 session = imap_session_get(folder);
928 debug_print("IMAP fetching messages\n");
929 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
930 NULL, NULL, NULL, NULL, FALSE);
931 if (ok != IMAP_SUCCESS) {
932 g_warning("can't select mailbox %s\n", item->path);
937 debug_print("getting message %d...\n", uid);
938 ok = imap_cmd_fetch(session, (guint32)uid, filename, headers, body);
940 if (ok != IMAP_SUCCESS) {
941 g_warning("can't fetch message %d\n", uid);
946 file_strip_crs(filename);
950 static gint imap_add_msg(Folder *folder, FolderItem *dest,
951 const gchar *file, MsgFlags *flags)
955 MsgFileInfo fileinfo;
957 g_return_val_if_fail(file != NULL, -1);
959 fileinfo.msginfo = NULL;
960 fileinfo.file = (gchar *)file;
961 fileinfo.flags = flags;
962 file_list.data = &fileinfo;
963 file_list.next = NULL;
965 ret = imap_add_msgs(folder, dest, &file_list, NULL);
969 static gint imap_add_msgs(Folder *folder, FolderItem *dest, GSList *file_list,
973 IMAPSession *session;
974 guint32 last_uid = 0;
976 MsgFileInfo *fileinfo;
980 g_return_val_if_fail(folder != NULL, -1);
981 g_return_val_if_fail(dest != NULL, -1);
982 g_return_val_if_fail(file_list != NULL, -1);
984 session = imap_session_get(folder);
988 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
990 for (cur = file_list; cur != NULL; cur = cur->next) {
991 IMAPFlags iflags = 0;
993 gchar *real_file = NULL;
994 gboolean file_is_tmp = FALSE;
995 fileinfo = (MsgFileInfo *)cur->data;
997 if (fileinfo->flags) {
998 if (MSG_IS_MARKED(*fileinfo->flags))
999 iflags |= IMAP_FLAG_FLAGGED;
1000 if (MSG_IS_REPLIED(*fileinfo->flags))
1001 iflags |= IMAP_FLAG_ANSWERED;
1002 if (!MSG_IS_UNREAD(*fileinfo->flags))
1003 iflags |= IMAP_FLAG_SEEN;
1006 if ((MSG_IS_QUEUED(*fileinfo->flags) || MSG_IS_DRAFT(*fileinfo->flags))
1007 && !folder_has_parent_of_type(dest, F_QUEUE)
1008 && !folder_has_parent_of_type(dest, F_DRAFT)) {
1009 real_file = get_tmp_file();
1011 if (procmsg_remove_special_headers(fileinfo->file, real_file) !=0) {
1017 real_file = g_strdup(fileinfo->file);
1019 if (folder_has_parent_of_type(dest, F_QUEUE) ||
1020 folder_has_parent_of_type(dest, F_OUTBOX) ||
1021 folder_has_parent_of_type(dest, F_DRAFT) ||
1022 folder_has_parent_of_type(dest, F_TRASH))
1023 iflags |= IMAP_FLAG_SEEN;
1025 ok = imap_cmd_append(session, destdir, real_file, iflags,
1028 if (ok != IMAP_SUCCESS) {
1029 g_warning("can't append message %s\n", real_file);
1031 g_unlink(real_file);
1037 if (relation != NULL)
1038 g_relation_insert(relation, fileinfo->msginfo != NULL ?
1039 (gpointer) fileinfo->msginfo : (gpointer) fileinfo,
1040 GINT_TO_POINTER(dest->last_num + 1));
1041 if (last_uid < new_uid)
1044 g_unlink(real_file);
1052 static gint imap_do_copy_msgs(Folder *folder, FolderItem *dest,
1053 MsgInfoList *msglist, GRelation *relation)
1057 GSList *seq_list, *cur;
1059 IMAPSession *session;
1060 gint ok = IMAP_SUCCESS;
1061 GRelation *uid_mapping;
1064 g_return_val_if_fail(folder != NULL, -1);
1065 g_return_val_if_fail(dest != NULL, -1);
1066 g_return_val_if_fail(msglist != NULL, -1);
1068 session = imap_session_get(folder);
1073 msginfo = (MsgInfo *)msglist->data;
1075 src = msginfo->folder;
1077 g_warning("the src folder is identical to the dest.\n");
1081 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
1082 NULL, NULL, NULL, NULL, FALSE);
1083 if (ok != IMAP_SUCCESS) {
1087 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
1088 seq_list = imap_get_lep_set_from_msglist(msglist);
1089 uid_mapping = g_relation_new(2);
1090 g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
1092 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
1093 struct mailimap_set * seq_set;
1095 seq_set = cur->data;
1097 debug_print("Copying messages from %s to %s ...\n",
1098 src->path, destdir);
1100 ok = imap_cmd_copy(session, seq_set, destdir, uid_mapping);
1101 if (ok != IMAP_SUCCESS) {
1102 g_relation_destroy(uid_mapping);
1103 imap_lep_set_free(seq_list);
1108 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
1109 MsgInfo *msginfo = (MsgInfo *)cur->data;
1112 tuples = g_relation_select(uid_mapping,
1113 GINT_TO_POINTER(msginfo->msgnum),
1115 if (tuples->len > 0) {
1116 gint num = GPOINTER_TO_INT(g_tuples_index(tuples, 0, 1));
1117 g_relation_insert(relation, msginfo,
1118 GPOINTER_TO_INT(num));
1122 g_relation_insert(relation, msginfo,
1123 GPOINTER_TO_INT(0));
1124 g_tuples_destroy(tuples);
1127 g_relation_destroy(uid_mapping);
1128 imap_lep_set_free(seq_list);
1132 IMAP_FOLDER_ITEM(dest)->lastuid = 0;
1133 IMAP_FOLDER_ITEM(dest)->uid_next = 0;
1134 g_slist_free(IMAP_FOLDER_ITEM(dest)->uid_list);
1135 IMAP_FOLDER_ITEM(dest)->uid_list = NULL;
1137 if (ok == IMAP_SUCCESS)
1143 static gint imap_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1147 g_return_val_if_fail(msginfo != NULL, -1);
1149 msglist.data = msginfo;
1150 msglist.next = NULL;
1152 return imap_copy_msgs(folder, dest, &msglist, NULL);
1155 static gint imap_copy_msgs(Folder *folder, FolderItem *dest,
1156 MsgInfoList *msglist, GRelation *relation)
1162 g_return_val_if_fail(folder != NULL, -1);
1163 g_return_val_if_fail(dest != NULL, -1);
1164 g_return_val_if_fail(msglist != NULL, -1);
1166 msginfo = (MsgInfo *)msglist->data;
1167 g_return_val_if_fail(msginfo->folder != NULL, -1);
1169 if (folder == msginfo->folder->folder &&
1170 !folder_has_parent_of_type(msginfo->folder, F_DRAFT) &&
1171 !folder_has_parent_of_type(msginfo->folder, F_QUEUE)) {
1172 ret = imap_do_copy_msgs(folder, dest, msglist, relation);
1176 file_list = procmsg_get_message_file_list(msglist);
1177 g_return_val_if_fail(file_list != NULL, -1);
1179 ret = imap_add_msgs(folder, dest, file_list, relation);
1181 procmsg_message_file_list_free(file_list);
1187 static gint imap_do_remove_msgs(Folder *folder, FolderItem *dest,
1188 MsgInfoList *msglist, GRelation *relation)
1191 GSList *seq_list = NULL, *cur;
1193 IMAPSession *session;
1194 gint ok = IMAP_SUCCESS;
1195 GRelation *uid_mapping;
1197 g_return_val_if_fail(folder != NULL, -1);
1198 g_return_val_if_fail(dest != NULL, -1);
1199 g_return_val_if_fail(msglist != NULL, -1);
1201 session = imap_session_get(folder);
1205 msginfo = (MsgInfo *)msglist->data;
1207 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
1208 NULL, NULL, NULL, NULL, FALSE);
1209 if (ok != IMAP_SUCCESS) {
1213 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
1214 for (cur = msglist; cur; cur = cur->next) {
1215 msginfo = (MsgInfo *)cur->data;
1216 seq_list = g_slist_append(seq_list, GINT_TO_POINTER(msginfo->msgnum));
1219 uid_mapping = g_relation_new(2);
1220 g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
1222 ok = imap_set_message_flags
1223 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
1224 seq_list, IMAP_FLAG_DELETED, TRUE);
1225 if (ok != IMAP_SUCCESS) {
1226 log_warning(_("can't set deleted flags\n"));
1229 ok = imap_cmd_expunge(session);
1230 if (ok != IMAP_SUCCESS) {
1231 log_warning(_("can't expunge\n"));
1235 g_relation_destroy(uid_mapping);
1236 g_slist_free(seq_list);
1240 if (ok == IMAP_SUCCESS)
1246 static gint imap_remove_msgs(Folder *folder, FolderItem *dest,
1247 MsgInfoList *msglist, GRelation *relation)
1251 g_return_val_if_fail(folder != NULL, -1);
1252 g_return_val_if_fail(dest != NULL, -1);
1253 if (msglist == NULL)
1256 msginfo = (MsgInfo *)msglist->data;
1257 g_return_val_if_fail(msginfo->folder != NULL, -1);
1259 return imap_do_remove_msgs(folder, dest, msglist, relation);
1262 static gint imap_remove_all_msg(Folder *folder, FolderItem *item)
1264 GSList *list = folder_item_get_msg_list(item);
1265 gint res = imap_remove_msgs(folder, item, list, NULL);
1266 procmsg_msg_list_free(list);
1270 static gboolean imap_is_msg_changed(Folder *folder, FolderItem *item,
1273 /* TODO: properly implement this method */
1277 static gint imap_close(Folder *folder, FolderItem *item)
1282 static gint imap_scan_tree(Folder *folder)
1284 FolderItem *item = NULL;
1285 IMAPSession *session;
1286 gchar *root_folder = NULL;
1288 g_return_val_if_fail(folder != NULL, -1);
1289 g_return_val_if_fail(folder->account != NULL, -1);
1291 session = imap_session_get(folder);
1293 if (!folder->node) {
1294 folder_tree_destroy(folder);
1295 item = folder_item_new(folder, folder->name, NULL);
1296 item->folder = folder;
1297 folder->node = item->node = g_node_new(item);
1302 if (folder->account->imap_dir && *folder->account->imap_dir) {
1307 Xstrdup_a(root_folder, folder->account->imap_dir, {return -1;});
1308 extract_quote(root_folder, '"');
1309 subst_char(root_folder,
1310 imap_get_path_separator(IMAP_FOLDER(folder),
1313 strtailchomp(root_folder, '/');
1314 real_path = imap_get_real_path
1315 (IMAP_FOLDER(folder), root_folder);
1316 debug_print("IMAP root directory: %s\n", real_path);
1318 /* check if root directory exist */
1320 r = imap_threaded_list(session->folder, "", real_path,
1322 if ((r != MAILIMAP_NO_ERROR) || (clist_count(lep_list) == 0)) {
1323 if (!folder->node) {
1324 item = folder_item_new(folder, folder->name, NULL);
1325 item->folder = folder;
1326 folder->node = item->node = g_node_new(item);
1331 mailimap_list_result_free(lep_list);
1337 item = FOLDER_ITEM(folder->node->data);
1338 if (!item || ((item->path || root_folder) &&
1339 strcmp2(item->path, root_folder) != 0)) {
1340 folder_tree_destroy(folder);
1341 item = folder_item_new(folder, folder->name, root_folder);
1342 item->folder = folder;
1343 folder->node = item->node = g_node_new(item);
1346 imap_scan_tree_recursive(session, FOLDER_ITEM(folder->node->data));
1347 imap_create_missing_folders(folder);
1352 static gint imap_scan_tree_recursive(IMAPSession *session, FolderItem *item)
1355 IMAPFolder *imapfolder;
1356 FolderItem *new_item;
1357 GSList *item_list, *cur;
1360 gchar *wildcard_path;
1366 g_return_val_if_fail(item != NULL, -1);
1367 g_return_val_if_fail(item->folder != NULL, -1);
1368 g_return_val_if_fail(item->no_sub == FALSE, -1);
1370 folder = item->folder;
1371 imapfolder = IMAP_FOLDER(folder);
1373 separator = imap_get_path_separator(imapfolder, item->path);
1375 if (folder->ui_func)
1376 folder->ui_func(folder, item, folder->ui_func_data);
1379 wildcard[0] = separator;
1382 real_path = imap_get_real_path(imapfolder, item->path);
1386 real_path = g_strdup("");
1389 Xstrcat_a(wildcard_path, real_path, wildcard,
1390 {g_free(real_path); return IMAP_ERROR;});
1392 r = imap_threaded_list(folder, "", wildcard_path, &lep_list);
1393 if (r != MAILIMAP_NO_ERROR) {
1397 item_list = imap_list_from_lep(imapfolder,
1398 lep_list, real_path);
1399 mailimap_list_result_free(lep_list);
1404 node = item->node->children;
1405 while (node != NULL) {
1406 FolderItem *old_item = FOLDER_ITEM(node->data);
1407 GNode *next = node->next;
1410 for (cur = item_list; cur != NULL; cur = cur->next) {
1411 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1412 if (!strcmp2(old_item->path, cur_item->path)) {
1413 new_item = cur_item;
1418 debug_print("folder '%s' not found. removing...\n",
1420 folder_item_remove(old_item);
1422 old_item->no_sub = new_item->no_sub;
1423 old_item->no_select = new_item->no_select;
1424 if (old_item->no_sub == TRUE && node->children) {
1425 debug_print("folder '%s' doesn't have "
1426 "subfolders. removing...\n",
1428 folder_item_remove_children(old_item);
1435 for (cur = item_list; cur != NULL; cur = cur->next) {
1436 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1438 for (node = item->node->children; node != NULL;
1439 node = node->next) {
1440 if (!strcmp2(FOLDER_ITEM(node->data)->path,
1442 new_item = FOLDER_ITEM(node->data);
1443 folder_item_destroy(cur_item);
1449 new_item = cur_item;
1450 debug_print("new folder '%s' found.\n", new_item->path);
1451 folder_item_append(item, new_item);
1454 if (!strcmp(new_item->path, "INBOX")) {
1455 new_item->stype = F_INBOX;
1456 folder->inbox = new_item;
1457 } else if (!folder_item_parent(item) || item->stype == F_INBOX) {
1460 base = g_path_get_basename(new_item->path);
1462 if (!folder->outbox && !g_ascii_strcasecmp(base, "Sent")) {
1463 new_item->stype = F_OUTBOX;
1464 folder->outbox = new_item;
1465 } else if (!folder->draft && !g_ascii_strcasecmp(base, "Drafts")) {
1466 new_item->stype = F_DRAFT;
1467 folder->draft = new_item;
1468 } else if (!folder->queue && !g_ascii_strcasecmp(base, "Queue")) {
1469 new_item->stype = F_QUEUE;
1470 folder->queue = new_item;
1471 } else if (!folder->trash && !g_ascii_strcasecmp(base, "Trash")) {
1472 new_item->stype = F_TRASH;
1473 folder->trash = new_item;
1478 if (new_item->no_sub == FALSE)
1479 imap_scan_tree_recursive(session, new_item);
1482 g_slist_free(item_list);
1484 return IMAP_SUCCESS;
1487 static gint imap_create_tree(Folder *folder)
1489 g_return_val_if_fail(folder != NULL, -1);
1490 g_return_val_if_fail(folder->node != NULL, -1);
1491 g_return_val_if_fail(folder->node->data != NULL, -1);
1492 g_return_val_if_fail(folder->account != NULL, -1);
1494 imap_scan_tree(folder);
1495 imap_create_missing_folders(folder);
1500 static void imap_create_missing_folders(Folder *folder)
1502 g_return_if_fail(folder != NULL);
1505 folder->inbox = imap_create_special_folder
1506 (folder, F_INBOX, "INBOX");
1508 folder->trash = imap_create_special_folder
1509 (folder, F_TRASH, "Trash");
1511 folder->queue = imap_create_special_folder
1512 (folder, F_QUEUE, "Queue");
1513 if (!folder->outbox)
1514 folder->outbox = imap_create_special_folder
1515 (folder, F_OUTBOX, "Sent");
1517 folder->draft = imap_create_special_folder
1518 (folder, F_DRAFT, "Drafts");
1521 static FolderItem *imap_create_special_folder(Folder *folder,
1522 SpecialFolderItemType stype,
1526 FolderItem *new_item;
1528 g_return_val_if_fail(folder != NULL, NULL);
1529 g_return_val_if_fail(folder->node != NULL, NULL);
1530 g_return_val_if_fail(folder->node->data != NULL, NULL);
1531 g_return_val_if_fail(folder->account != NULL, NULL);
1532 g_return_val_if_fail(name != NULL, NULL);
1534 item = FOLDER_ITEM(folder->node->data);
1535 new_item = imap_create_folder(folder, item, name);
1538 g_warning("Can't create '%s'\n", name);
1539 if (!folder->inbox) return NULL;
1541 new_item = imap_create_folder(folder, folder->inbox, name);
1543 g_warning("Can't create '%s' under INBOX\n", name);
1545 new_item->stype = stype;
1547 new_item->stype = stype;
1552 static gchar *imap_folder_get_path(Folder *folder)
1556 g_return_val_if_fail(folder != NULL, NULL);
1557 g_return_val_if_fail(folder->account != NULL, NULL);
1559 folder_path = g_strconcat(get_imap_cache_dir(),
1561 folder->account->recv_server,
1563 folder->account->userid,
1569 static gchar *imap_item_get_path(Folder *folder, FolderItem *item)
1571 gchar *folder_path, *path;
1573 g_return_val_if_fail(folder != NULL, NULL);
1574 g_return_val_if_fail(item != NULL, NULL);
1575 folder_path = imap_folder_get_path(folder);
1577 g_return_val_if_fail(folder_path != NULL, NULL);
1578 if (folder_path[0] == G_DIR_SEPARATOR) {
1580 path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
1583 path = g_strdup(folder_path);
1586 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1587 folder_path, G_DIR_SEPARATOR_S,
1590 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1593 g_free(folder_path);
1598 static FolderItem *imap_create_folder(Folder *folder, FolderItem *parent,
1601 gchar *dirpath, *imap_path;
1602 IMAPSession *session;
1603 FolderItem *new_item;
1609 g_return_val_if_fail(folder != NULL, NULL);
1610 g_return_val_if_fail(folder->account != NULL, NULL);
1611 g_return_val_if_fail(parent != NULL, NULL);
1612 g_return_val_if_fail(name != NULL, NULL);
1614 session = imap_session_get(folder);
1619 if (!folder_item_parent(parent) && strcmp(name, "INBOX") == 0)
1620 dirpath = g_strdup(name);
1621 else if (parent->path)
1622 dirpath = g_strconcat(parent->path, "/", name, NULL);
1623 else if ((p = strchr(name, '/')) != NULL && *(p + 1) != '\0')
1624 dirpath = g_strdup(name);
1625 else if (folder->account->imap_dir && *folder->account->imap_dir) {
1628 Xstrdup_a(imap_dir, folder->account->imap_dir, {return NULL;});
1629 strtailchomp(imap_dir, '/');
1630 dirpath = g_strconcat(imap_dir, "/", name, NULL);
1632 dirpath = g_strdup(name);
1634 /* keep trailing directory separator to create a folder that contains
1636 imap_path = imap_utf8_to_modified_utf7(dirpath);
1637 strtailchomp(dirpath, '/');
1638 Xstrdup_a(new_name, name, {
1642 strtailchomp(new_name, '/');
1643 separator = imap_get_path_separator(IMAP_FOLDER(folder), imap_path);
1644 imap_path_separator_subst(imap_path, separator);
1645 subst_char(new_name, '/', separator);
1647 if (strcmp(name, "INBOX") != 0) {
1649 gboolean exist = FALSE;
1653 argbuf = g_ptr_array_new();
1654 r = imap_threaded_list(folder, "", imap_path, &lep_list);
1655 if (r != MAILIMAP_NO_ERROR) {
1656 log_warning(_("can't create mailbox: LIST failed\n"));
1659 ptr_array_free_strings(argbuf);
1660 g_ptr_array_free(argbuf, TRUE);
1664 if (clist_count(lep_list) > 0)
1668 ok = imap_cmd_create(session, imap_path);
1669 if (ok != IMAP_SUCCESS) {
1670 log_warning(_("can't create mailbox\n"));
1678 new_item = folder_item_new(folder, new_name, dirpath);
1679 folder_item_append(parent, new_item);
1683 dirpath = folder_item_get_path(new_item);
1684 if (!is_dir_exist(dirpath))
1685 make_dir_hier(dirpath);
1691 static gint imap_rename_folder(Folder *folder, FolderItem *item,
1696 gchar *real_oldpath;
1697 gchar *real_newpath;
1699 gchar *old_cache_dir;
1700 gchar *new_cache_dir;
1701 IMAPSession *session;
1704 gint exists, recent, unseen;
1705 guint32 uid_validity;
1707 g_return_val_if_fail(folder != NULL, -1);
1708 g_return_val_if_fail(item != NULL, -1);
1709 g_return_val_if_fail(item->path != NULL, -1);
1710 g_return_val_if_fail(name != NULL, -1);
1712 if (strchr(name, imap_get_path_separator(IMAP_FOLDER(folder), item->path)) != NULL) {
1713 g_warning(_("New folder name must not contain the namespace "
1718 session = imap_session_get(folder);
1722 real_oldpath = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1724 g_free(session->mbox);
1725 session->mbox = NULL;
1726 ok = imap_cmd_examine(session, "INBOX",
1727 &exists, &recent, &unseen, &uid_validity, FALSE);
1728 if (ok != IMAP_SUCCESS) {
1729 g_free(real_oldpath);
1733 separator = imap_get_path_separator(IMAP_FOLDER(folder), item->path);
1734 if (strchr(item->path, G_DIR_SEPARATOR)) {
1735 dirpath = g_path_get_dirname(item->path);
1736 newpath = g_strconcat(dirpath, G_DIR_SEPARATOR_S, name, NULL);
1739 newpath = g_strdup(name);
1741 real_newpath = imap_utf8_to_modified_utf7(newpath);
1742 imap_path_separator_subst(real_newpath, separator);
1744 ok = imap_cmd_rename(session, real_oldpath, real_newpath);
1745 if (ok != IMAP_SUCCESS) {
1746 log_warning(_("can't rename mailbox: %s to %s\n"),
1747 real_oldpath, real_newpath);
1748 g_free(real_oldpath);
1750 g_free(real_newpath);
1755 item->name = g_strdup(name);
1757 old_cache_dir = folder_item_get_path(item);
1759 paths[0] = g_strdup(item->path);
1761 g_node_traverse(item->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1762 imap_rename_folder_func, paths);
1764 if (is_dir_exist(old_cache_dir)) {
1765 new_cache_dir = folder_item_get_path(item);
1766 if (rename(old_cache_dir, new_cache_dir) < 0) {
1767 FILE_OP_ERROR(old_cache_dir, "rename");
1769 g_free(new_cache_dir);
1772 g_free(old_cache_dir);
1775 g_free(real_oldpath);
1776 g_free(real_newpath);
1781 static gint imap_remove_folder_real(Folder *folder, FolderItem *item)
1784 IMAPSession *session;
1787 gint exists, recent, unseen;
1788 guint32 uid_validity;
1790 g_return_val_if_fail(folder != NULL, -1);
1791 g_return_val_if_fail(item != NULL, -1);
1792 g_return_val_if_fail(item->path != NULL, -1);
1794 session = imap_session_get(folder);
1798 path = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1800 ok = imap_cmd_examine(session, "INBOX",
1801 &exists, &recent, &unseen, &uid_validity, FALSE);
1802 if (ok != IMAP_SUCCESS) {
1807 ok = imap_cmd_delete(session, path);
1808 if (ok != IMAP_SUCCESS) {
1809 log_warning(_("can't delete mailbox\n"));
1815 cache_dir = folder_item_get_path(item);
1816 if (is_dir_exist(cache_dir) && remove_dir_recursive(cache_dir) < 0)
1817 g_warning("can't remove directory '%s'\n", cache_dir);
1819 folder_item_remove(item);
1824 static gint imap_remove_folder(Folder *folder, FolderItem *item)
1828 g_return_val_if_fail(item != NULL, -1);
1829 g_return_val_if_fail(item->folder != NULL, -1);
1830 g_return_val_if_fail(item->node != NULL, -1);
1832 node = item->node->children;
1833 while (node != NULL) {
1835 if (imap_remove_folder(folder, FOLDER_ITEM(node->data)) < 0)
1839 debug_print("IMAP removing %s\n", item->path);
1841 if (imap_remove_all_msg(folder, item) < 0)
1843 return imap_remove_folder_real(folder, item);
1846 typedef struct _uncached_data {
1847 IMAPSession *session;
1849 MsgNumberList *numlist;
1855 static void *imap_get_uncached_messages_thread(void *data)
1857 uncached_data *stuff = (uncached_data *)data;
1858 IMAPSession *session = stuff->session;
1859 FolderItem *item = stuff->item;
1860 MsgNumberList *numlist = stuff->numlist;
1862 GSList *newlist = NULL;
1863 GSList *llast = NULL;
1864 GSList *seq_list, *cur;
1866 debug_print("uncached_messages\n");
1868 if (session == NULL || item == NULL || item->folder == NULL
1869 || FOLDER_CLASS(item->folder) != &imap_class) {
1874 seq_list = imap_get_lep_set_from_numlist(numlist);
1875 debug_print("get msgs info\n");
1876 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
1877 struct mailimap_set * imapset;
1883 imapset = cur->data;
1885 r = imap_threaded_fetch_env(session->folder,
1886 imapset, &env_list);
1887 if (r != MAILIMAP_NO_ERROR)
1891 for(i = 0 ; i < carray_count(env_list) ; i ++) {
1892 struct imap_fetch_env_info * info;
1895 info = carray_get(env_list, i);
1896 msginfo = imap_envelope_from_lep(info, item);
1897 msginfo->folder = item;
1899 llast = newlist = g_slist_append(newlist, msginfo);
1901 llast = g_slist_append(llast, msginfo);
1902 llast = llast->next;
1907 imap_fetch_env_free(env_list);
1910 session_set_access_time(SESSION(session));
1915 #define MAX_MSG_NUM 50
1917 static GSList *imap_get_uncached_messages(IMAPSession *session,
1919 MsgNumberList *numlist)
1921 GSList *result = NULL;
1923 uncached_data *data = g_new0(uncached_data, 1);
1928 data->total = g_slist_length(numlist);
1929 debug_print("messages list : %i\n", data->total);
1931 while (cur != NULL) {
1932 GSList * partial_result;
1940 while (count < MAX_MSG_NUM) {
1945 if (newlist == NULL)
1946 llast = newlist = g_slist_append(newlist, p);
1948 llast = g_slist_append(llast, p);
1949 llast = llast->next;
1959 data->session = session;
1961 data->numlist = newlist;
1964 if (prefs_common.work_offline && !imap_gtk_should_override()) {
1970 (GSList *)imap_get_uncached_messages_thread(data);
1972 statusbar_progress_all(data->cur,data->total, 1);
1974 g_slist_free(newlist);
1976 result = g_slist_concat(result, partial_result);
1980 statusbar_progress_all(0,0,0);
1981 statusbar_pop_all();
1986 static void imap_delete_all_cached_messages(FolderItem *item)
1990 g_return_if_fail(item != NULL);
1991 g_return_if_fail(item->folder != NULL);
1992 g_return_if_fail(FOLDER_CLASS(item->folder) == &imap_class);
1994 debug_print("Deleting all cached messages...\n");
1996 dir = folder_item_get_path(item);
1997 if (is_dir_exist(dir))
1998 remove_all_numbered_files(dir);
2001 debug_print("done.\n");
2004 static IMAPNameSpace *imap_find_namespace_from_list(GList *ns_list,
2007 IMAPNameSpace *namespace = NULL;
2008 gchar *tmp_path, *name;
2010 if (!path) path = "";
2012 for (; ns_list != NULL; ns_list = ns_list->next) {
2013 IMAPNameSpace *tmp_ns = ns_list->data;
2015 Xstrcat_a(tmp_path, path, "/", return namespace);
2016 Xstrdup_a(name, tmp_ns->name, return namespace);
2017 if (tmp_ns->separator && tmp_ns->separator != '/') {
2018 subst_char(tmp_path, tmp_ns->separator, '/');
2019 subst_char(name, tmp_ns->separator, '/');
2021 if (strncmp(tmp_path, name, strlen(name)) == 0)
2028 static IMAPNameSpace *imap_find_namespace(IMAPFolder *folder,
2031 IMAPNameSpace *namespace;
2033 g_return_val_if_fail(folder != NULL, NULL);
2035 namespace = imap_find_namespace_from_list(folder->ns_personal, path);
2036 if (namespace) return namespace;
2037 namespace = imap_find_namespace_from_list(folder->ns_others, path);
2038 if (namespace) return namespace;
2039 namespace = imap_find_namespace_from_list(folder->ns_shared, path);
2040 if (namespace) return namespace;
2046 static gchar imap_get_path_separator(IMAPFolder *folder, const gchar *path)
2048 IMAPNameSpace *namespace;
2049 gchar separator = '/';
2051 if (folder->last_seen_separator == 0) {
2053 int r = imap_threaded_list((Folder *)folder, "", "", &lep_list);
2054 if (r != MAILIMAP_NO_ERROR) {
2055 log_warning(_("LIST failed\n"));
2059 if (clist_count(lep_list) > 0) {
2060 clistiter * iter = clist_begin(lep_list);
2061 struct mailimap_mailbox_list * mb;
2062 mb = clist_content(iter);
2064 folder->last_seen_separator = mb->mb_delimiter;
2065 debug_print("got separator: %c\n", folder->last_seen_separator);
2067 mailimap_list_result_free(lep_list);
2070 if (folder->last_seen_separator != 0) {
2071 debug_print("using separator: %c\n", folder->last_seen_separator);
2072 return folder->last_seen_separator;
2075 namespace = imap_find_namespace(folder, path);
2076 if (namespace && namespace->separator)
2077 separator = namespace->separator;
2082 static gchar *imap_get_real_path(IMAPFolder *folder, const gchar *path)
2087 g_return_val_if_fail(folder != NULL, NULL);
2088 g_return_val_if_fail(path != NULL, NULL);
2090 real_path = imap_utf8_to_modified_utf7(path);
2091 separator = imap_get_path_separator(folder, path);
2092 imap_path_separator_subst(real_path, separator);
2097 static gint imap_set_message_flags(IMAPSession *session,
2098 MsgNumberList *numlist,
2106 seq_list = imap_get_lep_set_from_numlist(numlist);
2108 for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) {
2109 struct mailimap_set * imapset;
2111 imapset = cur->data;
2113 ok = imap_cmd_store(session, imapset,
2117 imap_lep_set_free(seq_list);
2119 return IMAP_SUCCESS;
2122 typedef struct _select_data {
2123 IMAPSession *session;
2128 guint32 *uid_validity;
2132 static gint imap_select(IMAPSession *session, IMAPFolder *folder,
2134 gint *exists, gint *recent, gint *unseen,
2135 guint32 *uid_validity, gboolean block)
2139 gint exists_, recent_, unseen_;
2140 guint32 uid_validity_;
2142 if (!exists || !recent || !unseen || !uid_validity) {
2143 if (session->mbox && strcmp(session->mbox, path) == 0)
2144 return IMAP_SUCCESS;
2148 uid_validity = &uid_validity_;
2151 g_free(session->mbox);
2152 session->mbox = NULL;
2154 real_path = imap_get_real_path(folder, path);
2156 ok = imap_cmd_select(session, real_path,
2157 exists, recent, unseen, uid_validity, block);
2158 if (ok != IMAP_SUCCESS)
2159 log_warning(_("can't select folder: %s\n"), real_path);
2161 session->mbox = g_strdup(path);
2162 session->folder_content_changed = FALSE;
2169 static gint imap_status(IMAPSession *session, IMAPFolder *folder,
2171 gint *messages, gint *recent,
2172 guint32 *uid_next, guint32 *uid_validity,
2173 gint *unseen, gboolean block)
2177 struct mailimap_mailbox_data_status * data_status;
2181 real_path = imap_get_real_path(folder, path);
2183 r = imap_threaded_status(FOLDER(folder), real_path, &data_status);
2185 if (r != MAILIMAP_NO_ERROR) {
2186 debug_print("status err %d\n", r);
2190 if (data_status->st_info_list == NULL) {
2191 mailimap_mailbox_data_status_free(data_status);
2192 debug_print("status->st_info_list == NULL\n");
2197 for(iter = clist_begin(data_status->st_info_list) ; iter != NULL ;
2198 iter = clist_next(iter)) {
2199 struct mailimap_status_info * info;
2201 info = clist_content(iter);
2202 switch (info->st_att) {
2203 case MAILIMAP_STATUS_ATT_MESSAGES:
2204 * messages = info->st_value;
2205 got_values |= 1 << 0;
2208 case MAILIMAP_STATUS_ATT_RECENT:
2209 * recent = info->st_value;
2210 got_values |= 1 << 1;
2213 case MAILIMAP_STATUS_ATT_UIDNEXT:
2214 * uid_next = info->st_value;
2215 got_values |= 1 << 2;
2218 case MAILIMAP_STATUS_ATT_UIDVALIDITY:
2219 * uid_validity = info->st_value;
2220 got_values |= 1 << 3;
2223 case MAILIMAP_STATUS_ATT_UNSEEN:
2224 * unseen = info->st_value;
2225 got_values |= 1 << 4;
2229 mailimap_mailbox_data_status_free(data_status);
2231 if (got_values != ((1 << 4) + (1 << 3) +
2232 (1 << 2) + (1 << 1) + (1 << 0))) {
2233 debug_print("status: incomplete values received (%d)\n", got_values);
2236 return IMAP_SUCCESS;
2239 static void imap_free_capabilities(IMAPSession *session)
2241 slist_free_strings(session->capability);
2242 g_slist_free(session->capability);
2243 session->capability = NULL;
2246 /* low-level IMAP4rev1 commands */
2249 static gint imap_cmd_authenticate(IMAPSession *session, const gchar *user,
2250 const gchar *pass, IMAPAuthType type)
2257 gchar hexdigest[33];
2261 auth_type = "CRAM-MD5";
2263 imap_gen_send(session, "AUTHENTICATE %s", auth_type);
2264 ok = imap_gen_recv(session, &buf);
2265 if (ok != IMAP_SUCCESS || buf[0] != '+' || buf[1] != ' ') {
2270 challenge = g_malloc(strlen(buf + 2) + 1);
2271 challenge_len = base64_decode(challenge, buf + 2, -1);
2272 challenge[challenge_len] = '\0';
2275 md5_hex_hmac(hexdigest, challenge, challenge_len, pass, strlen(pass));
2278 response = g_strdup_printf("%s %s", user, hexdigest);
2279 response64 = g_malloc((strlen(response) + 3) * 2 + 1);
2280 base64_encode(response64, response, strlen(response));
2283 sock_puts(SESSION(session)->sock, response64);
2284 ok = imap_cmd_ok(session, NULL);
2285 if (ok != IMAP_SUCCESS)
2286 log_warning(_("IMAP4 authentication failed.\n"));
2292 static gint imap_cmd_login(IMAPSession *session,
2293 const gchar *user, const gchar *pass,
2299 log_print("IMAP4> Logging %s to %s using %s\n",
2301 SESSION(session)->server,
2303 r = imap_threaded_login(session->folder, user, pass, type);
2304 if (r != MAILIMAP_NO_ERROR) {
2305 log_error("IMAP4< Error logging in to %s\n",
2306 SESSION(session)->server);
2314 static gint imap_cmd_logout(IMAPSession *session)
2316 imap_threaded_disconnect(session->folder);
2318 return IMAP_SUCCESS;
2321 static gint imap_cmd_noop(IMAPSession *session)
2324 unsigned int exists;
2326 r = imap_threaded_noop(session->folder, &exists);
2327 if (r != MAILIMAP_NO_ERROR) {
2328 debug_print("noop err %d\n", r);
2331 session->exists = exists;
2332 session_set_access_time(SESSION(session));
2334 return IMAP_SUCCESS;
2338 static gint imap_cmd_starttls(IMAPSession *session)
2342 r = imap_threaded_starttls(session->folder);
2343 if (r != MAILIMAP_NO_ERROR) {
2344 debug_print("starttls err %d\n", r);
2347 return IMAP_SUCCESS;
2351 static gint imap_cmd_select(IMAPSession *session, const gchar *folder,
2352 gint *exists, gint *recent, gint *unseen,
2353 guint32 *uid_validity, gboolean block)
2357 r = imap_threaded_select(session->folder, folder,
2358 exists, recent, unseen, uid_validity);
2359 if (r != MAILIMAP_NO_ERROR) {
2360 debug_print("select err %d\n", r);
2363 return IMAP_SUCCESS;
2366 static gint imap_cmd_examine(IMAPSession *session, const gchar *folder,
2367 gint *exists, gint *recent, gint *unseen,
2368 guint32 *uid_validity, gboolean block)
2372 r = imap_threaded_examine(session->folder, folder,
2373 exists, recent, unseen, uid_validity);
2374 if (r != MAILIMAP_NO_ERROR) {
2375 debug_print("examine err %d\n", r);
2379 return IMAP_SUCCESS;
2382 static gint imap_cmd_create(IMAPSession *session, const gchar *folder)
2386 r = imap_threaded_create(session->folder, folder);
2387 if (r != MAILIMAP_NO_ERROR) {
2392 return IMAP_SUCCESS;
2395 static gint imap_cmd_rename(IMAPSession *session, const gchar *old_folder,
2396 const gchar *new_folder)
2400 r = imap_threaded_rename(session->folder, old_folder,
2402 if (r != MAILIMAP_NO_ERROR) {
2407 return IMAP_SUCCESS;
2410 static gint imap_cmd_delete(IMAPSession *session, const gchar *folder)
2415 r = imap_threaded_delete(session->folder, folder);
2416 if (r != MAILIMAP_NO_ERROR) {
2421 return IMAP_SUCCESS;
2424 typedef struct _fetch_data {
2425 IMAPSession *session;
2427 const gchar *filename;
2433 static void *imap_cmd_fetch_thread(void *data)
2435 fetch_data *stuff = (fetch_data *)data;
2436 IMAPSession *session = stuff->session;
2437 guint32 uid = stuff->uid;
2438 const gchar *filename = stuff->filename;
2442 r = imap_threaded_fetch_content(session->folder,
2446 r = imap_threaded_fetch_content(session->folder,
2449 if (r != MAILIMAP_NO_ERROR) {
2450 debug_print("fetch err %d\n", r);
2451 return GINT_TO_POINTER(IMAP_ERROR);
2453 return GINT_TO_POINTER(IMAP_SUCCESS);
2456 static gint imap_cmd_fetch(IMAPSession *session, guint32 uid,
2457 const gchar *filename, gboolean headers,
2460 fetch_data *data = g_new0(fetch_data, 1);
2463 data->session = session;
2465 data->filename = filename;
2466 data->headers = headers;
2469 if (prefs_common.work_offline && !imap_gtk_should_override()) {
2473 statusbar_print_all(_("Fetching message..."));
2474 result = GPOINTER_TO_INT(imap_cmd_fetch_thread(data));
2475 statusbar_pop_all();
2481 static gint imap_cmd_append(IMAPSession *session, const gchar *destfolder,
2482 const gchar *file, IMAPFlags flags,
2485 struct mailimap_flag_list * flag_list;
2488 g_return_val_if_fail(file != NULL, IMAP_ERROR);
2490 flag_list = imap_flag_to_lep(flags);
2491 statusbar_print_all(_("Adding messages..."));
2492 r = imap_threaded_append(session->folder, destfolder,
2494 statusbar_pop_all();
2495 if (new_uid != NULL)
2498 if (r != MAILIMAP_NO_ERROR) {
2499 debug_print("append err %d\n", r);
2502 return IMAP_SUCCESS;
2505 static gint imap_cmd_copy(IMAPSession *session, struct mailimap_set * set,
2506 const gchar *destfolder, GRelation *uid_mapping)
2510 g_return_val_if_fail(session != NULL, IMAP_ERROR);
2511 g_return_val_if_fail(set != NULL, IMAP_ERROR);
2512 g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
2514 statusbar_print_all(_("Copying messages..."));
2515 r = imap_threaded_copy(session->folder, set, destfolder);
2516 statusbar_pop_all();
2517 if (r != MAILIMAP_NO_ERROR) {
2522 return IMAP_SUCCESS;
2525 static gint imap_cmd_store(IMAPSession *session, struct mailimap_set * set,
2526 IMAPFlags flags, int do_add)
2529 struct mailimap_flag_list * flag_list;
2530 struct mailimap_store_att_flags * store_att_flags;
2532 flag_list = imap_flag_to_lep(flags);
2536 mailimap_store_att_flags_new_add_flags_silent(flag_list);
2539 mailimap_store_att_flags_new_remove_flags_silent(flag_list);
2541 r = imap_threaded_store(session->folder, set, store_att_flags);
2542 if (r != MAILIMAP_NO_ERROR) {
2547 return IMAP_SUCCESS;
2550 static gint imap_cmd_expunge(IMAPSession *session)
2554 if (prefs_common.work_offline && !imap_gtk_should_override()) {
2558 r = imap_threaded_expunge(session->folder);
2559 if (r != MAILIMAP_NO_ERROR) {
2564 return IMAP_SUCCESS;
2567 static void imap_path_separator_subst(gchar *str, gchar separator)
2570 gboolean in_escape = FALSE;
2572 if (!separator || separator == '/') return;
2574 for (p = str; *p != '\0'; p++) {
2575 if (*p == '/' && !in_escape)
2577 else if (*p == '&' && *(p + 1) != '-' && !in_escape)
2579 else if (*p == '-' && in_escape)
2584 static gchar *imap_modified_utf7_to_utf8(const gchar *mutf7_str)
2586 static iconv_t cd = (iconv_t)-1;
2587 static gboolean iconv_ok = TRUE;
2590 size_t norm_utf7_len;
2592 gchar *to_str, *to_p;
2594 gboolean in_escape = FALSE;
2596 if (!iconv_ok) return g_strdup(mutf7_str);
2598 if (cd == (iconv_t)-1) {
2599 cd = iconv_open(CS_INTERNAL, CS_UTF_7);
2600 if (cd == (iconv_t)-1) {
2601 g_warning("iconv cannot convert UTF-7 to %s\n",
2604 return g_strdup(mutf7_str);
2608 /* modified UTF-7 to normal UTF-7 conversion */
2609 norm_utf7 = g_string_new(NULL);
2611 for (p = mutf7_str; *p != '\0'; p++) {
2612 /* replace: '&' -> '+',
2614 escaped ',' -> '/' */
2615 if (!in_escape && *p == '&') {
2616 if (*(p + 1) != '-') {
2617 g_string_append_c(norm_utf7, '+');
2620 g_string_append_c(norm_utf7, '&');
2623 } else if (in_escape && *p == ',') {
2624 g_string_append_c(norm_utf7, '/');
2625 } else if (in_escape && *p == '-') {
2626 g_string_append_c(norm_utf7, '-');
2629 g_string_append_c(norm_utf7, *p);
2633 norm_utf7_p = norm_utf7->str;
2634 norm_utf7_len = norm_utf7->len;
2635 to_len = strlen(mutf7_str) * 5;
2636 to_p = to_str = g_malloc(to_len + 1);
2638 if (iconv(cd, (ICONV_CONST gchar **)&norm_utf7_p, &norm_utf7_len,
2639 &to_p, &to_len) == -1) {
2640 g_warning(_("iconv cannot convert UTF-7 to %s\n"),
2641 conv_get_locale_charset_str());
2642 g_string_free(norm_utf7, TRUE);
2644 return g_strdup(mutf7_str);
2647 /* second iconv() call for flushing */
2648 iconv(cd, NULL, NULL, &to_p, &to_len);
2649 g_string_free(norm_utf7, TRUE);
2655 static gchar *imap_utf8_to_modified_utf7(const gchar *from)
2657 static iconv_t cd = (iconv_t)-1;
2658 static gboolean iconv_ok = TRUE;
2659 gchar *norm_utf7, *norm_utf7_p;
2660 size_t from_len, norm_utf7_len;
2662 gchar *from_tmp, *to, *p;
2663 gboolean in_escape = FALSE;
2665 if (!iconv_ok) return g_strdup(from);
2667 if (cd == (iconv_t)-1) {
2668 cd = iconv_open(CS_UTF_7, CS_INTERNAL);
2669 if (cd == (iconv_t)-1) {
2670 g_warning(_("iconv cannot convert %s to UTF-7\n"),
2673 return g_strdup(from);
2677 /* UTF-8 to normal UTF-7 conversion */
2678 Xstrdup_a(from_tmp, from, return g_strdup(from));
2679 from_len = strlen(from);
2680 norm_utf7_len = from_len * 5;
2681 Xalloca(norm_utf7, norm_utf7_len + 1, return g_strdup(from));
2682 norm_utf7_p = norm_utf7;
2684 #define IS_PRINT(ch) (isprint(ch) && IS_ASCII(ch))
2686 while (from_len > 0) {
2687 if (*from_tmp == '+') {
2688 *norm_utf7_p++ = '+';
2689 *norm_utf7_p++ = '-';
2693 } else if (IS_PRINT(*(guchar *)from_tmp)) {
2694 /* printable ascii char */
2695 *norm_utf7_p = *from_tmp;
2701 size_t conv_len = 0;
2703 /* unprintable char: convert to UTF-7 */
2705 while (!IS_PRINT(*(guchar *)p) && conv_len < from_len) {
2706 conv_len += g_utf8_skip[*(guchar *)p];
2707 p += g_utf8_skip[*(guchar *)p];
2710 from_len -= conv_len;
2711 if (iconv(cd, (ICONV_CONST gchar **)&from_tmp,
2713 &norm_utf7_p, &norm_utf7_len) == -1) {
2714 g_warning(_("iconv cannot convert UTF-8 to UTF-7\n"));
2715 return g_strdup(from);
2718 /* second iconv() call for flushing */
2719 iconv(cd, NULL, NULL, &norm_utf7_p, &norm_utf7_len);
2725 *norm_utf7_p = '\0';
2726 to_str = g_string_new(NULL);
2727 for (p = norm_utf7; p < norm_utf7_p; p++) {
2728 /* replace: '&' -> "&-",
2731 BASE64 '/' -> ',' */
2732 if (!in_escape && *p == '&') {
2733 g_string_append(to_str, "&-");
2734 } else if (!in_escape && *p == '+') {
2735 if (*(p + 1) == '-') {
2736 g_string_append_c(to_str, '+');
2739 g_string_append_c(to_str, '&');
2742 } else if (in_escape && *p == '/') {
2743 g_string_append_c(to_str, ',');
2744 } else if (in_escape && *p == '-') {
2745 g_string_append_c(to_str, '-');
2748 g_string_append_c(to_str, *p);
2754 g_string_append_c(to_str, '-');
2758 g_string_free(to_str, FALSE);
2763 static gboolean imap_rename_folder_func(GNode *node, gpointer data)
2765 FolderItem *item = node->data;
2766 gchar **paths = data;
2767 const gchar *oldpath = paths[0];
2768 const gchar *newpath = paths[1];
2770 gchar *new_itempath;
2773 oldpathlen = strlen(oldpath);
2774 if (strncmp(oldpath, item->path, oldpathlen) != 0) {
2775 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
2779 base = item->path + oldpathlen;
2780 while (*base == G_DIR_SEPARATOR) base++;
2782 new_itempath = g_strdup(newpath);
2784 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
2787 item->path = new_itempath;
2792 typedef struct _get_list_uid_data {
2794 IMAPFolderItem *item;
2795 GSList **msgnum_list;
2797 } get_list_uid_data;
2799 static void *get_list_of_uids_thread(void *data)
2801 get_list_uid_data *stuff = (get_list_uid_data *)data;
2802 Folder *folder = stuff->folder;
2803 IMAPFolderItem *item = stuff->item;
2804 GSList **msgnum_list = stuff->msgnum_list;
2805 gint ok, nummsgs = 0, lastuid_old;
2806 IMAPSession *session;
2807 GSList *uidlist, *elem;
2808 struct mailimap_set * set;
2809 clist * lep_uidlist;
2812 session = imap_session_get(folder);
2813 if (session == NULL) {
2815 return GINT_TO_POINTER(-1);
2818 ok = imap_select(session, IMAP_FOLDER(folder), item->item.path,
2819 NULL, NULL, NULL, NULL, TRUE);
2820 if (ok != IMAP_SUCCESS) {
2822 return GINT_TO_POINTER(-1);
2827 set = mailimap_set_new_interval(item->lastuid + 1, 0);
2828 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SIMPLE, set,
2830 if (r == MAILIMAP_NO_ERROR) {
2831 GSList * fetchuid_list;
2834 imap_uid_list_from_lep(lep_uidlist);
2835 uidlist = g_slist_concat(fetchuid_list, uidlist);
2838 GSList * fetchuid_list;
2839 carray * lep_uidtab;
2841 r = imap_threaded_fetch_uid(folder, item->lastuid + 1,
2843 if (r == MAILIMAP_NO_ERROR) {
2845 imap_uid_list_from_lep_tab(lep_uidtab);
2846 uidlist = g_slist_concat(fetchuid_list, uidlist);
2850 lastuid_old = item->lastuid;
2851 *msgnum_list = g_slist_copy(item->uid_list);
2852 nummsgs = g_slist_length(*msgnum_list);
2853 debug_print("Got %d uids from cache\n", g_slist_length(item->uid_list));
2855 for (elem = uidlist; elem != NULL; elem = g_slist_next(elem)) {
2858 msgnum = GPOINTER_TO_INT(elem->data);
2859 if (msgnum > lastuid_old) {
2860 *msgnum_list = g_slist_prepend(*msgnum_list, GINT_TO_POINTER(msgnum));
2861 item->uid_list = g_slist_prepend(item->uid_list, GINT_TO_POINTER(msgnum));
2864 if(msgnum > item->lastuid)
2865 item->lastuid = msgnum;
2868 g_slist_free(uidlist);
2871 return GINT_TO_POINTER(nummsgs);
2874 static gint get_list_of_uids(Folder *folder, IMAPFolderItem *item, GSList **msgnum_list)
2877 get_list_uid_data *data = g_new0(get_list_uid_data, 1);
2879 data->folder = folder;
2881 data->msgnum_list = msgnum_list;
2883 if (prefs_common.work_offline && !imap_gtk_should_override()) {
2888 result = GPOINTER_TO_INT(get_list_of_uids_thread(data));
2894 gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list, gboolean *old_uids_valid)
2896 IMAPFolderItem *item = (IMAPFolderItem *)_item;
2897 IMAPSession *session;
2898 gint ok, nummsgs = 0, exists, recent, uid_val, uid_next, unseen;
2899 GSList *uidlist = NULL;
2901 gboolean selected_folder;
2903 debug_print("get_num_list\n");
2904 statusbar_print_all("Scanning %s...\n", FOLDER_ITEM(item)->path
2905 ? FOLDER_ITEM(item)->path:"");
2907 g_return_val_if_fail(folder != NULL, -1);
2908 g_return_val_if_fail(item != NULL, -1);
2909 g_return_val_if_fail(item->item.path != NULL, -1);
2910 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
2911 g_return_val_if_fail(folder->account != NULL, -1);
2913 session = imap_session_get(folder);
2914 g_return_val_if_fail(session != NULL, -1);
2916 selected_folder = (session->mbox != NULL) &&
2917 (!strcmp(session->mbox, item->item.path));
2918 if (selected_folder) {
2919 ok = imap_cmd_noop(session);
2920 if (ok != IMAP_SUCCESS) {
2921 debug_print("disconnected!\n");
2922 session = imap_reconnect_if_possible(folder, session);
2923 if (session == NULL) {
2924 statusbar_pop_all();
2928 exists = session->exists;
2930 *old_uids_valid = TRUE;
2932 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
2933 &exists, &recent, &uid_next, &uid_val, &unseen, FALSE);
2934 if (ok != IMAP_SUCCESS) {
2935 statusbar_pop_all();
2938 if(item->item.mtime == uid_val)
2939 *old_uids_valid = TRUE;
2941 *old_uids_valid = FALSE;
2943 debug_print("Freeing imap uid cache\n");
2945 g_slist_free(item->uid_list);
2946 item->uid_list = NULL;
2948 item->item.mtime = uid_val;
2950 imap_delete_all_cached_messages((FolderItem *)item);
2954 if (!selected_folder)
2955 item->uid_next = uid_next;
2957 /* If old uid_next matches new uid_next we can be sure no message
2958 was added to the folder */
2959 if (( selected_folder && !session->folder_content_changed) ||
2960 (!selected_folder && uid_next == item->uid_next)) {
2961 nummsgs = g_slist_length(item->uid_list);
2963 /* If number of messages is still the same we
2964 know our caches message numbers are still valid,
2965 otherwise if the number of messages has decrease
2966 we discard our cache to start a new scan to find
2967 out which numbers have been removed */
2968 if (exists == nummsgs) {
2969 *msgnum_list = g_slist_copy(item->uid_list);
2970 statusbar_pop_all();
2972 } else if (exists < nummsgs) {
2973 debug_print("Freeing imap uid cache");
2975 g_slist_free(item->uid_list);
2976 item->uid_list = NULL;
2981 *msgnum_list = NULL;
2982 statusbar_pop_all();
2986 nummsgs = get_list_of_uids(folder, item, &uidlist);
2989 statusbar_pop_all();
2993 if (nummsgs != exists) {
2994 /* Cache contains more messages then folder, we have cached
2995 an old UID of a message that was removed and new messages
2996 have been added too, otherwise the uid_next check would
2998 debug_print("Freeing imap uid cache");
3000 g_slist_free(item->uid_list);
3001 item->uid_list = NULL;
3003 g_slist_free(*msgnum_list);
3005 nummsgs = get_list_of_uids(folder, item, &uidlist);
3008 *msgnum_list = uidlist;
3010 dir = folder_item_get_path((FolderItem *)item);
3011 debug_print("removing old messages from %s\n", dir);
3012 remove_numbered_files_not_in_list(dir, *msgnum_list);
3015 debug_print("get_num_list - ok - %i\n", nummsgs);
3016 statusbar_pop_all();
3020 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item)
3025 flags.perm_flags = MSG_NEW|MSG_UNREAD;
3026 flags.tmp_flags = 0;
3028 g_return_val_if_fail(item != NULL, NULL);
3029 g_return_val_if_fail(file != NULL, NULL);
3031 if (folder_has_parent_of_type(item, F_QUEUE)) {
3032 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
3033 } else if (folder_has_parent_of_type(item, F_DRAFT)) {
3034 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
3037 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3038 if (!msginfo) return NULL;
3040 msginfo->plaintext_file = g_strdup(file);
3041 msginfo->folder = item;
3046 GSList *imap_get_msginfos(Folder *folder, FolderItem *item,
3047 GSList *msgnum_list)
3049 IMAPSession *session;
3050 MsgInfoList *ret = NULL;
3053 debug_print("get_msginfos\n");
3055 g_return_val_if_fail(folder != NULL, NULL);
3056 g_return_val_if_fail(item != NULL, NULL);
3057 g_return_val_if_fail(msgnum_list != NULL, NULL);
3059 session = imap_session_get(folder);
3060 g_return_val_if_fail(session != NULL, NULL);
3062 debug_print("IMAP getting msginfos\n");
3063 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3064 NULL, NULL, NULL, NULL, FALSE);
3065 if (ok != IMAP_SUCCESS)
3068 if (!(folder_has_parent_of_type(item, F_DRAFT) ||
3069 folder_has_parent_of_type(item, F_QUEUE))) {
3070 ret = g_slist_concat(ret,
3071 imap_get_uncached_messages(session, item,
3074 MsgNumberList *sorted_list, *elem;
3075 gint startnum, lastnum;
3077 sorted_list = g_slist_sort(g_slist_copy(msgnum_list), g_int_compare);
3079 startnum = lastnum = GPOINTER_TO_INT(sorted_list->data);
3081 for (elem = sorted_list;; elem = g_slist_next(elem)) {
3085 num = GPOINTER_TO_INT(elem->data);
3087 if (num > lastnum + 1 || elem == NULL) {
3089 for (i = startnum; i <= lastnum; ++i) {
3092 file = imap_fetch_msg(folder, item, i);
3094 MsgInfo *msginfo = imap_parse_msg(file, item);
3095 if (msginfo != NULL) {
3096 msginfo->msgnum = i;
3097 ret = g_slist_append(ret, msginfo);
3111 g_slist_free(sorted_list);
3117 MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid)
3119 MsgInfo *msginfo = NULL;
3120 MsgInfoList *msginfolist;
3121 MsgNumberList numlist;
3123 numlist.next = NULL;
3124 numlist.data = GINT_TO_POINTER(uid);
3126 msginfolist = imap_get_msginfos(folder, item, &numlist);
3127 if (msginfolist != NULL) {
3128 msginfo = msginfolist->data;
3129 g_slist_free(msginfolist);
3135 gboolean imap_scan_required(Folder *folder, FolderItem *_item)
3137 IMAPSession *session;
3138 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3139 gint ok, exists = 0, recent = 0, unseen = 0;
3140 guint32 uid_next, uid_val = 0;
3141 gboolean selected_folder;
3143 g_return_val_if_fail(folder != NULL, FALSE);
3144 g_return_val_if_fail(item != NULL, FALSE);
3145 g_return_val_if_fail(item->item.folder != NULL, FALSE);
3146 g_return_val_if_fail(FOLDER_CLASS(item->item.folder) == &imap_class, FALSE);
3148 if (item->item.path == NULL)
3151 session = imap_session_get(folder);
3152 g_return_val_if_fail(session != NULL, FALSE);
3154 selected_folder = (session->mbox != NULL) &&
3155 (!strcmp(session->mbox, item->item.path));
3156 if (selected_folder) {
3157 ok = imap_cmd_noop(session);
3158 if (ok != IMAP_SUCCESS) {
3159 debug_print("disconnected!\n");
3160 session = imap_reconnect_if_possible(folder, session);
3161 if (session == NULL)
3165 if (session->folder_content_changed
3166 || session->exists != item->item.total_msgs)
3169 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
3170 &exists, &recent, &uid_next, &uid_val, &unseen, FALSE);
3171 if (ok != IMAP_SUCCESS)
3174 if ((uid_next != item->uid_next) || (exists != item->item.total_msgs))
3181 void imap_change_flags(Folder *folder, FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
3183 IMAPSession *session;
3184 IMAPFlags flags_set = 0, flags_unset = 0;
3185 gint ok = IMAP_SUCCESS;
3186 MsgNumberList numlist;
3187 hashtable_data *ht_data = NULL;
3189 g_return_if_fail(folder != NULL);
3190 g_return_if_fail(folder->klass == &imap_class);
3191 g_return_if_fail(item != NULL);
3192 g_return_if_fail(item->folder == folder);
3193 g_return_if_fail(msginfo != NULL);
3194 g_return_if_fail(msginfo->folder == item);
3196 session = imap_session_get(folder);
3200 if ((ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
3201 NULL, NULL, NULL, NULL, FALSE)) != IMAP_SUCCESS) {
3205 if (!MSG_IS_MARKED(msginfo->flags) && (newflags & MSG_MARKED))
3206 flags_set |= IMAP_FLAG_FLAGGED;
3207 if ( MSG_IS_MARKED(msginfo->flags) && !(newflags & MSG_MARKED))
3208 flags_unset |= IMAP_FLAG_FLAGGED;
3210 if (!MSG_IS_UNREAD(msginfo->flags) && (newflags & MSG_UNREAD))
3211 flags_unset |= IMAP_FLAG_SEEN;
3212 if ( MSG_IS_UNREAD(msginfo->flags) && !(newflags & MSG_UNREAD))
3213 flags_set |= IMAP_FLAG_SEEN;
3215 if (!MSG_IS_REPLIED(msginfo->flags) && (newflags & MSG_REPLIED))
3216 flags_set |= IMAP_FLAG_ANSWERED;
3217 if ( MSG_IS_REPLIED(msginfo->flags) && !(newflags & MSG_REPLIED))
3218 flags_unset |= IMAP_FLAG_ANSWERED;
3220 if (!MSG_IS_DELETED(msginfo->flags) && (newflags & MSG_DELETED))
3221 flags_set |= IMAP_FLAG_DELETED;
3222 if ( MSG_IS_DELETED(msginfo->flags) && !(newflags & MSG_DELETED))
3223 flags_unset |= IMAP_FLAG_DELETED;
3225 numlist.next = NULL;
3226 numlist.data = GINT_TO_POINTER(msginfo->msgnum);
3228 if (IMAP_FOLDER_ITEM(item)->batching) {
3229 /* instead of performing an UID STORE command for each message change,
3230 * as a lot of them can change "together", we just fill in hashtables
3231 * and defer the treatment so that we're able to send only one
3234 debug_print("IMAP batch mode on, deferring flags change\n");
3236 ht_data = g_hash_table_lookup(flags_set_table, GINT_TO_POINTER(flags_set));
3237 if (ht_data == NULL) {
3238 ht_data = g_new0(hashtable_data, 1);
3239 ht_data->session = session;
3240 g_hash_table_insert(flags_set_table, GINT_TO_POINTER(flags_set), ht_data);
3242 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
3243 ht_data->msglist = g_slist_prepend(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum));
3246 ht_data = g_hash_table_lookup(flags_unset_table, GINT_TO_POINTER(flags_unset));
3247 if (ht_data == NULL) {
3248 ht_data = g_new0(hashtable_data, 1);
3249 ht_data->session = session;
3250 g_hash_table_insert(flags_unset_table, GINT_TO_POINTER(flags_unset), ht_data);
3252 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
3253 ht_data->msglist = g_slist_prepend(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum));
3256 debug_print("IMAP changing flags\n");
3258 ok = imap_set_message_flags(session, &numlist, flags_set, TRUE);
3259 if (ok != IMAP_SUCCESS) {
3265 ok = imap_set_message_flags(session, &numlist, flags_unset, FALSE);
3266 if (ok != IMAP_SUCCESS) {
3271 msginfo->flags.perm_flags = newflags;
3276 static gint imap_remove_msg(Folder *folder, FolderItem *item, gint uid)
3279 IMAPSession *session;
3281 MsgNumberList numlist;
3283 g_return_val_if_fail(folder != NULL, -1);
3284 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3285 g_return_val_if_fail(item != NULL, -1);
3287 session = imap_session_get(folder);
3288 if (!session) return -1;
3290 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3291 NULL, NULL, NULL, NULL, FALSE);
3292 if (ok != IMAP_SUCCESS)
3295 numlist.next = NULL;
3296 numlist.data = GINT_TO_POINTER(uid);
3298 ok = imap_set_message_flags
3299 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
3300 &numlist, IMAP_FLAG_DELETED, TRUE);
3301 if (ok != IMAP_SUCCESS) {
3302 log_warning(_("can't set deleted flags: %d\n"), uid);
3306 if (!session->uidplus) {
3307 ok = imap_cmd_expunge(session);
3311 uidstr = g_strdup_printf("%u", uid);
3312 ok = imap_cmd_expunge(session);
3315 if (ok != IMAP_SUCCESS) {
3316 log_warning(_("can't expunge\n"));
3320 IMAP_FOLDER_ITEM(item)->uid_list = g_slist_remove(
3321 IMAP_FOLDER_ITEM(item)->uid_list, numlist.data);
3322 dir = folder_item_get_path(item);
3323 if (is_dir_exist(dir))
3324 remove_numbered_files(dir, uid, uid);
3327 return IMAP_SUCCESS;
3330 static gint compare_msginfo(gconstpointer a, gconstpointer b)
3332 return ((MsgInfo *)a)->msgnum - ((MsgInfo *)b)->msgnum;
3335 static guint gslist_find_next_num(MsgNumberList **list, guint num)
3339 g_return_val_if_fail(list != NULL, -1);
3341 for (elem = *list; elem != NULL; elem = g_slist_next(elem))
3342 if (GPOINTER_TO_INT(elem->data) >= num)
3345 return elem != NULL ? GPOINTER_TO_INT(elem->data) : (gint)-1;
3349 * NEW and DELETED flags are not syncronized
3350 * - The NEW/RECENT flags in IMAP folders can not really be directly
3351 * modified by Sylpheed
3352 * - The DELETE/DELETED flag in IMAP and Sylpheed don't have the same
3353 * meaning, in IMAP it always removes the messages from the FolderItem
3354 * in Sylpheed it can mean to move the message to trash
3357 typedef struct _get_flags_data {
3360 MsgInfoList *msginfo_list;
3361 GRelation *msgflags;
3365 static /*gint*/ void *imap_get_flags_thread(void *data)
3367 get_flags_data *stuff = (get_flags_data *)data;
3368 Folder *folder = stuff->folder;
3369 FolderItem *item = stuff->item;
3370 MsgInfoList *msginfo_list = stuff->msginfo_list;
3371 GRelation *msgflags = stuff->msgflags;
3372 IMAPSession *session;
3373 GSList *sorted_list;
3374 GSList *unseen = NULL, *answered = NULL, *flagged = NULL, *deleted = NULL;
3375 GSList *p_unseen, *p_answered, *p_flagged, *p_deleted;
3377 GSList *seq_list, *cur;
3378 gboolean reverse_seen = FALSE;
3381 gint exists_cnt, recent_cnt, unseen_cnt, uid_next;
3382 guint32 uidvalidity;
3383 gboolean selected_folder;
3385 if (folder == NULL || item == NULL) {
3387 return GINT_TO_POINTER(-1);
3389 if (msginfo_list == NULL) {
3391 return GINT_TO_POINTER(0);
3394 session = imap_session_get(folder);
3395 if (session == NULL) {
3397 return GINT_TO_POINTER(-1);
3400 selected_folder = (session->mbox != NULL) &&
3401 (!strcmp(session->mbox, item->path));
3403 if (!selected_folder) {
3404 ok = imap_status(session, IMAP_FOLDER(folder), item->path,
3405 &exists_cnt, &recent_cnt, &uid_next, &uidvalidity, &unseen_cnt, TRUE);
3406 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3407 NULL, NULL, NULL, NULL, TRUE);
3408 if (ok != IMAP_SUCCESS) {
3410 return GINT_TO_POINTER(-1);
3413 if (unseen_cnt > exists_cnt / 2)
3414 reverse_seen = TRUE;
3417 if (item->unread_msgs > item->total_msgs / 2)
3418 reverse_seen = TRUE;
3421 cmd_buf = g_string_new(NULL);
3423 sorted_list = g_slist_sort(g_slist_copy(msginfo_list), compare_msginfo);
3425 seq_list = imap_get_lep_set_from_msglist(msginfo_list);
3427 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
3428 struct mailimap_set * imapset;
3429 clist * lep_uidlist;
3432 imapset = cur->data;
3434 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SEEN,
3435 imapset, &lep_uidlist);
3438 r = imap_threaded_search(folder,
3439 IMAP_SEARCH_TYPE_UNSEEN,
3440 imapset, &lep_uidlist);
3442 if (r == MAILIMAP_NO_ERROR) {
3445 uidlist = imap_uid_list_from_lep(lep_uidlist);
3446 mailimap_search_result_free(lep_uidlist);
3448 unseen = g_slist_concat(unseen, uidlist);
3451 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_ANSWERED,
3452 imapset, &lep_uidlist);
3453 if (r == MAILIMAP_NO_ERROR) {
3456 uidlist = imap_uid_list_from_lep(lep_uidlist);
3457 mailimap_search_result_free(lep_uidlist);
3459 answered = g_slist_concat(answered, uidlist);
3462 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_FLAGGED,
3463 imapset, &lep_uidlist);
3464 if (r == MAILIMAP_NO_ERROR) {
3467 uidlist = imap_uid_list_from_lep(lep_uidlist);
3468 mailimap_search_result_free(lep_uidlist);