/*
 * Copyright (c) 2002-2006 Sendmail, Inc. and its suppliers.
 *	All rights reserved.
 *
 * By using this file, you agree to the terms and conditions set
 * forth in the LICENSE file which can be found at the top level of
 * the sendmail distribution.
 *
 * $Id: smtps.h,v 1.189 2007/11/02 04:54:58 ca Exp $
 */

#ifndef SMTPS_H
#define SMTPS_H 1

#include "sm/generic.h"
#include "sm/rpool.h"
#include "sm/time.h"
#include "sm/io.h"
#include "sm/rfc2821.h"
#include "sm/regex.h"
#include "sm/mta.h"
#include "sm/cstr.h"
#include "sm/rcb.h"
#include "sm/net.h"
#include "sm/log.h"
#include "sm/cdb.h"
#include "sm/hdrmod.h"
#include "statethreads/st.h"
#include "sm/tls.h"
#include "sm/sasl.h"
#include "sm/sm-conf.h"
#include "sm/ssdef.h"
#include "sm/sscnf.h"

#if MTA_USE_PMILTER
# include "sm/pmilter.h"
# include "sm/pmfdef.h"
#endif

#include "smtps-str.h"

#ifndef MTA_USE_RSAD
# define MTA_USE_RSAD	2
#endif

#ifndef SS_STATS
# define SS_STATS	0
#endif

#ifndef SSSE_S2Q_IDX
# define SSSE_S2Q_IDX	1
#endif

#ifndef SS_CHECK_LINE_LEN
# define SS_CHECK_LINE_LEN	0
#endif

#ifndef SS_CTX_CHECK
# define SS_CTX_CHECK	1
#endif
#ifndef SS_SESS_CHECK
# define SS_SESS_CHECK	1
#endif

#include "sm/sscnfdef.h"

#ifndef SS_TA_CHECK

/*
**  Currently not useful because it's neither allocated nor freed, it's only
**  cleaned out and it's a local variable in ss_hdl_session().
*/

# define SS_TA_CHECK	0
#endif /* SS_TA_CHECK */

#if SS_TEST
/*
**  Values to trigger errors in certain places to test error handling.
**  Currently these are used as bitflags which allows for combining them.
**  Alternatively numbers might be used if there are many tests that don't
**  need to be combined.
*/

# define SS_TEST_S2A_FAIL     0x00000001  /* cause a s2q comm. failure */

#endif

#define SS_R_OK		"250 2.0.0 Ok.\r\n"
#define SS_R_CONT	"354 Go.\r\n"
#define SS_R_REJ	"550 5.7.0 Rejected.\r\n"
#define SS_R_EHLO_REQ	"503 5.0.0 Need EHLO first\r\n"
#define SS_R_SEQ	"503 5.5.1 Bad sequence of commands.\r\n"
#define SS_R_SYN_PAR	"501 5.5.2 Syntax error in parameters or arguments.\r\n"
#define SS_R_SYN_S_A	"501 5.1.7 Bad sender's mailbox address syntax.\r\n"
#define SS_R_SYN_R_A	"501 5.1.3 Bad destination mailbox address syntax.\r\n"
#define SS_R_SYN_ARG	"501 5.5.2 Syntax error in argument(s).\r\n"
#define SS_R_SYNE	"501 5.5.2 Syntax error.\r\n"
#define SS_R_NOTNOW	"503 5.5.0 Not now\r\n"
#define SS_R_TMP	"451 4.5.0 Temporary error.\r\n"
#define SS_R_SSD	"421 4.3.0 Too busy.\r\n"
#define SS_R_GREY	"421 4.7.0 Come back later.\r\n"

/* access check result structure, see sm/smar.h */
struct ss_acc_S
{
	sm_ret_T	 ssa_map_result; /* result of map lookup: SM_ACC_x */

	/* the next values are only valid if the map result is SM_ACC_FOUND */
	sm_ret_T	 ssa_reply_code;	/* sm/mta.h: */
	sm_str_P	 ssa_reply_text;	/* NULL by default */
};
typedef struct ss_acc_S	ss_acc_T, *ss_acc_P;

