File Coverage

blib/lib/Net/HTTPServer.pm
Criterion Covered Total %
statement 114 735 15.5
branch 27 308 8.7
condition 3 63 4.7
subroutine 14 66 21.2
pod 8 8 100.0
total 166 1180 14.0


line stmt bran cond sub pod time code
1             ##############################################################################
2             #
3             # This library is free software; you can redistribute it and/or
4             # modify it under the terms of the GNU Library General Public
5             # License as published by the Free Software Foundation; either
6             # version 2 of the License, or (at your option) any later version.
7             #
8             # This library is distributed in the hope that it will be useful,
9             # but WITHOUT ANY WARRANTY; without even the implied warranty of
10             # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11             # Library General Public License for more details.
12             #
13             # You should have received a copy of the GNU Library General Public
14             # License along with this library; if not, write to the
15             # Free Software Foundation, Inc., 59 Temple Place - Suite 330,
16             # Boston, MA 02111-1307, USA.
17             #
18             # Copyright (C) 2003-2005 Ryan Eatmon
19             #
20             ##############################################################################
21             package Net::HTTPServer;
22              
23             =head1 NAME
24              
25             Net::HTTPServer
26              
27             =head1 SYNOPSIS
28              
29             Net::HTTPServer provides a lite HTTP server. It can serve files, or can
30             be configured to call Perl functions when a URL is accessed.
31              
32             =head1 DESCRIPTION
33              
34             Net::HTTPServer basically turns a CGI script into a stand alone server.
35             Useful for temporary services, mobile/local servers, or embedding an HTTP
36             server into another program.
37              
38             =head1 EXAMPLES
39              
40             use Net::HTTPServer;
41              
42             my $server = new Net::HTTPServer(port=>5000,
43             docroot=>"/var/www/site");
44              
45             $server->Start();
46              
47             $server->Process(); # Run forever
48              
49             ...or...
50              
51             while(1)
52             {
53             $server->Process(5); # Run for 5 seconds
54             # Do something else...
55             }
56              
57             $server->Stop();
58              
59             =head1 METHODS
60              
61             =head2 new(%cfg)
62              
63             Given a config hash, return a server object that you can start, process,
64             and stop. The config hash takes the options:
65              
66             chroot => 0|1 - Run the server behind a virtual chroot().
67             Since only root can actually call chroot,
68             a URL munger is provided that will not
69             allow URLs to go beyond the document root
70             if this is specified.
71             ( Default: 1 )
72              
73             datadir => string - Path on the filesystem where you want to
74             store the server side session files.
75             ( Deault: "/tmp/nethttpserver.sessions" )
76              
77             docroot => string - Path on the filesystem that you want to be
78             the document root "/" for the server. If
79             set to undef, then the server will not serve
80             any files off the local filesystem, but will
81             still serve callbacks.
82             ( Default: undef )
83              
84             index => list - Specify a list of file names to use as the
85             the index file when a directory is requested.
86             ( Default: ["index.html","index.htm"] )
87              
88             log => string - Path to store the log at. If you set this to
89             "STDOUT" then it will display to STDOUT.
90             ( Default: access.log )
91              
92             mimetypes => string - Path to an alternate mime.types file.
93             ( Default: included in release )
94              
95             numproc => int - When type is set to "forking", this tells the
96             server how many child processes to keep
97             running at all times.
98             ( Default: 5 )
99              
100             oldrequests => 0|1 - With the new request objects, old programs
101             will not work. To postpone updating your
102             code, just set this to 1 and your programs
103             should work again.
104             ( Default: 0 )
105            
106             port => int - Port number to use. You can optionally
107             specify the string "scan", and the server
108             will loop through ports until it finds one
109             it can listen on. This port is then returned
110             by the Start() method.
111             ( Default: 9000 )
112              
113             sessions => 0|1 - Enable/disable server side session support.
114             ( Default: 0 )
115            
116             ssl => 0|1 - Run a secure server using SSL. You must
117             specify ssl_key, ssl_cert, and ssl_ca if
118             set this to 1.
119             ( Default: 0 )
120              
121             ssl_ca => string - Path to the SSL ca file.
122             ( Default: undef )
123              
124             ssl_cert => string - Path to the SSL cert file.
125             ( Default: undef )
126              
127             ssl_key => string - Path to the SSL key file.
128             ( Default: undef )
129              
130             type => string - What kind of server to create? Available
131             types are:
132             single - single process/no forking
133             forking - preforking server
134             (Default: "single")
135              
136              
137             =head2 AddServerTokens(token,[token,...])
138              
139             Adds one or more tokens onto the Server header line that the server sends
140             back in a response. The list is seperated by a ; to distinguish the
141             various tokens from each other.
142              
143             $server->AddServerTokens("test/1.3");
144              
145             This would result in the following header being sent in a response:
146              
147             HTTP/1.1 200
148             Server: Net::HTTPServer/0.9 test/1.3
149             Content-Type: text/html
150             ...
151              
152             =head2 Process(timeout)
153              
154             Listens for incoming requests and responds back to them. This function
155             will block, unless a timeout is specified, then it will block for that
156             number of seconds before returning. Useful for embedding this into
157             other programs and still letting the other program get some CPU time.
158              
159             =head2 RegisterAuth(method,url,realm,function)
160              
161             Protect the URL using the Authentication method provided. The supported
162             methods are: "Basic" and "Digest".
163              
164             When a URL with a path component that matchs the specified URL is
165             requested the server requests that the client perform the specified
166             of authentication for the given realm. When the URL is accessed the
167             second time, the client provides the authentication pieces and the
168             server parses the pieces and using the return value from the specified
169             function answers the request. The function is called with the username
170             and the URL they are trying to access. It is required that the function
171             return a two item list with a return code and the users's password.
172              
173             The valid return codes are:
174              
175             200 The user exists and is allowed to access
176             this URL. Return the password.
177             return( "200", password )
178              
179             401 The user does not exist. Obviously you
180             do not have to return a password in this
181             case.
182             return( "401" )
183              
184             403 The user is forbidden to access this URL.
185             (You must still return the password because
186             if the user did not auth, then we do not want
187             to tip off the bad people that this username
188             is valid.)
189             return( "403", password )
190              
191             The reasoning for having the function return the password is that Digest
192             authentication is just complicated enough that asking you to write part of
193             logic would be considered rude. By just having you give the server the
194             password we can keep the whole Auth interface simple.
195              
196             Here is an example:
197              
198             $server->RegisterAuth("Basic","/foo/bar.pl","Secure",\&testBasic);
199              
200             sub testBasic
201             {
202             my $url = shift;
203             my $user = shift;
204              
205             my $password = &lookupPassword($user);
206            
207             return("401","") unless defined($password);
208            
209             if (($url eq "/foo/bar.pl") && ($user eq "dr_evil"))
210             {
211             return ("403",$password);
212             }
213              
214             return ("200",$password);
215             }
216              
217             sub lookupPassword
218             {
219             my $user = shift;
220              
221             my %passwd;
222             $passwd{larry} = "wall";
223             $passwd{dr_evil} = "1million";
224              
225             return unless exists($passwd{$user});
226             return $passwd{$user};
227             }
228              
229             Start a server with that, and the following RegisterURL example,
230             and point your browser to:
231              
232             http://localhost:9000/foo/bar.pl?test=bing&test2=bong
233              
234             You should be prompted for a userid and password, entering "larry"
235             and "wall" will allow you to see the page. Entering "dr_evil" and
236             "1million" should result in getting a Forbidden page (and likely
237             needing to restart your browser). Entering any other userid or
238             password should result in you being asked again.
239              
240             If you have a handler for both RegisterURL and RegisterAuth, then
241             your function for RegisterURL can find the identify of the user in
242             the C<$env-E{'REMOTE_USER'}> hash entry. This is similar to CGI
243             scripts.
244              
245             You can have multiple handlers for different URLs. If you do this,
246             then the longest complete URL handler will be called. For example,
247             if you have handlers for C and C, and a URL
248             of C is called, then the handler C is
249             called to authorize this request, but if a URL of C
250             is called, then the handler C is called.
251              
252             Only complete directories are matched, so if you had a handler for
253             C, then it would not be called for either /foo/bar.pl or
254             C.
255              
256             =head2 RegisterRegex(regex,function)
257              
258             Register the function with the provided regular expression. When a
259             URL that matches that regular expression is requested, the function
260             is called and passed the environment (GET+POST) so that it can do
261             something meaningfiul with them. For more information on how the
262             function is called and should be used see the section on RegisterURL
263             below.
264              
265             $server->RegisterRegex(".*.news$",\&news);
266              
267             This will match any URL that ends in ".news" and call the &news
268             function. The URL that the user request can be retrieved via the
269             Request object ($reg->Path()).
270              
271             =head2 RegisterRegex(hash ref)
272              
273             Instead of calling RegisterRegex a bunch of times, you can just pass
274             it a hash ref containing Regex/callback pairs.
275              
276             $server->RegisterRegex({
277             ".*.news$" => \&news,
278             ".*.foo$" => \&foo,
279             });
280              
281             =head2 RegisterURL(url,function)
282              
283             Register the function with the provided URL. When that URL is requested,
284             the function is called and passed in the environment (GET+POST) so that
285             it can do something meaningful with them. A simple handler looks like:
286              
287             $server->RegisterURL("/foo/bar.pl",\&test);
288              
289             sub test
290             {
291             my $req = shift; # Net::HTTPServer::Request object
292             my $res = $req->Response(); # Net::HTTPServer::Response object
293              
294             $res->Print("\n");
295             $res->Print(" \n");
296             $res->Print(" This is a test\n");
297             $res->Print(" \n");
298             $res->Print(" \n");
299             $res->Print("
\n"); 
300              
301             foreach my $var (keys(%{$req->Env()}))
302             {
303             $res->Print("$var -> ".$req->Env($var)."\n");
304             }
305            
306             $res->Print(" \n");
307             $res->Print(" \n");
308             $res->Print("\n");
309              
310             return $res;
311             }
312            
313             Start a server with that and point your browser to:
314              
315             http://localhost:9000/foo/bar.pl?test=bing&test2=bong
316              
317             You should see a page titled "This is a test" with this body:
318              
319             test -> bing
320             test2 -> bong
321            
322             =head2 RegisterURL(hash ref)
323              
324             Instead of calling RegisterURL a bunch of times, you can just pass
325             it a hash ref containing URL/callback pairs.
326              
327             $server->RegisterURL({
328             "/foo/bar.pl" => \&test1,
329             "/foo/baz.pl" => \&test2,
330             });
331              
332             See RegisterURL() above for more information on how callbacks work.
333              
334             =head2 Start()
335              
336             Starts the server based on the config options passed to new(). Returns
337             the port number the server is listening on, or undef if the server was
338             unable to start.
339              
340             =head2 Stop()
341              
342             Shuts down the socket connection and cleans up after itself.
343              
344             =head1 SESSIONS
345              
346             Net::HTTPServer provides support for server-side sessions much like PHP's
347             session model. A handler that you register can ask that the request object
348             start a new session. It will check a cookie value to see if an existing
349             session exists, if not it will create a new one with a unique key.
350              
351             You can store any arbitrary Perl data structures in the session. The next
352             time the user accesses your handler, you can restore those values and have
353             them available again. When you are done, simple destroy the session.
354              
355             =head1 HEADERS
356              
357             Net::HTTPServer sets a few headers automatically. Due to the timing of
358             events, you cannot get to those headers programatically, so we will
359             discuss them general.
360              
361             Obviously for file serving, errors, and authentication it sends back
362             all of the appropriate headers. You likely do not need to worry about
363             those cases. In RegisterURL mode though, here are the headers that are
364             added:
365              
366             Accept-Ranges: none (not supported)
367             Allow: GET, HEAD, POST, TRACE
368             Content-Length:
369             Connection: close (not supported)
370             Content-Type: text/html (unless you set it)
371             Date:
372             Server:
373             plus what you add using the
374             AddServerTokens method>
375              
376             If you have any other questions about what is being sent, try using
377             DEBUG (later section).
378              
379             =head1 DEBUG
380              
381             When you are writing your application you might see behavior that is
382             unexpected. I've found it useful to check some debugging statements
383             that I have in the module to see what it is doing. If you want to
384             turn debugging on simply provide the debug => [ zones ] option when
385             creating the server. You can optionally specify a file to write
386             the log into instead of STDOUT by specifying the debuglog => file
387             option.
388              
389             I've coded the modules debugging using the concept of zones. Each
390             zone (or task) has it's own debug messages and you can enable/disable
391             them as you want to. Here are the list of available zones:
392              
393             INIT - Initializing the sever
394             PROC - Processing a request
395             REQ - Parsing requests
396             RESP - Returning the response (file contents are not printed)
397             AUTH - Handling and authentication request
398             FILE - Handling a file system request.
399             READ - Low-level read
400             SEND - Low-level send (even prints binary characters)
401             ALL - Turn all of the above on.
402              
403             So as an example:
404              
405             my $server = new Net::HTTPServer(..., debug=>["REQ","RESP"],...);
406              
407             That would show all requests and responses.
408              
409             =head1 AUTHOR
410              
411             Ryan Eatmon
412              
413             =head1 COPYRIGHT
414              
415             Copyright (c) 2003-2005 Ryan Eatmon . All rights
416             reserved. This program is free software; you can redistribute it
417             and/or modify it under the same terms as Perl itself.
418              
419             =cut
420            
421 4     4   74720 use strict;
  4         12  
  4         199  
422 4     4   38 use Carp;
  4         8  
  4         396  
423 4     4   4826 use IO::Socket;
  4         147004  
  4         21  
424 4     4   12539 use IO::Select;
  4         8131  
  4         202  
425 4     4   3476 use FileHandle;
  4         19129  
  4         45  
426 4     4   1798 use File::Path;
  4         11  
  4         524  
427 4     4   3883 use POSIX;
  4         36950  
  4         35  
428 4     4   23148 use Net::HTTPServer::Session;
  4         51  
  4         136  
429 4     4   2676 use Net::HTTPServer::Response;
  4         14  
  4         136  
430 4     4   2827 use Net::HTTPServer::Request;
  4         14  
  4         178  
431              
432 4     4   32 use vars qw ( $VERSION %ALLOWED $SSL $Base64 $DigestMD5 );
  4         7  
  4         44193  
433              
434             $VERSION = "1.1.1";
435              
436             $ALLOWED{GET} = 1;
437             $ALLOWED{HEAD} = 1;
438             $ALLOWED{OPTIONS} = 1;
439             $ALLOWED{POST} = 1;
440             $ALLOWED{TRACE} = 1;
441              
442             #------------------------------------------------------------------------------
443             # Do we have IO::Socket::SSL for https support?
444             #------------------------------------------------------------------------------
445             if (eval "require IO::Socket::SSL;")
446             {
447             require IO::Socket::SSL;
448             import IO::Socket::SSL;
449             $SSL = 1;
450             }
451             else
452             {
453             $SSL = 0;
454             }
455              
456             #------------------------------------------------------------------------------
457             # Do we have MIME::Base64 for Basic Authentication support?
458             #------------------------------------------------------------------------------
459             if (eval "require MIME::Base64;")
460             {
461             require MIME::Base64;
462             import MIME::Base64;
463             $Base64 = 1;
464             }
465             else
466             {
467             $Base64 = 0;
468             }
469              
470             #------------------------------------------------------------------------------
471             # Do we have Digest::MD5 for Digest Authentication support?
472             #------------------------------------------------------------------------------
473             if (eval "require Digest::MD5;")
474             {
475             require Digest::MD5;
476             import Digest::MD5;
477             $DigestMD5 = 1;
478             }
479             else
480             {
481             $DigestMD5 = 0;
482             }
483              
484              
485             sub new
486             {
487 3     3 1 68 my $proto = shift;
488 3   33     28 my $class = ref($proto) || $proto;
489 3         7 my $self = { };
490            
491 3         16 bless($self, $proto);
492              
493 3         22 my (%args) = @_;
494              
495 3         23 $self->{ARGS} = \%args;
496              
497             #--------------------------------------------------------------------------
498             # Get the hostname...
499             #--------------------------------------------------------------------------
500 3         54 my $hostname = (uname)[1];
501 3         1291 my $address = gethostbyname($hostname);
502 3 50       17 if ($address)
503             {
504 3         7 $hostname = $address;
505 3         188 my $temp = gethostbyaddr($address, AF_INET);
506 3 50       22 $hostname = $temp if ($temp);
507             }
508              
509 3         17 $self->{SERVER}->{NAME} = $hostname;
510              
511 3         24 $self->{CFG}->{ADMIN} = $self->_arg("admin",'webmaster@'.$hostname);
512 3         11 $self->{CFG}->{CHROOT} = $self->_arg("chroot",1);
513 3         11 $self->{CFG}->{DATADIR} = $self->_arg("datadir","/tmp/nethttpserver.sessions");
514 3         11 $self->{CFG}->{DOCROOT} = $self->_arg("docroot",undef);
515 3         14 $self->{CFG}->{INDEX} = $self->_arg("index",["index.html","index.htm"]);
516 3         10 $self->{CFG}->{LOG} = $self->_arg("log","access.log");
517 3         10 $self->{CFG}->{MIMETYPES} = $self->_arg("mimetypes",undef);
518 3         11 $self->{CFG}->{NUMPROC} = $self->_arg("numproc",5);
519 3         11 $self->{CFG}->{OLDREQUEST} = $self->_arg("oldrequest",0);
520 3         10 $self->{CFG}->{PORT} = $self->_arg("port",9000);
521 3         11 $self->{CFG}->{SESSIONS} = $self->_arg("sessions",0);
522 3   33     10 $self->{CFG}->{SSL} = $self->_arg("ssl",0) && $SSL;
523 3         12 $self->{CFG}->{SSL_KEY} = $self->_arg("ssl_key",undef);
524 3         11 $self->{CFG}->{SSL_CERT} = $self->_arg("ssl_cert",undef);
525 3         12 $self->{CFG}->{SSL_CA} = $self->_arg("ssl_ca",undef);
526 3         17 $self->{CFG}->{TYPE} = $self->_arg("type","single",["single","forking"]);
527              
528 3 50       19 if ($self->{CFG}->{LOG} eq "STDOUT")
529             {
530 0         0 $| = 1;
531 0         0 $self->{LOG} = \*STDOUT;
532             }
533             else
534             {
535 3         42 $self->{LOG} = new FileHandle(">>$self->{CFG}->{LOG}");
536 3 50       350 if (!defined($self->{LOG}))
537             {
538 0         0 croak("Could not open log $self->{CFG}->{LOG} for append:\n $!");
539             }
540             }
541 3         19 FileHandle::autoflush($self->{LOG},1);
542              
543 3         265 $self->{DEBUGZONES} = {};
544 3         13 $self->{DEBUG} = $self->_arg("debug",[]);
545 3         13 $self->{DEBUGLOG} = $self->_arg("debuglog","STDOUT");
546              
547 3 50 33     23 if ((ref($self->{DEBUG}) eq "ARRAY") && ($#{$self->{DEBUG}} > -1))
  3         18  
548             {
549              
550 0         0 foreach my $zone (@{$self->{DEBUG}})
  0         0  
551             {
552 0         0 $self->{DEBUGZONES}->{$zone} = 1;
553             }
554              
555 0 0       0 if ($self->{DEBUGLOG} eq "STDOUT")
556             {
557 0         0 $| = 1;
558 0         0 $self->{DEBUGLOG} = \*STDOUT;
559             }
560             else
561             {
562 0         0 my $log = $self->{DEBUGLOG};
563 0         0 $self->{DEBUGLOG} = new FileHandle(">$log");
564 0 0       0 if (!defined($self->{DEBUGLOG}))
565             {
566 0         0 croak("Could not open log $log for write:\n $!");
567             }
568             }
569 0         0 FileHandle::autoflush($self->{DEBUGLOG},1);
570             }
571              
572 3         10 delete($self->{ARGS});
573              
574 3 50       14 if (!defined($self->{CFG}->{MIMETYPES}))
575             {
576 3         11 foreach my $lib (@INC)
577             {
578 9 100       205 if (-e "$lib/Net/HTTPServer/mime.types")
579             {
580 3         13 $self->{CFG}->{MIMETYPES} = "$lib/Net/HTTPServer/mime.types";
581 3         11 last;
582             }
583             }
584             }
585            
586 3         33 $self->_mimetypes();
587            
588 3 50       18 if ($DigestMD5)
589             {
590 3         70 $self->{PRIVATEKEY} = Digest::MD5::md5_hex("Net::HTTPServer/$VERSION".time);
591             }
592              
593 3         12 $self->{AUTH} = {};
594 3         10 $self->{CALLBACKS} = {};
595 3         15 $self->{SERVER_TOKENS} = [ "Net::HTTPServer/$VERSION" ];
596              
597 3 100       31 if ($self->{CFG}->{SESSIONS})
598             {
599 2 50       58 if (-d $self->{CFG}->{DATADIR})
600             {
601 2         86114 File::Path::rmtree($self->{CFG}->{DATADIR});
602             }
603            
604 2 50       80 if (!(-d $self->{CFG}->{DATADIR}))
605             {
606 2         703 File::Path::mkpath($self->{CFG}->{DATADIR},0,0700);
607             }
608             }
609              
610 3         266 $self->{REGEXID} = 0;
611              
612             #XXX Clean up the datadir of files older than a certain time.
613              
614 3         37 return $self;
615             }
616              
617              
618             ###############################################################################
619             #
620             # AddServerTokens - Add more tokens that will be sent on the Server: header
621             # line of a response.
622             #
623             ###############################################################################
624             sub AddServerTokens
625             {
626 0     0 1 0 my $self = shift;
627 0         0 my (@tokens) = @_;
628              
629 0         0 foreach my $token (@tokens)
630             {
631 0 0       0 if ($token =~ / /)
632             {
633 0         0 croak("Server token cannot contain any spaces: \"$token\"");
634             }
635            
636 0         0 push(@{$self->{SERVER_TOKENS}},$token);
  0         0  
637             }
638             }
639              
640              
641             ###############################################################################
642             #
643             # Process - Inner loop to handle connection, read requests, process them, and
644             # respond.
645             #
646             ###############################################################################
647             sub Process
648             {
649 0     0 1 0 my $self = shift;
650 0         0 my $timeout = shift;
651              
652 0 0       0 if (!defined($self->{SOCK}))
653             {
654 0         0 croak("Process() called on undefined socket. Check the result from Start().\n ");
655             }
656              
657 0         0 my $timestop = undef;
658 0 0       0 $timestop = time + $timeout if defined($timeout);
659            
660 0         0 $self->_debug("PROC","Process: type($self->{CFG}->{TYPE})");
661              
662 0         0 my $block = 1;
663 0         0 while($block)
664             {
665 0 0       0 if ($self->{CFG}->{TYPE} eq "single")
    0          
666             {
667 0         0 $self->_single_process($timestop);
668             }
669             elsif ($self->{CFG}->{TYPE} eq "forking")
670             {
671 0         0 $self->_forking_process();
672             }
673              
674 0 0 0     0 $block = 0 if (defined($timestop) && (($timestop - time) <= 0));
675             }
676             }
677              
678              
679             ###############################################################################
680             #
681             # RegisterAuth - Protect the given URL using the given authentication method
682             # and calling the supplied function to verify the username
683             # and password.
684             #
685             ###############################################################################
686             sub RegisterAuth
687             {
688 0     0 1 0 my $self = shift;
689 0         0 my $method = shift;
690 0         0 my $url = shift;
691 0         0 my $realm = shift;
692 0         0 my $callback = shift;
693              
694 0         0 $method = lc($method);
695            
696 0 0 0     0 if (($method ne "basic") && ($method ne "digest"))
697             {
698 0         0 croak("You did not specify a valid method to RegisterAuth: \"$method\"\nValid options are:\n basic, digest\n");
699             }
700              
701 0 0 0     0 if (($method eq "basic") || ($method eq "digest"))
702             {
703 0 0       0 if (!$Base64)
704             {
705 0         0 $self->_log("Cannot register authentication callback as MIME::Base64 is not installed...");
706 0         0 carp("Cannot register authentication callback as MIME::Base64 is not installed...");
707             }
708             }
709            
710 0 0       0 if ($method eq "digest")
711             {
712 0 0       0 if (!$DigestMD5)
713             {
714 0         0 $self->_log("Cannot register authentication callback as Digest::MD5 is not installed...");
715 0         0 carp("Cannot register authentication callback as Digest::MD5 is not installed...");
716             }
717             }
718            
719 0         0 $self->{AUTH}->{$url}->{method} = $method;
720 0         0 $self->{AUTH}->{$url}->{realm} = $realm;
721 0         0 $self->{AUTH}->{$url}->{callback} = $callback;
722             }
723              
724              
725             ###############################################################################
726             #
727             # RegisterRegex - given a regular expressions, call the supplied function when
728             # it a request path matches it.
729             #
730             ###############################################################################
731             sub RegisterRegex
732             {
733 0     0 1 0 my $self = shift;
734 0         0 my $regex = shift;
735 0         0 my $callback = shift;
736              
737 0         0 $regex =~ s/\//\\\//g;
738              
739 0         0 $self->{REGEXID}++;
740 0         0 my $id = "__nethttpserver__:regex:".$self->{REGEXID};
741            
742 0         0 $self->{REGEXCALLBACKS}->{$regex}->{callback} = $id;
743 0         0 $self->{REGEXCALLBACKS}->{$regex}->{id} = $self->{REGEXID};
744 0         0 $self->{CALLBACKS}->{$id} = $callback;
745             }
746              
747              
748             ###############################################################################
749             #
750             # RegisterURL - given a URL path, call the supplied function when it is
751             # requested.
752             #
753             ###############################################################################
754             sub RegisterURL
755             {
756 0     0 1 0 my $self = shift;
757 0         0 my $url = shift;
758              
759 0 0       0 if (ref($url) eq "HASH")
760             {
761 0         0 foreach my $hashURL (keys(%{$url}))
  0         0  
762             {
763 0         0 $self->{CALLBACKS}->{$hashURL} = $url->{$hashURL};
764             }
765             }
766             else
767             {
768 0         0 my $callback = shift;
769              
770 0         0 $self->{CALLBACKS}->{$url} = $callback;
771             }
772             }
773              
774              
775             ###############################################################################
776             #
777             # Start - Just a little initialization routine to start the server.
778             #
779             ###############################################################################
780             sub Start
781             {
782 0     0 1 0 my $self = shift;
783              
784 0         0 $self->_debug("INIT","Start: Starting the server");
785              
786 0         0 my $port = $self->{CFG}->{PORT};
787 0 0       0 my $scan = ($port eq "scan" ? 1 : 0);
788 0 0       0 $port = 8000 if $scan;
789            
790 0         0 $self->{SOCK} = undef;
791              
792 0         0 while(!defined($self->{SOCK}))
793             {
794 0         0 $self->_debug("INIT","Start: Attempting to listen on port $port");
795            
796 0 0       0 if ($self->{CFG}->{SSL} == 0)
797             {
798 0 0       0 $self->{SOCK} = new IO::Socket::INET(LocalPort=>$port,
799             Proto=>"tcp",
800             Listen=>10,
801             Reuse=>1,
802             (($^O ne "MSWin32") ?
803             (Blocking=>0) :
804             ()
805             ),
806             );
807             }
808             else
809             {
810 0 0 0     0 if (!defined($self->{CFG}->{SSL_KEY}) ||
      0        
811             !defined($self->{CFG}->{SSL_CERT}) ||
812             !defined($self->{CFG}->{SSL_CA}))
813             {
814 0         0 croak("You must specify ssl_key, ssl_cert, and ssl_ca if you want to use SSL.");
815 0         0 return;
816             }
817 0         0 $self->_debug("INIT","Start: Create an SSL socket.");
818 0 0       0 $self->{SOCK} = new IO::Socket::SSL(LocalPort=>$port,
819             Proto=>"tcp",
820             Listen=>10,
821             Reuse=>1,
822             SSL_key_file=>$self->{CFG}->{SSL_KEY},
823             SSL_cert_file=>$self->{CFG}->{SSL_CERT},
824             SSL_ca_file=>$self->{CFG}->{SSL_CA},
825             SSL_verify_mode=> 0x01,
826             (($^O ne "MSWin32") ?
827             (Blocking=>0) :
828             ()
829             ),
830             );
831             }
832 0 0       0 last if defined($self->{SOCK});
833 0 0       0 last if ($port == 9999);
834 0 0       0 last if !$scan;
835            
836 0         0 $port++;
837             }
838              
839 0 0       0 if (!defined($self->{SOCK}))
840             {
841 0         0 $self->_log("Could not start the server...");
842 0 0       0 if ($self->{CFG}->{SSL} == 0)
843             {
844 0         0 carp("Could not start the server: $!");
845             }
846             else
847             {
848 0         0 carp("Could not start the server: ",&IO::Socket::SSL::errstr);
849             }
850              
851 0         0 return;
852             }
853              
854 0         0 $self->{SELECT} = new IO::Select($self->{SOCK});
855              
856 0 0       0 if ($self->{CFG}->{TYPE} eq "forking")
857             {
858 0         0 $self->_debug("INIT","Start: Initializing forking");
859 0     0   0 $SIG{CHLD} = sub{ $self->_forking_reaper(); };
  0         0  
860 0         0 $self->{CHILDREN} = {};
861 0         0 $self->{NUMCHILDREN} = 0;
862             }
863            
864 0         0 $self->_log("Server running on port $port");
865              
866 0         0 $self->{SERVER}->{PORT} = $port;
867              
868 0         0 return $port;
869             }
870              
871              
872             ###############################################################################
873             #
874             # Stop - Stop the server.
875             #
876             ###############################################################################
877             sub Stop
878             {
879 0     0 1 0 my $self = shift;
880              
881 0         0 $self->_debug("INIT","Stop: Stopping the server");
882              
883 0 0       0 if ($self->{CFG}->{TYPE} eq "forking")
884             {
885 0         0 $self->_forking_huntsman();
886             }
887            
888 0 0 0     0 if (exists($self->{SELECT}) && defined($self->{SELECT}))
889             {
890 0         0 $self->{SELECT}->remove($self->{SOCK});
891             }
892              
893 0 0 0     0 if (exists($self->{SOCK}) && defined($self->{SOCK}))
894             {
895 0         0 $self->{SOCK}->close();
896             }
897             }
898              
899              
900              
901              
902             ###############################################################################
903             #+-----------------------------------------------------------------------------
904             #| Private Flow Functions
905             #+-----------------------------------------------------------------------------
906             ###############################################################################
907              
908             ###############################################################################
909             #
910             # _HandleAuth - Make sure that the user has passed the authentication to view
911             # this page.
912             #
913             ###############################################################################
914             sub _HandleAuth
915             {
916 0     0   0 my $self = shift;
917 0         0 my $requestObj = shift;
918            
919 0         0 my $authURL = $self->_checkAuth($requestObj->Path());
920 0 0       0 return unless defined($authURL);
921              
922 0         0 $self->_debug("AUTH","_HandleAuth: url(".$requestObj->Path().")");
923 0         0 $self->_debug("AUTH","_HandleAuth: authURL($authURL) method($self->{AUTH}->{$authURL}->{method})");
924              
925 0 0       0 if ($self->{AUTH}->{$authURL}->{method} eq "basic")
    0          
926             {
927 0         0 return $self->_HandleAuthBasic($authURL,$requestObj);
928             }
929             elsif ($self->{AUTH}->{$authURL}->{method} eq "digest")
930             {
931 0         0 return $self->_HandleAuthDigest($authURL,$requestObj);
932             }
933              
934 0         0 return;
935             }
936              
937              
938             ###############################################################################
939             #
940             # _HandleAuthBasic - Parse the Authentication header and make sure that the
941             # user is allowed to see this page.
942             #
943             ###############################################################################
944             sub _HandleAuthBasic
945             {
946 0     0   0 my $self = shift;
947 0         0 my $authURL = shift;
948 0         0 my $requestObj = shift;
949              
950 0         0 my $realm = $self->{AUTH}->{$authURL}->{realm};
951              
952 0         0 $self->_debug("AUTH","_HandleAuthBasic: authURL($authURL) realm($realm)");
953              
954             #-------------------------------------------------------------------------
955             # Auth if they did not send an Authorization
956             #-------------------------------------------------------------------------
957 0 0       0 return $self->_AuthBasic($realm) unless $requestObj->Header("Authorization");
958 0         0 $self->_debug("AUTH","_HandleAuthBasic: there was an Authorization");
959              
960 0         0 my ($method,$base64) = split(" ",$requestObj->Header("Authorization"),2);
961              
962             #-------------------------------------------------------------------------
963             # Auth if they did not send a Basic Authorization
964             #-------------------------------------------------------------------------
965 0 0       0 return $self->_AuthBasic($realm) if (lc($method) ne "basic");
966 0         0 $self->_debug("AUTH","_HandleAuthBasic: it was a Basic");
967              
968 0         0 my ($user,$password) = split(":",MIME::Base64::decode($base64));
969              
970 0         0 my ($code,$real_password) =
971 0         0 &{$self->{AUTH}->{$authURL}->{callback}}($requestObj->Path(),$user);
972 0         0 $self->_debug("AUTH","_HandleAuthBasic: callback return code($code)");
973              
974             #-------------------------------------------------------------------------
975             # Return the results of the authentication handler
976             #-------------------------------------------------------------------------
977 0 0       0 return $self->_AuthBasic($realm) if ($code eq "401");
978 0 0       0 return $self->_AuthBasic($realm) if ($password ne $real_password);
979 0 0       0 return $self->_Forbidden() if ($code eq "403");
980              
981             #-------------------------------------------------------------------------
982             # We authed, so set REMOTE_USER in the env hash and return
983             #-------------------------------------------------------------------------
984 0         0 $requestObj->_env("AUTH_TYPE","Basic");
985 0         0 $requestObj->_env("REMOTE_USER",$user);
986 0         0 return;
987             }
988              
989              
990             ###############################################################################
991             #
992             # _HandleAuthDigest - Parse the Authentication header and make sure that the
993             # user is allowed to see this page.
994             #
995             ###############################################################################
996             sub _HandleAuthDigest
997             {
998 0     0   0 my $self = shift;
999 0         0 my $authURL = shift;
1000 0         0 my $requestObj = shift;
1001              
1002 0         0 my %digest;
1003 0         0 $digest{algorithm} = "MD5";
1004 0         0 $digest{nonce} = $self->_nonce();
1005 0         0 $digest{realm} = $self->{AUTH}->{$authURL}->{realm};
1006 0         0 $digest{qop} = "auth";
1007              
1008 0         0 $self->_debug("AUTH","_HandleAuthDigest: authURL($authURL) realm($digest{realm})");
1009              
1010             #-------------------------------------------------------------------------
1011             # Auth if they did not send an Authorization
1012             #-------------------------------------------------------------------------
1013 0 0       0 return $self->_AuthDigest(\%digest) unless $requestObj->Header("Authorization");
1014 0         0 $self->_debug("AUTH","_HandleAuthDigest: there was an Authorization");
1015              
1016 0         0 my ($method,$directives) = split(" ",$requestObj->Header("Authorization"),2);
1017              
1018             #-------------------------------------------------------------------------
1019             # Auth if they did not send a Digest Authorization
1020             #-------------------------------------------------------------------------
1021 0 0       0 return $self->_AuthDigest(\%digest) if (lc($method) ne "digest");
1022 0         0 $self->_debug("AUTH","_HandleAuthDigest: it was a Digest");
1023              
1024 0         0 my %authorization;
1025 0         0 foreach my $directive (split(",",$directives))
1026             {
1027 0         0 my ($key,$value) = ($directive =~ /^\s*([^=]+)\s*=\s*\"?(.+?)\"?\s*$/);
1028 0         0 $authorization{$key} = $value;
1029             }
1030            
1031             #-------------------------------------------------------------------------
1032             # Make sure that the uri in the auth and the request are the same.
1033             #-------------------------------------------------------------------------
1034 0 0       0 return $self->_BadRequest() if ($requestObj->URL() ne $authorization{uri});
1035              
1036 0         0 my ($code,$real_password) =
1037 0         0 &{$self->{AUTH}->{$authURL}->{callback}}($requestObj->Path(),$authorization{username});
1038 0         0 $self->_debug("AUTH","_HandleAuthDigest: callback return code($code)");
1039              
1040 0         0 my $ha1 = $self->_digest_HA1(\%authorization,$real_password);
1041 0         0 my $ha2 = $self->_digest_HA2(\%authorization,$requestObj->Method());
1042 0         0 my $kd = $self->_digest_KD(\%authorization,$ha1,$ha2);
1043              
1044             #-------------------------------------------------------------------------
1045             # Return the results of the authentication handler
1046             #-------------------------------------------------------------------------
1047 0 0       0 return $self->_AuthDigest(\%digest) if ($code eq "401");
1048 0 0       0 return $self->_AuthDigest(\%digest) if ($kd ne $authorization{response});
1049 0 0       0 return $self->_Forbidden() if ($code eq "403");
1050              
1051             #-------------------------------------------------------------------------
1052             # If they authed, then check over the nonce and make sure it's valid.
1053             #-------------------------------------------------------------------------
1054 0         0 my ($time,$privatekey) = split(":",MIME::Base64::decode($authorization{nonce}));
1055              
1056 0 0       0 if ($privatekey ne $self->{PRIVATEKEY})
1057             {
1058 0         0 $self->_debug("AUTH","_HandleAuthDigest: nonce is stale due to key.");
1059 0         0 $digest{stale} = "TRUE";
1060 0         0 return $self->_AuthDigest(\%digest)
1061             }
1062              
1063 0 0       0 if ((time - $time) > 30)
1064             {
1065 0         0 $self->_debug("AUTH","_HandleAuthDigest: nonce is stale due to time.");
1066 0         0 $digest{stale} = "TRUE";
1067 0         0 return $self->_AuthDigest(\%digest);
1068             }
1069            
1070             # XXX - check nc for replay attack
1071             # XXX - better nonce to minimize replay attacks?
1072            
1073             #-------------------------------------------------------------------------
1074             # We authed, so set REMOTE_USER in the env hash and return
1075             #-------------------------------------------------------------------------
1076 0         0 $requestObj->_env("AUTH_TYPE","Digest");
1077 0         0 $requestObj->_env("REMOTE_USER",$authorization{username});
1078 0         0 return;
1079             }
1080              
1081              
1082             ###############################################################################
1083             #
1084             # _ProcessRequest - Based on the URL and Environment, figure out what they
1085             # wanted, and call the correct handler.
1086             #
1087             ###############################################################################
1088             sub _ProcessRequest
1089             {
1090 0     0   0 my $self = shift;
1091 0         0 my $requestObj = shift;
1092              
1093             #-------------------------------------------------------------------------
1094             # Catch some common errors/reponses without doing any real hard work
1095             #-------------------------------------------------------------------------
1096 0 0       0 return $self->_ExpectationFailed()
1097             if ($requestObj->_failure() eq "expect");
1098            
1099 0 0       0 return $self->_MethodNotAllowed()
1100             unless exists($ALLOWED{$requestObj->Method()});
1101            
1102 0 0       0 return $self->_BadRequest()
1103             unless $requestObj->Header("Host");
1104            
1105 0 0 0     0 return $self->_LengthRequired()
1106             if ($requestObj->Header("Transfer-Encoding") &&
1107             $requestObj->Header("Transfer-Encoding") ne "identity");
1108              
1109 0 0       0 return $self->_Options()
1110             if ($requestObj->Method() eq "OPTIONS");
1111              
1112 0 0       0 return new Net::HTTPServer::Response()
1113             if ($requestObj->Method() eq "TRACE");
1114              
1115 0         0 my $responseObj;
1116              
1117 0         0 my $reqPath = $requestObj->Path();
1118 0         0 my $method = "not found";
1119              
1120 0         0 my $reqPath1 = $reqPath."/";
1121 0         0 my ($reqPath2) = ($reqPath =~ /^(.+)\/$/);
1122 0 0       0 $reqPath2 = $reqPath if !defined($reqPath);
1123              
1124 0 0 0     0 if (exists($self->{CALLBACKS}->{$reqPath}))
    0          
    0          
    0          
    0          
1125             {
1126 0         0 $method = "callback";
1127             }
1128             elsif (exists($self->{CALLBACKS}->{$reqPath1}))
1129             {
1130 0         0 $method = "callback";
1131 0         0 $reqPath = $reqPath1;
1132             }
1133             elsif (exists($self->{CALLBACKS}->{$reqPath2}))
1134             {
1135 0         0 $method = "callback";
1136 0         0 $reqPath = $reqPath2;
1137             }
1138             elsif (my $regex = $self->_RegexMatch($reqPath))
1139             {
1140 0         0 $reqPath = $regex;
1141 0         0 $method = "callback";
1142             }
1143             elsif (defined($self->{CFG}->{DOCROOT}) &&
1144             (-e $self->{CFG}->{DOCROOT}."/".$reqPath))
1145             {
1146 0         0 $method = "file";
1147              
1148 0 0       0 if (-d $self->{CFG}->{DOCROOT}."/".$reqPath)
1149             {
1150 0         0 $self->_debug("PROC","_ProcessRequest: This is a directory, look for an index file.");
1151 0         0 foreach my $index (@{$self->{CFG}->{INDEX}})
  0         0  
1152             {
1153 0         0 my $testPath = $reqPath;
1154 0 0       0 $testPath .= "/" unless ($reqPath =~ /\/$/);
1155 0         0 $testPath .= $index;
1156              
1157 0         0 $self->_debug("PROC","_ProcessRequest: index? ($testPath)");
1158              
1159 0 0       0 if (exists($self->{CALLBACKS}->{$testPath}))
1160             {
1161 0         0 $self->_debug("PROC","_ProcessRequest: index: callback: ($testPath)");
1162 0         0 $method = "callback";
1163 0         0 $reqPath = $testPath;
1164 0         0 last;
1165             }
1166              
1167 0 0       0 if (-f $self->{CFG}->{DOCROOT}."/".$testPath)
1168             {
1169 0         0 $self->_debug("PROC","_ProcessRequest: index: file: ($testPath)");
1170 0         0 $reqPath = $testPath;
1171 0         0 last;
1172             }
1173             }
1174             }
1175             }
1176             else
1177             {
1178 0         0 $self->_debug("PROC","_ProcessRequest: Might be a virtual directory... index callback?");
1179              
1180 0         0 foreach my $index (@{$self->{CFG}->{INDEX}})
  0         0  
1181             {
1182 0         0 my $testPath = $reqPath;
1183 0 0       0 $testPath .= "/" unless ($reqPath =~ /\/$/);
1184 0         0 $testPath .= $index;
1185            
1186 0         0 $self->_debug("PROC","_ProcessRequest: index? ($testPath)");
1187            
1188 0 0       0 if (exists($self->{CALLBACKS}->{$testPath}))
1189             {
1190 0         0 $self->_debug("PROC","_ProcessRequest: index: callback: ($testPath)");
1191 0         0 $method = "callback";
1192 0         0 $reqPath = $testPath;
1193 0         0 last;
1194             }
1195             }
1196             }
1197              
1198 0         0 $self->_debug("PROC","_ProcessRequest: method($method)");
1199            
1200 0 0       0 if ($method eq "callback")
    0          
1201             {
1202 0         0 my $auth = $self->_HandleAuth($requestObj);
1203 0 0       0 return $auth if defined($auth);
1204              
1205 0         0 $self->_debug("PROC","_ProcessRequest: Callback");
1206 0 0       0 if ($self->{CFG}->{OLDREQUEST})
1207             {
1208 0         0 my $response = &{$self->{CALLBACKS}->{$reqPath}}($requestObj->Env(),$requestObj->Cookie());
  0         0  
1209 0         0 $responseObj = new Net::HTTPServer::Response(code=>$response->[0],
1210             headers=>$response->[1],
1211             body=>$response->[2],
1212             );
1213             }
1214             else
1215             {
1216 0         0 $responseObj = &{$self->{CALLBACKS}->{$reqPath}}($requestObj);
  0         0  
1217             }
1218             }
1219             elsif ($method eq "file")
1220             {
1221 0         0 my $auth = $self->_HandleAuth($requestObj);
1222 0 0       0 return $auth if defined($auth);
1223              
1224 0         0 $self->_debug("PROC","_ProcessRequest: File");
1225 0         0 $responseObj = $self->_ServeFile($reqPath);
1226             }
1227             else
1228             {
1229 0         0 $self->_debug("PROC","_ProcessRequest: Not found");
1230 0         0 $responseObj = $self->_NotFound();
1231             }
1232              
1233 0         0 return $responseObj;
1234             }
1235              
1236              
1237             ###############################################################################
1238             #
1239             # _ReadRequest - Take the full request, pull out the type, url, GET, POST, etc.
1240             #
1241             ###############################################################################
1242             sub _ReadRequest
1243             {
1244 0     0   0 my $self = shift;
1245 0         0 my $request = shift;
1246            
1247 0         0 my $requestObj =
1248             new Net::HTTPServer::Request(chroot=>$self->{CFG}->{CHROOT},
1249             request=>$request,
1250             server=>$self,
1251             );
1252              
1253 0         0 $self->_debug("REQ","_ReadRequest: method(".$requestObj->Method().") url(".$requestObj->URL().")");
1254 0         0 $self->_debug("REQ","_ReadRequest: request(".$requestObj->Request().")");
1255 0         0 $self->_log($requestObj->Method()." ".$requestObj->URL());
1256              
1257 0         0 return $requestObj;
1258             }
1259              
1260              
1261             ###############################################################################
1262             #
1263             # _RegexMatch - loop through all of the regex callbacks and see if any match
1264             # the request path.
1265             #
1266             ###############################################################################
1267             sub _RegexMatch
1268             {
1269 0     0   0 my $self = shift;
1270 0         0 my $reqPath = shift;
1271              
1272 0 0       0 return unless exists($self->{REGEXCALLBACKS});
1273              
1274 0         0 foreach my $regex (sort {$self->{REGEXCALLBACKS}->{$a}->{id} <=> $self->{REGEXCALLBACKS}->{$b}->{id}} keys(%{$self->{REGEXCALLBACKS}}))
  0         0  
  0         0  
1275             {
1276 0 0       0 return $self->{REGEXCALLBACKS}->{$regex}->{callback} if ($reqPath =~ /$regex/);
1277             }
1278              
1279 0         0 return;
1280             }
1281              
1282              
1283             ###############################################################################
1284             #
1285             # _ReturnResponse - Take all of the pieces and generate the reponse, and send
1286             # it out.
1287             #
1288             ###############################################################################
1289             sub _ReturnResponse
1290             {
1291 0     0   0 my $self = shift;
1292 0         0 my $client = shift;
1293 0         0 my $requestObj = shift;
1294 0         0 my $responseObj = shift;
1295              
1296             #-------------------------------------------------------------------------
1297             # If this is not a redirect...
1298             #-------------------------------------------------------------------------
1299 0 0       0 if (!$responseObj->Header("Location"))
1300             {
1301             #---------------------------------------------------------------------
1302             # Initialize the content type
1303             #---------------------------------------------------------------------
1304 0 0       0 $responseObj->Header("Content-Type","text/html")
1305             unless $responseObj->Header("Content-Type");
1306            
1307             #---------------------------------------------------------------------
1308             # Check that it's acceptable to the client
1309             #---------------------------------------------------------------------
1310 0 0       0 if ($requestObj->Header("Accept"))
1311             {
1312 0 0       0 $responseObj = $self->_NotAcceptable()
1313             unless $self->_accept($requestObj->Header("Accept"),
1314             $responseObj->Header("Content-Type")
1315             );
1316             }
1317              
1318             #---------------------------------------------------------------------
1319             # Initialize any missing (and required) headers
1320             #---------------------------------------------------------------------
1321 0         0 $responseObj->Header("Accept-Ranges","none");
1322 0         0 $responseObj->Header("Allow",join(", ",keys(%ALLOWED)));
1323 0 0       0 $responseObj->Header("Content-Length",length($responseObj->Body()))
1324             unless $responseObj->Header("Content-Length");
1325 0         0 $responseObj->Header("Connection","close");
1326 0         0 $responseObj->Header("Date",&_date());
1327 0         0 $responseObj->Header("Server",join(" ",@{$self->{SERVER_TOKENS}}));
  0         0  
1328             }
1329              
1330             #-------------------------------------------------------------------------
1331             # If this was a HEAD, then there is no response
1332             #-------------------------------------------------------------------------
1333 0 0       0 $responseObj->Clear() if ($requestObj->Method() eq "HEAD");
1334            
1335 0 0       0 if ($requestObj->Method() eq "TRACE")
1336             {
1337 0         0 $responseObj->Header("Content-Type","message/http");
1338 0         0 $responseObj->Body($requestObj->Request());
1339             }
1340              
1341 0         0 my ($header,$body) = $responseObj->_build();
1342              
1343             #-------------------------------------------------------------------------
1344             # Debug
1345             #-------------------------------------------------------------------------
1346 0         0 $self->_debug("RESP","_ReturnResponse: ----------------------------------------");
1347 0         0 $self->_debug("RESP","_ReturnResponse: $header");
1348 0 0 0     0 if (($responseObj->Header("Content-Type") eq "text/html") ||
1349             ($responseObj->Header("Content-Type") eq "text/plain"))
1350             {
1351 0         0 $self->_debug("RESP","_ReturnResponse: $body");
1352             }
1353 0         0 $self->_debug("RESP","_ReturnResponse: ----------------------------------------");
1354              
1355             #-------------------------------------------------------------------------
1356             # Send the headers and response
1357             #-------------------------------------------------------------------------
1358 0 0       0 return unless defined($self->_send($client,$header));
1359 0 0       0 return unless defined($self->_send($client,$body));
1360             }
1361              
1362              
1363             ###############################################################################
1364             #
1365             # _ServeFile - If they asked for a valid file in the file system, then we need
1366             # to suck it in, profile it, and ship it back out.
1367             #
1368             ###############################################################################
1369             sub _ServeFile
1370             {
1371 0     0   0 my $self = shift;
1372 0         0 my $path = shift;
1373              
1374 0         0 my $fullpath = $self->{CFG}->{DOCROOT}."/$path";
1375              
1376 0         0 $self->_debug("FILE","_ServeFile: fullpath($fullpath)");
1377            
1378 0 0       0 if (-d $fullpath)
1379             {
1380 0         0 $self->_debug("FILE","_ServeFile: This is a directory.");
1381              
1382 0 0       0 if ($path !~ /\/$/)
1383             {
1384 0         0 return $self->_Redirect($path."/");
1385             }
1386              
1387 0         0 $self->_debug("FILE","_ServeFile: Show a directory listing.");
1388 0         0 return $self->_DirList($path);
1389             }
1390              
1391 0 0       0 if (!(-f $fullpath))
1392             {
1393 0         0 $self->_debug("FILE","_ServeFile: 404, File not found. Whoop! Whoop!");
1394 0         0 return $self->_NotFound();
1395             }
1396              
1397 0         0 my $fileHandle = new FileHandle($fullpath);
1398 0 0       0 return $self->_NotFound() unless defined($fileHandle);
1399              
1400 0         0 my $response = new Net::HTTPServer::Response();
1401              
1402 0         0 my ($ext) = ($fullpath =~ /\.([^\.]+?)$/);
1403 0 0 0     0 if (($ext ne "") && exists($self->{MIMETYPES}->{$ext}))
    0          
1404             {
1405 0         0 $response->Header("Content-Type",$self->{MIMETYPES}->{$ext});
1406             }
1407             elsif (-T $fullpath)
1408             {
1409 0         0 $response->Header("Content-Type",$self->{MIMETYPES}->{txt});
1410             }
1411              
1412 0         0 $response->Header("Content-Length",(stat( $fullpath ))[7]);
1413 0         0 $response->Header("Last-Modified",&_date((stat( $fullpath ))[9]));
1414              
1415 0         0 $response->Body($fileHandle);
1416              
1417 0         0 return $response;
1418             }
1419              
1420              
1421              
1422              
1423             ###############################################################################
1424             #+-----------------------------------------------------------------------------
1425             #| Private Canned Responses
1426             #+-----------------------------------------------------------------------------
1427             ###############################################################################
1428              
1429             ###############################################################################
1430             #
1431             # _Auth - Send an authentication response
1432             #
1433             ###############################################################################
1434             sub _Auth
1435             {
1436 0     0   0 my $self = shift;
1437 0         0 my $method = shift;
1438 0         0 my $args = shift;
1439              
1440 0         0 my @directives = "";
1441              
1442 0         0 foreach my $key (keys(%{$args}))
  0         0  
1443             {
1444 0         0 push(@directives,$key.'="'.$args->{$key}.'"');
1445             }
1446              
1447 0         0 my $directives = join(",",@directives);
1448            
1449 0         0 return $self->_Error("401",
1450             { 'WWW-Authenticate' => "$method $directives" },
1451             "Unauthorized",
1452             "Authorization is required to access this object on this server."
1453             );
1454             }
1455              
1456              
1457             ###############################################################################
1458             #
1459             # _AuthBasic - Send a Basic authentication response
1460             #
1461             ###############################################################################
1462             sub _AuthBasic
1463             {
1464 0     0   0 my $self = shift;
1465 0         0 my $realm = shift;
1466              
1467 0         0 return $self->_Auth("Basic",{ realm=>$realm });
1468             }
1469              
1470              
1471             ###############################################################################
1472             #
1473             # _AuthDigest - Send a Digest authentication response
1474             #
1475             ###############################################################################
1476             sub _AuthDigest
1477             {
1478 0     0   0 my $self = shift;
1479 0         0 my $args = shift;
1480              
1481 0         0 return $self->_Auth("Digest",$args);
1482             }
1483              
1484              
1485             ###############################################################################
1486             #
1487             # _BadRequest - 400, someone was being naughty
1488             #
1489             ###############################################################################
1490             sub _BadRequest
1491             {
1492 0     0   0 my $self = shift;
1493              
1494 0         0 return $self->_Error("400",
1495             {},
1496             "Bad Request",
1497             "You made a bad request. Something you sent did not match up.",
1498             );
1499             }
1500              
1501              
1502             ###############################################################################
1503             #
1504             # _DirList - If they want a directory... let's give them a directory.
1505             #
1506             ###############################################################################
1507             sub _DirList
1508             {
1509 0     0   0 my $self = shift;
1510 0         0 my $path = shift;
1511              
1512 0         0 my $res = "Dir listing for $path\n";
1513            
1514 0         0 opendir(DIR,$self->{CFG}->{DOCROOT}."/".$path);
1515 0         0 foreach my $file (sort {$a cmp $b} readdir(DIR))
  0         0  
1516             {
1517 0 0       0 next if ($file eq ".");
1518 0 0 0     0 next if (($file eq "..") && ($path eq "/"));
1519              
1520 0 0       0 if ($file =~ /\:/)
1521             {
1522 0         0 $res .= "$file
\n";
1523             }
1524             else
1525             {
1526 0         0 $res .= "$file
\n";
1527             }
1528             }
1529              
1530 0         0 $res .= "\n";
1531              
1532 0         0 return new Net::HTTPServer::Response(body=>$res);
1533             }
1534              
1535              
1536             ###############################################################################
1537             #
1538             # _Error - take a code, headers, error string, and text and return a standard
1539             # response.
1540             #
1541             ###############################################################################
1542             sub _Error
1543             {
1544 0     0   0 my $self = shift;
1545 0         0 my $code = shift;
1546 0         0 my $headers = shift;
1547 0         0 my $string = shift;
1548 0         0 my $body = shift;
1549              
1550 0         0 my $response = "";
1551 0         0 $response .= "".$string."!";
1552 0         0 $response .= "";
1553 0         0 $response .= "

".$string."!

";
1554 0         0 $response .= "
".$body."
";
1555 0         0 $response .= "

Error ".$code."

";
1556 0         0 $response .= "";
1557 0         0 $response .= "";
1558              
1559 0         0 return new Net::HTTPServer::Response(code=>$code,
1560             headers=>$headers,
1561             body=>$response,
1562             );
1563             }
1564              
1565              
1566             ###############################################################################
1567             #
1568             # _ExpectationFailed - 417, sigh... I never meet anyone's expectations
1569             #
1570             ###############################################################################
1571             sub _ExpectationFailed
1572             {
1573 0     0   0 my $self = shift;
1574              
1575 0         0 return $self->_Error("400",
1576             {},
1577             "Expectation Failed",
1578             "The server could not meet the expectations you had for it."
1579             );
1580             }
1581              
1582              
1583             ###############################################################################
1584             #
1585             # _Forbidden - ahhh the equally dreaded 403
1586             #
1587             ###############################################################################
1588             sub _Forbidden
1589             {
1590 0     0   0 my $self = shift;
1591              
1592 0         0 return $self->_Error("403",
1593             {},
1594             "Forbidden",
1595             "You do not have permission to access this object on this server.",
1596             );
1597             }
1598              
1599              
1600             ###############################################################################
1601             #
1602             # _LengthRequired - 411, we got a Transfer-Encoding that was not set to
1603             # "identity".
1604             #
1605             ###############################################################################
1606             sub _LengthRequired
1607             {
1608 0     0   0 my $self = shift;
1609              
1610 0         0 return $self->_Error("411",
1611             {},
1612             "Length Required",
1613             "You must specify the length of the request.",
1614             );
1615             }
1616              
1617              
1618             ###############################################################################
1619             #
1620             # _MethodNotAllowed - 405... you must only do what is allowed
1621             #
1622             ###############################################################################
1623             sub _MethodNotAllowed
1624             {
1625 0     0   0 my $self = shift;
1626              
1627 0         0 return $self->_Error("405",
1628             {},
1629             "Method Not Allowed",
1630             "You are not allowed to do what you just tried to do..."
1631             );
1632             }
1633              
1634              
1635             ###############################################################################
1636             #
1637             # _NotAcceptable - the client is being inflexiable... they won't accept what
1638             # we want to send.
1639             #
1640             ###############################################################################
1641             sub _NotAcceptable
1642             {
1643 0     0   0 my $self = shift;
1644              
1645 0         0 return $self->_Error("406",
1646             {},
1647             "Not Acceptable",
1648             "The server wants to return a file in a format that your browser does not accept.",
1649             );
1650             }
1651              
1652              
1653             ###############################################################################
1654             #
1655             # _NotFound - ahhh the dreaded 404
1656             #
1657             ###############################################################################
1658             sub _NotFound
1659             {
1660 0     0   0 my $self = shift;
1661              
1662 0         0 return $self->_Error("404",
1663             {},
1664             "Not Found",
1665             "The requested URL was not found on this server. If you entered the URL manually please check your spelling and try again."
1666             );
1667             }
1668              
1669              
1670             ###############################################################################
1671             #
1672             # _Options - returns a response to an OPTIONS request
1673             #
1674             ###############################################################################
1675             sub _Options
1676             {
1677 0     0   0 my $self = shift;
1678              
1679 0         0 return new Net::HTTPServer::Response(code=>200,
1680             headers=>{},
1681             body=>"",
1682             );
1683             }
1684              
1685              
1686             ###############################################################################
1687             #
1688             # _Redirect - Excuse me. You need to be going somewhere else...
1689             #
1690             ###############################################################################
1691             sub _Redirect
1692             {
1693 0     0   0 my $self = shift;
1694 0         0 my $url = shift;
1695              
1696 0         0 return new Net::HTTPServer::Response(code=>"307",
1697             headers=>{ Location=>$url },
1698             );
1699             }
1700              
1701              
1702              
1703              
1704             ###############################################################################
1705             #+-----------------------------------------------------------------------------
1706             #| Private Socket Functions
1707             #+-----------------------------------------------------------------------------
1708             ###############################################################################
1709              
1710             ###############################################################################
1711             #
1712             # _read - Read it all in. All of it.
1713             #
1714             ###############################################################################
1715             sub _read
1716             {
1717 0     0   0 my $self = shift;
1718 0         0 my $client = shift;
1719              
1720 0         0 $self->_nonblock($client);
1721 0         0 my $select = new IO::Select($client);
1722            
1723 0         0 my $request = "";
1724 0         0 my $headers = "";
1725 0         0 my $got_request = 0;
1726 0         0 my $body_length = 0;
1727              
1728 0         0 my $timeEnd = time+5;
1729              
1730 0         0 my $done = 1;
1731 0         0 my $met_expectation = 0;
1732            
1733 0         0 while(!$got_request)
1734             {
1735 0         0 while( $request !~ /\015?\012\015?\012/s)
1736             {
1737 0         0 $self->_read_chunk($select,$client,\$request);
1738 0 0       0 return if (time >= $timeEnd);
1739             }
1740            
1741 0 0       0 if ($headers eq "")
1742             {
1743 0         0 ($headers) = ($request =~ /^(.+?\015?\012\015?\012)/s);
1744 0 0       0 if ($headers =~ /^Content-Length\s*:\s*(\d+)\015?\012?$/im)
1745             {
1746 0         0 $body_length = $1;
1747             }
1748             }
1749              
1750            
1751 0 0 0     0 if (!$met_expectation && ($request =~ /^Expect\s*:\s*(.+?)\015?\012?$/im))
1752             {
1753 0         0 my $expect = $1;
1754 0 0       0 if ($expect eq "100-continue")
1755             {
1756 0         0 $self->_send($client,"HTTP/1.1 100\n");
1757 0         0 $met_expectation = 1;
1758             }
1759             else
1760             {
1761 0         0 return $request."\012\012";
1762             }
1763             }
1764              
1765 0         0 $self->_debug("READ","_read: length: request (",length($request),")");
1766 0         0 $self->_debug("READ","_read: length: headers (",length($headers),")");
1767 0         0 $self->_debug("READ","_read: length: body (",$body_length,")");
1768            
1769 0 0       0 if (length($request) == (length($headers) + $body_length))
1770             {
1771 0         0 $self->_debug("READ","_read: Ok. We got a request.");
1772 0         0 $got_request = 1;
1773             }
1774             else
1775             {
1776 0         0 my $status = $self->_read_chunk($select,$client,\$request);
1777 0 0       0 return unless defined($status);
1778 0 0       0 $got_request = 1 if ($status == 0);
1779 0 0       0 return if (time >= $timeEnd);
1780             }
1781             }
1782              
1783 0         0 return $request;
1784             }
1785              
1786              
1787             ###############################################################################
1788             #
1789             # _read_chunk - Read a chunk at a time.
1790             #
1791             ###############################################################################
1792             sub _read_chunk
1793             {
1794 0     0   0 my $self = shift;
1795 0         0 my $select = shift;
1796 0         0 my $client = shift;
1797 0         0 my $request = shift;
1798            
1799 0 0       0 if ($select->can_read(.01))
1800             {
1801 0         0 my $status = $client->sysread($$request,4*POSIX::BUFSIZ,length($$request));
1802 0 0       0 if (!defined($status))
    0          
1803             {
1804 0         0 $self->_debug("READ","_read_chunk: Something... isn't... right... whoa!");
1805             }
1806             elsif ($status == 0)
1807             {
1808 0         0 $self->_debug("READ","_read_chunk: End of file.");
1809             }
1810             else
1811             {
1812 0         0 $self->_debug("READ","_read_chunk: status($status)\n");
1813 0         0 $self->_debug("READ","_read_chunk: request($$request)\n");
1814             }
1815              
1816 0         0 return $status;
1817             }
1818            
1819 0         0 return 1;
1820             }
1821              
1822              
1823             ###############################################################################
1824             #
1825             # _send - helper function to keep sending until all of the data has been
1826             # returned.
1827             #
1828             ###############################################################################
1829             sub _send
1830             {
1831 0     0   0 my $self = shift;
1832 0         0 my $sock = shift;
1833 0         0 my $data = shift;
1834              
1835 0 0       0 if (ref($data) eq "")
1836             {
1837 0 0       0 return unless defined($self->_send_data($sock,$data));
1838             }
1839 0 0       0 if (ref($data) eq "FileHandle")
1840             {
1841 0         0 while(my $temp = <$data>)
1842             {
1843 0 0       0 return unless defined($self->_send_data($sock,$temp));
1844             }
1845             }
1846              
1847 0         0 return 1;
1848             }
1849              
1850              
1851             ###############################################################################
1852             #
1853             # _send_data - helper function to keep sending until all of the data has been
1854             # returned.
1855             #
1856             ###############################################################################
1857             sub _send_data
1858             {
1859 0     0   0 my $self = shift;
1860 0         0 my $sock = shift;
1861 0         0 my $data = shift;
1862              
1863 0         0 my $select = new IO::Select($sock);
1864            
1865 0         0 my $length = length($data);
1866 0         0 my $offset = 0;
1867 0   0     0 while (($length != 0) && $select->can_write())
1868             {
1869 0         0 $self->_debug("SEND","_send_data: offset($offset) length($length)");
1870 0         0 my $written = $sock->syswrite($data,$length,$offset);
1871 0 0       0 if (defined($written))
1872             {
1873 0         0 $self->_debug("SEND","_send_data: written($written)");
1874 0         0 $length -= $written;
1875 0         0 $offset += $written;
1876             }
1877             else
1878             {
1879 0         0 $self->_debug("SEND","_send_data: error");
1880 0         0 return;
1881             }
1882             }
1883              
1884 0         0 $self->_debug("SEND","_send_data: sent all data");
1885 0         0 return 1;
1886             }
1887              
1888              
1889              
1890              
1891             ###############################################################################
1892             #+-----------------------------------------------------------------------------
1893             #| Private Server Functions
1894             #+-----------------------------------------------------------------------------
1895             ###############################################################################
1896              
1897             ###############################################################################
1898             #
1899             # _forking_huntsman - Kill all of the child processes
1900             #
1901             ###############################################################################
1902             sub _forking_huntsman
1903             {
1904 0     0   0 my $self = shift;
1905              
1906 0         0 $self->_debug("PROC","_forking_hunstman: Killing children");
1907 0         0 $self->_log("Killing children");
1908            
1909 0         0 $SIG{CHLD} = 'IGNORE';
1910            
1911 0 0       0 if (scalar(keys(%{$self->{CHILDREN}})) > 0)
  0         0  
1912             {
1913 0         0 kill("INT",keys(%{$self->{CHILDREN}}));
  0         0  
1914             }
1915             }
1916              
1917              
1918             ###############################################################################
1919             #
1920             # _forking_process - This is a forking model.
1921             #
1922             ###############################################################################
1923             sub _forking_process
1924             {
1925 0     0   0 my $self = shift;
1926            
1927 0         0 while($self->{NUMCHILDREN} < $self->{CFG}->{NUMPROC})
1928             {
1929 0         0 $self->_forking_spawn();
1930             }
1931              
1932 0         0 select(undef,undef,undef,0.1);
1933             }
1934              
1935              
1936             ###############################################################################
1937             #
1938             # _forking_reaper - When a child dies, have a funeral, mourn, and then move on
1939             #
1940             ###############################################################################
1941             sub _forking_reaper
1942             {
1943 0     0   0 my $self = shift;
1944              
1945 0     0   0 $SIG{CHLD} = sub{ $self->_forking_reaper(); };
  0         0  
1946 0         0 my $pid = wait;
1947 0 0       0 if (exists($self->{CHILDREN}->{$pid}))
1948             {
1949 0         0 $self->{NUMCHILDREN}--;
1950 0         0 delete($self->{CHILDREN}->{$pid});
1951             }
1952             }
1953              
1954              
1955             ###############################################################################
1956             #
1957             # _forking_spawn - Give birth to a new child process
1958             #
1959             ###############################################################################
1960             sub _forking_spawn
1961             {
1962 0     0   0 my $self = shift;
1963              
1964 0         0 my $pid;
1965              
1966 0 0       0 croak("Could not fork: $!") unless defined ($pid = fork);
1967            
1968 0 0       0 if ($pid)
1969             {
1970 0         0 $self->{CHILDREN}->{$pid} = 1;
1971 0         0 $self->{NUMCHILDREN}++;
1972 0         0 return;
1973             }
1974             else
1975             {
1976 0         0 $SIG{INT} = $SIG{TERM} = $SIG{HUP} = 'DEFAULT';
1977 0         0 $SIG{PIPE} = 'DEFAULT';
1978              
1979 0         0 my $max_clients = 20; # Make this a config?
1980            
1981 0         0 foreach my $i (0..$max_clients)
1982             {
1983 0         0 my $client;
1984 0 0       0 if($self->{SELECT}->can_read())
1985             {
1986 0         0 $client = $self->{SOCK}->accept();
1987             }
1988 0 0       0 last unless defined($client);
1989 0         0 $self->_process($client);
1990             }
1991              
1992 0         0 exit;
1993             }
1994             }
1995              
1996              
1997             ###############################################################################
1998             #
1999             # _process - Handle a client.
2000             #
2001             ###############################################################################
2002             sub _process
2003             {
2004 0     0   0 my $self = shift;
2005 0         0 my $client = shift;
2006              
2007 0         0 $self->_debug("PROC","_process: We have a client, let's treat them well.");
2008              
2009 0         0 $client->autoflush(1);
2010            
2011 0         0 my $request = $self->_read($client);
2012            
2013             #--------------------------------------------------------------------------
2014             # Take the request and do the magic
2015             #--------------------------------------------------------------------------
2016 0 0       0 if (defined($request))
2017             {
2018             #----------------------------------------------------------------------
2019             # Create the Request Object
2020             #----------------------------------------------------------------------
2021 0         0 my $requestObj = $self->_ReadRequest($request);
2022            
2023             #----------------------------------------------------------------------
2024             # Profile the client
2025             #----------------------------------------------------------------------
2026 0         0 my $other_end = $client->peername();
2027              
2028 0 0       0 if ($other_end)
2029             {
2030 0         0 my ($port, $iaddr) = unpack_sockaddr_in($other_end);
2031 0         0 my $ip_addr = inet_ntoa($iaddr);
2032 0         0 $requestObj->_env("REMOTE_ADDR",$ip_addr);
2033            
2034 0         0 my $hostname = gethostbyaddr($iaddr, AF_INET);
2035 0 0       0 $requestObj->_env("REMOTE_NAME",$hostname) if ($hostname);
2036             }
2037              
2038 0 0       0 $requestObj->_env("DOCUMENT_ROOT",$self->{CFG}->{DOCROOT})
2039             if defined($self->{CFG}->{DOCROOT});
2040 0         0 $requestObj->_env("GATEWAY_INTERFACE","CGI/1.1");
2041 0 0       0 $requestObj->_env("HTTP_REFERER",$requestObj->Header("Referer"))
2042             if defined($requestObj->Header("Referer"));
2043 0 0       0 $requestObj->_env("HTTP_USER_AGENT",$requestObj->Header("User-Agent"))
2044             if defined($requestObj->Header("User-Agent"));
2045 0         0 $requestObj->_env("QUERY_STRING",$requestObj->Query());
2046 0         0 $requestObj->_env("REQUEST_METHOD",$requestObj->Method());
2047 0         0 $requestObj->_env("SCRIPT_NAME",$requestObj->Path());
2048 0         0 $requestObj->_env("SERVER_ADMIN",$self->{CFG}->{ADMIN});
2049 0         0 $requestObj->_env("SERVER_NAME",$self->{SERVER}->{NAME});
2050 0         0 $requestObj->_env("SERVER_PORT",$self->{SERVER}->{PORT});
2051 0         0 $requestObj->_env("SERVER_PROTOCOL",$requestObj->Protocol());
2052 0         0 $requestObj->_env("SERVER_SOFTWARE",join(" ",@{$self->{SERVER_TOKENS}}));
  0         0  
2053            
2054             #----------------------------------------------------------------------
2055             # Process the Request
2056             #----------------------------------------------------------------------
2057 0         0 my $responseObj = $self->_ProcessRequest($requestObj);
2058            
2059             #----------------------------------------------------------------------
2060             # Return the Response
2061             #----------------------------------------------------------------------
2062 0         0 $self->_ReturnResponse($client,$requestObj,$responseObj);
2063             }
2064            
2065             #------------------------------------------------------------------
2066             # That's it. Close down the connection.
2067             #------------------------------------------------------------------
2068 0 0       0 $client->close() if ($self->{CFG}->{SSL} == 0);
2069 0 0       0 $client->close(SSL_no_shutdown=>1) if ($self->{CFG}->{SSL} == 1);
2070            
2071 0         0 $self->_debug("PROC","_process: Thanks for shopping with us!");
2072             }
2073              
2074              
2075             ###############################################################################
2076             #
2077             # _single_process - This is a single process model.
2078             #
2079             ###############################################################################
2080             sub _single_process
2081             {
2082 0     0   0 my $self = shift;
2083 0         0 my $timestop = shift;
2084              
2085 0         0 my $client;
2086             my $clientSelect;
2087            
2088 0 0       0 my $wait = (defined($timestop) ? $timestop - time : 10);
2089 0         0 $self->_debug("PROC","_single_process: Wait for $wait seconds");
2090            
2091             #------------------------------------------------------------------
2092             # Take the request and do the magic
2093             #------------------------------------------------------------------
2094 0 0       0 if ($self->{SELECT}->can_read($wait))
2095             {
2096 0         0 $self->_debug("PROC","_single_process: Incoming traffic");
2097 0         0 $client = $self->{SOCK}->accept();
2098             }
2099            
2100 0 0       0 if (defined($client))
2101             {
2102 0         0 $self->_process($client);
2103             }
2104             }
2105              
2106              
2107              
2108             ###############################################################################
2109             #+-----------------------------------------------------------------------------
2110             #| Private Utility Functions
2111             #+-----------------------------------------------------------------------------
2112             ###############################################################################
2113              
2114             ###############################################################################
2115             #
2116             # _accept - given an Accept line and Content-Type, is it in the list?
2117             #
2118             ###############################################################################
2119             sub _accept
2120             {
2121 0     0   0 my $self = shift;
2122 0         0 my $accept = shift;
2123 0         0 my $contentType = shift;
2124              
2125 0         0 $accept =~ s/\s*\,\s*/\,/g;
2126 0         0 $accept =~ s/\s*\;\s*/\;/g;
2127 0         0 $accept =~ s/\s*$//;
2128              
2129 0         0 my ($mainType,$subType) = split("/",$contentType,2);
2130              
2131 0         0 foreach my $entry (split(",",$accept))
2132             {
2133 0         0 my ($testType,$scale) = split(";",$entry,2);
2134 0 0       0 return 1 if ($testType eq $contentType);
2135 0 0       0 return 1 if ($testType eq "$mainType/*");
2136 0 0       0 return 1 if ($testType eq "*/*");
2137             }
2138              
2139 0         0 return;
2140             }
2141              
2142              
2143             ###############################################################################
2144             #
2145             # _arg - if the arg exists then use it, else use the default.
2146             #
2147             ###############################################################################
2148             sub _arg
2149             {
2150 54     54   78 my $self = shift;
2151 54         64 my $arg = shift;
2152 54         60 my $default = shift;
2153 54         57 my $valid = shift;
2154              
2155 54 100       129 my $val = (exists($self->{ARGS}->{$arg}) ? $self->{ARGS}->{$arg} : $default);
2156              
2157 54 100       105 if (defined($valid))
2158             {
2159 3         6 my $pass = 0;
2160 3         6 foreach my $check (@{$valid})
  3         10  
2161             {
2162 6 100       21 $pass = 1 if ($check eq $val);
2163             }
2164 3 50       13 if ($pass == 0)
2165             {
2166 0         0 croak("Invalid value for setting '$arg' = '$val'. Valid are: ['".join("','",@{$valid})."']");
  0         0  
2167             }
2168             }
2169            
2170              
2171 54         176 return $val;
2172             }
2173              
2174              
2175             ###############################################################################
2176             #
2177             # _checkAuth - return 1 if the url requires an Auth, undefined otherwise.
2178             #
2179             ###############################################################################
2180             sub _checkAuth
2181             {
2182 0     0   0 my $self = shift;
2183 0         0 my $url = shift;
2184              
2185 0         0 my @url = split("/",$url);
2186 0         0 foreach my $i (reverse 0..$#url)
2187             {
2188 0         0 my $check = join("/",@url[0..$i]);
2189 0 0       0 if($check eq "")
2190             {
2191 0         0 $check = "/";
2192             }
2193 0         0 $self->_debug("AUTH","_checkAuth: check($check)");
2194 0 0       0 return $check if exists($self->{AUTH}->{$check});
2195             }
2196              
2197 0         0 return;
2198             }
2199              
2200              
2201             ###############################################################################
2202             #
2203             # _date - format the date correctly for the given time.
2204             #
2205             ###############################################################################
2206             sub _date
2207             {
2208 0     0   0 my $time = shift;
2209 0         0 my $delta = shift;
2210              
2211 0 0       0 $time = time unless defined($time);
2212 0 0       0 $time += $delta if defined($delta);
2213              
2214 0         0 my @times = gmtime($time);
2215            
2216 0         0 my $date = sprintf("%s, %02d-%s-%d %02d:%02d:%02d GMT",
2217             (qw(Sun Mon Tue Wed Thu Fri Sat))[$times[6]],
2218             $times[3],
2219             (qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec))[$times[4]],
2220             $times[5]+1900,
2221             $times[2],
2222             $times[1],
2223             $times[0]
2224             );
2225              
2226 0         0 return $date;
2227             }
2228              
2229              
2230             ###############################################################################
2231             #
2232             # _debug - print out a debug message
2233             #
2234             ###############################################################################
2235             sub _debug
2236             {
2237 0     0   0 my $self = shift;
2238 0         0 my $zone = shift;
2239 0         0 my (@message) = @_;
2240            
2241 0         0 my $fh = $self->{DEBUGLOG};
2242 0 0 0     0 print $fh "$zone: ",join("",@message),"\n"
2243             if (exists($self->{DEBUGZONES}->{$zone}) ||
2244             exists($self->{DEBUGZONES}->{ALL}));
2245             }
2246              
2247              
2248             ###############################################################################
2249             #
2250             # _digest_HA1 - calculate the H(A1) per RFC2617
2251             #
2252             ###############################################################################
2253             sub _digest_HA1
2254             {
2255 0     0   0 my $self = shift;
2256 0         0 my $auth = shift;
2257 0         0 my $passwd = shift;
2258            
2259 0         0 my @raw;
2260 0         0 push(@raw,$auth->{username});
2261 0         0 push(@raw,$auth->{realm});
2262 0         0 push(@raw,$passwd);
2263            
2264 0         0 my $raw = join(":",@raw);
2265              
2266             #$self->_debug("AUTH","_digest_HA1: raw($raw)");
2267              
2268 0         0 return Digest::MD5::md5_hex($raw);
2269             }
2270              
2271              
2272             ###############################################################################
2273             #
2274             # _digest_HA2 - calculate the H(A2) per RFC2617
2275             #
2276             ###############################################################################
2277             sub _digest_HA2
2278             {
2279 0     0   0 my $self = shift;
2280 0         0 my $auth = shift;
2281 0         0 my $method = shift;
2282              
2283 0         0 my @raw;
2284 0         0 push(@raw,$method);
2285 0         0 push(@raw,$auth->{uri});
2286              
2287 0         0 my $raw = join(":",@raw);
2288              
2289             #$self->_debug("AUTH","_digest_HA2: raw($raw)");
2290              
2291 0         0 return Digest::MD5::md5_hex($raw);
2292             }
2293              
2294              
2295             ###############################################################################
2296             #
2297             # _digest_KD - calculate the KD() per RFC2617
2298             #
2299             ###############################################################################
2300             sub _digest_KD
2301             {
2302 0     0   0 my $self = shift;
2303 0         0 my $auth = shift;
2304 0         0 my $ha1 = shift;
2305 0         0 my $ha2 = shift;
2306              
2307 0         0 my @raw;
2308 0         0 push(@raw,$ha1);
2309 0         0 push(@raw,$auth->{nonce});
2310              
2311 0 0 0     0 if(exists($auth->{qop}) && ($auth->{qop} eq "auth"))
2312             {
2313 0         0 push(@raw,$auth->{nc});
2314 0         0 push(@raw,$auth->{cnonce});
2315 0         0 push(@raw,$auth->{qop});
2316             }
2317              
2318 0         0 push(@raw,$ha2);
2319            
2320 0         0 my $raw = join(":",@raw);
2321              
2322             #$self->_debug("AUTH","_digest_KD: raw($raw)");
2323              
2324 0         0 return Digest::MD5::md5_hex($raw);
2325             }
2326              
2327              
2328             ###############################################################################
2329             #
2330             # _log - print out the message to a log with the current time
2331             #
2332             ###############################################################################
2333             sub _log
2334             {
2335 0     0   0 my $self = shift;
2336 0         0 my (@message) = @_;
2337            
2338 0         0 my $fh = $self->{LOG};
2339            
2340 0         0 print $fh $self->_timestamp()," - ",join("",@message),"\n";
2341             }
2342              
2343              
2344             ###############################################################################
2345             #
2346             # _mimetypes - Read in the mime.types file
2347             #
2348             ###############################################################################
2349             sub _mimetypes
2350             {
2351 3     3   7 my $self = shift;
2352              
2353 3         124 open(MT,$self->{CFG}->{MIMETYPES});
2354 3         59 while()
2355             {
2356 1923 100       3975 next if /^\#/;
2357 1857 100       4657 next if /^\s+$/;
2358              
2359 1818         6727 my ($mime_type,$extensions) = /^(\S+)(.*?)$/;
2360              
2361 1818 100       7406 next if ($extensions =~ /^\s*$/);
2362            
2363 693         2185 $extensions =~ s/\s+/\ /g;
2364            
2365 693         1523 foreach my $ext (split(" ",$extensions))
2366             {
2367 963 50       1735 next if ($ext eq "");
2368              
2369 963         4830 $self->{MIMETYPES}->{$ext} = $mime_type;
2370             }
2371             }
2372 3         51 close(MT);
2373             }
2374              
2375              
2376             ###############################################################################
2377             #
2378             # _nonblock - given a socket, make it non-blocking
2379             #
2380             ###############################################################################
2381             sub _nonblock
2382             {
2383 0     0     my $self = shift;
2384 0           my $socket = shift;
2385            
2386             #--------------------------------------------------------------------------
2387             # Code copied from POE::Wheel::SocketFactory...
2388             # Win32 does things one way...
2389             #--------------------------------------------------------------------------
2390 0 0 0       if (($^O eq "MSWin32") || ($^O eq "cygwin"))
2391             {
2392 0           my $FIONBIO = 0x8004667E;
2393 0           my $temp = 1;
2394 0 0         ioctl( $socket, $FIONBIO, \$temp) ||
2395             croak("Can't make socket nonblocking (".$^O."): $!");
2396 0           return;
2397             }
2398              
2399             #--------------------------------------------------------------------------
2400             # And UNIX does them another
2401             #--------------------------------------------------------------------------
2402 0   0       my $flags = fcntl($socket, F_GETFL, 0) ||
2403             croak("Can't get flags for socket: $!\n");
2404 0 0         fcntl($socket, F_SETFL, $flags | O_NONBLOCK) ||
2405             croak("Can't make socket nonblocking: $!\n");
2406             }
2407              
2408              
2409             ###############################################################################
2410             #
2411             # _nonce - produce a new nonce
2412             #
2413             ###############################################################################
2414             sub _nonce
2415             {
2416 0     0     my $self = shift;
2417              
2418 0           return MIME::Base64::encode(time.":".$self->{PRIVATEKEY},"");
2419             }
2420              
2421              
2422             ###############################################################################
2423             #
2424             # _timestamp - generic funcion for getting a timestamp.
2425             #
2426             ###############################################################################
2427             sub _timestamp
2428             {
2429 0     0     my $self = shift;
2430              
2431 0           my ($sec,$min,$hour,$mday,$mon,$year,$wday) = localtime(time);
2432              
2433 0           my $month = ('Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec')[$mon];
2434 0           $mon++;
2435              
2436 0           return sprintf("%d/%02d/%02d %02d:%02d:%02d",($year + 1900),$mon,$mday,$hour,$min,$sec);
2437             }
2438              
2439              
2440             1;