#!/usr/local/bin/perl
#
# Automatically search and apply FIDO nodediffs to nodelist
#
# Requires
#     sumcrc
#     nl-diff
#

$opt_v = 1;

##############################################################################
#
#
# Perl functions to read FIDOGATE config file,
# included by <INCLUDE config.pl> when running subst.pl
#

my %CONFIG;

# specials for DosDrive and Zone
my %CONFIG_dosdrive;
my %CONFIG_zone;



my %CONFIG_default =
    (
##Automatically generated by subst.pl, DO NOT EDIT!!!##
	"seq_pack", "%V/seq/pack",
	"outpkt_news", "%S/outpkt/news",
	"bindir", "/usr/local/bin",
	"seq_pkt", "%V/seq/pkt",
	"outpkt_mail", "%S/outpkt/mail",
	"seq_news", "%V/seq/news",
	"lock_history", "history",
	"passwd", "%C/passwd",
	"seq_split", "%V/seq/split",
	"tic_history", "%V/tic_hist",
	"routing", "%C/routing",
	"seq_tick", "%V/seq/tick",
	"uplinks", "%C/uplinks",
	"newsetcdir", "/usr/local/news/etc",
	"toss_bad", "%S/toss/bad",
	"history", "%V/history",
	"spooldir", "/var/spool/fido/gate",
	"logdir", "/var/log/fido/gate",
	"configdir", "/usr/local/etc/fido/gate",
	"toss_toss", "%S/toss/toss",
	"pinbound", "/var/spool/fido/bt/pin",
	"toss_pack", "%S/toss/pack",
	"charsetmap", "%L/charset.bin",
	"ftnacl", "%C/ftnacl",
	"vardir", "/var/db/fidogate",
	"outpkt", "%S/outpkt",
	"inbound", "/var/spool/fido/bt/in",
	"outrfc_news", "%S/outrfc/news",
	"logfile", "%G/log",
	"newsbindir", "/usr/local/news/bin",
	"config_gate", "%C/fidogate.conf",
	"outrfc_mail", "%S/outrfc/mail",
	"tick_hold", "%B/tick",
	"seq_msgid", "%V/seq/msgid",
	"packing", "%C/packing",
	"dbc_history", "%V/bdc",
	"toss_route", "%S/toss/route",
	"lockdir", "/var/run/fidogate",
	"seq_mail", "%V/seq/mail",
	"newsvardir", "/usr/local/news/db",
	"seq_ff", "%V/seq/ff",
	"seq_toss", "%V/seq/toss",
	"inn_batchdir", "/usr/local/news/spool/outgoing",
	"hosts", "%C/hosts",
	"acl", "%C/acl",
	"netmaildir", "/var/spool/fido/bt/netmail",
	"uuinbound", "/var/spool/fido/bt/uuin",
	"config_main", "%C/fidogate.conf",
	"btbasedir", "/var/spool/fido/bt",
	"newslibdir", "/usr/local/news",
	"spyes", "%C/spyes",
	"hubroutedb", "%V/route",
	"areas", "%C/areas",
	"ftpinbound", "/var/spool/fido/bt/ftpin",
	"newsspooldir", "/usr/local/news/spool/articles",
	"lock_tic_hist", "tic_hist",
	"lock_dbc", "dbc",
	"aliases", "%C/aliases",
	"libexecdir", "/usr/local/libexec/fidogate",
     );
my %CONFIG_abbrev =
    (
##Automatically generated by subst.pl, DO NOT EDIT!!!##
	"P", "pinbound",
	"S", "spooldir",
	"K", "lockdir",
	"G", "logdir",
	"L", "libexecdir",
	"I", "inbound",
	"V", "vardir",
	"B", "btbasedir",
	"C", "configdir",
	"N", "bindir",
     );



