/*
 * 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.
 */

#include "sm/generic.h"
SM_RCSID("@(#)$Id: smtpsh.c,v 1.69 2007/10/16 04:20:36 ca Exp $")

#include "sm/assert.h"
#include "sm/error.h"
#include "sm/ctype.h"
#include "sm/memops.h"
#include "sm/io.h"
#include "sm/str.h"
#include "sm/string.h"
#include "sm/time.h"
#include "sm/limits.h"
#include "sm/types.h"
#include "sm/net.h"
#include "statethreads/st.h"
#define MTA_USE_STATETHREADS 1
#include "sm/stsock.h"
#include "sm/tls.h"
#include "sm/tlsbio.h"
#include "sm/sasl.h"
#include "sm/rfc2821.h"
#include "sm/misc.h"

#include "s2q.h"
#include "smtps.h"
#include "s2m.h"
#include "smtpsrv.h"
#include "smtpsh.h"
#include "log.h"
#if MTA_USE_PMILTER
# include "pmilter.h"
#endif
#include "sm/sm-conf-prt.h"

/*
**  SSM_MAIL_NEW -- create new mail address
**
**	Parameters:
**		ss_ta -- SMTP server transaction context
**		alloc_pa -- also create sm_str for printable address?
**		pmail -- mail (sender) address (output)
**
**	Returns:
**		usual return code
*/

sm_ret_T
ssm_mail_new(ss_ta_P ss_ta, bool alloc_pa, ss_mail_P *pmail)
{
	ss_mail_P mail;

	SM_REQUIRE(pmail != NULL);
	mail = (ss_mail_P) sm_rpool_zalloc(ss_ta->ssta_rpool, sizeof(*mail));
	if (NULL == mail)
		return sm_error_temp(SM_EM_SMTPS, ENOMEM);
	A2821_INIT_RP(&mail->ssm_a2821, ss_ta->ssta_rpool);
	if (alloc_pa) {
		mail->ssm_pa = sm_str_new(ss_ta->ssta_rpool, MAXADDRLEN, MAXADDRLEN);
		if (NULL == mail->ssm_pa) {
			ssm_mail_free(ss_ta, mail);
			return sm_error_temp(SM_EM_SMTPS, ENOMEM);
		}
	}
	*pmail = mail;
	return SM_SUCCESS;
}

/*
**  SSM_MAIL_FREE -- free mail address
**
**	Parameters:
**		ss_ta -- SMTP server transaction context
**		mail -- mail (sender) address
**
**	Returns:
**		usual return code
*/

sm_ret_T
ssm_mail_free(ss_ta_P ss_ta, ss_mail_P mail)
{
	if (NULL == mail)
		return SM_SUCCESS;
	a2821_free(&mail->ssm_a2821);
	SM_STR_FREE(mail->ssm_pa);
	sm_rpool_free(ss_ta->ssta_rpool, mail);
	return SM_SUCCESS;
}

/*
**  SSR_RCPTS_NEW -- add a new recipient to the recipient list
**
**	Parameters:
**		ss_ta -- SMTP server transaction context
**		rcpts -- list of recipients
**		prcpt -- new recipient (output)
**
**	Returns:
**		usual return code
*/

sm_ret_T
ssr_rcpts_new(ss_ta_P ss_ta, ss_rcpts_P rcpts, ss_rcpt_P *prcpt)
{
	SM_REQUIRE(rcpts != NULL);
	SM_REQUIRE(prcpt != NULL);
	*prcpt = (ss_rcpt_P) sm_rpool_zalloc(ss_ta->ssta_rpool, sizeof(**prcpt));
	if (*prcpt == NULL)
		goto error;
	A2821_INIT_RP(&((*prcpt)->ssr_a2821), ss_ta->ssta_rpool);
	SS_RCPTS_INSERT_TAIL(rcpts, *prcpt);
	(*prcpt)->ssr_idx = ss_ta->ssta_rcpts_tot++;
	return SM_SUCCESS;

  error:
	SM_RPOOL_FREE(ss_ta->ssta_rpool, *prcpt);
	return sm_error_temp(SM_EM_SMTPS, ENOMEM);
}

/*
**  SSR_RCPT_FREE -- free a single recipient address
**
**	Parameters:
**		ss_ta -- SMTP server transaction context
**		rcpt -- recipient to free
**
**	Returns:
**		usual return code
*/

