Add SMTP support for *v15-compat* and *smtp-hostname*
[s-mailx.git] / sendout.c
blob999562ac4ad360e670e95783c2c34a1fd15cd555
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 #define INFIX_BUF \
47 ((1024 / B64_ENCODE_INPUT_PER_LINE) * B64_ENCODE_INPUT_PER_LINE)
49 static char * _sendout_boundary;
50 static bool_t _sendout_error;
52 static enum okay _putname(char const *line, enum gfield w,
53 enum sendaction action, size_t *gotcha,
54 char const *prefix, FILE *fo, struct name **xp);
56 /* Get an encoding flag based on the given string */
57 static char const * _get_encoding(const enum conversion convert);
59 /* Write an attachment to the file buffer, converting to MIME */
60 static int _attach_file(struct attachment *ap, FILE *fo);
61 static int __attach_file(struct attachment *ap, FILE *fo);
63 /* There are non-local receivers, collect credentials etc. */
64 static bool_t _sendbundle_setup_creds(struct sendbundle *sbpm,
65 bool_t signing_caps);
67 /* Prepare arguments for the MTA (non *smtp* mode) */
68 static char const ** _prepare_mta_args(struct name *to, struct header *hp);
70 /* Fix the header by glopping all of the expanded names from the distribution
71 * list into the appropriate fields */
72 static struct name * fixhead(struct header *hp, struct name *tolist);
74 /* Put the signature file at fo. TODO layer rewrite: *integrate in body*!! */
75 static int put_signature(FILE *fo, int convert);
77 /* Attach a message to the file buffer */
78 static int attach_message(struct attachment *ap, FILE *fo);
80 /* Generate the body of a MIME multipart message */
81 static int make_multipart(struct header *hp, int convert, FILE *fi,
82 FILE *fo, char const *contenttype, char const *charset);
84 /* Prepend a header in front of the collected stuff and return the new file */
85 static FILE * infix(struct header *hp, FILE *fi);
87 /* Save the outgoing mail on the passed file */
88 static int savemail(char const *name, FILE *fi);
90 /* Send mail to a bunch of user names. The interface is through mail() */
91 static int sendmail_internal(void *v, int recipient_record);
93 /* */
94 static bool_t _transfer(struct sendbundle *sbp);
96 /* Start the MTA mailing */
97 static bool_t start_mta(struct sendbundle *sbp);
99 /* Record outgoing mail if instructed to do so; in *record* unless to is set */
100 static bool_t mightrecord(FILE *fp, struct name *to);
102 /* Create a Message-Id: header field. Use either host name or from address */
103 static void message_id(FILE *fo, struct header *hp);
105 /* Format the given header line to not exceed 72 characters */
106 static int fmt(char const *str, struct name *np, FILE *fo, int comma,
107 int dropinvalid, int domime);
109 /* Rewrite a message for resending, adding the Resent-Headers */
110 static int infix_resend(FILE *fi, FILE *fo, struct message *mp,
111 struct name *to, int add_resent);
113 static enum okay
114 _putname(char const *line, enum gfield w, enum sendaction action,
115 size_t *gotcha, char const *prefix, FILE *fo, struct name **xp)
117 struct name *np;
118 enum okay rv = STOP;
119 NYD_ENTER;
121 np = lextract(line, GEXTRA | GFULL);
122 if (xp != NULL)
123 *xp = np;
124 if (np == NULL)
126 else if (fmt(prefix, np, fo, w & GCOMMA, 0, (action != SEND_TODISP)))
127 rv = OKAY;
128 else if (gotcha != NULL)
129 ++(*gotcha);
130 NYD_LEAVE;
131 return rv;
134 static char const *
135 _get_encoding(enum conversion const convert)
137 char const *rv;
138 NYD_ENTER;
140 switch (convert) {
141 case CONV_7BIT: rv = "7bit"; break;
142 case CONV_8BIT: rv = "8bit"; break;
143 case CONV_TOQP: rv = "quoted-printable"; break;
144 case CONV_TOB64: rv = "base64"; break;
145 default: rv = NULL; break;
147 NYD_LEAVE;
148 return rv;
151 static int
152 _attach_file(struct attachment *ap, FILE *fo)
154 /* TODO of course, the MIME classification needs to performed once
155 * TODO only, not for each and every charset anew ... ;-// */
156 char *charset_iter_orig[2];
157 long offs;
158 int err = 0;
159 NYD_ENTER;
161 /* Is this already in target charset? Simply copy over */
162 if (ap->a_conv == AC_TMPFILE) {
163 err = __attach_file(ap, fo);
164 Fclose(ap->a_tmpf);
165 DBG( ap->a_tmpf = NULL; )
166 goto jleave;
169 /* If we don't apply charset conversion at all (fixed input=ouput charset)
170 * we also simply copy over, since it's the users desire */
171 if (ap->a_conv == AC_FIX_INCS) {
172 ap->a_charset = ap->a_input_charset;
173 err = __attach_file(ap, fo);
174 goto jleave;
177 /* Otherwise we need to iterate over all possible output charsets */
178 if ((offs = ftell(fo)) == -1) {
179 err = EIO;
180 goto jleave;
182 charset_iter_recurse(charset_iter_orig);
183 for (charset_iter_reset(NULL);; charset_iter_next()) {
184 if (!charset_iter_is_valid()) {
185 err = EILSEQ;
186 break;
188 err = __attach_file(ap, fo);
189 if (err == 0 || (err != EILSEQ && err != EINVAL))
190 break;
191 clearerr(fo);
192 if (fseek(fo, offs, SEEK_SET) == -1) {
193 err = EIO;
194 break;
196 if (ap->a_conv != AC_DEFAULT) {
197 err = EILSEQ;
198 break;
200 ap->a_charset = NULL;
202 charset_iter_restore(charset_iter_orig);
203 jleave:
204 NYD_LEAVE;
205 return err;
208 static int
209 __attach_file(struct attachment *ap, FILE *fo) /* XXX linelength */
211 int err = 0, do_iconv;
212 FILE *fi;
213 char const *charset;
214 enum conversion convert;
215 char *buf;
216 size_t bufsize, lncnt, inlen;
217 NYD_ENTER;
219 /* Either charset-converted temporary file, or plain path */
220 if (ap->a_conv == AC_TMPFILE) {
221 fi = ap->a_tmpf;
222 assert(ftell(fi) == 0);
223 } else if ((fi = Fopen(ap->a_name, "r")) == NULL) {
224 err = errno;
225 perror(ap->a_name);
226 goto jleave;
229 /* MIME part header for attachment */
230 { char const *bn = ap->a_name, *ct;
232 if ((ct = strrchr(bn, '/')) != NULL)
233 bn = ++ct;
234 ct = ap->a_content_type;
235 charset = ap->a_charset;
236 convert = mime_classify_file(fi, (char const**)&ct, &charset, &do_iconv);
237 if (charset == NULL || ap->a_conv == AC_FIX_INCS ||
238 ap->a_conv == AC_TMPFILE)
239 do_iconv = 0;
241 if (fprintf(fo, "\n--%s\nContent-Type: %s", _sendout_boundary, ct) == -1)
242 goto jerr_header;
244 if (charset == NULL) {
245 if (putc('\n', fo) == EOF)
246 goto jerr_header;
247 } else if (fprintf(fo, "; charset=%s\n", charset) == -1)
248 goto jerr_header;
250 if (fprintf(fo, "Content-Transfer-Encoding: %s\n"
251 "Content-Disposition: %s;\n filename=\"",
252 _get_encoding(convert), ap->a_content_disposition) == -1)
253 goto jerr_header;
254 if (xmime_write(bn, strlen(bn), fo, CONV_TOHDR, TD_NONE, NULL) < 0)
255 goto jerr_header;
256 if (fwrite("\"\n", sizeof(char), 2, fo) != 2 * sizeof(char))
257 goto jerr_header;
259 if ((bn = ap->a_content_id) != NULL &&
260 fprintf(fo, "Content-ID: %s\n", bn) == -1)
261 goto jerr_header;
263 if ((bn = ap->a_content_description) != NULL &&
264 fprintf(fo, "Content-Description: %s\n", bn) == -1)
265 goto jerr_header;
267 if (putc('\n', fo) == EOF) {
268 jerr_header:
269 err = errno;
270 goto jerr_fclose;
274 #ifdef HAVE_ICONV
275 if (iconvd != (iconv_t)-1)
276 n_iconv_close(iconvd);
277 if (do_iconv) {
278 char const *tcs = charset_get_lc();
279 if (asccasecmp(charset, tcs) &&
280 (iconvd = n_iconv_open(charset, tcs)) == (iconv_t)-1 &&
281 (err = errno) != 0) {
282 if (err == EINVAL)
283 fprintf(stderr, tr(179, "Cannot convert from %s to %s\n"),
284 tcs, charset);
285 else
286 perror("iconv_open");
287 goto jerr_fclose;
290 #endif
292 bufsize = INFIX_BUF;
293 buf = smalloc(bufsize);
294 if (convert == CONV_TOQP
295 #ifdef HAVE_ICONV
296 || iconvd != (iconv_t)-1
297 #endif
299 lncnt = fsize(fi);
300 for (;;) {
301 if (convert == CONV_TOQP
302 #ifdef HAVE_ICONV
303 || iconvd != (iconv_t)-1
304 #endif
306 if (fgetline(&buf, &bufsize, &lncnt, &inlen, fi, 0) == NULL)
307 break;
308 } else if ((inlen = fread(buf, sizeof *buf, bufsize, fi)) == 0)
309 break;
310 if (xmime_write(buf, inlen, fo, convert, TD_ICONV, NULL) < 0) {
311 err = errno;
312 goto jerr;
315 if (ferror(fi))
316 err = EDOM;
317 jerr:
318 free(buf);
319 jerr_fclose:
320 if (ap->a_conv != AC_TMPFILE)
321 Fclose(fi);
322 jleave:
323 NYD_LEAVE;
324 return err;
327 static bool_t
328 _sendbundle_setup_creds(struct sendbundle *sbp, bool_t signing_caps)
330 bool_t v15, rv = FAL0;
331 char *shost, *from, *smtp;
332 NYD_ENTER;
334 v15 = ok_blook(v15_compat);
335 shost = (v15 ? ok_vlook(smtp_hostname) : NULL);
336 from = ((signing_caps || !v15 || shost == NULL)
337 ? skin(myorigin(sbp->sb_hp)) : NULL);
339 if (signing_caps) {
340 if (from == NULL) {
341 #ifdef HAVE_SSL
342 fprintf(stderr, tr(531, "No *from* address for signing specified\n"));
343 goto jleave;
344 #endif
345 } else
346 sbp->sb_signer.l = strlen(sbp->sb_signer.s = from);
349 if ((smtp = ok_vlook(smtp)) == NULL) {
350 rv = TRU1;
351 goto jleave;
354 if (!url_parse(&sbp->sb_url, CPROTO_SMTP, smtp))
355 goto jleave;
357 if (v15) {
358 if (shost == NULL) {
359 assert(from != NULL);
360 sbp->sb_url.url_uh.l = strlen(sbp->sb_url.url_uh.s = from);
362 if (!ccred_lookup(&sbp->sb_ccred, &sbp->sb_url))
363 goto jleave;
364 } else {
365 if (sbp->sb_url.url_had_user || sbp->sb_url.url_pass.s != NULL) {
366 fprintf(stderr, "New-style URL used without *v15-compat* being set\n");
367 goto jleave;
369 if (!ccred_lookup_old(&sbp->sb_ccred, CPROTO_SMTP, smtp))
370 goto jleave;
371 assert(from != NULL);
372 sbp->sb_url.url_uh.l = strlen(sbp->sb_url.url_uh.s = from);
375 rv = TRU1;
376 jleave:
377 NYD_LEAVE;
378 return rv;
381 static char const **
382 _prepare_mta_args(struct name *to, struct header *hp)
384 size_t j, i;
385 char const **args;
386 NYD_ENTER;
388 i = 4 + smopts_count + 2 + count(to) + 1;
389 args = salloc(i * sizeof(char*));
391 args[0] = ok_vlook(sendmail_progname);
392 if (args[0] == NULL || *args[0] == '\0')
393 args[0] = SENDMAIL_PROGNAME;
395 args[1] = "-i";
396 i = 2;
397 if (ok_blook(metoo))
398 args[i++] = "-m";
399 if (options & OPT_VERBOSE)
400 args[i++] = "-v";
402 for (j = 0; j < smopts_count; ++j, ++i)
403 args[i] = smopts[j];
405 /* -r option? We may only pass skinned addresses */
406 if (options & OPT_r_FLAG) {
407 char const *froma;
409 if (option_r_arg[0] != '\0')
410 froma = option_r_arg;
411 else if (hp != NULL) {
412 /* puthead() did it, then */
413 assert(hp->h_from != NULL);
414 froma = hp->h_from->n_name;
415 } else
416 froma = skin(myorigin(NULL)); /* XXX ugh! ugh!! */
417 if (froma != NULL) { /* XXX ugh! */
418 args[i++] = "-r";
419 args[i++] = froma;
423 /* Receivers follow */
424 for (; to != NULL; to = to->n_flink)
425 if (!(to->n_type & GDEL))
426 args[i++] = to->n_name;
427 args[i] = NULL;
428 NYD_LEAVE;
429 return args;
432 static struct name *
433 fixhead(struct header *hp, struct name *tolist)
435 struct name **npp, *np;
436 NYD_ENTER;
438 tolist = elide(tolist);
440 hp->h_to = hp->h_cc = hp->h_bcc = NULL;
441 for (np = tolist; np != NULL; np = np->n_flink) {
442 switch (np->n_type & (GDEL | GMASK)) {
443 case GTO: npp = &hp->h_to; break;
444 case GCC: npp = &hp->h_cc; break;
445 case GBCC: npp = &hp->h_bcc; break;
446 default: continue;
448 *npp = cat(*npp, ndup(np, np->n_type | GFULL));
450 NYD_LEAVE;
451 return tolist;
454 static int
455 put_signature(FILE *fo, int convert)
457 char buf[INFIX_BUF], *sig, c = '\n';
458 FILE *fsig;
459 size_t sz;
460 int rv;
461 NYD_ENTER;
463 if ((sig = ok_vlook(signature)) == NULL || *sig == '\0') {
464 rv = 0;
465 goto jleave;
467 rv = -1;
469 if ((sig = file_expand(sig)) == NULL)
470 goto jleave;
472 if ((fsig = Fopen(sig, "r")) == NULL) {
473 perror(sig);
474 goto jleave;
476 while ((sz = fread(buf, sizeof *buf, INFIX_BUF, fsig)) != 0) {
477 c = buf[sz - 1];
478 if (xmime_write(buf, sz, fo, convert, TD_NONE, NULL) < 0)
479 goto jerr;
481 if (ferror(fsig)) {
482 jerr:
483 perror(sig);
484 Fclose(fsig);
485 goto jleave;
487 Fclose(fsig);
488 if (c != '\n')
489 putc('\n', fo);
491 rv = 0;
492 jleave:
493 NYD_LEAVE;
494 return rv;
497 static int
498 attach_message(struct attachment *ap, FILE *fo)
500 struct message *mp;
501 char const *ccp;
502 int rv;
503 NYD_ENTER;
505 fprintf(fo, "\n--%s\nContent-Type: message/rfc822\n"
506 "Content-Disposition: inline\n", _sendout_boundary);
507 if ((ccp = ap->a_content_description) != NULL)
508 fprintf(fo, "Content-Description: %s\n", ccp);
509 fputc('\n', fo);
511 mp = message + ap->a_msgno - 1;
512 touch(mp);
513 rv = (sendmp(mp, fo, 0, NULL, SEND_RFC822, NULL) < 0) ? -1 : 0;
514 NYD_LEAVE;
515 return rv;
518 static int
519 make_multipart(struct header *hp, int convert, FILE *fi, FILE *fo,
520 char const *contenttype, char const *charset)
522 struct attachment *att;
523 int rv = -1;
524 NYD_ENTER;
526 fputs("This is a multi-part message in MIME format.\n", fo);
527 if (fsize(fi) != 0) {
528 char *buf;
529 size_t sz, bufsize, cnt;
531 fprintf(fo, "\n--%s\n", _sendout_boundary);
532 fprintf(fo, "Content-Type: %s", contenttype);
533 if (charset != NULL)
534 fprintf(fo, "; charset=%s", charset);
535 fprintf(fo, "\nContent-Transfer-Encoding: %s\n"
536 "Content-Disposition: inline\n\n", _get_encoding(convert));
538 buf = smalloc(bufsize = INFIX_BUF);
539 if (convert == CONV_TOQP
540 #ifdef HAVE_ICONV
541 || iconvd != (iconv_t)-1
542 #endif
544 fflush(fi);
545 cnt = fsize(fi);
547 for (;;) {
548 if (convert == CONV_TOQP
549 #ifdef HAVE_ICONV
550 || iconvd != (iconv_t)-1
551 #endif
553 if (fgetline(&buf, &bufsize, &cnt, &sz, fi, 0) == NULL)
554 break;
555 } else if ((sz = fread(buf, sizeof *buf, bufsize, fi)) == 0)
556 break;
558 if (xmime_write(buf, sz, fo, convert, TD_ICONV, NULL) < 0) {
559 free(buf);
560 goto jleave;
563 free(buf);
565 if (ferror(fi))
566 goto jleave;
567 if (charset != NULL)
568 put_signature(fo, convert);
571 for (att = hp->h_attach; att != NULL; att = att->a_flink) {
572 if (att->a_msgno) {
573 if (attach_message(att, fo) != 0)
574 goto jleave;
575 } else if (_attach_file(att, fo) != 0)
576 goto jleave;
579 /* the final boundary with two attached dashes */
580 fprintf(fo, "\n--%s--\n", _sendout_boundary);
581 rv = 0;
582 jleave:
583 NYD_LEAVE;
584 return rv;
587 static FILE *
588 infix(struct header *hp, FILE *fi) /* TODO check */
590 FILE *nfo, *nfi = NULL;
591 char *tempMail;
592 char const *contenttype, *charset = NULL;
593 enum conversion convert;
594 int do_iconv = 0, err;
595 #ifdef HAVE_ICONV
596 char const *tcs, *convhdr = NULL;
597 #endif
598 NYD_ENTER;
600 if ((nfo = Ftmp(&tempMail, "infix", OF_WRONLY | OF_HOLDSIGS | OF_REGISTER,
601 0600)) == NULL) {
602 perror(tr(178, "temporary mail file"));
603 goto jleave;
605 if ((nfi = Fopen(tempMail, "r")) == NULL) {
606 perror(tempMail);
607 Fclose(nfo);
609 Ftmp_release(&tempMail);
610 if (nfi == NULL)
611 goto jleave;
613 contenttype = "text/plain"; /* XXX mail body - always text/plain, want XX? */
614 convert = mime_classify_file(fi, &contenttype, &charset, &do_iconv);
616 #ifdef HAVE_ICONV
617 tcs = charset_get_lc();
618 if ((convhdr = need_hdrconv(hp, GTO | GSUBJECT | GCC | GBCC | GIDENT))) {
619 if (iconvd != (iconv_t)-1) /* XXX */
620 n_iconv_close(iconvd);
621 if (asccasecmp(convhdr, tcs) != 0 &&
622 (iconvd = n_iconv_open(convhdr, tcs)) == (iconv_t)-1 &&
623 (err = errno) != 0)
624 goto jiconv_err;
626 #endif
627 if (puthead(hp, nfo,
628 (GTO | GSUBJECT | GCC | GBCC | GNL | GCOMMA | GUA | GMIME | GMSGID |
629 GIDENT | GREF | GDATE), SEND_MBOX, convert, contenttype, charset))
630 goto jerr;
631 #ifdef HAVE_ICONV
632 if (iconvd != (iconv_t)-1)
633 n_iconv_close(iconvd);
634 #endif
636 #ifdef HAVE_ICONV
637 if (do_iconv && charset != NULL) { /*TODO charset->mime_classify_file*/
638 if (asccasecmp(charset, tcs) != 0 &&
639 (iconvd = n_iconv_open(charset, tcs)) == (iconv_t)-1 &&
640 (err = errno) != 0) {
641 jiconv_err:
642 if (err == EINVAL)
643 fprintf(stderr, tr(179, "Cannot convert from %s to %s\n"),
644 tcs, charset);
645 else
646 perror("iconv_open");
647 goto jerr;
650 #endif
652 if (hp->h_attach != NULL) {
653 if (make_multipart(hp, convert, fi, nfo, contenttype, charset) != 0)
654 goto jerr;
655 } else {
656 size_t sz, bufsize, cnt;
657 char *buf;
659 if (convert == CONV_TOQP
660 #ifdef HAVE_ICONV
661 || iconvd != (iconv_t)-1
662 #endif
664 fflush(fi);
665 cnt = fsize(fi);
667 buf = smalloc(bufsize = INFIX_BUF);
668 for (err = 0;;) {
669 if (convert == CONV_TOQP
670 #ifdef HAVE_ICONV
671 || iconvd != (iconv_t)-1
672 #endif
674 if (fgetline(&buf, &bufsize, &cnt, &sz, fi, 0) == NULL)
675 break;
676 } else if ((sz = fread(buf, sizeof *buf, bufsize, fi)) == 0)
677 break;
678 if (xmime_write(buf, sz, nfo, convert, TD_ICONV, NULL) < 0) {
679 err = 1;
680 break;
683 free(buf);
685 if (err || ferror(fi)) {
686 jerr:
687 Fclose(nfo);
688 Fclose(nfi);
689 #ifdef HAVE_ICONV
690 if (iconvd != (iconv_t)-1)
691 n_iconv_close(iconvd);
692 #endif
693 nfi = NULL;
694 goto jleave;
696 if (charset != NULL)
697 put_signature(nfo, convert); /* XXX if (text/) !! */
700 #ifdef HAVE_ICONV
701 if (iconvd != (iconv_t)-1)
702 n_iconv_close(iconvd);
703 #endif
705 fflush(nfo);
706 if ((err = ferror(nfo)))
707 perror(tr(180, "temporary mail file"));
708 Fclose(nfo);
709 if (!err) {
710 fflush_rewind(nfi);
711 Fclose(fi);
712 } else {
713 Fclose(nfi);
714 nfi = NULL;
716 jleave:
717 NYD_LEAVE;
718 return nfi;
721 static int
722 savemail(char const *name, FILE *fi)
724 FILE *fo;
725 char *buf;
726 size_t bufsize, buflen, cnt;
727 int prependnl = 0, rv = -1;
728 NYD_ENTER;
730 buf = smalloc(bufsize = LINESIZE);
732 if ((fo = Zopen(name, "a+", NULL)) == NULL) {
733 if ((fo = Zopen(name, "wx", NULL)) == NULL) {
734 perror(name);
735 goto jleave;
737 } else {
738 if (fseek(fo, -2L, SEEK_END) == 0) {
739 switch (fread(buf, sizeof *buf, 2, fo)) {
740 case 2:
741 if (buf[1] != '\n') {
742 prependnl = 1;
743 break;
745 /* FALLTHRU */
746 case 1:
747 if (buf[0] != '\n')
748 prependnl = 1;
749 break;
750 default:
751 if (ferror(fo)) {
752 perror(name);
753 goto jleave;
756 if (prependnl) {
757 putc('\n', fo);
759 fflush(fo);
763 fprintf(fo, "From %s %s", myname, time_current.tc_ctime);
764 fflush_rewind(fi);
765 cnt = fsize(fi);
766 buflen = 0;
767 while (fgetline(&buf, &bufsize, &cnt, &buflen, fi, 0) != NULL) {
768 #ifdef HAVE_DEBUG /* TODO assert legacy */
769 assert(!is_head(buf, buflen));
770 #else
771 if (is_head(buf, buflen))
772 putc('>', fo);
773 #endif
774 fwrite(buf, sizeof *buf, buflen, fo);
776 if (buflen && *(buf + buflen - 1) != '\n')
777 putc('\n', fo);
778 putc('\n', fo);
779 fflush(fo);
781 rv = 0;
782 if (ferror(fo)) {
783 perror(name);
784 rv = -1;
786 if (Fclose(fo) != 0)
787 rv = -1;
788 fflush_rewind(fi);
789 jleave:
790 free(buf);
791 NYD_LEAVE;
792 return rv;
795 static int
796 sendmail_internal(void *v, int recipient_record)
798 struct header head;
799 char *str = v;
800 int rv;
801 NYD_ENTER;
803 memset(&head, 0, sizeof head);
804 head.h_to = lextract(str, GTO | GFULL);
805 rv = mail1(&head, 0, NULL, NULL, recipient_record, 0);
806 NYD_LEAVE;
807 return rv;
810 static bool_t
811 _transfer(struct sendbundle *sbp)
813 struct name *np;
814 ui32_t cnt;
815 bool_t rv = TRU1;
816 NYD_ENTER;
818 for (cnt = 0, np = sbp->sb_to; np != NULL;) {
819 char const k[] = "smime-encrypt-";
820 size_t nl = strlen(np->n_name);
821 char *cp, *vs = ac_alloc(sizeof(k)-1 + nl +1);
822 memcpy(vs, k, sizeof(k) -1);
823 memcpy(vs + sizeof(k) -1, np->n_name, nl +1);
825 if ((cp = vok_vlook(vs)) != NULL) {
826 #ifdef HAVE_SSL
827 FILE *ef;
829 if ((ef = smime_encrypt(sbp->sb_input, cp, np->n_name)) != NULL) {
830 FILE *fisave = sbp->sb_input;
831 struct name *nsave = sbp->sb_to;
833 sbp->sb_to = ndup(np, np->n_type & ~(GFULL | GSKIN));
834 sbp->sb_input = ef;
835 if (!start_mta(sbp))
836 rv = FAL0;
837 sbp->sb_to = nsave;
838 sbp->sb_input = fisave;
840 Fclose(ef);
841 } else {
842 #else
843 fprintf(stderr, tr(225, "No SSL support compiled in.\n"));
844 rv = FAL0;
845 #endif
846 fprintf(stderr, tr(38, "Message not sent to <%s>\n"), np->n_name);
847 _sendout_error = TRU1;
848 #ifdef HAVE_SSL
850 #endif
851 rewind(sbp->sb_input);
853 if (np->n_flink != NULL)
854 np->n_flink->n_blink = np->n_blink;
855 if (np->n_blink != NULL)
856 np->n_blink->n_flink = np->n_flink;
857 if (np == sbp->sb_to)
858 sbp->sb_to = np->n_flink;
859 np = np->n_flink;
860 } else {
861 ++cnt;
862 np = np->n_flink;
864 ac_free(vs);
867 if (cnt > 0 && (ok_blook(smime_force_encryption) || !start_mta(sbp)))
868 rv = FAL0;
869 NYD_LEAVE;
870 return rv;
873 static bool_t
874 start_mta(struct sendbundle *sbp)
876 char const **args = NULL, **t, *mta;
877 char *smtp;
878 pid_t pid;
879 sigset_t nset;
880 bool_t rv = FAL0;
881 NYD_ENTER;
883 if ((smtp = ok_vlook(smtp)) == NULL) {
884 if ((mta = ok_vlook(sendmail)) != NULL) {
885 if ((mta = file_expand(mta)) == NULL)
886 goto jstop;
887 } else
888 mta = SENDMAIL;
890 args = _prepare_mta_args(sbp->sb_to, sbp->sb_hp);
891 if (options & OPT_DEBUG) {
892 printf(tr(181, "Sendmail arguments:"));
893 for (t = args; *t != NULL; ++t)
894 printf(" \"%s\"", *t);
895 printf("\n");
896 rv = TRU1;
897 goto jleave;
899 } else {
900 mta = NULL; /* Silence cc */
901 #ifndef HAVE_SMTP
902 fputs(tr(194, "No SMTP support compiled in.\n"), stderr);
903 goto jstop;
904 #else
905 /* XXX assert that sendbundle is setup? */
906 #endif
909 /* Fork, set up the temporary mail file as standard input for "mail", and
910 * exec with the user list we generated far above */
911 if ((pid = fork()) == -1) {
912 perror("fork");
913 jstop:
914 savedeadletter(sbp->sb_input, 0);
915 _sendout_error = TRU1;
916 goto jleave;
918 if (pid == 0) {
919 sigemptyset(&nset);
920 sigaddset(&nset, SIGHUP);
921 sigaddset(&nset, SIGINT);
922 sigaddset(&nset, SIGQUIT);
923 sigaddset(&nset, SIGTSTP);
924 sigaddset(&nset, SIGTTIN);
925 sigaddset(&nset, SIGTTOU);
926 freopen("/dev/null", "r", stdin);
927 #ifdef HAVE_SMTP
928 if (smtp != NULL) {
929 prepare_child(&nset, 0, 1);
930 if (smtp_mta(sbp))
931 _exit(0);
932 } else {
933 #endif
934 prepare_child(&nset, fileno(sbp->sb_input), -1);
935 /* If *record* is set then savemail() will move the file position;
936 * it'll call rewind(), but that may optimize away the systemcall if
937 * possible, and since dup2() shares the position with the original FD
938 * the MTA may end up reading nothing */
939 lseek(0, 0, SEEK_SET);
940 execv(mta, UNCONST(args));
941 perror(mta);
942 #ifdef HAVE_SMTP
944 #endif
945 savedeadletter(sbp->sb_input, 1);
946 fputs(tr(182, "... message not sent.\n"), stderr);
947 _exit(1);
949 if ((options & (OPT_DEBUG | OPT_VERBOSE | OPT_BATCH_FLAG)) ||
950 ok_blook(sendwait)) {
951 if (wait_child(pid, NULL))
952 rv = TRU1;
953 else
954 _sendout_error = TRU1;
955 } else {
956 rv = TRU1;
957 free_child(pid);
959 jleave:
960 NYD_LEAVE;
961 return rv;
964 static bool_t
965 mightrecord(FILE *fp, struct name *to)
967 char *cp, *cq;
968 char const *ep;
969 bool_t rv = TRU1;
970 NYD_ENTER;
972 if (to != NULL) {
973 cp = savestr(skinned_name(to));
974 for (cq = cp; *cq != '\0' && *cq != '@'; ++cq)
976 *cq = '\0';
977 } else
978 cp = ok_vlook(record);
980 if (cp != NULL) {
981 if ((ep = expand(cp)) == NULL) {
982 ep = "NULL";
983 goto jbail;
986 if (*ep != '/' && *ep != '+' && ok_blook(outfolder) &&
987 which_protocol(ep) == PROTO_FILE) {
988 size_t i = strlen(cp);
989 cq = salloc(i + 1 +1);
990 cq[0] = '+';
991 memcpy(cq + 1, cp, i +1);
992 cp = cq;
993 if ((ep = file_expand(cp)) == NULL) {
994 ep = "NULL";
995 goto jbail;
999 if (savemail(ep, fp) != 0) {
1000 jbail:
1001 fprintf(stderr, tr(285, "Failed to save message in %s - "
1002 "message not sent\n"), ep);
1003 exit_status |= EXIT_ERR;
1004 savedeadletter(fp, 1);
1005 rv = FAL0;
1008 NYD_LEAVE;
1009 return rv;
1012 static void
1013 message_id(FILE *fo, struct header *hp)
1015 char const *h;
1016 size_t rl;
1017 struct tm *tmp;
1018 NYD_ENTER;
1020 if (ok_blook(message_id_disable))
1021 goto jleave;
1023 if ((h = ok_vlook(hostname)) != NULL)
1024 rl = 24;
1025 else if ((h = skin(myorigin(hp))) != NULL && strchr(h, '@') != NULL)
1026 rl = 16;
1027 else
1028 /* Up to MTA */
1029 goto jleave;
1031 tmp = &time_current.tc_gm;
1032 fprintf(fo, "Message-ID: <%04d%02d%02d%02d%02d%02d.%s%c%s>\n",
1033 tmp->tm_year + 1900, tmp->tm_mon + 1, tmp->tm_mday,
1034 tmp->tm_hour, tmp->tm_min, tmp->tm_sec,
1035 getrandstring(rl), (rl == 16 ? '%' : '@'), h);
1036 jleave:
1037 NYD_LEAVE;
1040 static int
1041 fmt(char const *str, struct name *np, FILE *fo, int flags, int dropinvalid,
1042 int domime)
1044 enum {
1045 m_INIT = 1<<0,
1046 m_COMMA = 1<<1,
1047 m_NOPF = 1<<2,
1048 m_CSEEN = 1<<3
1049 } m = (flags & GCOMMA) ? m_COMMA : 0;
1050 ssize_t col, len;
1051 int rv = 1;
1052 NYD_ENTER;
1054 col = strlen(str);
1055 if (col) {
1056 fwrite(str, sizeof *str, col, fo);
1057 if (flags & GFILES)
1058 goto jstep;
1059 if (col == 9 && !asccasecmp(str, "reply-to:")) {
1060 m |= m_NOPF;
1061 goto jstep;
1063 if (ok_blook(add_file_recipients))
1064 goto jstep;
1065 if ((col == 3 && (!asccasecmp(str, "to:") || !asccasecmp(str, "cc:"))) ||
1066 (col == 4 && !asccasecmp(str, "bcc:")) ||
1067 (col == 10 && !asccasecmp(str, "Resent-To:")))
1068 m |= m_NOPF;
1070 jstep:
1071 for (; np != NULL; np = np->n_flink) {
1072 if ((m & m_NOPF) && is_fileorpipe_addr(np))
1073 continue;
1074 if (is_addr_invalid(np, !dropinvalid)) {
1075 if (dropinvalid)
1076 continue;
1077 else
1078 goto jleave;
1080 if ((m & (m_INIT | m_COMMA)) == (m_INIT | m_COMMA)) {
1081 putc(',', fo);
1082 m |= m_CSEEN;
1083 ++col;
1085 len = strlen(np->n_fullname);
1086 ++col; /* The separating space */
1087 if ((m & m_INIT) && col > 1 && col + len > 72) {
1088 fputs("\n ", fo);
1089 col = 1;
1090 m &= ~m_CSEEN;
1091 } else
1092 putc(' ', fo);
1093 m = (m & ~m_CSEEN) | m_INIT;
1094 len = xmime_write(np->n_fullname, len, fo,
1095 (domime ? CONV_TOHDR_A : CONV_NONE), TD_ICONV, NULL);
1096 if (len < 0)
1097 goto jleave;
1098 col += len;
1100 putc('\n', fo);
1101 rv = 0;
1102 jleave:
1103 NYD_LEAVE;
1104 return rv;
1107 static int
1108 infix_resend(FILE *fi, FILE *fo, struct message *mp, struct name *to,
1109 int add_resent)
1111 size_t cnt, c, bufsize = 0;
1112 char *buf = NULL;
1113 char const *cp;
1114 struct name *fromfield = NULL, *senderfield = NULL;
1115 int rv = 1;
1116 NYD_ENTER;
1118 cnt = mp->m_size;
1120 /* Write the Resent-Fields */
1121 if (add_resent) {
1122 fputs("Resent-", fo);
1123 mkdate(fo, "Date");
1124 if ((cp = myaddrs(NULL)) != NULL) {
1125 if (_putname(cp, GCOMMA, SEND_MBOX, NULL, "Resent-From:", fo,
1126 &fromfield))
1127 goto jleave;
1129 if ((cp = ok_vlook(sender)) != NULL) {
1130 if (_putname(cp, GCOMMA, SEND_MBOX, NULL, "Resent-Sender:", fo,
1131 &senderfield))
1132 goto jleave;
1134 if (fmt("Resent-To:", to, fo, 1, 1, 0))
1135 goto jleave;
1136 if ((cp = ok_vlook(stealthmua)) == NULL || !strcmp(cp, "noagent")) {
1137 fputs("Resent-", fo);
1138 message_id(fo, NULL);
1141 if (check_from_and_sender(fromfield, senderfield))
1142 goto jleave;
1144 /* Write the original headers */
1145 while (cnt > 0) {
1146 if (fgetline(&buf, &bufsize, &cnt, &c, fi, 0) == NULL)
1147 break;
1148 /* XXX more checks: The From_ line may be seen when resending */
1149 /* During headers is_head() is actually overkill, so ^From_ is sufficient
1150 * && !is_head(buf, c) */
1151 if (ascncasecmp("status: ", buf, 8) && strncmp("From ", buf, 5))
1152 fwrite(buf, sizeof *buf, c, fo);
1153 if (cnt > 0 && *buf == '\n')
1154 break;
1157 /* Write the message body */
1158 while (cnt > 0) {
1159 if (fgetline(&buf, &bufsize, &cnt, &c, fi, 0) == NULL)
1160 break;
1161 if (cnt == 0 && *buf == '\n')
1162 break;
1163 fwrite(buf, sizeof *buf, c, fo);
1165 if (buf != NULL)
1166 free(buf);
1167 if (ferror(fo)) {
1168 perror(tr(188, "temporary mail file"));
1169 goto jleave;
1171 rv = 0;
1172 jleave:
1173 NYD_LEAVE;
1174 return rv;
1177 FL int
1178 mail(struct name *to, struct name *cc, struct name *bcc, char *subject,
1179 struct attachment *attach, char *quotefile, int recipient_record)
1181 struct header head;
1182 struct str in, out;
1183 NYD_ENTER;
1185 memset(&head, 0, sizeof head);
1187 /* The given subject may be in RFC1522 format. */
1188 if (subject != NULL) {
1189 in.s = subject;
1190 in.l = strlen(subject);
1191 mime_fromhdr(&in, &out, /* TODO ??? TD_ISPR |*/ TD_ICONV);
1192 head.h_subject = out.s;
1194 if (!(options & OPT_t_FLAG)) {
1195 head.h_to = to;
1196 head.h_cc = cc;
1197 head.h_bcc = bcc;
1199 head.h_attach = attach;
1201 mail1(&head, 0, NULL, quotefile, recipient_record, 0);
1203 if (subject != NULL)
1204 free(out.s);
1205 NYD_LEAVE;
1206 return 0;
1209 FL int
1210 c_sendmail(void *v)
1212 int rv;
1213 NYD_ENTER;
1215 rv = sendmail_internal(v, 0);
1216 NYD_LEAVE;
1217 return rv;
1220 FL int
1221 c_Sendmail(void *v)
1223 int rv;
1224 NYD_ENTER;
1226 rv = sendmail_internal(v, 1);
1227 NYD_LEAVE;
1228 return rv;
1231 FL enum okay
1232 mail1(struct header *hp, int printheaders, struct message *quote,
1233 char *quotefile, int recipient_record, int doprefix)
1235 struct sendbundle sb;
1236 struct name *to;
1237 FILE *mtf, *nmtf;
1238 int dosign = -1, err;
1239 char const *cp;
1240 enum okay rv = STOP;
1241 NYD_ENTER;
1243 _sendout_error = FAL0;
1245 /* Update some globals we likely need first */
1246 time_current_update(&time_current, TRU1);
1248 /* */
1249 if ((cp = ok_vlook(autocc)) != NULL && *cp != '\0')
1250 hp->h_cc = cat(hp->h_cc, checkaddrs(lextract(cp, GCC | GFULL)));
1251 if ((cp = ok_vlook(autobcc)) != NULL && *cp != '\0')
1252 hp->h_bcc = cat(hp->h_bcc, checkaddrs(lextract(cp, GBCC | GFULL)));
1254 /* Collect user's mail from standard input. Get the result as mtf */
1255 mtf = collect(hp, printheaders, quote, quotefile, doprefix);
1256 if (mtf == NULL)
1257 goto j_leave;
1259 if (options & OPT_INTERACTIVE) {
1260 err = (ok_blook(bsdcompat) || ok_blook(askatend));
1261 if (err == 0)
1262 goto jaskeot;
1263 if (ok_blook(askcc))
1264 ++err, grab_headers(hp, GCC, 1);
1265 if (ok_blook(askbcc))
1266 ++err, grab_headers(hp, GBCC, 1);
1267 if (ok_blook(askattach))
1268 ++err, edit_attachments(&hp->h_attach);
1269 if (ok_blook(asksign))
1270 ++err, dosign = getapproval(tr(35, "Sign this message (y/n)? "), TRU1);
1271 if (err == 1) {
1272 jaskeot:
1273 printf(tr(183, "EOT\n"));
1274 fflush(stdout);
1278 if (fsize(mtf) == 0) {
1279 if (options & OPT_E_FLAG)
1280 goto jleave;
1281 if (hp->h_subject == NULL)
1282 printf(tr(184, "No message, no subject; hope that's ok\n"));
1283 else if (ok_blook(bsdcompat) || ok_blook(bsdmsgs))
1284 printf(tr(185, "Null message body; hope that's ok\n"));
1287 if (dosign < 0)
1288 dosign = ok_blook(smime_sign);
1289 #ifndef HAVE_SSL
1290 if (dosign) {
1291 fprintf(stderr, tr(225, "No SSL support compiled in.\n"));
1292 goto jleave;
1294 #endif
1296 /* XXX Update time_current again; once collect() offers editing of more
1297 * XXX headers, including Date:, this must only happen if Date: is the
1298 * XXX same that it was before collect() (e.g., postponing etc.).
1299 * XXX But *do* update otherwise because the mail seems to be backdated
1300 * XXX if the user edited some time, which looks odd and it happened
1301 * XXX to me that i got mis-dated response mails due to that... */
1302 time_current_update(&time_current, TRU1);
1304 /* TODO hrmpf; the MIME/send layer rewrite MUST address the init crap:
1305 * TODO setup the header ONCE; note this affects edit.c, collect.c ...,
1306 * TODO but: offer a hook that rebuilds/expands/checks/fixates all
1307 * TODO header fields ONCE, call that ONCE after user editing etc. has
1308 * TODO completed (one edit cycle) */
1310 /* Take the user names from the combined to and cc lists and do all the
1311 * alias processing. The POSIX standard says:
1312 * The names shall be substituted when alias is used as a recipient
1313 * address specified by the user in an outgoing message (that is,
1314 * other recipients addressed indirectly through the reply command
1315 * shall not be substituted in this manner).
1316 * S-nail thus violates POSIX, as has been pointed out correctly by
1317 * Martin Neitzel, but logic and usability of POSIX standards is not seldom
1318 * disputable anyway. Go for user friendliness */
1320 /* Do alias expansion on Reply-To: members, too */
1321 /* TODO puthead() YET (!!! see ONCE note above) expands the value, but
1322 * TODO doesn't perform alias expansion; encapsulate in the ONCE-o */
1323 if (hp->h_replyto == NULL && (cp = ok_vlook(replyto)) != NULL)
1324 hp->h_replyto = checkaddrs(lextract(cp, GEXTRA | GFULL));
1325 if (hp->h_replyto != NULL)
1326 hp->h_replyto = elide(usermap(hp->h_replyto, TRU1));
1328 /* TODO what happens now is that all recipients are merged into
1329 * TODO a duplicated list with expanded aliases, then this list is
1330 * TODO splitted again into the three individual recipient lists (with
1331 * TODO duplicates removed).
1332 * TODO later on we use the merged list for outof() pipe/file saving,
1333 * TODO then we eliminate duplicates (again) and then we use that one
1334 * TODO for mightrecord() and _transfer(), and again. ... Please ... */
1336 /* NOTE: Due to elide() in fixhead(), ENSURE to,cc,bcc order of to!,
1337 * because otherwise the recipients will be "degraded" if they occur
1338 * multiple times */
1339 to = usermap(cat(hp->h_to, cat(hp->h_cc, hp->h_bcc)), FAL0);
1340 if (to == NULL) {
1341 fprintf(stderr, tr(186, "No recipients specified\n"));
1342 _sendout_error = TRU1;
1344 to = fixhead(hp, to);
1346 /* */
1347 memset(&sb, 0, sizeof sb);
1348 sb.sb_hp = hp;
1349 sb.sb_to = to;
1350 sb.sb_input = mtf;
1351 if ((dosign || count_nonlocal(to) > 0) &&
1352 !_sendbundle_setup_creds(&sb, (dosign > 0)))
1353 /* TODO saving $DEAD and recovering etc is not yet well defined */
1354 goto jfail_dead;
1356 /* 'Bit ugly kind of control flow until we find a charset that does it */
1357 for (charset_iter_reset(hp->h_charset);; charset_iter_next()) {
1358 if (!charset_iter_is_valid())
1360 else if ((nmtf = infix(hp, mtf)) != NULL)
1361 break;
1362 else if ((err = errno) == EILSEQ || err == EINVAL) {
1363 rewind(mtf);
1364 continue;
1367 perror("");
1368 jfail_dead:
1369 _sendout_error = TRU1;
1370 savedeadletter(mtf, TRU1);
1371 fputs(tr(182, "... message not sent.\n"), stderr);
1372 goto jleave;
1374 mtf = nmtf;
1376 /* */
1377 #ifdef HAVE_SSL
1378 if (dosign) {
1379 if ((nmtf = smime_sign(mtf, sb.sb_signer.s)) == NULL)
1380 goto jfail_dead;
1381 Fclose(mtf);
1382 mtf = nmtf;
1384 #endif
1386 /* TODO truly - i still don't get what follows: (1) we deliver file
1387 * TODO and pipe addressees, (2) we mightrecord() and (3) we transfer
1388 * TODO even if (1) savedeadletter() etc. To me this doesn't make sense? */
1390 /* Deliver pipe and file addressees */
1391 to = outof(to, mtf, &_sendout_error);
1392 if (_sendout_error)
1393 savedeadletter(mtf, FAL0);
1395 to = elide(to); /* XXX needed only to drop GDELs due to outof()! */
1396 { ui32_t cnt = count(to);
1397 if ((!recipient_record || cnt > 0) &&
1398 !mightrecord(mtf, (recipient_record ? to : NULL)))
1399 goto jleave;
1400 if (cnt > 0) {
1401 sb.sb_hp = hp;
1402 sb.sb_to = to;
1403 sb.sb_input = mtf;
1404 if (_transfer(&sb))
1405 rv = OKAY;
1406 } else if (!_sendout_error)
1407 rv = OKAY;
1409 jleave:
1410 Fclose(mtf);
1411 j_leave:
1412 if (_sendout_error)
1413 exit_status |= EXIT_SEND_ERROR;
1414 NYD_LEAVE;
1415 return rv;
1418 FL int
1419 mkdate(FILE *fo, char const *field)
1421 struct tm *tmptr;
1422 int tzdiff, tzdiff_hour, tzdiff_min, rv;
1423 NYD_ENTER;
1425 tzdiff = time_current.tc_time - mktime(&time_current.tc_gm);
1426 tzdiff_hour = (int)(tzdiff / 60);
1427 tzdiff_min = tzdiff_hour % 60;
1428 tzdiff_hour /= 60;
1429 tmptr = &time_current.tc_local;
1430 if (tmptr->tm_isdst > 0)
1431 ++tzdiff_hour;
1432 rv = fprintf(fo, "%s: %s, %02d %s %04d %02d:%02d:%02d %+05d\n",
1433 field,
1434 weekday_names[tmptr->tm_wday],
1435 tmptr->tm_mday, month_names[tmptr->tm_mon],
1436 tmptr->tm_year + 1900, tmptr->tm_hour,
1437 tmptr->tm_min, tmptr->tm_sec,
1438 tzdiff_hour * 100 + tzdiff_min);
1439 NYD_LEAVE;
1440 return rv;
1443 FL int
1444 puthead(struct header *hp, FILE *fo, enum gfield w, enum sendaction action,
1445 enum conversion convert, char const *contenttype, char const *charset)
1447 #define FMT_CC_AND_BCC() \
1448 do {\
1449 if (hp->h_cc != NULL && (w & GCC)) {\
1450 if (fmt("Cc:", hp->h_cc, fo, (w & (GCOMMA | GFILES)), 0,\
1451 (action != SEND_TODISP)))\
1452 goto jleave;\
1453 ++gotcha;\
1455 if (hp->h_bcc != NULL && (w & GBCC)) {\
1456 if (fmt("Bcc:", hp->h_bcc, fo, (w & (GCOMMA | GFILES)), 0,\
1457 (action != SEND_TODISP)))\
1458 goto jleave;\
1459 ++gotcha;\
1461 } while (0)
1463 char const *addr;
1464 size_t gotcha, l;
1465 struct name *np, *fromfield = NULL, *senderfield = NULL;
1466 int stealthmua, rv = 1;
1467 bool_t nodisp;
1468 NYD_ENTER;
1470 if ((addr = ok_vlook(stealthmua)) != NULL)
1471 stealthmua = !strcmp(addr, "noagent") ? -1 : 1;
1472 else
1473 stealthmua = 0;
1474 gotcha = 0;
1475 nodisp = (action != SEND_TODISP);
1477 if (w & GDATE)
1478 mkdate(fo, "Date"), ++gotcha;
1479 if (w & GIDENT) {
1480 if (hp->h_from != NULL) {
1481 if (fmt("From:", hp->h_from, fo, (w & (GCOMMA | GFILES)), 0, nodisp))
1482 goto jleave;
1483 ++gotcha;
1484 fromfield = hp->h_from;
1485 } else if ((addr = myaddrs(hp)) != NULL) {
1486 if (_putname(addr, w, action, &gotcha, "From:", fo, &fromfield))
1487 goto jleave;
1488 hp->h_from = fromfield;
1491 if (((addr = hp->h_organization) != NULL ||
1492 (addr = ok_vlook(ORGANIZATION)) != NULL) &&
1493 (l = strlen(addr)) > 0) {
1494 fwrite("Organization: ", sizeof(char), 14, fo);
1495 if (xmime_write(addr, l, fo, (!nodisp ? CONV_NONE : CONV_TOHDR),
1496 (!nodisp ? TD_ISPR | TD_ICONV : TD_ICONV), NULL) < 0)
1497 goto jleave;
1498 ++gotcha;
1499 putc('\n', fo);
1502 /* TODO see the ONCE TODO note somewhere around this file;
1503 * TODO but anyway, do NOT perform alias expansion UNLESS
1504 * TODO we are actually sending out! */
1505 if (hp->h_replyto != NULL) {
1506 if (fmt("Reply-To:", hp->h_replyto, fo, w & GCOMMA, 0, nodisp))
1507 goto jleave;
1508 ++gotcha;
1509 } else if ((addr = ok_vlook(replyto)) != NULL)
1510 if (_putname(addr, w, action, &gotcha, "Reply-To:", fo, NULL))
1511 goto jleave;
1513 if (hp->h_sender != NULL) {
1514 if (fmt("Sender:", hp->h_sender, fo, w & GCOMMA, 0, nodisp))
1515 goto jleave;
1516 ++gotcha;
1517 senderfield = hp->h_sender;
1518 } else if ((addr = ok_vlook(sender)) != NULL)
1519 if (_putname(addr, w, action, &gotcha, "Sender:", fo, &senderfield))
1520 goto jleave;
1522 if (check_from_and_sender(fromfield, senderfield))
1523 goto jleave;
1526 if (hp->h_to != NULL && w & GTO) {
1527 if (fmt("To:", hp->h_to, fo, (w & (GCOMMA | GFILES)), 0, nodisp))
1528 goto jleave;
1529 ++gotcha;
1532 if (!ok_blook(bsdcompat) && !ok_blook(bsdorder))
1533 FMT_CC_AND_BCC();
1535 if (hp->h_subject != NULL && (w & GSUBJECT)) {
1536 fwrite("Subject: ", sizeof (char), 9, fo);
1537 if (!ascncasecmp(hp->h_subject, "re: ", 4)) {/* TODO localizable */
1538 fwrite("Re: ", sizeof(char), 4, fo);
1539 if (strlen(hp->h_subject + 4) > 0 &&
1540 xmime_write(hp->h_subject + 4, strlen(hp->h_subject + 4), fo,
1541 (!nodisp ? CONV_NONE : CONV_TOHDR),
1542 (!nodisp ? TD_ISPR | TD_ICONV : TD_ICONV), NULL) < 0)
1543 goto jleave;
1544 } else if (*hp->h_subject != '\0') {
1545 if (xmime_write(hp->h_subject, strlen(hp->h_subject), fo,
1546 (!nodisp ? CONV_NONE : CONV_TOHDR),
1547 (!nodisp ? TD_ISPR | TD_ICONV : TD_ICONV), NULL) < 0)
1548 goto jleave;
1550 ++gotcha;
1551 fwrite("\n", sizeof (char), 1, fo);
1554 if (ok_blook(bsdcompat) || ok_blook(bsdorder))
1555 FMT_CC_AND_BCC();
1557 if ((w & GMSGID) && stealthmua <= 0)
1558 message_id(fo, hp), ++gotcha;
1560 if ((np = hp->h_ref) != NULL && (w & GREF)) {
1561 fmt("References:", np, fo, 0, 1, 0);
1562 if (np->n_name != NULL) {
1563 while (np->n_flink != NULL)
1564 np = np->n_flink;
1565 if (!is_addr_invalid(np, 0)) {
1566 fprintf(fo, "In-Reply-To: %s\n", np->n_name);
1567 ++gotcha;
1572 if ((w & GUA) && stealthmua == 0)
1573 fprintf(fo, "User-Agent: %s %s\n", uagent, version), ++gotcha;
1575 if (w & GMIME) {
1576 fputs("MIME-Version: 1.0\n", fo), ++gotcha;
1577 if (hp->h_attach != NULL) {
1578 _sendout_boundary = mime_create_boundary();/*TODO carrier*/
1579 fprintf(fo, "Content-Type: multipart/mixed;\n boundary=\"%s\"\n",
1580 _sendout_boundary);
1581 } else {
1582 fprintf(fo, "Content-Type: %s", contenttype);
1583 if (charset != NULL)
1584 fprintf(fo, "; charset=%s", charset);
1585 fprintf(fo, "\nContent-Transfer-Encoding: %s\n",
1586 _get_encoding(convert));
1590 if (gotcha && (w & GNL))
1591 putc('\n', fo);
1592 rv = 0;
1593 jleave:
1594 NYD_LEAVE;
1595 return rv;
1596 #undef FMT_CC_AND_BCC
1599 FL enum okay
1600 resend_msg(struct message *mp, struct name *to, int add_resent) /* TODO check */
1602 struct sendbundle sb;
1603 FILE *ibuf, *nfo, *nfi;
1604 char *tempMail;
1605 enum okay rv = STOP;
1606 NYD_ENTER;
1608 _sendout_error = FAL0;
1610 /* Update some globals we likely need first */
1611 time_current_update(&time_current, TRU1);
1613 if ((to = checkaddrs(to)) == NULL) {
1614 _sendout_error = TRU1;
1615 goto jleave;
1618 if ((nfo = Ftmp(&tempMail, "resend", OF_WRONLY | OF_HOLDSIGS | OF_REGISTER,
1619 0600)) == NULL) {
1620 _sendout_error = TRU1;
1621 perror(tr(189, "temporary mail file"));
1622 goto jleave;
1624 if ((nfi = Fopen(tempMail, "r")) == NULL) {
1625 _sendout_error = TRU1;
1626 perror(tempMail);
1628 Ftmp_release(&tempMail);
1629 if (nfi == NULL)
1630 goto jerr_o;
1632 if ((ibuf = setinput(&mb, mp, NEED_BODY)) == NULL)
1633 goto jerr_all;
1635 memset(&sb, 0, sizeof sb);
1636 sb.sb_to = to;
1637 sb.sb_input = nfi;
1638 if (count_nonlocal(to) > 0 && !_sendbundle_setup_creds(&sb, FAL0))
1639 /* TODO saving $DEAD and recovering etc is not yet well defined */
1640 goto jerr_all;
1642 if (infix_resend(ibuf, nfo, mp, to, add_resent) != 0) {
1643 savedeadletter(nfi, TRU1);
1644 fputs(tr(182, "... message not sent.\n"), stderr);
1645 jerr_all:
1646 Fclose(nfi);
1647 jerr_o:
1648 Fclose(nfo);
1649 _sendout_error = TRU1;
1650 goto jleave;
1652 Fclose(nfo);
1653 rewind(nfi);
1655 to = outof(to, nfi, &_sendout_error);
1656 if (_sendout_error)
1657 savedeadletter(nfi, FAL0);
1659 to = elide(to); /* TODO should have been done in fixhead()? */
1660 if (count(to) != 0) {
1661 if (!ok_blook(record_resent) || mightrecord(nfi, to)) {
1662 sb.sb_to = to;
1663 /*sb.sb_input = nfi;*/
1664 if (_transfer(&sb))
1665 rv = OKAY;
1667 } else if (!_sendout_error)
1668 rv = OKAY;
1670 Fclose(nfi);
1671 jleave:
1672 if (_sendout_error)
1673 exit_status |= EXIT_SEND_ERROR;
1674 NYD_LEAVE;
1675 return rv;
1678 /* vim:set fenc=utf-8:s-it-mode */