4.4BSD snapshot (revision 8.1); add 1993 to copyright
[unix-history] / usr / src / bin / rmail / rmail.c
CommitLineData
9b7cb659 1/*
1acd8217 2 * Copyright (c) 1988, 1993 The Regents of the University of California.
9b7cb659
KB
3 * All rights reserved.
4 *
27c71911 5 * %sccs.include.redist.c%
9b7cb659
KB
6 */
7
8#ifndef lint
9char copyright[] =
1acd8217 10"@(#) Copyright (c) 1988, 1993 The Regents of the University of California.\n\
9b7cb659
KB
11 All rights reserved.\n";
12#endif /* not lint */
13
205a2d85 14#ifndef lint
46b3dbee 15static char sccsid[] = "@(#)rmail.c 5.2 (Berkeley) %G%";
9b7cb659 16#endif /* not lint */
205a2d85 17
9adc8f2c 18/*
306f949d
KB
19 * RMAIL -- UUCP mail server.
20 *
1acd8217
KB
21 * This program reads the >From ... remote from ... lines that UUCP is so
22 * fond of and turns them into something reasonable. It then execs sendmail
23 * with various options built from these lines.
24 *
25 * The expected syntax is:
26 *
27 * <user> := [-a-z0-9]+
28 * <date> := ctime format
29 * <site> := [-a-z0-9!]+
30 * <blank line> := "^\n$"
31 * <from> := "From" <space> <user> <space> <date>
32 * [<space> "remote from" <space> <site>]
33 * <forward> := ">" <from>
34 * msg := <from> <forward>* <blank-line> <body>
35 *
36 * The output of rmail(8) compresses the <forward> lines into a single
37 * from path.
38 *
39 * The err(3) routine is included here deliberately to make this code
40 * a bit more portable.
306f949d 41 */
1acd8217 42#include <sys/param.h>
306f949d 43#include <sys/stat.h>
1acd8217
KB
44#include <sys/wait.h>
45
46#include <ctype.h>
47#include <fcntl.h>
7abf8d65 48#include <paths.h>
1acd8217
KB
49#include <stdio.h>
50#include <stdlib.h>
51#include <string.h>
52#include <sysexits.h>
53#include <unistd.h>
b5fd168f 54# include "conf.h"
9adc8f2c 55
1acd8217
KB
56void err __P((int, const char *, ...));
57void usage __P((void));
0f5791a6 58
1acd8217 59int
9adc8f2c 60main(argc, argv)
306f949d 61 int argc;
1acd8217 62 char *argv[];
9adc8f2c 63{
1acd8217
KB
64 extern char *optarg;
65 extern int errno, optind;
66 FILE *fp;
67 struct stat sb;
68 size_t fplen, fptlen, len;
69 off_t offset;
70 int ch, debug, i, pdes[2], pid, status;
71 char *addrp, *domain, *p, *t;
72 char *from_path, *from_sys, *from_user;
73 char *args[100], buf[2048], lbuf[2048];
9adc8f2c 74
1acd8217
KB
75 debug = 0;
76 domain = "UUCP"; /* Default "domain". */
77 while ((ch = getopt(argc, argv, "D:T")) != EOF)
78 switch (ch) {
79 case 'T':
80 debug = 1;
9adc8f2c 81 break;
1acd8217
KB
82 case 'D':
83 domain = optarg;
84 break;
85 case '?':
86 default:
87 usage();
88 }
89 argc -= optind;
90 argv += optind;
91
92 if (argc < 1)
93 usage();
94
95 from_path = from_sys = from_user = NULL;
96 for (offset = 0;;) {
97
98 /* Get and nul-terminate the line. */
99 if (fgets(lbuf, sizeof(lbuf), stdin) == NULL)
100 exit (EX_DATAERR);
101 if ((p = strchr(lbuf, '\n')) == NULL)
102 err(EX_DATAERR, "line too long");
103 *p = '\0';
104
105 /* Parse lines until reach a non-"From" line. */
106 if (!strncmp(lbuf, "From ", 5))
107 addrp = lbuf + 5;
108 else if (!strncmp(lbuf, ">From ", 6))
109 addrp = lbuf + 6;
110 else if (offset == 0)
111 err(EX_DATAERR,
112 "missing or empty From line: %s", lbuf);
113 else {
114 *p = '\n';
115 break;
116 }
117
118 if (*addrp == '\0')
119 err(EX_DATAERR, "corrupted From line: %s", lbuf);
120
121 /* Use the "remote from" if it exists. */
122 for (p = addrp; (p = strchr(p + 1, 'r')) != NULL;)
123 if (!strncmp(p, "remote from ", 12)) {
124 for (t = p += 12; *t && !isspace(*t); ++t);
125 *t = '\0';
126 if (debug)
127 (void)fprintf(stderr,
128 "remote from: %s\n", p);
9adc8f2c 129 break;
1acd8217
KB
130 }
131
132 /* Else use the string up to the last bang. */
133 if (p == NULL)
134 if (*addrp == '!')
135 err(EX_DATAERR,
136 "bang starts address: %s", addrp);
137 else if ((t = strrchr(addrp, '!')) != NULL) {
138 *t = '\0';
139 p = addrp;
140 addrp = t + 1;
141 if (*addrp == '\0')
142 err(EX_DATAERR,
143 "corrupted From line: %s", lbuf);
144 if (debug)
145 (void)fprintf(stderr, "bang: %s\n", p);
146 }
147
148 /* 'p' now points to any system string from this line. */
149 if (p != NULL) {
150 /* Nul terminate it as necessary. */
151 for (t = p; *t && !isspace(*t); ++t);
152 *t = '\0';
153
154 /* If the first system, copy to the from_sys string. */
155 if (from_sys == NULL) {
156 if ((from_sys = strdup(p)) == NULL)
157 err(EX_TEMPFAIL, NULL);
158 if (debug)
159 (void)fprintf(stderr,
160 "from_sys: %s\n", from_sys);
161 }
162
163 /* Concatenate to the path string. */
164 len = t - p;
165 if (from_path == NULL) {
166 fplen = 0;
167 if ((from_path = malloc(fptlen = 256)) == NULL)
168 err(EX_TEMPFAIL, NULL);
169 }
170 if (fplen + len + 2 > fptlen) {
171 fptlen += MAX(fplen + len + 2, 256);
172 if ((from_path =
173 realloc(from_path, fptlen)) == NULL)
174 err(EX_TEMPFAIL, NULL);
175 }
176 memmove(from_path + fplen, p, len);
177 fplen += len;
178 from_path[fplen++] = '!';
179 from_path[fplen] = '\0';
9adc8f2c 180 }
1acd8217
KB
181
182 /* Save off from user's address; the last one wins. */
183 for (p = addrp; *p && !isspace(*p); ++p);
184 *p = '\0';
185 if (from_user != NULL)
186 free(from_user);
187 if ((from_user = strdup(addrp)) == NULL)
188 err(EX_TEMPFAIL, NULL);
189
190 if (debug) {
191 if (from_path != NULL)
192 (void)fprintf(stderr,
193 "from_path: %s\n", from_path);
194 (void)fprintf(stderr, "from_user: %s\n", from_user);
306f949d 195 }
1acd8217
KB
196
197 if (offset != -1)
198 offset = (off_t)ftell(stdin);
9adc8f2c 199 }
1acd8217
KB
200
201 i = 0;
202 args[i++] = _PATH_SENDMAIL; /* Build sendmail's argument list. */
203 args[i++] = "-oee"; /* No errors, just status. */
204 args[i++] = "-odq"; /* Queue it, don't try to deliver. */
205 args[i++] = "-oi"; /* Ignore '.' on a line by itself. */
206
207 if (from_sys != NULL) { /* Set sender's host name. */
208 if (strchr(from_sys, '.') == NULL)
209 (void)snprintf(buf, sizeof(buf),
210 "-oMs%s.%s", from_sys, domain);
211 else
212 (void)snprintf(buf, sizeof(buf), "-oMs%s", from_sys);
213 if ((args[i++] = strdup(buf)) == NULL)
214 err(EX_TEMPFAIL, NULL);
306f949d 215 }
1acd8217
KB
216 /* Set protocol used. */
217 (void)snprintf(buf, sizeof(buf), "-oMr%s", domain);
218 if ((args[i++] = strdup(buf)) == NULL)
219 err(EX_TEMPFAIL, NULL);
220
221 /* Set name of ``from'' person. */
46b3dbee
EA
222 (void)snprintf(buf, sizeof(buf), "-f%s%s",
223 from_path ? from_path : "", from_user);
1acd8217
KB
224 if ((args[i++] = strdup(buf)) == NULL)
225 err(EX_TEMPFAIL, NULL);
306f949d
KB
226
227 /*
1acd8217
KB
228 * Don't copy arguments beginning with - as they will be
229 * passed to sendmail and could be interpreted as flags.
306f949d 230 */
1acd8217 231 do {
46b3dbee 232 if (*argv && **argv == '-')
1acd8217
KB
233 err(EX_USAGE, "dash precedes argument: %s", *argv);
234 } while ((args[i++] = *argv++) != NULL);
235
236 if (debug) {
237 (void)fprintf(stderr, "Sendmail arguments:\n");
238 for (i = 0; args[i]; i++)
239 (void)fprintf(stderr, "\t%s\n", args[i]);
306f949d 240 }
1acd8217
KB
241
242 /*
243 * If called with a regular file as standard input, seek to the right
244 * position in the file and just exec sendmail. Could probably skip
245 * skip the stat, but it's not unreasonable to believe that a failed
246 * seek will cause future reads to fail.
247 */
248 if (!fstat(STDIN_FILENO, &sb) && S_ISREG(sb.st_mode)) {
249 if (lseek(STDIN_FILENO, offset, SEEK_SET) != offset)
250 err(EX_TEMPFAIL, "stdin seek");
251 execv(_PATH_SENDMAIL, args);
252 err(EX_OSERR, "%s", _PATH_SENDMAIL);
0f5791a6 253 }
1acd8217
KB
254
255 if (pipe(pdes) < 0)
256 err(EX_OSERR, NULL);
257
258 switch (pid = vfork()) {
259 case -1: /* Err. */
260 err(EX_OSERR, NULL);
261 case 0: /* Child. */
262 if (pdes[0] != STDIN_FILENO) {
263 (void)dup2(pdes[0], STDIN_FILENO);
264 (void)close(pdes[0]);
265 }
266 (void)close(pdes[1]);
267 execv(_PATH_SENDMAIL, args);
268 _exit(127);
269 /* NOTREACHED */
9777ef27 270 }
1acd8217
KB
271
272 if ((fp = fdopen(pdes[1], "w")) == NULL)
273 err(EX_OSERR, NULL);
274 (void)close(pdes[0]);
275
276 /* Copy the file down the pipe. */
277 do {
278 (void)fprintf(fp, "%s", lbuf);
279 } while (fgets(lbuf, sizeof(lbuf), stdin) != NULL);
280
281 if (ferror(stdin))
282 err(EX_TEMPFAIL, "stdin: %s", strerror(errno));
283
284 if (fclose(fp))
285 err(EX_OSERR, NULL);
286
287 if ((waitpid(pid, &status, 0)) == -1)
288 err(EX_OSERR, "%s", _PATH_SENDMAIL);
289
290 if (!WIFEXITED(status))
291 err(EX_OSERR,
292 "%s: did not terminate normally", _PATH_SENDMAIL);
293
294 if (WEXITSTATUS(status))
295 err(status, "%s: terminated with %d (non-zero) status",
296 _PATH_SENDMAIL, WEXITSTATUS(status));
297 exit(EX_OK);
298}
299
300void
301usage()
302{
303 (void)fprintf(stderr, "usage: rmail [-T] [-D domain] user ...\n");
304 exit(EX_USAGE);
305}
306
307#ifdef __STDC__
308#include <stdarg.h>
309#else
310#include <varargs.h>
306f949d 311#endif
1acd8217
KB
312
313void
314#ifdef __STDC__
315err(int eval, const char *fmt, ...)
316#else
317err(eval, fmt, va_alist)
318 int eval;
319 const char *fmt;
320 va_dcl
1b48a4b1 321#endif
1acd8217
KB
322{
323 va_list ap;
324#if __STDC__
325 va_start(ap, fmt);
326#else
327 va_start(ap);
328#endif
329 (void)fprintf(stderr, "rmail: ");
330 (void)vfprintf(stderr, fmt, ap);
331 va_end(ap);
332 (void)fprintf(stderr, "\n");
333 exit(eval);
9adc8f2c 334}