1 package BackupPC::Config::Db;
3 use base 'BackupPC::Config';
12 my($self, $client) = @_;
14 # ORDER BY is important! BackupPC_dump expects list to be sorted
15 my $cmd = "SELECT " . join(', ', @{ $self->{BackupFields} })
16 . " FROM Backup WHERE client = '$client' ORDER BY num";
17 my $sth = $self->{dbh}->prepare($cmd);
23 while ($row = $sth->fetchrow_hashref) {
24 $backups[@backups] = { %$row };
32 my($self, $client, @backups) = @_;
34 #BackupPC_dump passes an array containing all backup records, so we must
35 #1) figure out which ones aren't in the database and add them; then
36 #2) delete records in the database that weren't passed
38 # get a hash of currently existing backup nums from database
39 my %current = map {$_, 1}
40 @{ $self->{dbh}->selectcol_arrayref("SELECT num FROM Backup") };
42 my %textFields = map {$_, 1} 'client', @{ $self->{BackupTextFields} };
47 foreach my $backup (@backups) {
48 $num = $backup->{num};
50 if (defined $current{$num}) {
51 #it's in the database as well as @backups; delete it from hash
52 delete $current{$num};
55 #it's not in database yet, so add it
56 $cmd = "INSERT Backup (client, " . join(', ', @{ $self->{BackupFields} })
57 . ") VALUES ('$client', " . join(', ',
58 map {(defined $textFields{$_})? "'$backup->{$_}'" : $backup->{$_}}
59 @{ $self->{BackupFields} }) . ")";
61 $self->{dbh}->prepare($cmd)->execute;
66 # any remaining items in %current should be discarded
68 $cmd = "DELETE FROM Backup WHERE num IN (" . join(', ', sort keys %current)
70 $self->{dbh}->prepare($cmd)->execute;
76 # See comments in "Backup" subs, above
79 my($self, $client) = @_;
81 # ORDER BY is important! BackupPC_dump expects list to be sorted
82 my $cmd = "SELECT " . join(', ', @{ $self->{RestoreFields} })
83 . " FROM Restore WHERE client = '$client' ORDER BY num";
84 my $sth = $self->{dbh}->prepare($cmd);
90 while ($row = $sth->fetchrow_hashref) {
91 $restores[@restores] = { %$row };
98 # See comments in "Backup" subs, above
101 my($self, $client, @restores) = @_;
103 my %current = map {$_, 1}
104 @{ $self->{dbh}->selectcol_arrayref("SELECT num FROM Restore") };
106 my %textFields = map {$_, 1} 'client', @{ $self->{RestoreTextFields} };
108 my($num, $cmd, $sth);
111 foreach my $restore (@restores) {
112 $num = $restore->{num};
114 if (defined $current{$num}) {
115 delete $current{$num};
118 $cmd = "INSERT Restore (client, " . join(', ', @{ $self->{RestoreFields} })
119 . ") VALUES ('$client', " . join(', ',
120 map {(defined $textFields{$_})? "'$restore->{$_}'" : $restore->{$_}}
121 @{ $self->{RestoreFields} }) . ")";
123 $self->{dbh}->prepare($cmd)->execute;
129 $cmd = "DELETE FROM Restore WHERE num IN (" . join(', ', sort keys %current)
131 $self->{dbh}->prepare($cmd)->execute;
137 my($self, $oneClient) = @_;
139 my $cmd = "SELECT client AS host, dhcp, user, moreUsers FROM Client";
140 my $sth = $self->{dbh}->prepare($cmd);
143 my($row, $client, %clients);
146 while ($row = $sth->fetchrow_hashref) {
147 $client = $row->{host};
149 if (defined $oneClient) {
150 next CLIENT unless $oneClient eq $client;
151 $clients{$client} = {%$row};
155 $clients{$client} = {%$row};
163 #TODO: Replace w/ Db version!!
166 my($self, $host) = @_;
167 my($ret, $mesg, $config, @configs);
172 push(@configs, "$self->{TopDir}/conf/config.pl");
173 push(@configs, "$self->{TopDir}/pc/$host/config.pl")
174 if ( defined($host) && -f "$self->{TopDir}/pc/$host/config.pl" );
175 foreach $config ( @configs ) {
177 if ( !defined($ret = do $config) && ($! || $@) ) {
178 $mesg = "Couldn't open $config: $!" if ( $! );
179 $mesg = "Couldn't execute $config: $@" if ( $@ );
180 $mesg =~ s/[\n\r]+//;
183 %{$self->{Conf}} = ( %{$self->{Conf} || {}}, %Conf );
186 #$mesg = $self->CheckConfigInfo;
187 #return $mesg if $mesg;
189 return if ( !defined($self->{Conf}{Language}) );
191 my $langFile = "$self->{LibDir}/BackupPC/Lang/$self->{Conf}{Language}.pm";
193 if ( !defined($ret = do $langFile) && ($! || $@) ) {
194 $mesg = "Couldn't open language file $langFile: $!" if ( $! );
195 $mesg = "Couldn't execute language file $langFile: $@" if ( $@ );
196 $mesg =~ s/[\n\r]+//;
201 $self->{Lang} = \%Lang;
206 our %gConfigWriteHandler = (SCALAR => \&_ConfigWriteScalar,
207 ARRAY => \&_ConfigWriteArray,
208 HASH => \&_ConfigWriteHash,
209 ARRAYOFHASH => \&_ConfigWriteArrayOfHash,
210 HASHOFARRAY => \&_ConfigWriteHashOfArray,
213 our %gConfigTypeField; # will be defined by database-specific Config module
216 my($self, $client) = @_;
218 my $dbh = $self->{dbh};
220 $dbh->{RaiseError} = 0;
223 $cmd = "DELETE FROM Config WHERE client = '~~$client'";
224 $sth = $dbh->prepare($cmd) or return "$cmd\n". $dbh->errstr;
225 $sth->execute or return "$cmd\n". $dbh->errstr;
227 $cmd = "UPDATE Config SET client = '~~$client' WHERE client = '$client'";
228 $sth = $dbh->prepare($cmd) or return "$cmd\n". $dbh->errstr;
229 $sth->execute or return "$cmd\n". $dbh->errstr;
231 my($attr, $val, $def, $handler, $mesg);
233 foreach $attr (sort keys %{ $self->{Conf} }) {
234 $val = $self->{Conf}->{$attr};
235 $def = $self->{ConfigDef}->{$attr};
237 $handler = $gConfigWriteHandler{$def->{struct}};
238 $mesg = &$handler($dbh, $def, $client, $attr, $val);
239 return $mesg if $mesg;
243 $cmd = "DELETE FROM Config WHERE client = '~~$client'";
244 $sth = $dbh->prepare($cmd) or return "$cmd\n". $dbh->errstr;
245 $sth->execute or return "$cmd\n". $dbh->errstr;
247 $self->{dbh}->{RaiseError} = 1;
252 sub _ConfigWriteScalar {
253 my($dbh, $def, $client, $attr, $val) = @_;
254 $SELF->Debug("SCALAR $val") if $attr eq 'BackupFilesOnly';
255 return if !defined $val;
260 return "Expected $attr to be SCALAR, but got $ref";
263 &_WriteConfigRow($dbh, $client, $attr, -1, '', $def->{type}, $val)
266 sub _ConfigWriteArray {
267 my($dbh, $def, $client, $attr, $val, $key) = @_;
268 $SELF->Debug("ARRAY $val, $key") if $attr eq 'BackupFilesOnly';
269 return if !defined $val;
271 $key = '' unless defined $key;
275 #expecting ARRAY, got string -- implicit convert
277 } elsif ($ref ne 'ARRAY') {
278 $attr = "$attr\{$key}" if $key ne '';
279 return "Expected $attr to be ARRAY, but got $ref";
284 my $type = $def->{type};
286 foreach $item (@$val) {
287 &_WriteConfigRow($dbh, $client, $attr, $subscript++,
292 sub _ConfigWriteHash {
293 my($dbh, $def, $client, $attr, $val, $subscript) = @_;
294 $SELF->Debug("HASH $val") if $attr eq 'BackupFilesOnly';
295 return if !defined $val;
297 $subscript = -1 unless defined $subscript;
301 #expecting HASH, got string -- implicit convert
302 $val = { '*' => $val };
303 } elsif ($ref ne 'HASH') {
304 $attr = "$attr\[$subscript]" if $subscript != -1;
305 return "Expected $attr to be HASH, but got $ref";
309 my $type = $def->{type};
311 # If 'type' is a hash ref, this means the attribute's subvalue type
312 # depends on what its corresponding key is. In that case, we set
313 # $thisType for each iteration; otherwise, we leave it set to 'type'
314 my $typeByKey = ref $type;
315 my $thisType = $type;
317 foreach $key (sort keys %$val) {
318 $item = $val->{$key};
321 $thisType = $type->{$key};
323 if (!defined $thisType) {
324 return "Don't know how to handle subvalue $key for $attr";
328 &_WriteConfigRow($dbh, $client, $attr, $subscript,
329 $key, $thisType, $item)
333 sub _ConfigWriteArrayOfHash {
334 my($dbh, $def, $client, $attr, $val) = @_;
335 return if !defined $val;
340 #expecting ARRAY, got string -- implicit convert
342 } elsif ($ref ne 'ARRAY') {
343 return "Expected $attr to be ARRAY, but got $ref";
349 foreach $item (@$val) {
350 &_ConfigWriteHash($dbh, $def, $client, $attr,
351 $item, $subscript++);
355 sub _ConfigWriteHashOfArray {
356 my($dbh, $def, $client, $attr, $val) = @_;
357 $SELF->Debug("HASHOFARRAY $val") if $attr eq 'BackupFilesOnly';
358 return if !defined $val;
363 #expecting HASH, got string -- implicit convert
364 $val = { '*' => $val };
365 } elsif ($ref ne 'HASH') {
366 return "Expected $attr to be HASH, but got $ref";
371 foreach $key (sort keys %$val) {
372 $item = $val->{$key};
373 &_ConfigWriteArray($dbh, $def, $client, $attr,
379 sub _WriteConfigRow {
380 my($dbh, $client, $attr, $subscript, $key, $type, $val) = @_;
382 defined $gConfigTypeField{$type}
383 or return "Unknown ConfigDef type '$type' ($attr); aborting";
385 my($confType, $field, %fields);
387 while(($confType, $field) = each %gConfigTypeField) {
388 if ($confType eq $type) {
389 # this is the correct field for value of interest,
390 # so copy and format it
391 $fields{$field} = ($confType =~ /^(STRING|MEMO)$/)?
392 $dbh->quote($val) : $val;
394 $fields{$field} = 'NULL';
398 $fields{'client'} = $dbh->quote($client);
399 $fields{'clientGroup'} = "''"; #TODO: add group logic
400 $fields{'attribute'} = $dbh->quote($attr);
401 $fields{'subscript'} = $subscript;
402 $fields{'hashKey'} = $dbh->quote($key);
404 my @fields = sort keys %fields;
405 my @values = map { $fields{ $_ } } @fields;
407 my $cmd = "INSERT Config (" . join(', ', @fields) . ")\nVALUES ("
408 . join(', ', @values) . ")";
410 my $sth = $dbh->prepare($cmd) or return "$cmd\n\n" . $dbh->errstr;
411 $sth->execute or return "$cmd\n\n". $dbh->errstr;
416 #TODO: Replace w/ Db version!!
418 # Return the mtime of the config file
423 return (stat("$self->{TopDir}/conf/config.pl"))[9];
431 $self->{dbh}->disconnect if defined $self->{dbh};