struct ss_ctx_S
{
#if SS_CTX_CHECK
	sm_magic_T	 sm_magic;
#endif
	ss_cnf_T	 ssc_cnf;
	int			 ssc_id;	/* smtps id */
	sm_str_P	 ssc_hostname;	/* hostname of server */
	regex_t		 ssc_relayfrom;
	regex_t		 ssc_relayto;
	uint32_t	 ssc_flags;
	sm_log_ctx_P	 ssc_lctx;
	sm_logconfig_P	 ssc_lcfg;
	cdb_ctx_P	 ssc_cdb_ctx;

	st_utime_t	 ssc_mod_tmo;	/* ss_cnf_mod_tmo as micro seconds */

	/* ctx for SMTPS/QMGR communication subsystem */
	s2q_ctx_P	 ssc_s2q_ctx;

	/* ctx for SMTPS/SMAR communication subsystem */
	s2q_ctx_P	 ssc_s2a_ctx;

	time_t		 ssc_shutdown;	/* when to shutdown forcefully */

#if MTA_USE_PMILTER
	/*
	**  currently only used for policy milter, could be general,
	**  but then the individual states of the modules need to be
	**  stored somewhere (QMGR, SMAR)
	*/

	sm_ret_T	 ssc_state;
	uint32_t	 ssc_pmcap;	/* what does milter want? */
	time_t		 ssc_pm_lasttry;

	uint32_t	 ssc_mac_names[PM_SMST_MAX][PM_MAX_MACROS];

	/* ctx for SMTPS/Milter communication subsystem */
	s2q_ctx_P	 ssc_s2m_ctx;
#endif /* MTA_USE_PMILTER */

#if MTA_USE_TLS
	SSL_CTX		*ssc_ssl_ctx;
	tlsl_ctx_P	 ssc_tlsl_ctx;
#endif
#if MTA_USE_SASL
	sm_sasl_ctx_P	 ssc_sasl_ctx;
#endif
};

/* SMTP Server Context flags */
#define SSC_FL_RELAYFROM	0x0001u	/* relay from pattern ok */
#define SSC_FL_RELAYTO		0x0002u	/* relay to pattern ok */
#define SSC_FL_TLS_OK		0x0004u	/* TLS initialized ok */
#define SSC_FL_SASL_OK		0x0008u	/* SASL initialized ok */
#define SSC_FL_TERMINATING	0x0010u	/* server is terminating */
#define SSC_FL_RESTARTDEP	0x0020u	/* ask to restart dependencies */
#define SSC_FL_SHUTDOWN		0x0040u	/* shutdown requested */
#define SSC_FL_SHTDWN_TS	0x0080u	/* shutdown timer started */
#define SSC_FL_SHTDWN_FRC	0x0100u	/* shutdown forced */

#if MTA_USE_PMILTER
#define SSC_FL_PM_USE		0x0100u	/* use policy milter */
#define SSC_FL_PM_TRYCONN	0x0200u	/* try to reconnect to policy milter */
/*
it is possible to encode the milter connect state in this way:

00	do no use milter
01	use milter
10	try to reconnect to milter, tempfail on error
11	try to reconnect to milter, ssd on error

but that's not worth it: it is not easily extendible for other error states.
Hence the error state is stored in ssc_state
*/

/* trying to connect to policy milter (mutex) */
#define SSC_FL_PM_TRYING	0x0400u
#endif /* MTA_USE_PMILTER */

#define SSC_SET_FLAG(ss_ctx, fl) (ss_ctx)->ssc_flags |= (fl)
#define SSC_CLR_FLAG(ss_ctx, fl) (ss_ctx)->ssc_flags &= ~(fl)
#define SSC_IS_FLAG(ss_ctx, fl)	(((ss_ctx)->ssc_flags & (fl)) != 0)

#define SSC_SET_CFLAG(ss_ctx, fl) (ss_ctx)->ssc_cnf.ss_cnf_cflags |= (fl)
#define SSC_CLR_CFLAG(ss_ctx, fl) (ss_ctx)->ssc_cnf.ss_cnf_cflags &= ~(fl)
#define SSC_IS_CFLAG(ss_ctx, fl) (((ss_ctx)->ssc_cnf.ss_cnf_cflags & (fl)) != 0)

#define SSC_SET_MFLAG(ss_ctx, fl) (ss_ctx)->ssc_cnf.ss_cnf_mflags |= (fl)
#define SSC_CLR_MFLAG(ss_ctx, fl) (ss_ctx)->ssc_cnf.ss_cnf_mflags &= ~(fl)
#define SSC_IS_MFLAG(ss_ctx, fl) (((ss_ctx)->ssc_cnf.ss_cnf_mflags & (fl)) != 0)

