/*
 * $Id: nspamc.c,v 1.1 2006/12/12 14:28:47 niklas Exp niklas $
 *
 * Copyright (c) 2006 Niklas Olmes <niklas@noxa.de>
 */

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

#include <netdb.h>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>

#define E_OK 0
#define E_RES 1
#define E_CON 2
#define E_LARGE 4
#define E_OPEN 5
#define E_SPAMD 6
#define E_LEN 7

static const char *cmd = "PROCESS SPAMC/1.2\r\n";
static const char *hosts[] = {
	"127.0.0.1",
	NULL
};

static in_addr_t
resolve(const char *h)
{
	struct hostent *hp;
	in_addr_t ip;

	if ((ip = inet_addr(h)) != INADDR_NONE)
		return ip;

	if ((hp = gethostbyname(h)) == NULL)
		return INADDR_NONE;
	else {
		(void) memcpy(&ip, hp->h_addr, hp->h_length);
		return ip;
	}
}

static char
sgetc(int s)
{
	char c;

	if (read(s, &c, 1) != 1 || c < 1)
		return -1;
	else
		return c;
}

static size_t
getline(int s, char buf[], int len)
{
	int c;
	size_t i;

	i = 0;
	while (--len > 0) {
		c = sgetc(s);
		if (c == '\n' || c == -1)
			break;
		else if (c != '\r')
			buf[i++] = c;
	}
	buf[i] = '\0';
	return i;
}

static int
doscan(int argc, char *argv[])
{
	struct sockaddr_in addr;
	unsigned long ip;
	void *foo;
	int s;
	char strbuf[2048];
	unsigned char readbuf[8096];
	unsigned char databuf[1024*1024*1]; /* 1 MB */
	char *user = NULL;
	ssize_t r = 0;
	ssize_t len = 0;
#ifdef USE_FD
	int in, out;
	unsigned char *p;
#else
	int c;
	ssize_t count;
#endif

	if ((ip = resolve(hosts[0])) == INADDR_NONE)
		return E_RES;

	(void) memset(&addr, 0, sizeof(addr));
	addr.sin_addr.s_addr = ip;
	addr.sin_port = htons(784);
	addr.sin_family = AF_INET;

	if (
		((s = socket(AF_INET, SOCK_STREAM, 0)) == -1)
		|| (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &foo, sizeof(foo)) != 0)
		|| (connect(s, (struct sockaddr *) & addr, sizeof(addr)) == -1)
	)
		return E_CON;

#ifdef USE_FD
	in = open("/dev/stdin", O_RDONLY, 0);
	out = open("/dev/stdout", O_WRONLY, 0);
	if (in < 0 || out < 0) {
		return E_OPEN;
	}

	p = databuf;
	while ((r = read(in, readbuf, sizeof(readbuf))) > 0) {
		if (sizeof(databuf) - len - r < 1) {
			(void) write(out, databuf, len);
			(void) write(out, readbuf, r);
			return E_LARGE;
		}

		(void) memcpy(p, readbuf, r);
		p += r;
		len += r;
	}
#else
	while (len < sizeof(databuf) && (c = getchar()) != EOF)
		databuf[len++] = c;
	if (len >= sizeof(databuf)) {
		count = 0;
		while (count < sizeof(databuf))
			(void) putchar(databuf[count++]);
		return E_LARGE;
	}
#endif

	if (argc > 2)
		user = argv[2];
	else
		user = strdup("");

	(void) snprintf(strbuf, sizeof(strbuf), "Content-length: %d\r\nUser: %s\r\n\r\n", len, user);
	(void) write(s, cmd, strlen(cmd));
	(void) write(s, strbuf, strlen(strbuf));

	(void) write(s, databuf, len);

	(void) getline(s, strbuf, sizeof(strbuf));
	if (
		(strncmp(strbuf, "SPAMD/1.", strlen("SPAMD/1.")) != 0)
		|| strbuf[strlen(strbuf)-1] != 'K'
		|| strbuf[strlen(strbuf)-2] != 'O'
		|| strbuf[strlen(strbuf)-3] != '_'
	)
		return E_SPAMD;

	(void) getline(s, strbuf, sizeof(strbuf));
	if (strncmp(strbuf, "Content", strlen("Content")) != 0)
		return E_SPAMD;

	(void) getline(s, strbuf, sizeof(strbuf));
	if (strlen(strbuf) != 0)
		return E_SPAMD;

	len = 0;
	while ((r = read(s, readbuf, sizeof(readbuf))) > 0) {
		len += r;
#ifdef USE_FD
		(void) write(out, readbuf, r);
#else
		c = 0;
		while (c < r)
			(void) putchar(readbuf[c++]);
#endif
	}

	return E_OK;
}

int
main(int argc, char *argv[])
{
	int e, c;

	if ((e = doscan(argc, argv)) != E_OK) {
		if (e != E_SPAMD) {
			while ((c = getchar()) != EOF)
				(void) putchar(c);
		}
	}

	return 0;
}
