/*
 * Copyright (c) 2004, 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: pmilter_smtps.c,v 1.12 2006/10/05 04:27:38 ca Exp $")
#include "sm/error.h"
#include "sm/assert.h"
#include "sm/io.h"
#include "sm/rcb.h"
#include "pmilter.h"

#if MTA_USE_PMILTER
/*
**  SM_PMILT2SMTPS -- PMILTER - SMTPS interface
**	Called if socket is writable.
**
**	Parameters:
**		tsk -- evthr task
**
**	Returns:
**		usual sm_error code
**
**	Last code review:
*/

static sm_ret_T
sm_pmilt2smtps(sm_evthr_task_P tsk)
{
	sm_ret_T ret;
	pmss_ctx_P pmss_ctx;

	SM_IS_EVTHR_TSK(tsk);
	pmss_ctx = (pmss_ctx_P) tsk->evthr_t_actx;
	SM_IS_PMSS_CTX(pmss_ctx);
	ret = sm_rcbcom2mod(tsk, &pmss_ctx->pmss_com);
	return ret;
}

/*
**  SM_PMILT_SMTPS -- PMILTER - SMTPS interface
**	This runs as a task.
**
**	Parameters:
**		tsk -- evthr task
**
**	Returns:
**		usual sm_error code
*/

static sm_ret_T
sm_pmilt_smtps(sm_evthr_task_P tsk)
{
	sm_ret_T ret;

	SM_IS_EVTHR_TSK(tsk);
	PM_LEV_DPRINTF(3, (PM_DEBFP, "sev=DBG, func=sm_pmilt_smtps, fd=%d, ev_rq=%x, ev_occ=%x\n", tsk->evthr_t_fd, evthr_rqevents(tsk), tsk->evthr_t_evocc));
	if (is_valid_fd(tsk->evthr_t_fd))
	{
		ret = EVTHR_WAITQ;
		if (evthr_got_slp(tsk))
		{
#if 0
			sm_ret_T res;

			/* don't set ret here, it's the wrong return value */
			res = sm_smtps_wakeup(tsk);
			if (sm_is_err(res))
			{
				PM_LEV_DPRINTF(3, (PM_DEBFP, "sev=DBG, func=sm_pmilt_smtps, sm_smtps_wakeup=%x\n", res));
			}
#endif /* 0 */
		}
		if (evthr_got_wr(tsk))
		{
			ret = sm_pmilt2smtps(tsk);
			if (sm_is_err(ret))
			{
				sm_io_fprintf(smioerr,
					"sev=DBG, func=sm_pmilt_smtps, sm_pmilt2smtps=%x\n", ret);
				/* really return here? */
				return ret;
			}
		}
		if (evthr_got_rd(tsk))
		{
			/*
			**  This routine could set EVTHR_EV_WR
			**  to cause data to be written back to the SMTPS.
			**  However, most of the routines may work
			**  asynchronously and hence they should return the
			**  correct value themselves.
			*/

			ret = sm_smtps2pmilt(tsk);
			if (sm_is_err(ret))
			{
				sm_io_fprintf(smioerr,
					"sev=DBG, func=sm_pmilt_smtps, sm_smtps2pmilt=%x\n", ret);
				return ret;
			}
		}
	PM_LEV_DPRINTF(3, (PM_DEBFP, "sev=DBG, func=sm_pmilt_smtps, ret=%x\n", ret));
		return ret;
	}
	return EVTHR_DEL;
}

/*
**  SM_PMILT_SMTPSLI -- Handle new connections from SMTPS
**	This runs as a (listen) task.
**
**	Parameters:
**		tsk -- evthr task
**
**	Returns:
**		usual sm_error code
**
**	Last code review:
*/