sm_ret_T
ssr_rcpt_free(ss_ta_P ss_ta, ss_rcpt_P rcpt)
{
	if (NULL == rcpt)
		return SM_SUCCESS;
	a2821_free(&rcpt->ssr_a2821);
	sm_rpool_free(ss_ta->ssta_rpool, rcpt);

	/* remove adr from list? it's done by SS_RCPTS_REMOVE_FREE() */
	/* XXX adjust some counter? */

	return SM_SUCCESS;
}

/*
**  SSR_RCPTS_FREE -- free an entire recipient list
**
**	Parameters:
**		ss_ta -- SMTP server transaction context
**		rcpts -- recipient list to free
**
**	Returns:
**		usual return code
*/

static sm_ret_T
ssr_rcpts_free(ss_ta_P ss_ta, ss_rcpts_P rcpts)
{
	ss_rcpt_P ss_rcpt, ss_rcpt_nxt;

	if (SS_RCPTS_EMPTY(rcpts))
		return SM_SUCCESS;
	for (ss_rcpt = SS_RCPTS_FIRST(rcpts); ss_rcpt != SS_RCPTS_END(rcpts);
	     ss_rcpt = ss_rcpt_nxt)
	{
		ss_rcpt_nxt = SS_RCPTS_NEXT(ss_rcpt);

		/* remove adr from list? use SS_RCPTS_REMOVE_FREE()? */
		ssr_rcpt_free(ss_ta, ss_rcpt);
	}
	SS_RCPTS_INIT(rcpts);
	return SM_SUCCESS;
}

#if MTA_USE_PMILTER
/*
**  SSR_RCPT_ADD_MOD -- add recipient modification
**
**	Parameters:
**		ss_sess -- SMTP server session context
**		type -- add/delete
**		rcpt_idx -- recipient index
**
**	Returns:
**		usual return code
*/

sm_ret_T
ssr_rcpt_add_mod(ss_sess_P ss_sess, ushort type, rcpt_idx_T rcpt_idx)
{
	sm_ret_T ret;
	ss_rcpt_P ss_rcpt;
	ss_ta_P ss_ta;

	ss_rcpt = NULL;
	ss_ta = ss_sess->ssse_ta;

	if (PM_RCPT_DEL == type) {
		ss_rcpts_P ss_rcpt_hd;

		/* outside allowed range? */
		if (rcpt_idx >= ss_ta->ssta_rcpts_tot_orig) {
			sm_log_write(ss_sess->ssse_sctx->ssc_lctx,
				SS_LCAT_SERVER, SS_LMOD_SERVER,
				SM_LOG_NOTICE, 10,
				"sev=NOTICE, func=ssr_rcpt_add_mod, delete_rcpt_idx=%u, max_rcpt_idx=%u"
				, rcpt_idx, ss_ta->ssta_rcpts_tot_orig);
			return SM_E_NOTFOUND;
		}

		ss_rcpt_hd = &ss_ta->ssta_rcpts;
		for (ss_rcpt = SS_RCPTS_FIRST(ss_rcpt_hd);
		     ss_rcpt != SS_RCPTS_END(ss_rcpt_hd);
		     ss_rcpt = SS_RCPTS_NEXT(ss_rcpt))
		{
			if (ss_rcpt->ssr_idx == rcpt_idx) {
				ss_rcpt->ssr_type = type;
				ss_rcpt->ssr_rcode = SMTP_R_DISCARD;
				--ss_ta->ssta_rcpts_ok; /* ??? */
				return SM_SUCCESS;
			}
		}
		sm_log_write(ss_sess->ssse_sctx->ssc_lctx,
			SS_LCAT_SERVER, SS_LMOD_SERVER,
			SM_LOG_NOTICE, 10,
			"sev=NOTICE, func=ssr_rcpt_add_mod, delete_rcpt_idx=%u, status=not_found"
			, rcpt_idx);
		return SM_E_NOTFOUND;
	}

	ret = ssr_rcpts_new(ss_ta, &ss_ta->ssta_rcpts, &ss_rcpt);
	if (sm_is_err(ret))
		goto error;
	(void) sm_postmaster_add_domain(ss_sess->ssse_rd,
			ss_sess->ssse_sctx->ssc_hostname, 0);
	ret = t2821_scan((sm_rdstr_P) ss_sess->ssse_rd, &ss_rcpt->ssr_a2821, 0);
	if (sm_is_err(ret))
		goto error;
	if ((uint)ret < sm_str_getlen(ss_sess->ssse_rd) &&
	    !ISSPACE(sm_str_rd_elem(ss_sess->ssse_rd, ret)))
		goto error;

	/* check args: none for now, fixme: misleading error if no address */
	if ((uint)ret < sm_str_getlen(ss_sess->ssse_rd))
		goto error;

	/* checks should be configurable */
	ret = t2821_parse(&ss_rcpt->ssr_a2821, R2821_CORRECT);
	if (sm_is_err(ret))
		goto error;

	ss_rcpt->ssr_type = type;
	return ret;

  error:
	if (ss_rcpt != NULL)
		SS_RCPTS_REMOVE_FREE(ss_ta, &ss_ta->ssta_rcpts, ss_rcpt);
	if (sm_is_success(ret))
		ret = sm_err_perm(SM_E_SYNTAX);
	return ret;
}
#endif /* MTA_USE_PMILTER */

