/*
 * 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: sockmap.c,v 1.18 2007/01/22 17:25:06 ca Exp $")

#include "sm/assert.h"
#include "sm/error.h"
#include "sm/memops.h"
#include "sm/heap.h"
#include "map.h"
#include "sm/map.h"
#include "sm/maps.h"
#include "sm/mapc.h"
#include "sm/mapclasses.h"
#include "sockmap.h"

static sm_map_alloc_F	 sm_sockmap_alloc;
static sm_map_free_F	 sm_sockmap_free;
static sm_map_locate_F	 sm_sockmap_locate;
static sm_map_first_F	 sm_sockmap_first;
static sm_map_next_F	 sm_sockmap_next;

/* --------- socket map abstraction layer ------ */

/*
**  SM_SOCKMAP_DESTROY -- destroy map
**	XXX more parameters...
**
**	Parameters:
**		map -- map
**		flags -- flags
**
**	Returns:
**		usual sm_error code
*/

static sm_ret_T
sm_sockmap_destroy(sm_map_P map, uint32_t flags)
{
	sm_ret_T ret;
	sm_mapc_P mapc;
	sm_sockmap_P db;

	SM_IS_MAP(map);
	mapc = map->sm_map_class;
	SM_IS_MAPC(mapc);
	ret = SM_SUCCESS;

	db = (sm_sockmap_P) map->sm_map_db;
	if (NULL == db)
		return sm_error_perm(SM_EM_MAP, ENOENT);
	sockmap_destroy(&db);
	map->sm_map_db = NULL;
	return ret;
}

/*
**  SM_SOCKMAP_SETOPT -- set options for map
**
**	Parameters:
**		map -- map
**		ap -- options
**
**	Returns:
**		usual sm_error code
*/

static sm_ret_T
sm_sockmap_setopt(sm_map_P map, va_list ap)
{
	sm_ret_T ret;
	uint k, u;
	sm_sockmap_P db;

	SM_IS_MAP(map);
	db = (sm_sockmap_P) map->sm_map_db;
	if (NULL == db)
		return sm_error_perm(SM_EM_MAP, ENOENT);
	ret = SM_SUCCESS;

	for (;;) {
		k = va_arg(ap, uint);
		if (SMPO_END == k)
			break;

		switch (k) {
		  case SMPO_PORT:
			u = va_arg(ap, int);
			db->sockmap_port = (short) u;
			break;
		  case SMPO_IPV4:
			u = va_arg(ap, ipv4_T);
			db->sockmap_ipv4 = u;
			break;
		  case SMPO_TMOUT:
			db->sockmap_tmout = va_arg(ap, sm_intvl_T);
			break;
		  case SMPO_SOCKPATH:
			db->sockmap_path = va_arg(ap, char *);
			break;
		  default:
			/* silently ignore bogus options? */
			break;
		}
	}
	return ret;
}

/*
**  SM_SOCKMAP_GETOPT -- get options for map
**
**	Parameters:
**		map -- map
**		which -- which option?
**		valp -- pointer to place where result should be stored
**
**	Returns:
**		usual sm_error code
*/

static sm_ret_T
sm_sockmap_getopt(sm_map_P map, int which, void *valp)
{
	sm_ret_T ret;
	sm_sockmap_P db;

	SM_IS_MAP(map);
	db = (sm_sockmap_P) map->sm_map_db;
	if (NULL == db)
		return sm_error_perm(SM_EM_MAP, SM_E_NOMAP);

	/* ... */

	ret = SM_SUCCESS;
	return ret;
}


/*
**  SM_SOCKMAP_CLOSE -- close map
**	XXX more parameters...
**
**	Parameters:
**		map -- map
**		flags -- flags
**
**	Returns:
**		usual sm_error code
*/

static sm_ret_T
sm_sockmap_close(sm_map_P map, uint32_t flags)
{
	sm_sockmap_P db;

	SM_IS_MAP(map);
	db = map->sm_map_db;
	if (NULL == db)
		return sm_error_perm(SM_EM_MAP, SM_E_NOMAP);
	SM_REQUIRE(db != NULL);
	return sockmap_close(db);
}

