/*
** rcvstore.c -- asynchronously add mail to a folder
**
** This code is Copyright (c) 2002, by the authors of nmh.  See the
** COPYRIGHT file in the root directory of the nmh distribution for
** complete copyright information.
*/

#include <h/mh.h>
#include <h/utils.h>
#include <fcntl.h>
#include <h/signals.h>
#include <errno.h>
#include <signal.h>
#include <unistd.h>
#include <sys/stat.h>
#include <locale.h>
#include <sysexits.h>

static struct swit switches[] = {
#define CRETSW  0
	{ "create",   0 },
#define NCRETSW  1
	{ "nocreate", 2 },
#define UNSEENSW  2
	{ "unseen",   0 },
#define NUNSEENSW  3
	{ "nounseen", 2 },
#define PUBSW  4
	{ "public",   0 },
#define NPUBSW  5
	{ "nopublic", 2 },
#define ZEROSW  6
	{ "zero",     0 },
#define NZEROSW  7
	{ "nozero",   2 },
#define SEQSW  8
	{ "sequence name", 0 },
#define VERSIONSW  9
	{ "Version", 0 },
#define HELPSW  10
	{ "help", 0 },
	{ NULL, 0 }
};

char *version=VERSION;

/*
** name of temporary file to store incoming message
*/
static char *tmpfilenam = NULL;

void unlink_done();
static void fix_mbox(int out, char *ofile);

