/*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
/*	  All Rights Reserved  	*/

/*	THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF AT&T	*/
/*	The copyright notice above does not evidence any   	*/
/*	actual or intended publication of such source code.	*/

#ident	"@(#)alerts.c	1.7	97/06/27 SMI"	/* SVr4.0 1.17	*/
/* EMACS_MODES: !fill, lnumb, !overwrite, !nodelete, !picture */

#include "stdio.h"
#include "string.h"
#include "errno.h"
#include "limits.h"
#include "unistd.h"

#include "lp.h"

extern char		**environ;

static void		envlist(int, char **);

/*
 * We recognize the following key phrases in the alert prototype
 * file, and replace them with appropriate values.
 */
#define NALRT_KEYS	7
# define ALRT_ENV		0
# define ALRT_PWD		1
# define ALRT_ULIMIT		2
# define ALRT_UMASK		3
# define ALRT_INTERVAL		4
# define ALRT_CMD		5
# define ALRT_USER		6

static struct {
	char			*v;
	short			len;
}			shell_keys[NALRT_KEYS] = {
#define	ENTRY(X)	X, sizeof(X)-1
	ENTRY("-ENVIRONMENT-"),
	ENTRY("-PWD-"),
	ENTRY("-ULIMIT-"),
	ENTRY("-UMASK-"),
	ENTRY("-INTERVAL-"),
	ENTRY("-CMD-"),
	ENTRY("-USER-"),
};

/*
 * These are used to bracket the administrator's command, so that
 * we can find it easily. We're out of luck if the administrator
 * includes an identical phrase in his or her command.
 */
#define ALRT_CMDSTART "## YOUR COMMAND STARTS HERE -- DON'T TOUCH ABOVE!!"
#define ALRT_CMDEND   "## YOUR COMMAND ENDS HERE -- DON'T TOUCH BELOW!!"

/**
 ** putalert() - WRITE ALERT TO FILES
 **/

