]> git.sur5r.net Git - bacula/bacula/blob - gui/bweb/cgi/bresto.pl
ebl Sql optimizations
[bacula/bacula] / gui / bweb / cgi / bresto.pl
1 #!/usr/bin/perl -w
2
3 my $bresto_enable = 1;
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) = @_;
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 update_cache
125 {
126     my ($self) = @_;
127
128     $self->{dbh}->begin_work();
129
130     my $query = "
131   SELECT JobId from Job 
132    WHERE JobId NOT IN (SELECT JobId FROM brestore_knownjobid) AND JobStatus IN ('T', 'f', 'A') ORDER BY JobId";
133     my $jobs = $self->dbh_selectall_arrayref($query);
134
135     $self->update_brestore_table(map { $_->[0] } @$jobs);
136
137     print STDERR "Cleaning path visibility\n";
138     
139     my $nb = $self->dbh_do("
140   DELETE FROM brestore_pathvisibility
141       WHERE NOT EXISTS 
142    (SELECT 1 FROM Job WHERE JobId=brestore_pathvisibility.JobId)");
143
144     print STDERR "$nb rows affected\n";
145     print STDERR "Cleaning known jobid\n";
146
147     $nb = $self->dbh_do("
148   DELETE FROM brestore_knownjobid
149       WHERE NOT EXISTS 
150    (SELECT 1 FROM Job WHERE JobId=brestore_knownjobid.JobId)");
151
152     print STDERR "$nb rows affected\n";
153
154     $self->{dbh}->commit();
155 }
156
157 sub update_brestore_table
158 {
159     my ($self, @jobs) = @_;
160
161     $self->debug(\@jobs);
162
163     foreach my $job (sort {$a <=> $b} @jobs)
164     {
165         my $query = "SELECT 1 FROM brestore_knownjobid WHERE JobId = $job";
166         my $retour = $self->dbh_selectrow_arrayref($query);
167         next if ($retour and ($retour->[0] == 1)); # We have allready done this one ...
168
169         print STDERR "Inserting path records for JobId $job\n";
170         $query = "INSERT INTO brestore_pathvisibility (PathId, JobId) 
171                    (SELECT DISTINCT PathId, JobId FROM File WHERE JobId = $job)";
172
173         $self->dbh_do($query);
174
175         # Now we have to do the directory recursion stuff to determine missing visibility
176         # We try to avoid recursion, to be as fast as possible
177         # We also only work on not allready hierarchised directories...
178
179         print STDERR "Creating missing recursion paths for $job\n";
180
181         $query = "SELECT brestore_pathvisibility.PathId, Path FROM brestore_pathvisibility 
182                   JOIN Path ON( brestore_pathvisibility.PathId = Path.PathId)
183                   LEFT JOIN brestore_pathhierarchy ON (brestore_pathvisibility.PathId = brestore_pathhierarchy.PathId)
184                   WHERE brestore_pathvisibility.JobId = $job
185                   AND brestore_pathhierarchy.PathId IS NULL
186                   ORDER BY Path";
187
188         my $sth = $self->dbh_prepare($query);
189         $sth->execute();
190         my $pathid; my $path;
191         $sth->bind_columns(\$pathid,\$path);
192         
193         while ($sth->fetch)
194         {
195             $self->build_path_hierarchy($path,$pathid);
196         }
197         $sth->finish();
198
199         # Great. We have calculated all dependancies. We can use them to add the missing pathids ...
200         # This query gives all parent pathids for a given jobid that aren't stored.
201         # It has to be called until no record is updated ...
202         $query = "
203         INSERT INTO brestore_pathvisibility (PathId, JobId) (
204         SELECT a.PathId,$job
205         FROM
206                 (SELECT DISTINCT h.PPathId AS PathId
207                 FROM brestore_pathhierarchy AS h
208                 JOIN  brestore_pathvisibility AS p ON (h.PathId=p.PathId)
209                 WHERE p.JobId=$job) AS a
210                 LEFT JOIN
211                 (SELECT PathId
212                 FROM brestore_pathvisibility
213                 WHERE JobId=$job) AS b
214                 ON (a.PathId = b.PathId)
215         WHERE b.PathId IS NULL)";
216
217         my $rows_affected;
218         while (($rows_affected = $self->dbh_do($query)) and ($rows_affected !~ /^0/))
219         {
220             print STDERR "Recursively adding $rows_affected records from $job\n";
221         }
222         # Job's done
223         $query = "INSERT INTO brestore_knownjobid (JobId) VALUES ($job)";
224         $self->dbh_do($query);
225     }
226 }
227
228 sub parent_dir
229 {
230     my ($path) = @_;
231     # Root Unix case :
232     if ($path eq '/')
233     {
234         return '';
235     }
236     # Root Windows case :
237     if ($path =~ /^[a-z]+:\/$/i)
238     {
239         return '';
240     }
241     # Split
242     my @tmp = split('/',$path);
243     # We remove the last ...
244     pop @tmp;
245     my $tmp = join ('/',@tmp) . '/';
246     return $tmp;
247 }
248
249 sub build_path_hierarchy
250 {
251     my ($self, $path,$pathid)=@_;
252     # Does the ppathid exist for this ? we use a memory cache...
253     # In order to avoid the full loop, we consider that if a dir is allready in the
254     # brestore_pathhierarchy table, then there is no need to calculate all the hierarchy
255     while ($path ne '')
256     {
257         if (! $self->{cache_ppathid}->{$pathid})
258         {
259             my $query = "SELECT PPathId FROM brestore_pathhierarchy WHERE PathId = ?";
260             my $sth2 = $self->{dbh}->prepare_cached($query);
261             $sth2->execute($pathid);
262             # Do we have a result ?
263             if (my $refrow = $sth2->fetchrow_arrayref)
264             {
265                 $self->{cache_ppathid}->{$pathid}=$refrow->[0];
266                 $sth2->finish();
267                 # This dir was in the db ...
268                 # It means we can leave, the tree has allready been built for
269                 # this dir
270                 return 1;
271             } else {
272                 $sth2->finish();
273                 # We have to create the record ...
274                 # What's the current p_path ?
275                 my $ppath = parent_dir($path);
276                 my $ppathid = $self->return_pathid_from_path($ppath);
277                 $self->{cache_ppathid}->{$pathid}= $ppathid;
278                 
279                 $query = "INSERT INTO brestore_pathhierarchy (pathid, ppathid) VALUES (?,?)";
280                 $sth2 = $self->{dbh}->prepare_cached($query);
281                 $sth2->execute($pathid,$ppathid);
282                 $sth2->finish();
283                 $path = $ppath;
284                 $pathid = $ppathid;
285             }
286         } else {
287            # It's allready in the cache.
288            # We can leave, no time to waste here, all the parent dirs have allready
289            # been done
290            return 1;
291         }
292     }
293     return 1;
294 }
295
296 sub return_pathid_from_path
297 {
298     my ($self, $path) = @_;
299     my $query = "SELECT PathId FROM Path WHERE Path = ?";
300
301     #print STDERR $query,"\n" if $debug;
302     my $sth = $self->{dbh}->prepare_cached($query);
303     $sth->execute($path);
304     my $result =$sth->fetchrow_arrayref();
305     $sth->finish();
306     if (defined $result)
307     {
308         return $result->[0];
309
310     } else {
311         # A bit dirty : we insert into path, and we have to be sure
312         # we aren't deleted by a purge. We still need to insert into path to get
313         # the pathid, because of mysql
314         $query = "INSERT INTO Path (Path) VALUES (?)";
315         #print STDERR $query,"\n" if $debug;
316         $sth = $self->{dbh}->prepare_cached($query);
317         $sth->execute($path);
318         $sth->finish();
319         
320         $query = "SELECT PathId FROM Path WHERE Path = ?";
321         #print STDERR $query,"\n" if $debug;
322         $sth = $self->{dbh}->prepare_cached($query);
323         $sth->execute($path);
324         $result = $sth->fetchrow_arrayref();
325         $sth->finish();
326         return $result->[0];
327     }
328 }
329
330 sub ls_files
331 {
332     my ($self) = @_;
333
334     return undef unless ($self->{curjobids});
335
336     my $inclause   = $self->{curjobids};
337     my $inlistpath = $self->{cwdid};
338
339     my $query = 
340 "SELECT File.FilenameId, listfiles.id, listfiles.Name, File.LStat, File.JobId
341  FROM
342         (SELECT Filename.Name, max(File.FileId) as id
343          FROM File, Filename
344          WHERE File.FilenameId = Filename.FilenameId
345            AND Filename.Name != ''
346            AND File.PathId IN ($inlistpath)
347            AND File.JobId IN ($inclause)
348          GROUP BY Filename.Name
349          ORDER BY Filename.Name) AS listfiles,
350 File
351 WHERE File.FileId = listfiles.id";
352         
353     $self->debug($query);
354     my $result = $self->dbh_selectall_arrayref($query);
355     $self->debug($result);
356         
357     return $result;
358 }
359
360
361 # return ($dirid,$dir_basename,$lstat,$jobid)
362 sub ls_dirs
363 {
364     my ($self) = @_;
365
366     return undef unless ($self->{curjobids});
367
368     my $pathid = $self->{cwdid};
369     my $jobclause = $self->{curjobids};
370
371     # Let's retrieve the list of the visible dirs in this dir ...
372     # First, I need the empty filenameid to locate efficiently the dirs in the file table
373     my $query = "SELECT FilenameId FROM Filename WHERE Name = ''";
374     my $sth = $self->dbh_prepare($query);
375     $sth->execute();
376     my $result = $sth->fetchrow_arrayref();
377     $sth->finish();
378     my $dir_filenameid = $result->[0];
379      
380     # Then we get all the dir entries from File ...
381     $query = "
382 SELECT PathId, Path, JobId, Lstat FROM (
383     
384     SELECT Path1.PathId, Path1.Path, lower(Path1.Path),
385            listfile1.JobId, listfile1.Lstat
386     FROM (
387         SELECT DISTINCT brestore_pathhierarchy1.PathId
388         FROM brestore_pathhierarchy AS brestore_pathhierarchy1
389         JOIN Path AS Path2
390             ON (brestore_pathhierarchy1.PathId = Path2.PathId)
391         JOIN brestore_pathvisibility AS brestore_pathvisibility1
392             ON (brestore_pathhierarchy1.PathId = brestore_pathvisibility1.PathId)
393         WHERE brestore_pathhierarchy1.PPathId = $pathid
394         AND brestore_pathvisibility1.jobid IN ($jobclause)) AS listpath1
395     JOIN Path AS Path1 ON (listpath1.PathId = Path1.PathId)
396     LEFT JOIN (
397         SELECT File1.PathId, File1.JobId, File1.Lstat FROM File AS File1
398         WHERE File1.FilenameId = $dir_filenameid
399         AND File1.JobId IN ($jobclause)) AS listfile1
400         ON (listpath1.PathId = listfile1.PathId)
401      ) AS A ORDER BY 2,3 DESC
402 ";
403     $self->debug($query);
404     $sth=$self->dbh_prepare($query);
405     $sth->execute();
406     $result = $sth->fetchall_arrayref();
407     my @return_list;
408     my $prev_dir='';
409     foreach my $refrow (@{$result})
410     {
411         my $dirid = $refrow->[0];
412         my $dir = $refrow->[1];
413         my $lstat = $refrow->[3];
414         my $jobid = $refrow->[2] || 0;
415         next if ($dirid eq $prev_dir);
416         # We have to clean up this dirname ... we only want it's 'basename'
417         my $return_value;
418         if ($dir ne '/')
419         {
420             my @temp = split ('/',$dir);
421             $return_value = pop @temp;
422         }
423         else
424         {
425             $return_value = '/';
426         }
427         my @return_array = ($dirid,$return_value,$lstat,$jobid);
428         push @return_list,(\@return_array);
429         $prev_dir = $dirid;
430     }
431     $self->debug(\@return_list);
432     return \@return_list;    
433 }
434
435 # TODO : we want be able to restore files from a bad ended backup
436 # we have JobStatus IN ('T', 'A', 'E') and we must 
437
438 # Data acces subs from here. Interaction with SGBD and caching
439
440 # This sub retrieves the list of jobs corresponding to the jobs selected in the
441 # GUI and stores them in @CurrentJobIds.
442 # date must be quoted
443 sub set_job_ids_for_date
444 {
445     my ($self, $client, $date)=@_;
446
447     if (!$client or !$date) {
448         return ();
449     }
450         
451     # The algorithm : for a client, we get all the backups for each
452     # fileset, in reverse order Then, for each fileset, we store the 'good'
453     # incrementals and differentials until we have found a full so it goes
454     # like this : store all incrementals until we have found a differential
455     # or a full, then find the full #
456
457     my $query = "SELECT JobId, FileSet, Level, JobStatus
458                 FROM Job, Client, FileSet
459                 WHERE Job.ClientId = Client.ClientId
460                 AND FileSet.FileSetId = Job.FileSetId
461                 AND EndTime <= $date
462                 AND Client.Name = '$client'
463                 AND Type IN ('B')
464                 AND JobStatus IN ('T')
465                 ORDER BY FileSet, JobTDate DESC";
466     
467     my @CurrentJobIds;
468     my $result = $self->dbh_selectall_arrayref($query);
469     my %progress;
470     foreach my $refrow (@$result)
471     {
472         my $jobid = $refrow->[0];
473         my $fileset = $refrow->[1];
474         my $level = $refrow->[2];
475                 
476         defined $progress{$fileset} or $progress{$fileset}='U'; # U for unknown
477                 
478         next if $progress{$fileset} eq 'F'; # It's over for this fileset...
479                 
480         if ($level eq 'I')
481         {
482             next unless ($progress{$fileset} eq 'U' or $progress{$fileset} eq 'I');
483             push @CurrentJobIds,($jobid);
484         }
485         elsif ($level eq 'D')
486         {
487             next if $progress{$fileset} eq 'D'; # We allready have a differential
488             push @CurrentJobIds,($jobid);
489         }
490         elsif ($level eq 'F')
491         {
492             push @CurrentJobIds,($jobid);
493         }
494
495         my $status = $refrow->[3] ;
496         if ($status eq 'T') {              # good end of job
497             $progress{$fileset} = $level;
498         }
499     }
500
501     return @CurrentJobIds;
502 }
503
504 sub dbh_selectrow_arrayref
505 {
506     my ($self, $query) = @_;
507     $self->debug($query, up => 1);
508     return $self->{dbh}->selectrow_arrayref($query);
509 }
510
511 # Returns list of versions of a file that could be restored
512 # returns an array of 
513 # (jobid,fileindex,mtime,size,inchanger,md5,volname,fileid)
514 # there will be only one jobid in the array of jobids...
515 sub get_all_file_versions
516 {
517     my ($self,$pathid,$fileid,$client,$see_all)=@_;
518     
519     defined $see_all or $see_all=0;
520     
521     my @versions;
522     my $query;
523     $query = 
524 "SELECT File.JobId, File.FileId, File.Lstat, 
525         File.Md5, Media.VolumeName, Media.InChanger
526  FROM File, Job, Client, JobMedia, Media
527  WHERE File.FilenameId = $fileid
528    AND File.PathId=$pathid
529    AND File.JobId = Job.JobId
530    AND Job.ClientId = Client.ClientId
531    AND Job.JobId = JobMedia.JobId
532    AND File.FileIndex >= JobMedia.FirstIndex
533    AND File.FileIndex <= JobMedia.LastIndex
534    AND JobMedia.MediaId = Media.MediaId
535    AND Client.Name = '$client'";
536
537     $self->debug($query);
538         
539     my $result = $self->dbh_selectall_arrayref($query);
540         
541     foreach my $refrow (@$result)
542     {
543         my ($jobid, $fid, $lstat, $md5, $volname, $inchanger) = @$refrow;
544         my @attribs = parse_lstat($lstat);
545         my $mtime = array_attrib('st_mtime',\@attribs);
546         my $size = array_attrib('st_size',\@attribs);
547                 
548         my @list = ($pathid,$fileid,$jobid,
549                     $fid, $mtime, $size, $inchanger,
550                     $md5, $volname);
551         push @versions, (\@list);
552     }
553         
554     # We have the list of all versions of this file.
555     # We'll sort it by mtime desc, size, md5, inchanger desc
556     # the rest of the algorithm will be simpler
557     # ('FILE:',filename,jobid,fileindex,mtime,size,inchanger,md5,volname)
558     @versions = sort { $b->[4] <=> $a->[4] 
559                     || $a->[5] <=> $b->[5] 
560                     || $a->[7] cmp $a->[7] 
561                     || $b->[6] <=> $a->[6]} @versions;
562
563         
564     my @good_versions;
565     my %allready_seen_by_mtime;
566     my %allready_seen_by_md5;
567     # Now we should create a new array with only the interesting records
568     foreach my $ref (@versions)
569     {   
570         if ($ref->[7])
571         {
572             # The file has a md5. We compare his md5 to other known md5...
573             # We take size into account. It may happen that 2 files
574             # have the same md5sum and are different. size is a supplementary
575             # criterion
576             
577             # If we allready have a (better) version
578             next if ( (not $see_all) 
579                       and $allready_seen_by_md5{$ref->[7] .'-'. $ref->[5]}); 
580
581             # we never met this one before...
582             $allready_seen_by_md5{$ref->[7] .'-'. $ref->[5]}=1;
583         }
584         # Even if it has a md5, we should also work with mtimes
585         # We allready have a (better) version
586         next if ( (not $see_all)
587                   and $allready_seen_by_mtime{$ref->[4] .'-'. $ref->[5]}); 
588         $allready_seen_by_mtime{$ref->[4] .'-'. $ref->[5] . '-' . $ref->[7]}=1;
589         
590         # We reached there. The file hasn't been seen.
591         push @good_versions,($ref);
592     }
593         
594     # To be nice with the user, we re-sort good_versions by
595     # inchanger desc, mtime desc
596     @good_versions = sort { $b->[4] <=> $a->[4] 
597                          || $b->[2] <=> $a->[2]} @good_versions;
598         
599     return \@good_versions;
600 }
601 {
602     my %attrib_name_id = ( 'st_dev' => 0,'st_ino' => 1,'st_mode' => 2,
603                           'st_nlink' => 3,'st_uid' => 4,'st_gid' => 5,
604                           'st_rdev' => 6,'st_size' => 7,'st_blksize' => 8,
605                           'st_blocks' => 9,'st_atime' => 10,'st_mtime' => 11,
606                           'st_ctime' => 12,'LinkFI' => 13,'st_flags' => 14,
607                           'data_stream' => 15);;
608     sub array_attrib
609     {
610         my ($attrib,$ref_attrib)=@_;
611         return $ref_attrib->[$attrib_name_id{$attrib}];
612     }
613         
614     sub file_attrib
615     {   # $file = [filenameid,listfiles.id,listfiles.Name, File.LStat, File.JobId]
616
617         my ($file, $attrib)=@_;
618         
619         if (defined $attrib_name_id{$attrib}) {
620
621             my @d = split(' ', $file->[3]) ; # TODO : cache this
622             
623             return from_base64($d[$attrib_name_id{$attrib}]);
624
625         } elsif ($attrib eq 'jobid') {
626
627             return $file->[4];
628
629         } elsif ($attrib eq 'name') {
630
631             return $file->[2];
632             
633         } else  {
634             die "Attribute not known : $attrib.\n";
635         }
636     }
637     
638     sub lstat_attrib
639     {
640         my ($lstat,$attrib)=@_;
641         if ($lstat and defined $attrib_name_id{$attrib}) 
642         {
643             my @d = split(' ', $lstat) ; # TODO : cache this
644             return from_base64($d[$attrib_name_id{$attrib}]);
645         }
646         return 0;
647     }
648 }
649
650 {
651     # Base 64 functions, directly from recover.pl.
652     # Thanks to
653     # Karl Hakimian <hakimian@aha.com>
654     # This section is also under GPL v2 or later.
655     my @base64_digits;
656     my @base64_map;
657     my $is_init=0;
658     sub init_base64
659     {
660         @base64_digits = (
661         'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
662         'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
663         'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
664         'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
665         '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'
666                           );
667         @base64_map = (0) x 128;
668         
669         for (my $i=0; $i<64; $i++) {
670             $base64_map[ord($base64_digits[$i])] = $i;
671         }
672         $is_init = 1;
673     }
674
675     sub from_base64 {
676         if(not $is_init)
677         {
678             init_base64();
679         }
680         my $where = shift;
681         my $val = 0;
682         my $i = 0;
683         my $neg = 0;
684         
685         if (substr($where, 0, 1) eq '-') {
686             $neg = 1;
687             $where = substr($where, 1);
688         }
689         
690         while ($where ne '') {
691             $val *= 64;
692             my $d = substr($where, 0, 1);
693             $val += $base64_map[ord(substr($where, 0, 1))];
694             $where = substr($where, 1);
695         }
696         
697         return $val;
698     }
699
700     sub parse_lstat {
701         my ($lstat)=@_;
702         my @attribs = split(' ',$lstat);
703         foreach my $element (@attribs)
704         {
705             $element = from_base64($element);
706         }
707         return @attribs;
708     }
709 }
710
711
712 ################################################################
713
714
715 package main;
716 use strict;
717 use Bweb;
718
719 my $conf = new Bweb::Config(config_file => $Bweb::config_file);
720 $conf->load();
721
722 my $bvfs = new Bvfs(info => $conf);
723 $bvfs->connect_db();
724
725 my $action = CGI::param('action') || '';
726
727 my $args = $bvfs->get_form('pathid', 'filenameid', 'fileid', 'qdate',
728                            'limit', 'offset', 'client');
729
730 my @jobid = grep { /^\d+$/ } CGI::param('jobid');
731
732 if (!scalar(@jobid) and $args->{qdate} and $args->{client}) {
733     @jobid = $bvfs->set_job_ids_for_date($args->{client}, $args->{qdate});    
734 }
735
736 $bvfs->set_curjobids(@jobid);
737
738 $bvfs->set_limits($args->{limit}, $args->{offset});
739
740 print CGI::header('application/x-javascript');
741
742 if ($action eq 'list_client') {
743
744   my $q = 'SELECT Name FROM Client';
745   my $ret = $bvfs->dbh_selectall_arrayref($q);
746
747   print "[";
748   print join(',', map { "['$_->[0]']" } @$ret);
749   print "]\n";
750   exit 0;
751
752 } elsif ($action eq 'list_job') {
753
754     my $query = "
755  SELECT Job.EndTime, FileSet.FileSet, Job.Level, Job.JobStatus, Job.JobId
756   FROM Job,Client,FileSet
757   WHERE Job.ClientId=Client.ClientId
758   AND Client.Name = '$args->{client}'
759   AND Job.Type = 'B'
760   AND JobStatus IN ('f', 'T')
761   AND Job.FileSetId = FileSet.FileSetId
762   ORDER BY EndTime desc";
763     my $result = $bvfs->dbh_selectall_arrayref($query);
764
765     print "[";
766
767     print join(',', map {
768       "[$_->[4], '$_->[0]', '$_->[0] $_->[1] $_->[2] ($_->[3])']"
769       } @$result);
770
771     print "]\n";
772 }
773
774 if (CGI::param('init')) {
775     $bvfs->update_brestore_table(@jobid);
776 }
777
778 my $pathid = CGI::param('node') || '';
779 my $path = CGI::param('path');
780
781 if ($pathid =~ /^(\d+)$/) {
782     $pathid = $1;
783 } elsif ($path) {
784     $pathid = $bvfs->get_pathid($path);
785 } else {
786     $pathid = $bvfs->get_root();
787 }
788
789 $bvfs->ch_dir($pathid);
790
791 if ($action eq 'list_files') {
792     print "[";
793     my $files = $bvfs->ls_files();
794 #       [ 1, 2, 3, "Bill",  10, '2007-01-01 00:00:00'], 
795 #   File.FilenameId, listfiles.id, listfiles.Name, File.LStat, File.JobId 
796
797     print join(',',
798                map { "[$_->[1], $_->[0], $pathid, \"$_->[2]\", 10, \"2007-01-01 00:00:00\"]" }
799                @$files);
800     print "]\n";
801
802 } elsif ($action eq 'list_dirs') {
803
804     print "[";
805     my $dirs = $bvfs->ls_dirs();
806         # return ($dirid,$dir_basename,$lstat,$jobid)
807
808     print join(',', 
809                map { "{ 'id': '$_->[0]', 'text': '$_->[1]', 'cls':'folder'}" }
810                @$dirs);
811
812     print "]\n";
813
814 } elsif ($action eq 'list_versions') {
815
816     my $vafv = CGI::param('vafv') || 'false'; # view all file versions
817     $vafv = ($vafv eq 'false')?0:1;
818
819     print "[";
820     #   0       1       2        3   4       5      6           7      8
821     #($pathid,$fileid,$jobid, $fid, $mtime, $size, $inchanger, $md5, $volname);
822     my $files = $bvfs->get_all_file_versions($args->{pathid}, $args->{filenameid}, $args->{client}, $vafv);
823     print join(',', 
824                map { "[ $_->[3], $_->[1], $_->[0], $_->[2], '$_->[8]', $_->[6], '$_->[7]', $_->[5], $_->[4] ]" }
825                @$files);
826     print "]\n";
827
828 } elsif ($action eq 'get_media') {
829
830     my $jobid = join(',', @jobid);
831     my $fileid = join(',', grep { /^\d+$/ } CGI::param('fileid'));
832
833 my $q="
834  SELECT DISTINCT VolumeName, InChanger
835    FROM File, 
836     ( -- Get all media from this job
837       SELECT MAX(FirstIndex) AS FirstIndex, MIN(LastIndex) AS LastIndex,
838              VolumeName, Inchanger
839         FROM JobMedia JOIN Media USING (MediaId)
840        WHERE JobId IN ($jobid)
841        GROUP BY VolumeName, InChanger
842     ) AS allmedia
843   WHERE File.FileId IN ($fileid)
844     AND File.FileIndex >= allmedia.FirstIndex 
845     AND File.FileIndex <= allmedia.LastIndex;
846 ";
847     my $lst = $bvfs->dbh_selectall_arrayref($q);
848     print "[";
849     print join(',', map { "[ '$_->[0]', $_->[1]]" } @$lst);
850     print "]\n";
851
852 } elsif ($action eq 'list_storage') { # TODO: use .storage hier
853     my $q="SELECT Name FROM Storage";
854     my $lst = $bvfs->dbh_selectall_arrayref($q);
855     print "[";
856     print join(',', map { "[ '$_->[0]' ]" } @$lst);
857     print "]\n";
858
859 } elsif ($action eq 'restore') {
860     my $fileid = join(',', grep { /^\d+$/ } CGI::param('fileid'));
861     my @dirid = grep { /^\d+$/ } CGI::param('dirid');
862     my $inclause = join(',', @jobid);
863
864     my @union;
865
866     if ($fileid) {
867       push @union, "(SELECT JobId, FileIndex FROM File WHERE FileId IN ($fileid))";
868     }
869
870     # using this is not good because the sql engine doesn't know 
871     # what LIKE will use. It will be better to get Path% in perl
872     # but it doesn't work with accents... :(
873     foreach my $dirid (@dirid) {
874       push @union, "
875   (SELECT File.JobId, File.FileIndex, File.FilenameId, File.PathId
876     FROM Path JOIN File USING (PathId)
877    WHERE Path.Path LIKE 
878         (SELECT ". $bvfs->dbh_strcat('Path',"'\%'") ." FROM Path
879           WHERE PathId = $dirid
880         )
881      AND File.JobId IN ($inclause))";
882     }
883
884     my $u = join(" UNION ", @union);
885
886     $bvfs->dbh_do("CREATE TEMP TABLE btemp AS ($u)");
887     # TODO: remove FilenameId et PathId
888     $bvfs->dbh_do("CREATE TABLE b2$$ AS (
889 SELECT btemp.JobId, btemp.FileIndex, btemp.FilenameId, btemp.PathId
890   FROM btemp,
891        (SELECT max(JobId) as JobId, PathId, FilenameId
892           FROM btemp
893       GROUP BY PathId, FilenameId
894       ORDER BY JobId DESC) AS a
895   WHERE a.JobId = btemp.JobId
896     AND a.PathId= btemp.PathId
897     AND a.FilenameId = btemp.FilenameId
898 )");
899
900     print "b2$$";
901 }
902
903
904 __END__ 
905
906 CREATE VIEW files AS 
907  SELECT path || name AS name,pathid,filenameid,fileid,jobid 
908    FROM File JOIN FileName USING (FilenameId) JOIN Path USING (PathId);
909
910 SELECT 'drop table ' || tablename || ';' 
911   FROM pg_tables WHERE tablename ~ '^b[0-9]'