/*
 *
 * ldap.c
 *
 * Complex LDAP searches, and string routines
 *
 * Author: Landon Fuller <landonf@go2net.com>
 *
 * Copyright (c) 2000-2001 InfoSpace, Inc. All Rights Reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *         This product includes software developed by InfoSpace, Inc.
 *         and its contributors.
 * 4. Neither the name of InfoSpace, Inc nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 */

#include "../../../autoconf.h"

#include <stdlib.h>
#include <stdio.h>
#ifdef HAVE_STRINGS_H
#include <strings.h>
#else
#include <string.h>
#endif
#include <errno.h>
#include <pthread.h>
#include <semaphore.h>
#include <lber.h>
#include <ldap.h>

#include "yp.h"
#include "../../../include/config.h"

#include "ldap.h"
#include "cache.h"

#define CONFIG_FILE CONFDIR "/ldap.conf"
/*
#define MAPS "auto.home auto_home amd.home passwd.byname passwd.byuid group.byname group.bygid"
*/
#define MAPS "auto.home auto_home amd.home master.passwd.byname master.passwd.byuid passwd.byname passwd.byuid group.byname group.bygid"

struct ldaprefs ldaprefs;

typedef enum {
	CACHE, HOST, PORT, BASEDN, BINDDN, PASS, BADOPTION
} ConfigOptions;

static struct {
	const char *name;
	ConfigOptions opcode;
} keywords [] = {
	{ "cache", CACHE },
	{ "host", HOST },
	{ "port", PORT },
	{ "basedn", BASEDN },
	{ "binddn", BINDDN },
	{ "password", PASS },
	{ NULL, 0 }
};

static void init_config (void)
{
	ldaprefs.cache = 0;
	ldaprefs.host = NULL;
	ldaprefs.port = 0;
	ldaprefs.basedn = NULL;
	ldaprefs.binddn = NULL;
	ldaprefs.pass = NULL;
	ldaprefs.dflag = 0;
	ldaprefs.perr = 1;
}

static int fill_config (void)
{
	if (ldaprefs.host == NULL)
	{
		log("Missing required %s directive in config file\n", keywords[HOST].name);
		return (1);
	}
	if (ldaprefs.port == 0)
		ldaprefs.port = LDAP_PORT;

	if (ldaprefs.basedn == NULL)
	{
		log("Missing required %s directive in config file\n", keywords[BASEDN].name);
		return (1);
	}
	if (ldaprefs.binddn == NULL)
	{
		log("Missing required %s directive in config file\n", keywords[BINDDN].name);
		return (1);
	}
	if (ldaprefs.pass == NULL)
	{
		log("Missing required %s directive in config file\n", keywords[PASS].name);
		return (1);
	}
	return (0);
}

static ConfigOptions parse_opcode (const char *cp, const char *filename,
		int linenum)
{
	unsigned int i;

	for (i = 0; keywords[i].name; i++)
		if (strcasecmp(cp, keywords[i].name) == 0)
			return(keywords[i].opcode);

	log("%s: line %d: Bad configuration option: %s\n", filename, linenum, cp);
	return(BADOPTION);
}

static int read_server_config (char *filename)
{
	FILE *config;
	char line[1024];
	char *cp;
	int linenum = 0;
	int bad_options = 0;
	ConfigOptions opcode;

	config = fopen(filename, "r");
	if (!config)
	{
		log("Failed to open config file %s for reading\n", CONFIG_FILE);
		return(1);
	}

	while (fgets(line, sizeof(line), config) != NULL)
	{
		linenum++;
		cp = line + strspn(line, WHITESPACE);
		if (!*cp || *cp == '#')
			continue;
		cp = strtok(cp, WHITESPACE);
		opcode = parse_opcode(cp, filename, linenum);
		switch (opcode)
		{
			char *endptr;
			case BADOPTION:
				bad_options++;
				continue;
			case CACHE:
				cp = strtok(NULL, WHITESPACE);
				if (!cp)
					log("%s line %d: missing cache size\n", filename, linenum);
				ldaprefs.cache = strtol(cp, &endptr, 10);
				if (*endptr != '\0')
					log("Invalid cache size: %s\n", cp);
				break;
			case HOST:
				cp = strtok(NULL, WHITESPACE);
				if (!cp)
					log("%s line %d: missing LDAP host\n", filename, linenum);
				ldaprefs.host = safe_strdup(cp);
				break;
			case PORT:
				cp = strtok(NULL, WHITESPACE);
				if (!cp)
					log("%s line %d: missing LDAP port\n", filename, linenum);
				ldaprefs.port = strtol(cp, &endptr, 10);
				if (*endptr != '\0' || ldaprefs.port > 65535)
					log("Invalid port: %s\n", cp);
				break;
			case BASEDN:
				cp = strtok(NULL, WHITESPACE);
				if (!cp)
					log("%s line %d: missing base DN\n", filename, linenum);
				ldaprefs.basedn = safe_strdup(cp);
				break;
			case BINDDN:
				cp = strtok(NULL, WHITESPACE);
				if (!cp)
					log("%s line %d: missing bind DN\n", filename, linenum);
				ldaprefs.binddn = safe_strdup(cp);
				break;
			case PASS:
				cp = strtok(NULL, WHITESPACE);
				if (!cp)
					log("%s line %d: missing password\n", filename, linenum);
				ldaprefs.pass = safe_strdup(cp);
				break;
			default:
				log("%s line %d: Missing handler for config opcode %s (%d)\n", filename, linenum, cp, opcode);
		}
		if (strtok(NULL, WHITESPACE) != NULL)
			log("%s line %d: garbage at end of line.\n", filename, linenum);
	}
	fclose(config);
	if (bad_options > 0)
	{
		log("%s: terminating, %d bad configuration options\n", filename, bad_options);
		return (1);
	}
	return (0);
}

