summaryrefslogtreecommitdiffstats
path: root/maildrop/reformail.C
diff options
context:
space:
mode:
Diffstat (limited to 'maildrop/reformail.C')
-rw-r--r--maildrop/reformail.C1204
1 files changed, 1204 insertions, 0 deletions
diff --git a/maildrop/reformail.C b/maildrop/reformail.C
new file mode 100644
index 0000000..be1e9cb
--- /dev/null
+++ b/maildrop/reformail.C
@@ -0,0 +1,1204 @@
+/*
+** Copyright 1998 - 2009 Double Precision, Inc.
+** See COPYING for distribution information.
+*/
+
+#include "config.h"
+
+#include <stdio.h>
+#include <iostream>
+#include <iomanip>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <signal.h>
+#include <pwd.h>
+
+#if HAVE_LOCALE_H
+#include <locale.h>
+#endif
+
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#include "mytime.h"
+#include <sys/types.h>
+#include "mywait.h"
+#if HAVE_SYS_FILE_H
+#include <sys/file.h>
+#endif
+#include "rfc822.h"
+#include "buffer.h"
+#include "liblock/config.h"
+#include "liblock/liblock.h"
+
+#if HAS_GETHOSTNAME
+#else
+extern "C" int gethostname(const char *, size_t);
+#endif
+
+
+static int inbody=0, addcrs=0, catenate=0;
+static const char *(*append_more_headers)();
+static Buffer optx, optX, opta, optA, opti, optI, optu, optU, optubuf, optUbuf, optR;
+
+static Buffer add_from_filter_buf;
+static const char *add_from_filter_buf_ptr;
+static const char *cache_maxlen="", *cache_name="";
+
+static const char *( *from_filter)();
+static Buffer current_line;
+
+void outofmem()
+{
+ std::cerr << "reformail: Out of memory." << std::endl;
+ exit(1);
+}
+
+void help()
+{
+ std::cerr << "reformail: Invalid arguments." << std::endl;
+ exit(1);
+}
+
+// Return next line from standard input. Trailing CRs are stripped
+
+const char *NextLine()
+{
+static Buffer buf;
+int c;
+
+ buf.reset();
+ while ((c=std::cin.get()) >= 0 && c != '\n')
+ buf.push(c);
+ if (c < 0 && buf.Length() == 0) return (0);
+ if (buf.Length()) // Strip CRs
+ {
+ c=buf.pop();
+ if (c != '\r') buf.push(c);
+ }
+ buf.push('\n');
+ buf.push('\0');
+ return (buf);
+}
+
+// from_filter is the initial filtering done on the message.
+// from_filter adds or subtracts "From " quoting from the message.
+// from_filter returns the next line from the message, after filtering.
+// The line is always terminated by a newline character.
+// When the header is being read, multiline headers are silently
+// concatenated into a single return from from_filter() (separated by
+// newlines, of course.
+//
+// from_filter is initialized to either from_filter(), add_from_filter()
+// and rem_from_filter() respectively, to cause either addition, removal of,
+// or no change to from quoting. The pointer is automatically updated.
+//
+// Also, the from_filter silently discards empty lines at the beginning of
+// the message.
+
+// DO NOT CHANGE FROM QUOTING.
+
+const char *no_from_filter_header();
+
+const char *no_from_filter()
+{
+const char *p;
+
+ while ((p=NextLine()) && *p == '\n')
+ ;
+ if (!p) return (0);
+
+ current_line=p;
+ return ( no_from_filter_header() );
+}
+
+const char *read_blank();
+
+const char *no_from_filter_header()
+{
+const char *p;
+
+static Buffer buf;
+
+ from_filter= &no_from_filter_header;
+
+ while ((p=NextLine()) && *p && *p != '\n' && isspace((unsigned char)*p))
+ current_line += p;
+
+ buf=current_line;
+ buf+='\0';
+ if (!p || *p == '\n')
+ {
+ from_filter= &read_blank;
+ return (buf);
+ }
+ current_line=p;
+ return (buf);
+}
+
+const char *read_blank()
+{
+ from_filter= &NextLine;
+ return ("\n");
+}
+
+//////////////////////////////////////////////////////////////////////////
+//
+// Add 'From ' quoting. All headers are read into add_from_filter_buf,
+// and a suitable return address is located. The 'From ' line is
+// generated, and return. Subsequent calls fetch one header at a
+// time from add_from_filter_buf, then resume reading the body of the
+// message.
+//
+//////////////////////////////////////////////////////////////////////////
+
+const char *add_from_filter_header();
+const char *add_from_filter()
+{
+const char *p;
+int n;
+ while ((p=NextLine()) && *p == '\n')
+ ;
+ if (!p) return (0);
+
+ current_line=p;
+ if (strncmp(p, "From ", 5) == 0)
+ return ( no_from_filter_header() );
+ add_from_filter_buf.reset();
+ while (p && *p != '\n')
+ {
+ add_from_filter_buf += p;
+ p=NextLine();
+ }
+
+ add_from_filter_buf += '\0';
+
+static Buffer return_path;
+static Buffer from_header;
+
+ return_path.reset();
+ from_header.reset();
+
+ for (p=add_from_filter_buf; *p; )
+ {
+ Buffer header;
+
+ while (*p && *p != ':' && *p != '\n')
+ {
+ int c= (unsigned char)*p++;
+
+ c=tolower(c);
+ header.push(c);
+ }
+ for (;;)
+ {
+ while (*p && *p != '\n')
+ {
+ header.push(*p);
+ p++;
+ }
+ if (!*p) break;
+ ++p;
+ header.push('\n');
+ if (!*p || !isspace((unsigned char)*p)) break;
+ }
+ header.push('\0');
+ if (strncmp(header, "return-path:", 12) == 0 ||
+ strncmp(header, ">return-path:", 13) == 0 ||
+ strncmp(header, "errors-to:", 10) == 0 ||
+ strncmp(header, ">errors-to:", 11) == 0)
+ {
+ for (p=header; *p != ':'; p++)
+ ;
+ return_path=p;
+ }
+
+ if (strncmp(header, "from:", 5) == 0)
+ from_header=(const char *)header + 5;
+ }
+ if (return_path.Length() == 0) return_path=from_header;
+ return_path += '\0';
+
+ struct rfc822t *rfc=rfc822t_alloc_new( (const char *)return_path,
+ NULL, NULL);
+
+ if (!rfc) outofmem();
+
+ struct rfc822a *rfca=rfc822a_alloc( rfc);
+
+ if (!rfca) outofmem();
+
+ from_header.reset();
+
+ for (n=0; n<rfca->naddrs; ++n)
+ {
+ if (rfca->addrs[n].tokens)
+ {
+ char *p=rfc822_display_addr_tobuf(rfca, n, NULL);
+
+ if (p)
+ {
+ try {
+ from_header=p;
+ } catch (...)
+ {
+ free(p);
+ throw;
+ }
+ free(p);
+ break;
+ }
+ }
+ }
+
+ rfc822a_free(rfca);
+ rfc822t_free(rfc);
+
+ if (from_header.Length() == 0) from_header="root";
+ return_path="From ";
+ return_path += from_header;
+ return_path.push(' ');
+time_t t;
+
+ time(&t);
+ p=ctime(&t);
+ while (*p && *p != '\n')
+ {
+ return_path.push(*p);
+ p++;
+ }
+ return_path += '\n';
+ return_path += '\0';
+ from_filter=add_from_filter_header;
+ add_from_filter_buf_ptr=add_from_filter_buf;
+ return (return_path);
+}
+
+const char *add_from_filter_body();
+
+const char *add_from_filter_header()
+{
+static Buffer buf;
+
+ buf.reset();
+
+ if (*add_from_filter_buf_ptr == '\0')
+ {
+ from_filter= &add_from_filter_body;
+ return ("\n");
+ }
+
+ do
+ {
+ while (*add_from_filter_buf_ptr)
+ {
+ buf.push ( (unsigned char)*add_from_filter_buf_ptr );
+ if ( *add_from_filter_buf_ptr++ == '\n') break;
+ }
+ } while ( *add_from_filter_buf_ptr && *add_from_filter_buf_ptr != '\n'
+ && isspace( (unsigned char)*add_from_filter_buf_ptr ));
+ buf += '\0';
+ return (buf);
+}
+
+const char *add_from_filter_body()
+{
+const char *p=NextLine();
+
+ if (!p) return (p);
+
+const char *q;
+
+ for (q=p; *q == '>'; q++)
+ ;
+ if (strncmp(q, "From ", 5)) return (p);
+
+static Buffer add_from_buf;
+
+ add_from_buf=">";
+ add_from_buf += p;
+ add_from_buf += '\0';
+ return (add_from_buf);
+}
+
+////////////////////////////////////////////////////////////////////////////
+//
+// Strip From quoting.
+//
+////////////////////////////////////////////////////////////////////////////
+
+const char *rem_from_filter_header();
+
+const char *(*rem_from_filter_header_ptr)();
+
+const char *rem_from_filter()
+{
+const char *p;
+
+ while ((p=NextLine()) && *p == '\n')
+ ;
+ if (!p) return (0);
+
+ if (strncmp(p, "From ", 5))
+ {
+ current_line=p;
+ return ( no_from_filter_header() );
+ }
+ p=NextLine();
+ if (!p) return (p);
+ current_line=p;
+ rem_from_filter_header_ptr= &no_from_filter_header;
+ return ( rem_from_filter_header() );
+}
+
+const char *rem_from_filter_body();
+const char *rem_from_filter_header()
+{
+const char *p=(*rem_from_filter_header_ptr)();
+
+ rem_from_filter_header_ptr=from_filter;
+ from_filter=rem_from_filter_header;
+ if (!p || *p == '\n')
+ {
+ from_filter=&rem_from_filter_body;
+ p="\n";
+ }
+ return (p);
+}
+
+const char *rem_from_filter_body()
+{
+const char *p=NextLine();
+
+ if (!p) return (p);
+
+ if (*p == '>')
+ {
+ const char *q;
+
+ for (q=p; *q == '>'; q++)
+ ;
+ if (strncmp(q, "From ", 5) == 0) ++p;
+ }
+ return (p);
+}
+
+static const char *HostName()
+{
+static char hostname_buf[256];
+
+ hostname_buf[0]=0;
+ hostname_buf[sizeof(hostname_buf)-1]=0;
+ gethostname(hostname_buf, sizeof(hostname_buf)-1);
+ return (hostname_buf);
+}
+
+////////////////////////////////////////////////////////////////////////////
+//
+// Return TRUE if header is already in a list of headers.
+//
+// hdrs: null separated list of headers (and header contents)
+// hdr - header to check (must be lowercase and terminated by a colon)
+// pos - offset into hdrs where it's found.
+//
+
+static int has_hdr(const Buffer &hdrs, const char *hdr, unsigned &pos)
+{
+const char *r=hdrs;
+int l=hdrs.Length();
+Buffer buf2;
+unsigned pos2=0;
+
+ while (l)
+ {
+ buf2.reset();
+ pos=pos2;
+ while (l)
+ {
+ --l;
+ ++pos2;
+ buf2.push( tolower(*r));
+ if (*r++ == 0) break;
+ }
+ buf2.push('\0');
+ if (strncmp(hdr, buf2, strlen(hdr)) == 0) return (1);
+ }
+ return (0);
+}
+
+static int has_hdr(const Buffer &hdrs, const char *hdr)
+{
+unsigned dummy;
+
+ return (has_hdr(hdrs, hdr, dummy));
+}
+
+static void strip_empty_header(Buffer &buf)
+{
+Buffer newbuf;
+int l;
+const char *p;
+
+ for (p=buf, l=buf.Length(); l; )
+ {
+ if (p[strlen(p)-1] == ':')
+ {
+ while (l)
+ {
+ --l;
+ if (*p++ == '\0') break;
+ }
+ continue;
+ }
+ while (l)
+ {
+ --l;
+ newbuf.push( *p );
+ if (*p++ == '\0') break;
+ }
+ }
+ buf=newbuf;
+}
+
+static void strip_header(Buffer &header, unsigned offset)
+{
+Buffer buf1;
+const char *p=header;
+int l=header.Length();
+
+ while (l)
+ {
+ if (!offset)
+ {
+ while (l)
+ {
+ --l;
+ if (*p++ == '\0') break;
+ }
+ break;
+ }
+ buf1.push( *p++ );
+ --l;
+ --offset;
+ }
+ while (l--)
+ buf1.push( *p++ );
+ header=buf1;
+}
+
+const char *ReadLineAddNewHeader();
+
+const char *ReadLineAddHeader()
+{
+Buffer buf1;
+const char *q;
+const char *p;
+unsigned pos;
+static Buffer oldbuf;
+
+ for (;;)
+ {
+ p= (*from_filter)();
+
+ if (!p) return p;
+ if (*p == '\n')
+ {
+ strip_empty_header(opti);
+ strip_empty_header(optI);
+ return ( ReadLineAddNewHeader());
+ }
+ buf1.reset();
+ for (q=p; *q && *q != '\n'; q++)
+ {
+ buf1.push( tolower(*q) );
+ if (*q == ':') break;
+ }
+ buf1 += '\0';
+
+ if (has_hdr(opti, buf1))
+ {
+ oldbuf="old-";
+ oldbuf += buf1;
+ buf1=oldbuf;
+
+ Buffer tbuf;
+
+ tbuf="Old-";
+ tbuf += p;
+ oldbuf=tbuf;
+ oldbuf += '\0';
+ p=oldbuf;
+ }
+ if (has_hdr(optR, buf1, pos))
+ {
+ Buffer tbuf;
+
+ q=optR;
+ q += pos + strlen(buf1);
+ tbuf=q;
+
+ p += strlen(buf1);
+ tbuf += p;
+ oldbuf=tbuf;
+ oldbuf += '\0';
+ p=oldbuf;
+ }
+
+ if (has_hdr(optI, buf1))
+ continue;
+ if (has_hdr(optu, buf1))
+ {
+ if (!has_hdr(optubuf, buf1))
+ {
+ q=p;
+ do
+ {
+ optubuf.push( *q );
+ } while (*q++);
+ break;
+ }
+ continue;
+ }
+
+ if (has_hdr(optU, buf1))
+ {
+ if (has_hdr(optUbuf, buf1, pos))
+ strip_header(optUbuf, pos);
+ while (*p)
+ {
+ optUbuf.push( *p );
+ p++;
+ }
+ optUbuf.pop();
+ optUbuf.push('\0');
+ continue;
+ }
+ break;
+ }
+
+unsigned offset;
+
+ if (has_hdr(opta, buf1, offset))
+ strip_header(opta, offset);
+ return (p);
+}
+
+const char *ReadLineAddNewHeaderDone();
+
+const char *ReadLineAddNewHeader()
+{
+ append_more_headers= &ReadLineAddNewHeader;
+
+Buffer *bufptr;
+
+ if (opta.Length()) bufptr= &opta;
+ else if (optA.Length()) bufptr= &optA;
+ else if (opti.Length()) bufptr= &opti;
+ else if (optI.Length()) bufptr= &optI;
+ else if (optUbuf.Length()) bufptr= &optUbuf;
+ else
+ {
+ append_more_headers=&ReadLineAddNewHeaderDone;
+ return ("\n");
+ }
+
+static Buffer buf1;
+Buffer buf2;
+
+ buf1.reset();
+
+const char *p= *bufptr;
+int l= bufptr->Length();
+
+ while (l)
+ {
+ if ( !*p )
+ {
+ p++;
+ l--;
+ break;
+ }
+ buf1.push( *p );
+ p++;
+ l--;
+ }
+ buf1.push('\n');
+ buf1.push('\0');
+
+ while (l--)
+ buf2.push (*p++);
+ *bufptr=buf2;
+ return (buf1);
+}
+
+const char *ReadLineAddNewHeaderDone()
+{
+ return ( (*from_filter)() );
+}
+
+////////////////////////////////////////////////////////////////////////////
+const char *ReadLine()
+{
+const char *p=(*append_more_headers)();
+
+ if (!p) return (p);
+
+static Buffer buf;
+
+ if (*p == '\n')
+ inbody=1;
+
+ if (catenate && !inbody)
+ {
+ const char *q;
+
+ buf.reset();
+ for (q=p; *q; q++)
+ {
+ if (*q != '\n')
+ {
+ buf.push(*q);
+ continue;
+ }
+ do
+ {
+ ++q;
+ } while (*q && isspace(*q));
+ if (*q)
+ buf.push(' ');
+ --q;
+ }
+ if (addcrs) buf.push('\r');
+ buf.push('\n');
+ buf.push('\0');
+ return (buf);
+ }
+
+ if (addcrs)
+ {
+ buf=p;
+ buf.pop();
+ buf += "\r\n";
+ buf += '\0';
+ return (buf);
+ }
+ return (p);
+}
+
+/////////////////////////////////////////////////////////////////////////
+//
+// Default activity: just copy the message (let the low-level format
+// filters do their job.
+//
+/////////////////////////////////////////////////////////////////////////
+
+void copy(int, char *[], int)
+{
+const char *p;
+
+ while ((p= ReadLine()) != 0)
+ std::cout << p;
+}
+
+void cache(int, char *[], int)
+{
+const char *p;
+Buffer buf;
+int found=0;
+
+ addcrs=0;
+ while ((p= ReadLine()) != 0)
+ {
+ int c;
+
+ if (inbody) break;
+ buf.reset();
+ while (*p && *p != '\n')
+ {
+ c= (unsigned char)*p;
+ c=tolower(c);
+ buf.push(c);
+ if (*p++ == ':') break;
+ }
+ if (!(buf == "message-id:")) continue;
+ buf += '\0';
+ while (*p && isspace( (unsigned char)*p)) p++;
+ buf.reset();
+ while (*p)
+ {
+ buf.push(*p);
+ ++p;
+ }
+
+ while ( (c=(unsigned char)buf.pop()) != 0 && isspace(c))
+ ;
+ if (c) buf.push(c);
+ if (buf.Length() == 0) break;
+ buf.push('\0');
+
+ int fd=open(cache_name, O_RDWR | O_CREAT, 0600);
+
+ if (fd < 0)
+ {
+ perror("open");
+ exit(75);
+ }
+
+ if (ll_lock_ex(fd) < 0)
+ {
+ perror("lock");
+ exit(75);
+ }
+
+ off_t pos=0;
+
+ if (lseek(fd, 0L, SEEK_END) == -1 ||
+ (pos=lseek(fd, 0L, SEEK_CUR)) == -1 ||
+ lseek(fd, 0L, SEEK_SET) == -1)
+ {
+ perror("seek");
+ exit(75);
+ }
+
+ off_t maxlen_n=atol(cache_maxlen);
+ char *charbuf;
+ off_t newpos=maxlen_n;
+
+ if (newpos < pos) newpos=pos;
+
+ if ((charbuf=new char[newpos+buf.Length()+1]) == NULL)
+ outofmem();
+
+ off_t readcnt=read(fd, charbuf, newpos);
+
+ if (readcnt < 0) perror("read");
+
+ char *q, *r;
+
+ for (q=r=charbuf; q<charbuf+readcnt; )
+ {
+ if (*q == '\0') break; // Double null terminator
+ if (strcmp( (const char *)buf, q) == 0)
+ {
+ found=1;
+ while (q < charbuf+readcnt)
+ if (*q++ == '\0') break;
+ }
+ else while (q < charbuf+readcnt)
+ if ( (*r++=*q++) == '\0') break;
+ }
+ memcpy(r, (const char *)buf, buf.Length());
+ r += buf.Length();
+ for (q=charbuf; q<r; )
+ {
+ if (r - q < maxlen_n)
+ break;
+ while (q < r)
+ if (*q++ == '\0') break;
+ }
+ if (q == r) q=charbuf;
+ *r++ = '\0';
+ if (lseek(fd, 0L, SEEK_SET) == -1)
+ {
+ perror("lseek");
+ exit(1);
+ }
+ while (q < r)
+ {
+ readcnt=write(fd, q, r-q);
+ if (readcnt == -1)
+ {
+ perror("write");
+ exit(1);
+ }
+ q += readcnt;
+ }
+ close(fd);
+ delete[] charbuf;
+ break;
+ }
+ while ((p= ReadLine()) != 0)
+ ;
+ exit(found ? 0:1);
+}
+
+//////////////////////////////////////////////////////////////////////////
+//
+// Extract headers
+
+void extract_headers(int, char *[], int)
+{
+const char *p, *q;
+Buffer b;
+
+ catenate=1;
+ while ((p=ReadLine()) && !inbody)
+ {
+ b.reset();
+ for (q=p; *q && *q != '\n'; )
+ {
+ int c= (unsigned char)*q;
+
+ b.push( tolower(c) );
+ if ( *q++ == ':') break;
+ }
+ b.push(0);
+
+ if (has_hdr(optx, b))
+ {
+ while (*q && *q != '\n' && isspace(*q))
+ q++;
+ std::cout << q;
+ continue;
+ }
+
+ if (has_hdr(optX, b))
+ {
+ std::cout << p;
+ continue;
+ }
+ }
+ if (!std::cin.seekg(0, std::ios::end).fail())
+ return;
+ std::cin.clear();
+
+ while ( ReadLine() )
+ ;
+}
+//////////////////////////////////////////////////////////////////////////
+//
+// Split mbox file into messages.
+
+void split(int argc, char *argv[], int argn)
+{
+const char *p;
+Buffer buf;
+int l;
+int do_environ=1;
+unsigned long environ=0;
+unsigned environ_len=3;
+const char *env;
+
+ if (argn >= argc) help();
+
+ while ( (p=NextLine()) && *p == '\n')
+ ;
+
+ signal(SIGCHLD, SIG_DFL);
+ signal(SIGPIPE, SIG_IGN);
+ env=getenv("FILENO");
+ if (env)
+ {
+ const char *q;
+
+ for (q=env; *q; q++)
+ if (!isdigit(*q)) break;
+ if (*q) do_environ=0;
+ else
+ {
+ environ_len=strlen(env);
+ environ=atol(env);
+ }
+ }
+
+ while (p)
+ {
+ int fds[2];
+
+ if (pipe(fds) < 0)
+ {
+ std::cerr << "reformail: pipe() failed." << std::endl;
+ exit(1);
+ }
+
+ pid_t pid=fork();
+
+ if (pid == -1)
+ {
+ std::cerr << "reformail: fork() failed." << std::endl;
+ exit(1);
+ }
+
+ if (pid == 0)
+ {
+ dup2(fds[0], 0);
+ close(fds[0]);
+ close(fds[1]);
+
+ Buffer buf, buf2;
+
+ if (do_environ)
+ {
+ char *s;
+
+ while (environ || environ_len)
+ {
+ buf.push( "0123456789"[environ % 10]);
+ environ /= 10;
+ if (environ_len) --environ_len;
+ }
+
+ buf2="FILENO=";
+ while (buf.Length())
+ buf2.push(buf.pop());
+ buf2 += '\0';
+ s=strdup(buf2);
+ if (!s)
+ {
+ perror("strdup");
+ exit (1);
+ }
+ putenv(s);
+ }
+
+ execvp( argv[argn], argv+argn);
+ std::cerr << "reformail: exec() failed." << std::endl;
+ exit(1);
+ }
+ close(fds[0]);
+ environ++;
+
+ do
+ {
+ buf=p;
+ p=ReadLine();
+ if (!p || strncmp(p, "From ", 5) == 0)
+ buf.pop(); // Drop trailing newline
+ else
+ {
+ if (addcrs)
+ {
+ buf.pop();
+ buf.push('\r');
+ buf.push('\n');
+ }
+ }
+
+ const char *q=buf;
+
+ l=buf.Length();
+ while (l)
+ {
+ int n= ::write( fds[1], q, l);
+ if (n <= 0)
+ {
+ std::cerr
+ << "reformail: write() failed."
+ << std::endl;
+ exit(1);
+ }
+ q += n;
+ l -= n;
+ }
+ } while (p && strncmp(p, "From ", 5));
+ close(fds[1]);
+
+ int wait_stat;
+
+ while ( wait(&wait_stat) != pid )
+ ;
+ if (!WIFEXITED(wait_stat) || WEXITSTATUS(wait_stat))
+ break; // Rely on diagnostic from child
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+static void add_bin64(Buffer &buf, unsigned long n)
+{
+int i;
+
+ for (i=0; i<16; i++)
+ {
+ buf.push ( "0123456789ABCDEF"[n % 16] );
+ n /= 16;
+ }
+}
+
+static void add_messageid(Buffer &buf)
+{
+time_t t;
+
+ buf.push('<');
+ time(&t);
+ add_bin64(buf,t);
+ buf.push('.');
+ add_bin64(buf, getpid() );
+ buf += ".reformail@";
+ buf += HostName();
+ buf.push('>');
+}
+
+static void add_opta(Buffer &buf, const char *optarg)
+{
+Buffer chk_buf;
+const char *c;
+
+ for (c=optarg; *c; c++)
+ chk_buf.push ( tolower( (unsigned char)*c ));
+ if (chk_buf == "message-id:" || chk_buf == "resent_message_id:")
+ {
+ chk_buf=optarg;
+ chk_buf += ' ';
+ add_messageid(chk_buf);
+ chk_buf += '\0';
+ optarg=chk_buf;
+ }
+
+ do
+ {
+ buf.push( *optarg );
+ } while (*optarg++);
+}
+
+int main(int argc, char *argv[])
+{
+int argn, done;
+const char *optarg;
+void (*function)(int, char *[], int)=0;
+
+#if HAVE_SETLOCALE
+ setlocale(LC_ALL, "C");
+#endif
+
+ from_filter= &no_from_filter;
+ append_more_headers=&ReadLineAddHeader;
+ done=0;
+ for (argn=1; argn<argc; argn++)
+ {
+ if (strcmp(argv[argn], "--") == 0 || strcmp(argv[argn],"-")==0)
+ {
+ ++argn;
+ break;
+ }
+ if (argv[argn][0] != '-') break;
+ optarg=argv[argn]+2;
+ if (!*optarg) optarg=0;
+ switch ( argv[argn][1] ) {
+ case 'd':
+ if (!optarg || !*optarg) optarg="1";
+ addcrs=atoi(optarg);
+ break;
+ case 'c':
+ catenate=1;
+ break;
+ case 'f':
+ if (!optarg && argn+1 < argc) optarg=argv[++argn];
+ if (!optarg || *optarg == '0')
+ from_filter=&rem_from_filter;
+ else
+ from_filter=&add_from_filter;
+ break;
+ case 'D':
+ if (!optarg && argn+1 < argc) optarg=argv[++argn];
+ if (!optarg || argn+1 >= argc) help();
+ if (function) help();
+ function=cache;
+ cache_maxlen=optarg;
+ cache_name=argv[++argn];
+ break;
+ case 'a':
+ if (!optarg && argn+1 < argc) optarg=argv[++argn];
+ if (!optarg || !*optarg) help();
+ if (function) help();
+ add_opta(opta, optarg);
+ break;
+ case 'A':
+ if (!optarg && argn+1 < argc) optarg=argv[++argn];
+ if (!optarg || !*optarg) help();
+ if (function) help();
+ add_opta(optA, optarg);
+ break;
+ case 'i':
+ if (!optarg && argn+1 < argc) optarg=argv[++argn];
+ if (!optarg || !*optarg) help();
+ if (function) help();
+ do
+ {
+ opti.push( *optarg );
+ } while (*optarg++);
+ break;
+ case 'I':
+ if (!optarg && argn+1 < argc) optarg=argv[++argn];
+ if (!optarg || !*optarg) help();
+ if (function) help();
+ do
+ {
+ optI.push( *optarg );
+ } while (*optarg++);
+ break;
+ case 'R':
+ if (!optarg && argn+1 < argc) optarg=argv[++argn];
+ if (!optarg || !*optarg) help();
+ if (function) help();
+ while (*optarg)
+ optR.push(*optarg++);
+ if (argn+1 >= argc) help();
+ optarg=argv[++argn];
+ while (*optarg)
+ optR.push(*optarg++);
+ optR.push(0);
+ break;
+ case 'u':
+ if (!optarg && argn+1 < argc) optarg=argv[++argn];
+ if (!optarg || !*optarg) help();
+ if (function) help();
+ while (*optarg)
+ optu.push(*optarg++);
+ optu.push(0);
+ break;
+ case 'U':
+ if (!optarg && argn+1 < argc) optarg=argv[++argn];
+ if (!optarg || !*optarg) help();
+ if (function) help();
+ while (*optarg)
+ optU.push(*optarg++);
+ optU.push(0);
+ break;
+ case 'x':
+ if (!optarg && argn+1 < argc) optarg=argv[++argn];
+ if (!optarg || !*optarg) help();
+ if (function) help();
+ while (*optarg)
+ optx.push(*optarg++);
+ optx.push(0);
+ break;
+ case 'X':
+ if (!optarg && argn+1 < argc) optarg=argv[++argn];
+ if (!optarg || !*optarg) help();
+ if (function) help();
+ while (*optarg)
+ optX.push(*optarg++);
+ optX.push(0);
+ break;
+ case 's':
+ if (function) help();
+ function= &split;
+ ++argn;
+ done=1;
+ break;
+ default:
+ help();
+ }
+ if (done) break;
+ }
+ if (optx.Length() || optX.Length())
+ {
+ if (function) help();
+ function=extract_headers;
+ }
+
+ if (!function) function=copy;
+ (*function)(argc, argv, argn);
+ std::cout.flush();
+ if (std::cout.fail())
+ {
+ std::cerr << "reformail: error writing output." << std::endl;
+ exit(1);
+ }
+ exit(0);
+}