/*
 * 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: sc.c,v 1.78 2007/10/25 05:24:06 ca Exp $")

#include "sm/assert.h"
#include "sm/error.h"
#include "sm/str.h"
#include "sm/limits.h"
#include "sm/qmgrcomm.h"
#include "sm/memops.h"
#include "sm/net.h"
#include "smtpc.h"
#include "c2q.h"
#include "log.h"

/*
**  SC_T_CTX_CLR -- clear a SMTPC thread context such that it can be reused.
**
**	Parameters:
**		sc_t_ctx -- SMTPC thread context
**
**	Returns:
**		SM_SUCCESS
*/

sm_ret_T
sc_t_ctx_clr(sc_t_ctx_P sc_t_ctx)
{
	if (NULL == sc_t_ctx)
		return SM_SUCCESS;
	SM_IS_SC_T_CTX(sc_t_ctx);
	(void) sm_rcb_close_decn(sc_t_ctx->sct_rcb);
	return SM_SUCCESS;
}

/*
**  SC_T_CTX_NEW -- create a new SMTPC thread context.
**	allocate new sc_t_ctx structure, put a pointer to it into
**	the array of thread contexts in SMTPC context.
**
**	Parameters:
**		sc_ctx -- SMTPC context
**		psc_t_ctx -- pointer to SMTPC thread context (output)
**		thr_id -- thread id
**
**	Returns:
**		usual sm_error code
*/

sm_ret_T
sc_t_ctx_new(sc_ctx_P sc_ctx, sc_t_ctx_P *psc_t_ctx, uint thr_id)
{
	sm_ret_T ret;
	sc_t_ctx_P sc_t_ctx;

	SM_REQUIRE(psc_t_ctx != NULL);
	sc_t_ctx = sm_zalloc(sizeof(*sc_t_ctx));
	if (NULL == sc_t_ctx)
		return sm_error_temp(SM_EM_SMTPC, ENOMEM);

	sc_t_ctx->sct_rcb = sm_rcb_new(NULL, QSS_RC_SZ, QSS_RC_MAXSZ);
	if (NULL == sc_t_ctx->sct_rcb)
	{
		ret = sm_error_temp(SM_EM_SMTPC, ENOMEM);
		goto error;
	}
	sc_t_ctx->sct_sc_ctx = sc_ctx;
	sc_t_ctx->sct_thr_id = thr_id;
	sc_t_ctx->sct_cond_rd = st_cond_new();
	if (NULL == sc_t_ctx->sct_cond_rd)
	{
		ret = sm_error_temp(SM_EM_SMTPC, errno);
		goto error;
	}

	SM_ASSERT(thr_id < SC_MAX_THREADS(sc_ctx));
	(sc_ctx->scc_scts)[thr_id] = sc_t_ctx;
	sc_t_ctx->sm_magic = SM_SC_T_CTX_MAGIC;
	*psc_t_ctx = sc_t_ctx;
	return SM_SUCCESS;

 error:
	if (sc_t_ctx->sct_cond_rd != NULL)
		(void) st_cond_destroy(sc_t_ctx->sct_cond_rd);
	SM_RCB_FREE(sc_t_ctx->sct_rcb);
	sm_free_size(sc_t_ctx, sizeof(*sc_t_ctx));
	return ret;
}

/*
**  SC_T_CTX_FREE -- free a SMTPC thread context,
**	clear entry in the array of thread contexts in SMTPC context.
**
**	Parameters:
**		sc_ctx -- SMTPC context
**		sc_t_ctx -- SMTPC thread context
**
**	Returns:
**		usual sm_error code
*/

