1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 - 2014 Steffen (Daode) Nurpmeso <sdaoden@users.sf.net>.
8 * Copyright (c) 1980, 1993
9 * The Regents of the University of California. All rights reserved.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. All advertising materials mentioning features or use of this software
20 * must display the following acknowledgement:
21 * This product includes software developed by the University of
22 * California, Berkeley and its contributors.
23 * 4. Neither the name of the University nor the names of its contributors
24 * may be used to endorse or promote products derived from this software
25 * without specific prior written permission.
27 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
40 #ifndef HAVE_AMALGAMATION
47 #define SEND_LINESIZE \
48 ((1024 / B64_ENCODE_INPUT_PER_LINE) * B64_ENCODE_INPUT_PER_LINE)
50 static char const *__sendout_ident
; /* TODO temporary hack; rewrite puthead() */
51 static char * _sendout_boundary
;
52 static bool_t _sendout_error
;
54 static enum okay
_putname(char const *line
, enum gfield w
,
55 enum sendaction action
, size_t *gotcha
,
56 char const *prefix
, FILE *fo
, struct name
**xp
);
58 /* Get an encoding flag based on the given string */
59 static char const * _get_encoding(const enum conversion convert
);
61 /* Write an attachment to the file buffer, converting to MIME */
62 static int _attach_file(struct attachment
*ap
, FILE *fo
);
63 static int __attach_file(struct attachment
*ap
, FILE *fo
);
65 /* There are non-local receivers, collect credentials etc. */
66 static bool_t
_sendbundle_setup_creds(struct sendbundle
*sbpm
,
69 /* Prepare arguments for the MTA (non *smtp* mode) */
70 static char const ** _prepare_mta_args(struct name
*to
, struct header
*hp
);
72 /* Fix the header by glopping all of the expanded names from the distribution
73 * list into the appropriate fields */
74 static struct name
* fixhead(struct header
*hp
, struct name
*tolist
);
76 /* Put the signature file at fo. TODO layer rewrite: *integrate in body*!! */
77 static int put_signature(FILE *fo
, int convert
);
79 /* Attach a message to the file buffer */
80 static int attach_message(struct attachment
*ap
, FILE *fo
);
82 /* Generate the body of a MIME multipart message */
83 static int make_multipart(struct header
*hp
, int convert
, FILE *fi
,
84 FILE *fo
, char const *contenttype
, char const *charset
);
86 /* Prepend a header in front of the collected stuff and return the new file */
87 static FILE * infix(struct header
*hp
, FILE *fi
);
89 /* Check wether Disposition-Notification-To: is desired */
90 static bool_t
_check_dispo_notif(struct name
*mdn
, struct header
*hp
,
93 /* Save the outgoing mail on the passed file */
94 static int savemail(char const *name
, FILE *fi
);
96 /* Send mail to a bunch of user names. The interface is through mail() */
97 static int sendmail_internal(void *v
, int recipient_record
);
100 static bool_t
_transfer(struct sendbundle
*sbp
);
102 /* Start the MTA mailing */
103 static bool_t
start_mta(struct sendbundle
*sbp
);
105 /* Record outgoing mail if instructed to do so; in *record* unless to is set */
106 static bool_t
mightrecord(FILE *fp
, struct name
*to
);
108 /* Create a Message-Id: header field. Use either host name or from address */
109 static char * _message_id(struct header
*hp
);
111 /* Format the given header line to not exceed 72 characters */
112 static int fmt(char const *str
, struct name
*np
, FILE *fo
, int comma
,
113 int dropinvalid
, int domime
);
115 /* Rewrite a message for resending, adding the Resent-Headers */
116 static int infix_resend(FILE *fi
, FILE *fo
, struct message
*mp
,
117 struct name
*to
, int add_resent
);
120 _putname(char const *line
, enum gfield w
, enum sendaction action
,
121 size_t *gotcha
, char const *prefix
, FILE *fo
, struct name
**xp
)
127 np
= lextract(line
, GEXTRA
| GFULL
);
132 else if (fmt(prefix
, np
, fo
, w
& GCOMMA
, 0, (action
!= SEND_TODISP
)))
134 else if (gotcha
!= NULL
)
141 _get_encoding(enum conversion
const convert
)
147 case CONV_7BIT
: rv
= "7bit"; break;
148 case CONV_8BIT
: rv
= "8bit"; break;
149 case CONV_TOQP
: rv
= "quoted-printable"; break;
150 case CONV_TOB64
: rv
= "base64"; break;
151 default: rv
= NULL
; break;
158 _attach_file(struct attachment
*ap
, FILE *fo
)
160 /* TODO of course, the MIME classification needs to performed once
161 * TODO only, not for each and every charset anew ... ;-// */
162 char *charset_iter_orig
[2];
167 /* Is this already in target charset? Simply copy over */
168 if (ap
->a_conv
== AC_TMPFILE
) {
169 err
= __attach_file(ap
, fo
);
171 DBG( ap
->a_tmpf
= NULL
; )
175 /* If we don't apply charset conversion at all (fixed input=ouput charset)
176 * we also simply copy over, since it's the users desire */
177 if (ap
->a_conv
== AC_FIX_INCS
) {
178 ap
->a_charset
= ap
->a_input_charset
;
179 err
= __attach_file(ap
, fo
);
183 /* Otherwise we need to iterate over all possible output charsets */
184 if ((offs
= ftell(fo
)) == -1) {
188 charset_iter_recurse(charset_iter_orig
);
189 for (charset_iter_reset(NULL
);; charset_iter_next()) {
190 if (!charset_iter_is_valid()) {
194 err
= __attach_file(ap
, fo
);
195 if (err
== 0 || (err
!= EILSEQ
&& err
!= EINVAL
))
198 if (fseek(fo
, offs
, SEEK_SET
) == -1) {
202 if (ap
->a_conv
!= AC_DEFAULT
) {
206 ap
->a_charset
= NULL
;
208 charset_iter_restore(charset_iter_orig
);
215 __attach_file(struct attachment
*ap
, FILE *fo
) /* XXX linelength */
217 int err
= 0, do_iconv
;
220 enum conversion convert
;
222 size_t bufsize
, lncnt
, inlen
;
225 /* Either charset-converted temporary file, or plain path */
226 if (ap
->a_conv
== AC_TMPFILE
) {
228 assert(ftell(fi
) == 0);
229 } else if ((fi
= Fopen(ap
->a_name
, "r")) == NULL
) {
235 /* MIME part header for attachment */
236 { char const *bn
= ap
->a_name
, *ct
;
238 if ((ct
= strrchr(bn
, '/')) != NULL
)
240 ct
= ap
->a_content_type
;
241 charset
= ap
->a_charset
;
242 convert
= mime_classify_file(fi
, (char const**)&ct
, &charset
, &do_iconv
);
243 if (charset
== NULL
|| ap
->a_conv
== AC_FIX_INCS
||
244 ap
->a_conv
== AC_TMPFILE
)
247 if (fprintf(fo
, "\n--%s\nContent-Type: %s", _sendout_boundary
, ct
) == -1)
250 if (charset
== NULL
) {
251 if (putc('\n', fo
) == EOF
)
253 } else if (fprintf(fo
, "; charset=%s\n", charset
) == -1)
256 if (fprintf(fo
, "Content-Transfer-Encoding: %s\n"
257 "Content-Disposition: %s;\n filename=\"",
258 _get_encoding(convert
), ap
->a_content_disposition
) == -1)
260 if (xmime_write(bn
, strlen(bn
), fo
, CONV_TOHDR
, TD_NONE
, NULL
) < 0)
262 if (fwrite("\"\n", sizeof(char), 2, fo
) != 2 * sizeof(char))
265 if ((bn
= ap
->a_content_id
) != NULL
&&
266 fprintf(fo
, "Content-ID: %s\n", bn
) == -1)
269 if ((bn
= ap
->a_content_description
) != NULL
&&
270 fprintf(fo
, "Content-Description: %s\n", bn
) == -1)
273 if (putc('\n', fo
) == EOF
) {
281 if (iconvd
!= (iconv_t
)-1)
282 n_iconv_close(iconvd
);
284 char const *tcs
= charset_get_lc();
285 if (asccasecmp(charset
, tcs
) &&
286 (iconvd
= n_iconv_open(charset
, tcs
)) == (iconv_t
)-1 &&
287 (err
= errno
) != 0) {
289 fprintf(stderr
, _("Cannot convert from %s to %s\n"), tcs
, charset
);
291 perror("iconv_open");
297 bufsize
= SEND_LINESIZE
;
298 buf
= smalloc(bufsize
);
299 if (convert
== CONV_TOQP
301 || iconvd
!= (iconv_t
)-1
306 if (convert
== CONV_TOQP
308 || iconvd
!= (iconv_t
)-1
311 if (fgetline(&buf
, &bufsize
, &lncnt
, &inlen
, fi
, 0) == NULL
)
313 } else if ((inlen
= fread(buf
, sizeof *buf
, bufsize
, fi
)) == 0)
315 if (xmime_write(buf
, inlen
, fo
, convert
, TD_ICONV
, NULL
) < 0) {
325 if (ap
->a_conv
!= AC_TMPFILE
)
333 _sendbundle_setup_creds(struct sendbundle
*sbp
, bool_t signing_caps
)
335 bool_t v15
, rv
= FAL0
;
342 v15
= ok_blook(v15_compat
);
343 shost
= (v15
? ok_vlook(smtp_hostname
) : NULL
);
344 from
= ((signing_caps
|| !v15
|| shost
== NULL
)
345 ? skin(myorigin(sbp
->sb_hp
)) : NULL
);
350 fprintf(stderr
, _("No *from* address for signing specified\n"));
354 sbp
->sb_signer
.l
= strlen(sbp
->sb_signer
.s
= from
);
358 if ((smtp
= ok_vlook(smtp
)) == NULL
) {
363 if (!url_parse(&sbp
->sb_url
, CPROTO_SMTP
, smtp
))
370 sbp
->sb_url
.url_u_h
.l
= strlen(sbp
->sb_url
.url_u_h
.s
= from
);
372 __sendout_ident
= sbp
->sb_url
.url_u_h
.s
;
373 if (!ccred_lookup(&sbp
->sb_ccred
, &sbp
->sb_url
))
376 if (sbp
->sb_url
.url_had_user
|| sbp
->sb_url
.url_pass
.s
!= NULL
) {
377 fprintf(stderr
, "New-style URL used without *v15-compat* being set\n");
380 /* TODO part of the entire myorigin() disaster, get rid of this! */
383 fprintf(stderr
, _("Your configuration requires a *from* address, "
384 "but none was given\n"));
387 if (!ccred_lookup_old(&sbp
->sb_ccred
, CPROTO_SMTP
, from
))
389 sbp
->sb_url
.url_u_h
.l
= strlen(sbp
->sb_url
.url_u_h
.s
= from
);
393 #endif /* HAVE_SMTP */
394 #if defined HAVE_SSL || defined HAVE_SMTP
402 _prepare_mta_args(struct name
*to
, struct header
*hp
)
404 size_t vas_count
, i
, j
;
409 if ((cp
= ok_vlook(sendmail_arguments
)) == NULL
) {
414 vas
= ac_alloc(sizeof(*vas
) * (j
>> 1));
415 vas_count
= (size_t)getrawlist(cp
, j
, vas
, (int)(j
>> 1), TRU1
);
418 i
= 4 + smopts_count
+ vas_count
+ 2 + count(to
) + 1;
419 args
= salloc(i
* sizeof(char*));
421 args
[0] = ok_vlook(sendmail_progname
);
422 if (args
[0] == NULL
|| *args
[0] == '\0')
423 args
[0] = SENDMAIL_PROGNAME
;
429 if (options
& OPT_VERB
)
432 for (j
= 0; j
< smopts_count
; ++j
, ++i
)
435 for (j
= 0; j
< vas_count
; ++j
, ++i
)
438 /* -r option? We may only pass skinned addresses */
439 if (options
& OPT_r_FLAG
) {
440 if (option_r_arg
[0] != '\0')
442 else if (hp
!= NULL
) {
443 /* puthead() did it, then */
444 assert(hp
->h_from
!= NULL
);
445 cp
= hp
->h_from
->n_name
;
447 cp
= skin(myorigin(NULL
)); /* XXX ugh! ugh!! */
448 if (cp
!= NULL
) { /* XXX ugh! */
454 /* Receivers follow */
455 for (; to
!= NULL
; to
= to
->n_flink
)
456 if (!(to
->n_type
& GDEL
))
457 args
[i
++] = to
->n_name
;
467 fixhead(struct header
*hp
, struct name
*tolist
)
469 struct name
**npp
, *np
;
472 tolist
= elide(tolist
);
474 hp
->h_to
= hp
->h_cc
= hp
->h_bcc
= NULL
;
475 for (np
= tolist
; np
!= NULL
; np
= np
->n_flink
) {
476 switch (np
->n_type
& (GDEL
| GMASK
)) {
477 case GTO
: npp
= &hp
->h_to
; break;
478 case GCC
: npp
= &hp
->h_cc
; break;
479 case GBCC
: npp
= &hp
->h_bcc
; break;
482 *npp
= cat(*npp
, ndup(np
, np
->n_type
| GFULL
));
489 put_signature(FILE *fo
, int convert
)
491 char buf
[SEND_LINESIZE
], *sig
, c
= '\n';
497 if ((sig
= ok_vlook(signature
)) == NULL
|| *sig
== '\0') {
503 if ((sig
= file_expand(sig
)) == NULL
)
506 if ((fsig
= Fopen(sig
, "r")) == NULL
) {
510 while ((sz
= fread(buf
, sizeof *buf
, SEND_LINESIZE
, fsig
)) != 0) {
512 if (xmime_write(buf
, sz
, fo
, convert
, TD_NONE
, NULL
) < 0)
532 attach_message(struct attachment
*ap
, FILE *fo
)
539 fprintf(fo
, "\n--%s\nContent-Type: message/rfc822\n"
540 "Content-Disposition: inline\n", _sendout_boundary
);
541 if ((ccp
= ap
->a_content_description
) != NULL
)
542 fprintf(fo
, "Content-Description: %s\n", ccp
);
545 mp
= message
+ ap
->a_msgno
- 1;
547 rv
= (sendmp(mp
, fo
, 0, NULL
, SEND_RFC822
, NULL
) < 0) ? -1 : 0;
553 make_multipart(struct header
*hp
, int convert
, FILE *fi
, FILE *fo
,
554 char const *contenttype
, char const *charset
)
556 struct attachment
*att
;
560 fputs("This is a multi-part message in MIME format.\n", fo
);
561 if (fsize(fi
) != 0) {
563 size_t sz
, bufsize
, cnt
;
565 fprintf(fo
, "\n--%s\n", _sendout_boundary
);
566 fprintf(fo
, "Content-Type: %s", contenttype
);
568 fprintf(fo
, "; charset=%s", charset
);
569 fprintf(fo
, "\nContent-Transfer-Encoding: %s\n"
570 "Content-Disposition: inline\n\n", _get_encoding(convert
));
572 buf
= smalloc(bufsize
= SEND_LINESIZE
);
573 if (convert
== CONV_TOQP
575 || iconvd
!= (iconv_t
)-1
582 if (convert
== CONV_TOQP
584 || iconvd
!= (iconv_t
)-1
587 if (fgetline(&buf
, &bufsize
, &cnt
, &sz
, fi
, 0) == NULL
)
589 } else if ((sz
= fread(buf
, sizeof *buf
, bufsize
, fi
)) == 0)
592 if (xmime_write(buf
, sz
, fo
, convert
, TD_ICONV
, NULL
) < 0) {
602 put_signature(fo
, convert
);
605 for (att
= hp
->h_attach
; att
!= NULL
; att
= att
->a_flink
) {
607 if (attach_message(att
, fo
) != 0)
609 } else if (_attach_file(att
, fo
) != 0)
613 /* the final boundary with two attached dashes */
614 fprintf(fo
, "\n--%s--\n", _sendout_boundary
);
622 infix(struct header
*hp
, FILE *fi
) /* TODO check */
624 FILE *nfo
, *nfi
= NULL
;
626 char const *contenttype
, *charset
= NULL
;
627 enum conversion convert
;
628 int do_iconv
= 0, err
;
630 char const *tcs
, *convhdr
= NULL
;
634 if ((nfo
= Ftmp(&tempMail
, "infix", OF_WRONLY
| OF_HOLDSIGS
| OF_REGISTER
,
636 perror(_("temporary mail file"));
639 if ((nfi
= Fopen(tempMail
, "r")) == NULL
) {
643 Ftmp_release(&tempMail
);
647 contenttype
= "text/plain"; /* XXX mail body - always text/plain, want XX? */
648 convert
= mime_classify_file(fi
, &contenttype
, &charset
, &do_iconv
);
651 tcs
= charset_get_lc();
652 if ((convhdr
= need_hdrconv(hp
, GTO
| GSUBJECT
| GCC
| GBCC
| GIDENT
))) {
653 if (iconvd
!= (iconv_t
)-1) /* XXX */
654 n_iconv_close(iconvd
);
655 if (asccasecmp(convhdr
, tcs
) != 0 &&
656 (iconvd
= n_iconv_open(convhdr
, tcs
)) == (iconv_t
)-1 &&
662 (GTO
| GSUBJECT
| GCC
| GBCC
| GNL
| GCOMMA
| GUA
| GMIME
| GMSGID
|
663 GIDENT
| GREF
| GDATE
), SEND_MBOX
, convert
, contenttype
, charset
))
666 if (iconvd
!= (iconv_t
)-1)
667 n_iconv_close(iconvd
);
671 if (do_iconv
&& charset
!= NULL
) { /*TODO charset->mime_classify_file*/
672 if (asccasecmp(charset
, tcs
) != 0 &&
673 (iconvd
= n_iconv_open(charset
, tcs
)) == (iconv_t
)-1 &&
674 (err
= errno
) != 0) {
677 fprintf(stderr
, _("Cannot convert from %s to %s\n"), tcs
, charset
);
679 perror("iconv_open");
685 if (hp
->h_attach
!= NULL
) {
686 if (make_multipart(hp
, convert
, fi
, nfo
, contenttype
, charset
) != 0)
689 size_t sz
, bufsize
, cnt
;
692 if (convert
== CONV_TOQP
694 || iconvd
!= (iconv_t
)-1
700 buf
= smalloc(bufsize
= SEND_LINESIZE
);
702 if (convert
== CONV_TOQP
704 || iconvd
!= (iconv_t
)-1
707 if (fgetline(&buf
, &bufsize
, &cnt
, &sz
, fi
, 0) == NULL
)
709 } else if ((sz
= fread(buf
, sizeof *buf
, bufsize
, fi
)) == 0)
711 if (xmime_write(buf
, sz
, nfo
, convert
, TD_ICONV
, NULL
) < 0) {
718 if (err
|| ferror(fi
)) {
723 if (iconvd
!= (iconv_t
)-1)
724 n_iconv_close(iconvd
);
730 put_signature(nfo
, convert
); /* XXX if (text/) !! */
734 if (iconvd
!= (iconv_t
)-1)
735 n_iconv_close(iconvd
);
739 if ((err
= ferror(nfo
)))
740 perror(_("temporary mail file"));
755 _check_dispo_notif(struct name
*mdn
, struct header
*hp
, FILE *fo
)
761 /* TODO smtp_disposition_notification (RFC 3798): relation to return-path
762 * TODO not yet checked */
763 if (!ok_blook(disposition_notification_send
))
766 if (mdn
!= NULL
&& mdn
!= (struct name
*)0x1)
768 else if ((from
= myorigin(hp
)) == NULL
) {
769 if (options
& OPT_D_V
)
770 fprintf(stderr
, _("*disposition-notification-send*: no *from* set\n"));
774 if (fmt("Disposition-Notification-To:", nalloc(UNCONST(from
), 0), fo
,
783 savemail(char const *name
, FILE *fi
)
787 size_t bufsize
, buflen
, cnt
;
788 int prependnl
= 0, rv
= -1;
791 buf
= smalloc(bufsize
= LINESIZE
);
793 if ((fo
= Zopen(name
, "a+", NULL
)) == NULL
) {
794 if ((fo
= Zopen(name
, "wx", NULL
)) == NULL
) {
799 if (fseek(fo
, -2L, SEEK_END
) == 0) {
800 switch (fread(buf
, sizeof *buf
, 2, fo
)) {
802 if (buf
[1] != '\n') {
824 fprintf(fo
, "From %s %s", myname
, time_current
.tc_ctime
);
828 while (fgetline(&buf
, &bufsize
, &cnt
, &buflen
, fi
, 0) != NULL
) {
829 #ifdef HAVE_DEBUG /* TODO assert legacy */
830 assert(!is_head(buf
, buflen
));
832 if (is_head(buf
, buflen
))
835 fwrite(buf
, sizeof *buf
, buflen
, fo
);
837 if (buflen
&& *(buf
+ buflen
- 1) != '\n')
857 sendmail_internal(void *v
, int recipient_record
)
864 memset(&head
, 0, sizeof head
);
865 head
.h_to
= lextract(str
, GTO
| GFULL
);
866 rv
= mail1(&head
, 0, NULL
, NULL
, recipient_record
, 0);
872 _transfer(struct sendbundle
*sbp
)
879 for (cnt
= 0, np
= sbp
->sb_to
; np
!= NULL
;) {
880 char const k
[] = "smime-encrypt-";
881 size_t nl
= strlen(np
->n_name
);
882 char *cp
, *vs
= ac_alloc(sizeof(k
)-1 + nl
+1);
883 memcpy(vs
, k
, sizeof(k
) -1);
884 memcpy(vs
+ sizeof(k
) -1, np
->n_name
, nl
+1);
886 if ((cp
= vok_vlook(vs
)) != NULL
) {
890 if ((ef
= smime_encrypt(sbp
->sb_input
, cp
, np
->n_name
)) != NULL
) {
891 FILE *fisave
= sbp
->sb_input
;
892 struct name
*nsave
= sbp
->sb_to
;
894 sbp
->sb_to
= ndup(np
, np
->n_type
& ~(GFULL
| GSKIN
));
899 sbp
->sb_input
= fisave
;
904 fprintf(stderr
, _("No SSL support compiled in.\n"));
907 fprintf(stderr
, _("Message not sent to <%s>\n"), np
->n_name
);
908 _sendout_error
= TRU1
;
912 rewind(sbp
->sb_input
);
914 if (np
->n_flink
!= NULL
)
915 np
->n_flink
->n_blink
= np
->n_blink
;
916 if (np
->n_blink
!= NULL
)
917 np
->n_blink
->n_flink
= np
->n_flink
;
918 if (np
== sbp
->sb_to
)
919 sbp
->sb_to
= np
->n_flink
;
928 if (cnt
> 0 && (ok_blook(smime_force_encryption
) || !start_mta(sbp
)))
935 start_mta(struct sendbundle
*sbp
)
937 char const **args
= NULL
, **t
, *mta
;
944 if ((smtp
= ok_vlook(smtp
)) == NULL
) {
945 if ((mta
= ok_vlook(sendmail
)) != NULL
) {
946 if ((mta
= file_expand(mta
)) == NULL
)
951 args
= _prepare_mta_args(sbp
->sb_to
, sbp
->sb_hp
);
952 if (options
& OPT_DEBUG
) {
953 printf(_("Sendmail arguments:"));
954 for (t
= args
; *t
!= NULL
; ++t
)
955 printf(" \"%s\"", *t
);
961 mta
= NULL
; /* Silence cc */
963 fputs(_("No SMTP support compiled in.\n"), stderr
);
966 /* XXX assert that sendbundle is setup? */
970 /* Fork, set up the temporary mail file as standard input for "mail", and
971 * exec with the user list we generated far above */
972 if ((pid
= fork()) == -1) {
975 savedeadletter(sbp
->sb_input
, 0);
976 _sendout_error
= TRU1
;
981 sigaddset(&nset
, SIGHUP
);
982 sigaddset(&nset
, SIGINT
);
983 sigaddset(&nset
, SIGQUIT
);
984 sigaddset(&nset
, SIGTSTP
);
985 sigaddset(&nset
, SIGTTIN
);
986 sigaddset(&nset
, SIGTTOU
);
987 freopen("/dev/null", "r", stdin
);
990 prepare_child(&nset
, 0, 1);
995 prepare_child(&nset
, fileno(sbp
->sb_input
), -1);
996 /* If *record* is set then savemail() will move the file position;
997 * it'll call rewind(), but that may optimize away the systemcall if
998 * possible, and since dup2() shares the position with the original FD
999 * the MTA may end up reading nothing */
1000 lseek(0, 0, SEEK_SET
);
1001 execv(mta
, UNCONST(args
));
1006 savedeadletter(sbp
->sb_input
, 1);
1007 fputs(_("... message not sent.\n"), stderr
);
1010 if ((options
& (OPT_DEBUG
| OPT_VERB
| OPT_BATCH_FLAG
)) ||
1011 ok_blook(sendwait
)) {
1012 if (wait_child(pid
, NULL
))
1015 _sendout_error
= TRU1
;
1026 mightrecord(FILE *fp
, struct name
*to
)
1034 cp
= savestr(skinned_name(to
));
1035 for (cq
= cp
; *cq
!= '\0' && *cq
!= '@'; ++cq
)
1039 cp
= ok_vlook(record
);
1042 if ((ep
= expand(cp
)) == NULL
) {
1047 if (*ep
!= '/' && *ep
!= '+' && ok_blook(outfolder
) &&
1048 which_protocol(ep
) == PROTO_FILE
) {
1049 size_t i
= strlen(cp
);
1050 cq
= salloc(i
+ 1 +1);
1052 memcpy(cq
+ 1, cp
, i
+1);
1054 if ((ep
= file_expand(cp
)) == NULL
) {
1060 if (savemail(ep
, fp
) != 0) {
1062 fprintf(stderr
, _("Failed to save message in %s - message not sent\n"),
1064 exit_status
|= EXIT_ERR
;
1065 savedeadletter(fp
, 1);
1074 _message_id(struct header
*hp
)
1082 if (ok_blook(message_id_disable
))
1085 if ((h
= __sendout_ident
) != NULL
)
1087 else if ((h
= ok_vlook(hostname
)) != NULL
)
1089 else if ((h
= skin(myorigin(hp
))) != NULL
&& strchr(h
, '@') != NULL
)
1095 tmp
= &time_current
.tc_gm
;
1096 i
= sizeof("Message-ID: <%04d%02d%02d%02d%02d%02d.%s%c%s>") -1 +
1099 snprintf(rv
, i
, "Message-ID: <%04d%02d%02d%02d%02d%02d.%s%c%s>",
1100 tmp
->tm_year
+ 1900, tmp
->tm_mon
+ 1, tmp
->tm_mday
,
1101 tmp
->tm_hour
, tmp
->tm_min
, tmp
->tm_sec
,
1102 getrandstring(rl
), (rl
== 8 ? '%' : '@'), h
);
1103 rv
[i
] = '\0'; /* Because we don't test snprintf(3) return */
1105 __sendout_ident
= NULL
;
1111 fmt(char const *str
, struct name
*np
, FILE *fo
, int flags
, int dropinvalid
,
1119 } m
= (flags
& GCOMMA
) ? m_COMMA
: 0;
1126 fwrite(str
, sizeof *str
, col
, fo
);
1129 if (col
== 9 && !asccasecmp(str
, "reply-to:")) {
1133 if (ok_blook(add_file_recipients
))
1135 if ((col
== 3 && (!asccasecmp(str
, "to:") || !asccasecmp(str
, "cc:"))) ||
1136 (col
== 4 && !asccasecmp(str
, "bcc:")) ||
1137 (col
== 10 && !asccasecmp(str
, "Resent-To:")))
1141 for (; np
!= NULL
; np
= np
->n_flink
) {
1142 if ((m
& m_NOPF
) && is_fileorpipe_addr(np
))
1144 if (is_addr_invalid(np
, !dropinvalid
)) {
1150 if ((m
& (m_INIT
| m_COMMA
)) == (m_INIT
| m_COMMA
)) {
1155 len
= strlen(np
->n_fullname
);
1156 ++col
; /* The separating space */
1157 if ((m
& m_INIT
) && /*col > 1 &&*/ UICMP(z
, col
+ len
, >, 72)) {
1163 m
= (m
& ~m_CSEEN
) | m_INIT
;
1164 len
= xmime_write(np
->n_fullname
, len
, fo
,
1165 (domime
? CONV_TOHDR_A
: CONV_NONE
), TD_ICONV
, NULL
);
1178 infix_resend(FILE *fi
, FILE *fo
, struct message
*mp
, struct name
*to
,
1181 size_t cnt
, c
, bufsize
= 0;
1184 struct name
*fromfield
= NULL
, *senderfield
= NULL
, *mdn
;
1190 /* Write the Resent-Fields */
1192 fputs("Resent-", fo
);
1194 if ((cp
= myaddrs(NULL
)) != NULL
) {
1195 if (_putname(cp
, GCOMMA
, SEND_MBOX
, NULL
, "Resent-From:", fo
,
1199 /* TODO RFC 5322: Resent-Sender SHOULD NOT be used if it's EQ -From: */
1200 if ((cp
= ok_vlook(sender
)) != NULL
) {
1201 if (_putname(cp
, GCOMMA
, SEND_MBOX
, NULL
, "Resent-Sender:", fo
,
1205 if (fmt("Resent-To:", to
, fo
, 1, 1, 0))
1207 if (((cp
= ok_vlook(stealthmua
)) == NULL
|| !strcmp(cp
, "noagent")) &&
1208 (cp
= _message_id(NULL
)) != NULL
)
1209 fprintf(fo
, "Resent-%s\n", cp
);
1212 if ((mdn
= UNCONST(check_from_and_sender(fromfield
, senderfield
))) == NULL
)
1214 if (!_check_dispo_notif(mdn
, NULL
, fo
))
1217 /* Write the original headers */
1219 if (fgetline(&buf
, &bufsize
, &cnt
, &c
, fi
, 0) == NULL
)
1221 /* XXX more checks: The From_ line may be seen when resending */
1222 /* During headers is_head() is actually overkill, so ^From_ is sufficient
1223 * && !is_head(buf, c) */
1224 if (ascncasecmp("status:", buf
, 7) && strncmp("From ", buf
, 5) &&
1225 ascncasecmp("disposition-notification-to:", buf
, 28))
1226 fwrite(buf
, sizeof *buf
, c
, fo
);
1227 if (cnt
> 0 && *buf
== '\n')
1231 /* Write the message body */
1233 if (fgetline(&buf
, &bufsize
, &cnt
, &c
, fi
, 0) == NULL
)
1235 if (cnt
== 0 && *buf
== '\n')
1237 fwrite(buf
, sizeof *buf
, c
, fo
);
1242 perror(_("temporary mail file"));
1252 mail(struct name
*to
, struct name
*cc
, struct name
*bcc
, char *subject
,
1253 struct attachment
*attach
, char *quotefile
, int recipient_record
)
1259 memset(&head
, 0, sizeof head
);
1261 /* The given subject may be in RFC1522 format. */
1262 if (subject
!= NULL
) {
1264 in
.l
= strlen(subject
);
1265 mime_fromhdr(&in
, &out
, /* TODO ??? TD_ISPR |*/ TD_ICONV
);
1266 head
.h_subject
= out
.s
;
1268 if (!(options
& OPT_t_FLAG
)) {
1273 head
.h_attach
= attach
;
1275 mail1(&head
, 0, NULL
, quotefile
, recipient_record
, 0);
1277 if (subject
!= NULL
)
1289 rv
= sendmail_internal(v
, 0);
1300 rv
= sendmail_internal(v
, 1);
1306 mail1(struct header
*hp
, int printheaders
, struct message
*quote
,
1307 char *quotefile
, int recipient_record
, int doprefix
)
1309 struct sendbundle sb
;
1312 int dosign
= -1, err
;
1314 enum okay rv
= STOP
;
1317 _sendout_error
= FAL0
;
1319 /* Update some globals we likely need first */
1320 time_current_update(&time_current
, TRU1
);
1323 if ((cp
= ok_vlook(autocc
)) != NULL
&& *cp
!= '\0')
1324 hp
->h_cc
= cat(hp
->h_cc
, checkaddrs(lextract(cp
, GCC
| GFULL
)));
1325 if ((cp
= ok_vlook(autobcc
)) != NULL
&& *cp
!= '\0')
1326 hp
->h_bcc
= cat(hp
->h_bcc
, checkaddrs(lextract(cp
, GBCC
| GFULL
)));
1328 /* Collect user's mail from standard input. Get the result as mtf */
1329 mtf
= collect(hp
, printheaders
, quote
, quotefile
, doprefix
);
1333 if (options
& OPT_INTERACTIVE
) {
1334 err
= (ok_blook(bsdcompat
) || ok_blook(askatend
));
1337 if (ok_blook(askcc
))
1338 ++err
, grab_headers(hp
, GCC
, 1);
1339 if (ok_blook(askbcc
))
1340 ++err
, grab_headers(hp
, GBCC
, 1);
1341 if (ok_blook(askattach
))
1342 ++err
, edit_attachments(&hp
->h_attach
);
1343 if (ok_blook(asksign
))
1344 ++err
, dosign
= getapproval(_("Sign this message (y/n)? "), TRU1
);
1352 if (fsize(mtf
) == 0) {
1353 if (options
& OPT_E_FLAG
)
1355 if (hp
->h_subject
== NULL
)
1356 printf(_("No message, no subject; hope that's ok\n"));
1357 else if (ok_blook(bsdcompat
) || ok_blook(bsdmsgs
))
1358 printf(_("Null message body; hope that's ok\n"));
1362 dosign
= ok_blook(smime_sign
);
1365 fprintf(stderr
, _("No SSL support compiled in.\n"));
1370 /* XXX Update time_current again; once collect() offers editing of more
1371 * XXX headers, including Date:, this must only happen if Date: is the
1372 * XXX same that it was before collect() (e.g., postponing etc.).
1373 * XXX But *do* update otherwise because the mail seems to be backdated
1374 * XXX if the user edited some time, which looks odd and it happened
1375 * XXX to me that i got mis-dated response mails due to that... */
1376 time_current_update(&time_current
, TRU1
);
1378 /* TODO hrmpf; the MIME/send layer rewrite MUST address the init crap:
1379 * TODO setup the header ONCE; note this affects edit.c, collect.c ...,
1380 * TODO but: offer a hook that rebuilds/expands/checks/fixates all
1381 * TODO header fields ONCE, call that ONCE after user editing etc. has
1382 * TODO completed (one edit cycle) */
1384 /* Take the user names from the combined to and cc lists and do all the
1385 * alias processing. The POSIX standard says:
1386 * The names shall be substituted when alias is used as a recipient
1387 * address specified by the user in an outgoing message (that is,
1388 * other recipients addressed indirectly through the reply command
1389 * shall not be substituted in this manner).
1390 * S-nail thus violates POSIX, as has been pointed out correctly by
1391 * Martin Neitzel, but logic and usability of POSIX standards is not seldom
1392 * disputable anyway. Go for user friendliness */
1394 /* Do alias expansion on Reply-To: members, too */
1395 /* TODO puthead() YET (!!! see ONCE note above) expands the value, but
1396 * TODO doesn't perform alias expansion; encapsulate in the ONCE-o */
1397 if (hp
->h_replyto
== NULL
&& (cp
= ok_vlook(replyto
)) != NULL
)
1398 hp
->h_replyto
= checkaddrs(lextract(cp
, GEXTRA
| GFULL
));
1399 if (hp
->h_replyto
!= NULL
)
1400 hp
->h_replyto
= elide(usermap(hp
->h_replyto
, TRU1
));
1402 /* TODO what happens now is that all recipients are merged into
1403 * TODO a duplicated list with expanded aliases, then this list is
1404 * TODO splitted again into the three individual recipient lists (with
1405 * TODO duplicates removed).
1406 * TODO later on we use the merged list for outof() pipe/file saving,
1407 * TODO then we eliminate duplicates (again) and then we use that one
1408 * TODO for mightrecord() and _transfer(), and again. ... Please ... */
1410 /* NOTE: Due to elide() in fixhead(), ENSURE to,cc,bcc order of to!,
1411 * because otherwise the recipients will be "degraded" if they occur
1413 to
= usermap(cat(hp
->h_to
, cat(hp
->h_cc
, hp
->h_bcc
)), FAL0
);
1415 fprintf(stderr
, _("No recipients specified\n"));
1416 _sendout_error
= TRU1
;
1418 to
= fixhead(hp
, to
);
1421 memset(&sb
, 0, sizeof sb
);
1425 if ((dosign
|| count_nonlocal(to
) > 0) &&
1426 !_sendbundle_setup_creds(&sb
, (dosign
> 0)))
1427 /* TODO saving $DEAD and recovering etc is not yet well defined */
1430 /* 'Bit ugly kind of control flow until we find a charset that does it */
1431 for (charset_iter_reset(hp
->h_charset
);; charset_iter_next()) {
1432 if (!charset_iter_is_valid())
1434 else if ((nmtf
= infix(hp
, mtf
)) != NULL
)
1436 else if ((err
= errno
) == EILSEQ
|| err
== EINVAL
) {
1443 _sendout_error
= TRU1
;
1444 savedeadletter(mtf
, TRU1
);
1445 fputs(_("... message not sent.\n"), stderr
);
1453 if ((nmtf
= smime_sign(mtf
, sb
.sb_signer
.s
)) == NULL
)
1460 /* TODO truly - i still don't get what follows: (1) we deliver file
1461 * TODO and pipe addressees, (2) we mightrecord() and (3) we transfer
1462 * TODO even if (1) savedeadletter() etc. To me this doesn't make sense? */
1464 /* Deliver pipe and file addressees */
1465 to
= outof(to
, mtf
, &_sendout_error
);
1467 savedeadletter(mtf
, FAL0
);
1469 to
= elide(to
); /* XXX needed only to drop GDELs due to outof()! */
1470 { ui32_t cnt
= count(to
);
1471 if ((!recipient_record
|| cnt
> 0) &&
1472 !mightrecord(mtf
, (recipient_record
? to
: NULL
)))
1480 } else if (!_sendout_error
)
1487 exit_status
|= EXIT_SEND_ERROR
;
1493 mkdate(FILE *fo
, char const *field
)
1496 int tzdiff
, tzdiff_hour
, tzdiff_min
, rv
;
1499 tzdiff
= time_current
.tc_time
- mktime(&time_current
.tc_gm
);
1500 tzdiff_hour
= (int)(tzdiff
/ 60);
1501 tzdiff_min
= tzdiff_hour
% 60;
1503 tmptr
= &time_current
.tc_local
;
1504 if (tmptr
->tm_isdst
> 0)
1506 rv
= fprintf(fo
, "%s: %s, %02d %s %04d %02d:%02d:%02d %+05d\n",
1508 weekday_names
[tmptr
->tm_wday
],
1509 tmptr
->tm_mday
, month_names
[tmptr
->tm_mon
],
1510 tmptr
->tm_year
+ 1900, tmptr
->tm_hour
,
1511 tmptr
->tm_min
, tmptr
->tm_sec
,
1512 tzdiff_hour
* 100 + tzdiff_min
);
1518 puthead(struct header
*hp
, FILE *fo
, enum gfield w
, enum sendaction action
,
1519 enum conversion convert
, char const *contenttype
, char const *charset
)
1521 #define FMT_CC_AND_BCC() \
1523 if (hp->h_cc != NULL && (w & GCC)) {\
1524 if (fmt("Cc:", hp->h_cc, fo, (w & (GCOMMA | GFILES)), 0,\
1525 (action != SEND_TODISP)))\
1529 if (hp->h_bcc != NULL && (w & GBCC)) {\
1530 if (fmt("Bcc:", hp->h_bcc, fo, (w & (GCOMMA | GFILES)), 0,\
1531 (action != SEND_TODISP)))\
1539 struct name
*np
, *fromfield
= NULL
, *senderfield
= NULL
;
1540 int stealthmua
, rv
= 1;
1544 if ((addr
= ok_vlook(stealthmua
)) != NULL
)
1545 stealthmua
= !strcmp(addr
, "noagent") ? -1 : 1;
1549 nodisp
= (action
!= SEND_TODISP
);
1552 mkdate(fo
, "Date"), ++gotcha
;
1554 if (hp
->h_from
!= NULL
) {
1555 if (fmt("From:", hp
->h_from
, fo
, (w
& (GCOMMA
| GFILES
)), 0, nodisp
))
1558 fromfield
= hp
->h_from
;
1559 } else if ((addr
= myaddrs(hp
)) != NULL
) {
1560 if (_putname(addr
, w
, action
, &gotcha
, "From:", fo
, &fromfield
))
1562 hp
->h_from
= fromfield
;
1565 if (((addr
= hp
->h_organization
) != NULL
||
1566 (addr
= ok_vlook(ORGANIZATION
)) != NULL
) &&
1567 (l
= strlen(addr
)) > 0) {
1568 fwrite("Organization: ", sizeof(char), 14, fo
);
1569 if (xmime_write(addr
, l
, fo
, (!nodisp
? CONV_NONE
: CONV_TOHDR
),
1570 (!nodisp
? TD_ISPR
| TD_ICONV
: TD_ICONV
), NULL
) < 0)
1576 /* TODO see the ONCE TODO note somewhere around this file;
1577 * TODO but anyway, do NOT perform alias expansion UNLESS
1578 * TODO we are actually sending out! */
1579 if (hp
->h_replyto
!= NULL
) {
1580 if (fmt("Reply-To:", hp
->h_replyto
, fo
, w
& GCOMMA
, 0, nodisp
))
1583 } else if ((addr
= ok_vlook(replyto
)) != NULL
)
1584 if (_putname(addr
, w
, action
, &gotcha
, "Reply-To:", fo
, NULL
))
1587 if (hp
->h_sender
!= NULL
) {
1588 if (fmt("Sender:", hp
->h_sender
, fo
, w
& GCOMMA
, 0, nodisp
))
1591 senderfield
= hp
->h_sender
;
1592 } else if ((addr
= ok_vlook(sender
)) != NULL
)
1593 if (_putname(addr
, w
, action
, &gotcha
, "Sender:", fo
, &senderfield
))
1596 if ((np
= UNCONST(check_from_and_sender(fromfield
, senderfield
))) == NULL
)
1598 if (!_check_dispo_notif(np
, hp
, fo
))
1602 if (hp
->h_to
!= NULL
&& w
& GTO
) {
1603 if (fmt("To:", hp
->h_to
, fo
, (w
& (GCOMMA
| GFILES
)), 0, nodisp
))
1608 if (!ok_blook(bsdcompat
) && !ok_blook(bsdorder
))
1611 if (hp
->h_subject
!= NULL
&& (w
& GSUBJECT
)) {
1612 char *sub
= subject_re_trim(hp
->h_subject
);
1613 size_t sublen
= strlen(sub
);
1615 fwrite("Subject: ", sizeof(char), 9, fo
);
1616 if (sub
!= hp
->h_subject
) {
1617 fwrite("Re: ", sizeof(char), 4, fo
); /* RFC mandates english "Re: " */
1619 xmime_write(sub
, sublen
, fo
, (!nodisp
? CONV_NONE
: CONV_TOHDR
),
1620 (!nodisp
? TD_ISPR
| TD_ICONV
: TD_ICONV
), NULL
) < 0)
1622 } else if (*sub
!= '\0') {
1623 if (xmime_write(sub
, sublen
, fo
, (!nodisp
? CONV_NONE
: CONV_TOHDR
),
1624 (!nodisp
? TD_ISPR
| TD_ICONV
: TD_ICONV
), NULL
) < 0)
1631 if (ok_blook(bsdcompat
) || ok_blook(bsdorder
))
1634 if ((w
& GMSGID
) && stealthmua
<= 0 && (addr
= _message_id(hp
)) != NULL
) {
1640 if ((np
= hp
->h_ref
) != NULL
&& (w
& GREF
)) {
1641 fmt("References:", np
, fo
, 0, 1, 0);
1642 if (np
->n_name
!= NULL
) {
1643 while (np
->n_flink
!= NULL
)
1645 if (!is_addr_invalid(np
, 0)) {
1646 fprintf(fo
, "In-Reply-To: %s\n", np
->n_name
);
1652 if ((w
& GUA
) && stealthmua
== 0)
1653 fprintf(fo
, "User-Agent: %s %s\n", uagent
, version
), ++gotcha
;
1656 fputs("MIME-Version: 1.0\n", fo
), ++gotcha
;
1657 if (hp
->h_attach
!= NULL
) {
1658 _sendout_boundary
= mime_create_boundary();/*TODO carrier*/
1659 fprintf(fo
, "Content-Type: multipart/mixed;\n boundary=\"%s\"\n",
1662 fprintf(fo
, "Content-Type: %s", contenttype
);
1663 if (charset
!= NULL
)
1664 fprintf(fo
, "; charset=%s", charset
);
1665 fprintf(fo
, "\nContent-Transfer-Encoding: %s\n",
1666 _get_encoding(convert
));
1670 if (gotcha
&& (w
& GNL
))
1676 #undef FMT_CC_AND_BCC
1680 resend_msg(struct message
*mp
, struct name
*to
, int add_resent
) /* TODO check */
1682 struct sendbundle sb
;
1683 FILE *ibuf
, *nfo
, *nfi
;
1685 enum okay rv
= STOP
;
1688 _sendout_error
= FAL0
;
1690 /* Update some globals we likely need first */
1691 time_current_update(&time_current
, TRU1
);
1693 if ((to
= checkaddrs(to
)) == NULL
) {
1694 _sendout_error
= TRU1
;
1698 if ((nfo
= Ftmp(&tempMail
, "resend", OF_WRONLY
| OF_HOLDSIGS
| OF_REGISTER
,
1700 _sendout_error
= TRU1
;
1701 perror(_("temporary mail file"));
1704 if ((nfi
= Fopen(tempMail
, "r")) == NULL
) {
1705 _sendout_error
= TRU1
;
1708 Ftmp_release(&tempMail
);
1712 if ((ibuf
= setinput(&mb
, mp
, NEED_BODY
)) == NULL
)
1715 memset(&sb
, 0, sizeof sb
);
1718 if (count_nonlocal(to
) > 0 && !_sendbundle_setup_creds(&sb
, FAL0
))
1719 /* TODO saving $DEAD and recovering etc is not yet well defined */
1722 if (infix_resend(ibuf
, nfo
, mp
, to
, add_resent
) != 0) {
1723 savedeadletter(nfi
, TRU1
);
1724 fputs(_("... message not sent.\n"), stderr
);
1729 _sendout_error
= TRU1
;
1735 to
= outof(to
, nfi
, &_sendout_error
);
1737 savedeadletter(nfi
, FAL0
);
1739 to
= elide(to
); /* TODO should have been done in fixhead()? */
1740 if (count(to
) != 0) {
1741 if (!ok_blook(record_resent
) || mightrecord(nfi
, to
)) {
1743 /*sb.sb_input = nfi;*/
1747 } else if (!_sendout_error
)
1753 exit_status
|= EXIT_SEND_ERROR
;
1758 #undef SEND_LINESIZE