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 |
35 | static 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 |
41 | static 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 | |
81 | void err __P((int, const char *, ...)); |
82 | void usage __P((void)); |
83 | |
84 | int |
85 | main(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 | |
325 | void |
326 | usage() |
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 | |
338 | void |
339 | #ifdef __STDC__ |
340 | err(int eval, const char *fmt, ...) |
341 | #else |
342 | err(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 | } |