2 * 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 Steffen "Daode" Nurpmeso.
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
49 * Mail -- a mail program
54 static char *send_boundary
;
56 static char *getencoding(enum conversion convert
);
57 static struct name
*fixhead(struct header
*hp
, struct name
*tolist
);
58 static int put_signature(FILE *fo
, int convert
);
59 static int attach_file1(struct attachment
*ap
, FILE *fo
, int dosign
);
60 static int attach_file(struct attachment
*ap
, FILE *fo
, int dosign
);
61 static int attach_message(struct attachment
*ap
, FILE *fo
, int dosign
);
62 static int make_multipart(struct header
*hp
, int convert
, FILE *fi
, FILE *fo
,
63 const char *contenttype
, const char *charset
, int dosign
);
64 static FILE *infix(struct header
*hp
, FILE *fi
, int dosign
);
65 static int savemail(char *name
, FILE *fi
);
66 static int sendmail_internal(void *v
, int recipient_record
);
67 static enum okay
transfer(struct name
*to
, FILE *input
, struct header
*hp
);
68 static char ** prepare_mta_args(struct name
*to
);
69 static enum okay
start_mta(struct name
*to
, FILE *input
, struct header
*hp
);
70 static void message_id(FILE *fo
, struct header
*hp
);
71 static int fmt(char *str
, struct name
*np
, FILE *fo
, int comma
,
72 int dropinvalid
, int domime
);
73 static int infix_resend(FILE *fi
, FILE *fo
, struct message
*mp
,
74 struct name
*to
, int add_resent
);
77 * Generate a boundary for MIME multipart messages.
82 static char bound
[70];
86 snprintf(bound
, sizeof bound
, "=_%lx.%s", (long)now
, getrandstring(48));
87 send_boundary
= bound
;
92 * Get an encoding flag based on the given string.
95 getencoding(enum conversion convert
)
103 return "quoted-printable";
114 * Fix the header by glopping all of the expanded names from
115 * the distribution list into the appropriate fields.
118 fixhead(struct header
*hp
, struct name
*tolist
) /* TODO !HAVE_ASSERTS legacy*/
122 hp
->h_to
= hp
->h_cc
= hp
->h_bcc
= NULL
;
123 for (np
= tolist
; np
!= NULL
; np
= np
->n_flink
)
124 if (np
->n_type
& GDEL
) {
126 assert(0); /* Shouldn't happen here, but later on :)) */
130 } else switch (np
->n_type
& GMASK
) {
132 hp
->h_to
= cat(hp
->h_to
, ndup(np
, np
->n_type
|GFULL
));
135 hp
->h_cc
= cat(hp
->h_cc
, ndup(np
, np
->n_type
|GFULL
));
138 hp
->h_bcc
= cat(hp
->h_bcc
, ndup(np
, np
->n_type
|GFULL
));
148 * Do not change, you get incorrect base64 encodings else!
150 #define INFIX_BUF 972
153 * Put the signature file at fo.
156 put_signature(FILE *fo
, int convert
)
158 char *sig
, buf
[INFIX_BUF
], c
= '\n';
162 sig
= value("signature");
163 if (sig
== NULL
|| *sig
== '\0')
165 else if ((sig
= file_expand(sig
)) == NULL
)
167 if ((fsig
= Fopen(sig
, "r")) == NULL
) {
171 while ((sz
= fread(buf
, sizeof *buf
, INFIX_BUF
, fsig
)) != 0) {
173 if (mime_write(buf
, sz
, fo
, convert
, TD_NONE
,
174 NULL
, (size_t)0, NULL
, NULL
)
193 * Write an attachment to the file buffer, converting to MIME.
196 attach_file1(struct attachment
*ap
, FILE *fo
, int dosign
)
199 char *charset
= NULL
, *contenttype
= NULL
, *basename
;
200 enum conversion convert
= CONV_TOB64
;
202 enum mimeclean isclean
;
205 size_t bufsize
, count
;
211 if ((fi
= Fopen(ap
->a_name
, "r")) == NULL
) {
215 if ((basename
= strrchr(ap
->a_name
, '/')) == NULL
)
216 basename
= ap
->a_name
;
219 if (ap
->a_content_type
)
220 contenttype
= ap
->a_content_type
;
222 contenttype
= mime_filecontent(basename
);
224 charset
= ap
->a_charset
;
225 convert
= get_mime_convert(fi
, &contenttype
, &charset
, &isclean
,
230 send_boundary
, contenttype
);
234 fprintf(fo
, ";\n charset=%s\n", charset
);
235 if (ap
->a_content_disposition
== NULL
)
236 ap
->a_content_disposition
= "attachment";
237 fprintf(fo
, "Content-Transfer-Encoding: %s\n"
238 "Content-Disposition: %s;\n"
240 getencoding(convert
),
241 ap
->a_content_disposition
);
242 mime_write(basename
, strlen(basename
), fo
,
243 CONV_TOHDR
, TD_NONE
, NULL
, (size_t)0, NULL
, NULL
);
244 fwrite("\"\n", sizeof (char), 2, fo
);
245 if (ap
->a_content_id
)
246 fprintf(fo
, "Content-ID: %s\n", ap
->a_content_id
);
247 if (ap
->a_content_description
)
248 fprintf(fo
, "Content-Description: %s\n",
249 ap
->a_content_description
);
252 if (iconvd
!= (iconv_t
)-1) {
254 iconvd
= (iconv_t
)-1;
257 if ((isclean
& (MIME_HASNUL
|MIME_CTRLCHAR
)) == 0 &&
258 ascncasecmp(contenttype
, "text/", 5) == 0 &&
259 isclean
& MIME_HIGHBIT
&&
261 if ((iconvd
= iconv_open_ft(charset
, tcs
)) == (iconv_t
)-1 &&
264 fprintf(stderr
, catgets(catd
, CATSET
, 179,
265 "Cannot convert from %s to %s\n"), tcs
, charset
);
267 perror("iconv_open");
272 #endif /* HAVE_ICONV */
273 buf
= smalloc(bufsize
= INFIX_BUF
);
274 if (convert
== CONV_TOQP
276 || iconvd
!= (iconv_t
)-1
283 if (convert
== CONV_TOQP
285 || iconvd
!= (iconv_t
)-1
288 if (fgetline(&buf
, &bufsize
, &count
, &sz
, fi
, 0)
292 if ((sz
= fread(buf
, sizeof *buf
, bufsize
, fi
)) == 0)
296 if (mime_write(buf
, sz
, fo
, convert
, TD_ICONV
,
297 NULL
, (size_t)0, NULL
, NULL
) == 0)
300 if (convert
== CONV_TOQP
&& lastc
!= '\n')
301 fwrite("=\n", 1, 2, fo
);
310 * Try out different character set conversions to attach a file.
313 attach_file(struct attachment
*ap
, FILE *fo
, int dosign
)
315 char *_wantcharset
, *charsets
, *ncs
;
316 size_t offs
= ftell(fo
);
318 if (ap
->a_charset
|| (charsets
= value("sendcharsets")) == NULL
)
319 return attach_file1(ap
, fo
, dosign
);
320 _wantcharset
= wantcharset
;
321 wantcharset
= savestr(charsets
);
322 loop
: if ((ncs
= strchr(wantcharset
, ',')) != NULL
)
324 try: if (attach_file1(ap
, fo
, dosign
) != 0) {
325 if (errno
== EILSEQ
|| errno
== EINVAL
) {
329 fseek(fo
, offs
, SEEK_SET
);
333 if (wantcharset
== (char *)-1)
336 wantcharset
= (char *)-1;
338 fseek(fo
, offs
, SEEK_SET
);
344 wantcharset
= _wantcharset
;
349 * Attach a message to the file buffer.
352 attach_message(struct attachment
*ap
, FILE *fo
, int dosign
)
357 fprintf(fo
, "\n--%s\n"
358 "Content-Type: message/rfc822\n"
359 "Content-Disposition: inline\n\n", send_boundary
);
360 mp
= &message
[ap
->a_msgno
- 1];
362 if (send(mp
, fo
, 0, NULL
, SEND_RFC822
, NULL
) < 0)
368 * Generate the body of a MIME multipart message.
371 make_multipart(struct header
*hp
, int convert
, FILE *fi
, FILE *fo
,
372 const char *contenttype
, const char *charset
, int dosign
)
374 struct attachment
*att
;
376 fputs("This is a multi-part message in MIME format.\n", fo
);
377 if (fsize(fi
) != 0) {
379 size_t sz
, bufsize
, count
;
381 fprintf(fo
, "\n--%s\n", send_boundary
);
382 fprintf(fo
, "Content-Type: %s", contenttype
);
384 fprintf(fo
, "; charset=%s", charset
);
385 fprintf(fo
, "\nContent-Transfer-Encoding: %s\n"
386 "Content-Disposition: inline\n\n",
387 getencoding(convert
));
388 buf
= smalloc(bufsize
= INFIX_BUF
);
389 if (convert
== CONV_TOQP
391 || iconvd
!= (iconv_t
)-1
392 #endif /* HAVE_ICONV */
398 if (convert
== CONV_TOQP
400 || iconvd
!= (iconv_t
)-1
401 #endif /* HAVE_ICONV */
403 if (fgetline(&buf
, &bufsize
, &count
, &sz
, fi
, 0)
407 sz
= fread(buf
, sizeof *buf
, bufsize
, fi
);
412 if (mime_write(buf
, sz
, fo
, convert
,
413 TD_ICONV
, NULL
, (size_t)0,
425 put_signature(fo
, convert
);
427 for (att
= hp
->h_attach
; att
!= NULL
; att
= att
->a_flink
) {
429 if (attach_message(att
, fo
, dosign
) != 0)
432 if (attach_file(att
, fo
, dosign
) != 0)
436 /* the final boundary with two attached dashes */
437 fprintf(fo
, "\n--%s--\n", send_boundary
);
442 * Prepend a header in front of the collected stuff
443 * and return the new file.
446 infix(struct header
*hp
, FILE *fi
, int dosign
)
451 char *tcs
, *convhdr
= NULL
;
453 enum mimeclean isclean
;
454 enum conversion convert
;
455 char *charset
= NULL
, *contenttype
= NULL
;
458 if ((nfo
= Ftemp(&tempMail
, "Rs", "w", 0600, 1)) == NULL
) {
459 perror(catgets(catd
, CATSET
, 178, "temporary mail file"));
462 if ((nfi
= Fopen(tempMail
, "r")) == NULL
) {
469 convert
= get_mime_convert(fi
, &contenttype
, &charset
,
473 if ((convhdr
= need_hdrconv(hp
, GTO
|GSUBJECT
|GCC
|GBCC
|GIDENT
)) != 0) {
474 if (iconvd
!= (iconv_t
)-1)
476 if ((iconvd
= iconv_open_ft(convhdr
, tcs
)) == (iconv_t
)-1
479 fprintf(stderr
, catgets(catd
, CATSET
, 179,
480 "Cannot convert from %s to %s\n"), tcs
, convhdr
);
482 perror("iconv_open");
487 #endif /* HAVE_ICONV */
489 GTO
|GSUBJECT
|GCC
|GBCC
|GNL
|GCOMMA
|GUA
|GMIME
490 |GMSGID
|GIDENT
|GREF
|GDATE
,
491 SEND_MBOX
, convert
, contenttype
, charset
)) {
495 if (iconvd
!= (iconv_t
)-1) {
497 iconvd
= (iconv_t
)-1;
503 if (convhdr
&& iconvd
!= (iconv_t
)-1) {
505 iconvd
= (iconv_t
)-1;
507 if ((isclean
& (MIME_HASNUL
|MIME_CTRLCHAR
)) == 0 &&
508 ascncasecmp(contenttype
, "text/", 5) == 0 &&
509 isclean
& MIME_HIGHBIT
&&
511 if (iconvd
!= (iconv_t
)-1)
513 if ((iconvd
= iconv_open_ft(charset
, tcs
)) == (iconv_t
)-1
516 fprintf(stderr
, catgets(catd
, CATSET
, 179,
517 "Cannot convert from %s to %s\n"), tcs
, charset
);
519 perror("iconv_open");
525 if (hp
->h_attach
!= NULL
) {
526 if (make_multipart(hp
, convert
, fi
, nfo
,
527 contenttype
, charset
, dosign
) != 0) {
531 if (iconvd
!= (iconv_t
)-1) {
533 iconvd
= (iconv_t
)-1;
539 size_t sz
, bufsize
, count
;
542 if (convert
== CONV_TOQP
544 || iconvd
!= (iconv_t
)-1
545 #endif /* HAVE_ICONV */
550 buf
= smalloc(bufsize
= INFIX_BUF
);
552 if (convert
== CONV_TOQP
554 || iconvd
!= (iconv_t
)-1
555 #endif /* HAVE_ICONV */
557 if (fgetline(&buf
, &bufsize
, &count
, &sz
, fi
, 0)
561 sz
= fread(buf
, sizeof *buf
, bufsize
, fi
);
566 if (mime_write(buf
, sz
, nfo
, convert
,
567 TD_ICONV
, NULL
, (size_t)0,
572 if (iconvd
!= (iconv_t
)-1) {
574 iconvd
= (iconv_t
)-1;
581 if (convert
== CONV_TOQP
&& lastc
!= '\n')
582 fwrite("=\n", 1, 2, nfo
);
588 if (iconvd
!= (iconv_t
)-1) {
590 iconvd
= (iconv_t
)-1;
596 put_signature(nfo
, convert
);
599 if (iconvd
!= (iconv_t
)-1) {
601 iconvd
= (iconv_t
)-1;
606 perror(catgets(catd
, CATSET
, 180, "temporary mail file"));
619 * Save the outgoing mail on the passed file.
624 savemail(char *name
, FILE *fi
)
628 size_t bufsize
, buflen
, count
;
634 buf
= smalloc(bufsize
= LINESIZE
);
636 if ((fo
= Zopen(name
, "a+", NULL
)) == NULL
) {
637 if ((fo
= Zopen(name
, "wx", NULL
)) == NULL
) {
643 if (fseek(fo
, -2L, SEEK_END
) == 0) {
644 switch (fread(buf
, sizeof *buf
, 2, fo
)) {
646 if (buf
[1] != '\n') {
669 fprintf(fo
, "From %s %s", myname
, ctime(&now
));
674 while (fgetline(&buf
, &bufsize
, &count
, &buflen
, fi
, 0) != NULL
) {
679 if (strncmp(p
, "From ", 5) == 0)
680 /* we got a masked From line */
682 } else if (strncmp(buf
, "From ", 5) == 0)
684 fwrite(buf
, sizeof *buf
, buflen
, fo
);
686 if (buflen
&& *(buf
+ buflen
- 1) != '\n')
703 * Interface between the argument list and the mail1 routine
704 * which does all the dirty work.
707 mail(struct name
*to
, struct name
*cc
, struct name
*bcc
,
708 char *subject
, struct attachment
*attach
,
709 char *quotefile
, int recipient_record
, int tflag
, int Eflag
)
714 memset(&head
, 0, sizeof head
);
715 /* The given subject may be in RFC1522 format. */
716 if (subject
!= NULL
) {
718 in
.l
= strlen(subject
);
719 mime_fromhdr(&in
, &out
, TD_ISPR
| TD_ICONV
);
720 head
.h_subject
= out
.s
;
727 head
.h_attach
= attach
;
728 mail1(&head
, 0, NULL
, quotefile
, recipient_record
, 0, tflag
, Eflag
);
735 * Send mail to a bunch of user names. The interface is through
736 * the mail routine below.
739 sendmail_internal(void *v
, int recipient_record
)
745 memset(&head
, 0, sizeof head
);
746 head
.h_to
= lextract(str
, GTO
|GFULL
);
747 Eflag
= value("skipemptybody") != NULL
;
748 mail1(&head
, 0, NULL
, NULL
, recipient_record
, 0, 0, Eflag
);
755 return sendmail_internal(v
, 0);
761 return sendmail_internal(v
, 1);
765 transfer(struct name
*to
, FILE *input
, struct header
*hp
)
767 char o
[LINESIZE
], *cp
;
768 struct name
*np
, *nt
;
775 snprintf(o
, sizeof o
, "smime-encrypt-%s", np
->n_name
);
776 if ((cp
= value(o
)) != NULL
) {
777 if ((ef
= smime_encrypt(input
, cp
, np
->n_name
)) != 0) {
778 nt
= ndup(np
, np
->n_type
& ~(GFULL
|GSKIN
));
779 if (start_mta(nt
, ef
, hp
) != OKAY
)
783 fprintf(stderr
, "Message not sent to <%s>\n",
789 np
->n_flink
->n_blink
= np
->n_blink
;
791 np
->n_blink
->n_flink
= np
->n_flink
;
801 if (value("smime-force-encryption") ||
802 start_mta(to
, input
, hp
) != OKAY
)
809 prepare_mta_args(struct name
*to
)
811 size_t j
, i
= 4 + smopts_count
+ count(to
) + 1;
812 char **args
= salloc(i
* sizeof(char*));
814 args
[0] = "send-mail";
819 if (value("verbose"))
821 for (j
= 0; j
< smopts_count
; ++j
, ++i
)
823 for (; to
!= NULL
; to
= to
->n_flink
)
824 if ((to
->n_type
& GDEL
) == 0)
825 args
[i
++] = to
->n_name
;
831 * Start the Mail Transfer Agent
832 * mailing to namelist and stdin redirected to input.
835 start_mta(struct name
*to
, FILE *input
, struct header
*hp
)
841 char **args
= NULL
, *user
= NULL
, *password
= NULL
, *skinned
= NULL
,
847 if ((smtp
= value("smtp")) == NULL
) {
848 if ((mta
= value("sendmail")) != NULL
) {
849 if ((mta
= file_expand(mta
)) == NULL
)
854 args
= prepare_mta_args(to
);
855 if (debug
|| value("debug")) {
856 printf(tr(181, "Sendmail arguments:"));
857 for (t
= args
; *t
!= NULL
; t
++)
858 printf(" \"%s\"", *t
);
865 skinned
= skin(myorigin(hp
));
866 if ((user
= smtp_auth_var("-user", skinned
)) != NULL
&&
867 (password
= smtp_auth_var("-password",
869 password
= getpassword(&otio
, &reset_tio
, NULL
);
873 * Fork, set up the temporary mail file as standard
874 * input for "mail", and exec with the user list we generated
877 if ((pid
= fork()) == -1) {
879 jstop
: savedeadletter(input
);
885 sigaddset(&nset
, SIGHUP
);
886 sigaddset(&nset
, SIGINT
);
887 sigaddset(&nset
, SIGQUIT
);
888 sigaddset(&nset
, SIGTSTP
);
889 sigaddset(&nset
, SIGTTIN
);
890 sigaddset(&nset
, SIGTTOU
);
891 freopen("/dev/null", "r", stdin
);
893 prepare_child(&nset
, 0, 1);
894 if (smtp_mta(smtp
, to
, input
, hp
,
895 user
, password
, skinned
) == 0)
898 prepare_child(&nset
, fileno(input
), -1);
902 savedeadletter(input
);
903 fputs(tr(182, ". . . message not sent.\n"), stderr
);
906 if (value("verbose") != NULL
|| value("sendwait") || debug
908 if (wait_child(pid
) == 0)
920 * Record outgoing mail if instructed to do so.
923 mightrecord(FILE *fp
, struct name
*to
, int recipient_record
)
927 if (recipient_record
) {
928 cq
= skinned_name(to
);
929 cp
= salloc(strlen(cq
) + 1);
931 for (cq
= cp
; *cq
&& *cq
!= '@'; cq
++);
934 cp
= value("record");
941 if (value("outfolder") && *ep
!= '/' && *ep
!= '+' &&
942 which_protocol(ep
) == PROTO_FILE
) {
943 cq
= salloc(strlen(cp
) + 2);
953 if (savemail(ep
, fp
) != 0) {
954 jbail
: fprintf(stderr
, tr(285,
955 "Failed to save message in %s - "
956 "message not sent\n"), ep
);
967 * Mail a message on standard input to the people indicated
968 * in the passed header. (Internal interface).
971 mail1(struct header
*hp
, int printheaders
, struct message
*quote
,
972 char *quotefile
, int recipient_record
, int doprefix
, int tflag
,
979 char *charsets
, *ncs
= NULL
, *cp
;
982 if ((hp
->h_to
= checkaddrs(hp
->h_to
)) == NULL
) {
987 if ((cp
= value("autocc")) != NULL
&& *cp
)
988 hp
->h_cc
= cat(hp
->h_cc
, checkaddrs(lextract(cp
, GCC
|GFULL
)));
989 if ((cp
= value("autobcc")) != NULL
&& *cp
)
990 hp
->h_bcc
= cat(hp
->h_bcc
, checkaddrs(lextract(cp
,GBCC
|GFULL
)));
992 * Collect user's mail from standard input.
993 * Get the result as mtf.
995 if ((mtf
= collect(hp
, printheaders
, quote
, quotefile
, doprefix
,
998 if (value("interactive") != NULL
) {
999 if (((value("bsdcompat") || value("askatend"))
1000 && (value("askcc") != NULL
||
1001 value("askbcc") != NULL
)) ||
1002 value("askattach") != NULL
||
1003 value("asksign") != NULL
) {
1004 if (value("askcc") != NULL
)
1006 if (value("askbcc") != NULL
)
1008 if (value("askattach") != NULL
)
1009 hp
->h_attach
= edit_attachments(hp
->h_attach
);
1010 if (value("asksign") != NULL
)
1011 dosign
= yorn("Sign this message (y/n)? ");
1013 printf(catgets(catd
, CATSET
, 183, "EOT\n"));
1017 if (fsize(mtf
) == 0) {
1020 if (hp
->h_subject
== NULL
)
1021 printf(catgets(catd
, CATSET
, 184,
1022 "No message, no subject; hope that's ok\n"));
1023 else if (value("bsdcompat") || value("bsdmsgs"))
1024 printf(catgets(catd
, CATSET
, 185,
1025 "Null message body; hope that's ok\n"));
1028 if (value("smime-sign") != NULL
)
1034 * Now, take the user names from the combined
1035 * to and cc lists and do all the alias
1039 if ((to
= usermap(cat(hp
->h_bcc
, cat(hp
->h_to
, hp
->h_cc
)))) == NULL
) {
1040 printf(catgets(catd
, CATSET
, 186, "No recipients specified\n"));
1043 to
= fixhead(hp
, to
);
1044 if (hp
->h_charset
) {
1045 wantcharset
= hp
->h_charset
;
1048 hloop
: wantcharset
= NULL
;
1049 if ((charsets
= value("sendcharsets")) != NULL
) {
1050 wantcharset
= savestr(charsets
);
1051 loop
: if ((ncs
= strchr(wantcharset
, ',')) != NULL
)
1054 try: if ((nmtf
= infix(hp
, mtf
, dosign
)) == NULL
) {
1055 if (hp
->h_charset
&& (errno
== EILSEQ
|| errno
== EINVAL
)) {
1057 hp
->h_charset
= NULL
;
1060 if (errno
== EILSEQ
|| errno
== EINVAL
) {
1066 if (wantcharset
&& value("interactive") == NULL
) {
1067 if (wantcharset
== (char *)-1)
1071 wantcharset
= (char *)-1;
1076 /* fprintf(stderr, ". . . message lost, sorry.\n"); */
1080 savedeadletter(mtf
);
1081 fputs(catgets(catd
, CATSET
, 187,
1082 ". . . message not sent.\n"), stderr
);
1087 if ((nmtf
= smime_sign(mtf
, hp
)) == NULL
)
1093 * Look through the recipient list for names with /'s
1094 * in them which we write to as files directly.
1096 to
= outof(to
, mtf
, hp
);
1098 savedeadletter(mtf
);
1100 if (count(to
) == 0) {
1105 if (mightrecord(mtf
, to
, recipient_record
) != OKAY
)
1107 ok
= transfer(to
, mtf
, hp
);
1114 * Create a Message-Id: header field.
1115 * Use either the host name or the from address.
1118 message_id(FILE *fo
, struct header
*hp
)
1127 if ((h
= value("hostname")) != NULL
)
1129 else if ((h
= skin(myorigin(hp
))) != NULL
&& strchr(h
, '@') != NULL
)
1132 /* Delivery seems to dependent on a MTA -- it's up to it */
1135 fprintf(fo
, "Message-ID: <%04d%02d%02d%02d%02d%02d.%s%c%s>\n",
1136 tmp
->tm_year
+ 1900, tmp
->tm_mon
+ 1, tmp
->tm_mday
,
1137 tmp
->tm_hour
, tmp
->tm_min
, tmp
->tm_sec
,
1138 getrandstring(rl
), (rl
== 16 ? '%' : '@'), h
);
1141 static const char *weekday_names
[] = {
1142 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
1145 const char *month_names
[] = {
1146 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
1147 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", NULL
1151 * Create a Date: header field.
1152 * We compare the localtime() and gmtime() results to get the timezone,
1153 * because numeric timezones are easier to read and because $TZ is
1154 * not set on most GNU systems.
1157 mkdate(FILE *fo
, const char *field
)
1161 int tzdiff
, tzdiff_hour
, tzdiff_min
;
1164 tzdiff
= t
- mktime(gmtime(&t
));
1165 tzdiff_hour
= (int)(tzdiff
/ 60);
1166 tzdiff_min
= tzdiff_hour
% 60;
1168 tmptr
= localtime(&t
);
1169 if (tmptr
->tm_isdst
> 0)
1171 return fprintf(fo
, "%s: %s, %02d %s %04d %02d:%02d:%02d %+05d\n",
1173 weekday_names
[tmptr
->tm_wday
],
1174 tmptr
->tm_mday
, month_names
[tmptr
->tm_mon
],
1175 tmptr
->tm_year
+ 1900, tmptr
->tm_hour
,
1176 tmptr
->tm_min
, tmptr
->tm_sec
,
1177 tzdiff_hour
* 100 + tzdiff_min
);
1181 putname(char *line
, enum gfield w
, enum sendaction action
, int *gotcha
,
1182 char *prefix
, FILE *fo
, struct name
**xp
)
1186 np
= lextract(line
, GEXTRA
|GFULL
);
1191 if (fmt(prefix
, np
, fo
, w
&(GCOMMA
|GFILES
), 0, action
!= SEND_TODISP
))
1198 #define FMT_CC_AND_BCC { \
1199 if (hp->h_cc != NULL && w & GCC) { \
1200 if (fmt("Cc:", hp->h_cc, fo, \
1201 w&(GCOMMA|GFILES), 0, \
1202 action!=SEND_TODISP)) \
1206 if (hp->h_bcc != NULL && w & GBCC) { \
1207 if (fmt("Bcc:", hp->h_bcc, fo, \
1208 w&(GCOMMA|GFILES), 0, \
1209 action!=SEND_TODISP)) \
1215 * Dump the to, subject, cc header on the
1216 * passed file buffer.
1219 puthead(struct header
*hp
, FILE *fo
, enum gfield w
,
1220 enum sendaction action
, enum conversion convert
,
1221 char *contenttype
, char *charset
)
1224 char *addr
/*, *cp*/;
1226 struct name
*np
, *fromfield
= NULL
, *senderfield
= NULL
;
1229 if ((addr
= value("stealthmua")) != NULL
) {
1230 stealthmua
= (strcmp(addr
, "noagent") == 0) ? -1 : 1;
1235 mkdate(fo
, "Date"), gotcha
++;
1238 if (hp
->h_from
!= NULL
) {
1239 if (fmt("From:", hp
->h_from
, fo
, w
&(GCOMMA
|GFILES
), 0,
1240 action
!=SEND_TODISP
))
1243 fromfield
= hp
->h_from
;
1244 } else if ((addr
= myaddrs(hp
)) != NULL
)
1245 if (putname(addr
, w
, action
, &gotcha
, "From:", fo
,
1248 if (((addr
= hp
->h_organization
) != NULL
||
1249 (addr
= value("ORGANIZATION")) != NULL
)
1250 && strlen(addr
) > 0) {
1251 fwrite("Organization: ", sizeof (char), 14, fo
);
1252 if (mime_write(addr
, strlen(addr
), fo
,
1253 action
== SEND_TODISP
?
1254 CONV_NONE
:CONV_TOHDR
,
1255 action
== SEND_TODISP
?
1256 TD_ISPR
|TD_ICONV
:TD_ICONV
,
1263 if (hp
->h_replyto
!= NULL
) {
1264 if (fmt("Reply-To:", hp
->h_replyto
, fo
,
1265 w
&(GCOMMA
|GFILES
), 0,
1266 action
!=SEND_TODISP
))
1269 } else if ((addr
= value("replyto")) != NULL
)
1270 if (putname(addr
, w
, action
, &gotcha
, "Reply-To:", fo
,
1273 if (hp
->h_sender
!= NULL
) {
1274 if (fmt("Sender:", hp
->h_sender
, fo
,
1275 w
&(GCOMMA
|GFILES
), 0,
1276 action
!=SEND_TODISP
))
1279 senderfield
= hp
->h_sender
;
1280 } else if ((addr
= value("sender")) != NULL
)
1281 if (putname(addr
, w
, action
, &gotcha
, "Sender:", fo
,
1284 if (check_from_and_sender(fromfield
, senderfield
))
1287 if (hp
->h_to
!= NULL
&& w
& GTO
) {
1288 if (fmt("To:", hp
->h_to
, fo
, w
&(GCOMMA
|GFILES
), 0,
1289 action
!=SEND_TODISP
))
1293 if (value("bsdcompat") == NULL
&& value("bsdorder") == NULL
)
1295 if (hp
->h_subject
!= NULL
&& w
& GSUBJECT
) {
1296 fwrite("Subject: ", sizeof (char), 9, fo
);
1297 if (ascncasecmp(hp
->h_subject
, "re: ", 4) == 0) {
1298 fwrite("Re: ", sizeof (char), 4, fo
);
1299 if (strlen(hp
->h_subject
+ 4) > 0 &&
1300 mime_write(hp
->h_subject
+ 4,
1301 strlen(hp
->h_subject
+ 4),
1302 fo
, action
== SEND_TODISP
?
1303 CONV_NONE
:CONV_TOHDR
,
1304 action
== SEND_TODISP
?
1305 TD_ISPR
|TD_ICONV
:TD_ICONV
,
1309 } else if (*hp
->h_subject
) {
1310 if (mime_write(hp
->h_subject
,
1311 strlen(hp
->h_subject
),
1312 fo
, action
== SEND_TODISP
?
1313 CONV_NONE
:CONV_TOHDR
,
1314 action
== SEND_TODISP
?
1315 TD_ISPR
|TD_ICONV
:TD_ICONV
,
1321 fwrite("\n", sizeof (char), 1, fo
);
1323 if (value("bsdcompat") || value("bsdorder"))
1325 if (w
& GMSGID
&& stealthmua
<= 0)
1326 message_id(fo
, hp
), gotcha
++;
1327 if ((np
= hp
->h_ref
) != NULL
&& w
& GREF
) {
1328 fmt("References:", np
, fo
, 0, 1, 0);
1332 if (is_addr_invalid(np
, 0) == 0) {
1333 fprintf(fo
, "In-Reply-To: %s\n", np
->n_name
);
1338 if (w
& GUA
&& stealthmua
== 0)
1339 fprintf(fo
, "User-Agent: %s %s\n", uagent
, version
), gotcha
++;
1341 fputs("MIME-Version: 1.0\n", fo
), gotcha
++;
1342 if (hp
->h_attach
!= NULL
) {
1344 fprintf(fo
, "Content-Type: multipart/mixed;\n"
1345 " boundary=\"%s\"\n", send_boundary
);
1347 fprintf(fo
, "Content-Type: %s", contenttype
);
1349 fprintf(fo
, "; charset=%s", charset
);
1350 fprintf(fo
, "\nContent-Transfer-Encoding: %s\n",
1351 getencoding(convert
));
1354 if (gotcha
&& w
& GNL
)
1360 * Format the given header line to not exceed 72 characters.
1363 fmt(char *str
, struct name
*np
, FILE *fo
, int flags
, int dropinvalid
,
1371 } m
= (flags
& GCOMMA
) ? m_COMMA
: 0;
1376 fwrite(str
, sizeof *str
, strlen(str
), fo
);
1377 if ((flags
&GFILES
) == 0 && ! value("add-file-recipients") &&
1378 ((col
== 3 && ((asccasecmp(str
, "to:") == 0) ||
1379 asccasecmp(str
, "cc:") == 0)) ||
1380 (col
== 4 && asccasecmp(str
, "bcc:") == 0) ||
1382 asccasecmp(str
, "Resent-To:") == 0)))
1385 for (; np
!= NULL
; np
= np
->n_flink
) {
1386 if ((m
& m_NOPF
) && is_fileorpipe_addr(np
))
1388 if (is_addr_invalid(np
, ! dropinvalid
)) {
1394 if ((m
& (m_INIT
| m_COMMA
)) == (m_INIT
| m_COMMA
)) {
1399 len
= strlen(np
->n_fullname
);
1400 ++col
; /* The separating space */
1401 if ((m
& m_INIT
) && col
> 1 && col
+ len
> 72) {
1407 m
= (m
& ~m_CSEEN
) | m_INIT
;
1408 len
= mime_write(np
->n_fullname
,
1410 domime
?CONV_TOHDR_A
:CONV_NONE
,
1411 TD_ICONV
, NULL
, (size_t)0,
1420 * Rewrite a message for resending, adding the Resent-Headers.
1423 infix_resend(FILE *fi
, FILE *fo
, struct message
*mp
, struct name
*to
,
1427 char *buf
= NULL
, *cp
/*, *cp2*/;
1428 size_t c
, bufsize
= 0;
1429 struct name
*fromfield
= NULL
, *senderfield
= NULL
;
1433 * Write the Resent-Fields.
1436 fputs("Resent-", fo
);
1438 if ((cp
= myaddrs(NULL
)) != NULL
) {
1439 if (putname(cp
, GCOMMA
, SEND_MBOX
, NULL
,
1440 "Resent-From:", fo
, &fromfield
))
1443 if ((cp
= value("sender")) != NULL
) {
1444 if (putname(cp
, GCOMMA
, SEND_MBOX
, NULL
,
1445 "Resent-Sender:", fo
, &senderfield
))
1448 if (fmt("Resent-To:", to
, fo
, 1, 1, 0)) {
1453 if ((cp
= value("stealthmua")) == NULL
||
1454 strcmp(cp
, "noagent") == 0) {
1455 fputs("Resent-", fo
);
1456 message_id(fo
, NULL
);
1459 if (check_from_and_sender(fromfield
, senderfield
))
1462 * Write the original headers.
1465 if ((cp
= foldergets(&buf
, &bufsize
, &count
, &c
, fi
)) == NULL
)
1467 if (ascncasecmp("status: ", buf
, 8) != 0
1468 && strncmp("From ", buf
, 5) != 0) {
1469 fwrite(buf
, sizeof *buf
, c
, fo
);
1471 if (count
> 0 && *buf
== '\n')
1475 * Write the message body.
1478 if (foldergets(&buf
, &bufsize
, &count
, &c
, fi
) == NULL
)
1480 if (count
== 0 && *buf
== '\n')
1482 fwrite(buf
, sizeof *buf
, c
, fo
);
1487 perror(catgets(catd
, CATSET
, 188, "temporary mail file"));
1494 resend_msg(struct message
*mp
, struct name
*to
, int add_resent
)
1496 FILE *ibuf
, *nfo
, *nfi
;
1499 enum okay ok
= STOP
;
1501 memset(&head
, 0, sizeof head
);
1502 if ((to
= checkaddrs(to
)) == NULL
) {
1506 if ((nfo
= Ftemp(&tempMail
, "Rs", "w", 0600, 1)) == NULL
) {
1508 perror(catgets(catd
, CATSET
, 189, "temporary mail file"));
1511 if ((nfi
= Fopen(tempMail
, "r")) == NULL
) {
1518 if ((ibuf
= setinput(&mb
, mp
, NEED_BODY
)) == NULL
)
1521 to
= fixhead(&head
, to
);
1522 if (infix_resend(ibuf
, nfo
, mp
, head
.h_to
, add_resent
) != 0) {
1525 savedeadletter(nfi
);
1526 fputs(catgets(catd
, CATSET
, 190,
1527 ". . . message not sent.\n"), stderr
);
1535 to
= outof(to
, nfi
, &head
);
1537 savedeadletter(nfi
);
1539 if (count(to
) != 0) {
1540 if (value("record-resent") == NULL
||
1541 mightrecord(nfi
, to
, 0) == OKAY
)
1542 ok
= transfer(to
, nfi
, NULL
);
1543 } else if (senderr
== 0)