int
putalert(char *parent, char *name, FALERT *alertp)
{
	char			*path,
				cur_dir[PATH_MAX + 1],
				buf[BUFSIZ];

	int			cur_umask;

	int fdout, fdin;


	if (!parent || !*parent || !name || !*name) {
		errno = EINVAL;
		return (-1);
	}

	if (!alertp->shcmd) {
		errno = EINVAL;
		return (-1);
	}

	if (STREQU(alertp->shcmd, NAME_NONE))
		return (delalert(parent, name));

	/*
	 * See if the form/printer/print-wheel exists.
	 */

	if (!(path = makepath(parent, name, (char *)0)))
		return (-1);

	if (Access(path, F_OK) == -1) {
		if (errno == ENOENT)
			errno = ENOTDIR; /* not quite, but what else? */
		Free (path);
		return (-1);
	}
	Free (path);

	/*
	 * First, the shell command file.
	 */

	if (!(path = makepath(parent, name, ALERTSHFILE, (char *)0)))
		return (-1);

	if ((fdout = open_locked(path, "w", MODE_NOEXEC)) < 0) {
		Free (path);
		return (-1);
	}
	Free (path);

	/*
	 * We use a prototype file to build the shell command,
	 * so that the alerts are easily customized. The shell
	 * is expected to handle repeat alerts and failed alerts,
	 * because the Spooler doesn't. Also, the Spooler runs
	 * each alert with the UID and GID of the administrator
	 * who defined the alert. Otherwise, anything goes.
	 */

	if (!Lp_Bin) {
		getpaths ();
		if (!Lp_Bin)
			return (-1);
	}
	if (!(path = makepath(Lp_Bin, ALERTPROTOFILE, (char *)0)))
		return (-1);

	if ((fdin = open_locked(path, "r", 0)) < 0) {
		Free (path);
		return (-1);
	}
	Free (path);

	errno = 0;
	while (fdgets(buf, BUFSIZ, fdin)) {
		int			key;
		char			*cp,
					*dash;

		cp = buf;
		while ((dash = strchr(cp, '-'))) {

		    *dash = 0;
		    fdputs (cp, fdout);
		    *(cp = dash) = '-';

		    for (key = 0; key < NALRT_KEYS; key++)
			if (STRNEQU(
				cp,
				shell_keys[key].v,
				shell_keys[key].len
			)) {
				register char	*newline =
						(cp != buf)? "\n" : "";

				cp += shell_keys[key].len;

				switch (key) {

				case ALRT_ENV:
					fdprintf(fdout, newline);
					envlist(fdout, environ);
					break;

				case ALRT_PWD:
					getcwd (cur_dir, PATH_MAX);
					fdprintf (fdout, "%s", cur_dir);
					break;

				case ALRT_ULIMIT:
					fdprintf (fdout, "%ld", ulimit(1, (long)0));
					break;

				case ALRT_UMASK:
					umask (cur_umask = umask(0));
					fdprintf (fdout, "%03o", cur_umask);
					break;

				case ALRT_INTERVAL:
					fdprintf(fdout, "%ld", (long)alertp->W);
					break;

				case ALRT_CMD:
					fdprintf(fdout, newline);
					fdprintf(fdout, "%s\n", ALRT_CMDSTART);
					fdprintf(fdout, "%s\n", alertp->shcmd);
					fdprintf(fdout, "%s\n", ALRT_CMDEND);
					break;

				case ALRT_USER:
					fdprintf(fdout, "%s", getname());
					break;

				}

				break;
			}
		    if (key >= NALRT_KEYS)
			fdputc(*cp++, fdout);

		}
		fdputs(cp, fdout);

	}
	if (errno != 0) {
		int			save_errno = errno;

		close(fdin);
		close(fdout);
		errno = save_errno;
		return (-1);
	}
	close(fdin);
	close(fdout);

	/*
	 * Next, the variables file.
	 */

	if (!(path = makepath(parent, name, ALERTVARSFILE, (char *)0)))
		return (-1);

	if ((fdout = open_locked(path, "w", MODE_NOREAD)) < 0) {
		Free (path);
		return (-1);
	}
	Free (path);

	fdprintf(fdout, "%d\n", alertp->Q > 0? alertp->Q : 1);
	fdprintf(fdout, "%d\n", alertp->W >= 0? alertp->W : 0);

	close(fdout);

	return (0);
}

/**
 ** getalert() - EXTRACT ALERT FROM FILES
 **/

FALERT *
getalert(char *parent, char *name)
{
	int fd;
	char *tmp;
	static FALERT		alert;
	register char		*path;
	char			buf[BUFSIZ];
	int			len;

	if (!parent || !*parent || !name || !*name) {
		errno = EINVAL;
		return (0);
	}

	/*
	 * See if the form/printer/print-wheel exists.
	 */

	if (!(path = makepath(parent, name, (char *)0)))
		return (0);

	if (Access(path, F_OK) == -1) {
		if (errno == ENOENT)
			errno = ENOTDIR; /* not quite, but what else? */
		Free (path);
		return (0);
	}
	Free (path);

	/*
	 * First, the shell command file.
	 */

	if (!(path = makepath(parent, name, ALERTSHFILE, (char *)0)))
		return (0);

	if ((fd = open_locked(path, "r", 0)) < 0) {
		Free (path);
		return (0);
	}
	Free (path);

	/*
	 * Skip over environment setting stuff, while loop, etc.,
	 * to find the beginning of the command.
	 */
	errno = 0;
	while ((tmp =  fdgets(buf, BUFSIZ, fd)) &&
		!STRNEQU(buf, ALRT_CMDSTART, sizeof(ALRT_CMDSTART)-1))
		;
	if ((tmp == NULL) || (errno != 0)) {
		int			save_errno = errno;

		close(fd);
		errno = save_errno;
		return (0);
	}

	alert.shcmd = sop_up_rest(fd, ALRT_CMDEND);

	close(fd);

	if (!alert.shcmd)
		return (0);

	/*
	 * Drop terminating newline.
	 */
	if (alert.shcmd[(len = strlen(alert.shcmd)) - 1] == '\n')
		alert.shcmd[len - 1] = 0;


	/*
	 * Next, the variables file.
	 */

	if (!(path = makepath(parent, name, ALERTVARSFILE, (char *)0)))
		return (0);

	if ((fd = open_locked(path, "r", 0)) < 0) {
		Free (path);
		return (0);
	}
	Free (path);

	errno = 0;
	(void)fdgets (buf, BUFSIZ, fd);
	if (errno != 0) {
		int			save_errno = errno;

		close(fd);
		errno = save_errno;
		return (0);
	}
	alert.Q = atoi(buf);

	(void)fdgets (buf, BUFSIZ, fd);
	if (errno != 0) {
		int			save_errno = errno;

		close(fd);
		errno = save_errno;
		return (0);
	}
	alert.W = atoi(buf);

	close(fd);

	return (&alert);
}

