#=================================================================
# imp_servdb.pm
# Mark Pruett - 06/12/2000
#
# These routines handle actual database access. This module can
# be replaced by other modules that access other database
# back-ends. Replacement modules should retain the same functions
# and parameter semantics.
#
# This module supports the RDB database format, which should be
# sufficient for all intranet and most moderate-traffic intranet
# sites.
#=================================================================
use db_RDB;

$db_dir = "";
$db_locations_file = "";
$db_aliases_file = "";
$db_geography_file = "";
$db_geopref_file = "";

$db_backup_table = 0;

$rdbdir = "/usr/local/bin";

#-----------------------------------------------------------------
# Initialize some module-global variables based on configuration
# values or hard-coded defaults.
#-----------------------------------------------------------------
sub impdb_init {
    my (%DBCONF) = @_;

    if (defined ($DBCONF{"DB_DIR"})) {
	$db_dir = $DBCONF{"DB_DIR"};
    }
    else {
	$db_dir = "/tmp"; # MLP: probably not a good default...
    }

    if (defined ($DBCONF{"DB_DEF_LOC_FILE"})) {
	$db_locations_file = $DBCONF{"DB_DEF_LOC_FILE"};
    }
    else {
	$db_locations_file = "locations.rdb";
    }
    $db_aliases_file = "aliases.rdb";
    $db_geography_file = "geography.rdb";
    $db_geopref_file = "geo_preference.rdb";

    if (defined ($DBCONF{"RDBDIR"})) {
	$rdbdir = $DBCONF{"RDBDIR"};
    }
}

#-----------------------------------------------------------------
# Given a table "token" return the actual table name. 
#-----------------------------------------------------------------
sub impdb_get_table_name {
    my ($tbl_token) = @_;

    my $name;

    if ($tbl_token eq "geography") {
	$name = $db_geography_file;
    }
    elsif ($tbl_token eq "geo_preference") {
	$name = $db_geopref_file;
    }
    elsif ($tbl_token eq "aliases") {
	$name = $db_aliases_file;
    }
    elsif ($tbl_token eq "locations") {
	$name = $db_locations_file;
    }

    return $name;
}

#-----------------------------------------------------------------
# Read the entire aliases table and return in recs array.
#-----------------------------------------------------------------
sub impdb_get_aliases_records {
    my @recs;
    my $row = 0;
    my $filename = "$db_dir/$db_aliases_file";

    # Check for table write lock, wait a few seconds if necessary.
    if (! db_wait_for_unlock ($filename, 10)) {
	return undef;
    }

    my %al_tbl = db_get_table ("cat $filename");
    for ($i=0; $i < $al_tbl{NUM_RECORDS}; $i++) {
	$recs[$i]->{"alias"} =
	    db_get_field ("alias", $i, %al_tbl);
	$recs[$i]->{"canonical_name"} =
	    db_get_field ("canonical_name", $i, %al_tbl);
    }

    return @recs;
}

#-----------------------------------------------------------------
# Read the entire geography table and return in recs array.
#-----------------------------------------------------------------
sub impdb_get_geography_records {
    my @recs;
    my $row = 0;
    my $filename = "$db_dir/$db_geography_file";

    # Check for table write lock, wait a few seconds if necessary.
    if (! db_wait_for_unlock ($filename, 10)) {
	return undef;
    }

    my %geo_tbl = db_get_table ("cat $filename");
    for ($i=0; $i < $geo_tbl{NUM_RECORDS}; $i++) {
	$recs[$i]->{"geo_code"} =
	    db_get_field ("geo_code", $i, %geo_tbl);
	$recs[$i]->{"geo_description"} =
	    db_get_field ("geo_description", $i, %geo_tbl);
    }

    return @recs;
}

#-----------------------------------------------------------------
# Read the geo_preferences table and return in recs array.
#-----------------------------------------------------------------
sub impdb_get_geopref_records {
    my @recs;
    my $filename = "$db_dir/$db_geopref_file";

    # Check for table write lock, wait a few seconds if necessary.
    if (! db_wait_for_unlock ($filename, 10)) {
	return undef;
    }

    my %geopref_tbl = db_get_table ("cat $filename");
    for ($i=0; $i < $geopref_tbl{NUM_RECORDS}; $i++) {
	$recs[$i]->{"gpid"} =
	    db_get_field ("gpid", $i, %geopref_tbl);
	$recs[$i]->{"geo_code"} =
	    db_get_field ("geo_code", $i, %geopref_tbl);
	$recs[$i]->{"table_name"} =
	    db_get_field ("table_name", $i, %geopref_tbl);
    }

    return @recs;
}

