/*
 * 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: qm_ssh.c,v 1.74 2007/03/27 03:04:41 ca Exp $")
#include "sm/error.h"
#include "sm/assert.h"
#include "sm/memops.h"
#include "sm/hdrmod.h"
#include "sm/qmgr.h"
#include "sm/qmgr-int.h"
#include "qmgr.h"

/*
**  This module contains (de)allocation functions for QMGR/SMTPS related data.
*/

/*
**  QSS_MAIL_NEW -- create new QMGR/SMTPS mail entry
**
**	Parameters:
**		qss_ta -- QMGR/SMTPS transaction
**
**	Returns:
**		usual sm_error code
**
**	Last code review:
**	Last code change:
*/

sm_ret_T
qss_mail_new(qss_ta_P qss_ta)
{
	qss_mail_P qss_mail;

	SM_IS_QS_TA(qss_ta);
	qss_mail = (qss_mail_P) sm_rpool_zalloc(qss_ta->qssta_rpool,
						sizeof(*qss_mail));
	if (NULL == qss_mail)
		return sm_error_temp(SM_EM_Q_Q2SS, ENOMEM);
	qss_ta->qssta_mail = qss_mail;
	return SM_SUCCESS;
}

/*
**  QSS_MAIL_FREE -- free QMGR/SMTPS mail entry
**
**	Parameters:
**		qss_ta -- QMGR/SMTPS transaction
**
**	Returns:
**		SM_SUCCESS
**
**	Last code review: 2005-04-01 18:09:25
**	Last code change:
*/

sm_ret_T
qss_mail_free(qss_ta_P qss_ta)
{
	SM_IS_QS_TA(qss_ta);
	if (NULL == qss_ta->qssta_mail)
		return SM_SUCCESS;
	SM_STR_FREE(qss_ta->qssta_mail->qsm_pa);
	SM_RPOOL_FREE(qss_ta->qssta_rpool, qss_ta->qssta_mail);
	return SM_SUCCESS;
}

/*
**  QSS_RCPTS_NEW -- create new QMGR/SMTPS recipient and add it to rcpt list
**
**	Parameters:
**		qss_ta -- QMGR/SMTPS transaction
**		prcpt_pa -- (pointer to) recipient printable address
**		rcpt_idx -- recipient index
**		prcpt -- (pointer to) recipient (output)
**
**	Returns:
**		usual sm_error code
**
**	Last code review:
**	Last code change:
*/

sm_ret_T
qss_rcpts_new(qss_ta_P qss_ta, sm_str_P *prcpt_pa, rcpt_idx_T rcpt_idx, qss_rcpt_P *prcpt)
{
	qss_rcpt_P qss_rcpt;

	SM_IS_QS_TA(qss_ta);
	SM_REQUIRE(prcpt_pa != NULL && *prcpt_pa != NULL);
	SM_REQUIRE(prcpt != NULL);
	if (qss_ta->qssta_rcpts_tot >= SMTP_RCPTIDX_MAX)
		return sm_error_temp(SM_EM_Q_Q2SS, SM_E_FULL);
	qss_rcpt = (qss_rcpt_P) sm_rpool_zalloc(qss_ta->qssta_rpool,
						sizeof(*qss_rcpt));
	if (NULL == qss_rcpt)
		goto error;
	qss_rcpt->qsr_pa = *prcpt_pa;
	*prcpt_pa = NULL;
	qss_rcpt->qsr_idx = rcpt_idx;
	sm_snprintf(qss_rcpt->qsr_id, sizeof(qss_rcpt->qsr_id),
		SMTP_RCPTID_FORMAT, qss_ta->qssta_id, rcpt_idx);
	QSRCPTS_INSERT_TAIL(&qss_ta->qssta_rcpts, qss_rcpt);
	++qss_ta->qssta_rcpts_tot;
	*prcpt = qss_rcpt;
	return SM_SUCCESS;

  error:
	SM_RPOOL_FREE(qss_ta->qssta_rpool, qss_rcpt);
	return sm_error_temp(SM_EM_Q_Q2SS, ENOMEM);
}

