xref: /csrg-svn/bin/rmail/rmail.c (revision 69475)
136220Sbostic /*
260691Sbostic  * Copyright (c) 1988, 1993
360691Sbostic  *	The Regents of the University of California.  All rights reserved.
436220Sbostic  *
542540Sbostic  * %sccs.include.redist.c%
636220Sbostic  */
736220Sbostic 
814466Ssam #ifndef lint
960691Sbostic static char copyright[] =
1060691Sbostic "@(#) Copyright (c) 1988, 1993\n\
1160691Sbostic 	The Regents of the University of California.  All rights reserved.\n";
1236220Sbostic #endif /* not lint */
1314466Ssam 
1436220Sbostic #ifndef lint
15*69475Seric static char sccsid[] = "@(#)rmail.c	8.3 (Berkeley) 05/15/95";
1636220Sbostic #endif /* not lint */
1736220Sbostic 
184308Seric /*
1933981Sbostic  * RMAIL -- UUCP mail server.
2033981Sbostic  *
2159553Sbostic  * This program reads the >From ... remote from ... lines that UUCP is so
2259553Sbostic  * fond of and turns them into something reasonable.  It then execs sendmail
2359553Sbostic  * with various options built from these lines.
2459553Sbostic  *
2559553Sbostic  * The expected syntax is:
2659553Sbostic  *
2759553Sbostic  *	 <user> := [-a-z0-9]+
2859553Sbostic  *	 <date> := ctime format
2959553Sbostic  *	 <site> := [-a-z0-9!]+
3059553Sbostic  * <blank line> := "^\n$"
3159553Sbostic  *	 <from> := "From" <space> <user> <space> <date>
3259553Sbostic  *		  [<space> "remote from" <space> <site>]
3359553Sbostic  *    <forward> := ">" <from>
3459553Sbostic  *	    msg := <from> <forward>* <blank-line> <body>
3559553Sbostic  *
3659553Sbostic  * The output of rmail(8) compresses the <forward> lines into a single
3759553Sbostic  * from path.
3859553Sbostic  *
3959553Sbostic  * The err(3) routine is included here deliberately to make this code
4059553Sbostic  * a bit more portable.
4133981Sbostic  */
4259553Sbostic #include <sys/param.h>
4359553Sbostic #include <sys/stat.h>
4459553Sbostic #include <sys/wait.h>
454308Seric 
4659553Sbostic #include <ctype.h>
4759553Sbostic #include <fcntl.h>
4859553Sbostic #include <paths.h>
4959553Sbostic #include <stdio.h>
5059553Sbostic #include <stdlib.h>
5159553Sbostic #include <string.h>
5233981Sbostic #include <sysexits.h>
5359553Sbostic #include <unistd.h>
544308Seric 
5567911Seric #ifndef MAX
5667911Seric # define MAX(a, b)	((a) < (b) ? (b) : (a))
5767911Seric #endif
5867911Seric 
5959553Sbostic void err __P((int, const char *, ...));
6059553Sbostic void usage __P((void));
614308Seric 
6259553Sbostic int
main(argc,argv)634308Seric main(argc, argv)
6433981Sbostic 	int argc;
6559553Sbostic 	char *argv[];
664308Seric {
6759553Sbostic 	extern char *optarg;
6859553Sbostic 	extern int errno, optind;
6959553Sbostic 	FILE *fp;
7059553Sbostic 	struct stat sb;
7159553Sbostic 	size_t fplen, fptlen, len;
7259553Sbostic 	off_t offset;
7359553Sbostic 	int ch, debug, i, pdes[2], pid, status;
7459553Sbostic 	char *addrp, *domain, *p, *t;
7559553Sbostic 	char *from_path, *from_sys, *from_user;
7659553Sbostic 	char *args[100], buf[2048], lbuf[2048];
774308Seric 
7859553Sbostic 	debug = 0;
7959553Sbostic 	domain = "UUCP";		/* Default "domain". */
8059553Sbostic 	while ((ch = getopt(argc, argv, "D:T")) != EOF)
8159553Sbostic 		switch (ch) {
8259553Sbostic 		case 'T':
8359553Sbostic 			debug = 1;
8459553Sbostic 			break;
8559553Sbostic 		case 'D':
8659553Sbostic 			domain = optarg;
8759553Sbostic 			break;
8859553Sbostic 		case '?':
8959553Sbostic 		default:
9059553Sbostic 			usage();
9159553Sbostic 		}
9259553Sbostic 	argc -= optind;
9359553Sbostic 	argv += optind;
944308Seric 
9559553Sbostic 	if (argc < 1)
9659553Sbostic 		usage();
976243Seric 
9859553Sbostic 	from_path = from_sys = from_user = NULL;
9959553Sbostic 	for (offset = 0;;) {
10059553Sbostic 
10159553Sbostic 		/* Get and nul-terminate the line. */
10259553Sbostic 		if (fgets(lbuf, sizeof(lbuf), stdin) == NULL)
10359553Sbostic 			exit (EX_DATAERR);
10459553Sbostic 		if ((p = strchr(lbuf, '\n')) == NULL)
10559553Sbostic 			err(EX_DATAERR, "line too long");
10659553Sbostic 		*p = '\0';
10759553Sbostic 
10859553Sbostic 		/* Parse lines until reach a non-"From" line. */
10959553Sbostic 		if (!strncmp(lbuf, "From ", 5))
11059553Sbostic 			addrp = lbuf + 5;
11159553Sbostic 		else if (!strncmp(lbuf, ">From ", 6))
11259553Sbostic 			addrp = lbuf + 6;
11359553Sbostic 		else if (offset == 0)
11459553Sbostic 			err(EX_DATAERR,
11559553Sbostic 			    "missing or empty From line: %s", lbuf);
11659553Sbostic 		else {
11759553Sbostic 			*p = '\n';
1184308Seric 			break;
11959553Sbostic 		}
1204714Seric 
12159553Sbostic 		if (*addrp == '\0')
12259553Sbostic 			err(EX_DATAERR, "corrupted From line: %s", lbuf);
12359553Sbostic 
12459553Sbostic 		/* Use the "remote from" if it exists. */
12559553Sbostic 		for (p = addrp; (p = strchr(p + 1, 'r')) != NULL;)
12659553Sbostic 			if (!strncmp(p, "remote from ", 12)) {
12759553Sbostic 				for (t = p += 12; *t && !isspace(*t); ++t);
12859553Sbostic 				*t = '\0';
12959553Sbostic 				if (debug)
13059553Sbostic 					(void)fprintf(stderr,
13159553Sbostic 					    "remote from: %s\n", p);
13259553Sbostic 				break;
1334714Seric 			}
13459553Sbostic 
13559553Sbostic 		/* Else use the string up to the last bang. */
13659553Sbostic 		if (p == NULL)
13759553Sbostic 			if (*addrp == '!')
13859553Sbostic 				err(EX_DATAERR,
13959553Sbostic 				    "bang starts address: %s", addrp);
14059553Sbostic 			else if ((t = strrchr(addrp, '!')) != NULL) {
14159553Sbostic 				*t = '\0';
14259553Sbostic 				p = addrp;
14359553Sbostic 				addrp = t + 1;
14459553Sbostic 				if (*addrp == '\0')
14559553Sbostic 					err(EX_DATAERR,
14659553Sbostic 					    "corrupted From line: %s", lbuf);
14759553Sbostic 				if (debug)
14859553Sbostic 					(void)fprintf(stderr, "bang: %s\n", p);
14959553Sbostic 			}
15059553Sbostic 
15159553Sbostic 		/* 'p' now points to any system string from this line. */
15259553Sbostic 		if (p != NULL) {
15359553Sbostic 			/* Nul terminate it as necessary. */
15459553Sbostic 			for (t = p; *t && !isspace(*t); ++t);
15559553Sbostic 			*t = '\0';
15659553Sbostic 
15759553Sbostic 			/* If the first system, copy to the from_sys string. */
15859553Sbostic 			if (from_sys == NULL) {
15959553Sbostic 				if ((from_sys = strdup(p)) == NULL)
16059553Sbostic 					err(EX_TEMPFAIL, NULL);
16159553Sbostic 				if (debug)
16259553Sbostic 					(void)fprintf(stderr,
16359553Sbostic 					    "from_sys: %s\n", from_sys);
16459553Sbostic 			}
16559553Sbostic 
16659553Sbostic 			/* Concatenate to the path string. */
16759553Sbostic 			len = t - p;
16859553Sbostic 			if (from_path == NULL) {
16959553Sbostic 				fplen = 0;
17059553Sbostic 				if ((from_path = malloc(fptlen = 256)) == NULL)
17159553Sbostic 					err(EX_TEMPFAIL, NULL);
17259553Sbostic 			}
17359553Sbostic 			if (fplen + len + 2 > fptlen) {
17459553Sbostic 				fptlen += MAX(fplen + len + 2, 256);
17559553Sbostic 				if ((from_path =
17659553Sbostic 				    realloc(from_path, fptlen)) == NULL)
17759553Sbostic 					err(EX_TEMPFAIL, NULL);
17859553Sbostic 			}
17959553Sbostic 			memmove(from_path + fplen, p, len);
18059553Sbostic 			fplen += len;
18159553Sbostic 			from_path[fplen++] = '!';
18259553Sbostic 			from_path[fplen] = '\0';
1834308Seric 		}
18459553Sbostic 
18559553Sbostic 		/* Save off from user's address; the last one wins. */
18659553Sbostic 		for (p = addrp; *p && !isspace(*p); ++p);
18759553Sbostic 		*p = '\0';
188*69475Seric 		if (*addrp == '\0')
189*69475Seric 			addrp = "<>";
19059553Sbostic 		if (from_user != NULL)
19159553Sbostic 			free(from_user);
19259553Sbostic 		if ((from_user = strdup(addrp)) == NULL)
19359553Sbostic 			err(EX_TEMPFAIL, NULL);
19459553Sbostic 
19559553Sbostic 		if (debug) {
19659553Sbostic 			if (from_path != NULL)
19759553Sbostic 				(void)fprintf(stderr,
19859553Sbostic 				    "from_path: %s\n", from_path);
19959553Sbostic 			(void)fprintf(stderr, "from_user: %s\n", from_user);
20033981Sbostic 		}
20159553Sbostic 
20259553Sbostic 		if (offset != -1)
20359553Sbostic 			offset = (off_t)ftell(stdin);
2044308Seric 	}
20559553Sbostic 
20659553Sbostic 	i = 0;
20759553Sbostic 	args[i++] = _PATH_SENDMAIL;	/* Build sendmail's argument list. */
20859553Sbostic 	args[i++] = "-oee";		/* No errors, just status. */
20959553Sbostic 	args[i++] = "-odq";		/* Queue it, don't try to deliver. */
21059553Sbostic 	args[i++] = "-oi";		/* Ignore '.' on a line by itself. */
21159553Sbostic 
212*69475Seric 	/* set from system and protocol used */
213*69475Seric 	if (from_sys == NULL)
214*69475Seric 		(void)snprintf(buf, sizeof(buf), "-p%s", domain);
215*69475Seric 	else if (strchr(from_sys, '.') == NULL)
216*69475Seric 		(void)snprintf(buf, sizeof(buf), "-p%s:%s.%s",
217*69475Seric 			domain, from_sys, domain);
218*69475Seric 	else
219*69475Seric 		(void)snprintf(buf, sizeof(buf), "-p%s:%s", domain, from_sys);
22059553Sbostic 	if ((args[i++] = strdup(buf)) == NULL)
22159553Sbostic 		err(EX_TEMPFAIL, NULL);
2224308Seric 
22359553Sbostic 					/* Set name of ``from'' person. */
22459984Seric 	(void)snprintf(buf, sizeof(buf), "-f%s%s",
22559984Seric 	    from_path ? from_path : "", from_user);
22659553Sbostic 	if ((args[i++] = strdup(buf)) == NULL)
22759553Sbostic 		err(EX_TEMPFAIL, NULL);
22859553Sbostic 
22933981Sbostic 	/*
23059553Sbostic 	 * Don't copy arguments beginning with - as they will be
23159553Sbostic 	 * passed to sendmail and could be interpreted as flags.
23267911Seric 	 * To prevent confusion of sendmail wrap < and > around
23367911Seric 	 * the address (helps to pass addrs like @gw1,@gw2:aa@bb)
23433981Sbostic 	 */
23567911Seric 	while (*argv) {
23667911Seric 		if (**argv == '-')
23759553Sbostic 			err(EX_USAGE, "dash precedes argument: %s", *argv);
23867911Seric 		if (strchr(*argv, ',') == NULL || strchr(*argv, '<') != NULL)
23967911Seric 			args[i++] = *argv;
24067911Seric 		else {
24167911Seric 			if ((args[i] = malloc(strlen(*argv) + 3)) == NULL)
24267911Seric 				err(EX_TEMPFAIL, "Cannot malloc");
24367911Seric 			sprintf (args [i++], "<%s>", *argv);
24467911Seric 		}
24567911Seric 		argv++;
24667911Seric 	}
24767911Seric 	args[i] = 0;
24833981Sbostic 
24959553Sbostic 	if (debug) {
25059553Sbostic 		(void)fprintf(stderr, "Sendmail arguments:\n");
25159553Sbostic 		for (i = 0; args[i]; i++)
25259553Sbostic 			(void)fprintf(stderr, "\t%s\n", args[i]);
2534309Seric 	}
25433981Sbostic 
25559553Sbostic 	/*
25659553Sbostic 	 * If called with a regular file as standard input, seek to the right
25759553Sbostic 	 * position in the file and just exec sendmail.  Could probably skip
25859553Sbostic 	 * skip the stat, but it's not unreasonable to believe that a failed
25959553Sbostic 	 * seek will cause future reads to fail.
26059553Sbostic 	 */
26159553Sbostic 	if (!fstat(STDIN_FILENO, &sb) && S_ISREG(sb.st_mode)) {
26259553Sbostic 		if (lseek(STDIN_FILENO, offset, SEEK_SET) != offset)
26359553Sbostic 			err(EX_TEMPFAIL, "stdin seek");
26459553Sbostic 		execv(_PATH_SENDMAIL, args);
26559553Sbostic 		err(EX_OSERR, "%s", _PATH_SENDMAIL);
26633981Sbostic 	}
26733981Sbostic 
26859553Sbostic 	if (pipe(pdes) < 0)
26959553Sbostic 		err(EX_OSERR, NULL);
27059553Sbostic 
27159553Sbostic 	switch (pid = vfork()) {
27259553Sbostic 	case -1:				/* Err. */
27359553Sbostic 		err(EX_OSERR, NULL);
27459553Sbostic 	case 0:					/* Child. */
27559553Sbostic 		if (pdes[0] != STDIN_FILENO) {
27659553Sbostic 			(void)dup2(pdes[0], STDIN_FILENO);
27759553Sbostic 			(void)close(pdes[0]);
27833981Sbostic 		}
27959553Sbostic 		(void)close(pdes[1]);
28059553Sbostic 		execv(_PATH_SENDMAIL, args);
28159553Sbostic 		_exit(127);
28259553Sbostic 		/* NOTREACHED */
2836067Seric 	}
28459553Sbostic 
28559553Sbostic 	if ((fp = fdopen(pdes[1], "w")) == NULL)
28659553Sbostic 		err(EX_OSERR, NULL);
28759553Sbostic 	(void)close(pdes[0]);
28859553Sbostic 
28959553Sbostic 	/* Copy the file down the pipe. */
29059553Sbostic 	do {
29159553Sbostic 		(void)fprintf(fp, "%s", lbuf);
29259553Sbostic 	} while (fgets(lbuf, sizeof(lbuf), stdin) != NULL);
29359553Sbostic 
29459553Sbostic 	if (ferror(stdin))
29559553Sbostic 		err(EX_TEMPFAIL, "stdin: %s", strerror(errno));
29659553Sbostic 
29759553Sbostic 	if (fclose(fp))
29859553Sbostic 		err(EX_OSERR, NULL);
29959553Sbostic 
30059553Sbostic 	if ((waitpid(pid, &status, 0)) == -1)
30159553Sbostic 		err(EX_OSERR, "%s", _PATH_SENDMAIL);
30259553Sbostic 
30359553Sbostic 	if (!WIFEXITED(status))
30459553Sbostic 		err(EX_OSERR,
30559553Sbostic 		    "%s: did not terminate normally", _PATH_SENDMAIL);
30659553Sbostic 
30759553Sbostic 	if (WEXITSTATUS(status))
30859553Sbostic 		err(status, "%s: terminated with %d (non-zero) status",
30959553Sbostic 		    _PATH_SENDMAIL, WEXITSTATUS(status));
31059553Sbostic 	exit(EX_OK);
3114308Seric }
31259553Sbostic 
31359553Sbostic void
usage()31459553Sbostic usage()
31559553Sbostic {
31659553Sbostic 	(void)fprintf(stderr, "usage: rmail [-T] [-D domain] user ...\n");
31759553Sbostic 	exit(EX_USAGE);
31859553Sbostic }
31959553Sbostic 
32059553Sbostic #ifdef __STDC__
32159553Sbostic #include <stdarg.h>
32259553Sbostic #else
32359553Sbostic #include <varargs.h>
32459553Sbostic #endif
32559553Sbostic 
32659553Sbostic void
32759553Sbostic #ifdef __STDC__
err(int eval,const char * fmt,...)32859553Sbostic err(int eval, const char *fmt, ...)
32959553Sbostic #else
33059553Sbostic err(eval, fmt, va_alist)
33159553Sbostic 	int eval;
33259553Sbostic 	const char *fmt;
33359553Sbostic 	va_dcl
33459553Sbostic #endif
33559553Sbostic {
33659553Sbostic 	va_list ap;
33759553Sbostic #if __STDC__
33859553Sbostic 	va_start(ap, fmt);
33959553Sbostic #else
34059553Sbostic 	va_start(ap);
34159553Sbostic #endif
34259553Sbostic 	(void)fprintf(stderr, "rmail: ");
34359553Sbostic 	(void)vfprintf(stderr, fmt, ap);
34459553Sbostic 	va_end(ap);
34559553Sbostic 	(void)fprintf(stderr, "\n");
34659553Sbostic 	exit(eval);
34759553Sbostic }
348