/* $Header: copymsg.c,v 2.4 90/02/06 11:56:38 chip Exp $
 *
 * Take the message from standard input and write it to two temp files,
 * one for the header (including the empty line) and one for the body.
 *
 * $Log:	copymsg.c,v $
 * Revision 2.4  90/02/06  11:56:38  chip
 * Enforce MBX_MODE regardless of UMASK.
 * Enforce ordered logging with a log lockfile.
 * Revise log format.
 * 
 * Revision 2.3  89/11/27  14:18:29  network
 * Strip trailing spaces from date on From_ line.
 * 
 * Revision 2.2  89/09/29  18:17:53  network
 * Save message when delivery file produces no output,
 * unless delivery file output the "DROP" string.
 * Don't recopy temp files for sys and post-user delfiles.
 * 
 * Revision 2.1  89/06/09  12:25:16  network
 * Update RCS revisions.
 * 
 * Revision 1.9  89/06/09  12:23:40  network
 * Baseline for 2.0 release.
 * 
 */

#include "deliver.h"

/*
 * Macros.
 */

/* Does a string start with "From "? */

#define ISFROM(p) ((p)[0] == 'F' && (p)[1] == 'r' && (p)[2] == 'o' \
		&& (p)[3] == 'm' && (p)[4] == ' ')

/*----------------------------------------------------------------------
 * Copy the message on the standard input to two temp files:
 * one for the header and one for the body.
 */

int
copy_message()
{
	char    buf[BUFSIZ];
	FILE    *dfp[T_MAX];
	char    *p, *from_line, *fsender, *fdate, *fremote;
	int     t, b, empty_line;
	int     ret = 0;

	/*
	 * Create temporary files to hold the header and message body.
	 */

	for (t = T_HDR; t <= T_BODY; ++t)
	{
		int     fd;

		tfile[t] = tempfile();
		if ((tfd[t] = tcreate(tfile[t])) == -1)
			return -1;

		if ((fd = dup(tfd[t])) == -1)
		{
			syserr("dup %s fd", ttype[t]);
			return -1;
		}
		(void) lseek(fd, 0L, 0);
		if ((dfp[t] = fdopen(fd, "r+")) == NULL)
		{
			error("can't fdopen %s fd", ttype[t]);
			return -1;
		}
	}

	/* Debugging message for later examination of temp files. */

	if (verbose)
	{
		message("header=%s, body=%s\n",
			tfile[T_HDR], tfile[T_BODY]);
	}

	/*
	 * If there is a From_ line, find the sender name therein.
	 */

	from_line = NULL;
	fsender = fdate = fremote = NULL;

	b = (fgets(buf, GETSIZE(buf), stdin) ? TRUE : FALSE);

	if (b && ISFROM(buf) && (p = strchr(buf, '\n')) != NULL)
	{
		b = FALSE;

		/* Make a mungable copy of the From_ line */

		from_line = copystr(buf);
		if ((p = strchr(from_line, '\n')) != NULL)
			*p = 0;

		/* Find sender */

		p = from_line + 5;
		while (*p && isspace(*p))
			++p;
		fsender = p;
		while (*p && !isspace(*p))
			++p;
		if (*p)
			*p++ = 0;

		/* Date received should be around here somewhere */

		fdate = p;

		/* Find 'remote from' phrase (if any) */

		for (; (p = strchr(p, 'r')) != NULL; ++p)
		{
			if (strncmp(p, "remote from", 11) == 0)
			{
				*p = 0;
				p += 11;
				while (*p && isspace(*p))
					++p;
				if (*p)
					fremote = p;
				break;
			}
		}

		/*
		 * Advance to first non-space in date.
		 * Strip trailing spaces from date.
		 * If there is no date, clear the date pointer.
		 */

		while (*fdate && isspace(*fdate))
			++fdate;
		p = fdate + strlen(fdate);
		while (p > fdate && isspace(*(p - 1)))
			*--p = 0;
		if (*fdate == 0)
			fdate = NULL;

		/*
		 * If sender is missing, or if date is invalid,
		 * we consider the entire From_ line invalid.
		 */

		if (*fsender == 0
		 || (fdate != NULL && unctime(fdate) == -1) )
		{
			/* Ignore everything we found. */

			fsender = fdate = fremote = NULL;

			/* Print invalid From_ line in a harmless way. */

			(void) strcpy(from_line, buf);
			(void) strcpy(buf, "Invalid-UUCP-From: ");
			(void) strcat(buf, from_line);
			b = TRUE;
		}
	}

	/*
	 * Write a From_ line to the header file.
	 */

	/* if caller specified sender, use it */
	if (sender)
		; /* fine */

	/* else if we found a From_ line, use it */
	else if (fsender)
	{
		if (fremote)
		{
			sender = zalloc(strlen(fremote) + sizeof("!")
					+ strlen(fsender));
			(void) sprintf(sender, "%s!%s", fremote, fsender);
		}
		else
			sender = copystr(fsender);
	}

	/* else use our real ID */
	else
		sender = real_ct->ct_name;

	/* debugging message */

	if (verbose)
		message("copy_msg: sender is \"%s\"\n", sender);

	/*
	 * Finally!  Write the From_ line.
	 */

	(void) fputs("From ", dfp[T_HDR]);
	(void) fputs(sender, dfp[T_HDR]);
	(void) fputc(' ', dfp[T_HDR]);
	if (fdate)
	{
		(void) fputs(fdate, dfp[T_HDR]);
		(void) fputc('\n', dfp[T_HDR]);
	}
	else
	{
		time_t  now;

		(void) time(&now);
		(void) fputs(ctime(&now), dfp[T_HDR]);
	}

	/*
	 * Free the From_ line if we allocated a copy of it.
	 */

	if (from_line)
		free(from_line);

	/*
	 * Copy the rest of the header (if any).
	 */

	for (; !feof(stdin) && !ferror(stdin); b = FALSE)
	{
		if (!b)
		{
			if (fgets(buf, GETSIZE(buf), stdin))
				b = TRUE;
			else
				break;
		}

		/* Empty line means "end of header" */

		if (buf[0] == '\n')
		{
			b = FALSE;    /* Don't put this line in the body. */
			break;
		}

		/*
		 * A line too long to fit in buf[] can't be a header line.
		 * At least, that's my opinion... :-)
		 */

		if (!strchr(buf, '\n'))
			break;

		/*
		 * If line begins with whitespace, it's a continuation.
		 * Else if line begins with From_ or '>', prepend '>'.
		 * Else if line doesn't look like a header, this must
		 * be the beginning of the body.
		 */

		if (isspace(buf[0]))
			;               /* continuation */
		else if (ISFROM(buf) || (buf[0] == '>'))
			(void) fputc('>', dfp[T_HDR]);
		else
		{
			/* look for the colon on a header label */

			p = buf;
			while (isalpha(*p) || *p == '-')
				++p;
			if ((p == buf) || (*p != ':'))
				break;  /* Not a header line! */
		}

		/* Write the line to the header file. */

		(void) fputs(buf, dfp[T_HDR]);
	}

	/*
	 * End the header file with a blank line.
	 * This enables us to simply concatenate it with the body file
	 * to produce a valid message.
	 */

	(void) fputc('\n', dfp[T_HDR]);

	/*
	 * Copy the body (if any).
	 */

	empty_line = FALSE;
	for (; !feof(stdin) && !ferror(stdin); b = FALSE)
	{
		if (!b)
		{
			if (fgets(buf, GETSIZE(buf), stdin))
				b = TRUE;
			else
				break;
		}

		if (ISFROM(buf))
			(void) fputc('>', dfp[T_BODY]);
		(void) fputs(buf, dfp[T_BODY]);

		empty_line = (buf[0] == '\n');

		/*
		 * Output the rest of a very long line.
		 * We do this here, instead of going around the loop,
		 * in order to avoid misinterpreting From_ strings
		 * that may be found in long lines.
		 */

		while (!strchr(buf, '\n')
		    && !feof(stdin)
		    && !ferror(stdin)
		    && fgets(buf, GETSIZE(buf), stdin))
			(void) fputs(buf, dfp[T_BODY]);
	}

	/* Ensure that the body ends with a blank line. */

	if (! empty_line)
		(void) fputc('\n', dfp[T_BODY]);

	/*
	 * If we encountered any trouble writing to the temp files,
	 * let's not keep it secret.
	 */

	for (t = T_HDR; t <= T_BODY; ++t)
	{
		if (ferror(dfp[t]))
		{
			error("error writing to %s file %s\n",
				ttype[t], tfile[t]);
			ret = -1;
		}

		(void) fclose(dfp[t]);
	}

	/* Return error/success. */

	return ret;
}

