/*
 * Copyright (c) 2003-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-mapaddr-0.c,v 1.21 2006/10/05 04:27:38 ca Exp $")

#include "sm/error.h"
#include "sm/heap.h"
#include "sm/memops.h"
#include "sm/sysexits.h"
#include "sm/string.h"
#include "sm/maps.h"
#include "sm/mapc.h"
#include "sm/map.h"
#include "sm/mapclasses.h"
#include "sm/bdb.h"
#include "sm/cdb_map.h"
#include "sm/test.h"
#include "sm/io.h"

/*
**  Test program for sm_map_lookup_addr()
**  Input: lines on stdin, each line has 4 fields separated by ':'
**  tag:user:detail:domain
**  If an element should be NULL replace it by 'N'.
*/

int Verbose = 0;

#define BUFLEN	512
#define MAXLEN	1024

#define MAPC_TYPE	"hash"
#define MAPC_NAME	"bdb3"
#define MAPC_FILE	"./bdb3.db"

#define MAPC_STYPE	"strmap"
#define MAPC_SNAME	"str"
#define MAPC_SFILE	"none"

/* put into libcheck? */
static int
stripcr(char *buf)
{
	int n;

	n = strlen(buf);
	while (n > 0 && buf[n - 1] == '\n')
	{
	    buf[n - 1] = '\0';
	   --n;
	}
	return n;
}

static void
usage(const char *prg)
{
	sm_io_fprintf(smioerr,
		"%s: usage: %s [options]\n"
		"test address lookup in %s map %s\n"
		"Input: lines on stdin, each line has 4 fields separated by ':'\n"
		"tag:user:detail:domain\n"
		"If an element should be NULL replace it by 'N'.\n"
		"options:\n"
		"  -a        alias lookup type\n"
		"  -d delim  set delimiter\n"
		"  -F file   name of db file [%s]\n"
		"  -f flags  set lookup flags\n"
		"  -m        set replace ${macro} flag\n"
		"  -n name   name of map [%s]\n"
		"  -r        set replace %%n flag\n"
		"  -s key    use a str map with key\n"
		"  -T type   map type [%s]\n"
		"  -v        virtuser lookup\n"
		, prg, prg
		, MAPC_TYPE
		, MAPC_FILE
		, MAPC_FILE
		, MAPC_NAME
		, MAPC_TYPE
		);
	exit(EX_USAGE);
}

static sm_ret_T
init_mapc_test(sm_maps_P maps)
{
	sm_ret_T ret;

	SM_REQUIRE(maps != NULL);
	ret = sm_bdb_class_create(maps);
	SM_TEST(sm_is_success(ret));
#if MTA_USE_TINYCDB
	ret = sm_cdb_class_create(maps);
	SM_TEST(sm_is_success(ret));
#endif
	return ret;
}

static sm_ret_T
db_map_open(sm_maps_P maps, sm_map_P *pmap, const char *mapname, const char *mapfile, const char *maptype)
{
	sm_ret_T ret;
	sm_map_P map;
	sm_cstr_P mtype, mname;

	mtype = mname = NULL;
	ret = SM_SUCCESS;
	mtype = sm_cstr_scpyn0((const uchar *)maptype, strlen(maptype));
	SM_TEST(mtype != NULL);
	if (mtype == NULL)
		goto error;

	mname = sm_cstr_scpyn0((const uchar *)mapname, strlen(mapname));
	SM_TEST(mname != NULL);
	if (mname == NULL)
		goto error;

	init_mapc_test(maps);

	map = NULL;
	ret = sm_map_open(maps, mname, mtype, 0, mapfile, SMAP_MODE_RDONLY,
			&map, SMPO_END);
	SM_CSTR_FREE(mtype);
	SM_CSTR_FREE(mname);
	SM_TEST(sm_is_success(ret));
	*pmap = map;
	return ret;

  error:
	SM_CSTR_FREE(mtype);
	SM_CSTR_FREE(mname);
	if (sm_is_success(ret))
		ret = ENOMEM;
	return ret;
}