/*
**  SS_TA_CLR -- clear out a transaction context (for reuse)
**
**	Parameters:
**		ss_ta -- SMTP server transaction context
**
**	Returns:
**		usual return code
*/

sm_ret_T
ss_ta_clr(ss_ta_P ss_ta)
{
	SM_IS_SS_TA(ss_ta);

	ssm_mail_free(ss_ta, ss_ta->ssta_mail);
	ssr_rcpts_free(ss_ta, &ss_ta->ssta_rcpts);
	SM_STR_FREE(ss_ta->ssta_mail_acc.ssa_reply_text);
	SM_STR_FREE(ss_ta->ssta_rcpt_acc.ssa_reply_text);
#if MTA_USE_PMILTER
	SM_STR_FREE(ss_ta->ssta_msg_acc.ssa_reply_text);
	SM_STR_FREE(ss_ta->ssta_mail_new);
	sm_hdrmodl_free(&ss_ta->ssta_hdrmodhd);
	SM_CSTR_FREE(ss_ta->ssta_cdb_id);
	sspm_clrreplies(ss_ta);
#endif
	SM_STR_FREE(ss_ta->ssta_msgid);
	if (ss_ta->ssta_rpool != NULL) {
		sm_rpool_delete(ss_ta->ssta_rpool);
		ss_ta->ssta_rpool = NULL;
	}

	/* clear transaction, also resets counters */
	sm_memzero(ss_ta, sizeof(*ss_ta));
	SS_RCPTS_INIT(&ss_ta->ssta_rcpts);
#if SS_TA_CHECK
	ss_ta->sm_magic = SM_SS_TA_MAGIC;
#endif
	return SM_SUCCESS;
}

/*
**  SS_TA_ABORT -- abort a transaction
**
**	Parameters:
**		ss_sess -- SMTP server session context
**		ss_ta -- SMTP server transaction context
**
**	Returns:
**		usual return code
*/

sm_ret_T
ss_ta_abort(ss_sess_P ss_sess, ss_ta_P ss_ta)
{
	SM_IS_SS_TA(ss_ta);

	/* check existing transaction */
	if (ss_ta->ssta_state != SSTA_ST_NONE) {
#if MTA_USE_PMILTER
		if (SSTA_IS_FLAG(ss_ta, SSTA_FL_PM_CALLED) &&
		    SSSE_IS_FLAG(ss_sess, SSSE_FL_PM_USE))
		{
			sm_ret_T ret;
			ss_ctx_P ss_ctx;

			ss_ctx = ss_sess->ssse_sctx;
			ret = sm_s2m_abort(ss_sess, ss_ctx->ssc_s2m_ctx, ss_sess->ssse_id);
			if (sm_is_err(ret)) {
				sm_log_write(ss_ctx->ssc_lctx,
					SS_LCAT_SERVER, SS_LMOD_SERVER,
					SM_LOG_ERROR, 6,
					"sev=ERROR, func=ss_ta_abort, ss_sess=%s, sm_s2m_abort=%m"
					, ss_sess->ssse_id, ret);
			}
		}
#endif /* MTA_USE_PMILTER */

		/* XXX ... */
		/* abort all performed actions */
		if (ss_ta->ssta_state >= SSTA_ST_MAIL) {
			(void) sm_rcb_close_decn(ss_sess->ssse_rcb);
		}
	}
	ss_ta_clr(ss_ta);
	return SM_SUCCESS;
}