static bool_t yp_ldapconnect(void)
{
	if(ldaprefs.ldap != NULL)
		ldap_unbind(ldaprefs.ldap);

	if ((ldaprefs.ldap = ldap_init(ldaprefs.host, (int) ldaprefs.port)) == NULL)
	{
		log("ldap init failure for server %s, port %d: %s\n", ldaprefs.host, ldaprefs.port, strerror(errno));
		return (1);
	}

	if ((ldap_bind_s(ldaprefs.ldap, ldaprefs.binddn, ldaprefs.pass, LDAP_AUTH_SIMPLE)) != LDAP_SUCCESS)
	{
#if defined(LDAP_API_VERSION) && LDAP_API_VERSION > 1823
		int error;
		if(ldap_get_option (ldaprefs.ldap, LDAP_OPT_ERROR_NUMBER, &error) != 0)
		{
			log("ldap bind failure for server %s, port %d\n", ldaprefs.host, ldaprefs.port);
		} else {
			log("ldap bind failure for server %s, port %d: %s\n", ldaprefs.host, ldaprefs.port, ldap_err2string(error));
		}
#else
		log("ldap bind failure for server %s, port %d: %s\n", ldaprefs.host, ldaprefs.port, ldap_err2string(ldaprefs.ldap->ld_errno));
#endif
		return (1);
	}
	if (ldaprefs.cache != 0)
	{
		if ((ldap_enable_cache(ldaprefs.ldap, 300, ldaprefs.cache * 1024)) == -1)
			log("ldap caching memory allocation failed. Caching will not be enabled\n");
	}
	return (0);
}

/* getbykey will pull data directly from LDAP. This will be used to update internal
 * maps cached for the other YP calls */

ypstat yp_getbykey(keydat *key, mapname *map, valdat *val)
{
	ypstat rval;

	rval = yp_get_record(key, map, val);
	return (rval);
}

ypstat yp_firstbykey(keydat *key, mapname *map, valdat *val)
{
	ypstat rval;

	rval = yp_first_record(key, map, val);
	return (rval);
}

ypstat yp_nextbykey(keydat *key, mapname *map, valdat *val)
{
	ypstat rval;

	rval = yp_next_record(key, map, val);
	return (rval);
}

int yp_clear (void)
{
	ldap_flush_cache(ldaprefs.ldap);
	return (0);
}

bool_t init(struct ypmodulelist *yp_modulelist)
{
	char *maps;
	pthread_t cache_thread;
	bool_t result = 0;

	ldaprefs.yp_moduledata = yp_modulelist->yp_moduledata;
	log ("LDAP module: initializing\n");
	/* XXX Supported maps? */
	maps = safe_strdup(MAPS);
	yp_modulelist->maps = yp_maplist_create(maps);
	free(maps);
	init_config();
	if(read_server_config(CONFIG_FILE))
		return (1);
	if(fill_config())
		return (1);
	if(yp_ldapconnect())
		return (1);
	log ("LDAP module: building cache\n");
	ldaprefs.mapcache = create_cache(yp_modulelist->maps);
	log ("LDAP module: cache built.\n");
	pthread_create ( &cache_thread, NULL, (void *) &maintain_cache, NULL); 
	return (result);
}


syntax highlighted by Code2HTML, v. 0.9.1