summaryrefslogtreecommitdiffstats
path: root/src/lib-smtp/smtp-server.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib-smtp/smtp-server.h')
-rw-r--r--src/lib-smtp/smtp-server.h802
1 files changed, 802 insertions, 0 deletions
diff --git a/src/lib-smtp/smtp-server.h b/src/lib-smtp/smtp-server.h
new file mode 100644
index 0000000..5c13491
--- /dev/null
+++ b/src/lib-smtp/smtp-server.h
@@ -0,0 +1,802 @@
+#ifndef SMTP_SERVER_H
+#define SMTP_SERVER_H
+
+#include "smtp-common.h"
+#include "smtp-command.h"
+#include "smtp-params.h"
+
+struct smtp_address;
+struct smtp_reply;
+struct smtp_command;
+
+struct smtp_server_helo_data;
+
+struct smtp_server_esmtp_param;
+struct smtp_server_cmd_ehlo;
+struct smtp_server_cmd_mail;
+struct smtp_server_cmd_ctx;
+struct smtp_server_command;
+struct smtp_server_reply;
+struct smtp_server_recipient;
+struct smtp_server_transaction;
+
+struct smtp_server;
+
+/*
+ * Types
+ */
+
+enum smtp_server_state {
+ SMTP_SERVER_STATE_GREETING = 0,
+ SMTP_SERVER_STATE_XCLIENT,
+ SMTP_SERVER_STATE_HELO,
+ SMTP_SERVER_STATE_STARTTLS,
+ SMTP_SERVER_STATE_AUTH,
+ SMTP_SERVER_STATE_READY,
+ SMTP_SERVER_STATE_MAIL_FROM,
+ SMTP_SERVER_STATE_RCPT_TO,
+ SMTP_SERVER_STATE_DATA,
+};
+extern const char *const smtp_server_state_names[];
+
+struct smtp_server_helo_data {
+ const char *domain;
+
+ bool domain_valid:1; /* Valid domain/literal specified */
+ bool old_smtp:1; /* Client sent HELO rather than EHLO */
+};
+
+/*
+ * Recipient
+ */
+
+enum smtp_server_recipient_hook_type {
+ /* approved: the server is about to approve this recipient by sending
+ a success reply to the RCPT command. */
+ SMTP_SERVER_RECIPIENT_HOOK_APPROVED,
+ /* data_replied: the DATA command is replied for this recipient */
+ SMTP_SERVER_RECIPIENT_HOOK_DATA_REPLIED,
+ /* destroy: recipient is about to be destroyed. */
+ SMTP_SERVER_RECIPIENT_HOOK_DESTROY
+};
+
+typedef void smtp_server_rcpt_func_t(struct smtp_server_recipient *rcpt,
+ void *context);
+
+struct smtp_server_recipient {
+ pool_t pool;
+ struct smtp_server_connection *conn;
+ struct smtp_server_transaction *trans;
+ struct event *event;
+
+ struct smtp_address *path;
+ struct smtp_params_rcpt params;
+
+ /* The associated RCPT or DATA command (whichever applies). This is NULL
+ when no command is active. */
+ struct smtp_server_cmd_ctx *cmd;
+
+ /* The index in the list of approved recipients */
+ unsigned int index;
+
+ void *context;
+
+ bool replied:1;
+ bool finished:1;
+};
+ARRAY_DEFINE_TYPE(smtp_server_recipient, struct smtp_server_recipient *);
+
+/* Returns the original recipient path if available. Otherwise, it returns the
+ final path. */
+const struct smtp_address *
+smtp_server_recipient_get_original(struct smtp_server_recipient *rcpt);
+
+struct smtp_server_reply *
+smtp_server_recipient_get_reply(struct smtp_server_recipient *rcpt);
+bool smtp_server_recipient_is_replied(struct smtp_server_recipient *rcpt);
+void smtp_server_recipient_replyv(struct smtp_server_recipient *rcpt,
+ unsigned int status, const char *enh_code,
+ const char *fmt, va_list args)
+ ATTR_FORMAT(4, 0);
+void smtp_server_recipient_reply(struct smtp_server_recipient *rcpt,
+ unsigned int status, const char *enh_code,
+ const char *fmt, ...) ATTR_FORMAT(4, 5);
+void smtp_server_recipient_reply_forward(struct smtp_server_recipient *rcpt,
+ const struct smtp_reply *from);
+
+/* Hooks */
+
+void smtp_server_recipient_add_hook(struct smtp_server_recipient *rcpt,
+ enum smtp_server_recipient_hook_type type,
+ smtp_server_rcpt_func_t func,
+ void *context);
+#define smtp_server_recipient_add_hook(_rcpt, _type, _func, _context) \
+ smtp_server_recipient_add_hook((_rcpt), (_type) - \
+ CALLBACK_TYPECHECK(_func, void (*)( \
+ struct smtp_server_recipient *, typeof(_context))), \
+ (smtp_server_rcpt_func_t *)(_func), (_context))
+void smtp_server_recipient_remove_hook(
+ struct smtp_server_recipient *rcpt,
+ enum smtp_server_recipient_hook_type type,
+ smtp_server_rcpt_func_t *func);
+#define smtp_server_recipient_remove_hook(_rcpt, _type, _func) \
+ smtp_server_recipient_remove_hook((_rcpt), (_type), \
+ (smtp_server_rcpt_func_t *)(_func));
+
+/*
+ * Transaction
+ */
+
+enum smtp_server_trace_rcpt_to_address {
+ /* Don't add recipient address to trace header. */
+ SMTP_SERVER_TRACE_RCPT_TO_ADDRESS_NONE,
+ /* Add final recipient address to trace header. */
+ SMTP_SERVER_TRACE_RCPT_TO_ADDRESS_FINAL,
+ /* Add original recipient address to trace header. */
+ SMTP_SERVER_TRACE_RCPT_TO_ADDRESS_ORIGINAL,
+};
+
+enum smtp_server_transaction_flags {
+ SMTP_SERVER_TRANSACTION_FLAG_REPLY_PER_RCPT = BIT(0),
+};
+
+struct smtp_server_transaction {
+ pool_t pool;
+ struct smtp_server_connection *conn;
+ struct event *event;
+ const char *id;
+ struct timeval timestamp;
+
+ enum smtp_server_transaction_flags flags;
+
+ struct smtp_address *mail_from;
+ struct smtp_params_mail params;
+ ARRAY_TYPE(smtp_server_recipient) rcpt_to;
+
+ /* The associated DATA command. This is NULL until the last DATA/BDAT
+ command is issued.
+ */
+ struct smtp_server_cmd_ctx *cmd;
+
+ void *context;
+
+ bool finished:1;
+};
+
+struct smtp_server_recipient *
+smtp_server_transaction_find_rcpt_duplicate(
+ struct smtp_server_transaction *trans,
+ struct smtp_server_recipient *rcpt);
+
+void smtp_server_transaction_fail_data(struct smtp_server_transaction *trans,
+ struct smtp_server_cmd_ctx *data_cmd,
+ unsigned int status,
+ const char *enh_code,
+ const char *fmt, va_list args)
+ ATTR_FORMAT(5, 0);
+
+void smtp_server_transaction_write_trace_record(
+ string_t *str, struct smtp_server_transaction *trans,
+ enum smtp_server_trace_rcpt_to_address rcpt_to_address);
+
+/*
+ * Callbacks
+ */
+
+struct smtp_server_cmd_helo {
+ struct smtp_server_helo_data helo;
+
+ bool first:1; /* This is the first */
+ bool changed:1; /* This EHLO/HELO/LHLO is the first or different
+ from a previous one */
+};
+
+struct smtp_server_cmd_mail {
+ struct smtp_address *path;
+ struct smtp_params_mail params;
+
+ struct timeval timestamp;
+
+ enum smtp_server_transaction_flags flags;
+};
+
+struct smtp_server_cmd_auth {
+ const char *sasl_mech;
+ const char *initial_response;
+};
+
+struct smtp_server_callbacks {
+ /* Command callbacks:
+
+ These are used to override/implement the behavior of the various core
+ SMTP commands. Commands are handled asynchronously, which means that
+ the command is not necessarily finished when the callback ends. A
+ command is finished either when 1 is returned or a reply is submitted
+ for it. When a callback returns 0, the command implementation is
+ waiting for an external event and when it returns -1 an error
+ occurred. When 1 is returned, a default success reply is set if no
+ reply was submitted. Not submitting an error reply when -1 is
+ returned causes an assert fail. Except for RCPT and DATA, all these
+ callbacks are optional to implement; appropriate default behavior is
+ provided.
+
+ The SMTP server API takes care of transaction state checking.
+ However, until all previous commands are handled, a transaction
+ command cannot rely on the transaction state being final. Use
+ cmd->hook_next to get notified when all previous commands are
+ finished and the current command is next in line to reply.
+
+ If the implementation does not need asynchronous behavior, set
+ max_pipelined_commands=1 and don't return 0 from any command handler.
+ */
+
+ /* HELO/EHLO/LHLO */
+ int (*conn_cmd_helo)(void *conn_ctx, struct smtp_server_cmd_ctx *cmd,
+ struct smtp_server_cmd_helo *data);
+ /* STARTTLS */
+ int (*conn_cmd_starttls)(void *conn_ctx,
+ struct smtp_server_cmd_ctx *cmd);
+ /* AUTH */
+ int (*conn_cmd_auth)(void *conn_ctx, struct smtp_server_cmd_ctx *cmd,
+ struct smtp_server_cmd_auth *data);
+ int (*conn_cmd_auth_continue)(void *conn_ctx,
+ struct smtp_server_cmd_ctx *cmd,
+ const char *response);
+ /* MAIL */
+ int (*conn_cmd_mail)(void *conn_ctx, struct smtp_server_cmd_ctx *cmd,
+ struct smtp_server_cmd_mail *data);
+ /* RCPT */
+ int (*conn_cmd_rcpt)(void *conn_ctx, struct smtp_server_cmd_ctx *cmd,
+ struct smtp_server_recipient *rcpt);
+ /* RSET */
+ int (*conn_cmd_rset)(void *conn_ctx, struct smtp_server_cmd_ctx *cmd);
+ /* DATA */
+ int (*conn_cmd_data_begin)(void *conn_ctx,
+ struct smtp_server_cmd_ctx *cmd,
+ struct smtp_server_transaction *trans,
+ struct istream *data_input);
+ int (*conn_cmd_data_continue)(void *conn_ctx,
+ struct smtp_server_cmd_ctx *cmd,
+ struct smtp_server_transaction *trans);
+ /* VRFY */
+ int (*conn_cmd_vrfy)(void *conn_ctx, struct smtp_server_cmd_ctx *cmd,
+ const char *param);
+ /* NOOP */
+ int (*conn_cmd_noop)(void *conn_ctx, struct smtp_server_cmd_ctx *cmd);
+ /* QUIT */
+ int (*conn_cmd_quit)(void *conn_ctx, struct smtp_server_cmd_ctx *cmd);
+ /* XCLIENT */
+ void (*conn_cmd_xclient)(void *conn_ctx,
+ struct smtp_server_cmd_ctx *cmd,
+ struct smtp_proxy_data *data);
+
+ /* Command input callbacks:
+
+ These can be used to do stuff before and after a pipelined group of
+ commands is read.
+ */
+ void (*conn_cmd_input_pre)(void *context);
+ void (*conn_cmd_input_post)(void *context);
+
+ /* Transaction events */
+ void (*conn_trans_start)(void *context,
+ struct smtp_server_transaction *trans);
+ void (*conn_trans_free)(void *context,
+ struct smtp_server_transaction *trans);
+
+ /* Protocol state events */
+ void (*conn_state_changed)(void *context,
+ enum smtp_server_state new_state,
+ const char *new_args) ATTR_NULL(3);
+
+ /* Proxy data */
+ void (*conn_proxy_data_updated)(void *conn_ctx,
+ const struct smtp_proxy_data *data);
+
+ /* Connection */
+ int (*conn_start_tls)(void *conn_ctx,
+ struct istream **input, struct ostream **output);
+ /* Connection is disconnected. This is always called before
+ conn_free(). */
+ void (*conn_disconnect)(void *context, const char *reason);
+ /* The last reference to connection is dropped, causing the connection
+ to be freed. */
+ void (*conn_free)(void *context);
+
+ /* Security */
+ bool (*conn_is_trusted)(void *context);
+};
+
+/*
+ * Server
+ */
+
+enum smtp_server_workarounds {
+ SMTP_SERVER_WORKAROUND_WHITESPACE_BEFORE_PATH = BIT(0),
+ SMTP_SERVER_WORKAROUND_MAILBOX_FOR_PATH = BIT(1)
+};
+
+struct smtp_server_settings {
+ /* The protocol we are serving */
+ enum smtp_protocol protocol;
+ /* Standard capabilities supported by the server */
+ enum smtp_capability capabilities;
+ /* Enabled workarounds for client protocol deviations */
+ enum smtp_server_workarounds workarounds;
+
+ /* Module name for event reason codes. */
+ const char *reason_code_module;
+ /* Our hostname as presented to the client */
+ const char *hostname;
+ /* The message sent in the SMTP server greeting */
+ const char *login_greeting;
+ /* The directory that - if it exists and is accessible - is used to
+ write raw protocol logs for debugging */
+ const char *rawlog_dir;
+
+ /* SSL settings; if NULL, master_service_ssl_init() is used instead */
+ const struct ssl_iostream_settings *ssl;
+
+ /* The maximum time in milliseconds a client is allowed to be idle
+ before it is disconnected. */
+ unsigned int max_client_idle_time_msecs;
+
+ /* Maximum number of commands in pipeline per connection (default = 1)
+ */
+ unsigned int max_pipelined_commands;
+
+ /* Maximum number of sequential bad commands */
+ unsigned int max_bad_commands;
+
+ /* Maximum number of recipients in a transaction
+ (0 means unlimited, which is the default) */
+ unsigned int max_recipients;
+
+ /* Command limits */
+ struct smtp_command_limits command_limits;
+
+ /* Message size limit */
+ uoff_t max_message_size;
+
+ /* Accept these additional custom MAIL parameters */
+ const char *const *mail_param_extensions;
+ /* Accept these additional custom RCPT parameters */
+ const char *const *rcpt_param_extensions;
+ /* Accept these additional custom XCLIENT fields */
+ const char *const *xclient_extensions;
+
+ /* The kernel send/receive buffer sizes used for the connection sockets.
+ Configuring this is mainly useful for the test suite. The kernel
+ defaults are used when these settings are 0. */
+ size_t socket_send_buffer_size;
+ size_t socket_recv_buffer_size;
+
+ /* Event to use for the smtp server. */
+ struct event *event_parent;
+
+ /* Enable logging debug messages */
+ bool debug:1;
+ /* Authentication is not required for this service */
+ bool auth_optional:1;
+ /* TLS security is required for this service */
+ bool tls_required:1;
+ /* The path provided to the MAIL command does not need to be valid. A
+ completely invalid path will parse as <>. Paths that can still be
+ fixed by splitting it on the last `@' yielding a usable localpart and
+ domain, will be parsed as such. There are limits though; when the
+ path is badly delimited or contains control characters, the MAIL
+ command will still fail. The unparsed broken address will be
+ available in the `raw' field of struct smtp_address for logging etc.
+ */
+ bool mail_path_allow_broken:1;
+ /* The path provided to the RCPT command does not need to have the
+ domain part. */
+ bool rcpt_domain_optional:1;
+ /* Don't include "(state=%s)" in the disconnection reason string. */
+ bool no_state_in_reason:1;
+ /* Don't send a greeting or login success message to the client upon
+ connection start. */
+ bool no_greeting:1;
+};
+
+struct smtp_server_stats {
+ unsigned int command_count, reply_count;
+ uoff_t input, output;
+};
+
+/*
+ * Server
+ */
+
+struct smtp_server *smtp_server_init(const struct smtp_server_settings *set);
+void smtp_server_deinit(struct smtp_server **_server);
+
+void smtp_server_switch_ioloop(struct smtp_server *server);
+
+/*
+ * Connection
+ */
+
+/* Create connection. It is still inactive and needs to be started with
+ one of the functions below. */
+struct smtp_server_connection *
+smtp_server_connection_create(
+ struct smtp_server *server, int fd_in, int fd_out,
+ const struct ip_addr *remote_ip, in_port_t remote_port, bool ssl_start,
+ const struct smtp_server_settings *set,
+ const struct smtp_server_callbacks *callbacks, void *context)
+ ATTR_NULL(4, 6, 8);
+struct smtp_server_connection *
+smtp_server_connection_create_from_streams(
+ struct smtp_server *server,
+ struct istream *input, struct ostream *output,
+ const struct ip_addr *remote_ip, in_port_t remote_port,
+ const struct smtp_server_settings *set,
+ const struct smtp_server_callbacks *callbacks, void *context)
+ ATTR_NULL(4, 6, 8);
+
+void smtp_server_connection_ref(struct smtp_server_connection *conn);
+bool smtp_server_connection_unref(struct smtp_server_connection **_conn);
+
+/* Initialize the connection with state and data from login service */
+void smtp_server_connection_login(struct smtp_server_connection *conn,
+ const char *username, const char *helo,
+ const unsigned char *pdata,
+ unsigned int pdata_len, bool ssl_secured);
+
+/* Start the connection. Establishes SSL layer immediately if instructed,
+ and sends the greeting once the connection is ready for commands. */
+void smtp_server_connection_start(struct smtp_server_connection *conn);
+/* Start the connection, but only establish SSL layer and send greeting;
+ handling command input is held off until smtp_server_connection_resume() is
+ called. */
+void smtp_server_connection_start_pending(struct smtp_server_connection *conn);
+/* Abort the connection prematurely (before it is started). */
+void smtp_server_connection_abort(struct smtp_server_connection **_conn,
+ unsigned int status, const char *enh_code,
+ const char *reason);
+
+/* Halt connection command input and idle timeout entirely. */
+void smtp_server_connection_halt(struct smtp_server_connection *conn);
+/* Resume connection command input and idle timeout. */
+void smtp_server_connection_resume(struct smtp_server_connection *conn);
+
+void smtp_server_connection_input_lock(struct smtp_server_connection *conn);
+void smtp_server_connection_input_unlock(struct smtp_server_connection *conn);
+
+void smtp_server_connection_set_streams(struct smtp_server_connection *conn,
+ struct istream *input,
+ struct ostream *output);
+void smtp_server_connection_set_ssl_streams(struct smtp_server_connection *conn,
+ struct istream *input,
+ struct ostream *output);
+
+void smtp_server_connection_close(struct smtp_server_connection **_conn,
+ const char *reason) ATTR_NULL(2);
+void smtp_server_connection_terminate(struct smtp_server_connection **_conn,
+ const char *enh_code, const char *reason)
+ ATTR_NULL(3);
+
+bool smtp_server_connection_data_check_state(struct smtp_server_cmd_ctx *cmd);
+void smtp_server_connection_data_chunk_init(struct smtp_server_cmd_ctx *cmd);
+int smtp_server_connection_data_chunk_add(struct smtp_server_cmd_ctx *cmd,
+ struct istream *chunk,
+ uoff_t chunk_size, bool chunk_last,
+ bool client_input);
+
+enum smtp_server_state
+smtp_server_connection_get_state(struct smtp_server_connection *conn,
+ const char **args_r) ATTR_NULL(2);
+const char *
+smtp_server_connection_get_security_string(struct smtp_server_connection *conn);
+struct smtp_server_transaction *
+smtp_server_connection_get_transaction(struct smtp_server_connection *conn);
+const char *
+smtp_server_connection_get_transaction_id(struct smtp_server_connection *conn);
+const struct smtp_server_stats *
+smtp_server_connection_get_stats(struct smtp_server_connection *conn);
+void *smtp_server_connection_get_context(struct smtp_server_connection *conn)
+ ATTR_PURE;
+enum smtp_protocol
+smtp_server_connection_get_protocol(struct smtp_server_connection *conn)
+ ATTR_PURE;
+const char *
+smtp_server_connection_get_protocol_name(struct smtp_server_connection *conn);
+struct smtp_server_helo_data *
+smtp_server_connection_get_helo_data(struct smtp_server_connection *conn);
+
+void smtp_server_connection_get_proxy_data(struct smtp_server_connection *conn,
+ struct smtp_proxy_data *proxy_data);
+void smtp_server_connection_set_proxy_data(
+ struct smtp_server_connection *conn,
+ const struct smtp_proxy_data *proxy_data);
+
+void smtp_server_connection_set_capabilities(
+ struct smtp_server_connection *conn, enum smtp_capability capabilities);
+void smtp_server_connection_add_extra_capability(
+ struct smtp_server_connection *conn,
+ const struct smtp_capability_extra *cap);
+
+void smtp_server_connection_register_mail_param(
+ struct smtp_server_connection *conn, const char *param);
+void smtp_server_connection_register_rcpt_param(
+ struct smtp_server_connection *conn, const char *param);
+
+bool smtp_server_connection_is_ssl_secured(struct smtp_server_connection *conn);
+bool smtp_server_connection_is_trusted(struct smtp_server_connection *conn);
+
+/*
+ * Command
+ */
+
+enum smtp_server_command_flags {
+ SMTP_SERVER_CMD_FLAG_PRETLS = BIT(0),
+ SMTP_SERVER_CMD_FLAG_PREAUTH = BIT(1)
+};
+
+enum smtp_server_command_hook_type {
+ /* next: command is next to reply but has not submittted all replies
+ yet. */
+ SMTP_SERVER_COMMAND_HOOK_NEXT,
+ /* replied_one: command has submitted one reply. */
+ SMTP_SERVER_COMMAND_HOOK_REPLIED_ONE,
+ /* replied: command has submitted all replies. */
+ SMTP_SERVER_COMMAND_HOOK_REPLIED,
+ /* completed: server is about to send last replies for this command. */
+ SMTP_SERVER_COMMAND_HOOK_COMPLETED,
+ /* destroy: command is about to be destroyed. */
+ SMTP_SERVER_COMMAND_HOOK_DESTROY
+};
+
+/* Commands are handled asynchronously, which means that the command is not
+ necessary finished when the start function ends. A command is finished
+ when a reply is submitted for it. Several command hooks are available to
+ get notified about events in the command's life cycle.
+ */
+
+typedef void smtp_server_cmd_input_callback_t(struct smtp_server_cmd_ctx *cmd);
+typedef void smtp_server_cmd_start_func_t(struct smtp_server_cmd_ctx *cmd,
+ const char *params);
+typedef void smtp_server_cmd_func_t(struct smtp_server_cmd_ctx *cmd,
+ void *context);
+
+struct smtp_server_cmd_ctx {
+ pool_t pool;
+ struct event *event;
+ const char *name;
+
+ struct smtp_server *server;
+ struct smtp_server_connection *conn;
+ struct smtp_server_command *cmd;
+};
+
+/* Hooks:
+
+ */
+
+void smtp_server_command_add_hook(struct smtp_server_command *cmd,
+ enum smtp_server_command_hook_type type,
+ smtp_server_cmd_func_t func,
+ void *context);
+#define smtp_server_command_add_hook(_cmd, _type, _func, _context) \
+ smtp_server_command_add_hook((_cmd), (_type) - \
+ CALLBACK_TYPECHECK(_func, void (*)( \
+ struct smtp_server_cmd_ctx *, typeof(_context))), \
+ (smtp_server_cmd_func_t *)(_func), (_context))
+void smtp_server_command_remove_hook(struct smtp_server_command *cmd,
+ enum smtp_server_command_hook_type type,
+ smtp_server_cmd_func_t *func);
+#define smtp_server_command_remove_hook(_cmd, _type, _func) \
+ smtp_server_command_remove_hook((_cmd), (_type), \
+ (smtp_server_cmd_func_t *)(_func));
+
+/* The core SMTP commands are pre-registered. Special connection callbacks are
+ provided for the core SMTP commands. Only use this command registration API
+ when custom/extension SMTP commands are required. It is also possible to
+ completely override the default implementations.
+ */
+void smtp_server_command_register(struct smtp_server *server, const char *name,
+ smtp_server_cmd_start_func_t *func,
+ enum smtp_server_command_flags);
+void smtp_server_command_unregister(struct smtp_server *server,
+ const char *name);
+void smtp_server_command_override(struct smtp_server *server, const char *name,
+ smtp_server_cmd_start_func_t *func,
+ enum smtp_server_command_flags flags);
+
+void smtp_server_command_set_reply_count(struct smtp_server_command *cmd,
+ unsigned int count);
+unsigned int
+smtp_server_command_get_reply_count(struct smtp_server_command *cmd);
+
+void smtp_server_command_fail(struct smtp_server_command *cmd,
+ unsigned int status, const char *enh_code,
+ const char *fmt, ...) ATTR_FORMAT(4, 5);
+
+struct smtp_server_reply *
+smtp_server_command_get_reply(struct smtp_server_command *cmd,
+ unsigned int idx);
+bool smtp_server_command_reply_status_equals(struct smtp_server_command *cmd,
+ unsigned int status);
+bool smtp_server_command_is_replied(struct smtp_server_command *cmd);
+bool smtp_server_command_reply_is_forwarded(struct smtp_server_command *cmd);
+bool smtp_server_command_replied_success(struct smtp_server_command *cmd);
+
+void smtp_server_command_input_lock(struct smtp_server_cmd_ctx *cmd);
+void smtp_server_command_input_unlock(struct smtp_server_cmd_ctx *cmd);
+void smtp_server_command_input_capture(
+ struct smtp_server_cmd_ctx *cmd,
+ smtp_server_cmd_input_callback_t *callback);
+
+void smtp_server_command_pipeline_block(struct smtp_server_cmd_ctx *cmd);
+void smtp_server_command_pipeline_unblock(struct smtp_server_cmd_ctx *cmd);
+
+/* EHLO */
+
+void smtp_server_cmd_ehlo(struct smtp_server_cmd_ctx *cmd, const char *params);
+void smtp_server_cmd_helo(struct smtp_server_cmd_ctx *cmd, const char *params);
+
+struct smtp_server_reply *
+smtp_server_cmd_ehlo_reply_create(struct smtp_server_cmd_ctx *cmd);
+void smtp_server_cmd_ehlo_reply_default(struct smtp_server_cmd_ctx *cmd);
+
+/* STARTTLS */
+
+void smtp_server_cmd_starttls(struct smtp_server_cmd_ctx *cmd,
+ const char *params);
+
+/* AUTH */
+
+void smtp_server_cmd_auth(struct smtp_server_cmd_ctx *cmd, const char *params);
+
+void smtp_server_cmd_auth_send_challenge(struct smtp_server_cmd_ctx *cmd,
+ const char *challenge);
+void smtp_server_cmd_auth_success(struct smtp_server_cmd_ctx *cmd,
+ const char *username, const char *success_msg)
+ ATTR_NULL(3);
+
+/* MAIL */
+
+void smtp_server_cmd_mail(struct smtp_server_cmd_ctx *cmd, const char *params);
+
+void smtp_server_cmd_mail_reply_success(struct smtp_server_cmd_ctx *cmd);
+
+/* RCPT */
+
+void smtp_server_cmd_rcpt(struct smtp_server_cmd_ctx *cmd, const char *params);
+
+bool smtp_server_command_is_rcpt(struct smtp_server_cmd_ctx *cmd);
+void smtp_server_cmd_rcpt_reply_success(struct smtp_server_cmd_ctx *cmd);
+
+/* RSET */
+
+void smtp_server_cmd_rset(struct smtp_server_cmd_ctx *cmd, const char *params);
+
+void smtp_server_cmd_rset_reply_success(struct smtp_server_cmd_ctx *cmd);
+
+/* DATA */
+
+void smtp_server_cmd_data(struct smtp_server_cmd_ctx *cmd, const char *params);
+void smtp_server_cmd_bdat(struct smtp_server_cmd_ctx *cmd, const char *params);
+
+bool smtp_server_cmd_data_check_size(struct smtp_server_cmd_ctx *cmd);
+
+/* VRFY */
+
+void smtp_server_cmd_vrfy(struct smtp_server_cmd_ctx *cmd, const char *params);
+
+void smtp_server_cmd_vrfy_reply_default(struct smtp_server_cmd_ctx *cmd);
+
+/* NOOP */
+
+void smtp_server_cmd_noop(struct smtp_server_cmd_ctx *cmd, const char *params);
+
+void smtp_server_cmd_noop_reply_success(struct smtp_server_cmd_ctx *cmd);
+
+/* QUIT */
+
+void smtp_server_cmd_quit(struct smtp_server_cmd_ctx *cmd, const char *params);
+
+/* XCLIENT */
+
+void smtp_server_cmd_xclient(struct smtp_server_cmd_ctx *cmd,
+ const char *params);
+
+/*
+ * Reply
+ */
+
+struct smtp_server_reply *
+smtp_server_reply_create_index(struct smtp_server_command *cmd,
+ unsigned int index, unsigned int status,
+ const char *enh_code) ATTR_NULL(3);
+struct smtp_server_reply *
+smtp_server_reply_create(struct smtp_server_command *cmd, unsigned int status,
+ const char *enh_code) ATTR_NULL(3);
+struct smtp_server_reply *
+smtp_server_reply_create_forward(struct smtp_server_command *cmd,
+ unsigned int index,
+ const struct smtp_reply *from);
+
+void smtp_server_reply_set_status(struct smtp_server_reply *reply,
+ unsigned int status, const char *enh_code)
+ ATTR_NULL(3);
+unsigned int smtp_server_reply_get_status(struct smtp_server_reply *reply,
+ const char **enh_code_r) ATTR_NULL(3);
+
+void smtp_server_reply_add_text(struct smtp_server_reply *reply,
+ const char *line);
+void smtp_server_reply_prepend_text(struct smtp_server_reply *reply,
+ const char *text_prefix);
+void smtp_server_reply_replace_path(struct smtp_server_reply *reply,
+ struct smtp_address *path, bool add);
+
+void smtp_server_reply_submit(struct smtp_server_reply *reply);
+void smtp_server_reply_submit_duplicate(struct smtp_server_cmd_ctx *_cmd,
+ unsigned int index,
+ unsigned int from_index);
+
+/* Submit a reply for the command at the specified index (> 0 only if more than
+ a single reply is expected). */
+void smtp_server_reply_indexv(struct smtp_server_cmd_ctx *_cmd,
+ unsigned int index, unsigned int status,
+ const char *enh_code,
+ const char *fmt, va_list args) ATTR_FORMAT(5, 0);
+void smtp_server_reply_index(struct smtp_server_cmd_ctx *_cmd,
+ unsigned int index, unsigned int status,
+ const char *enh_code, const char *fmt, ...)
+ ATTR_FORMAT(5, 6);
+/* Submit the reply for the specified command. */
+void smtp_server_reply(struct smtp_server_cmd_ctx *_cmd, unsigned int status,
+ const char *enh_code, const char *fmt, ...)
+ ATTR_FORMAT(4, 5);
+/* Forward a reply for the command at the specified index (> 0 only if more
+ than a single reply is expected). */
+void smtp_server_reply_index_forward(struct smtp_server_cmd_ctx *cmd,
+ unsigned int index,
+ const struct smtp_reply *from);
+/* Forward the reply for the specified command. */
+void smtp_server_reply_forward(struct smtp_server_cmd_ctx *cmd,
+ const struct smtp_reply *from);
+/* Submit the same message for all expected replies for this command. */
+void smtp_server_reply_all(struct smtp_server_cmd_ctx *_cmd,
+ unsigned int status, const char *enh_code,
+ const char *fmt, ...) ATTR_FORMAT(4, 5);
+/* Submit and send the same message for all expected replies for this command
+ early; i.e., no matter whether all command data is received completely. */
+void smtp_server_reply_early(struct smtp_server_cmd_ctx *_cmd,
+ unsigned int status, const char *enh_code,
+ const char *fmt, ...) ATTR_FORMAT(4, 5);
+
+/* Reply the command with a 221 bye message */
+void smtp_server_reply_quit(struct smtp_server_cmd_ctx *_cmd);
+
+bool smtp_server_reply_is_success(const struct smtp_server_reply *reply);
+
+/* EHLO */
+
+struct smtp_server_reply *
+smtp_server_reply_create_ehlo(struct smtp_server_command *cmd);
+void smtp_server_reply_ehlo_add(struct smtp_server_reply *reply,
+ const char *keyword);
+void smtp_server_reply_ehlo_add_param(struct smtp_server_reply *reply,
+ const char *keyword,
+ const char *param_fmt, ...)
+ ATTR_FORMAT(3, 4);
+void smtp_server_reply_ehlo_add_params(struct smtp_server_reply *reply,
+ const char *keyword,
+ const char *const *params) ATTR_NULL(3);
+
+void smtp_server_reply_ehlo_add_8bitmime(struct smtp_server_reply *reply);
+void smtp_server_reply_ehlo_add_binarymime(struct smtp_server_reply *reply);
+void smtp_server_reply_ehlo_add_chunking(struct smtp_server_reply *reply);
+void smtp_server_reply_ehlo_add_dsn(struct smtp_server_reply *reply);
+void smtp_server_reply_ehlo_add_enhancedstatuscodes(
+ struct smtp_server_reply *reply);
+void smtp_server_reply_ehlo_add_pipelining(struct smtp_server_reply *reply);
+void smtp_server_reply_ehlo_add_size(struct smtp_server_reply *reply);
+void smtp_server_reply_ehlo_add_starttls(struct smtp_server_reply *reply);
+void smtp_server_reply_ehlo_add_vrfy(struct smtp_server_reply *reply);
+void smtp_server_reply_ehlo_add_xclient(struct smtp_server_reply *reply);
+
+#endif