/* $Header: uucp.c,v 2.6 90/05/03 10:35:51 chip Exp $
 *
 * Handle mail destined for other hosts via UUCP.
 * Deliver is intended as a very low-level program, so we don't
 * do anything fancy here.  We just hand the message to uux.
 *
 * $Log:	uucp.c,v $
 * Revision 2.6  90/05/03  10:35:51  chip
 * Quiet lint.
 * 
 * Revision 2.5  90/05/03  10:26:21  chip
 * Combine destinations with a common first hop.
 * 
 * Revision 2.4  90/02/23  14:16:54  chip
 * Support "#!" in delivery files.
 * Support "user|program" and "user?error" from delivery files.
 * Improve debugging and error message formatting.
 * Rearrange code for clarity.
 * 
 * Revision 2.3  89/12/19  16:26:41  network
 * Execute UUCP in real, not effective, context.
 * This allows users to cancel their UUCP jobs.
 * 
 * Revision 2.2  89/11/01  12:31:17  network
 * Use the new exists() function.
 * 
 * Revision 2.1  89/06/09  12:25:44  network
 * Update RCS revisions.
 * 
 * Revision 1.5  89/06/09  12:24:02  network
 * Baseline for 2.0 release.
 * 
 */

#include "deliver.h"

/*
 * Local functions.
 */

static	char	*find_uux();
static  int     uucp_copy();

/*----------------------------------------------------------------------
 * Send mail to UUCP addresses (if any).
 * Return count of UUCP addresses for which delivery was attempted.
 */

int
uucp_deliver()
{
	DEST    *d;
	char    *uav[UUX_ARGCOUNT + 8];     /* arguments for execv() */
	char	**av;			    /* remote addresses in uav[] */
	DEST	*dv[UUX_ARGCOUNT];	    /* destinations in av[] */
	char    rmail[UUCP_NAMESIZE + 8];   /* "sysname!rmail" */
	char    *uux;
	int	uucpcount;

	if ((uux = find_uux()) == NULL)
		return -1;

	av = uav;
	*av++ = "uux";
#ifdef UUX_OPTS
	*av++ = UUX_OPTS;
#endif
	*av++ = "-";
	*av++ = rmail;

	/*
	 * Look for a UUCP address that is "working".  If we find one,
	 * then we scan the rest of the list for other UUCP addresses
	 * that begin with the same first hop.  If we find any, then
	 * we handle them too.  Note that as we continue scanning,
	 * we'll find those same addresses again; that's okay, though,
	 * because their status fields will report that they're
	 * already done.  Cool, eh?
	 */

	uucpcount = 0;

	for (d = first_dest(); d; d = next_dest(d))
	{
		FILE    *uux_fp;
		DEST	*ud;
		DERROR	e;
		char	*bang;
		int	namesize, argcount, argsize, problem, a;

		if (d->d_class != CL_UUCP || d->d_state != ST_WORKING)
			continue;

		++uucpcount;

		if (printaddrs)
			(void) printf("%s\n", d->d_name);

		if (dryrun)
		{
			d->d_state = ST_DONE;
			continue;
		}

		/*
		 * This is the first destination with the given system
		 * as the first hop.  Generate the rmail command.
		 */

		if ((bang = strchr(d->d_name, '!')) == NULL
		 || (namesize = bang - d->d_name) > UUCP_NAMESIZE)
		{
			dest_err(d, E_NSHOST);
			continue;
		}

		(void) strncpy(rmail, d->d_name, namesize);
		(void) strcpy(rmail + namesize, "!rmail");

		/*
		 * Now keep looking for addresses until a limit is reached,
		 * either max arguments or max argument size.
		 * We'll find them again, but their statuses will prevent us
		 * from trying to mail to them twice.
		 */

		argcount = 0;
		argsize = 0;
		for (ud = d; ud; ud = next_dest(ud))
		{
			char *rest, *arg;

			if (ud->d_class != CL_UUCP
			 || ud->d_state != ST_WORKING)
				continue;

			if (strncmp(ud->d_name, d->d_name, namesize + 1) != 0)
				continue;

			/*
			 * We have a match!  (Or, it could be the first one.)
			 * Be sure we don't exceed our configured maxima,
			 * except for the first address, which always goes.
			 */

			rest = ud->d_name + namesize + 1;

			if (argcount > 0)
			{
				if (argcount + 1 > UUX_ARGCOUNT
				 || argsize + strlen(rest) + 2 > UUX_ARGSIZE)
					break;
			}

			/*
			 * Generate a uux argument and save the destination.
			 */

			arg = zalloc((unsigned) 3 + strlen(rest));
			(void) sprintf(arg, "(%s)", rest);

			av[argcount] = arg;
			dv[argcount] = ud;

			/*
			 * Keep track of arg count and total size.
			 */

			++argcount;
			argsize += strlen(arg);
		}

		av[argcount] = NULL;

		/*
		 * Do the dirty deed.
		 * We have to remember the error code as a variable,
		 * since it may apply to multiple destinations.
		 */

		problem = 0;
		e = E_PIPE;	/* default error */

		if ((uux_fp = ct_fopenv(real_ct, uux, uav, "w")) == NULL)
			problem = 1;
		else
		{
			if (uucp_copy(uux_fp) < 0)
				problem = 1;

			if (ct_fclose(uux_fp))
			{
				/* "No such host" overrides piping problems. */
				e = E_NSHOST;
				problem = 1;
			}
		}

		/*
		 * We're done.  Update each destination's status.
		 */

		for (a = 0; a < argcount; ++a)
		{
			free(av[a]);

			if (problem)
				dest_err(dv[a], e);
			else
				dv[a]->d_state = ST_DONE;
		}

		/* Track the correct count of UUCP addresses found. */

		uucpcount += argcount - 1;
	}

	return uucpcount;
}