/*----------------------------------------------------------------------
 * Don't bother copying message.
 * Put the original names in the environment.
 */

int
dont_copy()
{
	int     r, t;

	for (r = T_HDR, t = T_HDRCOPY; r <= T_BODY; ++r, ++t)
	{
		if (tenv[t] && tfile[r])
			alloc_env(tenv[t], tfile[r]);
	}

	if (verbose)
	{
		message("dont_copy: header is %s, body is %s\n",
			tfile[T_HDR], tfile[T_BODY]);
	}

	return 0;
}

/*----------------------------------------------------------------------
 * Create another copy of each temp file, for security reasons.
 * Also, put the names of the copies in the environment.
 */

int
copy_again()
{
	int     r, t;

	for (r = T_HDR, t = T_HDRCOPY; r <= T_BODY; ++r, ++t)
	{
		/*
		 * If the file exists, remove it but keep its name.
		 * Otherwise, make a new name and put that name in
		 * the environment.
		 */

		if (tfile[t])
			(void) unlink(tfile[t]);
		else
		{
			tfile[t] = tempfile();
			if (tenv[t])
				alloc_env(tenv[t], tfile[t]);
		}

		/*
		 * Create the file and copy the contents of the
		 * original file to it.
		 */

		if (tfd[t] != -1)
			(void) close(tfd[t]);

		if ((tfd[t] = tcreate(tfile[t])) == -1)
			return -1;

		(void) lseek(tfd[r], 0L, 0);
		if (copyfd(tfd[r], tfd[t]) < 0)
			return -1;
	}

	if (verbose)
	{
		message("copy_again: header to %s, body to %s\n",
			tfile[T_HDRCOPY], tfile[T_BODYCOPY]);
	}

	return 0;
}

/*----------------------------------------------------------------------
 * Copy a file via file descriptors.
 */

int
copyfd(src_fd, dest_fd)
int     src_fd;
int     dest_fd;
{
	char    buf[BUFSIZ];
	int     rd, wr;

	while ((rd = read(src_fd, buf, sizeof(buf))) > 0)
	{
		if ((wr = write(dest_fd, buf, (unsigned) rd)) != rd)
		{
			if (wr == -1)
				syserr("can't write in copyfd");
			else
				error("write error -- disk full?\n");
			return -1;
		}
	}

	return 0;
}