/**
 ** delalert() - DELETE ALERT FILES
 **/

int
delalert(char *parent, char *name)
{
	char			*path;


	if (!parent || !*parent || !name || !*name) {
		errno = EINVAL;
		return (-1);
	}

	/*
	 * See if the form/printer/print-wheel exists.
	 */

	if (!(path = makepath(parent, name, (char *)0)))
		return (-1);

	if (Access(path, F_OK) == -1) {
		if (errno == ENOENT)
			errno = ENOTDIR; /* not quite, but what else? */
		Free (path);
		return (-1);
	}
	Free (path);

	/*
	 * Remove the two files.
	 */

	if (!(path = makepath(parent, name, ALERTSHFILE, (char *)0)))
		return (-1);
	if (rmfile(path) == -1) {
		Free (path);
		return (-1);
	}
	Free (path);

	if (!(path = makepath(parent, name, ALERTVARSFILE, (char *)0)))
		return (-1);
	if (rmfile(path) == -1) {
		Free (path);
		return (-1);
	}
	Free (path);

	return (0);
}

/**
 ** envlist() - PRINT OUT ENVIRONMENT LIST SAFELY
 **/

static void
envlist(int fd, char **list)
{
	register char		*env,
				*value;

	if (!list || !*list)
		return;

	while ((env = *list++)) {
		if (!(value = strchr(env, '=')))
			continue;
		*value++ = 0;
		if (!strchr(value, '\''))
			fdprintf(fd, (char *)gettext("export %s; %s='%s'\n"),
				env, env, value);
		*--value = '=';
	}
}

/*
 * printalert() - PRINT ALERT DESCRIPTION
 *
 * This is not used in the scheduler, so we don't need to switch to using
 * file descriptors for scalability.
 */

void
printalert(FILE *fp, FALERT *alertp, int isfault)
{
	if (!alertp->shcmd) {
		if (isfault)
			(void)fprintf (fp, (char *)gettext("On fault: no alert\n"));
		else
			(void)fprintf (fp, (char *)gettext("No alert\n"));

	} else {
		register char	*copy = Strdup(alertp->shcmd),
				*cp;

		if (isfault)
			(void)fprintf (fp, (char *)gettext("On fault: "));
		else
			if (alertp->Q > 1)
				(void)fprintf (
					fp,
					(char *)gettext("When %d are queued: "),
					alertp->Q
				);
			else
				(void)fprintf (fp, (char *)gettext("Upon any being queued: "));

		if (copy && (cp = strchr(copy, ' ')))
			while (*cp == ' ')
				*cp++ = 0;

		if (
			copy
		     && syn_name(cp)
		     && (
				STREQU(copy, NAME_WRITE)
			     || STREQU(copy, NAME_MAIL)
			)
		)
			(void)fprintf (fp, "%s to %s ", copy, cp);
		else
			(void)fprintf (fp, (char *)gettext("alert with \"%s\" "), alertp->shcmd);

		if (alertp->W > 0)
			(void)fprintf (fp, (char *)gettext("every %d minutes\n"), alertp->W);
		else
			(void)fprintf (fp, (char *)gettext("once\n"));

		Free (copy);
	}
	return;
}
