/*
 * Copyright (c) 2004-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: t-pmilter-0.c,v 1.35 2006/10/05 04:27:38 ca Exp $")
#include "sm/error.h"
#include "sm/assert.h"
#include "sm/types.h"
#include "sm/sysexits.h"
#include "sm/fcntl.h"
#include "sm/io.h"
#include "sm/ctype.h"
#include "sm/reccom.h"
#include "sm/mta.h"
#define PMILTER_DEBUG_DEFINE 1
#include "pmilter.h"
#include "sm/pmfdef.h"
#include "sm/pmfapi.h"
#include "sm/pmilter.h"
#include "util.h"
#include "t-pmilter.h"

#if MTA_USE_PMILTER
/*
**  pmilter test program; uses direct access, not any "published" API.
*/

static pmt_ctx_T pmt_ctx;
static pmilter_T pmilter;

sm_ret_T
sm_pmilt_nseid(pmss_ctx_P pmss_ctx, pmse_ctx_P pmse_ctx, sm_rcbe_P rcbe)
{
	sm_ret_T ret;

	if (PMT_EXIT(pmt_ctx.pmt_rcode[SM_STAGE_NSEID]))
		exit(0);
	ret = sm_rcb_putv(&rcbe->rcbe_rcb, RCB_PUTV_FIRST,
		SM_RCBV_INT, RT_PROT_VER, PROT_VER_RT,
		SM_RCBV_INT, RT_M2S_ID, pmss_ctx->pmss_id,
		SM_RCBV_BUF, RT_M2S_SEID, pmse_ctx->pmse_se_id, SMTP_STID_SIZE,
		SM_RCBV_INT, RT_M2S_RCODE, pmt_ctx.pmt_rcode[SM_STAGE_NSEID],
		SM_RCBV_END);
	sm_io_fprintf(smioerr, "sev=DBG, seid=%s, host=%S, ret=%X\n",
		pmse_ctx->pmse_se_id, pmse_ctx->pmse_arg1, ret);
	return ret;
}

sm_ret_T
sm_pmilt_cseid(pmss_ctx_P pmss_ctx, pmse_ctx_P pmse_ctx)
{
	sm_io_fprintf(smioerr, "sev=DBG, seid=%s, where=close\n",
		pmse_ctx->pmse_se_id);
	return SM_SUCCESS;
}

sm_ret_T
sm_pmilt_helo(pmss_ctx_P pmss_ctx, pmse_ctx_P pmse_ctx, sm_rcbe_P rcbe, bool ehlo)
{
	sm_ret_T ret;

	sm_io_fprintf(smioerr, "sev=DBG, seid=%s, helo=%S\n",
		pmse_ctx->pmse_se_id, pmse_ctx->pmse_arg1);
	if (PMT_EXIT(pmt_ctx.pmt_rcode[SM_STAGE_HELO]))
		exit(0);
	ret = sm_rcb_putv(&rcbe->rcbe_rcb, RCB_PUTV_FIRST,
		SM_RCBV_INT, RT_PROT_VER, PROT_VER_RT,
		SM_RCBV_INT, RT_M2S_ID, pmss_ctx->pmss_id,
		SM_RCBV_BUF, RT_M2S_SEID, pmse_ctx->pmse_se_id, SMTP_STID_SIZE,
		SM_RCBV_INT, RT_M2S_RCODE, pmt_ctx.pmt_rcode[SM_STAGE_HELO],
		SM_RCBV_END);
	return ret;
}

sm_ret_T
sm_pmilt_mail(pmss_ctx_P pmss_ctx, pmse_ctx_P pmse_ctx, sm_rcbe_P rcbe)
{
	sm_ret_T ret;
	unsigned int nelem;
	char **argv;

	ret = args2argv(pmse_ctx->pmse_arg2, &nelem, &argv);
	sm_io_fprintf(smioerr,
		"sev=DBG, seid=%s, mail=%@S, args=%S, nelem=%u, args2argv=%r, argv[0]=%s\n",
		pmse_ctx->pmse_se_id, pmse_ctx->pmse_arg1,
		pmse_ctx->pmse_arg2, nelem, ret,
		(argv != NULL && argv[0] != NULL) ? argv[0] : "NULL");
	if (PMT_EXIT(pmt_ctx.pmt_rcode[SM_STAGE_MAIL]))
		exit(0);
	ret = sm_rcb_putv(&rcbe->rcbe_rcb, RCB_PUTV_FIRST,
		SM_RCBV_INT, RT_PROT_VER, PROT_VER_RT,
		SM_RCBV_INT, RT_M2S_ID, pmss_ctx->pmss_id,
		SM_RCBV_BUF, RT_M2S_SEID, pmse_ctx->pmse_se_id, SMTP_STID_SIZE,
		SM_RCBV_INT, RT_M2S_RCODE, pmt_ctx.pmt_rcode[SM_STAGE_MAIL],
		SM_RCBV_END);
	if (argv != NULL)
		sm_free(argv);
	return ret;
}

