/*
 * 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: smtpc.h,v 1.136 2007/05/27 15:13:00 ca Exp $
 */

#ifndef SMTPC_H
#define SMTPC_H 1

#include "sm/generic.h"
#include "sm/magic.h"
#include "sm/rpool.h"
#include "sm/time.h"
#include "sm/io.h"
#include "sm/rfc2821.h"
#include "sm/mta.h"
#include "sm/rcb.h"
#include "statethreads/st.h"
#include "sm/stsock.h"
#include "sm/log.h"
#include "sm/cdb.h"
#include "sm/tls.h"
#include "sm/sm-conf.h"
#include "sm/scdef.h"
#include "sm/sccnf.h"
#include "sm/hdrmod.h"

#ifndef SC_PIPELINING
# define SC_PIPELINING	1
#endif

#ifndef SC_STATS
# define SC_STATS	1
#endif

#ifndef SC_DEBUG
# define SC_DEBUG	0
#endif

#ifndef MTA_USE_RSAD
# define MTA_USE_RSAD	1			/* RCPT status after final dot  */
#endif

#include "sm/sccnfdef.h"

#if SC_DEBUG
# include <stdio.h>
# define SC_DPRINTF(x)	sm_io_fprintf x
# define SC_LEV_DPRINTF(sc_ctx, lev, x)	do			\
	{							\
		if ((lev) < (sc_ctx)->scc_cnf.sc_cnf_debug)	\
			sm_io_fprintf x;			\
	} while (0)
#else /* SC_DEBUG */
# define SC_DPRINTF(x)
# define SC_LEV_DPRINTF(sc_ctx, lev, x)
#endif /* SC_DEBUG */

#define REQUEST_TIMEOUT 20	/* requests from QMGR */

/* QMGR read timeout (in seconds) */
#define TMO_W4Q2C	10

extern sm_stream_T SmStThrIO;

void	*handle_request(void *arg);

#define SMTPC_OK_REPLY(ret) ((ret) == SMTP_OK || (ret) == SMTP_NO_REPLY)
#define SMTPC_R_IS_OK(ret) ((ret) == SMTP_OK || (ret) == SMTP_NO_REPLY)
#define NO_RCPTS(sc_ta) (0 == (sc_ta)->scta_rcpts_ok)

/* SMTPC context */
typedef struct sc_ctx_S		sc_ctx_T, *sc_ctx_P;

/* client context per thread */
typedef struct sc_t_ctx_S	sc_t_ctx_T, *sc_t_ctx_P;

/* session context */
typedef struct sc_sess_S	sc_sess_T, *sc_sess_P;

/* transaction context */
typedef struct sc_ta_S	sc_ta_T, *sc_ta_P;

struct sc_ctx_S
{
	sm_magic_T	 sm_magic;

	/*
	**  add pointer to c2q_ctx? That requires that we either use
	**	struct ... *
	**  or include the struct definition here.
	**
	**  rename some components?
	**
	**  note: scc_ is SmtpC Context, to avoid conflicts on HP: sc_flags
	*/

	sc_cnf_T	 scc_cnf;

	st_utime_t	 scc_qmgr_tmo;	/* sc_cnf_qmgr_tmo as micro seconds */

	/*
	**  invariant:
	**  scc_thrds_wait >= scc_thrds_idle + scc_thrds_clsng
	**	i.e., every idle/closing thread is also a waiting thread
	*/

	uint		 scc_thrds_wait; /* # of threads waiting */
	uint		 scc_thrds_idle; /* # of idle threads */
	uint		 scc_thrds_clsng; /* # of closing threads */
	uint		 scc_thrds_busy; /* # of threads processing request */

	/* statistics */
	uint		 scc_thrds_max_used; /* max number of threads used */

	uint		 scc_rqst_count; /* Total # of processed requests   */
	uint32_t	 scc_flags;	/* SMTPC flags */
	sm_str_P	 scc_hostname;	/* SMTPC hostname */
	sm_log_ctx_P	 scc_lctx;
	sm_logconfig_P	 scc_lcfg;
	cdb_ctx_P	 scc_cdb_ctx;
#if SC_STATS
	uint		 scc_mail_count; /* Total # of processed transactions */
	uint		 scc_rcpt_count; /* Total # of recipients */
	uint		 scc_rcpt_cnt_ok; /* Total # of sent recipients */
	uint		 scc_ta_cnt_ok;	/* Total # of sent transactions */
	uint		 scc_reuse;	/* # of reused sessions */
	uint		 scc_busy;
	uint		 scc_open_se; /* concurrently open sessions */
	uint		 scc_max_open_se; /* max concurrently open sessions */
	ulong		 scc_total;
#endif /* SC_STATS */
#if MTA_USE_TLS
	SSL_CTX		*scc_ssl_ctx;
	tlsl_ctx_P	 scc_tlsl_ctx;
#endif /* MTA_USE_TLS */
	sc_t_ctx_P	*scc_scts;	/* array of sct's */
};

