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