2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2007 Hiroyuki Yamamoto and the Claws Mail team
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
27 #include <glib/gi18n.h>
34 #include "alertpanel.h"
57 #include "procheader.h"
58 #include "prefs_account.h"
63 #include "prefs_common.h"
64 #include "inputdialog.h"
66 #include "remotefolder.h"
68 #include "statusbar.h"
70 #include "imap-thread.h"
73 typedef struct _IMAPFolder IMAPFolder;
74 typedef struct _IMAPSession IMAPSession;
75 typedef struct _IMAPNameSpace IMAPNameSpace;
76 typedef struct _IMAPFolderItem IMAPFolderItem;
78 #include "prefs_account.h"
80 #define IMAP_FOLDER(obj) ((IMAPFolder *)obj)
81 #define IMAP_FOLDER_ITEM(obj) ((IMAPFolderItem *)obj)
82 #define IMAP_SESSION(obj) ((IMAPSession *)obj)
88 /* list of IMAPNameSpace */
92 gchar last_seen_separator;
100 gboolean authenticated;
109 gboolean folder_content_changed;
116 struct _IMAPNameSpace
122 #define IMAP_SUCCESS 0
123 #define IMAP_SOCKET 2
124 #define IMAP_AUTHFAIL 3
125 #define IMAP_PROTOCOL 4
126 #define IMAP_SYNTAX 5
130 #define IMAPBUFSIZE 8192
134 IMAP_FLAG_SEEN = 1 << 0,
135 IMAP_FLAG_ANSWERED = 1 << 1,
136 IMAP_FLAG_FLAGGED = 1 << 2,
137 IMAP_FLAG_DELETED = 1 << 3,
138 IMAP_FLAG_DRAFT = 1 << 4
141 #define IMAP_IS_SEEN(flags) ((flags & IMAP_FLAG_SEEN) != 0)
142 #define IMAP_IS_ANSWERED(flags) ((flags & IMAP_FLAG_ANSWERED) != 0)
143 #define IMAP_IS_FLAGGED(flags) ((flags & IMAP_FLAG_FLAGGED) != 0)
144 #define IMAP_IS_DELETED(flags) ((flags & IMAP_FLAG_DELETED) != 0)
145 #define IMAP_IS_DRAFT(flags) ((flags & IMAP_FLAG_DRAFT) != 0)
148 #define IMAP4_PORT 143
150 #define IMAPS_PORT 993
153 #define IMAP_CMD_LIMIT 1000
155 struct _IMAPFolderItem
167 guint32 c_uid_validity;
170 GHashTable *flags_set_table;
171 GHashTable *flags_unset_table;
174 static XMLTag *imap_item_get_xml(Folder *folder, FolderItem *item);
175 static void imap_item_set_xml(Folder *folder, FolderItem *item, XMLTag *tag);
177 static void imap_folder_init (Folder *folder,
181 static Folder *imap_folder_new (const gchar *name,
183 static void imap_folder_destroy (Folder *folder);
185 static IMAPSession *imap_session_new (Folder *folder,
186 const PrefsAccount *account);
187 static void imap_session_authenticate(IMAPSession *session,
188 const PrefsAccount *account);
189 static void imap_session_destroy (Session *session);
191 static gchar *imap_fetch_msg (Folder *folder,
194 static gchar *imap_fetch_msg_full (Folder *folder,
199 static gint imap_add_msg (Folder *folder,
203 static gint imap_add_msgs (Folder *folder,
206 GRelation *relation);
208 static gint imap_copy_msg (Folder *folder,
211 static gint imap_copy_msgs (Folder *folder,
213 MsgInfoList *msglist,
214 GRelation *relation);
216 static gint imap_remove_msg (Folder *folder,
219 static gint imap_remove_msgs (Folder *folder,
221 MsgInfoList *msglist,
222 GRelation *relation);
223 static gint imap_remove_all_msg (Folder *folder,
226 static gboolean imap_is_msg_changed (Folder *folder,
230 static gint imap_close (Folder *folder,
233 static gint imap_scan_tree (Folder *folder);
235 static gint imap_create_tree (Folder *folder);
237 static FolderItem *imap_create_folder (Folder *folder,
240 static gint imap_rename_folder (Folder *folder,
243 static gint imap_remove_folder (Folder *folder,
246 static FolderItem *imap_folder_item_new (Folder *folder);
247 static void imap_folder_item_destroy (Folder *folder,
250 static IMAPSession *imap_session_get (Folder *folder);
252 static gint imap_auth (IMAPSession *session,
257 static gint imap_scan_tree_recursive (IMAPSession *session,
261 static void imap_create_missing_folders (Folder *folder);
262 static FolderItem *imap_create_special_folder
264 SpecialFolderItemType stype,
267 static gint imap_do_copy_msgs (Folder *folder,
269 MsgInfoList *msglist,
270 GRelation *relation);
272 static void imap_delete_all_cached_messages (FolderItem *item);
273 static void imap_set_batch (Folder *folder,
276 static gint imap_set_message_flags (IMAPSession *session,
277 MsgNumberList *numlist,
280 static gint imap_select (IMAPSession *session,
286 guint32 *uid_validity,
288 static gint imap_status (IMAPSession *session,
291 IMAPFolderItem *item,
294 guint32 *uid_validity,
298 static gchar imap_get_path_separator (IMAPSession *session,
301 static gchar *imap_get_real_path (IMAPSession *session,
304 static void imap_synchronise (FolderItem *item);
306 static void imap_free_capabilities (IMAPSession *session);
308 /* low-level IMAP4rev1 commands */
309 static gint imap_cmd_login (IMAPSession *session,
313 static gint imap_cmd_noop (IMAPSession *session);
315 static gint imap_cmd_starttls (IMAPSession *session);
317 static gint imap_cmd_select (IMAPSession *session,
322 guint32 *uid_validity,
324 static gint imap_cmd_examine (IMAPSession *session,
329 guint32 *uid_validity,
331 static gint imap_cmd_create (IMAPSession *sock,
332 const gchar *folder);
333 static gint imap_cmd_rename (IMAPSession *sock,
334 const gchar *oldfolder,
335 const gchar *newfolder);
336 static gint imap_cmd_delete (IMAPSession *session,
337 const gchar *folder);
338 static gint imap_cmd_fetch (IMAPSession *sock,
340 const gchar *filename,
343 static gint imap_cmd_append (IMAPSession *session,
344 const gchar *destfolder,
348 static gint imap_cmd_copy (IMAPSession *session,
349 struct mailimap_set * set,
350 const gchar *destfolder,
351 GRelation *uid_mapping,
352 struct mailimap_set ** source,
353 struct mailimap_set ** dest);
354 static gint imap_cmd_store (IMAPSession *session,
355 struct mailimap_set * set,
358 static gint imap_cmd_expunge (IMAPSession *session);
360 static void imap_path_separator_subst (gchar *str,
363 static gchar *imap_utf8_to_modified_utf7 (const gchar *from);
364 static gchar *imap_modified_utf7_to_utf8 (const gchar *mutf7_str);
366 static gboolean imap_rename_folder_func (GNode *node,
368 static gint imap_get_num_list (Folder *folder,
371 gboolean *old_uids_valid);
372 static GSList *imap_get_msginfos (Folder *folder,
374 GSList *msgnum_list);
375 static MsgInfo *imap_get_msginfo (Folder *folder,
378 static gboolean imap_scan_required (Folder *folder,
380 static void imap_change_flags (Folder *folder,
383 MsgPermFlags newflags);
384 static gint imap_get_flags (Folder *folder,
386 MsgInfoList *msglist,
387 GRelation *msgflags);
388 static gchar *imap_folder_get_path (Folder *folder);
389 static gchar *imap_item_get_path (Folder *folder,
391 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item);
394 /* data types conversion libetpan <-> claws */
395 static GSList * imap_list_from_lep(IMAPFolder * folder,
396 clist * list, const gchar * real_path, gboolean all);
397 static GSList * imap_get_lep_set_from_numlist(MsgNumberList *numlist);
398 static GSList * imap_get_lep_set_from_msglist(MsgInfoList *msglist);
399 static GSList * imap_uid_list_from_lep(clist * list);
400 static GSList * imap_uid_list_from_lep_tab(carray * list);
401 static MsgInfo *imap_envelope_from_lep(struct imap_fetch_env_info * info,
403 static void imap_lep_set_free(GSList *seq_list);
404 static struct mailimap_flag_list * imap_flag_to_lep(IMAPFlags flags);
406 typedef struct _hashtable_data {
407 IMAPSession *session;
409 IMAPFolderItem *item;
412 static FolderClass imap_class;
414 typedef struct _thread_data {
424 FolderClass *imap_get_class(void)
426 if (imap_class.idstr == NULL) {
427 imap_class.type = F_IMAP;
428 imap_class.idstr = "imap";
429 imap_class.uistr = "IMAP4";
431 /* Folder functions */
432 imap_class.new_folder = imap_folder_new;
433 imap_class.destroy_folder = imap_folder_destroy;
434 imap_class.scan_tree = imap_scan_tree;
435 imap_class.create_tree = imap_create_tree;
437 /* FolderItem functions */
438 imap_class.item_new = imap_folder_item_new;
439 imap_class.item_destroy = imap_folder_item_destroy;
440 imap_class.item_get_path = imap_item_get_path;
441 imap_class.create_folder = imap_create_folder;
442 imap_class.rename_folder = imap_rename_folder;
443 imap_class.remove_folder = imap_remove_folder;
444 imap_class.close = imap_close;
445 imap_class.get_num_list = imap_get_num_list;
446 imap_class.scan_required = imap_scan_required;
447 imap_class.set_xml = folder_set_xml;
448 imap_class.get_xml = folder_get_xml;
449 imap_class.item_set_xml = imap_item_set_xml;
450 imap_class.item_get_xml = imap_item_get_xml;
452 /* Message functions */
453 imap_class.get_msginfo = imap_get_msginfo;
454 imap_class.get_msginfos = imap_get_msginfos;
455 imap_class.fetch_msg = imap_fetch_msg;
456 imap_class.fetch_msg_full = imap_fetch_msg_full;
457 imap_class.add_msg = imap_add_msg;
458 imap_class.add_msgs = imap_add_msgs;
459 imap_class.copy_msg = imap_copy_msg;
460 imap_class.copy_msgs = imap_copy_msgs;
461 imap_class.remove_msg = imap_remove_msg;
462 imap_class.remove_msgs = imap_remove_msgs;
463 imap_class.remove_all_msg = imap_remove_all_msg;
464 imap_class.is_msg_changed = imap_is_msg_changed;
465 imap_class.change_flags = imap_change_flags;
466 imap_class.get_flags = imap_get_flags;
467 imap_class.set_batch = imap_set_batch;
468 imap_class.synchronise = imap_synchronise;
470 pthread_mutex_init(&imap_mutex, NULL);
477 static Folder *imap_folder_new(const gchar *name, const gchar *path)
481 folder = (Folder *)g_new0(IMAPFolder, 1);
482 folder->klass = &imap_class;
483 imap_folder_init(folder, name, path);
488 static void imap_folder_destroy(Folder *folder)
490 while (imap_folder_get_refcnt(folder) > 0)
491 gtk_main_iteration();
493 folder_remote_folder_destroy(REMOTE_FOLDER(folder));
497 static void imap_folder_init(Folder *folder, const gchar *name,
500 folder_remote_folder_init((Folder *)folder, name, path);
503 static FolderItem *imap_folder_item_new(Folder *folder)
505 IMAPFolderItem *item;
507 item = g_new0(IMAPFolderItem, 1);
510 item->uid_list = NULL;
512 return (FolderItem *)item;
515 static void imap_folder_item_destroy(Folder *folder, FolderItem *_item)
517 IMAPFolderItem *item = (IMAPFolderItem *)_item;
519 g_return_if_fail(item != NULL);
520 g_slist_free(item->uid_list);
525 static gboolean imap_reset_uid_lists_func(GNode *node, gpointer data)
527 IMAPFolderItem *item = (IMAPFolderItem *)node->data;
530 g_slist_free(item->uid_list);
531 item->uid_list = NULL;
536 static void imap_reset_uid_lists(Folder *folder)
538 if(folder->node == NULL)
541 /* Destroy all uid lists and rest last uid */
542 g_node_traverse(folder->node, G_IN_ORDER, G_TRAVERSE_ALL, -1, imap_reset_uid_lists_func, NULL);
545 static int imap_get_capabilities(IMAPSession *session)
547 struct mailimap_capability_data *capabilities = NULL;
551 if (session->capability != NULL)
552 return MAILIMAP_NO_ERROR;
554 capabilities = imap_threaded_capability(session->folder, &result);
556 if (result != MAILIMAP_NO_ERROR) {
557 return MAILIMAP_ERROR_CAPABILITY;
560 if (capabilities == NULL) {
561 return MAILIMAP_NO_ERROR;
564 for(cur = clist_begin(capabilities->cap_list) ; cur != NULL ;
565 cur = clist_next(cur)) {
566 struct mailimap_capability * cap =
568 if (!cap || cap->cap_data.cap_name == NULL)
570 session->capability = g_slist_append
571 (session->capability,
572 g_strdup(cap->cap_data.cap_name));
573 debug_print("got capa %s\n", cap->cap_data.cap_name);
575 mailimap_capability_data_free(capabilities);
576 return MAILIMAP_NO_ERROR;
579 static gboolean imap_has_capability(IMAPSession *session, const gchar *cap)
582 for (cur = session->capability; cur; cur = cur->next) {
583 if (!g_ascii_strcasecmp(cur->data, cap))
589 static gint imap_auth(IMAPSession *session, const gchar *user, const gchar *pass,
592 gint ok = IMAP_ERROR;
593 static time_t last_login_err = 0;
594 gchar *ext_info = "";
596 if (imap_get_capabilities(session) != MAILIMAP_NO_ERROR)
601 ok = imap_cmd_login(session, user, pass, "ANONYMOUS");
603 case IMAP_AUTH_CRAM_MD5:
604 ok = imap_cmd_login(session, user, pass, "CRAM-MD5");
606 case IMAP_AUTH_LOGIN:
607 ok = imap_cmd_login(session, user, pass, "LOGIN");
609 case IMAP_AUTH_GSSAPI:
610 ok = imap_cmd_login(session, user, pass, "GSSAPI");
613 debug_print("capabilities:\n"
618 imap_has_capability(session, "ANONYMOUS"),
619 imap_has_capability(session, "CRAM-MD5"),
620 imap_has_capability(session, "LOGIN"),
621 imap_has_capability(session, "GSSAPI"));
622 if (imap_has_capability(session, "CRAM-MD5"))
623 ok = imap_cmd_login(session, user, pass, "CRAM-MD5");
624 if (ok == IMAP_ERROR && imap_has_capability(session, "GSSAPI"))
625 ok = imap_cmd_login(session, user, pass, "GSSAPI");
626 if (ok == IMAP_ERROR) /* we always try LOGIN before giving up */
627 ok = imap_cmd_login(session, user, pass, "LOGIN");
630 if (ok == IMAP_SUCCESS)
631 session->authenticated = TRUE;
633 if (type == IMAP_AUTH_CRAM_MD5) {
634 ext_info = _("\n\nCRAM-MD5 logins only work if libetpan has been "
635 "compiled with SASL support and the "
636 "CRAM-MD5 SASL plugin is installed.");
639 if (time(NULL) - last_login_err > 10) {
640 if (!prefs_common.no_recv_err_panel) {
641 alertpanel_error(_("Connection to %s failed: "
643 SESSION(session)->server, ext_info);
645 log_error(LOG_PROTOCOL, _("Connection to %s failed: "
646 "login refused.%s\n"),
647 SESSION(session)->server, ext_info);
650 last_login_err = time(NULL);
655 static IMAPSession *imap_reconnect_if_possible(Folder *folder, IMAPSession *session)
657 RemoteFolder *rfolder = REMOTE_FOLDER(folder);
658 /* Check if this is the first try to establish a
659 connection, if yes we don't try to reconnect */
660 debug_print("reconnecting\n");
661 if (rfolder->session == NULL) {
662 log_warning(LOG_PROTOCOL, _("Connecting to %s failed"),
663 folder->account->recv_server);
664 session_destroy(SESSION(session));
667 log_warning(LOG_PROTOCOL, _("IMAP4 connection to %s has been"
668 " disconnected. Reconnecting...\n"),
669 folder->account->recv_server);
670 statusbar_print_all(_("IMAP4 connection to %s has been"
671 " disconnected. Reconnecting...\n"),
672 folder->account->recv_server);
673 SESSION(session)->state = SESSION_DISCONNECTED;
674 session_destroy(SESSION(session));
675 /* Clear folders session to make imap_session_get create
676 a new session, because of rfolder->session == NULL
677 it will not try to reconnect again and so avoid an
679 rfolder->session = NULL;
680 debug_print("getting session...\n");
681 session = imap_session_get(folder);
682 rfolder->session = SESSION(session);
688 static void lock_session(IMAPSession *session)
693 debug_print("locking session %p (%d)\n", session, session->busy);
695 debug_print(" SESSION WAS LOCKED !! \n");
696 session->busy = TRUE;
697 mainwin = mainwindow_get_mainwindow();
699 toolbar_main_set_sensitive(mainwin);
700 main_window_set_menu_sensitive(mainwin);
703 debug_print("can't lock null session\n");
707 static void unlock_session(IMAPSession *session)
712 debug_print("unlocking session %p\n", session);
713 session->busy = FALSE;
714 mainwin = mainwindow_get_mainwindow();
716 toolbar_main_set_sensitive(mainwin);
717 main_window_set_menu_sensitive(mainwin);
720 debug_print("can't unlock null session\n");
724 static IMAPSession *imap_session_get(Folder *folder)
726 RemoteFolder *rfolder = REMOTE_FOLDER(folder);
727 IMAPSession *session = NULL;
729 g_return_val_if_fail(folder != NULL, NULL);
730 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, NULL);
731 g_return_val_if_fail(folder->account != NULL, NULL);
733 if (prefs_common.work_offline &&
734 !inc_offline_should_override(FALSE,
735 _("Claws Mail needs network access in order "
736 "to access the IMAP server."))) {
740 /* Make sure we have a session */
741 if (rfolder->session != NULL) {
742 session = IMAP_SESSION(rfolder->session);
743 } else if (rfolder->connecting) {
744 debug_print("already connecting\n");
747 imap_reset_uid_lists(folder);
748 if (time(NULL) - rfolder->last_failure <= 2)
750 rfolder->connecting = TRUE;
751 session = imap_session_new(folder, folder->account);
753 if(session == NULL) {
754 rfolder->last_failure = time(NULL);
755 rfolder->connecting = FALSE;
759 /* Make sure session is authenticated */
760 if (!IMAP_SESSION(session)->authenticated)
761 imap_session_authenticate(IMAP_SESSION(session), folder->account);
763 if (!IMAP_SESSION(session)->authenticated) {
764 imap_threaded_disconnect(session->folder);
765 SESSION(session)->state = SESSION_DISCONNECTED;
766 session_destroy(SESSION(session));
767 rfolder->session = NULL;
768 rfolder->last_failure = time(NULL);
769 rfolder->connecting = FALSE;
773 lock_session(session);
775 /* I think the point of this code is to avoid sending a
776 * keepalive if we've used the session recently and therefore
777 * think it's still alive. Unfortunately, most of the code
778 * does not yet check for errors on the socket, and so if the
779 * connection drops we don't notice until the timeout expires.
780 * A better solution than sending a NOOP every time would be
781 * for every command to be prepared to retry until it is
782 * successfully sent. -- mbp */
783 if ((time(NULL) - SESSION(session)->last_access_time > SESSION_TIMEOUT_INTERVAL) || session->cancelled) {
784 /* verify that the session is still alive */
785 if (imap_cmd_noop(session) != IMAP_SUCCESS) {
786 debug_print("disconnected!\n");
787 session = imap_reconnect_if_possible(folder, session);
790 session->cancelled = FALSE;
793 rfolder->session = SESSION(session);
794 rfolder->connecting = FALSE;
796 return IMAP_SESSION(session);
799 static IMAPSession *imap_session_new(Folder * folder,
800 const PrefsAccount *account)
802 IMAPSession *session;
805 int authenticated = FALSE;
808 /* FIXME: IMAP over SSL only... */
811 port = account->set_imapport ? account->imapport
812 : account->ssl_imap == SSL_TUNNEL ? IMAPS_PORT : IMAP4_PORT;
813 ssl_type = account->ssl_imap;
815 if (account->ssl_imap != SSL_NONE) {
816 if (alertpanel_full(_("Insecure connection"),
817 _("This connection is configured to be secured "
818 "using SSL, but SSL is not available in this "
819 "build of Claws Mail. \n\n"
820 "Do you want to continue connecting to this "
821 "server? The communication would not be "
823 GTK_STOCK_CANCEL, _("Con_tinue connecting"),
824 NULL, FALSE, NULL, ALERT_WARNING,
825 G_ALERTDEFAULT) != G_ALERTALTERNATE)
828 port = account->set_imapport ? account->imapport
833 statusbar_print_all(_("Connecting to IMAP4 server: %s..."), folder->account->recv_server);
834 if (account->set_tunnelcmd) {
835 r = imap_threaded_connect_cmd(folder,
837 account->recv_server,
842 if (ssl_type == SSL_TUNNEL) {
843 r = imap_threaded_connect_ssl(folder,
844 account->recv_server,
850 r = imap_threaded_connect(folder,
851 account->recv_server,
857 if (r == MAILIMAP_NO_ERROR_AUTHENTICATED) {
858 authenticated = TRUE;
860 else if (r == MAILIMAP_NO_ERROR_NON_AUTHENTICATED) {
861 authenticated = FALSE;
864 #if (LIBETPAN_VERSION_MAJOR > 0 || LIBETPAN_VERSION_MINOR > 48)
866 if (r == MAILIMAP_ERROR_SSL)
867 log_error(LOG_PROTOCOL, _("SSL handshake failed\n"));
870 if(!prefs_common.no_recv_err_panel) {
871 alertpanel_error_log(_("Can't connect to IMAP4 server: %s:%d"),
872 account->recv_server, port);
874 log_error(LOG_PROTOCOL, _("Can't connect to IMAP4 server: %s:%d\n"),
875 account->recv_server, port);
881 session = g_new0(IMAPSession, 1);
882 session_init(SESSION(session));
883 SESSION(session)->type = SESSION_IMAP;
884 SESSION(session)->server = g_strdup(account->recv_server);
885 SESSION(session)->sock = NULL;
887 SESSION(session)->destroy = imap_session_destroy;
889 session->capability = NULL;
891 session->authenticated = authenticated;
892 session->mbox = NULL;
893 session->cmd_count = 0;
894 session->folder = folder;
895 IMAP_FOLDER(session->folder)->last_seen_separator = 0;
898 if (account->ssl_imap == SSL_STARTTLS) {
901 ok = imap_cmd_starttls(session);
902 if (ok != IMAP_SUCCESS) {
903 log_warning(LOG_PROTOCOL, _("Can't start TLS session.\n"));
904 session_destroy(SESSION(session));
908 imap_free_capabilities(session);
909 session->authenticated = FALSE;
910 session->uidplus = FALSE;
911 session->cmd_count = 1;
914 log_message(LOG_PROTOCOL, "IMAP connection is %s-authenticated\n",
915 (session->authenticated) ? "pre" : "un");
920 static void imap_session_authenticate(IMAPSession *session,
921 const PrefsAccount *account)
923 gchar *pass, *acc_pass;
924 gboolean failed = FALSE;
926 g_return_if_fail(account->userid != NULL);
927 acc_pass = account->passwd;
930 if (!pass && account->imap_auth_type != IMAP_AUTH_ANON) {
932 tmp_pass = input_dialog_query_password(account->recv_server, account->userid);
935 Xstrdup_a(pass, tmp_pass, {g_free(tmp_pass); return;});
937 } else if (account->imap_auth_type == IMAP_AUTH_ANON) {
940 statusbar_print_all(_("Connecting to IMAP4 server %s...\n"),
941 account->recv_server);
942 if (imap_auth(session, account->userid, pass, account->imap_auth_type) != IMAP_SUCCESS) {
950 if (prefs_common.no_recv_err_panel) {
951 log_error(LOG_PROTOCOL, _("Couldn't login to IMAP server %s."), account->recv_server);
952 mainwindow_show_error();
954 alertpanel_error_log(_("Couldn't login to IMAP server %s."), account->recv_server);
961 session->authenticated = TRUE;
965 static void imap_session_destroy(Session *session)
967 if (session->state != SESSION_DISCONNECTED)
968 imap_threaded_disconnect(IMAP_SESSION(session)->folder);
970 imap_free_capabilities(IMAP_SESSION(session));
971 g_free(IMAP_SESSION(session)->mbox);
972 sock_close(session->sock);
973 session->sock = NULL;
976 static gchar *imap_fetch_msg(Folder *folder, FolderItem *item, gint uid)
978 return imap_fetch_msg_full(folder, item, uid, TRUE, TRUE);
981 static guint get_file_size_with_crs(const gchar *filename)
987 if (filename == NULL)
990 fp = fopen(filename, "rb");
994 while (fgets(buf, sizeof (buf), fp) != NULL) {
996 if (!strstr(buf, "\r\n") && strstr(buf, "\n"))
1004 static gchar *imap_fetch_msg_full(Folder *folder, FolderItem *item, gint uid,
1005 gboolean headers, gboolean body)
1007 gchar *path, *filename;
1008 IMAPSession *session;
1011 g_return_val_if_fail(folder != NULL, NULL);
1012 g_return_val_if_fail(item != NULL, NULL);
1017 path = folder_item_get_path(item);
1018 if (!is_dir_exist(path))
1019 make_dir_hier(path);
1020 filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(uid), NULL);
1022 debug_print("trying to fetch cached %s\n", filename);
1023 if (is_file_exist(filename)) {
1024 /* see whether the local file represents the whole message
1025 * or not. As the IMAP server reports size with \r chars,
1026 * we have to update the local file (UNIX \n only) size */
1027 MsgInfo *cached = msgcache_get_msg(item->cache,uid);
1028 guint have_size = get_file_size_with_crs(filename);
1031 debug_print("message %d has been already %scached (%d/%d).\n", uid,
1032 have_size >= cached->size ? "fully ":"",
1033 have_size, (int)cached->size);
1035 if (cached && (cached->size <= have_size || !body)) {
1036 procmsg_msginfo_free(cached);
1037 file_strip_crs(filename);
1039 } else if (!cached && time(NULL) - get_file_mtime(filename) < 60) {
1040 debug_print("message not cached and file recent, considering file complete\n");
1041 file_strip_crs(filename);
1044 procmsg_msginfo_free(cached);
1048 debug_print("getting session...\n");
1049 session = imap_session_get(folder);
1056 debug_print("IMAP fetching messages\n");
1057 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
1058 NULL, NULL, NULL, NULL, FALSE);
1059 if (ok != IMAP_SUCCESS) {
1060 g_warning("can't select mailbox %s\n", item->path);
1062 unlock_session(session);
1066 debug_print("getting message %d...\n", uid);
1067 ok = imap_cmd_fetch(session, (guint32)uid, filename, headers, body);
1069 if (ok != IMAP_SUCCESS) {
1070 g_warning("can't fetch message %d\n", uid);
1072 unlock_session(session);
1076 unlock_session(session);
1077 file_strip_crs(filename);
1081 static gboolean imap_is_msg_fully_cached(Folder *folder, FolderItem *item, gint uid)
1083 gchar *path, *filename;
1085 MsgInfo *cached = msgcache_get_msg(item->cache,uid);
1090 path = folder_item_get_path(item);
1091 if (!is_dir_exist(path))
1094 filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(uid), NULL);
1096 if (is_file_exist(filename)) {
1097 if (cached && cached->total_size == cached->size) {
1102 size = get_file_size_with_crs(filename);
1105 if (cached && size >= cached->size) {
1106 cached->total_size = cached->size;
1107 procmsg_msginfo_free(cached);
1111 procmsg_msginfo_free(cached);
1115 void imap_cache_msg(FolderItem *item, gint msgnum)
1117 Folder *folder = NULL;
1121 folder = item->folder;
1123 if (!imap_is_msg_fully_cached(folder, item, msgnum)) {
1124 gchar *tmp = imap_fetch_msg_full(folder, item, msgnum, TRUE, TRUE);
1125 debug_print("fetched %s\n", tmp);
1130 static gint imap_add_msg(Folder *folder, FolderItem *dest,
1131 const gchar *file, MsgFlags *flags)
1135 MsgFileInfo fileinfo;
1137 g_return_val_if_fail(file != NULL, -1);
1139 fileinfo.msginfo = NULL;
1140 fileinfo.file = (gchar *)file;
1141 fileinfo.flags = flags;
1142 file_list.data = &fileinfo;
1143 file_list.next = NULL;
1145 ret = imap_add_msgs(folder, dest, &file_list, NULL);
1149 static gint imap_add_msgs(Folder *folder, FolderItem *dest, GSList *file_list,
1150 GRelation *relation)
1153 IMAPSession *session;
1154 guint32 last_uid = 0;
1156 MsgFileInfo *fileinfo;
1158 gint curnum = 0, total = 0;
1161 g_return_val_if_fail(folder != NULL, -1);
1162 g_return_val_if_fail(dest != NULL, -1);
1163 g_return_val_if_fail(file_list != NULL, -1);
1165 debug_print("getting session...\n");
1166 session = imap_session_get(folder);
1170 destdir = imap_get_real_path(session, IMAP_FOLDER(folder), dest->path);
1172 statusbar_print_all(_("Adding messages..."));
1173 total = g_slist_length(file_list);
1174 for (cur = file_list; cur != NULL; cur = cur->next) {
1175 IMAPFlags iflags = 0;
1176 guint32 new_uid = 0;
1177 gchar *real_file = NULL;
1178 fileinfo = (MsgFileInfo *)cur->data;
1180 statusbar_progress_all(curnum, total, total < 10 ? 1:10);
1183 if (fileinfo->flags) {
1184 if (MSG_IS_MARKED(*fileinfo->flags))
1185 iflags |= IMAP_FLAG_FLAGGED;
1186 if (MSG_IS_REPLIED(*fileinfo->flags))
1187 iflags |= IMAP_FLAG_ANSWERED;
1188 if (!MSG_IS_UNREAD(*fileinfo->flags))
1189 iflags |= IMAP_FLAG_SEEN;
1192 if (real_file == NULL)
1193 real_file = g_strdup(fileinfo->file);
1195 if (folder_has_parent_of_type(dest, F_QUEUE) ||
1196 folder_has_parent_of_type(dest, F_OUTBOX) ||
1197 folder_has_parent_of_type(dest, F_DRAFT) ||
1198 folder_has_parent_of_type(dest, F_TRASH))
1199 iflags |= IMAP_FLAG_SEEN;
1201 ok = imap_cmd_append(session, destdir, real_file, iflags,
1204 if (ok != IMAP_SUCCESS) {
1205 g_warning("can't append message %s\n", real_file);
1208 unlock_session(session);
1209 statusbar_progress_all(0,0,0);
1210 statusbar_pop_all();
1213 debug_print("appended new message as %d\n", new_uid);
1214 /* put the local file in the imapcache, so that we don't
1215 * have to fetch it back later. */
1217 gchar *cache_path = folder_item_get_path(dest);
1218 if (!is_dir_exist(cache_path))
1219 make_dir_hier(cache_path);
1220 if (is_dir_exist(cache_path)) {
1221 gchar *cache_file = g_strconcat(
1222 cache_path, G_DIR_SEPARATOR_S,
1223 itos(new_uid), NULL);
1224 copy_file(real_file, cache_file, TRUE);
1225 debug_print("copied to cache: %s\n", cache_file);
1232 if (relation != NULL)
1233 g_relation_insert(relation, fileinfo->msginfo != NULL ?
1234 (gpointer) fileinfo->msginfo : (gpointer) fileinfo,
1235 GINT_TO_POINTER(dest->last_num + 1));
1237 new_uid = dest->last_num+1;
1239 if (last_uid < new_uid) {
1245 statusbar_progress_all(0,0,0);
1246 statusbar_pop_all();
1248 imap_cmd_expunge(session);
1249 unlock_session(session);
1256 static gint imap_do_copy_msgs(Folder *folder, FolderItem *dest,
1257 MsgInfoList *msglist, GRelation *relation)
1261 GSList *seq_list, *cur;
1263 IMAPSession *session;
1264 gint ok = IMAP_SUCCESS;
1265 GRelation *uid_mapping;
1267 gboolean single = FALSE;
1269 g_return_val_if_fail(folder != NULL, -1);
1270 g_return_val_if_fail(dest != NULL, -1);
1271 g_return_val_if_fail(msglist != NULL, -1);
1273 debug_print("getting session...\n");
1274 session = imap_session_get(folder);
1280 msginfo = (MsgInfo *)msglist->data;
1281 if (msglist->next == NULL)
1283 src = msginfo->folder;
1285 g_warning("the src folder is identical to the dest.\n");
1286 unlock_session(session);
1290 if (src->folder != dest->folder) {
1291 GSList *infolist = NULL, *cur;
1293 for (cur = msglist; cur; cur = cur->next) {
1294 msginfo = (MsgInfo *)cur->data;
1295 MsgFileInfo *fileinfo = g_new0(MsgFileInfo, 1);
1296 fileinfo->file = procmsg_get_message_file(msginfo);
1297 fileinfo->flags = &(msginfo->flags);
1298 infolist = g_slist_prepend(infolist, fileinfo);
1300 infolist = g_slist_reverse(infolist);
1301 unlock_session(session);
1302 res = folder_item_add_msgs(dest, infolist, FALSE);
1303 for (cur = infolist; cur; cur = cur->next) {
1304 MsgFileInfo *info = (MsgFileInfo *)cur->data;
1308 g_slist_free(infolist);
1312 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
1313 NULL, NULL, NULL, NULL, FALSE);
1314 if (ok != IMAP_SUCCESS) {
1315 unlock_session(session);
1319 destdir = imap_get_real_path(session, IMAP_FOLDER(folder), dest->path);
1320 seq_list = imap_get_lep_set_from_msglist(msglist);
1321 uid_mapping = g_relation_new(2);
1322 g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
1324 statusbar_print_all(_("Copying messages..."));
1325 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
1326 struct mailimap_set * seq_set;
1327 struct mailimap_set * source = NULL;
1328 struct mailimap_set * dest = NULL;
1329 seq_set = cur->data;
1331 debug_print("Copying messages from %s to %s ...\n",
1332 src->path, destdir);
1334 ok = imap_cmd_copy(session, seq_set, destdir, uid_mapping,
1337 if (ok == IMAP_SUCCESS) {
1338 if (single && relation && source && dest) {
1339 clistiter *l = clist_begin(source->set_list);
1340 struct mailimap_set_item *i = (struct mailimap_set_item *)clist_content(l);
1341 int snum = i->set_first;
1343 l = clist_begin(dest->set_list);
1344 i = (struct mailimap_set_item *)clist_content(l);
1345 dnum = i->set_first;
1346 g_relation_insert(uid_mapping, GINT_TO_POINTER(snum),
1347 GINT_TO_POINTER(dnum));
1353 mailimap_set_free(source);
1355 mailimap_set_free(dest);
1357 if (ok != IMAP_SUCCESS) {
1358 g_relation_destroy(uid_mapping);
1359 imap_lep_set_free(seq_list);
1360 unlock_session(session);
1361 statusbar_pop_all();
1366 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
1367 MsgInfo *msginfo = (MsgInfo *)cur->data;
1370 tuples = g_relation_select(uid_mapping,
1371 GINT_TO_POINTER(msginfo->msgnum),
1373 if (tuples->len > 0) {
1374 gint num = GPOINTER_TO_INT(g_tuples_index(tuples, 0, 1));
1375 g_relation_insert(relation, msginfo,
1376 GINT_TO_POINTER(num));
1379 debug_print("copied new message as %d\n", num);
1380 /* put the local file in the imapcache, so that we don't
1381 * have to fetch it back later. */
1383 gchar *cache_path = folder_item_get_path(msginfo->folder);
1384 gchar *real_file = g_strconcat(
1385 cache_path, G_DIR_SEPARATOR_S,
1386 itos(msginfo->msgnum), NULL);
1387 gchar *cache_file = NULL;
1389 cache_path = folder_item_get_path(dest);
1390 cache_file = g_strconcat(
1391 cache_path, G_DIR_SEPARATOR_S,
1393 if (!is_dir_exist(cache_path))
1394 make_dir_hier(cache_path);
1395 if (is_file_exist(real_file) && is_dir_exist(cache_path)) {
1396 copy_file(real_file, cache_file, TRUE);
1397 debug_print("copied to cache: %s\n", cache_file);
1404 g_relation_insert(relation, msginfo,
1405 GINT_TO_POINTER(0));
1406 g_tuples_destroy(tuples);
1408 statusbar_pop_all();
1410 g_relation_destroy(uid_mapping);
1411 imap_lep_set_free(seq_list);
1415 IMAP_FOLDER_ITEM(dest)->lastuid = 0;
1416 IMAP_FOLDER_ITEM(dest)->uid_next = 0;
1417 g_slist_free(IMAP_FOLDER_ITEM(dest)->uid_list);
1418 IMAP_FOLDER_ITEM(dest)->uid_list = NULL;
1420 unlock_session(session);
1421 if (ok == IMAP_SUCCESS)
1427 static gint imap_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1431 g_return_val_if_fail(msginfo != NULL, -1);
1433 msglist.data = msginfo;
1434 msglist.next = NULL;
1436 return imap_copy_msgs(folder, dest, &msglist, NULL);
1439 static gint imap_copy_msgs(Folder *folder, FolderItem *dest,
1440 MsgInfoList *msglist, GRelation *relation)
1445 g_return_val_if_fail(folder != NULL, -1);
1446 g_return_val_if_fail(dest != NULL, -1);
1447 g_return_val_if_fail(msglist != NULL, -1);
1449 msginfo = (MsgInfo *)msglist->data;
1450 g_return_val_if_fail(msginfo->folder != NULL, -1);
1452 ret = imap_do_copy_msgs(folder, dest, msglist, relation);
1457 static gint imap_do_remove_msgs(Folder *folder, FolderItem *dest,
1458 MsgInfoList *msglist, GRelation *relation)
1460 gchar *destdir, *dir;
1461 GSList *numlist = NULL, *cur;
1463 IMAPSession *session;
1464 gint ok = IMAP_SUCCESS;
1465 GRelation *uid_mapping;
1467 g_return_val_if_fail(folder != NULL, -1);
1468 g_return_val_if_fail(dest != NULL, -1);
1469 g_return_val_if_fail(msglist != NULL, -1);
1471 debug_print("getting session...\n");
1472 session = imap_session_get(folder);
1477 msginfo = (MsgInfo *)msglist->data;
1479 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
1480 NULL, NULL, NULL, NULL, FALSE);
1481 if (ok != IMAP_SUCCESS) {
1482 unlock_session(session);
1486 destdir = imap_get_real_path(session, IMAP_FOLDER(folder), dest->path);
1487 for (cur = msglist; cur; cur = cur->next) {
1488 msginfo = (MsgInfo *)cur->data;
1489 if (!MSG_IS_DELETED(msginfo->flags))
1490 numlist = g_slist_prepend(numlist, GINT_TO_POINTER(msginfo->msgnum));
1492 numlist = g_slist_reverse(numlist);
1494 uid_mapping = g_relation_new(2);
1495 g_relation_index(uid_mapping, 0, g_direct_hash, g_direct_equal);
1497 ok = imap_set_message_flags
1498 (session, numlist, IMAP_FLAG_DELETED, TRUE);
1499 if (ok != IMAP_SUCCESS) {
1500 log_warning(LOG_PROTOCOL, _("can't set deleted flags\n"));
1501 unlock_session(session);
1504 ok = imap_cmd_expunge(session);
1505 if (ok != IMAP_SUCCESS) {
1506 log_warning(LOG_PROTOCOL, _("can't expunge\n"));
1507 unlock_session(session);
1511 dir = folder_item_get_path(msginfo->folder);
1512 if (is_dir_exist(dir)) {
1513 for (cur = msglist; cur; cur = cur->next) {
1514 msginfo = (MsgInfo *)cur->data;
1515 remove_numbered_files(dir, msginfo->msgnum, msginfo->msgnum);
1520 g_relation_destroy(uid_mapping);
1521 g_slist_free(numlist);
1524 unlock_session(session);
1525 if (ok == IMAP_SUCCESS)
1531 static gint imap_remove_msgs(Folder *folder, FolderItem *dest,
1532 MsgInfoList *msglist, GRelation *relation)
1536 g_return_val_if_fail(folder != NULL, -1);
1537 g_return_val_if_fail(dest != NULL, -1);
1538 if (msglist == NULL)
1541 msginfo = (MsgInfo *)msglist->data;
1542 g_return_val_if_fail(msginfo->folder != NULL, -1);
1544 return imap_do_remove_msgs(folder, dest, msglist, relation);
1547 static gint imap_remove_all_msg(Folder *folder, FolderItem *item)
1549 GSList *list = folder_item_get_msg_list(item);
1550 gint res = imap_remove_msgs(folder, item, list, NULL);
1551 procmsg_msg_list_free(list);
1555 static gboolean imap_is_msg_changed(Folder *folder, FolderItem *item,
1558 /* TODO: properly implement this method */
1562 static gint imap_close(Folder *folder, FolderItem *item)
1567 gint imap_scan_tree_real(Folder *folder, gboolean subs_only)
1569 FolderItem *item = NULL;
1570 IMAPSession *session;
1571 gchar *root_folder = NULL;
1573 g_return_val_if_fail(folder != NULL, -1);
1574 g_return_val_if_fail(folder->account != NULL, -1);
1576 debug_print("getting session...\n");
1577 session = imap_session_get(folder);
1579 if (!folder->node) {
1580 folder_tree_destroy(folder);
1581 item = folder_item_new(folder, folder->name, NULL);
1582 item->folder = folder;
1583 folder->node = item->node = g_node_new(item);
1588 if (folder->account->imap_dir && *folder->account->imap_dir) {
1593 Xstrdup_a(root_folder, folder->account->imap_dir, {unlock_session(session);return -1;});
1594 extract_quote(root_folder, '"');
1595 subst_char(root_folder,
1596 imap_get_path_separator(session, IMAP_FOLDER(folder),
1599 strtailchomp(root_folder, '/');
1600 real_path = imap_get_real_path
1601 (session, IMAP_FOLDER(folder), root_folder);
1602 debug_print("IMAP root directory: %s\n", real_path);
1604 /* check if root directory exist */
1606 r = imap_threaded_list(session->folder, "", real_path,
1608 if ((r != MAILIMAP_NO_ERROR) || (clist_count(lep_list) == 0)) {
1609 if (!folder->node) {
1610 item = folder_item_new(folder, folder->name, NULL);
1611 item->folder = folder;
1612 folder->node = item->node = g_node_new(item);
1614 unlock_session(session);
1617 mailimap_list_result_free(lep_list);
1623 item = FOLDER_ITEM(folder->node->data);
1625 if (item && !item->path && root_folder) {
1626 item->path = g_strdup(root_folder);
1629 if (!item || ((item->path || root_folder) &&
1630 strcmp2(item->path, root_folder) != 0)) {
1631 folder_tree_destroy(folder);
1632 item = folder_item_new(folder, folder->name, root_folder);
1633 item->folder = folder;
1634 folder->node = item->node = g_node_new(item);
1637 imap_scan_tree_recursive(session, FOLDER_ITEM(folder->node->data), subs_only);
1638 imap_create_missing_folders(folder);
1639 unlock_session(session);
1644 static gint imap_scan_tree(Folder *folder)
1646 gboolean subs_only = FALSE;
1647 if (folder->account) {
1648 debug_print(" scanning only subs %d\n", folder->account->imap_subsonly);
1649 subs_only = folder->account->imap_subsonly;
1651 return imap_scan_tree_real(folder, subs_only);
1654 static gint imap_scan_tree_recursive(IMAPSession *session, FolderItem *item, gboolean subs_only)
1657 IMAPFolder *imapfolder;
1658 FolderItem *new_item;
1659 GSList *item_list, *cur;
1662 gchar *wildcard_path;
1668 g_return_val_if_fail(item != NULL, -1);
1669 g_return_val_if_fail(item->folder != NULL, -1);
1670 g_return_val_if_fail(item->no_sub == FALSE, -1);
1672 folder = item->folder;
1673 imapfolder = IMAP_FOLDER(folder);
1675 separator = imap_get_path_separator(session, imapfolder, item->path);
1677 if (folder->ui_func)
1678 folder->ui_func(folder, item, folder->ui_func_data);
1681 wildcard[0] = separator;
1684 real_path = imap_get_real_path(session, imapfolder, item->path);
1688 real_path = g_strdup("");
1691 Xstrcat_a(wildcard_path, real_path, wildcard,
1692 {g_free(real_path); return IMAP_ERROR;});
1696 r = imap_threaded_lsub(folder, "", wildcard_path, &lep_list);
1698 r = imap_threaded_list(folder, "", wildcard_path, &lep_list);
1700 if (r != MAILIMAP_NO_ERROR) {
1704 item_list = imap_list_from_lep(imapfolder,
1705 lep_list, real_path, FALSE);
1706 mailimap_list_result_free(lep_list);
1711 node = item->node->children;
1712 while (node != NULL) {
1713 FolderItem *old_item = FOLDER_ITEM(node->data);
1714 GNode *next = node->next;
1717 for (cur = item_list; cur != NULL; cur = cur->next) {
1718 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1719 if (!strcmp2(old_item->path, cur_item->path)) {
1720 new_item = cur_item;
1725 if (old_item && old_item->path && !strcmp(old_item->path, "INBOX")) {
1726 debug_print("not removing INBOX\n");
1728 debug_print("folder '%s' not found. removing...\n",
1730 folder_item_remove(old_item);
1733 old_item->no_sub = new_item->no_sub;
1734 old_item->no_select = new_item->no_select;
1735 if (old_item->no_sub == TRUE && node->children) {
1736 debug_print("folder '%s' doesn't have "
1737 "subfolders. removing...\n",
1739 folder_item_remove_children(old_item);
1746 for (cur = item_list; cur != NULL; cur = cur->next) {
1747 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1750 for (node = item->node->children; node != NULL;
1751 node = node->next) {
1752 if (!strcmp2(FOLDER_ITEM(node->data)->path,
1754 new_item = FOLDER_ITEM(node->data);
1755 folder_item_destroy(cur_item);
1761 new_item = cur_item;
1762 debug_print("new folder '%s' found.\n", new_item->path);
1763 folder_item_append(item, new_item);
1766 if (!strcmp(new_item->path, "INBOX")) {
1767 new_item->stype = F_INBOX;
1768 folder->inbox = new_item;
1769 } else if (!folder_item_parent(item) || item->stype == F_INBOX) {
1772 base = g_path_get_basename(new_item->path);
1774 if (!folder->outbox && !g_ascii_strcasecmp(base, "Sent")) {
1775 new_item->stype = F_OUTBOX;
1776 folder->outbox = new_item;
1777 } else if (!folder->draft && !g_ascii_strcasecmp(base, "Drafts")) {
1778 new_item->stype = F_DRAFT;
1779 folder->draft = new_item;
1780 } else if (!folder->queue && !g_ascii_strcasecmp(base, "Queue")) {
1781 new_item->stype = F_QUEUE;
1782 folder->queue = new_item;
1783 } else if (!folder->trash && !g_ascii_strcasecmp(base, "Trash")) {
1784 new_item->stype = F_TRASH;
1785 folder->trash = new_item;
1790 if (new_item->no_sub == FALSE)
1791 imap_scan_tree_recursive(session, new_item, subs_only);
1794 g_slist_free(item_list);
1796 return IMAP_SUCCESS;
1799 GList *imap_scan_subtree(Folder *folder, FolderItem *item, gboolean unsubs_only, gboolean recursive)
1801 IMAPSession *session = imap_session_get(folder);
1803 gchar *wildcard_path;
1807 GSList *item_list = NULL, *cur;
1808 GList *child_list = NULL, *tmplist = NULL;
1809 GSList *sub_list = NULL;
1815 separator = imap_get_path_separator(session, IMAP_FOLDER(folder), item->path);
1818 wildcard[0] = separator;
1821 real_path = imap_get_real_path(session, IMAP_FOLDER(folder), item->path);
1825 real_path = g_strdup("");
1828 Xstrcat_a(wildcard_path, real_path, wildcard,
1829 {g_free(real_path); return NULL;});
1833 statusbar_print_all(_("Looking for unsubscribed folders in %s..."),
1834 item->path?item->path:item->name);
1836 statusbar_print_all(_("Looking for subfolders of %s..."),
1837 item->path?item->path:item->name);
1839 r = imap_threaded_list(folder, "", wildcard_path, &lep_list);
1841 statusbar_pop_all();
1844 item_list = imap_list_from_lep(IMAP_FOLDER(folder),
1845 lep_list, real_path, FALSE);
1846 mailimap_list_result_free(lep_list);
1848 for (cur = item_list; cur != NULL; cur = cur->next) {
1849 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1851 tmplist = imap_scan_subtree(folder, cur_item,
1852 unsubs_only, recursive);
1854 child_list = g_list_concat(child_list, tmplist);
1856 child_list = g_list_prepend(child_list,
1857 imap_get_real_path(session,
1858 IMAP_FOLDER(folder), cur_item->path));
1860 folder_item_destroy(cur_item);
1862 child_list = g_list_reverse(child_list);
1863 g_slist_free(item_list);
1866 r = imap_threaded_lsub(folder, "", wildcard_path, &lep_list);
1868 statusbar_pop_all();
1871 sub_list = imap_list_from_lep(IMAP_FOLDER(folder),
1872 lep_list, real_path, FALSE);
1873 mailimap_list_result_free(lep_list);
1875 for (cur = sub_list; cur != NULL; cur = cur->next) {
1876 FolderItem *cur_item = FOLDER_ITEM(cur->data);
1877 GList *oldlitem = NULL;
1878 gchar *tmp = imap_get_real_path(session,
1879 IMAP_FOLDER(folder), cur_item->path);
1880 folder_item_destroy(cur_item);
1881 oldlitem = g_list_find_custom(
1882 child_list, tmp, (GCompareFunc)strcmp2);
1884 child_list = g_list_remove_link(child_list, oldlitem);
1885 g_free(oldlitem->data);
1886 g_list_free(oldlitem);
1892 statusbar_pop_all();
1897 static gint imap_create_tree(Folder *folder)
1899 g_return_val_if_fail(folder != NULL, -1);
1900 g_return_val_if_fail(folder->node != NULL, -1);
1901 g_return_val_if_fail(folder->node->data != NULL, -1);
1902 g_return_val_if_fail(folder->account != NULL, -1);
1904 imap_scan_tree(folder);
1905 imap_create_missing_folders(folder);
1910 static void imap_create_missing_folders(Folder *folder)
1912 g_return_if_fail(folder != NULL);
1915 folder->inbox = imap_create_special_folder
1916 (folder, F_INBOX, "INBOX");
1918 folder->trash = imap_create_special_folder
1919 (folder, F_TRASH, "Trash");
1921 folder->queue = imap_create_special_folder
1922 (folder, F_QUEUE, "Queue");
1923 if (!folder->outbox)
1924 folder->outbox = imap_create_special_folder
1925 (folder, F_OUTBOX, "Sent");
1927 folder->draft = imap_create_special_folder
1928 (folder, F_DRAFT, "Drafts");
1931 static FolderItem *imap_create_special_folder(Folder *folder,
1932 SpecialFolderItemType stype,
1936 FolderItem *new_item;
1938 g_return_val_if_fail(folder != NULL, NULL);
1939 g_return_val_if_fail(folder->node != NULL, NULL);
1940 g_return_val_if_fail(folder->node->data != NULL, NULL);
1941 g_return_val_if_fail(folder->account != NULL, NULL);
1942 g_return_val_if_fail(name != NULL, NULL);
1944 item = FOLDER_ITEM(folder->node->data);
1945 new_item = imap_create_folder(folder, item, name);
1948 g_warning("Can't create '%s'\n", name);
1949 if (!folder->inbox) return NULL;
1951 new_item = imap_create_folder(folder, folder->inbox, name);
1953 g_warning("Can't create '%s' under INBOX\n", name);
1955 new_item->stype = stype;
1957 new_item->stype = stype;
1962 static gchar *imap_folder_get_path(Folder *folder)
1966 g_return_val_if_fail(folder != NULL, NULL);
1967 g_return_val_if_fail(folder->account != NULL, NULL);
1969 folder_path = g_strconcat(get_imap_cache_dir(),
1971 folder->account->recv_server,
1973 folder->account->userid,
1979 static gchar *imap_item_get_path(Folder *folder, FolderItem *item)
1981 gchar *folder_path, *path;
1983 g_return_val_if_fail(folder != NULL, NULL);
1984 g_return_val_if_fail(item != NULL, NULL);
1985 folder_path = imap_folder_get_path(folder);
1987 g_return_val_if_fail(folder_path != NULL, NULL);
1988 if (folder_path[0] == G_DIR_SEPARATOR) {
1990 path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
1993 path = g_strdup(folder_path);
1996 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1997 folder_path, G_DIR_SEPARATOR_S,
2000 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
2003 g_free(folder_path);
2008 static FolderItem *imap_create_folder(Folder *folder, FolderItem *parent,
2011 gchar *dirpath, *imap_path;
2012 IMAPSession *session;
2013 FolderItem *new_item;
2018 gboolean no_select = FALSE, no_sub = FALSE;
2019 gboolean exist = FALSE;
2021 g_return_val_if_fail(folder != NULL, NULL);
2022 g_return_val_if_fail(folder->account != NULL, NULL);
2023 g_return_val_if_fail(parent != NULL, NULL);
2024 g_return_val_if_fail(name != NULL, NULL);
2026 debug_print("getting session...\n");
2027 session = imap_session_get(folder);
2032 if (!folder_item_parent(parent) && strcmp(name, "INBOX") == 0) {
2033 dirpath = g_strdup(name);
2034 }else if (parent->path)
2035 dirpath = g_strconcat(parent->path, "/", name, NULL);
2036 else if ((p = strchr(name, '/')) != NULL && *(p + 1) != '\0')
2037 dirpath = g_strdup(name);
2038 else if (folder->account->imap_dir && *folder->account->imap_dir) {
2041 Xstrdup_a(imap_dir, folder->account->imap_dir, {unlock_session(session);return NULL;});
2042 strtailchomp(imap_dir, '/');
2043 dirpath = g_strconcat(imap_dir, "/", name, NULL);
2045 dirpath = g_strdup(name);
2049 /* keep trailing directory separator to create a folder that contains
2051 imap_path = imap_utf8_to_modified_utf7(dirpath);
2053 strtailchomp(dirpath, '/');
2054 Xstrdup_a(new_name, name, {
2056 unlock_session(session);
2059 separator = imap_get_path_separator(session, IMAP_FOLDER(folder), imap_path);
2060 imap_path_separator_subst(imap_path, separator);
2061 /* remove trailing / for display */
2062 strtailchomp(new_name, '/');
2064 if (strcmp(dirpath, "INBOX") != 0) {
2069 argbuf = g_ptr_array_new();
2070 r = imap_threaded_list(folder, "", imap_path, &lep_list);
2071 if (r != MAILIMAP_NO_ERROR) {
2072 log_warning(LOG_PROTOCOL, _("can't create mailbox: LIST failed\n"));
2075 ptr_array_free_strings(argbuf);
2076 g_ptr_array_free(argbuf, TRUE);
2077 unlock_session(session);
2081 if (clist_count(lep_list) > 0)
2083 mailimap_list_result_free(lep_list);
2086 ok = imap_cmd_create(session, imap_path);
2087 if (ok != IMAP_SUCCESS) {
2088 log_warning(LOG_PROTOCOL, _("can't create mailbox\n"));
2091 unlock_session(session);
2094 r = imap_threaded_list(folder, "", imap_path, &lep_list);
2095 if (r == MAILIMAP_NO_ERROR) {
2096 GSList *item_list = imap_list_from_lep(IMAP_FOLDER(folder),
2097 lep_list, dirpath, TRUE);
2099 FolderItem *cur_item = FOLDER_ITEM(item_list->data);
2100 no_select = cur_item->no_select;
2101 no_sub = cur_item->no_sub;
2102 g_slist_free(item_list);
2104 mailimap_list_result_free(lep_list);
2107 imap_threaded_subscribe(folder, imap_path, TRUE);
2111 /* just get flags */
2112 r = imap_threaded_list(folder, "", "INBOX", &lep_list);
2113 if (r == MAILIMAP_NO_ERROR) {
2114 GSList *item_list = imap_list_from_lep(IMAP_FOLDER(folder),
2115 lep_list, dirpath, TRUE);
2117 FolderItem *cur_item = FOLDER_ITEM(item_list->data);
2118 no_select = cur_item->no_select;
2119 no_sub = cur_item->no_sub;
2120 g_slist_free(item_list);
2122 mailimap_list_result_free(lep_list);
2126 new_item = folder_item_new(folder, new_name, dirpath);
2127 new_item->no_select = no_select;
2128 new_item->no_sub = no_sub;
2129 folder_item_append(parent, new_item);
2133 dirpath = folder_item_get_path(new_item);
2134 if (!is_dir_exist(dirpath))
2135 make_dir_hier(dirpath);
2137 unlock_session(session);
2140 /* folder existed, scan it */
2141 folder_item_scan_full(new_item, FALSE);
2147 static gint imap_rename_folder(Folder *folder, FolderItem *item,
2152 gchar *real_oldpath;
2153 gchar *real_newpath;
2155 gchar *old_cache_dir;
2156 gchar *new_cache_dir;
2157 IMAPSession *session;
2160 gint exists, recent, unseen;
2161 guint32 uid_validity;
2163 g_return_val_if_fail(folder != NULL, -1);
2164 g_return_val_if_fail(item != NULL, -1);
2165 g_return_val_if_fail(item->path != NULL, -1);
2166 g_return_val_if_fail(name != NULL, -1);
2168 debug_print("getting session...\n");
2169 session = imap_session_get(folder);
2174 if (strchr(name, imap_get_path_separator(session, IMAP_FOLDER(folder), item->path)) != NULL) {
2175 g_warning(_("New folder name must not contain the namespace "
2177 unlock_session(session);
2181 real_oldpath = imap_get_real_path(session, IMAP_FOLDER(folder), item->path);
2183 g_free(session->mbox);
2184 session->mbox = NULL;
2185 ok = imap_cmd_examine(session, "INBOX",
2186 &exists, &recent, &unseen, &uid_validity, FALSE);
2187 if (ok != IMAP_SUCCESS) {
2188 g_free(real_oldpath);
2189 unlock_session(session);
2193 separator = imap_get_path_separator(session, IMAP_FOLDER(folder), item->path);
2194 if (strchr(item->path, G_DIR_SEPARATOR)) {
2195 dirpath = g_path_get_dirname(item->path);
2196 newpath = g_strconcat(dirpath, G_DIR_SEPARATOR_S, name, NULL);
2199 newpath = g_strdup(name);
2201 real_newpath = imap_utf8_to_modified_utf7(newpath);
2202 imap_path_separator_subst(real_newpath, separator);
2204 ok = imap_cmd_rename(session, real_oldpath, real_newpath);
2205 if (ok != IMAP_SUCCESS) {
2206 log_warning(LOG_PROTOCOL, _("can't rename mailbox: %s to %s\n"),
2207 real_oldpath, real_newpath);
2208 g_free(real_oldpath);
2210 g_free(real_newpath);
2211 unlock_session(session);
2215 item->name = g_strdup(name);
2217 old_cache_dir = folder_item_get_path(item);
2219 paths[0] = g_strdup(item->path);
2221 g_node_traverse(item->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
2222 imap_rename_folder_func, paths);
2224 if (is_dir_exist(old_cache_dir)) {
2225 new_cache_dir = folder_item_get_path(item);
2226 if (rename(old_cache_dir, new_cache_dir) < 0) {
2227 FILE_OP_ERROR(old_cache_dir, "rename");
2229 g_free(new_cache_dir);
2232 g_free(old_cache_dir);
2235 g_free(real_oldpath);
2236 g_free(real_newpath);
2237 unlock_session(session);
2241 gint imap_subscribe(Folder *folder, FolderItem *item, gchar *rpath, gboolean sub)
2245 IMAPSession *session;
2246 debug_print("getting session...\n");
2248 session = imap_session_get(folder);
2252 if (item && item->path) {
2253 path = imap_get_real_path(session, IMAP_FOLDER(folder), item->path);
2256 if (!strcmp(path, "INBOX") && sub == FALSE)
2258 debug_print("%ssubscribing %s\n", sub?"":"un", path);
2259 r = imap_threaded_subscribe(folder, path, sub);
2262 r = imap_threaded_subscribe(folder, rpath, sub);
2268 static gint imap_remove_folder_real(Folder *folder, FolderItem *item)
2271 IMAPSession *session;
2275 g_return_val_if_fail(folder != NULL, -1);
2276 g_return_val_if_fail(item != NULL, -1);
2277 g_return_val_if_fail(item->path != NULL, -1);
2279 debug_print("getting session...\n");
2280 session = imap_session_get(folder);
2284 path = imap_get_real_path(session, IMAP_FOLDER(folder), item->path);
2286 imap_threaded_subscribe(folder, path, FALSE);
2287 ok = imap_cmd_delete(session, path);
2288 if (ok != IMAP_SUCCESS) {
2289 gchar *tmp = g_strdup_printf("%s%c", path,
2290 imap_get_path_separator(session, IMAP_FOLDER(folder), path));
2293 ok = imap_cmd_delete(session, path);
2296 if (ok != IMAP_SUCCESS) {
2297 log_warning(LOG_PROTOCOL, _("can't delete mailbox\n"));
2299 unlock_session(session);
2304 cache_dir = folder_item_get_path(item);
2305 if (is_dir_exist(cache_dir) && remove_dir_recursive(cache_dir) < 0)
2306 g_warning("can't remove directory '%s'\n", cache_dir);
2308 folder_item_remove(item);
2309 unlock_session(session);
2313 static gint imap_remove_folder(Folder *folder, FolderItem *item)
2317 g_return_val_if_fail(item != NULL, -1);
2318 g_return_val_if_fail(item->folder != NULL, -1);
2319 g_return_val_if_fail(item->node != NULL, -1);
2321 node = item->node->children;
2322 while (node != NULL) {
2324 if (imap_remove_folder(folder, FOLDER_ITEM(node->data)) < 0)
2328 debug_print("IMAP removing %s\n", item->path);
2330 if (imap_remove_all_msg(folder, item) < 0)
2332 return imap_remove_folder_real(folder, item);
2335 typedef struct _uncached_data {
2336 IMAPSession *session;
2338 MsgNumberList *numlist;
2344 static void *imap_get_uncached_messages_thread(void *data)
2346 uncached_data *stuff = (uncached_data *)data;
2347 IMAPSession *session = stuff->session;
2348 FolderItem *item = stuff->item;
2349 MsgNumberList *numlist = stuff->numlist;
2351 GSList *newlist = NULL;
2352 GSList *llast = NULL;
2353 GSList *seq_list, *cur;
2355 debug_print("uncached_messages\n");
2357 if (session == NULL || item == NULL || item->folder == NULL
2358 || FOLDER_CLASS(item->folder) != &imap_class) {
2363 seq_list = imap_get_lep_set_from_numlist(numlist);
2364 debug_print("get msgs info\n");
2365 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
2366 struct mailimap_set * imapset;
2372 if (session->cancelled)
2375 imapset = cur->data;
2377 r = imap_threaded_fetch_env(session->folder,
2378 imapset, &env_list);
2379 if (r != MAILIMAP_NO_ERROR)
2382 session_set_access_time(SESSION(session));
2385 for(i = 0 ; i < carray_count(env_list) ; i ++) {
2386 struct imap_fetch_env_info * info;
2389 info = carray_get(env_list, i);
2390 msginfo = imap_envelope_from_lep(info, item);
2391 if (msginfo == NULL)
2393 msginfo->folder = item;
2395 llast = newlist = g_slist_append(newlist, msginfo);
2397 llast = g_slist_append(llast, msginfo);
2398 llast = llast->next;
2403 imap_fetch_env_free(env_list);
2406 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
2407 struct mailimap_set * imapset;
2409 imapset = cur->data;
2410 mailimap_set_free(imapset);
2413 session_set_access_time(SESSION(session));
2418 #define MAX_MSG_NUM 50
2420 static GSList *imap_get_uncached_messages(IMAPSession *session,
2422 MsgNumberList *numlist)
2424 GSList *result = NULL;
2426 uncached_data *data = g_new0(uncached_data, 1);
2431 data->total = g_slist_length(numlist);
2432 debug_print("messages list : %i\n", data->total);
2434 while (cur != NULL) {
2435 GSList * partial_result;
2443 while (count < MAX_MSG_NUM) {
2448 if (newlist == NULL)
2449 llast = newlist = g_slist_append(newlist, p);
2451 llast = g_slist_append(llast, p);
2452 llast = llast->next;
2462 data->session = session;
2464 data->numlist = newlist;
2467 if (prefs_common.work_offline &&
2468 !inc_offline_should_override(FALSE,
2469 _("Claws Mail needs network access in order "
2470 "to access the IMAP server."))) {
2476 (GSList *)imap_get_uncached_messages_thread(data);
2478 statusbar_progress_all(data->cur,data->total, 1);
2480 g_slist_free(newlist);
2482 result = g_slist_concat(result, partial_result);
2486 statusbar_progress_all(0,0,0);
2487 statusbar_pop_all();
2492 static void imap_delete_all_cached_messages(FolderItem *item)
2496 g_return_if_fail(item != NULL);
2497 g_return_if_fail(item->folder != NULL);
2498 g_return_if_fail(FOLDER_CLASS(item->folder) == &imap_class);
2500 debug_print("Deleting all cached messages...\n");
2502 dir = folder_item_get_path(item);
2503 if (is_dir_exist(dir))
2504 remove_all_numbered_files(dir);
2507 debug_print("done.\n");
2510 gchar imap_get_path_separator_for_item(FolderItem *item)
2512 Folder *folder = NULL;
2513 IMAPFolder *imap_folder = NULL;
2514 IMAPSession *session = NULL;
2519 folder = item->folder;
2524 imap_folder = IMAP_FOLDER(folder);
2529 debug_print("getting session...");
2530 session = imap_session_get(FOLDER(folder));
2531 result = imap_get_path_separator(session, imap_folder, item->path);
2532 unlock_session(session);
2536 static gchar imap_refresh_path_separator(IMAPSession *session, IMAPFolder *folder, const gchar *subfolder)
2540 gchar separator = '\0';
2542 g_return_val_if_fail(session != NULL, '/');
2543 r = imap_threaded_list((Folder *)folder, "", subfolder, &lep_list);
2545 if (r != MAILIMAP_NO_ERROR) {
2546 log_warning(LOG_PROTOCOL, _("LIST failed\n"));
2550 if (clist_count(lep_list) > 0) {
2551 clistiter * iter = clist_begin(lep_list);
2552 struct mailimap_mailbox_list * mb;
2553 mb = clist_content(iter);
2555 separator = mb->mb_delimiter;
2556 debug_print("got separator: %c\n", folder->last_seen_separator);
2558 mailimap_list_result_free(lep_list);
2562 static gchar imap_get_path_separator(IMAPSession *session, IMAPFolder *folder, const gchar *path)
2564 gchar separator = '/';
2566 if (folder->last_seen_separator == 0) {
2567 folder->last_seen_separator = imap_refresh_path_separator(session, folder, "");
2570 if (folder->last_seen_separator == 0) {
2571 folder->last_seen_separator = imap_refresh_path_separator(session, folder, "INBOX");
2574 if (folder->last_seen_separator != 0) {
2575 debug_print("using separator: %c\n", folder->last_seen_separator);
2576 return folder->last_seen_separator;
2582 static gchar *imap_get_real_path(IMAPSession *session, IMAPFolder *folder, const gchar *path)
2587 g_return_val_if_fail(folder != NULL, NULL);
2588 g_return_val_if_fail(path != NULL, NULL);
2590 real_path = imap_utf8_to_modified_utf7(path);
2591 separator = imap_get_path_separator(session, folder, path);
2592 imap_path_separator_subst(real_path, separator);
2597 static gint imap_set_message_flags(IMAPSession *session,
2598 MsgNumberList *numlist,
2606 seq_list = imap_get_lep_set_from_numlist(numlist);
2608 for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) {
2609 struct mailimap_set * imapset;
2611 imapset = cur->data;
2613 ok = imap_cmd_store(session, imapset,
2617 imap_lep_set_free(seq_list);
2619 return IMAP_SUCCESS;
2622 typedef struct _select_data {
2623 IMAPSession *session;
2628 guint32 *uid_validity;
2632 static gint imap_select(IMAPSession *session, IMAPFolder *folder,
2634 gint *exists, gint *recent, gint *unseen,
2635 guint32 *uid_validity, gboolean block)
2639 gint exists_, recent_, unseen_;
2640 guint32 uid_validity_;
2642 if (!exists && !recent && !unseen && !uid_validity) {
2643 if (session->mbox && strcmp(session->mbox, path) == 0)
2644 return IMAP_SUCCESS;
2653 uid_validity = &uid_validity_;
2655 g_free(session->mbox);
2656 session->mbox = NULL;
2658 real_path = imap_get_real_path(session, folder, path);
2660 ok = imap_cmd_select(session, real_path,
2661 exists, recent, unseen, uid_validity, block);
2662 if (ok != IMAP_SUCCESS)
2663 log_warning(LOG_PROTOCOL, _("can't select folder: %s\n"), real_path);
2665 session->mbox = g_strdup(path);
2666 session->folder_content_changed = FALSE;
2673 static gint imap_status(IMAPSession *session, IMAPFolder *folder,
2674 const gchar *path, IMAPFolderItem *item,
2676 guint32 *uid_next, guint32 *uid_validity,
2677 gint *unseen, gboolean block)
2681 struct mailimap_mailbox_data_status * data_status;
2686 real_path = imap_get_real_path(session, folder, path);
2704 r = imap_threaded_status(FOLDER(folder), real_path,
2705 &data_status, mask);
2708 if (r != MAILIMAP_NO_ERROR) {
2709 debug_print("status err %d\n", r);
2713 if (data_status->st_info_list == NULL) {
2714 mailimap_mailbox_data_status_free(data_status);
2715 debug_print("status->st_info_list == NULL\n");
2720 for(iter = clist_begin(data_status->st_info_list) ; iter != NULL ;
2721 iter = clist_next(iter)) {
2722 struct mailimap_status_info * info;
2724 info = clist_content(iter);
2725 switch (info->st_att) {
2726 case MAILIMAP_STATUS_ATT_MESSAGES:
2728 * messages = info->st_value;
2729 got_values |= 1 << 0;
2733 case MAILIMAP_STATUS_ATT_UIDNEXT:
2735 * uid_next = info->st_value;
2736 got_values |= 1 << 2;
2740 case MAILIMAP_STATUS_ATT_UIDVALIDITY:
2742 * uid_validity = info->st_value;
2743 got_values |= 1 << 3;
2747 case MAILIMAP_STATUS_ATT_UNSEEN:
2749 * unseen = info->st_value;
2750 got_values |= 1 << 4;
2755 mailimap_mailbox_data_status_free(data_status);
2757 if (got_values != mask) {
2758 g_warning("status: incomplete values received (%d)\n", got_values);
2760 return IMAP_SUCCESS;
2763 static void imap_free_capabilities(IMAPSession *session)
2765 slist_free_strings(session->capability);
2766 g_slist_free(session->capability);
2767 session->capability = NULL;
2770 /* low-level IMAP4rev1 commands */
2772 static gint imap_cmd_login(IMAPSession *session,
2773 const gchar *user, const gchar *pass,
2779 if (!strcmp(type, "LOGIN") && imap_has_capability(session, "LOGINDISABLED")) {
2780 gint ok = IMAP_ERROR;
2781 if (imap_has_capability(session, "STARTTLS")) {
2783 log_warning(LOG_PROTOCOL, _("Server requires TLS to log in.\n"));
2784 ok = imap_cmd_starttls(session);
2785 if (ok != IMAP_SUCCESS) {
2786 log_warning(LOG_PROTOCOL, _("Can't start TLS session.\n"));
2790 imap_free_capabilities(session);
2791 if (imap_get_capabilities(session) != MAILIMAP_NO_ERROR) {
2792 log_warning(LOG_PROTOCOL, _("Can't refresh capabilities.\n"));
2797 log_error(LOG_PROTOCOL, _("Connection to %s failed: "
2798 "server requires TLS, but Claws Mail "
2799 "has been compiled without OpenSSL "
2801 SESSION(session)->server);
2805 log_error(LOG_PROTOCOL, _("Server logins are disabled.\n"));
2810 log_print(LOG_PROTOCOL, "IMAP4> Logging %s to %s using %s\n",
2812 SESSION(session)->server,
2814 r = imap_threaded_login(session->folder, user, pass, type);
2815 if (r != MAILIMAP_NO_ERROR) {
2816 log_print(LOG_PROTOCOL, "IMAP4< Error logging in to %s\n",
2817 SESSION(session)->server);
2820 log_print(LOG_PROTOCOL, "IMAP4< Login to %s successful\n",
2821 SESSION(session)->server);
2827 static gint imap_cmd_noop(IMAPSession *session)
2830 unsigned int exists;
2832 r = imap_threaded_noop(session->folder, &exists);
2833 if (r != MAILIMAP_NO_ERROR) {
2834 debug_print("noop err %d\n", r);
2837 session->exists = exists;
2838 session_set_access_time(SESSION(session));
2840 return IMAP_SUCCESS;
2844 static gint imap_cmd_starttls(IMAPSession *session)
2848 r = imap_threaded_starttls(session->folder,
2849 SESSION(session)->server, SESSION(session)->port);
2850 if (r != MAILIMAP_NO_ERROR) {
2851 debug_print("starttls err %d\n", r);
2854 return IMAP_SUCCESS;
2858 static gint imap_cmd_select(IMAPSession *session, const gchar *folder,
2859 gint *exists, gint *recent, gint *unseen,
2860 guint32 *uid_validity, gboolean block)
2864 r = imap_threaded_select(session->folder, folder,
2865 exists, recent, unseen, uid_validity);
2866 if (r != MAILIMAP_NO_ERROR) {
2867 debug_print("select err %d\n", r);
2870 return IMAP_SUCCESS;
2873 static gint imap_cmd_examine(IMAPSession *session, const gchar *folder,
2874 gint *exists, gint *recent, gint *unseen,
2875 guint32 *uid_validity, gboolean block)
2879 r = imap_threaded_examine(session->folder, folder,
2880 exists, recent, unseen, uid_validity);
2881 if (r != MAILIMAP_NO_ERROR) {
2882 debug_print("examine err %d\n", r);
2886 return IMAP_SUCCESS;
2889 static gint imap_cmd_create(IMAPSession *session, const gchar *folder)
2893 r = imap_threaded_create(session->folder, folder);
2894 if (r != MAILIMAP_NO_ERROR) {
2899 return IMAP_SUCCESS;
2902 static gint imap_cmd_rename(IMAPSession *session, const gchar *old_folder,
2903 const gchar *new_folder)
2907 r = imap_threaded_rename(session->folder, old_folder,
2909 if (r != MAILIMAP_NO_ERROR) {
2914 return IMAP_SUCCESS;
2917 static gint imap_cmd_delete(IMAPSession *session, const gchar *folder)
2922 r = imap_threaded_delete(session->folder, folder);
2923 if (r != MAILIMAP_NO_ERROR) {
2928 return IMAP_SUCCESS;
2931 typedef struct _fetch_data {
2932 IMAPSession *session;
2934 const gchar *filename;
2940 static void *imap_cmd_fetch_thread(void *data)
2942 fetch_data *stuff = (fetch_data *)data;
2943 IMAPSession *session = stuff->session;
2944 guint32 uid = stuff->uid;
2945 const gchar *filename = stuff->filename;
2949 r = imap_threaded_fetch_content(session->folder,
2953 r = imap_threaded_fetch_content(session->folder,
2956 if (r != MAILIMAP_NO_ERROR) {
2957 debug_print("fetch err %d\n", r);
2958 return GINT_TO_POINTER(IMAP_ERROR);
2960 return GINT_TO_POINTER(IMAP_SUCCESS);
2963 static gint imap_cmd_fetch(IMAPSession *session, guint32 uid,
2964 const gchar *filename, gboolean headers,
2967 fetch_data *data = g_new0(fetch_data, 1);
2970 data->session = session;
2972 data->filename = filename;
2973 data->headers = headers;
2976 if (prefs_common.work_offline &&
2977 !inc_offline_should_override(FALSE,
2978 _("Claws Mail needs network access in order "
2979 "to access the IMAP server."))) {
2983 statusbar_print_all(_("Fetching message..."));
2984 result = GPOINTER_TO_INT(imap_cmd_fetch_thread(data));
2985 statusbar_pop_all();
2991 static gint imap_cmd_append(IMAPSession *session, const gchar *destfolder,
2992 const gchar *file, IMAPFlags flags,
2995 struct mailimap_flag_list * flag_list;
2998 g_return_val_if_fail(file != NULL, IMAP_ERROR);
3000 flag_list = imap_flag_to_lep(flags);
3001 r = imap_threaded_append(session->folder, destfolder,
3002 file, flag_list, (int *)new_uid);
3003 mailimap_flag_list_free(flag_list);
3005 if (r != MAILIMAP_NO_ERROR) {
3006 debug_print("append err %d\n", r);
3009 return IMAP_SUCCESS;
3012 static gint imap_cmd_copy(IMAPSession *session, struct mailimap_set * set,
3013 const gchar *destfolder, GRelation *uid_mapping,
3014 struct mailimap_set **source, struct mailimap_set **dest)
3018 g_return_val_if_fail(session != NULL, IMAP_ERROR);
3019 g_return_val_if_fail(set != NULL, IMAP_ERROR);
3020 g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
3022 r = imap_threaded_copy(session->folder, set, destfolder, source, dest);
3023 if (r != MAILIMAP_NO_ERROR) {
3028 return IMAP_SUCCESS;
3031 static gint imap_cmd_store(IMAPSession *session, struct mailimap_set * set,
3032 IMAPFlags flags, int do_add)
3035 struct mailimap_flag_list * flag_list;
3036 struct mailimap_store_att_flags * store_att_flags;
3038 flag_list = imap_flag_to_lep(flags);
3042 mailimap_store_att_flags_new_add_flags_silent(flag_list);
3045 mailimap_store_att_flags_new_remove_flags_silent(flag_list);
3047 r = imap_threaded_store(session->folder, set, store_att_flags);
3048 mailimap_store_att_flags_free(store_att_flags);
3049 if (r != MAILIMAP_NO_ERROR) {
3054 return IMAP_SUCCESS;
3057 static gint imap_cmd_expunge(IMAPSession *session)
3061 if (prefs_common.work_offline &&
3062 !inc_offline_should_override(FALSE,
3063 _("Claws Mail needs network access in order "
3064 "to access the IMAP server."))) {
3068 r = imap_threaded_expunge(session->folder);
3069 if (r != MAILIMAP_NO_ERROR) {
3074 return IMAP_SUCCESS;
3077 static void imap_path_separator_subst(gchar *str, gchar separator)
3080 gboolean in_escape = FALSE;
3082 if (!separator || separator == '/') return;
3084 for (p = str; *p != '\0'; p++) {
3085 if (*p == '/' && !in_escape)
3087 else if (*p == '&' && *(p + 1) != '-' && !in_escape)
3089 else if (*p == '-' && in_escape)
3094 static gchar *imap_modified_utf7_to_utf8(const gchar *mutf7_str)
3096 static iconv_t cd = (iconv_t)-1;
3097 static gboolean iconv_ok = TRUE;
3100 size_t norm_utf7_len;
3102 gchar *to_str, *to_p;
3104 gboolean in_escape = FALSE;
3106 if (!iconv_ok) return g_strdup(mutf7_str);
3108 if (cd == (iconv_t)-1) {
3109 cd = iconv_open(CS_INTERNAL, CS_UTF_7);
3110 if (cd == (iconv_t)-1) {
3111 g_warning("iconv cannot convert UTF-7 to %s\n",
3114 return g_strdup(mutf7_str);
3118 /* modified UTF-7 to normal UTF-7 conversion */
3119 norm_utf7 = g_string_new(NULL);
3121 for (p = mutf7_str; *p != '\0'; p++) {
3122 /* replace: '&' -> '+',
3124 escaped ',' -> '/' */
3125 if (!in_escape && *p == '&') {
3126 if (*(p + 1) != '-') {
3127 g_string_append_c(norm_utf7, '+');
3130 g_string_append_c(norm_utf7, '&');
3133 } else if (in_escape && *p == ',') {
3134 g_string_append_c(norm_utf7, '/');
3135 } else if (in_escape && *p == '-') {
3136 g_string_append_c(norm_utf7, '-');
3139 g_string_append_c(norm_utf7, *p);
3143 norm_utf7_p = norm_utf7->str;
3144 norm_utf7_len = norm_utf7->len;
3145 to_len = strlen(mutf7_str) * 5;
3146 to_p = to_str = g_malloc(to_len + 1);
3148 if (iconv(cd, (ICONV_CONST gchar **)&norm_utf7_p, &norm_utf7_len,
3149 &to_p, &to_len) == -1) {
3150 g_warning(_("iconv cannot convert UTF-7 to %s\n"),
3151 conv_get_locale_charset_str());
3152 g_string_free(norm_utf7, TRUE);
3154 return g_strdup(mutf7_str);
3157 /* second iconv() call for flushing */
3158 iconv(cd, NULL, NULL, &to_p, &to_len);
3159 g_string_free(norm_utf7, TRUE);
3165 static gchar *imap_utf8_to_modified_utf7(const gchar *from)
3167 static iconv_t cd = (iconv_t)-1;
3168 static gboolean iconv_ok = TRUE;
3169 gchar *norm_utf7, *norm_utf7_p;
3170 size_t from_len, norm_utf7_len;
3172 gchar *from_tmp, *to, *p;
3173 gboolean in_escape = FALSE;
3175 if (!iconv_ok) return g_strdup(from);
3177 if (cd == (iconv_t)-1) {
3178 cd = iconv_open(CS_UTF_7, CS_INTERNAL);
3179 if (cd == (iconv_t)-1) {
3180 g_warning(_("iconv cannot convert %s to UTF-7\n"),
3183 return g_strdup(from);
3187 /* UTF-8 to normal UTF-7 conversion */
3188 Xstrdup_a(from_tmp, from, return g_strdup(from));
3189 from_len = strlen(from);
3190 norm_utf7_len = from_len * 5;
3191 Xalloca(norm_utf7, norm_utf7_len + 1, return g_strdup(from));
3192 norm_utf7_p = norm_utf7;
3194 #define IS_PRINT(ch) (isprint(ch) && IS_ASCII(ch))
3196 while (from_len > 0) {
3197 if (*from_tmp == '+') {
3198 *norm_utf7_p++ = '+';
3199 *norm_utf7_p++ = '-';
3203 } else if (IS_PRINT(*(guchar *)from_tmp)) {
3204 /* printable ascii char */
3205 *norm_utf7_p = *from_tmp;
3211 size_t conv_len = 0;
3213 /* unprintable char: convert to UTF-7 */
3215 while (!IS_PRINT(*(guchar *)p) && conv_len < from_len) {
3216 conv_len += g_utf8_skip[*(guchar *)p];
3217 p += g_utf8_skip[*(guchar *)p];
3220 from_len -= conv_len;
3221 if (iconv(cd, (ICONV_CONST gchar **)&from_tmp,
3223 &norm_utf7_p, &norm_utf7_len) == -1) {
3224 g_warning(_("iconv cannot convert UTF-8 to UTF-7\n"));
3225 return g_strdup(from);
3228 /* second iconv() call for flushing */
3229 iconv(cd, NULL, NULL, &norm_utf7_p, &norm_utf7_len);
3235 *norm_utf7_p = '\0';
3236 to_str = g_string_new(NULL);
3237 for (p = norm_utf7; p < norm_utf7_p; p++) {
3238 /* replace: '&' -> "&-",
3241 BASE64 '/' -> ',' */
3242 if (!in_escape && *p == '&') {
3243 g_string_append(to_str, "&-");
3244 } else if (!in_escape && *p == '+') {
3245 if (*(p + 1) == '-') {
3246 g_string_append_c(to_str, '+');
3249 g_string_append_c(to_str, '&');
3252 } else if (in_escape && *p == '/') {
3253 g_string_append_c(to_str, ',');
3254 } else if (in_escape && *p == '-') {
3255 g_string_append_c(to_str, '-');
3258 g_string_append_c(to_str, *p);
3264 g_string_append_c(to_str, '-');
3268 g_string_free(to_str, FALSE);
3273 static gboolean imap_rename_folder_func(GNode *node, gpointer data)
3275 FolderItem *item = node->data;
3276 gchar **paths = data;
3277 const gchar *oldpath = paths[0];
3278 const gchar *newpath = paths[1];
3279 gchar *real_oldpath, *real_newpath;
3281 gchar *new_itempath;
3283 IMAPSession *session = imap_session_get(item->folder);
3285 oldpathlen = strlen(oldpath);
3286 if (strncmp(oldpath, item->path, oldpathlen) != 0) {
3287 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
3291 base = item->path + oldpathlen;
3292 while (*base == G_DIR_SEPARATOR) base++;
3294 new_itempath = g_strdup(newpath);
3296 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
3299 real_oldpath = imap_get_real_path(session, IMAP_FOLDER(item->folder), item->path);
3301 item->path = new_itempath;
3303 real_newpath = imap_get_real_path(session, IMAP_FOLDER(item->folder), item->path);
3305 imap_threaded_subscribe(item->folder, real_oldpath, FALSE);
3306 imap_threaded_subscribe(item->folder, real_newpath, TRUE);
3308 g_free(real_oldpath);
3309 g_free(real_newpath);
3313 typedef struct _get_list_uid_data {
3315 IMAPSession *session;
3316 IMAPFolderItem *item;
3317 GSList **msgnum_list;
3319 } get_list_uid_data;
3321 static void *get_list_of_uids_thread(void *data)
3323 get_list_uid_data *stuff = (get_list_uid_data *)data;
3324 Folder *folder = stuff->folder;
3325 IMAPFolderItem *item = stuff->item;
3326 GSList **msgnum_list = stuff->msgnum_list;
3327 gint ok, nummsgs = 0, lastuid_old;
3328 IMAPSession *session;
3329 GSList *uidlist, *elem;
3330 clist * lep_uidlist;
3333 session = stuff->session;
3334 if (session == NULL) {
3336 return GINT_TO_POINTER(-1);
3338 /* no session locking here, it's already locked by caller */
3339 ok = imap_select(session, IMAP_FOLDER(folder), item->item.path,
3340 NULL, NULL, NULL, NULL, TRUE);
3341 if (ok != IMAP_SUCCESS) {
3343 return GINT_TO_POINTER(-1);
3348 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SIMPLE, NULL,
3351 if (r == MAILIMAP_NO_ERROR) {
3352 GSList * fetchuid_list;
3355 imap_uid_list_from_lep(lep_uidlist);
3356 mailimap_search_result_free(lep_uidlist);
3358 uidlist = g_slist_concat(fetchuid_list, uidlist);
3361 GSList * fetchuid_list;
3362 carray * lep_uidtab;
3364 r = imap_threaded_fetch_uid(folder, item->lastuid + 1,
3366 if (r == MAILIMAP_NO_ERROR) {
3368 imap_uid_list_from_lep_tab(lep_uidtab);
3369 imap_fetch_uid_list_free(lep_uidtab);
3370 uidlist = g_slist_concat(fetchuid_list, uidlist);
3374 lastuid_old = item->lastuid;
3375 *msgnum_list = g_slist_copy(item->uid_list);
3376 nummsgs = g_slist_length(*msgnum_list);
3377 debug_print("Got %d uids from cache\n", g_slist_length(item->uid_list));
3379 for (elem = uidlist; elem != NULL; elem = g_slist_next(elem)) {
3382 msgnum = GPOINTER_TO_INT(elem->data);
3383 if (msgnum > lastuid_old) {
3384 *msgnum_list = g_slist_prepend(*msgnum_list, GINT_TO_POINTER(msgnum));
3385 item->uid_list = g_slist_prepend(item->uid_list, GINT_TO_POINTER(msgnum));
3388 if(msgnum > item->lastuid)
3389 item->lastuid = msgnum;
3392 g_slist_free(uidlist);
3394 return GINT_TO_POINTER(nummsgs);
3397 static gint get_list_of_uids(IMAPSession *session, Folder *folder, IMAPFolderItem *item, GSList **msgnum_list)
3400 get_list_uid_data *data = g_new0(get_list_uid_data, 1);
3402 data->folder = folder;
3404 data->msgnum_list = msgnum_list;
3405 data->session = session;
3406 if (prefs_common.work_offline &&
3407 !inc_offline_should_override(FALSE,
3408 _("Claws Mail needs network access in order "
3409 "to access the IMAP server."))) {
3414 result = GPOINTER_TO_INT(get_list_of_uids_thread(data));
3420 gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list, gboolean *old_uids_valid)
3422 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3423 IMAPSession *session;
3424 gint ok, nummsgs = 0, exists;
3425 guint32 uid_next = 0, uid_val = 0;
3426 GSList *uidlist = NULL;
3428 gboolean selected_folder;
3429 debug_print("get_num_list\n");
3431 g_return_val_if_fail(folder != NULL, -1);
3432 g_return_val_if_fail(item != NULL, -1);
3433 g_return_val_if_fail(item->item.path != NULL, -1);
3434 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3435 g_return_val_if_fail(folder->account != NULL, -1);
3437 debug_print("getting session...\n");
3438 session = imap_session_get(folder);
3439 g_return_val_if_fail(session != NULL, -1);
3441 if (FOLDER_ITEM(item)->path)
3442 statusbar_print_all(_("Scanning folder %s%c%s ..."),
3443 FOLDER_ITEM(item)->folder->name,
3445 FOLDER_ITEM(item)->path);
3447 statusbar_print_all(_("Scanning folder %s ..."),
3448 FOLDER_ITEM(item)->folder->name);
3450 selected_folder = (session->mbox != NULL) &&
3451 (!strcmp(session->mbox, item->item.path));
3452 if (selected_folder && time(NULL) - item->use_cache < 2) {
3453 ok = imap_cmd_noop(session);
3454 if (ok != IMAP_SUCCESS) {
3455 debug_print("disconnected!\n");
3456 session = imap_reconnect_if_possible(folder, session);
3457 if (session == NULL) {
3458 statusbar_pop_all();
3459 unlock_session(session);
3463 exists = session->exists;
3465 uid_next = item->c_uid_next;
3466 uid_val = item->c_uid_validity;
3467 *old_uids_valid = TRUE;
3469 if (item->use_cache && time(NULL) - item->use_cache < 2) {
3470 exists = item->c_messages;
3471 uid_next = item->c_uid_next;
3472 uid_val = item->c_uid_validity;
3474 debug_print("using cache %d %d %d\n", exists, uid_next, uid_val);
3476 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path, item,
3477 &exists, &uid_next, &uid_val, NULL, FALSE);
3479 item->item.last_num = uid_next - 1;
3481 item->use_cache = (time_t)0;
3482 if (ok != IMAP_SUCCESS) {
3483 statusbar_pop_all();
3484 unlock_session(session);
3487 if(item->item.mtime == uid_val)
3488 *old_uids_valid = TRUE;
3490 *old_uids_valid = FALSE;
3492 debug_print("Freeing imap uid cache (%d != %d)\n",
3493 (int)item->item.mtime, uid_val);
3495 g_slist_free(item->uid_list);
3496 item->uid_list = NULL;
3498 item->item.mtime = uid_val;
3500 imap_delete_all_cached_messages((FolderItem *)item);
3504 /* If old uid_next matches new uid_next we can be sure no message
3505 was added to the folder */
3506 debug_print("uid_next is %d and item->uid_next %d \n",
3507 uid_next, item->uid_next);
3508 if (uid_next == item->uid_next) {
3509 nummsgs = g_slist_length(item->uid_list);
3511 /* If number of messages is still the same we
3512 know our caches message numbers are still valid,
3513 otherwise if the number of messages has decrease
3514 we discard our cache to start a new scan to find
3515 out which numbers have been removed */
3516 if (exists == nummsgs) {
3517 debug_print("exists == nummsgs\n");
3518 *msgnum_list = g_slist_copy(item->uid_list);
3519 statusbar_pop_all();
3520 unlock_session(session);
3522 } else if (exists < nummsgs) {
3523 debug_print("Freeing imap uid cache");
3525 g_slist_free(item->uid_list);
3526 item->uid_list = NULL;
3531 *msgnum_list = NULL;
3532 statusbar_pop_all();
3533 unlock_session(session);
3537 nummsgs = get_list_of_uids(session, folder, item, &uidlist);
3540 statusbar_pop_all();
3541 unlock_session(session);
3545 if (nummsgs != exists) {
3546 /* Cache contains more messages then folder, we have cached
3547 an old UID of a message that was removed and new messages
3548 have been added too, otherwise the uid_next check would
3550 debug_print("Freeing imap uid cache");
3552 g_slist_free(item->uid_list);
3553 item->uid_list = NULL;
3555 g_slist_free(*msgnum_list);
3557 nummsgs = get_list_of_uids(session, folder, item, &uidlist);
3560 *msgnum_list = uidlist;
3562 dir = folder_item_get_path((FolderItem *)item);
3563 debug_print("removing old messages from %s\n", dir);
3564 remove_numbered_files_not_in_list(dir, *msgnum_list);
3567 item->uid_next = uid_next;
3569 debug_print("get_num_list - ok - %i\n", nummsgs);
3570 statusbar_pop_all();
3571 unlock_session(session);
3575 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item)
3580 flags.perm_flags = MSG_NEW|MSG_UNREAD;
3581 flags.tmp_flags = 0;
3583 g_return_val_if_fail(item != NULL, NULL);
3584 g_return_val_if_fail(file != NULL, NULL);
3586 if (folder_has_parent_of_type(item, F_QUEUE)) {
3587 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
3588 } else if (folder_has_parent_of_type(item, F_DRAFT)) {
3589 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
3592 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3593 if (!msginfo) return NULL;
3595 msginfo->plaintext_file = g_strdup(file);
3596 msginfo->folder = item;
3601 GSList *imap_get_msginfos(Folder *folder, FolderItem *item,
3602 GSList *msgnum_list)
3604 IMAPSession *session;
3605 MsgInfoList *ret = NULL;
3608 debug_print("get_msginfos\n");
3610 g_return_val_if_fail(folder != NULL, NULL);
3611 g_return_val_if_fail(item != NULL, NULL);
3612 g_return_val_if_fail(msgnum_list != NULL, NULL);
3614 debug_print("getting session...\n");
3615 session = imap_session_get(folder);
3616 g_return_val_if_fail(session != NULL, NULL);
3618 debug_print("IMAP getting msginfos\n");
3619 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3620 NULL, NULL, NULL, NULL, FALSE);
3621 if (ok != IMAP_SUCCESS) {
3622 unlock_session(session);
3625 if (!(folder_has_parent_of_type(item, F_DRAFT) ||
3626 folder_has_parent_of_type(item, F_QUEUE))) {
3627 ret = g_slist_concat(ret,
3628 imap_get_uncached_messages(session, item,
3631 MsgNumberList *sorted_list, *elem, *llast = NULL;
3632 gint startnum, lastnum;
3634 sorted_list = g_slist_sort(g_slist_copy(msgnum_list), g_int_compare);
3636 startnum = lastnum = GPOINTER_TO_INT(sorted_list->data);
3638 llast = g_slist_last(ret);
3639 for (elem = sorted_list;; elem = g_slist_next(elem)) {
3643 num = GPOINTER_TO_INT(elem->data);
3645 if (num > lastnum + 1 || elem == NULL) {
3647 for (i = startnum; i <= lastnum; ++i) {
3650 file = imap_fetch_msg(folder, item, i);
3652 MsgInfo *msginfo = imap_parse_msg(file, item);
3653 if (msginfo != NULL) {
3654 msginfo->msgnum = i;
3656 llast = ret = g_slist_append(ret, msginfo);
3658 llast = g_slist_append(llast, msginfo);
3659 llast = llast->next;
3664 session_set_access_time(SESSION(session));
3675 g_slist_free(sorted_list);
3677 unlock_session(session);
3681 MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid)
3683 MsgInfo *msginfo = NULL;
3684 MsgInfoList *msginfolist;
3685 MsgNumberList numlist;
3687 numlist.next = NULL;
3688 numlist.data = GINT_TO_POINTER(uid);
3690 msginfolist = imap_get_msginfos(folder, item, &numlist);
3691 if (msginfolist != NULL) {
3692 msginfo = msginfolist->data;
3693 g_slist_free(msginfolist);
3699 gboolean imap_scan_required(Folder *folder, FolderItem *_item)
3701 IMAPSession *session;
3702 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3703 gint ok, exists = 0, unseen = 0;
3704 guint32 uid_next = 0, uid_val = 0;
3705 gboolean selected_folder;
3707 g_return_val_if_fail(folder != NULL, FALSE);
3708 g_return_val_if_fail(item != NULL, FALSE);
3709 g_return_val_if_fail(item->item.folder != NULL, FALSE);
3710 g_return_val_if_fail(FOLDER_CLASS(item->item.folder) == &imap_class, FALSE);
3712 if (item->item.path == NULL)
3715 debug_print("getting session...\n");
3716 session = imap_session_get(folder);
3717 g_return_val_if_fail(session != NULL, FALSE);
3719 selected_folder = (session->mbox != NULL) &&
3720 (!strcmp(session->mbox, item->item.path));
3721 if (selected_folder && time(NULL) - item->use_cache < 2) {
3722 ok = imap_cmd_noop(session);
3723 if (ok != IMAP_SUCCESS) {
3724 debug_print("disconnected!\n");
3725 session = imap_reconnect_if_possible(folder, session);
3726 if (session == NULL)
3730 if (session->folder_content_changed
3731 || session->exists != item->item.total_msgs) {
3732 unlock_session(session);
3736 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path, IMAP_FOLDER_ITEM(item),
3737 &exists, &uid_next, &uid_val, &unseen, FALSE);
3738 if (ok != IMAP_SUCCESS) {
3739 unlock_session(session);
3743 item->use_cache = time(NULL);
3744 item->c_messages = exists;
3745 item->c_uid_next = uid_next;
3746 item->c_uid_validity = uid_val;
3747 item->c_unseen = unseen;
3748 item->item.last_num = uid_next - 1;
3749 debug_print("uidnext %d, item->uid_next %d, exists %d, item->item.total_msgs %d\n",
3750 uid_next, item->uid_next, exists, item->item.total_msgs);
3751 if ((uid_next != item->uid_next) || (exists != item->item.total_msgs)
3752 || unseen != item->item.unread_msgs || uid_val != item->item.mtime) {
3753 unlock_session(session);
3757 unlock_session(session);
3761 void imap_change_flags(Folder *folder, FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
3763 IMAPSession *session;
3764 IMAPFlags flags_set = 0, flags_unset = 0;
3765 gint ok = IMAP_SUCCESS;
3766 MsgNumberList numlist;
3767 hashtable_data *ht_data = NULL;
3769 g_return_if_fail(folder != NULL);
3770 g_return_if_fail(folder->klass == &imap_class);
3771 g_return_if_fail(item != NULL);
3772 g_return_if_fail(item->folder == folder);
3773 g_return_if_fail(msginfo != NULL);
3774 g_return_if_fail(msginfo->folder == item);
3776 if (!MSG_IS_MARKED(msginfo->flags) && (newflags & MSG_MARKED))
3777 flags_set |= IMAP_FLAG_FLAGGED;
3778 if ( MSG_IS_MARKED(msginfo->flags) && !(newflags & MSG_MARKED))
3779 flags_unset |= IMAP_FLAG_FLAGGED;
3781 if (!MSG_IS_UNREAD(msginfo->flags) && (newflags & MSG_UNREAD))
3782 flags_unset |= IMAP_FLAG_SEEN;
3783 if ( MSG_IS_UNREAD(msginfo->flags) && !(newflags & MSG_UNREAD))
3784 flags_set |= IMAP_FLAG_SEEN;
3786 if (!MSG_IS_REPLIED(msginfo->flags) && (newflags & MSG_REPLIED))
3787 flags_set |= IMAP_FLAG_ANSWERED;
3788 if ( MSG_IS_REPLIED(msginfo->flags) && !(newflags & MSG_REPLIED))
3789 flags_unset |= IMAP_FLAG_ANSWERED;
3791 if (!MSG_IS_DELETED(msginfo->flags) && (newflags & MSG_DELETED))
3792 flags_set |= IMAP_FLAG_DELETED;
3793 if ( MSG_IS_DELETED(msginfo->flags) && !(newflags & MSG_DELETED))
3794 flags_unset |= IMAP_FLAG_DELETED;
3796 if (!flags_set && !flags_unset) {
3797 /* the changed flags were not translatable to IMAP-speak.
3798 * like MSG_POSTFILTERED, so just apply. */
3799 msginfo->flags.perm_flags = newflags;
3803 debug_print("getting session...\n");
3804 session = imap_session_get(folder);
3809 if ((ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
3810 NULL, NULL, NULL, NULL, FALSE)) != IMAP_SUCCESS) {
3811 unlock_session(session);
3814 numlist.next = NULL;
3815 numlist.data = GINT_TO_POINTER(msginfo->msgnum);
3817 if (IMAP_FOLDER_ITEM(item)->batching) {
3818 /* instead of performing an UID STORE command for each message change,
3819 * as a lot of them can change "together", we just fill in hashtables
3820 * and defer the treatment so that we're able to send only one
3823 debug_print("IMAP batch mode on, deferring flags change\n");
3825 ht_data = g_hash_table_lookup(IMAP_FOLDER_ITEM(item)->flags_set_table,
3826 GINT_TO_POINTER(flags_set));
3827 if (ht_data == NULL) {
3828 ht_data = g_new0(hashtable_data, 1);
3829 ht_data->session = session;
3830 ht_data->item = IMAP_FOLDER_ITEM(item);
3831 g_hash_table_insert(IMAP_FOLDER_ITEM(item)->flags_set_table,
3832 GINT_TO_POINTER(flags_set), ht_data);
3834 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
3835 ht_data->msglist = g_slist_prepend(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum));
3838 ht_data = g_hash_table_lookup(IMAP_FOLDER_ITEM(item)->flags_unset_table,
3839 GINT_TO_POINTER(flags_unset));
3840 if (ht_data == NULL) {
3841 ht_data = g_new0(hashtable_data, 1);
3842 ht_data->session = session;
3843 ht_data->item = IMAP_FOLDER_ITEM(item);
3844 g_hash_table_insert(IMAP_FOLDER_ITEM(item)->flags_unset_table,
3845 GINT_TO_POINTER(flags_unset), ht_data);
3847 if (!g_slist_find(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)))
3848 ht_data->msglist = g_slist_prepend(ht_data->msglist,
3849 GINT_TO_POINTER(msginfo->msgnum));
3852 debug_print("IMAP changing flags\n");
3854 ok = imap_set_message_flags(session, &numlist, flags_set, TRUE);
3855 if (ok != IMAP_SUCCESS) {
3856 unlock_session(session);
3862 ok = imap_set_message_flags(session, &numlist, flags_unset, FALSE);
3863 if (ok != IMAP_SUCCESS) {
3864 unlock_session(session);
3869 msginfo->flags.perm_flags = newflags;
3870 unlock_session(session);
3874 static gint imap_remove_msg(Folder *folder, FolderItem *item, gint uid)
3877 IMAPSession *session;
3879 MsgNumberList numlist;
3881 g_return_val_if_fail(folder != NULL, -1);
3882 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3883 g_return_val_if_fail(item != NULL, -1);
3885 debug_print("getting session...\n");
3886 session = imap_session_get(folder);
3887 if (!session) return -1;
3889 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3890 NULL, NULL, NULL, NULL, FALSE);
3891 if (ok != IMAP_SUCCESS) {
3892 unlock_session(session);
3895 numlist.next = NULL;
3896 numlist.data = GINT_TO_POINTER(uid);
3898 ok = imap_set_message_flags
3899 (session, &numlist, IMAP_FLAG_DELETED, TRUE);
3900 if (ok != IMAP_SUCCESS) {
3901 log_warning(LOG_PROTOCOL, _("can't set deleted flags: %d\n"), uid);
3902 unlock_session(session);
3906 if (!session->uidplus) {
3907 ok = imap_cmd_expunge(session);
3911 uidstr = g_strdup_printf("%u", uid);
3912 ok = imap_cmd_expunge(session);
3915 if (ok != IMAP_SUCCESS) {
3916 log_warning(LOG_PROTOCOL, _("can't expunge\n"));
3917 unlock_session(session);
3921 IMAP_FOLDER_ITEM(item)->uid_list = g_slist_remove(
3922 IMAP_FOLDER_ITEM(item)->uid_list, numlist.data);
3923 dir = folder_item_get_path(item);
3924 if (is_dir_exist(dir))
3925 remove_numbered_files(dir, uid, uid);
3927 unlock_session(session);
3928 return IMAP_SUCCESS;
3931 static gint compare_msginfo(gconstpointer a, gconstpointer b)
3933 return ((MsgInfo *)a)->msgnum - ((MsgInfo *)b)->msgnum;
3936 static guint gslist_find_next_num(MsgNumberList **list, guint num)
3940 g_return_val_if_fail(list != NULL, -1);
3942 for (elem = *list; elem != NULL; elem = g_slist_next(elem))
3943 if (GPOINTER_TO_INT(elem->data) >= num)
3946 return elem != NULL ? GPOINTER_TO_INT(elem->data) : (gint)-1;
3950 * NEW and DELETED flags are not syncronized
3951 * - The NEW/RECENT flags in IMAP folders can not really be directly
3952 * modified by Sylpheed
3953 * - The DELETE/DELETED flag in IMAP and Sylpheed don't have the same
3954 * meaning, in IMAP it always removes the messages from the FolderItem
3955 * in Sylpheed it can mean to move the message to trash
3958 typedef struct _get_flags_data {
3961 MsgInfoList *msginfo_list;
3962 GRelation *msgflags;
3963 gboolean full_search;
3967 static /*gint*/ void *imap_get_flags_thread(void *data)
3969 get_flags_data *stuff = (get_flags_data *)data;
3970 Folder *folder = stuff->folder;
3971 FolderItem *item = stuff->item;
3972 MsgInfoList *msginfo_list = stuff->msginfo_list;
3973 GRelation *msgflags = stuff->msgflags;
3974 gboolean full_search = stuff->full_search;
3975 IMAPSession *session;
3976 GSList *sorted_list = NULL;
3977 GSList *unseen = NULL, *answered = NULL, *flagged = NULL, *deleted = NULL;
3978 GSList *p_unseen, *p_answered, *p_flagged, *p_deleted;
3980 GSList *seq_list, *cur;
3981 gboolean reverse_seen = FALSE;
3984 gint exists_cnt, unseen_cnt;
3985 gboolean selected_folder;
3987 if (folder == NULL || item == NULL) {
3989 return GINT_TO_POINTER(-1);
3992 debug_print("getting session...\n");
3993 session = imap_session_get(folder);
3994 if (session == NULL) {
3996 return GINT_TO_POINTER(-1);
3999 selected_folder = (session->mbox != NULL) &&
4000 (!strcmp(session->mbox, item->path));
4002 if (!selected_folder) {
4003 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
4004 &exists_cnt, NULL, &unseen_cnt, NULL, TRUE);
4005 if (ok != IMAP_SUCCESS) {
4007 unlock_session(session);
4008 return GINT_TO_POINTER(-1);
4011 if (unseen_cnt > exists_cnt / 2)
4012 reverse_seen = TRUE;
4015 if (item->unread_msgs > item->total_msgs / 2)
4016 reverse_seen = TRUE;
4019 cmd_buf = g_string_new(NULL);
4021 sorted_list = g_slist_sort(g_slist_copy(msginfo_list), compare_msginfo);
4023 seq_list = imap_get_lep_set_from_msglist(msginfo_list);
4025 struct mailimap_set * set;
4026 set = mailimap_set_new_interval(1, 0);
4027 seq_list = g_slist_append(NULL, set);
4030 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
4031 struct mailimap_set * imapset;
4032 clist * lep_uidlist;
4035 imapset = cur->data;
4037 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SEEN,
4038 full_search ? NULL:imapset, &lep_uidlist);
4041 r = imap_threaded_search(folder,
4042 IMAP_SEARCH_TYPE_UNSEEN,
4043 full_search ? NULL:imapset, &lep_uidlist);
4045 if (r == MAILIMAP_NO_ERROR) {
4048 uidlist = imap_uid_list_from_lep(lep_uidlist);
4049 mailimap_search_result_free(lep_uidlist);
4051 unseen = g_slist_concat(unseen, uidlist);
4054 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_FLAGGED,
4055 full_search ? NULL:imapset, &lep_uidlist);
4056 if (r == MAILIMAP_NO_ERROR) {
4059 uidlist = imap_uid_list_from_lep(lep_uidlist);
4060 mailimap_search_result_free(lep_uidlist);
4062 flagged = g_slist_concat(flagged, uidlist);
4065 if (item->opened || item->processing_pending || item == folder->inbox) {
4066 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_ANSWERED,
4067 full_search ? NULL:imapset, &lep_uidlist);
4068 if (r == MAILIMAP_NO_ERROR) {
4071 uidlist = imap_uid_list_from_lep(lep_uidlist);
4072 mailimap_search_result_free(lep_uidlist);
4074 answered = g_slist_concat(answered, uidlist);
4077 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_DELETED,
4078 full_search ? NULL:imapset, &lep_uidlist);
4079 if (r == MAILIMAP_NO_ERROR) {
4082 uidlist = imap_uid_list_from_lep(lep_uidlist);
4083 mailimap_search_result_free(lep_uidlist);
4085 deleted = g_slist_concat(deleted, uidlist);
4091 p_answered = answered;
4092 p_flagged = flagged;
4093 p_deleted = deleted;
4095 for (elem = sorted_list; elem != NULL; elem = g_slist_next(elem)) {
4100 msginfo = (MsgInfo *) elem->data;
4101 flags = msginfo->flags.perm_flags;
4102 wasnew = (flags & MSG_NEW);
4103 if (item->opened || item->processing_pending || item == folder->inbox) {
4104 flags &= ~((reverse_seen ? 0 : MSG_UNREAD | MSG_NEW) | MSG_REPLIED | MSG_MARKED);
4106 flags &= ~((reverse_seen ? 0 : MSG_UNREAD | MSG_NEW | MSG_MARKED));
4109 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
4110 if (gslist_find_next_num(&p_unseen, msginfo->msgnum) == msginfo->msgnum) {
4111 if (!reverse_seen) {
4112 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
4114 flags &= ~(MSG_UNREAD | MSG_NEW);
4118 if (gslist_find_next_num(&p_flagged, msginfo->msgnum) == msginfo->msgnum)
4119 flags |= MSG_MARKED;
4121 flags &= ~MSG_MARKED;
4123 if (item->opened || item->processing_pending || item == folder->inbox) {
4124 if (gslist_find_next_num(&p_answered, msginfo->msgnum) == msginfo->msgnum)
4125 flags |= MSG_REPLIED;
4127 flags &= ~MSG_REPLIED;
4128 if (gslist_find_next_num(&p_deleted, msginfo->msgnum) == msginfo->msgnum)
4129 flags |= MSG_DELETED;
4131 flags &= ~MSG_DELETED;
4133 g_relation_insert(msgflags, msginfo, GINT_TO_POINTER(flags));
4136 imap_lep_set_free(seq_list);
4137 g_slist_free(flagged);
4138 g_slist_free(deleted);
4139 g_slist_free(answered);
4140 g_slist_free(unseen);
4141 g_slist_free(sorted_list);
4142 g_string_free(cmd_buf, TRUE);
4145 unlock_session(session);
4146 return GINT_TO_POINTER(0);
4149 static gint imap_get_flags(Folder *folder, FolderItem *item,
4150 MsgInfoList *msginfo_list, GRelation *msgflags)
4153 get_flags_data *data = g_new0(get_flags_data, 1);
4155 data->folder = folder;
4157 data->msginfo_list = msginfo_list;
4158 data->msgflags = msgflags;
4159 data->full_search = FALSE;
4161 GSList *tmp = NULL, *cur;
4163 if (prefs_common.work_offline &&
4164 !inc_offline_should_override(FALSE,
4165 _("Claws Mail needs network access in order "
4166 "to access the IMAP server."))) {
4171 tmp = folder_item_get_msg_list(item);
4173 if (g_slist_length(tmp) <= g_slist_length(msginfo_list))
4174 data->full_search = TRUE;
4176 for (cur = tmp; cur; cur = cur->next)
4177 procmsg_msginfo_free((MsgInfo *)cur->data);
4181 result = GPOINTER_TO_INT(imap_get_flags_thread(data));
4188 static gboolean process_flags(gpointer key, gpointer value, gpointer user_data)
4190 gboolean flags_set = GPOINTER_TO_INT(user_data);
4191 gint flags_value = GPOINTER_TO_INT(key);
4192 hashtable_data *data = (hashtable_data *)value;
4193 IMAPFolderItem *_item = data->item;
4194 FolderItem *item = (FolderItem *)_item;
4195 gint ok = IMAP_ERROR;
4196 IMAPSession *session = NULL;
4198 debug_print("getting session...\n");
4199 session = imap_session_get(item->folder);
4201 data->msglist = g_slist_reverse(data->msglist);
4203 debug_print("IMAP %ssetting flags to %d for %d messages\n",
4206 g_slist_length(data->msglist));
4209 ok = imap_select(session, IMAP_FOLDER(item->folder), item->path,
4210 NULL, NULL, NULL, NULL, FALSE);
4212 if (ok == IMAP_SUCCESS) {
4213 imap_set_message_flags(data->session, data->msglist, flags_value, flags_set);
4215 g_warning("can't select mailbox %s\n", item->path);
4218 unlock_session(session);
4219 g_slist_free(data->msglist);
4224 static void process_hashtable(IMAPFolderItem *item)
4226 if (item->flags_set_table) {
4227 g_hash_table_foreach_remove(item->flags_set_table, process_flags, GINT_TO_POINTER(TRUE));
4228 g_hash_table_destroy(item->flags_set_table);
4229 item->flags_set_table = NULL;
4231 if (item->flags_unset_table) {
4232 g_hash_table_foreach_remove(item->flags_unset_table, process_flags, GINT_TO_POINTER(FALSE));
4233 g_hash_table_destroy(item->flags_unset_table);
4234 item->flags_unset_table = NULL;
4238 static void imap_set_batch (Folder *folder, FolderItem *_item, gboolean batch)
4240 IMAPFolderItem *item = (IMAPFolderItem *)_item;
4242 g_return_if_fail(item != NULL);
4244 if (item->batching == batch)
4248 item->batching = TRUE;
4249 debug_print("IMAP switching to batch mode\n");
4250 if (!item->flags_set_table) {
4251 item->flags_set_table = g_hash_table_new(NULL, g_direct_equal);
4253 if (!item->flags_unset_table) {
4254 item->flags_unset_table = g_hash_table_new(NULL, g_direct_equal);
4257 debug_print("IMAP switching away from batch mode\n");
4259 process_hashtable(item);
4260 item->batching = FALSE;
4266 /* data types conversion libetpan <-> claws */
4270 #define ETPAN_IMAP_MB_MARKED 1
4271 #define ETPAN_IMAP_MB_UNMARKED 2
4272 #define ETPAN_IMAP_MB_NOSELECT 4
4273 #define ETPAN_IMAP_MB_NOINFERIORS 8
4275 static int imap_flags_to_flags(struct mailimap_mbx_list_flags * imap_flags)
4281 if (imap_flags->mbf_type == MAILIMAP_MBX_LIST_FLAGS_SFLAG) {
4282 switch (imap_flags->mbf_sflag) {
4283 case MAILIMAP_MBX_LIST_SFLAG_MARKED:
4284 flags |= ETPAN_IMAP_MB_MARKED;
4286 case MAILIMAP_MBX_LIST_SFLAG_NOSELECT:
4287 flags |= ETPAN_IMAP_MB_NOSELECT;
4289 case MAILIMAP_MBX_LIST_SFLAG_UNMARKED:
4290 flags |= ETPAN_IMAP_MB_UNMARKED;
4295 for(cur = clist_begin(imap_flags->mbf_oflags) ; cur != NULL ;
4296 cur = clist_next(cur)) {
4297 struct mailimap_mbx_list_oflag * oflag;
4299 oflag = clist_content(cur);
4301 switch (oflag->of_type) {
4302 case MAILIMAP_MBX_LIST_OFLAG_NOINFERIORS:
4303 flags |= ETPAN_IMAP_MB_NOINFERIORS;
4311 static GSList * imap_list_from_lep(IMAPFolder * folder,
4312 clist * list, const gchar * real_path, gboolean all)
4315 GSList * item_list = NULL, *llast = NULL;
4317 for(iter = clist_begin(list) ; iter != NULL ;
4318 iter = clist_next(iter)) {
4319 struct mailimap_mailbox_list * mb;
4327 FolderItem *new_item;
4329 mb = clist_content(iter);
4335 if (mb->mb_flag != NULL)
4336 flags = imap_flags_to_flags(mb->mb_flag);
4338 delimiter = mb->mb_delimiter;
4341 dup_name = strdup(name);
4342 if (delimiter != '\0')
4343 subst_char(dup_name, delimiter, '/');
4345 base = g_path_get_basename(dup_name);
4346 if (base[0] == '.') {
4351 if (!all && path_cmp(name, real_path) == 0) {
4357 if (!all && dup_name[strlen(dup_name)-1] == '/') {
4358 dup_name[strlen(dup_name)-1] = '\0';
4361 loc_name = imap_modified_utf7_to_utf8(base);
4362 loc_path = imap_modified_utf7_to_utf8(dup_name);
4364 new_item = folder_item_new(FOLDER(folder), loc_name, loc_path);
4365 if ((flags & ETPAN_IMAP_MB_NOINFERIORS) != 0)
4366 new_item->no_sub = TRUE;
4367 if (strcmp(dup_name, "INBOX") != 0 &&
4368 ((flags & ETPAN_IMAP_MB_NOSELECT) != 0))
4369 new_item->no_select = TRUE;
4371 if (item_list == NULL)
4372 llast = item_list = g_slist_append(item_list, new_item);
4374 llast = g_slist_append(llast, new_item);
4375 llast = llast->next;
4377 debug_print("folder '%s' found.\n", loc_path);
4388 static GSList * imap_get_lep_set_from_numlist(MsgNumberList *numlist)
4390 GSList *sorted_list, *cur;
4391 guint first, last, next;
4392 GSList *ret_list = NULL, *llast = NULL;
4394 struct mailimap_set * current_set;
4395 unsigned int item_count;
4397 if (numlist == NULL)
4401 current_set = mailimap_set_new_empty();
4403 sorted_list = g_slist_copy(numlist);
4404 sorted_list = g_slist_sort(sorted_list, g_int_compare);
4406 first = GPOINTER_TO_INT(sorted_list->data);
4409 for (cur = sorted_list; cur != NULL; cur = g_slist_next(cur)) {
4410 if (GPOINTER_TO_INT(cur->data) == 0)
4415 last = GPOINTER_TO_INT(cur->data);
4417 next = GPOINTER_TO_INT(cur->next->data);
4421 if (last + 1 != next || next == 0) {
4423 struct mailimap_set_item * item;
4424 item = mailimap_set_item_new(first, last);
4425 mailimap_set_add(current_set, item);
4430 if (count >= IMAP_SET_MAX_COUNT) {
4431 if (ret_list == NULL)
4432 llast = ret_list = g_slist_append(ret_list,
4435 llast = g_slist_append(llast, current_set);
4436 llast = llast->next;
4438 current_set = mailimap_set_new_empty();
4445 if (clist_count(current_set->set_list) > 0) {
4446 ret_list = g_slist_append(ret_list,
4450 g_slist_free(sorted_list);
4455 static GSList * imap_get_lep_set_from_msglist(MsgInfoList *msglist)
4457 MsgNumberList *numlist = NULL;
4461 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
4462 MsgInfo *msginfo = (MsgInfo *) cur->data;
4464 numlist = g_slist_prepend(numlist, GINT_TO_POINTER(msginfo->msgnum));
4466 numlist = g_slist_reverse(numlist);
4467 seq_list = imap_get_lep_set_from_numlist(numlist);
4468 g_slist_free(numlist);
4473 static GSList * imap_uid_list_from_lep(clist * list)
4480 for(iter = clist_begin(list) ; iter != NULL ;
4481 iter = clist_next(iter)) {
4484 puid = clist_content(iter);
4485 result = g_slist_prepend(result, GINT_TO_POINTER(* puid));
4488 result = g_slist_reverse(result);
4492 static GSList * imap_uid_list_from_lep_tab(carray * list)
4499 for(i = 0 ; i < carray_count(list) ; i ++) {
4502 puid = carray_get(list, i);
4503 result = g_slist_prepend(result, GINT_TO_POINTER(* puid));
4505 result = g_slist_reverse(result);
4509 static MsgInfo *imap_envelope_from_lep(struct imap_fetch_env_info * info,
4512 MsgInfo *msginfo = NULL;
4515 MsgFlags flags = {0, 0};
4517 if (info->headers == NULL)
4520 MSG_SET_TMP_FLAGS(flags, MSG_IMAP);
4521 if (folder_has_parent_of_type(item, F_QUEUE)) {
4522 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
4523 } else if (folder_has_parent_of_type(item, F_DRAFT)) {
4524 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
4526 flags.perm_flags = info->flags;
4530 msginfo = procheader_parse_str(info->headers, flags, FALSE, FALSE);
4533 msginfo->msgnum = uid;
4534 msginfo->size = size;
4540 static void imap_lep_set_free(GSList *seq_list)
4544 for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) {
4545 struct mailimap_set * imapset;
4547 imapset = cur->data;
4548 mailimap_set_free(imapset);
4550 g_slist_free(seq_list);
4553 static struct mailimap_flag_list * imap_flag_to_lep(IMAPFlags flags)
4555 struct mailimap_flag_list * flag_list;
4557 flag_list = mailimap_flag_list_new_empty();
4559 if (IMAP_IS_SEEN(flags))
4560 mailimap_flag_list_add(flag_list,
4561 mailimap_flag_new_seen());
4562 if (IMAP_IS_ANSWERED(flags))
4563 mailimap_flag_list_add(flag_list,
4564 mailimap_flag_new_answered());
4565 if (IMAP_IS_FLAGGED(flags))
4566 mailimap_flag_list_add(flag_list,
4567 mailimap_flag_new_flagged());
4568 if (IMAP_IS_DELETED(flags))
4569 mailimap_flag_list_add(flag_list,
4570 mailimap_flag_new_deleted());
4571 if (IMAP_IS_DRAFT(flags))
4572 mailimap_flag_list_add(flag_list,
4573 mailimap_flag_new_draft());
4578 guint imap_folder_get_refcnt(Folder *folder)
4580 return ((IMAPFolder *)folder)->refcnt;
4583 void imap_folder_ref(Folder *folder)
4585 ((IMAPFolder *)folder)->refcnt++;
4588 void imap_disconnect_all(void)
4591 for (list = account_get_list(); list != NULL; list = list->next) {
4592 PrefsAccount *account = list->data;
4593 if (account->protocol == A_IMAP4) {
4594 RemoteFolder *folder = (RemoteFolder *)account->folder;
4595 if (folder && folder->session) {
4596 IMAPSession *session = (IMAPSession *)folder->session;
4597 imap_threaded_disconnect(FOLDER(folder));
4598 SESSION(session)->state = SESSION_DISCONNECTED;
4599 session_destroy(SESSION(session));
4600 folder->session = NULL;
4606 void imap_folder_unref(Folder *folder)
4608 if (((IMAPFolder *)folder)->refcnt > 0)
4609 ((IMAPFolder *)folder)->refcnt--;
4612 void imap_cancel_all(void)
4617 folderlist = folder_get_list();
4618 for (cur = folderlist; cur != NULL; cur = g_list_next(cur)) {
4619 Folder *folder = (Folder *) cur->data;
4621 if (folder->klass == &imap_class) {
4622 if (imap_is_busy(folder)) {
4623 IMAPSession *imap_session;
4624 RemoteFolder *rfolder;
4626 fprintf(stderr, "cancelled\n");
4627 imap_threaded_cancel(folder);
4628 rfolder = (RemoteFolder *) folder;
4629 imap_session = (IMAPSession *) rfolder->session;
4630 imap_session->cancelled = 1;
4636 gboolean imap_cancel_all_enabled(void)
4641 folderlist = folder_get_list();
4642 for (cur = folderlist; cur != NULL; cur = g_list_next(cur)) {
4643 Folder *folder = (Folder *) cur->data;
4645 if (folder->klass == &imap_class) {
4646 if (imap_is_busy(folder)) {
4655 gboolean imap_is_busy(Folder *folder)
4657 IMAPSession *imap_session;
4658 RemoteFolder *rfolder;
4660 rfolder = (RemoteFolder *) folder;
4661 imap_session = (IMAPSession *) rfolder->session;
4662 if (imap_session == NULL)
4665 return imap_session->busy;
4668 #else /* HAVE_LIBETPAN */
4670 static FolderClass imap_class;
4672 static XMLTag *imap_item_get_xml(Folder *folder, FolderItem *item);
4673 static void imap_item_set_xml(Folder *folder, FolderItem *item, XMLTag *tag);
4675 static Folder *imap_folder_new (const gchar *name,
4678 static gboolean missing_imap_warning = TRUE;
4679 if (missing_imap_warning) {
4680 missing_imap_warning = FALSE;
4682 _("You have one or more IMAP accounts "
4683 "defined. However this version of "
4684 "Claws Mail has been built without "
4685 "IMAP support; your IMAP account(s) are "
4687 "You probably need to "
4688 "install libetpan and recompile "
4693 static gint imap_create_tree (Folder *folder)
4697 static FolderItem *imap_create_folder (Folder *folder,
4703 static gint imap_rename_folder (Folder *folder,
4710 gchar imap_get_path_separator_for_item(FolderItem *item)
4715 FolderClass *imap_get_class(void)
4717 if (imap_class.idstr == NULL) {
4718 imap_class.type = F_IMAP;
4719 imap_class.idstr = "imap";
4720 imap_class.uistr = "IMAP4";
4722 imap_class.new_folder = imap_folder_new;
4723 imap_class.create_tree = imap_create_tree;
4724 imap_class.create_folder = imap_create_folder;
4725 imap_class.rename_folder = imap_rename_folder;
4727 imap_class.set_xml = folder_set_xml;
4728 imap_class.get_xml = folder_get_xml;
4729 imap_class.item_set_xml = imap_item_set_xml;
4730 imap_class.item_get_xml = imap_item_get_xml;
4731 /* nothing implemented */
4737 void imap_disconnect_all(void)
4741 gint imap_scan_tree_real(Folder *folder, gboolean subs_only)
4746 gint imap_subscribe(Folder *folder, FolderItem *item, gchar *rpath, gboolean sub)
4751 GList * imap_scan_subtree(Folder *folder, FolderItem *item, gboolean unsubs_only, gboolean recursive)
4756 void imap_cache_msg(FolderItem *item, gint msgnum)
4760 void imap_cancel_all(void)
4764 gboolean imap_cancel_all_enabled(void)
4769 gboolean imap_is_busy(Folder *folder)
4776 void imap_synchronise(FolderItem *item)
4778 imap_gtk_synchronise(item);
4781 static void imap_item_set_xml(Folder *folder, FolderItem *item, XMLTag *tag)
4783 #ifdef HAVE_LIBETPAN
4786 folder_item_set_xml(folder, item, tag);
4788 #ifdef HAVE_LIBETPAN
4789 for (cur = tag->attr; cur != NULL; cur = g_list_next(cur)) {
4790 XMLAttr *attr = (XMLAttr *) cur->data;
4792 if (!attr || !attr->name || !attr->value) continue;
4793 if (!strcmp(attr->name, "uidnext"))
4794 IMAP_FOLDER_ITEM(item)->uid_next = atoi(attr->value);
4799 static XMLTag *imap_item_get_xml(Folder *folder, FolderItem *item)
4803 tag = folder_item_get_xml(folder, item);
4805 #ifdef HAVE_LIBETPAN
4806 xml_tag_add_attr(tag, xml_attr_new_int("uidnext",
4807 IMAP_FOLDER_ITEM(item)->uid_next));