static sm_ret_T
str_map_open(sm_str_P strmap, sm_maps_P maps, sm_map_P *pmap)
{
	sm_ret_T ret;
	sm_map_P map;
	sm_cstr_P mtype, mname;

	mtype = mname = NULL;
	ret = SM_SUCCESS;
	mtype = sm_cstr_scpyn0((const uchar *)MAPC_STYPE, strlen(MAPC_STYPE));
	SM_TEST(mtype != NULL);
	if (mtype == NULL)
		goto error;

	mname = sm_cstr_scpyn0((const uchar *)MAPC_SNAME, strlen(MAPC_SNAME));
	SM_TEST(mname != NULL);
	if (mname == NULL)
		goto error;

	ret = sm_strmap_class_create(maps);
	SM_TEST(sm_is_success(ret));

	map = NULL;
	ret = sm_map_open(maps, mname, mtype, 0, MAPC_SFILE, SMAP_MODE_RDONLY,
			&map, SMPO_STR, strmap, SMPO_END);
	SM_CSTR_FREE(mtype);
	SM_CSTR_FREE(mname);
	SM_TEST(sm_is_success(ret));
	*pmap = map;
	return ret;

  error:
	SM_CSTR_FREE(mtype);
	SM_CSTR_FREE(mname);
	if (sm_is_success(ret))
		ret = ENOMEM;
	return ret;
}