/*
**  SS_TA_INIT -- initialize transaction context
**	ss_ta is NOT allocated, it is a local variable in ss_hdl_session()
**
**	Parameters:
**		ss_sess -- SMTP server session context
**		ss_ta -- SMTP server transaction context
**
**	Returns:
**		usual return code
*/

sm_ret_T
ss_ta_init(ss_sess_P ss_sess, ss_ta_P ss_ta)
{
	sm_rpool_P rp;
	sm_ret_T ret;

	rp = NULL;
	ret = SM_SUCCESS;
	sm_memzero(ss_ta, sizeof(*ss_ta));
	if (Rpools) {
		rp = sm_rpool_new(NULL);
		if (NULL == rp) {
			ret = sm_error_temp(SM_EM_SMTPS, ENOMEM);
			goto error;
		}
		ss_ta->ssta_rpool = rp;
	}
	ret = ss_id_next(ss_sess->ssse_sctx->ssc_id, ss_ta->ssta_id);
	if (sm_is_err(ret))
		goto error;
	SS_RCPTS_INIT(&ss_ta->ssta_rcpts);
	ss_ta->ssta_state = SSTA_ST_INIT;

	/* inherit some flags from session; better (efficient) way?? */
	if (SSSE_IS_FLAG(ss_sess, SSSE_FL_DISCARD))
		SSTA_SET_FLAG(ss_ta, SSTA_FL_DISCARD);
	if (SSSE_IS_CFLAG(ss_sess, SSSE_CFL_DELAY_CHKS))
		SSTA_SET_FLAG(ss_ta, SSTA_FL_DELAY_CHKS);
#if MTA_USE_PMILTER
	if (SSSE_IS_FLAG(ss_sess, SSSE_FL_PM_USE))
		SSTA_SET_FLAG(ss_ta, SSTA_FL_PM_USE);
#endif

#if SS_TA_CHECK
	ss_ta->sm_magic = SM_SS_TA_MAGIC;
#endif
	return SM_SUCCESS;

  error:
	if (rp != NULL) {
		sm_rpool_delete(rp);
		ss_ta->ssta_rpool = NULL;
	}
	return ret;
}

/*
**  SS_SESS_FREE -- free a SMTP server session context
**
**	Parameters:
**		ss_sess -- SMTP server session context
**
**	Returns:
**		usual return code
*/

sm_ret_T
ss_sess_free(ss_sess_P ss_sess)
{
	ss_ta_P ss_ta;

	if (NULL == ss_sess)
		return SM_SUCCESS;

	/* make sure ss_sess isn't referenced anymore */
	if (ss_sess->ssse_sctx != NULL && ss_sess->ssse_sctx->ssc_s2q_ctx != NULL)
		(void) ss_clr_sess_rq(ss_sess->ssse_sctx->ssc_s2q_ctx, ss_sess);

	/* should this free the ss_ta inside? */
	ss_ta = ss_sess->ssse_ta;
	if (ss_ta != NULL)
		(void) ss_ta_abort(ss_sess, ss_ta);
	if (ss_sess->ssse_fp != NULL) {
		sm_io_close(ss_sess->ssse_fp, SM_IO_CF_NONE);
		ss_sess->ssse_fp = NULL;
#if MTA_USE_TLS
		ss_sess->ssse_fptls = NULL;
		(void) tlsi_free(ss_sess->ssse_tlsi);
#endif
	}

#if MTA_USE_SASL
	if (SSSE_IS_FLAG(ss_sess, SSSE_FL_SASL_OK)) {
		ss_ctx_P ss_ctx;

		ss_ctx = ss_sess->ssse_sctx;
		if (ss_ctx != NULL &&
		    ss_ctx->ssc_sasl_ctx != NULL &&
		    ss_sess->ssse_sasl_conn != NULL)
		{
			sasl_dispose(&ss_sess->ssse_sasl_conn);
		}
		SSSE_CLR_FLAG(ss_sess, SSSE_FL_SASL_OK);
	}
#endif /* MTA_USE_SASL */

	SM_STR_FREE(ss_sess->ssse_rd);
	SM_STR_FREE(ss_sess->ssse_wr);
	SM_STR_FREE(ss_sess->ssse_str);
	SM_STR_FREE(ss_sess->ssse_helo);
	SM_STR_FREE(ss_sess->ssse_acc.ssa_reply_text);
	SM_CSTR_FREE(ss_sess->ssse_cltname);
	SM_RCB_FREE(ss_sess->ssse_rcb);
#if MTA_USE_PMILTER
	/* SM_STR_FREE(ss_sess->ssse_ehlo_acc.ssa_reply_text); */
	SM_STR_FREE(ss_sess->ssse_macv);
# if MTA_USE_SASL
	SM_STR_FREE(ss_sess->ssse_sasl_mech);
	SM_STR_FREE(ss_sess->ssse_sasl_authen);
	SM_STR_FREE(ss_sess->ssse_sasl_author);
# endif /* MTA_USE_SASL */
#endif /* MTA_USE_PMILTER */
	if (ss_sess->ssse_cond_rd != NULL)
		st_cond_destroy(ss_sess->ssse_cond_rd);
#if MTA_USE_TLS
	if (ss_sess->ssse_cnf != NULL) {
		sm_conf_destroy(ss_sess->ssse_cnf);
		ss_sess->ssse_cnf = NULL;
	}
#endif

	ss_sess->ssse_sctx = NULL;
#if SS_SESS_CHECK
	ss_sess->sm_magic = SM_MAGIC_NULL;
#endif
	sm_free_size(ss_sess, sizeof(*ss_sess));
	return SM_SUCCESS;
}

