]> git.sur5r.net Git - bacula/bacula/blob - gui/bweb/cgi/bfileview.pl
bweb: Add sqlite support
[bacula/bacula] / gui / bweb / cgi / bfileview.pl
1 #!/usr/bin/perl -w
2
3 =head1 LICENSE
4
5    Bweb - A Bacula web interface
6    Bacula® - The Network Backup Solution
7
8    Copyright (C) 2000-2010 Free Software Foundation Europe e.V.
9
10    The main author of Bweb is Eric Bollengier.
11    The main author of Bacula is Kern Sibbald, with contributions from
12    many others, a complete list can be found in the file AUTHORS.
13    This program is Free Software; you can redistribute it and/or
14    modify it under the terms of version three of the GNU Affero General Public
15    License as published by the Free Software Foundation and included
16    in the file LICENSE.
17
18    This program is distributed in the hope that it will be useful, but
19    WITHOUT ANY WARRANTY; without even the implied warranty of
20    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21    Affero General Public License for more details.
22
23    You should have received a copy of the GNU Affero General Public License
24    along with this program; if not, write to the Free Software
25    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
26    02110-1301, USA.
27
28    Bacula® is a registered trademark of Kern Sibbald.
29    The licensor of Bacula is the Free Software Foundation Europe
30    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
31    Switzerland, email:ftf@fsfeurope.org.
32
33 =cut
34
35 use strict;
36 use POSIX qw/strftime/;
37 use Bweb;
38 use CCircle ;
39 use Digest::MD5 qw(md5_hex);
40 use File::Basename qw/basename dirname/;
41
42 my $conf = new Bweb::Config(config_file => $Bweb::config_file);
43 $conf->load();
44 my $bweb = new Bweb(info => $conf);
45 $bweb->connect_db();
46
47 my $arg = $bweb->get_form('where', 'jobid', 'pathid', 'filenameid');
48 my $where = $arg->{where} || '/';
49 my $jobid = $arg->{jobid};
50 my $pathid = $arg->{pathid};
51 my $fnid = $arg->{filenameid};
52 my $jobid_url = "jobid=$jobid";
53 my $opt_level = 2 ;
54 my $max_file = 20;
55 my $batch = CGI::param("mode") || '';
56
57 my $md5_rep = md5_hex("$where:$jobid:$pathid:$fnid") ;
58 my $base_url = '/bweb/fv' ;
59 my $base_fich = $conf->{fv_write_path};
60
61 if ($jobid and $batch eq 'batch') {
62     my $root = fv_get_root_pathid($where);
63     if ($root) {
64         fv_compute_size($jobid, $root);
65         exit 0;
66     }
67     exit 1;
68 }
69
70 $bweb->display_begin();
71 $bweb->display_job_zoom($jobid);
72
73 unless ($jobid) {
74     $bweb->error("Can't get where or jobid");
75     $bweb->display_end();
76     exit 0;
77 }
78
79 unless ($base_fich and -w $base_fich) {
80     $bweb->error("fv_write_path ($base_fich) is not writable." . 
81                  " See Bweb configuration.");
82     $bweb->display_end();
83     exit 0;
84 }
85
86 if (-f "$base_fich/$md5_rep.png" and -f "$base_fich/$md5_rep.tpl")
87 {
88     $bweb->display({}, "$base_fich/$md5_rep.tpl");
89     $bweb->display_end();
90     exit 0;
91 }
92
93 my $r = $bweb->dbh_selectrow_hashref("SELECT PurgedFiles AS ok FROM Job WHERE JobId = $jobid");
94 if (!$r || $r->{ok}) {
95     $bweb->error("File information for job $jobid has been pruned from catalog");
96     $bweb->display_end();
97     exit 0;    
98
99
100 $r = $bweb->dbh_selectrow_hashref("SELECT JobId AS ok FROM brestore_knownjobid WHERE JobId = $jobid");
101 if (!$r || !$r->{ok}) {         # TODO: compute information
102     $bweb->error("Path information for job $jobid has not been updated in the catalog");
103     $bweb->display_end();
104     exit 0;    
105
106
107 # if it's a file, display it
108 if ($fnid and $pathid)
109 {
110     my $attribs = fv_get_file_attribute_from_id($jobid, $pathid, $fnid);
111     if ($attribs->{found}) {
112         $bweb->display($attribs, 'fv_file_attribs.tpl');
113         $bweb->display_end();
114         exit 0;
115     }
116
117 } elsif ($where ne '/') {
118
119     my $attribs = fv_get_file_attribute($jobid, $where);
120     if ($attribs->{found}) {
121         $bweb->display($attribs, 'fv_file_attribs.tpl');
122         $bweb->display_end();
123         exit 0;
124     }
125 }
126
127 my $root;
128
129 if ($pathid) {
130     $root = $pathid;
131     $where = fv_get_root_path($pathid);
132
133 } else {
134     if ($where !~ m!/$!) {
135         $where = $where . "/" ;
136     }
137     
138     $root = fv_get_root_pathid($where);
139 }
140
141 if (!$root) {
142     $bweb->error("Can't find $where in catalog");
143     $bweb->display_end();
144     exit 0;
145 }
146
147 my $total = fv_compute_size($jobid, $root);
148
149 my $url_action = "bfileview.pl?opt_level=$opt_level" ;
150 my $top = new CCircle(
151                       display_other => 1,
152                       base_url => "$url_action;pathid=$root;$jobid_url;here=$where",
153                       ) ;
154
155 fv_display_rep($top, $total, $root, $opt_level) ;
156
157 $top->draw_labels() ;
158 $top->set_title(Bweb::human_size($total)) ;
159
160 open(OUT, ">$base_fich/$md5_rep.png") or die "$base_fich/$md5_rep.png $!";
161 # make sure we are writing to a binary stream
162 binmode OUT;
163 # Convert the image to PNG and print it on standard output
164 print OUT $CCircle::gd->png;
165 close(OUT) ;
166
167 open(OUT, ">$base_fich/$md5_rep.tpl") or die "$base_fich/$md5_rep.tpl $!";
168 print OUT "<br/>
169  <form action='$url_action' method='get'>
170   <div align='left'>
171    <input title='jobids' type='hidden' name='jobid' value='$jobid'>
172    <input title='directory' type='text' name='where' value='$where'/>
173    <input type='submit' size='256' name='go' value='go'/>
174   </div>
175  </form>
176  <br/>
177 " ;
178
179 print OUT $top->get_imagemap($where, "$base_url/$md5_rep.png") ;
180 close(OUT) ;
181
182 $bweb->display({}, "$base_fich/$md5_rep.tpl");
183 $bweb->display_end();
184
185 sub fv_display_rep
186 {
187     my ($ccircle, $max, $rep, $level) = @_ ;
188     return if ($max < 1);
189
190     my $sum = 0;
191     my $dirs = fv_list_dirs($jobid, $rep);      # 0: pathid, 1: pathname
192
193     foreach my $dir (@{$dirs})
194     {
195         my $size = fv_compute_size($jobid, $dir->[0]);
196         $sum += $size;
197
198         my $per = $size * 100 / $max;
199         # can't use pathname when using utf or accent
200         # a bit ugly
201         $ccircle->{base_url} =~ s/pathid=\d+;/pathid=$dir->[0];/;
202
203         my $chld = $ccircle->add_part($per, 
204                                       basename($dir->[1]) . '/',
205                                       basename($dir->[1]) 
206                                        . sprintf(' %.0f%% ', $per)
207                                        . Bweb::human_size($size)
208                                       ) ;
209
210         if ($chld && $level > 0) {
211             fv_display_rep($chld, $size, $dir->[0], $level - 1) ;
212         }
213     }
214
215     # 0: filenameid, 1: filename, 2: size
216     my $files = fv_get_big_files($jobid, $rep, 3*100/$max, $max_file/($level+1));
217     foreach my $f (@{$files}) {
218         $ccircle->{base_url} =~ s/pathid=\d+;(filenameid=\d+)?/pathid=$rep;filenameid=$f->[0];/;
219
220         $ccircle->add_part($f->[2] * 100 / $max, 
221                            $f->[1],
222                            $f->[1] . "\n" . Bweb::human_size($f->[2]));
223         $sum += $f->[2];
224     }
225
226     if ($sum < $max) {
227         $ccircle->add_part(($max - $sum) * 100 / $max, 
228                            "other files < 3%",
229                            "other\n" . Bweb::human_size($max - $sum));
230     }
231
232     $ccircle->finalize() ;
233 }
234
235 sub fv_compute_size
236 {
237     my ($jobid, $rep) = @_;
238
239     my $size = fv_get_size($jobid, $rep);
240     if ($size) {
241         return $size;
242     }
243
244     $size = fv_get_files_size($jobid, $rep);
245
246     my $dirs = fv_list_dirs($jobid, $rep);
247     foreach my $dir (@{$dirs}) {
248         $size += fv_compute_size($jobid, $dir->[0]);
249     }
250     
251     fv_update_size($jobid, $rep, $size);
252     return $size;
253 }
254
255 sub fv_list_dirs
256 {
257     my ($jobid, $rep) = @_;
258
259     my $ret = $bweb->dbh_selectall_arrayref("
260       SELECT P.PathId,
261              ( SELECT Path FROM Path WHERE PathId = P.PathId) AS Path
262         FROM (
263           SELECT PathId
264             FROM brestore_pathvisibility 
265       INNER JOIN brestore_pathhierarchy USING (PathId)
266            WHERE PPathId  = $rep
267              AND JobId = $jobid
268              ) AS P
269 ");
270
271     return $ret;
272 }
273
274 sub fv_get_file_attribute
275 {
276     my ($jobid, $full_name) = @_;
277
278     if ($full_name eq '/') {
279         return {found => 0};
280     }
281     
282     my $filename = $bweb->dbh_quote(basename($full_name));
283     my $path     = $bweb->dbh_quote(dirname($full_name) . "/");
284
285     my $attr = $bweb->dbh_selectrow_hashref("
286  SELECT 1    AS found,
287         MD5  AS md5,
288         base64_decode_lstat(8,  LStat) AS size,
289         base64_decode_lstat(11, LStat) AS atime,
290         base64_decode_lstat(12, LStat) AS mtime,
291         base64_decode_lstat(13, LStat) AS ctime
292
293    FROM File JOIN Filename USING (FilenameId)
294              JOIN Path     USING (PathId)
295   WHERE Name  = $filename
296    AND  Path  = $path
297    AND  JobId = $jobid
298 ");
299
300     $attr->{filename} = $full_name;
301     $attr->{size} = Bweb::human_size($attr->{size});
302     foreach my $d (qw/atime ctime mtime/) {
303         $attr->{$d} = strftime('%F %H:%M', localtime($attr->{$d}));
304     }
305     return $attr;
306 }
307
308
309 sub fv_get_file_attribute_from_id
310 {
311     my ($jobid, $pathid, $filenameid) = @_;
312     
313     my $attr = $bweb->dbh_selectrow_hashref("
314  SELECT 1    AS found,
315         MD5  AS md5,
316         base64_decode_lstat(8,  LStat) AS size,
317         base64_decode_lstat(11, LStat) AS atime,
318         base64_decode_lstat(12, LStat) AS mtime,
319         base64_decode_lstat(13, LStat) AS ctime,
320         Path.Path ||  Filename.Name AS filename
321
322    FROM File INNER JOIN Filename USING (FilenameId)
323              INNER JOIN Path     USING (PathId)
324   WHERE FilenameId  = $filenameid
325    AND  PathId  = $pathid
326    AND  JobId = $jobid
327 ");
328
329     $attr->{size} = Bweb::human_size($attr->{size});
330     foreach my $d (qw/atime ctime mtime/) {
331         $attr->{$d} = strftime('%F %H:%M', localtime($attr->{$d}));
332     }
333     return $attr;
334 }
335
336 sub fv_get_size
337 {
338     my ($jobid, $rep) = @_;
339
340     my $ret = $bweb->dbh_selectrow_hashref("
341  SELECT Size AS size
342    FROM brestore_pathvisibility
343   WHERE PathId = $rep
344     AND JobId = $jobid
345 ");
346
347     return $ret->{size};
348 }
349
350 sub fv_get_files_size
351 {
352     my ($jobid, $rep) = @_;
353
354     my $ret = $bweb->dbh_selectrow_hashref("
355  SELECT sum(base64_decode_lstat(8,LStat)) AS size
356    FROM File
357   WHERE PathId  = $rep
358     AND JobId = $jobid
359 ");
360
361     return $ret->{size};
362 }
363
364 sub fv_get_big_files
365 {
366     my ($jobid, $rep, $min, $limit) = @_;
367     $limit = int($limit);
368
369     my $ret = $bweb->dbh_selectall_arrayref("
370    SELECT FilenameId AS filenameid, Name AS name, size
371    FROM (
372          SELECT FilenameId, base64_decode_lstat(8,LStat) AS size
373            FROM File
374           WHERE PathId  = $rep
375             AND JobId = $jobid
376         ) AS S INNER JOIN Filename USING (FilenameId)
377    WHERE S.size > $min
378    ORDER BY S.size DESC
379    LIMIT $limit
380 ");
381
382     return $ret;
383 }
384
385 sub fv_update_size
386 {
387     my ($jobid, $rep, $size) = @_;
388
389     my $nb = $bweb->dbh_do("
390  UPDATE brestore_pathvisibility SET Size = $size 
391   WHERE JobId = $jobid 
392     AND PathId = $rep 
393 ");
394
395     return $nb;
396 }
397
398 sub fv_get_root_pathid
399 {
400     my ($path) = @_;
401     $path = $bweb->dbh_quote($path);
402     my $ret = $bweb->dbh_selectrow_hashref("SELECT PathId FROM Path WHERE Path = $path");
403     return $ret->{pathid};
404 }
405
406 sub fv_get_root_path
407 {
408     my ($pathid) = @_;
409     my $ret = $bweb->dbh_selectrow_hashref("SELECT Path FROM Path WHERE PathId = $pathid");
410     return $ret->{path};
411 }
412
413
414 __END__
415
416 CREATE OR REPLACE FUNCTION base64_decode_lstat(int4, varchar) RETURNS int8 AS $$
417 DECLARE
418 val int8;
419 b64 varchar(64);
420 size varchar(64);
421 i int;
422 BEGIN
423 size := split_part($2, ' ', $1);
424 b64 := 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
425 val := 0;
426 FOR i IN 1..length(size) LOOP
427 val := val + (strpos(b64, substr(size, i, 1))-1) * (64^(length(size)-i));
428 END LOOP;
429 RETURN val;
430 END;
431 $$ language 'plpgsql';
432
433 ALTER TABLE brestore_pathvisibility ADD Size  int8;
434
435
436
437 ALTER TABLE brestore_pathvisibility ADD Files int4;