sm_ret_T
sm_pmilt_smtpsli(sm_evthr_task_P tsk)
{
	int fd, r;
	uint u;
	uint32_t j;
	sm_ret_T ret;
	sm_evthr_task_P	task;
	pmg_ctx_P pmg_ctx;
	pmss_ctx_P pmss_ctx;

	SM_IS_EVTHR_TSK(tsk);
	SM_REQUIRE(tsk->evthr_t_nc != NULL);
	fd = INVALID_FD;
	pmg_ctx = (pmg_ctx_P) tsk->evthr_t_actx;
	SM_IS_PMG_CTX(pmg_ctx);

#if 0
	PM_LEV_DPRINTF(3, (PM_DEBFP, "func=sm_pmilt_smtpsli, status=new_connection\n"));
#endif /* 0 */

	/* add the new connection */
	if (tsk->evthr_t_nc != NULL
	    && is_valid_fd(fd = tsk->evthr_t_nc->evthr_a_fd))
	{
		r = pthread_mutex_lock(&pmg_ctx->pmg_mutex);
		SM_LOCK_OK(r);
		if (r != 0)
		{
			/* LOG */
			sm_io_fprintf(smioerr,
				"sev=ERROR, func=sm_pmilt_smtpsli, cannot get lock=%d\n", r);
			goto error;
		}
		if (pmg_ctx->pmg_ssnfd >= SM_ARRAY_SIZE(pmg_ctx->pmg_sctx))
		{
			/* LOG */
			/* too many: don't accept it */
			/* XXX maybe turn off accept job if max is reached? */
			sm_io_fprintf(smioerr,
				"sev=ERROR, func=sm_pmilt_smtpsli, too many connections\n");
			goto err2;
		}
		pmss_ctx = NULL;

		/* search for free sss ctx */
		for (j = 1, u = 0; u < SM_ARRAY_SIZE(pmg_ctx->pmg_sctx);
		     u++, j *= 2)
		{
			if ((pmg_ctx->pmg_sused & j) == 0)
			{
				pmg_ctx->pmg_sused |= j;
				pmss_ctx = pmg_ctx->pmg_sctx[u];
#if 0
				pmss_ctx->pmss_status = PMSS_ST_NONE;
#endif /* 0 */
				pmss_ctx->pmss_id = PMSS_ID_NONE;
				pmss_ctx->pmss_bit = j;
				break;
			}
		}
		if (u >= SM_ARRAY_SIZE(pmg_ctx->pmg_sctx))
		{
			/* see above */
			sm_io_fprintf(smioerr,
				"sev=ERROR, func=sm_pmilt_smtpsli, no free connection\n");
			goto err2;
		}
		ret = sm_rcb_open_rcv(pmss_ctx->pmss_com.rcbcom_rdrcb);
		if (sm_is_err(ret))
		{
			sm_io_fprintf(smioerr,
				"sev=ERROR, func=sm_pmilt_smtpsli, sm_rcb_open_rcv failed=%x\n",
				ret);
			goto err3;
		}
		ret = sm_fd_nonblock(fd, true);
		if (sm_is_err(ret))
		{
			sm_io_fprintf(smioerr,
				"sev=ERROR, func=sm_pmilt_smtpsli, sm_fd_nonblock=%x\n",
				ret);
			goto err3;
		}
		pmg_ctx->pmg_ssnfd++;

		/* start a task for the new SMTPS */
		ret = evthr_task_new(pmg_ctx->pmg_ev_ctx, &task,
					EVTHR_EV_RD, fd, NULL, sm_pmilt_smtps,
					pmss_ctx);
		if (sm_is_err(ret))
		{
			sm_io_fprintf(smioerr,
				"sev=ERROR, func=sm_pmilt_smtpsli, evthr_task_new failed=%x\n",
				ret);
			goto err3;
		}
		else
		{
			PM_LEV_DPRINTF(3, (PM_DEBFP,
				"sev=DBG, func=sm_pmilt_smtpsli, fd=%d, task=%p\n"
				, fd, task));
		}
		SM_IS_EVTHR_TSK(task);
		pmss_ctx->pmss_com.rcbcom_tsk = task;
	}
	r = pthread_mutex_unlock(&pmg_ctx->pmg_mutex);
	SM_ASSERT(r == 0);
	/* r isn't checked further; will fail on next iteration */

	return EVTHR_WAITQ;

  err3:
	pmg_ctx->pmg_sused &= ~j;
  err2:
	r = pthread_mutex_unlock(&pmg_ctx->pmg_mutex);
	SM_ASSERT(r == 0);
	/* r isn't checked further; will fail on next iteration */
  error:
	if (is_valid_fd(fd))
		close(fd);
	return EVTHR_WAITQ;
}
#endif /* MTA_USE_PMILTER */


syntax highlighted by Code2HTML, v. 0.9.1