/*
 * 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: qm_to_ar.c,v 1.69 2007/06/04 04:58:39 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 "sm/smar.h"
#include "sm/da.h"
#include "qmgr.h"

/*
**  QM2AR -- Send recipient data to AR, i.e., put it into RCB
**
**	Parameters:
**		qmgr_ctx -- QMGR context
**		aq_rcpt -- aq_rcpt
**		rcbe -- RCB entry
**
**	Returns:
**		usual sm_error code (from sm_rcb_putv())
**
**	Side Effects: fills in rcb
**		if error: rcb might have partial content
**
**	Locking: aq_ctx (aq_rcpt) must be locked by caller (to read aq_rcpt)
**
**	Last code review: 2003-10-17 17:22:58
**	Last code change:
*/

static sm_ret_T
qm2ar(qmgr_ctx_P qmgr_ctx, aq_rcpt_P aq_rcpt, sm_rcbe_P rcbe)
{
	sm_rcb_P rcb;
	sm_ret_T ret;
	rcpt_id_T rcpt_id;

	rcb = &rcbe->rcbe_rcb;
	sm_snprintf(rcpt_id, sizeof(rcpt_id), SMTP_RCPTID_FORMAT,
		aq_rcpt->aqr_ss_ta_id, aq_rcpt->aqr_idx);

	/* send rcpt_idx and ta_id separately?? */
	ret = sm_rcb_putv(rcb, RCB_PUTV_FIRST,
		SM_RCBV_INT, RT_PROT_VER, PROT_VER_RT,
		SM_RCBV_BUF, RT_Q2R_RCPT_ID, rcpt_id, SMTP_RCPTID_SIZE,
		SM_RCBV_STR, RT_Q2R_RCPT_PA, aq_rcpt->aqr_pa,
		SM_RCBV_INT, RT_Q2R_FLAGS,
			(AQR_IS_FLAG(aq_rcpt, AQR_FL_ALIAS)
			? SMARRQ_FL_NONE : SMARRQ_FL_ALIAS|SMARRQ_FL_OWNER|SMARRQ_FL_VERP)|
#if MTA_USE_TLS
			(QCNF_IS_FLAG(qmgr_ctx, QCNF_FL_SC_LKP_RCPT_CONF)
			? SMARRQ_FL_CONF : SMARRQ_FL_NONE)|
#endif
			SMARRQ_FL_CHK_LU,
#if MTA_USE_TLS
		SM_RCBV_INT,
			(qmgr_ctx->qmgr_cnf.q_cnf_sc_lfl != SMMAP_LFL_NONE)
			? RT_R2Q_RCPT_CNF_LKP_FL : RT_NOSEND,
			qmgr_ctx->qmgr_cnf.q_cnf_sc_lfl,
#endif
		SM_RCBV_INT, RT_Q2R_TIMEOUT,
			(aq_rcpt->aqr_tries == 0 ||
			 aq_rcpt->aqr_err_st != DA_AR_ERR ||
			 aq_rcpt->aqr_status != SMTP_AR_TEMP)
			? 0
			: (aq_rcpt->aqr_tries < 5)
				? (qmgr_ctx->qmgr_cnf.q_cnf_tmo_ar / 6 - aq_rcpt->aqr_tries)
				: (qmgr_ctx->qmgr_cnf.q_cnf_tmo_ar / 2)
		, SM_RCBV_END);
	if (sm_is_err(ret)) goto error;
	return ret;

  error:
	/* XXX leave rcb in a consistent state? */
	QM_LEV_DPRINTFC(QDC_Q2A, 1, (QM_DEBFP, "sev=ERROR, func=qr2ar, ret=%r, rcpt_id=%s\n", ret, rcpt_id));
	return ret;
}

/*
**  QMGR_RCPT2AR -- send a recipient to SMAR; append to wait queue
**
**	Parameters:
**		qmgr_ctx -- QMGR context
**		aq_rcpt -- aq_rcpt
**		locktype -- kind of locking
**
**	Returns:
**		usual sm_error code; SM_E_NO_AR, ENOMEM, SM_E_RANGE,
**			SM_E_OVFLW_NS, etc
**
**	Side Effects: always add rcpt to wait queue
**		if ok: increase aq_ta->aqt_rcpts_ar
**
**	Locking:
**		aq_ctx (aq_rcpt/aq_ta) can be locked here (locktype),
**		otherwise it must be locked by caller.
**			
**
**	Note: Caller has to enable WR for qar_task, e.g.,
**		evthr_en_wr(qmgr_ctx->qmgr_ar_tsk)
**
**	Last code review:
**	Last code change: 2006-03-30 02:44:09
*/

