2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2006 Hiroyuki Yamamoto and the Sylpheed-Claws team
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
27 #include <glib/gi18n.h>
55 #include "procheader.h"
56 #include "prefs_account.h"
61 #include "prefs_common.h"
62 #include "inputdialog.h"
64 #include "remotefolder.h"
65 #include "alertpanel.h"
67 #include "statusbar.h"
69 #include "imap-thread.h"
71 typedef struct _IMAPFolder IMAPFolder;
72 typedef struct _IMAPSession IMAPSession;
73 typedef struct _IMAPNameSpace IMAPNameSpace;
74 typedef struct _IMAPFolderItem IMAPFolderItem;
76 #include "prefs_account.h"
78 #define IMAP_FOLDER(obj) ((IMAPFolder *)obj)
79 #define IMAP_FOLDER_ITEM(obj) ((IMAPFolderItem *)obj)
80 #define IMAP_SESSION(obj) ((IMAPSession *)obj)
86 /* list of IMAPNameSpace */
90 gchar last_seen_separator;
98 gboolean authenticated;
107 gboolean folder_content_changed;
113 struct _IMAPNameSpace
119 #define IMAP_SUCCESS 0
120 #define IMAP_SOCKET 2
121 #define IMAP_AUTHFAIL 3
122 #define IMAP_PROTOCOL 4
123 #define IMAP_SYNTAX 5
127 #define IMAPBUFSIZE 8192
131 IMAP_FLAG_SEEN = 1 << 0,
132 IMAP_FLAG_ANSWERED = 1 << 1,
133 IMAP_FLAG_FLAGGED = 1 << 2,
134 IMAP_FLAG_DELETED = 1 << 3,
135 IMAP_FLAG_DRAFT = 1 << 4
138 #define IMAP_IS_SEEN(flags) ((flags & IMAP_FLAG_SEEN) != 0)
139 #define IMAP_IS_ANSWERED(flags) ((flags & IMAP_FLAG_ANSWERED) != 0)
140 #define IMAP_IS_FLAGGED(flags) ((flags & IMAP_FLAG_FLAGGED) != 0)
141 #define IMAP_IS_DELETED(flags) ((flags & IMAP_FLAG_DELETED) != 0)
142 #define IMAP_IS_DRAFT(flags) ((flags & IMAP_FLAG_DRAFT) != 0)
145 #define IMAP4_PORT 143
147 #define IMAPS_PORT 993
150 #define IMAP_CMD_LIMIT 1000
152 struct _IMAPFolderItem
164 guint32 c_uid_validity;
167 GHashTable *flags_set_table;
168 GHashTable *flags_unset_table;
171 static void imap_folder_init (Folder *folder,
175 static Folder *imap_folder_new (const gchar *name,
177 static void imap_folder_destroy (Folder *folder);
179 static IMAPSession *imap_session_new (Folder *folder,
180 const PrefsAccount *account);
181 static void imap_session_authenticate(IMAPSession *session,
182 const PrefsAccount *account);
183 static void imap_session_destroy (Session *session);
185 static gchar *imap_fetch_msg (Folder *folder,
188 static gchar *imap_fetch_msg_full (Folder *folder,
193 static gint imap_add_msg (Folder *folder,
197 static gint imap_add_msgs (Folder *folder,
200 GRelation *relation);
202 static gint imap_copy_msg (Folder *folder,
205 static gint imap_copy_msgs (Folder *folder,
207 MsgInfoList *msglist,
208 GRelation *relation);
210 static gint imap_remove_msg (Folder *folder,
213 static gint imap_remove_msgs (Folder *folder,
215 MsgInfoList *msglist,
216 GRelation *relation);
217 static gint imap_remove_all_msg (Folder *folder,
220 static gboolean imap_is_msg_changed (Folder *folder,
224 static gint imap_close (Folder *folder,
227 static gint imap_scan_tree (Folder *folder);
229 static gint imap_create_tree (Folder *folder);
231 static FolderItem *imap_create_folder (Folder *folder,
234 static gint imap_rename_folder (Folder *folder,
237 static gint imap_remove_folder (Folder *folder,
240 static FolderItem *imap_folder_item_new (Folder *folder);
241 static void imap_folder_item_destroy (Folder *folder,
244 static IMAPSession *imap_session_get (Folder *folder);
246 static gint imap_auth (IMAPSession *session,
251 static gint imap_scan_tree_recursive (IMAPSession *session,
254 static void imap_create_missing_folders (Folder *folder);
255 static FolderItem *imap_create_special_folder
257 SpecialFolderItemType stype,
260 static gint imap_do_copy_msgs (Folder *folder,
262 MsgInfoList *msglist,
263 GRelation *relation);
265 static void imap_delete_all_cached_messages (FolderItem *item);
266 static void imap_set_batch (Folder *folder,
269 static gint imap_set_message_flags (IMAPSession *session,
270 MsgNumberList *numlist,
273 static gint imap_select (IMAPSession *session,
279 guint32 *uid_validity,
281 static gint imap_status (IMAPSession *session,
284 IMAPFolderItem *item,
287 guint32 *uid_validity,
291 static IMAPNameSpace *imap_find_namespace (IMAPFolder *folder,
293 static gchar imap_get_path_separator (IMAPFolder *folder,
295 static gchar *imap_get_real_path (IMAPFolder *folder,
297 static void imap_synchronise (FolderItem *item);
299 static void imap_free_capabilities (IMAPSession *session);
301 /* low-level IMAP4rev1 commands */
302 static gint imap_cmd_login (IMAPSession *session,
306 static gint imap_cmd_logout (IMAPSession *session);
307 static gint imap_cmd_noop (IMAPSession *session);
309 static gint imap_cmd_starttls (IMAPSession *session);
311 static gint imap_cmd_select (IMAPSession *session,
316 guint32 *uid_validity,
318 static gint imap_cmd_examine (IMAPSession *session,
323 guint32 *uid_validity,
325 static gint imap_cmd_create (IMAPSession *sock,
326 const gchar *folder);
327 static gint imap_cmd_rename (IMAPSession *sock,
328 const gchar *oldfolder,
329 const gchar *newfolder);
330 static gint imap_cmd_delete (IMAPSession *session,
331 const gchar *folder);
332 static gint imap_cmd_fetch (IMAPSession *sock,
334 const gchar *filename,
337 static gint imap_cmd_append (IMAPSession *session,
338 const gchar *destfolder,
342 static gint imap_cmd_copy (IMAPSession *session,
343 struct mailimap_set * set,
344 const gchar *destfolder,
345 GRelation *uid_mapping);
346 static gint imap_cmd_store (IMAPSession *session,
347 struct mailimap_set * set,
350 static gint imap_cmd_expunge (IMAPSession *session);
352 static void imap_path_separator_subst (gchar *str,
355 static gchar *imap_utf8_to_modified_utf7 (const gchar *from);
356 static gchar *imap_modified_utf7_to_utf8 (const gchar *mutf7_str);
358 static gboolean imap_rename_folder_func (GNode *node,
360 static gint imap_get_num_list (Folder *folder,
363 gboolean *old_uids_valid);
364 static GSList *imap_get_msginfos (Folder *folder,
366 GSList *msgnum_list);
367 static MsgInfo *imap_get_msginfo (Folder *folder,
370 static gboolean imap_scan_required (Folder *folder,
372 static void imap_change_flags (Folder *folder,
375 MsgPermFlags newflags);
376 static gint imap_get_flags (Folder *folder,
378 MsgInfoList *msglist,
379 GRelation *msgflags);
380 static gchar *imap_folder_get_path (Folder *folder);
381 static gchar *imap_item_get_path (Folder *folder,
383 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item);
386 /* data types conversion libetpan <-> sylpheed */
387 static GSList * imap_list_from_lep(IMAPFolder * folder,
388 clist * list, const gchar * real_path, gboolean all);
389 static GSList * imap_get_lep_set_from_numlist(MsgNumberList *numlist);
390 static GSList * imap_get_lep_set_from_msglist(MsgInfoList *msglist);
391 static GSList * imap_uid_list_from_lep(clist * list);
392 static GSList * imap_uid_list_from_lep_tab(carray * list);
393 static MsgInfo *imap_envelope_from_lep(struct imap_fetch_env_info * info,
395 static void imap_lep_set_free(GSList *seq_list);
396 static struct mailimap_flag_list * imap_flag_to_lep(IMAPFlags flags);
398 typedef struct _hashtable_data {
399 IMAPSession *session;
401 IMAPFolderItem *item;
404 static FolderClass imap_class;
406 typedef struct _thread_data {
416 FolderClass *imap_get_class(void)
418 if (imap_class.idstr == NULL) {
419 imap_class.type = F_IMAP;
420 imap_class.idstr = "imap";
421 imap_class.uistr = "IMAP4";
423 /* Folder functions */
424 imap_class.new_folder = imap_folder_new;
425 imap_class.destroy_folder = imap_folder_destroy;
426 imap_class.scan_tree = imap_scan_tree;
427 imap_class.create_tree = imap_create_tree;
429 /* FolderItem functions */
430 imap_class.item_new = imap_folder_item_new;
431 imap_class.item_destroy = imap_folder_item_destroy;
432 imap_class.item_get_path = imap_item_get_path;
433 imap_class.create_folder = imap_create_folder;
434 imap_class.rename_folder = imap_rename_folder;
435 imap_class.remove_folder = imap_remove_folder;
436 imap_class.close = imap_close;
437 imap_class.get_num_list = imap_get_num_list;
438 imap_class.scan_required = imap_scan_required;
440 /* Message functions */
441 imap_class.get_msginfo = imap_get_msginfo;
442 imap_class.get_msginfos = imap_get_msginfos;
443 imap_class.fetch_msg = imap_fetch_msg;
444 imap_class.fetch_msg_full = imap_fetch_msg_full;
445 imap_class.add_msg = imap_add_msg;
446 imap_class.add_msgs = imap_add_msgs;
447 imap_class.copy_msg = imap_copy_msg;
448 imap_class.copy_msgs = imap_copy_msgs;
449 imap_class.remove_msg = imap_remove_msg;
450 imap_class.remove_msgs = imap_remove_msgs;
451 imap_class.remove_all_msg = imap_remove_all_msg;
452 imap_class.is_msg_changed = imap_is_msg_changed;
453 imap_class.change_flags = imap_change_flags;
454 imap_class.get_flags = imap_get_flags;
455 imap_class.set_batch = imap_set_batch;
456 imap_class.synchronise = imap_synchronise;
458 pthread_mutex_init(&imap_mutex, NULL);
465 static Folder *imap_folder_new(const gchar *name, const gchar *path)
469 folder = (Folder *)g_new0(IMAPFolder, 1);
470 folder->klass = &imap_class;
471 imap_folder_init(folder, name, path);
476 static void imap_folder_destroy(Folder *folder)
480 while (imap_folder_get_refcnt(folder) > 0)
481 gtk_main_iteration();
483 dir = imap_folder_get_path(folder);
484 if (is_dir_exist(dir))
485 remove_dir_recursive(dir);
488 folder_remote_folder_destroy(REMOTE_FOLDER(folder));
492 static void imap_folder_init(Folder *folder, const gchar *name,
495 folder_remote_folder_init((Folder *)folder, name, path);
498 static FolderItem *imap_folder_item_new(Folder *folder)
500 IMAPFolderItem *item;
502 item = g_new0(IMAPFolderItem, 1);
505 item->uid_list = NULL;
507 return (FolderItem *)item;
510 static void imap_folder_item_destroy(Folder *folder, FolderItem *_item)
512 IMAPFolderItem *item = (IMAPFolderItem *)_item;
514 g_return_if_fail(item != NULL);
515 g_slist_free(item->uid_list);
520 static gboolean imap_reset_uid_lists_func(GNode *node, gpointer data)
522 IMAPFolderItem *item = (IMAPFolderItem *)node->data;
526 g_slist_free(item->uid_list);
527 item->uid_list = NULL;
532 static void imap_reset_uid_lists(Folder *folder)
534 if(folder->node == NULL)
537 /* Destroy all uid lists and rest last uid */
538 g_node_traverse(folder->node, G_IN_ORDER, G_TRAVERSE_ALL, -1, imap_reset_uid_lists_func, NULL);
541 void imap_get_capabilities(IMAPSession *session)
543 struct mailimap_capability_data *capabilities = NULL;
546 if (session->capability != NULL)
549 capabilities = imap_threaded_capability(session->folder);
551 if (capabilities == NULL)
554 for(cur = clist_begin(capabilities->cap_list) ; cur != NULL ;
555 cur = clist_next(cur)) {
556 struct mailimap_capability * cap =
558 if (!cap || cap->cap_data.cap_name == NULL)
560 session->capability = g_slist_append
561 (session->capability,
562 g_strdup(cap->cap_data.cap_name));
563 debug_print("got capa %s\n", cap->cap_data.cap_name);
565 mailimap_capability_data_free(capabilities);
568 gboolean imap_has_capability(IMAPSession *session, const gchar *cap)
571 for (cur = session->capability; cur; cur = cur->next) {
572 if (!g_ascii_strcasecmp(cur->data, cap))
578 static gint imap_auth(IMAPSession *session, const gchar *user, const gchar *pass,
581 gint ok = IMAP_ERROR;
582 static time_t last_login_err = 0;
584 imap_get_capabilities(session);
587 case IMAP_AUTH_CRAM_MD5:
588 ok = imap_cmd_login(session, user, pass, "CRAM-MD5");
590 case IMAP_AUTH_LOGIN:
591 ok = imap_cmd_login(session, user, pass, "LOGIN");
594 debug_print("capabilities:\n"
597 imap_has_capability(session, "CRAM-MD5"),
598 imap_has_capability(session, "LOGIN"));
599 if (imap_has_capability(session, "CRAM-MD5"))
600 ok = imap_cmd_login(session, user, pass, "CRAM-MD5");
601 if (ok == IMAP_ERROR) /* we always try LOGIN before giving up */
602 ok = imap_cmd_login(session, user, pass, "LOGIN");
604 if (ok == IMAP_SUCCESS)
605 session->authenticated = TRUE;
607 gchar *ext_info = NULL;
609 if (type == IMAP_AUTH_CRAM_MD5) {
610 ext_info = _("\n\nCRAM-MD5 logins only work if libetpan has been "
611 "compiled with SASL support and the "
612 "CRAM-MD5 SASL plugin is installed.");
617 if (time(NULL) - last_login_err > 10) {
618 if (!prefs_common.no_recv_err_panel) {
619 alertpanel_error(_("Connection to %s failed: "
621 SESSION(session)->server, ext_info);
623 log_error(_("Connection to %s failed: "
624 "login refused.%s\n"),
625 SESSION(session)->server, ext_info);
628 last_login_err = time(NULL);
633 static IMAPSession *imap_reconnect_if_possible(Folder *folder, IMAPSession *session)
635 RemoteFolder *rfolder = REMOTE_FOLDER(folder);
636 /* Check if this is the first try to establish a
637 connection, if yes we don't try to reconnect */
638 debug_print("reconnecting\n");
639 if (rfolder->session == NULL) {
640 log_warning(_("Connecting to %s failed"),
641 folder->account->recv_server);
642 session_destroy(SESSION(session));
645 log_warning(_("IMAP4 connection to %s has been"
646 " disconnected. Reconnecting...\n"),
647 folder->account->recv_server);
648 statusbar_print_all(_("IMAP4 connection to %s has been"
649 " disconnected. Reconnecting...\n"),
650 folder->account->recv_server);
651 SESSION(session)->state = SESSION_DISCONNECTED;
652 session_destroy(SESSION(session));
653 /* Clear folders session to make imap_session_get create
654 a new session, because of rfolder->session == NULL
655 it will not try to reconnect again and so avoid an
657 rfolder->session = NULL;
658 session = imap_session_get(folder);
659 rfolder->session = SESSION(session);
665 #define lock_session() {\
666 debug_print("locking session\n"); \
667 session->busy = TRUE;\
670 #define unlock_session() {\
671 debug_print("unlocking session\n"); \
672 session->busy = FALSE;\
675 static IMAPSession *imap_session_get(Folder *folder)
677 RemoteFolder *rfolder = REMOTE_FOLDER(folder);
678 IMAPSession *session = NULL;
680 g_return_val_if_fail(folder != NULL, NULL);
681 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, NULL);
682 g_return_val_if_fail(folder->account != NULL, NULL);
684 if (prefs_common.work_offline && !inc_offline_should_override()) {
688 /* Make sure we have a session */
689 if (rfolder->session != NULL) {
690 session = IMAP_SESSION(rfolder->session);
691 /* don't do that yet...
696 imap_reset_uid_lists(folder);
697 session = imap_session_new(folder, folder->account);
702 /* Make sure session is authenticated */
703 if (!IMAP_SESSION(session)->authenticated)
704 imap_session_authenticate(IMAP_SESSION(session), folder->account);
706 if (!IMAP_SESSION(session)->authenticated) {
707 session_destroy(SESSION(session));
708 rfolder->session = NULL;
712 /* I think the point of this code is to avoid sending a
713 * keepalive if we've used the session recently and therefore
714 * think it's still alive. Unfortunately, most of the code
715 * does not yet check for errors on the socket, and so if the
716 * connection drops we don't notice until the timeout expires.
717 * A better solution than sending a NOOP every time would be
718 * for every command to be prepared to retry until it is
719 * successfully sent. -- mbp */
720 if (time(NULL) - SESSION(session)->last_access_time > SESSION_TIMEOUT_INTERVAL) {
721 /* verify that the session is still alive */
722 if (imap_cmd_noop(session) != IMAP_SUCCESS) {
723 debug_print("disconnected!\n");
724 session = imap_reconnect_if_possible(folder, session);
728 rfolder->session = SESSION(session);
730 return IMAP_SESSION(session);
733 static IMAPSession *imap_session_new(Folder * folder,
734 const PrefsAccount *account)
736 IMAPSession *session;
742 /* FIXME: IMAP over SSL only... */
745 port = account->set_imapport ? account->imapport
746 : account->ssl_imap == SSL_TUNNEL ? IMAPS_PORT : IMAP4_PORT;
747 ssl_type = account->ssl_imap;
749 if (account->ssl_imap != SSL_NONE) {
750 if (alertpanel_full(_("Insecure connection"),
751 _("This connection is configured to be secured "
752 "using SSL, but SSL is not available in this "
753 "build of Sylpheed-Claws. \n\n"
754 "Do you want to continue connecting to this "
755 "server? The communication would not be "
757 _("Con_tinue connecting"),
758 GTK_STOCK_CANCEL, NULL,
759 FALSE, NULL, ALERT_WARNING,
760 G_ALERTALTERNATE) != G_ALERTDEFAULT)
763 port = account->set_imapport ? account->imapport
768 statusbar_print_all(_("Connecting to IMAP4 server: %s..."), folder->account->recv_server);
769 if (account->set_tunnelcmd) {
770 r = imap_threaded_connect_cmd(folder,
772 account->recv_server,
777 if (ssl_type == SSL_TUNNEL) {
778 r = imap_threaded_connect_ssl(folder,
779 account->recv_server,
785 r = imap_threaded_connect(folder,
786 account->recv_server,
792 if (r == MAILIMAP_NO_ERROR_AUTHENTICATED) {
793 authenticated = TRUE;
795 else if (r == MAILIMAP_NO_ERROR_NON_AUTHENTICATED) {
796 authenticated = FALSE;
799 if(!prefs_common.no_recv_err_panel) {
800 alertpanel_error(_("Can't connect to IMAP4 server: %s:%d"),
801 account->recv_server, port);
803 log_error(_("Can't connect to IMAP4 server: %s:%d\n"),
804 account->recv_server, port);
810 session = g_new0(IMAPSession, 1);
811 session_init(SESSION(session));
812 SESSION(session)->type = SESSION_IMAP;
813 SESSION(session)->server = g_strdup(account->recv_server);
814 SESSION(session)->sock = NULL;
816 SESSION(session)->destroy = imap_session_destroy;
818 session->capability = NULL;
820 session->authenticated = authenticated;
821 session->mbox = NULL;
822 session->cmd_count = 0;
823 session->folder = folder;
824 IMAP_FOLDER(session->folder)->last_seen_separator = 0;
827 if (account->ssl_imap == SSL_STARTTLS) {
830 ok = imap_cmd_starttls(session);
831 if (ok != IMAP_SUCCESS) {
832 log_warning(_("Can't start TLS session.\n"));
833 session_destroy(SESSION(session));
837 imap_free_capabilities(session);
838 session->authenticated = FALSE;
839 session->uidplus = FALSE;
840 session->cmd_count = 1;
843 log_message("IMAP connection is %s-authenticated\n",
844 (session->authenticated) ? "pre" : "un");
849 static void imap_session_authenticate(IMAPSession *session,
850 const PrefsAccount *account)
854 g_return_if_fail(account->userid != NULL);
856 pass = account->passwd;
859 tmp_pass = input_dialog_query_password(account->recv_server, account->userid);
861 tmp_pass = g_strdup(""); /* allow empty password */
862 Xstrdup_a(pass, tmp_pass, {g_free(tmp_pass); return;});
865 statusbar_print_all(_("Connecting to IMAP4 server %s...\n"),
866 account->recv_server);
867 if (imap_auth(session, account->userid, pass, account->imap_auth_type) != IMAP_SUCCESS) {
868 imap_threaded_disconnect(session->folder);
869 imap_cmd_logout(session);
875 session->authenticated = TRUE;
878 static void imap_session_destroy(Session *session)
880 if (session->state != SESSION_DISCONNECTED)
881 imap_threaded_disconnect(IMAP_SESSION(session)->folder);
883 imap_free_capabilities(IMAP_SESSION(session));
884 g_free(IMAP_SESSION(session)->mbox);
885 sock_close(session->sock);
886 session->sock = NULL;
889 static gchar *imap_fetch_msg(Folder *folder, FolderItem *item, gint uid)
891 return imap_fetch_msg_full(folder, item, uid, TRUE, TRUE);
894 static guint get_size_with_crs(MsgInfo *info)
903 fp = procmsg_open_message(info);
907 while (fgets(buf, sizeof (buf), fp) != NULL) {
909 if (!strstr(buf, "\r") && strstr(buf, "\n"))
917 static gchar *imap_fetch_msg_full(Folder *folder, FolderItem *item, gint uid,
918 gboolean headers, gboolean body)
920 gchar *path, *filename;
921 IMAPSession *session;
924 g_return_val_if_fail(folder != NULL, NULL);
925 g_return_val_if_fail(item != NULL, NULL);
930 path = folder_item_get_path(item);
931 if (!is_dir_exist(path))
933 filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(uid), NULL);
935 debug_print("trying to fetch cached %s\n", filename);
936 if (is_file_exist(filename)) {
937 /* see whether the local file represents the whole message
938 * or not. As the IMAP server reports size with \r chars,
939 * we have to update the local file (UNIX \n only) size */
940 MsgInfo *msginfo = imap_parse_msg(filename, item);
941 MsgInfo *cached = msgcache_get_msg(item->cache,uid);
942 guint have_size = get_size_with_crs(msginfo);
945 debug_print("message %d has been already %scached (%d/%d).\n", uid,
946 have_size == cached->size ? "fully ":"",
947 have_size, (int)cached->size);
949 if (cached && (cached->size == have_size || !body)) {
950 procmsg_msginfo_free(cached);
951 procmsg_msginfo_free(msginfo);
952 file_strip_crs(filename);
954 } else if (!cached) {
955 debug_print("message not cached, considering file complete\n");
956 procmsg_msginfo_free(msginfo);
957 file_strip_crs(filename);
960 procmsg_msginfo_free(cached);
961 procmsg_msginfo_free(msginfo);
965 session = imap_session_get(folder);
974 debug_print("IMAP fetching messages\n");
975 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
976 NULL, NULL, NULL, NULL, FALSE);
977 if (ok != IMAP_SUCCESS) {
978 g_warning("can't select mailbox %s\n", item->path);
984 debug_print("getting message %d...\n", uid);
985 ok = imap_cmd_fetch(session, (guint32)uid, filename, headers, body);
987 if (ok != IMAP_SUCCESS) {
988 g_warning("can't fetch message %d\n", uid);
995 file_strip_crs(filename);
999 static gint imap_add_msg(Folder *folder, FolderItem *dest,
1000 const gchar *file, MsgFlags *flags)
1004 MsgFileInfo fileinfo;
1006 g_return_val_if_fail(file != NULL, -1);
1008 fileinfo.msginfo = NULL;
1009 fileinfo.file = (gchar *)file;
1010 fileinfo.flags = flags;
1011 file_list.data = &fileinfo;
1012 file_list.next = NULL;
1014 ret = imap_add_msgs(folder, dest, &file_list, NULL);
1018 static gint imap_add_msgs(Folder *folder, FolderItem *dest, GSList *file_list,
1019 GRelation *relation)
1022 IMAPSession *session;
1023 guint32 last_uid = 0;
1025 MsgFileInfo *fileinfo;
1027 gint curnum = 0, total = 0;
1030 g_return_val_if_fail(folder != NULL, -1);
1031 g_return_val_if_fail(dest != NULL, -1);
1032 g_return_val_if_fail(file_list != NULL, -1);
1034 session = imap_session_get(folder);
1039 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
1041 statusbar_print_all(_("Adding messages..."));
1042 total = g_slist_length(file_list);
1043 for (cur = file_list; cur != NULL; cur = cur->next) {
1044 IMAPFlags iflags = 0;
1045 guint32 new_uid = 0;
1046 gchar *real_file = NULL;
1047 gboolean file_is_tmp = FALSE;
1048 fileinfo = (MsgFileInfo *)cur->data;
1050 statusbar_progress_all(curnum, total, 1);
1053 if (fileinfo->flags) {
1054 if (MSG_IS_MARKED(*fileinfo->flags))
1055 iflags |= IMAP_FLAG_FLAGGED;
1056 if (MSG_IS_REPLIED(*fileinfo->flags))
1057 iflags |= IMAP_FLAG_ANSWERED;
1058 if (!MSG_IS_UNREAD(*fileinfo->flags))
1059 iflags |= IMAP_FLAG_SEEN;
1062 if (fileinfo->flags) {
1063 if ((MSG_IS_QUEUED(*fileinfo->flags)
1064 || MSG_IS_DRAFT(*fileinfo->flags))
1065 && !folder_has_parent_of_type(dest, F_QUEUE)
1066 && !folder_has_parent_of_type(dest, F_DRAFT)) {
1067 real_file = get_tmp_file();
1069 if (procmsg_remove_special_headers(
1077 } else if (!(MSG_IS_QUEUED(*fileinfo->flags)
1078 || MSG_IS_DRAFT(*fileinfo->flags))
1079 && (folder_has_parent_of_type(dest, F_QUEUE)
1080 || folder_has_parent_of_type(dest, F_DRAFT))) {
1084 if (real_file == NULL)
1085 real_file = g_strdup(fileinfo->file);
1087 if (folder_has_parent_of_type(dest, F_QUEUE) ||
1088 folder_has_parent_of_type(dest, F_OUTBOX) ||
1089 folder_has_parent_of_type(dest, F_DRAFT) ||
1090 folder_has_parent_of_type(dest, F_TRASH))
1091 iflags |= IMAP_FLAG_SEEN;
1093 ok = imap_cmd_append(session, destdir, real_file, iflags,
1096 if (ok != IMAP_SUCCESS) {
1097 g_warning("can't append message %s\n", real_file);
1099 g_unlink(real_file);
1103 statusbar_progress_all(0,0,0);
1104 statusbar_pop_all();
1107 debug_print("appended new message as %d\n", new_uid);
1108 /* put the local file in the imapcache, so that we don't
1109 * have to fetch it back later. */
1111 gchar *cache_path = folder_item_get_path(dest);
1112 if (!is_dir_exist(cache_path))
1113 make_dir_hier(cache_path);
1114 if (is_dir_exist(cache_path)) {
1115 gchar *cache_file = g_strconcat(
1116 cache_path, G_DIR_SEPARATOR_S,
1117 itos(new_uid), NULL);
1118 copy_file(real_file, cache_file, TRUE);
1119 debug_print("copied to cache: %s\n", cache_file);
1126 if (relation != NULL)
1127 g_relation_insert(relation, fileinfo->msginfo != NULL ?
1128 (gpointer) fileinfo->msginfo : (gpointer) fileinfo,
1129 GINT_TO_POINTER(dest->last_num + 1));
1131 new_uid = dest->last_num+1;
1133 if (last_uid < new_uid)
1136 g_unlink(real_file);
1140 statusbar_progress_all(0,0,0);
1141 statusbar_pop_all();
1143 imap_cmd_expunge(session);
1151 static gint imap_do_copy_msgs(Folder *folder, FolderItem *dest,
1152 MsgInfoList *msglist, GRelation *relation)
1156 GSList *seq_list, *cur;
1158 IMAPSession *session;
1159 gint ok = IMAP_SUCCESS;
1160 GRelation *uid_mapping;
1163 g_return_val_if_fail(folder != NULL, -1);
1164 g_return_val_if_fail(dest != NULL, -1);
1165 g_return_val_if_fail(msglist != NULL, -1);
1167 session = imap_session_get(folder);
1173 msginfo = (MsgInfo *)msglist->data;
1175 src = msginfo->folder;
1177 g_warning("the src folder is identical to the dest.\n");
1182 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
1183 NULL, NULL, NULL, NULL, FALSE);
1184 if (ok != IMAP_SUCCESS) {
1189 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
1190 seq_list = imap_get_lep_set_from_msglist(msglist);
1191 uid_mapping = g_relation_new(2);
1192 g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
1194 statusbar_print_all(_("Copying messages..."));
1195 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
1196 struct mailimap_set * seq_set;
1197 seq_set = cur->data;
1199 debug_print("Copying messages from %s to %s ...\n",
1200 src->path, destdir);
1202 ok = imap_cmd_copy(session, seq_set, destdir, uid_mapping);
1203 if (ok != IMAP_SUCCESS) {
1204 g_relation_destroy(uid_mapping);
1205 imap_lep_set_free(seq_list);
1211 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
1212 MsgInfo *msginfo = (MsgInfo *)cur->data;
1215 tuples = g_relation_select(uid_mapping,
1216 GINT_TO_POINTER(msginfo->msgnum),
1218 if (tuples->len > 0) {
1219 gint num = GPOINTER_TO_INT(g_tuples_index(tuples, 0, 1));
1220 g_relation_insert(relation, msginfo,
1221 GPOINTER_TO_INT(num));
1225 g_relation_insert(relation, msginfo,
1226 GPOINTER_TO_INT(0));
1227 g_tuples_destroy(tuples);
1229 statusbar_pop_all();
1231 g_relation_destroy(uid_mapping);
1232 imap_lep_set_free(seq_list);
1236 IMAP_FOLDER_ITEM(dest)->lastuid = 0;
1237 IMAP_FOLDER_ITEM(dest)->uid_next = 0;
1238 g_slist_free(IMAP_FOLDER_ITEM(dest)->uid_list);
1239 IMAP_FOLDER_ITEM(dest)->uid_list = NULL;
1242 if (ok == IMAP_SUCCESS)
1248 static gint imap_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1252 g_return_val_if_fail(msginfo != NULL, -1);
1254 msglist.data = msginfo;
1255 msglist.next = NULL;
1257 return imap_copy_msgs(folder, dest, &msglist, NULL);
1260 static gint imap_copy_msgs(Folder *folder, FolderItem *dest,
1261 MsgInfoList *msglist, GRelation *relation)
1267 g_return_val_if_fail(folder != NULL, -1);
1268 g_return_val_if_fail(dest != NULL, -1);
1269 g_return_val_if_fail(msglist != NULL, -1);
1271 msginfo = (MsgInfo *)msglist->data;
1272 g_return_val_if_fail(msginfo->folder != NULL, -1);
1274 /* if from/to are the same "type" (with or without extra headers),
1275 * copy them via imap */
1276 if (folder == msginfo->folder->folder &&
1277 !folder_has_parent_of_type(msginfo->folder, F_DRAFT) &&
1278 !folder_has_parent_of_type(msginfo->folder, F_QUEUE) &&
1279 !folder_has_parent_of_type(dest, F_DRAFT) &&
1280 !folder_has_parent_of_type(dest, F_QUEUE)) {
1281 ret = imap_do_copy_msgs(folder, dest, msglist, relation);
1283 } else if (folder == msginfo->folder->folder &&
1284 (folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
1285 folder_has_parent_of_type(msginfo->folder, F_QUEUE)) &&
1286 (folder_has_parent_of_type(dest, F_DRAFT) ||
1287 folder_has_parent_of_type(dest, F_QUEUE))) {
1288 ret = imap_do_copy_msgs(folder, dest, msglist, relation);
1291 /* else reupload them */
1292 file_list = procmsg_get_message_file_list(msglist);
1293 g_return_val_if_fail(file_list != NULL, -1);
1295 ret = imap_add_msgs(folder, dest, file_list, relation);
1296 procmsg_message_file_list_free(file_list);
1302 static gint imap_do_remove_msgs(Folder *folder, FolderItem *dest,
1303 MsgInfoList *msglist, GRelation *relation)
1306 GSList *numlist = NULL, *cur;
1308 IMAPSession *session;
1309 gint ok = IMAP_SUCCESS;
1310 GRelation *uid_mapping;
1312 g_return_val_if_fail(folder != NULL, -1);
1313 g_return_val_if_fail(dest != NULL, -1);
1314 g_return_val_if_fail(msglist != NULL, -1);
1316 session = imap_session_get(folder);
1321 msginfo = (MsgInfo *)msglist->data;
1323 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
1324 NULL, NULL, NULL, NULL, FALSE);
1325 if (ok != IMAP_SUCCESS) {
1330 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
1331 for (cur = msglist; cur; cur = cur->next) {
1332 msginfo = (MsgInfo *)cur->data;
1333 if (!MSG_IS_DELETED(msginfo->flags))
1334 numlist = g_slist_append(numlist, GINT_TO_POINTER(msginfo->msgnum));
1337 uid_mapping = g_relation_new(2);
1338 g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
1340 ok = imap_set_message_flags
1341 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
1342 numlist, IMAP_FLAG_DELETED, TRUE);
1343 if (ok != IMAP_SUCCESS) {
1344 log_warning(_("can't set deleted flags\n"));
1348 ok = imap_cmd_expunge(session);
1349 if (ok != IMAP_SUCCESS) {
1350 log_warning(_("can't expunge\n"));
1355 g_relation_destroy(uid_mapping);
1356 g_slist_free(numlist);
1360 if (ok == IMAP_SUCCESS)
1366 static gint imap_remove_msgs(Folder *folder, FolderItem *dest,
1367 MsgInfoList *msglist, GRelation *relation)
1371 g_return_val_if_fail(folder != NULL, -1);
1372 g_return_val_if_fail(dest != NULL, -1);
1373 if (msglist == NULL)
1376 msginfo = (MsgInfo *)msglist->data;
1377 g_return_val_if_fail(msginfo->folder != NULL, -1);
1379 return imap_do_remove_msgs(folder, dest, msglist, relation);
1382 static gint imap_remove_all_msg(Folder *folder, FolderItem *item)
1384 GSList *list = folder_item_get_msg_list(item);
1385 gint res = imap_remove_msgs(folder, item, list, NULL);
1386 procmsg_msg_list_free(list);
1390 static gboolean imap_is_msg_changed(Folder *folder, FolderItem *item,
1393 /* TODO: properly implement this method */
1397 static gint imap_close(Folder *folder, FolderItem *item)
1402 static gint imap_scan_tree(Folder *folder)
1404 FolderItem *item = NULL;
1405 IMAPSession *session;
1406 gchar *root_folder = NULL;
1408 g_return_val_if_fail(folder != NULL, -1);
1409 g_return_val_if_fail(folder->account != NULL, -1);
1411 session = imap_session_get(folder);
1413 if (!folder->node) {
1414 folder_tree_destroy(folder);
1415 item = folder_item_new(folder, folder->name, NULL);
1416 item->folder = folder;
1417 folder->node = item->node = g_node_new(item);
1423 if (folder->account->imap_dir && *folder->account->imap_dir) {
1428 Xstrdup_a(root_folder, folder->account->imap_dir, {unlock_session();return -1;});
1429 extract_quote(root_folder, '"');
1430 subst_char(root_folder,
1431 imap_get_path_separator(IMAP_FOLDER(folder),
1434 strtailchomp(root_folder, '/');
1435 real_path = imap_get_real_path
1436 (IMAP_FOLDER(folder), root_folder);
1437 debug_print("IMAP root directory: %s\n", real_path);
1439 /* check if root directory exist */
1441 r = imap_threaded_list(session->folder, "", real_path,
1443 if ((r != MAILIMAP_NO_ERROR) || (clist_count(lep_list) == 0)) {
1444 if (!folder->node) {
1445 item = folder_item_new(folder, folder->name, NULL);
1446 item->folder = folder;
1447 folder->node = item->node = g_node_new(item);
1452 mailimap_list_result_free(lep_list);
1458 item = FOLDER_ITEM(folder->node->data);
1459 if (!item || ((item->path || root_folder) &&
1460 strcmp2(item->path, root_folder) != 0)) {
1461 folder_tree_destroy(folder);
1462 item = folder_item_new(folder, folder->name, root_folder);
1463 item->folder = folder;
1464 folder->node = item->node = g_node_new(item);
1467 imap_scan_tree_recursive(session, FOLDER_ITEM(folder->node->data));
1468 imap_create_missing_folders(folder);
1474 static gint imap_scan_tree_recursive(IMAPSession *session, FolderItem *item)
1477 IMAPFolder *imapfolder;
1478 FolderItem *new_item;
1479 GSList *item_list, *cur;
1482 gchar *wildcard_path;
1488 g_return_val_if_fail(item != NULL, -1);
1489 g_return_val_if_fail(item->folder != NULL, -1);
1490 g_return_val_if_fail(item->no_sub == FALSE, -1);
1492 folder = item->folder;
1493 imapfolder = IMAP_FOLDER(folder);
1495 separator = imap_get_path_separator(imapfolder, item->path);
1497 if (folder->ui_func)
1498 folder->ui_func(folder, item, folder->ui_func_data);
1501 wildcard[0] = separator;
1504 real_path = imap_get_real_path(imapfolder, item->path);
1508 real_path = g_strdup("");
1511 Xstrcat_a(wildcard_path, real_path, wildcard,
1512 {g_free(real_path); return IMAP_ERROR;});
1514 r = imap_threaded_list(folder, "", wildcard_path, &lep_list);
1515 if (r != MAILIMAP_NO_ERROR) {
1519 item_list = imap_list_from_lep(imapfolder,
1520 lep_list, real_path, FALSE);
1521 mailimap_list_result_free(lep_list);
1526 node = item->node->children;
1527 while (node != NULL) {
1528 FolderItem *old_item = FOLDER_ITEM(node->data);
1529 GNode *next = node->next;
1532 for (cur = item_list; cur != NULL; cur = cur->next) {
1533 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1534 if (!strcmp2(old_item->path, cur_item->path)) {
1535 new_item = cur_item;
1540 debug_print("folder '%s' not found. removing...\n",
1542 folder_item_remove(old_item);
1544 old_item->no_sub = new_item->no_sub;
1545 old_item->no_select = new_item->no_select;
1546 if (old_item->no_sub == TRUE && node->children) {
1547 debug_print("folder '%s' doesn't have "
1548 "subfolders. removing...\n",
1550 folder_item_remove_children(old_item);
1557 for (cur = item_list; cur != NULL; cur = cur->next) {
1558 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1561 for (node = item->node->children; node != NULL;
1562 node = node->next) {
1563 if (!strcmp2(FOLDER_ITEM(node->data)->path,
1565 new_item = FOLDER_ITEM(node->data);
1566 folder_item_destroy(cur_item);
1572 new_item = cur_item;
1573 debug_print("new folder '%s' found.\n", new_item->path);
1574 folder_item_append(item, new_item);
1577 if (!strcmp(new_item->path, "INBOX")) {
1578 new_item->stype = F_INBOX;
1579 folder->inbox = new_item;
1580 } else if (!folder_item_parent(item) || item->stype == F_INBOX) {
1583 base = g_path_get_basename(new_item->path);
1585 if (!folder->outbox && !g_ascii_strcasecmp(base, "Sent")) {
1586 new_item->stype = F_OUTBOX;
1587 folder->outbox = new_item;
1588 } else if (!folder->draft && !g_ascii_strcasecmp(base, "Drafts")) {
1589 new_item->stype = F_DRAFT;
1590 folder->draft = new_item;
1591 } else if (!folder->queue && !g_ascii_strcasecmp(base, "Queue")) {
1592 new_item->stype = F_QUEUE;
1593 folder->queue = new_item;
1594 } else if (!folder->trash && !g_ascii_strcasecmp(base, "Trash")) {
1595 new_item->stype = F_TRASH;
1596 folder->trash = new_item;
1601 if (new_item->no_sub == FALSE)
1602 imap_scan_tree_recursive(session, new_item);
1605 g_slist_free(item_list);
1607 return IMAP_SUCCESS;
1610 static gint imap_create_tree(Folder *folder)
1612 g_return_val_if_fail(folder != NULL, -1);
1613 g_return_val_if_fail(folder->node != NULL, -1);
1614 g_return_val_if_fail(folder->node->data != NULL, -1);
1615 g_return_val_if_fail(folder->account != NULL, -1);
1617 imap_scan_tree(folder);
1618 imap_create_missing_folders(folder);
1623 static void imap_create_missing_folders(Folder *folder)
1625 g_return_if_fail(folder != NULL);
1628 folder->inbox = imap_create_special_folder
1629 (folder, F_INBOX, "INBOX");
1631 folder->trash = imap_create_special_folder
1632 (folder, F_TRASH, "Trash");
1634 folder->queue = imap_create_special_folder
1635 (folder, F_QUEUE, "Queue");
1636 if (!folder->outbox)
1637 folder->outbox = imap_create_special_folder
1638 (folder, F_OUTBOX, "Sent");
1640 folder->draft = imap_create_special_folder
1641 (folder, F_DRAFT, "Drafts");
1644 static FolderItem *imap_create_special_folder(Folder *folder,
1645 SpecialFolderItemType stype,
1649 FolderItem *new_item;
1651 g_return_val_if_fail(folder != NULL, NULL);
1652 g_return_val_if_fail(folder->node != NULL, NULL);
1653 g_return_val_if_fail(folder->node->data != NULL, NULL);
1654 g_return_val_if_fail(folder->account != NULL, NULL);
1655 g_return_val_if_fail(name != NULL, NULL);
1657 item = FOLDER_ITEM(folder->node->data);
1658 new_item = imap_create_folder(folder, item, name);
1661 g_warning("Can't create '%s'\n", name);
1662 if (!folder->inbox) return NULL;
1664 new_item = imap_create_folder(folder, folder->inbox, name);
1666 g_warning("Can't create '%s' under INBOX\n", name);
1668 new_item->stype = stype;
1670 new_item->stype = stype;
1675 static gchar *imap_folder_get_path(Folder *folder)
1679 g_return_val_if_fail(folder != NULL, NULL);
1680 g_return_val_if_fail(folder->account != NULL, NULL);
1682 folder_path = g_strconcat(get_imap_cache_dir(),
1684 folder->account->recv_server,
1686 folder->account->userid,
1692 static gchar *imap_item_get_path(Folder *folder, FolderItem *item)
1694 gchar *folder_path, *path;
1696 g_return_val_if_fail(folder != NULL, NULL);
1697 g_return_val_if_fail(item != NULL, NULL);
1698 folder_path = imap_folder_get_path(folder);
1700 g_return_val_if_fail(folder_path != NULL, NULL);
1701 if (folder_path[0] == G_DIR_SEPARATOR) {
1703 path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
1706 path = g_strdup(folder_path);
1709 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1710 folder_path, G_DIR_SEPARATOR_S,
1713 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1716 g_free(folder_path);
1721 static FolderItem *imap_create_folder(Folder *folder, FolderItem *parent,
1724 gchar *dirpath, *imap_path;
1725 IMAPSession *session;
1726 FolderItem *new_item;
1731 gboolean no_select = FALSE, no_sub = FALSE;
1733 g_return_val_if_fail(folder != NULL, NULL);
1734 g_return_val_if_fail(folder->account != NULL, NULL);
1735 g_return_val_if_fail(parent != NULL, NULL);
1736 g_return_val_if_fail(name != NULL, NULL);
1738 session = imap_session_get(folder);
1744 if (!folder_item_parent(parent) && strcmp(name, "INBOX") == 0) {
1745 dirpath = g_strdup(name);
1746 }else if (parent->path)
1747 dirpath = g_strconcat(parent->path, "/", name, NULL);
1748 else if ((p = strchr(name, '/')) != NULL && *(p + 1) != '\0')
1749 dirpath = g_strdup(name);
1750 else if (folder->account->imap_dir && *folder->account->imap_dir) {
1753 Xstrdup_a(imap_dir, folder->account->imap_dir, {unlock_session();return NULL;});
1754 strtailchomp(imap_dir, '/');
1755 dirpath = g_strconcat(imap_dir, "/", name, NULL);
1757 dirpath = g_strdup(name);
1761 /* keep trailing directory separator to create a folder that contains
1763 imap_path = imap_utf8_to_modified_utf7(dirpath);
1765 strtailchomp(dirpath, '/');
1766 Xstrdup_a(new_name, name, {
1771 separator = imap_get_path_separator(IMAP_FOLDER(folder), imap_path);
1772 imap_path_separator_subst(imap_path, separator);
1773 /* remove trailing / for display */
1774 strtailchomp(new_name, '/');
1776 if (strcmp(dirpath, "INBOX") != 0) {
1778 gboolean exist = FALSE;
1782 argbuf = g_ptr_array_new();
1783 r = imap_threaded_list(folder, "", imap_path, &lep_list);
1784 if (r != MAILIMAP_NO_ERROR) {
1785 log_warning(_("can't create mailbox: LIST failed\n"));
1788 ptr_array_free_strings(argbuf);
1789 g_ptr_array_free(argbuf, TRUE);
1794 if (clist_count(lep_list) > 0)
1796 mailimap_list_result_free(lep_list);
1799 ok = imap_cmd_create(session, imap_path);
1800 if (ok != IMAP_SUCCESS) {
1801 log_warning(_("can't create mailbox\n"));
1807 r = imap_threaded_list(folder, "", imap_path, &lep_list);
1808 if (r == MAILIMAP_NO_ERROR) {
1809 GSList *item_list = imap_list_from_lep(IMAP_FOLDER(folder),
1810 lep_list, dirpath, TRUE);
1812 FolderItem *cur_item = FOLDER_ITEM(item_list->data);
1813 no_select = cur_item->no_select;
1814 no_sub = cur_item->no_sub;
1815 g_slist_free(item_list);
1817 mailimap_list_result_free(lep_list);
1824 /* just get flags */
1825 r = imap_threaded_list(folder, "", "INBOX", &lep_list);
1826 if (r == MAILIMAP_NO_ERROR) {
1827 GSList *item_list = imap_list_from_lep(IMAP_FOLDER(folder),
1828 lep_list, dirpath, TRUE);
1830 FolderItem *cur_item = FOLDER_ITEM(item_list->data);
1831 no_select = cur_item->no_select;
1832 no_sub = cur_item->no_sub;
1833 g_slist_free(item_list);
1835 mailimap_list_result_free(lep_list);
1839 new_item = folder_item_new(folder, new_name, dirpath);
1840 new_item->no_select = no_select;
1841 new_item->no_sub = no_sub;
1842 folder_item_append(parent, new_item);
1846 dirpath = folder_item_get_path(new_item);
1847 if (!is_dir_exist(dirpath))
1848 make_dir_hier(dirpath);
1854 static gint imap_rename_folder(Folder *folder, FolderItem *item,
1859 gchar *real_oldpath;
1860 gchar *real_newpath;
1862 gchar *old_cache_dir;
1863 gchar *new_cache_dir;
1864 IMAPSession *session;
1867 gint exists, recent, unseen;
1868 guint32 uid_validity;
1870 g_return_val_if_fail(folder != NULL, -1);
1871 g_return_val_if_fail(item != NULL, -1);
1872 g_return_val_if_fail(item->path != NULL, -1);
1873 g_return_val_if_fail(name != NULL, -1);
1875 session = imap_session_get(folder);
1881 if (strchr(name, imap_get_path_separator(IMAP_FOLDER(folder), item->path)) != NULL) {
1882 g_warning(_("New folder name must not contain the namespace "
1888 real_oldpath = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1890 g_free(session->mbox);
1891 session->mbox = NULL;
1892 ok = imap_cmd_examine(session, "INBOX",
1893 &exists, &recent, &unseen, &uid_validity, FALSE);
1894 if (ok != IMAP_SUCCESS) {
1895 g_free(real_oldpath);
1900 separator = imap_get_path_separator(IMAP_FOLDER(folder), item->path);
1901 if (strchr(item->path, G_DIR_SEPARATOR)) {
1902 dirpath = g_path_get_dirname(item->path);
1903 newpath = g_strconcat(dirpath, G_DIR_SEPARATOR_S, name, NULL);
1906 newpath = g_strdup(name);
1908 real_newpath = imap_utf8_to_modified_utf7(newpath);
1909 imap_path_separator_subst(real_newpath, separator);
1911 ok = imap_cmd_rename(session, real_oldpath, real_newpath);
1912 if (ok != IMAP_SUCCESS) {
1913 log_warning(_("can't rename mailbox: %s to %s\n"),
1914 real_oldpath, real_newpath);
1915 g_free(real_oldpath);
1917 g_free(real_newpath);
1923 item->name = g_strdup(name);
1925 old_cache_dir = folder_item_get_path(item);
1927 paths[0] = g_strdup(item->path);
1929 g_node_traverse(item->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1930 imap_rename_folder_func, paths);
1932 if (is_dir_exist(old_cache_dir)) {
1933 new_cache_dir = folder_item_get_path(item);
1934 if (rename(old_cache_dir, new_cache_dir) < 0) {
1935 FILE_OP_ERROR(old_cache_dir, "rename");
1937 g_free(new_cache_dir);
1940 g_free(old_cache_dir);
1943 g_free(real_oldpath);
1944 g_free(real_newpath);
1949 static gint imap_remove_folder_real(Folder *folder, FolderItem *item)
1952 IMAPSession *session;
1956 g_return_val_if_fail(folder != NULL, -1);
1957 g_return_val_if_fail(item != NULL, -1);
1958 g_return_val_if_fail(item->path != NULL, -1);
1960 session = imap_session_get(folder);
1965 path = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1967 ok = imap_cmd_delete(session, path);
1968 if (ok != IMAP_SUCCESS) {
1969 gchar *tmp = g_strdup_printf("%s%c", path,
1970 imap_get_path_separator(IMAP_FOLDER(folder), path));
1973 ok = imap_cmd_delete(session, path);
1976 if (ok != IMAP_SUCCESS) {
1977 log_warning(_("can't delete mailbox\n"));
1984 cache_dir = folder_item_get_path(item);
1985 if (is_dir_exist(cache_dir) && remove_dir_recursive(cache_dir) < 0)
1986 g_warning("can't remove directory '%s'\n", cache_dir);
1988 folder_item_remove(item);
1993 static gint imap_remove_folder(Folder *folder, FolderItem *item)
1997 g_return_val_if_fail(item != NULL, -1);
1998 g_return_val_if_fail(item->folder != NULL, -1);
1999 g_return_val_if_fail(item->node != NULL, -1);
2001 node = item->node->children;
2002 while (node != NULL) {
2004 if (imap_remove_folder(folder, FOLDER_ITEM(node->data)) < 0)
2008 debug_print("IMAP removing %s\n", item->path);
2010 if (imap_remove_all_msg(folder, item) < 0)
2012 return imap_remove_folder_real(folder, item);
2015 typedef struct _uncached_data {
2016 IMAPSession *session;
2018 MsgNumberList *numlist;
2024 static void *imap_get_uncached_messages_thread(void *data)
2026 uncached_data *stuff = (uncached_data *)data;
2027 IMAPSession *session = stuff->session;
2028 FolderItem *item = stuff->item;
2029 MsgNumberList *numlist = stuff->numlist;
2031 GSList *newlist = NULL;
2032 GSList *llast = NULL;
2033 GSList *seq_list, *cur;
2035 debug_print("uncached_messages\n");
2037 if (session == NULL || item == NULL || item->folder == NULL
2038 || FOLDER_CLASS(item->folder) != &imap_class) {
2043 seq_list = imap_get_lep_set_from_numlist(numlist);
2044 debug_print("get msgs info\n");
2045 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
2046 struct mailimap_set * imapset;
2052 imapset = cur->data;
2054 r = imap_threaded_fetch_env(session->folder,
2055 imapset, &env_list);
2056 if (r != MAILIMAP_NO_ERROR)
2060 for(i = 0 ; i < carray_count(env_list) ; i ++) {
2061 struct imap_fetch_env_info * info;
2064 info = carray_get(env_list, i);
2065 msginfo = imap_envelope_from_lep(info, item);
2066 if (msginfo == NULL)
2068 msginfo->folder = item;
2070 llast = newlist = g_slist_append(newlist, msginfo);
2072 llast = g_slist_append(llast, msginfo);
2073 llast = llast->next;
2078 imap_fetch_env_free(env_list);
2081 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
2082 struct mailimap_set * imapset;
2084 imapset = cur->data;
2085 mailimap_set_free(imapset);
2088 session_set_access_time(SESSION(session));
2093 #define MAX_MSG_NUM 50
2095 static GSList *imap_get_uncached_messages(IMAPSession *session,
2097 MsgNumberList *numlist)
2099 GSList *result = NULL;
2101 uncached_data *data = g_new0(uncached_data, 1);
2106 data->total = g_slist_length(numlist);
2107 debug_print("messages list : %i\n", data->total);
2109 while (cur != NULL) {
2110 GSList * partial_result;
2118 while (count < MAX_MSG_NUM) {
2123 if (newlist == NULL)
2124 llast = newlist = g_slist_append(newlist, p);
2126 llast = g_slist_append(llast, p);
2127 llast = llast->next;
2137 data->session = session;
2139 data->numlist = newlist;
2142 if (prefs_common.work_offline && !inc_offline_should_override()) {
2148 (GSList *)imap_get_uncached_messages_thread(data);
2150 statusbar_progress_all(data->cur,data->total, 1);
2152 g_slist_free(newlist);
2154 result = g_slist_concat(result, partial_result);
2158 statusbar_progress_all(0,0,0);
2159 statusbar_pop_all();
2164 static void imap_delete_all_cached_messages(FolderItem *item)
2168 g_return_if_fail(item != NULL);
2169 g_return_if_fail(item->folder != NULL);
2170 g_return_if_fail(FOLDER_CLASS(item->folder) == &imap_class);
2172 debug_print("Deleting all cached messages...\n");
2174 dir = folder_item_get_path(item);
2175 if (is_dir_exist(dir))
2176 remove_all_numbered_files(dir);
2179 debug_print("done.\n");
2182 static IMAPNameSpace *imap_find_namespace_from_list(GList *ns_list,
2185 IMAPNameSpace *namespace = NULL;
2186 gchar *tmp_path, *name;
2188 if (!path) path = "";
2190 for (; ns_list != NULL; ns_list = ns_list->next) {
2191 IMAPNameSpace *tmp_ns = ns_list->data;
2193 Xstrcat_a(tmp_path, path, "/", return namespace);
2194 Xstrdup_a(name, tmp_ns->name, return namespace);
2195 if (tmp_ns->separator && tmp_ns->separator != '/') {
2196 subst_char(tmp_path, tmp_ns->separator, '/');
2197 subst_char(name, tmp_ns->separator, '/');
2199 if (strncmp(tmp_path, name, strlen(name)) == 0)
2206 static IMAPNameSpace *imap_find_namespace(IMAPFolder *folder,
2209 IMAPNameSpace *namespace;
2211 g_return_val_if_fail(folder != NULL, NULL);
2213 namespace = imap_find_namespace_from_list(folder->ns_personal, path);
2214 if (namespace) return namespace;
2215 namespace = imap_find_namespace_from_list(folder->ns_others, path);
2216 if (namespace) return namespace;
2217 namespace = imap_find_namespace_from_list(folder->ns_shared, path);
2218 if (namespace) return namespace;
2224 static gchar imap_get_path_separator(IMAPFolder *folder, const gchar *path)
2226 IMAPNameSpace *namespace;
2227 gchar separator = '/';
2229 if (folder->last_seen_separator == 0) {
2231 int r = imap_threaded_list((Folder *)folder, "", "", &lep_list);
2232 if (r != MAILIMAP_NO_ERROR) {
2233 log_warning(_("LIST failed\n"));
2237 if (clist_count(lep_list) > 0) {
2238 clistiter * iter = clist_begin(lep_list);
2239 struct mailimap_mailbox_list * mb;
2240 mb = clist_content(iter);
2242 folder->last_seen_separator = mb->mb_delimiter;
2243 debug_print("got separator: %c\n", folder->last_seen_separator);
2245 mailimap_list_result_free(lep_list);
2248 if (folder->last_seen_separator != 0) {
2249 debug_print("using separator: %c\n", folder->last_seen_separator);
2250 return folder->last_seen_separator;
2253 namespace = imap_find_namespace(folder, path);
2254 if (namespace && namespace->separator)
2255 separator = namespace->separator;
2260 static gchar *imap_get_real_path(IMAPFolder *folder, const gchar *path)
2265 g_return_val_if_fail(folder != NULL, NULL);
2266 g_return_val_if_fail(path != NULL, NULL);
2268 real_path = imap_utf8_to_modified_utf7(path);
2269 separator = imap_get_path_separator(folder, path);
2270 imap_path_separator_subst(real_path, separator);
2275 static gint imap_set_message_flags(IMAPSession *session,
2276 MsgNumberList *numlist,
2284 seq_list = imap_get_lep_set_from_numlist(numlist);
2286 for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) {
2287 struct mailimap_set * imapset;
2289 imapset = cur->data;
2291 ok = imap_cmd_store(session, imapset,
2295 imap_lep_set_free(seq_list);
2297 return IMAP_SUCCESS;
2300 typedef struct _select_data {
2301 IMAPSession *session;
2306 guint32 *uid_validity;
2310 static gint imap_select(IMAPSession *session, IMAPFolder *folder,
2312 gint *exists, gint *recent, gint *unseen,
2313 guint32 *uid_validity, gboolean block)
2317 gint exists_, recent_, unseen_;
2318 guint32 uid_validity_;
2320 if (!exists && !recent && !unseen && !uid_validity) {
2321 if (session->mbox && strcmp(session->mbox, path) == 0)
2322 return IMAP_SUCCESS;
2331 uid_validity = &uid_validity_;
2333 g_free(session->mbox);
2334 session->mbox = NULL;
2336 real_path = imap_get_real_path(folder, path);
2338 ok = imap_cmd_select(session, real_path,
2339 exists, recent, unseen, uid_validity, block);
2340 if (ok != IMAP_SUCCESS)
2341 log_warning(_("can't select folder: %s\n"), real_path);
2343 session->mbox = g_strdup(path);
2344 session->folder_content_changed = FALSE;
2351 static gint imap_status(IMAPSession *session, IMAPFolder *folder,
2352 const gchar *path, IMAPFolderItem *item,
2354 guint32 *uid_next, guint32 *uid_validity,
2355 gint *unseen, gboolean block)
2359 struct mailimap_mailbox_data_status * data_status;
2364 real_path = imap_get_real_path(folder, path);
2378 r = imap_threaded_status(FOLDER(folder), real_path,
2379 &data_status, mask);
2382 if (r != MAILIMAP_NO_ERROR) {
2383 debug_print("status err %d\n", r);
2387 if (data_status->st_info_list == NULL) {
2388 mailimap_mailbox_data_status_free(data_status);
2389 debug_print("status->st_info_list == NULL\n");
2394 for(iter = clist_begin(data_status->st_info_list) ; iter != NULL ;
2395 iter = clist_next(iter)) {
2396 struct mailimap_status_info * info;
2398 info = clist_content(iter);
2399 switch (info->st_att) {
2400 case MAILIMAP_STATUS_ATT_MESSAGES:
2401 * messages = info->st_value;
2402 got_values |= 1 << 0;
2405 case MAILIMAP_STATUS_ATT_UIDNEXT:
2406 * uid_next = info->st_value;
2407 got_values |= 1 << 2;
2410 case MAILIMAP_STATUS_ATT_UIDVALIDITY:
2411 * uid_validity = info->st_value;
2412 got_values |= 1 << 3;
2415 case MAILIMAP_STATUS_ATT_UNSEEN:
2416 * unseen = info->st_value;
2417 got_values |= 1 << 4;
2421 mailimap_mailbox_data_status_free(data_status);
2423 if (got_values != mask) {
2424 debug_print("status: incomplete values received (%d)\n", got_values);
2427 return IMAP_SUCCESS;
2430 static void imap_free_capabilities(IMAPSession *session)
2432 slist_free_strings(session->capability);
2433 g_slist_free(session->capability);
2434 session->capability = NULL;
2437 /* low-level IMAP4rev1 commands */
2440 static gint imap_cmd_authenticate(IMAPSession *session, const gchar *user,
2441 const gchar *pass, IMAPAuthType type)
2448 gchar hexdigest[33];
2452 auth_type = "CRAM-MD5";
2454 imap_gen_send(session, "AUTHENTICATE %s", auth_type);
2455 ok = imap_gen_recv(session, &buf);
2456 if (ok != IMAP_SUCCESS || buf[0] != '+' || buf[1] != ' ') {
2461 challenge = g_malloc(strlen(buf + 2) + 1);
2462 challenge_len = base64_decode(challenge, buf + 2, -1);
2463 challenge[challenge_len] = '\0';
2466 md5_hex_hmac(hexdigest, challenge, challenge_len, pass, strlen(pass));
2469 response = g_strdup_printf("%s %s", user, hexdigest);
2470 response64 = g_malloc((strlen(response) + 3) * 2 + 1);
2471 base64_encode(response64, response, strlen(response));
2474 sock_puts(SESSION(session)->sock, response64);
2475 ok = imap_cmd_ok(session, NULL);
2476 if (ok != IMAP_SUCCESS)
2477 log_warning(_("IMAP4 authentication failed.\n"));
2483 static gint imap_cmd_login(IMAPSession *session,
2484 const gchar *user, const gchar *pass,
2490 log_print("IMAP4> Logging %s to %s using %s\n",
2492 SESSION(session)->server,
2494 r = imap_threaded_login(session->folder, user, pass, type);
2495 if (r != MAILIMAP_NO_ERROR) {
2496 log_error("IMAP4< Error logging in to %s\n",
2497 SESSION(session)->server);
2505 static gint imap_cmd_logout(IMAPSession *session)
2507 imap_threaded_disconnect(session->folder);
2509 return IMAP_SUCCESS;
2512 static gint imap_cmd_noop(IMAPSession *session)
2515 unsigned int exists;
2517 r = imap_threaded_noop(session->folder, &exists);
2518 if (r != MAILIMAP_NO_ERROR) {
2519 debug_print("noop err %d\n", r);
2522 session->exists = exists;
2523 session_set_access_time(SESSION(session));
2525 return IMAP_SUCCESS;
2529 static gint imap_cmd_starttls(IMAPSession *session)
2533 r = imap_threaded_starttls(session->folder,
2534 SESSION(session)->server, SESSION(session)->port);
2535 if (r != MAILIMAP_NO_ERROR) {
2536 debug_print("starttls err %d\n", r);
2539 return IMAP_SUCCESS;
2543 static gint imap_cmd_select(IMAPSession *session, const gchar *folder,
2544 gint *exists, gint *recent, gint *unseen,
2545 guint32 *uid_validity, gboolean block)
2549 r = imap_threaded_select(session->folder, folder,
2550 exists, recent, unseen, uid_validity);
2551 if (r != MAILIMAP_NO_ERROR) {
2552 debug_print("select err %d\n", r);
2555 return IMAP_SUCCESS;
2558 static gint imap_cmd_examine(IMAPSession *session, const gchar *folder,
2559 gint *exists, gint *recent, gint *unseen,
2560 guint32 *uid_validity, gboolean block)
2564 r = imap_threaded_examine(session->folder, folder,
2565 exists, recent, unseen, uid_validity);
2566 if (r != MAILIMAP_NO_ERROR) {
2567 debug_print("examine err %d\n", r);
2571 return IMAP_SUCCESS;
2574 static gint imap_cmd_create(IMAPSession *session, const gchar *folder)
2578 r = imap_threaded_create(session->folder, folder);
2579 if (r != MAILIMAP_NO_ERROR) {
2584 return IMAP_SUCCESS;
2587 static gint imap_cmd_rename(IMAPSession *session, const gchar *old_folder,
2588 const gchar *new_folder)
2592 r = imap_threaded_rename(session->folder, old_folder,
2594 if (r != MAILIMAP_NO_ERROR) {
2599 return IMAP_SUCCESS;
2602 static gint imap_cmd_delete(IMAPSession *session, const gchar *folder)
2607 r = imap_threaded_delete(session->folder, folder);
2608 if (r != MAILIMAP_NO_ERROR) {
2613 return IMAP_SUCCESS;
2616 typedef struct _fetch_data {
2617 IMAPSession *session;
2619 const gchar *filename;
2625 static void *imap_cmd_fetch_thread(void *data)
2627 fetch_data *stuff = (fetch_data *)data;
2628 IMAPSession *session = stuff->session;
2629 guint32 uid = stuff->uid;
2630 const gchar *filename = stuff->filename;
2634 r = imap_threaded_fetch_content(session->folder,
2638 r = imap_threaded_fetch_content(session->folder,
2641 if (r != MAILIMAP_NO_ERROR) {
2642 debug_print("fetch err %d\n", r);
2643 return GINT_TO_POINTER(IMAP_ERROR);
2645 return GINT_TO_POINTER(IMAP_SUCCESS);
2648 static gint imap_cmd_fetch(IMAPSession *session, guint32 uid,
2649 const gchar *filename, gboolean headers,
2652 fetch_data *data = g_new0(fetch_data, 1);
2655 data->session = session;
2657 data->filename = filename;
2658 data->headers = headers;
2661 if (prefs_common.work_offline && !inc_offline_should_override()) {
2665 statusbar_print_all(_("Fetching message..."));
2666 result = GPOINTER_TO_INT(imap_cmd_fetch_thread(data));
2667 statusbar_pop_all();
2673 static gint imap_cmd_append(IMAPSession *session, const gchar *destfolder,
2674 const gchar *file, IMAPFlags flags,
2677 struct mailimap_flag_list * flag_list;
2680 g_return_val_if_fail(file != NULL, IMAP_ERROR);
2682 flag_list = imap_flag_to_lep(flags);
2683 r = imap_threaded_append(session->folder, destfolder,
2684 file, flag_list, new_uid);
2685 mailimap_flag_list_free(flag_list);
2687 if (r != MAILIMAP_NO_ERROR) {
2688 debug_print("append err %d\n", r);
2691 return IMAP_SUCCESS;
2694 static gint imap_cmd_copy(IMAPSession *session, struct mailimap_set * set,
2695 const gchar *destfolder, GRelation *uid_mapping)
2699 g_return_val_if_fail(session != NULL, IMAP_ERROR);
2700 g_return_val_if_fail(set != NULL, IMAP_ERROR);
2701 g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
2703 r = imap_threaded_copy(session->folder, set, destfolder);
2704 if (r != MAILIMAP_NO_ERROR) {
2709 return IMAP_SUCCESS;
2712 static gint imap_cmd_store(IMAPSession *session, struct mailimap_set * set,
2713 IMAPFlags flags, int do_add)
2716 struct mailimap_flag_list * flag_list;
2717 struct mailimap_store_att_flags * store_att_flags;
2719 flag_list = imap_flag_to_lep(flags);
2723 mailimap_store_att_flags_new_add_flags_silent(flag_list);
2726 mailimap_store_att_flags_new_remove_flags_silent(flag_list);
2728 r = imap_threaded_store(session->folder, set, store_att_flags);
2729 mailimap_store_att_flags_free(store_att_flags);
2730 if (r != MAILIMAP_NO_ERROR) {
2735 return IMAP_SUCCESS;
2738 static gint imap_cmd_expunge(IMAPSession *session)
2742 if (prefs_common.work_offline && !inc_offline_should_override()) {
2746 r = imap_threaded_expunge(session->folder);
2747 if (r != MAILIMAP_NO_ERROR) {
2752 return IMAP_SUCCESS;
2755 static void imap_path_separator_subst(gchar *str, gchar separator)
2758 gboolean in_escape = FALSE;
2760 if (!separator || separator == '/') return;
2762 for (p = str; *p != '\0'; p++) {
2763 if (*p == '/' && !in_escape)
2765 else if (*p == '&' && *(p + 1) != '-' && !in_escape)
2767 else if (*p == '-' && in_escape)
2772 static gchar *imap_modified_utf7_to_utf8(const gchar *mutf7_str)
2774 static iconv_t cd = (iconv_t)-1;
2775 static gboolean iconv_ok = TRUE;
2778 size_t norm_utf7_len;
2780 gchar *to_str, *to_p;
2782 gboolean in_escape = FALSE;
2784 if (!iconv_ok) return g_strdup(mutf7_str);
2786 if (cd == (iconv_t)-1) {
2787 cd = iconv_open(CS_INTERNAL, CS_UTF_7);
2788 if (cd == (iconv_t)-1) {
2789 g_warning("iconv cannot convert UTF-7 to %s\n",
2792 return g_strdup(mutf7_str);
2796 /* modified UTF-7 to normal UTF-7 conversion */
2797 norm_utf7 = g_string_new(NULL);
2799 for (p = mutf7_str; *p != '\0'; p++) {
2800 /* replace: '&' -> '+',
2802 escaped ',' -> '/' */
2803 if (!in_escape && *p == '&') {
2804 if (*(p + 1) != '-') {
2805 g_string_append_c(norm_utf7, '+');
2808 g_string_append_c(norm_utf7, '&');
2811 } else if (in_escape && *p == ',') {
2812 g_string_append_c(norm_utf7, '/');
2813 } else if (in_escape && *p == '-') {
2814 g_string_append_c(norm_utf7, '-');
2817 g_string_append_c(norm_utf7, *p);
2821 norm_utf7_p = norm_utf7->str;
2822 norm_utf7_len = norm_utf7->len;
2823 to_len = strlen(mutf7_str) * 5;
2824 to_p = to_str = g_malloc(to_len + 1);
2826 if (iconv(cd, (ICONV_CONST gchar **)&norm_utf7_p, &norm_utf7_len,
2827 &to_p, &to_len) == -1) {
2828 g_warning(_("iconv cannot convert UTF-7 to %s\n"),
2829 conv_get_locale_charset_str());
2830 g_string_free(norm_utf7, TRUE);
2832 return g_strdup(mutf7_str);
2835 /* second iconv() call for flushing */
2836 iconv(cd, NULL, NULL, &to_p, &to_len);
2837 g_string_free(norm_utf7, TRUE);
2843 static gchar *imap_utf8_to_modified_utf7(const gchar *from)
2845 static iconv_t cd = (iconv_t)-1;
2846 static gboolean iconv_ok = TRUE;
2847 gchar *norm_utf7, *norm_utf7_p;
2848 size_t from_len, norm_utf7_len;
2850 gchar *from_tmp, *to, *p;
2851 gboolean in_escape = FALSE;
2853 if (!iconv_ok) return g_strdup(from);
2855 if (cd == (iconv_t)-1) {
2856 cd = iconv_open(CS_UTF_7, CS_INTERNAL);
2857 if (cd == (iconv_t)-1) {
2858 g_warning(_("iconv cannot convert %s to UTF-7\n"),
2861 return g_strdup(from);
2865 /* UTF-8 to normal UTF-7 conversion */
2866 Xstrdup_a(from_tmp, from, return g_strdup(from));
2867 from_len = strlen(from);
2868 norm_utf7_len = from_len * 5;
2869 Xalloca(norm_utf7, norm_utf7_len + 1, return g_strdup(from));
2870 norm_utf7_p = norm_utf7;
2872 #define IS_PRINT(ch) (isprint(ch) && IS_ASCII(ch))
2874 while (from_len > 0) {
2875 if (*from_tmp == '+') {
2876 *norm_utf7_p++ = '+';
2877 *norm_utf7_p++ = '-';
2881 } else if (IS_PRINT(*(guchar *)from_tmp)) {
2882 /* printable ascii char */
2883 *norm_utf7_p = *from_tmp;
2889 size_t conv_len = 0;
2891 /* unprintable char: convert to UTF-7 */
2893 while (!IS_PRINT(*(guchar *)p) && conv_len < from_len) {
2894 conv_len += g_utf8_skip[*(guchar *)p];
2895 p += g_utf8_skip[*(guchar *)p];
2898 from_len -= conv_len;
2899 if (iconv(cd, (ICONV_CONST gchar **)&from_tmp,
2901 &norm_utf7_p, &norm_utf7_len) == -1) {
2902 g_warning(_("iconv cannot convert UTF-8 to UTF-7\n"));
2903 return g_strdup(from);
2906 /* second iconv() call for flushing */
2907 iconv(cd, NULL, NULL, &norm_utf7_p, &norm_utf7_len);
2913 *norm_utf7_p = '\0';
2914 to_str = g_string_new(NULL);
2915 for (p = norm_utf7; p < norm_utf7_p; p++) {
2916 /* replace: '&' -> "&-",
2919 BASE64 '/' -> ',' */
2920 if (!in_escape && *p == '&') {
2921 g_string_append(to_str, "&-");
2922 } else if (!in_escape && *p == '+') {
2923 if (*(p + 1) == '-') {
2924 g_string_append_c(to_str, '+');
2927 g_string_append_c(to_str, '&');
2930 } else if (in_escape && *p == '/') {
2931 g_string_append_c(to_str, ',');
2932 } else if (in_escape && *p == '-') {
2933 g_string_append_c(to_str, '-');
2936 g_string_append_c(to_str, *p);
2942 g_string_append_c(to_str, '-');
2946 g_string_free(to_str, FALSE);
2951 static gboolean imap_rename_folder_func(GNode *node, gpointer data)
2953 FolderItem *item = node->data;
2954 gchar **paths = data;
2955 const gchar *oldpath = paths[0];
2956 const gchar *newpath = paths[1];
2958 gchar *new_itempath;
2961 oldpathlen = strlen(oldpath);
2962 if (strncmp(oldpath, item->path, oldpathlen) != 0) {
2963 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
2967 base = item->path + oldpathlen;
2968 while (*base == G_DIR_SEPARATOR) base++;
2970 new_itempath = g_strdup(newpath);
2972 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
2975 item->path = new_itempath;
2980 typedef struct _get_list_uid_data {
2982 IMAPSession *session;
2983 IMAPFolderItem *item;
2984 GSList **msgnum_list;
2986 } get_list_uid_data;
2988 static void *get_list_of_uids_thread(void *data)
2990 get_list_uid_data *stuff = (get_list_uid_data *)data;
2991 Folder *folder = stuff->folder;
2992 IMAPFolderItem *item = stuff->item;
2993 GSList **msgnum_list = stuff->msgnum_list;
2994 gint ok, nummsgs = 0, lastuid_old;
2995 IMAPSession *session;
2996 GSList *uidlist, *elem;
2997 struct mailimap_set * set;
2998 clist * lep_uidlist;
3001 session = stuff->session;
3002 if (session == NULL) {
3004 return GINT_TO_POINTER(-1);
3006 /* no session locking here, it's already locked by caller */
3007 ok = imap_select(session, IMAP_FOLDER(folder), item->item.path,
3008 NULL, NULL, NULL, NULL, TRUE);
3009 if (ok != IMAP_SUCCESS) {
3011 return GINT_TO_POINTER(-1);
3016 set = mailimap_set_new_interval(item->lastuid + 1, 0);
3018 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SIMPLE, set,
3020 mailimap_set_free(set);
3022 if (r == MAILIMAP_NO_ERROR) {
3023 GSList * fetchuid_list;
3026 imap_uid_list_from_lep(lep_uidlist);
3027 mailimap_search_result_free(lep_uidlist);
3029 uidlist = g_slist_concat(fetchuid_list, uidlist);
3032 GSList * fetchuid_list;
3033 carray * lep_uidtab;
3035 r = imap_threaded_fetch_uid(folder, item->lastuid + 1,
3037 if (r == MAILIMAP_NO_ERROR) {
3039 imap_uid_list_from_lep_tab(lep_uidtab);
3040 imap_fetch_uid_list_free(lep_uidtab);
3041 uidlist = g_slist_concat(fetchuid_list, uidlist);
3045 lastuid_old = item->lastuid;
3046 *msgnum_list = g_slist_copy(item->uid_list);
3047 nummsgs = g_slist_length(*msgnum_list);
3048 debug_print("Got %d uids from cache\n", g_slist_length(item->uid_list));
3050 for (elem = uidlist; elem != NULL; elem = g_slist_next(elem)) {
3053 msgnum = GPOINTER_TO_INT(elem->data);
3054 if (msgnum > lastuid_old) {
3055 *msgnum_list = g_slist_prepend(*msgnum_list, GINT_TO_POINTER(msgnum));
3056 item->uid_list = g_slist_prepend(item->uid_list, GINT_TO_POINTER(msgnum));
3059 if(msgnum > item->lastuid)
3060 item->lastuid = msgnum;
3063 g_slist_free(uidlist);
3065 return GINT_TO_POINTER(nummsgs);
3068 static gint get_list_of_uids(IMAPSession *session, Folder *folder, IMAPFolderItem *item, GSList **msgnum_list)
3071 get_list_uid_data *data = g_new0(get_list_uid_data, 1);
3073 data->folder = folder;
3075 data->msgnum_list = msgnum_list;
3076 data->session = session;
3077 if (prefs_common.work_offline && !inc_offline_should_override()) {
3082 result = GPOINTER_TO_INT(get_list_of_uids_thread(data));
3088 gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list, gboolean *old_uids_valid)
3090 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3091 IMAPSession *session;
3092 gint ok, nummsgs = 0, exists, uid_val, uid_next;
3093 GSList *uidlist = NULL;
3095 gboolean selected_folder;
3097 debug_print("get_num_list\n");
3099 g_return_val_if_fail(folder != NULL, -1);
3100 g_return_val_if_fail(item != NULL, -1);
3101 g_return_val_if_fail(item->item.path != NULL, -1);
3102 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3103 g_return_val_if_fail(folder->account != NULL, -1);
3105 session = imap_session_get(folder);
3106 g_return_val_if_fail(session != NULL, -1);
3108 statusbar_print_all("Scanning %s...\n", FOLDER_ITEM(item)->path
3109 ? FOLDER_ITEM(item)->path:"");
3111 selected_folder = (session->mbox != NULL) &&
3112 (!strcmp(session->mbox, item->item.path));
3113 if (selected_folder && time(NULL) - item->use_cache < 2) {
3114 ok = imap_cmd_noop(session);
3115 if (ok != IMAP_SUCCESS) {
3116 debug_print("disconnected!\n");
3117 session = imap_reconnect_if_possible(folder, session);
3118 if (session == NULL) {
3119 statusbar_pop_all();
3124 exists = session->exists;
3126 *old_uids_valid = TRUE;
3128 if (item->use_cache && time(NULL) - item->use_cache < 2) {
3129 exists = item->c_messages;
3130 uid_next = item->c_uid_next;
3131 uid_val = item->c_uid_validity;
3133 debug_print("using cache %d %d %d\n", exists, uid_next, uid_val);
3135 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path, item,
3136 &exists, &uid_next, &uid_val, NULL, FALSE);
3138 item->item.last_num = uid_next - 1;
3140 item->use_cache = (time_t)0;
3141 if (ok != IMAP_SUCCESS) {
3142 statusbar_pop_all();
3146 if(item->item.mtime == uid_val)
3147 *old_uids_valid = TRUE;
3149 *old_uids_valid = FALSE;
3151 debug_print("Freeing imap uid cache\n");
3153 g_slist_free(item->uid_list);
3154 item->uid_list = NULL;
3156 item->item.mtime = uid_val;
3158 imap_delete_all_cached_messages((FolderItem *)item);
3162 /* If old uid_next matches new uid_next we can be sure no message
3163 was added to the folder */
3164 debug_print("uid_next is %d and item->uid_next %d \n",
3165 uid_next, item->uid_next);
3166 if (uid_next == item->uid_next) {
3167 nummsgs = g_slist_length(item->uid_list);
3169 /* If number of messages is still the same we
3170 know our caches message numbers are still valid,
3171 otherwise if the number of messages has decrease
3172 we discard our cache to start a new scan to find
3173 out which numbers have been removed */
3174 if (exists == nummsgs) {
3175 debug_print("exists == nummsgs\n");
3176 *msgnum_list = g_slist_copy(item->uid_list);
3177 statusbar_pop_all();
3180 } else if (exists < nummsgs) {
3181 debug_print("Freeing imap uid cache");
3183 g_slist_free(item->uid_list);
3184 item->uid_list = NULL;
3189 *msgnum_list = NULL;
3190 statusbar_pop_all();
3195 nummsgs = get_list_of_uids(session, folder, item, &uidlist);
3198 statusbar_pop_all();
3203 if (nummsgs != exists) {
3204 /* Cache contains more messages then folder, we have cached
3205 an old UID of a message that was removed and new messages
3206 have been added too, otherwise the uid_next check would
3208 debug_print("Freeing imap uid cache");
3210 g_slist_free(item->uid_list);
3211 item->uid_list = NULL;
3213 g_slist_free(*msgnum_list);
3215 nummsgs = get_list_of_uids(session, folder, item, &uidlist);
3218 *msgnum_list = uidlist;
3220 dir = folder_item_get_path((FolderItem *)item);
3221 debug_print("removing old messages from %s\n", dir);
3222 remove_numbered_files_not_in_list(dir, *msgnum_list);
3225 item->uid_next = uid_next;
3227 debug_print("get_num_list - ok - %i\n", nummsgs);
3228 statusbar_pop_all();
3233 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item)
3238 flags.perm_flags = MSG_NEW|MSG_UNREAD;
3239 flags.tmp_flags = 0;
3241 g_return_val_if_fail(item != NULL, NULL);
3242 g_return_val_if_fail(file != NULL, NULL);
3244 if (folder_has_parent_of_type(item, F_QUEUE)) {
3245 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
3246 } else if (folder_has_parent_of_type(item, F_DRAFT)) {
3247 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
3250 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3251 if (!msginfo) return NULL;
3253 msginfo->plaintext_file = g_strdup(file);
3254 msginfo->folder = item;
3259 GSList *imap_get_msginfos(Folder *folder, FolderItem *item,
3260 GSList *msgnum_list)
3262 IMAPSession *session;
3263 MsgInfoList *ret = NULL;
3266 debug_print("get_msginfos\n");
3268 g_return_val_if_fail(folder != NULL, NULL);
3269 g_return_val_if_fail(item != NULL, NULL);
3270 g_return_val_if_fail(msgnum_list != NULL, NULL);
3272 session = imap_session_get(folder);
3273 g_return_val_if_fail(session != NULL, NULL);
3275 debug_print("IMAP getting msginfos\n");
3276 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3277 NULL, NULL, NULL, NULL, FALSE);
3278 if (ok != IMAP_SUCCESS) {
3282 if (!(folder_has_parent_of_type(item, F_DRAFT) ||
3283 folder_has_parent_of_type(item, F_QUEUE))) {
3284 ret = g_slist_concat(ret,
3285 imap_get_uncached_messages(session, item,
3288 MsgNumberList *sorted_list, *elem;
3289 gint startnum, lastnum;
3291 sorted_list = g_slist_sort(g_slist_copy(msgnum_list), g_int_compare);
3293 startnum = lastnum = GPOINTER_TO_INT(sorted_list->data);
3295 for (elem = sorted_list;; elem = g_slist_next(elem)) {
3299 num = GPOINTER_TO_INT(elem->data);
3301 if (num > lastnum + 1 || elem == NULL) {
3303 for (i = startnum; i <= lastnum; ++i) {
3306 file = imap_fetch_msg(folder, item, i);
3308 MsgInfo *msginfo = imap_parse_msg(file, item);
3309 if (msginfo != NULL) {
3310 msginfo->msgnum = i;
3311 ret = g_slist_append(ret, msginfo);
3325 g_slist_free(sorted_list);
3331 MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid)
3333 MsgInfo *msginfo = NULL;
3334 MsgInfoList *msginfolist;
3335 MsgNumberList numlist;
3337 numlist.next = NULL;
3338 numlist.data = GINT_TO_POINTER(uid);
3340 msginfolist = imap_get_msginfos(folder, item, &numlist);
3341 if (msginfolist != NULL) {
3342 msginfo = msginfolist->data;
3343 g_slist_free(msginfolist);
3349 gboolean imap_scan_required(Folder *folder, FolderItem *_item)
3351 IMAPSession *session;
3352 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3353 gint ok, exists = 0, unseen = 0;
3354 guint32 uid_next, uid_val;
3355 gboolean selected_folder;
3357 g_return_val_if_fail(folder != NULL, FALSE);
3358 g_return_val_if_fail(item != NULL, FALSE);
3359 g_return_val_if_fail(item->item.folder != NULL, FALSE);
3360 g_return_val_if_fail(FOLDER_CLASS(item->item.folder) == &imap_class, FALSE);
3362 if (item->item.path == NULL)
3365 session = imap_session_get(folder);
3366 g_return_val_if_fail(session != NULL, FALSE);
3368 selected_folder = (session->mbox != NULL) &&
3369 (!strcmp(session->mbox, item->item.path));
3370 if (selected_folder && time(NULL) - item->use_cache < 2) {
3371 ok = imap_cmd_noop(session);
3372 if (ok != IMAP_SUCCESS) {
3373 debug_print("disconnected!\n");
3374 session = imap_reconnect_if_possible(folder, session);
3375 if (session == NULL)
3380 if (session->folder_content_changed
3381 || session->exists != item->item.total_msgs) {
3386 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path, IMAP_FOLDER_ITEM(item),
3387 &exists, &uid_next, &uid_val, &unseen, FALSE);
3388 if (ok != IMAP_SUCCESS) {
3393 item->use_cache = time(NULL);
3394 item->c_messages = exists;
3395 item->c_uid_next = uid_next;
3396 item->c_uid_validity = uid_val;
3397 item->c_unseen = unseen;
3398 item->item.last_num = uid_next - 1;
3399 debug_print("uidnext %d, item->uid_next %d, exists %d, item->item.total_msgs %d\n",
3400 uid_next, item->uid_next, exists, item->item.total_msgs);
3401 if ((uid_next != item->uid_next) || (exists != item->item.total_msgs)) {
3410 void imap_change_flags(Folder *folder, FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
3412 IMAPSession *session;
3413 IMAPFlags flags_set = 0, flags_unset = 0;
3414 gint ok = IMAP_SUCCESS;
3415 MsgNumberList numlist;
3416 hashtable_data *ht_data = NULL;
3418 g_return_if_fail(folder != NULL);
3419 g_return_if_fail(folder->klass == &imap_class);
3420 g_return_if_fail(item != NULL);
3421 g_return_if_fail(item->folder == folder);
3422 g_return_if_fail(msginfo != NULL);
3423 g_return_if_fail(msginfo->folder == item);
3425 session = imap_session_get(folder);
3430 if ((ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
3431 NULL, NULL, NULL, NULL, FALSE)) != IMAP_SUCCESS) {
3436 if (!MSG_IS_MARKED(msginfo->flags) && (newflags & MSG_MARKED))
3437 flags_set |= IMAP_FLAG_FLAGGED;
3438 if ( MSG_IS_MARKED(msginfo->flags) && !(newflags & MSG_MARKED))
3439 flags_unset |= IMAP_FLAG_FLAGGED;
3441 if (!MSG_IS_UNREAD(msginfo->flags) && (newflags & MSG_UNREAD))
3442 flags_unset |= IMAP_FLAG_SEEN;
3443 if ( MSG_IS_UNREAD(msginfo->flags) && !(newflags & MSG_UNREAD))
3444 flags_set |= IMAP_FLAG_SEEN;
3446 if (!MSG_IS_REPLIED(msginfo->flags) && (newflags & MSG_REPLIED))
3447 flags_set |= IMAP_FLAG_ANSWERED;
3448 if ( MSG_IS_REPLIED(msginfo->flags) && !(newflags & MSG_REPLIED))
3449 flags_unset |= IMAP_FLAG_ANSWERED;
3451 if (!MSG_IS_DELETED(msginfo->flags) && (newflags & MSG_DELETED))
3452 flags_set |= IMAP_FLAG_DELETED;
3453 if ( MSG_IS_DELETED(msginfo->flags) && !(newflags & MSG_DELETED))
3454 flags_unset |= IMAP_FLAG_DELETED;
3456 numlist.next = NULL;
3457 numlist.data = GINT_TO_POINTER(msginfo->msgnum);
3459 if (IMAP_FOLDER_ITEM(item)->batching) {
3460 /* instead of performing an UID STORE command for each message change,
3461 * as a lot of them can change "together", we just fill in hashtables
3462 * and defer the treatment so that we're able to send only one
3465 debug_print("IMAP batch mode on, deferring flags change\n");
3467 ht_data = g_hash_table_lookup(IMAP_FOLDER_ITEM(item)->flags_set_table,
3468 GINT_TO_POINTER(flags_set));
3469 if (ht_data == NULL) {
3470 ht_data = g_new0(hashtable_data, 1);
3471 ht_data->session = session;
3472 ht_data->item = IMAP_FOLDER_ITEM(item);
3473 g_hash_table_insert(IMAP_FOLDER_ITEM(item)->flags_set_table,
3474 GINT_TO_POINTER(flags_set), ht_data);
3476 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
3477 ht_data->msglist = g_slist_prepend(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum));
3480 ht_data = g_hash_table_lookup(IMAP_FOLDER_ITEM(item)->flags_unset_table,
3481 GINT_TO_POINTER(flags_unset));
3482 if (ht_data == NULL) {
3483 ht_data = g_new0(hashtable_data, 1);
3484 ht_data->session = session;
3485 ht_data->item = IMAP_FOLDER_ITEM(item);
3486 g_hash_table_insert(IMAP_FOLDER_ITEM(item)->flags_unset_table,
3487 GINT_TO_POINTER(flags_unset), ht_data);
3489 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
3490 ht_data->msglist = g_slist_prepend(ht_data->msglist,
3491 GINT_TO_POINTER(msginfo->msgnum));
3494 debug_print("IMAP changing flags\n");
3496 ok = imap_set_message_flags(session, &numlist, flags_set, TRUE);
3497 if (ok != IMAP_SUCCESS) {
3504 ok = imap_set_message_flags(session, &numlist, flags_unset, FALSE);
3505 if (ok != IMAP_SUCCESS) {
3511 msginfo->flags.perm_flags = newflags;
3516 static gint imap_remove_msg(Folder *folder, FolderItem *item, gint uid)
3519 IMAPSession *session;
3521 MsgNumberList numlist;
3523 g_return_val_if_fail(folder != NULL, -1);
3524 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3525 g_return_val_if_fail(item != NULL, -1);
3527 session = imap_session_get(folder);
3528 if (!session) return -1;
3530 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3531 NULL, NULL, NULL, NULL, FALSE);
3532 if (ok != IMAP_SUCCESS) {
3536 numlist.next = NULL;
3537 numlist.data = GINT_TO_POINTER(uid);
3539 ok = imap_set_message_flags
3540 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
3541 &numlist, IMAP_FLAG_DELETED, TRUE);
3542 if (ok != IMAP_SUCCESS) {
3543 log_warning(_("can't set deleted flags: %d\n"), uid);
3548 if (!session->uidplus) {
3549 ok = imap_cmd_expunge(session);
3553 uidstr = g_strdup_printf("%u", uid);
3554 ok = imap_cmd_expunge(session);
3557 if (ok != IMAP_SUCCESS) {
3558 log_warning(_("can't expunge\n"));
3563 IMAP_FOLDER_ITEM(item)->uid_list = g_slist_remove(
3564 IMAP_FOLDER_ITEM(item)->uid_list, numlist.data);
3565 dir = folder_item_get_path(item);
3566 if (is_dir_exist(dir))
3567 remove_numbered_files(dir, uid, uid);
3570 return IMAP_SUCCESS;
3573 static gint compare_msginfo(gconstpointer a, gconstpointer b)
3575 return ((MsgInfo *)a)->msgnum - ((MsgInfo *)b)->msgnum;
3578 static guint gslist_find_next_num(MsgNumberList **list, guint num)
3582 g_return_val_if_fail(list != NULL, -1);
3584 for (elem = *list; elem != NULL; elem = g_slist_next(elem))
3585 if (GPOINTER_TO_INT(elem->data) >= num)
3588 return elem != NULL ? GPOINTER_TO_INT(elem->data) : (gint)-1;
3592 * NEW and DELETED flags are not syncronized
3593 * - The NEW/RECENT flags in IMAP folders can not really be directly
3594 * modified by Sylpheed
3595 * - The DELETE/DELETED flag in IMAP and Sylpheed don't have the same
3596 * meaning, in IMAP it always removes the messages from the FolderItem
3597 * in Sylpheed it can mean to move the message to trash
3600 typedef struct _get_flags_data {
3603 MsgInfoList *msginfo_list;
3604 GRelation *msgflags;
3605 gboolean full_search;
3609 static /*gint*/ void *imap_get_flags_thread(void *data)
3611 get_flags_data *stuff = (get_flags_data *)data;
3612 Folder *folder = stuff->folder;
3613 FolderItem *item = stuff->item;
3614 MsgInfoList *msginfo_list = stuff->msginfo_list;
3615 GRelation *msgflags = stuff->msgflags;
3616 gboolean full_search = stuff->full_search;
3617 IMAPSession *session;
3618 GSList *sorted_list = NULL;
3619 GSList *unseen = NULL, *answered = NULL, *flagged = NULL, *deleted = NULL;
3620 GSList *p_unseen, *p_answered, *p_flagged, *p_deleted;
3622 GSList *seq_list, *cur;
3623 gboolean reverse_seen = FALSE;
3626 gint exists_cnt, unseen_cnt;
3627 gboolean selected_folder;
3629 if (folder == NULL || item == NULL) {
3631 return GINT_TO_POINTER(-1);
3634 session = imap_session_get(folder);
3635 if (session == NULL) {
3637 return GINT_TO_POINTER(-1);
3640 selected_folder = (session->mbox != NULL) &&
3641 (!strcmp(session->mbox, item->path));
3643 if (!selected_folder) {
3644 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3645 &exists_cnt, NULL, &unseen_cnt, NULL, TRUE);
3646 if (ok != IMAP_SUCCESS) {
3649 return GINT_TO_POINTER(-1);
3652 if (unseen_cnt > exists_cnt / 2)
3653 reverse_seen = TRUE;
3656 if (item->unread_msgs > item->total_msgs / 2)
3657 reverse_seen = TRUE;
3660 cmd_buf = g_string_new(NULL);
3662 sorted_list = g_slist_sort(g_slist_copy(msginfo_list), compare_msginfo);
3664 seq_list = imap_get_lep_set_from_msglist(msginfo_list);
3666 struct mailimap_set * set;
3667 set = mailimap_set_new_interval(1, 0);
3668 seq_list = g_slist_append(NULL, set);
3671 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
3672 struct mailimap_set * imapset;
3673 clist * lep_uidlist;
3676 imapset = cur->data;
3678 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SEEN,
3679 imapset, &lep_uidlist);
3682 r = imap_threaded_search(folder,
3683 IMAP_SEARCH_TYPE_UNSEEN,
3684 imapset, &lep_uidlist);
3686 if (r == MAILIMAP_NO_ERROR) {
3689 uidlist = imap_uid_list_from_lep(lep_uidlist);
3690 mailimap_search_result_free(lep_uidlist);
3692 unseen = g_slist_concat(unseen, uidlist);
3695 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_ANSWERED,
3696 imapset, &lep_uidlist);
3697 if (r == MAILIMAP_NO_ERROR) {
3700 uidlist = imap_uid_list_from_lep(lep_uidlist);
3701 mailimap_search_result_free(lep_uidlist);
3703 answered = g_slist_concat(answered, uidlist);
3706 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_FLAGGED,
3707 imapset, &lep_uidlist);
3708 if (r == MAILIMAP_NO_ERROR) {
3711 uidlist = imap_uid_list_from_lep(lep_uidlist);
3712 mailimap_search_result_free(lep_uidlist);
3714 flagged = g_slist_concat(flagged, uidlist);
3717 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_DELETED,
3718 imapset, &lep_uidlist);
3719 if (r == MAILIMAP_NO_ERROR) {
3722 uidlist = imap_uid_list_from_lep(lep_uidlist);
3723 mailimap_search_result_free(lep_uidlist);
3725 deleted = g_slist_concat(deleted, uidlist);
3730 p_answered = answered;
3731 p_flagged = flagged;
3732 p_deleted = deleted;
3734 for (elem = sorted_list; elem != NULL; elem = g_slist_next(elem)) {
3739 msginfo = (MsgInfo *) elem->data;
3740 flags = msginfo->flags.perm_flags;
3741 wasnew = (flags & MSG_NEW);
3742 flags &= ~((reverse_seen ? 0 : MSG_UNREAD | MSG_NEW) | MSG_REPLIED | MSG_MARKED);
3744 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
3745 if (gslist_find_next_num(&p_unseen, msginfo->msgnum) == msginfo->msgnum) {
3746 if (!reverse_seen) {
3747 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
3749 flags &= ~(MSG_UNREAD | MSG_NEW);
3752 if (gslist_find_next_num(&p_answered, msginfo->msgnum) == msginfo->msgnum)
3753 flags |= MSG_REPLIED;
3755 flags &= ~MSG_REPLIED;
3756 if (gslist_find_next_num(&p_flagged, msginfo->msgnum) == msginfo->msgnum)
3757 flags |= MSG_MARKED;
3759 flags &= ~MSG_MARKED;
3760 if (gslist_find_next_num(&p_deleted, msginfo->msgnum) == msginfo->msgnum)
3761 flags |= MSG_DELETED;
3763 flags &= ~MSG_DELETED;
3764 g_relation_insert(msgflags, msginfo, GINT_TO_POINTER(flags));
3767 imap_lep_set_free(seq_list);
3768 g_slist_free(flagged);
3769 g_slist_free(deleted);
3770 g_slist_free(answered);
3771 g_slist_free(unseen);
3772 g_slist_free(sorted_list);
3773 g_string_free(cmd_buf, TRUE);
3777 return GINT_TO_POINTER(0);
3780 static gint imap_get_flags(Folder *folder, FolderItem *item,
3781 MsgInfoList *msginfo_list, GRelation *msgflags)
3784 get_flags_data *data = g_new0(get_flags_data, 1);
3786 data->folder = folder;
3788 data->msginfo_list = msginfo_list;
3789 data->msgflags = msgflags;
3790 data->full_search = FALSE;
3792 GSList *tmp = NULL, *cur;
3794 if (prefs_common.work_offline && !inc_offline_should_override()) {
3799 tmp = folder_item_get_msg_list(item);
3801 if (g_slist_length(tmp) == g_slist_length(msginfo_list))
3802 data->full_search = TRUE;
3804 for (cur = tmp; cur; cur = cur->next)
3805 procmsg_msginfo_free((MsgInfo *)cur->data);
3809 result = GPOINTER_TO_INT(imap_get_flags_thread(data));
3816 static gboolean process_flags(gpointer key, gpointer value, gpointer user_data)
3818 gboolean flags_set = GPOINTER_TO_INT(user_data);
3819 gint flags_value = GPOINTER_TO_INT(key);
3820 hashtable_data *data = (hashtable_data *)value;
3821 IMAPFolderItem *_item = data->item;
3822 FolderItem *item = (FolderItem *)_item;
3823 gint ok = IMAP_ERROR;
3824 IMAPSession *session = imap_session_get(item->folder);
3826 data->msglist = g_slist_reverse(data->msglist);
3828 debug_print("IMAP %ssetting flags to %d for %d messages\n",
3831 g_slist_length(data->msglist));
3835 ok = imap_select(session, IMAP_FOLDER(item->folder), item->path,
3836 NULL, NULL, NULL, NULL, FALSE);
3838 if (ok == IMAP_SUCCESS) {
3839 imap_set_message_flags(data->session, data->msglist, flags_value, flags_set);
3841 g_warning("can't select mailbox %s\n", item->path);
3845 g_slist_free(data->msglist);
3850 static void process_hashtable(IMAPFolderItem *item)
3852 if (item->flags_set_table) {
3853 g_hash_table_foreach_remove(item->flags_set_table, process_flags, GINT_TO_POINTER(TRUE));
3854 g_hash_table_destroy(item->flags_set_table);
3855 item->flags_set_table = NULL;
3857 if (item->flags_unset_table) {
3858 g_hash_table_foreach_remove(item->flags_unset_table, process_flags, GINT_TO_POINTER(FALSE));
3859 g_hash_table_destroy(item->flags_unset_table);
3860 item->flags_unset_table = NULL;
3864 static void imap_set_batch (Folder *folder, FolderItem *_item, gboolean batch)
3866 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3868 g_return_if_fail(item != NULL);
3870 if (item->batching == batch)
3874 item->batching = TRUE;
3875 debug_print("IMAP switching to batch mode\n");
3876 if (!item->flags_set_table) {
3877 item->flags_set_table = g_hash_table_new(NULL, g_direct_equal);
3879 if (!item->flags_unset_table) {
3880 item->flags_unset_table = g_hash_table_new(NULL, g_direct_equal);
3883 debug_print("IMAP switching away from batch mode\n");
3885 process_hashtable(item);
3886 item->batching = FALSE;
3892 /* data types conversion libetpan <-> sylpheed */
3896 #define ETPAN_IMAP_MB_MARKED 1
3897 #define ETPAN_IMAP_MB_UNMARKED 2
3898 #define ETPAN_IMAP_MB_NOSELECT 4
3899 #define ETPAN_IMAP_MB_NOINFERIORS 8
3901 static int imap_flags_to_flags(struct mailimap_mbx_list_flags * imap_flags)
3907 if (imap_flags->mbf_type == MAILIMAP_MBX_LIST_FLAGS_SFLAG) {
3908 switch (imap_flags->mbf_sflag) {
3909 case MAILIMAP_MBX_LIST_SFLAG_MARKED:
3910 flags |= ETPAN_IMAP_MB_MARKED;
3912 case MAILIMAP_MBX_LIST_SFLAG_NOSELECT:
3913 flags |= ETPAN_IMAP_MB_NOSELECT;
3915 case MAILIMAP_MBX_LIST_SFLAG_UNMARKED:
3916 flags |= ETPAN_IMAP_MB_UNMARKED;
3921 for(cur = clist_begin(imap_flags->mbf_oflags) ; cur != NULL ;
3922 cur = clist_next(cur)) {
3923 struct mailimap_mbx_list_oflag * oflag;
3925 oflag = clist_content(cur);
3927 switch (oflag->of_type) {
3928 case MAILIMAP_MBX_LIST_OFLAG_NOINFERIORS:
3929 flags |= ETPAN_IMAP_MB_NOINFERIORS;
3937 static GSList * imap_list_from_lep(IMAPFolder * folder,
3938 clist * list, const gchar * real_path, gboolean all)
3945 for(iter = clist_begin(list) ; iter != NULL ;
3946 iter = clist_next(iter)) {
3947 struct mailimap_mailbox_list * mb;
3955 FolderItem *new_item;
3957 mb = clist_content(iter);
3963 if (mb->mb_flag != NULL)
3964 flags = imap_flags_to_flags(mb->mb_flag);
3966 delimiter = mb->mb_delimiter;
3969 dup_name = strdup(name);
3970 if (delimiter != '\0')
3971 subst_char(dup_name, delimiter, '/');
3973 base = g_path_get_basename(dup_name);
3974 if (base[0] == '.') {
3980 if (!all && strcmp(dup_name, real_path) == 0) {
3986 if (!all && dup_name[strlen(dup_name)-1] == '/') {
3992 loc_name = imap_modified_utf7_to_utf8(base);
3993 loc_path = imap_modified_utf7_to_utf8(dup_name);
3995 new_item = folder_item_new(FOLDER(folder), loc_name, loc_path);
3996 if ((flags & ETPAN_IMAP_MB_NOINFERIORS) != 0)
3997 new_item->no_sub = TRUE;
3998 if (strcmp(dup_name, "INBOX") != 0 &&
3999 ((flags & ETPAN_IMAP_MB_NOSELECT) != 0))
4000 new_item->no_select = TRUE;
4002 item_list = g_slist_append(item_list, new_item);
4004 debug_print("folder '%s' found.\n", loc_path);
4015 static GSList * imap_get_lep_set_from_numlist(MsgNumberList *numlist)
4017 GSList *sorted_list, *cur;
4018 guint first, last, next;
4019 GSList *ret_list = NULL;
4021 struct mailimap_set * current_set;
4022 unsigned int item_count;
4024 if (numlist == NULL)
4028 current_set = mailimap_set_new_empty();
4030 sorted_list = g_slist_copy(numlist);
4031 sorted_list = g_slist_sort(sorted_list, g_int_compare);
4033 first = GPOINTER_TO_INT(sorted_list->data);
4036 for (cur = sorted_list; cur != NULL; cur = g_slist_next(cur)) {
4037 if (GPOINTER_TO_INT(cur->data) == 0)
4042 last = GPOINTER_TO_INT(cur->data);
4044 next = GPOINTER_TO_INT(cur->next->data);
4048 if (last + 1 != next || next == 0) {
4050 struct mailimap_set_item * item;
4051 item = mailimap_set_item_new(first, last);
4052 mailimap_set_add(current_set, item);
4057 if (count >= IMAP_SET_MAX_COUNT) {
4058 ret_list = g_slist_append(ret_list,
4060 current_set = mailimap_set_new_empty();
4067 if (clist_count(current_set->set_list) > 0) {
4068 ret_list = g_slist_append(ret_list,
4072 g_slist_free(sorted_list);
4077 static GSList * imap_get_lep_set_from_msglist(MsgInfoList *msglist)
4079 MsgNumberList *numlist = NULL;
4083 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
4084 MsgInfo *msginfo = (MsgInfo *) cur->data;
4086 numlist = g_slist_append(numlist, GINT_TO_POINTER(msginfo->msgnum));
4088 seq_list = imap_get_lep_set_from_numlist(numlist);
4089 g_slist_free(numlist);
4094 static GSList * imap_uid_list_from_lep(clist * list)
4101 for(iter = clist_begin(list) ; iter != NULL ;
4102 iter = clist_next(iter)) {
4105 puid = clist_content(iter);
4106 result = g_slist_append(result, GINT_TO_POINTER(* puid));
4112 static GSList * imap_uid_list_from_lep_tab(carray * list)
4119 for(i = 0 ; i < carray_count(list) ; i ++) {
4122 puid = carray_get(list, i);
4123 result = g_slist_append(result, GINT_TO_POINTER(* puid));
4129 static MsgInfo *imap_envelope_from_lep(struct imap_fetch_env_info * info,
4132 MsgInfo *msginfo = NULL;
4135 MsgFlags flags = {0, 0};
4137 if (info->headers == NULL)
4140 MSG_SET_TMP_FLAGS(flags, MSG_IMAP);
4141 if (folder_has_parent_of_type(item, F_QUEUE)) {
4142 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
4143 } else if (folder_has_parent_of_type(item, F_DRAFT)) {
4144 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
4146 flags.perm_flags = info->flags;
4150 msginfo = procheader_parse_str(info->headers, flags, FALSE, FALSE);
4153 msginfo->msgnum = uid;
4154 msginfo->size = size;
4160 static void imap_lep_set_free(GSList *seq_list)
4164 for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) {
4165 struct mailimap_set * imapset;
4167 imapset = cur->data;
4168 mailimap_set_free(imapset);
4170 g_slist_free(seq_list);
4173 static struct mailimap_flag_list * imap_flag_to_lep(IMAPFlags flags)
4175 struct mailimap_flag_list * flag_list;
4177 flag_list = mailimap_flag_list_new_empty();
4179 if (IMAP_IS_SEEN(flags))
4180 mailimap_flag_list_add(flag_list,
4181 mailimap_flag_new_seen());
4182 if (IMAP_IS_ANSWERED(flags))
4183 mailimap_flag_list_add(flag_list,
4184 mailimap_flag_new_answered());
4185 if (IMAP_IS_FLAGGED(flags))
4186 mailimap_flag_list_add(flag_list,
4187 mailimap_flag_new_flagged());
4188 if (IMAP_IS_DELETED(flags))
4189 mailimap_flag_list_add(flag_list,
4190 mailimap_flag_new_deleted());
4191 if (IMAP_IS_DRAFT(flags))
4192 mailimap_flag_list_add(flag_list,
4193 mailimap_flag_new_draft());
4198 guint imap_folder_get_refcnt(Folder *folder)
4200 return ((IMAPFolder *)folder)->refcnt;
4203 void imap_folder_ref(Folder *folder)
4205 ((IMAPFolder *)folder)->refcnt++;
4208 void imap_folder_unref(Folder *folder)
4210 if (((IMAPFolder *)folder)->refcnt > 0)
4211 ((IMAPFolder *)folder)->refcnt--;
4214 #else /* HAVE_LIBETPAN */
4216 static FolderClass imap_class;
4218 static Folder *imap_folder_new (const gchar *name,
4223 static gint imap_create_tree (Folder *folder)
4227 static FolderItem *imap_create_folder (Folder *folder,
4233 static gint imap_rename_folder (Folder *folder,
4240 FolderClass *imap_get_class(void)
4242 if (imap_class.idstr == NULL) {
4243 imap_class.type = F_IMAP;
4244 imap_class.idstr = "imap";
4245 imap_class.uistr = "IMAP4";
4247 imap_class.new_folder = imap_folder_new;
4248 imap_class.create_tree = imap_create_tree;
4249 imap_class.create_folder = imap_create_folder;
4250 imap_class.rename_folder = imap_rename_folder;
4251 /* nothing implemented */
4258 void imap_synchronise(FolderItem *item)
4260 imap_gtk_synchronise(item);