]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
imapc: Write large message bodies to temp files rather than keeping in memory.
authorTimo Sirainen <tss@iki.fi>
Mon, 31 Jan 2011 02:02:04 +0000 (04:02 +0200)
committerTimo Sirainen <tss@iki.fi>
Mon, 31 Jan 2011 02:02:04 +0000 (04:02 +0200)
src/lib-storage/index/imapc/imapc-client.c
src/lib-storage/index/imapc/imapc-client.h
src/lib-storage/index/imapc/imapc-connection.c
src/lib-storage/index/imapc/imapc-mail.c
src/lib-storage/index/imapc/imapc-mail.h
src/lib-storage/index/imapc/imapc-mailbox.c
src/lib-storage/index/imapc/imapc-save.c
src/lib-storage/index/imapc/imapc-search.c
src/lib-storage/index/imapc/imapc-storage.c
src/lib-storage/index/imapc/imapc-storage.h

index 4871686595acbc1673ca849ef386909abf4ea53c..325fd440f0aec775c121e593215f029880ce1dfe 100644 (file)
@@ -2,11 +2,15 @@
 
 #include "lib.h"
 #include "array.h"
+#include "str.h"
 #include "ioloop.h"
+#include "safe-mkstemp.h"
 #include "imapc-seqmap.h"
 #include "imapc-connection.h"
 #include "imapc-client-private.h"
 
+#include <unistd.h>
+
 struct imapc_client_command_context {
        struct imapc_client_mailbox *box;
 
@@ -42,6 +46,8 @@ imapc_client_init(const struct imapc_client_settings *set)
        client->set.password = p_strdup(pool, set->password);
        client->set.dns_client_socket_path =
                p_strdup(pool, set->dns_client_socket_path);
+       client->set.temp_path_prefix =
+               p_strdup(pool, set->temp_path_prefix);
        p_array_init(&client->conns, pool, 8);
        return client;
 }
@@ -271,3 +277,28 @@ imapc_client_get_capabilities(struct imapc_client *client)
        connp = array_idx(&client->conns, 0);
        return imapc_connection_get_capabilities((*connp)->conn);
 }
