*pipe-TYPE/SUBTYPE*: add "@" triggers and $NAIL_FILENAME_TEMPORARY..
[s-mailx.git] / sendout.c
blobb9e97b1f21d21c085ca297ff713a8888ae1929a3
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 /* Dump many many headers to fo; gen_message says wether this will generate the
89 * final message to be send TODO puthead() must be rewritten ASAP! */
90 static int _puthead(bool_t gen_message, struct header *hp, FILE *fo,
91 enum gfield w, enum sendaction action,
92 enum conversion convert, char const *contenttype,
93 char const *charset);
95 /* Check wether Disposition-Notification-To: is desired */
96 static bool_t _check_dispo_notif(struct name *mdn, struct header *hp,
97 FILE *fo);
99 /* Send mail to a bunch of user names. The interface is through mail() */
100 static int sendmail_internal(void *v, int recipient_record);
102 /* Deal with file and pipe addressees */
103 static struct name * _outof(struct name *names, FILE *fo, bool_t *senderror);
105 /* Record outgoing mail if instructed to do so; in *record* unless to is set */
106 static bool_t mightrecord(FILE *fp, struct name *to);
108 static int __savemail(char const *name, FILE *fp);
110 /* */
111 static bool_t _transfer(struct sendbundle *sbp);
113 static bool_t __mta_start(struct sendbundle *sbp);
114 static char const ** __mta_prepare_args(struct name *to, struct header *hp);
115 static void __mta_debug(struct sendbundle *sbp, char const *mta,
116 char const **args);
118 /* Create a Message-Id: header field. Use either host name or from address */
119 static char * _message_id(struct header *hp);
121 /* Format the given header line to not exceed 72 characters */
122 static int fmt(char const *str, struct name *np, FILE *fo,
123 enum fmt_flags ff);
125 /* Rewrite a message for resending, adding the Resent-Headers */
126 static int infix_resend(FILE *fi, FILE *fo, struct message *mp,
127 struct name *to, int add_resent);
129 static enum okay
130 _putname(char const *line, enum gfield w, enum sendaction action,
131 size_t *gotcha, char const *prefix, FILE *fo, struct name **xp,
132 enum gfield addflags)
134 struct name *np;
135 enum okay rv = STOP;
136 NYD_ENTER;
138 np = lextract(line, GEXTRA | GFULL | addflags);
139 if (xp != NULL)
140 *xp = np;
141 if (np == NULL)
143 else if (fmt(prefix, np, fo, ((w & GCOMMA) |
144 ((action != SEND_TODISP) ? FMT_DOMIME : 0))))
145 rv = OKAY;
146 else if (gotcha != NULL)
147 ++(*gotcha);
148 NYD_LEAVE;
149 return rv;
152 static int
153 _put_ct(FILE *fo, char const *contenttype, char const *charset)
155 int rv, i;
156 NYD2_ENTER;
158 if ((rv = fprintf(fo, "Content-Type: %s", contenttype)) < 0)
159 goto jerr;
161 if (charset == NULL)
162 goto jend;
164 if (putc(';', fo) == EOF)
165 goto jerr;
166 ++rv;
168 if (strlen(contenttype) + sizeof("Content-Type: ;")-1 > 50) {
169 if (putc('\n', fo) == EOF)
170 goto jerr;
171 ++rv;
174 if ((i = fprintf(fo, " charset=%s", charset)) < 0)
175 goto jerr;
176 rv += i;
178 jend:
179 if (putc('\n', fo) == EOF)
180 goto jerr;
181 ++rv;
182 jleave:
183 NYD2_LEAVE;
184 return rv;
185 jerr:
186 rv = -1;
187 goto jleave;
190 SINLINE int
191 _put_cte(FILE *fo, enum conversion conv)
193 int rv;
194 NYD2_ENTER;
196 /* RFC 2045, 6.1.:
197 * This is the default value -- that is,
198 * "Content-Transfer-Encoding: 7BIT" is assumed if the
199 * Content-Transfer-Encoding header field is not present.
201 rv = (conv == CONV_7BIT) ? 0
202 : fprintf(fo, "Content-Transfer-Encoding: %s\n",
203 mime_enc_from_conversion(conv));
204 NYD2_LEAVE;
205 return rv;
208 static int
209 _put_cd(FILE *fo, char const *cd, char const *filename)
211 struct str f;
212 si8_t mpc;
213 int rv;
214 NYD2_ENTER;
216 f.s = NULL;
218 /* xxx Ugly with the trailing space in case of wrap! */
219 if ((rv = fprintf(fo, "Content-Disposition: %s; ", cd)) < 0)
220 goto jerr;
222 if (!(mpc = mime_param_create(&f, "filename", filename)))
223 goto jerr;
224 /* Always fold if result contains newlines */
225 if (mpc < 0 || f.l + rv > MIME_LINELEN) { /* FIXME MIME_LINELEN_MAX */
226 if (putc('\n', fo) == EOF || putc(' ', fo) == EOF)
227 goto jerr;
228 rv += 2;
230 if (fputs(f.s, fo) == EOF || putc('\n', fo) == EOF)
231 goto jerr;
232 rv += (int)++f.l;
234 jleave:
235 NYD2_LEAVE;
236 return rv;
237 jerr:
238 rv = -1;
239 goto jleave;
243 static int
244 _attach_file(struct attachment *ap, FILE *fo)
246 /* TODO of course, the MIME classification needs to performed once
247 * TODO only, not for each and every charset anew ... ;-// */
248 char *charset_iter_orig[2];
249 long offs;
250 int err = 0;
251 NYD_ENTER;
253 /* Is this already in target charset? Simply copy over */
254 if (ap->a_conv == AC_TMPFILE) {
255 err = __attach_file(ap, fo);
256 Fclose(ap->a_tmpf);
257 DBG( ap->a_tmpf = NULL; )
258 goto jleave;
261 /* If we don't apply charset conversion at all (fixed input=ouput charset)
262 * we also simply copy over, since it's the users desire */
263 if (ap->a_conv == AC_FIX_INCS) {
264 ap->a_charset = ap->a_input_charset;
265 err = __attach_file(ap, fo);
266 goto jleave;
269 /* Otherwise we need to iterate over all possible output charsets */
270 if ((offs = ftell(fo)) == -1) {
271 err = EIO;
272 goto jleave;
274 charset_iter_recurse(charset_iter_orig);
275 for (charset_iter_reset(NULL);; charset_iter_next()) {
276 if (!charset_iter_is_valid()) {
277 err = EILSEQ;
278 break;
280 err = __attach_file(ap, fo);
281 if (err == 0 || (err != EILSEQ && err != EINVAL))
282 break;
283 clearerr(fo);
284 if (fseek(fo, offs, SEEK_SET) == -1) {
285 err = EIO;
286 break;
288 if (ap->a_conv != AC_DEFAULT) {
289 err = EILSEQ;
290 break;
292 ap->a_charset = NULL;
294 charset_iter_restore(charset_iter_orig);
295 jleave:
296 NYD_LEAVE;
297 return err;
300 static int
301 __attach_file(struct attachment *ap, FILE *fo) /* XXX linelength */
303 int err = 0, do_iconv;
304 FILE *fi;
305 char const *charset;
306 enum conversion convert;
307 char *buf;
308 size_t bufsize, lncnt, inlen;
309 NYD_ENTER;
311 /* Either charset-converted temporary file, or plain path */
312 if (ap->a_conv == AC_TMPFILE) {
313 fi = ap->a_tmpf;
314 assert(ftell(fi) == 0);
315 } else if ((fi = Fopen(ap->a_name, "r")) == NULL) {
316 err = errno;
317 n_err(_("\"%s\": %s\n"), ap->a_name, strerror(errno));
318 goto jleave;
321 /* MIME part header for attachment */
322 { char const *bn = ap->a_name, *ct;
324 if ((ct = strrchr(bn, '/')) != NULL)
325 bn = ++ct;
326 ct = ap->a_content_type;
327 charset = ap->a_charset;
328 convert = mime_type_file_classify(fi, (char const**)&ct,
329 &charset, &do_iconv);
330 if (charset == NULL || ap->a_conv == AC_FIX_INCS ||
331 ap->a_conv == AC_TMPFILE)
332 do_iconv = 0;
334 if (fprintf(fo, "\n--%s\n", _sendout_boundary) < 0 ||
335 _put_ct(fo, ct, charset) < 0 || _put_cte(fo, convert) < 0 ||
336 _put_cd(fo, ap->a_content_disposition, bn) < 0)
337 goto jerr_header;
339 if ((bn = ap->a_content_id) != NULL &&
340 fprintf(fo, "Content-ID: %s\n", bn) == -1)
341 goto jerr_header;
343 if ((bn = ap->a_content_description) != NULL &&
344 fprintf(fo, "Content-Description: %s\n", bn) == -1) /* TODO MIME! */
345 goto jerr_header;
347 if (putc('\n', fo) == EOF) {
348 jerr_header:
349 err = errno;
350 goto jerr_fclose;
354 #ifdef HAVE_ICONV
355 if (iconvd != (iconv_t)-1)
356 n_iconv_close(iconvd);
357 if (do_iconv) {
358 char const *tcs = charset_get_lc();
359 if (asccasecmp(charset, tcs) &&
360 (iconvd = n_iconv_open(charset, tcs)) == (iconv_t)-1 &&
361 (err = errno) != 0) {
362 if (err == EINVAL)
363 n_err(_("Cannot convert from %s to %s\n"), tcs, charset);
364 else
365 n_err(_("iconv_open: %s\n"), strerror(err));
366 goto jerr_fclose;
369 #endif
371 bufsize = SEND_LINESIZE;
372 buf = smalloc(bufsize);
373 if (convert == CONV_TOQP
374 #ifdef HAVE_ICONV
375 || iconvd != (iconv_t)-1
376 #endif
378 lncnt = fsize(fi);
379 for (;;) {
380 if (convert == CONV_TOQP
381 #ifdef HAVE_ICONV
382 || iconvd != (iconv_t)-1
383 #endif
385 if (fgetline(&buf, &bufsize, &lncnt, &inlen, fi, 0) == NULL)
386 break;
387 } else if ((inlen = fread(buf, sizeof *buf, bufsize, fi)) == 0)
388 break;
389 if (xmime_write(buf, inlen, fo, convert, TD_ICONV) < 0) {
390 err = errno;
391 goto jerr;
394 if (ferror(fi))
395 err = EDOM;
396 jerr:
397 free(buf);
398 jerr_fclose:
399 if (ap->a_conv != AC_TMPFILE)
400 Fclose(fi);
401 jleave:
402 NYD_LEAVE;
403 return err;
406 static bool_t
407 _sendbundle_setup_creds(struct sendbundle *sbp, bool_t signing_caps)
409 bool_t v15, rv = FAL0;
410 char *shost, *from;
411 #ifdef HAVE_SMTP
412 char *smtp;
413 #endif
414 NYD_ENTER;
416 v15 = ok_blook(v15_compat);
417 shost = (v15 ? ok_vlook(smtp_hostname) : NULL);
418 from = ((signing_caps || !v15 || shost == NULL)
419 ? skin(myorigin(sbp->sb_hp)) : NULL);
421 if (signing_caps) {
422 if (from == NULL) {
423 #ifdef HAVE_SSL
424 n_err(_("No *from* address for signing specified\n"));
425 goto jleave;
426 #endif
427 } else
428 sbp->sb_signer.l = strlen(sbp->sb_signer.s = from);
431 #ifdef HAVE_SMTP
432 if ((smtp = ok_vlook(smtp)) == NULL) {
433 rv = TRU1;
434 goto jleave;
437 if (!url_parse(&sbp->sb_url, CPROTO_SMTP, smtp))
438 goto jleave;
440 if (v15) {
441 if (shost == NULL) {
442 if (from == NULL)
443 goto jenofrom;
444 sbp->sb_url.url_u_h.l = strlen(sbp->sb_url.url_u_h.s = from);
445 } else
446 __sendout_ident = sbp->sb_url.url_u_h.s;
447 if (!ccred_lookup(&sbp->sb_ccred, &sbp->sb_url))
448 goto jleave;
449 } else {
450 if (sbp->sb_url.url_had_user || sbp->sb_url.url_pass.s != NULL) {
451 n_err(_("New-style URL used without *v15-compat* being set\n"));
452 goto jleave;
454 /* TODO part of the entire myorigin() disaster, get rid of this! */
455 if (from == NULL) {
456 jenofrom:
457 n_err(_("Your configuration requires a *from* address, "
458 "but none was given\n"));
459 goto jleave;
461 if (!ccred_lookup_old(&sbp->sb_ccred, CPROTO_SMTP, from))
462 goto jleave;
463 sbp->sb_url.url_u_h.l = strlen(sbp->sb_url.url_u_h.s = from);
466 rv = TRU1;
467 #endif /* HAVE_SMTP */
468 #if defined HAVE_SSL || defined HAVE_SMTP
469 jleave:
470 #endif
471 NYD_LEAVE;
472 return rv;
475 static int
476 attach_message(struct attachment *ap, FILE *fo)
478 struct message *mp;
479 char const *ccp;
480 int rv;
481 NYD_ENTER;
483 fprintf(fo, "\n--%s\nContent-Type: message/rfc822\n"
484 "Content-Disposition: inline\n", _sendout_boundary);
485 if ((ccp = ap->a_content_description) != NULL)
486 fprintf(fo, "Content-Description: %s\n", ccp);/* TODO MIME! */
487 putc('\n', fo);
489 mp = message + ap->a_msgno - 1;
490 touch(mp);
491 rv = (sendmp(mp, fo, 0, NULL, SEND_RFC822, NULL) < 0) ? -1 : 0;
492 NYD_LEAVE;
493 return rv;
496 static int
497 make_multipart(struct header *hp, int convert, FILE *fi, FILE *fo,
498 char const *contenttype, char const *charset)
500 struct attachment *att;
501 int rv = -1;
502 NYD_ENTER;
504 fputs("This is a multi-part message in MIME format.\n", fo);
505 if (fsize(fi) != 0) {
506 char *buf;
507 size_t sz, bufsize, cnt;
509 if (fprintf(fo, "\n--%s\n", _sendout_boundary) < 0 ||
510 _put_ct(fo, contenttype, charset) < 0 ||
511 _put_cte(fo, convert) < 0 ||
512 fprintf(fo, "Content-Disposition: inline\n\n") < 0)
513 goto jleave;
515 buf = smalloc(bufsize = SEND_LINESIZE);
516 if (convert == CONV_TOQP
517 #ifdef HAVE_ICONV
518 || iconvd != (iconv_t)-1
519 #endif
521 fflush(fi);
522 cnt = fsize(fi);
524 for (;;) {
525 if (convert == CONV_TOQP
526 #ifdef HAVE_ICONV
527 || iconvd != (iconv_t)-1
528 #endif
530 if (fgetline(&buf, &bufsize, &cnt, &sz, fi, 0) == NULL)
531 break;
532 } else if ((sz = fread(buf, sizeof *buf, bufsize, fi)) == 0)
533 break;
535 if (xmime_write(buf, sz, fo, convert, TD_ICONV) < 0) {
536 free(buf);
537 goto jleave;
540 free(buf);
542 if (ferror(fi))
543 goto jleave;
546 for (att = hp->h_attach; att != NULL; att = att->a_flink) {
547 if (att->a_msgno) {
548 if (attach_message(att, fo) != 0)
549 goto jleave;
550 } else if (_attach_file(att, fo) != 0)
551 goto jleave;
554 /* the final boundary with two attached dashes */
555 fprintf(fo, "\n--%s--\n", _sendout_boundary);
556 rv = 0;
557 jleave:
558 NYD_LEAVE;
559 return rv;
562 static FILE *
563 infix(struct header *hp, FILE *fi) /* TODO check */
565 FILE *nfo, *nfi = NULL;
566 char *tempMail;
567 char const *contenttype, *charset = NULL;
568 enum conversion convert;
569 int do_iconv = 0, err;
570 #ifdef HAVE_ICONV
571 char const *tcs, *convhdr = NULL;
572 #endif
573 NYD_ENTER;
575 if ((nfo = Ftmp(&tempMail, "infix", OF_WRONLY | OF_HOLDSIGS | OF_REGISTER,
576 0600)) == NULL) {
577 n_perr(_("temporary mail file"), 0);
578 goto jleave;
580 if ((nfi = Fopen(tempMail, "r")) == NULL) {
581 n_perr(tempMail, 0);
582 Fclose(nfo);
584 Ftmp_release(&tempMail);
585 if (nfi == NULL)
586 goto jleave;
588 pstate &= ~PS_HEADER_NEEDED_MIME; /* TODO a hack should be carrier tracked */
590 contenttype = "text/plain"; /* XXX mail body - always text/plain, want XX? */
591 convert = mime_type_file_classify(fi, &contenttype, &charset, &do_iconv);
593 #ifdef HAVE_ICONV
594 tcs = charset_get_lc();
595 if ((convhdr = need_hdrconv(hp, GTO | GSUBJECT | GCC | GBCC | GIDENT))) {
596 if (iconvd != (iconv_t)-1) /* XXX */
597 n_iconv_close(iconvd);
598 if (asccasecmp(convhdr, tcs) != 0 &&
599 (iconvd = n_iconv_open(convhdr, tcs)) == (iconv_t)-1 &&
600 (err = errno) != 0)
601 goto jiconv_err;
603 #endif
604 if (_puthead(TRU1, hp, nfo,
605 (GTO | GSUBJECT | GCC | GBCC | GNL | GCOMMA | GUA | GMIME | GMSGID |
606 GIDENT | GREF | GDATE), SEND_MBOX, convert, contenttype, charset))
607 goto jerr;
608 #ifdef HAVE_ICONV
609 if (iconvd != (iconv_t)-1)
610 n_iconv_close(iconvd);
611 #endif
613 #ifdef HAVE_ICONV
614 if (do_iconv && charset != NULL) { /*TODO charset->mime_type_file_classify*/
615 if (asccasecmp(charset, tcs) != 0 &&
616 (iconvd = n_iconv_open(charset, tcs)) == (iconv_t)-1 &&
617 (err = errno) != 0) {
618 jiconv_err:
619 if (err == EINVAL)
620 n_err(_("Cannot convert from %s to %s\n"), tcs, charset);
621 else
622 n_perr("iconv_open", 0);
623 goto jerr;
626 #endif
628 if (hp->h_attach != NULL) {
629 if (make_multipart(hp, convert, fi, nfo, contenttype, charset) != 0)
630 goto jerr;
631 } else {
632 size_t sz, bufsize, cnt;
633 char *buf;
635 if (convert == CONV_TOQP
636 #ifdef HAVE_ICONV
637 || iconvd != (iconv_t)-1
638 #endif
640 fflush(fi);
641 cnt = fsize(fi);
643 buf = smalloc(bufsize = SEND_LINESIZE);
644 for (err = 0;;) {
645 if (convert == CONV_TOQP
646 #ifdef HAVE_ICONV
647 || iconvd != (iconv_t)-1
648 #endif
650 if (fgetline(&buf, &bufsize, &cnt, &sz, fi, 0) == NULL)
651 break;
652 } else if ((sz = fread(buf, sizeof *buf, bufsize, fi)) == 0)
653 break;
654 if (xmime_write(buf, sz, nfo, convert, TD_ICONV) < 0) {
655 err = 1;
656 break;
659 free(buf);
661 if (err || ferror(fi)) {
662 jerr:
663 Fclose(nfo);
664 Fclose(nfi);
665 #ifdef HAVE_ICONV
666 if (iconvd != (iconv_t)-1)
667 n_iconv_close(iconvd);
668 #endif
669 nfi = NULL;
670 goto jleave;
674 #ifdef HAVE_ICONV
675 if (iconvd != (iconv_t)-1)
676 n_iconv_close(iconvd);
677 #endif
679 fflush(nfo);
680 if ((err = ferror(nfo)))
681 n_perr(_("temporary mail file"), 0);
682 Fclose(nfo);
683 if (!err) {
684 fflush_rewind(nfi);
685 Fclose(fi);
686 } else {
687 Fclose(nfi);
688 nfi = NULL;
690 jleave:
691 NYD_LEAVE;
692 return nfi;
695 static int
696 _puthead(bool_t gen_message, struct header *hp, FILE *fo, enum gfield w,
697 enum sendaction action, enum conversion convert, char const *contenttype,
698 char const *charset)
700 #define FMT_CC_AND_BCC() \
701 do {\
702 if (hp->h_cc != NULL && (w & GCC)) {\
703 if (fmt("Cc:", hp->h_cc, fo, ff))\
704 goto jleave;\
705 ++gotcha;\
707 if (hp->h_bcc != NULL && (w & GBCC)) {\
708 if (fmt("Bcc:", hp->h_bcc, fo, ff))\
709 goto jleave;\
710 ++gotcha;\
712 } while (0)
714 char const *addr;
715 size_t gotcha, l;
716 struct name *np, *fromasender = NULL;
717 int stealthmua, rv = 1;
718 bool_t nodisp;
719 enum fmt_flags ff;
720 NYD_ENTER;
722 if ((addr = ok_vlook(stealthmua)) != NULL)
723 stealthmua = !strcmp(addr, "noagent") ? -1 : 1;
724 else
725 stealthmua = 0;
726 gotcha = 0;
727 nodisp = (action != SEND_TODISP);
728 ff = (w & (GCOMMA | GFILES)) | (nodisp ? FMT_DOMIME : 0);
730 if (w & GDATE)
731 mkdate(fo, "Date"), ++gotcha;
732 if (w & GIDENT) {
733 struct name *fromf = NULL, *senderf = NULL;
735 /* If -t parsed or composed From: then take it. With -t we otherwise
736 * want -r to be honoured in favour of *from* in order to have
737 * a behaviour that is compatible with what users would expect from e.g.
738 * postfix(1) */
739 if ((fromf = hp->h_from) != NULL ||
740 ((pstate & PS_t_FLAG) && (fromf = option_r_arg) != NULL)) {
741 if (fmt("From:", fromf, fo, ff))
742 goto jleave;
743 ++gotcha;
744 } else if ((addr = myaddrs(hp)) != NULL) {
745 if (_putname(addr, w, action, &gotcha, "From:", fo, &fromf,
746 GFULLEXTRA))
747 goto jleave;
748 ++gotcha;
750 hp->h_from = fromf;
752 if ((senderf = hp->h_sender) != NULL) {
753 if (fmt("Sender:", senderf, fo, ff))
754 goto jleave;
755 ++gotcha;
756 } else if ((addr = ok_vlook(sender)) != NULL) {
757 if (_putname(addr, w, action, &gotcha, "Sender:", fo, &senderf,
758 GFULLEXTRA))
759 goto jleave;
760 ++gotcha;
762 hp->h_sender = senderf;
764 if ((fromasender = UNCONST(check_from_and_sender(fromf,senderf))) == NULL)
765 goto jleave;
766 /* Note that fromasender is (NULL,) 0x1 or real sender here */
768 if (((addr = hp->h_organization) != NULL ||
769 (addr = ok_vlook(ORGANIZATION)) != NULL) &&
770 (l = strlen(addr)) > 0) {
771 fwrite("Organization: ", sizeof(char), 14, fo);
772 if (xmime_write(addr, l, fo, (!nodisp ? CONV_NONE : CONV_TOHDR),
773 (!nodisp ? TD_ISPR | TD_ICONV : TD_ICONV)) < 0)
774 goto jleave;
775 ++gotcha;
776 putc('\n', fo);
780 if (hp->h_to != NULL && w & GTO) {
781 if (fmt("To:", hp->h_to, fo, ff))
782 goto jleave;
783 ++gotcha;
786 if (!ok_blook(bsdcompat) && !ok_blook(bsdorder))
787 FMT_CC_AND_BCC();
789 if (hp->h_subject != NULL && (w & GSUBJECT)) {
790 char *sub = subject_re_trim(hp->h_subject);
791 size_t sublen = strlen(sub);
793 fwrite("Subject: ", sizeof(char), 9, fo);
794 /* Trimmed something, (re-)add Re: */
795 if (sub != hp->h_subject) {
796 fwrite("Re: ", sizeof(char), 4, fo); /* RFC mandates english "Re: " */
797 if (sublen > 0 &&
798 xmime_write(sub, sublen, fo, (!nodisp ? CONV_NONE : CONV_TOHDR),
799 (!nodisp ? TD_ISPR | TD_ICONV : TD_ICONV)) < 0)
800 goto jleave;
802 /* This may be, e.g., a Fwd: XXX yes, unfortunately we do like that */
803 else if (*sub != '\0') {
804 if (xmime_write(sub, sublen, fo, (!nodisp ? CONV_NONE : CONV_TOHDR),
805 (!nodisp ? TD_ISPR | TD_ICONV : TD_ICONV)) < 0)
806 goto jleave;
808 ++gotcha;
809 putc('\n', fo);
812 if (ok_blook(bsdcompat) || ok_blook(bsdorder))
813 FMT_CC_AND_BCC();
815 if ((w & GMSGID) && stealthmua <= 0 && (addr = _message_id(hp)) != NULL) {
816 fputs(addr, fo);
817 putc('\n', fo);
818 ++gotcha;
821 if ((np = hp->h_ref) != NULL && (w & GREF)) {
822 if (fmt("References:", np, fo, 0))
823 goto jleave;
824 if (hp->h_in_reply_to == NULL && np->n_name != NULL) {
825 while (np->n_flink != NULL)
826 np = np->n_flink;
827 if (!is_addr_invalid(np, /* TODO check that on parser side! */
828 /*EACM_STRICT | TODO '/' valid!! */ EACM_NOLOG | EACM_NONAME)) {
829 fprintf(fo, "In-Reply-To: <%s>\n", np->n_name);/*TODO RFC 5322 3.6.4*/
830 ++gotcha;
831 } else {
832 n_err(_("Invalid address in mail header: \"%s\"\n"), np->n_name);
833 goto jleave;
837 if ((np = hp->h_in_reply_to) != NULL && (w & GREF)) {
838 fprintf(fo, "In-Reply-To: <%s>\n", np->n_name);/*TODO RFC 5322 3.6.4*/
839 ++gotcha;
842 if (w & GIDENT) {
843 /* Reply-To:. Be careful not to destroy a possible user input, duplicate
844 * the list first.. TODO it is a terrible codebase.. */
845 if ((np = hp->h_replyto) != NULL)
846 np = namelist_dup(np, np->n_type);
847 else if ((addr = ok_vlook(replyto)) != NULL)
848 np = lextract(addr, GEXTRA | GFULL);
849 if (np != NULL &&
850 (np = elide(
851 checkaddrs(usermap(np, TRU1), EACM_STRICT | EACM_NOLOG,
852 NULL))) != NULL) {
853 if (fmt("Reply-To:", np, fo, ff))
854 goto jleave;
855 ++gotcha;
859 if ((w & GIDENT) && gen_message) {
860 /* Mail-Followup-To: TODO factor out this huge block of code */
861 /* Place ourselfs in there if any non-subscribed list is an addressee */
862 if ((hp->h_flags & HF_LIST_REPLY) || hp->h_mft != NULL ||
863 ok_blook(followup_to)) {
864 enum {_ANYLIST=1<<(HF__NEXT_SHIFT+0), _HADMFT=1<<(HF__NEXT_SHIFT+1)};
866 ui32_t f = hp->h_flags | (hp->h_mft != NULL ? _HADMFT : 0);
867 struct name *mft, *x;
869 /* But for that, we have to remove all incarnations of ourselfs first.
870 * TODO It is total crap that we have delete_alternates(), is_myname()
871 * TODO or whatever; these work only with variables, not with data
872 * TODO that is _currently_ in some header fields!!! v15.0: complete
873 * TODO rewrite, object based, lazy evaluated, on-the-fly marked.
874 * TODO then this should be a really cheap thing in here... */
875 np = elide(delete_alternates(cat(
876 namelist_dup(hp->h_to, GEXTRA | GFULL),
877 namelist_dup(hp->h_cc, GEXTRA | GFULL))));
878 addr = hp->h_list_post;
880 for (mft = NULL; (x = np) != NULL;) {
881 si8_t ml;
882 np = np->n_flink;
884 if ((ml = is_mlist(x->n_name, FAL0)) == MLIST_OTHER &&
885 addr != NULL && !asccasecmp(addr, x->n_name))
886 ml = MLIST_KNOWN;
888 /* Any non-subscribed list? Add ourselves */
889 switch (ml) {
890 case MLIST_KNOWN:
891 f |= HF_MFT_SENDER;
892 /* FALLTHRU */
893 case MLIST_SUBSCRIBED:
894 f |= _ANYLIST;
895 goto j_mft_add;
896 case MLIST_OTHER:
897 if (!(f & HF_LIST_REPLY)) {
898 j_mft_add:
899 if (!is_addr_invalid(x,
900 EACM_STRICT | EACM_NOLOG | EACM_NONAME)) {
901 x->n_flink = mft;
902 mft = x;
903 } /* XXX write some warning? if verbose?? */
904 continue;
906 /* And if this is a reply that honoured a MFT: header then we'll
907 * also add all members of the original MFT: that are still
908 * addressed by us, regardless of all other circumstances */
909 else if (f & _HADMFT) {
910 struct name *ox;
911 for (ox = hp->h_mft; ox != NULL; ox = ox->n_flink)
912 if (!asccasecmp(ox->n_name, x->n_name))
913 goto j_mft_add;
915 break;
919 if (f & (_ANYLIST | _HADMFT) && mft != NULL) {
920 if (((f & HF_MFT_SENDER) ||
921 ((f & (_ANYLIST | _HADMFT)) == _HADMFT)) &&
922 (np = fromasender) != NULL && np != (struct name*)0x1) {
923 np = ndup(np, (np->n_type & ~GMASK) | GEXTRA | GFULL);
924 np->n_flink = mft;
925 mft = np;
928 if (fmt("Mail-Followup-To:", mft, fo, ff))
929 goto jleave;
930 ++gotcha;
934 if (!_check_dispo_notif(fromasender, hp, fo))
935 goto jleave;
938 if ((w & GUA) && stealthmua == 0)
939 fprintf(fo, "User-Agent: %s %s\n", uagent, ok_vlook(version)), ++gotcha;
941 /* We don't need MIME unless.. we need MIME?! */
942 if ((w & GMIME) && ((pstate & PS_HEADER_NEEDED_MIME) ||
943 hp->h_attach != NULL || convert != CONV_7BIT ||
944 asccasecmp(charset, "US-ASCII"))) {
945 ++gotcha;
946 fputs("MIME-Version: 1.0\n", fo);
947 if (hp->h_attach != NULL) {
948 _sendout_boundary = mime_param_boundary_create();/*TODO carrier*/
949 fprintf(fo, "Content-Type: multipart/mixed;\n boundary=\"%s\"\n",
950 _sendout_boundary);
951 } else {
952 if (_put_ct(fo, contenttype, charset) < 0 || _put_cte(fo, convert) < 0)
953 goto jleave;
957 if (gotcha && (w & GNL))
958 putc('\n', fo);
959 rv = 0;
960 jleave:
961 NYD_LEAVE;
962 return rv;
963 #undef FMT_CC_AND_BCC
966 static bool_t
967 _check_dispo_notif(struct name *mdn, struct header *hp, FILE *fo)
969 char const *from;
970 bool_t rv = TRU1;
971 NYD_ENTER;
973 /* TODO smtp_disposition_notification (RFC 3798): relation to return-path
974 * TODO not yet checked */
975 if (!ok_blook(disposition_notification_send))
976 goto jleave;
978 if (mdn != NULL && mdn != (struct name*)0x1)
979 from = mdn->n_name;
980 else if ((from = myorigin(hp)) == NULL) {
981 if (options & OPT_D_V)
982 n_err(_("*disposition-notification-send*: no *from* set\n"));
983 goto jleave;
986 if (fmt("Disposition-Notification-To:", nalloc(UNCONST(from), 0), fo, 0))
987 rv = FAL0;
988 jleave:
989 NYD_LEAVE;
990 return rv;
993 static int
994 sendmail_internal(void *v, int recipient_record)
996 struct header head;
997 char *str = v;
998 int rv;
999 NYD_ENTER;
1001 memset(&head, 0, sizeof head);
1002 head.h_to = lextract(str, GTO | GFULL);
1003 rv = mail1(&head, 0, NULL, NULL, recipient_record, 0);
1004 NYD_LEAVE;
1005 return (rv == 0);
1008 static struct name *
1009 _outof(struct name *names, FILE *fo, bool_t *senderror)
1011 ui32_t pipecnt, xcnt, i;
1012 int *fda;
1013 char const *sh;
1014 struct name *np;
1015 FILE *fin = NULL, *fout;
1016 NYD_ENTER;
1018 /* Look through all recipients and do a quick return if no file or pipe
1019 * addressee is found */
1020 fda = NULL; /* Silence cc */
1021 for (pipecnt = xcnt = 0, np = names; np != NULL; np = np->n_flink) {
1022 if (np->n_type & GDEL)
1023 continue;
1024 switch (np->n_flags & NAME_ADDRSPEC_ISFILEORPIPE) {
1025 case NAME_ADDRSPEC_ISFILE:
1026 ++xcnt;
1027 break;
1028 case NAME_ADDRSPEC_ISPIPE:
1029 ++pipecnt;
1030 break;
1033 if (pipecnt == 0 && xcnt == 0)
1034 goto jleave;
1036 /* Otherwise create an array of file descriptors for each found pipe
1037 * addressee to get around the dup(2)-shared-file-offset problem, i.e.,
1038 * each pipe subprocess needs its very own file descriptor, and we need
1039 * to deal with that.
1040 * To make our life a bit easier let's just use the auto-reclaimed
1041 * string storage */
1042 if (pipecnt == 0 || (options & OPT_DEBUG)) {
1043 pipecnt = 0;
1044 fda = NULL;
1045 sh = NULL;
1046 } else {
1047 fda = salloc(sizeof(int) * pipecnt);
1048 for (i = 0; i < pipecnt; ++i)
1049 fda[i] = -1;
1050 if ((sh = ok_vlook(SHELL)) == NULL)
1051 sh = XSHELL;
1054 for (np = names; np != NULL; np = np->n_flink) {
1055 if (!(np->n_flags & NAME_ADDRSPEC_ISFILEORPIPE))
1056 continue;
1058 /* In days of old we removed the entry from the the list; now for sake of
1059 * header expansion we leave it in and mark it as deleted */
1060 np->n_type |= GDEL;
1062 if (options & OPT_DEBUG) {
1063 n_err(_(">>> Would write message via \"%s\"\n"), np->n_name);
1064 continue;
1066 if (options & OPT_VERBVERB)
1067 n_err(_(">>> Writing message via \"%s\"\n"), np->n_name);
1069 /* See if we have copied the complete message out yet. If not, do so */
1070 if (image < 0) {
1071 int c;
1072 char *tempEdit;
1074 if ((fout = Ftmp(&tempEdit, "outof",
1075 OF_WRONLY | OF_HOLDSIGS | OF_REGISTER, 0600)) == NULL) {
1076 n_perr(_("Creation of temporary image"), 0);
1077 *senderror = TRU1;
1078 goto jcant;
1080 if ((image = open(tempEdit, O_RDWR | _O_CLOEXEC)) >= 0) {
1081 _CLOEXEC_SET(image);
1082 for (i = 0; i < pipecnt; ++i) {
1083 int fd = open(tempEdit, O_RDONLY | _O_CLOEXEC);
1084 if (fd < 0) {
1085 close(image);
1086 image = -1;
1087 pipecnt = i;
1088 break;
1090 fda[i] = fd;
1091 _CLOEXEC_SET(fd);
1094 Ftmp_release(&tempEdit);
1096 if (image < 0) {
1097 n_perr(_("Creating descriptor duplicate of temporary image"), 0);
1098 *senderror = TRU1;
1099 Fclose(fout);
1100 goto jcant;
1103 fprintf(fout, "From %s %s", myname, time_current.tc_ctime);
1104 c = EOF;
1105 while (i = c, (c = getc(fo)) != EOF)
1106 putc(c, fout);
1107 rewind(fo);
1108 if ((int)i != '\n')
1109 putc('\n', fout);
1110 putc('\n', fout);
1111 fflush(fout);
1112 if (ferror(fout)) {
1113 n_perr(_("Finalizing write of temporary image"), 0);
1114 Fclose(fout);
1115 goto jcantfout;
1117 Fclose(fout);
1119 /* If we have to serve file addressees, open reader */
1120 if (xcnt != 0 && (fin = Fdopen(image, "r", FAL0)) == NULL) {
1121 n_perr(_("Failed to open a temporary image duplicate"), 0);
1122 jcantfout:
1123 *senderror = TRU1;
1124 close(image);
1125 image = -1;
1126 goto jcant;
1129 /* From now on use xcnt as a counter for pipecnt */
1130 xcnt = 0;
1133 /* Now either copy "image" to the desired file or give it as the standard
1134 * input to the desired program as appropriate */
1135 if (np->n_flags & NAME_ADDRSPEC_ISPIPE) {
1136 int pid;
1137 sigset_t nset;
1139 sigemptyset(&nset);
1140 sigaddset(&nset, SIGHUP);
1141 sigaddset(&nset, SIGINT);
1142 sigaddset(&nset, SIGQUIT);
1143 pid = start_command(sh, &nset, fda[xcnt++], -1, "-c",
1144 np->n_name + 1, NULL, NULL);
1145 if (pid < 0) {
1146 n_err(_("Piping message to \"%s\" failed\n"), np->n_name);
1147 *senderror = TRU1;
1148 goto jcant;
1150 free_child(pid);
1151 } else {
1152 int c;
1153 char *fname = file_expand(np->n_name);
1155 if (fname == NULL) {
1156 *senderror = TRU1;
1157 goto jcant;
1160 if ((fout = Zopen(fname, "a")) == NULL) {
1161 n_err(_("Writing message to \"%s\" failed: %s\n"),
1162 fname, strerror(errno));
1163 *senderror = TRU1;
1164 goto jcant;
1166 rewind(fin);
1167 while ((c = getc(fin)) != EOF)
1168 putc(c, fout);
1169 if (ferror(fout)) {
1170 n_err(_("Writing message to \"%s\" failed: %s\n"),
1171 fname, _("write error"));
1172 *senderror = TRU1;
1174 Fclose(fout);
1177 jcant:
1178 if (image < 0)
1179 goto jdelall;
1182 jleave:
1183 if (fin != NULL)
1184 Fclose(fin);
1185 for (i = 0; i < pipecnt; ++i)
1186 close(fda[i]);
1187 if (image >= 0) {
1188 close(image);
1189 image = -1;
1191 NYD_LEAVE;
1192 return names;
1194 jdelall:
1195 while (np != NULL) {
1196 if (np->n_flags & NAME_ADDRSPEC_ISFILEORPIPE)
1197 np->n_type |= GDEL;
1198 np = np->n_flink;
1200 goto jleave;
1203 static bool_t
1204 mightrecord(FILE *fp, struct name *to)
1206 char *cp, *cq;
1207 char const *ep;
1208 bool_t rv = TRU1;
1209 NYD_ENTER;
1211 if (options & OPT_DEBUG)
1212 cp = NULL;
1213 else if (to != NULL) {
1214 cp = savestr(skinned_name(to));
1215 for (cq = cp; *cq != '\0' && *cq != '@'; ++cq)
1217 *cq = '\0';
1218 } else
1219 cp = ok_vlook(record);
1221 if (cp != NULL) {
1222 if ((ep = expand(cp)) == NULL) {
1223 ep = "NULL";
1224 goto jbail;
1227 if (*ep != '/' && *ep != '+' && ok_blook(outfolder) &&
1228 which_protocol(ep) == PROTO_FILE) {
1229 size_t i = strlen(cp);
1230 cq = salloc(i + 1 +1);
1231 cq[0] = '+';
1232 memcpy(cq + 1, cp, i +1);
1233 cp = cq;
1234 if ((ep = file_expand(cp)) == NULL) {
1235 ep = "NULL";
1236 goto jbail;
1240 if (__savemail(ep, fp) != 0) {
1241 jbail:
1242 n_err(_("Failed to save message in \"%s\" - message not sent\n"), ep);
1243 exit_status |= EXIT_ERR;
1244 savedeadletter(fp, 1);
1245 rv = FAL0;
1248 NYD_LEAVE;
1249 return rv;
1252 static int
1253 __savemail(char const *name, FILE *fp)
1255 FILE *fo;
1256 char *buf;
1257 size_t bufsize, buflen, cnt;
1258 int prependnl = 0, rv = -1;
1259 bool_t emptyline;
1260 NYD_ENTER;
1262 buf = smalloc(bufsize = LINESIZE);
1264 if ((fo = Zopen(name, "a+")) == NULL) {
1265 if ((fo = Zopen(name, "wx")) == NULL) {
1266 n_perr(name, 0);
1267 goto jleave;
1269 } else {
1270 if (fseek(fo, -2L, SEEK_END) == 0) {
1271 switch (fread(buf, sizeof *buf, 2, fo)) {
1272 case 2:
1273 if (buf[1] != '\n') {
1274 prependnl = 1;
1275 break;
1277 /* FALLTHRU */
1278 case 1:
1279 if (buf[0] != '\n')
1280 prependnl = 1;
1281 break;
1282 default:
1283 if (ferror(fo)) {
1284 n_perr(name, 0);
1285 goto jleave;
1288 if (prependnl) {
1289 putc('\n', fo);
1291 fflush(fo);
1295 fprintf(fo, "From %s %s", myname, time_current.tc_ctime);
1296 fflush_rewind(fp);
1297 cnt = fsize(fp);
1298 buflen = 0;
1299 emptyline = FAL0;
1300 while (fgetline(&buf, &bufsize, &cnt, &buflen, fp, 0) != NULL) {
1301 #ifdef HAVE_DEBUG /* TODO assert legacy */
1302 assert(!is_head(buf, buflen, TRU1));
1303 UNUSED(emptyline);
1304 #else
1305 if (emptyline && is_head(buf, buflen, TRU1))
1306 putc('>', fo);
1307 #endif
1308 emptyline = (buflen > 0 && *buf == '\n');
1309 fwrite(buf, sizeof *buf, buflen, fo);
1311 if (buflen && *(buf + buflen - 1) != '\n')
1312 putc('\n', fo);
1313 putc('\n', fo);
1314 fflush(fo);
1316 rv = 0;
1317 if (ferror(fo)) {
1318 n_perr(name, 0);
1319 rv = -1;
1321 if (Fclose(fo) != 0)
1322 rv = -1;
1323 fflush_rewind(fp);
1324 jleave:
1325 free(buf);
1326 NYD_LEAVE;
1327 return rv;
1330 static bool_t
1331 _transfer(struct sendbundle *sbp)
1333 struct name *np;
1334 ui32_t cnt;
1335 bool_t rv = TRU1;
1336 NYD_ENTER;
1338 for (cnt = 0, np = sbp->sb_to; np != NULL;) {
1339 char const k[] = "smime-encrypt-";
1340 size_t nl = strlen(np->n_name);
1341 char *cp, *vs = ac_alloc(sizeof(k)-1 + nl +1);
1342 memcpy(vs, k, sizeof(k) -1);
1343 memcpy(vs + sizeof(k) -1, np->n_name, nl +1);
1345 if ((cp = vok_vlook(vs)) != NULL) {
1346 #ifdef HAVE_SSL
1347 FILE *ef;
1349 if ((ef = smime_encrypt(sbp->sb_input, cp, np->n_name)) != NULL) {
1350 FILE *fisave = sbp->sb_input;
1351 struct name *nsave = sbp->sb_to;
1353 sbp->sb_to = ndup(np, np->n_type & ~(GFULL | GSKIN));
1354 sbp->sb_input = ef;
1355 if (!__mta_start(sbp))
1356 rv = FAL0;
1357 sbp->sb_to = nsave;
1358 sbp->sb_input = fisave;
1360 Fclose(ef);
1361 } else {
1362 #else
1363 n_err(_("No SSL support compiled in\n"));
1364 rv = FAL0;
1365 #endif
1366 n_err(_("Message not sent to \"%s\"\n"), np->n_name);
1367 _sendout_error = TRU1;
1368 #ifdef HAVE_SSL
1370 #endif
1371 rewind(sbp->sb_input);
1373 if (np->n_flink != NULL)
1374 np->n_flink->n_blink = np->n_blink;
1375 if (np->n_blink != NULL)
1376 np->n_blink->n_flink = np->n_flink;
1377 if (np == sbp->sb_to)
1378 sbp->sb_to = np->n_flink;
1379 np = np->n_flink;
1380 } else {
1381 ++cnt;
1382 np = np->n_flink;
1384 ac_free(vs);
1387 if (cnt > 0 && (ok_blook(smime_force_encryption) || !__mta_start(sbp)))
1388 rv = FAL0;
1389 NYD_LEAVE;
1390 return rv;
1393 static bool_t
1394 __mta_start(struct sendbundle *sbp)
1396 char const **args = NULL, *mta, *smtp;
1397 pid_t pid;
1398 sigset_t nset;
1399 bool_t rv = FAL0;
1400 NYD_ENTER;
1402 if ((smtp = ok_vlook(smtp)) == NULL) {
1403 if ((mta = ok_vlook(sendmail)) != NULL) {
1404 if ((mta = file_expand(mta)) == NULL)
1405 goto jstop;
1406 } else
1407 mta = SENDMAIL;
1409 args = __mta_prepare_args(sbp->sb_to, sbp->sb_hp);
1410 if (options & OPT_DEBUG) {
1411 __mta_debug(sbp, mta, args);
1412 rv = TRU1;
1413 goto jleave;
1415 } else {
1416 UNINIT(mta, NULL); /* Silence cc */
1417 #ifndef HAVE_SMTP
1418 n_err(_("No SMTP support compiled in\n"));
1419 goto jstop;
1420 #else
1421 if (options & OPT_DEBUG) {
1422 (void)smtp_mta(sbp);
1423 rv = TRU1;
1424 goto jleave;
1426 #endif
1429 /* Fork, set up the temporary mail file as standard input for "mail", and
1430 * exec with the user list we generated far above */
1431 if ((pid = fork_child()) == -1) {
1432 n_perr("fork", 0);
1433 jstop:
1434 savedeadletter(sbp->sb_input, 0);
1435 _sendout_error = TRU1;
1436 goto jleave;
1438 if (pid == 0) {
1439 sigemptyset(&nset);
1440 sigaddset(&nset, SIGHUP);
1441 sigaddset(&nset, SIGINT);
1442 sigaddset(&nset, SIGQUIT);
1443 sigaddset(&nset, SIGTSTP);
1444 sigaddset(&nset, SIGTTIN);
1445 sigaddset(&nset, SIGTTOU);
1446 freopen("/dev/null", "r", stdin);
1447 #ifdef HAVE_SMTP
1448 if (smtp != NULL) {
1449 prepare_child(&nset, 0, 1);
1450 if (smtp_mta(sbp))
1451 _exit(EXIT_OK);
1452 } else
1453 #endif
1455 int e;
1457 prepare_child(&nset, fileno(sbp->sb_input), -1);
1458 /* If *record* is set then savemail() will move the file position;
1459 * it'll call rewind(), but that may optimize away the systemcall if
1460 * possible, and since dup2() shares the position with the original FD
1461 * the MTA may end up reading nothing */
1462 lseek(0, 0, SEEK_SET);
1463 execv(mta, UNCONST(args));
1464 e = errno;
1465 smtp = (e != ENOENT) ? strerror(e)
1466 : _("executable not found (adjust *sendmail* variable)");
1467 n_err(_("Cannot start \"%s\": %s\n"), mta, smtp);
1469 savedeadletter(sbp->sb_input, 1);
1470 n_err(_("... message not sent\n"));
1471 _exit(EXIT_ERR);
1474 if ((options & (OPT_DEBUG | OPT_VERB)) || ok_blook(sendwait)) {
1475 if (wait_child(pid, NULL))
1476 rv = TRU1;
1477 else
1478 _sendout_error = TRU1;
1479 } else {
1480 rv = TRU1;
1481 free_child(pid);
1483 jleave:
1484 NYD_LEAVE;
1485 return rv;
1488 static char const **
1489 __mta_prepare_args(struct name *to, struct header *hp)
1491 size_t vas_count, i, j;
1492 char **vas, *cp;
1493 char const **args;
1494 bool_t snda;
1495 NYD_ENTER;
1497 if ((cp = ok_vlook(sendmail_arguments)) == NULL) {
1498 vas_count = 0;
1499 vas = NULL;
1500 } else {
1501 /* Don't assume anything on the content but do allocate exactly j slots */
1502 j = strlen(cp);
1503 vas = ac_alloc(sizeof(*vas) * j);
1504 vas_count = (size_t)getrawlist(cp, j, vas, (int)j, FAL0);
1507 i = 4 + smopts_count + vas_count + 4 + 1 + count(to) + 1;
1508 args = salloc(i * sizeof(char*));
1510 if ((args[0] = ok_vlook(sendmail_progname)) == NULL || *args[0] == '\0')
1511 args[0] = SENDMAIL_PROGNAME;
1513 if ((snda = ok_blook(sendmail_no_default_arguments)))
1514 i = 1;
1515 else {
1516 args[1] = "-i";
1517 i = 2;
1518 if (ok_blook(metoo))
1519 args[i++] = "-m";
1520 if (options & OPT_VERB)
1521 args[i++] = "-v";
1524 for (j = 0; j < smopts_count; ++j, ++i)
1525 args[i] = smopts[j];
1527 for (j = 0; j < vas_count; ++j, ++i)
1528 args[i] = vas[j];
1530 /* -r option? In conjunction with -t we act compatible to postfix(1) and
1531 * ignore it (it is -f / -F there) if the message specified From:/Sender:.
1532 * The interdependency with -t has been resolved in _puthead() */
1533 if (!snda && (options & OPT_r_FLAG)) {
1534 struct name const *np;
1536 if (hp != NULL && (np = hp->h_from) != NULL) {
1537 /* However, what wasn't resolved there was the case that the message
1538 * specified multiple From: addresses and a Sender: */
1539 if ((pstate & PS_t_FLAG) && hp->h_sender != NULL)
1540 np = hp->h_sender;
1542 if (np->n_fullextra != NULL) {
1543 args[i++] = "-F";
1544 args[i++] = np->n_fullextra;
1546 cp = np->n_name;
1547 } else {
1548 assert(option_r_arg == NULL);
1549 cp = skin(myorigin(NULL));
1552 if (cp != NULL) {
1553 args[i++] = "-f";
1554 args[i++] = cp;
1558 /* Terminate option list to avoid false interpretation of system-wide
1559 * aliases that start with hyphen */
1560 if (!snda)
1561 args[i++] = "--";
1563 /* Receivers follow */
1564 for (; to != NULL; to = to->n_flink)
1565 if (!(to->n_type & GDEL))
1566 args[i++] = to->n_name;
1567 args[i] = NULL;
1569 if (vas != NULL)
1570 ac_free(vas);
1571 NYD_LEAVE;
1572 return args;
1575 static void
1576 __mta_debug(struct sendbundle *sbp, char const *mta, char const **args)
1578 size_t cnt, bufsize;
1579 char *buf;
1580 NYD_ENTER;
1582 n_err(_(">>> MTA: \"%s\", arguments:"), mta);
1583 for (; *args != NULL; ++args)
1584 n_err(" \"%s\"", *args);
1585 n_err("\n");
1587 fflush_rewind(sbp->sb_input);
1589 cnt = fsize(sbp->sb_input);
1590 buf = NULL;
1591 bufsize = 0;
1592 while (fgetline(&buf, &bufsize, &cnt, NULL, sbp->sb_input, 1) != NULL)
1593 n_err(">>> %s", buf);
1594 if (buf != NULL)
1595 free(buf);
1596 NYD_LEAVE;
1599 static char *
1600 _message_id(struct header *hp)
1602 char *rv = NULL, sep;
1603 char const *h;
1604 size_t rl, i;
1605 struct tm *tmp;
1606 NYD_ENTER;
1608 if (hp != NULL && hp->h_message_id != NULL) {
1609 i = strlen(hp->h_message_id->n_name);
1610 rl = sizeof("Message-ID: <>") -1;
1611 rv = salloc(rl + i +1);
1612 memcpy(rv, "Message-ID: <", --rl);
1613 memcpy(rv + rl, hp->h_message_id->n_name, i);
1614 rl += i;
1615 rv[rl++] = '>';
1616 rv[rl] = '\0';
1617 goto jleave;
1620 if (ok_blook(message_id_disable))
1621 goto jleave;
1623 sep = '%';
1624 rl = 9;
1625 if ((h = __sendout_ident) != NULL)
1626 goto jgen;
1627 if (ok_vlook(hostname) != NULL) {
1628 h = nodename(1);
1629 sep = '@';
1630 rl = 15;
1631 goto jgen;
1633 if (hp != NULL && (h = skin(myorigin(hp))) != NULL && strchr(h, '@') != NULL)
1634 goto jgen;
1635 /* Up to MTA */
1636 goto jleave;
1638 jgen:
1639 tmp = &time_current.tc_gm;
1640 i = sizeof("Message-ID: <%04d%02d%02d%02d%02d%02d.%s%c%s>") -1 +
1641 rl + strlen(h);
1642 rv = salloc(i +1);
1643 snprintf(rv, i, "Message-ID: <%04d%02d%02d%02d%02d%02d.%s%c%s>",
1644 tmp->tm_year + 1900, tmp->tm_mon + 1, tmp->tm_mday,
1645 tmp->tm_hour, tmp->tm_min, tmp->tm_sec,
1646 getrandstring(rl), sep, h);
1647 rv[i] = '\0'; /* Because we don't test snprintf(3) return */
1648 jleave:
1649 NYD_LEAVE;
1650 return rv;
1653 static int
1654 fmt(char const *str, struct name *np, FILE *fo, enum fmt_flags ff)
1656 enum {
1657 m_INIT = 1<<0,
1658 m_COMMA = 1<<1,
1659 m_NOPF = 1<<2,
1660 m_NONAME = 1<<3,
1661 m_CSEEN = 1<<4
1662 } m = (ff & GCOMMA) ? m_COMMA : 0;
1663 ssize_t col, len;
1664 int rv = 1;
1665 NYD_ENTER;
1667 col = strlen(str);
1668 if (col) {
1669 fwrite(str, sizeof *str, col, fo);
1670 #undef _X
1671 #define _X(S) (col == sizeof(S) -1 && !asccasecmp(str, S))
1672 if (ff & GFILES) {
1674 } else if (_X("reply-to:") || _X("mail-followup-to:") ||
1675 _X("references:") || _X("disposition-notification-to:"))
1676 m |= m_NOPF | m_NONAME;
1677 else if (ok_blook(add_file_recipients)) {
1679 } else if (_X("to:") || _X("cc:") || _X("bcc:") || _X("resent-to:"))
1680 m |= m_NOPF;
1681 #undef _X
1684 for (; np != NULL; np = np->n_flink) {
1685 if (is_addr_invalid(np,
1686 EACM_NOLOG | (m & m_NONAME ? EACM_NONAME : EACM_NONE)))
1687 continue;
1688 /* File and pipe addresses only printed with set *add-file-recipients* */
1689 if ((m & m_NOPF) && is_fileorpipe_addr(np))
1690 continue;
1692 if ((m & (m_INIT | m_COMMA)) == (m_INIT | m_COMMA)) {
1693 if (putc(',', fo) == EOF)
1694 goto jleave;
1695 m |= m_CSEEN;
1696 ++col;
1699 len = strlen(np->n_fullname);
1700 if (np->n_flags & GREF)
1701 len += 2;
1702 ++col; /* The separating space */
1703 if ((m & m_INIT) && /*col > 1 &&*/ UICMP(z, col + len, >, 72)) {
1704 if (fputs("\n ", fo) == EOF)
1705 goto jleave;
1706 col = 1;
1707 m &= ~m_CSEEN;
1708 } else
1709 putc(' ', fo);
1710 m = (m & ~m_CSEEN) | m_INIT;
1713 char *hb = np->n_fullname;
1714 /* GREF needs to be placed in angle brackets, but which are missing */
1715 if (np->n_type & GREF) {
1716 hb = ac_alloc(len + 2 +1);
1717 hb[0] = '<';
1718 hb[len + 1] = '>';
1719 hb[len + 2] = '\0';
1720 memcpy(hb + 1, np->n_fullname, len);
1721 len += 2;
1723 len = xmime_write(hb, len, fo,
1724 ((ff & FMT_DOMIME) ? CONV_TOHDR_A : CONV_NONE), TD_ICONV);
1725 if (np->n_type & GREF)
1726 ac_free(hb);
1728 if (len < 0)
1729 goto jleave;
1730 col += len;
1733 if (putc('\n', fo) != EOF)
1734 rv = 0;
1735 jleave:
1736 NYD_LEAVE;
1737 return rv;
1740 static int
1741 infix_resend(FILE *fi, FILE *fo, struct message *mp, struct name *to,
1742 int add_resent)
1744 size_t cnt, c, bufsize = 0;
1745 char *buf = NULL;
1746 char const *cp;
1747 struct name *fromfield = NULL, *senderfield = NULL, *mdn;
1748 int rv = 1;
1749 NYD_ENTER;
1751 cnt = mp->m_size;
1753 /* Write the Resent-Fields */
1754 if (add_resent) {
1755 fputs("Resent-", fo);
1756 mkdate(fo, "Date");
1757 if ((cp = myaddrs(NULL)) != NULL) {
1758 if (_putname(cp, GCOMMA, SEND_MBOX, NULL, "Resent-From:", fo,
1759 &fromfield, 0))
1760 goto jleave;
1762 /* TODO RFC 5322: Resent-Sender SHOULD NOT be used if it's EQ -From: */
1763 if ((cp = ok_vlook(sender)) != NULL) {
1764 if (_putname(cp, GCOMMA, SEND_MBOX, NULL, "Resent-Sender:", fo,
1765 &senderfield, 0))
1766 goto jleave;
1768 if (fmt("Resent-To:", to, fo, FMT_COMMA))
1769 goto jleave;
1770 if (((cp = ok_vlook(stealthmua)) == NULL || !strcmp(cp, "noagent")) &&
1771 (cp = _message_id(NULL)) != NULL)
1772 fprintf(fo, "Resent-%s\n", cp);
1775 if ((mdn = UNCONST(check_from_and_sender(fromfield, senderfield))) == NULL)
1776 goto jleave;
1777 if (!_check_dispo_notif(mdn, NULL, fo))
1778 goto jleave;
1780 /* Write the original headers */
1781 while (cnt > 0) {
1782 if (fgetline(&buf, &bufsize, &cnt, &c, fi, 0) == NULL)
1783 break;
1784 /* XXX more checks: The From_ line may be seen when resending */
1785 /* During headers is_head() is actually overkill, so ^From_ is sufficient
1786 * && !is_head(buf, c, TRU1) */
1787 if (ascncasecmp("status:", buf, 7) && strncmp("From ", buf, 5) &&
1788 ascncasecmp("disposition-notification-to:", buf, 28))
1789 fwrite(buf, sizeof *buf, c, fo);
1790 if (cnt > 0 && *buf == '\n')
1791 break;
1794 /* Write the message body */
1795 while (cnt > 0) {
1796 if (fgetline(&buf, &bufsize, &cnt, &c, fi, 0) == NULL)
1797 break;
1798 if (cnt == 0 && *buf == '\n')
1799 break;
1800 fwrite(buf, sizeof *buf, c, fo);
1802 if (buf != NULL)
1803 free(buf);
1804 if (ferror(fo)) {
1805 n_perr(_("temporary mail file"), 0);
1806 goto jleave;
1808 rv = 0;
1809 jleave:
1810 NYD_LEAVE;
1811 return rv;
1814 FL int
1815 mail(struct name *to, struct name *cc, struct name *bcc, char *subject,
1816 struct attachment *attach, char *quotefile, int recipient_record)
1818 struct header head;
1819 struct str in, out;
1820 NYD_ENTER;
1822 memset(&head, 0, sizeof head);
1824 /* The given subject may be in RFC1522 format. */
1825 if (subject != NULL) {
1826 in.s = subject;
1827 in.l = strlen(subject);
1828 mime_fromhdr(&in, &out, /* TODO ??? TD_ISPR |*/ TD_ICONV);
1829 head.h_subject = out.s;
1831 head.h_to = to;
1832 head.h_cc = cc;
1833 head.h_bcc = bcc;
1834 head.h_attach = attach;
1836 mail1(&head, 0, NULL, quotefile, recipient_record, 0);
1838 if (subject != NULL)
1839 free(out.s);
1840 NYD_LEAVE;
1841 return 0;
1844 FL int
1845 c_sendmail(void *v)
1847 int rv;
1848 NYD_ENTER;
1850 rv = sendmail_internal(v, 0);
1851 NYD_LEAVE;
1852 return rv;
1855 FL int
1856 c_Sendmail(void *v)
1858 int rv;
1859 NYD_ENTER;
1861 rv = sendmail_internal(v, 1);
1862 NYD_LEAVE;
1863 return rv;
1866 FL enum okay
1867 mail1(struct header *hp, int printheaders, struct message *quote,
1868 char *quotefile, int recipient_record, int doprefix)
1870 struct sendbundle sb;
1871 struct name *to;
1872 FILE *mtf, *nmtf;
1873 char const *cp;
1874 bool_t dosign;
1875 enum okay rv = STOP;
1876 NYD_ENTER;
1878 _sendout_error = FAL0;
1879 __sendout_ident = NULL;
1881 /* Update some globals we likely need first */
1882 time_current_update(&time_current, TRU1);
1884 /* */
1885 if ((cp = ok_vlook(autocc)) != NULL && *cp != '\0')
1886 hp->h_cc = cat(hp->h_cc, checkaddrs(lextract(cp, GCC | GFULL),
1887 EACM_NORMAL, &_sendout_error));
1888 if ((cp = ok_vlook(autobcc)) != NULL && *cp != '\0')
1889 hp->h_bcc = cat(hp->h_bcc, checkaddrs(lextract(cp, GBCC | GFULL),
1890 EACM_NORMAL, &_sendout_error));
1892 if (_sendout_error < 0) {
1893 n_err(_("Some addressees in *autocc* or *autobcc* were "
1894 "classified as \"hard error\"\n"));
1895 goto j_leave;
1898 /* Collect user's mail from standard input. Get the result as mtf */
1899 mtf = collect(hp, printheaders, quote, quotefile, doprefix, &_sendout_error);
1900 if (mtf == NULL)
1901 goto j_leave;
1903 dosign = TRUM1;
1905 if (options & OPT_INTERACTIVE) {
1906 bool_t eot = TRU1;
1908 if ((eot = (ok_blook(bsdcompat) || ok_blook(askatend)))) {
1909 if (hp->h_cc == NULL && ok_blook(askcc))
1910 eot = FAL0, grab_headers(hp, GCC, 1);
1911 if (hp->h_bcc == NULL && ok_blook(askbcc))
1912 eot = FAL0, grab_headers(hp, GBCC, 1);
1915 if (hp->h_attach == NULL && ok_blook(askattach))
1916 eot = FAL0, edit_attachments(&hp->h_attach);
1918 if (ok_blook(asksign))
1919 eot = FAL0, dosign = getapproval(_("Sign this message (y/n)? "), TRU1);
1921 if (eot) {
1922 printf(_("EOT\n"));
1923 fflush(stdout);
1927 if (fsize(mtf) == 0) {
1928 if (options & OPT_E_FLAG)
1929 goto jleave;
1930 if (hp->h_subject == NULL)
1931 printf(_("No message, no subject; hope that's ok\n"));
1932 else if (ok_blook(bsdcompat) || ok_blook(bsdmsgs))
1933 printf(_("Null message body; hope that's ok\n"));
1936 if (dosign == TRUM1)
1937 dosign = ok_blook(smime_sign); /* TODO USER@HOST <-> *from* +++!!! */
1938 #ifndef HAVE_SSL
1939 if (dosign) {
1940 n_err(_("No SSL support compiled in\n"));
1941 goto jleave;
1943 #endif
1945 /* XXX Update time_current again; once collect() offers editing of more
1946 * XXX headers, including Date:, this must only happen if Date: is the
1947 * XXX same that it was before collect() (e.g., postponing etc.).
1948 * XXX But *do* update otherwise because the mail seems to be backdated
1949 * XXX if the user edited some time, which looks odd and it happened
1950 * XXX to me that i got mis-dated response mails due to that... */
1951 time_current_update(&time_current, TRU1);
1953 /* TODO hrmpf; the MIME/send layer rewrite MUST address the init crap:
1954 * TODO setup the header ONCE; note this affects edit.c, collect.c ...,
1955 * TODO but: offer a hook that rebuilds/expands/checks/fixates all
1956 * TODO header fields ONCE, call that ONCE after user editing etc. has
1957 * TODO completed (one edit cycle) */
1959 /* Take the user names from the combined to and cc lists and do all the
1960 * alias processing. The POSIX standard says:
1961 * The names shall be substituted when alias is used as a recipient
1962 * address specified by the user in an outgoing message (that is,
1963 * other recipients addressed indirectly through the reply command
1964 * shall not be substituted in this manner).
1965 * S-nail thus violates POSIX, as has been pointed out correctly by
1966 * Martin Neitzel, but logic and usability of POSIX standards is not seldom
1967 * disputable anyway. Go for user friendliness */
1969 to = namelist_vaporise_head(hp,
1970 (EACM_NORMAL |
1971 (!(expandaddr_to_eaf() & EAF_NAME) ? EACM_NONAME : EACM_NONE)),
1972 TRU1, &_sendout_error);
1973 if (to == NULL) {
1974 n_err(_("No recipients specified\n"));
1975 goto jfail_dead;
1977 if (_sendout_error < 0) {
1978 n_err(_("Some addressees were classified as \"hard error\"\n"));
1979 goto jfail_dead;
1982 /* */
1983 memset(&sb, 0, sizeof sb);
1984 sb.sb_hp = hp;
1985 sb.sb_to = to;
1986 sb.sb_input = mtf;
1987 if ((dosign || count_nonlocal(to) > 0) &&
1988 !_sendbundle_setup_creds(&sb, (dosign > 0)))
1989 /* TODO saving $DEAD and recovering etc is not yet well defined */
1990 goto jfail_dead;
1992 /* 'Bit ugly kind of control flow until we find a charset that does it */
1993 for (charset_iter_reset(hp->h_charset);; charset_iter_next()) {
1994 int err;
1996 if (!charset_iter_is_valid())
1998 else if ((nmtf = infix(hp, mtf)) != NULL)
1999 break;
2000 else if ((err = errno) == EILSEQ || err == EINVAL) {
2001 rewind(mtf);
2002 continue;
2005 n_perr(_("Failed to create encoded message"), 0);
2006 goto jfail_dead;
2008 mtf = nmtf;
2010 /* */
2011 #ifdef HAVE_SSL
2012 if (dosign) {
2013 if ((nmtf = smime_sign(mtf, sb.sb_signer.s)) == NULL)
2014 goto jfail_dead;
2015 Fclose(mtf);
2016 mtf = nmtf;
2018 #endif
2020 /* TODO truly - i still don't get what follows: (1) we deliver file
2021 * TODO and pipe addressees, (2) we mightrecord() and (3) we transfer
2022 * TODO even if (1) savedeadletter() etc. To me this doesn't make sense? */
2024 /* Deliver pipe and file addressees */
2025 to = _outof(to, mtf, &_sendout_error);
2026 if (_sendout_error)
2027 savedeadletter(mtf, FAL0);
2029 to = elide(to); /* XXX needed only to drop GDELs due to _outof()! */
2031 { ui32_t cnt = count(to);
2032 if ((!recipient_record || cnt > 0) &&
2033 !mightrecord(mtf, (recipient_record ? to : NULL)))
2034 goto jleave;
2035 if (cnt > 0) {
2036 sb.sb_hp = hp;
2037 sb.sb_to = to;
2038 sb.sb_input = mtf;
2039 if (_transfer(&sb))
2040 rv = OKAY;
2041 } else if (!_sendout_error)
2042 rv = OKAY;
2044 jleave:
2045 Fclose(mtf);
2046 j_leave:
2047 if (_sendout_error)
2048 exit_status |= EXIT_SEND_ERROR;
2049 NYD_LEAVE;
2050 return rv;
2052 jfail_dead:
2053 _sendout_error = TRU1;
2054 savedeadletter(mtf, TRU1);
2055 n_err(_("... message not sent\n"));
2056 goto jleave;
2059 FL int
2060 mkdate(FILE *fo, char const *field)
2062 struct tm tmpgm, *tmptr;
2063 int tzdiff, tzdiff_hour, tzdiff_min, rv;
2064 NYD_ENTER;
2066 memcpy(&tmpgm, &time_current.tc_gm, sizeof tmpgm);
2067 tzdiff = time_current.tc_time - mktime(&tmpgm);
2068 tzdiff_hour = (int)(tzdiff / 60);
2069 tzdiff_min = tzdiff_hour % 60;
2070 tzdiff_hour /= 60;
2071 tmptr = &time_current.tc_local;
2072 if (tmptr->tm_isdst > 0)
2073 ++tzdiff_hour;
2074 rv = fprintf(fo, "%s: %s, %02d %s %04d %02d:%02d:%02d %+05d\n",
2075 field,
2076 weekday_names[tmptr->tm_wday],
2077 tmptr->tm_mday, month_names[tmptr->tm_mon],
2078 tmptr->tm_year + 1900, tmptr->tm_hour,
2079 tmptr->tm_min, tmptr->tm_sec,
2080 tzdiff_hour * 100 + tzdiff_min);
2081 NYD_LEAVE;
2082 return rv;
2085 FL int
2086 puthead(struct header *hp, FILE *fo, enum gfield w, enum sendaction action,
2087 enum conversion convert, char const *contenttype, char const *charset)
2089 int rv;
2090 NYD_ENTER;
2092 rv = _puthead(FAL0, hp, fo, w, action, convert, contenttype, charset);
2093 NYD_LEAVE;
2094 return rv;
2097 FL enum okay
2098 resend_msg(struct message *mp, struct name *to, int add_resent) /* TODO check */
2100 struct sendbundle sb;
2101 FILE *ibuf, *nfo, *nfi;
2102 char *tempMail;
2103 enum okay rv = STOP;
2104 NYD_ENTER;
2106 _sendout_error = FAL0;
2107 __sendout_ident = NULL;
2109 /* Update some globals we likely need first */
2110 time_current_update(&time_current, TRU1);
2112 if ((to = checkaddrs(to,
2113 (EACM_NORMAL |
2114 (!(expandaddr_to_eaf() & EAF_NAME) ? EACM_NONAME : EACM_NONE)),
2115 &_sendout_error)) == NULL)
2116 goto jleave;
2117 /* For the _sendout_error<0 case we want to wait until we can write DEAD! */
2118 if (_sendout_error < 0)
2119 n_err(_("Some addressees were classified as \"hard error\"\n"));
2121 if ((nfo = Ftmp(&tempMail, "resend", OF_WRONLY | OF_HOLDSIGS | OF_REGISTER,
2122 0600)) == NULL) {
2123 _sendout_error = TRU1;
2124 n_perr(_("temporary mail file"), 0);
2125 goto jleave;
2127 if ((nfi = Fopen(tempMail, "r")) == NULL)
2128 n_perr(tempMail, 0);
2129 Ftmp_release(&tempMail);
2130 if (nfi == NULL)
2131 goto jerr_o;
2133 if ((ibuf = setinput(&mb, mp, NEED_BODY)) == NULL)
2134 goto jerr_io;
2136 memset(&sb, 0, sizeof sb);
2137 sb.sb_to = to;
2138 sb.sb_input = nfi;
2139 if (count_nonlocal(to) > 0 && !_sendbundle_setup_creds(&sb, FAL0))
2140 /* ..wait until we can write DEAD */
2141 _sendout_error = -1;
2143 if (infix_resend(ibuf, nfo, mp, to, add_resent) != 0) {
2144 jfail_dead:
2145 savedeadletter(nfi, TRU1);
2146 n_err(_("... message not sent\n"));
2147 jerr_io:
2148 Fclose(nfi);
2149 jerr_o:
2150 Fclose(nfo);
2151 _sendout_error = TRU1;
2152 goto jleave;
2155 if (_sendout_error < 0)
2156 goto jfail_dead;
2158 Fclose(nfo);
2159 rewind(nfi);
2161 to = _outof(to, nfi, &_sendout_error);
2163 if (_sendout_error)
2164 savedeadletter(nfi, FAL0);
2166 if (count(to = elide(to)) != 0) {
2167 if (!ok_blook(record_resent) || mightrecord(nfi, NULL)) {
2168 sb.sb_to = to;
2169 /*sb.sb_input = nfi;*/
2170 if (_transfer(&sb))
2171 rv = OKAY;
2173 } else if (!_sendout_error)
2174 rv = OKAY;
2176 Fclose(nfi);
2177 jleave:
2178 if (_sendout_error)
2179 exit_status |= EXIT_SEND_ERROR;
2180 NYD_LEAVE;
2181 return rv;
2184 #undef SEND_LINESIZE
2186 /* s-it-mode */