/*
 * $Id: 1aeea699e6a0a48a78fdddbe3b9084b8e6cf3fef $
 *
 * 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 <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <dirent.h>
#include <errno.h>
#include <string.h>
#include <strings.h>
#include <assert.h>

#include "log.h"
#include "check.h"
#include "relay.h"
#include "config.h"

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

void
cleanup_relay(void)
{
	DIR *dp;
	struct dirent *entry;
	struct stat sb;
	char full[128];
	time_t t;

	if ((dp = opendir(nsmc_relay_temporary)) == NULL) {
		xwarn("warn: do_cleanup_relay() => unable to open relay dir <%s>", nsmc_relay_temporary);
		return;
	}

	t = time(NULL);

	while((entry = readdir(dp)) != NULL) {
		if (strncmp(entry->d_name, ".", 1) == 0)
			continue;

		(void)snprintf(full, sizeof(full), "%s/%s", nsmc_relay_temporary, entry->d_name);
		if (stat(full, &sb) != 0) {
			xwarn("warn: do_cleanup_relay() => stat(%s) failed: \"%s\"", full, strerror(errno));
			continue;
		}
		if ((sb.st_mode & S_IFMT) != S_IFREG) {
			xwarn("warn: do_cleanup_relay() => <%s> is not a regular file", full);
			continue;
		}

		if (sb.st_mtime + nsmc_relay_timeout < t && unlink(full) != 0) {
			xwarn("warn: do_cleanup_relay() => unlink(%s) failed: \"%s\"", full, strerror(errno));
			continue;
		}
	}

	(void)closedir(dp);
}

int
is_sane_relaytree(void)
{
	struct stat sb;

	#define DOD(dirname)	if (stat(dirname, &sb) != 0) { \
					xwarn("warn: is_sane_relaytree() => stat(%s) failed: \"%s\"", dirname, strerror(errno)); \
					return 0; \
				} \
				if ((sb.st_mode & S_IFMT) != S_IFDIR) { \
					xwarn("warn: is_sane_relaytree() => <%s> is not a directory", dirname); \
					return 0; \
				}

	if (stat(nsmc_relay_root, &sb) != 0) {
		xwarn("warn: is_sane_relaytree() => stat(%s) failed: \"%s\"", nsmc_relay_root, strerror(errno));
		return 0;
	}

	DOD(nsmc_relay_temporary);
	DOD(nsmc_relay_permanent);
	DOD(nsmc_relay_banned);

	return 1;
}

/*
 * return codes:
 * -1  - unable to determine atm
 * 0   - okay, may relay
 * 1   - no way
 */
int
relaystatus(const char *ip)
{
	struct stat sb;
	char fn[256];

	assert(ip != NULL);
	assert(*ip != '\0');

	if (!is_sane_relaytree())
		return -1;

	(void)snprintf(fn, sizeof(fn), "%s/%s", nsmc_relay_banned, ip);
	if (stat(fn, &sb) == 0)
		return 1;

	(void)snprintf(fn, sizeof(fn), "%s/%s", nsmc_relay_temporary, ip);
	if (stat(fn, &sb) == 0)
		return 0;
	(void)snprintf(fn, sizeof(fn), "%s/%s", nsmc_relay_permanent, ip);
	if (stat(fn, &sb) == 0)
		return 0;

	return 1;
}

void
temp_allow_relay(const char *ip)
{
	FILE *fp;
	struct stat sb;
	char fn[256];
	const char *ip4;

	assert(ip != NULL);
	assert(*ip != '\0');

	if (!is_sane_relaytree())
		return;

	if (!is_sane_ip(ip)) {
		xwarn("warn: temp_allow_relay() => ip=[%s] is not considered a valid ip address by is_sane_ip(), will not allow relaying", ip);
		return;
	}

	ip4 = ip;
	if (strncasecmp(ip, IPV6_V4PREFIX, strlen(IPV6_V4PREFIX)) == 0)
		ip4 = ip + strlen(IPV6_V4PREFIX);

	(void)snprintf(fn, sizeof(fn), "%s/%s", nsmc_relay_temporary, ip4);
	if (stat(fn, &sb) == 0) {
		if ((sb.st_mode & S_IFMT) != S_IFREG) {
			xwarn("warn: temp_allow_relay() => file <%s> exists and is not a regular file", fn);
			return;
		}
	}
	if ((fp = fopen(fn, "a")) == NULL) {
		xwarn("warn: temp_allow_relay() => fopen(%s, a) failed: \"%s\"", fn, strerror(errno));
		return;
	}
	(void)fprintf(fp, "%lu\n", time(NULL));
	(void)fclose(fp);
}