/* state of server */
#define SSC_ST_OK	0	/* everything ok */
#define SSC_ST_SSD	421	/* service shutting down */
#define SSC_ST_TEMP	450	/* answer everything with temp fail */
#define SSC_ST_PERM	550	/* permanently reject connections (unused?) */

#define SSC_SET_STATE(ss_ctx, newstate)	do {			\
		if ((ss_ctx)->ssc_state < (newstate))		\
			(ss_ctx)->ssc_state = (newstate);	\
	} while (0)

#define SSC_SET_PMCAP(ss_ctx, fl) (ss_ctx)->ssc_pmcap |= (fl)
#define SSC_CLR_PMCAP(ss_ctx, fl) (ss_ctx)->ssc_pmcap &= ~(fl)
#define SSC_IS_PMCAP(ss_ctx, fl) (((ss_ctx)->ssc_pmcap & (fl)) != 0)

#if MTA_USE_PMILTER
# define S2s_IS_IOERR(ss_ctx)	\
				(S2Q_IS_IOERR(ss_ctx->ssc_s2a_ctx) \
				|| S2Q_IS_IOERR(ss_ctx->ssc_s2q_ctx) \
				|| S2Q_IS_IOERR(ss_ctx->ssc_s2m_ctx))
#else
# define S2s_IS_IOERR(ss_ctx)	\
				(S2Q_IS_IOERR(ss_ctx->ssc_s2a_ctx) \
				|| S2Q_IS_IOERR(ss_ctx->ssc_s2q_ctx))
#endif

/* question: put into ss_ctx_S?? */
extern uint Max_threads;
extern uint Max_cur_threads;
extern uint Ss_wait4srv;

/* SMTP server session context */
struct ss_sess_S
{
#if SS_SESS_CHECK
	sm_magic_T	 sm_magic;
#endif
	ss_ctx_P	 ssse_sctx;	/* pointer to server context */
	sm_file_T	*ssse_fp;	/* file to use */
	sm_str_P	 ssse_rd;	/* smtp read buffer */
	sm_str_P	 ssse_wr;	/* smtp write buffer */
	sm_str_P	 ssse_str;	/* str for general use */
	sm_rpool_P	 ssse_rpool;
	uint		 ssse_idx;	/* index for ss_sck_ctx[] array */
	uint32_t	 ssse_flags;
	uint32_t	 ssse_cnf_flags;
	uint		 ssse_state;
	in_addr_T	 ssse_client;	/* fixme: use a generic struct! */
	sm_ret_T	 ssse_cltrslv;	/* result of resolving addr/hostname */
	sm_cstr_P	 ssse_cltname;	/* resolved hostname */
	sm_str_P	 ssse_helo;	/* helo param */
	ss_ta_P		 ssse_ta;	/* current transaction */
	uint		 ssse_badcmds;	/* bad commands */
	uint		 ssse_nopcmds;	/* number of "useless" commands */
	uint		 ssse_invldaddr; /* #cmds with invalid addresses */
	sessta_id_T	 ssse_id;
	sm_rcb_P	 ssse_rcb;	/* rcb for communication with QMGR */
	st_cond_t	 ssse_cond_rd;	/* receiving data from QMGR */
	int		 ssse_s2q_idx;	/* index in s2q_ctx->s2q_sess */
	uint		 ssse_ta_tot;	/* number of transactions */
	uint		 ssse_rcpts_tot; /* total recipients */
	uint		 ssse_max_line_len;	/* max line length */
	ss_acc_T	 ssse_acc;
	time_t		 ssse_connect;
	sm_ret_T	 ssse_maprescnf;	/* map lookup result for session conf */

/* id of s2q "server" (qmgr/smar/pmilter) to which this session is connected */
	int		 ssse_s2q_id[SS_MAX_COMM_SRVS];

