Logo Search packages:      
Sourcecode: maildrop version File versions  Download package

reformail.C

/*
** Copyright 1998 - 2002 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 const char rcsid[]="$Id: reformail.C,v 1.18 2002/02/15 13:31:35 mrsam Exp $";

static int inbody=0, addcrs=0, catenate=0, keep_body=0, autoreply_envelope=1;
static const char *(*autoreply_ptr)(), *(*append_more_headers)();
static const char *autoreply_pfix="> ";
static const char *autoreply_opts=0;
Buffer  autoreply_salutation;
Buffer      salutation_template;
Buffer      autoreply_buf;
Buffer      sender_name;
Buffer      sender_addr;
Buffer      optx, optX, opta, optA, opti, optI, optu, optU, optubuf, optUbuf, optR;
const char *autoreply_buf_ptr;

Buffer from_line;
Buffer add_from_filter_buf;
const char *add_from_filter_buf_ptr;
const char *cache_maxlen="", *cache_name="";

static const char *( *from_filter)();
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.
//
//////////////////////////////////////////////////////////////////////////

static void add_from_line(char c, void *p)
{
      p=p;
      from_line.push(c);
}

const char *add_from_filter_header();
const char *add_from_filter()
{
const char *p;

      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();

      rfc822_addrlist(rfca, &add_from_line, NULL);
      rfc822a_free(rfca);
      rfc822t_free(rfc);

      from_line.push('\0');
      from_header.reset();
      for (p=from_line; *p && *p != '\n'; p++)
            from_header.push(*p);
      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);
}

const char *no_autoreply()
{
      return (*from_filter)();
}

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);
}

static void add_sender_name(char c, void *p)
{
      p=p;
      if (c == '\n')    c=' ';
      sender_name.push(c);
}

static void add_sender_addr(char c, void *p)
{
      p=p;
      if (c == '\n')    c=' ';
      sender_addr.push(c);
}

static const char *MyAddress()
{
struct      passwd *p=getpwuid(getuid());
Buffer      name;
const char *q;

static Buffer     buf;

      if (!p)     return ("");
      for (q=p->pw_name; *q; q++)
            if (!isalpha( (unsigned char)*q ) && *q != '_' && *q != ':' &&
                  *q != '.')  break;

      if (!*q)    name=p->pw_name;
      else
      {
            name="\"";
            for (q=p->pw_name; *q; q++)
            {
                  if (*q == '\\' || *q == '"')
                        name.push('\\');
                  name.push( (unsigned char)*q);
            }
            name += "\"";
      }
      if ( (q=HostName()) != 0)
      {
            name += '@';
            name += q;
      }

      if (!p->pw_gecos || !*p->pw_gecos)
            buf=name;
      else
      {
            buf="\"";
            for (q=p->pw_gecos; *q; q++)
            {
                  if (*q == '\\' || *q == '"')
                        buf.push('\\');
                  buf.push ( (unsigned char)*q );
            }
            buf += "\" <";
            buf += name;
            buf += ">";
      }
      buf += '\0';
      return (buf);
}

//  Generate an autoreply header.

const char *get_autoreply();

const char *autoreply()
{
Buffer      from, reply_to, tos, ccs, envelope_from;
Buffer      message_id;
Buffer      newsgroups;
Buffer      newsgroup;
Buffer      references;
Buffer      subject;
Buffer      header;
Buffer      date;

const char *p, *q;

      autoreply_buf="";
      while ((p= (*from_filter)()) != 0 && *p != '\n')
      {
            header.reset();
            while (*p && *p != ':')
            {
            int   c= (unsigned char) *p++;

                  c=tolower(c);
                  header.push(c);
            }
            header += p;
            header += '\0';
            p=header;
            for (q=p; *q && *q != ':'; q++)
                  ;
            if (*q == ':')    q++;
            while (*q && *q != '\n' && isspace(*q))   q++;

            if (strncmp(p, "from:", 5) == 0)    from=q;
            if (strncmp(p, "reply-to:", 9) == 0)      reply_to=q;
            if (strncmp(p, "to:", 3) == 0)
            {
                  if (tos.Length())
                  {
                        tos.pop();
                        tos += ",\n    ";
                  }
                  tos += q;
            }
            if (strncmp(p, "cc:", 3) == 0)
            {
                  if (ccs.Length())
                  {
                        ccs.pop();
                        ccs += ",\n    ";
                  }
                  ccs += q;
            }
            if (strncmp(p, "subject:", 8) == 0)
                  subject=q;
            if (strncmp(p, "date:", 5) == 0)
                  date=q;
            if (strncmp(p, "newsgroups:", 11) == 0)
                  newsgroups=q;
            if (strncmp(p, "x-newsgroup:", 12) == 0)
                  newsgroup=q;
            if (strncmp(p, "message-id:", 11) == 0)
                  message_id=q;
            if (strncmp(p, "references:", 11) == 0)
                  references=q;
            if (strncmp(p, "x-loop:", 7) == 0)
            {
                  autoreply_buf += "X-Loop: ";
                  autoreply_buf += q;
            }
            if (strncmp(p, "return-path:", 12) == 0 ||
                  strncmp(p, ">return-path:", 13) == 0 ||
                  strncmp(p, "errors-to:", 10) == 0 ||
                  strncmp(p, ">errors-to:", 11) == 0)
            {
                  envelope_from=q;
            }
      }

      autoreply_salutation.reset();

      autoreply_buf += "From: ";
      autoreply_buf += MyAddress();
      autoreply_buf += "\n";
      if (reply_to.Length())  from=reply_to;
      if (autoreply_envelope && envelope_from.Length())
                  from=envelope_from;

      sender_name.reset();
      sender_addr.reset();
      if (from.Length())
      {
            autoreply_buf += "To: ";
            autoreply_buf += from;

      Buffer      tempbuf;

            tempbuf=from;
            tempbuf += '\0';

      struct rfc822t *rfc=rfc822t_alloc_new( (const char *)tempbuf, NULL,
                                     NULL);

            if (!rfc)   outofmem();

      struct rfc822a *rfca=rfc822a_alloc( rfc);

            if (!rfca)  outofmem();

            rfc822_namelist(rfca, add_sender_name, NULL);
            rfc822_addrlist(rfca, add_sender_addr, NULL);
            rfc822a_free(rfca);
            rfc822t_free(rfc);
      }

      if (sender_addr.Length() == 0)
            sender_addr="someone";
      if (sender_name.Length() == 0)
            sender_name="someone";

char  c;

      while ((c=sender_addr.pop()) != 0 && isspace(c))
            ;
      if (c)      sender_addr.push(c);
      while ((c=sender_name.pop()) != 0 && isspace(c))
            ;
      if (c)      sender_name.push(c);

      if (salutation_template.Length() == 0)
            salutation_template="%F writes:%n";
      salutation_template += '\0';

Buffer      salutation_buf;

      for (p=salutation_template; *p; p++)
      {
            if (*p != '%' || p[1] == '\0')
            {
                  salutation_buf.push( *p );
                  continue;
            }
            switch (*++p)     {
            case 'n':
                  salutation_buf.push('\n');
                  continue;
            case 'C':
                  salutation_buf += newsgroup;
                  break;
            case 'i':
                  salutation_buf += message_id;
                  break;
            case 'N':
                  salutation_buf += newsgroups;
                  break;
            case 'f':
                  salutation_buf += sender_addr;
                  break;
            case 'F':
                  salutation_buf += sender_name;
                  break;
            case 'd':
                  salutation_buf += date;
                  break;
            case 's':
                  salutation_buf += subject;
                  break;
            default:
                  salutation_buf.push (*p);
                  break;
            }

            c=salutation_buf.pop();
            if (c && c != '\n')     salutation_buf.push(c);
      }

      salutation_buf += "\n";

      p=salutation_buf;

int   l=salutation_buf.Length();
int   i,j;

      i=0;
      while (i<l)
      {
            j= -1;

      int   n;

            for (n=i;n<l && n-i < 76; n++)
            {
                  if ( p[n] == '\n')
                  {
                        j=n;
                        break;
                  }
                  if ( isspace(p[n]))
                        j=n;
            }
            if (j < 0) j=n;

            autoreply_salutation.append(p+i, j-i);
            autoreply_salutation += '\n';
            i=j;
            while (i < l)
            {
                  if (p[i] == '\n')
                  {
                        ++i;
                        break;
                  }
                  if (!isspace(p[i]))     break;
                  ++i;
            }
      }

      if (autoreply_opts && (*autoreply_opts == 'a' || *autoreply_opts == 'A')
            && tos.Length())
      {
            autoreply_buf += "To: ";
            autoreply_buf += tos;
      }

      if (autoreply_opts && *autoreply_opts == 'A' && ccs.Length())
      {
            autoreply_buf += "Cc: ";
            autoreply_buf += ccs;
      }

      if (message_id.Length())
      {
            autoreply_buf += "In-Reply-To: ";
            autoreply_buf += message_id;

            if (references.Length())
                  references += "      ";
            references += message_id;
      }
      if (references.Length())
      {
            autoreply_buf += "References: ";
            autoreply_buf += references;
      }
      if (newsgroups.Length())
      {
            autoreply_buf += "Newsgroups: ";
            autoreply_buf += newsgroups;
      }
      if (subject.Length())
      {
            subject += '\0';
            p=subject;
            if ( tolower( (unsigned char)*p ) == 'r' &&
                  tolower( (unsigned char)p[1] ) == 'e')
            {
                  q=p+2;
                  if (*q == '[')
                  {
                        do
                        {
                              ++q;
                        } while ( isdigit( (unsigned char)*q ));
                        if (*q == ']')    ++q;
                        else q=p+2;
                  }
                  if (*q == ':')
                  {
                        p=q+1;
                        while ( *p && *p != '\n' && isspace(
                              (unsigned char)*p))     p++;
                  }
            }
            autoreply_buf += "Subject: Re: ";
            autoreply_buf += p;
      }

      autoreply_buf += '\0';
      autoreply_buf_ptr=autoreply_buf;
      return (get_autoreply());
}

const char *get_autoreply_body();

const char *get_autoreply()
{
      autoreply_ptr= &get_autoreply;
      if (*autoreply_buf_ptr == '\0')
      {
            autoreply_ptr= &get_autoreply_body;
            return ("\n");
      }

static Buffer     buf;

      buf.reset();
      do
      {
            while (*autoreply_buf_ptr)
            {
                  buf.push (*autoreply_buf_ptr);
                  if (*autoreply_buf_ptr++ == '\n')   break;
            }
      } while ( *autoreply_buf_ptr &&
            isspace( (unsigned char) *autoreply_buf_ptr ));
      buf += '\0';
      return (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= (*autoreply_ptr)();

            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 ( (*autoreply_ptr)() );
}

////////////////////////////////////////////////////////////////////////////
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);
}

const char *get_autoreply_body()
{
      if (!keep_body)
      {
#ifdef      BUFSIZ
      char  buf[BUFSIZ];
#else
      char  buf[512];   // Flush body to prevent broken pipe
#endif

            while ( !std::cin.eof())
                  std::cin.read(buf, sizeof(buf));
            return (0);
      }

static Buffer     buf;

      if (autoreply_salutation.Length())
      {
      const char *p;
      int   l;
      int   cnt;

            buf=autoreply_salutation;

            p=buf;
            l=buf.Length();
            cnt=0;
            while (l)
            {
                  ++cnt;
                  --l;
                  if (*p++ == '\n') break;
            }
            autoreply_salutation.reset();
            autoreply_salutation.append(p, l);
            buf.Length(cnt);
            buf += '\0';
            return (buf);
      }

const char *p= (*from_filter)();
      if (!p)     return(p);

      buf=autoreply_pfix;
      buf += p;
      buf += '\0';
      return (buf);
}

/////////////////////////////////////////////////////////////////////////
//
// 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;
            }
      }
      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)
            {
                  close(0);
                  dup(fds[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<8; 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;
char  *optarg;
void  (*function)(int, char *[], int)=0;

#if HAVE_SETLOCALE
      setlocale(LC_ALL, "C");
#endif

      from_filter= &no_from_filter;
      autoreply_ptr= &no_autoreply;
      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 'r':
                  if (function)     help();
                  autoreply_ptr= &autoreply;
                  autoreply_opts=optarg;
                  break;
            case 'p':
                  if (!optarg && argn+1 < argc) optarg=argv[++argn];
                  autoreply_pfix=optarg;
                  break;
            case 'k':
                  keep_body=1;
                  break;
            case 't':
                  autoreply_envelope=0;
                  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;
            case 'P':
                  if (!optarg && argn+1 < argc) optarg=argv[++argn];
                  if (!optarg || !*optarg)      help();
                  salutation_template=optarg;
                  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);
}

Generated by  Doxygen 1.6.0   Back to index