/*
**  QSS_RCPT_FREE -- free a single QMGR/SMTPS recipient address
**
**	Parameters:
**		qss_ta -- QMGR/SMTPS transaction
**		qss_rcpt -- QMGR/SMTPS recipient
**		rmflags -- various flags (qmgr/qmgr.h: QSS_)
**
**	Returns:
**		SM_SUCCESS
**
**	Last code review: 2005-04-01 18:12:03
**	Last code change: 2005-04-01 18:10:42
*/

sm_ret_T
qss_rcpt_free(qss_ta_P qss_ta, qss_rcpt_P qss_rcpt, uint rmflags)
{
	if (NULL == qss_rcpt)
		return SM_SUCCESS;
	SM_IS_QS_TA(qss_ta);
	sm_str_free(qss_rcpt->qsr_pa);
	QSRCPTS_REMOVE(&qss_ta->qssta_rcpts, qss_rcpt);
	if (SM_IS_FLAG(rmflags, QSS_RMFIQDB)) {
		qmgr_ctx_P qmgr_ctx;

		qmgr_ctx = qss_ta->qssta_ssctx->qss_qmgr_ctx;
		iqdb_rcpt_rm(qmgr_ctx->qmgr_iqdb,
				qss_rcpt->qsr_id, SMTP_RCPTID_SIZE,
				SM_IS_FLAG(rmflags, QSS_IQDB_NOLOCK)
					? THR_NO_LOCK : THR_LOCK_UNLOCK);
		if (QMGR_IS_RFLAG_I(qmgr_ctx, QMGR_RFL_IQD)) {
			(void) qm_resource(qmgr_ctx, QMGR_UN_THROTTLE,
					iqdb_usage(qmgr_ctx->qmgr_iqdb),
					QMGR_RFL_IQD);
		}
	}
	sm_rpool_free(qss_ta->qssta_rpool, qss_rcpt);
	if (SM_IS_FLAG(rmflags, QSS_DECR_RCPTS_TOT)) {
		SM_ASSERT(qss_ta->qssta_rcpts_tot > 0);
		--qss_ta->qssta_rcpts_tot;
	}
	return SM_SUCCESS;
}

/*
**  QSS_RCPTS_FREE -- free an entire QMGR/SMTPS recipient list
**
**	Parameters:
**		qss_ta -- QMGR/SMTPS transaction
**		rmflags -- various flags (for qss_rcpt_free())
**		prcpt_idx_high -- (pointer to) highest rcpt idx (output)
**
**	Returns:
**		SM_SUCCESS (or SM_NOTDONE)
**
**	Last code review: 2005-04-01 18:12:30
**	Last code change: 2006-08-05 04:02:31
*/

sm_ret_T
qss_rcpts_free(qss_ta_P qss_ta, uint rmflags, rcpt_idx_T *prcpt_idx_high)
{
	qss_rcpts_P qss_rcpts;
	qss_rcpt_P qss_rcpt, qss_nxt_rcpt;
	rcpt_idx_T rcpt_idx_high;

	SM_IS_QS_TA(qss_ta);
	qss_rcpts = &qss_ta->qssta_rcpts;
	if (QSRCPTS_EMPTY(qss_rcpts))
		return SM_NOTDONE;
	rcpt_idx_high = 0;
	for (qss_rcpt = QSRCPTS_FIRST(qss_rcpts);
	     qss_rcpt != QSRCPTS_END(qss_rcpts);
	     qss_rcpt = qss_nxt_rcpt)
	{
		qss_nxt_rcpt = QSRCPTS_NEXT(qss_rcpt);
		if (rcpt_idx_high < qss_rcpt->qsr_idx)
			rcpt_idx_high = qss_rcpt->qsr_idx;
		qss_rcpt_free(qss_ta, qss_rcpt, rmflags);
	}
	QSRCPTS_INIT(&qss_ta->qssta_rcpts);
	if (prcpt_idx_high != NULL)
		*prcpt_idx_high = rcpt_idx_high;
	return SM_SUCCESS;
}

/*
**  QSS_TA_CLR -- clear out a QMGR/SMTPS transaction
**
**	Parameters:
**		qss_ta -- QMGR/SMTPS transaction
**		rmflags -- various flags (for qss_rcpt_free())
**
**	Returns:
**		SM_SUCCESS
**
**	Last code review: 2005-04-01 18:12:44
**	Last code change:
*/