#-----------------------------------------------------------------
# Return all records that match canonical name in all geo_preference
# tables. Return in rec array.
#
# Note: this does not return the records in any particular prefer-
# ence order.
#-----------------------------------------------------------------
sub impdb_get_printer_records {
    my ($canonical_name, $language) = @_;

    my @recs;
    my $row = 0;
    @geopref = impdb_get_geopref_records ();

    for ($j=0; $j <= $#geopref; $j++) {
	$table_name = $geopref[$j]->{"table_name"};
	$geo_code = $geopref[$j]->{"geo_code"};

	if (! db_wait_for_unlock ("$db_dir/$table_name", 10)) {
	    next;
	}

	my %locations_tbl = db_get_table ("cat $db_dir/$table_name"); 
	for ($i=0; $i < $locations_tbl{NUM_RECORDS}; $i++) {
	    $val = db_get_field ("printer_id", $i, %locations_tbl);

	    if ($val eq $canonical_name) {
		# If no language was specified, or the language matches the db.
		my $db_language = db_get_field ("language", $i, %locations_tbl);
		if ((! defined $language) || ($language eq $db_language)) { 
		    $recs[$row]->{"geo_code"} = $geo_code;
		    $recs[$row]->{"package_name"} = 
			db_get_field ("package_name", $i, %locations_tbl);
		    $recs[$row]->{"location_url"} = 
			db_get_field ("location_url", $i, %locations_tbl);
		    $recs[$row]->{"language"} = 
			db_get_field ("language", $i, %locations_tbl);
		    $recs[$row]->{"gpg_version"} = 
			db_get_field ("gpg_version", $i, %locations_tbl);
		    $recs[$row]->{"gpg_sig"} = 
			db_get_field ("gpg_sig", $i, %locations_tbl);
		    $recs[$row]->{"gpg_crc"} = 
			db_get_field ("gpg_crc", $i, %locations_tbl);
		}
		$row++;
	    }
	}
    }

    return @recs;
}

#-----------------------------------------------------------------
# Given a table name, an id (either an exact name or a partial
# name), a boolean indicating theat the match should be partial
# or exact, and the language string (en for english, etc.), return
# an array of records that match the ID.
#-----------------------------------------------------------------
sub impdb_query_printer_records {
    my ($table_name, $partial_id, $exact, $language) = @_;

    my @recs;
    my $row = 0;
    my $i;
    my $testpartial = $partial_id;

    if (! db_wait_for_unlock ("$db_dir/$table_name", 10)) {
	return undef;
    }

    # We need to do some transformations on the partial_id
    # if we're doing a partial match. These transforms 
    # will convert normal file-globbing-style wildcards
    # (* and ?) into equivalent perl regexps.
    if ($exact != 1) {
	# Special case: if $testpartial is blank, then
	# convert it to "*" to match everything.
	$testpartial = "*" if ($testpartial eq "");

	# Convert "*" to ".*"
	$testpartial =~ s/\*/\.\*/g;
	
	# convert (for example) "???" to ".{3}"
	while ($testpartial =~ /(\?+)/) {
	    $count = length($1);
	    $testpartial =~ s/(\?+)/\.{$count}/;
	}
    }	

    my %locations_tbl = db_get_table ("cat $db_dir/$table_name | $rdbdir/sorttbl printer_id"); 

    for ($i=0; $i < $locations_tbl{NUM_RECORDS}; $i++) {
	$val = db_get_field ("printer_id", $i, %locations_tbl);

	$matched = 0;
	if ($exact == 1) {
	    if ($val eq $partial_id) {
		$matched = 1;
	    }
	}
	else {
	    my $testval = $val;

	    # Squeeze out white space:
	    $testval =~ s/ //g;
	    $testpartial =~ s/ //g;

	    if ($testval =~ /^$testpartial$/i ) {
		$matched = 1;
	    }

	}

	if ($matched == 1) {
	    my $db_language = db_get_field ("language", $i, %locations_tbl);
	    # If no language was specified, or the language matches the db.
	    if ((! defined $language) || ($language eq $db_language)) { 

		$recs[$row]->{"printer_id"} = $val;
		$recs[$row]->{"table_name"} = $table_name;
		$recs[$row]->{"package_name"} = 
		    db_get_field ("package_name", $i, %locations_tbl);
		$recs[$row]->{"location_url"} = 
		    db_get_field ("location_url", $i, %locations_tbl);
		$recs[$row]->{"language"} = 
		    db_get_field ("language", $i, %locations_tbl);
		$recs[$row]->{"gpg_version"} = 
		    db_get_field ("gpg_version", $i, %locations_tbl);
		$recs[$row]->{"gpg_sig"} = 
		    db_get_field ("gpg_sig", $i, %locations_tbl);
		$recs[$row]->{"gpg_crc"} = 
		    db_get_field ("gpg_crc", $i, %locations_tbl);
		$row++;
	    }
	}
    }

    return @recs;
}



