/*
 * $Id: 0f2ad0074d262b9847473e26f07dc695c82d7aab $
 *
 * 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 <stdlib.h>
#include <unistd.h>
#include <pwd.h>
#include <grp.h>
#include <string.h>
#include <errno.h>

#include "ldap.h"

#include "ntl.h"
#include "ntlc.h"
#include "log.h"
#include "dns.h"
#include "misc.h"
#include "alloc.h"
#include "config.h"

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

extern int ntl_error;

void
drop_privileges(unsigned int uid, unsigned int gid, const char *chrt)
{
	struct passwd *pwd;

	/* resolve something before chroot, so that resolver config is loaded */
	(void)resolve(nsmc_name);

	if ((pwd = getpwuid(uid)) == NULL)
		xdie("fatal: drop_privileges() => getpwuid(%d) failed (non-existent UID entry?): \"%s\"", uid, strerror(errno));

	if (chrt != NULL) {
		if (chdir(chrt) != 0)
			xdie("fatal: drop_privileges() => chdir(%s) failed: \"%s\"", chrt, strerror(errno));
		if (chroot(chrt) != 0)
			xdie("fatal: drop_privileges() => chroot(%s) failed: \"%s\"", chrt, strerror(errno));
		xinfo("info: drop_privileges() => chroot()ed to <%s>", chrt);
	}

	if (setgid(gid) != 0)
		xdie("fatal: drop_privileges() => setgid(%d) failed: \"%s\"", gid, strerror(errno));
	if (initgroups(pwd->pw_name, gid) != 0)
		xdie("fatal: drop_privileges() => initgroups(%s) failed: \"%s\"", pwd->pw_name, strerror(errno));
	if (setuid(uid) != 0)
		xdie("fatal: drop_privileges() => setuid(%d) failed: \"%s\"", uid, strerror(errno));

	xinfo("info: drop_privileges() => now running as UID(%d), EUID(%d), GID(%d), EGID(%d)", getuid(), geteuid(), getgid(), getegid());
}

void
daemonize(void)
{

	if (daemon(1, 0) != 0)
		xdie("fatal: daemonize() => daemon() failed: \"%s\"", strerror(errno));
}

void
read_ldap_cfg(void)
{

	if (nsmc_ldap_cfgs != NULL)
		xdie("fatal: read_ldap_cfg() => LDAP config already loaded?");

	nsmc_ldap_cfgs = nsm_calloc(3, sizeof(NTL_CFG *));
	nsmc_ldap_cfgs[2] = NULL;

	if ((nsmc_ldap_cfgs[0] = ntlc_cfg_read(nsmc_ldap_cfg_file, "primary")) == NULL
	    || (nsmc_ldap_cfgs[1] = ntlc_cfg_read(nsmc_ldap_cfg_file, "secondary")) == NULL) {
		ntlc_perror("read_ldap_cfg");
		xdie("fatal: unable to read LDAP config");
	}
}

void
free_ldap_cfg(void)
{
	int i;

	if (nsmc_ldap_cfgs == NULL)
		return;

	for (i = 0; nsmc_ldap_cfgs[i] != NULL; i++) {
		ntlc_cfg_free(nsmc_ldap_cfgs[i]);
		nsmc_ldap_cfgs[i] = NULL;
	}

	nsm_free(nsmc_ldap_cfgs);
}

void
write_pid_file(const char *name)
{
	FILE *fp;
	char buf[128];

	(void)snprintf(buf, sizeof(buf), "%s/%s", nsmc_pidfile_dir, name);
	if ((fp = fopen(buf, "w")) == NULL)
		xdie("fatal: write_pid_file() => unable to write pid file for <%s>, fopen(%s) failed: \"%s\"", name, buf, strerror(errno));

	(void)snprintf(buf, sizeof(buf), "%u", getpid());
	if (fwrite(buf, strlen(buf), 1, fp) != 1) {
		(void)fclose(fp);
		xdie("fatal: write_pid_file() => unable to write pid file for <%s>, fwrite() failed: \"%s\"", name, strerror(errno));
	}

	(void)fclose(fp);
}

void
ldap_connect(LDAP **ld)
{

	if (nsmc_ldap_cfgs == NULL ||
	    ((*ld = ntl_connect(nsmc_ldap_cfgs[0])) == NULL
	     && (*ld = ntl_connect(nsmc_ldap_cfgs[1])) == NULL))
		xdie("fatal: ldap_connect() => unable to connect to any LDAP server");
}

int
ldap_reconnect(LDAP **ld)
{

	if (*ld != NULL)
		(void)ldap_ld_free(*ld, 0, NULL, NULL);
	ntl_error = 0;

	xwarn("warn: ldap_reconnect() => LDAP connection seems to have gone away, reconnecting");

	if (nsmc_ldap_cfgs == NULL ||
	    ((*ld = ntl_connect(nsmc_ldap_cfgs[0])) == NULL
	     && (*ld = ntl_connect(nsmc_ldap_cfgs[1])) == NULL)) {
		xwarn("warn: ldap_reconnect() => unable to connect to any LDAP server at reconnect, suggest waiting");
		return 1;
	}

	return 0;
}