#define SM_IS_SC_CTX(sc_ctx) SM_REQUIRE_ISA((sc_ctx), SM_SC_CTX_MAGIC)

/* SMTP Client Context flags: more? */
#define SCC_FL_NOTRDY	0		/* not ready yet */
#define SCC_FL_INIT	0x00000001	/* being initialized */
#define SCC_FL_COMMOK	0x00000002	/* sc_rcb_from_qmgr() may start */
#define SCC_FL_NOTIFIED	0x00000004	/* sc_rcb_from_qmgr() notified */
#define SCC_FL_OK	0x00000100	/* ok */
#define SCC_FL_TLS_OK	0x00000200	/* TLS ok */
#define SCC_FL_SHUTDOWN	0x00010000	/* shutting down */
#define SCC_FL_STOPPED	0x00100000	/* stopped */

#define SC_IS_FLAG(sc_ctx, flag)	((((sc_ctx)->scc_flags) & (flag)) != 0)
#define SC_SET_FLAG(sc_ctx, flag)	((sc_ctx)->scc_flags) |= (flag)

/*
**  Configuration flags; currently these are only for testing.
**  NOTE: these must be the same as SCSE_FL_*
*/

#define SCC_CFL_LMTP		0x00000001 /* use LMTP */
/* see also sm/scdef.h!
#define SCC_CFL_NOTTM		0x00000002 * don't check "talk to myself" *
*/

/* mask for "inheriting" sc_cnf_confflags */
#define SCC_CFL_INHMASK		0x00000003

#define SCC_CFL_BACKGROUND	0x00000010 /* run in background mode */
/* see also sm/scdef.h! */

#define SC_IS_CFLAG(sc_ctx, flag) ((((sc_ctx)->scc_cnf.sc_cnf_confflags) & (flag)) != 0)
#define SC_CLR_CFLAG(sc_ctx, flag) ((sc_ctx)->scc_cnf.sc_cnf_confflags) &= ~(flag)
#define SC_SET_CFLAG(sc_ctx, flag) ((sc_ctx)->scc_cnf.sc_cnf_confflags) |= (flag)

/*
**  Thread context.
**  Currently sess and thread are 1-1 related.
*/

struct sc_t_ctx_S
{
	sm_magic_T	 sm_magic;
	sc_ctx_P	 sct_sc_ctx;	/* pointer back to sc_ctx */

	uint		 sct_thr_id;	/* thread id (debugging) */
	uint		 sct_status;
	st_cond_t	 sct_cond_rd;	/* received data from QMGR */
	sc_sess_P	 sct_sess;	/* current session */
	sm_rcb_P	 sct_rcb;	/* rcb for communication with QMGR */
};

#define SM_IS_SC_T_CTX(sc_t_ctx) SM_REQUIRE_ISA((sc_t_ctx), SM_SC_T_CTX_MAGIC)

/* sc_t status: more? */
#define SC_T_NOTRDY	0	/* not ready for use */
#define SC_T_FREE	1	/* can be used */
#define SC_T_IDLE	2	/* open session, but no active TA */
#define SC_T_CLOSING	3	/* an idle session is closing */
#define SC_T_BUSY	4	/* is busy */
#define SC_T_REUSE	5	/* a closing task will be reused */

