Example #1
0
bool client_read_args(struct client_command_context *cmd, unsigned int count,
		      unsigned int flags, const struct imap_arg **args_r)
{
	string_t *str;
	int ret;

	i_assert(count <= INT_MAX);

	ret = imap_parser_read_args(cmd->parser, count, flags, args_r);
	if (ret >= (int)count) {
		/* all parameters read successfully */
		i_assert(cmd->client->input_lock == NULL ||
			 cmd->client->input_lock == cmd);

		str = t_str_new(256);
		imap_write_args(str, *args_r);
		cmd->args = p_strdup(cmd->pool, str_c(str));
		cmd->start_time = ioloop_timeval;

		cmd->client->input_lock = NULL;
		return TRUE;
	} else if (ret == -2) {
		/* need more data */
		if (cmd->client->input->closed) {
			/* disconnected */
			cmd->state = CLIENT_COMMAND_STATE_DONE;
		}
		return FALSE;
	} else {
		/* error, or missing arguments */
		client_send_command_error(cmd, ret < 0 ? NULL :
					  "Missing arguments");
		return FALSE;
	}
}
Example #2
0
static void test_imap_parser_crlf(void)
{
	static const char *test_input = "foo\r\nx\ry\n";
	struct istream *input;
	struct imap_parser *parser;
	const struct imap_arg *args;
	unsigned int i;
	bool fatal;

	test_begin("imap parser crlf handling");
	input = test_istream_create(test_input);
	parser = imap_parser_create(input, NULL, 1024);

	/* must return -2 until LF is read */
	for (i = 0; test_input[i] != '\n'; i++) {
		test_istream_set_size(input, i+1);
		(void)i_stream_read(input);
		test_assert(imap_parser_read_args(parser, 0, 0, &args) == -2);
	}
	test_istream_set_size(input, i+1);
	(void)i_stream_read(input);
	test_assert(imap_parser_read_args(parser, 0, 0, &args) == 1);
	test_assert(args[0].type == IMAP_ARG_ATOM);
	test_assert(args[1].type == IMAP_ARG_EOL);

	/* CR without LF should fail with error */
	imap_parser_reset(parser);
	i_stream_seek(input, ++i);
	test_istream_set_size(input, ++i);
	(void)i_stream_read(input);
	test_assert(imap_parser_read_args(parser, 0, 0, &args) == -2);
	test_istream_set_size(input, ++i);
	(void)i_stream_read(input);
	test_assert(imap_parser_read_args(parser, 0, 0, &args) == -2);
	test_istream_set_size(input, ++i);
	(void)i_stream_read(input);
	test_assert(imap_parser_read_args(parser, 0, 0, &args) == -1);
	test_assert(strcmp(imap_parser_get_error(parser, &fatal), "CR sent without LF") == 0 && !fatal);

	imap_parser_destroy(&parser);
	i_stream_destroy(&input);
	test_end();
}
Example #3
0
bool cmd_setmetadata(struct client_command_context *cmd)
{
    struct imap_setmetadata_context *ctx;
    const struct imap_arg *args;
    const char *mailbox;
    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 */
        return cmd_setmetadata_server(ctx);
    }

    return cmd_setmetadata_mailbox(ctx, mailbox);
}
Example #4
0
static bool cmd_append_parse_new_msg(struct client_command_context *cmd)
{
	struct client *client = cmd->client;
	struct cmd_append_context *ctx = cmd->context;
	const struct imap_arg *args;
	const char *msg;
	unsigned int arg_min_count;
	bool fatal, nonsync, last_literal;
	int ret;

	/* this function gets called 1) after parsing APPEND <mailbox> and
	   2) with MULTIAPPEND extension after already saving one or more
	   mails. */
	if (cmd->cancel) {
		/* cancel the command immediately (disconnection) */
		cmd_append_finish(ctx);
		return TRUE;
	}

	/* if error occurs, the CRLF is already read. */
	client->input_skip_line = FALSE;

	/* parse the entire line up to the first message literal, or in case
	   the input buffer is full of MULTIAPPEND CATENATE URLs, parse at
	   least until the beginning of the next message */
	arg_min_count = 0; last_literal = FALSE;
	do {
		if (!last_literal)
			arg_min_count++;
		else {
			/* we only read the literal size. now we read the
			   literal itself. */
		}
		ret = imap_parser_read_args(ctx->save_parser, arg_min_count,
					    IMAP_PARSE_FLAG_LITERAL_SIZE |
					    IMAP_PARSE_FLAG_LITERAL8, &args);
	} while (ret >= (int)arg_min_count &&
		 !cmd_append_args_can_stop(ctx, args, &last_literal));
	if (ret == -1) {
		if (!ctx->failed) {
			msg = imap_parser_get_error(ctx->save_parser, &fatal);
			if (fatal)
				client_disconnect_with_error(client, msg);
			else
				client_send_command_error(cmd, msg);
		}
		cmd_append_finish(ctx);
		return TRUE;
	}
	if (ret < 0) {
		/* need more data */
		return FALSE;
	}

	if (IMAP_ARG_IS_EOL(args)) {
		/* last message */
		return cmd_append_finish_parsing(cmd);
	}

	ret = cmd_append_handle_args(cmd, args, &nonsync);
	if (ret < 0) {
		/* invalid parameters, abort immediately */
		cmd_append_finish(ctx);
		return TRUE;
	}
	if (ret == 0) {
		/* CATENATE contained only URLs. Finish it and see if there
		   are more messsages. */
		cmd_append_finish_catenate(cmd);
		imap_parser_reset(ctx->save_parser);
		return cmd_append_parse_new_msg(cmd);
	}

	if (!nonsync) {
		if (!cmd_append_send_literal_continue(ctx)) {
			cmd_append_finish(ctx);
			return TRUE;
		}
	}

	i_assert(ctx->litinput != NULL);
	ctx->message_input = TRUE;
	cmd->func = cmd_append_continue_message;
	return cmd_append_continue_message(cmd);
}
Example #5
0
static bool cmd_append_continue_catenate(struct client_command_context *cmd)
{
	struct client *client = cmd->client;
	struct cmd_append_context *ctx = cmd->context;
	const struct imap_arg *args;
	const char *msg;
	bool fatal, nonsync = FALSE;
	int ret;

	if (cmd->cancel) {
		/* cancel the command immediately (disconnection) */
		cmd_append_finish(ctx);
		return TRUE;
	}

	/* we're parsing inside CATENATE (..) list after handling a TEXT part.
	   it's fine that this would need to fully fit into input buffer
	   (although clients attempting to DoS could simply insert an extra
	   {1+} between the URLs) */
	do {
		ret = imap_parser_read_args(ctx->save_parser, 0,
					    IMAP_PARSE_FLAG_LITERAL_SIZE |
					    IMAP_PARSE_FLAG_LITERAL8 |
					    IMAP_PARSE_FLAG_INSIDE_LIST, &args);
	} while (ret > 0 && !catenate_args_can_stop(ctx, args));
	if (ret == -1) {
		msg = imap_parser_get_error(ctx->save_parser, &fatal);
		if (fatal)
			client_disconnect_with_error(client, msg);
		else if (!ctx->failed)
			client_send_command_error(cmd, msg);
		client->input_skip_line = TRUE;
		cmd_append_finish(ctx);
		return TRUE;
	}
	if (ret < 0) {
		/* need more data */
		return FALSE;
	}

	if ((ret = cmd_append_catenate(cmd, args, &nonsync)) < 0) {
		/* invalid parameters, abort immediately */
		cmd_append_finish(ctx);
		return TRUE;
	}

	if (ret == 0) {
		/* ")" */
		cmd_append_finish_catenate(cmd);

		/* last catenate part */
		imap_parser_reset(ctx->save_parser);
		cmd->func = cmd_append_parse_new_msg;
		return cmd_append_parse_new_msg(cmd);
	}

	/* TEXT <literal> */

	if (!nonsync) {
		if (!cmd_append_send_literal_continue(ctx)) {
			cmd_append_finish(ctx);
			return TRUE;
		}
	}

	i_assert(ctx->litinput != NULL);
	ctx->message_input = TRUE;
	cmd->func = cmd_append_continue_message;
	return cmd_append_continue_message(cmd);
}
Example #6
0
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;
    }
    if (args[1].type == IMAP_ARG_EOL) {
        client_send_command_error(ctx->cmd, "Entry value missing");
        return -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(name, &error)) {
        client_send_command_error(ctx->cmd, error);
        ctx->cmd_error_sent = TRUE;
    }
    if (ctx->cmd_error_sent) {
        ctx->cmd->param_error = FALSE;
        ctx->cmd->state = CLIENT_COMMAND_STATE_WAIT_INPUT;

        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;
}
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);
}