#!/usr/bin/perl
#
#  Post-Installation script to complete the installation process.
#
#  In case of e.g. Solaris pkg systems, this should be run as one of
#  post-installation steps at those packages!
#
#  - To move into place (conditionally) various prototype files
#  - To fill in configuration information into MAILSHARE/db/
#    files, if nothing in there to begin with...
#
# Long ago this was a SH script, hence its source file name...
#

# - MAILSHARE/forms/proto       --> MAILSHARE/forms (conditionally)
# - MAILSHARE/proto/*.{conf,cf} --> MAILSHARE/      (conditionally)
# - MAILSHARE/cf/proto/*.cf     --> MAILSHARE/cf/   (conditionally)
# - MAILVAR/db/proto/*          --> MAILVAR/db/     (conditionally)

my $DESTDIR = '';


# ------------------------------------------
# Common file comparison sub-program
# Args: - base directory 
#

sub compare_files {
    local ($DIR) = @_;
    local ($x);

    if ($DRYRUN) {
	printf "compare_files(%s)\n",$DIR;
	return;
    }

    chdir "${DIR}/proto" || die ("Can't chdir('${DIR}/proto/')");

    opendir DH, "." || die ("Opendir('.') failed!");
    local(@files) = readdir(DH);
    closedir(DH);

    foreach $x (@files) {
	next if ($x eq '.' || $x eq '..');
	next if ($x =~ m/^.*\.md5$/o);

	& compare_file($x);
    }
}


sub compare_file {
    local($x) = @_;
    local($s1,$s2,$y);
    local($wrote) = 0;


    if ($DRYRUN) {
	# printf "cmpfile: %s\n",$x;
	return;
    }

    return if ( -d $x );
    return if ($x =~ m/^\./); # Ignore fot-files.

    if ( ! -f $x.'.md5' ) {

	# No MD5 sum filed ???  Ok, lets calculate it..
	
	if ( ! -f '../'.$x ) {

	    # A new file, easy to know that we can recalc the MD5SUM.
	    mysystem('cp', '-p', $x, '../'.$x);
	    $wrote = 1;
	    mysystem("$MD5SUM -b ../$x > ${x}.md5");

	} else {

	#    # Don't overwrite!
	#   if (!mysystem("cmp -s $x ../$x")) {
	#	# MATCH of the files (sum either of them..)
	#	mysystem("$MD5SUM -b ../$x > ${x}.md5");
	#   } else {
	#	# Non-match of the files, sum the NEW file!
		mysystem("$MD5SUM -b $x > ${x}.md5");
	#   }
	}
    }

    if ( ! -f '../'.$x ) {
	# Ok, we have MD5 sum, but don't have the file
	# in its proper place!
	mysystem('cp', '-p', $x, '../'.$x);
	$wrote = 1;
    }

    if ( ! $wrote ) {
	# We didn't write, so there exists an old file..
	# Lets compare MD5 sums
	# Destination file

	local($md1,$md2,$l,$junk);
	$l=`$MD5SUM -b "../${x}"`; # BACKTICK DANGER!
	($md1,$junk) = split(' ',$l);

	# And its associated MD5 signature file

	$l=`cat "${x}.md5"`; # BACKTICK DANGER!
	($md2,$junk) = split(' ',$l);

	# printf(STDERR "File: $x MD1=$md1 MD2=$md2\n");
	if ( $md1 eq $md2 ) {
	    # They do match! We overwrite old file with new
	   unlink('../'.$x);
	   mysystem('cp', '-p', $x, '../'.$x);
	   mysystem("$MD5SUM -b ../${x} > ${x}.md5");
	   $wrote = 1;
	}
    }
    if ( ! $wrote ) {
	$y = $x;
	$y =~ s!/proto/!/!;
	printf(STDERR "File $DIR/$y has mismatching MD5 sum.\n");
	printf(STDERR "    won't overwrite it with $DIR/$x.\n");
    }
}


# ----------  POSTOFFICE DIRECTORY BUILDER ------------

sub dirprealloc {
    local($DIR,$DSIZE,$DFILES) = @_;
    local(@st);

    $DSIZE *= 1000;

    local($NAME) = "filenamefilenamefilenamefilenamefilenamefilenamefilenamefilenamefilename";

    local($NUM) = 2;
    open(OFP, "> $DIR/filename.0");
    close(OFP);

    # Just in case the names in directories are limited...
    $NAME = "filenam" unless(link("$DIR/filename.0","$DIR/$NAME.1"));

    @st = stat("${DIR}/."); # Can't fail ( -- eh ? )
    local($SIZE) = $st[7];

    while ($SIZE < $DSIZE && $NUM < $DFILES) {
	# Link as long as you can -- or size is desired..
	link("$DIR/filename.0","$DIR/$NAME.$NUM") || last;

	@st = stat("${DIR}/."); # Can't fail ( -- eh ? )
	$SIZE = $st[7];

	++$NUM;
    }


    # Now clean up after allocations..
    
    opendir DH, "$DIR/." || die ("Opendir('$DIR/.') failed!");
    local(@files) = readdir(DH);
    closedir(DH);
    local($x);
    foreach $x (@files) {
	next if ($x eq '.' || $x eq '..');
	unlink("$DIR/$x");
    }
}