+
+int imapc_client_create_temp_fd(struct imapc_client *client,
+                               const char **path_r)
+{
+       string_t *path;
+       int fd;
+
+       path = t_str_new(128);
+       str_append(path, client->set.temp_path_prefix);
+       fd = safe_mkstemp(path, 0600, (uid_t)-1, (gid_t)-1);
+       if (fd == -1) {
+               i_error("safe_mkstemp(%s) failed: %m", str_c(path));
+               return -1;
+       }
+
+       /* we just want the fd, unlink it */
+       if (unlink(str_c(path)) < 0) {
+               /* shouldn't happen.. */
+               i_error("unlink(%s) failed: %m", str_c(path));
+               (void)close(fd);
+               return -1;
+       }
+       *path_r = str_c(path);
+       return fd;
+}
index d9762a19e549c2d3322d2ff014b32da231f3d3ce..072a4895d7cbbf12aeeb29d8f4e1432b4adcbc7a 100644 (file)
@@ -32,6 +32,7 @@ struct imapc_client_settings {
        const char *password;
 
        const char *dns_client_socket_path;
+       const char *temp_path_prefix;
 };
 
 struct imapc_command_reply {
@@ -46,6 +47,16 @@ struct imapc_command_reply {
        const char *text_without_resp;
 };
 
+struct imapc_arg_file {
+       /* file descriptor containing the value */
+       int fd;
+
+       /* parent_arg.list[list_idx] points to the IMAP_ARG_LITERAL_SIZE
+          argument */
+       const struct imap_arg *parent_arg;
+       unsigned int list_idx;
+};
+
 struct imapc_untagged_reply {
        /* name of the untagged reply, e.g. EXISTS */
        const char *name;
@@ -54,6 +65,10 @@ struct imapc_untagged_reply {
        uint32_t num;
        /* the rest of the reply can be read from these args. */
        const struct imap_arg *args;
+       /* arguments whose contents are stored into files. only
+          "FETCH (BODY[" arguments can be here. */
+       const struct imapc_arg_file *file_args;
+       unsigned int file_args_count;
 
        /* "* OK [RESP TEXT]" produces key=RESP, value=TEXT.
           "* OK [RESP]" produces key=RESP, value=NULL
@@ -111,4 +126,7 @@ void imapc_client_mailbox_idle(struct imapc_client_mailbox *box);
 enum imapc_capability
 imapc_client_get_capabilities(struct imapc_client *client);
 
+int imapc_client_create_temp_fd(struct imapc_client *client,
+                               const char **path_r);
+
 #endif
index 364566ec56fa59fe703449f23e36fa573be119c3..a5f6e7f362147f3ea270f4200d1406a8fe8b271f 100644 (file)
@@ -6,6 +6,7 @@
 #include "istream.h"
 #include "ostream.h"
 #include "base64.h"
+#include "write-full.h"
 #include "str.h"
 #include "dns-lookup.h"
 #include "imap-quote.h"
 #include "imapc-seqmap.h"
 #include "imapc-connection.h"
 
+#include <unistd.h>
 #include <ctype.h>
 
 #define IMAPC_DNS_LOOKUP_TIMEOUT_MSECS (1000*30)
 #define IMAPC_CONNECT_TIMEOUT_MSECS (1000*30)
+#define IMAPC_MAX_INLINE_LITERAL_SIZE (1024*32)
 
 enum imapc_input_state {
        IMAPC_INPUT_STATE_NONE = 0,
@@ -45,6 +48,15 @@ struct imapc_command {
        void *context;
 };
 
+struct imapc_connection_literal {
+       char *temp_path;
+       int fd;
+       uoff_t bytes_left;
+
+       const struct imap_arg *parent_arg;
+       unsigned int list_idx;
+};
+
 struct imapc_connection {
        struct imapc_client *client;
        char *name;
@@ -73,6 +85,9 @@ struct imapc_connection {
        unsigned int ips_count, prev_connect_idx;
        struct ip_addr *ips;
 
+       struct imapc_connection_literal literal;
+       ARRAY_DEFINE(literal_files, struct imapc_arg_file);
+
        unsigned int idling:1;
        unsigned int idle_stopping:1;
        unsigned int idle_plus_waiting:1;
@@ -94,8 +109,10 @@ imapc_connection_init(struct imapc_client *client)
        conn->fd = -1;
        conn->name = i_strdup_printf("%s:%u", client->set.host,
                                     client->set.port);
+       conn->literal.fd = -1;
        i_array_init(&conn->cmd_send_queue, 8);
        i_array_init(&conn->cmd_wait_list, 32);
+       i_array_init(&conn->literal_files, 4);
        return conn;
 }
 
@@ -109,6 +126,7 @@ void imapc_connection_deinit(struct imapc_connection **_conn)
        p_strsplit_free(default_pool, conn->capabilities_list);
        array_free(&conn->cmd_send_queue);
        array_free(&conn->cmd_wait_list);
+       array_free(&conn->literal_files);
        i_free(conn->ips);
        i_free(conn->name);
        i_free(conn);
@@ -162,11 +180,37 @@ static void imapc_connection_set_state(struct imapc_connection *conn,
        conn->state = state;
 }
 
+static void imapc_connection_lfiles_free(struct imapc_connection *conn)
+{
+       struct imapc_arg_file *lfile;
+
+       array_foreach_modifiable(&conn->literal_files, lfile) {
+               if (close(lfile->fd) < 0)
+                       i_error("imapc: close(literal file) failed: %m");
+       }
+       array_clear(&conn->literal_files);
+}
+
+static void
+imapc_connection_literal_reset(struct imapc_connection_literal *literal)
+{
+       if (literal->fd != -1) {
+               if (close(literal->fd) < 0)
+                       i_error("close(%s) failed: %m", literal->temp_path);
+       }
+       i_free_and_null(literal->temp_path);
+
+       memset(literal, 0, sizeof(*literal));
+       literal->fd = -1;
+}
+
 static void imapc_connection_disconnect(struct imapc_connection *conn)
 {
        if (conn->fd == -1)
                return;
 
+       imapc_connection_lfiles_free(conn);
+       imapc_connection_literal_reset(&conn->literal);
        if (conn->to != NULL)
                timeout_remove(&conn->to);
        imap_parser_destroy(&conn->parser);
@@ -192,14 +236,104 @@ imapc_connection_input_error(struct imapc_connection *conn,
        va_end(va);
 }
 
+static bool last_arg_is_fetch_body(const struct imap_arg *args,
+                                  const struct imap_arg **parent_arg_r,
+                                  unsigned int *idx_r)
+{
+       const struct imap_arg *list;
+       const char *name;
+       unsigned int count;
+
+       if (args[0].type == IMAP_ARG_ATOM &&
+           imap_arg_atom_equals(&args[1], "FETCH") &&
+           imap_arg_get_list_full(&args[2], &list, &count) && count >= 2 &&
+           list[count].type == IMAP_ARG_LITERAL_SIZE &&
+           imap_arg_get_atom(&list[count-1], &name) &&
+           strncasecmp(name, "BODY[", 5) == 0) {
+               *parent_arg_r = &args[2];
+               *idx_r = count;
+               return TRUE;
+       }
+       return FALSE;
+}
+
 static int
-imapc_connection_read_line(struct imapc_connection *conn,
-                          const struct imap_arg **imap_args_r)
+imapc_connection_read_literal_init(struct imapc_connection *conn, uoff_t size,
+                                  const struct imap_arg *args)
 {
-       int ret;
+       const char *path;
+       const struct imap_arg *parent_arg;
+       unsigned int idx;
+
+       i_assert(size > 0);
+       i_assert(conn->literal.fd == -1);
+
+       if (size <= IMAPC_MAX_INLINE_LITERAL_SIZE ||
+           !last_arg_is_fetch_body(args, &parent_arg, &idx)) {
+               /* read the literal directly into parser */
+               return 0;
+       }
+
+       conn->literal.fd = imapc_client_create_temp_fd(conn->client, &path);
+       if (conn->literal.fd == -1)
+               return -1;
+       conn->literal.temp_path = i_strdup(path);
+       conn->literal.bytes_left = size;
+       conn->literal.parent_arg = parent_arg;
+       conn->literal.list_idx = idx;
+       return 1;
+}
+
+static int imapc_connection_read_literal(struct imapc_connection *conn)
+{
+       struct imapc_arg_file *lfile;
+       const unsigned char *data;
+       size_t size;
+
+       if (conn->literal.bytes_left == 0)
+               return 1;
+
+       data = i_stream_get_data(conn->input, &size);
+       if (size > conn->literal.bytes_left)
+               size = conn->literal.bytes_left;
+       if (size > 0) {
+               if (write_full(conn->literal.fd, data, size) < 0) {
+                       i_error("imapc(%s): write(%s) failed: %m",
+                               conn->name, conn->literal.temp_path);
+                       imapc_connection_disconnect(conn);
+                       return -1;
+               }
+               i_stream_skip(conn->input, size);
+               conn->literal.bytes_left -= size;
+       }
+       if (conn->literal.bytes_left > 0)
+               return 0;
+
+       /* finished */
+       lfile = array_append_space(&conn->literal_files);
+       lfile->fd = conn->literal.fd;
+       lfile->parent_arg = conn->literal.parent_arg;
+       lfile->list_idx = conn->literal.list_idx;
+
+       conn->literal.fd = -1;
+       imapc_connection_literal_reset(&conn->literal);
+       return 1;
+}
+
+static int
+imapc_connection_read_line_more(struct imapc_connection *conn,
+                               const struct imap_arg **imap_args_r)
+{
+       uoff_t literal_size;
        bool fatal;
+       int ret;
+
+       if ((ret = imapc_connection_read_literal(conn)) <= 0)
+               return ret;
 
-       ret = imap_parser_read_args(conn->parser, 0, 0, imap_args_r);
+       ret = imap_parser_read_args(conn->parser, 0,
+                                   IMAP_PARSE_FLAG_LITERAL_SIZE |
+                                   IMAP_PARSE_FLAG_ATOM_ALLCHARS, imap_args_r);
        if (ret == -2) {
                /* need more data */
                return 0;
@@ -209,9 +343,29 @@ imapc_connection_read_line(struct imapc_connection *conn,
                        imap_parser_get_error(conn->parser, &fatal));
                return -1;
        }
+
+       if (imap_parser_get_literal_size(conn->parser, &literal_size)) {
+               if (imapc_connection_read_literal_init(conn, literal_size,
+                                                      *imap_args_r) <= 0) {
+                       imap_parser_read_last_literal(conn->parser);
+                       return 2;
+               }
+               return imapc_connection_read_line_more(conn, imap_args_r);
+       }
        return 1;
 }
 
+static int
+imapc_connection_read_line(struct imapc_connection *conn,
+                          const struct imap_arg **imap_args_r)
+{
+       int ret;
+
+       while ((ret = imapc_connection_read_line_more(conn, imap_args_r)) == 2)
+               ;
+       return ret;
+}
+
 static int
 imapc_connection_parse_capability(struct imapc_connection *conn,
                                  const char *value)
@@ -332,6 +486,7 @@ static void imapc_connection_input_reset(struct imapc_connection *conn)
        conn->cur_tag = 0;
        conn->cur_num = 0;
        imap_parser_reset(conn->parser);
+       imapc_connection_lfiles_free(conn);
 }
 
 static int imapc_connection_skip_line(struct imapc_connection *conn)