#-----------------------------------------------------------------
# Given an alias name, find the matching canonical printer name.
# The latter name is the "master key" for subsequent table look-ups.
#-----------------------------------------------------------------
sub impdb_get_alias_record {
    my ($alias) = @_;
    my $filename = "$db_dir/$db_aliases_file";

    # Check for table write lock, wait a few seconds if necessary.
    if (! db_wait_for_unlock ($filename, 10)) {
	return undef;
    }

    my $name = "";
    my %alias_tbl = db_get_table ("cat $filename");

    # search for alias match to $alias

    my $row = db_find_record ("alias", $alias, %alias_tbl);

    if ($row >= 0) {
	$name = db_get_field ("canonical_name", $row, %alias_tbl);
    }

    return $name;
}

#-----------------------------------------------------------------
# Add a record to the location table.
#-----------------------------------------------------------------
sub impdb_add_location_record {
    my ($prn_id, $fldstr, $valstr, $table_name) = @_;

    my $rc = 1;

    # Read the table
    my %rdb = db_get_table ("cat $db_dir/$table_name");

    # If there are no records, then this is a new table, so
    # we'll build the necessary data structures.

    if ($rdb{"NUM_RECORDS"} == 0) {
	%rdb = db_new_table ("# New Location Table", 
			     $fldstr, "20\t25\t100");
    }


    # Check if record already exists with this printer_id
    my $row = db_find_record ("printer_id", $prn_id, %rdb);

    # If no existing record, then add.
    if ($row == -1) {
	%rdb = db_add_record ("", $fldstr, $valstr, %rdb);
	# Write table
	$rc = write_rdb_table ("$db_dir/$table_name", $db_backup_table, %rdb);
    }

    return $rc;

}

#-----------------------------------------------------------------
# Add a record to the aliases table.
#-----------------------------------------------------------------
sub impdb_add_aliases_record {
    my ($alias_fname, $alias_fvalue, $fldstr, $valstr, $table_name) = @_;

    my $rc = 1;

    # Read the table
    my %rdb = db_get_table ("cat $db_dir/$table_name");

    # Check if record already exists with this printer_id
    my $row = db_find_record ($alias_fname, $alias_fvalue, %rdb);

    # If no existing record, then add.
    if ($row == -1) {
	%rdb = db_add_record ("", $fldstr, $valstr, %rdb);
	# Write table
	$rc = write_rdb_table ("$db_dir/$table_name", $db_backup_table, %rdb);
    }

    return $rc;

}

#-----------------------------------------------------------------
# Add a record to the geopref table.
#-----------------------------------------------------------------
sub impdb_add_geopref_record {
    my ($geopref_fname, $geopref_fvalue, $fldstr, $valstr, $table_name) = @_;

    my $rc = 1;

    # Read the table
    my %rdb = db_get_table ("cat $db_dir/$table_name");

    # Check if record already exists with this unique id
    my $row = db_find_record ($geopref_fname, $geopref_fvalue, %rdb);

    # If no existing record, then add.
    if ($row == -1) {
	%rdb = db_add_record ("$geopref_fname", $fldstr, $valstr, %rdb);
	# Write table
	$rc = write_rdb_table ("$db_dir/$table_name", $db_backup_table, %rdb);
    }

    return $rc;

}

