/*
 *	Copyright 1997 by Lai Yiu Fai (ccyflai@ust.hk), all rights reserved.
 *
 *	Merge to standard ZMailer distribution with autoconfiguration by
 *	Matti Aarnio <mea@nic.funet.fi> 1997; Code reorganization 1999
 *	to do db bind at open_ldap(), *only* searches at  search_ldap()..
 *
 *	Support code for LDAPv3, and report about need to handle server
 *	going away (e.g. restart LDAP lookups) by:
 *	   Jeff Warnica <jeffw@chebucto.ns.ca>
 */

/* LINTLIBRARY */

#include "mailer.h"
#include "search.h"
#include "io.h"
#include <ctype.h>
#ifdef USE_LDAP
#include "lber.h"
#include "ldap.h"

#ifndef LDAP_FILT_MAXSIZ
#define LDAP_FILT_MAXSIZ 1024
#endif

typedef struct ldapmap_struct {
	  /* Setup parameters */
	char *ldaphost;
	int  ldapport;
	char *base;
	char *binddn;
	char *passwd;
	int  scope;
	char *filter;
	char *attr;
	int   wildcards;
	int   protocol;
	  /* Bound state */
	LDAP *ld;
	int simple_bind_result;
} LDAPMAP;

extern int deferit;
extern void v_set();

static void open_lmap_ __((LDAPMAP *lmap));
static void
open_lmap_ (lmap)
     LDAPMAP *lmap;
{
	if (lmap->ld)
		ldap_unbind_s(lmap->ld);
	lmap->ld = NULL;

	if (lmap->ldaphost)
		lmap->ld = ldap_open(lmap->ldaphost, lmap->ldapport);

	if (lmap->ld != NULL) {

		if (lmap->protocol > 0 &&
		    ldap_set_option( ldap->ld,
				     LDAP_OPT_PROTOCOL_VERSION,
				     &lmap->protocol ) != LDAP_OPT_SUCCESS ) {
		  fprintf( stderr, "Could not set LDAP_OPT_PROTOCOL_VERSION %d\n", lmap->protocol );
		}

		lmap->simple_bind_result =
		  ldap_simple_bind_s(lmap->ld, lmap->binddn, lmap->passwd);
	}
}