int
main(int argc, char **argv)
{
	int publicsw = -1, zerosw = 0;
	int create = 1, unseensw = 1;
	int fd, msgnum;
	size_t seqp = 0;
	char *cp, *maildir, *folder = NULL, buf[BUFSIZ];
	char **argp, **arguments, *seqs[NUMATTRS+1];
	struct msgs *mp;
	struct stat st;

	if (atexit(unlink_done) != 0) {
		adios(EX_OSERR, NULL, "atexit failed");
	}

	setlocale(LC_ALL, "");
	invo_name = mhbasename(argv[0]);

	/* read user profile/context */
	context_read();

	arguments = getarguments(invo_name, argc, argv, 1);
	argp = arguments;

	/* parse arguments */
	while ((cp = *argp++)) {
		if (*cp == '-') {
			switch (smatch(++cp, switches)) {
			case AMBIGSW:
				ambigsw(cp, switches);
				exit(EX_USAGE);
			case UNKWNSW:
				adios(EX_USAGE, NULL, "-%s unknown", cp);

			case HELPSW:
				snprintf(buf, sizeof(buf),
						"%s [+folder] [switches]",
						invo_name);
				print_help(buf, switches, 1);
				exit(argc == 2 ? EX_OK : EX_USAGE);
			case VERSIONSW:
				print_version(invo_name);
				exit(argc == 2 ? EX_OK : EX_USAGE);

			case SEQSW:
				if (!(cp = *argp++) || *cp == '-')
					adios(EX_USAGE, NULL, "missing argument name to %s", argp[-2]);

				/* check if too many sequences specified */
				if (seqp >= NUMATTRS)
					adios(EX_USAGE, NULL, "too many sequences (more than %d) specified", NUMATTRS);
				seqs[seqp++] = cp;
				continue;

			case UNSEENSW:
				unseensw = 1;
				continue;
			case NUNSEENSW:
				unseensw = 0;
				continue;

			case PUBSW:
				publicsw = 1;
				continue;
			case NPUBSW:
				publicsw = 0;
				continue;

			case ZEROSW:
				zerosw++;
				continue;
			case NZEROSW:
				zerosw = 0;
				continue;

			case CRETSW:
				create++;
				continue;
			case NCRETSW:
				create = 0;
				continue;
			}
		}
		if (*cp == '+' || *cp == '@') {
			if (folder)
				adios(EX_USAGE, NULL, "only one folder at a time!");
			else
				folder = mh_xstrdup(expandfol(cp));
		} else {
			adios(EX_USAGE, NULL, "usage: %s [+folder] [switches]",
					invo_name);
		}
	}

	seqs[seqp] = NULL;  /* NULL terminate list of sequences */

	/* if no folder is given, use default folder */
	if (!folder)
		folder = getdeffol();
	maildir = toabsdir(folder);

	/* check if folder exists */
	if (stat(maildir, &st) == NOTOK) {
		if (errno != ENOENT)
			adios(EX_IOERR, maildir, "error on folder");
		if (!create)
			adios(EX_USAGE, NULL, "folder %s doesn't exist", maildir);
		if (!makedir(maildir))
			adios(EX_CANTCREAT, NULL, "unable to create folder %s", maildir);
	}

	if (chdir(maildir) == NOTOK)
		adios(EX_OSERR, maildir, "unable to change directory to");

	/* ignore a few signals */
	SIGNAL(SIGHUP, SIG_IGN);
	SIGNAL(SIGINT, SIG_IGN);
	SIGNAL(SIGQUIT, SIG_IGN);
	SIGNAL(SIGTERM, SIG_IGN);

	/* create a temporary file */
	tmpfilenam = m_mktemp(invo_name, &fd, NULL);
	if (tmpfilenam == NULL) {
		adios(EX_CANTCREAT, "rcvstore", "unable to create temporary file");
	}
	chmod(tmpfilenam, m_gmprot());

	/* check if incoming mail is in mbox-format */
	fix_mbox(fd, tmpfilenam);

	/* copy the message from stdin into temp file */
	cpydata(fileno(stdin), fd, "standard input", tmpfilenam);

	if (fstat(fd, &st) == NOTOK) {
		unlink(tmpfilenam);
		adios(EX_IOERR, tmpfilenam, "unable to fstat");
	}
	if (close(fd) == NOTOK)
		adios(EX_IOERR, tmpfilenam, "error closing");

	/* don't add file if it is empty */
	if (st.st_size == 0) {
		unlink(tmpfilenam);
		advise(NULL, "empty file");
		exit(EX_OK);
	}

	/*
	** read folder and create message structure
	*/
	if (!(mp = folder_read(folder)))
		adios(EX_IOERR, NULL, "unable to read folder %s", folder);

	/*
	** Link message into folder, and possibly add
	** to the Unseen-Sequence's.
	*/
	if ((msgnum = folder_addmsg(&mp, tmpfilenam, 0, unseensw, 0, 0, NULL))
			== -1)
		exit(EX_SOFTWARE);

	/*
	** Add the message to any extra sequences
	** that have been specified.
	*/
	for (seqp = 0; seqs[seqp]; seqp++) {
		if (!seq_addmsg(mp, seqs[seqp], msgnum, publicsw, zerosw))
			exit(EX_SOFTWARE);
	}

	seq_setunseen(mp, 1);  /* add new msgs to unseen sequences */
	seq_save(mp);  /* synchronize and save message sequences */
	folder_free(mp);  /* free folder/message structure */

	context_save();  /* save the global context file */
	unlink(tmpfilenam);  /* remove temporary file */
	tmpfilenam = NULL;

	return EX_OK;
}

static void
fix_mbox(int out, char *outfile)
{
	char mbox[5];
	int ret;

	if ((ret = read(fileno(stdin), mbox, sizeof(mbox))) != sizeof(mbox)) {
		if (ret == -1) {
			adios(EX_IOERR, "standard input", "error reading");
		}
		return;
	}

	if (strncmp(mbox, "From ", sizeof(mbox))==0) {
		do {
			if ((ret = read(fileno(stdin), mbox, 1)) != 1) {
				if (ret == -1) {
					adios(EX_IOERR, "standard input", "error reading");
				}
				return;
			}
		} while (*mbox != '\n');
	} else {
		if (write(out, mbox, sizeof(mbox)) != sizeof(mbox)) {
			adios(EX_IOERR, outfile, "error writing");
		}
	}
}

/*
** Clean up and exit
*/
void
unlink_done()
{
	if (tmpfilenam && *tmpfilenam) {
		unlink(tmpfilenam);
	}
}
