#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