#!/usr/bin/perl
#
#
#
use strict;
use Getopt::Std;
use MIME::Base64;
use LWP::UserAgent;
use JSON;
no warnings 'utf8';

my $VERSION = '2.6.10';

my %opts;
getopts('AhCIJnvDOrtVc:e:f:L:q:s:S:H:P:R:',\%opts);

help_exit() if $opts{h};

my $allmails  = $opts{A};
my $clist     = $opts{C};
my $container = $opts{c}; # || 'SYSDATA';
my $filter    = $opts{f};
my $host      = $opts{H} || 'localhost';
my $immediate = $opts{I};
my $limit     = $opts{L} || 20;
my $port      = $opts{P} || 21080;
my $query     = $opts{q};
my $report    = $opts{r};
my $reporturl = $opts{R} || 'https://www.benno-mailarchiv.de/report';
my $secret    = $opts{s} || fetchSecret('/etc/benno/rest.secret','/etc/benno/benno.xml');
my $sortfield = $opts{S} || 'sendedMails';
my $textout   = $opts{t};
my $verbose   = $opts{v};

my $user      = 'benno2';


# _lw_ provisorium
my $unobfuscate = $opts{O};

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

$verbose += 2 if $opts{D} ;

help_exit() unless $secret;

if (!$immediate) {
    # wait random time (0-200s) to send request
    my $wait = rand() * 1000; $wait = $wait % 200;
    sleep $wait;
}

my $infoquery = $query || 'From:* AND NOT HEADER-X-SPAM-FLAG:YES ';
my $filterquery = $filter || 'SortableDate:['.ts2date(time-365*86400).
                              ' TO '.ts2date(time).']';

my $proto = 'http';
my $params = { 'archive'       => $container,
               'filterQuery'   => $filterquery,
               'mincount'      => $limit,
               'query'         => $infoquery,
               'sort'          => $sortfield,
             };
$unobfuscate and $params->{decode} = 'true';
$allmails    and $params->{showAll} = 'true';

my $path  = '/stats/';

my $uri = $proto.'://'.$host.':'.$port.$path;

my $REST = new REST($user,$secret,$uri,$params);
eval {
    $REST->query();
};
if ($@) {
    $uri = $proto.'://'.$host.':'.$port.$path;
    print STDERR "ERROR Request to \"$uri\" failed: $@\n";
    print STDERR "  URI: $uri\n";
    foreach my $param (keys %{$params}) {
        print STDERR "    $param -> $params->{$param}\n";
    }
    exit 2;
}

verbose(2,"URI: $proto://$host:$port$path");
if ($verbose >= 2) {
    foreach my $param (keys %{$params}) {
        verbose(2,"$param: $params->{$param}");
    }
}


if ($textout) {
    my $data = $REST->get_data($textout,$verbose);
    print  "container:     $data->{container}\n";
    print  "address query: $data->{query}\n"            if $verbose;
    print  "filter query:  $data->{filter}\n"           if $verbose;
    print  "query limit:   $data->{minCount}\n"         if $verbose;
    print  "mailbox count: $data->{mailboxCount}\n";
}
elsif ($clist) {
    my $data = $REST->get_data(1,$verbose);
    foreach my $container (@{$data->{containerList}}) {
        print $container->{name};
        print " ($container->{mailCount} mails)" if $verbose;
        print "\n";
    }
}
else {
    $REST->anonymize if !$verbose;
    print $REST->get_data(0,$verbose);
}

if (!$report) {
    exit 0;
}

my $EXT = LWP::UserAgent->new;
$EXT->env_proxy();
my $BennoReport = $EXT->post($reporturl,{'restinfo' => $REST->get_data});
if (!$BennoReport->is_success) {
    print STDERR "Report post failure: ",$BennoReport->status_line,"\n";
    print STDERR $EXT->content,"\n";
    exit 1;
}




