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