	sm_conf_T	*ssse_cnf;
	tlsreq_cnf_T ssse_tlsreq_cnf;
	char		*ssse_feat_name;

#if MTA_USE_TLS
	SSL		*ssse_con;
	sm_file_T	*ssse_fptls;
	tlsi_ctx_P	 ssse_tlsi;
#endif

#if MTA_USE_SASL
	sasl_conn_t	*ssse_sasl_conn;
	uint		 ssse_sasl_ext_ssf;
	uint		 ssse_sasl_tries;
	uint		 ssse_sasl_state;
	char		*ssse_sasl_mech_list;
#endif /* MTA_USE_SASL */

#if MTA_USE_PMILTER
	sm_str_P	 ssse_mac_values[PM_SMST_MAX][PM_MAX_MACROS];
	sm_str_P	 ssse_macv;
# if MTA_USE_SASL
	sm_str_P	 ssse_sasl_mech;
	sm_str_P	 ssse_sasl_authen;
	sm_str_P	 ssse_sasl_author;
# endif /* MTA_USE_SASL */
#endif /* MTA_USE_PMILTER */

#if 0
session-id      /* session identifier, maybe obtained from QMGR  */
host            /* identification of connecting host
                (IP address, host name, ident)	*/
fd              /* file descriptor/handle for connection */
helo-host       /* host name from HELO/EHLO	*/
access times    /* start time, last read, last write	*/
status          /* EHLO/HELO, AUTH, STARTTLS (see above) */
features        /* features offered: AUTH, TLS, EXPN, ... */
workarounds     /* work around bugs in client	*/
reject-msg      /* message to use for rejections (nullserver)	*/
auth            /* AUTH context	*/
starttls        /* TLS context	*/
transaction     /* pointer to current transaction	*/
#endif /* 0 */

};

#define SSSE_S2Q_IDX_NONE	(-1)	/* initial value for s2q_idx */
#define SSSE_S2Q_IDX_CLSD	(-2)	/* connection has been closed */

#define S2Q_ID_NONE		(-1)	/* initial value for s2q_id */

typedef struct ss_mail_S	ss_mail_T, *ss_mail_P;
typedef struct ss_rcpt_S	ss_rcpt_T, *ss_rcpt_P;
typedef struct ss_rcpts_S	ss_rcpts_T, *ss_rcpts_P;

struct ss_mail_S
{
	sm_a2821_T	ssm_a2821;	/* mail */
	/* parameters?? */

	sm_str_P	ssm_pa;
};

/*
**  Note: currently this data isn't used anywhere: the recipients
**  are stored in here but the data isn't read.  It could be used
**  to send rcpts in "chunks" to qmgr instead of one by one.
**  Only for pmilter recipient additions/deletions this data will be read.
*/

struct ss_rcpt_S
{
	sm_a2821_T		ssr_a2821;	/* rcpt */
	/* parameters?? */

	rcpt_idx_T		ssr_idx;	/* rcpt idx */
	TAILQ_ENTRY(ss_rcpt_S)	ssr_link;	/* links */
#if MTA_USE_PMILTER
	ushort			ssr_type;	/* rcpt type: add/delete */
# if MTA_USE_RSAD
	sm_ret_T		ssr_rcode;	/* sm/mta.h */
# endif
#endif
};

TAILQ_HEAD(ss_rcpts_S, ss_rcpt_S);

/* operations on rcpt lists */
#define SS_RCPTS_INIT(rcpts)	TAILQ_INIT(rcpts)
#define SS_RCPTS_EMPTY(rcpts)	TAILQ_EMPTY(rcpts)
#define SS_RCPTS_FIRST(rcpts)	TAILQ_FIRST(rcpts)
#define SS_RCPTS_END(rcpts)	TAILQ_END(rcpts)
#define SS_RCPTS_NEXT(rcpt)	TAILQ_NEXT(rcpt, ssr_link)
#define SS_RCPTS_INSERT_TAIL(rcpts, rcpt) TAILQ_INSERT_TAIL(rcpts, rcpt, ssr_link)
#define SS_RCPTS_INSERT_HEAD(rcpts, rcpt) TAILQ_INSERT_HEAD(rcpts, rcpt, ssr_link)
#define SS_RCPTS_REMOVE(rcpts, rcpt)	TAILQ_REMOVE(rcpts, rcpt, ssr_link)
#define SS_RCPTS_REMOVE_FREE(ta, rcpts, rcpt) do {	\
		TAILQ_REMOVE((rcpts), (rcpt), ssr_link);	\
		ssr_rcpt_free((ta), (rcpt));		\
	} while (0)

/*
**  SMTPS transaction
**  Note: an SMTPS session always has an SMTP transaction associated,
**	even if no transaction has been started yet. Check ssta_state
**	to figure out in which state the transaction is (SSTA_ST_*).
*/

