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