#!/usr/bin/perl -w
# This script (sophomorix-samba) 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;
Getopt::Long::Configure ("bundling");
use Sophomorix::SophomorixConfig;
use Sophomorix::SophomorixBase qw(
                                 check_options
                                 read_smb_conf
                                 result_sophomorix_init
                                 config_sophomorix_read
                                 json_dump
                                 );
use Sophomorix::SophomorixSambaAD qw(
                                    samba_stop
                                    samba_start
                                    samba_status
                                    );
use JSON;
use Data::Dumper;
$Data::Dumper::Indent = 1;
$Data::Dumper::Sortkeys = 1;
$Data::Dumper::Useqq = 1;
$Data::Dumper::Terse = 1; 
#    return \%master;

use Sophomorix::SophomorixSambaAD qw(
                                 AD_school_create
                                 AD_bind_admin
                                 AD_dns_get
                                 AD_get_schema
                                 AD_unbind_admin
                                 AD_user_create
                                 AD_group_create
                                 AD_group_addmember
                                 AD_group_update
                                 AD_get_schoolname
                                 AD_get_name_tokened
                                 AD_dn_fetch_multivalue
                                 AD_object_search
                                 AD_get_passwd
                                 AD_sophomorix_schema_update
                                    );


my @arguments = @ARGV;

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

# Variablen für Optionen
$Conf::log_level=1;
my $help=0;
my $info=0;
my $json=0;
my $backup_samba="";
my $restore_samba="";
my $schema_load=0;
my $restart=0;
my $stop=0;
my $status=0;
my $start=0;

my $edit_searchflags="";
my $editor="emacs";
my $show_all_attributes=0;
my $show_sophomorix_attributes=0;
my $show_attribute="";
my $show_roletype=0;
my $show_webui_permissions=0;
my $show_lang=0;

my $samba_private="/var/lib/samba/private";
my $samba_private_d="/var/lib/samba/private/sam.ldb.d";


# Parsen der Optionen
my $testopt=GetOptions(
           "help|h" => \$help,     
           "info|i" => \$info,
           "json|j+" => \$json,
           "backup-samba=s" => \$backup_samba,
           "restore-samba=s" => \$restore_samba,
           "edit-searchflags=s" => \$edit_searchflags,
           "show-attribute=s" => \$show_attribute,
           "show-all-attributes" => \$show_all_attributes,
           "show-sophomorix-attributes" => \$show_sophomorix_attributes,
           "show-webui-permissions" => \$show_webui_permissions,
           "show-roletype" => \$show_roletype,
           "show-lang" => \$show_lang,
           "editor=s" => \$editor,
           "schema-load" => \$schema_load,
           "start" => \$start,
           "stop" => \$stop,
           "status" => \$status,
           "restart" => \$restart,
          );



# Prüfen, ob Optionen erkannt wurden, sonst Abbruch
&check_options($testopt);

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

Options
  -h  / --help
  --backup-samba suffix    (backups the database)
  --restore-samba suffix   (restores the database)
  --schema-load            (loads the sophomorix-schema)

  --restart                (restarts samba)
  --stop                   (stops samba)
  --start                  (starts samba)
  --status                 (show samba status)

If you forgot the suffix look here:
# ls -l /var/lib/samba/private/*ldb*


Show a schema attribute in detail:
  --show-attribute <attribute>
Show all attribute names
  --show-all-attributes
Show all sophomorix attribute names:
  --show-sophomorix-attributes 
Show webui permissions
--show-webui-permissions

Show all user roles and group types:
  --show-roletype

Show all allowed languages:
  --show-lang

You shold know exactly what you are doing, if you use the following options:

Editing searchFlags of <attribute> with an editor (default is emacs):
  --edit-searchflags <attribute> (--editor <nano>|<vi>) 
    (use hyphenated name for <attribute>)
  Example
    --edit-searchflags sophomorix-Comment (--editor vi)
       searchFlags: 0    -> public visible attribute
       searchFlags: 128  -> confidential attribute

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





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

# start and stop before connect-test 
# --start
if ($start==1){
    print "\nStarting samba\n\n";
    &samba_start();
    exit;
}



# --stop
if ($stop==1){
    print "\nStopping samba\n\n";
    &samba_stop();
    exit;
}



# --status
if ($status==1){
    &samba_status();
    exit;
}



# --restart
if ($restart==1){
    print "\nRestarting samba\n\n";
    &samba_stop();
    &samba_start();
    exit;
}



