/*
 * Copyright (c) 2005, 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-msgmod-0.c,v 1.24 2006/04/02 06:34:21 ca Exp $")
#include "sm/assert.h"
#include "sm/error.h"
#include "sm/string.h"
#include "sm/str.h"
#include "sm/cstr.h"
#include "sm/io.h"
#include "sm/ctype.h"
#include "sm/mta.h"
#include "sm/da.h"
#include "smtpc.h"
#define SMTPC_LOG_DEFINES 1
#include "log.h"

#include "sm/test.h"
#include "sm/sysexits.h"

#define SC_PHASE_NOREPLY 0

/*
**  SC_COMMAND -- send one SMTP command (dummy)
**
**	Parameters:
**		sc_t_ctx -- SMTPC thread context
**		phase -- phase of SMTP dialogue
**
**	Returns:
**		SMTP reply code (2xy -> SMTP_OK) or error code
*/

static sm_ret_T
sc_command(sc_t_ctx_P sc_t_ctx, int phase)
{
	sm_ret_T ret;
	ssize_t b;
	size_t l;
	sc_sess_P sc_sess;

	SM_REQUIRE(sc_t_ctx != NULL);
	sc_sess = sc_t_ctx->sct_sess;
	l = sm_str_getlen(sc_sess->scse_wr);
	ret = sm_io_write(smioout, sm_str_data(sc_sess->scse_wr), l, &b);
	return ret;
}

/* ----- BEGIN COPY FROM smtpc/smclt.c ----- */

/*
**  SC_WRTBUF -- write buffer
**
**	Parameters:
**		sc_t_ctx -- SMTPC thread context
**		buf -- buffer to write
**		wrt -- number of bytes to write
**		cfp -- output file
**		ptotal_size -- (pointer to) total size (in/out)
**
**	Returns:
**		usual error code
*/

static sm_ret_T
sc_wrtbuf(sc_t_ctx_P sc_t_ctx, uchar *buf, size_t wrt, sm_file_T *cfp, off_t *ptotal_size)
{
	size_t offset;
	ssize_t byteswritten;
	sm_ret_T ret;
	off_t total_size;

	ret = SM_SUCCESS;
	if (wrt == 0)
		return ret;
	offset = 0;
	total_size = *ptotal_size;
	do
	{
		ret = sm_io_write(cfp, buf + offset, wrt, &byteswritten);
		if (sm_is_err(ret))
			return ret;
		if (byteswritten > 0)
		{
			total_size += byteswritten;
			offset += byteswritten;
		}

		/* paranoia... should this be just an if ()? */
		SM_ASSERT(wrt >= byteswritten);
		if (wrt > byteswritten)
			sm_log_write(sc_t_ctx->sct_sc_ctx->scc_lctx,
				SC_LCAT_CLIENT, SC_LMOD_CLIENT,
				SM_LOG_INFO, 8,
				"sev=INFO, func=sc_data, wrt=%d, written=%d, ret=%m",
				wrt, byteswritten, ret);

		wrt -= byteswritten;
	} while (wrt > 0);
	*ptotal_size = total_size;
	return ret;
}

/*
**  SC_MSGDSN -- send msg (with modifications for DSN)
**
**	Parameters:
**		sc_t_ctx -- SMTPC thread context
**		ptotal_size -- (pointer to) total size (in/out)
**
**	Returns:
**		usual error code
*/

