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