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