static sm_ret_T
sc_msgdsn(sc_t_ctx_P sc_t_ctx, off_t *ptotal_size)
{
	int c;
	size_t wrt, idx;
	sm_ret_T ret;
	uint eoh_state;
	off_t total_size, cdb_rd;
	sc_ta_P sc_ta;
	sc_sess_P sc_sess;
	sm_file_T *cfp;
	static SM_DECL_EOH;

	sc_sess = sc_t_ctx->sct_sess;
	SM_REQUIRE(sc_sess != NULL);
	sc_ta = sc_sess->scse_ta;
	SM_IS_SC_TA(sc_ta);
	cfp = sc_sess->scse_fp;
	eoh_state = 0;
	cdb_rd = 0;
	total_size = *ptotal_size;
	ret = SM_SUCCESS;

	do
	{
		/* get new buffer */
		c = sm_rget(sc_ta->scta_cdb_fp);
		if (SM_IO_EOF == c)
			break;

		/* +1 because we got the first char already */
		wrt = f_r(*(sc_ta->scta_cdb_fp)) + 1;
		cdb_rd += wrt;

		/* check whether eoh is in this block */
		if (SCTA_IS_FLAG(sc_ta, SCTA_FL_HDR_ONLY)
		    && eoh_state < SM_EOH_LEN)
		{
			uchar *p;

			p = f_bfbase(*sc_ta->scta_cdb_fp);
			idx = 0;
			do
			{
				if (c == eoh[eoh_state])
					++eoh_state;
				else
				{
					eoh_state = 0;
					if (c == eoh[eoh_state])
						++eoh_state;
				}
				++idx;
				if (idx < wrt)
					c = p[idx];
			} while (eoh_state < SM_EOH_LEN && idx < wrt);

			/*
			**  Found end of header?  If yes: set the
			**  number of bytes to write; the buffer
			**  MUST end with \r\n such that the final
			**  dot is properly recognized (see below).
			*/

			if (eoh_state >= SM_EOH_LEN)
			{
				SCTA_SET_FLAG(sc_ta, SCTA_FL_CUT_HDR);
				wrt = idx;
			}
		}

		/*
		**  For a MIME DSN the last 3 bytes (.\r\n)
		**  of cdb must not be sent.
		*/

		if (SCTA_IS_FLAG(sc_ta, SCTA_FL_DSN_MIME) &&
		    !SCTA_IS_FLAG(sc_ta, SCTA_FL_CDB_CUT) &&
		    cdb_rd + 3 >= sc_ta->scta_msg_sz_b
		   )
		{
			size_t cutoff;

			SM_ASSERT(sc_ta->scta_msg_sz_b >= SM_EOT_LEN);
			SM_ASSERT(cdb_rd <= sc_ta->scta_msg_sz_b);
			cutoff = sc_ta->scta_msg_sz_b - cdb_rd + 3;
			if (wrt > cutoff)
				wrt -= cutoff;
			else
				wrt = 0;
			SCTA_SET_FLAG(sc_ta, SCTA_FL_CUT_DOT);
		}

		ret = sc_wrtbuf(sc_t_ctx,
			f_bfbase(*sc_ta->scta_cdb_fp), wrt, cfp, &total_size);
		if (sm_is_err(ret))
			goto error;

		if (SCTA_IS_FLAG(sc_ta, SCTA_FL_CUT_HDR) ||
		    SCTA_IS_FLAG(sc_ta, SCTA_FL_CUT_DOT))
		{
			/* the data above ended with \r\n (eoh) */
			if (SCTA_IS_FLAG(sc_ta, SCTA_FL_DSN_MIME))
			{
				if (sm_is_err(ret =
					sm_str_scopy(sc_sess->scse_wr,
						"\r\n--"))
				   || sm_is_err(ret =
					sm_str_cat(sc_sess->scse_wr,
						sc_sess->scse_str))
				   || sm_is_err(ret =
					sm_str_scat(sc_sess->scse_wr,
						"--\r\n.\r\n")))
					goto error;
			}
			else
			{
				ret = sm_str_scopy(sc_sess->scse_wr,
					".\r\n");
			}

			if (sm_is_err(ret))
				goto error;
			ret = sc_command(sc_t_ctx, SC_PHASE_NOREPLY);
			if (sm_is_err(ret))
				goto error;
			c = SM_IO_EOF;	/* force end of loop */
		}
	} while (c != SM_IO_EOF);
	*ptotal_size = total_size;
	return ret;

  error:
	*ptotal_size = total_size;
	return ret;
}