sub CONFIG_read {
    my($file) = @_;
    my($key, $arg);
    local *C;

    $file = CONFIG_expand($file);

    open(C,"$file") || die "config.pl: can't open config file $file\n";
    while(<C>) {
	chop;
	next if( /^\s*\#/ );	# comments
	next if( /^\s*$/  );	# empty
	s/\s*$//;		# remove trailing white space
	s/^\s*//;		# remove leading white space
	($key,$arg) = split(' ', $_, 2);
	$key =~ tr/A-Z/a-z/;
	if($key eq "include") {
	    CONFIG_read($arg);
	    next;
	}
	if($key eq "dosdrive") {
	    my ($d, $path) = split(' ', $arg);
	    $CONFIG_dosdrive{lc($d)} = $path;
	    next;
	}
	if($key eq "zone") {
	    my ($z, $rest) = split(' ', $arg, 2);
	    $CONFIG_zone{$z} = $rest;
	    next;
	}
	$CONFIG{$key} = $arg if(!$CONFIG{$key});
    }
    close(C);
}


sub CONFIG_get1 {
    my($key) = @_;
    my($ukey);

    $ukey = $key;
    $ukey =~ tr/a-z/A-Z/;
    return $ENV{"FIDOGATE_$ukey"} if($ENV{"FIDOGATE_$ukey"});

    return $CONFIG{$key} if($CONFIG{$key});
    return $CONFIG_default{$key};
}


sub CONFIG_get {
    my($key) = @_;
    my($ret);
    my($exp);

    $key =~ tr/A-Z/a-z/;
    return CONFIG_expand( CONFIG_get1($key) );
}


sub CONFIG_expand {
    my($v) = @_;
    my($exp);

    if($v =~ /^%([A-Z])/) {
	$exp = CONFIG_get1($CONFIG_abbrev{$1});
	$v =~ s/^%./$exp/;
    }

    return $v;
}


sub CONFIG_debug {    
    my($key);

    for $key (keys %CONFIG) {
	print "$key = $CONFIG{$key} -> ", CONFIG_get($key), "\n";
    }
}

##############################################################################

# read config
my $CONFIG = "%C/fidogate.conf";
CONFIG_read($CONFIG);


# Location nl-diff and sumcrc programs
$BINDIR=CONFIG_get("BINDIR");

# Diff's location
$DIFFDIR =CONFIG_get("DIFFDIR");
# Nodelist name
$NAME    = "nodelist";
# Current nodelist location
$NLDIR   =CONFIG_get("NLDIR");
# Directory for put archiving nodelist
$NLFDIR  =CONFIG_get("NLFDIR");

$NLDIFF  = "$BINDIR/nl-diff";

@ARCX    = ("no",   "xo");
@LHAX    = ("no",   "xf");
@ZIPX    = ("no",   "-9");
@UNZIPX  = ("/usr/bin/unzip", "-joL");
@CPNL    = ("cp");

require "getopts.pl";
&Getopts('v');

$dir  = $DIFFDIR;
$name = $NAME;
chdir $NLDIR;

##### Find latest nodediff ###################################################
sub latest {
    local($lc,$uc) = @_;
    local($f,$nodelist,$t,$x,$mtime);

    $t        = -1;
    $nodelist = "";

    for $f (<$lc.??? $uc.???>) {
	if(-f $f) {
	    ($x,$x,$x,$x,$x,$x,$x,$x,$x, $mtime, $x,$x,$x) = stat($f);
	    if($mtime > $t) {
		$t        = $mtime;
		$nodelist = $f;
	    }
	}
    }

    return $nodelist;
}

##### Run program, exit if it fails #####
sub run {
    print "Running @_\n" if($opt_v);
    $st = (system @_) >> 8;
    if($st) {
	print STDERR "nl-autoupd: running @_ failed (exit code=$st)\n";
	exit $st;
    }
}

##### Index and save new nodelist #####
sub after {
    print "Archiving new nodelist\n" if($opt_v);
    push @ZIPX, $name;
    if($cddd < 10)
	{$cddd = "00$cddd"}
    if($cddd < 100)
	{$cddd = "0$cddd"}
    push @ZIPX, "$name.$cddd";
    &run(@ZIPX);
    print "Copying new nodelist to public area\n" if($opt_v);
    push @CPNL, "$name.zip";
    push @CPNL, $NLFDIR;
    &run(@CPNL);
    unlink "$name.zip";
}

# Search for current nodelist
$uc = $name;
$uc =~ tr/a-z/A-Z/;
$nodelist = &latest($name,$uc);
die "nl-autoupd: can't find current nodelist\n" if(!$nodelist);

print "Current nodelist: $nodelist\n" if($opt_v);


# Parse filename
if($nodelist =~ /^(.+)\.(\d\d\d)/ ) {
    $basename=$1;
    $day=$dayc=$2;
    print "Current nodelist: basename=$basename day=$day\n" if($opt_v);
}
else {
    die "nl-autoupd: strange nodelist filename $nodelist\n";
}

# Search for nodediffs
$diff = $basename;
$diff =~ tr/A-Z/a-z/;
$diff =~ s/list/diff/;
$uc   = $diff;
$uc   =~ tr/a-z/A-Z/;

while(1) {
    $found = 0;

    ##FIXME: wrap-around for next year, take into account leap years##
    my($sec,$min,$hour,$mday,$mon,$year,$wday,$yday)=localtime();
    $year += 1899;
    my $HighYear = $year % 4 ? 0 : 1;
    my $DLY=365 + $HighYear - $day;
    if ($DLY < 7) {
	$day=7 - $DLY;
    }
    else {
	$day+=7;
    }

    $dd  = sprintf "%02d", $day % 100;
    $ddd = sprintf "%03d", $day;

    # ARC
    if(!$found) {
	$file = "$dir/$diff.a$dd";
	if(-f $file) {
	    $found = 1;
	    @extract = @ARCX;
	}
    }
    if(!$found) {
	$file = "$dir/$uc.A$dd";
	if(-f $file) {
	    $found = 1;
	    @extract = @ARCX;
	}
    }
    # LHA
    if(!$found) {
	$file = "$dir/$diff.l$dd";
	if(-f $file) {
	    $found = 1;
	    @extract = @LHAX;
	}
    }
    if(!$found) {
	$file = "$dir/$uc.L$dd";
	if(-f $file) {
	    $found = 1;
	    @extract = @LHAX;
	}
    }
    # ZIP
    if(!$found) {
	$file = "$dir/$diff.z$dd";
	if(-f $file) {
	    $found = 1;
	    @extract = @UNZIPX;
	}
    }
    if(!$found) {
	$file = "$dir/$uc.Z$dd";
	if(-f $file) {
	    $found = 1;
	    @extract = @UNZIPX;
	}
    }

    # Nothing found
    if(!$found) {
	print "No more nodediff files found, exiting\n" if($opt_v);
	$cddd = $ddd - 7;
	&after if($dayc lt $cddd);
	exit 0;
    }
    print "Nodediff: $file, extract=@extract\n" if($opt_v);
	
    # Extract
    push @extract, $file;
    &run(@extract);

    # Find extracted file
    $found = 0;
    if(!$found) {
	$found = -f ($file = "$diff.$ddd");
    }
    if(!$found) {
	$found = -f ($file = "$uc.$ddd");
    }

    ## Clear all nodediff in nodelist folder and exit
    if(!$found) {
      for $f (<$diff.???>) {
	unlink($f) if(-f $f);
      }
      print "nl-autoupd: strange, can't find extracted file\n";
      exit 1;
    }
    print "Nodediff: $file\n" if($opt_v);

    # Run nl-diff
    @nldiff = ($NLDIFF, "-r", "-b$basename");
    push @nldiff, "-v" if($opt_v);
    push @nldiff, $file;
    &run(@nldiff);
    unlink $file;

    open(F,"$name.$ddd") || die "nl-check: can't open $name.$ddd\n";
    $_ = <F>;

    # CRC check
    if( /Day number \d+ : (\d+)/ ) {
	$old = $1;
	print "CRC from 1st line of $name.$ddd: $old\n" if($opt_v);
	print "CRC check for $name.$ddd ... " if($opt_v);
	$new = `$BINDIR/sumcrc -1z $name.$ddd`;
	chop $new;
	if( $old == $new ) {
	    print "OK\n" if($opt_v);
	    print "CRC computed for $f: $new\n" if($opt_v);
	}
	else {
	    print "ERROR\n\texpected: $old\n\tcomputed: $new\n";
	}
    }
    else {
	print "nl-check:$f: strange error - no CRC found in 1st line\n";
    }

    close(F);

}
    
