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