/*
 *  splitmb by Davide Libenzi (mailbox file format splitter)
 *  Copyright (C) 2002  Davide Libenzi
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 *  Davide Libenzi <davidel@xmailserver.org>
 *
 */

#include <sys/time.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

static unsigned long long get_mstime(void)
{
        struct timeval tm;

        gettimeofday(&tm, NULL);

        return tm.tv_sec * 1000ULL + tm.tv_usec / 1000;
}

static FILE *xfopen(char const *path, char const *mode)
{
	FILE *file;

	if ((file = fopen(path, mode)) == NULL) {
		perror(path);
		exit(2);
	}

	return file;
}

static int is_rfc822_atext(int c)
{
	return isalpha(c) || isdigit(c) || strchr("!#$%&'*+-/=?^_`{|}~", c) != NULL;
}

static char const *is_dot_atom(char const *str, char const *top)
{
	for (;;) {
		char const *atm = str;

		for (; str < top && is_rfc822_atext(*str); str++);
		if (atm == str)
			return NULL;
		if (str == top || *str != '.')
			break;
		str++;
	}

	return str;
}

static char const *parse_hostname(char const *hname, char const *top)
{
	return is_dot_atom(hname, top);
}

static int is_valid_host(char const *hname, char const *top)
{
	char const *eoh;

	if ((eoh = parse_hostname(hname, top)) == NULL)
		return 0;

	return eoh == top;
}

static int is_valid_addr_spec(char const *addr, char const *top)
{
	char const *caddr;

	if ((caddr = is_dot_atom(addr, top)) == NULL)
		return 0;
	if (caddr == top)
		return 1;
	if (*caddr++ != '@')
		return 0;

	return is_valid_host(caddr, top);
}

static int is_split_msg(char const *ln)
{
	unsigned int d, h, m, s, y;
	char addr[256], wday[8], ymon[8];

	return sscanf(ln, "From %255s %3s %3s %u %u:%u:%u %u",
		      addr, wday, ymon, &d, &h, &m, &s, &y) == 8 &&
		is_valid_addr_spec(addr, addr + strlen(addr));
}

static void usage(char *prg)
{
	fprintf(stderr, "use: %s [-i MBPATH] [-d OUTDIR] [-X]\n", prg);
	exit(1);
}

int main(int argc, char *argv[])
{
	int i, lnum, add_xfile = 0;
	unsigned long long mstm;
	FILE *ofil, *ifil = stdin;
	char const *ipath = NULL, *outdir = ".";
	char hname[256], lnbuf[4096];

	for (i = 1; i < argc; i++) {
		if (strcmp(argv[i], "-d") == 0) {
			if (++i < argc)
				outdir = argv[i];
		} else if (strcmp(argv[i], "-i") == 0) {
			if (++i < argc)
				ipath = argv[i];
		} else if (strcmp(argv[i], "-X") == 0)
			add_xfile = 1;
		else
			usage(argv[0]);
	}
	if (ipath != NULL)
		ifil = xfopen(ipath, "r");
	gethostname(hname, sizeof(hname));
	for (ofil = NULL, lnum = 1; fgets(lnbuf, sizeof(lnbuf) - 1, ifil);
	     lnum++) {
		if (strncmp(lnbuf, "From ", 5) == 0 && is_split_msg(lnbuf)) {
			if (ofil != NULL)
				fclose(ofil);
			mstm = get_mstime();
			snprintf(lnbuf, sizeof(lnbuf), "%s/%llu.%d.%u.%s",
				 outdir, mstm, lnum, getpid(), hname);
			ofil = xfopen(lnbuf, "w");
			if (add_xfile)
				fprintf(ofil, "X-SplitMB-File: %llu.%d.%u.%s\n",
					mstm, lnum, getpid(), hname);
		} else if (ofil != NULL)
			fputs(lnbuf, ofil);
	}
	if (ofil != NULL)
		fclose(ofil);
	if (ifil != stdin)
		fclose(ifil);

	return 0;
}

