#!/usr/bin/perl -w
# This script (sophomorix-belwue) is maintained by Rüdiger Beck
# It is Free Software (License GPLv3)
# If you find errors, contact the author
# jeffbeck@web.de  or  jeffbeck@linuxmuster.net

# modules
use strict;
#use Quota;
use Getopt::Long;
#use IMAP::Admin;
#use DBI;
#use utf8;
use HTML::TableExtract;
use Net::LDAP;
use JSON;
use Data::Dumper;
$Data::Dumper::Indent = 1;
$Data::Dumper::Sortkeys = 1;
$Data::Dumper::Useqq = 1;
$Data::Dumper::Terse = 1; 

use Sophomorix::SophomorixBase qw(
                                 print_line
                                 print_title
                                 log_script_start
                                 log_script_end
                                 log_script_exit
                                 check_options
                                 config_sophomorix_read
                                 result_sophomorix_init
                                 result_sophomorix_add
                                 result_sophomorix_check_exit
                                 result_sophomorix_print
                                 );

use Sophomorix::SophomorixSambaAD qw(
                                 AD_bind_admin
                                 AD_unbind_admin
                                 AD_dns_get
                                    );

my @arguments = @ARGV;

# ===========================================================================
# Optionen verarbeiten
# ==========================================================================

# Variablen für Optionen
$Conf::log_level=1;
my $help=0;
my $info=0;
my $json=0;
my $ldap_fetch=1;
my $no_ldap_fetch=0;
my $copy_to_teachershare=0;
my $create_login_data="";
my $dump_belwue_data=0;

my $config_file="/etc/linuxmuster/sophomorix/belwue-mail/belwue.conf";
my $belwue_downloads="/var/lib/sophomorix/belwue/downloads";
my $belwue_mailboxes="/var/lib/sophomorix/belwue/belwue.multimailboxes";
my $belwue_maillist="/var/lib/sophomorix/belwue/belwue.maillists";
my $belwue_aliases="/var/lib/sophomorix/belwue/belwue.aliases";

# copy-to-teachershare
my $targetdir="/var/lib/sophomorix/belwue-copy-to-teachershare";
my $targetsubdir="/var/lib/sophomorix/belwue-copy-to-teachershare/logindata";

system("mkdir -p /var/lib/sophomorix/belwue"); 

# Parsen der Optionen
my $testopt=GetOptions(
           "help|h" => \$help,
           "info|i" => \$info,
           "json|j+" => \$json,
           "verbose|v+" => \$Conf::log_level,
           "dump-belwue-data" => \$dump_belwue_data,
           "create-login-data=s" => \$create_login_data,
           "copy-to-teachershare" => \$copy_to_teachershare,
          );

my %sophomorix_result=&result_sophomorix_init("sophomorix-belwue");
# Prüfen, ob Optionen erkannt wurden, sonst Abbruch
&check_options($testopt);

my %config=&read_config($config_file);
my %belwue=();
my @accounts_short=();

my @multi_mailboxes=();
my @aliases=();
my @maillists=();

# calculate connect data
$config{'connect'}{BASE}="CN=".$config{'config'}{'BASE'};
$config{'connect'}{BASESIMPLE}=$config{'config'}{'BASE'};
$config{'connect'}{SCOPE}="sub";
$config{'connect'}{FILTER}="(cn=*)";
$config{'connect'}{HOST}=$config{'config'}{PROTOCOL}."://".
                         $config{'config'}{SERVER}.":".
                         $config{'config'}{PORT};
$config{'connect'}{ADMINUSER}=$config{'config'}{ADMINUSER};
$config{'connect'}{ADMINURL}=$config{'config'}{ADMINURL};
$config{'connect'}{PASSWORD}=$config{'config'}{PASSWORD};

#print Dumper(%config);