sm_ret_T
sc_t_ctx_free(sc_ctx_P sc_ctx, sc_t_ctx_P sc_t_ctx)
{
	int r;
	sm_ret_T ret;
	uint thr_id;

	SM_REQUIRE(sc_ctx != NULL);
	if (NULL == sc_t_ctx)
		return SM_SUCCESS;
	SM_IS_SC_T_CTX(sc_t_ctx);

	ret = SM_SUCCESS;
	sc_t_ctx->sct_status = SC_T_NOTRDY;
	thr_id = sc_t_ctx->sct_thr_id;
	r = st_cond_destroy(sc_t_ctx->sct_cond_rd);
	if (r == -1)
	{
		sm_log_write(sc_ctx->scc_lctx,
			SC_LCAT_INTERN, SC_LMOD_INTERN,
			SM_LOG_ERR, 7,
			"sev=ERROR, func=sc_t_ctx_free, thread=%u, st_cond_destroy=%x",
			thr_id, r);
		ret = sm_error_perm(SM_EM_SMTPC, errno);
	}

	SM_ASSERT(thr_id < SC_MAX_THREADS(sc_ctx));
	(sc_ctx->scc_scts)[thr_id] = NULL;
	SM_RCB_FREE(sc_t_ctx->sct_rcb);
	sc_t_ctx->sm_magic = SM_MAGIC_NULL;
	sm_free_size(sc_t_ctx, sizeof(*sc_t_ctx));

	return ret;
}

/*
**  SC_SESS_NEW -- create a new SMTPC session context,
**	set it as current context in SMTPC thread context.
**
**	Parameters:
**		psc_sess -- pointer to SMTPC session context (output)
**		sc_t_ctx -- SMTPC thread context
**
**	Returns:
**		usual sm_error code
*/

#define SC_IOBUFSIZE (16*1024)

sm_ret_T
sc_sess_new(sc_sess_P *psc_sess, sc_t_ctx_P sc_t_ctx)
{
	sm_ret_T ret;
	sc_sess_P sc_sess;
	sm_str_P str;

	SM_REQUIRE(psc_sess != NULL);
	sc_sess = sm_zalloc(sizeof(*sc_sess));
	if (NULL == sc_sess)
		goto errnomem;

	sc_sess->scse_sct_ctx = sc_t_ctx;
	sc_t_ctx->sct_sess = sc_sess;
	str = sm_str_new(NULL, SC_IOBUFSIZE, SC_IOBUFSIZE);
	if (NULL == str)
		goto errnomem;
	sc_sess->scse_wr = str;
	str = sm_str_new(NULL, SC_IOBUFSIZE, SC_IOBUFSIZE);
	if (NULL == str)
		goto errnomem;
	sc_sess->scse_rd = str;
	str = sm_str_new(NULL, SC_IOBUFSIZE, SC_IOBUFSIZE);
	if (NULL == str)
		goto errnomem;
	sc_sess->scse_str = str;

#if MTA_USE_TLS
	ret = tlsi_new(&sc_sess->scse_tlsi);
	if (sm_is_err(ret))
		goto error;
#endif

	sm_memzero(&sc_sess->scse_rmt_addr, sizeof(sc_sess->scse_rmt_addr));

	sc_sess->scse_rmt_addr.sin.sin_family = AF_INET;
	sc_sess->scse_rmt_addr.sin.sin_port = htons(SMTPC_PORT);
	sc_sess->scse_c2q_idx = SCSE_C2Q_IDX_NONE;
	sc_sess->scse_state = SCSE_ST_NONE;

	/* initialize flags */
	SCSE_INHERIT_FLAG(sc_sess);
	sc_sess->sm_magic = SM_SC_SESS_MAGIC;
	*psc_sess = sc_sess;
	return SM_SUCCESS;

 errnomem:
	ret = sm_error_temp(SM_EM_SMTPC, ENOMEM);
#if MTA_USE_TLS
 error:
#endif
	(void) sc_sess_free(sc_sess, sc_t_ctx);
	return ret;
}

/*
**  SC_SESS_CLR -- clear a SMTPC session context (reset values to default)
**
**	Parameters:
**		sc_sess -- SMTPC session context
**
**	Returns:
**		usual sm_error code
*/

