4 die "bresto is not enabled" if (not $bresto_enable);
8 Bweb - A Bacula web interface
9 Bacula® - The Network Backup Solution
11 Copyright (C) 2000-2006 Free Software Foundation Europe e.V.
13 The main author of Bweb is Eric Bollengier.
14 The main author of Bacula is Kern Sibbald, with contributions from
15 many others, a complete list can be found in the file AUTHORS.
17 This program is Free Software; you can redistribute it and/or
18 modify it under the terms of version two of the GNU General Public
19 License as published by the Free Software Foundation plus additions
20 that are listed in the file LICENSE.
22 This program is distributed in the hope that it will be useful, but
23 WITHOUT ANY WARRANTY; without even the implied warranty of
24 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
25 General Public License for more details.
27 You should have received a copy of the GNU General Public License
28 along with this program; if not, write to the Free Software
29 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
32 Bacula® is a registered trademark of John Walker.
33 The licensor of Bacula is the Free Software Foundation Europe
34 (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zurich,
35 Switzerland, email:ftf@fsfeurope.org.
50 my ($self, $dir) = @_;
51 return $self->get_pathid('');
56 my ($self, $pathid) = @_;
57 $self->{cwdid} = $pathid;
65 FROM brestore_pathhierarchy
66 WHERE PathId IN ($self->{cwdid}) ";
68 my $all = $self->dbh_selectall_arrayref($query);
69 return unless ($all); # already at root
71 my $dir = join(',', map { $_->[0] } @$all);
80 return $self->get_path($self->{cwdid});
85 my ($self, $pathid) = @_;
86 $self->debug("Call with pathid = $pathid");
88 "SELECT Path FROM Path WHERE PathId IN (?)";
90 my $sth = $self->dbh_prepare($query);
91 $sth->execute($pathid);
92 my $result = $sth->fetchrow_arrayref();
99 my ($self, @jobids) = @_;
100 $self->{curjobids} = join(',', @jobids);
101 # $self->update_brestore_table(@jobids);
106 my ($self, $dir) = @_;
108 "SELECT PathId FROM Path WHERE Path = ?";
109 my $sth = $self->dbh_prepare($query);
111 my $result = $sth->fetchall_arrayref();
114 return join(',', map { $_->[0] } @$result);
119 my ($self, $offset, $limit) = @_;
120 $self->{limit} = $limit || 100;
121 $self->{offset} = $offset || 0;
128 return undef unless ($self->{curjobids});
130 my $inclause = $self->{curjobids};
131 my $inlistpath = $self->{cwdid};
134 "SELECT File.FilenameId, listfiles.id, listfiles.Name, File.LStat, File.JobId
136 (SELECT Filename.Name, max(File.FileId) as id
138 WHERE File.FilenameId = Filename.FilenameId
139 AND Filename.Name != ''
140 AND File.PathId IN ($inlistpath)
141 AND File.JobId IN ($inclause)
142 GROUP BY Filename.Name
143 ORDER BY Filename.Name) AS listfiles,
145 WHERE File.FileId = listfiles.id";
147 $self->debug($query);
148 my $result = $self->dbh_selectall_arrayref($query);
149 $self->debug($result);
155 # return ($dirid,$dir_basename,$lstat,$jobid)
160 return undef unless ($self->{curjobids});
162 my $pathid = $self->{cwdid};
163 my $jobclause = $self->{curjobids};
165 # Let's retrieve the list of the visible dirs in this dir ...
166 # First, I need the empty filenameid to locate efficiently the dirs in the file table
167 my $query = "SELECT FilenameId FROM Filename WHERE Name = ''";
168 my $sth = $self->dbh_prepare($query);
170 my $result = $sth->fetchrow_arrayref();
172 my $dir_filenameid = $result->[0];
174 # Then we get all the dir entries from File ...
176 SELECT PathId, Path, JobId, Lstat FROM (
178 SELECT Path1.PathId, Path1.Path, lower(Path1.Path),
179 listfile1.JobId, listfile1.Lstat
181 SELECT DISTINCT brestore_pathhierarchy1.PathId
182 FROM brestore_pathhierarchy AS brestore_pathhierarchy1
184 ON (brestore_pathhierarchy1.PathId = Path2.PathId)
185 JOIN brestore_pathvisibility AS brestore_pathvisibility1
186 ON (brestore_pathhierarchy1.PathId = brestore_pathvisibility1.PathId)
187 WHERE brestore_pathhierarchy1.PPathId = $pathid
188 AND brestore_pathvisibility1.jobid IN ($jobclause)) AS listpath1
189 JOIN Path AS Path1 ON (listpath1.PathId = Path1.PathId)
191 SELECT File1.PathId, File1.JobId, File1.Lstat FROM File AS File1
192 WHERE File1.FilenameId = $dir_filenameid
193 AND File1.JobId IN ($jobclause)) AS listfile1
194 ON (listpath1.PathId = listfile1.PathId)
195 ) AS A ORDER BY 2,3 DESC
197 $self->debug($query);
198 $sth=$self->dbh_prepare($query);
200 $result = $sth->fetchall_arrayref();
203 foreach my $refrow (@{$result})
205 my $dirid = $refrow->[0];
206 my $dir = $refrow->[1];
207 my $lstat = $refrow->[3];
208 my $jobid = $refrow->[2] || 0;
209 next if ($dirid eq $prev_dir);
210 # We have to clean up this dirname ... we only want it's 'basename'
214 my @temp = split ('/',$dir);
215 $return_value = pop @temp;
221 my @return_array = ($dirid,$return_value,$lstat,$jobid);
222 push @return_list,(\@return_array);
225 $self->debug(\@return_list);
226 return \@return_list;
230 # Returns list of versions of a file that could be restored
231 # returns an array of
232 # (jobid,fileindex,mtime,size,inchanger,md5,volname,fileid)
233 # there will be only one jobid in the array of jobids...
234 sub get_all_file_versions
236 my ($self,$pathid,$fileid,$client,$see_all)=@_;
238 defined $see_all or $see_all=0;
243 "SELECT File.JobId, File.FileId, File.Lstat,
244 File.Md5, Media.VolumeName, Media.InChanger
245 FROM File, Job, Client, JobMedia, Media
246 WHERE File.FilenameId = $fileid
247 AND File.PathId=$pathid
248 AND File.JobId = Job.JobId
249 AND Job.ClientId = Client.ClientId
250 AND Job.JobId = JobMedia.JobId
251 AND File.FileIndex >= JobMedia.FirstIndex
252 AND File.FileIndex <= JobMedia.LastIndex
253 AND JobMedia.MediaId = Media.MediaId
254 AND Client.Name = '$client'";
256 $self->debug($query);
258 my $result = $self->dbh_selectall_arrayref($query);
260 foreach my $refrow (@$result)
262 my ($jobid, $fid, $lstat, $md5, $volname, $inchanger) = @$refrow;
263 my @attribs = parse_lstat($lstat);
264 my $mtime = array_attrib('st_mtime',\@attribs);
265 my $size = array_attrib('st_size',\@attribs);
267 my @list = ($pathid,$fileid,$jobid,
268 $fid, $mtime, $size, $inchanger,
270 push @versions, (\@list);
273 # We have the list of all versions of this file.
274 # We'll sort it by mtime desc, size, md5, inchanger desc
275 # the rest of the algorithm will be simpler
276 # ('FILE:',filename,jobid,fileindex,mtime,size,inchanger,md5,volname)
277 @versions = sort { $b->[4] <=> $a->[4]
278 || $a->[5] <=> $b->[5]
279 || $a->[7] cmp $a->[7]
280 || $b->[6] <=> $a->[6]} @versions;
284 my %allready_seen_by_mtime;
285 my %allready_seen_by_md5;
286 # Now we should create a new array with only the interesting records
287 foreach my $ref (@versions)
291 # The file has a md5. We compare his md5 to other known md5...
292 # We take size into account. It may happen that 2 files
293 # have the same md5sum and are different. size is a supplementary
296 # If we allready have a (better) version
297 next if ( (not $see_all)
298 and $allready_seen_by_md5{$ref->[7] .'-'. $ref->[5]});
300 # we never met this one before...
301 $allready_seen_by_md5{$ref->[7] .'-'. $ref->[5]}=1;
303 # Even if it has a md5, we should also work with mtimes
304 # We allready have a (better) version
305 next if ( (not $see_all)
306 and $allready_seen_by_mtime{$ref->[4] .'-'. $ref->[5]});
307 $allready_seen_by_mtime{$ref->[4] .'-'. $ref->[5] . '-' . $ref->[7]}=1;
309 # We reached there. The file hasn't been seen.
310 push @good_versions,($ref);
313 # To be nice with the user, we re-sort good_versions by
314 # inchanger desc, mtime desc
315 @good_versions = sort { $b->[4] <=> $a->[4]
316 || $b->[2] <=> $a->[2]} @good_versions;
318 return \@good_versions;
321 my %attrib_name_id = ( 'st_dev' => 0,'st_ino' => 1,'st_mode' => 2,
322 'st_nlink' => 3,'st_uid' => 4,'st_gid' => 5,
323 'st_rdev' => 6,'st_size' => 7,'st_blksize' => 8,
324 'st_blocks' => 9,'st_atime' => 10,'st_mtime' => 11,
325 'st_ctime' => 12,'LinkFI' => 13,'st_flags' => 14,
326 'data_stream' => 15);;
329 my ($attrib,$ref_attrib)=@_;
330 return $ref_attrib->[$attrib_name_id{$attrib}];
334 { # $file = [filenameid,listfiles.id,listfiles.Name, File.LStat, File.JobId]
336 my ($file, $attrib)=@_;
338 if (defined $attrib_name_id{$attrib}) {
340 my @d = split(' ', $file->[3]) ; # TODO : cache this
342 return from_base64($d[$attrib_name_id{$attrib}]);
344 } elsif ($attrib eq 'jobid') {
348 } elsif ($attrib eq 'name') {
353 die "Attribute not known : $attrib.\n";
359 my ($lstat,$attrib)=@_;
360 if ($lstat and defined $attrib_name_id{$attrib})
362 my @d = split(' ', $lstat) ; # TODO : cache this
363 return from_base64($d[$attrib_name_id{$attrib}]);
370 # Base 64 functions, directly from recover.pl.
372 # Karl Hakimian <hakimian@aha.com>
373 # This section is also under GPL v2 or later.
380 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
381 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
382 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
383 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
384 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'
386 @base64_map = (0) x 128;
388 for (my $i=0; $i<64; $i++) {
389 $base64_map[ord($base64_digits[$i])] = $i;
404 if (substr($where, 0, 1) eq '-') {
406 $where = substr($where, 1);
409 while ($where ne '') {
411 my $d = substr($where, 0, 1);
412 $val += $base64_map[ord(substr($where, 0, 1))];
413 $where = substr($where, 1);
421 my @attribs = split(' ',$lstat);
422 foreach my $element (@attribs)
424 $element = from_base64($element);
431 ################################################################
438 my $conf = new Bweb::Config(config_file => $Bweb::config_file);
441 my $bvfs = new Bvfs(info => $conf);
444 my $action = CGI::param('action') || '';
446 my $args = $bvfs->get_form('pathid', 'filenameid', 'fileid',
447 'limit', 'offset', 'client');
449 my @jobid = grep { /^\d+$/ } CGI::param('jobid');
451 $bvfs->set_curjobids(@jobid);
452 $bvfs->set_limits($args->{limit}, $args->{offset});
455 print CGI::header('application/x-javascript');
457 if ($action eq 'list_client') {
459 my $q = 'SELECT Name FROM Client';
460 my $ret = $bvfs->dbh_selectall_arrayref($q);
463 print join(',', map { "['$_->[0]']" } @$ret);
467 } elsif ($action eq 'list_job') {
470 SELECT Job.EndTime, FileSet.FileSet, Job.Level, Job.JobStatus, Job.JobId
471 FROM Job,Client,FileSet
472 WHERE Job.ClientId=Client.ClientId
473 AND Client.Name = '$args->{client}'
475 AND JobStatus IN ('f', 'T')
476 AND Job.FileSetId = FileSet.FileSetId
477 ORDER BY EndTime desc";
478 my $result = $bvfs->dbh_selectall_arrayref($query);
482 print join(',', map {
483 "[$_->[4], '$_->[0] $_->[1] $_->[2] ($_->[3])']"
489 my $pathid = CGI::param('node') || 0;
490 if ($pathid =~ /^(\d+)$/) {
493 $pathid = $bvfs->get_root();
496 $bvfs->ch_dir($pathid);
498 if ($action eq 'list_files') {
500 my $files = $bvfs->ls_files();
501 # [ 1, 2, 3, "Bill", 10, '2007-01-01 00:00:00'],
502 # File.FilenameId, listfiles.id, listfiles.Name, File.LStat, File.JobId
505 map { "[$_->[1], $_->[0], $pathid, \"$_->[2]\", 10, \"2007-01-01 00:00:00\"]" }
509 } elsif ($action eq 'list_dirs') {
512 my $dirs = $bvfs->ls_dirs();
513 # return ($dirid,$dir_basename,$lstat,$jobid)
516 map { "{ 'id': '$_->[0]', 'text': '$_->[1]', 'cls':'folder'}" }
521 } elsif ($action eq 'list_versions') {
525 #($pathid,$fileid,$jobid, $fid, $mtime, $size, $inchanger, $md5, $volname);
526 my $files = $bvfs->get_all_file_versions($args->{pathid}, $args->{filenameid}, $args->{client}, 1);
528 map { "[ $_->[3], $_->[1], $_->[0], $_->[2], '$_->[8]', $_->[6], '$_->[7]', $_->[5], $_->[4] ]" }