]> git.sur5r.net Git - bacula/bacula/blob - gui/bweb/cgi/bfileview.pl
4783ced95f7f051c11c2f030ea41d04efb6a29ea
[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    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 print CGI::header('text/html');
71 $bweb->display_begin();
72 $bweb->display_job_zoom($jobid);
73
74 unless ($jobid) {
75     $bweb->error("Can't get where or jobid");
76     $bweb->display_end();
77     exit 0;
78 }
79
80 unless ($base_fich and -w $base_fich) {
81     $bweb->error("fv_write_path ($base_fich) is not writable." . 
82                  " See Bweb configuration.");
83     $bweb->display_end();
84     exit 0;
85 }
86
87 if (-f "$base_fich/$md5_rep.png" and -f "$base_fich/$md5_rep.tpl")
88 {
89     $bweb->display({}, "$base_fich/$md5_rep.tpl");
90     $bweb->display_end();
91     exit 0;
92 }
93
94 my $r = $bweb->dbh_selectrow_hashref("SELECT PurgedFiles AS ok FROM Job WHERE JobId = $jobid");
95 if (!$r || $r->{ok}) {
96     $bweb->error("File information for job $jobid has been pruned from catalog");
97     $bweb->display_end();
98     exit 0;    
99
100
101 $r = $bweb->dbh_selectrow_hashref("SELECT JobId AS ok FROM brestore_knownjobid WHERE JobId = $jobid");
102 if (!$r || !$r->{ok}) {         # TODO: compute information
103     $bweb->error("Path information for job $jobid has not been updated in the catalog");
104     $bweb->display_end();
105     exit 0;    
106
107
108 # if it's a file, display it
109 if ($fnid and $pathid)
110 {
111     my $attribs = fv_get_file_attribute_from_id($jobid, $pathid, $fnid);
112     if ($attribs->{found}) {
113         $bweb->display($attribs, 'fv_file_attribs.tpl');
114         $bweb->display_end();
115         exit 0;
116     }
117
118 } elsif ($where ne '/') {
119
120     my $attribs = fv_get_file_attribute($jobid, $where);
121     if ($attribs->{found}) {
122         $bweb->display($attribs, 'fv_file_attribs.tpl');
123         $bweb->display_end();
124         exit 0;
125     }
126 }
127
128 my $root;
129
130 if ($pathid) {
131     $root = $pathid;
132     $where = fv_get_root_path($pathid);
133
134 } else {
135     if ($where !~ m!/$!) {
136         $where = $where . "/" ;
137     }
138     
139     $root = fv_get_root_pathid($where);
140 }
141
142 if (!$root) {
143     $bweb->error("Can't find $where in catalog");
144     $bweb->display_end();
145     exit 0;
146 }
147
148 my $total = fv_compute_size($jobid, $root);
149
150 my $url_action = "bfileview.pl?opt_level=$opt_level" ;
151 my $top = new CCircle(
152                       display_other => 1,
153                       base_url => "$url_action;pathid=$root;$jobid_url;here=$where",
154                       ) ;
155
156 fv_display_rep($top, $total, $root, $opt_level) ;
157
158 $top->draw_labels() ;
159 $top->set_title(Bweb::human_size($total)) ;
160
161 open(OUT, ">$base_fich/$md5_rep.png") or die "$base_fich/$md5_rep.png $!";
162 # make sure we are writing to a binary stream
163 binmode OUT;
164 # Convert the image to PNG and print it on standard output
165 print OUT $CCircle::gd->png;
166 close(OUT) ;
167
168 open(OUT, ">$base_fich/$md5_rep.tpl") or die "$base_fich/$md5_rep.tpl $!";
169 print OUT "<br/>
170  <form action='$url_action' method='get'>
171   <div align='left'>
172    <input title='jobids' type='hidden' name='jobid' value='$jobid'>
173    <input title='directory' type='text' name='where' value='$where'/>
174    <input type='submit' size='256' name='go' value='go'/>
175   </div>
176  </form>
177  <br/>
178 " ;
179
180 print OUT $top->get_imagemap($where, "$base_url/$md5_rep.png") ;
181 close(OUT) ;
182
183 $bweb->display({}, "$base_fich/$md5_rep.tpl");
184 $bweb->display_end();
185
186 sub fv_display_rep
187 {
188     my ($ccircle, $max, $rep, $level) = @_ ;
189     return if ($max < 1);
190
191     my $sum = 0;
192     my $dirs = fv_list_dirs($jobid, $rep);      # 0: pathid, 1: pathname
193
194     foreach my $dir (@{$dirs})
195     {
196         my $size = fv_compute_size($jobid, $dir->[0]);
197         $sum += $size;
198
199         my $per = $size * 100 / $max;
200         # can't use pathname when using utf or accent
201         # a bit ugly
202         $ccircle->{base_url} =~ s/pathid=\d+;/pathid=$dir->[0];/;
203
204         my $chld = $ccircle->add_part($per, 
205                                       basename($dir->[1]) . '/',
206                                       basename($dir->[1]) 
207                                        . sprintf(' %.0f%% ', $per)
208                                        . Bweb::human_size($size)
209                                       ) ;
210
211         if ($chld && $level > 0) {
212             fv_display_rep($chld, $size, $dir->[0], $level - 1) ;
213         }
214     }
215
216     # 0: filenameid, 1: filename, 2: size
217     my $files = fv_get_big_files($jobid, $rep, 3*100/$max, $max_file/($level+1));
218     foreach my $f (@{$files}) {
219         $ccircle->{base_url} =~ s/pathid=\d+;(filenameid=\d+)?/pathid=$rep;filenameid=$f->[0];/;
220
221         $ccircle->add_part($f->[2] * 100 / $max, 
222                            $f->[1],
223                            $f->[1] . "\n" . Bweb::human_size($f->[2]));
224         $sum += $f->[2];
225     }
226
227     if ($sum < $max) {
228         $ccircle->add_part(($max - $sum) * 100 / $max, 
229                            "other files < 3%",
230                            "other\n" . Bweb::human_size($max - $sum));
231     }
232
233     $ccircle->finalize() ;
234 }
235
236 sub fv_compute_size
237 {
238     my ($jobid, $rep) = @_;
239
240     my $size = fv_get_size($jobid, $rep);
241     if ($size) {
242         return $size;
243     }
244
245     $size = fv_get_files_size($jobid, $rep);
246
247     my $dirs = fv_list_dirs($jobid, $rep);
248     foreach my $dir (@{$dirs}) {
249         $size += fv_compute_size($jobid, $dir->[0]);
250     }
251     
252     fv_update_size($jobid, $rep, $size);
253     return $size;
254 }
255
256 sub fv_list_dirs
257 {
258     my ($jobid, $rep) = @_;
259
260     my $ret = $bweb->dbh_selectall_arrayref("
261       SELECT P.PathId,
262              ( SELECT Path FROM Path WHERE PathId = P.PathId) AS Path
263         FROM (
264           SELECT PathId
265             FROM brestore_pathvisibility 
266       INNER JOIN brestore_pathhierarchy USING (PathId)
267            WHERE PPathId  = $rep
268              AND JobId = $jobid
269              ) AS P
270 ");
271
272     return $ret;
273 }
274
275 sub fv_get_file_attribute
276 {
277     my ($jobid, $full_name) = @_;
278
279     if ($full_name eq '/') {
280         return {found => 0};
281     }
282     
283     my $filename = $bweb->dbh_quote(basename($full_name));
284     my $path     = $bweb->dbh_quote(dirname($full_name) . "/");
285
286     my $attr = $bweb->dbh_selectrow_hashref("
287  SELECT 1    AS found,
288         MD5  AS md5,
289         base64_decode_lstat(8,  LStat) AS size,
290         base64_decode_lstat(11, LStat) AS atime,
291         base64_decode_lstat(12, LStat) AS mtime,
292         base64_decode_lstat(13, LStat) AS ctime
293
294    FROM File JOIN Filename USING (FilenameId)
295              JOIN Path     USING (PathId)
296   WHERE Name  = $filename
297    AND  Path  = $path
298    AND  JobId = $jobid
299 ");
300
301     $attr->{filename} = $full_name;
302     $attr->{size} = Bweb::human_size($attr->{size});
303     foreach my $d (qw/atime ctime mtime/) {
304         $attr->{$d} = strftime('%F %H:%M', localtime($attr->{$d}));
305     }
306     return $attr;
307 }
308
309
310 sub fv_get_file_attribute_from_id
311 {
312     my ($jobid, $pathid, $filenameid) = @_;
313     
314     my $attr = $bweb->dbh_selectrow_hashref("
315  SELECT 1    AS found,
316         MD5  AS md5,
317         base64_decode_lstat(8,  LStat) AS size,
318         base64_decode_lstat(11, LStat) AS atime,
319         base64_decode_lstat(12, LStat) AS mtime,
320         base64_decode_lstat(13, LStat) AS ctime,
321         Path.Path ||  Filename.Name AS filename
322
323    FROM File INNER JOIN Filename USING (FilenameId)
324              INNER JOIN Path     USING (PathId)
325   WHERE FilenameId  = $filenameid
326    AND  PathId  = $pathid
327    AND  JobId = $jobid
328 ");
329
330     $attr->{size} = Bweb::human_size($attr->{size});
331     foreach my $d (qw/atime ctime mtime/) {
332         $attr->{$d} = strftime('%F %H:%M', localtime($attr->{$d}));
333     }
334     return $attr;
335 }
336
337 sub fv_get_size
338 {
339     my ($jobid, $rep) = @_;
340
341     my $ret = $bweb->dbh_selectrow_hashref("
342  SELECT Size AS size
343    FROM brestore_pathvisibility
344   WHERE PathId = $rep
345     AND JobId = $jobid
346 ");
347
348     return $ret->{size};
349 }
350
351 sub fv_get_files_size
352 {
353     my ($jobid, $rep) = @_;
354
355     my $ret = $bweb->dbh_selectrow_hashref("
356  SELECT sum(base64_decode_lstat(8,LStat)) AS size
357    FROM File
358   WHERE PathId  = $rep
359     AND JobId = $jobid
360     AND FileIndex > 0
361 ");
362
363     return $ret->{size};
364 }
365
366 sub fv_get_big_files
367 {
368     my ($jobid, $rep, $min, $limit) = @_;
369     $limit = int($limit);
370
371     my $ret = $bweb->dbh_selectall_arrayref("
372    SELECT FilenameId AS filenameid, Name AS name, size
373    FROM (
374          SELECT FilenameId, base64_decode_lstat(8,LStat) AS size
375            FROM File
376           WHERE PathId  = $rep
377             AND JobId = $jobid
378             AND FileIndex > 0
379         ) AS S INNER JOIN Filename USING (FilenameId)
380    WHERE S.size > $min
381    ORDER BY S.size DESC
382    LIMIT $limit
383 ");
384
385     return $ret;
386 }
387
388 sub fv_update_size
389 {
390     my ($jobid, $rep, $size) = @_;
391
392     my $nb = $bweb->dbh_do("
393  UPDATE brestore_pathvisibility SET Size = $size 
394   WHERE JobId = $jobid 
395     AND PathId = $rep 
396 ");
397
398     return $nb;
399 }
400
401 sub fv_get_root_pathid
402 {
403     my ($path) = @_;
404     $path = $bweb->dbh_quote($path);
405     my $ret = $bweb->dbh_selectrow_hashref("SELECT PathId FROM Path WHERE Path = $path");
406     return $ret->{pathid};
407 }
408
409 sub fv_get_root_path
410 {
411     my ($pathid) = @_;
412     my $ret = $bweb->dbh_selectrow_hashref("SELECT Path FROM Path WHERE PathId = $pathid");
413     return $ret->{path};
414 }
415
416
417 __END__
418
419 CREATE OR REPLACE FUNCTION base64_decode_lstat(int4, varchar) RETURNS int8 AS $$
420 DECLARE
421 val int8;
422 b64 varchar(64);
423 size varchar(64);
424 i int;
425 BEGIN
426 size := split_part($2, ' ', $1);
427 b64 := 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
428 val := 0;
429 FOR i IN 1..length(size) LOOP
430 val := val + (strpos(b64, substr(size, i, 1))-1) * (64^(length(size)-i));
431 END LOOP;
432 RETURN val;
433 END;
434 $$ language 'plpgsql';
435
436 ALTER TABLE brestore_pathvisibility ADD Size  int8;
437
438
439
440 ALTER TABLE brestore_pathvisibility ADD Files int4;