/*
 * 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-conf-0.c,v 1.19 2006/12/24 00:20:24 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/sm-conf.h"
#include <stdio.h>
#endif /* SM_LIBCONF_ALONE */

/*
**  Just a test program to play around with libconf.
**  However, this is used in t-conf-0.sh, so don't break it.
*/

#define SM_SSPEC_MAGIC	SM_MAGIC('S', 'S', 'P', 'E')
#define SM_SUB_MAGIC	SM_MAGIC('S', 'S', 'U', 'B')
#define SM_SS_MAGIC	SM_MAGIC('S', 'S', '_', '_')
#define SM_S_MAGIC	SM_MAGIC('S', '_', '_', '_')

typedef struct
{
	sm_magic_T      sm_magic;
	int             sspec_type;
	short           sspec_port;
	ipv4_T          sspec_addr;
	char           *sspec_host;
} sockspec;

typedef struct
{
	sm_magic_T      sm_magic;
	unsigned int    sub_int_u32;
	unsigned short  sub_limit;     /* between 2 and 12 */
	unsigned long   sub_duration;
	sockspec       *sub_bind;
	unsigned int    sub_sockets_n;
} sub;

typedef struct
{
	sm_magic_T      sm_magic;
	unsigned short  ss_port;
	char           *ss_name;
} ss;

typedef struct
{
	sm_magic_T      sm_magic;
	char const     *s_socket;
	ipv4_T          s_ipv4;
	uint32_t        s_flags;
	uint32_t        s_int;
	sub             s_sub;
	ss              s_ss;
} structure;

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

/* 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		},
	{ SM_CONF_DEF_MAGIC, "m", sm_conf_type_u32_suffix, 60		},
	{ SM_CONF_DEF_MAGIC, "h", sm_conf_type_u32_suffix, 60 * 60	},
	{ SM_CONF_DEF_MAGIC, "d", sm_conf_type_u32_suffix, 60 * 60 * 24	},
	{ SM_CONF_DEF_MAGIC, "w", sm_conf_type_u32_suffix, 60 * 60 * 24 * 7 },
	{ SM_CONF_DEF_MAGIC, "y", sm_conf_type_u32_suffix, 60 * 60 * 24 * 365 },

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

#define SM_SOCK_INET	1
#define SM_SOCK_UNIX	2
#define SM_SOCK_INET6	4

sm_conf_definition_T const sock_types[] =
{
{ SM_CONF_DEF_MAGIC, "inet",	sm_conf_type_choice_value, SM_SOCK_INET},
{ SM_CONF_DEF_MAGIC, "unix",	sm_conf_type_choice_value, SM_SOCK_UNIX},
{ SM_CONF_DEF_MAGIC, "inet6",	sm_conf_type_choice_value, SM_SOCK_INET6},

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

static sm_conf_definition_T sock_spec_defs[] =
{
	{ SM_CONF_DEF_MAGIC, "type", sm_conf_type_choice,
		offsetof(sockspec, sspec_type),
		sizeof(int),
		"inet", SM_CONF_FLAG_KEEP_DEFAULT, sock_types
	},

	{ SM_CONF_DEF_MAGIC, "port", sm_conf_type_u32,
		offsetof(sockspec, sspec_port),
		sizeof(short),
		"1234", SM_CONF_FLAG_KEEP_DEFAULT
	},

	{ SM_CONF_DEF_MAGIC, "address", sm_conf_type_ipv4,
		offsetof(sockspec, sspec_addr),
		sizeof(ipv4_T),
		"127.0.0.1", SM_CONF_FLAG_KEEP_DEFAULT
	},

	{ SM_CONF_DEF_MAGIC, "host", sm_conf_type_string,
		offsetof(sockspec, sspec_host),
		0,
		NULL, 0
	},

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

static sm_conf_definition_T
sockets_defs[] =
{
	{ SM_CONF_DEF_MAGIC, "listen",
		sm_conf_type_section,
		offsetof(sub, sub_bind),
		sizeof(sockspec),
		NULL,
		SM_CONF_FLAG_MULTIPLE|SM_CONF_FLAG_KEEP_DEFAULT,
		sock_spec_defs
	},

	/* counter: unsigned int */
	{ SM_CONF_DEF_MAGIC, "", sm_conf_type_array_n,
		offsetof(sub, sub_sockets_n),
		sizeof(unsigned int)
	},

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

