]> git.sur5r.net Git - bacula/bacula/blob - gui/bweb/cgi/bresto.pl
ebl Add brestore ajax port.
[bacula/bacula] / gui / bweb / cgi / bresto.pl
1 #!/usr/bin/perl -w
2
3 my $bresto_enable = 0;
4 die "bresto is not enabled" if (not $bresto_enable);
5
6 =head1 LICENSE
7
8    Bweb - A Bacula web interface
9    Bacula® - The Network Backup Solution
10
11    Copyright (C) 2000-2006 Free Software Foundation Europe e.V.
12
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.
16
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.
21
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.
26
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
30    02110-1301, USA.
31
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.
36
37 =head1 VERSION
38
39     $Id$
40
41 =cut
42
43 use Bweb;
44
45 package Bvfs;
46 use base qw/Bweb/;
47
48 sub get_root
49 {
50     my ($self, $dir) = @_;
51     return $self->get_pathid('');
52 }
53
54 sub ch_dir
55 {
56     my ($self, $pathid) = @_;
57     $self->{cwdid} = $pathid;
58 }
59
60 sub up_dir
61 {
62     my ($self) = @_ ;
63     my $query = "
64   SELECT PPathId 
65     FROM brestore_pathhierarchy 
66    WHERE PathId IN ($self->{cwdid}) ";
67
68     my $all = $self->dbh_selectall_arrayref($query);
69     return unless ($all);       # already at root
70
71     my $dir = join(',', map { $_->[0] } @$all);
72     if ($dir) {
73         $self->ch_dir($dir);
74     }
75 }
76
77 sub pwd
78 {
79     my ($self) = @_;
80     return $self->get_path($self->{cwdid});
81 }
82
83 sub get_path
84 {
85     my ($self, $pathid) = @_;
86     $self->debug("Call with pathid = $pathid");
87     my $query = 
88         "SELECT Path FROM Path WHERE PathId IN (?)";
89
90     my $sth = $self->dbh_prepare($query);
91     $sth->execute($pathid);
92     my $result = $sth->fetchrow_arrayref();
93     $sth->finish();
94     return $result->[0];    
95 }
96
97 sub set_curjobids
98 {
99     my ($self, @jobids) = @_;
100     $self->{curjobids} = join(',', @jobids);
101 #    $self->update_brestore_table(@jobids);
102 }
103
104 sub get_pathid
105 {
106     my ($self, $dir) = @_;
107     my $query = 
108         "SELECT PathId FROM Path WHERE Path = ?";
109     my $sth = $self->dbh_prepare($query);
110     $sth->execute($dir);
111     my $result = $sth->fetchall_arrayref();
112     $sth->finish();
113     
114     return join(',', map { $_->[0] } @$result);
115 }
116
117 sub set_limits
118 {
119     my ($self, $offset, $limit) = @_;
120     $self->{limit}  = $limit  || 100;
121     $self->{offset} = $offset || 0;
122 }
123
124 sub ls_files
125 {
126     my ($self) = @_;
127
128     return undef unless ($self->{curjobids});
129
130     my $inclause   = $self->{curjobids};
131     my $inlistpath = $self->{cwdid};
132
133     my $query = 
134 "SELECT File.FilenameId, listfiles.id, listfiles.Name, File.LStat, File.JobId
135  FROM
136         (SELECT Filename.Name, max(File.FileId) as id
137          FROM File, Filename
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,
144 File
145 WHERE File.FileId = listfiles.id";
146         
147     $self->debug($query);
148     my $result = $self->dbh_selectall_arrayref($query);
149     $self->debug($result);
150         
151     return $result;
152 }
153
154
155 # return ($dirid,$dir_basename,$lstat,$jobid)
156 sub ls_dirs
157 {
158     my ($self) = @_;
159
160     return undef unless ($self->{curjobids});
161
162     my $pathid = $self->{cwdid};
163     my $jobclause = $self->{curjobids};
164
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);
169     $sth->execute();
170     my $result = $sth->fetchrow_arrayref();
171     $sth->finish();
172     my $dir_filenameid = $result->[0];
173      
174     # Then we get all the dir entries from File ...
175     $query = "
176 SELECT PathId, Path, JobId, Lstat FROM (
177     
178     SELECT Path1.PathId, Path1.Path, lower(Path1.Path),
179            listfile1.JobId, listfile1.Lstat
180     FROM (
181         SELECT DISTINCT brestore_pathhierarchy1.PathId
182         FROM brestore_pathhierarchy AS brestore_pathhierarchy1
183         JOIN Path AS Path2
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)
190     LEFT JOIN (
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
196 ";
197     $self->debug($query);
198     $sth=$self->dbh_prepare($query);
199     $sth->execute();
200     $result = $sth->fetchall_arrayref();
201     my @return_list;
202     my $prev_dir='';
203     foreach my $refrow (@{$result})
204     {
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'
211         my $return_value;
212         if ($dir ne '/')
213         {
214             my @temp = split ('/',$dir);
215             $return_value = pop @temp;
216         }
217         else
218         {
219             $return_value = '/';
220         }
221         my @return_array = ($dirid,$return_value,$lstat,$jobid);
222         push @return_list,(\@return_array);
223         $prev_dir = $dirid;
224     }
225     $self->debug(\@return_list);
226     return \@return_list;    
227 }
228
229
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
235 {
236     my ($self,$pathid,$fileid,$client,$see_all)=@_;
237     
238     defined $see_all or $see_all=0;
239     
240     my @versions;
241     my $query;
242     $query = 
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'";
255
256     $self->debug($query);
257         
258     my $result = $self->dbh_selectall_arrayref($query);
259         
260     foreach my $refrow (@$result)
261     {
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);
266                 
267         my @list = ($pathid,$fileid,$jobid,
268                     $fid, $mtime, $size, $inchanger,
269                     $md5, $volname);
270         push @versions, (\@list);
271     }
272         
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;
281
282         
283     my @good_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)
288     {   
289         if ($ref->[7])
290         {
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
294             # criterion
295             
296             # If we allready have a (better) version
297             next if ( (not $see_all) 
298                       and $allready_seen_by_md5{$ref->[7] .'-'. $ref->[5]}); 
299
300             # we never met this one before...
301             $allready_seen_by_md5{$ref->[7] .'-'. $ref->[5]}=1;
302         }
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;
308         
309         # We reached there. The file hasn't been seen.
310         push @good_versions,($ref);
311     }
312         
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;
317         
318     return \@good_versions;
319 }
320 {
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);;
327     sub array_attrib
328     {
329         my ($attrib,$ref_attrib)=@_;
330         return $ref_attrib->[$attrib_name_id{$attrib}];
331     }
332         
333     sub file_attrib
334     {   # $file = [filenameid,listfiles.id,listfiles.Name, File.LStat, File.JobId]
335
336         my ($file, $attrib)=@_;
337         
338         if (defined $attrib_name_id{$attrib}) {
339
340             my @d = split(' ', $file->[3]) ; # TODO : cache this
341             
342             return from_base64($d[$attrib_name_id{$attrib}]);
343
344         } elsif ($attrib eq 'jobid') {
345
346             return $file->[4];
347
348         } elsif ($attrib eq 'name') {
349
350             return $file->[2];
351             
352         } else  {
353             die "Attribute not known : $attrib.\n";
354         }
355     }
356     
357     sub lstat_attrib
358     {
359         my ($lstat,$attrib)=@_;
360         if ($lstat and defined $attrib_name_id{$attrib}) 
361         {
362             my @d = split(' ', $lstat) ; # TODO : cache this
363             return from_base64($d[$attrib_name_id{$attrib}]);
364         }
365         return 0;
366     }
367 }
368
369 {
370     # Base 64 functions, directly from recover.pl.
371     # Thanks to
372     # Karl Hakimian <hakimian@aha.com>
373     # This section is also under GPL v2 or later.
374     my @base64_digits;
375     my @base64_map;
376     my $is_init=0;
377     sub init_base64
378     {
379         @base64_digits = (
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', '+', '/'
385                           );
386         @base64_map = (0) x 128;
387         
388         for (my $i=0; $i<64; $i++) {
389             $base64_map[ord($base64_digits[$i])] = $i;
390         }
391         $is_init = 1;
392     }
393
394     sub from_base64 {
395         if(not $is_init)
396         {
397             init_base64();
398         }
399         my $where = shift;
400         my $val = 0;
401         my $i = 0;
402         my $neg = 0;
403         
404         if (substr($where, 0, 1) eq '-') {
405             $neg = 1;
406             $where = substr($where, 1);
407         }
408         
409         while ($where ne '') {
410             $val *= 64;
411             my $d = substr($where, 0, 1);
412             $val += $base64_map[ord(substr($where, 0, 1))];
413             $where = substr($where, 1);
414         }
415         
416         return $val;
417     }
418
419     sub parse_lstat {
420         my ($lstat)=@_;
421         my @attribs = split(' ',$lstat);
422         foreach my $element (@attribs)
423         {
424             $element = from_base64($element);
425         }
426         return @attribs;
427     }
428 }
429
430
431 ################################################################
432
433
434 package main;
435 use strict;
436 use Bweb;
437
438 my $conf = new Bweb::Config(config_file => $Bweb::config_file);
439 $conf->load();
440
441 my $bvfs = new Bvfs(info => $conf);
442 $bvfs->connect_db();
443
444 my $action = CGI::param('action') || '';
445
446 my $args = $bvfs->get_form('pathid', 'filenameid', 'fileid',
447                            'limit', 'offset', 'client');
448
449 my @jobid = grep { /^\d+$/ } CGI::param('jobid');
450
451 $bvfs->set_curjobids(@jobid);
452 $bvfs->set_limits($args->{limit}, $args->{offset});
453 #$bvfs->{debug}=1;
454
455 print CGI::header('application/x-javascript');
456
457 if ($action eq 'list_client') {
458
459   my $q = 'SELECT Name FROM Client';
460   my $ret = $bvfs->dbh_selectall_arrayref($q);
461
462   print "[";
463   print join(',', map { "['$_->[0]']" } @$ret);
464   print "]\n";
465   exit 0;
466
467 } elsif ($action eq 'list_job') {
468
469     my $query = "
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}'
474   AND Job.Type = 'B'
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);
479
480     print "[";
481
482     print join(',', map {
483       "[$_->[4], '$_->[0] $_->[1] $_->[2] ($_->[3])']"
484       } @$result);
485
486     print "]\n";
487 }
488
489 my $pathid = CGI::param('node') || 0;
490 if ($pathid =~ /^(\d+)$/) {
491     $pathid = $1;
492 } else {
493     $pathid = $bvfs->get_root();
494 }
495
496 $bvfs->ch_dir($pathid);
497
498 if ($action eq 'list_files') {
499     print "[";
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 
503
504     print join(',',
505                map { "[$_->[1], $_->[0], $pathid, \"$_->[2]\", 10, \"2007-01-01 00:00:00\"]" }
506                @$files);
507     print "]\n";
508
509 } elsif ($action eq 'list_dirs') {
510
511     print "[";
512     my $dirs = $bvfs->ls_dirs();
513         # return ($dirid,$dir_basename,$lstat,$jobid)
514
515     print join(',', 
516                map { "{ 'id': '$_->[0]', 'text': '$_->[1]', 'cls':'folder'}" }
517                @$dirs);
518
519     print "]\n";
520
521 } elsif ($action eq 'list_versions') {
522
523     print "[";
524     #   0       1       2        3   4       5      6           7      8
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);
527     print join(',', 
528                map { "[ $_->[3], $_->[1], $_->[0], $_->[2], '$_->[8]', $_->[6], '$_->[7]', $_->[5], $_->[4] ]" }
529                @$files);
530     print "]\n";
531
532 }
533
534
535