#-----------------------------------------------------------------
# Add a record to the geography table.
#-----------------------------------------------------------------
sub impdb_add_geography_record {
    my ($autoinc_fname, $fldstr, $valstr, $table_name) = @_;

    my $rc = 1;

    # Read the table
    my %rdb = db_get_table ("cat $db_dir/$table_name");

    # Check if record already exists with this geo_description
    my $row = db_find_record ("geo_description", $valstr, %rdb);

    # If no existing record, then add.
    if ($row == -1) {
	%rdb = db_add_record ($autoinc_fname, $fldstr, $valstr, %rdb);
	# Write table
	$rc = write_rdb_table ("$db_dir/$table_name", $db_backup_table, %rdb);
    }

    return $rc;

}

#-----------------------------------------------------------------
# Edit an existing geography record.
#-----------------------------------------------------------------
sub impdb_edit_geography_record {
    my ($target_fname, $target_fvalue, $fldstr, $valstr, $table_name) = @_;

    my $rc = 1;

    # Read the table
    my %rdb = db_get_table ("cat $db_dir/$table_name");

    # Find the record that has this geo_code.
    my $row = db_find_record ($target_fname, $target_fvalue, %rdb);

    # If record exists, edit.
    if ($row >= 0) {
	%rdb = db_edit_record ($row, $fldstr, $valstr, %rdb);
	# Write table
	$rc = write_rdb_table ("$db_dir/$table_name", $db_backup_table, %rdb);
    }

    return $rc;

}

#-----------------------------------------------------------------
# Edit an existing geopref record.
#-----------------------------------------------------------------
sub impdb_edit_geopref_record {
    my ($target_fname, $target_fvalue, $fldstr, $valstr, $table_name) = @_;

    my $rc = 1;

    # Read the table
    my %rdb = db_get_table ("cat $db_dir/$table_name");

    # Check if record already exists with this printer_id
    my $row = db_find_record ($target_fname, $target_fvalue, %rdb);

    # If record exists, edit.
    if ($row >= 0) {
	%rdb = db_edit_record ($row, $fldstr, $valstr, %rdb);
	# Write table
	$rc = write_rdb_table ("$db_dir/$table_name", $db_backup_table, %rdb);
    }

    return $rc;

}

#-----------------------------------------------------------------
# Edit an existing aliases record.
#-----------------------------------------------------------------
sub impdb_edit_aliases_record {
    my ($target_fname, $target_fvalue, $fldstr, $valstr, $table_name) = @_;

    my $rc = 1;

    # Read the table
    my %rdb = db_get_table ("cat $db_dir/$table_name");

    # Check if record already exists with this printer_id
    my $row = db_find_record ($target_fname, $target_fvalue, %rdb);

    # If record exists, edit.
    if ($row >= 0) {
	%rdb = db_edit_record ($row, $fldstr, $valstr, %rdb);
	# Write table
	$rc = write_rdb_table ("$db_dir/$table_name", $db_backup_table, %rdb);
    }

    return $rc;

}

#-----------------------------------------------------------------
# Edit an existing locations record.
#-----------------------------------------------------------------
sub impdb_edit_locations_record {
    my ($target_fname, $target_fvalue, $fldstr, $valstr, $table_name) = @_;

    my $rc = 1; # 1 = failure, 0 = success

    # Read the table
    my %rdb = db_get_table ("cat $db_dir/$table_name");

    # Check if record already exists with this printer_id
    my $row = db_find_record ($target_fname, $target_fvalue, %rdb);

    # If record exists, edit.
    if ($row >= 0) {
	%rdb = db_edit_record ($row, $fldstr, $valstr, %rdb);
	# Write table
	$rc = write_rdb_table ("$db_dir/$table_name", $db_backup_table, %rdb);
    }

    return $rc;

}

#-----------------------------------------------------------------
# Delete an existing locations record.
#-----------------------------------------------------------------
sub impdb_delete_locations_record {
    my ($fname, $fvalue, $table_name) = @_;

    my $rc = 0;
    my $write_rc;

    # Read the table
    my %rdb = db_get_table ("cat $db_dir/$table_name");

    # Check if record already exists with this printer_id
    ($rc, %rdb) = db_delete_record ($fname, $fvalue, %rdb);

    # Write table ($rc contains number of records deleted).
    if ($rc > 0) {
	$write_rc = write_rdb_table ("$db_dir/$table_name", $db_backup_table, %rdb);
	if ($write_rc != 0) {
	    $rc = 0;
	}
    }

    return $rc;

}

