Attachments: tweak selection, fix #NO content-description..
[s-mailx.git] / sendout.c
blob863ed46a3440c50d6220b4d38f78202622c77f38
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 static char const ** _prepare_mta_args(struct name *to, struct header *hp);
65 /* Fix the header by glopping all of the expanded names from the distribution
66 * list into the appropriate fields */
67 static struct name * fixhead(struct header *hp, struct name *tolist);
69 /* Put the signature file at fo. TODO layer rewrite: *integrate in body*!! */
70 static int put_signature(FILE *fo, int convert);
72 /* Attach a message to the file buffer */
73 static int attach_message(struct attachment *ap, FILE *fo);
75 /* Generate the body of a MIME multipart message */
76 static int make_multipart(struct header *hp, int convert, FILE *fi,
77 FILE *fo, char const *contenttype, char const *charset);
79 /* Prepend a header in front of the collected stuff and return the new file */
80 static FILE * infix(struct header *hp, FILE *fi);
82 /* Save the outgoing mail on the passed file */
83 static int savemail(char const *name, FILE *fi);
85 /* Send mail to a bunch of user names. The interface is through mail() */
86 static int sendmail_internal(void *v, int recipient_record);
88 static enum okay transfer(struct name *to, FILE *input, struct header *hp);
90 /* Start the MTA mailing to namelist and stdin redirected to input */
91 static enum okay start_mta(struct name *to, FILE *input, struct header *hp);
93 /* Record outgoing mail if instructed to do so; in *record* unless to is set */
94 static enum okay mightrecord(FILE *fp, struct name *to);
96 /* Create a Message-Id: header field. Use either host name or from address */
97 static void message_id(FILE *fo, struct header *hp);
99 /* Format the given header line to not exceed 72 characters */
100 static int fmt(char const *str, struct name *np, FILE *fo, int comma,
101 int dropinvalid, int domime);
103 /* Rewrite a message for resending, adding the Resent-Headers */
104 static int infix_resend(FILE *fi, FILE *fo, struct message *mp,
105 struct name *to, int add_resent);
107 static enum okay
108 _putname(char const *line, enum gfield w, enum sendaction action,
109 size_t *gotcha, char const *prefix, FILE *fo, struct name **xp)
111 struct name *np;
112 enum okay rv = STOP;
113 NYD_ENTER;
115 np = lextract(line, GEXTRA | GFULL);
116 if (xp != NULL)
117 *xp = np;
118 if (np == NULL)
120 else if (fmt(prefix, np, fo, w & GCOMMA, 0, (action != SEND_TODISP)))
121 rv = OKAY;
122 else if (gotcha != NULL)
123 ++(*gotcha);
124 NYD_LEAVE;
125 return rv;
128 static char const *
129 _get_encoding(enum conversion const convert)
131 char const *rv;
132 NYD_ENTER;
134 switch (convert) {
135 case CONV_7BIT: rv = "7bit"; break;
136 case CONV_8BIT: rv = "8bit"; break;
137 case CONV_TOQP: rv = "quoted-printable"; break;
138 case CONV_TOB64: rv = "base64"; break;
139 default: rv = NULL; break;
141 NYD_LEAVE;
142 return rv;
145 static int
146 _attach_file(struct attachment *ap, FILE *fo)
148 /* TODO of course, the MIME classification needs to performed once
149 * TODO only, not for each and every charset anew ... ;-// */
150 char *charset_iter_orig[2];
151 long offs;
152 int err = 0;
153 NYD_ENTER;
155 /* Is this already in target charset? Simply copy over */
156 if (ap->a_conv == AC_TMPFILE) {
157 err = __attach_file(ap, fo);
158 Fclose(ap->a_tmpf);
159 DBG( ap->a_tmpf = NULL; )
160 goto jleave;
163 /* If we don't apply charset conversion at all (fixed input=ouput charset)
164 * we also simply copy over, since it's the users desire */
165 if (ap->a_conv == AC_FIX_INCS) {
166 ap->a_charset = ap->a_input_charset;
167 err = __attach_file(ap, fo);
168 goto jleave;
171 /* Otherwise we need to iterate over all possible output charsets */
172 if ((offs = ftell(fo)) == -1) {
173 err = EIO;
174 goto jleave;
176 charset_iter_recurse(charset_iter_orig);
177 for (charset_iter_reset(NULL);; charset_iter_next()) {
178 if (!charset_iter_is_valid()) {
179 err = EILSEQ;
180 break;
182 err = __attach_file(ap, fo);
183 if (err == 0 || (err != EILSEQ && err != EINVAL))
184 break;
185 clearerr(fo);
186 if (fseek(fo, offs, SEEK_SET) == -1) {
187 err = EIO;
188 break;
190 if (ap->a_conv != AC_DEFAULT) {
191 err = EILSEQ;
192 break;
194 ap->a_charset = NULL;
196 charset_iter_restore(charset_iter_orig);
197 jleave:
198 NYD_LEAVE;
199 return err;
202 static int
203 __attach_file(struct attachment *ap, FILE *fo) /* XXX linelength */
205 int err = 0, do_iconv;
206 FILE *fi;
207 char const *charset;
208 enum conversion convert;
209 char *buf;
210 size_t bufsize, lncnt, inlen;
211 NYD_ENTER;
213 /* Either charset-converted temporary file, or plain path */
214 if (ap->a_conv == AC_TMPFILE) {
215 fi = ap->a_tmpf;
216 assert(ftell(fi) == 0);
217 } else if ((fi = Fopen(ap->a_name, "r")) == NULL) {
218 err = errno;
219 perror(ap->a_name);
220 goto jleave;
223 /* MIME part header for attachment */
224 { char const *bn = ap->a_name, *ct;
226 if ((ct = strrchr(bn, '/')) != NULL)
227 bn = ++ct;
228 ct = ap->a_content_type;
229 charset = ap->a_charset;
230 convert = mime_classify_file(fi, (char const**)&ct, &charset, &do_iconv);
231 if (charset == NULL || ap->a_conv == AC_FIX_INCS ||
232 ap->a_conv == AC_TMPFILE)
233 do_iconv = 0;
235 if (fprintf(fo, "\n--%s\nContent-Type: %s", _sendout_boundary, ct) == -1)
236 goto jerr_header;
238 if (charset == NULL) {
239 if (putc('\n', fo) == EOF)
240 goto jerr_header;
241 } else if (fprintf(fo, "; charset=%s\n", charset) == -1)
242 goto jerr_header;
244 if (fprintf(fo, "Content-Transfer-Encoding: %s\n"
245 "Content-Disposition: %s;\n filename=\"",
246 _get_encoding(convert), ap->a_content_disposition) == -1)
247 goto jerr_header;
248 if (xmime_write(bn, strlen(bn), fo, CONV_TOHDR, TD_NONE, NULL) < 0)
249 goto jerr_header;
250 if (fwrite("\"\n", sizeof(char), 2, fo) != 2 * sizeof(char))
251 goto jerr_header;
253 if ((bn = ap->a_content_id) != NULL &&
254 fprintf(fo, "Content-ID: %s\n", bn) == -1)
255 goto jerr_header;
257 if ((bn = ap->a_content_description) != NULL &&
258 fprintf(fo, "Content-Description: %s\n", bn) == -1)
259 goto jerr_header;
261 if (putc('\n', fo) == EOF) {
262 jerr_header:
263 err = errno;
264 goto jerr_fclose;
268 #ifdef HAVE_ICONV
269 if (iconvd != (iconv_t)-1)
270 n_iconv_close(iconvd);
271 if (do_iconv) {
272 char const *tcs = charset_get_lc();
273 if (asccasecmp(charset, tcs) &&
274 (iconvd = n_iconv_open(charset, tcs)) == (iconv_t)-1 &&
275 (err = errno) != 0) {
276 if (err == EINVAL)
277 fprintf(stderr, tr(179, "Cannot convert from %s to %s\n"),
278 tcs, charset);
279 else
280 perror("iconv_open");
281 goto jerr_fclose;
284 #endif
286 bufsize = INFIX_BUF;
287 buf = smalloc(bufsize);
288 if (convert == CONV_TOQP
289 #ifdef HAVE_ICONV
290 || iconvd != (iconv_t)-1
291 #endif
293 lncnt = fsize(fi);
294 for (;;) {
295 if (convert == CONV_TOQP
296 #ifdef HAVE_ICONV
297 || iconvd != (iconv_t)-1
298 #endif
300 if (fgetline(&buf, &bufsize, &lncnt, &inlen, fi, 0) == NULL)
301 break;
302 } else if ((inlen = fread(buf, sizeof *buf, bufsize, fi)) == 0)
303 break;
304 if (xmime_write(buf, inlen, fo, convert, TD_ICONV, NULL) < 0) {
305 err = errno;
306 goto jerr;
309 if (ferror(fi))
310 err = EDOM;
311 jerr:
312 free(buf);
313 jerr_fclose:
314 if (ap->a_conv != AC_TMPFILE)
315 Fclose(fi);
316 jleave:
317 NYD_LEAVE;
318 return err;
321 static char const **
322 _prepare_mta_args(struct name *to, struct header *hp)
324 size_t j, i;
325 char const **args;
326 NYD_ENTER;
328 i = 4 + smopts_count + 2 + count(to) + 1;
329 args = salloc(i * sizeof(char*));
331 args[0] = ok_vlook(sendmail_progname);
332 if (args[0] == NULL || *args[0] == '\0')
333 args[0] = SENDMAIL_PROGNAME;
335 args[1] = "-i";
336 i = 2;
337 if (ok_blook(metoo))
338 args[i++] = "-m";
339 if (options & OPT_VERBOSE)
340 args[i++] = "-v";
342 for (j = 0; j < smopts_count; ++j, ++i)
343 args[i] = smopts[j];
345 /* -r option? We may only pass skinned addresses, which is why we do
346 * not simply call myorigin() (TODO myorigin shouldn't fullname!) */
347 if (options & OPT_r_FLAG) {
348 char const *froma;
350 if (option_r_arg[0] != '\0')
351 froma = option_r_arg;
352 else if (hp->h_from != NULL)
353 froma = hp->h_from->n_name;
354 else
355 froma = myorigin(hp);
356 if (froma != NULL) {
357 args[i++] = "-r";
358 args[i++] = froma;
362 /* Receivers follow */
363 for (; to != NULL; to = to->n_flink)
364 if (!(to->n_type & GDEL))
365 args[i++] = to->n_name;
366 args[i] = NULL;
367 NYD_LEAVE;
368 return args;
371 static struct name *
372 fixhead(struct header *hp, struct name *tolist)
374 struct name **npp, *np;
375 NYD_ENTER;
377 tolist = elide(tolist);
379 hp->h_to = hp->h_cc = hp->h_bcc = NULL;
380 for (np = tolist; np != NULL; np = np->n_flink) {
381 switch (np->n_type & (GDEL | GMASK)) {
382 case GTO: npp = &hp->h_to; break;
383 case GCC: npp = &hp->h_cc; break;
384 case GBCC: npp = &hp->h_bcc; break;
385 default: continue;
387 *npp = cat(*npp, ndup(np, np->n_type | GFULL));
389 NYD_LEAVE;
390 return tolist;
393 static int
394 put_signature(FILE *fo, int convert)
396 char buf[INFIX_BUF], *sig, c = '\n';
397 FILE *fsig;
398 size_t sz;
399 int rv;
400 NYD_ENTER;
402 if ((sig = ok_vlook(signature)) == NULL || *sig == '\0') {
403 rv = 0;
404 goto jleave;
406 rv = -1;
408 if ((sig = file_expand(sig)) == NULL)
409 goto jleave;
411 if ((fsig = Fopen(sig, "r")) == NULL) {
412 perror(sig);
413 goto jleave;
415 while ((sz = fread(buf, sizeof *buf, INFIX_BUF, fsig)) != 0) {
416 c = buf[sz - 1];
417 if (xmime_write(buf, sz, fo, convert, TD_NONE, NULL) < 0)
418 goto jerr;
420 if (ferror(fsig)) {
421 jerr:
422 perror(sig);
423 Fclose(fsig);
424 goto jleave;
426 Fclose(fsig);
427 if (c != '\n')
428 putc('\n', fo);
430 rv = 0;
431 jleave:
432 NYD_LEAVE;
433 return rv;
436 static int
437 attach_message(struct attachment *ap, FILE *fo)
439 struct message *mp;
440 char const *ccp;
441 int rv;
442 NYD_ENTER;
444 fprintf(fo, "\n--%s\nContent-Type: message/rfc822\n"
445 "Content-Disposition: inline\n", _sendout_boundary);
446 if ((ccp = ap->a_content_description) != NULL)
447 fprintf(fo, "Content-Description: %s\n", ccp);
448 fputc('\n', fo);
450 mp = message + ap->a_msgno - 1;
451 touch(mp);
452 rv = (sendmp(mp, fo, 0, NULL, SEND_RFC822, NULL) < 0) ? -1 : 0;
453 NYD_LEAVE;
454 return rv;
457 static int
458 make_multipart(struct header *hp, int convert, FILE *fi, FILE *fo,
459 char const *contenttype, char const *charset)
461 struct attachment *att;
462 int rv = -1;
463 NYD_ENTER;
465 fputs("This is a multi-part message in MIME format.\n", fo);
466 if (fsize(fi) != 0) {
467 char *buf;
468 size_t sz, bufsize, cnt;
470 fprintf(fo, "\n--%s\n", _sendout_boundary);
471 fprintf(fo, "Content-Type: %s", contenttype);
472 if (charset != NULL)
473 fprintf(fo, "; charset=%s", charset);
474 fprintf(fo, "\nContent-Transfer-Encoding: %s\n"
475 "Content-Disposition: inline\n\n", _get_encoding(convert));
477 buf = smalloc(bufsize = INFIX_BUF);
478 if (convert == CONV_TOQP
479 #ifdef HAVE_ICONV
480 || iconvd != (iconv_t)-1
481 #endif
483 fflush(fi);
484 cnt = fsize(fi);
486 for (;;) {
487 if (convert == CONV_TOQP
488 #ifdef HAVE_ICONV
489 || iconvd != (iconv_t)-1
490 #endif
492 if (fgetline(&buf, &bufsize, &cnt, &sz, fi, 0) == NULL)
493 break;
494 } else if ((sz = fread(buf, sizeof *buf, bufsize, fi)) == 0)
495 break;
497 if (xmime_write(buf, sz, fo, convert, TD_ICONV, NULL) < 0) {
498 free(buf);
499 goto jleave;
502 free(buf);
504 if (ferror(fi))
505 goto jleave;
506 if (charset != NULL)
507 put_signature(fo, convert);
510 for (att = hp->h_attach; att != NULL; att = att->a_flink) {
511 if (att->a_msgno) {
512 if (attach_message(att, fo) != 0)
513 goto jleave;
514 } else if (_attach_file(att, fo) != 0)
515 goto jleave;
518 /* the final boundary with two attached dashes */
519 fprintf(fo, "\n--%s--\n", _sendout_boundary);
520 rv = 0;
521 jleave:
522 NYD_LEAVE;
523 return rv;
526 static FILE *
527 infix(struct header *hp, FILE *fi) /* TODO check */
529 FILE *nfo, *nfi = NULL;
530 char *tempMail;
531 char const *contenttype, *charset = NULL;
532 enum conversion convert;
533 int do_iconv = 0, err;
534 #ifdef HAVE_ICONV
535 char const *tcs, *convhdr = NULL;
536 #endif
537 NYD_ENTER;
539 if ((nfo = Ftmp(&tempMail, "infix", OF_WRONLY | OF_HOLDSIGS | OF_REGISTER,
540 0600)) == NULL) {
541 perror(tr(178, "temporary mail file"));
542 goto jleave;
544 if ((nfi = Fopen(tempMail, "r")) == NULL) {
545 perror(tempMail);
546 Fclose(nfo);
548 Ftmp_release(&tempMail);
549 if (nfi == NULL)
550 goto jleave;
552 contenttype = "text/plain"; /* XXX mail body - always text/plain, want XX? */
553 convert = mime_classify_file(fi, &contenttype, &charset, &do_iconv);
555 #ifdef HAVE_ICONV
556 tcs = charset_get_lc();
557 if ((convhdr = need_hdrconv(hp, GTO | GSUBJECT | GCC | GBCC | GIDENT))) {
558 if (iconvd != (iconv_t)-1) /* XXX */
559 n_iconv_close(iconvd);
560 if (asccasecmp(convhdr, tcs) != 0 &&
561 (iconvd = n_iconv_open(convhdr, tcs)) == (iconv_t)-1 &&
562 (err = errno) != 0)
563 goto jiconv_err;
565 #endif
566 if (puthead(hp, nfo,
567 (GTO | GSUBJECT | GCC | GBCC | GNL | GCOMMA | GUA | GMIME | GMSGID |
568 GIDENT | GREF | GDATE), SEND_MBOX, convert, contenttype, charset))
569 goto jerr;
570 #ifdef HAVE_ICONV
571 if (iconvd != (iconv_t)-1)
572 n_iconv_close(iconvd);
573 #endif
575 #ifdef HAVE_ICONV
576 if (do_iconv && charset != NULL) { /*TODO charset->mime_classify_file*/
577 if (asccasecmp(charset, tcs) != 0 &&
578 (iconvd = n_iconv_open(charset, tcs)) == (iconv_t)-1 &&
579 (err = errno) != 0) {
580 jiconv_err:
581 if (err == EINVAL)
582 fprintf(stderr, tr(179, "Cannot convert from %s to %s\n"),
583 tcs, charset);
584 else
585 perror("iconv_open");
586 goto jerr;
589 #endif
591 if (hp->h_attach != NULL) {
592 if (make_multipart(hp, convert, fi, nfo, contenttype, charset) != 0)
593 goto jerr;
594 } else {
595 size_t sz, bufsize, cnt;
596 char *buf;
598 if (convert == CONV_TOQP
599 #ifdef HAVE_ICONV
600 || iconvd != (iconv_t)-1
601 #endif
603 fflush(fi);
604 cnt = fsize(fi);
606 buf = smalloc(bufsize = INFIX_BUF);
607 for (err = 0;;) {
608 if (convert == CONV_TOQP
609 #ifdef HAVE_ICONV
610 || iconvd != (iconv_t)-1
611 #endif
613 if (fgetline(&buf, &bufsize, &cnt, &sz, fi, 0) == NULL)
614 break;
615 } else if ((sz = fread(buf, sizeof *buf, bufsize, fi)) == 0)
616 break;
617 if (xmime_write(buf, sz, nfo, convert, TD_ICONV, NULL) < 0) {
618 err = 1;
619 break;
622 free(buf);
624 if (err || ferror(fi)) {
625 jerr:
626 Fclose(nfo);
627 Fclose(nfi);
628 #ifdef HAVE_ICONV
629 if (iconvd != (iconv_t)-1)
630 n_iconv_close(iconvd);
631 #endif
632 nfi = NULL;
633 goto jleave;
635 if (charset != NULL)
636 put_signature(nfo, convert); /* XXX if (text/) !! */
639 #ifdef HAVE_ICONV
640 if (iconvd != (iconv_t)-1)
641 n_iconv_close(iconvd);
642 #endif
644 fflush(nfo);
645 if ((err = ferror(nfo)))
646 perror(tr(180, "temporary mail file"));
647 Fclose(nfo);
648 if (!err) {
649 fflush_rewind(nfi);
650 Fclose(fi);
651 } else {
652 Fclose(nfi);
653 nfi = NULL;
655 jleave:
656 NYD_LEAVE;
657 return nfi;
660 static int
661 savemail(char const *name, FILE *fi)
663 FILE *fo;
664 char *buf;
665 size_t bufsize, buflen, cnt;
666 int prependnl = 0, rv = -1;
667 NYD_ENTER;
669 buf = smalloc(bufsize = LINESIZE);
671 if ((fo = Zopen(name, "a+", NULL)) == NULL) {
672 if ((fo = Zopen(name, "wx", NULL)) == NULL) {
673 perror(name);
674 goto jleave;
676 } else {
677 if (fseek(fo, -2L, SEEK_END) == 0) {
678 switch (fread(buf, sizeof *buf, 2, fo)) {
679 case 2:
680 if (buf[1] != '\n') {
681 prependnl = 1;
682 break;
684 /* FALLTHRU */
685 case 1:
686 if (buf[0] != '\n')
687 prependnl = 1;
688 break;
689 default:
690 if (ferror(fo)) {
691 perror(name);
692 goto jleave;
695 if (prependnl) {
696 putc('\n', fo);
698 fflush(fo);
702 fprintf(fo, "From %s %s", myname, time_current.tc_ctime);
703 fflush_rewind(fi);
704 cnt = fsize(fi);
705 buflen = 0;
706 while (fgetline(&buf, &bufsize, &cnt, &buflen, fi, 0) != NULL) {
707 #ifdef HAVE_DEBUG /* TODO assert legacy */
708 assert(!is_head(buf, buflen));
709 #else
710 if (is_head(buf, buflen))
711 putc('>', fo);
712 #endif
713 fwrite(buf, sizeof *buf, buflen, fo);
715 if (buflen && *(buf + buflen - 1) != '\n')
716 putc('\n', fo);
717 putc('\n', fo);
718 fflush(fo);
720 rv = 0;
721 if (ferror(fo)) {
722 perror(name);
723 rv = -1;
725 if (Fclose(fo) != 0)
726 rv = -1;
727 fflush_rewind(fi);
728 jleave:
729 free(buf);
730 NYD_LEAVE;
731 return rv;
734 static int
735 sendmail_internal(void *v, int recipient_record)
737 struct header head;
738 char *str = v;
739 int rv;
740 NYD_ENTER;
742 memset(&head, 0, sizeof head);
743 head.h_to = lextract(str, GTO | GFULL);
744 rv = mail1(&head, 0, NULL, NULL, recipient_record, 0);
745 NYD_LEAVE;
746 return rv;
749 static enum okay
750 transfer(struct name *to, FILE *input, struct header *hp)
752 char o[LINESIZE], *cp;
753 struct name *np;
754 int cnt = 0;
755 enum okay rv = OKAY;
756 NYD_ENTER;
758 np = to;
759 while (np != NULL) {
760 snprintf(o, sizeof o, "smime-encrypt-%s", np->n_name);/* XXX */
761 if ((cp = vok_vlook(o)) != NULL) {
762 #ifdef HAVE_SSL
763 struct name *nt;
764 FILE *ef;
766 if ((ef = smime_encrypt(input, cp, np->n_name)) != NULL) {
767 nt = ndup(np, np->n_type & ~(GFULL | GSKIN));
768 rv = start_mta(nt, ef, hp);
769 Fclose(ef);
770 } else {
771 #else
772 fprintf(stderr, tr(225, "No SSL support compiled in.\n"));
773 rv = STOP;
774 #endif
775 fprintf(stderr, tr(38, "Message not sent to <%s>\n"), np->n_name);
776 _sendout_error = TRU1;
777 #ifdef HAVE_SSL
779 #endif
780 rewind(input);
782 if (np->n_flink != NULL)
783 np->n_flink->n_blink = np->n_blink;
784 if (np->n_blink != NULL)
785 np->n_blink->n_flink = np->n_flink;
786 if (np == to)
787 to = np->n_flink;
788 np = np->n_flink;
789 } else {
790 ++cnt;
791 np = np->n_flink;
795 if (cnt)
796 if (ok_blook(smime_force_encryption) || start_mta(to, input, hp) != OKAY)
797 rv = STOP;
798 NYD_LEAVE;
799 return rv;
802 static enum okay
803 start_mta(struct name *to, FILE *input, struct header *hp)
805 #ifdef HAVE_SMTP
806 char *user = NULL, *password = NULL, *skinned = NULL;
807 #endif
808 char const **args = NULL, **t, *mta;
809 char *smtp;
810 pid_t pid;
811 sigset_t nset;
812 enum okay rv = STOP;
813 NYD_ENTER;
814 UNUSED(hp);
816 if ((smtp = ok_vlook(smtp)) == NULL) {
817 if ((mta = ok_vlook(sendmail)) != NULL) {
818 if ((mta = file_expand(mta)) == NULL)
819 goto jstop;
820 } else
821 mta = SENDMAIL;
823 args = _prepare_mta_args(to, hp);
824 if (options & OPT_DEBUG) {
825 printf(tr(181, "Sendmail arguments:"));
826 for (t = args; *t != NULL; ++t)
827 printf(" \"%s\"", *t);
828 printf("\n");
829 rv = OKAY;
830 goto jleave;
832 } else {
833 mta = NULL; /* Silence cc */
834 #ifndef HAVE_SMTP
835 fputs(tr(194, "No SMTP support compiled in.\n"), stderr);
836 goto jstop;
837 #else
838 skinned = skin(myorigin(hp));
839 if ((user = smtp_auth_var("-user", skinned)) != NULL &&
840 (password = smtp_auth_var("-password", skinned)) == NULL)
841 password = getpassword(NULL);
842 #endif
845 /* Fork, set up the temporary mail file as standard input for "mail", and
846 * exec with the user list we generated far above */
847 if ((pid = fork()) == -1) {
848 perror("fork");
849 jstop:
850 savedeadletter(input, 0);
851 _sendout_error = TRU1;
852 goto jleave;
854 if (pid == 0) {
855 sigemptyset(&nset);
856 sigaddset(&nset, SIGHUP);
857 sigaddset(&nset, SIGINT);
858 sigaddset(&nset, SIGQUIT);
859 sigaddset(&nset, SIGTSTP);
860 sigaddset(&nset, SIGTTIN);
861 sigaddset(&nset, SIGTTOU);
862 freopen("/dev/null", "r", stdin);
863 #ifdef HAVE_SMTP
864 if (smtp != NULL) {
865 prepare_child(&nset, 0, 1);
866 if (smtp_mta(smtp, to, input, hp, user, password, skinned) == 0)
867 _exit(0);
868 } else {
869 #endif
870 prepare_child(&nset, fileno(input), -1);
871 /* If *record* is set then savemail() will move the file position;
872 * it'll call rewind(), but that may optimize away the systemcall if
873 * possible, and since dup2() shares the position with the original FD
874 * the MTA may end up reading nothing */
875 lseek(0, 0, SEEK_SET);
876 execv(mta, UNCONST(args));
877 perror(mta);
878 #ifdef HAVE_SMTP
880 #endif
881 savedeadletter(input, 1);
882 fputs(tr(182, ". . . message not sent.\n"), stderr);
883 _exit(1);
885 if ((options & (OPT_DEBUG | OPT_VERBOSE | OPT_BATCH_FLAG)) ||
886 ok_blook(sendwait)) {
887 if (wait_child(pid, NULL))
888 rv = OKAY;
889 else
890 _sendout_error = TRU1;
891 } else {
892 rv = OKAY;
893 free_child(pid);
895 jleave:
896 NYD_LEAVE;
897 return rv;
900 static enum okay
901 mightrecord(FILE *fp, struct name *to)
903 char *cp, *cq;
904 char const *ep;
905 enum okay rv = OKAY;
906 NYD_ENTER;
908 if (to != NULL) {
909 cp = savestr(skinned_name(to));
910 for (cq = cp; *cq != '\0' && *cq != '@'; ++cq)
912 *cq = '\0';
913 } else
914 cp = ok_vlook(record);
916 if (cp != NULL) {
917 if ((ep = expand(cp)) == NULL) {
918 ep = "NULL";
919 goto jbail;
922 if (*ep != '/' && *ep != '+' && ok_blook(outfolder) &&
923 which_protocol(ep) == PROTO_FILE) {
924 size_t i = strlen(cp);
925 cq = salloc(i + 1 +1);
926 cq[0] = '+';
927 memcpy(cq + 1, cp, i +1);
928 cp = cq;
929 if ((ep = file_expand(cp)) == NULL) {
930 ep = "NULL";
931 goto jbail;
935 if (savemail(ep, fp) != 0) {
936 jbail:
937 fprintf(stderr, tr(285, "Failed to save message in %s - "
938 "message not sent\n"), ep);
939 exit_status |= EXIT_ERR;
940 savedeadletter(fp, 1);
941 rv = STOP;
944 NYD_LEAVE;
945 return rv;
948 static void
949 message_id(FILE *fo, struct header *hp)
951 char const *h;
952 size_t rl;
953 struct tm *tmp;
954 NYD_ENTER;
956 if (ok_blook(message_id_disable))
957 goto jleave;
959 if ((h = ok_vlook(hostname)) != NULL)
960 rl = 24;
961 else if ((h = skin(myorigin(hp))) != NULL && strchr(h, '@') != NULL)
962 rl = 16;
963 else
964 /* Up to MTA */
965 goto jleave;
967 tmp = &time_current.tc_gm;
968 fprintf(fo, "Message-ID: <%04d%02d%02d%02d%02d%02d.%s%c%s>\n",
969 tmp->tm_year + 1900, tmp->tm_mon + 1, tmp->tm_mday,
970 tmp->tm_hour, tmp->tm_min, tmp->tm_sec,
971 getrandstring(rl), (rl == 16 ? '%' : '@'), h);
972 jleave:
973 NYD_LEAVE;
976 static int
977 fmt(char const *str, struct name *np, FILE *fo, int flags, int dropinvalid,
978 int domime)
980 enum {
981 m_INIT = 1<<0,
982 m_COMMA = 1<<1,
983 m_NOPF = 1<<2,
984 m_CSEEN = 1<<3
985 } m = (flags & GCOMMA) ? m_COMMA : 0;
986 ssize_t col, len;
987 int rv = 1;
988 NYD_ENTER;
990 col = strlen(str);
991 if (col) {
992 fwrite(str, sizeof *str, col, fo);
993 if (flags & GFILES)
994 goto jstep;
995 if (col == 9 && !asccasecmp(str, "reply-to:")) {
996 m |= m_NOPF;
997 goto jstep;
999 if (ok_blook(add_file_recipients))
1000 goto jstep;
1001 if ((col == 3 && (!asccasecmp(str, "to:") || !asccasecmp(str, "cc:"))) ||
1002 (col == 4 && !asccasecmp(str, "bcc:")) ||
1003 (col == 10 && !asccasecmp(str, "Resent-To:")))
1004 m |= m_NOPF;
1006 jstep:
1007 for (; np != NULL; np = np->n_flink) {
1008 if ((m & m_NOPF) && is_fileorpipe_addr(np))
1009 continue;
1010 if (is_addr_invalid(np, !dropinvalid)) {
1011 if (dropinvalid)
1012 continue;
1013 else
1014 goto jleave;
1016 if ((m & (m_INIT | m_COMMA)) == (m_INIT | m_COMMA)) {
1017 putc(',', fo);
1018 m |= m_CSEEN;
1019 ++col;
1021 len = strlen(np->n_fullname);
1022 ++col; /* The separating space */
1023 if ((m & m_INIT) && col > 1 && col + len > 72) {
1024 fputs("\n ", fo);
1025 col = 1;
1026 m &= ~m_CSEEN;
1027 } else
1028 putc(' ', fo);
1029 m = (m & ~m_CSEEN) | m_INIT;
1030 len = xmime_write(np->n_fullname, len, fo,
1031 (domime ? CONV_TOHDR_A : CONV_NONE), TD_ICONV, NULL);
1032 if (len < 0)
1033 goto jleave;
1034 col += len;
1036 putc('\n', fo);
1037 rv = 0;
1038 jleave:
1039 NYD_LEAVE;
1040 return rv;
1043 static int
1044 infix_resend(FILE *fi, FILE *fo, struct message *mp, struct name *to,
1045 int add_resent)
1047 size_t cnt, c, bufsize = 0;
1048 char *buf = NULL;
1049 char const *cp;
1050 struct name *fromfield = NULL, *senderfield = NULL;
1051 int rv = 1;
1052 NYD_ENTER;
1054 cnt = mp->m_size;
1056 /* Write the Resent-Fields */
1057 if (add_resent) {
1058 fputs("Resent-", fo);
1059 mkdate(fo, "Date");
1060 if ((cp = myaddrs(NULL)) != NULL) {
1061 if (_putname(cp, GCOMMA, SEND_MBOX, NULL, "Resent-From:", fo,
1062 &fromfield))
1063 goto jleave;
1065 if ((cp = ok_vlook(sender)) != NULL) {
1066 if (_putname(cp, GCOMMA, SEND_MBOX, NULL, "Resent-Sender:", fo,
1067 &senderfield))
1068 goto jleave;
1070 if (fmt("Resent-To:", to, fo, 1, 1, 0))
1071 goto jleave;
1072 if ((cp = ok_vlook(stealthmua)) == NULL || !strcmp(cp, "noagent")) {
1073 fputs("Resent-", fo);
1074 message_id(fo, NULL);
1077 if (check_from_and_sender(fromfield, senderfield))
1078 goto jleave;
1080 /* Write the original headers */
1081 while (cnt > 0) {
1082 if (fgetline(&buf, &bufsize, &cnt, &c, fi, 0) == NULL)
1083 break;
1084 /* XXX more checks: The From_ line may be seen when resending */
1085 /* During headers is_head() is actually overkill, so ^From_ is sufficient
1086 * && !is_head(buf, c) */
1087 if (ascncasecmp("status: ", buf, 8) && strncmp("From ", buf, 5))
1088 fwrite(buf, sizeof *buf, c, fo);
1089 if (cnt > 0 && *buf == '\n')
1090 break;
1093 /* Write the message body */
1094 while (cnt > 0) {
1095 if (fgetline(&buf, &bufsize, &cnt, &c, fi, 0) == NULL)
1096 break;
1097 if (cnt == 0 && *buf == '\n')
1098 break;
1099 fwrite(buf, sizeof *buf, c, fo);
1101 if (buf != NULL)
1102 free(buf);
1103 if (ferror(fo)) {
1104 perror(tr(188, "temporary mail file"));
1105 goto jleave;
1107 rv = 0;
1108 jleave:
1109 NYD_LEAVE;
1110 return rv;
1113 FL int
1114 mail(struct name *to, struct name *cc, struct name *bcc, char *subject,
1115 struct attachment *attach, char *quotefile, int recipient_record)
1117 struct header head;
1118 struct str in, out;
1119 NYD_ENTER;
1121 memset(&head, 0, sizeof head);
1123 /* The given subject may be in RFC1522 format. */
1124 if (subject != NULL) {
1125 in.s = subject;
1126 in.l = strlen(subject);
1127 mime_fromhdr(&in, &out, /* TODO ??? TD_ISPR |*/ TD_ICONV);
1128 head.h_subject = out.s;
1130 if (!(options & OPT_t_FLAG)) {
1131 head.h_to = to;
1132 head.h_cc = cc;
1133 head.h_bcc = bcc;
1135 head.h_attach = attach;
1137 mail1(&head, 0, NULL, quotefile, recipient_record, 0);
1139 if (subject != NULL)
1140 free(out.s);
1141 NYD_LEAVE;
1142 return 0;
1145 FL int
1146 c_sendmail(void *v)
1148 int rv;
1149 NYD_ENTER;
1151 rv = sendmail_internal(v, 0);
1152 NYD_LEAVE;
1153 return rv;
1156 FL int
1157 c_Sendmail(void *v)
1159 int rv;
1160 NYD_ENTER;
1162 rv = sendmail_internal(v, 1);
1163 NYD_LEAVE;
1164 return rv;
1167 FL enum okay
1168 mail1(struct header *hp, int printheaders, struct message *quote,
1169 char *quotefile, int recipient_record, int doprefix)
1171 struct name *to;
1172 FILE *mtf, *nmtf;
1173 int dosign = -1, err;
1174 char const *cp;
1175 enum okay rv = STOP;
1176 NYD_ENTER;
1178 _sendout_error = FAL0;
1180 /* Update some globals we likely need first */
1181 time_current_update(&time_current, TRU1);
1183 /* */
1184 if ((cp = ok_vlook(autocc)) != NULL && *cp != '\0')
1185 hp->h_cc = cat(hp->h_cc, checkaddrs(lextract(cp, GCC | GFULL)));
1186 if ((cp = ok_vlook(autobcc)) != NULL && *cp != '\0')
1187 hp->h_bcc = cat(hp->h_bcc, checkaddrs(lextract(cp, GBCC | GFULL)));
1189 /* Collect user's mail from standard input. Get the result as mtf */
1190 mtf = collect(hp, printheaders, quote, quotefile, doprefix);
1191 if (mtf == NULL)
1192 goto j_leave;
1194 if (options & OPT_INTERACTIVE) {
1195 err = (ok_blook(bsdcompat) || ok_blook(askatend));
1196 if (err == 0)
1197 goto jaskeot;
1198 if (ok_blook(askcc))
1199 ++err, grab_headers(hp, GCC, 1);
1200 if (ok_blook(askbcc))
1201 ++err, grab_headers(hp, GBCC, 1);
1202 if (ok_blook(askattach))
1203 ++err, edit_attachments(&hp->h_attach);
1204 if (ok_blook(asksign))
1205 ++err, dosign = yorn(tr(35, "Sign this message (y/n)? "));
1206 if (err == 1) {
1207 jaskeot:
1208 printf(tr(183, "EOT\n"));
1209 fflush(stdout);
1213 if (fsize(mtf) == 0) {
1214 if (options & OPT_E_FLAG)
1215 goto jleave;
1216 if (hp->h_subject == NULL)
1217 printf(tr(184, "No message, no subject; hope that's ok\n"));
1218 else if (ok_blook(bsdcompat) || ok_blook(bsdmsgs))
1219 printf(tr(185, "Null message body; hope that's ok\n"));
1222 if (dosign < 0)
1223 dosign = ok_blook(smime_sign);
1224 #ifndef HAVE_SSL
1225 if (dosign) {
1226 fprintf(stderr, tr(225, "No SSL support compiled in.\n"));
1227 goto jleave;
1229 #endif
1231 /* XXX Update time_current again; once collect() offers editing of more
1232 * XXX headers, including Date:, this must only happen if Date: is the
1233 * XXX same that it was before collect() (e.g., postponing etc.).
1234 * XXX But *do* update otherwise because the mail seems to be backdated
1235 * XXX if the user edited some time, which looks odd and it happened
1236 * XXX to me that i got mis-dated response mails due to that... */
1237 time_current_update(&time_current, TRU1);
1239 /* TODO hrmpf; the MIME/send layer rewrite MUST address the init crap:
1240 * TODO setup the header ONCE; note this affects edit.c, collect.c ...,
1241 * TODO but: offer a hook that rebuilds/expands/checks/fixates all
1242 * TODO header fields ONCE, call that ONCE after user editing etc. has
1243 * TODO completed (one edit cycle) */
1245 /* Take the user names from the combined to and cc lists and do all the
1246 * alias processing. The POSIX standard says:
1247 * The names shall be substituted when alias is used as a recipient
1248 * address specified by the user in an outgoing message (that is,
1249 * other recipients addressed indirectly through the reply command
1250 * shall not be substituted in this manner).
1251 * S-nail thus violates POSIX, as has been pointed out correctly by
1252 * Martin Neitzel, but logic and usability of POSIX standards is not seldom
1253 * disputable anyway. Go for user friendliness */
1255 /* Do alias expansion on Reply-To: members, too */
1256 /* TODO puthead() YET (!!! see ONCE note above) expands the value, but
1257 * TODO doesn't perform alias expansion; encapsulate in the ONCE-o */
1258 if (hp->h_replyto == NULL && (cp = ok_vlook(replyto)) != NULL)
1259 hp->h_replyto = checkaddrs(lextract(cp, GEXTRA | GFULL));
1260 if (hp->h_replyto != NULL)
1261 hp->h_replyto = elide(usermap(hp->h_replyto, TRU1));
1263 /* TODO what happens now is that all recipients are merged into
1264 * TODO a duplicated list with expanded aliases, then this list is
1265 * TODO splitted again into the three individual recipient lists (with
1266 * TODO duplicates removed).
1267 * TODO later on we use the merged list for outof() pipe/file saving,
1268 * TODO then we eliminate duplicates (again) and then we use that one
1269 * TODO for mightrecord() and transfer(), and again. ... Please ... */
1271 /* NOTE: Due to elide() in fixhead(), ENSURE to,cc,bcc order of to!,
1272 * because otherwise the recipients will be "degraded" if they occur
1273 * multiple times */
1274 to = usermap(cat(hp->h_to, cat(hp->h_cc, hp->h_bcc)), FAL0);
1275 if (to == NULL) {
1276 fprintf(stderr, tr(186, "No recipients specified\n"));
1277 _sendout_error = TRU1;
1279 to = fixhead(hp, to);
1281 /* 'Bit ugly kind of control flow until we find a charset that does it */
1282 for (charset_iter_reset(hp->h_charset);; charset_iter_next()) {
1283 if (!charset_iter_is_valid())
1285 else if ((nmtf = infix(hp, mtf)) != NULL)
1286 break;
1287 else if ((err = errno) == EILSEQ || err == EINVAL) {
1288 rewind(mtf);
1289 continue;
1292 perror("");
1293 #ifdef HAVE_SSL
1294 jfail_dead:
1295 #endif
1296 _sendout_error = TRU1;
1297 savedeadletter(mtf, 1);
1298 fputs(tr(187, ". . . message not sent.\n"), stderr);
1299 goto jleave;
1302 mtf = nmtf;
1303 #ifdef HAVE_SSL
1304 if (dosign) {
1305 if ((nmtf = smime_sign(mtf, hp)) == NULL)
1306 goto jfail_dead;
1307 Fclose(mtf);
1308 mtf = nmtf;
1310 #endif
1312 /* TODO truly - i still don't get what follows: (1) we deliver file
1313 * TODO and pipe addressees, (2) we mightrecord() and (3) we transfer
1314 * TODO even if (1) savedeadletter() etc. To me this doesn't make sense? */
1316 /* Deliver pipe and file addressees */
1317 to = outof(to, mtf, hp, &_sendout_error);
1318 if (_sendout_error)
1319 savedeadletter(mtf, 0);
1321 to = elide(to); /* XXX needed only to drop GDELs due to outof()! */
1322 dosign = (int)count(to); /* XXX ui32; reuse as "do-transfer" */
1323 if ((!recipient_record || dosign) &&
1324 mightrecord(mtf, (recipient_record ? to : NULL)) != OKAY)
1325 goto jleave;
1326 if (dosign)
1327 rv = transfer(to, mtf, hp);
1328 else if (!_sendout_error)
1329 rv = OKAY;
1331 jleave:
1332 Fclose(mtf);
1333 j_leave:
1334 if (_sendout_error)
1335 exit_status |= EXIT_SEND_ERROR;
1336 NYD_LEAVE;
1337 return rv;
1340 FL int
1341 mkdate(FILE *fo, char const *field)
1343 struct tm *tmptr;
1344 int tzdiff, tzdiff_hour, tzdiff_min, rv;
1345 NYD_ENTER;
1347 tzdiff = time_current.tc_time - mktime(&time_current.tc_gm);
1348 tzdiff_hour = (int)(tzdiff / 60);
1349 tzdiff_min = tzdiff_hour % 60;
1350 tzdiff_hour /= 60;
1351 tmptr = &time_current.tc_local;
1352 if (tmptr->tm_isdst > 0)
1353 ++tzdiff_hour;
1354 rv = fprintf(fo, "%s: %s, %02d %s %04d %02d:%02d:%02d %+05d\n",
1355 field,
1356 weekday_names[tmptr->tm_wday],
1357 tmptr->tm_mday, month_names[tmptr->tm_mon],
1358 tmptr->tm_year + 1900, tmptr->tm_hour,
1359 tmptr->tm_min, tmptr->tm_sec,
1360 tzdiff_hour * 100 + tzdiff_min);
1361 NYD_LEAVE;
1362 return rv;
1365 FL int
1366 puthead(struct header *hp, FILE *fo, enum gfield w, enum sendaction action,
1367 enum conversion convert, char const *contenttype, char const *charset)
1369 #define FMT_CC_AND_BCC() \
1370 do {\
1371 if (hp->h_cc != NULL && (w & GCC)) {\
1372 if (fmt("Cc:", hp->h_cc, fo, (w & (GCOMMA | GFILES)), 0,\
1373 (action != SEND_TODISP)))\
1374 goto jleave;\
1375 ++gotcha;\
1377 if (hp->h_bcc != NULL && (w & GBCC)) {\
1378 if (fmt("Bcc:", hp->h_bcc, fo, (w & (GCOMMA | GFILES)), 0,\
1379 (action != SEND_TODISP)))\
1380 goto jleave;\
1381 ++gotcha;\
1383 } while (0)
1385 char const *addr;
1386 size_t gotcha, l;
1387 struct name *np, *fromfield = NULL, *senderfield = NULL;
1388 int stealthmua, rv = 1;
1389 bool_t nodisp;
1390 NYD_ENTER;
1392 if ((addr = ok_vlook(stealthmua)) != NULL)
1393 stealthmua = !strcmp(addr, "noagent") ? -1 : 1;
1394 else
1395 stealthmua = 0;
1396 gotcha = 0;
1397 nodisp = (action != SEND_TODISP);
1399 if (w & GDATE)
1400 mkdate(fo, "Date"), ++gotcha;
1401 if (w & GIDENT) {
1402 if (hp->h_from != NULL) {
1403 if (fmt("From:", hp->h_from, fo, (w & (GCOMMA | GFILES)), 0, nodisp))
1404 goto jleave;
1405 ++gotcha;
1406 fromfield = hp->h_from;
1407 } else if ((addr = myaddrs(hp)) != NULL) {
1408 if (_putname(addr, w, action, &gotcha, "From:", fo, &fromfield))
1409 goto jleave;
1410 hp->h_from = fromfield;
1413 if (((addr = hp->h_organization) != NULL ||
1414 (addr = ok_vlook(ORGANIZATION)) != NULL) &&
1415 (l = strlen(addr)) > 0) {
1416 fwrite("Organization: ", sizeof(char), 14, fo);
1417 if (xmime_write(addr, l, fo, (!nodisp ? CONV_NONE : CONV_TOHDR),
1418 (!nodisp ? TD_ISPR | TD_ICONV : TD_ICONV), NULL) < 0)
1419 goto jleave;
1420 ++gotcha;
1421 putc('\n', fo);
1424 /* TODO see the ONCE TODO note somewhere around this file;
1425 * TODO but anyway, do NOT perform alias expansion UNLESS
1426 * TODO we are actually sending out! */
1427 if (hp->h_replyto != NULL) {
1428 if (fmt("Reply-To:", hp->h_replyto, fo, w & GCOMMA, 0, nodisp))
1429 goto jleave;
1430 ++gotcha;
1431 } else if ((addr = ok_vlook(replyto)) != NULL)
1432 if (_putname(addr, w, action, &gotcha, "Reply-To:", fo, NULL))
1433 goto jleave;
1435 if (hp->h_sender != NULL) {
1436 if (fmt("Sender:", hp->h_sender, fo, w & GCOMMA, 0, nodisp))
1437 goto jleave;
1438 ++gotcha;
1439 senderfield = hp->h_sender;
1440 } else if ((addr = ok_vlook(sender)) != NULL)
1441 if (_putname(addr, w, action, &gotcha, "Sender:", fo, &senderfield))
1442 goto jleave;
1444 if (check_from_and_sender(fromfield, senderfield))
1445 goto jleave;
1448 if (hp->h_to != NULL && w & GTO) {
1449 if (fmt("To:", hp->h_to, fo, (w & (GCOMMA | GFILES)), 0, nodisp))
1450 goto jleave;
1451 ++gotcha;
1454 if (!ok_blook(bsdcompat) && !ok_blook(bsdorder))
1455 FMT_CC_AND_BCC();
1457 if (hp->h_subject != NULL && (w & GSUBJECT)) {
1458 fwrite("Subject: ", sizeof (char), 9, fo);
1459 if (!ascncasecmp(hp->h_subject, "re: ", 4)) {/* TODO localizable */
1460 fwrite("Re: ", sizeof(char), 4, fo);
1461 if (strlen(hp->h_subject + 4) > 0 &&
1462 xmime_write(hp->h_subject + 4, strlen(hp->h_subject + 4), fo,
1463 (!nodisp ? CONV_NONE : CONV_TOHDR),
1464 (!nodisp ? TD_ISPR | TD_ICONV : TD_ICONV), NULL) < 0)
1465 goto jleave;
1466 } else if (*hp->h_subject != '\0') {
1467 if (xmime_write(hp->h_subject, strlen(hp->h_subject), fo,
1468 (!nodisp ? CONV_NONE : CONV_TOHDR),
1469 (!nodisp ? TD_ISPR | TD_ICONV : TD_ICONV), NULL) < 0)
1470 goto jleave;
1472 ++gotcha;
1473 fwrite("\n", sizeof (char), 1, fo);
1476 if (ok_blook(bsdcompat) || ok_blook(bsdorder))
1477 FMT_CC_AND_BCC();
1479 if ((w & GMSGID) && stealthmua <= 0)
1480 message_id(fo, hp), ++gotcha;
1482 if ((np = hp->h_ref) != NULL && (w & GREF)) {
1483 fmt("References:", np, fo, 0, 1, 0);
1484 if (np->n_name != NULL) {
1485 while (np->n_flink != NULL)
1486 np = np->n_flink;
1487 if (!is_addr_invalid(np, 0)) {
1488 fprintf(fo, "In-Reply-To: %s\n", np->n_name);
1489 ++gotcha;
1494 if ((w & GUA) && stealthmua == 0)
1495 fprintf(fo, "User-Agent: %s %s\n", uagent, version), ++gotcha;
1497 if (w & GMIME) {
1498 fputs("MIME-Version: 1.0\n", fo), ++gotcha;
1499 if (hp->h_attach != NULL) {
1500 _sendout_boundary = mime_create_boundary();/*TODO carrier*/
1501 fprintf(fo, "Content-Type: multipart/mixed;\n boundary=\"%s\"\n",
1502 _sendout_boundary);
1503 } else {
1504 fprintf(fo, "Content-Type: %s", contenttype);
1505 if (charset != NULL)
1506 fprintf(fo, "; charset=%s", charset);
1507 fprintf(fo, "\nContent-Transfer-Encoding: %s\n",
1508 _get_encoding(convert));
1512 if (gotcha && (w & GNL))
1513 putc('\n', fo);
1514 rv = 0;
1515 jleave:
1516 NYD_LEAVE;
1517 return rv;
1518 #undef FMT_CC_AND_BCC
1521 FL enum okay
1522 resend_msg(struct message *mp, struct name *to, int add_resent) /* TODO check */
1524 FILE *ibuf, *nfo, *nfi;
1525 char *tempMail;
1526 struct header head;
1527 enum okay rv = STOP;
1528 NYD_ENTER;
1530 _sendout_error = FAL0;
1532 /* Update some globals we likely need first */
1533 time_current_update(&time_current, TRU1);
1535 memset(&head, 0, sizeof head);
1537 if ((to = checkaddrs(to)) == NULL) {
1538 _sendout_error = TRU1;
1539 goto jleave;
1542 if ((nfo = Ftmp(&tempMail, "resend", OF_WRONLY | OF_HOLDSIGS | OF_REGISTER,
1543 0600)) == NULL) {
1544 _sendout_error = TRU1;
1545 perror(tr(189, "temporary mail file"));
1546 goto jleave;
1548 if ((nfi = Fopen(tempMail, "r")) == NULL) {
1549 _sendout_error = TRU1;
1550 perror(tempMail);
1552 Ftmp_release(&tempMail);
1553 if (nfi == NULL)
1554 goto jerr_o;
1556 if ((ibuf = setinput(&mb, mp, NEED_BODY)) == NULL)
1557 goto jerr_all;
1559 head.h_to = to;
1560 to = fixhead(&head, to);
1562 if (infix_resend(ibuf, nfo, mp, head.h_to, add_resent) != 0) {
1563 savedeadletter(nfi, 1);
1564 fputs(tr(190, ". . . message not sent.\n"), stderr);
1565 jerr_all:
1566 Fclose(nfi);
1567 jerr_o:
1568 Fclose(nfo);
1569 _sendout_error = TRU1;
1570 goto jleave;
1572 Fclose(nfo);
1573 rewind(nfi);
1575 to = outof(to, nfi, &head, &_sendout_error);
1576 if (_sendout_error)
1577 savedeadletter(nfi, 0);
1579 to = elide(to); /* TODO should have been done in fixhead()? */
1581 if (count(to) != 0) {
1582 if (!ok_blook(record_resent) || mightrecord(nfi, to) == OKAY)
1583 rv = transfer(to, nfi, NULL);
1584 } else if (!_sendout_error)
1585 rv = OKAY;
1587 Fclose(nfi);
1588 jleave:
1589 if (_sendout_error)
1590 exit_status |= EXIT_SEND_ERROR;
1591 NYD_LEAVE;
1592 return rv;
1595 /* vim:set fenc=utf-8:s-it-mode */