Commit | Line | Data |
---|---|---|
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 | |
9 | char 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 | 15 | static 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 |
56 | void err __P((int, const char *, ...)); |
57 | void usage __P((void)); | |
0f5791a6 | 58 | |
1acd8217 | 59 | int |
9adc8f2c | 60 | main(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 | ||
300 | void | |
301 | usage() | |
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 | |
313 | void | |
314 | #ifdef __STDC__ | |
315 | err(int eval, const char *fmt, ...) | |
316 | #else | |
317 | err(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 | } |