#!/usr/bin/perl
#
# $Id$
#
use strict;
use Getopt::Std;
use File::Find;
use IO::File;
use PerlIO::gzip;
use lib qw(/usr/lib/benno-import-tools);
use Benno;
use Benno::Log;

my $VERSION = '2.8.8';

my %opts;
getopts('VhvSE:H:P:R:a:c:l:n:p:u:',\%opts);

my %conf = Benno->config($opts{c}) if $opts{c};

my $archive_dir     = $opts{a} || $conf{archive};
my $boxstate        = $opts{b} || "$archive_dir/boxstate.xml";

my $hostname        = $opts{H} || $conf{hostname};
my $filelist        = $opts{l} || $conf{filelist};
my $noverifycert    = $opts{n} || $conf{noverifycert};
my $username        = $opts{u} || $conf{username};
my $password        = $opts{p} || $conf{password};
my $port            = $opts{P} || $conf{port}         || 21543;
my $runuser         = $opts{R} || $conf{runuser}      || 'benno';
my $verbose         = $opts{v};
my $retryfile       = $opts{E};

$EXPORT::SIMULATE = $opts{S};
$EXPORT::ERRCOUNT = 0;


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

help_exit() if $opts{h};
help_exit('Hostname not set.') if ! $hostname;
help_exit('Username not set.') if ! $username;
help_exit('Password not set.') if ! $password;

Benno->run_as($runuser);

help_exit() if not ($archive_dir or $filelist or $retryfile);

use warnings;

$SIG{INT} = \&print_info;

$EXPORT::uri = 'https://'.$hostname.':'.$port.'/rest/inbox';
if ($filelist) {
    $EXPORT::LOG = new Benno::Log($filelist);
    print "Write export log: ".$EXPORT::LOG->logfile."\n";
    upload_filelist($filelist);
}
elsif ($retryfile) {
    $EXPORT::LOG = new Benno::Log($retryfile);
    print "Write export log: ".$EXPORT::LOG->logfile."\n";
    retry_errors($retryfile);
}
else {
    my $config_errors = '';
    if (! -f "$boxstate") {
        $config_errors .= "Boxstate \"$boxstate\" not found! Repopath incorrect?\n";
    }

    if ($config_errors) {
        print STDERR $config_errors;
        exit 1;
    }
    $EXPORT::LOG = new Benno::Log($archive_dir.'/archive');
    print "Write export log: ".$EXPORT::LOG->logfile."\n";
    find({ wanted => \&process_archive, follow_fast => 1}, ($archive_dir));
}


print "Export log written: ".$EXPORT::LOG->logfile."\n";
if ($EXPORT::ERRCOUNT) {
    print $EXPORT::ERRCOUNT." error(s) while processing\n";
    print "Start again to retry export error files:\n";
    print '  benno-archive2rest -E '.$EXPORT::LOG->{logfile}."\n";
}


### SUBS ####
### process_archive
sub process_archive
{
    my $filename = $File::Find::name;
    my $dirname  = $File::Find::dir;

    if ($filename eq $dirname)   { return undef; }  # directory
    if ($filename !~ /\.gz$/)    { return undef; }  # not a gzipped file
    if ($dirname =~ m!/journal!) { return undef; }  # journal subdir

    my $gzfile   = $File::Find::fullname; # full path to file
    process_file($gzfile);
}


### process_file
sub process_file
{
    my ($gzfile) = @_;

    $EXPORT::LOG->verbose("Read $gzfile");
    my $hash_re      = qr/^===== Hash:\s(.+)$/;
    my $sender_re    = qr/^Sender:.+?(\S+\@\S+)/;
    my $recipient_re = qr/^Recipient:.+?(\S+\@\S+)/;

    # uncompress file to pipe
    # Possible bug with fork, see Parallel::ForkManager
    open(FILE, "<:unix:gzip", $gzfile) || die "PROCESSING ERROR Cannot unzip $gzfile: $!\n";

    my $error;
    my $benno_hash;
    my $sender_set;
    my $recipient_set;
    my $sender = '';
    my $mail_content;
    my @recipient_headers;
    foreach my $line (<FILE>) {
        if ($line =~ $sender_re)    { $sender = $1; $sender_set = 1; }
        if ($line =~ $recipient_re) {
            push @recipient_headers, $1;
            $recipient_set = 1;
        }

        if ($line =~ $hash_re) {
            $benno_hash = $1;

            my @recipients;
            foreach my $address (@recipient_headers) {
                $address =~ s/[<>]//g;
                push @recipients,$address;
            }

            $sender = 'NOTSET' unless $sender_set;
            $mail_content .= "X-REAL-MAILFROM: $sender\n";
            foreach my $recipient (@recipients) {
                $mail_content .= "X-REAL-RCPTTO: $recipient\n";
            }

            $EXPORT::LOG->verbose("Checksum: $benno_hash");
            $EXPORT::LOG->verbose("Sender: $sender");
            $EXPORT::LOG->verbose("Recipients: @recipients");
            next;
        }

        if(!$benno_hash)  { next; }
        $mail_content .= $line;
    }

    eval {
        if (! $benno_hash) {
            die "PROCESSING ERROR No metadata header in file\n";
        }

        my $RC = new Benno::REST::Client($EXPORT::uri,$username,$password,$noverifycert);
        my $ret = $RC->send($mail_content);
        my $logmsg = "Send $gzfile to $EXPORT::uri: $ret";
        $EXPORT::LOG->write($logmsg);
        $EXPORT::LOG->verbose($logmsg);
    };
    if ($@) {
        $EXPORT::LOG->write("ERROR Cannot export $gzfile: $@");
        print STDERR ("ERROR Cannot export $gzfile: $@");
        $EXPORT::ERRCOUNT++;
    }
}



