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