### SUBS ###
### print help and exit
sub help_exit
{
    print "Usage: $0 [-h] [-V] -s <secret> [-H <hostname>] [-c <container>] [-P <port>] [-q <query>] [-r] [-t] [-S]\n";
    print "    -c '<container>'    archive container\n";
    print "    -D                  debug mode\n";
    print "    -f '<lucene query>' lucene filter string or file with filter\n";
    print "    -H <hostname>       (localhost)\n";
    print "    -I                  immediate request (don't sleep random)\n";
    print "    -L '<num>'          limit response list to <num> mails\n";
    print "    -P <port>           (21080)\n";
    print "    -q '<lucene query>' lucene query string or file with filter\n";
    print "    -C                  print container list\n";
    print "    -r                  report (send) resultlist\n";
    print "    -R '<url>'          report url (default: https://www.benno-mailarchiv.de/report\n";
    print "    -s <secret>         shared secret\n";
    print "                        (search header as \"HEADER-<HEADER-NAME>\")\n";
    print "    -t                  human readable output (default JSON)\n";
    print "    -v                  verbose\n";
    print "    -V                  print version\n";
    print "\n";
    
    exit 1;
}

### print and format debug messages
sub verbose
{
    my ($level,$string) = @_;
    if ($verbose >= $level) {
        print STDERR $string,"\n";
    }   
}


### fetch password from config file
sub fetchSecret
{
    my $sfile    = shift;
    my $bennoxml = shift;
    my $secret;

    if (open SF, $sfile) {
        $secret = <SF>;
        chomp $secret;
        close SF;
        return $secret; 
    }

    open XMLFILE, $bennoxml;
    foreach my $line (<XMLFILE>) {
        chomp $line;
        ($secret) = $line =~ m!<sharedSecret>(.+)</sharedSecret>!;
        last if $secret;
    }
    return $secret;
}


### read first line from file
sub read_first_line
{
    my $filename = shift;

    if (!open FILE, $filename) {
        print STDERR "Cannot open file $filename. $!\n";
        exit 2;
    }
    my $line = <FILE>;
    chomp $line;

    return $line;
}


### ts2date
sub ts2date
{
    my ($ts) = @_;

    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst)=localtime($ts);
    #                          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);

    return $year.$mon.$mday.$hour.$min;
}


package REST;
use strict;
use MIME::Base64;

sub new
{
    my $this  = shift;
    my ($user,$password,$uri,$params) = @_;

    my $UA = LWP::UserAgent->new;

    my $b64creds = encode_base64($user.':'.$password);
    $UA->default_header('Authorization' => 'Basic '.$b64creds);

    my $self = { 
                 archiveSize  => 0,
                 mailboxCount => 0,
                 UA           => $UA,
                 JSON         => JSON->new->allow_nonref,
                 _uri         => $uri,
                 _params      => $params,
                 _data        => '',
    };

    my $class = ref($this) || $this;
    bless $self, $class;
}


sub query
{
    my ($self) = @_;

    my $Rest = $self->{UA}->post($self->{_uri},$self->{_params});
    my $j_data = $self->_get_response($Rest);

    $self->{_data} = $j_data;
    $self->{_data}->{anonymized}   = 0;
}


sub get_data
{
    my ($self,$textout,$verbose) = @_;

    my $retval;
    if ($textout) {
        $retval = $self->{_data};
    }
    else {
        $self->{JSON}->pretty(1) if $verbose;
        $retval = $self->{JSON}->encode($self->{_data});
    }
    
    return $retval;
}


sub anonymize
{
    my ($self) = @_;

    my $count = 1;
    foreach my $entry (@{$self->{_data}->{containerList}}) {
        next if $entry->{name} eq 'SYSDATA';
        $entry->{name} = $count;
        $count++;
    }

    $count = 1;
    foreach my $entry (@{$self->{_data}->{addressList}}) {
        $entry->{SenderAddress} = $count;
        $count++;
    }

    $count = 1;
    foreach my $entry (@{$self->{_data}->{containerList}}) {
        next if $entry->{name} eq 'SYSDATA';
        $entry->{name} = $count;
        $count++;
    }

    my $mcount = 1;
    foreach my $entry (@{$self->{_data}->{addressList}}) {
        $entry->{SenderAddress} = $count;
        $count++;
    }

    $self->{_data}->{anonymized} = 1;
}


sub format_json
{
    my ($json) = @_;

    
}



sub _get_response
{
    my ($self,$Rest) = @_;

    my $repdata;
    if ($Rest->is_success) {
        $repdata = $Rest->decoded_content;
    }
    else {
        #die "REST request error: ",$Rest->status_line,": ",$Rest->content,"\n";
        die "REST request error: ",$Rest->status_line,"\n";
    }

    my $retval;
    eval {
        $retval = $self->{JSON}->decode($repdata);
    };
    if ($@) {
        die "REST returns no JSON data";
    }

    return $retval;
}

### EOP ###
1;


