File Coverage

blib/lib/Apache2/Archive.pm
Criterion Covered Total %
statement 7 9 77.7
branch n/a
condition n/a
subroutine 3 3 100.0
pod n/a
total 10 12 83.3


line stmt bran cond sub pod time code
1             package Apache2::Archive;
2              
3              
4 1     1   845 use strict;
  1         3  
  1         41  
5 1     1   1246 use Archive::Tar;
  1         161297  
  1         170  
6 1     1   2494 use Apache2::Log;
  0            
  0            
7             use Apache2::Const;
8             use Apache2::Util ();
9             use Apache2::Status;
10             use Apache2::SubRequest ();
11              
12             use vars qw($VERSION @ISA @EXPORT @EXPORT_OK);
13              
14             require Exporter;
15              
16             @ISA = qw(Exporter AutoLoader);
17             # Items to export into callers namespace by default. Note: do not export
18             # names by default without a very good reason. Use EXPORT_OK instead.
19             # Do not simply export all your public functions/methods/constants.
20             @EXPORT = qw(
21            
22             );
23             $VERSION = '0.2';
24              
25              
26             sub handler{
27             my $r = shift;
28             my $t;
29             #$t->{Files}; # Contains info on all the files in the archive
30             #$t->{FileInfo}; # contains info on archive file itself
31             #$t->{filename}; # Canonical name of the archive file itself
32             #$t->{template}; # The template file (one line per array entry)
33             #$t->{Tar}; # The Archive::Tar object for the archive
34             #$t->{SizeLimit}; # The Maximum tar file size allowed. After opening a file larger
35             # that this, the processes will terminate to free memory.
36             $t->{Tar} = new Archive::Tar;
37             $t->{SizeLimit} = $r->dir_config('SizeLimit');
38            
39             ##
40             # Get the template file for later use
41             ##
42              
43             &getTemplateFile($t,$r->dir_config('Template'));
44            
45             ##
46             # Create the Tar object;
47             ##
48            
49            
50             $t->{filename} = $r->filename;
51             unless (-e $t->{filename} && -r $t->{filename}) {
52             return Apache2::Const::NOT_FOUND;
53             }
54             my($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime,$blksize,$blocks)=stat($t->{filename});
55              
56             ($t->{FileInfo}->{'name'}) = $t->{filename} =~ m!(([^/\\]|\\\/)+)$!;
57             $t->{FileInfo}->{'date'} = &getDatestring($mtime, $r->dir_config('Months'));
58             $t->{FileInfo}->{'rawsize'} = -s $t->{filename};
59             $t->{FileInfo}->{'size'} = &getSizestring($t->{FileInfo}->{'rawsize'});
60             $t->{FileInfo}->{'view_location'} = $r->uri . "/display/" . $t->{FileInfo}->{'name'};
61             $t->{FileInfo}->{'compressed'} = 1 if $t->{FileInfo}->{'name'} =~ /\.gz$/;
62             if (! $t->{Tar}->read($t->{filename}, $t->{FileInfo}->{'compressed'})){
63             &error_response($t,$r);
64             return Apache2::Const::SERVER_ERROR;
65             }
66            
67              
68            
69             @{$t->{Files}} = $t->{Tar}->list_files(['name','mtime','size']);
70              
71             &response($t,$r);
72              
73             # We check to see if we need to kill ourselves
74            
75             if ($t->{SizeLimit} >0 && $t->{FileInfo}->{'rawsize'} > ($t->{SizeLimit} * 1024))
76             {
77             # my $log = $r->log();
78             if (getppid() > 1) # check we aren't the parent process.
79             {
80             # $log->warn("Apache2::Archive is ending this process because SizeLimit reached. Just letting you know.");
81             $r->child_terminate;
82             }
83             }
84             return Apache2::Const::OK;
85             }
86              
87             sub response{
88             my $t = shift;
89             my $r = shift;
90            
91             if ($r->path_info =~ m!^/display/!){
92             &display($t,$r);
93             }
94             else{
95             &draw_menu($t,$r);
96             }
97             }
98             ##
99             # This extracts the file specified in the path info and dumps it
100             # to stdout.
101             ##
102              
103             sub display {
104             my $t = shift;
105             my $r = shift;
106             my $filename;
107            
108             ##
109             # We need to get both the actual file ($file) and the name without
110             # any path ($filename). We use $filename to find out the mime type.
111             ##
112            
113             my $file = $r->path_info;
114             ($filename) = $file =~ m!/([^/]+)$!;
115             $file =~ s!^/display/!!;
116             $file =~ s!\*\*!\./!g; # hack because tar components with ./ at the front get mangled in path_info handling
117            
118             ##
119             # This returns the content type. You need to set up a subrequest
120             # And then run the (hypothetical) lookup against it.
121             ##
122            
123             my $subr = $r->lookup_uri("/$filename");
124             my $ct = $subr->content_type;
125            
126             if(! defined $ct){
127             $ct = 'text/plain';
128             }
129            
130             ##
131             # Create and send the response
132             ##
133            
134             $r->content_type($ct);
135              
136             #$r->print("file was $file\n path was", $r->path_info);
137             $r->print($t->{Tar}->get_content($file));
138             }
139              
140              
141             sub draw_menu {
142             my $t = shift;
143             my $r = shift;
144             my $i = 0;
145             my $dataline;
146             $r->content_type("text/html");
147            
148             ###
149             ## This loops through each line of the template file. When it sees
150             ## The StartData tag it captures the $dataline out and generates
151             ## the table. Otherwise, it just prints the line of the template file
152             ###
153            
154             while ($i < @{$t->{template}}){
155             if ($t->{template}->[$i] =~ /##\s*StartData/){
156             $i++;
157             while ($t->{template}->[$i] !~ /##\s*EndData/){
158             chomp($t->{template}->[$i]);
159             $dataline .= $t->{template}->[$i];
160             $i++;
161             }
162             &draw_data_table($t,$r,$dataline);
163             }
164             else{
165             if ($t->{template}->[$i] =~ /##\w+/){
166             $t->{template}->[$i] = do_value_subs($t,$t->{template}->[$i]);
167             }
168             $r->print($t->{template}->[$i]);
169             }
170             $i++;
171             }
172            
173            
174             }
175              
176              
177              
178              
179             ##
180             # This takes a time in seconds (since 1970 ala unix 'time()' cmd), and an
181             # optional string containing comma seperated month names. It returns
182             # a more useful indication of time and date. If no month names are specified,
183             # it defaults to english three letter abbreviations.
184             ##
185             sub getDatestring{
186             my $Seconds = shift;
187             my $Months = shift;
188             my @Months;
189             if ($Months){
190             @Months = split(/,/, $Months);
191             unless(@Months == 12){ ## Make sure they specified 12 months
192             @Months = qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec);
193             }
194             }
195             else{
196             @Months = qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec);
197             }
198             my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($Seconds);
199            
200             return("$mday-$Months[$mon]-$year $hour:$min");
201            
202             }
203             sub getSizestring{
204             my $Bytes = shift;
205             my $Kb = int($Bytes/1024) || 1;
206             if ($Kb > 1023){
207             my $Mb = $Kb/1024;
208             ## Nasty hack to round to two dp
209             $Mb = int($Mb*100)/100;
210             return("$Mb Mb");
211             }
212             else{
213             return("$Kb Kb");
214             }
215             }
216              
217             ##
218             # This gets the template file, or uses its internal one, if there
219             # is none specified.
220             ##
221             sub getTemplateFile{
222            
223             ## TODO options to cache this file (i.e. not re-read each time).
224             ###
225             my $t = shift;
226            
227             if (my $file = shift){
228             open(IN, "$file") or die $!;
229             while(){
230             push @{$t->{template}}, $_;
231             }
232             close IN;
233             }
234             else{
235             @{$t->{template}} = split(/\n/, qq(\n
236             \n
237            

\n

238             ##ArchiveName\n
239             \n
240             ##ArchiveDate
\n
241             ##ArchiveSize
\n
242             This is the contents of the archive:\n
243            

\n

244             \n \n \n \n \n \n \n
245            
246             View itemNameDateSize
247            
248             ##StartData\n
249            
250             View File##FileName##FileDate##FileSize
251            
252             ##EndData\n
253             \n
254            
\n
255             \n
256             \n
257             ));
258             }
259             return 1;
260             }
261              
262             sub draw_data_table{
263             my $t = shift;
264             my $r = shift;
265             my $dataline = shift;
266             my $moddataline;
267             my $date_string;
268             my $size_string;
269             my $name_string;
270             my $view_string;
271             my $uri = $r->uri;
272             foreach (@{$t->{Files}}){
273             $moddataline = $dataline;
274             $date_string = getDatestring($_->{'mtime'}, $r->dir_config('Months'));
275             $size_string = getSizestring($_->{'size'});
276             $name_string = $_->{'name'};
277             $view_string = $name_string;
278             $view_string =~ s!\./!\*\*!g;# prevent path_info mangling if ./
279             $view_string = $uri . "/display/" . $view_string;
280            
281             if($_->{'name'} =~ /\/$/) {
282             $moddataline =~ s/##FileLink/#/gi;
283             } else {
284             $moddataline =~ s/##FileLink/$view_string/gi;
285             }
286             $moddataline =~ s/##FileName/$name_string/gi;
287             $moddataline =~ s/##FileDate/$date_string/gi;
288             $moddataline =~ s/##FileSize/$size_string/gi;
289            
290             $moddataline =~ s/##ArchiveDate/$t->{FileInfo}->{'date'}/gi;
291             $moddataline =~ s/##ArchiveSize/$t->{FileInfo}->{'size'}/gi;
292             $moddataline =~ s/##ArchiveName/$t->{FileInfo}->{'name'}/gi;
293             $r->print($moddataline);
294             }
295             }
296              
297             sub do_value_subs{
298             my $t = shift;
299             my $line = shift;
300             $line =~ s/##ArchiveDate/$t->{FileInfo}->{'date'}/gi;
301             $line =~ s/##ArchiveSize/$t->{FileInfo}->{'size'}/gi;
302             $line =~ s/##ArchiveName/$t->{FileInfo}->{'name'}/gi;
303             $line =~ s/##ArchiveLink/$t->{FileInfo}->{'view_location'}/gi;
304             return $line;
305             }
306            
307              
308             sub error_response{
309             my $t = shift;
310             my $r = shift;
311             my $Err = shift;
312             $r->content_type("text/html");
313              
314             $r->print("500 Internal Server Error
315            

Internal Server Error

The archive file requested
316             was not a valid file, or was corrupt.");
317             Apache2->warn("Requested file ", $t->{filename}, "is unreadable by Apache2::Archive");
318             return;
319            
320             }
321              
322              
323             1;
324             __END__