sub upload_filelist
{
    my ($filelist) = @_;
    open my $listfh, $filelist or die "Cannot open listfile: $!\n";
    foreach my $line (<$listfh>) {
        next if ($line =~ m!/journal/.+\.journal!);
        $line =~ s/\R//g;
        process_file($line);
    }
    close $listfh;
}


sub retry_errors
{
    my ($logfile) = @_;

    open my $logfh, $logfile or die "Cannot open logfile: $!\n";
    foreach my $line (<$logfh>) {
        next unless ($line =~ /ERROR/);
        my ($gzfile) = $line =~ /^.+?ERROR Cannot export (\S+)?:/;
        process_file($gzfile);
    }
    close $logfh;
}


sub print_info
{
    print STDERR "\n";
    print STDERR "Process $0 aborted.\n";
    print STDERR "\n";
    print STDERR $EXPORT::ERRCOUNT." error(s) while processing\n";
    print STDERR 'Logs written to '.$EXPORT::LOG->{logfile}."\n";
    exit 2;
}


### help_exit()
sub help_exit
{
    my $msg = shift;

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

    print "Usage: $0 <-a <repodir>|-l <filelist> -H <hostname> -u <username> -p <password>>\n";
    print "      [-c <configfile>] [-E <logfile> ] [-P <port>] [-R <user>] [-h] [-n] [-S] [-v]\n";
    print "\n";
    print "    -c <config>    config file\n";
    print "    -H <hostname>  hostname of benno-import-rest server\n";
    print "    -u <username>  username\n";
    print "    -p <password>  password\n";
    print "\n";
    print "    -a <repodir>   archive (repo) directory (/srv/benno/archive/repo)\n";
    print "    -l <filelist>  read filenames from listfile (created with: benno-write_repolist)\n";
    print "    -n             no verification of server certficate\n";
    print "    -P <port>      port (default: 21543)\n";
    print "    -b             boxstate file (default \$archive_dir/boxstate.xml)\n";
    print "    -E <logfile>   ERROR retry - try to export files with errors from log again.\n";
    print "\n";
    print "    -R <user>      run as user (default: benno)\n";
    print "    -S             simulate (connect but send no data)\n";
    print "    -v             verbose\n";
    print "    -V             print version\n";
    print "\n";
    
    exit 1;
}




package Benno::REST::Client;
use LWP::UserAgent;
use MIME::Base64;

sub new {
    my $class = shift;
    my ($uri,$username,$password,$noverifycert) = @_;

    my $UA;
    if ($noverifycert) {
        $UA = LWP::UserAgent->new(
               ssl_opts => {
                verify_hostname => 0, # will be removed in LWP::UserAgeng > 6.06
                SSL_verify_mode => 0, # LWP::UserAgent >= 6.05
                });
    }
    else {
        $UA = LWP::UserAgent->new();
    }

    my $self = {
        UA          => $UA,
        uri         => $uri,
        authstring  => encode_base64($username.':'.$password),

    };
    bless $self, $class;

    return $self;
}


sub send
{
    my ($self,$mail_content) = @_;

    my $Response;
    my $msg = '';

    if ($EXPORT::SIMULATE) {
        $mail_content = '';
    }

    $Response = $self->{UA}->put(
        $self->{uri},  
        Authorization   => 'Basic '.$self->{authstring},
        Content         => $mail_content,
    );

    my $return;
    if ($Response->is_success) {
        $return = $Response->decoded_content;
        $return =~ s/\r//g;
        if ($return =~ /^OK\s(.+?)$/) {
            return $1;
        }
        else {
            die 'RESPONSE ERROR '.$Response->status_line,"\n";
        }
    }
    else {
        if ($EXPORT::SIMULATE) {
            return '(SIMULATE)';
        }
        die 'HTTP ERROR '.$Response->status_line,"\n";
    }

    return '';
}


### EOP ###
1;


__END__

