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.
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.
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]
23 * Copyright (c) 1985, 2010, Oracle and/or its affiliates. All rights reserved.
24 * Copyright (c) 2016 by Delphix. All rights reserved.
27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
28 /* All Rights Reserved */
31 * University Copyright- Copyright (c) 1982, 1986, 1988
32 * The Regents of the University of California
35 * University Acknowledgment- Portions of this document are derived from
36 * software developed by the University of California, Berkeley, and its
44 * mailx -- a modified version of a University of California at Berkeley
50 static void fmt(register char *str
, register FILE *fo
);
51 static FILE *infix(struct header
*hp
, FILE *fi
);
52 static void statusput(register struct message
*mp
, register FILE *obuf
, int doign
, int (*fp
)(const char *, FILE *));
53 static int savemail(char name
[], struct header
*hp
, FILE *fi
);
54 static int sendmail(char *str
);
55 static int Sendmail(char *str
);
60 * Send message described by the passed pointer to the
61 * passed output buffer. Return -1 on error, but normally
62 * the number of lines written. Adjust the status: field
63 * if need be. If doign is set, suppress ignored header fields.
64 * Call (*fp)(line, obuf) to print the line.
68 struct message
*mailp
,
71 int (*fp
)(const char *, FILE *))
73 register struct message
*mp
;
76 char line
[LINESIZE
], field
[BUFSIZ
];
77 int ishead
, infld
, fline
, dostat
, doclen
, nread
, unused
;
79 int doign
= flag
& M_IGNORE
;
80 int oldign
= 0; /* previous line was ignored */
96 nread
= getaline(line
, LINESIZE
, ibuf
, &unused
);
101 * First line is the From line, so no headers
102 * there to worry about
109 * If line is blank, we've reached end of
110 * headers, so force out status: field
111 * and note that we are no longer in header
112 * fields. Also force out Content-Length: field.
114 if (line
[0] == '\n') {
116 statusput(mailp
, obuf
, doign
, fp
);
120 !isign("content-length", flag
&M_SAVING
)) {
121 snprintf(field
, sizeof (field
),
122 "Content-Length: %ld\n",
133 * If this line is a continuation
134 * of a previous header field, just echo it.
136 if (isspace(line
[0]) && infld
)
143 * If we are no longer looking at real
144 * header lines, force out status:
145 * This happens in uucp style mail where
146 * there are no headers at all.
148 if (!headerp(line
)) {
150 statusput(mailp
, obuf
, doign
, fp
);
159 * Pick up the header field.
160 * If it is an ignored field and
161 * we care about such things, skip it.
165 while (*cp
&& *cp
!= ':' && !isspace(*cp
))
168 oldign
= doign
&& isign(field
, flag
&M_SAVING
);
172 * If the field is "status," go compute and print the
175 if (icequal(field
, "status")) {
177 statusput(mailp
, obuf
, doign
, fp
);
182 if (icequal(field
, "content-length")) {
184 snprintf(line
, sizeof (line
),
185 "Content-Length: %ld\n",
196 if (!ishead
&& !mp
->m_text
&& mp
->m_clen
!= 0) {
201 n
= clen
< sizeof line
? clen
: sizeof line
;
202 if ((n
= fread(line
, 1, (int)n
, ibuf
)) <= 0) {
203 fprintf(stderr
, gettext(
204 "\t(Unexpected end-of-file).\n"));
207 if (fwrite(line
, 1, (int)n
, obuf
) != n
) {
208 fprintf(stderr
, gettext(
209 "\tError writing to the new file.\n"));
230 if (ishead
&& (mailp
->m_flag
& MSTATUS
))
231 printf(gettext("failed to fix up status field\n"));
236 * Test if the passed line is a header line, RFC 822 style.
239 headerp(register char *line
)
241 register char *cp
= line
;
243 while (*cp
&& *cp
!= ' ' && *cp
!= '\t' && *cp
!= ':')
249 * Output a reasonable looking status field.
250 * But if "status" is ignored and doign, forget it.
254 register struct message
*mp
,
257 int (*fp
)(const char *, FILE *))
261 if (doign
&& isign("status", 0))
263 if ((mp
->m_flag
& (MNEW
|MREAD
)) == MNEW
)
265 strcpy(statout
, "Status: ");
266 if (mp
->m_flag
& MREAD
)
267 strcat(statout
, "R");
268 if ((mp
->m_flag
& MNEW
) == 0)
269 strcat(statout
, "O");
270 strcat(statout
, "\n");
271 (*fp
)(statout
, obuf
);
275 * Interface between the argument list and the mail1 routine
276 * which does all the dirty work.
282 register char *cp2
, *cp3
;
287 for (s
= 0, ap
= people
; *ap
; ap
++)
288 s
+= strlen(*ap
) + 2;
289 buf
= (char *)salloc((unsigned)(s
+1));
291 for (ap
= people
; *ap
; ap
++) {
292 for (cp3
= *ap
; *cp3
; ) {
293 if (*cp3
== ' ' || *cp3
== '\t') {
295 while (*cp3
== ' ' || *cp3
== '\t')
300 cp2
= copy(*ap
, cp2
);
306 head
.h_subject
= head
.h_cc
= head
.h_bcc
= head
.h_defopt
= NOSTR
;
307 head
.h_others
= NOSTRPTR
;
309 mail1(&head
, Fflag
, NOSTR
);
316 if (value("flipm") != NOSTR
)
317 return(Sendmail(str
));
318 else return(sendmail(str
));
324 if (value("flipm") != NOSTR
)
325 return(sendmail(str
));
326 else return(Sendmail(str
));
330 * Interface to the mail1 routine for the -t flag
331 * (read headers from text).
339 head
.h_subject
= head
.h_cc
= head
.h_bcc
= head
.h_defopt
= NOSTR
;
340 head
.h_others
= NOSTRPTR
;
342 mail1(&head
, Fflag
, NOSTR
);
347 * Send mail to a bunch of user names. The interface is through
348 * the mail routine below.
358 head
.h_to
= addto(NOSTR
, str
);
359 head
.h_subject
= head
.h_cc
= head
.h_bcc
= head
.h_defopt
= NOSTR
;
360 head
.h_others
= NOSTRPTR
;
362 mail1(&head
, 0, NOSTR
);
367 * Send mail to a bunch of user names. The interface is through
368 * the mail routine below.
369 * save a copy of the letter
379 head
.h_to
= addto(NOSTR
, str
);
380 head
.h_subject
= head
.h_cc
= head
.h_bcc
= head
.h_defopt
= NOSTR
;
381 head
.h_others
= NOSTRPTR
;
383 mail1(&head
, 1, NOSTR
);
388 * Walk the list of fds, closing all but one.
391 closefd_walk(void *special_fd
, int fd
)
393 if (fd
> STDERR_FILENO
&& fd
!= *(int *)special_fd
)
399 * Mail a message on standard input to the people indicated
400 * in the passed header. (Internal interface).
403 mail1(struct header
*hp
, int use_to
, char *orig_to
)
407 char **namelist
, *deliver
;
408 struct name
*to
, *np
;
410 int remote
= rflag
!= NOSTR
|| rmail
;
413 char recfile
[PATHSIZE
];
416 * Collect user's mail from standard input.
417 * Get the result as mtf.
421 if ((mtf
= collect(hp
)) == NULL
)
424 if (hp
->h_subject
== NOSTR
)
425 hp
->h_subject
= sflag
;
426 if (fsize(mtf
) == 0 && hp
->h_subject
== NOSTR
) {
427 printf(gettext("No message !?!\n"));
431 printf(gettext("EOT\n"));
436 * If we need to use the To: line to determine the record
437 * file, save a copy of it before it's sorted below.
440 if (use_to
&& orig_to
== NOSTR
&& hp
->h_to
!= NOSTR
)
441 orig_to
= strcpy((char *)salloc(strlen(hp
->h_to
)+1), hp
->h_to
);
442 else if (orig_to
== NOSTR
)
446 * Now, take the user names from the combined
447 * to and cc lists and do all the alias
452 to
= cat(extract(hp
->h_bcc
, GBCC
),
453 cat(extract(hp
->h_to
, GTO
),
454 extract(hp
->h_cc
, GCC
)));
455 to
= translate(outpre(elide(usermap(to
))));
459 for (gotcha
= 0, np
= to
; np
!= NIL
; np
= np
->n_flink
)
460 if ((np
->n_type
& GDEL
) == 0)
462 hp
->h_to
= detract(to
, GTO
);
463 hp
->h_cc
= detract(to
, GCC
);
464 hp
->h_bcc
= detract(to
, GBCC
);
465 if ((mtf
= infix(hp
, mtf
)) == NULL
) {
466 fprintf(stderr
, gettext(". . . message lost, sorry.\n"));
470 if (askme
&& isatty(0)) {
472 puthead(hp
, stdout
, GTO
|GCC
|GBCC
, 0);
473 printf(gettext("Send? "));
475 if (fgets(ans
, sizeof(ans
), stdin
) && ans
[0] &&
476 (tolower(ans
[0]) != 'y' && ans
[0] != '\n'))
482 * Look through the recipient list for names with /'s
483 * in them which we write to as files directly.
488 printf(gettext("No recipients specified\n"));
494 getrecf(orig_to
, recfile
, use_to
, sizeof (recfile
));
495 if (recfile
!= NOSTR
&& *recfile
)
496 savemail(safeexpand(recfile
), hp
, mtf
);
499 namelist
= unpack(to
);
501 fprintf(stderr
, "Recipients of message:\n");
502 for (t
= namelist
; *t
!= NOSTR
; t
++)
503 fprintf(stderr
, " \"%s\"", *t
);
504 fprintf(stderr
, "\n");
509 * Wait, to absorb a potential zombie, then
510 * fork, set up the temporary mail file as standard
511 * input for "mail" and exec with the user list we generated
512 * far above. Return the process id to caller in case it
513 * wants to await the completion of mail.
517 while (wait3((int *)0, WNOHANG
, (struct rusage
*)0) > 0)
523 while (waitpid((pid_t
)-1, (int *)0, WNOHANG
) > 0)
529 if (pid
== (pid_t
)-1) {
532 deadletter
= Getf("DEAD");
533 if (fp
= fopen(deadletter
,
534 value("appenddeadletter") == NOSTR
? "w" : "a")) {
535 chmod(deadletter
, DEADPERM
);
536 puthead(hp
, fp
, GMASK
|GCLEN
, fsize(mtf
) - textpos
);
537 fseek(mtf
, textpos
, 0);
538 lcwrite(deadletter
, mtf
, fp
,
539 value("appenddeadletter") != NOSTR
);
549 sigset(SIGTSTP
, SIG_IGN
);
550 sigset(SIGTTIN
, SIG_IGN
);
551 sigset(SIGTTOU
, SIG_IGN
);
554 sigset(SIGHUP
, SIG_IGN
);
555 sigset(SIGINT
, SIG_IGN
);
556 sigset(SIGQUIT
, SIG_IGN
);
558 (void) fdwalk(closefd_walk
, &s
);
565 if ((deliver
= value("sendmail")) == NOSTR
)
571 execvp(safeexpand(deliver
), namelist
);
576 if (value("sendwait")!=NOSTR
)
580 while ((p
= wait(&s
)) != pid
&& p
!= (pid_t
)-1)
591 * Prepend a header in front of the collected stuff
592 * and return the new file.
596 infix(struct header
*hp
, FILE *fi
)
598 register FILE *nfo
, *nfi
;
600 char *postmark
, *returnaddr
;
604 if ((fd
= open(tempMail
, O_RDWR
|O_CREAT
|O_EXCL
, 0600)) < 0 ||
605 (nfo
= fdopen(fd
, "w")) == NULL
) {
609 if ((nfi
= fopen(tempMail
, "r")) == NULL
) {
614 removefile(tempMail
);
615 postmark
= value("postmark");
616 returnaddr
= value("returnaddr");
617 if ((postmark
!= NOSTR
) || (returnaddr
!= NOSTR
)) {
618 if (returnaddr
&& *returnaddr
)
619 fprintf(nfo
, "From: %s", returnaddr
);
621 fprintf(nfo
, "From: %s@%s", myname
, host
);
622 if (postmark
&& *postmark
)
623 fprintf(nfo
, " (%s)", postmark
);
626 puthead(hp
, nfo
, (GMASK
& ~GBCC
) | GCLEN
, fsize(fi
));
627 textpos
= ftell(nfo
);
628 while ((c
= getc(fi
)) != EOF
)
648 * Dump the message header on the
649 * passed file buffer.
653 puthead(struct header
*hp
, FILE *fo
, int w
, long clen
)
658 if (hp
->h_to
!= NOSTR
&& (w
& GTO
))
659 fprintf(fo
, "To: "), fmt(hp
->h_to
, fo
), gotcha
++;
660 if ((w
& GSUBJECT
) && (int)value("bsdcompat"))
661 if (hp
->h_subject
!= NOSTR
&& *hp
->h_subject
)
662 fprintf(fo
, "Subject: %s\n", hp
->h_subject
), gotcha
++;
665 fprintf(fo
, "Subject: %s\n", sflag
), gotcha
++;
666 if (hp
->h_cc
!= NOSTR
&& (w
& GCC
))
667 fprintf(fo
, "Cc: "), fmt(hp
->h_cc
, fo
), gotcha
++;
668 if (hp
->h_bcc
!= NOSTR
&& (w
& GBCC
))
669 fprintf(fo
, "Bcc: "), fmt(hp
->h_bcc
, fo
), gotcha
++;
670 if (hp
->h_defopt
!= NOSTR
&& (w
& GDEFOPT
))
672 fprintf(fo
, "Return-Receipt-To: %s\n",
673 hp
->h_defopt
), gotcha
++;
675 fprintf(fo
, "Default-Options: %s\n", hp
->h_defopt
), gotcha
++;
676 if ((w
& GSUBJECT
) && !(int)value("bsdcompat"))
677 if (hp
->h_subject
!= NOSTR
&& *hp
->h_subject
)
678 fprintf(fo
, "Subject: %s\n", hp
->h_subject
), gotcha
++;
681 fprintf(fo
, "Subject: %s\n", sflag
), gotcha
++;
682 if (hp
->h_others
!= NOSTRPTR
&& (w
& GOTHER
)) {
684 for (p
= hp
->h_others
; *p
; p
++)
685 fprintf(fo
, "%s\n", *p
);
690 fprintf(fo
, "Content-Length: %ld\n", clen
), gotcha
++;
692 if (gotcha
&& (w
& GNL
))
698 * Format the given text to not exceed 78 characters.
701 fmt(register char *str
, register FILE *fo
)
703 register int col
= 4;
707 str
= strcpy((char *)salloc(strlen(str
)+1), str
);
708 while (str
= yankword(str
, name
, sizeof (name
), 1)) {
711 if (col
+ len
> 76) {
726 * Save the outgoing mail on the passed file.
729 savemail(char name
[], struct header
*hp
, FILE *fi
)
741 fprintf(stderr
, gettext("save in '%s'\n"), name
);
742 if ((fo
= fopen(name
, "a")) == NULL
) {
750 fprintf(fo
, "From %s %s", n
, ctime(&now
));
751 puthead(hp
, fo
, GMASK
|GCLEN
, fsize(fi
) - textpos
);
752 fseek(fi
, textpos
, 0);
754 while (fgets(line
, sizeof line
, fi
)) {
755 if (!strncmp(line
, "From ", 5))
760 while ((c
= getc(fi
)) != EOF
)