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