#
# radbserver-misc.pl
#
# misc utilities for the radbserver.
#

require	"defines.pl";
require "misc.pl";

###################################################################################
#
# SetSources: change list of sources and associated db files
#     $SS is list uses wants to use.  check it and do it
#     *sources,*dbfiles are lists to receive new settings
#     $ChkIndexes  means insist the indexes exist
#
#     Returns non-null error message if error.

sub SetSources {
	local($SS,*sources,*dbfiles,$ChkIndexes) = @_;
	local($s,$DBIfname,$r) = ('','','');
	$ErrorMsg = '';
	$SS =~ tr/a-z/A-Z/;
    foreach $s (split( /,/, $SS )) {		# lets check these first...
		if ( ! -f $DBFILE{$s} ) {
			$r = "No db file found for source '$s'.";
			print STDERR "\n($$) SetSources: $r\n" if $opt_d;
			return($r);
		}
		$DBIfname = $DBIPath . "/" . $s;
		if ( $ChkIndexes && ! -f $DBIfname ) {
			$r = "No index file '$DBIfname' found for source '$s'.";
			print STDERR "\n($$) SetSources: $r\n" if $opt_d;
			return($r);
		}
	}
	@sources = split( /,/, $SS );
	@dbfiles = ();
    foreach $s (@sources) {
        push(@dbfiles,$DBFILE{$s});
    }
	print STDERR "\nSet sources: @sources.\n    dbfiles: @dbfiles.\n" if $opt_d;
	return($r);
}

###############################################################################################
#
# Tie[New]Source    tie/untie assoc array for source db to a file
# UnTieSource
#
# How is RW (or R)
#
# these are separate to speed up search (fewer file tests...)
#
# NOTE: DBIF is passed as a global var: if I pass it as par in eval,
#       it doesn't seem to work.
#
# Returns non-null msg if an error.

sub TieSource {
	local($source,$how) = @_;
	local($rm) = '';
	local($DBIF) = $DBIPath . "/" . $source;

	if ( ! -f $DBIF ) { 
   		$rm = "No index file $DBIF: can't use it!";
		print STDERR "---> TieSource: failed for $source (index file $DBIF)\n" if $opt_d;
   	}
	else {
		if ( $how eq "RW" ) {
			eval 'TieGDBM(*' . $source . ')';
		}
		else {
			eval 'TieGDBMR(*' . $source . ')';
		}
	}
	return $rm;
}

sub TieNewSource {
	local($source) = @_;
	local($rm) = '';
	local($DBIF) = $DBIPath . "/" . $source;
	if ( -f $DBIF ) {
		print STDERR "  (Removing $source first...)\n" if $opt_d;
  	    `rm -f $DBIF`;
	}
	eval 'TieNewGDBM(*' . $source . ')';

	return $rm;
}

sub UnTieSource {
	local($source) = @_;
	eval 'MyUnTie(*' , $source .')';
}

sub TieGDBM {
	local(*AA) = @_;
	tie( %AA,  GDBM_File, $DBIF, GDBM_WRITER, 00664 );
}
sub TieGDBMR {
	local(*AA) = @_;
	tie( %AA,  GDBM_File, $DBIF, GDBM_READER, 00664 );
}
sub TieNewGDBM {
	local(*AA) = @_;
	tie( %AA,  GDBM_File, $DBIF, GDBM_WRCREAT, 00664 );
}
sub MyUnTie {
	local(*AA) = @_;
	untie(%AA);
}

################################################################################
#
# Parse		parse command string to strip off -<opt>, return the rest.
#
# currently just strips -s (set source), and processes it.
# also strips out non-accepted characters.
#
################################################################################

sub Parse {
    local(*string,*sources,*dbfiles) = @_;
	local($rval) = '';

    print STDERR "($$) Entering parse...string is '$string'\n" if $opt_d;
    $string =~ s/^\s+//;
    while ($string =~ /^-/) {
        if ( $string =~ s/^-([sS])\s*(\S+)\s*//) {
            local($msg,$src) = ('',$2);
            if ( ($rval = &SetSources($2,*sources,*dbfiles,$CheckIndexesExist)) ) {
				print STDERR "($$) error from SetSources($2,...): $rval\n" if $opt_d;
				return( $rval );
			}
            next;
        }
        last;   # if we didn't get the above, assume we are into the key for query
    }

	&CleanInput( *string );

    print STDERR "($$) Leaving parse, string is '$string'.\n" if $opt_d;
    return( $rval );
}

sub CleanInput {   # get rid of junk >>>>====>>>> Change if syntax adds chars <<<<=====<<<<====
	local(*string) = @_;
    $string =~ tr/\ a-zA-Z0-9\,=_\-\/!\.\:\(\)//cd;
	return( $string );
}


sub DebugShow {
    print STDERR "\nDebugShow:\n";
    print STDERR "   sources: @sources\n";
    print STDERR "   dbfiles: @dbfiles\n";
}

