bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2013-2018 Dovecot authors, see the included COPYING file */
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen#include "imap-common.h"
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen#include "ioloop.h"
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen#include "istream.h"
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen#include "istream-seekable.h"
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen#include "ostream.h"
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen#include "str.h"
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen#include "imap-metadata.h"
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen#define METADATA_MAX_INMEM_SIZE (1024*128)
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainenstruct imap_setmetadata_context {
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen struct client_command_context *cmd;
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen struct imap_parser *parser;
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen struct mailbox *box;
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch struct imap_metadata_transaction *trans;
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen char *entry_name;
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen uoff_t entry_value_len;
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen struct istream *input;
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen bool failed;
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen bool cmd_error_sent;
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen bool storage_failure;
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen};
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainenstatic void cmd_setmetadata_deinit(struct imap_setmetadata_context *ctx)
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen{
fe779565bda49a0ed0476724819c6e3c1340c94bTimo Sirainen o_stream_set_flush_callback(ctx->cmd->client->output,
fe779565bda49a0ed0476724819c6e3c1340c94bTimo Sirainen client_output, ctx->cmd->client);
fe779565bda49a0ed0476724819c6e3c1340c94bTimo Sirainen
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen ctx->cmd->client->input_lock = NULL;
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen imap_parser_unref(&ctx->parser);
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen if (ctx->trans != NULL)
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch imap_metadata_transaction_rollback(&ctx->trans);
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch if (ctx->box != NULL && ctx->box != ctx->cmd->client->mailbox)
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen mailbox_free(&ctx->box);
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen i_free(ctx->entry_name);
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen}
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainenstatic int
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainencmd_setmetadata_parse_entryvalue(struct imap_setmetadata_context *ctx,
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen const char **entry_r,
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen const struct imap_arg **value_r)
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen{
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen const struct imap_arg *args;
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen const char *name, *error;
0adc24c0c534944b55a185795e09dfaea2ca3131Stephan Bosch enum imap_parser_error parse_error;
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen int ret;
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen /* parse the entry name */
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen ret = imap_parser_read_args(ctx->parser, 1,
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen IMAP_PARSE_FLAG_INSIDE_LIST, &args);
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen if (ret >= 0) {
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen if (ret == 0) {
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen /* ')' found */
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen *entry_r = NULL;
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen return 1;
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen }
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen if (!imap_arg_get_astring(args, &name)) {
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen client_send_command_error(ctx->cmd,
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen "Entry name isn't astring");
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen return -1;
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen }
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen ret = imap_parser_read_args(ctx->parser, 2,
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen IMAP_PARSE_FLAG_INSIDE_LIST |
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen IMAP_PARSE_FLAG_LITERAL_SIZE |
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen IMAP_PARSE_FLAG_LITERAL8, &args);
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen }
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen if (ret < 0) {
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen if (ret == -2)
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen return 0;
0adc24c0c534944b55a185795e09dfaea2ca3131Stephan Bosch error = imap_parser_get_error(ctx->parser, &parse_error);
0adc24c0c534944b55a185795e09dfaea2ca3131Stephan Bosch switch (parse_error) {
0adc24c0c534944b55a185795e09dfaea2ca3131Stephan Bosch case IMAP_PARSE_ERROR_NONE:
0adc24c0c534944b55a185795e09dfaea2ca3131Stephan Bosch i_unreached();
0adc24c0c534944b55a185795e09dfaea2ca3131Stephan Bosch case IMAP_PARSE_ERROR_LITERAL_TOO_BIG:
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen client_disconnect_with_error(ctx->cmd->client, error);
0adc24c0c534944b55a185795e09dfaea2ca3131Stephan Bosch break;
0adc24c0c534944b55a185795e09dfaea2ca3131Stephan Bosch default:
0adc24c0c534944b55a185795e09dfaea2ca3131Stephan Bosch client_send_command_error(ctx->cmd, error);
0adc24c0c534944b55a185795e09dfaea2ca3131Stephan Bosch break;
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen }
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen return -1;
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen }
1c75bf24894a3fc0631caa4954e5130e9bb01d8dTimo Sirainen if (args[1].type == IMAP_ARG_EOL) {
1c75bf24894a3fc0631caa4954e5130e9bb01d8dTimo Sirainen client_send_command_error(ctx->cmd, "Entry value missing");
1c75bf24894a3fc0631caa4954e5130e9bb01d8dTimo Sirainen return -1;
1c75bf24894a3fc0631caa4954e5130e9bb01d8dTimo Sirainen }
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen if (args[1].type == IMAP_ARG_LIST) {
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen client_send_command_error(ctx->cmd, "Entry value can't be a list");
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen return -1;
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen }
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch if (!ctx->cmd_error_sent &&
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch !imap_metadata_verify_entry_name(name, &error)) {
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch client_send_command_error(ctx->cmd, error);
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch ctx->cmd_error_sent = TRUE;
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch }
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch if (ctx->cmd_error_sent) {
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen ctx->cmd->param_error = FALSE;
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen ctx->cmd->state = CLIENT_COMMAND_STATE_WAIT_INPUT;
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen ctx->failed = TRUE;
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen if (args[1].type == IMAP_ARG_LITERAL_SIZE) {
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen /* client won't see "+ OK", so we can abort
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen immediately */
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen ctx->cmd->client->input_skip_line = FALSE;
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen return -1;
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen }
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen }
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen /* entry names are case-insensitive. handle this by using only
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen lowercase names. */
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen *entry_r = t_str_lcase(name);
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen *value_r = &args[1];
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen return 1;
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen}
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainenstatic int
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainencmd_setmetadata_entry_read_stream(struct imap_setmetadata_context *ctx)
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen{
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen const unsigned char *data;
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen size_t size;
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen struct mail_attribute_value value;
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen int ret;
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen
3858a7a5da361c35f1e6e50c8e3214dc0cf379d6Phil Carmody while ((ret = i_stream_read_more(ctx->input, &data, &size)) > 0)
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen i_stream_skip(ctx->input, size);
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen if (ctx->input->v_offset == ctx->entry_value_len) {
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen /* finished reading the value */
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen i_stream_seek(ctx->input, 0);
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen if (ctx->failed) {
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen i_stream_unref(&ctx->input);
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen return 1;
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen }
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen
efe78d3ba24fc866af1c79b9223dc0809ba26cadStephan Bosch i_zero(&value);
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen value.value_stream = ctx->input;
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch if (imap_metadata_set(ctx->trans, ctx->entry_name, &value) < 0) {
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen /* delay reporting the failure so we'll finish
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen reading the command input */
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen ctx->storage_failure = TRUE;
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen ctx->failed = TRUE;
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen }
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen i_stream_unref(&ctx->input);
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen return 1;
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen }
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen if (ctx->input->eof) {
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen /* client disconnected */
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen return -1;
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen }
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen return 0;
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen}
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainenstatic int
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainencmd_setmetadata_entry(struct imap_setmetadata_context *ctx,
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen const char *entry_name,
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen const struct imap_arg *entry_value)
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen{
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen struct istream *inputs[2];
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen struct mail_attribute_value value;
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen string_t *path;
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen int ret;
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen switch (entry_value->type) {
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen case IMAP_ARG_NIL:
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen case IMAP_ARG_ATOM:
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen case IMAP_ARG_STRING:
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen /* we have the value already */
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen if (ctx->failed)
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen return 1;
efe78d3ba24fc866af1c79b9223dc0809ba26cadStephan Bosch i_zero(&value);
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen value.value = imap_arg_as_nstring(entry_value);
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch ret = imap_metadata_set(ctx->trans, entry_name, &value);
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen if (ret < 0) {
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen /* delay reporting the failure so we'll finish
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen reading the command input */
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen ctx->storage_failure = TRUE;
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen ctx->failed = TRUE;
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen }
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen return 1;
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen case IMAP_ARG_LITERAL_SIZE:
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen o_stream_nsend(ctx->cmd->client->output, "+ OK\r\n", 6);
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen o_stream_uncork(ctx->cmd->client->output);
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen o_stream_cork(ctx->cmd->client->output);
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen /* fall through */
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen case IMAP_ARG_LITERAL_SIZE_NONSYNC:
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen i_free(ctx->entry_name);
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen ctx->entry_name = i_strdup(entry_name);
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen ctx->entry_value_len = imap_arg_as_literal_size(entry_value);
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen inputs[0] = i_stream_create_limit(ctx->cmd->client->input,
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen ctx->entry_value_len);
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen inputs[1] = NULL;
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen path = t_str_new(128);
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen mail_user_set_get_temp_prefix(path, ctx->cmd->client->user->set);
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen ctx->input = i_stream_create_seekable_path(inputs,
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen METADATA_MAX_INMEM_SIZE, str_c(path));
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen i_stream_set_name(ctx->input, i_stream_get_name(inputs[0]));
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen i_stream_unref(&inputs[0]);
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen return cmd_setmetadata_entry_read_stream(ctx);
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen case IMAP_ARG_LITERAL:
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen case IMAP_ARG_LIST:
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen case IMAP_ARG_EOL:
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen break;
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen }
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen i_unreached();
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen}
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainenstatic bool cmd_setmetadata_continue(struct client_command_context *cmd)
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen{
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen struct imap_setmetadata_context *ctx = cmd->context;
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch const char *entry, *error_string;
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch enum mail_error error;
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen const struct imap_arg *value;
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen int ret;
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen if (cmd->cancel) {
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen cmd_setmetadata_deinit(ctx);
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen return TRUE;
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen }
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen if (ctx->input != NULL) {
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen if ((ret = cmd_setmetadata_entry_read_stream(ctx)) == 0)
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen return FALSE;
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen if (ret < 0) {
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen cmd_setmetadata_deinit(ctx);
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen return TRUE;
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen }
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen }
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen while ((ret = cmd_setmetadata_parse_entryvalue(ctx, &entry, &value)) > 0 &&
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen entry != NULL) {
8855b8b57050fe3b6dc3f19283488512fae98648Timo Sirainen ret = ctx->failed ? 1 :
8855b8b57050fe3b6dc3f19283488512fae98648Timo Sirainen cmd_setmetadata_entry(ctx, entry, value);
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen imap_parser_reset(ctx->parser);
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen if (ret <= 0)
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen break;
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen }
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen if (ret == 0)
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen return 0;
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch if (ret < 0 || ctx->cmd_error_sent) {
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen /* already sent the error to client */ ;
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch } else if (ctx->storage_failure) {
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch if (ctx->box == NULL)
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch client_disconnect_if_inconsistent(cmd->client);
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch error_string = imap_metadata_transaction_get_last_error
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch (ctx->trans, &error);
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch client_send_tagline(cmd,
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch imap_get_error_string(cmd, error_string, error));
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch } else if (imap_metadata_transaction_commit(&ctx->trans,
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch &error, &error_string) < 0) {
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch if (ctx->box == NULL)
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch client_disconnect_if_inconsistent(cmd->client);
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch client_send_tagline(cmd,
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch imap_get_error_string(cmd, error_string, error));
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch } else {
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen client_send_tagline(cmd, "OK Setmetadata completed.");
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch }
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen cmd_setmetadata_deinit(ctx);
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen return TRUE;
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen}
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Boschstatic bool
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Boschcmd_setmetadata_start(struct imap_setmetadata_context *ctx)
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch{
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch struct client_command_context *cmd = ctx->cmd;
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch struct client *client = cmd->client;
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch /* we support large literals, so read the values from client
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch asynchronously the same way as APPEND does. */
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch client->input_lock = cmd;
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch ctx->parser = imap_parser_create(client->input, client->output,
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch client->set->imap_max_line_length);
d8aa10df6d1dae56d3aa485708a34d74e9e31e79Stephan Bosch if (client->set->imap_literal_minus)
d8aa10df6d1dae56d3aa485708a34d74e9e31e79Stephan Bosch imap_parser_enable_literal_minus(ctx->parser);
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch o_stream_unset_flush_callback(client->output);
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch cmd->func = cmd_setmetadata_continue;
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch cmd->context = ctx;
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch return cmd_setmetadata_continue(cmd);
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch}
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Boschstatic bool
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Boschcmd_setmetadata_server(struct imap_setmetadata_context *ctx)
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch{
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch ctx->trans = imap_metadata_transaction_begin_server(ctx->cmd->client->user);
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch return cmd_setmetadata_start(ctx);
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch}
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Boschstatic bool
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Boschcmd_setmetadata_mailbox(struct imap_setmetadata_context *ctx,
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch const char *mailbox)
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch{
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch struct client_command_context *cmd = ctx->cmd;
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch struct client *client = cmd->client;
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch struct mail_namespace *ns;
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch ns = client_find_namespace(cmd, &mailbox);
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch if (ns == NULL)
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch return TRUE;
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch if (client->mailbox != NULL && !client->mailbox_examined &&
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch mailbox_equals(client->mailbox, ns, mailbox))
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch ctx->box = client->mailbox;
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch else {
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch ctx->box = mailbox_alloc(ns->list, mailbox, 0);
61cf001f1944d92eb25f113ba4c08985d6e30d53Timo Sirainen mailbox_set_reason(ctx->box, "SETMETADATA");
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch if (mailbox_open(ctx->box) < 0) {
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch client_send_box_error(cmd, ctx->box);
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch mailbox_free(&ctx->box);
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch return TRUE;
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch }
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch }
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch ctx->trans = imap_metadata_transaction_begin(ctx->box);
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch return cmd_setmetadata_start(ctx);
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch}
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainenbool cmd_setmetadata(struct client_command_context *cmd)
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen{
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen struct imap_setmetadata_context *ctx;
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen const struct imap_arg *args;
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen const char *mailbox;
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen int ret;
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen ret = imap_parser_read_args(cmd->parser, 2,
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen IMAP_PARSE_FLAG_STOP_AT_LIST, &args);
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen if (ret == -1) {
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen client_send_command_error(cmd, NULL);
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen return TRUE;
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen }
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen if (ret == -2)
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen return FALSE;
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen if (!imap_arg_get_astring(&args[0], &mailbox) ||
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen args[1].type != IMAP_ARG_LIST) {
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen client_send_command_error(cmd, "Invalid arguments.");
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen return TRUE;
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen }
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen if (!cmd->client->imap_metadata_enabled) {
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen client_send_command_error(cmd, "METADATA disabled.");
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen return TRUE;
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen }
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen ctx = p_new(cmd->pool, struct imap_setmetadata_context, 1);
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen ctx->cmd = cmd;
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen ctx->cmd->context = ctx;
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen if (mailbox[0] == '\0') {
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen /* server attribute */
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch return cmd_setmetadata_server(ctx);
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen }
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen
8ccdf195768afdfbc32088d7be77dfca7dddd829Stephan Bosch return cmd_setmetadata_mailbox(ctx, mailbox);
02c75e04c6ff80726bb59e3ea34a7995ad1f6f7cTimo Sirainen}