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