sm_ret_T
sm_pmilt_rcpt(pmss_ctx_P pmss_ctx, pmse_ctx_P pmse_ctx, sm_rcbe_P rcbe)
{
	sm_ret_T ret;

	sm_io_fprintf(smioerr, "sev=DBG, seid=%s, rcpt=%@S, idx=%d\n",
		pmse_ctx->pmse_se_id, pmse_ctx->pmse_arg1,
		pmse_ctx->pmse_rcpt_idx);
	if (PMT_EXIT(pmt_ctx.pmt_rcode[SM_STAGE_RCPT]))
		exit(0);
	ret = sm_rcb_putv(&rcbe->rcbe_rcb, RCB_PUTV_FIRST,
		SM_RCBV_INT, RT_PROT_VER, PROT_VER_RT,
		SM_RCBV_INT, RT_M2S_ID, pmss_ctx->pmss_id,
		SM_RCBV_BUF, RT_M2S_SEID, pmse_ctx->pmse_se_id, SMTP_STID_SIZE,
		SM_RCBV_INT, RT_M2S_RCODE, pmt_ctx.pmt_rcode[SM_STAGE_RCPT],
		SM_RCBV_END);
	return ret;
}

sm_ret_T
sm_pmilt_data(pmss_ctx_P pmss_ctx, pmse_ctx_P pmse_ctx, sm_rcbe_P rcbe)
{
	sm_ret_T ret;

	sm_io_fprintf(smioerr, "sev=DBG, seid=%s, where=data\n",
		pmse_ctx->pmse_se_id);
	if (PMT_EXIT(pmt_ctx.pmt_rcode[SM_STAGE_DATA]))
		exit(0);
	ret = sm_rcb_putv(&rcbe->rcbe_rcb, RCB_PUTV_FIRST,
		SM_RCBV_INT, RT_PROT_VER, PROT_VER_RT,
		SM_RCBV_INT, RT_M2S_ID, pmss_ctx->pmss_id,
		SM_RCBV_BUF, RT_M2S_SEID, pmse_ctx->pmse_se_id, SMTP_STID_SIZE,
		SM_RCBV_INT, RT_M2S_RCODE, pmt_ctx.pmt_rcode[SM_STAGE_DATA],
		SM_RCBV_END);
	return ret;
}

static void
pm_open(pmse_ctx_P pmse_ctx)
{
	if (PMT_IS_FLAG(&pmt_ctx, PMT_FL_W2F) &&
	    !PMT_IS_FLAG(&pmt_ctx, PMT_FL_FD_OPEN) &&
	    !PMT_IS_FLAG(&pmt_ctx, PMT_FL_FD_FAIL))
	{
		pmt_ctx.pmt_fd = open(pmt_ctx.pmt_fname,
					O_WRONLY|O_APPEND|O_CREAT,
					0660);
		if (pmt_ctx.pmt_fd >= 0)
			PMT_SET_FLAG(&pmt_ctx, PMT_FL_FD_OPEN);
		else
			PMT_SET_FLAG(&pmt_ctx, PMT_FL_FD_FAIL);
		sm_io_fprintf(smioerr,
			"sev=DBG, seid=%s, where=pm_open, open=%d\n",
			pmse_ctx->pmse_se_id, pmt_ctx.pmt_fd);
	}
}

sfsistat_T
sm_pmilt_msg(pmss_ctx_P pmss_ctx, pmse_ctx_P pmse_ctx, uchar *buf, size_t len, sm_rcbe_P rcbe)
{
	pm_open(pmse_ctx);
	if (PMT_IS_FLAG(&pmt_ctx, PMT_FL_W2F) &&
	    PMT_IS_FLAG(&pmt_ctx, PMT_FL_FD_OPEN) &&
	    !PMT_IS_FLAG(&pmt_ctx, PMT_FL_WR_FAIL) &&
	    buf != NULL && len > 0
	   )
	{
		ssize_t written;

		written = write(pmt_ctx.pmt_fd, buf, len);
		if (written == -1)
		{
			sm_io_fprintf(smioerr,
				"sev=ERROR, seid=%s, where=msg, write=%d\n",
				pmse_ctx->pmse_se_id, (int) written);
			PMT_SET_FLAG(&pmt_ctx, PMT_FL_WR_FAIL);
			close(pmt_ctx.pmt_fd);
			pmt_ctx.pmt_fd = INVALID_FD;
			PMT_CLR_FLAG(&pmt_ctx, PMT_FL_FD_OPEN);
		}
	}

	sm_io_fprintf(smioerr, "sev=DBG, seid=%s, where=msg, len=%d\n",
		pmse_ctx->pmse_se_id, (int) len);
	return SM_SUCCESS;
}