# --backup-samba
if ($backup_samba ne ""){
    print "\nBacking up samba databases with suffix -$backup_samba\n\n";
    my @ldb_files = &get_ldb_files();
    print "##### saving ldb files: #####\n";
    foreach my $ldb (@ldb_files){
        my $command="cp -v ${samba_private}/${ldb} ${samba_private}/${ldb}-${backup_samba}";
        #print "  $command\n";
        system($command);
    } 
    my @ldb_files_d = &get_ldb_d_files();
    print "##### saving ldb files: #####\n";
    foreach my $ldb_d (@ldb_files_d){
        my $command="cp -v ${samba_private_d}/${ldb_d} ${samba_private_d}/${ldb_d}-${backup_samba}";
        #print "  $command\n";
        system($command);
    } 
    exit;
}



# --restore-samba
if ($restore_samba ne ""){
    &samba_stop();
    print "\nRestoring samba databases with suffix -$restore_samba\n\n";
    my @ldb_files = &get_ldb_files();
    print "##### restoring ldb files: #####\n";
    foreach my $ldb (@ldb_files){
        my $command="cp -v ${samba_private}/${ldb}-${restore_samba} ${samba_private}/${ldb} ";
        #print "  $command\n";
        system($command);
    } 
    my @ldb_files_d = &get_ldb_d_files();
    print "##### restoring ldb files: #####\n";
    foreach my $ldb_d (@ldb_files_d){
        my $command="cp -v ${samba_private_d}/${ldb_d}-${restore_samba} ${samba_private_d}/${ldb_d} ";
        #print "  $command\n";
        system($command);
    } 
    &samba_start();
    exit;
}



# --schema-load
if ($schema_load==1){
    my %smb_conf=();
    &read_smb_conf(\%smb_conf);
    if ($smb_conf{'samba'}{'from_smb.conf'}{'DomainDNS'} eq "DC="){
        print "\nERROR: Cannot load schema with a misconfigured smb.conf\n\n";
    } else {
        &samba_stop();
        print "\nLoading sophomorix schema into ldap\n\n";
        my $dn=$smb_conf{'samba'}{'from_smb.conf'}{'DomainDNS'};
        my $command="cd /usr/share/sophomorix/schema; ./sophomorix_schema_add.sh $dn . -H /var/lib/samba/private/sam.ldb -writechanges";
        print "$command\n";
        system($command);

        # adding schema updates
        &AD_sophomorix_schema_update($smb_conf{'samba'}{'from_smb.conf'}{'DomainDNS'});

        &samba_start();
    }
    exit;
}


############################################################
# options that need a connectable  AD follow here
############################################################

# Reading Configuration
my ($ldap,$root_dse) = &AD_bind_admin(\@arguments,\%sophomorix_result,$json);
my $root_dns=&AD_dns_get($root_dse);
my %sophomorix_config=&config_sophomorix_read($ldap,$root_dse,\%sophomorix_result);
#my ($smb_admin_pass)=&AD_get_passwd($DevelConf::sophomorix_file_admin,
#                                     $DevelConf::secret_file_sophomorix_file_admin);



# --set-confidential / --set-nonconfidential /
if ($edit_searchflags ne ""){
    my $attribute=$edit_searchflags;

    my $command="ldbedit --editor ".$editor." -H /var/lib/samba/private/sam.ldb CN=".
                $attribute." searchflags --option=\"dsdb:schema update allowed=yes\"".
                " -b CN=schema,CN=configuration,".$root_dse;
    print "$command\n";

    system($command);

    #my $ldif="dn: CN=Sophomorix-Comment,CN=Schema,CN=Configuration,DC=linuxmuster,DC=local\n".
    #         "searchFlags: 1\n";


}



# --show-all-attributes
if ($show_all_attributes==1){
    my ($ref_schema) = &AD_get_schema({ldap=>$ldap,
                                       root_dse=>$root_dse,
                                       root_dns=>$root_dns,
                                       sophomorix_config=>\%sophomorix_config,
                                      });
    # class schema (no searchflag)
    my $line1 ="+----------------------------------------------------------------------------+\n";
    my $line  ="+-------+--------------------------------------------------------------------+\n";
    my $header="| Nr.   | Attribute                                                          |\n";
    print $line1;
    printf "| classSchema: %-5s                                                         |\n",
            $ref_schema->{'RESULT'}{'LDAPDisplayName'}{'ALL_ATTRS'}{'classSchema'}{'COUNT'};    
    print $line;
    print $header;
    print $line1;
    my $count=0;
    foreach my $attr ( @{ $ref_schema->{'LISTS'}{'LDAPDisplayName'}{'ALL_ATTRS'}{'classSchema'} } ) {
        $count++;
        printf "| %6s|  %-66s|\n",$count,$attr;
    } 
    print $line;
    print "\n";

    # attribute schema
    $line1 ="+----------------------------------------------------------------------------+\n";
    $line  ="+------+-----+---------------------------------------------------------------+\n";
    $header="| Nr.  |  SF | Attribute                                                     |\n";
    print $line1;
    printf "| attributeSchema: %-5s                                                     |\n",
            $ref_schema->{'RESULT'}{'LDAPDisplayName'}{'ALL_ATTRS'}{'attributeSchema'}{'COUNT'};
    print $line;
    print $header;
    print $line;
    $count=0;
    foreach my $attr ( @{ $ref_schema->{'LISTS'}{'LDAPDisplayName'}{'ALL_ATTRS'}{'attributeSchema'} } ) {
        $count++;
        my $search_flag=${ $ref_schema->{'LDAPDisplayName'}{$attr}{'searchflags'} }[0];
        printf "|%6s| %3s | %-62s|\n",$count,$search_flag,$attr;
    } 
    print $line;
    print "SF: searchFlags (128: confidential   0:noindex   1:index)\n";
} 



