#!/usr/bin/perl
#
# $Id$
#
use strict;
use Getopt::Std;
use File::Find;
use IO::File;

my $VERSION = '2.8.4';


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

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

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

my $hostname        = $opts{H};
my $hostname        = $opts{H} || $conf{hostname};
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};

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


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

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

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

use warnings;
if (! -f "$boxstate") {
    print STDERR "Boxstate \"$boxstate\" not found! Repopath incorrect?\n";
    exit 1;
}

$EXPORT::LOG = new ExportLog($archive_dir);

my $uri = 'https://'.$hostname.':'.$port.'/rest/inbox';
find({ wanted => \&process_archive, follow => 1}, ($archive_dir));


### 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 return %config;
  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;
}


### 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 = $_;

    my $dirprefix;
    (my $boxfilepath = $dirname) =~ s/^$archive_dir//;
    if ($boxfilepath) {
        #($dirprefix) = $boxfilepath =~ /([^\/]+)/;
        ($dirprefix = $boxfilepath) =~ s/\///g;
    }

    if (!$dirprefix) {
        print STDERR "Dirname: $dirname\n";
        print STDERR "Cannot extract box prefix. Please set with -p\n";
        exit 1;
    }
    $dirprefix =~ s/\//:/;          # boxprefix
    $dirprefix =~ s/\///g;          # remove slashes

    eval {
        process_file($gzfile,$dirprefix,$archive_dir);
    };
    if ($@) {
        print STDERR "Cannot export $gzfile: $@\n";
    }
}


### process_file
sub process_file
{
    my ($gzfile,$dirprefix,$archive_dir,$export_dir) = @_;

    my $benno_hash;
    my @recipient_headers;

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

    # uncompress file to pipe
    open(FILE, "gzip -c -d $gzfile|") || print STDERR "Cannot unzip file. $!\n";
    if ($export_dir) {
        if (! -d $export_dir) {
            print STDERR "No directory: $export_dir\n";
            exit 1;
        }
        $filename =~ s/^$archive_dir//;
        $filename =~ s/\//-/g;
        $filename =~ s/^-//;
    }

    my $error;
    my $sender_set;
    my $recipient_set;
    my $sender = '';
    my $mail_content;
    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;
    }
    if ($error) {
        print STDERR "Mail error: $error ($File::Find::name)\n";
        $EXPORT::LOG->verbose("ERROR: Mail error $error ($File::Find::name)\n");

        return $error;
    }

    my $RC = new Benno::REST::Client($uri,$username,$password,$noverifycert);
    $RC->send($filename,$mail_content) 

}


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

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

    print "Usage: $0 [-h] [-S] [-v] -H <hostname> -u <username> -p <password>\n";
    print "          [-c <config>] [-P <port>] [-n] [-r <repo directory] [-R <user>]\n";
    print "\n";
    print "    -H <hostname>    hostname of benno-import-rest server\n";
    print "    -u <username>    username\n";
    print "    -p <password>    password\n";
    print "\n";
    print "    -c <config> config file)\n";
    print "    -n          no verification of server certficate\n";
    print "    -P <port>   port (default: 21543)\n";
    print "    -R <user>   run as user (default: benno)\n";
    print "    -r <repo>   repo directory (/srv/benno/archive/repo)\n";
    print "    -b          boxstate file (default \$archive_dir/boxstate.xml)\n";
    print "\n";
    print "    -S          simulate (connect but send no data)\n";
    print "    -v          verbose\n";
    print "    -V          print version\n";
    print "\n";
    
    exit 1;
}



package ExportLog;

### ExportLog
sub new
{
    my $class = shift;
    my ($repodir) = (@_);

    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst)=localtime(time);
    #                         0-11,+1900,0-6  ,0-366,SZ=(+)
    #                                   ,So-Sa,0-366,WZ=0,NA=(-)

    $year += 1900;
    $mon  += 1; $mon = sprintf("%02d",$mon);
    $mday = sprintf("%02d",$mday);

    my $filename = $repodir.'/export_rest-'.$year.'-'.$mon.'-'.$mday.'.log';

    open(my $logfh,'>>',$filename)
        or die "Cannot open logfile $filename: $!\n";

    my $self = {
        logfile => $filename,
        logfh   => $logfh,

    };
    bless $self, $class;

    return $self;
}


### logging
sub write
{
    my ($self,$msg) = (@_);

    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst)=localtime(time);
    #                         0-11,+1900,0-6  ,0-366,SZ=(+)
    #                                   ,So-Sa,0-366,WZ=0,NA=(-)

    $year += 1900;
    $mon  += 1; $mon = sprintf("%02d",$mon);
    $mday = sprintf("%02d",$mday);
    $hour = sprintf("%02d",$hour);
    $min  = sprintf("%02d",$min);
    $sec  = sprintf("%02d",$sec);
    my $ts = $year.'-'.$mon.'-'.$mday.' '.$hour.':'.$min.':'.$sec;

    my $logfh = $self->{logfh};
    print $logfh $ts.' '.$msg."\n"; 
    print $ts.' '.$msg."\n" if -t STDOUT;
}


### verbose
sub verbose
{
    my ($self,$msg) = @_;
    if ($verbose) {
        print $msg, "\n";
    }
}


### close
sub close
{
    my $self = shift;

    close $self->{logfh};
}


### EOP ###
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,$gzfile,$mail_content) = @_;

    my $Response;
    my $msg = '';

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

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

    my $logmsg = "Send $gzfile to $self->{uri}";
    $EXPORT::LOG->verbose($msg.$logmsg);

    my $return;
    if ($Response->is_success) {
        $return = $Response->decoded_content;
        $return =~ s/\r//g;
        if ($return =~ /^OK\s(.+?)$/) {
            $EXPORT::LOG->write($logmsg);
            return $1;
        }
        else {
            die "Error decoding return value: $return";
        }
    }
    else {
        if ($EXPORT::SIMULATE) {
            print "$msg$logmsg\n";
            return;
        }
        $EXPORT::LOG->write('ERROR: '.$Response->status_line);
        die 'ERROR '.$Response->status_line,"\n";
    }
}


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


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) {
        use Sys::Syslog;
        openlog('benno-send2rest','nowait,pid','mail');
        # cannot lock __DATA__ "file"
        print STDERR "An instance of $0 is already running.\n" if -t;
        syslog('ERR',"An instance of $0 is already running.\n");
        exit 1;
    }
}

### EOP ###
1;



__END__