sm_ret_T
sm_pmilt_dot(pmss_ctx_P pmss_ctx, pmse_ctx_P pmse_ctx, uchar *buf, size_t len, sm_rcbe_P rcbe)
{
	sm_ret_T ret;
	bool writefailed;
	int rcode;

	pm_open(pmse_ctx);
	if (PMT_IS_FLAG(&pmt_ctx, PMT_FL_W2F) &&
	    PMT_IS_FLAG(&pmt_ctx, PMT_FL_FD_OPEN) &&
	    !PMT_IS_FLAG(&pmt_ctx, PMT_FL_WR_FAIL) &&
	    buf != NULL && len > 0
	   )
	{
		ssize_t written;

		written = write(pmt_ctx.pmt_fd, buf, len);
		if (written == -1)
		{
			sm_io_fprintf(smioerr,
				"sev=ERROR, seid=%s, where=dot, write=%d\n",
				pmse_ctx->pmse_se_id, (int) written);
			PMT_SET_FLAG(&pmt_ctx, PMT_FL_WR_FAIL);
			close(pmt_ctx.pmt_fd);
			pmt_ctx.pmt_fd = INVALID_FD;
			PMT_CLR_FLAG(&pmt_ctx, PMT_FL_FD_OPEN);
		}
	}

	writefailed = PMT_IS_FLAG(&pmt_ctx, PMT_FL_W2F) &&
			PMT_IS_FLAG(&pmt_ctx, PMT_FL_WR_FAIL);
	if (PMT_IS_FLAG(&pmt_ctx, PMT_FL_W2F) &&
	    PMT_IS_FLAG(&pmt_ctx, PMT_FL_FD_OPEN) &&
	    !PMT_IS_FLAG(&pmt_ctx, PMT_FL_WR_FAIL))
	{
		int r;

		r = close(pmt_ctx.pmt_fd);
		sm_io_fprintf(smioerr,
			"sev=DBG, seid=%s, where=dot, write=%d\n",
			pmse_ctx->pmse_se_id, r);
		pmt_ctx.pmt_fd = INVALID_FD;
		PMT_CLR_FLAG(&pmt_ctx, PMT_FL_FD_OPEN);
	}

	rcode = pmt_ctx.pmt_rcode[SM_STAGE_DOT];
	if (rcode == SMTP_R_OK && writefailed)
		rcode = SMTP_R_TEMP;

	sm_io_fprintf(smioerr, "sev=DBG, seid=%s, where=dot, len=%d\n",
		pmse_ctx->pmse_se_id, (int) len);

	if (PMT_EXIT(pmt_ctx.pmt_rcode[SM_STAGE_DOT]))
		exit(0);
	ret = sm_rcb_putv(&rcbe->rcbe_rcb, RCB_PUTV_FIRST,
		SM_RCBV_INT, RT_PROT_VER, PROT_VER_RT,
		SM_RCBV_INT, RT_M2S_ID, pmss_ctx->pmss_id,
		SM_RCBV_BUF, RT_M2S_SEID, pmse_ctx->pmse_se_id, SMTP_STID_SIZE,
		SM_RCBV_INT, RT_M2S_RCODE, pmt_ctx.pmt_rcode[SM_STAGE_DOT],
		SM_RCBV_END);
	return ret;
}

sm_ret_T
sm_pmilt_abort_ta(pmss_ctx_P pmss_ctx, pmse_ctx_P pmse_ctx)
{
	sm_io_fprintf(smioerr, "sev=DBG, seid=%s, where=abort_ta\n",
		pmse_ctx->pmse_se_id);
	return SM_SUCCESS;
}

sm_ret_T
sm_pmilt_msg_rplc_stat(pmss_ctx_P pmss_ctx, pmse_ctx_P pmse_ctx, sm_ret_T status)
{
	return SM_SUCCESS;
}

/*
**  USAGE -- Print usage message to smioerr
**
**	Parameters:
**		prg -- program name
**
**	Returns:
**		exits
*/

static void
usage(const char *prg)
{
	sm_io_fprintf(smioerr, "usage: %s [options]\n"
#if PMILTER_DEBUG
		"-d n             set debug level\n"
#endif
		"-f name          write msg to file\n"
		"-r stage=rcode   set SMTP reply code for stage to rcode\n"
		"   stage:\n"
		"     c: new session (connect)\n"
		"     h: HELO\n"
		"     m: MAIL\n"
		"     r: RCPT\n"
		"     d: DATA\n"
		"     B: Body\n"
		"   rcode=%d will invoke exit() to simulate a fatal error\n"
		, prg
		, PMT_EXIT_CODE
		);
	exit(EX_USAGE);
}

