Support optional WANT_MD5 configuration
[s-mailx.git] / sendout.c
blob10d4c0d0ffd270041ddf0f5800b8970bc5c266ca
1 /*
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.
6 */
7 /*
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
13 * are met:
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
37 * SUCH DAMAGE.
40 #ifndef lint
41 #ifdef DOSCCS
42 static char sccsid[] = "@(#)sendout.c 2.100 (gritter) 3/1/09";
43 #endif
44 #endif /* not lint */
46 #include "rcv.h"
47 #include "extern.h"
48 #include <errno.h>
49 #include <sys/stat.h>
50 #include <fcntl.h>
51 #include <unistd.h>
52 #include <time.h>
55 * Mail -- a mail program
57 * Mail to others.
60 static char *send_boundary;
62 static char *getencoding(enum conversion convert);
63 static struct name *fixhead(struct header *hp, struct name *tolist);
64 static int put_signature(FILE *fo, int convert);
65 static int attach_file1(struct attachment *ap, FILE *fo, int dosign);
66 static int attach_file(struct attachment *ap, FILE *fo, int dosign);
67 static int attach_message(struct attachment *ap, FILE *fo, int dosign);
68 static int make_multipart(struct header *hp, int convert, FILE *fi, FILE *fo,
69 const char *contenttype, const char *charset, int dosign);
70 static FILE *infix(struct header *hp, FILE *fi, int dosign);
71 static int savemail(char *name, FILE *fi);
72 static int sendmail_internal(void *v, int recipient_record);
73 static enum okay transfer(struct name *to, struct name *mailargs, FILE *input,
74 struct header *hp);
75 static enum okay start_mta(struct name *to, struct name *mailargs, FILE *input,
76 struct header *hp);
77 static void message_id(FILE *fo, struct header *hp);
78 static int fmt(char *str, struct name *np, FILE *fo, int comma,
79 int dropinvalid, int domime);
80 static int infix_resend(FILE *fi, FILE *fo, struct message *mp,
81 struct name *to, int add_resent);
84 * Generate a boundary for MIME multipart messages.
86 char *
87 makeboundary(void)
89 static char bound[70];
90 time_t now;
92 time(&now);
93 snprintf(bound, sizeof bound, "=_%lx.%s", (long)now, getrandstring(48));
94 send_boundary = bound;
95 return send_boundary;
99 * Get an encoding flag based on the given string.
101 static char *
102 getencoding(enum conversion convert)
104 switch (convert) {
105 case CONV_7BIT:
106 return "7bit";
107 case CONV_8BIT:
108 return "8bit";
109 case CONV_TOQP:
110 return "quoted-printable";
111 case CONV_TOB64:
112 return "base64";
113 default:
114 break;
116 /*NOTREACHED*/
117 return NULL;
121 * Fix the header by glopping all of the expanded names from
122 * the distribution list into the appropriate fields.
124 static struct name *
125 fixhead(struct header *hp, struct name *tolist)
127 struct name *np;
129 hp->h_to = NULL;
130 hp->h_cc = NULL;
131 hp->h_bcc = NULL;
132 for (np = tolist; np != NULL; np = np->n_flink)
133 if ((np->n_type & GMASK) == GTO)
134 hp->h_to = cat(hp->h_to, ndup(np, GFULL));
135 else if ((np->n_type & GMASK) == GCC)
136 hp->h_cc = cat(hp->h_cc, ndup(np, GFULL));
137 else if ((np->n_type & GMASK) == GBCC)
138 hp->h_bcc = cat(hp->h_bcc, ndup(np, GFULL));
139 return tolist;
144 * Do not change, you get incorrect base64 encodings else!
146 #define INFIX_BUF 972
149 * Put the signature file at fo.
151 static int
152 put_signature(FILE *fo, int convert)
154 char *sig, buf[INFIX_BUF], c = '\n';
155 FILE *fsig;
156 size_t sz;
158 sig = value("signature");
159 if (sig == NULL || *sig == '\0')
160 return 0;
161 else
162 sig = expand(sig);
163 if ((fsig = Fopen(sig, "r")) == NULL) {
164 perror(sig);
165 return -1;
167 while ((sz = fread(buf, sizeof *buf, INFIX_BUF, fsig)) != 0) {
168 c = buf[sz - 1];
169 if (mime_write(buf, sz, fo, convert, TD_NONE,
170 NULL, (size_t)0, NULL, NULL)
171 == 0) {
172 perror(sig);
173 Fclose(fsig);
174 return -1;
177 if (ferror(fsig)) {
178 perror(sig);
179 Fclose(fsig);
180 return -1;
182 Fclose(fsig);
183 if (c != '\n')
184 putc('\n', fo);
185 return 0;
189 * Write an attachment to the file buffer, converting to MIME.
191 static int
192 attach_file1(struct attachment *ap, FILE *fo, int dosign)
194 FILE *fi;
195 char *charset = NULL, *contenttype = NULL, *basename;
196 enum conversion convert = CONV_TOB64;
197 int err = 0;
198 enum mimeclean isclean;
199 size_t sz;
200 char *buf;
201 size_t bufsize, count;
202 int lastc = EOF;
203 #ifdef HAVE_ICONV
204 char *tcs;
205 #endif
207 if ((fi = Fopen(ap->a_name, "r")) == NULL) {
208 perror(ap->a_name);
209 return -1;
211 if ((basename = strrchr(ap->a_name, '/')) == NULL)
212 basename = ap->a_name;
213 else
214 basename++;
215 if (ap->a_content_type)
216 contenttype = ap->a_content_type;
217 else
218 contenttype = mime_filecontent(basename);
219 if (ap->a_charset)
220 charset = ap->a_charset;
221 convert = get_mime_convert(fi, &contenttype, &charset, &isclean,
222 dosign);
223 fprintf(fo,
224 "\n--%s\n"
225 "Content-Type: %s",
226 send_boundary, contenttype);
227 if (charset == NULL)
228 putc('\n', fo);
229 else
230 fprintf(fo, ";\n charset=%s\n", charset);
231 if (ap->a_content_disposition == NULL)
232 ap->a_content_disposition = "attachment";
233 fprintf(fo, "Content-Transfer-Encoding: %s\n"
234 "Content-Disposition: %s;\n"
235 " filename=\"",
236 getencoding(convert),
237 ap->a_content_disposition);
238 mime_write(basename, strlen(basename), fo,
239 CONV_TOHDR, TD_NONE, NULL, (size_t)0, NULL, NULL);
240 fwrite("\"\n", sizeof (char), 2, fo);
241 if (ap->a_content_id)
242 fprintf(fo, "Content-ID: %s\n", ap->a_content_id);
243 if (ap->a_content_description)
244 fprintf(fo, "Content-Description: %s\n",
245 ap->a_content_description);
246 putc('\n', fo);
247 #ifdef HAVE_ICONV
248 if (iconvd != (iconv_t)-1) {
249 iconv_close(iconvd);
250 iconvd = (iconv_t)-1;
252 tcs = gettcharset();
253 if ((isclean & (MIME_HASNUL|MIME_CTRLCHAR)) == 0 &&
254 ascncasecmp(contenttype, "text/", 5) == 0 &&
255 isclean & MIME_HIGHBIT &&
256 charset != NULL) {
257 if ((iconvd = iconv_open_ft(charset, tcs)) == (iconv_t)-1 &&
258 errno != 0) {
259 if (errno == EINVAL)
260 fprintf(stderr, catgets(catd, CATSET, 179,
261 "Cannot convert from %s to %s\n"), tcs, charset);
262 else
263 perror("iconv_open");
264 Fclose(fi);
265 return -1;
268 #endif /* HAVE_ICONV */
269 buf = smalloc(bufsize = INFIX_BUF);
270 if (convert == CONV_TOQP
271 #ifdef HAVE_ICONV
272 || iconvd != (iconv_t)-1
273 #endif
275 fflush(fi);
276 count = fsize(fi);
278 for (;;) {
279 if (convert == CONV_TOQP
280 #ifdef HAVE_ICONV
281 || iconvd != (iconv_t)-1
282 #endif
284 if (fgetline(&buf, &bufsize, &count, &sz, fi, 0)
285 == NULL)
286 break;
287 } else {
288 if ((sz = fread(buf, sizeof *buf, bufsize, fi)) == 0)
289 break;
291 lastc = buf[sz-1];
292 if (mime_write(buf, sz, fo, convert, TD_ICONV,
293 NULL, (size_t)0, NULL, NULL) == 0)
294 err = -1;
296 if (convert == CONV_TOQP && lastc != '\n')
297 fwrite("=\n", 1, 2, fo);
298 if (ferror(fi))
299 err = -1;
300 Fclose(fi);
301 free(buf);
302 return err;
306 * Try out different character set conversions to attach a file.
308 static int
309 attach_file(struct attachment *ap, FILE *fo, int dosign)
311 char *_wantcharset, *charsets, *ncs;
312 size_t offs = ftell(fo);
314 if (ap->a_charset || (charsets = value("sendcharsets")) == NULL)
315 return attach_file1(ap, fo, dosign);
316 _wantcharset = wantcharset;
317 wantcharset = savestr(charsets);
318 loop: if ((ncs = strchr(wantcharset, ',')) != NULL)
319 *ncs++ = '\0';
320 try: if (attach_file1(ap, fo, dosign) != 0) {
321 if (errno == EILSEQ || errno == EINVAL) {
322 if (ncs && *ncs) {
323 wantcharset = ncs;
324 clearerr(fo);
325 fseek(fo, offs, SEEK_SET);
326 goto loop;
328 if (wantcharset) {
329 if (wantcharset == (char *)-1)
330 wantcharset = NULL;
331 else {
332 wantcharset = (char *)-1;
333 clearerr(fo);
334 fseek(fo, offs, SEEK_SET);
335 goto try;
340 wantcharset = _wantcharset;
341 return 0;
345 * Attach a message to the file buffer.
347 static int
348 attach_message(struct attachment *ap, FILE *fo, int dosign)
350 struct message *mp;
351 (void)dosign;
353 fprintf(fo, "\n--%s\n"
354 "Content-Type: message/rfc822\n"
355 "Content-Disposition: inline\n\n", send_boundary);
356 mp = &message[ap->a_msgno - 1];
357 touch(mp);
358 if (send(mp, fo, 0, NULL, SEND_RFC822, NULL) < 0)
359 return -1;
360 return 0;
364 * Generate the body of a MIME multipart message.
366 static int
367 make_multipart(struct header *hp, int convert, FILE *fi, FILE *fo,
368 const char *contenttype, const char *charset, int dosign)
370 struct attachment *att;
372 fputs("This is a multi-part message in MIME format.\n", fo);
373 if (fsize(fi) != 0) {
374 char *buf, c = '\n';
375 size_t sz, bufsize, count;
377 fprintf(fo, "\n--%s\n", send_boundary);
378 fprintf(fo, "Content-Type: %s", contenttype);
379 if (charset)
380 fprintf(fo, "; charset=%s", charset);
381 fprintf(fo, "\nContent-Transfer-Encoding: %s\n"
382 "Content-Disposition: inline\n\n",
383 getencoding(convert));
384 buf = smalloc(bufsize = INFIX_BUF);
385 if (convert == CONV_TOQP
386 #ifdef HAVE_ICONV
387 || iconvd != (iconv_t)-1
388 #endif /* HAVE_ICONV */
390 fflush(fi);
391 count = fsize(fi);
393 for (;;) {
394 if (convert == CONV_TOQP
395 #ifdef HAVE_ICONV
396 || iconvd != (iconv_t)-1
397 #endif /* HAVE_ICONV */
399 if (fgetline(&buf, &bufsize, &count, &sz, fi, 0)
400 == NULL)
401 break;
402 } else {
403 sz = fread(buf, sizeof *buf, bufsize, fi);
404 if (sz == 0)
405 break;
407 c = buf[sz - 1];
408 if (mime_write(buf, sz, fo, convert,
409 TD_ICONV, NULL, (size_t)0,
410 NULL, NULL) == 0) {
411 free(buf);
412 return -1;
415 free(buf);
416 if (ferror(fi))
417 return -1;
418 if (c != '\n')
419 putc('\n', fo);
420 if (charset != NULL)
421 put_signature(fo, convert);
423 for (att = hp->h_attach; att != NULL; att = att->a_flink) {
424 if (att->a_msgno) {
425 if (attach_message(att, fo, dosign) != 0)
426 return -1;
427 } else {
428 if (attach_file(att, fo, dosign) != 0)
429 return -1;
432 /* the final boundary with two attached dashes */
433 fprintf(fo, "\n--%s--\n", send_boundary);
434 return 0;
438 * Prepend a header in front of the collected stuff
439 * and return the new file.
441 static FILE *
442 infix(struct header *hp, FILE *fi, int dosign)
444 FILE *nfo, *nfi;
445 char *tempMail;
446 #ifdef HAVE_ICONV
447 char *tcs, *convhdr = NULL;
448 #endif
449 enum mimeclean isclean;
450 enum conversion convert;
451 char *charset = NULL, *contenttype = NULL;
452 int lastc = EOF;
454 if ((nfo = Ftemp(&tempMail, "Rs", "w", 0600, 1)) == NULL) {
455 perror(catgets(catd, CATSET, 178, "temporary mail file"));
456 return(NULL);
458 if ((nfi = Fopen(tempMail, "r")) == NULL) {
459 perror(tempMail);
460 Fclose(nfo);
461 return(NULL);
463 rm(tempMail);
464 Ftfree(&tempMail);
465 convert = get_mime_convert(fi, &contenttype, &charset,
466 &isclean, dosign);
467 #ifdef HAVE_ICONV
468 tcs = gettcharset();
469 if ((convhdr = need_hdrconv(hp, GTO|GSUBJECT|GCC|GBCC|GIDENT)) != 0) {
470 if (iconvd != (iconv_t)-1)
471 iconv_close(iconvd);
472 if ((iconvd = iconv_open_ft(convhdr, tcs)) == (iconv_t)-1
473 && errno != 0) {
474 if (errno == EINVAL)
475 fprintf(stderr, catgets(catd, CATSET, 179,
476 "Cannot convert from %s to %s\n"), tcs, convhdr);
477 else
478 perror("iconv_open");
479 Fclose(nfo);
480 return NULL;
483 #endif /* HAVE_ICONV */
484 if (puthead(hp, nfo,
485 GTO|GSUBJECT|GCC|GBCC|GNL|GCOMMA|GUA|GMIME
486 |GMSGID|GIDENT|GREF|GDATE,
487 SEND_MBOX, convert, contenttype, charset)) {
488 Fclose(nfo);
489 Fclose(nfi);
490 #ifdef HAVE_ICONV
491 if (iconvd != (iconv_t)-1) {
492 iconv_close(iconvd);
493 iconvd = (iconv_t)-1;
495 #endif
496 return NULL;
498 #ifdef HAVE_ICONV
499 if (convhdr && iconvd != (iconv_t)-1) {
500 iconv_close(iconvd);
501 iconvd = (iconv_t)-1;
503 if ((isclean & (MIME_HASNUL|MIME_CTRLCHAR)) == 0 &&
504 ascncasecmp(contenttype, "text/", 5) == 0 &&
505 isclean & MIME_HIGHBIT &&
506 charset != NULL) {
507 if (iconvd != (iconv_t)-1)
508 iconv_close(iconvd);
509 if ((iconvd = iconv_open_ft(charset, tcs)) == (iconv_t)-1
510 && errno != 0) {
511 if (errno == EINVAL)
512 fprintf(stderr, catgets(catd, CATSET, 179,
513 "Cannot convert from %s to %s\n"), tcs, charset);
514 else
515 perror("iconv_open");
516 Fclose(nfo);
517 return NULL;
520 #endif
521 if (hp->h_attach != NULL) {
522 if (make_multipart(hp, convert, fi, nfo,
523 contenttype, charset, dosign) != 0) {
524 Fclose(nfo);
525 Fclose(nfi);
526 #ifdef HAVE_ICONV
527 if (iconvd != (iconv_t)-1) {
528 iconv_close(iconvd);
529 iconvd = (iconv_t)-1;
531 #endif
532 return NULL;
534 } else {
535 size_t sz, bufsize, count;
536 char *buf;
538 if (convert == CONV_TOQP
539 #ifdef HAVE_ICONV
540 || iconvd != (iconv_t)-1
541 #endif /* HAVE_ICONV */
543 fflush(fi);
544 count = fsize(fi);
546 buf = smalloc(bufsize = INFIX_BUF);
547 for (;;) {
548 if (convert == CONV_TOQP
549 #ifdef HAVE_ICONV
550 || iconvd != (iconv_t)-1
551 #endif /* HAVE_ICONV */
553 if (fgetline(&buf, &bufsize, &count, &sz, fi, 0)
554 == NULL)
555 break;
556 } else {
557 sz = fread(buf, sizeof *buf, bufsize, fi);
558 if (sz == 0)
559 break;
561 lastc = buf[sz - 1];
562 if (mime_write(buf, sz, nfo, convert,
563 TD_ICONV, NULL, (size_t)0,
564 NULL, NULL) == 0) {
565 Fclose(nfo);
566 Fclose(nfi);
567 #ifdef HAVE_ICONV
568 if (iconvd != (iconv_t)-1) {
569 iconv_close(iconvd);
570 iconvd = (iconv_t)-1;
572 #endif
573 free(buf);
574 return NULL;
577 if (convert == CONV_TOQP && lastc != '\n')
578 fwrite("=\n", 1, 2, nfo);
579 free(buf);
580 if (ferror(fi)) {
581 Fclose(nfo);
582 Fclose(nfi);
583 #ifdef HAVE_ICONV
584 if (iconvd != (iconv_t)-1) {
585 iconv_close(iconvd);
586 iconvd = (iconv_t)-1;
588 #endif
589 return NULL;
591 if (charset != NULL)
592 put_signature(nfo, convert);
594 #ifdef HAVE_ICONV
595 if (iconvd != (iconv_t)-1) {
596 iconv_close(iconvd);
597 iconvd = (iconv_t)-1;
599 #endif
600 fflush(nfo);
601 if (ferror(nfo)) {
602 perror(catgets(catd, CATSET, 180, "temporary mail file"));
603 Fclose(nfo);
604 Fclose(nfi);
605 return NULL;
607 Fclose(nfo);
608 Fclose(fi);
609 fflush(nfi);
610 rewind(nfi);
611 return(nfi);
615 * Save the outgoing mail on the passed file.
618 /*ARGSUSED*/
619 static int
620 savemail(char *name, FILE *fi)
622 FILE *fo;
623 char *buf;
624 size_t bufsize, buflen, count;
625 char *p;
626 time_t now;
627 int prependnl = 0;
628 int error = 0;
630 buf = smalloc(bufsize = LINESIZE);
631 time(&now);
632 if ((fo = Zopen(name, "a+", NULL)) == NULL) {
633 if ((fo = Zopen(name, "wx", NULL)) == NULL) {
634 perror(name);
635 free(buf);
636 return (-1);
638 } else {
639 if (fseek(fo, -2L, SEEK_END) == 0) {
640 switch (fread(buf, sizeof *buf, 2, fo)) {
641 case 2:
642 if (buf[1] != '\n') {
643 prependnl = 1;
644 break;
646 /*FALLTHRU*/
647 case 1:
648 if (buf[0] != '\n')
649 prependnl = 1;
650 break;
651 default:
652 if (ferror(fo)) {
653 perror(name);
654 free(buf);
655 return -1;
658 fflush(fo);
659 if (prependnl) {
660 putc('\n', fo);
661 fflush(fo);
665 fprintf(fo, "From %s %s", myname, ctime(&now));
666 buflen = 0;
667 fflush(fi);
668 rewind(fi);
669 count = fsize(fi);
670 while (fgetline(&buf, &bufsize, &count, &buflen, fi, 0) != NULL) {
671 if (*buf == '>') {
672 p = buf + 1;
673 while (*p == '>')
674 p++;
675 if (strncmp(p, "From ", 5) == 0)
676 /* we got a masked From line */
677 putc('>', fo);
678 } else if (strncmp(buf, "From ", 5) == 0)
679 putc('>', fo);
680 fwrite(buf, sizeof *buf, buflen, fo);
682 if (buflen && *(buf + buflen - 1) != '\n')
683 putc('\n', fo);
684 putc('\n', fo);
685 fflush(fo);
686 if (ferror(fo)) {
687 perror(name);
688 error = -1;
690 if (Fclose(fo) != 0)
691 error = -1;
692 fflush(fi);
693 rewind(fi);
694 free(buf);
695 return error;
699 * Interface between the argument list and the mail1 routine
700 * which does all the dirty work.
702 int
703 mail(struct name *to, struct name *cc, struct name *bcc,
704 struct name *smopts, char *subject, struct attachment *attach,
705 char *quotefile, int recipient_record, int tflag, int Eflag)
707 struct header head;
708 struct str in, out;
710 memset(&head, 0, sizeof head);
711 /* The given subject may be in RFC1522 format. */
712 if (subject != NULL) {
713 in.s = subject;
714 in.l = strlen(subject);
715 mime_fromhdr(&in, &out, TD_ISPR | TD_ICONV);
716 head.h_subject = out.s;
718 if (tflag == 0) {
719 head.h_to = to;
720 head.h_cc = cc;
721 head.h_bcc = bcc;
723 head.h_attach = attach;
724 head.h_smopts = smopts;
725 mail1(&head, 0, NULL, quotefile, recipient_record, 0, tflag, Eflag);
726 if (subject != NULL)
727 free(out.s);
728 return(0);
732 * Send mail to a bunch of user names. The interface is through
733 * the mail routine below.
735 static int
736 sendmail_internal(void *v, int recipient_record)
738 int Eflag;
739 char *str = v;
740 struct header head;
742 memset(&head, 0, sizeof head);
743 head.h_to = extract(str, GTO|GFULL);
744 Eflag = value("skipemptybody") != NULL;
745 mail1(&head, 0, NULL, NULL, recipient_record, 0, 0, Eflag);
746 return(0);
749 int
750 sendmail(void *v)
752 return sendmail_internal(v, 0);
755 int
756 Sendmail(void *v)
758 return sendmail_internal(v, 1);
761 static enum okay
762 transfer(struct name *to, struct name *mailargs, FILE *input, struct header *hp)
764 char o[LINESIZE], *cp;
765 struct name *np, *nt;
766 int cnt = 0;
767 FILE *ef;
768 enum okay ok = OKAY;
770 np = to;
771 while (np) {
772 snprintf(o, sizeof o, "smime-encrypt-%s", np->n_name);
773 if ((cp = value(o)) != NULL) {
774 if ((ef = smime_encrypt(input, cp, np->n_name)) != 0) {
775 nt = nalloc(np->n_name,
776 np->n_type & ~(GFULL|GSKIN));
777 if (start_mta(nt, mailargs, ef, hp) != OKAY)
778 ok = STOP;
779 Fclose(ef);
780 } else {
781 fprintf(stderr, "Message not sent to <%s>\n",
782 np->n_name);
783 senderr++;
785 rewind(input);
786 if (np->n_flink)
787 np->n_flink->n_blink = np->n_blink;
788 if (np->n_blink)
789 np->n_blink->n_flink = np->n_flink;
790 if (np == to)
791 to = np->n_flink;
792 np = np->n_flink;
793 } else {
794 cnt++;
795 np = np->n_flink;
798 if (cnt) {
799 if (value("smime-force-encryption") ||
800 start_mta(to, mailargs, input, hp) != OKAY)
801 ok = STOP;
803 return ok;
807 * Start the Mail Transfer Agent
808 * mailing to namelist and stdin redirected to input.
810 static enum okay
811 start_mta(struct name *to, struct name *mailargs, FILE *input,
812 struct header *hp)
814 char **args = NULL, **t;
815 pid_t pid;
816 sigset_t nset;
817 char *cp, *smtp;
818 char *user = NULL, *password = NULL, *skinned = NULL;
819 enum okay ok = STOP;
820 #ifdef USE_SMTP
821 struct termios otio;
822 int reset_tio;
823 #endif
825 if ((smtp = value("smtp")) == NULL) {
826 args = unpack(cat(mailargs, to));
827 if (debug || value("debug")) {
828 printf(catgets(catd, CATSET, 181,
829 "Sendmail arguments:"));
830 for (t = args; *t != NULL; t++)
831 printf(" \"%s\"", *t);
832 printf("\n");
833 return OKAY;
836 #ifdef USE_SMTP
837 if (smtp != NULL) {
838 skinned = skin(myorigin(hp));
839 if ((user = smtp_auth_var("-user", skinned)) != NULL &&
840 (password = smtp_auth_var("-password",
841 skinned)) == NULL)
842 password = getpassword(&otio, &reset_tio, NULL);
844 #endif
846 * Fork, set up the temporary mail file as standard
847 * input for "mail", and exec with the user list we generated
848 * far above.
850 if ((pid = fork()) == -1) {
851 perror("fork");
852 savedeadletter(input);
853 senderr++;
854 return STOP;
856 if (pid == 0) {
857 sigemptyset(&nset);
858 sigaddset(&nset, SIGHUP);
859 sigaddset(&nset, SIGINT);
860 sigaddset(&nset, SIGQUIT);
861 sigaddset(&nset, SIGTSTP);
862 sigaddset(&nset, SIGTTIN);
863 sigaddset(&nset, SIGTTOU);
864 freopen("/dev/null", "r", stdin);
865 if (smtp != NULL) {
866 prepare_child(&nset, 0, 1);
867 if (smtp_mta(smtp, to, input, hp,
868 user, password, skinned) == 0)
869 _exit(0);
870 } else {
871 prepare_child(&nset, fileno(input), -1);
872 if ((cp = value("sendmail")) != NULL)
873 cp = expand(cp);
874 else
875 cp = SENDMAIL;
876 execv(cp, args);
877 perror(cp);
879 savedeadletter(input);
880 fputs(catgets(catd, CATSET, 182,
881 ". . . message not sent.\n"), stderr);
882 _exit(1);
884 if (value("verbose") != NULL || value("sendwait") || debug
885 || value("debug")) {
886 if (wait_child(pid) == 0)
887 ok = OKAY;
888 else
889 senderr++;
890 } else {
891 ok = OKAY;
892 free_child(pid);
894 return ok;
898 * Record outgoing mail if instructed to do so.
900 static enum okay
901 mightrecord(FILE *fp, struct name *to, int recipient_record)
903 char *cp, *cq, *ep;
905 if (recipient_record) {
906 cq = skin(to->n_name);
907 cp = salloc(strlen(cq) + 1);
908 strcpy(cp, cq);
909 for (cq = cp; *cq && *cq != '@'; cq++);
910 *cq = '\0';
911 } else
912 cp = value("record");
913 if (cp != NULL) {
914 ep = expand(cp);
915 if (value("outfolder") && *ep != '/' && *ep != '+' &&
916 which_protocol(ep) == PROTO_FILE) {
917 cq = salloc(strlen(cp) + 2);
918 cq[0] = '+';
919 strcpy(&cq[1], cp);
920 cp = cq;
921 ep = expand(cp);
923 if (savemail(ep, fp) != 0) {
924 fprintf(stderr,
925 "Error while saving message to %s - "
926 "message not sent\n", ep);
927 rewind(fp);
928 exit_status |= 1;
929 savedeadletter(fp);
930 return STOP;
933 return OKAY;
937 * Mail a message on standard input to the people indicated
938 * in the passed header. (Internal interface).
940 enum okay
941 mail1(struct header *hp, int printheaders, struct message *quote,
942 char *quotefile, int recipient_record, int doprefix, int tflag,
943 int Eflag)
945 struct name *to;
946 FILE *mtf, *nmtf;
947 enum okay ok = STOP;
948 int dosign = -1;
949 char *charsets, *ncs = NULL, *cp;
951 #ifdef notdef
952 if ((hp->h_to = checkaddrs(hp->h_to)) == NULL) {
953 senderr++;
954 return STOP;
956 #endif
957 if ((cp = value("autocc")) != NULL && *cp)
958 hp->h_cc = cat(hp->h_cc, checkaddrs(sextract(cp, GCC|GFULL)));
959 if ((cp = value("autobcc")) != NULL && *cp)
960 hp->h_bcc = cat(hp->h_bcc,
961 checkaddrs(sextract(cp, GBCC|GFULL)));
963 * Collect user's mail from standard input.
964 * Get the result as mtf.
966 if ((mtf = collect(hp, printheaders, quote, quotefile, doprefix,
967 tflag)) == NULL)
968 return STOP;
969 if (value("interactive") != NULL) {
970 if (((value("bsdcompat") || value("askatend"))
971 && (value("askcc") != NULL ||
972 value("askbcc") != NULL)) ||
973 value("askattach") != NULL ||
974 value("asksign") != NULL) {
975 if (value("askcc") != NULL)
976 grabh(hp, GCC, 1);
977 if (value("askbcc") != NULL)
978 grabh(hp, GBCC, 1);
979 if (value("askattach") != NULL)
980 hp->h_attach = edit_attachments(hp->h_attach);
981 if (value("asksign") != NULL)
982 dosign = yorn("Sign this message (y/n)? ");
983 } else {
984 printf(catgets(catd, CATSET, 183, "EOT\n"));
985 fflush(stdout);
988 if (fsize(mtf) == 0) {
989 if (Eflag)
990 goto out;
991 if (hp->h_subject == NULL)
992 printf(catgets(catd, CATSET, 184,
993 "No message, no subject; hope that's ok\n"));
994 else if (value("bsdcompat") || value("bsdmsgs"))
995 printf(catgets(catd, CATSET, 185,
996 "Null message body; hope that's ok\n"));
998 if (dosign < 0) {
999 if (value("smime-sign") != NULL)
1000 dosign = 1;
1001 else
1002 dosign = 0;
1005 * Now, take the user names from the combined
1006 * to and cc lists and do all the alias
1007 * processing.
1009 senderr = 0;
1010 if ((to = usermap(cat(hp->h_bcc, cat(hp->h_to, hp->h_cc)))) == NULL) {
1011 printf(catgets(catd, CATSET, 186, "No recipients specified\n"));
1012 senderr++;
1014 to = fixhead(hp, to);
1015 if (hp->h_charset) {
1016 wantcharset = hp->h_charset;
1017 goto try;
1019 hloop: wantcharset = NULL;
1020 if ((charsets = value("sendcharsets")) != NULL) {
1021 wantcharset = savestr(charsets);
1022 loop: if ((ncs = strchr(wantcharset, ',')) != NULL)
1023 *ncs++ = '\0';
1025 try: if ((nmtf = infix(hp, mtf, dosign)) == NULL) {
1026 if (hp->h_charset && (errno == EILSEQ || errno == EINVAL)) {
1027 rewind(mtf);
1028 hp->h_charset = NULL;
1029 goto hloop;
1031 if (errno == EILSEQ || errno == EINVAL) {
1032 if (ncs && *ncs) {
1033 rewind(mtf);
1034 wantcharset = ncs;
1035 goto loop;
1037 if (wantcharset && value("interactive") == NULL) {
1038 if (wantcharset == (char *)-1)
1039 wantcharset = NULL;
1040 else {
1041 rewind(mtf);
1042 wantcharset = (char *)-1;
1043 goto try;
1047 /* fprintf(stderr, ". . . message lost, sorry.\n"); */
1048 perror("");
1049 fail: senderr++;
1050 rewind(mtf);
1051 savedeadletter(mtf);
1052 fputs(catgets(catd, CATSET, 187,
1053 ". . . message not sent.\n"), stderr);
1054 return STOP;
1056 mtf = nmtf;
1057 if (dosign) {
1058 if ((nmtf = smime_sign(mtf, hp)) == NULL)
1059 goto fail;
1060 Fclose(mtf);
1061 mtf = nmtf;
1064 * Look through the recipient list for names with /'s
1065 * in them which we write to as files directly.
1067 to = outof(to, mtf, hp);
1068 if (senderr)
1069 savedeadletter(mtf);
1070 to = elide(to);
1071 if (count(to) == 0) {
1072 if (senderr == 0)
1073 ok = OKAY;
1074 goto out;
1076 if (mightrecord(mtf, to, recipient_record) != OKAY)
1077 goto out;
1078 ok = transfer(to, hp->h_smopts, mtf, hp);
1079 out:
1080 Fclose(mtf);
1081 return ok;
1085 * Create a Message-Id: header field.
1086 * Use either the host name or the from address.
1088 static void
1089 message_id(FILE *fo, struct header *hp)
1091 char *cp;
1092 time_t now;
1094 time(&now);
1095 if ((cp = value("hostname")) != NULL)
1096 fprintf(fo, "Message-ID: <%lx.%s@%s>\n",
1097 (long)now, getrandstring(24), cp);
1098 else if ((cp = skin(myorigin(hp))) != NULL && strchr(cp, '@') != NULL)
1099 fprintf(fo, "Message-ID: <%lx.%s%%%s>\n",
1100 (long)now, getrandstring(16), cp);
1103 static const char *weekday_names[] = {
1104 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
1107 const char *month_names[] = {
1108 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
1109 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", NULL
1113 * Create a Date: header field.
1114 * We compare the localtime() and gmtime() results to get the timezone,
1115 * because numeric timezones are easier to read and because $TZ is
1116 * not set on most GNU systems.
1119 mkdate(FILE *fo, const char *field)
1121 time_t t;
1122 struct tm *tmptr;
1123 int tzdiff, tzdiff_hour, tzdiff_min;
1125 time(&t);
1126 tzdiff = t - mktime(gmtime(&t));
1127 tzdiff_hour = (int)(tzdiff / 60);
1128 tzdiff_min = tzdiff_hour % 60;
1129 tzdiff_hour /= 60;
1130 tmptr = localtime(&t);
1131 if (tmptr->tm_isdst > 0)
1132 tzdiff_hour++;
1133 return fprintf(fo, "%s: %s, %02d %s %04d %02d:%02d:%02d %+05d\n",
1134 field,
1135 weekday_names[tmptr->tm_wday],
1136 tmptr->tm_mday, month_names[tmptr->tm_mon],
1137 tmptr->tm_year + 1900, tmptr->tm_hour,
1138 tmptr->tm_min, tmptr->tm_sec,
1139 tzdiff_hour * 100 + tzdiff_min);
1142 static enum okay
1143 putname(char *line, enum gfield w, enum sendaction action, int *gotcha,
1144 char *prefix, FILE *fo, struct name **xp)
1146 struct name *np;
1148 np = sextract(line, GEXTRA|GFULL);
1149 if (xp)
1150 *xp = np;
1151 if (np == NULL)
1152 return 0;
1153 if (fmt(prefix, np, fo, w&(GCOMMA|GFILES), 0, action != SEND_TODISP))
1154 return 1;
1155 if (gotcha)
1156 (*gotcha)++;
1157 return 0;
1160 #define FMT_CC_AND_BCC { \
1161 if (hp->h_cc != NULL && w & GCC) { \
1162 if (fmt("Cc:", hp->h_cc, fo, \
1163 w&(GCOMMA|GFILES), 0, \
1164 action!=SEND_TODISP)) \
1165 return 1; \
1166 gotcha++; \
1168 if (hp->h_bcc != NULL && w & GBCC) { \
1169 if (fmt("Bcc:", hp->h_bcc, fo, \
1170 w&(GCOMMA|GFILES), 0, \
1171 action!=SEND_TODISP)) \
1172 return 1; \
1173 gotcha++; \
1177 * Dump the to, subject, cc header on the
1178 * passed file buffer.
1181 puthead(struct header *hp, FILE *fo, enum gfield w,
1182 enum sendaction action, enum conversion convert,
1183 char *contenttype, char *charset)
1185 int gotcha;
1186 char *addr/*, *cp*/;
1187 int stealthmua;
1188 struct name *np, *fromfield = NULL, *senderfield = NULL;
1191 if (value("stealthmua"))
1192 stealthmua = 1;
1193 else
1194 stealthmua = 0;
1195 gotcha = 0;
1196 if (w & GDATE) {
1197 mkdate(fo, "Date"), gotcha++;
1199 if (w & GIDENT) {
1200 if (hp->h_from != NULL) {
1201 if (fmt("From:", hp->h_from, fo, w&(GCOMMA|GFILES), 0,
1202 action!=SEND_TODISP))
1203 return 1;
1204 gotcha++;
1205 fromfield = hp->h_from;
1206 } else if ((addr = myaddrs(hp)) != NULL)
1207 if (putname(addr, w, action, &gotcha, "From:", fo,
1208 &fromfield))
1209 return 1;
1210 if (((addr = hp->h_organization) != NULL ||
1211 (addr = value("ORGANIZATION")) != NULL)
1212 && strlen(addr) > 0) {
1213 fwrite("Organization: ", sizeof (char), 14, fo);
1214 if (mime_write(addr, strlen(addr), fo,
1215 action == SEND_TODISP ?
1216 CONV_NONE:CONV_TOHDR,
1217 action == SEND_TODISP ?
1218 TD_ISPR|TD_ICONV:TD_ICONV,
1219 NULL, (size_t)0,
1220 NULL, NULL) == 0)
1221 return 1;
1222 gotcha++;
1223 putc('\n', fo);
1225 if (hp->h_replyto != NULL) {
1226 if (fmt("Reply-To:", hp->h_replyto, fo,
1227 w&(GCOMMA|GFILES), 0,
1228 action!=SEND_TODISP))
1229 return 1;
1230 gotcha++;
1231 } else if ((addr = value("replyto")) != NULL)
1232 if (putname(addr, w, action, &gotcha, "Reply-To:", fo,
1233 NULL))
1234 return 1;
1235 if (hp->h_sender != NULL) {
1236 if (fmt("Sender:", hp->h_sender, fo,
1237 w&(GCOMMA|GFILES), 0,
1238 action!=SEND_TODISP))
1239 return 1;
1240 gotcha++;
1241 senderfield = hp->h_sender;
1242 } else if ((addr = value("sender")) != NULL)
1243 if (putname(addr, w, action, &gotcha, "Sender:", fo,
1244 &senderfield))
1245 return 1;
1246 if (check_from_and_sender(fromfield, senderfield))
1247 return 1;
1249 if (hp->h_to != NULL && w & GTO) {
1250 if (fmt("To:", hp->h_to, fo, w&(GCOMMA|GFILES), 0,
1251 action!=SEND_TODISP))
1252 return 1;
1253 gotcha++;
1255 if (value("bsdcompat") == NULL && value("bsdorder") == NULL)
1256 FMT_CC_AND_BCC
1257 if (hp->h_subject != NULL && w & GSUBJECT) {
1258 fwrite("Subject: ", sizeof (char), 9, fo);
1259 if (ascncasecmp(hp->h_subject, "re: ", 4) == 0) {
1260 fwrite("Re: ", sizeof (char), 4, fo);
1261 if (strlen(hp->h_subject + 4) > 0 &&
1262 mime_write(hp->h_subject + 4,
1263 strlen(hp->h_subject + 4),
1264 fo, action == SEND_TODISP ?
1265 CONV_NONE:CONV_TOHDR,
1266 action == SEND_TODISP ?
1267 TD_ISPR|TD_ICONV:TD_ICONV,
1268 NULL, (size_t)0,
1269 NULL, NULL) == 0)
1270 return 1;
1271 } else if (*hp->h_subject) {
1272 if (mime_write(hp->h_subject,
1273 strlen(hp->h_subject),
1274 fo, action == SEND_TODISP ?
1275 CONV_NONE:CONV_TOHDR,
1276 action == SEND_TODISP ?
1277 TD_ISPR|TD_ICONV:TD_ICONV,
1278 NULL, (size_t)0,
1279 NULL, NULL) == 0)
1280 return 1;
1282 gotcha++;
1283 fwrite("\n", sizeof (char), 1, fo);
1285 if (value("bsdcompat") || value("bsdorder"))
1286 FMT_CC_AND_BCC
1287 if (w & GMSGID && stealthmua == 0)
1288 message_id(fo, hp), gotcha++;
1289 if (hp->h_ref != NULL && w & GREF) {
1290 fmt("References:", hp->h_ref, fo, 0, 1, 0);
1291 if ((np = hp->h_ref) != NULL && np->n_name) {
1292 while (np->n_flink)
1293 np = np->n_flink;
1294 if (mime_name_invalid(np, 0) == 0) {
1295 fprintf(fo, "In-Reply-To: %s\n", np->n_name);
1296 gotcha++;
1300 if (w & GUA && stealthmua == 0)
1301 fprintf(fo, "User-Agent: S-nail %s\n",
1302 version), gotcha++;
1303 if (w & GMIME) {
1304 fputs("MIME-Version: 1.0\n", fo), gotcha++;
1305 if (hp->h_attach != NULL) {
1306 makeboundary();
1307 fprintf(fo, "Content-Type: multipart/mixed;\n"
1308 " boundary=\"%s\"\n", send_boundary);
1309 } else {
1310 fprintf(fo, "Content-Type: %s", contenttype);
1311 if (charset)
1312 fprintf(fo, "; charset=%s", charset);
1313 fprintf(fo, "\nContent-Transfer-Encoding: %s\n",
1314 getencoding(convert));
1317 if (gotcha && w & GNL)
1318 putc('\n', fo);
1319 return(0);
1323 * Format the given header line to not exceed 72 characters.
1325 static int
1326 fmt(char *str, struct name *np, FILE *fo, int flags, int dropinvalid,
1327 int domime)
1329 int col, len, count = 0;
1330 int is_to = 0, comma;
1332 comma = flags&GCOMMA ? 1 : 0;
1333 col = strlen(str);
1334 if (col) {
1335 fwrite(str, sizeof *str, strlen(str), fo);
1336 if (((flags&GFILES) == 0 &&
1337 col == 3 && asccasecmp(str, "to:") == 0) ||
1338 (col == 3 && asccasecmp(str, "cc:") == 0) ||
1339 (col == 4 && asccasecmp(str, "bcc:") == 0) ||
1340 (col == 10 &&
1341 asccasecmp(str, "Resent-To:") == 0))
1342 is_to = 1;
1344 for (; np != NULL; np = np->n_flink) {
1345 if (is_to && is_fileaddr(np->n_name))
1346 continue;
1347 if (np->n_flink == NULL)
1348 comma = 0;
1349 if (mime_name_invalid(np, !dropinvalid)) {
1350 if (dropinvalid)
1351 continue;
1352 else
1353 return 1;
1355 len = strlen(np->n_fullname);
1356 col++; /* for the space */
1357 if (count && col + len + comma > 72 && col > 1) {
1358 fputs("\n ", fo);
1359 col = 1;
1360 } else
1361 putc(' ', fo);
1362 len = mime_write(np->n_fullname,
1363 len, fo,
1364 domime?CONV_TOHDR_A:CONV_NONE,
1365 TD_ICONV, NULL, (size_t)0,
1366 NULL, NULL);
1367 if (comma && !(is_to && is_fileaddr(np->n_flink->n_name)))
1368 putc(',', fo);
1369 col += len + comma;
1370 count++;
1372 putc('\n', fo);
1373 return 0;
1377 * Rewrite a message for resending, adding the Resent-Headers.
1379 static int
1380 infix_resend(FILE *fi, FILE *fo, struct message *mp, struct name *to,
1381 int add_resent)
1383 size_t count;
1384 char *buf = NULL, *cp/*, *cp2*/;
1385 size_t c, bufsize = 0;
1386 struct name *fromfield = NULL, *senderfield = NULL;
1388 count = mp->m_size;
1390 * Write the Resent-Fields.
1392 if (add_resent) {
1393 fputs("Resent-", fo);
1394 mkdate(fo, "Date");
1395 if ((cp = myaddrs(NULL)) != NULL) {
1396 if (putname(cp, GCOMMA, SEND_MBOX, NULL,
1397 "Resent-From:", fo, &fromfield))
1398 return 1;
1400 if ((cp = value("sender")) != NULL) {
1401 if (putname(cp, GCOMMA, SEND_MBOX, NULL,
1402 "Resent-Sender:", fo, &senderfield))
1403 return 1;
1405 if (fmt("Resent-To:", to, fo, 1, 1, 0)) {
1406 if (buf)
1407 free(buf);
1408 return 1;
1410 if (value("stealthmua") == NULL) {
1411 fputs("Resent-", fo);
1412 message_id(fo, NULL);
1415 if (check_from_and_sender(fromfield, senderfield))
1416 return 1;
1418 * Write the original headers.
1420 while (count > 0) {
1421 if ((cp = foldergets(&buf, &bufsize, &count, &c, fi)) == NULL)
1422 break;
1423 if (ascncasecmp("status: ", buf, 8) != 0
1424 && strncmp("From ", buf, 5) != 0) {
1425 fwrite(buf, sizeof *buf, c, fo);
1427 if (count > 0 && *buf == '\n')
1428 break;
1431 * Write the message body.
1433 while (count > 0) {
1434 if (foldergets(&buf, &bufsize, &count, &c, fi) == NULL)
1435 break;
1436 if (count == 0 && *buf == '\n')
1437 break;
1438 fwrite(buf, sizeof *buf, c, fo);
1440 if (buf)
1441 free(buf);
1442 if (ferror(fo)) {
1443 perror(catgets(catd, CATSET, 188, "temporary mail file"));
1444 return 1;
1446 return 0;
1449 enum okay
1450 resend_msg(struct message *mp, struct name *to, int add_resent)
1452 FILE *ibuf, *nfo, *nfi;
1453 char *tempMail;
1454 struct header head;
1455 enum okay ok = STOP;
1457 memset(&head, 0, sizeof head);
1458 if ((to = checkaddrs(to)) == NULL) {
1459 senderr++;
1460 return STOP;
1462 if ((nfo = Ftemp(&tempMail, "Rs", "w", 0600, 1)) == NULL) {
1463 senderr++;
1464 perror(catgets(catd, CATSET, 189, "temporary mail file"));
1465 return STOP;
1467 if ((nfi = Fopen(tempMail, "r")) == NULL) {
1468 senderr++;
1469 perror(tempMail);
1470 return STOP;
1472 rm(tempMail);
1473 Ftfree(&tempMail);
1474 if ((ibuf = setinput(&mb, mp, NEED_BODY)) == NULL)
1475 return STOP;
1476 head.h_to = to;
1477 to = fixhead(&head, to);
1478 if (infix_resend(ibuf, nfo, mp, head.h_to, add_resent) != 0) {
1479 senderr++;
1480 rewind(nfo);
1481 savedeadletter(nfi);
1482 fputs(catgets(catd, CATSET, 190,
1483 ". . . message not sent.\n"), stderr);
1484 Fclose(nfo);
1485 Fclose(nfi);
1486 return STOP;
1488 fflush(nfo);
1489 rewind(nfo);
1490 Fclose(nfo);
1491 to = outof(to, nfi, &head);
1492 if (senderr)
1493 savedeadletter(nfi);
1494 to = elide(to);
1495 if (count(to) != 0) {
1496 if (value("record-resent") == NULL ||
1497 mightrecord(nfi, to, 0) == OKAY)
1498 ok = transfer(to, head.h_smopts, nfi, NULL);
1499 } else if (senderr == 0)
1500 ok = OKAY;
1501 Fclose(nfi);
1502 return ok;