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