This commit was manufactured by cvs2svn to create tag 'FreeBSD-release/1.0'.
[unix-history] / usr.sbin / sendmail / rmail / rmail.c
CommitLineData
78ed81a3 1/*
2 * Copyright (c) 1988, 1993
3 * The Regents of the University of California. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by the University of
16 * California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34#ifndef lint
35static char copyright[] =
36"@(#) Copyright (c) 1988, 1993\n\
37 The Regents of the University of California. All rights reserved.\n";
38#endif /* not lint */
39
40#ifndef lint
41static char sccsid[] = "@(#)rmail.c 8.1 (Berkeley) 5/31/93";
42#endif /* not lint */
43
44/*
45 * RMAIL -- UUCP mail server.
46 *
47 * This program reads the >From ... remote from ... lines that UUCP is so
48 * fond of and turns them into something reasonable. It then execs sendmail
49 * with various options built from these lines.
50 *
51 * The expected syntax is:
52 *
53 * <user> := [-a-z0-9]+
54 * <date> := ctime format
55 * <site> := [-a-z0-9!]+
56 * <blank line> := "^\n$"
57 * <from> := "From" <space> <user> <space> <date>
58 * [<space> "remote from" <space> <site>]
59 * <forward> := ">" <from>
60 * msg := <from> <forward>* <blank-line> <body>
61 *
62 * The output of rmail(8) compresses the <forward> lines into a single
63 * from path.
64 *
65 * The err(3) routine is included here deliberately to make this code
66 * a bit more portable.
67 */
68#include <sys/param.h>
69#include <sys/stat.h>
70#include <sys/wait.h>
71
72#include <ctype.h>
73#include <fcntl.h>
74#include <paths.h>
75#include <stdio.h>
76#include <stdlib.h>
77#include <string.h>
78#include <sysexits.h>
79#include <unistd.h>
80
81void err __P((int, const char *, ...));
82void usage __P((void));
83
84int
85main(argc, argv)
86 int argc;
87 char *argv[];
88{
89 extern char *optarg;
90 extern int errno, optind;
91 FILE *fp;
92 struct stat sb;
93 size_t fplen, fptlen, len;
94 off_t offset;
95 int ch, debug, i, pdes[2], pid, status;
96 char *addrp, *domain, *p, *t;
97 char *from_path, *from_sys, *from_user;
98 char *args[100], buf[2048], lbuf[2048];
99
100 debug = 0;
101 domain = "UUCP"; /* Default "domain". */
102 while ((ch = getopt(argc, argv, "D:T")) != EOF)
103 switch (ch) {
104 case 'T':
105 debug = 1;
106 break;
107 case 'D':
108 domain = optarg;
109 break;
110 case '?':
111 default:
112 usage();
113 }
114 argc -= optind;
115 argv += optind;
116
117 if (argc < 1)
118 usage();
119
120 from_path = from_sys = from_user = NULL;
121 for (offset = 0;;) {
122
123 /* Get and nul-terminate the line. */
124 if (fgets(lbuf, sizeof(lbuf), stdin) == NULL)
125 exit (EX_DATAERR);
126 if ((p = strchr(lbuf, '\n')) == NULL)
127 err(EX_DATAERR, "line too long");
128 *p = '\0';
129
130 /* Parse lines until reach a non-"From" line. */
131 if (!strncmp(lbuf, "From ", 5))
132 addrp = lbuf + 5;
133 else if (!strncmp(lbuf, ">From ", 6))
134 addrp = lbuf + 6;
135 else if (offset == 0)
136 err(EX_DATAERR,
137 "missing or empty From line: %s", lbuf);
138 else {
139 *p = '\n';
140 break;
141 }
142
143 if (*addrp == '\0')
144 err(EX_DATAERR, "corrupted From line: %s", lbuf);
145
146 /* Use the "remote from" if it exists. */
147 for (p = addrp; (p = strchr(p + 1, 'r')) != NULL;)
148 if (!strncmp(p, "remote from ", 12)) {
149 for (t = p += 12; *t && !isspace(*t); ++t);
150 *t = '\0';
151 if (debug)
152 (void)fprintf(stderr,
153 "remote from: %s\n", p);
154 break;
155 }
156
157 /* Else use the string up to the last bang. */
158 if (p == NULL)
159 if (*addrp == '!')
160 err(EX_DATAERR,
161 "bang starts address: %s", addrp);
162 else if ((t = strrchr(addrp, '!')) != NULL) {
163 *t = '\0';
164 p = addrp;
165 addrp = t + 1;
166 if (*addrp == '\0')
167 err(EX_DATAERR,
168 "corrupted From line: %s", lbuf);
169 if (debug)
170 (void)fprintf(stderr, "bang: %s\n", p);
171 }
172
173 /* 'p' now points to any system string from this line. */
174 if (p != NULL) {
175 /* Nul terminate it as necessary. */
176 for (t = p; *t && !isspace(*t); ++t);
177 *t = '\0';
178
179 /* If the first system, copy to the from_sys string. */
180 if (from_sys == NULL) {
181 if ((from_sys = strdup(p)) == NULL)
182 err(EX_TEMPFAIL, NULL);
183 if (debug)
184 (void)fprintf(stderr,
185 "from_sys: %s\n", from_sys);
186 }
187
188 /* Concatenate to the path string. */
189 len = t - p;
190 if (from_path == NULL) {
191 fplen = 0;
192 if ((from_path = malloc(fptlen = 256)) == NULL)
193 err(EX_TEMPFAIL, NULL);
194 }
195 if (fplen + len + 2 > fptlen) {
196 fptlen += MAX(fplen + len + 2, 256);
197 if ((from_path =
198 realloc(from_path, fptlen)) == NULL)
199 err(EX_TEMPFAIL, NULL);
200 }
201 memmove(from_path + fplen, p, len);
202 fplen += len;
203 from_path[fplen++] = '!';
204 from_path[fplen] = '\0';
205 }
206
207 /* Save off from user's address; the last one wins. */
208 for (p = addrp; *p && !isspace(*p); ++p);
209 *p = '\0';
210 if (from_user != NULL)
211 free(from_user);
212 if ((from_user = strdup(addrp)) == NULL)
213 err(EX_TEMPFAIL, NULL);
214
215 if (debug) {
216 if (from_path != NULL)
217 (void)fprintf(stderr,
218 "from_path: %s\n", from_path);
219 (void)fprintf(stderr, "from_user: %s\n", from_user);
220 }
221
222 if (offset != -1)
223 offset = (off_t)ftell(stdin);
224 }
225
226 i = 0;
227 args[i++] = _PATH_SENDMAIL; /* Build sendmail's argument list. */
228 args[i++] = "-oee"; /* No errors, just status. */
229 args[i++] = "-odq"; /* Queue it, don't try to deliver. */
230 args[i++] = "-oi"; /* Ignore '.' on a line by itself. */
231
232 if (from_sys != NULL) { /* Set sender's host name. */
233 if (strchr(from_sys, '.') == NULL)
234 (void)snprintf(buf, sizeof(buf),
235 "-oMs%s.%s", from_sys, domain);
236 else
237 (void)snprintf(buf, sizeof(buf), "-oMs%s", from_sys);
238 if ((args[i++] = strdup(buf)) == NULL)
239 err(EX_TEMPFAIL, NULL);
240 }
241 /* Set protocol used. */
242 (void)snprintf(buf, sizeof(buf), "-oMr%s", domain);
243 if ((args[i++] = strdup(buf)) == NULL)
244 err(EX_TEMPFAIL, NULL);
245
246 /* Set name of ``from'' person. */
247 (void)snprintf(buf, sizeof(buf), "-f%s%s",
248 from_path ? from_path : "", from_user);
249 if ((args[i++] = strdup(buf)) == NULL)
250 err(EX_TEMPFAIL, NULL);
251
252 /*
253 * Don't copy arguments beginning with - as they will be
254 * passed to sendmail and could be interpreted as flags.
255 */
256 do {
257 if (*argv && **argv == '-')
258 err(EX_USAGE, "dash precedes argument: %s", *argv);
259 } while ((args[i++] = *argv++) != NULL);
260
261 if (debug) {
262 (void)fprintf(stderr, "Sendmail arguments:\n");
263 for (i = 0; args[i]; i++)
264 (void)fprintf(stderr, "\t%s\n", args[i]);
265 }
266
267 /*
268 * If called with a regular file as standard input, seek to the right
269 * position in the file and just exec sendmail. Could probably skip
270 * skip the stat, but it's not unreasonable to believe that a failed
271 * seek will cause future reads to fail.
272 */
273 if (!fstat(STDIN_FILENO, &sb) && S_ISREG(sb.st_mode)) {
274 if (lseek(STDIN_FILENO, offset, SEEK_SET) != offset)
275 err(EX_TEMPFAIL, "stdin seek");
276 execv(_PATH_SENDMAIL, args);
277 err(EX_OSERR, "%s", _PATH_SENDMAIL);
278 }
279
280 if (pipe(pdes) < 0)
281 err(EX_OSERR, NULL);
282
283 switch (pid = vfork()) {
284 case -1: /* Err. */
285 err(EX_OSERR, NULL);
286 case 0: /* Child. */
287 if (pdes[0] != STDIN_FILENO) {
288 (void)dup2(pdes[0], STDIN_FILENO);
289 (void)close(pdes[0]);
290 }
291 (void)close(pdes[1]);
292 execv(_PATH_SENDMAIL, args);
293 _exit(127);
294 /* NOTREACHED */
295 }
296
297 if ((fp = fdopen(pdes[1], "w")) == NULL)
298 err(EX_OSERR, NULL);
299 (void)close(pdes[0]);
300
301 /* Copy the file down the pipe. */
302 do {
303 (void)fprintf(fp, "%s", lbuf);
304 } while (fgets(lbuf, sizeof(lbuf), stdin) != NULL);
305
306 if (ferror(stdin))
307 err(EX_TEMPFAIL, "stdin: %s", strerror(errno));
308
309 if (fclose(fp))
310 err(EX_OSERR, NULL);
311
312 if ((waitpid(pid, &status, 0)) == -1)
313 err(EX_OSERR, "%s", _PATH_SENDMAIL);
314
315 if (!WIFEXITED(status))
316 err(EX_OSERR,
317 "%s: did not terminate normally", _PATH_SENDMAIL);
318
319 if (WEXITSTATUS(status))
320 err(status, "%s: terminated with %d (non-zero) status",
321 _PATH_SENDMAIL, WEXITSTATUS(status));
322 exit(EX_OK);
323}
324
325void
326usage()
327{
328 (void)fprintf(stderr, "usage: rmail [-T] [-D domain] user ...\n");
329 exit(EX_USAGE);
330}
331
332#ifdef __STDC__
333#include <stdarg.h>
334#else
335#include <varargs.h>
336#endif
337
338void
339#ifdef __STDC__
340err(int eval, const char *fmt, ...)
341#else
342err(eval, fmt, va_alist)
343 int eval;
344 const char *fmt;
345 va_dcl
346#endif
347{
348 va_list ap;
349#if __STDC__
350 va_start(ap, fmt);
351#else
352 va_start(ap);
353#endif
354 (void)fprintf(stderr, "rmail: ");
355 (void)vfprintf(stderr, fmt, ap);
356 va_end(ap);
357 (void)fprintf(stderr, "\n");
358 exit(eval);
359}