cmd_tab.h: correct argument requirements for `mimetypes'
[s-mailx.git] / sendout.c
blob82b006081a2d16215d65ad011f255d3eaf1021e2
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 /* Save the outgoing mail on the passed file */
90 static int savemail(char const *name, FILE *fi);
92 /* Send mail to a bunch of user names. The interface is through mail() */
93 static int sendmail_internal(void *v, int recipient_record);
95 /* */
96 static bool_t _transfer(struct sendbundle *sbp);
98 /* Start the MTA mailing */
99 static bool_t start_mta(struct sendbundle *sbp);
101 /* Record outgoing mail if instructed to do so; in *record* unless to is set */
102 static bool_t mightrecord(FILE *fp, struct name *to);
104 /* Create a Message-Id: header field. Use either host name or from address */
105 static void _message_id(FILE *fo, struct header *hp);
107 /* Format the given header line to not exceed 72 characters */
108 static int fmt(char const *str, struct name *np, FILE *fo, int comma,
109 int dropinvalid, int domime);
111 /* Rewrite a message for resending, adding the Resent-Headers */
112 static int infix_resend(FILE *fi, FILE *fo, struct message *mp,
113 struct name *to, int add_resent);
115 static enum okay
116 _putname(char const *line, enum gfield w, enum sendaction action,
117 size_t *gotcha, char const *prefix, FILE *fo, struct name **xp)
119 struct name *np;
120 enum okay rv = STOP;
121 NYD_ENTER;
123 np = lextract(line, GEXTRA | GFULL);
124 if (xp != NULL)
125 *xp = np;
126 if (np == NULL)
128 else if (fmt(prefix, np, fo, w & GCOMMA, 0, (action != SEND_TODISP)))
129 rv = OKAY;
130 else if (gotcha != NULL)
131 ++(*gotcha);
132 NYD_LEAVE;
133 return rv;
136 static char const *
137 _get_encoding(enum conversion const convert)
139 char const *rv;
140 NYD_ENTER;
142 switch (convert) {
143 case CONV_7BIT: rv = "7bit"; break;
144 case CONV_8BIT: rv = "8bit"; break;
145 case CONV_TOQP: rv = "quoted-printable"; break;
146 case CONV_TOB64: rv = "base64"; break;
147 default: rv = NULL; break;
149 NYD_LEAVE;
150 return rv;
153 static int
154 _attach_file(struct attachment *ap, FILE *fo)
156 /* TODO of course, the MIME classification needs to performed once
157 * TODO only, not for each and every charset anew ... ;-// */
158 char *charset_iter_orig[2];
159 long offs;
160 int err = 0;
161 NYD_ENTER;
163 /* Is this already in target charset? Simply copy over */
164 if (ap->a_conv == AC_TMPFILE) {
165 err = __attach_file(ap, fo);
166 Fclose(ap->a_tmpf);
167 DBG( ap->a_tmpf = NULL; )
168 goto jleave;
171 /* If we don't apply charset conversion at all (fixed input=ouput charset)
172 * we also simply copy over, since it's the users desire */
173 if (ap->a_conv == AC_FIX_INCS) {
174 ap->a_charset = ap->a_input_charset;
175 err = __attach_file(ap, fo);
176 goto jleave;
179 /* Otherwise we need to iterate over all possible output charsets */
180 if ((offs = ftell(fo)) == -1) {
181 err = EIO;
182 goto jleave;
184 charset_iter_recurse(charset_iter_orig);
185 for (charset_iter_reset(NULL);; charset_iter_next()) {
186 if (!charset_iter_is_valid()) {
187 err = EILSEQ;
188 break;
190 err = __attach_file(ap, fo);
191 if (err == 0 || (err != EILSEQ && err != EINVAL))
192 break;
193 clearerr(fo);
194 if (fseek(fo, offs, SEEK_SET) == -1) {
195 err = EIO;
196 break;
198 if (ap->a_conv != AC_DEFAULT) {
199 err = EILSEQ;
200 break;
202 ap->a_charset = NULL;
204 charset_iter_restore(charset_iter_orig);
205 jleave:
206 NYD_LEAVE;
207 return err;
210 static int
211 __attach_file(struct attachment *ap, FILE *fo) /* XXX linelength */
213 int err = 0, do_iconv;
214 FILE *fi;
215 char const *charset;
216 enum conversion convert;
217 char *buf;
218 size_t bufsize, lncnt, inlen;
219 NYD_ENTER;
221 /* Either charset-converted temporary file, or plain path */
222 if (ap->a_conv == AC_TMPFILE) {
223 fi = ap->a_tmpf;
224 assert(ftell(fi) == 0);
225 } else if ((fi = Fopen(ap->a_name, "r")) == NULL) {
226 err = errno;
227 perror(ap->a_name);
228 goto jleave;
231 /* MIME part header for attachment */
232 { char const *bn = ap->a_name, *ct;
234 if ((ct = strrchr(bn, '/')) != NULL)
235 bn = ++ct;
236 ct = ap->a_content_type;
237 charset = ap->a_charset;
238 convert = mime_classify_file(fi, (char const**)&ct, &charset, &do_iconv);
239 if (charset == NULL || ap->a_conv == AC_FIX_INCS ||
240 ap->a_conv == AC_TMPFILE)
241 do_iconv = 0;
243 if (fprintf(fo, "\n--%s\nContent-Type: %s", _sendout_boundary, ct) == -1)
244 goto jerr_header;
246 if (charset == NULL) {
247 if (putc('\n', fo) == EOF)
248 goto jerr_header;
249 } else if (fprintf(fo, "; charset=%s\n", charset) == -1)
250 goto jerr_header;
252 if (fprintf(fo, "Content-Transfer-Encoding: %s\n"
253 "Content-Disposition: %s;\n filename=\"",
254 _get_encoding(convert), ap->a_content_disposition) == -1)
255 goto jerr_header;
256 if (xmime_write(bn, strlen(bn), fo, CONV_TOHDR, TD_NONE, NULL) < 0)
257 goto jerr_header;
258 if (fwrite("\"\n", sizeof(char), 2, fo) != 2 * sizeof(char))
259 goto jerr_header;
261 if ((bn = ap->a_content_id) != NULL &&
262 fprintf(fo, "Content-ID: %s\n", bn) == -1)
263 goto jerr_header;
265 if ((bn = ap->a_content_description) != NULL &&
266 fprintf(fo, "Content-Description: %s\n", bn) == -1)
267 goto jerr_header;
269 if (putc('\n', fo) == EOF) {
270 jerr_header:
271 err = errno;
272 goto jerr_fclose;
276 #ifdef HAVE_ICONV
277 if (iconvd != (iconv_t)-1)
278 n_iconv_close(iconvd);
279 if (do_iconv) {
280 char const *tcs = charset_get_lc();
281 if (asccasecmp(charset, tcs) &&
282 (iconvd = n_iconv_open(charset, tcs)) == (iconv_t)-1 &&
283 (err = errno) != 0) {
284 if (err == EINVAL)
285 fprintf(stderr, tr(179, "Cannot convert from %s to %s\n"),
286 tcs, charset);
287 else
288 perror("iconv_open");
289 goto jerr_fclose;
292 #endif
294 bufsize = SEND_LINESIZE;
295 buf = smalloc(bufsize);
296 if (convert == CONV_TOQP
297 #ifdef HAVE_ICONV
298 || iconvd != (iconv_t)-1
299 #endif
301 lncnt = fsize(fi);
302 for (;;) {
303 if (convert == CONV_TOQP
304 #ifdef HAVE_ICONV
305 || iconvd != (iconv_t)-1
306 #endif
308 if (fgetline(&buf, &bufsize, &lncnt, &inlen, fi, 0) == NULL)
309 break;
310 } else if ((inlen = fread(buf, sizeof *buf, bufsize, fi)) == 0)
311 break;
312 if (xmime_write(buf, inlen, fo, convert, TD_ICONV, NULL) < 0) {
313 err = errno;
314 goto jerr;
317 if (ferror(fi))
318 err = EDOM;
319 jerr:
320 free(buf);
321 jerr_fclose:
322 if (ap->a_conv != AC_TMPFILE)
323 Fclose(fi);
324 jleave:
325 NYD_LEAVE;
326 return err;
329 static bool_t
330 _sendbundle_setup_creds(struct sendbundle *sbp, bool_t signing_caps)
332 bool_t v15, rv = FAL0;
333 char *shost, *from, *smtp;
334 NYD_ENTER;
336 v15 = ok_blook(v15_compat);
337 shost = (v15 ? ok_vlook(smtp_hostname) : NULL);
338 from = ((signing_caps || !v15 || shost == NULL)
339 ? skin(myorigin(sbp->sb_hp)) : NULL);
341 if (signing_caps) {
342 if (from == NULL) {
343 #ifdef HAVE_SSL
344 fprintf(stderr, tr(531, "No *from* address for signing specified\n"));
345 goto jleave;
346 #endif
347 } else
348 sbp->sb_signer.l = strlen(sbp->sb_signer.s = from);
351 if ((smtp = ok_vlook(smtp)) == NULL) {
352 rv = TRU1;
353 goto jleave;
356 if (!url_parse(&sbp->sb_url, CPROTO_SMTP, smtp))
357 goto jleave;
359 if (v15) {
360 if (shost == NULL) {
361 assert(from != NULL);
362 sbp->sb_url.url_u_h.l = strlen(sbp->sb_url.url_u_h.s = from);
364 else
365 __sendout_ident = sbp->sb_url.url_u_h.s;
366 if (!ccred_lookup(&sbp->sb_ccred, &sbp->sb_url))
367 goto jleave;
368 } else {
369 if (sbp->sb_url.url_had_user || sbp->sb_url.url_pass.s != NULL) {
370 fprintf(stderr, "New-style URL used without *v15-compat* being set\n");
371 goto jleave;
373 assert(from != NULL);
374 if (!ccred_lookup_old(&sbp->sb_ccred, CPROTO_SMTP, from))
375 goto jleave;
376 sbp->sb_url.url_u_h.l = strlen(sbp->sb_url.url_u_h.s = from);
379 rv = TRU1;
380 jleave:
381 NYD_LEAVE;
382 return rv;
385 static char const **
386 _prepare_mta_args(struct name *to, struct header *hp)
388 size_t vas_count, i, j;
389 char **vas, *cp;
390 char const **args;
391 NYD_ENTER;
393 if ((cp = ok_vlook(sendmail_arguments)) == NULL) {
394 vas_count = 0;
395 vas = NULL;
396 } else {
397 j = strlen(cp);
398 vas = ac_alloc(sizeof(*vas) * (j >> 1));
399 vas_count = (size_t)getrawlist(cp, j, vas, (int)(j >> 1), TRU1);
402 i = 4 + smopts_count + vas_count + 2 + count(to) + 1;
403 args = salloc(i * sizeof(char*));
405 args[0] = ok_vlook(sendmail_progname);
406 if (args[0] == NULL || *args[0] == '\0')
407 args[0] = SENDMAIL_PROGNAME;
409 args[1] = "-i";
410 i = 2;
411 if (ok_blook(metoo))
412 args[i++] = "-m";
413 if (options & OPT_VERB)
414 args[i++] = "-v";
416 for (j = 0; j < smopts_count; ++j, ++i)
417 args[i] = smopts[j];
419 for (j = 0; j < vas_count; ++j, ++i)
420 args[i] = vas[j];
422 /* -r option? We may only pass skinned addresses */
423 if (options & OPT_r_FLAG) {
424 if (option_r_arg[0] != '\0')
425 cp = option_r_arg;
426 else if (hp != NULL) {
427 /* puthead() did it, then */
428 assert(hp->h_from != NULL);
429 cp = hp->h_from->n_name;
430 } else
431 cp = skin(myorigin(NULL)); /* XXX ugh! ugh!! */
432 if (cp != NULL) { /* XXX ugh! */
433 args[i++] = "-f";
434 args[i++] = cp;
438 /* Receivers follow */
439 for (; to != NULL; to = to->n_flink)
440 if (!(to->n_type & GDEL))
441 args[i++] = to->n_name;
442 args[i] = NULL;
444 if (vas != NULL)
445 ac_free(vas);
446 NYD_LEAVE;
447 return args;
450 static struct name *
451 fixhead(struct header *hp, struct name *tolist)
453 struct name **npp, *np;
454 NYD_ENTER;
456 tolist = elide(tolist);
458 hp->h_to = hp->h_cc = hp->h_bcc = NULL;
459 for (np = tolist; np != NULL; np = np->n_flink) {
460 switch (np->n_type & (GDEL | GMASK)) {
461 case GTO: npp = &hp->h_to; break;
462 case GCC: npp = &hp->h_cc; break;
463 case GBCC: npp = &hp->h_bcc; break;
464 default: continue;
466 *npp = cat(*npp, ndup(np, np->n_type | GFULL));
468 NYD_LEAVE;
469 return tolist;
472 static int
473 put_signature(FILE *fo, int convert)
475 char buf[SEND_LINESIZE], *sig, c = '\n';
476 FILE *fsig;
477 size_t sz;
478 int rv;
479 NYD_ENTER;
481 if ((sig = ok_vlook(signature)) == NULL || *sig == '\0') {
482 rv = 0;
483 goto jleave;
485 rv = -1;
487 if ((sig = file_expand(sig)) == NULL)
488 goto jleave;
490 if ((fsig = Fopen(sig, "r")) == NULL) {
491 perror(sig);
492 goto jleave;
494 while ((sz = fread(buf, sizeof *buf, SEND_LINESIZE, fsig)) != 0) {
495 c = buf[sz - 1];
496 if (xmime_write(buf, sz, fo, convert, TD_NONE, NULL) < 0)
497 goto jerr;
499 if (ferror(fsig)) {
500 jerr:
501 perror(sig);
502 Fclose(fsig);
503 goto jleave;
505 Fclose(fsig);
506 if (c != '\n')
507 putc('\n', fo);
509 rv = 0;
510 jleave:
511 NYD_LEAVE;
512 return rv;
515 static int
516 attach_message(struct attachment *ap, FILE *fo)
518 struct message *mp;
519 char const *ccp;
520 int rv;
521 NYD_ENTER;
523 fprintf(fo, "\n--%s\nContent-Type: message/rfc822\n"
524 "Content-Disposition: inline\n", _sendout_boundary);
525 if ((ccp = ap->a_content_description) != NULL)
526 fprintf(fo, "Content-Description: %s\n", ccp);
527 fputc('\n', fo);
529 mp = message + ap->a_msgno - 1;
530 touch(mp);
531 rv = (sendmp(mp, fo, 0, NULL, SEND_RFC822, NULL) < 0) ? -1 : 0;
532 NYD_LEAVE;
533 return rv;
536 static int
537 make_multipart(struct header *hp, int convert, FILE *fi, FILE *fo,
538 char const *contenttype, char const *charset)
540 struct attachment *att;
541 int rv = -1;
542 NYD_ENTER;
544 fputs("This is a multi-part message in MIME format.\n", fo);
545 if (fsize(fi) != 0) {
546 char *buf;
547 size_t sz, bufsize, cnt;
549 fprintf(fo, "\n--%s\n", _sendout_boundary);
550 fprintf(fo, "Content-Type: %s", contenttype);
551 if (charset != NULL)
552 fprintf(fo, "; charset=%s", charset);
553 fprintf(fo, "\nContent-Transfer-Encoding: %s\n"
554 "Content-Disposition: inline\n\n", _get_encoding(convert));
556 buf = smalloc(bufsize = SEND_LINESIZE);
557 if (convert == CONV_TOQP
558 #ifdef HAVE_ICONV
559 || iconvd != (iconv_t)-1
560 #endif
562 fflush(fi);
563 cnt = fsize(fi);
565 for (;;) {
566 if (convert == CONV_TOQP
567 #ifdef HAVE_ICONV
568 || iconvd != (iconv_t)-1
569 #endif
571 if (fgetline(&buf, &bufsize, &cnt, &sz, fi, 0) == NULL)
572 break;
573 } else if ((sz = fread(buf, sizeof *buf, bufsize, fi)) == 0)
574 break;
576 if (xmime_write(buf, sz, fo, convert, TD_ICONV, NULL) < 0) {
577 free(buf);
578 goto jleave;
581 free(buf);
583 if (ferror(fi))
584 goto jleave;
585 if (charset != NULL)
586 put_signature(fo, convert);
589 for (att = hp->h_attach; att != NULL; att = att->a_flink) {
590 if (att->a_msgno) {
591 if (attach_message(att, fo) != 0)
592 goto jleave;
593 } else if (_attach_file(att, fo) != 0)
594 goto jleave;
597 /* the final boundary with two attached dashes */
598 fprintf(fo, "\n--%s--\n", _sendout_boundary);
599 rv = 0;
600 jleave:
601 NYD_LEAVE;
602 return rv;
605 static FILE *
606 infix(struct header *hp, FILE *fi) /* TODO check */
608 FILE *nfo, *nfi = NULL;
609 char *tempMail;
610 char const *contenttype, *charset = NULL;
611 enum conversion convert;
612 int do_iconv = 0, err;
613 #ifdef HAVE_ICONV
614 char const *tcs, *convhdr = NULL;
615 #endif
616 NYD_ENTER;
618 if ((nfo = Ftmp(&tempMail, "infix", OF_WRONLY | OF_HOLDSIGS | OF_REGISTER,
619 0600)) == NULL) {
620 perror(tr(178, "temporary mail file"));
621 goto jleave;
623 if ((nfi = Fopen(tempMail, "r")) == NULL) {
624 perror(tempMail);
625 Fclose(nfo);
627 Ftmp_release(&tempMail);
628 if (nfi == NULL)
629 goto jleave;
631 contenttype = "text/plain"; /* XXX mail body - always text/plain, want XX? */
632 convert = mime_classify_file(fi, &contenttype, &charset, &do_iconv);
634 #ifdef HAVE_ICONV
635 tcs = charset_get_lc();
636 if ((convhdr = need_hdrconv(hp, GTO | GSUBJECT | GCC | GBCC | GIDENT))) {
637 if (iconvd != (iconv_t)-1) /* XXX */
638 n_iconv_close(iconvd);
639 if (asccasecmp(convhdr, tcs) != 0 &&
640 (iconvd = n_iconv_open(convhdr, tcs)) == (iconv_t)-1 &&
641 (err = errno) != 0)
642 goto jiconv_err;
644 #endif
645 if (puthead(hp, nfo,
646 (GTO | GSUBJECT | GCC | GBCC | GNL | GCOMMA | GUA | GMIME | GMSGID |
647 GIDENT | GREF | GDATE), SEND_MBOX, convert, contenttype, charset))
648 goto jerr;
649 #ifdef HAVE_ICONV
650 if (iconvd != (iconv_t)-1)
651 n_iconv_close(iconvd);
652 #endif
654 #ifdef HAVE_ICONV
655 if (do_iconv && charset != NULL) { /*TODO charset->mime_classify_file*/
656 if (asccasecmp(charset, tcs) != 0 &&
657 (iconvd = n_iconv_open(charset, tcs)) == (iconv_t)-1 &&
658 (err = errno) != 0) {
659 jiconv_err:
660 if (err == EINVAL)
661 fprintf(stderr, tr(179, "Cannot convert from %s to %s\n"),
662 tcs, charset);
663 else
664 perror("iconv_open");
665 goto jerr;
668 #endif
670 if (hp->h_attach != NULL) {
671 if (make_multipart(hp, convert, fi, nfo, contenttype, charset) != 0)
672 goto jerr;
673 } else {
674 size_t sz, bufsize, cnt;
675 char *buf;
677 if (convert == CONV_TOQP
678 #ifdef HAVE_ICONV
679 || iconvd != (iconv_t)-1
680 #endif
682 fflush(fi);
683 cnt = fsize(fi);
685 buf = smalloc(bufsize = SEND_LINESIZE);
686 for (err = 0;;) {
687 if (convert == CONV_TOQP
688 #ifdef HAVE_ICONV
689 || iconvd != (iconv_t)-1
690 #endif
692 if (fgetline(&buf, &bufsize, &cnt, &sz, fi, 0) == NULL)
693 break;
694 } else if ((sz = fread(buf, sizeof *buf, bufsize, fi)) == 0)
695 break;
696 if (xmime_write(buf, sz, nfo, convert, TD_ICONV, NULL) < 0) {
697 err = 1;
698 break;
701 free(buf);
703 if (err || ferror(fi)) {
704 jerr:
705 Fclose(nfo);
706 Fclose(nfi);
707 #ifdef HAVE_ICONV
708 if (iconvd != (iconv_t)-1)
709 n_iconv_close(iconvd);
710 #endif
711 nfi = NULL;
712 goto jleave;
714 if (charset != NULL)
715 put_signature(nfo, convert); /* XXX if (text/) !! */
718 #ifdef HAVE_ICONV
719 if (iconvd != (iconv_t)-1)
720 n_iconv_close(iconvd);
721 #endif
723 fflush(nfo);
724 if ((err = ferror(nfo)))
725 perror(tr(180, "temporary mail file"));
726 Fclose(nfo);
727 if (!err) {
728 fflush_rewind(nfi);
729 Fclose(fi);
730 } else {
731 Fclose(nfi);
732 nfi = NULL;
734 jleave:
735 NYD_LEAVE;
736 return nfi;
739 static int
740 savemail(char const *name, FILE *fi)
742 FILE *fo;
743 char *buf;
744 size_t bufsize, buflen, cnt;
745 int prependnl = 0, rv = -1;
746 NYD_ENTER;
748 buf = smalloc(bufsize = LINESIZE);
750 if ((fo = Zopen(name, "a+", NULL)) == NULL) {
751 if ((fo = Zopen(name, "wx", NULL)) == NULL) {
752 perror(name);
753 goto jleave;
755 } else {
756 if (fseek(fo, -2L, SEEK_END) == 0) {
757 switch (fread(buf, sizeof *buf, 2, fo)) {
758 case 2:
759 if (buf[1] != '\n') {
760 prependnl = 1;
761 break;
763 /* FALLTHRU */
764 case 1:
765 if (buf[0] != '\n')
766 prependnl = 1;
767 break;
768 default:
769 if (ferror(fo)) {
770 perror(name);
771 goto jleave;
774 if (prependnl) {
775 putc('\n', fo);
777 fflush(fo);
781 fprintf(fo, "From %s %s", myname, time_current.tc_ctime);
782 fflush_rewind(fi);
783 cnt = fsize(fi);
784 buflen = 0;
785 while (fgetline(&buf, &bufsize, &cnt, &buflen, fi, 0) != NULL) {
786 #ifdef HAVE_DEBUG /* TODO assert legacy */
787 assert(!is_head(buf, buflen));
788 #else
789 if (is_head(buf, buflen))
790 putc('>', fo);
791 #endif
792 fwrite(buf, sizeof *buf, buflen, fo);
794 if (buflen && *(buf + buflen - 1) != '\n')
795 putc('\n', fo);
796 putc('\n', fo);
797 fflush(fo);
799 rv = 0;
800 if (ferror(fo)) {
801 perror(name);
802 rv = -1;
804 if (Fclose(fo) != 0)
805 rv = -1;
806 fflush_rewind(fi);
807 jleave:
808 free(buf);
809 NYD_LEAVE;
810 return rv;
813 static int
814 sendmail_internal(void *v, int recipient_record)
816 struct header head;
817 char *str = v;
818 int rv;
819 NYD_ENTER;
821 memset(&head, 0, sizeof head);
822 head.h_to = lextract(str, GTO | GFULL);
823 rv = mail1(&head, 0, NULL, NULL, recipient_record, 0);
824 NYD_LEAVE;
825 return rv;
828 static bool_t
829 _transfer(struct sendbundle *sbp)
831 struct name *np;
832 ui32_t cnt;
833 bool_t rv = TRU1;
834 NYD_ENTER;
836 for (cnt = 0, np = sbp->sb_to; np != NULL;) {
837 char const k[] = "smime-encrypt-";
838 size_t nl = strlen(np->n_name);
839 char *cp, *vs = ac_alloc(sizeof(k)-1 + nl +1);
840 memcpy(vs, k, sizeof(k) -1);
841 memcpy(vs + sizeof(k) -1, np->n_name, nl +1);
843 if ((cp = vok_vlook(vs)) != NULL) {
844 #ifdef HAVE_SSL
845 FILE *ef;
847 if ((ef = smime_encrypt(sbp->sb_input, cp, np->n_name)) != NULL) {
848 FILE *fisave = sbp->sb_input;
849 struct name *nsave = sbp->sb_to;
851 sbp->sb_to = ndup(np, np->n_type & ~(GFULL | GSKIN));
852 sbp->sb_input = ef;
853 if (!start_mta(sbp))
854 rv = FAL0;
855 sbp->sb_to = nsave;
856 sbp->sb_input = fisave;
858 Fclose(ef);
859 } else {
860 #else
861 fprintf(stderr, tr(225, "No SSL support compiled in.\n"));
862 rv = FAL0;
863 #endif
864 fprintf(stderr, tr(38, "Message not sent to <%s>\n"), np->n_name);
865 _sendout_error = TRU1;
866 #ifdef HAVE_SSL
868 #endif
869 rewind(sbp->sb_input);
871 if (np->n_flink != NULL)
872 np->n_flink->n_blink = np->n_blink;
873 if (np->n_blink != NULL)
874 np->n_blink->n_flink = np->n_flink;
875 if (np == sbp->sb_to)
876 sbp->sb_to = np->n_flink;
877 np = np->n_flink;
878 } else {
879 ++cnt;
880 np = np->n_flink;
882 ac_free(vs);
885 if (cnt > 0 && (ok_blook(smime_force_encryption) || !start_mta(sbp)))
886 rv = FAL0;
887 NYD_LEAVE;
888 return rv;
891 static bool_t
892 start_mta(struct sendbundle *sbp)
894 char const **args = NULL, **t, *mta;
895 char *smtp;
896 pid_t pid;
897 sigset_t nset;
898 bool_t rv = FAL0;
899 NYD_ENTER;
901 if ((smtp = ok_vlook(smtp)) == NULL) {
902 if ((mta = ok_vlook(sendmail)) != NULL) {
903 if ((mta = file_expand(mta)) == NULL)
904 goto jstop;
905 } else
906 mta = SENDMAIL;
908 args = _prepare_mta_args(sbp->sb_to, sbp->sb_hp);
909 if (options & OPT_DEBUG) {
910 printf(tr(181, "Sendmail arguments:"));
911 for (t = args; *t != NULL; ++t)
912 printf(" \"%s\"", *t);
913 printf("\n");
914 rv = TRU1;
915 goto jleave;
917 } else {
918 mta = NULL; /* Silence cc */
919 #ifndef HAVE_SMTP
920 fputs(tr(194, "No SMTP support compiled in.\n"), stderr);
921 goto jstop;
922 #else
923 /* XXX assert that sendbundle is setup? */
924 #endif
927 /* Fork, set up the temporary mail file as standard input for "mail", and
928 * exec with the user list we generated far above */
929 if ((pid = fork()) == -1) {
930 perror("fork");
931 jstop:
932 savedeadletter(sbp->sb_input, 0);
933 _sendout_error = TRU1;
934 goto jleave;
936 if (pid == 0) {
937 sigemptyset(&nset);
938 sigaddset(&nset, SIGHUP);
939 sigaddset(&nset, SIGINT);
940 sigaddset(&nset, SIGQUIT);
941 sigaddset(&nset, SIGTSTP);
942 sigaddset(&nset, SIGTTIN);
943 sigaddset(&nset, SIGTTOU);
944 freopen("/dev/null", "r", stdin);
945 #ifdef HAVE_SMTP
946 if (smtp != NULL) {
947 prepare_child(&nset, 0, 1);
948 if (smtp_mta(sbp))
949 _exit(0);
950 } else {
951 #endif
952 prepare_child(&nset, fileno(sbp->sb_input), -1);
953 /* If *record* is set then savemail() will move the file position;
954 * it'll call rewind(), but that may optimize away the systemcall if
955 * possible, and since dup2() shares the position with the original FD
956 * the MTA may end up reading nothing */
957 lseek(0, 0, SEEK_SET);
958 execv(mta, UNCONST(args));
959 perror(mta);
960 #ifdef HAVE_SMTP
962 #endif
963 savedeadletter(sbp->sb_input, 1);
964 fputs(tr(182, "... message not sent.\n"), stderr);
965 _exit(1);
967 if ((options & (OPT_DEBUG | OPT_VERB | OPT_BATCH_FLAG)) ||
968 ok_blook(sendwait)) {
969 if (wait_child(pid, NULL))
970 rv = TRU1;
971 else
972 _sendout_error = TRU1;
973 } else {
974 rv = TRU1;
975 free_child(pid);
977 jleave:
978 NYD_LEAVE;
979 return rv;
982 static bool_t
983 mightrecord(FILE *fp, struct name *to)
985 char *cp, *cq;
986 char const *ep;
987 bool_t rv = TRU1;
988 NYD_ENTER;
990 if (to != NULL) {
991 cp = savestr(skinned_name(to));
992 for (cq = cp; *cq != '\0' && *cq != '@'; ++cq)
994 *cq = '\0';
995 } else
996 cp = ok_vlook(record);
998 if (cp != NULL) {
999 if ((ep = expand(cp)) == NULL) {
1000 ep = "NULL";
1001 goto jbail;
1004 if (*ep != '/' && *ep != '+' && ok_blook(outfolder) &&
1005 which_protocol(ep) == PROTO_FILE) {
1006 size_t i = strlen(cp);
1007 cq = salloc(i + 1 +1);
1008 cq[0] = '+';
1009 memcpy(cq + 1, cp, i +1);
1010 cp = cq;
1011 if ((ep = file_expand(cp)) == NULL) {
1012 ep = "NULL";
1013 goto jbail;
1017 if (savemail(ep, fp) != 0) {
1018 jbail:
1019 fprintf(stderr, tr(285, "Failed to save message in %s - "
1020 "message not sent\n"), ep);
1021 exit_status |= EXIT_ERR;
1022 savedeadletter(fp, 1);
1023 rv = FAL0;
1026 NYD_LEAVE;
1027 return rv;
1030 static void
1031 _message_id(FILE *fo, struct header *hp)
1033 char const *h;
1034 size_t rl;
1035 struct tm *tmp;
1036 NYD_ENTER;
1038 if (ok_blook(message_id_disable))
1039 goto jleave;
1041 if ((h = __sendout_ident) != NULL)
1042 rl = 8;
1043 else if ((h = ok_vlook(hostname)) != NULL)
1044 rl = 16;
1045 else if ((h = skin(myorigin(hp))) != NULL && strchr(h, '@') != NULL)
1046 rl = 8;
1047 else
1048 /* Up to MTA */
1049 goto jleave;
1051 tmp = &time_current.tc_gm;
1052 fprintf(fo, "Message-ID: <%04d%02d%02d%02d%02d%02d.%s%c%s>\n",
1053 tmp->tm_year + 1900, tmp->tm_mon + 1, tmp->tm_mday,
1054 tmp->tm_hour, tmp->tm_min, tmp->tm_sec,
1055 getrandstring(rl), (rl == 8 ? '%' : '@'), h);
1056 jleave:
1057 __sendout_ident = NULL;
1058 NYD_LEAVE;
1061 static int
1062 fmt(char const *str, struct name *np, FILE *fo, int flags, int dropinvalid,
1063 int domime)
1065 enum {
1066 m_INIT = 1<<0,
1067 m_COMMA = 1<<1,
1068 m_NOPF = 1<<2,
1069 m_CSEEN = 1<<3
1070 } m = (flags & GCOMMA) ? m_COMMA : 0;
1071 ssize_t col, len;
1072 int rv = 1;
1073 NYD_ENTER;
1075 col = strlen(str);
1076 if (col) {
1077 fwrite(str, sizeof *str, col, fo);
1078 if (flags & GFILES)
1079 goto jstep;
1080 if (col == 9 && !asccasecmp(str, "reply-to:")) {
1081 m |= m_NOPF;
1082 goto jstep;
1084 if (ok_blook(add_file_recipients))
1085 goto jstep;
1086 if ((col == 3 && (!asccasecmp(str, "to:") || !asccasecmp(str, "cc:"))) ||
1087 (col == 4 && !asccasecmp(str, "bcc:")) ||
1088 (col == 10 && !asccasecmp(str, "Resent-To:")))
1089 m |= m_NOPF;
1091 jstep:
1092 for (; np != NULL; np = np->n_flink) {
1093 if ((m & m_NOPF) && is_fileorpipe_addr(np))
1094 continue;
1095 if (is_addr_invalid(np, !dropinvalid)) {
1096 if (dropinvalid)
1097 continue;
1098 else
1099 goto jleave;
1101 if ((m & (m_INIT | m_COMMA)) == (m_INIT | m_COMMA)) {
1102 putc(',', fo);
1103 m |= m_CSEEN;
1104 ++col;
1106 len = strlen(np->n_fullname);
1107 ++col; /* The separating space */
1108 if ((m & m_INIT) && /*col > 1 &&*/ UICMP(z, col + len, >, 72)) {
1109 fputs("\n ", fo);
1110 col = 1;
1111 m &= ~m_CSEEN;
1112 } else
1113 putc(' ', fo);
1114 m = (m & ~m_CSEEN) | m_INIT;
1115 len = xmime_write(np->n_fullname, len, fo,
1116 (domime ? CONV_TOHDR_A : CONV_NONE), TD_ICONV, NULL);
1117 if (len < 0)
1118 goto jleave;
1119 col += len;
1121 putc('\n', fo);
1122 rv = 0;
1123 jleave:
1124 NYD_LEAVE;
1125 return rv;
1128 static int
1129 infix_resend(FILE *fi, FILE *fo, struct message *mp, struct name *to,
1130 int add_resent)
1132 size_t cnt, c, bufsize = 0;
1133 char *buf = NULL;
1134 char const *cp;
1135 struct name *fromfield = NULL, *senderfield = NULL;
1136 int rv = 1;
1137 NYD_ENTER;
1139 cnt = mp->m_size;
1141 /* Write the Resent-Fields */
1142 if (add_resent) {
1143 fputs("Resent-", fo);
1144 mkdate(fo, "Date");
1145 if ((cp = myaddrs(NULL)) != NULL) {
1146 if (_putname(cp, GCOMMA, SEND_MBOX, NULL, "Resent-From:", fo,
1147 &fromfield))
1148 goto jleave;
1150 if ((cp = ok_vlook(sender)) != NULL) {
1151 if (_putname(cp, GCOMMA, SEND_MBOX, NULL, "Resent-Sender:", fo,
1152 &senderfield))
1153 goto jleave;
1155 if (fmt("Resent-To:", to, fo, 1, 1, 0))
1156 goto jleave;
1157 if ((cp = ok_vlook(stealthmua)) == NULL || !strcmp(cp, "noagent")) {
1158 fputs("Resent-", fo);
1159 _message_id(fo, NULL);
1162 if (check_from_and_sender(fromfield, senderfield))
1163 goto jleave;
1165 /* Write the original headers */
1166 while (cnt > 0) {
1167 if (fgetline(&buf, &bufsize, &cnt, &c, fi, 0) == NULL)
1168 break;
1169 /* XXX more checks: The From_ line may be seen when resending */
1170 /* During headers is_head() is actually overkill, so ^From_ is sufficient
1171 * && !is_head(buf, c) */
1172 if (ascncasecmp("status: ", buf, 8) && strncmp("From ", buf, 5))
1173 fwrite(buf, sizeof *buf, c, fo);
1174 if (cnt > 0 && *buf == '\n')
1175 break;
1178 /* Write the message body */
1179 while (cnt > 0) {
1180 if (fgetline(&buf, &bufsize, &cnt, &c, fi, 0) == NULL)
1181 break;
1182 if (cnt == 0 && *buf == '\n')
1183 break;
1184 fwrite(buf, sizeof *buf, c, fo);
1186 if (buf != NULL)
1187 free(buf);
1188 if (ferror(fo)) {
1189 perror(tr(188, "temporary mail file"));
1190 goto jleave;
1192 rv = 0;
1193 jleave:
1194 NYD_LEAVE;
1195 return rv;
1198 FL int
1199 mail(struct name *to, struct name *cc, struct name *bcc, char *subject,
1200 struct attachment *attach, char *quotefile, int recipient_record)
1202 struct header head;
1203 struct str in, out;
1204 NYD_ENTER;
1206 memset(&head, 0, sizeof head);
1208 /* The given subject may be in RFC1522 format. */
1209 if (subject != NULL) {
1210 in.s = subject;
1211 in.l = strlen(subject);
1212 mime_fromhdr(&in, &out, /* TODO ??? TD_ISPR |*/ TD_ICONV);
1213 head.h_subject = out.s;
1215 if (!(options & OPT_t_FLAG)) {
1216 head.h_to = to;
1217 head.h_cc = cc;
1218 head.h_bcc = bcc;
1220 head.h_attach = attach;
1222 mail1(&head, 0, NULL, quotefile, recipient_record, 0);
1224 if (subject != NULL)
1225 free(out.s);
1226 NYD_LEAVE;
1227 return 0;
1230 FL int
1231 c_sendmail(void *v)
1233 int rv;
1234 NYD_ENTER;
1236 rv = sendmail_internal(v, 0);
1237 NYD_LEAVE;
1238 return rv;
1241 FL int
1242 c_Sendmail(void *v)
1244 int rv;
1245 NYD_ENTER;
1247 rv = sendmail_internal(v, 1);
1248 NYD_LEAVE;
1249 return rv;
1252 FL enum okay
1253 mail1(struct header *hp, int printheaders, struct message *quote,
1254 char *quotefile, int recipient_record, int doprefix)
1256 struct sendbundle sb;
1257 struct name *to;
1258 FILE *mtf, *nmtf;
1259 int dosign = -1, err;
1260 char const *cp;
1261 enum okay rv = STOP;
1262 NYD_ENTER;
1264 _sendout_error = FAL0;
1266 /* Update some globals we likely need first */
1267 time_current_update(&time_current, TRU1);
1269 /* */
1270 if ((cp = ok_vlook(autocc)) != NULL && *cp != '\0')
1271 hp->h_cc = cat(hp->h_cc, checkaddrs(lextract(cp, GCC | GFULL)));
1272 if ((cp = ok_vlook(autobcc)) != NULL && *cp != '\0')
1273 hp->h_bcc = cat(hp->h_bcc, checkaddrs(lextract(cp, GBCC | GFULL)));
1275 /* Collect user's mail from standard input. Get the result as mtf */
1276 mtf = collect(hp, printheaders, quote, quotefile, doprefix);
1277 if (mtf == NULL)
1278 goto j_leave;
1280 if (options & OPT_INTERACTIVE) {
1281 err = (ok_blook(bsdcompat) || ok_blook(askatend));
1282 if (err == 0)
1283 goto jaskeot;
1284 if (ok_blook(askcc))
1285 ++err, grab_headers(hp, GCC, 1);
1286 if (ok_blook(askbcc))
1287 ++err, grab_headers(hp, GBCC, 1);
1288 if (ok_blook(askattach))
1289 ++err, edit_attachments(&hp->h_attach);
1290 if (ok_blook(asksign))
1291 ++err, dosign = getapproval(tr(35, "Sign this message (y/n)? "), TRU1);
1292 if (err == 1) {
1293 jaskeot:
1294 printf(tr(183, "EOT\n"));
1295 fflush(stdout);
1299 if (fsize(mtf) == 0) {
1300 if (options & OPT_E_FLAG)
1301 goto jleave;
1302 if (hp->h_subject == NULL)
1303 printf(tr(184, "No message, no subject; hope that's ok\n"));
1304 else if (ok_blook(bsdcompat) || ok_blook(bsdmsgs))
1305 printf(tr(185, "Null message body; hope that's ok\n"));
1308 if (dosign < 0)
1309 dosign = ok_blook(smime_sign);
1310 #ifndef HAVE_SSL
1311 if (dosign) {
1312 fprintf(stderr, tr(225, "No SSL support compiled in.\n"));
1313 goto jleave;
1315 #endif
1317 /* XXX Update time_current again; once collect() offers editing of more
1318 * XXX headers, including Date:, this must only happen if Date: is the
1319 * XXX same that it was before collect() (e.g., postponing etc.).
1320 * XXX But *do* update otherwise because the mail seems to be backdated
1321 * XXX if the user edited some time, which looks odd and it happened
1322 * XXX to me that i got mis-dated response mails due to that... */
1323 time_current_update(&time_current, TRU1);
1325 /* TODO hrmpf; the MIME/send layer rewrite MUST address the init crap:
1326 * TODO setup the header ONCE; note this affects edit.c, collect.c ...,
1327 * TODO but: offer a hook that rebuilds/expands/checks/fixates all
1328 * TODO header fields ONCE, call that ONCE after user editing etc. has
1329 * TODO completed (one edit cycle) */
1331 /* Take the user names from the combined to and cc lists and do all the
1332 * alias processing. The POSIX standard says:
1333 * The names shall be substituted when alias is used as a recipient
1334 * address specified by the user in an outgoing message (that is,
1335 * other recipients addressed indirectly through the reply command
1336 * shall not be substituted in this manner).
1337 * S-nail thus violates POSIX, as has been pointed out correctly by
1338 * Martin Neitzel, but logic and usability of POSIX standards is not seldom
1339 * disputable anyway. Go for user friendliness */
1341 /* Do alias expansion on Reply-To: members, too */
1342 /* TODO puthead() YET (!!! see ONCE note above) expands the value, but
1343 * TODO doesn't perform alias expansion; encapsulate in the ONCE-o */
1344 if (hp->h_replyto == NULL && (cp = ok_vlook(replyto)) != NULL)
1345 hp->h_replyto = checkaddrs(lextract(cp, GEXTRA | GFULL));
1346 if (hp->h_replyto != NULL)
1347 hp->h_replyto = elide(usermap(hp->h_replyto, TRU1));
1349 /* TODO what happens now is that all recipients are merged into
1350 * TODO a duplicated list with expanded aliases, then this list is
1351 * TODO splitted again into the three individual recipient lists (with
1352 * TODO duplicates removed).
1353 * TODO later on we use the merged list for outof() pipe/file saving,
1354 * TODO then we eliminate duplicates (again) and then we use that one
1355 * TODO for mightrecord() and _transfer(), and again. ... Please ... */
1357 /* NOTE: Due to elide() in fixhead(), ENSURE to,cc,bcc order of to!,
1358 * because otherwise the recipients will be "degraded" if they occur
1359 * multiple times */
1360 to = usermap(cat(hp->h_to, cat(hp->h_cc, hp->h_bcc)), FAL0);
1361 if (to == NULL) {
1362 fprintf(stderr, tr(186, "No recipients specified\n"));
1363 _sendout_error = TRU1;
1365 to = fixhead(hp, to);
1367 /* */
1368 memset(&sb, 0, sizeof sb);
1369 sb.sb_hp = hp;
1370 sb.sb_to = to;
1371 sb.sb_input = mtf;
1372 if ((dosign || count_nonlocal(to) > 0) &&
1373 !_sendbundle_setup_creds(&sb, (dosign > 0)))
1374 /* TODO saving $DEAD and recovering etc is not yet well defined */
1375 goto jfail_dead;
1377 /* 'Bit ugly kind of control flow until we find a charset that does it */
1378 for (charset_iter_reset(hp->h_charset);; charset_iter_next()) {
1379 if (!charset_iter_is_valid())
1381 else if ((nmtf = infix(hp, mtf)) != NULL)
1382 break;
1383 else if ((err = errno) == EILSEQ || err == EINVAL) {
1384 rewind(mtf);
1385 continue;
1388 perror("");
1389 jfail_dead:
1390 _sendout_error = TRU1;
1391 savedeadletter(mtf, TRU1);
1392 fputs(tr(182, "... message not sent.\n"), stderr);
1393 goto jleave;
1395 mtf = nmtf;
1397 /* */
1398 #ifdef HAVE_SSL
1399 if (dosign) {
1400 if ((nmtf = smime_sign(mtf, sb.sb_signer.s)) == NULL)
1401 goto jfail_dead;
1402 Fclose(mtf);
1403 mtf = nmtf;
1405 #endif
1407 /* TODO truly - i still don't get what follows: (1) we deliver file
1408 * TODO and pipe addressees, (2) we mightrecord() and (3) we transfer
1409 * TODO even if (1) savedeadletter() etc. To me this doesn't make sense? */
1411 /* Deliver pipe and file addressees */
1412 to = outof(to, mtf, &_sendout_error);
1413 if (_sendout_error)
1414 savedeadletter(mtf, FAL0);
1416 to = elide(to); /* XXX needed only to drop GDELs due to outof()! */
1417 { ui32_t cnt = count(to);
1418 if ((!recipient_record || cnt > 0) &&
1419 !mightrecord(mtf, (recipient_record ? to : NULL)))
1420 goto jleave;
1421 if (cnt > 0) {
1422 sb.sb_hp = hp;
1423 sb.sb_to = to;
1424 sb.sb_input = mtf;
1425 if (_transfer(&sb))
1426 rv = OKAY;
1427 } else if (!_sendout_error)
1428 rv = OKAY;
1430 jleave:
1431 Fclose(mtf);
1432 j_leave:
1433 if (_sendout_error)
1434 exit_status |= EXIT_SEND_ERROR;
1435 NYD_LEAVE;
1436 return rv;
1439 FL int
1440 mkdate(FILE *fo, char const *field)
1442 struct tm *tmptr;
1443 int tzdiff, tzdiff_hour, tzdiff_min, rv;
1444 NYD_ENTER;
1446 tzdiff = time_current.tc_time - mktime(&time_current.tc_gm);
1447 tzdiff_hour = (int)(tzdiff / 60);
1448 tzdiff_min = tzdiff_hour % 60;
1449 tzdiff_hour /= 60;
1450 tmptr = &time_current.tc_local;
1451 if (tmptr->tm_isdst > 0)
1452 ++tzdiff_hour;
1453 rv = fprintf(fo, "%s: %s, %02d %s %04d %02d:%02d:%02d %+05d\n",
1454 field,
1455 weekday_names[tmptr->tm_wday],
1456 tmptr->tm_mday, month_names[tmptr->tm_mon],
1457 tmptr->tm_year + 1900, tmptr->tm_hour,
1458 tmptr->tm_min, tmptr->tm_sec,
1459 tzdiff_hour * 100 + tzdiff_min);
1460 NYD_LEAVE;
1461 return rv;
1464 FL int
1465 puthead(struct header *hp, FILE *fo, enum gfield w, enum sendaction action,
1466 enum conversion convert, char const *contenttype, char const *charset)
1468 #define FMT_CC_AND_BCC() \
1469 do {\
1470 if (hp->h_cc != NULL && (w & GCC)) {\
1471 if (fmt("Cc:", hp->h_cc, fo, (w & (GCOMMA | GFILES)), 0,\
1472 (action != SEND_TODISP)))\
1473 goto jleave;\
1474 ++gotcha;\
1476 if (hp->h_bcc != NULL && (w & GBCC)) {\
1477 if (fmt("Bcc:", hp->h_bcc, fo, (w & (GCOMMA | GFILES)), 0,\
1478 (action != SEND_TODISP)))\
1479 goto jleave;\
1480 ++gotcha;\
1482 } while (0)
1484 char const *addr;
1485 size_t gotcha, l;
1486 struct name *np, *fromfield = NULL, *senderfield = NULL;
1487 int stealthmua, rv = 1;
1488 bool_t nodisp;
1489 NYD_ENTER;
1491 if ((addr = ok_vlook(stealthmua)) != NULL)
1492 stealthmua = !strcmp(addr, "noagent") ? -1 : 1;
1493 else
1494 stealthmua = 0;
1495 gotcha = 0;
1496 nodisp = (action != SEND_TODISP);
1498 if (w & GDATE)
1499 mkdate(fo, "Date"), ++gotcha;
1500 if (w & GIDENT) {
1501 if (hp->h_from != NULL) {
1502 if (fmt("From:", hp->h_from, fo, (w & (GCOMMA | GFILES)), 0, nodisp))
1503 goto jleave;
1504 ++gotcha;
1505 fromfield = hp->h_from;
1506 } else if ((addr = myaddrs(hp)) != NULL) {
1507 if (_putname(addr, w, action, &gotcha, "From:", fo, &fromfield))
1508 goto jleave;
1509 hp->h_from = fromfield;
1512 if (((addr = hp->h_organization) != NULL ||
1513 (addr = ok_vlook(ORGANIZATION)) != NULL) &&
1514 (l = strlen(addr)) > 0) {
1515 fwrite("Organization: ", sizeof(char), 14, fo);
1516 if (xmime_write(addr, l, fo, (!nodisp ? CONV_NONE : CONV_TOHDR),
1517 (!nodisp ? TD_ISPR | TD_ICONV : TD_ICONV), NULL) < 0)
1518 goto jleave;
1519 ++gotcha;
1520 putc('\n', fo);
1523 /* TODO see the ONCE TODO note somewhere around this file;
1524 * TODO but anyway, do NOT perform alias expansion UNLESS
1525 * TODO we are actually sending out! */
1526 if (hp->h_replyto != NULL) {
1527 if (fmt("Reply-To:", hp->h_replyto, fo, w & GCOMMA, 0, nodisp))
1528 goto jleave;
1529 ++gotcha;
1530 } else if ((addr = ok_vlook(replyto)) != NULL)
1531 if (_putname(addr, w, action, &gotcha, "Reply-To:", fo, NULL))
1532 goto jleave;
1534 if (hp->h_sender != NULL) {
1535 if (fmt("Sender:", hp->h_sender, fo, w & GCOMMA, 0, nodisp))
1536 goto jleave;
1537 ++gotcha;
1538 senderfield = hp->h_sender;
1539 } else if ((addr = ok_vlook(sender)) != NULL)
1540 if (_putname(addr, w, action, &gotcha, "Sender:", fo, &senderfield))
1541 goto jleave;
1543 if (check_from_and_sender(fromfield, senderfield))
1544 goto jleave;
1547 if (hp->h_to != NULL && w & GTO) {
1548 if (fmt("To:", hp->h_to, fo, (w & (GCOMMA | GFILES)), 0, nodisp))
1549 goto jleave;
1550 ++gotcha;
1553 if (!ok_blook(bsdcompat) && !ok_blook(bsdorder))
1554 FMT_CC_AND_BCC();
1556 if (hp->h_subject != NULL && (w & GSUBJECT)) {
1557 fwrite("Subject: ", sizeof (char), 9, fo);
1558 if (!ascncasecmp(hp->h_subject, "re: ", 4)) {/* TODO localizable */
1559 fwrite("Re: ", sizeof(char), 4, fo);
1560 if (strlen(hp->h_subject + 4) > 0 &&
1561 xmime_write(hp->h_subject + 4, strlen(hp->h_subject + 4), fo,
1562 (!nodisp ? CONV_NONE : CONV_TOHDR),
1563 (!nodisp ? TD_ISPR | TD_ICONV : TD_ICONV), NULL) < 0)
1564 goto jleave;
1565 } else if (*hp->h_subject != '\0') {
1566 if (xmime_write(hp->h_subject, strlen(hp->h_subject), fo,
1567 (!nodisp ? CONV_NONE : CONV_TOHDR),
1568 (!nodisp ? TD_ISPR | TD_ICONV : TD_ICONV), NULL) < 0)
1569 goto jleave;
1571 ++gotcha;
1572 fwrite("\n", sizeof (char), 1, fo);
1575 if (ok_blook(bsdcompat) || ok_blook(bsdorder))
1576 FMT_CC_AND_BCC();
1578 if ((w & GMSGID) && stealthmua <= 0)
1579 _message_id(fo, hp), ++gotcha;
1581 if ((np = hp->h_ref) != NULL && (w & GREF)) {
1582 fmt("References:", np, fo, 0, 1, 0);
1583 if (np->n_name != NULL) {
1584 while (np->n_flink != NULL)
1585 np = np->n_flink;
1586 if (!is_addr_invalid(np, 0)) {
1587 fprintf(fo, "In-Reply-To: %s\n", np->n_name);
1588 ++gotcha;
1593 if ((w & GUA) && stealthmua == 0)
1594 fprintf(fo, "User-Agent: %s %s\n", uagent, version), ++gotcha;
1596 if (w & GMIME) {
1597 fputs("MIME-Version: 1.0\n", fo), ++gotcha;
1598 if (hp->h_attach != NULL) {
1599 _sendout_boundary = mime_create_boundary();/*TODO carrier*/
1600 fprintf(fo, "Content-Type: multipart/mixed;\n boundary=\"%s\"\n",
1601 _sendout_boundary);
1602 } else {
1603 fprintf(fo, "Content-Type: %s", contenttype);
1604 if (charset != NULL)
1605 fprintf(fo, "; charset=%s", charset);
1606 fprintf(fo, "\nContent-Transfer-Encoding: %s\n",
1607 _get_encoding(convert));
1611 if (gotcha && (w & GNL))
1612 putc('\n', fo);
1613 rv = 0;
1614 jleave:
1615 NYD_LEAVE;
1616 return rv;
1617 #undef FMT_CC_AND_BCC
1620 FL enum okay
1621 resend_msg(struct message *mp, struct name *to, int add_resent) /* TODO check */
1623 struct sendbundle sb;
1624 FILE *ibuf, *nfo, *nfi;
1625 char *tempMail;
1626 enum okay rv = STOP;
1627 NYD_ENTER;
1629 _sendout_error = FAL0;
1631 /* Update some globals we likely need first */
1632 time_current_update(&time_current, TRU1);
1634 if ((to = checkaddrs(to)) == NULL) {
1635 _sendout_error = TRU1;
1636 goto jleave;
1639 if ((nfo = Ftmp(&tempMail, "resend", OF_WRONLY | OF_HOLDSIGS | OF_REGISTER,
1640 0600)) == NULL) {
1641 _sendout_error = TRU1;
1642 perror(tr(189, "temporary mail file"));
1643 goto jleave;
1645 if ((nfi = Fopen(tempMail, "r")) == NULL) {
1646 _sendout_error = TRU1;
1647 perror(tempMail);
1649 Ftmp_release(&tempMail);
1650 if (nfi == NULL)
1651 goto jerr_o;
1653 if ((ibuf = setinput(&mb, mp, NEED_BODY)) == NULL)
1654 goto jerr_all;
1656 memset(&sb, 0, sizeof sb);
1657 sb.sb_to = to;
1658 sb.sb_input = nfi;
1659 if (count_nonlocal(to) > 0 && !_sendbundle_setup_creds(&sb, FAL0))
1660 /* TODO saving $DEAD and recovering etc is not yet well defined */
1661 goto jerr_all;
1663 if (infix_resend(ibuf, nfo, mp, to, add_resent) != 0) {
1664 savedeadletter(nfi, TRU1);
1665 fputs(tr(182, "... message not sent.\n"), stderr);
1666 jerr_all:
1667 Fclose(nfi);
1668 jerr_o:
1669 Fclose(nfo);
1670 _sendout_error = TRU1;
1671 goto jleave;
1673 Fclose(nfo);
1674 rewind(nfi);
1676 to = outof(to, nfi, &_sendout_error);
1677 if (_sendout_error)
1678 savedeadletter(nfi, FAL0);
1680 to = elide(to); /* TODO should have been done in fixhead()? */
1681 if (count(to) != 0) {
1682 if (!ok_blook(record_resent) || mightrecord(nfi, to)) {
1683 sb.sb_to = to;
1684 /*sb.sb_input = nfi;*/
1685 if (_transfer(&sb))
1686 rv = OKAY;
1688 } else if (!_sendout_error)
1689 rv = OKAY;
1691 Fclose(nfi);
1692 jleave:
1693 if (_sendout_error)
1694 exit_status |= EXIT_SEND_ERROR;
1695 NYD_LEAVE;
1696 return rv;
1699 #undef SEND_LINESIZE
1701 /* vim:set fenc=utf-8:s-it-mode */