r201@klaxLaptop: klax | 2005-10-12 15:13:36 +0200
[BackupPC.git] / lib / BackupPC / SearchLib.pm
1 #!/usr/bin/perl
2 package BackupPC::SearchLib;
3
4 use strict;
5 use BackupPC::CGI::Lib qw(:all);
6 use BackupPC::Attrib qw(:all);
7 use DBI;
8 use DateTime;
9 use vars qw(%In $MyURL);
10 use Time::HiRes qw/time/;
11 use XML::Writer;
12 use IO::File;
13
14 my $on_page = 100;
15 my $pager_pages = 10;
16
17 my $dsn = $Conf{SearchDSN};
18 my $db_user = $Conf{SearchUser} || '';
19
20 my $hest_index_path = $Conf{HyperEstraierIndex};
21
22 my $dbh;
23
24 sub get_dbh {
25         $dbh ||= DBI->connect($dsn, $db_user, "", { RaiseError => 1, AutoCommit => 1 } );
26         return $dbh;
27 }
28
29 sub getUnits() {
30         my @ret;
31
32         my $dbh = get_dbh();
33         my $sth = $dbh->prepare(qq{
34                 SELECT
35                         shares.id       as id,
36                         hosts.name || ':' || shares.name as share
37                 FROM shares
38                 JOIN hosts on hostid = hosts.id
39                 ORDER BY share
40         } );
41         $sth->execute();
42         push @ret, { 'id' => '', 'share' => '-'};       # dummy any
43
44         while ( my $row = $sth->fetchrow_hashref() ) {
45                 push @ret, $row;
46         }
47         return @ret;
48 }
49
50 sub epoch_to_iso {
51         my $t = shift || return;
52         my $iso = BackupPC::Lib::timeStamp(undef, $t);
53         $iso =~ s/\s/ /g;
54         return $iso;
55 }
56
57 sub dates_from_form($) {
58         my $param = shift || return;
59
60         sub mk_epoch_date($$) {
61                 my ($name,$suffix) = @_;
62
63                 my $yyyy = $param->{ $name . '_year_' . $suffix} || return undef;
64                 my $mm .= $param->{ $name . '_month_' . $suffix} ||
65                         ( $suffix eq 'from' ? 1 : 12);
66                 my $dd .= $param->{ $name . '_day_' . $suffix} ||
67                         ( $suffix eq 'from' ? 1 : 31);
68
69                 $yyyy =~ s/\D//g;
70                 $mm =~ s/\D//g;
71                 $dd =~ s/\D//g;
72
73                 my $h = my $m = my $s = 0;
74                 if ($suffix eq 'to') {
75                         $h = 23;
76                         $m = 59;
77                         $s = 59;
78                 }
79
80                 my $dt = new DateTime(
81                         year => $yyyy,
82                         month => $mm,
83                         day => $dd,
84                         hour => $h,
85                         minute => $m,
86                         second => $s,
87                 );
88                 print STDERR "mk_epoch_date($name,$suffix) [$yyyy-$mm-$dd] = " . $dt->ymd . " " . $dt->hms . "\n";
89                 return $dt->epoch || 'NULL';
90         }
91
92         my @ret = (
93                 mk_epoch_date('search_backup', 'from'),
94                 mk_epoch_date('search_backup', 'to'),
95                 mk_epoch_date('search', 'from'),
96                 mk_epoch_date('search', 'to'),
97         );
98
99         return @ret;
100
101 }
102
103
104 sub getWhere($) {
105         my $param = shift || return;
106
107         my ($backup_from, $backup_to, $files_from, $files_to) = dates_from_form($param);
108
109         my @conditions;
110         push @conditions, qq{ backups.date >= $backup_from } if ($backup_from);
111         push @conditions, qq{ backups.date <= $backup_to } if ($backup_to);
112         push @conditions, qq{ files.date >= $files_from } if ($files_from);
113         push @conditions, qq{ files.date <= $files_to } if ($files_to);
114
115         print STDERR "backup: $backup_from - $backup_to files: $files_from - $files_to cond:" . join(" and ",@conditions);
116
117         push( @conditions, ' files.shareid = ' . $param->{'search_share'} ) if ($param->{'search_share'});
118         push (@conditions, " upper(files.path) LIKE upper('%".$param->{'search_filename'}."%')") if ($param->{'search_filename'});
119
120         return join(" and ", @conditions);
121 }
122
123
124 sub getFiles($) {
125         my ($param) = @_;
126
127         my $offset = $param->{'offset'} || 0;
128         $offset *= $on_page;
129
130         my $dbh = get_dbh();
131
132         my $sql_cols = qq{
133                 files.id                        AS fid,
134                 hosts.name                      AS hname,
135                 shares.name                     AS sname,
136                 files.backupnum                 AS backupnum,
137                 files.path                      AS filepath,
138                 files.date                      AS date,
139                 files.type                      AS type,
140                 files.size                      AS size
141         };
142
143         my $sql_from = qq{
144                 FROM files 
145                         INNER JOIN shares       ON files.shareID=shares.ID
146                         INNER JOIN hosts        ON hosts.ID = shares.hostID
147                         INNER JOIN backups      ON backups.num = files.backupnum and backups.hostID = hosts.ID AND backups.shareID = files.shareID
148         };
149
150         my $sql_where;
151         my $where = getWhere($param);
152         $sql_where = " WHERE ". $where if ($where);
153
154         my $sql_order = qq{
155                 ORDER BY files.date
156                 LIMIT $on_page
157                 OFFSET ?
158         };
159
160         my $sql_count = qq{ select count(files.id) $sql_from $sql_where };
161         my $sql_results = qq{ select $sql_cols $sql_from $sql_where $sql_order };
162
163         my $sth = $dbh->prepare($sql_count);
164         $sth->execute();
165         my ($results) = $sth->fetchrow_array();
166
167         $sth = $dbh->prepare($sql_results);
168         $sth->execute( $offset );
169
170         if ($sth->rows != $results) {
171                 my $bug = "$0 BUG: [[ $sql_count ]] = $results while [[ $sql_results ]] = " . $sth->rows;
172                 $bug =~ s/\s+/ /gs;
173                 print STDERR "$bug\n";
174         }
175
176         my @ret;
177       
178         while (my $row = $sth->fetchrow_hashref()) {
179                 push @ret, $row;
180         }
181      
182         $sth->finish();
183         return ($results, \@ret);
184 }
185
186 sub getHyperEstraier_url($) {
187         my ($use_hest) = @_;
188
189         return unless $use_hest;
190
191         use HyperEstraier;
192         my ($index_path, $index_node_url);
193
194         if ($use_hest =~ m#^http://#) {
195                 $index_node_url = $use_hest;
196         } else {
197                 $index_path = $TopDir . '/' . $index_path;
198                 $index_path =~ s#//#/#g;
199         }
200         return ($index_path, $index_node_url);
201 }
202
203 sub getFilesHyperEstraier($) {
204         my ($param) = @_;
205
206         my $offset = $param->{'offset'} || 0;
207         $offset *= $on_page;
208
209         die "no index_path?" unless ($hest_index_path);
210
211         use HyperEstraier;
212
213         my ($index_path, $index_node_url) = getHyperEstraier_url($hest_index_path);
214
215         # open the database
216         my $db;
217         if ($index_path) {
218                 $db = HyperEstraier::Database->new();
219                 $db->open($index_path, $HyperEstraier::ESTDBREADER);
220         } elsif ($index_node_url) {
221                 $db ||= HyperEstraier::Node->new($index_node_url);
222                 $db->set_auth('admin', 'admin');
223         } else {
224                 die "BUG: unimplemented";
225         }
226
227         # create a search condition object
228         my $cond = HyperEstraier::Condition->new();
229
230         my $q = $param->{'search_filename'};
231         my $shareid = $param->{'search_share'};
232
233         if (length($q) > 0) {
234                 # exact match
235                 $cond->add_attr("filepath ISTRINC $q");
236
237                 $q =~ s/(.)/$1 /g;
238                 # set the search phrase to the search condition object
239                 $cond->set_phrase($q);
240         }
241
242         my ($backup_from, $backup_to, $files_from, $files_to) = dates_from_form($param);
243
244         $cond->add_attr("backup_date NUMGE $backup_from") if ($backup_from);
245         $cond->add_attr("backup_date NUMLE $backup_to") if ($backup_to);
246
247         $cond->add_attr("date NUMGE $files_from") if ($files_from);
248         $cond->add_attr("date NUMLE $files_to") if ($files_to);
249
250         $cond->add_attr("shareid NUMEQ $shareid") if ($shareid);
251
252 #       $cond->set_max( $offset + $on_page );
253         $cond->set_options( $HyperEstraier::Condition::SURE );
254         $cond->set_order( 'date NUMA' );
255
256         # get the result of search
257         my @res;
258         my ($result, $hits);
259
260         if ($index_path) {
261                 $result = $db->search($cond, 0);
262                 $hits = $result->size;
263         } elsif ($index_node_url) {
264                 $result = $db->search($cond, 0);
265                 $hits = $result->doc_num;
266         } else {
267                 die "BUG: unimplemented";
268         }
269
270         # for each document in result
271         for my $i ($offset .. ($offset + $on_page - 1)) {
272                 last if ($i >= $hits);
273
274                 my $doc;
275                 if ($index_path) {
276                         my $id = $result->get($i);
277                         $doc = $db->get_doc($id, 0);
278                 } elsif ($index_node_url) {
279                         $doc = $result->get_doc($i);
280                 } else {
281                         die "BUG: unimplemented";
282                 }
283
284                 my $row;
285                 foreach my $c (qw/fid hname sname backupnum fiilename filepath date type size/) {
286                         $row->{$c} = $doc->attr($c);
287                 }
288                 push @res, $row;
289         }
290
291         return ($hits, \@res);
292 }
293
294 sub getGzipName($$$)
295 {
296         my ($host, $share, $backupnum) = @_;
297         my $ret = $Conf{GzipSchema};
298         
299         $share =~ s/\//_/g;
300         $ret =~ s/\\h/$host/ge;
301         $ret =~ s/\\s/$share/ge;
302         $ret =~ s/\\n/$backupnum/ge;
303
304         $ret =~ s/__+/_/g;
305
306         return $ret;
307         
308 }
309
310 sub getGzipSize($$)
311 {
312         my ($hostID, $backupNum) = @_;
313         my $sql;
314         my $dbh = get_dbh();
315         
316         $sql = q{ 
317                                 SELECT hosts.name  as host,
318                                            shares.name as share,
319                                            backups.num as backupnum
320                                 FROM hosts, backups, shares
321                                 WHERE shares.id=backups.shareid AND
322                                           hosts.id =backups.hostid AND
323                                           hosts.id=? AND
324                                           backups.num=?
325                         };
326         my $sth = $dbh->prepare($sql);
327         $sth->execute($hostID, $backupNum);
328
329         my $row = $sth->fetchrow_hashref();
330         
331         my (undef,undef,undef,undef,undef,undef,undef,$ret,undef,undef,undef,undef,undef) = 
332                         stat( $Conf{InstallDir}.'/'.$Conf{GzipTempDir}.'/'.
333                                 getGzipName($row->{'host'}, $row->{share}, $row->{'backupnum'}));
334         
335         return $ret;    
336 }
337
338 sub getBackupsNotBurned() {
339
340         my $dbh = get_dbh();
341
342         my $sql = q{
343                 SELECT 
344                         backups.hostID AS hostID,
345                         hosts.name AS host,
346                         shares.name AS share,
347                         backups.num AS backupnum,
348                         backups.type AS type,
349                         backups.date AS date,
350                         backups.size AS size,
351                         backups.id AS id,
352                         backups.inc_size AS inc_size
353                 FROM backups 
354                 INNER JOIN shares       ON backups.shareID=shares.ID
355                 INNER JOIN hosts        ON backups.hostID = hosts.ID
356                 LEFT OUTER JOIN archive_backup ON archive_backup.backup_id = backups.id 
357                 WHERE backups.inc_size > 0 AND backups.inc_deleted is false AND archive_backup.backup_id IS NULL
358                 GROUP BY
359                         backups.hostID,
360                         hosts.name,
361                         shares.name,
362                         backups.num,
363                         backups.shareid,
364                         backups.id,
365                         backups.type,
366                         backups.date,
367                         backups.size,
368                         backups.inc_size
369                 ORDER BY backups.date
370         };
371         my $sth = $dbh->prepare( $sql );
372         my @ret;
373         $sth->execute();
374
375         while ( my $row = $sth->fetchrow_hashref() ) {
376                 $row->{'age'} = sprintf("%0.1f", ( (time() - $row->{'date'}) / 86400 ) );
377                 $row->{'size'} = sprintf("%0.2f", $row->{'size'} / 1024 / 1024);
378
379                 # do some cluster calculation (approximate) and convert to kB
380                 $row->{'inc_size'} = int(($row->{'inc_size'} + 1023 ) / ( 2 * 1024 ) * 2);
381                 push @ret, $row;
382         }
383       
384         return @ret;      
385 }
386
387 sub displayBackupsGrid() {
388
389         my $retHTML .= q{
390                 <form id="forma" method="POST" action="}.$MyURL.q{?action=burn">
391         };
392
393         $retHTML .= <<'EOF3';
394 <style type="text/css">
395 <!--
396 DIV#fixedBox {
397         position: absolute;
398         top: 50em;
399         left: -24%;
400         padding: 0.5em;
401         width: 20%;
402         background-color: #E0F0E0;
403         border: 1px solid #00C000;
404 }
405
406 DIV#fixedBox, DIV#fixedBox INPUT, DIV#fixedBox TEXTAREA {
407         font-size: 10pt;
408 }
409
410 FORM>DIV#fixedBox {
411         position: fixed !important;
412         left: 0.5em !important;
413         top: auto !important;
414         bottom: 1em !important;
415         width: 15% !important;
416 }
417
418 DIV#fixedBox INPUT[type=text], DIV#fixedBox TEXTAREA {
419         border: 1px solid #00C000;
420 }
421
422 DIV#fixedBox #note {
423         display: block;
424         width: 100%;
425 }
426
427 DIV#fixedBox #submitBurner {
428         display: block;
429         width: 100%;
430         margin-top: 0.5em;
431         cursor: pointer;
432 }
433
434 * HTML {
435         overflow-y: hidden;
436 }
437
438 * HTML BODY {
439         overflow-y: auto;
440         height: 100%;
441         font-size: 100%;
442 }
443
444 * HTML DIV#fixedBox {
445         position: absolute;
446 }
447
448 #mContainer, #gradient, #mask, #progressIndicator {
449         display: block;
450         width: 100%;
451         font-size: 10pt;
452         font-weight: bold;
453         text-align: center;
454         vertical-align: middle;
455         padding: 1px;
456 }
457
458 #gradient, #mask, #progressIndicator {
459         left: 0;
460         border-width: 1px;
461         border-style: solid;
462         border-color: #000000;
463         color: #404040;
464         margin: 0.4em;
465         position: absolute;
466         margin-left: -1px;
467         margin-top: -1px;
468         margin-bottom: -1px;
469         overflow: hidden;
470 }
471
472 #mContainer {
473         display: block;
474         position: relative;
475         padding: 0px;
476         margin-top: 0.4em;
477         margin-bottom: 0.5em;
478 }
479
480 #gradient {
481         z-index: 1;
482         background-color: #FFFF00;
483 }
484
485 #mask {
486         z-index: 2;
487         background-color: #FFFFFF;
488 }
489
490 #progressIndicator {
491         z-index: 3;
492         background-color: transparent;
493 }
494 -->
495 </style>
496 <script type="text/javascript">
497 <!--
498
499 var debug_div = null;
500 EOF3
501
502         # take maximum archive size from configuration
503         $retHTML .= 'var media_size = '. $Conf{MaxArchiveSize} .';';
504
505         $retHTML .= <<'EOF3';
506
507 function debug(msg) {
508 //      return; // Disable debugging
509
510         if (! debug_div) debug_div = document.getElementById('debug');
511
512         // this will create debug div if it doesn't exist.
513         if (! debug_div) {
514                 debug_div = document.createElement('div');
515                 if (document.body) document.body.appendChild(debug_div);
516                 else debug_div = null;
517         }
518         if (debug_div) {
519                 debug_div.appendChild(document.createTextNode(msg));
520                 debug_div.appendChild(document.createElement("br"));
521         }
522 }
523
524
525 var element_id_cache = Array();
526
527 function element_id(name,element) {
528         if (! element_id_cache[name]) {
529                 element_id_cache[name] = self.document.getElementById(name);
530         }
531         return element_id_cache[name];
532 }
533
534 function checkAll(location) {
535         var f = element_id('forma') || null;
536         if (!f) return false;
537
538         var len = f.elements.length;
539         var check_all = element_id('allFiles');
540         var suma = check_all.checked ? (parseInt(f.elements['totalsize'].value) || 0) : 0;
541
542         for (var i = 0; i < len; i++) {
543                 var e = f.elements[i];
544                 if (e.name != 'all' && e.name.substr(0, 3) == 'fcb') {
545                         if (check_all.checked) {
546                                 if (e.checked) continue;
547                                 var el = element_id("fss" + e.name.substr(3));
548                                 var size = parseInt(el.value) || 0;
549                                 debug('suma: '+suma+' size: '+size);
550                                 if ((suma + size) < media_size) {
551                                         suma += size;
552                                         e.checked = true;
553                                 } else {
554                                         break;
555                                 }
556                         } else {
557                                 e.checked = false;
558                         }
559                 }
560         }
561         update_sum(suma);
562 }
563
564 function update_sum(suma) {
565         element_id('forma').elements['totalsize'].value = suma;
566         pbar_set(suma, media_size);
567         debug('total size: ' + suma);
568 }
569
570 function sumiraj(e) {
571         var suma = parseInt(element_id('forma').elements['totalsize'].value) || 0;
572         var len = element_id('forma').elements.length;
573         if (e) {
574                 var size = parseInt( element_id("fss" + e.name.substr(3)).value);
575                 if (e.checked) {
576                         suma += size;
577                 } else {
578                         suma -= size;
579                 }
580         } else {
581                 suma = 0;
582                 for (var i = 0; i < len; i++) {
583                         var e = element_id('forma').elements[i];
584                         if (e.name != 'all' && e.checked && e.name.substr(0,3) == 'fcb') {
585                                 var el = element_id("fss" + e.name.substr(3));
586                                 if (el && el.value) suma += parseInt(el.value) || 0;
587                         }
588                 }
589         }
590         update_sum(suma);
591         return suma;
592 }
593
594 /* progress bar */
595
596 var _pbar_width = null;
597 var _pbar_warn = 10;    // change color in last 10%
598
599 function pbar_reset() {
600         element_id("mask").style.left = "0px";
601         _pbar_width = element_id("mContainer").offsetWidth - 2;
602         element_id("mask").style.width = _pbar_width + "px";
603         element_id("mask").style.display = "block";
604         element_id("progressIndicator").style.zIndex  = 10;
605         element_id("progressIndicator").innerHTML = "0";
606 }
607
608 function dec2hex(d) {
609         var hch = '0123456789ABCDEF';
610         var a = d % 16;
611         var q = (d - a) / 16;
612         return hch.charAt(q) + hch.charAt(a);
613 }
614
615 function pbar_set(amount, max) {
616         debug('pbar_set('+amount+', '+max+')');
617
618         if (_pbar_width == null) {
619                 var _mc = element_id("mContainer");
620                 if (_pbar_width == null) _pbar_width = parseInt(_mc.offsetWidth ? (_mc.offsetWidth - 2) : 0) || null;
621                 if (_pbar_width == null) _pbar_width = parseInt(_mc.clientWidth ? (_mc.clientWidth + 2) : 0) || null;
622                 if (_pbar_width == null) _pbar_width = 0;
623         }
624
625         var pcnt = Math.floor(amount * 100 / max);
626         var p90 = 100 - _pbar_warn;
627         var pcol = pcnt - p90;
628         if (Math.round(pcnt) <= 100) {
629                 if (pcol < 0) pcol = 0;
630                 var e = element_id("submitBurner");
631                 debug('enable_button');
632                 e.disabled = false;
633                 var a = e.getAttributeNode('disabled') || null;
634                 if (a) e.removeAttributeNode(a);
635         } else {
636                 debug('disable button');
637                 pcol = _pbar_warn;
638                 var e = element_id("submitBurner");
639                 if (!e.disabled) e.disabled = true;
640         }
641         var col_g = Math.floor((_pbar_warn - pcol) * 255 / _pbar_warn);
642         var col = '#FF' + dec2hex(col_g) + '00';
643
644         //debug('pcol: '+pcol+' g:'+col_g+' _pbar_warn:'+ _pbar_warn + ' color: '+col);
645         element_id("gradient").style.backgroundColor = col;
646
647         element_id("progressIndicator").innerHTML = pcnt + '%';
648         //element_id("progressIndicator").innerHTML = amount;
649
650         element_id("mask").style.clip = 'rect(' + Array(
651                 '0px',
652                 element_id("mask").offsetWidth + 'px',
653                 element_id("mask").offsetHeight + 'px',
654                 Math.round(_pbar_width * amount / max) + 'px'
655         ).join(' ') + ')';
656 }
657
658 if (!self.body) self.body = new Object();
659 self.onload = self.document.onload = self.body.onload = function() {
660         //pbar_reset();
661         sumiraj();
662 };
663
664 // -->
665 </script>
666 <div id="fixedBox">
667
668 Size: <input type="text" name="totalsize" size="7" readonly="readonly" style="text-align:right;" value="0" /> kB
669
670 <div id="mContainer">
671         <div id="gradient">&nbsp;</div>
672         <div id="mask">&nbsp;</div>
673         <div id="progressIndicator">0%</div>
674 </div>
675 <br/>
676
677 Note:
678 <textarea name="note" cols="10" rows="5" id="note"></textarea>
679
680 <input type="submit" id="submitBurner" value="Burn selected" name="submitBurner" />
681
682 </div>
683 <!--
684 <div id="debug" style="float: right; width: 10em; border: 1px #ff0000 solid; background-color: #ffe0e0; -moz-opacity: 0.7;">
685 no debug output yet
686 </div>
687 -->
688 EOF3
689         $retHTML .= q{
690                         <input type="hidden" value="burn" name="action">
691                         <input type="hidden" value="results" name="search_results">
692                         <table style="fview" border="0" cellspacing="0" cellpadding="2">
693                         <tr class="tableheader">
694                         <td class="tableheader">
695                                 <input type="checkbox" name="allFiles" id="allFiles" onClick="checkAll('allFiles');">
696                         </td>
697                         <td align="center">Share</td>
698                         <td align="center">Backup no</td>
699                         <td align="center">Type</td>
700                         <td align="center">date</td>
701                         <td align="center">age/days</td>
702                         <td align="center">size/MB</td>
703                         <td align="center">gzip size/kB</td>
704                         </tr>
705
706         };
707
708         my @color = (' bgcolor="#e0e0e0"', '');
709
710         my $i = 0;
711         my $host = '';
712
713         foreach my $backup ( getBackupsNotBurned() ) {
714
715                 if ($host ne $backup->{'host'}) {
716                         $i++;
717                         $host = $backup->{'host'};
718                 }
719                 my $ftype = "";
720
721                 my $checkbox_key = $backup->{'hostid'}. '_' .$backup->{'backupnum'} . '_' . $backup->{'id'};
722
723                 $retHTML .=
724                         '<tr' . $color[$i %2 ] . '>
725                         <td class="fview">';
726
727                 if (($backup->{'inc_size'} || 0) > 0) {
728                         $retHTML .= '
729                         <input type="checkbox" name="fcb' . $checkbox_key . '" value="' . $checkbox_key . '" onClick="sumiraj(this);">';
730                 }
731
732                 $retHTML .=
733                         '</td>' .
734                         '<td align="right">' . $backup->{'host'} . ':' . $backup->{'share'} . '</td>' .
735                         '<td align="center">' . $backup->{'backupnum'} . '</td>' .
736                         '<td align="center">' . $backup->{'type'} . '</td>' .
737                         '<td align="center">' . epoch_to_iso( $backup->{'date'} ) . '</td>' .
738                         '<td align="center">' . $backup->{'age'} . '</td>' .
739                         '<td align="right">' . $backup->{'size'} . '</td>' .
740                         '<td align="right">' . $backup->{'inc_size'} .
741                         '<input type="hidden" iD="fss'.$checkbox_key .'" value="'. $backup->{'inc_size'} .'"></td>' .
742
743                         "</tr>\n";
744         }
745
746         $retHTML .= "</table>";
747         $retHTML .= "</form>";
748       
749         return $retHTML;
750 }      
751
752 sub displayGrid($) {
753         my ($param) = @_;
754
755         my $offset = $param->{'offset'};
756         my $hilite = $param->{'search_filename'};
757
758         my $retHTML = "";
759  
760         my $start_t = time();
761
762         my ($results, $files);
763         if ($param->{'use_hest'} && length($hilite) > 0) {
764                 ($results, $files) = getFilesHyperEstraier($param);
765         } else {
766                 ($results, $files) = getFiles($param);
767         }
768
769         my $dur_t = time() - $start_t;
770         my $dur = sprintf("%0.4fs", $dur_t);
771
772         my ($from, $to) = (($offset * $on_page) + 1, ($offset * $on_page) + $on_page);
773
774         if ($results <= 0) {
775                 $retHTML .= qq{
776                         <p style="color: red;">No results found...</p>
777                 };
778                 return $retHTML;
779         } else {
780                 # DEBUG
781                 #use Data::Dumper;
782                 #$retHTML .= '<pre>' . Dumper($files) . '</pre>';
783         }
784
785
786         $retHTML .= qq{
787         <div>
788         Found <b>$results files</b> showing <b>$from - $to</b> (took $dur)
789         </div>
790         <table style="fview" width="100%" border="0" cellpadding="2" cellspacing="0">
791                 <tr class="fviewheader"> 
792                 <td></td>
793                 <td align="center">Share</td>
794                 <td align="center">Type and Name</td>
795                 <td align="center">#</td>
796                 <td align="center">Size</td>
797                 <td align="center">Date</td>
798                 <td align="center">Media</td>
799                 </tr>
800         };
801
802         my $file;
803
804         sub hilite_html($$) {
805                 my ($html, $search) = @_;
806                 $html =~ s#($search)#<b>$1</b>#gis;
807                 return $html;
808         }
809
810         sub restore_link($$$$$$) {
811                 my $type = shift;
812                 my $action = 'RestoreFile';
813                 $action = 'browse' if (lc($type) eq 'dir');
814                 return sprintf(qq{<a href="?action=%s&host=%s&num=%d&share=%s&dir=%s">%s</a>}, $action, @_);
815         }
816
817         my $i = $offset * $on_page;
818
819         foreach $file (@{ $files }) {
820                 $i++;
821
822                 my $typeStr  = BackupPC::Attrib::fileType2Text(undef, $file->{'type'});
823                 $retHTML .= qq{<tr class="fviewborder">};
824
825                 $retHTML .= qq{<td class="fviewborder">$i</td>};
826
827                 $retHTML .=
828                         qq{<td class="fviewborder" align="right">} . $file->{'hname'} . ':' . $file->{'sname'} . qq{</td>} .
829                         qq{<td class="fviewborder"><img src="$Conf{CgiImageDirURL}/icon-$typeStr.gif" alt="$typeStr" align="middle">&nbsp;} . hilite_html( $file->{'filepath'}, $hilite ) . qq{</td>} .
830                         qq{<td class="fviewborder" align="center">} . restore_link( $typeStr, ${EscURI( $file->{'hname'} )}, $file->{'backupnum'}, ${EscURI( $file->{'sname'})}, ${EscURI( $file->{'filepath'} )}, $file->{'backupnum'} ) . qq{</td>} .
831                         qq{<td class="fviewborder" align="right">} . $file->{'size'} . qq{</td>} .
832                         qq{<td class="fviewborder">} . epoch_to_iso( $file->{'date'} ) . qq{</td>} .
833                         qq{<td class="fviewborder">} . '?' . qq{</td>};
834
835                 $retHTML .= "</tr>";
836         }
837         $retHTML .= "</table>";
838
839         # all variables which has to be transfered
840         foreach my $n (qw/search_day_from search_month_from search_year_from search_day_to search_month_to search_year_to search_backup_day_from search_backup_month_from search_backup_year_from search_backup_day_to search_backup_month_to search_backup_year_to search_filename offset/) {
841                 $retHTML .= qq{<INPUT TYPE="hidden" NAME="$n" VALUE="$In{$n}">\n};
842         }
843
844         my $del = '';
845         my $max_page = int( $results / $on_page );
846         my $page = 0;
847
848         sub page_link($$$) {
849                 my ($param,$page,$display) = @_;
850
851                 $param->{'offset'} = $page;
852
853                 my $html = '<a href = "' . $MyURL;
854                 my $del = '?';
855                 foreach my $k (keys %{ $param }) {
856                         if ($param->{$k}) {
857                                 $html .= $del . $k . '=' . ${EscURI( $param->{$k} )};
858                                 $del = '&';
859                         }
860                 }
861                 $html .= '">' . $display . '</a>';
862         }
863
864         $retHTML .= '<div style="text-align: center;">';
865
866         if ($offset > 0) {
867                 $retHTML .= page_link($param, $offset - 1, '&lt;&lt;') . ' ';
868         }
869
870         while ($page <= $max_page) {
871                 if ($page == $offset) {
872                         $retHTML .= $del . '<b>' . ($page + 1) . '</b>';
873                 } else {
874                         $retHTML .= $del . page_link($param, $page, $page + 1);
875                 }
876
877                 if ($page < $offset - $pager_pages && $page != 0) {
878                         $retHTML .= " ... ";
879                         $page = $offset - $pager_pages;
880                         $del = '';
881                 } elsif ($page > $offset + $pager_pages && $page != $max_page) {
882                         $retHTML .= " ... ";
883                         $page = $max_page;
884                         $del = '';
885                 } else {
886                         $del = ' | ';
887                         $page++;
888                 }
889         }
890
891         if ($offset < $max_page) {
892                 $retHTML .= ' ' . page_link($param, $offset + 1, '&gt;&gt;');
893         }
894
895         $retHTML .= "</div>";
896
897         return $retHTML;
898 }
899
900 sub dumpArchive2XML($$)
901 {
902         my ($arcID, $filename) = @_;
903         my $dbh;
904         my $sth;
905         my $sth_backups;
906         my $sth_files;
907         my $row;
908         my $row_backups;
909         my $row_files;
910         my %archive;    
911         my $output = new IO::File(">$filename");
912         my $writer = new XML::Writer(OUTPUT=>$output, NEWLINES => 1);
913
914
915         $dbh = get_dbh();
916 #       my $bpc = BackupPC::Lib->new(undef, undef, 1) || die;
917 #       my %Conf = $bpc->Conf();
918
919 #       my $dsn = $Conf{SearchDSN} || die "Need SearchDSN in config.pl\n";
920 #       my $user = $Conf{SearchUser} || '';
921
922         my $files_sql = q{
923                                                         SELECT 
924                                                                 files.name AS filename,
925                                                                 files.path AS filepath,
926                                                                 files.date AS filedate,
927                                                                 files.type AS filetype,
928                                                                 files.size AS filesize
929                                                         FROM files, backups, shares
930                                                         WHERE files.backupnum=backups.num AND
931                                                                   files.shareid=shares.id AND
932                                                                   shares.hostid=backups.hostid AND
933                                                                   backups.id=?                                                            
934                                                  };
935
936         my $backups_sql = q{
937                                                         SELECT backups.id   AS backupid,
938                                                                         hosts.name   AS hostname,
939                                                                backups.num  AS backupnum,
940                                                                backups.date AS backupdate,
941                                                                backups.type AS backuptype,
942                                                                shares.name  AS sharename,
943                                                                backups.size AS backupsize,
944                                                                backups.inc_size AS inc_size,
945                                                                backups.inc_deleted AS inc_deleted
946                                                         FROM backups, archive_backup, hosts, shares
947                                                         WHERE archive_backup.backup_id = backups.id
948                                                                AND hosts.id=backups.hostid
949                                                                AND shares.id=backups.shareid
950                                                                AND archive_backup.archive_id = ?
951                                               };
952         
953         $dbh = DBI->connect($dsn, $user, "", { RaiseError => 1, AutoCommit => 1 });
954
955         $sth = $dbh->prepare("SELECT dvd_nr, total_size, note, username, date FROM archive WHERE ID=?");
956         $sth->execute($arcID);
957         $row = $sth->fetchrow_hashref();
958         $writer->startTag("archive", "dvd_nr"     => $row->{'dvd_nr'},
959                                      "total_size" => $row->{'total_size'},
960                                      "username"   => $row->{'username'},
961                                      "date"       => $row->{'date'}
962                          );
963
964
965         $writer->startTag("note");
966         $writer->characters( $row->{'note'});
967         $writer->endTag("note");
968         $sth_backups = $dbh->prepare( $backups_sql );
969         $sth_backups->execute($arcID);
970         while ($row_backups = $sth_backups->fetchrow_hashref())
971         {
972                 $writer->startTag("backup", 
973                                         "host" => $row_backups->{'hostname'},
974                                                     "num"  => $row_backups->{'backupnum'},
975                                                     "date" => $row_backups->{'backupdate'},
976                                                     "type" => $row_backups->{'backuptype'},
977                                                     "share"=> $row_backups->{'sharename'},
978                                                     "size" => $row_backups->{'backupsize'},
979                                                     "inc_size" => $row_backups->{'inc_size'},
980                                                     "inc_deleted" => $row_backups->{'inc_deleted'}
981                                  );
982                                  
983                 $sth_files = $dbh->prepare(
984                                                         $files_sql
985                                          );
986                 $sth_files->execute($row_backups->{'backupid'});
987                 while ($row_files = $sth_files->fetchrow_hashref())
988                 {
989                         $writer->startTag("file", 
990                                                                  "path" => $row_files->{'filepath'},
991                                                                  "date" => $row_files->{'filedate'},
992                                                                  "type" => $row_files->{'filetype'},
993                                                                  "size" => $row_files->{'filesize'}
994                                                         );
995                         $writer->characters( $row_files->{'filename'});
996                         $writer->endTag("file");
997                 }
998                 $writer->endTag("backup");
999         }       
1000                          
1001         $writer->endTag("archive");
1002         $writer->end(); 
1003         
1004 }
1005
1006 1;