Merge branch 'topic/srelax'
[s-mailx.git] / sendout.c
blobf705246d7927d7eeba57ef90f4439cf80d869558
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 - 2013 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 #include "nail.h"
42 #include <fcntl.h>
44 #define INFIX_BUF \
45 ((1024 / B64_ENCODE_INPUT_PER_LINE) * B64_ENCODE_INPUT_PER_LINE)
47 static char *send_boundary;
48 static bool_t _senderror;
50 static enum okay _putname(char const *line, enum gfield w,
51 enum sendaction action, int *gotcha,
52 char const *prefix, FILE *fo, struct name **xp);
54 /* Get an encoding flag based on the given string */
55 static char const * _get_encoding(const enum conversion convert);
57 /* Write an attachment to the file buffer, converting to MIME */
58 static int _attach_file(struct attachment *ap, FILE *fo);
59 static int __attach_file(struct attachment *ap, FILE *fo);
61 static char const ** _prepare_mta_args(struct name *to, struct header *hp);
63 static struct name *fixhead(struct header *hp, struct name *tolist);
64 static int put_signature(FILE *fo, int convert);
65 static int attach_message(struct attachment *ap, FILE *fo);
66 static int make_multipart(struct header *hp, int convert, FILE *fi, FILE *fo,
67 const char *contenttype, const char *charset);
68 static FILE *infix(struct header *hp, FILE *fi);
69 static int savemail(char const *name, FILE *fi);
70 static int sendmail_internal(void *v, int recipient_record);
71 static enum okay transfer(struct name *to, FILE *input, struct header *hp);
72 static enum okay start_mta(struct name *to, FILE *input, struct header *hp);
73 static void message_id(FILE *fo, struct header *hp);
74 static int fmt(char const *str, struct name *np, FILE *fo, int comma,
75 int dropinvalid, int domime);
76 static int infix_resend(FILE *fi, FILE *fo, struct message *mp,
77 struct name *to, int add_resent);
79 static enum okay
80 _putname(char const *line, enum gfield w, enum sendaction action, int *gotcha,
81 char const *prefix, FILE *fo, struct name **xp)
83 enum okay ret = STOP;
84 struct name *np;
86 np = lextract(line, GEXTRA|GFULL);
87 if (xp)
88 *xp = np;
89 if (np == NULL)
91 else if (fmt(prefix, np, fo, w & GCOMMA, 0, action != SEND_TODISP))
92 ret = OKAY;
93 else if (gotcha)
94 ++(*gotcha);
95 return (ret);
98 static char const *
99 _get_encoding(enum conversion const convert)
101 char const *ret;
102 switch (convert) {
103 case CONV_7BIT: ret = "7bit"; break;
104 case CONV_8BIT: ret = "8bit"; break;
105 case CONV_TOQP: ret = "quoted-printable"; break;
106 case CONV_TOB64: ret = "base64"; break;
107 default: ret = NULL; break;
109 return (ret);
112 static int
113 _attach_file(struct attachment *ap, FILE *fo)
115 /* TODO of course, the MIME classification needs to performed once
116 * only, not for each and every charset anew ... ;-// */
117 int err = 0;
118 char *charset_iter_orig[2];
119 long offs;
121 /* Is this already in target charset? */
122 if (ap->a_conv == AC_TMPFILE) {
123 err = __attach_file(ap, fo);
124 Fclose(ap->a_tmpf);
125 goto jleave;
128 /* We "consume" *ap*, so directly adjust it as we need it */
129 if (ap->a_conv == AC_FIX_INCS)
130 ap->a_charset = ap->a_input_charset;
132 if ((offs = ftell(fo)) < 0) {
133 err = EIO;
134 goto jleave;
137 charset_iter_recurse(charset_iter_orig);
138 for (charset_iter_reset(NULL); charset_iter_next() != NULL;) {
139 err = __attach_file(ap, fo);
140 if (err == 0 || (err != EILSEQ && err != EINVAL))
141 break;
142 clearerr(fo);
143 if (fseek(fo, offs, SEEK_SET) < 0) {
144 err = EIO;
145 break;
147 if (ap->a_conv != AC_DEFAULT) {
148 err = EILSEQ;
149 break;
151 ap->a_charset = NULL;
153 charset_iter_restore(charset_iter_orig);
154 jleave:
155 return err;
158 static int
159 __attach_file(struct attachment *ap, FILE *fo) /* XXX linelength */
161 int err = 0, do_iconv;
162 FILE *fi;
163 char const *charset;
164 enum conversion convert;
165 char *buf;
166 size_t bufsize, lncnt, inlen;
168 /* Either charset-converted temporary file, or plain path */
169 if (ap->a_conv == AC_TMPFILE) {
170 fi = ap->a_tmpf;
171 assert(ftell(fi) == 0x0l);
172 } else if ((fi = Fopen(ap->a_name, "r")) == NULL) {
173 err = errno;
174 perror(ap->a_name);
175 goto jleave;
178 /* MIME part header for attachment */
179 { char const *bn = ap->a_name, *ct;
181 if ((ct = strrchr(bn, '/')) != NULL)
182 bn = ++ct;
183 ct = ap->a_content_type;
184 charset = ap->a_charset;
185 convert = mime_classify_file(fi, (char const**)&ct, &charset,
186 &do_iconv);
187 if (charset == NULL || ap->a_conv == AC_FIX_INCS ||
188 ap->a_conv == AC_TMPFILE)
189 do_iconv = 0;
191 if (fprintf(fo, "\n--%s\nContent-Type: %s", send_boundary, ct)
192 < 0)
193 goto jerr_header;
195 if (charset == NULL) {
196 if (putc('\n', fo) == EOF)
197 goto jerr_header;
198 } else if (fprintf(fo, "; charset=%s\n", charset) < 0)
199 goto jerr_header;
201 if (fprintf(fo, "Content-Transfer-Encoding: %s\n"
202 "Content-Disposition: %s;\n filename=\"",
203 _get_encoding(convert),
204 ap->a_content_disposition) < 0)
205 goto jerr_header;
206 if (xmime_write(bn, strlen(bn), fo, CONV_TOHDR, TD_NONE, NULL)
207 < 0)
208 goto jerr_header;
209 if (fwrite("\"\n", sizeof(char), 2, fo) != 2 * sizeof(char))
210 goto jerr_header;
212 if (ap->a_content_id != NULL && fprintf(fo, "Content-ID: %s\n",
213 ap->a_content_id) < 0)
214 goto jerr_header;
216 if (ap->a_content_description != NULL && fprintf(fo,
217 "Content-Description: %s\n",
218 ap->a_content_description) < 0)
219 goto jerr_header;
221 if (putc('\n', fo) == EOF) {
222 jerr_header: err = errno;
223 goto jerr_fclose;
227 #ifdef HAVE_ICONV
228 if (iconvd != (iconv_t)-1)
229 n_iconv_close(iconvd);
230 if (do_iconv) {
231 char const *tcs = charset_get_lc();
232 if (asccasecmp(charset, tcs) != 0 &&
233 (iconvd = n_iconv_open(charset, tcs))
234 == (iconv_t)-1 && (err = errno) != 0) {
235 if (err == EINVAL)
236 fprintf(stderr, tr(179,
237 "Cannot convert from %s to %s\n"),
238 tcs, charset);
239 else
240 perror("iconv_open");
241 goto jerr_fclose;
244 #endif
246 bufsize = INFIX_BUF;
247 buf = smalloc(bufsize);
248 if (convert == CONV_TOQP
249 #ifdef HAVE_ICONV
250 || iconvd != (iconv_t)-1
251 #endif
253 lncnt = fsize(fi);
254 for (;;) {
255 if (convert == CONV_TOQP
256 #ifdef HAVE_ICONV
257 || iconvd != (iconv_t)-1
258 #endif
260 if (fgetline(&buf, &bufsize, &lncnt, &inlen, fi, 0)
261 == NULL)
262 break;
263 } else if ((inlen = fread(buf, sizeof *buf, bufsize, fi)) == 0)
264 break;
265 if (xmime_write(buf, inlen, fo, convert, TD_ICONV, NULL) < 0) {
266 err = errno;
267 goto jerr;
270 if (ferror(fi))
271 err = EDOM;
272 jerr:
273 free(buf);
274 jerr_fclose:
275 if (ap->a_conv != AC_TMPFILE)
276 Fclose(fi);
277 jleave:
278 return (err);
281 static char const **
282 _prepare_mta_args(struct name *to, struct header *hp)
284 size_t j, i = 4 + smopts_count + 2 + count(to) + 1;
285 char const **args = salloc(i * sizeof(char*));
287 args[0] = value("sendmail-progname");
288 if (args[0] == NULL || *args[0] == '\0')
289 args[0] = SENDMAIL_PROGNAME;
291 args[1] = "-i";
292 i = 2;
293 if (value("metoo"))
294 args[i++] = "-m";
295 if (options & OPT_VERBOSE)
296 args[i++] = "-v";
298 for (j = 0; j < smopts_count; ++j, ++i)
299 args[i] = smopts[j];
301 /* -r option? We may only pass skinned addresses, which is why we do
302 * not simply call myorigin() (TODO myorigin shouldn't fullname!) */
303 if (options & OPT_r_FLAG) {
304 char const *froma;
306 if (option_r_arg[0] != '\0')
307 froma = option_r_arg;
308 else if (hp->h_from != NULL)
309 froma = hp->h_from->n_name;
310 else
311 froma = myorigin(hp);
312 if (froma != NULL) {
313 args[i++] = "-r";
314 args[i++] = froma;
318 /* Receivers follow */
319 for (; to != NULL; to = to->n_flink)
320 if ((to->n_type & GDEL) == 0)
321 args[i++] = to->n_name;
322 args[i] = NULL;
323 return args;
327 * Fix the header by glopping all of the expanded names from
328 * the distribution list into the appropriate fields.
330 static struct name *
331 fixhead(struct header *hp, struct name *tolist) /* TODO !HAVE_ASSERTS legacy*/
333 struct name *np;
335 tolist = elide(tolist);
337 hp->h_to = hp->h_cc = hp->h_bcc = NULL;
338 for (np = tolist; np != NULL; np = np->n_flink)
339 if (np->n_type & GDEL) {
340 #ifdef HAVE_ASSERTS
341 assert(0); /* Shouldn't happen here, but later on :)) */
342 #else
343 continue;
344 #endif
345 } else switch (np->n_type & GMASK) {
346 case (GTO):
347 hp->h_to = cat(hp->h_to, ndup(np, np->n_type|GFULL));
348 break;
349 case (GCC):
350 hp->h_cc = cat(hp->h_cc, ndup(np, np->n_type|GFULL));
351 break;
352 case (GBCC):
353 hp->h_bcc = cat(hp->h_bcc, ndup(np, np->n_type|GFULL));
354 break;
355 default:
356 break;
358 return (tolist);
362 * Put the signature file at fo. TODO send layer rewrite: *integrate in body*!!
364 static int
365 put_signature(FILE *fo, int convert)
367 char *sig, buf[INFIX_BUF], c = '\n';
368 FILE *fsig;
369 size_t sz;
371 sig = value("signature");
372 if (sig == NULL || *sig == '\0')
373 return 0;
374 else if ((sig = file_expand(sig)) == NULL)
375 return (-1);
376 if ((fsig = Fopen(sig, "r")) == NULL) {
377 perror(sig);
378 return -1;
380 while ((sz = fread(buf, sizeof *buf, INFIX_BUF, fsig)) != 0) {
381 c = buf[sz - 1];
382 if (xmime_write(buf, sz, fo, convert, TD_NONE, NULL) < 0) {
383 perror(sig);
384 Fclose(fsig);
385 return -1;
388 if (ferror(fsig)) {
389 perror(sig);
390 Fclose(fsig);
391 return -1;
393 Fclose(fsig);
394 if (c != '\n')
395 putc('\n', fo);
396 return 0;
400 * Attach a message to the file buffer.
402 static int
403 attach_message(struct attachment *ap, FILE *fo)
405 struct message *mp;
407 fprintf(fo, "\n--%s\n"
408 "Content-Type: message/rfc822\n"
409 "Content-Disposition: inline\n", send_boundary);
410 if (ap->a_content_description != NULL)
411 fprintf(fo, "Content-Description: %s\n",
412 ap->a_content_description);
413 fputc('\n', fo);
415 mp = &message[ap->a_msgno - 1];
416 touch(mp);
417 if (sendmp(mp, fo, 0, NULL, SEND_RFC822, NULL) < 0)
418 return -1;
419 return 0;
423 * Generate the body of a MIME multipart message.
425 static int
426 make_multipart(struct header *hp, int convert, FILE *fi, FILE *fo,
427 const char *contenttype, const char *charset)
429 struct attachment *att;
431 fputs("This is a multi-part message in MIME format.\n", fo);
432 if (fsize(fi) != 0) {
433 char *buf;
434 size_t sz, bufsize, cnt;
436 fprintf(fo, "\n--%s\n", send_boundary);
437 fprintf(fo, "Content-Type: %s", contenttype);
438 if (charset)
439 fprintf(fo, "; charset=%s", charset);
440 fprintf(fo, "\nContent-Transfer-Encoding: %s\n"
441 "Content-Disposition: inline\n\n",
442 _get_encoding(convert));
443 buf = smalloc(bufsize = INFIX_BUF);
444 if (convert == CONV_TOQP
445 #ifdef HAVE_ICONV
446 || iconvd != (iconv_t)-1
447 #endif
449 fflush(fi);
450 cnt = fsize(fi);
453 for (;;) {
454 if (convert == CONV_TOQP
455 #ifdef HAVE_ICONV
456 || iconvd != (iconv_t)-1
457 #endif
459 if (fgetline(&buf, &bufsize, &cnt, &sz, fi, 0)
460 == NULL)
461 break;
462 } else {
463 sz = fread(buf, sizeof *buf, bufsize, fi);
464 if (sz == 0)
465 break;
468 if (xmime_write(buf, sz, fo, convert, TD_ICONV, NULL)
469 < 0) {
470 free(buf);
471 return -1;
474 free(buf);
475 if (ferror(fi))
476 return -1;
477 if (charset != NULL)
478 put_signature(fo, convert);
480 for (att = hp->h_attach; att != NULL; att = att->a_flink) {
481 if (att->a_msgno) {
482 if (attach_message(att, fo) != 0)
483 return -1;
484 } else {
485 if (_attach_file(att, fo) != 0)
486 return -1;
489 /* the final boundary with two attached dashes */
490 fprintf(fo, "\n--%s--\n", send_boundary);
491 return 0;
495 * Prepend a header in front of the collected stuff
496 * and return the new file.
498 static FILE *
499 infix(struct header *hp, FILE *fi) /* TODO check */
501 FILE *nfo, *nfi;
502 char *tempMail;
503 #ifdef HAVE_ICONV
504 char const *tcs, *convhdr = NULL;
505 #endif
506 enum conversion convert;
507 char const *contenttype, *charset = NULL;
508 int do_iconv = 0;
510 if ((nfo = Ftemp(&tempMail, "Rs", "w", 0600, 1)) == NULL) {
511 perror(tr(178, "temporary mail file"));
512 return(NULL);
514 if ((nfi = Fopen(tempMail, "r")) == NULL) {
515 perror(tempMail);
516 Fclose(nfo);
518 rm(tempMail);
519 Ftfree(&tempMail);
520 if (nfi == NULL)
521 return NULL;
523 contenttype = "text/plain"; /* XXX mail body - always text/plain */
524 convert = mime_classify_file(fi, &contenttype, &charset, &do_iconv);
526 #ifdef HAVE_ICONV
527 tcs = charset_get_lc();
528 if ((convhdr = need_hdrconv(hp, GTO|GSUBJECT|GCC|GBCC|GIDENT)) != 0) {
529 if (iconvd != (iconv_t)-1)
530 n_iconv_close(iconvd);
531 if (asccasecmp(convhdr, tcs) != 0 &&
532 (iconvd = n_iconv_open(convhdr, tcs))
533 == (iconv_t)-1 && errno != 0) {
534 if (errno == EINVAL)
535 fprintf(stderr, tr(179,
536 "Cannot convert from %s to %s\n"),
537 tcs, convhdr);
538 else
539 perror("iconv_open");
540 Fclose(nfo);
541 return NULL;
544 #endif
545 if (puthead(hp, nfo,
546 GTO|GSUBJECT|GCC|GBCC|GNL|GCOMMA|GUA|GMIME
547 |GMSGID|GIDENT|GREF|GDATE,
548 SEND_MBOX, convert, contenttype, charset)) {
549 Fclose(nfo);
550 Fclose(nfi);
551 #ifdef HAVE_ICONV
552 if (iconvd != (iconv_t)-1)
553 n_iconv_close(iconvd);
554 #endif
555 return NULL;
557 #ifdef HAVE_ICONV
558 if (iconvd != (iconv_t)-1)
559 n_iconv_close(iconvd);
560 if (do_iconv && charset != NULL) { /*TODO charset->mime_classify_file*/
561 int err;
562 if (asccasecmp(charset, tcs) != 0 &&
563 (iconvd = n_iconv_open(charset, tcs))
564 == (iconv_t)-1 && (err = errno) != 0) {
565 if (err == EINVAL)
566 fprintf(stderr, tr(179,
567 "Cannot convert from %s to %s\n"),
568 tcs, charset);
569 else
570 perror("iconv_open");
571 Fclose(nfo);
572 return (NULL);
575 #endif
576 if (hp->h_attach != NULL) {
577 if (make_multipart(hp, convert, fi, nfo, contenttype, charset)
578 != 0) {
579 Fclose(nfo);
580 Fclose(nfi);
581 #ifdef HAVE_ICONV
582 if (iconvd != (iconv_t)-1)
583 n_iconv_close(iconvd);
584 #endif
585 return NULL;
587 } else {
588 size_t sz, bufsize, cnt;
589 char *buf;
591 if (convert == CONV_TOQP
592 #ifdef HAVE_ICONV
593 || iconvd != (iconv_t)-1
594 #endif
596 fflush(fi);
597 cnt = fsize(fi);
599 buf = smalloc(bufsize = INFIX_BUF);
600 for (;;) {
601 if (convert == CONV_TOQP
602 #ifdef HAVE_ICONV
603 || iconvd != (iconv_t)-1
604 #endif
606 if (fgetline(&buf, &bufsize, &cnt, &sz, fi, 0)
607 == NULL)
608 break;
609 } else {
610 sz = fread(buf, sizeof *buf, bufsize, fi);
611 if (sz == 0)
612 break;
614 if (xmime_write(buf, sz, nfo, convert, TD_ICONV, NULL)
615 < 0) {
616 Fclose(nfo);
617 Fclose(nfi);
618 #ifdef HAVE_ICONV
619 if (iconvd != (iconv_t)-1)
620 n_iconv_close(iconvd);
621 #endif
622 free(buf);
623 return NULL;
626 free(buf);
627 if (ferror(fi)) {
628 Fclose(nfo);
629 Fclose(nfi);
630 #ifdef HAVE_ICONV
631 if (iconvd != (iconv_t)-1)
632 n_iconv_close(iconvd);
633 #endif
634 return NULL;
636 if (charset != NULL)
637 put_signature(nfo, convert); /* XXX if (text/) !! */
639 #ifdef HAVE_ICONV
640 if (iconvd != (iconv_t)-1)
641 n_iconv_close(iconvd);
642 #endif
643 fflush(nfo);
644 if (ferror(nfo)) {
645 perror(tr(180, "temporary mail file"));
646 Fclose(nfo);
647 Fclose(nfi);
648 return NULL;
650 Fclose(nfo);
651 Fclose(fi);
652 fflush(nfi);
653 rewind(nfi);
654 return(nfi);
658 * Save the outgoing mail on the passed file.
661 /*ARGSUSED*/
662 static int
663 savemail(char const *name, FILE *fi)
665 FILE *fo;
666 char *buf;
667 size_t bufsize, buflen, cnt;
668 int prependnl = 0, error = 0;
670 buf = smalloc(bufsize = LINESIZE);
671 if ((fo = Zopen(name, "a+", NULL)) == NULL) {
672 if ((fo = Zopen(name, "wx", NULL)) == NULL) {
673 perror(name);
674 free(buf);
675 return (-1);
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 free(buf);
694 return -1;
697 fflush(fo);
698 if (prependnl) {
699 putc('\n', fo);
700 fflush(fo);
705 fprintf(fo, "From %s %s", myname, time_current.tc_ctime);
706 buflen = 0;
707 fflush(fi);
708 rewind(fi);
709 cnt = fsize(fi);
710 while (fgetline(&buf, &bufsize, &cnt, &buflen, fi, 0) != NULL) {
711 #ifdef HAVE_ASSERTS /* TODO assert legacy */
712 assert(! is_head(buf, buflen));
713 #else
714 if (is_head(buf, buflen))
715 putc('>', fo);
716 #endif
717 fwrite(buf, sizeof *buf, buflen, fo);
719 if (buflen && *(buf + buflen - 1) != '\n')
720 putc('\n', fo);
721 putc('\n', fo);
722 fflush(fo);
723 if (ferror(fo)) {
724 perror(name);
725 error = -1;
727 if (Fclose(fo) != 0)
728 error = -1;
729 fflush(fi);
730 rewind(fi);
731 free(buf);
732 return error;
736 * Interface between the argument list and the mail1 routine
737 * which does all the dirty work.
739 int
740 mail(struct name *to, struct name *cc, struct name *bcc,
741 char *subject, struct attachment *attach,
742 char *quotefile, int recipient_record)
744 struct header head;
745 struct str in, out;
747 memset(&head, 0, sizeof head);
748 /* The given subject may be in RFC1522 format. */
749 if (subject != NULL) {
750 in.s = subject;
751 in.l = strlen(subject);
752 mime_fromhdr(&in, &out, /* TODO ??? TD_ISPR |*/ TD_ICONV);
753 head.h_subject = out.s;
755 if (! (options & OPT_t_FLAG)) {
756 head.h_to = to;
757 head.h_cc = cc;
758 head.h_bcc = bcc;
760 head.h_attach = attach;
761 mail1(&head, 0, NULL, quotefile, recipient_record, 0);
762 if (subject != NULL)
763 free(out.s);
764 return 0;
768 * Send mail to a bunch of user names. The interface is through
769 * the mail routine below.
771 static int
772 sendmail_internal(void *v, int recipient_record)
774 char *str = v;
775 struct header head;
777 memset(&head, 0, sizeof head);
778 head.h_to = lextract(str, GTO|GFULL);
779 mail1(&head, 0, NULL, NULL, recipient_record, 0);
780 return 0;
783 int
784 csendmail(void *v)
786 return sendmail_internal(v, 0);
789 int
790 cSendmail(void *v)
792 return sendmail_internal(v, 1);
795 static enum okay
796 transfer(struct name *to, FILE *input, struct header *hp)
798 char o[LINESIZE], *cp;
799 struct name *np;
800 int cnt = 0;
801 enum okay ok = OKAY;
803 np = to;
804 while (np) {
805 snprintf(o, sizeof o, "smime-encrypt-%s", np->n_name);
806 if ((cp = value(o)) != NULL) {
807 #ifdef HAVE_SSL
808 struct name *nt;
809 FILE *ef;
810 if ((ef = smime_encrypt(input, cp, np->n_name)) != 0) {
811 nt = ndup(np, np->n_type & ~(GFULL|GSKIN));
812 if (start_mta(nt, ef, hp) != OKAY)
813 ok = STOP;
814 Fclose(ef);
815 } else {
816 #else
817 fprintf(stderr, tr(225,
818 "No SSL support compiled in.\n"));
819 ok = STOP;
820 #endif
821 fprintf(stderr, tr(38,
822 "Message not sent to <%s>\n"),
823 np->n_name);
824 _senderror = TRU1;
825 #ifdef HAVE_SSL
827 #endif
828 rewind(input);
829 if (np->n_flink)
830 np->n_flink->n_blink = np->n_blink;
831 if (np->n_blink)
832 np->n_blink->n_flink = np->n_flink;
833 if (np == to)
834 to = np->n_flink;
835 np = np->n_flink;
836 } else {
837 cnt++;
838 np = np->n_flink;
841 if (cnt) {
842 if (value("smime-force-encryption") ||
843 start_mta(to, input, hp) != OKAY)
844 ok = STOP;
846 return ok;
850 * Start the Mail Transfer Agent
851 * mailing to namelist and stdin redirected to input.
853 static enum okay
854 start_mta(struct name *to, FILE *input, struct header *hp)
856 #ifdef HAVE_SMTP
857 char *user = NULL, *password = NULL, *skinned = NULL;
858 #endif
859 char const **args = NULL, **t, *mta;
860 char *smtp;
861 enum okay ok = STOP;
862 pid_t pid;
863 sigset_t nset;
864 (void)hp;
866 if ((smtp = value("smtp")) == NULL) {
867 if ((mta = value("sendmail")) != NULL) {
868 if ((mta = file_expand(mta)) == NULL)
869 goto jstop;
870 } else
871 mta = SENDMAIL;
873 args = _prepare_mta_args(to, hp);
874 if (options & OPT_DEBUG) {
875 printf(tr(181, "Sendmail arguments:"));
876 for (t = args; *t != NULL; t++)
877 printf(" \"%s\"", *t);
878 printf("\n");
879 ok = OKAY;
880 goto jleave;
882 } else {
883 mta = NULL; /* Silence cc */
884 #ifndef HAVE_SMTP
885 fputs(tr(194, "No SMTP support compiled in.\n"), stderr);
886 goto jstop;
887 #else
888 skinned = skin(myorigin(hp));
889 if ((user = smtp_auth_var("-user", skinned)) != NULL &&
890 (password = smtp_auth_var("-password",
891 skinned)) == NULL)
892 password = getpassword(NULL);
893 #endif
897 * Fork, set up the temporary mail file as standard
898 * input for "mail", and exec with the user list we generated
899 * far above.
901 if ((pid = fork()) == -1) {
902 perror("fork");
903 jstop: savedeadletter(input, 0);
904 _senderror = TRU1;
905 goto jleave;
907 if (pid == 0) {
908 sigemptyset(&nset);
909 sigaddset(&nset, SIGHUP);
910 sigaddset(&nset, SIGINT);
911 sigaddset(&nset, SIGQUIT);
912 sigaddset(&nset, SIGTSTP);
913 sigaddset(&nset, SIGTTIN);
914 sigaddset(&nset, SIGTTOU);
915 freopen("/dev/null", "r", stdin);
916 #ifdef HAVE_SMTP
917 if (smtp != NULL) {
918 prepare_child(&nset, 0, 1);
919 if (smtp_mta(smtp, to, input, hp,
920 user, password, skinned) == 0)
921 _exit(0);
922 } else {
923 #endif
924 prepare_child(&nset, fileno(input), -1);
925 /* If *record* is set then savemail() will move the
926 * file position; it'll call rewind(), but that may
927 * optimize away the systemcall if possible, and since
928 * dup2() shares the position with the original FD the
929 * MTA may end up reading nothing */
930 lseek(0, 0, SEEK_SET);
931 execv(mta, UNCONST(args));
932 perror(mta);
933 #ifdef HAVE_SMTP
935 #endif
936 savedeadletter(input, 1);
937 fputs(tr(182, ". . . message not sent.\n"), stderr);
938 _exit(1);
940 if ((options & (OPT_DEBUG|OPT_VERBOSE|OPT_BATCH_FLAG)) ||
941 value("sendwait")) {
942 if (wait_child(pid) == 0)
943 ok = OKAY;
944 else
945 _senderror = TRU1;
946 } else {
947 ok = OKAY;
948 free_child(pid);
950 jleave:
951 return ok;
955 * Record outgoing mail if instructed to do so.
957 static enum okay
958 mightrecord(FILE *fp, struct name *to, int recipient_record)
960 char *cp, *cq;
961 char const *ep;
963 if (recipient_record) {
964 size_t i = strlen(cq = skinned_name(to)) + 1;
965 cp = salloc(i);
966 memcpy(cp, cq, i);
967 for (cq = cp; *cq && *cq != '@'; cq++)
969 *cq = '\0';
970 } else
971 cp = value("record");
972 if (cp != NULL) {
973 ep = expand(cp);
974 if (ep == NULL) {
975 ep = "NULL";
976 goto jbail;
978 if (value("outfolder") && *ep != '/' && *ep != '+' &&
979 which_protocol(ep) == PROTO_FILE) {
980 size_t i = strlen(cp);
981 cq = salloc(i + 2);
982 cq[0] = '+';
983 memcpy(cq + 1, cp, i + 1);
984 cp = cq;
985 ep = expand(cp); /* TODO file_expand() possible? */
986 if (ep == NULL) {
987 ep = "NULL";
988 goto jbail;
991 if (savemail(ep, fp) != 0) {
992 jbail: fprintf(stderr, tr(285,
993 "Failed to save message in %s - "
994 "message not sent\n"), ep);
995 exit_status |= 1;
996 savedeadletter(fp, 1);
997 return STOP;
1000 return OKAY;
1004 * Mail a message on standard input to the people indicated
1005 * in the passed header. (Internal interface).
1007 enum okay
1008 mail1(struct header *hp, int printheaders, struct message *quote,
1009 char *quotefile, int recipient_record, int doprefix)
1011 enum okay ok = STOP;
1012 struct name *to;
1013 FILE *mtf, *nmtf;
1014 int dosign = -1, err;
1015 char const *cp;
1017 _senderror = FAL0;
1019 /* Update some globals we likely need first */
1020 time_current_update(&time_current, TRU1);
1022 /* */
1023 if ((cp = value("autocc")) != NULL && *cp)
1024 hp->h_cc = cat(hp->h_cc, checkaddrs(lextract(cp, GCC|GFULL)));
1025 if ((cp = value("autobcc")) != NULL && *cp)
1026 hp->h_bcc = cat(hp->h_bcc, checkaddrs(lextract(cp,GBCC|GFULL)));
1029 * Collect user's mail from standard input.
1030 * Get the result as mtf.
1032 mtf = collect(hp, printheaders, quote, quotefile, doprefix);
1033 if (mtf == NULL)
1034 goto j_leave;
1036 if (options & OPT_INTERACTIVE) {
1037 err = (value("bsdcompat") || value("askatend"));
1038 if (err == 0)
1039 goto jaskeot;
1040 if (value("askcc"))
1041 ++err, grab_headers(hp, GCC, 1);
1042 if (value("askbcc"))
1043 ++err, grab_headers(hp, GBCC, 1);
1044 if (value("askattach"))
1045 ++err, hp->h_attach = edit_attachments(hp->h_attach);
1046 if (value("asksign"))
1047 ++err, dosign = yorn(tr(35,
1048 "Sign this message (y/n)? "));
1049 if (err == 1) {
1050 jaskeot:
1051 printf(tr(183, "EOT\n"));
1052 fflush(stdout);
1056 if (fsize(mtf) == 0) {
1057 if (options & OPT_E_FLAG)
1058 goto jleave;
1059 if (hp->h_subject == NULL)
1060 printf(tr(184,
1061 "No message, no subject; hope that's ok\n"));
1062 else if (value("bsdcompat") || value("bsdmsgs"))
1063 printf(tr(185, "Null message body; hope that's ok\n"));
1066 if (dosign < 0)
1067 dosign = (value("smime-sign") != NULL);
1068 #ifndef HAVE_SSL
1069 if (dosign) {
1070 fprintf(stderr, tr(225, "No SSL support compiled in.\n"));
1071 goto jleave;
1073 #endif
1075 /* XXX Update time_current again; once collect() offers editing of more
1076 * XXX headers, including Date:, this must only happen if Date: is the
1077 * XXX same that it was before collect() (e.g., postponing etc.).
1078 * XXX But *do* update otherwise because the mail seems to be backdated
1079 * XXX if the user edited some time, which looks odd and it happened
1080 * XXX to me that i got mis-dated response mails due to that... */
1081 time_current_update(&time_current, TRU1);
1083 /* TODO hrmpf; the MIME/send layer rewrite MUST address the init crap:
1084 * TODO setup the header ONCE; note this affects edit.c, collect.c ...,
1085 * TODO but: offer a hook that rebuilds/expands/checks/fixates all
1086 * TODO header fields ONCE, call that ONCE after user editing etc. has
1087 * TODO completed (one edit cycle) */
1090 * Take the user names from the combined to and cc lists and do all the
1091 * alias processing. The POSIX standard says:
1092 * The names shall be substituted when alias is used as a recipient
1093 * address specified by the user in an outgoing message (that is,
1094 * other recipients addressed indirectly through the reply command
1095 * shall not be substituted in this manner).
1096 * S-nail thus violates POSIX, as has been pointed out correctly by
1097 * Martin Neitzel, but logic, usability und intellectual penetration of
1098 * POSIX standards is disputable anyway. Go for user friendliness.
1101 /* Do alias expansion on Reply-To: members, too (Martin Neitzel) */
1102 /* TODO puthead() YET (!!! see ONCE note above) expands the value, but
1103 * TODO doesn't perform alias expansion; encapsulate in the ONCE-o */
1104 if (hp->h_replyto == NULL && (cp = value("replyto")) != NULL)
1105 hp->h_replyto = checkaddrs(lextract(cp, GEXTRA|GFULL));
1106 if (hp->h_replyto != NULL)
1107 hp->h_replyto = elide(usermap(hp->h_replyto, TRU1));
1110 * TODO what happens now is that all recipients are merged into
1111 * TODO a duplicated list with expanded aliases, then this list is
1112 * TODO splitted again into the three individual recipient lists (with
1113 * TODO duplicates removed).
1114 * TODO later on we use the merged list for outof() pipe/file saving,
1115 * TODO then we eliminate duplicates (again) and then we use that one
1116 * TODO for mightrecord() and transfer(), and again. ... Please ...
1119 * NOTE: Due to elide() in fixhead(), ENSURE to,cc,bcc order of to!,
1120 * because otherwise the recipients will be "degraded" if they occur
1121 * multiple times
1123 to = usermap(cat(hp->h_to, cat(hp->h_cc, hp->h_bcc)), FAL0);
1124 if (to == NULL) {
1125 fprintf(stderr, tr(186, "No recipients specified\n"));
1126 _senderror = TRU1;
1128 to = fixhead(hp, to);
1131 * 'Bit ugly kind of control flow until we find a charset that does it.
1132 * XXX Can maybe be done nicer once we have a carrier struct instead
1133 * XXX of globals
1135 for (charset_iter_reset(hp->h_charset);;) {
1136 if (charset_iter_next() == NULL)
1138 else if ((nmtf = infix(hp, mtf)) != NULL)
1139 break;
1140 else if ((err = errno) == EILSEQ || err == EINVAL) {
1141 rewind(mtf);
1142 continue;
1145 perror("");
1146 #ifdef HAVE_SSL
1147 jfail_dead:
1148 #endif
1149 _senderror = TRU1;
1150 savedeadletter(mtf, 1);
1151 fputs(tr(187, ". . . message not sent.\n"), stderr);
1152 goto jleave;
1155 mtf = nmtf;
1156 #ifdef HAVE_SSL
1157 if (dosign) {
1158 if ((nmtf = smime_sign(mtf, hp)) == NULL)
1159 goto jfail_dead;
1160 Fclose(mtf);
1161 mtf = nmtf;
1163 #endif
1166 * Look through the recipient list for names with /'s
1167 * in them which we write to as files directly.
1169 to = outof(to, mtf, hp, &_senderror);
1170 if (_senderror)
1171 savedeadletter(mtf, 0);
1172 to = elide(to); /* XXX needed only to drop GDELs due to outof()! */
1173 if (count(to) == 0) {
1174 if (! _senderror)
1175 ok = OKAY;
1176 goto jleave;
1179 if (mightrecord(mtf, to, recipient_record) != OKAY)
1180 goto jleave;
1181 ok = transfer(to, mtf, hp);
1183 jleave:
1184 Fclose(mtf);
1185 j_leave:
1186 if (_senderror)
1187 exit_status |= EXIT_SEND_ERROR;
1188 return ok;
1192 * Create a Message-Id: header field.
1193 * Use either the host name or the from address.
1195 static void
1196 message_id(FILE *fo, struct header *hp)
1198 char const *h;
1199 size_t rl;
1200 struct tm *tmp;
1202 if (boption("message-id-disable"))
1203 goto jleave;
1204 if ((h = voption("hostname")) != NULL)
1205 rl = 24;
1206 else if ((h = skin(myorigin(hp))) != NULL && strchr(h, '@') != NULL)
1207 rl = 16;
1208 else
1209 /* Up to MTA */
1210 goto jleave;
1212 tmp = &time_current.tc_gm;
1213 fprintf(fo, "Message-ID: <%04d%02d%02d%02d%02d%02d.%s%c%s>\n",
1214 tmp->tm_year + 1900, tmp->tm_mon + 1, tmp->tm_mday,
1215 tmp->tm_hour, tmp->tm_min, tmp->tm_sec,
1216 getrandstring(rl), (rl == 16 ? '%' : '@'), h);
1217 jleave:
1222 * Create a Date: header field.
1223 * We compare the localtime() and gmtime() results to get the timezone,
1224 * because numeric timezones are easier to read and because $TZ is
1225 * not set on most GNU systems.
1228 mkdate(FILE *fo, const char *field)
1230 struct tm *tmptr;
1231 int tzdiff, tzdiff_hour, tzdiff_min;
1233 tzdiff = time_current.tc_time - mktime(&time_current.tc_gm);
1234 tzdiff_hour = (int)(tzdiff / 60);
1235 tzdiff_min = tzdiff_hour % 60;
1236 tzdiff_hour /= 60;
1237 tmptr = &time_current.tc_local;
1238 if (tmptr->tm_isdst > 0)
1239 tzdiff_hour++;
1240 return fprintf(fo, "%s: %s, %02d %s %04d %02d:%02d:%02d %+05d\n",
1241 field,
1242 weekday_names[tmptr->tm_wday],
1243 tmptr->tm_mday, month_names[tmptr->tm_mon],
1244 tmptr->tm_year + 1900, tmptr->tm_hour,
1245 tmptr->tm_min, tmptr->tm_sec,
1246 tzdiff_hour * 100 + tzdiff_min);
1249 #define FMT_CC_AND_BCC { \
1250 if (hp->h_cc != NULL && w & GCC) { \
1251 if (fmt("Cc:", hp->h_cc, fo, \
1252 w&(GCOMMA|GFILES), 0, \
1253 action!=SEND_TODISP)) \
1254 return 1; \
1255 gotcha++; \
1257 if (hp->h_bcc != NULL && w & GBCC) { \
1258 if (fmt("Bcc:", hp->h_bcc, fo, \
1259 w&(GCOMMA|GFILES), 0, \
1260 action!=SEND_TODISP)) \
1261 return 1; \
1262 gotcha++; \
1266 * Dump the to, subject, cc header on the
1267 * passed file buffer.
1270 puthead(struct header *hp, FILE *fo, enum gfield w,
1271 enum sendaction action, enum conversion convert,
1272 char const *contenttype, char const *charset)
1274 int gotcha;
1275 char const *addr;
1276 int stealthmua;
1277 size_t l;
1278 struct name *np, *fromfield = NULL, *senderfield = NULL;
1280 if ((addr = value("stealthmua")) != NULL) {
1281 stealthmua = (strcmp(addr, "noagent") == 0) ? -1 : 1;
1282 } else
1283 stealthmua = 0;
1284 gotcha = 0;
1285 if (w & GDATE) {
1286 mkdate(fo, "Date"), gotcha++;
1288 if (w & GIDENT) {
1289 if (hp->h_from != NULL) {
1290 if (fmt("From:", hp->h_from, fo, w&(GCOMMA|GFILES), 0,
1291 action!=SEND_TODISP))
1292 return 1;
1293 gotcha++;
1294 fromfield = hp->h_from;
1295 } else if ((addr = myaddrs(hp)) != NULL) {
1296 if (_putname(addr, w, action, &gotcha, "From:", fo,
1297 &fromfield))
1298 return 1;
1299 hp->h_from = fromfield;
1301 if (((addr = hp->h_organization) != NULL ||
1302 (addr = value("ORGANIZATION")) != NULL) &&
1303 (l = strlen(addr)) > 0) {
1304 fwrite("Organization: ", sizeof (char), 14, fo);
1305 if (xmime_write(addr, l, fo,
1306 action == SEND_TODISP ?
1307 CONV_NONE:CONV_TOHDR,
1308 action == SEND_TODISP ?
1309 TD_ISPR|TD_ICONV:TD_ICONV,
1310 NULL) < 0)
1311 return 1;
1312 gotcha++;
1313 putc('\n', fo);
1315 /* TODO see the ONCE TODO note somewhere around this file;
1316 * TODO but anyway, do NOT perform alias expansion UNLESS
1317 * TODO we are actually sending out! */
1318 if (hp->h_replyto != NULL) {
1319 if (fmt("Reply-To:", hp->h_replyto, fo, w & GCOMMA, 0,
1320 action!=SEND_TODISP))
1321 return 1;
1322 gotcha++;
1323 } else if ((addr = value("replyto")) != NULL)
1324 if (_putname(addr, w, action, &gotcha, "Reply-To:", fo,
1325 NULL))
1326 return 1;
1327 if (hp->h_sender != NULL) {
1328 if (fmt("Sender:", hp->h_sender, fo, w & GCOMMA, 0,
1329 action!=SEND_TODISP))
1330 return 1;
1331 gotcha++;
1332 senderfield = hp->h_sender;
1333 } else if ((addr = value("sender")) != NULL)
1334 if (_putname(addr, w, action, &gotcha, "Sender:", fo,
1335 &senderfield))
1336 return 1;
1337 if (check_from_and_sender(fromfield, senderfield))
1338 return 1;
1340 if (hp->h_to != NULL && w & GTO) {
1341 if (fmt("To:", hp->h_to, fo, w&(GCOMMA|GFILES), 0,
1342 action!=SEND_TODISP))
1343 return 1;
1344 gotcha++;
1346 if (value("bsdcompat") == NULL && value("bsdorder") == NULL)
1347 FMT_CC_AND_BCC
1348 if (hp->h_subject != NULL && w & GSUBJECT) {
1349 fwrite("Subject: ", sizeof (char), 9, fo);
1350 if (ascncasecmp(hp->h_subject, "re: ", 4) == 0) {
1351 fwrite("Re: ", sizeof (char), 4, fo);
1352 if (strlen(hp->h_subject + 4) > 0 &&
1353 xmime_write(hp->h_subject + 4,
1354 strlen(hp->h_subject + 4), fo,
1355 action == SEND_TODISP ?
1356 CONV_NONE:CONV_TOHDR,
1357 action == SEND_TODISP ?
1358 TD_ISPR|TD_ICONV:TD_ICONV,
1359 NULL) < 0)
1360 return 1;
1361 } else if (*hp->h_subject) {
1362 if (xmime_write(hp->h_subject, strlen(hp->h_subject),
1363 fo, action == SEND_TODISP ?
1364 CONV_NONE:CONV_TOHDR,
1365 action == SEND_TODISP ?
1366 TD_ISPR|TD_ICONV:TD_ICONV,
1367 NULL) < 0)
1368 return 1;
1370 gotcha++;
1371 fwrite("\n", sizeof (char), 1, fo);
1373 if (value("bsdcompat") || value("bsdorder"))
1374 FMT_CC_AND_BCC
1375 if (w & GMSGID && stealthmua <= 0)
1376 message_id(fo, hp), gotcha++;
1377 if ((np = hp->h_ref) != NULL && w & GREF) {
1378 fmt("References:", np, fo, 0, 1, 0);
1379 if (np->n_name) {
1380 while (np->n_flink)
1381 np = np->n_flink;
1382 if (is_addr_invalid(np, 0) == 0) {
1383 fprintf(fo, "In-Reply-To: %s\n", np->n_name);
1384 gotcha++;
1388 if (w & GUA && stealthmua == 0)
1389 fprintf(fo, "User-Agent: %s %s\n", uagent, version), gotcha++;
1390 if (w & GMIME) {
1391 fputs("MIME-Version: 1.0\n", fo), gotcha++;
1392 if (hp->h_attach != NULL) {
1393 send_boundary = mime_create_boundary();/*TODO carrier*/
1394 fprintf(fo, "Content-Type: multipart/mixed;\n"
1395 " boundary=\"%s\"\n", send_boundary);
1396 } else {
1397 fprintf(fo, "Content-Type: %s", contenttype);
1398 if (charset)
1399 fprintf(fo, "; charset=%s", charset);
1400 fprintf(fo, "\nContent-Transfer-Encoding: %s\n",
1401 _get_encoding(convert));
1404 if (gotcha && w & GNL)
1405 putc('\n', fo);
1406 return(0);
1410 * Format the given header line to not exceed 72 characters.
1412 static int
1413 fmt(char const *str, struct name *np, FILE *fo, int flags, int dropinvalid,
1414 int domime)
1416 enum {
1417 m_INIT = 1<<0,
1418 m_COMMA = 1<<1,
1419 m_NOPF = 1<<2,
1420 m_CSEEN = 1<<3
1421 } m = (flags & GCOMMA) ? m_COMMA : 0;
1422 ssize_t col, len;
1424 col = strlen(str);
1425 if (col) {
1426 fwrite(str, sizeof *str, col, fo);
1427 if (flags & GFILES)
1428 goto jstep;
1429 if (col == 9 && asccasecmp(str, "reply-to:") == 0) {
1430 m |= m_NOPF;
1431 goto jstep;
1433 if (value("add-file-recipients"))
1434 goto jstep;
1435 if ((col == 3 && ((asccasecmp(str, "to:") == 0) ||
1436 asccasecmp(str, "cc:") == 0)) ||
1437 (col == 4 && asccasecmp(str, "bcc:") == 0) ||
1438 (col == 10 &&
1439 asccasecmp(str, "Resent-To:") == 0))
1440 m |= m_NOPF;
1443 jstep:
1444 for (; np != NULL; np = np->n_flink) {
1445 if ((m & m_NOPF) && is_fileorpipe_addr(np))
1446 continue;
1447 if (is_addr_invalid(np, ! dropinvalid)) {
1448 if (dropinvalid)
1449 continue;
1450 else
1451 return (1);
1453 if ((m & (m_INIT | m_COMMA)) == (m_INIT | m_COMMA)) {
1454 putc(',', fo);
1455 m |= m_CSEEN;
1456 ++col;
1458 len = strlen(np->n_fullname);
1459 ++col; /* The separating space */
1460 if ((m & m_INIT) && col > 1 && col + len > 72) {
1461 fputs("\n ", fo);
1462 col = 1;
1463 m &= ~m_CSEEN;
1464 } else
1465 putc(' ', fo);
1466 m = (m & ~m_CSEEN) | m_INIT;
1467 len = xmime_write(np->n_fullname, len, fo,
1468 domime?CONV_TOHDR_A:CONV_NONE,
1469 TD_ICONV, NULL);
1470 if (len < 0)
1471 return (1);
1472 col += len;
1474 putc('\n', fo);
1475 return (0);
1479 * Rewrite a message for resending, adding the Resent-Headers.
1481 static int
1482 infix_resend(FILE *fi, FILE *fo, struct message *mp, struct name *to,
1483 int add_resent)
1485 size_t cnt, c, bufsize = 0;
1486 char *buf = NULL;
1487 char const *cp;
1488 struct name *fromfield = NULL, *senderfield = NULL;
1490 cnt = mp->m_size;
1492 * Write the Resent-Fields.
1494 if (add_resent) {
1495 fputs("Resent-", fo);
1496 mkdate(fo, "Date");
1497 if ((cp = myaddrs(NULL)) != NULL) {
1498 if (_putname(cp, GCOMMA, SEND_MBOX, NULL,
1499 "Resent-From:", fo, &fromfield))
1500 return 1;
1502 if ((cp = value("sender")) != NULL) {
1503 if (_putname(cp, GCOMMA, SEND_MBOX, NULL,
1504 "Resent-Sender:", fo, &senderfield))
1505 return 1;
1507 if (fmt("Resent-To:", to, fo, 1, 1, 0))
1508 return 1;
1509 if ((cp = value("stealthmua")) == NULL ||
1510 strcmp(cp, "noagent") == 0) {
1511 fputs("Resent-", fo);
1512 message_id(fo, NULL);
1515 if (check_from_and_sender(fromfield, senderfield))
1516 return 1;
1518 * Write the original headers.
1520 while (cnt > 0) {
1521 if (fgetline(&buf, &bufsize, &cnt, &c, fi, 0) == NULL)
1522 break;
1523 /* XXX more checks: The From_ line may be seen when resending */
1524 if (ascncasecmp("status: ", buf, 8) != 0 &&
1525 strncmp("From ", buf, 5) != 0
1526 /* In the headers, is_head() is actually
1527 * overkill, so a simple ^From_ is sufficient.
1528 * ! is_head(buf, c) */
1530 fwrite(buf, sizeof *buf, c, fo);
1531 if (cnt > 0 && *buf == '\n')
1532 break;
1535 * Write the message body.
1537 while (cnt > 0) {
1538 if (fgetline(&buf, &bufsize, &cnt, &c, fi, 0) == NULL)
1539 break;
1540 if (cnt == 0 && *buf == '\n')
1541 break;
1542 fwrite(buf, sizeof *buf, c, fo);
1544 if (buf)
1545 free(buf);
1546 if (ferror(fo)) {
1547 perror(tr(188, "temporary mail file"));
1548 return 1;
1550 return 0;
1553 enum okay
1554 resend_msg(struct message *mp, struct name *to, int add_resent) /* TODO check */
1556 enum okay ok = STOP;
1557 FILE *ibuf, *nfo, *nfi;
1558 char *tempMail;
1559 struct header head;
1561 _senderror = FAL0;
1563 /* Update some globals we likely need first */
1564 time_current_update(&time_current, TRU1);
1566 memset(&head, 0, sizeof head);
1568 if ((to = checkaddrs(to)) == NULL) {
1569 _senderror = TRU1;
1570 goto jleave;
1573 if ((nfo = Ftemp(&tempMail, "Rs", "w", 0600, 1)) == NULL) {
1574 _senderror = TRU1;
1575 perror(tr(189, "temporary mail file"));
1576 goto jleave;
1578 if ((nfi = Fopen(tempMail, "r")) == NULL) {
1579 _senderror = TRU1;
1580 perror(tempMail);
1582 rm(tempMail);
1583 Ftfree(&tempMail);
1584 if (nfi == NULL)
1585 goto jerr_o;
1587 if ((ibuf = setinput(&mb, mp, NEED_BODY)) == NULL)
1588 goto jerr_all;
1589 head.h_to = to;
1590 to = fixhead(&head, to);
1591 if (infix_resend(ibuf, nfo, mp, head.h_to, add_resent) != 0) {
1592 savedeadletter(nfi, 1);
1593 fputs(tr(190, ". . . message not sent.\n"), stderr);
1594 jerr_all:
1595 Fclose(nfi);
1596 jerr_o:
1597 Fclose(nfo);
1598 _senderror = TRU1;
1599 goto jleave;
1601 Fclose(nfo);
1602 rewind(nfi);
1603 to = outof(to, nfi, &head, &_senderror);
1604 if (_senderror)
1605 savedeadletter(nfi, 0);
1606 to = elide(to); /* TODO should have been done in fixhead()? */
1607 if (count(to) != 0) {
1608 if (value("record-resent") == NULL ||
1609 mightrecord(nfi, to, 0) == OKAY)
1610 ok = transfer(to, nfi, NULL);
1611 } else if (! _senderror)
1612 ok = OKAY;
1613 Fclose(nfi);
1614 jleave:
1615 if (_senderror)
1616 exit_status |= EXIT_SEND_ERROR;
1617 return ok;