File Coverage

blib/lib/HTML/EP/Explorer.pm
Criterion Covered Total %
statement 10 12 83.3
branch n/a
condition n/a
subroutine 4 4 100.0
pod n/a
total 14 16 87.5


line stmt bran cond sub pod time code
1             # -*- perl -*-
2             #
3             # HTML::EP::Explorer - A Perl package for browsing filesystems and
4             # executing actions on files.
5             #
6             #
7             # This module is
8             #
9             # Copyright (C) 1999 Jochen Wiedmann
10             # Am Eisteich 9
11             # 72555 Metzingen
12             # Germany
13             #
14             # Email: joe@ispsoft.de
15             #
16             # All Rights Reserved.
17             #
18             # You may distribute under the terms of either the GNU General Public
19             # License or the Artistic License, as specified in the Perl README file.
20             #
21             # $Id: Explorer.pm,v 1.2 1999/11/04 12:24:10 joe Exp $
22             #
23              
24 1     1   1404 use strict;
  1         14  
  1         82  
25              
26 1     1   6 use Cwd ();
  1         2  
  1         18  
27 1     1   22 use File::Spec ();
  1         1  
  1         14  
28 1     1   452 use HTML::EP ();
  0            
  0            
29             use HTML::EP::Locale ();
30             use HTML::EP::Session ();
31              
32              
33             package HTML::EP::Explorer;
34              
35             @HTML::EP::Explorer::ISA = qw(HTML::EP::Session HTML::EP::Locale HTML::EP);
36             $HTML::EP::Explorer::VERSION = '0.1006';
37              
38             sub init {
39             my $self = shift;
40             $self->HTML::EP::Session::init(@_);
41             $self->HTML::EP::Locale::init(@_);
42             }
43              
44             sub _ep_explorer_init {
45             my $self = shift; my $attr = shift;
46             return '' if $self->{_ep_explorer_init_done};
47             $self->print("_ep_explorer_init: attr = (", join(",", %$attr), ")\n")
48             if $self->{'debug'};
49             $self->{_ep_explorer_init_done} = 1;
50             my $cgi = $self->{'cgi'};
51             $attr->{'class'} ||= "HTML::EP::Session::Cookie";
52             $attr->{'id'} ||= "explorer-session";
53             $attr->{'path'} ||= "/";
54             $attr->{'expires'} ||= "+10y";
55             eval { $self->_ep_session($attr) };
56             my $session = $self->{$attr->{'var'} || 'session'};
57             if ($self->{debug}) {
58             require Data::Dumper;
59             $self->print("Session = ", Data::Dumper::Dumper($session), "\n");
60             }
61             if (!$attr->{'noprefs'} and
62             ($@ or !exists($session->{'prefs'}))) {
63             # First time run, open the prefs page.
64             my $prefs = $attr->{'prefs_page'} || "prefs.ep";
65             my $return_to = $attr->{'return_to'} || $self->{'env'}->{'PATH_INFO'};
66             $self->print("_ep_explorer_init: Redirecting to $prefs, returning to $return_to\n")
67             if $self->{'debug'};
68             $cgi->param('return_to', $return_to);
69             $self->{'_ep_output'} .= $self->_ep_include({file => $prefs});
70             $self->_ep_exit({});
71             }
72             '';
73             }
74              
75             sub InitialConfig {
76             my $self = shift;
77             { 'actions' => [],
78             'filetypes' => [
79             { 'name' => $self->{'_ep_language'} eq 'de' ?
80             'Alle Dateien (*)' : 'All Files (*)',
81             'icon' => '/icons/unknown.gif',
82             're' => '.*'
83             }],
84             'directories' => [
85             { 'name' => 'Root-Directory (/)',
86             'dir' => '/'
87             }]
88             }
89             }
90              
91              
92             sub ReadArray {
93             my $self = shift; my $prefix = shift;
94             my $cgi = $self->{'cgi'};
95             my %hash;
96             foreach my $key ($cgi->param()) {
97             next unless $key =~ /^$prefix(.*)/;
98             $self->print("ReadArray: Found key $key, saving as $1 (",
99             join(",", $cgi->param($key)), "\n") if $self->{'debug'};
100             $hash{$1} = [$cgi->param($key)];
101             }
102             my @array;
103             while (@{$hash{'name'}}) {
104             my %h;
105             while (my($var, $val) = each %hash) {
106             $h{$var} = shift @$val;
107             }
108             push(@array, \%h) if $h{'name'};
109             }
110             \@array;
111             }
112              
113              
114             sub ReadDirectories {
115             my $dirs = shift()->ReadArray('explorer_directory_');
116             my $pwd;
117             foreach my $dir (@$dirs) {
118             # Don't save the name, that the user gave us. Save the physical
119             # filesystem path, so that we can later compare it to paths
120             # requested by other users, if "Allow access to other directories"
121             # is off.
122             $pwd = Cwd::cwd() unless $pwd;
123             chdir($dir->{'dir'}) or die "Failed to change directory to $dir: $!";
124             $dir->{'dir'} = Cwd::cwd();
125             }
126             chdir $pwd if $pwd;
127             $dirs;
128             }
129              
130              
131             sub ReadConfig {
132             my $self = shift; my $config = shift;
133             my $cgi = $self->{'cgi'};
134              
135             foreach my $var ($cgi->param()) {
136             next unless $var =~ /^explorer_config_(.*)/;
137             my $v = $1;
138             $config->{$v} = $cgi->param($var);
139             }
140              
141             $config->{'actions'} = $self->ReadArray('explorer_action_');
142             $config->{'status_actions'} = $self->ReadArray('explorer_status_action_');
143             $config->{'filetypes'} = $self->ReadArray('explorer_filetype_');
144             $config->{'directories'} = $self->ReadDirectories();
145             $config;
146             }
147              
148              
149             sub _ep_explorer_config {
150             my $self = shift; my $attr = shift;
151             my $debug = $self->{'debug'};
152             my $cgi = $self->{'cgi'};
153             my $file = $attr->{'file'} || "config.pm";
154             $self->{'config'} = eval { require $file } || $self->InitialConfig();
155             if ($attr->{'maysafe'} && $cgi->param('save')) {
156             $self->print("_ep_explorer_config: Saving.\n") if $debug;
157             $self->{'config'} = $self->ReadConfig($self->{'config'});
158             require Data::Dumper;
159             my $fh = Symbol::gensym();
160             my $dump = Data::Dumper->new([$self->{'config'}])->Indent(1)->Terse(1);
161             $self->print("_ep_explorer_config: Got\n", $dump->Dump(), "\n")
162             if $debug;
163             (open($fh, ">$file") and (print $fh $dump->Dump()) and close($fh))
164             or die "Failed to create $file: $!";
165             }
166             $self->{'actions'} = $self->{'config'}->{'actions'};
167             $self->{'status_actions'} = $self->{'config'}->{'status_actions'};
168             $self->{'directories'} = $self->{'config'}->{'directories'};
169             $self->{'filetypes'} = $self->{'config'}->{'filetypes'};
170             $self->{'num_directories'} = @{$self->{'directories'}};
171             '';
172             }
173              
174             sub ReadPrefs {
175             my $self = shift; my $prefs = shift;
176             my $cgi = $self->{'cgi'};
177             foreach my $var ($cgi->param()) {
178             next unless $var =~ /^explorer_prefs_(.*)/;
179             my $vr = $1;
180             my $val = $cgi->param($var);
181             $prefs->{$vr} = $val;
182             }
183             $prefs;
184             }
185              
186             sub _ep_explorer_prefs {
187             my $self = shift; my $attr = shift;
188             my $debug = $self->{'debug'};
189             $attr->{'noprefs'} = 1;
190             $self->_ep_explorer_init($attr);
191             my $session = $self->{$attr->{'var'} ||= 'session'};
192             my $cgi = $self->{'cgi'};
193             my $return;
194             if (($return = $cgi->param('save_and_return')) ||
195             $cgi->param('save')) {
196             $self->print("_ep_explorer_prefs: Saving.\n") if $debug;
197             $session->{'prefs'} = $self->ReadPrefs($session->{'prefs'});
198             if ($debug) {
199             require Data::Dumper;
200             $self->print("_ep_explorer_save: Got\n",
201             Data::Dumper->new([$session->{'prefs'}])
202             ->Indent(1)->Terse(1)->Dump(), "\n");
203             }
204             $attr->{'locked'} = 1;
205             $self->_ep_session_store($attr);
206             }
207             if ($return and (my $return_to = $cgi->param('return_to'))) {
208             $self->print("Returning to $return_to\n") if $debug;
209             $self->{'_ep_output'} .=
210             $self->_ep_include({'file' => $return_to});
211             $self->print("Done including $return_to\n") if $debug;
212             $self->_ep_exit({});
213             }
214             '';
215             }
216              
217             sub _ep_explorer_basedir {
218             my $self = shift; my $attr = shift;
219             return if $self->{'basedir'};
220             my $cgi = $self->{'cgi'};
221             my $session = $self->{'session'};
222             my $debug = $self->{'debug'};
223             my $basedir = $cgi->param('basedir') || $session->{'basedir'}
224             || $attr->{'basedir'} || $self->{'directories'}->[0]
225             || $ENV{'DOCUMENT_ROOT'};
226             $basedir = HTML::EP::Explorer::Dir->new($basedir)->{'dir'};
227             chdir($basedir)
228             or die "Failed to change directory to $basedir: $!";
229             $basedir = Cwd::cwd();
230             if (!$session->{'basedir'} or $session->{'basedir'} ne $basedir) {
231             $self->{'modified'} = 1;
232             $session->{'basedir'} = $basedir;
233             }
234             foreach my $dir (@{$self->{'directories'}}) {
235             $self->print("Checking whether $dir->{'dir'} is $basedir.\n")
236             if $debug;
237             if ($dir->{'dir'} eq $basedir) {
238             $self->{'in_top_dir'} = 1;
239             $self->{'in_base_dir'} = $dir;
240             $self->{'display_dir'} = "/";
241             $self->print("Yes, it is.\n") if $debug;
242             last;
243             }
244             }
245             if (!$self->{'in_top_dir'}) {
246             $self->{'in_top_dir'} = ($basedir eq File::Spec->rootdir());
247             foreach my $dir (@{$self->{'directories'}}) {
248             $self->print("Checking whether $basedir is below $dir->{'dir'}.\n")
249             if $debug;
250             if ($basedir =~ /^\Q$dir->{'dir'}\E(\/.*)$/) {
251             $self->{'in_base_dir'} = $dir;
252             $self->{'display_dir'} = $1;
253             $self->print("Yes, it is.\n") if $debug;
254             last;
255             }
256             }
257             if (!$self->{'in_base_dir'}) {
258             die "Directory $basedir is outside of the permitted area."
259             if $self->{'config'}->{'dirs_restricted'};
260             $self->{'display_dir'} = $basedir;
261             }
262             }
263             $self->print("Basedir is $basedir.\n") if $debug;
264             $self->{'basedir'} = $basedir;
265             '';
266             }
267              
268             sub _ep_explorer_sortby {
269             my $self = shift; my $attr = shift;
270             my $cgi = $self->{'cgi'};
271             my $session = $self->{'session'};
272             my $sortby = $cgi->param('sortby') || $session->{'sortby'} ||
273             $attr->{'sortby'} || "name";
274             if (!$session->{'sortby'} || $session->{'sortby'} ne $sortby) {
275             $self->{'modified'} = 1;
276             $session->{'sortby'} = $sortby;
277             }
278             $self->print("Sorting by $sortby.\n") if $self->{'debug'};
279             $self->{'sortby'} = $sortby;
280             '';
281             }
282              
283             sub _ep_explorer_filetype {
284             my $self = shift; my $attr = shift;
285             my $cgi = $self->{'cgi'};
286             my $debug = $self->{'debug'};
287             my $session = $self->{'session'};
288             my $filetype = $cgi->param('filetype') || $session->{'filetype'}
289             || $attr->{'filetype'} || '';
290             $self->print("Looking for file type $filetype\n") if $debug;
291             my $found;
292             foreach my $ft (@{$self->{'filetypes'}}) {
293             if ($filetype eq $ft->{'name'}) {
294             $found = $ft;
295             last;
296             }
297             }
298             if ($found) {
299             $self->print("Found it.\n") if $debug;
300             } elsif (@{$self->{'filetypes'}}) {
301             $found = $self->{'filetypes'}->[0];
302             $self->print("Choosing default file type $found->{'name'}\n")
303             if $debug;
304             } else {
305             $self->print("No file type found.\n");
306             }
307              
308             $found->{'selected'} = 'SELECTED' if $found;
309             my $name = $found ? $found->{'name'} : '';
310             if (!defined($session->{'filetype'}) ||
311             $session->{'filetype'} ne $name) {
312             $self->{'modified'} = 1;
313             $session->{'filetype'} = $name;
314             }
315             $self->print("Filetype is $found->{'name'}.\n")
316             if $self->{'debug'} and $found;
317             $self->{'filetype'} = $found;
318             '';
319             }
320              
321             sub _ep_explorer_browse {
322             my $self = shift; my $attr = shift;
323             my $cgi = $self->{'cgi'};
324             my $debug = $self->{'debug'};
325             my $session = $self->{'session'};
326             $self->{'modified'} = 0;
327             my $dir_template = $self->{'dir_template'}
328             or die "Missing template variable: dir_template";
329             my $item = $attr->{'item'} || die "Missing item name";
330              
331             $self->_ep_explorer_basedir($attr);
332             $self->_ep_explorer_filetype($attr);
333             $self->_ep_explorer_sortby($attr);
334              
335             my $dir = HTML::EP::Explorer::Dir->new($self->{'basedir'});
336             my $list = $dir->Read($self->{'filetype'}->{'re'});
337             my $sortby = $self->{'sortby'};
338             my $updir;
339             if ($list->[0]->IsDir()
340             and $list->[0]->{'name'} eq File::Spec->updir()) {
341             $updir = shift @$list;
342             }
343             $self->print("Sorting by $sortby.\n") if $debug;
344             if ($sortby eq 'type') {
345             @$list = sort {
346             if ($a->IsDir()) {
347             $b->IsDir() ? $a->{'name'} cmp $b->{'name'} : -1;
348             } elsif ($b->IsDir()) {
349             return 1;
350             } else {
351             my $ae = ($a =~ /\.(.*?)$/) ? $1 : '';
352             my $be = ($b =~ /\.(.*?)$/) ? $1 : '';
353             ($ae cmp $be) || ($a->{'name'} cmp $b->{'name'});
354             }
355             } @$list;
356             } elsif ($sortby eq 'uid') {
357             @$list = sort { (getpwuid($a->{'uid'}) || '') cmp
358             (getpwuid($b->{'uid'}) || '')} @$list;
359             } elsif ($sortby eq 'gid') {
360             @$list = sort { (getgrgid($a->{'gid'}) || '') cmp
361             (getgrgid($b->{'gid'}) || '')} @$list;
362             } elsif ($sortby =~ /^(?:size|[amc]time)$/) {
363             @$list = sort { $a->{$sortby} <=> $b->{$sortby} } @$list;
364             } else {
365             @$list = sort { $a->{$sortby} cmp $b->{$sortby} } @$list;
366             }
367             unshift(@$list, $updir)
368             if $updir and !$self->{'in_top_dir'};
369             my $output = '';
370             $self->{'i'} = 0;
371             foreach my $i (@$list) {
372             $self->{$item} = $i;
373             $output .= $i->AsHtml($self, $item);
374             ++$self->{'i'};
375             }
376              
377             $self->_ep_session_store($attr) if $self->{'modified'};
378             $output;
379             }
380              
381             sub _format_ACTIONS {
382             my $self = shift; my $item = shift;
383              
384             my $str = '';
385             foreach my $action (@{$self->{'actions'}}) {
386             $self->{'action'} = $action;
387             $self->{'icon'} = $action->{'icon'} ?
388             qq{$action->{'name'}} :
389             $action->{'name'};
390             $str .= $self->ParseVars($self->{'action_template'});
391             }
392              
393             $str;
394             }
395              
396             sub FindAction {
397             my $self = shift; my $attr = shift;
398             my $cgi = $self->{'cgi'};
399             my $name = $cgi->param('faction') || $attr->{'faction'} ||
400             die "Missing action name";
401             my $debug = $self->{'debug'};
402             $self->print("FindAction: Looking for $name\n") if $debug;
403             my $action;
404             foreach my $a (@{$self->{'actions'}}) {
405             if ($a->{'name'} eq $name) {
406             $action = $a;
407             last;
408             }
409             }
410             $self->{'action'} = $action or die "Unknown action: $name";
411             $self->print("Selected action is $action->{'name'}\n") if $debug;
412             $action;
413             }
414              
415             sub FindStatusAction {
416             my $self = shift; my $script = shift; my $attr = shift;
417             my $debug = $self->{'debug'};
418             $self->print("FindStatusAction: Looking for $script\n") if $debug;
419             my $action;
420             foreach my $sa (@{$self->{'status_actions'}}) {
421             if ($sa->{'name'} eq $script) {
422             $self->print("FindStatusAction: Returning ",
423             join(",", %$sa), "\n") if $debug;
424             return $sa;
425             }
426             }
427             die "FindStatusAction: Unknown script $script";
428             }
429              
430              
431             sub _ep_explorer_logfile {
432             my $self = shift; my $attr = shift;
433             my $debug = $self->{'debug'};
434             my $action = $self->FindAction({});
435             my $fh = Symbol::gensym();
436             require Fcntl;
437             $self->print("Opening logfile: $action->{'logfile'}\n") if $debug;
438             sysopen($fh, $action->{'logfile'}, Fcntl::O_RDONLY())
439             or die "Failed to open logfile $action->{'logfile'}: $!";
440             $self->Stop();
441             my $cgi = $self->{'cgi'};
442             $self->print($cgi->header('-type' => 'text/plain'));
443             $self->print("\n");
444             seek($fh, -2000, 2);
445             $| = 1;
446             my $pos;
447             local $/ = undef;
448             while(1) {
449             $pos = tell($fh);
450             if (eof($fh)) {
451             sleep 15;
452             seek($fh, $pos, 0);
453             } else {
454             my $line = <$fh>;
455             if (!defined($line)) {
456             $self->print("Failed to read: $!");
457             last;
458             } else {
459             $self->print($line);
460             }
461             }
462             }
463             '';
464             }
465              
466              
467             sub _ep_explorer_queue {
468             my $self = shift; my $attr = shift;
469             my $cgi = $self->{'cgi'};
470             my $debug = $self->{'debug'};
471             my $action = $self->FindAction($attr);
472              
473             my $ignore_cache;
474             if (my $script = $cgi->param('script')) {
475             my $status_action = $self->FindStatusAction($script, $attr);
476             my %env = %ENV;
477             $env{'job'} = quotemeta($cgi->param('job'));
478             $env{'user'} = quotemeta($self->User());
479             foreach my $var (split(/\n/, $action->{'vars'})) {
480             if ($var =~ /^\s*(.*?)\s*=\s*(.*?)\s*$/) {
481             $env{$1} = $2;
482             }
483             }
484             local %ENV = %env;
485             if ($debug) {
486             my $command = $status_action->{'script'};
487             $command =~ s/\$(\w+)/$ENV{$1}/g;
488             $self->print("_ep_explorer_queue: Executing command ($command)\n");
489             }
490             system "$status_action->{'script'} >/dev/null";
491             $ignore_cache = 1;
492             }
493              
494             my $input;
495             my $file = File::Spec->catfile("status",
496             $cgi->escape($action->{'name'}));
497             if (!$ignore_cache && $self->{'config'}->{'cache'}) {
498             my($dev, $ino, $mode, $nlink, $uid, $gid, $rdev, $size, $atime,
499             $mtime) = stat $file;
500             my $regen_time = $mtime + $self->{'config'}->{'cache'};
501             $self->print("Cache file $file ",
502             -f _ ? "exists.\n" : "doesn't exist.\n",
503             -f _ ? "Modification time is $mtime, current time is " .
504             time() . ", regeneration time is $regen_time\n"
505             : "") if $debug;
506             if (-f _ && $regen_time > time()) {
507             $self->print("Trying to load cache file $file.\n") if $debug;
508             require Fcntl;
509             my $fh = Symbol::gensym();
510             if (open($fh, "<$file") and flock($fh, Fcntl::LOCK_SH())) {
511             local $/ = undef;
512             $input = <$fh>;
513             }
514             $self->print($input ? "Got:\n$input\n" : "Not successful ($!)\n")
515             if $debug;
516             }
517             }
518             if (!$input) {
519             my $command = $action->{'status'};
520             local $ENV{'user'} = quotemeta($self->User());
521             $input = `$command 2>&1`;
522             if ($self->{'config'}->{'cache'}) {
523             require Fcntl;
524             my $fh = Symbol::gensym();
525             if (sysopen($fh, $file, Fcntl::O_RDWR()|Fcntl::O_CREAT())
526             and flock($fh, Fcntl::LOCK_EX())) {
527             print $fh $input;
528             truncate($fh, length($input));
529             }
530             }
531             }
532              
533             my @status;
534             foreach my $line (split(/\n/, $input)) {
535             if ($line =~ /^(\S+)\s+(\S+)\s+(\d+)\s+(\S.*?)\s+(\d+)\s+bytes/) {
536             push(@status, { 'rank' => $1,
537             'owner' => $2,
538             'job' => $3,
539             'file' => $4,
540             'size' => $5 });
541             }
542             }
543             $self->{'status'} = \@status;
544             $self->{'status_num'} = @status;
545             '';
546             }
547              
548             sub _ep_explorer_action {
549             my $self = shift; my $attr = shift;
550             my $cgi = $self->{'cgi'};
551             my $debug = $self->{'debug'};
552             my $name = $cgi->param('faction') || $attr->{'faction'}
553             || die "Missing action name";
554             my $action = $self->FindAction($attr);
555              
556             my @files;
557             my $file;
558             if (($file = $cgi->param('files')) || ($file = $attr->{'files'})) {
559             @files = split(" ", $file);
560             } elsif (($file = $cgi->param('file')) || ($file = $attr->{'file'})) {
561             @files = $file;
562             } else {
563             die "Missing file name";
564             }
565             $self->print("Selected files are:\n", map{" $_\n"} @files) if $debug;
566              
567             my $command = $action->{'script'};
568             my $files;
569             if ($command =~ /\$files/) {
570             # Can handle multiple files
571             $files = join(" ", map {
572             quotemeta(HTML::EP::Explorer::File->new($_)->{'file'})
573             } @files);
574             $command =~ s/\$files/$files/sg;
575             $command .= " 2>&1" if $attr->{'execute'};
576             } else {
577             my @commands;
578             foreach my $file (@files) {
579             my $c = $command;
580             my $f = quotemeta(HTML::EP::Explorer::File->new($file)->{'file'});
581             $c =~ s/\$file/$f/sg;
582             push(@commands, $attr->{'execute'} ? "$c 2>&1" : $c);
583             }
584             $command = join(";", @commands);
585             }
586             $self->print("Selected command is $command\n") if $debug;
587             local $ENV{'user'} = quotemeta($self->User());
588             local $ENV{'files'} = $files if $files;
589             if ($attr->{'execute'}) {
590             return `$command`;
591             } else {
592             return $command;
593             }
594             }
595              
596             sub User {
597             $ENV{'REMOTE_USER'} || "anonymous";
598             }
599              
600             sub _format_MODE {
601             my $self = shift; my $mode = shift;
602             (($mode & 0400) ? "r" : "-") .
603             (($mode & 0200) ? "w" : "-") .
604             (($mode & 04000) ? "s" : (($mode & 0100) ? "x" : "-")) .
605             (($mode & 040) ? "r" : "-") .
606             (($mode & 020) ? "w" : "-") .
607             (($mode & 02000) ? "s" : (($mode & 010) ? "x" : "-")) .
608             (($mode & 04) ? "r" : "-") .
609             (($mode & 02) ? "w" : "-") .
610             (($mode & 01) ? "x" : "-");
611             }
612              
613             sub _format_UID {
614             my $self = shift; my $uid = shift;
615             my $u = getpwuid($uid);
616             defined $u ? $u : $uid;
617             }
618              
619             sub _format_GID {
620             my $self = shift; my $gid = shift;
621             my $g = getgrgid($gid);
622             defined $g ? $g : $gid;
623             }
624              
625             sub _format_DATE {
626             my $self = shift; my $time = shift;
627             return '' unless $time;
628             return $self->_format_TIME(scalar(localtime($time)));
629             }
630              
631             sub _format_SELECTED {
632             my $self = shift; shift() ? "SELECTED" : "";
633             }
634              
635             package HTML::EP::Explorer::File;
636              
637             sub new {
638             my $proto = shift; my $file = shift;
639             $file =~ s/^file://;
640             my $self = { 'file' => $file, @_ };
641             $self->{'name'} ||= File::Basename::basename($file);
642             $self->{'url'} ||= "file:$file";
643             bless($self, (ref($proto) || $proto));
644             }
645              
646             sub IsDir { 0 }
647              
648             sub AsHtml {
649             my $self = shift; my $ep = shift;
650             foreach my $ft (@{$ep->{'filetypes'}}) {
651             if ($ft->{'icon'} && $self->{'name'} =~ /$ft->{'re'}/) {
652             $self->{'icon'} = $ft->{'icon'};
653             last;
654             }
655             }
656             $self->{'icon'} = "unknown.gif" unless $self->{'icon'};
657             $ep->ParseVars($ep->{'file_template'}
658             or die "Missing template variable: file_template");
659             }
660              
661              
662             package HTML::EP::Explorer::Dir;
663              
664             sub new {
665             my $proto = shift; my $dir = shift;
666             $dir =~ s/^file://;
667             my $self = { 'dir' => $dir, @_ };
668             $self->{'name'} ||= File::Basename::basename($dir);
669             $self->{'url'} ||= "file:$dir";
670             bless($self, (ref($proto) || $proto));
671             }
672              
673             sub IsDir { 1 }
674              
675             sub AsHtml {
676             my $self = shift; my $ep = shift;
677             $ep->ParseVars($ep->{'dir_template'}
678             or die "Missing template variable: dir_template");
679             }
680              
681             sub Read {
682             my $self = shift; my $re = shift;
683             my $fh = Symbol::gensym();
684             my $pwd = Cwd::cwd();
685             my $curdir = File::Spec->curdir();
686             my $dir = $self->{'dir'};
687             my @list;
688             chdir $dir or die "Failed to change directory to $dir: $!";
689             opendir($fh, $curdir) or die "Failed to open directory $dir: $!";
690             while (defined(my $f = readdir($fh))) {
691             next if $f eq $curdir;
692             my($dev, $ino, $mode, $nlink, $uid, $gid, $rdev, $size,
693             $atime, $mtime, $ctime, $blksize) = stat $f;
694             if (-f _) {
695             push(@list,
696             HTML::EP::Explorer::File->new(File::Spec->catfile($dir, $f),
697             'name' => $f,
698             'mode' => $mode,
699             'uid' => $uid,
700             'gid' => $gid,
701             'size' => $size,
702             'mtime' => $mtime,
703             'ctime' => $ctime,
704             'atime' => $atime))
705             if !$re || $f =~ /$re/;
706             } elsif (-d _) {
707             push(@list,
708             HTML::EP::Explorer::Dir->new(File::Spec->catdir($dir, $f),
709             'name' => $f,
710             'mode' => $mode,
711             'uid' => $uid,
712             'gid' => $gid,
713             'size' => $size,
714             'mtime' => $mtime,
715             'ctime' => $ctime,
716             'atime' => $atime))
717             }
718             }
719             closedir $fh;
720             chdir $pwd;
721             \@list;
722             }
723              
724              
725             1;
726              
727             __END__