Silence longjmp() warnings and one harmless unused warning
[s-mailx.git] / sendout.c
blob6b912f58af04a9245b00050e0debbad390119f2a
1 /*
2 * S-nail - a mail user agent derived from Berkeley Mail.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 Steffen "Daode" Nurpmeso.
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 "rcv.h"
41 #include "extern.h"
42 #include <errno.h>
43 #include <sys/stat.h>
44 #include <fcntl.h>
45 #include <unistd.h>
46 #include <time.h>
49 * Mail -- a mail program
51 * Mail to others.
54 static char *send_boundary;
56 static char *getencoding(enum conversion convert);
57 static struct name *fixhead(struct header *hp, struct name *tolist);
58 static int put_signature(FILE *fo, int convert);
59 static int attach_file1(struct attachment *ap, FILE *fo, int dosign);
60 static int attach_file(struct attachment *ap, FILE *fo, int dosign);
61 static int attach_message(struct attachment *ap, FILE *fo, int dosign);
62 static int make_multipart(struct header *hp, int convert, FILE *fi, FILE *fo,
63 const char *contenttype, const char *charset, int dosign);
64 static FILE *infix(struct header *hp, FILE *fi, int dosign);
65 static int savemail(char *name, FILE *fi);
66 static int sendmail_internal(void *v, int recipient_record);
67 static enum okay transfer(struct name *to, struct name *mailargs, FILE *input,
68 struct header *hp);
69 static enum okay start_mta(struct name *to, struct name *mailargs, FILE *input,
70 struct header *hp);
71 static void message_id(FILE *fo, struct header *hp);
72 static int fmt(char *str, struct name *np, FILE *fo, int comma,
73 int dropinvalid, int domime);
74 static int infix_resend(FILE *fi, FILE *fo, struct message *mp,
75 struct name *to, int add_resent);
78 * Generate a boundary for MIME multipart messages.
80 char *
81 makeboundary(void)
83 static char bound[70];
84 time_t now;
86 time(&now);
87 snprintf(bound, sizeof bound, "=_%lx.%s", (long)now, getrandstring(48));
88 send_boundary = bound;
89 return send_boundary;
93 * Get an encoding flag based on the given string.
95 static char *
96 getencoding(enum conversion convert)
98 switch (convert) {
99 case CONV_7BIT:
100 return "7bit";
101 case CONV_8BIT:
102 return "8bit";
103 case CONV_TOQP:
104 return "quoted-printable";
105 case CONV_TOB64:
106 return "base64";
107 default:
108 break;
110 /*NOTREACHED*/
111 return NULL;
115 * Fix the header by glopping all of the expanded names from
116 * the distribution list into the appropriate fields.
118 static struct name *
119 fixhead(struct header *hp, struct name *tolist) /* TODO !HAVE_ASSERTS legacy*/
121 struct name *np;
123 hp->h_to = hp->h_cc = hp->h_bcc = NULL;
124 for (np = tolist; np != NULL; np = np->n_flink)
125 if (np->n_type & GDEL) {
126 #ifdef HAVE_ASSERTS
127 assert(0); /* Shouldn't happen here, but later on :)) */
128 #else
129 continue;
130 #endif
131 } else switch (np->n_type & GMASK) {
132 case (GTO):
133 hp->h_to = cat(hp->h_to, ndup(np, np->n_type|GFULL));
134 break;
135 case (GCC):
136 hp->h_cc = cat(hp->h_cc, ndup(np, np->n_type|GFULL));
137 break;
138 case (GBCC):
139 hp->h_bcc = cat(hp->h_bcc, ndup(np, np->n_type|GFULL));
140 break;
141 default:
142 break;
144 return (tolist);
149 * Do not change, you get incorrect base64 encodings else!
151 #define INFIX_BUF 972
154 * Put the signature file at fo.
156 static int
157 put_signature(FILE *fo, int convert)
159 char *sig, buf[INFIX_BUF], c = '\n';
160 FILE *fsig;
161 size_t sz;
163 sig = value("signature");
164 if (sig == NULL || *sig == '\0')
165 return 0;
166 else
167 sig = expand(sig);
168 if ((fsig = Fopen(sig, "r")) == NULL) {
169 perror(sig);
170 return -1;
172 while ((sz = fread(buf, sizeof *buf, INFIX_BUF, fsig)) != 0) {
173 c = buf[sz - 1];
174 if (mime_write(buf, sz, fo, convert, TD_NONE,
175 NULL, (size_t)0, NULL, NULL)
176 == 0) {
177 perror(sig);
178 Fclose(fsig);
179 return -1;
182 if (ferror(fsig)) {
183 perror(sig);
184 Fclose(fsig);
185 return -1;
187 Fclose(fsig);
188 if (c != '\n')
189 putc('\n', fo);
190 return 0;
194 * Write an attachment to the file buffer, converting to MIME.
196 static int
197 attach_file1(struct attachment *ap, FILE *fo, int dosign)
199 FILE *fi;
200 char *charset = NULL, *contenttype = NULL, *basename;
201 enum conversion convert = CONV_TOB64;
202 int err = 0;
203 enum mimeclean isclean;
204 size_t sz;
205 char *buf;
206 size_t bufsize, count;
207 int lastc = EOF;
208 #ifdef HAVE_ICONV
209 char *tcs;
210 #endif
212 if ((fi = Fopen(ap->a_name, "r")) == NULL) {
213 perror(ap->a_name);
214 return -1;
216 if ((basename = strrchr(ap->a_name, '/')) == NULL)
217 basename = ap->a_name;
218 else
219 basename++;
220 if (ap->a_content_type)
221 contenttype = ap->a_content_type;
222 else
223 contenttype = mime_filecontent(basename);
224 if (ap->a_charset)
225 charset = ap->a_charset;
226 convert = get_mime_convert(fi, &contenttype, &charset, &isclean,
227 dosign);
228 fprintf(fo,
229 "\n--%s\n"
230 "Content-Type: %s",
231 send_boundary, contenttype);
232 if (charset == NULL)
233 putc('\n', fo);
234 else
235 fprintf(fo, ";\n charset=%s\n", charset);
236 if (ap->a_content_disposition == NULL)
237 ap->a_content_disposition = "attachment";
238 fprintf(fo, "Content-Transfer-Encoding: %s\n"
239 "Content-Disposition: %s;\n"
240 " filename=\"",
241 getencoding(convert),
242 ap->a_content_disposition);
243 mime_write(basename, strlen(basename), fo,
244 CONV_TOHDR, TD_NONE, NULL, (size_t)0, NULL, NULL);
245 fwrite("\"\n", sizeof (char), 2, fo);
246 if (ap->a_content_id)
247 fprintf(fo, "Content-ID: %s\n", ap->a_content_id);
248 if (ap->a_content_description)
249 fprintf(fo, "Content-Description: %s\n",
250 ap->a_content_description);
251 putc('\n', fo);
252 #ifdef HAVE_ICONV
253 if (iconvd != (iconv_t)-1) {
254 iconv_close(iconvd);
255 iconvd = (iconv_t)-1;
257 tcs = gettcharset();
258 if ((isclean & (MIME_HASNUL|MIME_CTRLCHAR)) == 0 &&
259 ascncasecmp(contenttype, "text/", 5) == 0 &&
260 isclean & MIME_HIGHBIT &&
261 charset != NULL) {
262 if ((iconvd = iconv_open_ft(charset, tcs)) == (iconv_t)-1 &&
263 errno != 0) {
264 if (errno == EINVAL)
265 fprintf(stderr, catgets(catd, CATSET, 179,
266 "Cannot convert from %s to %s\n"), tcs, charset);
267 else
268 perror("iconv_open");
269 Fclose(fi);
270 return -1;
273 #endif /* HAVE_ICONV */
274 buf = smalloc(bufsize = INFIX_BUF);
275 if (convert == CONV_TOQP
276 #ifdef HAVE_ICONV
277 || iconvd != (iconv_t)-1
278 #endif
280 fflush(fi);
281 count = fsize(fi);
283 for (;;) {
284 if (convert == CONV_TOQP
285 #ifdef HAVE_ICONV
286 || iconvd != (iconv_t)-1
287 #endif
289 if (fgetline(&buf, &bufsize, &count, &sz, fi, 0)
290 == NULL)
291 break;
292 } else {
293 if ((sz = fread(buf, sizeof *buf, bufsize, fi)) == 0)
294 break;
296 lastc = buf[sz-1];
297 if (mime_write(buf, sz, fo, convert, TD_ICONV,
298 NULL, (size_t)0, NULL, NULL) == 0)
299 err = -1;
301 if (convert == CONV_TOQP && lastc != '\n')
302 fwrite("=\n", 1, 2, fo);
303 if (ferror(fi))
304 err = -1;
305 Fclose(fi);
306 free(buf);
307 return err;
311 * Try out different character set conversions to attach a file.
313 static int
314 attach_file(struct attachment *ap, FILE *fo, int dosign)
316 char *_wantcharset, *charsets, *ncs;
317 size_t offs = ftell(fo);
319 if (ap->a_charset || (charsets = value("sendcharsets")) == NULL)
320 return attach_file1(ap, fo, dosign);
321 _wantcharset = wantcharset;
322 wantcharset = savestr(charsets);
323 loop: if ((ncs = strchr(wantcharset, ',')) != NULL)
324 *ncs++ = '\0';
325 try: if (attach_file1(ap, fo, dosign) != 0) {
326 if (errno == EILSEQ || errno == EINVAL) {
327 if (ncs && *ncs) {
328 wantcharset = ncs;
329 clearerr(fo);
330 fseek(fo, offs, SEEK_SET);
331 goto loop;
333 if (wantcharset) {
334 if (wantcharset == (char *)-1)
335 wantcharset = NULL;
336 else {
337 wantcharset = (char *)-1;
338 clearerr(fo);
339 fseek(fo, offs, SEEK_SET);
340 goto try;
345 wantcharset = _wantcharset;
346 return 0;
350 * Attach a message to the file buffer.
352 static int
353 attach_message(struct attachment *ap, FILE *fo, int dosign)
355 struct message *mp;
356 (void)dosign;
358 fprintf(fo, "\n--%s\n"
359 "Content-Type: message/rfc822\n"
360 "Content-Disposition: inline\n\n", send_boundary);
361 mp = &message[ap->a_msgno - 1];
362 touch(mp);
363 if (send(mp, fo, 0, NULL, SEND_RFC822, NULL) < 0)
364 return -1;
365 return 0;
369 * Generate the body of a MIME multipart message.
371 static int
372 make_multipart(struct header *hp, int convert, FILE *fi, FILE *fo,
373 const char *contenttype, const char *charset, int dosign)
375 struct attachment *att;
377 fputs("This is a multi-part message in MIME format.\n", fo);
378 if (fsize(fi) != 0) {
379 char *buf, c = '\n';
380 size_t sz, bufsize, count;
382 fprintf(fo, "\n--%s\n", send_boundary);
383 fprintf(fo, "Content-Type: %s", contenttype);
384 if (charset)
385 fprintf(fo, "; charset=%s", charset);
386 fprintf(fo, "\nContent-Transfer-Encoding: %s\n"
387 "Content-Disposition: inline\n\n",
388 getencoding(convert));
389 buf = smalloc(bufsize = INFIX_BUF);
390 if (convert == CONV_TOQP
391 #ifdef HAVE_ICONV
392 || iconvd != (iconv_t)-1
393 #endif /* HAVE_ICONV */
395 fflush(fi);
396 count = fsize(fi);
398 for (;;) {
399 if (convert == CONV_TOQP
400 #ifdef HAVE_ICONV
401 || iconvd != (iconv_t)-1
402 #endif /* HAVE_ICONV */
404 if (fgetline(&buf, &bufsize, &count, &sz, fi, 0)
405 == NULL)
406 break;
407 } else {
408 sz = fread(buf, sizeof *buf, bufsize, fi);
409 if (sz == 0)
410 break;
412 c = buf[sz - 1];
413 if (mime_write(buf, sz, fo, convert,
414 TD_ICONV, NULL, (size_t)0,
415 NULL, NULL) == 0) {
416 free(buf);
417 return -1;
420 free(buf);
421 if (ferror(fi))
422 return -1;
423 if (c != '\n')
424 putc('\n', fo);
425 if (charset != NULL)
426 put_signature(fo, convert);
428 for (att = hp->h_attach; att != NULL; att = att->a_flink) {
429 if (att->a_msgno) {
430 if (attach_message(att, fo, dosign) != 0)
431 return -1;
432 } else {
433 if (attach_file(att, fo, dosign) != 0)
434 return -1;
437 /* the final boundary with two attached dashes */
438 fprintf(fo, "\n--%s--\n", send_boundary);
439 return 0;
443 * Prepend a header in front of the collected stuff
444 * and return the new file.
446 static FILE *
447 infix(struct header *hp, FILE *fi, int dosign)
449 FILE *nfo, *nfi;
450 char *tempMail;
451 #ifdef HAVE_ICONV
452 char *tcs, *convhdr = NULL;
453 #endif
454 enum mimeclean isclean;
455 enum conversion convert;
456 char *charset = NULL, *contenttype = NULL;
457 int lastc = EOF;
459 if ((nfo = Ftemp(&tempMail, "Rs", "w", 0600, 1)) == NULL) {
460 perror(catgets(catd, CATSET, 178, "temporary mail file"));
461 return(NULL);
463 if ((nfi = Fopen(tempMail, "r")) == NULL) {
464 perror(tempMail);
465 Fclose(nfo);
466 return(NULL);
468 rm(tempMail);
469 Ftfree(&tempMail);
470 convert = get_mime_convert(fi, &contenttype, &charset,
471 &isclean, dosign);
472 #ifdef HAVE_ICONV
473 tcs = gettcharset();
474 if ((convhdr = need_hdrconv(hp, GTO|GSUBJECT|GCC|GBCC|GIDENT)) != 0) {
475 if (iconvd != (iconv_t)-1)
476 iconv_close(iconvd);
477 if ((iconvd = iconv_open_ft(convhdr, tcs)) == (iconv_t)-1
478 && errno != 0) {
479 if (errno == EINVAL)
480 fprintf(stderr, catgets(catd, CATSET, 179,
481 "Cannot convert from %s to %s\n"), tcs, convhdr);
482 else
483 perror("iconv_open");
484 Fclose(nfo);
485 return NULL;
488 #endif /* HAVE_ICONV */
489 if (puthead(hp, nfo,
490 GTO|GSUBJECT|GCC|GBCC|GNL|GCOMMA|GUA|GMIME
491 |GMSGID|GIDENT|GREF|GDATE,
492 SEND_MBOX, convert, contenttype, charset)) {
493 Fclose(nfo);
494 Fclose(nfi);
495 #ifdef HAVE_ICONV
496 if (iconvd != (iconv_t)-1) {
497 iconv_close(iconvd);
498 iconvd = (iconv_t)-1;
500 #endif
501 return NULL;
503 #ifdef HAVE_ICONV
504 if (convhdr && iconvd != (iconv_t)-1) {
505 iconv_close(iconvd);
506 iconvd = (iconv_t)-1;
508 if ((isclean & (MIME_HASNUL|MIME_CTRLCHAR)) == 0 &&
509 ascncasecmp(contenttype, "text/", 5) == 0 &&
510 isclean & MIME_HIGHBIT &&
511 charset != NULL) {
512 if (iconvd != (iconv_t)-1)
513 iconv_close(iconvd);
514 if ((iconvd = iconv_open_ft(charset, tcs)) == (iconv_t)-1
515 && errno != 0) {
516 if (errno == EINVAL)
517 fprintf(stderr, catgets(catd, CATSET, 179,
518 "Cannot convert from %s to %s\n"), tcs, charset);
519 else
520 perror("iconv_open");
521 Fclose(nfo);
522 return NULL;
525 #endif
526 if (hp->h_attach != NULL) {
527 if (make_multipart(hp, convert, fi, nfo,
528 contenttype, charset, dosign) != 0) {
529 Fclose(nfo);
530 Fclose(nfi);
531 #ifdef HAVE_ICONV
532 if (iconvd != (iconv_t)-1) {
533 iconv_close(iconvd);
534 iconvd = (iconv_t)-1;
536 #endif
537 return NULL;
539 } else {
540 size_t sz, bufsize, count;
541 char *buf;
543 if (convert == CONV_TOQP
544 #ifdef HAVE_ICONV
545 || iconvd != (iconv_t)-1
546 #endif /* HAVE_ICONV */
548 fflush(fi);
549 count = fsize(fi);
551 buf = smalloc(bufsize = INFIX_BUF);
552 for (;;) {
553 if (convert == CONV_TOQP
554 #ifdef HAVE_ICONV
555 || iconvd != (iconv_t)-1
556 #endif /* HAVE_ICONV */
558 if (fgetline(&buf, &bufsize, &count, &sz, fi, 0)
559 == NULL)
560 break;
561 } else {
562 sz = fread(buf, sizeof *buf, bufsize, fi);
563 if (sz == 0)
564 break;
566 lastc = buf[sz - 1];
567 if (mime_write(buf, sz, nfo, convert,
568 TD_ICONV, NULL, (size_t)0,
569 NULL, NULL) == 0) {
570 Fclose(nfo);
571 Fclose(nfi);
572 #ifdef HAVE_ICONV
573 if (iconvd != (iconv_t)-1) {
574 iconv_close(iconvd);
575 iconvd = (iconv_t)-1;
577 #endif
578 free(buf);
579 return NULL;
582 if (convert == CONV_TOQP && lastc != '\n')
583 fwrite("=\n", 1, 2, nfo);
584 free(buf);
585 if (ferror(fi)) {
586 Fclose(nfo);
587 Fclose(nfi);
588 #ifdef HAVE_ICONV
589 if (iconvd != (iconv_t)-1) {
590 iconv_close(iconvd);
591 iconvd = (iconv_t)-1;
593 #endif
594 return NULL;
596 if (charset != NULL)
597 put_signature(nfo, convert);
599 #ifdef HAVE_ICONV
600 if (iconvd != (iconv_t)-1) {
601 iconv_close(iconvd);
602 iconvd = (iconv_t)-1;
604 #endif
605 fflush(nfo);
606 if (ferror(nfo)) {
607 perror(catgets(catd, CATSET, 180, "temporary mail file"));
608 Fclose(nfo);
609 Fclose(nfi);
610 return NULL;
612 Fclose(nfo);
613 Fclose(fi);
614 fflush(nfi);
615 rewind(nfi);
616 return(nfi);
620 * Save the outgoing mail on the passed file.
623 /*ARGSUSED*/
624 static int
625 savemail(char *name, FILE *fi)
627 FILE *fo;
628 char *buf;
629 size_t bufsize, buflen, count;
630 char *p;
631 time_t now;
632 int prependnl = 0;
633 int error = 0;
635 buf = smalloc(bufsize = LINESIZE);
636 time(&now);
637 if ((fo = Zopen(name, "a+", NULL)) == NULL) {
638 if ((fo = Zopen(name, "wx", NULL)) == NULL) {
639 perror(name);
640 free(buf);
641 return (-1);
643 } else {
644 if (fseek(fo, -2L, SEEK_END) == 0) {
645 switch (fread(buf, sizeof *buf, 2, fo)) {
646 case 2:
647 if (buf[1] != '\n') {
648 prependnl = 1;
649 break;
651 /*FALLTHRU*/
652 case 1:
653 if (buf[0] != '\n')
654 prependnl = 1;
655 break;
656 default:
657 if (ferror(fo)) {
658 perror(name);
659 free(buf);
660 return -1;
663 fflush(fo);
664 if (prependnl) {
665 putc('\n', fo);
666 fflush(fo);
670 fprintf(fo, "From %s %s", myname, ctime(&now));
671 buflen = 0;
672 fflush(fi);
673 rewind(fi);
674 count = fsize(fi);
675 while (fgetline(&buf, &bufsize, &count, &buflen, fi, 0) != NULL) {
676 if (*buf == '>') {
677 p = buf + 1;
678 while (*p == '>')
679 p++;
680 if (strncmp(p, "From ", 5) == 0)
681 /* we got a masked From line */
682 putc('>', fo);
683 } else if (strncmp(buf, "From ", 5) == 0)
684 putc('>', fo);
685 fwrite(buf, sizeof *buf, buflen, fo);
687 if (buflen && *(buf + buflen - 1) != '\n')
688 putc('\n', fo);
689 putc('\n', fo);
690 fflush(fo);
691 if (ferror(fo)) {
692 perror(name);
693 error = -1;
695 if (Fclose(fo) != 0)
696 error = -1;
697 fflush(fi);
698 rewind(fi);
699 free(buf);
700 return error;
704 * Interface between the argument list and the mail1 routine
705 * which does all the dirty work.
707 int
708 mail(struct name *to, struct name *cc, struct name *bcc,
709 struct name *smopts, char *subject, struct attachment *attach,
710 char *quotefile, int recipient_record, int tflag, int Eflag)
712 struct header head;
713 struct str in, out;
715 memset(&head, 0, sizeof head);
716 /* The given subject may be in RFC1522 format. */
717 if (subject != NULL) {
718 in.s = subject;
719 in.l = strlen(subject);
720 mime_fromhdr(&in, &out, TD_ISPR | TD_ICONV);
721 head.h_subject = out.s;
723 if (tflag == 0) {
724 head.h_to = to;
725 head.h_cc = cc;
726 head.h_bcc = bcc;
728 head.h_attach = attach;
729 head.h_smopts = smopts;
730 mail1(&head, 0, NULL, quotefile, recipient_record, 0, tflag, Eflag);
731 if (subject != NULL)
732 free(out.s);
733 return(0);
737 * Send mail to a bunch of user names. The interface is through
738 * the mail routine below.
740 static int
741 sendmail_internal(void *v, int recipient_record)
743 int Eflag;
744 char *str = v;
745 struct header head;
747 memset(&head, 0, sizeof head);
748 head.h_to = extract(str, GTO|GFULL);
749 Eflag = value("skipemptybody") != NULL;
750 mail1(&head, 0, NULL, NULL, recipient_record, 0, 0, Eflag);
751 return(0);
754 int
755 sendmail(void *v)
757 return sendmail_internal(v, 0);
760 int
761 Sendmail(void *v)
763 return sendmail_internal(v, 1);
766 static enum okay
767 transfer(struct name *to, struct name *mailargs, FILE *input, struct header *hp)
769 char o[LINESIZE], *cp;
770 struct name *np, *nt;
771 int cnt = 0;
772 FILE *ef;
773 enum okay ok = OKAY;
775 np = to;
776 while (np) {
777 snprintf(o, sizeof o, "smime-encrypt-%s", np->n_name);
778 if ((cp = value(o)) != NULL) {
779 if ((ef = smime_encrypt(input, cp, np->n_name)) != 0) {
780 nt = ndup(np, np->n_type & ~(GFULL|GSKIN));
781 if (start_mta(nt, mailargs, ef, hp) != OKAY)
782 ok = STOP;
783 Fclose(ef);
784 } else {
785 fprintf(stderr, "Message not sent to <%s>\n",
786 np->n_name);
787 senderr++;
789 rewind(input);
790 if (np->n_flink)
791 np->n_flink->n_blink = np->n_blink;
792 if (np->n_blink)
793 np->n_blink->n_flink = np->n_flink;
794 if (np == to)
795 to = np->n_flink;
796 np = np->n_flink;
797 } else {
798 cnt++;
799 np = np->n_flink;
802 if (cnt) {
803 if (value("smime-force-encryption") ||
804 start_mta(to, mailargs, input, hp) != OKAY)
805 ok = STOP;
807 return ok;
811 * Start the Mail Transfer Agent
812 * mailing to namelist and stdin redirected to input.
814 static enum okay
815 start_mta(struct name *to, struct name *mailargs, FILE *input,
816 struct header *hp)
818 char **args = NULL, **t;
819 pid_t pid;
820 sigset_t nset;
821 char *cp, *smtp;
822 char *user = NULL, *password = NULL, *skinned = NULL;
823 enum okay ok = STOP;
824 #ifdef USE_SMTP
825 struct termios otio;
826 int reset_tio;
827 #endif
829 if ((smtp = value("smtp")) == NULL) {
830 args = unpack(cat(mailargs, to));
831 if (debug || value("debug")) {
832 printf(catgets(catd, CATSET, 181,
833 "Sendmail arguments:"));
834 for (t = args; *t != NULL; t++)
835 printf(" \"%s\"", *t);
836 printf("\n");
837 return OKAY;
840 #ifdef USE_SMTP
841 if (smtp != NULL) {
842 skinned = skin(myorigin(hp));
843 if ((user = smtp_auth_var("-user", skinned)) != NULL &&
844 (password = smtp_auth_var("-password",
845 skinned)) == NULL)
846 password = getpassword(&otio, &reset_tio, NULL);
848 #endif
850 * Fork, set up the temporary mail file as standard
851 * input for "mail", and exec with the user list we generated
852 * far above.
854 if ((pid = fork()) == -1) {
855 perror("fork");
856 savedeadletter(input);
857 senderr++;
858 return STOP;
860 if (pid == 0) {
861 sigemptyset(&nset);
862 sigaddset(&nset, SIGHUP);
863 sigaddset(&nset, SIGINT);
864 sigaddset(&nset, SIGQUIT);
865 sigaddset(&nset, SIGTSTP);
866 sigaddset(&nset, SIGTTIN);
867 sigaddset(&nset, SIGTTOU);
868 freopen("/dev/null", "r", stdin);
869 if (smtp != NULL) {
870 prepare_child(&nset, 0, 1);
871 if (smtp_mta(smtp, to, input, hp,
872 user, password, skinned) == 0)
873 _exit(0);
874 } else {
875 prepare_child(&nset, fileno(input), -1);
876 if ((cp = value("sendmail")) != NULL)
877 cp = expand(cp);
878 else
879 cp = SENDMAIL;
880 execv(cp, args);
881 perror(cp);
883 savedeadletter(input);
884 fputs(catgets(catd, CATSET, 182,
885 ". . . message not sent.\n"), stderr);
886 _exit(1);
888 if (value("verbose") != NULL || value("sendwait") || debug
889 || value("debug")) {
890 if (wait_child(pid) == 0)
891 ok = OKAY;
892 else
893 senderr++;
894 } else {
895 ok = OKAY;
896 free_child(pid);
898 return ok;
902 * Record outgoing mail if instructed to do so.
904 static enum okay
905 mightrecord(FILE *fp, struct name *to, int recipient_record)
907 char *cp, *cq, *ep;
909 if (recipient_record) {
910 cq = skinned_name(to);
911 cp = salloc(strlen(cq) + 1);
912 strcpy(cp, cq);
913 for (cq = cp; *cq && *cq != '@'; cq++);
914 *cq = '\0';
915 } else
916 cp = value("record");
917 if (cp != NULL) {
918 ep = expand(cp);
919 if (value("outfolder") && *ep != '/' && *ep != '+' &&
920 which_protocol(ep) == PROTO_FILE) {
921 cq = salloc(strlen(cp) + 2);
922 cq[0] = '+';
923 strcpy(&cq[1], cp);
924 cp = cq;
925 ep = expand(cp);
927 if (savemail(ep, fp) != 0) {
928 fprintf(stderr,
929 "Error while saving message to %s - "
930 "message not sent\n", ep);
931 rewind(fp);
932 exit_status |= 1;
933 savedeadletter(fp);
934 return STOP;
937 return OKAY;
941 * Mail a message on standard input to the people indicated
942 * in the passed header. (Internal interface).
944 enum okay
945 mail1(struct header *hp, int printheaders, struct message *quote,
946 char *quotefile, int recipient_record, int doprefix, int tflag,
947 int Eflag)
949 struct name *to;
950 FILE *mtf, *nmtf;
951 enum okay ok = STOP;
952 int dosign = -1;
953 char *charsets, *ncs = NULL, *cp;
955 #ifdef notdef
956 if ((hp->h_to = checkaddrs(hp->h_to)) == NULL) {
957 senderr++;
958 return STOP;
960 #endif
961 if ((cp = value("autocc")) != NULL && *cp)
962 hp->h_cc = cat(hp->h_cc, checkaddrs(lextract(cp, GCC|GFULL)));
963 if ((cp = value("autobcc")) != NULL && *cp)
964 hp->h_bcc = cat(hp->h_bcc, checkaddrs(lextract(cp,GBCC|GFULL)));
966 * Collect user's mail from standard input.
967 * Get the result as mtf.
969 if ((mtf = collect(hp, printheaders, quote, quotefile, doprefix,
970 tflag)) == NULL)
971 return STOP;
972 if (value("interactive") != NULL) {
973 if (((value("bsdcompat") || value("askatend"))
974 && (value("askcc") != NULL ||
975 value("askbcc") != NULL)) ||
976 value("askattach") != NULL ||
977 value("asksign") != NULL) {
978 if (value("askcc") != NULL)
979 grabh(hp, GCC, 1);
980 if (value("askbcc") != NULL)
981 grabh(hp, GBCC, 1);
982 if (value("askattach") != NULL)
983 hp->h_attach = edit_attachments(hp->h_attach);
984 if (value("asksign") != NULL)
985 dosign = yorn("Sign this message (y/n)? ");
986 } else {
987 printf(catgets(catd, CATSET, 183, "EOT\n"));
988 fflush(stdout);
991 if (fsize(mtf) == 0) {
992 if (Eflag)
993 goto out;
994 if (hp->h_subject == NULL)
995 printf(catgets(catd, CATSET, 184,
996 "No message, no subject; hope that's ok\n"));
997 else if (value("bsdcompat") || value("bsdmsgs"))
998 printf(catgets(catd, CATSET, 185,
999 "Null message body; hope that's ok\n"));
1001 if (dosign < 0) {
1002 if (value("smime-sign") != NULL)
1003 dosign = 1;
1004 else
1005 dosign = 0;
1008 * Now, take the user names from the combined
1009 * to and cc lists and do all the alias
1010 * processing.
1012 senderr = 0;
1013 if ((to = usermap(cat(hp->h_bcc, cat(hp->h_to, hp->h_cc)))) == NULL) {
1014 printf(catgets(catd, CATSET, 186, "No recipients specified\n"));
1015 senderr++;
1017 to = fixhead(hp, to);
1018 if (hp->h_charset) {
1019 wantcharset = hp->h_charset;
1020 goto try;
1022 hloop: wantcharset = NULL;
1023 if ((charsets = value("sendcharsets")) != NULL) {
1024 wantcharset = savestr(charsets);
1025 loop: if ((ncs = strchr(wantcharset, ',')) != NULL)
1026 *ncs++ = '\0';
1028 try: if ((nmtf = infix(hp, mtf, dosign)) == NULL) {
1029 if (hp->h_charset && (errno == EILSEQ || errno == EINVAL)) {
1030 rewind(mtf);
1031 hp->h_charset = NULL;
1032 goto hloop;
1034 if (errno == EILSEQ || errno == EINVAL) {
1035 if (ncs && *ncs) {
1036 rewind(mtf);
1037 wantcharset = ncs;
1038 goto loop;
1040 if (wantcharset && value("interactive") == NULL) {
1041 if (wantcharset == (char *)-1)
1042 wantcharset = NULL;
1043 else {
1044 rewind(mtf);
1045 wantcharset = (char *)-1;
1046 goto try;
1050 /* fprintf(stderr, ". . . message lost, sorry.\n"); */
1051 perror("");
1052 fail: senderr++;
1053 rewind(mtf);
1054 savedeadletter(mtf);
1055 fputs(catgets(catd, CATSET, 187,
1056 ". . . message not sent.\n"), stderr);
1057 return STOP;
1059 mtf = nmtf;
1060 if (dosign) {
1061 if ((nmtf = smime_sign(mtf, hp)) == NULL)
1062 goto fail;
1063 Fclose(mtf);
1064 mtf = nmtf;
1067 * Look through the recipient list for names with /'s
1068 * in them which we write to as files directly.
1070 to = outof(to, mtf, hp);
1071 if (senderr)
1072 savedeadletter(mtf);
1073 to = elide(to);
1074 if (count(to) == 0) {
1075 if (senderr == 0)
1076 ok = OKAY;
1077 goto out;
1079 if (mightrecord(mtf, to, recipient_record) != OKAY)
1080 goto out;
1081 ok = transfer(to, hp->h_smopts, mtf, hp);
1082 out:
1083 Fclose(mtf);
1084 return ok;
1088 * Create a Message-Id: header field.
1089 * Use either the host name or the from address.
1091 static void
1092 message_id(FILE *fo, struct header *hp)
1094 time_t now;
1095 struct tm *tmp;
1096 char *h;
1097 size_t rl;
1099 time(&now);
1100 tmp = gmtime(&now);
1101 if ((h = value("hostname")) != NULL)
1102 rl = 24;
1103 else if ((h = skin(myorigin(hp))) != NULL && strchr(h, '@') != NULL)
1104 rl = 16;
1105 else
1106 /* Delivery seems to dependent on a MTA -- it's up to it */
1107 return;
1109 fprintf(fo, "Message-ID: <%04d%02d%02d%02d%02d%02d.%s%c%s>\n",
1110 tmp->tm_year + 1900, tmp->tm_mon + 1, tmp->tm_mday,
1111 tmp->tm_hour, tmp->tm_min, tmp->tm_sec,
1112 getrandstring(rl), (rl == 16 ? '%' : '@'), h);
1115 static const char *weekday_names[] = {
1116 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
1119 const char *month_names[] = {
1120 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
1121 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", NULL
1125 * Create a Date: header field.
1126 * We compare the localtime() and gmtime() results to get the timezone,
1127 * because numeric timezones are easier to read and because $TZ is
1128 * not set on most GNU systems.
1131 mkdate(FILE *fo, const char *field)
1133 time_t t;
1134 struct tm *tmptr;
1135 int tzdiff, tzdiff_hour, tzdiff_min;
1137 time(&t);
1138 tzdiff = t - mktime(gmtime(&t));
1139 tzdiff_hour = (int)(tzdiff / 60);
1140 tzdiff_min = tzdiff_hour % 60;
1141 tzdiff_hour /= 60;
1142 tmptr = localtime(&t);
1143 if (tmptr->tm_isdst > 0)
1144 tzdiff_hour++;
1145 return fprintf(fo, "%s: %s, %02d %s %04d %02d:%02d:%02d %+05d\n",
1146 field,
1147 weekday_names[tmptr->tm_wday],
1148 tmptr->tm_mday, month_names[tmptr->tm_mon],
1149 tmptr->tm_year + 1900, tmptr->tm_hour,
1150 tmptr->tm_min, tmptr->tm_sec,
1151 tzdiff_hour * 100 + tzdiff_min);
1154 static enum okay
1155 putname(char *line, enum gfield w, enum sendaction action, int *gotcha,
1156 char *prefix, FILE *fo, struct name **xp)
1158 struct name *np;
1160 np = sextract(line, GEXTRA|GFULL);
1161 if (xp)
1162 *xp = np;
1163 if (np == NULL)
1164 return 0;
1165 if (fmt(prefix, np, fo, w&(GCOMMA|GFILES), 0, action != SEND_TODISP))
1166 return 1;
1167 if (gotcha)
1168 (*gotcha)++;
1169 return 0;
1172 #define FMT_CC_AND_BCC { \
1173 if (hp->h_cc != NULL && w & GCC) { \
1174 if (fmt("Cc:", hp->h_cc, fo, \
1175 w&(GCOMMA|GFILES), 0, \
1176 action!=SEND_TODISP)) \
1177 return 1; \
1178 gotcha++; \
1180 if (hp->h_bcc != NULL && w & GBCC) { \
1181 if (fmt("Bcc:", hp->h_bcc, fo, \
1182 w&(GCOMMA|GFILES), 0, \
1183 action!=SEND_TODISP)) \
1184 return 1; \
1185 gotcha++; \
1189 * Dump the to, subject, cc header on the
1190 * passed file buffer.
1193 puthead(struct header *hp, FILE *fo, enum gfield w,
1194 enum sendaction action, enum conversion convert,
1195 char *contenttype, char *charset)
1197 int gotcha;
1198 char *addr/*, *cp*/;
1199 int stealthmua;
1200 struct name *np, *fromfield = NULL, *senderfield = NULL;
1203 if ((addr = value("stealthmua")) != NULL) {
1204 stealthmua = (strcmp(addr, "noagent") == 0) ? -1 : 1;
1205 } else
1206 stealthmua = 0;
1207 gotcha = 0;
1208 if (w & GDATE) {
1209 mkdate(fo, "Date"), gotcha++;
1211 if (w & GIDENT) {
1212 if (hp->h_from != NULL) {
1213 if (fmt("From:", hp->h_from, fo, w&(GCOMMA|GFILES), 0,
1214 action!=SEND_TODISP))
1215 return 1;
1216 gotcha++;
1217 fromfield = hp->h_from;
1218 } else if ((addr = myaddrs(hp)) != NULL)
1219 if (putname(addr, w, action, &gotcha, "From:", fo,
1220 &fromfield))
1221 return 1;
1222 if (((addr = hp->h_organization) != NULL ||
1223 (addr = value("ORGANIZATION")) != NULL)
1224 && strlen(addr) > 0) {
1225 fwrite("Organization: ", sizeof (char), 14, fo);
1226 if (mime_write(addr, strlen(addr), fo,
1227 action == SEND_TODISP ?
1228 CONV_NONE:CONV_TOHDR,
1229 action == SEND_TODISP ?
1230 TD_ISPR|TD_ICONV:TD_ICONV,
1231 NULL, (size_t)0,
1232 NULL, NULL) == 0)
1233 return 1;
1234 gotcha++;
1235 putc('\n', fo);
1237 if (hp->h_replyto != NULL) {
1238 if (fmt("Reply-To:", hp->h_replyto, fo,
1239 w&(GCOMMA|GFILES), 0,
1240 action!=SEND_TODISP))
1241 return 1;
1242 gotcha++;
1243 } else if ((addr = value("replyto")) != NULL)
1244 if (putname(addr, w, action, &gotcha, "Reply-To:", fo,
1245 NULL))
1246 return 1;
1247 if (hp->h_sender != NULL) {
1248 if (fmt("Sender:", hp->h_sender, fo,
1249 w&(GCOMMA|GFILES), 0,
1250 action!=SEND_TODISP))
1251 return 1;
1252 gotcha++;
1253 senderfield = hp->h_sender;
1254 } else if ((addr = value("sender")) != NULL)
1255 if (putname(addr, w, action, &gotcha, "Sender:", fo,
1256 &senderfield))
1257 return 1;
1258 if (check_from_and_sender(fromfield, senderfield))
1259 return 1;
1261 if (hp->h_to != NULL && w & GTO) {
1262 if (fmt("To:", hp->h_to, fo, w&(GCOMMA|GFILES), 0,
1263 action!=SEND_TODISP))
1264 return 1;
1265 gotcha++;
1267 if (value("bsdcompat") == NULL && value("bsdorder") == NULL)
1268 FMT_CC_AND_BCC
1269 if (hp->h_subject != NULL && w & GSUBJECT) {
1270 fwrite("Subject: ", sizeof (char), 9, fo);
1271 if (ascncasecmp(hp->h_subject, "re: ", 4) == 0) {
1272 fwrite("Re: ", sizeof (char), 4, fo);
1273 if (strlen(hp->h_subject + 4) > 0 &&
1274 mime_write(hp->h_subject + 4,
1275 strlen(hp->h_subject + 4),
1276 fo, action == SEND_TODISP ?
1277 CONV_NONE:CONV_TOHDR,
1278 action == SEND_TODISP ?
1279 TD_ISPR|TD_ICONV:TD_ICONV,
1280 NULL, (size_t)0,
1281 NULL, NULL) == 0)
1282 return 1;
1283 } else if (*hp->h_subject) {
1284 if (mime_write(hp->h_subject,
1285 strlen(hp->h_subject),
1286 fo, action == SEND_TODISP ?
1287 CONV_NONE:CONV_TOHDR,
1288 action == SEND_TODISP ?
1289 TD_ISPR|TD_ICONV:TD_ICONV,
1290 NULL, (size_t)0,
1291 NULL, NULL) == 0)
1292 return 1;
1294 gotcha++;
1295 fwrite("\n", sizeof (char), 1, fo);
1297 if (value("bsdcompat") || value("bsdorder"))
1298 FMT_CC_AND_BCC
1299 if (w & GMSGID && stealthmua <= 0)
1300 message_id(fo, hp), gotcha++;
1301 if ((np = hp->h_ref) != NULL && w & GREF) {
1302 fmt("References:", np, fo, 0, 1, 0);
1303 if (np->n_name) {
1304 while (np->n_flink)
1305 np = np->n_flink;
1306 if (is_addr_invalid(np, 0) == 0) {
1307 fprintf(fo, "In-Reply-To: %s\n", np->n_name);
1308 gotcha++;
1312 if (w & GUA && stealthmua == 0)
1313 fprintf(fo, "User-Agent: %s %s\n", uagent, version), gotcha++;
1314 if (w & GMIME) {
1315 fputs("MIME-Version: 1.0\n", fo), gotcha++;
1316 if (hp->h_attach != NULL) {
1317 makeboundary();
1318 fprintf(fo, "Content-Type: multipart/mixed;\n"
1319 " boundary=\"%s\"\n", send_boundary);
1320 } else {
1321 fprintf(fo, "Content-Type: %s", contenttype);
1322 if (charset)
1323 fprintf(fo, "; charset=%s", charset);
1324 fprintf(fo, "\nContent-Transfer-Encoding: %s\n",
1325 getencoding(convert));
1328 if (gotcha && w & GNL)
1329 putc('\n', fo);
1330 return(0);
1334 * Format the given header line to not exceed 72 characters.
1336 static int
1337 fmt(char *str, struct name *np, FILE *fo, int flags, int dropinvalid,
1338 int domime)
1340 enum {
1341 m_INIT = 1<<0,
1342 m_COMMA = 1<<1,
1343 m_NOPF = 1<<2,
1344 m_CSEEN = 1<<3
1345 } m = (flags & GCOMMA) ? m_COMMA : 0;
1346 int col, len;
1348 col = strlen(str);
1349 if (col) {
1350 fwrite(str, sizeof *str, strlen(str), fo);
1351 if ((flags&GFILES) == 0 && ! value("add-file-recipients") &&
1352 ((col == 3 && ((asccasecmp(str, "to:") == 0) ||
1353 asccasecmp(str, "cc:") == 0)) ||
1354 (col == 4 && asccasecmp(str, "bcc:") == 0) ||
1355 (col == 10 &&
1356 asccasecmp(str, "Resent-To:") == 0)))
1357 m |= m_NOPF;
1359 for (; np != NULL; np = np->n_flink) {
1360 if ((m & m_NOPF) && is_fileorpipe_addr(np))
1361 continue;
1362 if (is_addr_invalid(np, ! dropinvalid)) {
1363 if (dropinvalid)
1364 continue;
1365 else
1366 return (1);
1368 if ((m & (m_INIT | m_COMMA)) == (m_INIT | m_COMMA)) {
1369 putc(',', fo);
1370 m |= m_CSEEN;
1371 ++col;
1373 len = strlen(np->n_fullname);
1374 ++col; /* The separating space */
1375 if ((m & m_INIT) && col > 1 && col + len > 72) {
1376 fputs("\n ", fo);
1377 col = 1;
1378 m &= ~m_CSEEN;
1379 } else
1380 putc(' ', fo);
1381 m = (m & ~m_CSEEN) | m_INIT;
1382 len = mime_write(np->n_fullname,
1383 len, fo,
1384 domime?CONV_TOHDR_A:CONV_NONE,
1385 TD_ICONV, NULL, (size_t)0,
1386 NULL, NULL);
1387 col += len;
1389 putc('\n', fo);
1390 return (0);
1394 * Rewrite a message for resending, adding the Resent-Headers.
1396 static int
1397 infix_resend(FILE *fi, FILE *fo, struct message *mp, struct name *to,
1398 int add_resent)
1400 size_t count;
1401 char *buf = NULL, *cp/*, *cp2*/;
1402 size_t c, bufsize = 0;
1403 struct name *fromfield = NULL, *senderfield = NULL;
1405 count = mp->m_size;
1407 * Write the Resent-Fields.
1409 if (add_resent) {
1410 fputs("Resent-", fo);
1411 mkdate(fo, "Date");
1412 if ((cp = myaddrs(NULL)) != NULL) {
1413 if (putname(cp, GCOMMA, SEND_MBOX, NULL,
1414 "Resent-From:", fo, &fromfield))
1415 return 1;
1417 if ((cp = value("sender")) != NULL) {
1418 if (putname(cp, GCOMMA, SEND_MBOX, NULL,
1419 "Resent-Sender:", fo, &senderfield))
1420 return 1;
1422 if (fmt("Resent-To:", to, fo, 1, 1, 0)) {
1423 if (buf)
1424 free(buf);
1425 return 1;
1427 if ((cp = value("stealthmua")) == NULL ||
1428 strcmp(cp, "noagent") == 0) {
1429 fputs("Resent-", fo);
1430 message_id(fo, NULL);
1433 if (check_from_and_sender(fromfield, senderfield))
1434 return 1;
1436 * Write the original headers.
1438 while (count > 0) {
1439 if ((cp = foldergets(&buf, &bufsize, &count, &c, fi)) == NULL)
1440 break;
1441 if (ascncasecmp("status: ", buf, 8) != 0
1442 && strncmp("From ", buf, 5) != 0) {
1443 fwrite(buf, sizeof *buf, c, fo);
1445 if (count > 0 && *buf == '\n')
1446 break;
1449 * Write the message body.
1451 while (count > 0) {
1452 if (foldergets(&buf, &bufsize, &count, &c, fi) == NULL)
1453 break;
1454 if (count == 0 && *buf == '\n')
1455 break;
1456 fwrite(buf, sizeof *buf, c, fo);
1458 if (buf)
1459 free(buf);
1460 if (ferror(fo)) {
1461 perror(catgets(catd, CATSET, 188, "temporary mail file"));
1462 return 1;
1464 return 0;
1467 enum okay
1468 resend_msg(struct message *mp, struct name *to, int add_resent)
1470 FILE *ibuf, *nfo, *nfi;
1471 char *tempMail;
1472 struct header head;
1473 enum okay ok = STOP;
1475 memset(&head, 0, sizeof head);
1476 if ((to = checkaddrs(to)) == NULL) {
1477 senderr++;
1478 return STOP;
1480 if ((nfo = Ftemp(&tempMail, "Rs", "w", 0600, 1)) == NULL) {
1481 senderr++;
1482 perror(catgets(catd, CATSET, 189, "temporary mail file"));
1483 return STOP;
1485 if ((nfi = Fopen(tempMail, "r")) == NULL) {
1486 senderr++;
1487 perror(tempMail);
1488 return STOP;
1490 rm(tempMail);
1491 Ftfree(&tempMail);
1492 if ((ibuf = setinput(&mb, mp, NEED_BODY)) == NULL)
1493 return STOP;
1494 head.h_to = to;
1495 to = fixhead(&head, to);
1496 if (infix_resend(ibuf, nfo, mp, head.h_to, add_resent) != 0) {
1497 senderr++;
1498 rewind(nfo);
1499 savedeadletter(nfi);
1500 fputs(catgets(catd, CATSET, 190,
1501 ". . . message not sent.\n"), stderr);
1502 Fclose(nfo);
1503 Fclose(nfi);
1504 return STOP;
1506 fflush(nfo);
1507 rewind(nfo);
1508 Fclose(nfo);
1509 to = outof(to, nfi, &head);
1510 if (senderr)
1511 savedeadletter(nfi);
1512 to = elide(to);
1513 if (count(to) != 0) {
1514 if (value("record-resent") == NULL ||
1515 mightrecord(nfi, to, 0) == OKAY)
1516 ok = transfer(to, head.h_smopts, nfi, NULL);
1517 } else if (senderr == 0)
1518 ok = OKAY;
1519 Fclose(nfi);
1520 return ok;