/*
 * 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: t-conf-1.c,v 1.23 2007/06/10 16:05:07 ca Exp $")

#if SM_LIBCONF_ALONE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "sm-conf.h"
#else /* SM_LIBCONF_ALONE */
#include "sm/string.h"
#include "sm/net.h"
#include "sm/io.h"
#include "sm/sm-conf.h"
#include "sm/sm-conf-prt.h"
#define SM_CONF_LOG_DEF 1
#include "sm/logcnfdef.h"
#include <stdio.h>
#endif /* SM_LIBCONF_ALONE */

/*
**  Just a test program to play around with libconf.
*/

typedef struct
{
	char const     *s_socket;
	uchar           s_socket_isset;
	ipv4_T          s_ipv4;
	uchar           s_ipv4_isset;
	uint32_t        s_flags;
	uchar           s_flags_isset;
	uint32_t        s_cflags;
	uchar           s_cflags_isset;
	unsigned int    s_int_u32;
	uchar           s_int_u32_isset;
	unsigned int    s_type;
	uchar           s_type_isset;
	unsigned short  s_limit;       /* between 2 and 12 */
	uchar           s_limit_isset;
	unsigned long   s_duration;
	uchar           s_duration_isset;
	uchar           s_char;
	uchar           s_char_isset;
	sm_logspec_T    s_log;
	uchar           s_log_isset;

	char            s_pi[13];
	unsigned int    s_pi_n;

	char            s_seq[5];
	unsigned int    s_seq_n;
} structure;


sm_conf_definition_T const flag_names[] =
{
{ SM_CONF_DEF_MAGIC, "Flag1",	sm_conf_type_choice_value, 0x01,
	0, NULL, 0, NULL, NULL,	NULL, "first flag"	},
{ SM_CONF_DEF_MAGIC, "Flag2",	sm_conf_type_choice_value, 0x02,
	0, NULL, 0, NULL, NULL,	NULL, "second flag"	},

/* Sentinel */
{ SM_CONF_DEF_MAGIC, NULL }
};


sm_conf_definition_T const cflag_names[] =
{
{ SM_CONF_DEF_MAGIC, "cflag1",	sm_conf_type_choice_value, 0x01,
	0, "cflag2", 0, NULL, NULL,	NULL, "first cflag"	},
{ SM_CONF_DEF_MAGIC, "cflag2",	sm_conf_type_choice_value, 0x02,
	0, "cflag2", 0, NULL, NULL,	NULL, "second cflag"	},

/* Sentinel */
{ SM_CONF_DEF_MAGIC, NULL }
};


sm_conf_definition_T const alias_options[] =
{
{ SM_CONF_DEF_MAGIC, "none",		sm_conf_type_choice_value, 0x00,
	0, NULL, 0, NULL, NULL,	NULL, "no flag"	},
{ SM_CONF_DEF_MAGIC, "localpart",	sm_conf_type_choice_value, 0x04,
	0, NULL, 0, NULL, NULL,	NULL, "apply aliases to local part"	},
{ SM_CONF_DEF_MAGIC, "localdomains",	sm_conf_type_choice_value, 0x08,
	0, NULL, 0, NULL, NULL,	NULL, "apply aliases to local domains"	},
{ SM_CONF_DEF_MAGIC, "all",		sm_conf_type_choice_value, 0x10,
	0, NULL, 0, NULL, NULL,	NULL, "apply aliases to all domains"	},

/* Sentinel */
{ SM_CONF_DEF_MAGIC, NULL }
};


sm_conf_definition_T const limit_constraints[] =
{
	{ SM_CONF_DEF_MAGIC, "", sm_conf_type_u32_minimum, 2	},
	{ SM_CONF_DEF_MAGIC, "", sm_conf_type_u32_maximum, 12	},

	/* Sentinel */
	{ SM_CONF_DEF_MAGIC, NULL }
};

sm_conf_definition_T const duration_suffixes[] =
{
{ SM_CONF_DEF_MAGIC, "s", sm_conf_type_u32_suffix, 1, 60	},
{ SM_CONF_DEF_MAGIC, "m", sm_conf_type_u32_suffix, 60, 60	},
{ SM_CONF_DEF_MAGIC, "h", sm_conf_type_u32_suffix, 60 * 60, 24	},
{ SM_CONF_DEF_MAGIC, "d", sm_conf_type_u32_suffix, 60 * 60 * 24, 7	},
{ SM_CONF_DEF_MAGIC, "w", sm_conf_type_u32_suffix, 60 * 60 * 24 * 7, 365 },
{ SM_CONF_DEF_MAGIC, "y", sm_conf_type_u32_suffix, 60 * 60 * 24 * 365 },

/* Sentinel */
{ SM_CONF_DEF_MAGIC, NULL }
};

