/*
 * Copyright (c) 2002-2005 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: qss_ctaid.c,v 1.81 2006/11/27 03:24:52 ca Exp $")
#include "sm/error.h"
#include "sm/assert.h"
#include "sm/io.h"
#include "sm/rcb.h"
#include "sm/qmgr.h"
#include "sm/qmgr-int.h"
#include "sm/reccom.h"
#include "qmgr.h"
#include "log.h"

/*
**  QM_SS_CTAID -- close SMTPS transaction: write IBDB record
**
**	Parameters:
**		qss_ctx -- QMGR/SMTPS context
**		qss_ta -- transaction
**
**	Returns:
**		<0: usual sm_error code: only for severe problems!
**		>=0: acceptance/rejection code
**
**	Side Effects:
**		writes transaction to IBDB
**		adds transaction to AQ; does not handle error properly:
**		see comments below.
**
**	Last code review: 2003-10-22 20:18:56, see comments below!
*/

#define SM_IS_IBDB_DISK_FULL(ret)		\
	(sm_error_value(ret) == ENOSPC		\
	 || sm_error_value(ret) == EFBIG	\
	 || sm_error_value(ret) == EDQUOT)

sm_ret_T
qm_ss_ctaid(qss_ctx_P qss_ctx, qss_ta_P qss_ta)
{
	sm_ret_T ret;
	ibdb_ta_T ibdb_ta;
	const char *func;

	SM_IS_QSS_CTX(qss_ctx);
	SM_IS_QS_TA(qss_ta);
	ret = SM_SUCCESS;

	/* Do some checks here: is it ok to accept this mail? */

	/*
	**  assign data to ibdb_ta just for ibdb_ta_status, ibdb_ta is not
	**  used at all afterwards.
	*/

	ibdb_ta.ibt_ta_id = qss_ta->qssta_id;
	ibdb_ta.ibt_mail_pa = qss_ta->qssta_mail->qsm_pa;
	ibdb_ta.ibt_cdb_id = qss_ta->qssta_cdb_id;
	ibdb_ta.ibt_nrcpts = qss_ta->qssta_rcpts_tot;
	ret = ibdb_ta_status(qss_ctx->qss_qmgr_ctx->qmgr_ibdb, &ibdb_ta,
			IBDB_TA_NEW, IBDB_FL_NONE, 0, THR_LOCK_UNLOCK);
	if (sm_is_err(ret)) {
		func = "ibdb_ta_status";
		goto err_write;
	}

	ret = ibdb_hdrmodl_wr(qss_ctx->qss_qmgr_ctx->qmgr_ibdb,
			qss_ta->qssta_hdrmodhd, qss_ta->qssta_id,
			IBDB_FL_NONE, THR_LOCK_UNLOCK);
	if (sm_is_err(ret)) {
		func = "ibdb_hdrmodl_wr";
		goto err_write;
	}

	QSS_TA_SET_FLAG(qss_ta, QSS_TA_FL_IBDB);

	/* copy transaction into AQ immediately */
	ret = aq_env_add_iqdb(qss_ctx->qss_qmgr_ctx->qmgr_aq, qss_ta,
			qss_ctx->qss_qmgr_ctx);

	/*
	**  XXX Error handling is far from optimal!
	**  Current solution: discard TA (see below).
	**  Alternatives:
	**  If the data couldn't be transferred to AQ because it is full
	**  - try DEFEDB
	**  - throttle SMTPS and try to add the TA later on to AQ.
	**    (Note: the transaction is still in IQDB!)
	**  If none of that works: reject the mail (we can't deal with it).
	*/

	if (sm_is_err(ret)) {
		QM_LEV_DPRINTFC(QDC_Q2S, 0, (QM_DEBFP, "sev=ERROR, func=qm_ss_ctaid, aq_env_add_iqdb=%r\n", ret));
		sm_log_write(qss_ctx->qss_qmgr_ctx->qmgr_lctx,
			QM_LCAT_SMTPS, QM_LMOD_FROM_SMTPS,
			SM_LOG_ERR, 0,
			"sev=ERROR, func=qm_ss_ctaid, aq_env_add_iqdb=%m", ret);

		if (sm_error_value(ret) == SM_E_FULL || sm_error_value(ret) == ENOMEM)
			(void) qss_control(qss_ctx, QMGR_THROTTLE, 100,
					QMGR_RFL_AQ_I, THR_LOCK_UNLOCK);

		/*
		**  Simply discard the TA for now, see above.
		**  This will be done in the caller because we return
		**  a temporary error, except for ibdb changes.
		*/

		ret = ibdb_ta_status(qss_ctx->qss_qmgr_ctx->qmgr_ibdb,
				&ibdb_ta, IBDB_TA_CANCEL, IBDB_FL_NONE,
				0, THR_LOCK_UNLOCK);
		if (sm_is_err(ret)) {
			sm_log_write(qss_ctx->qss_qmgr_ctx->qmgr_lctx,
				QM_LCAT_SMTPS, QM_LMOD_FROM_SMTPS,
				SM_LOG_ERR, 0,
				"sev=ERROR, func=qm_ss_ctaid, ibdb_ta_status=%m",
				ret);
		}

		ret = SMTP_R_TEMP;
		goto error;
	}
	else {	/* currently not really required because of the goto above */
		/*
		**  No error; why don't we remove the entry from IQDB now?
		**  See qda_upd_iqdb(), currently it uses qss_ta to update
		**  IBDB; maybe this can be changed to use data from AQ?
		*/

		(void) qss_control(qss_ctx, QMGR_THROTTLE, ret, QMGR_RFL_AQ_I,
				THR_LOCK_UNLOCK);
		QSS_TA_SET_FLAG(qss_ta, QSS_TA_FL_AQ);
	}

	return SMTP_R_OK;

  err_write:
	/*
	**  Disk full? Need to clean up IBDB or otherwise "wait" for free space.
	*/

	if (SM_IS_IBDB_DISK_FULL(ret)) {
		(void) qss_control(qss_ctx, QMGR_THROTTLE, 100, QMGR_RFL_IBD_I,
				THR_LOCK_UNLOCK);
		sm_log_write(qss_ctx->qss_qmgr_ctx->qmgr_lctx,
			QM_LCAT_SMTPS,
			QM_LMOD_FROM_SMTPS,
			SM_LOG_ALERT, 0,
			"sev=ALERT, func=qm_ss_ctaid, %s=%m, status=disk_space_exceeded"
			, func, ret);
	}
	else if (sm_error_value(ret) == ENOMEM) {
		(void) qss_control(qss_ctx, QMGR_THROTTLE, 100, QMGR_RFL_MEM_I,
				THR_LOCK_UNLOCK);
		sm_log_write(qss_ctx->qss_qmgr_ctx->qmgr_lctx,
			QM_LCAT_SMTPS,
			QM_LMOD_FROM_SMTPS,
			SM_LOG_ALERT, 0,
			"sev=ALERT, func=qm_ss_ctaid, %s=%m, status=ENOMEM"
			, func, ret);
	}
	else {
		sm_log_write(qss_ctx->qss_qmgr_ctx->qmgr_lctx,
			QM_LCAT_SMTPS,
			QM_LMOD_FROM_SMTPS,
			SM_LOG_ERR, 0,
			"sev=ERROR, func=qm_ss_ctaid, %s=%m"
			, func, ret);
	}

	/* change the error code to a temporary rejection XXX */
	ret = SMTP_R_TEMP;

  error:
	sm_log_write(qss_ctx->qss_qmgr_ctx->qmgr_lctx,
		QM_LCAT_SMTPS,
		QM_LMOD_FROM_SMTPS,
		SM_LOG_ERR, 1,
		"sev=ERROR, func=qm_ss_ctaid, ret=%m", ret);
	return ret;
}