/*
**  MAIN -- PMILTER test server
**
**	Parameters:
**		argc -- number of arguments
**		argv -- vector of arguments
**
**	Returns:
**		exit code
*/

int
main(int argc, char *argv[])
{
	sm_ret_T ret;
	int c, stage;
	unsigned int u, off;
	pmg_ctx_P pmg_ctx;
	char *prg;

	prg = argv[0];
	pmg_ctx = NULL;
	if (getuid() == 0 || geteuid() == 0)
	{
		sm_io_fprintf(smioerr,
			"%s: ERROR: do not run this as super-user!\n",
			prg);
		exit(EX_USAGE);
	}

	/* initialize test context */
	sm_memzero(&pmt_ctx, sizeof(pmt_ctx));
	for (c = 0; c < SM_STAGES; c++)
		pmt_ctx.pmt_rcode[c] = SMTP_R_CONT;
	pmt_ctx.pmt_fname[0] = '\0';
	pmt_ctx.pmt_fd = INVALID_FD;

	sm_memzero(&pmilter, sizeof(pmilter));
	pmilter.pmfi_name = "t-pmilter-0";
	pmilter.pmfi_version = LPMILTER_VERSION;
	pmilter.pmfi_dfl_cap = SM_SCAP_PM_BASIC;
	pmilter.pmfi_dfl_fct = 0;
	pmilter.pmfi_dfl_feat = 0;
	pmilter.pmfi_dfl_misc = 0;

	/* basic initialization */
	ret = sm_pmilt_init0(&pmg_ctx);
	if (sm_is_err(ret))
		goto error0;
#if SM_HEAP_CHECK
	SmHeapCheck = 1;
	if (HEAP_CHECK)
		sm_heap_report(smioerr, 3);
#endif

	while ((c = getopt(argc, argv, "d:f:r:")) != -1)
	{
		switch (c)
		{
		  case 'd':
#if PMILTER_DEBUG
			pm_debug = atoi(optarg);
#endif
			break;
		  case 'f':
			strlcpy(pmt_ctx.pmt_fname, optarg,
				sizeof(pmt_ctx.pmt_fname));
			PMT_SET_FLAG(&pmt_ctx, PMT_FL_W2F);
			break;
		  case 'r':
			if (optarg != NULL && ISALPHA(optarg[0])
			    && optarg[1] == '=')
				off = 2;
			else
			{
				usage(prg);
				break;
			}

			u = (unsigned int) atoi(optarg + off);
			stage = sm_getstage(optarg[0]);
			if (stage < 0 || stage > SM_STAGES)
			{
				usage(prg);
				/* NOTREACHED */
				SM_ASSERT(false);
			}
			pmt_ctx.pmt_rcode[stage] = u;
			break;
		  default:
			usage(prg);
			/* NOTREACHED */
			SM_ASSERT(false);
			break;
		}
	}

	argc -= optind;
	argv += optind;

	/* XXX HACK */
	if (argc > 0)
		pmg_ctx->pmg_sockname = argv[0];
	else
		pmg_ctx->pmg_sockname = "pmilter.sock";
	pmg_ctx->pmg_pmilter = &pmilter;

#if 0
	/* read config */
	ret = sm_pmilt_rdcf(pmg_ctx, argc, argv);
	if (sm_is_err(ret))
		goto error0;
#endif

	/* initialize system */
	ret = sm_pmilt_init1(pmg_ctx);
	if (sm_is_err(ret))
	{
		sm_io_fprintf(smioerr, "sev=ERROR, sm_pmilt_init=%x\n", ret);
		goto error;
	}

	/* startup pmilt */
	ret = sm_pmilt_start(pmg_ctx);
	if (sm_is_err(ret))
	{
		sm_io_fprintf(smioerr, "sev=ERROR, sm_pmilt_start=%x\n", ret);
		goto error;
	}

	/* wait for events, schedule tasks */
	ret = evthr_loop(pmg_ctx->pmg_ev_ctx);

	ret = sm_pmilt_stop(pmg_ctx);
#if SM_HEAP_CHECK
	if (HEAP_CHECK)
		sm_heap_report(smioerr, 3);
#endif
	return 0;

  error:
	/* ignore shutdown errors... */
	(void) sm_pmilt_stop(pmg_ctx);

  error0:
	/* select an appropriate error here... */
	return sm_error_value(ret);
}
#else /* MTA_USE_PMILTER */
int
main(int argc, char *argv[])
{
	return 0;
}
#endif /* MTA_USE_PMILTER */


syntax highlighted by Code2HTML, v. 0.9.1