struct sc_sess_S
{
	sm_magic_T	 sm_magic;
	sc_t_ctx_P	 scse_sct_ctx;	/* pointer to thread context */
	sm_file_T	*scse_fp;	/* file to use (SMTP) */
	sm_str_P	 scse_rd;	/* smtp read buffer */
	sm_str_P	 scse_wr;	/* smtp write buffer */
	sm_str_P	 scse_str;	/* str for general use */
	sm_str_P	 scse_reply;	/* SMTP reply (for error reporting) */
	sm_rpool_P	 scse_rpool;
	uint		 scse_cap;	/* server capabilities, see below */
	uint		 scse_flags;
	uint		 scse_state;
	uint		 scse_err_st;	/* state which caused an error */
#if 0
	sm_ret_T	 scse_ret;	/* error code */
	struct in_addr	*scse_client;	/* fixme: use a generic struct! */
#endif
	sc_ta_P		 scse_ta;	/* current transaction */
	sessta_id_T	 scse_id;
	sessta_id_T	 scse_id2;
	uint		 scse_da_idx;	/* DA idx */
	sm_sockaddr_T	 scse_rmt_addr;	/* remote address */
	st_netfd_t	 scse_rmt_fd;	/* fd */
	int		 scse_c2q_idx;	/* index in c2q session array */

	/* only useful if the size of the message is known... */
	off_t		 scse_max_sz_b;	/* SIZE value advertised by server */

#if MTA_USE_TLS
	SSL		*scse_con;
	sm_file_T	*scse_fptls;
	tlsi_ctx_P	 scse_tlsi;
	sm_conf_T	*scse_cnf;
	tlsreq_cnf_T scse_tlsreq_cnf;
#endif /* MTA_USE_TLS */
};

#define SM_IS_SC_SE(sc_sess)	SM_REQUIRE_ISA((sc_sess), SM_SC_SESS_MAGIC)

#define SCSE_C2Q_IDX_NONE	(-1)	/* initial value for c2q_idx */

/* session states (should these be flags too?) */
#define SCSE_ST_NONE		0	/* no session active	*/
#define SCSE_ST_NEW		1	/* new session	*/
#define SCSE_ST_CONNECTED	2	/* connection succeeded	*/
#define SCSE_ST_GREETED		3	/* received greeting	*/
#define SCSE_ST_OPEN		4	/* connection open	*/
#define SCSE_ST_CLOSED		8	/* close session	*/

/* session flags */
#define SCSE_FL_NONE		0x00000000 /* guess	*/

/* NOTE: these must be the same as SCC_CFL_* */
#define SCSE_FL_LMTP		0x00000001 /* use LMTP	*/
#define SCSE_FL_NOTTM		0x00000002 /* don't check "talk to myself" */


#define SCSE_FL_CLOSE_EX	0x00000008 /* close existing session */
#define SCSE_FL_EHLO		0x00000010 /* sent EHLO	*/
#define SCSE_FL_HELO		0x00000020 /* sent HELO	*/
#define SCSE_FL_RSET		0x00000040 /* sent RSET	*/
#define SCSE_FL_AUTH		0x00000100 /* AUTH	*/
#define SCSE_FL_STARTTLS	0x00000200 /* STARTTLS	*/
#define SCSE_FL_QUIT_S		0x00000400 /* sent QUIT */
#define SCSE_FL_QUIT_R		0x00000800 /* received reply for QUIT */
#define SCSE_FL_SSD			0x00001000 /* encountered 421	*/
#define SCSE_FL_RSAD		0x00004000 /* used RCPT status after dot */

/*
**  close session; note: this can be set during SMTP and will cause a session
**  close ASAP. Do NOT set this in response to a qmgr task!
*/

#define SCSE_FL_CLOSE		0x00004000

/* last TA: close session afterwards */
#define SCSE_FL_LAST_TA		0x00010000
#define SCSE_FL_LWR		0x00020000 /* lower case recipient	*/
#define SCSE_FL_RETPATH		0x00040000 /* add Return-Path: */
#define SCSE_FL_ERROR		0x00100000 /* error occurred	*/
#define SCSE_FL_IO_ERR		0x00200000 /* I/O error occurred	*/
#define SCSE_FL_LOGGED		0x00400000 /* error already logged	*/

/* need to send status (in sc_t_ctx->sct_rcb) to QMGR	*/
#define SCSE_FL_SND_ST		0x01000000

/* tell QMGR about session close (cleared as soon as it was done somewhere) */
#define SCSE_FL_SE_CLS2QMGR	0x02000000

#define SCSE_SET_FLAG(sc_sess, fl)	(sc_sess)->scse_flags |= (fl)
#define SCSE_CLR_FLAG(sc_sess, fl)	(sc_sess)->scse_flags &= ~(fl)
#define SCSE_IS_FLAG(sc_sess, fl)	(((sc_sess)->scse_flags & (fl)) != 0)