sm_ret_T
qmgr_rcpt2ar(qmgr_ctx_P qmgr_ctx, aq_rcpt_P aq_rcpt, thr_lock_T locktype)
{
	sm_ret_T ret;
	int r;
	sm_evthr_task_P qar_tsk;
	qar_ctx_P qar_ctx;
	sm_rcbe_P rcbe;
	aq_ta_P aq_ta;
	aq_ctx_P aq_ctx;
	time_T exp;

	SM_IS_QMGR_CTX(qmgr_ctx);
	SM_IS_AQ_RCPT(aq_rcpt);
	qar_ctx = qmgr_ctx->qmgr_ar_ctx;
	SM_IS_QAR_CTX(qar_ctx);
	qar_tsk = qmgr_ctx->qmgr_ar_tsk;
	aq_ctx = qmgr_ctx->qmgr_aq;
	SM_IS_AQ(aq_ctx);
	rcbe = NULL;

	if (thr_lock_it(locktype)) {
		r = pthread_mutex_lock(&aq_ctx->aq_mutex);
		SM_LOCK_OK(r);
		if (r != 0)
			return sm_error_perm(SM_EM_AQ, r);
	}

	/* always add recipient to wait queue for cleanup to work */
	(void) aq_waitq_add(qmgr_ctx->qmgr_aq, aq_rcpt, 0, AQWQ_AR, false);
	exp = aq_waitq_first_tmo(qmgr_ctx->qmgr_aq, AQWQ_AR, false);
	if (exp > 0)
		(void) qmgr_set_aq_cleanup(qmgr_ctx->qmgr_cleanup_ctx, exp, true);
	else
		QM_LEV_DPRINTFC(QDC_Q2A, 3, (QM_DEBFP, "func=qmgr_rcpt2ar, exp=%7ld\n", (long) exp));

	/* there might be no task... */
	if (NULL == qar_tsk) {
		/*
		**  XXX Need to ask MCP to start SMAR?
		**  Not really, MCP notices when a component fails and
		**  starts it itself (unless it fails too often).
		**
		**  XXX Need to (un)throttle SMTPS, probably somewhere in
		**  qm_fr_ss.c; use qss_control().
		*/

		QM_LEV_DPRINTTC(QDC_Q2A, 3, (QM_DEBFP, "func=qmgr_rcpt2ar, qar_tsk=NULL\n"),
			qmgr_ctx->qmgr_ev_ctx->evthr_c_time);
		ret = sm_error_temp(SM_EM_Q_Q2AR, SM_E_NO_AR);
		QMGR_SET_SFLAG(qmgr_ctx, QMGR_SFL_AR);
		goto error;
	}
	if (QMGR_IS_SFLAG(qmgr_ctx, QMGR_SFL_AR))
		QMGR_CLR_SFLAG(qmgr_ctx, QMGR_SFL_AR);
	aq_ta = aq_rcpt->aqr_ss_ta;
	SM_IS_AQ_TA(aq_ta);

	ret = sm_rcbe_new_enc(&rcbe, -1, 0);
	if (sm_is_err(ret)) goto error;
	ret = qm2ar(qmgr_ctx, aq_rcpt, rcbe);
	if (sm_is_err(ret)) goto error;
	ret = sm_rcbcom_endrep(&qar_ctx->qar_com, qar_tsk, true, &rcbe);
	if (sm_is_err(ret)) goto error;
	AQR_SET_FLAG(aq_rcpt, AQR_FL_SENT2AR);
	if (!AQR_IS_FLAG(aq_rcpt, AQR_FL_IS_BNC|AQR_FL_IS_DBNC))
		++aq_ta->aqt_rcpts_ar;
	QM_LEV_DPRINTTC(QDC_Q2A, 3, (QM_DEBFP, "func=qmgr_rcpt2ar, status=ok, aqt_rcpts_ar=%u, rcpt_id=%s, rcpt_pa=%S\n"
		, aq_ta->aqt_rcpts_ar, aq_rcpt->aqr_ss_ta_id, aq_rcpt->aqr_pa), qmgr_ctx->qmgr_ev_ctx->evthr_c_time);
	if (thr_unl_no_err(locktype)) {
		r = pthread_mutex_unlock(&aq_ctx->aq_mutex);
		SM_ASSERT(r == 0);
		if (r != 0 && sm_is_success(ret))
			ret = sm_error_perm(SM_EM_AQ, r);
	}
	return SM_SUCCESS;

  error:
	if (thr_unl_if_err(locktype)) {
		r = pthread_mutex_unlock(&aq_ctx->aq_mutex);
		SM_ASSERT(r == 0);
		if (r != 0 && sm_is_success(ret))
			ret = sm_error_perm(SM_EM_AQ, r);
	}
	if (rcbe != NULL)
		sm_rcbe_free(rcbe);
	QM_LEV_DPRINTFC(QDC_Q2A, 1, (QM_DEBFP, "sev=ERROR, func=qmgr_rcpt2ar, ret=%r\n", ret));
	return ret;
}

/*
**  QMGR2AR -- QMGR - AR interface
**
**	Parameters:
**		tsk -- evthr task
**
**	Returns:
**		usual sm_error code
*/

sm_ret_T
qm_to_ar(sm_evthr_task_P tsk)
{
	qar_ctx_P qar_ctx;

	SM_IS_EVTHR_TSK(tsk);
	qar_ctx = (qar_ctx_P) tsk->evthr_t_actx;
	SM_IS_QAR_CTX(qar_ctx);
	QM_LEV_DPRINTFC(QDC_Q2A, 6, (QM_DEBFP, "func=qm_to_ar, tsk=%p\n", tsk));
	return sm_rcbcom2mod(tsk, &qar_ctx->qar_com);
}


syntax highlighted by Code2HTML, v. 0.9.1