File Coverage

lib/HTTP/Server/VirtualHost.pm
Criterion Covered Total %
statement 33 125 26.4
branch 0 68 0.0
condition 0 13 0.0
subroutine 11 26 42.3
pod 0 12 0.0
total 44 244 18.0


line stmt bran cond sub pod time code
1             # Copyrights 2008 by Mark Overmeer.
2             # For other contributors see ChangeLog.
3             # See the manual pages for details on the licensing terms.
4             # Pod stripped from pm file by OODoc 1.05.
5             package HTTP::Server::VirtualHost;
6 1     1   5 use vars '$VERSION';
  1         2  
  1         50  
7             $VERSION = '0.11';
8              
9 1     1   4 use warnings;
  1         9  
  1         29  
10 1     1   6 use strict;
  1         2  
  1         40  
11              
12 1     1   6 use HTTP::Server::Multiplex;
  1         2  
  1         34  
13 1     1   505 use HTTP::Server::Directory;
  1         3  
  1         37  
14 1     1   631 use HTTP::Server::Directory::UserDirs;
  1         2  
  1         33  
15              
16 1     1   6 use Log::Report 'httpd-multiplex', syntax => 'SHORT';
  1         2  
  1         6  
17              
18 1     1   1255 use HTTP::Status;
  1         3954  
  1         343  
19 1     1   8 use List::Util qw/first/;
  1         2  
  1         60  
20 1     1   739 use English qw/$EUID/;
  1         1924  
  1         5  
21 1     1   234 use File::Spec ();
  1         3  
  1         1463  
22              
23              
24             sub new(@)
25 0     0 0   { my $class = shift;
26 0 0         my $args = @_==1 ? shift : {@_};
27 0           (bless {}, $class)->init($args);
28             }
29              
30             sub init($)
31 0     0 0   { my ($self, $args) = @_;
32              
33 0           my $name = $self->{HSV_name} = $args->{name};
34 0 0         defined $name
35             or error __x"virtual host {pkg} has no name", pkg => ref $self;
36              
37 0   0       my $aliases = $args->{aliases} || [];
38 0 0         $self->{HSV_aliases} = ref $aliases eq 'ARRAY' ? $aliases : [$aliases];
39              
40 0   0 0     $self->{HSV_rewrite} = $args->{rewrite} || sub {$_[0]};
  0            
41 0           $self->{HSV_dirlist} = $args->{directory_list};
42 0   0       $self->{HSV_handlers} = $args->{handlers} || {};
43              
44 0           $self->{HSV_dirs} = {};
45 0 0         if(my $docroot = $args->{documents})
46 0 0         { File::Spec->file_name_is_absolute($docroot)
47             or error __x"vhost {name} documents directory must be absolute"
48             , name => $name;
49 0 0         -d $docroot
50             or error __x"vhost {name} documents `{dir}' must point to dir"
51             , name => $name, dir => $docroot;
52 0           $docroot =~ s/\\$//; # strip trailing / if present
53 0           $self->addDirectory(path => '/', location => $docroot);
54             }
55 0   0       my $dirs = $args->{directories} || [];
56 0 0         $self->addDirectory($_) for ref $dirs eq 'ARRAY' ? @$dirs : $dirs;
57              
58 0           my $ud;
59 0 0         if(!exists $args->{user_dirs})
    0          
60 0           { $ud = HTTP::Server::Directory::UserDirs->new }
61             elsif($ud = $args->{user_dirs})
62 0 0         { if(ref $ud eq 'HASH')
    0          
63 0           { $ud = HTTP::Server::Directory::UserDirs->new($ud) }
64             elsif(not $ud->isa('HTTP::Server::Directory::UserDirs'))
65 0           { error __x"vhost {name} user_dirs is not an ::UserDirs object"
66             , name => $self->name;
67             }
68             }
69 0           $self->{HSV_udirs} = $ud;
70              
71 0           my $if = $args->{index_file};
72 0 0         my @if = ref $if eq 'ARRAY' ? @$if
    0          
73             : defined $if ? $if
74             : qw/index.html index.html/;
75 0           $self->{HSV_indexfns} = \@if;
76              
77 0           $self;
78             }
79              
80             #---------------------
81              
82 0     0 0   sub name() {shift->{HSV_name}}
83 0     0 0   sub aliases() {@{shift->{HSV_aliases}}}
  0            
