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