#!/usr/bin/perl
#
#
#
use lib qw(/usr/lib/benno-exchange);
use strict;
use Carp;
use Digest::MD5;
use Getopt::Std;
use Sys::Syslog;
use File::Temp;
use Benno::Exchange2007;
no warnings 'utf8';

$main::VERSION = '2.10.0';

our(%opts);
getopts('bhkvVc:i:m:q:r:R:s:',\%opts);

if ($opts{V}) { print "$main::VERSION\n"; exit; }

my %conf = read_config($opts{c}) if $opts{c};

$conf{backup}    = $opts{b};
$conf{keep_err}  = $opts{k};
$conf{inbox}     = $opts{i} || $conf{inbox}        || '/srv/benno/inbox';
$conf{queueid}   = $opts{q} || $$;
$conf{recipient} = $opts{r};
$conf{runuser}   = $opts{R} || $conf{runuser}      || 'benno';
$conf{sender}    = $opts{s};
$conf{verbose}   = $opts{v};
$conf{version}   = $opts{V};

help_exit() if $opts{h};
if ($conf{version}) {
    print "Version $main::VERSION\n";
    exit;
}

LWs::RunAs->import($conf{runuser});
openlog('benno-journalmail2inbox','nowait,pid','mail');

## VERIFY MAIL ADDRESS
my ($subscr,$ckext,$salt) = $conf{recipient} =~ /^(\S+?)\+(\S+?)(\S{4})\@/;
my $ckcmp = sprintf("%x",crc32($subscr.$salt));
unless ($ckext == $ckcmp) {
    syslog('ERR',"Mail from $conf{sender} to $conf{recipient} with queueid $conf{queueid} has CRC ERROR.");
    print STDERR "5.1.5 Email verification failed.";
    exit 75;
}
$conf{gwheader} = $subscr;  # X-BENNO-GW header value

## READ FROM STDIN AND EXIT ####################################################
my $msg = "Read file from STDIN\n";
print "$msg\n" if $conf{verbose};

eval {
    my $importfile = stdin2inbox(%conf);
    syslog('INFO',"Mail from $conf{sender} to $conf{recipient} with queueid $conf{queueid} archived: $importfile.");
    print "Email archived: $importfile\n" if $conf{verbose};
};
if ($@) {
    my $err = $@;
    syslog('ERR',"FORMAT ERROR: $conf{sender} -> $conf{recipient} ($conf{queueid}): $err");
    print STDERR "5.6.5 Cannot convert journal message.";
    exit 75;
} 
closelog;
exit;


### SUBS ###
### crc32
sub crc32 {
    my ($input, $init_value, $polynomial) = @_;
    $init_value = 0 unless (defined $init_value);
    $polynomial = 0xedb88320 unless (defined $polynomial);

    my @lookup_table;
    for (my $i=0; $i<256; $i++) {
        my $x = $i;
        for (my $j=0; $j<8; $j++) {
            if ($x & 1) { $x = ($x >> 1) ^ $polynomial; }
            else { $x = $x >> 1; }
        }
        push @lookup_table, $x;
    }
    my $crc = $init_value ^ 0xffffffff;
    foreach my $x (unpack ('C*', $input)) {
        $crc = (($crc >> 8) & 0xffffff) ^ $lookup_table[ ($crc ^ $x) & 0xff ];
    }
    $crc = $crc ^ 0xffffffff;
    return $crc;
}


