Improve config/make system, silence smake(1)..
[s-mailx.git] / sendout.c
blob4080b19f0f36f0d09473d1850567857944833eca
1 /*
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.
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 #include "rcv.h"
41 #include "extern.h"
42 #include <errno.h>
43 #include <sys/stat.h>
44 #include <fcntl.h>
45 #include <unistd.h>
46 #include <time.h>
49 * Mail -- a mail program
51 * Mail to others.
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, struct name *mailargs, FILE *input,
68 struct header *hp);
69 static enum okay start_mta(struct name *to, struct name *mailargs, FILE *input,
70 struct header *hp);
71 static void message_id(FILE *fo, struct header *hp);
72 static int fmt(char *str, struct name *np, FILE *fo, int comma,
73 int dropinvalid, int domime);
74 static int infix_resend(FILE *fi, FILE *fo, struct message *mp,
75 struct name *to, int add_resent);
78 * Generate a boundary for MIME multipart messages.
80 char *
81 makeboundary(void)
83 static char bound[70];
84 time_t now;
86 time(&now);
87 snprintf(bound, sizeof bound, "=_%lx.%s", (long)now, getrandstring(48));
88 send_boundary = bound;
89 return send_boundary;
93 * Get an encoding flag based on the given string.
95 static char *
96 getencoding(enum conversion convert)
98 switch (convert) {
99 case CONV_7BIT:
100 return "7bit";
101 case CONV_8BIT:
102 return "8bit";
103 case CONV_TOQP:
104 return "quoted-printable";
105 case CONV_TOB64:
106 return "base64";
107 default:
108 break;
110 /*NOTREACHED*/
111 return NULL;
115 * Fix the header by glopping all of the expanded names from
116 * the distribution list into the appropriate fields.
118 static struct name *
119 fixhead(struct header *hp, struct name *tolist)
121 struct name *np;
123 hp->h_to = NULL;
124 hp->h_cc = NULL;
125 hp->h_bcc = NULL;
126 for (np = tolist; np != NULL; np = np->n_flink)
127 if ((np->n_type & GMASK) == GTO)
128 hp->h_to = cat(hp->h_to, ndup(np, GFULL));
129 else if ((np->n_type & GMASK) == GCC)
130 hp->h_cc = cat(hp->h_cc, ndup(np, GFULL));
131 else if ((np->n_type & GMASK) == GBCC)
132 hp->h_bcc = cat(hp->h_bcc, ndup(np, GFULL));
133 return tolist;
138 * Do not change, you get incorrect base64 encodings else!
140 #define INFIX_BUF 972
143 * Put the signature file at fo.
145 static int
146 put_signature(FILE *fo, int convert)
148 char *sig, buf[INFIX_BUF], c = '\n';
149 FILE *fsig;
150 size_t sz;
152 sig = value("signature");
153 if (sig == NULL || *sig == '\0')
154 return 0;
155 else
156 sig = expand(sig);
157 if ((fsig = Fopen(sig, "r")) == NULL) {
158 perror(sig);
159 return -1;
161 while ((sz = fread(buf, sizeof *buf, INFIX_BUF, fsig)) != 0) {
162 c = buf[sz - 1];
163 if (mime_write(buf, sz, fo, convert, TD_NONE,
164 NULL, (size_t)0, NULL, NULL)
165 == 0) {
166 perror(sig);
167 Fclose(fsig);
168 return -1;
171 if (ferror(fsig)) {
172 perror(sig);
173 Fclose(fsig);
174 return -1;
176 Fclose(fsig);
177 if (c != '\n')
178 putc('\n', fo);
179 return 0;
183 * Write an attachment to the file buffer, converting to MIME.
185 static int
186 attach_file1(struct attachment *ap, FILE *fo, int dosign)
188 FILE *fi;
189 char *charset = NULL, *contenttype = NULL, *basename;
190 enum conversion convert = CONV_TOB64;
191 int err = 0;
192 enum mimeclean isclean;
193 size_t sz;
194 char *buf;
195 size_t bufsize, count;
196 int lastc = EOF;
197 #ifdef HAVE_ICONV
198 char *tcs;
199 #endif
201 if ((fi = Fopen(ap->a_name, "r")) == NULL) {
202 perror(ap->a_name);
203 return -1;
205 if ((basename = strrchr(ap->a_name, '/')) == NULL)
206 basename = ap->a_name;
207 else
208 basename++;
209 if (ap->a_content_type)
210 contenttype = ap->a_content_type;
211 else
212 contenttype = mime_filecontent(basename);
213 if (ap->a_charset)
214 charset = ap->a_charset;
215 convert = get_mime_convert(fi, &contenttype, &charset, &isclean,
216 dosign);
217 fprintf(fo,
218 "\n--%s\n"
219 "Content-Type: %s",
220 send_boundary, contenttype);
221 if (charset == NULL)
222 putc('\n', fo);
223 else
224 fprintf(fo, ";\n charset=%s\n", charset);
225 if (ap->a_content_disposition == NULL)
226 ap->a_content_disposition = "attachment";
227 fprintf(fo, "Content-Transfer-Encoding: %s\n"
228 "Content-Disposition: %s;\n"
229 " filename=\"",
230 getencoding(convert),
231 ap->a_content_disposition);
232 mime_write(basename, strlen(basename), fo,
233 CONV_TOHDR, TD_NONE, NULL, (size_t)0, NULL, NULL);
234 fwrite("\"\n", sizeof (char), 2, fo);
235 if (ap->a_content_id)
236 fprintf(fo, "Content-ID: %s\n", ap->a_content_id);
237 if (ap->a_content_description)
238 fprintf(fo, "Content-Description: %s\n",
239 ap->a_content_description);
240 putc('\n', fo);
241 #ifdef HAVE_ICONV
242 if (iconvd != (iconv_t)-1) {
243 iconv_close(iconvd);
244 iconvd = (iconv_t)-1;
246 tcs = gettcharset();
247 if ((isclean & (MIME_HASNUL|MIME_CTRLCHAR)) == 0 &&
248 ascncasecmp(contenttype, "text/", 5) == 0 &&
249 isclean & MIME_HIGHBIT &&
250 charset != NULL) {
251 if ((iconvd = iconv_open_ft(charset, tcs)) == (iconv_t)-1 &&
252 errno != 0) {
253 if (errno == EINVAL)
254 fprintf(stderr, catgets(catd, CATSET, 179,
255 "Cannot convert from %s to %s\n"), tcs, charset);
256 else
257 perror("iconv_open");
258 Fclose(fi);
259 return -1;
262 #endif /* HAVE_ICONV */
263 buf = smalloc(bufsize = INFIX_BUF);
264 if (convert == CONV_TOQP
265 #ifdef HAVE_ICONV
266 || iconvd != (iconv_t)-1
267 #endif
269 fflush(fi);
270 count = fsize(fi);
272 for (;;) {
273 if (convert == CONV_TOQP
274 #ifdef HAVE_ICONV
275 || iconvd != (iconv_t)-1
276 #endif
278 if (fgetline(&buf, &bufsize, &count, &sz, fi, 0)
279 == NULL)
280 break;
281 } else {
282 if ((sz = fread(buf, sizeof *buf, bufsize, fi)) == 0)
283 break;
285 lastc = buf[sz-1];
286 if (mime_write(buf, sz, fo, convert, TD_ICONV,
287 NULL, (size_t)0, NULL, NULL) == 0)
288 err = -1;
290 if (convert == CONV_TOQP && lastc != '\n')
291 fwrite("=\n", 1, 2, fo);
292 if (ferror(fi))
293 err = -1;
294 Fclose(fi);
295 free(buf);
296 return err;
300 * Try out different character set conversions to attach a file.
302 static int
303 attach_file(struct attachment *ap, FILE *fo, int dosign)
305 char *_wantcharset, *charsets, *ncs;
306 size_t offs = ftell(fo);
308 if (ap->a_charset || (charsets = value("sendcharsets")) == NULL)
309 return attach_file1(ap, fo, dosign);
310 _wantcharset = wantcharset;
311 wantcharset = savestr(charsets);
312 loop: if ((ncs = strchr(wantcharset, ',')) != NULL)
313 *ncs++ = '\0';
314 try: if (attach_file1(ap, fo, dosign) != 0) {
315 if (errno == EILSEQ || errno == EINVAL) {
316 if (ncs && *ncs) {
317 wantcharset = ncs;
318 clearerr(fo);
319 fseek(fo, offs, SEEK_SET);
320 goto loop;
322 if (wantcharset) {
323 if (wantcharset == (char *)-1)
324 wantcharset = NULL;
325 else {
326 wantcharset = (char *)-1;
327 clearerr(fo);
328 fseek(fo, offs, SEEK_SET);
329 goto try;
334 wantcharset = _wantcharset;
335 return 0;
339 * Attach a message to the file buffer.
341 static int
342 attach_message(struct attachment *ap, FILE *fo, int dosign)
344 struct message *mp;
345 (void)dosign;
347 fprintf(fo, "\n--%s\n"
348 "Content-Type: message/rfc822\n"
349 "Content-Disposition: inline\n\n", send_boundary);
350 mp = &message[ap->a_msgno - 1];
351 touch(mp);
352 if (send(mp, fo, 0, NULL, SEND_RFC822, NULL) < 0)
353 return -1;
354 return 0;
358 * Generate the body of a MIME multipart message.
360 static int
361 make_multipart(struct header *hp, int convert, FILE *fi, FILE *fo,
362 const char *contenttype, const char *charset, int dosign)
364 struct attachment *att;
366 fputs("This is a multi-part message in MIME format.\n", fo);
367 if (fsize(fi) != 0) {
368 char *buf, c = '\n';
369 size_t sz, bufsize, count;
371 fprintf(fo, "\n--%s\n", send_boundary);
372 fprintf(fo, "Content-Type: %s", contenttype);
373 if (charset)
374 fprintf(fo, "; charset=%s", charset);
375 fprintf(fo, "\nContent-Transfer-Encoding: %s\n"
376 "Content-Disposition: inline\n\n",
377 getencoding(convert));
378 buf = smalloc(bufsize = INFIX_BUF);
379 if (convert == CONV_TOQP
380 #ifdef HAVE_ICONV
381 || iconvd != (iconv_t)-1
382 #endif /* HAVE_ICONV */
384 fflush(fi);
385 count = fsize(fi);
387 for (;;) {
388 if (convert == CONV_TOQP
389 #ifdef HAVE_ICONV
390 || iconvd != (iconv_t)-1
391 #endif /* HAVE_ICONV */
393 if (fgetline(&buf, &bufsize, &count, &sz, fi, 0)
394 == NULL)
395 break;
396 } else {
397 sz = fread(buf, sizeof *buf, bufsize, fi);
398 if (sz == 0)
399 break;
401 c = buf[sz - 1];
402 if (mime_write(buf, sz, fo, convert,
403 TD_ICONV, NULL, (size_t)0,
404 NULL, NULL) == 0) {
405 free(buf);
406 return -1;
409 free(buf);
410 if (ferror(fi))
411 return -1;
412 if (c != '\n')
413 putc('\n', fo);
414 if (charset != NULL)
415 put_signature(fo, convert);
417 for (att = hp->h_attach; att != NULL; att = att->a_flink) {
418 if (att->a_msgno) {
419 if (attach_message(att, fo, dosign) != 0)
420 return -1;
421 } else {
422 if (attach_file(att, fo, dosign) != 0)
423 return -1;
426 /* the final boundary with two attached dashes */
427 fprintf(fo, "\n--%s--\n", send_boundary);
428 return 0;
432 * Prepend a header in front of the collected stuff
433 * and return the new file.
435 static FILE *
436 infix(struct header *hp, FILE *fi, int dosign)
438 FILE *nfo, *nfi;
439 char *tempMail;
440 #ifdef HAVE_ICONV
441 char *tcs, *convhdr = NULL;
442 #endif
443 enum mimeclean isclean;
444 enum conversion convert;
445 char *charset = NULL, *contenttype = NULL;
446 int lastc = EOF;
448 if ((nfo = Ftemp(&tempMail, "Rs", "w", 0600, 1)) == NULL) {
449 perror(catgets(catd, CATSET, 178, "temporary mail file"));
450 return(NULL);
452 if ((nfi = Fopen(tempMail, "r")) == NULL) {
453 perror(tempMail);
454 Fclose(nfo);
455 return(NULL);
457 rm(tempMail);
458 Ftfree(&tempMail);
459 convert = get_mime_convert(fi, &contenttype, &charset,
460 &isclean, dosign);
461 #ifdef HAVE_ICONV
462 tcs = gettcharset();
463 if ((convhdr = need_hdrconv(hp, GTO|GSUBJECT|GCC|GBCC|GIDENT)) != 0) {
464 if (iconvd != (iconv_t)-1)
465 iconv_close(iconvd);
466 if ((iconvd = iconv_open_ft(convhdr, tcs)) == (iconv_t)-1
467 && errno != 0) {
468 if (errno == EINVAL)
469 fprintf(stderr, catgets(catd, CATSET, 179,
470 "Cannot convert from %s to %s\n"), tcs, convhdr);
471 else
472 perror("iconv_open");
473 Fclose(nfo);
474 return NULL;
477 #endif /* HAVE_ICONV */
478 if (puthead(hp, nfo,
479 GTO|GSUBJECT|GCC|GBCC|GNL|GCOMMA|GUA|GMIME
480 |GMSGID|GIDENT|GREF|GDATE,
481 SEND_MBOX, convert, contenttype, charset)) {
482 Fclose(nfo);
483 Fclose(nfi);
484 #ifdef HAVE_ICONV
485 if (iconvd != (iconv_t)-1) {
486 iconv_close(iconvd);
487 iconvd = (iconv_t)-1;
489 #endif
490 return NULL;
492 #ifdef HAVE_ICONV
493 if (convhdr && iconvd != (iconv_t)-1) {
494 iconv_close(iconvd);
495 iconvd = (iconv_t)-1;
497 if ((isclean & (MIME_HASNUL|MIME_CTRLCHAR)) == 0 &&
498 ascncasecmp(contenttype, "text/", 5) == 0 &&
499 isclean & MIME_HIGHBIT &&
500 charset != NULL) {
501 if (iconvd != (iconv_t)-1)
502 iconv_close(iconvd);
503 if ((iconvd = iconv_open_ft(charset, tcs)) == (iconv_t)-1
504 && errno != 0) {
505 if (errno == EINVAL)
506 fprintf(stderr, catgets(catd, CATSET, 179,
507 "Cannot convert from %s to %s\n"), tcs, charset);
508 else
509 perror("iconv_open");
510 Fclose(nfo);
511 return NULL;
514 #endif
515 if (hp->h_attach != NULL) {
516 if (make_multipart(hp, convert, fi, nfo,
517 contenttype, charset, dosign) != 0) {
518 Fclose(nfo);
519 Fclose(nfi);
520 #ifdef HAVE_ICONV
521 if (iconvd != (iconv_t)-1) {
522 iconv_close(iconvd);
523 iconvd = (iconv_t)-1;
525 #endif
526 return NULL;
528 } else {
529 size_t sz, bufsize, count;
530 char *buf;
532 if (convert == CONV_TOQP
533 #ifdef HAVE_ICONV
534 || iconvd != (iconv_t)-1
535 #endif /* HAVE_ICONV */
537 fflush(fi);
538 count = fsize(fi);
540 buf = smalloc(bufsize = INFIX_BUF);
541 for (;;) {
542 if (convert == CONV_TOQP
543 #ifdef HAVE_ICONV
544 || iconvd != (iconv_t)-1
545 #endif /* HAVE_ICONV */
547 if (fgetline(&buf, &bufsize, &count, &sz, fi, 0)
548 == NULL)
549 break;
550 } else {
551 sz = fread(buf, sizeof *buf, bufsize, fi);
552 if (sz == 0)
553 break;
555 lastc = buf[sz - 1];
556 if (mime_write(buf, sz, nfo, convert,
557 TD_ICONV, NULL, (size_t)0,
558 NULL, NULL) == 0) {
559 Fclose(nfo);
560 Fclose(nfi);
561 #ifdef HAVE_ICONV
562 if (iconvd != (iconv_t)-1) {
563 iconv_close(iconvd);
564 iconvd = (iconv_t)-1;
566 #endif
567 free(buf);
568 return NULL;
571 if (convert == CONV_TOQP && lastc != '\n')
572 fwrite("=\n", 1, 2, nfo);
573 free(buf);
574 if (ferror(fi)) {
575 Fclose(nfo);
576 Fclose(nfi);
577 #ifdef HAVE_ICONV
578 if (iconvd != (iconv_t)-1) {
579 iconv_close(iconvd);
580 iconvd = (iconv_t)-1;
582 #endif
583 return NULL;
585 if (charset != NULL)
586 put_signature(nfo, convert);
588 #ifdef HAVE_ICONV
589 if (iconvd != (iconv_t)-1) {
590 iconv_close(iconvd);
591 iconvd = (iconv_t)-1;
593 #endif
594 fflush(nfo);
595 if (ferror(nfo)) {
596 perror(catgets(catd, CATSET, 180, "temporary mail file"));
597 Fclose(nfo);
598 Fclose(nfi);
599 return NULL;
601 Fclose(nfo);
602 Fclose(fi);
603 fflush(nfi);
604 rewind(nfi);
605 return(nfi);
609 * Save the outgoing mail on the passed file.
612 /*ARGSUSED*/
613 static int
614 savemail(char *name, FILE *fi)
616 FILE *fo;
617 char *buf;
618 size_t bufsize, buflen, count;
619 char *p;
620 time_t now;
621 int prependnl = 0;
622 int error = 0;
624 buf = smalloc(bufsize = LINESIZE);
625 time(&now);
626 if ((fo = Zopen(name, "a+", NULL)) == NULL) {
627 if ((fo = Zopen(name, "wx", NULL)) == NULL) {
628 perror(name);
629 free(buf);
630 return (-1);
632 } else {
633 if (fseek(fo, -2L, SEEK_END) == 0) {
634 switch (fread(buf, sizeof *buf, 2, fo)) {
635 case 2:
636 if (buf[1] != '\n') {
637 prependnl = 1;
638 break;
640 /*FALLTHRU*/
641 case 1:
642 if (buf[0] != '\n')
643 prependnl = 1;
644 break;
645 default:
646 if (ferror(fo)) {
647 perror(name);
648 free(buf);
649 return -1;
652 fflush(fo);
653 if (prependnl) {
654 putc('\n', fo);
655 fflush(fo);
659 fprintf(fo, "From %s %s", myname, ctime(&now));
660 buflen = 0;
661 fflush(fi);
662 rewind(fi);
663 count = fsize(fi);
664 while (fgetline(&buf, &bufsize, &count, &buflen, fi, 0) != NULL) {
665 if (*buf == '>') {
666 p = buf + 1;
667 while (*p == '>')
668 p++;
669 if (strncmp(p, "From ", 5) == 0)
670 /* we got a masked From line */
671 putc('>', fo);
672 } else if (strncmp(buf, "From ", 5) == 0)
673 putc('>', fo);
674 fwrite(buf, sizeof *buf, buflen, fo);
676 if (buflen && *(buf + buflen - 1) != '\n')
677 putc('\n', fo);
678 putc('\n', fo);
679 fflush(fo);
680 if (ferror(fo)) {
681 perror(name);
682 error = -1;
684 if (Fclose(fo) != 0)
685 error = -1;
686 fflush(fi);
687 rewind(fi);
688 free(buf);
689 return error;
693 * Interface between the argument list and the mail1 routine
694 * which does all the dirty work.
696 int
697 mail(struct name *to, struct name *cc, struct name *bcc,
698 struct name *smopts, char *subject, struct attachment *attach,
699 char *quotefile, int recipient_record, int tflag, int Eflag)
701 struct header head;
702 struct str in, out;
704 memset(&head, 0, sizeof head);
705 /* The given subject may be in RFC1522 format. */
706 if (subject != NULL) {
707 in.s = subject;
708 in.l = strlen(subject);
709 mime_fromhdr(&in, &out, TD_ISPR | TD_ICONV);
710 head.h_subject = out.s;
712 if (tflag == 0) {
713 head.h_to = to;
714 head.h_cc = cc;
715 head.h_bcc = bcc;
717 head.h_attach = attach;
718 head.h_smopts = smopts;
719 mail1(&head, 0, NULL, quotefile, recipient_record, 0, tflag, Eflag);
720 if (subject != NULL)
721 free(out.s);
722 return(0);
726 * Send mail to a bunch of user names. The interface is through
727 * the mail routine below.
729 static int
730 sendmail_internal(void *v, int recipient_record)
732 int Eflag;
733 char *str = v;
734 struct header head;
736 memset(&head, 0, sizeof head);
737 head.h_to = extract(str, GTO|GFULL);
738 Eflag = value("skipemptybody") != NULL;
739 mail1(&head, 0, NULL, NULL, recipient_record, 0, 0, Eflag);
740 return(0);
743 int
744 sendmail(void *v)
746 return sendmail_internal(v, 0);
749 int
750 Sendmail(void *v)
752 return sendmail_internal(v, 1);
755 static enum okay
756 transfer(struct name *to, struct name *mailargs, FILE *input, struct header *hp)
758 char o[LINESIZE], *cp;
759 struct name *np, *nt;
760 int cnt = 0;
761 FILE *ef;
762 enum okay ok = OKAY;
764 np = to;
765 while (np) {
766 snprintf(o, sizeof o, "smime-encrypt-%s", np->n_name);
767 if ((cp = value(o)) != NULL) {
768 if ((ef = smime_encrypt(input, cp, np->n_name)) != 0) {
769 nt = nalloc(np->n_name,
770 np->n_type & ~(GFULL|GSKIN));
771 if (start_mta(nt, mailargs, ef, hp) != OKAY)
772 ok = STOP;
773 Fclose(ef);
774 } else {
775 fprintf(stderr, "Message not sent to <%s>\n",
776 np->n_name);
777 senderr++;
779 rewind(input);
780 if (np->n_flink)
781 np->n_flink->n_blink = np->n_blink;
782 if (np->n_blink)
783 np->n_blink->n_flink = np->n_flink;
784 if (np == to)
785 to = np->n_flink;
786 np = np->n_flink;
787 } else {
788 cnt++;
789 np = np->n_flink;
792 if (cnt) {
793 if (value("smime-force-encryption") ||
794 start_mta(to, mailargs, input, hp) != OKAY)
795 ok = STOP;
797 return ok;
801 * Start the Mail Transfer Agent
802 * mailing to namelist and stdin redirected to input.
804 static enum okay
805 start_mta(struct name *to, struct name *mailargs, FILE *input,
806 struct header *hp)
808 char **args = NULL, **t;
809 pid_t pid;
810 sigset_t nset;
811 char *cp, *smtp;
812 char *user = NULL, *password = NULL, *skinned = NULL;
813 enum okay ok = STOP;
814 #ifdef USE_SMTP
815 struct termios otio;
816 int reset_tio;
817 #endif
819 if ((smtp = value("smtp")) == NULL) {
820 args = unpack(cat(mailargs, to));
821 if (debug || value("debug")) {
822 printf(catgets(catd, CATSET, 181,
823 "Sendmail arguments:"));
824 for (t = args; *t != NULL; t++)
825 printf(" \"%s\"", *t);
826 printf("\n");
827 return OKAY;
830 #ifdef USE_SMTP
831 if (smtp != NULL) {
832 skinned = skin(myorigin(hp));
833 if ((user = smtp_auth_var("-user", skinned)) != NULL &&
834 (password = smtp_auth_var("-password",
835 skinned)) == NULL)
836 password = getpassword(&otio, &reset_tio, NULL);
838 #endif
840 * Fork, set up the temporary mail file as standard
841 * input for "mail", and exec with the user list we generated
842 * far above.
844 if ((pid = fork()) == -1) {
845 perror("fork");
846 savedeadletter(input);
847 senderr++;
848 return STOP;
850 if (pid == 0) {
851 sigemptyset(&nset);
852 sigaddset(&nset, SIGHUP);
853 sigaddset(&nset, SIGINT);
854 sigaddset(&nset, SIGQUIT);
855 sigaddset(&nset, SIGTSTP);
856 sigaddset(&nset, SIGTTIN);
857 sigaddset(&nset, SIGTTOU);
858 freopen("/dev/null", "r", stdin);
859 if (smtp != NULL) {
860 prepare_child(&nset, 0, 1);
861 if (smtp_mta(smtp, to, input, hp,
862 user, password, skinned) == 0)
863 _exit(0);
864 } else {
865 prepare_child(&nset, fileno(input), -1);
866 if ((cp = value("sendmail")) != NULL)
867 cp = expand(cp);
868 else
869 cp = SENDMAIL;
870 execv(cp, args);
871 perror(cp);
873 savedeadletter(input);
874 fputs(catgets(catd, CATSET, 182,
875 ". . . message not sent.\n"), stderr);
876 _exit(1);
878 if (value("verbose") != NULL || value("sendwait") || debug
879 || value("debug")) {
880 if (wait_child(pid) == 0)
881 ok = OKAY;
882 else
883 senderr++;
884 } else {
885 ok = OKAY;
886 free_child(pid);
888 return ok;
892 * Record outgoing mail if instructed to do so.
894 static enum okay
895 mightrecord(FILE *fp, struct name *to, int recipient_record)
897 char *cp, *cq, *ep;
899 if (recipient_record) {
900 cq = skin(to->n_name);
901 cp = salloc(strlen(cq) + 1);
902 strcpy(cp, cq);
903 for (cq = cp; *cq && *cq != '@'; cq++);
904 *cq = '\0';
905 } else
906 cp = value("record");
907 if (cp != NULL) {
908 ep = expand(cp);
909 if (value("outfolder") && *ep != '/' && *ep != '+' &&
910 which_protocol(ep) == PROTO_FILE) {
911 cq = salloc(strlen(cp) + 2);
912 cq[0] = '+';
913 strcpy(&cq[1], cp);
914 cp = cq;
915 ep = expand(cp);
917 if (savemail(ep, fp) != 0) {
918 fprintf(stderr,
919 "Error while saving message to %s - "
920 "message not sent\n", ep);
921 rewind(fp);
922 exit_status |= 1;
923 savedeadletter(fp);
924 return STOP;
927 return OKAY;
931 * Mail a message on standard input to the people indicated
932 * in the passed header. (Internal interface).
934 enum okay
935 mail1(struct header *hp, int printheaders, struct message *quote,
936 char *quotefile, int recipient_record, int doprefix, int tflag,
937 int Eflag)
939 struct name *to;
940 FILE *mtf, *nmtf;
941 enum okay ok = STOP;
942 int dosign = -1;
943 char *charsets, *ncs = NULL, *cp;
945 #ifdef notdef
946 if ((hp->h_to = checkaddrs(hp->h_to)) == NULL) {
947 senderr++;
948 return STOP;
950 #endif
951 if ((cp = value("autocc")) != NULL && *cp)
952 hp->h_cc = cat(hp->h_cc, checkaddrs(sextract(cp, GCC|GFULL)));
953 if ((cp = value("autobcc")) != NULL && *cp)
954 hp->h_bcc = cat(hp->h_bcc,
955 checkaddrs(sextract(cp, GBCC|GFULL)));
957 * Collect user's mail from standard input.
958 * Get the result as mtf.
960 if ((mtf = collect(hp, printheaders, quote, quotefile, doprefix,
961 tflag)) == NULL)
962 return STOP;
963 if (value("interactive") != NULL) {
964 if (((value("bsdcompat") || value("askatend"))
965 && (value("askcc") != NULL ||
966 value("askbcc") != NULL)) ||
967 value("askattach") != NULL ||
968 value("asksign") != NULL) {
969 if (value("askcc") != NULL)
970 grabh(hp, GCC, 1);
971 if (value("askbcc") != NULL)
972 grabh(hp, GBCC, 1);
973 if (value("askattach") != NULL)
974 hp->h_attach = edit_attachments(hp->h_attach);
975 if (value("asksign") != NULL)
976 dosign = yorn("Sign this message (y/n)? ");
977 } else {
978 printf(catgets(catd, CATSET, 183, "EOT\n"));
979 fflush(stdout);
982 if (fsize(mtf) == 0) {
983 if (Eflag)
984 goto out;
985 if (hp->h_subject == NULL)
986 printf(catgets(catd, CATSET, 184,
987 "No message, no subject; hope that's ok\n"));
988 else if (value("bsdcompat") || value("bsdmsgs"))
989 printf(catgets(catd, CATSET, 185,
990 "Null message body; hope that's ok\n"));
992 if (dosign < 0) {
993 if (value("smime-sign") != NULL)
994 dosign = 1;
995 else
996 dosign = 0;
999 * Now, take the user names from the combined
1000 * to and cc lists and do all the alias
1001 * processing.
1003 senderr = 0;
1004 if ((to = usermap(cat(hp->h_bcc, cat(hp->h_to, hp->h_cc)))) == NULL) {
1005 printf(catgets(catd, CATSET, 186, "No recipients specified\n"));
1006 senderr++;
1008 to = fixhead(hp, to);
1009 if (hp->h_charset) {
1010 wantcharset = hp->h_charset;
1011 goto try;
1013 hloop: wantcharset = NULL;
1014 if ((charsets = value("sendcharsets")) != NULL) {
1015 wantcharset = savestr(charsets);
1016 loop: if ((ncs = strchr(wantcharset, ',')) != NULL)
1017 *ncs++ = '\0';
1019 try: if ((nmtf = infix(hp, mtf, dosign)) == NULL) {
1020 if (hp->h_charset && (errno == EILSEQ || errno == EINVAL)) {
1021 rewind(mtf);
1022 hp->h_charset = NULL;
1023 goto hloop;
1025 if (errno == EILSEQ || errno == EINVAL) {
1026 if (ncs && *ncs) {
1027 rewind(mtf);
1028 wantcharset = ncs;
1029 goto loop;
1031 if (wantcharset && value("interactive") == NULL) {
1032 if (wantcharset == (char *)-1)
1033 wantcharset = NULL;
1034 else {
1035 rewind(mtf);
1036 wantcharset = (char *)-1;
1037 goto try;
1041 /* fprintf(stderr, ". . . message lost, sorry.\n"); */
1042 perror("");
1043 fail: senderr++;
1044 rewind(mtf);
1045 savedeadletter(mtf);
1046 fputs(catgets(catd, CATSET, 187,
1047 ". . . message not sent.\n"), stderr);
1048 return STOP;
1050 mtf = nmtf;
1051 if (dosign) {
1052 if ((nmtf = smime_sign(mtf, hp)) == NULL)
1053 goto fail;
1054 Fclose(mtf);
1055 mtf = nmtf;
1058 * Look through the recipient list for names with /'s
1059 * in them which we write to as files directly.
1061 to = outof(to, mtf, hp);
1062 if (senderr)
1063 savedeadletter(mtf);
1064 to = elide(to);
1065 if (count(to) == 0) {
1066 if (senderr == 0)
1067 ok = OKAY;
1068 goto out;
1070 if (mightrecord(mtf, to, recipient_record) != OKAY)
1071 goto out;
1072 ok = transfer(to, hp->h_smopts, mtf, hp);
1073 out:
1074 Fclose(mtf);
1075 return ok;
1079 * Create a Message-Id: header field.
1080 * Use either the host name or the from address.
1082 static void
1083 message_id(FILE *fo, struct header *hp)
1085 char *cp;
1086 time_t now;
1088 time(&now);
1089 if ((cp = value("hostname")) != NULL)
1090 fprintf(fo, "Message-ID: <%lx.%s@%s>\n",
1091 (long)now, getrandstring(24), cp);
1092 else if ((cp = skin(myorigin(hp))) != NULL && strchr(cp, '@') != NULL)
1093 fprintf(fo, "Message-ID: <%lx.%s%%%s>\n",
1094 (long)now, getrandstring(16), cp);
1097 static const char *weekday_names[] = {
1098 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
1101 const char *month_names[] = {
1102 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
1103 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", NULL
1107 * Create a Date: header field.
1108 * We compare the localtime() and gmtime() results to get the timezone,
1109 * because numeric timezones are easier to read and because $TZ is
1110 * not set on most GNU systems.
1113 mkdate(FILE *fo, const char *field)
1115 time_t t;
1116 struct tm *tmptr;
1117 int tzdiff, tzdiff_hour, tzdiff_min;
1119 time(&t);
1120 tzdiff = t - mktime(gmtime(&t));
1121 tzdiff_hour = (int)(tzdiff / 60);
1122 tzdiff_min = tzdiff_hour % 60;
1123 tzdiff_hour /= 60;
1124 tmptr = localtime(&t);
1125 if (tmptr->tm_isdst > 0)
1126 tzdiff_hour++;
1127 return fprintf(fo, "%s: %s, %02d %s %04d %02d:%02d:%02d %+05d\n",
1128 field,
1129 weekday_names[tmptr->tm_wday],
1130 tmptr->tm_mday, month_names[tmptr->tm_mon],
1131 tmptr->tm_year + 1900, tmptr->tm_hour,
1132 tmptr->tm_min, tmptr->tm_sec,
1133 tzdiff_hour * 100 + tzdiff_min);
1136 static enum okay
1137 putname(char *line, enum gfield w, enum sendaction action, int *gotcha,
1138 char *prefix, FILE *fo, struct name **xp)
1140 struct name *np;
1142 np = sextract(line, GEXTRA|GFULL);
1143 if (xp)
1144 *xp = np;
1145 if (np == NULL)
1146 return 0;
1147 if (fmt(prefix, np, fo, w&(GCOMMA|GFILES), 0, action != SEND_TODISP))
1148 return 1;
1149 if (gotcha)
1150 (*gotcha)++;
1151 return 0;
1154 #define FMT_CC_AND_BCC { \
1155 if (hp->h_cc != NULL && w & GCC) { \
1156 if (fmt("Cc:", hp->h_cc, fo, \
1157 w&(GCOMMA|GFILES), 0, \
1158 action!=SEND_TODISP)) \
1159 return 1; \
1160 gotcha++; \
1162 if (hp->h_bcc != NULL && w & GBCC) { \
1163 if (fmt("Bcc:", hp->h_bcc, fo, \
1164 w&(GCOMMA|GFILES), 0, \
1165 action!=SEND_TODISP)) \
1166 return 1; \
1167 gotcha++; \
1171 * Dump the to, subject, cc header on the
1172 * passed file buffer.
1175 puthead(struct header *hp, FILE *fo, enum gfield w,
1176 enum sendaction action, enum conversion convert,
1177 char *contenttype, char *charset)
1179 int gotcha;
1180 char *addr/*, *cp*/;
1181 int stealthmua;
1182 struct name *np, *fromfield = NULL, *senderfield = NULL;
1185 if (value("stealthmua"))
1186 stealthmua = 1;
1187 else
1188 stealthmua = 0;
1189 gotcha = 0;
1190 if (w & GDATE) {
1191 mkdate(fo, "Date"), gotcha++;
1193 if (w & GIDENT) {
1194 if (hp->h_from != NULL) {
1195 if (fmt("From:", hp->h_from, fo, w&(GCOMMA|GFILES), 0,
1196 action!=SEND_TODISP))
1197 return 1;
1198 gotcha++;
1199 fromfield = hp->h_from;
1200 } else if ((addr = myaddrs(hp)) != NULL)
1201 if (putname(addr, w, action, &gotcha, "From:", fo,
1202 &fromfield))
1203 return 1;
1204 if (((addr = hp->h_organization) != NULL ||
1205 (addr = value("ORGANIZATION")) != NULL)
1206 && strlen(addr) > 0) {
1207 fwrite("Organization: ", sizeof (char), 14, fo);
1208 if (mime_write(addr, strlen(addr), fo,
1209 action == SEND_TODISP ?
1210 CONV_NONE:CONV_TOHDR,
1211 action == SEND_TODISP ?
1212 TD_ISPR|TD_ICONV:TD_ICONV,
1213 NULL, (size_t)0,
1214 NULL, NULL) == 0)
1215 return 1;
1216 gotcha++;
1217 putc('\n', fo);
1219 if (hp->h_replyto != NULL) {
1220 if (fmt("Reply-To:", hp->h_replyto, fo,
1221 w&(GCOMMA|GFILES), 0,
1222 action!=SEND_TODISP))
1223 return 1;
1224 gotcha++;
1225 } else if ((addr = value("replyto")) != NULL)
1226 if (putname(addr, w, action, &gotcha, "Reply-To:", fo,
1227 NULL))
1228 return 1;
1229 if (hp->h_sender != NULL) {
1230 if (fmt("Sender:", hp->h_sender, fo,
1231 w&(GCOMMA|GFILES), 0,
1232 action!=SEND_TODISP))
1233 return 1;
1234 gotcha++;
1235 senderfield = hp->h_sender;
1236 } else if ((addr = value("sender")) != NULL)
1237 if (putname(addr, w, action, &gotcha, "Sender:", fo,
1238 &senderfield))
1239 return 1;
1240 if (check_from_and_sender(fromfield, senderfield))
1241 return 1;
1243 if (hp->h_to != NULL && w & GTO) {
1244 if (fmt("To:", hp->h_to, fo, w&(GCOMMA|GFILES), 0,
1245 action!=SEND_TODISP))
1246 return 1;
1247 gotcha++;
1249 if (value("bsdcompat") == NULL && value("bsdorder") == NULL)
1250 FMT_CC_AND_BCC
1251 if (hp->h_subject != NULL && w & GSUBJECT) {
1252 fwrite("Subject: ", sizeof (char), 9, fo);
1253 if (ascncasecmp(hp->h_subject, "re: ", 4) == 0) {
1254 fwrite("Re: ", sizeof (char), 4, fo);
1255 if (strlen(hp->h_subject + 4) > 0 &&
1256 mime_write(hp->h_subject + 4,
1257 strlen(hp->h_subject + 4),
1258 fo, action == SEND_TODISP ?
1259 CONV_NONE:CONV_TOHDR,
1260 action == SEND_TODISP ?
1261 TD_ISPR|TD_ICONV:TD_ICONV,
1262 NULL, (size_t)0,
1263 NULL, NULL) == 0)
1264 return 1;
1265 } else if (*hp->h_subject) {
1266 if (mime_write(hp->h_subject,
1267 strlen(hp->h_subject),
1268 fo, action == SEND_TODISP ?
1269 CONV_NONE:CONV_TOHDR,
1270 action == SEND_TODISP ?
1271 TD_ISPR|TD_ICONV:TD_ICONV,
1272 NULL, (size_t)0,
1273 NULL, NULL) == 0)
1274 return 1;
1276 gotcha++;
1277 fwrite("\n", sizeof (char), 1, fo);
1279 if (value("bsdcompat") || value("bsdorder"))
1280 FMT_CC_AND_BCC
1281 if (w & GMSGID && stealthmua == 0)
1282 message_id(fo, hp), gotcha++;
1283 if (hp->h_ref != NULL && w & GREF) {
1284 fmt("References:", hp->h_ref, fo, 0, 1, 0);
1285 if ((np = hp->h_ref) != NULL && np->n_name) {
1286 while (np->n_flink)
1287 np = np->n_flink;
1288 if (mime_name_invalid(np, 0) == 0) {
1289 fprintf(fo, "In-Reply-To: %s\n", np->n_name);
1290 gotcha++;
1294 if (w & GUA && stealthmua == 0)
1295 fprintf(fo, "User-Agent: S-nail %s\n",
1296 version), gotcha++;
1297 if (w & GMIME) {
1298 fputs("MIME-Version: 1.0\n", fo), gotcha++;
1299 if (hp->h_attach != NULL) {
1300 makeboundary();
1301 fprintf(fo, "Content-Type: multipart/mixed;\n"
1302 " boundary=\"%s\"\n", send_boundary);
1303 } else {
1304 fprintf(fo, "Content-Type: %s", contenttype);
1305 if (charset)
1306 fprintf(fo, "; charset=%s", charset);
1307 fprintf(fo, "\nContent-Transfer-Encoding: %s\n",
1308 getencoding(convert));
1311 if (gotcha && w & GNL)
1312 putc('\n', fo);
1313 return(0);
1317 * Format the given header line to not exceed 72 characters.
1319 static int
1320 fmt(char *str, struct name *np, FILE *fo, int flags, int dropinvalid,
1321 int domime)
1323 int col, len, count = 0;
1324 int is_to = 0, comma;
1326 comma = flags&GCOMMA ? 1 : 0;
1327 col = strlen(str);
1328 if (col) {
1329 fwrite(str, sizeof *str, strlen(str), fo);
1330 if (((flags&GFILES) == 0 &&
1331 col == 3 && asccasecmp(str, "to:") == 0) ||
1332 (col == 3 && asccasecmp(str, "cc:") == 0) ||
1333 (col == 4 && asccasecmp(str, "bcc:") == 0) ||
1334 (col == 10 &&
1335 asccasecmp(str, "Resent-To:") == 0))
1336 is_to = 1;
1338 for (; np != NULL; np = np->n_flink) {
1339 if (is_to && is_fileaddr(np->n_name))
1340 continue;
1341 if (np->n_flink == NULL)
1342 comma = 0;
1343 if (mime_name_invalid(np, !dropinvalid)) {
1344 if (dropinvalid)
1345 continue;
1346 else
1347 return 1;
1349 len = strlen(np->n_fullname);
1350 col++; /* for the space */
1351 if (count && col + len + comma > 72 && col > 1) {
1352 fputs("\n ", fo);
1353 col = 1;
1354 } else
1355 putc(' ', fo);
1356 len = mime_write(np->n_fullname,
1357 len, fo,
1358 domime?CONV_TOHDR_A:CONV_NONE,
1359 TD_ICONV, NULL, (size_t)0,
1360 NULL, NULL);
1361 if (comma && !(is_to && is_fileaddr(np->n_flink->n_name)))
1362 putc(',', fo);
1363 col += len + comma;
1364 count++;
1366 putc('\n', fo);
1367 return 0;
1371 * Rewrite a message for resending, adding the Resent-Headers.
1373 static int
1374 infix_resend(FILE *fi, FILE *fo, struct message *mp, struct name *to,
1375 int add_resent)
1377 size_t count;
1378 char *buf = NULL, *cp/*, *cp2*/;
1379 size_t c, bufsize = 0;
1380 struct name *fromfield = NULL, *senderfield = NULL;
1382 count = mp->m_size;
1384 * Write the Resent-Fields.
1386 if (add_resent) {
1387 fputs("Resent-", fo);
1388 mkdate(fo, "Date");
1389 if ((cp = myaddrs(NULL)) != NULL) {
1390 if (putname(cp, GCOMMA, SEND_MBOX, NULL,
1391 "Resent-From:", fo, &fromfield))
1392 return 1;
1394 if ((cp = value("sender")) != NULL) {
1395 if (putname(cp, GCOMMA, SEND_MBOX, NULL,
1396 "Resent-Sender:", fo, &senderfield))
1397 return 1;
1399 if (fmt("Resent-To:", to, fo, 1, 1, 0)) {
1400 if (buf)
1401 free(buf);
1402 return 1;
1404 if (value("stealthmua") == NULL) {
1405 fputs("Resent-", fo);
1406 message_id(fo, NULL);
1409 if (check_from_and_sender(fromfield, senderfield))
1410 return 1;
1412 * Write the original headers.
1414 while (count > 0) {
1415 if ((cp = foldergets(&buf, &bufsize, &count, &c, fi)) == NULL)
1416 break;
1417 if (ascncasecmp("status: ", buf, 8) != 0
1418 && strncmp("From ", buf, 5) != 0) {
1419 fwrite(buf, sizeof *buf, c, fo);
1421 if (count > 0 && *buf == '\n')
1422 break;
1425 * Write the message body.
1427 while (count > 0) {
1428 if (foldergets(&buf, &bufsize, &count, &c, fi) == NULL)
1429 break;
1430 if (count == 0 && *buf == '\n')
1431 break;
1432 fwrite(buf, sizeof *buf, c, fo);
1434 if (buf)
1435 free(buf);
1436 if (ferror(fo)) {
1437 perror(catgets(catd, CATSET, 188, "temporary mail file"));
1438 return 1;
1440 return 0;
1443 enum okay
1444 resend_msg(struct message *mp, struct name *to, int add_resent)
1446 FILE *ibuf, *nfo, *nfi;
1447 char *tempMail;
1448 struct header head;
1449 enum okay ok = STOP;
1451 memset(&head, 0, sizeof head);
1452 if ((to = checkaddrs(to)) == NULL) {
1453 senderr++;
1454 return STOP;
1456 if ((nfo = Ftemp(&tempMail, "Rs", "w", 0600, 1)) == NULL) {
1457 senderr++;
1458 perror(catgets(catd, CATSET, 189, "temporary mail file"));
1459 return STOP;
1461 if ((nfi = Fopen(tempMail, "r")) == NULL) {
1462 senderr++;
1463 perror(tempMail);
1464 return STOP;
1466 rm(tempMail);
1467 Ftfree(&tempMail);
1468 if ((ibuf = setinput(&mb, mp, NEED_BODY)) == NULL)
1469 return STOP;
1470 head.h_to = to;
1471 to = fixhead(&head, to);
1472 if (infix_resend(ibuf, nfo, mp, head.h_to, add_resent) != 0) {
1473 senderr++;
1474 rewind(nfo);
1475 savedeadletter(nfi);
1476 fputs(catgets(catd, CATSET, 190,
1477 ". . . message not sent.\n"), stderr);
1478 Fclose(nfo);
1479 Fclose(nfi);
1480 return STOP;
1482 fflush(nfo);
1483 rewind(nfo);
1484 Fclose(nfo);
1485 to = outof(to, nfi, &head);
1486 if (senderr)
1487 savedeadletter(nfi);
1488 to = elide(to);
1489 if (count(to) != 0) {
1490 if (value("record-resent") == NULL ||
1491 mightrecord(nfi, to, 0) == OKAY)
1492 ok = transfer(to, head.h_smopts, nfi, NULL);
1493 } else if (senderr == 0)
1494 ok = OKAY;
1495 Fclose(nfi);
1496 return ok;