1 /* 2 * Copyright (c) 1998 Sendmail, Inc. All rights reserved. 3 * Copyright (c) 1988, 1993 4 * The Regents of the University of California. All rights reserved. 5 * 6 * By using this file, you agree to the terms and conditions set 7 * forth in the LICENSE file which can be found at the top level of 8 * the sendmail distribution. 9 * 10 */ 11 12 #ifndef lint 13 static char copyright[] = 14 "@(#) Copyright (c) 1988, 1993\n\ 15 The Regents of the University of California. All rights reserved.\n"; 16 #endif /* not lint */ 17 18 #ifndef lint 19 static char sccsid[] = "@(#)rmail.c 8.18 (Berkeley) 10/23/1998"; 20 #endif /* not lint */ 21 22 /* 23 * RMAIL -- UUCP mail server. 24 * 25 * This program reads the >From ... remote from ... lines that UUCP is so 26 * fond of and turns them into something reasonable. It then execs sendmail 27 * with various options built from these lines. 28 * 29 * The expected syntax is: 30 * 31 * <user> := [-a-z0-9]+ 32 * <date> := ctime format 33 * <site> := [-a-z0-9!]+ 34 * <blank line> := "^\n$" 35 * <from> := "From" <space> <user> <space> <date> 36 * [<space> "remote from" <space> <site>] 37 * <forward> := ">" <from> 38 * msg := <from> <forward>* <blank-line> <body> 39 * 40 * The output of rmail(8) compresses the <forward> lines into a single 41 * from path. 42 * 43 * The err(3) routine is included here deliberately to make this code 44 * a bit more portable. 45 */ 46 #include <sys/param.h> 47 #include <sys/stat.h> 48 #include <sys/wait.h> 49 50 #include <ctype.h> 51 #include <fcntl.h> 52 #ifdef BSD4_4 53 # define FORK vfork 54 # include <paths.h> 55 #else 56 # define FORK fork 57 # ifndef _PATH_SENDMAIL 58 # define _PATH_SENDMAIL "/usr/lib/sendmail" 59 # endif 60 #endif 61 #include <stdio.h> 62 #include <stdlib.h> 63 #include <string.h> 64 #include <unistd.h> 65 #ifdef EX_OK 66 # undef EX_OK /* unistd.h may have another use for this */ 67 #endif 68 #include <sysexits.h> 69 70 #ifndef MAX 71 # define MAX(a, b) ((a) < (b) ? (b) : (a)) 72 #endif 73 74 #ifndef __P 75 # ifdef __STDC__ 76 # define __P(protos) protos 77 # else 78 # define __P(protos) () 79 # define const 80 # endif 81 #endif 82 83 #if defined(BSD4_4) || defined(linux) || SOLARIS >= 20600 || (SOLARIS < 10000 && SOLARIS >= 206) 84 # define HASSNPRINTF 1 85 #endif 86 87 #if defined(IRIX64) || defined(IRIX5) || defined(IRIX6) || \ 88 defined(BSD4_4) || defined(__osf__) || defined(__GNU_LIBRARY__) 89 # ifndef HASSTRERROR 90 # define HASSTRERROR 1 /* has strerror(3) */ 91 # endif 92 #endif 93 94 #if !HASSTRERROR 95 extern char *strerror __P((int)); 96 #endif 97 98 #if defined(sun) && !defined(BSD) && !defined(SOLARIS) && !defined(__svr4__) && !defined(__SVR4) 99 # define memmove(d, s, l) (bcopy((s), (d), (l))) 100 #endif 101 102 #if !HASSNPRINTF 103 extern int snprintf __P((char *, size_t, const char *, ...)); 104 #endif /* !HASSNPRINTF */ 105 106 u_char tTdvect[100]; 107 108 void err __P((int, const char *, ...)); 109 void usage __P((void)); 110 char *xalloc __P((int)); 111 112 #define newstr(s) strcpy(xalloc(strlen(s) + 1), s) 113 114 char * 115 xalloc(sz) 116 register int sz; 117 { 118 register char *p; 119 120 /* some systems can't handle size zero mallocs */ 121 if (sz <= 0) 122 sz = 1; 123 124 p = malloc((unsigned) sz); 125 if (p == NULL) 126 err(EX_TEMPFAIL, "out of memory"); 127 return (p); 128 } 129 130 int 131 main(argc, argv) 132 int argc; 133 char *argv[]; 134 { 135 extern char *optarg; 136 extern int errno, optind; 137 FILE *fp; 138 struct stat sb; 139 size_t fplen = 0, fptlen = 0, len; 140 off_t offset; 141 int ch, debug, i, pdes[2], pid, status; 142 char *addrp = NULL, *domain, *p, *t; 143 char *from_path, *from_sys, *from_user; 144 char *args[100], buf[2048], lbuf[2048]; 145 146 debug = 0; 147 domain = "UUCP"; /* Default "domain". */ 148 while ((ch = getopt(argc, argv, "D:T")) != -1) 149 switch (ch) { 150 case 'T': 151 debug = 1; 152 break; 153 case 'D': 154 domain = optarg; 155 break; 156 case '?': 157 default: 158 usage(); 159 } 160 argc -= optind; 161 argv += optind; 162 163 if (argc < 1) 164 usage(); 165 166 fplen = fptlen = 0; 167 addrp = ""; 168 from_path = from_sys = from_user = NULL; 169 for (offset = 0;;) { 170 171 /* Get and nul-terminate the line. */ 172 if (fgets(lbuf, sizeof(lbuf), stdin) == NULL) 173 exit (EX_DATAERR); 174 if ((p = strchr(lbuf, '\n')) == NULL) 175 err(EX_DATAERR, "line too long"); 176 *p = '\0'; 177 178 /* Parse lines until reach a non-"From" line. */ 179 if (!strncmp(lbuf, "From ", 5)) 180 addrp = lbuf + 5; 181 else if (!strncmp(lbuf, ">From ", 6)) 182 addrp = lbuf + 6; 183 else if (offset == 0) 184 err(EX_DATAERR, 185 "missing or empty From line: %s", lbuf); 186 else { 187 *p = '\n'; 188 break; 189 } 190 191 if (*addrp == '\0') 192 err(EX_DATAERR, "corrupted From line: %s", lbuf); 193 194 /* Use the "remote from" if it exists. */ 195 for (p = addrp; (p = strchr(p + 1, 'r')) != NULL;) 196 if (!strncmp(p, "remote from ", 12)) { 197 for (t = p += 12; 198 *t && !(isascii(*t) && isspace(*t)); ++t); 199 *t = '\0'; 200 if (debug) 201 (void)fprintf(stderr, 202 "remote from: %s\n", p); 203 break; 204 } 205 206 /* Else use the string up to the last bang. */ 207 if (p == NULL) { 208 if (*addrp == '!') 209 err(EX_DATAERR, 210 "bang starts address: %s", addrp); 211 else if ((t = strrchr(addrp, '!')) != NULL) { 212 *t = '\0'; 213 p = addrp; 214 addrp = t + 1; 215 if (*addrp == '\0') 216 err(EX_DATAERR, 217 "corrupted From line: %s", lbuf); 218 if (debug) 219 (void)fprintf(stderr, "bang: %s\n", p); 220 } 221 } 222 /* 'p' now points to any system string from this line. */ 223 if (p != NULL) { 224 /* Nul terminate it as necessary. */ 225 for (t = p; *t && !(isascii(*t) && isspace(*t)); ++t); 226 *t = '\0'; 227 228 /* If the first system, copy to the from_sys string. */ 229 if (from_sys == NULL) { 230 from_sys = newstr(p); 231 if (debug) 232 (void)fprintf(stderr, 233 "from_sys: %s\n", from_sys); 234 } 235 236 /* Concatenate to the path string. */ 237 len = t - p; 238 if (from_path == NULL) { 239 fplen = 0; 240 if ((from_path = malloc(fptlen = 256)) == NULL) 241 err(EX_TEMPFAIL, NULL); 242 } 243 if (fplen + len + 2 > fptlen) { 244 fptlen += MAX(fplen + len + 2, 256); 245 if ((from_path = 246 realloc(from_path, fptlen)) == NULL) 247 err(EX_TEMPFAIL, NULL); 248 } 249 memmove(from_path + fplen, p, len); 250 fplen += len; 251 from_path[fplen++] = '!'; 252 from_path[fplen] = '\0'; 253 } 254 255 /* Save off from user's address; the last one wins. */ 256 for (p = addrp; *p && !(isascii(*p) && isspace(*p)); ++p); 257 *p = '\0'; 258 if (*addrp == '\0') 259 addrp = "<>"; 260 if (from_user != NULL) 261 free(from_user); 262 from_user = newstr(addrp); 263 264 if (debug) { 265 if (from_path != NULL) 266 (void)fprintf(stderr, 267 "from_path: %s\n", from_path); 268 (void)fprintf(stderr, "from_user: %s\n", from_user); 269 } 270 271 if (offset != -1) 272 offset = (off_t)ftell(stdin); 273 } 274 275 i = 0; 276 args[i++] = _PATH_SENDMAIL; /* Build sendmail's argument list. */ 277 args[i++] = "-oee"; /* No errors, just status. */ 278 #ifdef QUEUE_ONLY 279 args[i++] = "-odq"; /* Queue it, don't try to deliver. */ 280 #else 281 args[i++] = "-odi"; /* Deliver in foreground. */ 282 #endif 283 args[i++] = "-oi"; /* Ignore '.' on a line by itself. */ 284 285 /* set from system and protocol used */ 286 if (from_sys == NULL) 287 (void)snprintf(buf, sizeof(buf), "-p%s", domain); 288 else if (strchr(from_sys, '.') == NULL) 289 (void)snprintf(buf, sizeof(buf), "-p%s:%s.%s", 290 domain, from_sys, domain); 291 else 292 (void)snprintf(buf, sizeof(buf), "-p%s:%s", domain, from_sys); 293 args[i++] = newstr(buf); 294 295 /* Set name of ``from'' person. */ 296 (void)snprintf(buf, sizeof(buf), "-f%s%s", 297 from_path ? from_path : "", from_user); 298 args[i++] = newstr(buf); 299 300 /* 301 * Don't copy arguments beginning with - as they will be 302 * passed to sendmail and could be interpreted as flags. 303 * To prevent confusion of sendmail wrap < and > around 304 * the address (helps to pass addrs like @gw1,@gw2:aa@bb) 305 */ 306 while (*argv) { 307 if (**argv == '-') 308 err(EX_USAGE, "dash precedes argument: %s", *argv); 309 if (strchr(*argv, ',') == NULL || strchr(*argv, '<') != NULL) 310 args[i++] = *argv; 311 else { 312 if ((args[i] = malloc(strlen(*argv) + 3)) == NULL) 313 err(EX_TEMPFAIL, "Cannot malloc"); 314 sprintf (args [i++], "<%s>", *argv); 315 } 316 argv++; 317 } 318 args[i] = 0; 319 320 if (debug) { 321 (void)fprintf(stderr, "Sendmail arguments:\n"); 322 for (i = 0; args[i]; i++) 323 (void)fprintf(stderr, "\t%s\n", args[i]); 324 } 325 326 /* 327 * If called with a regular file as standard input, seek to the right 328 * position in the file and just exec sendmail. Could probably skip 329 * skip the stat, but it's not unreasonable to believe that a failed 330 * seek will cause future reads to fail. 331 */ 332 if (!fstat(STDIN_FILENO, &sb) && S_ISREG(sb.st_mode)) { 333 if (lseek(STDIN_FILENO, offset, SEEK_SET) != offset) 334 err(EX_TEMPFAIL, "stdin seek"); 335 execv(_PATH_SENDMAIL, args); 336 err(EX_OSERR, "%s", _PATH_SENDMAIL); 337 } 338 339 if (pipe(pdes) < 0) 340 err(EX_OSERR, NULL); 341 342 switch (pid = FORK()) { 343 case -1: /* Err. */ 344 err(EX_OSERR, NULL); 345 case 0: /* Child. */ 346 if (pdes[0] != STDIN_FILENO) { 347 (void)dup2(pdes[0], STDIN_FILENO); 348 (void)close(pdes[0]); 349 } 350 (void)close(pdes[1]); 351 execv(_PATH_SENDMAIL, args); 352 _exit(127); 353 /* NOTREACHED */ 354 } 355 356 if ((fp = fdopen(pdes[1], "w")) == NULL) 357 err(EX_OSERR, NULL); 358 (void)close(pdes[0]); 359 360 /* Copy the file down the pipe. */ 361 do { 362 (void)fprintf(fp, "%s", lbuf); 363 } while (fgets(lbuf, sizeof(lbuf), stdin) != NULL); 364 365 if (ferror(stdin)) 366 err(EX_TEMPFAIL, "stdin: %s", strerror(errno)); 367 368 if (fclose(fp)) 369 err(EX_OSERR, NULL); 370 371 if ((waitpid(pid, &status, 0)) == -1) 372 err(EX_OSERR, "%s", _PATH_SENDMAIL); 373 374 if (!WIFEXITED(status)) 375 err(EX_OSERR, 376 "%s: did not terminate normally", _PATH_SENDMAIL); 377 378 if (WEXITSTATUS(status)) 379 err(status, "%s: terminated with %d (non-zero) status", 380 _PATH_SENDMAIL, WEXITSTATUS(status)); 381 exit(EX_OK); 382 } 383 384 void 385 usage() 386 { 387 (void)fprintf(stderr, "usage: rmail [-T] [-D domain] user ...\n"); 388 exit(EX_USAGE); 389 } 390 391 #ifdef __STDC__ 392 #include <stdarg.h> 393 #else 394 #include <varargs.h> 395 #endif 396 397 void 398 #ifdef __STDC__ 399 err(int eval, const char *fmt, ...) 400 #else 401 err(eval, fmt, va_alist) 402 int eval; 403 const char *fmt; 404 va_dcl 405 #endif 406 { 407 va_list ap; 408 #if __STDC__ 409 va_start(ap, fmt); 410 #else 411 va_start(ap); 412 #endif 413 (void)fprintf(stderr, "rmail: "); 414 (void)vfprintf(stderr, fmt, ap); 415 va_end(ap); 416 (void)fprintf(stderr, "\n"); 417 exit(eval); 418 } 419 420 #if !HASSTRERROR 421 422 char * 423 strerror(eno) 424 int eno; 425 { 426 extern int sys_nerr; 427 extern char *sys_errlist[]; 428 static char ebuf[60]; 429 430 if (eno >= 0 && eno < sys_nerr) 431 return sys_errlist[eno]; 432 (void) sprintf(ebuf, "Error %d", eno); 433 return ebuf; 434 } 435 436 #endif /* !HASSTRERROR */ 437