/*
**  QM_2SS_CTAID -- Compose reply to "close TA", put data into RCB entry
**
**	Parameters:
**		qss_ctx -- QMGR/SMTPS context
**		qss_ta -- transaction
**		rcbe -- RCB entry (must be open for encoding)
**		status -- status code
**
**	Returns:
**		usual sm_error code
**
**	Called by:
**		q_ibdb_commit() (group commit)
**		qm_fr_ss_react() (if something goes wrong when CTAID is received
**			from SMTPS)
**
**	Last code review: 2003-10-23 00:57:57
*/

sm_ret_T
qm_2ss_ctaid(qss_ctx_P qss_ctx, qss_ta_P qss_ta, sm_rcbe_P rcbe, int status)
{
	sm_rcb_P rcb;
	sm_ret_T ret;

	SM_IS_QSS_CTX(qss_ctx);
	SM_IS_QS_TA(qss_ta);
	rcb = &rcbe->rcbe_rcb;
	ret = sm_rcb_putv(rcb, RCB_PUTV_FIRST,
		SM_RCBV_INT, RT_PROT_VER, PROT_VER_RT,
		SM_RCBV_INT, RT_Q2S_ID, qss_ctx->qss_id,
		SM_RCBV_BUF, RT_Q2S_TAID, qss_ta->qssta_id, SMTP_STID_SIZE,
		SM_RCBV_INT, RT_Q2S_STAT, (uint32_t) status,
		SM_RCBV_END);

	/* can be simplified [return ret;] if debug output below is removed */
	if (sm_is_err(ret))
		goto error;

	QM_LEV_DPRINTTC(QDC_Q2S, 1, (QM_DEBFP, "func=qm_2ss_ctaid, cdb-id=%C, stat=%d\n", qss_ta->qssta_cdb_id, status), qss_ctx->qss_qmgr_ctx->qmgr_ev_ctx->evthr_c_time);
	return ret;

  error:
	return ret;
}


syntax highlighted by Code2HTML, v. 0.9.1