#define SCSE_INHERIT_FLAG(sc_sess)	(sc_sess)->scse_flags = (sc_sess)->scse_sct_ctx->sct_sc_ctx->scc_cnf.sc_cnf_confflags & SCC_CFL_INHMASK

/* server capabilities */
#define SCSE_CAP_NONE		0x0000	/* guess	*/
#define SCSE_CAP_ESMTP		0x0001	/* ESMTP	*/
#define SCSE_CAP_PIPELINING	0x0010	/* PIPELINING	*/
#define SCSE_CAP_8BITMIME	0x0020	/* 8BITMIME	*/
#define SCSE_CAP_SIZE		0x0040	/* SIZE		*/
#define SCSE_CAP_ENHSTAT	0x0080	/* ENHANCEDSTATUSCODES	*/
#define SCSE_CAP_AUTH		0x0100	/* AUTH		*/
#define SCSE_CAP_STARTTLS	0x0200	/* STARTTLS	*/
#define SCSE_CAP_RSAD		0x0800	/* used RCPT status after dot */

#define SCSE_SET_CAP(sc_sess, cap)	(sc_sess)->scse_cap |= (cap)
#define SCSE_CLR_CAP(sc_sess, cap)	(sc_sess)->scse_cap &= ~(cap)
#define SCSE_IS_CAP(sc_sess, cap)	(((sc_sess)->scse_cap & (cap)) != 0)


typedef struct sc_mail_S	sc_mail_T, *sc_mail_P;

struct sc_mail_S
{
	sm_str_P			scm_pa;	/* mail */
	/* parameters?? */

	smtp_status_T			scm_st;	/* status */
	sm_str_P			scm_reply;	/* reply text */
	/* enhanced status, reply text?? */
};

typedef struct sc_rcpt_S	sc_rcpt_T, *sc_rcpt_P;
typedef struct sc_rcpts_S	sc_rcpts_T, *sc_rcpts_P;

struct sc_rcpt_S
{
	sm_str_P		 scr_pa;		/* rcpt */
	/* parameters?? */

	rcpt_idx_T		 scr_idx;	/* rcpt index */
	smtp_status_T	 scr_st;		/* status */
	uint			 scr_flags;	/* flags; see below */
	sm_str_P		 scr_reply;	/* reply text */

#if MTA_USE_TLS
	sm_conf_T		*scr_cnf;
	tlsreq_cnf_T	 scr_tlsreq_cnf;
#endif

	/* enhanced status, reply text?? */
	TAILQ_ENTRY(sc_rcpt_S)		scr_l;	/* links */
};

/* recipient flags (scr_flags) */
#define SCR_FL_NONE		0x00000000 /* guess */
#define SCR_FL_RCVD		0x00000001 /* received from qmgr */
#define SCR_FL_SENT		0x00000002 /* sent to SMTP server */
#define SCR_FL_STAT		0x00000004 /* got status from SMTP server */
#define SCR_FL_LOGGED		0x00000010 /* status has been logged */

#define SCR_SET_FLAG(sc_rcpt, fl)	(sc_rcpt)->scr_flags |= (fl)
#define SCR_CLR_FLAG(sc_rcpt, fl)	(sc_rcpt)->scr_flags &= ~(fl)
#define SCR_IS_FLAG(sc_rcpt, fl)	(((sc_rcpt)->scr_flags & (fl)) != 0)


TAILQ_HEAD(sc_rcpts_S, sc_rcpt_S);

/* operations on rcpt lists */
#define SC_RCPTS_INIT(rcpts)	TAILQ_INIT(rcpts)
#define SC_RCPTS_EMPTY(rcpts)	TAILQ_EMPTY(rcpts)
#define SC_RCPTS_FIRST(rcpts)	TAILQ_FIRST(rcpts)
#define SC_RCPTS_END(rcpts)	TAILQ_END(rcpts)
#define SC_RCPTS_NEXT(rcpts)	TAILQ_NEXT(rcpts, scr_l)
#define SC_RCPTS_INSERT_TAIL(rcpts, rcpt) TAILQ_INSERT_TAIL(rcpts, rcpt, scr_l)
#define SC_RCPTS_INSERT_HEAD(rcpts, rcpt) TAILQ_INSERT_HEAD(rcpts, rcpt, scr_l)
#define SC_RCPTS_REMOVE(rcpts, rcpt)	TAILQ_REMOVE(rcpts, rcpt, scr_l)
#define SC_RCPTS_REMOVE_FREE(ta, rcpts, rcpt) do {	\
		TAILQ_REMOVE((rcpts), (rcpt), scr_l);	\
		scr_free((ta), (rcpt));			\
	} while (0)