/*
**  SC_HDRS_APP -- append headers
**
**	Parameters:
**		sc_t_ctx -- SMTPC thread context
**		sm_hdrmod -- current header to append
**		ptotal_size -- (pointer to) total size (in/out)
**
**	Returns:
**		usual error code
*/

static sm_ret_T
sc_hdrs_app(sc_t_ctx_P sc_t_ctx, sm_hdrmod_P sm_hdrmod, off_t *ptotal_size)
{
	sm_ret_T ret;
	sc_ta_P sc_ta;
	sc_sess_P sc_sess;
	sm_file_T *cfp;
	sm_cstr_P hdr;

	sc_sess = sc_t_ctx->sct_sess;
	SM_REQUIRE(sc_sess != NULL);
	sc_ta = sc_sess->scse_ta;
	SM_IS_SC_TA(sc_ta);
	cfp = sc_sess->scse_fp;
	ret = SM_SUCCESS;

	while (sm_hdrmod != NULL &&
		SM_HM_TYPE_APPEND == sm_hdrmod->sm_hm_type
		&& (hdr = sm_hdrmod->sm_hm_hdr) != NULL)
	{
		ret = sc_wrtbuf(sc_t_ctx, sm_cstr_data(hdr),
			sm_cstr_getlen(hdr),
			cfp, ptotal_size);
		sm_hdrmod_rm(&sc_ta->scta_hdrmodhd);
		sm_hdrmod = HDRMODL_FIRST(sc_ta->scta_hdrmodhd);
	}
	return ret;
}

/*
**  SC_HDRS_INS -- perform header insertions at a given position
**
**	Parameters:
**		sc_t_ctx -- SMTPC thread context
**		hdr_count -- current header number
**		writeit -- are we writing or skipping headers?
**		idx -- current (read) index in file buffer
**		poffset -- (ptr) current (write) offset in file buffer (in/out)
**		ptotal_size -- (pointer to) total size (in/out)
**
**	Returns:
**		>0: number of header insertions
**		<=0: usual error code
*/

static sm_ret_T
sc_hdrs_ins(sc_t_ctx_P sc_t_ctx, uint hdr_count, bool writeit, size_t idx, size_t *poffset, off_t *ptotal_size)
{
	sm_ret_T ret;
	uint mods;
	sm_hdrmod_P sm_hdrmod;
	sm_cstr_P hdr;
	sc_ta_P sc_ta;
	sc_sess_P sc_sess;
	sm_file_T *cfp;

	sc_sess = sc_t_ctx->sct_sess;
	SM_REQUIRE(sc_sess != NULL);
	sc_ta = sc_sess->scse_ta;
	SM_IS_SC_TA(sc_ta);
	mods = 0;

	cfp = sc_sess->scse_fp;

	if (writeit &&
	    (sm_hdrmod = HDRMODL_FIRST(sc_ta->scta_hdrmodhd)) != NULL &&
	    (SM_HM_TYPE_INSERT == sm_hdrmod->sm_hm_type ||
	     SM_HM_TYPE_REPLACE == sm_hdrmod->sm_hm_type) &&
	    hdr_count == sm_hdrmod->sm_hm_pos &&
	    (hdr = sm_hdrmod->sm_hm_hdr) != NULL)
	{
		size_t offset;

		SM_ASSERT(poffset != NULL);
		offset = *poffset;
		SM_ASSERT(idx >= offset);
		ret = sc_wrtbuf(sc_t_ctx,
			f_bfbase(*sc_ta->scta_cdb_fp) + offset,
			idx - offset, cfp, ptotal_size);
		if (sm_is_err(ret))
			return ret;
		*poffset = idx;
	}

	while ((sm_hdrmod = HDRMODL_FIRST(sc_ta->scta_hdrmodhd)) != NULL &&
	       (SM_HM_TYPE_INSERT == sm_hdrmod->sm_hm_type ||
		SM_HM_TYPE_REPLACE == sm_hdrmod->sm_hm_type) &&
	       hdr_count == sm_hdrmod->sm_hm_pos &&
	       (hdr = sm_hdrmod->sm_hm_hdr) != NULL)
	{
		++mods;
		ret = sc_wrtbuf(sc_t_ctx, sm_cstr_data(hdr),
			sm_cstr_getlen(hdr), cfp, ptotal_size);
		if (sm_is_err(ret))
			return ret;
		sm_hdrmod_rm(&sc_ta->scta_hdrmodhd);
		sm_hdrmod = HDRMODL_FIRST(sc_ta->scta_hdrmodhd);
	}
	return (mods > 0) ? mods : SM_SUCCESS;
}

