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