#define SM_IS_SC_TA(sc_ta)	SM_REQUIRE_ISA((sc_ta), SM_SC_TA_MAGIC)

/* SMTPC Transaction context */
struct sc_ta_S
{
	sm_magic_T	 sm_magic;
	sc_sess_P	 scta_sess;	/* pointer to session */
	sm_rpool_P	 scta_rpool;
	sm_file_T	*scta_cdb_fp;	/* cdb fp */
	sc_mail_P	 scta_mail;	/* mail from */
	sc_rcpts_T	 scta_rcpts;	/* rcpts */
#if SC_PIPELINING
	sc_rcpt_P	 scta_rcpt_p;	/* current rcpt for reply */
	uint		 scta_rcpts_rcvd; /* # of recipients replies received */
#endif
	uint		 scta_rcpts_tot; /* number of recipients total */
	uint		 scta_rcpts_snt; /* number of recipients sent */
	uint		 scta_rcpts_ok;	/* number of recipients ok */
	uint		 scta_rcpts_dot; /* #rcpts still to collect after data */
	uint		 scta_state;	/* see below */
	uint		 scta_flags;	/* see below */
	uint		 scta_err_state; /* state which caused an error: da.h */
	smtp_status_T	 scta_status;	/* SMTP status code (if applicable) */
	/* add sm_ret_T	 scta_err_status; ? */
	sessta_id_T	 scta_id;	/* DA transaction id */
	sessta_id_T	 scta_ssta_id;	/* SMTPS transaction id (logging) */
	sm_str_P	 scta_reply;	/* reply text (data/dot) */
	sm_str_P	 scta_cdb_id;	/* CDB id */
	sm_str_P	 scta_b_msg;	/* bounce message (hack) */
	size_t		 scta_msg_sz_b;	/* message size (bytes) */
	sm_hdrmodhd_P	 scta_hdrmodhd;
};

/*
**  Transaction states, these must be sorted!
**  This is a combination of flags (bit values) and state (ordered values)
*/

#define SCTA_NONE	0x00000000	/* must be 0 */
#define SCTA_INIT	0x00000001	/* ta initialized */
#define SCTA_MAIL_S	0x00000002	/* sent MAIL */
#define SCTA_MAIL_R	0x00000004	/* received reply for MAIL */
#define SCTA_RCPT_S	0x00000010	/* sent RCPT */
#define SCTA_RCPT_R	0x00000020	/* received replies for RCPT */
#define SCTA_DATA_S	0x00000040	/* sent DATA */
#define SCTA_DATA_R	0x00000080	/* received reply for DATA */
#define SCTA_DOT_S	0x00000100	/* sent final dot */
#define SCTA_DOT_R	0x00000200	/* received reply for final dot */
#define SCTA_L_RCPT_R	0x00000400	/* received replies for LMTP RCPT */
#define SCTA_D_RCPT_R	0x00000800	/* rcvd replies for RCPT Status after dot */
#define SCTA_R_PERM	0x00001000	/* one RCPT had a perm.error */
#define SCTA_R_TEMP	0x00002000	/* one RCPT had a temp.error */
#define SCTA_FAIL_TEMP	0x00010000	/* ta temp failed */
#define SCTA_FAIL_PERM	0x00020000	/* ta perm failed */
#define SCTA_COMPLETE	0x00040000	/* ta completed */
#define SCTA_DOT_F_R	0x00080000	/* received final reply for dot (RSAD) */

#define SCTA_SET_STATE(sc_ta, fl)	(sc_ta)->scta_state |= (fl)
#define SCTA_CLR_STATE(sc_ta, fl)	(sc_ta)->scta_state &= ~(fl)
#define SCTA_IS_STATE(sc_ta, fl)	(((sc_ta)->scta_state & (fl)) != 0)

#define SCTA_FL_NONE	0x00000000	/* must be 0 */

/* see sm/da.h */
#define SCTA_FL_HDR_ONLY DA_FL_HDR_ONLY	/* send bounce with headers only */
#define SCTA_FL_DSN_MIME DA_FL_DSN_MIME	/* DSN in MIME format (not yet impl) */

