1 /*
2 ** Copyright 2001-2010 Double Precision, Inc.
3 ** See COPYING for distribution information.
4 */
5 
6 #include "config.h"
7 #include "dbobj.h"
8 #include "liblock/config.h"
9 #include "liblock/liblock.h"
10 #include "unicode/unicode.h"
11 #include "numlib/numlib.h"
12 #include <string.h>
13 #include <stdlib.h>
14 #include <unistd.h>
15 #include <fcntl.h>
16 #include <stdio.h>
17 #include <time.h>
18 #if HAVE_LOCALE_H
19 #include <locale.h>
20 #endif
21 #include <langinfo.h>
22 #if HAVE_STRINGS_H
23 #include <strings.h>
24 #endif
25 #include <ctype.h>
26 #include "rfc822/rfc822.h"
27 #include "rfc2045/rfc2045.h"
28 #include "rfc2045/rfc2045charset.h"
29 #include <sys/types.h>
30 #include "mywait.h"
31 #include <signal.h>
32 #if HAVE_SYSEXITS_H
33 #include <sysexits.h>
34 #endif
35 
36 #ifndef EX_TEMPFAIL
37 #define EX_TEMPFAIL	75
38 #endif
39 
40 static const char *recips=0;
41 static const char *dbfile=0;
42 static char *charset=RFC2045CHARSET;
43 static unsigned interval=1;
44 static char *sender;
45 
46 struct header {
47 	struct header *next;
48 	char *buf;
49 } ;
50 
51 static struct header *header_list;
52 
53 static struct header *extra_headers=0;
54 
55 
rfc2045_error(const char * str)56 void rfc2045_error(const char *str)
57 {
58 	fprintf(stderr, "%s\n", str);
59 	exit(1);
60 }
61 
usage()62 static void usage()
63 {
64 	fprintf(stderr,
65 		"Usage: mailbot [ options ] [ $MAILER arg arg... ]\n"
66 		"\n"
67 		"    -t filename        - text autoresponse\n"
68 		"    -c charset         - text MIME character set (default %s)\n"
69 		"    -m filename        - text autoresponse with a MIME header\n"
70 		"    -r addr1,addr2...  - any 'addr' required in a To/Cc header\n",
71 		charset);
72 
73 	fprintf(stderr,
74 		"    -d $pathname       - database to prevent duplicate autoresponses\n"
75 		"    -D x               - at least 'x' days before dupes (default: 1)\n"
76 		"    -s subject         - Subject: on autoresponses\n"
77 		"    -A \"Header: stuff\" - Additional header on the autoresponse\n"
78 		"    -M recipient       - format autoresponse as a DSN from 'recipient'\n"
79 		"    -n                 - only show the resulting message, do not send it\n"
80 		"    $MAILER arg arg... - run $MAILER (sendmail) to mail the autoresponse\n"
81 		);
82 	exit(EX_TEMPFAIL);
83 }
84 
read_headers(FILE * tmpfp)85 static void read_headers(FILE *tmpfp)
86 {
87 	char buf[BUFSIZ];
88 	struct header **lasthdr= &header_list, *prevhdr=0;
89 
90 	while (fgets(buf, sizeof(buf), tmpfp))
91 	{
92 		size_t l=strlen(buf);
93 
94 		if (l > 0 && buf[l-1] == '\n')
95 			--l;
96 		if (l > 0 && buf[l-1] == '\r')
97 			--l;
98 		buf[l]=0;
99 
100 		if (l == 0)
101 		{
102 			/* Eat rest of message from stdin */
103 
104 			while (getc(stdin) != EOF)
105 				;
106 			break;
107 		}
108 
109 		if (isspace((int)(unsigned char)buf[0]) && prevhdr)
110 		{
111 			if ( (prevhdr->buf=
112 			     realloc( prevhdr->buf,
113 				      strlen (prevhdr->buf)+2+strlen(buf)))
114 			     == NULL)
115 			{
116 				perror("malloc");
117 				exit(EX_TEMPFAIL);
118 			}
119 			strcat(strcat( prevhdr->buf, "\n"), buf);
120 		}
121 		else
122 		{
123 			if ((*lasthdr=(struct header *)
124 			     malloc(sizeof(struct header))) == NULL ||
125 			    ((*lasthdr)->buf=strdup(buf)) == NULL)
126 			{
127 				perror("malloc");
128 				exit(EX_TEMPFAIL);
129 			}
130 
131 			prevhdr= *lasthdr;
132 			lasthdr= &(*lasthdr)->next;
133 		}
134 	}
135 
136 	*lasthdr=NULL;
137 }
138 
hdr(const char * hdrname)139 const char *hdr(const char *hdrname)
140 {
141 	struct header *h;
142 	size_t l=strlen(hdrname);
143 
144 	for (h=header_list; h; h=h->next)
145 	{
146 		if (strncasecmp(h->buf, hdrname, l) == 0 &&
147 		    h->buf[l] == ':')
148 		{
149 			const char *p=h->buf+l+1;
150 
151 			while (*p && isspace((int)(unsigned char)*p))
152 				++p;
153 			return (p);
154 		}
155 	}
156 
157 	return ("");
158 }
159 
160 /*
161 ** Get the sender's address
162 */
163 
check_sender()164 static void check_sender()
165 {
166 	const char *h=hdr("reply-to");
167 	struct rfc822t *t;
168 	struct rfc822a *a;
169 
170 	if (!h || !*h)
171 		h=hdr("from");
172 
173 	if (!h || !*h)
174 		exit(0);
175 
176 	t=rfc822t_alloc_new(h, NULL, NULL);
177 
178 	if (!t || !(a=rfc822a_alloc(t)))
179 	{
180 		perror("malloc");
181 		exit(EX_TEMPFAIL);
182 	}
183 
184 	if (a->naddrs <= 0)
185 		exit (0);
186 	sender=rfc822_getaddr(a, 0);
187 	rfc822a_free(a);
188 	rfc822t_free(t);
189 
190 	if (!sender || !*sender)
191 		exit(0);
192 }
193 
194 /*
195 ** Do not autorespond to DSNs
196 */
197 
check_dsn()198 static void check_dsn()
199 {
200 	static const char ct[]="multipart/report;";
201 
202 	const char *p=hdr("content-type");
203 
204 	if (strncasecmp(p, ct, sizeof(ct)-1) == 0)
205 		exit(0);
206 
207 	p=hdr("precedence");
208 
209 	if (strncasecmp(p, "junk", 4) == 0 ||
210 	    strncasecmp(p, "bulk", 4) == 0 ||
211 	    strncasecmp(p, "list", 4) == 0)
212 		exit(0);	/* Just in case */
213 
214 	p=hdr("auto-submitted");
215 
216 	if (*p && strcmp(p, "no"))
217 		exit(0);
218 
219 	p=hdr("list-id");
220 
221 	if (*p)
222 		exit(0);
223 }
224 
225 /*
226 ** Check for a required recipient
227 */
228 
check_recips()229 static void check_recips()
230 {
231 	char *buf;
232 	struct rfc822t *t;
233 	struct rfc822a *a;
234 	struct header *h;
235 
236 	if (!recips || !*recips)
237 		return;
238 
239 	buf=strdup(recips);
240 	if (!buf)
241 	{
242 		perror("strdup");
243 		exit(EX_TEMPFAIL);
244 	}
245 
246 	for (h=header_list; h; h=h->next)
247 	{
248 		int i;
249 
250 		if (strncasecmp(h->buf, "to:", 3) &&
251 		    strncasecmp(h->buf, "cc:", 3))
252 			continue;
253 
254 		t=rfc822t_alloc_new(h->buf+3, NULL, NULL);
255 		if (!t || !(a=rfc822a_alloc(t)))
256 		{
257 			perror("malloc");
258 			exit(EX_TEMPFAIL);
259 		}
260 
261 		for (i=0; i<a->naddrs; i++)
262 		{
263 			char *p=rfc822_getaddr(a, i);
264 			char *q;
265 
266 			strcpy(buf, recips);
267 
268 			for (q=buf; (q=strtok(q, ", ")) != 0; q=0)
269 			{
270 				if (p && strcasecmp(p, q) == 0)
271 				{
272 					free(p);
273 					free(buf);
274 					rfc822a_free(a);
275 					rfc822t_free(t);
276 					return;
277 				}
278 			}
279 
280 			free(p);
281 		}
282 		rfc822a_free(a);
283 		rfc822t_free(t);
284 	}
285 	free(buf);
286 	exit(0);
287 }
288 
289 /*
290 ** Check the dupe database.
291 */
292 
293 #ifdef DbObj
check_db()294 static void check_db()
295 {
296 	char *dbname;
297 	char *lockname;
298 	int lockfd;
299 	struct dbobj db;
300 	time_t now;
301 	char *sender_key, *p;
302 
303 	size_t val_len;
304 	char *val;
305 
306 	if (!dbfile || !*dbfile)
307 		return;
308 
309 	sender_key=strdup(sender);
310 	dbname=malloc(strlen(dbfile)+ sizeof( "." DBNAME));
311 	lockname=malloc(strlen(dbfile)+ sizeof(".lock"));
312 
313 	for (p=sender_key; *p; p++)
314 		*p=tolower((int)(unsigned char)*p);
315 
316 	if (!dbname || !lockname || !sender)
317 	{
318 		perror("malloc");
319 		exit(EX_TEMPFAIL);
320 	}
321 
322 	strcat(strcpy(dbname, dbfile), "." DBNAME);
323 	strcat(strcpy(lockname, dbfile), ".lock");
324 
325 	lockfd=open(lockname, O_RDWR|O_CREAT, 0666);
326 
327 	if (lockfd < 0 || ll_lock_ex(lockfd))
328 	{
329 		perror(lockname);
330 		exit(EX_TEMPFAIL);
331 	}
332 
333 	dbobj_init(&db);
334 
335 	if (dbobj_open(&db, dbname, "C") < 0)
336 	{
337 		perror(dbname);
338 		exit(EX_TEMPFAIL);
339 	}
340 
341 	time(&now);
342 
343 	val=dbobj_fetch(&db, sender_key, strlen(sender_key), &val_len, "");
344 
345 	if (val)
346 	{
347 		time_t t;
348 
349 		if (val_len >= sizeof(t))
350 		{
351 			memcpy(&t, val, sizeof(t));
352 
353 			if (t >= now - interval * 60 * 60 * 24)
354 			{
355 				free(val);
356 				dbobj_close(&db);
357 				close(lockfd);
358 				exit(0);
359 			}
360 		}
361 		free(val);
362 	}
363 
364 	dbobj_store(&db, sender_key, strlen(sender_key),
365 		    (void *)&now, sizeof(now), "R");
366 	dbobj_close(&db);
367 	close(lockfd);
368 }
369 #endif
370 
opensendmail(int argn,int argc,char ** argv)371 static void opensendmail(int argn, int argc, char **argv)
372 {
373 	char **newargv;
374 	int i;
375 
376 	if (argn >= argc)
377 	{
378 		static char *sendmail_argv[]={"sendmail", "-f", ""};
379 
380 		argn=0;
381 		argc=3;
382 		argv=sendmail_argv;
383 	}
384 
385 	newargv=(char **)malloc( sizeof(char *)*(argc-argn+1));
386 	if (!newargv)
387 	{
388 		perror("malloc");
389 		exit(EX_TEMPFAIL);
390 	}
391 
392 	for (i=0; argn+i < argc; i++)
393 		newargv[i]=argv[argn+i];
394 	newargv[i]=0;
395 	signal(SIGCHLD, SIG_DFL);
396 
397 	execvp(newargv[0], newargv);
398 	perror(newargv[0]);
399 	exit(EX_TEMPFAIL);
400 }
401 
savemessage(FILE * tmpfp)402 static struct rfc2045 *savemessage(FILE *tmpfp)
403 {
404 	struct rfc2045 *rfcp=rfc2045_alloc();
405 	char buf[BUFSIZ];
406 	int n;
407 
408 	if (!rfcp)
409 	{
410 		perror("rfc2045_alloc");
411 		exit(1);
412 	}
413 
414 	while ((n=fread(buf, 1, sizeof(buf), stdin)) > 0)
415 	{
416 		if (fwrite(buf, n, 1, tmpfp) != 1)
417 		{
418 			perror("fwrite(tempfile)");
419 			exit(1);
420 		}
421 
422 		rfc2045_parse(rfcp, buf, n);
423 	}
424 
425 	if (n < 0)
426 	{
427 		perror("tempfile");
428 		exit(1);
429 	}
430 	return rfcp;
431 }
432 
433 
434 struct mimeautoreply_s {
435 	struct rfc2045_mkreplyinfo info;
436 	FILE *outf;
437 
438 	FILE *contentf;
439 };
440 
mimeautoreply_write_func(const char * str,size_t cnt,void * ptr)441 static void mimeautoreply_write_func(const char *str, size_t cnt, void *ptr)
442 {
443 	if (cnt &&
444 	    fwrite(str, cnt, 1, ((struct mimeautoreply_s *)ptr)->outf) != 1)
445 	{
446 		perror("tmpfile");
447 		exit(1);
448 	}
449 }
450 
mimeautoreply_writesig_func(void * ptr)451 static void mimeautoreply_writesig_func(void *ptr)
452 {
453 }
454 
mimeautoreply_myaddr_func(const char * addr,void * ptr)455 static int mimeautoreply_myaddr_func(const char *addr, void *ptr)
456 {
457 	return 0;
458 }
459 
copy_headers(void * ptr)460 static void copy_headers(void *ptr)
461 {
462 	struct mimeautoreply_s *p=(struct mimeautoreply_s *)ptr;
463 	char buf[BUFSIZ];
464 
465 	static const char ct[]="Content-Transfer-Encoding:";
466 
467 	while (fgets(buf, sizeof(buf), p->contentf) != NULL)
468 	{
469 		if (buf[0] == '\n')
470 			break;
471 
472 		if (strncasecmp(buf, ct, sizeof(ct)-1) == 0)
473 			continue;
474 
475 		mimeautoreply_write_func(buf, strlen(buf), ptr);
476 
477 		while (strchr(buf, '\n') == NULL)
478 		{
479 			if (fgets(buf, sizeof(buf), p->contentf) == NULL)
480 				break;
481 
482 			mimeautoreply_write_func(buf, strlen(buf), ptr);
483 		}
484 	}
485 }
486 
copy_body(void * ptr)487 static void copy_body(void *ptr)
488 {
489 	struct mimeautoreply_s *p=(struct mimeautoreply_s *)ptr;
490 	char buf[BUFSIZ];
491 
492 	while (fgets(buf, sizeof(buf), p->contentf) != NULL)
493 	{
494 		mimeautoreply_write_func(buf, strlen(buf), ptr);
495 	}
496 }
497 
main(int argc,char ** argv)498 int main(int argc, char **argv)
499 {
500 	int argn;
501 	FILE *tmpfp;
502 	struct rfc2045 *rfcp;
503 	struct mimeautoreply_s replyinfo;
504 	const char *subj=0;
505 	const char *txtfile=0, *mimefile=0;
506 	const char *mimedsn=0;
507 	int nosend=0;
508 	const char *replymode="reply";
509 	int replytoenvelope=0;
510 	int donotquote=0;
511 	const char *forwardsep="--- Forwarded message ---";
512 	const char *replysalut="%F writes:";
513 	const struct unicode_info *uinfo;
514 
515 	setlocale(LC_ALL, "");
516 	charset=strdup(nl_langinfo(CODESET));
517 
518 	if (!charset)
519 	{
520 		perror("malloc");
521 		exit(1);
522 	}
523 
524 	sender=NULL;
525 	for (argn=1; argn < argc; argn++)
526 	{
527 		char optc;
528 		char *optarg;
529 
530 		if (argv[argn][0] != '-')
531 			break;
532 
533 		if (strcmp(argv[argn], "--") == 0)
534 		{
535 			++argn;
536 			break;
537 		}
538 
539 		optc=argv[argn][1];
540 		optarg=argv[argn]+2;
541 
542 		if (!*optarg)
543 			optarg=NULL;
544 
545 		switch (optc) {
546 		case 'c':
547 			if (!optarg && argn+1 < argc)
548 				optarg=argv[++argn];
549 
550 			if (optarg && *optarg)
551 			{
552 				charset=strdup(optarg);
553 
554 				if (charset == NULL ||
555 				    unicode_find(charset) == NULL)
556 				{
557 					fprintf(stderr, "Unknown charset: %s\n",
558 						charset);
559 					exit(1);
560 				}
561 			}
562 			continue;
563 		case 't':
564 			if (!optarg && argn+1 < argc)
565 				optarg=argv[++argn];
566 
567 			txtfile=optarg;
568 			continue;
569 		case 'm':
570 			if (!optarg && argn+1 < argc)
571 				optarg=argv[++argn];
572 
573 			mimefile=optarg;
574 			continue;
575 		case 'r':
576 			if (!optarg && argn+1 < argc)
577 				optarg=argv[++argn];
578 
579 			recips=optarg;
580 			continue;
581 		case 'M':
582 			if (!optarg && argn+1 < argc)
583 				optarg=argv[++argn];
584 
585 			mimedsn=optarg;
586 			continue;
587 		case 'd':
588 			if (!optarg && argn+1 < argc)
589 				optarg=argv[++argn];
590 
591 			dbfile=optarg;
592 			continue;
593 		case 'e':
594 			replytoenvelope=1;
595 			continue;
596 		case 'T':
597 			if (!optarg && argn+1 < argc)
598 				optarg=argv[++argn];
599 
600 			if (optarg && *optarg)
601 				replymode=optarg;
602 			continue;
603 		case 'N':
604 			donotquote=1;
605 			continue;
606 		case 'F':
607 			if (!optarg && argn+1 < argc)
608 				optarg=argv[++argn];
609 
610 			if (optarg && *optarg)
611 				forwardsep=optarg;
612 			continue;
613 		case 'S':
614 			if (!optarg && argn+1 < argc)
615 				optarg=argv[++argn];
616 
617 			if (optarg && *optarg)
618 				replysalut=optarg;
619 			continue;
620 		case 'D':
621 			if (!optarg && argn+1 < argc)
622 				optarg=argv[++argn];
623 
624 			interval=optarg ? atoi(optarg):1;
625 			continue;
626 		case 'A':
627 			if (!optarg && argn+1 < argc)
628 				optarg=argv[++argn];
629 
630 			if (optarg)
631 			{
632 				struct header **h;
633 
634 				for (h= &extra_headers; *h;
635 				     h= &(*h)->next)
636 					;
637 
638 				if ((*h=malloc(sizeof(struct header))) == 0 ||
639 				    ((*h)->buf=strdup(optarg)) == 0)
640 				{
641 					perror("malloc");
642 					exit(EX_TEMPFAIL);
643 				}
644 				(*h)->next=0;
645 			}
646 			continue;
647 		case 's':
648 			if (!optarg && argn+1 < argc)
649 				optarg=argv[++argn];
650 
651 			subj=optarg;
652 			continue;
653 
654 		case 'f':
655 			if (optarg && *optarg)
656 			{
657 				sender=strdup(optarg);
658 			}
659 			else
660 			{
661 				sender=getenv("SENDER");
662 				if (!sender)
663 					continue;
664 				sender=strdup(sender);
665 			}
666 			if (sender == NULL)
667 			{
668 				perror("malloc");
669 				exit(1);
670 			}
671 			continue;
672 		case 'n':
673 			nosend=1;
674 			continue;
675 		default:
676 			usage();
677 		}
678 	}
679 
680 	if (!txtfile && !mimefile)
681 		usage();
682 
683 	if (txtfile && mimefile)
684 		usage();
685 
686 	tmpfp=tmpfile();
687 
688 	if (!tmpfp)
689 	{
690 		perror("tmpfile");
691 		exit(1);
692 	}
693 
694 	rfcp=savemessage(tmpfp);
695 
696 	if (fseek(tmpfp, 0L, SEEK_SET) < 0)
697 	{
698 		perror("fseek(tempfile)");
699 		exit(1);
700 	}
701 
702 	read_headers(tmpfp);
703 
704 	if (sender == NULL || *sender == 0)
705 		check_sender();
706 
707 	check_dsn();
708 	check_recips();
709 #ifdef DbObj
710 	check_db();
711 #endif
712 
713 	uinfo=unicode_find(charset);
714 
715 	memset(&replyinfo, 0, sizeof(replyinfo));
716 
717 	replyinfo.info.fd=fileno(tmpfp);
718 	replyinfo.info.rfc2045partp=rfcp;
719 	replyinfo.info.voidarg=&replyinfo;
720 
721 	replyinfo.info.write_func=mimeautoreply_write_func;
722 
723 	replyinfo.info.writesig_func=mimeautoreply_writesig_func;
724 
725 	replyinfo.info.myaddr_func=mimeautoreply_myaddr_func;
726 
727 	replyinfo.info.replymode=replymode;
728 	replyinfo.info.replytoenvelope=replytoenvelope;
729 	replyinfo.info.donotquote=donotquote;
730 
731 	replyinfo.info.replysalut=replysalut;
732 	replyinfo.info.forwarddescr="Forwarded message";
733 	replyinfo.info.mailinglists="";
734 	replyinfo.info.charset=(uinfo ? uinfo:&unicode_ISO8859_1)->chset;
735 	replyinfo.info.subject=subj;
736 	replyinfo.info.forwardsep=forwardsep;
737 
738 	if (mimedsn && *mimedsn)
739 	{
740 		replyinfo.info.dsnfrom=mimedsn;
741 		replyinfo.info.replymode="replydsn";
742 	}
743 
744 	if (mimefile)
745 	{
746 		if ((replyinfo.contentf=fopen(mimefile, "r")) == NULL)
747 		{
748 			perror(mimefile);
749 			exit(1);
750 		}
751 
752 		{
753 			struct rfc2045 *rfcp=rfc2045_alloc();
754 			static const char mv[]="Mime-Version: 1.0\n";
755 			char buf[BUFSIZ];
756 			int l;
757 			const char *content_type;
758 			const char *content_transfer_encoding;
759 			const char *charset;
760 
761 			rfc2045_parse(rfcp, mv, sizeof(mv)-1);
762 
763 			while ((l=fread(buf, 1, sizeof(buf), replyinfo.contentf)
764 				) > 0)
765 			{
766 				rfc2045_parse(rfcp, buf, l);
767 			}
768 
769 			if (l < 0 ||
770 			    fseek(replyinfo.contentf, 0L, SEEK_SET) < 0)
771 			{
772 				perror(mimefile);
773 				exit(1);
774 			}
775 
776 			rfc2045_mimeinfo(rfcp, &content_type,
777 					 &content_transfer_encoding,
778 					 &charset);
779 
780 			if (strcasecmp(content_type, "text/plain"))
781 			{
782 				fprintf(stderr,
783 					"%s must specify text/plain MIME type\n",
784 					mimefile);
785 				exit(1);
786 			}
787 			if (charset == NULL ||
788 			    (uinfo=unicode_find(charset)) == NULL)
789 			{
790 				fprintf(stderr, "Unknown charset in %s\n",
791 					mimefile);
792 				exit(1);
793 			}
794 			replyinfo.info.charset=uinfo->chset;
795 			rfc2045_free(rfcp);
796 		}
797 		replyinfo.info.content_set_charset=copy_headers;
798 		replyinfo.info.content_specify=copy_body;
799 	}
800 	else if (txtfile)
801 	{
802 		if ((replyinfo.contentf=fopen(txtfile, "r")) == NULL)
803 		{
804 			perror(mimefile);
805 			exit(1);
806 		}
807 		replyinfo.info.content_specify=copy_body;
808 	}
809 
810 	if (replyinfo.contentf)
811 		fcntl(fileno(replyinfo.contentf), F_SETFD, FD_CLOEXEC);
812 
813 	if (nosend)
814 		replyinfo.outf=stdout;
815 	else
816 	{
817 		replyinfo.outf=tmpfile();
818 
819 		if (replyinfo.outf == NULL)
820 		{
821 			perror("tmpfile");
822 			exit(1);
823 		}
824 	}
825 
826 	{
827 		struct header *h;
828 
829 		for (h=extra_headers; h; h=h->next)
830 			fprintf(replyinfo.outf, "%s\n", h->buf);
831 	}
832 	fprintf(replyinfo.outf,
833 		"Precedence: junk\n"
834 		"Auto-Submitted: auto-replied\n");
835 
836 	if (rfc2045_makereply_unicode(&replyinfo.info) < 0 ||
837 	    fflush(replyinfo.outf) < 0 || ferror(replyinfo.outf) ||
838 	    (!nosend &&
839 	     (
840 	      fseek(replyinfo.outf, 0L, SEEK_SET) < 0 ||
841 	      (close(0), dup(fileno(replyinfo.outf))) < 0)
842 	     ))
843 	{
844 		perror("tempfile");
845 		exit(1);
846 	}
847 	fclose(replyinfo.outf);
848 	fcntl(0, F_SETFD, 0);
849 
850 	free(charset);
851 	rfc2045_free(rfcp);
852 
853 	if (!nosend)
854 		opensendmail(argn, argc, argv);
855 	return (0);
856 }
857