/*
 * Copyright (c) 2003-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: updcnts.c,v 1.3 2005/03/30 22:18:30 ca Exp $")
#include "sm/error.h"
#include "sm/assert.h"
#include "sm/qmgrdbg.h"
#include "sm/actdb-int.h"
#define ACTDB_LOG_DEFINES 1
#include "log.h"

/*
**  AQ_UPD_TA_RCPT_CNTS -- update recipient counters in a transaction
**
**	Parameters:
**		aq_ta -- AQ transaction
**		oldstatus -- old recipient status
**		newstatus -- new recipient status
**		lctx -- logging context
**
**	Returns:
**		SM_SUCCESS
**
**	Called by: q_upd_rcpt_stat(), qar_alias()
**
**	Side Effects:
**		may set AQ_TA_FL_EDB_UPD_C in aq_ta.
**		increases aqt_rcpts_tried,
**		changes aqt_rcpts_left,aqt_rcpts_perm, aqt_rcpts_temp
**	Note: for "reversibility" the counters should be saved by the caller.
**		However, that doesn't work if another task updates the counters
**		too. Hence the "change" must be saved, but even then it
**		could cause a problem if some function acts if a counter
**		reaches 0 (before it is "restored"). Therefore the only
**		way to do this reliably is to lock aq_ta until the entire
**		update "transaction" is done.
**
**	Locking: aq_ta must be locked by caller
**
**	Last code review: 2005-03-30 00:37:46
**	Last code change:
*/

