Mercurial > dovecot > core-2.2
view src/imap/cmd-setmetadata.c @ 17130:add8c00fb3cc
Updated copyright notices to include year 2014.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Tue, 04 Feb 2014 16:23:22 -0500 |
parents | cded53530a9f |
children | a350812e07bf |
line wrap: on
line source
/* Copyright (c) 2013-2014 Dovecot authors, see the included COPYING file */ #include "imap-common.h" #include "ioloop.h" #include "istream.h" #include "istream-seekable.h" #include "ostream.h" #include "str.h" #include "imap-metadata.h" #define METADATA_MAX_INMEM_SIZE (1024*128) struct imap_setmetadata_context { struct client_command_context *cmd; struct imap_parser *parser; const char *key_prefix; struct mailbox *box; struct mailbox_transaction_context *trans; char *entry_name; uoff_t entry_value_len; struct istream *input; bool failed; bool cmd_error_sent; bool storage_failure; }; static void cmd_setmetadata_deinit(struct imap_setmetadata_context *ctx) { ctx->cmd->client->input_lock = NULL; imap_parser_unref(&ctx->parser); if (ctx->trans != NULL) mailbox_transaction_rollback(&ctx->trans); if (ctx->box != ctx->cmd->client->mailbox) mailbox_free(&ctx->box); i_free(ctx->entry_name); } static int cmd_setmetadata_parse_entryvalue(struct imap_setmetadata_context *ctx, const char **entry_r, const struct imap_arg **value_r) { const struct imap_arg *args; const char *name, *error; int ret; bool fatal; /* parse the entry name */ ret = imap_parser_read_args(ctx->parser, 1, IMAP_PARSE_FLAG_INSIDE_LIST, &args); if (ret >= 0) { if (ret == 0) { /* ')' found */ *entry_r = NULL; return 1; } if (!imap_arg_get_astring(args, &name)) { client_send_command_error(ctx->cmd, "Entry name isn't astring"); return -1; } ret = imap_parser_read_args(ctx->parser, 2, IMAP_PARSE_FLAG_INSIDE_LIST | IMAP_PARSE_FLAG_LITERAL_SIZE | IMAP_PARSE_FLAG_LITERAL8, &args); } if (ret < 0) { if (ret == -2) return 0; error = imap_parser_get_error(ctx->parser, &fatal); if (fatal) { client_disconnect_with_error(ctx->cmd->client, error); return -1; } client_send_command_error(ctx->cmd, error); return -1; } i_assert(!IMAP_ARG_IS_EOL(&args[1])); if (args[1].type == IMAP_ARG_LIST) { client_send_command_error(ctx->cmd, "Entry value can't be a list"); return -1; } if (ctx->cmd_error_sent || !imap_metadata_verify_entry_name(ctx->cmd, name)) { ctx->cmd->param_error = FALSE; ctx->cmd->state = CLIENT_COMMAND_STATE_WAIT_INPUT; ctx->cmd_error_sent = TRUE; ctx->failed = TRUE; if (args[1].type == IMAP_ARG_LITERAL_SIZE) { /* client won't see "+ OK", so we can abort immediately */ ctx->cmd->client->input_skip_line = FALSE; return -1; } } /* entry names are case-insensitive. handle this by using only lowercase names. */ *entry_r = t_str_lcase(name); *value_r = &args[1]; return 1; } static int cmd_setmetadata_entry_read_stream(struct imap_setmetadata_context *ctx) { const unsigned char *data; size_t size; enum mail_attribute_type type; const char *key; struct mail_attribute_value value; int ret; while ((ret = i_stream_read_data(ctx->input, &data, &size, 0)) > 0) i_stream_skip(ctx->input, size); if (ctx->input->v_offset == ctx->entry_value_len) { /* finished reading the value */ i_stream_seek(ctx->input, 0); if (ctx->failed) { i_stream_unref(&ctx->input); return 1; } imap_metadata_entry2key(ctx->entry_name, ctx->key_prefix, &type, &key); memset(&value, 0, sizeof(value)); value.value_stream = ctx->input; if (mailbox_attribute_set(ctx->trans, type, key, &value) < 0) { /* delay reporting the failure so we'll finish reading the command input */ ctx->storage_failure = TRUE; ctx->failed = TRUE; } i_stream_unref(&ctx->input); return 1; } if (ctx->input->eof) { /* client disconnected */ return -1; } return 0; } static int cmd_setmetadata_entry(struct imap_setmetadata_context *ctx, const char *entry_name, const struct imap_arg *entry_value) { struct istream *inputs[2]; enum mail_attribute_type type; const char *key; struct mail_attribute_value value; string_t *path; int ret; switch (entry_value->type) { case IMAP_ARG_NIL: case IMAP_ARG_ATOM: case IMAP_ARG_STRING: /* we have the value already */ imap_metadata_entry2key(entry_name, ctx->key_prefix, &type, &key); if (ctx->failed) return 1; memset(&value, 0, sizeof(value)); value.value = imap_arg_as_nstring(entry_value); ret = value.value == NULL ? mailbox_attribute_unset(ctx->trans, type, key) : mailbox_attribute_set(ctx->trans, type, key, &value); if (ret < 0) { /* delay reporting the failure so we'll finish reading the command input */ ctx->storage_failure = TRUE; ctx->failed = TRUE; } return 1; case IMAP_ARG_LITERAL_SIZE: o_stream_nsend(ctx->cmd->client->output, "+ OK\r\n", 6); o_stream_nflush(ctx->cmd->client->output); o_stream_uncork(ctx->cmd->client->output); o_stream_cork(ctx->cmd->client->output); /* fall through */ case IMAP_ARG_LITERAL_SIZE_NONSYNC: i_free(ctx->entry_name); ctx->entry_name = i_strdup(entry_name); ctx->entry_value_len = imap_arg_as_literal_size(entry_value); inputs[0] = i_stream_create_limit(ctx->cmd->client->input, ctx->entry_value_len); inputs[1] = NULL; path = t_str_new(128); mail_user_set_get_temp_prefix(path, ctx->cmd->client->user->set); ctx->input = i_stream_create_seekable_path(inputs, METADATA_MAX_INMEM_SIZE, str_c(path)); i_stream_set_name(ctx->input, i_stream_get_name(inputs[0])); i_stream_unref(&inputs[0]); return cmd_setmetadata_entry_read_stream(ctx); case IMAP_ARG_LITERAL: case IMAP_ARG_LIST: case IMAP_ARG_EOL: break; } i_unreached(); } static bool cmd_setmetadata_continue(struct client_command_context *cmd) { struct imap_setmetadata_context *ctx = cmd->context; const char *entry; const struct imap_arg *value; int ret; if (cmd->cancel) { cmd_setmetadata_deinit(ctx); return TRUE; } if (ctx->input != NULL) { if ((ret = cmd_setmetadata_entry_read_stream(ctx)) == 0) return FALSE; if (ret < 0) { cmd_setmetadata_deinit(ctx); return TRUE; } } while ((ret = cmd_setmetadata_parse_entryvalue(ctx, &entry, &value)) > 0 && entry != NULL) { ret = ctx->failed ? 1 : cmd_setmetadata_entry(ctx, entry, value); imap_parser_reset(ctx->parser); if (ret <= 0) break; } if (ret == 0) return 0; if (ret < 0 || ctx->cmd_error_sent) /* already sent the error to client */ ; else if (ctx->storage_failure) client_send_box_error(cmd, ctx->box); else if (mailbox_transaction_commit(&ctx->trans) < 0) client_send_box_error(cmd, ctx->box); else client_send_tagline(cmd, "OK Setmetadata completed."); cmd_setmetadata_deinit(ctx); return TRUE; } bool cmd_setmetadata(struct client_command_context *cmd) { struct imap_setmetadata_context *ctx; const struct imap_arg *args; const char *mailbox; struct mail_namespace *ns; int ret; ret = imap_parser_read_args(cmd->parser, 2, IMAP_PARSE_FLAG_STOP_AT_LIST, &args); if (ret == -1) { client_send_command_error(cmd, NULL); return TRUE; } if (ret == -2) return FALSE; if (!imap_arg_get_astring(&args[0], &mailbox) || args[1].type != IMAP_ARG_LIST) { client_send_command_error(cmd, "Invalid arguments."); return TRUE; } if (!cmd->client->imap_metadata_enabled) { client_send_command_error(cmd, "METADATA disabled."); return TRUE; } ctx = p_new(cmd->pool, struct imap_setmetadata_context, 1); ctx->cmd = cmd; ctx->cmd->context = ctx; if (mailbox[0] == '\0') { /* server attribute */ ctx->key_prefix = MAILBOX_ATTRIBUTE_PREFIX_DOVECOT_PVT_SERVER; ns = mail_namespace_find_inbox(cmd->client->user->namespaces); mailbox = "INBOX"; } else { ns = client_find_namespace(cmd, &mailbox); if (ns == NULL) return TRUE; } if (cmd->client->mailbox != NULL && !cmd->client->mailbox_examined && mailbox_equals(cmd->client->mailbox, ns, mailbox)) ctx->box = cmd->client->mailbox; else { ctx->box = mailbox_alloc(ns->list, mailbox, 0); if (mailbox_open(ctx->box) < 0) { client_send_box_error(cmd, ctx->box); mailbox_free(&ctx->box); return TRUE; } } ctx->trans = mailbox_transaction_begin(ctx->box, 0); /* we support large literals, so read the values from client asynchronously the same way as APPEND does. */ cmd->client->input_lock = cmd; ctx->parser = imap_parser_create(cmd->client->input, cmd->client->output, cmd->client->set->imap_max_line_length); o_stream_unset_flush_callback(cmd->client->output); cmd->func = cmd_setmetadata_continue; cmd->context = ctx; return cmd_setmetadata_continue(cmd); }