#ifndef SMTP_CLIENT_PRIVATE_H
#define SMTP_CLIENT_PRIVATE_H

#include "connection.h"

#include "smtp-common.h"
#include "smtp-params.h"
#include "smtp-client.h"
#include "smtp-client-command.h"
#include "smtp-client-transaction.h"
#include "smtp-client-connection.h"

#define SMTP_CLIENT_DATA_CHUNK_SIZE IO_BLOCK_SIZE

struct smtp_client_command {
	pool_t pool;
	unsigned int refcount;

	struct smtp_client_command *prev, *next;

	buffer_t *data;
	unsigned int send_pos;
	const char *name;

	enum smtp_client_command_flags flags;

	struct smtp_client_connection *conn;
	enum smtp_client_command_state state;
	unsigned int replies_expected;
	unsigned int replies_seen;

	struct istream *stream;
	uoff_t stream_size;

	smtp_client_command_callback_t *callback;
	void *context;

	void (*abort_callback)(void *context);
	void *abort_context;

	void (*sent_callback)(void *context);
	void *sent_context;

	bool has_stream:1;
	bool stream_dot:1;
	bool ehlo:1;
	bool locked:1;
	bool plug:1;
	bool aborting:1;
};

struct smtp_client_transaction_rcpt {
	pool_t pool;
	struct smtp_client_transaction *trans;

	struct smtp_address *rcpt_to;
	struct smtp_params_rcpt rcpt_params;

	smtp_client_command_callback_t *rcpt_callback;
	smtp_client_command_callback_t *data_callback;
	void *context;

	struct smtp_client_command *cmd_rcpt_to;

	bool failed:1;
};

struct smtp_client_transaction {
	pool_t pool;
	int refcount;

	struct smtp_client_transaction *prev, *next;

	struct smtp_client_connection *conn;
	struct smtp_address *mail_from;
	struct smtp_params_mail mail_params;

	enum smtp_client_transaction_state state;
	struct smtp_client_command *cmd_mail_from, *cmd_data;
	struct smtp_client_command *cmd_plug, *cmd_last;
	struct smtp_reply *failure;

	smtp_client_command_callback_t *mail_from_callback;
	void *mail_from_context;

	ARRAY(struct smtp_client_transaction_rcpt *) rcpts, rcpts_pending;
	unsigned int rcpts_next_send_idx;
	unsigned int rcpt_next_data_idx;

	struct istream *data_input;
	smtp_client_command_callback_t *data_callback;
	void *data_context;

	smtp_client_transaction_callback_t *callback;
	void *context;

	struct smtp_client_transaction_times times;

	unsigned int finish_timeout_msecs;
	struct timeout *to_finish, *to_send;

	bool finished:1;
	bool submitted_data:1;
};

struct smtp_client_connection {
	struct connection conn;
	pool_t pool;
	int refcount;

	struct smtp_client *client;
	unsigned int id;
	char *label;

	enum smtp_protocol protocol;
	const char *host;
	in_port_t port;
	enum smtp_client_connection_ssl_mode ssl_mode;

	struct smtp_client_settings set;
	char *password;

	enum smtp_capability capabilities;
	pool_t cap_pool;
	const char **cap_auth_mechanisms;
	const char **cap_xclient_args;
	uoff_t cap_size;

	struct smtp_reply_parser *reply_parser;
	struct smtp_reply reply;

	struct dns_lookup *dns_lookup;
	struct dsasl_client *sasl_client;
	struct timeout *to_connect, *to_trans, *to_commands;

	struct istream *raw_input;
	struct ostream *raw_output, *dot_output;

	struct ssl_iostream *ssl_iostream;

	enum smtp_client_connection_state state;

	smtp_client_command_callback_t *login_callback;
	void *login_context;

	/* commands pending in queue to be sent */
	struct smtp_client_command *cmd_send_queue_head, *cmd_send_queue_tail;
	unsigned int cmd_send_queue_count;
	/* commands that have been sent, waiting for response */
	struct smtp_client_command *cmd_wait_list_head, *cmd_wait_list_tail;
	unsigned int cmd_wait_list_count;

	/* active transactions */
	struct smtp_client_transaction *transactions_head, *transactions_tail;

	unsigned int ips_count, prev_connect_idx;
	struct ip_addr *ips;

	bool old_smtp:1;
	bool authenticated:1;
	bool initial_xclient_sent:1;
	bool connect_failed:1;
	bool handshake_failed:1;
	bool corked:1;
	bool sent_quit:1;
	bool sending_command:1;
	bool reset_needed:1;
	bool destroying:1;
	bool closed:1;
};

struct smtp_client {
	pool_t pool;

	struct smtp_client_settings set;

	struct ioloop *ioloop;
	struct ssl_iostream_context *ssl_ctx;

	struct connection_list *conn_list;
};

/*
 * Command
 */

void smtp_client_command_free(struct smtp_client_command *cmd);
int smtp_client_command_send_more(struct smtp_client_connection *conn,
				  const char **error_r);
int smtp_client_command_input_reply(struct smtp_client_command *cmd,
				    const struct smtp_reply *reply);
void smtp_client_command_fail(struct smtp_client_command **_cmd,
			      unsigned int status, const char *error);
void smtp_client_command_fail_reply(struct smtp_client_command **_cmd,
				    const struct smtp_reply *reply);

/*
 * Transaction
 */

void smtp_client_transaction_connection_result(
	struct smtp_client_transaction *trans,
	const struct smtp_reply *reply);
void smtp_client_transaction_switch_ioloop(
	struct smtp_client_transaction *trans);

/*
 * Connection
 */

struct connection_list *smtp_client_connection_list_init(void);

const char *
smpt_client_connection_label(struct smtp_client_connection *conn);

void smtp_client_connection_fail(struct smtp_client_connection *conn,
				 unsigned int status, const char *error);

void smtp_client_connection_trigger_output(
	struct smtp_client_connection *conn);

void smtp_client_connection_start_cmd_timeout(
	struct smtp_client_connection *conn);
void smtp_client_connection_update_cmd_timeout(
	struct smtp_client_connection *conn);

void smtp_client_connection_add_transaction(
	struct smtp_client_connection *conn,
	struct smtp_client_transaction *trans);
void smtp_client_connection_abort_transaction(
	struct smtp_client_connection *conn,
	struct smtp_client_transaction *trans);
void smtp_client_connection_next_transaction(
	struct smtp_client_connection *conn,
	struct smtp_client_transaction *trans);

#endif
