/*
 * $Id: e459f5272af2beb3820a87d2289b8988b4da347b $
 *
 * This file is part of nsmtp.
 *
 * Copyright (c) 2007, 2008 Niklas Olmes <niklas@noxa.de>
 *                          http://www.nevm.de/nsmtp/
 *                          NEVM-RIPE
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License Version 2
 * as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include <ldap.h>

#include "ntl.h"
#include "alloc.h"
#include "check.h"
#include "log.h"
#include "ldapauth.h"
#include "config.h"

static const char rcsid[] = "$Id: e459f5272af2beb3820a87d2289b8988b4da347b $";

extern int ntl_error;
extern const char *ntl_error_str;

/*
 * return codes:
 * -1 - unable to determine atm
 * 0  - okay
 * 1  - failed
 */
int
ldapauth(const char *user, const char *pass)
{
	LDAP *ld;
	LDAPMessage *res, *entry;
	NTL_ATTR *a;
	NTL_CFG cfg;
	char buf[1024];
	char dn[512];
	char localpart[256];
	const char *attrs[] = { "accountDisabled", NULL };
	const char *domain;
	size_t i;
	int e;

	if ((e = check_mailaddress(user, 2)) > 0)
		return 1;
	else if (e < 0)
		return -1;

	/* Note: check_mailaddress() assured that @ delimiter exists in user */

	i = 0;
	while (i < sizeof(localpart) - 1 && i < strlen(user) && user[i] != '@') {
		localpart[i] = user[i];
		i++;
	}
	localpart[i] = '\0';

	domain = strchr(user, '@') + 1;

	DEBUG(("ldapauth() => got localpart <%s> domain <%s>", localpart, domain));

	if (nsmc_ldap_cfgs == NULL || nsmc_ldap_cfgs[0] == NULL) {
		xfatal("fatal: internal error: ldapauth: no suitable LDAP config loaded, unable to auth");
		return -1;
	}

	if (strcasecmp(domain, "h3c.de") == 0)
		(void)snprintf(dn, sizeof(dn), "uid=%s,ou=noxamail.de,%s", localpart, nsmc_mail_basedn);
	else
		(void)snprintf(dn, sizeof(dn), "uid=%s,ou=%s,%s", localpart, domain, nsmc_mail_basedn);
	cfg.dn = dn;
	cfg.cred = nsm_strdup(pass);
	cfg.basedn = nsmc_ldap_cfgs[0]->basedn;
	cfg.host = nsmc_ldap_cfgs[0]->host;
	cfg.port = nsmc_ldap_cfgs[0]->port;
	cfg.tplc = NULL;
	cfg.secure = nsmc_ldap_cfgs[0]->secure;
	cfg.showop = 0;

	/*
	 * Authentificate to LDAP with uid=locapart,ou=domain and pass
	 */
	if ((ld = ntl_connect(&cfg)) == NULL) {
		nsm_free(cfg.cred);
		if (ntl_error == LDAP_INVALID_CREDENTIALS)
			return 1;
		else {
			xwarn("warn: ldapauth: failed for <%s> (error %d, \"%s\")", user, ntl_error, ntl_error_str);
			return -1;
		}
	}
	nsm_free(cfg.cred);
	(void)ldap_unbind(ld);

	/*
	 * Reconnect to LDAP with nsmtp Privileges to search for address
	 */
	if (nsmc_ldap_cfgs == NULL ||
	    ((ld = ntl_connect(nsmc_ldap_cfgs[0])) == NULL
	     && (ld = ntl_connect(nsmc_ldap_cfgs[1])) == NULL)) {
		if (ntl_error == LDAP_INVALID_CREDENTIALS)
			return 1;
		else {
			xwarn("warn: ldapauth: unable to connect to any ldap server for <%s> (error %d, \"%s\")", user, ntl_error, ntl_error_str);
			return -1;
		}
	}

	e = 0;
	/*
	 * Look for accountDisabled attribute
	 */
	(void)snprintf(buf, sizeof(buf), "(|(mail=%s)(mailAlternateAddress=%s))", user, user);
	if ((res = ntl_search(ld, nsmc_mail_basedn, LDAP_SCOPE_SUBTREE, buf, attrs)) != NULL
	    && (entry = ldap_first_entry(ld, res)) != NULL) {
		a = ntl_entry(ld, entry);
		if (ntl_lookup_val(a, attrs[0], "TRUE")) {
			xinfo("info: ldapauth: user <%s> is disabled", user);
			e = 1;
		}
		ntl_entry_free(a);
	} else {
		xwarn("warn: ldapauth: user <%s> not found in LDAP, this is considered IMPOSSIBLE, since we authed with it against LDAP", user);
		e = -1;
	}

	(void)ldap_msgfree(res);
	(void)ldap_unbind(ld);

	return e;
}