struct ss_ta_S
{
#if SS_TA_CHECK
	sm_magic_T	 sm_magic;
#endif
	sm_rpool_P	 ssta_rpool;
	uint		 ssta_flags;
	sm_hdrmodhd_P	 ssta_hdrmodhd;
	ss_mail_P	 ssta_mail;	/* mail from */
	ss_acc_T	 ssta_mail_acc;	/* access check for mail */
	ss_rcpts_T	 ssta_rcpts;	/* rcpts */

	/*
	**  Access check for current recipient.  If multiple recipients
	**  can be checked concurrently, then this must be in ss_rcpt_S.
	*/

	ss_acc_T	 ssta_rcpt_acc;
	ss_acc_T	 ssta_rcpt_acc2;
	uint		 ssta_rcpts_tot; /* number of recipients total */
	uint		 ssta_rcpts_ok;	/* number of recipients ok */
	uint		 ssta_rcpts_len; /* length of all recipient addresses */
	uint		 ssta_state;
	off_t		 ssta_msg_sz_b;	/* messages size (bytes) */
	sessta_id_T	 ssta_id;

	sm_file_T	*ssta_dfp;	/* cdb (currently: fp) */

	/* message-id, used for logging and pmilter */
	sm_str_P	 ssta_msgid;

	uint		 ssta_badcmds;	/* bad commands */
	uint		 ssta_nopcmds;	/* number of "useless" commands */
	uint		 ssta_invldaddr; /* #cmds with invalid addresses */
#if 0
transaction-id  /* transaction identifier, maybe obtained from QMGR	*/
cdb-id          /* CDB identifier (obtained from cdb?)	*/
cmd-failures    /* number of failures for certain commands	*/
#endif

#if SS_EHLO_ACCESS_CHK
	ss_acc_T	 ssta_ehlo_acc;	/* access check for ehlo */
#endif

#if MTA_USE_PMILTER
	sm_str_P	 ssta_mail_new;	/* new MAIL address */
	uint		 ssta_rcpts_tot_orig;
	uint		 ssta_rcpts_ok_orig;

	ss_acc_T	 ssta_msg_acc;	/* result of msg check */
	uint		 ssta_msg_st;	/* state of message replacement */
	uint		 ssta_eot_state;
	sm_file_T	*ssta_msg_fp;
	sm_cstr_P	 ssta_cdb_id;	/* cdb-id */
	off_t		 ssta_rmsg_sz_b; /* new messages size (bytes) */

	uint		 ssta_hops;
# if MTA_USE_RSAD
	uint		 ssta_nreplies;	/* number of replies */
	uint32_t	*ssta_rcodes;
	sm_str_P	*ssta_replies;	/* replies */
# endif
#endif /* MTA_USE_PMILTER */

};

/* SMTP Server Session flags */
/* #define SSSE_FL_ACTIVE	0x00000001	* session is active (currently UNUSED!) */

/* local only, don't accept, don't tell QMGR (set but not checked) */
#define SSSE_FL_LOCAL_SSD	0x00000002
#define SSSE_FL_CSEID	0x00000004 /* tell QMGR about session close */
#define SSSE_FL_NULL	0x00000008 /* null server */
#define SSSE_FL_QUICK	0x00000010 /* session check returned QUICK */

#define SSSE_FL_SASL_OK	0x00000020 /* SASL initialized ok	*/
#define SSSE_FL_AUTH	0x00000040 /* AUTH (only once, additive)	*/
#define SSSE_FL_STARTTLS 0x00000080 /* STARTTLS is in use */
#define SSSE_FL_HELO	0x00000100 /* client used HELO instead of EHLO */
#define SSSE_FL_CLT_RLY_TMP	0x00001000 /* clt relay: temp.error during test */
#define SSSE_FL_CLIENT_RELAY	0x00002000 /* client can relay */
#define SSSE_FL_CLIENT_OK	0x00004000 /* client OK (access map) */
#define SSSE_FL_DISCARD		0x00008000 /* discard entire session */
#if MTA_USE_PMILTER
	/* use policy milter in this session */
# define SSSE_FL_PM_USE		0x00020000

	/* pmilter has been called in this session */
# define SSSE_FL_PM_CALLED	0x00040000
#endif
#define SSSE_FL_GREYCHKD	0x00080000 /* grey check done for session */
#define SSSE_FL_GREYLSTD	0x00100000 /* greylisted */
#define SSSE_FL_CONN_LOGGED	0x00200000 /* connection info has been logged */