static sm_ret_T
qss_ta_clr(qss_ta_P qss_ta, uint rmflags)
{
	SM_IS_QS_TA(qss_ta);

	/*
	**  If all data inside qss_ta is allocated via rpool,
	**  we could simply free the rpool.
	**  Cleanup functions (resources) could be registered with
	**  the rpool... future enhancement??
	*/

	qss_mail_free(qss_ta);
	qss_rcpts_free(qss_ta, rmflags, NULL);
	if (qss_ta->qssta_rpool != NULL) {
		sm_rpool_delete(qss_ta->qssta_rpool);
		qss_ta->qssta_rpool = NULL;
	}
	SM_CSTR_FREE(qss_ta->qssta_cdb_id);
	sm_hdrmodl_free(&qss_ta->qssta_hdrmodhd);

	/* clear transaction */
	sm_memzero(qss_ta, sizeof(*qss_ta));
	QSRCPTS_INIT(&qss_ta->qssta_rcpts);
	return SM_SUCCESS;
}

#if 0
///*
//**  QSS_TA_ABORT -- abort a QMGR/SMTPS transaction
//**
//**	Parameters:
//**		qss_ta -- QMGR/SMTPS transaction
//**
//**	Returns:
//**		usual sm_error code
//**
//**	Comments:
//**		this is currently not really used.
//*/
//
//sm_ret_T
//qss_ta_abort(qss_ta_P qss_ta)
//{
//	SM_IS_QS_TA(qss_ta);
//
//#if 0
//	/* check existing transaction */
//	if (qss_ta->qssta_state != STA_NONE)
//	{
//		/* XXX ... */
//		/* abort all performed actions */
//
//	}
//#endif
//
//	/* XXX should this be dependent on the state? */
//	qss_ta_clr(qss_ta, QSS_RMFIQDB);
//	return SM_SUCCESS;
//}
#endif /* 0 */

/*
**  QSS_TA_FREE -- free a QMGR/SMTPS transaction
**
**	Parameters:
**		qss_ta -- QMGR/SMTPS transaction
**		unlock -- transaction is locked: unlock it before freeing
**		flag -- from where is qss_ta removed?
**		rmflags -- various flags (qmgr/qmgr.h: QSS_)
**
**	Returns:
**		SM_SUCCESS except for (un)lock errors
**
**	Last code review: 2005-04-01 18:17:03
**	Last code change: 2005-04-01 18:06:05
*/

sm_ret_T
qss_ta_free(qss_ta_P qss_ta, bool unlock, uint flag, uint rmflags)
{
	int r;
	uint flags;

	if (NULL == qss_ta) {
		if (unlock)
			QM_LEV_DPRINTFC(QDC_Q2S, 0, (QM_DEBFP,
				"sev=ERROR, func=qss_ta_free, status=qss_ta_is_NULL_but_unlock_is_set\n"));
		SM_ASSERT(!unlock);
		return SM_SUCCESS;
	}
	SM_IS_QS_TA(qss_ta);

	/*
	**  Get a copy of flags before unlocking qss_ta.
	**  Alternatively unlock qss_ta after the check down below.
	*/

	flags = qss_ta->qssta_flags;

	QM_LEV_DPRINTFC(QDC_Q2S, 1, (QM_DEBFP, "sev=DBG, func=qss_ta_free, qss_ta=%p, flags=%#x, flag=%#x, unlock=%d, free=%d\n", qss_ta, flags, flag, unlock, QSS_TA_OK_FREE(flags)
));
	if (unlock) {
		r = pthread_mutex_unlock(&qss_ta->qssta_mutex);
		if (r != 0) {
			QM_LEV_DPRINTFC(QDC_Q2S, 0, (QM_DEBFP, "sev=ERROR, func=qss_ta_free, unlock=%d\n", r));
			SM_ASSERT(r == 0);
			return sm_error_perm(SM_EM_Q_Q2SS, r);
		}
	}

	/* more conditions when to remove qss_ta? */
	if (SM_IS_FLAG(flag, QSS_TA_FREE_ALWAYS) || QSS_TA_OK_FREE(flags)) {
		(void) pthread_mutex_destroy(&qss_ta->qssta_mutex);
		(void) qss_ta_clr(qss_ta, rmflags);
#if QS_TA_CHECK
		qss_ta->sm_magic = SM_MAGIC_NULL;
#endif
		sm_free_size(qss_ta, sizeof(*qss_ta));
	}
	return SM_SUCCESS;
}

