Merge branch 'topic/resend'
[s-mailx.git] / sendout.c
blobf85094f3425e59e29f5cd5b5111d8947080d624c
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 */
346 if (options & OPT_r_FLAG) {
347 char const *froma;
349 if (option_r_arg[0] != '\0')
350 froma = option_r_arg;
351 else if (hp != NULL) {
352 /* puthead() did it, then */
353 assert(hp->h_from != NULL);
354 froma = hp->h_from->n_name;
355 } else
356 froma = skin(myorigin(NULL)); /* XXX ugh! ugh!! */
357 if (froma != NULL) { /* XXX ugh! */
358 args[i++] = "-r";
359 args[i++] = froma;
363 /* Receivers follow */
364 for (; to != NULL; to = to->n_flink)
365 if (!(to->n_type & GDEL))
366 args[i++] = to->n_name;
367 args[i] = NULL;
368 NYD_LEAVE;
369 return args;
372 static struct name *
373 fixhead(struct header *hp, struct name *tolist)
375 struct name **npp, *np;
376 NYD_ENTER;
378 tolist = elide(tolist);
380 hp->h_to = hp->h_cc = hp->h_bcc = NULL;
381 for (np = tolist; np != NULL; np = np->n_flink) {
382 switch (np->n_type & (GDEL | GMASK)) {
383 case GTO: npp = &hp->h_to; break;
384 case GCC: npp = &hp->h_cc; break;
385 case GBCC: npp = &hp->h_bcc; break;
386 default: continue;
388 *npp = cat(*npp, ndup(np, np->n_type | GFULL));
390 NYD_LEAVE;
391 return tolist;
394 static int
395 put_signature(FILE *fo, int convert)
397 char buf[INFIX_BUF], *sig, c = '\n';
398 FILE *fsig;
399 size_t sz;
400 int rv;
401 NYD_ENTER;
403 if ((sig = ok_vlook(signature)) == NULL || *sig == '\0') {
404 rv = 0;
405 goto jleave;
407 rv = -1;
409 if ((sig = file_expand(sig)) == NULL)
410 goto jleave;
412 if ((fsig = Fopen(sig, "r")) == NULL) {
413 perror(sig);
414 goto jleave;
416 while ((sz = fread(buf, sizeof *buf, INFIX_BUF, fsig)) != 0) {
417 c = buf[sz - 1];
418 if (xmime_write(buf, sz, fo, convert, TD_NONE, NULL) < 0)
419 goto jerr;
421 if (ferror(fsig)) {
422 jerr:
423 perror(sig);
424 Fclose(fsig);
425 goto jleave;
427 Fclose(fsig);
428 if (c != '\n')
429 putc('\n', fo);
431 rv = 0;
432 jleave:
433 NYD_LEAVE;
434 return rv;
437 static int
438 attach_message(struct attachment *ap, FILE *fo)
440 struct message *mp;
441 char const *ccp;
442 int rv;
443 NYD_ENTER;
445 fprintf(fo, "\n--%s\nContent-Type: message/rfc822\n"
446 "Content-Disposition: inline\n", _sendout_boundary);
447 if ((ccp = ap->a_content_description) != NULL)
448 fprintf(fo, "Content-Description: %s\n", ccp);
449 fputc('\n', fo);
451 mp = message + ap->a_msgno - 1;
452 touch(mp);
453 rv = (sendmp(mp, fo, 0, NULL, SEND_RFC822, NULL) < 0) ? -1 : 0;
454 NYD_LEAVE;
455 return rv;
458 static int
459 make_multipart(struct header *hp, int convert, FILE *fi, FILE *fo,
460 char const *contenttype, char const *charset)
462 struct attachment *att;
463 int rv = -1;
464 NYD_ENTER;
466 fputs("This is a multi-part message in MIME format.\n", fo);
467 if (fsize(fi) != 0) {
468 char *buf;
469 size_t sz, bufsize, cnt;
471 fprintf(fo, "\n--%s\n", _sendout_boundary);
472 fprintf(fo, "Content-Type: %s", contenttype);
473 if (charset != NULL)
474 fprintf(fo, "; charset=%s", charset);
475 fprintf(fo, "\nContent-Transfer-Encoding: %s\n"
476 "Content-Disposition: inline\n\n", _get_encoding(convert));
478 buf = smalloc(bufsize = INFIX_BUF);
479 if (convert == CONV_TOQP
480 #ifdef HAVE_ICONV
481 || iconvd != (iconv_t)-1
482 #endif
484 fflush(fi);
485 cnt = fsize(fi);
487 for (;;) {
488 if (convert == CONV_TOQP
489 #ifdef HAVE_ICONV
490 || iconvd != (iconv_t)-1
491 #endif
493 if (fgetline(&buf, &bufsize, &cnt, &sz, fi, 0) == NULL)
494 break;
495 } else if ((sz = fread(buf, sizeof *buf, bufsize, fi)) == 0)
496 break;
498 if (xmime_write(buf, sz, fo, convert, TD_ICONV, NULL) < 0) {
499 free(buf);
500 goto jleave;
503 free(buf);
505 if (ferror(fi))
506 goto jleave;
507 if (charset != NULL)
508 put_signature(fo, convert);
511 for (att = hp->h_attach; att != NULL; att = att->a_flink) {
512 if (att->a_msgno) {
513 if (attach_message(att, fo) != 0)
514 goto jleave;
515 } else if (_attach_file(att, fo) != 0)
516 goto jleave;
519 /* the final boundary with two attached dashes */
520 fprintf(fo, "\n--%s--\n", _sendout_boundary);
521 rv = 0;
522 jleave:
523 NYD_LEAVE;
524 return rv;
527 static FILE *
528 infix(struct header *hp, FILE *fi) /* TODO check */
530 FILE *nfo, *nfi = NULL;
531 char *tempMail;
532 char const *contenttype, *charset = NULL;
533 enum conversion convert;
534 int do_iconv = 0, err;
535 #ifdef HAVE_ICONV
536 char const *tcs, *convhdr = NULL;
537 #endif
538 NYD_ENTER;
540 if ((nfo = Ftmp(&tempMail, "infix", OF_WRONLY | OF_HOLDSIGS | OF_REGISTER,
541 0600)) == NULL) {
542 perror(tr(178, "temporary mail file"));
543 goto jleave;
545 if ((nfi = Fopen(tempMail, "r")) == NULL) {
546 perror(tempMail);
547 Fclose(nfo);
549 Ftmp_release(&tempMail);
550 if (nfi == NULL)
551 goto jleave;
553 contenttype = "text/plain"; /* XXX mail body - always text/plain, want XX? */
554 convert = mime_classify_file(fi, &contenttype, &charset, &do_iconv);
556 #ifdef HAVE_ICONV
557 tcs = charset_get_lc();
558 if ((convhdr = need_hdrconv(hp, GTO | GSUBJECT | GCC | GBCC | GIDENT))) {
559 if (iconvd != (iconv_t)-1) /* XXX */
560 n_iconv_close(iconvd);
561 if (asccasecmp(convhdr, tcs) != 0 &&
562 (iconvd = n_iconv_open(convhdr, tcs)) == (iconv_t)-1 &&
563 (err = errno) != 0)
564 goto jiconv_err;
566 #endif
567 if (puthead(hp, nfo,
568 (GTO | GSUBJECT | GCC | GBCC | GNL | GCOMMA | GUA | GMIME | GMSGID |
569 GIDENT | GREF | GDATE), SEND_MBOX, convert, contenttype, charset))
570 goto jerr;
571 #ifdef HAVE_ICONV
572 if (iconvd != (iconv_t)-1)
573 n_iconv_close(iconvd);
574 #endif
576 #ifdef HAVE_ICONV
577 if (do_iconv && charset != NULL) { /*TODO charset->mime_classify_file*/
578 if (asccasecmp(charset, tcs) != 0 &&
579 (iconvd = n_iconv_open(charset, tcs)) == (iconv_t)-1 &&
580 (err = errno) != 0) {
581 jiconv_err:
582 if (err == EINVAL)
583 fprintf(stderr, tr(179, "Cannot convert from %s to %s\n"),
584 tcs, charset);
585 else
586 perror("iconv_open");
587 goto jerr;
590 #endif
592 if (hp->h_attach != NULL) {
593 if (make_multipart(hp, convert, fi, nfo, contenttype, charset) != 0)
594 goto jerr;
595 } else {
596 size_t sz, bufsize, cnt;
597 char *buf;
599 if (convert == CONV_TOQP
600 #ifdef HAVE_ICONV
601 || iconvd != (iconv_t)-1
602 #endif
604 fflush(fi);
605 cnt = fsize(fi);
607 buf = smalloc(bufsize = INFIX_BUF);
608 for (err = 0;;) {
609 if (convert == CONV_TOQP
610 #ifdef HAVE_ICONV
611 || iconvd != (iconv_t)-1
612 #endif
614 if (fgetline(&buf, &bufsize, &cnt, &sz, fi, 0) == NULL)
615 break;
616 } else if ((sz = fread(buf, sizeof *buf, bufsize, fi)) == 0)
617 break;
618 if (xmime_write(buf, sz, nfo, convert, TD_ICONV, NULL) < 0) {
619 err = 1;
620 break;
623 free(buf);
625 if (err || ferror(fi)) {
626 jerr:
627 Fclose(nfo);
628 Fclose(nfi);
629 #ifdef HAVE_ICONV
630 if (iconvd != (iconv_t)-1)
631 n_iconv_close(iconvd);
632 #endif
633 nfi = NULL;
634 goto jleave;
636 if (charset != NULL)
637 put_signature(nfo, convert); /* XXX if (text/) !! */
640 #ifdef HAVE_ICONV
641 if (iconvd != (iconv_t)-1)
642 n_iconv_close(iconvd);
643 #endif
645 fflush(nfo);
646 if ((err = ferror(nfo)))
647 perror(tr(180, "temporary mail file"));
648 Fclose(nfo);
649 if (!err) {
650 fflush_rewind(nfi);
651 Fclose(fi);
652 } else {
653 Fclose(nfi);
654 nfi = NULL;
656 jleave:
657 NYD_LEAVE;
658 return nfi;
661 static int
662 savemail(char const *name, FILE *fi)
664 FILE *fo;
665 char *buf;
666 size_t bufsize, buflen, cnt;
667 int prependnl = 0, rv = -1;
668 NYD_ENTER;
670 buf = smalloc(bufsize = LINESIZE);
672 if ((fo = Zopen(name, "a+", NULL)) == NULL) {
673 if ((fo = Zopen(name, "wx", NULL)) == NULL) {
674 perror(name);
675 goto jleave;
677 } else {
678 if (fseek(fo, -2L, SEEK_END) == 0) {
679 switch (fread(buf, sizeof *buf, 2, fo)) {
680 case 2:
681 if (buf[1] != '\n') {
682 prependnl = 1;
683 break;
685 /* FALLTHRU */
686 case 1:
687 if (buf[0] != '\n')
688 prependnl = 1;
689 break;
690 default:
691 if (ferror(fo)) {
692 perror(name);
693 goto jleave;
696 if (prependnl) {
697 putc('\n', fo);
699 fflush(fo);
703 fprintf(fo, "From %s %s", myname, time_current.tc_ctime);
704 fflush_rewind(fi);
705 cnt = fsize(fi);
706 buflen = 0;
707 while (fgetline(&buf, &bufsize, &cnt, &buflen, fi, 0) != NULL) {
708 #ifdef HAVE_DEBUG /* TODO assert legacy */
709 assert(!is_head(buf, buflen));
710 #else
711 if (is_head(buf, buflen))
712 putc('>', fo);
713 #endif
714 fwrite(buf, sizeof *buf, buflen, fo);
716 if (buflen && *(buf + buflen - 1) != '\n')
717 putc('\n', fo);
718 putc('\n', fo);
719 fflush(fo);
721 rv = 0;
722 if (ferror(fo)) {
723 perror(name);
724 rv = -1;
726 if (Fclose(fo) != 0)
727 rv = -1;
728 fflush_rewind(fi);
729 jleave:
730 free(buf);
731 NYD_LEAVE;
732 return rv;
735 static int
736 sendmail_internal(void *v, int recipient_record)
738 struct header head;
739 char *str = v;
740 int rv;
741 NYD_ENTER;
743 memset(&head, 0, sizeof head);
744 head.h_to = lextract(str, GTO | GFULL);
745 rv = mail1(&head, 0, NULL, NULL, recipient_record, 0);
746 NYD_LEAVE;
747 return rv;
750 static enum okay
751 transfer(struct name *to, FILE *input, struct header *hp)
753 char o[LINESIZE], *cp;
754 struct name *np;
755 int cnt = 0;
756 enum okay rv = OKAY;
757 NYD_ENTER;
759 np = to;
760 while (np != NULL) {
761 snprintf(o, sizeof o, "smime-encrypt-%s", np->n_name);/* XXX */
762 if ((cp = vok_vlook(o)) != NULL) {
763 #ifdef HAVE_SSL
764 struct name *nt;
765 FILE *ef;
767 if ((ef = smime_encrypt(input, cp, np->n_name)) != NULL) {
768 nt = ndup(np, np->n_type & ~(GFULL | GSKIN));
769 rv = start_mta(nt, ef, hp);
770 Fclose(ef);
771 } else {
772 #else
773 fprintf(stderr, tr(225, "No SSL support compiled in.\n"));
774 rv = STOP;
775 #endif
776 fprintf(stderr, tr(38, "Message not sent to <%s>\n"), np->n_name);
777 _sendout_error = TRU1;
778 #ifdef HAVE_SSL
780 #endif
781 rewind(input);
783 if (np->n_flink != NULL)
784 np->n_flink->n_blink = np->n_blink;
785 if (np->n_blink != NULL)
786 np->n_blink->n_flink = np->n_flink;
787 if (np == to)
788 to = np->n_flink;
789 np = np->n_flink;
790 } else {
791 ++cnt;
792 np = np->n_flink;
796 if (cnt)
797 if (ok_blook(smime_force_encryption) || start_mta(to, input, hp) != OKAY)
798 rv = STOP;
799 NYD_LEAVE;
800 return rv;
803 static enum okay
804 start_mta(struct name *to, FILE *input, struct header *hp)
806 #ifdef HAVE_SMTP
807 char *user = NULL, *password = NULL, *skinned = NULL;
808 #endif
809 char const **args = NULL, **t, *mta;
810 char *smtp;
811 pid_t pid;
812 sigset_t nset;
813 enum okay rv = STOP;
814 NYD_ENTER;
815 UNUSED(hp);
817 if ((smtp = ok_vlook(smtp)) == NULL) {
818 if ((mta = ok_vlook(sendmail)) != NULL) {
819 if ((mta = file_expand(mta)) == NULL)
820 goto jstop;
821 } else
822 mta = SENDMAIL;
824 args = _prepare_mta_args(to, hp);
825 if (options & OPT_DEBUG) {
826 printf(tr(181, "Sendmail arguments:"));
827 for (t = args; *t != NULL; ++t)
828 printf(" \"%s\"", *t);
829 printf("\n");
830 rv = OKAY;
831 goto jleave;
833 } else {
834 mta = NULL; /* Silence cc */
835 #ifndef HAVE_SMTP
836 fputs(tr(194, "No SMTP support compiled in.\n"), stderr);
837 goto jstop;
838 #else
839 skinned = skin(myorigin(hp));
840 if ((user = smtp_auth_var("-user", skinned)) != NULL &&
841 (password = smtp_auth_var("-password", skinned)) == NULL)
842 password = getpassword(NULL);
843 #endif
846 /* Fork, set up the temporary mail file as standard input for "mail", and
847 * exec with the user list we generated far above */
848 if ((pid = fork()) == -1) {
849 perror("fork");
850 jstop:
851 savedeadletter(input, 0);
852 _sendout_error = TRU1;
853 goto jleave;
855 if (pid == 0) {
856 sigemptyset(&nset);
857 sigaddset(&nset, SIGHUP);
858 sigaddset(&nset, SIGINT);
859 sigaddset(&nset, SIGQUIT);
860 sigaddset(&nset, SIGTSTP);
861 sigaddset(&nset, SIGTTIN);
862 sigaddset(&nset, SIGTTOU);
863 freopen("/dev/null", "r", stdin);
864 #ifdef HAVE_SMTP
865 if (smtp != NULL) {
866 prepare_child(&nset, 0, 1);
867 if (smtp_mta(smtp, to, input, hp, user, password, skinned) == 0)
868 _exit(0);
869 } else {
870 #endif
871 prepare_child(&nset, fileno(input), -1);
872 /* If *record* is set then savemail() will move the file position;
873 * it'll call rewind(), but that may optimize away the systemcall if
874 * possible, and since dup2() shares the position with the original FD
875 * the MTA may end up reading nothing */
876 lseek(0, 0, SEEK_SET);
877 execv(mta, UNCONST(args));
878 perror(mta);
879 #ifdef HAVE_SMTP
881 #endif
882 savedeadletter(input, 1);
883 fputs(tr(182, "... message not sent.\n"), stderr);
884 _exit(1);
886 if ((options & (OPT_DEBUG | OPT_VERBOSE | OPT_BATCH_FLAG)) ||
887 ok_blook(sendwait)) {
888 if (wait_child(pid, NULL))
889 rv = OKAY;
890 else
891 _sendout_error = TRU1;
892 } else {
893 rv = OKAY;
894 free_child(pid);
896 jleave:
897 NYD_LEAVE;
898 return rv;
901 static enum okay
902 mightrecord(FILE *fp, struct name *to)
904 char *cp, *cq;
905 char const *ep;
906 enum okay rv = OKAY;
907 NYD_ENTER;
909 if (to != NULL) {
910 cp = savestr(skinned_name(to));
911 for (cq = cp; *cq != '\0' && *cq != '@'; ++cq)
913 *cq = '\0';
914 } else
915 cp = ok_vlook(record);
917 if (cp != NULL) {
918 if ((ep = expand(cp)) == NULL) {
919 ep = "NULL";
920 goto jbail;
923 if (*ep != '/' && *ep != '+' && ok_blook(outfolder) &&
924 which_protocol(ep) == PROTO_FILE) {
925 size_t i = strlen(cp);
926 cq = salloc(i + 1 +1);
927 cq[0] = '+';
928 memcpy(cq + 1, cp, i +1);
929 cp = cq;
930 if ((ep = file_expand(cp)) == NULL) {
931 ep = "NULL";
932 goto jbail;
936 if (savemail(ep, fp) != 0) {
937 jbail:
938 fprintf(stderr, tr(285, "Failed to save message in %s - "
939 "message not sent\n"), ep);
940 exit_status |= EXIT_ERR;
941 savedeadletter(fp, 1);
942 rv = STOP;
945 NYD_LEAVE;
946 return rv;
949 static void
950 message_id(FILE *fo, struct header *hp)
952 char const *h;
953 size_t rl;
954 struct tm *tmp;
955 NYD_ENTER;
957 if (ok_blook(message_id_disable))
958 goto jleave;
960 if ((h = ok_vlook(hostname)) != NULL)
961 rl = 24;
962 else if ((h = skin(myorigin(hp))) != NULL && strchr(h, '@') != NULL)
963 rl = 16;
964 else
965 /* Up to MTA */
966 goto jleave;
968 tmp = &time_current.tc_gm;
969 fprintf(fo, "Message-ID: <%04d%02d%02d%02d%02d%02d.%s%c%s>\n",
970 tmp->tm_year + 1900, tmp->tm_mon + 1, tmp->tm_mday,
971 tmp->tm_hour, tmp->tm_min, tmp->tm_sec,
972 getrandstring(rl), (rl == 16 ? '%' : '@'), h);
973 jleave:
974 NYD_LEAVE;
977 static int
978 fmt(char const *str, struct name *np, FILE *fo, int flags, int dropinvalid,
979 int domime)
981 enum {
982 m_INIT = 1<<0,
983 m_COMMA = 1<<1,
984 m_NOPF = 1<<2,
985 m_CSEEN = 1<<3
986 } m = (flags & GCOMMA) ? m_COMMA : 0;
987 ssize_t col, len;
988 int rv = 1;
989 NYD_ENTER;
991 col = strlen(str);
992 if (col) {
993 fwrite(str, sizeof *str, col, fo);
994 if (flags & GFILES)
995 goto jstep;
996 if (col == 9 && !asccasecmp(str, "reply-to:")) {
997 m |= m_NOPF;
998 goto jstep;
1000 if (ok_blook(add_file_recipients))
1001 goto jstep;
1002 if ((col == 3 && (!asccasecmp(str, "to:") || !asccasecmp(str, "cc:"))) ||
1003 (col == 4 && !asccasecmp(str, "bcc:")) ||
1004 (col == 10 && !asccasecmp(str, "Resent-To:")))
1005 m |= m_NOPF;
1007 jstep:
1008 for (; np != NULL; np = np->n_flink) {
1009 if ((m & m_NOPF) && is_fileorpipe_addr(np))
1010 continue;
1011 if (is_addr_invalid(np, !dropinvalid)) {
1012 if (dropinvalid)
1013 continue;
1014 else
1015 goto jleave;
1017 if ((m & (m_INIT | m_COMMA)) == (m_INIT | m_COMMA)) {
1018 putc(',', fo);
1019 m |= m_CSEEN;
1020 ++col;
1022 len = strlen(np->n_fullname);
1023 ++col; /* The separating space */
1024 if ((m & m_INIT) && col > 1 && col + len > 72) {
1025 fputs("\n ", fo);
1026 col = 1;
1027 m &= ~m_CSEEN;
1028 } else
1029 putc(' ', fo);
1030 m = (m & ~m_CSEEN) | m_INIT;
1031 len = xmime_write(np->n_fullname, len, fo,
1032 (domime ? CONV_TOHDR_A : CONV_NONE), TD_ICONV, NULL);
1033 if (len < 0)
1034 goto jleave;
1035 col += len;
1037 putc('\n', fo);
1038 rv = 0;
1039 jleave:
1040 NYD_LEAVE;
1041 return rv;
1044 static int
1045 infix_resend(FILE *fi, FILE *fo, struct message *mp, struct name *to,
1046 int add_resent)
1048 size_t cnt, c, bufsize = 0;
1049 char *buf = NULL;
1050 char const *cp;
1051 struct name *fromfield = NULL, *senderfield = NULL;
1052 int rv = 1;
1053 NYD_ENTER;
1055 cnt = mp->m_size;
1057 /* Write the Resent-Fields */
1058 if (add_resent) {
1059 fputs("Resent-", fo);
1060 mkdate(fo, "Date");
1061 if ((cp = myaddrs(NULL)) != NULL) {
1062 if (_putname(cp, GCOMMA, SEND_MBOX, NULL, "Resent-From:", fo,
1063 &fromfield))
1064 goto jleave;
1066 if ((cp = ok_vlook(sender)) != NULL) {
1067 if (_putname(cp, GCOMMA, SEND_MBOX, NULL, "Resent-Sender:", fo,
1068 &senderfield))
1069 goto jleave;
1071 if (fmt("Resent-To:", to, fo, 1, 1, 0))
1072 goto jleave;
1073 if ((cp = ok_vlook(stealthmua)) == NULL || !strcmp(cp, "noagent")) {
1074 fputs("Resent-", fo);
1075 message_id(fo, NULL);
1078 if (check_from_and_sender(fromfield, senderfield))
1079 goto jleave;
1081 /* Write the original headers */
1082 while (cnt > 0) {
1083 if (fgetline(&buf, &bufsize, &cnt, &c, fi, 0) == NULL)
1084 break;
1085 /* XXX more checks: The From_ line may be seen when resending */
1086 /* During headers is_head() is actually overkill, so ^From_ is sufficient
1087 * && !is_head(buf, c) */
1088 if (ascncasecmp("status: ", buf, 8) && strncmp("From ", buf, 5))
1089 fwrite(buf, sizeof *buf, c, fo);
1090 if (cnt > 0 && *buf == '\n')
1091 break;
1094 /* Write the message body */
1095 while (cnt > 0) {
1096 if (fgetline(&buf, &bufsize, &cnt, &c, fi, 0) == NULL)
1097 break;
1098 if (cnt == 0 && *buf == '\n')
1099 break;
1100 fwrite(buf, sizeof *buf, c, fo);
1102 if (buf != NULL)
1103 free(buf);
1104 if (ferror(fo)) {
1105 perror(tr(188, "temporary mail file"));
1106 goto jleave;
1108 rv = 0;
1109 jleave:
1110 NYD_LEAVE;
1111 return rv;
1114 FL int
1115 mail(struct name *to, struct name *cc, struct name *bcc, char *subject,
1116 struct attachment *attach, char *quotefile, int recipient_record)
1118 struct header head;
1119 struct str in, out;
1120 NYD_ENTER;
1122 memset(&head, 0, sizeof head);
1124 /* The given subject may be in RFC1522 format. */
1125 if (subject != NULL) {
1126 in.s = subject;
1127 in.l = strlen(subject);
1128 mime_fromhdr(&in, &out, /* TODO ??? TD_ISPR |*/ TD_ICONV);
1129 head.h_subject = out.s;
1131 if (!(options & OPT_t_FLAG)) {
1132 head.h_to = to;
1133 head.h_cc = cc;
1134 head.h_bcc = bcc;
1136 head.h_attach = attach;
1138 mail1(&head, 0, NULL, quotefile, recipient_record, 0);
1140 if (subject != NULL)
1141 free(out.s);
1142 NYD_LEAVE;
1143 return 0;
1146 FL int
1147 c_sendmail(void *v)
1149 int rv;
1150 NYD_ENTER;
1152 rv = sendmail_internal(v, 0);
1153 NYD_LEAVE;
1154 return rv;
1157 FL int
1158 c_Sendmail(void *v)
1160 int rv;
1161 NYD_ENTER;
1163 rv = sendmail_internal(v, 1);
1164 NYD_LEAVE;
1165 return rv;
1168 FL enum okay
1169 mail1(struct header *hp, int printheaders, struct message *quote,
1170 char *quotefile, int recipient_record, int doprefix)
1172 struct name *to;
1173 FILE *mtf, *nmtf;
1174 int dosign = -1, err;
1175 char const *cp;
1176 enum okay rv = STOP;
1177 NYD_ENTER;
1179 _sendout_error = FAL0;
1181 /* Update some globals we likely need first */
1182 time_current_update(&time_current, TRU1);
1184 /* */
1185 if ((cp = ok_vlook(autocc)) != NULL && *cp != '\0')
1186 hp->h_cc = cat(hp->h_cc, checkaddrs(lextract(cp, GCC | GFULL)));
1187 if ((cp = ok_vlook(autobcc)) != NULL && *cp != '\0')
1188 hp->h_bcc = cat(hp->h_bcc, checkaddrs(lextract(cp, GBCC | GFULL)));
1190 /* Collect user's mail from standard input. Get the result as mtf */
1191 mtf = collect(hp, printheaders, quote, quotefile, doprefix);
1192 if (mtf == NULL)
1193 goto j_leave;
1195 if (options & OPT_INTERACTIVE) {
1196 err = (ok_blook(bsdcompat) || ok_blook(askatend));
1197 if (err == 0)
1198 goto jaskeot;
1199 if (ok_blook(askcc))
1200 ++err, grab_headers(hp, GCC, 1);
1201 if (ok_blook(askbcc))
1202 ++err, grab_headers(hp, GBCC, 1);
1203 if (ok_blook(askattach))
1204 ++err, edit_attachments(&hp->h_attach);
1205 if (ok_blook(asksign))
1206 ++err, dosign = getapproval(tr(35, "Sign this message (y/n)? "), TRU1);
1207 if (err == 1) {
1208 jaskeot:
1209 printf(tr(183, "EOT\n"));
1210 fflush(stdout);
1214 if (fsize(mtf) == 0) {
1215 if (options & OPT_E_FLAG)
1216 goto jleave;
1217 if (hp->h_subject == NULL)
1218 printf(tr(184, "No message, no subject; hope that's ok\n"));
1219 else if (ok_blook(bsdcompat) || ok_blook(bsdmsgs))
1220 printf(tr(185, "Null message body; hope that's ok\n"));
1223 if (dosign < 0)
1224 dosign = ok_blook(smime_sign);
1225 #ifndef HAVE_SSL
1226 if (dosign) {
1227 fprintf(stderr, tr(225, "No SSL support compiled in.\n"));
1228 goto jleave;
1230 #endif
1232 /* XXX Update time_current again; once collect() offers editing of more
1233 * XXX headers, including Date:, this must only happen if Date: is the
1234 * XXX same that it was before collect() (e.g., postponing etc.).
1235 * XXX But *do* update otherwise because the mail seems to be backdated
1236 * XXX if the user edited some time, which looks odd and it happened
1237 * XXX to me that i got mis-dated response mails due to that... */
1238 time_current_update(&time_current, TRU1);
1240 /* TODO hrmpf; the MIME/send layer rewrite MUST address the init crap:
1241 * TODO setup the header ONCE; note this affects edit.c, collect.c ...,
1242 * TODO but: offer a hook that rebuilds/expands/checks/fixates all
1243 * TODO header fields ONCE, call that ONCE after user editing etc. has
1244 * TODO completed (one edit cycle) */
1246 /* Take the user names from the combined to and cc lists and do all the
1247 * alias processing. The POSIX standard says:
1248 * The names shall be substituted when alias is used as a recipient
1249 * address specified by the user in an outgoing message (that is,
1250 * other recipients addressed indirectly through the reply command
1251 * shall not be substituted in this manner).
1252 * S-nail thus violates POSIX, as has been pointed out correctly by
1253 * Martin Neitzel, but logic and usability of POSIX standards is not seldom
1254 * disputable anyway. Go for user friendliness */
1256 /* Do alias expansion on Reply-To: members, too */
1257 /* TODO puthead() YET (!!! see ONCE note above) expands the value, but
1258 * TODO doesn't perform alias expansion; encapsulate in the ONCE-o */
1259 if (hp->h_replyto == NULL && (cp = ok_vlook(replyto)) != NULL)
1260 hp->h_replyto = checkaddrs(lextract(cp, GEXTRA | GFULL));
1261 if (hp->h_replyto != NULL)
1262 hp->h_replyto = elide(usermap(hp->h_replyto, TRU1));
1264 /* TODO what happens now is that all recipients are merged into
1265 * TODO a duplicated list with expanded aliases, then this list is
1266 * TODO splitted again into the three individual recipient lists (with
1267 * TODO duplicates removed).
1268 * TODO later on we use the merged list for outof() pipe/file saving,
1269 * TODO then we eliminate duplicates (again) and then we use that one
1270 * TODO for mightrecord() and transfer(), and again. ... Please ... */
1272 /* NOTE: Due to elide() in fixhead(), ENSURE to,cc,bcc order of to!,
1273 * because otherwise the recipients will be "degraded" if they occur
1274 * multiple times */
1275 to = usermap(cat(hp->h_to, cat(hp->h_cc, hp->h_bcc)), FAL0);
1276 if (to == NULL) {
1277 fprintf(stderr, tr(186, "No recipients specified\n"));
1278 _sendout_error = TRU1;
1280 to = fixhead(hp, to);
1282 /* 'Bit ugly kind of control flow until we find a charset that does it */
1283 for (charset_iter_reset(hp->h_charset);; charset_iter_next()) {
1284 if (!charset_iter_is_valid())
1286 else if ((nmtf = infix(hp, mtf)) != NULL)
1287 break;
1288 else if ((err = errno) == EILSEQ || err == EINVAL) {
1289 rewind(mtf);
1290 continue;
1293 perror("");
1294 #ifdef HAVE_SSL
1295 jfail_dead:
1296 #endif
1297 _sendout_error = TRU1;
1298 savedeadletter(mtf, 1);
1299 fputs(tr(182, "... message not sent.\n"), stderr);
1300 goto jleave;
1303 mtf = nmtf;
1304 #ifdef HAVE_SSL
1305 if (dosign) {
1306 if ((nmtf = smime_sign(mtf, hp)) == NULL)
1307 goto jfail_dead;
1308 Fclose(mtf);
1309 mtf = nmtf;
1311 #endif
1313 /* TODO truly - i still don't get what follows: (1) we deliver file
1314 * TODO and pipe addressees, (2) we mightrecord() and (3) we transfer
1315 * TODO even if (1) savedeadletter() etc. To me this doesn't make sense? */
1317 /* Deliver pipe and file addressees */
1318 to = outof(to, mtf, hp, &_sendout_error);
1319 if (_sendout_error)
1320 savedeadletter(mtf, 0);
1322 to = elide(to); /* XXX needed only to drop GDELs due to outof()! */
1323 dosign = (int)count(to); /* XXX ui32; reuse as "do-transfer" */
1324 if ((!recipient_record || dosign) &&
1325 mightrecord(mtf, (recipient_record ? to : NULL)) != OKAY)
1326 goto jleave;
1327 if (dosign)
1328 rv = transfer(to, mtf, hp);
1329 else if (!_sendout_error)
1330 rv = OKAY;
1332 jleave:
1333 Fclose(mtf);
1334 j_leave:
1335 if (_sendout_error)
1336 exit_status |= EXIT_SEND_ERROR;
1337 NYD_LEAVE;
1338 return rv;
1341 FL int
1342 mkdate(FILE *fo, char const *field)
1344 struct tm *tmptr;
1345 int tzdiff, tzdiff_hour, tzdiff_min, rv;
1346 NYD_ENTER;
1348 tzdiff = time_current.tc_time - mktime(&time_current.tc_gm);
1349 tzdiff_hour = (int)(tzdiff / 60);
1350 tzdiff_min = tzdiff_hour % 60;
1351 tzdiff_hour /= 60;
1352 tmptr = &time_current.tc_local;
1353 if (tmptr->tm_isdst > 0)
1354 ++tzdiff_hour;
1355 rv = fprintf(fo, "%s: %s, %02d %s %04d %02d:%02d:%02d %+05d\n",
1356 field,
1357 weekday_names[tmptr->tm_wday],
1358 tmptr->tm_mday, month_names[tmptr->tm_mon],
1359 tmptr->tm_year + 1900, tmptr->tm_hour,
1360 tmptr->tm_min, tmptr->tm_sec,
1361 tzdiff_hour * 100 + tzdiff_min);
1362 NYD_LEAVE;
1363 return rv;
1366 FL int
1367 puthead(struct header *hp, FILE *fo, enum gfield w, enum sendaction action,
1368 enum conversion convert, char const *contenttype, char const *charset)
1370 #define FMT_CC_AND_BCC() \
1371 do {\
1372 if (hp->h_cc != NULL && (w & GCC)) {\
1373 if (fmt("Cc:", hp->h_cc, fo, (w & (GCOMMA | GFILES)), 0,\
1374 (action != SEND_TODISP)))\
1375 goto jleave;\
1376 ++gotcha;\
1378 if (hp->h_bcc != NULL && (w & GBCC)) {\
1379 if (fmt("Bcc:", hp->h_bcc, fo, (w & (GCOMMA | GFILES)), 0,\
1380 (action != SEND_TODISP)))\
1381 goto jleave;\
1382 ++gotcha;\
1384 } while (0)
1386 char const *addr;
1387 size_t gotcha, l;
1388 struct name *np, *fromfield = NULL, *senderfield = NULL;
1389 int stealthmua, rv = 1;
1390 bool_t nodisp;
1391 NYD_ENTER;
1393 if ((addr = ok_vlook(stealthmua)) != NULL)
1394 stealthmua = !strcmp(addr, "noagent") ? -1 : 1;
1395 else
1396 stealthmua = 0;
1397 gotcha = 0;
1398 nodisp = (action != SEND_TODISP);
1400 if (w & GDATE)
1401 mkdate(fo, "Date"), ++gotcha;
1402 if (w & GIDENT) {
1403 if (hp->h_from != NULL) {
1404 if (fmt("From:", hp->h_from, fo, (w & (GCOMMA | GFILES)), 0, nodisp))
1405 goto jleave;
1406 ++gotcha;
1407 fromfield = hp->h_from;
1408 } else if ((addr = myaddrs(hp)) != NULL) {
1409 if (_putname(addr, w, action, &gotcha, "From:", fo, &fromfield))
1410 goto jleave;
1411 hp->h_from = fromfield;
1414 if (((addr = hp->h_organization) != NULL ||
1415 (addr = ok_vlook(ORGANIZATION)) != NULL) &&
1416 (l = strlen(addr)) > 0) {
1417 fwrite("Organization: ", sizeof(char), 14, fo);
1418 if (xmime_write(addr, l, fo, (!nodisp ? CONV_NONE : CONV_TOHDR),
1419 (!nodisp ? TD_ISPR | TD_ICONV : TD_ICONV), NULL) < 0)
1420 goto jleave;
1421 ++gotcha;
1422 putc('\n', fo);
1425 /* TODO see the ONCE TODO note somewhere around this file;
1426 * TODO but anyway, do NOT perform alias expansion UNLESS
1427 * TODO we are actually sending out! */
1428 if (hp->h_replyto != NULL) {
1429 if (fmt("Reply-To:", hp->h_replyto, fo, w & GCOMMA, 0, nodisp))
1430 goto jleave;
1431 ++gotcha;
1432 } else if ((addr = ok_vlook(replyto)) != NULL)
1433 if (_putname(addr, w, action, &gotcha, "Reply-To:", fo, NULL))
1434 goto jleave;
1436 if (hp->h_sender != NULL) {
1437 if (fmt("Sender:", hp->h_sender, fo, w & GCOMMA, 0, nodisp))
1438 goto jleave;
1439 ++gotcha;
1440 senderfield = hp->h_sender;
1441 } else if ((addr = ok_vlook(sender)) != NULL)
1442 if (_putname(addr, w, action, &gotcha, "Sender:", fo, &senderfield))
1443 goto jleave;
1445 if (check_from_and_sender(fromfield, senderfield))
1446 goto jleave;
1449 if (hp->h_to != NULL && w & GTO) {
1450 if (fmt("To:", hp->h_to, fo, (w & (GCOMMA | GFILES)), 0, nodisp))
1451 goto jleave;
1452 ++gotcha;
1455 if (!ok_blook(bsdcompat) && !ok_blook(bsdorder))
1456 FMT_CC_AND_BCC();
1458 if (hp->h_subject != NULL && (w & GSUBJECT)) {
1459 fwrite("Subject: ", sizeof (char), 9, fo);
1460 if (!ascncasecmp(hp->h_subject, "re: ", 4)) {/* TODO localizable */
1461 fwrite("Re: ", sizeof(char), 4, fo);
1462 if (strlen(hp->h_subject + 4) > 0 &&
1463 xmime_write(hp->h_subject + 4, strlen(hp->h_subject + 4), fo,
1464 (!nodisp ? CONV_NONE : CONV_TOHDR),
1465 (!nodisp ? TD_ISPR | TD_ICONV : TD_ICONV), NULL) < 0)
1466 goto jleave;
1467 } else if (*hp->h_subject != '\0') {
1468 if (xmime_write(hp->h_subject, strlen(hp->h_subject), fo,
1469 (!nodisp ? CONV_NONE : CONV_TOHDR),
1470 (!nodisp ? TD_ISPR | TD_ICONV : TD_ICONV), NULL) < 0)
1471 goto jleave;
1473 ++gotcha;
1474 fwrite("\n", sizeof (char), 1, fo);
1477 if (ok_blook(bsdcompat) || ok_blook(bsdorder))
1478 FMT_CC_AND_BCC();
1480 if ((w & GMSGID) && stealthmua <= 0)
1481 message_id(fo, hp), ++gotcha;
1483 if ((np = hp->h_ref) != NULL && (w & GREF)) {
1484 fmt("References:", np, fo, 0, 1, 0);
1485 if (np->n_name != NULL) {
1486 while (np->n_flink != NULL)
1487 np = np->n_flink;
1488 if (!is_addr_invalid(np, 0)) {
1489 fprintf(fo, "In-Reply-To: %s\n", np->n_name);
1490 ++gotcha;
1495 if ((w & GUA) && stealthmua == 0)
1496 fprintf(fo, "User-Agent: %s %s\n", uagent, version), ++gotcha;
1498 if (w & GMIME) {
1499 fputs("MIME-Version: 1.0\n", fo), ++gotcha;
1500 if (hp->h_attach != NULL) {
1501 _sendout_boundary = mime_create_boundary();/*TODO carrier*/
1502 fprintf(fo, "Content-Type: multipart/mixed;\n boundary=\"%s\"\n",
1503 _sendout_boundary);
1504 } else {
1505 fprintf(fo, "Content-Type: %s", contenttype);
1506 if (charset != NULL)
1507 fprintf(fo, "; charset=%s", charset);
1508 fprintf(fo, "\nContent-Transfer-Encoding: %s\n",
1509 _get_encoding(convert));
1513 if (gotcha && (w & GNL))
1514 putc('\n', fo);
1515 rv = 0;
1516 jleave:
1517 NYD_LEAVE;
1518 return rv;
1519 #undef FMT_CC_AND_BCC
1522 FL enum okay
1523 resend_msg(struct message *mp, struct name *to, int add_resent) /* TODO check */
1525 FILE *ibuf, *nfo, *nfi;
1526 char *tempMail;
1527 struct header head;
1528 enum okay rv = STOP;
1529 NYD_ENTER;
1531 _sendout_error = FAL0;
1533 /* Update some globals we likely need first */
1534 time_current_update(&time_current, TRU1);
1536 memset(&head, 0, sizeof head);
1538 if ((to = checkaddrs(to)) == NULL) {
1539 _sendout_error = TRU1;
1540 goto jleave;
1543 if ((nfo = Ftmp(&tempMail, "resend", OF_WRONLY | OF_HOLDSIGS | OF_REGISTER,
1544 0600)) == NULL) {
1545 _sendout_error = TRU1;
1546 perror(tr(189, "temporary mail file"));
1547 goto jleave;
1549 if ((nfi = Fopen(tempMail, "r")) == NULL) {
1550 _sendout_error = TRU1;
1551 perror(tempMail);
1553 Ftmp_release(&tempMail);
1554 if (nfi == NULL)
1555 goto jerr_o;
1557 if ((ibuf = setinput(&mb, mp, NEED_BODY)) == NULL)
1558 goto jerr_all;
1560 head.h_to = to;
1561 to = fixhead(&head, to);
1563 if (infix_resend(ibuf, nfo, mp, head.h_to, add_resent) != 0) {
1564 savedeadletter(nfi, 1);
1565 fputs(tr(182, "... message not sent.\n"), stderr);
1566 jerr_all:
1567 Fclose(nfi);
1568 jerr_o:
1569 Fclose(nfo);
1570 _sendout_error = TRU1;
1571 goto jleave;
1573 Fclose(nfo);
1574 rewind(nfi);
1576 to = outof(to, nfi, &head, &_sendout_error);
1577 if (_sendout_error)
1578 savedeadletter(nfi, 0);
1580 to = elide(to); /* TODO should have been done in fixhead()? */
1582 if (count(to) != 0) {
1583 if (!ok_blook(record_resent) || mightrecord(nfi, to) == OKAY)
1584 rv = transfer(to, nfi, NULL);
1585 } else if (!_sendout_error)
1586 rv = OKAY;
1588 Fclose(nfi);
1589 jleave:
1590 if (_sendout_error)
1591 exit_status |= EXIT_SEND_ERROR;
1592 NYD_LEAVE;
1593 return rv;
1596 /* vim:set fenc=utf-8:s-it-mode */