#define SSSE_SET_FLAG(ss_sess, fl)	(ss_sess)->ssse_flags |= (fl)
#define SSSE_CLR_FLAG(ss_sess, fl)	(ss_sess)->ssse_flags &= ~(fl)
#define SSSE_IS_FLAG(ss_sess, fl)	(((ss_sess)->ssse_flags & (fl)) != 0)
#define SSSE_ARE_FLAGS(ss_sess, fl)	(((ss_sess)->ssse_flags & (fl)) == (fl))

#define SSSE_SET_CFLAG(ss_sess, fl)	(ss_sess)->ssse_cnf_flags |= (fl)
#define SSSE_CLR_CFLAG(ss_sess, fl)	(ss_sess)->ssse_cnf_flags &= ~(fl)
#define SSSE_IS_CFLAG(ss_sess, fl)	(((ss_sess)->ssse_cnf_flags & (fl)) != 0)

/* session states (fixme: some of these belong into SSSE flags) */
#define SSSE_ST_NONE		0x0000	/* no session active	*/
#define SSSE_ST_CONNECTING	0x0001	/* connection attempt	*/
#define SSSE_ST_CONNECTED	0x0002	/* connection succeeded	*/
#define SSSE_ST_GREETED		0x0004	/* sent greeting	*/
#define SSSE_ST_EHLO		0x0010	/* EHLO; aborts transaction	*/
#define SSSE_ST_HELO		0x0020	/* HELO; aborts transaction	*/
#define SSSE_ST_QUIT		0x0800	/* QUIT command received	*/

/* transaction states, these must be sorted! */
#define SSTA_ST_NONE	0x00	/* must be 0 */
#define SSTA_ST_INIT	0x01	/* ta initialized, rpool created, etc */
#define SSTA_ST_MAIL	0x02	/* MAIL received */
#define SSTA_ST_RCPT	0x04	/* RCPT received */
#define SSTA_ST_DATA	0x08	/* DATA received */
#define SSTA_ST_DOT	0x10	/* Final dot received */

/* a transaction is "active" */
#define SSTA_IS_ACTIVE(ss_ta)	((ss_ta)->ssta_state >= SSTA_ST_MAIL)

/* SMTP Server Transaction flags */
#define SSTA_FL_NONE		0x00000000
/* #define SSTA_FL_MAIL_QCK	0x00000001	 * mail check returned QUICK */
/* #define SSTA_FL_RCPT_QCK	0x00000002	 * rcpt check returned QUICK */
#define SSTA_FL_LL_EXC		0x00000004	/* maximum line length exceeded */
#define SSTA_FL_DISCARD		0x00000008	/* discard entire transaction */
#define SSTA_FL_DSCRD_RCPT	0x00000010	/* discarded at least one recipient */
#define SSTA_FL_DELAY_CHKS	0x00000020	/* delay checks */
#define SSTA_FL_CDB_EXISTS	0x00000040	/* cdb exists */
#define SSTA_FL_DSN		0x00000080	/* MAIL From:<> (DSN) */
#if MTA_USE_PMILTER
#define SSTA_FL_PM_USE		0x00000100 /* use pmilter for this TA */
#define SSTA_FL_PM_CALLED	0x00000200 /* pmilter has been called for this TA */
#define SSTA_FL_PM_SKIP_MSG	0x00000400 /* pmilter has been called for this TA */
#endif
#define SSTA_FL_XVERP		0x00002000 /* XVERB for MAIL */

#if MTA_USE_PMILTER
#define SSTA_FL_RCPT_MOD	0x00004000	/* recipient modifications exist */
#define SSTA_FL_MAIL_MOD	0x00008000	/* MAIL modification exist */
#endif

#define SSTA_FL_RSAD		0x00020000	/* RSAD for MAIL */

#define SSTA_SET_FLAG(ss_ta, fl)	(ss_ta)->ssta_flags |= (fl)
#define SSTA_CLR_FLAG(ss_ta, fl)	(ss_ta)->ssta_flags &= ~(fl)
#define SSTA_IS_FLAG(ss_ta, fl)	(((ss_ta)->ssta_flags & (fl)) != 0)

#if MTA_USE_PMILTER
#define SSTA_ST_MR_NONE		0x0000	/* Must be 0 */
#define SSTA_ST_MR_CDB_OK	0x0001	/* cdb created */
#define SSTA_ST_MR_CDB_NONE	0x0002	/* cdb creation failed */
#define SSTA_ST_MR_CDB_WR	0x0004	/* writing to cdb */
#define SSTA_ST_MR_CDB_FAIL	0x0002	/* cdb write failed */
#define SSTA_ST_MR_CDB_CLSD	0x0008	/* cdb closed */