/*
**  SS_SESS_NEW -- create a new SMTP server session context
**
**	Parameters:
**		ss_ctx -- SMTP server context
**		pss_sess -- (pointer to) SMTP server session context (output)
**
**	Returns:
**		usual return code
*/

sm_ret_T
ss_sess_new(ss_ctx_P ss_ctx, ss_sess_P *pss_sess)
{
	sm_str_P str;
	sm_ret_T ret;
	ss_sess_P ss_sess;
	uint u;
	extern sm_cstr_P HostnameNone;

	SM_REQUIRE(pss_sess != NULL);
	ss_sess = (ss_sess_P) sm_zalloc(sizeof(*ss_sess));
	if (NULL == ss_sess)
		goto errnomem;
	str = sm_str_new(NULL, SMTPBUFSIZE, SMTPMAXSIZE);
	if (NULL == str)
		goto errnomem;
	ss_sess->ssse_rd = str;
	str = sm_str_new(NULL, SMTPBUFSIZE, SMTPMAXSIZE);
	if (NULL == str)
		goto errnomem;
	ss_sess->ssse_wr = str;
	str = sm_str_new(NULL, SMTPBUFSIZE, SMTPMAXSIZE);
	if (NULL == str)
		goto errnomem;
	ss_sess->ssse_str = str;
	str = sm_str_new(NULL, 32, SMTPMAXSIZE);
	if (NULL == str)
		goto errnomem;
	ss_sess->ssse_helo = str;

#define NO_EHLONAME "Did_not_use_EHLO/HELO"
	sm_str_scat(ss_sess->ssse_helo, NO_EHLONAME);

	str = sm_str_new(NULL, 64, SMTPMAXSIZE);
	if (NULL == str)
		goto errnomem;

	ss_sess->ssse_acc.ssa_reply_text = str;
	ss_sess->ssse_rcb = sm_rcb_new(NULL, S2Q_RCB_SIZE, QSS_RC_MAXSZ);
	if (NULL == ss_sess->ssse_rcb)
		goto errnomem;
	ss_sess->ssse_cond_rd = st_cond_new();
	if (NULL == ss_sess->ssse_cond_rd) {
		ret = sm_error_temp(SM_EM_SMTPS, errno);
		goto error;
	}
	ss_sess->ssse_s2q_idx = SSSE_S2Q_IDX_NONE;
#if SS_SESS_CHECK
	ss_sess->sm_magic = SM_SS_SESS_MAGIC;
#endif
	ss_sess->ssse_cltname = SM_CSTR_DUP(HostnameNone);

#if MTA_USE_TLS
	ss_sess->ssse_maprescnf = sm_err_perm(SM_E_NOTFOUND);
	ret = tlsi_new(&ss_sess->ssse_tlsi);
#endif

#if MTA_USE_SASL
	if (SSC_IS_FLAG(ss_ctx, SSC_FL_SASL_OK))
		SSSE_SET_FLAG(ss_sess, SSSE_FL_SASL_OK);
	if (SSSE_IS_FLAG(ss_sess, SSSE_FL_SASL_OK)) {
		ret = sasl_server_new("smtp",
			(const char *)sm_str_getdata(ss_ctx->ssc_hostname),
			NULL, NULL, NULL, NULL, 0, &ss_sess->ssse_sasl_conn);
		if (ret != SASL_OK) {
			SSSE_CLR_FLAG(ss_sess, SSSE_FL_SASL_OK);
			sm_log_write(ss_ctx->ssc_lctx,
				SS_LCAT_SERVER, SS_LMOD_SERVER,
				SM_LOG_WARN, 9,
				"sev=WARN, func=ss_sess_new, sasl_server_new=%r",
				ret);
		}
		else {
			sm_log_write(ss_ctx->ssc_lctx,
				SS_LCAT_SERVER, SS_LMOD_SERVER,
				SM_LOG_INFO, 19,
				"sev=INFO, func=ss_sess_new, sasl_server_new=%p",
				ss_sess->ssse_sasl_conn);
		}
	}
#if 0
	/* security layer not yet implemented */
	if (SSSE_IS_FLAG(ss_sess, SSSE_FL_SASL_OK)) {
		sasl_security_properties_t ssp;

		(void) memset(&ssp, '\0', sizeof ssp);

		ssp.max_ssf = 168;
		ssp.maxbufsize = MAXOUTLEN;
		ssp.security_flags = SASLOpts & SASL_SEC_MASK;
		ret = sasl_setprop(conn, SASL_SEC_PROPS, &ssp);
		if (ret != SASL_OK) {
			SSSE_CLR_FLAG(ss_sess, SSSE_FL_SASL_OK);
			sm_log_write(ss_ctx->ssc_lctx,
				SS_LCAT_SERVER, SS_LMOD_SERVER,
				SM_LOG_WARN, 9,
				"sev=WARN, func=ss_sess_new, set_sec=%r",
				ret);
		}

	}
#endif
	if (SSSE_IS_FLAG(ss_sess, SSSE_FL_SASL_OK)) {

		/*
		**  SASL set properties for sasl
		**  set local/remote IP
		**
		**  XXX where exactly are these used/required?
		**  Kerberos_v4
		*/

		/* set properties */
		sm_memzero(&ss_ctx->ssc_sasl_ctx->sm_sasl_ssp,
			sizeof(ss_ctx->ssc_sasl_ctx->sm_sasl_ssp));

		ss_ctx->ssc_sasl_ctx->sm_sasl_ssp.security_flags =
				ss_ctx->ssc_sasl_ctx->sm_sasl_sec_flags
					& SASL_SEC_MAXIMUM;
		if (sasl_setprop(ss_sess->ssse_sasl_conn, SASL_SEC_PROPS,
				&ss_ctx->ssc_sasl_ctx->sm_sasl_ssp) != SASL_OK)
			SSSE_CLR_FLAG(ss_sess, SSSE_FL_SASL_OK);

		if (SSSE_IS_FLAG(ss_sess, SSSE_FL_SASL_OK)) {
			int ext_ssf;

			/*
			**  external security strength factor;
			**	currently we have none so zero
			*/

			ext_ssf = 0;
			if (!((sasl_setprop(ss_sess->ssse_sasl_conn,
					SASL_SSF_EXTERNAL, &ext_ssf) == SASL_OK) &&
			      (sasl_setprop(ss_sess->ssse_sasl_conn,
					SASL_AUTH_EXTERNAL, NULL) == SASL_OK)))
				SSSE_CLR_FLAG(ss_sess, SSSE_FL_SASL_OK);
		}
		if (SSSE_IS_FLAG(ss_sess, SSSE_FL_SASL_OK)) {
			ss_ctx->ssc_sasl_ctx->sm_sasl_n_mechs =
				sm_saslmechs(ss_ctx->ssc_sasl_ctx, ss_sess->ssse_sasl_conn,
					&ss_sess->ssse_sasl_mech_list);
			if (ss_ctx->ssc_sasl_ctx->sm_sasl_n_mechs <= 0)
				SSSE_CLR_FLAG(ss_sess, SSSE_FL_SASL_OK);
		}
	}
	sm_log_write(ss_ctx->ssc_lctx,
		SS_LCAT_SERVER, SS_LMOD_SERVER,
		SM_LOG_INFO, 19,
		"sev=INFO, func=ss_sess_new, sasl_conn=%p, flags=%x",
		ss_sess->ssse_sasl_conn, ss_sess->ssse_flags);
#endif /* MTA_USE_SASL */

	/* inherit some flags from context */
	ss_sess->ssse_cnf_flags = ss_ctx->ssc_cnf.ss_cnf_cflags & SSSE_CFL_INHERIT;
	if (!SM_IS_FLAG(ss_ctx->ssc_cnf.ss_cnf_cflags, SSC_CFL_PMILTER))
		ss_sess->ssse_cnf_flags &= ~SSSE_CFL_RSAD;
#if MTA_USE_TLS
	ss_sess->ssse_tlsreq_cnf.tlsreqcnf_viol = ss_ctx->ssc_cnf.ss_cnf_tlsreqfail;
#endif

	for (u = 0; u < SS_MAX_COMM_SRVS; u++)
		ss_sess->ssse_s2q_id[u] = S2Q_ID_NONE;

	ss_sess->ssse_sctx = ss_ctx;
	ret = ss_id_next(ss_ctx->ssc_id, ss_sess->ssse_id);
	*pss_sess = ss_sess;
	return ret;

  errnomem:
	ret = sm_error_temp(SM_EM_SMTPS, ENOMEM);
  error:
	if (ss_sess != NULL) {
		/* free all allocated data! */
		SM_STR_FREE(ss_sess->ssse_rd);
		SM_STR_FREE(ss_sess->ssse_wr);
		SM_STR_FREE(ss_sess->ssse_str);
		SM_STR_FREE(ss_sess->ssse_helo);
		SM_STR_FREE(ss_sess->ssse_acc.ssa_reply_text);
		SM_CSTR_FREE(ss_sess->ssse_cltname);
		SM_RCB_FREE(ss_sess->ssse_rcb);
		if (ss_sess->ssse_cond_rd != NULL)
			st_cond_destroy(ss_sess->ssse_cond_rd);
#if MTA_USE_TLS
		if (ss_sess->ssse_cnf != NULL) {
			sm_conf_destroy(ss_sess->ssse_cnf);
			ss_sess->ssse_cnf = NULL;
		}
#endif
	}

	/* complain */
	sm_log_write(ss_ctx->ssc_lctx,
		SS_LCAT_SERVER, SS_LMOD_SERVER,
		SM_LOG_ERR, 1,
		"sev=ERROR, func=ss_sess_new, status=cannot_create_session_context");
	ss_sess_free(ss_sess);
	*pss_sess = NULL;
	return ret;
}

