1 # 2 3 #include "rcv.h" 4 #ifdef VMUNIX 5 #include <sys/wait.h> 6 #endif 7 #include <ctype.h> 8 #include <sys/stat.h> 9 10 /* 11 * Mail -- a mail program 12 * 13 * Mail to others. 14 */ 15 16 static char *SccsId = "@(#)send.c 2.12 07/02/83"; 17 18 /* 19 * Send message described by the passed pointer to the 20 * passed output buffer. Return -1 on error, but normally 21 * the number of lines written. Adjust the status: field 22 * if need be. If doign is set, suppress ignored header fields. 23 */ 24 send(mailp, obuf, doign) 25 struct message *mailp; 26 FILE *obuf; 27 { 28 register struct message *mp; 29 register int t; 30 long c; 31 FILE *ibuf; 32 char line[LINESIZE], field[BUFSIZ]; 33 int lc, ishead, infld, fline, dostat; 34 char *cp, *cp2; 35 36 mp = mailp; 37 ibuf = setinput(mp); 38 c = mp->m_size; 39 ishead = 1; 40 dostat = 1; 41 infld = 0; 42 fline = 1; 43 lc = 0; 44 while (c > 0L) { 45 fgets(line, LINESIZE, ibuf); 46 c -= (long) strlen(line); 47 lc++; 48 if (ishead) { 49 /* 50 * First line is the From line, so no headers 51 * there to worry about 52 */ 53 if (fline) { 54 fline = 0; 55 goto writeit; 56 } 57 /* 58 * If line is blank, we've reached end of 59 * headers, so force out status: field 60 * and note that we are no longer in header 61 * fields 62 */ 63 if (line[0] == '\n') { 64 if (dostat) { 65 statusput(mailp, obuf, doign); 66 dostat = 0; 67 } 68 ishead = 0; 69 goto writeit; 70 } 71 /* 72 * If this line is a continuation (via space or tab) 73 * of a previous header field, just echo it 74 * (unless the field should be ignored). 75 */ 76 if (infld && (isspace(line[0]) || line[0] == '\t')) { 77 if (doign && isign(field)) continue; 78 goto writeit; 79 } 80 infld = 0; 81 /* 82 * If we are no longer looking at real 83 * header lines, force out status: 84 * This happens in uucp style mail where 85 * there are no headers at all. 86 */ 87 if (!headerp(line)) { 88 if (dostat) { 89 statusput(mailp, obuf, doign); 90 dostat = 0; 91 } 92 putc('\n', obuf); 93 ishead = 0; 94 goto writeit; 95 } 96 infld++; 97 /* 98 * Pick up the header field. 99 * If it is an ignored field and 100 * we care about such things, skip it. 101 */ 102 cp = line; 103 cp2 = field; 104 while (*cp && *cp != ':' && !isspace(*cp)) 105 *cp2++ = *cp++; 106 *cp2 = 0; 107 if (doign && isign(field)) 108 continue; 109 /* 110 * If the field is "status," go compute and print the 111 * real Status: field 112 */ 113 if (icequal(field, "status")) { 114 if (dostat) { 115 statusput(mailp, obuf, doign); 116 dostat = 0; 117 } 118 continue; 119 } 120 } 121 writeit: 122 fputs(line, obuf); 123 if (ferror(obuf)) 124 return(-1); 125 } 126 if (ferror(obuf)) 127 return(-1); 128 if (ishead && (mailp->m_flag & MSTATUS)) 129 printf("failed to fix up status field\n"); 130 return(lc); 131 } 132 133 /* 134 * Test if the passed line is a header line, RFC 733 style. 135 */ 136 headerp(line) 137 register char *line; 138 { 139 register char *cp = line; 140 141 while (*cp && !isspace(*cp) && *cp != ':') 142 cp++; 143 while (*cp && isspace(*cp)) 144 cp++; 145 return(*cp == ':'); 146 } 147 148 /* 149 * Output a reasonable looking status field. 150 * But if "status" is ignored and doign, forget it. 151 */ 152 statusput(mp, obuf, doign) 153 register struct message *mp; 154 register FILE *obuf; 155 { 156 char statout[3]; 157 158 if (doign && isign("status")) 159 return; 160 if ((mp->m_flag & (MNEW|MREAD)) == MNEW) 161 return; 162 if (mp->m_flag & MREAD) 163 strcpy(statout, "R"); 164 else 165 strcpy(statout, ""); 166 if ((mp->m_flag & MNEW) == 0) 167 strcat(statout, "O"); 168 fprintf(obuf, "Status: %s\n", statout); 169 } 170 171 172 /* 173 * Interface between the argument list and the mail1 routine 174 * which does all the dirty work. 175 */ 176 177 mail(people) 178 char **people; 179 { 180 register char *cp2; 181 register int s; 182 char *buf, **ap; 183 struct header head; 184 185 for (s = 0, ap = people; *ap != (char *) -1; ap++) 186 s += strlen(*ap) + 1; 187 buf = salloc(s+1); 188 cp2 = buf; 189 for (ap = people; *ap != (char *) -1; ap++) { 190 cp2 = copy(*ap, cp2); 191 *cp2++ = ' '; 192 } 193 if (cp2 != buf) 194 cp2--; 195 *cp2 = '\0'; 196 head.h_to = buf; 197 head.h_subject = NOSTR; 198 head.h_cc = NOSTR; 199 head.h_bcc = NOSTR; 200 head.h_seq = 0; 201 mail1(&head); 202 return(0); 203 } 204 205 206 /* 207 * Send mail to a bunch of user names. The interface is through 208 * the mail routine below. 209 */ 210 211 sendmail(str) 212 char *str; 213 { 214 register char **ap; 215 char *bufp; 216 register int t; 217 struct header head; 218 219 if (blankline(str)) 220 head.h_to = NOSTR; 221 else 222 head.h_to = str; 223 head.h_subject = NOSTR; 224 head.h_cc = NOSTR; 225 head.h_bcc = NOSTR; 226 head.h_seq = 0; 227 mail1(&head); 228 return(0); 229 } 230 231 /* 232 * Mail a message on standard input to the people indicated 233 * in the passed header. (Internal interface). 234 */ 235 236 mail1(hp) 237 struct header *hp; 238 { 239 register char *cp; 240 int pid, i, s, p, gotcha; 241 char **namelist, *deliver; 242 struct name *to, *np; 243 struct stat sbuf; 244 FILE *mtf, *postage; 245 int remote = rflag != NOSTR || rmail; 246 char **t; 247 248 /* 249 * Collect user's mail from standard input. 250 * Get the result as mtf. 251 */ 252 253 pid = -1; 254 if ((mtf = collect(hp)) == NULL) 255 return(-1); 256 hp->h_seq = 1; 257 if (hp->h_subject == NOSTR) 258 hp->h_subject = sflag; 259 if (intty && value("askcc") != NOSTR) 260 grabh(hp, GCC); 261 else if (intty) { 262 printf("EOT\n"); 263 flush(); 264 } 265 266 /* 267 * Now, take the user names from the combined 268 * to and cc lists and do all the alias 269 * processing. 270 */ 271 272 senderr = 0; 273 to = usermap(cat(extract(hp->h_bcc, GBCC), 274 cat(extract(hp->h_to, GTO), extract(hp->h_cc, GCC)))); 275 if (to == NIL) { 276 printf("No recipients specified\n"); 277 goto topdog; 278 } 279 280 /* 281 * Look through the recipient list for names with /'s 282 * in them which we write to as files directly. 283 */ 284 285 to = outof(to, mtf, hp); 286 rewind(mtf); 287 to = verify(to); 288 if (senderr && !remote) { 289 topdog: 290 291 if (fsize(mtf) != 0) { 292 remove(deadletter); 293 exwrite(deadletter, mtf, 1); 294 rewind(mtf); 295 } 296 } 297 for (gotcha = 0, np = to; np != NIL; np = np->n_flink) 298 if ((np->n_type & GDEL) == 0) { 299 gotcha++; 300 break; 301 } 302 if (!gotcha) 303 goto out; 304 to = elide(to); 305 mechk(to); 306 if (count(to) > 1) 307 hp->h_seq++; 308 if (hp->h_seq > 0 && !remote) { 309 fixhead(hp, to); 310 if (fsize(mtf) == 0) 311 if (hp->h_subject == NOSTR) 312 printf("No message, no subject; hope that's ok\n"); 313 else 314 printf("Null message body; hope that's ok\n"); 315 if ((mtf = infix(hp, mtf)) == NULL) { 316 fprintf(stderr, ". . . message lost, sorry.\n"); 317 return(-1); 318 } 319 } 320 namelist = unpack(to); 321 if (debug) { 322 printf("Recipients of message:\n"); 323 for (t = namelist; *t != NOSTR; t++) 324 printf(" \"%s\"", *t); 325 printf("\n"); 326 fflush(stdout); 327 return; 328 } 329 if ((cp = value("record")) != NOSTR) 330 savemail(expand(cp), hp, mtf); 331 332 /* 333 * Wait, to absorb a potential zombie, then 334 * fork, set up the temporary mail file as standard 335 * input for "mail" and exec with the user list we generated 336 * far above. Return the process id to caller in case he 337 * wants to await the completion of mail. 338 */ 339 340 #ifdef VMUNIX 341 #ifdef pdp11 342 while (wait2(&s, WNOHANG) > 0) 343 #endif 344 #if defined(vax) || defined(sun) 345 while (wait3(&s, WNOHANG, 0) > 0) 346 #endif 347 ; 348 #else 349 wait(&s); 350 #endif 351 rewind(mtf); 352 pid = fork(); 353 if (pid == -1) { 354 perror("fork"); 355 remove(deadletter); 356 exwrite(deadletter, mtf, 1); 357 goto out; 358 } 359 if (pid == 0) { 360 sigchild(); 361 #ifdef SIGTSTP 362 if (remote == 0) { 363 sigset(SIGTSTP, SIG_IGN); 364 sigset(SIGTTIN, SIG_IGN); 365 sigset(SIGTTOU, SIG_IGN); 366 } 367 #endif 368 for (i = SIGHUP; i <= SIGQUIT; i++) 369 sigset(i, SIG_IGN); 370 if (!stat(POSTAGE, &sbuf)) 371 if ((postage = fopen(POSTAGE, "a")) != NULL) { 372 fprintf(postage, "%s %d %d\n", myname, 373 count(to), fsize(mtf)); 374 fclose(postage); 375 } 376 s = fileno(mtf); 377 for (i = 3; i < 15; i++) 378 if (i != s) 379 close(i); 380 close(0); 381 dup(s); 382 close(s); 383 #ifdef CC 384 submit(getpid()); 385 #endif CC 386 #ifdef SENDMAIL 387 if ((deliver = value("sendmail")) == NOSTR) 388 deliver = SENDMAIL; 389 execv(deliver, namelist); 390 #endif SENDMAIL 391 execv(MAIL, namelist); 392 perror(MAIL); 393 exit(1); 394 } 395 396 out: 397 if (remote || (value("verbose") != NOSTR)) { 398 while ((p = wait(&s)) != pid && p != -1) 399 ; 400 if (s != 0) 401 senderr++; 402 pid = 0; 403 } 404 fclose(mtf); 405 return(pid); 406 } 407 408 /* 409 * Fix the header by glopping all of the expanded names from 410 * the distribution list into the appropriate fields. 411 * If there are any ARPA net recipients in the message, 412 * we must insert commas, alas. 413 */ 414 415 fixhead(hp, tolist) 416 struct header *hp; 417 struct name *tolist; 418 { 419 register struct name *nlist; 420 register int f; 421 register struct name *np; 422 423 for (f = 0, np = tolist; np != NIL; np = np->n_flink) 424 if (any('@', np->n_name)) { 425 f |= GCOMMA; 426 break; 427 } 428 429 if (debug && f & GCOMMA) 430 fprintf(stderr, "Should be inserting commas in recip lists\n"); 431 hp->h_to = detract(tolist, GTO|f); 432 hp->h_cc = detract(tolist, GCC|f); 433 } 434 435 /* 436 * Prepend a header in front of the collected stuff 437 * and return the new file. 438 */ 439 440 FILE * 441 infix(hp, fi) 442 struct header *hp; 443 FILE *fi; 444 { 445 extern char tempMail[]; 446 register FILE *nfo, *nfi; 447 register int c; 448 449 rewind(fi); 450 if ((nfo = fopen(tempMail, "w")) == NULL) { 451 perror(tempMail); 452 return(fi); 453 } 454 if ((nfi = fopen(tempMail, "r")) == NULL) { 455 perror(tempMail); 456 fclose(nfo); 457 return(fi); 458 } 459 remove(tempMail); 460 puthead(hp, nfo, GTO|GSUBJECT|GCC|GNL); 461 c = getc(fi); 462 while (c != EOF) { 463 putc(c, nfo); 464 c = getc(fi); 465 } 466 if (ferror(fi)) { 467 perror("read"); 468 return(fi); 469 } 470 fflush(nfo); 471 if (ferror(nfo)) { 472 perror(tempMail); 473 fclose(nfo); 474 fclose(nfi); 475 return(fi); 476 } 477 fclose(nfo); 478 fclose(fi); 479 rewind(nfi); 480 return(nfi); 481 } 482 483 /* 484 * Dump the to, subject, cc header on the 485 * passed file buffer. 486 */ 487 488 puthead(hp, fo, w) 489 struct header *hp; 490 FILE *fo; 491 { 492 register int gotcha; 493 494 gotcha = 0; 495 if (hp->h_to != NOSTR && w & GTO) 496 fmt("To: ", hp->h_to, fo), gotcha++; 497 if (hp->h_subject != NOSTR && w & GSUBJECT) 498 fprintf(fo, "Subject: %s\n", hp->h_subject), gotcha++; 499 if (hp->h_cc != NOSTR && w & GCC) 500 fmt("Cc: ", hp->h_cc, fo), gotcha++; 501 if (hp->h_bcc != NOSTR && w & GBCC) 502 fmt("Bcc: ", hp->h_bcc, fo), gotcha++; 503 if (gotcha && w & GNL) 504 putc('\n', fo); 505 return(0); 506 } 507 508 /* 509 * Format the given text to not exceed 72 characters. 510 */ 511 512 fmt(str, txt, fo) 513 register char *str, *txt; 514 register FILE *fo; 515 { 516 register int col; 517 register char *bg, *bl, *pt, ch; 518 519 col = strlen(str); 520 if (col) 521 fprintf(fo, "%s", str); 522 pt = bg = txt; 523 bl = 0; 524 while (*bg) { 525 pt++; 526 if (++col >72) { 527 if (!bl) { 528 bl = bg; 529 while (*bl && !isspace(*bl)) 530 bl++; 531 } 532 if (!*bl) 533 goto finish; 534 ch = *bl; 535 *bl = '\0'; 536 fprintf(fo, "%s\n ", bg); 537 col = 4; 538 *bl = ch; 539 pt = bg = ++bl; 540 bl = 0; 541 } 542 if (!*pt) { 543 finish: 544 fprintf(fo, "%s\n", bg); 545 return; 546 } 547 if (isspace(*pt)) 548 bl = pt; 549 } 550 } 551 552 /* 553 * Save the outgoing mail on the passed file. 554 */ 555 556 savemail(name, hp, fi) 557 char name[]; 558 struct header *hp; 559 FILE *fi; 560 { 561 register FILE *fo; 562 register int c; 563 long now; 564 char *n; 565 566 if ((fo = fopen(name, "a")) == NULL) { 567 perror(name); 568 return(-1); 569 } 570 time(&now); 571 n = rflag; 572 if (n == NOSTR) 573 n = myname; 574 fprintf(fo, "From %s %s", n, ctime(&now)); 575 rewind(fi); 576 for (c = getc(fi); c != EOF; c = getc(fi)) 577 putc(c, fo); 578 fprintf(fo, "\n"); 579 fflush(fo); 580 if (ferror(fo)) 581 perror(name); 582 fclose(fo); 583 return(0); 584 } 585