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