sm_ret_T
aq_upd_ta_rcpt_cnts(aq_ta_P aq_ta, smtp_status_T oldstatus, smtp_status_T newstatus, sm_log_ctx_P lctx)
{
	SM_IS_AQ_TA(aq_ta);
	if (aqr_is_smtp_reply(oldstatus) && aqr_is_smtp_reply(newstatus)
	    && smtp_reply_type(oldstatus) == smtp_reply_type(newstatus))
	{
		/* No change */
		return SM_SUCCESS;
	}
	QM_LEV_DPRINTFC(QDC_UPDRCPT, 6, (QM_DEBFP, "sev=DBG, func=aq_upd_ta_rcpt_cnts, aq_ta=%p, old=%d, new=%d, tried=%u, left=%u, temp=%u, perm=%u, flags=%#x\n", aq_ta, oldstatus, newstatus, aq_ta->aqt_rcpts_tried, aq_ta->aqt_rcpts_left, aq_ta->aqt_rcpts_temp, aq_ta->aqt_rcpts_perm, aq_ta->aqt_flags));

	/* New entry? Then it has been tried now... */
	if (oldstatus == AQR_ST_NEW)
		++aq_ta->aqt_rcpts_tried;

	if (newstatus == SM_SUCCESS)
		newstatus = AQR_ST_DONE;
	if (oldstatus == SM_SUCCESS)
		oldstatus = AQR_ST_DONE;
	switch (smtp_reply_type(oldstatus))
	{
	  case SMTP_RTYPE_OK:
		/* Can't be... rcpt has been delivered before; abort?? */
		sm_log_write(lctx, ACTDB_LCAT_ACTDB, ACTDB_LMOD_ACTDB,
			SM_LOG_ERR, 1,
			"sev=ERROR, func=aq_upd_ta_rcpt_cnts, status=inconsistent_state, aq_ta=%p, old=%d, new=%d, tried=%u, left=%u, temp=%u, perm=%u, flags=%#x"
			, aq_ta, oldstatus, newstatus, aq_ta->aqt_rcpts_tried
			, aq_ta->aqt_rcpts_left, aq_ta->aqt_rcpts_temp
			, aq_ta->aqt_rcpts_perm, aq_ta->aqt_flags);
		QM_LEV_DPRINTFC(QDC_UPDRCPT, 0, (QM_DEBFP, "sev=ERROR, func=aq_upd_ta_rcpt_cnts, aq_ta=%p, old=%d, new=%d, tried=%u, left=%u, temp=%u, perm=%u, flags=%#x\n", aq_ta, oldstatus, newstatus, aq_ta->aqt_rcpts_tried, aq_ta->aqt_rcpts_left, aq_ta->aqt_rcpts_temp, aq_ta->aqt_rcpts_perm, aq_ta->aqt_flags));
		break;

	  case SMTP_RTYPE_TEMP:
		AQ_TA_SET_FLAG(aq_ta, AQ_TA_FL_EDB_UPD_C);
		SM_ASSERT(aq_ta->aqt_rcpts_temp > 0);
		--aq_ta->aqt_rcpts_temp;
		switch (smtp_reply_type(newstatus))
		{
		  case SMTP_RTYPE_OK:
			SM_ASSERT(aq_ta->aqt_rcpts_left > 0);
			--aq_ta->aqt_rcpts_left;
			break;

		  case SMTP_RTYPE_TEMP:
			/* Should have been caught above; COMPLAIN XXX */
			sm_log_write(lctx, ACTDB_LCAT_ACTDB, ACTDB_LMOD_ACTDB,
				SM_LOG_ERR, 1,
				"sev=ERROR, func=aq_upd_ta_rcpt_cnts, status=inconsistent_state, aq_ta=%p, old=%d, new=%d, tried=%u, left=%u, temp=%u, perm=%u, flags=%#x"
				, aq_ta, oldstatus, newstatus
				, aq_ta->aqt_rcpts_tried
				, aq_ta->aqt_rcpts_left, aq_ta->aqt_rcpts_temp
				, aq_ta->aqt_rcpts_perm, aq_ta->aqt_flags);
			break;

		  case SMTP_RTYPE_PERM:
			++aq_ta->aqt_rcpts_perm;
			break;

		  default:
			/* Nothing happened this time */
			break;
		}
		break;

	  case SMTP_RTYPE_PERM:
		AQ_TA_SET_FLAG(aq_ta, AQ_TA_FL_EDB_UPD_C);
		SM_ASSERT(aq_ta->aqt_rcpts_perm > 0);
		--aq_ta->aqt_rcpts_perm;
		switch (smtp_reply_type(newstatus))
		{
		  case SMTP_RTYPE_OK:
			SM_ASSERT(aq_ta->aqt_rcpts_left > 0);
			--aq_ta->aqt_rcpts_left;
			break;

		  case SMTP_RTYPE_TEMP:
			++aq_ta->aqt_rcpts_temp;
			break;

		  case SMTP_RTYPE_PERM:
			/* Should have been caught above; COMPLAIN XXX */
			sm_log_write(lctx, ACTDB_LCAT_ACTDB, ACTDB_LMOD_ACTDB,
				SM_LOG_ERR, 1,
				"sev=ERROR, func=aq_upd_ta_rcpt_cnts, status=inconsistent_state, aq_ta=%p, old=%d, new=%d, tried=%u, left=%u, temp=%u, perm=%u, flags=%#x"
				, aq_ta, oldstatus, newstatus
				, aq_ta->aqt_rcpts_tried
				, aq_ta->aqt_rcpts_left, aq_ta->aqt_rcpts_temp
				, aq_ta->aqt_rcpts_perm, aq_ta->aqt_flags);
			break;

		  default:
			/* Nothing happened this time */
			break;
		}
		break;

	  default:
		/* Nothing happened before */
		switch (smtp_reply_type(newstatus))
		{
		  case SMTP_RTYPE_OK:
			SM_ASSERT(aq_ta->aqt_rcpts_left > 0);
			AQ_TA_SET_FLAG(aq_ta, AQ_TA_FL_EDB_UPD_C);
			--aq_ta->aqt_rcpts_left;
			break;

		  case SMTP_RTYPE_TEMP:
			AQ_TA_SET_FLAG(aq_ta, AQ_TA_FL_EDB_UPD_C);
			++aq_ta->aqt_rcpts_temp;
			break;

		  case SMTP_RTYPE_PERM:
			AQ_TA_SET_FLAG(aq_ta, AQ_TA_FL_EDB_UPD_C);
			++aq_ta->aqt_rcpts_perm;
			break;

		  default:
			/*
			**  Nothing happened this time either.
			**  This case is possible since the check above is
			**  restricted to SMTP reply codes.
			*/

			/* XXX Really? */
#if 0
			AQ_TA_SET_FLAG(aq_ta, AQ_TA_FL_EDB_UPD_C);
#endif
			break;
		}
		break;
	}
	QM_LEV_DPRINTFC(QDC_UPDRCPT, 6, (QM_DEBFP, "sev=DBG, func=aq_upd_ta_rcpt_cnts, aq_ta=%p, old=%d, new=%d, tried=%u, left=%u, temp=%u, perm=%u, flags=%#x\n", aq_ta, oldstatus, newstatus, aq_ta->aqt_rcpts_tried, aq_ta->aqt_rcpts_left, aq_ta->aqt_rcpts_temp, aq_ta->aqt_rcpts_perm, aq_ta->aqt_flags));
	return SM_SUCCESS;
}


syntax highlighted by Code2HTML, v. 0.9.1