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