#!/usr/bin/perl
#
#
# v1.6.6
use strict;
use lib '/usr/lib/benno-exchange';
use Getopt::Std;
use Sys::Syslog;
use IO::Socket;
use File::Temp qw/tempfile/;
use IO::File;
use MIME::Parser;
use Benno::Exchange2003;
use Benno::Exchange2007;
use Carp;

my $VERSION = '2.8.9';

my %opts;
getopts('DdhvVR:c:e:i:m:s:t:',\%opts);

help_exit() if $opts{h};

if ($opts{V}) {
    print "benno-exchange-dirimport $VERSION\n"; exit 0;
}

my $conf;
if ($opts{c}) {
    $conf = read_config($opts{c});
}
my $DEBUG     = $conf->{DEBUG}     || $opts{D};
my $delete    = $conf->{delete}    || $opts{d};
my $ssl       = $conf->{ssl}       || $opts{s};
my $VERBOSE   = $conf->{VERBOSE}   || $opts{v};
my $inbox     = $conf->{inbox}     || $opts{i} || '/srv/benno/inbox';
my $type      = $conf->{type}      || $opts{t} || '2007';
my $runuser   = $conf->{runuser}   || $opts{R} || 'benno';
my $spooldir  = $conf->{spooldir}  || $opts{s} || '/srv/benno/exchange-spool';
my $extension = $conf->{extension} || $opts{e} || 'jrnl';

LWs::RunAs->import($runuser);

my $force_delete = $opts{f};

($type eq '2007') || ($type eq '2003') || help_exit(); 

my $ERRMSG;
$VERBOSE = 1 if $DEBUG;

$0 = "benno-exchange-dirimport";

openlog('benno-exchange-dirimport','nowaid,pid','mail');

my $count = 0;
opendir(my $dh, $spooldir) || die "Cannot open import dir $spooldir: $!\n";
while(my $journal_file = readdir($dh)) {
    next if $journal_file =~ /^..?$/;
    next if $journal_file !~ /\.$extension$/;

    print "Read $journal_file\n" if $DEBUG;
    my $journal_filepath = "$spooldir/$journal_file";

    my $jf;
    my $journal_msg;
    {
        local $/;
        open($jf, "$journal_filepath") or die "Cannot open $journal_file: $!\n";
        $journal_msg = <$jf>;
        close $jf;
    }

    $count++;
    my ($fh,$tmpfile) = tempfile($journal_file.'_XXXXXXXX',
                                 DIR => $inbox,
                                 SUFFIX => '');
    eval {
        my ($message,$sender,@recipients);
        if ($type == '2003') {
            ($message,$sender,@recipients) = Benno::Exchange2003::parse_journalmail($journal_msg);
        }
        elsif ($type == '2007') {
            ($message,$sender,@recipients) = Benno::Exchange2007::parse_journalmail($journal_msg);
        }
        else {
            print STDERR "Incorrect type of metadata given\n" if $VERBOSE;
            help_exit();
        }

        # No envelope header in message
        if (!$sender) {
            print STDERR "Error parse msg #$journal_file.\n" if $VERBOSE;
            syslog('ERR',"Error parse msg #$journal_file.");
            $fh->close;
            unlink $tmpfile; 
            next unless $force_delete;
        }

        my $envelopemsg = "X-REAL-MAILFROM: ".lc $sender."\r\n";
        for my $recipient (@recipients) {
            $envelopemsg .= "X-REAL-RCPTTO: ".lc $recipient."\r\n";
        }
        print $fh $envelopemsg,$message;
        if (! $fh->close) {
            croak "Cannot write tempfile $tmpfile: $!\n";
        }
        my $emlfile = $tmpfile.'.eml';
        link $tmpfile, $emlfile or die "Cannot link $tmpfile to $emlfile: $!\n";
        unlink $tmpfile;
        unlink $journal_filepath if $delete;
    };
    if ($@) {
            print $fh $journal_msg; # save original mail data to tmpfile
            if (! $fh->close) {
                croak "Cannot write tempfile $tmpfile: $!\n";
            }

            my $errfile = $tmpfile.'.errdnl';
            if (link $tmpfile, $errfile) {
                unlink $tmpfile;
                print STDERR "Error parse msg #$count. Saved as $errfile (Original error: $@.\n" if $VERBOSE;
                syslog('ERR',"Error parse msg #$count. Saved as $errfile (Original error: $@");
            }
            else {
                print STDERR "Error parse msg mail #$count. Saved as $tmpfile (Original error: $@.\n" if $VERBOSE;
                syslog('ERR',"Error parse msg mail #$count. Saved as $tmpfile (Original error: $@");

            }
            next unless $force_delete;
            unlink $journal_filepath || die "Forced delete of $journal_filepath failed\n";
    }
    print "Save journalmail $journal_file as ${tmpfile}.eml\n" if $VERBOSE;
}
syslog('INFO',"$count journalfiles converted.");
print "$count journalfiles converted.\n" if $VERBOSE;


#
### SUBS ###

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


# help
#
sub help_exit
{
    print "Aufruf: benno-exchange-dirimport [-c <configfile>] [-D] [-i <inbox dir>] [[-n <num>] [-s] [-j] [-S]\n";
    print "  -c <config> read configuration from file\n";
    print "  -D          print debug messages\n";
    print "  -e <ext>    convert emails with extension (default: jrnl)\n";
    print "  -i <dir>    inbox directory (/srv/benno/inbox)\n";
    print "  -s <spool>  import spooldir (/srv/benno/exchange-spool)\n";
    print "  -t 2003     MS Exchange 2003 journal format (default >= 2007)\n";
    print "  -f          force deletion on parse errors\n";
    print "  -R <userid> run as <userid> (default benno)\n";
    exit 1;
}


### EOP ###
1;


package LWs::SingleInstance;

use strict;

use Fcntl ':flock';

#
# Exit program if more than one instance is runningg
#
INIT {
    if (tell(*main::DATA) == -1) {
        # __DATA__ handle not available
        print STDERR "$0 needs an __END__ literal at the end of the file.\n";
        exit 2;
    }
    elsif (!flock main::DATA, LOCK_EX | LOCK_NB) {
        # cannot lock __DATA__ "file"
        print STDERR "An instance of $0 is already running.\n";
        exit 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__

