r157@klaxLaptop: klax | 2005-10-07 12:28:46 +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
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         return $ret;
293         
294 }
295
296 sub getGzipSize($$)
297 {
298         my ($hostID, $backupNum) = @_;
299         my $ret;
300         my $sql;
301         my $dbh = get_dbh();
302         
303         $sql = q{ 
304                                 SELECT hosts.name  as host,
305                                            shares.name as share,
306                                            backups.num as backupnum
307                                 FROM hosts, backups, shares
308                                 WHERE shares.id=backups.shareid AND
309                                           hosts.id =backups.hostid AND
310                                           hosts.id=? AND
311                                           backups.num=?;
312                         };
313         my $sth = $dbh->prepare($sql);
314         $sth->execute($hostID, $backupNUM);
315         my $row = $res->fetchrow_hashref();
316         
317         my (undef,undef,undef,undef,undef,undef,undef,$ret,undef,undef,undef,undef,undef) = 
318                         stat( $Conf{InstallDir}.'/'.$Conf{GzipTempDir}.'/'.
319                                 getGzipName($row->{'host'}, $row->{share}, $row->{'backupnum'}));
320         
321         return $ret;    
322 }
323
324 sub getBackupsNotBurned() {
325
326         my $dbh = get_dbh();
327
328         my $sql = q{
329                 SELECT 
330                         backups.hostID AS hostID,
331                         hosts.name AS host,
332                         shares.name AS share,
333                         backups.id AS backupnum,
334                         backups.type AS type,
335                         backups.date AS date,
336                         backups.size AS size
337                 FROM backups 
338                 INNER JOIN shares       ON backups.shareID=shares.ID
339                 INNER JOIN hosts        ON backups.hostID = hosts.ID
340                 LEFT OUTER JOIN archive_backup ON archive_backup.backup_id = backups.id 
341                 WHERE backups.size > 0 AND archive_backup.backup_id IS NULL
342                 GROUP BY
343                         backups.hostID,
344                         hosts.name,
345                         shares.name,
346                         backups.num,
347                         backups.shareid,
348                         backups.id,
349                         backups.type,
350                         backups.date,
351                         backups.size
352                 ORDER BY backups.date
353         };
354         my $sth = $dbh->prepare( $sql );
355         my @ret;
356         $sth->execute();
357
358         while ( my $row = $sth->fetchrow_hashref() ) {
359                 $row->{'age'} = sprintf("%0.1f", ( (time() - $row->{'date'}) / 86400 ) );
360                 $row->{'size'} = sprintf("%0.2f", $row->{'size'} / 1024 / 1024);
361                 my (undef,undef,undef,undef,undef,undef,undef,$fs_size,undef,undef,undef,undef,undef) = 
362                         stat( $Conf{InstallDir}.'/'.$Conf{GzipTempDir}.'/'.
363                                 getGzipName($row->{'host'}, $row->{share}, $row->{'backupnum'}));
364                 $row->{'fs_size'} = $fs_size;
365                 push @ret, $row;
366         }
367       
368         return @ret;      
369 }
370
371 sub displayBackupsGrid() {
372
373         my $retHTML .= q{
374                 <form id="forma" method="POST" action="}.$MyURL.q{?action=burn">
375         };
376
377         $retHTML .= <<'EOF3';
378 <style type="text/css">
379 <!--
380 DIV#fixedBox {
381         position: absolute;
382         top: 50em;
383         left: -24%;
384         padding: 0.5em;
385         width: 20%;
386         background-color: #E0F0E0;
387         border: 1px solid #00C000;
388 }
389
390 DIV#fixedBox, DIV#fixedBox INPUT, DIV#fixedBox TEXTAREA {
391         font-size: 10pt;
392 }
393
394 FORM>DIV#fixedBox {
395         position: fixed !important;
396         left: 0.5em !important;
397         top: auto !important;
398         bottom: 1em !important;
399         width: 15% !important;
400 }
401
402 DIV#fixedBox INPUT[type=text], DIV#fixedBox TEXTAREA {
403         border: 1px solid #00C000;
404 }
405
406 DIV#fixedBox #note {
407         display: block;
408         width: 100%;
409 }
410
411 DIV#fixedBox #submitBurner {
412         display: block;
413         width: 100%;
414         margin-top: 0.5em;
415         cursor: pointer;
416 }
417
418 * HTML {
419         overflow-y: hidden;
420 }
421
422 * HTML BODY {
423         overflow-y: auto;
424         height: 100%;
425         font-size: 100%;
426 }
427
428 * HTML DIV#fixedBox {
429         position: absolute;
430 }
431
432 #mContainer, #gradient, #mask, #progressIndicator {
433         display: block;
434         width: 100%;
435         font-size: 10pt;
436         font-weight: bold;
437         text-align: center;
438         vertical-align: middle;
439         padding: 1px;
440 }
441
442 #gradient, #mask, #progressIndicator {
443         left: 0;
444         border-width: 1px;
445         border-style: solid;
446         border-color: #000000;
447         color: #404040;
448         margin: 0.4em;
449         position: absolute;
450         margin-left: -1px;
451         margin-top: -1px;
452         margin-bottom: -1px;
453         overflow: hidden;
454 }
455
456 #mContainer {
457         display: block;
458         position: relative;
459         padding: 0px;
460         margin-top: 0.4em;
461         margin-bottom: 0.5em;
462 }
463
464 #gradient {
465         z-index: 1;
466         background-color: #FFFF00;
467 }
468
469 #mask {
470         z-index: 2;
471         background-color: #FFFFFF;
472 }
473
474 #progressIndicator {
475         z-index: 3;
476         background-color: transparent;
477 }
478 -->
479 </style>
480 <script type="text/javascript">
481 <!--
482
483 var debug_div = null;
484 var media_size = 4400 * 1024;
485
486 function debug(msg) {
487 //      return; // Disable debugging
488
489         if (! debug_div) debug_div = document.getElementById('debug');
490
491         // this will create debug div if it doesn't exist.
492         if (! debug_div) {
493                 debug_div = document.createElement('div');
494                 if (document.body) document.body.appendChild(debug_div);
495                 else debug_div = null;
496         }
497         if (debug_div) {
498                 debug_div.appendChild(document.createTextNode(msg));
499                 debug_div.appendChild(document.createElement("br"));
500         }
501 }
502
503
504 var element_id_cache = Array();
505
506 function element_id(name,element) {
507         if (! element_id_cache[name]) {
508                 element_id_cache[name] = self.document.getElementById(name);
509         }
510         return element_id_cache[name];
511 }
512
513 function checkAll(location) {
514         var f = element_id('forma') || null;
515         if (!f) return false;
516
517         var len = f.elements.length;
518         var check_all = element_id('allFiles');
519         var suma = check_all.checked ? (parseInt(f.elements['totalsize'].value) || 0) : 0;
520
521         for (var i = 0; i < len; i++) {
522                 var e = f.elements[i];
523                 if (e.name != 'all' && e.name.substr(0, 3) == 'fcb') {
524                         if (check_all.checked) {
525                                 if (e.checked) continue;
526                                 var el = element_id("fss" + e.name.substr(3));
527                                 var size = parseInt(el.value) || 0;
528                                 debug('suma: '+suma+' size: '+size);
529                                 if ((suma + size) < media_size) {
530                                         suma += size;
531                                         e.checked = true;
532                                 } else {
533                                         break;
534                                 }
535                         } else {
536                                 e.checked = false;
537                         }
538                 }
539         }
540         update_sum(suma);
541 }
542
543 function update_sum(suma) {
544         element_id('forma').elements['totalsize'].value = suma;
545         pbar_set(suma, media_size);
546         debug('total size: ' + suma);
547 }
548
549 function sumiraj(e) {
550         var suma = parseInt(element_id('forma').elements['totalsize'].value) || 0;
551         var len = element_id('forma').elements.length;
552         if (e) {
553                 var size = parseInt( element_id("fss" + e.name.substr(3)).value);
554                 if (e.checked) {
555                         suma += size;
556                 } else {
557                         suma -= size;
558                 }
559         } else {
560                 suma = 0;
561                 for (var i = 0; i < len; i++) {
562                         var e = element_id('forma').elements[i];
563                         if (e.name != 'all' && e.checked && e.name.substr(0,3) == 'fcb') {
564                                 var el = element_id("fss" + e.name.substr(3));
565                                 if (el && el.value) suma += parseInt(el.value) || 0;
566                         }
567                 }
568         }
569         update_sum(suma);
570         return suma;
571 }
572
573 /* progress bar */
574
575 var _pbar_width = null;
576 var _pbar_warn = 10;    // change color in last 10%
577
578 function pbar_reset() {
579         element_id("mask").style.left = "0px";
580         _pbar_width = element_id("mContainer").offsetWidth - 2;
581         element_id("mask").style.width = _pbar_width + "px";
582         element_id("mask").style.display = "block";
583         element_id("progressIndicator").style.zIndex  = 10;
584         element_id("progressIndicator").innerHTML = "0";
585 }
586
587 function dec2hex(d) {
588         var hch = '0123456789ABCDEF';
589         var a = d % 16;
590         var q = (d - a) / 16;
591         return hch.charAt(q) + hch.charAt(a);
592 }
593
594 function pbar_set(amount, max) {
595         debug('pbar_set('+amount+', '+max+')');
596
597         if (_pbar_width == null) {
598                 var _mc = element_id("mContainer");
599                 if (_pbar_width == null) _pbar_width = parseInt(_mc.offsetWidth ? (_mc.offsetWidth - 2) : 0) || null;
600                 if (_pbar_width == null) _pbar_width = parseInt(_mc.clientWidth ? (_mc.clientWidth + 2) : 0) || null;
601                 if (_pbar_width == null) _pbar_width = 0;
602         }
603
604         var pcnt = Math.floor(amount * 100 / max);
605         var p90 = 100 - _pbar_warn;
606         var pcol = pcnt - p90;
607         if (Math.round(pcnt) <= 100) {
608                 if (pcol < 0) pcol = 0;
609                 var e = element_id("submitBurner");
610                 debug('enable_button');
611                 e.disabled = false;
612                 var a = e.getAttributeNode('disabled') || null;
613                 if (a) e.removeAttributeNode(a);
614         } else {
615                 debug('disable button');
616                 pcol = _pbar_warn;
617                 var e = element_id("submitBurner");
618                 if (!e.disabled) e.disabled = true;
619         }
620         var col_g = Math.floor((_pbar_warn - pcol) * 255 / _pbar_warn);
621         var col = '#FF' + dec2hex(col_g) + '00';
622
623         //debug('pcol: '+pcol+' g:'+col_g+' _pbar_warn:'+ _pbar_warn + ' color: '+col);
624         element_id("gradient").style.backgroundColor = col;
625
626         element_id("progressIndicator").innerHTML = pcnt + '%';
627         //element_id("progressIndicator").innerHTML = amount;
628
629         element_id("mask").style.clip = 'rect(' + Array(
630                 '0px',
631                 element_id("mask").offsetWidth + 'px',
632                 element_id("mask").offsetHeight + 'px',
633                 Math.round(_pbar_width * amount / max) + 'px'
634         ).join(' ') + ')';
635 }
636
637 if (!self.body) self.body = new Object();
638 self.onload = self.document.onload = self.body.onload = function() {
639         //pbar_reset();
640         sumiraj();
641 };
642
643 // -->
644 </script>
645 <div id="fixedBox">
646
647 Size: <input type="text" name="totalsize" size="7" readonly="readonly" style="text-align:right;" value="0" /> kB
648
649 <div id="mContainer">
650         <div id="gradient">&nbsp;</div>
651         <div id="mask">&nbsp;</div>
652         <div id="progressIndicator">0%</div>
653 </div>
654 <br/>
655
656 Note:
657 <textarea name="note" cols="10" rows="5" id="note"></textarea>
658
659 <input type="submit" id="submitBurner" value="Burn selected" name="submitBurner" />
660
661 </div>
662 <!--
663 <div id="debug" style="float: right; width: 10em; border: 1px #ff0000 solid; background-color: #ffe0e0; -moz-opacity: 0.7;">
664 no debug output yet
665 </div>
666 -->
667 EOF3
668         $retHTML .= q{
669                         <input type="hidden" value="burn" name="action">
670                         <input type="hidden" value="results" name="search_results">
671                         <table style="fview" border="0" cellspacing="0" cellpadding="2">
672                         <tr class="tableheader">
673                         <td class="tableheader">
674                                 <input type="checkbox" name="allFiles" id="allFiles" onClick="checkAll('allFiles');">
675                         </td>
676                         <td align="center">Share</td>
677                         <td align="center">Backup no</td>
678                         <td align="center">Type</td>
679                         <td align="center">date</td>
680                         <td align="center">age/days</td>
681                         <td align="center">size/MB</td>
682                         <td align="center">gzip size</td>
683                         </tr>
684
685         };
686
687         my @color = (' bgcolor="#e0e0e0"', '');
688
689         my $i = 0;
690         my $host = '';
691
692         foreach my $backup ( getBackupsNotBurned() ) {
693
694                 if ($host ne $backup->{'host'}) {
695                         $i++;
696                         $host = $backup->{'host'};
697                 }
698                 my $ftype = "";
699
700                 $retHTML .=
701                         '<tr' . $color[$i %2 ] . '>
702                         <td class="fview">';
703                 # FIXME
704                 $backup->{'fs_size'} = int($backup->{'size'} * 1024);
705                 if (($backup->{'fs_size'} || 0) > 0) {
706                         $retHTML .= '
707                         <input type="checkbox" name="fcb' .
708                         $backup->{'hostid'}.'_'.$backup->{'backupnum'} . 
709                         '" value="' . $backup->{'hostid'}.'_'.$backup->{'backupnum'} .
710                         '" onClick="sumiraj(this);">';
711                 }
712                 $retHTML .=
713                         '</td>' .
714                         '<td align="right">' . $backup->{'host'} . ':' . $backup->{'share'} . '</td>' .
715                         '<td align="center">' . $backup->{'backupnum'} . '</td>' .
716                         '<td align="center">' . $backup->{'type'} . '</td>' .
717                         '<td align="center">' . epoch_to_iso( $backup->{'date'} ) . '</td>' .
718                         '<td align="center">' . $backup->{'age'} . '</td>' .
719                         '<td align="right">' . $backup->{'size'} . '</td>' .
720                         '<td align="right">' . $backup->{'fs_size'} .
721                         '<input type="hidden" iD="fss'.$backup->{'hostid'}.'_'.$backup->{'backupnum'} . '" value="'. $backup->{'fs_size'} .'"></td>' .
722
723                         "</tr>\n";
724         }
725
726         $retHTML .= "</table>";
727         $retHTML .= "</form>";
728       
729         return $retHTML;
730 }      
731
732 sub displayGrid($) {
733         my ($param) = @_;
734
735         my $offset = $param->{'offset'};
736         my $hilite = $param->{'search_filename'};
737
738         my $retHTML = "";
739  
740         my $start_t = time();
741
742         my ($results, $files);
743         if ($param->{'use_hest'} && length($hilite) > 0) {
744                 ($results, $files) = getFilesHyperEstraier($param);
745         } else {
746                 ($results, $files) = getFiles($param);
747         }
748
749         my $dur_t = time() - $start_t;
750         my $dur = sprintf("%0.4fs", $dur_t);
751
752         my ($from, $to) = (($offset * $on_page) + 1, ($offset * $on_page) + $on_page);
753
754         if ($results <= 0) {
755                 $retHTML .= qq{
756                         <p style="color: red;">No results found...</p>
757                 };
758                 return $retHTML;
759         } else {
760                 # DEBUG
761                 #use Data::Dumper;
762                 #$retHTML .= '<pre>' . Dumper($files) . '</pre>';
763         }
764
765
766         $retHTML .= qq{
767         <div>
768         Found <b>$results files</b> showing <b>$from - $to</b> (took $dur)
769         </div>
770         <table style="fview" width="100%" border="0" cellpadding="2" cellspacing="0">
771                 <tr class="fviewheader"> 
772                 <td></td>
773                 <td align="center">Share</td>
774                 <td align="center">Type and Name</td>
775                 <td align="center">#</td>
776                 <td align="center">Size</td>
777                 <td align="center">Date</td>
778                 <td align="center">Media</td>
779                 </tr>
780         };
781
782         my $file;
783
784         sub hilite_html($$) {
785                 my ($html, $search) = @_;
786                 $html =~ s#($search)#<b>$1</b>#gis;
787                 return $html;
788         }
789
790         sub restore_link($$$$$$) {
791                 my $type = shift;
792                 my $action = 'RestoreFile';
793                 $action = 'browse' if (lc($type) eq 'dir');
794                 return sprintf(qq{<a href="?action=%s&host=%s&num=%d&share=%s&dir=%s">%s</a>}, $action, @_);
795         }
796
797         my $i = $offset * $on_page;
798
799         foreach $file (@{ $files }) {
800                 $i++;
801
802                 my $typeStr  = BackupPC::Attrib::fileType2Text(undef, $file->{'type'});
803                 $retHTML .= qq{<tr class="fviewborder">};
804
805                 $retHTML .= qq{<td class="fviewborder">$i</td>};
806
807                 $retHTML .=
808                         qq{<td class="fviewborder" align="right">} . $file->{'hname'} . ':' . $file->{'sname'} . qq{</td>} .
809                         qq{<td class="fviewborder"><img src="$Conf{CgiImageDirURL}/icon-$typeStr.gif" alt="$typeStr" align="middle">&nbsp;} . hilite_html( $file->{'filepath'}, $hilite ) . qq{</td>} .
810                         qq{<td class="fviewborder" align="center">} . restore_link( $typeStr, ${EscURI( $file->{'hname'} )}, $file->{'backupnum'}, ${EscURI( $file->{'sname'})}, ${EscURI( $file->{'filepath'} )}, $file->{'backupnum'} ) . qq{</td>} .
811                         qq{<td class="fviewborder" align="right">} . $file->{'size'} . qq{</td>} .
812                         qq{<td class="fviewborder">} . epoch_to_iso( $file->{'date'} ) . qq{</td>} .
813                         qq{<td class="fviewborder">} . '?' . qq{</td>};
814
815                 $retHTML .= "</tr>";
816         }
817         $retHTML .= "</table>";
818
819         # all variables which has to be transfered
820         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/) {
821                 $retHTML .= qq{<INPUT TYPE="hidden" NAME="$n" VALUE="$In{$n}">\n};
822         }
823
824         my $del = '';
825         my $max_page = int( $results / $on_page );
826         my $page = 0;
827
828         sub page_link($$$) {
829                 my ($param,$page,$display) = @_;
830
831                 $param->{'offset'} = $page;
832
833                 my $html = '<a href = "' . $MyURL;
834                 my $del = '?';
835                 foreach my $k (keys %{ $param }) {
836                         if ($param->{$k}) {
837                                 $html .= $del . $k . '=' . ${EscURI( $param->{$k} )};
838                                 $del = '&';
839                         }
840                 }
841                 $html .= '">' . $display . '</a>';
842         }
843
844         $retHTML .= '<div style="text-align: center;">';
845
846         if ($offset > 0) {
847                 $retHTML .= page_link($param, $offset - 1, '&lt;&lt;') . ' ';
848         }
849
850         while ($page <= $max_page) {
851                 if ($page == $offset) {
852                         $retHTML .= $del . '<b>' . ($page + 1) . '</b>';
853                 } else {
854                         $retHTML .= $del . page_link($param, $page, $page + 1);
855                 }
856
857                 if ($page < $offset - $pager_pages && $page != 0) {
858                         $retHTML .= " ... ";
859                         $page = $offset - $pager_pages;
860                         $del = '';
861                 } elsif ($page > $offset + $pager_pages && $page != $max_page) {
862                         $retHTML .= " ... ";
863                         $page = $max_page;
864                         $del = '';
865                 } else {
866                         $del = ' | ';
867                         $page++;
868                 }
869         }
870
871         if ($offset < $max_page) {
872                 $retHTML .= ' ' . page_link($param, $offset + 1, '&gt;&gt;');
873         }
874
875         $retHTML .= "</div>";
876
877         return $retHTML;
878 }
879
880 1;