/*
 * 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: sm-conf-type-ipv4.c,v 1.7 2006/01/09 19:06:25 ca Exp $")

#if SM_LIBCONF_ALONE
#include <limits.h>
#include <errno.h>
#include <ctype.h>
#include <string.h>
#include "sm-conf.h"
#include "sm-util.h"
#else /* SM_LIBCONF_ALONE */
#include "sm/limits.h"
#include "sm/error.h"
#include "sm/ctype.h"
#include "sm/memops.h"
#include "sm/string.h"
#include "sm/sm-conf.h"
#include "sm/net.h"
#endif /* SM_LIBCONF_ALONE */

#include "sm-conf-node.h"
#include "sm-conf-state.h"
#include "sm-conf-type.h"
#include "sm-conf-util.h"


/*
**  SM_MEMTOIPV4 --
**
**	Parameters:
**		text -- the text to be converted
**		text_n -- # of bytes pointed to by text
**		endptr -- if non-NULL, a pointer to the first
**			unconverted byte is stored here.
**		val_out -- value is stored here.
**
**	Returns:
**		EINVAL for a format error
**		ERANGE for overflow
**		0 for success.
*/

static sm_ret_T
sm_memtoipv4(
	char const		*text,
	size_t			text_n,
	char			**endptr,
	unsigned long		*val_out)
{
	size_t n;
	sm_ret_T ret;
	ipv4_T ipv4;
	char ipv4str[20];	/* 4 * 4 + 2 "[]" + '\0' + 1 as safety */

	if (text_n >= sizeof(ipv4str))
		n = sizeof(ipv4str) - 1;
	else
		n = text_n;
	sm_memcpy(ipv4str, text, n);
	ipv4str[n] = '\0';
	ret = sm_inet_a2ipv4(ipv4str, endptr, &ipv4);
	*val_out = ipv4;
	return ret;
}

static int
ipv4_write(
	sm_conf_definition_T const	*def,
	void				*data,
	unsigned long			val)
{
	SM_IS_CONF_DEF(def);
	if (def->scd_size != sizeof(ipv4_T))
		return SM_CONF_ERR_INVALID;

	*(ipv4_T *)data = (ipv4_T)val;
	return 0;
}

static int
ipv4_scan(
	sm_conf_T		*smc,
	sm_conf_node_T const	*node,
	sm_conf_definition_T const *def,
	char const		*text,
	size_t			text_n,
	unsigned long		*val_out)
{
	char			loc[SM_CONF_ERROR_BUFFER_SIZE];
	size_t			i;
	int			err;
	char			*endptr;

	SM_IS_CONF_DEF(def);

	err = sm_memtoipv4(text, text_n, &endptr, val_out);
	if (err != 0)
	{
		sm_conf_error_add(smc,
			"%s: expected IPv4 address, got '%.*s'",
			sm_conf_node_location(smc, node, loc, sizeof loc),
			(int)text_n, text);
		return SM_CONF_ERR_TYPE;
	}

	i = endptr - text;

	/* If there's something left, and we have suffixes, use them. */
	if (i < text_n)
	{
		sm_conf_error_add(smc, "%s: unexpected trailing "
			"characters in '%.*s'",
			sm_conf_node_location(smc, node, loc, sizeof loc),
			(int)text_n, text);
		return SM_CONF_ERR_TYPE;
	}

	return 0;
}

static int
sm_conf_type_ipv4_node_to_value(
	sm_conf_T			*smc,
	sm_conf_definition_T const	*def,
	sm_conf_node_T			*node,
	void				*data)
{
	char const			*text;
	size_t				text_n;
	int				err;
	unsigned long			val;

	SM_IS_CONF_DEF(def);
	err = sm_conf_node_to_value(smc, "ipv4ean", node, &text, &text_n);
	if (err != 0)
		return err;

	err = ipv4_scan(smc, node, def, text, text_n, &val);
	if (err != 0)
		return err;

	if (data != NULL)
		err = ipv4_write(def, data, val);

	return err;
}

static int
sm_conf_type_ipv4_value_check(
	sm_conf_T			*smc,
	sm_conf_definition_T const	*def,
	void const			*data)
{
	SM_IS_CONF_DEF(def);

	/* if we have a check function, use it. */
	if (def->scd_check != NULL)
		return (* def->scd_check)(smc, def->scd_check_data, def, data);
	return 0;
}

static int
sm_conf_type_ipv4_value_null(
	sm_conf_T			*smc,
	sm_conf_definition_T const	*def,
	void				*data)
{
	unsigned long			val;
	int				err;

	if (data != NULL)
	{
		SM_IS_CONF_DEF(def);
		val = 0;

		/* is there a default? if NULL: don't set value to 0 */
		if (def->scd_default != NULL)
		{
			/* is there an actual value? */
			if (*def->scd_default != '\0')
			{
				err = ipv4_scan(smc, NULL, def, def->scd_default,
					strlen(def->scd_default), &val);
				if (err != 0)
					return err;
			}
			err = ipv4_write(def, data, val);
			if (err != 0)
				return err;
		}
	}
	return 0;
}

sm_conf_type_T const
sm_conf_type_ipv4_data =
{
	sm_conf_type_ipv4_node_to_value,
	sm_conf_type_ipv4_value_check,
	sm_conf_type_ipv4_value_null
};


syntax highlighted by Code2HTML, v. 0.9.1