2 * Heirloom mailx - a mail user agent derived from Berkeley Mail.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 Steffen Daode Nurpmeso.
9 * Copyright (c) 1980, 1993
10 * The Regents of the University of California. All rights reserved.
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
15 * 1. Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
20 * 3. All advertising materials mentioning features or use of this software
21 * must display the following acknowledgement:
22 * This product includes software developed by the University of
23 * California, Berkeley and its contributors.
24 * 4. Neither the name of the University nor the names of its contributors
25 * may be used to endorse or promote products derived from this software
26 * without specific prior written permission.
28 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
29 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
30 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
31 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
32 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
33 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
34 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
35 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
36 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
37 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
43 static char sccsid
[] = "@(#)sendout.c 2.100 (gritter) 3/1/09";
57 * Mail -- a mail program
62 static char *send_boundary
;
64 static char *getencoding(enum conversion convert
);
65 static struct name
*fixhead(struct header
*hp
, struct name
*tolist
);
66 static int put_signature(FILE *fo
, int convert
);
67 static int attach_file1(struct attachment
*ap
, FILE *fo
, int dosign
);
68 static int attach_file(struct attachment
*ap
, FILE *fo
, int dosign
);
69 static int attach_message(struct attachment
*ap
, FILE *fo
, int dosign
);
70 static int make_multipart(struct header
*hp
, int convert
, FILE *fi
, FILE *fo
,
71 const char *contenttype
, const char *charset
, int dosign
);
72 static FILE *infix(struct header
*hp
, FILE *fi
, int dosign
);
73 static int savemail(char *name
, FILE *fi
);
74 static int sendmail_internal(void *v
, int recipient_record
);
75 static enum okay
transfer(struct name
*to
, struct name
*mailargs
, FILE *input
,
77 static enum okay
start_mta(struct name
*to
, struct name
*mailargs
, FILE *input
,
79 static void message_id(FILE *fo
, struct header
*hp
);
80 static int fmt(char *str
, struct name
*np
, FILE *fo
, int comma
,
81 int dropinvalid
, int domime
);
82 static int infix_resend(FILE *fi
, FILE *fo
, struct message
*mp
,
83 struct name
*to
, int add_resent
);
86 * Generate a boundary for MIME multipart messages.
91 static char bound
[70];
95 snprintf(bound
, sizeof bound
, "=_%lx.%s", (long)now
, getrandstring(48));
96 send_boundary
= bound
;
101 * Get an encoding flag based on the given string.
104 getencoding(enum conversion convert
)
112 return "quoted-printable";
123 * Fix the header by glopping all of the expanded names from
124 * the distribution list into the appropriate fields.
127 fixhead(struct header
*hp
, struct name
*tolist
)
134 for (np
= tolist
; np
!= NULL
; np
= np
->n_flink
)
135 if ((np
->n_type
& GMASK
) == GTO
)
137 cat(hp
->h_to
, nalloc(np
->n_fullname
,
139 else if ((np
->n_type
& GMASK
) == GCC
)
141 cat(hp
->h_cc
, nalloc(np
->n_fullname
,
143 else if ((np
->n_type
& GMASK
) == GBCC
)
145 cat(hp
->h_bcc
, nalloc(np
->n_fullname
,
152 * Do not change, you get incorrect base64 encodings else!
154 #define INFIX_BUF 972
157 * Put the signature file at fo.
160 put_signature(FILE *fo
, int convert
)
162 char *sig
, buf
[INFIX_BUF
], c
= '\n';
166 sig
= value("signature");
167 if (sig
== NULL
|| *sig
== '\0')
171 if ((fsig
= Fopen(sig
, "r")) == NULL
) {
175 while ((sz
= fread(buf
, sizeof *buf
, INFIX_BUF
, fsig
)) != 0) {
177 if (mime_write(buf
, sz
, fo
, convert
, TD_NONE
,
178 NULL
, (size_t)0, NULL
, NULL
)
197 * Write an attachment to the file buffer, converting to MIME.
200 attach_file1(struct attachment
*ap
, FILE *fo
, int dosign
)
203 char *charset
= NULL
, *contenttype
= NULL
, *basename
;
204 enum conversion convert
= CONV_TOB64
;
206 enum mimeclean isclean
;
209 size_t bufsize
, count
;
215 if ((fi
= Fopen(ap
->a_name
, "r")) == NULL
) {
219 if ((basename
= strrchr(ap
->a_name
, '/')) == NULL
)
220 basename
= ap
->a_name
;
223 if (ap
->a_content_type
)
224 contenttype
= ap
->a_content_type
;
226 contenttype
= mime_filecontent(basename
);
228 charset
= ap
->a_charset
;
229 convert
= get_mime_convert(fi
, &contenttype
, &charset
, &isclean
,
234 send_boundary
, contenttype
);
238 fprintf(fo
, ";\n charset=%s\n", charset
);
239 if (ap
->a_content_disposition
== NULL
)
240 ap
->a_content_disposition
= "attachment";
241 fprintf(fo
, "Content-Transfer-Encoding: %s\n"
242 "Content-Disposition: %s;\n"
244 getencoding(convert
),
245 ap
->a_content_disposition
);
246 mime_write(basename
, strlen(basename
), fo
,
247 CONV_TOHDR
, TD_NONE
, NULL
, (size_t)0, NULL
, NULL
);
248 fwrite("\"\n", sizeof (char), 2, fo
);
249 if (ap
->a_content_id
)
250 fprintf(fo
, "Content-ID: %s\n", ap
->a_content_id
);
251 if (ap
->a_content_description
)
252 fprintf(fo
, "Content-Description: %s\n",
253 ap
->a_content_description
);
256 if (iconvd
!= (iconv_t
)-1) {
258 iconvd
= (iconv_t
)-1;
261 if ((isclean
& (MIME_HASNUL
|MIME_CTRLCHAR
)) == 0 &&
262 ascncasecmp(contenttype
, "text/", 5) == 0 &&
263 isclean
& MIME_HIGHBIT
&&
265 if ((iconvd
= iconv_open_ft(charset
, tcs
)) == (iconv_t
)-1 &&
268 fprintf(stderr
, catgets(catd
, CATSET
, 179,
269 "Cannot convert from %s to %s\n"), tcs
, charset
);
271 perror("iconv_open");
276 #endif /* HAVE_ICONV */
277 buf
= smalloc(bufsize
= INFIX_BUF
);
278 if (convert
== CONV_TOQP
280 || iconvd
!= (iconv_t
)-1
287 if (convert
== CONV_TOQP
289 || iconvd
!= (iconv_t
)-1
292 if (fgetline(&buf
, &bufsize
, &count
, &sz
, fi
, 0)
296 if ((sz
= fread(buf
, sizeof *buf
, bufsize
, fi
)) == 0)
300 if (mime_write(buf
, sz
, fo
, convert
, TD_ICONV
,
301 NULL
, (size_t)0, NULL
, NULL
) == 0)
304 if (convert
== CONV_TOQP
&& lastc
!= '\n')
305 fwrite("=\n", 1, 2, fo
);
314 * Try out different character set conversions to attach a file.
317 attach_file(struct attachment
*ap
, FILE *fo
, int dosign
)
319 char *_wantcharset
, *charsets
, *ncs
;
320 size_t offs
= ftell(fo
);
322 if (ap
->a_charset
|| (charsets
= value("sendcharsets")) == NULL
)
323 return attach_file1(ap
, fo
, dosign
);
324 _wantcharset
= wantcharset
;
325 wantcharset
= savestr(charsets
);
326 loop
: if ((ncs
= strchr(wantcharset
, ',')) != NULL
)
328 try: if (attach_file1(ap
, fo
, dosign
) != 0) {
329 if (errno
== EILSEQ
|| errno
== EINVAL
) {
333 fseek(fo
, offs
, SEEK_SET
);
337 if (wantcharset
== (char *)-1)
340 wantcharset
= (char *)-1;
342 fseek(fo
, offs
, SEEK_SET
);
348 wantcharset
= _wantcharset
;
353 * Attach a message to the file buffer.
356 attach_message(struct attachment
*ap
, FILE *fo
, int dosign
)
360 fprintf(fo
, "\n--%s\n"
361 "Content-Type: message/rfc822\n"
362 "Content-Disposition: inline\n\n", send_boundary
);
363 mp
= &message
[ap
->a_msgno
- 1];
365 if (send(mp
, fo
, 0, NULL
, SEND_RFC822
, NULL
) < 0)
371 * Generate the body of a MIME multipart message.
374 make_multipart(struct header
*hp
, int convert
, FILE *fi
, FILE *fo
,
375 const char *contenttype
, const char *charset
, int dosign
)
377 struct attachment
*att
;
379 fputs("This is a multi-part message in MIME format.\n", fo
);
380 if (fsize(fi
) != 0) {
382 size_t sz
, bufsize
, count
;
384 fprintf(fo
, "\n--%s\n", send_boundary
);
385 fprintf(fo
, "Content-Type: %s", contenttype
);
387 fprintf(fo
, "; charset=%s", charset
);
388 fprintf(fo
, "\nContent-Transfer-Encoding: %s\n"
389 "Content-Disposition: inline\n\n",
390 getencoding(convert
));
391 buf
= smalloc(bufsize
= INFIX_BUF
);
392 if (convert
== CONV_TOQP
394 || iconvd
!= (iconv_t
)-1
395 #endif /* HAVE_ICONV */
401 if (convert
== CONV_TOQP
403 || iconvd
!= (iconv_t
)-1
404 #endif /* HAVE_ICONV */
406 if (fgetline(&buf
, &bufsize
, &count
, &sz
, fi
, 0)
410 sz
= fread(buf
, sizeof *buf
, bufsize
, fi
);
415 if (mime_write(buf
, sz
, fo
, convert
,
416 TD_ICONV
, NULL
, (size_t)0,
428 put_signature(fo
, convert
);
430 for (att
= hp
->h_attach
; att
!= NULL
; att
= att
->a_flink
) {
432 if (attach_message(att
, fo
, dosign
) != 0)
435 if (attach_file(att
, fo
, dosign
) != 0)
439 /* the final boundary with two attached dashes */
440 fprintf(fo
, "\n--%s--\n", send_boundary
);
445 * Prepend a header in front of the collected stuff
446 * and return the new file.
449 infix(struct header
*hp
, FILE *fi
, int dosign
)
454 char *tcs
, *convhdr
= NULL
;
456 enum mimeclean isclean
;
457 enum conversion convert
;
458 char *charset
= NULL
, *contenttype
= NULL
;
461 if ((nfo
= Ftemp(&tempMail
, "Rs", "w", 0600, 1)) == NULL
) {
462 perror(catgets(catd
, CATSET
, 178, "temporary mail file"));
465 if ((nfi
= Fopen(tempMail
, "r")) == NULL
) {
472 convert
= get_mime_convert(fi
, &contenttype
, &charset
,
476 if ((convhdr
= need_hdrconv(hp
, GTO
|GSUBJECT
|GCC
|GBCC
|GIDENT
)) != 0) {
477 if (iconvd
!= (iconv_t
)-1)
479 if ((iconvd
= iconv_open_ft(convhdr
, tcs
)) == (iconv_t
)-1
482 fprintf(stderr
, catgets(catd
, CATSET
, 179,
483 "Cannot convert from %s to %s\n"), tcs
, convhdr
);
485 perror("iconv_open");
490 #endif /* HAVE_ICONV */
492 GTO
|GSUBJECT
|GCC
|GBCC
|GNL
|GCOMMA
|GUA
|GMIME
493 |GMSGID
|GIDENT
|GREF
|GDATE
,
494 SEND_MBOX
, convert
, contenttype
, charset
)) {
498 if (iconvd
!= (iconv_t
)-1) {
500 iconvd
= (iconv_t
)-1;
506 if (convhdr
&& iconvd
!= (iconv_t
)-1) {
508 iconvd
= (iconv_t
)-1;
510 if ((isclean
& (MIME_HASNUL
|MIME_CTRLCHAR
)) == 0 &&
511 ascncasecmp(contenttype
, "text/", 5) == 0 &&
512 isclean
& MIME_HIGHBIT
&&
514 if (iconvd
!= (iconv_t
)-1)
516 if ((iconvd
= iconv_open_ft(charset
, tcs
)) == (iconv_t
)-1
519 fprintf(stderr
, catgets(catd
, CATSET
, 179,
520 "Cannot convert from %s to %s\n"), tcs
, charset
);
522 perror("iconv_open");
528 if (hp
->h_attach
!= NULL
) {
529 if (make_multipart(hp
, convert
, fi
, nfo
,
530 contenttype
, charset
, dosign
) != 0) {
534 if (iconvd
!= (iconv_t
)-1) {
536 iconvd
= (iconv_t
)-1;
542 size_t sz
, bufsize
, count
;
545 if (convert
== CONV_TOQP
547 || iconvd
!= (iconv_t
)-1
548 #endif /* HAVE_ICONV */
553 buf
= smalloc(bufsize
= INFIX_BUF
);
555 if (convert
== CONV_TOQP
557 || iconvd
!= (iconv_t
)-1
558 #endif /* HAVE_ICONV */
560 if (fgetline(&buf
, &bufsize
, &count
, &sz
, fi
, 0)
564 sz
= fread(buf
, sizeof *buf
, bufsize
, fi
);
569 if (mime_write(buf
, sz
, nfo
, convert
,
570 TD_ICONV
, NULL
, (size_t)0,
575 if (iconvd
!= (iconv_t
)-1) {
577 iconvd
= (iconv_t
)-1;
584 if (convert
== CONV_TOQP
&& lastc
!= '\n')
585 fwrite("=\n", 1, 2, nfo
);
591 if (iconvd
!= (iconv_t
)-1) {
593 iconvd
= (iconv_t
)-1;
599 put_signature(nfo
, convert
);
602 if (iconvd
!= (iconv_t
)-1) {
604 iconvd
= (iconv_t
)-1;
609 perror(catgets(catd
, CATSET
, 180, "temporary mail file"));
622 * Save the outgoing mail on the passed file.
627 savemail(char *name
, FILE *fi
)
631 size_t bufsize
, buflen
, count
;
637 buf
= smalloc(bufsize
= LINESIZE
);
639 if ((fo
= Zopen(name
, "a+", NULL
)) == NULL
) {
640 if ((fo
= Zopen(name
, "wx", NULL
)) == NULL
) {
646 if (fseek(fo
, -2L, SEEK_END
) == 0) {
647 switch (fread(buf
, sizeof *buf
, 2, fo
)) {
649 if (buf
[1] != '\n') {
672 fprintf(fo
, "From %s %s", myname
, ctime(&now
));
677 while (fgetline(&buf
, &bufsize
, &count
, &buflen
, fi
, 0) != NULL
) {
682 if (strncmp(p
, "From ", 5) == 0)
683 /* we got a masked From line */
685 } else if (strncmp(buf
, "From ", 5) == 0)
687 fwrite(buf
, sizeof *buf
, buflen
, fo
);
689 if (buflen
&& *(buf
+ buflen
- 1) != '\n')
702 * OpenBSD 3.2 and NetBSD 1.5.2 were reported not to
703 * reset the kernel file offset after the calls above,
704 * a clear violation of IEEE Std 1003.1, 1996, 8.2.3.7.
705 * So do it 'manually'.
707 lseek(fileno(fi
), 0, SEEK_SET
);
713 * Interface between the argument list and the mail1 routine
714 * which does all the dirty work.
717 mail(struct name
*to
, struct name
*cc
, struct name
*bcc
,
718 struct name
*smopts
, char *subject
, struct attachment
*attach
,
719 char *quotefile
, int recipient_record
, int tflag
, int Eflag
)
724 memset(&head
, 0, sizeof head
);
725 /* The given subject may be in RFC1522 format. */
726 if (subject
!= NULL
) {
728 in
.l
= strlen(subject
);
729 mime_fromhdr(&in
, &out
, TD_ISPR
| TD_ICONV
);
730 head
.h_subject
= out
.s
;
737 head
.h_attach
= attach
;
738 head
.h_smopts
= smopts
;
739 mail1(&head
, 0, NULL
, quotefile
, recipient_record
, 0, tflag
, Eflag
);
746 * Send mail to a bunch of user names. The interface is through
747 * the mail routine below.
750 sendmail_internal(void *v
, int recipient_record
)
756 memset(&head
, 0, sizeof head
);
757 head
.h_to
= extract(str
, GTO
|GFULL
);
758 Eflag
= value("skipemptybody") != NULL
;
759 mail1(&head
, 0, NULL
, NULL
, recipient_record
, 0, 0, Eflag
);
766 return sendmail_internal(v
, 0);
772 return sendmail_internal(v
, 1);
776 transfer(struct name
*to
, struct name
*mailargs
, FILE *input
, struct header
*hp
)
778 char o
[LINESIZE
], *cp
;
779 struct name
*np
, *nt
;
786 snprintf(o
, sizeof o
, "smime-encrypt-%s", np
->n_name
);
787 if ((cp
= value(o
)) != NULL
) {
788 if ((ef
= smime_encrypt(input
, cp
, np
->n_name
)) != 0) {
789 nt
= nalloc(np
->n_name
,
790 np
->n_type
& ~(GFULL
|GSKIN
));
791 if (start_mta(nt
, mailargs
, ef
, hp
) != OKAY
)
795 fprintf(stderr
, "Message not sent to <%s>\n",
801 np
->n_flink
->n_blink
= np
->n_blink
;
803 np
->n_blink
->n_flink
= np
->n_flink
;
813 if (value("smime-force-encryption") ||
814 start_mta(to
, mailargs
, input
, hp
) != OKAY
)
821 * Start the Mail Transfer Agent
822 * mailing to namelist and stdin redirected to input.
825 start_mta(struct name
*to
, struct name
*mailargs
, FILE *input
,
828 char **args
= NULL
, **t
;
832 char *user
= NULL
, *password
= NULL
, *skinned
= NULL
;
837 #endif /* HAVE_SOCKETS */
839 if ((smtp
= value("smtp")) == NULL
) {
840 args
= unpack(cat(mailargs
, to
));
841 if (debug
|| value("debug")) {
842 printf(catgets(catd
, CATSET
, 181,
843 "Sendmail arguments:"));
844 for (t
= args
; *t
!= NULL
; t
++)
845 printf(" \"%s\"", *t
);
852 skinned
= skin(myorigin(hp
));
853 if ((user
= smtp_auth_var("-user", skinned
)) != NULL
&&
854 (password
= smtp_auth_var("-password",
856 password
= getpassword(&otio
, &reset_tio
, NULL
);
858 #endif /* HAVE_SOCKETS */
860 * Fork, set up the temporary mail file as standard
861 * input for "mail", and exec with the user list we generated
864 if ((pid
= fork()) == -1) {
866 savedeadletter(input
);
872 sigaddset(&nset
, SIGHUP
);
873 sigaddset(&nset
, SIGINT
);
874 sigaddset(&nset
, SIGQUIT
);
875 sigaddset(&nset
, SIGTSTP
);
876 sigaddset(&nset
, SIGTTIN
);
877 sigaddset(&nset
, SIGTTOU
);
878 freopen("/dev/null", "r", stdin
);
880 prepare_child(&nset
, 0, 1);
881 if (smtp_mta(smtp
, to
, input
, hp
,
882 user
, password
, skinned
) == 0)
885 prepare_child(&nset
, fileno(input
), -1);
886 if ((cp
= value("sendmail")) != NULL
)
893 savedeadletter(input
);
894 fputs(catgets(catd
, CATSET
, 182,
895 ". . . message not sent.\n"), stderr
);
898 if (value("verbose") != NULL
|| value("sendwait") || debug
900 if (wait_child(pid
) == 0)
912 * Record outgoing mail if instructed to do so.
915 mightrecord(FILE *fp
, struct name
*to
, int recipient_record
)
919 if (recipient_record
) {
920 cq
= skin(to
->n_name
);
921 cp
= salloc(strlen(cq
) + 1);
923 for (cq
= cp
; *cq
&& *cq
!= '@'; cq
++);
926 cp
= value("record");
929 if (value("outfolder") && *ep
!= '/' && *ep
!= '+' &&
930 which_protocol(ep
) == PROTO_FILE
) {
931 cq
= salloc(strlen(cp
) + 2);
937 if (savemail(ep
, fp
) != 0) {
939 "Error while saving message to %s - "
940 "message not sent\n", ep
);
951 * Mail a message on standard input to the people indicated
952 * in the passed header. (Internal interface).
955 mail1(struct header
*hp
, int printheaders
, struct message
*quote
,
956 char *quotefile
, int recipient_record
, int doprefix
, int tflag
,
963 char *charsets
, *ncs
= NULL
, *cp
;
966 if ((hp
->h_to
= checkaddrs(hp
->h_to
)) == NULL
) {
971 if ((cp
= value("autocc")) != NULL
&& *cp
)
972 hp
->h_cc
= cat(hp
->h_cc
, checkaddrs(sextract(cp
, GCC
|GFULL
)));
973 if ((cp
= value("autobcc")) != NULL
&& *cp
)
974 hp
->h_bcc
= cat(hp
->h_bcc
,
975 checkaddrs(sextract(cp
, GBCC
|GFULL
)));
977 * Collect user's mail from standard input.
978 * Get the result as mtf.
980 if ((mtf
= collect(hp
, printheaders
, quote
, quotefile
, doprefix
,
983 if (value("interactive") != NULL
) {
984 if (((value("bsdcompat") || value("askatend"))
985 && (value("askcc") != NULL
||
986 value("askbcc") != NULL
)) ||
987 value("askattach") != NULL
||
988 value("asksign") != NULL
) {
989 if (value("askcc") != NULL
)
991 if (value("askbcc") != NULL
)
993 if (value("askattach") != NULL
)
994 hp
->h_attach
= edit_attachments(hp
->h_attach
);
995 if (value("asksign") != NULL
)
996 dosign
= yorn("Sign this message (y/n)? ");
998 printf(catgets(catd
, CATSET
, 183, "EOT\n"));
1002 if (fsize(mtf
) == 0) {
1005 if (hp
->h_subject
== NULL
)
1006 printf(catgets(catd
, CATSET
, 184,
1007 "No message, no subject; hope that's ok\n"));
1008 else if (value("bsdcompat") || value("bsdmsgs"))
1009 printf(catgets(catd
, CATSET
, 185,
1010 "Null message body; hope that's ok\n"));
1013 if (value("smime-sign") != NULL
)
1019 * Now, take the user names from the combined
1020 * to and cc lists and do all the alias
1024 if ((to
= usermap(cat(hp
->h_bcc
, cat(hp
->h_to
, hp
->h_cc
)))) == NULL
) {
1025 printf(catgets(catd
, CATSET
, 186, "No recipients specified\n"));
1028 to
= fixhead(hp
, to
);
1029 if (hp
->h_charset
) {
1030 wantcharset
= hp
->h_charset
;
1033 hloop
: wantcharset
= NULL
;
1034 if ((charsets
= value("sendcharsets")) != NULL
) {
1035 wantcharset
= savestr(charsets
);
1036 loop
: if ((ncs
= strchr(wantcharset
, ',')) != NULL
)
1039 try: if ((nmtf
= infix(hp
, mtf
, dosign
)) == NULL
) {
1040 if (hp
->h_charset
&& (errno
== EILSEQ
|| errno
== EINVAL
)) {
1042 hp
->h_charset
= NULL
;
1045 if (errno
== EILSEQ
|| errno
== EINVAL
) {
1051 if (wantcharset
&& value("interactive") == NULL
) {
1052 if (wantcharset
== (char *)-1)
1056 wantcharset
= (char *)-1;
1061 /* fprintf(stderr, ". . . message lost, sorry.\n"); */
1065 savedeadletter(mtf
);
1066 fputs(catgets(catd
, CATSET
, 187,
1067 ". . . message not sent.\n"), stderr
);
1072 if ((nmtf
= smime_sign(mtf
, hp
)) == NULL
)
1078 * Look through the recipient list for names with /'s
1079 * in them which we write to as files directly.
1081 to
= outof(to
, mtf
, hp
);
1083 savedeadletter(mtf
);
1085 if (count(to
) == 0) {
1090 if (mightrecord(mtf
, to
, recipient_record
) != OKAY
)
1092 ok
= transfer(to
, hp
->h_smopts
, mtf
, hp
);
1099 * Create a Message-Id: header field.
1100 * Use either the host name or the from address.
1103 message_id(FILE *fo
, struct header
*hp
)
1109 if ((cp
= value("hostname")) != NULL
)
1110 fprintf(fo
, "Message-ID: <%lx.%s@%s>\n",
1111 (long)now
, getrandstring(24), cp
);
1112 else if ((cp
= skin(myorigin(hp
))) != NULL
&& strchr(cp
, '@') != NULL
)
1113 fprintf(fo
, "Message-ID: <%lx.%s%%%s>\n",
1114 (long)now
, getrandstring(16), cp
);
1117 static const char *weekday_names
[] = {
1118 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
1121 const char *month_names
[] = {
1122 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
1123 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", NULL
1127 * Create a Date: header field.
1128 * We compare the localtime() and gmtime() results to get the timezone,
1129 * because numeric timezones are easier to read and because $TZ is
1130 * not set on most GNU systems.
1133 mkdate(FILE *fo
, const char *field
)
1137 int tzdiff
, tzdiff_hour
, tzdiff_min
;
1140 tzdiff
= t
- mktime(gmtime(&t
));
1141 tzdiff_hour
= (int)(tzdiff
/ 60);
1142 tzdiff_min
= tzdiff_hour
% 60;
1144 tmptr
= localtime(&t
);
1145 if (tmptr
->tm_isdst
> 0)
1147 return fprintf(fo
, "%s: %s, %02d %s %04d %02d:%02d:%02d %+05d\n",
1149 weekday_names
[tmptr
->tm_wday
],
1150 tmptr
->tm_mday
, month_names
[tmptr
->tm_mon
],
1151 tmptr
->tm_year
+ 1900, tmptr
->tm_hour
,
1152 tmptr
->tm_min
, tmptr
->tm_sec
,
1153 tzdiff_hour
* 100 + tzdiff_min
);
1157 putname(char *line
, enum gfield w
, enum sendaction action
, int *gotcha
,
1158 char *prefix
, FILE *fo
, struct name
**xp
)
1162 np
= sextract(line
, GEXTRA
|GFULL
);
1167 if (fmt(prefix
, np
, fo
, w
&(GCOMMA
|GFILES
), 0, action
!= SEND_TODISP
))
1174 #define FMT_CC_AND_BCC { \
1175 if (hp->h_cc != NULL && w & GCC) { \
1176 if (fmt("Cc:", hp->h_cc, fo, \
1177 w&(GCOMMA|GFILES), 0, \
1178 action!=SEND_TODISP)) \
1182 if (hp->h_bcc != NULL && w & GBCC) { \
1183 if (fmt("Bcc:", hp->h_bcc, fo, \
1184 w&(GCOMMA|GFILES), 0, \
1185 action!=SEND_TODISP)) \
1191 * Dump the to, subject, cc header on the
1192 * passed file buffer.
1195 puthead(struct header
*hp
, FILE *fo
, enum gfield w
,
1196 enum sendaction action
, enum conversion convert
,
1197 char *contenttype
, char *charset
)
1200 char *addr
/*, *cp*/;
1202 struct name
*np
, *fromfield
= NULL
, *senderfield
= NULL
;
1205 if (value("stealthmua"))
1211 mkdate(fo
, "Date"), gotcha
++;
1214 if (hp
->h_from
!= NULL
) {
1215 if (fmt("From:", hp
->h_from
, fo
, w
&(GCOMMA
|GFILES
), 0,
1216 action
!=SEND_TODISP
))
1219 fromfield
= hp
->h_from
;
1220 } else if ((addr
= myaddrs(hp
)) != NULL
)
1221 if (putname(addr
, w
, action
, &gotcha
, "From:", fo
,
1224 if (((addr
= hp
->h_organization
) != NULL
||
1225 (addr
= value("ORGANIZATION")) != NULL
)
1226 && strlen(addr
) > 0) {
1227 fwrite("Organization: ", sizeof (char), 14, fo
);
1228 if (mime_write(addr
, strlen(addr
), fo
,
1229 action
== SEND_TODISP
?
1230 CONV_NONE
:CONV_TOHDR
,
1231 action
== SEND_TODISP
?
1232 TD_ISPR
|TD_ICONV
:TD_ICONV
,
1239 if (hp
->h_replyto
!= NULL
) {
1240 if (fmt("Reply-To:", hp
->h_replyto
, fo
,
1241 w
&(GCOMMA
|GFILES
), 0,
1242 action
!=SEND_TODISP
))
1245 } else if ((addr
= value("replyto")) != NULL
)
1246 if (putname(addr
, w
, action
, &gotcha
, "Reply-To:", fo
,
1249 if (hp
->h_sender
!= NULL
) {
1250 if (fmt("Sender:", hp
->h_sender
, fo
,
1251 w
&(GCOMMA
|GFILES
), 0,
1252 action
!=SEND_TODISP
))
1255 senderfield
= hp
->h_sender
;
1256 } else if ((addr
= value("sender")) != NULL
)
1257 if (putname(addr
, w
, action
, &gotcha
, "Sender:", fo
,
1260 if (check_from_and_sender(fromfield
, senderfield
))
1263 if (hp
->h_to
!= NULL
&& w
& GTO
) {
1264 if (fmt("To:", hp
->h_to
, fo
, w
&(GCOMMA
|GFILES
), 0,
1265 action
!=SEND_TODISP
))
1269 if (value("bsdcompat") == NULL
&& value("bsdorder") == NULL
)
1271 if (hp
->h_subject
!= NULL
&& w
& GSUBJECT
) {
1272 fwrite("Subject: ", sizeof (char), 9, fo
);
1273 if (ascncasecmp(hp
->h_subject
, "re: ", 4) == 0) {
1274 fwrite("Re: ", sizeof (char), 4, fo
);
1275 if (strlen(hp
->h_subject
+ 4) > 0 &&
1276 mime_write(hp
->h_subject
+ 4,
1277 strlen(hp
->h_subject
+ 4),
1278 fo
, action
== SEND_TODISP
?
1279 CONV_NONE
:CONV_TOHDR
,
1280 action
== SEND_TODISP
?
1281 TD_ISPR
|TD_ICONV
:TD_ICONV
,
1285 } else if (*hp
->h_subject
) {
1286 if (mime_write(hp
->h_subject
,
1287 strlen(hp
->h_subject
),
1288 fo
, action
== SEND_TODISP
?
1289 CONV_NONE
:CONV_TOHDR
,
1290 action
== SEND_TODISP
?
1291 TD_ISPR
|TD_ICONV
:TD_ICONV
,
1297 fwrite("\n", sizeof (char), 1, fo
);
1299 if (value("bsdcompat") || value("bsdorder"))
1301 if (w
& GMSGID
&& stealthmua
== 0)
1302 message_id(fo
, hp
), gotcha
++;
1303 if (hp
->h_ref
!= NULL
&& w
& GREF
) {
1304 fmt("References:", hp
->h_ref
, fo
, 0, 1, 0);
1305 if ((np
= hp
->h_ref
) != NULL
&& np
->n_name
) {
1308 if (mime_name_invalid(np
->n_name
, 0) == 0) {
1309 fprintf(fo
, "In-Reply-To: %s\n", np
->n_name
);
1314 if (w
& GUA
&& stealthmua
== 0)
1315 fprintf(fo
, "User-Agent: S-nail %s\n",
1318 fputs("MIME-Version: 1.0\n", fo
), gotcha
++;
1319 if (hp
->h_attach
!= NULL
) {
1321 fprintf(fo
, "Content-Type: multipart/mixed;\n"
1322 " boundary=\"%s\"\n", send_boundary
);
1324 fprintf(fo
, "Content-Type: %s", contenttype
);
1326 fprintf(fo
, "; charset=%s", charset
);
1327 fprintf(fo
, "\nContent-Transfer-Encoding: %s\n",
1328 getencoding(convert
));
1331 if (gotcha
&& w
& GNL
)
1337 * Format the given header line to not exceed 72 characters.
1340 fmt(char *str
, struct name
*np
, FILE *fo
, int flags
, int dropinvalid
,
1343 int col
, len
, count
= 0;
1344 int is_to
= 0, comma
;
1346 comma
= flags
&GCOMMA
? 1 : 0;
1349 fwrite(str
, sizeof *str
, strlen(str
), fo
);
1350 if ((flags
&GFILES
) == 0 &&
1351 col
== 3 && asccasecmp(str
, "to:") == 0 ||
1352 col
== 3 && asccasecmp(str
, "cc:") == 0 ||
1353 col
== 4 && asccasecmp(str
, "bcc:") == 0 ||
1354 col
== 10 && asccasecmp(str
, "Resent-To:") == 0)
1357 for (; np
!= NULL
; np
= np
->n_flink
) {
1358 if (is_to
&& is_fileaddr(np
->n_name
))
1360 if (np
->n_flink
== NULL
)
1362 if (mime_name_invalid(np
->n_name
, !dropinvalid
)) {
1368 len
= strlen(np
->n_fullname
);
1369 col
++; /* for the space */
1370 if (count
&& col
+ len
+ comma
> 72 && col
> 1) {
1375 len
= mime_write(np
->n_fullname
,
1377 domime
?CONV_TOHDR_A
:CONV_NONE
,
1378 TD_ICONV
, NULL
, (size_t)0,
1380 if (comma
&& !(is_to
&& is_fileaddr(np
->n_flink
->n_name
)))
1390 * Rewrite a message for resending, adding the Resent-Headers.
1393 infix_resend(FILE *fi
, FILE *fo
, struct message
*mp
, struct name
*to
,
1397 char *buf
= NULL
, *cp
/*, *cp2*/;
1398 size_t c
, bufsize
= 0;
1399 struct name
*fromfield
= NULL
, *senderfield
= NULL
;
1403 * Write the Resent-Fields.
1406 fputs("Resent-", fo
);
1408 if ((cp
= myaddrs(NULL
)) != NULL
) {
1409 if (putname(cp
, GCOMMA
, SEND_MBOX
, NULL
,
1410 "Resent-From:", fo
, &fromfield
))
1413 if ((cp
= value("sender")) != NULL
) {
1414 if (putname(cp
, GCOMMA
, SEND_MBOX
, NULL
,
1415 "Resent-Sender:", fo
, &senderfield
))
1420 * RFC 2822 disallows generation of this field.
1422 cp
= value("replyto");
1424 if (mime_name_invalid(cp
, 1)) {
1429 fwrite("Resent-Reply-To: ", sizeof (char),
1431 mime_write(cp
, strlen(cp
), fo
,
1432 CONV_TOHDR_A
, TD_ICONV
,
1438 if (fmt("Resent-To:", to
, fo
, 1, 1, 0)) {
1443 if (value("stealthmua") == NULL
) {
1444 fputs("Resent-", fo
);
1445 message_id(fo
, NULL
);
1448 if (check_from_and_sender(fromfield
, senderfield
))
1451 * Write the original headers.
1454 if ((cp
= foldergets(&buf
, &bufsize
, &count
, &c
, fi
)) == NULL
)
1456 if (ascncasecmp("status: ", buf
, 8) != 0
1457 && strncmp("From ", buf
, 5) != 0) {
1458 fwrite(buf
, sizeof *buf
, c
, fo
);
1460 if (count
> 0 && *buf
== '\n')
1464 * Write the message body.
1467 if (foldergets(&buf
, &bufsize
, &count
, &c
, fi
) == NULL
)
1469 if (count
== 0 && *buf
== '\n')
1471 fwrite(buf
, sizeof *buf
, c
, fo
);
1476 perror(catgets(catd
, CATSET
, 188, "temporary mail file"));
1483 resend_msg(struct message
*mp
, struct name
*to
, int add_resent
)
1485 FILE *ibuf
, *nfo
, *nfi
;
1488 enum okay ok
= STOP
;
1490 memset(&head
, 0, sizeof head
);
1491 if ((to
= checkaddrs(to
)) == NULL
) {
1495 if ((nfo
= Ftemp(&tempMail
, "Rs", "w", 0600, 1)) == NULL
) {
1497 perror(catgets(catd
, CATSET
, 189, "temporary mail file"));
1500 if ((nfi
= Fopen(tempMail
, "r")) == NULL
) {
1507 if ((ibuf
= setinput(&mb
, mp
, NEED_BODY
)) == NULL
)
1510 to
= fixhead(&head
, to
);
1511 if (infix_resend(ibuf
, nfo
, mp
, head
.h_to
, add_resent
) != 0) {
1514 savedeadletter(nfi
);
1515 fputs(catgets(catd
, CATSET
, 190,
1516 ". . . message not sent.\n"), stderr
);
1524 to
= outof(to
, nfi
, &head
);
1526 savedeadletter(nfi
);
1528 if (count(to
) != 0) {
1529 if (value("record-resent") == NULL
||
1530 mightrecord(nfi
, to
, 0) == OKAY
)
1531 ok
= transfer(to
, head
.h_smopts
, nfi
, NULL
);
1532 } else if (senderr
== 0)