| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package Plack::Middleware::DirListing; |
|
2
|
|
|
|
|
|
|
# ABSTRACT: Display a directory listing if no default index html page |
|
3
|
|
|
|
|
|
|
|
|
4
|
2
|
|
|
2
|
|
579361
|
use parent qw( Plack::Middleware ); |
|
|
2
|
|
|
|
|
4
|
|
|
|
2
|
|
|
|
|
35
|
|
|
5
|
2
|
|
|
2
|
|
1164
|
use DirHandle; |
|
|
2
|
|
|
|
|
1146
|
|
|
|
2
|
|
|
|
|
59
|
|
|
6
|
2
|
|
|
2
|
|
1034
|
use HTML::Entities; |
|
|
2
|
|
|
|
|
12173
|
|
|
|
2
|
|
|
|
|
208
|
|
|
7
|
2
|
|
|
2
|
|
1184
|
use Plack::Request; |
|
|
2
|
|
|
|
|
185389
|
|
|
|
2
|
|
|
|
|
105
|
|
|
8
|
2
|
|
|
2
|
|
1160
|
use Plack::MIME; |
|
|
2
|
|
|
|
|
2210
|
|
|
|
2
|
|
|
|
|
128
|
|
|
9
|
2
|
|
|
2
|
|
14
|
use Plack::Util::Accessor qw( root ); |
|
|
2
|
|
|
|
|
3
|
|
|
|
2
|
|
|
|
|
22
|
|
|
10
|
2
|
|
|
2
|
|
146
|
use URI::Escape; |
|
|
2
|
|
|
|
|
3
|
|
|
|
2
|
|
|
|
|
126
|
|
|
11
|
2
|
|
|
2
|
|
1174
|
use Time::Piece; |
|
|
2
|
|
|
|
|
25994
|
|
|
|
2
|
|
|
|
|
9
|
|
|
12
|
|
|
|
|
|
|
|
|
13
|
2
|
|
|
2
|
|
182
|
use strict; |
|
|
2
|
|
|
|
|
3
|
|
|
|
2
|
|
|
|
|
51
|
|
|
14
|
2
|
|
|
2
|
|
8
|
use warnings; |
|
|
2
|
|
|
|
|
3
|
|
|
|
2
|
|
|
|
|
112
|
|
|
15
|
2
|
|
|
2
|
|
22
|
use v5.10; |
|
|
2
|
|
|
|
|
6
|
|
|
16
|
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
sub dir_html { |
|
18
|
|
|
|
|
|
|
|
|
19
|
5
|
|
|
5
|
0
|
6
|
state $html = do { |
|
20
|
2
|
|
|
|
|
16
|
local $/ = undef; |
|
21
|
2
|
|
|
|
|
155
|
my $data_string = ; |
|
22
|
2
|
|
|
|
|
27
|
close DATA; |
|
23
|
|
|
|
|
|
|
|
|
24
|
2
|
|
|
|
|
193
|
$data_string =~ s/%(?!s)/%%/g; |
|
25
|
2
|
|
|
|
|
93
|
$data_string; |
|
26
|
|
|
|
|
|
|
}; |
|
27
|
|
|
|
|
|
|
|
|
28
|
5
|
|
|
|
|
342
|
return $html; |
|
29
|
|
|
|
|
|
|
} |
|
30
|
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
sub file_html { |
|
32
|
26
|
|
|
26
|
0
|
38
|
return <
|
|
33
|
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
| |
|
35
|
|
|
|
|
|
|
| %s |
|
36
|
|
|
|
|
|
|
| %s |
|
37
|
|
|
|
|
|
|
| %s |
|
38
|
|
|
|
|
|
|
| %s |
|
39
|
|
|
|
|
|
|
|
|
40
|
|
|
|
|
|
|
FILE |
|
41
|
|
|
|
|
|
|
} |
|
42
|
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
sub last_modified { |
|
44
|
21
|
|
|
21
|
0
|
24
|
my ($self, $date) = @_; |
|
45
|
|
|
|
|
|
|
|
|
46
|
21
|
|
|
|
|
59
|
return Time::Piece->new( $date )->strftime( "%d-%b-%Y %H:%M" ); |
|
47
|
|
|
|
|
|
|
} |
|
48
|
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
sub sort_order { |
|
50
|
5
|
|
|
5
|
0
|
14
|
my ($self, $env, $page) = @_; |
|
51
|
|
|
|
|
|
|
|
|
52
|
5
|
100
|
|
|
|
43
|
if (my ($field, $order) = $env->{ QUERY_STRING } =~ /C=(\w);O=(\w)/) { |
|
53
|
3
|
100
|
|
|
|
11
|
my $invert = ($order eq 'A') ? 'D' : 'A'; |
|
54
|
3
|
|
|
|
|
101
|
$page =~ s/C=$field;O=$order/C=$field;O=$invert/; |
|
55
|
|
|
|
|
|
|
} |
|
56
|
|
|
|
|
|
|
|
|
57
|
5
|
|
|
|
|
41
|
return $page; |
|
58
|
|
|
|
|
|
|
} |
|
59
|
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
my %col_sort = ( |
|
61
|
|
|
|
|
|
|
'NA' => sub { $a->[2] cmp $b->[2] }, |
|
62
|
|
|
|
|
|
|
'ND' => sub { $b->[2] cmp $a->[2] }, |
|
63
|
|
|
|
|
|
|
'MA' => sub { $a->[6] <=> $b->[6] }, |
|
64
|
|
|
|
|
|
|
'MD' => sub { $b->[6] <=> $a->[6] }, |
|
65
|
|
|
|
|
|
|
'SA' => sub { $a->[4] <=> $b->[4] }, |
|
66
|
|
|
|
|
|
|
'SD' => sub { $b->[4] <=> $a->[4] }, |
|
67
|
|
|
|
|
|
|
'DA' => sub { $a->[5] cmp $b->[5] }, |
|
68
|
|
|
|
|
|
|
'DD' => sub { $b->[5] cmp $a->[5] }, |
|
69
|
|
|
|
|
|
|
); |
|
70
|
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
sub filetype_class { |
|
72
|
21
|
|
|
21
|
0
|
26
|
my ($self, $filetype) = @_; |
|
73
|
|
|
|
|
|
|
|
|
74
|
21
|
50
|
|
|
|
31
|
return 'ft_directory' if ($filetype eq 'directory'); |
|
75
|
21
|
50
|
|
|
|
61
|
return 'ft_image' if ($filetype =~ /^image/); |
|
76
|
21
|
50
|
|
|
|
27
|
return 'ft_pdf' if ($filetype =~ /pdf$/); |
|
77
|
21
|
50
|
|
|
|
32
|
return 'ft_html' if ($filetype =~ /html$/); |
|
78
|
|
|
|
|
|
|
|
|
79
|
21
|
|
|
|
|
37
|
return ''; |
|
80
|
|
|
|
|
|
|
} |
|
81
|
|
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
sub read_dir { |
|
84
|
5
|
|
|
5
|
0
|
12
|
my ($self, $env, $dir) = @_; |
|
85
|
|
|
|
|
|
|
|
|
86
|
5
|
|
|
|
|
6
|
my @files; |
|
87
|
|
|
|
|
|
|
|
|
88
|
5
|
|
|
|
|
30
|
my $dh = DirHandle->new($dir); |
|
89
|
|
|
|
|
|
|
|
|
90
|
5
|
|
|
|
|
412
|
while (defined(my $ent = $dh->read)) { |
|
91
|
31
|
100
|
100
|
|
|
1606
|
next if $ent eq '.' or $ent eq '..'; |
|
92
|
|
|
|
|
|
|
|
|
93
|
21
|
|
|
|
|
29
|
my $file = "$dir/$ent"; |
|
94
|
21
|
|
|
|
|
34
|
my $url = $env->{PATH_INFO} . $ent; |
|
95
|
|
|
|
|
|
|
|
|
96
|
21
|
|
|
|
|
221
|
my $is_dir = -d $file; |
|
97
|
21
|
|
|
|
|
45
|
my @stat = stat _; |
|
98
|
|
|
|
|
|
|
|
|
99
|
21
|
|
|
|
|
58
|
$url = join '/', map {uri_escape($_)} split m{/}, $url; |
|
|
43
|
|
|
|
|
398
|
|
|
100
|
|
|
|
|
|
|
|
|
101
|
21
|
50
|
|
|
|
209
|
if ($is_dir) { |
|
102
|
0
|
|
|
|
|
0
|
$ent .= "/"; |
|
103
|
0
|
|
|
|
|
0
|
$url .= "/"; |
|
104
|
|
|
|
|
|
|
} |
|
105
|
|
|
|
|
|
|
|
|
106
|
21
|
50
|
50
|
|
|
75
|
my $mime_type = $is_dir ? 'directory' : ( Plack::MIME->mime_type($file) || 'text/plain' ); |
|
107
|
21
|
|
|
|
|
226
|
my $filetype_class = $self->filetype_class( $mime_type ); |
|
108
|
21
|
|
|
|
|
49
|
push @files, [ $filetype_class, $url, $ent, $self->last_modified( $stat[9] ), $stat[7], $mime_type, $stat[9] ]; |
|
109
|
|
|
|
|
|
|
} |
|
110
|
|
|
|
|
|
|
|
|
111
|
5
|
|
|
|
|
795
|
my ($field, $order) = $env->{ QUERY_STRING } =~ /C=(\w);O=(\w)/; |
|
112
|
5
|
|
100
|
|
|
19
|
$field ||= 'N'; |
|
113
|
5
|
|
100
|
|
|
26
|
$order ||= 'A'; |
|
114
|
|
|
|
|
|
|
|
|
115
|
5
|
|
|
|
|
35
|
@files = sort { &{ $col_sort{ "$field$order" } } } @files; |
|
|
27
|
|
|
|
|
24
|
|
|
|
27
|
|
|
|
|
29
|
|
|
116
|
|
|
|
|
|
|
|
|
117
|
5
|
|
|
|
|
31
|
return [ [ 'ft_parent', "../", "Parent Directory", '', '', '', 0], @files ]; |
|
118
|
|
|
|
|
|
|
} |
|
119
|
|
|
|
|
|
|
|
|
120
|
|
|
|
|
|
|
sub prepare_app { |
|
121
|
2
|
|
|
2
|
1
|
713054
|
my ($self) = @_; |
|
122
|
|
|
|
|
|
|
|
|
123
|
2
|
50
|
|
|
|
13
|
$self->root('.') unless $self->root; |
|
124
|
|
|
|
|
|
|
} |
|
125
|
|
|
|
|
|
|
|
|
126
|
|
|
|
|
|
|
# NOTE: Copied from Plack::App::Directory as that module makes it |
|
127
|
|
|
|
|
|
|
# impossible to override the HTML. |
|
128
|
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
sub serve_path { |
|
130
|
5
|
|
|
5
|
0
|
32
|
my $self = shift; |
|
131
|
5
|
|
|
|
|
10
|
my ($env, $dir) = @_; |
|
132
|
|
|
|
|
|
|
|
|
133
|
5
|
|
|
|
|
14
|
my $files = $self->read_dir( $env, $dir ); |
|
134
|
|
|
|
|
|
|
|
|
135
|
5
|
|
|
|
|
187
|
my $path = Plack::Util::encode_html("Index of $env->{PATH_INFO}"); |
|
136
|
|
|
|
|
|
|
my $files_html = join "\n", map { |
|
137
|
26
|
|
|
|
|
952
|
my $f = $_; |
|
138
|
26
|
|
|
|
|
67
|
sprintf $self->file_html, map Plack::Util::encode_html($_), @{ $f }[ 0..5 ]; |
|
|
26
|
|
|
|
|
72
|
|
|
139
|
5
|
|
|
|
|
60
|
} @{ $files }; |
|
|
5
|
|
|
|
|
11
|
|
|
140
|
5
|
|
|
|
|
237
|
my $page = sprintf $self->dir_html, $path, $path, $files_html, $env->{ HTTP_HOST }; |
|
141
|
|
|
|
|
|
|
|
|
142
|
5
|
|
|
|
|
28
|
$page = $self->sort_order( $env, $page ); |
|
143
|
|
|
|
|
|
|
|
|
144
|
5
|
|
|
|
|
106
|
return [ 200, ['Content-Type' => 'text/html; charset=utf-8'], [ $page ] ]; |
|
145
|
|
|
|
|
|
|
} |
|
146
|
|
|
|
|
|
|
|
|
147
|
|
|
|
|
|
|
sub call { |
|
148
|
7
|
|
|
7
|
1
|
93022
|
my ( $self, $env ) = @_; |
|
149
|
7
|
|
|
|
|
59
|
my $req = Plack::Request->new( $env ); |
|
150
|
|
|
|
|
|
|
|
|
151
|
7
|
|
|
|
|
90
|
my $dir = $self->root . $req->path_info(); |
|
152
|
7
|
50
|
|
|
|
288
|
if (-d $dir) { |
|
153
|
7
|
100
|
|
|
|
28
|
if (substr( $dir, -1 ) eq '/') { |
|
154
|
5
|
|
|
|
|
16
|
return $self->serve_path( $env, $dir ); |
|
155
|
|
|
|
|
|
|
} |
|
156
|
|
|
|
|
|
|
else { |
|
157
|
2
|
|
|
|
|
34
|
my $uri = $req->uri(); |
|
158
|
2
|
|
|
|
|
796
|
$uri->path( $uri->path . '/' ); |
|
159
|
2
|
|
|
|
|
152
|
my $res = $req->new_response(301); # new Plack::Response |
|
160
|
2
|
|
|
|
|
2617
|
$res->headers([ |
|
161
|
|
|
|
|
|
|
'Location' => $uri, |
|
162
|
|
|
|
|
|
|
'Content-Type' => 'text/html; charset=UTF-8', |
|
163
|
|
|
|
|
|
|
'Cache-Control' => 'must-revalidate, max-age=3600' |
|
164
|
|
|
|
|
|
|
]); |
|
165
|
|
|
|
|
|
|
|
|
166
|
2
|
|
|
|
|
318
|
my $uhe = encode_entities($uri); |
|
167
|
2
|
|
|
|
|
87
|
$res->body( <
|
|
168
|
|
|
|
|
|
|
301 Moved PermanentlyMoved PermanentlyThe document has moved here. |
|
169
|
|
|
|
|
|
|
REDIRECT_BODY |
|
170
|
|
|
|
|
|
|
|
|
171
|
2
|
|
|
|
|
33
|
return $res->finalize; |
|
172
|
|
|
|
|
|
|
} |
|
173
|
|
|
|
|
|
|
} |
|
174
|
|
|
|
|
|
|
|
|
175
|
0
|
|
|
|
|
|
return $self->app->($env); |
|
176
|
|
|
|
|
|
|
} |
|
177
|
|
|
|
|
|
|
|
|
178
|
|
|
|
|
|
|
=head1 NAME |
|
179
|
|
|
|
|
|
|
|
|
180
|
|
|
|
|
|
|
Plack::Middleware::DirListing - Display a listing of a directory in html |
|
181
|
|
|
|
|
|
|
|
|
182
|
|
|
|
|
|
|
=head1 SYNOPSIS |
|
183
|
|
|
|
|
|
|
|
|
184
|
|
|
|
|
|
|
use Plack::Builder; |
|
185
|
|
|
|
|
|
|
use Plack::App::File; |
|
186
|
|
|
|
|
|
|
|
|
187
|
|
|
|
|
|
|
my $app = Plack::App::File->new({ root => '.' })->to_app; |
|
188
|
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
builder { |
|
190
|
|
|
|
|
|
|
enable "DirListing", root => '.'; |
|
191
|
|
|
|
|
|
|
$app; |
|
192
|
|
|
|
|
|
|
} |
|
193
|
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
=head1 DESCRIPTION |
|
195
|
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
This Plack middleware provides the same functionality as L, but *only* serves the directory listing if the URL points to a directory. It does not try to serve any files. |
|
197
|
|
|
|
|
|
|
|
|
198
|
|
|
|
|
|
|
It also strives to have a cleaner UI that more closely matches a prettified version of the Apache web server's output. |
|
199
|
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
This modules does not attempt to find a default html file for the directory. If desired, include L or L before this module. |
|
201
|
|
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
=head1 CONFIGURATION |
|
203
|
|
|
|
|
|
|
|
|
204
|
|
|
|
|
|
|
=over 4 |
|
205
|
|
|
|
|
|
|
|
|
206
|
|
|
|
|
|
|
=item root |
|
207
|
|
|
|
|
|
|
|
|
208
|
|
|
|
|
|
|
Document root directory. Defaults to the current directory. |
|
209
|
|
|
|
|
|
|
|
|
210
|
|
|
|
|
|
|
=back |
|
211
|
|
|
|
|
|
|
|
|
212
|
|
|
|
|
|
|
=head1 AUTHOR |
|
213
|
|
|
|
|
|
|
|
|
214
|
|
|
|
|
|
|
Keith Carangelo |
|
215
|
|
|
|
|
|
|
|
|
216
|
|
|
|
|
|
|
=cut |
|
217
|
|
|
|
|
|
|
|
|
218
|
|
|
|
|
|
|
1; |
|
219
|
|
|
|
|
|
|
|
|
220
|
|
|
|
|
|
|
__DATA__ |