/*
**  QSS_TA_NEW -- create new QMGR/SMTPS transaction
**	Note: qssta_cdb_id is not allocated here, it's done when an RCB is read
**
**	Parameters:
**		pqss_ta -- QMGR/SMTPS transaction (output)
**		qss_ctx -- QMGR/SMTPS context
**		rpool -- rpool: only for data in qss_ta, not for qss_ta itself
**
**	Returns:
**		usual sm_error code
**
**	Last code review:
**	Last code change:
*/

sm_ret_T
qss_ta_new(qss_ta_P *pqss_ta, qss_ctx_P qss_ctx, sm_rpool_P rpool)
{
	int r;
	sm_ret_T ret;
	qss_ta_P qss_ta;

	SM_REQUIRE(pqss_ta != NULL);
	SM_IS_QSS_CTX(qss_ctx);
	qss_ta = (qss_ta_P) sm_zalloc(sizeof(*qss_ta));
	if (NULL == qss_ta)
		return sm_error_temp(SM_EM_Q_Q2SS, ENOMEM);
	r = pthread_mutex_init(&qss_ta->qssta_mutex, SM_PTHREAD_MUTEXATTR);
	if (r != 0) {
		ret = sm_error_perm(SM_EM_Q_Q2SS, r);
		goto error;
	}

	qss_ta->qssta_rpool = rpool;
	qss_ta->qssta_ssctx = qss_ctx;
	QSRCPTS_INIT(&qss_ta->qssta_rcpts);
#if 0
	qss_ta->qssta_sess = sess;
#endif
#if QS_TA_CHECK
	qss_ta->sm_magic = SM_QSS_TA_MAGIC;
#endif
	*pqss_ta = qss_ta;
	return SM_SUCCESS;

  error:
	if (*pqss_ta != NULL)
		sm_free_size(*pqss_ta, sizeof(**pqss_ta));
	*pqss_ta = NULL;
	return ret;
}

/*
**  QSS_SESS_FREE -- free QMGR/SMTPS session
**
**	Parameters:
**		qss_sess -- QMGR/SMTPS session
**
**	Returns:
**		usual sm_error code
**
**	Last code review: 2005-04-10 05:28:30
**	Last code change: 2005-04-10 05:28:26
*/

sm_ret_T
qss_sess_free(qss_sess_P qss_sess)
{
	if (NULL == qss_sess)
		return SM_SUCCESS;
	SM_IS_QS_SE(qss_sess);

#if 0
	/* should this free the ta inside? we don't know the current ta! */
	qss_ta = qss_sess->sss_ta;
	if (qss_ta != NULL)
		qss_ta_abort(qss_ta);
#endif

#if QS_SE_CHECK
	qss_sess->sm_magic = SM_MAGIC_NULL;
#endif
	sm_free_size(qss_sess, sizeof(*qss_sess));
	return SM_SUCCESS;
}

/*
**  QSS_SESS_NEW -- create new QMGR/SMTPS session
**
**	Parameters:
**		pqss_sess -- (pointer to) QMGR/SMTPS session (output)
**		rpool -- rpool: for data in qss_sess, not for qss_sess itself
**
**	Returns:
**		usual sm_error code
**
**	Last code review:
**	Last code change:
*/

/* pass it an rpool or let it create one?? */
sm_ret_T
qss_sess_new(qss_sess_P *pqss_sess, sm_rpool_P rpool)
{
	SM_REQUIRE(pqss_sess != NULL);
	*pqss_sess = (qss_sess_P) sm_zalloc(sizeof(**pqss_sess));
	if (*pqss_sess == NULL)
		goto error;
	(*pqss_sess)->qsess_rpool = rpool;
#if QS_SE_CHECK
	(*pqss_sess)->sm_magic = SM_QSS_SE_MAGIC;
#endif
	return SM_SUCCESS;

  error:
	/* complain?? */
	/* qss_sess_free(*pqss_sess); not needed now */
	return sm_error_temp(SM_EM_Q_Q2SS, ENOMEM);
}


syntax highlighted by Code2HTML, v. 0.9.1