sub mkpostofficedir {
    local($DIR,$MOD,$HASH,$DSIZE,$DFILES) = @_;

    $MOD = 02000 + oct($MOD);

    chdir($POSTOFFICE);
    if ( ! -d "${DIR}/." ) {

	printf(STDERR "Constructing dir: %s hash=%s\n",$DIR,$HASH);
	return if ($DRYRUN);


	mkdir $DIR, $MOD || die "mkdir ${DIR} failed!";
	chmod $MOD, $DIR || die "chmod ${DIR} failed!";
	& dirprealloc($DIR, $DSIZE, $DFILES);
	local($xx,$yy);
	if ($HASH eq 'HASH') {
	    printf(STDERR " HASH: ");
	    foreach $xx ('A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z') {
		printf(STDERR " %s", $xx);
		mkdir "$DIR/$xx", $MOD || die "mkdir ${DIR}/$xx failed!";
		chmod $MOD, "$DIR/$xx" || die "chmod ${DIR}/$xx failed!";
		& dirprealloc("$DIR/$xx", $DSIZE, $DFILES);
	    }
	    printf(STDERR "\n");
	}
	if ($HASH eq 'HASH2') {
	    printf(STDERR " HASH2: ");
	    foreach $xx ('A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z') {
		printf(STDERR " %s", $xx);
		mkdir "$DIR/$xx", $MOD || die "mkdir ${DIR}/$xx failed!";
		chmod $MOD, "$DIR/$xx" || die "chmod ${DIR}/$xx failed!";
		& dirprealloc("$DIR/$xx", $DSIZE, $DFILES);

	    }
	    printf(STDERR "\n");
	    foreach $xx ('A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z') {

		printf(STDERR " HASH2/%s: ",$xx);
		foreach $yy ('A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z') {
		    printf(STDERR " %s", $yy);
		    mkdir "$DIR/$xx/$yy", $MOD || die "mkdir ${DIR}/$xx/$yy failed!";
		    chmod $MOD, "$DIR/$xx/$yy" || die "chmod ${DIR}/$xx/$yy failed!";
		    & dirprealloc("$DIR/$xx/$yy", $DSIZE, $DFILES);
		}
		printf(STDERR "\n");
	    }
	}
    }
}


sub  mysystem {
    my (@cmd) = @_;
    if ($DRYRUN) {
	printf "cmd: %s\n",join(' ',@cmd);
	return 0;
    } else {
	return system(@cmd);
    }
}

# -----------------  pick_zenv() ----------------------

sub pick_zenv {

    my($ZCONFIG) = @_;

    open(ZZ, "< ".$ZCONFIG) || die "No ZCONFIG file at '$ZCONFIG'";

    while (<ZZ>) {
	chomp;
	local($n,$l) = split(/=/,$_,2);
	$ZENV{$n} = $l  if ($n =~ m/^[A-Z0-9a-z]/);
    }
    close(ZZ);
}

# ------------------  MAIN PROGRAM --------------------

select STDOUT; $| = 1;
select STDERR; $| = 1;

%ZENV  = ();

$ZCONFIG = '/opt/mail/zmailer.conf';

$STYLE = 'MD5';

$DRYRUN = 0;

while (defined $ARGV[0]) {

	if ($ARGV[0] eq '-MD5') {
	    $STYLE = 'MD5';
	    shift @ARGV;
	    next;
	}

	if ($ARGV[0] eq '-OLDSTYLE') {
	    $STYLE = 'OLD';
	    shift @ARGV;
	    next;
	}

	if ($ARGV[0] eq '--zconfig') {
	    shift @ARGV;
	    $ZCONFIG = $ARGV[0];
	    shift @ARGV;
	    next;
	}
	if ($ARGV[0] eq '--dryrun'  or $ARGV[0] eq '-n') {
	    shift @ARGV;
	    $DRYRUN = 1;
	    next;
	}

	if ($ARGV[0] eq '--destdir') {
	    shift @ARGV;
	    $DESTDIR = $ARGV[0];
	    shift @ARGV;
	    next;
	}

	printf(STDERR "unknown parameter: %s\n", $ARGV[0]);
	printf(STDERR "post-install [-n|--dryrun] [--destdir /virtual/root] [--zconfig /opt/mail/zmailer.conf] [-MD5[|-OLDSTYLE]]\n");
	exit(64);
}