/*
**  SS_SESS_FEAT -- set SMTP server session context features
**
**	Parameters:
**		ss_sess -- SMTP server session context
**
**	Returns:
**		usual return code
**
**	Side Effects: if a session_feature is found that matches the name
**		then its options are copied into the session context.
*/

static sm_ret_T
ss_sess_feat(ss_sess_P ss_sess, const char *feat_name, size_t feat_name_len)
{
	uint u;
	ss_ctx_P ss_ctx;

	SM_IS_SS_SESS(ss_sess);
	ss_ctx = ss_sess->ssse_sctx;
	SM_IS_SS_CTX(ss_ctx);
	if (NULL == feat_name)
		return SM_NOMATCH;
	if (0 == feat_name_len)
		return SM_NOMATCH;
	if (NULL == ss_ctx->ssc_cnf.ss_cnf_se_feats.sssfs_feat)
		return SM_NOMATCH;
	for (u = 0; u < ss_ctx->ssc_cnf.ss_cnf_se_feats.sssfs_n; u++) {
		ss_se_feat_T ss_se_feat;

		ss_se_feat = ss_ctx->ssc_cnf.ss_cnf_se_feats.sssfs_feat[u];
		if (ss_se_feat.sssf_name != NULL
		    && strlen(ss_se_feat.sssf_name) == feat_name_len
		    && sm_memeq(ss_se_feat.sssf_name, feat_name, feat_name_len))
		{
			ss_sess->ssse_cnf_flags = ss_se_feat.sssf_flags & SSSE_CFL_INHERIT;
			return SM_SUCCESS;
		}
	}
	return SM_NOMATCH;
}

