1 /* $NetBSD: send.c,v 1.6 1996/06/08 19:48:39 christos Exp $ */ 2 3 /* 4 * Copyright (c) 1980, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed by the University of 18 * California, Berkeley and its contributors. 19 * 4. Neither the name of the University nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36 #ifndef lint 37 #if 0 38 static char sccsid[] = "@(#)send.c 8.1 (Berkeley) 6/6/93"; 39 #else 40 static char rcsid[] = "$NetBSD: send.c,v 1.6 1996/06/08 19:48:39 christos Exp $"; 41 #endif 42 #endif /* not lint */ 43 44 #include "rcv.h" 45 #include "extern.h" 46 47 /* 48 * Mail -- a mail program 49 * 50 * Mail to others. 51 */ 52 53 /* 54 * Send message described by the passed pointer to the 55 * passed output buffer. Return -1 on error. 56 * Adjust the status: field if need be. 57 * If doign is given, suppress ignored header fields. 58 * prefix is a string to prepend to each output line. 59 */ 60 int 61 send(mp, obuf, doign, prefix) 62 register struct message *mp; 63 FILE *obuf; 64 struct ignoretab *doign; 65 char *prefix; 66 { 67 long count; 68 register FILE *ibuf; 69 char line[LINESIZE]; 70 int ishead, infld, ignoring = 0, dostat, firstline; 71 register char *cp, *cp2; 72 register int c = 0; 73 int length; 74 int prefixlen = 0; 75 76 /* 77 * Compute the prefix string, without trailing whitespace 78 */ 79 if (prefix != NOSTR) { 80 cp2 = 0; 81 for (cp = prefix; *cp; cp++) 82 if (*cp != ' ' && *cp != '\t') 83 cp2 = cp; 84 prefixlen = cp2 == 0 ? 0 : cp2 - prefix + 1; 85 } 86 ibuf = setinput(mp); 87 count = mp->m_size; 88 ishead = 1; 89 dostat = doign == 0 || !isign("status", doign); 90 infld = 0; 91 firstline = 1; 92 /* 93 * Process headers first 94 */ 95 while (count > 0 && ishead) { 96 if (fgets(line, LINESIZE, ibuf) == NULL) 97 break; 98 count -= length = strlen(line); 99 if (firstline) { 100 /* 101 * First line is the From line, so no headers 102 * there to worry about 103 */ 104 firstline = 0; 105 ignoring = doign == ignoreall; 106 } else if (line[0] == '\n') { 107 /* 108 * If line is blank, we've reached end of 109 * headers, so force out status: field 110 * and note that we are no longer in header 111 * fields 112 */ 113 if (dostat) { 114 statusput(mp, obuf, prefix); 115 dostat = 0; 116 } 117 ishead = 0; 118 ignoring = doign == ignoreall; 119 } else if (infld && (line[0] == ' ' || line[0] == '\t')) { 120 /* 121 * If this line is a continuation (via space or tab) 122 * of a previous header field, just echo it 123 * (unless the field should be ignored). 124 * In other words, nothing to do. 125 */ 126 } else { 127 /* 128 * Pick up the header field if we have one. 129 */ 130 for (cp = line; (c = *cp++) && c != ':' && !isspace(c);) 131 ; 132 cp2 = --cp; 133 while (isspace(*cp++)) 134 ; 135 if (cp[-1] != ':') { 136 /* 137 * Not a header line, force out status: 138 * This happens in uucp style mail where 139 * there are no headers at all. 140 */ 141 if (dostat) { 142 statusput(mp, obuf, prefix); 143 dostat = 0; 144 } 145 if (doign != ignoreall) 146 /* add blank line */ 147 (void) putc('\n', obuf); 148 ishead = 0; 149 ignoring = 0; 150 } else { 151 /* 152 * If it is an ignored field and 153 * we care about such things, skip it. 154 */ 155 *cp2 = 0; /* temporarily null terminate */ 156 if (doign && isign(line, doign)) 157 ignoring = 1; 158 else if ((line[0] == 's' || line[0] == 'S') && 159 strcasecmp(line, "status") == 0) { 160 /* 161 * If the field is "status," go compute 162 * and print the real Status: field 163 */ 164 if (dostat) { 165 statusput(mp, obuf, prefix); 166 dostat = 0; 167 } 168 ignoring = 1; 169 } else { 170 ignoring = 0; 171 *cp2 = c; /* restore */ 172 } 173 infld = 1; 174 } 175 } 176 if (!ignoring) { 177 /* 178 * Strip trailing whitespace from prefix 179 * if line is blank. 180 */ 181 if (prefix != NOSTR) 182 if (length > 1) 183 fputs(prefix, obuf); 184 else 185 (void) fwrite(prefix, sizeof *prefix, 186 prefixlen, obuf); 187 (void) fwrite(line, sizeof *line, length, obuf); 188 if (ferror(obuf)) 189 return -1; 190 } 191 } 192 /* 193 * Copy out message body 194 */ 195 if (doign == ignoreall) 196 count--; /* skip final blank line */ 197 if (prefix != NOSTR) 198 while (count > 0) { 199 if (fgets(line, LINESIZE, ibuf) == NULL) { 200 c = 0; 201 break; 202 } 203 count -= c = strlen(line); 204 /* 205 * Strip trailing whitespace from prefix 206 * if line is blank. 207 */ 208 if (c > 1) 209 fputs(prefix, obuf); 210 else 211 (void) fwrite(prefix, sizeof *prefix, 212 prefixlen, obuf); 213 (void) fwrite(line, sizeof *line, c, obuf); 214 if (ferror(obuf)) 215 return -1; 216 } 217 else 218 while (count > 0) { 219 c = count < LINESIZE ? count : LINESIZE; 220 if ((c = fread(line, sizeof *line, c, ibuf)) <= 0) 221 break; 222 count -= c; 223 if (fwrite(line, sizeof *line, c, obuf) != c) 224 return -1; 225 } 226 if (doign == ignoreall && c > 0 && line[c - 1] != '\n') 227 /* no final blank line */ 228 if ((c = getc(ibuf)) != EOF && putc(c, obuf) == EOF) 229 return -1; 230 return 0; 231 } 232 233 /* 234 * Output a reasonable looking status field. 235 */ 236 void 237 statusput(mp, obuf, prefix) 238 register struct message *mp; 239 FILE *obuf; 240 char *prefix; 241 { 242 char statout[3]; 243 register char *cp = statout; 244 245 if (mp->m_flag & MREAD) 246 *cp++ = 'R'; 247 if ((mp->m_flag & MNEW) == 0) 248 *cp++ = 'O'; 249 *cp = 0; 250 if (statout[0]) 251 fprintf(obuf, "%sStatus: %s\n", 252 prefix == NOSTR ? "" : prefix, statout); 253 } 254 255 /* 256 * Interface between the argument list and the mail1 routine 257 * which does all the dirty work. 258 */ 259 int 260 mail(to, cc, bcc, smopts, subject) 261 struct name *to, *cc, *bcc, *smopts; 262 char *subject; 263 { 264 struct header head; 265 266 head.h_to = to; 267 head.h_subject = subject; 268 head.h_cc = cc; 269 head.h_bcc = bcc; 270 head.h_smopts = smopts; 271 mail1(&head, 0); 272 return(0); 273 } 274 275 276 /* 277 * Send mail to a bunch of user names. The interface is through 278 * the mail routine below. 279 */ 280 int 281 sendmail(v) 282 void *v; 283 { 284 char *str = v; 285 struct header head; 286 287 head.h_to = extract(str, GTO); 288 head.h_subject = NOSTR; 289 head.h_cc = NIL; 290 head.h_bcc = NIL; 291 head.h_smopts = NIL; 292 mail1(&head, 0); 293 return(0); 294 } 295 296 /* 297 * Mail a message on standard input to the people indicated 298 * in the passed header. (Internal interface). 299 */ 300 void 301 mail1(hp, printheaders) 302 struct header *hp; 303 int printheaders; 304 { 305 char *cp; 306 int pid; 307 char **namelist; 308 struct name *to; 309 FILE *mtf; 310 311 /* 312 * Collect user's mail from standard input. 313 * Get the result as mtf. 314 */ 315 if ((mtf = collect(hp, printheaders)) == NULL) 316 return; 317 if (value("interactive") != NOSTR) 318 if (value("askcc") != NOSTR || value("askbcc") != NOSTR) { 319 if (value("askcc") != NOSTR) 320 grabh(hp, GCC); 321 if (value("askbcc") != NOSTR) 322 grabh(hp, GBCC); 323 } else { 324 printf("EOT\n"); 325 (void) fflush(stdout); 326 } 327 if (fsize(mtf) == 0) 328 if (hp->h_subject == NOSTR) 329 printf("No message, no subject; hope that's ok\n"); 330 else 331 printf("Null message body; hope that's ok\n"); 332 /* 333 * Now, take the user names from the combined 334 * to and cc lists and do all the alias 335 * processing. 336 */ 337 senderr = 0; 338 to = usermap(cat(hp->h_bcc, cat(hp->h_to, hp->h_cc))); 339 if (to == NIL) { 340 printf("No recipients specified\n"); 341 senderr++; 342 } 343 /* 344 * Look through the recipient list for names with /'s 345 * in them which we write to as files directly. 346 */ 347 to = outof(to, mtf, hp); 348 if (senderr) 349 savedeadletter(mtf); 350 to = elide(to); 351 if (count(to) == 0) 352 goto out; 353 fixhead(hp, to); 354 if ((mtf = infix(hp, mtf)) == NULL) { 355 fprintf(stderr, ". . . message lost, sorry.\n"); 356 return; 357 } 358 namelist = unpack(cat(hp->h_smopts, to)); 359 if (debug) { 360 char **t; 361 362 printf("Sendmail arguments:"); 363 for (t = namelist; *t != NOSTR; t++) 364 printf(" \"%s\"", *t); 365 printf("\n"); 366 goto out; 367 } 368 if ((cp = value("record")) != NOSTR) 369 (void) savemail(expand(cp), mtf); 370 /* 371 * Fork, set up the temporary mail file as standard 372 * input for "mail", and exec with the user list we generated 373 * far above. 374 */ 375 pid = fork(); 376 if (pid == -1) { 377 perror("fork"); 378 savedeadletter(mtf); 379 goto out; 380 } 381 if (pid == 0) { 382 sigset_t nset; 383 sigemptyset(&nset); 384 sigaddset(&nset, SIGHUP); 385 sigaddset(&nset, SIGINT); 386 sigaddset(&nset, SIGQUIT); 387 sigaddset(&nset, SIGTSTP); 388 sigaddset(&nset, SIGTTIN); 389 sigaddset(&nset, SIGTTOU); 390 prepare_child(&nset, fileno(mtf), -1); 391 if ((cp = value("sendmail")) != NOSTR) 392 cp = expand(cp); 393 else 394 cp = _PATH_SENDMAIL; 395 execv(cp, namelist); 396 perror(cp); 397 _exit(1); 398 } 399 if (value("verbose") != NOSTR) 400 (void) wait_child(pid); 401 else 402 free_child(pid); 403 out: 404 (void) Fclose(mtf); 405 } 406 407 /* 408 * Fix the header by glopping all of the expanded names from 409 * the distribution list into the appropriate fields. 410 */ 411 void 412 fixhead(hp, tolist) 413 struct header *hp; 414 struct name *tolist; 415 { 416 register struct name *np; 417 418 hp->h_to = NIL; 419 hp->h_cc = NIL; 420 hp->h_bcc = NIL; 421 for (np = tolist; np != NIL; np = np->n_flink) 422 if ((np->n_type & GMASK) == GTO) 423 hp->h_to = 424 cat(hp->h_to, nalloc(np->n_name, np->n_type)); 425 else if ((np->n_type & GMASK) == GCC) 426 hp->h_cc = 427 cat(hp->h_cc, nalloc(np->n_name, np->n_type)); 428 else if ((np->n_type & GMASK) == GBCC) 429 hp->h_bcc = 430 cat(hp->h_bcc, nalloc(np->n_name, np->n_type)); 431 } 432 433 /* 434 * Prepend a header in front of the collected stuff 435 * and return the new file. 436 */ 437 FILE * 438 infix(hp, fi) 439 struct header *hp; 440 FILE *fi; 441 { 442 extern char *tempMail; 443 register FILE *nfo, *nfi; 444 register int c; 445 446 if ((nfo = Fopen(tempMail, "w")) == NULL) { 447 perror(tempMail); 448 return(fi); 449 } 450 if ((nfi = Fopen(tempMail, "r")) == NULL) { 451 perror(tempMail); 452 (void) Fclose(nfo); 453 return(fi); 454 } 455 (void) rm(tempMail); 456 (void) puthead(hp, nfo, GTO|GSUBJECT|GCC|GBCC|GNL|GCOMMA); 457 c = getc(fi); 458 while (c != EOF) { 459 (void) putc(c, nfo); 460 c = getc(fi); 461 } 462 if (ferror(fi)) { 463 perror("read"); 464 rewind(fi); 465 return(fi); 466 } 467 (void) fflush(nfo); 468 if (ferror(nfo)) { 469 perror(tempMail); 470 (void) Fclose(nfo); 471 (void) Fclose(nfi); 472 rewind(fi); 473 return(fi); 474 } 475 (void) Fclose(nfo); 476 (void) 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 int 486 puthead(hp, fo, w) 487 struct header *hp; 488 FILE *fo; 489 int w; 490 { 491 register int gotcha; 492 493 gotcha = 0; 494 if (hp->h_to != NIL && w & GTO) 495 fmt("To:", hp->h_to, fo, w&GCOMMA), gotcha++; 496 if (hp->h_subject != NOSTR && w & GSUBJECT) 497 fprintf(fo, "Subject: %s\n", hp->h_subject), gotcha++; 498 if (hp->h_cc != NIL && w & GCC) 499 fmt("Cc:", hp->h_cc, fo, w&GCOMMA), gotcha++; 500 if (hp->h_bcc != NIL && w & GBCC) 501 fmt("Bcc:", hp->h_bcc, fo, w&GCOMMA), gotcha++; 502 if (gotcha && w & GNL) 503 (void) putc('\n', fo); 504 return(0); 505 } 506 507 /* 508 * Format the given header line to not exceed 72 characters. 509 */ 510 void 511 fmt(str, np, fo, comma) 512 char *str; 513 register struct name *np; 514 FILE *fo; 515 int comma; 516 { 517 register col, len; 518 519 comma = comma ? 1 : 0; 520 col = strlen(str); 521 if (col) 522 fputs(str, fo); 523 for (; np != NIL; np = np->n_flink) { 524 if (np->n_flink == NIL) 525 comma = 0; 526 len = strlen(np->n_name); 527 col++; /* for the space */ 528 if (col + len + comma > 72 && col > 4) { 529 fputs("\n ", fo); 530 col = 4; 531 } else 532 putc(' ', fo); 533 fputs(np->n_name, fo); 534 if (comma) 535 putc(',', fo); 536 col += len + comma; 537 } 538 putc('\n', fo); 539 } 540 541 /* 542 * Save the outgoing mail on the passed file. 543 */ 544 545 /*ARGSUSED*/ 546 int 547 savemail(name, fi) 548 char name[]; 549 register FILE *fi; 550 { 551 register FILE *fo; 552 char buf[BUFSIZ]; 553 register i; 554 time_t now; 555 556 if ((fo = Fopen(name, "a")) == NULL) { 557 perror(name); 558 return (-1); 559 } 560 (void) time(&now); 561 fprintf(fo, "From %s %s", myname, ctime(&now)); 562 while ((i = fread(buf, 1, sizeof buf, fi)) > 0) 563 (void) fwrite(buf, 1, i, fo); 564 (void) putc('\n', fo); 565 (void) fflush(fo); 566 if (ferror(fo)) 567 perror(name); 568 (void) Fclose(fo); 569 rewind(fi); 570 return (0); 571 } 572