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;
}
void client_send_untagged_storage_error(struct client *client,
					struct mail_storage *storage)
{
	const char *error_string;
	enum mail_error error;

	error_string = mail_storage_get_last_error(storage, &error);
	client_send_line(client, t_strconcat("* NO ", error_string, NULL));

	if (client->mailbox != NULL &&
	    mailbox_is_inconsistent(client->mailbox)) {
		/* we can't do forced CLOSE, so have to disconnect */
		client_disconnect_with_error(client,
			"IMAP session state is inconsistent, please relogin.");
	}
}
void client_send_storage_error(struct client_command_context *cmd,
			       struct mail_storage *storage)
{
	const char *error_string;
	enum mail_error error;

	error_string = mail_storage_get_last_error(storage, &error);
	client_send_tagline(cmd, imap_get_error_string(cmd, error_string,
						       error));

	if (cmd->client->mailbox != NULL &&
	    mailbox_is_inconsistent(cmd->client->mailbox)) {
		/* we can't do forced CLOSE, so have to disconnect */
		client_disconnect_with_error(cmd->client,
			"IMAP session state is inconsistent, please relogin.");
	}
}
Beispiel #4
0
static bool cmd_fetch_finish(struct imap_fetch_context *ctx,
			     struct client_command_context *cmd)
{
	static const char *ok_message = "OK Fetch completed.";
	const char *tagged_reply = ok_message;
	enum mail_error error;
	bool failed, seen_flags_changed = ctx->state.seen_flags_changed;

	if (ctx->state.skipped_expunged_msgs) {
		tagged_reply = "OK ["IMAP_RESP_CODE_EXPUNGEISSUED"] "
			"Some messages were already expunged.";
	}

	failed = imap_fetch_end(ctx) < 0;
	imap_fetch_free(&ctx);

	if (failed) {
		const char *errstr;

		if (cmd->client->output->closed) {
			client_disconnect(cmd->client, "Disconnected");
			return TRUE;
		}

		errstr = mailbox_get_last_error(cmd->client->mailbox, &error);
		if (error == MAIL_ERROR_CONVERSION ||
		    error == MAIL_ERROR_INVALIDDATA) {
			/* a) BINARY found unsupported Content-Transfer-Encoding
			   b) Content was invalid */
			tagged_reply = t_strdup_printf(
				"NO ["IMAP_RESP_CODE_UNKNOWN_CTE"] %s", errstr);
		} else {
			/* We never want to reply NO to FETCH requests,
			   BYE is preferrable (see imap-ml for reasons). */
			client_disconnect_with_error(cmd->client, errstr);
			return TRUE;
		}
	}

	return cmd_sync(cmd,
			(seen_flags_changed ? 0 : MAILBOX_SYNC_FLAG_FAST) |
			(cmd->uid ? 0 : MAILBOX_SYNC_FLAG_NO_EXPUNGES), 0,
			tagged_reply);
}
Beispiel #5
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);
}
Beispiel #6
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);
}
Beispiel #7
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;
}