Merge branch 'topic/notsupp'
[s-mailx.git] / sendout.c
bloba84ede102568abe52a86ede133f256fda91beaa5
1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ Mail to others.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 - 2015 Steffen (Daode) Nurpmeso <sdaoden@users.sf.net>.
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. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
35 #undef n_FILE
36 #define n_FILE sendout
38 #ifndef HAVE_AMALGAMATION
39 # include "nail.h"
40 #endif
42 #undef SEND_LINESIZE
43 #define SEND_LINESIZE \
44 ((1024 / B64_ENCODE_INPUT_PER_LINE) * B64_ENCODE_INPUT_PER_LINE)
46 enum fmt_flags {
47 FMT_DOMIME = 1<<0,
48 FMT_COMMA = GCOMMA,
49 FMT_FILES = GFILES,
50 _FMT_GMASK = FMT_COMMA | FMT_FILES
52 CTA(!(_FMT_GMASK & FMT_DOMIME));
54 static char const *__sendout_ident; /* TODO temporary hack; rewrite puthead() */
55 static char * _sendout_boundary;
56 static si8_t _sendout_error;
58 static enum okay _putname(char const *line, enum gfield w,
59 enum sendaction action, size_t *gotcha,
60 char const *prefix, FILE *fo, struct name **xp,
61 enum gfield addflags);
63 /* Place Content-Type:, Content-Transfer-Encoding:, Content-Disposition:
64 * headers, respectively */
65 static int _put_ct(FILE *fo, char const *contenttype,
66 char const *charset);
67 SINLINE int _put_cte(FILE *fo, enum conversion conv);
68 static int _put_cd(FILE *fo, char const *cd, char const *filename);
70 /* Write an attachment to the file buffer, converting to MIME */
71 static int _attach_file(struct attachment *ap, FILE *fo);
72 static int __attach_file(struct attachment *ap, FILE *fo);
74 /* There are non-local receivers, collect credentials etc. */
75 static bool_t _sendbundle_setup_creds(struct sendbundle *sbpm,
76 bool_t signing_caps);
78 /* Attach a message to the file buffer */
79 static int attach_message(struct attachment *ap, FILE *fo);
81 /* Generate the body of a MIME multipart message */
82 static int make_multipart(struct header *hp, int convert, FILE *fi,
83 FILE *fo, char const *contenttype, char const *charset);
85 /* Prepend a header in front of the collected stuff and return the new file */
86 static FILE * infix(struct header *hp, FILE *fi);
88 /* Check wether Disposition-Notification-To: is desired */
89 static bool_t _check_dispo_notif(struct name *mdn, struct header *hp,
90 FILE *fo);
92 /* Send mail to a bunch of user names. The interface is through mail() */
93 static int sendmail_internal(void *v, int recipient_record);
95 /* Deal with file and pipe addressees */
96 static struct name * _outof(struct name *names, FILE *fo, bool_t *senderror);
98 /* Record outgoing mail if instructed to do so; in *record* unless to is set */
99 static bool_t mightrecord(FILE *fp, struct name *to);
101 static int __savemail(char const *name, FILE *fp);
103 /* */
104 static bool_t _transfer(struct sendbundle *sbp);
106 static bool_t __mta_start(struct sendbundle *sbp);
107 static char const ** __mta_prepare_args(struct name *to, struct header *hp);
108 static void __mta_debug(struct sendbundle *sbp, char const *mta,
109 char const **args);
111 /* Create a Message-Id: header field. Use either host name or from address */
112 static char * _message_id(struct header *hp);
114 /* Format the given header line to not exceed 72 characters */
115 static int fmt(char const *str, struct name *np, FILE *fo,
116 enum fmt_flags ff);
118 /* Rewrite a message for resending, adding the Resent-Headers */
119 static int infix_resend(FILE *fi, FILE *fo, struct message *mp,
120 struct name *to, int add_resent);
122 static enum okay
123 _putname(char const *line, enum gfield w, enum sendaction action,
124 size_t *gotcha, char const *prefix, FILE *fo, struct name **xp,
125 enum gfield addflags)
127 struct name *np;
128 enum okay rv = STOP;
129 NYD_ENTER;
131 np = lextract(line, GEXTRA | GFULL | addflags);
132 if (xp != NULL)
133 *xp = np;
134 if (np == NULL)
136 else if (fmt(prefix, np, fo, ((w & GCOMMA) |
137 ((action != SEND_TODISP) ? FMT_DOMIME : 0))))
138 rv = OKAY;
139 else if (gotcha != NULL)
140 ++(*gotcha);
141 NYD_LEAVE;
142 return rv;
145 static int
146 _put_ct(FILE *fo, char const *contenttype, char const *charset)
148 int rv, i;
149 NYD2_ENTER;
151 if ((rv = fprintf(fo, "Content-Type: %s", contenttype)) < 0)
152 goto jerr;
154 if (charset == NULL)
155 goto jend;
157 if (putc(';', fo) == EOF)
158 goto jerr;
159 ++rv;
161 if (strlen(contenttype) + sizeof("Content-Type: ;")-1 > 50) {
162 if (putc('\n', fo) == EOF)
163 goto jerr;
164 ++rv;
167 if ((i = fprintf(fo, " charset=%s", charset)) < 0)
168 goto jerr;
169 rv += i;
171 jend:
172 if (putc('\n', fo) == EOF)
173 goto jerr;
174 ++rv;
175 jleave:
176 NYD2_LEAVE;
177 return rv;
178 jerr:
179 rv = -1;
180 goto jleave;
183 SINLINE int
184 _put_cte(FILE *fo, enum conversion conv)
186 int rv;
187 NYD2_ENTER;
189 /* RFC 2045, 6.1.:
190 * This is the default value -- that is,
191 * "Content-Transfer-Encoding: 7BIT" is assumed if the
192 * Content-Transfer-Encoding header field is not present.
194 rv = (conv == CONV_7BIT) ? 0
195 : fprintf(fo, "Content-Transfer-Encoding: %s\n",
196 mime_enc_from_conversion(conv));
197 NYD2_LEAVE;
198 return rv;
201 static int
202 _put_cd(FILE *fo, char const *cd, char const *filename)
204 struct str f;
205 si8_t mpc;
206 int rv;
207 NYD2_ENTER;
209 f.s = NULL;
211 /* xxx Ugly with the trailing space in case of wrap! */
212 if ((rv = fprintf(fo, "Content-Disposition: %s; ", cd)) < 0)
213 goto jerr;
215 if (!(mpc = mime_param_create(&f, "filename", filename)))
216 goto jerr;
217 /* Always fold if result contains newlines */
218 if (mpc < 0 || f.l + rv > MIME_LINELEN) { /* FIXME MIME_LINELEN_MAX */
219 if (putc('\n', fo) == EOF || putc(' ', fo) == EOF)
220 goto jerr;
221 rv += 2;
223 if (fputs(f.s, fo) == EOF || putc('\n', fo) == EOF)
224 goto jerr;
225 rv += (int)++f.l;
227 jleave:
228 NYD2_LEAVE;
229 return rv;
230 jerr:
231 rv = -1;
232 goto jleave;
236 static int
237 _attach_file(struct attachment *ap, FILE *fo)
239 /* TODO of course, the MIME classification needs to performed once
240 * TODO only, not for each and every charset anew ... ;-// */
241 char *charset_iter_orig[2];
242 long offs;
243 int err = 0;
244 NYD_ENTER;
246 /* Is this already in target charset? Simply copy over */
247 if (ap->a_conv == AC_TMPFILE) {
248 err = __attach_file(ap, fo);
249 Fclose(ap->a_tmpf);
250 DBG( ap->a_tmpf = NULL; )
251 goto jleave;
254 /* If we don't apply charset conversion at all (fixed input=ouput charset)
255 * we also simply copy over, since it's the users desire */
256 if (ap->a_conv == AC_FIX_INCS) {
257 ap->a_charset = ap->a_input_charset;
258 err = __attach_file(ap, fo);
259 goto jleave;
262 /* Otherwise we need to iterate over all possible output charsets */
263 if ((offs = ftell(fo)) == -1) {
264 err = EIO;
265 goto jleave;
267 charset_iter_recurse(charset_iter_orig);
268 for (charset_iter_reset(NULL);; charset_iter_next()) {
269 if (!charset_iter_is_valid()) {
270 err = EILSEQ;
271 break;
273 err = __attach_file(ap, fo);
274 if (err == 0 || (err != EILSEQ && err != EINVAL))
275 break;
276 clearerr(fo);
277 if (fseek(fo, offs, SEEK_SET) == -1) {
278 err = EIO;
279 break;
281 if (ap->a_conv != AC_DEFAULT) {
282 err = EILSEQ;
283 break;
285 ap->a_charset = NULL;
287 charset_iter_restore(charset_iter_orig);
288 jleave:
289 NYD_LEAVE;
290 return err;
293 static int
294 __attach_file(struct attachment *ap, FILE *fo) /* XXX linelength */
296 int err = 0, do_iconv;
297 FILE *fi;
298 char const *charset;
299 enum conversion convert;
300 char *buf;
301 size_t bufsize, lncnt, inlen;
302 NYD_ENTER;
304 /* Either charset-converted temporary file, or plain path */
305 if (ap->a_conv == AC_TMPFILE) {
306 fi = ap->a_tmpf;
307 assert(ftell(fi) == 0);
308 } else if ((fi = Fopen(ap->a_name, "r")) == NULL) {
309 err = errno;
310 n_err(_("\"%s\": %s\n"), ap->a_name, strerror(errno));
311 goto jleave;
314 /* MIME part header for attachment */
315 { char const *bn = ap->a_name, *ct;
317 if ((ct = strrchr(bn, '/')) != NULL)
318 bn = ++ct;
319 ct = ap->a_content_type;
320 charset = ap->a_charset;
321 convert = mime_type_classify_file(fi, (char const**)&ct,
322 &charset, &do_iconv);
323 if (charset == NULL || ap->a_conv == AC_FIX_INCS ||
324 ap->a_conv == AC_TMPFILE)
325 do_iconv = 0;
327 if (fprintf(fo, "\n--%s\n", _sendout_boundary) < 0 ||
328 _put_ct(fo, ct, charset) < 0 || _put_cte(fo, convert) < 0 ||
329 _put_cd(fo, ap->a_content_disposition, bn) < 0)
330 goto jerr_header;
332 if ((bn = ap->a_content_id) != NULL &&
333 fprintf(fo, "Content-ID: %s\n", bn) == -1)
334 goto jerr_header;
336 if ((bn = ap->a_content_description) != NULL &&
337 fprintf(fo, "Content-Description: %s\n", bn) == -1) /* TODO MIME! */
338 goto jerr_header;
340 if (putc('\n', fo) == EOF) {
341 jerr_header:
342 err = errno;
343 goto jerr_fclose;
347 #ifdef HAVE_ICONV
348 if (iconvd != (iconv_t)-1)
349 n_iconv_close(iconvd);
350 if (do_iconv) {
351 char const *tcs = charset_get_lc();
352 if (asccasecmp(charset, tcs) &&
353 (iconvd = n_iconv_open(charset, tcs)) == (iconv_t)-1 &&
354 (err = errno) != 0) {
355 if (err == EINVAL)
356 n_err(_("Cannot convert from %s to %s\n"), tcs, charset);
357 else
358 n_err(_("iconv_open: %s\n"), strerror(err));
359 goto jerr_fclose;
362 #endif
364 bufsize = SEND_LINESIZE;
365 buf = smalloc(bufsize);
366 if (convert == CONV_TOQP
367 #ifdef HAVE_ICONV
368 || iconvd != (iconv_t)-1
369 #endif
371 lncnt = fsize(fi);
372 for (;;) {
373 if (convert == CONV_TOQP
374 #ifdef HAVE_ICONV
375 || iconvd != (iconv_t)-1
376 #endif
378 if (fgetline(&buf, &bufsize, &lncnt, &inlen, fi, 0) == NULL)
379 break;
380 } else if ((inlen = fread(buf, sizeof *buf, bufsize, fi)) == 0)
381 break;
382 if (xmime_write(buf, inlen, fo, convert, TD_ICONV) < 0) {
383 err = errno;
384 goto jerr;
387 if (ferror(fi))
388 err = EDOM;
389 jerr:
390 free(buf);
391 jerr_fclose:
392 if (ap->a_conv != AC_TMPFILE)
393 Fclose(fi);
394 jleave:
395 NYD_LEAVE;
396 return err;
399 static bool_t
400 _sendbundle_setup_creds(struct sendbundle *sbp, bool_t signing_caps)
402 bool_t v15, rv = FAL0;
403 char *shost, *from;
404 #ifdef HAVE_SMTP
405 char *smtp;
406 #endif
407 NYD_ENTER;
409 v15 = ok_blook(v15_compat);
410 shost = (v15 ? ok_vlook(smtp_hostname) : NULL);
411 from = ((signing_caps || !v15 || shost == NULL)
412 ? skin(myorigin(sbp->sb_hp)) : NULL);
414 if (signing_caps) {
415 if (from == NULL) {
416 #ifdef HAVE_SSL
417 n_err(_("No *from* address for signing specified\n"));
418 goto jleave;
419 #endif
420 } else
421 sbp->sb_signer.l = strlen(sbp->sb_signer.s = from);
424 #ifdef HAVE_SMTP
425 if ((smtp = ok_vlook(smtp)) == NULL) {
426 rv = TRU1;
427 goto jleave;
430 if (!url_parse(&sbp->sb_url, CPROTO_SMTP, smtp))
431 goto jleave;
433 if (v15) {
434 if (shost == NULL) {
435 if (from == NULL)
436 goto jenofrom;
437 sbp->sb_url.url_u_h.l = strlen(sbp->sb_url.url_u_h.s = from);
438 } else
439 __sendout_ident = sbp->sb_url.url_u_h.s;
440 if (!ccred_lookup(&sbp->sb_ccred, &sbp->sb_url))
441 goto jleave;
442 } else {
443 if (sbp->sb_url.url_had_user || sbp->sb_url.url_pass.s != NULL) {
444 n_err(_("New-style URL used without *v15-compat* being set\n"));
445 goto jleave;
447 /* TODO part of the entire myorigin() disaster, get rid of this! */
448 if (from == NULL) {
449 jenofrom:
450 n_err(_("Your configuration requires a *from* address, "
451 "but none was given\n"));
452 goto jleave;
454 if (!ccred_lookup_old(&sbp->sb_ccred, CPROTO_SMTP, from))
455 goto jleave;
456 sbp->sb_url.url_u_h.l = strlen(sbp->sb_url.url_u_h.s = from);
459 rv = TRU1;
460 #endif /* HAVE_SMTP */
461 #if defined HAVE_SSL || defined HAVE_SMTP
462 jleave:
463 #endif
464 NYD_LEAVE;
465 return rv;
468 static int
469 attach_message(struct attachment *ap, FILE *fo)
471 struct message *mp;
472 char const *ccp;
473 int rv;
474 NYD_ENTER;
476 fprintf(fo, "\n--%s\nContent-Type: message/rfc822\n"
477 "Content-Disposition: inline\n", _sendout_boundary);
478 if ((ccp = ap->a_content_description) != NULL)
479 fprintf(fo, "Content-Description: %s\n", ccp);/* TODO MIME! */
480 putc('\n', fo);
482 mp = message + ap->a_msgno - 1;
483 touch(mp);
484 rv = (sendmp(mp, fo, 0, NULL, SEND_RFC822, NULL) < 0) ? -1 : 0;
485 NYD_LEAVE;
486 return rv;
489 static int
490 make_multipart(struct header *hp, int convert, FILE *fi, FILE *fo,
491 char const *contenttype, char const *charset)
493 struct attachment *att;
494 int rv = -1;
495 NYD_ENTER;
497 fputs("This is a multi-part message in MIME format.\n", fo);
498 if (fsize(fi) != 0) {
499 char *buf;
500 size_t sz, bufsize, cnt;
502 if (fprintf(fo, "\n--%s\n", _sendout_boundary) < 0 ||
503 _put_ct(fo, contenttype, charset) < 0 ||
504 _put_cte(fo, convert) < 0 ||
505 fprintf(fo, "Content-Disposition: inline\n\n") < 0)
506 goto jleave;
508 buf = smalloc(bufsize = SEND_LINESIZE);
509 if (convert == CONV_TOQP
510 #ifdef HAVE_ICONV
511 || iconvd != (iconv_t)-1
512 #endif
514 fflush(fi);
515 cnt = fsize(fi);
517 for (;;) {
518 if (convert == CONV_TOQP
519 #ifdef HAVE_ICONV
520 || iconvd != (iconv_t)-1
521 #endif
523 if (fgetline(&buf, &bufsize, &cnt, &sz, fi, 0) == NULL)
524 break;
525 } else if ((sz = fread(buf, sizeof *buf, bufsize, fi)) == 0)
526 break;
528 if (xmime_write(buf, sz, fo, convert, TD_ICONV) < 0) {
529 free(buf);
530 goto jleave;
533 free(buf);
535 if (ferror(fi))
536 goto jleave;
539 for (att = hp->h_attach; att != NULL; att = att->a_flink) {
540 if (att->a_msgno) {
541 if (attach_message(att, fo) != 0)
542 goto jleave;
543 } else if (_attach_file(att, fo) != 0)
544 goto jleave;
547 /* the final boundary with two attached dashes */
548 fprintf(fo, "\n--%s--\n", _sendout_boundary);
549 rv = 0;
550 jleave:
551 NYD_LEAVE;
552 return rv;
555 static FILE *
556 infix(struct header *hp, FILE *fi) /* TODO check */
558 FILE *nfo, *nfi = NULL;
559 char *tempMail;
560 char const *contenttype, *charset = NULL;
561 enum conversion convert;
562 int do_iconv = 0, err;
563 #ifdef HAVE_ICONV
564 char const *tcs, *convhdr = NULL;
565 #endif
566 NYD_ENTER;
568 if ((nfo = Ftmp(&tempMail, "infix", OF_WRONLY | OF_HOLDSIGS | OF_REGISTER))
569 == NULL) {
570 n_perr(_("temporary mail file"), 0);
571 goto jleave;
573 if ((nfi = Fopen(tempMail, "r")) == NULL) {
574 n_perr(tempMail, 0);
575 Fclose(nfo);
577 Ftmp_release(&tempMail);
578 if (nfi == NULL)
579 goto jleave;
581 pstate &= ~PS_HEADER_NEEDED_MIME; /* TODO a hack should be carrier tracked */
583 contenttype = "text/plain"; /* XXX mail body - always text/plain, want XX? */
584 convert = mime_type_classify_file(fi, &contenttype, &charset, &do_iconv);
586 #ifdef HAVE_ICONV
587 tcs = charset_get_lc();
588 if ((convhdr = need_hdrconv(hp, GTO | GSUBJECT | GCC | GBCC | GIDENT))) {
589 if (iconvd != (iconv_t)-1) /* XXX */
590 n_iconv_close(iconvd);
591 if (asccasecmp(convhdr, tcs) != 0 &&
592 (iconvd = n_iconv_open(convhdr, tcs)) == (iconv_t)-1 &&
593 (err = errno) != 0)
594 goto jiconv_err;
596 #endif
597 if (puthead(FAL0, hp, nfo,
598 (GTO | GSUBJECT | GCC | GBCC | GNL | GCOMMA | GUA | GMIME | GMSGID |
599 GIDENT | GREF | GDATE), SEND_MBOX, convert, contenttype, charset))
600 goto jerr;
601 #ifdef HAVE_ICONV
602 if (iconvd != (iconv_t)-1)
603 n_iconv_close(iconvd);
604 #endif
606 #ifdef HAVE_ICONV
607 if (do_iconv && charset != NULL) { /*TODO charset->mime_type_classify_file*/
608 if (asccasecmp(charset, tcs) != 0 &&
609 (iconvd = n_iconv_open(charset, tcs)) == (iconv_t)-1 &&
610 (err = errno) != 0) {
611 jiconv_err:
612 if (err == EINVAL)
613 n_err(_("Cannot convert from %s to %s\n"), tcs, charset);
614 else
615 n_perr("iconv_open", 0);
616 goto jerr;
619 #endif
621 if (hp->h_attach != NULL) {
622 if (make_multipart(hp, convert, fi, nfo, contenttype, charset) != 0)
623 goto jerr;
624 } else {
625 size_t sz, bufsize, cnt;
626 char *buf;
628 if (convert == CONV_TOQP
629 #ifdef HAVE_ICONV
630 || iconvd != (iconv_t)-1
631 #endif
633 fflush(fi);
634 cnt = fsize(fi);
636 buf = smalloc(bufsize = SEND_LINESIZE);
637 for (err = 0;;) {
638 if (convert == CONV_TOQP
639 #ifdef HAVE_ICONV
640 || iconvd != (iconv_t)-1
641 #endif
643 if (fgetline(&buf, &bufsize, &cnt, &sz, fi, 0) == NULL)
644 break;
645 } else if ((sz = fread(buf, sizeof *buf, bufsize, fi)) == 0)
646 break;
647 if (xmime_write(buf, sz, nfo, convert, TD_ICONV) < 0) {
648 err = 1;
649 break;
652 free(buf);
654 if (err || ferror(fi)) {
655 jerr:
656 Fclose(nfo);
657 Fclose(nfi);
658 #ifdef HAVE_ICONV
659 if (iconvd != (iconv_t)-1)
660 n_iconv_close(iconvd);
661 #endif
662 nfi = NULL;
663 goto jleave;
667 #ifdef HAVE_ICONV
668 if (iconvd != (iconv_t)-1)
669 n_iconv_close(iconvd);
670 #endif
672 fflush(nfo);
673 if ((err = ferror(nfo)))
674 n_perr(_("temporary mail file"), 0);
675 Fclose(nfo);
676 if (!err) {
677 fflush_rewind(nfi);
678 Fclose(fi);
679 } else {
680 Fclose(nfi);
681 nfi = NULL;
683 jleave:
684 NYD_LEAVE;
685 return nfi;
688 static bool_t
689 _check_dispo_notif(struct name *mdn, struct header *hp, FILE *fo)
691 char const *from;
692 bool_t rv = TRU1;
693 NYD_ENTER;
695 /* TODO smtp_disposition_notification (RFC 3798): relation to return-path
696 * TODO not yet checked */
697 if (!ok_blook(disposition_notification_send))
698 goto jleave;
700 if (mdn != NULL && mdn != (struct name*)0x1)
701 from = mdn->n_name;
702 else if ((from = myorigin(hp)) == NULL) {
703 if (options & OPT_D_V)
704 n_err(_("*disposition-notification-send*: no *from* set\n"));
705 goto jleave;
708 if (fmt("Disposition-Notification-To:", nalloc(UNCONST(from), 0), fo, 0))
709 rv = FAL0;
710 jleave:
711 NYD_LEAVE;
712 return rv;
715 static int
716 sendmail_internal(void *v, int recipient_record)
718 struct header head;
719 char *str = v;
720 int rv;
721 NYD_ENTER;
723 memset(&head, 0, sizeof head);
724 head.h_to = lextract(str, GTO | GFULL);
725 rv = mail1(&head, 0, NULL, NULL, recipient_record, 0);
726 NYD_LEAVE;
727 return (rv == 0);
730 static struct name *
731 _outof(struct name *names, FILE *fo, bool_t *senderror)
733 ui32_t pipecnt, xcnt, i;
734 int *fda;
735 char const *sh;
736 struct name *np;
737 FILE *fin = NULL, *fout;
738 NYD_ENTER;
740 /* Look through all recipients and do a quick return if no file or pipe
741 * addressee is found */
742 fda = NULL; /* Silence cc */
743 for (pipecnt = xcnt = 0, np = names; np != NULL; np = np->n_flink) {
744 if (np->n_type & GDEL)
745 continue;
746 switch (np->n_flags & NAME_ADDRSPEC_ISFILEORPIPE) {
747 case NAME_ADDRSPEC_ISFILE:
748 ++xcnt;
749 break;
750 case NAME_ADDRSPEC_ISPIPE:
751 ++pipecnt;
752 break;
755 if (pipecnt == 0 && xcnt == 0)
756 goto jleave;
758 /* Otherwise create an array of file descriptors for each found pipe
759 * addressee to get around the dup(2)-shared-file-offset problem, i.e.,
760 * each pipe subprocess needs its very own file descriptor, and we need
761 * to deal with that.
762 * To make our life a bit easier let's just use the auto-reclaimed
763 * string storage */
764 if (pipecnt == 0 || (options & OPT_DEBUG)) {
765 pipecnt = 0;
766 fda = NULL;
767 sh = NULL;
768 } else {
769 fda = salloc(sizeof(int) * pipecnt);
770 for (i = 0; i < pipecnt; ++i)
771 fda[i] = -1;
772 if ((sh = ok_vlook(SHELL)) == NULL)
773 sh = XSHELL;
776 for (np = names; np != NULL; np = np->n_flink) {
777 if (!(np->n_flags & NAME_ADDRSPEC_ISFILEORPIPE))
778 continue;
780 /* In days of old we removed the entry from the the list; now for sake of
781 * header expansion we leave it in and mark it as deleted */
782 np->n_type |= GDEL;
784 if (options & OPT_DEBUG) {
785 n_err(_(">>> Would write message via \"%s\"\n"), np->n_name);
786 continue;
788 if (options & OPT_VERBVERB)
789 n_err(_(">>> Writing message via \"%s\"\n"), np->n_name);
791 /* See if we have copied the complete message out yet. If not, do so */
792 if (image < 0) {
793 int c;
794 char *tempEdit;
796 if ((fout = Ftmp(&tempEdit, "outof",
797 OF_WRONLY | OF_HOLDSIGS | OF_REGISTER)) == NULL) {
798 n_perr(_("Creation of temporary image"), 0);
799 *senderror = TRU1;
800 goto jcant;
802 if ((image = open(tempEdit, O_RDWR | _O_CLOEXEC)) >= 0) {
803 _CLOEXEC_SET(image);
804 for (i = 0; i < pipecnt; ++i) {
805 int fd = open(tempEdit, O_RDONLY | _O_CLOEXEC);
806 if (fd < 0) {
807 close(image);
808 image = -1;
809 pipecnt = i;
810 break;
812 fda[i] = fd;
813 _CLOEXEC_SET(fd);
816 Ftmp_release(&tempEdit);
818 if (image < 0) {
819 n_perr(_("Creating descriptor duplicate of temporary image"), 0);
820 *senderror = TRU1;
821 Fclose(fout);
822 goto jcant;
825 fprintf(fout, "From %s %s", myname, time_current.tc_ctime);
826 c = EOF;
827 while (i = c, (c = getc(fo)) != EOF)
828 putc(c, fout);
829 rewind(fo);
830 if ((int)i != '\n')
831 putc('\n', fout);
832 putc('\n', fout);
833 fflush(fout);
834 if (ferror(fout)) {
835 n_perr(_("Finalizing write of temporary image"), 0);
836 Fclose(fout);
837 goto jcantfout;
839 Fclose(fout);
841 /* If we have to serve file addressees, open reader */
842 if (xcnt != 0 && (fin = Fdopen(image, "r", FAL0)) == NULL) {
843 n_perr(_("Failed to open a temporary image duplicate"), 0);
844 jcantfout:
845 *senderror = TRU1;
846 close(image);
847 image = -1;
848 goto jcant;
851 /* From now on use xcnt as a counter for pipecnt */
852 xcnt = 0;
855 /* Now either copy "image" to the desired file or give it as the standard
856 * input to the desired program as appropriate */
857 if (np->n_flags & NAME_ADDRSPEC_ISPIPE) {
858 int pid;
859 sigset_t nset;
861 sigemptyset(&nset);
862 sigaddset(&nset, SIGHUP);
863 sigaddset(&nset, SIGINT);
864 sigaddset(&nset, SIGQUIT);
865 pid = start_command(sh, &nset, fda[xcnt++], COMMAND_FD_NULL, "-c",
866 np->n_name + 1, NULL, NULL);
867 if (pid < 0) {
868 n_err(_("Piping message to \"%s\" failed\n"), np->n_name);
869 *senderror = TRU1;
870 goto jcant;
872 free_child(pid);
873 } else {
874 int c;
875 char *fname = file_expand(np->n_name);
877 if (fname == NULL) {
878 *senderror = TRU1;
879 goto jcant;
882 if ((fout = Zopen(fname, "a")) == NULL) {
883 n_err(_("Writing message to \"%s\" failed: %s\n"),
884 fname, strerror(errno));
885 *senderror = TRU1;
886 goto jcant;
888 rewind(fin);
889 while ((c = getc(fin)) != EOF)
890 putc(c, fout);
891 if (ferror(fout)) {
892 n_err(_("Writing message to \"%s\" failed: %s\n"),
893 fname, _("write error"));
894 *senderror = TRU1;
896 Fclose(fout);
899 jcant:
900 if (image < 0)
901 goto jdelall;
904 jleave:
905 if (fin != NULL)
906 Fclose(fin);
907 for (i = 0; i < pipecnt; ++i)
908 close(fda[i]);
909 if (image >= 0) {
910 close(image);
911 image = -1;
913 NYD_LEAVE;
914 return names;
916 jdelall:
917 while (np != NULL) {
918 if (np->n_flags & NAME_ADDRSPEC_ISFILEORPIPE)
919 np->n_type |= GDEL;
920 np = np->n_flink;
922 goto jleave;
925 static bool_t
926 mightrecord(FILE *fp, struct name *to)
928 char *cp, *cq;
929 char const *ep;
930 bool_t rv = TRU1;
931 NYD_ENTER;
933 if (options & OPT_DEBUG)
934 cp = NULL;
935 else if (to != NULL) {
936 cp = savestr(skinned_name(to));
937 for (cq = cp; *cq != '\0' && *cq != '@'; ++cq)
939 *cq = '\0';
940 } else
941 cp = ok_vlook(record);
943 if (cp != NULL) {
944 if ((ep = expand(cp)) == NULL) {
945 ep = "NULL";
946 goto jbail;
949 if (*ep != '/' && *ep != '+' && ok_blook(outfolder) &&
950 which_protocol(ep) == PROTO_FILE) {
951 size_t i = strlen(cp);
952 cq = salloc(i + 1 +1);
953 cq[0] = '+';
954 memcpy(cq + 1, cp, i +1);
955 cp = cq;
956 if ((ep = file_expand(cp)) == NULL) {
957 ep = "NULL";
958 goto jbail;
962 if (__savemail(ep, fp) != 0) {
963 jbail:
964 n_err(_("Failed to save message in \"%s\" - message not sent\n"), ep);
965 exit_status |= EXIT_ERR;
966 savedeadletter(fp, 1);
967 rv = FAL0;
970 NYD_LEAVE;
971 return rv;
974 static int
975 __savemail(char const *name, FILE *fp)
977 FILE *fo;
978 char *buf;
979 size_t bufsize, buflen, cnt;
980 int prependnl = 0, rv = -1;
981 bool_t emptyline;
982 NYD_ENTER;
984 buf = smalloc(bufsize = LINESIZE);
986 if ((fo = Zopen(name, "a+")) == NULL) {
987 if ((fo = Zopen(name, "wx")) == NULL) {
988 n_perr(name, 0);
989 goto jleave;
991 } else {
992 if (fseek(fo, -2L, SEEK_END) == 0) {
993 switch (fread(buf, sizeof *buf, 2, fo)) {
994 case 2:
995 if (buf[1] != '\n') {
996 prependnl = 1;
997 break;
999 /* FALLTHRU */
1000 case 1:
1001 if (buf[0] != '\n')
1002 prependnl = 1;
1003 break;
1004 default:
1005 if (ferror(fo)) {
1006 n_perr(name, 0);
1007 goto jleave;
1010 if (prependnl) {
1011 putc('\n', fo);
1013 fflush(fo);
1017 fprintf(fo, "From %s %s", myname, time_current.tc_ctime);
1018 fflush_rewind(fp);
1019 cnt = fsize(fp);
1020 buflen = 0;
1021 emptyline = FAL0;
1022 while (fgetline(&buf, &bufsize, &cnt, &buflen, fp, 0) != NULL) {
1023 #ifdef HAVE_DEBUG /* TODO assert legacy */
1024 assert(!is_head(buf, buflen, TRU1));
1025 UNUSED(emptyline);
1026 #else
1027 if (emptyline && is_head(buf, buflen, TRU1))
1028 putc('>', fo);
1029 #endif
1030 emptyline = (buflen > 0 && *buf == '\n');
1031 fwrite(buf, sizeof *buf, buflen, fo);
1033 if (buflen && *(buf + buflen - 1) != '\n')
1034 putc('\n', fo);
1035 putc('\n', fo);
1036 fflush(fo);
1038 rv = 0;
1039 if (ferror(fo)) {
1040 n_perr(name, 0);
1041 rv = -1;
1043 if (Fclose(fo) != 0)
1044 rv = -1;
1045 fflush_rewind(fp);
1046 jleave:
1047 free(buf);
1048 NYD_LEAVE;
1049 return rv;
1052 static bool_t
1053 _transfer(struct sendbundle *sbp)
1055 struct name *np;
1056 ui32_t cnt;
1057 bool_t rv = TRU1;
1058 NYD_ENTER;
1060 for (cnt = 0, np = sbp->sb_to; np != NULL;) {
1061 char const k[] = "smime-encrypt-";
1062 size_t nl = strlen(np->n_name);
1063 char *cp, *vs = ac_alloc(sizeof(k)-1 + nl +1);
1064 memcpy(vs, k, sizeof(k) -1);
1065 memcpy(vs + sizeof(k) -1, np->n_name, nl +1);
1067 if ((cp = vok_vlook(vs)) != NULL) {
1068 #ifdef HAVE_SSL
1069 FILE *ef;
1071 if ((ef = smime_encrypt(sbp->sb_input, cp, np->n_name)) != NULL) {
1072 FILE *fisave = sbp->sb_input;
1073 struct name *nsave = sbp->sb_to;
1075 sbp->sb_to = ndup(np, np->n_type & ~(GFULL | GSKIN));
1076 sbp->sb_input = ef;
1077 if (!__mta_start(sbp))
1078 rv = FAL0;
1079 sbp->sb_to = nsave;
1080 sbp->sb_input = fisave;
1082 Fclose(ef);
1083 } else {
1084 #else
1085 n_err(_("No SSL support compiled in\n"));
1086 rv = FAL0;
1087 #endif
1088 n_err(_("Message not sent to \"%s\"\n"), np->n_name);
1089 _sendout_error = TRU1;
1090 #ifdef HAVE_SSL
1092 #endif
1093 rewind(sbp->sb_input);
1095 if (np->n_flink != NULL)
1096 np->n_flink->n_blink = np->n_blink;
1097 if (np->n_blink != NULL)
1098 np->n_blink->n_flink = np->n_flink;
1099 if (np == sbp->sb_to)
1100 sbp->sb_to = np->n_flink;
1101 np = np->n_flink;
1102 } else {
1103 ++cnt;
1104 np = np->n_flink;
1106 ac_free(vs);
1109 if (cnt > 0 && (ok_blook(smime_force_encryption) || !__mta_start(sbp)))
1110 rv = FAL0;
1111 NYD_LEAVE;
1112 return rv;
1115 static bool_t
1116 __mta_start(struct sendbundle *sbp)
1118 char const **args = NULL, *mta, *smtp;
1119 pid_t pid;
1120 sigset_t nset;
1121 bool_t rv = FAL0;
1122 NYD_ENTER;
1124 if ((smtp = ok_vlook(smtp)) == NULL) {
1125 if ((mta = ok_vlook(sendmail)) != NULL) {
1126 if ((mta = file_expand(mta)) == NULL)
1127 goto jstop;
1128 } else
1129 mta = SENDMAIL;
1131 args = __mta_prepare_args(sbp->sb_to, sbp->sb_hp);
1132 if (options & OPT_DEBUG) {
1133 __mta_debug(sbp, mta, args);
1134 rv = TRU1;
1135 goto jleave;
1137 } else {
1138 UNINIT(mta, NULL); /* Silence cc */
1139 #ifndef HAVE_SMTP
1140 n_err(_("No SMTP support compiled in\n"));
1141 goto jstop;
1142 #else
1143 if (options & OPT_DEBUG) {
1144 (void)smtp_mta(sbp);
1145 rv = TRU1;
1146 goto jleave;
1148 #endif
1151 /* Fork, set up the temporary mail file as standard input for "mail", and
1152 * exec with the user list we generated far above */
1153 if ((pid = fork_child()) == -1) {
1154 n_perr("fork", 0);
1155 jstop:
1156 savedeadletter(sbp->sb_input, 0);
1157 _sendout_error = TRU1;
1158 goto jleave;
1160 if (pid == 0) {
1161 sigemptyset(&nset);
1162 sigaddset(&nset, SIGHUP);
1163 sigaddset(&nset, SIGINT);
1164 sigaddset(&nset, SIGQUIT);
1165 sigaddset(&nset, SIGTSTP);
1166 sigaddset(&nset, SIGTTIN);
1167 sigaddset(&nset, SIGTTOU);
1168 freopen("/dev/null", "r", stdin);
1169 #ifdef HAVE_SMTP
1170 if (smtp != NULL) {
1171 prepare_child(&nset, 0, 1);
1172 if (smtp_mta(sbp))
1173 _exit(EXIT_OK);
1174 } else
1175 #endif
1177 int e;
1179 prepare_child(&nset, fileno(sbp->sb_input), -1);
1180 /* If *record* is set then savemail() will move the file position;
1181 * it'll call rewind(), but that may optimize away the systemcall if
1182 * possible, and since dup2() shares the position with the original FD
1183 * the MTA may end up reading nothing */
1184 lseek(0, 0, SEEK_SET);
1185 execv(mta, UNCONST(args));
1186 e = errno;
1187 smtp = (e != ENOENT) ? strerror(e)
1188 : _("executable not found (adjust *sendmail* variable)");
1189 n_err(_("Cannot start \"%s\": %s\n"), mta, smtp);
1191 savedeadletter(sbp->sb_input, 1);
1192 n_err(_("... message not sent\n"));
1193 _exit(EXIT_ERR);
1196 if ((options & (OPT_DEBUG | OPT_VERB)) || ok_blook(sendwait)) {
1197 if (wait_child(pid, NULL))
1198 rv = TRU1;
1199 else
1200 _sendout_error = TRU1;
1201 } else {
1202 rv = TRU1;
1203 free_child(pid);
1205 jleave:
1206 NYD_LEAVE;
1207 return rv;
1210 static char const **
1211 __mta_prepare_args(struct name *to, struct header *hp)
1213 size_t vas_count, i, j;
1214 char **vas, *cp;
1215 char const **args;
1216 bool_t snda;
1217 NYD_ENTER;
1219 if ((cp = ok_vlook(sendmail_arguments)) == NULL) {
1220 vas_count = 0;
1221 vas = NULL;
1222 } else {
1223 /* Don't assume anything on the content but do allocate exactly j slots */
1224 j = strlen(cp);
1225 vas = ac_alloc(sizeof(*vas) * j);
1226 vas_count = (size_t)getrawlist(cp, j, vas, (int)j, FAL0);
1229 i = 4 + smopts_count + vas_count + 4 + 1 + count(to) + 1;
1230 args = salloc(i * sizeof(char*));
1232 if ((args[0] = ok_vlook(sendmail_progname)) == NULL || *args[0] == '\0')
1233 args[0] = SENDMAIL_PROGNAME;
1235 if ((snda = ok_blook(sendmail_no_default_arguments)))
1236 i = 1;
1237 else {
1238 args[1] = "-i";
1239 i = 2;
1240 if (ok_blook(metoo))
1241 args[i++] = "-m";
1242 if (options & OPT_VERB)
1243 args[i++] = "-v";
1246 for (j = 0; j < smopts_count; ++j, ++i)
1247 args[i] = smopts[j];
1249 for (j = 0; j < vas_count; ++j, ++i)
1250 args[i] = vas[j];
1252 /* -r option? In conjunction with -t we act compatible to postfix(1) and
1253 * ignore it (it is -f / -F there) if the message specified From:/Sender:.
1254 * The interdependency with -t has been resolved in puthead() */
1255 if (!snda && (options & OPT_r_FLAG)) {
1256 struct name const *np;
1258 if (hp != NULL && (np = hp->h_from) != NULL) {
1259 /* However, what wasn't resolved there was the case that the message
1260 * specified multiple From: addresses and a Sender: */
1261 if ((pstate & PS_t_FLAG) && hp->h_sender != NULL)
1262 np = hp->h_sender;
1264 if (np->n_fullextra != NULL) {
1265 args[i++] = "-F";
1266 args[i++] = np->n_fullextra;
1268 cp = np->n_name;
1269 } else {
1270 assert(option_r_arg == NULL);
1271 cp = skin(myorigin(NULL));
1274 if (cp != NULL) {
1275 args[i++] = "-f";
1276 args[i++] = cp;
1280 /* Terminate option list to avoid false interpretation of system-wide
1281 * aliases that start with hyphen */
1282 if (!snda)
1283 args[i++] = "--";
1285 /* Receivers follow */
1286 for (; to != NULL; to = to->n_flink)
1287 if (!(to->n_type & GDEL))
1288 args[i++] = to->n_name;
1289 args[i] = NULL;
1291 if (vas != NULL)
1292 ac_free(vas);
1293 NYD_LEAVE;
1294 return args;
1297 static void
1298 __mta_debug(struct sendbundle *sbp, char const *mta, char const **args)
1300 size_t cnt, bufsize;
1301 char *buf;
1302 NYD_ENTER;
1304 n_err(_(">>> MTA: \"%s\", arguments:"), mta);
1305 for (; *args != NULL; ++args)
1306 n_err(" \"%s\"", *args);
1307 n_err("\n");
1309 fflush_rewind(sbp->sb_input);
1311 cnt = fsize(sbp->sb_input);
1312 buf = NULL;
1313 bufsize = 0;
1314 while (fgetline(&buf, &bufsize, &cnt, NULL, sbp->sb_input, 1) != NULL)
1315 n_err(">>> %s", buf);
1316 if (buf != NULL)
1317 free(buf);
1318 NYD_LEAVE;
1321 static char *
1322 _message_id(struct header *hp)
1324 char *rv = NULL, sep;
1325 char const *h;
1326 size_t rl, i;
1327 struct tm *tmp;
1328 NYD_ENTER;
1330 if (hp != NULL && hp->h_message_id != NULL) {
1331 i = strlen(hp->h_message_id->n_name);
1332 rl = sizeof("Message-ID: <>") -1;
1333 rv = salloc(rl + i +1);
1334 memcpy(rv, "Message-ID: <", --rl);
1335 memcpy(rv + rl, hp->h_message_id->n_name, i);
1336 rl += i;
1337 rv[rl++] = '>';
1338 rv[rl] = '\0';
1339 goto jleave;
1342 if (ok_blook(message_id_disable))
1343 goto jleave;
1345 sep = '%';
1346 rl = 9;
1347 if ((h = __sendout_ident) != NULL)
1348 goto jgen;
1349 if (ok_vlook(hostname) != NULL) {
1350 h = nodename(1);
1351 sep = '@';
1352 rl = 15;
1353 goto jgen;
1355 if (hp != NULL && (h = skin(myorigin(hp))) != NULL && strchr(h, '@') != NULL)
1356 goto jgen;
1357 /* Up to MTA */
1358 goto jleave;
1360 jgen:
1361 tmp = &time_current.tc_gm;
1362 i = sizeof("Message-ID: <%04d%02d%02d%02d%02d%02d.%s%c%s>") -1 +
1363 rl + strlen(h);
1364 rv = salloc(i +1);
1365 snprintf(rv, i, "Message-ID: <%04d%02d%02d%02d%02d%02d.%s%c%s>",
1366 tmp->tm_year + 1900, tmp->tm_mon + 1, tmp->tm_mday,
1367 tmp->tm_hour, tmp->tm_min, tmp->tm_sec,
1368 getrandstring(rl), sep, h);
1369 rv[i] = '\0'; /* Because we don't test snprintf(3) return */
1370 jleave:
1371 NYD_LEAVE;
1372 return rv;
1375 static int
1376 fmt(char const *str, struct name *np, FILE *fo, enum fmt_flags ff)
1378 enum {
1379 m_INIT = 1<<0,
1380 m_COMMA = 1<<1,
1381 m_NOPF = 1<<2,
1382 m_NONAME = 1<<3,
1383 m_CSEEN = 1<<4
1384 } m = (ff & GCOMMA) ? m_COMMA : 0;
1385 ssize_t col, len;
1386 int rv = 1;
1387 NYD_ENTER;
1389 col = strlen(str);
1390 if (col) {
1391 fwrite(str, sizeof *str, col, fo);
1392 #undef _X
1393 #define _X(S) (col == sizeof(S) -1 && !asccasecmp(str, S))
1394 if (ff & GFILES) {
1396 } else if (_X("reply-to:") || _X("mail-followup-to:") ||
1397 _X("references:") || _X("disposition-notification-to:"))
1398 m |= m_NOPF | m_NONAME;
1399 else if (ok_blook(add_file_recipients)) {
1401 } else if (_X("to:") || _X("cc:") || _X("bcc:") || _X("resent-to:"))
1402 m |= m_NOPF;
1403 #undef _X
1406 for (; np != NULL; np = np->n_flink) {
1407 if (is_addr_invalid(np,
1408 EACM_NOLOG | (m & m_NONAME ? EACM_NONAME : EACM_NONE)))
1409 continue;
1410 /* File and pipe addresses only printed with set *add-file-recipients* */
1411 if ((m & m_NOPF) && is_fileorpipe_addr(np))
1412 continue;
1414 if ((m & (m_INIT | m_COMMA)) == (m_INIT | m_COMMA)) {
1415 if (putc(',', fo) == EOF)
1416 goto jleave;
1417 m |= m_CSEEN;
1418 ++col;
1421 len = strlen(np->n_fullname);
1422 if (np->n_flags & GREF)
1423 len += 2;
1424 ++col; /* The separating space */
1425 if ((m & m_INIT) && /*col > 1 &&*/ UICMP(z, col + len, >, 72)) {
1426 if (fputs("\n ", fo) == EOF)
1427 goto jleave;
1428 col = 1;
1429 m &= ~m_CSEEN;
1430 } else
1431 putc(' ', fo);
1432 m = (m & ~m_CSEEN) | m_INIT;
1435 char *hb = np->n_fullname;
1436 /* GREF needs to be placed in angle brackets, but which are missing */
1437 if (np->n_type & GREF) {
1438 hb = ac_alloc(len + 2 +1);
1439 hb[0] = '<';
1440 hb[len + 1] = '>';
1441 hb[len + 2] = '\0';
1442 memcpy(hb + 1, np->n_fullname, len);
1443 len += 2;
1445 len = xmime_write(hb, len, fo,
1446 ((ff & FMT_DOMIME) ? CONV_TOHDR_A : CONV_NONE), TD_ICONV);
1447 if (np->n_type & GREF)
1448 ac_free(hb);
1450 if (len < 0)
1451 goto jleave;
1452 col += len;
1455 if (putc('\n', fo) != EOF)
1456 rv = 0;
1457 jleave:
1458 NYD_LEAVE;
1459 return rv;
1462 static int
1463 infix_resend(FILE *fi, FILE *fo, struct message *mp, struct name *to,
1464 int add_resent)
1466 size_t cnt, c, bufsize = 0;
1467 char *buf = NULL;
1468 char const *cp;
1469 struct name *fromfield = NULL, *senderfield = NULL, *mdn;
1470 int rv = 1;
1471 NYD_ENTER;
1473 cnt = mp->m_size;
1475 /* Write the Resent-Fields */
1476 if (add_resent) {
1477 fputs("Resent-", fo);
1478 mkdate(fo, "Date");
1479 if ((cp = myaddrs(NULL)) != NULL) {
1480 if (_putname(cp, GCOMMA, SEND_MBOX, NULL, "Resent-From:", fo,
1481 &fromfield, 0))
1482 goto jleave;
1484 /* TODO RFC 5322: Resent-Sender SHOULD NOT be used if it's EQ -From: */
1485 if ((cp = ok_vlook(sender)) != NULL) {
1486 if (_putname(cp, GCOMMA, SEND_MBOX, NULL, "Resent-Sender:", fo,
1487 &senderfield, 0))
1488 goto jleave;
1490 if (fmt("Resent-To:", to, fo, FMT_COMMA))
1491 goto jleave;
1492 if (((cp = ok_vlook(stealthmua)) == NULL || !strcmp(cp, "noagent")) &&
1493 (cp = _message_id(NULL)) != NULL)
1494 fprintf(fo, "Resent-%s\n", cp);
1497 if ((mdn = UNCONST(check_from_and_sender(fromfield, senderfield))) == NULL)
1498 goto jleave;
1499 if (!_check_dispo_notif(mdn, NULL, fo))
1500 goto jleave;
1502 /* Write the original headers */
1503 while (cnt > 0) {
1504 if (fgetline(&buf, &bufsize, &cnt, &c, fi, 0) == NULL)
1505 break;
1506 /* XXX more checks: The From_ line may be seen when resending */
1507 /* During headers is_head() is actually overkill, so ^From_ is sufficient
1508 * && !is_head(buf, c, TRU1) */
1509 if (ascncasecmp("status:", buf, 7) && strncmp("From ", buf, 5) &&
1510 ascncasecmp("disposition-notification-to:", buf, 28))
1511 fwrite(buf, sizeof *buf, c, fo);
1512 if (cnt > 0 && *buf == '\n')
1513 break;
1516 /* Write the message body */
1517 while (cnt > 0) {
1518 if (fgetline(&buf, &bufsize, &cnt, &c, fi, 0) == NULL)
1519 break;
1520 if (cnt == 0 && *buf == '\n')
1521 break;
1522 fwrite(buf, sizeof *buf, c, fo);
1524 if (buf != NULL)
1525 free(buf);
1526 if (ferror(fo)) {
1527 n_perr(_("temporary mail file"), 0);
1528 goto jleave;
1530 rv = 0;
1531 jleave:
1532 NYD_LEAVE;
1533 return rv;
1536 FL int
1537 mail(struct name *to, struct name *cc, struct name *bcc, char *subject,
1538 struct attachment *attach, char *quotefile, int recipient_record)
1540 struct header head;
1541 struct str in, out;
1542 NYD_ENTER;
1544 memset(&head, 0, sizeof head);
1546 /* The given subject may be in RFC1522 format. */
1547 if (subject != NULL) {
1548 in.s = subject;
1549 in.l = strlen(subject);
1550 mime_fromhdr(&in, &out, /* TODO ??? TD_ISPR |*/ TD_ICONV);
1551 head.h_subject = out.s;
1553 head.h_to = to;
1554 head.h_cc = cc;
1555 head.h_bcc = bcc;
1556 head.h_attach = attach;
1558 mail1(&head, 0, NULL, quotefile, recipient_record, 0);
1560 if (subject != NULL)
1561 free(out.s);
1562 NYD_LEAVE;
1563 return 0;
1566 FL int
1567 c_sendmail(void *v)
1569 int rv;
1570 NYD_ENTER;
1572 rv = sendmail_internal(v, 0);
1573 NYD_LEAVE;
1574 return rv;
1577 FL int
1578 c_Sendmail(void *v)
1580 int rv;
1581 NYD_ENTER;
1583 rv = sendmail_internal(v, 1);
1584 NYD_LEAVE;
1585 return rv;
1588 FL enum okay
1589 mail1(struct header *hp, int printheaders, struct message *quote,
1590 char *quotefile, int recipient_record, int doprefix)
1592 struct sendbundle sb;
1593 struct name *to;
1594 FILE *mtf, *nmtf;
1595 char const *cp;
1596 bool_t dosign;
1597 enum okay rv = STOP;
1598 NYD_ENTER;
1600 _sendout_error = FAL0;
1601 __sendout_ident = NULL;
1603 /* Update some globals we likely need first */
1604 time_current_update(&time_current, TRU1);
1606 /* */
1607 if ((cp = ok_vlook(autocc)) != NULL && *cp != '\0')
1608 hp->h_cc = cat(hp->h_cc, checkaddrs(lextract(cp, GCC | GFULL),
1609 EACM_NORMAL, &_sendout_error));
1610 if ((cp = ok_vlook(autobcc)) != NULL && *cp != '\0')
1611 hp->h_bcc = cat(hp->h_bcc, checkaddrs(lextract(cp, GBCC | GFULL),
1612 EACM_NORMAL, &_sendout_error));
1614 if (_sendout_error < 0) {
1615 n_err(_("Some addressees in *autocc* or *autobcc* were "
1616 "classified as \"hard error\"\n"));
1617 goto j_leave;
1620 /* Collect user's mail from standard input. Get the result as mtf */
1621 mtf = collect(hp, printheaders, quote, quotefile, doprefix, &_sendout_error);
1622 if (mtf == NULL)
1623 goto j_leave;
1625 dosign = TRUM1;
1627 if (options & OPT_INTERACTIVE) {
1628 bool_t eot = TRU1;
1630 if ((eot = (ok_blook(bsdcompat) || ok_blook(askatend)))) {
1631 if (hp->h_cc == NULL && ok_blook(askcc))
1632 eot = FAL0, grab_headers(hp, GCC, 1);
1633 if (hp->h_bcc == NULL && ok_blook(askbcc))
1634 eot = FAL0, grab_headers(hp, GBCC, 1);
1637 if (hp->h_attach == NULL && ok_blook(askattach))
1638 eot = FAL0, edit_attachments(&hp->h_attach);
1640 if (ok_blook(asksign))
1641 eot = FAL0, dosign = getapproval(_("Sign this message (y/n)? "), TRU1);
1643 if (eot) {
1644 printf(_("EOT\n"));
1645 fflush(stdout);
1649 if (fsize(mtf) == 0) {
1650 if (options & OPT_E_FLAG)
1651 goto jleave;
1652 if (hp->h_subject == NULL)
1653 printf(_("No message, no subject; hope that's ok\n"));
1654 else if (ok_blook(bsdcompat) || ok_blook(bsdmsgs))
1655 printf(_("Null message body; hope that's ok\n"));
1658 if (dosign == TRUM1)
1659 dosign = ok_blook(smime_sign); /* TODO USER@HOST <-> *from* +++!!! */
1660 #ifndef HAVE_SSL
1661 if (dosign) {
1662 n_err(_("No SSL support compiled in\n"));
1663 goto jleave;
1665 #endif
1667 /* XXX Update time_current again; once collect() offers editing of more
1668 * XXX headers, including Date:, this must only happen if Date: is the
1669 * XXX same that it was before collect() (e.g., postponing etc.).
1670 * XXX But *do* update otherwise because the mail seems to be backdated
1671 * XXX if the user edited some time, which looks odd and it happened
1672 * XXX to me that i got mis-dated response mails due to that... */
1673 time_current_update(&time_current, TRU1);
1675 /* TODO hrmpf; the MIME/send layer rewrite MUST address the init crap:
1676 * TODO setup the header ONCE; note this affects edit.c, collect.c ...,
1677 * TODO but: offer a hook that rebuilds/expands/checks/fixates all
1678 * TODO header fields ONCE, call that ONCE after user editing etc. has
1679 * TODO completed (one edit cycle) */
1681 /* Take the user names from the combined to and cc lists and do all the
1682 * alias processing. The POSIX standard says:
1683 * The names shall be substituted when alias is used as a recipient
1684 * address specified by the user in an outgoing message (that is,
1685 * other recipients addressed indirectly through the reply command
1686 * shall not be substituted in this manner).
1687 * S-nail thus violates POSIX, as has been pointed out correctly by
1688 * Martin Neitzel, but logic and usability of POSIX standards is not seldom
1689 * disputable anyway. Go for user friendliness */
1691 to = namelist_vaporise_head(hp,
1692 (EACM_NORMAL |
1693 (!(expandaddr_to_eaf() & EAF_NAME) ? EACM_NONAME : EACM_NONE)),
1694 TRU1, &_sendout_error);
1695 if (to == NULL) {
1696 n_err(_("No recipients specified\n"));
1697 goto jfail_dead;
1699 if (_sendout_error < 0) {
1700 n_err(_("Some addressees were classified as \"hard error\"\n"));
1701 goto jfail_dead;
1704 /* */
1705 memset(&sb, 0, sizeof sb);
1706 sb.sb_hp = hp;
1707 sb.sb_to = to;
1708 sb.sb_input = mtf;
1709 if ((dosign || count_nonlocal(to) > 0) &&
1710 !_sendbundle_setup_creds(&sb, (dosign > 0)))
1711 /* TODO saving $DEAD and recovering etc is not yet well defined */
1712 goto jfail_dead;
1714 /* 'Bit ugly kind of control flow until we find a charset that does it */
1715 for (charset_iter_reset(hp->h_charset);; charset_iter_next()) {
1716 int err;
1718 if (!charset_iter_is_valid())
1720 else if ((nmtf = infix(hp, mtf)) != NULL)
1721 break;
1722 else if ((err = errno) == EILSEQ || err == EINVAL) {
1723 rewind(mtf);
1724 continue;
1727 n_perr(_("Failed to create encoded message"), 0);
1728 goto jfail_dead;
1730 mtf = nmtf;
1732 /* */
1733 #ifdef HAVE_SSL
1734 if (dosign) {
1735 if ((nmtf = smime_sign(mtf, sb.sb_signer.s)) == NULL)
1736 goto jfail_dead;
1737 Fclose(mtf);
1738 mtf = nmtf;
1740 #endif
1742 /* TODO truly - i still don't get what follows: (1) we deliver file
1743 * TODO and pipe addressees, (2) we mightrecord() and (3) we transfer
1744 * TODO even if (1) savedeadletter() etc. To me this doesn't make sense? */
1746 /* Deliver pipe and file addressees */
1747 to = _outof(to, mtf, &_sendout_error);
1748 if (_sendout_error)
1749 savedeadletter(mtf, FAL0);
1751 to = elide(to); /* XXX needed only to drop GDELs due to _outof()! */
1753 { ui32_t cnt = count(to);
1754 if ((!recipient_record || cnt > 0) &&
1755 !mightrecord(mtf, (recipient_record ? to : NULL)))
1756 goto jleave;
1757 if (cnt > 0) {
1758 sb.sb_hp = hp;
1759 sb.sb_to = to;
1760 sb.sb_input = mtf;
1761 if (_transfer(&sb))
1762 rv = OKAY;
1763 } else if (!_sendout_error)
1764 rv = OKAY;
1766 jleave:
1767 Fclose(mtf);
1768 j_leave:
1769 if (_sendout_error)
1770 exit_status |= EXIT_SEND_ERROR;
1771 NYD_LEAVE;
1772 return rv;
1774 jfail_dead:
1775 _sendout_error = TRU1;
1776 savedeadletter(mtf, TRU1);
1777 n_err(_("... message not sent\n"));
1778 goto jleave;
1781 FL int
1782 mkdate(FILE *fo, char const *field)
1784 struct tm tmpgm, *tmptr;
1785 int tzdiff, tzdiff_hour, tzdiff_min, rv;
1786 NYD_ENTER;
1788 memcpy(&tmpgm, &time_current.tc_gm, sizeof tmpgm);
1789 tzdiff = time_current.tc_time - mktime(&tmpgm);
1790 tzdiff_hour = (int)(tzdiff / 60);
1791 tzdiff_min = tzdiff_hour % 60;
1792 tzdiff_hour /= 60;
1793 tmptr = &time_current.tc_local;
1794 if (tmptr->tm_isdst > 0)
1795 ++tzdiff_hour;
1796 rv = fprintf(fo, "%s: %s, %02d %s %04d %02d:%02d:%02d %+05d\n",
1797 field,
1798 weekday_names[tmptr->tm_wday],
1799 tmptr->tm_mday, month_names[tmptr->tm_mon],
1800 tmptr->tm_year + 1900, tmptr->tm_hour,
1801 tmptr->tm_min, tmptr->tm_sec,
1802 tzdiff_hour * 100 + tzdiff_min);
1803 NYD_LEAVE;
1804 return rv;
1807 FL int
1808 puthead(bool_t nosend_msg, struct header *hp, FILE *fo, enum gfield w,
1809 enum sendaction action, enum conversion convert, char const *contenttype,
1810 char const *charset)
1812 #define FMT_CC_AND_BCC() \
1813 do {\
1814 if ((w & GCC) && (hp->h_cc != NULL || nosend_msg == TRUM1)) {\
1815 if (fmt("Cc:", hp->h_cc, fo, ff))\
1816 goto jleave;\
1817 ++gotcha;\
1819 if ((w & GBCC) && (hp->h_bcc != NULL || nosend_msg == TRUM1)) {\
1820 if (fmt("Bcc:", hp->h_bcc, fo, ff))\
1821 goto jleave;\
1822 ++gotcha;\
1824 } while (0)
1826 char const *addr;
1827 size_t gotcha, l;
1828 struct name *np, *fromasender = NULL;
1829 int stealthmua, rv = 1;
1830 bool_t nodisp;
1831 enum fmt_flags ff;
1832 NYD_ENTER;
1834 if ((addr = ok_vlook(stealthmua)) != NULL)
1835 stealthmua = !strcmp(addr, "noagent") ? -1 : 1;
1836 else
1837 stealthmua = 0;
1838 gotcha = 0;
1839 nodisp = (action != SEND_TODISP);
1840 ff = (w & (GCOMMA | GFILES)) | (nodisp ? FMT_DOMIME : 0);
1842 if (w & GDATE)
1843 mkdate(fo, "Date"), ++gotcha;
1844 if (w & GIDENT) {
1845 struct name *fromf = NULL, *senderf = NULL;
1847 /* If -t parsed or composed From: then take it. With -t we otherwise
1848 * want -r to be honoured in favour of *from* in order to have
1849 * a behaviour that is compatible with what users would expect from e.g.
1850 * postfix(1) */
1851 if ((fromf = hp->h_from) != NULL ||
1852 ((pstate & PS_t_FLAG) && (fromf = option_r_arg) != NULL)) {
1853 if (fmt("From:", fromf, fo, ff))
1854 goto jleave;
1855 ++gotcha;
1856 } else if ((addr = myaddrs(hp)) != NULL) {
1857 if (_putname(addr, w, action, &gotcha, "From:", fo, &fromf,
1858 GFULLEXTRA))
1859 goto jleave;
1860 ++gotcha;
1862 hp->h_from = fromf;
1864 if ((senderf = hp->h_sender) != NULL) {
1865 if (fmt("Sender:", senderf, fo, ff))
1866 goto jleave;
1867 ++gotcha;
1868 } else if ((addr = ok_vlook(sender)) != NULL) {
1869 if (_putname(addr, w, action, &gotcha, "Sender:", fo, &senderf,
1870 GFULLEXTRA))
1871 goto jleave;
1872 ++gotcha;
1874 hp->h_sender = senderf;
1876 if ((fromasender = UNCONST(check_from_and_sender(fromf,senderf))) == NULL)
1877 goto jleave;
1878 /* Note that fromasender is (NULL,) 0x1 or real sender here */
1880 if (((addr = hp->h_organization) != NULL ||
1881 (addr = ok_vlook(ORGANIZATION)) != NULL) &&
1882 (l = strlen(addr)) > 0) {
1883 fwrite("Organization: ", sizeof(char), 14, fo);
1884 if (xmime_write(addr, l, fo, (!nodisp ? CONV_NONE : CONV_TOHDR),
1885 (!nodisp ? TD_ISPR | TD_ICONV : TD_ICONV)) < 0)
1886 goto jleave;
1887 ++gotcha;
1888 putc('\n', fo);
1892 if ((w & GTO) && (hp->h_to != NULL || nosend_msg == TRUM1)) {
1893 if (fmt("To:", hp->h_to, fo, ff))
1894 goto jleave;
1895 ++gotcha;
1898 if (!ok_blook(bsdcompat) && !ok_blook(bsdorder))
1899 FMT_CC_AND_BCC();
1901 if ((w & GSUBJECT) && (hp->h_subject != NULL || nosend_msg == TRUM1)) {
1902 fwrite("Subject: ", sizeof(char), 9, fo);
1903 if (hp->h_subject != NULL) {
1904 char *sub = subject_re_trim(hp->h_subject);
1905 size_t sublen = strlen(sub);
1907 /* Trimmed something, (re-)add Re: */
1908 if (sub != hp->h_subject) {
1909 fwrite("Re: ", sizeof(char), 4, fo); /* RFC mandates eng. "Re: " */
1910 if (sublen > 0 &&
1911 xmime_write(sub, sublen, fo,
1912 (!nodisp ? CONV_NONE : CONV_TOHDR),
1913 (!nodisp ? TD_ISPR | TD_ICONV : TD_ICONV)) < 0)
1914 goto jleave;
1916 /* This may be, e.g., a Fwd: XXX yes, unfortunately we do like that */
1917 else if (*sub != '\0') {
1918 if (xmime_write(sub, sublen, fo, (!nodisp ? CONV_NONE : CONV_TOHDR),
1919 (!nodisp ? TD_ISPR | TD_ICONV : TD_ICONV)) < 0)
1920 goto jleave;
1923 ++gotcha;
1924 putc('\n', fo);
1927 if (ok_blook(bsdcompat) || ok_blook(bsdorder))
1928 FMT_CC_AND_BCC();
1930 if ((w & GMSGID) && stealthmua <= 0 && (addr = _message_id(hp)) != NULL) {
1931 fputs(addr, fo);
1932 putc('\n', fo);
1933 ++gotcha;
1936 if ((np = hp->h_ref) != NULL && (w & GREF)) {
1937 if (fmt("References:", np, fo, 0))
1938 goto jleave;
1939 if (hp->h_in_reply_to == NULL && np->n_name != NULL) {
1940 while (np->n_flink != NULL)
1941 np = np->n_flink;
1942 if (!is_addr_invalid(np, /* TODO check that on parser side! */
1943 /*EACM_STRICT | TODO '/' valid!! */ EACM_NOLOG | EACM_NONAME)) {
1944 fprintf(fo, "In-Reply-To: <%s>\n",np->n_name);/*TODO RFC5322 3.6.4*/
1945 ++gotcha;
1946 } else {
1947 n_err(_("Invalid address in mail header: \"%s\"\n"), np->n_name);
1948 goto jleave;
1952 if ((np = hp->h_in_reply_to) != NULL && (w & GREF)) {
1953 fprintf(fo, "In-Reply-To: <%s>\n", np->n_name);/*TODO RFC 5322 3.6.4*/
1954 ++gotcha;
1957 if (w & GIDENT) {
1958 /* Reply-To:. Be careful not to destroy a possible user input, duplicate
1959 * the list first.. TODO it is a terrible codebase.. */
1960 if ((np = hp->h_replyto) != NULL)
1961 np = namelist_dup(np, np->n_type);
1962 else if ((addr = ok_vlook(replyto)) != NULL)
1963 np = lextract(addr, GEXTRA | GFULL);
1964 if (np != NULL &&
1965 (np = elide(
1966 checkaddrs(usermap(np, TRU1), EACM_STRICT | EACM_NOLOG,
1967 NULL))) != NULL) {
1968 if (fmt("Reply-To:", np, fo, ff))
1969 goto jleave;
1970 ++gotcha;
1974 if ((w & GIDENT) && !nosend_msg) {
1975 /* Mail-Followup-To: TODO factor out this huge block of code */
1976 /* Place ourselfs in there if any non-subscribed list is an addressee */
1977 if ((hp->h_flags & HF_LIST_REPLY) || hp->h_mft != NULL ||
1978 ok_blook(followup_to)) {
1979 enum {_ANYLIST=1<<(HF__NEXT_SHIFT+0), _HADMFT=1<<(HF__NEXT_SHIFT+1)};
1981 ui32_t f = hp->h_flags | (hp->h_mft != NULL ? _HADMFT : 0);
1982 struct name *mft, *x;
1984 /* But for that, we have to remove all incarnations of ourselfs first.
1985 * TODO It is total crap that we have delete_alternates(), is_myname()
1986 * TODO or whatever; these work only with variables, not with data
1987 * TODO that is _currently_ in some header fields!!! v15.0: complete
1988 * TODO rewrite, object based, lazy evaluated, on-the-fly marked.
1989 * TODO then this should be a really cheap thing in here... */
1990 np = elide(delete_alternates(cat(
1991 namelist_dup(hp->h_to, GEXTRA | GFULL),
1992 namelist_dup(hp->h_cc, GEXTRA | GFULL))));
1993 addr = hp->h_list_post;
1995 for (mft = NULL; (x = np) != NULL;) {
1996 si8_t ml;
1997 np = np->n_flink;
1999 if ((ml = is_mlist(x->n_name, FAL0)) == MLIST_OTHER &&
2000 addr != NULL && !asccasecmp(addr, x->n_name))
2001 ml = MLIST_KNOWN;
2003 /* Any non-subscribed list? Add ourselves */
2004 switch (ml) {
2005 case MLIST_KNOWN:
2006 f |= HF_MFT_SENDER;
2007 /* FALLTHRU */
2008 case MLIST_SUBSCRIBED:
2009 f |= _ANYLIST;
2010 goto j_mft_add;
2011 case MLIST_OTHER:
2012 if (!(f & HF_LIST_REPLY)) {
2013 j_mft_add:
2014 if (!is_addr_invalid(x,
2015 EACM_STRICT | EACM_NOLOG | EACM_NONAME)) {
2016 x->n_flink = mft;
2017 mft = x;
2018 } /* XXX write some warning? if verbose?? */
2019 continue;
2021 /* And if this is a reply that honoured a MFT: header then we'll
2022 * also add all members of the original MFT: that are still
2023 * addressed by us, regardless of all other circumstances */
2024 else if (f & _HADMFT) {
2025 struct name *ox;
2026 for (ox = hp->h_mft; ox != NULL; ox = ox->n_flink)
2027 if (!asccasecmp(ox->n_name, x->n_name))
2028 goto j_mft_add;
2030 break;
2034 if (f & (_ANYLIST | _HADMFT) && mft != NULL) {
2035 if (((f & HF_MFT_SENDER) ||
2036 ((f & (_ANYLIST | _HADMFT)) == _HADMFT)) &&
2037 (np = fromasender) != NULL && np != (struct name*)0x1) {
2038 np = ndup(np, (np->n_type & ~GMASK) | GEXTRA | GFULL);
2039 np->n_flink = mft;
2040 mft = np;
2043 if (fmt("Mail-Followup-To:", mft, fo, ff))
2044 goto jleave;
2045 ++gotcha;
2049 if (!_check_dispo_notif(fromasender, hp, fo))
2050 goto jleave;
2053 if ((w & GUA) && stealthmua == 0)
2054 fprintf(fo, "User-Agent: %s %s\n", uagent, ok_vlook(version)), ++gotcha;
2056 /* We don't need MIME unless.. we need MIME?! */
2057 if ((w & GMIME) && ((pstate & PS_HEADER_NEEDED_MIME) ||
2058 hp->h_attach != NULL || convert != CONV_7BIT ||
2059 asccasecmp(charset, "US-ASCII"))) {
2060 ++gotcha;
2061 fputs("MIME-Version: 1.0\n", fo);
2062 if (hp->h_attach != NULL) {
2063 _sendout_boundary = mime_param_boundary_create();/*TODO carrier*/
2064 fprintf(fo, "Content-Type: multipart/mixed;\n boundary=\"%s\"\n",
2065 _sendout_boundary);
2066 } else {
2067 if (_put_ct(fo, contenttype, charset) < 0 || _put_cte(fo, convert) < 0)
2068 goto jleave;
2072 if (gotcha && (w & GNL))
2073 putc('\n', fo);
2074 rv = 0;
2075 jleave:
2076 NYD_LEAVE;
2077 return rv;
2078 #undef FMT_CC_AND_BCC
2081 FL enum okay
2082 resend_msg(struct message *mp, struct name *to, int add_resent) /* TODO check */
2084 struct sendbundle sb;
2085 FILE *ibuf, *nfo, *nfi;
2086 char *tempMail;
2087 enum okay rv = STOP;
2088 NYD_ENTER;
2090 _sendout_error = FAL0;
2091 __sendout_ident = NULL;
2093 /* Update some globals we likely need first */
2094 time_current_update(&time_current, TRU1);
2096 if ((to = checkaddrs(to,
2097 (EACM_NORMAL |
2098 (!(expandaddr_to_eaf() & EAF_NAME) ? EACM_NONAME : EACM_NONE)),
2099 &_sendout_error)) == NULL)
2100 goto jleave;
2101 /* For the _sendout_error<0 case we want to wait until we can write DEAD! */
2102 if (_sendout_error < 0)
2103 n_err(_("Some addressees were classified as \"hard error\"\n"));
2105 if ((nfo = Ftmp(&tempMail, "resend", OF_WRONLY | OF_HOLDSIGS | OF_REGISTER))
2106 == NULL) {
2107 _sendout_error = TRU1;
2108 n_perr(_("temporary mail file"), 0);
2109 goto jleave;
2111 if ((nfi = Fopen(tempMail, "r")) == NULL)
2112 n_perr(tempMail, 0);
2113 Ftmp_release(&tempMail);
2114 if (nfi == NULL)
2115 goto jerr_o;
2117 if ((ibuf = setinput(&mb, mp, NEED_BODY)) == NULL)
2118 goto jerr_io;
2120 memset(&sb, 0, sizeof sb);
2121 sb.sb_to = to;
2122 sb.sb_input = nfi;
2123 if (count_nonlocal(to) > 0 && !_sendbundle_setup_creds(&sb, FAL0))
2124 /* ..wait until we can write DEAD */
2125 _sendout_error = -1;
2127 if (infix_resend(ibuf, nfo, mp, to, add_resent) != 0) {
2128 jfail_dead:
2129 savedeadletter(nfi, TRU1);
2130 n_err(_("... message not sent\n"));
2131 jerr_io:
2132 Fclose(nfi);
2133 jerr_o:
2134 Fclose(nfo);
2135 _sendout_error = TRU1;
2136 goto jleave;
2139 if (_sendout_error < 0)
2140 goto jfail_dead;
2142 Fclose(nfo);
2143 rewind(nfi);
2145 to = _outof(to, nfi, &_sendout_error);
2147 if (_sendout_error)
2148 savedeadletter(nfi, FAL0);
2150 if (count(to = elide(to)) != 0) {
2151 if (!ok_blook(record_resent) || mightrecord(nfi, NULL)) {
2152 sb.sb_to = to;
2153 /*sb.sb_input = nfi;*/
2154 if (_transfer(&sb))
2155 rv = OKAY;
2157 } else if (!_sendout_error)
2158 rv = OKAY;
2160 Fclose(nfi);
2161 jleave:
2162 if (_sendout_error)
2163 exit_status |= EXIT_SEND_ERROR;
2164 NYD_LEAVE;
2165 return rv;
2168 #undef SEND_LINESIZE
2170 /* s-it-mode */