# --show-sophomorix-attributes
if ($show_sophomorix_attributes==1){
    my ($ref_schema) = &AD_get_schema({ldap=>$ldap,
                                       root_dse=>$root_dse,
                                       root_dns=>$root_dns,
                                       sophomorix_config=>\%sophomorix_config,
                                      });
    #print Dumper $ref_schema;
    # class schema (no searchflag)

    my $line1 ="+--------------------------------------------------------------------------------------------+\n";
    my $line  ="+-------+------------------------------------------------------------------------------------+\n";
    my $header="| Nr.   | Attribute                                                                          |\n";
    print "SophomorixSchemaVersion: ${ $ref_schema->{'LDAPDisplayName'}{'sophomorixSchemaVersion'}{'rangeupper'} }[0]\n";
    print $line1;
    printf "| classSchema: %-5s                                                                         |\n",
            $ref_schema->{'RESULT'}{'LDAPDisplayName'}{'SOPHOMORIX_ATTRS'}{'classSchema'}{'COUNT'};    
    print $line;
    print $header;
    print $line;
    my $count=0;
    foreach my $attr ( @{ $ref_schema->{'LISTS'}{'LDAPDisplayName'}{'SOPHOMORIX_ATTRS'}{'classSchema'} } ) {
        $count++;
	my $cn=${ $ref_schema->{'LDAPDisplayName'}{$attr}{'cn'} }[0];
        printf "| %6s|  %-82s|\n",$count,$attr." (".$cn.")";
    } 
    print $line;
    print "\n";

    # attribute schema
    $line1 ="+--------------------------------------------------------------------------------------------+\n";
    $line  ="+------+-----+-------------------------------------------------------------------------------+\n";
    $header="| Nr.  |  SF | Attribute                                                                     |\n";
    print $line1;
    printf  "| attributeSchema: %-5s                                                                     |\n",
            $ref_schema->{'RESULT'}{'LDAPDisplayName'}{'SOPHOMORIX_ATTRS'}{'attributeSchema'}{'COUNT'};
    print $line;
    print $header;
    print $line;
    $count=0;
    foreach my $attr ( @{ $ref_schema->{'LISTS'}{'LDAPDisplayName'}{'SOPHOMORIX_ATTRS'}{'attributeSchema'} } ) {
        $count++;
	my $search_flag=${ $ref_schema->{'LDAPDisplayName'}{$attr}{'searchflags'} }[0];
	my $cn=${ $ref_schema->{'LDAPDisplayName'}{$attr}{'cn'} }[0];

        printf "|%6s| %3s | %-78s|\n",$count,$search_flag,$attr." (".$cn.")";
    }
    print $line;
    print "SophomorixSchemaVersion: ${ $ref_schema->{'LDAPDisplayName'}{'sophomorixSchemaVersion'}{'rangeupper'} }[0]\n";
    print "SF: searchFlags (128: confidential   0:noindex   1:index)\n";
} 



# --show-attribute <LDAPDisplayName>
if ($show_attribute ne ""){
    my ($ref_schema) = &AD_get_schema({ldap=>$ldap,
                                       root_dse=>$root_dse,
                                       root_dns=>$root_dns,
                                       sophomorix_config=>\%sophomorix_config,
                                      });
    my $jsoninfo="SCHEMA_ATTRIBUTE";
    my $jsoncomment="The AD Schema";
    &json_dump({json => $json,
                jsoninfo => $jsoninfo,
                jsoncomment => $jsoncomment,
                object_name => $show_attribute,
                log_level => $Conf::log_level,
                hash_ref => $ref_schema,
                sophomorix_config => \%sophomorix_config,
               });

}



