2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2005 Hiroyuki Yamamoto
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27 #include <glib/gi18n.h>
54 #include "procheader.h"
55 #include "prefs_account.h"
60 #include "prefs_common.h"
61 #include "inputdialog.h"
63 #include "remotefolder.h"
64 #include "alertpanel.h"
66 #include "statusbar.h"
68 #include "imap-thread.h"
70 typedef struct _IMAPFolder IMAPFolder;
71 typedef struct _IMAPSession IMAPSession;
72 typedef struct _IMAPNameSpace IMAPNameSpace;
73 typedef struct _IMAPFolderItem IMAPFolderItem;
75 #include "prefs_account.h"
77 #define IMAP_FOLDER(obj) ((IMAPFolder *)obj)
78 #define IMAP_FOLDER_ITEM(obj) ((IMAPFolderItem *)obj)
79 #define IMAP_SESSION(obj) ((IMAPSession *)obj)
85 /* list of IMAPNameSpace */
89 gchar last_seen_separator;
97 gboolean authenticated;
106 gboolean folder_content_changed;
111 struct _IMAPNameSpace
117 #define IMAP_SUCCESS 0
118 #define IMAP_SOCKET 2
119 #define IMAP_AUTHFAIL 3
120 #define IMAP_PROTOCOL 4
121 #define IMAP_SYNTAX 5
125 #define IMAPBUFSIZE 8192
129 IMAP_FLAG_SEEN = 1 << 0,
130 IMAP_FLAG_ANSWERED = 1 << 1,
131 IMAP_FLAG_FLAGGED = 1 << 2,
132 IMAP_FLAG_DELETED = 1 << 3,
133 IMAP_FLAG_DRAFT = 1 << 4
136 #define IMAP_IS_SEEN(flags) ((flags & IMAP_FLAG_SEEN) != 0)
137 #define IMAP_IS_ANSWERED(flags) ((flags & IMAP_FLAG_ANSWERED) != 0)
138 #define IMAP_IS_FLAGGED(flags) ((flags & IMAP_FLAG_FLAGGED) != 0)
139 #define IMAP_IS_DELETED(flags) ((flags & IMAP_FLAG_DELETED) != 0)
140 #define IMAP_IS_DRAFT(flags) ((flags & IMAP_FLAG_DRAFT) != 0)
143 #define IMAP4_PORT 143
145 #define IMAPS_PORT 993
148 #define IMAP_CMD_LIMIT 1000
150 struct _IMAPFolderItem
160 static void imap_folder_init (Folder *folder,
164 static Folder *imap_folder_new (const gchar *name,
166 static void imap_folder_destroy (Folder *folder);
168 static IMAPSession *imap_session_new (Folder *folder,
169 const PrefsAccount *account);
170 static void imap_session_authenticate(IMAPSession *session,
171 const PrefsAccount *account);
172 static void imap_session_destroy (Session *session);
174 static gchar *imap_fetch_msg (Folder *folder,
177 static gchar *imap_fetch_msg_full (Folder *folder,
182 static gint imap_add_msg (Folder *folder,
186 static gint imap_add_msgs (Folder *folder,
189 GRelation *relation);
191 static gint imap_copy_msg (Folder *folder,
194 static gint imap_copy_msgs (Folder *folder,
196 MsgInfoList *msglist,
197 GRelation *relation);
199 static gint imap_remove_msg (Folder *folder,
202 static gint imap_remove_msgs (Folder *folder,
204 MsgInfoList *msglist,
205 GRelation *relation);
206 static gint imap_remove_all_msg (Folder *folder,
209 static gboolean imap_is_msg_changed (Folder *folder,
213 static gint imap_close (Folder *folder,
216 static gint imap_scan_tree (Folder *folder);
218 static gint imap_create_tree (Folder *folder);
220 static FolderItem *imap_create_folder (Folder *folder,
223 static gint imap_rename_folder (Folder *folder,
226 static gint imap_remove_folder (Folder *folder,
229 static FolderItem *imap_folder_item_new (Folder *folder);
230 static void imap_folder_item_destroy (Folder *folder,
233 static IMAPSession *imap_session_get (Folder *folder);
235 static gint imap_auth (IMAPSession *session,
240 static gint imap_scan_tree_recursive (IMAPSession *session,
243 static void imap_create_missing_folders (Folder *folder);
244 static FolderItem *imap_create_special_folder
246 SpecialFolderItemType stype,
249 static gint imap_do_copy_msgs (Folder *folder,
251 MsgInfoList *msglist,
252 GRelation *relation);
254 static void imap_delete_all_cached_messages (FolderItem *item);
255 static void imap_set_batch (Folder *folder,
258 static gint imap_set_message_flags (IMAPSession *session,
259 MsgNumberList *numlist,
262 static gint imap_select (IMAPSession *session,
268 guint32 *uid_validity,
270 static gint imap_status (IMAPSession *session,
276 guint32 *uid_validity,
280 static IMAPNameSpace *imap_find_namespace (IMAPFolder *folder,
282 static gchar imap_get_path_separator (IMAPFolder *folder,
284 static gchar *imap_get_real_path (IMAPFolder *folder,
287 static void imap_free_capabilities (IMAPSession *session);
289 /* low-level IMAP4rev1 commands */
290 static gint imap_cmd_login (IMAPSession *session,
293 static gint imap_cmd_logout (IMAPSession *session);
294 static gint imap_cmd_noop (IMAPSession *session);
296 static gint imap_cmd_starttls (IMAPSession *session);
298 static gint imap_cmd_select (IMAPSession *session,
303 guint32 *uid_validity,
305 static gint imap_cmd_examine (IMAPSession *session,
310 guint32 *uid_validity,
312 static gint imap_cmd_create (IMAPSession *sock,
313 const gchar *folder);
314 static gint imap_cmd_rename (IMAPSession *sock,
315 const gchar *oldfolder,
316 const gchar *newfolder);
317 static gint imap_cmd_delete (IMAPSession *session,
318 const gchar *folder);
319 static gint imap_cmd_fetch (IMAPSession *sock,
321 const gchar *filename,
324 static gint imap_cmd_append (IMAPSession *session,
325 const gchar *destfolder,
329 static gint imap_cmd_copy (IMAPSession *session,
330 struct mailimap_set * set,
331 const gchar *destfolder,
332 GRelation *uid_mapping);
333 static gint imap_cmd_store (IMAPSession *session,
334 struct mailimap_set * set,
337 static gint imap_cmd_expunge (IMAPSession *session);
339 static void imap_path_separator_subst (gchar *str,
342 static gchar *imap_utf8_to_modified_utf7 (const gchar *from);
343 static gchar *imap_modified_utf7_to_utf8 (const gchar *mutf7_str);
345 static gboolean imap_rename_folder_func (GNode *node,
347 static gint imap_get_num_list (Folder *folder,
350 gboolean *old_uids_valid);
351 static GSList *imap_get_msginfos (Folder *folder,
353 GSList *msgnum_list);
354 static MsgInfo *imap_get_msginfo (Folder *folder,
357 static gboolean imap_scan_required (Folder *folder,
359 static void imap_change_flags (Folder *folder,
362 MsgPermFlags newflags);
363 static gint imap_get_flags (Folder *folder,
365 MsgInfoList *msglist,
366 GRelation *msgflags);
367 static gchar *imap_folder_get_path (Folder *folder);
368 static gchar *imap_item_get_path (Folder *folder,
370 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item);
373 /* data types conversion libetpan <-> sylpheed */
374 static GSList * imap_list_from_lep(IMAPFolder * folder,
375 clist * list, const gchar * real_path);
376 static GSList * imap_get_lep_set_from_numlist(MsgNumberList *numlist);
377 static GSList * imap_get_lep_set_from_msglist(MsgInfoList *msglist);
378 static GSList * imap_uid_list_from_lep(clist * list);
379 static GSList * imap_uid_list_from_lep_tab(carray * list);
380 static MsgInfo *imap_envelope_from_lep(struct imap_fetch_env_info * info,
382 static void imap_lep_set_free(GSList *seq_list);
383 static struct mailimap_flag_list * imap_flag_to_lep(IMAPFlags flags);
386 static GHashTable *flags_set_table = NULL;
387 static GHashTable *flags_unset_table = NULL;
388 typedef struct _hashtable_data {
389 IMAPSession *session;
393 static FolderClass imap_class;
395 typedef struct _thread_data {
405 FolderClass *imap_get_class(void)
407 if (imap_class.idstr == NULL) {
408 imap_class.type = F_IMAP;
409 imap_class.idstr = "imap";
410 imap_class.uistr = "IMAP4";
412 /* Folder functions */
413 imap_class.new_folder = imap_folder_new;
414 imap_class.destroy_folder = imap_folder_destroy;
415 imap_class.scan_tree = imap_scan_tree;
416 imap_class.create_tree = imap_create_tree;
418 /* FolderItem functions */
419 imap_class.item_new = imap_folder_item_new;
420 imap_class.item_destroy = imap_folder_item_destroy;
421 imap_class.item_get_path = imap_item_get_path;
422 imap_class.create_folder = imap_create_folder;
423 imap_class.rename_folder = imap_rename_folder;
424 imap_class.remove_folder = imap_remove_folder;
425 imap_class.close = imap_close;
426 imap_class.get_num_list = imap_get_num_list;
427 imap_class.scan_required = imap_scan_required;
429 /* Message functions */
430 imap_class.get_msginfo = imap_get_msginfo;
431 imap_class.get_msginfos = imap_get_msginfos;
432 imap_class.fetch_msg = imap_fetch_msg;
433 imap_class.fetch_msg_full = imap_fetch_msg_full;
434 imap_class.add_msg = imap_add_msg;
435 imap_class.add_msgs = imap_add_msgs;
436 imap_class.copy_msg = imap_copy_msg;
437 imap_class.copy_msgs = imap_copy_msgs;
438 imap_class.remove_msg = imap_remove_msg;
439 imap_class.remove_msgs = imap_remove_msgs;
440 imap_class.remove_all_msg = imap_remove_all_msg;
441 imap_class.is_msg_changed = imap_is_msg_changed;
442 imap_class.change_flags = imap_change_flags;
443 imap_class.get_flags = imap_get_flags;
444 imap_class.set_batch = imap_set_batch;
446 pthread_mutex_init(&imap_mutex, NULL);
453 static Folder *imap_folder_new(const gchar *name, const gchar *path)
457 folder = (Folder *)g_new0(IMAPFolder, 1);
458 folder->klass = &imap_class;
459 imap_folder_init(folder, name, path);
464 static void imap_folder_destroy(Folder *folder)
468 while (imap_folder_get_refcnt(folder) > 0)
469 gtk_main_iteration();
471 dir = imap_folder_get_path(folder);
472 if (is_dir_exist(dir))
473 remove_dir_recursive(dir);
476 folder_remote_folder_destroy(REMOTE_FOLDER(folder));
480 static void imap_folder_init(Folder *folder, const gchar *name,
483 folder_remote_folder_init((Folder *)folder, name, path);
486 static FolderItem *imap_folder_item_new(Folder *folder)
488 IMAPFolderItem *item;
490 item = g_new0(IMAPFolderItem, 1);
493 item->uid_list = NULL;
495 return (FolderItem *)item;
498 static void imap_folder_item_destroy(Folder *folder, FolderItem *_item)
500 IMAPFolderItem *item = (IMAPFolderItem *)_item;
502 g_return_if_fail(item != NULL);
503 g_slist_free(item->uid_list);
508 static gboolean imap_reset_uid_lists_func(GNode *node, gpointer data)
510 IMAPFolderItem *item = (IMAPFolderItem *)node->data;
514 g_slist_free(item->uid_list);
515 item->uid_list = NULL;
520 static void imap_reset_uid_lists(Folder *folder)
522 if(folder->node == NULL)
525 /* Destroy all uid lists and rest last uid */
526 g_node_traverse(folder->node, G_IN_ORDER, G_TRAVERSE_ALL, -1, imap_reset_uid_lists_func, NULL);
529 static gint imap_auth(IMAPSession *session, const gchar *user, const gchar *pass,
534 ok = imap_cmd_login(session, user, pass);
536 if (ok == IMAP_SUCCESS)
537 session->authenticated = TRUE;
542 static IMAPSession *imap_session_get(Folder *folder)
544 RemoteFolder *rfolder = REMOTE_FOLDER(folder);
545 IMAPSession *session = NULL;
547 g_return_val_if_fail(folder != NULL, NULL);
548 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, NULL);
549 g_return_val_if_fail(folder->account != NULL, NULL);
551 if (prefs_common.work_offline && !imap_gtk_should_override()) {
555 /* Make sure we have a session */
556 if (rfolder->session != NULL) {
557 session = IMAP_SESSION(rfolder->session);
559 imap_reset_uid_lists(folder);
560 session = imap_session_new(folder, folder->account);
565 /* Make sure session is authenticated */
566 if (!IMAP_SESSION(session)->authenticated)
567 imap_session_authenticate(IMAP_SESSION(session), folder->account);
569 if (!IMAP_SESSION(session)->authenticated) {
570 session_destroy(SESSION(session));
571 rfolder->session = NULL;
576 /* Make sure we have parsed the IMAP namespace */
577 imap_parse_namespace(IMAP_SESSION(session),
578 IMAP_FOLDER(folder));
581 /* I think the point of this code is to avoid sending a
582 * keepalive if we've used the session recently and therefore
583 * think it's still alive. Unfortunately, most of the code
584 * does not yet check for errors on the socket, and so if the
585 * connection drops we don't notice until the timeout expires.
586 * A better solution than sending a NOOP every time would be
587 * for every command to be prepared to retry until it is
588 * successfully sent. -- mbp */
589 if (time(NULL) - SESSION(session)->last_access_time > SESSION_TIMEOUT_INTERVAL) {
590 /* verify that the session is still alive */
591 if (imap_cmd_noop(session) != IMAP_SUCCESS) {
592 /* Check if this is the first try to establish a
593 connection, if yes we don't try to reconnect */
594 if (rfolder->session == NULL) {
595 log_warning(_("Connecting to %s failed"),
596 folder->account->recv_server);
597 session_destroy(SESSION(session));
600 log_warning(_("IMAP4 connection to %s has been"
601 " disconnected. Reconnecting...\n"),
602 folder->account->recv_server);
603 statusbar_print_all(_("IMAP4 connection to %s has been"
604 " disconnected. Reconnecting...\n"),
605 folder->account->recv_server);
606 session_destroy(SESSION(session));
607 /* Clear folders session to make imap_session_get create
608 a new session, because of rfolder->session == NULL
609 it will not try to reconnect again and so avoid an
611 rfolder->session = NULL;
612 session = imap_session_get(folder);
618 rfolder->session = SESSION(session);
620 return IMAP_SESSION(session);
623 static IMAPSession *imap_session_new(Folder * folder,
624 const PrefsAccount *account)
626 IMAPSession *session;
632 /* FIXME: IMAP over SSL only... */
635 port = account->set_imapport ? account->imapport
636 : account->ssl_imap == SSL_TUNNEL ? IMAPS_PORT : IMAP4_PORT;
637 ssl_type = account->ssl_imap;
639 port = account->set_imapport ? account->imapport
644 statusbar_print_all(_("Connecting to IMAP4 server: %s..."), folder->account->recv_server);
645 if (account->set_tunnelcmd) {
646 r = imap_threaded_connect_cmd(folder,
648 account->recv_server,
653 if (ssl_type == SSL_TUNNEL) {
654 r = imap_threaded_connect_ssl(folder,
655 account->recv_server,
661 r = imap_threaded_connect(folder,
662 account->recv_server,
668 if (r == MAILIMAP_NO_ERROR_AUTHENTICATED) {
669 authenticated = TRUE;
671 else if (r == MAILIMAP_NO_ERROR_NON_AUTHENTICATED) {
672 authenticated = FALSE;
675 if(!prefs_common.no_recv_err_panel) {
676 alertpanel_error(_("Can't connect to IMAP4 server: %s:%d"),
677 account->recv_server, port);
683 session = g_new0(IMAPSession, 1);
684 session_init(SESSION(session));
685 SESSION(session)->type = SESSION_IMAP;
686 SESSION(session)->server = g_strdup(account->recv_server);
687 SESSION(session)->sock = NULL;
689 SESSION(session)->destroy = imap_session_destroy;
691 session->capability = NULL;
693 session->authenticated = authenticated;
694 session->mbox = NULL;
695 session->cmd_count = 0;
696 session->folder = folder;
697 IMAP_FOLDER(session->folder)->last_seen_separator = 0;
700 if (account->ssl_imap == SSL_STARTTLS) {
703 ok = imap_cmd_starttls(session);
704 if (ok != IMAP_SUCCESS) {
705 log_warning(_("Can't start TLS session.\n"));
706 session_destroy(SESSION(session));
710 imap_free_capabilities(session);
711 session->authenticated = FALSE;
712 session->uidplus = FALSE;
713 session->cmd_count = 1;
716 log_message("IMAP connection is %s-authenticated\n",
717 (session->authenticated) ? "pre" : "un");
722 static void imap_session_authenticate(IMAPSession *session,
723 const PrefsAccount *account)
727 g_return_if_fail(account->userid != NULL);
729 pass = account->passwd;
732 tmp_pass = input_dialog_query_password(account->recv_server, account->userid);
735 Xstrdup_a(pass, tmp_pass, {g_free(tmp_pass); return;});
738 statusbar_print_all(_("Connecting to IMAP4 server %s...\n"),
739 account->recv_server);
740 if (imap_auth(session, account->userid, pass, account->imap_auth_type) != IMAP_SUCCESS) {
741 imap_threaded_disconnect(session->folder);
742 imap_cmd_logout(session);
748 session->authenticated = TRUE;
751 static void imap_session_destroy(Session *session)
753 imap_threaded_disconnect(IMAP_SESSION(session)->folder);
755 imap_free_capabilities(IMAP_SESSION(session));
756 g_free(IMAP_SESSION(session)->mbox);
757 sock_close(session->sock);
758 session->sock = NULL;
761 static gchar *imap_fetch_msg(Folder *folder, FolderItem *item, gint uid)
763 return imap_fetch_msg_full(folder, item, uid, TRUE, TRUE);
766 static guint get_size_with_lfs(MsgInfo *info)
775 fp = procmsg_open_message(info);
779 while (fgets(buf, sizeof (buf), fp) != NULL) {
781 if (!strstr(buf, "\r") && strstr(buf, "\n"))
789 static gchar *imap_fetch_msg_full(Folder *folder, FolderItem *item, gint uid,
790 gboolean headers, gboolean body)
792 gchar *path, *filename;
793 IMAPSession *session;
796 g_return_val_if_fail(folder != NULL, NULL);
797 g_return_val_if_fail(item != NULL, NULL);
802 path = folder_item_get_path(item);
803 if (!is_dir_exist(path))
805 filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(uid), NULL);
808 if (is_file_exist(filename)) {
809 /* see whether the local file represents the whole message
810 * or not. As the IMAP server reports size with \r chars,
811 * we have to update the local file (UNIX \n only) size */
812 MsgInfo *msginfo = imap_parse_msg(filename, item);
813 MsgInfo *cached = msgcache_get_msg(item->cache,uid);
814 guint have_size = get_size_with_lfs(msginfo);
815 debug_print("message %d has been already %scached (%d/%d).\n", uid,
816 have_size == cached->size ? "fully ":"",
817 have_size, cached? (int)cached->size : -1);
819 if (cached && (cached->size == have_size || !body)) {
820 procmsg_msginfo_free(cached);
821 procmsg_msginfo_free(msginfo);
824 procmsg_msginfo_free(cached);
825 procmsg_msginfo_free(msginfo);
829 session = imap_session_get(folder);
835 debug_print("IMAP fetching messages\n");
836 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
837 NULL, NULL, NULL, NULL, FALSE);
838 if (ok != IMAP_SUCCESS) {
839 g_warning("can't select mailbox %s\n", item->path);
844 debug_print("getting message %d...\n", uid);
845 ok = imap_cmd_fetch(session, (guint32)uid, filename, headers, body);
847 if (ok != IMAP_SUCCESS) {
848 g_warning("can't fetch message %d\n", uid);
856 static gint imap_add_msg(Folder *folder, FolderItem *dest,
857 const gchar *file, MsgFlags *flags)
861 MsgFileInfo fileinfo;
863 g_return_val_if_fail(file != NULL, -1);
865 fileinfo.msginfo = NULL;
866 fileinfo.file = (gchar *)file;
867 fileinfo.flags = flags;
868 file_list.data = &fileinfo;
869 file_list.next = NULL;
871 ret = imap_add_msgs(folder, dest, &file_list, NULL);
875 static gint imap_add_msgs(Folder *folder, FolderItem *dest, GSList *file_list,
879 IMAPSession *session;
880 guint32 last_uid = 0;
882 MsgFileInfo *fileinfo;
886 g_return_val_if_fail(folder != NULL, -1);
887 g_return_val_if_fail(dest != NULL, -1);
888 g_return_val_if_fail(file_list != NULL, -1);
890 session = imap_session_get(folder);
894 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
896 for (cur = file_list; cur != NULL; cur = cur->next) {
897 IMAPFlags iflags = 0;
900 fileinfo = (MsgFileInfo *)cur->data;
902 if (fileinfo->flags) {
903 if (MSG_IS_MARKED(*fileinfo->flags))
904 iflags |= IMAP_FLAG_FLAGGED;
905 if (MSG_IS_REPLIED(*fileinfo->flags))
906 iflags |= IMAP_FLAG_ANSWERED;
907 if (!MSG_IS_UNREAD(*fileinfo->flags))
908 iflags |= IMAP_FLAG_SEEN;
911 if (dest->stype == F_OUTBOX ||
912 dest->stype == F_QUEUE ||
913 dest->stype == F_DRAFT ||
914 dest->stype == F_TRASH)
915 iflags |= IMAP_FLAG_SEEN;
917 ok = imap_cmd_append(session, destdir, fileinfo->file, iflags,
920 if (ok != IMAP_SUCCESS) {
921 g_warning("can't append message %s\n", fileinfo->file);
926 if (relation != NULL)
927 g_relation_insert(relation, fileinfo->msginfo != NULL ?
928 (gpointer) fileinfo->msginfo : (gpointer) fileinfo,
929 GINT_TO_POINTER(dest->last_num + 1));
930 if (last_uid < new_uid)
939 static gint imap_do_copy_msgs(Folder *folder, FolderItem *dest,
940 MsgInfoList *msglist, GRelation *relation)
944 GSList *seq_list, *cur;
946 IMAPSession *session;
947 gint ok = IMAP_SUCCESS;
948 GRelation *uid_mapping;
951 g_return_val_if_fail(folder != NULL, -1);
952 g_return_val_if_fail(dest != NULL, -1);
953 g_return_val_if_fail(msglist != NULL, -1);
955 session = imap_session_get(folder);
960 msginfo = (MsgInfo *)msglist->data;
962 src = msginfo->folder;
964 g_warning("the src folder is identical to the dest.\n");
968 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
969 NULL, NULL, NULL, NULL, FALSE);
970 if (ok != IMAP_SUCCESS) {
974 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
975 seq_list = imap_get_lep_set_from_msglist(msglist);
976 uid_mapping = g_relation_new(2);
977 g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
979 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
980 struct mailimap_set * seq_set;
984 debug_print("Copying messages from %s to %s ...\n",
987 ok = imap_cmd_copy(session, seq_set, destdir, uid_mapping);
988 if (ok != IMAP_SUCCESS) {
989 g_relation_destroy(uid_mapping);
990 imap_lep_set_free(seq_list);
995 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
996 MsgInfo *msginfo = (MsgInfo *)cur->data;
999 tuples = g_relation_select(uid_mapping,
1000 GINT_TO_POINTER(msginfo->msgnum),
1002 if (tuples->len > 0) {
1003 gint num = GPOINTER_TO_INT(g_tuples_index(tuples, 0, 1));
1004 g_relation_insert(relation, msginfo,
1005 GPOINTER_TO_INT(num));
1009 g_relation_insert(relation, msginfo,
1010 GPOINTER_TO_INT(0));
1011 g_tuples_destroy(tuples);
1014 g_relation_destroy(uid_mapping);
1015 imap_lep_set_free(seq_list);
1019 IMAP_FOLDER_ITEM(dest)->lastuid = 0;
1020 IMAP_FOLDER_ITEM(dest)->uid_next = 0;
1021 g_slist_free(IMAP_FOLDER_ITEM(dest)->uid_list);
1022 IMAP_FOLDER_ITEM(dest)->uid_list = NULL;
1024 if (ok == IMAP_SUCCESS)
1030 static gint imap_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1034 g_return_val_if_fail(msginfo != NULL, -1);
1036 msglist.data = msginfo;
1037 msglist.next = NULL;
1039 return imap_copy_msgs(folder, dest, &msglist, NULL);
1042 static gint imap_copy_msgs(Folder *folder, FolderItem *dest,
1043 MsgInfoList *msglist, GRelation *relation)
1049 g_return_val_if_fail(folder != NULL, -1);
1050 g_return_val_if_fail(dest != NULL, -1);
1051 g_return_val_if_fail(msglist != NULL, -1);
1053 msginfo = (MsgInfo *)msglist->data;
1054 g_return_val_if_fail(msginfo->folder != NULL, -1);
1056 if (folder == msginfo->folder->folder) {
1057 ret = imap_do_copy_msgs(folder, dest, msglist, relation);
1061 file_list = procmsg_get_message_file_list(msglist);
1062 g_return_val_if_fail(file_list != NULL, -1);
1064 ret = imap_add_msgs(folder, dest, file_list, relation);
1066 procmsg_message_file_list_free(file_list);
1072 static gint imap_do_remove_msgs(Folder *folder, FolderItem *dest,
1073 MsgInfoList *msglist, GRelation *relation)
1076 GSList *seq_list = NULL, *cur;
1078 IMAPSession *session;
1079 gint ok = IMAP_SUCCESS;
1080 GRelation *uid_mapping;
1082 g_return_val_if_fail(folder != NULL, -1);
1083 g_return_val_if_fail(dest != NULL, -1);
1084 g_return_val_if_fail(msglist != NULL, -1);
1086 session = imap_session_get(folder);
1090 msginfo = (MsgInfo *)msglist->data;
1092 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
1093 NULL, NULL, NULL, NULL, FALSE);
1094 if (ok != IMAP_SUCCESS) {
1098 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
1099 for (cur = msglist; cur; cur = cur->next) {
1100 msginfo = (MsgInfo *)cur->data;
1101 seq_list = g_slist_append(seq_list, GINT_TO_POINTER(msginfo->msgnum));
1104 uid_mapping = g_relation_new(2);
1105 g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
1107 ok = imap_set_message_flags
1108 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
1109 seq_list, IMAP_FLAG_DELETED, TRUE);
1110 if (ok != IMAP_SUCCESS) {
1111 log_warning(_("can't set deleted flags\n"));
1114 ok = imap_cmd_expunge(session);
1115 if (ok != IMAP_SUCCESS) {
1116 log_warning(_("can't expunge\n"));
1120 g_relation_destroy(uid_mapping);
1121 g_slist_free(seq_list);
1125 if (ok == IMAP_SUCCESS)
1131 static gint imap_remove_msgs(Folder *folder, FolderItem *dest,
1132 MsgInfoList *msglist, GRelation *relation)
1136 g_return_val_if_fail(folder != NULL, -1);
1137 g_return_val_if_fail(dest != NULL, -1);
1138 if (msglist == NULL)
1141 msginfo = (MsgInfo *)msglist->data;
1142 g_return_val_if_fail(msginfo->folder != NULL, -1);
1144 return imap_do_remove_msgs(folder, dest, msglist, relation);
1147 static gint imap_remove_all_msg(Folder *folder, FolderItem *item)
1149 GSList *list = folder_item_get_msg_list(item);
1150 gint res = imap_remove_msgs(folder, item, list, NULL);
1155 static gboolean imap_is_msg_changed(Folder *folder, FolderItem *item,
1158 /* TODO: properly implement this method */
1162 static gint imap_close(Folder *folder, FolderItem *item)
1167 static gint imap_scan_tree(Folder *folder)
1169 FolderItem *item = NULL;
1170 IMAPSession *session;
1171 gchar *root_folder = NULL;
1173 g_return_val_if_fail(folder != NULL, -1);
1174 g_return_val_if_fail(folder->account != NULL, -1);
1176 session = imap_session_get(folder);
1178 if (!folder->node) {
1179 folder_tree_destroy(folder);
1180 item = folder_item_new(folder, folder->name, NULL);
1181 item->folder = folder;
1182 folder->node = item->node = g_node_new(item);
1187 if (folder->account->imap_dir && *folder->account->imap_dir) {
1192 Xstrdup_a(root_folder, folder->account->imap_dir, {return -1;});
1193 extract_quote(root_folder, '"');
1194 subst_char(root_folder,
1195 imap_get_path_separator(IMAP_FOLDER(folder),
1198 strtailchomp(root_folder, '/');
1199 real_path = imap_get_real_path
1200 (IMAP_FOLDER(folder), root_folder);
1201 debug_print("IMAP root directory: %s\n", real_path);
1203 /* check if root directory exist */
1205 r = imap_threaded_list(session->folder, "", real_path,
1207 if ((r != MAILIMAP_NO_ERROR) || (clist_count(lep_list) == 0)) {
1208 if (!folder->node) {
1209 item = folder_item_new(folder, folder->name, NULL);
1210 item->folder = folder;
1211 folder->node = item->node = g_node_new(item);
1216 mailimap_list_result_free(lep_list);
1222 item = FOLDER_ITEM(folder->node->data);
1223 if (!item || ((item->path || root_folder) &&
1224 strcmp2(item->path, root_folder) != 0)) {
1225 folder_tree_destroy(folder);
1226 item = folder_item_new(folder, folder->name, root_folder);
1227 item->folder = folder;
1228 folder->node = item->node = g_node_new(item);
1231 imap_scan_tree_recursive(session, FOLDER_ITEM(folder->node->data));
1232 imap_create_missing_folders(folder);
1237 static gint imap_scan_tree_recursive(IMAPSession *session, FolderItem *item)
1240 IMAPFolder *imapfolder;
1241 FolderItem *new_item;
1242 GSList *item_list, *cur;
1245 gchar *wildcard_path;
1251 g_return_val_if_fail(item != NULL, -1);
1252 g_return_val_if_fail(item->folder != NULL, -1);
1253 g_return_val_if_fail(item->no_sub == FALSE, -1);
1255 folder = item->folder;
1256 imapfolder = IMAP_FOLDER(folder);
1258 separator = imap_get_path_separator(imapfolder, item->path);
1260 if (folder->ui_func)
1261 folder->ui_func(folder, item, folder->ui_func_data);
1264 wildcard[0] = separator;
1267 real_path = imap_get_real_path(imapfolder, item->path);
1271 real_path = g_strdup("");
1274 Xstrcat_a(wildcard_path, real_path, wildcard,
1275 {g_free(real_path); return IMAP_ERROR;});
1277 r = imap_threaded_list(folder, "", wildcard_path, &lep_list);
1278 if (r != MAILIMAP_NO_ERROR) {
1282 item_list = imap_list_from_lep(imapfolder,
1283 lep_list, real_path);
1284 mailimap_list_result_free(lep_list);
1289 node = item->node->children;
1290 while (node != NULL) {
1291 FolderItem *old_item = FOLDER_ITEM(node->data);
1292 GNode *next = node->next;
1295 for (cur = item_list; cur != NULL; cur = cur->next) {
1296 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1297 if (!strcmp2(old_item->path, cur_item->path)) {
1298 new_item = cur_item;
1303 debug_print("folder '%s' not found. removing...\n",
1305 folder_item_remove(old_item);
1307 old_item->no_sub = new_item->no_sub;
1308 old_item->no_select = new_item->no_select;
1309 if (old_item->no_sub == TRUE && node->children) {
1310 debug_print("folder '%s' doesn't have "
1311 "subfolders. removing...\n",
1313 folder_item_remove_children(old_item);
1320 for (cur = item_list; cur != NULL; cur = cur->next) {
1321 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1323 for (node = item->node->children; node != NULL;
1324 node = node->next) {
1325 if (!strcmp2(FOLDER_ITEM(node->data)->path,
1327 new_item = FOLDER_ITEM(node->data);
1328 folder_item_destroy(cur_item);
1334 new_item = cur_item;
1335 debug_print("new folder '%s' found.\n", new_item->path);
1336 folder_item_append(item, new_item);
1339 if (!strcmp(new_item->path, "INBOX")) {
1340 new_item->stype = F_INBOX;
1341 folder->inbox = new_item;
1342 } else if (!folder_item_parent(item) || item->stype == F_INBOX) {
1345 base = g_path_get_basename(new_item->path);
1347 if (!folder->outbox && !g_ascii_strcasecmp(base, "Sent")) {
1348 new_item->stype = F_OUTBOX;
1349 folder->outbox = new_item;
1350 } else if (!folder->draft && !g_ascii_strcasecmp(base, "Drafts")) {
1351 new_item->stype = F_DRAFT;
1352 folder->draft = new_item;
1353 } else if (!folder->queue && !g_ascii_strcasecmp(base, "Queue")) {
1354 new_item->stype = F_QUEUE;
1355 folder->queue = new_item;
1356 } else if (!folder->trash && !g_ascii_strcasecmp(base, "Trash")) {
1357 new_item->stype = F_TRASH;
1358 folder->trash = new_item;
1363 if (new_item->no_sub == FALSE)
1364 imap_scan_tree_recursive(session, new_item);
1367 g_slist_free(item_list);
1369 return IMAP_SUCCESS;
1372 static gint imap_create_tree(Folder *folder)
1374 g_return_val_if_fail(folder != NULL, -1);
1375 g_return_val_if_fail(folder->node != NULL, -1);
1376 g_return_val_if_fail(folder->node->data != NULL, -1);
1377 g_return_val_if_fail(folder->account != NULL, -1);
1379 imap_scan_tree(folder);
1380 imap_create_missing_folders(folder);
1385 static void imap_create_missing_folders(Folder *folder)
1387 g_return_if_fail(folder != NULL);
1390 folder->inbox = imap_create_special_folder
1391 (folder, F_INBOX, "INBOX");
1393 folder->trash = imap_create_special_folder
1394 (folder, F_TRASH, "Trash");
1397 static FolderItem *imap_create_special_folder(Folder *folder,
1398 SpecialFolderItemType stype,
1402 FolderItem *new_item;
1404 g_return_val_if_fail(folder != NULL, NULL);
1405 g_return_val_if_fail(folder->node != NULL, NULL);
1406 g_return_val_if_fail(folder->node->data != NULL, NULL);
1407 g_return_val_if_fail(folder->account != NULL, NULL);
1408 g_return_val_if_fail(name != NULL, NULL);
1410 item = FOLDER_ITEM(folder->node->data);
1411 new_item = imap_create_folder(folder, item, name);
1414 g_warning("Can't create '%s'\n", name);
1415 if (!folder->inbox) return NULL;
1417 new_item = imap_create_folder(folder, folder->inbox, name);
1419 g_warning("Can't create '%s' under INBOX\n", name);
1421 new_item->stype = stype;
1423 new_item->stype = stype;
1428 static gchar *imap_folder_get_path(Folder *folder)
1432 g_return_val_if_fail(folder != NULL, NULL);
1433 g_return_val_if_fail(folder->account != NULL, NULL);
1435 folder_path = g_strconcat(get_imap_cache_dir(),
1437 folder->account->recv_server,
1439 folder->account->userid,
1445 static gchar *imap_item_get_path(Folder *folder, FolderItem *item)
1447 gchar *folder_path, *path;
1449 g_return_val_if_fail(folder != NULL, NULL);
1450 g_return_val_if_fail(item != NULL, NULL);
1451 folder_path = imap_folder_get_path(folder);
1453 g_return_val_if_fail(folder_path != NULL, NULL);
1454 if (folder_path[0] == G_DIR_SEPARATOR) {
1456 path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
1459 path = g_strdup(folder_path);
1462 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1463 folder_path, G_DIR_SEPARATOR_S,
1466 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1469 g_free(folder_path);
1474 static FolderItem *imap_create_folder(Folder *folder, FolderItem *parent,
1477 gchar *dirpath, *imap_path;
1478 IMAPSession *session;
1479 FolderItem *new_item;
1485 g_return_val_if_fail(folder != NULL, NULL);
1486 g_return_val_if_fail(folder->account != NULL, NULL);
1487 g_return_val_if_fail(parent != NULL, NULL);
1488 g_return_val_if_fail(name != NULL, NULL);
1490 session = imap_session_get(folder);
1495 if (!folder_item_parent(parent) && strcmp(name, "INBOX") == 0)
1496 dirpath = g_strdup(name);
1497 else if (parent->path)
1498 dirpath = g_strconcat(parent->path, "/", name, NULL);
1499 else if ((p = strchr(name, '/')) != NULL && *(p + 1) != '\0')
1500 dirpath = g_strdup(name);
1501 else if (folder->account->imap_dir && *folder->account->imap_dir) {
1504 Xstrdup_a(imap_dir, folder->account->imap_dir, {return NULL;});
1505 strtailchomp(imap_dir, '/');
1506 dirpath = g_strconcat(imap_dir, "/", name, NULL);
1508 dirpath = g_strdup(name);
1510 /* keep trailing directory separator to create a folder that contains
1512 imap_path = imap_utf8_to_modified_utf7(dirpath);
1513 strtailchomp(dirpath, '/');
1514 Xstrdup_a(new_name, name, {
1518 strtailchomp(new_name, '/');
1519 separator = imap_get_path_separator(IMAP_FOLDER(folder), imap_path);
1520 imap_path_separator_subst(imap_path, separator);
1521 subst_char(new_name, '/', separator);
1523 if (strcmp(name, "INBOX") != 0) {
1525 gboolean exist = FALSE;
1529 argbuf = g_ptr_array_new();
1530 r = imap_threaded_list(folder, "", imap_path, &lep_list);
1531 if (r != MAILIMAP_NO_ERROR) {
1532 log_warning(_("can't create mailbox: LIST failed\n"));
1535 ptr_array_free_strings(argbuf);
1536 g_ptr_array_free(argbuf, TRUE);
1540 if (clist_count(lep_list) > 0)
1544 ok = imap_cmd_create(session, imap_path);
1545 if (ok != IMAP_SUCCESS) {
1546 log_warning(_("can't create mailbox\n"));
1554 new_item = folder_item_new(folder, new_name, dirpath);
1555 folder_item_append(parent, new_item);
1559 dirpath = folder_item_get_path(new_item);
1560 if (!is_dir_exist(dirpath))
1561 make_dir_hier(dirpath);
1567 static gint imap_rename_folder(Folder *folder, FolderItem *item,
1572 gchar *real_oldpath;
1573 gchar *real_newpath;
1575 gchar *old_cache_dir;
1576 gchar *new_cache_dir;
1577 IMAPSession *session;
1580 gint exists, recent, unseen;
1581 guint32 uid_validity;
1583 g_return_val_if_fail(folder != NULL, -1);
1584 g_return_val_if_fail(item != NULL, -1);
1585 g_return_val_if_fail(item->path != NULL, -1);
1586 g_return_val_if_fail(name != NULL, -1);
1588 if (strchr(name, imap_get_path_separator(IMAP_FOLDER(folder), item->path)) != NULL) {
1589 g_warning(_("New folder name must not contain the namespace "
1594 session = imap_session_get(folder);
1598 real_oldpath = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1600 g_free(session->mbox);
1601 session->mbox = NULL;
1602 ok = imap_cmd_examine(session, "INBOX",
1603 &exists, &recent, &unseen, &uid_validity, FALSE);
1604 if (ok != IMAP_SUCCESS) {
1605 g_free(real_oldpath);
1609 separator = imap_get_path_separator(IMAP_FOLDER(folder), item->path);
1610 if (strchr(item->path, G_DIR_SEPARATOR)) {
1611 dirpath = g_path_get_dirname(item->path);
1612 newpath = g_strconcat(dirpath, G_DIR_SEPARATOR_S, name, NULL);
1615 newpath = g_strdup(name);
1617 real_newpath = imap_utf8_to_modified_utf7(newpath);
1618 imap_path_separator_subst(real_newpath, separator);
1620 ok = imap_cmd_rename(session, real_oldpath, real_newpath);
1621 if (ok != IMAP_SUCCESS) {
1622 log_warning(_("can't rename mailbox: %s to %s\n"),
1623 real_oldpath, real_newpath);
1624 g_free(real_oldpath);
1626 g_free(real_newpath);
1631 item->name = g_strdup(name);
1633 old_cache_dir = folder_item_get_path(item);
1635 paths[0] = g_strdup(item->path);
1637 g_node_traverse(item->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1638 imap_rename_folder_func, paths);
1640 if (is_dir_exist(old_cache_dir)) {
1641 new_cache_dir = folder_item_get_path(item);
1642 if (rename(old_cache_dir, new_cache_dir) < 0) {
1643 FILE_OP_ERROR(old_cache_dir, "rename");
1645 g_free(new_cache_dir);
1648 g_free(old_cache_dir);
1651 g_free(real_oldpath);
1652 g_free(real_newpath);
1657 static gint imap_remove_folder_real(Folder *folder, FolderItem *item)
1660 IMAPSession *session;
1663 gint exists, recent, unseen;
1664 guint32 uid_validity;
1666 g_return_val_if_fail(folder != NULL, -1);
1667 g_return_val_if_fail(item != NULL, -1);
1668 g_return_val_if_fail(item->path != NULL, -1);
1670 session = imap_session_get(folder);
1674 path = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1676 ok = imap_cmd_examine(session, "INBOX",
1677 &exists, &recent, &unseen, &uid_validity, FALSE);
1678 if (ok != IMAP_SUCCESS) {
1683 ok = imap_cmd_delete(session, path);
1684 if (ok != IMAP_SUCCESS) {
1685 log_warning(_("can't delete mailbox\n"));
1691 cache_dir = folder_item_get_path(item);
1692 if (is_dir_exist(cache_dir) && remove_dir_recursive(cache_dir) < 0)
1693 g_warning("can't remove directory '%s'\n", cache_dir);
1695 folder_item_remove(item);
1700 static gint imap_remove_folder(Folder *folder, FolderItem *item)
1704 g_return_val_if_fail(item != NULL, -1);
1705 g_return_val_if_fail(item->folder != NULL, -1);
1706 g_return_val_if_fail(item->node != NULL, -1);
1708 node = item->node->children;
1709 while (node != NULL) {
1711 if (imap_remove_folder(folder, FOLDER_ITEM(node->data)) < 0)
1715 debug_print("IMAP removing %s\n", item->path);
1717 if (imap_remove_all_msg(folder, item) < 0)
1719 return imap_remove_folder_real(folder, item);
1722 typedef struct _uncached_data {
1723 IMAPSession *session;
1725 MsgNumberList *numlist;
1731 static void *imap_get_uncached_messages_thread(void *data)
1733 uncached_data *stuff = (uncached_data *)data;
1734 IMAPSession *session = stuff->session;
1735 FolderItem *item = stuff->item;
1736 MsgNumberList *numlist = stuff->numlist;
1738 GSList *newlist = NULL;
1739 GSList *llast = NULL;
1740 GSList *seq_list, *cur;
1742 debug_print("uncached_messages\n");
1744 if (session == NULL || item == NULL || item->folder == NULL
1745 || FOLDER_CLASS(item->folder) != &imap_class) {
1750 seq_list = imap_get_lep_set_from_numlist(numlist);
1751 debug_print("get msgs info\n");
1752 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
1753 struct mailimap_set * imapset;
1759 imapset = cur->data;
1761 r = imap_threaded_fetch_env(session->folder,
1762 imapset, &env_list);
1763 if (r != MAILIMAP_NO_ERROR)
1767 for(i = 0 ; i < carray_count(env_list) ; i ++) {
1768 struct imap_fetch_env_info * info;
1771 info = carray_get(env_list, i);
1772 msginfo = imap_envelope_from_lep(info, item);
1773 msginfo->folder = item;
1775 llast = newlist = g_slist_append(newlist, msginfo);
1777 llast = g_slist_append(llast, msginfo);
1778 llast = llast->next;
1783 imap_fetch_env_free(env_list);
1786 session_set_access_time(SESSION(session));
1791 #define MAX_MSG_NUM 50
1793 static GSList *imap_get_uncached_messages(IMAPSession *session,
1795 MsgNumberList *numlist)
1797 GSList *result = NULL;
1799 uncached_data *data = g_new0(uncached_data, 1);
1804 data->total = g_slist_length(numlist);
1805 debug_print("messages list : %i\n", data->total);
1807 while (cur != NULL) {
1808 GSList * partial_result;
1816 while (count < MAX_MSG_NUM) {
1821 if (newlist == NULL)
1822 llast = newlist = g_slist_append(newlist, p);
1824 llast = g_slist_append(llast, p);
1825 llast = llast->next;
1835 data->session = session;
1837 data->numlist = newlist;
1840 if (prefs_common.work_offline && !imap_gtk_should_override()) {
1846 (GSList *)imap_get_uncached_messages_thread(data);
1848 statusbar_progress_all(data->cur,data->total, 1);
1850 g_slist_free(newlist);
1852 result = g_slist_concat(result, partial_result);
1856 statusbar_progress_all(0,0,0);
1857 statusbar_pop_all();
1862 static void imap_delete_all_cached_messages(FolderItem *item)
1866 g_return_if_fail(item != NULL);
1867 g_return_if_fail(item->folder != NULL);
1868 g_return_if_fail(FOLDER_CLASS(item->folder) == &imap_class);
1870 debug_print("Deleting all cached messages...\n");
1872 dir = folder_item_get_path(item);
1873 if (is_dir_exist(dir))
1874 remove_all_numbered_files(dir);
1877 debug_print("done.\n");
1880 static IMAPNameSpace *imap_find_namespace_from_list(GList *ns_list,
1883 IMAPNameSpace *namespace = NULL;
1884 gchar *tmp_path, *name;
1886 if (!path) path = "";
1888 for (; ns_list != NULL; ns_list = ns_list->next) {
1889 IMAPNameSpace *tmp_ns = ns_list->data;
1891 Xstrcat_a(tmp_path, path, "/", return namespace);
1892 Xstrdup_a(name, tmp_ns->name, return namespace);
1893 if (tmp_ns->separator && tmp_ns->separator != '/') {
1894 subst_char(tmp_path, tmp_ns->separator, '/');
1895 subst_char(name, tmp_ns->separator, '/');
1897 if (strncmp(tmp_path, name, strlen(name)) == 0)
1904 static IMAPNameSpace *imap_find_namespace(IMAPFolder *folder,
1907 IMAPNameSpace *namespace;
1909 g_return_val_if_fail(folder != NULL, NULL);
1911 namespace = imap_find_namespace_from_list(folder->ns_personal, path);
1912 if (namespace) return namespace;
1913 namespace = imap_find_namespace_from_list(folder->ns_others, path);
1914 if (namespace) return namespace;
1915 namespace = imap_find_namespace_from_list(folder->ns_shared, path);
1916 if (namespace) return namespace;
1922 static gchar imap_get_path_separator(IMAPFolder *folder, const gchar *path)
1924 IMAPNameSpace *namespace;
1925 gchar separator = '/';
1927 if (folder->last_seen_separator == 0) {
1929 int r = imap_threaded_list((Folder *)folder, "", "", &lep_list);
1930 if (r != MAILIMAP_NO_ERROR) {
1931 log_warning(_("LIST failed\n"));
1935 if (clist_count(lep_list) > 0) {
1936 clistiter * iter = clist_begin(lep_list);
1937 struct mailimap_mailbox_list * mb;
1938 mb = clist_content(iter);
1940 folder->last_seen_separator = mb->mb_delimiter;
1941 debug_print("got separator: %c\n", folder->last_seen_separator);
1943 mailimap_list_result_free(lep_list);
1946 if (folder->last_seen_separator != 0) {
1947 debug_print("using separator: %c\n", folder->last_seen_separator);
1948 return folder->last_seen_separator;
1951 namespace = imap_find_namespace(folder, path);
1952 if (namespace && namespace->separator)
1953 separator = namespace->separator;
1958 static gchar *imap_get_real_path(IMAPFolder *folder, const gchar *path)
1963 g_return_val_if_fail(folder != NULL, NULL);
1964 g_return_val_if_fail(path != NULL, NULL);
1966 real_path = imap_utf8_to_modified_utf7(path);
1967 separator = imap_get_path_separator(folder, path);
1968 imap_path_separator_subst(real_path, separator);
1973 static gint imap_set_message_flags(IMAPSession *session,
1974 MsgNumberList *numlist,
1982 seq_list = imap_get_lep_set_from_numlist(numlist);
1984 for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) {
1985 struct mailimap_set * imapset;
1987 imapset = cur->data;
1989 ok = imap_cmd_store(session, imapset,
1993 imap_lep_set_free(seq_list);
1995 return IMAP_SUCCESS;
1998 typedef struct _select_data {
1999 IMAPSession *session;
2004 guint32 *uid_validity;
2008 static gint imap_select(IMAPSession *session, IMAPFolder *folder,
2010 gint *exists, gint *recent, gint *unseen,
2011 guint32 *uid_validity, gboolean block)
2015 gint exists_, recent_, unseen_;
2016 guint32 uid_validity_;
2018 if (!exists || !recent || !unseen || !uid_validity) {
2019 if (session->mbox && strcmp(session->mbox, path) == 0)
2020 return IMAP_SUCCESS;
2024 uid_validity = &uid_validity_;
2027 g_free(session->mbox);
2028 session->mbox = NULL;
2030 real_path = imap_get_real_path(folder, path);
2032 ok = imap_cmd_select(session, real_path,
2033 exists, recent, unseen, uid_validity, block);
2034 if (ok != IMAP_SUCCESS)
2035 log_warning(_("can't select folder: %s\n"), real_path);
2037 session->mbox = g_strdup(path);
2038 session->folder_content_changed = FALSE;
2045 static gint imap_status(IMAPSession *session, IMAPFolder *folder,
2047 gint *messages, gint *recent,
2048 guint32 *uid_next, guint32 *uid_validity,
2049 gint *unseen, gboolean block)
2053 struct mailimap_mailbox_data_status * data_status;
2057 real_path = imap_get_real_path(folder, path);
2059 r = imap_threaded_status(FOLDER(folder), real_path, &data_status);
2061 if (r != MAILIMAP_NO_ERROR) {
2062 debug_print("status err %d\n", r);
2066 if (data_status->st_info_list == NULL) {
2067 mailimap_mailbox_data_status_free(data_status);
2068 debug_print("status->st_info_list == NULL\n");
2073 for(iter = clist_begin(data_status->st_info_list) ; iter != NULL ;
2074 iter = clist_next(iter)) {
2075 struct mailimap_status_info * info;
2077 info = clist_content(iter);
2078 switch (info->st_att) {
2079 case MAILIMAP_STATUS_ATT_MESSAGES:
2080 * messages = info->st_value;
2081 got_values |= 1 << 0;
2084 case MAILIMAP_STATUS_ATT_RECENT:
2085 * recent = info->st_value;
2086 got_values |= 1 << 1;
2089 case MAILIMAP_STATUS_ATT_UIDNEXT:
2090 * uid_next = info->st_value;
2091 got_values |= 1 << 2;
2094 case MAILIMAP_STATUS_ATT_UIDVALIDITY:
2095 * uid_validity = info->st_value;
2096 got_values |= 1 << 3;
2099 case MAILIMAP_STATUS_ATT_UNSEEN:
2100 * unseen = info->st_value;
2101 got_values |= 1 << 4;
2105 mailimap_mailbox_data_status_free(data_status);
2107 if (got_values != ((1 << 4) + (1 << 3) +
2108 (1 << 2) + (1 << 1) + (1 << 0))) {
2109 debug_print("status: incomplete values received (%d)\n", got_values);
2112 return IMAP_SUCCESS;
2115 static void imap_free_capabilities(IMAPSession *session)
2117 g_strfreev(session->capability);
2118 session->capability = NULL;
2121 /* low-level IMAP4rev1 commands */
2124 static gint imap_cmd_authenticate(IMAPSession *session, const gchar *user,
2125 const gchar *pass, IMAPAuthType type)
2132 gchar hexdigest[33];
2136 auth_type = "CRAM-MD5";
2138 imap_gen_send(session, "AUTHENTICATE %s", auth_type);
2139 ok = imap_gen_recv(session, &buf);
2140 if (ok != IMAP_SUCCESS || buf[0] != '+' || buf[1] != ' ') {
2145 challenge = g_malloc(strlen(buf + 2) + 1);
2146 challenge_len = base64_decode(challenge, buf + 2, -1);
2147 challenge[challenge_len] = '\0';
2150 md5_hex_hmac(hexdigest, challenge, challenge_len, pass, strlen(pass));
2153 response = g_strdup_printf("%s %s", user, hexdigest);
2154 response64 = g_malloc((strlen(response) + 3) * 2 + 1);
2155 base64_encode(response64, response, strlen(response));
2158 sock_puts(SESSION(session)->sock, response64);
2159 ok = imap_cmd_ok(session, NULL);
2160 if (ok != IMAP_SUCCESS)
2161 log_warning(_("IMAP4 authentication failed.\n"));
2167 static gint imap_cmd_login(IMAPSession *session,
2168 const gchar *user, const gchar *pass)
2172 static time_t last_login_err = 0;
2174 log_print("IMAP4> Logging in to %s\n", SESSION(session)->server);
2175 r = imap_threaded_login(session->folder, user, pass);
2176 if (r != MAILIMAP_NO_ERROR) {
2177 if (time(NULL) - last_login_err > 10) {
2178 alertpanel_error(_("Connection to %s failed: login refused."),
2179 SESSION(session)->server);
2181 last_login_err = time(NULL);
2182 log_error("IMAP4< Error logging in to %s\n",
2183 SESSION(session)->server);
2191 static gint imap_cmd_logout(IMAPSession *session)
2193 imap_threaded_disconnect(session->folder);
2195 return IMAP_SUCCESS;
2198 static gint imap_cmd_noop(IMAPSession *session)
2201 unsigned int exists;
2203 r = imap_threaded_noop(session->folder, &exists);
2204 if (r != MAILIMAP_NO_ERROR) {
2205 debug_print("noop err %d\n", r);
2208 session->exists = exists;
2209 session_set_access_time(SESSION(session));
2211 return IMAP_SUCCESS;
2215 static gint imap_cmd_starttls(IMAPSession *session)
2219 r = imap_threaded_starttls(session->folder);
2220 if (r != MAILIMAP_NO_ERROR) {
2221 debug_print("starttls err %d\n", r);
2224 return IMAP_SUCCESS;
2228 static gint imap_cmd_select(IMAPSession *session, const gchar *folder,
2229 gint *exists, gint *recent, gint *unseen,
2230 guint32 *uid_validity, gboolean block)
2234 r = imap_threaded_select(session->folder, folder,
2235 exists, recent, unseen, uid_validity);
2236 if (r != MAILIMAP_NO_ERROR) {
2237 debug_print("select err %d\n", r);
2240 return IMAP_SUCCESS;
2243 static gint imap_cmd_examine(IMAPSession *session, const gchar *folder,
2244 gint *exists, gint *recent, gint *unseen,
2245 guint32 *uid_validity, gboolean block)
2249 r = imap_threaded_examine(session->folder, folder,
2250 exists, recent, unseen, uid_validity);
2251 if (r != MAILIMAP_NO_ERROR) {
2252 debug_print("examine err %d\n", r);
2256 return IMAP_SUCCESS;
2259 static gint imap_cmd_create(IMAPSession *session, const gchar *folder)
2263 r = imap_threaded_create(session->folder, folder);
2264 if (r != MAILIMAP_NO_ERROR) {
2269 return IMAP_SUCCESS;
2272 static gint imap_cmd_rename(IMAPSession *session, const gchar *old_folder,
2273 const gchar *new_folder)
2277 r = imap_threaded_rename(session->folder, old_folder,
2279 if (r != MAILIMAP_NO_ERROR) {
2284 return IMAP_SUCCESS;
2287 static gint imap_cmd_delete(IMAPSession *session, const gchar *folder)
2292 r = imap_threaded_delete(session->folder, folder);
2293 if (r != MAILIMAP_NO_ERROR) {
2298 return IMAP_SUCCESS;
2301 typedef struct _fetch_data {
2302 IMAPSession *session;
2304 const gchar *filename;
2310 static void *imap_cmd_fetch_thread(void *data)
2312 fetch_data *stuff = (fetch_data *)data;
2313 IMAPSession *session = stuff->session;
2314 guint32 uid = stuff->uid;
2315 const gchar *filename = stuff->filename;
2319 r = imap_threaded_fetch_content(session->folder,
2323 r = imap_threaded_fetch_content(session->folder,
2326 if (r != MAILIMAP_NO_ERROR) {
2327 debug_print("fetch err %d\n", r);
2328 return GINT_TO_POINTER(IMAP_ERROR);
2330 return GINT_TO_POINTER(IMAP_SUCCESS);
2333 static gint imap_cmd_fetch(IMAPSession *session, guint32 uid,
2334 const gchar *filename, gboolean headers,
2337 fetch_data *data = g_new0(fetch_data, 1);
2340 data->session = session;
2342 data->filename = filename;
2343 data->headers = headers;
2346 if (prefs_common.work_offline && !imap_gtk_should_override()) {
2351 result = GPOINTER_TO_INT(imap_cmd_fetch_thread(data));
2357 static gint imap_cmd_append(IMAPSession *session, const gchar *destfolder,
2358 const gchar *file, IMAPFlags flags,
2361 struct mailimap_flag_list * flag_list;
2364 g_return_val_if_fail(file != NULL, IMAP_ERROR);
2366 flag_list = imap_flag_to_lep(flags);
2367 r = imap_threaded_append(session->folder, destfolder,
2370 if (new_uid != NULL)
2373 if (r != MAILIMAP_NO_ERROR) {
2374 debug_print("append err %d\n", r);
2377 return IMAP_SUCCESS;
2380 static gint imap_cmd_copy(IMAPSession *session, struct mailimap_set * set,
2381 const gchar *destfolder, GRelation *uid_mapping)
2385 g_return_val_if_fail(session != NULL, IMAP_ERROR);
2386 g_return_val_if_fail(set != NULL, IMAP_ERROR);
2387 g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
2389 r = imap_threaded_copy(session->folder, set, destfolder);
2390 if (r != MAILIMAP_NO_ERROR) {
2395 return IMAP_SUCCESS;
2398 static gint imap_cmd_store(IMAPSession *session, struct mailimap_set * set,
2399 IMAPFlags flags, int do_add)
2402 struct mailimap_flag_list * flag_list;
2403 struct mailimap_store_att_flags * store_att_flags;
2405 flag_list = imap_flag_to_lep(flags);
2409 mailimap_store_att_flags_new_add_flags_silent(flag_list);
2412 mailimap_store_att_flags_new_remove_flags_silent(flag_list);
2414 r = imap_threaded_store(session->folder, set, store_att_flags);
2415 if (r != MAILIMAP_NO_ERROR) {
2420 return IMAP_SUCCESS;
2423 static gint imap_cmd_expunge(IMAPSession *session)
2427 if (prefs_common.work_offline && !imap_gtk_should_override()) {
2431 r = imap_threaded_expunge(session->folder);
2432 if (r != MAILIMAP_NO_ERROR) {
2437 return IMAP_SUCCESS;
2440 static void imap_path_separator_subst(gchar *str, gchar separator)
2443 gboolean in_escape = FALSE;
2445 if (!separator || separator == '/') return;
2447 for (p = str; *p != '\0'; p++) {
2448 if (*p == '/' && !in_escape)
2450 else if (*p == '&' && *(p + 1) != '-' && !in_escape)
2452 else if (*p == '-' && in_escape)
2457 static gchar *imap_modified_utf7_to_utf8(const gchar *mutf7_str)
2459 static iconv_t cd = (iconv_t)-1;
2460 static gboolean iconv_ok = TRUE;
2463 size_t norm_utf7_len;
2465 gchar *to_str, *to_p;
2467 gboolean in_escape = FALSE;
2469 if (!iconv_ok) return g_strdup(mutf7_str);
2471 if (cd == (iconv_t)-1) {
2472 cd = iconv_open(CS_INTERNAL, CS_UTF_7);
2473 if (cd == (iconv_t)-1) {
2474 g_warning("iconv cannot convert UTF-7 to %s\n",
2477 return g_strdup(mutf7_str);
2481 /* modified UTF-7 to normal UTF-7 conversion */
2482 norm_utf7 = g_string_new(NULL);
2484 for (p = mutf7_str; *p != '\0'; p++) {
2485 /* replace: '&' -> '+',
2487 escaped ',' -> '/' */
2488 if (!in_escape && *p == '&') {
2489 if (*(p + 1) != '-') {
2490 g_string_append_c(norm_utf7, '+');
2493 g_string_append_c(norm_utf7, '&');
2496 } else if (in_escape && *p == ',') {
2497 g_string_append_c(norm_utf7, '/');
2498 } else if (in_escape && *p == '-') {
2499 g_string_append_c(norm_utf7, '-');
2502 g_string_append_c(norm_utf7, *p);
2506 norm_utf7_p = norm_utf7->str;
2507 norm_utf7_len = norm_utf7->len;
2508 to_len = strlen(mutf7_str) * 5;
2509 to_p = to_str = g_malloc(to_len + 1);
2511 if (iconv(cd, (ICONV_CONST gchar **)&norm_utf7_p, &norm_utf7_len,
2512 &to_p, &to_len) == -1) {
2513 g_warning(_("iconv cannot convert UTF-7 to %s\n"),
2514 conv_get_locale_charset_str());
2515 g_string_free(norm_utf7, TRUE);
2517 return g_strdup(mutf7_str);
2520 /* second iconv() call for flushing */
2521 iconv(cd, NULL, NULL, &to_p, &to_len);
2522 g_string_free(norm_utf7, TRUE);
2528 static gchar *imap_utf8_to_modified_utf7(const gchar *from)
2530 static iconv_t cd = (iconv_t)-1;
2531 static gboolean iconv_ok = TRUE;
2532 gchar *norm_utf7, *norm_utf7_p;
2533 size_t from_len, norm_utf7_len;
2535 gchar *from_tmp, *to, *p;
2536 gboolean in_escape = FALSE;
2538 if (!iconv_ok) return g_strdup(from);
2540 if (cd == (iconv_t)-1) {
2541 cd = iconv_open(CS_UTF_7, CS_INTERNAL);
2542 if (cd == (iconv_t)-1) {
2543 g_warning(_("iconv cannot convert %s to UTF-7\n"),
2546 return g_strdup(from);
2550 /* UTF-8 to normal UTF-7 conversion */
2551 Xstrdup_a(from_tmp, from, return g_strdup(from));
2552 from_len = strlen(from);
2553 norm_utf7_len = from_len * 5;
2554 Xalloca(norm_utf7, norm_utf7_len + 1, return g_strdup(from));
2555 norm_utf7_p = norm_utf7;
2557 #define IS_PRINT(ch) (isprint(ch) && IS_ASCII(ch))
2559 while (from_len > 0) {
2560 if (*from_tmp == '+') {
2561 *norm_utf7_p++ = '+';
2562 *norm_utf7_p++ = '-';
2566 } else if (IS_PRINT(*(guchar *)from_tmp)) {
2567 /* printable ascii char */
2568 *norm_utf7_p = *from_tmp;
2574 size_t conv_len = 0;
2576 /* unprintable char: convert to UTF-7 */
2578 while (!IS_PRINT(*(guchar *)p) && conv_len < from_len) {
2579 conv_len += g_utf8_skip[*(guchar *)p];
2580 p += g_utf8_skip[*(guchar *)p];
2583 from_len -= conv_len;
2584 if (iconv(cd, (ICONV_CONST gchar **)&from_tmp,
2586 &norm_utf7_p, &norm_utf7_len) == -1) {
2587 g_warning(_("iconv cannot convert UTF-8 to UTF-7\n"));
2588 return g_strdup(from);
2591 /* second iconv() call for flushing */
2592 iconv(cd, NULL, NULL, &norm_utf7_p, &norm_utf7_len);
2598 *norm_utf7_p = '\0';
2599 to_str = g_string_new(NULL);
2600 for (p = norm_utf7; p < norm_utf7_p; p++) {
2601 /* replace: '&' -> "&-",
2604 BASE64 '/' -> ',' */
2605 if (!in_escape && *p == '&') {
2606 g_string_append(to_str, "&-");
2607 } else if (!in_escape && *p == '+') {
2608 if (*(p + 1) == '-') {
2609 g_string_append_c(to_str, '+');
2612 g_string_append_c(to_str, '&');
2615 } else if (in_escape && *p == '/') {
2616 g_string_append_c(to_str, ',');
2617 } else if (in_escape && *p == '-') {
2618 g_string_append_c(to_str, '-');
2621 g_string_append_c(to_str, *p);
2627 g_string_append_c(to_str, '-');
2631 g_string_free(to_str, FALSE);
2636 static gboolean imap_rename_folder_func(GNode *node, gpointer data)
2638 FolderItem *item = node->data;
2639 gchar **paths = data;
2640 const gchar *oldpath = paths[0];
2641 const gchar *newpath = paths[1];
2643 gchar *new_itempath;
2646 oldpathlen = strlen(oldpath);
2647 if (strncmp(oldpath, item->path, oldpathlen) != 0) {
2648 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
2652 base = item->path + oldpathlen;
2653 while (*base == G_DIR_SEPARATOR) base++;
2655 new_itempath = g_strdup(newpath);
2657 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
2660 item->path = new_itempath;
2665 typedef struct _get_list_uid_data {
2667 IMAPFolderItem *item;
2668 GSList **msgnum_list;
2670 } get_list_uid_data;
2672 static void *get_list_of_uids_thread(void *data)
2674 get_list_uid_data *stuff = (get_list_uid_data *)data;
2675 Folder *folder = stuff->folder;
2676 IMAPFolderItem *item = stuff->item;
2677 GSList **msgnum_list = stuff->msgnum_list;
2678 gint ok, nummsgs = 0, lastuid_old;
2679 IMAPSession *session;
2680 GSList *uidlist, *elem;
2681 struct mailimap_set * set;
2682 clist * lep_uidlist;
2685 session = imap_session_get(folder);
2686 if (session == NULL) {
2688 return GINT_TO_POINTER(-1);
2691 ok = imap_select(session, IMAP_FOLDER(folder), item->item.path,
2692 NULL, NULL, NULL, NULL, TRUE);
2693 if (ok != IMAP_SUCCESS) {
2695 return GINT_TO_POINTER(-1);
2700 set = mailimap_set_new_interval(item->lastuid + 1, 0);
2701 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SIMPLE, set,
2703 if (r == MAILIMAP_NO_ERROR) {
2704 GSList * fetchuid_list;
2707 imap_uid_list_from_lep(lep_uidlist);
2708 uidlist = g_slist_concat(fetchuid_list, uidlist);
2711 GSList * fetchuid_list;
2712 carray * lep_uidtab;
2714 r = imap_threaded_fetch_uid(folder, item->lastuid + 1,
2716 if (r == MAILIMAP_NO_ERROR) {
2718 imap_uid_list_from_lep_tab(lep_uidtab);
2719 uidlist = g_slist_concat(fetchuid_list, uidlist);
2723 lastuid_old = item->lastuid;
2724 *msgnum_list = g_slist_copy(item->uid_list);
2725 nummsgs = g_slist_length(*msgnum_list);
2726 debug_print("Got %d uids from cache\n", g_slist_length(item->uid_list));
2728 for (elem = uidlist; elem != NULL; elem = g_slist_next(elem)) {
2731 msgnum = GPOINTER_TO_INT(elem->data);
2732 if (msgnum > lastuid_old) {
2733 *msgnum_list = g_slist_prepend(*msgnum_list, GINT_TO_POINTER(msgnum));
2734 item->uid_list = g_slist_prepend(item->uid_list, GINT_TO_POINTER(msgnum));
2737 if(msgnum > item->lastuid)
2738 item->lastuid = msgnum;
2741 g_slist_free(uidlist);
2744 return GINT_TO_POINTER(nummsgs);
2747 static gint get_list_of_uids(Folder *folder, IMAPFolderItem *item, GSList **msgnum_list)
2750 get_list_uid_data *data = g_new0(get_list_uid_data, 1);
2752 data->folder = folder;
2754 data->msgnum_list = msgnum_list;
2756 if (prefs_common.work_offline && !imap_gtk_should_override()) {
2761 result = GPOINTER_TO_INT(get_list_of_uids_thread(data));
2767 gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list, gboolean *old_uids_valid)
2769 IMAPFolderItem *item = (IMAPFolderItem *)_item;
2770 IMAPSession *session;
2771 gint ok, nummsgs = 0, exists, recent, uid_val, uid_next, unseen;
2772 GSList *uidlist = NULL;
2774 gboolean selected_folder;
2776 debug_print("get_num_list\n");
2778 g_return_val_if_fail(folder != NULL, -1);
2779 g_return_val_if_fail(item != NULL, -1);
2780 g_return_val_if_fail(item->item.path != NULL, -1);
2781 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
2782 g_return_val_if_fail(folder->account != NULL, -1);
2784 session = imap_session_get(folder);
2785 g_return_val_if_fail(session != NULL, -1);
2787 selected_folder = (session->mbox != NULL) &&
2788 (!strcmp(session->mbox, item->item.path));
2789 if (selected_folder) {
2790 ok = imap_cmd_noop(session);
2791 if (ok != IMAP_SUCCESS) {
2795 exists = session->exists;
2797 *old_uids_valid = TRUE;
2799 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
2800 &exists, &recent, &uid_next, &uid_val, &unseen, FALSE);
2801 if (ok != IMAP_SUCCESS) {
2805 if(item->item.mtime == uid_val)
2806 *old_uids_valid = TRUE;
2808 *old_uids_valid = FALSE;
2810 debug_print("Freeing imap uid cache\n");
2812 g_slist_free(item->uid_list);
2813 item->uid_list = NULL;
2815 item->item.mtime = uid_val;
2817 imap_delete_all_cached_messages((FolderItem *)item);
2821 if (!selected_folder)
2822 item->uid_next = uid_next;
2824 /* If old uid_next matches new uid_next we can be sure no message
2825 was added to the folder */
2826 if (( selected_folder && !session->folder_content_changed) ||
2827 (!selected_folder && uid_next == item->uid_next)) {
2828 nummsgs = g_slist_length(item->uid_list);
2830 /* If number of messages is still the same we
2831 know our caches message numbers are still valid,
2832 otherwise if the number of messages has decrease
2833 we discard our cache to start a new scan to find
2834 out which numbers have been removed */
2835 if (exists == nummsgs) {
2836 *msgnum_list = g_slist_copy(item->uid_list);
2838 } else if (exists < nummsgs) {
2839 debug_print("Freeing imap uid cache");
2841 g_slist_free(item->uid_list);
2842 item->uid_list = NULL;
2847 *msgnum_list = NULL;
2851 nummsgs = get_list_of_uids(folder, item, &uidlist);
2858 if (nummsgs != exists) {
2859 /* Cache contains more messages then folder, we have cached
2860 an old UID of a message that was removed and new messages
2861 have been added too, otherwise the uid_next check would
2863 debug_print("Freeing imap uid cache");
2865 g_slist_free(item->uid_list);
2866 item->uid_list = NULL;
2868 g_slist_free(*msgnum_list);
2870 nummsgs = get_list_of_uids(folder, item, &uidlist);
2873 *msgnum_list = uidlist;
2875 dir = folder_item_get_path((FolderItem *)item);
2876 debug_print("removing old messages from %s\n", dir);
2877 remove_numbered_files_not_in_list(dir, *msgnum_list);
2880 debug_print("get_num_list - ok - %i\n", nummsgs);
2885 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item)
2890 flags.perm_flags = MSG_NEW|MSG_UNREAD;
2891 flags.tmp_flags = 0;
2893 g_return_val_if_fail(item != NULL, NULL);
2894 g_return_val_if_fail(file != NULL, NULL);
2896 if (item->stype == F_QUEUE) {
2897 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
2898 } else if (item->stype == F_DRAFT) {
2899 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
2902 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
2903 if (!msginfo) return NULL;
2905 msginfo->plaintext_file = g_strdup(file);
2906 msginfo->folder = item;
2911 GSList *imap_get_msginfos(Folder *folder, FolderItem *item,
2912 GSList *msgnum_list)
2914 IMAPSession *session;
2915 MsgInfoList *ret = NULL;
2918 debug_print("get_msginfos\n");
2920 g_return_val_if_fail(folder != NULL, NULL);
2921 g_return_val_if_fail(item != NULL, NULL);
2922 g_return_val_if_fail(msgnum_list != NULL, NULL);
2924 session = imap_session_get(folder);
2925 g_return_val_if_fail(session != NULL, NULL);
2927 debug_print("IMAP getting msginfos\n");
2928 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
2929 NULL, NULL, NULL, NULL, FALSE);
2930 if (ok != IMAP_SUCCESS)
2933 if (!(item->stype == F_QUEUE || item->stype == F_DRAFT)) {
2934 ret = g_slist_concat(ret,
2935 imap_get_uncached_messages(session, item,
2938 MsgNumberList *sorted_list, *elem;
2939 gint startnum, lastnum;
2941 sorted_list = g_slist_sort(g_slist_copy(msgnum_list), g_int_compare);
2943 startnum = lastnum = GPOINTER_TO_INT(sorted_list->data);
2945 for (elem = sorted_list;; elem = g_slist_next(elem)) {
2949 num = GPOINTER_TO_INT(elem->data);
2951 if (num > lastnum + 1 || elem == NULL) {
2953 for (i = startnum; i <= lastnum; ++i) {
2956 file = imap_fetch_msg(folder, item, i);
2958 MsgInfo *msginfo = imap_parse_msg(file, item);
2959 if (msginfo != NULL) {
2960 msginfo->msgnum = i;
2961 ret = g_slist_append(ret, msginfo);
2975 g_slist_free(sorted_list);
2981 MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid)
2983 MsgInfo *msginfo = NULL;
2984 MsgInfoList *msginfolist;
2985 MsgNumberList numlist;
2987 numlist.next = NULL;
2988 numlist.data = GINT_TO_POINTER(uid);
2990 msginfolist = imap_get_msginfos(folder, item, &numlist);
2991 if (msginfolist != NULL) {
2992 msginfo = msginfolist->data;
2993 g_slist_free(msginfolist);
2999 gboolean imap_scan_required(Folder *folder, FolderItem *_item)
3001 IMAPSession *session;
3002 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3003 gint ok, exists = 0, recent = 0, unseen = 0;
3004 guint32 uid_next, uid_val = 0;
3005 gboolean selected_folder;
3007 g_return_val_if_fail(folder != NULL, FALSE);
3008 g_return_val_if_fail(item != NULL, FALSE);
3009 g_return_val_if_fail(item->item.folder != NULL, FALSE);
3010 g_return_val_if_fail(FOLDER_CLASS(item->item.folder) == &imap_class, FALSE);
3012 if (item->item.path == NULL)
3015 session = imap_session_get(folder);
3016 g_return_val_if_fail(session != NULL, FALSE);
3018 selected_folder = (session->mbox != NULL) &&
3019 (!strcmp(session->mbox, item->item.path));
3020 if (selected_folder) {
3021 ok = imap_cmd_noop(session);
3022 if (ok != IMAP_SUCCESS)
3025 if (session->folder_content_changed
3026 || session->exists != item->item.total_msgs)
3029 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
3030 &exists, &recent, &uid_next, &uid_val, &unseen, FALSE);
3031 if (ok != IMAP_SUCCESS)
3034 if ((uid_next != item->uid_next) || (exists != item->item.total_msgs))
3041 void imap_change_flags(Folder *folder, FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
3043 IMAPSession *session;
3044 IMAPFlags flags_set = 0, flags_unset = 0;
3045 gint ok = IMAP_SUCCESS;
3046 MsgNumberList numlist;
3047 hashtable_data *ht_data = NULL;
3049 g_return_if_fail(folder != NULL);
3050 g_return_if_fail(folder->klass == &imap_class);
3051 g_return_if_fail(item != NULL);
3052 g_return_if_fail(item->folder == folder);
3053 g_return_if_fail(msginfo != NULL);
3054 g_return_if_fail(msginfo->folder == item);
3056 session = imap_session_get(folder);
3060 if ((ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
3061 NULL, NULL, NULL, NULL, FALSE)) != IMAP_SUCCESS) {
3065 if (!MSG_IS_MARKED(msginfo->flags) && (newflags & MSG_MARKED))
3066 flags_set |= IMAP_FLAG_FLAGGED;
3067 if ( MSG_IS_MARKED(msginfo->flags) && !(newflags & MSG_MARKED))
3068 flags_unset |= IMAP_FLAG_FLAGGED;
3070 if (!MSG_IS_UNREAD(msginfo->flags) && (newflags & MSG_UNREAD))
3071 flags_unset |= IMAP_FLAG_SEEN;
3072 if ( MSG_IS_UNREAD(msginfo->flags) && !(newflags & MSG_UNREAD))
3073 flags_set |= IMAP_FLAG_SEEN;
3075 if (!MSG_IS_REPLIED(msginfo->flags) && (newflags & MSG_REPLIED))
3076 flags_set |= IMAP_FLAG_ANSWERED;
3077 if ( MSG_IS_REPLIED(msginfo->flags) && !(newflags & MSG_REPLIED))
3078 flags_set |= IMAP_FLAG_ANSWERED;
3080 numlist.next = NULL;
3081 numlist.data = GINT_TO_POINTER(msginfo->msgnum);
3083 if (IMAP_FOLDER_ITEM(item)->batching) {
3084 /* instead of performing an UID STORE command for each message change,
3085 * as a lot of them can change "together", we just fill in hashtables
3086 * and defer the treatment so that we're able to send only one
3089 debug_print("IMAP batch mode on, deferring flags change\n");
3091 ht_data = g_hash_table_lookup(flags_set_table, GINT_TO_POINTER(flags_set));
3092 if (ht_data == NULL) {
3093 ht_data = g_new0(hashtable_data, 1);
3094 ht_data->session = session;
3095 g_hash_table_insert(flags_set_table, GINT_TO_POINTER(flags_set), ht_data);
3097 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
3098 ht_data->msglist = g_slist_prepend(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum));
3101 ht_data = g_hash_table_lookup(flags_unset_table, GINT_TO_POINTER(flags_unset));
3102 if (ht_data == NULL) {
3103 ht_data = g_new0(hashtable_data, 1);
3104 ht_data->session = session;
3105 g_hash_table_insert(flags_unset_table, GINT_TO_POINTER(flags_unset), ht_data);
3107 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
3108 ht_data->msglist = g_slist_prepend(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum));
3111 debug_print("IMAP changing flags\n");
3113 ok = imap_set_message_flags(session, &numlist, flags_set, TRUE);
3114 if (ok != IMAP_SUCCESS) {
3120 ok = imap_set_message_flags(session, &numlist, flags_unset, FALSE);
3121 if (ok != IMAP_SUCCESS) {
3126 msginfo->flags.perm_flags = newflags;
3131 static gint imap_remove_msg(Folder *folder, FolderItem *item, gint uid)
3134 IMAPSession *session;
3136 MsgNumberList numlist;
3138 g_return_val_if_fail(folder != NULL, -1);
3139 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3140 g_return_val_if_fail(item != NULL, -1);
3142 session = imap_session_get(folder);
3143 if (!session) return -1;
3145 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3146 NULL, NULL, NULL, NULL, FALSE);
3147 if (ok != IMAP_SUCCESS)
3150 numlist.next = NULL;
3151 numlist.data = GINT_TO_POINTER(uid);
3153 ok = imap_set_message_flags
3154 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
3155 &numlist, IMAP_FLAG_DELETED, TRUE);
3156 if (ok != IMAP_SUCCESS) {
3157 log_warning(_("can't set deleted flags: %d\n"), uid);
3161 if (!session->uidplus) {
3162 ok = imap_cmd_expunge(session);
3166 uidstr = g_strdup_printf("%u", uid);
3167 ok = imap_cmd_expunge(session);
3170 if (ok != IMAP_SUCCESS) {
3171 log_warning(_("can't expunge\n"));
3175 IMAP_FOLDER_ITEM(item)->uid_list = g_slist_remove(
3176 IMAP_FOLDER_ITEM(item)->uid_list, numlist.data);
3177 dir = folder_item_get_path(item);
3178 if (is_dir_exist(dir))
3179 remove_numbered_files(dir, uid, uid);
3182 return IMAP_SUCCESS;
3185 static gint compare_msginfo(gconstpointer a, gconstpointer b)
3187 return ((MsgInfo *)a)->msgnum - ((MsgInfo *)b)->msgnum;
3190 static guint gslist_find_next_num(MsgNumberList **list, guint num)
3194 g_return_val_if_fail(list != NULL, -1);
3196 for (elem = *list; elem != NULL; elem = g_slist_next(elem))
3197 if (GPOINTER_TO_INT(elem->data) >= num)
3200 return elem != NULL ? GPOINTER_TO_INT(elem->data) : (gint)-1;
3204 * NEW and DELETED flags are not syncronized
3205 * - The NEW/RECENT flags in IMAP folders can not really be directly
3206 * modified by Sylpheed
3207 * - The DELETE/DELETED flag in IMAP and Sylpheed don't have the same
3208 * meaning, in IMAP it always removes the messages from the FolderItem
3209 * in Sylpheed it can mean to move the message to trash
3212 typedef struct _get_flags_data {
3215 MsgInfoList *msginfo_list;
3216 GRelation *msgflags;
3220 static /*gint*/ void *imap_get_flags_thread(void *data)
3222 get_flags_data *stuff = (get_flags_data *)data;
3223 Folder *folder = stuff->folder;
3224 FolderItem *item = stuff->item;
3225 MsgInfoList *msginfo_list = stuff->msginfo_list;
3226 GRelation *msgflags = stuff->msgflags;
3227 IMAPSession *session;
3228 GSList *sorted_list;
3229 GSList *unseen = NULL, *answered = NULL, *flagged = NULL;
3230 GSList *p_unseen, *p_answered, *p_flagged;
3232 GSList *seq_list, *cur;
3233 gboolean reverse_seen = FALSE;
3236 gint exists_cnt, recent_cnt, unseen_cnt, uid_next;
3237 guint32 uidvalidity;
3238 gboolean selected_folder;
3240 if (folder == NULL || item == NULL) {
3242 return GINT_TO_POINTER(-1);
3244 if (msginfo_list == NULL) {
3246 return GINT_TO_POINTER(0);
3249 session = imap_session_get(folder);
3250 if (session == NULL) {
3252 return GINT_TO_POINTER(-1);
3255 selected_folder = (session->mbox != NULL) &&
3256 (!strcmp(session->mbox, item->path));
3258 if (!selected_folder) {
3259 ok = imap_status(session, IMAP_FOLDER(folder), item->path,
3260 &exists_cnt, &recent_cnt, &uid_next, &uidvalidity, &unseen_cnt, TRUE);
3261 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3262 NULL, NULL, NULL, NULL, TRUE);
3263 if (ok != IMAP_SUCCESS) {
3265 return GINT_TO_POINTER(-1);
3268 if (unseen_cnt > exists_cnt / 2)
3269 reverse_seen = TRUE;
3272 if (item->unread_msgs > item->total_msgs / 2)
3273 reverse_seen = TRUE;
3276 cmd_buf = g_string_new(NULL);
3278 sorted_list = g_slist_sort(g_slist_copy(msginfo_list), compare_msginfo);
3280 seq_list = imap_get_lep_set_from_msglist(msginfo_list);
3282 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
3283 struct mailimap_set * imapset;
3284 clist * lep_uidlist;
3287 imapset = cur->data;
3289 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SEEN,
3290 imapset, &lep_uidlist);
3293 r = imap_threaded_search(folder,
3294 IMAP_SEARCH_TYPE_UNSEEN,
3295 imapset, &lep_uidlist);
3297 if (r == MAILIMAP_NO_ERROR) {
3300 uidlist = imap_uid_list_from_lep(lep_uidlist);
3301 mailimap_search_result_free(lep_uidlist);
3303 unseen = g_slist_concat(unseen, uidlist);
3306 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_ANSWERED,
3307 imapset, &lep_uidlist);
3308 if (r == MAILIMAP_NO_ERROR) {
3311 uidlist = imap_uid_list_from_lep(lep_uidlist);
3312 mailimap_search_result_free(lep_uidlist);
3314 answered = g_slist_concat(answered, uidlist);
3317 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_FLAGGED,
3318 imapset, &lep_uidlist);
3319 if (r == MAILIMAP_NO_ERROR) {
3322 uidlist = imap_uid_list_from_lep(lep_uidlist);
3323 mailimap_search_result_free(lep_uidlist);
3325 flagged = g_slist_concat(flagged, uidlist);
3330 p_answered = answered;
3331 p_flagged = flagged;
3333 for (elem = sorted_list; elem != NULL; elem = g_slist_next(elem)) {
3338 msginfo = (MsgInfo *) elem->data;
3339 flags = msginfo->flags.perm_flags;
3340 wasnew = (flags & MSG_NEW);
3341 flags &= ~((reverse_seen ? 0 : MSG_UNREAD | MSG_NEW) | MSG_REPLIED | MSG_MARKED);
3343 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
3344 if (gslist_find_next_num(&p_unseen, msginfo->msgnum) == msginfo->msgnum) {
3345 if (!reverse_seen) {
3346 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
3348 flags &= ~(MSG_UNREAD | MSG_NEW);
3351 if (gslist_find_next_num(&p_answered, msginfo->msgnum) == msginfo->msgnum)
3352 flags |= MSG_REPLIED;
3353 if (gslist_find_next_num(&p_flagged, msginfo->msgnum) == msginfo->msgnum)
3354 flags |= MSG_MARKED;
3355 g_relation_insert(msgflags, msginfo, GINT_TO_POINTER(flags));
3358 imap_lep_set_free(seq_list);
3359 g_slist_free(flagged);
3360 g_slist_free(answered);
3361 g_slist_free(unseen);
3362 g_slist_free(sorted_list);
3363 g_string_free(cmd_buf, TRUE);
3366 return GINT_TO_POINTER(0);
3369 static gint imap_get_flags(Folder *folder, FolderItem *item,
3370 MsgInfoList *msginfo_list, GRelation *msgflags)
3373 get_flags_data *data = g_new0(get_flags_data, 1);
3375 data->folder = folder;
3377 data->msginfo_list = msginfo_list;
3378 data->msgflags = msgflags;
3380 if (prefs_common.work_offline && !imap_gtk_should_override()) {
3385 result = GPOINTER_TO_INT(imap_get_flags_thread(data));
3392 static gboolean process_flags(gpointer key, gpointer value, gpointer user_data)
3394 gboolean flags_set = GPOINTER_TO_INT(user_data);
3395 gint flags_value = GPOINTER_TO_INT(key);
3396 hashtable_data *data = (hashtable_data *)value;
3398 data->msglist = g_slist_reverse(data->msglist);
3400 debug_print("IMAP %ssetting flags to %d for %d messages\n",
3403 g_slist_length(data->msglist));
3404 imap_set_message_flags(data->session, data->msglist, flags_value, flags_set);
3406 g_slist_free(data->msglist);
3411 static void process_hashtable(void)
3413 if (flags_set_table) {
3414 g_hash_table_foreach_remove(flags_set_table, process_flags, GINT_TO_POINTER(TRUE));
3415 g_free(flags_set_table);
3416 flags_set_table = NULL;
3418 if (flags_unset_table) {
3419 g_hash_table_foreach_remove(flags_unset_table, process_flags, GINT_TO_POINTER(FALSE));
3420 g_free(flags_unset_table);
3421 flags_unset_table = NULL;
3425 static IMAPFolderItem *batching_item = NULL;
3427 static void imap_set_batch (Folder *folder, FolderItem *_item, gboolean batch)
3429 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3431 g_return_if_fail(item != NULL);
3433 if (batch && batching_item != NULL) {
3434 g_warning("already batching on %s\n", batching_item->item.path);
3438 if (item->batching == batch)
3441 item->batching = batch;
3443 batching_item = batch?item:NULL;
3446 debug_print("IMAP switching to batch mode\n");
3447 if (flags_set_table) {
3448 g_warning("flags_set_table non-null but we just entered batch mode!\n");
3449 flags_set_table = NULL;
3451 if (flags_unset_table) {
3452 g_warning("flags_unset_table non-null but we just entered batch mode!\n");
3453 flags_unset_table = NULL;
3455 flags_set_table = g_hash_table_new(NULL, g_direct_equal);
3456 flags_unset_table = g_hash_table_new(NULL, g_direct_equal);
3458 debug_print("IMAP switching away from batch mode\n");
3460 process_hashtable();
3466 /* data types conversion libetpan <-> sylpheed */
3470 #define ETPAN_IMAP_MB_MARKED 1
3471 #define ETPAN_IMAP_MB_UNMARKED 2
3472 #define ETPAN_IMAP_MB_NOSELECT 4
3473 #define ETPAN_IMAP_MB_NOINFERIORS 8
3475 static int imap_flags_to_flags(struct mailimap_mbx_list_flags * imap_flags)
3481 if (imap_flags->mbf_type == MAILIMAP_MBX_LIST_FLAGS_SFLAG) {
3482 switch (imap_flags->mbf_sflag) {
3483 case MAILIMAP_MBX_LIST_SFLAG_MARKED:
3484 flags |= ETPAN_IMAP_MB_MARKED;
3486 case MAILIMAP_MBX_LIST_SFLAG_NOSELECT:
3487 flags |= ETPAN_IMAP_MB_NOSELECT;