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