#define SCTA_FL_DELAY	0x00000010	/* ta describes a delay DSN */
#define SCTA_FL_BOUNCE	0x00000020	/* ta describes a bounce */
#define SCTA_FL_DBOUNCE	0x00000040	/* ta describes a double bounce */
#define SCTA_FL_NO_BODY	0x00000080	/* send bounce without body */
#define SCTA_FL_CUT_HDR	0x00000200	/* cutoff at header has been done */
#define SCTA_FL_CUT_DOT	0x00000400	/* cutoff at dot has been done */
#define SCTA_FL_CDB_CUT (SCTA_FL_CUT_DOT|SCTA_FL_CUT_HDR)

/* header scanning necessary for modifications */
#define SCTA_FL_HDR_SCAN 0x00001000

#define SCTA_FL_DSN	(SCTA_FL_DELAY|SCTA_FL_BOUNCE|SCTA_FL_DBOUNCE)

#define SCTA_SET_FLAG(sc_ta, fl)	(sc_ta)->scta_flags |= (fl)
#define SCTA_CLR_FLAG(sc_ta, fl)	(sc_ta)->scta_flags &= ~(fl)
#define SCTA_IS_FLAG(sc_ta, fl)	(((sc_ta)->scta_flags & (fl)) != 0)

void		*sc_hdl_requests(void *arg);
sm_ret_T sc_read_cnf(sc_ctx_P _sc_ctx, const char *_fn, sm_conf_T **_psmc);

sc_t_ctx_P	 get_free_thr(sc_ctx_P _sc_ctx);
sm_ret_T sc_sess_open(sc_t_ctx_P _sc_t_ctx);
sm_ret_T sc_sess_close(sc_t_ctx_P _sc_t_ctx);
sm_ret_T sc_one_ta(sc_t_ctx_P _sc_t_ctx);
sm_ret_T sc_t_ctx_new(sc_ctx_P _sc_ctx, sc_t_ctx_P *_sc_t_ctx, uint _thr_id);
sm_ret_T sc_t_ctx_free(sc_ctx_P _sc_ctx, sc_t_ctx_P _sc_t_ctx);
sm_ret_T sc_t_ctx_clr(sc_t_ctx_P _sc_t_ctx);
sm_ret_T sc_sess_new(sc_sess_P *_sc_sess, sc_t_ctx_P _sc_t_ctx);
sm_ret_T sc_sess_free(sc_sess_P _sc_sess, sc_t_ctx_P _sc_t_ctx);
sm_ret_T sc_sess_clr(sc_sess_P _sc_sess);
sm_ret_T sc_ta_new(sc_ta_P *_sc_ta, sc_sess_P _sc_sess);
sm_ret_T sc_ta_free(sc_ta_P _sc_ta);
sm_ret_T sc_ta_clr(sc_ta_P _sc_ta);
sm_ret_T sc_mail_new(sc_ta_P _sc_ta);
sm_ret_T sc_mail_free(sc_ta_P _sc_ta);
sm_ret_T sc_rcpts_new(sc_ta_P _sc_ta, sm_str_P _rcpt_pa, rcpt_idx_T _rcpt_idx, sc_rcpt_P *_prcpt);
sm_ret_T sc_rcpt_free(sc_ta_P _sc_ta, sc_rcpt_P _addr);
sm_ret_T sc_rcpts_free(sc_ta_P _sc_ta);

sm_ret_T sc_hdrmod_new(sc_ta_P _sc_ta, sm_hdrmod_P *_psm_hdrmod);
sm_ret_T sc_hdrmod_rm(sc_ta_P _sc_ta);