/*
**  SS_SESS_CONF -- set SMTP server session context configuration
**
**	Parameters:
**		ss_sess -- SMTP server session context
**
**	Returns:
**		usual return code
*/

static sm_conf_definition_T
ssse_tlsreq_defs[] = {
TLSREQ_DEFS(tlsreq_cnf_T, tlsreqcnf),

/* Sentinel */
{ SM_CONF_DEF_MAGIC, NULL, 0, 0, 0, NULL, 0, NULL, NULL, NULL, NULL SM_LC_NO_ISSET SM_LC_SET_MAGIC(0)}
};

static sm_conf_definition_T
ssse_mapcnf_defs[] = {
{ SM_CONF_DEF_MAGIC, "tls_requirements", sm_conf_type_section,
	offsetof(ss_sess_T, ssse_tlsreq_cnf), sizeof(tlsreq_cnf_T), NULL,
	SM_CONF_FLAG_KEEP_DEFAULT,
	ssse_tlsreq_defs,
	NULL, NULL, NULL
	SM_LC_NO_ISSET	SM_LC_SET_MAGIC(0) },

{ SM_CONF_DEF_MAGIC, "session_feature",	sm_conf_type_string,
	offsetof(ss_sess_T, ssse_feat_name),	0,
	NULL,
	0,	NULL,	NULL,	NULL,
	"name of session feature"
	SM_LC_NO_ISSET	SM_LC_SET_MAGIC(0) },

/* Sentinel */
{ SM_CONF_DEF_MAGIC, NULL, 0, 0, 0, NULL, 0, NULL, NULL, NULL, NULL SM_LC_NO_ISSET SM_LC_SET_MAGIC(0)}
};