$MKDIR = "mkdir -p";


#if ($STYLE eq '') {
#    printf(STDERR "post-install [-MD5[|-OLDSTYLE]]\n");
#    exit 1;
#}

if ($STYLE eq '') {
    printf(STDERR "post-install [-n|--dryrun] [--destdir /virtual/root] [--zconfig /opt/mail/zmailer.conf] [-MD5[|-OLDSTYLE]]\n");
    exit 1;
}


& pick_zenv( $DESTDIR.$ZCONFIG );

$POSTOFFICE = $DESTDIR.$ZENV{'POSTOFFICE'};
$MAILSHARE  = $DESTDIR.$ZENV{'MAILSHARE'};
$MAILVAR    = $DESTDIR.$ZENV{'MAILVAR'};
$MAILBIN    = $DESTDIR.$ZENV{'MAILBIN'};

die "Failed to pick POSTOFFICE from ${DESTDIR}${ZCONFIG} !?"
    if (!defined $POSTOFFICE || $POSTOFFICE eq '');

$MD5SUM = "/usr/bin/md5sum"; # Either system md5sum,  or our own...
$MD5SUM = $DESTDIR.$MD5SUM if ($DESTDIR.$MD5SUM eq $MAILBIN.'/md5sum');

# trap 'echo "Directory access failure; DIR=$DIR/proto"' 0

# exit 99 # Sorry, not yet!

if ( ! -d "$MAILSHARE/bak" ) {
    mysystem("$MKDIR $MAILSHARE/bak");
}

foreach $x ("$MAILSHARE", "$MAILSHARE/forms", "$MAILSHARE/cf", "$MAILVAR/db") {
    & compare_files($x);
}

# ----------------------------
#    Database prototypes ???

foreach $x (qw("$MAILSHARE/proto/router.cf $MAILSHARE/proto/scheduler.conf
	 $MAILSHARE/proto/smtpserver.conf  $MAILSHARE/proto/sm.conf
	 $MAILSHARE/proto/smtp-tls.conf
	 $MAILVAR/db/proto/dbases.conf
	 $MAILVAR/db/proto/aliases         $MAILVAR/db/proto/localnames
	 $MAILVAR/db/proto/fqdnaliases     $MAILVAR/db/proto/fqdnroutes
	 $MAILVAR/db/proto/routes
	 $MAILVAR/db/proto/smtp-policy.src $MAILVAR/db/proto/smtp-policy.mx
	 $MAILVAR/db/proto/smtp-policy.relay")) {

    $y = $x;
    $y =~ s!/proto/!/!;
    if ( -f $x ) {
	printf(STDERR "**** Verify  $y  file!\n")
	    unless (system("cmp -s $x $y"));
    }
}

if ($DESTDIR eq '') {
    mysystem("$MAILBIN/zmailer newdb");
    mysystem("$MAILBIN/policy-builder.sh -n");
}

printf(STDERR "
***
*** Will create directory hierarchies into  %s
*** This is potentially VERY LENGTHY PROCESS
*** please be patient...
***
", $POSTOFFICE);

if (! -d "$POSTOFFICE/.") {
    mymysystem("$MKDIR -p $POSTOFFICE");
    
}
mysystem("chmod 2755 $POSTOFFICE/.");
mysystem("chmod g+s $POSTOFFICE/.");

#  subdir protchmod "no"/"HASH"/"HASH2" dirpreallocsize
& mkpostofficedir('deferred',     '750', 'no',    6, 9999);
& mkpostofficedir('TLSsrvrcache', '700', 'HASH',  6, 9999);
& mkpostofficedir('TLSclntcache', '700', 'HASH',  6, 9999);
& mkpostofficedir('freezer',      '750', 'no',    6, 9999);
& mkpostofficedir('postman',      '750', 'no',    6, 9999);
& mkpostofficedir('public',      '1777', 'no',    6, 9999);
& mkpostofficedir('queue',        '750', 'HASH2', 6, 9999);
& mkpostofficedir('transport',    '750', 'HASH2', 6, 9999);
& mkpostofficedir('router',      '1777', 'HASH',  6, 9999);

printf(STDERR "
***
*** Remember to verify input databases, and then execute commands:
***    $MAILBIN/zmailer newdb
***    $MAILBIN/policy-builder.sh (-n)
***
");

#trap "" 0
exit 0;

1;
