2 * Copyright (c) 1998-2001 Sendmail, Inc. and its suppliers.
4 * Copyright (c) 1988, 1993
5 * The Regents of the University of California. All rights reserved.
7 * By using this file, you agree to the terms and conditions set
8 * forth in the LICENSE file which can be found at the top level of
9 * the sendmail distribution.
11 * $FreeBSD: src/contrib/sendmail/rmail/rmail.c,v 1.4.6.7 2002/09/03 01:50:11 gshapiro Exp $
12 * $DragonFly: src/contrib/sendmail/rmail/Attic/rmail.c,v 1.2 2003/06/17 04:24:06 dillon Exp $
19 "@(#) Copyright (c) 1998-2001 Sendmail, Inc. and its suppliers.\n\
20 All rights reserved.\n\
21 Copyright (c) 1988, 1993\n\
22 The Regents of the University of California. All rights reserved.\n")
24 SM_IDSTR(id, "@(#)$Id: rmail.c,v 8.61 2001/09/18 21:45:29 gshapiro Exp $")
27 * RMAIL -- UUCP mail server.
29 * This program reads the >From ... remote from ... lines that UUCP is so
30 * fond of and turns them into something reasonable. It then execs sendmail
31 * with various options built from these lines.
33 * The expected syntax is:
35 * <user> := [-a-z0-9]+
36 * <date> := ctime format
37 * <site> := [-a-z0-9!]+
38 * <blank line> := "^\n$"
39 * <from> := "From" <space> <user> <space> <date>
40 * [<space> "remote from" <space> <site>]
41 * <forward> := ">" <from>
42 * msg := <from> <forward>* <blank-line> <body>
44 * The output of rmail(8) compresses the <forward> lines into a single
47 * The err(3) routine is included here deliberately to make this code
48 * a bit more portable.
51 #include <sys/types.h>
52 #include <sys/param.h>
60 #include <sm/string.h>
63 # undef EX_OK /* unistd.h may have another use for this */
68 #include <sm/errstring.h>
69 #include <sendmail/pathnames.h>
71 static void err __P((int, const char *, ...));
72 static void usage __P((void));
73 static char *xalloc __P((int));
75 #define newstr(s) strcpy(xalloc(strlen(s) + 1), s)
83 /* some systems can't handle size zero mallocs */
89 err(EX_TEMPFAIL, "out of memory");
98 int ch, debug, i, pdes[2], pid, status;
99 size_t fplen = 0, fptlen = 0, len;
102 char *addrp = NULL, *domain, *p, *t;
103 char *from_path, *from_sys, *from_user;
104 char **args, buf[2048], lbuf[2048];
110 domain = "UUCP"; /* Default "domain". */
111 while ((ch = getopt(argc, argv, "D:T")) != -1)
135 from_path = from_sys = from_user = NULL;
138 /* Get and nul-terminate the line. */
139 if (sm_io_fgets(smioin, SM_TIME_DEFAULT, lbuf,
140 sizeof(lbuf)) == NULL)
141 err(EX_DATAERR, "no data");
142 if ((p = strchr(lbuf, '\n')) == NULL)
143 err(EX_DATAERR, "line too long");
146 /* Parse lines until reach a non-"From" line. */
147 if (!strncmp(lbuf, "From ", 5))
149 else if (!strncmp(lbuf, ">From ", 6))
151 else if (offset == 0)
153 "missing or empty From line: %s", lbuf);
160 if (addrp == NULL || *addrp == '\0')
161 err(EX_DATAERR, "corrupted From line: %s", lbuf);
163 /* Use the "remote from" if it exists. */
164 for (p = addrp; (p = strchr(p + 1, 'r')) != NULL; )
166 if (!strncmp(p, "remote from ", 12))
168 for (t = p += 12; *t != '\0'; ++t)
170 if (isascii(*t) && isspace(*t))
175 (void) sm_io_fprintf(smioerr,
183 /* Else use the string up to the last bang. */
187 err(EX_DATAERR, "bang starts address: %s",
189 else if ((t = strrchr(addrp, '!')) != NULL)
196 "corrupted From line: %s", lbuf);
198 (void) sm_io_fprintf(smioerr,
204 /* 'p' now points to any system string from this line. */
207 /* Nul terminate it as necessary. */
208 for (t = p; *t != '\0'; ++t)
210 if (isascii(*t) && isspace(*t))
215 /* If the first system, copy to the from_sys string. */
216 if (from_sys == NULL)
218 from_sys = newstr(p);
220 (void) sm_io_fprintf(smioerr,
226 /* Concatenate to the path string. */
228 if (from_path == NULL)
231 if ((from_path = malloc(fptlen = 256)) == NULL)
232 err(EX_TEMPFAIL, "out of memory");
234 if (fplen + len + 2 > fptlen)
236 fptlen += SM_MAX(fplen + len + 2, 256);
237 if ((from_path = realloc(from_path,
239 err(EX_TEMPFAIL, "out of memory");
241 memmove(from_path + fplen, p, len);
243 from_path[fplen++] = '!';
244 from_path[fplen] = '\0';
247 /* Save off from user's address; the last one wins. */
248 for (p = addrp; *p != '\0'; ++p)
250 if (isascii(*p) && isspace(*p))
256 if (from_user != NULL)
258 from_user = newstr(addrp);
262 if (from_path != NULL)
263 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
266 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
267 "from_user: %s\n", from_user);
271 offset = (off_t)sm_io_tell(smioin, SM_TIME_DEFAULT);
275 /* Allocate args (with room for sendmail args as well as recipients */
276 args = (char **)xalloc(sizeof(*args) * (10 + argc));
279 args[i++] = _PATH_SENDMAIL; /* Build sendmail's argument list. */
280 args[i++] = "-G"; /* relay submission */
281 args[i++] = "-oee"; /* No errors, just status. */
283 args[i++] = "-odq"; /* Queue it, don't try to deliver. */
285 args[i++] = "-odi"; /* Deliver in foreground. */
287 args[i++] = "-oi"; /* Ignore '.' on a line by itself. */
289 /* set from system and protocol used */
290 if (from_sys == NULL)
291 sm_snprintf(buf, sizeof(buf), "-p%s", domain);
292 else if (strchr(from_sys, '.') == NULL)
293 sm_snprintf(buf, sizeof(buf), "-p%s:%s.%s",
294 domain, from_sys, domain);
296 sm_snprintf(buf, sizeof(buf), "-p%s:%s", domain, from_sys);
297 args[i++] = newstr(buf);
299 /* Set name of ``from'' person. */
300 sm_snprintf(buf, sizeof(buf), "-f%s%s",
301 from_path ? from_path : "", from_user);
302 args[i++] = newstr(buf);
305 ** Don't copy arguments beginning with - as they will be
306 ** passed to sendmail and could be interpreted as flags.
307 ** To prevent confusion of sendmail wrap < and > around
308 ** the address (helps to pass addrs like @gw1,@gw2:aa@bb)
311 while (*argv != NULL)
314 err(EX_USAGE, "dash precedes argument: %s", *argv);
316 if (strchr(*argv, ',') == NULL || strchr(*argv, '<') != NULL)
320 len = strlen(*argv) + 3;
321 if ((args[i] = malloc(len)) == NULL)
322 err(EX_TEMPFAIL, "Cannot malloc");
323 sm_snprintf(args[i++], len, "<%s>", *argv);
328 /* Paranoia check, argc used for args[] bound */
330 err(EX_SOFTWARE, "Argument count mismatch");
336 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
337 "Sendmail arguments:\n");
338 for (i = 0; args[i] != NULL; i++)
339 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
344 ** If called with a regular file as standard input, seek to the right
345 ** position in the file and just exec sendmail. Could probably skip
346 ** skip the stat, but it's not unreasonable to believe that a failed
347 ** seek will cause future reads to fail.
350 if (!fstat(STDIN_FILENO, &sb) && S_ISREG(sb.st_mode))
352 if (lseek(STDIN_FILENO, offset, SEEK_SET) != offset)
353 err(EX_TEMPFAIL, "stdin seek");
354 (void) execv(_PATH_SENDMAIL, args);
355 err(EX_OSERR, "%s", _PATH_SENDMAIL);
359 err(EX_OSERR, "pipe failed");
361 switch (pid = fork())
364 err(EX_OSERR, "fork failed");
368 if (pdes[0] != STDIN_FILENO)
370 (void) dup2(pdes[0], STDIN_FILENO);
371 (void) close(pdes[0]);
373 (void) close(pdes[1]);
374 (void) execv(_PATH_SENDMAIL, args);
375 err(EX_UNAVAILABLE, "%s", _PATH_SENDMAIL);
379 if ((fp = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT, (void *) &(pdes[1]),
380 SM_IO_WRONLY, NULL)) == NULL)
381 err(EX_OSERR, "sm_io_open failed");
382 (void) close(pdes[0]);
384 /* Copy the file down the pipe. */
387 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%s", lbuf);
388 } while (sm_io_fgets(smioin, SM_TIME_DEFAULT, lbuf,
389 sizeof(lbuf)) != NULL);
391 if (sm_io_error(smioin))
392 err(EX_TEMPFAIL, "stdin: %s", sm_errstring(errno));
394 if (sm_io_close(fp, SM_TIME_DEFAULT))
395 err(EX_OSERR, "sm_io_close failed");
397 if ((waitpid(pid, &status, 0)) == -1)
398 err(EX_OSERR, "%s", _PATH_SENDMAIL);
400 if (!WIFEXITED(status))
401 err(EX_OSERR, "%s: did not terminate normally", _PATH_SENDMAIL);
403 if (WEXITSTATUS(status))
404 err(status, "%s: terminated with %d (non-zero) status",
405 _PATH_SENDMAIL, WEXITSTATUS(status));
414 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
415 "usage: rmail [-T] [-D domain] user ...\n");
421 err(int eval, const char *fmt, ...)
423 err(eval, fmt, va_alist)
427 #endif /* __STDC__ */
433 SM_VA_START(ap, fmt);
434 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, "rmail: ");
435 (void) sm_io_vfprintf(smioerr, SM_TIME_DEFAULT, fmt, ap);
437 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, "\n");