84              
85             #---------------------
86              
87             sub requestForMe($)
88 0     0 0   { my ($self, $uri) = @_;
89 0           my $host = $uri->host;
90 0 0   0     $host eq $self->name || first {$host eq $_} $self->aliases;
  0            
91             }
92              
93              
94             sub handleRequest($$)
95 0     0 0   { my ($self, $conn, $req) = @_;
96              
97 0           my $uri = $self->rewrite($req->uri);
98 0 0         if($uri ne $req->uri)
99 0           { info $req->id." rewritten to $uri";
100 0 0         $self->requestForMe($uri)
101             or return $conn->sendRedirect($req, RC_TEMPORARY_REDIRECT, $uri);
102             }
103              
104 0           my $path = $uri->path;
105 0 0         my $tree = $self->directoryOf($path)
106             or return $conn->sendStatus($req, RC_FORBIDDEN, "$path not configured");
107              
108 0 0         $tree->allow($conn->client, $conn->session, $req, $uri)
109             or return
110             $conn->sendStatus($req, RC_FORBIDDEN, "$path access not allowed");
111              
112 0 0         if(my $handler = $self->{HSV_handlers}{$path})
113 0           { return $handler->($conn, $req, $uri);
114             }
115              
116 0           my $item = $tree->filename($path);
117              
118 0 0         -f $item # filename
119             and return $conn->sendFile($req, $item);
120              
121 0 0         -d _ # neither file nor directory
122             or return $conn->sendStatus($req, RC_NOT_FOUND
123             , "special file cannot be accessed");
124              
125 0 0         substr($item, -1) eq '/'
126             or return $conn->sendRedirect($req, RC_TEMPORARY_REDIRECT, $path .'/');
127              
128 0           foreach my $if (@{$self->{HSV_indexfns}})
  0            
129 0 0         { return $conn->sendFile($req, $item.$if, [Location => $path.$if])
130             if -f $item.$if;
131             }
132              
133 0 0         $self->{HSV_dirlist}
134             or return $conn->sendStatus($req, RC_FORBIDDEN, "no directory list");
135              
136             # Directory handling
137              
138             $conn->directoryList($req, $item
139 0     0     , sub { my $list = shift;
140 0 0         return $list if UNIVERSAL::isa($list, 'HTTP::Response');
141 0           $self->showDirectory($conn, $req, $path, $list);
142 0           });
143             }
144              
145              
146             sub showDirectory($$$$)
147 0     0 0   { my ($self, $conn, $req, $dir, $list) = @_;
148 0           my $now = localtime;
149 0           my @rows;
150 0 0         push @rows, <<__UP if $dir ne '/';
151            
 (up)
152             __UP
153              
154 0           foreach my $item (sort keys %$list)
155 0           { my $d = $list->{$item};
156 0           push @rows, <<__ROW;
157            
$d->{flags}
158             $d->{user}
159             $d->{group}
160             $d->{size_nice}
161             $d->{mtime_nice}
162             $d->{name}
163             __ROW
164             }
165              
166 0           local $" = "\n";
167 0           $conn->sendResponse($req, RC_OK, [], <<__PAGE);
168             $dir
169            
170            
171            

Directory $dir

172            
173             @rows
174            
175            

Generated $now

176            
177             __PAGE
178             }
179              
180             #----------------------
181              
182 0     0 0   sub rewrite($) { $_[0]->{HSV_rewrite}->($_[1]) }
183              
184              
185             sub allow($$$$)
186 0     0 0   { my ($self, $client, $session, $req, $uri) = @_;
187              
188 0 0 0       if($EUID==0 && substr($uri->path, 0, 2) eq '/~')
189 0           { notice "deamon running as root, {session} only access to {path}"
190             , session => $session->id, path => '/~user';
191 0           return 0;
192             }
193 0           1;
194             }
195              
196             #------------------
197              
198             sub filename($)
199 0     0 0   { my ($self, $uri) = @_;
200              
201 0           my $path = $uri->path;
202 0           my $dir = $self->directoryOf($path);
203 0 0         $dir ? $dir->filename($path) : undef;
204             }
205              
206              
207             sub addDirectory(@)
208 0     0 0   { my $self = shift;
209 0 0         my $dir = @_==1 ? shift : HTTP::Server::Directory->new(@_);
210 0   0       my $path = $dir->path || '';
211 0 0         !exists $self->{HSV_dirs}{$path}
212             or error __x"vhost {name} directory `{path}' defined twice"
213             , name => $self->name, path => $path;
214 0           $self->{HSV_dirs}{$path} = $dir;
215             }
216              
217              
218             sub directoryOf($)
219 0     0 0   { my ($self, $path) = @_;
220 0 0         return $self->{HSV_udirs}
221             if $path =~ m!^/\~!;
222              
223 0           my $dirs = $self->{HSV_dirs};
224 0           $path =~ s!/$!!;
225              
226 0           while(length $path)
227 0 0         { return $dirs->{$path} if $dirs->{$path};
228 0 0         $path =~ s!/[^/]+$!! or return;
229             }
230 0 0         $dirs->{'/'} ? $dirs->{'/'} : ();
231             }
232              
233              
234              
235             1;