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.2 (Berkeley) 11/13/94"; 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 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 (from_user != NULL) 189 free(from_user); 190 if ((from_user = strdup(addrp)) == NULL) 191 err(EX_TEMPFAIL, NULL); 192 193 if (debug) { 194 if (from_path != NULL) 195 (void)fprintf(stderr, 196 "from_path: %s\n", from_path); 197 (void)fprintf(stderr, "from_user: %s\n", from_user); 198 } 199 200 if (offset != -1) 201 offset = (off_t)ftell(stdin); 202 } 203 204 i = 0; 205 args[i++] = _PATH_SENDMAIL; /* Build sendmail's argument list. */ 206 args[i++] = "-oee"; /* No errors, just status. */ 207 args[i++] = "-odq"; /* Queue it, don't try to deliver. */ 208 args[i++] = "-oi"; /* Ignore '.' on a line by itself. */ 209 210 if (from_sys != NULL) { /* Set sender's host name. */ 211 if (strchr(from_sys, '.') == NULL) 212 (void)snprintf(buf, sizeof(buf), 213 "-oMs%s.%s", from_sys, domain); 214 else 215 (void)snprintf(buf, sizeof(buf), "-oMs%s", from_sys); 216 if ((args[i++] = strdup(buf)) == NULL) 217 err(EX_TEMPFAIL, NULL); 218 } 219 /* Set protocol used. */ 220 (void)snprintf(buf, sizeof(buf), "-oMr%s", domain); 221 if ((args[i++] = strdup(buf)) == NULL) 222 err(EX_TEMPFAIL, NULL); 223 224 /* Set name of ``from'' person. */ 225 (void)snprintf(buf, sizeof(buf), "-f%s%s", 226 from_path ? from_path : "", from_user); 227 if ((args[i++] = strdup(buf)) == NULL) 228 err(EX_TEMPFAIL, NULL); 229 230 /* 231 * Don't copy arguments beginning with - as they will be 232 * passed to sendmail and could be interpreted as flags. 233 * To prevent confusion of sendmail wrap < and > around 234 * the address (helps to pass addrs like @gw1,@gw2:aa@bb) 235 */ 236 while (*argv) { 237 if (**argv == '-') 238 err(EX_USAGE, "dash precedes argument: %s", *argv); 239 if (strchr(*argv, ',') == NULL || strchr(*argv, '<') != NULL) 240 args[i++] = *argv; 241 else { 242 if ((args[i] = malloc(strlen(*argv) + 3)) == NULL) 243 err(EX_TEMPFAIL, "Cannot malloc"); 244 sprintf (args [i++], "<%s>", *argv); 245 } 246 argv++; 247 } 248 args[i] = 0; 249 250 if (debug) { 251 (void)fprintf(stderr, "Sendmail arguments:\n"); 252 for (i = 0; args[i]; i++) 253 (void)fprintf(stderr, "\t%s\n", args[i]); 254 } 255 256 /* 257 * If called with a regular file as standard input, seek to the right 258 * position in the file and just exec sendmail. Could probably skip 259 * skip the stat, but it's not unreasonable to believe that a failed 260 * seek will cause future reads to fail. 261 */ 262 if (!fstat(STDIN_FILENO, &sb) && S_ISREG(sb.st_mode)) { 263 if (lseek(STDIN_FILENO, offset, SEEK_SET) != offset) 264 err(EX_TEMPFAIL, "stdin seek"); 265 execv(_PATH_SENDMAIL, args); 266 err(EX_OSERR, "%s", _PATH_SENDMAIL); 267 } 268 269 if (pipe(pdes) < 0) 270 err(EX_OSERR, NULL); 271 272 switch (pid = vfork()) { 273 case -1: /* Err. */ 274 err(EX_OSERR, NULL); 275 case 0: /* Child. */ 276 if (pdes[0] != STDIN_FILENO) { 277 (void)dup2(pdes[0], STDIN_FILENO); 278 (void)close(pdes[0]); 279 } 280 (void)close(pdes[1]); 281 execv(_PATH_SENDMAIL, args); 282 _exit(127); 283 /* NOTREACHED */ 284 } 285 286 if ((fp = fdopen(pdes[1], "w")) == NULL) 287 err(EX_OSERR, NULL); 288 (void)close(pdes[0]); 289 290 /* Copy the file down the pipe. */ 291 do { 292 (void)fprintf(fp, "%s", lbuf); 293 } while (fgets(lbuf, sizeof(lbuf), stdin) != NULL); 294 295 if (ferror(stdin)) 296 err(EX_TEMPFAIL, "stdin: %s", strerror(errno)); 297 298 if (fclose(fp)) 299 err(EX_OSERR, NULL); 300 301 if ((waitpid(pid, &status, 0)) == -1) 302 err(EX_OSERR, "%s", _PATH_SENDMAIL); 303 304 if (!WIFEXITED(status)) 305 err(EX_OSERR, 306 "%s: did not terminate normally", _PATH_SENDMAIL); 307 308 if (WEXITSTATUS(status)) 309 err(status, "%s: terminated with %d (non-zero) status", 310 _PATH_SENDMAIL, WEXITSTATUS(status)); 311 exit(EX_OK); 312 } 313 314 void 315 usage() 316 { 317 (void)fprintf(stderr, "usage: rmail [-T] [-D domain] user ...\n"); 318 exit(EX_USAGE); 319 } 320 321 #ifdef __STDC__ 322 #include <stdarg.h> 323 #else 324 #include <varargs.h> 325 #endif 326 327 void 328 #ifdef __STDC__ 329 err(int eval, const char *fmt, ...) 330 #else 331 err(eval, fmt, va_alist) 332 int eval; 333 const char *fmt; 334 va_dcl 335 #endif 336 { 337 va_list ap; 338 #if __STDC__ 339 va_start(ap, fmt); 340 #else 341 va_start(ap); 342 #endif 343 (void)fprintf(stderr, "rmail: "); 344 (void)vfprintf(stderr, fmt, ap); 345 va_end(ap); 346 (void)fprintf(stderr, "\n"); 347 exit(eval); 348 } 349