/*----------------------------------------------------------------------
 * Where is uux?
 */

static char *
find_uux()
{
	static char uux1[] = "/bin/uux";
	static char uux2[] = "/usr/bin/uux";

	if (exists(uux1))
		return uux1;
	if (exists(uux2))
		return uux2;
	error("can't find uux!?\n");
	return NULL;
}

/*----------------------------------------------------------------------
 * Write the message for UUCP transmission to the given file.
 */

static int
uucp_copy(ofp)
FILE    *ofp;
{
	FILE    *ifp;
	char    *p;
	register int c;
	int     fd;
	char    buf[BUFSIZ];

	if ((fd = dup(tfd[T_HDR])) == -1)
	{
		syserr("can't dup header fd");
		return -1;
	}
	(void) lseek(fd, 0L, 0);
	if ((ifp = fdopen(fd, "r")) == NULL)
	{
		error("can't fdopen header fd");
		return -1;
	}

	/*
	 * Copy the header, but tack "remote from" onto the end of the
	 * From_ line.  (If it weren't for dealing with the From_ line,
	 * I'd skip stream I/O altogether and use read/write.  Maybe
	 * I should save the length of the From_ line when I copy it...)
	 */

	(void) fgets(buf, GETSIZE(buf), ifp);
	if ((p = strchr(buf, '\n')) != NULL)
		*p = 0;
	(void) fprintf(ofp, "%s remote from %s\n", buf, hostname);

	while ((c = getc(ifp)) != EOF)
		(void) putc(c, ofp);

	(void) fclose(ifp);

	/*
	 * Copy the body
	 */

	if ((fd = dup(tfd[T_BODY])) == -1)
	{
		syserr("can't dup body fd");
		return -1;
	}
	(void) lseek(fd, 0L, 0);
	if ((ifp = fdopen(fd, "r")) == NULL)
	{
		error("can't fdopen body fd");
		(void) close(fd);
		return -1;
	}

	while ((c = getc(ifp)) != EOF)
		(void) putc(c, ofp);

	(void) fclose(ifp);
	return 0;
}
