1 /* $NetBSD: send.c,v 1.35 2009/04/12 22:47:39 he Exp $ */
4 * Copyright (c) 1980, 1993
5 * The Regents of the University of California. All rights reserved.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the University nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 #include <sys/cdefs.h>
35 static char sccsid
[] = "@(#)send.c 8.1 (Berkeley) 6/6/93";
37 __RCSID("$NetBSD: send.c,v 1.35 2009/04/12 22:47:39 he Exp $");
51 * Mail -- a mail program
57 * compare twwo name structures. Support for get_smopts.
60 namecmp(struct name
*np1
, struct name
*np2
)
62 for (/*EMPTY*/; np1
&& np2
; np1
= np1
->n_flink
, np2
= np2
->n_flink
) {
63 if (strcmp(np1
->n_name
, np2
->n_name
) != 0)
70 * Get the sendmail options from the smopts list.
73 get_smopts(struct name
*to
)
76 struct name
*smargs
, *np
;
78 for (np
= to
; np
; np
= np
->n_flink
) {
79 if ((sp
= findsmopts(np
->n_name
, 0)) == NULL
)
82 smargs
= sp
->s_smopts
;
83 else if (namecmp(smargs
, sp
->s_smopts
) != 0)
87 smargs
->n_flink
== NULL
&&
88 (smargs
->n_name
== NULL
|| smargs
->n_name
[0] == '\0'))
94 * Output a reasonable looking status field.
97 statusput(struct message
*mp
, FILE *obuf
, const char *prefix
)
102 if (mp
->m_flag
& MREAD
)
104 if ((mp
->m_flag
& MNEW
) == 0)
108 (void)fprintf(obuf
, "%sStatus: %s\n",
109 prefix
== NULL
? "" : prefix
, statout
);
113 * Send message described by the passed pointer to the
114 * passed output buffer. Return -1 on error.
115 * Adjust the status: field if need be.
116 * If doign is given, suppress ignored header fields.
117 * prefix is a string to prepend to each output line.
120 sendmessage(struct message
*mp
, FILE *obuf
, struct ignoretab
*doign
,
121 const char *prefix
, struct mime_info
*mip
)
126 int isheadflag
, infld
, ignoring
, dostat
, firstline
;
137 * Compute the prefix string, without trailing whitespace
139 if (prefix
!= NULL
) {
140 const char *dp
, *dp2
= NULL
;
141 for (dp
= prefix
; *dp
; dp
++)
144 prefixlen
= dp2
== 0 ? 0 : dp2
- prefix
+ 1;
149 dostat
= doign
== 0 || !isign("status", doign
);
155 * Process headers first
159 obuf
= mime_decode_header(mip
);
161 while (len
> 0 && isheadflag
) {
163 if (fgets(line
, (int)sizeof(line
), ibuf
) == NULL
)
165 len
-= linelen
= strlen(line
);
168 * First line is the From line, so no headers
169 * there to worry about
172 ignoring
= doign
== ignoreall
|| doign
== bouncetab
;
174 /* XXX - ignore multipart boundary lines! */
175 if (line
[0] == '-' && line
[1] == '-')
178 } else if (line
[0] == '\n') {
180 * If line is blank, we've reached end of
181 * headers, so force out status: field
182 * and note that we are no longer in header
186 statusput(mp
, obuf
, prefix
);
190 ignoring
= doign
== ignoreall
;
191 } else if (infld
&& is_WSP(line
[0])) {
193 * If this line is a continuation (via space or tab)
194 * of a previous header field, just echo it
195 * (unless the field should be ignored).
196 * In other words, nothing to do.
200 * Pick up the header field if we have one.
203 for (cp
= line
; *cp
&& *cp
!= ':' && !is_WSP(*cp
); cp
++)
209 * Not a header line, force out status:
210 * This happens in uucp style mail where
211 * there are no headers at all.
214 statusput(mp
, obuf
, prefix
);
217 if (doign
!= ignoreall
)
219 (void)putc('\n', obuf
);
224 * If it is an ignored field and
225 * we care about such things, skip it.
229 *save_cp
= 0; /* temporarily null terminate */
230 if (doign
&& isign(line
, doign
))
232 else if ((line
[0] == 's' || line
[0] == 'S') &&
233 strcasecmp(line
, "status") == 0) {
235 * If the field is "status," go compute
236 * and print the real Status: field
239 statusput(mp
, obuf
, prefix
);
247 *save_cp
= save_c
; /* restore the line */
252 * Strip trailing whitespace from prefix
255 if (prefix
!= NULL
) {
257 (void)fputs(prefix
, obuf
);
259 (void)fwrite(prefix
, sizeof(*prefix
),
262 (void)fwrite(line
, sizeof(*line
), linelen
, obuf
);
270 * Copy out message body
274 obuf
= mime_decode_body(mip
);
276 if (obuf
== NULL
) { /* XXX - early out */
282 if (doign
== ignoreall
)
283 len
--; /* skip final blank line */
285 linelen
= 0; /* needed for in case len == 0 */
286 if (prefix
!= NULL
) {
289 if (fgets(line
, (int)sizeof(line
), ibuf
) == NULL
) {
293 len
-= linelen
= strlen(line
);
295 * Strip trailing whitespace from prefix
299 (void)fputs(prefix
, obuf
);
301 (void)fwrite(prefix
, sizeof(*prefix
),
303 (void)fwrite(line
, sizeof(*line
), linelen
, obuf
);
311 linelen
= (size_t)len
< sizeof(line
) ? (size_t)len
: sizeof(line
);
312 if ((linelen
= fread(line
, sizeof(*line
), linelen
, ibuf
)) == 0) {
316 if (fwrite(line
, sizeof(*line
), linelen
, obuf
) != linelen
)
321 if (doign
== ignoreall
&& linelen
> 0 && line
[linelen
- 1] != '\n') {
324 /* no final blank line */
325 if ((c
= getc(ibuf
)) != EOF
&& putc(c
, obuf
) == EOF
)
335 * Fix the header by glopping all of the expanded names from
336 * the distribution list into the appropriate fields.
339 fixhead(struct header
*hp
, struct name
*tolist
)
346 for (np
= tolist
; np
!= NULL
; np
= np
->n_flink
) {
347 if (np
->n_type
& GDEL
)
348 continue; /* Don't copy deleted addresses to the header */
349 if ((np
->n_type
& GMASK
) == GTO
)
351 cat(hp
->h_to
, nalloc(np
->n_name
, np
->n_type
));
352 else if ((np
->n_type
& GMASK
) == GCC
)
354 cat(hp
->h_cc
, nalloc(np
->n_name
, np
->n_type
));
355 else if ((np
->n_type
& GMASK
) == GBCC
)
357 cat(hp
->h_bcc
, nalloc(np
->n_name
, np
->n_type
));
362 * Format the given header line to not exceed 72 characters.
365 fmt(const char *str
, struct name
*np
, FILE *fo
, size_t comma
)
370 comma
= comma
? 1 : 0;
373 (void)fputs(str
, fo
);
374 for (/*EMPTY*/; np
!= NULL
; np
= np
->n_flink
) {
375 if (np
->n_flink
== NULL
)
377 len
= strlen(np
->n_name
);
378 col
++; /* for the space */
379 if (col
+ len
+ comma
> 72 && col
> 4) {
380 (void)fputs("\n ", fo
);
384 (void)fputs(np
->n_name
, fo
);
389 (void)putc('\n', fo
);
393 * Formatting for extra_header lines: try to wrap them before 72
396 * XXX - should this allow for quoting?
399 fmt2(const char *str
, FILE *fo
)
405 p
= strchr(str
, ':');
406 assert(p
!= NULL
); /* this is a coding error */
408 while (str
<= p
) { /* output the header name */
409 (void)putc(*str
, fo
);
413 assert(is_WSP(*str
)); /* this is a coding error */
415 (void)putc(' ', fo
); /* output a single space after the ':' */
418 while (*str
!= '\0') {
420 p
= strpbrk(str
, " \t");
422 p
= str
+ strlen(str
);
423 if (col
+ (p
- str
) > 72 && col
> 4) {
424 (void)fputs("\n ", fo
);
430 (void)putc(*str
, fo
);
434 (void)putc('\n', fo
);
438 * Dump the to, subject, cc header on the
439 * passed file buffer.
442 puthead(struct header
*hp
, FILE *fo
, int w
)
448 if (hp
->h_to
!= NULL
&& w
& GTO
)
449 fmt("To:", hp
->h_to
, fo
, w
& GCOMMA
), gotcha
++;
450 if (hp
->h_subject
!= NULL
&& w
& GSUBJECT
)
451 (void)fprintf(fo
, "Subject: %s\n", hp
->h_subject
), gotcha
++;
452 if (hp
->h_cc
!= NULL
&& w
& GCC
)
453 fmt("Cc:", hp
->h_cc
, fo
, w
& GCOMMA
), gotcha
++;
454 if (hp
->h_bcc
!= NULL
&& w
& GBCC
)
455 fmt("Bcc:", hp
->h_bcc
, fo
, w
& GCOMMA
), gotcha
++;
456 if (hp
->h_in_reply_to
!= NULL
&& w
& GMISC
)
457 (void)fprintf(fo
, "In-Reply-To: %s\n", hp
->h_in_reply_to
), gotcha
++;
458 if (hp
->h_references
!= NULL
&& w
& GMISC
)
459 fmt("References:", hp
->h_references
, fo
, w
& GCOMMA
), gotcha
++;
460 if (hp
->h_extra
!= NULL
&& w
& GMISC
) {
461 for (np
= hp
->h_extra
; np
!= NULL
; np
= np
->n_flink
)
462 fmt2(np
->n_name
, fo
);
465 if (hp
->h_smopts
!= NULL
&& w
& GSMOPTS
)
466 (void)fprintf(fo
, "(sendmail options: %s)\n", detract(hp
->h_smopts
, GSMOPTS
)), gotcha
++;
468 if (w
& GMIME
&& (hp
->h_attach
|| value(ENAME_MIME_ENCODE_MSG
)))
469 mime_putheader(fo
, hp
), gotcha
++;
471 if (gotcha
&& w
& GNL
)
472 (void)putc('\n', fo
);
477 * Prepend a header in front of the collected stuff
478 * and return the new file.
481 infix(struct header
*hp
, FILE *fi
)
485 char tempname
[PATHSIZE
];
487 (void)snprintf(tempname
, sizeof(tempname
),
488 "%s/mail.RsXXXXXXXXXX", tmpdir
);
489 if ((fd
= mkstemp(tempname
)) == -1 ||
490 (nfo
= Fdopen(fd
, "w")) == NULL
) {
493 warn("%s", tempname
);
496 if ((nfi
= Fopen(tempname
, "r")) == NULL
) {
497 warn("%s", tempname
);
503 (void)puthead(hp
, nfo
,
504 GTO
| GSUBJECT
| GCC
| GBCC
| GMISC
| GNL
| GCOMMA
522 warn("%s", tempname
);
535 * Save the outgoing mail on the passed file.
537 * Take care not to save a valid headline or the mbox file will be
538 * broken. Prefix valid headlines with '>'.
540 * Note: most servers prefix any line starting with "From " in the
541 * body of the message. We are more restrictive to avoid header/body
546 savemail(const char name
[], FILE *fi
)
556 fo
= Fopen(name
, "a");
563 (void)fprintf(fo
, "From %s %s", myname
, ctime(&now
));
565 while ((line
= fgetln(fi
, &linelen
)) != NULL
) {
567 cp
= line
+ linelen
- 1;
569 linelen
> sizeof("From . Aaa Aaa O0 00:00 0000") &&
579 (void)fputc('>', fo
);
582 (void)fwrite(line
, 1, linelen
, fo
);
583 afterblank
= linelen
== 1 && line
[0] == '\n';
585 (void)putc('\n', fo
);
595 * Mail a message that is already prepared in a file.
598 mail2(FILE *mtf
, const char **namelist
)
606 (void)printf("Sendmail arguments:");
607 for (t
= namelist
; *t
!= NULL
; t
++)
608 (void)printf(" \"%s\"", *t
);
612 if ((cp
= value(ENAME_RECORD
)) != NULL
)
613 (void)savemail(expand(cp
), mtf
);
615 * Fork, set up the temporary mail file as standard
616 * input for "mail", and exec with the user list we generated
628 (void)sigemptyset(&nset
);
629 (void)sigaddset(&nset
, SIGHUP
);
630 (void)sigaddset(&nset
, SIGINT
);
631 (void)sigaddset(&nset
, SIGQUIT
);
632 (void)sigaddset(&nset
, SIGTSTP
);
633 (void)sigaddset(&nset
, SIGTTIN
);
634 (void)sigaddset(&nset
, SIGTTOU
);
635 prepare_child(&nset
, fileno(mtf
), -1);
636 if ((cp
= value(ENAME_SENDMAIL
)) != NULL
)
640 (void)execv(cp
, (char *const *)__UNCONST(namelist
));
643 break; /* Appease GCC */
646 if (value(ENAME_VERBOSE
) != NULL
)
647 (void)wait_child(pid
);
655 ncopy(struct name
*np
)
661 lp
= NULL
; /* XXX gcc -Wuninitialized sh3 */
663 for (/*EMPTY*/; np
; np
= np
->n_flink
) {
664 tp
= nalloc(np
->n_name
, np
->n_type
);
677 * Mail a message on standard input to the people indicated
678 * in the passed header. (Internal interface).
681 mail1(struct header
*hp
, int printheaders
)
683 const char **namelist
;
688 * Collect user's mail from standard input.
689 * Get the result as mtf.
691 if ((mtf
= collect(hp
, printheaders
)) == NULL
)
695 * Grab any extra header lines. Do this after collect() so
696 * that we can add header lines while collecting.
698 hp
->h_extra
= ncopy(extra_headers
);
700 if (value(ENAME_INTERACTIVE
) != NULL
) {
701 if (value(ENAME_ASKCC
) != NULL
|| value(ENAME_ASKBCC
) != NULL
) {
702 if (value(ENAME_ASKCC
) != NULL
)
703 (void)grabh(hp
, GCC
);
704 if (value(ENAME_ASKBCC
) != NULL
)
705 (void)grabh(hp
, GBCC
);
707 (void)printf("EOT\n");
708 (void)fflush(stdout
);
711 if (fsize(mtf
) == 0) {
712 if (value(ENAME_DONTSENDEMPTY
) != NULL
)
714 if (hp
->h_subject
== NULL
)
715 (void)printf("No message, no subject; hope that's ok\n");
717 (void)printf("Null message body; hope that's ok\n");
720 * Now, take the user names from the combined
721 * to and cc lists and do all the alias
725 to
= usermap(cat(hp
->h_bcc
, cat(hp
->h_to
, hp
->h_cc
)));
727 (void)printf("No recipients specified\n");
732 * If there are attachments, repackage the mail as a
733 * multi-part MIME message.
735 if (hp
->h_attach
|| value(ENAME_MIME_ENCODE_MSG
))
736 mtf
= mime_encode(mtf
, hp
);
739 * Look through the recipient list for names with /'s
740 * in them which we write to as files directly.
742 to
= outof(to
, mtf
, hp
);
749 if ((mtf
= infix(hp
, mtf
)) == NULL
) {
750 (void)fprintf(stderr
, ". . . message lost, sorry.\n");
753 if (hp
->h_smopts
== NULL
) {
754 hp
->h_smopts
= get_smopts(to
);
755 if (hp
->h_smopts
!= NULL
&&
756 hp
->h_smopts
->n_name
[0] != '\0' &&
757 value(ENAME_SMOPTS_VERIFY
) != NULL
)
758 if (grabh(hp
, GSMOPTS
)) {
759 (void)printf("mail aborted!\n");
764 namelist
= unpack(cat(hp
->h_smopts
, to
));
765 mail2(mtf
, namelist
);
771 * Interface between the argument list and the mail1 routine
772 * which does all the dirty work.
775 mail(struct name
*to
, struct name
*cc
, struct name
*bcc
,
776 struct name
*smopts
, char *subject
, struct attachment
*attach
)
780 /* ensure that all header fields are initially NULL */
781 (void)memset(&head
, 0, sizeof(head
));
784 head
.h_subject
= subject
;
787 head
.h_smopts
= smopts
;
789 head
.h_attach
= attach
;
796 * Send mail to a bunch of user names. The interface is through
797 * the mail1 routine above.
805 /* ensure that all header fields are initially NULL */
806 (void)memset(&head
, 0, sizeof(head
));
808 head
.h_to
= extract(str
, GTO
);