static sm_conf_definition_T subdefinitions[] =
{
	{ SM_CONF_DEF_MAGIC, "value",
		sm_conf_type_u32,
		offsetof(sub, sub_int_u32),
		sizeof(unsigned int),
		NULL,
		0, NULL, NULL, NULL,
		"value"
#if SM_LIBCONF_ISSET
		, 0
#endif
#if SM_LIBCONF_MAGIC
		, SM_SUB_MAGIC
#endif
	},

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

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

	{ SM_CONF_DEF_MAGIC, "listen",
		sm_conf_type_array,
		0,	/* offset (n/a; see contents)	    */
		10,	/* size: maximum number of elements */
		NULL,
		SM_CONF_FLAG_MULTIPLE|SM_CONF_FLAG_KEEP_DEFAULT,
		sockets_defs,
		NULL,  NULL, "listen socket(s)"
	},

#if 0
	{ SM_CONF_DEF_MAGIC, "listen",
		sm_conf_type_section,
		offsetof(sub, sub_bind),
		sizeof(sockspec),
		NULL,
		SM_CONF_FLAG_MULTIPLE|SM_CONF_FLAG_KEEP_DEFAULT,
		sock_spec_defs
	},
#endif /* 0 */

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

static sm_conf_definition_T smtps_defs[] =
{
	{ SM_CONF_DEF_MAGIC, "port",
		sm_conf_type_u32,
		offsetof(ss, ss_port),
		sizeof(unsigned short),
		"25",
		0, NULL, NULL, NULL,
		"port"
#if SM_LIBCONF_ISSET
		, 0
#endif
#if SM_LIBCONF_MAGIC
		, SM_SS_MAGIC
#endif
	},

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


sm_conf_definition_T definitions[] =
{
	{ SM_CONF_DEF_MAGIC, "socket",
		sm_conf_type_string,
		offsetof(structure, s_socket),
		0,
		"path/to/socket",
		0, NULL, NULL, NULL,
		"socket"
#if SM_LIBCONF_ISSET
		, 0
#endif
#if SM_LIBCONF_MAGIC
		, SM_S_MAGIC
#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"
#if SM_LIBCONF_ISSET
		, 0
#endif
#if SM_LIBCONF_MAGIC
		, SM_S_MAGIC
#endif
	},

	{ SM_CONF_DEF_MAGIC, "flags",
		sm_conf_type_choice,
		offsetof(structure, s_flags),
		sizeof(uint32_t),
		NULL,
		SM_CONF_FLAG_MULTIPLE,
		flag_names,
		NULL, NULL,
		"flags"
#if SM_LIBCONF_ISSET
		, 0
#endif
#if SM_LIBCONF_MAGIC
		, SM_S_MAGIC
#endif
	},

	{ SM_CONF_DEF_MAGIC, "int",
		sm_conf_type_u32,
		offsetof(structure, s_int),
		sizeof(unsigned int),
		NULL,
		0, NULL, NULL, NULL,
		"int"
#if SM_LIBCONF_ISSET
		, 0
#endif
#if SM_LIBCONF_MAGIC
		, SM_S_MAGIC
#endif
	},

	{ SM_CONF_DEF_MAGIC, "qmgr",
		sm_conf_type_section,
		offsetof(structure, s_sub),
		sizeof(sub),
		NULL, SM_CONF_FLAG_KEEP_DEFAULT, subdefinitions,
		NULL, NULL,
		"qmgr"
#if SM_LIBCONF_ISSET
		, 0
#endif
#if SM_LIBCONF_MAGIC
		, SM_S_MAGIC
#endif
	},

	{ SM_CONF_DEF_MAGIC, "smtps",
		sm_conf_type_section,
		offsetof(structure, s_ss),
		sizeof(ss),
		NULL,
		SM_CONF_FLAG_MULTIPLE|SM_CONF_FLAG_PARSE_ONLY,
		smtps_defs,
		NULL, NULL,
		"smtp server"
#if SM_LIBCONF_ISSET
		, 0
#endif
#if SM_LIBCONF_MAGIC
		, SM_S_MAGIC
#endif
	},

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

static structure st;

static void
print_ipv4(ipv4_T ipv4, const char *name)
{
	sm_ret_T ret;
	sm_str_P ipv4s;

	ipv4s = sm_str_new(NULL, 20, 32);
	if (ipv4s == NULL)
	{
		printf("out of memory\n");
		return;
	}
	ret = sm_inet_ipv4str(ipv4, ipv4s);
	if (ret != SM_SUCCESS)
	{
		printf("sm_inet_ipv4str=0x%x\n", ret);
		return;
	}
	printf("%s= %s\n", name, sm_str_getdata(ipv4s));
}

static void
print_sock_spec(sockspec *ss, char *sockspecname, const char *name, int l)
{
	printf("\nsockspec= %s\n", sockspecname);
	printf("name= %s\n", name == NULL ? "(none)" : name);
	printf("type= %u\n", ss->sspec_type);
	if (ss->sspec_type == SM_SOCK_INET)
	{
		printf("port= %hu\n", ss->sspec_port);
		printf("host= \"%s\"\n", (ss->sspec_host == NULL) ? "(NULL)"
					: ss->sspec_host);
		print_ipv4(ss->sspec_addr, "address");
	}
	else if (ss->sspec_type == SM_SOCK_UNIX)
	{
		printf("path= \"%s\"\n", (ss->sspec_host == NULL) ? "(NULL)"
					: ss->sspec_host);
	}
	else
	{
		printf("type=unknown\n");
	}
	printf("\n");
}

static void
print_structure(structure *strct)
{
	unsigned int u;
	char *name;

	printf("value= %u\n",		strct->s_sub.sub_int_u32);
	printf("limit= %hu\n",		strct->s_sub.sub_limit);
	printf("duration= %lu\n",	strct->s_sub.sub_duration);
	printf("socket= \"%s\"\n",	strct->s_socket);
	printf("flags= %X\n",		strct->s_flags);
	print_ipv4(strct->s_ipv4, "nameserver");
	for (u = 0; u < strct->s_sub.sub_sockets_n; u++)
	  print_sock_spec(&(strct->s_sub.sub_bind[u]), "struct", NULL, 0);
	name = strct->s_ss.ss_name;
#if 0
	printf("smtps= %s\n",		name == NULL ? "(NoName)" : name);
	printf("smtps.port= %d\n",	strct->s_ss.ss_port);
#endif /* 0 */
}

static int
process(char const *name, FILE *fp, int show, const char *opt, const char *ssname, int flags)
{
	sm_conf_T		*stream;
	int			err;
	unsigned int	v;
	char const *service_name;
	size_t service_name_n;
	sm_conf_iterator_T service_iter;
	sockspec sock_spec;
	char buf[SM_CONF_ERROR_BUFFER_SIZE];
	char const *e = NULL;

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

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

		sm_conf_destroy(stream);
		return 2;
	}

	st.sm_magic = SM_S_MAGIC;
	st.s_sub.sm_magic = SM_SUB_MAGIC;
	st.s_ss.sm_magic = SM_SS_MAGIC;
	st.s_flags = flags;
	if ((err = sm_conf_scan(stream, definitions, 0, &st)) != 0)
	{

		fprintf(stderr, "(while scanning) %s: %s\n",
			name ? name : "*stdin*",
			sm_conf_strerror(err, buf, sizeof buf));

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

		sm_conf_destroy(stream);
		return 3;
	}

	if (show)
		print_structure(&st);

	if (opt != NULL && *opt != '\0')
	{
		err = sm_conf_get(stream, opt,
			sm_conf_type_u32, NULL,
			0,
			(void *) &v, sizeof(v));
		fprintf(stdout, "opt=%s, conf_get=%d, text=%s\n",
			opt != NULL ? opt : "(null)", err,
			err == 0 ? "OK" : sm_conf_strerror(err, buf,
							sizeof buf));
	}

	service_iter = NULL;
	while ((err = sm_conf_scan_next(stream, "qmgr.listen",
				sock_spec_defs, 0,
				&service_name, &service_name_n,
				&sock_spec, &service_iter)) == 0)
	{
		print_sock_spec(&sock_spec, "loop"
				, service_name, service_name_n);
	}

	service_iter = NULL;
	while ((err = sm_conf_scan_next(stream, "smtps",
				smtps_defs, 0,
				&service_name, &service_name_n,
				&(st.s_ss), &service_iter)) == 0)
	{
		if (ssname == NULL || (
		    strlen(ssname) == service_name_n &&
		    strncmp(ssname, service_name,  service_name_n) == 0))
		{
			const char *n;
#if 0
			print_structure(&st);
#endif /* 0 */
			n = service_name;
			printf("smtps= %s\n", n == NULL ? "(NoName)" : n);
			printf("smtps.port= %hu\n", st.s_ss.ss_port);
		}
	}

	if (ssname != NULL)
	{
		service_iter = NULL;
		snprintf(buf, sizeof(buf), "smtps{%s}", ssname);
		while ((err = sm_conf_scan_next(stream, buf, smtps_defs, 0,
					&service_name, &service_name_n,
					&(st.s_ss), &service_iter)) == 0)
		{
			{
				const char *n;

				n = service_name;
				printf("smtps{%s}= %s\n", ssname,
						n == NULL ? "(NoName)" : n);
				printf("smtps.port= %hu\n", st.s_ss.ss_port);
			}
		}
	}



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

	return 0;
}

int
main(int ac, char **av)
{
	int ai, c, ret, done, flags;
	char *opt, *ssname;

	done = 0;
	opt = "qmgr.limit";
	ssname = NULL;
	flags = 0;
	while ((c = getopt(ac, av, "F:f:l:N:o:v:")) != -1)
	{
		switch (c)
		{
		  case 'F':
			flags = strtol(optarg, NULL, 0);
			break;
		  case 'f':
			ret = process(optarg, NULL, 0, opt, ssname, flags);
			if (ret != 0)
				return ret;
			done = 1;
			break;
		  case 'l':
			st.s_sub.sub_limit = atoi(optarg);
			break;
		  case 'N':
			ssname = strdup(optarg);
			if (ssname == NULL)
				return ENOMEM;
			break;
		  case 'o':
			opt = strdup(optarg);
			if (opt == NULL)
				return ENOMEM;
			break;
		  case 'v':
			st.s_sub.sub_int_u32 = atoi(optarg);
			break;
		}
	}
	ac -= optind;
	av += optind;
	if (done)
	{
		print_structure(&st);
		return 0;
	}

	if (ac == 0)
		return process("*stdin*", stdin, 1, opt, ssname, flags);

	for (ai = 0; ai < ac; ai++)
	{
		int ret = process(av[ai], NULL, 1, opt, ssname, flags);
		if (ret != 0)
			return ret;
	}
	return 0;
}


syntax highlighted by Code2HTML, v. 0.9.1