static LDAPMAP *
open_ldap(sip, caller)
	search_info *sip;
	char *caller;
{
	LDAPMAP *lmap;
	spkey_t symid;
	struct spblk *spl;
	FILE *fp;
	char buf[256];

	if (sip->file == NULL)
		return NULL;

	symid = symbol_db((u_char *)sip->file, spt_files->symbols);
	spl = sp_lookup(symid, spt_files);
	if (spl == NULL || (lmap = (LDAPMAP *)spl->data) == NULL) {

		fp = fopen(sip->file, "r");
		if (fp == NULL) {
			++deferit;
			v_set(DEFER, DEFER_IO_ERROR);
			fprintf(stderr, "%s: cannot open %s!\n",
					caller, sip->file);
			return NULL;
		}

		lmap = (LDAPMAP *) emalloc(sizeof(LDAPMAP));

		if (spl == NULL)
			sp_install(symid, (u_char *)lmap, 0, spt_files);
		else
			spl->data = (u_char *)lmap;

		memset(lmap, 0, sizeof(LDAPMAP));

		lmap->ldapport = LDAP_PORT;
		lmap->scope = LDAP_SCOPE_SUBTREE;
	
		while (fgets(buf, sizeof(buf), fp) != NULL)  {
			register char *p = buf;

			buf[sizeof(buf)-1] = '\0';	/* make sure we didn't
							   overfill the buf */
			if (buf[0] != 0)
			  buf[strlen(buf)-1] = '\0';	/* chop() */

			while (isascii(*p) && isspace(*p))
				p++;
			if (*p == '#')			/* skip comment */
				continue;

			if (strncasecmp(p, "base", 4) == 0)  {
				p += 4;
				while (isascii(*++p) && isspace(*p))
					continue;
				lmap->base = strdup(p);
			}
			else if (strncasecmp(p, "ldaphost", 8) == 0) {
				p += 8;
				while (isascii(*++p) && isspace(*p))
					continue;
				lmap->ldaphost = strdup(p);
			}
			else if (strncasecmp(p, "ldapport", 8) == 0) {
				p += 8;
				while (isascii(*++p) && isspace(*p))
					continue;
				lmap->ldapport = atoi(p);
			}
			else if (strncasecmp(p, "binddn", 6) == 0) {
				p += 6;
				while (isascii(*++p) && isspace(*p))
					continue;
				lmap->binddn = strdup(p);
			}
			else if (strncasecmp(p, "passwd", 6) == 0) {
				p += 6;
				while (isascii(*++p) && isspace(*p))
					continue;
				lmap->passwd = strdup(p);
			}
			else if (strncasecmp(p, "attr", 4) == 0) {
				p += 4;
				while (isascii(*++p) && isspace(*p))
					continue;
				lmap->attr = strdup(p);
			}
			else if (strncasecmp(p, "filter", 6) == 0) {
				p += 6;
				while (isascii(*++p) && isspace(*p))
					continue;
				lmap->filter = strdup(p);
			}
			else if (strncasecmp(p, "protocol", 8) == 0) {
				p += 8; 
				while (isascii(*++p) && isspace(*p))
					continue;
				if (strncasecmp(p, "3", 1) == 0)
					lmap->protocol = LDAP_VERSION3;
				else if (strncasecmp(p, "2", 1) == 0)
					lmap->protocol = LDAP_VERSION2;
			}
			else if (strncasecmp(p, "scope", 5) == 0) {
				p += 5;
				while (isascii(*++p) && isspace(*p))
					continue;
				if (strncasecmp(p, "base", 4) == 0)
					lmap->scope = LDAP_SCOPE_BASE;
				else if (strncasecmp(p, "one", 3) == 0)
					lmap->scope = LDAP_SCOPE_ONELEVEL;
				else if (strncasecmp(p, "sub", 3) == 0)
       	                         	lmap->scope = LDAP_SCOPE_SUBTREE;
			}
			else if (strncasecmp(p, "wildcards", 9) == 0) {
				p += 9;
				while (isascii(*++p) && isspace(*p))
					continue;
				if (strncasecmp(p, "true", 4) == 0)
					lmap->wildcards = 1;
			}
		}
		fclose(fp);

		open_lmap_(lmap);
	}

	return lmap;
}

/*
 * Search LDAP for a key attribute.
 */
