#!/usr/bin/perl

use warnings;
use strict;

use IO::Socket;	#to get IP address
use File::Find;
use File::Compare;
use File::Basename;
#use File::Path qw(make_path);
use File::Path qw(mkpath);

# IMPORTANT:
# After loading the debconf module DO NOT output anything to STDOUT
# use STDERR instead.
use Debconf::Client::ConfModule ':all';

our $IDP_CONF_BASE      = "/etc/shibboleth-identity-provider";

our $IDP_CONF_DIST = "$IDP_CONF_BASE/dist-conf";
our $CRED_DIR     = "$IDP_CONF_BASE/credentials";
our $CONF_DIR     = "$IDP_CONF_BASE/conf";

our $CRT_NAME      = "$CRED_DIR/idp.crt";
our $KEY_NAME      = "$CRED_DIR/idp.key";

our $SIG_CRT_NAME      = "$CRED_DIR/idp-signing.crt";
our $SIG_KEY_NAME      = "$CRED_DIR/idp-signing.key";

our $ENC_CRT_NAME      = "$CRED_DIR/idp-encryption.crt";
our $ENC_KEY_NAME      = "$CRED_DIR/idp-encryption.key";

our $METADATA_DIR  = "$IDP_CONF_BASE/metadata";
our $METADATA_FILE = "$METADATA_DIR/idp-metadata.xml";

our $TOMCAT_DIST = "$IDP_CONF_DIST/tomcat-conf";
our $TOMCAT_BASE = "$IDP_CONF_BASE/tomcat-conf";

my $server_fqdn = (get('shibboleth-identity-provider/server_fqdn'))[1];
my $server_path = (get('shibboleth-identity-provider/server_path'))[1];
my $server_scop = (get('shibboleth-identity-provider/server_scop'))[1];
my $server_ladr = (get('shibboleth-identity-provider/server_ladr'))[1];
my $server_jvmr = (get('shibboleth-identity-provider/server_jvmr'))[1];
my $shib_url    = $server_path . "/idp/shibboleth";
my $entity_id   = "https://" . $server_fqdn . $shib_url;

my @puppet_confs = ("access-control.xml", "global.xml", "services.xml", "audit.xml", "logback.xml", "attribute-resolver.xml", "attribute-filter.xml", "relying-party.xml", "saml-nameid.xml", "metadata-providers.xml", "authn/authn-comparison.xml", "authn/mfa-authn-config.xml");
my @hu_confs = ("intercept/profile-intercept.xml", "intercept/intercept-events-flow.xml", "authn/password-authn-config.xml");

our %replacement;
$replacement{'IDP_ENTITY_ID'}   = $entity_id;
$replacement{'IDP_SCOPE'}       = $server_scop;
$replacement{'IDP_HOSTNAME'}    = $server_fqdn;
$replacement{'IDP_PATH'}        = $server_path;
$replacement{'IDP_LADR'}        = $server_ladr;
$replacement{'IDP_JVMR'}        = $server_jvmr;
$replacement{'IDP_CONF'}        = $IDP_CONF_BASE;
$replacement{'IDP_LOG'}         = "/var/log/shibboleth-identity-provider";
$replacement{'IDP_CERTIFICATE'} = "";

print STDERR "processing idp conf...\n";
find(sub { copy_filter($IDP_CONF_DIST, $CONF_DIR); }, @{ [$IDP_CONF_DIST] });
print STDERR "done", "\n";
print STDERR "processing tomcat conf...\n";
find(sub { copy_filter_overwrite($TOMCAT_DIST, $TOMCAT_BASE); }, @{ [$TOMCAT_DIST] });

# create the directory to hold the credentials...
if (!-d $CRED_DIR) {
  mkpath($CRED_DIR);
}
my $output = "";

my $keygen = "$IDP_CONF_BASE/bin/keygen.sh";
my $keygen_args = "--hostname " . "$server_fqdn" . " --dnsAltName " . "DNS:$server_fqdn" . " --lifetime " . "10" . " --certfile " . "$CRT_NAME" . " --keyfile " . "$KEY_NAME";
# check whether the cert files exist
if (!-e $CRT_NAME && !-e $KEY_NAME ) {
  print STDERR "No credentials found! \n";
  print STDERR "Create credentials: ...\n";
  # qx/[x]/   same as backticks `[x]`
  $output = qx/id/; print STDERR "$output\n";
  $output = qx/$keygen $keygen_args/; print STDERR "$output\n";
  #system("$IDP_CONF_BASE/bin/keygen.sh", "--hostname", $server_fqdn, "--dnsAltName", "DNS:$server_fqdn", "--lifetime", "10", "--certfile", $CRT_NAME, "--keyfile", $KEY_NAME);
  print STDERR "Creating credentials with bin/keygen.sh --hostname $server_fqdn --dnsAltName DNS:$server_fqdn --lifetime 10 --certfile $CRT_NAME  --keyfile $KEY_NAME done.\n";
  #die "No credentials.", "\n";
} else {
  print STDERR "Credentials exist.", "\n";
}
if (!-e $SIG_CRT_NAME && !-e $SIG_KEY_NAME ) {
  print STDERR "No signing credentials found! \n";
  print STDERR "Create link to credentials: ...\n";
  # qx/[x]/   same as backticks `[x]`
  $output = qx/ln -s $CRT_NAME $SIG_CRT_NAME/; print STDERR "$output\n";
  $output = qx/ln -s $KEY_NAME $SIG_KEY_NAME/; print STDERR "$output\n";
  print STDERR "... done.\n";
} else {
  print STDERR "Signing credentials exist.", "\n";
}

