Exemple #1
0
static bool client_handle_parser_error(struct imap_client *client,
				       struct imap_parser *parser)
{
	const char *msg;
	enum imap_parser_error parse_error;

	msg = imap_parser_get_error(parser, &parse_error);
	switch (parse_error) {
	case IMAP_PARSE_ERROR_NONE:
		i_unreached();
	case IMAP_PARSE_ERROR_LITERAL_TOO_BIG:
		client_send_reply(&client->common,
				  IMAP_CMD_REPLY_BYE, msg);
		client_destroy(&client->common,
			       t_strconcat("Disconnected: ", msg, NULL));
		return FALSE;
	default:
		break;
	}

	client_send_reply(&client->common, IMAP_CMD_REPLY_BAD, msg);
	client->cmd_finished = TRUE;
	client->skip_line = TRUE;
	return TRUE;
}
Exemple #2
0
ARRAY_TYPE(imap_arg_list) *
test_parse_imap_args(pool_t pool, const char *line, unsigned int linelen,
		     const char **error_r)
{
	struct imap_parser *imap_parser;
	struct istream *input;
	const struct imap_arg *args;
	ARRAY_TYPE(imap_arg_list) *dup_args;
	enum imap_parser_error fatal;
	int ret;

	input = i_stream_create_from_data(line, linelen);
	imap_parser = imap_parser_create(input, NULL, (size_t)-1);
	ret = imap_parser_finish_line(imap_parser, 0,
				      IMAP_PARSE_FLAG_LITERAL8 |
				      IMAP_PARSE_FLAG_LITERAL_TYPE |
				      IMAP_PARSE_FLAG_ATOM_ALLCHARS |
				      IMAP_PARSE_FLAG_MULTILINE_STR, &args);
	if (ret < 0) {
		dup_args = NULL;
		if (ret == -2)
			*error_r = "Missing data";
		else {
			*error_r = t_strdup(imap_parser_get_error(imap_parser,
								  &fatal));
		}
	} else {
		dup_args = test_parse_imap_args_dup(pool, args);
	}
	imap_parser_unref(&imap_parser);
	i_stream_unref(&input);
	return dup_args;
}
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();
}
Exemple #4
0
static bool client_handle_parser_error(struct imap_client *client,
				       struct imap_parser *parser)
{
	const char *msg;
	bool fatal;

	msg = imap_parser_get_error(parser, &fatal);
	if (fatal) {
		client_send_reply(&client->common,
				  IMAP_CMD_REPLY_BYE, msg);
		client_destroy(&client->common,
			       t_strconcat("Disconnected: ", msg, NULL));
		return FALSE;
	}

	client_send_reply(&client->common, IMAP_CMD_REPLY_BAD, msg);
	client->cmd_finished = TRUE;
	client->skip_line = TRUE;
	return TRUE;
}
static struct mail_search_args *
virtual_search_args_parse(const string_t *rule, const char **error_r)
{
	struct istream *input;
	struct imap_parser *imap_parser;
	const struct imap_arg *args;
	struct mail_search_parser *parser;
	struct mail_search_args *sargs;
	const char *charset = "UTF-8";
	bool fatal;
	int ret;

	if (str_len(rule) == 0) {
		sargs = mail_search_build_init();
		mail_search_build_add_all(sargs);
		return sargs;
	}

	input = i_stream_create_from_data(str_data(rule), str_len(rule));
	(void)i_stream_read(input);

	imap_parser = imap_parser_create(input, NULL, (size_t)-1);
	ret = imap_parser_finish_line(imap_parser, 0,  0, &args);
	if (ret < 0) {
		sargs = NULL;
		*error_r = t_strdup(imap_parser_get_error(imap_parser, &fatal));
	} else {
		parser = mail_search_parser_init_imap(args);
		if (mail_search_build(mail_search_register_get_imap(),
				      parser, &charset, &sargs, error_r) < 0)
			sargs = NULL;
		mail_search_parser_deinit(&parser);
	}

	imap_parser_unref(&imap_parser);
	i_stream_destroy(&input);
	return sargs;
}
void client_send_command_error(struct client_command_context *cmd,
			       const char *msg)
{
	struct client *client = cmd->client;
	const char *error, *cmd_name;
	bool fatal;

	if (msg == NULL) {
		msg = imap_parser_get_error(cmd->parser, &fatal);
		if (fatal) {
			client_disconnect_with_error(client, msg);
			return;
		}
	}

	if (cmd->tag == NULL)
		error = t_strconcat("BAD Error in IMAP tag: ", msg, NULL);
	else if (cmd->name == NULL)
		error = t_strconcat("BAD Error in IMAP command: ", msg, NULL);
	else {
		cmd_name = t_str_ucase(cmd->name);
		error = t_strconcat("BAD Error in IMAP command ",
				    cmd_name, ": ", msg, NULL);
	}

	client_send_tagline(cmd, error);

	if (++client->bad_counter >= CLIENT_MAX_BAD_COMMANDS) {
		client_disconnect_with_error(client,
			"Too many invalid IMAP commands.");
	}

	cmd->param_error = TRUE;
	/* client_read_args() failures rely on this being set, so that the
	   command processing is stopped even while command function returns
	   FALSE. */
	cmd->state = CLIENT_COMMAND_STATE_DONE;
}
Exemple #7
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);
}
Exemple #8
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);
}
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;
}