Example #1
0
void client_command_free(struct client_command_context **_cmd)
{
	struct client_command_context *cmd = *_cmd;
	struct client *client = cmd->client;
	enum client_command_state state = cmd->state;

	*_cmd = NULL;

	i_assert(client->output_cmd_lock == NULL);

	/* reset input idle time because command output might have taken a
	   long time and we don't want to disconnect client immediately then */
	client->last_input = ioloop_time;
	timeout_reset(client->to_idle);

	if (cmd->cancel) {
		cmd->cancel = FALSE;
		client_send_tagline(cmd, "NO Command cancelled.");
	}

	if (!cmd->param_error)
		client->bad_counter = 0;

	if (client->input_lock == cmd)
		client->input_lock = NULL;
	if (client->mailbox_change_lock == cmd)
		client->mailbox_change_lock = NULL;

	if (client->free_parser == NULL) {
		imap_parser_reset(cmd->parser);
		client->free_parser = cmd->parser;
	} else if (cmd->parser != NULL) {
		imap_parser_unref(&cmd->parser);
	}

	client->command_queue_size--;
	DLLIST_REMOVE(&client->command_queue, cmd);
	cmd = NULL;

	if (client->command_queue == NULL) {
		/* no commands left in the queue, we can clear the pool */
		p_clear(client->command_pool);
		if (client->to_idle_output != NULL)
			timeout_remove(&client->to_idle_output);
	}
	imap_client_notify_command_freed(client);
	imap_refresh_proctitle();

	/* if command finished from external event, check input for more
	   unhandled commands since we may not be executing from client_input
	   or client_output. */
	if (state == CLIENT_COMMAND_STATE_WAIT_EXTERNAL &&
	    !client->disconnected) {
		client_add_missing_io(client);
		if (client->to_delayed_input == NULL) {
			client->to_delayed_input =
				timeout_add(0, client_input, client);
		}
	}
}
Example #2
0
static bool cmd_setmetadata_continue(struct client_command_context *cmd)
{
    struct imap_setmetadata_context *ctx = cmd->context;
    const char *entry, *error_string;
    enum mail_error error;
    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) {
        if (ctx->box == NULL)
            client_disconnect_if_inconsistent(cmd->client);
        error_string = imap_metadata_transaction_get_last_error
                       (ctx->trans, &error);
        client_send_tagline(cmd,
                            imap_get_error_string(cmd, error_string, error));
    } else if (imap_metadata_transaction_commit(&ctx->trans,
               &error, &error_string) < 0) {
        if (ctx->box == NULL)
            client_disconnect_if_inconsistent(cmd->client);
        client_send_tagline(cmd,
                            imap_get_error_string(cmd, error_string, error));
    } else {
        client_send_tagline(cmd, "OK Setmetadata completed.");
    }
    cmd_setmetadata_deinit(ctx);
    return TRUE;
}
Example #3
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();
}
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;
}
Example #5
0
static bool cmd_append_continue_message(struct client_command_context *cmd)
{
	struct client *client = cmd->client;
	struct cmd_append_context *ctx = cmd->context;
	int ret = 0;

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

	if (ctx->save_ctx != NULL) {
		while (ctx->litinput->v_offset != ctx->literal_size) {
			ret = i_stream_read(ctx->litinput);
			if (mailbox_save_continue(ctx->save_ctx) < 0) {
				/* we still have to finish reading the message
				   from client */
				mailbox_save_cancel(&ctx->save_ctx);
				break;
			}
			if (ret == -1 || ret == 0)
				break;
		}
	}

	if (ctx->save_ctx == NULL) {
		/* saving has already failed, we're just eating away the
		   literal */
		(void)i_stream_read(ctx->litinput);
		i_stream_skip(ctx->litinput,
			      i_stream_get_data_size(ctx->litinput));
	}

	if (ctx->litinput->eof || client->input->closed) {
		uoff_t lit_offset = ctx->litinput->v_offset;

		/* finished - do one more read, to make sure istream-chain
		   unreferences its stream, which is needed for litinput's
		   unreferencing to seek the client->input to correct
		   position. the seek is needed to avoid trying to seek
		   backwards in the ctx->input's parent stream. */
		i_stream_seek(ctx->input, ctx->input->v_offset);
		(void)i_stream_read(ctx->input);
		i_stream_unref(&ctx->litinput);

		if (ctx->failed) {
			if (ctx->save_ctx != NULL)
				mailbox_save_cancel(&ctx->save_ctx);
		} else if (ctx->save_ctx == NULL) {
			/* failed above */
			client_send_box_error(cmd, ctx->box);
			ctx->failed = TRUE;
		} else if (lit_offset != ctx->literal_size) {
			/* client disconnected before it finished sending the
			   whole message. */
			ctx->failed = TRUE;
			mailbox_save_cancel(&ctx->save_ctx);
			client_disconnect(client,
				get_disconnect_reason(ctx, lit_offset));
		} else if (ctx->catenate) {
			/* CATENATE isn't finished yet */
		} else if (mailbox_save_finish(&ctx->save_ctx) < 0) {
			client_send_box_error(cmd, ctx->box);
			ctx->failed = TRUE;
		}

		if (client->input->closed) {
			cmd_append_finish(ctx);
			return TRUE;
		}

		/* prepare for the next message (or its part with catenate) */
		ctx->message_input = FALSE;
		imap_parser_reset(ctx->save_parser);

		if (ctx->catenate) {
			cmd->func = cmd_append_continue_catenate;
			return cmd_append_continue_catenate(cmd);
		}

		i_stream_unref(&ctx->input);
		cmd->func = cmd_append_parse_new_msg;
		return cmd_append_parse_new_msg(cmd);
	}
	return FALSE;
}
Example #6
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 #7
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);
}