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 (fileinfo->flags) {
1007 if ((MSG_IS_QUEUED(*fileinfo->flags)
1008 || MSG_IS_DRAFT(*fileinfo->flags))
1009 && !folder_has_parent_of_type(dest, F_QUEUE)
1010 && !folder_has_parent_of_type(dest, F_DRAFT)) {
1011 real_file = get_tmp_file();
1013 if (procmsg_remove_special_headers(
1022 if (real_file == NULL)
1023 real_file = g_strdup(fileinfo->file);
1025 if (folder_has_parent_of_type(dest, F_QUEUE) ||
1026 folder_has_parent_of_type(dest, F_OUTBOX) ||
1027 folder_has_parent_of_type(dest, F_DRAFT) ||
1028 folder_has_parent_of_type(dest, F_TRASH))
1029 iflags |= IMAP_FLAG_SEEN;
1031 ok = imap_cmd_append(session, destdir, real_file, iflags,
1034 if (ok != IMAP_SUCCESS) {
1035 g_warning("can't append message %s\n", real_file);
1037 g_unlink(real_file);
1043 if (relation != NULL)
1044 g_relation_insert(relation, fileinfo->msginfo != NULL ?
1045 (gpointer) fileinfo->msginfo : (gpointer) fileinfo,
1046 GINT_TO_POINTER(dest->last_num + 1));
1047 if (last_uid < new_uid)
1050 g_unlink(real_file);
1058 static gint imap_do_copy_msgs(Folder *folder, FolderItem *dest,
1059 MsgInfoList *msglist, GRelation *relation)
1063 GSList *seq_list, *cur;
1065 IMAPSession *session;
1066 gint ok = IMAP_SUCCESS;
1067 GRelation *uid_mapping;
1070 g_return_val_if_fail(folder != NULL, -1);
1071 g_return_val_if_fail(dest != NULL, -1);
1072 g_return_val_if_fail(msglist != NULL, -1);
1074 session = imap_session_get(folder);
1079 msginfo = (MsgInfo *)msglist->data;
1081 src = msginfo->folder;
1083 g_warning("the src folder is identical to the dest.\n");
1087 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
1088 NULL, NULL, NULL, NULL, FALSE);
1089 if (ok != IMAP_SUCCESS) {
1093 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
1094 seq_list = imap_get_lep_set_from_msglist(msglist);
1095 uid_mapping = g_relation_new(2);
1096 g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
1098 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
1099 struct mailimap_set * seq_set;
1101 seq_set = cur->data;
1103 debug_print("Copying messages from %s to %s ...\n",
1104 src->path, destdir);
1106 ok = imap_cmd_copy(session, seq_set, destdir, uid_mapping);
1107 if (ok != IMAP_SUCCESS) {
1108 g_relation_destroy(uid_mapping);
1109 imap_lep_set_free(seq_list);
1114 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
1115 MsgInfo *msginfo = (MsgInfo *)cur->data;
1118 tuples = g_relation_select(uid_mapping,
1119 GINT_TO_POINTER(msginfo->msgnum),
1121 if (tuples->len > 0) {
1122 gint num = GPOINTER_TO_INT(g_tuples_index(tuples, 0, 1));
1123 g_relation_insert(relation, msginfo,
1124 GPOINTER_TO_INT(num));
1128 g_relation_insert(relation, msginfo,
1129 GPOINTER_TO_INT(0));
1130 g_tuples_destroy(tuples);
1133 g_relation_destroy(uid_mapping);
1134 imap_lep_set_free(seq_list);
1138 IMAP_FOLDER_ITEM(dest)->lastuid = 0;
1139 IMAP_FOLDER_ITEM(dest)->uid_next = 0;
1140 g_slist_free(IMAP_FOLDER_ITEM(dest)->uid_list);
1141 IMAP_FOLDER_ITEM(dest)->uid_list = NULL;
1143 if (ok == IMAP_SUCCESS)
1149 static gint imap_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1153 g_return_val_if_fail(msginfo != NULL, -1);
1155 msglist.data = msginfo;
1156 msglist.next = NULL;
1158 return imap_copy_msgs(folder, dest, &msglist, NULL);
1161 static gint imap_copy_msgs(Folder *folder, FolderItem *dest,
1162 MsgInfoList *msglist, GRelation *relation)
1168 g_return_val_if_fail(folder != NULL, -1);
1169 g_return_val_if_fail(dest != NULL, -1);
1170 g_return_val_if_fail(msglist != NULL, -1);
1172 msginfo = (MsgInfo *)msglist->data;
1173 g_return_val_if_fail(msginfo->folder != NULL, -1);
1175 if (folder == msginfo->folder->folder &&
1176 !folder_has_parent_of_type(msginfo->folder, F_DRAFT) &&
1177 !folder_has_parent_of_type(msginfo->folder, F_QUEUE)) {
1178 ret = imap_do_copy_msgs(folder, dest, msglist, relation);
1182 file_list = procmsg_get_message_file_list(msglist);
1183 g_return_val_if_fail(file_list != NULL, -1);
1185 ret = imap_add_msgs(folder, dest, file_list, relation);
1187 procmsg_message_file_list_free(file_list);
1193 static gint imap_do_remove_msgs(Folder *folder, FolderItem *dest,
1194 MsgInfoList *msglist, GRelation *relation)
1197 GSList *seq_list = NULL, *cur;
1199 IMAPSession *session;
1200 gint ok = IMAP_SUCCESS;
1201 GRelation *uid_mapping;
1203 g_return_val_if_fail(folder != NULL, -1);
1204 g_return_val_if_fail(dest != NULL, -1);
1205 g_return_val_if_fail(msglist != NULL, -1);
1207 session = imap_session_get(folder);
1211 msginfo = (MsgInfo *)msglist->data;
1213 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
1214 NULL, NULL, NULL, NULL, FALSE);
1215 if (ok != IMAP_SUCCESS) {
1219 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
1220 for (cur = msglist; cur; cur = cur->next) {
1221 msginfo = (MsgInfo *)cur->data;
1222 seq_list = g_slist_append(seq_list, GINT_TO_POINTER(msginfo->msgnum));
1225 uid_mapping = g_relation_new(2);
1226 g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
1228 ok = imap_set_message_flags
1229 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
1230 seq_list, IMAP_FLAG_DELETED, TRUE);
1231 if (ok != IMAP_SUCCESS) {
1232 log_warning(_("can't set deleted flags\n"));
1235 ok = imap_cmd_expunge(session);
1236 if (ok != IMAP_SUCCESS) {
1237 log_warning(_("can't expunge\n"));
1241 g_relation_destroy(uid_mapping);
1242 g_slist_free(seq_list);
1246 if (ok == IMAP_SUCCESS)
1252 static gint imap_remove_msgs(Folder *folder, FolderItem *dest,
1253 MsgInfoList *msglist, GRelation *relation)
1257 g_return_val_if_fail(folder != NULL, -1);
1258 g_return_val_if_fail(dest != NULL, -1);
1259 if (msglist == NULL)
1262 msginfo = (MsgInfo *)msglist->data;
1263 g_return_val_if_fail(msginfo->folder != NULL, -1);
1265 return imap_do_remove_msgs(folder, dest, msglist, relation);
1268 static gint imap_remove_all_msg(Folder *folder, FolderItem *item)
1270 GSList *list = folder_item_get_msg_list(item);
1271 gint res = imap_remove_msgs(folder, item, list, NULL);
1272 procmsg_msg_list_free(list);
1276 static gboolean imap_is_msg_changed(Folder *folder, FolderItem *item,
1279 /* TODO: properly implement this method */
1283 static gint imap_close(Folder *folder, FolderItem *item)
1288 static gint imap_scan_tree(Folder *folder)
1290 FolderItem *item = NULL;
1291 IMAPSession *session;
1292 gchar *root_folder = NULL;
1294 g_return_val_if_fail(folder != NULL, -1);
1295 g_return_val_if_fail(folder->account != NULL, -1);
1297 session = imap_session_get(folder);
1299 if (!folder->node) {
1300 folder_tree_destroy(folder);
1301 item = folder_item_new(folder, folder->name, NULL);
1302 item->folder = folder;
1303 folder->node = item->node = g_node_new(item);
1308 if (folder->account->imap_dir && *folder->account->imap_dir) {
1313 Xstrdup_a(root_folder, folder->account->imap_dir, {return -1;});
1314 extract_quote(root_folder, '"');
1315 subst_char(root_folder,
1316 imap_get_path_separator(IMAP_FOLDER(folder),
1319 strtailchomp(root_folder, '/');
1320 real_path = imap_get_real_path
1321 (IMAP_FOLDER(folder), root_folder);
1322 debug_print("IMAP root directory: %s\n", real_path);
1324 /* check if root directory exist */
1326 r = imap_threaded_list(session->folder, "", real_path,
1328 if ((r != MAILIMAP_NO_ERROR) || (clist_count(lep_list) == 0)) {
1329 if (!folder->node) {
1330 item = folder_item_new(folder, folder->name, NULL);
1331 item->folder = folder;
1332 folder->node = item->node = g_node_new(item);
1337 mailimap_list_result_free(lep_list);
1343 item = FOLDER_ITEM(folder->node->data);
1344 if (!item || ((item->path || root_folder) &&
1345 strcmp2(item->path, root_folder) != 0)) {
1346 folder_tree_destroy(folder);
1347 item = folder_item_new(folder, folder->name, root_folder);
1348 item->folder = folder;
1349 folder->node = item->node = g_node_new(item);
1352 imap_scan_tree_recursive(session, FOLDER_ITEM(folder->node->data));
1353 imap_create_missing_folders(folder);
1358 static gint imap_scan_tree_recursive(IMAPSession *session, FolderItem *item)
1361 IMAPFolder *imapfolder;
1362 FolderItem *new_item;
1363 GSList *item_list, *cur;
1366 gchar *wildcard_path;
1372 g_return_val_if_fail(item != NULL, -1);
1373 g_return_val_if_fail(item->folder != NULL, -1);
1374 g_return_val_if_fail(item->no_sub == FALSE, -1);
1376 folder = item->folder;
1377 imapfolder = IMAP_FOLDER(folder);
1379 separator = imap_get_path_separator(imapfolder, item->path);
1381 if (folder->ui_func)
1382 folder->ui_func(folder, item, folder->ui_func_data);
1385 wildcard[0] = separator;
1388 real_path = imap_get_real_path(imapfolder, item->path);
1392 real_path = g_strdup("");
1395 Xstrcat_a(wildcard_path, real_path, wildcard,
1396 {g_free(real_path); return IMAP_ERROR;});
1398 r = imap_threaded_list(folder, "", wildcard_path, &lep_list);
1399 if (r != MAILIMAP_NO_ERROR) {
1403 item_list = imap_list_from_lep(imapfolder,
1404 lep_list, real_path);
1405 mailimap_list_result_free(lep_list);
1410 node = item->node->children;
1411 while (node != NULL) {
1412 FolderItem *old_item = FOLDER_ITEM(node->data);
1413 GNode *next = node->next;
1416 for (cur = item_list; cur != NULL; cur = cur->next) {
1417 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1418 if (!strcmp2(old_item->path, cur_item->path)) {
1419 new_item = cur_item;
1424 debug_print("folder '%s' not found. removing...\n",
1426 folder_item_remove(old_item);
1428 old_item->no_sub = new_item->no_sub;
1429 old_item->no_select = new_item->no_select;
1430 if (old_item->no_sub == TRUE && node->children) {
1431 debug_print("folder '%s' doesn't have "
1432 "subfolders. removing...\n",
1434 folder_item_remove_children(old_item);
1441 for (cur = item_list; cur != NULL; cur = cur->next) {
1442 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1444 for (node = item->node->children; node != NULL;
1445 node = node->next) {
1446 if (!strcmp2(FOLDER_ITEM(node->data)->path,
1448 new_item = FOLDER_ITEM(node->data);
1449 folder_item_destroy(cur_item);
1455 new_item = cur_item;
1456 debug_print("new folder '%s' found.\n", new_item->path);
1457 folder_item_append(item, new_item);
1460 if (!strcmp(new_item->path, "INBOX")) {
1461 new_item->stype = F_INBOX;
1462 folder->inbox = new_item;
1463 } else if (!folder_item_parent(item) || item->stype == F_INBOX) {
1466 base = g_path_get_basename(new_item->path);
1468 if (!folder->outbox && !g_ascii_strcasecmp(base, "Sent")) {
1469 new_item->stype = F_OUTBOX;
1470 folder->outbox = new_item;
1471 } else if (!folder->draft && !g_ascii_strcasecmp(base, "Drafts")) {
1472 new_item->stype = F_DRAFT;
1473 folder->draft = new_item;
1474 } else if (!folder->queue && !g_ascii_strcasecmp(base, "Queue")) {
1475 new_item->stype = F_QUEUE;
1476 folder->queue = new_item;
1477 } else if (!folder->trash && !g_ascii_strcasecmp(base, "Trash")) {
1478 new_item->stype = F_TRASH;
1479 folder->trash = new_item;
1484 if (new_item->no_sub == FALSE)
1485 imap_scan_tree_recursive(session, new_item);
1488 g_slist_free(item_list);
1490 return IMAP_SUCCESS;
1493 static gint imap_create_tree(Folder *folder)
1495 g_return_val_if_fail(folder != NULL, -1);
1496 g_return_val_if_fail(folder->node != NULL, -1);
1497 g_return_val_if_fail(folder->node->data != NULL, -1);
1498 g_return_val_if_fail(folder->account != NULL, -1);
1500 imap_scan_tree(folder);
1501 imap_create_missing_folders(folder);
1506 static void imap_create_missing_folders(Folder *folder)
1508 g_return_if_fail(folder != NULL);
1511 folder->inbox = imap_create_special_folder
1512 (folder, F_INBOX, "INBOX");
1514 folder->trash = imap_create_special_folder
1515 (folder, F_TRASH, "Trash");
1517 folder->queue = imap_create_special_folder
1518 (folder, F_QUEUE, "Queue");
1519 if (!folder->outbox)
1520 folder->outbox = imap_create_special_folder
1521 (folder, F_OUTBOX, "Sent");
1523 folder->draft = imap_create_special_folder
1524 (folder, F_DRAFT, "Drafts");
1527 static FolderItem *imap_create_special_folder(Folder *folder,
1528 SpecialFolderItemType stype,
1532 FolderItem *new_item;
1534 g_return_val_if_fail(folder != NULL, NULL);
1535 g_return_val_if_fail(folder->node != NULL, NULL);
1536 g_return_val_if_fail(folder->node->data != NULL, NULL);
1537 g_return_val_if_fail(folder->account != NULL, NULL);
1538 g_return_val_if_fail(name != NULL, NULL);
1540 item = FOLDER_ITEM(folder->node->data);
1541 new_item = imap_create_folder(folder, item, name);
1544 g_warning("Can't create '%s'\n", name);
1545 if (!folder->inbox) return NULL;
1547 new_item = imap_create_folder(folder, folder->inbox, name);
1549 g_warning("Can't create '%s' under INBOX\n", name);
1551 new_item->stype = stype;
1553 new_item->stype = stype;
1558 static gchar *imap_folder_get_path(Folder *folder)
1562 g_return_val_if_fail(folder != NULL, NULL);
1563 g_return_val_if_fail(folder->account != NULL, NULL);
1565 folder_path = g_strconcat(get_imap_cache_dir(),
1567 folder->account->recv_server,
1569 folder->account->userid,
1575 static gchar *imap_item_get_path(Folder *folder, FolderItem *item)
1577 gchar *folder_path, *path;
1579 g_return_val_if_fail(folder != NULL, NULL);
1580 g_return_val_if_fail(item != NULL, NULL);
1581 folder_path = imap_folder_get_path(folder);
1583 g_return_val_if_fail(folder_path != NULL, NULL);
1584 if (folder_path[0] == G_DIR_SEPARATOR) {
1586 path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
1589 path = g_strdup(folder_path);
1592 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1593 folder_path, G_DIR_SEPARATOR_S,
1596 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1599 g_free(folder_path);
1604 static FolderItem *imap_create_folder(Folder *folder, FolderItem *parent,
1607 gchar *dirpath, *imap_path;
1608 IMAPSession *session;
1609 FolderItem *new_item;
1615 g_return_val_if_fail(folder != NULL, NULL);
1616 g_return_val_if_fail(folder->account != NULL, NULL);
1617 g_return_val_if_fail(parent != NULL, NULL);
1618 g_return_val_if_fail(name != NULL, NULL);
1620 session = imap_session_get(folder);
1625 if (!folder_item_parent(parent) && strcmp(name, "INBOX") == 0)
1626 dirpath = g_strdup(name);
1627 else if (parent->path)
1628 dirpath = g_strconcat(parent->path, "/", name, NULL);
1629 else if ((p = strchr(name, '/')) != NULL && *(p + 1) != '\0')
1630 dirpath = g_strdup(name);
1631 else if (folder->account->imap_dir && *folder->account->imap_dir) {
1634 Xstrdup_a(imap_dir, folder->account->imap_dir, {return NULL;});
1635 strtailchomp(imap_dir, '/');
1636 dirpath = g_strconcat(imap_dir, "/", name, NULL);
1638 dirpath = g_strdup(name);
1640 /* keep trailing directory separator to create a folder that contains
1642 imap_path = imap_utf8_to_modified_utf7(dirpath);
1643 strtailchomp(dirpath, '/');
1644 Xstrdup_a(new_name, name, {
1648 strtailchomp(new_name, '/');
1649 separator = imap_get_path_separator(IMAP_FOLDER(folder), imap_path);
1650 imap_path_separator_subst(imap_path, separator);
1651 subst_char(new_name, '/', separator);
1653 if (strcmp(name, "INBOX") != 0) {
1655 gboolean exist = FALSE;
1659 argbuf = g_ptr_array_new();
1660 r = imap_threaded_list(folder, "", imap_path, &lep_list);
1661 if (r != MAILIMAP_NO_ERROR) {
1662 log_warning(_("can't create mailbox: LIST failed\n"));
1665 ptr_array_free_strings(argbuf);
1666 g_ptr_array_free(argbuf, TRUE);
1670 if (clist_count(lep_list) > 0)
1674 ok = imap_cmd_create(session, imap_path);
1675 if (ok != IMAP_SUCCESS) {
1676 log_warning(_("can't create mailbox\n"));
1684 new_item = folder_item_new(folder, new_name, dirpath);
1685 folder_item_append(parent, new_item);
1689 dirpath = folder_item_get_path(new_item);
1690 if (!is_dir_exist(dirpath))
1691 make_dir_hier(dirpath);
1697 static gint imap_rename_folder(Folder *folder, FolderItem *item,
1702 gchar *real_oldpath;
1703 gchar *real_newpath;
1705 gchar *old_cache_dir;
1706 gchar *new_cache_dir;
1707 IMAPSession *session;
1710 gint exists, recent, unseen;
1711 guint32 uid_validity;
1713 g_return_val_if_fail(folder != NULL, -1);
1714 g_return_val_if_fail(item != NULL, -1);
1715 g_return_val_if_fail(item->path != NULL, -1);
1716 g_return_val_if_fail(name != NULL, -1);
1718 if (strchr(name, imap_get_path_separator(IMAP_FOLDER(folder), item->path)) != NULL) {
1719 g_warning(_("New folder name must not contain the namespace "
1724 session = imap_session_get(folder);
1728 real_oldpath = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1730 g_free(session->mbox);
1731 session->mbox = NULL;
1732 ok = imap_cmd_examine(session, "INBOX",
1733 &exists, &recent, &unseen, &uid_validity, FALSE);
1734 if (ok != IMAP_SUCCESS) {
1735 g_free(real_oldpath);
1739 separator = imap_get_path_separator(IMAP_FOLDER(folder), item->path);
1740 if (strchr(item->path, G_DIR_SEPARATOR)) {
1741 dirpath = g_path_get_dirname(item->path);
1742 newpath = g_strconcat(dirpath, G_DIR_SEPARATOR_S, name, NULL);
1745 newpath = g_strdup(name);
1747 real_newpath = imap_utf8_to_modified_utf7(newpath);
1748 imap_path_separator_subst(real_newpath, separator);
1750 ok = imap_cmd_rename(session, real_oldpath, real_newpath);
1751 if (ok != IMAP_SUCCESS) {
1752 log_warning(_("can't rename mailbox: %s to %s\n"),
1753 real_oldpath, real_newpath);
1754 g_free(real_oldpath);
1756 g_free(real_newpath);
1761 item->name = g_strdup(name);
1763 old_cache_dir = folder_item_get_path(item);
1765 paths[0] = g_strdup(item->path);
1767 g_node_traverse(item->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1768 imap_rename_folder_func, paths);
1770 if (is_dir_exist(old_cache_dir)) {
1771 new_cache_dir = folder_item_get_path(item);
1772 if (rename(old_cache_dir, new_cache_dir) < 0) {
1773 FILE_OP_ERROR(old_cache_dir, "rename");
1775 g_free(new_cache_dir);
1778 g_free(old_cache_dir);
1781 g_free(real_oldpath);
1782 g_free(real_newpath);
1787 static gint imap_remove_folder_real(Folder *folder, FolderItem *item)
1790 IMAPSession *session;
1793 gint exists, recent, unseen;
1794 guint32 uid_validity;
1796 g_return_val_if_fail(folder != NULL, -1);
1797 g_return_val_if_fail(item != NULL, -1);
1798 g_return_val_if_fail(item->path != NULL, -1);
1800 session = imap_session_get(folder);
1804 path = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1806 ok = imap_cmd_examine(session, "INBOX",
1807 &exists, &recent, &unseen, &uid_validity, FALSE);
1808 if (ok != IMAP_SUCCESS) {
1813 ok = imap_cmd_delete(session, path);
1814 if (ok != IMAP_SUCCESS) {
1815 log_warning(_("can't delete mailbox\n"));
1821 cache_dir = folder_item_get_path(item);
1822 if (is_dir_exist(cache_dir) && remove_dir_recursive(cache_dir) < 0)
1823 g_warning("can't remove directory '%s'\n", cache_dir);
1825 folder_item_remove(item);
1830 static gint imap_remove_folder(Folder *folder, FolderItem *item)
1834 g_return_val_if_fail(item != NULL, -1);
1835 g_return_val_if_fail(item->folder != NULL, -1);
1836 g_return_val_if_fail(item->node != NULL, -1);
1838 node = item->node->children;
1839 while (node != NULL) {
1841 if (imap_remove_folder(folder, FOLDER_ITEM(node->data)) < 0)
1845 debug_print("IMAP removing %s\n", item->path);
1847 if (imap_remove_all_msg(folder, item) < 0)
1849 return imap_remove_folder_real(folder, item);
1852 typedef struct _uncached_data {
1853 IMAPSession *session;
1855 MsgNumberList *numlist;
1861 static void *imap_get_uncached_messages_thread(void *data)
1863 uncached_data *stuff = (uncached_data *)data;
1864 IMAPSession *session = stuff->session;
1865 FolderItem *item = stuff->item;
1866 MsgNumberList *numlist = stuff->numlist;
1868 GSList *newlist = NULL;
1869 GSList *llast = NULL;
1870 GSList *seq_list, *cur;
1872 debug_print("uncached_messages\n");
1874 if (session == NULL || item == NULL || item->folder == NULL
1875 || FOLDER_CLASS(item->folder) != &imap_class) {
1880 seq_list = imap_get_lep_set_from_numlist(numlist);
1881 debug_print("get msgs info\n");
1882 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
1883 struct mailimap_set * imapset;
1889 imapset = cur->data;
1891 r = imap_threaded_fetch_env(session->folder,
1892 imapset, &env_list);
1893 if (r != MAILIMAP_NO_ERROR)
1897 for(i = 0 ; i < carray_count(env_list) ; i ++) {
1898 struct imap_fetch_env_info * info;
1901 info = carray_get(env_list, i);
1902 msginfo = imap_envelope_from_lep(info, item);
1903 msginfo->folder = item;
1905 llast = newlist = g_slist_append(newlist, msginfo);
1907 llast = g_slist_append(llast, msginfo);
1908 llast = llast->next;
1913 imap_fetch_env_free(env_list);
1916 session_set_access_time(SESSION(session));
1921 #define MAX_MSG_NUM 50
1923 static GSList *imap_get_uncached_messages(IMAPSession *session,
1925 MsgNumberList *numlist)
1927 GSList *result = NULL;
1929 uncached_data *data = g_new0(uncached_data, 1);
1934 data->total = g_slist_length(numlist);
1935 debug_print("messages list : %i\n", data->total);
1937 while (cur != NULL) {
1938 GSList * partial_result;
1946 while (count < MAX_MSG_NUM) {
1951 if (newlist == NULL)
1952 llast = newlist = g_slist_append(newlist, p);
1954 llast = g_slist_append(llast, p);
1955 llast = llast->next;
1965 data->session = session;
1967 data->numlist = newlist;
1970 if (prefs_common.work_offline && !imap_gtk_should_override()) {
1976 (GSList *)imap_get_uncached_messages_thread(data);
1978 statusbar_progress_all(data->cur,data->total, 1);
1980 g_slist_free(newlist);
1982 result = g_slist_concat(result, partial_result);
1986 statusbar_progress_all(0,0,0);
1987 statusbar_pop_all();
1992 static void imap_delete_all_cached_messages(FolderItem *item)
1996 g_return_if_fail(item != NULL);
1997 g_return_if_fail(item->folder != NULL);
1998 g_return_if_fail(FOLDER_CLASS(item->folder) == &imap_class);
2000 debug_print("Deleting all cached messages...\n");
2002 dir = folder_item_get_path(item);
2003 if (is_dir_exist(dir))
2004 remove_all_numbered_files(dir);
2007 debug_print("done.\n");
2010 static IMAPNameSpace *imap_find_namespace_from_list(GList *ns_list,
2013 IMAPNameSpace *namespace = NULL;
2014 gchar *tmp_path, *name;
2016 if (!path) path = "";
2018 for (; ns_list != NULL; ns_list = ns_list->next) {
2019 IMAPNameSpace *tmp_ns = ns_list->data;
2021 Xstrcat_a(tmp_path, path, "/", return namespace);
2022 Xstrdup_a(name, tmp_ns->name, return namespace);
2023 if (tmp_ns->separator && tmp_ns->separator != '/') {
2024 subst_char(tmp_path, tmp_ns->separator, '/');
2025 subst_char(name, tmp_ns->separator, '/');
2027 if (strncmp(tmp_path, name, strlen(name)) == 0)
2034 static IMAPNameSpace *imap_find_namespace(IMAPFolder *folder,
2037 IMAPNameSpace *namespace;
2039 g_return_val_if_fail(folder != NULL, NULL);
2041 namespace = imap_find_namespace_from_list(folder->ns_personal, path);
2042 if (namespace) return namespace;
2043 namespace = imap_find_namespace_from_list(folder->ns_others, path);
2044 if (namespace) return namespace;
2045 namespace = imap_find_namespace_from_list(folder->ns_shared, path);
2046 if (namespace) return namespace;
2052 static gchar imap_get_path_separator(IMAPFolder *folder, const gchar *path)
2054 IMAPNameSpace *namespace;
2055 gchar separator = '/';
2057 if (folder->last_seen_separator == 0) {
2059 int r = imap_threaded_list((Folder *)folder, "", "", &lep_list);
2060 if (r != MAILIMAP_NO_ERROR) {
2061 log_warning(_("LIST failed\n"));
2065 if (clist_count(lep_list) > 0) {
2066 clistiter * iter = clist_begin(lep_list);
2067 struct mailimap_mailbox_list * mb;
2068 mb = clist_content(iter);
2070 folder->last_seen_separator = mb->mb_delimiter;
2071 debug_print("got separator: %c\n", folder->last_seen_separator);
2073 mailimap_list_result_free(lep_list);
2076 if (folder->last_seen_separator != 0) {
2077 debug_print("using separator: %c\n", folder->last_seen_separator);
2078 return folder->last_seen_separator;
2081 namespace = imap_find_namespace(folder, path);
2082 if (namespace && namespace->separator)
2083 separator = namespace->separator;
2088 static gchar *imap_get_real_path(IMAPFolder *folder, const gchar *path)
2093 g_return_val_if_fail(folder != NULL, NULL);
2094 g_return_val_if_fail(path != NULL, NULL);
2096 real_path = imap_utf8_to_modified_utf7(path);
2097 separator = imap_get_path_separator(folder, path);
2098 imap_path_separator_subst(real_path, separator);
2103 static gint imap_set_message_flags(IMAPSession *session,
2104 MsgNumberList *numlist,
2112 seq_list = imap_get_lep_set_from_numlist(numlist);
2114 for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) {
2115 struct mailimap_set * imapset;
2117 imapset = cur->data;
2119 ok = imap_cmd_store(session, imapset,
2123 imap_lep_set_free(seq_list);
2125 return IMAP_SUCCESS;
2128 typedef struct _select_data {
2129 IMAPSession *session;
2134 guint32 *uid_validity;
2138 static gint imap_select(IMAPSession *session, IMAPFolder *folder,
2140 gint *exists, gint *recent, gint *unseen,
2141 guint32 *uid_validity, gboolean block)
2145 gint exists_, recent_, unseen_;
2146 guint32 uid_validity_;
2148 if (!exists || !recent || !unseen || !uid_validity) {
2149 if (session->mbox && strcmp(session->mbox, path) == 0)
2150 return IMAP_SUCCESS;
2154 uid_validity = &uid_validity_;
2157 g_free(session->mbox);
2158 session->mbox = NULL;
2160 real_path = imap_get_real_path(folder, path);
2162 ok = imap_cmd_select(session, real_path,
2163 exists, recent, unseen, uid_validity, block);
2164 if (ok != IMAP_SUCCESS)
2165 log_warning(_("can't select folder: %s\n"), real_path);
2167 session->mbox = g_strdup(path);
2168 session->folder_content_changed = FALSE;
2175 static gint imap_status(IMAPSession *session, IMAPFolder *folder,
2177 gint *messages, gint *recent,
2178 guint32 *uid_next, guint32 *uid_validity,
2179 gint *unseen, gboolean block)
2183 struct mailimap_mailbox_data_status * data_status;
2187 real_path = imap_get_real_path(folder, path);
2189 r = imap_threaded_status(FOLDER(folder), real_path, &data_status);
2191 if (r != MAILIMAP_NO_ERROR) {
2192 debug_print("status err %d\n", r);
2196 if (data_status->st_info_list == NULL) {
2197 mailimap_mailbox_data_status_free(data_status);
2198 debug_print("status->st_info_list == NULL\n");
2203 for(iter = clist_begin(data_status->st_info_list) ; iter != NULL ;
2204 iter = clist_next(iter)) {
2205 struct mailimap_status_info * info;
2207 info = clist_content(iter);
2208 switch (info->st_att) {
2209 case MAILIMAP_STATUS_ATT_MESSAGES:
2210 * messages = info->st_value;
2211 got_values |= 1 << 0;
2214 case MAILIMAP_STATUS_ATT_RECENT:
2215 * recent = info->st_value;
2216 got_values |= 1 << 1;
2219 case MAILIMAP_STATUS_ATT_UIDNEXT:
2220 * uid_next = info->st_value;
2221 got_values |= 1 << 2;
2224 case MAILIMAP_STATUS_ATT_UIDVALIDITY:
2225 * uid_validity = info->st_value;
2226 got_values |= 1 << 3;
2229 case MAILIMAP_STATUS_ATT_UNSEEN:
2230 * unseen = info->st_value;
2231 got_values |= 1 << 4;
2235 mailimap_mailbox_data_status_free(data_status);
2237 if (got_values != ((1 << 4) + (1 << 3) +
2238 (1 << 2) + (1 << 1) + (1 << 0))) {
2239 debug_print("status: incomplete values received (%d)\n", got_values);
2242 return IMAP_SUCCESS;
2245 static void imap_free_capabilities(IMAPSession *session)
2247 slist_free_strings(session->capability);
2248 g_slist_free(session->capability);
2249 session->capability = NULL;
2252 /* low-level IMAP4rev1 commands */
2255 static gint imap_cmd_authenticate(IMAPSession *session, const gchar *user,
2256 const gchar *pass, IMAPAuthType type)
2263 gchar hexdigest[33];
2267 auth_type = "CRAM-MD5";
2269 imap_gen_send(session, "AUTHENTICATE %s", auth_type);
2270 ok = imap_gen_recv(session, &buf);
2271 if (ok != IMAP_SUCCESS || buf[0] != '+' || buf[1] != ' ') {
2276 challenge = g_malloc(strlen(buf + 2) + 1);
2277 challenge_len = base64_decode(challenge, buf + 2, -1);
2278 challenge[challenge_len] = '\0';
2281 md5_hex_hmac(hexdigest, challenge, challenge_len, pass, strlen(pass));
2284 response = g_strdup_printf("%s %s", user, hexdigest);
2285 response64 = g_malloc((strlen(response) + 3) * 2 + 1);
2286 base64_encode(response64, response, strlen(response));
2289 sock_puts(SESSION(session)->sock, response64);
2290 ok = imap_cmd_ok(session, NULL);
2291 if (ok != IMAP_SUCCESS)
2292 log_warning(_("IMAP4 authentication failed.\n"));
2298 static gint imap_cmd_login(IMAPSession *session,
2299 const gchar *user, const gchar *pass,
2305 log_print("IMAP4> Logging %s to %s using %s\n",
2307 SESSION(session)->server,
2309 r = imap_threaded_login(session->folder, user, pass, type);
2310 if (r != MAILIMAP_NO_ERROR) {
2311 log_error("IMAP4< Error logging in to %s\n",
2312 SESSION(session)->server);
2320 static gint imap_cmd_logout(IMAPSession *session)
2322 imap_threaded_disconnect(session->folder);
2324 return IMAP_SUCCESS;
2327 static gint imap_cmd_noop(IMAPSession *session)
2330 unsigned int exists;
2332 r = imap_threaded_noop(session->folder, &exists);
2333 if (r != MAILIMAP_NO_ERROR) {
2334 debug_print("noop err %d\n", r);
2337 session->exists = exists;
2338 session_set_access_time(SESSION(session));
2340 return IMAP_SUCCESS;
2344 static gint imap_cmd_starttls(IMAPSession *session)
2348 r = imap_threaded_starttls(session->folder);
2349 if (r != MAILIMAP_NO_ERROR) {
2350 debug_print("starttls err %d\n", r);
2353 return IMAP_SUCCESS;
2357 static gint imap_cmd_select(IMAPSession *session, const gchar *folder,
2358 gint *exists, gint *recent, gint *unseen,
2359 guint32 *uid_validity, gboolean block)
2363 r = imap_threaded_select(session->folder, folder,
2364 exists, recent, unseen, uid_validity);
2365 if (r != MAILIMAP_NO_ERROR) {
2366 debug_print("select err %d\n", r);
2369 return IMAP_SUCCESS;
2372 static gint imap_cmd_examine(IMAPSession *session, const gchar *folder,
2373 gint *exists, gint *recent, gint *unseen,
2374 guint32 *uid_validity, gboolean block)
2378 r = imap_threaded_examine(session->folder, folder,
2379 exists, recent, unseen, uid_validity);
2380 if (r != MAILIMAP_NO_ERROR) {
2381 debug_print("examine err %d\n", r);
2385 return IMAP_SUCCESS;
2388 static gint imap_cmd_create(IMAPSession *session, const gchar *folder)
2392 r = imap_threaded_create(session->folder, folder);
2393 if (r != MAILIMAP_NO_ERROR) {
2398 return IMAP_SUCCESS;
2401 static gint imap_cmd_rename(IMAPSession *session, const gchar *old_folder,
2402 const gchar *new_folder)
2406 r = imap_threaded_rename(session->folder, old_folder,
2408 if (r != MAILIMAP_NO_ERROR) {
2413 return IMAP_SUCCESS;
2416 static gint imap_cmd_delete(IMAPSession *session, const gchar *folder)
2421 r = imap_threaded_delete(session->folder, folder);
2422 if (r != MAILIMAP_NO_ERROR) {
2427 return IMAP_SUCCESS;
2430 typedef struct _fetch_data {
2431 IMAPSession *session;
2433 const gchar *filename;
2439 static void *imap_cmd_fetch_thread(void *data)
2441 fetch_data *stuff = (fetch_data *)data;
2442 IMAPSession *session = stuff->session;
2443 guint32 uid = stuff->uid;
2444 const gchar *filename = stuff->filename;
2448 r = imap_threaded_fetch_content(session->folder,
2452 r = imap_threaded_fetch_content(session->folder,
2455 if (r != MAILIMAP_NO_ERROR) {
2456 debug_print("fetch err %d\n", r);
2457 return GINT_TO_POINTER(IMAP_ERROR);
2459 return GINT_TO_POINTER(IMAP_SUCCESS);
2462 static gint imap_cmd_fetch(IMAPSession *session, guint32 uid,
2463 const gchar *filename, gboolean headers,
2466 fetch_data *data = g_new0(fetch_data, 1);
2469 data->session = session;
2471 data->filename = filename;
2472 data->headers = headers;
2475 if (prefs_common.work_offline && !imap_gtk_should_override()) {
2479 statusbar_print_all(_("Fetching message..."));
2480 result = GPOINTER_TO_INT(imap_cmd_fetch_thread(data));
2481 statusbar_pop_all();
2487 static gint imap_cmd_append(IMAPSession *session, const gchar *destfolder,
2488 const gchar *file, IMAPFlags flags,
2491 struct mailimap_flag_list * flag_list;
2494 g_return_val_if_fail(file != NULL, IMAP_ERROR);
2496 flag_list = imap_flag_to_lep(flags);
2497 statusbar_print_all(_("Adding messages..."));
2498 r = imap_threaded_append(session->folder, destfolder,
2500 statusbar_pop_all();
2501 if (new_uid != NULL)
2504 if (r != MAILIMAP_NO_ERROR) {
2505 debug_print("append err %d\n", r);
2508 return IMAP_SUCCESS;
2511 static gint imap_cmd_copy(IMAPSession *session, struct mailimap_set * set,
2512 const gchar *destfolder, GRelation *uid_mapping)
2516 g_return_val_if_fail(session != NULL, IMAP_ERROR);
2517 g_return_val_if_fail(set != NULL, IMAP_ERROR);
2518 g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
2520 statusbar_print_all(_("Copying messages..."));
2521 r = imap_threaded_copy(session->folder, set, destfolder);
2522 statusbar_pop_all();
2523 if (r != MAILIMAP_NO_ERROR) {
2528 return IMAP_SUCCESS;
2531 static gint imap_cmd_store(IMAPSession *session, struct mailimap_set * set,
2532 IMAPFlags flags, int do_add)
2535 struct mailimap_flag_list * flag_list;
2536 struct mailimap_store_att_flags * store_att_flags;
2538 flag_list = imap_flag_to_lep(flags);
2542 mailimap_store_att_flags_new_add_flags_silent(flag_list);
2545 mailimap_store_att_flags_new_remove_flags_silent(flag_list);
2547 r = imap_threaded_store(session->folder, set, store_att_flags);
2548 if (r != MAILIMAP_NO_ERROR) {
2553 return IMAP_SUCCESS;
2556 static gint imap_cmd_expunge(IMAPSession *session)
2560 if (prefs_common.work_offline && !imap_gtk_should_override()) {
2564 r = imap_threaded_expunge(session->folder);
2565 if (r != MAILIMAP_NO_ERROR) {
2570 return IMAP_SUCCESS;
2573 static void imap_path_separator_subst(gchar *str, gchar separator)
2576 gboolean in_escape = FALSE;
2578 if (!separator || separator == '/') return;
2580 for (p = str; *p != '\0'; p++) {
2581 if (*p == '/' && !in_escape)
2583 else if (*p == '&' && *(p + 1) != '-' && !in_escape)
2585 else if (*p == '-' && in_escape)
2590 static gchar *imap_modified_utf7_to_utf8(const gchar *mutf7_str)
2592 static iconv_t cd = (iconv_t)-1;
2593 static gboolean iconv_ok = TRUE;
2596 size_t norm_utf7_len;
2598 gchar *to_str, *to_p;
2600 gboolean in_escape = FALSE;
2602 if (!iconv_ok) return g_strdup(mutf7_str);
2604 if (cd == (iconv_t)-1) {
2605 cd = iconv_open(CS_INTERNAL, CS_UTF_7);
2606 if (cd == (iconv_t)-1) {
2607 g_warning("iconv cannot convert UTF-7 to %s\n",
2610 return g_strdup(mutf7_str);
2614 /* modified UTF-7 to normal UTF-7 conversion */
2615 norm_utf7 = g_string_new(NULL);
2617 for (p = mutf7_str; *p != '\0'; p++) {
2618 /* replace: '&' -> '+',
2620 escaped ',' -> '/' */
2621 if (!in_escape && *p == '&') {
2622 if (*(p + 1) != '-') {
2623 g_string_append_c(norm_utf7, '+');
2626 g_string_append_c(norm_utf7, '&');
2629 } else if (in_escape && *p == ',') {
2630 g_string_append_c(norm_utf7, '/');
2631 } else if (in_escape && *p == '-') {
2632 g_string_append_c(norm_utf7, '-');
2635 g_string_append_c(norm_utf7, *p);
2639 norm_utf7_p = norm_utf7->str;
2640 norm_utf7_len = norm_utf7->len;
2641 to_len = strlen(mutf7_str) * 5;
2642 to_p = to_str = g_malloc(to_len + 1);
2644 if (iconv(cd, (ICONV_CONST gchar **)&norm_utf7_p, &norm_utf7_len,
2645 &to_p, &to_len) == -1) {
2646 g_warning(_("iconv cannot convert UTF-7 to %s\n"),
2647 conv_get_locale_charset_str());
2648 g_string_free(norm_utf7, TRUE);
2650 return g_strdup(mutf7_str);
2653 /* second iconv() call for flushing */
2654 iconv(cd, NULL, NULL, &to_p, &to_len);
2655 g_string_free(norm_utf7, TRUE);
2661 static gchar *imap_utf8_to_modified_utf7(const gchar *from)
2663 static iconv_t cd = (iconv_t)-1;
2664 static gboolean iconv_ok = TRUE;
2665 gchar *norm_utf7, *norm_utf7_p;
2666 size_t from_len, norm_utf7_len;
2668 gchar *from_tmp, *to, *p;
2669 gboolean in_escape = FALSE;
2671 if (!iconv_ok) return g_strdup(from);
2673 if (cd == (iconv_t)-1) {
2674 cd = iconv_open(CS_UTF_7, CS_INTERNAL);
2675 if (cd == (iconv_t)-1) {
2676 g_warning(_("iconv cannot convert %s to UTF-7\n"),
2679 return g_strdup(from);
2683 /* UTF-8 to normal UTF-7 conversion */
2684 Xstrdup_a(from_tmp, from, return g_strdup(from));
2685 from_len = strlen(from);
2686 norm_utf7_len = from_len * 5;
2687 Xalloca(norm_utf7, norm_utf7_len + 1, return g_strdup(from));
2688 norm_utf7_p = norm_utf7;
2690 #define IS_PRINT(ch) (isprint(ch) && IS_ASCII(ch))
2692 while (from_len > 0) {
2693 if (*from_tmp == '+') {
2694 *norm_utf7_p++ = '+';
2695 *norm_utf7_p++ = '-';
2699 } else if (IS_PRINT(*(guchar *)from_tmp)) {
2700 /* printable ascii char */
2701 *norm_utf7_p = *from_tmp;
2707 size_t conv_len = 0;
2709 /* unprintable char: convert to UTF-7 */
2711 while (!IS_PRINT(*(guchar *)p) && conv_len < from_len) {
2712 conv_len += g_utf8_skip[*(guchar *)p];
2713 p += g_utf8_skip[*(guchar *)p];
2716 from_len -= conv_len;
2717 if (iconv(cd, (ICONV_CONST gchar **)&from_tmp,
2719 &norm_utf7_p, &norm_utf7_len) == -1) {
2720 g_warning(_("iconv cannot convert UTF-8 to UTF-7\n"));
2721 return g_strdup(from);
2724 /* second iconv() call for flushing */
2725 iconv(cd, NULL, NULL, &norm_utf7_p, &norm_utf7_len);
2731 *norm_utf7_p = '\0';
2732 to_str = g_string_new(NULL);
2733 for (p = norm_utf7; p < norm_utf7_p; p++) {
2734 /* replace: '&' -> "&-",
2737 BASE64 '/' -> ',' */
2738 if (!in_escape && *p == '&') {
2739 g_string_append(to_str, "&-");
2740 } else if (!in_escape && *p == '+') {
2741 if (*(p + 1) == '-') {
2742 g_string_append_c(to_str, '+');
2745 g_string_append_c(to_str, '&');
2748 } else if (in_escape && *p == '/') {
2749 g_string_append_c(to_str, ',');
2750 } else if (in_escape && *p == '-') {
2751 g_string_append_c(to_str, '-');
2754 g_string_append_c(to_str, *p);
2760 g_string_append_c(to_str, '-');
2764 g_string_free(to_str, FALSE);
2769 static gboolean imap_rename_folder_func(GNode *node, gpointer data)
2771 FolderItem *item = node->data;
2772 gchar **paths = data;
2773 const gchar *oldpath = paths[0];
2774 const gchar *newpath = paths[1];
2776 gchar *new_itempath;
2779 oldpathlen = strlen(oldpath);
2780 if (strncmp(oldpath, item->path, oldpathlen) != 0) {
2781 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
2785 base = item->path + oldpathlen;
2786 while (*base == G_DIR_SEPARATOR) base++;
2788 new_itempath = g_strdup(newpath);
2790 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
2793 item->path = new_itempath;
2798 typedef struct _get_list_uid_data {
2800 IMAPFolderItem *item;
2801 GSList **msgnum_list;
2803 } get_list_uid_data;
2805 static void *get_list_of_uids_thread(void *data)
2807 get_list_uid_data *stuff = (get_list_uid_data *)data;
2808 Folder *folder = stuff->folder;
2809 IMAPFolderItem *item = stuff->item;
2810 GSList **msgnum_list = stuff->msgnum_list;
2811 gint ok, nummsgs = 0, lastuid_old;
2812 IMAPSession *session;
2813 GSList *uidlist, *elem;
2814 struct mailimap_set * set;
2815 clist * lep_uidlist;
2818 session = imap_session_get(folder);
2819 if (session == NULL) {
2821 return GINT_TO_POINTER(-1);
2824 ok = imap_select(session, IMAP_FOLDER(folder), item->item.path,
2825 NULL, NULL, NULL, NULL, TRUE);
2826 if (ok != IMAP_SUCCESS) {
2828 return GINT_TO_POINTER(-1);
2833 set = mailimap_set_new_interval(item->lastuid + 1, 0);
2834 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SIMPLE, set,
2836 if (r == MAILIMAP_NO_ERROR) {
2837 GSList * fetchuid_list;
2840 imap_uid_list_from_lep(lep_uidlist);
2841 uidlist = g_slist_concat(fetchuid_list, uidlist);
2844 GSList * fetchuid_list;
2845 carray * lep_uidtab;
2847 r = imap_threaded_fetch_uid(folder, item->lastuid + 1,
2849 if (r == MAILIMAP_NO_ERROR) {
2851 imap_uid_list_from_lep_tab(lep_uidtab);
2852 uidlist = g_slist_concat(fetchuid_list, uidlist);
2856 lastuid_old = item->lastuid;
2857 *msgnum_list = g_slist_copy(item->uid_list);
2858 nummsgs = g_slist_length(*msgnum_list);
2859 debug_print("Got %d uids from cache\n", g_slist_length(item->uid_list));
2861 for (elem = uidlist; elem != NULL; elem = g_slist_next(elem)) {
2864 msgnum = GPOINTER_TO_INT(elem->data);
2865 if (msgnum > lastuid_old) {
2866 *msgnum_list = g_slist_prepend(*msgnum_list, GINT_TO_POINTER(msgnum));
2867 item->uid_list = g_slist_prepend(item->uid_list, GINT_TO_POINTER(msgnum));
2870 if(msgnum > item->lastuid)
2871 item->lastuid = msgnum;
2874 g_slist_free(uidlist);
2877 return GINT_TO_POINTER(nummsgs);
2880 static gint get_list_of_uids(Folder *folder, IMAPFolderItem *item, GSList **msgnum_list)
2883 get_list_uid_data *data = g_new0(get_list_uid_data, 1);
2885 data->folder = folder;
2887 data->msgnum_list = msgnum_list;
2889 if (prefs_common.work_offline && !imap_gtk_should_override()) {
2894 result = GPOINTER_TO_INT(get_list_of_uids_thread(data));
2900 gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list, gboolean *old_uids_valid)
2902 IMAPFolderItem *item = (IMAPFolderItem *)_item;
2903 IMAPSession *session;
2904 gint ok, nummsgs = 0, exists, recent, uid_val, uid_next, unseen;
2905 GSList *uidlist = NULL;
2907 gboolean selected_folder;
2909 debug_print("get_num_list\n");
2910 statusbar_print_all("Scanning %s...\n", FOLDER_ITEM(item)->path
2911 ? FOLDER_ITEM(item)->path:"");
2913 g_return_val_if_fail(folder != NULL, -1);
2914 g_return_val_if_fail(item != NULL, -1);
2915 g_return_val_if_fail(item->item.path != NULL, -1);
2916 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
2917 g_return_val_if_fail(folder->account != NULL, -1);
2919 session = imap_session_get(folder);
2920 g_return_val_if_fail(session != NULL, -1);
2922 selected_folder = (session->mbox != NULL) &&
2923 (!strcmp(session->mbox, item->item.path));
2924 if (selected_folder) {
2925 ok = imap_cmd_noop(session);
2926 if (ok != IMAP_SUCCESS) {
2927 debug_print("disconnected!\n");
2928 session = imap_reconnect_if_possible(folder, session);
2929 if (session == NULL) {
2930 statusbar_pop_all();
2934 exists = session->exists;
2936 *old_uids_valid = TRUE;
2938 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
2939 &exists, &recent, &uid_next, &uid_val, &unseen, FALSE);
2940 if (ok != IMAP_SUCCESS) {
2941 statusbar_pop_all();
2944 if(item->item.mtime == uid_val)
2945 *old_uids_valid = TRUE;
2947 *old_uids_valid = FALSE;
2949 debug_print("Freeing imap uid cache\n");
2951 g_slist_free(item->uid_list);
2952 item->uid_list = NULL;
2954 item->item.mtime = uid_val;
2956 imap_delete_all_cached_messages((FolderItem *)item);
2960 if (!selected_folder)
2961 item->uid_next = uid_next;
2963 /* If old uid_next matches new uid_next we can be sure no message
2964 was added to the folder */
2965 if (( selected_folder && !session->folder_content_changed) ||
2966 (!selected_folder && uid_next == item->uid_next)) {
2967 nummsgs = g_slist_length(item->uid_list);
2969 /* If number of messages is still the same we
2970 know our caches message numbers are still valid,
2971 otherwise if the number of messages has decrease
2972 we discard our cache to start a new scan to find
2973 out which numbers have been removed */
2974 if (exists == nummsgs) {
2975 *msgnum_list = g_slist_copy(item->uid_list);
2976 statusbar_pop_all();
2978 } else if (exists < nummsgs) {
2979 debug_print("Freeing imap uid cache");
2981 g_slist_free(item->uid_list);
2982 item->uid_list = NULL;
2987 *msgnum_list = NULL;
2988 statusbar_pop_all();
2992 nummsgs = get_list_of_uids(folder, item, &uidlist);
2995 statusbar_pop_all();
2999 if (nummsgs != exists) {
3000 /* Cache contains more messages then folder, we have cached
3001 an old UID of a message that was removed and new messages
3002 have been added too, otherwise the uid_next check would
3004 debug_print("Freeing imap uid cache");
3006 g_slist_free(item->uid_list);
3007 item->uid_list = NULL;
3009 g_slist_free(*msgnum_list);
3011 nummsgs = get_list_of_uids(folder, item, &uidlist);
3014 *msgnum_list = uidlist;
3016 dir = folder_item_get_path((FolderItem *)item);
3017 debug_print("removing old messages from %s\n", dir);
3018 remove_numbered_files_not_in_list(dir, *msgnum_list);
3021 debug_print("get_num_list - ok - %i\n", nummsgs);
3022 statusbar_pop_all();
3026 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item)
3031 flags.perm_flags = MSG_NEW|MSG_UNREAD;
3032 flags.tmp_flags = 0;
3034 g_return_val_if_fail(item != NULL, NULL);
3035 g_return_val_if_fail(file != NULL, NULL);
3037 if (folder_has_parent_of_type(item, F_QUEUE)) {
3038 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
3039 } else if (folder_has_parent_of_type(item, F_DRAFT)) {
3040 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
3043 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3044 if (!msginfo) return NULL;
3046 msginfo->plaintext_file = g_strdup(file);
3047 msginfo->folder = item;
3052 GSList *imap_get_msginfos(Folder *folder, FolderItem *item,
3053 GSList *msgnum_list)
3055 IMAPSession *session;
3056 MsgInfoList *ret = NULL;
3059 debug_print("get_msginfos\n");
3061 g_return_val_if_fail(folder != NULL, NULL);
3062 g_return_val_if_fail(item != NULL, NULL);
3063 g_return_val_if_fail(msgnum_list != NULL, NULL);
3065 session = imap_session_get(folder);
3066 g_return_val_if_fail(session != NULL, NULL);
3068 debug_print("IMAP getting msginfos\n");
3069 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3070 NULL, NULL, NULL, NULL, FALSE);
3071 if (ok != IMAP_SUCCESS)
3074 if (!(folder_has_parent_of_type(item, F_DRAFT) ||
3075 folder_has_parent_of_type(item, F_QUEUE))) {
3076 ret = g_slist_concat(ret,
3077 imap_get_uncached_messages(session, item,
3080 MsgNumberList *sorted_list, *elem;
3081 gint startnum, lastnum;
3083 sorted_list = g_slist_sort(g_slist_copy(msgnum_list), g_int_compare);
3085 startnum = lastnum = GPOINTER_TO_INT(sorted_list->data);
3087 for (elem = sorted_list;; elem = g_slist_next(elem)) {
3091 num = GPOINTER_TO_INT(elem->data);
3093 if (num > lastnum + 1 || elem == NULL) {
3095 for (i = startnum; i <= lastnum; ++i) {
3098 file = imap_fetch_msg(folder, item, i);
3100 MsgInfo *msginfo = imap_parse_msg(file, item);
3101 if (msginfo != NULL) {
3102 msginfo->msgnum = i;
3103 ret = g_slist_append(ret, msginfo);
3117 g_slist_free(sorted_list);
3123 MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid)
3125 MsgInfo *msginfo = NULL;
3126 MsgInfoList *msginfolist;
3127 MsgNumberList numlist;
3129 numlist.next = NULL;
3130 numlist.data = GINT_TO_POINTER(uid);
3132 msginfolist = imap_get_msginfos(folder, item, &numlist);
3133 if (msginfolist != NULL) {
3134 msginfo = msginfolist->data;
3135 g_slist_free(msginfolist);
3141 gboolean imap_scan_required(Folder *folder, FolderItem *_item)
3143 IMAPSession *session;
3144 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3145 gint ok, exists = 0, recent = 0, unseen = 0;
3146 guint32 uid_next, uid_val = 0;
3147 gboolean selected_folder;
3149 g_return_val_if_fail(folder != NULL, FALSE);
3150 g_return_val_if_fail(item != NULL, FALSE);
3151 g_return_val_if_fail(item->item.folder != NULL, FALSE);
3152 g_return_val_if_fail(FOLDER_CLASS(item->item.folder) == &imap_class, FALSE);
3154 if (item->item.path == NULL)
3157 session = imap_session_get(folder);
3158 g_return_val_if_fail(session != NULL, FALSE);
3160 selected_folder = (session->mbox != NULL) &&
3161 (!strcmp(session->mbox, item->item.path));
3162 if (selected_folder) {
3163 ok = imap_cmd_noop(session);
3164 if (ok != IMAP_SUCCESS) {
3165 debug_print("disconnected!\n");
3166 session = imap_reconnect_if_possible(folder, session);
3167 if (session == NULL)
3171 if (session->folder_content_changed
3172 || session->exists != item->item.total_msgs)
3175 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
3176 &exists, &recent, &uid_next, &uid_val, &unseen, FALSE);
3177 if (ok != IMAP_SUCCESS)
3180 if ((uid_next != item->uid_next) || (exists != item->item.total_msgs))
3187 void imap_change_flags(Folder *folder, FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
3189 IMAPSession *session;
3190 IMAPFlags flags_set = 0, flags_unset = 0;
3191 gint ok = IMAP_SUCCESS;
3192 MsgNumberList numlist;
3193 hashtable_data *ht_data = NULL;
3195 g_return_if_fail(folder != NULL);
3196 g_return_if_fail(folder->klass == &imap_class);
3197 g_return_if_fail(item != NULL);
3198 g_return_if_fail(item->folder == folder);
3199 g_return_if_fail(msginfo != NULL);
3200 g_return_if_fail(msginfo->folder == item);
3202 session = imap_session_get(folder);
3206 if ((ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
3207 NULL, NULL, NULL, NULL, FALSE)) != IMAP_SUCCESS) {
3211 if (!MSG_IS_MARKED(msginfo->flags) && (newflags & MSG_MARKED))
3212 flags_set |= IMAP_FLAG_FLAGGED;
3213 if ( MSG_IS_MARKED(msginfo->flags) && !(newflags & MSG_MARKED))
3214 flags_unset |= IMAP_FLAG_FLAGGED;
3216 if (!MSG_IS_UNREAD(msginfo->flags) && (newflags & MSG_UNREAD))
3217 flags_unset |= IMAP_FLAG_SEEN;
3218 if ( MSG_IS_UNREAD(msginfo->flags) && !(newflags & MSG_UNREAD))
3219 flags_set |= IMAP_FLAG_SEEN;
3221 if (!MSG_IS_REPLIED(msginfo->flags) && (newflags & MSG_REPLIED))
3222 flags_set |= IMAP_FLAG_ANSWERED;
3223 if ( MSG_IS_REPLIED(msginfo->flags) && !(newflags & MSG_REPLIED))
3224 flags_unset |= IMAP_FLAG_ANSWERED;
3226 if (!MSG_IS_DELETED(msginfo->flags) && (newflags & MSG_DELETED))
3227 flags_set |= IMAP_FLAG_DELETED;
3228 if ( MSG_IS_DELETED(msginfo->flags) && !(newflags & MSG_DELETED))
3229 flags_unset |= IMAP_FLAG_DELETED;
3231 numlist.next = NULL;
3232 numlist.data = GINT_TO_POINTER(msginfo->msgnum);
3234 if (IMAP_FOLDER_ITEM(item)->batching) {
3235 /* instead of performing an UID STORE command for each message change,
3236 * as a lot of them can change "together", we just fill in hashtables
3237 * and defer the treatment so that we're able to send only one
3240 debug_print("IMAP batch mode on, deferring flags change\n");
3242 ht_data = g_hash_table_lookup(flags_set_table, GINT_TO_POINTER(flags_set));
3243 if (ht_data == NULL) {
3244 ht_data = g_new0(hashtable_data, 1);
3245 ht_data->session = session;
3246 g_hash_table_insert(flags_set_table, GINT_TO_POINTER(flags_set), ht_data);
3248 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
3249 ht_data->msglist = g_slist_prepend(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum));
3252 ht_data = g_hash_table_lookup(flags_unset_table, GINT_TO_POINTER(flags_unset));
3253 if (ht_data == NULL) {
3254 ht_data = g_new0(hashtable_data, 1);
3255 ht_data->session = session;
3256 g_hash_table_insert(flags_unset_table, GINT_TO_POINTER(flags_unset), ht_data);
3258 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
3259 ht_data->msglist = g_slist_prepend(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum));
3262 debug_print("IMAP changing flags\n");
3264 ok = imap_set_message_flags(session, &numlist, flags_set, TRUE);
3265 if (ok != IMAP_SUCCESS) {
3271 ok = imap_set_message_flags(session, &numlist, flags_unset, FALSE);
3272 if (ok != IMAP_SUCCESS) {
3277 msginfo->flags.perm_flags = newflags;
3282 static gint imap_remove_msg(Folder *folder, FolderItem *item, gint uid)
3285 IMAPSession *session;
3287 MsgNumberList numlist;
3289 g_return_val_if_fail(folder != NULL, -1);
3290 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3291 g_return_val_if_fail(item != NULL, -1);
3293 session = imap_session_get(folder);
3294 if (!session) return -1;
3296 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3297 NULL, NULL, NULL, NULL, FALSE);
3298 if (ok != IMAP_SUCCESS)
3301 numlist.next = NULL;
3302 numlist.data = GINT_TO_POINTER(uid);
3304 ok = imap_set_message_flags
3305 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
3306 &numlist, IMAP_FLAG_DELETED, TRUE);
3307 if (ok != IMAP_SUCCESS) {
3308 log_warning(_("can't set deleted flags: %d\n"), uid);
3312 if (!session->uidplus) {
3313 ok = imap_cmd_expunge(session);
3317 uidstr = g_strdup_printf("%u", uid);
3318 ok = imap_cmd_expunge(session);
3321 if (ok != IMAP_SUCCESS) {
3322 log_warning(_("can't expunge\n"));
3326 IMAP_FOLDER_ITEM(item)->uid_list = g_slist_remove(
3327 IMAP_FOLDER_ITEM(item)->uid_list, numlist.data);
3328 dir = folder_item_get_path(item);
3329 if (is_dir_exist(dir))
3330 remove_numbered_files(dir, uid, uid);
3333 return IMAP_SUCCESS;
3336 static gint compare_msginfo(gconstpointer a, gconstpointer b)
3338 return ((MsgInfo *)a)->msgnum - ((MsgInfo *)b)->msgnum;
3341 static guint gslist_find_next_num(MsgNumberList **list, guint num)
3345 g_return_val_if_fail(list != NULL, -1);
3347 for (elem = *list; elem != NULL; elem = g_slist_next(elem))
3348 if (GPOINTER_TO_INT(elem->data) >= num)
3351 return elem != NULL ? GPOINTER_TO_INT(elem->data) : (gint)-1;
3355 * NEW and DELETED flags are not syncronized
3356 * - The NEW/RECENT flags in IMAP folders can not really be directly
3357 * modified by Sylpheed
3358 * - The DELETE/DELETED flag in IMAP and Sylpheed don't have the same
3359 * meaning, in IMAP it always removes the messages from the FolderItem
3360 * in Sylpheed it can mean to move the message to trash
3363 typedef struct _get_flags_data {
3366 MsgInfoList *msginfo_list;
3367 GRelation *msgflags;
3371 static /*gint*/ void *imap_get_flags_thread(void *data)
3373 get_flags_data *stuff = (get_flags_data *)data;
3374 Folder *folder = stuff->folder;
3375 FolderItem *item = stuff->item;
3376 MsgInfoList *msginfo_list = stuff->msginfo_list;
3377 GRelation *msgflags = stuff->msgflags;
3378 IMAPSession *session;
3379 GSList *sorted_list;
3380 GSList *unseen = NULL, *answered = NULL, *flagged = NULL, *deleted = NULL;
3381 GSList *p_unseen, *p_answered, *p_flagged, *p_deleted;
3383 GSList *seq_list, *cur;
3384 gboolean reverse_seen = FALSE;
3387 gint exists_cnt, recent_cnt, unseen_cnt, uid_next;
3388 guint32 uidvalidity;
3389 gboolean selected_folder;
3391 if (folder == NULL || item == NULL) {
3393 return GINT_TO_POINTER(-1);
3395 if (msginfo_list == NULL) {
3397 return GINT_TO_POINTER(0);
3400 session = imap_session_get(folder);
3401 if (session == NULL) {
3403 return GINT_TO_POINTER(-1);
3406 selected_folder = (session->mbox != NULL) &&
3407 (!strcmp(session->mbox, item->path));
3409 if (!selected_folder) {
3410 ok = imap_status(session, IMAP_FOLDER(folder), item->path,
3411 &exists_cnt, &recent_cnt, &uid_next, &uidvalidity, &unseen_cnt, TRUE);
3412 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3413 NULL, NULL, NULL, NULL, TRUE);
3414 if (ok != IMAP_SUCCESS) {
3416 return GINT_TO_POINTER(-1);
3419 if (unseen_cnt > exists_cnt / 2)
3420 reverse_seen = TRUE;
3423 if (item->unread_msgs > item->total_msgs / 2)
3424 reverse_seen = TRUE;
3427 cmd_buf = g_string_new(NULL);
3429 sorted_list = g_slist_sort(g_slist_copy(msginfo_list), compare_msginfo);
3431 seq_list = imap_get_lep_set_from_msglist(msginfo_list);
3433 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
3434 struct mailimap_set * imapset;
3435 clist * lep_uidlist;
3438 imapset = cur->data;
3440 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SEEN,
3441 imapset, &lep_uidlist);
3444 r = imap_threaded_search(folder,
3445 IMAP_SEARCH_TYPE_UNSEEN,
3446 imapset, &lep_uidlist);
3448 if (r == MAILIMAP_NO_ERROR) {
3451 uidlist = imap_uid_list_from_lep(lep_uidlist);
3452 mailimap_search_result_free(lep_uidlist);
3454 unseen = g_slist_concat(unseen, uidlist);
3457 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_ANSWERED,
3458 imapset, &lep_uidlist);
3459 if (r == MAILIMAP_NO_ERROR) {
3462 uidlist = imap_uid_list_from_lep(lep_uidlist);
3463 mailimap_search_result_free(lep_uidlist);
3465 answered = g_slist_concat(answered, uidlist);
3468 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_FLAGGED,
3469 imapset, &lep_uidlist);
3470 if (r == MAILIMAP_NO_ERROR) {
3473 uidlist = imap_uid_list_from_lep(lep_uidlist);
3474 mailimap_search_result_free(lep_uidlist);
3476 flagged = g_slist_concat(flagged, uidlist);
3479 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_DELETED,
3480 imapset, &lep_uidlist);
3481 if (r == MAILIMAP_NO_ERROR) {
3484 uidlist = imap_uid_list_from_lep(lep_uidlist);
3485 mailimap_search_result_free(lep_uidlist);
3487 deleted = g_slist_concat(deleted, uidlist);
3492 p_answered = answered;
3493 p_flagged = flagged;
3494 p_deleted = deleted;
3496 for (elem = sorted_list; elem != NULL; elem = g_slist_next(elem)) {
3501 msginfo = (MsgInfo *) elem->data;
3502 flags = msginfo->flags.perm_flags;
3503 wasnew = (flags & MSG_NEW);
3504 flags &= ~((reverse_seen ? 0 : MSG_UNREAD | MSG_NEW) | MSG_REPLIED | MSG_MARKED);
3506 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
3507 if (gslist_find_next_num(&p_unseen, msginfo->msgnum) == msginfo->msgnum) {
3508 if (!reverse_seen) {
3509 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
3511 flags &= ~(MSG_UNREAD | MSG_NEW);
3514 if (gslist_find_next_num(&p_answered, msginfo->msgnum) == msginfo->msgnum)
3515 flags |= MSG_REPLIED;
3517 flags &= ~MSG_REPLIED;
3518 if (gslist_find_next_num(&p_flagged, msginfo->msgnum) == msginfo->msgnum)
3519 flags |= MSG_MARKED;
3521 flags &= ~MSG_MARKED;
3522 if (gslist_find_next_num(&p_deleted, msginfo->msgnum) == msginfo->msgnum)
3523 flags |= MSG_DELETED;
3525 flags &= ~MSG_DELETED;
3526 g_relation_insert(msgflags, msginfo, GINT_TO_POINTER(flags));
3529 imap_lep_set_free(seq_list);
3530 g_slist_free(flagged);
3531 g_slist_free(deleted);
3532 g_slist_free(answered);
3533 g_slist_free(unseen);
3534 g_slist_free(sorted_list);
3535 g_string_free(cmd_buf, TRUE);
3538 return GINT_TO_POINTER(0);
3541 static gint imap_get_flags(Folder *folder, FolderItem *item,
3542 MsgInfoList *msginfo_list, GRelation *msgflags)
3545 get_flags_data *data = g_new0(get_flags_data, 1);
3547 data->folder = folder;
3549 data->msginfo_list = msginfo_list;
3550 data->msgflags = msgflags;
3552 if (prefs_common.work_offline && !imap_gtk_should_override()) {
3557 result = GPOINTER_TO_INT(imap_get_flags_thread(data));
3564 static gboolean process_flags(gpointer key, gpointer value, gpointer user_data)
3566 gboolean flags_set = GPOINTER_TO_INT(user_data);
3567 gint flags_value = GPOINTER_TO_INT(key);
3568 hashtable_data *data = (hashtable_data *)value;
3570 data->msglist = g_slist_reverse(data->msglist);
3572 debug_print("IMAP %ssetting flags to %d for %d messages\n",
3575 g_slist_length(data->msglist));
3576 imap_set_message_flags(data->session, data->msglist, flags_value, flags_set);
3578 g_slist_free(data->msglist);
3583 static void process_hashtable(void)
3585 if (flags_set_table) {
3586 g_hash_table_foreach_remove(flags_set_table, process_flags, GINT_TO_POINTER(TRUE));
3587 g_free(flags_set_table);
3588 flags_set_table = NULL;
3590 if (flags_unset_table) {
3591 g_hash_table_foreach_remove(flags_unset_table, process_flags, GINT_TO_POINTER(FALSE));
3592 g_free(flags_unset_table);
3593 flags_unset_table = NULL;
3597 static IMAPFolderItem *batching_item = NULL;
3599 static void imap_set_batch (Folder *folder, FolderItem *_item, gboolean batch)
3601 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3603 g_return_if_fail(item != NULL);
3605 if (batch && batching_item != NULL) {
3606 g_warning("already batching on %s\n", batching_item->item.path);
3610 if (item->batching == batch)
3613 item->batching = batch;
3615 batching_item = batch?item:NULL;
3618 debug_print("IMAP switching to batch mode\n");
3619 if (flags_set_table) {
3620 g_warning("flags_set_table non-null but we just entered batch mode!\n");
3621 flags_set_table = NULL;
3623 if (flags_unset_table) {
3624 g_warning("flags_unset_table non-null but we just entered batch mode!\n");
3625 flags_unset_table = NULL;
3627 flags_set_table = g_hash_table_new(NULL, g_direct_equal);
3628 flags_unset_table = g_hash_table_new(NULL, g_direct_equal);
3630 debug_print("IMAP switching away from batch mode\n");
3632 process_hashtable();
3638 /* data types conversion libetpan <-> sylpheed */
3642 #define ETPAN_IMAP_MB_MARKED 1
3643 #define ETPAN_IMAP_MB_UNMARKED 2
3644 #define ETPAN_IMAP_MB_NOSELECT 4
3645 #define ETPAN_IMAP_MB_NOINFERIORS 8
3647 static int imap_flags_to_flags(struct mailimap_mbx_list_flags * imap_flags)
3653 if (imap_flags->mbf_type == MAILIMAP_MBX_LIST_FLAGS_SFLAG) {
3654 switch (imap_flags->mbf_sflag) {
3655 case MAILIMAP_MBX_LIST_SFLAG_MARKED:
3656 flags |= ETPAN_IMAP_MB_MARKED;
3658 case MAILIMAP_MBX_LIST_SFLAG_NOSELECT:
3659 flags |= ETPAN_IMAP_MB_NOSELECT;
3661 case MAILIMAP_MBX_LIST_SFLAG_UNMARKED:
3662 flags |= ETPAN_IMAP_MB_UNMARKED;
3667 for(cur = clist_begin(imap_flags->mbf_oflags) ; cur != NULL ;
3668 cur = clist_next(cur)) {
3669 struct mailimap_mbx_list_oflag * oflag;
3671 oflag = clist_content(cur);
3673 switch (oflag->of_type) {
3674 case MAILIMAP_MBX_LIST_OFLAG_NOINFERIORS:
3675 flags |= ETPAN_IMAP_MB_NOINFERIORS;
3683 static GSList * imap_list_from_lep(IMAPFolder * folder,
3684 clist * list, const gchar * real_path)
3691 for(iter = clist_begin(list) ; iter != NULL ;
3692 iter = clist_next(iter)) {
3693 struct mailimap_mailbox_list * mb;
3701 FolderItem *new_item;
3703 mb = clist_content(iter);
3706 if (mb->mb_flag != NULL)
3707 flags = imap_flags_to_flags(mb->mb_flag);
3709 delimiter = mb->mb_delimiter;
3712 dup_name = strdup(name);
3713 if (delimiter != '\0')
3714 subst_char(dup_name, delimiter, '/');
3716 base = g_path_get_basename(dup_name);
3717 if (base[0] == '.') {
3723 if (strcmp(dup_name, real_path) == 0) {
3729 if (dup_name[strlen(dup_name)-1] == '/') {
3735 loc_name = imap_modified_utf7_to_utf8(base);
3736 loc_path = imap_modified_utf7_to_utf8(dup_name);
3738 new_item = folder_item_new(FOLDER(folder), loc_name, loc_path);
3739 if ((flags & ETPAN_IMAP_MB_NOINFERIORS) != 0)
3740 new_item->no_sub = TRUE;
3741 if (strcmp(dup_name, "INBOX") != 0 &&
3742 ((flags & ETPAN_IMAP_MB_NOSELECT) != 0))
3743 new_item->no_select = TRUE;
3745 item_list = g_slist_append(item_list, new_item);
3747 debug_print("folder '%s' found.\n", loc_path);
3758 static GSList * imap_get_lep_set_from_numlist(MsgNumberList *numlist)
3760 GSList *sorted_list, *cur;
3761 guint first, last, next;
3762 GSList *ret_list = NULL;
3764 struct mailimap_set * current_set;
3765 unsigned int item_count;
3767 if (numlist == NULL)
3771 current_set = mailimap_set_new_empty();
3773 sorted_list = g_slist_copy(numlist);
3774 sorted_list = g_slist_sort(sorted_list, g_int_compare);
3776 first = GPOINTER_TO_INT(sorted_list->data);
3779 for (cur = sorted_list; cur != NULL; cur = g_slist_next(cur)) {
3780 if (GPOINTER_TO_INT(cur->data) == 0)
3785 last = GPOINTER_TO_INT(cur->data);
3787 next = GPOINTER_TO_INT(cur->next->data);
3791 if (last + 1 != next || next == 0) {
3793 struct mailimap_set_item * item;
3794 item = mailimap_set_item_new(first, last);
3795 mailimap_set_add(current_set, item);
3800 if (count >= IMAP_SET_MAX_COUNT) {
3801 ret_list = g_slist_append(ret_list,
3803 current_set = mailimap_set_new_empty();
3810 if (clist_count(current_set->set_list) > 0) {
3811 ret_list = g_slist_append(ret_list,
3815 g_slist_free(sorted_list);
3820 static GSList * imap_get_lep_set_from_msglist(MsgInfoList *msglist)
3822 MsgNumberList *numlist = NULL;
3826 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
3827 MsgInfo *msginfo = (MsgInfo *) cur->data;
3829 numlist = g_slist_append(numlist, GINT_TO_POINTER(msginfo->msgnum));
3831 seq_list = imap_get_lep_set_from_numlist(numlist);
3832 g_slist_free(numlist);
3837 static GSList * imap_uid_list_from_lep(clist * list)
3844 for(iter = clist_begin(list) ; iter != NULL ;
3845 iter = clist_next(iter)) {
3848 puid = clist_content(iter);
3849 result = g_slist_append(result, GINT_TO_POINTER(* puid));
3855 static GSList * imap_uid_list_from_lep_tab(carray * list)
3862 for(i = 0 ; i < carray_count(list) ; i ++) {
3865 puid = carray_get(list, i);
3866 result = g_slist_append(result, GINT_TO_POINTER(* puid));
3872 static MsgInfo *imap_envelope_from_lep(struct imap_fetch_env_info * info,
3875 MsgInfo *msginfo = NULL;
3878 MsgFlags flags = {0, 0};
3880 MSG_SET_TMP_FLAGS(flags, MSG_IMAP);
3881 if (folder_has_parent_of_type(item, F_QUEUE)) {
3882 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
3883 } else if (folder_has_parent_of_type(item, F_DRAFT)) {
3884 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
3886 flags.perm_flags = info->flags;
3890 msginfo = procheader_parse_str(info->headers, flags, FALSE, FALSE);
3893 msginfo->msgnum = uid;
3894 msginfo->size = size;
3900 static void imap_lep_set_free(GSList *seq_list)
3904 for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) {
3905 struct mailimap_set * imapset;
3907 imapset = cur->data;
3908 mailimap_set_free(imapset);
3910 g_slist_free(seq_list);
3913 static struct mailimap_flag_list * imap_flag_to_lep(IMAPFlags flags)
3915 struct mailimap_flag_list * flag_list;
3917 flag_list = mailimap_flag_list_new_empty();
3919 if (IMAP_IS_SEEN(flags))
3920 mailimap_flag_list_add(flag_list,
3921 mailimap_flag_new_seen());
3922 if (IMAP_IS_ANSWERED(flags))
3923 mailimap_flag_list_add(flag_list,
3924 mailimap_flag_new_answered());
3925 if (IMAP_IS_FLAGGED(flags))
3926 mailimap_flag_list_add(flag_list,
3927 mailimap_flag_new_flagged());
3928 if (IMAP_IS_DELETED(flags))
3929 mailimap_flag_list_add(flag_list,
3930 mailimap_flag_new_deleted());
3931 if (IMAP_IS_DRAFT(flags))
3932 mailimap_flag_list_add(flag_list,
3933 mailimap_flag_new_draft());
3938 guint imap_folder_get_refcnt(Folder *folder)
3940 return ((IMAPFolder *)folder)->refcnt;
3943 void imap_folder_ref(Folder *folder)
3945 ((IMAPFolder *)folder)->refcnt++;
3948 void imap_folder_unref(Folder *folder)
3950 if (((IMAPFolder *)folder)->refcnt > 0)
3951 ((IMAPFolder *)folder)->refcnt--;
3954 #else /* HAVE_LIBETPAN */
3956 static FolderClass imap_class;
3958 static Folder *imap_folder_new (const gchar *name,
3963 static gint imap_create_tree (Folder *folder)
3967 static FolderItem *imap_create_folder (Folder *folder,
3973 static gint imap_rename_folder (Folder *folder,
3980 FolderClass *imap_get_class(void)
3982 if (imap_class.idstr == NULL) {
3983 imap_class.type = F_IMAP;
3984 imap_class.idstr = "imap";
3985 imap_class.uistr = "IMAP4";
3987 imap_class.new_folder = imap_folder_new;
3988 imap_class.create_tree = imap_create_tree;
3989 imap_class.create_folder = imap_create_folder;
3990 imap_class.rename_folder = imap_rename_folder;
3991 /* nothing implemented */
3998 void imap_synchronise(FolderItem *item)
4000 imap_gtk_synchronise(item);