#=================================================================
# 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;