#-----------------------------------------------------------------
# Delete an existing geography record.
#-----------------------------------------------------------------
sub impdb_delete_geography_record {
    my ($fname, $fvalue, $table_name) = @_;

    my $rc = 0;
    my $write_rc;

    # Read the table
    my %rdb = db_get_table ("cat $db_dir/$table_name");

    # Check if record already exists with this printer_id
    ($rc, %rdb) = db_delete_record ($fname, $fvalue, %rdb);

    # Write table ($rc contains number of records deleted).
    if ($rc > 0) {
	$write_rc = write_rdb_table ("$db_dir/$table_name", $db_backup_table, %rdb);
	if ($write_rc != 0) {
	    $rc = 0;
	}
    }

    return $rc;

}

#-----------------------------------------------------------------
# Delete an existing geopref record.
#-----------------------------------------------------------------
sub impdb_delete_geopref_record {
    my ($fname, $fvalue, $table_name) = @_;

    my $rc = 0;
    my $write_rc;

    # Read the table
    my %rdb = db_get_table ("cat $db_dir/$table_name");

    # If record matches, delete it
    ($rc, %rdb) = db_delete_record ($fname, $fvalue, %rdb);

    # Write table ($rc contains number of records deleted).
    if ($rc > 0) {
	$write_rc = write_rdb_table ("$db_dir/$table_name", $db_backup_table, %rdb);
	if ($write_rc != 0) {
	    $rc = 0;
	}
    }

    return $rc;
}

#-----------------------------------------------------------------
# Delete an existing aliases record.
#-----------------------------------------------------------------
sub impdb_delete_aliases_record {
    my ($fname, $fvalue, $table_name) = @_;

    my $rc = 0;
    my $write_rc;

    # Read the table
    my %rdb = db_get_table ("cat $db_dir/$table_name");

    # If record matches, delete it
    ($rc, %rdb) = db_delete_record ($fname, $fvalue, %rdb);

    # Write table ($rc contains number of records deleted).
    if ($rc > 0) {
	$write_rc = write_rdb_table ("$db_dir/$table_name", $db_backup_table, %rdb);
	if ($write_rc != 0) {
	    $rc = 0;
	}
    }

    return $rc;
}

#-----------------------------------------------------------------
# Read a table and return it "as-is".
#-----------------------------------------------------------------
sub impdb_get_table {
    my ($tabname) = @_;

    my $filename = "$db_dir/$tabname";

    # Check for table write lock, wait a few seconds if necessary.
    if (! db_wait_for_unlock ($filename, 10)) {
	return undef;
    }

    my %tbl = db_get_table ("cat $filename");
    return %tbl;
}

#-----------------------------------------------------------------
#-----------------------------------------------------------------
sub impdb_dump_table {
    my (%rdb) = @_;
    my $order = ($rdb{ORDER});

    # COMMENTS
    my $comments = $rdb{COMMENTS};
    for ($idx=0; $idx <= $#$comments; $idx++) {
	print $comments->[$idx]."\n";
    }

    # FIELD NAMES
    $textout = $order->[0];
    for ($fld=1; $fld <= $#$order; $fld++) {
	$textout .= "\t".$order->[$fld];
    }
    print $textout."\n";

    # FIELD TYPES
    $textout = $rdb{FIELDS}->{$order->[0]}->{FTYPE};
    for ($fld=1; $fld <= $#$order; $fld++) {
	$textout .= "\t".$rdb{FIELDS}->{$order->[$fld]}->{FTYPE};
    }
    print $textout."\n";


    # DATA
    my $rec;


    for ($rec=0; $rec <= ($rdb{NUM_RECORDS}-1); $rec++) {
	if ((! defined($rdb{DELETED}->[$rec])) || ($rdb{DELETED}->[$rec] != 1)) {
	    $textout = $rdb{FIELDS}->{$order->[0]}->{VALUE}->[$rec];
	    for ($fld=1; $fld <= $#$order; $fld++) {
		$textout .= "\t".$rdb{FIELDS}->{$order->[$fld]}->{VALUE}->[$rec];
	    }
	    print $textout."\n";
	}

    }

}


1;

