File Coverage

blib/lib/LWP/Protocol/file.pm
Criterion Covered Total %
statement 56 64 87.5
branch 20 34 58.8
condition 3 9 33.3
subroutine 4 4 100.0
pod 1 1 100.0
total 84 112 75.0


line stmt bran cond sub pod time code
1             package LWP::Protocol::file;
2              
3 3     3   467 use base qw(LWP::Protocol);
  3         6  
  3         281  
4              
5 3     3   15 use strict;
  3         6  
  3         2063  
6              
7             our $VERSION = '6.34';
8              
9             require LWP::MediaTypes;
10             require HTTP::Request;
11             require HTTP::Response;
12             require HTTP::Status;
13             require HTTP::Date;
14              
15              
16             sub request
17             {
18 7     7 1 16 my($self, $request, $proxy, $arg, $size) = @_;
19              
20 7 50 33     18 $size = 4096 unless defined $size and $size > 0;
21              
22             # check proxy
23 7 50       14 if (defined $proxy)
24             {
25 0         0 return HTTP::Response->new( HTTP::Status::RC_BAD_REQUEST,
26             'You can not proxy through the filesystem');
27             }
28              
29             # check method
30 7         18 my $method = $request->method;
31 7 50 66     94 unless ($method eq 'GET' || $method eq 'HEAD') {
32 0         0 return HTTP::Response->new( HTTP::Status::RC_BAD_REQUEST,
33             'Library does not allow method ' .
34             "$method for 'file:' URLs");
35             }
36              
37             # check url
38 7         18 my $url = $request->uri;
39              
40 7         54 my $scheme = $url->scheme;
41 7 50       974 if ($scheme ne 'file') {
42 0         0 return HTTP::Response->new( HTTP::Status::RC_INTERNAL_SERVER_ERROR,
43             "LWP::Protocol::file::request called for '$scheme'");
44             }
45              
46             # URL OK, look at file
47 7         22 my $path = $url->file;
48              
49             # test file exists and is readable
50 7 50       2585 unless (-e $path) {
51 0         0 return HTTP::Response->new( HTTP::Status::RC_NOT_FOUND,
52             "File `$path' does not exist");
53             }
54 7 50       58 unless (-r _) {
55 0         0 return HTTP::Response->new( HTTP::Status::RC_FORBIDDEN,
56             'User does not have read permission');
57             }
58              
59             # looks like file exists
60 7         29 my($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$filesize,
61             $atime,$mtime,$ctime,$blksize,$blocks)
62             = stat(_);
63              
64             # XXX should check Accept headers?
65              
66             # check if-modified-since
67 7         67 my $ims = $request->header('If-Modified-Since');
68 7 50       409 if (defined $ims) {
69 0         0 my $time = HTTP::Date::str2time($ims);
70 0 0 0     0 if (defined $time and $time >= $mtime) {
71 0         0 return HTTP::Response->new( HTTP::Status::RC_NOT_MODIFIED,
72             "$method $path");
73             }
74             }
75              
76             # Ok, should be an OK response by now...
77 7         28 my $response = HTTP::Response->new( HTTP::Status::RC_OK );
78              
79             # fill in response headers
80 7         366 $response->header('Last-Modified', HTTP::Date::time2str($mtime));
81              
82 7 100       500 if (-d _) { # If the path is a directory, process it
83             # generate the HTML for directory
84 2 50       79 opendir(D, $path) or
85             return HTTP::Response->new( HTTP::Status::RC_INTERNAL_SERVER_ERROR,
86             "Cannot read directory '$path': $!");
87 2         136 my(@files) = sort readdir(D);
88 2         49 closedir(D);
89              
90             # Make directory listing
91 2         14 require URI::Escape;
92 2         929 require HTML::Entities;
93 2 50       9534 my $pathe = $path . ( $^O eq 'MacOS' ? ':' : '/');
94 2         5 for (@files) {
95 52         98 my $furl = URI::Escape::uri_escape($_);
96 52 100       962 if ( -d "$pathe$_" ) {
97 16         33 $furl .= '/';
98 16         18 $_ .= '/';
99             }
100 52         133 my $desc = HTML::Entities::encode($_);
101 52         643 $_ = qq{
  • $desc};
  • 102             }
    103             # Ensure that the base URL is "/" terminated
    104 2         26 my $base = $url->clone;
    105 2 50       23 unless ($base->path =~ m|/$|) {
    106 2         71 $base->path($base->path . "/");
    107             }
    108 2         132 my $html = join("\n",
    109             "\n",
    110             "Directory $path",
    111             "",
    112             "\n",
    113             "

    Directory listing of $path

    ",
    114             "
      ", @files, "
    ",
    115             "\n\n");
    116              
    117 2         40 $response->header('Content-Type', 'text/html');
    118 2         119 $response->header('Content-Length', length $html);
    119 2 50       81 $html = "" if $method eq "HEAD";
    120              
    121 2         20 return $self->collect_once($arg, $response, $html);
    122              
    123             }
    124              
    125             # path is a regular file
    126 5         12 $response->header('Content-Length', $filesize);
    127 5         192 LWP::MediaTypes::guess_media_type($path, $response);
    128              
    129             # read the file
    130 5 100       757 if ($method ne "HEAD") {
    131 3 50       115 open(my $fh, '<', $path) or return new
    132             HTTP::Response(HTTP::Status::RC_INTERNAL_SERVER_ERROR,
    133             "Cannot read file '$path': $!");
    134 3         10 binmode($fh);
    135             $response = $self->collect($arg, $response, sub {
    136 6     6   6 my $content = "";
    137 6         66 my $bytes = sysread($fh, $content, $size);
    138 6 100       28 return \$content if $bytes > 0;
    139 3         19 return \ "";
    140 3         26 });
    141 3         32 close($fh);
    142             }
    143              
    144 5         25 $response;
    145             }
    146              
    147             1;