Bump s-nail v14.6.1
[s-mailx.git] / sendout.c
blob264827e5801d285b32201f1b571e0dc76c586787
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 int err = 0;
151 char *charset_iter_orig[2];
152 long offs;
153 NYD_ENTER;
155 /* Is this already in target charset? */
156 if (ap->a_conv == AC_TMPFILE) {
157 err = __attach_file(ap, fo);
158 Fclose(ap->a_tmpf);
159 goto jleave;
162 /* We "consume" *ap*, so directly adjust it as we need it */
163 if (ap->a_conv == AC_FIX_INCS)
164 ap->a_charset = ap->a_input_charset;
166 if ((offs = ftell(fo)) == -1) {
167 err = EIO;
168 goto jleave;
171 charset_iter_recurse(charset_iter_orig);
172 for (charset_iter_reset(NULL); charset_iter_next() != NULL;) {
173 err = __attach_file(ap, fo);
174 if (err == 0 || (err != EILSEQ && err != EINVAL))
175 break;
176 clearerr(fo);
177 if (fseek(fo, offs, SEEK_SET) == -1) {
178 err = EIO;
179 break;
181 if (ap->a_conv != AC_DEFAULT) {
182 err = EILSEQ;
183 break;
185 ap->a_charset = NULL;
187 charset_iter_restore(charset_iter_orig);
188 jleave:
189 NYD_LEAVE;
190 return err;
193 static int
194 __attach_file(struct attachment *ap, FILE *fo) /* XXX linelength */
196 int err = 0, do_iconv;
197 FILE *fi;
198 char const *charset;
199 enum conversion convert;
200 char *buf;
201 size_t bufsize, lncnt, inlen;
202 NYD_ENTER;
204 /* Either charset-converted temporary file, or plain path */
205 if (ap->a_conv == AC_TMPFILE) {
206 fi = ap->a_tmpf;
207 assert(ftell(fi) == 0);
208 } else if ((fi = Fopen(ap->a_name, "r")) == NULL) {
209 err = errno;
210 perror(ap->a_name);
211 goto jleave;
214 /* MIME part header for attachment */
215 { char const *bn = ap->a_name, *ct;
217 if ((ct = strrchr(bn, '/')) != NULL)
218 bn = ++ct;
219 ct = ap->a_content_type;
220 charset = ap->a_charset;
221 convert = mime_classify_file(fi, (char const**)&ct, &charset, &do_iconv);
222 if (charset == NULL || ap->a_conv == AC_FIX_INCS ||
223 ap->a_conv == AC_TMPFILE)
224 do_iconv = 0;
226 if (fprintf(fo, "\n--%s\nContent-Type: %s", _sendout_boundary, ct) == -1)
227 goto jerr_header;
229 if (charset == NULL) {
230 if (putc('\n', fo) == EOF)
231 goto jerr_header;
232 } else if (fprintf(fo, "; charset=%s\n", charset) == -1)
233 goto jerr_header;
235 if (fprintf(fo, "Content-Transfer-Encoding: %s\n"
236 "Content-Disposition: %s;\n filename=\"",
237 _get_encoding(convert), ap->a_content_disposition) == -1)
238 goto jerr_header;
239 if (xmime_write(bn, strlen(bn), fo, CONV_TOHDR, TD_NONE, NULL) < 0)
240 goto jerr_header;
241 if (fwrite("\"\n", sizeof(char), 2, fo) != 2 * sizeof(char))
242 goto jerr_header;
244 if ((bn = ap->a_content_id) != NULL &&
245 fprintf(fo, "Content-ID: %s\n", bn) == -1)
246 goto jerr_header;
248 if ((bn = ap->a_content_description) != NULL &&
249 fprintf(fo, "Content-Description: %s\n", bn) == -1)
250 goto jerr_header;
252 if (putc('\n', fo) == EOF) {
253 jerr_header:
254 err = errno;
255 goto jerr_fclose;
259 #ifdef HAVE_ICONV
260 if (iconvd != (iconv_t)-1)
261 n_iconv_close(iconvd);
262 if (do_iconv) {
263 char const *tcs = charset_get_lc();
264 if (asccasecmp(charset, tcs) &&
265 (iconvd = n_iconv_open(charset, tcs)) == (iconv_t)-1 &&
266 (err = errno) != 0) {
267 if (err == EINVAL)
268 fprintf(stderr, tr(179, "Cannot convert from %s to %s\n"),
269 tcs, charset);
270 else
271 perror("iconv_open");
272 goto jerr_fclose;
275 #endif
277 bufsize = INFIX_BUF;
278 buf = smalloc(bufsize);
279 if (convert == CONV_TOQP
280 #ifdef HAVE_ICONV
281 || iconvd != (iconv_t)-1
282 #endif
284 lncnt = fsize(fi);
285 for (;;) {
286 if (convert == CONV_TOQP
287 #ifdef HAVE_ICONV
288 || iconvd != (iconv_t)-1
289 #endif
291 if (fgetline(&buf, &bufsize, &lncnt, &inlen, fi, 0) == NULL)
292 break;
293 } else if ((inlen = fread(buf, sizeof *buf, bufsize, fi)) == 0)
294 break;
295 if (xmime_write(buf, inlen, fo, convert, TD_ICONV, NULL) < 0) {
296 err = errno;
297 goto jerr;
300 if (ferror(fi))
301 err = EDOM;
302 jerr:
303 free(buf);
304 jerr_fclose:
305 if (ap->a_conv != AC_TMPFILE)
306 Fclose(fi);
307 jleave:
308 NYD_LEAVE;
309 return err;
312 static char const **
313 _prepare_mta_args(struct name *to, struct header *hp)
315 size_t j, i;
316 char const **args;
317 NYD_ENTER;
319 i = 4 + smopts_count + 2 + count(to) + 1;
320 args = salloc(i * sizeof(char*));
322 args[0] = ok_vlook(sendmail_progname);
323 if (args[0] == NULL || *args[0] == '\0')
324 args[0] = SENDMAIL_PROGNAME;
326 args[1] = "-i";
327 i = 2;
328 if (ok_blook(metoo))
329 args[i++] = "-m";
330 if (options & OPT_VERBOSE)
331 args[i++] = "-v";
333 for (j = 0; j < smopts_count; ++j, ++i)
334 args[i] = smopts[j];
336 /* -r option? We may only pass skinned addresses, which is why we do
337 * not simply call myorigin() (TODO myorigin shouldn't fullname!) */
338 if (options & OPT_r_FLAG) {
339 char const *froma;
341 if (option_r_arg[0] != '\0')
342 froma = option_r_arg;
343 else if (hp->h_from != NULL)
344 froma = hp->h_from->n_name;
345 else
346 froma = myorigin(hp);
347 if (froma != NULL) {
348 args[i++] = "-r";
349 args[i++] = froma;
353 /* Receivers follow */
354 for (; to != NULL; to = to->n_flink)
355 if (!(to->n_type & GDEL))
356 args[i++] = to->n_name;
357 args[i] = NULL;
358 NYD_LEAVE;
359 return args;
362 static struct name *
363 fixhead(struct header *hp, struct name *tolist)
365 struct name **npp, *np;
366 NYD_ENTER;
368 tolist = elide(tolist);
370 hp->h_to = hp->h_cc = hp->h_bcc = NULL;
371 for (np = tolist; np != NULL; np = np->n_flink) {
372 switch (np->n_type & (GDEL | GMASK)) {
373 case GTO: npp = &hp->h_to; break;
374 case GCC: npp = &hp->h_cc; break;
375 case GBCC: npp = &hp->h_bcc; break;
376 default: continue;
378 *npp = cat(*npp, ndup(np, np->n_type | GFULL));
380 NYD_LEAVE;
381 return tolist;
384 static int
385 put_signature(FILE *fo, int convert)
387 char buf[INFIX_BUF], *sig, c = '\n';
388 FILE *fsig;
389 size_t sz;
390 int rv;
391 NYD_ENTER;
393 if ((sig = ok_vlook(signature)) == NULL || *sig == '\0') {
394 rv = 0;
395 goto jleave;
397 rv = -1;
399 if ((sig = file_expand(sig)) == NULL)
400 goto jleave;
402 if ((fsig = Fopen(sig, "r")) == NULL) {
403 perror(sig);
404 goto jleave;
406 while ((sz = fread(buf, sizeof *buf, INFIX_BUF, fsig)) != 0) {
407 c = buf[sz - 1];
408 if (xmime_write(buf, sz, fo, convert, TD_NONE, NULL) < 0)
409 goto jerr;
411 if (ferror(fsig)) {
412 jerr:
413 perror(sig);
414 Fclose(fsig);
415 goto jleave;
417 Fclose(fsig);
418 if (c != '\n')
419 putc('\n', fo);
421 rv = 0;
422 jleave:
423 NYD_LEAVE;
424 return rv;
427 static int
428 attach_message(struct attachment *ap, FILE *fo)
430 struct message *mp;
431 char const *ccp;
432 int rv;
433 NYD_ENTER;
435 fprintf(fo, "\n--%s\nContent-Type: message/rfc822\n"
436 "Content-Disposition: inline\n", _sendout_boundary);
437 if ((ccp = ap->a_content_description) != NULL)
438 fprintf(fo, "Content-Description: %s\n", ccp);
439 fputc('\n', fo);
441 mp = message + ap->a_msgno - 1;
442 touch(mp);
443 rv = (sendmp(mp, fo, 0, NULL, SEND_RFC822, NULL) < 0) ? -1 : 0;
444 NYD_LEAVE;
445 return rv;
448 static int
449 make_multipart(struct header *hp, int convert, FILE *fi, FILE *fo,
450 char const *contenttype, char const *charset)
452 struct attachment *att;
453 int rv = -1;
454 NYD_ENTER;
456 fputs("This is a multi-part message in MIME format.\n", fo);
457 if (fsize(fi) != 0) {
458 char *buf;
459 size_t sz, bufsize, cnt;
461 fprintf(fo, "\n--%s\n", _sendout_boundary);
462 fprintf(fo, "Content-Type: %s", contenttype);
463 if (charset != NULL)
464 fprintf(fo, "; charset=%s", charset);
465 fprintf(fo, "\nContent-Transfer-Encoding: %s\n"
466 "Content-Disposition: inline\n\n", _get_encoding(convert));
468 buf = smalloc(bufsize = INFIX_BUF);
469 if (convert == CONV_TOQP
470 #ifdef HAVE_ICONV
471 || iconvd != (iconv_t)-1
472 #endif
474 fflush(fi);
475 cnt = fsize(fi);
477 for (;;) {
478 if (convert == CONV_TOQP
479 #ifdef HAVE_ICONV
480 || iconvd != (iconv_t)-1
481 #endif
483 if (fgetline(&buf, &bufsize, &cnt, &sz, fi, 0) == NULL)
484 break;
485 } else if ((sz = fread(buf, sizeof *buf, bufsize, fi)) == 0)
486 break;
488 if (xmime_write(buf, sz, fo, convert, TD_ICONV, NULL) < 0) {
489 free(buf);
490 goto jleave;
493 free(buf);
495 if (ferror(fi))
496 goto jleave;
497 if (charset != NULL)
498 put_signature(fo, convert);
501 for (att = hp->h_attach; att != NULL; att = att->a_flink) {
502 if (att->a_msgno) {
503 if (attach_message(att, fo) != 0)
504 goto jleave;
505 } else if (_attach_file(att, fo) != 0)
506 goto jleave;
509 /* the final boundary with two attached dashes */
510 fprintf(fo, "\n--%s--\n", _sendout_boundary);
511 rv = 0;
512 jleave:
513 NYD_LEAVE;
514 return rv;
517 static FILE *
518 infix(struct header *hp, FILE *fi) /* TODO check */
520 FILE *nfo, *nfi = NULL;
521 char *tempMail;
522 char const *contenttype, *charset = NULL;
523 enum conversion convert;
524 int do_iconv = 0, err;
525 #ifdef HAVE_ICONV
526 char const *tcs, *convhdr = NULL;
527 #endif
528 NYD_ENTER;
530 if ((nfo = Ftmp(&tempMail, "infix", OF_WRONLY | OF_HOLDSIGS | OF_REGISTER,
531 0600)) == NULL) {
532 perror(tr(178, "temporary mail file"));
533 goto jleave;
535 if ((nfi = Fopen(tempMail, "r")) == NULL) {
536 perror(tempMail);
537 Fclose(nfo);
539 Ftmp_release(&tempMail);
540 if (nfi == NULL)
541 goto jleave;
543 contenttype = "text/plain"; /* XXX mail body - always text/plain, want XX? */
544 convert = mime_classify_file(fi, &contenttype, &charset, &do_iconv);
546 #ifdef HAVE_ICONV
547 tcs = charset_get_lc();
548 if ((convhdr = need_hdrconv(hp, GTO | GSUBJECT | GCC | GBCC | GIDENT))) {
549 if (iconvd != (iconv_t)-1) /* XXX */
550 n_iconv_close(iconvd);
551 if (asccasecmp(convhdr, tcs) != 0 &&
552 (iconvd = n_iconv_open(convhdr, tcs)) == (iconv_t)-1 &&
553 (err = errno) != 0)
554 goto jiconv_err;
556 #endif
557 if (puthead(hp, nfo,
558 (GTO | GSUBJECT | GCC | GBCC | GNL | GCOMMA | GUA | GMIME | GMSGID |
559 GIDENT | GREF | GDATE), SEND_MBOX, convert, contenttype, charset))
560 goto jerr;
561 #ifdef HAVE_ICONV
562 if (iconvd != (iconv_t)-1)
563 n_iconv_close(iconvd);
564 #endif
566 #ifdef HAVE_ICONV
567 if (do_iconv && charset != NULL) { /*TODO charset->mime_classify_file*/
568 if (asccasecmp(charset, tcs) != 0 &&
569 (iconvd = n_iconv_open(charset, tcs)) == (iconv_t)-1 &&
570 (err = errno) != 0) {
571 jiconv_err:
572 if (err == EINVAL)
573 fprintf(stderr, tr(179, "Cannot convert from %s to %s\n"),
574 tcs, charset);
575 else
576 perror("iconv_open");
577 goto jerr;
580 #endif
582 if (hp->h_attach != NULL) {
583 if (make_multipart(hp, convert, fi, nfo, contenttype, charset) != 0)
584 goto jerr;
585 } else {
586 size_t sz, bufsize, cnt;
587 char *buf;
589 if (convert == CONV_TOQP
590 #ifdef HAVE_ICONV
591 || iconvd != (iconv_t)-1
592 #endif
594 fflush(fi);
595 cnt = fsize(fi);
597 buf = smalloc(bufsize = INFIX_BUF);
598 for (err = 0;;) {
599 if (convert == CONV_TOQP
600 #ifdef HAVE_ICONV
601 || iconvd != (iconv_t)-1
602 #endif
604 if (fgetline(&buf, &bufsize, &cnt, &sz, fi, 0) == NULL)
605 break;
606 } else if ((sz = fread(buf, sizeof *buf, bufsize, fi)) == 0)
607 break;
608 if (xmime_write(buf, sz, nfo, convert, TD_ICONV, NULL) < 0) {
609 err = 1;
610 break;
613 free(buf);
615 if (err || ferror(fi)) {
616 jerr:
617 Fclose(nfo);
618 Fclose(nfi);
619 #ifdef HAVE_ICONV
620 if (iconvd != (iconv_t)-1)
621 n_iconv_close(iconvd);
622 #endif
623 nfi = NULL;
624 goto jleave;
626 if (charset != NULL)
627 put_signature(nfo, convert); /* XXX if (text/) !! */
630 #ifdef HAVE_ICONV
631 if (iconvd != (iconv_t)-1)
632 n_iconv_close(iconvd);
633 #endif
635 fflush(nfo);
636 if ((err = ferror(nfo)))
637 perror(tr(180, "temporary mail file"));
638 Fclose(nfo);
639 if (!err) {
640 fflush_rewind(nfi);
641 Fclose(fi);
642 } else {
643 Fclose(nfi);
644 nfi = NULL;
646 jleave:
647 NYD_LEAVE;
648 return nfi;
651 static int
652 savemail(char const *name, FILE *fi)
654 FILE *fo;
655 char *buf;
656 size_t bufsize, buflen, cnt;
657 int prependnl = 0, rv = -1;
658 NYD_ENTER;
660 buf = smalloc(bufsize = LINESIZE);
662 if ((fo = Zopen(name, "a+", NULL)) == NULL) {
663 if ((fo = Zopen(name, "wx", NULL)) == NULL) {
664 perror(name);
665 goto jleave;
667 } else {
668 if (fseek(fo, -2L, SEEK_END) == 0) {
669 switch (fread(buf, sizeof *buf, 2, fo)) {
670 case 2:
671 if (buf[1] != '\n') {
672 prependnl = 1;
673 break;
675 /* FALLTHRU */
676 case 1:
677 if (buf[0] != '\n')
678 prependnl = 1;
679 break;
680 default:
681 if (ferror(fo)) {
682 perror(name);
683 goto jleave;
686 if (prependnl) {
687 putc('\n', fo);
689 fflush(fo);
693 fprintf(fo, "From %s %s", myname, time_current.tc_ctime);
694 fflush_rewind(fi);
695 cnt = fsize(fi);
696 buflen = 0;
697 while (fgetline(&buf, &bufsize, &cnt, &buflen, fi, 0) != NULL) {
698 #ifdef HAVE_DEBUG /* TODO assert legacy */
699 assert(!is_head(buf, buflen));
700 #else
701 if (is_head(buf, buflen))
702 putc('>', fo);
703 #endif
704 fwrite(buf, sizeof *buf, buflen, fo);
706 if (buflen && *(buf + buflen - 1) != '\n')
707 putc('\n', fo);
708 putc('\n', fo);
709 fflush(fo);
711 rv = 0;
712 if (ferror(fo)) {
713 perror(name);
714 rv = -1;
716 if (Fclose(fo) != 0)
717 rv = -1;
718 fflush_rewind(fi);
719 jleave:
720 free(buf);
721 NYD_LEAVE;
722 return rv;
725 static int
726 sendmail_internal(void *v, int recipient_record)
728 struct header head;
729 char *str = v;
730 int rv;
731 NYD_ENTER;
733 memset(&head, 0, sizeof head);
734 head.h_to = lextract(str, GTO | GFULL);
735 rv = mail1(&head, 0, NULL, NULL, recipient_record, 0);
736 NYD_LEAVE;
737 return rv;
740 static enum okay
741 transfer(struct name *to, FILE *input, struct header *hp)
743 char o[LINESIZE], *cp;
744 struct name *np;
745 int cnt = 0;
746 enum okay rv = OKAY;
747 NYD_ENTER;
749 np = to;
750 while (np != NULL) {
751 snprintf(o, sizeof o, "smime-encrypt-%s", np->n_name);/* XXX */
752 if ((cp = vok_vlook(o)) != NULL) {
753 #ifdef HAVE_SSL
754 struct name *nt;
755 FILE *ef;
757 if ((ef = smime_encrypt(input, cp, np->n_name)) != NULL) {
758 nt = ndup(np, np->n_type & ~(GFULL | GSKIN));
759 rv = start_mta(nt, ef, hp);
760 Fclose(ef);
761 } else {
762 #else
763 fprintf(stderr, tr(225, "No SSL support compiled in.\n"));
764 rv = STOP;
765 #endif
766 fprintf(stderr, tr(38, "Message not sent to <%s>\n"), np->n_name);
767 _sendout_error = TRU1;
768 #ifdef HAVE_SSL
770 #endif
771 rewind(input);
773 if (np->n_flink != NULL)
774 np->n_flink->n_blink = np->n_blink;
775 if (np->n_blink != NULL)
776 np->n_blink->n_flink = np->n_flink;
777 if (np == to)
778 to = np->n_flink;
779 np = np->n_flink;
780 } else {
781 ++cnt;
782 np = np->n_flink;
786 if (cnt)
787 if (ok_blook(smime_force_encryption) || start_mta(to, input, hp) != OKAY)
788 rv = STOP;
789 NYD_LEAVE;
790 return rv;
793 static enum okay
794 start_mta(struct name *to, FILE *input, struct header *hp)
796 #ifdef HAVE_SMTP
797 char *user = NULL, *password = NULL, *skinned = NULL;
798 #endif
799 char const **args = NULL, **t, *mta;
800 char *smtp;
801 pid_t pid;
802 sigset_t nset;
803 enum okay rv = STOP;
804 NYD_ENTER;
805 UNUSED(hp);
807 if ((smtp = ok_vlook(smtp)) == NULL) {
808 if ((mta = ok_vlook(sendmail)) != NULL) {
809 if ((mta = file_expand(mta)) == NULL)
810 goto jstop;
811 } else
812 mta = SENDMAIL;
814 args = _prepare_mta_args(to, hp);
815 if (options & OPT_DEBUG) {
816 printf(tr(181, "Sendmail arguments:"));
817 for (t = args; *t != NULL; ++t)
818 printf(" \"%s\"", *t);
819 printf("\n");
820 rv = OKAY;
821 goto jleave;
823 } else {
824 mta = NULL; /* Silence cc */
825 #ifndef HAVE_SMTP
826 fputs(tr(194, "No SMTP support compiled in.\n"), stderr);
827 goto jstop;
828 #else
829 skinned = skin(myorigin(hp));
830 if ((user = smtp_auth_var("-user", skinned)) != NULL &&
831 (password = smtp_auth_var("-password", skinned)) == NULL)
832 password = getpassword(NULL);
833 #endif
836 /* Fork, set up the temporary mail file as standard input for "mail", and
837 * exec with the user list we generated far above */
838 if ((pid = fork()) == -1) {
839 perror("fork");
840 jstop:
841 savedeadletter(input, 0);
842 _sendout_error = TRU1;
843 goto jleave;
845 if (pid == 0) {
846 sigemptyset(&nset);
847 sigaddset(&nset, SIGHUP);
848 sigaddset(&nset, SIGINT);
849 sigaddset(&nset, SIGQUIT);
850 sigaddset(&nset, SIGTSTP);
851 sigaddset(&nset, SIGTTIN);
852 sigaddset(&nset, SIGTTOU);
853 freopen("/dev/null", "r", stdin);
854 #ifdef HAVE_SMTP
855 if (smtp != NULL) {
856 prepare_child(&nset, 0, 1);
857 if (smtp_mta(smtp, to, input, hp, user, password, skinned) == 0)
858 _exit(0);
859 } else {
860 #endif
861 prepare_child(&nset, fileno(input), -1);
862 /* If *record* is set then savemail() will move the file position;
863 * it'll call rewind(), but that may optimize away the systemcall if
864 * possible, and since dup2() shares the position with the original FD
865 * the MTA may end up reading nothing */
866 lseek(0, 0, SEEK_SET);
867 execv(mta, UNCONST(args));
868 perror(mta);
869 #ifdef HAVE_SMTP
871 #endif
872 savedeadletter(input, 1);
873 fputs(tr(182, ". . . message not sent.\n"), stderr);
874 _exit(1);
876 if ((options & (OPT_DEBUG | OPT_VERBOSE | OPT_BATCH_FLAG)) ||
877 ok_blook(sendwait)) {
878 if (wait_child(pid, NULL))
879 rv = OKAY;
880 else
881 _sendout_error = TRU1;
882 } else {
883 rv = OKAY;
884 free_child(pid);
886 jleave:
887 NYD_LEAVE;
888 return rv;
891 static enum okay
892 mightrecord(FILE *fp, struct name *to)
894 char *cp, *cq;
895 char const *ep;
896 enum okay rv = OKAY;
897 NYD_ENTER;
899 if (to != NULL) {
900 cp = savestr(skinned_name(to));
901 for (cq = cp; *cq != '\0' && *cq != '@'; ++cq)
903 *cq = '\0';
904 } else
905 cp = ok_vlook(record);
907 if (cp != NULL) {
908 if ((ep = expand(cp)) == NULL) {
909 ep = "NULL";
910 goto jbail;
913 if (*ep != '/' && *ep != '+' && ok_blook(outfolder) &&
914 which_protocol(ep) == PROTO_FILE) {
915 size_t i = strlen(cp);
916 cq = salloc(i + 1 +1);
917 cq[0] = '+';
918 memcpy(cq + 1, cp, i +1);
919 cp = cq;
920 if ((ep = file_expand(cp)) == NULL) {
921 ep = "NULL";
922 goto jbail;
926 if (savemail(ep, fp) != 0) {
927 jbail:
928 fprintf(stderr, tr(285, "Failed to save message in %s - "
929 "message not sent\n"), ep);
930 exit_status |= EXIT_ERR;
931 savedeadletter(fp, 1);
932 rv = STOP;
935 NYD_LEAVE;
936 return rv;
939 static void
940 message_id(FILE *fo, struct header *hp)
942 char const *h;
943 size_t rl;
944 struct tm *tmp;
945 NYD_ENTER;
947 if (ok_blook(message_id_disable))
948 goto jleave;
950 if ((h = ok_vlook(hostname)) != NULL)
951 rl = 24;
952 else if ((h = skin(myorigin(hp))) != NULL && strchr(h, '@') != NULL)
953 rl = 16;
954 else
955 /* Up to MTA */
956 goto jleave;
958 tmp = &time_current.tc_gm;
959 fprintf(fo, "Message-ID: <%04d%02d%02d%02d%02d%02d.%s%c%s>\n",
960 tmp->tm_year + 1900, tmp->tm_mon + 1, tmp->tm_mday,
961 tmp->tm_hour, tmp->tm_min, tmp->tm_sec,
962 getrandstring(rl), (rl == 16 ? '%' : '@'), h);
963 jleave:
964 NYD_LEAVE;
967 static int
968 fmt(char const *str, struct name *np, FILE *fo, int flags, int dropinvalid,
969 int domime)
971 enum {
972 m_INIT = 1<<0,
973 m_COMMA = 1<<1,
974 m_NOPF = 1<<2,
975 m_CSEEN = 1<<3
976 } m = (flags & GCOMMA) ? m_COMMA : 0;
977 ssize_t col, len;
978 int rv = 1;
979 NYD_ENTER;
981 col = strlen(str);
982 if (col) {
983 fwrite(str, sizeof *str, col, fo);
984 if (flags & GFILES)
985 goto jstep;
986 if (col == 9 && !asccasecmp(str, "reply-to:")) {
987 m |= m_NOPF;
988 goto jstep;
990 if (ok_blook(add_file_recipients))
991 goto jstep;
992 if ((col == 3 && (!asccasecmp(str, "to:") || !asccasecmp(str, "cc:"))) ||
993 (col == 4 && !asccasecmp(str, "bcc:")) ||
994 (col == 10 && !asccasecmp(str, "Resent-To:")))
995 m |= m_NOPF;
997 jstep:
998 for (; np != NULL; np = np->n_flink) {
999 if ((m & m_NOPF) && is_fileorpipe_addr(np))
1000 continue;
1001 if (is_addr_invalid(np, !dropinvalid)) {
1002 if (dropinvalid)
1003 continue;
1004 else
1005 goto jleave;
1007 if ((m & (m_INIT | m_COMMA)) == (m_INIT | m_COMMA)) {
1008 putc(',', fo);
1009 m |= m_CSEEN;
1010 ++col;
1012 len = strlen(np->n_fullname);
1013 ++col; /* The separating space */
1014 if ((m & m_INIT) && col > 1 && col + len > 72) {
1015 fputs("\n ", fo);
1016 col = 1;
1017 m &= ~m_CSEEN;
1018 } else
1019 putc(' ', fo);
1020 m = (m & ~m_CSEEN) | m_INIT;
1021 len = xmime_write(np->n_fullname, len, fo,
1022 (domime ? CONV_TOHDR_A : CONV_NONE), TD_ICONV, NULL);
1023 if (len < 0)
1024 goto jleave;
1025 col += len;
1027 putc('\n', fo);
1028 rv = 0;
1029 jleave:
1030 NYD_LEAVE;
1031 return rv;
1034 static int
1035 infix_resend(FILE *fi, FILE *fo, struct message *mp, struct name *to,
1036 int add_resent)
1038 size_t cnt, c, bufsize = 0;
1039 char *buf = NULL;
1040 char const *cp;
1041 struct name *fromfield = NULL, *senderfield = NULL;
1042 int rv = 1;
1043 NYD_ENTER;
1045 cnt = mp->m_size;
1047 /* Write the Resent-Fields */
1048 if (add_resent) {
1049 fputs("Resent-", fo);
1050 mkdate(fo, "Date");
1051 if ((cp = myaddrs(NULL)) != NULL) {
1052 if (_putname(cp, GCOMMA, SEND_MBOX, NULL, "Resent-From:", fo,
1053 &fromfield))
1054 goto jleave;
1056 if ((cp = ok_vlook(sender)) != NULL) {
1057 if (_putname(cp, GCOMMA, SEND_MBOX, NULL, "Resent-Sender:", fo,
1058 &senderfield))
1059 goto jleave;
1061 if (fmt("Resent-To:", to, fo, 1, 1, 0))
1062 goto jleave;
1063 if ((cp = ok_vlook(stealthmua)) == NULL || !strcmp(cp, "noagent")) {
1064 fputs("Resent-", fo);
1065 message_id(fo, NULL);
1068 if (check_from_and_sender(fromfield, senderfield))
1069 goto jleave;
1071 /* Write the original headers */
1072 while (cnt > 0) {
1073 if (fgetline(&buf, &bufsize, &cnt, &c, fi, 0) == NULL)
1074 break;
1075 /* XXX more checks: The From_ line may be seen when resending */
1076 /* During headers is_head() is actually overkill, so ^From_ is sufficient
1077 * && !is_head(buf, c) */
1078 if (ascncasecmp("status: ", buf, 8) && strncmp("From ", buf, 5))
1079 fwrite(buf, sizeof *buf, c, fo);
1080 if (cnt > 0 && *buf == '\n')
1081 break;
1084 /* Write the message body */
1085 while (cnt > 0) {
1086 if (fgetline(&buf, &bufsize, &cnt, &c, fi, 0) == NULL)
1087 break;
1088 if (cnt == 0 && *buf == '\n')
1089 break;
1090 fwrite(buf, sizeof *buf, c, fo);
1092 if (buf != NULL)
1093 free(buf);
1094 if (ferror(fo)) {
1095 perror(tr(188, "temporary mail file"));
1096 goto jleave;
1098 rv = 0;
1099 jleave:
1100 NYD_LEAVE;
1101 return rv;
1104 FL int
1105 mail(struct name *to, struct name *cc, struct name *bcc, char *subject,
1106 struct attachment *attach, char *quotefile, int recipient_record)
1108 struct header head;
1109 struct str in, out;
1110 NYD_ENTER;
1112 memset(&head, 0, sizeof head);
1114 /* The given subject may be in RFC1522 format. */
1115 if (subject != NULL) {
1116 in.s = subject;
1117 in.l = strlen(subject);
1118 mime_fromhdr(&in, &out, /* TODO ??? TD_ISPR |*/ TD_ICONV);
1119 head.h_subject = out.s;
1121 if (!(options & OPT_t_FLAG)) {
1122 head.h_to = to;
1123 head.h_cc = cc;
1124 head.h_bcc = bcc;
1126 head.h_attach = attach;
1128 mail1(&head, 0, NULL, quotefile, recipient_record, 0);
1130 if (subject != NULL)
1131 free(out.s);
1132 NYD_LEAVE;
1133 return 0;
1136 FL int
1137 c_sendmail(void *v)
1139 int rv;
1140 NYD_ENTER;
1142 rv = sendmail_internal(v, 0);
1143 NYD_LEAVE;
1144 return rv;
1147 FL int
1148 c_Sendmail(void *v)
1150 int rv;
1151 NYD_ENTER;
1153 rv = sendmail_internal(v, 1);
1154 NYD_LEAVE;
1155 return rv;
1158 FL enum okay
1159 mail1(struct header *hp, int printheaders, struct message *quote,
1160 char *quotefile, int recipient_record, int doprefix)
1162 struct name *to;
1163 FILE *mtf, *nmtf;
1164 int dosign = -1, err;
1165 char const *cp;
1166 enum okay rv = STOP;
1167 NYD_ENTER;
1169 _sendout_error = FAL0;
1171 /* Update some globals we likely need first */
1172 time_current_update(&time_current, TRU1);
1174 /* */
1175 if ((cp = ok_vlook(autocc)) != NULL && *cp != '\0')
1176 hp->h_cc = cat(hp->h_cc, checkaddrs(lextract(cp, GCC | GFULL)));
1177 if ((cp = ok_vlook(autobcc)) != NULL && *cp != '\0')
1178 hp->h_bcc = cat(hp->h_bcc, checkaddrs(lextract(cp, GBCC | GFULL)));
1180 /* Collect user's mail from standard input. Get the result as mtf */
1181 mtf = collect(hp, printheaders, quote, quotefile, doprefix);
1182 if (mtf == NULL)
1183 goto j_leave;
1185 if (options & OPT_INTERACTIVE) {
1186 err = (ok_blook(bsdcompat) || ok_blook(askatend));
1187 if (err == 0)
1188 goto jaskeot;
1189 if (ok_blook(askcc))
1190 ++err, grab_headers(hp, GCC, 1);
1191 if (ok_blook(askbcc))
1192 ++err, grab_headers(hp, GBCC, 1);
1193 if (ok_blook(askattach))
1194 ++err, hp->h_attach = edit_attachments(hp->h_attach);
1195 if (ok_blook(asksign))
1196 ++err, dosign = yorn(tr(35, "Sign this message (y/n)? "));
1197 if (err == 1) {
1198 jaskeot:
1199 printf(tr(183, "EOT\n"));
1200 fflush(stdout);
1204 if (fsize(mtf) == 0) {
1205 if (options & OPT_E_FLAG)
1206 goto jleave;
1207 if (hp->h_subject == NULL)
1208 printf(tr(184, "No message, no subject; hope that's ok\n"));
1209 else if (ok_blook(bsdcompat) || ok_blook(bsdmsgs))
1210 printf(tr(185, "Null message body; hope that's ok\n"));
1213 if (dosign < 0)
1214 dosign = ok_blook(smime_sign);
1215 #ifndef HAVE_SSL
1216 if (dosign) {
1217 fprintf(stderr, tr(225, "No SSL support compiled in.\n"));
1218 goto jleave;
1220 #endif
1222 /* XXX Update time_current again; once collect() offers editing of more
1223 * XXX headers, including Date:, this must only happen if Date: is the
1224 * XXX same that it was before collect() (e.g., postponing etc.).
1225 * XXX But *do* update otherwise because the mail seems to be backdated
1226 * XXX if the user edited some time, which looks odd and it happened
1227 * XXX to me that i got mis-dated response mails due to that... */
1228 time_current_update(&time_current, TRU1);
1230 /* TODO hrmpf; the MIME/send layer rewrite MUST address the init crap:
1231 * TODO setup the header ONCE; note this affects edit.c, collect.c ...,
1232 * TODO but: offer a hook that rebuilds/expands/checks/fixates all
1233 * TODO header fields ONCE, call that ONCE after user editing etc. has
1234 * TODO completed (one edit cycle) */
1236 /* Take the user names from the combined to and cc lists and do all the
1237 * alias processing. The POSIX standard says:
1238 * The names shall be substituted when alias is used as a recipient
1239 * address specified by the user in an outgoing message (that is,
1240 * other recipients addressed indirectly through the reply command
1241 * shall not be substituted in this manner).
1242 * S-nail thus violates POSIX, as has been pointed out correctly by
1243 * Martin Neitzel, but logic and usability of POSIX standards is not seldom
1244 * disputable anyway. Go for user friendliness */
1246 /* Do alias expansion on Reply-To: members, too */
1247 /* TODO puthead() YET (!!! see ONCE note above) expands the value, but
1248 * TODO doesn't perform alias expansion; encapsulate in the ONCE-o */
1249 if (hp->h_replyto == NULL && (cp = ok_vlook(replyto)) != NULL)
1250 hp->h_replyto = checkaddrs(lextract(cp, GEXTRA | GFULL));
1251 if (hp->h_replyto != NULL)
1252 hp->h_replyto = elide(usermap(hp->h_replyto, TRU1));
1254 /* TODO what happens now is that all recipients are merged into
1255 * TODO a duplicated list with expanded aliases, then this list is
1256 * TODO splitted again into the three individual recipient lists (with
1257 * TODO duplicates removed).
1258 * TODO later on we use the merged list for outof() pipe/file saving,
1259 * TODO then we eliminate duplicates (again) and then we use that one
1260 * TODO for mightrecord() and transfer(), and again. ... Please ... */
1262 /* NOTE: Due to elide() in fixhead(), ENSURE to,cc,bcc order of to!,
1263 * because otherwise the recipients will be "degraded" if they occur
1264 * multiple times */
1265 to = usermap(cat(hp->h_to, cat(hp->h_cc, hp->h_bcc)), FAL0);
1266 if (to == NULL) {
1267 fprintf(stderr, tr(186, "No recipients specified\n"));
1268 _sendout_error = TRU1;
1270 to = fixhead(hp, to);
1272 /* 'Bit ugly kind of control flow until we find a charset that does it */
1273 for (charset_iter_reset(hp->h_charset);;) {
1274 if (charset_iter_next() == NULL)
1276 else if ((nmtf = infix(hp, mtf)) != NULL)
1277 break;
1278 else if ((err = errno) == EILSEQ || err == EINVAL) {
1279 rewind(mtf);
1280 continue;
1283 perror("");
1284 #ifdef HAVE_SSL
1285 jfail_dead:
1286 #endif
1287 _sendout_error = TRU1;
1288 savedeadletter(mtf, 1);
1289 fputs(tr(187, ". . . message not sent.\n"), stderr);
1290 goto jleave;
1293 mtf = nmtf;
1294 #ifdef HAVE_SSL
1295 if (dosign) {
1296 if ((nmtf = smime_sign(mtf, hp)) == NULL)
1297 goto jfail_dead;
1298 Fclose(mtf);
1299 mtf = nmtf;
1301 #endif
1303 /* TODO truly - i still don't get what follows: (1) we deliver file
1304 * TODO and pipe addressees, (2) we mightrecord() and (3) we transfer
1305 * TODO even if (1) savedeadletter() etc. To me this doesn't make sense? */
1307 /* Deliver pipe and file addressees */
1308 to = outof(to, mtf, hp, &_sendout_error);
1309 if (_sendout_error)
1310 savedeadletter(mtf, 0);
1312 to = elide(to); /* XXX needed only to drop GDELs due to outof()! */
1313 dosign = (int)count(to); /* XXX ui32; reuse as "do-transfer" */
1314 if ((!recipient_record || dosign) &&
1315 mightrecord(mtf, (recipient_record ? to : NULL)) != OKAY)
1316 goto jleave;
1317 if (dosign)
1318 rv = transfer(to, mtf, hp);
1319 else if (!_sendout_error)
1320 rv = OKAY;
1322 jleave:
1323 Fclose(mtf);
1324 j_leave:
1325 if (_sendout_error)
1326 exit_status |= EXIT_SEND_ERROR;
1327 NYD_LEAVE;
1328 return rv;
1331 FL int
1332 mkdate(FILE *fo, char const *field)
1334 struct tm *tmptr;
1335 int tzdiff, tzdiff_hour, tzdiff_min, rv;
1336 NYD_ENTER;
1338 tzdiff = time_current.tc_time - mktime(&time_current.tc_gm);
1339 tzdiff_hour = (int)(tzdiff / 60);
1340 tzdiff_min = tzdiff_hour % 60;
1341 tzdiff_hour /= 60;
1342 tmptr = &time_current.tc_local;
1343 if (tmptr->tm_isdst > 0)
1344 ++tzdiff_hour;
1345 rv = fprintf(fo, "%s: %s, %02d %s %04d %02d:%02d:%02d %+05d\n",
1346 field,
1347 weekday_names[tmptr->tm_wday],
1348 tmptr->tm_mday, month_names[tmptr->tm_mon],
1349 tmptr->tm_year + 1900, tmptr->tm_hour,
1350 tmptr->tm_min, tmptr->tm_sec,
1351 tzdiff_hour * 100 + tzdiff_min);
1352 NYD_LEAVE;
1353 return rv;
1356 FL int
1357 puthead(struct header *hp, FILE *fo, enum gfield w, enum sendaction action,
1358 enum conversion convert, char const *contenttype, char const *charset)
1360 #define FMT_CC_AND_BCC() \
1361 do {\
1362 if (hp->h_cc != NULL && (w & GCC)) {\
1363 if (fmt("Cc:", hp->h_cc, fo, (w & (GCOMMA | GFILES)), 0,\
1364 (action != SEND_TODISP)))\
1365 goto jleave;\
1366 ++gotcha;\
1368 if (hp->h_bcc != NULL && (w & GBCC)) {\
1369 if (fmt("Bcc:", hp->h_bcc, fo, (w & (GCOMMA | GFILES)), 0,\
1370 (action != SEND_TODISP)))\
1371 goto jleave;\
1372 ++gotcha;\
1374 } while (0)
1376 char const *addr;
1377 size_t gotcha, l;
1378 struct name *np, *fromfield = NULL, *senderfield = NULL;
1379 int stealthmua, rv = 1;
1380 bool_t nodisp;
1381 NYD_ENTER;
1383 if ((addr = ok_vlook(stealthmua)) != NULL)
1384 stealthmua = !strcmp(addr, "noagent") ? -1 : 1;
1385 else
1386 stealthmua = 0;
1387 gotcha = 0;
1388 nodisp = (action != SEND_TODISP);
1390 if (w & GDATE)
1391 mkdate(fo, "Date"), ++gotcha;
1392 if (w & GIDENT) {
1393 if (hp->h_from != NULL) {
1394 if (fmt("From:", hp->h_from, fo, (w & (GCOMMA | GFILES)), 0, nodisp))
1395 goto jleave;
1396 ++gotcha;
1397 fromfield = hp->h_from;
1398 } else if ((addr = myaddrs(hp)) != NULL) {
1399 if (_putname(addr, w, action, &gotcha, "From:", fo, &fromfield))
1400 goto jleave;
1401 hp->h_from = fromfield;
1404 if (((addr = hp->h_organization) != NULL ||
1405 (addr = ok_vlook(ORGANIZATION)) != NULL) &&
1406 (l = strlen(addr)) > 0) {
1407 fwrite("Organization: ", sizeof(char), 14, fo);
1408 if (xmime_write(addr, l, fo, (!nodisp ? CONV_NONE : CONV_TOHDR),
1409 (!nodisp ? TD_ISPR | TD_ICONV : TD_ICONV), NULL) < 0)
1410 goto jleave;
1411 ++gotcha;
1412 putc('\n', fo);
1415 /* TODO see the ONCE TODO note somewhere around this file;
1416 * TODO but anyway, do NOT perform alias expansion UNLESS
1417 * TODO we are actually sending out! */
1418 if (hp->h_replyto != NULL) {
1419 if (fmt("Reply-To:", hp->h_replyto, fo, w & GCOMMA, 0, nodisp))
1420 goto jleave;
1421 ++gotcha;
1422 } else if ((addr = ok_vlook(replyto)) != NULL)
1423 if (_putname(addr, w, action, &gotcha, "Reply-To:", fo, NULL))
1424 goto jleave;
1426 if (hp->h_sender != NULL) {
1427 if (fmt("Sender:", hp->h_sender, fo, w & GCOMMA, 0, nodisp))
1428 goto jleave;
1429 ++gotcha;
1430 senderfield = hp->h_sender;
1431 } else if ((addr = ok_vlook(sender)) != NULL)
1432 if (_putname(addr, w, action, &gotcha, "Sender:", fo, &senderfield))
1433 goto jleave;
1435 if (check_from_and_sender(fromfield, senderfield))
1436 goto jleave;
1439 if (hp->h_to != NULL && w & GTO) {
1440 if (fmt("To:", hp->h_to, fo, (w & (GCOMMA | GFILES)), 0, nodisp))
1441 goto jleave;
1442 ++gotcha;
1445 if (!ok_blook(bsdcompat) && !ok_blook(bsdorder))
1446 FMT_CC_AND_BCC();
1448 if (hp->h_subject != NULL && (w & GSUBJECT)) {
1449 fwrite("Subject: ", sizeof (char), 9, fo);
1450 if (!ascncasecmp(hp->h_subject, "re: ", 4)) {/* TODO localizable */
1451 fwrite("Re: ", sizeof(char), 4, fo);
1452 if (strlen(hp->h_subject + 4) > 0 &&
1453 xmime_write(hp->h_subject + 4, strlen(hp->h_subject + 4), fo,
1454 (!nodisp ? CONV_NONE : CONV_TOHDR),
1455 (!nodisp ? TD_ISPR | TD_ICONV : TD_ICONV), NULL) < 0)
1456 goto jleave;
1457 } else if (*hp->h_subject != '\0') {
1458 if (xmime_write(hp->h_subject, strlen(hp->h_subject), fo,
1459 (!nodisp ? CONV_NONE : CONV_TOHDR),
1460 (!nodisp ? TD_ISPR | TD_ICONV : TD_ICONV), NULL) < 0)
1461 goto jleave;
1463 ++gotcha;
1464 fwrite("\n", sizeof (char), 1, fo);
1467 if (ok_blook(bsdcompat) || ok_blook(bsdorder))
1468 FMT_CC_AND_BCC();
1470 if ((w & GMSGID) && stealthmua <= 0)
1471 message_id(fo, hp), ++gotcha;
1473 if ((np = hp->h_ref) != NULL && (w & GREF)) {
1474 fmt("References:", np, fo, 0, 1, 0);
1475 if (np->n_name != NULL) {
1476 while (np->n_flink != NULL)
1477 np = np->n_flink;
1478 if (!is_addr_invalid(np, 0)) {
1479 fprintf(fo, "In-Reply-To: %s\n", np->n_name);
1480 ++gotcha;
1485 if ((w & GUA) && stealthmua == 0)
1486 fprintf(fo, "User-Agent: %s %s\n", uagent, version), ++gotcha;
1488 if (w & GMIME) {
1489 fputs("MIME-Version: 1.0\n", fo), ++gotcha;
1490 if (hp->h_attach != NULL) {
1491 _sendout_boundary = mime_create_boundary();/*TODO carrier*/
1492 fprintf(fo, "Content-Type: multipart/mixed;\n boundary=\"%s\"\n",
1493 _sendout_boundary);
1494 } else {
1495 fprintf(fo, "Content-Type: %s", contenttype);
1496 if (charset != NULL)
1497 fprintf(fo, "; charset=%s", charset);
1498 fprintf(fo, "\nContent-Transfer-Encoding: %s\n",
1499 _get_encoding(convert));
1503 if (gotcha && (w & GNL))
1504 putc('\n', fo);
1505 rv = 0;
1506 jleave:
1507 NYD_LEAVE;
1508 return rv;
1509 #undef FMT_CC_AND_BCC
1512 FL enum okay
1513 resend_msg(struct message *mp, struct name *to, int add_resent) /* TODO check */
1515 FILE *ibuf, *nfo, *nfi;
1516 char *tempMail;
1517 struct header head;
1518 enum okay rv = STOP;
1519 NYD_ENTER;
1521 _sendout_error = FAL0;
1523 /* Update some globals we likely need first */
1524 time_current_update(&time_current, TRU1);
1526 memset(&head, 0, sizeof head);
1528 if ((to = checkaddrs(to)) == NULL) {
1529 _sendout_error = TRU1;
1530 goto jleave;
1533 if ((nfo = Ftmp(&tempMail, "resend", OF_WRONLY | OF_HOLDSIGS | OF_REGISTER,
1534 0600)) == NULL) {
1535 _sendout_error = TRU1;
1536 perror(tr(189, "temporary mail file"));
1537 goto jleave;
1539 if ((nfi = Fopen(tempMail, "r")) == NULL) {
1540 _sendout_error = TRU1;
1541 perror(tempMail);
1543 Ftmp_release(&tempMail);
1544 if (nfi == NULL)
1545 goto jerr_o;
1547 if ((ibuf = setinput(&mb, mp, NEED_BODY)) == NULL)
1548 goto jerr_all;
1550 head.h_to = to;
1551 to = fixhead(&head, to);
1553 if (infix_resend(ibuf, nfo, mp, head.h_to, add_resent) != 0) {
1554 savedeadletter(nfi, 1);
1555 fputs(tr(190, ". . . message not sent.\n"), stderr);
1556 jerr_all:
1557 Fclose(nfi);
1558 jerr_o:
1559 Fclose(nfo);
1560 _sendout_error = TRU1;
1561 goto jleave;
1563 Fclose(nfo);
1564 rewind(nfi);
1566 to = outof(to, nfi, &head, &_sendout_error);
1567 if (_sendout_error)
1568 savedeadletter(nfi, 0);
1570 to = elide(to); /* TODO should have been done in fixhead()? */
1572 if (count(to) != 0) {
1573 if (!ok_blook(record_resent) || mightrecord(nfi, to) == OKAY)
1574 rv = transfer(to, nfi, NULL);
1575 } else if (!_sendout_error)
1576 rv = OKAY;
1578 Fclose(nfi);
1579 jleave:
1580 if (_sendout_error)
1581 exit_status |= EXIT_SEND_ERROR;
1582 NYD_LEAVE;
1583 return rv;
1586 /* vim:set fenc=utf-8:s-it-mode */