conscell *
search_ldap(sip)
	search_info *sip;
{
	LDAPMAP *lmap;

	LDAPMessage *msg = NULL, *entry;
	char filter[LDAP_FILT_MAXSIZ + 1];
	char **vals = NULL;
	char *attrs[] = {NULL, NULL};
	char *filterstring = NULL;
	int starcount = 0;
	int filterlength = 0;
	int counter = 0;
	int filterpos = 0;
	int rc;

	conscell *tmp = NULL;

	lmap = open_ldap(sip, "search_ldap");
	if (lmap == NULL)
		return NULL;

	if (lmap->ld == NULL || lmap->simple_bind_result != LDAP_SUCCESS) {
		++deferit;
		v_set(DEFER, DEFER_IO_ERROR);
		fprintf(stderr, "search_ldap: cannot connect '%s'/'%s'!\n",
			lmap->ldaphost ? lmap->ldaphost : "",
			lmap->binddn   ? lmap->binddn : "");
		goto ldap_exit;
	}

	filterstring = (char *)sip->key;

	if (!lmap->wildcards) {
		filterlength = strlen(filterstring);

		for (counter = 0; counter < filterlength; counter++) {
			if (filterstring[counter] == '*') {
				starcount++;
			}
		}

		if (starcount) {
			filterstring = malloc(filterlength + 2*starcount + 1);
			filterpos = 0;
			counter = 0;
			for (; counter < filterlength; ) {
				if (((char *)sip->key)[counter] == '*')  {
					filterstring[filterpos++] = '\\';
					filterstring[filterpos++] = '2';
					filterstring[filterpos++] = 'a';
				} else {
					filterstring[filterpos++] = ((char *)sip->key)[counter];
				}
				counter++;
			}
			filterstring[filterpos] = 0;
		}
	}

	sprintf(filter, lmap->filter, filterstring);

	if (!lmap->wildcards && starcount) {
		free(filterstring);
	}

	attrs[0] = lmap->attr;
	rc = ldap_search_s(lmap->ld, lmap->base, lmap->scope, filter,
			   attrs, 0, &msg);
	if (rc != LDAP_SUCCESS) {
		++deferit;
		v_set(DEFER, DEFER_IO_ERROR);
		fprintf(stderr, "search_ldap: ldap_search_s error!\n");
		goto ldap_exit;		 
	}

	entry = ldap_first_entry(lmap->ld, msg);
	if (entry == NULL)
		goto ldap_exit;

	/* only get the first attribute, ignore others if defined */
	vals = ldap_get_values(lmap->ld, entry, lmap->attr);
	if (vals != NULL) {
		/* if there is more that one, use the first */
		int slen = strlen(vals[0]);
		char *s = dupnstr(vals[0], slen);
		tmp = newstring(s, slen);
	}

ldap_exit:
	if (vals != NULL)
		ldap_value_free(vals);
	if (msg != NULL)
		ldap_msgfree(msg);

	return tmp;
}

void
close_ldap(sip,comment)
	search_info *sip;
	const char *comment;
{
	LDAPMAP *lmap;
	struct spblk *spl;
	spkey_t symid;

	if (sip->file == NULL)
		return;
	symid = symbol_db((u_char *)sip->file, spt_files->symbols);
	spl = sp_lookup(symid, spt_modcheck);
	if (spl != NULL)
		sp_delete(spl, spt_modcheck);
	spl = sp_lookup(symid, spt_files);
	if (spl == NULL || (lmap = (LDAPMAP *)spl->data) == NULL)
		return;
	sp_delete(spl, spt_files);
	symbol_free_db(sip->file, spt_files->symbols);

	if (lmap->ld != NULL)
		ldap_unbind_s(lmap->ld);
	if (lmap->base != NULL)
		free(lmap->base);
	if (lmap->ldaphost != NULL)
		free(lmap->ldaphost);
	if (lmap->binddn != NULL)
		free(lmap->binddn);
	if (lmap->passwd != NULL)
		free(lmap->passwd);
	if (lmap->filter != NULL)
		free(lmap->filter);
	if (lmap->attr != NULL)
		free(lmap->attr);
	free(lmap);
	return;
}

int
modp_ldap(sip)
	search_info *sip;
{
        LDAPMAP *lmap;
        struct stat stbuf;
        struct spblk *spl;
        spkey_t symid;
        int rval;

        if (sip->file == NULL)
	  return 0; /* No file defined */

	if ((lmap = open_ldap(sip, "modp_ldap")) == NULL)
	  return 0; /* Fails to open ??? */

	if (lmap->simple_bind_result != LDAP_SUCCESS)
	  return 1; /* Rebind might help ?? */

        if (lstat(sip->file, &stbuf) < 0) {
	  fprintf(stderr, "modp_ldap: cannot fstat(\"%s\")!\n",
		  sip->file);
	  return 0;
        }

        symid = symbol_db((u_char *)sip->file, spt_files->symbols);
        spl = sp_lookup(symid, spt_modcheck);
        if (spl != NULL) {
	  rval = ((long)stbuf.st_mtime != (long)spl->data
		  || (long)stbuf.st_nlink != (long)spl->mark);
        } else
	  rval = 0;

        sp_install(symid, (u_char *)((long)stbuf.st_mtime),
                   stbuf.st_nlink, spt_modcheck);
        return rval;
}
#endif	/* USE_LDAP */


syntax highlighted by Code2HTML, v. 0.9.1