1 /*
2  * Heirloom mailx - a mail user agent derived from Berkeley Mail.
3  *
4  * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5  */
6 /*
7  * Copyright (c) 1980, 1993
8  *	The Regents of the University of California.  All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. All advertising materials mentioning features or use of this software
19  *    must display the following acknowledgement:
20  *	This product includes software developed by the University of
21  *	California, Berkeley and its contributors.
22  * 4. Neither the name of the University nor the names of its contributors
23  *    may be used to endorse or promote products derived from this software
24  *    without specific prior written permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36  * SUCH DAMAGE.
37  */
38 
39 #ifndef lint
40 #ifdef	DOSCCS
41 static char sccsid[] = "@(#)sendout.c	2.99 (gritter) 7/4/08";
42 #endif
43 #endif /* not lint */
44 
45 #include "rcv.h"
46 #include "extern.h"
47 #include <errno.h>
48 #include <sys/stat.h>
49 #include <fcntl.h>
50 #include <unistd.h>
51 #include <time.h>
52 #include "md5.h"
53 
54 /*
55  * Mail -- a mail program
56  *
57  * Mail to others.
58  */
59 
60 static char	*send_boundary;
61 
62 static char *getencoding(enum conversion convert);
63 static struct name *fixhead(struct header *hp, struct name *tolist);
64 static int put_signature(FILE *fo, int convert);
65 static int attach_file1(struct attachment *ap, FILE *fo, int dosign);
66 static int attach_file(struct attachment *ap, FILE *fo, int dosign);
67 static int attach_message(struct attachment *ap, FILE *fo, int dosign);
68 static int make_multipart(struct header *hp, int convert, FILE *fi, FILE *fo,
69 		const char *contenttype, const char *charset, int dosign);
70 static FILE *infix(struct header *hp, FILE *fi, int dosign);
71 static int savemail(char *name, FILE *fi);
72 static int sendmail_internal(void *v, int recipient_record);
73 static enum okay transfer(struct name *to, struct name *mailargs, FILE *input,
74 		struct header *hp);
75 static enum okay start_mta(struct name *to, struct name *mailargs, FILE *input,
76 		struct header *hp);
77 static void message_id(FILE *fo, struct header *hp);
78 static int fmt(char *str, struct name *np, FILE *fo, int comma,
79 		int dropinvalid, int domime);
80 static int infix_resend(FILE *fi, FILE *fo, struct message *mp,
81 		struct name *to, int add_resent);
82 
83 /*
84  * Generate a boundary for MIME multipart messages.
85  */
86 char *
makeboundary(void)87 makeboundary(void)
88 {
89 	static char bound[70];
90 	time_t	now;
91 
92 	time(&now);
93 	snprintf(bound, sizeof bound, "=_%lx.%s", (long)now, getrandstring(48));
94 	send_boundary = bound;
95 	return send_boundary;
96 }
97 
98 /*
99  * Get an encoding flag based on the given string.
100  */
101 static char *
getencoding(enum conversion convert)102 getencoding(enum conversion convert)
103 {
104 	switch (convert) {
105 	case CONV_7BIT:
106 		return "7bit";
107 	case CONV_8BIT:
108 		return "8bit";
109 	case CONV_TOQP:
110 		return "quoted-printable";
111 	case CONV_TOB64:
112 		return "base64";
113 	default:
114 		break;
115 	}
116 	/*NOTREACHED*/
117 	return NULL;
118 }
119 
120 /*
121  * Fix the header by glopping all of the expanded names from
122  * the distribution list into the appropriate fields.
123  */
124 static struct name *
fixhead(struct header * hp,struct name * tolist)125 fixhead(struct header *hp, struct name *tolist)
126 {
127 	struct name *np;
128 
129 	hp->h_to = NULL;
130 	hp->h_cc = NULL;
131 	hp->h_bcc = NULL;
132 	for (np = tolist; np != NULL; np = np->n_flink)
133 		if ((np->n_type & GMASK) == GTO)
134 			hp->h_to =
135 				cat(hp->h_to, nalloc(np->n_fullname,
136 							np->n_type|GFULL));
137 		else if ((np->n_type & GMASK) == GCC)
138 			hp->h_cc =
139 				cat(hp->h_cc, nalloc(np->n_fullname,
140 							np->n_type|GFULL));
141 		else if ((np->n_type & GMASK) == GBCC)
142 			hp->h_bcc =
143 				cat(hp->h_bcc, nalloc(np->n_fullname,
144 							np->n_type|GFULL));
145 	return tolist;
146 }
147 
148 
149 /*
150  * Do not change, you get incorrect base64 encodings else!
151  */
152 #define	INFIX_BUF	972
153 
154 /*
155  * Put the signature file at fo.
156  */
157 static int
put_signature(FILE * fo,int convert)158 put_signature(FILE *fo, int convert)
159 {
160 	char *sig, buf[INFIX_BUF], c = '\n';
161 	FILE *fsig;
162 	size_t sz;
163 
164 	sig = value("signature");
165 	if (sig == NULL || *sig == '\0')
166 		return 0;
167 	else
168 		sig = expand(sig);
169 	if ((fsig = Fopen(sig, "r")) == NULL) {
170 		perror(sig);
171 		return -1;
172 	}
173 	while ((sz = fread(buf, sizeof *buf, INFIX_BUF, fsig)) != 0) {
174 		c = buf[sz - 1];
175 		if (mime_write(buf, sz, fo, convert, TD_NONE,
176 					NULL, (size_t)0, NULL, NULL)
177 				== 0) {
178 			perror(sig);
179 			Fclose(fsig);
180 			return -1;
181 		}
182 	}
183 	if (ferror(fsig)) {
184 		perror(sig);
185 		Fclose(fsig);
186 		return -1;
187 	}
188 	Fclose(fsig);
189 	if (c != '\n')
190 		putc('\n', fo);
191 	return 0;
192 }
193 
194 /*
195  * Write an attachment to the file buffer, converting to MIME.
196  */
197 static int
attach_file1(struct attachment * ap,FILE * fo,int dosign)198 attach_file1(struct attachment *ap, FILE *fo, int dosign)
199 {
200 	FILE *fi;
201 	char *charset = NULL, *contenttype = NULL, *basename;
202 	enum conversion convert = CONV_TOB64;
203 	int err = 0;
204 	enum mimeclean isclean;
205 	size_t sz;
206 	char *buf;
207 	size_t bufsize, count;
208 	int	lastc = EOF;
209 #ifdef	HAVE_ICONV
210 	char	*tcs;
211 #endif
212 
213 	if ((fi = Fopen(ap->a_name, "r")) == NULL) {
214 		perror(ap->a_name);
215 		return -1;
216 	}
217 	if ((basename = strrchr(ap->a_name, '/')) == NULL)
218 		basename = ap->a_name;
219 	else
220 		basename++;
221 	if (ap->a_content_type)
222 		contenttype = ap->a_content_type;
223 	else
224 		contenttype = mime_filecontent(basename);
225 	if (ap->a_charset)
226 		charset = ap->a_charset;
227 	convert = get_mime_convert(fi, &contenttype, &charset, &isclean,
228 			dosign);
229 	fprintf(fo,
230 		"\n--%s\n"
231 		"Content-Type: %s",
232 		send_boundary, contenttype);
233 	if (charset == NULL)
234 		putc('\n', fo);
235 	else
236 		fprintf(fo, ";\n charset=%s\n", charset);
237 	if (ap->a_content_disposition == NULL)
238 		ap->a_content_disposition = "attachment";
239 	fprintf(fo, "Content-Transfer-Encoding: %s\n"
240 		"Content-Disposition: %s;\n"
241 		" filename=\"",
242 		getencoding(convert),
243 		ap->a_content_disposition);
244 	mime_write(basename, strlen(basename), fo,
245 			CONV_TOHDR, TD_NONE, NULL, (size_t)0, NULL, NULL);
246 	fwrite("\"\n", sizeof (char), 2, fo);
247 	if (ap->a_content_id)
248 		fprintf(fo, "Content-ID: %s\n", ap->a_content_id);
249 	if (ap->a_content_description)
250 		fprintf(fo, "Content-Description: %s\n",
251 				ap->a_content_description);
252 	putc('\n', fo);
253 #ifdef	HAVE_ICONV
254 	if (iconvd != (iconv_t)-1) {
255 		iconv_close(iconvd);
256 		iconvd = (iconv_t)-1;
257 	}
258 	tcs = gettcharset();
259 	if ((isclean & (MIME_HASNUL|MIME_CTRLCHAR)) == 0 &&
260 			ascncasecmp(contenttype, "text/", 5) == 0 &&
261 			isclean & MIME_HIGHBIT &&
262 			charset != NULL) {
263 		if ((iconvd = iconv_open_ft(charset, tcs)) == (iconv_t)-1 &&
264 				errno != 0) {
265 			if (errno == EINVAL)
266 				fprintf(stderr, catgets(catd, CATSET, 179,
267 			"Cannot convert from %s to %s\n"), tcs, charset);
268 			else
269 				perror("iconv_open");
270 			Fclose(fi);
271 			return -1;
272 		}
273 	}
274 #endif	/* HAVE_ICONV */
275 	buf = smalloc(bufsize = INFIX_BUF);
276 	if (convert == CONV_TOQP
277 #ifdef	HAVE_ICONV
278 			|| iconvd != (iconv_t)-1
279 #endif
280 			) {
281 		fflush(fi);
282 		count = fsize(fi);
283 	}
284 	for (;;) {
285 		if (convert == CONV_TOQP
286 #ifdef	HAVE_ICONV
287 				|| iconvd != (iconv_t)-1
288 #endif
289 				) {
290 			if (fgetline(&buf, &bufsize, &count, &sz, fi, 0)
291 					== NULL)
292 				break;
293 		} else {
294 			if ((sz = fread(buf, sizeof *buf, bufsize, fi)) == 0)
295 				break;
296 		}
297 		lastc = buf[sz-1];
298 		if (mime_write(buf, sz, fo, convert, TD_ICONV,
299 					NULL, (size_t)0, NULL, NULL) == 0)
300 			err = -1;
301 	}
302 	if (convert == CONV_TOQP && lastc != '\n')
303 		fwrite("=\n", 1, 2, fo);
304 	if (ferror(fi))
305 		err = -1;
306 	Fclose(fi);
307 	free(buf);
308 	return err;
309 }
310 
311 /*
312  * Try out different character set conversions to attach a file.
313  */
314 static int
attach_file(struct attachment * ap,FILE * fo,int dosign)315 attach_file(struct attachment *ap, FILE *fo, int dosign)
316 {
317 	char	*_wantcharset, *charsets, *ncs;
318 	size_t	offs = ftell(fo);
319 
320 	if (ap->a_charset || (charsets = value("sendcharsets")) == NULL)
321 		return attach_file1(ap, fo, dosign);
322 	_wantcharset = wantcharset;
323 	wantcharset = savestr(charsets);
324 loop:	if ((ncs = strchr(wantcharset, ',')) != NULL)
325 		*ncs++ = '\0';
326 try:	if (attach_file1(ap, fo, dosign) != 0) {
327 		if (errno == EILSEQ || errno == EINVAL) {
328 			if (ncs && *ncs) {
329 				wantcharset = ncs;
330 				clearerr(fo);
331 				fseek(fo, offs, SEEK_SET);
332 				goto loop;
333 			}
334 			if (wantcharset) {
335 				if (wantcharset == (char *)-1)
336 					wantcharset = NULL;
337 				else {
338 					wantcharset = (char *)-1;
339 					clearerr(fo);
340 					fseek(fo, offs, SEEK_SET);
341 					goto try;
342 				}
343 			}
344 		}
345 	}
346 	wantcharset = _wantcharset;
347 	return 0;
348 }
349 
350 /*
351  * Attach a message to the file buffer.
352  */
353 static int
attach_message(struct attachment * ap,FILE * fo,int dosign)354 attach_message(struct attachment *ap, FILE *fo, int dosign)
355 {
356 	struct message	*mp;
357 
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;
366 }
367 
368 /*
369  * Generate the body of a MIME multipart message.
370  */
371 static int
make_multipart(struct header * hp,int convert,FILE * fi,FILE * fo,const char * contenttype,const char * charset,int dosign)372 make_multipart(struct header *hp, int convert, FILE *fi, FILE *fo,
373 		const char *contenttype, const char *charset, int dosign)
374 {
375 	struct attachment *att;
376 
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;
381 
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 */
394 				) {
395 			fflush(fi);
396 			count = fsize(fi);
397 		}
398 		for (;;) {
399 			if (convert == CONV_TOQP
400 #ifdef	HAVE_ICONV
401 					|| iconvd != (iconv_t)-1
402 #endif	/* HAVE_ICONV */
403 					) {
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;
411 			}
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;
418 			}
419 		}
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);
427 	}
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;
435 		}
436 	}
437 	/* the final boundary with two attached dashes */
438 	fprintf(fo, "\n--%s--\n", send_boundary);
439 	return 0;
440 }
441 
442 /*
443  * Prepend a header in front of the collected stuff
444  * and return the new file.
445  */
446 static FILE *
infix(struct header * hp,FILE * fi,int dosign)447 infix(struct header *hp, FILE *fi, int dosign)
448 {
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;
458 
459 	if ((nfo = Ftemp(&tempMail, "Rs", "w", 0600, 1)) == NULL) {
460 		perror(catgets(catd, CATSET, 178, "temporary mail file"));
461 		return(NULL);
462 	}
463 	if ((nfi = Fopen(tempMail, "r")) == NULL) {
464 		perror(tempMail);
465 		Fclose(nfo);
466 		return(NULL);
467 	}
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;
486 		}
487 	}
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;
499 		}
500 #endif
501 		return NULL;
502 	}
503 #ifdef	HAVE_ICONV
504 	if (convhdr && iconvd != (iconv_t)-1) {
505 		iconv_close(iconvd);
506 		iconvd = (iconv_t)-1;
507 	}
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;
523 		}
524 	}
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;
535 			}
536 #endif
537 			return NULL;
538 		}
539 	} else {
540 		size_t sz, bufsize, count;
541 		char *buf;
542 
543 		if (convert == CONV_TOQP
544 #ifdef	HAVE_ICONV
545 				|| iconvd != (iconv_t)-1
546 #endif	/* HAVE_ICONV */
547 				) {
548 			fflush(fi);
549 			count = fsize(fi);
550 		}
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 */
557 					) {
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;
565 			}
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;
576 				}
577 #endif
578 				free(buf);
579 				return NULL;
580 			}
581 		}
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;
592 			}
593 #endif
594 			return NULL;
595 		}
596 		if (charset != NULL)
597 			put_signature(nfo, convert);
598 	}
599 #ifdef	HAVE_ICONV
600 	if (iconvd != (iconv_t)-1) {
601 		iconv_close(iconvd);
602 		iconvd = (iconv_t)-1;
603 	}
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;
611 	}
612 	Fclose(nfo);
613 	Fclose(fi);
614 	fflush(nfi);
615 	rewind(nfi);
616 	return(nfi);
617 }
618 
619 /*
620  * Save the outgoing mail on the passed file.
621  */
622 
623 /*ARGSUSED*/
624 static int
savemail(char * name,FILE * fi)625 savemail(char *name, FILE *fi)
626 {
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;
634 
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);
642 		}
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;
650 				}
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;
661 				}
662 			}
663 			fflush(fo);
664 			if (prependnl) {
665 				putc('\n', fo);
666 				fflush(fo);
667 			}
668 		}
669 	}
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);
686 	}
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;
694 	}
695 	if (Fclose(fo) != 0)
696 		error = -1;
697 	fflush(fi);
698 	rewind(fi);
699 	/*
700 	 * OpenBSD 3.2 and NetBSD 1.5.2 were reported not to
701 	 * reset the kernel file offset after the calls above,
702 	 * a clear violation of IEEE Std 1003.1, 1996, 8.2.3.7.
703 	 * So do it 'manually'.
704 	 */
705 	lseek(fileno(fi), 0, SEEK_SET);
706 	free(buf);
707 	return error;
708 }
709 
710 /*
711  * Interface between the argument list and the mail1 routine
712  * which does all the dirty work.
713  */
714 int
mail(struct name * to,struct name * cc,struct name * bcc,struct name * smopts,char * subject,struct attachment * attach,char * quotefile,int recipient_record,int tflag,int Eflag)715 mail(struct name *to, struct name *cc, struct name *bcc,
716 		struct name *smopts, char *subject, struct attachment *attach,
717 		char *quotefile, int recipient_record, int tflag, int Eflag)
718 {
719 	struct header head;
720 	struct str in, out;
721 
722 	memset(&head, 0, sizeof head);
723 	/* The given subject may be in RFC1522 format. */
724 	if (subject != NULL) {
725 		in.s = subject;
726 		in.l = strlen(subject);
727 		mime_fromhdr(&in, &out, TD_ISPR | TD_ICONV);
728 		head.h_subject = out.s;
729 	}
730 	if (tflag == 0) {
731 		head.h_to = to;
732 		head.h_cc = cc;
733 		head.h_bcc = bcc;
734 	}
735 	head.h_attach = attach;
736 	head.h_smopts = smopts;
737 	mail1(&head, 0, NULL, quotefile, recipient_record, 0, tflag, Eflag);
738 	if (subject != NULL)
739 		free(out.s);
740 	return(0);
741 }
742 
743 /*
744  * Send mail to a bunch of user names.  The interface is through
745  * the mail routine below.
746  */
747 static int
sendmail_internal(void * v,int recipient_record)748 sendmail_internal(void *v, int recipient_record)
749 {
750 	int Eflag;
751 	char *str = v;
752 	struct header head;
753 
754 	memset(&head, 0, sizeof head);
755 	head.h_to = extract(str, GTO|GFULL);
756 	Eflag = value("skipemptybody") != NULL;
757 	mail1(&head, 0, NULL, NULL, recipient_record, 0, 0, Eflag);
758 	return(0);
759 }
760 
761 int
sendmail(void * v)762 sendmail(void *v)
763 {
764 	return sendmail_internal(v, 0);
765 }
766 
767 int
Sendmail(void * v)768 Sendmail(void *v)
769 {
770 	return sendmail_internal(v, 1);
771 }
772 
773 static enum okay
transfer(struct name * to,struct name * mailargs,FILE * input,struct header * hp)774 transfer(struct name *to, struct name *mailargs, FILE *input, struct header *hp)
775 {
776 	char	o[LINESIZE], *cp;
777 	struct name	*np, *nt;
778 	int	cnt = 0;
779 	FILE	*ef;
780 	enum okay	ok = OKAY;
781 
782 	np = to;
783 	while (np) {
784 		snprintf(o, sizeof o, "smime-encrypt-%s", np->n_name);
785 		if ((cp = value(o)) != NULL) {
786 			if ((ef = smime_encrypt(input, cp, np->n_name)) != 0) {
787 				nt = nalloc(np->n_name,
788 					np->n_type & ~(GFULL|GSKIN));
789 				if (start_mta(nt, mailargs, ef, hp) != OKAY)
790 					ok = STOP;
791 				Fclose(ef);
792 			} else {
793 				fprintf(stderr, "Message not sent to <%s>\n",
794 						np->n_name);
795 				senderr++;
796 			}
797 			rewind(input);
798 			if (np->n_flink)
799 				np->n_flink->n_blink = np->n_blink;
800 			if (np->n_blink)
801 				np->n_blink->n_flink = np->n_flink;
802 			if (np == to)
803 				to = np->n_flink;
804 			np = np->n_flink;
805 		} else {
806 			cnt++;
807 			np = np->n_flink;
808 		}
809 	}
810 	if (cnt) {
811 		if (value("smime-force-encryption") ||
812 				start_mta(to, mailargs, input, hp) != OKAY)
813 			ok = STOP;
814 	}
815 	return ok;
816 }
817 
818 /*
819  * Start the Mail Transfer Agent
820  * mailing to namelist and stdin redirected to input.
821  */
822 static enum okay
start_mta(struct name * to,struct name * mailargs,FILE * input,struct header * hp)823 start_mta(struct name *to, struct name *mailargs, FILE *input,
824 		struct header *hp)
825 {
826 	char **args = NULL, **t;
827 	pid_t pid;
828 	sigset_t nset;
829 	char *cp, *smtp;
830 	char	*user = NULL, *password = NULL, *skinned = NULL;
831 	enum okay	ok = STOP;
832 #ifdef	HAVE_SOCKETS
833 	struct termios	otio;
834 	int	reset_tio;
835 #endif	/* HAVE_SOCKETS */
836 
837 	if ((smtp = value("smtp")) == NULL) {
838 		args = unpack(cat(mailargs, to));
839 		if (debug || value("debug")) {
840 			printf(catgets(catd, CATSET, 181,
841 					"Sendmail arguments:"));
842 			for (t = args; *t != NULL; t++)
843 				printf(" \"%s\"", *t);
844 			printf("\n");
845 			return OKAY;
846 		}
847 	}
848 #ifdef	HAVE_SOCKETS
849 	if (smtp != NULL) {
850 		skinned = skin(myorigin(hp));
851 		if ((user = smtp_auth_var("-user", skinned)) != NULL &&
852 				(password = smtp_auth_var("-password",
853 					skinned)) == NULL)
854 			password = getpassword(&otio, &reset_tio, NULL);
855 	}
856 #endif	/* HAVE_SOCKETS */
857 	/*
858 	 * Fork, set up the temporary mail file as standard
859 	 * input for "mail", and exec with the user list we generated
860 	 * far above.
861 	 */
862 	if ((pid = fork()) == -1) {
863 		perror("fork");
864 		savedeadletter(input);
865 		senderr++;
866 		return STOP;
867 	}
868 	if (pid == 0) {
869 		sigemptyset(&nset);
870 		sigaddset(&nset, SIGHUP);
871 		sigaddset(&nset, SIGINT);
872 		sigaddset(&nset, SIGQUIT);
873 		sigaddset(&nset, SIGTSTP);
874 		sigaddset(&nset, SIGTTIN);
875 		sigaddset(&nset, SIGTTOU);
876 		freopen("/dev/null", "r", stdin);
877 		if (smtp != NULL) {
878 			prepare_child(&nset, 0, 1);
879 			if (smtp_mta(smtp, to, input, hp,
880 					user, password, skinned) == 0)
881 				_exit(0);
882 		} else {
883 			prepare_child(&nset, fileno(input), -1);
884 			if ((cp = value("sendmail")) != NULL)
885 				cp = expand(cp);
886 			else
887 				cp = SENDMAIL;
888 			args[0] = cp;
889 			execv(cp, args);
890 			perror(cp);
891 		}
892 		savedeadletter(input);
893 		fputs(catgets(catd, CATSET, 182,
894 				". . . message not sent.\n"), stderr);
895 		_exit(1);
896 	}
897 	if (value("verbose") != NULL || value("sendwait") || debug
898 			|| value("debug")) {
899 		if (wait_child(pid) == 0)
900 			ok = OKAY;
901 		else
902 			senderr++;
903 	} else {
904 		ok = OKAY;
905 		free_child(pid);
906 	}
907 	return ok;
908 }
909 
910 /*
911  * Record outgoing mail if instructed to do so.
912  */
913 static enum okay
mightrecord(FILE * fp,struct name * to,int recipient_record)914 mightrecord(FILE *fp, struct name *to, int recipient_record)
915 {
916 	char	*cp, *cq, *ep;
917 
918 	if (recipient_record) {
919 		cq = skin(to->n_name);
920 		cp = salloc(strlen(cq) + 1);
921 		strcpy(cp, cq);
922 		for (cq = cp; *cq && *cq != '@'; cq++);
923 		*cq = '\0';
924 	} else
925 		cp = value("record");
926 	if (cp != NULL) {
927 		ep = expand(cp);
928 		if (value("outfolder") && *ep != '/' && *ep != '+' &&
929 				which_protocol(ep) == PROTO_FILE) {
930 			cq = salloc(strlen(cp) + 2);
931 			cq[0] = '+';
932 			strcpy(&cq[1], cp);
933 			cp = cq;
934 			ep = expand(cp);
935 		}
936 		if (savemail(ep, fp) != 0) {
937 			fprintf(stderr,
938 				"Error while saving message to %s - "
939 				"message not sent\n", ep);
940 			rewind(fp);
941 			exit_status |= 1;
942 			savedeadletter(fp);
943 			return STOP;
944 		}
945 	}
946 	return OKAY;
947 }
948 
949 /*
950  * Mail a message on standard input to the people indicated
951  * in the passed header.  (Internal interface).
952  */
953 enum okay
mail1(struct header * hp,int printheaders,struct message * quote,char * quotefile,int recipient_record,int doprefix,int tflag,int Eflag)954 mail1(struct header *hp, int printheaders, struct message *quote,
955 		char *quotefile, int recipient_record, int doprefix, int tflag,
956 		int Eflag)
957 {
958 	struct name *to;
959 	FILE *mtf, *nmtf;
960 	enum okay	ok = STOP;
961 	int	dosign = -1;
962 	char	*charsets, *ncs = NULL, *cp;
963 
964 #ifdef	notdef
965 	if ((hp->h_to = checkaddrs(hp->h_to)) == NULL) {
966 		senderr++;
967 		return STOP;
968 	}
969 #endif
970 	if ((cp = value("autocc")) != NULL && *cp)
971 		hp->h_cc = cat(hp->h_cc, checkaddrs(sextract(cp, GCC|GFULL)));
972 	if ((cp = value("autobcc")) != NULL && *cp)
973 		hp->h_bcc = cat(hp->h_bcc,
974 				checkaddrs(sextract(cp, GBCC|GFULL)));
975 	/*
976 	 * Collect user's mail from standard input.
977 	 * Get the result as mtf.
978 	 */
979 	if ((mtf = collect(hp, printheaders, quote, quotefile, doprefix,
980 					tflag)) == NULL)
981 		return STOP;
982 	if (value("interactive") != NULL) {
983 		if (((value("bsdcompat") || value("askatend"))
984 					&& (value("askcc") != NULL ||
985 					value("askbcc") != NULL)) ||
986 				value("askattach") != NULL ||
987 				value("asksign") != NULL) {
988 			if (value("askcc") != NULL)
989 				grabh(hp, GCC, 1);
990 			if (value("askbcc") != NULL)
991 				grabh(hp, GBCC, 1);
992 			if (value("askattach") != NULL)
993 				hp->h_attach = edit_attachments(hp->h_attach);
994 			if (value("asksign") != NULL)
995 				dosign = yorn("Sign this message (y/n)? ");
996 		} else {
997 			printf(catgets(catd, CATSET, 183, "EOT\n"));
998 			fflush(stdout);
999 		}
1000 	}
1001 	if (fsize(mtf) == 0) {
1002 		if (Eflag)
1003 			goto out;
1004 		if (hp->h_subject == NULL)
1005 			printf(catgets(catd, CATSET, 184,
1006 				"No message, no subject; hope that's ok\n"));
1007 		else if (value("bsdcompat") || value("bsdmsgs"))
1008 			printf(catgets(catd, CATSET, 185,
1009 				"Null message body; hope that's ok\n"));
1010 	}
1011 	if (dosign < 0) {
1012 		if (value("smime-sign") != NULL)
1013 			dosign = 1;
1014 		else
1015 			dosign = 0;
1016 	}
1017 	/*
1018 	 * Now, take the user names from the combined
1019 	 * to and cc lists and do all the alias
1020 	 * processing.
1021 	 */
1022 	senderr = 0;
1023 	if ((to = usermap(cat(hp->h_bcc, cat(hp->h_to, hp->h_cc)))) == NULL) {
1024 		printf(catgets(catd, CATSET, 186, "No recipients specified\n"));
1025 		senderr++;
1026 	}
1027 	to = fixhead(hp, to);
1028 	if (hp->h_charset) {
1029 		wantcharset = hp->h_charset;
1030 		goto try;
1031 	}
1032 hloop:	wantcharset = NULL;
1033 	if ((charsets = value("sendcharsets")) != NULL) {
1034 		wantcharset = savestr(charsets);
1035 	loop:	if ((ncs = strchr(wantcharset, ',')) != NULL)
1036 			*ncs++ = '\0';
1037 	}
1038 try:	if ((nmtf = infix(hp, mtf, dosign)) == NULL) {
1039 		if (hp->h_charset && (errno == EILSEQ || errno == EINVAL)) {
1040 			rewind(mtf);
1041 			hp->h_charset = NULL;
1042 			goto hloop;
1043 		}
1044 		if (errno == EILSEQ || errno == EINVAL) {
1045 			if (ncs && *ncs) {
1046 				rewind(mtf);
1047 				wantcharset = ncs;
1048 				goto loop;
1049 			}
1050 			if (wantcharset && value("interactive") == NULL) {
1051 				if (wantcharset == (char *)-1)
1052 					wantcharset = NULL;
1053 				else {
1054 					rewind(mtf);
1055 					wantcharset = (char *)-1;
1056 					goto try;
1057 				}
1058 			}
1059 		}
1060 		/* fprintf(stderr, ". . . message lost, sorry.\n"); */
1061 		perror("");
1062 	fail:	senderr++;
1063 		rewind(mtf);
1064 		savedeadletter(mtf);
1065 		fputs(catgets(catd, CATSET, 187,
1066 				". . . message not sent.\n"), stderr);
1067 		return STOP;
1068 	}
1069 	mtf = nmtf;
1070 	if (dosign) {
1071 		if ((nmtf = smime_sign(mtf, hp)) == NULL)
1072 			goto fail;
1073 		Fclose(mtf);
1074 		mtf = nmtf;
1075 	}
1076 	/*
1077 	 * Look through the recipient list for names with /'s
1078 	 * in them which we write to as files directly.
1079 	 */
1080 	to = outof(to, mtf, hp);
1081 	if (senderr)
1082 		savedeadletter(mtf);
1083 	to = elide(to);
1084 	if (count(to) == 0) {
1085 		if (senderr == 0)
1086 			ok = OKAY;
1087 		goto out;
1088 	}
1089 	if (mightrecord(mtf, to, recipient_record) != OKAY)
1090 		goto out;
1091 	ok = transfer(to, hp->h_smopts, mtf, hp);
1092 out:
1093 	Fclose(mtf);
1094 	return ok;
1095 }
1096 
1097 /*
1098  * Create a Message-Id: header field.
1099  * Use either the host name or the from address.
1100  */
1101 static void
message_id(FILE * fo,struct header * hp)1102 message_id(FILE *fo, struct header *hp)
1103 {
1104 	char	*cp;
1105 	time_t	now;
1106 
1107 	time(&now);
1108 	if ((cp = value("hostname")) != NULL)
1109 		fprintf(fo, "Message-ID: <%lx.%s@%s>\n",
1110 				(long)now, getrandstring(24), cp);
1111 	else if ((cp = skin(myorigin(hp))) != NULL && strchr(cp, '@') != NULL)
1112 		fprintf(fo, "Message-ID: <%lx.%s%%%s>\n",
1113 				(long)now, getrandstring(16), cp);
1114 }
1115 
1116 static const char *weekday_names[] = {
1117 	"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
1118 };
1119 
1120 const char *month_names[] = {
1121 	"Jan", "Feb", "Mar", "Apr", "May", "Jun",
1122 	"Jul", "Aug", "Sep", "Oct", "Nov", "Dec", NULL
1123 };
1124 
1125 /*
1126  * Create a Date: header field.
1127  * We use tm->tm_gmtoff to account for negative DST adjustments (e.g. Ireland).
1128  */
1129 int
mkdate(FILE * fo,const char * field)1130 mkdate(FILE *fo, const char *field)
1131 {
1132 	time_t t;
1133 	struct tm *tmptr;
1134 	int tzdiff, tzdiff_hour, tzdiff_min;
1135 
1136 	time(&t);
1137 	tmptr = localtime(&t);
1138 	tzdiff = tmptr->tm_gmtoff; /* seconds east of GMT */
1139 	tzdiff_hour = (int)(tzdiff / 60);
1140 	tzdiff_min = tzdiff_hour % 60;
1141 	tzdiff_hour /= 60;
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);
1149 }
1150 
1151 static enum okay
putname(char * line,enum gfield w,enum sendaction action,int * gotcha,char * prefix,FILE * fo,struct name ** xp)1152 putname(char *line, enum gfield w, enum sendaction action, int *gotcha,
1153 		char *prefix, FILE *fo, struct name **xp)
1154 {
1155 	struct name	*np;
1156 
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;
1167 }
1168 
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++; \
1176 				} \
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++; \
1183 				} \
1184 			}
1185 /*
1186  * Dump the to, subject, cc header on the
1187  * passed file buffer.
1188  */
1189 int
puthead(struct header * hp,FILE * fo,enum gfield w,enum sendaction action,enum conversion convert,char * contenttype,char * charset)1190 puthead(struct header *hp, FILE *fo, enum gfield w,
1191 		enum sendaction action, enum conversion convert,
1192 		char *contenttype, char *charset)
1193 {
1194 	int gotcha;
1195 	char *addr/*, *cp*/;
1196 	int stealthmua;
1197 	struct name *np, *fromfield = NULL, *senderfield = NULL;
1198 
1199 
1200 	if (value("stealthmua"))
1201 		stealthmua = 1;
1202 	else
1203 		stealthmua = 0;
1204 	gotcha = 0;
1205 	if (w & GDATE) {
1206 		mkdate(fo, "Date"), gotcha++;
1207 	}
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 			fwrite("Organization: ", sizeof (char), 14, fo);
1222 			if (mime_write(addr, strlen(addr), fo,
1223 					action == SEND_TODISP ?
1224 					CONV_NONE:CONV_TOHDR,
1225 					action == SEND_TODISP ?
1226 					TD_ISPR|TD_ICONV:TD_ICONV,
1227 					NULL, (size_t)0,
1228 					NULL, NULL) == 0)
1229 				return 1;
1230 			gotcha++;
1231 			putc('\n', fo);
1232 		}
1233 		if (hp->h_replyto != NULL) {
1234 			if (fmt("Reply-To:", hp->h_replyto, fo,
1235 					w&(GCOMMA|GFILES), 0,
1236 					action!=SEND_TODISP))
1237 				return 1;
1238 			gotcha++;
1239 		} else if ((addr = value("replyto")) != NULL)
1240 			if (putname(addr, w, action, &gotcha, "Reply-To:", fo,
1241 						NULL))
1242 				return 1;
1243 		if (hp->h_sender != NULL) {
1244 			if (fmt("Sender:", hp->h_sender, fo,
1245 					w&(GCOMMA|GFILES), 0,
1246 					action!=SEND_TODISP))
1247 				return 1;
1248 			gotcha++;
1249 			senderfield = hp->h_sender;
1250 		} else if ((addr = value("sender")) != NULL)
1251 			if (putname(addr, w, action, &gotcha, "Sender:", fo,
1252 						&senderfield))
1253 				return 1;
1254 		if (check_from_and_sender(fromfield, senderfield))
1255 			return 1;
1256 	}
1257 	if (hp->h_to != NULL && w & GTO) {
1258 		if (fmt("To:", hp->h_to, fo, w&(GCOMMA|GFILES), 0,
1259 					action!=SEND_TODISP))
1260 			return 1;
1261 		gotcha++;
1262 	}
1263 	if (value("bsdcompat") == NULL && value("bsdorder") == NULL)
1264 		FMT_CC_AND_BCC
1265 	if (hp->h_subject != NULL && w & GSUBJECT) {
1266 		fwrite("Subject: ", sizeof (char), 9, fo);
1267 		if (ascncasecmp(hp->h_subject, "re: ", 4) == 0) {
1268 			fwrite("Re: ", sizeof (char), 4, fo);
1269 			if (mime_write(hp->h_subject + 4,
1270 					strlen(hp->h_subject + 4),
1271 					fo, action == SEND_TODISP ?
1272 					CONV_NONE:CONV_TOHDR,
1273 					action == SEND_TODISP ?
1274 					TD_ISPR|TD_ICONV:TD_ICONV,
1275 					NULL, (size_t)0,
1276 					NULL, NULL) == 0)
1277 				return 1;
1278 		} else if (*hp->h_subject) {
1279 			if (mime_write(hp->h_subject,
1280 					strlen(hp->h_subject),
1281 					fo, action == SEND_TODISP ?
1282 					CONV_NONE:CONV_TOHDR,
1283 					action == SEND_TODISP ?
1284 					TD_ISPR|TD_ICONV:TD_ICONV,
1285 					NULL, (size_t)0,
1286 					NULL, NULL) == 0)
1287 				return 1;
1288 		}
1289 		gotcha++;
1290 		fwrite("\n", sizeof (char), 1, fo);
1291 	}
1292 	if (value("bsdcompat") || value("bsdorder"))
1293 		FMT_CC_AND_BCC
1294 	if (w & GMSGID && stealthmua == 0)
1295 		message_id(fo, hp), gotcha++;
1296 	if (hp->h_ref != NULL && w & GREF) {
1297 		fmt("References:", hp->h_ref, fo, 0, 1, 0);
1298 		if ((np = hp->h_ref) != NULL && np->n_name) {
1299 			while (np->n_flink)
1300 				np = np->n_flink;
1301 			if (mime_name_invalid(np->n_name, 0) == 0) {
1302 				fprintf(fo, "In-Reply-To: %s\n", np->n_name);
1303 				gotcha++;
1304 			}
1305 		}
1306 	}
1307 	if (w & GUA && stealthmua == 0)
1308 		fprintf(fo, "User-Agent: Heirloom mailx %s\n",
1309 				version), gotcha++;
1310 	if (w & GMIME) {
1311 		fputs("MIME-Version: 1.0\n", fo), gotcha++;
1312 		if (hp->h_attach != NULL) {
1313 			makeboundary();
1314 			fprintf(fo, "Content-Type: multipart/mixed;\n"
1315 				" boundary=\"%s\"\n", send_boundary);
1316 		} else {
1317 			fprintf(fo, "Content-Type: %s", contenttype);
1318 			if (charset)
1319 				fprintf(fo, "; charset=%s", charset);
1320 			fprintf(fo, "\nContent-Transfer-Encoding: %s\n",
1321 					getencoding(convert));
1322 		}
1323 	}
1324 	if (gotcha && w & GNL)
1325 		putc('\n', fo);
1326 	return(0);
1327 }
1328 
1329 /*
1330  * Format the given header line to not exceed 72 characters.
1331  */
1332 static int
fmt(char * str,struct name * np,FILE * fo,int flags,int dropinvalid,int domime)1333 fmt(char *str, struct name *np, FILE *fo, int flags, int dropinvalid,
1334 		int domime)
1335 {
1336 	int col, len, count = 0;
1337 	int is_to = 0, comma;
1338 
1339 	comma = flags&GCOMMA ? 1 : 0;
1340 	col = strlen(str);
1341 	if (col) {
1342 		fwrite(str, sizeof *str, strlen(str), fo);
1343 		if ((flags&GFILES) == 0 &&
1344 				col == 3 && asccasecmp(str, "to:") == 0 ||
1345 				col == 3 && asccasecmp(str, "cc:") == 0 ||
1346 				col == 4 && asccasecmp(str, "bcc:") == 0 ||
1347 				col == 10 && asccasecmp(str, "Resent-To:") == 0)
1348 			is_to = 1;
1349 	}
1350 	for (; np != NULL; np = np->n_flink) {
1351 		if (is_to && is_fileaddr(np->n_name))
1352 			continue;
1353 		if (np->n_flink == NULL)
1354 			comma = 0;
1355 		if (mime_name_invalid(np->n_name, !dropinvalid)) {
1356 			if (dropinvalid)
1357 				continue;
1358 			else
1359 				return 1;
1360 		}
1361 		len = strlen(np->n_fullname);
1362 		col++;		/* for the space */
1363 		if (count && col + len + comma > 72 && col > 1) {
1364 			fputs("\n ", fo);
1365 			col = 1;
1366 		} else
1367 			putc(' ', fo);
1368 		len = mime_write(np->n_fullname,
1369 				len, fo,
1370 				domime?CONV_TOHDR_A:CONV_NONE,
1371 				TD_ICONV, NULL, (size_t)0,
1372 				NULL, NULL);
1373 		if (comma && !(is_to && is_fileaddr(np->n_flink->n_name)))
1374 			putc(',', fo);
1375 		col += len + comma;
1376 		count++;
1377 	}
1378 	putc('\n', fo);
1379 	return 0;
1380 }
1381 
1382 /*
1383  * Rewrite a message for resending, adding the Resent-Headers.
1384  */
1385 static int
infix_resend(FILE * fi,FILE * fo,struct message * mp,struct name * to,int add_resent)1386 infix_resend(FILE *fi, FILE *fo, struct message *mp, struct name *to,
1387 		int add_resent)
1388 {
1389 	size_t count;
1390 	char *buf = NULL, *cp/*, *cp2*/;
1391 	size_t c, bufsize = 0;
1392 	struct name	*fromfield = NULL, *senderfield = NULL;
1393 
1394 	count = mp->m_size;
1395 	/*
1396 	 * Write the Resent-Fields.
1397 	 */
1398 	if (add_resent) {
1399 		fputs("Resent-", fo);
1400 		mkdate(fo, "Date");
1401 		if ((cp = myaddrs(NULL)) != NULL) {
1402 			if (putname(cp, GCOMMA, SEND_MBOX, NULL,
1403 					"Resent-From:", fo, &fromfield))
1404 				return 1;
1405 		}
1406 		if ((cp = value("sender")) != NULL) {
1407 			if (putname(cp, GCOMMA, SEND_MBOX, NULL,
1408 					"Resent-Sender:", fo, &senderfield))
1409 				return 1;
1410 		}
1411 #ifdef	notdef
1412 		/*
1413 		 * RFC 2822 disallows generation of this field.
1414 		 */
1415 		cp = value("replyto");
1416 		if (cp != NULL) {
1417 			if (mime_name_invalid(cp, 1)) {
1418 				if (buf)
1419 					free(buf);
1420 				return 1;
1421 			}
1422 			fwrite("Resent-Reply-To: ", sizeof (char),
1423 					17, fo);
1424 			mime_write(cp, strlen(cp), fo,
1425 					CONV_TOHDR_A, TD_ICONV,
1426 					NULL, (size_t)0,
1427 					NULL, NULL);
1428 			putc('\n', fo);
1429 		}
1430 #endif	/* notdef */
1431 		if (fmt("Resent-To:", to, fo, 1, 1, 0)) {
1432 			if (buf)
1433 				free(buf);
1434 			return 1;
1435 		}
1436 		if (value("stealthmua") == NULL) {
1437 			fputs("Resent-", fo);
1438 			message_id(fo, NULL);
1439 		}
1440 	}
1441 	if (check_from_and_sender(fromfield, senderfield))
1442 		return 1;
1443 	/*
1444 	 * Write the original headers.
1445 	 */
1446 	while (count > 0) {
1447 		if ((cp = foldergets(&buf, &bufsize, &count, &c, fi)) == NULL)
1448 			break;
1449 		if (ascncasecmp("status: ", buf, 8) != 0
1450 				&& strncmp("From ", buf, 5) != 0) {
1451 			fwrite(buf, sizeof *buf, c, fo);
1452 		}
1453 		if (count > 0 && *buf == '\n')
1454 			break;
1455 	}
1456 	/*
1457 	 * Write the message body.
1458 	 */
1459 	while (count > 0) {
1460 		if (foldergets(&buf, &bufsize, &count, &c, fi) == NULL)
1461 			break;
1462 		if (count == 0 && *buf == '\n')
1463 			break;
1464 		fwrite(buf, sizeof *buf, c, fo);
1465 	}
1466 	if (buf)
1467 		free(buf);
1468 	if (ferror(fo)) {
1469 		perror(catgets(catd, CATSET, 188, "temporary mail file"));
1470 		return 1;
1471 	}
1472 	return 0;
1473 }
1474 
1475 enum okay
resend_msg(struct message * mp,struct name * to,int add_resent)1476 resend_msg(struct message *mp, struct name *to, int add_resent)
1477 {
1478 	FILE *ibuf, *nfo, *nfi;
1479 	char *tempMail;
1480 	struct header head;
1481 	enum okay	ok = STOP;
1482 
1483 	memset(&head, 0, sizeof head);
1484 	if ((to = checkaddrs(to)) == NULL) {
1485 		senderr++;
1486 		return STOP;
1487 	}
1488 	if ((nfo = Ftemp(&tempMail, "Rs", "w", 0600, 1)) == NULL) {
1489 		senderr++;
1490 		perror(catgets(catd, CATSET, 189, "temporary mail file"));
1491 		return STOP;
1492 	}
1493 	if ((nfi = Fopen(tempMail, "r")) == NULL) {
1494 		senderr++;
1495 		perror(tempMail);
1496 		return STOP;
1497 	}
1498 	rm(tempMail);
1499 	Ftfree(&tempMail);
1500 	if ((ibuf = setinput(&mb, mp, NEED_BODY)) == NULL)
1501 		return STOP;
1502 	head.h_to = to;
1503 	to = fixhead(&head, to);
1504 	if (infix_resend(ibuf, nfo, mp, head.h_to, add_resent) != 0) {
1505 		senderr++;
1506 		rewind(nfo);
1507 		savedeadletter(nfi);
1508 		fputs(catgets(catd, CATSET, 190,
1509 				". . . message not sent.\n"), stderr);
1510 		Fclose(nfo);
1511 		Fclose(nfi);
1512 		return STOP;
1513 	}
1514 	fflush(nfo);
1515 	rewind(nfo);
1516 	Fclose(nfo);
1517 	to = outof(to, nfi, &head);
1518 	if (senderr)
1519 		savedeadletter(nfi);
1520 	to = elide(to);
1521 	if (count(to) != 0) {
1522 		if (value("record-resent") == NULL ||
1523 				mightrecord(nfi, to, 0) == OKAY)
1524 			ok = transfer(to, head.h_smopts, nfi, NULL);
1525 	} else if (senderr == 0)
1526 		ok = OKAY;
1527 	Fclose(nfi);
1528 	return ok;
1529 }
1530