sm_ret_T
sc_sess_clr(sc_sess_P sc_sess)
{
	extern c2q_ctx_T c2q_ctx;

	if (NULL == sc_sess)
		return SM_SUCCESS;
	SM_IS_SC_SE(sc_sess);

	(void) sc_rm_sess_rq(sc_sess, &c2q_ctx);
	if (sc_sess->scse_fp != NULL)
	{
		sm_io_close(sc_sess->scse_fp, SM_IO_CF_NONE);
		sc_sess->scse_fp = NULL;
	}

	/* other fields? */
	sc_sess->scse_cap = SCSE_CAP_NONE;
	sc_sess->scse_flags = SCSE_FL_NONE;
	sc_sess->scse_da_idx = 0;
	sc_sess->scse_max_sz_b = 0;
	SCSE_INHERIT_FLAG(sc_sess);
#if 0
	sc_sess->scse_client = NULL;
#endif
	/* sc_sess->scse_ta = NULL; ??? */
#if 0
	(void) sm_rcb_close_decn(sc_sess->scse_rcb);
#endif
	SESSTA_CLR(sc_sess->scse_id);
	sc_sess->scse_c2q_idx = SCSE_C2Q_IDX_NONE;
	sc_sess->scse_state = SCSE_ST_NONE;
	sc_sess->scse_err_st = 0;
	SM_STR_CLR(sc_sess->scse_reply);
	SM_STR_CLR(sc_sess->scse_wr);
	SM_STR_CLR(sc_sess->scse_rd);
	SM_STR_CLR(sc_sess->scse_str);
	sm_memzero(&sc_sess->scse_rmt_addr, sizeof(sc_sess->scse_rmt_addr));

	sc_sess->scse_rmt_addr.sin.sin_family = AF_INET;
	sc_sess->scse_rmt_addr.sin.sin_port = htons(SMTPC_PORT);
#if MTA_USE_TLS
	(void) tlsi_clr(sc_sess->scse_tlsi);
#endif
	return SM_SUCCESS;
}

/*
**  SC_SESS_FREE -- free a SMTPC session context,
**	including current transaction and clearing pointer in sc_t_ctx
**
**	Parameters:
**		sc_sess -- SMTPC session context
**		sc_t_ctx -- SMTPC thread context
**
**	Returns:
**		usual sm_error code
*/

sm_ret_T
sc_sess_free(sc_sess_P sc_sess, sc_t_ctx_P sc_t_ctx)
{
	sm_ret_T ret;
	extern c2q_ctx_T c2q_ctx;

	if (NULL == sc_sess)
		return SM_SUCCESS;
	SM_IS_SC_SE(sc_sess);

	ret = sc_rm_sess_rq(sc_sess, &c2q_ctx);
	sc_t_ctx->sct_sess = NULL;
	if (sc_sess->scse_fp != NULL)
		sm_io_close(sc_sess->scse_fp, SM_IO_CF_NONE);
	ret = sc_ta_free(sc_sess->scse_ta);
	SM_STR_FREE(sc_sess->scse_wr);
	SM_STR_FREE(sc_sess->scse_rd);
	SM_STR_FREE(sc_sess->scse_str);
	SM_STR_FREE(sc_sess->scse_reply);
#if MTA_USE_TLS
	ret = tlsi_free(sc_sess->scse_tlsi);
	if (sc_sess->scse_cnf != NULL) {
		sm_conf_destroy(sc_sess->scse_cnf);
		sc_sess->scse_cnf = NULL;
	}
#endif
	sc_sess->sm_magic = SM_MAGIC_NULL;
	SM_FREE_SIZE(sc_sess, sizeof(*sc_sess));
	return ret;
}

/*
**  SC_MAIL_NEW -- create new mail entry
**
**	Parameters:
**		sc_ta -- transaction
**
**	Returns:
**		usual sm_error code
**
**	Note: this does not create scm_pa.
*/

sm_ret_T
sc_mail_new(sc_ta_P sc_ta)
{
	sc_mail_P sc_mail;

	SM_IS_SC_TA(sc_ta);
	sc_mail = (sc_mail_P) sm_rpool_zalloc(sc_ta->scta_rpool,
					sizeof(*sc_mail));
	if (NULL == sc_mail)
		return sm_error_temp(SM_EM_SMTPC, ENOMEM);
	sc_ta->scta_mail = sc_mail;
	return SM_SUCCESS;
}

