IMAP: add support for international mailbox names..
[s-mailx.git] / sendout.c
blob1f7183c7d9c4d668f0e5661c3127bc513ac3c9ed
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, bool_t resend);
108 static bool_t a_sendout__savemail(char const *name, FILE *fp, bool_t resend);
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, bool_t resend)
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 (!a_sendout__savemail(ep, fp, resend)) {
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 bool_t
1253 a_sendout__savemail(char const *name, FILE *fp, bool_t resend){
1254 FILE *fo;
1255 size_t bufsize, buflen, cnt;
1256 bool_t rv, emptyline;
1257 char *buf;
1258 NYD_ENTER;
1260 buf = smalloc(bufsize = LINESIZE);
1261 rv = emptyline = FAL0;
1263 if((fo = Zopen(name, "a+")) == NULL){
1264 if((fo = Zopen(name, "wx")) == NULL){
1265 n_perr(name, 0);
1266 goto j_leave;
1268 emptyline = TRU1;
1271 /* TODO RETURN check, but be aware of protocols: v15: Mailbox->lock()! */
1272 file_lock(fileno(fo), FLT_WRITE, 0,0, 0);
1274 rv = TRU1;
1276 if(!emptyline){
1277 if(fseek(fo, -2L, SEEK_END) == 0){ /* TODO Should be Mailbox::->append */
1278 switch(fread(buf, sizeof *buf, 2, fo)){
1279 case 2:
1280 if(buf[1] != '\n')
1281 emptyline = TRU1;
1282 break;
1283 case 1:
1284 if(buf[0] != '\n')
1285 emptyline = TRU1;
1286 break;
1287 default:
1288 if(ferror(fo)){
1289 n_perr(name, 0);
1290 rv = FAL0;
1291 goto jleave;
1294 if(emptyline){
1295 putc('\n', fo);
1296 fflush(fo);
1301 fflush_rewind(fp);
1303 fprintf(fo, "From %s %s", myname, time_current.tc_ctime);
1304 for(emptyline = FAL0, buflen = 0, cnt = fsize(fp);
1305 fgetline(&buf, &bufsize, &cnt, &buflen, fp, 0) != NULL;){
1306 /* Only if we are resending it can happen that we have to quote From_
1307 * lines here; we don't generate messages which are ambiguous ourselves */
1308 if(resend){
1309 if(emptyline && is_head(buf, buflen, FAL0))
1310 putc('>', fo);
1311 }DBG(else assert(!is_head(buf, buflen, FAL0)); )
1313 emptyline = (buflen > 0 && *buf == '\n');
1314 fwrite(buf, sizeof *buf, buflen, fo);
1316 if(buflen > 0 && buf[buflen - 1] != '\n')
1317 putc('\n', fo);
1318 putc('\n', fo);
1319 fflush(fo);
1320 if(ferror(fo)){
1321 n_perr(name, 0);
1322 rv = FAL0;
1325 jleave:
1326 really_rewind(fp);
1327 if(Fclose(fo) != 0)
1328 rv = FAL0;
1329 j_leave:
1330 free(buf);
1331 NYD_LEAVE;
1332 return rv;
1335 static bool_t
1336 _transfer(struct sendbundle *sbp)
1338 struct name *np;
1339 ui32_t cnt;
1340 bool_t rv = TRU1;
1341 NYD_ENTER;
1343 for (cnt = 0, np = sbp->sb_to; np != NULL;) {
1344 char const k[] = "smime-encrypt-";
1345 size_t nl = strlen(np->n_name);
1346 char *cp, *vs = ac_alloc(sizeof(k)-1 + nl +1);
1347 memcpy(vs, k, sizeof(k) -1);
1348 memcpy(vs + sizeof(k) -1, np->n_name, nl +1);
1350 if ((cp = vok_vlook(vs)) != NULL) {
1351 #ifdef HAVE_SSL
1352 FILE *ef;
1354 if ((ef = smime_encrypt(sbp->sb_input, cp, np->n_name)) != NULL) {
1355 FILE *fisave = sbp->sb_input;
1356 struct name *nsave = sbp->sb_to;
1358 sbp->sb_to = ndup(np, np->n_type & ~(GFULL | GSKIN));
1359 sbp->sb_input = ef;
1360 if (!__mta_start(sbp))
1361 rv = FAL0;
1362 sbp->sb_to = nsave;
1363 sbp->sb_input = fisave;
1365 Fclose(ef);
1366 } else {
1367 #else
1368 n_err(_("No SSL support compiled in\n"));
1369 rv = FAL0;
1370 #endif
1371 n_err(_("Message not sent to \"%s\"\n"), np->n_name);
1372 _sendout_error = TRU1;
1373 #ifdef HAVE_SSL
1375 #endif
1376 rewind(sbp->sb_input);
1378 if (np->n_flink != NULL)
1379 np->n_flink->n_blink = np->n_blink;
1380 if (np->n_blink != NULL)
1381 np->n_blink->n_flink = np->n_flink;
1382 if (np == sbp->sb_to)
1383 sbp->sb_to = np->n_flink;
1384 np = np->n_flink;
1385 } else {
1386 ++cnt;
1387 np = np->n_flink;
1389 ac_free(vs);
1392 if (cnt > 0 && (ok_blook(smime_force_encryption) || !__mta_start(sbp)))
1393 rv = FAL0;
1394 NYD_LEAVE;
1395 return rv;
1398 static bool_t
1399 __mta_start(struct sendbundle *sbp)
1401 char const **args = NULL, *mta, *smtp;
1402 pid_t pid;
1403 sigset_t nset;
1404 bool_t rv = FAL0;
1405 NYD_ENTER;
1407 if ((smtp = ok_vlook(smtp)) == NULL) {
1408 if ((mta = ok_vlook(sendmail)) != NULL) {
1409 if ((mta = file_expand(mta)) == NULL)
1410 goto jstop;
1411 } else
1412 mta = SENDMAIL;
1414 args = __mta_prepare_args(sbp->sb_to, sbp->sb_hp);
1415 if (options & OPT_DEBUG) {
1416 __mta_debug(sbp, mta, args);
1417 rv = TRU1;
1418 goto jleave;
1420 } else {
1421 UNINIT(mta, NULL); /* Silence cc */
1422 #ifndef HAVE_SMTP
1423 n_err(_("No SMTP support compiled in\n"));
1424 goto jstop;
1425 #else
1426 if (options & OPT_DEBUG) {
1427 (void)smtp_mta(sbp);
1428 rv = TRU1;
1429 goto jleave;
1431 #endif
1434 /* Fork, set up the temporary mail file as standard input for "mail", and
1435 * exec with the user list we generated far above */
1436 if ((pid = fork_child()) == -1) {
1437 n_perr("fork", 0);
1438 jstop:
1439 savedeadletter(sbp->sb_input, 0);
1440 _sendout_error = TRU1;
1441 goto jleave;
1443 if (pid == 0) {
1444 sigemptyset(&nset);
1445 sigaddset(&nset, SIGHUP);
1446 sigaddset(&nset, SIGINT);
1447 sigaddset(&nset, SIGQUIT);
1448 sigaddset(&nset, SIGTSTP);
1449 sigaddset(&nset, SIGTTIN);
1450 sigaddset(&nset, SIGTTOU);
1451 freopen("/dev/null", "r", stdin);
1452 #ifdef HAVE_SMTP
1453 if (smtp != NULL) {
1454 prepare_child(&nset, 0, 1);
1455 if (smtp_mta(sbp))
1456 _exit(EXIT_OK);
1457 } else
1458 #endif
1460 int e;
1462 prepare_child(&nset, fileno(sbp->sb_input), -1);
1463 /* If *record* is set then savemail() will move the file position;
1464 * it'll call rewind(), but that may optimize away the systemcall if
1465 * possible, and since dup2() shares the position with the original FD
1466 * the MTA may end up reading nothing */
1467 lseek(0, 0, SEEK_SET);
1468 execv(mta, UNCONST(args));
1469 e = errno;
1470 smtp = (e != ENOENT) ? strerror(e)
1471 : _("executable not found (adjust *sendmail* variable)");
1472 n_err(_("Cannot start \"%s\": %s\n"), mta, smtp);
1474 savedeadletter(sbp->sb_input, 1);
1475 n_err(_("... message not sent\n"));
1476 _exit(EXIT_ERR);
1479 if ((options & (OPT_DEBUG | OPT_VERB)) || ok_blook(sendwait)) {
1480 if (wait_child(pid, NULL))
1481 rv = TRU1;
1482 else
1483 _sendout_error = TRU1;
1484 } else {
1485 rv = TRU1;
1486 free_child(pid);
1488 jleave:
1489 NYD_LEAVE;
1490 return rv;
1493 static char const **
1494 __mta_prepare_args(struct name *to, struct header *hp)
1496 size_t vas_count, i, j;
1497 char **vas, *cp;
1498 char const **args;
1499 bool_t snda;
1500 NYD_ENTER;
1502 if ((cp = ok_vlook(sendmail_arguments)) == NULL) {
1503 vas_count = 0;
1504 vas = NULL;
1505 } else {
1506 /* Don't assume anything on the content but do allocate exactly j slots */
1507 j = strlen(cp);
1508 vas = ac_alloc(sizeof(*vas) * j);
1509 vas_count = (size_t)getrawlist(cp, j, vas, (int)j, FAL0);
1512 i = 4 + smopts_count + vas_count + 4 + 1 + count(to) + 1;
1513 args = salloc(i * sizeof(char*));
1515 if ((args[0] = ok_vlook(sendmail_progname)) == NULL || *args[0] == '\0')
1516 args[0] = SENDMAIL_PROGNAME;
1518 if ((snda = ok_blook(sendmail_no_default_arguments)))
1519 i = 1;
1520 else {
1521 args[1] = "-i";
1522 i = 2;
1523 if (ok_blook(metoo))
1524 args[i++] = "-m";
1525 if (options & OPT_VERB)
1526 args[i++] = "-v";
1529 for (j = 0; j < smopts_count; ++j, ++i)
1530 args[i] = smopts[j];
1532 for (j = 0; j < vas_count; ++j, ++i)
1533 args[i] = vas[j];
1535 /* -r option? In conjunction with -t we act compatible to postfix(1) and
1536 * ignore it (it is -f / -F there) if the message specified From:/Sender:.
1537 * The interdependency with -t has been resolved in _puthead() */
1538 if (!snda && (options & OPT_r_FLAG)) {
1539 struct name const *np;
1541 if (hp != NULL && (np = hp->h_from) != NULL) {
1542 /* However, what wasn't resolved there was the case that the message
1543 * specified multiple From: addresses and a Sender: */
1544 if ((pstate & PS_t_FLAG) && hp->h_sender != NULL)
1545 np = hp->h_sender;
1547 if (np->n_fullextra != NULL) {
1548 args[i++] = "-F";
1549 args[i++] = np->n_fullextra;
1551 cp = np->n_name;
1552 } else {
1553 assert(option_r_arg == NULL);
1554 cp = skin(myorigin(NULL));
1557 if (cp != NULL) {
1558 args[i++] = "-f";
1559 args[i++] = cp;
1563 /* Terminate option list to avoid false interpretation of system-wide
1564 * aliases that start with hyphen */
1565 if (!snda)
1566 args[i++] = "--";
1568 /* Receivers follow */
1569 for (; to != NULL; to = to->n_flink)
1570 if (!(to->n_type & GDEL))
1571 args[i++] = to->n_name;
1572 args[i] = NULL;
1574 if (vas != NULL)
1575 ac_free(vas);
1576 NYD_LEAVE;
1577 return args;
1580 static void
1581 __mta_debug(struct sendbundle *sbp, char const *mta, char const **args)
1583 size_t cnt, bufsize;
1584 char *buf;
1585 NYD_ENTER;
1587 n_err(_(">>> MTA: \"%s\", arguments:"), mta);
1588 for (; *args != NULL; ++args)
1589 n_err(" \"%s\"", *args);
1590 n_err("\n");
1592 fflush_rewind(sbp->sb_input);
1594 cnt = fsize(sbp->sb_input);
1595 buf = NULL;
1596 bufsize = 0;
1597 while (fgetline(&buf, &bufsize, &cnt, NULL, sbp->sb_input, 1) != NULL)
1598 n_err(">>> %s", buf);
1599 if (buf != NULL)
1600 free(buf);
1601 NYD_LEAVE;
1604 static char *
1605 _message_id(struct header *hp)
1607 char *rv = NULL, sep;
1608 char const *h;
1609 size_t rl, i;
1610 struct tm *tmp;
1611 NYD_ENTER;
1613 if (hp != NULL && hp->h_message_id != NULL) {
1614 i = strlen(hp->h_message_id->n_name);
1615 rl = sizeof("Message-ID: <>") -1;
1616 rv = salloc(rl + i +1);
1617 memcpy(rv, "Message-ID: <", --rl);
1618 memcpy(rv + rl, hp->h_message_id->n_name, i);
1619 rl += i;
1620 rv[rl++] = '>';
1621 rv[rl] = '\0';
1622 goto jleave;
1625 if (ok_blook(message_id_disable))
1626 goto jleave;
1628 sep = '%';
1629 rl = 9;
1630 if ((h = __sendout_ident) != NULL)
1631 goto jgen;
1632 if (ok_vlook(hostname) != NULL) {
1633 h = nodename(1);
1634 sep = '@';
1635 rl = 15;
1636 goto jgen;
1638 if (hp != NULL && (h = skin(myorigin(hp))) != NULL && strchr(h, '@') != NULL)
1639 goto jgen;
1640 /* Up to MTA */
1641 goto jleave;
1643 jgen:
1644 tmp = &time_current.tc_gm;
1645 i = sizeof("Message-ID: <%04d%02d%02d%02d%02d%02d.%s%c%s>") -1 +
1646 rl + strlen(h);
1647 rv = salloc(i +1);
1648 snprintf(rv, i, "Message-ID: <%04d%02d%02d%02d%02d%02d.%s%c%s>",
1649 tmp->tm_year + 1900, tmp->tm_mon + 1, tmp->tm_mday,
1650 tmp->tm_hour, tmp->tm_min, tmp->tm_sec,
1651 getrandstring(rl), sep, h);
1652 rv[i] = '\0'; /* Because we don't test snprintf(3) return */
1653 jleave:
1654 NYD_LEAVE;
1655 return rv;
1658 static int
1659 fmt(char const *str, struct name *np, FILE *fo, enum fmt_flags ff)
1661 enum {
1662 m_INIT = 1<<0,
1663 m_COMMA = 1<<1,
1664 m_NOPF = 1<<2,
1665 m_NONAME = 1<<3,
1666 m_CSEEN = 1<<4
1667 } m = (ff & GCOMMA) ? m_COMMA : 0;
1668 ssize_t col, len;
1669 int rv = 1;
1670 NYD_ENTER;
1672 col = strlen(str);
1673 if (col) {
1674 fwrite(str, sizeof *str, col, fo);
1675 #undef _X
1676 #define _X(S) (col == sizeof(S) -1 && !asccasecmp(str, S))
1677 if (ff & GFILES) {
1679 } else if (_X("reply-to:") || _X("mail-followup-to:") ||
1680 _X("references:") || _X("disposition-notification-to:"))
1681 m |= m_NOPF | m_NONAME;
1682 else if (ok_blook(add_file_recipients)) {
1684 } else if (_X("to:") || _X("cc:") || _X("bcc:") || _X("resent-to:"))
1685 m |= m_NOPF;
1686 #undef _X
1689 for (; np != NULL; np = np->n_flink) {
1690 if (is_addr_invalid(np,
1691 EACM_NOLOG | (m & m_NONAME ? EACM_NONAME : EACM_NONE)))
1692 continue;
1693 /* File and pipe addresses only printed with set *add-file-recipients* */
1694 if ((m & m_NOPF) && is_fileorpipe_addr(np))
1695 continue;
1697 if ((m & (m_INIT | m_COMMA)) == (m_INIT | m_COMMA)) {
1698 if (putc(',', fo) == EOF)
1699 goto jleave;
1700 m |= m_CSEEN;
1701 ++col;
1704 len = strlen(np->n_fullname);
1705 if (np->n_flags & GREF)
1706 len += 2;
1707 ++col; /* The separating space */
1708 if ((m & m_INIT) && /*col > 1 &&*/ UICMP(z, col + len, >, 72)) {
1709 if (fputs("\n ", fo) == EOF)
1710 goto jleave;
1711 col = 1;
1712 m &= ~m_CSEEN;
1713 } else
1714 putc(' ', fo);
1715 m = (m & ~m_CSEEN) | m_INIT;
1718 char *hb = np->n_fullname;
1719 /* GREF needs to be placed in angle brackets, but which are missing */
1720 if (np->n_type & GREF) {
1721 hb = ac_alloc(len + 2 +1);
1722 hb[0] = '<';
1723 hb[len + 1] = '>';
1724 hb[len + 2] = '\0';
1725 memcpy(hb + 1, np->n_fullname, len);
1726 len += 2;
1728 len = xmime_write(hb, len, fo,
1729 ((ff & FMT_DOMIME) ? CONV_TOHDR_A : CONV_NONE), TD_ICONV);
1730 if (np->n_type & GREF)
1731 ac_free(hb);
1733 if (len < 0)
1734 goto jleave;
1735 col += len;
1738 if (putc('\n', fo) != EOF)
1739 rv = 0;
1740 jleave:
1741 NYD_LEAVE;
1742 return rv;
1745 static int
1746 infix_resend(FILE *fi, FILE *fo, struct message *mp, struct name *to,
1747 int add_resent)
1749 size_t cnt, c, bufsize = 0;
1750 char *buf = NULL;
1751 char const *cp;
1752 struct name *fromfield = NULL, *senderfield = NULL, *mdn;
1753 int rv = 1;
1754 NYD_ENTER;
1756 cnt = mp->m_size;
1758 /* Write the Resent-Fields */
1759 if (add_resent) {
1760 fputs("Resent-", fo);
1761 mkdate(fo, "Date");
1762 if ((cp = myaddrs(NULL)) != NULL) {
1763 if (_putname(cp, GCOMMA, SEND_MBOX, NULL, "Resent-From:", fo,
1764 &fromfield, 0))
1765 goto jleave;
1767 /* TODO RFC 5322: Resent-Sender SHOULD NOT be used if it's EQ -From: */
1768 if ((cp = ok_vlook(sender)) != NULL) {
1769 if (_putname(cp, GCOMMA, SEND_MBOX, NULL, "Resent-Sender:", fo,
1770 &senderfield, 0))
1771 goto jleave;
1773 if (fmt("Resent-To:", to, fo, FMT_COMMA))
1774 goto jleave;
1775 if (((cp = ok_vlook(stealthmua)) == NULL || !strcmp(cp, "noagent")) &&
1776 (cp = _message_id(NULL)) != NULL)
1777 fprintf(fo, "Resent-%s\n", cp);
1780 if ((mdn = UNCONST(check_from_and_sender(fromfield, senderfield))) == NULL)
1781 goto jleave;
1782 if (!_check_dispo_notif(mdn, NULL, fo))
1783 goto jleave;
1785 /* Write the original headers */
1786 while (cnt > 0) {
1787 if (fgetline(&buf, &bufsize, &cnt, &c, fi, 0) == NULL)
1788 break;
1789 if (ascncasecmp("status:", buf, 7) &&
1790 ascncasecmp("disposition-notification-to:", buf, 28) &&
1791 !is_head(buf, c, FAL0))
1792 fwrite(buf, sizeof *buf, c, fo);
1793 if (cnt > 0 && *buf == '\n')
1794 break;
1797 /* Write the message body */
1798 while (cnt > 0) {
1799 if (fgetline(&buf, &bufsize, &cnt, &c, fi, 0) == NULL)
1800 break;
1801 if (cnt == 0 && *buf == '\n')
1802 break;
1803 fwrite(buf, sizeof *buf, c, fo);
1805 if (buf != NULL)
1806 free(buf);
1807 if (ferror(fo)) {
1808 n_perr(_("temporary mail file"), 0);
1809 goto jleave;
1811 rv = 0;
1812 jleave:
1813 NYD_LEAVE;
1814 return rv;
1817 FL int
1818 mail(struct name *to, struct name *cc, struct name *bcc, char *subject,
1819 struct attachment *attach, char *quotefile, int recipient_record)
1821 struct header head;
1822 struct str in, out;
1823 NYD_ENTER;
1825 memset(&head, 0, sizeof head);
1827 /* The given subject may be in RFC1522 format. */
1828 if (subject != NULL) {
1829 in.s = subject;
1830 in.l = strlen(subject);
1831 mime_fromhdr(&in, &out, /* TODO ??? TD_ISPR |*/ TD_ICONV);
1832 head.h_subject = out.s;
1834 head.h_to = to;
1835 head.h_cc = cc;
1836 head.h_bcc = bcc;
1837 head.h_attach = attach;
1839 mail1(&head, 0, NULL, quotefile, recipient_record, 0);
1841 if (subject != NULL)
1842 free(out.s);
1843 NYD_LEAVE;
1844 return 0;
1847 FL int
1848 c_sendmail(void *v)
1850 int rv;
1851 NYD_ENTER;
1853 rv = sendmail_internal(v, 0);
1854 NYD_LEAVE;
1855 return rv;
1858 FL int
1859 c_Sendmail(void *v)
1861 int rv;
1862 NYD_ENTER;
1864 rv = sendmail_internal(v, 1);
1865 NYD_LEAVE;
1866 return rv;
1869 FL enum okay
1870 mail1(struct header *hp, int printheaders, struct message *quote,
1871 char *quotefile, int recipient_record, int doprefix)
1873 struct sendbundle sb;
1874 struct name *to;
1875 FILE *mtf, *nmtf;
1876 char const *cp;
1877 bool_t dosign;
1878 enum okay rv = STOP;
1879 NYD_ENTER;
1881 _sendout_error = FAL0;
1882 __sendout_ident = NULL;
1884 /* Update some globals we likely need first */
1885 time_current_update(&time_current, TRU1);
1887 /* */
1888 if ((cp = ok_vlook(autocc)) != NULL && *cp != '\0')
1889 hp->h_cc = cat(hp->h_cc, checkaddrs(lextract(cp, GCC | GFULL),
1890 EACM_NORMAL, &_sendout_error));
1891 if ((cp = ok_vlook(autobcc)) != NULL && *cp != '\0')
1892 hp->h_bcc = cat(hp->h_bcc, checkaddrs(lextract(cp, GBCC | GFULL),
1893 EACM_NORMAL, &_sendout_error));
1895 if (_sendout_error < 0) {
1896 n_err(_("Some addressees in *autocc* or *autobcc* were "
1897 "classified as \"hard error\"\n"));
1898 goto j_leave;
1901 /* Collect user's mail from standard input. Get the result as mtf */
1902 mtf = collect(hp, printheaders, quote, quotefile, doprefix, &_sendout_error);
1903 if (mtf == NULL)
1904 goto j_leave;
1906 dosign = TRUM1;
1908 if (options & OPT_INTERACTIVE) {
1909 bool_t eot = TRU1;
1911 if ((eot = (ok_blook(bsdcompat) || ok_blook(askatend)))) {
1912 if (hp->h_cc == NULL && ok_blook(askcc))
1913 eot = FAL0, grab_headers(hp, GCC, 1);
1914 if (hp->h_bcc == NULL && ok_blook(askbcc))
1915 eot = FAL0, grab_headers(hp, GBCC, 1);
1918 if (hp->h_attach == NULL && ok_blook(askattach))
1919 eot = FAL0, edit_attachments(&hp->h_attach);
1921 if (ok_blook(asksign))
1922 eot = FAL0, dosign = getapproval(_("Sign this message (y/n)? "), TRU1);
1924 if (eot) {
1925 printf(_("EOT\n"));
1926 fflush(stdout);
1930 if (fsize(mtf) == 0) {
1931 if (options & OPT_E_FLAG)
1932 goto jleave;
1933 if (hp->h_subject == NULL)
1934 printf(_("No message, no subject; hope that's ok\n"));
1935 else if (ok_blook(bsdcompat) || ok_blook(bsdmsgs))
1936 printf(_("Null message body; hope that's ok\n"));
1939 if (dosign == TRUM1)
1940 dosign = ok_blook(smime_sign); /* TODO USER@HOST <-> *from* +++!!! */
1941 #ifndef HAVE_SSL
1942 if (dosign) {
1943 n_err(_("No SSL support compiled in\n"));
1944 goto jleave;
1946 #endif
1948 /* XXX Update time_current again; once collect() offers editing of more
1949 * XXX headers, including Date:, this must only happen if Date: is the
1950 * XXX same that it was before collect() (e.g., postponing etc.).
1951 * XXX But *do* update otherwise because the mail seems to be backdated
1952 * XXX if the user edited some time, which looks odd and it happened
1953 * XXX to me that i got mis-dated response mails due to that... */
1954 time_current_update(&time_current, TRU1);
1956 /* TODO hrmpf; the MIME/send layer rewrite MUST address the init crap:
1957 * TODO setup the header ONCE; note this affects edit.c, collect.c ...,
1958 * TODO but: offer a hook that rebuilds/expands/checks/fixates all
1959 * TODO header fields ONCE, call that ONCE after user editing etc. has
1960 * TODO completed (one edit cycle) */
1962 /* Take the user names from the combined to and cc lists and do all the
1963 * alias processing. The POSIX standard says:
1964 * The names shall be substituted when alias is used as a recipient
1965 * address specified by the user in an outgoing message (that is,
1966 * other recipients addressed indirectly through the reply command
1967 * shall not be substituted in this manner).
1968 * S-nail thus violates POSIX, as has been pointed out correctly by
1969 * Martin Neitzel, but logic and usability of POSIX standards is not seldom
1970 * disputable anyway. Go for user friendliness */
1972 to = namelist_vaporise_head(hp,
1973 (EACM_NORMAL |
1974 (!(expandaddr_to_eaf() & EAF_NAME) ? EACM_NONAME : EACM_NONE)),
1975 TRU1, &_sendout_error);
1976 if (to == NULL) {
1977 n_err(_("No recipients specified\n"));
1978 goto jfail_dead;
1980 if (_sendout_error < 0) {
1981 n_err(_("Some addressees were classified as \"hard error\"\n"));
1982 goto jfail_dead;
1985 /* */
1986 memset(&sb, 0, sizeof sb);
1987 sb.sb_hp = hp;
1988 sb.sb_to = to;
1989 sb.sb_input = mtf;
1990 if ((dosign || count_nonlocal(to) > 0) &&
1991 !_sendbundle_setup_creds(&sb, (dosign > 0)))
1992 /* TODO saving $DEAD and recovering etc is not yet well defined */
1993 goto jfail_dead;
1995 /* 'Bit ugly kind of control flow until we find a charset that does it */
1996 for (charset_iter_reset(hp->h_charset);; charset_iter_next()) {
1997 int err;
1999 if (!charset_iter_is_valid())
2001 else if ((nmtf = infix(hp, mtf)) != NULL)
2002 break;
2003 else if ((err = errno) == EILSEQ || err == EINVAL) {
2004 rewind(mtf);
2005 continue;
2008 n_perr(_("Failed to create encoded message"), 0);
2009 goto jfail_dead;
2011 mtf = nmtf;
2013 /* */
2014 #ifdef HAVE_SSL
2015 if (dosign) {
2016 if ((nmtf = smime_sign(mtf, sb.sb_signer.s)) == NULL)
2017 goto jfail_dead;
2018 Fclose(mtf);
2019 mtf = nmtf;
2021 #endif
2023 /* TODO truly - i still don't get what follows: (1) we deliver file
2024 * TODO and pipe addressees, (2) we mightrecord() and (3) we transfer
2025 * TODO even if (1) savedeadletter() etc. To me this doesn't make sense? */
2027 /* Deliver pipe and file addressees */
2028 to = _outof(to, mtf, &_sendout_error);
2029 if (_sendout_error)
2030 savedeadletter(mtf, FAL0);
2032 to = elide(to); /* XXX needed only to drop GDELs due to _outof()! */
2034 { ui32_t cnt = count(to);
2035 if ((!recipient_record || cnt > 0) &&
2036 !mightrecord(mtf, (recipient_record ? to : NULL), FAL0))
2037 goto jleave;
2038 if (cnt > 0) {
2039 sb.sb_hp = hp;
2040 sb.sb_to = to;
2041 sb.sb_input = mtf;
2042 if (_transfer(&sb))
2043 rv = OKAY;
2044 } else if (!_sendout_error)
2045 rv = OKAY;
2047 jleave:
2048 Fclose(mtf);
2049 j_leave:
2050 if (_sendout_error)
2051 exit_status |= EXIT_SEND_ERROR;
2052 NYD_LEAVE;
2053 return rv;
2055 jfail_dead:
2056 _sendout_error = TRU1;
2057 savedeadletter(mtf, TRU1);
2058 n_err(_("... message not sent\n"));
2059 goto jleave;
2062 FL int
2063 mkdate(FILE *fo, char const *field)
2065 struct tm tmpgm, *tmptr;
2066 int tzdiff, tzdiff_hour, tzdiff_min, rv;
2067 NYD_ENTER;
2069 memcpy(&tmpgm, &time_current.tc_gm, sizeof tmpgm);
2070 tzdiff = time_current.tc_time - mktime(&tmpgm);
2071 tzdiff_hour = (int)(tzdiff / 60);
2072 tzdiff_min = tzdiff_hour % 60;
2073 tzdiff_hour /= 60;
2074 tmptr = &time_current.tc_local;
2075 if (tmptr->tm_isdst > 0)
2076 ++tzdiff_hour;
2077 rv = fprintf(fo, "%s: %s, %02d %s %04d %02d:%02d:%02d %+05d\n",
2078 field,
2079 weekday_names[tmptr->tm_wday],
2080 tmptr->tm_mday, month_names[tmptr->tm_mon],
2081 tmptr->tm_year + 1900, tmptr->tm_hour,
2082 tmptr->tm_min, tmptr->tm_sec,
2083 tzdiff_hour * 100 + tzdiff_min);
2084 NYD_LEAVE;
2085 return rv;
2088 FL int
2089 puthead(struct header *hp, FILE *fo, enum gfield w, enum sendaction action,
2090 enum conversion convert, char const *contenttype, char const *charset)
2092 int rv;
2093 NYD_ENTER;
2095 rv = _puthead(FAL0, hp, fo, w, action, convert, contenttype, charset);
2096 NYD_LEAVE;
2097 return rv;
2100 FL enum okay
2101 resend_msg(struct message *mp, struct name *to, int add_resent) /* TODO check */
2103 struct sendbundle sb;
2104 FILE *ibuf, *nfo, *nfi;
2105 char *tempMail;
2106 enum okay rv = STOP;
2107 NYD_ENTER;
2109 _sendout_error = FAL0;
2110 __sendout_ident = NULL;
2112 /* Update some globals we likely need first */
2113 time_current_update(&time_current, TRU1);
2115 if ((to = checkaddrs(to,
2116 (EACM_NORMAL |
2117 (!(expandaddr_to_eaf() & EAF_NAME) ? EACM_NONAME : EACM_NONE)),
2118 &_sendout_error)) == NULL)
2119 goto jleave;
2120 /* For the _sendout_error<0 case we want to wait until we can write DEAD! */
2121 if (_sendout_error < 0)
2122 n_err(_("Some addressees were classified as \"hard error\"\n"));
2124 if ((nfo = Ftmp(&tempMail, "resend", OF_WRONLY | OF_HOLDSIGS | OF_REGISTER,
2125 0600)) == NULL) {
2126 _sendout_error = TRU1;
2127 n_perr(_("temporary mail file"), 0);
2128 goto jleave;
2130 if ((nfi = Fopen(tempMail, "r")) == NULL)
2131 n_perr(tempMail, 0);
2132 Ftmp_release(&tempMail);
2133 if (nfi == NULL)
2134 goto jerr_o;
2136 if ((ibuf = setinput(&mb, mp, NEED_BODY)) == NULL)
2137 goto jerr_io;
2139 memset(&sb, 0, sizeof sb);
2140 sb.sb_to = to;
2141 sb.sb_input = nfi;
2142 if (count_nonlocal(to) > 0 && !_sendbundle_setup_creds(&sb, FAL0))
2143 /* ..wait until we can write DEAD */
2144 _sendout_error = -1;
2146 if (infix_resend(ibuf, nfo, mp, to, add_resent) != 0) {
2147 jfail_dead:
2148 savedeadletter(nfi, TRU1);
2149 n_err(_("... message not sent\n"));
2150 jerr_io:
2151 Fclose(nfi);
2152 jerr_o:
2153 Fclose(nfo);
2154 _sendout_error = TRU1;
2155 goto jleave;
2158 if (_sendout_error < 0)
2159 goto jfail_dead;
2161 Fclose(nfo);
2162 rewind(nfi);
2164 to = _outof(to, nfi, &_sendout_error);
2166 if (_sendout_error)
2167 savedeadletter(nfi, FAL0);
2169 if (count(to = elide(to)) != 0) {
2170 if (!ok_blook(record_resent) || mightrecord(nfi, NULL, TRU1)) {
2171 sb.sb_to = to;
2172 /*sb.sb_input = nfi;*/
2173 if (_transfer(&sb))
2174 rv = OKAY;
2176 } else if (!_sendout_error)
2177 rv = OKAY;
2179 Fclose(nfi);
2180 jleave:
2181 if (_sendout_error)
2182 exit_status |= EXIT_SEND_ERROR;
2183 NYD_LEAVE;
2184 return rv;
2187 #undef SEND_LINESIZE
2189 /* s-it-mode */