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