/*
**  SM_SOCKMAP_CREATE -- create map
**
**	Parameters:
**		mapc -- map context
**		type -- type of map
**		flags -- flags for map
**		map -- map
**
**	Returns:
**		usual sm_error code
*/

static sm_ret_T
sm_sockmap_create(sm_mapc_P mapc, const sm_cstr_P type, uint32_t flags, sm_map_P map)
{
	sm_ret_T ret;
	sm_sockmap_P db;

	SM_IS_MAPC(mapc);
	SM_REQUIRE(map != NULL);
	db = NULL;
	ret = sockmap_new(&db);
	if (sm_is_success(ret)) {
		map->sm_map_db = db;
		map->sm_map_caps = SMMAP_CAPS_LTALL;
	}
	return ret;
}

/*
**  SM_SOCKMAP_OPEN -- open map
**
**	Parameters:
**		mapc -- map context
**		type -- type of map (currently ignored)
**		flags -- flags for map (currently ignored)
**		name -- name of map
**		mode -- open mode (currently ignored)
**		map -- map
**
**	Returns:
**		usual sm_error code
*/

static sm_ret_T
sm_sockmap_open(sm_mapc_P mapc, const sm_cstr_P type, uint32_t flags, const char *name, int mode, sm_map_P map, va_list ap)
{
	sm_ret_T ret;
	uint k, u;
	sm_sockmap_P db;

	SM_IS_MAPC(mapc);
	SM_REQUIRE(map != NULL);

	db = map->sm_map_db;
	SM_REQUIRE(db != NULL);
	db->sockmap_name = name;
	for (;;) {
		k = va_arg(ap, uint);
		if (SMPO_END == k)
			break;

		switch (k) {
		  case SMPO_SOCKPATH:
			db->sockmap_path = va_arg(ap, char *);
			break;
		  case SMPO_PORT:
			u = va_arg(ap, int);
			db->sockmap_port = (short) u;
			break;
		  case SMPO_IPV4:
			u = va_arg(ap, ipv4_T);
			db->sockmap_ipv4 = u;
			break;
		  default:
			/* silently ignore bogus options? */
			break;
		}
	}
	ret = sockmap_open(db);
	if (sm_is_err(ret))
		goto error;
	return SM_SUCCESS;

  error:
	if (db != NULL) {
		(void) sockmap_destroy(&db);
		db = NULL;
	}
	return ret;
}

/*
**  SM_SOCKMAP_REOPEN -- reopen map
**
**	Parameters:
**		map -- map
**		flags -- flags for map
**
**	Returns:
**		usual sm_error code
*/

static sm_ret_T
sm_sockmap_reopen(sm_map_P map, uint32_t flags)
{
	sm_ret_T ret;
	sm_sockmap_P db;

	SM_REQUIRE(map != NULL);
	db = map->sm_map_db;
	SM_REQUIRE(db != NULL);
	SMMAP_CLR_FL(map, SMMAP_FL_OPEN);
	SMMAP_SET_FL(map, SMMAP_FL_CLOSING);
	ret = sockmap_close(db);
	SMMAP_CLR_FL(map, SMMAP_FL_CLOSING);
	SMMAP_SET_FL(map, SMMAP_FL_CLOSED);
	ret = sockmap_open(db);
	if (sm_is_err(ret))
		goto error;
	SMMAP_SET_FL(map, SMMAP_FL_OPEN);
	SMMAP_CLR_FL(map, SMMAP_FL_CLOSED);
	return SM_SUCCESS;

  error:
	/* cleanup? */
	return ret;
}

/*
**  SM_SOCKMAP_LOOKUP -- lookup a key in SOCKMAP, return data if found
**
**	Parameters:
**		map -- map context
**		flags -- flags
**		key -- key
**		data -- data (output)
**
**	Returns:
**		usual sm_error code
*/

