/*	$OpenBSD: map.c,v 1.9 2010/02/17 17:37:15 gilles Exp $	*/

/*
 * Copyright (c) 2008 Gilles Chehade <gilles@openbsd.org>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include <sys/types.h>
#include <sys/queue.h>
#include <sys/tree.h>
#include <sys/param.h>
#include <sys/socket.h>

#include <ctype.h>
#include <db.h>
#include <err.h>
#include <errno.h>
#include <event.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "smtpd.h"

/* db(3) backend */
void *map_db_open(char *);
void map_db_close(void *);
char *map_db_get(void *, char *);
int map_db_put(void *, char *, char *);

/* stdio(3) backend */
void *map_stdio_open(char *);
void map_stdio_close(void *);
char *map_stdio_get(void *, char *);
int map_stdio_put(void *, char *, char *);


struct map_backend {
	enum map_src map_type;
	void *(*open)(char *);
	void (*close)(void *);
	char *(*get)(void *, char *);
	int (*put)(void *, char *, char *);
} map_backends[] = {
	{ S_DB,
	  map_db_open, map_db_close, map_db_get, map_db_put },
	{ S_FILE,
	  map_stdio_open, map_stdio_close, map_stdio_get, map_stdio_put },
};

struct map *
map_findbyname(struct smtpd *env, const char *name)
{
	struct map	*m;

	TAILQ_FOREACH(m, env->sc_maps, m_entry) {
		if (strcmp(m->m_name, name) == 0)
			break;
	}
	return (m);
}

struct map *
map_find(struct smtpd *env, objid_t id)
{
	struct map	*m;

	TAILQ_FOREACH(m, env->sc_maps, m_entry) {
		if (m->m_id == id)
			break;
	}
	return (m);
}

char *
map_lookup(struct smtpd *env, objid_t mapid, char *key)
{
	u_int8_t i;
	void *hdl = NULL;
	char *result = NULL;
	struct map *map;
	struct map_backend *backend = NULL;

	map = map_find(env, mapid);
	if (map == NULL)
		return NULL;

	for (i = 0; i < nitems(map_backends); ++i)
		if (map_backends[i].map_type == map->m_src)
			break;

	if (i == nitems(map_backends))
		fatalx("invalid map type");

	backend = &map_backends[i];
	hdl = backend->open(map->m_config);
	if (hdl == NULL) {
		log_warn("map_lookup: can't open %s", map->m_config);
		return NULL;
	}

	result = backend->get(hdl, key);
	backend->close(hdl);

	return result;
}


/* db(3) backend */
void *
map_db_open(char *src)
{
	return dbopen(src, O_RDONLY, 0600, DB_HASH, NULL);
}

void
map_db_close(void *hdl)
{
	DB *db = hdl;

	db->close(db);
}

char *
map_db_get(void *hdl, char *key)
{
	int ret;
	DBT dbk;
	DBT dbv;
	DB *db = hdl;
	char *result = NULL;

	dbk.data = key;
	dbk.size = strlen(dbk.data) + 1;

	if ((ret = db->get(db, &dbk, &dbv, 0)) != 0)
		return NULL;

	result = calloc(dbv.size, 1);
	if (result == NULL)
		fatal("calloc");
	(void)strlcpy(result, dbv.data, dbv.size);

	return result;
}

int
map_db_put(void *hdl, char *key, char *val)
{
	return 0;
}


/* stdio(3) backend */
void *
map_stdio_open(char *src)
{
	return fopen(src, "r");
}

void
map_stdio_close(void *hdl)
{
	FILE *fp = hdl;

	fclose(fp);
}

char *
map_stdio_get(void *hdl, char *key)
{
	char *buf, *lbuf;
	size_t len;
	char *keyp;
	char *valp;
	FILE *fp = hdl;
	char *result = NULL;

	lbuf = NULL;
	while ((buf = fgetln(fp, &len))) {
		if (buf[len - 1] == '\n')
			buf[len - 1] = '\0';
		else {
			if ((lbuf = malloc(len + 1)) == NULL)
				err(1, NULL);
			memcpy(lbuf, buf, len);
			lbuf[len] = '\0';
			buf = lbuf;
		}

		keyp = buf;
		while (isspace((int)*keyp))
			++keyp;
		if (*keyp == '\0' || *keyp == '#')
			continue;

		valp = keyp;
		strsep(&valp, " \t:");
		if (valp == NULL || valp == keyp)
			continue;

		if (strcmp(keyp, key) != 0)
			continue;

		result = strdup(buf);
		if (result == NULL)
			err(1, NULL);
		break;
	}
	free(lbuf);

	return result;
}

int
map_stdio_put(void *hdl, char *key, char *val)
{
	return 0;
}