1 /* 2 * Copyright (c) 1980, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * %sccs.include.redist.c% 6 */ 7 8 #ifndef lint 9 static char sccsid[] = "@(#)send.c 8.1 (Berkeley) 06/06/93"; 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 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 void 370 fixhead(hp, tolist) 371 struct header *hp; 372 struct name *tolist; 373 { 374 register struct name *np; 375 376 hp->h_to = NIL; 377 hp->h_cc = NIL; 378 hp->h_bcc = NIL; 379 for (np = tolist; np != NIL; np = np->n_flink) 380 if ((np->n_type & GMASK) == GTO) 381 hp->h_to = 382 cat(hp->h_to, nalloc(np->n_name, np->n_type)); 383 else if ((np->n_type & GMASK) == GCC) 384 hp->h_cc = 385 cat(hp->h_cc, nalloc(np->n_name, np->n_type)); 386 else if ((np->n_type & GMASK) == GBCC) 387 hp->h_bcc = 388 cat(hp->h_bcc, nalloc(np->n_name, np->n_type)); 389 } 390 391 /* 392 * Prepend a header in front of the collected stuff 393 * and return the new file. 394 */ 395 FILE * 396 infix(hp, fi) 397 struct header *hp; 398 FILE *fi; 399 { 400 extern char tempMail[]; 401 register FILE *nfo, *nfi; 402 register int c; 403 404 if ((nfo = Fopen(tempMail, "w")) == NULL) { 405 perror(tempMail); 406 return(fi); 407 } 408 if ((nfi = Fopen(tempMail, "r")) == NULL) { 409 perror(tempMail); 410 (void) Fclose(nfo); 411 return(fi); 412 } 413 (void) rm(tempMail); 414 (void) puthead(hp, nfo, GTO|GSUBJECT|GCC|GBCC|GNL|GCOMMA); 415 c = getc(fi); 416 while (c != EOF) { 417 (void) putc(c, nfo); 418 c = getc(fi); 419 } 420 if (ferror(fi)) { 421 perror("read"); 422 rewind(fi); 423 return(fi); 424 } 425 (void) fflush(nfo); 426 if (ferror(nfo)) { 427 perror(tempMail); 428 (void) Fclose(nfo); 429 (void) Fclose(nfi); 430 rewind(fi); 431 return(fi); 432 } 433 (void) Fclose(nfo); 434 (void) Fclose(fi); 435 rewind(nfi); 436 return(nfi); 437 } 438 439 /* 440 * Dump the to, subject, cc header on the 441 * passed file buffer. 442 */ 443 int 444 puthead(hp, fo, w) 445 struct header *hp; 446 FILE *fo; 447 int w; 448 { 449 register int gotcha; 450 451 gotcha = 0; 452 if (hp->h_to != NIL && w & GTO) 453 fmt("To:", hp->h_to, fo, w&GCOMMA), gotcha++; 454 if (hp->h_subject != NOSTR && w & GSUBJECT) 455 fprintf(fo, "Subject: %s\n", hp->h_subject), gotcha++; 456 if (hp->h_cc != NIL && w & GCC) 457 fmt("Cc:", hp->h_cc, fo, w&GCOMMA), gotcha++; 458 if (hp->h_bcc != NIL && w & GBCC) 459 fmt("Bcc:", hp->h_bcc, fo, w&GCOMMA), gotcha++; 460 if (gotcha && w & GNL) 461 (void) putc('\n', fo); 462 return(0); 463 } 464 465 /* 466 * Format the given header line to not exceed 72 characters. 467 */ 468 void 469 fmt(str, np, fo, comma) 470 char *str; 471 register struct name *np; 472 FILE *fo; 473 int comma; 474 { 475 register col, len; 476 477 comma = comma ? 1 : 0; 478 col = strlen(str); 479 if (col) 480 fputs(str, fo); 481 for (; np != NIL; np = np->n_flink) { 482 if (np->n_flink == NIL) 483 comma = 0; 484 len = strlen(np->n_name); 485 col++; /* for the space */ 486 if (col + len + comma > 72 && col > 4) { 487 fputs("\n ", fo); 488 col = 4; 489 } else 490 putc(' ', fo); 491 fputs(np->n_name, fo); 492 if (comma) 493 putc(',', fo); 494 col += len + comma; 495 } 496 putc('\n', fo); 497 } 498 499 /* 500 * Save the outgoing mail on the passed file. 501 */ 502 503 /*ARGSUSED*/ 504 int 505 savemail(name, fi) 506 char name[]; 507 register FILE *fi; 508 { 509 register FILE *fo; 510 char buf[BUFSIZ]; 511 register i; 512 time_t now, time(); 513 char *ctime(); 514 515 if ((fo = Fopen(name, "a")) == NULL) { 516 perror(name); 517 return (-1); 518 } 519 (void) time(&now); 520 fprintf(fo, "From %s %s", myname, ctime(&now)); 521 while ((i = fread(buf, 1, sizeof buf, fi)) > 0) 522 (void) fwrite(buf, 1, i, fo); 523 (void) putc('\n', fo); 524 (void) fflush(fo); 525 if (ferror(fo)) 526 perror(name); 527 (void) Fclose(fo); 528 rewind(fi); 529 return (0); 530 } 531