### stdin2inbox
sub stdin2inbox
{
    my (%conf) = @_;

    my $inbox    = $conf{inbox};
    my $gwheader = $conf{gwheader};
    my ($journalmsg, $tmpfile, $infh, $outfh, $emlfile);
    my $filetpl = "$conf{queueid}_$conf{gwheader}-XXXXXXXXXX";

    eval 
    {
        $infh  = \*STDIN;
        while (my $line = <$infh>) {
            $journalmsg .= $line;
        }
        close $infh;

        # save original journalfile
        $conf{backup} && save_backup($inbox,$filetpl,$journalmsg);

        my ($message,$sender,@recipients) =
                            Benno::Exchange2007::parse_journalmail($journalmsg);

        # No envelope header in message
        if (!$sender) {
            #die('ERR',"No sender in journal file.\n");
            syslog('WARNING',"No sender in journal file.");
        }

        my $envelopemsg = "X-REAL-MAILFROM: ".lc $sender."\r\n";
        for my $recipient (@recipients) {
            $envelopemsg .= "X-REAL-RCPTTO: ".lc $recipient."\r\n";
        }

        my $outfh = new File::Temp($filetpl,
                                   DIR => $inbox,
                                   SUFFIX => '.tmp')
                        or die("Cannot open tempfile: $!\n");
        $tmpfile = $outfh->filename;

        $gwheader && print $outfh 'X-BENNO-GW: '.$gwheader."\r\n";
        print $outfh $envelopemsg,$message;
        if (! $outfh->close) {
            die("Cannot write tempfile $tmpfile: $!\n");
        }
        close $outfh;

        ($emlfile = $tmpfile) =~ s/(\.tmp$)/.eml/;
#        ($emlfile = $tmpfile) =~ s/(\.tmp$)/.jrnl/;
        link $tmpfile, $emlfile or die "Rename error: $! $tmpfile\n";
        unlink $tmpfile or die "Cannot unlink $tmpfile: $!\n";
    };
    if ($@) {
        $conf{keep_err} && save_backup($inbox,$filetpl,$journalmsg);
        die $@;
    }

    return $emlfile;
}


###
# save_backup
#
sub save_backup
{
    my ($inbox,$filetpl,$maildata) = @_;

    my $backfh = new File::Temp($filetpl,
                               DIR => $inbox,
                               SUFFIX => '.o365')
                    or syslog('ERR',"Cannot open $filetpl: $!");
    $backfh->unlink_on_destroy(0);
    syslog('INFO',"Save backup journalfile: ".$backfh->filename);
    print $backfh $maildata;
    if (! $backfh->close) {
        syslog('ERR',"Cannot write jornalfile $backfh->filename: $!\n");
    }
    close $backfh;
}



###
# read configuration from file
#
sub read_config
{
  my $configfile = shift;
  my %config;
  # _very_ simple config file parser
  #
  # Config format:   var = val
  #
  open CONF, "$configfile" or die "Cannot open config file $configfile. $!\n";
  foreach my $line (<CONF>) {
      next if $line =~ /^$/;
      next if $line =~ /^#/;
      chomp $line;
      my ($var,$val) = split(/=/, $line,2);
      # strip ws
      $var =~ s/\s//g;
      $val =~ s/^\s+//g;
      $val =~ s/\s+$//g;
      $config{$var} = $val;
  }
  close CONF;
  return %config;
}


### print help and exit
sub help_exit
{
    my $msg = shift;

    if ($msg) {
        print $msg,"\n\n";
    }

    print "Usage: $0 [-h] [<options>]\n";
    print "\n";
    print "\n";
    print "     -c <configfile> configuration file\n";
    print "     -i <inbox>      inboxdir (default /srv/benno/inbox)\n";
    print "\n";
    print "     -r <recipient>  envelope recipient address of mail\n";
    print "     -s <sender>     envelope sender address of mail\n";
    print "     -q <queue_id>   queue id from mta (default: process id)\n";
    print "     -R <user>       run as user (default benno)\n";
    print "     -b              save backup of journal file for debugging\n";
    print "     -V              print version\n";
    print "     -v              verbose\n";
    print "     -h              print this help\n";
    print "\n";
    
    exit 1;
}


### EOP ###
1;

### EOP ###
1;


package LWs::RunAs;
use strict;

sub import {
    my ($package,$user) = @_;
    unless( $user ){
        print STDERR __PACKAGE__." must be imported with user to run as.\n";
        exit 1;
    }
    if (($< == 0) || (getpwuid($<) eq $user)) {
        my ($uid,$gid) = (getpwnam($user))[2,3];
        $( = $gid;
        $) = $gid;
        $> = $uid;
        $< = $uid;
    }
    else {
        print STDERR "Program must be run as $user or root.\n";
        exit 2;
    }
}

### EOP ###
1;


__END__

