--- /dev/null
+package BackupPC::Config;
+
+use warnings;
+use Data::Dumper;
+
+our %ConfigDef;
+
+# this currently isn't used (or completed)
+sub CheckConfigInfo
+{
+ my($self) = @_;
+ my $errstr = '';
+
+ my($attr, $val, $def, $ref);
+
+ foreach $attr (sort keys %{ $self->{Conf} }) {
+ print AA "Checking $attr...";
+ $val = $self->{Conf}->{$attr};
+ $ref = ref $val;
+ $def = $ConfigDef{$attr};
+
+ if (!defined $def) {
+ $errstr .= "Unknown attribute $attr; ";
+ } elsif ($def->{struct} eq 'SCALAR' && $ref) {
+ $errstr .= "$attr expected to be SCALAR but is $ref; ";
+ } elsif ($def->{struct} =~ /^ARRAY(OFHASH)$/ && $ref && $ref ne 'ARRAY') {
+ $errstr .= "$attr expected to be ARRAY but is $ref; ";
+ } elsif ($def->{struct} =~ /^HASH(OFARRAY)$/ && $ref && $ref ne 'HASH') {
+ $errstr .= "$attr expected to be HASH but is $ref; ";
+ # still working out this logic..
+ #} elsif (defined $val && !$ref) {
+ # # if we got a scalar but were expecting a reference, fix it
+ #
+ # if($def->{struct} eq 'ARRAY') {
+ # $val = [ $val ];
+ # } elsif ($def->{struct} eq 'HASH') {
+ # $val = { $val };
+ # } elsif ($def->{struct} eq 'ARRAYOFHASH') {
+ # $val = [ { $val } ];
+ # } elsif ($def->{struct} eq 'HASHOFARRAY') {
+ # $val = { [ $val ] };
+ # }
+
+ }
+ }
+
+ return $errstr;
+}
+
+sub TopDir
+{
+ my($self) = @_;
+ return $self->{TopDir};
+}
+
+sub BinDir
+{
+ my($self) = @_;
+ return $self->{BinDir};
+}
+
+sub Version
+{
+ my($self) = @_;
+ return $self->{Version};
+}
+
+sub Conf
+{
+ my($self) = @_;
+ return %{$self->{Conf}};
+}
+
+sub ConfigDef
+{
+ my($self) = @_;
+ return \%ConfigDef;
+}
+
+sub Lang
+{
+ my($self) = @_;
+ return $self->{Lang};
+}
+
+sub adminJob
+{
+ return " admin ";
+}
+
+sub trashJob
+{
+ return " trashClean ";
+}
+
+sub timeStamp
+{
+ my($self, $t, $noPad) = @_;
+ my($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst)
+ = localtime($t || time);
+ $year += 1900;
+ $mon++;
+ return "$year/$mon/$mday " . sprintf("%02d:%02d:%02d", $hour, $min, $sec)
+ . ($noPad ? "" : " ");
+}
+
+sub ConnectData {
+ # fallback routine in case no database used
+ return 1;
+
+}
+
+###########################
+
+
+%ConfigDef = (
+ ServerHost => {struct => 'SCALAR',
+ type => 'STRING', },
+
+ ServerPort => {struct => 'SCALAR',
+ type => 'INT', },
+
+ ServerMesgSecret => {struct => 'SCALAR',
+ type => 'STRING', },
+
+ MyPath => {struct => 'SCALAR',
+ type => 'STRING', },
+
+ UmaskMode => {struct => 'SCALAR',
+ type => 'INT', },
+
+ WakeupSchedule => {struct => 'ARRAY',
+ type => 'INT', },
+
+ MaxBackups => {struct => 'SCALAR',
+ type => 'INT', },
+
+ MaxUserBackups => {struct => 'SCALAR',
+ type => 'INT', },
+
+ MaxPendingCmds => {struct => 'SCALAR',
+ type => 'INT', },
+
+ MaxOldLogFiles => {struct => 'SCALAR',
+ type => 'INT', },
+
+ DfPath => {struct => 'SCALAR',
+ type => 'STRING', },
+
+ DfMaxUsagePct => {struct => 'SCALAR',
+ type => 'INT', },
+
+ TrashCleanSleepSec => {struct => 'SCALAR',
+ type => 'INT', },
+
+ DHCPAddressRanges => {struct => 'ARRAYOFHASH',
+ type => {ipAddrBase => 'STRING',
+ first => 'INT',
+ last => 'INT',}, },
+
+ BackupPCUser => {struct => 'SCALAR',
+ type => 'STRING', },
+
+ CgiDir => {struct => 'SCALAR',
+ type => 'STRING', },
+
+ InstallDir => {struct => 'SCALAR',
+ type => 'STRING', },
+
+ BackupPCUserVerify => {struct => 'SCALAR',
+ type => 'BOOLEAN', },
+
+ SmbShareName => {struct => 'ARRAY',
+ type => 'STRING', },
+
+ SmbShareUserName => {struct => 'SCALAR',
+ type => 'STRING', },
+
+ SmbSharePasswd => {struct => 'SCALAR',
+ type => 'STRING', },
+
+ TarShareName => {struct => 'ARRAY',
+ type => 'STRING', },
+
+ FullPeriod => {struct => 'SCALAR',
+ type => 'FLOAT', },
+
+ IncrPeriod => {struct => 'SCALAR',
+ type => 'FLOAT', },
+
+ FullKeepCnt => {struct => 'SCALAR',
+ type => 'INT', },
+
+ FullKeepCntMin => {struct => 'SCALAR',
+ type => 'INT', },
+
+ FullAgeMax => {struct => 'SCALAR',
+ type => 'INT', },
+
+ IncrKeepCnt => {struct => 'SCALAR',
+ type => 'INT', },
+
+ IncrKeepCntMin => {struct => 'SCALAR',
+ type => 'INT', },
+
+ IncrAgeMax => {struct => 'SCALAR',
+ type => 'INT', },
+
+ IncrFill => {struct => 'SCALAR',
+ type => 'BOOLEAN', },
+
+ RestoreInfoKeepCnt => {struct => 'SCALAR',
+ type => 'INT', },
+
+ BackupFilesOnly => {struct => 'HASHOFARRAY',
+ type => 'STRING', },
+
+ BackupFilesExclude => {struct => 'HASHOFARRAY',
+ type => 'STRING', },
+
+ BlackoutBadPingLimit => {struct => 'SCALAR',
+ type => 'INT', },
+
+ BlackoutGoodCnt => {struct => 'SCALAR',
+ type => 'INT', },
+
+ BlackoutHourBegin => {struct => 'SCALAR',
+ type => 'FLOAT', },
+
+ BlackoutHourEnd => {struct => 'SCALAR',
+ type => 'FLOAT', },
+
+ BlackoutWeekDays => {struct => 'ARRAY',
+ type => 'INT', },
+
+ XferMethod => {struct => 'SCALAR',
+ type => 'STRING', },
+
+ SmbClientPath => {struct => 'SCALAR',
+ type => 'STRING', },
+
+ SmbClientArgs => {struct => 'SCALAR',
+ type => 'STRING', },
+
+ TarClientCmd => {struct => 'SCALAR',
+ type => 'STRING', },
+
+ TarFullArgs => {struct => 'SCALAR',
+ type => 'STRING', },
+
+ TarIncrArgs => {struct => 'SCALAR',
+ type => 'STRING', },
+
+ TarClientRestoreCmd => {struct => 'SCALAR',
+ type => 'STRING', },
+
+ TarClientPath => {struct => 'SCALAR',
+ type => 'STRING', },
+
+ SshPath => {struct => 'SCALAR',
+ type => 'STRING', },
+
+ NmbLookupPath => {struct => 'SCALAR',
+ type => 'STRING', },
+
+ FixedIPNetBiosNameCheck => {struct => 'SCALAR',
+ type => 'BOOLEAN', },
+
+ PingPath => {struct => 'SCALAR',
+ type => 'STRING', },
+
+ PingArgs => {struct => 'SCALAR',
+ type => 'STRING', },
+
+ CompressLevel => {struct => 'SCALAR',
+ type => 'INT', },
+
+ PingMaxMsec => {struct => 'SCALAR',
+ type => 'INT', },
+
+ SmbClientTimeout => {struct => 'SCALAR',
+ type => 'INT', },
+
+ MaxOldPerPCLogFiles => {struct => 'SCALAR',
+ type => 'INT', },
+
+ SendmailPath => {struct => 'SCALAR',
+ type => 'STRING', },
+
+ EMailNotifyMinDays => {struct => 'SCALAR',
+ type => 'INT', },
+
+ EMailFromUserName => {struct => 'SCALAR',
+ type => 'STRING', },
+
+ EMailAdminUserName => {struct => 'SCALAR',
+ type => 'STRING', },
+
+ EMailNoBackupEverMesg => {struct => 'SCALAR',
+ type => 'MEMO', },
+
+ EMailNotifyOldBackupDays => {struct => 'SCALAR',
+ type => 'INT', },
+
+ EMailNoBackupRecentMesg => {struct => 'SCALAR',
+ type => 'MEMO', },
+
+ EMailNotifyOldOutlookDays => {struct => 'SCALAR',
+ type => 'INT', },
+
+ EMailOutlookBackupMesg => {struct => 'SCALAR',
+ type => 'MEMO', },
+
+ CgiAdminUserGroup => {struct => 'SCALAR',
+ type => 'STRING', },
+
+ CgiAdminUsers => {struct => 'SCALAR',
+ type => 'STRING', },
+
+ Language => {struct => 'SCALAR',
+ type => 'STRING', },
+
+ CgiUserHomePageCheck => {struct => 'SCALAR',
+ type => 'STRING', },
+
+ CgiUserUrlCreate => {struct => 'SCALAR',
+ type => 'STRING', },
+
+ CgiDateFormatMMDD => {struct => 'SCALAR',
+ type => 'BOOLEAN', },
+
+ CgiNavBarAdminAllHosts => {struct => 'SCALAR',
+ type => 'BOOLEAN', },
+
+ CgiHeaderFontType => {struct => 'SCALAR',
+ type => 'STRING', },
+
+ CgiHeaderFontSize => {struct => 'SCALAR',
+ type => 'INT', },
+
+ CgiNavBarBgColor => {struct => 'SCALAR',
+ type => 'STRING', },
+
+ CgiHeaderBgColor => {struct => 'SCALAR',
+ type => 'STRING', },
+
+ CgiHeaders => {struct => 'SCALAR',
+ type => 'STRING', },
+
+ CgiImageDir => {struct => 'SCALAR',
+ type => 'STRING', },
+
+ CgiImageDirURL => {struct => 'SCALAR',
+ type => 'STRING', },
+
+);
+
+1;
--- /dev/null
+package BackupPC::Config::Db;
+
+use base 'BackupPC::Config';
+use warnings;
+use strict;
+
+use DBI;
+our $SELF;
+
+sub BackupInfoRead
+{
+ my($self, $client) = @_;
+
+ # ORDER BY is important! BackupPC_dump expects list to be sorted
+ my $cmd = "SELECT " . join(', ', @{ $self->{BackupFields} })
+ . " FROM Backup WHERE client = '$client' ORDER BY num";
+ my $sth = $self->{dbh}->prepare($cmd);
+
+ $sth->execute;
+ my($row, @backups);
+
+NUM:
+ while ($row = $sth->fetchrow_hashref) {
+ $backups[@backups] = { %$row };
+ }
+
+ return @backups;
+}
+
+sub BackupInfoWrite
+{
+ my($self, $client, @backups) = @_;
+
+ #BackupPC_dump passes an array containing all backup records, so we must
+ #1) figure out which ones aren't in the database and add them; then
+ #2) delete records in the database that weren't passed
+
+ # get a hash of currently existing backup nums from database
+ my %current = map {$_, 1}
+ @{ $self->{dbh}->selectcol_arrayref("SELECT num FROM Backup") };
+
+ my %textFields = map {$_, 1} 'client', @{ $self->{BackupTextFields} };
+
+ my($num, $cmd, $sth);
+
+NUM:
+ foreach my $backup (@backups) {
+ $num = $backup->{num};
+
+ if (defined $current{$num}) {
+ #it's in the database as well as @backups; delete it from hash
+ delete $current{$num};
+
+ } else {
+ #it's not in database yet, so add it
+ $cmd = "INSERT Backup (client, " . join(', ', @{ $self->{BackupFields} })
+ . ") VALUES ('$client', " . join(', ',
+ map {(defined $textFields{$_})? "'$backup->{$_}'" : $backup->{$_}}
+ @{ $self->{BackupFields} }) . ")";
+
+ $self->{dbh}->prepare($cmd)->execute;
+ }
+
+ }
+
+ # any remaining items in %current should be discarded
+ if (%current) {
+ $cmd = "DELETE FROM Backup WHERE num IN (" . join(', ', sort keys %current)
+ . ")";
+ $self->{dbh}->prepare($cmd)->execute;
+ }
+
+}
+
+
+# See comments in "Backup" subs, above
+sub RestoreInfoRead
+{
+ my($self, $client) = @_;
+
+ # ORDER BY is important! BackupPC_dump expects list to be sorted
+ my $cmd = "SELECT " . join(', ', @{ $self->{RestoreFields} })
+ . " FROM Restore WHERE client = '$client' ORDER BY num";
+ my $sth = $self->{dbh}->prepare($cmd);
+
+ $sth->execute;
+ my($row, @restores);
+
+NUM:
+ while ($row = $sth->fetchrow_hashref) {
+ $restores[@restores] = { %$row };
+ }
+
+ return @restores;
+}
+
+
+# See comments in "Backup" subs, above
+sub RestoreInfoWrite
+{
+ my($self, $client, @restores) = @_;
+
+ my %current = map {$_, 1}
+ @{ $self->{dbh}->selectcol_arrayref("SELECT num FROM Restore") };
+
+ my %textFields = map {$_, 1} 'client', @{ $self->{RestoreTextFields} };
+
+ my($num, $cmd, $sth);
+
+NUM:
+ foreach my $restore (@restores) {
+ $num = $restore->{num};
+
+ if (defined $current{$num}) {
+ delete $current{$num};
+
+ } else {
+ $cmd = "INSERT Restore (client, " . join(', ', @{ $self->{RestoreFields} })
+ . ") VALUES ('$client', " . join(', ',
+ map {(defined $textFields{$_})? "'$restore->{$_}'" : $restore->{$_}}
+ @{ $self->{RestoreFields} }) . ")";
+
+ $self->{dbh}->prepare($cmd)->execute;
+ }
+
+ }
+
+ if (%current) {
+ $cmd = "DELETE FROM Restore WHERE num IN (" . join(', ', sort keys %current)
+ . ")";
+ $self->{dbh}->prepare($cmd)->execute;
+ }
+
+}
+
+sub HostInfoRead {
+ my($self, $oneClient) = @_;
+
+ my $cmd = "SELECT client AS host, dhcp, user, moreUsers FROM Client";
+ my $sth = $self->{dbh}->prepare($cmd);
+
+ $sth->execute;
+ my($row, $client, %clients);
+
+CLIENT:
+ while ($row = $sth->fetchrow_hashref) {
+ $client = $row->{host};
+
+ if (defined $oneClient) {
+ next CLIENT unless $oneClient eq $client;
+ $clients{$client} = {%$row};
+ return \%clients;
+ }
+
+ $clients{$client} = {%$row};
+ }
+
+ return \%clients;
+
+}
+
+
+#TODO: Replace w/ Db version!!
+sub ConfigRead
+{
+ my($self, $host) = @_;
+ my($ret, $mesg, $config, @configs);
+
+ our %Conf;
+
+ $self->{Conf} = ();
+ push(@configs, "$self->{TopDir}/conf/config.pl");
+ push(@configs, "$self->{TopDir}/pc/$host/config.pl")
+ if ( defined($host) && -f "$self->{TopDir}/pc/$host/config.pl" );
+ foreach $config ( @configs ) {
+ %Conf = ();
+ if ( !defined($ret = do $config) && ($! || $@) ) {
+ $mesg = "Couldn't open $config: $!" if ( $! );
+ $mesg = "Couldn't execute $config: $@" if ( $@ );
+ $mesg =~ s/[\n\r]+//;
+ return $mesg;
+ }
+ %{$self->{Conf}} = ( %{$self->{Conf} || {}}, %Conf );
+ }
+
+ #$mesg = $self->CheckConfigInfo;
+ #return $mesg if $mesg;
+
+ return if ( !defined($self->{Conf}{Language}) );
+
+ my $langFile = "$self->{LibDir}/BackupPC/Lang/$self->{Conf}{Language}.pm";
+
+ if ( !defined($ret = do $langFile) && ($! || $@) ) {
+ $mesg = "Couldn't open language file $langFile: $!" if ( $! );
+ $mesg = "Couldn't execute language file $langFile: $@" if ( $@ );
+ $mesg =~ s/[\n\r]+//;
+ return $mesg;
+ }
+
+ our %Lang;
+ $self->{Lang} = \%Lang;
+
+ return;
+}
+
+our %gConfigWriteHandler = (SCALAR => \&_ConfigWriteScalar,
+ ARRAY => \&_ConfigWriteArray,
+ HASH => \&_ConfigWriteHash,
+ ARRAYOFHASH => \&_ConfigWriteArrayOfHash,
+ HASHOFARRAY => \&_ConfigWriteHashOfArray,
+ );
+
+our %gConfigTypeField; # will be defined by database-specific Config module
+
+sub ConfigWrite {
+ my($self, $client) = @_;
+ $SELF = $self;
+ my $dbh = $self->{dbh};
+
+ $dbh->{RaiseError} = 0;
+ my($cmd, $sth);
+
+ $cmd = "DELETE FROM Config WHERE client = '~~$client'";
+ $sth = $dbh->prepare($cmd) or return "$cmd\n". $dbh->errstr;
+ $sth->execute or return "$cmd\n". $dbh->errstr;
+
+ $cmd = "UPDATE Config SET client = '~~$client' WHERE client = '$client'";
+ $sth = $dbh->prepare($cmd) or return "$cmd\n". $dbh->errstr;
+ $sth->execute or return "$cmd\n". $dbh->errstr;
+
+ my($attr, $val, $def, $handler, $mesg);
+
+ foreach $attr (sort keys %{ $self->{Conf} }) {
+ $val = $self->{Conf}->{$attr};
+ $def = $self->{ConfigDef}->{$attr};
+
+ $handler = $gConfigWriteHandler{$def->{struct}};
+ $mesg = &$handler($dbh, $def, $client, $attr, $val);
+ return $mesg if $mesg;
+ }
+
+
+ $cmd = "DELETE FROM Config WHERE client = '~~$client'";
+ $sth = $dbh->prepare($cmd) or return "$cmd\n". $dbh->errstr;
+ $sth->execute or return "$cmd\n". $dbh->errstr;
+
+ $self->{dbh}->{RaiseError} = 1;
+
+ return;
+}
+
+sub _ConfigWriteScalar {
+ my($dbh, $def, $client, $attr, $val) = @_;
+ $SELF->Debug("SCALAR $val") if $attr eq 'BackupFilesOnly';
+ return if !defined $val;
+
+ my $ref = ref $val;
+
+ if ($ref) {
+ return "Expected $attr to be SCALAR, but got $ref";
+ }
+
+ &_WriteConfigRow($dbh, $client, $attr, -1, '', $def->{type}, $val)
+}
+
+sub _ConfigWriteArray {
+ my($dbh, $def, $client, $attr, $val, $key) = @_;
+ $SELF->Debug("ARRAY $val, $key") if $attr eq 'BackupFilesOnly';
+ return if !defined $val;
+
+ $key = '' unless defined $key;
+ my $ref = ref $val;
+
+ if (!$ref) {
+ #expecting ARRAY, got string -- implicit convert
+ $val = [ $val ];
+ } elsif ($ref ne 'ARRAY') {
+ $attr = "$attr\{$key}" if $key ne '';
+ return "Expected $attr to be ARRAY, but got $ref";
+ }
+
+ my $subscript = 0;
+ my $item;
+ my $type = $def->{type};
+
+ foreach $item (@$val) {
+ &_WriteConfigRow($dbh, $client, $attr, $subscript++,
+ $key, $type, $item)
+ }
+}
+
+sub _ConfigWriteHash {
+ my($dbh, $def, $client, $attr, $val, $subscript) = @_;
+ $SELF->Debug("HASH $val") if $attr eq 'BackupFilesOnly';
+ return if !defined $val;
+
+ $subscript = -1 unless defined $subscript;
+ my $ref = ref $val;
+
+ if (!$ref) {
+ #expecting HASH, got string -- implicit convert
+ $val = { '*' => $val };
+ } elsif ($ref ne 'HASH') {
+ $attr = "$attr\[$subscript]" if $subscript != -1;
+ return "Expected $attr to be HASH, but got $ref";
+ }
+
+ my($key, $item);
+ my $type = $def->{type};
+
+ # If 'type' is a hash ref, this means the attribute's subvalue type
+ # depends on what its corresponding key is. In that case, we set
+ # $thisType for each iteration; otherwise, we leave it set to 'type'
+ my $typeByKey = ref $type;
+ my $thisType = $type;
+
+ foreach $key (sort keys %$val) {
+ $item = $val->{$key};
+
+ if ($typeByKey) {
+ $thisType = $type->{$key};
+
+ if (!defined $thisType) {
+ return "Don't know how to handle subvalue $key for $attr";
+ }
+ }
+
+ &_WriteConfigRow($dbh, $client, $attr, $subscript,
+ $key, $thisType, $item)
+ }
+}
+
+sub _ConfigWriteArrayOfHash {
+ my($dbh, $def, $client, $attr, $val) = @_;
+ return if !defined $val;
+
+ my $ref = ref $val;
+
+ if (!$ref) {
+ #expecting ARRAY, got string -- implicit convert
+ $val = [ $val ];
+ } elsif ($ref ne 'ARRAY') {
+ return "Expected $attr to be ARRAY, but got $ref";
+ }
+
+ my $subscript = 0;
+ my $item;
+
+ foreach $item (@$val) {
+ &_ConfigWriteHash($dbh, $def, $client, $attr,
+ $item, $subscript++);
+ }
+}
+
+sub _ConfigWriteHashOfArray {
+ my($dbh, $def, $client, $attr, $val) = @_;
+ $SELF->Debug("HASHOFARRAY $val") if $attr eq 'BackupFilesOnly';
+ return if !defined $val;
+
+ my $ref = ref $val;
+
+ if (!$ref) {
+ #expecting HASH, got string -- implicit convert
+ $val = { '*' => $val };
+ } elsif ($ref ne 'HASH') {
+ return "Expected $attr to be HASH, but got $ref";
+ }
+
+ my($key, $item);
+
+ foreach $key (sort keys %$val) {
+ $item = $val->{$key};
+ &_ConfigWriteArray($dbh, $def, $client, $attr,
+ $item, $key);
+ }
+
+}
+
+sub _WriteConfigRow {
+ my($dbh, $client, $attr, $subscript, $key, $type, $val) = @_;
+
+ defined $gConfigTypeField{$type}
+ or return "Unknown ConfigDef type '$type' ($attr); aborting";
+
+ my($confType, $field, %fields);
+
+ while(($confType, $field) = each %gConfigTypeField) {
+ if ($confType eq $type) {
+ # this is the correct field for value of interest,
+ # so copy and format it
+ $fields{$field} = ($confType =~ /^(STRING|MEMO)$/)?
+ $dbh->quote($val) : $val;
+ } else {
+ $fields{$field} = 'NULL';
+ }
+ }
+
+ $fields{'client'} = $dbh->quote($client);
+ $fields{'clientGroup'} = "''"; #TODO: add group logic
+ $fields{'attribute'} = $dbh->quote($attr);
+ $fields{'subscript'} = $subscript;
+ $fields{'hashKey'} = $dbh->quote($key);
+
+ my @fields = sort keys %fields;
+ my @values = map { $fields{ $_ } } @fields;
+
+ my $cmd = "INSERT Config (" . join(', ', @fields) . ")\nVALUES ("
+ . join(', ', @values) . ")";
+
+ my $sth = $dbh->prepare($cmd) or return "$cmd\n\n" . $dbh->errstr;
+ $sth->execute or return "$cmd\n\n". $dbh->errstr;
+
+ return;
+}
+
+#TODO: Replace w/ Db version!!
+#
+# Return the mtime of the config file
+#
+sub ConfigMTime
+{
+ my($self) = @_;
+ return (stat("$self->{TopDir}/conf/config.pl"))[9];
+}
+
+
+
+sub DESTROY {
+ my($self) = @_;
+
+ $self->{dbh}->disconnect if defined $self->{dbh};
+}
+
+
+1;
--- /dev/null
+package BackupPC::Config::Db::MySQL;
+
+use base 'BackupPC::Config::Db';
+use warnings;
+use strict;
+
+use DBI;
+use DBD::mysql;
+
+%BackupPC::Config::Db::gConfigTypeField =
+ (BOOLEAN => 'valueBit',
+ INT => 'valueInt',
+ FLOAT => 'valueFloat',
+ STRING => 'valueString',
+ MEMO => 'valueMemo',
+ );
+
+sub ConnectData {
+ my($self) = @_;
+
+ my $dsn = 'DBI:mysql:database=backuppc;host=reagan';
+ $self->{dbh} = DBI->connect($dsn, 'root', undef, {RaiseError => 1,
+ AutoCommit => 1,});
+}
+
+sub ConfigMTime {
+ my($self) = @_;
+return time();
+ my $cmd = "SHOW TABLE STATUS LIKE 'Config'";
+ my $sth = $self->{dbh}->prepare($cmd);
+
+ $sth->execute;
+ my $row = $sth->fetchrow_hashref || return time();
+ my $mtime;
+
+ if (defined($mtime = $row->{'Update_time'})) {
+ $mtime =~ m/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/
+ || return time();
+ return &Date_SecsSince1970($2,$3,$1,$4,$5,$6);
+ } else {
+ return time();
+ }
+}
+
+sub HostsMTime {
+ my($self) = @_;
+return time();
+ my $cmd = "SHOW TABLE STATUS LIKE 'Client'";
+ my $sth = $self->{dbh}->prepare($cmd);
+
+ $sth->execute;
+ my $row = $sth->fetchrow_hashref || return time();
+ my $mtime;
+
+ if (defined($mtime = $row->{'Update_time'})) {
+ return &Date_SecsSince1970($mtime);
+ } else {
+ return time();
+ }
+}
+
+# Date subs borrowed from Date::Manip.
+# Copyright (c) 1995-2001 Sullivan Beck. All rights reserved.
+# This program is free software; you can redistribute it and/or modify it
+# under the same terms as Perl itself.
+
+sub Date_SecsSince1970 {
+ my($mysqlDate) = @_;
+ $mysqlDate =~ m/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/
+ || return time();
+ my($y,$m,$d,$h,$mn,$s) = ($1,$2,$3,$4,$5,$6);
+ my($sec_now,$sec_70,$Ny,$N4,$N100,$N400,$dayofyear,$days)=();
+ my($cc,$yy)=();
+
+ $y=~ /(\d{2})(\d{2})/;
+ ($cc,$yy)=($1,$2);
+
+ $Ny=$y;
+
+ $N4=($Ny-1)/4 + 1;
+ $N4=0 if ($y==0);
+
+ $N100=$cc + 1;
+ $N100-- if ($yy==0);
+ $N100=0 if ($y==0);
+
+ $N400=($N100-1)/4 + 1;
+ $N400=0 if ($y==0);
+
+ my(@days) = ( 0, 31, 59, 90,120,151,181,212,243,273,304,334,365);
+ my($ly)=0;
+ $ly=1 if ($m>2 && &Date_LeapYear($y));
+
+ $dayofyear=$days[$m-1]+$d+$ly;
+ $days= $Ny*365 + $N4 - $N100 + $N400 + $dayofyear;
+ $sec_now=($days-1)*24*3600 + $h*3600 + $mn*60 + $s;
+ $sec_70 =62167219200;
+ return ($sec_now-$sec_70);
+}
+
+sub Date_LeapYear {
+ my($y)=@_;
+ return 0 if $y % 4;
+ return 1 if $y % 100;
+ return 0 if $y % 400;
+ return 1;
+}
+
+
+
+1;
--- /dev/null
+package BackupPC::Config::Text;
+
+use warnings;
+use strict;
+use Fcntl qw/:flock/;
+
+use base 'BackupPC::Config';
+
+sub BackupInfoRead
+{
+ my($self, $host) = @_;
+ local(*BK_INFO, *LOCK);
+ my(@Backups);
+
+ flock(LOCK, LOCK_EX) if open(LOCK, "$self->{TopDir}/pc/$host/LOCK");
+ if ( open(BK_INFO, "$self->{TopDir}/pc/$host/backups") ) {
+ while ( <BK_INFO> ) {
+ s/[\n\r]+//;
+ next if ( !/^(\d+\t(incr|full)[\d\t]*$)/ );
+ $_ = $1;
+ @{$Backups[@Backups]}{@{$self->{BackupFields}}} = split(/\t/);
+ }
+ close(BK_INFO);
+ }
+ close(LOCK);
+ return @Backups;
+}
+
+sub BackupInfoWrite
+{
+ my($self, $host, @Backups) = @_;
+ local(*BK_INFO, *LOCK);
+ my($i);
+
+ flock(LOCK, LOCK_EX) if open(LOCK, "$self->{TopDir}/pc/$host/LOCK");
+ unlink("$self->{TopDir}/pc/$host/backups.old")
+ if ( -f "$self->{TopDir}/pc/$host/backups.old" );
+ rename("$self->{TopDir}/pc/$host/backups",
+ "$self->{TopDir}/pc/$host/backups.old")
+ if ( -f "$self->{TopDir}/pc/$host/backups" );
+ if ( open(BK_INFO, ">$self->{TopDir}/pc/$host/backups") ) {
+ for ( $i = 0 ; $i < @Backups ; $i++ ) {
+ my %b = %{$Backups[$i]};
+ printf(BK_INFO "%s\n", join("\t", @b{@{$self->{BackupFields}}}));
+ }
+ close(BK_INFO);
+ }
+ close(LOCK);
+}
+
+sub RestoreInfoRead
+{
+ my($self, $host) = @_;
+ local(*RESTORE_INFO, *LOCK);
+ my(@Restores);
+
+ flock(LOCK, LOCK_EX) if open(LOCK, "$self->{TopDir}/pc/$host/LOCK");
+ if ( open(RESTORE_INFO, "$self->{TopDir}/pc/$host/restores") ) {
+ while ( <RESTORE_INFO> ) {
+ s/[\n\r]+//;
+ next if ( !/^(\d+.*)/ );
+ $_ = $1;
+ @{$Restores[@Restores]}{@{$self->{RestoreFields}}} = split(/\t/);
+ }
+ close(RESTORE_INFO);
+ }
+ close(LOCK);
+ return @Restores;
+}
+
+sub RestoreInfoWrite
+{
+ my($self, $host, @Restores) = @_;
+ local(*RESTORE_INFO, *LOCK);
+ my($i);
+
+ flock(LOCK, LOCK_EX) if open(LOCK, "$self->{TopDir}/pc/$host/LOCK");
+ unlink("$self->{TopDir}/pc/$host/restores.old")
+ if ( -f "$self->{TopDir}/pc/$host/restores.old" );
+ rename("$self->{TopDir}/pc/$host/restores",
+ "$self->{TopDir}/pc/$host/restores.old")
+ if ( -f "$self->{TopDir}/pc/$host/restores" );
+ if ( open(RESTORE_INFO, ">$self->{TopDir}/pc/$host/restores") ) {
+ for ( $i = 0 ; $i < @Restores ; $i++ ) {
+ my %b = %{$Restores[$i]};
+ printf(RESTORE_INFO "%s\n",
+ join("\t", @b{@{$self->{RestoreFields}}}));
+ }
+ close(RESTORE_INFO);
+ }
+ close(LOCK);
+}
+
+sub ConfigRead
+{
+ my($self, $host) = @_;
+ my($ret, $mesg, $config, @configs);
+
+ our %Conf;
+
+ $self->{Conf} = ();
+ push(@configs, "$self->{TopDir}/conf/config.pl");
+ push(@configs, "$self->{TopDir}/pc/$host/config.pl")
+ if ( defined($host) && -f "$self->{TopDir}/pc/$host/config.pl" );
+ foreach $config ( @configs ) {
+ %Conf = ();
+ if ( !defined($ret = do $config) && ($! || $@) ) {
+ $mesg = "Couldn't open $config: $!" if ( $! );
+ $mesg = "Couldn't execute $config: $@" if ( $@ );
+ $mesg =~ s/[\n\r]+//;
+ return $mesg;
+ }
+ %{$self->{Conf}} = ( %{$self->{Conf} || {}}, %Conf );
+ }
+
+ #$mesg = $self->CheckConfigInfo;
+ #return $mesg if $mesg;
+
+ return if ( !defined($self->{Conf}{Language}) );
+
+ my $langFile = "$self->{LibDir}/BackupPC/Lang/$self->{Conf}{Language}.pm";
+
+ if ( !defined($ret = do $langFile) && ($! || $@) ) {
+ $mesg = "Couldn't open language file $langFile: $!" if ( $! );
+ $mesg = "Couldn't execute language file $langFile: $@" if ( $@ );
+ $mesg =~ s/[\n\r]+//;
+ return $mesg;
+ }
+
+ our %Lang;
+ $self->{Lang} = \%Lang;
+
+ return;
+}
+
+
+#
+# Return the mtime of the config file
+#
+sub ConfigMTime
+{
+ my($self) = @_;
+ return (stat("$self->{TopDir}/conf/config.pl"))[9];
+}
+
+#
+# Returns information from the host file in $self->{TopDir}/conf/hosts.
+# With no argument a ref to a hash of hosts is returned. Each
+# hash contains fields as specified in the hosts file. With an
+# argument a ref to a single hash is returned with information
+# for just that host.
+#
+sub HostInfoRead
+{
+ my($self, $host) = @_;
+ my(%hosts, @hdr, @fld);
+ local(*HOST_INFO);
+
+ if ( !open(HOST_INFO, "$self->{TopDir}/conf/hosts") ) {
+ print(STDERR $self->timeStamp,
+ "Can't open $self->{TopDir}/conf/hosts\n");
+ return {};
+ }
+ while ( <HOST_INFO> ) {
+ s/[\n\r]+//;
+ s/#.*//;
+ s/\s+$//;
+ next if ( /^\s*$/ || !/^([\w\.-]+\s+.*)/ );
+ @fld = split(/\s+/, $1);
+ if ( @hdr ) {
+ if ( defined($host) ) {
+ next if ( lc($fld[0]) ne $host );
+ @{$hosts{lc($fld[0])}}{@hdr} = @fld;
+ close(HOST_INFO);
+ return \%hosts;
+ } else {
+ @{$hosts{lc($fld[0])}}{@hdr} = @fld;
+ }
+ } else {
+ @hdr = @fld;
+ }
+ }
+ close(HOST_INFO);
+ return \%hosts;
+}
+
+#
+# Return the mtime of the hosts file
+#
+sub HostsMTime
+{
+ my($self) = @_;
+ return (stat("$self->{TopDir}/conf/hosts"))[9];
+}
+
+
+
+1;