1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright (c) 1985, 2010, Oracle and/or its affiliates. All rights reserved.
24 */
25
26 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
27 /* All Rights Reserved */
28
29 /*
30 * University Copyright- Copyright (c) 1982, 1986, 1988
31 * The Regents of the University of California
32 * All Rights Reserved
33 *
34 * University Acknowledgment- Portions of this document are derived from
35 * software developed by the University of California, Berkeley, and its
36 * contributors.
37 */
38
39 #include "rcv.h"
40 #include <locale.h>
41
42 /*
43 * mailx -- a modified version of a University of California at Berkeley
44 * mail program
45 *
46 * Mail to others.
47 */
48
49 static void fmt(register char *str, register FILE *fo);
50 static FILE *infix(struct header *hp, FILE *fi);
51 static void statusput(register struct message *mp, register FILE *obuf, int doign, int (*fp)(const char *, FILE *));
52 static int savemail(char name[], struct header *hp, FILE *fi);
53 static int sendmail(char *str);
54 static int Sendmail(char *str);
55
56 static off_t textpos;
57
58 /*
59 * Send message described by the passed pointer to the
60 * passed output buffer. Return -1 on error, but normally
61 * the number of lines written. Adjust the status: field
62 * if need be. If doign is set, suppress ignored header fields.
63 * Call (*fp)(line, obuf) to print the line.
64 */
65 long
msend(struct message * mailp,FILE * obuf,int flag,int (* fp)(const char *,FILE *))66 msend(
67 struct message *mailp,
68 FILE *obuf,
69 int flag,
70 int (*fp)(const char *, FILE *))
71 {
72 register struct message *mp;
73 long clen, n, c;
74 FILE *ibuf;
75 char line[LINESIZE], field[BUFSIZ];
76 int ishead, infld, fline, dostat, doclen, nread, unused;
77 char *cp, *cp2;
78 int doign = flag & M_IGNORE;
79 int oldign = 0; /* previous line was ignored */
80 long lc;
81
82 mp = mailp;
83 if (mp->m_clen == 0)
84 setclen(mp);
85 ibuf = setinput(mp);
86 c = mp->m_size;
87 ishead = 1;
88 dostat = 1;
89 doclen = 1;
90 infld = 0;
91 fline = 1;
92 lc = 0;
93 clearerr(obuf);
94 while (c > 0L) {
95 nread = getaline(line, LINESIZE, ibuf, &unused);
96 c -= nread;
97 lc++;
98 if (ishead) {
99 /*
100 * First line is the From line, so no headers
101 * there to worry about
102 */
103 if (fline) {
104 fline = 0;
105 goto writeit;
106 }
107 /*
108 * If line is blank, we've reached end of
109 * headers, so force out status: field
110 * and note that we are no longer in header
111 * fields. Also force out Content-Length: field.
112 */
113 if (line[0] == '\n') {
114 if (dostat) {
115 statusput(mailp, obuf, doign, fp);
116 dostat = 0;
117 }
118 if (doclen &&
119 !isign("content-length", flag&M_SAVING)) {
120 snprintf(field, sizeof (field),
121 "Content-Length: %ld\n",
122 mp->m_clen - 1);
123 (*fp)(field, obuf);
124 if (ferror(obuf))
125 return(-1);
126 doclen = 0;
127 }
128 ishead = 0;
129 goto writeit;
130 }
131 /*
132 * If this line is a continuation
133 * of a previous header field, just echo it.
134 */
135 if (isspace(line[0]) && infld)
136 if (oldign)
137 continue;
138 else
139 goto writeit;
140 infld = 0;
141 /*
142 * If we are no longer looking at real
143 * header lines, force out status:
144 * This happens in uucp style mail where
145 * there are no headers at all.
146 */
147 if (!headerp(line)) {
148 if (dostat) {
149 statusput(mailp, obuf, doign, fp);
150 dostat = 0;
151 }
152 (*fp)("\n", obuf);
153 ishead = 0;
154 goto writeit;
155 }
156 infld++;
157 /*
158 * Pick up the header field.
159 * If it is an ignored field and
160 * we care about such things, skip it.
161 */
162 cp = line;
163 cp2 = field;
164 while (*cp && *cp != ':' && !isspace(*cp))
165 *cp2++ = *cp++;
166 *cp2 = 0;
167 oldign = doign && isign(field, flag&M_SAVING);
168 if (oldign)
169 continue;
170 /*
171 * If the field is "status," go compute and print the
172 * real Status: field
173 */
174 if (icequal(field, "status")) {
175 if (dostat) {
176 statusput(mailp, obuf, doign, fp);
177 dostat = 0;
178 }
179 continue;
180 }
181 if (icequal(field, "content-length")) {
182 if (doclen) {
183 snprintf(line, sizeof (line),
184 "Content-Length: %ld\n",
185 mp->m_clen - 1);
186 (*fp)(line, obuf);
187 if (ferror(obuf))
188 return(-1);
189 doclen = 0;
190 }
191 continue;
192 }
193 }
194 writeit:
195 if (!ishead && !mp->m_text && mp->m_clen != 0) {
196 if (line[0] == '\n')
197 putc('\n', obuf);
198 clen = mp->m_clen-1;
199 for (;;) {
200 n = clen < sizeof line ? clen : sizeof line;
201 if ((n = fread(line, 1, (int)n, ibuf)) <= 0) {
202 fprintf(stderr, gettext(
203 "\t(Unexpected end-of-file).\n"));
204 clen = 0;
205 } else {
206 if (fwrite(line, 1, (int)n, obuf) != n) {
207 fprintf(stderr, gettext(
208 "\tError writing to the new file.\n"));
209 fflush(obuf);
210 if (fferror(obuf))
211 return (-1);
212 }
213 }
214 clen -= n;
215 if (clen <= 0) {
216 break;
217 }
218 }
219 c = 0L;
220 } else {
221 (*fp)(line, obuf);
222 if (ferror(obuf))
223 return(-1);
224 }
225 }
226 fflush(obuf);
227 if (ferror(obuf))
228 return(-1);
229 if (ishead && (mailp->m_flag & MSTATUS))
230 printf(gettext("failed to fix up status field\n"));
231 return(lc);
232 }
233
234 /*
235 * Test if the passed line is a header line, RFC 822 style.
236 */
237 int
headerp(register char * line)238 headerp(register char *line)
239 {
240 register char *cp = line;
241
242 while (*cp && *cp != ' ' && *cp != '\t' && *cp != ':')
243 cp++;
244 return(*cp == ':');
245 }
246
247 /*
248 * Output a reasonable looking status field.
249 * But if "status" is ignored and doign, forget it.
250 */
251 static void
statusput(register struct message * mp,register FILE * obuf,int doign,int (* fp)(const char *,FILE *))252 statusput(
253 register struct message *mp,
254 register FILE *obuf,
255 int doign,
256 int (*fp)(const char *, FILE *))
257 {
258 char statout[12];
259
260 if (doign && isign("status", 0))
261 return;
262 if ((mp->m_flag & (MNEW|MREAD)) == MNEW)
263 return;
264 strcpy(statout, "Status: ");
265 if (mp->m_flag & MREAD)
266 strcat(statout, "R");
267 if ((mp->m_flag & MNEW) == 0)
268 strcat(statout, "O");
269 strcat(statout, "\n");
270 (*fp)(statout, obuf);
271 }
272
273 /*
274 * Interface between the argument list and the mail1 routine
275 * which does all the dirty work.
276 */
277
278 int
mail(char ** people)279 mail(char **people)
280 {
281 register char *cp2, *cp3;
282 register int s;
283 char *buf, **ap;
284 struct header head;
285
286 for (s = 0, ap = people; *ap; ap++)
287 s += strlen(*ap) + 2;
288 buf = (char *)salloc((unsigned)(s+1));
289 cp2 = buf;
290 for (ap = people; *ap; ap++) {
291 for (cp3 = *ap; *cp3; ) {
292 if (*cp3 == ' ' || *cp3 == '\t') {
293 *cp3++ = ',';
294 while (*cp3 == ' ' || *cp3 == '\t')
295 cp3++;
296 } else
297 cp3++;
298 }
299 cp2 = copy(*ap, cp2);
300 *cp2++ = ',';
301 *cp2++ = ' ';
302 }
303 *cp2 = '\0';
304 head.h_to = buf;
305 head.h_subject = head.h_cc = head.h_bcc = head.h_defopt = NOSTR;
306 head.h_others = NOSTRPTR;
307 head.h_seq = 0;
308 mail1(&head, Fflag, NOSTR);
309 return(0);
310 }
311
312 int
sendm(char * str)313 sendm(char *str)
314 {
315 if (value("flipm") != NOSTR)
316 return(Sendmail(str));
317 else return(sendmail(str));
318 }
319
320 int
Sendm(char * str)321 Sendm(char *str)
322 {
323 if (value("flipm") != NOSTR)
324 return(sendmail(str));
325 else return(Sendmail(str));
326 }
327
328 /*
329 * Interface to the mail1 routine for the -t flag
330 * (read headers from text).
331 */
332 int
tmail(void)333 tmail(void)
334 {
335 struct header head;
336
337 head.h_to = NOSTR;
338 head.h_subject = head.h_cc = head.h_bcc = head.h_defopt = NOSTR;
339 head.h_others = NOSTRPTR;
340 head.h_seq = 0;
341 mail1(&head, Fflag, NOSTR);
342 return(0);
343 }
344
345 /*
346 * Send mail to a bunch of user names. The interface is through
347 * the mail routine below.
348 */
349 static int
sendmail(char * str)350 sendmail(char *str)
351 {
352 struct header head;
353
354 if (blankline(str))
355 head.h_to = NOSTR;
356 else
357 head.h_to = addto(NOSTR, str);
358 head.h_subject = head.h_cc = head.h_bcc = head.h_defopt = NOSTR;
359 head.h_others = NOSTRPTR;
360 head.h_seq = 0;
361 mail1(&head, 0, NOSTR);
362 return(0);
363 }
364
365 /*
366 * Send mail to a bunch of user names. The interface is through
367 * the mail routine below.
368 * save a copy of the letter
369 */
370 static int
Sendmail(char * str)371 Sendmail(char *str)
372 {
373 struct header head;
374
375 if (blankline(str))
376 head.h_to = NOSTR;
377 else
378 head.h_to = addto(NOSTR, str);
379 head.h_subject = head.h_cc = head.h_bcc = head.h_defopt = NOSTR;
380 head.h_others = NOSTRPTR;
381 head.h_seq = 0;
382 mail1(&head, 1, NOSTR);
383 return(0);
384 }
385
386 /*
387 * Walk the list of fds, closing all but one.
388 */
389 static int
closefd_walk(void * special_fd,int fd)390 closefd_walk(void *special_fd, int fd)
391 {
392 if (fd > STDERR_FILENO && fd != *(int *)special_fd)
393 (void) close(fd);
394 return (0);
395 }
396
397 /*
398 * Mail a message on standard input to the people indicated
399 * in the passed header. (Internal interface).
400 */
401 void
mail1(struct header * hp,int use_to,char * orig_to)402 mail1(struct header *hp, int use_to, char *orig_to)
403 {
404 pid_t p, pid;
405 int i, s, gotcha;
406 char **namelist, *deliver;
407 struct name *to, *np;
408 FILE *mtf, *fp;
409 int remote = rflag != NOSTR || rmail;
410 char **t;
411 char *deadletter;
412 char recfile[PATHSIZE];
413
414 /*
415 * Collect user's mail from standard input.
416 * Get the result as mtf.
417 */
418
419 pid = (pid_t)-1;
420 if ((mtf = collect(hp)) == NULL)
421 return;
422 hp->h_seq = 1;
423 if (hp->h_subject == NOSTR)
424 hp->h_subject = sflag;
425 if (fsize(mtf) == 0 && hp->h_subject == NOSTR) {
426 printf(gettext("No message !?!\n"));
427 goto out;
428 }
429 if (intty) {
430 printf(gettext("EOT\n"));
431 flush();
432 }
433
434 /*
435 * If we need to use the To: line to determine the record
436 * file, save a copy of it before it's sorted below.
437 */
438
439 if (use_to && orig_to == NOSTR && hp->h_to != NOSTR)
440 orig_to = strcpy((char *)salloc(strlen(hp->h_to)+1), hp->h_to);
441 else if (orig_to == NOSTR)
442 orig_to = "";
443
444 /*
445 * Now, take the user names from the combined
446 * to and cc lists and do all the alias
447 * processing.
448 */
449
450 senderr = 0;
451 to = cat(extract(hp->h_bcc, GBCC),
452 cat(extract(hp->h_to, GTO),
453 extract(hp->h_cc, GCC)));
454 to = translate(outpre(elide(usermap(to))));
455 if (!senderr)
456 mapf(to, myname);
457 mechk(to);
458 for (gotcha = 0, np = to; np != NIL; np = np->n_flink)
459 if ((np->n_type & GDEL) == 0)
460 gotcha++;
461 hp->h_to = detract(to, GTO);
462 hp->h_cc = detract(to, GCC);
463 hp->h_bcc = detract(to, GBCC);
464 if ((mtf = infix(hp, mtf)) == NULL) {
465 fprintf(stderr, gettext(". . . message lost, sorry.\n"));
466 return;
467 }
468 rewind(mtf);
469 if (askme && isatty(0)) {
470 char ans[64];
471 puthead(hp, stdout, GTO|GCC|GBCC, 0);
472 printf(gettext("Send? "));
473 printf("[yes] ");
474 if (fgets(ans, sizeof(ans), stdin) && ans[0] &&
475 (tolower(ans[0]) != 'y' && ans[0] != '\n'))
476 goto dead;
477 }
478 if (senderr)
479 goto dead;
480 /*
481 * Look through the recipient list for names with /'s
482 * in them which we write to as files directly.
483 */
484 i = outof(to, mtf);
485 rewind(mtf);
486 if (!gotcha && !i) {
487 printf(gettext("No recipients specified\n"));
488 goto dead;
489 }
490 if (senderr)
491 goto dead;
492
493 getrecf(orig_to, recfile, use_to, sizeof (recfile));
494 if (recfile != NOSTR && *recfile)
495 savemail(safeexpand(recfile), hp, mtf);
496 if (!gotcha)
497 goto out;
498 namelist = unpack(to);
499 if (debug) {
500 fprintf(stderr, "Recipients of message:\n");
501 for (t = namelist; *t != NOSTR; t++)
502 fprintf(stderr, " \"%s\"", *t);
503 fprintf(stderr, "\n");
504 return;
505 }
506
507 /*
508 * Wait, to absorb a potential zombie, then
509 * fork, set up the temporary mail file as standard
510 * input for "mail" and exec with the user list we generated
511 * far above. Return the process id to caller in case he
512 * wants to await the completion of mail.
513 */
514
515 #ifdef VMUNIX
516 while (wait3((int *)0, WNOHANG, (struct rusage *)0) > 0)
517 ;
518 #else
519 #ifdef preSVr4
520 wait((int *)0);
521 #else
522 while (waitpid((pid_t)-1, (int *)0, WNOHANG) > 0)
523 ;
524 #endif
525 #endif
526 rewind(mtf);
527 pid = fork();
528 if (pid == (pid_t)-1) {
529 perror("fork");
530 dead:
531 deadletter = Getf("DEAD");
532 if (fp = fopen(deadletter,
533 value("appenddeadletter") == NOSTR ? "w" : "a")) {
534 chmod(deadletter, DEADPERM);
535 puthead(hp, fp, GMASK|GCLEN, fsize(mtf) - textpos);
536 fseek(mtf, textpos, 0);
537 lcwrite(deadletter, mtf, fp,
538 value("appenddeadletter") != NOSTR);
539 fclose(fp);
540 } else
541 perror(deadletter);
542 goto out;
543 }
544 if (pid == 0) {
545 sigchild();
546 #ifdef SIGTSTP
547 if (remote == 0) {
548 sigset(SIGTSTP, SIG_IGN);
549 sigset(SIGTTIN, SIG_IGN);
550 sigset(SIGTTOU, SIG_IGN);
551 }
552 #endif
553 sigset(SIGHUP, SIG_IGN);
554 sigset(SIGINT, SIG_IGN);
555 sigset(SIGQUIT, SIG_IGN);
556 s = fileno(mtf);
557 (void) fdwalk(closefd_walk, &s);
558 close(0);
559 dup(s);
560 close(s);
561 #ifdef CC
562 submit(getpid());
563 #endif /* CC */
564 if ((deliver = value("sendmail")) == NOSTR)
565 #ifdef SENDMAIL
566 deliver = SENDMAIL;
567 #else
568 deliver = MAIL;
569 #endif
570 execvp(safeexpand(deliver), namelist);
571 perror(deliver);
572 exit(1);
573 }
574
575 if (value("sendwait")!=NOSTR)
576 remote++;
577 out:
578 if (remote) {
579 while ((p = wait(&s)) != pid && p != (pid_t)-1)
580 ;
581 if (s != 0)
582 senderr++;
583 pid = 0;
584 }
585 fclose(mtf);
586 return;
587 }
588
589 /*
590 * Prepend a header in front of the collected stuff
591 * and return the new file.
592 */
593
594 static FILE *
infix(struct header * hp,FILE * fi)595 infix(struct header *hp, FILE *fi)
596 {
597 register FILE *nfo, *nfi;
598 register int c;
599 char *postmark, *returnaddr;
600 int fd = -1;
601
602 rewind(fi);
603 if ((fd = open(tempMail, O_RDWR|O_CREAT|O_EXCL, 0600)) < 0 ||
604 (nfo = fdopen(fd, "w")) == NULL) {
605 perror(tempMail);
606 return(fi);
607 }
608 if ((nfi = fopen(tempMail, "r")) == NULL) {
609 perror(tempMail);
610 fclose(nfo);
611 return(fi);
612 }
613 removefile(tempMail);
614 postmark = value("postmark");
615 returnaddr = value("returnaddr");
616 if ((postmark != NOSTR) || (returnaddr != NOSTR)) {
617 if (returnaddr && *returnaddr)
618 fprintf(nfo, "From: %s", returnaddr);
619 else
620 fprintf(nfo, "From: %s@%s", myname, host);
621 if (postmark && *postmark)
622 fprintf(nfo, " (%s)", postmark);
623 putc('\n', nfo);
624 }
625 puthead(hp, nfo, (GMASK & ~GBCC) | GCLEN, fsize(fi));
626 textpos = ftell(nfo);
627 while ((c = getc(fi)) != EOF)
628 putc(c, nfo);
629 if (ferror(fi)) {
630 perror("read");
631 return(fi);
632 }
633 fflush(nfo);
634 if (fferror(nfo)) {
635 perror(tempMail);
636 fclose(nfo);
637 fclose(nfi);
638 return(fi);
639 }
640 fclose(nfo);
641 fclose(fi);
642 rewind(nfi);
643 return(nfi);
644 }
645
646 /*
647 * Dump the message header on the
648 * passed file buffer.
649 */
650
651 int
puthead(struct header * hp,FILE * fo,int w,long clen)652 puthead(struct header *hp, FILE *fo, int w, long clen)
653 {
654 register int gotcha;
655
656 gotcha = 0;
657 if (hp->h_to != NOSTR && (w & GTO))
658 fprintf(fo, "To: "), fmt(hp->h_to, fo), gotcha++;
659 if ((w & GSUBJECT) && (int)value("bsdcompat"))
660 if (hp->h_subject != NOSTR && *hp->h_subject)
661 fprintf(fo, "Subject: %s\n", hp->h_subject), gotcha++;
662 else
663 if (sflag && *sflag)
664 fprintf(fo, "Subject: %s\n", sflag), gotcha++;
665 if (hp->h_cc != NOSTR && (w & GCC))
666 fprintf(fo, "Cc: "), fmt(hp->h_cc, fo), gotcha++;
667 if (hp->h_bcc != NOSTR && (w & GBCC))
668 fprintf(fo, "Bcc: "), fmt(hp->h_bcc, fo), gotcha++;
669 if (hp->h_defopt != NOSTR && (w & GDEFOPT))
670 if (receipt_flg)
671 fprintf(fo, "Return-Receipt-To: %s\n",
672 hp->h_defopt), gotcha++;
673 else
674 fprintf(fo, "Default-Options: %s\n", hp->h_defopt), gotcha++;
675 if ((w & GSUBJECT) && !(int)value("bsdcompat"))
676 if (hp->h_subject != NOSTR && *hp->h_subject)
677 fprintf(fo, "Subject: %s\n", hp->h_subject), gotcha++;
678 else
679 if (sflag && *sflag)
680 fprintf(fo, "Subject: %s\n", sflag), gotcha++;
681 if (hp->h_others != NOSTRPTR && (w & GOTHER)) {
682 char **p;
683 for (p = hp->h_others; *p; p++)
684 fprintf(fo, "%s\n", *p);
685 gotcha++;
686 }
687 #ifndef preSVr4
688 if (w & GCLEN)
689 fprintf(fo, "Content-Length: %ld\n", clen), gotcha++;
690 #endif
691 if (gotcha && (w & GNL))
692 putc('\n', fo);
693 return(0);
694 }
695
696 /*
697 * Format the given text to not exceed 78 characters.
698 */
699 static void
fmt(register char * str,register FILE * fo)700 fmt(register char *str, register FILE *fo)
701 {
702 register int col = 4;
703 char name[256];
704 int len;
705
706 str = strcpy((char *)salloc(strlen(str)+1), str);
707 while (str = yankword(str, name, sizeof (name), 1)) {
708 len = strlen(name);
709 if (col > 4) {
710 if (col + len > 76) {
711 fputs(",\n ", fo);
712 col = 4;
713 } else {
714 fputs(", ", fo);
715 col += 2;
716 }
717 }
718 fputs(name, fo);
719 col += len;
720 }
721 putc('\n', fo);
722 }
723
724 /*
725 * Save the outgoing mail on the passed file.
726 */
727 static int
savemail(char name[],struct header * hp,FILE * fi)728 savemail(char name[], struct header *hp, FILE *fi)
729 {
730 register FILE *fo;
731 time_t now;
732 char *n;
733 #ifdef preSVr4
734 char line[BUFSIZ];
735 #else
736 int c;
737 #endif
738
739 if (debug)
740 fprintf(stderr, gettext("save in '%s'\n"), name);
741 if ((fo = fopen(name, "a")) == NULL) {
742 perror(name);
743 return(-1);
744 }
745 time(&now);
746 n = rflag;
747 if (n == NOSTR)
748 n = myname;
749 fprintf(fo, "From %s %s", n, ctime(&now));
750 puthead(hp, fo, GMASK|GCLEN, fsize(fi) - textpos);
751 fseek(fi, textpos, 0);
752 #ifdef preSVr4
753 while (fgets(line, sizeof line, fi)) {
754 if (!strncmp(line, "From ", 5))
755 putc('>', fo);
756 fputs(line, fo);
757 }
758 #else
759 while ((c = getc(fi)) != EOF)
760 putc(c, fo);
761 #endif
762 putc('\n', fo);
763 fflush(fo);
764 if (fferror(fo))
765 perror(name);
766 fclose(fo);
767 return(0);
768 }
769