/*
 * 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.
 */

#define SM_SMARCNFDEF 1

#include "sm/generic.h"
SM_RCSID("@(#)$Id: smarconf.c,v 1.41 2007/11/14 06:03:09 ca Exp $")
#include "sm/error.h"
#include "sm/assert.h"
#include "sm/sysexits.h"
#include "sm/string.h"
#include "sm/ctype.h"
#include "sm/io.h"
#include "sm/misc.h"
#include "log.h"
#include "sm/qmgrcomm.h"
#include "sm/sm-conf.h"
#include "sm/sm-conf-prt.h"

#define SM_LOG_IDENT	"smar"
#define SM_CONF_TIME_DEF 1
#define SM_CONF_LOG_DEF 1
#define SM_MAPCNFDEF 1
#define SM_GREYCNFDEF 1

#include "smar.h"
#include "sm/map.h"
#include "sm/mapconf.h"
#include "sm/mapcnf.h"
#include "sm/mapcnfdef.h"
#include "sm/greycnf.h"
#include "sm/greycnfdef.h"
#include "sm/smarcnf.h"
#include "sm/smarcnfdef.h"

/*
**  SMAR_READ_CNF -- Read configuration file
**
**	Parameters:
**		smar_ctx -- SMAR context
**		fn -- filename of configuration file
**		psmc -- (pointer to) configuration context (output)
**
**	Returns:
**		0 on success, everything else is an error
*/

sm_ret_T
smar_read_cnf(smar_ctx_P smar_ctx, const char *fn, sm_conf_T **psmc)
{
	int err;
	sm_ret_T ret;
	uint ui;
	uchar uc, *us;
	char *s;
	sm_conf_T *smc;
	sm_conf_iterator_T cnf_iter;
	FILE *fp;
	char confdir[PATH_MAX];

	fp = NULL;
	smc = sm_conf_new(fn);
	if (NULL == smc) {
		err = errno;

		sm_io_fprintf(smioerr,
			"sev=ERROR, func=smar_read_cnf, file=%.256s, sm_conf_new=NULL, errno=%d\n",
			fn, err);
		return sm_error_temp(SM_EM_Q_CONF, ENOMEM);
	}
	err = sm_conf_read_FILE(smc, fn, fp);
	if (err != 0) {
		sm_prt_conferr(fn, smc, err, smioerr);
		goto error;
	}

	err = sm_conf_scan(smc, smar_global_defs, SM_CONF_FLAG_ALLOW_ANY,
			&smar_ctx->smar_cnf);
	if (err != 0) {
		sm_prt_conferr(fn, smc, err, smioerr);
		goto error;
	}

	/*
	**  HACK: "parse" configuration data.
	**  This will go away as soon as the configuration
	**  definition evolves... (array of IPv4 addresses)
	*/

	while ((ui = smar_ctx->smar_dns_ntsks) < SM_DNS_MAX_TSKS
		&& (s = smar_ctx->smar_cnf.smar_cnf_nameservers[ui]) != NULL)
	{
		smar_ctx->smar_nameserveripv4s[ui] = (ipv4_T) inet_addr(s);
		++smar_ctx->smar_dns_ntsks;
	}
	if (smar_ctx->smar_cnf.smar_cnf_hostname != NULL) {
		size_t l;

		l = strlen(smar_ctx->smar_cnf.smar_cnf_hostname) + 1;
		smar_ctx->smar_hostname = sm_str_scpyn0(NULL,
			smar_ctx->smar_cnf.smar_cnf_hostname, l, l);
		if (NULL == smar_ctx->smar_hostname) {
			err = sm_error_temp(SM_EM_Q_CONF, ENOMEM);
			goto error;
		}
	}

	if (smar_ctx->smar_cnf.smar_cnf_dnsbl[0].scdb_name != NULL)
		SMAR_SET_FLAG(smar_ctx, SMAR_FL_HASDNSBL);

	if (smar_ctx->smar_cnf.smar_cnf_dns_timeout > 0) {
		(void) dns_mgr_set_timeout(smar_ctx->smar_dns_mgr_ctx,
			smar_ctx->smar_cnf.smar_cnf_dns_timeout);
	}

#if 0
	/* this create a core dump: see TODO.smi */
	cnf_iter = NULL;
	err = sm_conf_scan_next(smc, "smar.log", sm_log_spec_defs, 0,
			&cnf_name, &cnf_name_n, NULL, &cnf_iter);
SMAR_LEV_DPRINTF(0, (SMAR_DEBFP, "sev=DBG, func=smar_read_cnf, scan_smar.log=%d\n", err));
	if (0 == err)
#else /* 0 */

	/*
	**  HACK! it should check whether log is in config file
	**  this only works because facility 0 is KERN.
	*/

	if (smar_ctx->smar_cnf.smar_cnf_log.sm_logspc_facility != 0)
#endif /* 0 */
	{
		err = sm_log_opensyslog(
			smar_ctx->smar_cnf.smar_cnf_log.sm_logspc_ident,
			smar_ctx->smar_cnf.smar_cnf_log.sm_logspc_opt,
			smar_ctx->smar_cnf.smar_cnf_log.sm_logspc_facility);
		if (sm_is_err(err))
			goto error;
		err = sm_log_setfp_fd(smar_ctx->smar_lctx, NULL, INVALID_FD);
		if (sm_is_err(err))
			goto error;
	}

	ret = sm_dirname(fn, confdir, sizeof(confdir));
	if (sm_is_err(ret)) {
		sm_log_write(smar_ctx->smar_lctx,
			AR_LCAT_RESOLVER, AR_LMOD_RESOLVER,
			SM_LOG_ERR, 4,
			"sev=ERROR, func=smar_read_cnf, fn=%s, sm_dirname=%m"
			, fn, err);
		err = ret;
		goto error;
	}
	err = sm_mapconfopen(&smar_ctx->smar_cnf.smar_cnf_mapdecl,
			smar_ctx->smar_maps, confdir);
	if (sm_is_err(err)) {
		sm_log_write(smar_ctx->smar_lctx,
			AR_LCAT_RESOLVER, AR_LMOD_RESOLVER,
			SM_LOG_ERR, 4,
			"sev=ERROR, func=smar_read_cnf, sm_mapconfopen=%m"
			, err);
		goto error;
	}

	for (us = smar_ctx->smar_cnf.smar_cnf_addr_delim; (uc = *us) != '\0'; us++)
	{
		if (!ISPRINT(uc) || uc == '<' || uc == '>' || uc == '@') {
			err = sm_error_perm(SM_EM_Q_CONF, EINVAL);
			sm_log_write(smar_ctx->smar_lctx,
				AR_LCAT_RESOLVER, AR_LMOD_RESOLVER,
				SM_LOG_ERR, 4,
				"sev=ERROR, func=smar_read_cnf, address_delimiter=%#x, status=invalid_character"
				, (int) uc);
			goto error;
		}
	}

	cnf_iter = NULL;
	err = sm_conf_scan_next(smc, "smar.greylisting", greycnf_defs,
				SM_CONF_FLAG_PARSE_ONLY, NULL, NULL,
				&smar_ctx->smar_cnf.smar_cnf_grey, &cnf_iter);
	if (0 == err) {
		ret = sm_greyctl_crt(&smar_ctx->smar_greyctx);
		if (ret != SM_SUCCESS) {
			smar_ctx->smar_greyctx = NULL;
			sm_log_write(smar_ctx->smar_lctx,
				AR_LCAT_RESOLVER, AR_LMOD_RESOLVER,
				SM_LOG_ERR, 4,
				"sev=ERROR, func=smar_read_cnf, sm_greyctl_crt=%m"
				, ret);
		}
		else if (smar_ctx->smar_greyctx != NULL) /* paranoia */
			SMAR_SET_FLAG(smar_ctx, SMAR_FL_HASGREY);
	}

	cnf_iter = NULL;
	err = sm_conf_scan_next(smc, "smar.aliases", smar_alias_defs,
				SM_CONF_FLAG_PARSE_ONLY, NULL, NULL,
				NULL, &cnf_iter);
	if (0 == err)
		SMAR_SET_FLAG(smar_ctx, SMAR_FL_REQALIAS);

	cnf_iter = NULL;
	err = sm_conf_scan_next(smc, "smar.access_map", smar_access_defs,
				SM_CONF_FLAG_PARSE_ONLY, NULL, NULL,
				NULL, &cnf_iter);
	if (0 == err)
		SMAR_SET_FLAG(smar_ctx, SMAR_FL_REQACCESS);
	else {
		cnf_iter = NULL;
		err = sm_conf_scan_next(smc, "smar.dnsbl", smar_dnsbls_defs,
				SM_CONF_FLAG_PARSE_ONLY, NULL, NULL,
				NULL, &cnf_iter);
		if (0 == err)
			SMAR_SET_FLAG(smar_ctx, SMAR_FL_REQACCESS);
	}

	if (psmc != NULL)
		*psmc = smc;

	return SM_SUCCESS;

  error:
	if (smc != NULL) {
		sm_conf_destroy(smc);
		smc = NULL;
	}
	return err;
}

