2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2003 Hiroyuki Yamamoto
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
49 #include "procheader.h"
50 #include "prefs_account.h"
53 #include "inputdialog.h"
56 typedef struct _IMAPFolder IMAPFolder;
57 typedef struct _IMAPSession IMAPSession;
58 typedef struct _IMAPNameSpace IMAPNameSpace;
59 typedef struct _IMAPFolderItem IMAPFolderItem;
61 #include "prefs_account.h"
63 #define IMAP_FOLDER(obj) ((IMAPFolder *)obj)
64 #define IMAP_SESSION(obj) ((IMAPSession *)obj)
70 /* list of IMAPNameSpace */
82 time_t last_access_time;
83 gboolean authenticated;
85 gboolean folder_content_changed;
95 #define IMAP_SUCCESS 0
97 #define IMAP_AUTHFAIL 3
98 #define IMAP_PROTOCOL 4
103 #define IMAPBUFSIZE 8192
104 #define IMAPCMDLIMIT 1000
108 IMAP_FLAG_SEEN = 1 << 0,
109 IMAP_FLAG_ANSWERED = 1 << 1,
110 IMAP_FLAG_FLAGGED = 1 << 2,
111 IMAP_FLAG_DELETED = 1 << 3,
112 IMAP_FLAG_DRAFT = 1 << 4
115 #define IMAP_IS_SEEN(flags) ((flags & IMAP_FLAG_SEEN) != 0)
116 #define IMAP_IS_ANSWERED(flags) ((flags & IMAP_FLAG_ANSWERED) != 0)
117 #define IMAP_IS_FLAGGED(flags) ((flags & IMAP_FLAG_FLAGGED) != 0)
118 #define IMAP_IS_DELETED(flags) ((flags & IMAP_FLAG_DELETED) != 0)
119 #define IMAP_IS_DRAFT(flags) ((flags & IMAP_FLAG_DRAFT) != 0)
122 #define IMAP4_PORT 143
124 #define IMAPS_PORT 993
127 #define QUOTE_IF_REQUIRED(out, str) \
129 if (*str != '"' && strpbrk(str, " \t(){}%*") != NULL) { \
133 len = strlen(str) + 3; \
134 Xalloca(__tmp, len, return IMAP_ERROR); \
135 g_snprintf(__tmp, len, "\"%s\"", str); \
138 Xstrdup_a(out, str, return IMAP_ERROR); \
142 typedef gchar * IMAPSet;
144 struct _IMAPFolderItem
153 static Folder *imap_folder_new(const gchar * name, const gchar * path);
154 static void imap_folder_destroy(Folder * folder);
156 static IMAPSession *imap_session_new(const PrefsAccount * account);
157 static void imap_session_authenticate(IMAPSession * session,
158 const PrefsAccount * account);
159 static void imap_session_destroy(Session * session);
161 static gchar *imap_fetch_msg(Folder * folder, FolderItem * item, gint uid);
162 static gint imap_add_msg(Folder * folder,
164 const gchar * file, gboolean remove_source);
166 static gint imap_copy_msg(Folder * folder,
167 FolderItem * dest, MsgInfo * msginfo);
169 static gint imap_remove_msg(Folder * folder, FolderItem * item, gint uid);
170 static gint imap_remove_all_msg(Folder * folder, FolderItem * item);
172 static gboolean imap_is_msg_changed(Folder * folder,
173 FolderItem * item, MsgInfo * msginfo);
175 static void imap_scan_tree(Folder * folder);
177 static gint imap_create_tree(Folder * folder);
179 static FolderItem *imap_create_folder(Folder * folder,
182 static gint imap_rename_folder(Folder * folder,
183 FolderItem * item, const gchar * name);
184 static gint imap_remove_folder(Folder * folder, FolderItem * item);
187 static void imap_folder_init (Folder *folder,
191 static FolderItem *imap_folder_item_new (Folder *folder);
192 static void imap_folder_item_destroy (Folder *folder,
195 static IMAPSession *imap_session_get (Folder *folder);
197 static gint imap_scan_tree_recursive (IMAPSession *session,
199 static GSList *imap_parse_list (IMAPFolder *folder,
200 IMAPSession *session,
201 const gchar *real_path,
204 static void imap_create_missing_folders (Folder *folder);
205 static FolderItem *imap_create_special_folder
207 SpecialFolderItemType stype,
210 static gint imap_do_copy (Folder *folder,
213 gboolean remove_source);
215 static void imap_delete_all_cached_messages (FolderItem *item);
218 static SockInfo *imap_open (const gchar *server,
222 static SockInfo *imap_open (const gchar *server,
227 static SockInfo *imap_open_tunnel(const gchar *server,
228 const gchar *tunnelcmd,
231 static SockInfo *imap_open_tunnel(const gchar *server,
232 const gchar *tunnelcmd);
236 static SockInfo *imap_init_sock(SockInfo *sock, SSLType ssl_type);
238 static SockInfo *imap_init_sock(SockInfo *sock);
241 static gint imap_set_message_flags (IMAPSession *session,
242 MsgNumberList *numlist,
245 static gint imap_select (IMAPSession *session,
251 guint32 *uid_validity);
252 static gint imap_status (IMAPSession *session,
258 guint32 *uid_validity,
261 static void imap_parse_namespace (IMAPSession *session,
263 static void imap_get_namespace_by_list (IMAPSession *session,
265 static IMAPNameSpace *imap_find_namespace (IMAPFolder *folder,
267 static gchar imap_get_path_separator (IMAPFolder *folder,
269 static gchar *imap_get_real_path (IMAPFolder *folder,
272 static gchar *imap_parse_atom (SockInfo *sock,
277 static MsgFlags imap_parse_flags (const gchar *flag_str);
278 static MsgInfo *imap_parse_envelope (SockInfo *sock,
281 static gint imap_greeting (IMAPSession *session);
282 static gboolean imap_has_capability (IMAPSession *session,
284 void imap_free_capabilities (IMAPSession *session);
285 static const IMAPSet numberlist_to_imapset
286 (MsgNumberList *list);
288 /* low-level IMAP4rev1 commands */
289 static gint imap_cmd_login (IMAPSession *sock,
292 static gint imap_cmd_logout (IMAPSession *sock);
293 static gint imap_cmd_noop (IMAPSession *sock);
294 static gint imap_cmd_starttls (IMAPSession *sock);
295 static gint imap_cmd_namespace (IMAPSession *sock,
297 static gint imap_cmd_list (IMAPSession *session,
299 const gchar *mailbox,
301 static gint imap_cmd_do_select (IMAPSession *sock,
307 guint32 *uid_validity);
308 static gint imap_cmd_select (IMAPSession *sock,
313 guint32 *uid_validity);
314 static gint imap_cmd_examine (IMAPSession *sock,
319 guint32 *uid_validity);
320 static gint imap_cmd_create (IMAPSession *sock,
321 const gchar *folder);
322 static gint imap_cmd_rename (IMAPSession *sock,
323 const gchar *oldfolder,
324 const gchar *newfolder);
325 static gint imap_cmd_delete (IMAPSession *sock,
326 const gchar *folder);
327 static gint imap_cmd_envelope (IMAPSession *sock,
329 static gint imap_cmd_fetch (IMAPSession *sock,
331 const gchar *filename);
332 static gint imap_cmd_append (IMAPSession *session,
333 const gchar *destfolder,
336 static gint imap_cmd_copy (IMAPSession *session,
338 const gchar *destfolder,
340 static gint imap_cmd_store (IMAPSession *sock,
343 static gint imap_cmd_expunge (IMAPSession *sock);
345 static gint imap_cmd_ok (IMAPSession *session,
347 static void imap_gen_send (IMAPSession *sock,
348 const gchar *format, ...);
349 static gint imap_gen_recv (IMAPSession *sock,
352 /* misc utility functions */
353 static gchar *strchr_cpy (const gchar *src,
357 static gchar *get_quoted (const gchar *src,
361 static gchar *search_array_contain_str (GPtrArray *array,
363 static gchar *search_array_str (GPtrArray *array,
365 static void imap_path_separator_subst (gchar *str,
368 static gchar *imap_modified_utf7_to_locale (const gchar *mutf7_str);
369 static gchar *imap_locale_to_modified_utf7 (const gchar *from);
371 static gboolean imap_rename_folder_func (GNode *node,
373 static gint imap_get_num_list (Folder *folder,
376 static GSList *imap_get_msginfos (Folder *folder,
378 GSList *msgnum_list);
379 static MsgInfo *imap_get_msginfo (Folder *folder,
382 static gboolean imap_check_msgnum_validity (Folder *folder,
384 static void imap_change_flags (Folder *folder,
387 MsgPermFlags newflags);
388 static gchar *imap_folder_get_path (Folder *folder);
389 static gchar *imap_item_get_path (Folder *folder,
392 FolderClass imap_class =
398 /* Folder functions */
404 /* FolderItem functions */
405 imap_folder_item_new,
406 imap_folder_item_destroy,
415 imap_check_msgnum_validity,
417 /* Message functions */
429 FolderClass *imap_get_class(void)
434 Folder *imap_folder_new(const gchar *name, const gchar *path)
438 folder = (Folder *)g_new0(IMAPFolder, 1);
439 folder->klass = &imap_class;
440 imap_folder_init(folder, name, path);
445 void imap_folder_destroy(Folder *folder)
449 dir = imap_folder_get_path(folder);
450 if (is_dir_exist(dir))
451 remove_dir_recursive(dir);
454 folder_remote_folder_destroy(REMOTE_FOLDER(folder));
457 static void imap_folder_init(Folder *folder, const gchar *name,
460 folder_remote_folder_init((Folder *)folder, name, path);
463 static FolderItem *imap_folder_item_new(Folder *folder)
465 IMAPFolderItem *item;
467 item = g_new0(IMAPFolderItem, 1);
470 item->uid_list = NULL;
472 return (FolderItem *)item;
475 static void imap_folder_item_destroy(Folder *folder, FolderItem *_item)
477 IMAPFolderItem *item = (IMAPFolderItem *)_item;
479 g_return_if_fail(item != NULL);
480 g_slist_free(item->uid_list);
485 static gboolean imap_reset_uid_lists_func(GNode *node, gpointer data)
487 IMAPFolderItem *item = (IMAPFolderItem *)node->data;
491 g_slist_free(item->uid_list);
492 item->uid_list = NULL;
497 static void imap_reset_uid_lists(Folder *folder)
499 if(folder->node == NULL)
502 /* Destroy all uid lists and rest last uid */
503 g_node_traverse(folder->node, G_IN_ORDER, G_TRAVERSE_ALL, -1, imap_reset_uid_lists_func, NULL);
506 static IMAPSession *imap_session_get(Folder *folder)
508 RemoteFolder *rfolder = REMOTE_FOLDER(folder);
509 IMAPSession *session = NULL;
512 g_return_val_if_fail(folder != NULL, NULL);
513 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, NULL);
514 g_return_val_if_fail(folder->account != NULL, NULL);
517 port = folder->account->set_imapport ? folder->account->imapport
518 : folder->account->ssl_imap == SSL_TUNNEL
519 ? IMAPS_PORT : IMAP4_PORT;
521 port = folder->account->set_imapport ? folder->account->imapport
525 /* Make sure we have a session */
526 if (rfolder->session != NULL) {
527 session = IMAP_SESSION(rfolder->session);
529 imap_reset_uid_lists(folder);
530 session = imap_session_new(folder->account);
535 if (SESSION(session)->sock->state == CONN_DISCONNECTED) {
536 debug_print("IMAP server disconnected\n");
537 session_destroy(SESSION(session));
538 imap_reset_uid_lists(folder);
539 session = imap_session_new(folder->account);
542 /* Make sure session is authenticated */
543 if (!IMAP_SESSION(session)->authenticated)
544 imap_session_authenticate(IMAP_SESSION(session), folder->account);
545 if (!IMAP_SESSION(session)->authenticated) {
546 session_destroy(SESSION(session));
547 rfolder->session = NULL;
551 /* Make sure we have parsed the IMAP namespace */
552 imap_parse_namespace(IMAP_SESSION(session),
553 IMAP_FOLDER(folder));
555 /* I think the point of this code is to avoid sending a
556 * keepalive if we've used the session recently and therefore
557 * think it's still alive. Unfortunately, most of the code
558 * does not yet check for errors on the socket, and so if the
559 * connection drops we don't notice until the timeout expires.
560 * A better solution than sending a NOOP every time would be
561 * for every command to be prepared to retry until it is
562 * successfully sent. -- mbp */
563 if (time(NULL) - session->last_access_time > SESSION_TIMEOUT) {
564 /* verify that the session is still alive */
565 if (imap_cmd_noop(session) != IMAP_SUCCESS) {
566 /* Check if this is the first try to establish a
567 connection, if yes we don't try to reconnect */
568 if (rfolder->session == NULL) {
569 log_warning(_("Connecting %s:%d failed"),
570 folder->account->recv_server, port);
571 session_destroy(SESSION(session));
574 log_warning(_("IMAP4 connection to %s:%d has been"
575 " disconnected. Reconnecting...\n"),
576 folder->account->recv_server, port);
577 session_destroy(SESSION(session));
578 /* Clear folders session to make imap_session_get create
579 a new session, because of rfolder->session == NULL
580 it will not try to reconnect again and so avoid an
582 rfolder->session = NULL;
583 session = imap_session_get(folder);
588 rfolder->session = SESSION(session);
590 session->last_access_time = time(NULL);
592 return IMAP_SESSION(session);
595 IMAPSession *imap_session_new(const PrefsAccount *account)
597 IMAPSession *session;
602 /* FIXME: IMAP over SSL only... */
605 port = account->set_imapport ? account->imapport
606 : account->ssl_imap == SSL_TUNNEL ? IMAPS_PORT : IMAP4_PORT;
607 ssl_type = account->ssl_imap;
609 port = account->set_imapport ? account->imapport
613 if (account->set_tunnelcmd) {
614 log_message(_("creating tunneled IMAP4 connection\n"));
616 if ((imap_sock = imap_open_tunnel(account->recv_server,
620 if ((imap_sock = imap_open_tunnel(account->recv_server,
621 account->tunnelcmd)) == NULL)
625 g_return_val_if_fail(account->recv_server != NULL, NULL);
627 log_message(_("creating IMAP4 connection to %s:%d ...\n"),
628 account->recv_server, port);
631 if ((imap_sock = imap_open(account->recv_server, port,
634 if ((imap_sock = imap_open(account->recv_server, port)) == NULL)
639 session = g_new0(IMAPSession, 1);
640 session_init(SESSION(session));
641 SESSION(session)->type = SESSION_IMAP;
642 SESSION(session)->server = g_strdup(account->recv_server);
643 SESSION(session)->sock = imap_sock;
645 SESSION(session)->destroy = imap_session_destroy;
647 session->capability = NULL;
649 session->mbox = NULL;
650 session->authenticated = FALSE;
651 session->cmd_count = 0;
653 /* Only need to log in if the connection was not PREAUTH */
654 if (imap_greeting(session) != IMAP_SUCCESS) {
655 session_destroy(SESSION(session));
660 if (account->ssl_imap == SSL_STARTTLS && imap_has_capability(session, "STARTTLS")) {
663 ok = imap_cmd_starttls(session);
664 if (ok != IMAP_SUCCESS) {
665 log_warning(_("Can't start TLS session.\n"));
666 session_destroy(SESSION(session));
669 if (!ssl_init_socket_with_method(SESSION(session)->sock, SSL_METHOD_TLSv1)) {
670 session_destroy(SESSION(session));
674 imap_free_capabilities(session);
675 session->authenticated = FALSE;
676 session->cmd_count = 1;
678 if (imap_greeting(session) != IMAP_SUCCESS) {
679 session_destroy(SESSION(session));
684 log_message("IMAP connection is %s-authenticated\n",
685 (session->authenticated) ? "pre" : "un");
690 void imap_session_authenticate(IMAPSession *session, const PrefsAccount *account)
694 g_return_if_fail(account->userid != NULL);
696 pass = account->passwd;
699 tmp_pass = input_dialog_query_password(account->recv_server, account->userid);
702 Xstrdup_a(pass, tmp_pass, {g_free(tmp_pass); return;});
706 if (imap_cmd_login(session, account->userid, pass) != IMAP_SUCCESS) {
707 imap_cmd_logout(session);
711 session->authenticated = TRUE;
714 void imap_session_destroy(Session *session)
716 sock_close(session->sock);
717 session->sock = NULL;
719 g_free(IMAP_SESSION(session)->mbox);
720 imap_free_capabilities(IMAP_SESSION(session));
723 gchar *imap_fetch_msg(Folder *folder, FolderItem *item, gint uid)
725 gchar *path, *filename;
726 IMAPSession *session;
729 g_return_val_if_fail(folder != NULL, NULL);
730 g_return_val_if_fail(item != NULL, NULL);
732 path = folder_item_get_path(item);
733 if (!is_dir_exist(path))
735 filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(uid), NULL);
738 if (is_file_exist(filename)) {
739 debug_print("message %d has been already cached.\n", uid);
743 session = imap_session_get(folder);
749 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
750 NULL, NULL, NULL, NULL);
751 if (ok != IMAP_SUCCESS) {
752 g_warning("can't select mailbox %s\n", item->path);
757 debug_print("getting message %d...\n", uid);
758 ok = imap_cmd_fetch(session, (guint32)uid, filename);
760 if (ok != IMAP_SUCCESS) {
761 g_warning("can't fetch message %d\n", uid);
769 gint imap_add_msg(Folder *folder, FolderItem *dest, const gchar *file,
770 gboolean remove_source)
773 IMAPSession *session;
776 g_return_val_if_fail(folder != NULL, -1);
777 g_return_val_if_fail(dest != NULL, -1);
778 g_return_val_if_fail(file != NULL, -1);
780 session = imap_session_get(folder);
781 if (!session) return -1;
783 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
784 ok = imap_cmd_append(session, destdir, file, &newuid);
787 if (ok != IMAP_SUCCESS) {
788 g_warning("can't append message %s\n", file);
793 if (unlink(file) < 0)
794 FILE_OP_ERROR(file, "unlink");
800 static gint imap_do_copy(Folder *folder, FolderItem *dest, MsgInfo *msginfo,
801 gboolean remove_source)
804 IMAPSession *session;
808 g_return_val_if_fail(folder != NULL, -1);
809 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
810 g_return_val_if_fail(dest != NULL, -1);
811 g_return_val_if_fail(msginfo != NULL, -1);
813 session = imap_session_get(folder);
814 if (!session) return -1;
816 if (msginfo->folder == dest) {
817 g_warning("the src folder is identical to the dest.\n");
821 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
823 /* ensure source folder selected */
824 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
825 NULL, NULL, NULL, NULL);
826 if (ok != IMAP_SUCCESS)
830 debug_print("Moving message %s%c%d to %s ...\n",
831 msginfo->folder->path, G_DIR_SEPARATOR,
832 msginfo->msgnum, destdir);
834 debug_print("Copying message %s%c%d to %s ...\n",
835 msginfo->folder->path, G_DIR_SEPARATOR,
836 msginfo->msgnum, destdir);
838 ok = imap_cmd_copy(session, msginfo->msgnum, destdir, &newuid);
840 if (ok == IMAP_SUCCESS && remove_source) {
841 MsgNumberList numlist;
844 numlist.data = GINT_TO_POINTER(msginfo->msgnum);
846 imap_set_message_flags(session, &numlist,
847 IMAP_FLAG_DELETED, TRUE);
848 ok = imap_cmd_expunge(session);
853 if (ok == IMAP_SUCCESS)
859 gint imap_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
864 g_return_val_if_fail(folder != NULL, -1);
865 g_return_val_if_fail(dest != NULL, -1);
866 g_return_val_if_fail(msginfo != NULL, -1);
867 g_return_val_if_fail(msginfo->folder != NULL, -1);
869 if (folder == msginfo->folder->folder)
870 return imap_do_copy(folder, dest, msginfo, FALSE);
872 srcfile = procmsg_get_message_file(msginfo);
873 if (!srcfile) return -1;
875 ret = imap_add_msg(folder, dest, srcfile, FALSE);
882 gint imap_remove_msg(Folder *folder, FolderItem *item, gint uid)
885 IMAPSession *session;
887 MsgNumberList numlist;
889 g_return_val_if_fail(folder != NULL, -1);
890 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
891 g_return_val_if_fail(item != NULL, -1);
893 session = imap_session_get(folder);
894 if (!session) return -1;
896 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
897 NULL, NULL, NULL, NULL);
898 if (ok != IMAP_SUCCESS)
902 numlist.data = GINT_TO_POINTER(uid);
904 ok = imap_set_message_flags
905 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
906 &numlist, IMAP_FLAG_DELETED, TRUE);
907 if (ok != IMAP_SUCCESS) {
908 log_warning(_("can't set deleted flags: %d\n"), uid);
912 ok = imap_cmd_expunge(session);
913 if (ok != IMAP_SUCCESS) {
914 log_warning(_("can't expunge\n"));
918 dir = folder_item_get_path(item);
919 if (is_dir_exist(dir))
920 remove_numbered_files(dir, uid, uid);
926 gint imap_remove_all_msg(Folder *folder, FolderItem *item)
928 gint exists, recent, unseen;
929 guint32 uid_validity;
931 IMAPSession *session;
934 g_return_val_if_fail(folder != NULL, -1);
935 g_return_val_if_fail(item != NULL, -1);
937 session = imap_session_get(folder);
938 if (!session) return -1;
940 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
941 &exists, &recent, &unseen, &uid_validity);
942 if (ok != IMAP_SUCCESS)
947 imap_gen_send(session,
948 "STORE 1:%d +FLAGS (\\Deleted)", exists);
949 ok = imap_cmd_ok(session, NULL);
950 if (ok != IMAP_SUCCESS) {
951 log_warning(_("can't set deleted flags: 1:%d\n"), exists);
955 ok = imap_cmd_expunge(session);
956 if (ok != IMAP_SUCCESS) {
957 log_warning(_("can't expunge\n"));
961 dir = folder_item_get_path(item);
962 if (is_dir_exist(dir))
963 remove_all_numbered_files(dir);
969 gboolean imap_is_msg_changed(Folder *folder, FolderItem *item, MsgInfo *msginfo)
971 /* TODO: properly implement this method */
975 void imap_scan_tree(Folder *folder)
978 IMAPSession *session;
979 gchar *root_folder = NULL;
981 g_return_if_fail(folder != NULL);
982 g_return_if_fail(folder->account != NULL);
984 session = imap_session_get(folder);
987 folder_tree_destroy(folder);
988 item = folder_item_new(folder, folder->name, NULL);
989 item->folder = folder;
990 folder->node = g_node_new(item);
995 if (folder->account->imap_dir && *folder->account->imap_dir) {
996 Xstrdup_a(root_folder, folder->account->imap_dir, return);
997 strtailchomp(root_folder, '/');
998 debug_print("IMAP root directory: %s\n", root_folder);
1001 item = folder_item_new(folder, folder->name, root_folder);
1002 item->folder = folder;
1003 item->no_select = TRUE;
1004 folder->node = g_node_new(item);
1006 imap_scan_tree_recursive(session, item);
1008 imap_create_missing_folders(folder);
1011 static gint imap_scan_tree_recursive(IMAPSession *session, FolderItem *item)
1014 IMAPFolder *imapfolder;
1015 FolderItem *new_item;
1016 GSList *item_list, *cur;
1018 gchar *wildcard_path;
1022 g_return_val_if_fail(item != NULL, -1);
1023 g_return_val_if_fail(item->folder != NULL, -1);
1024 g_return_val_if_fail(item->no_sub == FALSE, -1);
1026 folder = FOLDER(item->folder);
1027 imapfolder = IMAP_FOLDER(folder);
1029 separator = imap_get_path_separator(imapfolder, item->path);
1031 if (item->folder->ui_func)
1032 item->folder->ui_func(folder, item, folder->ui_func_data);
1035 wildcard[0] = separator;
1038 real_path = imap_get_real_path(imapfolder, item->path);
1042 real_path = g_strdup("");
1045 Xstrcat_a(wildcard_path, real_path, wildcard,
1046 {g_free(real_path); return IMAP_ERROR;});
1047 QUOTE_IF_REQUIRED(wildcard_path, wildcard_path);
1049 imap_gen_send(session, "LIST \"\" %s",
1052 strtailchomp(real_path, separator);
1053 item_list = imap_parse_list(imapfolder, session, real_path, NULL);
1056 for (cur = item_list; cur != NULL; cur = cur->next) {
1057 new_item = cur->data;
1058 if (!strcmp(new_item->path, "INBOX")) {
1059 if (!folder->inbox) {
1060 new_item->stype = F_INBOX;
1061 item->folder->inbox = new_item;
1063 folder_item_destroy(new_item);
1066 } else if (!item->parent || item->stype == F_INBOX) {
1069 base = g_basename(new_item->path);
1071 if (!folder->outbox && !strcasecmp(base, "Sent")) {
1072 new_item->stype = F_OUTBOX;
1073 folder->outbox = new_item;
1074 } else if (!folder->draft && !strcasecmp(base, "Drafts")) {
1075 new_item->stype = F_DRAFT;
1076 folder->draft = new_item;
1077 } else if (!folder->queue && !strcasecmp(base, "Queue")) {
1078 new_item->stype = F_QUEUE;
1079 folder->queue = new_item;
1080 } else if (!folder->trash && !strcasecmp(base, "Trash")) {
1081 new_item->stype = F_TRASH;
1082 folder->trash = new_item;
1085 folder_item_append(item, new_item);
1086 if (new_item->no_sub == FALSE)
1087 imap_scan_tree_recursive(session, new_item);
1090 return IMAP_SUCCESS;
1093 static GSList *imap_parse_list(IMAPFolder *folder, IMAPSession *session,
1094 const gchar *real_path, gchar *separator)
1096 gchar buf[IMAPBUFSIZE];
1098 gchar separator_str[16];
1101 gchar *loc_name, *loc_path;
1102 GSList *item_list = NULL;
1104 FolderItem *new_item;
1106 debug_print("getting list of %s ...\n",
1107 *real_path ? real_path : "\"\"");
1109 str = g_string_new(NULL);
1112 if (sock_gets(SESSION(session)->sock, buf, sizeof(buf)) <= 0) {
1113 log_warning(_("error occurred while getting LIST.\n"));
1117 if (buf[0] != '*' || buf[1] != ' ') {
1118 log_print("IMAP4< %s\n", buf);
1121 debug_print("IMAP4< %s\n", buf);
1123 g_string_assign(str, buf);
1125 if (strncmp(p, "LIST ", 5) != 0) continue;
1128 if (*p != '(') continue;
1130 p = strchr_cpy(p, ')', flags, sizeof(flags));
1132 while (*p == ' ') p++;
1134 p = strchr_cpy(p, ' ', separator_str, sizeof(separator_str));
1136 extract_quote(separator_str, '"');
1137 if (!strcmp(separator_str, "NIL"))
1138 separator_str[0] = '\0';
1140 *separator = separator_str[0];
1143 while (*p == ' ') p++;
1144 if (*p == '{' || *p == '"')
1145 p = imap_parse_atom(SESSION(session)->sock, p,
1146 buf, sizeof(buf), str);
1148 strncpy2(buf, p, sizeof(buf));
1149 strtailchomp(buf, separator_str[0]);
1150 if (buf[0] == '\0') continue;
1151 if (!strcmp(buf, real_path)) continue;
1153 if (separator_str[0] != '\0')
1154 subst_char(buf, separator_str[0], '/');
1155 name = g_basename(buf);
1156 if (name[0] == '.') continue;
1158 loc_name = imap_modified_utf7_to_locale(name);
1159 loc_path = imap_modified_utf7_to_locale(buf);
1160 new_item = folder_item_new(FOLDER(folder), loc_name, loc_path);
1161 if (strcasestr(flags, "\\Noinferiors") != NULL)
1162 new_item->no_sub = TRUE;
1163 if (strcmp(buf, "INBOX") != 0 &&
1164 strcasestr(flags, "\\Noselect") != NULL)
1165 new_item->no_select = TRUE;
1167 item_list = g_slist_append(item_list, new_item);
1169 debug_print("folder %s has been added.\n", loc_path);
1174 g_string_free(str, TRUE);
1179 gint imap_create_tree(Folder *folder)
1181 g_return_val_if_fail(folder != NULL, -1);
1182 g_return_val_if_fail(folder->node != NULL, -1);
1183 g_return_val_if_fail(folder->node->data != NULL, -1);
1184 g_return_val_if_fail(folder->account != NULL, -1);
1186 imap_scan_tree(folder);
1187 imap_create_missing_folders(folder);
1192 static void imap_create_missing_folders(Folder *folder)
1194 g_return_if_fail(folder != NULL);
1197 folder->inbox = imap_create_special_folder
1198 (folder, F_INBOX, "INBOX");
1200 if (!folder->outbox)
1201 folder->outbox = imap_create_special_folder
1202 (folder, F_OUTBOX, "Sent");
1204 folder->draft = imap_create_special_folder
1205 (folder, F_DRAFT, "Drafts");
1207 folder->queue = imap_create_special_folder
1208 (folder, F_QUEUE, "Queue");
1211 folder->trash = imap_create_special_folder
1212 (folder, F_TRASH, "Trash");
1215 static FolderItem *imap_create_special_folder(Folder *folder,
1216 SpecialFolderItemType stype,
1220 FolderItem *new_item;
1222 g_return_val_if_fail(folder != NULL, NULL);
1223 g_return_val_if_fail(folder->node != NULL, NULL);
1224 g_return_val_if_fail(folder->node->data != NULL, NULL);
1225 g_return_val_if_fail(folder->account != NULL, NULL);
1226 g_return_val_if_fail(name != NULL, NULL);
1228 item = FOLDER_ITEM(folder->node->data);
1229 new_item = imap_create_folder(folder, item, name);
1232 g_warning("Can't create '%s'\n", name);
1233 if (!folder->inbox) return NULL;
1235 new_item = imap_create_folder(folder, folder->inbox, name);
1237 g_warning("Can't create '%s' under INBOX\n", name);
1239 new_item->stype = stype;
1241 new_item->stype = stype;
1246 static gchar *imap_folder_get_path(Folder *folder)
1250 g_return_val_if_fail(folder != NULL, NULL);
1251 g_return_val_if_fail(folder->account != NULL, NULL);
1253 folder_path = g_strconcat(get_imap_cache_dir(),
1255 folder->account->recv_server,
1257 folder->account->userid,
1263 static gchar *imap_item_get_path(Folder *folder, FolderItem *item)
1265 gchar *folder_path, *path;
1267 g_return_val_if_fail(folder != NULL, NULL);
1268 g_return_val_if_fail(item != NULL, NULL);
1269 folder_path = imap_folder_get_path(folder);
1271 g_return_val_if_fail(folder_path != NULL, NULL);
1272 if (folder_path[0] == G_DIR_SEPARATOR) {
1274 path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
1277 path = g_strdup(folder_path);
1280 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1281 folder_path, G_DIR_SEPARATOR_S,
1284 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1287 g_free(folder_path);
1292 FolderItem *imap_create_folder(Folder *folder, FolderItem *parent,
1295 gchar *dirpath, *imap_path;
1296 IMAPSession *session;
1297 FolderItem *new_item;
1303 g_return_val_if_fail(folder != NULL, NULL);
1304 g_return_val_if_fail(folder->account != NULL, NULL);
1305 g_return_val_if_fail(parent != NULL, NULL);
1306 g_return_val_if_fail(name != NULL, NULL);
1308 session = imap_session_get(folder);
1309 if (!session) return NULL;
1311 if (!parent->parent && strcmp(name, "INBOX") == 0)
1312 dirpath = g_strdup(name);
1313 else if (parent->path)
1314 dirpath = g_strconcat(parent->path, "/", name, NULL);
1315 else if ((p = strchr(name, '/')) != NULL && *(p + 1) != '\0')
1316 dirpath = g_strdup(name);
1317 else if (folder->account->imap_dir && *folder->account->imap_dir) {
1320 Xstrdup_a(imap_dir, folder->account->imap_dir, return NULL);
1321 strtailchomp(imap_dir, '/');
1322 dirpath = g_strconcat(imap_dir, "/", name, NULL);
1324 dirpath = g_strdup(name);
1326 /* keep trailing directory separator to create a folder that contains
1328 imap_path = imap_locale_to_modified_utf7(dirpath);
1329 strtailchomp(dirpath, '/');
1330 Xstrdup_a(new_name, name, {g_free(dirpath); return NULL;});
1331 strtailchomp(new_name, '/');
1332 separator = imap_get_path_separator(IMAP_FOLDER(folder), imap_path);
1333 imap_path_separator_subst(imap_path, separator);
1334 subst_char(new_name, '/', separator);
1336 if (strcmp(name, "INBOX") != 0) {
1339 gboolean exist = FALSE;
1341 argbuf = g_ptr_array_new();
1342 ok = imap_cmd_list(session, NULL, imap_path,
1344 if (ok != IMAP_SUCCESS) {
1345 log_warning(_("can't create mailbox: LIST failed\n"));
1348 ptr_array_free_strings(argbuf);
1349 g_ptr_array_free(argbuf, TRUE);
1353 for (i = 0; i < argbuf->len; i++) {
1355 str = g_ptr_array_index(argbuf, i);
1356 if (!strncmp(str, "LIST ", 5)) {
1361 ptr_array_free_strings(argbuf);
1362 g_ptr_array_free(argbuf, TRUE);
1365 ok = imap_cmd_create(session, imap_path);
1366 if (ok != IMAP_SUCCESS) {
1367 log_warning(_("can't create mailbox\n"));
1375 new_item = folder_item_new(folder, new_name, dirpath);
1376 folder_item_append(parent, new_item);
1380 dirpath = folder_item_get_path(new_item);
1381 if (!is_dir_exist(dirpath))
1382 make_dir_hier(dirpath);
1388 gint imap_rename_folder(Folder *folder, FolderItem *item, const gchar *name)
1392 gchar *real_oldpath;
1393 gchar *real_newpath;
1396 gchar *old_cache_dir;
1397 gchar *new_cache_dir;
1398 IMAPSession *session;
1401 gint exists, recent, unseen;
1402 guint32 uid_validity;
1404 g_return_val_if_fail(folder != NULL, -1);
1405 g_return_val_if_fail(item != NULL, -1);
1406 g_return_val_if_fail(item->path != NULL, -1);
1407 g_return_val_if_fail(name != NULL, -1);
1409 session = imap_session_get(folder);
1410 if (!session) return -1;
1412 real_oldpath = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1414 g_free(session->mbox);
1415 session->mbox = NULL;
1416 ok = imap_cmd_examine(session, "INBOX",
1417 &exists, &recent, &unseen, &uid_validity);
1418 if (ok != IMAP_SUCCESS) {
1419 g_free(real_oldpath);
1423 separator = imap_get_path_separator(IMAP_FOLDER(folder), item->path);
1424 if (strchr(item->path, G_DIR_SEPARATOR)) {
1425 dirpath = g_dirname(item->path);
1426 newpath = g_strconcat(dirpath, G_DIR_SEPARATOR_S, name, NULL);
1429 newpath = g_strdup(name);
1431 real_newpath = imap_locale_to_modified_utf7(newpath);
1432 imap_path_separator_subst(real_newpath, separator);
1434 ok = imap_cmd_rename(session, real_oldpath, real_newpath);
1435 if (ok != IMAP_SUCCESS) {
1436 log_warning(_("can't rename mailbox: %s to %s\n"),
1437 real_oldpath, real_newpath);
1438 g_free(real_oldpath);
1440 g_free(real_newpath);
1445 item->name = g_strdup(name);
1447 old_cache_dir = folder_item_get_path(item);
1449 node = g_node_find(item->folder->node, G_PRE_ORDER, G_TRAVERSE_ALL,
1451 paths[0] = g_strdup(item->path);
1453 g_node_traverse(node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1454 imap_rename_folder_func, paths);
1456 if (is_dir_exist(old_cache_dir)) {
1457 new_cache_dir = folder_item_get_path(item);
1458 if (rename(old_cache_dir, new_cache_dir) < 0) {
1459 FILE_OP_ERROR(old_cache_dir, "rename");
1461 g_free(new_cache_dir);
1464 g_free(old_cache_dir);
1467 g_free(real_oldpath);
1468 g_free(real_newpath);
1473 gint imap_remove_folder(Folder *folder, FolderItem *item)
1476 IMAPSession *session;
1479 gint exists, recent, unseen;
1480 guint32 uid_validity;
1482 g_return_val_if_fail(folder != NULL, -1);
1483 g_return_val_if_fail(item != NULL, -1);
1484 g_return_val_if_fail(item->path != NULL, -1);
1486 session = imap_session_get(folder);
1487 if (!session) return -1;
1489 path = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1491 ok = imap_cmd_examine(session, "INBOX",
1492 &exists, &recent, &unseen, &uid_validity);
1493 if (ok != IMAP_SUCCESS) {
1498 ok = imap_cmd_delete(session, path);
1499 if (ok != IMAP_SUCCESS) {
1500 log_warning(_("can't delete mailbox\n"));
1506 cache_dir = folder_item_get_path(item);
1507 if (is_dir_exist(cache_dir) && remove_dir_recursive(cache_dir) < 0)
1508 g_warning("can't remove directory '%s'\n", cache_dir);
1510 folder_item_remove(item);
1515 static GSList *imap_get_uncached_messages(IMAPSession *session,
1517 MsgNumberList *numlist)
1520 GSList *newlist = NULL;
1521 GSList *llast = NULL;
1526 g_return_val_if_fail(session != NULL, NULL);
1527 g_return_val_if_fail(item != NULL, NULL);
1528 g_return_val_if_fail(item->folder != NULL, NULL);
1529 g_return_val_if_fail(FOLDER_CLASS(item->folder) == &imap_class, NULL);
1531 imapset = numberlist_to_imapset(numlist);
1532 while (imapset != NULL) {
1533 if (imap_cmd_envelope(session, imapset)
1535 log_warning(_("can't get envelope\n"));
1539 str = g_string_new(NULL);
1542 if ((tmp = sock_getline(SESSION(session)->sock)) == NULL) {
1543 log_warning(_("error occurred while getting envelope.\n"));
1544 g_string_free(str, TRUE);
1548 if (tmp[0] != '*' || tmp[1] != ' ') {
1549 log_print("IMAP4< %s\n", tmp);
1553 if (strstr(tmp, "FETCH") == NULL) {
1554 log_print("IMAP4< %s\n", tmp);
1558 log_print("IMAP4< %s\n", tmp);
1559 g_string_assign(str, tmp);
1562 msginfo = imap_parse_envelope
1563 (SESSION(session)->sock, item, str);
1565 log_warning(_("can't parse envelope: %s\n"), str->str);
1568 if (item->stype == F_QUEUE) {
1569 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_QUEUED);
1570 } else if (item->stype == F_DRAFT) {
1571 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_DRAFT);
1574 msginfo->folder = item;
1577 llast = newlist = g_slist_append(newlist, msginfo);
1579 llast = g_slist_append(llast, msginfo);
1580 llast = llast->next;
1584 g_string_free(str, TRUE);
1585 imapset = numberlist_to_imapset(NULL);
1591 static void imap_delete_all_cached_messages(FolderItem *item)
1595 g_return_if_fail(item != NULL);
1596 g_return_if_fail(item->folder != NULL);
1597 g_return_if_fail(FOLDER_CLASS(item->folder) == &imap_class);
1599 debug_print("Deleting all cached messages...\n");
1601 dir = folder_item_get_path(item);
1602 if (is_dir_exist(dir))
1603 remove_all_numbered_files(dir);
1606 debug_print("done.\n");
1610 static SockInfo *imap_open_tunnel(const gchar *server,
1611 const gchar *tunnelcmd,
1614 static SockInfo *imap_open_tunnel(const gchar *server,
1615 const gchar *tunnelcmd)
1620 if ((sock = sock_connect_cmd(server, tunnelcmd)) == NULL) {
1621 log_warning(_("Can't establish IMAP4 session with: %s\n"),
1626 return imap_init_sock(sock, ssl_type);
1628 return imap_init_sock(sock);
1634 static SockInfo *imap_open(const gchar *server, gushort port,
1637 static SockInfo *imap_open(const gchar *server, gushort port)
1642 if ((sock = sock_connect(server, port)) == NULL) {
1643 log_warning(_("Can't connect to IMAP4 server: %s:%d\n"),
1649 if (ssl_type == SSL_TUNNEL && !ssl_init_socket(sock)) {
1650 log_warning(_("Can't establish IMAP4 session with: %s:%d\n"),
1660 static SockInfo *imap_init_sock(SockInfo *sock, SSLType ssl_type)
1662 static SockInfo *imap_init_sock(SockInfo *sock)
1669 static GList *imap_parse_namespace_str(gchar *str)
1674 IMAPNameSpace *namespace;
1675 GList *ns_list = NULL;
1677 while (*p != '\0') {
1678 /* parse ("#foo" "/") */
1680 while (*p && *p != '(') p++;
1681 if (*p == '\0') break;
1684 while (*p && *p != '"') p++;
1685 if (*p == '\0') break;
1689 while (*p && *p != '"') p++;
1690 if (*p == '\0') break;
1694 while (*p && isspace(*p)) p++;
1695 if (*p == '\0') break;
1696 if (strncmp(p, "NIL", 3) == 0)
1698 else if (*p == '"') {
1701 while (*p && *p != '"') p++;
1702 if (*p == '\0') break;
1707 while (*p && *p != ')') p++;
1708 if (*p == '\0') break;
1711 namespace = g_new(IMAPNameSpace, 1);
1712 namespace->name = g_strdup(name);
1713 namespace->separator = separator ? separator[0] : '\0';
1714 ns_list = g_list_append(ns_list, namespace);
1720 static void imap_parse_namespace(IMAPSession *session, IMAPFolder *folder)
1725 g_return_if_fail(session != NULL);
1726 g_return_if_fail(folder != NULL);
1728 if (folder->ns_personal != NULL ||
1729 folder->ns_others != NULL ||
1730 folder->ns_shared != NULL)
1733 if (!imap_has_capability(session, "NAMESPACE")) {
1734 imap_get_namespace_by_list(session, folder);
1738 if (imap_cmd_namespace(session, &ns_str)
1740 log_warning(_("can't get namespace\n"));
1744 str_array = strsplit_parenthesis(ns_str, '(', ')', 3);
1745 if (str_array == NULL) {
1747 imap_get_namespace_by_list(session, folder);
1751 folder->ns_personal = imap_parse_namespace_str(str_array[0]);
1752 if (str_array[0] && str_array[1])
1753 folder->ns_others = imap_parse_namespace_str(str_array[1]);
1754 if (str_array[0] && str_array[1] && str_array[2])
1755 folder->ns_shared = imap_parse_namespace_str(str_array[2]);
1756 g_strfreev(str_array);
1760 static void imap_get_namespace_by_list(IMAPSession *session, IMAPFolder *folder)
1762 GSList *item_list, *cur;
1763 gchar separator = '\0';
1764 IMAPNameSpace *namespace;
1766 g_return_if_fail(session != NULL);
1767 g_return_if_fail(folder != NULL);
1769 if (folder->ns_personal != NULL ||
1770 folder->ns_others != NULL ||
1771 folder->ns_shared != NULL)
1774 imap_gen_send(session, "LIST \"\" \"\"");
1775 item_list = imap_parse_list(folder, session, "", &separator);
1776 for (cur = item_list; cur != NULL; cur = cur->next)
1777 folder_item_destroy(FOLDER_ITEM(cur->data));
1778 g_slist_free(item_list);
1780 namespace = g_new(IMAPNameSpace, 1);
1781 namespace->name = g_strdup("");
1782 namespace->separator = separator;
1783 folder->ns_personal = g_list_append(NULL, namespace);
1786 static IMAPNameSpace *imap_find_namespace_from_list(GList *ns_list,
1789 IMAPNameSpace *namespace = NULL;
1790 gchar *tmp_path, *name;
1792 if (!path) path = "";
1794 Xstrcat_a(tmp_path, path, "/", return NULL);
1796 for (; ns_list != NULL; ns_list = ns_list->next) {
1797 IMAPNameSpace *tmp_ns = ns_list->data;
1799 Xstrdup_a(name, tmp_ns->name, return namespace);
1800 if (tmp_ns->separator && tmp_ns->separator != '/')
1801 subst_char(name, tmp_ns->separator, '/');
1802 if (strncmp(tmp_path, name, strlen(name)) == 0)
1809 static IMAPNameSpace *imap_find_namespace(IMAPFolder *folder,
1812 IMAPNameSpace *namespace;
1814 g_return_val_if_fail(folder != NULL, NULL);
1816 namespace = imap_find_namespace_from_list(folder->ns_personal, path);
1817 if (namespace) return namespace;
1818 namespace = imap_find_namespace_from_list(folder->ns_others, path);
1819 if (namespace) return namespace;
1820 namespace = imap_find_namespace_from_list(folder->ns_shared, path);
1821 if (namespace) return namespace;
1826 static gchar imap_get_path_separator(IMAPFolder *folder, const gchar *path)
1828 IMAPNameSpace *namespace;
1829 gchar separator = '/';
1831 namespace = imap_find_namespace(folder, path);
1832 if (namespace && namespace->separator)
1833 separator = namespace->separator;
1838 static gchar *imap_get_real_path(IMAPFolder *folder, const gchar *path)
1843 g_return_val_if_fail(folder != NULL, NULL);
1844 g_return_val_if_fail(path != NULL, NULL);
1846 real_path = imap_locale_to_modified_utf7(path);
1847 separator = imap_get_path_separator(folder, path);
1848 imap_path_separator_subst(real_path, separator);
1853 static gchar *imap_parse_atom(SockInfo *sock, gchar *src,
1854 gchar *dest, gint dest_len, GString *str)
1856 gchar *cur_pos = src;
1859 g_return_val_if_fail(str != NULL, cur_pos);
1861 /* read the next line if the current response buffer is empty */
1862 while (isspace(*cur_pos)) cur_pos++;
1863 while (*cur_pos == '\0') {
1864 if ((nextline = sock_getline(sock)) == NULL)
1866 g_string_assign(str, nextline);
1868 strretchomp(nextline);
1869 /* log_print("IMAP4< %s\n", nextline); */
1870 debug_print("IMAP4< %s\n", nextline);
1873 while (isspace(*cur_pos)) cur_pos++;
1876 if (!strncmp(cur_pos, "NIL", 3)) {
1879 } else if (*cur_pos == '\"') {
1882 p = get_quoted(cur_pos, '\"', dest, dest_len);
1883 cur_pos = p ? p : cur_pos + 2;
1884 } else if (*cur_pos == '{') {
1889 cur_pos = strchr_cpy(cur_pos + 1, '}', buf, sizeof(buf));
1891 g_return_val_if_fail(len > 0, cur_pos);
1893 g_string_truncate(str, 0);
1897 if ((nextline = sock_getline(sock)) == NULL)
1899 line_len += strlen(nextline);
1900 g_string_append(str, nextline);
1902 strretchomp(nextline);
1903 /* log_print("IMAP4< %s\n", nextline); */
1904 debug_print("IMAP4< %s\n", nextline);
1906 } while (line_len < len);
1908 memcpy(dest, cur_pos, MIN(len, dest_len - 1));
1909 dest[MIN(len, dest_len - 1)] = '\0';
1916 static gchar *imap_get_header(SockInfo *sock, gchar *cur_pos, gchar **headers,
1926 g_return_val_if_fail(str != NULL, cur_pos);
1928 while (isspace(*cur_pos)) cur_pos++;
1930 g_return_val_if_fail(*cur_pos == '{', cur_pos);
1932 cur_pos = strchr_cpy(cur_pos + 1, '}', buf, sizeof(buf));
1934 g_return_val_if_fail(len > 0, cur_pos);
1936 g_string_truncate(str, 0);
1940 if ((nextline = sock_getline(sock)) == NULL)
1942 block_len += strlen(nextline);
1943 g_string_append(str, nextline);
1945 strretchomp(nextline);
1946 /* debug_print("IMAP4< %s\n", nextline); */
1948 } while (block_len < len);
1950 debug_print("IMAP4< [contents of BODY.PEEK[HEADER.FIELDS (...)]]\n");
1952 *headers = g_strndup(cur_pos, len);
1955 while (isspace(*cur_pos)) cur_pos++;
1956 while (*cur_pos == '\0') {
1957 if ((nextline = sock_getline(sock)) == NULL)
1959 g_string_assign(str, nextline);
1961 strretchomp(nextline);
1962 debug_print("IMAP4< %s\n", nextline);
1965 while (isspace(*cur_pos)) cur_pos++;
1971 static MsgFlags imap_parse_flags(const gchar *flag_str)
1973 const gchar *p = flag_str;
1974 MsgFlags flags = {0, 0};
1976 flags.perm_flags = MSG_UNREAD;
1978 while ((p = strchr(p, '\\')) != NULL) {
1981 if (g_strncasecmp(p, "Recent", 6) == 0 && MSG_IS_UNREAD(flags)) {
1982 MSG_SET_PERM_FLAGS(flags, MSG_NEW);
1983 } else if (g_strncasecmp(p, "Seen", 4) == 0) {
1984 MSG_UNSET_PERM_FLAGS(flags, MSG_NEW|MSG_UNREAD);
1985 } else if (g_strncasecmp(p, "Deleted", 7) == 0) {
1986 MSG_SET_PERM_FLAGS(flags, MSG_DELETED);
1987 } else if (g_strncasecmp(p, "Flagged", 7) == 0) {
1988 MSG_SET_PERM_FLAGS(flags, MSG_MARKED);
1989 } else if (g_strncasecmp(p, "Answered", 8) == 0) {
1990 MSG_SET_PERM_FLAGS(flags, MSG_REPLIED);
1997 static MsgInfo *imap_parse_envelope(SockInfo *sock, FolderItem *item,
2000 gchar buf[IMAPBUFSIZE];
2001 MsgInfo *msginfo = NULL;
2006 MsgFlags flags = {0, 0}, imap_flags = {0, 0};
2008 g_return_val_if_fail(line_str != NULL, NULL);
2009 g_return_val_if_fail(line_str->str[0] == '*' &&
2010 line_str->str[1] == ' ', NULL);
2012 MSG_SET_TMP_FLAGS(flags, MSG_IMAP);
2013 if (item->stype == F_QUEUE) {
2014 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
2015 } else if (item->stype == F_DRAFT) {
2016 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
2019 cur_pos = line_str->str + 2;
2021 #define PARSE_ONE_ELEMENT(ch) \
2023 cur_pos = strchr_cpy(cur_pos, ch, buf, sizeof(buf)); \
2024 if (cur_pos == NULL) { \
2025 g_warning("cur_pos == NULL\n"); \
2026 procmsg_msginfo_free(msginfo); \
2031 PARSE_ONE_ELEMENT(' ');
2034 PARSE_ONE_ELEMENT(' ');
2035 g_return_val_if_fail(!strcmp(buf, "FETCH"), NULL);
2037 g_return_val_if_fail(*cur_pos == '(', NULL);
2040 while (*cur_pos != '\0' && *cur_pos != ')') {
2041 while (*cur_pos == ' ') cur_pos++;
2043 if (!strncmp(cur_pos, "UID ", 4)) {
2045 uid = strtoul(cur_pos, &cur_pos, 10);
2046 } else if (!strncmp(cur_pos, "FLAGS ", 6)) {
2048 if (*cur_pos != '(') {
2049 g_warning("*cur_pos != '('\n");
2050 procmsg_msginfo_free(msginfo);
2054 PARSE_ONE_ELEMENT(')');
2055 imap_flags = imap_parse_flags(buf);
2056 } else if (!strncmp(cur_pos, "RFC822.SIZE ", 12)) {
2058 size = strtol(cur_pos, &cur_pos, 10);
2059 } else if (!strncmp(cur_pos, "BODY[HEADER.FIELDS ", 19)) {
2063 if (*cur_pos != '(') {
2064 g_warning("*cur_pos != '('\n");
2065 procmsg_msginfo_free(msginfo);
2069 PARSE_ONE_ELEMENT(')');
2070 if (*cur_pos != ']') {
2071 g_warning("*cur_pos != ']'\n");
2072 procmsg_msginfo_free(msginfo);
2077 cur_pos = imap_get_header(sock, cur_pos, &headers,
2079 msginfo = procheader_parse_str(headers, flags, FALSE, FALSE);
2082 g_warning("invalid FETCH response: %s\n", cur_pos);
2088 msginfo->msgnum = uid;
2089 msginfo->size = size;
2090 msginfo->flags.tmp_flags |= imap_flags.tmp_flags;
2091 msginfo->flags.perm_flags = imap_flags.perm_flags;
2097 static gint imap_set_message_flags(IMAPSession *session,
2098 MsgNumberList *numlist,
2106 buf = g_string_new(is_set ? "+FLAGS (" : "-FLAGS (");
2108 if (IMAP_IS_SEEN(flags)) g_string_append(buf, "\\Seen ");
2109 if (IMAP_IS_ANSWERED(flags)) g_string_append(buf, "\\Answered ");
2110 if (IMAP_IS_FLAGGED(flags)) g_string_append(buf, "\\Flagged ");
2111 if (IMAP_IS_DELETED(flags)) g_string_append(buf, "\\Deleted ");
2112 if (IMAP_IS_DRAFT(flags)) g_string_append(buf, "\\Draft");
2114 if (buf->str[buf->len - 1] == ' ')
2115 g_string_truncate(buf, buf->len - 1);
2117 g_string_append_c(buf, ')');
2119 imapset = numberlist_to_imapset(numlist);
2120 while (imapset != NULL) {
2121 ok = imap_cmd_store(session, imapset,
2123 imapset = numberlist_to_imapset(NULL);
2125 g_string_free(buf, TRUE);
2130 static gint imap_select(IMAPSession *session, IMAPFolder *folder,
2132 gint *exists, gint *recent, gint *unseen,
2133 guint32 *uid_validity)
2137 gint exists_, recent_, unseen_, uid_validity_;
2139 if (!exists || !recent || !unseen || !uid_validity) {
2140 if (session->mbox && strcmp(session->mbox, path) == 0)
2141 return IMAP_SUCCESS;
2145 uid_validity = &uid_validity_;
2148 g_free(session->mbox);
2149 session->mbox = NULL;
2151 real_path = imap_get_real_path(folder, path);
2152 ok = imap_cmd_select(session, real_path,
2153 exists, recent, unseen, uid_validity);
2154 if (ok != IMAP_SUCCESS)
2155 log_warning(_("can't select folder: %s\n"), real_path);
2157 session->mbox = g_strdup(path);
2158 session->folder_content_changed = FALSE;
2165 #define THROW(err) { ok = err; goto catch; }
2167 static gint imap_status(IMAPSession *session, IMAPFolder *folder,
2169 gint *messages, gint *recent,
2170 guint32 *uid_next, guint32 *uid_validity,
2179 *messages = *recent = *uid_next = *uid_validity = *unseen = 0;
2181 argbuf = g_ptr_array_new();
2183 real_path = imap_get_real_path(folder, path);
2184 QUOTE_IF_REQUIRED(real_path_, real_path);
2185 imap_gen_send(session, "STATUS %s "
2186 "(MESSAGES RECENT UIDNEXT UIDVALIDITY UNSEEN)",
2189 ok = imap_cmd_ok(session, argbuf);
2190 if (ok != IMAP_SUCCESS) THROW(ok);
2192 str = search_array_str(argbuf, "STATUS");
2193 if (!str) THROW(IMAP_ERROR);
2195 str = strchr(str, '(');
2196 if (!str) THROW(IMAP_ERROR);
2198 while (*str != '\0' && *str != ')') {
2199 while (*str == ' ') str++;
2201 if (!strncmp(str, "MESSAGES ", 9)) {
2203 *messages = strtol(str, &str, 10);
2204 } else if (!strncmp(str, "RECENT ", 7)) {
2206 *recent = strtol(str, &str, 10);
2207 } else if (!strncmp(str, "UIDNEXT ", 8)) {
2209 *uid_next = strtoul(str, &str, 10);
2210 } else if (!strncmp(str, "UIDVALIDITY ", 12)) {
2212 *uid_validity = strtoul(str, &str, 10);
2213 } else if (!strncmp(str, "UNSEEN ", 7)) {
2215 *unseen = strtol(str, &str, 10);
2217 g_warning("invalid STATUS response: %s\n", str);
2224 ptr_array_free_strings(argbuf);
2225 g_ptr_array_free(argbuf, TRUE);
2233 /* low-level IMAP4rev1 commands */
2235 static gint imap_cmd_login(IMAPSession *session,
2236 const gchar *user, const gchar *pass)
2238 gchar *user_, *pass_;
2241 QUOTE_IF_REQUIRED(user_, user);
2242 QUOTE_IF_REQUIRED(pass_, pass);
2243 imap_gen_send(session, "LOGIN %s %s", user_, pass_);
2245 ok = imap_cmd_ok(session, NULL);
2246 if (ok != IMAP_SUCCESS)
2247 log_warning(_("IMAP4 login failed.\n"));
2252 static gint imap_cmd_logout(IMAPSession *session)
2254 imap_gen_send(session, "LOGOUT");
2255 return imap_cmd_ok(session, NULL);
2258 /* Send CAPABILITY, and examine the server's response to see whether this
2259 * connection is pre-authenticated or not and build a list of CAPABILITIES. */
2260 static gint imap_greeting(IMAPSession *session)
2265 imap_gen_send(session, "CAPABILITY");
2267 argbuf = g_ptr_array_new();
2269 if (imap_cmd_ok(session, argbuf) != IMAP_SUCCESS ||
2270 ((capstr = search_array_str(argbuf, "CAPABILITY ")) == NULL)) {
2271 ptr_array_free_strings(argbuf);
2272 g_ptr_array_free(argbuf, TRUE);
2276 session->authenticated = search_array_str(argbuf, "PREAUTH") != NULL;
2278 capstr += strlen("CAPABILITY ");
2280 IMAP_SESSION(session)->capability = g_strsplit(capstr, " ", 0);
2282 ptr_array_free_strings(argbuf);
2283 g_ptr_array_free(argbuf, TRUE);
2288 static gboolean imap_has_capability(IMAPSession *session, const gchar *cap)
2292 for (p = session->capability; *p != NULL; ++p)
2293 if (g_strcasecmp(*p, cap) == 0)
2299 void imap_free_capabilities(IMAPSession *session)
2301 g_strfreev(session->capability);
2302 session->capability = NULL;
2305 static const IMAPSet numberlist_to_imapset(MsgNumberList *list)
2307 static GString *imapset = NULL;
2308 static MsgNumberList *numlist, *elem;
2309 guint first, last, next;
2311 if (imapset == NULL)
2312 imapset = g_string_sized_new(256);
2314 g_string_truncate(imapset, 0);
2317 g_slist_free(numlist);
2318 numlist = g_slist_copy(list);
2319 numlist = g_slist_sort(numlist, g_int_compare);
2320 } else if (numlist == NULL) {
2324 first = GPOINTER_TO_INT(numlist->data);
2326 for(elem = g_slist_next(numlist); elem != NULL; elem = g_slist_next(elem)) {
2327 next = GPOINTER_TO_INT(elem->data);
2329 if(next != (last + 1)) {
2330 if (imapset->len > 0)
2331 g_string_append(imapset, ",");
2333 g_string_sprintfa(imapset, "%d", first);
2335 g_string_sprintfa(imapset, "%d:%d", first, last);
2337 if (imapset->len > IMAPCMDLIMIT) {
2347 if (imapset->len > 0)
2348 g_string_append(imapset, ",");
2350 g_string_sprintfa(imapset, "%d", first);
2352 g_string_sprintfa(imapset, "%d:%d", first, last);
2354 g_slist_free(numlist);
2357 MsgNumberList *remaining;
2359 remaining = elem->next;
2360 remaining = g_slist_prepend(remaining, elem->data);
2362 g_slist_free(numlist);
2363 numlist = remaining;
2366 return imapset->str;
2369 static gint imap_cmd_noop(IMAPSession *session)
2371 imap_gen_send(session, "NOOP");
2372 return imap_cmd_ok(session, NULL);
2375 static gint imap_cmd_starttls(IMAPSession *session)
2377 imap_gen_send(session, "STARTTLS");
2378 return imap_cmd_ok(session, NULL);
2381 #define THROW(err) { ok = err; goto catch; }
2383 static gint imap_cmd_namespace(IMAPSession *session, gchar **ns_str)
2389 argbuf = g_ptr_array_new();
2391 imap_gen_send(session, "NAMESPACE");
2392 if ((ok = imap_cmd_ok(session, argbuf)) != IMAP_SUCCESS) THROW(ok);
2394 str = search_array_str(argbuf, "NAMESPACE");
2395 if (!str) THROW(IMAP_ERROR);
2397 *ns_str = g_strdup(str);
2400 ptr_array_free_strings(argbuf);
2401 g_ptr_array_free(argbuf, TRUE);
2408 static gint imap_cmd_list(IMAPSession *session, const gchar *ref,
2409 const gchar *mailbox, GPtrArray *argbuf)
2411 gchar *ref_, *mailbox_;
2413 if (!ref) ref = "\"\"";
2414 if (!mailbox) mailbox = "\"\"";
2416 QUOTE_IF_REQUIRED(ref_, ref);
2417 QUOTE_IF_REQUIRED(mailbox_, mailbox);
2418 imap_gen_send(session, "LIST %s %s", ref_, mailbox_);
2420 return imap_cmd_ok(session, argbuf);
2423 #define THROW goto catch
2425 static gint imap_cmd_do_select(IMAPSession *session, const gchar *folder,
2427 gint *exists, gint *recent, gint *unseen,
2428 guint32 *uid_validity)
2436 *exists = *recent = *unseen = *uid_validity = 0;
2437 argbuf = g_ptr_array_new();
2440 select_cmd = "EXAMINE";
2442 select_cmd = "SELECT";
2444 QUOTE_IF_REQUIRED(folder_, folder);
2445 imap_gen_send(session, "%s %s", select_cmd, folder_);
2447 if ((ok = imap_cmd_ok(session, argbuf)) != IMAP_SUCCESS) THROW;
2449 resp_str = search_array_contain_str(argbuf, "EXISTS");
2451 if (sscanf(resp_str,"%d EXISTS", exists) != 1) {
2452 g_warning("imap_cmd_select(): invalid EXISTS line.\n");
2457 resp_str = search_array_contain_str(argbuf, "RECENT");
2459 if (sscanf(resp_str, "%d RECENT", recent) != 1) {
2460 g_warning("imap_cmd_select(): invalid RECENT line.\n");
2465 resp_str = search_array_contain_str(argbuf, "UIDVALIDITY");
2467 if (sscanf(resp_str, "OK [UIDVALIDITY %u] ", uid_validity)
2469 g_warning("imap_cmd_select(): invalid UIDVALIDITY line.\n");
2474 resp_str = search_array_contain_str(argbuf, "UNSEEN");
2476 if (sscanf(resp_str, "OK [UNSEEN %d] ", unseen) != 1) {
2477 g_warning("imap_cmd_select(): invalid UNSEEN line.\n");
2483 ptr_array_free_strings(argbuf);
2484 g_ptr_array_free(argbuf, TRUE);
2489 static gint imap_cmd_select(IMAPSession *session, const gchar *folder,
2490 gint *exists, gint *recent, gint *unseen,
2491 guint32 *uid_validity)
2493 return imap_cmd_do_select(session, folder, FALSE,
2494 exists, recent, unseen, uid_validity);
2497 static gint imap_cmd_examine(IMAPSession *session, const gchar *folder,
2498 gint *exists, gint *recent, gint *unseen,
2499 guint32 *uid_validity)
2501 return imap_cmd_do_select(session, folder, TRUE,
2502 exists, recent, unseen, uid_validity);
2507 static gint imap_cmd_create(IMAPSession *session, const gchar *folder)
2511 QUOTE_IF_REQUIRED(folder_, folder);
2512 imap_gen_send(session, "CREATE %s", folder_);
2514 return imap_cmd_ok(session, NULL);
2517 static gint imap_cmd_rename(IMAPSession *session, const gchar *old_folder,
2518 const gchar *new_folder)
2520 gchar *old_folder_, *new_folder_;
2522 QUOTE_IF_REQUIRED(old_folder_, old_folder);
2523 QUOTE_IF_REQUIRED(new_folder_, new_folder);
2524 imap_gen_send(session, "RENAME %s %s", old_folder_, new_folder_);
2526 return imap_cmd_ok(session, NULL);
2529 static gint imap_cmd_delete(IMAPSession *session, const gchar *folder)
2533 QUOTE_IF_REQUIRED(folder_, folder);
2534 imap_gen_send(session, "DELETE %s", folder_);
2536 return imap_cmd_ok(session, NULL);
2539 static gint imap_cmd_search(IMAPSession *session, const gchar *criteria, GSList **list)
2545 g_return_val_if_fail(criteria != NULL, IMAP_ERROR);
2546 g_return_val_if_fail(list != NULL, IMAP_ERROR);
2550 argbuf = g_ptr_array_new();
2551 imap_gen_send(session, "UID SEARCH %s", criteria);
2553 ok = imap_cmd_ok(session, argbuf);
2554 if (ok != IMAP_SUCCESS) {
2555 ptr_array_free_strings(argbuf);
2556 g_ptr_array_free(argbuf, TRUE);
2560 if ((uidlist = search_array_str(argbuf, "SEARCH ")) != NULL) {
2561 gchar **strlist, **p;
2563 strlist = g_strsplit(uidlist + 7, " ", 0);
2564 for (p = strlist; *p != NULL; ++p) {
2567 if (sscanf(*p, "%d", &msgnum) == 1)
2568 *list = g_slist_append(*list, GINT_TO_POINTER(msgnum));
2570 g_strfreev(strlist);
2572 ptr_array_free_strings(argbuf);
2573 g_ptr_array_free(argbuf, TRUE);
2575 return IMAP_SUCCESS;
2578 static gint imap_cmd_fetch(IMAPSession *session, guint32 uid, const gchar *filename)
2586 g_return_val_if_fail(filename != NULL, IMAP_ERROR);
2588 imap_gen_send(session, "UID FETCH %d BODY.PEEK[]", uid);
2590 while ((ok = imap_gen_recv(session, &buf))
2592 if (buf[0] != '*' || buf[1] != ' ') {
2596 if (strstr(buf, "FETCH") != NULL)
2600 if (ok != IMAP_SUCCESS) {
2605 cur_pos = strchr(buf, '{');
2606 if (cur_pos == NULL) {
2611 cur_pos = strchr_cpy(cur_pos + 1, '}', size_str, sizeof(size_str));
2612 if (cur_pos == NULL) {
2616 size_num = atol(size_str);
2617 g_return_val_if_fail(size_num > 0, IMAP_ERROR);
2619 if (*cur_pos != '\0') {
2624 if (recv_bytes_write_to_file(SESSION(session)->sock, size_num, filename) != 0) {
2629 if (imap_gen_recv(session, &buf) != IMAP_SUCCESS) {
2634 if (buf[0] == '\0' || buf[strlen(buf) - 1] != ')') {
2640 ok = imap_cmd_ok(session, NULL);
2645 static gint imap_cmd_append(IMAPSession *session, const gchar *destfolder,
2646 const gchar *file, gint32 *new_uid)
2651 gchar buf[BUFFSIZE], *imapbuf;
2656 g_return_val_if_fail(file != NULL, IMAP_ERROR);
2658 size = get_file_size_as_crlf(file);
2659 if ((fp = fopen(file, "rb")) == NULL) {
2660 FILE_OP_ERROR(file, "fopen");
2663 QUOTE_IF_REQUIRED(destfolder_, destfolder);
2664 imap_gen_send(session, "APPEND %s (\\Seen) {%d}", destfolder_, size);
2666 ok = imap_gen_recv(session, &imapbuf);
2667 if (ok != IMAP_SUCCESS || imapbuf[0] != '+' || imapbuf[1] != ' ') {
2668 log_warning(_("can't append %s to %s\n"), file, destfolder_);
2675 log_print("IMAP4> %s\n", _("(sending file...)"));
2677 while (fgets(buf, sizeof(buf), fp) != NULL) {
2679 if (sock_puts(SESSION(session)->sock, buf) < 0) {
2686 FILE_OP_ERROR(file, "fgets");
2691 sock_puts(SESSION(session)->sock, "");
2695 reply = g_ptr_array_new();
2698 ok = imap_cmd_ok(session, reply);
2699 if (ok != IMAP_SUCCESS)
2700 log_warning(_("can't append message to %s\n"), destfolder_);
2702 (new_uid != NULL) &&
2703 (imap_has_capability(session, "UIDPLUS") && reply->len > 0) &&
2704 ((okmsginfo = g_ptr_array_index(reply, reply->len - 1)) != NULL) &&
2705 (sscanf(okmsginfo, "%*u OK [APPENDUID %*u %u]", &newuid) == 1)) {
2709 ptr_array_free_strings(reply);
2710 g_ptr_array_free(reply, TRUE);
2715 static gint imap_cmd_copy(IMAPSession * session,
2717 const gchar * destfolder, gint32 * new_uid)
2720 gint32 olduid, newuid;
2725 g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
2726 g_return_val_if_fail(session != NULL, IMAP_ERROR);
2727 g_return_val_if_fail(new_uid != NULL, IMAP_ERROR);
2729 QUOTE_IF_REQUIRED(destfolder_, destfolder);
2730 imap_gen_send(session, "UID COPY %d %s", msgnum, destfolder_);
2732 reply = g_ptr_array_new();
2735 ok = imap_cmd_ok(session, reply);
2736 if (ok != IMAP_SUCCESS)
2737 log_warning(_("can't copy %d to %s\n"), msgnum, destfolder_);
2738 else if (imap_has_capability(session, "UIDPLUS") && reply->len > 0)
2739 if ((okmsginfo = g_ptr_array_index(reply, reply->len - 1)) != NULL &&
2740 sscanf(okmsginfo, "%*u OK [COPYUID %*u %u %u]", &olduid, &newuid) == 2 &&
2744 ptr_array_free_strings(reply);
2745 g_ptr_array_free(reply, TRUE);
2749 gint imap_cmd_envelope(IMAPSession *session, IMAPSet set)
2751 static GString *header_fields = NULL;
2753 if (header_fields == NULL) {
2754 const HeaderEntry *headers, *elem;
2756 headers = procheader_get_headernames(FALSE);
2757 header_fields = g_string_new("");
2759 for (elem = headers; elem->name != NULL; ++elem) {
2760 gint namelen = strlen(elem->name);
2762 /* Header fields ending with space are not rfc822 headers */
2763 if (elem->name[namelen - 1] == ' ')
2766 /* strip : at the of header field */
2767 if(elem->name[namelen - 1] == ':')
2773 g_string_sprintfa(header_fields, "%s%.*s",
2774 header_fields->str[0] != '\0' ? " " : "",
2775 namelen, elem->name);
2780 (session, "UID FETCH %s (UID FLAGS RFC822.SIZE BODY.PEEK[HEADER.FIELDS (%s)])",
2781 set, header_fields->str);
2783 return IMAP_SUCCESS;
2786 static gint imap_cmd_store(IMAPSession *session, IMAPSet set,
2791 imap_gen_send(session, "UID STORE %s %s",
2794 if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS) {
2795 log_warning(_("error while imap command: STORE %s %s\n"),
2800 return IMAP_SUCCESS;
2803 static gint imap_cmd_expunge(IMAPSession *session)
2807 imap_gen_send(session, "EXPUNGE");
2808 if ((ok = imap_cmd_ok(session, NULL)) != IMAP_SUCCESS) {
2809 log_warning(_("error while imap command: EXPUNGE\n"));
2813 return IMAP_SUCCESS;
2816 static gint imap_cmd_ok(IMAPSession *session, GPtrArray *argbuf)
2818 gint ok = IMAP_SUCCESS;
2823 while ((ok = imap_gen_recv(session, &buf))
2825 // make sure data is long enough for any substring of buf
2826 data = alloca(strlen(buf) + 1);
2828 // untagged line read
2829 if (buf[0] == '*' && buf[1] == ' ') {
2832 g_ptr_array_add(argbuf, g_strdup(buf + 2));
2834 if (sscanf(buf + 2, "%d %s", &num, data) >= 2) {
2835 if (!strcmp(data, "EXISTS")) {
2836 session->exists = num;
2837 session->folder_content_changed = TRUE;
2840 if(!strcmp(data, "EXPUNGE")) {
2842 session->folder_content_changed = TRUE;
2845 // tagged line with correct tag and OK response found
2846 } else if ((sscanf(buf, "%d %s", &cmd_num, data) >= 2) &&
2847 (cmd_num == session->cmd_count) &&
2848 !strcmp(data, "OK")) {
2850 g_ptr_array_add(argbuf, g_strdup(buf));
2864 static void imap_gen_send(IMAPSession *session, const gchar *format, ...)
2871 va_start(args, format);
2872 tmp = g_strdup_vprintf(format, args);
2875 session->cmd_count++;
2877 buf = g_strdup_printf("%d %s\r\n", session->cmd_count, tmp);
2878 if (!strncasecmp(tmp, "LOGIN ", 6) && (p = strchr(tmp + 6, ' '))) {
2880 log_print("IMAP4> %d %s ********\n", session->cmd_count, tmp);
2882 log_print("IMAP4> %d %s\n", session->cmd_count, tmp);
2884 sock_write_all(SESSION(session)->sock, buf, strlen(buf));
2889 static gint imap_gen_recv(IMAPSession *session, gchar **buf)
2891 if ((*buf = sock_getline(SESSION(session)->sock)) == NULL)
2896 log_print("IMAP4< %s\n", *buf);
2898 return IMAP_SUCCESS;
2902 /* misc utility functions */
2904 static gchar *strchr_cpy(const gchar *src, gchar ch, gchar *dest, gint len)
2909 tmp = strchr(src, ch);
2913 memcpy(dest, src, MIN(tmp - src, len - 1));
2914 dest[MIN(tmp - src, len - 1)] = '\0';
2919 static gchar *get_quoted(const gchar *src, gchar ch, gchar *dest, gint len)
2921 const gchar *p = src;
2924 g_return_val_if_fail(*p == ch, NULL);
2929 while (*p != '\0' && *p != ch) {
2931 if (*p == '\\' && *(p + 1) != '\0')
2940 return (gchar *)(*p == ch ? p + 1 : p);
2943 static gchar *search_array_contain_str(GPtrArray *array, const gchar *str)
2947 for (i = 0; i < array->len; i++) {
2950 tmp = g_ptr_array_index(array, i);
2951 if (strstr(tmp, str) != NULL)
2958 static gchar *search_array_str(GPtrArray *array, const gchar *str)
2965 for (i = 0; i < array->len; i++) {
2968 tmp = g_ptr_array_index(array, i);
2969 if (!strncmp(tmp, str, len))
2976 static void imap_path_separator_subst(gchar *str, gchar separator)
2979 gboolean in_escape = FALSE;
2981 if (!separator || separator == '/') return;
2983 for (p = str; *p != '\0'; p++) {
2984 if (*p == '/' && !in_escape)
2986 else if (*p == '&' && *(p + 1) != '-' && !in_escape)
2988 else if (*p == '-' && in_escape)
2993 static gchar *imap_modified_utf7_to_locale(const gchar *mutf7_str)
2996 const gchar *from_p;
2999 to = g_malloc(strlen(mutf7_str) + 1);
3002 for (from_p = mutf7_str; *from_p != '\0'; from_p++) {
3003 if (*from_p == '&' && *(from_p + 1) == '-') {
3013 static iconv_t cd = (iconv_t)-1;
3014 static gboolean iconv_ok = TRUE;
3017 size_t norm_utf7_len;
3019 gchar *to_str, *to_p;
3021 gboolean in_escape = FALSE;
3023 if (!iconv_ok) return g_strdup(mutf7_str);
3025 if (cd == (iconv_t)-1) {
3026 cd = iconv_open(conv_get_current_charset_str(), "UTF-7");
3027 if (cd == (iconv_t)-1) {
3028 g_warning("iconv cannot convert UTF-7 to %s\n",
3029 conv_get_current_charset_str());
3031 return g_strdup(mutf7_str);
3035 norm_utf7 = g_string_new(NULL);
3037 for (p = mutf7_str; *p != '\0'; p++) {
3038 /* replace: '&' -> '+',
3040 escaped ',' -> '/' */
3041 if (!in_escape && *p == '&') {
3042 if (*(p + 1) != '-') {
3043 g_string_append_c(norm_utf7, '+');
3046 g_string_append_c(norm_utf7, '&');
3049 } else if (in_escape && *p == ',') {
3050 g_string_append_c(norm_utf7, '/');
3051 } else if (in_escape && *p == '-') {
3052 g_string_append_c(norm_utf7, '-');
3055 g_string_append_c(norm_utf7, *p);
3059 norm_utf7_p = norm_utf7->str;
3060 norm_utf7_len = norm_utf7->len;
3061 to_len = strlen(mutf7_str) * 5;
3062 to_p = to_str = g_malloc(to_len + 1);
3064 if (iconv(cd, (ICONV_CONST gchar **)&norm_utf7_p, &norm_utf7_len,
3065 &to_p, &to_len) == -1) {
3066 g_warning(_("iconv cannot convert UTF-7 to %s\n"),
3067 conv_get_current_charset_str());
3068 g_string_free(norm_utf7, TRUE);
3070 return g_strdup(mutf7_str);
3073 /* second iconv() call for flushing */
3074 iconv(cd, NULL, NULL, &to_p, &to_len);
3075 g_string_free(norm_utf7, TRUE);
3079 #endif /* !HAVE_ICONV */
3082 static gchar *imap_locale_to_modified_utf7(const gchar *from)
3085 const gchar *from_p;
3088 to = g_malloc(strlen(from) * 2 + 1);
3091 for (from_p = from; *from_p != '\0'; from_p++) {
3092 if (*from_p == '&') {
3102 static iconv_t cd = (iconv_t)-1;
3103 static gboolean iconv_ok = TRUE;
3104 gchar *norm_utf7, *norm_utf7_p;
3105 size_t from_len, norm_utf7_len;
3107 gchar *from_tmp, *to, *p;
3108 gboolean in_escape = FALSE;
3110 if (!iconv_ok) return g_strdup(from);
3112 if (cd == (iconv_t)-1) {
3113 cd = iconv_open("UTF-7", conv_get_current_charset_str());
3114 if (cd == (iconv_t)-1) {
3115 g_warning("iconv cannot convert %s to UTF-7\n",
3116 conv_get_current_charset_str());
3118 return g_strdup(from);
3122 Xstrdup_a(from_tmp, from, return g_strdup(from));
3123 from_len = strlen(from);
3124 norm_utf7_len = from_len * 5;
3125 Xalloca(norm_utf7, norm_utf7_len + 1, return g_strdup(from));
3126 norm_utf7_p = norm_utf7;
3128 #define IS_PRINT(ch) (isprint(ch) && isascii(ch))
3130 while (from_len > 0) {
3131 if (*from_tmp == '+') {
3132 *norm_utf7_p++ = '+';
3133 *norm_utf7_p++ = '-';
3137 } else if (IS_PRINT(*from_tmp)) {
3138 /* printable ascii char */
3139 *norm_utf7_p = *from_tmp;
3145 size_t mb_len = 0, conv_len = 0;
3147 /* unprintable char: convert to UTF-7 */
3149 while (!IS_PRINT(*p) && conv_len < from_len) {
3150 mb_len = mblen(p, MB_LEN_MAX);
3152 g_warning("wrong multibyte sequence\n");
3153 return g_strdup(from);
3159 from_len -= conv_len;
3160 if (iconv(cd, (ICONV_CONST gchar **)&from_tmp,
3162 &norm_utf7_p, &norm_utf7_len) == -1) {
3163 g_warning("iconv cannot convert %s to UTF-7\n",
3164 conv_get_current_charset_str());
3165 return g_strdup(from);
3168 /* second iconv() call for flushing */
3169 iconv(cd, NULL, NULL, &norm_utf7_p, &norm_utf7_len);
3175 *norm_utf7_p = '\0';
3176 to_str = g_string_new(NULL);
3177 for (p = norm_utf7; p < norm_utf7_p; p++) {
3178 /* replace: '&' -> "&-",
3181 BASE64 '/' -> ',' */
3182 if (!in_escape && *p == '&') {
3183 g_string_append(to_str, "&-");
3184 } else if (!in_escape && *p == '+') {
3185 if (*(p + 1) == '-') {
3186 g_string_append_c(to_str, '+');
3189 g_string_append_c(to_str, '&');
3192 } else if (in_escape && *p == '/') {
3193 g_string_append_c(to_str, ',');
3194 } else if (in_escape && *p == '-') {
3195 g_string_append_c(to_str, '-');
3198 g_string_append_c(to_str, *p);
3204 g_string_append_c(to_str, '-');
3208 g_string_free(to_str, FALSE);
3211 #endif /* !HAVE_ICONV */
3214 static gboolean imap_rename_folder_func(GNode *node, gpointer data)
3216 FolderItem *item = node->data;
3217 gchar **paths = data;
3218 const gchar *oldpath = paths[0];
3219 const gchar *newpath = paths[1];
3221 gchar *new_itempath;
3224 oldpathlen = strlen(oldpath);
3225 if (strncmp(oldpath, item->path, oldpathlen) != 0) {
3226 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
3230 base = item->path + oldpathlen;
3231 while (*base == G_DIR_SEPARATOR) base++;
3233 new_itempath = g_strdup(newpath);
3235 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
3238 item->path = new_itempath;
3243 static gint get_list_of_uids(Folder *folder, IMAPFolderItem *item, GSList **msgnum_list)
3245 gint ok, nummsgs = 0, lastuid_old;
3246 IMAPSession *session;
3247 GSList *uidlist, *elem;
3250 session = imap_session_get(folder);
3251 g_return_val_if_fail(session != NULL, -1);
3253 ok = imap_select(session, IMAP_FOLDER(folder), item->item.path,
3254 NULL, NULL, NULL, NULL);
3255 if (ok != IMAP_SUCCESS)
3258 cmd_buf = g_strdup_printf("UID %d:*", item->lastuid + 1);
3259 ok = imap_cmd_search(session, cmd_buf, &uidlist);
3262 if (ok == IMAP_SOCKET) {
3263 session_destroy((Session *)session);
3264 ((RemoteFolder *)folder)->session = NULL;
3268 if (ok != IMAP_SUCCESS) {
3272 argbuf = g_ptr_array_new();
3274 cmd_buf = g_strdup_printf("UID FETCH %d:* (UID)", item->lastuid + 1);
3275 imap_gen_send(session, cmd_buf);
3277 ok = imap_cmd_ok(session, argbuf);
3278 if (ok != IMAP_SUCCESS) {
3279 ptr_array_free_strings(argbuf);
3280 g_ptr_array_free(argbuf, TRUE);
3284 for(i = 0; i < argbuf->len; i++) {
3287 if((ret = sscanf(g_ptr_array_index(argbuf, i),
3288 "%*d FETCH (UID %d)", &msgnum)) == 1)
3289 uidlist = g_slist_prepend(uidlist, GINT_TO_POINTER(msgnum));
3291 ptr_array_free_strings(argbuf);
3292 g_ptr_array_free(argbuf, TRUE);
3295 lastuid_old = item->lastuid;
3296 *msgnum_list = g_slist_copy(item->uid_list);
3297 nummsgs = g_slist_length(*msgnum_list);
3298 debug_print("Got %d uids from cache\n", g_slist_length(item->uid_list));
3300 for (elem = uidlist; elem != NULL; elem = g_slist_next(elem)) {
3303 msgnum = GPOINTER_TO_INT(elem->data);
3304 if (msgnum > lastuid_old) {
3305 *msgnum_list = g_slist_prepend(*msgnum_list, GINT_TO_POINTER(msgnum));
3306 item->uid_list = g_slist_prepend(item->uid_list, GINT_TO_POINTER(msgnum));
3309 if(msgnum > item->lastuid)
3310 item->lastuid = msgnum;
3313 g_slist_free(uidlist);
3318 gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list)
3320 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3321 IMAPSession *session;
3322 gint ok, nummsgs = 0, exists, recent, unseen, uid_val, uid_next;
3325 gboolean selected_folder;
3327 g_return_val_if_fail(folder != NULL, -1);
3328 g_return_val_if_fail(item != NULL, -1);
3329 g_return_val_if_fail(item->item.path != NULL, -1);
3330 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
3331 g_return_val_if_fail(folder->account != NULL, -1);
3333 session = imap_session_get(folder);
3334 g_return_val_if_fail(session != NULL, -1);
3336 selected_folder = (session->mbox != NULL) &&
3337 (!strcmp(session->mbox, item->item.path));
3338 if (selected_folder) {
3339 ok = imap_cmd_noop(session);
3340 if (ok != IMAP_SUCCESS)
3342 exists = session->exists;
3344 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
3345 &exists, &recent, &uid_next, &uid_val, &unseen);
3346 if (ok != IMAP_SUCCESS)
3350 /* If old uid_next matches new uid_next we can be sure no message
3351 was added to the folder */
3352 if (( selected_folder && !session->folder_content_changed) ||
3353 (!selected_folder && uid_next == item->uid_next)) {
3354 nummsgs = g_slist_length(item->uid_list);
3356 /* If number of messages is still the same we
3357 know our caches message numbers are still valid,
3358 otherwise if the number of messages has decrease
3359 we discard our cache to start a new scan to find
3360 out which numbers have been removed */
3361 if (exists == nummsgs) {
3362 *msgnum_list = g_slist_copy(item->uid_list);
3364 } else if (exists < nummsgs) {
3365 debug_print("Freeing imap uid cache");
3367 g_slist_free(item->uid_list);
3368 item->uid_list = NULL;
3371 if (!selected_folder)
3372 item->uid_next = uid_next;
3375 *msgnum_list = NULL;
3379 nummsgs = get_list_of_uids(folder, item, &uidlist);
3381 if (nummsgs != exists) {
3382 /* Cache contains more messages then folder, we have cached
3383 an old UID of a message that was removed and new messages
3384 have been added too, otherwise the uid_next check would
3386 debug_print("Freeing imap uid cache");
3388 g_slist_free(item->uid_list);
3389 item->uid_list = NULL;
3391 g_slist_free(*msgnum_list);
3393 nummsgs = get_list_of_uids(folder, item, &uidlist);
3396 *msgnum_list = uidlist;
3398 dir = folder_item_get_path((FolderItem *)item);
3399 debug_print("removing old messages from %s\n", dir);
3400 remove_numbered_files_not_in_list(dir, *msgnum_list);
3406 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item)
3411 flags.perm_flags = MSG_NEW|MSG_UNREAD;
3412 flags.tmp_flags = 0;
3414 g_return_val_if_fail(item != NULL, NULL);
3415 g_return_val_if_fail(file != NULL, NULL);
3417 if (item->stype == F_QUEUE) {
3418 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
3419 } else if (item->stype == F_DRAFT) {
3420 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
3423 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3424 if (!msginfo) return NULL;
3426 msginfo->folder = item;
3431 GSList *imap_get_msginfos(Folder *folder, FolderItem *item, GSList *msgnum_list)
3433 IMAPSession *session;
3434 MsgInfoList *ret = NULL;
3437 g_return_val_if_fail(folder != NULL, NULL);
3438 g_return_val_if_fail(item != NULL, NULL);
3439 g_return_val_if_fail(msgnum_list != NULL, NULL);
3441 session = imap_session_get(folder);
3442 g_return_val_if_fail(session != NULL, NULL);
3444 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
3445 NULL, NULL, NULL, NULL);
3446 if (ok != IMAP_SUCCESS)
3449 if (!(item->stype == F_QUEUE || item->stype == F_DRAFT)) {
3450 ret = g_slist_concat(ret,
3451 imap_get_uncached_messages(
3452 session, item, msgnum_list));
3454 MsgNumberList *sorted_list, *elem;
3455 gint startnum, lastnum;
3457 sorted_list = g_slist_sort(g_slist_copy(msgnum_list), g_int_compare);
3459 startnum = lastnum = GPOINTER_TO_INT(sorted_list->data);
3461 for (elem = sorted_list;; elem = g_slist_next(elem)) {
3465 num = GPOINTER_TO_INT(elem->data);
3467 if (num > lastnum + 1 || elem == NULL) {
3469 for (i = startnum; i <= lastnum; ++i) {
3472 file = imap_fetch_msg(folder, item, i);
3474 MsgInfo *msginfo = imap_parse_msg(file, item);
3475 if (msginfo != NULL) {
3476 msginfo->msgnum = i;
3477 ret = g_slist_append(ret, msginfo);
3491 g_slist_free(sorted_list);
3497 MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid)
3499 MsgInfo *msginfo = NULL;
3500 MsgInfoList *msginfolist;
3501 MsgNumberList numlist;
3503 numlist.next = NULL;
3504 numlist.data = GINT_TO_POINTER(uid);
3506 msginfolist = imap_get_msginfos(folder, item, &numlist);
3507 if (msginfolist != NULL) {
3508 msginfo = msginfolist->data;
3509 g_slist_free(msginfolist);
3515 gboolean imap_check_msgnum_validity(Folder *folder, FolderItem *_item)
3517 IMAPSession *session;
3518 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3519 gint ok, exists = 0, recent = 0, unseen = 0;
3520 guint32 uid_next, uid_validity = 0;
3522 g_return_val_if_fail(folder != NULL, FALSE);
3523 g_return_val_if_fail(item != NULL, FALSE);
3524 g_return_val_if_fail(item->item.folder != NULL, FALSE);
3525 g_return_val_if_fail(FOLDER_CLASS(item->item.folder) == &imap_class, FALSE);
3527 session = imap_session_get(folder);
3528 g_return_val_if_fail(session != NULL, FALSE);
3530 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
3531 &exists, &recent, &uid_next, &uid_validity, &unseen);
3532 if (ok != IMAP_SUCCESS)
3535 if(item->item.mtime == uid_validity)
3538 debug_print("Freeing imap uid cache\n");
3540 g_slist_free(item->uid_list);
3541 item->uid_list = NULL;
3543 item->item.mtime = uid_validity;
3545 imap_delete_all_cached_messages((FolderItem *)item);
3550 void imap_change_flags(Folder *folder, FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
3552 IMAPSession *session;
3553 IMAPFlags flags_set = 0, flags_unset = 0;
3554 gint ok = IMAP_SUCCESS;
3555 MsgNumberList numlist;
3557 g_return_if_fail(folder != NULL);
3558 g_return_if_fail(folder->klass == &imap_class);
3559 g_return_if_fail(item != NULL);
3560 g_return_if_fail(item->folder == folder);
3561 g_return_if_fail(msginfo != NULL);
3562 g_return_if_fail(msginfo->folder == item);
3564 session = imap_session_get(folder);
3565 if (!session) return;
3567 if ((ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
3568 NULL, NULL, NULL, NULL)) != IMAP_SUCCESS)
3571 if (!MSG_IS_MARKED(msginfo->flags) && (newflags & MSG_MARKED))
3572 flags_set |= IMAP_FLAG_FLAGGED;
3573 if ( MSG_IS_MARKED(msginfo->flags) && !(newflags & MSG_MARKED))
3574 flags_unset |= IMAP_FLAG_FLAGGED;
3576 if (!MSG_IS_UNREAD(msginfo->flags) && (newflags & MSG_UNREAD))
3577 flags_unset |= IMAP_FLAG_SEEN;
3578 if ( MSG_IS_UNREAD(msginfo->flags) && !(newflags & MSG_UNREAD))
3579 flags_set |= IMAP_FLAG_SEEN;
3581 if (!MSG_IS_REPLIED(msginfo->flags) && (newflags & MSG_REPLIED))
3582 flags_set |= IMAP_FLAG_ANSWERED;
3583 if ( MSG_IS_REPLIED(msginfo->flags) && !(newflags & MSG_REPLIED))
3584 flags_set |= IMAP_FLAG_ANSWERED;
3586 numlist.next = NULL;
3587 numlist.data = GINT_TO_POINTER(msginfo->msgnum);
3590 ok = imap_set_message_flags(session, &numlist, flags_set, TRUE);
3591 if (ok != IMAP_SUCCESS) return;
3595 ok = imap_set_message_flags(session, &numlist, flags_unset, FALSE);
3596 if (ok != IMAP_SUCCESS) return;
3599 msginfo->flags.perm_flags = newflags;