static sm_ret_T
sm_sockmap_lookup(sm_map_P map, uint32_t flags, sm_map_key_P key, sm_map_data_P data)
{
	sm_ret_T ret;
	sm_mapc_P mapc;
	sm_sockmap_P db;

	SM_IS_MAP(map);
	SM_IS_KEY(key);
	SM_IS_DATA(data);
	mapc = map->sm_map_class;
	SM_IS_MAPC(mapc);
	ret = SM_SUCCESS;

	db = (sm_sockmap_P) map->sm_map_db;
	if (NULL == db)
		return sm_error_perm(SM_EM_MAP, ENOENT);	/* XXX */
	if (!SMMAP_IS_FL(map, SMMAP_FL_OPEN)) {
		/* map closed but can be reopened? */
		if (mapc->sm_mapc_reopenf != NULL)
			ret = mapc->sm_mapc_reopenf(map, 0);
		else
			ret = sm_error_perm(SM_EM_MAP, SM_E_CLOSEDMAP);
		if (sm_is_err(ret))
			return ret;
	}

	/* XXX WARNING: changes key inplace! */
	if (SM_IS_FLAG(flags, SMMAP_FL_LWR_KEY))
		sm_str2lower(key);

	ret = sockmap_lookup(db, key, data);

	/* this doesn't look right: violation of abstraction... */
	if (sm_is_err(ret) && NULL == db->sockmap_fp) {
		SMMAP_CLR_FL(map, SMMAP_FL_OPEN);
		SMMAP_SET_FL(map, SMMAP_FL_CLOSED);
	}
	return ret;
}

#if 0
/*
**  SM_SOCKMAP_ADD -- add key/data to SOCKMAP
**
**	Parameters:
**		map -- map context
**		key -- key
**		data -- data
**		flags -- flags
**
**	Returns:
**		usual sm_error code
*/

static sm_ret_T
sm_sockmap_add(sm_map_P map, sm_map_key_P key, sm_map_data_P data, uint flags)
{
	return sm_error_perm(SM_EM_MAP, EINVAL);
}

/*
**  SM_SOCKMAP_RM -- remove key/data from SOCKMAP
**
**	Parameters:
**		map -- map context
**		key -- key
**
**	Returns:
**		usual sm_error code
*/

static sm_ret_T
sm_sockmap_rm(sm_map_P map, sm_map_key_P key)
{
	return sm_error_perm(SM_EM_MAP, EINVAL);
}
#endif /* 0 */

/*
**  SM_SOCKMAP_CLASS_CREATE -- create SOCKMAP map class
**
**	Parameters:
**		maps -- map system context
**
**	Returns:
**		usual sm_error code
*/

sm_ret_T
sm_sockmap_class_create(sm_maps_P maps)
{
	sm_ret_T ret;
	sm_mapc_P mapc;
	sm_cstr_P htype;

#define SOCKMAP_TYPE	"socket"

	ret = SM_SUCCESS;
	mapc = NULL;
	htype = sm_cstr_scpyn0((const uchar *)SOCKMAP_TYPE,
			strlen(SOCKMAP_TYPE));
	if (NULL == htype)
		goto error;

	ret = sm_mapc_create(maps, htype, SMMAPC_FL_LCK_FULL,
			sm_sockmap_create,
			sm_sockmap_open,
			sm_sockmap_close,
			sm_sockmap_reopen,
			sm_sockmap_destroy,
			NULL /*sm_sockmap_add*/,
			NULL /*sm_sockmap_rm*/,
			sm_sockmap_alloc,
			sm_sockmap_free,
			sm_sockmap_lookup,
			sm_sockmap_locate,
			sm_sockmap_first,
			sm_sockmap_next,
			sm_sockmap_setopt,
			sm_sockmap_getopt,
			&mapc);

	SM_CSTR_FREE(htype);
	return ret;

  error:
	if (SM_SUCCESS == ret)
		ret = sm_error_temp(SM_EM_MAP, ENOMEM);
	/* cleanup mapc? */
	return ret;
}


syntax highlighted by Code2HTML, v. 0.9.1