sm_ret_T	ss_replacemsg(ss_sess_P _ss_sess, uint32_t _l);
sm_ret_T	ss_rplcmsg(ss_sess_P _ss_sess);
#endif /* MTA_USE_PMILTER */

typedef char		*ss_id_T;

typedef struct ss_socket_ctx_S ss_socket_ctx_T;
struct ss_socket_ctx_S
{
	st_netfd_t	 sssc_lfd;	/* Listening socket */
	char		*sssc_addr;	/* Bind address */
	int		 sssc_port;	/* Port */
	int		 sssc_wait_threads; /* threads waiting to accept */
	int		 sssc_busy_threads; /* threads processing request */
	int		 sssc_maxb_threads; /* max # of threads */
	ulong		 sssc_rqst_count; /* Total # of processed requests */
	ulong		 sssc_ta_count;	/* Total # of processed transactions */
};

sm_ret_T ss_read_cnf(ss_ctx_P _ss_ctx, const char *_fn, sm_conf_T **_psmc);
sm_ret_T ss_chk_cnf(ss_ctx_P _ss_ctx);
sm_ret_T ss_id_init(id_count_T _id_val);
sm_ret_T ss_id_end(void);
sm_ret_T ss_id_next(int _smtpsid, ss_id_T _id);
#if 0
sm_ret_T ss_id_set(id_count_T _id_val);
#endif

sm_ret_T ss_relay_to(ss_ctx_P _ss_ctx, const char *_str);
sm_ret_T ss_relay_from(ss_ctx_P _ss_ctx, const char *_str);

#define SS_PHASE_OTHER	0	/* no special treatment */
#define SS_PHASE_INIT	1	/* initial 220 greeting */
#define SS_PHASE_EHLO	2	/* EHLO response */
#define SS_PHASE_TLS	3	/* STARTTLS response */
#define SS_PHASE_AUTH	4	/* AUTH response */
#define SS_PHASE_MAIL	5	/* MAIL response */
#define SS_PHASE_RCPT	6	/* RCPT response */
#define SS_PHASE_DATA	7	/* DATA response */
#define SS_PHASE_DOT	8	/* response to final dot */
#define SS_PHASE_QUIT	9	/* QUIT */
#define SS_PHASE_NOREPLY	10		/* no reply requested */

sm_ret_T ss_crt_reply(sm_str_P _reply, sm_ret_T _ret, int _phase, bool _new);

/* #if SSQ_DEBUG */
void	 dump_thrd_info(void);
/* #endif */

/* useful macros */
#define WAIT_THREADS(i)	(ss_sck_ctx[i].sssc_wait_threads)
#define BUSY_THREADS(i)	(ss_sck_ctx[i].sssc_busy_threads)
#define MAXB_THREADS(i) (ss_sck_ctx[i].sssc_maxb_threads)
#define TOTAL_THREADS(i) (WAIT_THREADS(i) + BUSY_THREADS(i))
#define RQST_COUNT(i)	(ss_sck_ctx[i].sssc_rqst_count)
#define TA_COUNT(i)	(ss_sck_ctx[i].sssc_ta_count)


#if SS_CTX_CHECK
# define SM_IS_SS_CTX(ss_ctx)	SM_REQUIRE_ISA((ss_ctx), SM_SS_CTX_MAGIC)
#else
# define SM_IS_SS_CTX(ss_ctx)	SM_REQUIRE((ss_ctx) != NULL)
#endif

#if SS_SESS_CHECK
# define SM_IS_SS_SESS(ss_sess)	SM_REQUIRE_ISA((ss_sess), SM_SS_SESS_MAGIC)
#else
# define SM_IS_SS_SESS(ss_sess)	SM_REQUIRE((ss_sess) != NULL)
#endif

#if SS_TA_CHECK
# define SM_IS_SS_TA(ss_ta)	SM_REQUIRE_ISA((ss_ta), SM_SS_TA_MAGIC)
#else
# define SM_IS_SS_TA(ss_ta)	SM_REQUIRE((ss_ta) != NULL)
#endif

#endif /* SMTPS_H */


syntax highlighted by Code2HTML, v. 0.9.1