##################################################################
#
# ExpandASMacroASLines
#
# Takes one or more as-macro al (as-list) lines
# and expands to all AS values, ie, expanding any
# embedded as-macros. 
#
# Assumes macros all start AS-*
#
# $rval is 0 if all ok, else >= 1 cnt of number of failures...
#
# Level:	0  don't expand
#			1  recursively expand ASMacroNames to all AS numbers.
#
#
# Note: calling &AskServerOnce from here seems to 
#     direct the answers to the radbserver daemon window, rather than
#     to the asker.  But calling it from client.pl -O<cmd> seems to work ok.
#     I am confused about something...
#     THUS...the terrible hack of repeating the codes in ExpandASMacro...

sub ExpandASMacroASLines {
	local($string,$level,$sources) = @_;
	local($rstring,$rmsg,$rval) = ('','',0);

	print STDERR "enter ExpandASMacroASLines: level $level, sources $sources,\n string: '$string'\n";

	if ( !$level ) {
		return ($string,$rval);
	}

	while ( $string =~ s/^(\S*)\s*// ) {
		if ( substr($1,0,3) eq "AS-" ) {
			local(@sss) = split(',',$sources);
			local($rc,$rm,$rv) = eval `client.pl -Ss$sources:i$1,$level:`;
			$rv =~ tr/|/\n/;
			$rstring .= "$rv";	# this assumes client.pl will return a |, ie \n, at end
			$rval += $rc;
			$rmsg .= " $rm ";
		}
		else {
			$rstring .= "$1 ";
		}
		last if length $string <= 1;
	}

	return( $rstring, $rval, $rmsg );
}


###################################################################
#
# GetSpecialCommRoutes
#
# cmdstring should be:  <comm>[,<asnum>[,<file>]]
#
# if <file> is present, use that (should be full path/fname).
# if <file> not given, look for a file of name <comm>
# in the default directory for special communities.
# Open the file, error if can't.
# Should have a format like this:
# <asnum1> r1 r2 ...
# <asnum1> r3 r4 ...
#  ...
# <asnumN> r5 56 ...
# ...
#
# If no <asnum>, get all the routes.
# If <asnum>, find lines that start with it and get only
# routes on those lines.  (in effect community=<comm> Intersect Origin=<asnum>)
#
# This prints out the start-of-data lines and data-lines.
# Caller prints end-of-data or error line, and syslog entries.

sub GetSpecialCommRoutes {
	local($cmdstring) = @_;
	local($comm,$asnum,$file,$routes) = ('','','','');

	$_ = $cmdstring;
	s/^(\w*)//;
	$comm = $1;
	if ( s/,([AS\d]*)// ) {
		$asnum = $1;
		if ( s/,(.*)// ) {
			$file = $1;
		}
	}

   	$file = "$SpecialCommRoutesPath/$comm" if !$file;
	if ( !open(SCFILE,$file) ) {
		return "Can't open SpecialCommRoutes file '$file' ($!)\n";
	}

   	while ( <SCFILE> ) {
   		/^([AS\d]*)\ (.*)$/;
   		$routes .= "$2\n" if ( !$asnum || $asnum eq $1 ); 
   	}
   	close(SCFILE);

	$l = length($routes);
	printf "%c%d\n", $DataLenCode, $l;
	do {
   		$routes =~ s/(.*)\n*//;
        printf "%c$1\n", $DataReturnCode;
    } while ( $routes );

	return '';
}



###################################################################
#
# StringFromStdOutOfCommand
#

sub StringFromStdOutOfCommand {
	local($cmd) = @_;
	local($rs) = '';

	open( SFSOFILE, "$cmd |" );
	while ( <SFSOFILE> ) {
		$rs .= "$_";
	}
	close(SFSOFILE);

	$rs;
}


#	It allows you to write to various logfiles defined in general config
#	

sub syslogRLR {
	local($log, $string) = @_;

# Get the time

        ($s, $m, $h, $md, $mo, $y, $wd, $yd, $is) = localtime(time);
        $thismonth = (Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec)[$mo];

        $s = "0".$s unless $s > 9;
        $m = "0".$m unless $m > 9;
        $h = "0".$h unless $h > 9;

        $date = sprintf("%3s %2d %2s:%2s:%2s", $thismonth, $md, $h, $m , $s);

# Now we got a date let's open the logfile
# If we cant try to open the errorlog or failing that write to the console

	if (!(open (LOG, ">>$LOGFILE{$log}")))  {
		if (!(open (LOG, ">>$LOGFILE{ERRLOG}"))) {
			if (!(open (LOG, "+>/dev/console"))) {
				return;
			}
		}
	}
#
# So we assume we can write the string
# Attempt to lock the log as well
#
# rlr changed 4 nov 94 from this
#         select(LOG); $| = 1; select(STDOUT);
# to the following.  it seemed to not leave things at right fhandle sometimes.
# I don't really get why the do this at all, since they don't use the default
# filehandle here anyway!

	local($oldfn) = select(LOG); $| = 1; select($oldfn);
	&lock(LOG);
	print LOG "$date $string\n";
	&unlock(LOG);
	close(LOG);
	return;
}

################################ I guess we need this...

1;