/*
**  SC_MSGMOD -- send msg (possibly with modifications)
**
**	Parameters:
**		sc_t_ctx -- SMTPC thread context
**		ptotal_size -- (pointer to) total size (in/out)
**
**	Returns:
**		usual error code
*/

static sm_ret_T
sc_msgmod(sc_t_ctx_P sc_t_ctx, off_t *ptotal_size)
{
	int c;
	size_t wrt, idx;
	sm_ret_T ret;
	uint eoh_state;
	uint hdr_count, eol_state;
	bool writeit;
	off_t total_size, cdb_rd;
	sc_ta_P sc_ta;
	sc_sess_P sc_sess;
	sm_file_T *cfp;
	sm_hdrmod_P sm_hdrmod;
	sm_cstr_P hdr;
	static SM_DECL_EOH;
	static SM_DECL_EOL;

	sc_sess = sc_t_ctx->sct_sess;
	SM_REQUIRE(sc_sess != NULL);
	sc_ta = sc_sess->scse_ta;
	SM_IS_SC_TA(sc_ta);
	cfp = sc_sess->scse_fp;
	eoh_state = 0;
	cdb_rd = 0;
	total_size = *ptotal_size;
	ret = SM_SUCCESS;
	hdr_count = 0;
	eol_state = 0;
	writeit = true;

	sm_hdrmod = HDRMODL_FIRST(sc_ta->scta_hdrmodhd);
	while (sm_hdrmod != NULL &&
	       SM_HM_TYPE_PREPEND == sm_hdrmod->sm_hm_type &&
	       (hdr = sm_hdrmod->sm_hm_hdr) != NULL)
	{
		ret = sc_wrtbuf(sc_t_ctx, sm_cstr_data(hdr),
			sm_cstr_getlen(hdr), cfp, &total_size);
		if (sm_is_err(ret))
			goto error;
		sm_hdrmod_rm(&sc_ta->scta_hdrmodhd);
		sm_hdrmod = HDRMODL_FIRST(sc_ta->scta_hdrmodhd);
	}
	if (sm_hdrmod != NULL &&
	    (SM_HM_TYPE_REMOVE == sm_hdrmod->sm_hm_type ||
	     SM_HM_TYPE_REPLACE == sm_hdrmod->sm_hm_type) &&
	    hdr_count == sm_hdrmod->sm_hm_pos)
		writeit = false;

	ret = sc_hdrs_ins(sc_t_ctx, hdr_count, false, 0, NULL, &total_size);
	if (sm_is_err(ret))
		goto error;

	do
	{
		size_t offset;

		offset = 0;

		/* get new buffer */
		c = sm_rget(sc_ta->scta_cdb_fp);
		if (SM_IO_EOF == c)
			break;

		/* +1 because we got the first char already */
		wrt = f_r(*(sc_ta->scta_cdb_fp)) + 1;
		cdb_rd += wrt;

		/* analyse current buffer (if it contains headers) */
		if (SCTA_IS_FLAG(sc_ta, SCTA_FL_HDR_ONLY|SCTA_FL_HDR_SCAN)
		    && eoh_state < SM_EOH_LEN)
		{
			uchar *p;

			p = f_bfbase(*sc_ta->scta_cdb_fp);
			idx = 0;
			do
			{
				if (c == eoh[eoh_state])
					++eoh_state;
				else
				{
					eoh_state = 0;
					if (c == eoh[eoh_state])
						++eoh_state;
				}

				if (eol_state >= SM_EOL_LEN &&
				    c != ' ' && c != '\t')
				{
					bool hdr_rm;

					++hdr_count;
					hdr_rm = (sm_hdrmod != NULL &&
						  (SM_HM_TYPE_REMOVE ==
							sm_hdrmod->sm_hm_type ||
						   SM_HM_TYPE_REPLACE ==
							sm_hdrmod->sm_hm_type)
						  && hdr_count ==
							sm_hdrmod->sm_hm_pos);
					if (hdr_rm &&
					    SM_HM_TYPE_REMOVE ==
						sm_hdrmod->sm_hm_type)
					{
						sm_hdrmod_rm(&sc_ta->scta_hdrmodhd);
						sm_hdrmod = HDRMODL_FIRST(sc_ta->scta_hdrmodhd);
					}

					if (writeit && hdr_rm)
					{
						SM_ASSERT(idx >= offset);
						ret = sc_wrtbuf(sc_t_ctx,
							f_bfbase(*sc_ta->scta_cdb_fp)
								+ offset,
							idx - offset,
							cfp,
							&total_size);
						if (sm_is_err(ret))
							goto error;
						offset = idx;
						if (c != '\r')
							writeit = false;
					}
					else if (!writeit && !hdr_rm)
					{
						writeit = true;
						offset = idx;
					}
					ret = sc_hdrs_ins(sc_t_ctx, hdr_count,
						writeit, idx, &offset,
						&total_size);
					if (ret > 0)
						sm_hdrmod = HDRMODL_FIRST(sc_ta->scta_hdrmodhd);
				}

				/*
				**  End of headers reached? This is not
				**  entirely correct as it just checks for CR
				**  not CR LF, but we have to "insert" these
				**  headers before the second CRLF
				*/

				if (eol_state >= SM_EOL_LEN && c == '\r' &&
				    sm_hdrmod != NULL &&
				    SM_HM_TYPE_APPEND == sm_hdrmod->sm_hm_type
				    && sm_hdrmod->sm_hm_hdr != NULL)
				{
					ret = sc_wrtbuf(sc_t_ctx,
						f_bfbase(*sc_ta->scta_cdb_fp)
								+ offset,
						idx - offset, cfp,
						&total_size);
					if (sm_is_err(ret))
						goto error;
					ret = sc_hdrs_app(sc_t_ctx, sm_hdrmod,
							&total_size);
					offset = idx;
				}

				if (c == eol[eol_state])
					++eol_state;
				else
				{
					eol_state = 0;
					if (c == eol[eol_state])
						++eol_state;
				}

				++idx;
				if (idx < wrt)
					c = p[idx];
			} while (eoh_state < SM_EOH_LEN && idx < wrt);
		}

		if (writeit)
		{
			ret = sc_wrtbuf(sc_t_ctx,
				f_bfbase(*sc_ta->scta_cdb_fp) + offset,
				wrt - offset, cfp, &total_size);
			if (sm_is_err(ret))
				goto error;
		}
	} while (c != SM_IO_EOF);
	*ptotal_size = total_size;
	return ret;

  error:
	*ptotal_size = total_size;
	return ret;
}