sm_ret_T
ss_sess_conf(ss_sess_P ss_sess)
{
	int err;
	ss_ctx_P ss_ctx;
#define SS_SESS_CONF "ss_session_conf"

	SM_IS_SS_SESS(ss_sess);
	ss_ctx = ss_sess->ssse_sctx;
	SM_IS_SS_CTX(ss_ctx);
	ss_sess->ssse_cnf = sm_conf_new(SS_SESS_CONF);
	if (NULL == ss_sess->ssse_cnf) {
		err = errno;

		sm_log_write(ss_ctx->ssc_lctx,
			SS_LCAT_SERVER, SS_LMOD_SERVER,
			SM_LOG_ERR, 8,
			"sev=ERROR, func=ss_sess_conf, sm_conf_new=NULL, errno=%d\n",
			err);
		return sm_error_temp(SM_EM_SMTPS, ENOMEM);
	}
	err = sm_conf_read_data(ss_sess->ssse_cnf,
				(char *)sm_str_getdata(ss_sess->ssse_str),
				sm_str_getlen(ss_sess->ssse_str), true);
	if (err != 0) {
		sm_prt_conferr(SS_SESS_CONF, ss_sess->ssse_cnf, err, smioerr);
		sm_log_write(ss_ctx->ssc_lctx,
			SS_LCAT_SERVER, SS_LMOD_SERVER,
			SM_LOG_ERR, 8,
			"sev=ERROR, func=ss_sess_conf, sm_conf_read_data=%m\n",
			err);
		sm_conf_destroy(ss_sess->ssse_cnf);
		ss_sess->ssse_cnf = NULL;
		return err;
	}

	err = sm_conf_scan(ss_sess->ssse_cnf, ssse_mapcnf_defs, 0, ss_sess);
	if (err != 0) {
		sm_log_write(ss_ctx->ssc_lctx,
			SS_LCAT_SERVER, SS_LMOD_SERVER,
			SM_LOG_ERR, 8,
			"sev=ERROR, func=ss_sess_conf, sm_conf_scan=%m\n",
			err);
	}

	if (ss_ctx->ssc_cnf.ss_cnf_se_feats.sssfs_n > 0 &&
	    ss_sess->ssse_feat_name != NULL) {
		sm_ret_T r;

		r = ss_sess_feat(ss_sess, ss_sess->ssse_feat_name,
				strlen(ss_sess->ssse_feat_name));
		sm_log_write(ss_ctx->ssc_lctx,
			SS_LCAT_SERVER, SS_LMOD_SERVER,
			SM_LOG_DEBUG, 12,
			"sev=DBG, func=ss_sess_conf, ss_sess=%s, session_features=%#s, ss_sess_feat=%r"
			, ss_sess->ssse_id, ss_sess->ssse_feat_name, r);
	}

	return err;
}


syntax highlighted by Code2HTML, v. 0.9.1