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) 255 struct name *to, *cc, *bcc, *smopts; 256 char *subject; 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 mail1(&head, 0); 266 return(0); 267 } 268 269 270 /* 271 * Send mail to a bunch of user names. The interface is through 272 * the mail routine below. 273 */ 274 int 275 sendmail(str) 276 char *str; 277 { 278 struct header head; 279 280 head.h_to = extract(str, GTO); 281 head.h_subject = NOSTR; 282 head.h_cc = NIL; 283 head.h_bcc = NIL; 284 head.h_smopts = NIL; 285 mail1(&head, 0); 286 return(0); 287 } 288 289 /* 290 * Mail a message on standard input to the people indicated 291 * in the passed header. (Internal interface). 292 */ 293 void 294 mail1(hp, printheaders) 295 struct header *hp; 296 int printheaders; 297 { 298 char *cp; 299 int pid; 300 char **namelist; 301 struct name *to; 302 FILE *mtf; 303 304 /* 305 * Collect user's mail from standard input. 306 * Get the result as mtf. 307 */ 308 if ((mtf = collect(hp, printheaders)) == NULL) 309 return; 310 if (value("interactive") != NOSTR) 311 if (value("askcc") != NOSTR) 312 grabh(hp, GCC); 313 else { 314 printf("EOT\n"); 315 (void) fflush(stdout); 316 } 317 if (fsize(mtf) == 0) 318 if (hp->h_subject == NOSTR) 319 printf("No message, no subject; hope that's ok\n"); 320 else 321 printf("Null message body; hope that's ok\n"); 322 /* 323 * Now, take the user names from the combined 324 * to and cc lists and do all the alias 325 * processing. 326 */ 327 senderr = 0; 328 to = usermap(cat(hp->h_bcc, cat(hp->h_to, hp->h_cc))); 329 if (to == NIL) { 330 printf("No recipients specified\n"); 331 senderr++; 332 } 333 /* 334 * Look through the recipient list for names with /'s 335 * in them which we write to as files directly. 336 */ 337 to = outof(to, mtf, hp); 338 if (senderr) 339 savedeadletter(mtf); 340 to = elide(to); 341 if (count(to) == 0) 342 goto out; 343 fixhead(hp, to); 344 if ((mtf = infix(hp, mtf)) == NULL) { 345 fprintf(stderr, ". . . message lost, sorry.\n"); 346 return; 347 } 348 namelist = unpack(cat(hp->h_smopts, to)); 349 if (debug) { 350 char **t; 351 352 printf("Sendmail arguments:"); 353 for (t = namelist; *t != NOSTR; t++) 354 printf(" \"%s\"", *t); 355 printf("\n"); 356 goto out; 357 } 358 if ((cp = value("record")) != NOSTR) 359 (void) savemail(expand(cp), mtf); 360 /* 361 * Fork, set up the temporary mail file as standard 362 * input for "mail", and exec with the user list we generated 363 * far above. 364 */ 365 pid = fork(); 366 if (pid == -1) { 367 perror("fork"); 368 savedeadletter(mtf); 369 goto out; 370 } 371 if (pid == 0) { 372 prepare_child(sigmask(SIGHUP)|sigmask(SIGINT)|sigmask(SIGQUIT)| 373 sigmask(SIGTSTP)|sigmask(SIGTTIN)|sigmask(SIGTTOU), 374 fileno(mtf), -1); 375 if ((cp = value("sendmail")) != NOSTR) 376 cp = expand(cp); 377 else 378 cp = _PATH_SENDMAIL; 379 execv(cp, namelist); 380 perror(cp); 381 _exit(1); 382 } 383 if (value("verbose") != NOSTR) 384 (void) wait_child(pid); 385 else 386 free_child(pid); 387 out: 388 (void) Fclose(mtf); 389 } 390 391 /* 392 * Fix the header by glopping all of the expanded names from 393 * the distribution list into the appropriate fields. 394 */ 395 void 396 fixhead(hp, tolist) 397 struct header *hp; 398 struct name *tolist; 399 { 400 register struct name *np; 401 402 hp->h_to = NIL; 403 hp->h_cc = NIL; 404 hp->h_bcc = NIL; 405 for (np = tolist; np != NIL; np = np->n_flink) 406 if ((np->n_type & GMASK) == GTO) 407 hp->h_to = 408 cat(hp->h_to, nalloc(np->n_name, np->n_type)); 409 else if ((np->n_type & GMASK) == GCC) 410 hp->h_cc = 411 cat(hp->h_cc, nalloc(np->n_name, np->n_type)); 412 else if ((np->n_type & GMASK) == GBCC) 413 hp->h_bcc = 414 cat(hp->h_bcc, nalloc(np->n_name, np->n_type)); 415 } 416 417 /* 418 * Prepend a header in front of the collected stuff 419 * and return the new file. 420 */ 421 FILE * 422 infix(hp, fi) 423 struct header *hp; 424 FILE *fi; 425 { 426 extern char tempMail[]; 427 register FILE *nfo, *nfi; 428 register int c; 429 430 if ((nfo = Fopen(tempMail, "w")) == NULL) { 431 perror(tempMail); 432 return(fi); 433 } 434 if ((nfi = Fopen(tempMail, "r")) == NULL) { 435 perror(tempMail); 436 (void) Fclose(nfo); 437 return(fi); 438 } 439 (void) rm(tempMail); 440 (void) puthead(hp, nfo, GTO|GSUBJECT|GCC|GBCC|GNL|GCOMMA); 441 c = getc(fi); 442 while (c != EOF) { 443 (void) putc(c, nfo); 444 c = getc(fi); 445 } 446 if (ferror(fi)) { 447 perror("read"); 448 rewind(fi); 449 return(fi); 450 } 451 (void) fflush(nfo); 452 if (ferror(nfo)) { 453 perror(tempMail); 454 (void) Fclose(nfo); 455 (void) Fclose(nfi); 456 rewind(fi); 457 return(fi); 458 } 459 (void) Fclose(nfo); 460 (void) Fclose(fi); 461 rewind(nfi); 462 return(nfi); 463 } 464 465 /* 466 * Dump the to, subject, cc header on the 467 * passed file buffer. 468 */ 469 int 470 puthead(hp, fo, w) 471 struct header *hp; 472 FILE *fo; 473 int w; 474 { 475 register int gotcha; 476 477 gotcha = 0; 478 if (hp->h_to != NIL && w & GTO) 479 fmt("To:", hp->h_to, fo, w&GCOMMA), gotcha++; 480 if (hp->h_subject != NOSTR && w & GSUBJECT) 481 fprintf(fo, "Subject: %s\n", hp->h_subject), gotcha++; 482 if (hp->h_cc != NIL && w & GCC) 483 fmt("Cc:", hp->h_cc, fo, w&GCOMMA), gotcha++; 484 if (hp->h_bcc != NIL && w & GBCC) 485 fmt("Bcc:", hp->h_bcc, fo, w&GCOMMA), gotcha++; 486 if (gotcha && w & GNL) 487 (void) putc('\n', fo); 488 return(0); 489 } 490 491 /* 492 * Format the given header line to not exceed 72 characters. 493 */ 494 void 495 fmt(str, np, fo, comma) 496 char *str; 497 register struct name *np; 498 FILE *fo; 499 int comma; 500 { 501 register col, len; 502 503 comma = comma ? 1 : 0; 504 col = strlen(str); 505 if (col) 506 fputs(str, fo); 507 for (; np != NIL; np = np->n_flink) { 508 if (np->n_flink == NIL) 509 comma = 0; 510 len = strlen(np->n_name); 511 col++; /* for the space */ 512 if (col + len + comma > 72 && col > 4) { 513 fputs("\n ", fo); 514 col = 4; 515 } else 516 putc(' ', fo); 517 fputs(np->n_name, fo); 518 if (comma) 519 putc(',', fo); 520 col += len + comma; 521 } 522 putc('\n', fo); 523 } 524 525 /* 526 * Save the outgoing mail on the passed file. 527 */ 528 529 /*ARGSUSED*/ 530 int 531 savemail(name, fi) 532 char name[]; 533 register FILE *fi; 534 { 535 register FILE *fo; 536 char buf[BUFSIZ]; 537 register i; 538 time_t now, time(); 539 char *ctime(); 540 541 if ((fo = Fopen(name, "a")) == NULL) { 542 perror(name); 543 return (-1); 544 } 545 (void) time(&now); 546 fprintf(fo, "From %s %s", myname, ctime(&now)); 547 while ((i = fread(buf, 1, sizeof buf, fi)) > 0) 548 (void) fwrite(buf, 1, i, fo); 549 (void) putc('\n', fo); 550 (void) fflush(fo); 551 if (ferror(fo)) 552 perror(name); 553 (void) Fclose(fo); 554 rewind(fi); 555 return (0); 556 } 557