#define SC_MAX_WAIT_THREADS(sc_ctx) ((sc_ctx)->scc_cnf.sc_cnf_max_wait_threads)
#define SC_MIN_WAIT_THREADS(sc_ctx) ((sc_ctx)->scc_cnf.sc_cnf_min_wait_threads)
#define SC_MAX_THREADS(sc_ctx)	((sc_ctx)->scc_cnf.sc_cnf_max_threads)
#define SC_MAX_USED_THREADS(sc_ctx) ((sc_ctx)->scc_thrds_max_used)
#define SC_WAIT_THREADS(sc_ctx)	((sc_ctx)->scc_thrds_wait)
#define SC_BUSY_THREADS(sc_ctx)	((sc_ctx)->scc_thrds_busy)
#define SC_IDLE_THREADS(sc_ctx)	((sc_ctx)->scc_thrds_idle)
#define SC_CLOSING_THREADS(sc_ctx)	((sc_ctx)->scc_thrds_clsng)
#define SC_AVAIL_THREADS(sc_ctx)	(SC_WAIT_THREADS(sc_ctx) - SC_IDLE_THREADS(sc_ctx) - SC_CLOSING_THREADS(sc_ctx))
#define SC_TOTAL_THREADS(sc_ctx)	(SC_WAIT_THREADS(sc_ctx) + SC_BUSY_THREADS(sc_ctx))
#define SC_RQST_COUNT(sc_ctx)	((sc_ctx)->scc_rqst_count)
#if SC_STATS
# define SC_BUSY(sc_t_ctx)		((sc_t_ctx)->sct_sc_ctx->scc_busy)
# define SC_TOTAL(sc_t_ctx)	((sc_t_ctx)->sct_sc_ctx->scc_total)
# define SC_MAIL_COUNT(sc_ctx)	((sc_ctx)->scc_mail_count)
# define SC_RCPT_COUNT(sc_ctx)	((sc_ctx)->scc_rcpt_count)
# define SC_TA_CNT_OK(sc_ctx)	((sc_ctx)->scc_ta_cnt_ok)
# define SC_RCPT_CNT_OK(sc_ctx)	((sc_ctx)->scc_rcpt_cnt_ok)
# define SC_SE_REUSE(sc_ctx)	((sc_ctx)->scc_reuse)
# define SC_OPEN_SE(sc_ctx)	((sc_ctx)->scc_open_se)
# define SC_MAX_OPEN_SE(sc_ctx)	((sc_ctx)->scc_max_open_se)
#endif /* SC_STATS */

/*
**  Number of file descriptors needed to handle one client session
**  1 client communication
**  1 data file
*/

#define SC_FD_PER_THREAD 2

#if SC_TIMING
# define SC_DEBFP smioerr
# if HAVE_GETHRTIME
#  define SC_HRT_DPRINTF(x)	do		\
	{						\
		hrtime_t hrt;				\
		hrt = gethrtime();			\
		sm_io_fprintf(SC_DEBFP, "r=%lld: ", hrt);	\
		sm_io_fprintf x;			\
	} while (0)
# endif

# if HAVE_GETHRVTIME
#  define SC_HRVT_DPRINTF(x)	do		\
	{						\
		hrtime_t hrt;				\
		hrt = gethrvtime();			\
		sm_io_fprintf(SC_DEBFP, "v=%lld: ", hrt);	\
		sm_io_fprintf x;			\
	} while (0)
# endif

# if HAVE_GETHRVTIME && HAVE_GETHRTIME
#  define SC_HRBT_DPRINTF(x)	do			\
	{						\
		hrtime_t hrvt, hrt;			\
		hrt = gethrtime();			\
		hrvt = gethrvtime();			\
		sm_io_fprintf(SC_DEBFP, "r=%lld, v=%lld: ", hrt, hrvt);	\
		sm_io_fprintf x;			\
	} while (0)
# elif HAVE_CLOCK_GETTIME
#  define SC_HRBT_DPRINTF(x)	do		\
	{						\
		struct timespec tsr, tsv;		\
		int r;					\
		r = clock_gettime(CLOCK_MONOTONIC, &tsr);	\
		if (r != 0) timespecclear(&tsr);	\
		r = clock_gettime(CLOCK_PROF, &tsv);	\
		if (r != 0) timespecclear(&tsv);	\
		sm_io_fprintf(SC_DEBFP, "r=%d.%09ld, v=%d.%09ld: ", tsr.tv_sec, tsr.tv_nsec, tsv.tv_sec, tsv.tv_nsec);			\
		sm_io_fprintf x;			\
	} while (0)
# endif
#endif /* SC_TIMING */

#ifndef	SC_HRT_DPRINTF
# define SC_HRT_DPRINTF(x)
#endif

#ifndef	SC_HRVT_DPRINTF
# define SC_HRVT_DPRINTF(x)
#endif

#ifndef	SC_HRBT_DPRINTF
# define SC_HRBT_DPRINTF(x)
#endif


#endif /* SMTPC_H */


syntax highlighted by Code2HTML, v. 0.9.1