]> git.sur5r.net Git - bacula/bacula/blob - gui/bweb/cgi/bresto.pl
f982692c806e8d90adaf2700ae02758249655315
[bacula/bacula] / gui / bweb / cgi / bresto.pl
1 #!/usr/bin/perl -w
2 use strict;
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-2010 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    This program is Free Software; you can redistribute it and/or
17    modify it under the terms of version three of the GNU Affero General Public
18    License as published by the Free Software Foundation and included
19    in the file LICENSE.
20
21    This program is distributed in the hope that it will be useful, but
22    WITHOUT ANY WARRANTY; without even the implied warranty of
23    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
24    Affero General Public License for more details.
25
26    You should have received a copy of the GNU Affero General Public License
27    along with this program; if not, write to the Free Software
28    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
29    02110-1301, USA.
30
31    Bacula® is a registered trademark of Kern Sibbald.
32    The licensor of Bacula is the Free Software Foundation Europe
33    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
34    Switzerland, email:ftf@fsfeurope.org.
35
36 =cut
37
38 use Bweb;
39
40 package Bvfs;
41 use base qw/Bweb/;
42
43 sub get_root
44 {
45     my ($self) = @_;
46     return $self->get_pathid('');
47 }
48
49 # change the current directory
50 sub ch_dir
51 {
52     my ($self, $pathid) = @_;
53     $self->{cwdid} = $pathid;
54 }
55
56 # return the current PWD
57 sub pwd
58 {
59     my ($self) = @_;
60     return $self->get_path($self->{cwdid});
61 }
62
63 # get the Path from a PathId
64 sub get_path
65 {
66     my ($self, $pathid) = @_;
67     $self->debug("Call with pathid = $pathid");
68     my $query = "SELECT Path FROM Path WHERE PathId = ?";
69     my $sth = $self->dbh_prepare($query);
70     $sth->execute($pathid);
71     my $result = $sth->fetchrow_arrayref();
72     $sth->finish();
73     return $result->[0];
74 }
75
76 # we are working with these jobids
77 sub set_curjobids
78 {
79     my ($self, @jobids) = @_;
80     $self->{curjobids} = join(',', @jobids);
81 #    $self->update_brestore_table(@jobids);
82 }
83
84 # get the PathId from a Path
85 sub get_pathid
86 {
87     my ($self, $dir) = @_;
88     my $query =
89         "SELECT PathId FROM Path WHERE Path = ?";
90     my $sth = $self->dbh_prepare($query);
91     $sth->execute($dir);
92     my $result = $sth->fetchrow_arrayref();
93     $sth->finish();
94
95     return $result->[0];
96 }
97
98 sub set_limits
99 {
100     my ($self, $offset, $limit) = @_;
101     $self->{limit}  = $limit  || 100;
102     $self->{offset} = $offset || 0;
103 }
104
105 sub set_pattern
106 {
107     my ($self, $pattern) = @_;
108     $self->{pattern} = $pattern;
109 }
110
111 # fill brestore_xxx tables for speedup
112 sub update_cache
113 {
114     my ($self) = @_;
115     my $b = $self->get_bconsole();
116     $b->send_one_cmd(".bvfs_update" . $self->{bvfs_user});
117 }
118
119 sub update_brestore_table
120 {
121     my ($self, @jobs) = @_;
122     my $jobs = join(",", sort {$a <=> $b} @jobs);
123     my $b = $self->get_bconsole();
124     $b->send_one_cmd(".bvfs_update jobid=$jobs" . $self->{bvfs_user});
125 }
126
127 # list all files in a directory, accross curjobids
128 sub ls_files
129 {
130     my ($self) = @_;
131
132     return undef unless ($self->{curjobids});
133     
134     my $pathid = $self->{cwdid};
135     my $jobclause = $self->{curjobids};
136     my $filter ='';
137
138     if ($self->{pattern}) {
139         $filter = " pattern=\"$self->{pattern}\"";
140     }
141     my $b = $self->get_bconsole();
142     my $ret = $b->send_one_cmd(".bvfs_lsfiles jobid=$jobclause " .
143                                "pathid=$pathid " . $self->{bvfs_user} .
144                                "limit=$self->{limit} offset=$self->{offset} " .
145                                $filter);
146
147     #   0        1          2       3     4       5
148     # PathId, FilenameId, fileid, jobid, lstat, Name
149     my @return_list;
150     foreach my $line (@{$ret})
151     {
152         next unless ($line =~ /^\d+\t\d+/);
153         chomp($line);
154         my @row = split("\t", $line, 6);
155         my $fid = $row[2];
156         my $fnid = $row[1];
157         my $name = $row[5];
158         my $lstat = $row[4];
159         my $jobid = $row[3] || 0;
160         # We have to clean up this dirname ... we only want it's 'basename'
161         my @return_array = ($fnid, $fid,$name,$lstat,$jobid);
162         push @return_list,(\@return_array);
163     }
164 #FilenameId, listfiles.id, Name, File.LStat, File.JobId
165
166     return \@return_list;
167 }
168
169 # list all directories in a directory, accross curjobids
170 # return ($dirid,$dir_basename,$lstat,$jobid)
171 sub ls_dirs
172 {
173     my ($self) = @_;
174
175     return undef unless ($self->{curjobids});
176     
177     my $pathid = $self->{cwdid};
178     my $jobclause = $self->{curjobids};
179     my $filter ='';
180
181     if ($self->{pattern}) {
182         $filter = " pattern=\"$self->{pattern}\" ";
183     }
184     my $b = $self->get_bconsole();
185     my $ret = $b->send_one_cmd(".bvfs_lsdir jobid=$jobclause pathid=$pathid " .
186                                $self->{bvfs_user} .
187                                "limit=$self->{limit} offset=$self->{offset} " .
188                                $filter);
189
190     #   0     1     2      3      4     5
191     # PathId, 0, fileid, jobid, lstat, path
192     my @return_list;
193     my $prev_dir='';
194     foreach my $line (@{$ret})
195     {
196         next unless ($line =~ /^\d+\t\d+/);
197         chomp($line);
198         my @row = split("\t", $line, 6);
199         my $dirid = $row[0];
200         my $dir = $row[5];
201         my $lstat = $row[4];
202         my $jobid = $row[3] || 0;
203         next if ($self->{skipdot} && $dir =~ /^\.+$/);
204         # We have to clean up this dirname ... we only want it's 'basename'
205         my @return_array = ($dirid,$dir,'', $lstat,$jobid);
206         push @return_list,(\@return_array);
207         $prev_dir = $dirid;
208     }
209
210     return \@return_list;
211 }
212
213 # TODO : we want be able to restore files from a bad ended backup
214 # we have JobStatus IN ('T', 'A', 'E') and we must
215
216 # Data acces subs from here. Interaction with SGBD and caching
217
218 # This sub retrieves the list of jobs corresponding to the jobs selected in the
219 # GUI and stores them in @CurrentJobIds.
220 # date must be quoted
221 sub set_job_ids_for_date
222 {
223     my ($self, $client, $date)=@_;
224
225     if (!$client or !$date) {
226         return ();
227     }
228     my $filter = $self->get_client_filter();
229     # The algorithm : for a client, we get all the backups for each
230     # fileset, in reverse order Then, for each fileset, we store the 'good'
231     # incrementals and differentials until we have found a full so it goes
232     # like this : store all incrementals until we have found a differential
233     # or a full, then find the full
234     my $query = "
235 SELECT JobId, FileSet, Level, JobStatus
236   FROM Job 
237        JOIN FileSet USING (FileSetId)
238        JOIN Client USING (ClientId) $filter
239  WHERE EndTime <= $date
240    AND Client.Name = '$client'
241    AND Type IN ('B')
242    AND JobStatus IN ('T')
243  ORDER BY FileSet, JobTDate DESC";
244
245     my @CurrentJobIds;
246     my $result = $self->dbh_selectall_arrayref($query);
247     my %progress;
248     foreach my $refrow (@$result)
249     {
250         my $jobid = $refrow->[0];
251         my $fileset = $refrow->[1];
252         my $level = $refrow->[2];
253
254         defined $progress{$fileset} or $progress{$fileset}='U'; # U for unknown
255
256         next if $progress{$fileset} eq 'F'; # It's over for this fileset...
257
258         if ($level eq 'I')
259         {
260             next unless ($progress{$fileset} eq 'U' or $progress{$fileset} eq 'I');
261             push @CurrentJobIds,($jobid);
262         }
263         elsif ($level eq 'D')
264         {
265             next if $progress{$fileset} eq 'D'; # We allready have a differential
266             push @CurrentJobIds,($jobid);
267         }
268         elsif ($level eq 'F')
269         {
270             push @CurrentJobIds,($jobid);
271         }
272
273         my $status = $refrow->[3] ;
274         if ($status eq 'T') {              # good end of job
275             $progress{$fileset} = $level;
276         }
277     }
278
279     return @CurrentJobIds;
280 }
281
282 sub dbh_selectrow_arrayref
283 {
284     my ($self, $query) = @_;
285     $self->debug($query, up => 1);
286     return $self->{dbh}->selectrow_arrayref($query);
287 }
288
289 # Returns list of versions of a file that could be restored
290 # returns an array of
291 # (jobid,fileindex,mtime,size,inchanger,md5,volname,fileid,LinkFI)
292 # there will be only one jobid in the array of jobids...
293 sub get_all_file_versions
294 {
295     my ($self,$pathid,$fileid,$client,$see_all,$see_copies)=@_;
296
297     defined $see_all or $see_all=0;
298     my $backup_type="";
299     if ($see_copies) {
300         $backup_type=" copies ";
301     }
302
303     my $bc = $self->get_bconsole();
304     my $res = $bc->send_one_cmd(".bvfs_versions fnid=$fileid pathid=$pathid " . 
305                                 "client=\"$client\" jobid=1 $backup_type" .
306                                 $self->{bvfs_user});
307
308     my @versions;
309     # (pathid,fileid,jobid,fid,mtime,size,inchanger,md5,volname,LinkFI );
310     # PathId, FilenameId, fileid, jobid, lstat, Md5, VolName, VolInchanger
311     foreach my $row (@$res)
312     {
313         next unless $row =~ /^\d+\t\d+/;
314         my ($pathid, $fid, $fileid, $jobid, $lstat, $md5, $volname, $inchanger) 
315             = split(/\t/, $row);
316
317         my @attribs = parse_lstat($lstat);
318         my $mtime = array_attrib('st_mtime',\@attribs);
319         my $size = array_attrib('st_size',\@attribs);
320         my $LinkFI = array_attrib('LinkFI',\@attribs);
321
322         #              0     1      2       3        4      5        6
323         my @list = ($pathid,$fileid,$jobid, $fid, $mtime, $size, $inchanger,
324                     $md5, $volname, $LinkFI);
325         push @versions, (\@list);
326     }
327
328     # We have the list of all versions of this file.
329     # We'll sort it by mtime desc, size, md5, inchanger desc, FileId
330     # the rest of the algorithm will be simpler
331     # ('FILE:',filename,jobid,fileindex,mtime,size,inchanger,md5,volname)
332     @versions = sort { $b->[4] <=> $a->[4]
333                     || $a->[5] <=> $b->[5]
334                     || $a->[7] cmp $a->[7]
335                     || $b->[6] <=> $a->[6]} @versions;
336
337     my @good_versions;
338     my %allready_seen_by_mtime;
339     my %allready_seen_by_md5;
340     # Now we should create a new array with only the interesting records
341     foreach my $ref (@versions)
342     {
343         if ($ref->[7])
344         {
345             # The file has a md5. We compare his md5 to other known md5...
346             # We take size into account. It may happen that 2 files
347             # have the same md5sum and are different. size is a supplementary
348             # criterion
349
350             # If we allready have a (better) version
351             next if ( (not $see_all)
352                       and $allready_seen_by_md5{$ref->[7] .'-'. $ref->[5]});
353
354             # we never met this one before...
355             $allready_seen_by_md5{$ref->[7] .'-'. $ref->[5]}=1;
356         }
357         # Even if it has a md5, we should also work with mtimes
358         # We allready have a (better) version
359         next if ( (not $see_all)
360                   and $allready_seen_by_mtime{$ref->[4] .'-'. $ref->[5]});
361         $allready_seen_by_mtime{$ref->[4] .'-'. $ref->[5] . '-' . $ref->[7]}=1;
362
363         # We reached there. The file hasn't been seen.
364         push @good_versions,($ref);
365     }
366
367     # To be nice with the user, we re-sort good_versions by
368     # inchanger desc, mtime desc
369     @good_versions = sort { $b->[4] <=> $a->[4]
370                          || $b->[2] <=> $a->[2]} @good_versions;
371
372     return \@good_versions;
373 }
374 {
375     my %attrib_name_id = ( 'st_dev' => 0,'st_ino' => 1,'st_mode' => 2,
376                           'st_nlink' => 3,'st_uid' => 4,'st_gid' => 5,
377                           'st_rdev' => 6,'st_size' => 7,'st_blksize' => 8,
378                           'st_blocks' => 9,'st_atime' => 10,'st_mtime' => 11,
379                           'st_ctime' => 12,'LinkFI' => 13,'st_flags' => 14,
380                           'data_stream' => 15);;
381     sub array_attrib
382     {
383         my ($attrib,$ref_attrib)=@_;
384         return $ref_attrib->[$attrib_name_id{$attrib}];
385     }
386
387     sub file_attrib
388     {   # $file = [filenameid,listfiles.id,listfiles.Name, File.LStat, File.JobId]
389
390         my ($file, $attrib)=@_;
391
392         if (defined $attrib_name_id{$attrib}) {
393
394             my @d = split(' ', $file->[3]) ; # TODO : cache this
395
396             return from_base64($d[$attrib_name_id{$attrib}]);
397
398         } elsif ($attrib eq 'jobid') {
399
400             return $file->[4];
401
402         } elsif ($attrib eq 'name') {
403
404             return $file->[2];
405
406         } else  {
407             die "Attribute not known : $attrib.\n";
408         }
409     }
410
411     sub lstat_attrib
412     {
413         my ($lstat,$attrib)=@_;
414         if ($lstat and defined $attrib_name_id{$attrib})
415         {
416             my @d = split(' ', $lstat) ; # TODO : cache this
417             return from_base64($d[$attrib_name_id{$attrib}]);
418         }
419         return 0;
420     }
421 }
422
423 {
424     # Base 64 functions, directly from recover.pl.
425     # Thanks to
426     # Karl Hakimian <hakimian@aha.com>
427     # This section is also under GPL v2 or later.
428     my @base64_digits;
429     my @base64_map;
430     my $is_init=0;
431     sub init_base64
432     {
433         @base64_digits = (
434         'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
435         'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
436         'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
437         'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
438         '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'
439                           );
440         @base64_map = (0) x 128;
441
442         for (my $i=0; $i<64; $i++) {
443             $base64_map[ord($base64_digits[$i])] = $i;
444         }
445         $is_init = 1;
446     }
447
448     sub from_base64 {
449         if(not $is_init)
450         {
451             init_base64();
452         }
453         my $where = shift;
454         my $val = 0;
455         my $i = 0;
456         my $neg = 0;
457
458         if (substr($where, 0, 1) eq '-') {
459             $neg = 1;
460             $where = substr($where, 1);
461         }
462
463         while ($where ne '') {
464             $val *= 64;
465             my $d = substr($where, 0, 1);
466             $val += $base64_map[ord(substr($where, 0, 1))];
467             $where = substr($where, 1);
468         }
469
470         return $val;
471     }
472
473     sub parse_lstat {
474         my ($lstat)=@_;
475         my @attribs = split(' ',$lstat);
476         foreach my $element (@attribs)
477         {
478             $element = from_base64($element);
479         }
480         return @attribs;
481     }
482 }
483
484 # get jobids that the current user can view (ACL)
485 sub get_jobids
486 {
487   my ($self, @jobid) = @_;
488   my $filter = $self->get_client_filter();
489   if ($filter) {
490     my $jobids = $self->dbh_join(@jobid);
491     my $q="
492 SELECT JobId 
493   FROM Job JOIN Client USING (ClientId) $filter 
494  WHERE Jobid IN ($jobids)";
495     my $res = $self->dbh_selectall_arrayref($q);
496     @jobid = map { $_->[0] } @$res;
497   }
498   return @jobid;
499 }
500
501 ################################################################
502
503
504 package main;
505 use strict;
506 use POSIX qw/strftime/;
507 use Bweb;
508
509 my $conf = new Bweb::Config(config_file => $Bweb::config_file);
510 $conf->load();
511
512 my $skipdot=0;
513 if (CGI::param("skipdot")) {
514     $skipdot=1;
515 }
516
517 my $bvfs = new Bvfs(info => $conf, skipdot => $skipdot);
518 my $user = $bvfs->{loginname};
519 if ($bvfs->{loginname}) {
520     $bvfs->{bvfs_user} = " username=\"$bvfs->{loginname}\" ";
521 } else {
522     $bvfs->{bvfs_user} = "";
523 }
524 $bvfs->connect_db();
525
526 my $action = CGI::param('action') || '';
527
528 my $args = $bvfs->get_form('pathid', 'filenameid', 'fileid', 'qdate',
529                            'limit', 'offset', 'client');
530
531 if ($action eq 'batch') {
532     $bvfs->update_cache();
533     exit 0;
534 }
535
536 my $pattern = CGI::param('pattern') || '';
537 if ($pattern =~ /^([\w\d,:\.\-% ]+)$/) {
538     $bvfs->set_pattern($1);
539 }
540
541 my $nodir;
542 if ($conf->{subconf} 
543     && scalar(%{$conf->{subconf}}) # we have non empty subconf
544     && !$conf->{current_conf})
545 {
546     $nodir=1;
547 }
548 # All these functions are returning JSON compatible data
549 # for javascript parsing
550
551 if ($action eq 'list_client') { # list all client [ ['c1'],['c2']..]
552     print CGI::header('application/x-javascript');
553
554     if ($nodir) {
555         print "[['Choose a Director first']]\n";
556         exit 0;
557     }
558
559     my $filter = $bvfs->get_client_filter();
560     my $q = "SELECT Name FROM Client $filter";
561     my $ret = $bvfs->dbh_selectall_arrayref($q);
562
563     print "[";
564     print join(',', map { "['$_->[0]']" } @$ret);
565     print "]\n";
566     exit 0;
567     
568 } elsif ($action eq 'list_job') {
569     # list jobs for a client [[jobid,endtime,'desc'],..]
570
571     print CGI::header('application/x-javascript');
572     
573     my $filter = $bvfs->get_client_filter();
574     my $query = "
575  SELECT Job.JobId,Job.EndTime, FileSet.FileSet, Job.Level, Job.JobStatus
576   FROM Job JOIN FileSet USING (FileSetId) JOIN Client USING (ClientId) $filter
577  WHERE Client.Name = '$args->{client}'
578    AND Job.Type = 'B'
579    AND JobStatus IN ('f', 'T')
580  ORDER BY EndTime desc";
581     my $result = $bvfs->dbh_selectall_arrayref($query);
582
583     print "[";
584
585     print join(',', map {
586       "[$_->[0], '$_->[1]', '$_->[1] $_->[2] $_->[3] ($_->[4]) $_->[0]']"
587       } @$result);
588
589     print "]\n";
590     exit 0;
591
592 } elsif ($action eq 'list_storage') { 
593     print CGI::header('application/x-javascript');
594
595     my $bconsole = $bvfs->get_bconsole();
596     my @lst = $bconsole->list_storage();
597     print "[";
598     print join(',', map { "[ '$_' ]" } @lst);
599     print "]\n";
600     exit 0;
601 }
602
603 sub fill_table_for_restore
604 {
605     my (@jobid) = @_;
606
607     # in "force" mode, we need the FileId to compute media list
608     my $FileId = CGI::param('force')?",FileId":"";
609
610     my $fileid = join(',', grep { /^\d+$/ } CGI::param('fileid'));
611     # can get dirid=("10,11", 10, 11)
612     my $dirid = join(',', grep { /^\d+$/ } 
613                            map { split(/,/) } CGI::param('dirid')) ;
614     my $findex = join(',', grep { /^\d+$/ } 
615                              map { split(/,|\//) } CGI::param('findex')) ;
616     my $jobid = join(',', grep { /^\d+$/ }
617                            map { split(/,/) } CGI::param('jobid')) ;
618     my $inclause = join(',', @jobid);
619
620     my $b = $bvfs->get_bconsole();
621     my $ret = $b->send_one_cmd(".bvfs_restore path=b2$$ fileid=$fileid " .
622                                "dirid=$dirid hardlink=$findex jobid=$jobid"
623                                . $bvfs->{bvfs_user});
624     if (grep (/OK/, @$ret)) {
625         return "b2$$";
626     }
627     return;
628 }
629
630 sub get_media_list_with_dir
631 {
632     my ($table) = @_;
633     my $q="
634  SELECT DISTINCT VolumeName, Enabled, InChanger
635    FROM $table,
636     ( -- Get all media from this job
637       SELECT MIN(FirstIndex) AS FirstIndex, MAX(LastIndex) AS LastIndex,
638              VolumeName, Enabled, Inchanger
639         FROM JobMedia JOIN Media USING (MediaId)
640        WHERE JobId IN (SELECT DISTINCT JobId FROM $table)
641        GROUP BY VolumeName,Enabled,InChanger
642     ) AS allmedia
643   WHERE $table.FileIndex >= allmedia.FirstIndex
644     AND $table.FileIndex <= allmedia.LastIndex
645 ";
646     my $lst = $bvfs->dbh_selectall_arrayref($q);
647     return $lst;
648 }
649
650 sub get_media_list
651 {
652     my ($jobid, $fileid) = @_;
653     my $q="
654  SELECT DISTINCT VolumeName, Enabled, InChanger
655    FROM File,
656     ( -- Get all media from this job
657       SELECT MIN(FirstIndex) AS FirstIndex, MAX(LastIndex) AS LastIndex,
658              VolumeName, Enabled, Inchanger
659         FROM JobMedia JOIN Media USING (MediaId)
660        WHERE JobId IN ($jobid)
661        GROUP BY VolumeName,Enabled,InChanger
662     ) AS allmedia
663   WHERE File.FileId IN ($fileid)
664     AND File.FileIndex >= allmedia.FirstIndex
665     AND File.FileIndex <= allmedia.LastIndex
666 ";
667     my $lst = $bvfs->dbh_selectall_arrayref($q);
668     return $lst;
669 }
670
671 # get jobid param and apply user filter
672 my @jobid = $bvfs->get_jobids(grep { /^\d+(,\d+)*$/ } CGI::param('jobid'));
673
674 # get jobid from date arg
675 if (!scalar(@jobid) and $args->{qdate} and $args->{client}) {
676     @jobid = $bvfs->set_job_ids_for_date($args->{client}, $args->{qdate});
677 }
678
679 $bvfs->set_curjobids(@jobid);
680 $bvfs->set_limits($args->{offset}, $args->{limit});
681
682 if (!scalar(@jobid)) {
683     exit 0;
684 }
685
686 if (CGI::param('init')) { # used when choosing a job
687     $bvfs->update_brestore_table(@jobid);
688 }
689
690 my $pathid = CGI::param('node') || CGI::param('pathid') || '';
691 my $path = CGI::param('path');
692
693 if ($pathid =~ /^(\d+)$/) {
694     $pathid = $1;
695 } elsif ($path) {
696     $pathid = $bvfs->get_pathid($path);
697 } else {
698     $pathid = $bvfs->get_root();
699 }
700 $bvfs->ch_dir($pathid);
701
702 #print STDERR "pathid=$pathid\n";
703
704 if ($action eq 'restore') {
705
706     # TODO: pouvoir choisir le replace et le jobname
707     my $arg = $bvfs->get_form(qw/client storage regexwhere where comment dir/);
708
709     if (!$arg->{client}) {
710         print "ERROR: missing client\n";
711         exit 1;
712     }
713
714     my $table = fill_table_for_restore(@jobid);
715     if (!$table) {
716         print "ERROR: can create restore table\n";
717         exit 1;
718     }
719
720     # TODO: remove it after a while
721     if ($bvfs->get_db_field('Comment') ne 'Comment') {
722         delete $arg->{comment};
723     }
724
725     my $bconsole = $bvfs->get_bconsole();
726     # TODO: pouvoir choisir le replace et le jobname
727     my $jobid = $bconsole->run(client    => $arg->{client},
728                                storage   => $arg->{storage},
729                                where     => $arg->{where},
730                                regexwhere=> $arg->{regexwhere},
731                                restore   => 1,
732                                comment   => $arg->{comment},
733                                file      => "?$table");
734     
735     $bvfs->dbh_do("DROP TABLE $table");
736
737     if (!$jobid) {
738         print CGI::header('text/html');
739         $bvfs->display_begin();
740         $bvfs->error("Can't start your job:<br/>" . $bconsole->before());
741         $bvfs->display_end();
742         exit 0;
743     }
744
745     sleep(2);
746
747     my $dir='';
748     if ($arg->{dir}) {
749         $dir=";dir=$arg->{dir}";
750     }
751
752     print CGI::redirect("bweb.pl?action=dsp_cur_job;jobid=$jobid$dir") ;
753     exit 0;
754 }
755 sub escape_quote
756 {
757     my ($str) = @_;
758     my %esc = (
759         "\n" => '\n',
760         "\r" => '\r',
761         "\t" => '\t',
762         "\f" => '\f',
763         "\b" => '\b',
764         "\"" => '\"',
765         "\\" => '\\\\',
766         "\'" => '\\\'',
767     );
768
769     if (!$str) {
770         return '';
771     }
772
773     $str =~ s/([\x22\x5c\n\r\t\f\b])/$esc{$1}/g;
774     $str =~ s/\//\\\//g;
775     $str =~ s/([\x00-\x08\x0b\x0e-\x1f])/'\\u00' . unpack('H2', $1)/eg;
776     return $str;
777 }
778
779 print CGI::header('application/x-javascript');
780
781
782 if ($action eq 'list_files_dirs') {
783 # fileid, filenameid, pathid, jobid, name, size, mtime, LinkFI
784     my $jids = join(",", @jobid);
785
786     my $files = $bvfs->ls_dirs();
787     # return ($dirid,$dir_basename,$lstat,$jobid)
788     print '[', join(',',
789                map { my @p=Bvfs::parse_lstat($_->[3]); 
790                      '[' . join(',', 
791                                 0, # fileid
792                                 0, # filenameid
793                                 $_->[0], # pathid
794                                 "'$jids'", # jobid
795                                 '"' . escape_quote($_->[1]) . '"', # name
796                                 "'" . $p[7] . "'",                 # size
797                                 "'" . strftime('%Y-%m-%d %H:%m:%S', localtime($p[11]||0)) .  "'",
798                                 0) . # LinkFI
799                     ']'; 
800                } @$files);
801
802     print "," if (@$files);
803  
804     $files = $bvfs->ls_files();
805     print join(',',
806                map { my @p=Bvfs::parse_lstat($_->[3]); 
807                      '[' . join(',', 
808                                 $_->[1], # fileid
809                                 $_->[0], # fnid
810                                 $pathid, # pathid
811                                 $_->[4], # jobid
812                                 '"' . escape_quote($_->[2]) . '"', # name
813                                 "'" . $p[7] . "'",
814                                 "'" . strftime('%Y-%m-%d %H:%m:%S', localtime($p[11])) .  "'",
815                                 $p[13]) . # LinkFI
816                     ']'; 
817                } @$files);
818     print "]\n";
819
820 } elsif ($action eq 'list_files') {
821     print "[[0,0,0,0,'.',4096,'1970-01-01 00:00:00'],";
822     my $files = $bvfs->ls_files();
823 #       [ 1, 2, 3, "Bill",  10, '2007-01-01 00:00:00'],
824 #   File.FilenameId, listfiles.id, listfiles.Name, File.LStat, File.JobId,LinkFI
825
826     print join(',',
827                map { my @p=Bvfs::parse_lstat($_->[3]); 
828                      '[' . join(',', 
829                                 $_->[1],
830                                 $_->[0],
831                                 $pathid,
832                                 $_->[4],
833                                 '"' . escape_quote($_->[2]) . '"', # name
834                                 "'" . $p[7] . "'",
835                                 "'" . strftime('%Y-%m-%d %H:%m:%S', localtime($p[11])) .  "'",
836                                 $p[13]) . # LinkFI
837                     ']'; 
838                } @$files);
839     print "]\n";
840
841 } elsif ($action eq 'list_dirs') {
842
843     print "[";
844     my $dirs = $bvfs->ls_dirs();
845
846     # return ($dirid,$dir_basename,$lstat,$jobid)
847
848     print join(',',
849                map { "{ 'jobid': '$bvfs->{curjobids}', 'id': '$_->[0]'," . 
850                          "'text': '" . escape_quote($_->[1]) . "', 'cls':'folder'}" } 
851                @$dirs);
852     print "]\n";
853
854 } elsif ($action eq 'list_versions') {
855
856     my $vafv = CGI::param('vafv') || 'false'; # view all file versions
857     $vafv = ($vafv eq 'false')?0:1;
858
859     my $vcopies = CGI::param('vcopies') || 'false'; # view copies file versions
860     $vcopies = ($vcopies eq 'false')?0:1;
861
862     print "[";
863     #   0     1      2     3   4     5     6        7      8     9
864     #(pathid,fileid,jobid,fid,mtime,size,inchanger,md5,volname,LinkFI );
865     my $files = $bvfs->get_all_file_versions($args->{pathid}, $args->{filenameid}, $args->{client}, $vafv, $vcopies);
866     print join(',',
867                map { "[ $_->[1], $_->[3], $_->[0], $_->[2], '$_->[8]', $_->[6], '$_->[7]', $_->[5],'" . strftime('%Y-%m-%d %H:%m:%S', localtime($_->[4])) . "',$_->[9]]" }
868                @$files);
869     print "]\n";
870
871 # this action is used when the restore box appear, we can display
872 # the media list that will be needed for restore
873 } elsif ($action eq 'get_media') {
874     my ($jobid, $fileid, $table);
875     my $lst;
876
877     # in this mode, we compute the result to get all needed media
878 #    print STDERR "force=", CGI::param('force'), "\n";
879     if (CGI::param('force')) {
880         $table = fill_table_for_restore(@jobid);
881         if (!$table) {
882             exit 1;
883         }
884         # mysql is very slow without this index...
885         if ($bvfs->dbh_is_mysql()) {
886             $bvfs->dbh_do("CREATE INDEX idx_$table ON $table (JobId)");
887         }
888         $lst = get_media_list_with_dir($table);
889     } else {
890         $jobid = join(',', @jobid);
891         $fileid = join(',', grep { /^\d+(,\d+)*$/ } CGI::param('fileid'));
892         $lst = get_media_list($jobid, $fileid);
893     }        
894     
895     if ($lst) {
896         print "[";
897         print join(',', map { "['$_->[0]',$_->[1],$_->[2]]" } @$lst);
898         print "]\n";
899     }
900
901     if ($table) {
902         my $b = $bvfs->get_bconsole();
903         $b->send_one_cmd(".bvfs_cleanup path=b2$$");
904     }
905
906 }
907
908 __END__
909
910 CREATE VIEW files AS
911  SELECT path || name AS name,pathid,filenameid,fileid,jobid
912    FROM File JOIN FileName USING (FilenameId) JOIN Path USING (PathId);
913
914 SELECT 'drop table ' || tablename || ';'
915     FROM pg_tables WHERE tablename ~ '^b[0-9]';