static sm_conf_definition_T
qmgr_definitions[] = {
	{ SM_CONF_DEF_MAGIC, "type",
		sm_conf_type_u32,
		offsetof(structure, s_type),
		sizeof(unsigned int),
		NULL,
		SM_CONF_FLAG_STRICTLY_REQUIRED
	},

	{ SM_CONF_DEF_MAGIC, "value",
		sm_conf_type_u32,
		offsetof(structure, s_int_u32),
		sizeof(unsigned int),
		NULL
	},

	{ SM_CONF_DEF_MAGIC, "limit",
		sm_conf_type_u32,
		offsetof(structure, s_limit),
		sizeof(unsigned short),
		NULL,
		0,
		limit_constraints
	},

	/* A number with suffix multipliers. */
	{ SM_CONF_DEF_MAGIC, "duration",
		sm_conf_type_u32,
		offsetof(structure, s_duration),
		sizeof(unsigned long),
		"0",
		0,
		duration_suffixes
	},

{ SM_CONF_DEF_MAGIC, "log", sm_conf_type_section,
	offsetof(structure, s_log),	0,
	NULL, SM_CONF_FLAG_KEEP_DEFAULT, sm_log_spec_defs,
	NULL,	NULL,
	"Specify syslog configuration instead of logging to files"	},

	/* sentinel */
	{ SM_CONF_DEF_MAGIC, NULL }
};

#define SS_PR_CNF	"protected_recipients"
#define SS_PR_A_SENDER	"sender"
#define SS_PR_A_CLTADDR	"client_ip"
#define SS_PR_USE_LOOKUP	"use_lookup"
#define SS_PR_MATCH_DET	"implicitly_match_detail"

sm_conf_definition_T const
ss_protrcpt_allow_names[] =
{
{ SM_CONF_DEF_MAGIC,
	SS_PR_A_SENDER,			sm_conf_type_choice_value,
	0x0100,
	0, NULL, 0, NULL, NULL, NULL, NULL },

{ SM_CONF_DEF_MAGIC,
	SS_PR_A_CLTADDR,		sm_conf_type_choice_value,
	0x0200,
	0, NULL, 0, NULL, NULL, NULL, NULL },

/* Sentinel */
{ SM_CONF_DEF_MAGIC, NULL, 0, 0, 0, NULL, 0, NULL, NULL, NULL, NULL }
};


sm_conf_definition_T const
ss_protrcpt_match_names[] =
{
{ SM_CONF_DEF_MAGIC,
	"exact",		sm_conf_type_choice_value,
	0x0000,
	0, NULL, 0, NULL, NULL, NULL, NULL },

{ SM_CONF_DEF_MAGIC,
	SS_PR_USE_LOOKUP,		sm_conf_type_choice_value,
	0x0400,
	0, NULL, 0, NULL, NULL, NULL, NULL },

{ SM_CONF_DEF_MAGIC,
	SS_PR_MATCH_DET,		sm_conf_type_choice_value,
	0x0800,
	0, NULL, 0, NULL, NULL, NULL, NULL },

/* Sentinel */
{ SM_CONF_DEF_MAGIC, NULL, 0, 0, 0, NULL, 0, NULL, NULL, NULL, NULL }
};


static sm_conf_definition_T
ss_protrcpt_defs[] =
{
	{ SM_CONF_DEF_MAGIC, "allow_by", sm_conf_type_choice,
	offsetof(structure, s_flags),	sizeof(uint32_t),
	NULL, SM_CONF_FLAG_STRICTLY_REQUIRED|SM_CONF_FLAG_MULTIPLE,
	ss_protrcpt_allow_names,
	NULL,	NULL,
	"allow by"	},

	{ SM_CONF_DEF_MAGIC, "match_type", sm_conf_type_choice,
	offsetof(structure, s_flags),	sizeof(uint32_t),
	NULL, SM_CONF_FLAG_OR, ss_protrcpt_match_names, NULL,	NULL,
	"match type"	},

	/* sentinel */
	{ SM_CONF_DEF_MAGIC, NULL }
};

