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);
734 tmp_pass = g_strdup(""); /* allow empty password */
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);
1151 procmsg_msg_list_free(list);
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");
1396 folder->queue = imap_create_special_folder
1397 (folder, F_QUEUE, "Queue");
1398 if (!folder->outbox)
1399 folder->outbox = imap_create_special_folder
1400 (folder, F_OUTBOX, "Sent");
1402 folder->draft = imap_create_special_folder
1403 (folder, F_DRAFT, "Drafts");
1406 static FolderItem *imap_create_special_folder(Folder *folder,
1407 SpecialFolderItemType stype,
1411 FolderItem *new_item;
1413 g_return_val_if_fail(folder != NULL, NULL);
1414 g_return_val_if_fail(folder->node != NULL, NULL);
1415 g_return_val_if_fail(folder->node->data != NULL, NULL);
1416 g_return_val_if_fail(folder->account != NULL, NULL);
1417 g_return_val_if_fail(name != NULL, NULL);
1419 item = FOLDER_ITEM(folder->node->data);
1420 new_item = imap_create_folder(folder, item, name);
1423 g_warning("Can't create '%s'\n", name);
1424 if (!folder->inbox) return NULL;
1426 new_item = imap_create_folder(folder, folder->inbox, name);
1428 g_warning("Can't create '%s' under INBOX\n", name);
1430 new_item->stype = stype;
1432 new_item->stype = stype;
1437 static gchar *imap_folder_get_path(Folder *folder)
1441 g_return_val_if_fail(folder != NULL, NULL);
1442 g_return_val_if_fail(folder->account != NULL, NULL);
1444 folder_path = g_strconcat(get_imap_cache_dir(),
1446 folder->account->recv_server,
1448 folder->account->userid,
1454 static gchar *imap_item_get_path(Folder *folder, FolderItem *item)
1456 gchar *folder_path, *path;
1458 g_return_val_if_fail(folder != NULL, NULL);
1459 g_return_val_if_fail(item != NULL, NULL);
1460 folder_path = imap_folder_get_path(folder);
1462 g_return_val_if_fail(folder_path != NULL, NULL);
1463 if (folder_path[0] == G_DIR_SEPARATOR) {
1465 path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
1468 path = g_strdup(folder_path);
1471 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1472 folder_path, G_DIR_SEPARATOR_S,
1475 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1478 g_free(folder_path);
1483 static FolderItem *imap_create_folder(Folder *folder, FolderItem *parent,
1486 gchar *dirpath, *imap_path;
1487 IMAPSession *session;
1488 FolderItem *new_item;
1494 g_return_val_if_fail(folder != NULL, NULL);
1495 g_return_val_if_fail(folder->account != NULL, NULL);
1496 g_return_val_if_fail(parent != NULL, NULL);
1497 g_return_val_if_fail(name != NULL, NULL);
1499 session = imap_session_get(folder);
1504 if (!folder_item_parent(parent) && strcmp(name, "INBOX") == 0)
1505 dirpath = g_strdup(name);
1506 else if (parent->path)
1507 dirpath = g_strconcat(parent->path, "/", name, NULL);
1508 else if ((p = strchr(name, '/')) != NULL && *(p + 1) != '\0')
1509 dirpath = g_strdup(name);
1510 else if (folder->account->imap_dir && *folder->account->imap_dir) {
1513 Xstrdup_a(imap_dir, folder->account->imap_dir, {return NULL;});
1514 strtailchomp(imap_dir, '/');
1515 dirpath = g_strconcat(imap_dir, "/", name, NULL);
1517 dirpath = g_strdup(name);
1519 /* keep trailing directory separator to create a folder that contains
1521 imap_path = imap_utf8_to_modified_utf7(dirpath);
1522 strtailchomp(dirpath, '/');
1523 Xstrdup_a(new_name, name, {
1527 strtailchomp(new_name, '/');
1528 separator = imap_get_path_separator(IMAP_FOLDER(folder), imap_path);
1529 imap_path_separator_subst(imap_path, separator);
1530 subst_char(new_name, '/', separator);
1532 if (strcmp(name, "INBOX") != 0) {
1534 gboolean exist = FALSE;
1538 argbuf = g_ptr_array_new();
1539 r = imap_threaded_list(folder, "", imap_path, &lep_list);
1540 if (r != MAILIMAP_NO_ERROR) {
1541 log_warning(_("can't create mailbox: LIST failed\n"));
1544 ptr_array_free_strings(argbuf);
1545 g_ptr_array_free(argbuf, TRUE);
1549 if (clist_count(lep_list) > 0)
1553 ok = imap_cmd_create(session, imap_path);
1554 if (ok != IMAP_SUCCESS) {
1555 log_warning(_("can't create mailbox\n"));
1563 new_item = folder_item_new(folder, new_name, dirpath);
1564 folder_item_append(parent, new_item);
1568 dirpath = folder_item_get_path(new_item);
1569 if (!is_dir_exist(dirpath))
1570 make_dir_hier(dirpath);
1576 static gint imap_rename_folder(Folder *folder, FolderItem *item,
1581 gchar *real_oldpath;
1582 gchar *real_newpath;
1584 gchar *old_cache_dir;
1585 gchar *new_cache_dir;
1586 IMAPSession *session;
1589 gint exists, recent, unseen;
1590 guint32 uid_validity;
1592 g_return_val_if_fail(folder != NULL, -1);
1593 g_return_val_if_fail(item != NULL, -1);
1594 g_return_val_if_fail(item->path != NULL, -1);
1595 g_return_val_if_fail(name != NULL, -1);
1597 if (strchr(name, imap_get_path_separator(IMAP_FOLDER(folder), item->path)) != NULL) {
1598 g_warning(_("New folder name must not contain the namespace "
1603 session = imap_session_get(folder);
1607 real_oldpath = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1609 g_free(session->mbox);
1610 session->mbox = NULL;
1611 ok = imap_cmd_examine(session, "INBOX",
1612 &exists, &recent, &unseen, &uid_validity, FALSE);
1613 if (ok != IMAP_SUCCESS) {
1614 g_free(real_oldpath);
1618 separator = imap_get_path_separator(IMAP_FOLDER(folder), item->path);
1619 if (strchr(item->path, G_DIR_SEPARATOR)) {
1620 dirpath = g_path_get_dirname(item->path);
1621 newpath = g_strconcat(dirpath, G_DIR_SEPARATOR_S, name, NULL);
1624 newpath = g_strdup(name);
1626 real_newpath = imap_utf8_to_modified_utf7(newpath);
1627 imap_path_separator_subst(real_newpath, separator);
1629 ok = imap_cmd_rename(session, real_oldpath, real_newpath);
1630 if (ok != IMAP_SUCCESS) {
1631 log_warning(_("can't rename mailbox: %s to %s\n"),
1632 real_oldpath, real_newpath);
1633 g_free(real_oldpath);
1635 g_free(real_newpath);
1640 item->name = g_strdup(name);
1642 old_cache_dir = folder_item_get_path(item);
1644 paths[0] = g_strdup(item->path);
1646 g_node_traverse(item->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1647 imap_rename_folder_func, paths);
1649 if (is_dir_exist(old_cache_dir)) {
1650 new_cache_dir = folder_item_get_path(item);
1651 if (rename(old_cache_dir, new_cache_dir) < 0) {
1652 FILE_OP_ERROR(old_cache_dir, "rename");
1654 g_free(new_cache_dir);
1657 g_free(old_cache_dir);
1660 g_free(real_oldpath);
1661 g_free(real_newpath);
1666 static gint imap_remove_folder_real(Folder *folder, FolderItem *item)
1669 IMAPSession *session;
1672 gint exists, recent, unseen;
1673 guint32 uid_validity;
1675 g_return_val_if_fail(folder != NULL, -1);
1676 g_return_val_if_fail(item != NULL, -1);
1677 g_return_val_if_fail(item->path != NULL, -1);
1679 session = imap_session_get(folder);
1683 path = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1685 ok = imap_cmd_examine(session, "INBOX",
1686 &exists, &recent, &unseen, &uid_validity, FALSE);
1687 if (ok != IMAP_SUCCESS) {
1692 ok = imap_cmd_delete(session, path);
1693 if (ok != IMAP_SUCCESS) {
1694 log_warning(_("can't delete mailbox\n"));
1700 cache_dir = folder_item_get_path(item);
1701 if (is_dir_exist(cache_dir) && remove_dir_recursive(cache_dir) < 0)
1702 g_warning("can't remove directory '%s'\n", cache_dir);
1704 folder_item_remove(item);
1709 static gint imap_remove_folder(Folder *folder, FolderItem *item)
1713 g_return_val_if_fail(item != NULL, -1);
1714 g_return_val_if_fail(item->folder != NULL, -1);
1715 g_return_val_if_fail(item->node != NULL, -1);
1717 node = item->node->children;
1718 while (node != NULL) {
1720 if (imap_remove_folder(folder, FOLDER_ITEM(node->data)) < 0)
1724 debug_print("IMAP removing %s\n", item->path);
1726 if (imap_remove_all_msg(folder, item) < 0)
1728 return imap_remove_folder_real(folder, item);
1731 typedef struct _uncached_data {
1732 IMAPSession *session;
1734 MsgNumberList *numlist;
1740 static void *imap_get_uncached_messages_thread(void *data)
1742 uncached_data *stuff = (uncached_data *)data;
1743 IMAPSession *session = stuff->session;
1744 FolderItem *item = stuff->item;
1745 MsgNumberList *numlist = stuff->numlist;
1747 GSList *newlist = NULL;
1748 GSList *llast = NULL;
1749 GSList *seq_list, *cur;
1751 debug_print("uncached_messages\n");
1753 if (session == NULL || item == NULL || item->folder == NULL
1754 || FOLDER_CLASS(item->folder) != &imap_class) {
1759 seq_list = imap_get_lep_set_from_numlist(numlist);
1760 debug_print("get msgs info\n");
1761 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
1762 struct mailimap_set * imapset;
1768 imapset = cur->data;
1770 r = imap_threaded_fetch_env(session->folder,
1771 imapset, &env_list);
1772 if (r != MAILIMAP_NO_ERROR)
1776 for(i = 0 ; i < carray_count(env_list) ; i ++) {
1777 struct imap_fetch_env_info * info;
1780 info = carray_get(env_list, i);
1781 msginfo = imap_envelope_from_lep(info, item);
1782 msginfo->folder = item;
1784 llast = newlist = g_slist_append(newlist, msginfo);
1786 llast = g_slist_append(llast, msginfo);
1787 llast = llast->next;
1792 imap_fetch_env_free(env_list);
1795 session_set_access_time(SESSION(session));
1800 #define MAX_MSG_NUM 50
1802 static GSList *imap_get_uncached_messages(IMAPSession *session,
1804 MsgNumberList *numlist)
1806 GSList *result = NULL;
1808 uncached_data *data = g_new0(uncached_data, 1);
1813 data->total = g_slist_length(numlist);
1814 debug_print("messages list : %i\n", data->total);
1816 while (cur != NULL) {
1817 GSList * partial_result;
1825 while (count < MAX_MSG_NUM) {
1830 if (newlist == NULL)
1831 llast = newlist = g_slist_append(newlist, p);
1833 llast = g_slist_append(llast, p);
1834 llast = llast->next;
1844 data->session = session;
1846 data->numlist = newlist;
1849 if (prefs_common.work_offline && !imap_gtk_should_override()) {
1855 (GSList *)imap_get_uncached_messages_thread(data);
1857 statusbar_progress_all(data->cur,data->total, 1);
1859 g_slist_free(newlist);
1861 result = g_slist_concat(result, partial_result);
1865 statusbar_progress_all(0,0,0);
1866 statusbar_pop_all();
1871 static void imap_delete_all_cached_messages(FolderItem *item)
1875 g_return_if_fail(item != NULL);
1876 g_return_if_fail(item->folder != NULL);
1877 g_return_if_fail(FOLDER_CLASS(item->folder) == &imap_class);
1879 debug_print("Deleting all cached messages...\n");
1881 dir = folder_item_get_path(item);
1882 if (is_dir_exist(dir))
1883 remove_all_numbered_files(dir);
1886 debug_print("done.\n");
1889 static IMAPNameSpace *imap_find_namespace_from_list(GList *ns_list,
1892 IMAPNameSpace *namespace = NULL;
1893 gchar *tmp_path, *name;
1895 if (!path) path = "";
1897 for (; ns_list != NULL; ns_list = ns_list->next) {
1898 IMAPNameSpace *tmp_ns = ns_list->data;
1900 Xstrcat_a(tmp_path, path, "/", return namespace);
1901 Xstrdup_a(name, tmp_ns->name, return namespace);
1902 if (tmp_ns->separator && tmp_ns->separator != '/') {
1903 subst_char(tmp_path, tmp_ns->separator, '/');
1904 subst_char(name, tmp_ns->separator, '/');
1906 if (strncmp(tmp_path, name, strlen(name)) == 0)
1913 static IMAPNameSpace *imap_find_namespace(IMAPFolder *folder,
1916 IMAPNameSpace *namespace;
1918 g_return_val_if_fail(folder != NULL, NULL);
1920 namespace = imap_find_namespace_from_list(folder->ns_personal, path);
1921 if (namespace) return namespace;
1922 namespace = imap_find_namespace_from_list(folder->ns_others, path);
1923 if (namespace) return namespace;
1924 namespace = imap_find_namespace_from_list(folder->ns_shared, path);
1925 if (namespace) return namespace;
1931 static gchar imap_get_path_separator(IMAPFolder *folder, const gchar *path)
1933 IMAPNameSpace *namespace;
1934 gchar separator = '/';
1936 if (folder->last_seen_separator == 0) {
1938 int r = imap_threaded_list((Folder *)folder, "", "", &lep_list);
1939 if (r != MAILIMAP_NO_ERROR) {
1940 log_warning(_("LIST failed\n"));
1944 if (clist_count(lep_list) > 0) {
1945 clistiter * iter = clist_begin(lep_list);
1946 struct mailimap_mailbox_list * mb;
1947 mb = clist_content(iter);
1949 folder->last_seen_separator = mb->mb_delimiter;
1950 debug_print("got separator: %c\n", folder->last_seen_separator);
1952 mailimap_list_result_free(lep_list);
1955 if (folder->last_seen_separator != 0) {
1956 debug_print("using separator: %c\n", folder->last_seen_separator);
1957 return folder->last_seen_separator;
1960 namespace = imap_find_namespace(folder, path);
1961 if (namespace && namespace->separator)
1962 separator = namespace->separator;
1967 static gchar *imap_get_real_path(IMAPFolder *folder, const gchar *path)
1972 g_return_val_if_fail(folder != NULL, NULL);
1973 g_return_val_if_fail(path != NULL, NULL);
1975 real_path = imap_utf8_to_modified_utf7(path);
1976 separator = imap_get_path_separator(folder, path);
1977 imap_path_separator_subst(real_path, separator);
1982 static gint imap_set_message_flags(IMAPSession *session,
1983 MsgNumberList *numlist,
1991 seq_list = imap_get_lep_set_from_numlist(numlist);
1993 for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) {
1994 struct mailimap_set * imapset;
1996 imapset = cur->data;
1998 ok = imap_cmd_store(session, imapset,
2002 imap_lep_set_free(seq_list);
2004 return IMAP_SUCCESS;
2007 typedef struct _select_data {
2008 IMAPSession *session;
2013 guint32 *uid_validity;
2017 static gint imap_select(IMAPSession *session, IMAPFolder *folder,
2019 gint *exists, gint *recent, gint *unseen,
2020 guint32 *uid_validity, gboolean block)
2024 gint exists_, recent_, unseen_;
2025 guint32 uid_validity_;
2027 if (!exists || !recent || !unseen || !uid_validity) {
2028 if (session->mbox && strcmp(session->mbox, path) == 0)
2029 return IMAP_SUCCESS;
2033 uid_validity = &uid_validity_;
2036 g_free(session->mbox);
2037 session->mbox = NULL;
2039 real_path = imap_get_real_path(folder, path);
2041 ok = imap_cmd_select(session, real_path,
2042 exists, recent, unseen, uid_validity, block);
2043 if (ok != IMAP_SUCCESS)
2044 log_warning(_("can't select folder: %s\n"), real_path);
2046 session->mbox = g_strdup(path);
2047 session->folder_content_changed = FALSE;
2054 static gint imap_status(IMAPSession *session, IMAPFolder *folder,
2056 gint *messages, gint *recent,
2057 guint32 *uid_next, guint32 *uid_validity,
2058 gint *unseen, gboolean block)
2062 struct mailimap_mailbox_data_status * data_status;
2066 real_path = imap_get_real_path(folder, path);
2068 r = imap_threaded_status(FOLDER(folder), real_path, &data_status);
2070 if (r != MAILIMAP_NO_ERROR) {
2071 debug_print("status err %d\n", r);
2075 if (data_status->st_info_list == NULL) {
2076 mailimap_mailbox_data_status_free(data_status);
2077 debug_print("status->st_info_list == NULL\n");
2082 for(iter = clist_begin(data_status->st_info_list) ; iter != NULL ;
2083 iter = clist_next(iter)) {
2084 struct mailimap_status_info * info;
2086 info = clist_content(iter);
2087 switch (info->st_att) {
2088 case MAILIMAP_STATUS_ATT_MESSAGES:
2089 * messages = info->st_value;
2090 got_values |= 1 << 0;
2093 case MAILIMAP_STATUS_ATT_RECENT:
2094 * recent = info->st_value;
2095 got_values |= 1 << 1;
2098 case MAILIMAP_STATUS_ATT_UIDNEXT:
2099 * uid_next = info->st_value;
2100 got_values |= 1 << 2;
2103 case MAILIMAP_STATUS_ATT_UIDVALIDITY:
2104 * uid_validity = info->st_value;
2105 got_values |= 1 << 3;
2108 case MAILIMAP_STATUS_ATT_UNSEEN:
2109 * unseen = info->st_value;
2110 got_values |= 1 << 4;
2114 mailimap_mailbox_data_status_free(data_status);
2116 if (got_values != ((1 << 4) + (1 << 3) +
2117 (1 << 2) + (1 << 1) + (1 << 0))) {
2118 debug_print("status: incomplete values received (%d)\n", got_values);
2121 return IMAP_SUCCESS;
2124 static void imap_free_capabilities(IMAPSession *session)
2126 g_strfreev(session->capability);
2127 session->capability = NULL;
2130 /* low-level IMAP4rev1 commands */
2133 static gint imap_cmd_authenticate(IMAPSession *session, const gchar *user,
2134 const gchar *pass, IMAPAuthType type)
2141 gchar hexdigest[33];
2145 auth_type = "CRAM-MD5";
2147 imap_gen_send(session, "AUTHENTICATE %s", auth_type);
2148 ok = imap_gen_recv(session, &buf);
2149 if (ok != IMAP_SUCCESS || buf[0] != '+' || buf[1] != ' ') {
2154 challenge = g_malloc(strlen(buf + 2) + 1);
2155 challenge_len = base64_decode(challenge, buf + 2, -1);
2156 challenge[challenge_len] = '\0';
2159 md5_hex_hmac(hexdigest, challenge, challenge_len, pass, strlen(pass));
2162 response = g_strdup_printf("%s %s", user, hexdigest);
2163 response64 = g_malloc((strlen(response) + 3) * 2 + 1);
2164 base64_encode(response64, response, strlen(response));
2167 sock_puts(SESSION(session)->sock, response64);
2168 ok = imap_cmd_ok(session, NULL);
2169 if (ok != IMAP_SUCCESS)
2170 log_warning(_("IMAP4 authentication failed.\n"));
2176 static gint imap_cmd_login(IMAPSession *session,
2177 const gchar *user, const gchar *pass)
2181 static time_t last_login_err = 0;
2183 log_print("IMAP4> Logging in to %s\n", SESSION(session)->server);
2184 r = imap_threaded_login(session->folder, user, pass);
2185 if (r != MAILIMAP_NO_ERROR) {
2186 if (time(NULL) - last_login_err > 10) {
2187 alertpanel_error(_("Connection to %s failed: login refused."),
2188 SESSION(session)->server);
2190 last_login_err = time(NULL);
2191 log_error("IMAP4< Error logging in to %s\n",
2192 SESSION(session)->server);
2200 static gint imap_cmd_logout(IMAPSession *session)
2202 imap_threaded_disconnect(session->folder);
2204 return IMAP_SUCCESS;
2207 static gint imap_cmd_noop(IMAPSession *session)
2210 unsigned int exists;
2212 r = imap_threaded_noop(session->folder, &exists);
2213 if (r != MAILIMAP_NO_ERROR) {
2214 debug_print("noop err %d\n", r);
2217 session->exists = exists;
2218 session_set_access_time(SESSION(session));
2220 return IMAP_SUCCESS;
2224 static gint imap_cmd_starttls(IMAPSession *session)
2228 r = imap_threaded_starttls(session->folder);
2229 if (r != MAILIMAP_NO_ERROR) {
2230 debug_print("starttls err %d\n", r);
2233 return IMAP_SUCCESS;
2237 static gint imap_cmd_select(IMAPSession *session, const gchar *folder,
2238 gint *exists, gint *recent, gint *unseen,
2239 guint32 *uid_validity, gboolean block)
2243 r = imap_threaded_select(session->folder, folder,
2244 exists, recent, unseen, uid_validity);
2245 if (r != MAILIMAP_NO_ERROR) {
2246 debug_print("select err %d\n", r);
2249 return IMAP_SUCCESS;
2252 static gint imap_cmd_examine(IMAPSession *session, const gchar *folder,
2253 gint *exists, gint *recent, gint *unseen,
2254 guint32 *uid_validity, gboolean block)
2258 r = imap_threaded_examine(session->folder, folder,
2259 exists, recent, unseen, uid_validity);
2260 if (r != MAILIMAP_NO_ERROR) {
2261 debug_print("examine err %d\n", r);
2265 return IMAP_SUCCESS;
2268 static gint imap_cmd_create(IMAPSession *session, const gchar *folder)
2272 r = imap_threaded_create(session->folder, folder);
2273 if (r != MAILIMAP_NO_ERROR) {
2278 return IMAP_SUCCESS;
2281 static gint imap_cmd_rename(IMAPSession *session, const gchar *old_folder,
2282 const gchar *new_folder)
2286 r = imap_threaded_rename(session->folder, old_folder,
2288 if (r != MAILIMAP_NO_ERROR) {
2293 return IMAP_SUCCESS;
2296 static gint imap_cmd_delete(IMAPSession *session, const gchar *folder)
2301 r = imap_threaded_delete(session->folder, folder);
2302 if (r != MAILIMAP_NO_ERROR) {
2307 return IMAP_SUCCESS;
2310 typedef struct _fetch_data {
2311 IMAPSession *session;
2313 const gchar *filename;
2319 static void *imap_cmd_fetch_thread(void *data)
2321 fetch_data *stuff = (fetch_data *)data;
2322 IMAPSession *session = stuff->session;
2323 guint32 uid = stuff->uid;
2324 const gchar *filename = stuff->filename;
2328 r = imap_threaded_fetch_content(session->folder,
2332 r = imap_threaded_fetch_content(session->folder,
2335 if (r != MAILIMAP_NO_ERROR) {
2336 debug_print("fetch err %d\n", r);
2337 return GINT_TO_POINTER(IMAP_ERROR);
2339 return GINT_TO_POINTER(IMAP_SUCCESS);
2342 static gint imap_cmd_fetch(IMAPSession *session, guint32 uid,
2343 const gchar *filename, gboolean headers,
2346 fetch_data *data = g_new0(fetch_data, 1);
2349 data->session = session;
2351 data->filename = filename;
2352 data->headers = headers;
2355 if (prefs_common.work_offline && !imap_gtk_should_override()) {
2360 result = GPOINTER_TO_INT(imap_cmd_fetch_thread(data));
2366 static gint imap_cmd_append(IMAPSession *session, const gchar *destfolder,
2367 const gchar *file, IMAPFlags flags,
2370 struct mailimap_flag_list * flag_list;
2373 g_return_val_if_fail(file != NULL, IMAP_ERROR);
2375 flag_list = imap_flag_to_lep(flags);
2376 r = imap_threaded_append(session->folder, destfolder,
2379 if (new_uid != NULL)
2382 if (r != MAILIMAP_NO_ERROR) {
2383 debug_print("append err %d\n", r);
2386 return IMAP_SUCCESS;
2389 static gint imap_cmd_copy(IMAPSession *session, struct mailimap_set * set,
2390 const gchar *destfolder, GRelation *uid_mapping)
2394 g_return_val_if_fail(session != NULL, IMAP_ERROR);
2395 g_return_val_if_fail(set != NULL, IMAP_ERROR);
2396 g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
2398 r = imap_threaded_copy(session->folder, set, destfolder);
2399 if (r != MAILIMAP_NO_ERROR) {
2404 return IMAP_SUCCESS;
2407 static gint imap_cmd_store(IMAPSession *session, struct mailimap_set * set,
2408 IMAPFlags flags, int do_add)
2411 struct mailimap_flag_list * flag_list;
2412 struct mailimap_store_att_flags * store_att_flags;
2414 flag_list = imap_flag_to_lep(flags);
2418 mailimap_store_att_flags_new_add_flags_silent(flag_list);
2421 mailimap_store_att_flags_new_remove_flags_silent(flag_list);
2423 r = imap_threaded_store(session->folder, set, store_att_flags);
2424 if (r != MAILIMAP_NO_ERROR) {
2429 return IMAP_SUCCESS;
2432 static gint imap_cmd_expunge(IMAPSession *session)
2436 if (prefs_common.work_offline && !imap_gtk_should_override()) {
2440 r = imap_threaded_expunge(session->folder);
2441 if (r != MAILIMAP_NO_ERROR) {
2446 return IMAP_SUCCESS;
2449 static void imap_path_separator_subst(gchar *str, gchar separator)
2452 gboolean in_escape = FALSE;
2454 if (!separator || separator == '/') return;
2456 for (p = str; *p != '\0'; p++) {
2457 if (*p == '/' && !in_escape)
2459 else if (*p == '&' && *(p + 1) != '-' && !in_escape)
2461 else if (*p == '-' && in_escape)
2466 static gchar *imap_modified_utf7_to_utf8(const gchar *mutf7_str)
2468 static iconv_t cd = (iconv_t)-1;
2469 static gboolean iconv_ok = TRUE;
2472 size_t norm_utf7_len;
2474 gchar *to_str, *to_p;
2476 gboolean in_escape = FALSE;
2478 if (!iconv_ok) return g_strdup(mutf7_str);
2480 if (cd == (iconv_t)-1) {
2481 cd = iconv_open(CS_INTERNAL, CS_UTF_7);
2482 if (cd == (iconv_t)-1) {
2483 g_warning("iconv cannot convert UTF-7 to %s\n",
2486 return g_strdup(mutf7_str);
2490 /* modified UTF-7 to normal UTF-7 conversion */
2491 norm_utf7 = g_string_new(NULL);
2493 for (p = mutf7_str; *p != '\0'; p++) {
2494 /* replace: '&' -> '+',
2496 escaped ',' -> '/' */
2497 if (!in_escape && *p == '&') {
2498 if (*(p + 1) != '-') {
2499 g_string_append_c(norm_utf7, '+');
2502 g_string_append_c(norm_utf7, '&');
2505 } else if (in_escape && *p == ',') {
2506 g_string_append_c(norm_utf7, '/');
2507 } else if (in_escape && *p == '-') {
2508 g_string_append_c(norm_utf7, '-');
2511 g_string_append_c(norm_utf7, *p);
2515 norm_utf7_p = norm_utf7->str;
2516 norm_utf7_len = norm_utf7->len;
2517 to_len = strlen(mutf7_str) * 5;
2518 to_p = to_str = g_malloc(to_len + 1);
2520 if (iconv(cd, (ICONV_CONST gchar **)&norm_utf7_p, &norm_utf7_len,
2521 &to_p, &to_len) == -1) {
2522 g_warning(_("iconv cannot convert UTF-7 to %s\n"),
2523 conv_get_locale_charset_str());
2524 g_string_free(norm_utf7, TRUE);
2526 return g_strdup(mutf7_str);
2529 /* second iconv() call for flushing */
2530 iconv(cd, NULL, NULL, &to_p, &to_len);
2531 g_string_free(norm_utf7, TRUE);
2537 static gchar *imap_utf8_to_modified_utf7(const gchar *from)
2539 static iconv_t cd = (iconv_t)-1;
2540 static gboolean iconv_ok = TRUE;
2541 gchar *norm_utf7, *norm_utf7_p;
2542 size_t from_len, norm_utf7_len;
2544 gchar *from_tmp, *to, *p;
2545 gboolean in_escape = FALSE;
2547 if (!iconv_ok) return g_strdup(from);
2549 if (cd == (iconv_t)-1) {
2550 cd = iconv_open(CS_UTF_7, CS_INTERNAL);
2551 if (cd == (iconv_t)-1) {
2552 g_warning(_("iconv cannot convert %s to UTF-7\n"),
2555 return g_strdup(from);
2559 /* UTF-8 to normal UTF-7 conversion */
2560 Xstrdup_a(from_tmp, from, return g_strdup(from));
2561 from_len = strlen(from);
2562 norm_utf7_len = from_len * 5;
2563 Xalloca(norm_utf7, norm_utf7_len + 1, return g_strdup(from));
2564 norm_utf7_p = norm_utf7;
2566 #define IS_PRINT(ch) (isprint(ch) && IS_ASCII(ch))
2568 while (from_len > 0) {
2569 if (*from_tmp == '+') {
2570 *norm_utf7_p++ = '+';
2571 *norm_utf7_p++ = '-';
2575 } else if (IS_PRINT(*(guchar *)from_tmp)) {
2576 /* printable ascii char */
2577 *norm_utf7_p = *from_tmp;
2583 size_t conv_len = 0;
2585 /* unprintable char: convert to UTF-7 */
2587 while (!IS_PRINT(*(guchar *)p) && conv_len < from_len) {
2588 conv_len += g_utf8_skip[*(guchar *)p];
2589 p += g_utf8_skip[*(guchar *)p];
2592 from_len -= conv_len;
2593 if (iconv(cd, (ICONV_CONST gchar **)&from_tmp,
2595 &norm_utf7_p, &norm_utf7_len) == -1) {
2596 g_warning(_("iconv cannot convert UTF-8 to UTF-7\n"));
2597 return g_strdup(from);
2600 /* second iconv() call for flushing */
2601 iconv(cd, NULL, NULL, &norm_utf7_p, &norm_utf7_len);
2607 *norm_utf7_p = '\0';
2608 to_str = g_string_new(NULL);
2609 for (p = norm_utf7; p < norm_utf7_p; p++) {
2610 /* replace: '&' -> "&-",
2613 BASE64 '/' -> ',' */
2614 if (!in_escape && *p == '&') {
2615 g_string_append(to_str, "&-");
2616 } else if (!in_escape && *p == '+') {
2617 if (*(p + 1) == '-') {
2618 g_string_append_c(to_str, '+');
2621 g_string_append_c(to_str, '&');
2624 } else if (in_escape && *p == '/') {
2625 g_string_append_c(to_str, ',');
2626 } else if (in_escape && *p == '-') {
2627 g_string_append_c(to_str, '-');
2630 g_string_append_c(to_str, *p);
2636 g_string_append_c(to_str, '-');
2640 g_string_free(to_str, FALSE);
2645 static gboolean imap_rename_folder_func(GNode *node, gpointer data)
2647 FolderItem *item = node->data;
2648 gchar **paths = data;
2649 const gchar *oldpath = paths[0];
2650 const gchar *newpath = paths[1];
2652 gchar *new_itempath;
2655 oldpathlen = strlen(oldpath);
2656 if (strncmp(oldpath, item->path, oldpathlen) != 0) {
2657 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
2661 base = item->path + oldpathlen;
2662 while (*base == G_DIR_SEPARATOR) base++;
2664 new_itempath = g_strdup(newpath);
2666 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
2669 item->path = new_itempath;
2674 typedef struct _get_list_uid_data {
2676 IMAPFolderItem *item;
2677 GSList **msgnum_list;
2679 } get_list_uid_data;
2681 static void *get_list_of_uids_thread(void *data)
2683 get_list_uid_data *stuff = (get_list_uid_data *)data;
2684 Folder *folder = stuff->folder;
2685 IMAPFolderItem *item = stuff->item;
2686 GSList **msgnum_list = stuff->msgnum_list;
2687 gint ok, nummsgs = 0, lastuid_old;
2688 IMAPSession *session;
2689 GSList *uidlist, *elem;
2690 struct mailimap_set * set;
2691 clist * lep_uidlist;
2694 session = imap_session_get(folder);
2695 if (session == NULL) {
2697 return GINT_TO_POINTER(-1);
2700 ok = imap_select(session, IMAP_FOLDER(folder), item->item.path,
2701 NULL, NULL, NULL, NULL, TRUE);
2702 if (ok != IMAP_SUCCESS) {
2704 return GINT_TO_POINTER(-1);
2709 set = mailimap_set_new_interval(item->lastuid + 1, 0);
2710 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SIMPLE, set,
2712 if (r == MAILIMAP_NO_ERROR) {
2713 GSList * fetchuid_list;
2716 imap_uid_list_from_lep(lep_uidlist);
2717 uidlist = g_slist_concat(fetchuid_list, uidlist);
2720 GSList * fetchuid_list;
2721 carray * lep_uidtab;
2723 r = imap_threaded_fetch_uid(folder, item->lastuid + 1,
2725 if (r == MAILIMAP_NO_ERROR) {
2727 imap_uid_list_from_lep_tab(lep_uidtab);
2728 uidlist = g_slist_concat(fetchuid_list, uidlist);
2732 lastuid_old = item->lastuid;
2733 *msgnum_list = g_slist_copy(item->uid_list);
2734 nummsgs = g_slist_length(*msgnum_list);
2735 debug_print("Got %d uids from cache\n", g_slist_length(item->uid_list));
2737 for (elem = uidlist; elem != NULL; elem = g_slist_next(elem)) {
2740 msgnum = GPOINTER_TO_INT(elem->data);
2741 if (msgnum > lastuid_old) {
2742 *msgnum_list = g_slist_prepend(*msgnum_list, GINT_TO_POINTER(msgnum));
2743 item->uid_list = g_slist_prepend(item->uid_list, GINT_TO_POINTER(msgnum));
2746 if(msgnum > item->lastuid)
2747 item->lastuid = msgnum;
2750 g_slist_free(uidlist);
2753 return GINT_TO_POINTER(nummsgs);
2756 static gint get_list_of_uids(Folder *folder, IMAPFolderItem *item, GSList **msgnum_list)
2759 get_list_uid_data *data = g_new0(get_list_uid_data, 1);
2761 data->folder = folder;
2763 data->msgnum_list = msgnum_list;
2765 if (prefs_common.work_offline && !imap_gtk_should_override()) {
2770 result = GPOINTER_TO_INT(get_list_of_uids_thread(data));
2776 gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list, gboolean *old_uids_valid)
2778 IMAPFolderItem *item = (IMAPFolderItem *)_item;
2779 IMAPSession *session;
2780 gint ok, nummsgs = 0, exists, recent, uid_val, uid_next, unseen;
2781 GSList *uidlist = NULL;
2783 gboolean selected_folder;
2785 debug_print("get_num_list\n");
2787 g_return_val_if_fail(folder != NULL, -1);
2788 g_return_val_if_fail(item != NULL, -1);
2789 g_return_val_if_fail(item->item.path != NULL, -1);
2790 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
2791 g_return_val_if_fail(folder->account != NULL, -1);
2793 session = imap_session_get(folder);
2794 g_return_val_if_fail(session != NULL, -1);
2796 selected_folder = (session->mbox != NULL) &&
2797 (!strcmp(session->mbox, item->item.path));
2798 if (selected_folder) {
2799 ok = imap_cmd_noop(session);
2800 if (ok != IMAP_SUCCESS) {
2804 exists = session->exists;
2806 *old_uids_valid = TRUE;
2808 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
2809 &exists, &recent, &uid_next, &uid_val, &unseen, FALSE);
2810 if (ok != IMAP_SUCCESS) {
2814 if(item->item.mtime == uid_val)
2815 *old_uids_valid = TRUE;
2817 *old_uids_valid = FALSE;
2819 debug_print("Freeing imap uid cache\n");
2821 g_slist_free(item->uid_list);
2822 item->uid_list = NULL;
2824 item->item.mtime = uid_val;
2826 imap_delete_all_cached_messages((FolderItem *)item);
2830 if (!selected_folder)
2831 item->uid_next = uid_next;
2833 /* If old uid_next matches new uid_next we can be sure no message
2834 was added to the folder */
2835 if (( selected_folder && !session->folder_content_changed) ||
2836 (!selected_folder && uid_next == item->uid_next)) {
2837 nummsgs = g_slist_length(item->uid_list);
2839 /* If number of messages is still the same we
2840 know our caches message numbers are still valid,
2841 otherwise if the number of messages has decrease
2842 we discard our cache to start a new scan to find
2843 out which numbers have been removed */
2844 if (exists == nummsgs) {
2845 *msgnum_list = g_slist_copy(item->uid_list);
2847 } else if (exists < nummsgs) {
2848 debug_print("Freeing imap uid cache");
2850 g_slist_free(item->uid_list);
2851 item->uid_list = NULL;
2856 *msgnum_list = NULL;
2860 nummsgs = get_list_of_uids(folder, item, &uidlist);
2867 if (nummsgs != exists) {
2868 /* Cache contains more messages then folder, we have cached
2869 an old UID of a message that was removed and new messages
2870 have been added too, otherwise the uid_next check would
2872 debug_print("Freeing imap uid cache");
2874 g_slist_free(item->uid_list);
2875 item->uid_list = NULL;
2877 g_slist_free(*msgnum_list);
2879 nummsgs = get_list_of_uids(folder, item, &uidlist);
2882 *msgnum_list = uidlist;
2884 dir = folder_item_get_path((FolderItem *)item);
2885 debug_print("removing old messages from %s\n", dir);
2886 remove_numbered_files_not_in_list(dir, *msgnum_list);
2889 debug_print("get_num_list - ok - %i\n", nummsgs);
2894 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item)
2899 flags.perm_flags = MSG_NEW|MSG_UNREAD;
2900 flags.tmp_flags = 0;
2902 g_return_val_if_fail(item != NULL, NULL);
2903 g_return_val_if_fail(file != NULL, NULL);
2905 if (item->stype == F_QUEUE) {
2906 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
2907 } else if (item->stype == F_DRAFT) {
2908 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
2911 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
2912 if (!msginfo) return NULL;
2914 msginfo->plaintext_file = g_strdup(file);
2915 msginfo->folder = item;
2920 GSList *imap_get_msginfos(Folder *folder, FolderItem *item,
2921 GSList *msgnum_list)
2923 IMAPSession *session;
2924 MsgInfoList *ret = NULL;
2927 debug_print("get_msginfos\n");
2929 g_return_val_if_fail(folder != NULL, NULL);
2930 g_return_val_if_fail(item != NULL, NULL);
2931 g_return_val_if_fail(msgnum_list != NULL, NULL);
2933 session = imap_session_get(folder);
2934 g_return_val_if_fail(session != NULL, NULL);
2936 debug_print("IMAP getting msginfos\n");
2937 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
2938 NULL, NULL, NULL, NULL, FALSE);
2939 if (ok != IMAP_SUCCESS)
2942 if (!(item->stype == F_QUEUE || item->stype == F_DRAFT)) {
2943 ret = g_slist_concat(ret,
2944 imap_get_uncached_messages(session, item,
2947 MsgNumberList *sorted_list, *elem;
2948 gint startnum, lastnum;
2950 sorted_list = g_slist_sort(g_slist_copy(msgnum_list), g_int_compare);
2952 startnum = lastnum = GPOINTER_TO_INT(sorted_list->data);
2954 for (elem = sorted_list;; elem = g_slist_next(elem)) {
2958 num = GPOINTER_TO_INT(elem->data);
2960 if (num > lastnum + 1 || elem == NULL) {
2962 for (i = startnum; i <= lastnum; ++i) {
2965 file = imap_fetch_msg(folder, item, i);
2967 MsgInfo *msginfo = imap_parse_msg(file, item);
2968 if (msginfo != NULL) {
2969 msginfo->msgnum = i;
2970 ret = g_slist_append(ret, msginfo);
2984 g_slist_free(sorted_list);
2990 MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid)
2992 MsgInfo *msginfo = NULL;
2993 MsgInfoList *msginfolist;
2994 MsgNumberList numlist;
2996 numlist.next = NULL;
2997 numlist.data = GINT_TO_POINTER(uid);
2999 msginfolist = imap_get_msginfos(folder, item, &numlist);
3000 if (msginfolist != NULL) {
3001 msginfo = msginfolist->data;
3002 g_slist_free(msginfolist);
3008 gboolean imap_scan_required(Folder *folder, FolderItem *_item)
3010 IMAPSession *session;
3011 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3012 gint ok, exists = 0, recent = 0, unseen = 0;
3013 guint32 uid_next, uid_val = 0;
3014 gboolean selected_folder;
3016 g_return_val_if_fail(folder != NULL, FALSE);
3017 g_return_val_if_fail(item != NULL, FALSE);
3018 g_return_val_if_fail(item->item.folder != NULL, FALSE);
3019 g_return_val_if_fail(FOLDER_CLASS(item->item.folder) == &imap_class, FALSE);
3021 if (item->item.path == NULL)
3024 session = imap_session_get(folder);
3025 g_return_val_if_fail(session != NULL, FALSE);
3027 selected_folder = (session->mbox != NULL) &&
3028 (!strcmp(session->mbox, item->item.path));
3029 if (selected_folder) {
3030 ok = imap_cmd_noop(session);
3031 if (ok != IMAP_SUCCESS)
3034 if (session->folder_content_changed
3035 || session->exists != item->item.total_msgs)
3038 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
3039 &exists, &recent, &uid_next, &uid_val, &unseen, FALSE);
3040 if (ok != IMAP_SUCCESS)
3043 if ((uid_next != item->uid_next) || (exists != item->item.total_msgs))
3050 void imap_change_flags(Folder *folder, FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
3052 IMAPSession *session;
3053 IMAPFlags flags_set = 0, flags_unset = 0;
3054 gint ok = IMAP_SUCCESS;
3055 MsgNumberList numlist;
3056 hashtable_data *ht_data = NULL;
3058 g_return_if_fail(folder != NULL);
3059 g_return_if_fail(folder->klass == &imap_class);
3060 g_return_if_fail(item != NULL);
3061 g_return_if_fail(item->folder == folder);
3062 g_return_if_fail(msginfo != NULL);
3063 g_return_if_fail(msginfo->folder == item);
3065 session = imap_session_get(folder);
3069 if ((ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
3070 NULL, NULL, NULL, NULL, FALSE)) != IMAP_SUCCESS) {
3074 if (!MSG_IS_MARKED(msginfo->flags) && (newflags & MSG_MARKED))
3075 flags_set |= IMAP_FLAG_FLAGGED;
3076 if ( MSG_IS_MARKED(msginfo->flags) && !(newflags & MSG_MARKED))
3077 flags_unset |= IMAP_FLAG_FLAGGED;
3079 if (!MSG_IS_UNREAD(msginfo->flags) && (newflags & MSG_UNREAD))
3080 flags_unset |= IMAP_FLAG_SEEN;
3081 if ( MSG_IS_UNREAD(msginfo->flags) && !(newflags & MSG_UNREAD))
3082 flags_set |= IMAP_FLAG_SEEN;
3084 if (!MSG_IS_REPLIED(msginfo->flags) && (newflags & MSG_REPLIED))
3085 flags_set |= IMAP_FLAG_ANSWERED;
3086 if ( MSG_IS_REPLIED(msginfo->flags) && !(newflags & MSG_REPLIED))
3087 flags_set |= IMAP_FLAG_ANSWERED;
3089 numlist.next = NULL;
3090 numlist.data = GINT_TO_POINTER(msginfo->msgnum);
3092 if (IMAP_FOLDER_ITEM(item)->batching) {
3093 /* instead of performing an UID STORE command for each message change,
3094 * as a lot of them can change "together", we just fill in hashtables
3095 * and defer the treatment so that we're able to send only one
3098 debug_print("IMAP batch mode on, deferring flags change\n");
3100 ht_data = g_hash_table_lookup(flags_set_table, GINT_TO_POINTER(flags_set));
3101 if (ht_data == NULL) {
3102 ht_data = g_new0(hashtable_data, 1);
3103 ht_data->session = session;
3104 g_hash_table_insert(flags_set_table, GINT_TO_POINTER(flags_set), ht_data);
3106 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
3107 ht_data->msglist = g_slist_prepend(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum));
3110 ht_data = g_hash_table_lookup(flags_unset_table, GINT_TO_POINTER(flags_unset));
3111 if (ht_data == NULL) {
3112 ht_data = g_new0(hashtable_data, 1);
3113 ht_data->session = session;
3114 g_hash_table_insert(flags_unset_table, GINT_TO_POINTER(flags_unset), ht_data);
3116 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
3117 ht_data->msglist = g_slist_prepend(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum));
3120 debug_print("IMAP changing flags\n");
3122 ok = imap_set_message_flags(session, &numlist, flags_set, TRUE);
3123 if (ok != IMAP_SUCCESS) {
3129 ok = imap_set_message_flags(session, &numlist, flags_unset, FALSE);
3130 if (ok != IMAP_SUCCESS) {
3135 msginfo->flags.perm_flags = newflags;
3140 static gint imap_remove_msg(Folder *folder, FolderItem *item, gint uid)
3143 IMAPSession *session;
3145 MsgNumberList numlist;
3147 g_return_val_if_fail(folder != NULL, -1);
3148 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3149 g_return_val_if_fail(item != NULL, -1);
3151 session = imap_session_get(folder);
3152 if (!session) return -1;
3154 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3155 NULL, NULL, NULL, NULL, FALSE);
3156 if (ok != IMAP_SUCCESS)
3159 numlist.next = NULL;
3160 numlist.data = GINT_TO_POINTER(uid);
3162 ok = imap_set_message_flags
3163 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
3164 &numlist, IMAP_FLAG_DELETED, TRUE);
3165 if (ok != IMAP_SUCCESS) {
3166 log_warning(_("can't set deleted flags: %d\n"), uid);
3170 if (!session->uidplus) {
3171 ok = imap_cmd_expunge(session);
3175 uidstr = g_strdup_printf("%u", uid);
3176 ok = imap_cmd_expunge(session);
3179 if (ok != IMAP_SUCCESS) {
3180 log_warning(_("can't expunge\n"));
3184 IMAP_FOLDER_ITEM(item)->uid_list = g_slist_remove(
3185 IMAP_FOLDER_ITEM(item)->uid_list, numlist.data);
3186 dir = folder_item_get_path(item);
3187 if (is_dir_exist(dir))
3188 remove_numbered_files(dir, uid, uid);
3191 return IMAP_SUCCESS;
3194 static gint compare_msginfo(gconstpointer a, gconstpointer b)
3196 return ((MsgInfo *)a)->msgnum - ((MsgInfo *)b)->msgnum;
3199 static guint gslist_find_next_num(MsgNumberList **list, guint num)
3203 g_return_val_if_fail(list != NULL, -1);
3205 for (elem = *list; elem != NULL; elem = g_slist_next(elem))
3206 if (GPOINTER_TO_INT(elem->data) >= num)
3209 return elem != NULL ? GPOINTER_TO_INT(elem->data) : (gint)-1;
3213 * NEW and DELETED flags are not syncronized
3214 * - The NEW/RECENT flags in IMAP folders can not really be directly
3215 * modified by Sylpheed
3216 * - The DELETE/DELETED flag in IMAP and Sylpheed don't have the same
3217 * meaning, in IMAP it always removes the messages from the FolderItem
3218 * in Sylpheed it can mean to move the message to trash
3221 typedef struct _get_flags_data {
3224 MsgInfoList *msginfo_list;
3225 GRelation *msgflags;
3229 static /*gint*/ void *imap_get_flags_thread(void *data)
3231 get_flags_data *stuff = (get_flags_data *)data;
3232 Folder *folder = stuff->folder;
3233 FolderItem *item = stuff->item;
3234 MsgInfoList *msginfo_list = stuff->msginfo_list;
3235 GRelation *msgflags = stuff->msgflags;
3236 IMAPSession *session;
3237 GSList *sorted_list;
3238 GSList *unseen = NULL, *answered = NULL, *flagged = NULL;
3239 GSList *p_unseen, *p_answered, *p_flagged;
3241 GSList *seq_list, *cur;
3242 gboolean reverse_seen = FALSE;
3245 gint exists_cnt, recent_cnt, unseen_cnt, uid_next;
3246 guint32 uidvalidity;
3247 gboolean selected_folder;
3249 if (folder == NULL || item == NULL) {
3251 return GINT_TO_POINTER(-1);
3253 if (msginfo_list == NULL) {
3255 return GINT_TO_POINTER(0);
3258 session = imap_session_get(folder);
3259 if (session == NULL) {
3261 return GINT_TO_POINTER(-1);
3264 selected_folder = (session->mbox != NULL) &&
3265 (!strcmp(session->mbox, item->path));
3267 if (!selected_folder) {
3268 ok = imap_status(session, IMAP_FOLDER(folder), item->path,
3269 &exists_cnt, &recent_cnt, &uid_next, &uidvalidity, &unseen_cnt, TRUE);
3270 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3271 NULL, NULL, NULL, NULL, TRUE);
3272 if (ok != IMAP_SUCCESS) {
3274 return GINT_TO_POINTER(-1);
3277 if (unseen_cnt > exists_cnt / 2)
3278 reverse_seen = TRUE;
3281 if (item->unread_msgs > item->total_msgs / 2)
3282 reverse_seen = TRUE;
3285 cmd_buf = g_string_new(NULL);
3287 sorted_list = g_slist_sort(g_slist_copy(msginfo_list), compare_msginfo);
3289 seq_list = imap_get_lep_set_from_msglist(msginfo_list);
3291 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
3292 struct mailimap_set * imapset;
3293 clist * lep_uidlist;
3296 imapset = cur->data;
3298 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SEEN,
3299 imapset, &lep_uidlist);
3302 r = imap_threaded_search(folder,
3303 IMAP_SEARCH_TYPE_UNSEEN,
3304 imapset, &lep_uidlist);
3306 if (r == MAILIMAP_NO_ERROR) {
3309 uidlist = imap_uid_list_from_lep(lep_uidlist);
3310 mailimap_search_result_free(lep_uidlist);
3312 unseen = g_slist_concat(unseen, uidlist);
3315 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_ANSWERED,
3316 imapset, &lep_uidlist);
3317 if (r == MAILIMAP_NO_ERROR) {
3320 uidlist = imap_uid_list_from_lep(lep_uidlist);
3321 mailimap_search_result_free(lep_uidlist);
3323 answered = g_slist_concat(answered, uidlist);
3326 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_FLAGGED,
3327 imapset, &lep_uidlist);
3328 if (r == MAILIMAP_NO_ERROR) {
3331 uidlist = imap_uid_list_from_lep(lep_uidlist);
3332 mailimap_search_result_free(lep_uidlist);
3334 flagged = g_slist_concat(flagged, uidlist);
3339 p_answered = answered;
3340 p_flagged = flagged;
3342 for (elem = sorted_list; elem != NULL; elem = g_slist_next(elem)) {
3347 msginfo = (MsgInfo *) elem->data;
3348 flags = msginfo->flags.perm_flags;
3349 wasnew = (flags & MSG_NEW);
3350 flags &= ~((reverse_seen ? 0 : MSG_UNREAD | MSG_NEW) | MSG_REPLIED | MSG_MARKED);
3352 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
3353 if (gslist_find_next_num(&p_unseen, msginfo->msgnum) == msginfo->msgnum) {
3354 if (!reverse_seen) {
3355 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
3357 flags &= ~(MSG_UNREAD | MSG_NEW);
3360 if (gslist_find_next_num(&p_answered, msginfo->msgnum) == msginfo->msgnum)
3361 flags |= MSG_REPLIED;
3362 if (gslist_find_next_num(&p_flagged, msginfo->msgnum) == msginfo->msgnum)
3363 flags |= MSG_MARKED;
3364 g_relation_insert(msgflags, msginfo, GINT_TO_POINTER(flags));
3367 imap_lep_set_free(seq_list);
3368 g_slist_free(flagged);
3369 g_slist_free(answered);
3370 g_slist_free(unseen);
3371 g_slist_free(sorted_list);
3372 g_string_free(cmd_buf, TRUE);
3375 return GINT_TO_POINTER(0);
3378 static gint imap_get_flags(Folder *folder, FolderItem *item,
3379 MsgInfoList *msginfo_list, GRelation *msgflags)
3382 get_flags_data *data = g_new0(get_flags_data, 1);
3384 data->folder = folder;
3386 data->msginfo_list = msginfo_list;
3387 data->msgflags = msgflags;
3389 if (prefs_common.work_offline && !imap_gtk_should_override()) {
3394 result = GPOINTER_TO_INT(imap_get_flags_thread(data));
3401 static gboolean process_flags(gpointer key, gpointer value, gpointer user_data)
3403 gboolean flags_set = GPOINTER_TO_INT(user_data);
3404 gint flags_value = GPOINTER_TO_INT(key);
3405 hashtable_data *data = (hashtable_data *)value;
3407 data->msglist = g_slist_reverse(data->msglist);
3409 debug_print("IMAP %ssetting flags to %d for %d messages\n",
3412 g_slist_length(data->msglist));
3413 imap_set_message_flags(data->session, data->msglist, flags_value, flags_set);
3415 g_slist_free(data->msglist);
3420 static void process_hashtable(void)
3422 if (flags_set_table) {
3423 g_hash_table_foreach_remove(flags_set_table, process_flags, GINT_TO_POINTER(TRUE));
3424 g_free(flags_set_table);
3425 flags_set_table = NULL;
3427 if (flags_unset_table) {
3428 g_hash_table_foreach_remove(flags_unset_table, process_flags, GINT_TO_POINTER(FALSE));
3429 g_free(flags_unset_table);
3430 flags_unset_table = NULL;
3434 static IMAPFolderItem *batching_item = NULL;
3436 static void imap_set_batch (Folder *folder, FolderItem *_item, gboolean batch)
3438 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3440 g_return_if_fail(item != NULL);
3442 if (batch && batching_item != NULL) {
3443 g_warning("already batching on %s\n", batching_item->item.path);
3447 if (item->batching == batch)
3450 item->batching = batch;
3452 batching_item = batch?item:NULL;
3455 debug_print("IMAP switching to batch mode\n");
3456 if (flags_set_table) {
3457 g_warning("flags_set_table non-null but we just entered batch mode!\n");
3458 flags_set_table = NULL;
3460 if (flags_unset_table) {
3461 g_warning("flags_unset_table non-null but we just entered batch mode!\n");
3462 flags_unset_table = NULL;
3464 flags_set_table = g_hash_table_new(NULL, g_direct_equal);
3465 flags_unset_table = g_hash_table_new(NULL, g_direct_equal);
3467 debug_print("IMAP switching away from batch mode\n");
3469 process_hashtable();
3475 /* data types conversion libetpan <-> sylpheed */
3479 #define ETPAN_IMAP_MB_MARKED 1
3480 #define ETPAN_IMAP_MB_UNMARKED 2
3481 #define ETPAN_IMAP_MB_NOSELECT 4
3482 #define ETPAN_IMAP_MB_NOINFERIORS 8
3484 static int imap_flags_to_flags(struct mailimap_mbx_list_flags * imap_flags)
3490 if (imap_flags->mbf_type == MAILIMAP_MBX_LIST_FLAGS_SFLAG) {
3491 switch (imap_flags->mbf_sflag) {
3492 case MAILIMAP_MBX_LIST_SFLAG_MARKED:
3493 flags |= ETPAN_IMAP_MB_MARKED;
3495 case MAILIMAP_MBX_LIST_SFLAG_NOSELECT:
3496 flags |= ETPAN_IMAP_MB_NOSELECT;
3498 case MAILIMAP_MBX_LIST_SFLAG_UNMARKED:
3499 flags |= ETPAN_IMAP_MB_UNMARKED;
3504 for(cur = clist_begin(imap_flags->mbf_oflags) ; cur != NULL ;
3505 cur = clist_next(cur)) {
3506 struct mailimap_mbx_list_oflag * oflag;
3508 oflag = clist_content(cur);
3510 switch (oflag->of_type) {
3511 case MAILIMAP_MBX_LIST_OFLAG_NOINFERIORS:
3512 flags |= ETPAN_IMAP_MB_NOINFERIORS;
3520 static GSList * imap_list_from_lep(IMAPFolder * folder,
3521 clist * list, const gchar * real_path)
3528 for(iter = clist_begin(list) ; iter != NULL ;
3529 iter = clist_next(iter)) {
3530 struct mailimap_mailbox_list * mb;
3538 FolderItem *new_item;
3540 mb = clist_content(iter);
3543 if (mb->mb_flag != NULL)
3544 flags = imap_flags_to_flags(mb->mb_flag);
3546 delimiter = mb->mb_delimiter;
3549 dup_name = strdup(name);
3550 if (delimiter != '\0')
3551 subst_char(dup_name, delimiter, '/');
3553 base = g_path_get_basename(dup_name);
3554 if (base[0] == '.') {
3560 if (strcmp(dup_name, real_path) == 0) {
3566 loc_name = imap_modified_utf7_to_utf8(base);
3567 loc_path = imap_modified_utf7_to_utf8(dup_name);
3569 new_item = folder_item_new(FOLDER(folder), loc_name, loc_path);
3570 if ((flags & ETPAN_IMAP_MB_NOINFERIORS) != 0)
3571 new_item->no_sub = TRUE;
3572 if (strcmp(dup_name, "INBOX") != 0 &&
3573 ((flags & ETPAN_IMAP_MB_NOSELECT) != 0))
3574 new_item->no_select = TRUE;
3576 item_list = g_slist_append(item_list, new_item);
3578 debug_print("folder '%s' found.\n", loc_path);
3589 static GSList * imap_get_lep_set_from_numlist(MsgNumberList *numlist)
3591 GSList *sorted_list, *cur;
3592 guint first, last, next;
3593 GSList *ret_list = NULL;
3595 struct mailimap_set * current_set;
3596 unsigned int item_count;
3598 if (numlist == NULL)
3602 current_set = mailimap_set_new_empty();
3604 sorted_list = g_slist_copy(numlist);
3605 sorted_list = g_slist_sort(sorted_list, g_int_compare);
3607 first = GPOINTER_TO_INT(sorted_list->data);
3610 for (cur = sorted_list; cur != NULL; cur = g_slist_next(cur)) {
3611 if (GPOINTER_TO_INT(cur->data) == 0)
3616 last = GPOINTER_TO_INT(cur->data);
3618 next = GPOINTER_TO_INT(cur->next->data);
3622 if (last + 1 != next || next == 0) {
3624 struct mailimap_set_item * item;
3625 item = mailimap_set_item_new(first, last);
3626 mailimap_set_add(current_set, item);
3631 if (count >= IMAP_SET_MAX_COUNT) {
3632 ret_list = g_slist_append(ret_list,
3634 current_set = mailimap_set_new_empty();
3641 if (clist_count(current_set->set_list) > 0) {
3642 ret_list = g_slist_append(ret_list,
3646 g_slist_free(sorted_list);
3651 static GSList * imap_get_lep_set_from_msglist(MsgInfoList *msglist)
3653 MsgNumberList *numlist = NULL;
3657 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
3658 MsgInfo *msginfo = (MsgInfo *) cur->data;
3660 numlist = g_slist_append(numlist, GINT_TO_POINTER(msginfo->msgnum));
3662 seq_list = imap_get_lep_set_from_numlist(numlist);
3663 g_slist_free(numlist);
3668 static GSList * imap_uid_list_from_lep(clist * list)
3675 for(iter = clist_begin(list) ; iter != NULL ;
3676 iter = clist_next(iter)) {
3679 puid = clist_content(iter);
3680 result = g_slist_append(result, GINT_TO_POINTER(* puid));
3686 static GSList * imap_uid_list_from_lep_tab(carray * list)
3693 for(i = 0 ; i < carray_count(list) ; i ++) {
3696 puid = carray_get(list, i);
3697 result = g_slist_append(result, GINT_TO_POINTER(* puid));
3703 static MsgInfo *imap_envelope_from_lep(struct imap_fetch_env_info * info,
3706 MsgInfo *msginfo = NULL;
3709 MsgFlags flags = {0, 0};
3711 MSG_SET_TMP_FLAGS(flags, MSG_IMAP);
3712 if (item->stype == F_QUEUE) {
3713 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
3714 } else if (item->stype == F_DRAFT) {
3715 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
3717 flags.perm_flags = info->flags;
3721 msginfo = procheader_parse_str(info->headers, flags, FALSE, FALSE);
3724 msginfo->msgnum = uid;
3725 msginfo->size = size;
3731 static void imap_lep_set_free(GSList *seq_list)
3735 for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) {
3736 struct mailimap_set * imapset;
3738 imapset = cur->data;
3739 mailimap_set_free(imapset);
3741 g_slist_free(seq_list);
3744 static struct mailimap_flag_list * imap_flag_to_lep(IMAPFlags flags)
3746 struct mailimap_flag_list * flag_list;
3748 flag_list = mailimap_flag_list_new_empty();
3750 if (IMAP_IS_SEEN(flags))
3751 mailimap_flag_list_add(flag_list,
3752 mailimap_flag_new_seen());
3753 if (IMAP_IS_ANSWERED(flags))
3754 mailimap_flag_list_add(flag_list,
3755 mailimap_flag_new_answered());
3756 if (IMAP_IS_FLAGGED(flags))
3757 mailimap_flag_list_add(flag_list,
3758 mailimap_flag_new_flagged());
3759 if (IMAP_IS_DELETED(flags))
3760 mailimap_flag_list_add(flag_list,
3761 mailimap_flag_new_deleted());
3762 if (IMAP_IS_DRAFT(flags))
3763 mailimap_flag_list_add(flag_list,
3764 mailimap_flag_new_draft());
3769 guint imap_folder_get_refcnt(Folder *folder)
3771 return ((IMAPFolder *)folder)->refcnt;
3774 void imap_folder_ref(Folder *folder)
3776 ((IMAPFolder *)folder)->refcnt++;
3779 void imap_folder_unref(Folder *folder)
3781 if (((IMAPFolder *)folder)->refcnt > 0)
3782 ((IMAPFolder *)folder)->refcnt--;
3785 #else /* HAVE_LIBETPAN */
3787 static FolderClass imap_class;
3789 static Folder *imap_folder_new (const gchar *name,
3794 static gint imap_create_tree (Folder *folder)
3798 static FolderItem *imap_create_folder (Folder *folder,
3804 static gint imap_rename_folder (Folder *folder,
3811 FolderClass *imap_get_class(void)
3813 if (imap_class.idstr == NULL) {
3814 imap_class.type = F_IMAP;
3815 imap_class.idstr = "imap";
3816 imap_class.uistr = "IMAP4";
3818 imap_class.new_folder = imap_folder_new;
3819 imap_class.create_tree = imap_create_tree;
3820 imap_class.create_folder = imap_create_folder;
3821 imap_class.rename_folder = imap_rename_folder;
3822 /* nothing implemented */