1 /*
2 ** Copyright 1998 - 2009 Double Precision, Inc.
3 ** See COPYING for distribution information.
4 */
5 
6 #include	"config.h"
7 
8 #include	<stdio.h>
9 #include	<iostream>
10 #include	<iomanip>
11 #include	<stdlib.h>
12 #include	<string.h>
13 #include	<ctype.h>
14 #include	<signal.h>
15 #include	<pwd.h>
16 
17 #if	HAVE_LOCALE_H
18 #include	<locale.h>
19 #endif
20 
21 #if HAVE_UNISTD_H
22 #include	<unistd.h>
23 #endif
24 #if HAVE_FCNTL_H
25 #include	<fcntl.h>
26 #endif
27 #include	"mytime.h"
28 #include	<sys/types.h>
29 #include	"mywait.h"
30 #if HAVE_SYS_FILE_H
31 #include	<sys/file.h>
32 #endif
33 #include	"rfc822.h"
34 #include	"buffer.h"
35 #include	"liblock/config.h"
36 #include	"liblock/liblock.h"
37 
38 #if	HAS_GETHOSTNAME
39 #else
40 extern "C" int gethostname(const char *, size_t);
41 #endif
42 
43 static const char rcsid[]="$Id: reformail.C,v 1.23 2009/11/22 18:46:53 mrsam Exp $";
44 
45 static int inbody=0, addcrs=0, catenate=0;
46 static const char *(*append_more_headers)();
47 static Buffer	optx, optX, opta, optA, opti, optI, optu, optU, optubuf, optUbuf, optR;
48 
49 static Buffer add_from_filter_buf;
50 static const char *add_from_filter_buf_ptr;
51 static const char *cache_maxlen="", *cache_name="";
52 
53 static const char *( *from_filter)();
54 static Buffer	current_line;
55 
outofmem()56 void outofmem()
57 {
58 	std::cerr << "reformail: Out of memory." << std::endl;
59 	exit(1);
60 }
61 
help()62 void help()
63 {
64 	std::cerr << "reformail: Invalid arguments." << std::endl;
65 	exit(1);
66 }
67 
68 // Return next line from standard input.  Trailing CRs are stripped
69 
NextLine()70 const char *NextLine()
71 {
72 static Buffer buf;
73 int	c;
74 
75 	buf.reset();
76 	while ((c=std::cin.get()) >= 0 && c != '\n')
77 		buf.push(c);
78 	if (c < 0 && buf.Length() == 0)	return (0);
79 	if (buf.Length())	// Strip CRs
80 	{
81 		c=buf.pop();
82 		if (c != '\r')	buf.push(c);
83 	}
84 	buf.push('\n');
85 	buf.push('\0');
86 	return (buf);
87 }
88 
89 // from_filter is the initial filtering done on the message.
90 // from_filter adds or subtracts "From " quoting from the message.
91 // from_filter returns the next line from the message, after filtering.
92 // The line is always terminated by a newline character.
93 // When the header is being read, multiline headers are silently
94 // concatenated into a single return from from_filter() (separated by
95 // newlines, of course.
96 //
97 // from_filter is initialized to either from_filter(), add_from_filter()
98 // and rem_from_filter() respectively, to cause either addition, removal of,
99 // or no change to from quoting.  The pointer is automatically updated.
100 //
101 // Also, the from_filter silently discards empty lines at the beginning of
102 // the message.
103 
104 // DO NOT CHANGE FROM QUOTING.
105 
106 const char *no_from_filter_header();
107 
no_from_filter()108 const char *no_from_filter()
109 {
110 const	char *p;
111 
112 	while ((p=NextLine()) && *p == '\n')
113 		;
114 	if (!p)	return (0);
115 
116 	current_line=p;
117 	return ( no_from_filter_header() );
118 }
119 
120 const char *read_blank();
121 
no_from_filter_header()122 const char *no_from_filter_header()
123 {
124 const	char *p;
125 
126 static Buffer buf;
127 
128 	from_filter= &no_from_filter_header;
129 
130 	while ((p=NextLine()) && *p && *p != '\n' && isspace((unsigned char)*p))
131 		current_line += p;
132 
133 	buf=current_line;
134 	buf+='\0';
135 	if (!p || *p == '\n')
136 	{
137 		from_filter= &read_blank;
138 		return (buf);
139 	}
140 	current_line=p;
141 	return (buf);
142 }
143 
read_blank()144 const char *read_blank()
145 {
146 	from_filter= &NextLine;
147 	return ("\n");
148 }
149 
150 //////////////////////////////////////////////////////////////////////////
151 //
152 //  Add 'From ' quoting.  All headers are read into add_from_filter_buf,
153 //  and a suitable return address is located.  The 'From ' line is
154 //  generated, and return.  Subsequent calls fetch one header at a
155 //  time from add_from_filter_buf, then resume reading the body of the
156 //  message.
157 //
158 //////////////////////////////////////////////////////////////////////////
159 
160 const char *add_from_filter_header();
add_from_filter()161 const char *add_from_filter()
162 {
163 const	char *p;
164 int n;
165 	while ((p=NextLine()) && *p == '\n')
166 		;
167 	if (!p)	return (0);
168 
169 	current_line=p;
170 	if (strncmp(p, "From ", 5) == 0)
171 		return ( no_from_filter_header() );
172 	add_from_filter_buf.reset();
173 	while (p && *p != '\n')
174 	{
175 		add_from_filter_buf += p;
176 		p=NextLine();
177 	}
178 
179 	add_from_filter_buf += '\0';
180 
181 static Buffer	return_path;
182 static Buffer	from_header;
183 
184 	return_path.reset();
185 	from_header.reset();
186 
187 	for (p=add_from_filter_buf; *p; )
188 	{
189 	Buffer	header;
190 
191 		while (*p && *p != ':' && *p != '\n')
192 		{
193 		int	c= (unsigned char)*p++;
194 
195 			c=tolower(c);
196 			header.push(c);
197 		}
198 		for (;;)
199 		{
200 			while (*p && *p != '\n')
201 			{
202 				header.push(*p);
203 				p++;
204 			}
205 			if (!*p)	break;
206 			++p;
207 			header.push('\n');
208 			if (!*p || !isspace((unsigned char)*p))	break;
209 		}
210 		header.push('\0');
211 		if (strncmp(header, "return-path:", 12) == 0 ||
212 			strncmp(header, ">return-path:", 13) == 0 ||
213 			strncmp(header, "errors-to:", 10) == 0 ||
214 			strncmp(header, ">errors-to:", 11) == 0)
215 		{
216 			for (p=header; *p != ':'; p++)
217 				;
218 			return_path=p;
219 		}
220 
221 		if (strncmp(header, "from:", 5) == 0)
222 			from_header=(const char *)header + 5;
223 	}
224 	if (return_path.Length() == 0)	return_path=from_header;
225 	return_path += '\0';
226 
227 	struct rfc822t *rfc=rfc822t_alloc_new( (const char *)return_path,
228 					       NULL, NULL);
229 
230 	if (!rfc)	outofmem();
231 
232 	struct rfc822a *rfca=rfc822a_alloc( rfc);
233 
234 	if (!rfca)	outofmem();
235 
236 	from_header.reset();
237 
238 	for (n=0; n<rfca->naddrs; ++n)
239 	{
240 		if (rfca->addrs[n].tokens)
241 		{
242 			char *p=rfc822_display_addr_tobuf(rfca, n, NULL);
243 
244 			if (p)
245 			{
246 				try {
247 					from_header=p;
248 				} catch (...)
249 				{
250 					free(p);
251 					throw;
252 				}
253 				free(p);
254 				break;
255 			}
256 		}
257 	}
258 
259 	rfc822a_free(rfca);
260 	rfc822t_free(rfc);
261 
262 	if (from_header.Length() == 0)	from_header="root";
263 	return_path="From ";
264 	return_path += from_header;
265 	return_path.push(' ');
266 time_t	t;
267 
268 	time(&t);
269 	p=ctime(&t);
270 	while (*p && *p != '\n')
271 	{
272 		return_path.push(*p);
273 		p++;
274 	}
275 	return_path += '\n';
276 	return_path += '\0';
277 	from_filter=add_from_filter_header;
278 	add_from_filter_buf_ptr=add_from_filter_buf;
279 	return (return_path);
280 }
281 
282 const char *add_from_filter_body();
283 
add_from_filter_header()284 const char *add_from_filter_header()
285 {
286 static Buffer buf;
287 
288 	buf.reset();
289 
290 	if (*add_from_filter_buf_ptr == '\0')
291 	{
292 		from_filter= &add_from_filter_body;
293 		return ("\n");
294 	}
295 
296 	do
297 	{
298 		while (*add_from_filter_buf_ptr)
299 		{
300 			buf.push ( (unsigned char)*add_from_filter_buf_ptr );
301 			if ( *add_from_filter_buf_ptr++ == '\n')	break;
302 		}
303 	} while ( *add_from_filter_buf_ptr && *add_from_filter_buf_ptr != '\n'
304 		&& isspace( (unsigned char)*add_from_filter_buf_ptr ));
305 	buf += '\0';
306 	return (buf);
307 }
308 
add_from_filter_body()309 const char *add_from_filter_body()
310 {
311 const char *p=NextLine();
312 
313 	if (!p)	return (p);
314 
315 const char *q;
316 
317 	for (q=p; *q == '>'; q++)
318 		;
319 	if (strncmp(q, "From ", 5))	return (p);
320 
321 static Buffer add_from_buf;
322 
323 	add_from_buf=">";
324 	add_from_buf += p;
325 	add_from_buf += '\0';
326 	return (add_from_buf);
327 }
328 
329 ////////////////////////////////////////////////////////////////////////////
330 //
331 // Strip From quoting.
332 //
333 ////////////////////////////////////////////////////////////////////////////
334 
335 const char *rem_from_filter_header();
336 
337 const char *(*rem_from_filter_header_ptr)();
338 
rem_from_filter()339 const char *rem_from_filter()
340 {
341 const	char *p;
342 
343 	while ((p=NextLine()) && *p == '\n')
344 		;
345 	if (!p)	return (0);
346 
347 	if (strncmp(p, "From ", 5))
348 	{
349 		current_line=p;
350 		return ( no_from_filter_header() );
351 	}
352 	p=NextLine();
353 	if (!p)	return (p);
354 	current_line=p;
355 	rem_from_filter_header_ptr= &no_from_filter_header;
356 	return ( rem_from_filter_header() );
357 }
358 
359 const char *rem_from_filter_body();
rem_from_filter_header()360 const char *rem_from_filter_header()
361 {
362 const char *p=(*rem_from_filter_header_ptr)();
363 
364 	rem_from_filter_header_ptr=from_filter;
365 	from_filter=rem_from_filter_header;
366 	if (!p || *p == '\n')
367 	{
368 		from_filter=&rem_from_filter_body;
369 		p="\n";
370 	}
371 	return (p);
372 }
373 
rem_from_filter_body()374 const char *rem_from_filter_body()
375 {
376 const char *p=NextLine();
377 
378 	if (!p)	return (p);
379 
380 	if (*p == '>')
381 	{
382 	const char *q;
383 
384 		for (q=p; *q == '>'; q++)
385 			;
386 		if (strncmp(q, "From ", 5) == 0)	++p;
387 	}
388 	return (p);
389 }
390 
HostName()391 static const char *HostName()
392 {
393 static char hostname_buf[256];
394 
395 	hostname_buf[0]=0;
396 	hostname_buf[sizeof(hostname_buf)-1]=0;
397 	gethostname(hostname_buf, sizeof(hostname_buf)-1);
398 	return (hostname_buf);
399 }
400 
401 ////////////////////////////////////////////////////////////////////////////
402 //
403 // Return TRUE if header is already in a list of headers.
404 //
405 // hdrs: null separated list of headers (and header contents)
406 // hdr - header to check (must be lowercase and terminated by a colon)
407 // pos - offset into hdrs where it's found.
408 //
409 
has_hdr(const Buffer & hdrs,const char * hdr,unsigned & pos)410 static int has_hdr(const Buffer &hdrs, const char *hdr, unsigned &pos)
411 {
412 const char *r=hdrs;
413 int l=hdrs.Length();
414 Buffer	buf2;
415 unsigned pos2=0;
416 
417 	while (l)
418 	{
419 		buf2.reset();
420 		pos=pos2;
421 		while (l)
422 		{
423 			--l;
424 			++pos2;
425 			buf2.push( tolower(*r));
426 			if (*r++ == 0)	break;
427 		}
428 		buf2.push('\0');
429 		if (strncmp(hdr, buf2, strlen(hdr)) == 0)	return (1);
430 	}
431 	return (0);
432 }
433 
has_hdr(const Buffer & hdrs,const char * hdr)434 static int has_hdr(const Buffer &hdrs, const char *hdr)
435 {
436 unsigned dummy;
437 
438 	return (has_hdr(hdrs, hdr, dummy));
439 }
440 
strip_empty_header(Buffer & buf)441 static void strip_empty_header(Buffer &buf)
442 {
443 Buffer	newbuf;
444 int l;
445 const char *p;
446 
447 	for (p=buf, l=buf.Length(); l; )
448 	{
449 		if (p[strlen(p)-1] == ':')
450 		{
451 			while (l)
452 			{
453 				--l;
454 				if (*p++ == '\0')	break;
455 			}
456 			continue;
457 		}
458 		while (l)
459 		{
460 			--l;
461 			newbuf.push( *p );
462 			if (*p++ == '\0')	break;
463 		}
464 	}
465 	buf=newbuf;
466 }
467 
strip_header(Buffer & header,unsigned offset)468 static void strip_header(Buffer &header, unsigned offset)
469 {
470 Buffer	buf1;
471 const char *p=header;
472 int l=header.Length();
473 
474 	while (l)
475 	{
476 		if (!offset)
477 		{
478 			while (l)
479 			{
480 				--l;
481 				if (*p++ == '\0')	break;
482 			}
483 			break;
484 		}
485 		buf1.push( *p++ );
486 		--l;
487 		--offset;
488 	}
489 	while (l--)
490 		buf1.push( *p++ );
491 	header=buf1;
492 }
493 
494 const char *ReadLineAddNewHeader();
495 
ReadLineAddHeader()496 const char *ReadLineAddHeader()
497 {
498 Buffer	buf1;
499 const char *q;
500 const char *p;
501 unsigned pos;
502 static Buffer oldbuf;
503 
504 	for (;;)
505 	{
506 		p= (*from_filter)();
507 
508 		if (!p)	return p;
509 		if (*p == '\n')
510 		{
511 			strip_empty_header(opti);
512 			strip_empty_header(optI);
513 			return ( ReadLineAddNewHeader());
514 		}
515 		buf1.reset();
516 		for (q=p; *q && *q != '\n'; q++)
517 		{
518 			buf1.push( tolower(*q) );
519 			if (*q == ':')	break;
520 		}
521 		buf1 += '\0';
522 
523 		if (has_hdr(opti, buf1))
524 		{
525 			oldbuf="old-";
526 			oldbuf += buf1;
527 			buf1=oldbuf;
528 
529 		Buffer	tbuf;
530 
531 			tbuf="Old-";
532 			tbuf += p;
533 			oldbuf=tbuf;
534 			oldbuf += '\0';
535 			p=oldbuf;
536 		}
537 		if (has_hdr(optR, buf1, pos))
538 		{
539 		Buffer	tbuf;
540 
541 			q=optR;
542 			q += pos + strlen(buf1);
543 			tbuf=q;
544 
545 			p += strlen(buf1);
546 			tbuf += p;
547 			oldbuf=tbuf;
548 			oldbuf += '\0';
549 			p=oldbuf;
550 		}
551 
552 		if (has_hdr(optI, buf1))
553 			continue;
554 		if (has_hdr(optu, buf1))
555 		{
556 			if (!has_hdr(optubuf, buf1))
557 			{
558 				q=p;
559 				do
560 				{
561 					optubuf.push( *q );
562 				} while (*q++);
563 				break;
564 			}
565 			continue;
566 		}
567 
568 		if (has_hdr(optU, buf1))
569 		{
570 			if (has_hdr(optUbuf, buf1, pos))
571 				strip_header(optUbuf, pos);
572 			while (*p)
573 			{
574 				optUbuf.push( *p );
575 				p++;
576 			}
577 			optUbuf.pop();
578 			optUbuf.push('\0');
579 			continue;
580 		}
581 		break;
582 	}
583 
584 unsigned offset;
585 
586 	if (has_hdr(opta, buf1, offset))
587 		strip_header(opta, offset);
588 	return (p);
589 }
590 
591 const char *ReadLineAddNewHeaderDone();
592 
ReadLineAddNewHeader()593 const char *ReadLineAddNewHeader()
594 {
595 	append_more_headers= &ReadLineAddNewHeader;
596 
597 Buffer	*bufptr;
598 
599 	if (opta.Length())	bufptr= &opta;
600 	else if (optA.Length())	bufptr= &optA;
601 	else if (opti.Length())	bufptr= &opti;
602 	else if (optI.Length())	bufptr= &optI;
603 	else if (optUbuf.Length())	bufptr= &optUbuf;
604 	else
605 	{
606 		append_more_headers=&ReadLineAddNewHeaderDone;
607 		return ("\n");
608 	}
609 
610 static Buffer buf1;
611 Buffer	buf2;
612 
613 	buf1.reset();
614 
615 const char *p= *bufptr;
616 int l= bufptr->Length();
617 
618 	while (l)
619 	{
620 		if ( !*p )
621 		{
622 			p++;
623 			l--;
624 			break;
625 		}
626 		buf1.push( *p );
627 		p++;
628 		l--;
629 	}
630 	buf1.push('\n');
631 	buf1.push('\0');
632 
633 	while (l--)
634 		buf2.push (*p++);
635 	*bufptr=buf2;
636 	return (buf1);
637 }
638 
ReadLineAddNewHeaderDone()639 const char *ReadLineAddNewHeaderDone()
640 {
641 	return ( (*from_filter)() );
642 }
643 
644 ////////////////////////////////////////////////////////////////////////////
ReadLine()645 const char *ReadLine()
646 {
647 const char *p=(*append_more_headers)();
648 
649 	if (!p)	return (p);
650 
651 static Buffer	buf;
652 
653 	if (*p == '\n')
654 		inbody=1;
655 
656 	if (catenate && !inbody)
657 	{
658 	const char *q;
659 
660 		buf.reset();
661 		for (q=p; *q; q++)
662 		{
663 			if (*q != '\n')
664 			{
665 				buf.push(*q);
666 				continue;
667 			}
668 			do
669 			{
670 				++q;
671 			} while (*q && isspace(*q));
672 			if (*q)
673 				buf.push(' ');
674 			--q;
675 		}
676 		if (addcrs)	buf.push('\r');
677 		buf.push('\n');
678 		buf.push('\0');
679 		return (buf);
680 	}
681 
682 	if (addcrs)
683 	{
684 		buf=p;
685 		buf.pop();
686 		buf += "\r\n";
687 		buf += '\0';
688 		return (buf);
689 	}
690 	return (p);
691 }
692 
693 /////////////////////////////////////////////////////////////////////////
694 //
695 // Default activity: just copy the message (let the low-level format
696 // filters do their job.
697 //
698 /////////////////////////////////////////////////////////////////////////
699 
copy(int,char * [],int)700 void copy(int, char *[], int)
701 {
702 const char *p;
703 
704 	while ((p= ReadLine()) != 0)
705 		std::cout << p;
706 }
707 
cache(int,char * [],int)708 void cache(int, char *[], int)
709 {
710 const char *p;
711 Buffer	buf;
712 int found=0;
713 
714 	addcrs=0;
715 	while ((p= ReadLine()) != 0)
716 	{
717 	int	c;
718 
719 		if (inbody)	break;
720 		buf.reset();
721 		while (*p && *p != '\n')
722 		{
723 			c= (unsigned char)*p;
724 			c=tolower(c);
725 			buf.push(c);
726 			if (*p++ == ':')	break;
727 		}
728 		if (!(buf == "message-id:"))	continue;
729 		buf += '\0';
730 		while (*p && isspace( (unsigned char)*p))	p++;
731 		buf.reset();
732 		while (*p)
733 		{
734 			buf.push(*p);
735 			++p;
736 		}
737 
738 		while ( (c=(unsigned char)buf.pop()) != 0 && isspace(c))
739 			;
740 		if (c)	buf.push(c);
741 		if (buf.Length() == 0)	break;
742 		buf.push('\0');
743 
744 	int	fd=open(cache_name, O_RDWR | O_CREAT, 0600);
745 
746 		if (fd < 0)
747 		{
748 			perror("open");
749 			exit(75);
750 		}
751 
752 		if (ll_lock_ex(fd) < 0)
753 		{
754 			perror("lock");
755 			exit(75);
756 		}
757 
758 	off_t	pos=0;
759 
760 		if (lseek(fd, 0L, SEEK_END) == -1 ||
761 			(pos=lseek(fd, 0L, SEEK_CUR)) == -1 ||
762 			lseek(fd, 0L, SEEK_SET) == -1)
763 		{
764 			perror("seek");
765 			exit(75);
766 		}
767 
768 	off_t	maxlen_n=atol(cache_maxlen);
769 	char	*charbuf;
770 	off_t	newpos=maxlen_n;
771 
772 		if (newpos < pos)	newpos=pos;
773 
774 		if ((charbuf=new char[newpos+buf.Length()+1]) == NULL)
775 			outofmem();
776 
777 	off_t	readcnt=read(fd, charbuf, newpos);
778 
779 		if (readcnt < 0)	perror("read");
780 
781 	char *q, *r;
782 
783 		for (q=r=charbuf; q<charbuf+readcnt; )
784 		{
785 			if (*q == '\0')	break;	// Double null terminator
786 			if (strcmp( (const char *)buf, q) == 0)
787 			{
788 				found=1;
789 				while (q < charbuf+readcnt)
790 					if (*q++ == '\0')	break;
791 			}
792 			else while (q < charbuf+readcnt)
793 				if ( (*r++=*q++) == '\0') break;
794 		}
795 		memcpy(r, (const char *)buf, buf.Length());
796 		r += buf.Length();
797 		for (q=charbuf; q<r; )
798 		{
799 			if (r - q < maxlen_n)
800 				break;
801 			while (q < r)
802 				if (*q++ == '\0')	break;
803 		}
804 		if (q == r)	q=charbuf;
805 		*r++ = '\0';
806 		if (lseek(fd, 0L, SEEK_SET) == -1)
807 		{
808 			perror("lseek");
809 			exit(1);
810 		}
811 		while (q < r)
812 		{
813 			readcnt=write(fd, q, r-q);
814 			if (readcnt == -1)
815 			{
816 				perror("write");
817 				exit(1);
818 			}
819 			q += readcnt;
820 		}
821 		close(fd);
822 		delete[] charbuf;
823 		break;
824 	}
825 	while ((p= ReadLine()) != 0)
826 		;
827 	exit(found ? 0:1);
828 }
829 
830 //////////////////////////////////////////////////////////////////////////
831 //
832 // Extract headers
833 
extract_headers(int,char * [],int)834 void extract_headers(int, char *[], int)
835 {
836 const char *p, *q;
837 Buffer	b;
838 
839 	catenate=1;
840 	while ((p=ReadLine()) && !inbody)
841 	{
842 		b.reset();
843 		for (q=p; *q && *q != '\n'; )
844 		{
845 		int	c= (unsigned char)*q;
846 
847 			b.push( tolower(c) );
848 			if ( *q++ == ':')	break;
849 		}
850 		b.push(0);
851 
852 		if (has_hdr(optx, b))
853 		{
854 			while (*q && *q != '\n' && isspace(*q))
855 				q++;
856 			std::cout << q;
857 			continue;
858 		}
859 
860 		if (has_hdr(optX, b))
861 		{
862 			std::cout << p;
863 			continue;
864 		}
865 	}
866 	if (!std::cin.seekg(0, std::ios::end).fail())
867 		return;
868 	std::cin.clear();
869 
870 	while ( ReadLine() )
871 		;
872 }
873 //////////////////////////////////////////////////////////////////////////
874 //
875 // Split mbox file into messages.
876 
split(int argc,char * argv[],int argn)877 void split(int argc, char *argv[], int argn)
878 {
879 const char *p;
880 Buffer	buf;
881 int	l;
882 int	do_environ=1;
883 unsigned long	environ=0;
884 unsigned	environ_len=3;
885 const	char *env;
886 
887 	if (argn >= argc)	help();
888 
889 	while ( (p=NextLine()) && *p == '\n')
890 		;
891 
892 	signal(SIGCHLD, SIG_DFL);
893 	signal(SIGPIPE, SIG_IGN);
894 	env=getenv("FILENO");
895 	if (env)
896 	{
897 	const char *q;
898 
899 		for (q=env; *q; q++)
900 			if (!isdigit(*q))	break;
901 		if (*q)	do_environ=0;
902 		else
903 		{
904 			environ_len=strlen(env);
905 			environ=atol(env);
906 		}
907 	}
908 
909 	while (p)
910 	{
911 	int	fds[2];
912 
913 		if (pipe(fds) < 0)
914 		{
915 			std::cerr << "reformail: pipe() failed." << std::endl;
916 			exit(1);
917 		}
918 
919 	pid_t	pid=fork();
920 
921 		if (pid == -1)
922 		{
923 			std::cerr << "reformail: fork() failed." << std::endl;
924 			exit(1);
925 		}
926 
927 		if (pid == 0)
928 		{
929 			dup2(fds[0], 0);
930 			close(fds[0]);
931 			close(fds[1]);
932 
933 		Buffer	buf, buf2;
934 
935 			if (do_environ)
936 			{
937 			char	*s;
938 
939 				while (environ || environ_len)
940 				{
941 					buf.push( "0123456789"[environ % 10]);
942 					environ /= 10;
943 					if (environ_len)	--environ_len;
944 				}
945 
946 				buf2="FILENO=";
947 				while (buf.Length())
948 					buf2.push(buf.pop());
949 				buf2 += '\0';
950 				s=strdup(buf2);
951 				if (!s)
952 				{
953 					perror("strdup");
954 					exit (1);
955 				}
956 				putenv(s);
957 			}
958 
959 			execvp( argv[argn], argv+argn);
960 			std::cerr << "reformail: exec() failed." << std::endl;
961 			exit(1);
962 		}
963 		close(fds[0]);
964 		environ++;
965 
966 		do
967 		{
968 			buf=p;
969 			p=ReadLine();
970 			if (!p || strncmp(p, "From ", 5) == 0)
971 				buf.pop();	// Drop trailing newline
972 			else
973 			{
974 				if (addcrs)
975 				{
976 					buf.pop();
977 					buf.push('\r');
978 					buf.push('\n');
979 				}
980 			}
981 
982 		const char *q=buf;
983 
984 			l=buf.Length();
985 			while (l)
986 			{
987 			int	n= ::write( fds[1], q, l);
988 				if (n <= 0)
989 				{
990 					std::cerr
991 					  << "reformail: write() failed."
992 					  << std::endl;
993 					exit(1);
994 				}
995 				q += n;
996 				l -= n;
997 			}
998 		} while (p && strncmp(p, "From ", 5));
999 		close(fds[1]);
1000 
1001 	int	wait_stat;
1002 
1003 		while ( wait(&wait_stat) != pid )
1004 			;
1005 		if (!WIFEXITED(wait_stat) || WEXITSTATUS(wait_stat))
1006 			break;	// Rely on diagnostic from child
1007 	}
1008 }
1009 
1010 //////////////////////////////////////////////////////////////////////////////
1011 
add_bin64(Buffer & buf,unsigned long n)1012 static void add_bin64(Buffer &buf, unsigned long n)
1013 {
1014 int	i;
1015 
1016 	for (i=0; i<16; i++)
1017 	{
1018 		buf.push ( "0123456789ABCDEF"[n % 16] );
1019 		n /= 16;
1020 	}
1021 }
1022 
add_messageid(Buffer & buf)1023 static void add_messageid(Buffer &buf)
1024 {
1025 time_t	t;
1026 
1027 	buf.push('<');
1028 	time(&t);
1029 	add_bin64(buf,t);
1030 	buf.push('.');
1031 	add_bin64(buf, getpid() );
1032 	buf += ".reformail@";
1033 	buf += HostName();
1034 	buf.push('>');
1035 }
1036 
add_opta(Buffer & buf,const char * optarg)1037 static void add_opta(Buffer &buf, const char *optarg)
1038 {
1039 Buffer	chk_buf;
1040 const char *c;
1041 
1042 	for (c=optarg; *c; c++)
1043 		chk_buf.push ( tolower( (unsigned char)*c ));
1044 	if (chk_buf == "message-id:" || chk_buf == "resent_message_id:")
1045 	{
1046 		chk_buf=optarg;
1047 		chk_buf += ' ';
1048 		add_messageid(chk_buf);
1049 		chk_buf += '\0';
1050 		optarg=chk_buf;
1051 	}
1052 
1053 	do
1054 	{
1055 		buf.push( *optarg );
1056 	} while (*optarg++);
1057 }
1058 
main(int argc,char * argv[])1059 int main(int argc, char *argv[])
1060 {
1061 int	argn, done;
1062 const char	*optarg;
1063 void	(*function)(int, char *[], int)=0;
1064 
1065 #if HAVE_SETLOCALE
1066 	setlocale(LC_ALL, "C");
1067 #endif
1068 
1069 	from_filter= &no_from_filter;
1070 	append_more_headers=&ReadLineAddHeader;
1071 	done=0;
1072 	for (argn=1; argn<argc; argn++)
1073 	{
1074 		if (strcmp(argv[argn], "--") == 0 || strcmp(argv[argn],"-")==0)
1075 		{
1076 			++argn;
1077 			break;
1078 		}
1079 		if (argv[argn][0] != '-')	break;
1080 		optarg=argv[argn]+2;
1081 		if (!*optarg)	optarg=0;
1082 		switch ( argv[argn][1] )	{
1083 		case 'd':
1084 			if (!optarg || !*optarg)	optarg="1";
1085 			addcrs=atoi(optarg);
1086 			break;
1087 		case 'c':
1088 			catenate=1;
1089 			break;
1090 		case 'f':
1091 			if (!optarg && argn+1 < argc)	optarg=argv[++argn];
1092 			if (!optarg || *optarg == '0')
1093 				from_filter=&rem_from_filter;
1094 			else
1095 				from_filter=&add_from_filter;
1096 			break;
1097 		case 'D':
1098 			if (!optarg && argn+1 < argc)	optarg=argv[++argn];
1099 			if (!optarg || argn+1 >= argc)	help();
1100 			if (function)	help();
1101 			function=cache;
1102 			cache_maxlen=optarg;
1103 			cache_name=argv[++argn];
1104 			break;
1105 		case 'a':
1106 			if (!optarg && argn+1 < argc)	optarg=argv[++argn];
1107 			if (!optarg || !*optarg)	help();
1108 			if (function)	help();
1109 			add_opta(opta, optarg);
1110 			break;
1111 		case 'A':
1112 			if (!optarg && argn+1 < argc)	optarg=argv[++argn];
1113 			if (!optarg || !*optarg)	help();
1114 			if (function)	help();
1115 			add_opta(optA, optarg);
1116 			break;
1117 		case 'i':
1118 			if (!optarg && argn+1 < argc)	optarg=argv[++argn];
1119 			if (!optarg || !*optarg)	help();
1120 			if (function)	help();
1121 			do
1122 			{
1123 				opti.push( *optarg );
1124 			} while (*optarg++);
1125 			break;
1126 		case 'I':
1127 			if (!optarg && argn+1 < argc)	optarg=argv[++argn];
1128 			if (!optarg || !*optarg)	help();
1129 			if (function)	help();
1130 			do
1131 			{
1132 				optI.push( *optarg );
1133 			} while (*optarg++);
1134 			break;
1135 		case 'R':
1136 			if (!optarg && argn+1 < argc)	optarg=argv[++argn];
1137 			if (!optarg || !*optarg)	help();
1138 			if (function)	help();
1139 			while (*optarg)
1140 				optR.push(*optarg++);
1141 			if (argn+1 >= argc)	help();
1142 			optarg=argv[++argn];
1143 			while (*optarg)
1144 				optR.push(*optarg++);
1145 			optR.push(0);
1146 			break;
1147 		case 'u':
1148 			if (!optarg && argn+1 < argc)	optarg=argv[++argn];
1149 			if (!optarg || !*optarg)	help();
1150 			if (function)	help();
1151 			while (*optarg)
1152 				optu.push(*optarg++);
1153 			optu.push(0);
1154 			break;
1155 		case 'U':
1156 			if (!optarg && argn+1 < argc)	optarg=argv[++argn];
1157 			if (!optarg || !*optarg)	help();
1158 			if (function)	help();
1159 			while (*optarg)
1160 				optU.push(*optarg++);
1161 			optU.push(0);
1162 			break;
1163 		case 'x':
1164 			if (!optarg && argn+1 < argc)	optarg=argv[++argn];
1165 			if (!optarg || !*optarg)	help();
1166 			if (function)	help();
1167 			while (*optarg)
1168 				optx.push(*optarg++);
1169 			optx.push(0);
1170 			break;
1171 		case 'X':
1172 			if (!optarg && argn+1 < argc)	optarg=argv[++argn];
1173 			if (!optarg || !*optarg)	help();
1174 			if (function)	help();
1175 			while (*optarg)
1176 				optX.push(*optarg++);
1177 			optX.push(0);
1178 			break;
1179 		case 's':
1180 			if (function)	help();
1181 			function= &split;
1182 			++argn;
1183 			done=1;
1184 			break;
1185 		default:
1186 			help();
1187 		}
1188 		if (done)	break;
1189 	}
1190 	if (optx.Length() || optX.Length())
1191 	{
1192 		if (function)	help();
1193 		function=extract_headers;
1194 	}
1195 
1196 	if (!function)	function=copy;
1197 	(*function)(argc, argv, argn);
1198 	std::cout.flush();
1199 	if (std::cout.fail())
1200 	{
1201 		std::cerr << "reformail: error writing output." << std::endl;
1202 		exit(1);
1203 	}
1204 	exit(0);
1205 }
1206