static sm_conf_definition_T
pi_definitions[] =
{
	/* numbers: char */
	{ SM_CONF_DEF_MAGIC, "", sm_conf_type_u32,
		offsetof(structure, s_pi), sizeof(char),  NULL, 0, NULL
	},

	/* counter: unsigned int */
	{ SM_CONF_DEF_MAGIC, "", sm_conf_type_array_n,
		offsetof(structure, s_pi_n), sizeof(unsigned int),  NULL, 0, NULL
	},

	/* sentinel */
	{ SM_CONF_DEF_MAGIC, NULL }
};


sm_conf_definition_T const seq_names[] =
{
{ SM_CONF_DEF_MAGIC, "dnsbl",	sm_conf_type_choice_value, 0x01	},
{ SM_CONF_DEF_MAGIC, "grey",	sm_conf_type_choice_value, 0x02 },
{ SM_CONF_DEF_MAGIC, "access",	sm_conf_type_choice_value, 0x04 },

/* Sentinel */
{ SM_CONF_DEF_MAGIC, NULL }
};


static sm_conf_definition_T
seq_defs[] =
{
	/* values: choise */
	{ SM_CONF_DEF_MAGIC, "", sm_conf_type_choice,
		offsetof(structure, s_seq), sizeof(char),  NULL, 0, seq_names
	},

	/* counter: unsigned int */
	{ SM_CONF_DEF_MAGIC, "", sm_conf_type_array_n,
		offsetof(structure, s_seq_n), sizeof(unsigned int),  NULL, 0, NULL
	},

	/* sentinel */
	{ SM_CONF_DEF_MAGIC, NULL }
};


sm_conf_definition_T
definitions[] =
{
	{ SM_CONF_DEF_MAGIC, "qmgr",
		sm_conf_type_section,
		0,
		sizeof(structure),
		NULL, SM_CONF_FLAG_ALLOW_ANY, qmgr_definitions
	},

	{ SM_CONF_DEF_MAGIC, "socket", sm_conf_type_string,
		offsetof(structure, s_socket), 0,
		"path/to/socket",
		0,
		NULL, NULL, NULL,
		"socket path"
#if SM_LIBCONF_ISSET
		, offsetof(structure, s_socket_isset)
#endif
	},

	{ SM_CONF_DEF_MAGIC, "delim",
		sm_conf_type_char,
		offsetof(structure, s_char),
		0,
		"+",
		0,
		NULL, NULL, NULL,
		"delimiter"
#if SM_LIBCONF_ISSET
		, offsetof(structure, s_char_isset)
#endif
	},

	{ SM_CONF_DEF_MAGIC, "nameserver",
		sm_conf_type_ipv4,
		offsetof(structure, s_ipv4),
		sizeof(ipv4_T),
		"127.0.0.1",
		0,
		NULL, NULL, NULL,
		"nameserver IPv4"
#if SM_LIBCONF_ISSET
		, offsetof(structure, s_ipv4_isset)
#endif
	},

	{ SM_CONF_DEF_MAGIC, "flags",
		sm_conf_type_choice,
		offsetof(structure, s_flags),
		sizeof(uint32_t),
		NULL,
		SM_CONF_FLAG_MULTIPLE|SM_CONF_FLAG_KEEP_DEFAULT,
		flag_names,
		NULL, NULL,
		"flags what else"
#if SM_LIBCONF_ISSET
		, offsetof(structure, s_flags_isset)
#endif
	},

	{ SM_CONF_DEF_MAGIC, "cflags",
		sm_conf_type_choice,
		offsetof(structure, s_cflags),
		sizeof(uint32_t),
		"cflag2",
		SM_CONF_FLAG_MULTIPLE,
		cflag_names
	},

	{ SM_CONF_DEF_MAGIC, "sequence",
		sm_conf_type_array,
		0,
		4,
		NULL,
		SM_CONF_FLAG_FLAT,
		seq_defs
	},

	{ SM_CONF_DEF_MAGIC, "pi",
		sm_conf_type_array,
		0,	/* offset (n/a; see contents)	*/
		12,	/* size: maximum number of elements */
		NULL,
		SM_CONF_FLAG_FLAT,
		pi_definitions,
		NULL,  NULL, "digits of pi"
	},

	{ SM_CONF_DEF_MAGIC, "aliases",
		sm_conf_type_choice,
		offsetof(structure, s_flags),
		sizeof(uint32_t),
		NULL,
		SM_CONF_FLAG_MULTIPLE|SM_CONF_FLAG_KEEP_DEFAULT,
		alias_options
	},

{ SM_CONF_DEF_MAGIC, SS_PR_CNF,		sm_conf_type_section,
	0,	sizeof(uint32_t),
	NULL, SM_CONF_FLAG_KEEP_DEFAULT,	ss_protrcpt_defs,
	NULL,	NULL,
	"how to protect recipient addresses"	},

	/* sentinel */
	{ SM_CONF_DEF_MAGIC, NULL }
};