@@ -507,6 +662,9 @@ static int imapc_connection_input_untagged(struct imapc_connection *conn)
        reply.name = name;
        reply.num = conn->cur_num;
        reply.args = imap_args;
+       reply.file_args = array_get(&conn->literal_files,
+                                   &reply.file_args_count);
+
        if (conn->selected_box != NULL) {
                reply.untagged_box_context =
                        conn->selected_box->untagged_box_context;
index 1df5168727397909909038b578d827e032ac40d3..293b7718470803e44f54fc4ce08ae738db2e8e3b 100644 (file)
@@ -23,6 +23,15 @@ imapc_mail_alloc(struct mailbox_transaction_context *t,
        return &mail->imail.mail.mail;
 }
 
+static void imapc_mail_free(struct mail *_mail)
+{
+       struct imapc_mail *mail = (struct imapc_mail *)_mail;
+
+       if (mail->body != NULL)
+               buffer_free(&mail->body);
+       index_mail_free(_mail);
+}
+
 static int imapc_mail_get_received_date(struct mail *_mail, time_t *date_r)
 {
        struct index_mail *mail = (struct index_mail *)_mail;
@@ -126,7 +135,7 @@ imapc_mail_get_stream(struct mail *_mail, struct message_size *hdr_size,
 
 struct mail_vfuncs imapc_mail_vfuncs = {
        index_mail_close,
-       index_mail_free,
+       imapc_mail_free,
        index_mail_set_seq,
        index_mail_set_uid,
        index_mail_set_uid_cache_updates,
index e1cb8889caeca9b70e67271a145f854bdc5ef932..182bf91afebc99d42f584b6cf0b7929d2a8fd065 100644 (file)
@@ -5,6 +5,8 @@
 
 struct imapc_mail {
        struct index_mail imail;
+
+       buffer_t *body;
        unsigned int searching:1;
        unsigned int fetch_one:1;
 };
index 28758b2a7c37a9e8fd15bf16d28576c5389611f4..7081e4534e94a60cf61e91420a136991c9a58b4d 100644 (file)
@@ -131,7 +131,7 @@ static void imapc_untagged_fetch(const struct imapc_untagged_reply *reply,
 
        if (mbox->cur_fetch_mail != NULL && mbox->cur_fetch_mail->seq == seq) {
                i_assert(uid == 0 || mbox->cur_fetch_mail->uid == uid);
-               imapc_fetch_mail_update(mbox->cur_fetch_mail, list);
+               imapc_fetch_mail_update(mbox->cur_fetch_mail, reply, list);
        }
 
        imapc_mailbox_init_delayed_trans(mbox);
index e7dd2a0f544e1a44ad5bd0024d301d9f7f8a1ffc..61f4640b853be52a91897d1ccd7868f0e8f91911 100644 (file)
@@ -64,7 +64,7 @@ int imapc_save_begin(struct mail_save_context *_ctx, struct istream *input)
 
        i_assert(ctx->fd == -1);
 
-       ctx->fd = imapc_create_temp_fd(storage->user, &path);
+       ctx->fd = imapc_client_create_temp_fd(ctx->mbox->storage->client, &path);
        if (ctx->fd == -1) {
                mail_storage_set_critical(storage,
                                          "Couldn't create temp file %s", path);
index 7864ac9da0dc84fd6b03aca470c4177352c81979..c20101a911286849a3dd8cf48b117fc42d784257 100644 (file)
@@ -230,29 +230,67 @@ bool imapc_search_next_update_seq(struct mail_search_context *_ctx)
        return TRUE;
 }
 
+static bool imapc_find_lfile_arg(const struct imapc_untagged_reply *reply,
+                                const struct imap_arg *arg, int *fd_r)
+{
+       const struct imap_arg *list;
+       unsigned int i, count;
+
+       for (i = 0; i < reply->file_args_count; i++) {
+               const struct imapc_arg_file *farg = &reply->file_args[i];
+
+               if (farg->parent_arg == arg->parent &&
+                   imap_arg_get_list_full(arg->parent, &list, &count) &&
+                   farg->list_idx < count && &list[farg->list_idx] == arg) {
+                       *fd_r = farg->fd;
+                       return TRUE;
+               }
+       }
+       return FALSE;
+}
+
 static void
-imapc_fetch_stream(struct index_mail *imail, const char *value, bool body)
+imapc_fetch_stream(struct imapc_mail *mail,
+                  const struct imapc_untagged_reply *reply,
+                  const struct imap_arg *arg, bool body)
 {
+       struct index_mail *imail = &mail->imail;
        struct mail *_mail = &imail->mail.mail;
        struct istream *input;
-       size_t value_len = strlen(value);
        uoff_t size;
-       const char *path;
+       const char *value;
        int fd, ret;
 
        if (imail->data.stream != NULL)
                return;
 
-       fd = imapc_create_temp_fd(_mail->box->storage->user, &path);
-       if (fd == -1)
-               return;
-       if (write_full(fd, value, value_len) < 0) {
-               (void)close(fd);
-               return;
+       if (arg->type == IMAP_ARG_LITERAL_SIZE) {
+               if (!imapc_find_lfile_arg(reply, arg, &fd))
+                       return;
+               if ((fd = dup(fd)) == -1) {
+                       i_error("dup() failed: %m");
+                       return;
+               }
+               imail->data.stream = i_stream_create_fd(fd, 0, TRUE);
+       } else {
+               if (!imap_arg_get_nstring(arg, &value))
+                       return;
+               if (value == NULL) {
+                       mail_set_expunged(_mail);
+                       return;
+               }
+               if (mail->body == NULL) {
+                       mail->body = buffer_create_dynamic(default_pool,
+                                                          arg->str_len + 1);
+               }
+               buffer_set_used_size(mail->body, 0);
+               buffer_append(mail->body, value, arg->str_len);
+               imail->data.stream = i_stream_create_from_data(mail->body->data,
+                                                              mail->body->used);
        }
 
-       imail->data.stream = i_stream_create_fd(fd, 0, TRUE);
-       i_stream_set_name(imail->data.stream, path);
+       i_stream_set_name(imail->data.stream,
+                         t_strdup_printf("imapc mail uid=%u", _mail->uid));
        index_mail_set_read_buffer_size(_mail, imail->data.stream);
 
        if (imail->mail.v.istream_opened != NULL) {
@@ -278,11 +316,13 @@ imapc_fetch_stream(struct index_mail *imail, const char *value, bool body)
                i_stream_unref(&imail->data.stream);
 }
 
-void imapc_fetch_mail_update(struct mail *mail, const struct imap_arg *args)
+void imapc_fetch_mail_update(struct mail *mail,
+                            const struct imapc_untagged_reply *reply,
+                            const struct imap_arg *args)
 {
        struct imapc_mail *imapmail = (struct imapc_mail *)mail;
        struct imapc_mailbox *mbox = (struct imapc_mailbox *)mail->box;
-       struct index_mail *imail = (struct index_mail *)mail;
+       struct imapc_mail *imail = (struct imapc_mail *)mail;
        const char *key, *value;
        unsigned int i;
        time_t t;
@@ -291,23 +331,16 @@ void imapc_fetch_mail_update(struct mail *mail, const struct imap_arg *args)
        for (i = 0; args[i].type != IMAP_ARG_EOL; i += 2) {
                if (!imap_arg_get_atom(&args[i], &key) ||
                    args[i+1].type == IMAP_ARG_EOL)
-                       return;
+                       break;
 
-               if (strcasecmp(key, "BODY[]") == 0) {
-                       if (!imap_arg_get_nstring(&args[i+1], &value))
-                               return;
-                       if (value != NULL)
-                               imapc_fetch_stream(imail, value, TRUE);
-               } else if (strcasecmp(key, "BODY[HEADER]") == 0) {
-                       if (!imap_arg_get_nstring(&args[i+1], &value))
-                               return;
-                       if (value != NULL)
-                               imapc_fetch_stream(imail, value, FALSE);
-               } else if (strcasecmp(key, "INTERNALDATE") == 0) {
-                       if (!imap_arg_get_astring(&args[i+1], &value) ||
-                           !imap_parse_datetime(value, &t, &tz))
-                               return;
-                       imail->data.received_date = t;
+               if (strcasecmp(key, "BODY[]") == 0)
+                       imapc_fetch_stream(imail, reply, &args[i+1], TRUE);
+               else if (strcasecmp(key, "BODY[HEADER]") == 0)
+                       imapc_fetch_stream(imail, reply, &args[i+1], FALSE);
+               else if (strcasecmp(key, "INTERNALDATE") == 0) {
+                       if (imap_arg_get_astring(&args[i+1], &value) &&
+                           imap_parse_datetime(value, &t, &tz))
+                               imail->imail.data.received_date = t;
                }
        }
        if (!imapmail->fetch_one)
index 96beba6ce925489c0915dfcb0171e30c07fed3df..9177c222800e66f8d80997fefdd10c6af540c46d 100644 (file)
@@ -3,7 +3,6 @@
 #include "lib.h"
 #include "ioloop.h"
 #include "str.h"
-#include "safe-mkstemp.h"
 #include "imap-arg.h"
 #include "imap-resp-code.h"
 #include "imapc-mail.h"
@@ -165,6 +164,7 @@ imapc_storage_create(struct mail_storage *_storage,
        struct imapc_storage *storage = (struct imapc_storage *)_storage;
        struct imapc_client_settings set;
        const char *port;
+       string_t *str;
 
        memset(&set, 0, sizeof(set));
        set.host = ns->list->set.root_dir;
@@ -192,6 +192,10 @@ imapc_storage_create(struct mail_storage *_storage,
        set.dns_client_socket_path =
                t_strconcat(_storage->user->set->base_dir, "/",
                            DNS_CLIENT_SOCKET_NAME, NULL);
+       str = t_str_new(128);
+       mail_user_set_get_temp_prefix(str, _storage->user->set);
+       set.temp_path_prefix = str_c(str);
+
        storage->list = (struct imapc_mailbox_list *)ns->list;
        storage->list->storage = storage;
        storage->client = imapc_client_init(&set);
@@ -477,30 +481,6 @@ static void imapc_notify_changes(struct mailbox *box ATTR_UNUSED)
        /* we're doing IDLE all the time anyway - nothing to do here */
 }
 
-int imapc_create_temp_fd(struct mail_user *user, const char **path_r)
-{
-       string_t *path;
-       int fd;
-
-       path = t_str_new(128);
-       mail_user_set_get_temp_prefix(path, user->set);
-       fd = safe_mkstemp(path, 0600, (uid_t)-1, (gid_t)-1);
-       if (fd == -1) {
-               i_error("safe_mkstemp(%s) failed: %m", str_c(path));
-               return -1;
-       }
-
-       /* we just want the fd, unlink it */
-       if (unlink(str_c(path)) < 0) {
-               /* shouldn't happen.. */
-               i_error("unlink(%s) failed: %m", str_c(path));
-               (void)close(fd);
-               return -1;
-       }
-       *path_r = str_c(path);
-       return fd;
-}
-
 struct mail_storage imapc_storage = {
        .name = IMAPC_STORAGE_NAME,
        .class_flags = 0,
index a4b9f97864606c8cc6dc77b693d4739260e82146..32cb180baf49c705a6170c9fb5b27cdbc03ce80b 100644 (file)
@@ -81,7 +81,9 @@ int imapc_search_deinit(struct mail_search_context *_ctx);
 bool imapc_search_next_nonblock(struct mail_search_context *_ctx,
                                struct mail *mail, bool *tryagain_r);
 bool imapc_search_next_update_seq(struct mail_search_context *_ctx);
-void imapc_fetch_mail_update(struct mail *mail, const struct imap_arg *args);
+void imapc_fetch_mail_update(struct mail *mail,
+                            const struct imapc_untagged_reply *reply,
+                            const struct imap_arg *args);
 
 void imapc_copy_error_from_reply(struct imapc_storage *storage,
                                 enum mail_error default_error,
@@ -102,6 +104,5 @@ void imapc_mailbox_register_resp_text(struct imapc_mailbox *mbox,
                                      imapc_mailbox_callback_t *callback);
 
 void imapc_mailbox_register_callbacks(struct imapc_mailbox *mbox);
-int imapc_create_temp_fd(struct mail_user *user, const char **path_r);
 
 #endif