int
main(int argc, char *argv[])
{
	int n, c, list_mapc;
	sm_ret_T ret;
	uint32_t flags;
	sm_maps_P maps;
	sm_map_P map;
	sm_str_P user, detail, domain, tag, rhs, strmap;
	char *buf, *ap, *s, *sep;
	uchar delim;
	char *mapname, *mapfile, *maptype, *prg;

	prg = argv[0];
	mapname = MAPC_NAME;
	mapfile = MAPC_FILE;
	maptype = MAPC_TYPE;
	sep = ":";
	delim = (uchar) '+';
	buf = NULL;
	flags = SMMAP_LFL_ALL;
	strmap = NULL;
	list_mapc = -1;

	while ((c = getopt(argc, argv, "ad:F:f:l:mn:rs:T:vV")) != -1)
	{
		switch (c)
		{
		  case 'a':
			flags = SMMAP_LFL_ALIAS;
			break;
		  case 'd':
			delim = (uchar) optarg[0];
			break;
		  case 'F':
			SM_STRDUP_OPT(mapfile, optarg);
			break;
		  case 'f':
			flags = (uint32_t) strtoul(optarg, NULL, 0);
			break;
		  case 'l':
			list_mapc = (int) strtol(optarg, NULL, 0);
			break;
		  case 'm':
			flags |= SMMAP_LFL_MCR_REPL;
			break;
		  case 'n':
			SM_STRDUP_OPT(mapname, optarg);
			break;
		  case 'r':
			flags |= SMMAP_LFL_REPL;
			break;
		  case 's':
			strmap = sm_str_scpy(NULL, optarg, 256);
			break;
		  case 'T':
			SM_STRDUP_OPT(maptype, optarg);
			break;
		  case 'v':
			flags = SMMAP_LFL_VIRTUSER;
			break;
		  case 'V':
			++Verbose;
			break;
		  default:
			usage(prg);
			exit(EX_USAGE);
		}
	}

	sm_test_begin(argc, argv, "test map address");

	if (list_mapc >= 0)
	{
		sm_ret_T ret;
		sm_maps_P maps;

		maps = NULL;
		ret = sm_maps_init(&maps);
		if (NULL == maps)
			exit(EX_OSERR);
		ret = init_mapc_test(maps);
		if (sm_is_err(ret))
			exit(EX_OSERR);
		c = sm_mapc_list(smioout, maps, list_mapc, 0);
		return 0;
	}

	maps = NULL;
	rhs = NULL;
	user = NULL;
	detail = NULL;
	domain = NULL;
	tag = NULL;

	buf = sm_malloc(BUFLEN);
	SM_TEST(buf != NULL);
	if (buf == NULL)
		goto error;

	ret = sm_maps_init(&maps);
	SM_TEST(maps != NULL);
	if (maps == NULL)
		goto error;
	SM_TEST(sm_is_success(ret));

	if (strmap != NULL)
		ret = str_map_open(strmap, maps, &map);
	else
		ret = db_map_open(maps, &map, mapname, mapfile, maptype);
	SM_TEST(sm_is_success(ret));
	if (sm_is_err(ret))
		goto error;

	user = sm_str_new(NULL, 64, 1024);
	SM_TEST(user != NULL);
	if (user == NULL)
		goto error;

	detail = sm_str_new(NULL, 64, 1024);
	SM_TEST(detail != NULL);
	if (detail == NULL)
		goto error;

	domain = sm_str_new(NULL, 256, 1024);
	SM_TEST(domain != NULL);
	if (domain == NULL)
		goto error;

	tag = sm_str_new(NULL, 16, 1024);
	SM_TEST(tag != NULL);
	if (tag == NULL)
		goto error;

	rhs = sm_str_new(NULL, 256, 1024);
	SM_TEST(rhs != NULL);
	if (rhs == NULL)
		goto error;


	/* perform some operations ... */

	while (fgets(buf, BUFLEN, stdin))
	{
		n = getc(stdin);
		ungetc(n, stdin);
		if (n == ' ' || n == '\t')
		{
			n = strlen(buf);
			fgets(buf + n, BUFLEN - n, stdin);
		}
		n = stripcr(buf);
		if (n <= 0)
			goto error; /* bogus input, don't try to recover */

		sm_str_clr(user);
		sm_str_clr(detail);
		sm_str_clr(domain);
		sm_str_clr(tag);

		s = buf;

#define GET_PART(string)	\
	do {					\
		ap = strsep(&s, sep);		\
		SM_TEST(ap != NULL);		\
		if (ap == NULL)			\
			break;			\
		ret = sm_str_scat(string, ap);	\
		SM_TEST(ret == SM_SUCCESS);	\
		if (ret != SM_SUCCESS)		\
			break;			\
	} while (0)

#define ISNULL(str)	\
	(sm_str_getlen(str) == 1 && sm_str_rd_elem(str, 0) == 'N')
#define MAYBENULL(str)	(ISNULL(str) ? NULL : (str))

		GET_PART(tag);
		if (!ISNULL(tag))
		{
			ret = sm_str_put(tag, (uchar) ':');
			SM_TEST(ret == SM_SUCCESS);
			if (ret != SM_SUCCESS)
				break;
		}
		GET_PART(user);
		GET_PART(detail);
		GET_PART(domain);

		if (Verbose > 1)
		{
			sm_io_fprintf(smioerr, "tag='%S' ", tag);
			sm_io_fprintf(smioerr, "user='%S' ", user);
			sm_io_fprintf(smioerr, "detail='%S' ", detail);
			sm_io_fprintf(smioerr, "domain='%S'\n", domain);
		}

		sm_str_clr(rhs);

		ret = sm_map_lookup_addr(map,
			MAYBENULL(user),
			MAYBENULL(detail),
			MAYBENULL(domain),
			MAYBENULL(tag),
			delim, flags, rhs);

		sm_io_fprintf(smioerr, "ret=%x, rhs='%S'\n", ret, rhs);

	}

	ret = sm_map_close(map, 0);
	SM_TEST(sm_is_success(ret));

	ret = sm_maps_term(maps);
	SM_TEST(sm_is_success(ret));
	maps = NULL;
	/* fallthrough for cleanup */

  error:
	if (maps != NULL)
		sm_maps_term(maps);
	SM_STR_FREE(user);
	SM_STR_FREE(detail);
	SM_STR_FREE(domain);
	SM_STR_FREE(tag);
	SM_STR_FREE(rhs);
	SM_FREE(buf);
	return sm_test_end();
}


syntax highlighted by Code2HTML, v. 0.9.1