static structure s;

static void
print_structure(structure *st)
{
	sm_ret_T ret;
	sm_str_P ipv4s;
	unsigned int i;

	ipv4s = sm_str_new(NULL, 20, 32);
	if (ipv4s == NULL)
	{
		printf("out of memory\n");
		return;
	}
	printf("int_u32: %u\n",		st->s_int_u32);
	printf("limit: %hu\n",		st->s_limit);
	printf("duration: %lu\n",	st->s_duration);
	printf("socket: \"%s\"\n",	st->s_socket);
	printf("flags: %X\n",		st->s_flags);
	printf("cflags: %X\n",		st->s_cflags);
	printf("char: %c\n",		st->s_char);
	ret = sm_inet_ipv4str(st->s_ipv4, ipv4s);
	if (ret != SM_SUCCESS)
	{
		printf("sm_inet_ipv4str=0x%x\n", ret);
		return;
	}
	printf("nameserver: %s\n",	sm_str_getdata(ipv4s));

	printf("pi: [%lu]", (unsigned long)st->s_pi_n);
	for (i = 0; i < st->s_pi_n; i++)
		printf(" %d", (int)st->s_pi[i]);
	putchar('\n');

	printf("seq: [%lu]", (unsigned long)st->s_seq_n);
	for (i = 0; i < st->s_seq_n; i++)
		printf(" %d", (int)st->s_seq[i]);
	putchar('\n');

	printf("int_u32: %c\n",		st->s_int_u32_isset + '0');
	printf("limit: %c\n",		st->s_limit_isset + '0');
	printf("duration: %c\n",	st->s_duration_isset + '0');
	printf("socket: %c\n",		st->s_socket_isset + '0');
	printf("flags: %c\n",		st->s_flags_isset + '0');
	printf("cflags: %c\n",		st->s_cflags_isset + '0');
	printf("char: %c\n",		st->s_char_isset + '0');
	printf("ns: %c\n",		st->s_ipv4_isset + '0');
}