/*
**  SC_MAIL_FREE -- free mail entry
**
**	Parameters:
**		sc_ta -- transaction
**
**	Returns:
**		usual sm_error code
**
**	Note: this does free scm_pa.
*/

sm_ret_T
sc_mail_free(sc_ta_P sc_ta)
{
	SM_IS_SC_TA(sc_ta);
	if (NULL == sc_ta->scta_mail)
		return SM_SUCCESS;
	SM_STR_FREE(sc_ta->scta_mail->scm_pa);
	SM_STR_FREE(sc_ta->scta_mail->scm_reply);
	SM_RPOOL_FREE_SIZE(sc_ta->scta_rpool, sc_ta->scta_mail,
			sizeof(*sc_ta->scta_mail));
	return SM_SUCCESS;
}

/*
**  SC_RCPTS_NEW -- add a new recipient to the recipient list
**
**	Parameters:
**		sc_ta -- transaction
**		sc_rcpt_pa -- recipient (printable address)
**		sc_rcpt_idx -- recipient index
**		psc_rcpt -- recipient (return parameter)
**
**	Returns:
**		usual sm_error code
*/

sm_ret_T
sc_rcpts_new(sc_ta_P sc_ta, sm_str_P sc_rcpt_pa, rcpt_idx_T sc_rcpt_idx, sc_rcpt_P *psc_rcpt)
{
	sc_rcpt_P sc_rcpt;

	SM_IS_SC_TA(sc_ta);
	SM_REQUIRE(sc_rcpt_pa != NULL);
	SM_REQUIRE(psc_rcpt != NULL);
	sc_rcpt = (sc_rcpt_P) sm_rpool_zalloc(sc_ta->scta_rpool, sizeof(*sc_rcpt));
	if (NULL == sc_rcpt)
		goto error;
	sc_rcpt->scr_pa = sc_rcpt_pa;
	sc_rcpt->scr_idx = sc_rcpt_idx;
	sc_rcpt->scr_st = SMTP_NO_REPLY;
	SC_RCPTS_INSERT_TAIL(&sc_ta->scta_rcpts, sc_rcpt);
	sc_ta->scta_rcpts_tot++;
	*psc_rcpt = sc_rcpt;
	return SM_SUCCESS;

  error:
	SM_RPOOL_FREE(sc_ta->scta_rpool, sc_rcpt);
	return sm_error_temp(SM_EM_SMTPC, ENOMEM);
}

/*
**  SC_RCPT_FREE -- free a single recipient address
**
**	Parameters:
**		sc_ta -- transaction
**		sc_rcpt -- recipient
**
**	Returns:
**		usual sm_error code
*/

sm_ret_T
sc_rcpt_free(sc_ta_P sc_ta, sc_rcpt_P sc_rcpt)
{
	if (NULL == sc_rcpt)
		return SM_SUCCESS;
	SM_IS_SC_TA(sc_ta);
	SM_REQUIRE(sc_ta->scta_rcpts_tot > 0);
	sm_str_free(sc_rcpt->scr_pa);
	SM_STR_FREE(sc_rcpt->scr_reply);
	SC_RCPTS_REMOVE(&sc_ta->scta_rcpts, sc_rcpt);
#if MTA_USE_TLS
	if (sc_rcpt->scr_cnf != NULL) {
		sm_conf_destroy(sc_rcpt->scr_cnf);
		sc_rcpt->scr_cnf = NULL;
	}
#endif
	sm_rpool_free(sc_ta->scta_rpool, sc_rcpt);
	sc_ta->scta_rcpts_tot--;
	return SM_SUCCESS;
}

/*
**  SC_RCPTS_FREE -- free an entire recipient list
**
**	Parameters:
**		sc_ta -- transaction
**
**	Returns:
**		usual sm_error code
*/