/* ----- END COPY FROM smtpc/smclt.c ----- */

static void
usage(const char *prg)
{
	sm_io_fprintf(smioerr,
		"usage: %s [options]\n"
		"Test message modifications for delivery\n"
		"options:\n"
		"-b n            set I/O buffer size to n\n"
		"-d n            delete header n\n"
		"-f n            set flags\n"
		"-i pos=header   insert header at pos\n"
		"-M type=header  prepend or append header\n"
		"                type=a or p\n"
		"-r pos=header   replace header at pos\n"
		, prg);
}

int
main(int argc, char **argv)
{
	int c;
	unsigned long l;
	sm_ret_T ret;
	off_t total_size;
	size_t size;
	sc_ta_P sc_ta;
	sc_sess_P sc_sess;
	sc_t_ctx_P sc_t_ctx;
	sc_ctx_P sc_ctx;
	sm_hdrmod_P sm_hdrmod;
	char hdr[256], *str;

	sc_ta = NULL;
	sc_ta = sm_zalloc(sizeof(*sc_ta));
	if (NULL == sc_ta)
		return(EX_OSERR);
	sc_sess = sm_zalloc(sizeof(*sc_sess));
	if (NULL == sc_sess)
		return(EX_OSERR);
	sc_t_ctx = sm_zalloc(sizeof(*sc_t_ctx));
	if (NULL == sc_t_ctx)
		return(EX_OSERR);
	sc_ctx = sm_zalloc(sizeof(*sc_ctx));
	if (NULL == sc_ctx)
		return(EX_OSERR);

	sc_t_ctx->sm_magic = SM_SC_T_CTX_MAGIC;
	sc_sess->sm_magic = SM_SC_SESS_MAGIC;
	sc_ta->sm_magic = SM_SC_TA_MAGIC;

	sc_t_ctx->sct_sc_ctx = sc_ctx;
	sc_sess->scse_sct_ctx = sc_t_ctx;
	sc_t_ctx->sct_sess = sc_sess;
	sc_sess->scse_fp = smioout;
	sc_sess->scse_ta = sc_ta;

	while ((c = getopt(argc, argv, "b:d:f:i:M:r:")) != -1)
	{
		switch (c)
		{
		  case 'b':
			size = strtoul(optarg, NULL, 0);
			ret = sm_io_setvbuf(smioin, NULL, SM_IO_FBF, size);
			break;

		  case 'd':
			sm_hdrmod = NULL;
			ret = sm_hdrmod_new(&sc_ta->scta_hdrmodhd, false,
					&sm_hdrmod);
			SM_TEST(SM_SUCCESS == ret);
			if (SM_SUCCESS != ret)
				return(EX_OSERR);
			SM_TEST(sm_hdrmod != NULL);
			if (NULL == sm_hdrmod)
				return(EX_OSERR);
			sm_hdrmod->sm_hm_type = SM_HM_TYPE_REMOVE;
			sm_hdrmod->sm_hm_pos = strtoul(optarg, NULL, 0);
			ret = sm_hdrmod_insert(sc_ta->scta_hdrmodhd, sm_hdrmod);
			SM_TEST(SM_SUCCESS == ret);
			if (SM_SUCCESS != ret)
				return(EX_OSERR);
			SCTA_SET_FLAG(sc_ta, SCTA_FL_HDR_SCAN);
			break;

		  case 'f':
			sc_ta->scta_flags = strtol(optarg, NULL, 0);
			break;

		  case 'i':
			l = strtoul(optarg, &str, 0);
			if (ULONG_MAX == l || l > UINT_MAX)
				usage(argv[0]);
			if (NULL == str || *str != '=')
				usage(argv[0]);

			sm_hdrmod = NULL;
			strlcpy(hdr, str + 1, sizeof(hdr));
			strlcat(hdr, "\r\n", sizeof(hdr));
			ret = sm_hdrmod_new(&sc_ta->scta_hdrmodhd, false,
					&sm_hdrmod);
			SM_TEST(SM_SUCCESS == ret);
			if (SM_SUCCESS != ret)
				return(EX_OSERR);
			SM_TEST(sm_hdrmod != NULL);
			if (NULL == sm_hdrmod)
				return(EX_OSERR);
			sm_hdrmod->sm_hm_hdr = sm_cstr_scpyn0((const uchar *)hdr
							, strlen(hdr));
			sm_hdrmod->sm_hm_pos = l;
			sm_hdrmod->sm_hm_type = SM_HM_TYPE_INSERT;
			SCTA_SET_FLAG(sc_ta, SCTA_FL_HDR_SCAN);
			ret = sm_hdrmod_insert(sc_ta->scta_hdrmodhd, sm_hdrmod);
			SM_TEST(SM_SUCCESS == ret);
			if (SM_SUCCESS != ret)
				return(EX_OSERR);
			break;

		  case 'M':
			if (optarg == NULL ||
			    (optarg[0] != 'p' && optarg[0] != 'a')
			    || optarg[1] != '=')
			{
				usage(argv[0]);
				break;
			}

			sm_hdrmod = NULL;
			strlcpy(hdr, optarg + 2, sizeof(hdr));
			strlcat(hdr, "\r\n", sizeof(hdr));
			ret = sm_hdrmod_new(&sc_ta->scta_hdrmodhd, false,
					&sm_hdrmod);
			SM_TEST(SM_SUCCESS == ret);
			if (SM_SUCCESS != ret)
				return(EX_OSERR);
			SM_TEST(sm_hdrmod != NULL);
			if (NULL == sm_hdrmod)
				return(EX_OSERR);
			sm_hdrmod->sm_hm_hdr = sm_cstr_scpyn0((const uchar *)hdr
							, strlen(hdr));
			if (optarg[0] == 'p')
				sm_hdrmod->sm_hm_type = SM_HM_TYPE_PREPEND;
			else
			{
				sm_hdrmod->sm_hm_type = SM_HM_TYPE_APPEND;
				SCTA_SET_FLAG(sc_ta, SCTA_FL_HDR_SCAN);
			}
			ret = sm_hdrmod_insert(sc_ta->scta_hdrmodhd, sm_hdrmod);
			SM_TEST(SM_SUCCESS == ret);
			if (SM_SUCCESS != ret)
				return(EX_OSERR);
			break;

		  case 'r':
			l = strtoul(optarg, &str, 0);
			if (ULONG_MAX == l || l > UINT_MAX)
				usage(argv[0]);
			if (NULL == str || *str != '=')
				usage(argv[0]);

			sm_hdrmod = NULL;
			strlcpy(hdr, str + 1, sizeof(hdr));
			strlcat(hdr, "\r\n", sizeof(hdr));
			ret = sm_hdrmod_new(&sc_ta->scta_hdrmodhd, false,
					&sm_hdrmod);
			SM_TEST(SM_SUCCESS == ret);
			if (SM_SUCCESS != ret)
				return(EX_OSERR);
			SM_TEST(sm_hdrmod != NULL);
			if (NULL == sm_hdrmod)
				return(EX_OSERR);
			sm_hdrmod->sm_hm_hdr = sm_cstr_scpyn0((const uchar *)hdr
							, strlen(hdr));
			sm_hdrmod->sm_hm_pos = l;
			sm_hdrmod->sm_hm_type = SM_HM_TYPE_REPLACE;
			SCTA_SET_FLAG(sc_ta, SCTA_FL_HDR_SCAN);
			ret = sm_hdrmod_insert(sc_ta->scta_hdrmodhd, sm_hdrmod);
			SM_TEST(SM_SUCCESS == ret);
			if (SM_SUCCESS != ret)
				return(EX_OSERR);
			break;


		  default:
			usage(argv[0]);
			return(EX_USAGE);
		}
	}

	sm_test_begin(argc, argv, "message modification test 0");
	argc -= optind;
	argv += optind;

	sc_ta->scta_cdb_fp = smioin;
	sc_sess->scse_wr = sm_str_new(NULL, 256, 1024);
	SM_TEST(sc_sess->scse_wr != NULL);
	sc_sess->scse_str = sm_str_new(NULL, 256, 1024);
	SM_TEST(sc_sess->scse_str != NULL);
	total_size = 0;
	if (!HDRMODL_EMPTY(sc_ta->scta_hdrmodhd))
		ret = sc_msgmod(sc_t_ctx, &total_size);
	else
		ret = sc_msgdsn(sc_t_ctx, &total_size);
	SM_TEST(SM_SUCCESS == ret);
	sm_io_flush(smioout);
	SM_STR_FREE(sc_sess->scse_wr);
	SM_STR_FREE(sc_sess->scse_str);
	if (sc_ta != NULL)
		sm_free(sc_ta);
	return sm_test_end();
}


syntax highlighted by Code2HTML, v. 0.9.1