1 /*
2 * Copyright (c) 1980, 1993
3 * The Regents of the University of California. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by the University of
16 * California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34 #ifndef lint
35 static char sccsid[] = "@(#)send.c 8.1 (Berkeley) 6/6/93";
36 #endif /* not lint */
37
38 #include "rcv.h"
39 #include "extern.h"
40
41 /*
42 * Mail -- a mail program
43 *
44 * Mail to others.
45 */
46
47 /*
48 * Send message described by the passed pointer to the
49 * passed output buffer. Return -1 on error.
50 * Adjust the status: field if need be.
51 * If doign is given, suppress ignored header fields.
52 * prefix is a string to prepend to each output line.
53 */
54 int
send(mp,obuf,doign,prefix)55 send(mp, obuf, doign, prefix)
56 register struct message *mp;
57 FILE *obuf;
58 struct ignoretab *doign;
59 char *prefix;
60 {
61 long count;
62 register FILE *ibuf;
63 char line[LINESIZE];
64 int ishead, infld, ignoring, dostat, firstline;
65 register char *cp, *cp2;
66 register int c;
67 int length;
68 int prefixlen;
69
70 /*
71 * Compute the prefix string, without trailing whitespace
72 */
73 if (prefix != NOSTR) {
74 cp2 = 0;
75 for (cp = prefix; *cp; cp++)
76 if (*cp != ' ' && *cp != '\t')
77 cp2 = cp;
78 prefixlen = cp2 == 0 ? 0 : cp2 - prefix + 1;
79 }
80 ibuf = setinput(mp);
81 count = mp->m_size;
82 ishead = 1;
83 dostat = doign == 0 || !isign("status", doign);
84 infld = 0;
85 firstline = 1;
86 /*
87 * Process headers first
88 */
89 while (count > 0 && ishead) {
90 if (fgets(line, LINESIZE, ibuf) == NULL)
91 break;
92 count -= length = strlen(line);
93 if (firstline) {
94 /*
95 * First line is the From line, so no headers
96 * there to worry about
97 */
98 firstline = 0;
99 ignoring = doign == ignoreall;
100 } else if (line[0] == '\n') {
101 /*
102 * If line is blank, we've reached end of
103 * headers, so force out status: field
104 * and note that we are no longer in header
105 * fields
106 */
107 if (dostat) {
108 statusput(mp, obuf, prefix);
109 dostat = 0;
110 }
111 ishead = 0;
112 ignoring = doign == ignoreall;
113 } else if (infld && (line[0] == ' ' || line[0] == '\t')) {
114 /*
115 * If this line is a continuation (via space or tab)
116 * of a previous header field, just echo it
117 * (unless the field should be ignored).
118 * In other words, nothing to do.
119 */
120 } else {
121 /*
122 * Pick up the header field if we have one.
123 */
124 for (cp = line; (c = *cp++) && c != ':' && !isspace(c);)
125 ;
126 cp2 = --cp;
127 while (isspace(*cp++))
128 ;
129 if (cp[-1] != ':') {
130 /*
131 * Not a header line, force out status:
132 * This happens in uucp style mail where
133 * there are no headers at all.
134 */
135 if (dostat) {
136 statusput(mp, obuf, prefix);
137 dostat = 0;
138 }
139 if (doign != ignoreall)
140 /* add blank line */
141 (void) putc('\n', obuf);
142 ishead = 0;
143 ignoring = 0;
144 } else {
145 /*
146 * If it is an ignored field and
147 * we care about such things, skip it.
148 */
149 *cp2 = 0; /* temporarily null terminate */
150 if (doign && isign(line, doign))
151 ignoring = 1;
152 else if ((line[0] == 's' || line[0] == 'S') &&
153 strcasecmp(line, "status") == 0) {
154 /*
155 * If the field is "status," go compute
156 * and print the real Status: field
157 */
158 if (dostat) {
159 statusput(mp, obuf, prefix);
160 dostat = 0;
161 }
162 ignoring = 1;
163 } else {
164 ignoring = 0;
165 *cp2 = c; /* restore */
166 }
167 infld = 1;
168 }
169 }
170 if (!ignoring) {
171 /*
172 * Strip trailing whitespace from prefix
173 * if line is blank.
174 */
175 if (prefix != NOSTR)
176 if (length > 1)
177 fputs(prefix, obuf);
178 else
179 (void) fwrite(prefix, sizeof *prefix,
180 prefixlen, obuf);
181 (void) fwrite(line, sizeof *line, length, obuf);
182 if (ferror(obuf))
183 return -1;
184 }
185 }
186 /*
187 * Copy out message body
188 */
189 if (doign == ignoreall)
190 count--; /* skip final blank line */
191 if (prefix != NOSTR)
192 while (count > 0) {
193 if (fgets(line, LINESIZE, ibuf) == NULL) {
194 c = 0;
195 break;
196 }
197 count -= c = strlen(line);
198 /*
199 * Strip trailing whitespace from prefix
200 * if line is blank.
201 */
202 if (c > 1)
203 fputs(prefix, obuf);
204 else
205 (void) fwrite(prefix, sizeof *prefix,
206 prefixlen, obuf);
207 (void) fwrite(line, sizeof *line, c, obuf);
208 if (ferror(obuf))
209 return -1;
210 }
211 else
212 while (count > 0) {
213 c = count < LINESIZE ? count : LINESIZE;
214 if ((c = fread(line, sizeof *line, c, ibuf)) <= 0)
215 break;
216 count -= c;
217 if (fwrite(line, sizeof *line, c, obuf) != c)
218 return -1;
219 }
220 if (doign == ignoreall && c > 0 && line[c - 1] != '\n')
221 /* no final blank line */
222 if ((c = getc(ibuf)) != EOF && putc(c, obuf) == EOF)
223 return -1;
224 return 0;
225 }
226
227 /*
228 * Output a reasonable looking status field.
229 */
230 void
statusput(mp,obuf,prefix)231 statusput(mp, obuf, prefix)
232 register struct message *mp;
233 FILE *obuf;
234 char *prefix;
235 {
236 char statout[3];
237 register char *cp = statout;
238
239 if (mp->m_flag & MREAD)
240 *cp++ = 'R';
241 if ((mp->m_flag & MNEW) == 0)
242 *cp++ = 'O';
243 *cp = 0;
244 if (statout[0])
245 fprintf(obuf, "%sStatus: %s\n",
246 prefix == NOSTR ? "" : prefix, statout);
247 }
248
249 /*
250 * Interface between the argument list and the mail1 routine
251 * which does all the dirty work.
252 */
253 int
mail(to,cc,bcc,smopts,subject,replyto)254 mail(to, cc, bcc, smopts, subject, replyto)
255 struct name *to, *cc, *bcc, *smopts;
256 char *subject, *replyto;
257 {
258 struct header head;
259
260 head.h_to = to;
261 head.h_subject = subject;
262 head.h_cc = cc;
263 head.h_bcc = bcc;
264 head.h_smopts = smopts;
265 head.h_replyto = replyto;
266 head.h_inreplyto = NOSTR;
267 mail1(&head, 0);
268 return(0);
269 }
270
271
272 /*
273 * Send mail to a bunch of user names. The interface is through
274 * the mail routine below.
275 */
276 int
sendmail(str)277 sendmail(str)
278 char *str;
279 {
280 struct header head;
281
282 head.h_to = extract(str, GTO);
283 head.h_subject = NOSTR;
284 head.h_cc = NIL;
285 head.h_bcc = NIL;
286 head.h_smopts = NIL;
287 if ((head.h_replyto = getenv("REPLYTO")) == NULL)
288 head.h_replyto = NOSTR;
289 head.h_inreplyto = NOSTR;
290 mail1(&head, 0);
291 return(0);
292 }
293
294 /*
295 * Mail a message on standard input to the people indicated
296 * in the passed header. (Internal interface).
297 */
298 void
mail1(hp,printheaders)299 mail1(hp, printheaders)
300 struct header *hp;
301 int printheaders;
302 {
303 char *cp;
304 int pid;
305 char **namelist;
306 struct name *to;
307 FILE *mtf;
308
309 /*
310 * Collect user's mail from standard input.
311 * Get the result as mtf.
312 */
313 if ((mtf = collect(hp, printheaders)) == NULL)
314 return;
315 #if MAILX
316 getheader(hp, mtf, 1);
317 #endif /* MAILX */
318 if (value("interactive") != NOSTR)
319 #if MAILX
320 {
321 #else
322 if (value("askcc") != NOSTR)
323 grabh(hp, GCC);
324 else {
325 #endif /* MAILX */
326 printf("EOT\n");
327 (void) fflush(stdout);
328 }
329 if (fsize(mtf) == 0)
330 if (hp->h_subject == NOSTR)
331 printf("No message, no subject; hope that's ok\n");
332 else
333 printf("Null message body; hope that's ok\n");
334 #if MAILX
335 senderr = 0;
336 to = cat(hp->h_bcc, cat(hp->h_to, hp->h_cc));
337 #else
338 /*
339 * Now, take the user names from the combined
340 * to and cc lists and do all the alias
341 * processing.
342 */
343 senderr = 0;
344 to = usermap(cat(hp->h_bcc, cat(hp->h_to, hp->h_cc)));
345 if (to == NIL) {
346 printf("No recipients specified\n");
347 senderr++;
348 }
349 #endif /* MAILX */
350 /*
351 * Look through the recipient list for names with /'s
352 * in them which we write to as files directly.
353 */
354 to = outof(to, mtf, hp);
355 if (senderr)
356 savedeadletter(mtf);
357 to = elide(to);
358 if (count(to) == 0)
359 goto out;
360 fixhead(hp, to);
361 if ((mtf = infix(hp, mtf)) == NULL) {
362 fprintf(stderr, ". . . message lost, sorry.\n");
363 return;
364 }
365 namelist = unpack(cat(hp->h_smopts, to));
366 if (debug) {
367 char **t;
368
369 printf("Sendmail arguments:");
370 for (t = namelist; *t != NOSTR; t++)
371 printf(" \"%s\"", *t);
372 printf("\n");
373 goto out;
374 }
375 if ((cp = value("record")) != NOSTR)
376 (void) savemail(expand(cp), mtf);
377 /*
378 * Fork, set up the temporary mail file as standard
379 * input for "mail", and exec with the user list we generated
380 * far above.
381 */
382 pid = fork();
383 if (pid == -1) {
384 perror("fork");
385 savedeadletter(mtf);
386 goto out;
387 }
388 if (pid == 0) {
389 prepare_child(sigmask(SIGHUP)|sigmask(SIGINT)|sigmask(SIGQUIT)|
390 sigmask(SIGTSTP)|sigmask(SIGTTIN)|sigmask(SIGTTOU),
391 fileno(mtf), -1);
392 if ((cp = value("sendmail")) != NOSTR)
393 cp = expand(cp);
394 else
395 cp = _PATH_SENDMAIL;
396 execv(cp, namelist);
397 perror(cp);
398 _exit(1);
399 }
400 if (value("verbose") != NOSTR)
401 (void) wait_child(pid);
402 else
403 free_child(pid);
404 out:
405 (void) Fclose(mtf);
406 }
407
408 /*
409 * Fix the header by glopping all of the expanded names from
410 * the distribution list into the appropriate fields.
411 */
412 void
fixhead(hp,tolist)413 fixhead(hp, tolist)
414 struct header *hp;
415 struct name *tolist;
416 {
417 register struct name *np;
418
419 hp->h_to = NIL;
420 hp->h_cc = NIL;
421 hp->h_bcc = NIL;
422 for (np = tolist; np != NIL; np = np->n_flink) {
423 #if MAILX
424 if (np->n_type & GDEL)
425 continue;
426 #endif /* MAILX */
427 if ((np->n_type & GMASK) == GTO)
428 hp->h_to =
429 cat(hp->h_to, nalloc(np->n_name, np->n_type));
430 else if ((np->n_type & GMASK) == GCC)
431 hp->h_cc =
432 cat(hp->h_cc, nalloc(np->n_name, np->n_type));
433 else if ((np->n_type & GMASK) == GBCC)
434 hp->h_bcc =
435 cat(hp->h_bcc, nalloc(np->n_name, np->n_type));
436 }
437 }
438
439 #if MAILX
440 /*
441 * get header info from file.
442 */
443 void
getheader(hp,fi,um)444 getheader(hp, fi, um)
445 struct header *hp;
446 FILE *fi;
447 int um;
448 {
449 char *cp;
450
451 rewind(fi);
452 if ((cp = skin(ffield("to", fi))) != NOSTR) {
453 hp->h_to = cat(hp->h_to, extract(cp, GTO));
454 if (um)
455 hp->h_to = usermap(hp->h_to);
456 }
457 if ((cp = ffield("reply-to", fi)) != NOSTR) { /* 1998/07/11 */
458 hp->h_replyto = cp;
459 }
460 if ((cp = erase_braket(ffield("in-reply-to", fi))) != NOSTR) { /* 1998/07/11 */
461 hp->h_inreplyto = cp;
462 }
463 if ((cp = skin(ffield("cc", fi))) != NOSTR) {
464 hp->h_cc = cat(hp->h_cc, extract(cp, GCC));
465 if (um)
466 hp->h_cc = usermap(hp->h_cc);
467 }
468 if ((cp = skin(ffield("bcc", fi))) != NOSTR) {
469 hp->h_bcc = cat(hp->h_bcc, extract(cp, GBCC));
470 if (um)
471 hp->h_bcc = usermap(hp->h_bcc);
472 }
473 rewind(fi);
474 }
475 #endif /* MAILX */
476
477 /*
478 * Prepend a header in front of the collected stuff
479 * and return the new file.
480 */
481 #if MAILX
482 FILE *
infix(hp,fi)483 infix(hp, fi)
484 struct header *hp;
485 FILE *fi;
486 {
487 extern char tempMail[];
488 register FILE *nfo, *nfi;
489 register int c;
490 char linebuf[LINESIZE];
491 register char *cp;
492
493 if ((nfo = Fopen(tempMail, "w")) == NULL) {
494 perror(tempMail);
495 return(fi);
496 }
497 if ((nfi = Fopen(tempMail, "r")) == NULL) {
498 perror(tempMail);
499 (void) Fclose(nfo);
500 return(fi);
501 }
502 (void) rm(tempMail);
503 rewind(fi);
504 (void) puthead(hp, nfo,
505 GTO|GSUBJECT|GCC|GBCC|GREPLYTO|GINREPLYTO|GCOMMA);
506 for (;;) {
507 if (readline(fi, linebuf, LINESIZE) < 0)
508 return(fi);
509 for (cp = linebuf; isprint(*cp) && *cp != ' ' && *cp != ':';
510 cp++)
511 ;
512 if (cp == linebuf) {
513 putline(nfo, linebuf); /* NL put */
514 break;
515 }
516 if (*cp == ':') {
517 if (ishfield(linebuf, cp, "to") != 0 ||
518 ishfield(linebuf, cp, "subject") != 0 ||
519 ishfield(linebuf, cp, "cc") != 0 ||
520 ishfield(linebuf, cp, "bcc") != 0 ||
521 ishfield(linebuf, cp, "reply-to") != 0 || /* 1998/07/11 */
522 ishfield(linebuf, cp, "in-reply-to") != 0) { /* 1998/07/11 */
523 for (;;) {
524 ungetc(c = getc(fi), fi);
525 if (c != ' ' && c != '\t')
526 break;
527 if (readline(fi, linebuf, LINESIZE) < 0)
528 return(fi);
529 }
530 continue;
531 }
532 }
533 putline(nfo, linebuf);
534 }
535 c = getc(fi);
536 while (c != EOF) {
537 (void) putc(c, nfo);
538 c = getc(fi);
539 }
540 if (ferror(fi)) {
541 perror("read");
542 rewind(fi);
543 return(fi);
544 }
545 (void) fflush(nfo);
546 if (ferror(nfo)) {
547 perror(tempMail);
548 (void) Fclose(nfo);
549 (void) Fclose(nfi);
550 rewind(fi);
551 return(fi);
552 }
553 (void) Fclose(nfo);
554 (void) Fclose(fi);
555 rewind(nfi);
556 return(nfi);
557 }
558 #else /* MAILX */
559 FILE *
infix(hp,fi)560 infix(hp, fi)
561 struct header *hp;
562 FILE *fi;
563 {
564 extern char tempMail[];
565 register FILE *nfo, *nfi;
566 register int c;
567
568 if ((nfo = Fopen(tempMail, "w")) == NULL) {
569 perror(tempMail);
570 return(fi);
571 }
572 if ((nfi = Fopen(tempMail, "r")) == NULL) {
573 perror(tempMail);
574 (void) Fclose(nfo);
575 return(fi);
576 }
577 (void) rm(tempMail);
578 (void) puthead(hp, nfo,
579 GTO|GSUBJECT|GCC|GBCC|GREPLYTO|GINREPLYTO|GNL|GCOMMA);
580 c = getc(fi);
581 while (c != EOF) {
582 (void) putc(c, nfo);
583 c = getc(fi);
584 }
585 if (ferror(fi)) {
586 perror("read");
587 rewind(fi);
588 return(fi);
589 }
590 (void) fflush(nfo);
591 if (ferror(nfo)) {
592 perror(tempMail);
593 (void) Fclose(nfo);
594 (void) Fclose(nfi);
595 rewind(fi);
596 return(fi);
597 }
598 (void) Fclose(nfo);
599 (void) Fclose(fi);
600 rewind(nfi);
601 return(nfi);
602 }
603 #endif /* MAILX */
604
605 /*
606 * Dump the to, subject, cc header on the
607 * passed file buffer.
608 */
609 int
puthead(hp,fo,w)610 puthead(hp, fo, w)
611 struct header *hp;
612 FILE *fo;
613 int w;
614 {
615 register int gotcha;
616
617 gotcha = 0;
618 if (hp->h_to != NIL && w & GTO)
619 fmt("To:", hp->h_to, fo, w&GCOMMA), gotcha++;
620 #if !MAILX
621 if (hp->h_subject != NOSTR && w & GSUBJECT)
622 fprintf(fo, "Subject: %s\n", hp->h_subject), gotcha++;
623 #endif /* MAILX */
624 if (hp->h_cc != NIL && w & GCC)
625 fmt("Cc:", hp->h_cc, fo, w&GCOMMA), gotcha++;
626 if (hp->h_bcc != NIL && w & GBCC)
627 fmt("Bcc:", hp->h_bcc, fo, w&GCOMMA), gotcha++;
628 #if MAILX
629 if (hp->h_subject != NOSTR && w & GSUBJECT)
630 fprintf(fo, "Subject: %s\n", hp->h_subject), gotcha++;
631 #endif /* MAILX */
632 if (hp->h_replyto != NOSTR && w & GREPLYTO)
633 fprintf(fo, "Reply-To: %s\n", hp->h_replyto), gotcha++;
634 if (hp->h_inreplyto != NOSTR && w & GINREPLYTO)
635 fprintf(fo, "In-Reply-To: <%s>\n", hp->h_inreplyto), gotcha++;
636 #if MAILX
637 if (fo != stdout && gotcha && w & GNL)
638 #else
639 if (gotcha && w & GNL)
640 #endif /* MAILX */
641 (void) putc('\n', fo);
642 return(0);
643 }
644
645 /*
646 * Format the given header line to not exceed 72 characters.
647 */
648 void
fmt(str,np,fo,comma)649 fmt(str, np, fo, comma)
650 char *str;
651 register struct name *np;
652 FILE *fo;
653 int comma;
654 {
655 register col, len;
656
657 comma = comma ? 1 : 0;
658 col = strlen(str);
659 if (col)
660 fputs(str, fo);
661 for (; np != NIL; np = np->n_flink) {
662 if (np->n_flink == NIL)
663 comma = 0;
664 len = strlen(np->n_name);
665 col++; /* for the space */
666 if (col + len + comma > 72 && col > 4) {
667 fputs("\n ", fo);
668 col = 4;
669 } else
670 putc(' ', fo);
671 fputs(np->n_name, fo);
672 if (comma)
673 putc(',', fo);
674 col += len + comma;
675 }
676 putc('\n', fo);
677 }
678
679 /*
680 * Save the outgoing mail on the passed file.
681 */
682
683 /*ARGSUSED*/
684 int
savemail(name,fi)685 savemail(name, fi)
686 char name[];
687 register FILE *fi;
688 {
689 register FILE *fo;
690 char buf[BUFSIZ];
691 register i;
692 time_t now, time();
693 char *ctime();
694
695 if ((fo = Fopen(name, "a")) == NULL) {
696 perror(name);
697 return (-1);
698 }
699 (void) time(&now);
700 fprintf(fo, "From %s %s", myname, ctime(&now));
701 while ((i = fread(buf, 1, sizeof buf, fi)) > 0)
702 (void) fwrite(buf, 1, i, fo);
703 (void) putc('\n', fo);
704 (void) fflush(fo);
705 if (ferror(fo))
706 perror(name);
707 (void) Fclose(fo);
708 rewind(fi);
709 return (0);
710 }
711