#if STANDALONE
static int
process(char const *name, FILE *fp)
{
	sm_conf_T		*stream;
	int			err;
	smar_cnf_T		s;
	sm_conf_iterator_T	smar_cnf_iter;
	char const		*smar_cnf_name;
	size_t			smar_cnf_name_n;

	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) {
		char buf[200];
		char const *e = NULL;

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

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

		sm_conf_destroy(stream);
		return 2;
	}

	smar_cnf_iter = NULL;
	while ((err = sm_conf_scan_next(stream, "sc", mcp_defs, 0, &smar_cnf_name,
				&smar_cnf_name_n, &s, &smar_cnf_iter)) == 0)
	{
		print_structure(&s, smar_cnf_name, smar_cnf_name_n);
	}
	if (err != 0 && err != SM_CONF_ERR_NOT_FOUND) {
		char buf[200];
		char const *e = NULL;

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

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

		sm_conf_destroy(stream);
		return 3;
	}
	sm_conf_destroy(stream);
	return 0;
}

int
main(int ac, char **av)
{
	int	ret;
	int	ai;

	ret = 0;	/* make compiler happy */
	if (1 == ac)
		ret = process("*stdin*", stdin);
	else {
		for (ai = 1; ai < ac; ai++) {
			ret = process(av[ai], NULL);
			if (ret != 0)
				break;
		}
	}

	return ret;
}
#endif /* STANDALONE */


syntax highlighted by Code2HTML, v. 0.9.1