sm_ret_T
sc_rcpts_free(sc_ta_P sc_ta)
{
	sc_rcpts_P sc_rcpts;
	sc_rcpt_P sc_rcpt, sc_rcpt_nxt;

	SM_IS_SC_TA(sc_ta);
	sc_rcpts = &sc_ta->scta_rcpts;
	if (SC_RCPTS_EMPTY(sc_rcpts))
		return SM_SUCCESS;
	for (sc_rcpt = SC_RCPTS_FIRST(sc_rcpts); sc_rcpt != SC_RCPTS_END(sc_rcpts);
	     sc_rcpt = sc_rcpt_nxt)
	{
		sc_rcpt_nxt = SC_RCPTS_NEXT(sc_rcpt);
		sc_rcpt_free(sc_ta, sc_rcpt);
	}
	SC_RCPTS_INIT(&sc_ta->scta_rcpts);
	return SM_SUCCESS;
}

/*
**  SC_TA_NEW -- create a new SMTPC transaction context,
**	assign it to SMTPC session as current transaction.
**
**	Parameters:
**		psc_ta -- pointer to SMTPC transaction context (output)
**		sc_sess -- SMTPC session context
**
**	Returns:
**		usual sm_error code
*/

sm_ret_T
sc_ta_new(sc_ta_P *psc_ta, sc_sess_P sc_sess)
{
	sm_ret_T ret;
	sc_ta_P sc_ta;

	SM_REQUIRE(psc_ta != NULL);
	sc_ta = sm_zalloc(sizeof(*sc_ta));
	if (NULL == sc_ta)
		goto errnomem;

	sc_ta->scta_sess = sc_sess;
	sc_sess->scse_ta = sc_ta;
	SC_RCPTS_INIT(&sc_ta->scta_rcpts);

	/* more data? */

	sc_ta->sm_magic = SM_SC_TA_MAGIC;
	*psc_ta = sc_ta;
	return SM_SUCCESS;

 errnomem:
	ret = sm_error_temp(SM_EM_SMTPC, ENOMEM);
	sc_ta_free(sc_ta);	/* overkill right now... */
	return ret;
}

/*
**  SC_TA_FREE -- free a SMTPC transaction context
**
**	Parameters:
**		sc_ta -- SMTPC transaction context
**
**	Returns:
**		usual sm_error code
*/

sm_ret_T
sc_ta_free(sc_ta_P sc_ta)
{
	sm_ret_T ret;

	if (NULL == sc_ta)
		return SM_SUCCESS;
	SM_IS_SC_TA(sc_ta);
	ret = sc_ta_clr(sc_ta);
	sc_ta->sm_magic = SM_MAGIC_NULL;
	SM_FREE(sc_ta);
	return ret;
}

/*
**  SC_TA_CLR -- clear a SMTPC transaction context (for reuse etc)
**
**	Parameters:
**		sc_ta -- SMTPC transaction context
**
**	Returns:
**		usual sm_error code
*/

sm_ret_T
sc_ta_clr(sc_ta_P sc_ta)
{
	if (NULL == sc_ta)
		return SM_SUCCESS;
	SM_IS_SC_TA(sc_ta);

	/* free data inside transaction... */
	(void) sc_rcpts_free(sc_ta);
	(void) sc_mail_free(sc_ta);
	SM_STR_FREE(sc_ta->scta_cdb_id);
	SM_STR_FREE(sc_ta->scta_reply);
	SM_STR_FREE(sc_ta->scta_b_msg);

	/* closed in smtpc_ta() or sc_data() */
	SM_ASSERT(NULL == sc_ta->scta_cdb_fp);

	/* clean out all data */
	sm_memzero(sc_ta, sizeof(*sc_ta));

	/* initialize necessary data */
	sc_ta->sm_magic = SM_SC_TA_MAGIC;
	SC_RCPTS_INIT(&sc_ta->scta_rcpts);
	sm_hdrmodl_free(&sc_ta->scta_hdrmodhd);
	return SM_SUCCESS;
}


syntax highlighted by Code2HTML, v. 0.9.1