static int
process(char const *confname, FILE *fp, bool show, bool prt, const char *option, const char *opt)
{
	sm_conf_T *smc;
	int err;
	char const *name, *kw;
	size_t name_n, kw_n;
#if 0
	sm_conf_iterator_T service_iter;
#endif /* 0 */
	sm_conf_node_T *node, *root;
	char buf[SM_CONF_ERROR_BUFFER_SIZE];
	char const *e = NULL;

	if (((smc = sm_conf_new(confname ? confname : "*stdin*"))) == NULL)
	{
		fprintf(stderr, "error -- sm_conf_new() returns NULL!\n");
		return 1;
	}
	if ((err = sm_conf_read_FILE(smc, confname, fp)) != 0)
	{
		fprintf(stderr, "%s: %s\n",
			confname ? confname : "*stdin*",
			sm_conf_strerror(err, buf, sizeof buf));

		while ((e = sm_conf_syntax_error(smc, e)) != NULL)
			fprintf(stderr, "%s\n", e);

		sm_conf_destroy(smc);
		return 2;
	}

	/* s.s_flags = 0x7; */
	s.s_cflags = 0x3;
	err = sm_conf_scan(smc, definitions,
		SM_CONF_FLAG_ALLOW_ANY|SM_CONF_FLAG_KEEP_DEFAULT, &s);
	if (err != 0)
	{
#if 0
		fprintf(stderr, "%s: %d\n", confname ? confname : "*stdin*", err);
#endif /* 0 */
		fprintf(stderr, "(while scanning) %s: %s\n",
			confname ? confname : "*stdin*",
			sm_conf_strerror(err, buf, sizeof buf));

		while ((e = sm_conf_syntax_error(smc, e)) != NULL)
			fprintf(stderr, "%s\n", e);

		sm_conf_destroy(smc);
		return 3;
	}

	if (show)
		print_structure(&s);

	if (prt)
	{
		sm_str_P text;

		text = sm_str_new(NULL, 256, 8192);
		sm_io_fprintf(smioout, "prt_conf1:\n");
		(void) sm_conf_prt_cnfs(definitions, &s, 0, smioout, 0, NULL,
				text);
		sm_io_flush(smioout);
	}
#if 0
	service_iter = NULL;
	while ((err = sm_conf_scan_next(smc, "",
				definitions, 0,
				&service_name, &service_name_n,
				&s, &service_iter)) == 0)
	{
		node = (sm_conf_node_T *) service_iter;
		err = sm_conf_section_keyword(smc, node, &kw, &kw_n);
		if (kw == NULL)
			printf("kw=(none)\n");
		else
			printf("kw=%*s\n", (int) kw_n, kw);
		if (service_name == NULL)
			printf("name=(none)\n");
		else
			printf("name=%*s\n", (int) service_name_n,
				service_name);
	}
#endif /* 0 */

	root = sm_conf_root(smc);
	node = NULL;
	while ((node = sm_conf_section_next_subsection(smc, root,
			NULL, 0,
			NULL, 0,
			node)) != NULL)
	{
		err = sm_conf_section_keyword(smc, node, &kw, &kw_n);
		if (kw == NULL)
			printf("kw=(none)\n");
		else
			printf("kw=%*s\n", (int) kw_n, kw);

		err = sm_conf_section_name(smc, node, &name, &name_n);
		if (name == NULL)
			printf("name=(none)\n");
		else
			printf("name=%*s\n", (int) name_n, name);
		printf("type=%d\n", sm_conf_node_type(smc, node));
		err = sm_conf_get_relative(smc, node, NULL,
				sm_conf_type_section, qmgr_definitions,
				SM_CONF_FLAG_ALLOW_ANY, &s, sizeof(s));
		if (err != 0)
		{
			fprintf(stderr, "%s: %#x\n",
				confname ? confname : "*stdin*", err);

#if 0
			fprintf(stderr, "%s: %s\n",
				confname ? confname : "*stdin*",
				sm_conf_strerror(err, buf, sizeof buf));

			while ((e = sm_conf_syntax_error(smc, e)) != NULL)
				fprintf(stderr, "%s\n", e);
#endif /* 0 */
		}
	}

	if (option != NULL && *option != '\0')
	{
		uint v;

		err = sm_conf_get(smc, option, sm_conf_type_u32, NULL, 0,
			(void *) &v, sizeof(v));
		fprintf(stdout, "option=%s, conf_get=%d, text=%s, val=%u\n",
			option != NULL ? option : "(null)", err,
			err == 0 ? "OK" : sm_conf_strerror(err, buf,
							sizeof buf),
			v);
	}

	if (opt != NULL && *opt != '\0')
	{
		char *v;

		err = sm_conf_get(smc, opt, sm_conf_type_string, NULL, 0,
			(void *) &v, 0);
		fprintf(stdout, "opt=%s, conf_get=%d, text=%s, val=\"%s\"\n",
			opt != NULL ? opt : "(null)", err,
			err == 0 ? "OK" : sm_conf_strerror(err, buf,
							sizeof buf),
			err == 0 ? v : "(some error)");
	}


#if 0
	sm_conf_destroy(smc);
#endif /* 0 */

	return 0;
}

int
main(int ac, char **av)
{
	int ai, c, ret;
	bool prt, done;
	char *option, *opt;

	done = false;
	prt = false;
	option = NULL;
	opt = NULL;
	while ((c = getopt(ac, av, "df:l:O:o:sv:")) != -1)
	{
		switch (c)
		{
		  case 'd':
			sm_conf_prt_dflt(definitions, SMC_FLD_FLAGS, smioout);
			sm_io_flush(smioout);
			return 0;
		  case 'f':
			ret = process(optarg, NULL, 0, prt, option, opt);
			if (ret != 0)
				return ret;
			done = true;
			break;
		  case 'l':
			s.s_limit = atoi(optarg);
			break;
		  case 'O':
			opt = strdup(optarg);
			break;
		  case 'o':
			option = strdup(optarg);
			break;
		  case 's':
			prt = true;
			break;
		  case 'v':
			s.s_int_u32 = atoi(optarg);
			break;
		}
	}
	ac -= optind;
	av += optind;
	if (done)
	{
		print_structure(&s);
		if (prt)
		{
			sm_io_fprintf(smioout, "prt_conf:\n");
			(void) sm_conf_prt_cnfs(definitions, &s, 0,
					smioout, 0, NULL, NULL);
			sm_io_flush(smioout);
		}
		return 0;
	}

	if (ac == 0)
		return process("*stdin*", stdin, 1, prt, option, opt);

	for (ai = 0; ai < ac; ai++)
	{
		int ret;

		ret = process(av[ai], NULL, 1, prt, option, opt);
		if (ret != 0)
			return ret;
	}
	return 0;
}


syntax highlighted by Code2HTML, v. 0.9.1