# --help
if ($help==1) {
   # Scriptname ermitteln
   my @list = split(/\//,$0);
   my $scriptname = pop @list;
   # Befehlsbeschreibung
   print('
sophomorix-belwue

Options
  -h  / --help
  -i  / --info
  -v  / --verbose
  -vv / --verbose --verbose

  --dump-belwue-data
  --create-login-data /path/to/file
  --copy-to-teachershare

Please see the sophomorix-belwue(8) man pages for full documentation
');
   print "\n";
   exit;
}



# --info
if ($info==1) {


}

##################################################
# Script starts here
##################################################

# --dump-belwue-data
if ($dump_belwue_data==1){
    ############################################################
    # ldap address book
    ############################################################ 
    print "Accessing Belwue addressbook via ldap\n";
    my ($ldap) = &bind_admin();
    my $mesg = $ldap->search(
                   base   => $config{'connect'}{BASE},
                   scope => $config{'connect'}{SCOPE},
                   filter => $config{'connect'}{FILTER},
                        );

    my $max = $mesg->count; 
    my $count = $mesg->count;
    for( my $index = 0 ; $index < $max ; $index++) {
        my $num=$index+1;
        my $entry = $mesg->entry($index);
        my $dn=$entry->dn();
        my $uid=$entry->get_value('uid');
        my $cn=$entry->get_value('cn');
        my $given=$entry->get_value('givenName');
        my $sn=$entry->get_value('sn');
        my $server=$entry->get_value('hostServer');
        my $mail=$entry->get_value('mail');

        if (not defined $given){
            $given="";
        }
        if($Conf::log_level>=2){
	    print "$num) dn: $dn\n";
	    print "   * cn:          $cn\n";
	    print "   * givenName:   $given\n";
	    print "   * hostServer:  $server\n";
	    print "   * mail:        $mail\n";
	    print "   * sn:          $sn\n";
	    print "   * uid:         $uid\n";
            print "\n";
        }

        #$belwue{'ldap'}{$uid}{'CN'}=$cn;
        #$belwue{'ldap'}{$uid}{'givenName'}=$given;
        #$belwue{'ldap'}{$uid}{'hostServer'}=$server;
        #$belwue{'ldap'}{$uid}{'mail'}=$mail;
        #$belwue{'ldap'}{$uid}{'sn'}=$sn;
        #$belwue{'ldap'}{$uid}{'uid'}=$uid;

        $belwue{'objects'}{$uid}{'CN'}=$cn;
        $belwue{'objects'}{$uid}{'givenName'}=$given;
        $belwue{'objects'}{$uid}{'hostServer'}=$server;
        $belwue{'objects'}{$uid}{'mail'}=$mail;
        $belwue{'objects'}{$uid}{'sn'}=$sn;
        $belwue{'objects'}{$uid}{'uid'}=$uid;
    }
    &unbind_admin($ldap); 
    ############################################################
    # downloading
    ############################################################ 
    print "Downloading and parsing html files:\n";
    system("mkdir -p $belwue_downloads");

    # object list
    my $download_objects_command="wget -q --http-user=admin ".
         "--http-password=$config{'connect'}{PASSWORD} ".
         "--output-document=$belwue_downloads/objects.html ".
         "\"$config{'connect'}{ADMINURL}/$config{'connect'}{BASESIMPLE}/ObjectList.html?domainName=$config{'connect'}{BASESIMPLE}\"";

   if($Conf::log_level>=2){
       print "\nWGET: $download_objects_command\n\n";
    }
    my $return=system($download_objects_command);
    &wget_return($return,"Objects");

    ############################################################
    # parsing
    ############################################################

    # file to parse
    my $html_doc = "$belwue_downloads/objects.html";
    print "   * Parsing $html_doc\n";

    # list of headers
    my $table_headers = [ 'Objekt', 'Typ','Speicher','Letzter Zugriff' ];

    # constructor
    my $table_extract = HTML::TableExtract->new(headers => $table_headers);
    $table_extract->utf8_mode(1);
    $table_extract->parse_file($html_doc);
    my ($table) = $table_extract->tables;

    foreach my $row ($table->rows) {
        my ($object,$type,$storage,$access)=@$row;
        if (not defined $access){
            $access="never";
        }
        # add to types
        if ($type eq "Mailingliste"){
            $belwue{'maillist'}{$object}{'type'}=$type;
            push @maillists, $object;
            ############################################################
            # downloading maillist data
            ############################################################
            my $download_list_command="wget -q --http-user=admin ".
                "--http-password=$config{'connect'}{PASSWORD} ".
                "--output-document=$belwue_downloads/$object.html ".
                "\"$config{'connect'}{ADMINURL}/$config{'connect'}{BASESIMPLE}/Subscribers.html?InCluster=1&domainName=$config{'connect'}{BASESIMPLE}&&listName=$object\"";
            if($Conf::log_level>=2){
                print "\nWGET: $download_list_command\n\n";
            }
            my $return=system($download_list_command);
            &wget_return($return,$object);

            ############################################################
            # parsing maillist data
            ############################################################
            my $html_doc = "$belwue_downloads/$object.html";
            print "   * Parsing $html_doc\n";

            # list of headers
            # Umlaut in header does nor work
#            my $table_headers = [ 'Email','Zustellungsart','Anmeldezeit','Nachrichten zurückgewiesene','Angezeigter Name' ];
            my $table_headers = [ 'Email', 'Zustellungsart', 'Anmeldezeit'];

            # constructor
            my $table_extract = HTML::TableExtract->new(headers => $table_headers);
            $table_extract->utf8_mode(1);
            $table_extract->parse_file($html_doc);
            my ($table) = $table_extract->tables;
            foreach my $row ($table->rows) {
                my ($member_email,$type,$time,$bounced,$displayname)=@$row;
                #print "$object: $member_email  $type  $time\n";
                if ($type ne "Abonnement aufheben"){
                    $belwue{'maillist'}{$object}{'members'}{$member_email}="member";
                    push @{ $belwue{'maillist'}{$object}{'memberlist'} }, $member_email;
                }
            }
            # sort the memberlist if not empty
            if ( not  $#{ $belwue{'maillist'}{$object}{'memberlist'} }==-1 ){
                 @{ $belwue{'maillist'}{$object}{'memberlist'} } = sort @{ $belwue{'maillist'}{$object}{'memberlist'}  };
            }
        } elsif ($type eq "Alias"){
            $belwue{'alias'}{$object}=$storage;
            $belwue{'alias_reverse'}{$storage}=$object;
            # aliases are in storage
            $belwue{'objects'}{$storage}{'alias'}=$object;
            push @aliases, $object;
        } elsif ($type eq "Multi-Mailbox"){
            $belwue{'multimailbox'}{$object}{'type'}="Multi-Mailbox";
            $belwue{'multimailbox'}{$object}{'storage'}=$storage;
            $belwue{'multimailbox'}{$object}{'access'}=$access;
            push @multi_mailboxes, $object;
        } else {
            print "Unknown object $type\n";
        }
    }
    ############################################################
    # printout data/write data into files
    ############################################################
    open (BOX, ">$belwue_mailboxes");
    open (ALIAS, ">$belwue_aliases");
    open (LIST, ">$belwue_maillist");

    @multi_mailboxes = sort @multi_mailboxes;
    print "\nMultiMailboxen:\n";
    my $num_mbox=1;
    foreach my $uid (@multi_mailboxes){
        print BOX "$uid\n";
        print "   $num_mbox) $uid\n";
        $num_mbox++;
    }

    @maillists = sort @maillists;
    print "\nMailing Listen:\n";
    foreach my $list (@maillists){
        my $member_string=join(",",@{ $belwue{'maillist'}{$list}{'memberlist'} });
        print LIST "$list:$member_string\n";
        print "   * $list\n";
        my $num_list=1;
        foreach my $member ( @{ $belwue{'maillist'}{$list}{'memberlist'} } ){
            if (not defined $member){
                next;
            } 
            print "     $num_list) --> $member\n";
            $num_list++;
        }
    }

    @aliases = sort @aliases;
    print "\nMail Aliase:\n";
    my $num=1;
    foreach my $alias (@aliases){
        print ALIAS "$alias:$belwue{'alias'}{$alias}\n";
        print "   $num) $alias --> $belwue{'alias'}{$alias}\n";
        $num++
    }

    close(BOX);
    close(ALIAS);
    close(LIST);

    if($Conf::log_level>=2){
        print Dumper(\%belwue);
    }
} 




# --create-login-data
if($create_login_data ne ""){
    # remove old data        
    system("rm -rf $targetdir"); 
    system("mkdir -p $targetdir"); 

    open(FILE,"$create_login_data") || 
              die "ERROR: $create_login_data not found!";
    while(<FILE>){
        chomp();
        my ($uid,$realname,$type,$pass,$storage,$alias)=split(/\t/);
        if ($uid eq "Name" and $realname eq "Realname"){
            next;
        }
        print "   * $uid\n";
        system("mkdir -p $targetdir/${uid}");
        open(USER,">$targetdir/${uid}/${uid}-mail-login.txt") || 
              die "ERROR: $targetdir not found!";
        print USER "\n";
        print USER "Zugangsdaten für die Emailadresse: ${uid}\@$config{'config'}{BASE}\n";
        print USER "                            Alias: $alias\@$config{'config'}{BASE}\n";
        print USER "\n";
        #print USER "Belwue-Mail-URL:  https://$config{'config'}{SERVER}\n";
        print USER "Benutzername:     ${uid}\@$config{'config'}{BASE}\n";
        print USER "Erst-Password:    $pass\n";
        print USER "Erst-Mailquota:   $storage\n";
        print USER "Mailbox-Typ:      $type\n";
        print USER "Adressbuch-Name:  $realname\n";
        print USER "\n";
        close(USER);
        system("chown ${uid}.root $targetdir/${uid}");
        system("chmod 0700 $targetdir/${uid}");
        system("chown ${uid}.root $targetdir/${uid}/${uid}-mail-login.txt");
        system("chmod 0600 $targetdir/${uid}/${uid}-mail-login.txt");
    }
    close(FILE);
    print "Login-Data created in $targetdir\n";
}


# --copy-to-teachershare
if($copy_to_teachershare){
	my $copy_command="rsync -av ${targetdir}/ ".
          "$config{'config'}{TEACHERUSER}\@".
          "$config{'config'}{TEACHERSERVER}:".
          "$config{'config'}{TEACHERSERVERDIR}";
    print "$copy_command\n";
    system($copy_command);
}


if($Conf::log_level>=2){
    print Dumper(%config);
}



############################################################
# subs
############################################################

sub bind_admin {
    # check connection 
    if($Conf::log_level>=3){
        print "   Checking Belwue connection ...\n";
    }

    # bind
    if($Conf::log_level>=3){
        print "HOST:      $config{'connect'}{HOST} \n";
        print "ADMINUSER: $config{'connect'}{ADMINUSER} \n";
        print "PASS:      $config{'connect'}{PASSWORD}\n";
    }
    my $ldap = Net::LDAP->new($config{'connect'}{HOST});

    my $mesg = $ldap->bind($config{'connect'}{ADMINUSER}, 
                      password => $config{'connect'}{PASSWORD});
    # show errors from bind
    $mesg->code && die $mesg->error;
    return ($ldap);
}



######################################################################
sub unbind_admin {
    my ($ldap) = @_;
    my $mesg = $ldap->unbind();
    #  show errors from unbind
    $mesg->code && die $mesg->error;
}



######################################################################
# error, when options are not given correctly
sub  check_options{
   my ($parse_ergebnis) = @_;
   if (not $parse_ergebnis==1){
      my @list = split(/\//,$0);
      my $scriptname = pop @list;
      print "\nYou have made a mistake, when specifying options.\n"; 
      print "See error message above. \n\n";
      print "... $scriptname is terminating.\n\n";
      exit;
   } else {
      if($Conf::log_level>=3){
         print "All options  were recognized.\n";
      }
   }
}



######################################################################
sub wget_return {
    my ($return,$object)=@_;
    if ($return==0){
        print "   * Succesfully downloded html page for $object\n";
    } else {
        print "   * ERROR downloading $object: wget returned error code $return\n";
        exit;
    }
}



######################################################################
sub read_config {
    my %config=();
    my ($file) = @_;
    if (-e $file) {
        open (SECRET, $file);
        while(<SECRET>){
            my ($key,$value)=split(/=/);
            $key=~s/^\s+//g;# remove leading whitespace
            $key=~s/\s+$//g;# remove trailing whitespace
            $value=~s/^\s+//g;# remove leading whitespace
            $value=~s/\s+$//g;# remove trailing whitespace
            $config{'config'}{$key}=$value;
        }
        close(SECRET);
    } else {
        print "Config File $file must be created with:\n";
        print "  PASSWORD=xyz\n";
        print "  ADMINUSER=admin\@mail.bszleo.de\n";
        print "  ADMINURL=admin\@mail.bszleo.de\n";
        print "  SERVER=mbox1.belwue.de\n";
        print "  BASE=mail.bszleo.de\n";
        print "  PORT=636\n";
        print "  PROTOCOL=ldaps\n";
        exit;
    }
    return %config;
}

