smtp-server.h revision 912e87d5be9dd8895e8cb7c6cb51d8a752edbe8c
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainenextern const char *const smtp_server_state_names[];
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen bool domain_valid:1; /* valid domain/literal specified */
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen bool old_smtp:1; /* client sent HELO rather than EHLO */
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen * Transaction
48566ca412a7cf3b42512fd0ec112744778e5da0Timo SirainenARRAY_DEFINE_TYPE(smtp_server_recipient, struct smtp_server_recipient *);
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch const char *id;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen const char *fmt, va_list args) ATTR_FORMAT(5, 0);
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainenvoid smtp_server_transaction_write_trace_record(string_t *str,
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen bool changed:1; /* this EHLO/HELO/LHLO is the first or
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen different from a previous one */
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen /* called once the recipient is definitively added to the transaction */
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch void (*hook_finished)(struct smtp_server_cmd_ctx *cmd,
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch unsigned int index);
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch /* Command callbacks:
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch These are used to override/implement the behavior of the various core
53ec1ff2231d477db3103c51987fa9cb6033bc16Timo Sirainen SMTP commands. Commands are handled asynchronously, which means that
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen the command is not necessarily finished when the callback ends. A
c215ca02d468b0e542523df1ed18e5f2d7e63968Timo Sirainen command is finished either when 1 is returned or a reply is submitted
efe78d3ba24fc866af1c79b9223dc0809ba26cadStephan Bosch for it. When a callback returns 0, the command implementation is
c215ca02d468b0e542523df1ed18e5f2d7e63968Timo Sirainen waiting for an external event and when it returns -1 an error
c215ca02d468b0e542523df1ed18e5f2d7e63968Timo Sirainen occurred. When 1 is returned, a default success reply is set if no
c215ca02d468b0e542523df1ed18e5f2d7e63968Timo Sirainen reply was submitted. Not submitting an error reply when -1 is
c215ca02d468b0e542523df1ed18e5f2d7e63968Timo Sirainen returned causes an assert fail. Except for RCPT and DATA, all these
c215ca02d468b0e542523df1ed18e5f2d7e63968Timo Sirainen callbacks are optional to implement; appropriate default behavior is
88b90ce9dfe1056d1ec5497b95592d30a849e5f1Timo Sirainen The SMTP server API takes care of transaction state checking.
c215ca02d468b0e542523df1ed18e5f2d7e63968Timo Sirainen However, until all previous commands are handled, a transaction
c215ca02d468b0e542523df1ed18e5f2d7e63968Timo Sirainen command cannot rely on the transaction state being final. Use
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen cmd->hook_next to get notified when all previous commands are
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen finished and the current command is next in line to reply.
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen If the implementation does not need asynchronous behavior, set
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen max_pipelined_commands=1 and don't return 0 from any command handler.
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen /* STARTTLS */
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen struct smtp_server_cmd_ctx *cmd, const char *response);
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen const char *param);
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen /* XCLIENT */
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen /* Command input callbacks:
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen These can be used to do stuff before and after a pipelined group of
a0c453a8edaec90fb0d945c874de0b1845bc7d7eTimo Sirainen commands is read.
a0c453a8edaec90fb0d945c874de0b1845bc7d7eTimo Sirainen /* Transaction events */
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen /* Protocol state events */
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen /* Proxy data */
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen void (*conn_proxy_data_updated)(void *conn_ctx,
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen /* Connection */
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen struct istream **input, struct ostream **output);
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen void (*conn_disconnect)(void *context, const char *reason);
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen /* Security */
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen /* The maximum time in milliseconds a client is allowed to be idle
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen before it is disconnected. */
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen /* maximum number of commands in pipeline per connection (default = 1)
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen /* maximum number of sequential bad commands */
a0c453a8edaec90fb0d945c874de0b1845bc7d7eTimo Sirainen /* maximum number of recipients in a transaction
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen (0 means unlimited, which is the default) */
a0c453a8edaec90fb0d945c874de0b1845bc7d7eTimo Sirainen /* command limits */
a0c453a8edaec90fb0d945c874de0b1845bc7d7eTimo Sirainen /* accept these additional custom XCLIENT fields */
a0c453a8edaec90fb0d945c874de0b1845bc7d7eTimo Sirainen const char *const *xclient_extensions;
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen /* The kernel send/receive buffer sizes used for the connection sockets.
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen Configuring this is mainly useful for the test suite. The kernel
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen defaults are used when these settings are 0. */
91c58af8e992d028eb325707904debb58ae80438Timo Sirainenstruct smtp_server *smtp_server_init(const struct smtp_server_settings *set);
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainenvoid smtp_server_deinit(struct smtp_server **_server);
a0c453a8edaec90fb0d945c874de0b1845bc7d7eTimo Sirainen/* Create connection. It is still inactive and needs to be started with
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen one of the functions below. */
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainensmtp_server_connection_create(struct smtp_server *server,
34a41fd572d2516549b3a3b340c36730f284612aTimo Sirainen const struct ip_addr *remote_ip, in_port_t remote_port,
34a41fd572d2516549b3a3b340c36730f284612aTimo Sirainen bool ssl_start, const struct smtp_server_settings *set,
34a41fd572d2516549b3a3b340c36730f284612aTimo Sirainen const struct smtp_server_callbacks *callbacks, void *context)
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainensmtp_server_connection_create_from_streams(struct smtp_server *server,
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen struct istream *input, struct ostream *output,
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen const struct ip_addr *remote_ip, in_port_t remote_port,
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen const struct smtp_server_callbacks *callbacks, void *context)
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainenvoid smtp_server_connection_ref(struct smtp_server_connection *conn);
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainenbool smtp_server_connection_unref(struct smtp_server_connection **_conn);
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen/* Initialize the connection with state and data from login service */
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainenvoid smtp_server_connection_login(struct smtp_server_connection *conn,
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen const unsigned char *pdata,
34a41fd572d2516549b3a3b340c36730f284612aTimo Sirainen/* Start the connection. Establishes SSL layer immediately if instructed,
34a41fd572d2516549b3a3b340c36730f284612aTimo Sirainen and sends the greeting once the connection is ready for commands. */
34a41fd572d2516549b3a3b340c36730f284612aTimo Sirainenvoid smtp_server_connection_start(struct smtp_server_connection *conn);
34a41fd572d2516549b3a3b340c36730f284612aTimo Sirainen/* Start the connection, but only establish SSL layer and send greeting;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen handling command input is held off until smtp_server_connection_resume() is
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainenvoid smtp_server_connection_start_pending(struct smtp_server_connection *conn);
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen/* Halt connection command input and idle timeout entirely. */
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainenvoid smtp_server_connection_halt(struct smtp_server_connection *conn);
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen/* Resume connection command input and idle timeout. */
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainenvoid smtp_server_connection_resume(struct smtp_server_connection *conn);
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainenvoid smtp_server_connection_input_lock(struct smtp_server_connection *conn);
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainenvoid smtp_server_connection_input_unlock(struct smtp_server_connection *conn);
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainenvoid smtp_server_connection_set_streams(struct smtp_server_connection *conn,
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen struct istream *input, struct ostream *output);
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainenvoid smtp_server_connection_set_ssl_streams(struct smtp_server_connection *conn,
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen struct istream *input, struct ostream *output);
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainenvoid smtp_server_connection_close(struct smtp_server_connection **_conn,
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainenvoid smtp_server_connection_terminate(struct smtp_server_connection **_conn,
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainenbool smtp_server_connection_data_check_state(struct smtp_server_cmd_ctx *cmd);
acc4e0a41f1c8ef0559a19c280afc1b97b9e0818Timo Sirainenvoid smtp_server_connection_data_chunk_init(struct smtp_server_cmd_ctx *cmd);
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainenint smtp_server_connection_data_chunk_add(struct smtp_server_cmd_ctx *cmd,
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen struct istream *chunk, uoff_t chunk_size, bool chunk_last,
34a41fd572d2516549b3a3b340c36730f284612aTimo Sirainensmtp_server_connection_get_state(struct smtp_server_connection *conn);
34a41fd572d2516549b3a3b340c36730f284612aTimo Sirainensmtp_server_connection_get_security_string(struct smtp_server_connection *conn);
34a41fd572d2516549b3a3b340c36730f284612aTimo Sirainensmtp_server_connection_get_transaction(struct smtp_server_connection *conn);
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainensmtp_server_connection_get_transaction_id(struct smtp_server_connection *conn);
a0c453a8edaec90fb0d945c874de0b1845bc7d7eTimo Sirainensmtp_server_connection_get_stats(struct smtp_server_connection *conn);
a0c453a8edaec90fb0d945c874de0b1845bc7d7eTimo Sirainenvoid *smtp_server_connection_get_context(struct smtp_server_connection *conn)
acc4e0a41f1c8ef0559a19c280afc1b97b9e0818Timo Sirainensmtp_server_connection_get_protocol(struct smtp_server_connection *conn)
acc4e0a41f1c8ef0559a19c280afc1b97b9e0818Timo Sirainensmtp_server_connection_get_protocol_name(struct smtp_server_connection *conn);
acc4e0a41f1c8ef0559a19c280afc1b97b9e0818Timo Sirainensmtp_server_connection_get_helo_data(struct smtp_server_connection *conn);
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainenvoid smtp_server_connection_get_proxy_data(struct smtp_server_connection *conn,
b8835b8a21c617ceb82ddc5a176243faf36aa8f7Timo Sirainen struct smtp_server_connection *conn, enum smtp_capability capabilities);
b8835b8a21c617ceb82ddc5a176243faf36aa8f7Timo Sirainenbool smtp_server_connection_is_ssl_secured(struct smtp_server_connection *conn);
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainenbool smtp_server_connection_is_trusted(struct smtp_server_connection *conn);
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch/* Commands are handled asynchronously, which means that the command is not
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch necessary finished when the start function ends. A command is finished
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch when a reply is submitted for it. Several command hooks are available to
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch get notified about events in the command's life cycle.
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Boschtypedef void smtp_server_cmd_input_callback_t(struct smtp_server_cmd_ctx *cmd);
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Boschtypedef void smtp_server_cmd_start_func_t(struct smtp_server_cmd_ctx *cmd,
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch const char *params);
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Boschtypedef void smtp_server_cmd_func_t(struct smtp_server_cmd_ctx *cmd);
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch /* public hooks */
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch /* next: command is next to reply but has not submittted all replies
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch /* replied: command has submitted all replies */
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch /* completed: server is about to send last replies for this command */
1a115c1eb46e9103f81228dde272852cb78ed4b5Timo Sirainen /* destroy: command is about to be destroyed */
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch/* The core SMTP commands are pre-registered. Special connection callbacks are
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch provided for the core SMTP commands. Only use this command registration API
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch when custom/extension SMTP commands are required.
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Boschvoid smtp_server_command_register(struct smtp_server *server, const char *name,
9937dfc157dc64ed781b7aa264aa893d1d50c5c4Timo Sirainenvoid smtp_server_command_unregister(struct smtp_server *server,
9937dfc157dc64ed781b7aa264aa893d1d50c5c4Timo Sirainen const char *name);
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Boschvoid smtp_server_command_set_reply_count(struct smtp_server_command *cmd,
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch unsigned int count);
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainenvoid smtp_server_command_fail(struct smtp_server_command *cmd,
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainensmtp_server_command_get_reply(struct smtp_server_command *cmd,
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen unsigned int idx);
efe78d3ba24fc866af1c79b9223dc0809ba26cadStephan Boschbool smtp_server_command_reply_status_equals(struct smtp_server_command *cmd,
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen unsigned int status);
678d0463849ba777106eb7875f27db07a5d8e3dfTimo Sirainenbool smtp_server_command_is_replied(struct smtp_server_command *cmd);
678d0463849ba777106eb7875f27db07a5d8e3dfTimo Sirainenbool smtp_server_command_replied_success(struct smtp_server_command *cmd);
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainenvoid smtp_server_command_input_lock(struct smtp_server_cmd_ctx *cmd);
c649139f889c02154fc9a153728b81619edb5663Timo Sirainenvoid smtp_server_command_input_unlock(struct smtp_server_cmd_ctx *cmd);
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainenvoid smtp_server_command_input_capture(struct smtp_server_cmd_ctx *cmd,
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainenvoid smtp_server_cmd_auth_send_challenge(struct smtp_server_cmd_ctx *cmd,
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Boschvoid smtp_server_cmd_auth_success(struct smtp_server_cmd_ctx *cmd,
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen const char *username, const char *success_msg)
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Boschsmtp_server_reply_create_index(struct smtp_server_command *cmd,
c215ca02d468b0e542523df1ed18e5f2d7e63968Timo Sirainen unsigned int index, unsigned int status, const char *enh_code)
34a41fd572d2516549b3a3b340c36730f284612aTimo Sirainensmtp_server_reply_create(struct smtp_server_command *cmd,
428d63767dc20aeb87695b82fb01cd0a06d7769cTimo Sirainen unsigned int status, const char *enh_code) ATTR_NULL(3);
a10ed8c47534b4c6b6bf2711ccfe577e720a47b4Timo Sirainensmtp_server_reply_create_forward(struct smtp_server_command *cmd,
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen unsigned int index, const struct smtp_reply *from);
31a574fda352ef4f71dbff9c30e15e4744e132c0Timo Sirainenvoid smtp_server_reply_add_text(struct smtp_server_reply *reply,
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainen const char *line);
8d587838c414c48a331f0b54cd7ffd97e5024abdTimo Sirainenvoid smtp_server_reply_submit(struct smtp_server_reply *reply);
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen/* Submit a reply for the command at the specified index (> 0 only if more than
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch a single reply is expected). */
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Boschvoid smtp_server_reply_indexv(struct smtp_server_cmd_ctx *_cmd,
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch unsigned int index, unsigned int status, const char *enh_code,
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch const char *fmt, va_list args) ATTR_FORMAT(5, 0);
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Boschvoid smtp_server_reply_index(struct smtp_server_cmd_ctx *_cmd,
1a115c1eb46e9103f81228dde272852cb78ed4b5Timo Sirainen unsigned int index, unsigned int status, const char *enh_code,
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch/* Submit the reply for the specified command. */
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Boschvoid smtp_server_reply(struct smtp_server_cmd_ctx *_cmd,
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch unsigned int status, const char *enh_code, const char *fmt, ...)
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch/* Forward a reply for the command at the specified index (> 0 only if more
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch than a single reply is expected). */
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Boschvoid smtp_server_reply_index_forward(struct smtp_server_cmd_ctx *cmd,
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch unsigned int index, const struct smtp_reply *from);
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch/* Forward the reply for the specified command. */
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Boschvoid smtp_server_reply_forward(struct smtp_server_cmd_ctx *cmd,
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch/* Submit the same message for all expected replies for this command. */
c215ca02d468b0e542523df1ed18e5f2d7e63968Timo Sirainenvoid smtp_server_reply_all(struct smtp_server_cmd_ctx *_cmd,
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch/* Submit and send the same message for all expected replies for this command
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch early; i.e., no matter whether all command data is received completely. */
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Boschvoid smtp_server_reply_early(struct smtp_server_cmd_ctx *_cmd,
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen/* Reply the command with a 221 bye message */
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainenvoid smtp_server_reply_quit(struct smtp_server_cmd_ctx *_cmd);
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainenvoid smtp_server_switch_ioloop(struct smtp_server *server);
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Boschsmtp_server_reply_create_ehlo(struct smtp_server_command *cmd);
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Boschvoid smtp_server_reply_ehlo_add(struct smtp_server_reply *reply,
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Bosch const char *keyword);
2d7df7973f80011033e8e9fa676d3ff4c14468d8Stephan Boschvoid smtp_server_reply_ehlo_add_param(struct smtp_server_reply *reply,
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen const char *keyword, const char *param_fmt, ...) ATTR_FORMAT(3, 4);