if (!-e $ENC_CRT_NAME && !-e $ENC_KEY_NAME ) {
  print STDERR "No encryption credentials found! \n";
  print STDERR "Create link to credentials: ...\n";
  # qx/[x]/   same as backticks `[x]`
  $output = qx/ln -s $CRT_NAME $ENC_CRT_NAME/; print STDERR "$output\n";
  $output = qx/ln -s $KEY_NAME $ENC_KEY_NAME/; print STDERR "$output\n";
  print STDERR "... done.\n";
} else {
  print STDERR "Encryption credentials exist.", "\n";
}

my $fh;
open($fh, "<", $CRT_NAME);
while (<$fh>) {
  next if (/^-----(BEGIN|END)/);
  $replacement{'IDP_CERTIFICATE'} .= $_;
}
close($fh);

# generate metadata if not exist
# create the directory to hold the metadata ...
if (!-d $METADATA_DIR) {
  mkpath($METADATA_DIR);
}

my $mdgen = "$IDP_CONF_BASE/bin/ant.sh";
my $mdgen_args = "-Didp.host.name=" . "$server_fqdn" . " -Didp.target.dir=" . "$IDP_CONF_BASE" . " -Didp.keystore.password=ks-pw -Didp.sealer.password=pw " . " metadata-gen";
if (!-e $METADATA_FILE) {
  print STDERR "No metadata found! \n";
  print STDERR "Create metadata: ...\n";
  $output = qx/$mdgen $mdgen_args/; print STDERR "$output\n";
} else {
  print STDERR "Metadata exist.", "\n";
}

print STDERR "done.", "\n";

sub copy_filter {
  -d && return;    # ignore directories
  my ($src_base, $dst_base) = @_;
  my $src      = $File::Find::name;
  my $rel_path = File::Spec->abs2rel($src, $src_base);
  my $dst      = File::Spec->rel2abs($rel_path, $dst_base);
  #print STDERR $src, " -> ", $dst, "\n";
  my $dst_dir = (File::Spec->splitpath($dst))[1];

  if (!-d $dst_dir) {
    mkpath($dst_dir);
  }
  print STDERR "processing: ", $rel_path, " ... \n";
  if (-e $dst) {
    if( $dst =~ /\.properties$/ 
        || (index($dst, "access-control") != -1) 
        || (index($dst, "global") != -1) 
        || (index($dst, "services") != -1) 
        || (index($dst, "audit") != -1) 
        || (index($dst, "logback") != -1) 
        || (index($dst, "attribute-resolver") != -1) 
        || (index($dst, "attribute-filter") != -1) 
        || (index($dst, "relying-party") != -1) 
        || (index($dst, "saml-nameid") != -1) 
        || (index($dst, "metadata-providers") != -1)
        || (grep /$rel_path/, @puppet_confs) 
        || (grep /$rel_path/, @hu_confs) 
        || (index($rel_path, "attributes") == 0) ){
      print STDERR $dst, " already exists, no overwrite. \n";
      return; # -> new file ist in dist-conf
      # $dst .= ".dpkg-new";
      # print STDERR "Using ", $dst, " instead.\n";
    } else {
      print STDERR $dst, " already exists, ";
      if (compare($dst, $src) == 0) {
        print STDERR "no changes. \n";
        return;
      }
      my $dst_old = $dst.".dpkg-old";
      if(-e $dst_old) {
	  unlink($dst_old);
      }
      rename($dst, $dst_old);
      print STDERR "Saved old version in ", $dst_old, ".\n";    
    }
  }
  my ($in, $out);
  open($in,  "<", $src);
  open($out, ">", $dst);
  while (<$in>) {
    # remove the 8443 port from the hostname
    #s/\$:8443/\$/g;
    s{/idp/}{\$IDP_PATH\$/}g;
    while (/\$([A-Z\_]+)\$/) {
      my $tok = $1;
      if (!defined $replacement{$tok}) {
        die "replacement for ",$tok, " not defined\n";
      }
      #print STDERR $tok, " => ", $replacement{$tok}, "\n";
      s/\$\Q$tok\E\$/$replacement{$tok}/g;
    }
    print $out $_;
  }
  close $in;
  close $out;
  print STDERR "copied ", $dst, " \n";
} ## end sub copy_filter

sub copy_filter_overwrite {
  -d && return;    # ignore directories
  my ($src_base, $dst_base) = @_;
  my $src      = $File::Find::name;
  my $rel_path = File::Spec->abs2rel($src, $src_base);
  my $dst      = File::Spec->rel2abs($rel_path, $dst_base);
  print STDERR $src, " -> ", $dst, "\n";
  my $dst_dir = (File::Spec->splitpath($dst))[1];

  if (!-d $dst_dir) {
    mkpath($dst_dir);
  }
  if (-e $dst) {
    print STDERR $dst, " already exists. \n";
    if (compare($dst, $src) == 0) {
      print STDERR $dst, " not changed. \n";
      return;
    }
    my $dst_old = $dst.".dpkg-old";
    if(-e $dst_old) {
      unlink($dst_old);
    }
    rename($dst, $dst_old);
    print STDERR "Saved old version in ", $dst_old, ".\n";
  }
  my ($in, $out);
  open($in,  "<", $src);
  open($out, ">", $dst);
  while (<$in>) {
    # remove the 8443 port from the hostname
    #s/\$:8443/\$/g;
    s{/idp/}{\$IDP_PATH\$/}g;
    while (/\$([A-Z\_]+)\$/) {
      my $tok = $1;
      if (!defined $replacement{$tok}) {
        die "replacement for ",$tok, " not defined\n";
      }
      #print STDERR $tok, " => ", $replacement{$tok}, "\n";
      s/\$\Q$tok\E\$/$replacement{$tok}/g;
    }
    print $out $_;
  }
  close $in;
  close $out;
} ## end sub copy_filter
