Resort options, exact usage hint..
[s-mailx.git] / sendout.c
blob3c6fe6f8a08dc3d2c5cfaf32ee0ade9c75f845cb
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)
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 continue;
127 else switch (np->n_type & GMASK) {
128 case (GTO):
129 hp->h_to = cat(hp->h_to, ndup(np, np->n_type|GFULL));
130 break;
131 case (GCC):
132 hp->h_cc = cat(hp->h_cc, ndup(np, np->n_type|GFULL));
133 break;
134 case (GBCC):
135 hp->h_bcc = cat(hp->h_bcc, ndup(np, np->n_type|GFULL));
136 break;
137 default:
138 break;
140 return (tolist);
145 * Do not change, you get incorrect base64 encodings else!
147 #define INFIX_BUF 972
150 * Put the signature file at fo.
152 static int
153 put_signature(FILE *fo, int convert)
155 char *sig, buf[INFIX_BUF], c = '\n';
156 FILE *fsig;
157 size_t sz;
159 sig = value("signature");
160 if (sig == NULL || *sig == '\0')
161 return 0;
162 else
163 sig = expand(sig);
164 if ((fsig = Fopen(sig, "r")) == NULL) {
165 perror(sig);
166 return -1;
168 while ((sz = fread(buf, sizeof *buf, INFIX_BUF, fsig)) != 0) {
169 c = buf[sz - 1];
170 if (mime_write(buf, sz, fo, convert, TD_NONE,
171 NULL, (size_t)0, NULL, NULL)
172 == 0) {
173 perror(sig);
174 Fclose(fsig);
175 return -1;
178 if (ferror(fsig)) {
179 perror(sig);
180 Fclose(fsig);
181 return -1;
183 Fclose(fsig);
184 if (c != '\n')
185 putc('\n', fo);
186 return 0;
190 * Write an attachment to the file buffer, converting to MIME.
192 static int
193 attach_file1(struct attachment *ap, FILE *fo, int dosign)
195 FILE *fi;
196 char *charset = NULL, *contenttype = NULL, *basename;
197 enum conversion convert = CONV_TOB64;
198 int err = 0;
199 enum mimeclean isclean;
200 size_t sz;
201 char *buf;
202 size_t bufsize, count;
203 int lastc = EOF;
204 #ifdef HAVE_ICONV
205 char *tcs;
206 #endif
208 if ((fi = Fopen(ap->a_name, "r")) == NULL) {
209 perror(ap->a_name);
210 return -1;
212 if ((basename = strrchr(ap->a_name, '/')) == NULL)
213 basename = ap->a_name;
214 else
215 basename++;
216 if (ap->a_content_type)
217 contenttype = ap->a_content_type;
218 else
219 contenttype = mime_filecontent(basename);
220 if (ap->a_charset)
221 charset = ap->a_charset;
222 convert = get_mime_convert(fi, &contenttype, &charset, &isclean,
223 dosign);
224 fprintf(fo,
225 "\n--%s\n"
226 "Content-Type: %s",
227 send_boundary, contenttype);
228 if (charset == NULL)
229 putc('\n', fo);
230 else
231 fprintf(fo, ";\n charset=%s\n", charset);
232 if (ap->a_content_disposition == NULL)
233 ap->a_content_disposition = "attachment";
234 fprintf(fo, "Content-Transfer-Encoding: %s\n"
235 "Content-Disposition: %s;\n"
236 " filename=\"",
237 getencoding(convert),
238 ap->a_content_disposition);
239 mime_write(basename, strlen(basename), fo,
240 CONV_TOHDR, TD_NONE, NULL, (size_t)0, NULL, NULL);
241 fwrite("\"\n", sizeof (char), 2, fo);
242 if (ap->a_content_id)
243 fprintf(fo, "Content-ID: %s\n", ap->a_content_id);
244 if (ap->a_content_description)
245 fprintf(fo, "Content-Description: %s\n",
246 ap->a_content_description);
247 putc('\n', fo);
248 #ifdef HAVE_ICONV
249 if (iconvd != (iconv_t)-1) {
250 iconv_close(iconvd);
251 iconvd = (iconv_t)-1;
253 tcs = gettcharset();
254 if ((isclean & (MIME_HASNUL|MIME_CTRLCHAR)) == 0 &&
255 ascncasecmp(contenttype, "text/", 5) == 0 &&
256 isclean & MIME_HIGHBIT &&
257 charset != NULL) {
258 if ((iconvd = iconv_open_ft(charset, tcs)) == (iconv_t)-1 &&
259 errno != 0) {
260 if (errno == EINVAL)
261 fprintf(stderr, catgets(catd, CATSET, 179,
262 "Cannot convert from %s to %s\n"), tcs, charset);
263 else
264 perror("iconv_open");
265 Fclose(fi);
266 return -1;
269 #endif /* HAVE_ICONV */
270 buf = smalloc(bufsize = INFIX_BUF);
271 if (convert == CONV_TOQP
272 #ifdef HAVE_ICONV
273 || iconvd != (iconv_t)-1
274 #endif
276 fflush(fi);
277 count = fsize(fi);
279 for (;;) {
280 if (convert == CONV_TOQP
281 #ifdef HAVE_ICONV
282 || iconvd != (iconv_t)-1
283 #endif
285 if (fgetline(&buf, &bufsize, &count, &sz, fi, 0)
286 == NULL)
287 break;
288 } else {
289 if ((sz = fread(buf, sizeof *buf, bufsize, fi)) == 0)
290 break;
292 lastc = buf[sz-1];
293 if (mime_write(buf, sz, fo, convert, TD_ICONV,
294 NULL, (size_t)0, NULL, NULL) == 0)
295 err = -1;
297 if (convert == CONV_TOQP && lastc != '\n')
298 fwrite("=\n", 1, 2, fo);
299 if (ferror(fi))
300 err = -1;
301 Fclose(fi);
302 free(buf);
303 return err;
307 * Try out different character set conversions to attach a file.
309 static int
310 attach_file(struct attachment *ap, FILE *fo, int dosign)
312 char *_wantcharset, *charsets, *ncs;
313 size_t offs = ftell(fo);
315 if (ap->a_charset || (charsets = value("sendcharsets")) == NULL)
316 return attach_file1(ap, fo, dosign);
317 _wantcharset = wantcharset;
318 wantcharset = savestr(charsets);
319 loop: if ((ncs = strchr(wantcharset, ',')) != NULL)
320 *ncs++ = '\0';
321 try: if (attach_file1(ap, fo, dosign) != 0) {
322 if (errno == EILSEQ || errno == EINVAL) {
323 if (ncs && *ncs) {
324 wantcharset = ncs;
325 clearerr(fo);
326 fseek(fo, offs, SEEK_SET);
327 goto loop;
329 if (wantcharset) {
330 if (wantcharset == (char *)-1)
331 wantcharset = NULL;
332 else {
333 wantcharset = (char *)-1;
334 clearerr(fo);
335 fseek(fo, offs, SEEK_SET);
336 goto try;
341 wantcharset = _wantcharset;
342 return 0;
346 * Attach a message to the file buffer.
348 static int
349 attach_message(struct attachment *ap, FILE *fo, int dosign)
351 struct message *mp;
352 (void)dosign;
354 fprintf(fo, "\n--%s\n"
355 "Content-Type: message/rfc822\n"
356 "Content-Disposition: inline\n\n", send_boundary);
357 mp = &message[ap->a_msgno - 1];
358 touch(mp);
359 if (send(mp, fo, 0, NULL, SEND_RFC822, NULL) < 0)
360 return -1;
361 return 0;
365 * Generate the body of a MIME multipart message.
367 static int
368 make_multipart(struct header *hp, int convert, FILE *fi, FILE *fo,
369 const char *contenttype, const char *charset, int dosign)
371 struct attachment *att;
373 fputs("This is a multi-part message in MIME format.\n", fo);
374 if (fsize(fi) != 0) {
375 char *buf, c = '\n';
376 size_t sz, bufsize, count;
378 fprintf(fo, "\n--%s\n", send_boundary);
379 fprintf(fo, "Content-Type: %s", contenttype);
380 if (charset)
381 fprintf(fo, "; charset=%s", charset);
382 fprintf(fo, "\nContent-Transfer-Encoding: %s\n"
383 "Content-Disposition: inline\n\n",
384 getencoding(convert));
385 buf = smalloc(bufsize = INFIX_BUF);
386 if (convert == CONV_TOQP
387 #ifdef HAVE_ICONV
388 || iconvd != (iconv_t)-1
389 #endif /* HAVE_ICONV */
391 fflush(fi);
392 count = fsize(fi);
394 for (;;) {
395 if (convert == CONV_TOQP
396 #ifdef HAVE_ICONV
397 || iconvd != (iconv_t)-1
398 #endif /* HAVE_ICONV */
400 if (fgetline(&buf, &bufsize, &count, &sz, fi, 0)
401 == NULL)
402 break;
403 } else {
404 sz = fread(buf, sizeof *buf, bufsize, fi);
405 if (sz == 0)
406 break;
408 c = buf[sz - 1];
409 if (mime_write(buf, sz, fo, convert,
410 TD_ICONV, NULL, (size_t)0,
411 NULL, NULL) == 0) {
412 free(buf);
413 return -1;
416 free(buf);
417 if (ferror(fi))
418 return -1;
419 if (c != '\n')
420 putc('\n', fo);
421 if (charset != NULL)
422 put_signature(fo, convert);
424 for (att = hp->h_attach; att != NULL; att = att->a_flink) {
425 if (att->a_msgno) {
426 if (attach_message(att, fo, dosign) != 0)
427 return -1;
428 } else {
429 if (attach_file(att, fo, dosign) != 0)
430 return -1;
433 /* the final boundary with two attached dashes */
434 fprintf(fo, "\n--%s--\n", send_boundary);
435 return 0;
439 * Prepend a header in front of the collected stuff
440 * and return the new file.
442 static FILE *
443 infix(struct header *hp, FILE *fi, int dosign)
445 FILE *nfo, *nfi;
446 char *tempMail;
447 #ifdef HAVE_ICONV
448 char *tcs, *convhdr = NULL;
449 #endif
450 enum mimeclean isclean;
451 enum conversion convert;
452 char *charset = NULL, *contenttype = NULL;
453 int lastc = EOF;
455 if ((nfo = Ftemp(&tempMail, "Rs", "w", 0600, 1)) == NULL) {
456 perror(catgets(catd, CATSET, 178, "temporary mail file"));
457 return(NULL);
459 if ((nfi = Fopen(tempMail, "r")) == NULL) {
460 perror(tempMail);
461 Fclose(nfo);
462 return(NULL);
464 rm(tempMail);
465 Ftfree(&tempMail);
466 convert = get_mime_convert(fi, &contenttype, &charset,
467 &isclean, dosign);
468 #ifdef HAVE_ICONV
469 tcs = gettcharset();
470 if ((convhdr = need_hdrconv(hp, GTO|GSUBJECT|GCC|GBCC|GIDENT)) != 0) {
471 if (iconvd != (iconv_t)-1)
472 iconv_close(iconvd);
473 if ((iconvd = iconv_open_ft(convhdr, tcs)) == (iconv_t)-1
474 && errno != 0) {
475 if (errno == EINVAL)
476 fprintf(stderr, catgets(catd, CATSET, 179,
477 "Cannot convert from %s to %s\n"), tcs, convhdr);
478 else
479 perror("iconv_open");
480 Fclose(nfo);
481 return NULL;
484 #endif /* HAVE_ICONV */
485 if (puthead(hp, nfo,
486 GTO|GSUBJECT|GCC|GBCC|GNL|GCOMMA|GUA|GMIME
487 |GMSGID|GIDENT|GREF|GDATE,
488 SEND_MBOX, convert, contenttype, charset)) {
489 Fclose(nfo);
490 Fclose(nfi);
491 #ifdef HAVE_ICONV
492 if (iconvd != (iconv_t)-1) {
493 iconv_close(iconvd);
494 iconvd = (iconv_t)-1;
496 #endif
497 return NULL;
499 #ifdef HAVE_ICONV
500 if (convhdr && iconvd != (iconv_t)-1) {
501 iconv_close(iconvd);
502 iconvd = (iconv_t)-1;
504 if ((isclean & (MIME_HASNUL|MIME_CTRLCHAR)) == 0 &&
505 ascncasecmp(contenttype, "text/", 5) == 0 &&
506 isclean & MIME_HIGHBIT &&
507 charset != NULL) {
508 if (iconvd != (iconv_t)-1)
509 iconv_close(iconvd);
510 if ((iconvd = iconv_open_ft(charset, tcs)) == (iconv_t)-1
511 && errno != 0) {
512 if (errno == EINVAL)
513 fprintf(stderr, catgets(catd, CATSET, 179,
514 "Cannot convert from %s to %s\n"), tcs, charset);
515 else
516 perror("iconv_open");
517 Fclose(nfo);
518 return NULL;
521 #endif
522 if (hp->h_attach != NULL) {
523 if (make_multipart(hp, convert, fi, nfo,
524 contenttype, charset, dosign) != 0) {
525 Fclose(nfo);
526 Fclose(nfi);
527 #ifdef HAVE_ICONV
528 if (iconvd != (iconv_t)-1) {
529 iconv_close(iconvd);
530 iconvd = (iconv_t)-1;
532 #endif
533 return NULL;
535 } else {
536 size_t sz, bufsize, count;
537 char *buf;
539 if (convert == CONV_TOQP
540 #ifdef HAVE_ICONV
541 || iconvd != (iconv_t)-1
542 #endif /* HAVE_ICONV */
544 fflush(fi);
545 count = fsize(fi);
547 buf = smalloc(bufsize = INFIX_BUF);
548 for (;;) {
549 if (convert == CONV_TOQP
550 #ifdef HAVE_ICONV
551 || iconvd != (iconv_t)-1
552 #endif /* HAVE_ICONV */
554 if (fgetline(&buf, &bufsize, &count, &sz, fi, 0)
555 == NULL)
556 break;
557 } else {
558 sz = fread(buf, sizeof *buf, bufsize, fi);
559 if (sz == 0)
560 break;
562 lastc = buf[sz - 1];
563 if (mime_write(buf, sz, nfo, convert,
564 TD_ICONV, NULL, (size_t)0,
565 NULL, NULL) == 0) {
566 Fclose(nfo);
567 Fclose(nfi);
568 #ifdef HAVE_ICONV
569 if (iconvd != (iconv_t)-1) {
570 iconv_close(iconvd);
571 iconvd = (iconv_t)-1;
573 #endif
574 free(buf);
575 return NULL;
578 if (convert == CONV_TOQP && lastc != '\n')
579 fwrite("=\n", 1, 2, nfo);
580 free(buf);
581 if (ferror(fi)) {
582 Fclose(nfo);
583 Fclose(nfi);
584 #ifdef HAVE_ICONV
585 if (iconvd != (iconv_t)-1) {
586 iconv_close(iconvd);
587 iconvd = (iconv_t)-1;
589 #endif
590 return NULL;
592 if (charset != NULL)
593 put_signature(nfo, convert);
595 #ifdef HAVE_ICONV
596 if (iconvd != (iconv_t)-1) {
597 iconv_close(iconvd);
598 iconvd = (iconv_t)-1;
600 #endif
601 fflush(nfo);
602 if (ferror(nfo)) {
603 perror(catgets(catd, CATSET, 180, "temporary mail file"));
604 Fclose(nfo);
605 Fclose(nfi);
606 return NULL;
608 Fclose(nfo);
609 Fclose(fi);
610 fflush(nfi);
611 rewind(nfi);
612 return(nfi);
616 * Save the outgoing mail on the passed file.
619 /*ARGSUSED*/
620 static int
621 savemail(char *name, FILE *fi)
623 FILE *fo;
624 char *buf;
625 size_t bufsize, buflen, count;
626 char *p;
627 time_t now;
628 int prependnl = 0;
629 int error = 0;
631 buf = smalloc(bufsize = LINESIZE);
632 time(&now);
633 if ((fo = Zopen(name, "a+", NULL)) == NULL) {
634 if ((fo = Zopen(name, "wx", NULL)) == NULL) {
635 perror(name);
636 free(buf);
637 return (-1);
639 } else {
640 if (fseek(fo, -2L, SEEK_END) == 0) {
641 switch (fread(buf, sizeof *buf, 2, fo)) {
642 case 2:
643 if (buf[1] != '\n') {
644 prependnl = 1;
645 break;
647 /*FALLTHRU*/
648 case 1:
649 if (buf[0] != '\n')
650 prependnl = 1;
651 break;
652 default:
653 if (ferror(fo)) {
654 perror(name);
655 free(buf);
656 return -1;
659 fflush(fo);
660 if (prependnl) {
661 putc('\n', fo);
662 fflush(fo);
666 fprintf(fo, "From %s %s", myname, ctime(&now));
667 buflen = 0;
668 fflush(fi);
669 rewind(fi);
670 count = fsize(fi);
671 while (fgetline(&buf, &bufsize, &count, &buflen, fi, 0) != NULL) {
672 if (*buf == '>') {
673 p = buf + 1;
674 while (*p == '>')
675 p++;
676 if (strncmp(p, "From ", 5) == 0)
677 /* we got a masked From line */
678 putc('>', fo);
679 } else if (strncmp(buf, "From ", 5) == 0)
680 putc('>', fo);
681 fwrite(buf, sizeof *buf, buflen, fo);
683 if (buflen && *(buf + buflen - 1) != '\n')
684 putc('\n', fo);
685 putc('\n', fo);
686 fflush(fo);
687 if (ferror(fo)) {
688 perror(name);
689 error = -1;
691 if (Fclose(fo) != 0)
692 error = -1;
693 fflush(fi);
694 rewind(fi);
695 free(buf);
696 return error;
700 * Interface between the argument list and the mail1 routine
701 * which does all the dirty work.
703 int
704 mail(struct name *to, struct name *cc, struct name *bcc,
705 struct name *smopts, char *subject, struct attachment *attach,
706 char *quotefile, int recipient_record, int tflag, int Eflag)
708 struct header head;
709 struct str in, out;
711 memset(&head, 0, sizeof head);
712 /* The given subject may be in RFC1522 format. */
713 if (subject != NULL) {
714 in.s = subject;
715 in.l = strlen(subject);
716 mime_fromhdr(&in, &out, TD_ISPR | TD_ICONV);
717 head.h_subject = out.s;
719 if (tflag == 0) {
720 head.h_to = to;
721 head.h_cc = cc;
722 head.h_bcc = bcc;
724 head.h_attach = attach;
725 head.h_smopts = smopts;
726 mail1(&head, 0, NULL, quotefile, recipient_record, 0, tflag, Eflag);
727 if (subject != NULL)
728 free(out.s);
729 return(0);
733 * Send mail to a bunch of user names. The interface is through
734 * the mail routine below.
736 static int
737 sendmail_internal(void *v, int recipient_record)
739 int Eflag;
740 char *str = v;
741 struct header head;
743 memset(&head, 0, sizeof head);
744 head.h_to = extract(str, GTO|GFULL);
745 Eflag = value("skipemptybody") != NULL;
746 mail1(&head, 0, NULL, NULL, recipient_record, 0, 0, Eflag);
747 return(0);
750 int
751 sendmail(void *v)
753 return sendmail_internal(v, 0);
756 int
757 Sendmail(void *v)
759 return sendmail_internal(v, 1);
762 static enum okay
763 transfer(struct name *to, struct name *mailargs, FILE *input, struct header *hp)
765 char o[LINESIZE], *cp;
766 struct name *np, *nt;
767 int cnt = 0;
768 FILE *ef;
769 enum okay ok = OKAY;
771 np = to;
772 while (np) {
773 snprintf(o, sizeof o, "smime-encrypt-%s", np->n_name);
774 if ((cp = value(o)) != NULL) {
775 if ((ef = smime_encrypt(input, cp, np->n_name)) != 0) {
776 nt = ndup(np, np->n_type & ~(GFULL|GSKIN));
777 if (start_mta(nt, mailargs, ef, hp) != OKAY)
778 ok = STOP;
779 Fclose(ef);
780 } else {
781 fprintf(stderr, "Message not sent to <%s>\n",
782 np->n_name);
783 senderr++;
785 rewind(input);
786 if (np->n_flink)
787 np->n_flink->n_blink = np->n_blink;
788 if (np->n_blink)
789 np->n_blink->n_flink = np->n_flink;
790 if (np == to)
791 to = np->n_flink;
792 np = np->n_flink;
793 } else {
794 cnt++;
795 np = np->n_flink;
798 if (cnt) {
799 if (value("smime-force-encryption") ||
800 start_mta(to, mailargs, input, hp) != OKAY)
801 ok = STOP;
803 return ok;
807 * Start the Mail Transfer Agent
808 * mailing to namelist and stdin redirected to input.
810 static enum okay
811 start_mta(struct name *to, struct name *mailargs, FILE *input,
812 struct header *hp)
814 char **args = NULL, **t;
815 pid_t pid;
816 sigset_t nset;
817 char *cp, *smtp;
818 char *user = NULL, *password = NULL, *skinned = NULL;
819 enum okay ok = STOP;
820 #ifdef USE_SMTP
821 struct termios otio;
822 int reset_tio;
823 #endif
825 if ((smtp = value("smtp")) == NULL) {
826 args = unpack(cat(mailargs, to));
827 if (debug || value("debug")) {
828 printf(catgets(catd, CATSET, 181,
829 "Sendmail arguments:"));
830 for (t = args; *t != NULL; t++)
831 printf(" \"%s\"", *t);
832 printf("\n");
833 return OKAY;
836 #ifdef USE_SMTP
837 if (smtp != NULL) {
838 skinned = skin(myorigin(hp));
839 if ((user = smtp_auth_var("-user", skinned)) != NULL &&
840 (password = smtp_auth_var("-password",
841 skinned)) == NULL)
842 password = getpassword(&otio, &reset_tio, NULL);
844 #endif
846 * Fork, set up the temporary mail file as standard
847 * input for "mail", and exec with the user list we generated
848 * far above.
850 if ((pid = fork()) == -1) {
851 perror("fork");
852 savedeadletter(input);
853 senderr++;
854 return STOP;
856 if (pid == 0) {
857 sigemptyset(&nset);
858 sigaddset(&nset, SIGHUP);
859 sigaddset(&nset, SIGINT);
860 sigaddset(&nset, SIGQUIT);
861 sigaddset(&nset, SIGTSTP);
862 sigaddset(&nset, SIGTTIN);
863 sigaddset(&nset, SIGTTOU);
864 freopen("/dev/null", "r", stdin);
865 if (smtp != NULL) {
866 prepare_child(&nset, 0, 1);
867 if (smtp_mta(smtp, to, input, hp,
868 user, password, skinned) == 0)
869 _exit(0);
870 } else {
871 prepare_child(&nset, fileno(input), -1);
872 if ((cp = value("sendmail")) != NULL)
873 cp = expand(cp);
874 else
875 cp = SENDMAIL;
876 execv(cp, args);
877 perror(cp);
879 savedeadletter(input);
880 fputs(catgets(catd, CATSET, 182,
881 ". . . message not sent.\n"), stderr);
882 _exit(1);
884 if (value("verbose") != NULL || value("sendwait") || debug
885 || value("debug")) {
886 if (wait_child(pid) == 0)
887 ok = OKAY;
888 else
889 senderr++;
890 } else {
891 ok = OKAY;
892 free_child(pid);
894 return ok;
898 * Record outgoing mail if instructed to do so.
900 static enum okay
901 mightrecord(FILE *fp, struct name *to, int recipient_record)
903 char *cp, *cq, *ep;
905 if (recipient_record) {
906 cq = skinned_name(to);
907 cp = salloc(strlen(cq) + 1);
908 strcpy(cp, cq);
909 for (cq = cp; *cq && *cq != '@'; cq++);
910 *cq = '\0';
911 } else
912 cp = value("record");
913 if (cp != NULL) {
914 ep = expand(cp);
915 if (value("outfolder") && *ep != '/' && *ep != '+' &&
916 which_protocol(ep) == PROTO_FILE) {
917 cq = salloc(strlen(cp) + 2);
918 cq[0] = '+';
919 strcpy(&cq[1], cp);
920 cp = cq;
921 ep = expand(cp);
923 if (savemail(ep, fp) != 0) {
924 fprintf(stderr,
925 "Error while saving message to %s - "
926 "message not sent\n", ep);
927 rewind(fp);
928 exit_status |= 1;
929 savedeadletter(fp);
930 return STOP;
933 return OKAY;
937 * Mail a message on standard input to the people indicated
938 * in the passed header. (Internal interface).
940 enum okay
941 mail1(struct header *hp, int printheaders, struct message *quote,
942 char *quotefile, int recipient_record, int doprefix, int tflag,
943 int Eflag)
945 struct name *to;
946 FILE *mtf, *nmtf;
947 enum okay ok = STOP;
948 int dosign = -1;
949 char *charsets, *ncs = NULL, *cp;
951 #ifdef notdef
952 if ((hp->h_to = checkaddrs(hp->h_to)) == NULL) {
953 senderr++;
954 return STOP;
956 #endif
957 if ((cp = value("autocc")) != NULL && *cp)
958 hp->h_cc = cat(hp->h_cc, checkaddrs(sextract(cp, GCC|GFULL)));
959 if ((cp = value("autobcc")) != NULL && *cp)
960 hp->h_bcc = cat(hp->h_bcc,
961 checkaddrs(sextract(cp, GBCC|GFULL)));
963 * Collect user's mail from standard input.
964 * Get the result as mtf.
966 if ((mtf = collect(hp, printheaders, quote, quotefile, doprefix,
967 tflag)) == NULL)
968 return STOP;
969 if (value("interactive") != NULL) {
970 if (((value("bsdcompat") || value("askatend"))
971 && (value("askcc") != NULL ||
972 value("askbcc") != NULL)) ||
973 value("askattach") != NULL ||
974 value("asksign") != NULL) {
975 if (value("askcc") != NULL)
976 grabh(hp, GCC, 1);
977 if (value("askbcc") != NULL)
978 grabh(hp, GBCC, 1);
979 if (value("askattach") != NULL)
980 hp->h_attach = edit_attachments(hp->h_attach);
981 if (value("asksign") != NULL)
982 dosign = yorn("Sign this message (y/n)? ");
983 } else {
984 printf(catgets(catd, CATSET, 183, "EOT\n"));
985 fflush(stdout);
988 if (fsize(mtf) == 0) {
989 if (Eflag)
990 goto out;
991 if (hp->h_subject == NULL)
992 printf(catgets(catd, CATSET, 184,
993 "No message, no subject; hope that's ok\n"));
994 else if (value("bsdcompat") || value("bsdmsgs"))
995 printf(catgets(catd, CATSET, 185,
996 "Null message body; hope that's ok\n"));
998 if (dosign < 0) {
999 if (value("smime-sign") != NULL)
1000 dosign = 1;
1001 else
1002 dosign = 0;
1005 * Now, take the user names from the combined
1006 * to and cc lists and do all the alias
1007 * processing.
1009 senderr = 0;
1010 if ((to = usermap(cat(hp->h_bcc, cat(hp->h_to, hp->h_cc)))) == NULL) {
1011 printf(catgets(catd, CATSET, 186, "No recipients specified\n"));
1012 senderr++;
1014 to = fixhead(hp, to);
1015 if (hp->h_charset) {
1016 wantcharset = hp->h_charset;
1017 goto try;
1019 hloop: wantcharset = NULL;
1020 if ((charsets = value("sendcharsets")) != NULL) {
1021 wantcharset = savestr(charsets);
1022 loop: if ((ncs = strchr(wantcharset, ',')) != NULL)
1023 *ncs++ = '\0';
1025 try: if ((nmtf = infix(hp, mtf, dosign)) == NULL) {
1026 if (hp->h_charset && (errno == EILSEQ || errno == EINVAL)) {
1027 rewind(mtf);
1028 hp->h_charset = NULL;
1029 goto hloop;
1031 if (errno == EILSEQ || errno == EINVAL) {
1032 if (ncs && *ncs) {
1033 rewind(mtf);
1034 wantcharset = ncs;
1035 goto loop;
1037 if (wantcharset && value("interactive") == NULL) {
1038 if (wantcharset == (char *)-1)
1039 wantcharset = NULL;
1040 else {
1041 rewind(mtf);
1042 wantcharset = (char *)-1;
1043 goto try;
1047 /* fprintf(stderr, ". . . message lost, sorry.\n"); */
1048 perror("");
1049 fail: senderr++;
1050 rewind(mtf);
1051 savedeadletter(mtf);
1052 fputs(catgets(catd, CATSET, 187,
1053 ". . . message not sent.\n"), stderr);
1054 return STOP;
1056 mtf = nmtf;
1057 if (dosign) {
1058 if ((nmtf = smime_sign(mtf, hp)) == NULL)
1059 goto fail;
1060 Fclose(mtf);
1061 mtf = nmtf;
1064 * Look through the recipient list for names with /'s
1065 * in them which we write to as files directly.
1067 to = outof(to, mtf, hp);
1068 if (senderr)
1069 savedeadletter(mtf);
1070 to = elide(to);
1071 if (count(to) == 0) {
1072 if (senderr == 0)
1073 ok = OKAY;
1074 goto out;
1076 if (mightrecord(mtf, to, recipient_record) != OKAY)
1077 goto out;
1078 ok = transfer(to, hp->h_smopts, mtf, hp);
1079 out:
1080 Fclose(mtf);
1081 return ok;
1085 * Create a Message-Id: header field.
1086 * Use either the host name or the from address.
1088 static void
1089 message_id(FILE *fo, struct header *hp)
1091 time_t now;
1092 struct tm *tmp;
1093 char *h;
1094 size_t rl;
1096 time(&now);
1097 tmp = gmtime(&now);
1098 if ((h = value("hostname")) != NULL)
1099 rl = 24;
1100 else if ((h = skin(myorigin(hp))) != NULL && strchr(h, '@') != NULL)
1101 rl = 16;
1102 else
1103 /* Delivery seems to dependent on a MTA -- it's up to it */
1104 return;
1106 fprintf(fo, "Message-ID: <%04d%02d%02d%02d%02d%02d.%s%c%s>\n",
1107 tmp->tm_year + 1900, tmp->tm_mon + 1, tmp->tm_mday,
1108 tmp->tm_hour, tmp->tm_min, tmp->tm_sec,
1109 getrandstring(rl), (rl == 16 ? '%' : '@'), h);
1112 static const char *weekday_names[] = {
1113 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
1116 const char *month_names[] = {
1117 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
1118 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", NULL
1122 * Create a Date: header field.
1123 * We compare the localtime() and gmtime() results to get the timezone,
1124 * because numeric timezones are easier to read and because $TZ is
1125 * not set on most GNU systems.
1128 mkdate(FILE *fo, const char *field)
1130 time_t t;
1131 struct tm *tmptr;
1132 int tzdiff, tzdiff_hour, tzdiff_min;
1134 time(&t);
1135 tzdiff = t - mktime(gmtime(&t));
1136 tzdiff_hour = (int)(tzdiff / 60);
1137 tzdiff_min = tzdiff_hour % 60;
1138 tzdiff_hour /= 60;
1139 tmptr = localtime(&t);
1140 if (tmptr->tm_isdst > 0)
1141 tzdiff_hour++;
1142 return fprintf(fo, "%s: %s, %02d %s %04d %02d:%02d:%02d %+05d\n",
1143 field,
1144 weekday_names[tmptr->tm_wday],
1145 tmptr->tm_mday, month_names[tmptr->tm_mon],
1146 tmptr->tm_year + 1900, tmptr->tm_hour,
1147 tmptr->tm_min, tmptr->tm_sec,
1148 tzdiff_hour * 100 + tzdiff_min);
1151 static enum okay
1152 putname(char *line, enum gfield w, enum sendaction action, int *gotcha,
1153 char *prefix, FILE *fo, struct name **xp)
1155 struct name *np;
1157 np = sextract(line, GEXTRA|GFULL);
1158 if (xp)
1159 *xp = np;
1160 if (np == NULL)
1161 return 0;
1162 if (fmt(prefix, np, fo, w&(GCOMMA|GFILES), 0, action != SEND_TODISP))
1163 return 1;
1164 if (gotcha)
1165 (*gotcha)++;
1166 return 0;
1169 #define FMT_CC_AND_BCC { \
1170 if (hp->h_cc != NULL && w & GCC) { \
1171 if (fmt("Cc:", hp->h_cc, fo, \
1172 w&(GCOMMA|GFILES), 0, \
1173 action!=SEND_TODISP)) \
1174 return 1; \
1175 gotcha++; \
1177 if (hp->h_bcc != NULL && w & GBCC) { \
1178 if (fmt("Bcc:", hp->h_bcc, fo, \
1179 w&(GCOMMA|GFILES), 0, \
1180 action!=SEND_TODISP)) \
1181 return 1; \
1182 gotcha++; \
1186 * Dump the to, subject, cc header on the
1187 * passed file buffer.
1190 puthead(struct header *hp, FILE *fo, enum gfield w,
1191 enum sendaction action, enum conversion convert,
1192 char *contenttype, char *charset)
1194 int gotcha;
1195 char *addr/*, *cp*/;
1196 int stealthmua;
1197 struct name *np, *fromfield = NULL, *senderfield = NULL;
1200 if ((addr = value("stealthmua")) != NULL) {
1201 stealthmua = (strcmp(addr, "noagent") == 0) ? -1 : 1;
1202 } else
1203 stealthmua = 0;
1204 gotcha = 0;
1205 if (w & GDATE) {
1206 mkdate(fo, "Date"), gotcha++;
1208 if (w & GIDENT) {
1209 if (hp->h_from != NULL) {
1210 if (fmt("From:", hp->h_from, fo, w&(GCOMMA|GFILES), 0,
1211 action!=SEND_TODISP))
1212 return 1;
1213 gotcha++;
1214 fromfield = hp->h_from;
1215 } else if ((addr = myaddrs(hp)) != NULL)
1216 if (putname(addr, w, action, &gotcha, "From:", fo,
1217 &fromfield))
1218 return 1;
1219 if (((addr = hp->h_organization) != NULL ||
1220 (addr = value("ORGANIZATION")) != NULL)
1221 && strlen(addr) > 0) {
1222 fwrite("Organization: ", sizeof (char), 14, fo);
1223 if (mime_write(addr, strlen(addr), fo,
1224 action == SEND_TODISP ?
1225 CONV_NONE:CONV_TOHDR,
1226 action == SEND_TODISP ?
1227 TD_ISPR|TD_ICONV:TD_ICONV,
1228 NULL, (size_t)0,
1229 NULL, NULL) == 0)
1230 return 1;
1231 gotcha++;
1232 putc('\n', fo);
1234 if (hp->h_replyto != NULL) {
1235 if (fmt("Reply-To:", hp->h_replyto, fo,
1236 w&(GCOMMA|GFILES), 0,
1237 action!=SEND_TODISP))
1238 return 1;
1239 gotcha++;
1240 } else if ((addr = value("replyto")) != NULL)
1241 if (putname(addr, w, action, &gotcha, "Reply-To:", fo,
1242 NULL))
1243 return 1;
1244 if (hp->h_sender != NULL) {
1245 if (fmt("Sender:", hp->h_sender, fo,
1246 w&(GCOMMA|GFILES), 0,
1247 action!=SEND_TODISP))
1248 return 1;
1249 gotcha++;
1250 senderfield = hp->h_sender;
1251 } else if ((addr = value("sender")) != NULL)
1252 if (putname(addr, w, action, &gotcha, "Sender:", fo,
1253 &senderfield))
1254 return 1;
1255 if (check_from_and_sender(fromfield, senderfield))
1256 return 1;
1258 if (hp->h_to != NULL && w & GTO) {
1259 if (fmt("To:", hp->h_to, fo, w&(GCOMMA|GFILES), 0,
1260 action!=SEND_TODISP))
1261 return 1;
1262 gotcha++;
1264 if (value("bsdcompat") == NULL && value("bsdorder") == NULL)
1265 FMT_CC_AND_BCC
1266 if (hp->h_subject != NULL && w & GSUBJECT) {
1267 fwrite("Subject: ", sizeof (char), 9, fo);
1268 if (ascncasecmp(hp->h_subject, "re: ", 4) == 0) {
1269 fwrite("Re: ", sizeof (char), 4, fo);
1270 if (strlen(hp->h_subject + 4) > 0 &&
1271 mime_write(hp->h_subject + 4,
1272 strlen(hp->h_subject + 4),
1273 fo, action == SEND_TODISP ?
1274 CONV_NONE:CONV_TOHDR,
1275 action == SEND_TODISP ?
1276 TD_ISPR|TD_ICONV:TD_ICONV,
1277 NULL, (size_t)0,
1278 NULL, NULL) == 0)
1279 return 1;
1280 } else if (*hp->h_subject) {
1281 if (mime_write(hp->h_subject,
1282 strlen(hp->h_subject),
1283 fo, action == SEND_TODISP ?
1284 CONV_NONE:CONV_TOHDR,
1285 action == SEND_TODISP ?
1286 TD_ISPR|TD_ICONV:TD_ICONV,
1287 NULL, (size_t)0,
1288 NULL, NULL) == 0)
1289 return 1;
1291 gotcha++;
1292 fwrite("\n", sizeof (char), 1, fo);
1294 if (value("bsdcompat") || value("bsdorder"))
1295 FMT_CC_AND_BCC
1296 if (w & GMSGID && stealthmua <= 0)
1297 message_id(fo, hp), gotcha++;
1298 if (hp->h_ref != NULL && w & GREF) {
1299 fmt("References:", hp->h_ref, fo, 0, 1, 0);
1300 if ((np = hp->h_ref) != NULL && np->n_name) {
1301 while (np->n_flink)
1302 np = np->n_flink;
1303 if (mime_name_invalid(np, 0) == 0) {
1304 fprintf(fo, "In-Reply-To: %s\n", np->n_name);
1305 gotcha++;
1309 if (w & GUA && stealthmua == 0)
1310 fprintf(fo, "User-Agent: %s %s\n", uagent, version), gotcha++;
1311 if (w & GMIME) {
1312 fputs("MIME-Version: 1.0\n", fo), gotcha++;
1313 if (hp->h_attach != NULL) {
1314 makeboundary();
1315 fprintf(fo, "Content-Type: multipart/mixed;\n"
1316 " boundary=\"%s\"\n", send_boundary);
1317 } else {
1318 fprintf(fo, "Content-Type: %s", contenttype);
1319 if (charset)
1320 fprintf(fo, "; charset=%s", charset);
1321 fprintf(fo, "\nContent-Transfer-Encoding: %s\n",
1322 getencoding(convert));
1325 if (gotcha && w & GNL)
1326 putc('\n', fo);
1327 return(0);
1331 * Format the given header line to not exceed 72 characters.
1333 static int
1334 fmt(char *str, struct name *np, FILE *fo, int flags, int dropinvalid,
1335 int domime)
1337 enum {
1338 m_INIT = 1<<0,
1339 m_COMMA = 1<<1,
1340 m_NOPF = 1<<2,
1341 m_CSEEN = 1<<3
1342 } m = (flags & GCOMMA) ? m_COMMA : 0;
1343 int col, len;
1345 col = strlen(str);
1346 if (col) {
1347 fwrite(str, sizeof *str, strlen(str), fo);
1348 if ((flags&GFILES) == 0 && ! value("add-file-recipients") &&
1349 ((col == 3 && (asccasecmp(str, "to:") == 0) ||
1350 asccasecmp(str, "cc:") == 0) ||
1351 (col == 4 && asccasecmp(str, "bcc:") == 0) ||
1352 (col == 10 &&
1353 asccasecmp(str, "Resent-To:") == 0)))
1354 m |= m_NOPF;
1356 for (; np != NULL; np = np->n_flink) {
1357 if ((m & m_NOPF) && is_fileorpipe_addr(np))
1358 continue;
1359 if (mime_name_invalid(np, ! dropinvalid)) {
1360 if (dropinvalid)
1361 continue;
1362 else
1363 return (1);
1365 if ((m & (m_INIT | m_COMMA)) == (m_INIT | m_COMMA)) {
1366 putc(',', fo);
1367 m |= m_CSEEN;
1368 ++col;
1370 len = strlen(np->n_fullname);
1371 ++col; /* The separating space */
1372 if ((m & m_INIT) && col > 1 && col + len > 72) {
1373 fputs("\n ", fo);
1374 col = 1;
1375 m &= ~m_CSEEN;
1376 } else
1377 putc(' ', fo);
1378 m = (m & ~m_CSEEN) | m_INIT;
1379 len = mime_write(np->n_fullname,
1380 len, fo,
1381 domime?CONV_TOHDR_A:CONV_NONE,
1382 TD_ICONV, NULL, (size_t)0,
1383 NULL, NULL);
1384 col += len;
1386 putc('\n', fo);
1387 return (0);
1391 * Rewrite a message for resending, adding the Resent-Headers.
1393 static int
1394 infix_resend(FILE *fi, FILE *fo, struct message *mp, struct name *to,
1395 int add_resent)
1397 size_t count;
1398 char *buf = NULL, *cp/*, *cp2*/;
1399 size_t c, bufsize = 0;
1400 struct name *fromfield = NULL, *senderfield = NULL;
1402 count = mp->m_size;
1404 * Write the Resent-Fields.
1406 if (add_resent) {
1407 fputs("Resent-", fo);
1408 mkdate(fo, "Date");
1409 if ((cp = myaddrs(NULL)) != NULL) {
1410 if (putname(cp, GCOMMA, SEND_MBOX, NULL,
1411 "Resent-From:", fo, &fromfield))
1412 return 1;
1414 if ((cp = value("sender")) != NULL) {
1415 if (putname(cp, GCOMMA, SEND_MBOX, NULL,
1416 "Resent-Sender:", fo, &senderfield))
1417 return 1;
1419 if (fmt("Resent-To:", to, fo, 1, 1, 0)) {
1420 if (buf)
1421 free(buf);
1422 return 1;
1424 if ((cp = value("stealthmua")) == NULL ||
1425 strcmp(cp, "noagent") == 0) {
1426 fputs("Resent-", fo);
1427 message_id(fo, NULL);
1430 if (check_from_and_sender(fromfield, senderfield))
1431 return 1;
1433 * Write the original headers.
1435 while (count > 0) {
1436 if ((cp = foldergets(&buf, &bufsize, &count, &c, fi)) == NULL)
1437 break;
1438 if (ascncasecmp("status: ", buf, 8) != 0
1439 && strncmp("From ", buf, 5) != 0) {
1440 fwrite(buf, sizeof *buf, c, fo);
1442 if (count > 0 && *buf == '\n')
1443 break;
1446 * Write the message body.
1448 while (count > 0) {
1449 if (foldergets(&buf, &bufsize, &count, &c, fi) == NULL)
1450 break;
1451 if (count == 0 && *buf == '\n')
1452 break;
1453 fwrite(buf, sizeof *buf, c, fo);
1455 if (buf)
1456 free(buf);
1457 if (ferror(fo)) {
1458 perror(catgets(catd, CATSET, 188, "temporary mail file"));
1459 return 1;
1461 return 0;
1464 enum okay
1465 resend_msg(struct message *mp, struct name *to, int add_resent)
1467 FILE *ibuf, *nfo, *nfi;
1468 char *tempMail;
1469 struct header head;
1470 enum okay ok = STOP;
1472 memset(&head, 0, sizeof head);
1473 if ((to = checkaddrs(to)) == NULL) {
1474 senderr++;
1475 return STOP;
1477 if ((nfo = Ftemp(&tempMail, "Rs", "w", 0600, 1)) == NULL) {
1478 senderr++;
1479 perror(catgets(catd, CATSET, 189, "temporary mail file"));
1480 return STOP;
1482 if ((nfi = Fopen(tempMail, "r")) == NULL) {
1483 senderr++;
1484 perror(tempMail);
1485 return STOP;
1487 rm(tempMail);
1488 Ftfree(&tempMail);
1489 if ((ibuf = setinput(&mb, mp, NEED_BODY)) == NULL)
1490 return STOP;
1491 head.h_to = to;
1492 to = fixhead(&head, to);
1493 if (infix_resend(ibuf, nfo, mp, head.h_to, add_resent) != 0) {
1494 senderr++;
1495 rewind(nfo);
1496 savedeadletter(nfi);
1497 fputs(catgets(catd, CATSET, 190,
1498 ". . . message not sent.\n"), stderr);
1499 Fclose(nfo);
1500 Fclose(nfi);
1501 return STOP;
1503 fflush(nfo);
1504 rewind(nfo);
1505 Fclose(nfo);
1506 to = outof(to, nfi, &head);
1507 if (senderr)
1508 savedeadletter(nfi);
1509 to = elide(to);
1510 if (count(to) != 0) {
1511 if (value("record-resent") == NULL ||
1512 mightrecord(nfi, to, 0) == OKAY)
1513 ok = transfer(to, head.h_smopts, nfi, NULL);
1514 } else if (senderr == 0)
1515 ok = OKAY;
1516 Fclose(nfi);
1517 return ok;