NYD: head.c
[s-mailx.git] / sendout.c
blobe3f4b8291eb77e43a8713686b82edb9ccb85662d
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 *send_boundary;
50 static bool_t _senderror;
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 static struct name *fixhead(struct header *hp, struct name *tolist);
66 static int put_signature(FILE *fo, int convert);
67 static int attach_message(struct attachment *ap, FILE *fo);
68 static int make_multipart(struct header *hp, int convert, FILE *fi, FILE *fo,
69 const char *contenttype, const char *charset);
70 static FILE *infix(struct header *hp, FILE *fi);
71 static int savemail(char const *name, FILE *fi);
72 static int sendmail_internal(void *v, int recipient_record);
73 static enum okay transfer(struct name *to, FILE *input, struct header *hp);
74 static enum okay start_mta(struct name *to, FILE *input, struct header *hp);
75 static void message_id(FILE *fo, struct header *hp);
76 static int fmt(char const *str, struct name *np, FILE *fo, int comma,
77 int dropinvalid, int domime);
78 static int infix_resend(FILE *fi, FILE *fo, struct message *mp,
79 struct name *to, int add_resent);
81 static enum okay
82 _putname(char const *line, enum gfield w, enum sendaction action,
83 size_t *gotcha, char const *prefix, FILE *fo, struct name **xp)
85 enum okay ret = STOP;
86 struct name *np;
88 np = lextract(line, GEXTRA|GFULL);
89 if (xp)
90 *xp = np;
91 if (np == NULL)
93 else if (fmt(prefix, np, fo, w & GCOMMA, 0, action != SEND_TODISP))
94 ret = OKAY;
95 else if (gotcha)
96 ++(*gotcha);
97 return (ret);
100 static char const *
101 _get_encoding(enum conversion const convert)
103 char const *ret;
104 switch (convert) {
105 case CONV_7BIT: ret = "7bit"; break;
106 case CONV_8BIT: ret = "8bit"; break;
107 case CONV_TOQP: ret = "quoted-printable"; break;
108 case CONV_TOB64: ret = "base64"; break;
109 default: ret = NULL; break;
111 return (ret);
114 static int
115 _attach_file(struct attachment *ap, FILE *fo)
117 /* TODO of course, the MIME classification needs to performed once
118 * only, not for each and every charset anew ... ;-// */
119 int err = 0;
120 char *charset_iter_orig[2];
121 long offs;
123 /* Is this already in target charset? */
124 if (ap->a_conv == AC_TMPFILE) {
125 err = __attach_file(ap, fo);
126 Fclose(ap->a_tmpf);
127 goto jleave;
130 /* We "consume" *ap*, so directly adjust it as we need it */
131 if (ap->a_conv == AC_FIX_INCS)
132 ap->a_charset = ap->a_input_charset;
134 if ((offs = ftell(fo)) < 0) {
135 err = EIO;
136 goto jleave;
139 charset_iter_recurse(charset_iter_orig);
140 for (charset_iter_reset(NULL); charset_iter_next() != NULL;) {
141 err = __attach_file(ap, fo);
142 if (err == 0 || (err != EILSEQ && err != EINVAL))
143 break;
144 clearerr(fo);
145 if (fseek(fo, offs, SEEK_SET) < 0) {
146 err = EIO;
147 break;
149 if (ap->a_conv != AC_DEFAULT) {
150 err = EILSEQ;
151 break;
153 ap->a_charset = NULL;
155 charset_iter_restore(charset_iter_orig);
156 jleave:
157 return err;
160 static int
161 __attach_file(struct attachment *ap, FILE *fo) /* XXX linelength */
163 int err = 0, do_iconv;
164 FILE *fi;
165 char const *charset;
166 enum conversion convert;
167 char *buf;
168 size_t bufsize, lncnt, inlen;
170 /* Either charset-converted temporary file, or plain path */
171 if (ap->a_conv == AC_TMPFILE) {
172 fi = ap->a_tmpf;
173 assert(ftell(fi) == 0x0l);
174 } else if ((fi = Fopen(ap->a_name, "r")) == NULL) {
175 err = errno;
176 perror(ap->a_name);
177 goto jleave;
180 /* MIME part header for attachment */
181 { char const *bn = ap->a_name, *ct;
183 if ((ct = strrchr(bn, '/')) != NULL)
184 bn = ++ct;
185 ct = ap->a_content_type;
186 charset = ap->a_charset;
187 convert = mime_classify_file(fi, (char const**)&ct, &charset,
188 &do_iconv);
189 if (charset == NULL || ap->a_conv == AC_FIX_INCS ||
190 ap->a_conv == AC_TMPFILE)
191 do_iconv = 0;
193 if (fprintf(fo, "\n--%s\nContent-Type: %s", send_boundary, ct)
194 < 0)
195 goto jerr_header;
197 if (charset == NULL) {
198 if (putc('\n', fo) == EOF)
199 goto jerr_header;
200 } else if (fprintf(fo, "; charset=%s\n", charset) < 0)
201 goto jerr_header;
203 if (fprintf(fo, "Content-Transfer-Encoding: %s\n"
204 "Content-Disposition: %s;\n filename=\"",
205 _get_encoding(convert),
206 ap->a_content_disposition) < 0)
207 goto jerr_header;
208 if (xmime_write(bn, strlen(bn), fo, CONV_TOHDR, TD_NONE, NULL)
209 < 0)
210 goto jerr_header;
211 if (fwrite("\"\n", sizeof(char), 2, fo) != 2 * sizeof(char))
212 goto jerr_header;
214 if (ap->a_content_id != NULL && fprintf(fo, "Content-ID: %s\n",
215 ap->a_content_id) < 0)
216 goto jerr_header;
218 if (ap->a_content_description != NULL && fprintf(fo,
219 "Content-Description: %s\n",
220 ap->a_content_description) < 0)
221 goto jerr_header;
223 if (putc('\n', fo) == EOF) {
224 jerr_header: err = errno;
225 goto jerr_fclose;
229 #ifdef HAVE_ICONV
230 if (iconvd != (iconv_t)-1)
231 n_iconv_close(iconvd);
232 if (do_iconv) {
233 char const *tcs = charset_get_lc();
234 if (asccasecmp(charset, tcs) != 0 &&
235 (iconvd = n_iconv_open(charset, tcs))
236 == (iconv_t)-1 && (err = errno) != 0) {
237 if (err == EINVAL)
238 fprintf(stderr, tr(179,
239 "Cannot convert from %s to %s\n"),
240 tcs, charset);
241 else
242 perror("iconv_open");
243 goto jerr_fclose;
246 #endif
248 bufsize = INFIX_BUF;
249 buf = smalloc(bufsize);
250 if (convert == CONV_TOQP
251 #ifdef HAVE_ICONV
252 || iconvd != (iconv_t)-1
253 #endif
255 lncnt = fsize(fi);
256 for (;;) {
257 if (convert == CONV_TOQP
258 #ifdef HAVE_ICONV
259 || iconvd != (iconv_t)-1
260 #endif
262 if (fgetline(&buf, &bufsize, &lncnt, &inlen, fi, 0)
263 == NULL)
264 break;
265 } else if ((inlen = fread(buf, sizeof *buf, bufsize, fi)) == 0)
266 break;
267 if (xmime_write(buf, inlen, fo, convert, TD_ICONV, NULL) < 0) {
268 err = errno;
269 goto jerr;
272 if (ferror(fi))
273 err = EDOM;
274 jerr:
275 free(buf);
276 jerr_fclose:
277 if (ap->a_conv != AC_TMPFILE)
278 Fclose(fi);
279 jleave:
280 return (err);
283 static char const **
284 _prepare_mta_args(struct name *to, struct header *hp)
286 size_t j, i = 4 + smopts_count + 2 + count(to) + 1;
287 char const **args = salloc(i * sizeof(char*));
289 args[0] = ok_vlook(sendmail_progname);
290 if (args[0] == NULL || *args[0] == '\0')
291 args[0] = SENDMAIL_PROGNAME;
293 args[1] = "-i";
294 i = 2;
295 if (ok_blook(metoo))
296 args[i++] = "-m";
297 if (options & OPT_VERBOSE)
298 args[i++] = "-v";
300 for (j = 0; j < smopts_count; ++j, ++i)
301 args[i] = smopts[j];
303 /* -r option? We may only pass skinned addresses, which is why we do
304 * not simply call myorigin() (TODO myorigin shouldn't fullname!) */
305 if (options & OPT_r_FLAG) {
306 char const *froma;
308 if (option_r_arg[0] != '\0')
309 froma = option_r_arg;
310 else if (hp->h_from != NULL)
311 froma = hp->h_from->n_name;
312 else
313 froma = myorigin(hp);
314 if (froma != NULL) {
315 args[i++] = "-r";
316 args[i++] = froma;
320 /* Receivers follow */
321 for (; to != NULL; to = to->n_flink)
322 if ((to->n_type & GDEL) == 0)
323 args[i++] = to->n_name;
324 args[i] = NULL;
325 return args;
329 * Fix the header by glopping all of the expanded names from
330 * the distribution list into the appropriate fields.
332 static struct name *
333 fixhead(struct header *hp, struct name *tolist)
335 struct name *np;
337 tolist = elide(tolist);
339 hp->h_to = hp->h_cc = hp->h_bcc = NULL;
340 for (np = tolist; np != NULL; np = np->n_flink)
341 if (np->n_type & GDEL) {
342 #ifdef HAVE_DEBUG /* TODO LEGACY CODE */
343 assert(0); /* Shouldn't happen here, but later on :)) */
344 #else
345 continue;
346 #endif
347 } else switch (np->n_type & GMASK) {
348 case (GTO):
349 hp->h_to = cat(hp->h_to, ndup(np, np->n_type|GFULL));
350 break;
351 case (GCC):
352 hp->h_cc = cat(hp->h_cc, ndup(np, np->n_type|GFULL));
353 break;
354 case (GBCC):
355 hp->h_bcc = cat(hp->h_bcc, ndup(np, np->n_type|GFULL));
356 break;
357 default:
358 break;
360 return (tolist);
364 * Put the signature file at fo. TODO send layer rewrite: *integrate in body*!!
366 static int
367 put_signature(FILE *fo, int convert)
369 char *sig, buf[INFIX_BUF], c = '\n';
370 FILE *fsig;
371 size_t sz;
373 sig = ok_vlook(signature);
374 if (sig == NULL || *sig == '\0')
375 return 0;
376 else if ((sig = file_expand(sig)) == NULL)
377 return (-1);
378 if ((fsig = Fopen(sig, "r")) == NULL) {
379 perror(sig);
380 return -1;
382 while ((sz = fread(buf, sizeof *buf, INFIX_BUF, fsig)) != 0) {
383 c = buf[sz - 1];
384 if (xmime_write(buf, sz, fo, convert, TD_NONE, NULL) < 0) {
385 perror(sig);
386 Fclose(fsig);
387 return -1;
390 if (ferror(fsig)) {
391 perror(sig);
392 Fclose(fsig);
393 return -1;
395 Fclose(fsig);
396 if (c != '\n')
397 putc('\n', fo);
398 return 0;
402 * Attach a message to the file buffer.
404 static int
405 attach_message(struct attachment *ap, FILE *fo)
407 struct message *mp;
409 fprintf(fo, "\n--%s\n"
410 "Content-Type: message/rfc822\n"
411 "Content-Disposition: inline\n", send_boundary);
412 if (ap->a_content_description != NULL)
413 fprintf(fo, "Content-Description: %s\n",
414 ap->a_content_description);
415 fputc('\n', fo);
417 mp = &message[ap->a_msgno - 1];
418 touch(mp);
419 if (sendmp(mp, fo, 0, NULL, SEND_RFC822, NULL) < 0)
420 return -1;
421 return 0;
425 * Generate the body of a MIME multipart message.
427 static int
428 make_multipart(struct header *hp, int convert, FILE *fi, FILE *fo,
429 const char *contenttype, const char *charset)
431 struct attachment *att;
433 fputs("This is a multi-part message in MIME format.\n", fo);
434 if (fsize(fi) != 0) {
435 char *buf;
436 size_t sz, bufsize, cnt;
438 fprintf(fo, "\n--%s\n", send_boundary);
439 fprintf(fo, "Content-Type: %s", contenttype);
440 if (charset)
441 fprintf(fo, "; charset=%s", charset);
442 fprintf(fo, "\nContent-Transfer-Encoding: %s\n"
443 "Content-Disposition: inline\n\n",
444 _get_encoding(convert));
445 buf = smalloc(bufsize = INFIX_BUF);
446 if (convert == CONV_TOQP
447 #ifdef HAVE_ICONV
448 || iconvd != (iconv_t)-1
449 #endif
451 fflush(fi);
452 cnt = fsize(fi);
455 for (;;) {
456 if (convert == CONV_TOQP
457 #ifdef HAVE_ICONV
458 || iconvd != (iconv_t)-1
459 #endif
461 if (fgetline(&buf, &bufsize, &cnt, &sz, fi, 0)
462 == NULL)
463 break;
464 } else {
465 sz = fread(buf, sizeof *buf, bufsize, fi);
466 if (sz == 0)
467 break;
470 if (xmime_write(buf, sz, fo, convert, TD_ICONV, NULL)
471 < 0) {
472 free(buf);
473 return -1;
476 free(buf);
477 if (ferror(fi))
478 return -1;
479 if (charset != NULL)
480 put_signature(fo, convert);
482 for (att = hp->h_attach; att != NULL; att = att->a_flink) {
483 if (att->a_msgno) {
484 if (attach_message(att, fo) != 0)
485 return -1;
486 } else {
487 if (_attach_file(att, fo) != 0)
488 return -1;
491 /* the final boundary with two attached dashes */
492 fprintf(fo, "\n--%s--\n", send_boundary);
493 return 0;
497 * Prepend a header in front of the collected stuff
498 * and return the new file.
500 static FILE *
501 infix(struct header *hp, FILE *fi) /* TODO check */
503 FILE *nfo, *nfi;
504 char *tempMail;
505 #ifdef HAVE_ICONV
506 char const *tcs, *convhdr = NULL;
507 #endif
508 enum conversion convert;
509 char const *contenttype, *charset = NULL;
510 int do_iconv = 0;
512 if ((nfo = Ftemp(&tempMail, "Rs", "w", 0600, 1)) == NULL) {
513 perror(tr(178, "temporary mail file"));
514 return(NULL);
516 if ((nfi = Fopen(tempMail, "r")) == NULL) {
517 perror(tempMail);
518 Fclose(nfo);
520 rm(tempMail);
521 Ftfree(&tempMail);
522 if (nfi == NULL)
523 return NULL;
525 contenttype = "text/plain"; /* XXX mail body - always text/plain */
526 convert = mime_classify_file(fi, &contenttype, &charset, &do_iconv);
528 #ifdef HAVE_ICONV
529 tcs = charset_get_lc();
530 if ((convhdr = need_hdrconv(hp, GTO|GSUBJECT|GCC|GBCC|GIDENT)) != 0) {
531 if (iconvd != (iconv_t)-1)
532 n_iconv_close(iconvd);
533 if (asccasecmp(convhdr, tcs) != 0 &&
534 (iconvd = n_iconv_open(convhdr, tcs))
535 == (iconv_t)-1 && errno != 0) {
536 if (errno == EINVAL)
537 fprintf(stderr, tr(179,
538 "Cannot convert from %s to %s\n"),
539 tcs, convhdr);
540 else
541 perror("iconv_open");
542 Fclose(nfo);
543 return NULL;
546 #endif
547 if (puthead(hp, nfo,
548 GTO|GSUBJECT|GCC|GBCC|GNL|GCOMMA|GUA|GMIME
549 |GMSGID|GIDENT|GREF|GDATE,
550 SEND_MBOX, convert, contenttype, charset)) {
551 Fclose(nfo);
552 Fclose(nfi);
553 #ifdef HAVE_ICONV
554 if (iconvd != (iconv_t)-1)
555 n_iconv_close(iconvd);
556 #endif
557 return NULL;
559 #ifdef HAVE_ICONV
560 if (iconvd != (iconv_t)-1)
561 n_iconv_close(iconvd);
562 if (do_iconv && charset != NULL) { /*TODO charset->mime_classify_file*/
563 int err;
564 if (asccasecmp(charset, tcs) != 0 &&
565 (iconvd = n_iconv_open(charset, tcs))
566 == (iconv_t)-1 && (err = errno) != 0) {
567 if (err == EINVAL)
568 fprintf(stderr, tr(179,
569 "Cannot convert from %s to %s\n"),
570 tcs, charset);
571 else
572 perror("iconv_open");
573 Fclose(nfo);
574 return (NULL);
577 #endif
578 if (hp->h_attach != NULL) {
579 if (make_multipart(hp, convert, fi, nfo, contenttype, charset)
580 != 0) {
581 Fclose(nfo);
582 Fclose(nfi);
583 #ifdef HAVE_ICONV
584 if (iconvd != (iconv_t)-1)
585 n_iconv_close(iconvd);
586 #endif
587 return NULL;
589 } else {
590 size_t sz, bufsize, cnt;
591 char *buf;
593 if (convert == CONV_TOQP
594 #ifdef HAVE_ICONV
595 || iconvd != (iconv_t)-1
596 #endif
598 fflush(fi);
599 cnt = fsize(fi);
601 buf = smalloc(bufsize = INFIX_BUF);
602 for (;;) {
603 if (convert == CONV_TOQP
604 #ifdef HAVE_ICONV
605 || iconvd != (iconv_t)-1
606 #endif
608 if (fgetline(&buf, &bufsize, &cnt, &sz, fi, 0)
609 == NULL)
610 break;
611 } else {
612 sz = fread(buf, sizeof *buf, bufsize, fi);
613 if (sz == 0)
614 break;
616 if (xmime_write(buf, sz, nfo, convert, TD_ICONV, NULL)
617 < 0) {
618 Fclose(nfo);
619 Fclose(nfi);
620 #ifdef HAVE_ICONV
621 if (iconvd != (iconv_t)-1)
622 n_iconv_close(iconvd);
623 #endif
624 free(buf);
625 return NULL;
628 free(buf);
629 if (ferror(fi)) {
630 Fclose(nfo);
631 Fclose(nfi);
632 #ifdef HAVE_ICONV
633 if (iconvd != (iconv_t)-1)
634 n_iconv_close(iconvd);
635 #endif
636 return NULL;
638 if (charset != NULL)
639 put_signature(nfo, convert); /* XXX if (text/) !! */
641 #ifdef HAVE_ICONV
642 if (iconvd != (iconv_t)-1)
643 n_iconv_close(iconvd);
644 #endif
645 fflush(nfo);
646 if (ferror(nfo)) {
647 perror(tr(180, "temporary mail file"));
648 Fclose(nfo);
649 Fclose(nfi);
650 return NULL;
652 Fclose(nfo);
653 Fclose(fi);
654 fflush(nfi);
655 rewind(nfi);
656 return(nfi);
660 * Save the outgoing mail on the passed file.
663 /*ARGSUSED*/
664 static int
665 savemail(char const *name, FILE *fi)
667 FILE *fo;
668 char *buf;
669 size_t bufsize, buflen, cnt;
670 int prependnl = 0, error = 0;
672 buf = smalloc(bufsize = LINESIZE);
673 if ((fo = Zopen(name, "a+", NULL)) == NULL) {
674 if ((fo = Zopen(name, "wx", NULL)) == NULL) {
675 perror(name);
676 free(buf);
677 return (-1);
679 } else {
680 if (fseek(fo, -2L, SEEK_END) == 0) {
681 switch (fread(buf, sizeof *buf, 2, fo)) {
682 case 2:
683 if (buf[1] != '\n') {
684 prependnl = 1;
685 break;
687 /*FALLTHRU*/
688 case 1:
689 if (buf[0] != '\n')
690 prependnl = 1;
691 break;
692 default:
693 if (ferror(fo)) {
694 perror(name);
695 free(buf);
696 return -1;
699 fflush(fo);
700 if (prependnl) {
701 putc('\n', fo);
702 fflush(fo);
707 fprintf(fo, "From %s %s", myname, time_current.tc_ctime);
708 buflen = 0;
709 fflush(fi);
710 rewind(fi);
711 cnt = fsize(fi);
712 while (fgetline(&buf, &bufsize, &cnt, &buflen, fi, 0) != NULL) {
713 #ifdef HAVE_DEBUG /* TODO assert legacy */
714 assert(!is_head(buf, buflen));
715 #else
716 if (is_head(buf, buflen))
717 putc('>', fo);
718 #endif
719 fwrite(buf, sizeof *buf, buflen, fo);
721 if (buflen && *(buf + buflen - 1) != '\n')
722 putc('\n', fo);
723 putc('\n', fo);
724 fflush(fo);
725 if (ferror(fo)) {
726 perror(name);
727 error = -1;
729 if (Fclose(fo) != 0)
730 error = -1;
731 fflush(fi);
732 rewind(fi);
733 free(buf);
734 return error;
738 * Interface between the argument list and the mail1 routine
739 * which does all the dirty work.
741 FL int
742 mail(struct name *to, struct name *cc, struct name *bcc,
743 char *subject, struct attachment *attach,
744 char *quotefile, int recipient_record)
746 struct header head;
747 struct str in, out;
749 memset(&head, 0, sizeof head);
750 /* The given subject may be in RFC1522 format. */
751 if (subject != NULL) {
752 in.s = subject;
753 in.l = strlen(subject);
754 mime_fromhdr(&in, &out, /* TODO ??? TD_ISPR |*/ TD_ICONV);
755 head.h_subject = out.s;
757 if (! (options & OPT_t_FLAG)) {
758 head.h_to = to;
759 head.h_cc = cc;
760 head.h_bcc = bcc;
762 head.h_attach = attach;
763 mail1(&head, 0, NULL, quotefile, recipient_record, 0);
764 if (subject != NULL)
765 free(out.s);
766 return 0;
770 * Send mail to a bunch of user names. The interface is through
771 * the mail routine below.
773 static int
774 sendmail_internal(void *v, int recipient_record)
776 char *str = v;
777 struct header head;
779 memset(&head, 0, sizeof head);
780 head.h_to = lextract(str, GTO|GFULL);
781 mail1(&head, 0, NULL, NULL, recipient_record, 0);
782 return 0;
785 FL int
786 csendmail(void *v)
788 return sendmail_internal(v, 0);
791 FL int
792 cSendmail(void *v)
794 return sendmail_internal(v, 1);
797 static enum okay
798 transfer(struct name *to, FILE *input, struct header *hp)
800 char o[LINESIZE], *cp;
801 struct name *np;
802 int cnt = 0;
803 enum okay ok = OKAY;
805 np = to;
806 while (np) {
807 snprintf(o, sizeof o, "smime-encrypt-%s", np->n_name);/* XXX */
808 if ((cp = vok_vlook(o)) != NULL) {
809 #ifdef HAVE_SSL
810 struct name *nt;
811 FILE *ef;
812 if ((ef = smime_encrypt(input, cp, np->n_name)) != 0) {
813 nt = ndup(np, np->n_type & ~(GFULL|GSKIN));
814 if (start_mta(nt, ef, hp) != OKAY)
815 ok = STOP;
816 Fclose(ef);
817 } else {
818 #else
819 fprintf(stderr, tr(225,
820 "No SSL support compiled in.\n"));
821 ok = STOP;
822 #endif
823 fprintf(stderr, tr(38,
824 "Message not sent to <%s>\n"),
825 np->n_name);
826 _senderror = TRU1;
827 #ifdef HAVE_SSL
829 #endif
830 rewind(input);
831 if (np->n_flink)
832 np->n_flink->n_blink = np->n_blink;
833 if (np->n_blink)
834 np->n_blink->n_flink = np->n_flink;
835 if (np == to)
836 to = np->n_flink;
837 np = np->n_flink;
838 } else {
839 cnt++;
840 np = np->n_flink;
843 if (cnt) {
844 if (ok_blook(smime_force_encryption) ||
845 start_mta(to, input, hp) != OKAY)
846 ok = STOP;
848 return ok;
852 * Start the Mail Transfer Agent
853 * mailing to namelist and stdin redirected to input.
855 static enum okay
856 start_mta(struct name *to, FILE *input, struct header *hp)
858 #ifdef HAVE_SMTP
859 char *user = NULL, *password = NULL, *skinned = NULL;
860 #endif
861 char const **args = NULL, **t, *mta;
862 char *smtp;
863 enum okay ok = STOP;
864 pid_t pid;
865 sigset_t nset;
866 (void)hp;
868 if ((smtp = ok_vlook(smtp)) == NULL) {
869 if ((mta = ok_vlook(sendmail)) != NULL) {
870 if ((mta = file_expand(mta)) == NULL)
871 goto jstop;
872 } else
873 mta = SENDMAIL;
875 args = _prepare_mta_args(to, hp);
876 if (options & OPT_DEBUG) {
877 printf(tr(181, "Sendmail arguments:"));
878 for (t = args; *t != NULL; t++)
879 printf(" \"%s\"", *t);
880 printf("\n");
881 ok = OKAY;
882 goto jleave;
884 } else {
885 mta = NULL; /* Silence cc */
886 #ifndef HAVE_SMTP
887 fputs(tr(194, "No SMTP support compiled in.\n"), stderr);
888 goto jstop;
889 #else
890 skinned = skin(myorigin(hp));
891 if ((user = smtp_auth_var("-user", skinned)) != NULL &&
892 (password = smtp_auth_var("-password",
893 skinned)) == NULL)
894 password = getpassword(NULL);
895 #endif
899 * Fork, set up the temporary mail file as standard
900 * input for "mail", and exec with the user list we generated
901 * far above.
903 if ((pid = fork()) == -1) {
904 perror("fork");
905 jstop: savedeadletter(input, 0);
906 _senderror = TRU1;
907 goto jleave;
909 if (pid == 0) {
910 sigemptyset(&nset);
911 sigaddset(&nset, SIGHUP);
912 sigaddset(&nset, SIGINT);
913 sigaddset(&nset, SIGQUIT);
914 sigaddset(&nset, SIGTSTP);
915 sigaddset(&nset, SIGTTIN);
916 sigaddset(&nset, SIGTTOU);
917 freopen("/dev/null", "r", stdin);
918 #ifdef HAVE_SMTP
919 if (smtp != NULL) {
920 prepare_child(&nset, 0, 1);
921 if (smtp_mta(smtp, to, input, hp,
922 user, password, skinned) == 0)
923 _exit(0);
924 } else {
925 #endif
926 prepare_child(&nset, fileno(input), -1);
927 /* If *record* is set then savemail() will move the
928 * file position; it'll call rewind(), but that may
929 * optimize away the systemcall if possible, and since
930 * dup2() shares the position with the original FD the
931 * MTA may end up reading nothing */
932 lseek(0, 0, SEEK_SET);
933 execv(mta, UNCONST(args));
934 perror(mta);
935 #ifdef HAVE_SMTP
937 #endif
938 savedeadletter(input, 1);
939 fputs(tr(182, ". . . message not sent.\n"), stderr);
940 _exit(1);
942 if ((options & (OPT_DEBUG | OPT_VERBOSE | OPT_BATCH_FLAG)) ||
943 ok_blook(sendwait)) {
944 if (wait_child(pid, NULL))
945 ok = OKAY;
946 else
947 _senderror = TRU1;
948 } else {
949 ok = OKAY;
950 free_child(pid);
952 jleave:
953 return ok;
957 * Record outgoing mail if instructed to do so.
959 static enum okay
960 mightrecord(FILE *fp, struct name *to, int recipient_record)
962 char *cp, *cq;
963 char const *ep;
965 if (recipient_record) {
966 size_t i = strlen(cq = skinned_name(to)) + 1;
967 cp = salloc(i);
968 memcpy(cp, cq, i);
969 for (cq = cp; *cq && *cq != '@'; cq++)
971 *cq = '\0';
972 } else
973 cp = ok_vlook(record);
974 if (cp != NULL) {
975 ep = expand(cp);
976 if (ep == NULL) {
977 ep = "NULL";
978 goto jbail;
980 if (ok_blook(outfolder) && *ep != '/' && *ep != '+' &&
981 which_protocol(ep) == PROTO_FILE) {
982 size_t i = strlen(cp);
983 cq = salloc(i + 2);
984 cq[0] = '+';
985 memcpy(cq + 1, cp, i + 1);
986 cp = cq;
987 ep = expand(cp); /* TODO file_expand() possible? */
988 if (ep == NULL) {
989 ep = "NULL";
990 goto jbail;
993 if (savemail(ep, fp) != 0) {
994 jbail: fprintf(stderr, tr(285,
995 "Failed to save message in %s - "
996 "message not sent\n"), ep);
997 exit_status |= 1;
998 savedeadletter(fp, 1);
999 return STOP;
1002 return OKAY;
1006 * Mail a message on standard input to the people indicated
1007 * in the passed header. (Internal interface).
1009 FL enum okay
1010 mail1(struct header *hp, int printheaders, struct message *quote,
1011 char *quotefile, int recipient_record, int doprefix)
1013 enum okay ok = STOP;
1014 struct name *to;
1015 FILE *mtf, *nmtf;
1016 int dosign = -1, err;
1017 char const *cp;
1019 _senderror = FAL0;
1021 /* Update some globals we likely need first */
1022 time_current_update(&time_current, TRU1);
1024 /* */
1025 if ((cp = ok_vlook(autocc)) != NULL && *cp)
1026 hp->h_cc = cat(hp->h_cc, checkaddrs(lextract(cp, GCC|GFULL)));
1027 if ((cp = ok_vlook(autobcc)) != NULL && *cp)
1028 hp->h_bcc = cat(hp->h_bcc, checkaddrs(lextract(cp,GBCC|GFULL)));
1031 * Collect user's mail from standard input.
1032 * Get the result as mtf.
1034 mtf = collect(hp, printheaders, quote, quotefile, doprefix);
1035 if (mtf == NULL)
1036 goto j_leave;
1038 if (options & OPT_INTERACTIVE) {
1039 err = (ok_blook(bsdcompat) || ok_blook(askatend));
1040 if (err == 0)
1041 goto jaskeot;
1042 if (ok_blook(askcc))
1043 ++err, grab_headers(hp, GCC, 1);
1044 if (ok_blook(askbcc))
1045 ++err, grab_headers(hp, GBCC, 1);
1046 if (ok_blook(askattach))
1047 ++err, hp->h_attach = edit_attachments(hp->h_attach);
1048 if (ok_blook(asksign))
1049 ++err, dosign = yorn(tr(35,
1050 "Sign this message (y/n)? "));
1051 if (err == 1) {
1052 jaskeot:
1053 printf(tr(183, "EOT\n"));
1054 fflush(stdout);
1058 if (fsize(mtf) == 0) {
1059 if (options & OPT_E_FLAG)
1060 goto jleave;
1061 if (hp->h_subject == NULL)
1062 printf(tr(184,
1063 "No message, no subject; hope that's ok\n"));
1064 else if (ok_blook(bsdcompat) || ok_blook(bsdmsgs))
1065 printf(tr(185, "Null message body; hope that's ok\n"));
1068 if (dosign < 0)
1069 dosign = ok_blook(smime_sign);
1070 #ifndef HAVE_SSL
1071 if (dosign) {
1072 fprintf(stderr, tr(225, "No SSL support compiled in.\n"));
1073 goto jleave;
1075 #endif
1077 /* XXX Update time_current again; once collect() offers editing of more
1078 * XXX headers, including Date:, this must only happen if Date: is the
1079 * XXX same that it was before collect() (e.g., postponing etc.).
1080 * XXX But *do* update otherwise because the mail seems to be backdated
1081 * XXX if the user edited some time, which looks odd and it happened
1082 * XXX to me that i got mis-dated response mails due to that... */
1083 time_current_update(&time_current, TRU1);
1085 /* TODO hrmpf; the MIME/send layer rewrite MUST address the init crap:
1086 * TODO setup the header ONCE; note this affects edit.c, collect.c ...,
1087 * TODO but: offer a hook that rebuilds/expands/checks/fixates all
1088 * TODO header fields ONCE, call that ONCE after user editing etc. has
1089 * TODO completed (one edit cycle) */
1092 * Take the user names from the combined to and cc lists and do all the
1093 * alias processing. The POSIX standard says:
1094 * The names shall be substituted when alias is used as a recipient
1095 * address specified by the user in an outgoing message (that is,
1096 * other recipients addressed indirectly through the reply command
1097 * shall not be substituted in this manner).
1098 * S-nail thus violates POSIX, as has been pointed out correctly by
1099 * Martin Neitzel, but logic, usability und intellectual penetration of
1100 * POSIX standards is disputable anyway. Go for user friendliness.
1103 /* Do alias expansion on Reply-To: members, too (Martin Neitzel) */
1104 /* TODO puthead() YET (!!! see ONCE note above) expands the value, but
1105 * TODO doesn't perform alias expansion; encapsulate in the ONCE-o */
1106 if (hp->h_replyto == NULL && (cp = ok_vlook(replyto)) != NULL)
1107 hp->h_replyto = checkaddrs(lextract(cp, GEXTRA | GFULL));
1108 if (hp->h_replyto != NULL)
1109 hp->h_replyto = elide(usermap(hp->h_replyto, TRU1));
1112 * TODO what happens now is that all recipients are merged into
1113 * TODO a duplicated list with expanded aliases, then this list is
1114 * TODO splitted again into the three individual recipient lists (with
1115 * TODO duplicates removed).
1116 * TODO later on we use the merged list for outof() pipe/file saving,
1117 * TODO then we eliminate duplicates (again) and then we use that one
1118 * TODO for mightrecord() and transfer(), and again. ... Please ...
1121 * NOTE: Due to elide() in fixhead(), ENSURE to,cc,bcc order of to!,
1122 * because otherwise the recipients will be "degraded" if they occur
1123 * multiple times
1125 to = usermap(cat(hp->h_to, cat(hp->h_cc, hp->h_bcc)), FAL0);
1126 if (to == NULL) {
1127 fprintf(stderr, tr(186, "No recipients specified\n"));
1128 _senderror = TRU1;
1130 to = fixhead(hp, to);
1133 * 'Bit ugly kind of control flow until we find a charset that does it.
1134 * XXX Can maybe be done nicer once we have a carrier struct instead
1135 * XXX of globals
1137 for (charset_iter_reset(hp->h_charset);;) {
1138 if (charset_iter_next() == NULL)
1140 else if ((nmtf = infix(hp, mtf)) != NULL)
1141 break;
1142 else if ((err = errno) == EILSEQ || err == EINVAL) {
1143 rewind(mtf);
1144 continue;
1147 perror("");
1148 #ifdef HAVE_SSL
1149 jfail_dead:
1150 #endif
1151 _senderror = TRU1;
1152 savedeadletter(mtf, 1);
1153 fputs(tr(187, ". . . message not sent.\n"), stderr);
1154 goto jleave;
1157 mtf = nmtf;
1158 #ifdef HAVE_SSL
1159 if (dosign) {
1160 if ((nmtf = smime_sign(mtf, hp)) == NULL)
1161 goto jfail_dead;
1162 Fclose(mtf);
1163 mtf = nmtf;
1165 #endif
1168 * Look through the recipient list for names with /'s
1169 * in them which we write to as files directly.
1171 to = outof(to, mtf, hp, &_senderror);
1172 if (_senderror)
1173 savedeadletter(mtf, 0);
1174 to = elide(to); /* XXX needed only to drop GDELs due to outof()! */
1175 if (count(to) == 0) {
1176 if (! _senderror)
1177 ok = OKAY;
1178 goto jleave;
1181 if (mightrecord(mtf, to, recipient_record) != OKAY)
1182 goto jleave;
1183 ok = transfer(to, mtf, hp);
1185 jleave:
1186 Fclose(mtf);
1187 j_leave:
1188 if (_senderror)
1189 exit_status |= EXIT_SEND_ERROR;
1190 return ok;
1194 * Create a Message-Id: header field.
1195 * Use either the host name or the from address.
1197 static void
1198 message_id(FILE *fo, struct header *hp)
1200 char const *h;
1201 size_t rl;
1202 struct tm *tmp;
1204 if (ok_blook(message_id_disable))
1205 goto jleave;
1206 if ((h = ok_vlook(hostname)) != NULL)
1207 rl = 24;
1208 else if ((h = skin(myorigin(hp))) != NULL && strchr(h, '@') != NULL)
1209 rl = 16;
1210 else
1211 /* Up to MTA */
1212 goto jleave;
1214 tmp = &time_current.tc_gm;
1215 fprintf(fo, "Message-ID: <%04d%02d%02d%02d%02d%02d.%s%c%s>\n",
1216 tmp->tm_year + 1900, tmp->tm_mon + 1, tmp->tm_mday,
1217 tmp->tm_hour, tmp->tm_min, tmp->tm_sec,
1218 getrandstring(rl), (rl == 16 ? '%' : '@'), h);
1219 jleave:
1224 * Create a Date: header field.
1225 * We compare the localtime() and gmtime() results to get the timezone,
1226 * because numeric timezones are easier to read and because $TZ is
1227 * not set on most GNU systems.
1229 FL int
1230 mkdate(FILE *fo, const char *field)
1232 struct tm *tmptr;
1233 int tzdiff, tzdiff_hour, tzdiff_min;
1235 tzdiff = time_current.tc_time - mktime(&time_current.tc_gm);
1236 tzdiff_hour = (int)(tzdiff / 60);
1237 tzdiff_min = tzdiff_hour % 60;
1238 tzdiff_hour /= 60;
1239 tmptr = &time_current.tc_local;
1240 if (tmptr->tm_isdst > 0)
1241 tzdiff_hour++;
1242 return fprintf(fo, "%s: %s, %02d %s %04d %02d:%02d:%02d %+05d\n",
1243 field,
1244 weekday_names[tmptr->tm_wday],
1245 tmptr->tm_mday, month_names[tmptr->tm_mon],
1246 tmptr->tm_year + 1900, tmptr->tm_hour,
1247 tmptr->tm_min, tmptr->tm_sec,
1248 tzdiff_hour * 100 + tzdiff_min);
1251 #define FMT_CC_AND_BCC { \
1252 if (hp->h_cc != NULL && w & GCC) { \
1253 if (fmt("Cc:", hp->h_cc, fo, \
1254 w&(GCOMMA|GFILES), 0, \
1255 action!=SEND_TODISP)) \
1256 return 1; \
1257 gotcha++; \
1259 if (hp->h_bcc != NULL && w & GBCC) { \
1260 if (fmt("Bcc:", hp->h_bcc, fo, \
1261 w&(GCOMMA|GFILES), 0, \
1262 action!=SEND_TODISP)) \
1263 return 1; \
1264 gotcha++; \
1268 * Dump the to, subject, cc header on the
1269 * passed file buffer.
1271 FL int
1272 puthead(struct header *hp, FILE *fo, enum gfield w,
1273 enum sendaction action, enum conversion convert,
1274 char const *contenttype, char const *charset)
1276 char const *addr;
1277 int stealthmua;
1278 size_t gotcha, l;
1279 struct name *np, *fromfield = NULL, *senderfield = NULL;
1281 if ((addr = ok_vlook(stealthmua)) != NULL) {
1282 stealthmua = (strcmp(addr, "noagent") == 0) ? -1 : 1;
1283 } else
1284 stealthmua = 0;
1285 gotcha = 0;
1286 if (w & GDATE) {
1287 mkdate(fo, "Date"), gotcha++;
1289 if (w & GIDENT) {
1290 if (hp->h_from != NULL) {
1291 if (fmt("From:", hp->h_from, fo, w&(GCOMMA|GFILES), 0,
1292 action!=SEND_TODISP))
1293 return 1;
1294 gotcha++;
1295 fromfield = hp->h_from;
1296 } else if ((addr = myaddrs(hp)) != NULL) {
1297 if (_putname(addr, w, action, &gotcha, "From:", fo,
1298 &fromfield))
1299 return 1;
1300 hp->h_from = fromfield;
1302 if (((addr = hp->h_organization) != NULL ||
1303 (addr = ok_vlook(ORGANIZATION)) != NULL) &&
1304 (l = strlen(addr)) > 0) {
1305 fwrite("Organization: ", sizeof (char), 14, fo);
1306 if (xmime_write(addr, l, fo,
1307 action == SEND_TODISP ?
1308 CONV_NONE:CONV_TOHDR,
1309 action == SEND_TODISP ?
1310 TD_ISPR|TD_ICONV:TD_ICONV,
1311 NULL) < 0)
1312 return 1;
1313 gotcha++;
1314 putc('\n', fo);
1316 /* TODO see the ONCE TODO note somewhere around this file;
1317 * TODO but anyway, do NOT perform alias expansion UNLESS
1318 * TODO we are actually sending out! */
1319 if (hp->h_replyto != NULL) {
1320 if (fmt("Reply-To:", hp->h_replyto, fo, w & GCOMMA, 0,
1321 action!=SEND_TODISP))
1322 return 1;
1323 gotcha++;
1324 } else if ((addr = ok_vlook(replyto)) != NULL)
1325 if (_putname(addr, w, action, &gotcha, "Reply-To:", fo,
1326 NULL))
1327 return 1;
1328 if (hp->h_sender != NULL) {
1329 if (fmt("Sender:", hp->h_sender, fo, w & GCOMMA, 0,
1330 action!=SEND_TODISP))
1331 return 1;
1332 gotcha++;
1333 senderfield = hp->h_sender;
1334 } else if ((addr = ok_vlook(sender)) != NULL)
1335 if (_putname(addr, w, action, &gotcha, "Sender:", fo,
1336 &senderfield))
1337 return 1;
1338 if (check_from_and_sender(fromfield, senderfield))
1339 return 1;
1341 if (hp->h_to != NULL && w & GTO) {
1342 if (fmt("To:", hp->h_to, fo, w&(GCOMMA|GFILES), 0,
1343 action!=SEND_TODISP))
1344 return 1;
1345 gotcha++;
1347 if (!ok_blook(bsdcompat) && !ok_blook(bsdorder))
1348 FMT_CC_AND_BCC
1349 if (hp->h_subject != NULL && w & GSUBJECT) {
1350 fwrite("Subject: ", sizeof (char), 9, fo);
1351 if (ascncasecmp(hp->h_subject, "re: ", 4) == 0) {
1352 fwrite("Re: ", sizeof (char), 4, fo);
1353 if (strlen(hp->h_subject + 4) > 0 &&
1354 xmime_write(hp->h_subject + 4,
1355 strlen(hp->h_subject + 4), fo,
1356 action == SEND_TODISP ?
1357 CONV_NONE:CONV_TOHDR,
1358 action == SEND_TODISP ?
1359 TD_ISPR|TD_ICONV:TD_ICONV,
1360 NULL) < 0)
1361 return 1;
1362 } else if (*hp->h_subject) {
1363 if (xmime_write(hp->h_subject, strlen(hp->h_subject),
1364 fo, action == SEND_TODISP ?
1365 CONV_NONE:CONV_TOHDR,
1366 action == SEND_TODISP ?
1367 TD_ISPR|TD_ICONV:TD_ICONV,
1368 NULL) < 0)
1369 return 1;
1371 gotcha++;
1372 fwrite("\n", sizeof (char), 1, fo);
1374 if (ok_blook(bsdcompat) || ok_blook(bsdorder))
1375 FMT_CC_AND_BCC
1376 if (w & GMSGID && stealthmua <= 0)
1377 message_id(fo, hp), gotcha++;
1378 if ((np = hp->h_ref) != NULL && w & GREF) {
1379 fmt("References:", np, fo, 0, 1, 0);
1380 if (np->n_name) {
1381 while (np->n_flink)
1382 np = np->n_flink;
1383 if (is_addr_invalid(np, 0) == 0) {
1384 fprintf(fo, "In-Reply-To: %s\n", np->n_name);
1385 gotcha++;
1389 if (w & GUA && stealthmua == 0)
1390 fprintf(fo, "User-Agent: %s %s\n", uagent, version), gotcha++;
1391 if (w & GMIME) {
1392 fputs("MIME-Version: 1.0\n", fo), gotcha++;
1393 if (hp->h_attach != NULL) {
1394 send_boundary = mime_create_boundary();/*TODO carrier*/
1395 fprintf(fo, "Content-Type: multipart/mixed;\n"
1396 " boundary=\"%s\"\n", send_boundary);
1397 } else {
1398 fprintf(fo, "Content-Type: %s", contenttype);
1399 if (charset)
1400 fprintf(fo, "; charset=%s", charset);
1401 fprintf(fo, "\nContent-Transfer-Encoding: %s\n",
1402 _get_encoding(convert));
1405 if (gotcha && w & GNL)
1406 putc('\n', fo);
1407 return(0);
1411 * Format the given header line to not exceed 72 characters.
1413 static int
1414 fmt(char const *str, struct name *np, FILE *fo, int flags, int dropinvalid,
1415 int domime)
1417 enum {
1418 m_INIT = 1<<0,
1419 m_COMMA = 1<<1,
1420 m_NOPF = 1<<2,
1421 m_CSEEN = 1<<3
1422 } m = (flags & GCOMMA) ? m_COMMA : 0;
1423 ssize_t col, len;
1425 col = strlen(str);
1426 if (col) {
1427 fwrite(str, sizeof *str, col, fo);
1428 if (flags & GFILES)
1429 goto jstep;
1430 if (col == 9 && asccasecmp(str, "reply-to:") == 0) {
1431 m |= m_NOPF;
1432 goto jstep;
1434 if (ok_blook(add_file_recipients))
1435 goto jstep;
1436 if ((col == 3 && ((asccasecmp(str, "to:") == 0) ||
1437 asccasecmp(str, "cc:") == 0)) ||
1438 (col == 4 && asccasecmp(str, "bcc:") == 0) ||
1439 (col == 10 &&
1440 asccasecmp(str, "Resent-To:") == 0))
1441 m |= m_NOPF;
1444 jstep:
1445 for (; np != NULL; np = np->n_flink) {
1446 if ((m & m_NOPF) && is_fileorpipe_addr(np))
1447 continue;
1448 if (is_addr_invalid(np, ! dropinvalid)) {
1449 if (dropinvalid)
1450 continue;
1451 else
1452 return (1);
1454 if ((m & (m_INIT | m_COMMA)) == (m_INIT | m_COMMA)) {
1455 putc(',', fo);
1456 m |= m_CSEEN;
1457 ++col;
1459 len = strlen(np->n_fullname);
1460 ++col; /* The separating space */
1461 if ((m & m_INIT) && col > 1 && col + len > 72) {
1462 fputs("\n ", fo);
1463 col = 1;
1464 m &= ~m_CSEEN;
1465 } else
1466 putc(' ', fo);
1467 m = (m & ~m_CSEEN) | m_INIT;
1468 len = xmime_write(np->n_fullname, len, fo,
1469 domime?CONV_TOHDR_A:CONV_NONE,
1470 TD_ICONV, NULL);
1471 if (len < 0)
1472 return (1);
1473 col += len;
1475 putc('\n', fo);
1476 return (0);
1480 * Rewrite a message for resending, adding the Resent-Headers.
1482 static int
1483 infix_resend(FILE *fi, FILE *fo, struct message *mp, struct name *to,
1484 int add_resent)
1486 size_t cnt, c, bufsize = 0;
1487 char *buf = NULL;
1488 char const *cp;
1489 struct name *fromfield = NULL, *senderfield = NULL;
1491 cnt = mp->m_size;
1493 * Write the Resent-Fields.
1495 if (add_resent) {
1496 fputs("Resent-", fo);
1497 mkdate(fo, "Date");
1498 if ((cp = myaddrs(NULL)) != NULL) {
1499 if (_putname(cp, GCOMMA, SEND_MBOX, NULL,
1500 "Resent-From:", fo, &fromfield))
1501 return 1;
1503 if ((cp = ok_vlook(sender)) != NULL) {
1504 if (_putname(cp, GCOMMA, SEND_MBOX, NULL,
1505 "Resent-Sender:", fo, &senderfield))
1506 return 1;
1508 if (fmt("Resent-To:", to, fo, 1, 1, 0))
1509 return 1;
1510 if ((cp = ok_vlook(stealthmua)) == NULL ||
1511 strcmp(cp, "noagent") == 0) {
1512 fputs("Resent-", fo);
1513 message_id(fo, NULL);
1516 if (check_from_and_sender(fromfield, senderfield))
1517 return 1;
1519 * Write the original headers.
1521 while (cnt > 0) {
1522 if (fgetline(&buf, &bufsize, &cnt, &c, fi, 0) == NULL)
1523 break;
1524 /* XXX more checks: The From_ line may be seen when resending */
1525 if (ascncasecmp("status: ", buf, 8) != 0 &&
1526 strncmp("From ", buf, 5) != 0
1527 /* In the headers, is_head() is actually
1528 * overkill, so a simple ^From_ is sufficient.
1529 * ! is_head(buf, c) */
1531 fwrite(buf, sizeof *buf, c, fo);
1532 if (cnt > 0 && *buf == '\n')
1533 break;
1536 * Write the message body.
1538 while (cnt > 0) {
1539 if (fgetline(&buf, &bufsize, &cnt, &c, fi, 0) == NULL)
1540 break;
1541 if (cnt == 0 && *buf == '\n')
1542 break;
1543 fwrite(buf, sizeof *buf, c, fo);
1545 if (buf)
1546 free(buf);
1547 if (ferror(fo)) {
1548 perror(tr(188, "temporary mail file"));
1549 return 1;
1551 return 0;
1554 FL enum okay
1555 resend_msg(struct message *mp, struct name *to, int add_resent) /* TODO check */
1557 enum okay ok = STOP;
1558 FILE *ibuf, *nfo, *nfi;
1559 char *tempMail;
1560 struct header head;
1562 _senderror = FAL0;
1564 /* Update some globals we likely need first */
1565 time_current_update(&time_current, TRU1);
1567 memset(&head, 0, sizeof head);
1569 if ((to = checkaddrs(to)) == NULL) {
1570 _senderror = TRU1;
1571 goto jleave;
1574 if ((nfo = Ftemp(&tempMail, "Rs", "w", 0600, 1)) == NULL) {
1575 _senderror = TRU1;
1576 perror(tr(189, "temporary mail file"));
1577 goto jleave;
1579 if ((nfi = Fopen(tempMail, "r")) == NULL) {
1580 _senderror = TRU1;
1581 perror(tempMail);
1583 rm(tempMail);
1584 Ftfree(&tempMail);
1585 if (nfi == NULL)
1586 goto jerr_o;
1588 if ((ibuf = setinput(&mb, mp, NEED_BODY)) == NULL)
1589 goto jerr_all;
1590 head.h_to = to;
1591 to = fixhead(&head, to);
1592 if (infix_resend(ibuf, nfo, mp, head.h_to, add_resent) != 0) {
1593 savedeadletter(nfi, 1);
1594 fputs(tr(190, ". . . message not sent.\n"), stderr);
1595 jerr_all:
1596 Fclose(nfi);
1597 jerr_o:
1598 Fclose(nfo);
1599 _senderror = TRU1;
1600 goto jleave;
1602 Fclose(nfo);
1603 rewind(nfi);
1604 to = outof(to, nfi, &head, &_senderror);
1605 if (_senderror)
1606 savedeadletter(nfi, 0);
1607 to = elide(to); /* TODO should have been done in fixhead()? */
1608 if (count(to) != 0) {
1609 if (!ok_blook(record_resent) || 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;