THANKS: v14.7.5
[s-mailx.git] / sendout.c
blob9b3658c35af289d09382693c5cc25a91c1b98207
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 - 2014 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. All advertising materials mentioning features or use of this software
20 * must display the following acknowledgement:
21 * This product includes software developed by the University of
22 * California, Berkeley and its contributors.
23 * 4. Neither the name of the University nor the names of its contributors
24 * may be used to endorse or promote products derived from this software
25 * without specific prior written permission.
27 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 * SUCH DAMAGE.
40 #ifndef HAVE_AMALGAMATION
41 # include "nail.h"
42 #endif
44 #include <fcntl.h>
46 #undef SEND_LINESIZE
47 #define SEND_LINESIZE \
48 ((1024 / B64_ENCODE_INPUT_PER_LINE) * B64_ENCODE_INPUT_PER_LINE)
50 static char const *__sendout_ident; /* TODO temporary hack; rewrite puthead() */
51 static char * _sendout_boundary;
52 static bool_t _sendout_error;
54 static enum okay _putname(char const *line, enum gfield w,
55 enum sendaction action, size_t *gotcha,
56 char const *prefix, FILE *fo, struct name **xp);
58 /* Get an encoding flag based on the given string */
59 static char const * _get_encoding(const enum conversion convert);
61 /* Write an attachment to the file buffer, converting to MIME */
62 static int _attach_file(struct attachment *ap, FILE *fo);
63 static int __attach_file(struct attachment *ap, FILE *fo);
65 /* There are non-local receivers, collect credentials etc. */
66 static bool_t _sendbundle_setup_creds(struct sendbundle *sbpm,
67 bool_t signing_caps);
69 /* Prepare arguments for the MTA (non *smtp* mode) */
70 static char const ** _prepare_mta_args(struct name *to, struct header *hp);
72 /* Fix the header by glopping all of the expanded names from the distribution
73 * list into the appropriate fields */
74 static struct name * fixhead(struct header *hp, struct name *tolist);
76 /* Put the signature file at fo. TODO layer rewrite: *integrate in body*!! */
77 static int put_signature(FILE *fo, int convert);
79 /* Attach a message to the file buffer */
80 static int attach_message(struct attachment *ap, FILE *fo);
82 /* Generate the body of a MIME multipart message */
83 static int make_multipart(struct header *hp, int convert, FILE *fi,
84 FILE *fo, char const *contenttype, char const *charset);
86 /* Prepend a header in front of the collected stuff and return the new file */
87 static FILE * infix(struct header *hp, FILE *fi);
89 /* Check wether Disposition-Notification-To: is desired */
90 static bool_t _check_dispo_notif(struct name *mdn, struct header *hp,
91 FILE *fo);
93 /* Save the outgoing mail on the passed file */
94 static int savemail(char const *name, FILE *fi);
96 /* Send mail to a bunch of user names. The interface is through mail() */
97 static int sendmail_internal(void *v, int recipient_record);
99 /* */
100 static bool_t _transfer(struct sendbundle *sbp);
102 /* Start the MTA mailing */
103 static bool_t start_mta(struct sendbundle *sbp);
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 /* Create a Message-Id: header field. Use either host name or from address */
109 static void _message_id(FILE *fo, struct header *hp);
111 /* Format the given header line to not exceed 72 characters */
112 static int fmt(char const *str, struct name *np, FILE *fo, int comma,
113 int dropinvalid, int domime);
115 /* Rewrite a message for resending, adding the Resent-Headers */
116 static int infix_resend(FILE *fi, FILE *fo, struct message *mp,
117 struct name *to, int add_resent);
119 static enum okay
120 _putname(char const *line, enum gfield w, enum sendaction action,
121 size_t *gotcha, char const *prefix, FILE *fo, struct name **xp)
123 struct name *np;
124 enum okay rv = STOP;
125 NYD_ENTER;
127 np = lextract(line, GEXTRA | GFULL);
128 if (xp != NULL)
129 *xp = np;
130 if (np == NULL)
132 else if (fmt(prefix, np, fo, w & GCOMMA, 0, (action != SEND_TODISP)))
133 rv = OKAY;
134 else if (gotcha != NULL)
135 ++(*gotcha);
136 NYD_LEAVE;
137 return rv;
140 static char const *
141 _get_encoding(enum conversion const convert)
143 char const *rv;
144 NYD_ENTER;
146 switch (convert) {
147 case CONV_7BIT: rv = "7bit"; break;
148 case CONV_8BIT: rv = "8bit"; break;
149 case CONV_TOQP: rv = "quoted-printable"; break;
150 case CONV_TOB64: rv = "base64"; break;
151 default: rv = NULL; break;
153 NYD_LEAVE;
154 return rv;
157 static int
158 _attach_file(struct attachment *ap, FILE *fo)
160 /* TODO of course, the MIME classification needs to performed once
161 * TODO only, not for each and every charset anew ... ;-// */
162 char *charset_iter_orig[2];
163 long offs;
164 int err = 0;
165 NYD_ENTER;
167 /* Is this already in target charset? Simply copy over */
168 if (ap->a_conv == AC_TMPFILE) {
169 err = __attach_file(ap, fo);
170 Fclose(ap->a_tmpf);
171 DBG( ap->a_tmpf = NULL; )
172 goto jleave;
175 /* If we don't apply charset conversion at all (fixed input=ouput charset)
176 * we also simply copy over, since it's the users desire */
177 if (ap->a_conv == AC_FIX_INCS) {
178 ap->a_charset = ap->a_input_charset;
179 err = __attach_file(ap, fo);
180 goto jleave;
183 /* Otherwise we need to iterate over all possible output charsets */
184 if ((offs = ftell(fo)) == -1) {
185 err = EIO;
186 goto jleave;
188 charset_iter_recurse(charset_iter_orig);
189 for (charset_iter_reset(NULL);; charset_iter_next()) {
190 if (!charset_iter_is_valid()) {
191 err = EILSEQ;
192 break;
194 err = __attach_file(ap, fo);
195 if (err == 0 || (err != EILSEQ && err != EINVAL))
196 break;
197 clearerr(fo);
198 if (fseek(fo, offs, SEEK_SET) == -1) {
199 err = EIO;
200 break;
202 if (ap->a_conv != AC_DEFAULT) {
203 err = EILSEQ;
204 break;
206 ap->a_charset = NULL;
208 charset_iter_restore(charset_iter_orig);
209 jleave:
210 NYD_LEAVE;
211 return err;
214 static int
215 __attach_file(struct attachment *ap, FILE *fo) /* XXX linelength */
217 int err = 0, do_iconv;
218 FILE *fi;
219 char const *charset;
220 enum conversion convert;
221 char *buf;
222 size_t bufsize, lncnt, inlen;
223 NYD_ENTER;
225 /* Either charset-converted temporary file, or plain path */
226 if (ap->a_conv == AC_TMPFILE) {
227 fi = ap->a_tmpf;
228 assert(ftell(fi) == 0);
229 } else if ((fi = Fopen(ap->a_name, "r")) == NULL) {
230 err = errno;
231 perror(ap->a_name);
232 goto jleave;
235 /* MIME part header for attachment */
236 { char const *bn = ap->a_name, *ct;
238 if ((ct = strrchr(bn, '/')) != NULL)
239 bn = ++ct;
240 ct = ap->a_content_type;
241 charset = ap->a_charset;
242 convert = mime_classify_file(fi, (char const**)&ct, &charset, &do_iconv);
243 if (charset == NULL || ap->a_conv == AC_FIX_INCS ||
244 ap->a_conv == AC_TMPFILE)
245 do_iconv = 0;
247 if (fprintf(fo, "\n--%s\nContent-Type: %s", _sendout_boundary, ct) == -1)
248 goto jerr_header;
250 if (charset == NULL) {
251 if (putc('\n', fo) == EOF)
252 goto jerr_header;
253 } else if (fprintf(fo, "; charset=%s\n", charset) == -1)
254 goto jerr_header;
256 if (fprintf(fo, "Content-Transfer-Encoding: %s\n"
257 "Content-Disposition: %s;\n filename=\"",
258 _get_encoding(convert), ap->a_content_disposition) == -1)
259 goto jerr_header;
260 if (xmime_write(bn, strlen(bn), fo, CONV_TOHDR, TD_NONE, NULL) < 0)
261 goto jerr_header;
262 if (fwrite("\"\n", sizeof(char), 2, fo) != 2 * sizeof(char))
263 goto jerr_header;
265 if ((bn = ap->a_content_id) != NULL &&
266 fprintf(fo, "Content-ID: %s\n", bn) == -1)
267 goto jerr_header;
269 if ((bn = ap->a_content_description) != NULL &&
270 fprintf(fo, "Content-Description: %s\n", bn) == -1)
271 goto jerr_header;
273 if (putc('\n', fo) == EOF) {
274 jerr_header:
275 err = errno;
276 goto jerr_fclose;
280 #ifdef HAVE_ICONV
281 if (iconvd != (iconv_t)-1)
282 n_iconv_close(iconvd);
283 if (do_iconv) {
284 char const *tcs = charset_get_lc();
285 if (asccasecmp(charset, tcs) &&
286 (iconvd = n_iconv_open(charset, tcs)) == (iconv_t)-1 &&
287 (err = errno) != 0) {
288 if (err == EINVAL)
289 fprintf(stderr, _("Cannot convert from %s to %s\n"), tcs, charset);
290 else
291 perror("iconv_open");
292 goto jerr_fclose;
295 #endif
297 bufsize = SEND_LINESIZE;
298 buf = smalloc(bufsize);
299 if (convert == CONV_TOQP
300 #ifdef HAVE_ICONV
301 || iconvd != (iconv_t)-1
302 #endif
304 lncnt = fsize(fi);
305 for (;;) {
306 if (convert == CONV_TOQP
307 #ifdef HAVE_ICONV
308 || iconvd != (iconv_t)-1
309 #endif
311 if (fgetline(&buf, &bufsize, &lncnt, &inlen, fi, 0) == NULL)
312 break;
313 } else if ((inlen = fread(buf, sizeof *buf, bufsize, fi)) == 0)
314 break;
315 if (xmime_write(buf, inlen, fo, convert, TD_ICONV, NULL) < 0) {
316 err = errno;
317 goto jerr;
320 if (ferror(fi))
321 err = EDOM;
322 jerr:
323 free(buf);
324 jerr_fclose:
325 if (ap->a_conv != AC_TMPFILE)
326 Fclose(fi);
327 jleave:
328 NYD_LEAVE;
329 return err;
332 static bool_t
333 _sendbundle_setup_creds(struct sendbundle *sbp, bool_t signing_caps)
335 bool_t v15, rv = FAL0;
336 char *shost, *from;
337 #ifdef HAVE_SMTP
338 char *smtp;
339 #endif
340 NYD_ENTER;
342 v15 = ok_blook(v15_compat);
343 shost = (v15 ? ok_vlook(smtp_hostname) : NULL);
344 from = ((signing_caps || !v15 || shost == NULL)
345 ? skin(myorigin(sbp->sb_hp)) : NULL);
347 if (signing_caps) {
348 if (from == NULL) {
349 #ifdef HAVE_SSL
350 fprintf(stderr, _("No *from* address for signing specified\n"));
351 goto jleave;
352 #endif
353 } else
354 sbp->sb_signer.l = strlen(sbp->sb_signer.s = from);
357 #ifdef HAVE_SMTP
358 if ((smtp = ok_vlook(smtp)) == NULL) {
359 rv = TRU1;
360 goto jleave;
363 if (!url_parse(&sbp->sb_url, CPROTO_SMTP, smtp))
364 goto jleave;
366 if (v15) {
367 if (shost == NULL) {
368 if (from == NULL)
369 goto jenofrom;
370 sbp->sb_url.url_u_h.l = strlen(sbp->sb_url.url_u_h.s = from);
371 } else
372 __sendout_ident = sbp->sb_url.url_u_h.s;
373 if (!ccred_lookup(&sbp->sb_ccred, &sbp->sb_url))
374 goto jleave;
375 } else {
376 if (sbp->sb_url.url_had_user || sbp->sb_url.url_pass.s != NULL) {
377 fprintf(stderr, "New-style URL used without *v15-compat* being set\n");
378 goto jleave;
380 /* TODO part of the entire myorigin() disaster, get rid of this! */
381 if (from == NULL) {
382 jenofrom:
383 fprintf(stderr, _("Your configuration requires a *from* address, "
384 "but none was given\n"));
385 goto jleave;
387 if (!ccred_lookup_old(&sbp->sb_ccred, CPROTO_SMTP, from))
388 goto jleave;
389 sbp->sb_url.url_u_h.l = strlen(sbp->sb_url.url_u_h.s = from);
392 rv = TRU1;
393 #endif /* HAVE_SMTP */
394 #if defined HAVE_SSL || defined HAVE_SMTP
395 jleave:
396 #endif
397 NYD_LEAVE;
398 return rv;
401 static char const **
402 _prepare_mta_args(struct name *to, struct header *hp)
404 size_t vas_count, i, j;
405 char **vas, *cp;
406 char const **args;
407 NYD_ENTER;
409 if ((cp = ok_vlook(sendmail_arguments)) == NULL) {
410 vas_count = 0;
411 vas = NULL;
412 } else {
413 j = strlen(cp);
414 vas = ac_alloc(sizeof(*vas) * (j >> 1));
415 vas_count = (size_t)getrawlist(cp, j, vas, (int)(j >> 1), TRU1);
418 i = 4 + smopts_count + vas_count + 2 + count(to) + 1;
419 args = salloc(i * sizeof(char*));
421 args[0] = ok_vlook(sendmail_progname);
422 if (args[0] == NULL || *args[0] == '\0')
423 args[0] = SENDMAIL_PROGNAME;
425 args[1] = "-i";
426 i = 2;
427 if (ok_blook(metoo))
428 args[i++] = "-m";
429 if (options & OPT_VERB)
430 args[i++] = "-v";
432 for (j = 0; j < smopts_count; ++j, ++i)
433 args[i] = smopts[j];
435 for (j = 0; j < vas_count; ++j, ++i)
436 args[i] = vas[j];
438 /* -r option? We may only pass skinned addresses */
439 if (options & OPT_r_FLAG) {
440 if (option_r_arg[0] != '\0')
441 cp = option_r_arg;
442 else if (hp != NULL) {
443 /* puthead() did it, then */
444 assert(hp->h_from != NULL);
445 cp = hp->h_from->n_name;
446 } else
447 cp = skin(myorigin(NULL)); /* XXX ugh! ugh!! */
448 if (cp != NULL) { /* XXX ugh! */
449 args[i++] = "-f";
450 args[i++] = cp;
454 /* Receivers follow */
455 for (; to != NULL; to = to->n_flink)
456 if (!(to->n_type & GDEL))
457 args[i++] = to->n_name;
458 args[i] = NULL;
460 if (vas != NULL)
461 ac_free(vas);
462 NYD_LEAVE;
463 return args;
466 static struct name *
467 fixhead(struct header *hp, struct name *tolist)
469 struct name **npp, *np;
470 NYD_ENTER;
472 tolist = elide(tolist);
474 hp->h_to = hp->h_cc = hp->h_bcc = NULL;
475 for (np = tolist; np != NULL; np = np->n_flink) {
476 switch (np->n_type & (GDEL | GMASK)) {
477 case GTO: npp = &hp->h_to; break;
478 case GCC: npp = &hp->h_cc; break;
479 case GBCC: npp = &hp->h_bcc; break;
480 default: continue;
482 *npp = cat(*npp, ndup(np, np->n_type | GFULL));
484 NYD_LEAVE;
485 return tolist;
488 static int
489 put_signature(FILE *fo, int convert)
491 char buf[SEND_LINESIZE], *sig, c = '\n';
492 FILE *fsig;
493 size_t sz;
494 int rv;
495 NYD_ENTER;
497 if ((sig = ok_vlook(signature)) == NULL || *sig == '\0') {
498 rv = 0;
499 goto jleave;
501 rv = -1;
503 if ((sig = file_expand(sig)) == NULL)
504 goto jleave;
506 if ((fsig = Fopen(sig, "r")) == NULL) {
507 perror(sig);
508 goto jleave;
510 while ((sz = fread(buf, sizeof *buf, SEND_LINESIZE, fsig)) != 0) {
511 c = buf[sz - 1];
512 if (xmime_write(buf, sz, fo, convert, TD_NONE, NULL) < 0)
513 goto jerr;
515 if (ferror(fsig)) {
516 jerr:
517 perror(sig);
518 Fclose(fsig);
519 goto jleave;
521 Fclose(fsig);
522 if (c != '\n')
523 putc('\n', fo);
525 rv = 0;
526 jleave:
527 NYD_LEAVE;
528 return rv;
531 static int
532 attach_message(struct attachment *ap, FILE *fo)
534 struct message *mp;
535 char const *ccp;
536 int rv;
537 NYD_ENTER;
539 fprintf(fo, "\n--%s\nContent-Type: message/rfc822\n"
540 "Content-Disposition: inline\n", _sendout_boundary);
541 if ((ccp = ap->a_content_description) != NULL)
542 fprintf(fo, "Content-Description: %s\n", ccp);
543 fputc('\n', fo);
545 mp = message + ap->a_msgno - 1;
546 touch(mp);
547 rv = (sendmp(mp, fo, 0, NULL, SEND_RFC822, NULL) < 0) ? -1 : 0;
548 NYD_LEAVE;
549 return rv;
552 static int
553 make_multipart(struct header *hp, int convert, FILE *fi, FILE *fo,
554 char const *contenttype, char const *charset)
556 struct attachment *att;
557 int rv = -1;
558 NYD_ENTER;
560 fputs("This is a multi-part message in MIME format.\n", fo);
561 if (fsize(fi) != 0) {
562 char *buf;
563 size_t sz, bufsize, cnt;
565 fprintf(fo, "\n--%s\n", _sendout_boundary);
566 fprintf(fo, "Content-Type: %s", contenttype);
567 if (charset != NULL)
568 fprintf(fo, "; charset=%s", charset);
569 fprintf(fo, "\nContent-Transfer-Encoding: %s\n"
570 "Content-Disposition: inline\n\n", _get_encoding(convert));
572 buf = smalloc(bufsize = SEND_LINESIZE);
573 if (convert == CONV_TOQP
574 #ifdef HAVE_ICONV
575 || iconvd != (iconv_t)-1
576 #endif
578 fflush(fi);
579 cnt = fsize(fi);
581 for (;;) {
582 if (convert == CONV_TOQP
583 #ifdef HAVE_ICONV
584 || iconvd != (iconv_t)-1
585 #endif
587 if (fgetline(&buf, &bufsize, &cnt, &sz, fi, 0) == NULL)
588 break;
589 } else if ((sz = fread(buf, sizeof *buf, bufsize, fi)) == 0)
590 break;
592 if (xmime_write(buf, sz, fo, convert, TD_ICONV, NULL) < 0) {
593 free(buf);
594 goto jleave;
597 free(buf);
599 if (ferror(fi))
600 goto jleave;
601 if (charset != NULL)
602 put_signature(fo, convert);
605 for (att = hp->h_attach; att != NULL; att = att->a_flink) {
606 if (att->a_msgno) {
607 if (attach_message(att, fo) != 0)
608 goto jleave;
609 } else if (_attach_file(att, fo) != 0)
610 goto jleave;
613 /* the final boundary with two attached dashes */
614 fprintf(fo, "\n--%s--\n", _sendout_boundary);
615 rv = 0;
616 jleave:
617 NYD_LEAVE;
618 return rv;
621 static FILE *
622 infix(struct header *hp, FILE *fi) /* TODO check */
624 FILE *nfo, *nfi = NULL;
625 char *tempMail;
626 char const *contenttype, *charset = NULL;
627 enum conversion convert;
628 int do_iconv = 0, err;
629 #ifdef HAVE_ICONV
630 char const *tcs, *convhdr = NULL;
631 #endif
632 NYD_ENTER;
634 if ((nfo = Ftmp(&tempMail, "infix", OF_WRONLY | OF_HOLDSIGS | OF_REGISTER,
635 0600)) == NULL) {
636 perror(_("temporary mail file"));
637 goto jleave;
639 if ((nfi = Fopen(tempMail, "r")) == NULL) {
640 perror(tempMail);
641 Fclose(nfo);
643 Ftmp_release(&tempMail);
644 if (nfi == NULL)
645 goto jleave;
647 contenttype = "text/plain"; /* XXX mail body - always text/plain, want XX? */
648 convert = mime_classify_file(fi, &contenttype, &charset, &do_iconv);
650 #ifdef HAVE_ICONV
651 tcs = charset_get_lc();
652 if ((convhdr = need_hdrconv(hp, GTO | GSUBJECT | GCC | GBCC | GIDENT))) {
653 if (iconvd != (iconv_t)-1) /* XXX */
654 n_iconv_close(iconvd);
655 if (asccasecmp(convhdr, tcs) != 0 &&
656 (iconvd = n_iconv_open(convhdr, tcs)) == (iconv_t)-1 &&
657 (err = errno) != 0)
658 goto jiconv_err;
660 #endif
661 if (puthead(hp, nfo,
662 (GTO | GSUBJECT | GCC | GBCC | GNL | GCOMMA | GUA | GMIME | GMSGID |
663 GIDENT | GREF | GDATE), SEND_MBOX, convert, contenttype, charset))
664 goto jerr;
665 #ifdef HAVE_ICONV
666 if (iconvd != (iconv_t)-1)
667 n_iconv_close(iconvd);
668 #endif
670 #ifdef HAVE_ICONV
671 if (do_iconv && charset != NULL) { /*TODO charset->mime_classify_file*/
672 if (asccasecmp(charset, tcs) != 0 &&
673 (iconvd = n_iconv_open(charset, tcs)) == (iconv_t)-1 &&
674 (err = errno) != 0) {
675 jiconv_err:
676 if (err == EINVAL)
677 fprintf(stderr, _("Cannot convert from %s to %s\n"), tcs, charset);
678 else
679 perror("iconv_open");
680 goto jerr;
683 #endif
685 if (hp->h_attach != NULL) {
686 if (make_multipart(hp, convert, fi, nfo, contenttype, charset) != 0)
687 goto jerr;
688 } else {
689 size_t sz, bufsize, cnt;
690 char *buf;
692 if (convert == CONV_TOQP
693 #ifdef HAVE_ICONV
694 || iconvd != (iconv_t)-1
695 #endif
697 fflush(fi);
698 cnt = fsize(fi);
700 buf = smalloc(bufsize = SEND_LINESIZE);
701 for (err = 0;;) {
702 if (convert == CONV_TOQP
703 #ifdef HAVE_ICONV
704 || iconvd != (iconv_t)-1
705 #endif
707 if (fgetline(&buf, &bufsize, &cnt, &sz, fi, 0) == NULL)
708 break;
709 } else if ((sz = fread(buf, sizeof *buf, bufsize, fi)) == 0)
710 break;
711 if (xmime_write(buf, sz, nfo, convert, TD_ICONV, NULL) < 0) {
712 err = 1;
713 break;
716 free(buf);
718 if (err || ferror(fi)) {
719 jerr:
720 Fclose(nfo);
721 Fclose(nfi);
722 #ifdef HAVE_ICONV
723 if (iconvd != (iconv_t)-1)
724 n_iconv_close(iconvd);
725 #endif
726 nfi = NULL;
727 goto jleave;
729 if (charset != NULL)
730 put_signature(nfo, convert); /* XXX if (text/) !! */
733 #ifdef HAVE_ICONV
734 if (iconvd != (iconv_t)-1)
735 n_iconv_close(iconvd);
736 #endif
738 fflush(nfo);
739 if ((err = ferror(nfo)))
740 perror(_("temporary mail file"));
741 Fclose(nfo);
742 if (!err) {
743 fflush_rewind(nfi);
744 Fclose(fi);
745 } else {
746 Fclose(nfi);
747 nfi = NULL;
749 jleave:
750 NYD_LEAVE;
751 return nfi;
754 static bool_t
755 _check_dispo_notif(struct name *mdn, struct header *hp, FILE *fo)
757 char const *from;
758 bool_t rv = TRU1;
759 NYD_ENTER;
761 /* TODO smtp_disposition_notification (RFC 3798): relation to return-path
762 * TODO not yet checked */
763 if (!ok_blook(disposition_notification_send))
764 goto jleave;
766 if (mdn != NULL && mdn != (struct name*)0x1)
767 from = mdn->n_name;
768 else if ((from = myorigin(hp)) == NULL) {
769 if (options & OPT_D_V)
770 fprintf(stderr, _("*disposition-notification-send*: no *from* set\n"));
771 goto jleave;
774 if (fmt("Disposition-Notification-To:", nalloc(UNCONST(from), 0), fo,
775 GFILES, TRU1, 0))
776 rv = FAL0;
777 jleave:
778 NYD_LEAVE;
779 return rv;
782 static int
783 savemail(char const *name, FILE *fi)
785 FILE *fo;
786 char *buf;
787 size_t bufsize, buflen, cnt;
788 int prependnl = 0, rv = -1;
789 NYD_ENTER;
791 buf = smalloc(bufsize = LINESIZE);
793 if ((fo = Zopen(name, "a+", NULL)) == NULL) {
794 if ((fo = Zopen(name, "wx", NULL)) == NULL) {
795 perror(name);
796 goto jleave;
798 } else {
799 if (fseek(fo, -2L, SEEK_END) == 0) {
800 switch (fread(buf, sizeof *buf, 2, fo)) {
801 case 2:
802 if (buf[1] != '\n') {
803 prependnl = 1;
804 break;
806 /* FALLTHRU */
807 case 1:
808 if (buf[0] != '\n')
809 prependnl = 1;
810 break;
811 default:
812 if (ferror(fo)) {
813 perror(name);
814 goto jleave;
817 if (prependnl) {
818 putc('\n', fo);
820 fflush(fo);
824 fprintf(fo, "From %s %s", myname, time_current.tc_ctime);
825 fflush_rewind(fi);
826 cnt = fsize(fi);
827 buflen = 0;
828 while (fgetline(&buf, &bufsize, &cnt, &buflen, fi, 0) != NULL) {
829 #ifdef HAVE_DEBUG /* TODO assert legacy */
830 assert(!is_head(buf, buflen));
831 #else
832 if (is_head(buf, buflen))
833 putc('>', fo);
834 #endif
835 fwrite(buf, sizeof *buf, buflen, fo);
837 if (buflen && *(buf + buflen - 1) != '\n')
838 putc('\n', fo);
839 putc('\n', fo);
840 fflush(fo);
842 rv = 0;
843 if (ferror(fo)) {
844 perror(name);
845 rv = -1;
847 if (Fclose(fo) != 0)
848 rv = -1;
849 fflush_rewind(fi);
850 jleave:
851 free(buf);
852 NYD_LEAVE;
853 return rv;
856 static int
857 sendmail_internal(void *v, int recipient_record)
859 struct header head;
860 char *str = v;
861 int rv;
862 NYD_ENTER;
864 memset(&head, 0, sizeof head);
865 head.h_to = lextract(str, GTO | GFULL);
866 rv = mail1(&head, 0, NULL, NULL, recipient_record, 0);
867 NYD_LEAVE;
868 return rv;
871 static bool_t
872 _transfer(struct sendbundle *sbp)
874 struct name *np;
875 ui32_t cnt;
876 bool_t rv = TRU1;
877 NYD_ENTER;
879 for (cnt = 0, np = sbp->sb_to; np != NULL;) {
880 char const k[] = "smime-encrypt-";
881 size_t nl = strlen(np->n_name);
882 char *cp, *vs = ac_alloc(sizeof(k)-1 + nl +1);
883 memcpy(vs, k, sizeof(k) -1);
884 memcpy(vs + sizeof(k) -1, np->n_name, nl +1);
886 if ((cp = vok_vlook(vs)) != NULL) {
887 #ifdef HAVE_SSL
888 FILE *ef;
890 if ((ef = smime_encrypt(sbp->sb_input, cp, np->n_name)) != NULL) {
891 FILE *fisave = sbp->sb_input;
892 struct name *nsave = sbp->sb_to;
894 sbp->sb_to = ndup(np, np->n_type & ~(GFULL | GSKIN));
895 sbp->sb_input = ef;
896 if (!start_mta(sbp))
897 rv = FAL0;
898 sbp->sb_to = nsave;
899 sbp->sb_input = fisave;
901 Fclose(ef);
902 } else {
903 #else
904 fprintf(stderr, _("No SSL support compiled in.\n"));
905 rv = FAL0;
906 #endif
907 fprintf(stderr, _("Message not sent to <%s>\n"), np->n_name);
908 _sendout_error = TRU1;
909 #ifdef HAVE_SSL
911 #endif
912 rewind(sbp->sb_input);
914 if (np->n_flink != NULL)
915 np->n_flink->n_blink = np->n_blink;
916 if (np->n_blink != NULL)
917 np->n_blink->n_flink = np->n_flink;
918 if (np == sbp->sb_to)
919 sbp->sb_to = np->n_flink;
920 np = np->n_flink;
921 } else {
922 ++cnt;
923 np = np->n_flink;
925 ac_free(vs);
928 if (cnt > 0 && (ok_blook(smime_force_encryption) || !start_mta(sbp)))
929 rv = FAL0;
930 NYD_LEAVE;
931 return rv;
934 static bool_t
935 start_mta(struct sendbundle *sbp)
937 char const **args = NULL, **t, *mta;
938 char *smtp;
939 pid_t pid;
940 sigset_t nset;
941 bool_t rv = FAL0;
942 NYD_ENTER;
944 if ((smtp = ok_vlook(smtp)) == NULL) {
945 if ((mta = ok_vlook(sendmail)) != NULL) {
946 if ((mta = file_expand(mta)) == NULL)
947 goto jstop;
948 } else
949 mta = SENDMAIL;
951 args = _prepare_mta_args(sbp->sb_to, sbp->sb_hp);
952 if (options & OPT_DEBUG) {
953 printf(_("Sendmail arguments:"));
954 for (t = args; *t != NULL; ++t)
955 printf(" \"%s\"", *t);
956 printf("\n");
957 rv = TRU1;
958 goto jleave;
960 } else {
961 mta = NULL; /* Silence cc */
962 #ifndef HAVE_SMTP
963 fputs(_("No SMTP support compiled in.\n"), stderr);
964 goto jstop;
965 #else
966 /* XXX assert that sendbundle is setup? */
967 #endif
970 /* Fork, set up the temporary mail file as standard input for "mail", and
971 * exec with the user list we generated far above */
972 if ((pid = fork()) == -1) {
973 perror("fork");
974 jstop:
975 savedeadletter(sbp->sb_input, 0);
976 _sendout_error = TRU1;
977 goto jleave;
979 if (pid == 0) {
980 sigemptyset(&nset);
981 sigaddset(&nset, SIGHUP);
982 sigaddset(&nset, SIGINT);
983 sigaddset(&nset, SIGQUIT);
984 sigaddset(&nset, SIGTSTP);
985 sigaddset(&nset, SIGTTIN);
986 sigaddset(&nset, SIGTTOU);
987 freopen("/dev/null", "r", stdin);
988 #ifdef HAVE_SMTP
989 if (smtp != NULL) {
990 prepare_child(&nset, 0, 1);
991 if (smtp_mta(sbp))
992 _exit(0);
993 } else {
994 #endif
995 prepare_child(&nset, fileno(sbp->sb_input), -1);
996 /* If *record* is set then savemail() will move the file position;
997 * it'll call rewind(), but that may optimize away the systemcall if
998 * possible, and since dup2() shares the position with the original FD
999 * the MTA may end up reading nothing */
1000 lseek(0, 0, SEEK_SET);
1001 execv(mta, UNCONST(args));
1002 perror(mta);
1003 #ifdef HAVE_SMTP
1005 #endif
1006 savedeadletter(sbp->sb_input, 1);
1007 fputs(_("... message not sent.\n"), stderr);
1008 _exit(1);
1010 if ((options & (OPT_DEBUG | OPT_VERB | OPT_BATCH_FLAG)) ||
1011 ok_blook(sendwait)) {
1012 if (wait_child(pid, NULL))
1013 rv = TRU1;
1014 else
1015 _sendout_error = TRU1;
1016 } else {
1017 rv = TRU1;
1018 free_child(pid);
1020 jleave:
1021 NYD_LEAVE;
1022 return rv;
1025 static bool_t
1026 mightrecord(FILE *fp, struct name *to)
1028 char *cp, *cq;
1029 char const *ep;
1030 bool_t rv = TRU1;
1031 NYD_ENTER;
1033 if (to != NULL) {
1034 cp = savestr(skinned_name(to));
1035 for (cq = cp; *cq != '\0' && *cq != '@'; ++cq)
1037 *cq = '\0';
1038 } else
1039 cp = ok_vlook(record);
1041 if (cp != NULL) {
1042 if ((ep = expand(cp)) == NULL) {
1043 ep = "NULL";
1044 goto jbail;
1047 if (*ep != '/' && *ep != '+' && ok_blook(outfolder) &&
1048 which_protocol(ep) == PROTO_FILE) {
1049 size_t i = strlen(cp);
1050 cq = salloc(i + 1 +1);
1051 cq[0] = '+';
1052 memcpy(cq + 1, cp, i +1);
1053 cp = cq;
1054 if ((ep = file_expand(cp)) == NULL) {
1055 ep = "NULL";
1056 goto jbail;
1060 if (savemail(ep, fp) != 0) {
1061 jbail:
1062 fprintf(stderr, _("Failed to save message in %s - message not sent\n"),
1063 ep);
1064 exit_status |= EXIT_ERR;
1065 savedeadletter(fp, 1);
1066 rv = FAL0;
1069 NYD_LEAVE;
1070 return rv;
1073 static void
1074 _message_id(FILE *fo, struct header *hp)
1076 char const *h;
1077 size_t rl;
1078 struct tm *tmp;
1079 NYD_ENTER;
1081 if (ok_blook(message_id_disable))
1082 goto jleave;
1084 if ((h = __sendout_ident) != NULL)
1085 rl = 8;
1086 else if ((h = ok_vlook(hostname)) != NULL)
1087 rl = 16;
1088 else if ((h = skin(myorigin(hp))) != NULL && strchr(h, '@') != NULL)
1089 rl = 8;
1090 else
1091 /* Up to MTA */
1092 goto jleave;
1094 tmp = &time_current.tc_gm;
1095 fprintf(fo, "Message-ID: <%04d%02d%02d%02d%02d%02d.%s%c%s>\n",
1096 tmp->tm_year + 1900, tmp->tm_mon + 1, tmp->tm_mday,
1097 tmp->tm_hour, tmp->tm_min, tmp->tm_sec,
1098 getrandstring(rl), (rl == 8 ? '%' : '@'), h);
1099 jleave:
1100 __sendout_ident = NULL;
1101 NYD_LEAVE;
1104 static int
1105 fmt(char const *str, struct name *np, FILE *fo, int flags, int dropinvalid,
1106 int domime)
1108 enum {
1109 m_INIT = 1<<0,
1110 m_COMMA = 1<<1,
1111 m_NOPF = 1<<2,
1112 m_CSEEN = 1<<3
1113 } m = (flags & GCOMMA) ? m_COMMA : 0;
1114 ssize_t col, len;
1115 int rv = 1;
1116 NYD_ENTER;
1118 col = strlen(str);
1119 if (col) {
1120 fwrite(str, sizeof *str, col, fo);
1121 if (flags & GFILES)
1122 goto jstep;
1123 if (col == 9 && !asccasecmp(str, "reply-to:")) {
1124 m |= m_NOPF;
1125 goto jstep;
1127 if (ok_blook(add_file_recipients))
1128 goto jstep;
1129 if ((col == 3 && (!asccasecmp(str, "to:") || !asccasecmp(str, "cc:"))) ||
1130 (col == 4 && !asccasecmp(str, "bcc:")) ||
1131 (col == 10 && !asccasecmp(str, "Resent-To:")))
1132 m |= m_NOPF;
1134 jstep:
1135 for (; np != NULL; np = np->n_flink) {
1136 if ((m & m_NOPF) && is_fileorpipe_addr(np))
1137 continue;
1138 if (is_addr_invalid(np, !dropinvalid)) {
1139 if (dropinvalid)
1140 continue;
1141 else
1142 goto jleave;
1144 if ((m & (m_INIT | m_COMMA)) == (m_INIT | m_COMMA)) {
1145 putc(',', fo);
1146 m |= m_CSEEN;
1147 ++col;
1149 len = strlen(np->n_fullname);
1150 ++col; /* The separating space */
1151 if ((m & m_INIT) && /*col > 1 &&*/ UICMP(z, col + len, >, 72)) {
1152 fputs("\n ", fo);
1153 col = 1;
1154 m &= ~m_CSEEN;
1155 } else
1156 putc(' ', fo);
1157 m = (m & ~m_CSEEN) | m_INIT;
1158 len = xmime_write(np->n_fullname, len, fo,
1159 (domime ? CONV_TOHDR_A : CONV_NONE), TD_ICONV, NULL);
1160 if (len < 0)
1161 goto jleave;
1162 col += len;
1164 putc('\n', fo);
1165 rv = 0;
1166 jleave:
1167 NYD_LEAVE;
1168 return rv;
1171 static int
1172 infix_resend(FILE *fi, FILE *fo, struct message *mp, struct name *to,
1173 int add_resent)
1175 size_t cnt, c, bufsize = 0;
1176 char *buf = NULL;
1177 char const *cp;
1178 struct name *fromfield = NULL, *senderfield = NULL, *mdn;
1179 int rv = 1;
1180 NYD_ENTER;
1182 cnt = mp->m_size;
1184 /* Write the Resent-Fields */
1185 if (add_resent) {
1186 fputs("Resent-", fo);
1187 mkdate(fo, "Date");
1188 if ((cp = myaddrs(NULL)) != NULL) {
1189 if (_putname(cp, GCOMMA, SEND_MBOX, NULL, "Resent-From:", fo,
1190 &fromfield))
1191 goto jleave;
1193 if ((cp = ok_vlook(sender)) != NULL) {
1194 if (_putname(cp, GCOMMA, SEND_MBOX, NULL, "Resent-Sender:", fo,
1195 &senderfield))
1196 goto jleave;
1198 if (fmt("Resent-To:", to, fo, 1, 1, 0))
1199 goto jleave;
1200 if ((cp = ok_vlook(stealthmua)) == NULL || !strcmp(cp, "noagent")) {
1201 fputs("Resent-", fo);
1202 _message_id(fo, NULL);
1206 if ((mdn = UNCONST(check_from_and_sender(fromfield, senderfield))) == NULL)
1207 goto jleave;
1208 if (!_check_dispo_notif(mdn, NULL, fo))
1209 goto jleave;
1211 /* Write the original headers */
1212 while (cnt > 0) {
1213 if (fgetline(&buf, &bufsize, &cnt, &c, fi, 0) == NULL)
1214 break;
1215 /* XXX more checks: The From_ line may be seen when resending */
1216 /* During headers is_head() is actually overkill, so ^From_ is sufficient
1217 * && !is_head(buf, c) */
1218 if (ascncasecmp("status:", buf, 7) && strncmp("From ", buf, 5) &&
1219 ascncasecmp("disposition-notification-to:", buf, 28))
1220 fwrite(buf, sizeof *buf, c, fo);
1221 if (cnt > 0 && *buf == '\n')
1222 break;
1225 /* Write the message body */
1226 while (cnt > 0) {
1227 if (fgetline(&buf, &bufsize, &cnt, &c, fi, 0) == NULL)
1228 break;
1229 if (cnt == 0 && *buf == '\n')
1230 break;
1231 fwrite(buf, sizeof *buf, c, fo);
1233 if (buf != NULL)
1234 free(buf);
1235 if (ferror(fo)) {
1236 perror(_("temporary mail file"));
1237 goto jleave;
1239 rv = 0;
1240 jleave:
1241 NYD_LEAVE;
1242 return rv;
1245 FL int
1246 mail(struct name *to, struct name *cc, struct name *bcc, char *subject,
1247 struct attachment *attach, char *quotefile, int recipient_record)
1249 struct header head;
1250 struct str in, out;
1251 NYD_ENTER;
1253 memset(&head, 0, sizeof head);
1255 /* The given subject may be in RFC1522 format. */
1256 if (subject != NULL) {
1257 in.s = subject;
1258 in.l = strlen(subject);
1259 mime_fromhdr(&in, &out, /* TODO ??? TD_ISPR |*/ TD_ICONV);
1260 head.h_subject = out.s;
1262 if (!(options & OPT_t_FLAG)) {
1263 head.h_to = to;
1264 head.h_cc = cc;
1265 head.h_bcc = bcc;
1267 head.h_attach = attach;
1269 mail1(&head, 0, NULL, quotefile, recipient_record, 0);
1271 if (subject != NULL)
1272 free(out.s);
1273 NYD_LEAVE;
1274 return 0;
1277 FL int
1278 c_sendmail(void *v)
1280 int rv;
1281 NYD_ENTER;
1283 rv = sendmail_internal(v, 0);
1284 NYD_LEAVE;
1285 return rv;
1288 FL int
1289 c_Sendmail(void *v)
1291 int rv;
1292 NYD_ENTER;
1294 rv = sendmail_internal(v, 1);
1295 NYD_LEAVE;
1296 return rv;
1299 FL enum okay
1300 mail1(struct header *hp, int printheaders, struct message *quote,
1301 char *quotefile, int recipient_record, int doprefix)
1303 struct sendbundle sb;
1304 struct name *to;
1305 FILE *mtf, *nmtf;
1306 int dosign = -1, err;
1307 char const *cp;
1308 enum okay rv = STOP;
1309 NYD_ENTER;
1311 _sendout_error = FAL0;
1313 /* Update some globals we likely need first */
1314 time_current_update(&time_current, TRU1);
1316 /* */
1317 if ((cp = ok_vlook(autocc)) != NULL && *cp != '\0')
1318 hp->h_cc = cat(hp->h_cc, checkaddrs(lextract(cp, GCC | GFULL)));
1319 if ((cp = ok_vlook(autobcc)) != NULL && *cp != '\0')
1320 hp->h_bcc = cat(hp->h_bcc, checkaddrs(lextract(cp, GBCC | GFULL)));
1322 /* Collect user's mail from standard input. Get the result as mtf */
1323 mtf = collect(hp, printheaders, quote, quotefile, doprefix);
1324 if (mtf == NULL)
1325 goto j_leave;
1327 if (options & OPT_INTERACTIVE) {
1328 err = (ok_blook(bsdcompat) || ok_blook(askatend));
1329 if (err == 0)
1330 goto jaskeot;
1331 if (ok_blook(askcc))
1332 ++err, grab_headers(hp, GCC, 1);
1333 if (ok_blook(askbcc))
1334 ++err, grab_headers(hp, GBCC, 1);
1335 if (ok_blook(askattach))
1336 ++err, edit_attachments(&hp->h_attach);
1337 if (ok_blook(asksign))
1338 ++err, dosign = getapproval(_("Sign this message (y/n)? "), TRU1);
1339 if (err == 1) {
1340 jaskeot:
1341 printf(_("EOT\n"));
1342 fflush(stdout);
1346 if (fsize(mtf) == 0) {
1347 if (options & OPT_E_FLAG)
1348 goto jleave;
1349 if (hp->h_subject == NULL)
1350 printf(_("No message, no subject; hope that's ok\n"));
1351 else if (ok_blook(bsdcompat) || ok_blook(bsdmsgs))
1352 printf(_("Null message body; hope that's ok\n"));
1355 if (dosign < 0)
1356 dosign = ok_blook(smime_sign);
1357 #ifndef HAVE_SSL
1358 if (dosign) {
1359 fprintf(stderr, _("No SSL support compiled in.\n"));
1360 goto jleave;
1362 #endif
1364 /* XXX Update time_current again; once collect() offers editing of more
1365 * XXX headers, including Date:, this must only happen if Date: is the
1366 * XXX same that it was before collect() (e.g., postponing etc.).
1367 * XXX But *do* update otherwise because the mail seems to be backdated
1368 * XXX if the user edited some time, which looks odd and it happened
1369 * XXX to me that i got mis-dated response mails due to that... */
1370 time_current_update(&time_current, TRU1);
1372 /* TODO hrmpf; the MIME/send layer rewrite MUST address the init crap:
1373 * TODO setup the header ONCE; note this affects edit.c, collect.c ...,
1374 * TODO but: offer a hook that rebuilds/expands/checks/fixates all
1375 * TODO header fields ONCE, call that ONCE after user editing etc. has
1376 * TODO completed (one edit cycle) */
1378 /* Take the user names from the combined to and cc lists and do all the
1379 * alias processing. The POSIX standard says:
1380 * The names shall be substituted when alias is used as a recipient
1381 * address specified by the user in an outgoing message (that is,
1382 * other recipients addressed indirectly through the reply command
1383 * shall not be substituted in this manner).
1384 * S-nail thus violates POSIX, as has been pointed out correctly by
1385 * Martin Neitzel, but logic and usability of POSIX standards is not seldom
1386 * disputable anyway. Go for user friendliness */
1388 /* Do alias expansion on Reply-To: members, too */
1389 /* TODO puthead() YET (!!! see ONCE note above) expands the value, but
1390 * TODO doesn't perform alias expansion; encapsulate in the ONCE-o */
1391 if (hp->h_replyto == NULL && (cp = ok_vlook(replyto)) != NULL)
1392 hp->h_replyto = checkaddrs(lextract(cp, GEXTRA | GFULL));
1393 if (hp->h_replyto != NULL)
1394 hp->h_replyto = elide(usermap(hp->h_replyto, TRU1));
1396 /* TODO what happens now is that all recipients are merged into
1397 * TODO a duplicated list with expanded aliases, then this list is
1398 * TODO splitted again into the three individual recipient lists (with
1399 * TODO duplicates removed).
1400 * TODO later on we use the merged list for outof() pipe/file saving,
1401 * TODO then we eliminate duplicates (again) and then we use that one
1402 * TODO for mightrecord() and _transfer(), and again. ... Please ... */
1404 /* NOTE: Due to elide() in fixhead(), ENSURE to,cc,bcc order of to!,
1405 * because otherwise the recipients will be "degraded" if they occur
1406 * multiple times */
1407 to = usermap(cat(hp->h_to, cat(hp->h_cc, hp->h_bcc)), FAL0);
1408 if (to == NULL) {
1409 fprintf(stderr, _("No recipients specified\n"));
1410 _sendout_error = TRU1;
1412 to = fixhead(hp, to);
1414 /* */
1415 memset(&sb, 0, sizeof sb);
1416 sb.sb_hp = hp;
1417 sb.sb_to = to;
1418 sb.sb_input = mtf;
1419 if ((dosign || count_nonlocal(to) > 0) &&
1420 !_sendbundle_setup_creds(&sb, (dosign > 0)))
1421 /* TODO saving $DEAD and recovering etc is not yet well defined */
1422 goto jfail_dead;
1424 /* 'Bit ugly kind of control flow until we find a charset that does it */
1425 for (charset_iter_reset(hp->h_charset);; charset_iter_next()) {
1426 if (!charset_iter_is_valid())
1428 else if ((nmtf = infix(hp, mtf)) != NULL)
1429 break;
1430 else if ((err = errno) == EILSEQ || err == EINVAL) {
1431 rewind(mtf);
1432 continue;
1435 perror("");
1436 jfail_dead:
1437 _sendout_error = TRU1;
1438 savedeadletter(mtf, TRU1);
1439 fputs(_("... message not sent.\n"), stderr);
1440 goto jleave;
1442 mtf = nmtf;
1444 /* */
1445 #ifdef HAVE_SSL
1446 if (dosign) {
1447 if ((nmtf = smime_sign(mtf, sb.sb_signer.s)) == NULL)
1448 goto jfail_dead;
1449 Fclose(mtf);
1450 mtf = nmtf;
1452 #endif
1454 /* TODO truly - i still don't get what follows: (1) we deliver file
1455 * TODO and pipe addressees, (2) we mightrecord() and (3) we transfer
1456 * TODO even if (1) savedeadletter() etc. To me this doesn't make sense? */
1458 /* Deliver pipe and file addressees */
1459 to = outof(to, mtf, &_sendout_error);
1460 if (_sendout_error)
1461 savedeadletter(mtf, FAL0);
1463 to = elide(to); /* XXX needed only to drop GDELs due to outof()! */
1464 { ui32_t cnt = count(to);
1465 if ((!recipient_record || cnt > 0) &&
1466 !mightrecord(mtf, (recipient_record ? to : NULL)))
1467 goto jleave;
1468 if (cnt > 0) {
1469 sb.sb_hp = hp;
1470 sb.sb_to = to;
1471 sb.sb_input = mtf;
1472 if (_transfer(&sb))
1473 rv = OKAY;
1474 } else if (!_sendout_error)
1475 rv = OKAY;
1477 jleave:
1478 Fclose(mtf);
1479 j_leave:
1480 if (_sendout_error)
1481 exit_status |= EXIT_SEND_ERROR;
1482 NYD_LEAVE;
1483 return rv;
1486 FL int
1487 mkdate(FILE *fo, char const *field)
1489 struct tm *tmptr;
1490 int tzdiff, tzdiff_hour, tzdiff_min, rv;
1491 NYD_ENTER;
1493 tzdiff = time_current.tc_time - mktime(&time_current.tc_gm);
1494 tzdiff_hour = (int)(tzdiff / 60);
1495 tzdiff_min = tzdiff_hour % 60;
1496 tzdiff_hour /= 60;
1497 tmptr = &time_current.tc_local;
1498 if (tmptr->tm_isdst > 0)
1499 ++tzdiff_hour;
1500 rv = fprintf(fo, "%s: %s, %02d %s %04d %02d:%02d:%02d %+05d\n",
1501 field,
1502 weekday_names[tmptr->tm_wday],
1503 tmptr->tm_mday, month_names[tmptr->tm_mon],
1504 tmptr->tm_year + 1900, tmptr->tm_hour,
1505 tmptr->tm_min, tmptr->tm_sec,
1506 tzdiff_hour * 100 + tzdiff_min);
1507 NYD_LEAVE;
1508 return rv;
1511 FL int
1512 puthead(struct header *hp, FILE *fo, enum gfield w, enum sendaction action,
1513 enum conversion convert, char const *contenttype, char const *charset)
1515 #define FMT_CC_AND_BCC() \
1516 do {\
1517 if (hp->h_cc != NULL && (w & GCC)) {\
1518 if (fmt("Cc:", hp->h_cc, fo, (w & (GCOMMA | GFILES)), 0,\
1519 (action != SEND_TODISP)))\
1520 goto jleave;\
1521 ++gotcha;\
1523 if (hp->h_bcc != NULL && (w & GBCC)) {\
1524 if (fmt("Bcc:", hp->h_bcc, fo, (w & (GCOMMA | GFILES)), 0,\
1525 (action != SEND_TODISP)))\
1526 goto jleave;\
1527 ++gotcha;\
1529 } while (0)
1531 char const *addr;
1532 size_t gotcha, l;
1533 struct name *np, *fromfield = NULL, *senderfield = NULL;
1534 int stealthmua, rv = 1;
1535 bool_t nodisp;
1536 NYD_ENTER;
1538 if ((addr = ok_vlook(stealthmua)) != NULL)
1539 stealthmua = !strcmp(addr, "noagent") ? -1 : 1;
1540 else
1541 stealthmua = 0;
1542 gotcha = 0;
1543 nodisp = (action != SEND_TODISP);
1545 if (w & GDATE)
1546 mkdate(fo, "Date"), ++gotcha;
1547 if (w & GIDENT) {
1548 if (hp->h_from != NULL) {
1549 if (fmt("From:", hp->h_from, fo, (w & (GCOMMA | GFILES)), 0, nodisp))
1550 goto jleave;
1551 ++gotcha;
1552 fromfield = hp->h_from;
1553 } else if ((addr = myaddrs(hp)) != NULL) {
1554 if (_putname(addr, w, action, &gotcha, "From:", fo, &fromfield))
1555 goto jleave;
1556 hp->h_from = fromfield;
1559 if (((addr = hp->h_organization) != NULL ||
1560 (addr = ok_vlook(ORGANIZATION)) != NULL) &&
1561 (l = strlen(addr)) > 0) {
1562 fwrite("Organization: ", sizeof(char), 14, fo);
1563 if (xmime_write(addr, l, fo, (!nodisp ? CONV_NONE : CONV_TOHDR),
1564 (!nodisp ? TD_ISPR | TD_ICONV : TD_ICONV), NULL) < 0)
1565 goto jleave;
1566 ++gotcha;
1567 putc('\n', fo);
1570 /* TODO see the ONCE TODO note somewhere around this file;
1571 * TODO but anyway, do NOT perform alias expansion UNLESS
1572 * TODO we are actually sending out! */
1573 if (hp->h_replyto != NULL) {
1574 if (fmt("Reply-To:", hp->h_replyto, fo, w & GCOMMA, 0, nodisp))
1575 goto jleave;
1576 ++gotcha;
1577 } else if ((addr = ok_vlook(replyto)) != NULL)
1578 if (_putname(addr, w, action, &gotcha, "Reply-To:", fo, NULL))
1579 goto jleave;
1581 if (hp->h_sender != NULL) {
1582 if (fmt("Sender:", hp->h_sender, fo, w & GCOMMA, 0, nodisp))
1583 goto jleave;
1584 ++gotcha;
1585 senderfield = hp->h_sender;
1586 } else if ((addr = ok_vlook(sender)) != NULL)
1587 if (_putname(addr, w, action, &gotcha, "Sender:", fo, &senderfield))
1588 goto jleave;
1590 if ((np = UNCONST(check_from_and_sender(fromfield, senderfield))) == NULL)
1591 goto jleave;
1592 if (!_check_dispo_notif(np, hp, fo))
1593 goto jleave;
1596 if (hp->h_to != NULL && w & GTO) {
1597 if (fmt("To:", hp->h_to, fo, (w & (GCOMMA | GFILES)), 0, nodisp))
1598 goto jleave;
1599 ++gotcha;
1602 if (!ok_blook(bsdcompat) && !ok_blook(bsdorder))
1603 FMT_CC_AND_BCC();
1605 if (hp->h_subject != NULL && (w & GSUBJECT)) {
1606 fwrite("Subject: ", sizeof (char), 9, fo);
1607 if (!ascncasecmp(hp->h_subject, "re: ", 4)) {/* TODO localizable */
1608 fwrite("Re: ", sizeof(char), 4, fo);
1609 if (strlen(hp->h_subject + 4) > 0 &&
1610 xmime_write(hp->h_subject + 4, strlen(hp->h_subject + 4), fo,
1611 (!nodisp ? CONV_NONE : CONV_TOHDR),
1612 (!nodisp ? TD_ISPR | TD_ICONV : TD_ICONV), NULL) < 0)
1613 goto jleave;
1614 } else if (*hp->h_subject != '\0') {
1615 if (xmime_write(hp->h_subject, strlen(hp->h_subject), fo,
1616 (!nodisp ? CONV_NONE : CONV_TOHDR),
1617 (!nodisp ? TD_ISPR | TD_ICONV : TD_ICONV), NULL) < 0)
1618 goto jleave;
1620 ++gotcha;
1621 fwrite("\n", sizeof (char), 1, fo);
1624 if (ok_blook(bsdcompat) || ok_blook(bsdorder))
1625 FMT_CC_AND_BCC();
1627 if ((w & GMSGID) && stealthmua <= 0)
1628 _message_id(fo, hp), ++gotcha;
1630 if ((np = hp->h_ref) != NULL && (w & GREF)) {
1631 fmt("References:", np, fo, 0, 1, 0);
1632 if (np->n_name != NULL) {
1633 while (np->n_flink != NULL)
1634 np = np->n_flink;
1635 if (!is_addr_invalid(np, 0)) {
1636 fprintf(fo, "In-Reply-To: %s\n", np->n_name);
1637 ++gotcha;
1642 if ((w & GUA) && stealthmua == 0)
1643 fprintf(fo, "User-Agent: %s %s\n", uagent, version), ++gotcha;
1645 if (w & GMIME) {
1646 fputs("MIME-Version: 1.0\n", fo), ++gotcha;
1647 if (hp->h_attach != NULL) {
1648 _sendout_boundary = mime_create_boundary();/*TODO carrier*/
1649 fprintf(fo, "Content-Type: multipart/mixed;\n boundary=\"%s\"\n",
1650 _sendout_boundary);
1651 } else {
1652 fprintf(fo, "Content-Type: %s", contenttype);
1653 if (charset != NULL)
1654 fprintf(fo, "; charset=%s", charset);
1655 fprintf(fo, "\nContent-Transfer-Encoding: %s\n",
1656 _get_encoding(convert));
1660 if (gotcha && (w & GNL))
1661 putc('\n', fo);
1662 rv = 0;
1663 jleave:
1664 NYD_LEAVE;
1665 return rv;
1666 #undef FMT_CC_AND_BCC
1669 FL enum okay
1670 resend_msg(struct message *mp, struct name *to, int add_resent) /* TODO check */
1672 struct sendbundle sb;
1673 FILE *ibuf, *nfo, *nfi;
1674 char *tempMail;
1675 enum okay rv = STOP;
1676 NYD_ENTER;
1678 _sendout_error = FAL0;
1680 /* Update some globals we likely need first */
1681 time_current_update(&time_current, TRU1);
1683 if ((to = checkaddrs(to)) == NULL) {
1684 _sendout_error = TRU1;
1685 goto jleave;
1688 if ((nfo = Ftmp(&tempMail, "resend", OF_WRONLY | OF_HOLDSIGS | OF_REGISTER,
1689 0600)) == NULL) {
1690 _sendout_error = TRU1;
1691 perror(_("temporary mail file"));
1692 goto jleave;
1694 if ((nfi = Fopen(tempMail, "r")) == NULL) {
1695 _sendout_error = TRU1;
1696 perror(tempMail);
1698 Ftmp_release(&tempMail);
1699 if (nfi == NULL)
1700 goto jerr_o;
1702 if ((ibuf = setinput(&mb, mp, NEED_BODY)) == NULL)
1703 goto jerr_all;
1705 memset(&sb, 0, sizeof sb);
1706 sb.sb_to = to;
1707 sb.sb_input = nfi;
1708 if (count_nonlocal(to) > 0 && !_sendbundle_setup_creds(&sb, FAL0))
1709 /* TODO saving $DEAD and recovering etc is not yet well defined */
1710 goto jerr_all;
1712 if (infix_resend(ibuf, nfo, mp, to, add_resent) != 0) {
1713 savedeadletter(nfi, TRU1);
1714 fputs(_("... message not sent.\n"), stderr);
1715 jerr_all:
1716 Fclose(nfi);
1717 jerr_o:
1718 Fclose(nfo);
1719 _sendout_error = TRU1;
1720 goto jleave;
1722 Fclose(nfo);
1723 rewind(nfi);
1725 to = outof(to, nfi, &_sendout_error);
1726 if (_sendout_error)
1727 savedeadletter(nfi, FAL0);
1729 to = elide(to); /* TODO should have been done in fixhead()? */
1730 if (count(to) != 0) {
1731 if (!ok_blook(record_resent) || mightrecord(nfi, to)) {
1732 sb.sb_to = to;
1733 /*sb.sb_input = nfi;*/
1734 if (_transfer(&sb))
1735 rv = OKAY;
1737 } else if (!_sendout_error)
1738 rv = OKAY;
1740 Fclose(nfi);
1741 jleave:
1742 if (_sendout_error)
1743 exit_status |= EXIT_SEND_ERROR;
1744 NYD_LEAVE;
1745 return rv;
1748 #undef SEND_LINESIZE
1750 /* s-it-mode */