# --show_lang
if ($show_lang==1){
    print "\n";
    print "########## Language configuration ####################################\n";
    my $ref_sophomorix_config=\%sophomorix_config;
    my @itemlist=();
    foreach my $keyname (keys %{$ref_sophomorix_config->{'LOOKUP'}{'LANG_ALLOWED'} } ) {
        push @itemlist,$keyname;
    }
    foreach my $item (@itemlist){
        print "   ALLOWED LANG: $item     (from sophomorix.ini)\n";
    }

    print "\n";
    print "Configured languages on this system:\n\n";
    print "   GLOBAL:\n";
    printf "%20s: %-6s \n","global",$ref_sophomorix_config->{'GLOBAL'}{'LANG'};
    print "\n";
    print "   SCHOOLS (inherited from GLOBAL if not configured otherwise):\n";
    foreach my $school (@{ $ref_sophomorix_config->{'LISTS'}{'SCHOOLS'} }){
        printf "%20s: %-5s \n",$school,$ref_sophomorix_config->{'SCHOOLS'}{$school}{'LANG'};
    }
    print "\n";
}



# --show-roletype
if ($show_roletype==1){
    print "\n";
    print "########## sophomorixRole ####################################\n";
    my @itemlist=();
    my $ref_sophomorix_config=\%sophomorix_config;
    foreach my $keyname (keys %{$ref_sophomorix_config->{'LOOKUP'} } ) {
        if ($keyname eq "ROLES_ALL" or $keyname eq "ROLES_ALLADMINS"){
            next;
        }
        push @itemlist,$keyname;
    }
    @itemlist = sort @itemlist;
    foreach my $item (@itemlist){
        my @rolelist=();
        print "$item:\n";
        foreach my $keyname (keys %{$ref_sophomorix_config->{'LOOKUP'}{$item} } ) {
           push @rolelist,$keyname; 
        }
        @rolelist = sort @rolelist;
        foreach my $role (@rolelist){
            if ($item eq "ROLES_DEVICE"){
                if ($ref_sophomorix_config->{'INI'}{"computerrole.".$role}{'COMPUTER_ACCOUNT'} eq "TRUE"){
                    print "   * $role (computer account)\n";
                } else {
                    print "   * $role\n";
                }
            } else {
                print "   * $role \n";
            }
        }
        print "\n";
    }

    my @typelist=();
    print "########## sophomorixType ####################################\n";
    foreach my $keyname (keys %{$ref_sophomorix_config->{'INI'}{'TYPE'} } ) {
        push @typelist,$ref_sophomorix_config->{'INI'}{'TYPE'}{$keyname};
    }
    @typelist = sort @typelist;
        foreach my $type (@typelist){
            print "   * $type\n";
        }
        print "\n";
}



# --show-webui-permissions
if ($show_webui_permissions==1){
    print "\n";
    my $ref_sophomorix_config=\%sophomorix_config;
    print "##### allowed module paths for sophomorix-webui-permissions #####################\n";
    print "##### $ref_sophomorix_config->{'INI'}{'WEBUI'}{'INI'}\n";
    print "##### webui maintainer defaults in ()\n";
    my @rolelist=();
    foreach my $role (keys %{$ref_sophomorix_config->{'UI'}{'CONFIG'}{'WEBUI_PERMISSIONS'} } ) {
        push @rolelist,$role;
    }
    @rolelist = sort @rolelist;
    foreach my $role (@rolelist){
        print "   * $role\n";
        my @modlist=();
        foreach my $mod_path (keys %{ $ref_sophomorix_config->{'UI'}{'CONFIG'}{'WEBUI_PERMISSIONS_LOOKUP'}{$role} } ){
            push @modlist,$mod_path;
	}
        @modlist = sort @modlist;
        foreach my $mod_path (@modlist){
            print "      --->  $mod_path ($ref_sophomorix_config->{'UI'}{'CONFIG'}{'WEBUI_PERMISSIONS_LOOKUP'}{$role}{$mod_path})\n";
        }
    }
    print "\n";
}



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



sub get_ldb_files {
    my @files=();
    opendir SAMBADB, $samba_private or return;
    foreach my $file (readdir SAMBADB){
        if ($file eq "."){next};
        if ($file eq ".."){next};
        if ($file=~m/.ldb$/){
            push @files,$file;
        }
    }
    closedir SAMBADB;
    @files = sort @files;
    return @files;
}



sub get_ldb_d_files {
    my @files=();
    opendir SAMBADB, $samba_private_d or return;
    foreach my $file (readdir SAMBADB){
        if ($file eq "."){next};
        if ($file eq ".."){next};
        if ($file=~m/.ldb$/){
	    push @files,$file;
        }
    }
    closedir SAMBADB;
    @files = sort @files;
    return @files;
}
