OSDN Git Service

2013.10.24
[uclinux-h8/uClinux-dist.git] / user / mini_httpd / mini_httpd.c
1 /* mini_httpd - small HTTP server
2 **
3 ** Copyright © 1999,2000 by Jef Poskanzer <jef@acme.com>.
4 ** All rights reserved.
5 **
6 ** Redistribution and use in source and binary forms, with or without
7 ** modification, are permitted provided that the following conditions
8 ** are met:
9 ** 1. Redistributions of source code must retain the above copyright
10 **    notice, this list of conditions and the following disclaimer.
11 ** 2. Redistributions in binary form must reproduce the above copyright
12 **    notice, this list of conditions and the following disclaimer in the
13 **    documentation and/or other materials provided with the distribution.
14 **
15 ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 ** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 ** ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 ** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 ** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 ** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 ** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 ** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 ** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 ** SUCH DAMAGE.
26 */
27
28
29 #include "version.h"
30 #include "port.h"
31
32 #include <unistd.h>
33 #include <stdlib.h>
34 #include <stdio.h>
35 #include <string.h>
36 #include <time.h>
37 #include <sys/types.h>
38 #include <sys/stat.h>
39 #include <sys/mman.h>
40 #include <sys/time.h>
41 #include <pwd.h>
42 #include <errno.h>
43 #include <fcntl.h>
44 #include <signal.h>
45 #include <ctype.h>
46 #include <sys/wait.h>
47 #include <sys/socket.h>
48 #include <netinet/in.h>
49 #include <arpa/inet.h>
50 #include <netdb.h>
51
52 #ifdef USE_SSL
53 #include <openssl/ssl.h>
54 #endif /* USE_SSL */
55
56
57 #define ERR_DIR "errors"
58 #define DEFAULT_HTTP_PORT 80
59 #ifdef USE_SSL
60 #define DEFAULT_HTTPS_PORT 443
61 #ifdef EMBED
62 #define CERT_FILE "/etc/config/cert.pem"
63 #define KEY_FILE  "/etc/config/key.pem"
64 #else /* EMBED */
65 #define CERT_FILE "cert.pem"
66 #define KEY_FILE "key.pem"
67 #endif /* EMBED */
68 #endif /* USE_SSL */
69 #define DEFAULT_USER "nobody"
70 #define CGI_NICE 10
71 #define CGI_PATH "/usr/local/bin:/usr/ucb:/bin:/usr/bin"
72 #define CGI_LD_LIBRARY_PATH "/usr/local/lib:/usr/lib"
73 #define AUTH_FILE ".htpasswd"
74
75 #define METHOD_GET 1
76 #define METHOD_HEAD 2
77 #define METHOD_POST 3
78
79 #ifndef MIN
80 #define MIN(a,b) ((a) < (b) ? (a) : (b))
81 #endif
82
83
84 /* A multi-family sockaddr. */
85 typedef union {
86     struct sockaddr sa;
87     struct sockaddr_in sa_in;
88 #ifdef HAVE_SOCKADDR_IN6
89     struct sockaddr_in6 sa_in6;
90 #endif /* HAVE_SOCKADDR_IN6 */
91 #ifdef HAVE_SOCKADDR_STORAGE
92     struct sockaddr_storage sa_stor;
93 #endif /* HAVE_SOCKADDR_STORAGE */
94     } usockaddr;
95
96
97 static char* argv0;
98 static int debug;
99 static int port;
100 static int do_chroot;
101 static int vhost;
102 static char* user;
103 static char* cgi_pattern;
104 static char* hostname;
105 static char hostname_buf[500];
106 static u_int hostaddr;
107 static char* logfile;
108 static char* pidfile;
109 static char* charset;
110 static FILE* logfp;
111 static int listen4_fd, listen6_fd;
112 #ifdef USE_SSL
113 static int do_ssl;
114 static SSL_CTX* ssl_ctx;
115 #endif /* USE_SSL */
116
117
118 /* Request variables. */
119 static int conn_fd;
120 #ifdef USE_SSL
121 static SSL* ssl;
122 #endif /* USE_SSL */
123 static usockaddr client_addr;
124 static char* request;
125 static int request_size, request_len, request_idx;
126 static int method;
127 static char* path;
128 static char* file;
129 struct stat sb;
130 static char* query;
131 static char* protocol;
132 static int status;
133 static long bytes;
134 static char* req_hostname;
135
136 static char* authorization;
137 static long content_length;
138 static char* content_type;
139 static char* cookie;
140 static char* host;
141 static time_t if_modified_since;
142 static char* referer;
143 static char* useragent;
144
145 static char* remoteuser;
146
147
148 /* Forwards. */
149 static void usage( void );
150 static int initialize_listen_socket( usockaddr* usaP );
151 static void handle_request( void );
152 static void de_dotdot( char* file );
153 static void do_file( void );
154 static void do_dir( void );
155 static void do_cgi( void );
156 static void cgi_interpose_input( int wfd );
157 static void cgi_interpose_output( int rfd, int parse_headers );
158 static char** make_argp( void );
159 static char** make_envp( void );
160 static char* build_env( char* fmt, char* arg );
161 static void auth_check( char* dirname );
162 static void send_authenticate( char* realm );
163 static char* virtual_file( char* file );
164 static void send_error( int s, char* title, char* extra_header, char* text );
165 static void send_error_body( int s, char* title, char* text );
166 static int send_error_file( char* filename );
167 static void send_error_tail( void );
168 static void add_headers( int s, char* title, char* extra_header, char* mime_type, long b, time_t mod );
169 static void start_request( void );
170 static void add_to_request( char* str, int len );
171 static char* get_request_line( void );
172 static void start_response( void );
173 static void add_to_response( char* str, int len );
174 static void send_response( void );
175 static int my_read( char* buf, int size );
176 static int my_write( char* buf, int size );
177 static void add_to_buf( char** bufP, int* bufsizeP, int* buflenP, char* str, int len );
178 static void make_log_entry( void );
179 static char* get_method_str( int m );
180 static char* get_mime_type( char* name );
181 static void handle_sigterm( int sig );
182 static void handle_sigchld( int sig );
183 static void lookup_hostname( usockaddr* usa4P, size_t sa4_len, int* gotv4P, usockaddr* usa6P, size_t sa6_len, int* gotv6P );
184 static char* ntoa( usockaddr* usaP );
185 static size_t sockaddr_len( usockaddr* usaP );
186 static void strdecode( char* to, char* from );
187 static int hexit( char c );
188 static int b64_decode( const char* str, unsigned char* space, int size );
189 static int match( const char* pattern, const char* string );
190 static int match_one( const char* pattern, int patternlen, const char* string );
191
192
193 int
194 main( int argc, char** argv )
195     {
196     int argn;
197     uid_t uid;
198     usockaddr host_addr4;
199     usockaddr host_addr6;
200     int gotv4, gotv6;
201     fd_set afdset;
202     int maxfd;
203     usockaddr usa;
204     int sz, r;
205
206     /* Parse args. */
207     argv0 = argv[0];
208     debug = 0;
209     port = -1;
210     do_chroot = 0;
211     vhost = 0;
212     cgi_pattern = (char*) 0;
213     charset = "iso-8859-1";
214     user = DEFAULT_USER;
215     hostname = (char*) 0;
216     logfile = (char*) 0;
217     pidfile = (char*) 0;
218     logfp = (FILE*) 0;
219 #ifdef USE_SSL
220     do_ssl = 0;
221 #endif /* USE_SSL */
222     argn = 1;
223     while ( argn < argc && argv[argn][0] == '-' )
224         {
225         if ( strcmp( argv[argn], "-D" ) == 0 )
226             debug = 1;
227 #ifdef USE_SSL
228         else if ( strcmp( argv[argn], "-S" ) == 0 )
229             do_ssl = 1;
230 #endif /* USE_SSL */
231         else if ( strcmp( argv[argn], "-p" ) == 0 && argn + 1 < argc )
232             {
233             ++argn;
234             port = atoi( argv[argn] );
235             }
236         else if ( strcmp( argv[argn], "-c" ) == 0 && argn + 1 < argc )
237             {
238             ++argn;
239             cgi_pattern = argv[argn];
240             }
241         else if ( strcmp( argv[argn], "-u" ) == 0 && argn + 1 < argc )
242             {
243             ++argn;
244             user = argv[argn];
245             }
246         else if ( strcmp( argv[argn], "-h" ) == 0 && argn + 1 < argc )
247             {
248             ++argn;
249             hostname = argv[argn];
250             }
251         else if ( strcmp( argv[argn], "-r" ) == 0 )
252             do_chroot = 1;
253         else if ( strcmp( argv[argn], "-v" ) == 0 )
254             vhost = 1;
255         else if ( strcmp( argv[argn], "-l" ) == 0 && argn + 1 < argc )
256             {
257             ++argn;
258             logfile = argv[argn];
259             }
260         else if ( strcmp( argv[argn], "-i" ) == 0 && argn + 1 < argc )
261             {
262             ++argn;
263             pidfile = argv[argn];
264             }
265         else if ( strcmp( argv[argn], "-T" ) == 0 && argn + 1 < argc )
266             {
267             ++argn;
268             charset = argv[argn];
269             }
270         else
271             usage();
272         ++argn;
273         }
274     if ( argn != argc )
275         usage();
276
277     if ( port == -1 )
278         {
279 #ifdef USE_SSL
280         if ( do_ssl )
281             port = DEFAULT_HTTPS_PORT;
282         else
283             port = DEFAULT_HTTP_PORT;
284 #else /* USE_SSL */
285         port = DEFAULT_HTTP_PORT;
286 #endif /* USE_SSL */
287         }
288
289     if ( logfile != (char*) 0 )
290         {
291         /* Open the log file. */
292         logfp = fopen( logfile, "a" );
293         if ( logfp == (FILE*) 0 )
294             {
295             perror( logfile );
296             exit( 1 );
297             }
298         }
299
300     /* Look up hostname. */
301     lookup_hostname(
302         &host_addr4, sizeof(host_addr4), &gotv4,
303         &host_addr6, sizeof(host_addr6), &gotv6 );
304     if ( hostname == (char*) 0 )
305         {
306         (void) gethostname( hostname_buf, sizeof(hostname_buf) );
307         hostname = hostname_buf;
308         }
309     if ( ! ( gotv4 || gotv6 ) )
310         {
311         (void) fprintf( stderr, "can't find any valid address\n" );
312         exit( 1 );
313         }
314
315     /* Initialize listen sockets.  Try v6 first because of a Linux peculiarity; 
316     ** unlike other systems, it has magical v6 sockets that also listen for v4,
317     ** but if you bind a v4 socket first then the v6 bind fails.
318     */
319     if ( gotv6 )
320         listen6_fd = initialize_listen_socket( &host_addr6 );
321     else
322         listen6_fd = -1;
323     if ( gotv4 )
324         listen4_fd = initialize_listen_socket( &host_addr4 );
325     else
326         listen4_fd = -1;
327     /* If we didn't get any valid sockets, fail. */
328     if ( listen4_fd == -1 && listen6_fd == -1 )
329         {
330         (void) fprintf( stderr, "can't bind to any address\n" );
331         exit( 1 );
332         }
333
334 #ifdef USE_SSL
335     if ( do_ssl )
336         {
337         SSLeay_add_ssl_algorithms();
338         SSL_load_error_strings();
339         ssl_ctx = SSL_CTX_new( SSLv23_server_method() );
340         if ( SSL_CTX_use_certificate_file( ssl_ctx, CERT_FILE, SSL_FILETYPE_PEM ) == 0 ||
341              SSL_CTX_use_PrivateKey_file( ssl_ctx, KEY_FILE, SSL_FILETYPE_PEM ) == 0 ||
342              SSL_CTX_check_private_key( ssl_ctx ) == 0 )
343             {
344             ERR_print_errors_fp( stderr );
345             exit( 1 );
346             }
347         }
348 #endif /* USE_SSL */
349
350     if ( ! debug )
351         {
352         /* Make ourselves a daemon. */
353 #ifdef HAVE_DAEMON
354         if ( daemon( 1, 1 ) < 0 )
355             {
356             perror( "daemon" );
357             exit( 1 );
358             }
359 #else
360         switch ( fork() )
361             {
362             case 0:
363             break;
364             case -1:
365             perror( "fork" );
366             exit( 1 );
367             default:
368             exit( 0 );
369             }
370 #ifdef HAVE_SETSID
371         (void) setsid();
372 #endif
373 #endif
374         }
375     else
376         {
377         /* Even if we don't daemonize, we still want to disown our parent
378         ** process.
379         */
380 #ifdef HAVE_SETSID
381         (void) setsid();
382 #endif /* HAVE_SETSID */
383         }
384
385     if ( pidfile != (char*) 0 )
386         {
387         /* Write the PID file. */
388         FILE* pidfp = fopen( pidfile, "w" );
389         if ( pidfp == (FILE*) 0 )
390             {
391             perror( pidfile );
392             exit( 1 );
393             }
394         (void) fprintf( pidfp, "%d\n", (int) getpid() );
395         (void) fclose( pidfp );
396         }
397
398     /* Read zone info now, in case we chroot(). */
399     tzset();
400
401 #ifndef EMBED
402     /* If we're root, start becoming someone else. */
403     if ( getuid() == 0 )
404         {
405         struct passwd* pwd;
406         pwd = getpwnam( user );
407         if ( pwd == (struct passwd*) 0 )
408             {
409             (void) fprintf( stderr, "%s: unknown user - '%s'\n", argv0, user );
410             exit( 1 );
411             }
412         /* Set aux groups to null. */
413         if ( setgroups( 0, (const gid_t*) 0 ) < 0 )
414             {
415             perror( "setgroups" );
416             exit( 1 );
417             }
418         /* Set primary group. */
419         if ( setgid( pwd->pw_gid ) < 0 )
420             {
421             perror( "setgid" );
422             exit( 1 );
423             }
424         /* Try setting aux groups correctly - not critical if this fails. */
425         if ( initgroups( user, pwd->pw_gid ) < 0 )
426             perror( "initgroups" );
427 #ifdef HAVE_SETLOGIN
428         /* Set login name. */
429         (void) setlogin( user );
430 #endif /* HAVE_SETLOGIN */
431         /* Save the new uid for setting after we chroot(). */
432         uid = pwd->pw_uid;
433         }
434 #endif /* EMBED */
435
436     /* Chroot if requested. */
437     if ( do_chroot )
438         {
439         char cwd[1000];
440         (void) getcwd( cwd, sizeof(cwd) - 1 );
441         if ( chroot( cwd ) < 0 )
442             {
443             perror( "chroot" );
444             exit( 1 );
445             }
446         /* Always chdir to / after a chroot. */
447         if ( chdir( "/" ) < 0 )
448             {
449             perror( "chroot chdir" );
450             exit( 1 );
451             }
452
453         }
454
455 #ifndef EMBED
456     /* If we're root, become someone else. */
457     if ( getuid() == 0 )
458         {
459         /* Set uid. */
460         if ( setuid( uid ) < 0 )
461             {
462             perror( "setuid" );
463             exit( 1 );
464             }
465         /* Check for unnecessary security exposure. */
466         if ( ! do_chroot )
467             (void) fprintf( stderr,
468                 "%s: started as root without requesting chroot(), warning only\n", argv0 );
469         }
470 #endif /* EMBED */
471
472     /* Catch various termination signals. */
473     (void) signal( SIGTERM, handle_sigterm );
474     (void) signal( SIGINT, handle_sigterm );
475     (void) signal( SIGHUP, handle_sigterm );
476     (void) signal( SIGUSR1, handle_sigterm );
477
478     /* Catch defunct children. */
479     (void) signal( SIGCHLD, handle_sigchld );
480
481     /* And get EPIPE instead of SIGPIPE. */
482     (void) signal( SIGPIPE, SIG_IGN );
483
484     /* Main loop. */
485     for (;;)
486         {
487         /* Possibly do a select() on the possible two listen fds. */
488         FD_ZERO( &afdset );
489         maxfd = -1;
490         if ( listen4_fd != -1 )
491             {
492             FD_SET( listen4_fd, &afdset );
493             if ( listen4_fd > maxfd )
494                 maxfd = listen4_fd;
495             }
496         if ( listen6_fd != -1 )
497             {
498             FD_SET( listen6_fd, &afdset );
499             if ( listen6_fd > maxfd )
500                 maxfd = listen6_fd;
501             }
502         if ( listen4_fd != -1 && listen6_fd != -1 )
503             if ( select( maxfd + 1, &afdset, (fd_set*) 0, (fd_set*) 0, (struct timeval*) 0 ) < 0 )
504                 {
505                 perror( "select" );
506                 exit( 1 );
507                 }
508         /* (If we don't have two listen fds, we can just skip the select()
509         ** and fall through.  Whichever listen fd we do have will do a
510         ** blocking accept() instead.)
511         */
512
513         /* Accept the new connection. */
514         sz = sizeof(usa);
515         if ( listen4_fd != -1 && FD_ISSET( listen4_fd, &afdset ) )
516             conn_fd = accept( listen4_fd, &usa.sa, &sz );
517         else if ( listen6_fd != -1 && FD_ISSET( listen6_fd, &afdset ) )
518             conn_fd = accept( listen6_fd, &usa.sa, &sz );
519         else
520             {
521             (void) fprintf( stderr, "%s: select failure\n", argv0 );
522             exit( 1 );
523             }
524         if ( conn_fd < 0 )
525             {
526             if ( errno == EINTR )
527                 continue;       /* try again */
528             perror( "accept" );
529             exit( 1 );
530             }
531
532         /* Fork a sub-process to handle the connection. */
533         r = fork();
534         if ( r < 0 )
535             {
536             perror( "fork" );
537             exit( 1 );
538             }
539         if ( r == 0 )
540             {
541             /* Child process. */
542             client_addr = usa;
543             if ( listen4_fd != -1 )
544                 (void) close( listen4_fd );
545             if ( listen6_fd != -1 )
546                 (void) close( listen6_fd );
547             handle_request();
548             exit( 0 );
549             }
550         (void) close( conn_fd );
551         }
552     }
553
554
555 static void
556 usage( void )
557     {
558 #ifdef USE_SSL
559     (void) fprintf( stderr, "usage:  %s [-D] [-S] [-p port] [-c cgipat] [-u user] [-h hostname] [-r] [-v] [-l logfile] [-i pidfile] [-T charset]\n", argv0 );
560 #else /* USE_SSL */
561     (void) fprintf( stderr, "usage:  %s [-D] [-p port] [-c cgipat] [-u user] [-h hostname] [-r] [-v] [-l logfile] [-i pidfile] [-T charset]\n", argv0 );
562 #endif /* USE_SSL */
563     exit( 1 );
564     }
565
566
567 static int
568 initialize_listen_socket( usockaddr* usaP )
569     {
570     int listen_fd;
571     int i;
572
573     listen_fd = socket( usaP->sa.sa_family, SOCK_STREAM, 0 );
574     if ( listen_fd < 0 )
575         {
576         perror( "socket" );
577         return -1;
578         }
579     (void) fcntl( listen_fd, F_SETFD, 1 );
580     i = 1;
581     if ( setsockopt( listen_fd, SOL_SOCKET, SO_REUSEADDR, (char*) &i, sizeof(i) ) < 0 )
582         {
583         perror( "setsockopt" );
584         return -1;
585         }
586     if ( bind( listen_fd, &usaP->sa, sockaddr_len( usaP ) ) < 0 )
587         {
588         perror( "bind" );
589         return -1;
590         }
591     if ( listen( listen_fd, 1024 ) < 0 )
592         {
593         perror( "listen" );
594         return -1;
595         }
596     return listen_fd;
597     }
598
599
600 /* This runs in a child process, and exits when done, so cleanup is
601 ** not needed.
602 */
603 static void
604 handle_request( void )
605     {
606     char* method_str;
607     char* line;
608     char* cp;
609
610     /* Initialize the request variables. */
611     remoteuser = (char*) 0;
612     method = -1;
613     path = (char*) 0;
614     file = (char*) 0;
615     query = "";
616     protocol = "HTTP/1.0";
617     status = 0;
618     bytes = -1;
619     req_hostname = (char*) 0;
620
621     authorization = (char*) 0;
622     content_type = (char*) 0;
623     content_length = -1;
624     cookie = (char*) 0;
625     host = (char*) 0;
626     if_modified_since = (time_t) -1;
627     referer = "";
628     useragent = "";
629
630 #ifdef USE_SSL
631     if ( do_ssl )
632         {
633         ssl = SSL_new( ssl_ctx );
634         SSL_set_fd( ssl, conn_fd );
635         if ( SSL_accept( ssl ) == 0 )
636             {
637             ERR_print_errors_fp( stderr );
638             exit( 1 );
639             }
640         }
641 #endif /* USE_SSL */
642
643     /* Read in the request. */
644     start_request();
645     for (;;)
646         {
647         char buf[10000];
648         int r = my_read( buf, sizeof(buf) );
649         if ( r <= 0 )
650             break;
651         add_to_request( buf, r );
652         if ( strstr( request, "\r\n\r\n" ) != (char*) 0 ||
653              strstr( request, "\n\n" ) != (char*) 0 )
654             break;
655         }
656
657     /* Parse the first line of the request. */
658     method_str = get_request_line();
659     path = strpbrk( method_str, " \t\n\r" );
660     if ( path == (char*) 0 )
661         send_error( 400, "Bad Request", (char*) 0, "Can't parse request." );
662     *path++ = '\0';
663     path += strspn( path, " \t\n\r" );
664     protocol = strpbrk( path, " \t\n\r" );
665     if ( protocol == (char*) 0 )
666         send_error( 400, "Bad Request", (char*) 0, "Can't parse request." );
667     *protocol++ = '\0';
668     query = strchr( path, '?' );
669     if ( query == (char*) 0 )
670         query = "";
671     else
672         *query++ = '\0';
673
674     /* Parse the rest of the request headers. */
675     while ( ( line = get_request_line() ) != (char*) 0 )
676         {
677         if ( line[0] == '\0' )
678             break;
679         else if ( strncasecmp( line, "Authorization:", 14 ) == 0 )
680             {
681             cp = &line[14];
682             cp += strspn( cp, " \t" );
683             authorization = cp;
684             }
685         else if ( strncasecmp( line, "Content-Length:", 15 ) == 0 )
686             {
687             cp = &line[15];
688             cp += strspn( cp, " \t" );
689             content_length = atol( cp );
690             }
691         else if ( strncasecmp( line, "Content-Type:", 13 ) == 0 )
692             {
693             cp = &line[13];
694             cp += strspn( cp, " \t" );
695             content_type = cp;
696             }
697         else if ( strncasecmp( line, "Cookie:", 7 ) == 0 )
698             {
699             cp = &line[7];
700             cp += strspn( cp, " \t" );
701             cookie = cp;
702             }
703         else if ( strncasecmp( line, "Host:", 5 ) == 0 )
704             {
705             cp = &line[5];
706             cp += strspn( cp, " \t" );
707             host = cp;
708             }
709         else if ( strncasecmp( line, "If-Modified-Since:", 18 ) == 0 )
710             {
711             cp = &line[18];
712             cp += strspn( cp, " \t" );
713             if_modified_since = tdate_parse( cp );
714             }
715         else if ( strncasecmp( line, "Referer:", 8 ) == 0 )
716             {
717             cp = &line[8];
718             cp += strspn( cp, " \t" );
719             referer = cp;
720             }
721         else if ( strncasecmp( line, "User-Agent:", 11 ) == 0 )
722             {
723             cp = &line[11];
724             cp += strspn( cp, " \t" );
725             useragent = cp;
726             }
727         }
728
729     if ( strcasecmp( method_str, get_method_str( METHOD_GET ) ) == 0 )
730         method = METHOD_GET;
731     else if ( strcasecmp( method_str, get_method_str( METHOD_HEAD ) ) == 0 )
732         method = METHOD_HEAD;
733     else if ( strcasecmp( method_str, get_method_str( METHOD_POST ) ) == 0 )
734         method = METHOD_POST;
735     else
736         send_error( 501, "Not Implemented", (char*) 0, "That method is not implemented." );
737
738     strdecode( path, path );
739     if ( path[0] != '/' )
740         send_error( 400, "Bad Request", (char*) 0, "Bad filename." );
741     file = &(path[1]);
742     if ( file[0] == '\0' )
743         file = "./";
744     de_dotdot( file );
745     if ( file[0] == '/' ||
746          ( file[0] == '.' && file[1] == '.' &&
747            ( file[2] == '\0' || file[2] == '/' ) ) )
748         send_error( 400, "Bad Request", (char*) 0, "Illegal filename." );
749     if ( vhost )
750         file = virtual_file( file );
751
752     if ( stat( file, &sb ) < 0 )
753         send_error( 404, "Not Found", (char*) 0, "File not found." );
754     if ( ! S_ISDIR( sb.st_mode ) )
755         do_file();
756     else
757         {
758         char idx[10000];
759         if ( file[strlen(file) - 1] != '/' )
760             {
761             char location[10000];
762             (void) snprintf( location, sizeof(location), "Location: %s/", path );
763             send_error( 302, "Found", location, "Directories must end with a slash." );
764             }
765         (void) snprintf( idx, sizeof(idx), "%sindex.html", file );
766         if ( stat( idx, &sb ) >= 0 )
767             {
768             file = idx;
769             do_file();
770             }
771         else
772             {
773             (void) snprintf( idx, sizeof(idx), "%sindex.htm", file );
774             if ( stat( idx, &sb ) >= 0 )
775                 {
776                 file = idx;
777                 do_file();
778                 }
779             else
780                 {
781                 (void) snprintf( idx, sizeof(idx), "%sindex.cgi", file );
782                 if ( stat( idx, &sb ) >= 0 )
783                     {
784                     file = idx;
785                     do_file();
786                     }
787                 else
788                     do_dir();
789                 }
790             }
791         }
792
793 #ifdef USE_SSL
794     SSL_free( ssl );
795 #endif /* USE_SSL */
796     }
797
798
799 static void
800 de_dotdot( char* file )
801     {
802     char* cp;
803     char* cp2;
804     int l;
805
806     /* Elide any xxx/../ sequences. */
807     while ( ( cp = strstr( file, "/../" ) ) != (char*) 0 )
808         {
809         for ( cp2 = cp - 1; cp2 >= file && *cp2 != '/'; --cp2 )
810             continue;
811         if ( cp2 < file )
812             break;
813         (void) strcpy( cp2, cp + 3 );
814         }
815
816     /* Also elide any xxx/.. at the end. */
817     while ( ( l = strlen( file ) ) > 3 &&
818             strcmp( ( cp = file + l - 3 ), "/.." ) == 0 )
819         {
820         for ( cp2 = cp - 1; cp2 >= file && *cp2 != '/'; --cp2 )
821             continue;
822         if ( cp2 < file )
823             break;
824         *cp2 = '\0';
825         }
826     }
827
828
829 static void
830 do_file( void )
831     {
832     char buf[10000];
833     char* type;
834     char fixed_type[500];
835     char* cp;
836     int fd;
837     void* ptr;
838
839     /* Check authorization for this directory. */
840     (void) strncpy( buf, file, sizeof(buf) );
841     cp = strrchr( buf, '/' );
842     if ( cp == (char*) 0 )
843         (void) strcpy( buf, "." );
844     else
845         *cp = '\0';
846     auth_check( buf );
847
848     /* Check if the filename is the AUTH_FILE itself - that's verboten. */
849     if ( strcmp( file, AUTH_FILE ) == 0 ||
850          ( strcmp( &(file[strlen(file) - sizeof(AUTH_FILE) + 1]), AUTH_FILE ) == 0 &&
851            file[strlen(file) - sizeof(AUTH_FILE)] == '/' ) )
852         send_error( 403, "Forbidden", (char*) 0, "File is protected." );
853
854     /* Is it CGI? */
855     if ( cgi_pattern != (char*) 0 && match( cgi_pattern, file ) )
856         {
857         do_cgi();
858         return;
859         }
860
861     fd = open( file, O_RDONLY );
862     if ( fd < 0 )
863         send_error( 403, "Forbidden", (char*) 0, "File is protected." );
864     type = get_mime_type( file );
865     (void) snprintf( fixed_type, sizeof(fixed_type), type, charset );
866     if ( if_modified_since != (time_t) -1 &&
867          if_modified_since >= sb.st_mtime )
868         {
869         add_headers(
870             304, "Not Modified", (char*) 0, fixed_type, sb.st_size,
871             sb.st_mtime );
872         send_response();
873         return;
874         }
875     add_headers( 200, "Ok", (char*) 0, fixed_type, sb.st_size, sb.st_mtime );
876     send_response();
877     if ( method == METHOD_HEAD )
878         return;
879     if ( sb.st_size > 0 )       /* avoid zero-length mmap */
880         {
881         ptr = mmap( 0, sb.st_size, PROT_READ, MAP_SHARED, fd, 0 );
882         if ( ptr != (void*) -1 )
883             {
884             (void) my_write( ptr, sb.st_size );
885             (void) munmap( ptr, sb.st_size );
886             }
887         }
888     (void) close( fd );
889     }
890
891
892 static void
893 do_dir( void )
894     {
895     char buf[10000];
896     int buflen;
897     char command[10000];
898     char* contents;
899     int contents_size, contents_len;
900     FILE* fp;
901
902     /* Check authorization for this directory. */
903     auth_check( file );
904
905     contents_size = 0;
906     buflen = snprintf( buf, sizeof(buf),
907         "<HTML><HEAD><TITLE>Index of %s</TITLE></HEAD>\n<BODY BGCOLOR=\"#99cc99\"><H4>Index of %s</H4>\n<PRE>\n",
908         file, file );
909     add_to_buf( &contents, &contents_size, &contents_len, buf, buflen );
910
911     /* Magic HTML ls command! */
912     if ( strchr( file, '\'' ) == (char*) 0 )
913         {
914         (void) snprintf(
915             command, sizeof(command),
916             "ls -lgF '%s' | tail +2 | sed -e 's/^\\([^ ][^ ]*\\)\\(  *[^ ][^ ]*  *[^ ][^ ]*  *[^ ][^ ]*\\)\\(  *[^ ][^ ]*\\)  *\\([^ ][^ ]*  *[^ ][^ ]*  *[^ ][^ ]*\\)  *\\(.*\\)$/\\1 \\3  \\4  |\\5/' -e '/ -> /!s,|\\([^*]*\\)$,|<A HREF=\"\\1\">\\1</A>,' -e '/ -> /!s,|\\(.*\\)\\([*]\\)$,|<A HREF=\"\\1\">\\1</A>\\2,' -e '/ -> /s,|\\([^@]*\\)\\(@* -> \\),|<A HREF=\"\\1\">\\1</A>\\2,' -e 's/|//'",
917             file );
918         fp = popen( command, "r" );
919         for (;;)
920             {
921             int r;
922             r = fread( buf, 1, sizeof(buf), fp );
923             if ( r <= 0 )
924                 break;
925             add_to_buf( &contents, &contents_size, &contents_len, buf, r );
926             }
927         (void) pclose( fp );
928         }
929
930     buflen = snprintf( buf, sizeof(buf),
931         "</PRE>\n<HR>\n<ADDRESS><A HREF=\"%s\">%s</A></ADDRESS>\n</BODY></HTML>\n",
932         SERVER_URL, SERVER_SOFTWARE );
933     add_to_buf( &contents, &contents_size, &contents_len, buf, buflen );
934
935     add_headers( 200, "Ok", (char*) 0, "text/html", contents_len, sb.st_mtime );
936     if ( method == METHOD_HEAD )
937         {
938         send_response();
939         return;
940         }
941     add_to_response( contents, contents_len );
942     send_response();
943     }
944
945
946 static void
947 do_cgi( void )
948     {
949     char** argp;
950     char** envp;
951     int parse_headers;
952     char* binary;
953     char* directory;
954
955     if ( method != METHOD_GET && method != METHOD_POST )
956         send_error( 501, "Not Implemented", (char*) 0, "That method is not implemented for CGI." );
957
958     /* Make the environment vector. */
959     envp = make_envp();
960
961     /* Make the argument vector. */
962     argp = make_argp();
963
964     /* Set up stdin.  For POSTs we may have to set up a pipe from an
965     ** interposer process, depending on if we've read some of the data
966     ** into our buffer.  We also have to do this for all SSL CGIs.
967     */
968 #ifdef USE_SSL
969     if ( ( method == METHOD_POST && request_len > request_idx ) || do_ssl )
970 #else /* USE_SSL */
971     if ( ( method == METHOD_POST && request_len > request_idx ) )
972 #endif /* USE_SSL */
973         {
974         int p[2];
975         int r;
976
977         if ( pipe( p ) < 0 )
978             send_error( 500, "Internal Error", (char*) 0, "Something unexpected went wrong making a pipe." );
979         r = fork();
980         if ( r < 0 )
981             send_error( 500, "Internal Error", (char*) 0, "Something unexpected went wrong forking an interposer." );
982         if ( r == 0 )
983             {
984             /* Interposer process. */
985             (void) close( p[0] );
986             cgi_interpose_input( p[1] );
987             exit( 0 );
988             }
989         (void) close( p[1] );
990         (void) dup2( p[0], STDIN_FILENO );
991         }
992     else
993         {
994         /* Otherwise, the request socket is stdin. */
995         (void) dup2( conn_fd, STDIN_FILENO );
996         }
997
998     /* Set up stdout/stderr.  For SSL, or if we're doing CGI header parsing,
999     ** we need an output interposer too.
1000     */
1001     if ( strncmp( argp[0], "nph-", 4 ) == 0 )
1002         parse_headers = 0;
1003     else
1004         parse_headers = 1;
1005 #ifdef USE_SSL
1006     if ( parse_headers || do_ssl )
1007 #else /* USE_SSL */
1008     if ( parse_headers )
1009 #endif /* USE_SSL */
1010         {
1011         int p[2];
1012         int r;
1013
1014         if ( pipe( p ) < 0 )
1015             send_error( 500, "Internal Error", (char*) 0, "Something unexpected went wrong making a pipe." );
1016         r = fork();
1017         if ( r < 0 )
1018             send_error( 500, "Internal Error", (char*) 0, "Something unexpected went wrong forking an interposer." );
1019         if ( r == 0 )
1020             {
1021             /* Interposer process. */
1022             (void) close( p[1] );
1023             cgi_interpose_output( p[0], parse_headers );
1024             exit( 0 );
1025             }
1026         (void) close( p[0] );
1027         (void) dup2( p[1], STDOUT_FILENO );
1028         (void) dup2( p[1], STDERR_FILENO );
1029         }
1030     else
1031         {
1032         /* Otherwise, the request socket is stdout/stderr. */
1033         (void) dup2( conn_fd, STDOUT_FILENO );
1034         (void) dup2( conn_fd, STDERR_FILENO );
1035         }
1036
1037     /* Set priority. */
1038     (void) nice( CGI_NICE );
1039
1040     /* Split the program into directory and binary, so we can chdir()
1041     ** to the program's own directory.  This isn't in the CGI 1.1
1042     ** spec, but it's what other HTTP servers do.
1043     */
1044     directory = strdup( file );
1045     if ( directory == (char*) 0 )
1046         binary = file;  /* ignore errors */
1047     else
1048         {
1049         binary = strrchr( directory, '/' );
1050         if ( binary == (char*) 0 )
1051             binary = file;
1052         else
1053             {
1054             *binary++ = '\0';
1055             (void) chdir( directory );  /* ignore errors */
1056             }
1057         }
1058
1059     /* Default behavior for SIGPIPE. */
1060     (void) signal( SIGPIPE, SIG_DFL );
1061
1062     /* Run the program. */
1063     (void) execve( binary, argp, envp );
1064
1065     /* Something went wrong. */
1066     send_error( 500, "Internal Error", (char*) 0, "Something unexpected went wrong running a CGI program." );
1067     }
1068
1069
1070 /* This routine is used only for POST requests.  It reads the data
1071 ** from the request and sends it to the child process.  The only reason
1072 ** we need to do it this way instead of just letting the child read
1073 ** directly is that we have already read part of the data into our
1074 ** buffer.
1075 **
1076 ** Oh, and it's also used for all SSL CGIs.
1077 */
1078 static void
1079 cgi_interpose_input( int wfd )
1080     {
1081     int c, r;
1082     char buf[1024];
1083
1084     c = request_len - request_idx;
1085     if ( c > 0 )
1086         {
1087         if ( write( wfd, &(request[request_idx]), c ) != c )
1088             return;
1089         }
1090     while ( c < content_length )
1091         {
1092         r = my_read( buf, MIN( sizeof(buf), content_length - c ) );
1093         if ( r == 0 )
1094             sleep( 1 );
1095         else if ( r < 0 )
1096             {
1097             if ( errno == EAGAIN )
1098                 sleep( 1 );
1099             else
1100                 return;
1101             }
1102         else
1103             {
1104             if ( write( wfd, buf, r ) != r )
1105                 return;
1106             c += r;
1107             }
1108         }
1109     }
1110
1111
1112 /* This routine is used for parsed-header CGIs and for all SSL CGIs. */
1113 static void
1114 cgi_interpose_output( int rfd, int parse_headers )
1115     {
1116     int r;
1117     char buf[1024];
1118
1119     if ( ! parse_headers )
1120         {
1121         /* If we're not parsing headers, write out the default status line
1122         ** and proceed to the echo phase.
1123         */
1124         char http_head[] = "HTTP/1.0 200 OK\r\n";
1125         (void) my_write( http_head, sizeof(http_head) );
1126         }
1127     else
1128         {
1129         /* Header parsing.  The idea here is that the CGI can return special
1130         ** headers such as "Status:" and "Location:" which change the return
1131         ** status of the response.  Since the return status has to be the very
1132         ** first line written out, we have to accumulate all the headers
1133         ** and check for the special ones before writing the status.  Then
1134         ** we write out the saved headers and proceed to echo the rest of
1135         ** the response.
1136         */
1137         int headers_size, headers_len;
1138         char* headers;
1139         char* br;
1140         int status;
1141         char* title;
1142         char* cp;
1143
1144         /* Slurp in all headers. */
1145         headers_size = 0;
1146         add_to_buf( &headers, &headers_size, &headers_len, (char*) 0, 0 );
1147         for (;;)
1148             {
1149             r = read( rfd, buf, sizeof(buf) );
1150             if ( r <= 0 )
1151                 {
1152                 br = &(headers[headers_len]);
1153                 break;
1154                 }
1155             add_to_buf( &headers, &headers_size, &headers_len, buf, r );
1156             if ( ( br = strstr( headers, "\r\n\r\n" ) ) != (char*) 0 ||
1157                  ( br = strstr( headers, "\n\n" ) ) != (char*) 0 )
1158                 break;
1159             }
1160
1161         /* Figure out the status. */
1162         status = 200;
1163         if ( ( cp = strstr( headers, "Status:" ) ) != (char*) 0 &&
1164              cp < br &&
1165              ( cp == headers || *(cp-1) == '\n' ) )
1166             {
1167             cp += 7;
1168             cp += strspn( cp, " \t" );
1169             status = atoi( cp );
1170             }
1171         if ( ( cp = strstr( headers, "Location:" ) ) != (char*) 0 &&
1172              cp < br &&
1173              ( cp == headers || *(cp-1) == '\n' ) )
1174             status = 302;
1175
1176         /* Write the status line. */
1177         switch ( status )
1178             {
1179             case 200: title = "OK"; break;
1180             case 302: title = "Found"; break;
1181             case 304: title = "Not Modified"; break;
1182             case 400: title = "Bad Request"; break;
1183             case 401: title = "Unauthorized"; break;
1184             case 403: title = "Forbidden"; break;
1185             case 404: title = "Not Found"; break;
1186             case 408: title = "Request Timeout"; break;
1187             case 500: title = "Internal Error"; break;
1188             case 501: title = "Not Implemented"; break;
1189             case 503: title = "Service Temporarily Overloaded"; break;
1190             default: title = "Something"; break;
1191             }
1192         (void) snprintf(
1193             buf, sizeof(buf), "HTTP/1.0 %d %s\r\n", status, title );
1194         (void) my_write( buf, strlen( buf ) );
1195
1196         /* Write the saved headers. */
1197         (void) my_write( headers, headers_len );
1198         }
1199
1200     /* Echo the rest of the output. */
1201     for (;;)
1202         {
1203         r = read( rfd, buf, sizeof(buf) );
1204         if ( r <= 0 )
1205             return;
1206         if ( my_write( buf, r ) != r )
1207             return;
1208         }
1209     }
1210
1211
1212 /* Set up CGI argument vector.  We don't have to worry about freeing
1213 ** stuff since we're a sub-process.  This gets done after make_envp() because
1214 ** we scribble on query.
1215 */
1216 static char**
1217 make_argp( void )
1218     {
1219     char** argp;
1220     int argn;
1221     char* cp1;
1222     char* cp2;
1223
1224     /* By allocating an arg slot for every character in the query, plus
1225     ** one for the filename and one for the NULL, we are guaranteed to
1226     ** have enough.  We could actually use strlen/2.
1227     */
1228     argp = (char**) malloc( ( strlen( query ) + 2 ) * sizeof(char*) );
1229     if ( argp == (char**) 0 )
1230         return (char**) 0;
1231
1232     argp[0] = strrchr( file, '/' );
1233     if ( argp[0] != (char*) 0 )
1234         ++argp[0];
1235     else
1236         argp[0] = file;
1237
1238     argn = 1;
1239     /* According to the CGI spec at http://hoohoo.ncsa.uiuc.edu/cgi/cl.html,
1240     ** "The server should search the query information for a non-encoded =
1241     ** character to determine if the command line is to be used, if it finds
1242     ** one, the command line is not to be used."
1243     */
1244     if ( strchr( query, '=' ) == (char*) 0 )
1245         {
1246         for ( cp1 = cp2 = query; *cp2 != '\0'; ++cp2 )
1247             {
1248             if ( *cp2 == '+' )
1249                 {
1250                 *cp2 = '\0';
1251                 strdecode( cp1, cp1 );
1252                 argp[argn++] = cp1;
1253                 cp1 = cp2 + 1;
1254                 }
1255             }
1256         if ( cp2 != cp1 )
1257             {
1258             strdecode( cp1, cp1 );
1259             argp[argn++] = cp1;
1260             }
1261         }
1262
1263     argp[argn] = (char*) 0;
1264     return argp;
1265     }
1266
1267
1268 /* Set up CGI environment variables. Be real careful here to avoid
1269 ** letting malicious clients overrun a buffer.  We don't have
1270 ** to worry about freeing stuff since we're a sub-process.
1271 */
1272 static char**
1273 make_envp( void )
1274     {
1275     static char* envp[50];
1276     int envn;
1277     char* cp;
1278     char buf[256];
1279
1280     envn = 0;
1281     envp[envn++] = build_env( "PATH=%s", CGI_PATH );
1282     envp[envn++] = build_env( "LD_LIBRARY_PATH=%s", CGI_LD_LIBRARY_PATH );
1283     envp[envn++] = build_env( "SERVER_SOFTWARE=%s", SERVER_SOFTWARE );
1284     if ( ! vhost )
1285         cp = hostname;
1286     else
1287         cp = req_hostname;      /* already computed by virtual_file() */
1288     if ( cp != (char*) 0 )
1289         envp[envn++] = build_env( "SERVER_NAME=%s", cp );
1290     envp[envn++] = "GATEWAY_INTERFACE=CGI/1.1";
1291     envp[envn++] = "SERVER_PROTOCOL=HTTP/1.0";
1292     (void) snprintf( buf, sizeof(buf), "%d", port );
1293     envp[envn++] = build_env( "SERVER_PORT=%s", buf );
1294     envp[envn++] = build_env(
1295         "REQUEST_METHOD=%s", get_method_str( method ) );
1296     envp[envn++] = build_env( "SCRIPT_NAME=%s", path );
1297     if ( query[0] != '\0' )
1298         envp[envn++] = build_env( "QUERY_STRING=%s", query );
1299     envp[envn++] = build_env( "REMOTE_ADDR=%s", ntoa( &client_addr ) );
1300     if ( referer[0] != '\0' )
1301         envp[envn++] = build_env( "HTTP_REFERER=%s", referer );
1302     if ( useragent[0] != '\0' )
1303         envp[envn++] = build_env( "HTTP_USER_AGENT=%s", useragent );
1304     if ( cookie != (char*) 0 )
1305         envp[envn++] = build_env( "HTTP_COOKIE=%s", cookie );
1306     if ( content_type != (char*) 0 )
1307         envp[envn++] = build_env( "CONTENT_TYPE=%s", content_type );
1308     if ( content_length != -1 )
1309         {
1310         (void) snprintf( buf, sizeof(buf), "%ld", content_length );
1311         envp[envn++] = build_env( "CONTENT_LENGTH=%s", buf );
1312         }
1313     if ( remoteuser != (char*) 0 )
1314         envp[envn++] = build_env( "REMOTE_USER=%s", remoteuser );
1315     if ( authorization != (char*) 0 )
1316         envp[envn++] = build_env( "AUTH_TYPE=%s", "Basic" );
1317     if ( getenv( "TZ" ) != (char*) 0 )
1318         envp[envn++] = build_env( "TZ=%s", getenv( "TZ" ) );
1319
1320     envp[envn] = (char*) 0;
1321     return envp;
1322     }
1323
1324
1325 static char*
1326 build_env( char* fmt, char* arg )
1327     {
1328     char* cp;
1329     int size;
1330     static char* buf;
1331     static int maxbuf = 0;
1332
1333     size = strlen( fmt ) + strlen( arg );
1334     if ( size > maxbuf )
1335         {
1336         if ( maxbuf == 0 )
1337             {
1338             maxbuf = 256;
1339             buf = (char*) malloc( maxbuf );
1340             }
1341         else
1342             {
1343             maxbuf *= 2;
1344             buf = (char*) realloc( (void*) buf, maxbuf );
1345             }
1346         if ( buf == (char*) 0 )
1347             {
1348             (void) fprintf( stderr, "%s: out of memory\n", argv0 );
1349             exit( 1 );
1350             }
1351         }
1352     (void) snprintf( buf, maxbuf, fmt, arg );
1353     cp = strdup( buf );
1354     if ( cp == (char*) 0 )
1355         {
1356         (void) fprintf( stderr, "%s: out of memory\n", argv0 );
1357         exit( 1 );
1358         }
1359     return cp;
1360     }
1361
1362
1363 static void
1364 auth_check( char* dirname )
1365     {
1366     char authpath[10000];
1367     struct stat sb;
1368     char authinfo[500];
1369     char* authpass;
1370     static char line[10000];
1371     int l;
1372     FILE* fp;
1373     char* cryp;
1374
1375     /* Construct auth filename. */
1376     if ( dirname[strlen(dirname) - 1] == '/' )
1377         (void) snprintf( authpath, sizeof(authpath), "%s%s", dirname, AUTH_FILE );
1378     else
1379         (void) snprintf( authpath, sizeof(authpath), "%s/%s", dirname, AUTH_FILE );
1380
1381     /* Does this directory have an auth file? */
1382     if ( stat( authpath, &sb ) < 0 )
1383         /* Nope, let the request go through. */
1384         return;
1385
1386     /* Does this request contain authorization info? */
1387     if ( authorization == (char*) 0 )
1388         /* Nope, return a 401 Unauthorized. */
1389         send_authenticate( dirname );
1390
1391     /* Basic authorization info? */
1392     if ( strncmp( authorization, "Basic ", 6 ) != 0 )
1393         send_authenticate( dirname );
1394
1395     /* Decode it. */
1396     l = b64_decode( &(authorization[6]), authinfo, sizeof(authinfo) );
1397     authinfo[l] = '\0';
1398     /* Split into user and password. */
1399     authpass = strchr( authinfo, ':' );
1400     if ( authpass == (char*) 0 )
1401         /* No colon?  Bogus auth info. */
1402         send_authenticate( dirname );
1403     *authpass++ = '\0';
1404
1405     /* Open the password file. */
1406     fp = fopen( authpath, "r" );
1407     if ( fp == (FILE*) 0 )
1408         /* The file exists but we can't open it?  Disallow access. */
1409         send_error( 403, "Forbidden", (char*) 0, "File is protected." );
1410
1411     /* Read it. */
1412     while ( fgets( line, sizeof(line), fp ) != (char*) 0 )
1413         {
1414         /* Nuke newline. */
1415         l = strlen( line );
1416         if ( line[l - 1] == '\n' )
1417             line[l - 1] = '\0';
1418         /* Split into user and encrypted password. */
1419         cryp = strchr( line, ':' );
1420         if ( cryp == (char*) 0 )
1421             continue;
1422         *cryp++ = '\0';
1423         /* Is this the right user? */
1424         if ( strcmp( line, authinfo ) == 0 )
1425             {
1426             /* Yes. */
1427             (void) fclose( fp );
1428             /* So is the password right? */
1429             if ( strcmp( crypt( authpass, cryp ), cryp ) == 0 )
1430                 {
1431                 /* Ok! */
1432                 remoteuser = line;
1433                 return;
1434                 }
1435             else
1436                 /* No. */
1437                 send_authenticate( dirname );
1438             }
1439         }
1440
1441     /* Didn't find that user.  Access denied. */
1442     (void) fclose( fp );
1443     send_authenticate( dirname );
1444     }
1445
1446
1447 static void
1448 send_authenticate( char* realm )
1449     {
1450     char header[10000];
1451
1452     (void) snprintf(
1453         header, sizeof(header), "WWW-Authenticate: Basic realm=\"%s\"", realm );
1454     send_error( 401, "Unauthorized", header, "Authorization required." );
1455     }
1456
1457
1458 static char*
1459 virtual_file( char* file )
1460     {
1461     char* cp;
1462     static char vfile[10000];
1463
1464     /* Use the request's hostname, or fall back on the IP address. */
1465     if ( host != (char*) 0 )
1466         req_hostname = host;
1467     else
1468         {
1469         usockaddr usa;
1470         int sz = sizeof(usa);
1471         if ( getsockname( conn_fd, &usa.sa, &sz ) < 0 )
1472             req_hostname = "UNKNOWN_HOST";
1473         else
1474             req_hostname = ntoa( &usa );
1475         }
1476     /* Pound it to lower case. */
1477     for ( cp = req_hostname; *cp != '\0'; ++cp )
1478         if ( isupper( *cp ) )
1479             *cp = tolower( *cp );
1480     (void) snprintf( vfile, sizeof(vfile), "%s/%s", req_hostname, file );
1481     return vfile;
1482     }
1483
1484
1485 static void
1486 send_error( int s, char* title, char* extra_header, char* text )
1487     {
1488     add_headers( s, title, extra_header, "text/html", -1, -1 );
1489
1490     send_error_body( s, title, text );
1491
1492     send_error_tail();
1493
1494     send_response();
1495
1496 #ifdef USE_SSL
1497     SSL_free( ssl );
1498 #endif /* USE_SSL */
1499     exit( 1 );
1500     }
1501
1502
1503 static void
1504 send_error_body( int s, char* title, char* text )
1505     {
1506     char filename[1000];
1507     char buf[10000];
1508     int buflen;
1509
1510     if ( vhost && req_hostname != (char*) 0 )
1511         {
1512         /* Try virtual-host custom error page. */
1513         (void) snprintf(
1514             filename, sizeof(filename), "%s/%s/err%d.html",
1515             req_hostname, ERR_DIR, s );
1516         if ( send_error_file( filename ) )
1517             return;
1518         }
1519
1520     /* Try server-wide custom error page. */
1521     (void) snprintf(
1522         filename, sizeof(filename), "%s/err%d.html", ERR_DIR, s );
1523     if ( send_error_file( filename ) )
1524         return;
1525
1526     /* Send built-in error page. */
1527     buflen = snprintf(
1528         buf, sizeof(buf),
1529         "<HTML><HEAD><TITLE>%d %s</TITLE></HEAD>\n<BODY BGCOLOR=\"#cc9999\"><H4>%d %s</H4>\n",
1530         s, title, s, title );
1531     add_to_response( buf, buflen );
1532     buflen = snprintf( buf, sizeof(buf), "%s\n", text );
1533     add_to_response( buf, buflen );
1534     }
1535
1536
1537 static int
1538 send_error_file( char* filename )
1539     {
1540     FILE* fp;
1541     char buf[1000];
1542     int r;
1543
1544     fp = fopen( filename, "r" );
1545     if ( fp == (FILE*) 0 )
1546         return 0;
1547     for (;;)
1548         {
1549         r = fread( buf, 1, sizeof(buf), fp );
1550         if ( r <= 0 )
1551             break;
1552         add_to_response( buf, r );
1553         }
1554     (void) fclose( fp );
1555     return 1;
1556     }
1557
1558
1559 static void
1560 send_error_tail( void )
1561     {
1562     char buf[500];
1563     int buflen;
1564
1565     if ( match( "**MSIE**", useragent ) )
1566         {
1567         int n;
1568         buflen = snprintf( buf, sizeof(buf), "<!--\n" );
1569         add_to_response( buf, buflen );
1570         for ( n = 0; n < 6; ++n )
1571             {
1572             buflen = snprintf( buf, sizeof(buf), "Padding so that MSIE deigns to show this error instead of its own canned one.\n" );
1573             add_to_response( buf, buflen );
1574             }
1575         buflen = snprintf( buf, sizeof(buf), "-->\n" );
1576         add_to_response( buf, buflen );
1577         }
1578
1579     buflen = snprintf( buf, sizeof(buf), "<HR>\n<ADDRESS><A HREF=\"%s\">%s</A></ADDRESS>\n</BODY></HTML>\n", SERVER_URL, SERVER_SOFTWARE );
1580     add_to_response( buf, buflen );
1581     }
1582
1583
1584 static void
1585 add_headers( int s, char* title, char* extra_header, char* mime_type, long b, time_t mod )
1586     {
1587     time_t now;
1588     char timebuf[100];
1589     char buf[10000];
1590     int buflen;
1591     const char* rfc1123_fmt = "%a, %d %b %Y %H:%M:%S GMT";
1592
1593     status = s;
1594     bytes = b;
1595     make_log_entry();
1596     start_response();
1597     buflen = snprintf( buf, sizeof(buf), "%s %d %s\r\n", protocol, status, title );
1598     add_to_response( buf, buflen );
1599     buflen = snprintf( buf, sizeof(buf), "Server: %s\r\n", SERVER_SOFTWARE );
1600     add_to_response( buf, buflen );
1601     now = time( (time_t*) 0 );
1602     (void) strftime( timebuf, sizeof(timebuf), rfc1123_fmt, gmtime( &now ) );
1603     buflen = snprintf( buf, sizeof(buf), "Date: %s\r\n", timebuf );
1604     add_to_response( buf, buflen );
1605     if ( extra_header != (char*) 0 )
1606         {
1607         buflen = snprintf( buf, sizeof(buf), "%s\r\n", extra_header );
1608         add_to_response( buf, buflen );
1609         }
1610     if ( mime_type != (char*) 0 )
1611         {
1612         buflen = snprintf( buf, sizeof(buf), "Content-type: %s\r\n", mime_type );
1613         add_to_response( buf, buflen );
1614         }
1615     if ( bytes >= 0 )
1616         {
1617         buflen = snprintf( buf, sizeof(buf), "Content-length: %ld\r\n", bytes );
1618         add_to_response( buf, buflen );
1619         }
1620     if ( mod != (time_t) -1 )
1621         {
1622         (void) strftime( timebuf, sizeof(timebuf), rfc1123_fmt, gmtime( &mod ) );
1623         buflen = snprintf( buf, sizeof(buf), "Last-modified: %s\r\n", timebuf );
1624         add_to_response( buf, buflen );
1625         }
1626     buflen = snprintf( buf, sizeof(buf), "Connection: close\r\n\r\n" );
1627     add_to_response( buf, buflen );
1628     }
1629
1630
1631 static void
1632 start_request( void )
1633     {
1634     request_size = 0;
1635     request_idx = 0;
1636     }
1637
1638 static void
1639 add_to_request( char* str, int len )
1640     {
1641     add_to_buf( &request, &request_size, &request_len, str, len );
1642     }
1643
1644 static char*
1645 get_request_line( void )
1646     {
1647     int i;
1648     char c;
1649
1650     for ( i = request_idx; request_idx < request_len; ++request_idx )
1651         {
1652         c = request[request_idx];
1653         if ( c == '\n' || c == '\r' )
1654             {
1655             request[request_idx] = '\0';
1656             ++request_idx;
1657             if ( c == '\r' && request_idx < request_len &&
1658                  request[request_idx] == '\n' )
1659                 {
1660                 request[request_idx] = '\0';
1661                 ++request_idx;
1662                 }
1663             return &(request[i]);
1664             }
1665         }
1666     return (char*) 0;
1667     }
1668
1669
1670 static char* response;
1671 static int response_size, response_len;
1672
1673 static void
1674 start_response( void )
1675     {
1676     response_size = 0;
1677     }
1678
1679 static void
1680 add_to_response( char* str, int len )
1681     {
1682     add_to_buf( &response, &response_size, &response_len, str, len );
1683     }
1684
1685 static void
1686 send_response( void )
1687     {
1688     (void) my_write( response, response_len );
1689     }
1690
1691
1692 static int
1693 my_read( char* buf, int size )
1694     {
1695 #ifdef USE_SSL
1696     if ( do_ssl )
1697         return SSL_read( ssl, buf, size );
1698     else
1699         return read( conn_fd, buf, size );
1700 #else /* USE_SSL */
1701     return read( conn_fd, buf, size );
1702 #endif /* USE_SSL */
1703     }
1704
1705
1706 static int
1707 my_write( char* buf, int size )
1708     {
1709 #ifdef USE_SSL
1710     if ( do_ssl )
1711         return SSL_write( ssl, buf, size );
1712     else
1713         return write( conn_fd, buf, size );
1714 #else /* USE_SSL */
1715     return write( conn_fd, buf, size );
1716 #endif /* USE_SSL */
1717     }
1718
1719
1720 static void
1721 add_to_buf( char** bufP, int* bufsizeP, int* buflenP, char* str, int len )
1722     {
1723     if ( *bufsizeP == 0 )
1724         {
1725         *bufsizeP = len + 500;
1726         *buflenP = 0;
1727         *bufP = (char*) malloc( *bufsizeP );
1728         }
1729     else if ( *buflenP + len >= *bufsizeP )
1730         {
1731         *bufsizeP = *buflenP + len + 500;
1732         *bufP = (char*) realloc( (void*) *bufP, *bufsizeP );
1733         }
1734     if ( *bufP == (char*) 0 )
1735         {
1736         (void) fprintf( stderr, "%s: out of memory\n", argv0 );
1737         exit( 1 );
1738         }
1739     (void) memcpy( &((*bufP)[*buflenP]), str, len );
1740     *buflenP += len;
1741     (*bufP)[*buflenP] = '\0';
1742     }
1743
1744
1745 static void
1746 make_log_entry( void )
1747     {
1748     char* ru;
1749     char url[500];
1750     char bytes_str[40];
1751     time_t now;
1752     struct tm* t;
1753     const char* cernfmt_nozone = "%d/%b/%Y:%H:%M:%S";
1754     char date_nozone[100];
1755     int zone;
1756     char sign;
1757     char date[100];
1758
1759     if ( logfp == (FILE*) 0 )
1760         return;
1761
1762     /* Format the user. */
1763     if ( remoteuser != (char*) 0 )
1764         ru = remoteuser;
1765     else
1766         ru = "-";
1767     now = time( (time_t*) 0 );
1768     /* If we're vhosting, prepend the hostname to the url.  This is
1769     ** a little weird, perhaps writing separate log files for
1770     ** each vhost would make more sense.
1771     */
1772     if ( vhost )
1773         (void) snprintf( url, sizeof(url), "/%s%s", req_hostname, path );
1774     else
1775         (void) snprintf( url, sizeof(url), "%s", path );
1776     /* Format the bytes. */
1777     if ( bytes >= 0 )
1778         (void) snprintf( bytes_str, sizeof(bytes_str), "%ld", bytes );
1779     else
1780         (void) strcpy( bytes_str, "-" );
1781     /* Format the time, forcing a numeric timezone (some log analyzers
1782     ** are stoooopid about this).
1783     */
1784     t = localtime( &now );
1785     (void) strftime( date_nozone, sizeof(date_nozone), cernfmt_nozone, t );
1786 #ifdef HAVE_TM_GMTOFF
1787     zone = t->tm_gmtoff / 60L;
1788 #else
1789     zone = - ( timezone / 60L );
1790     /* Probably have to add something about daylight time here. */
1791 #endif
1792     if ( zone >= 0 )
1793         sign = '+';
1794     else
1795         {
1796         sign = '-';
1797         zone = -zone;
1798         }
1799     zone = ( zone / 60 ) * 100 + zone % 60;
1800     (void) snprintf( date, sizeof(date), "%s %c%04d", date_nozone, sign, zone );
1801     /* And write the log entry. */
1802     (void) fprintf( logfp,
1803         "%.80s - %.80s [%s] \"%.80s %.200s %.80s\" %d %s \"%.200s\" \"%.80s\"\n",
1804         ntoa( &client_addr ), ru, date, get_method_str( method ), url,
1805         protocol, status, bytes_str, referer, useragent );
1806     (void) fflush( logfp );
1807     }
1808
1809
1810 static char*
1811 get_method_str( int m )
1812     {
1813     switch ( m )
1814         {
1815         case METHOD_GET: return "GET";
1816         case METHOD_HEAD: return "HEAD";
1817         case METHOD_POST: return "POST";
1818         default: return (char*) 0;
1819         }
1820     }
1821
1822
1823 static char*
1824 get_mime_type( char* name )
1825     {
1826     struct {
1827         char* ext;
1828         char* type;
1829         } table[] = {
1830 #include "mime_types.h"
1831         };
1832     int fl = strlen( name );
1833     int i;
1834
1835     for ( i = 0; i < sizeof(table) / sizeof(*table); ++i )
1836         {
1837         int el = strlen( table[i].ext );
1838         if ( strcasecmp( &(name[fl - el]), table[i].ext ) == 0 )
1839             return table[i].type;
1840         }
1841     return "text/plain; charset=%s";
1842     }
1843
1844
1845 static void
1846 handle_sigterm( int sig )
1847     {
1848     (void) fprintf( stderr, "%s: exiting due to signal %d\n", argv0, sig );
1849     exit( 1 );
1850     }
1851
1852
1853 static void
1854 handle_sigchld( int sig )
1855     {
1856     pid_t pid;
1857     int status;
1858
1859     /* Reap defunct children until there aren't any more. */
1860     for (;;)
1861         {
1862 #ifdef HAVE_WAITPID
1863         pid = waitpid( (pid_t) -1, &status, WNOHANG );
1864 #else /* HAVE_WAITPID */
1865         pid = wait3( &status, WNOHANG, (struct rusage*) 0 );
1866 #endif /* HAVE_WAITPID */
1867         if ( (int) pid == 0 )           /* none left */
1868             break;
1869         if ( (int) pid < 0 )
1870             {
1871             if ( errno == EINTR )       /* because of ptrace */
1872                 continue;
1873             /* ECHILD shouldn't happen with the WNOHANG option,
1874             ** but with some kernels it does anyway.  Ignore it.
1875             */
1876             if ( errno != ECHILD )
1877                 perror( "child wait" );
1878             break;
1879             }
1880         }
1881     }
1882
1883
1884 static void
1885 lookup_hostname( usockaddr* usa4P, size_t sa4_len, int* gotv4P, usockaddr* usa6P, size_t sa6_len, int* gotv6P )
1886     {
1887 #if defined(HAVE_GETADDRINFO) && defined(HAVE_GAI_STRERROR)
1888
1889     struct addrinfo hints;
1890     struct addrinfo* ai;
1891     struct addrinfo* ai2;
1892     struct addrinfo* aiv4;
1893     struct addrinfo* aiv6;
1894     int gaierr;
1895     char strport[10];
1896
1897     memset( &hints, 0, sizeof(hints) );
1898     hints.ai_family = AF_UNSPEC;
1899     hints.ai_flags = AI_PASSIVE;
1900     hints.ai_socktype = SOCK_STREAM;
1901     (void) snprintf( strport, sizeof(strport), "%d", port );
1902     if ( (gaierr = getaddrinfo( hostname, strport, &hints, &ai )) != 0 )
1903         {
1904         (void) fprintf( stderr, "getaddrinfo %.80s - %s\n", hostname, gai_strerror( gaierr ) );
1905         exit( 1 );
1906         }
1907
1908     /* Find the first IPv4 and IPv6 entries. */
1909     aiv4 = (struct addrinfo*) 0;
1910     aiv6 = (struct addrinfo*) 0;
1911     for ( ai2 = ai; ai2 != (struct addrinfo*) 0; ai2 = ai2->ai_next )
1912         {
1913         switch ( ai2->ai_family )
1914             {
1915             case AF_INET:
1916             if ( aiv4 == (struct addrinfo*) 0 )
1917                 aiv4 = ai2;
1918             break;
1919 #if defined(AF_INET6) && defined(HAVE_SOCKADDR_IN6)
1920             case AF_INET6:
1921             if ( aiv6 == (struct addrinfo*) 0 )
1922                 aiv6 = ai2;
1923             break;
1924 #endif /* AF_INET6 && HAVE_SOCKADDR_IN6 */
1925             }
1926         }
1927
1928     if ( aiv4 == (struct addrinfo*) 0 )
1929         *gotv4P = 0;
1930     else
1931         {
1932         if ( sa4_len < aiv4->ai_addrlen )
1933             {
1934             (void) fprintf(
1935                 stderr, "%.80s - sockaddr too small (%d < %d)\n",
1936                 hostname, sa4_len, aiv4->ai_addrlen );
1937             exit( 1 );
1938             }
1939         memset( usa4P, 0, sa4_len );
1940         memcpy( usa4P, aiv4->ai_addr, aiv4->ai_addrlen );
1941         *gotv4P = 1;
1942         }
1943     if ( aiv6 == (struct addrinfo*) 0 )
1944         *gotv6P = 0;
1945     else
1946         {
1947         if ( sa6_len < aiv6->ai_addrlen )
1948             {
1949             (void) fprintf(
1950                 stderr, "%.80s - sockaddr too small (%d < %d)\n",
1951                 hostname, sa6_len, aiv6->ai_addrlen );
1952             exit( 1 );
1953             }
1954         memset( usa6P, 0, sa6_len );
1955         memcpy( usa6P, aiv6->ai_addr, aiv6->ai_addrlen );
1956         *gotv6P = 1;
1957         }
1958
1959     freeaddrinfo( ai );
1960
1961 #else /* HAVE_GETADDRINFO && HAVE_GAI_STRERROR */
1962
1963     struct hostent* he;
1964
1965     *gotv6P = 0;
1966
1967     memset( usa4P, 0, sa4_len );
1968     usa4P->sa.sa_family = AF_INET;
1969     if ( hostname == (char*) 0 )
1970         usa4P->sa_in.sin_addr.s_addr = htonl( INADDR_ANY );
1971     else
1972         {
1973         usa4P->sa_in.sin_addr.s_addr = inet_addr( hostname );
1974         if ( (int) usa4P->sa_in.sin_addr.s_addr == -1 )
1975             {
1976             he = gethostbyname( hostname );
1977             if ( he == (struct hostent*) 0 )
1978                 {
1979 #ifdef HAVE_HSTRERROR
1980                 (void) fprintf( stderr, "gethostbyname %.80s - %s\n", hostname, hstrerror( h_errno ) );
1981 #else /* HAVE_HSTRERROR */
1982                 (void) fprintf( stderr, "gethostbyname %.80s failed\n", hostname );
1983 #endif /* HAVE_HSTRERROR */
1984                 exit( 1 );
1985                 }
1986             if ( he->h_addrtype != AF_INET )
1987                 {
1988                 (void) fprintf( stderr, "%.80s - non-IP network address\n", hostname );
1989                 exit( 1 );
1990                 }
1991             (void) memcpy(
1992                 &usa4P->sa_in.sin_addr.s_addr, he->h_addr, he->h_length );
1993             }
1994         }
1995     usa4P->sa_in.sin_port = htons( port );
1996     *gotv4P = 1;
1997
1998 #endif /* HAVE_GETADDRINFO && HAVE_GAI_STRERROR */
1999     }
2000
2001
2002 static char*
2003 ntoa( usockaddr* usaP )
2004     {
2005 #ifdef HAVE_GETNAMEINFO
2006     static char str[200];
2007
2008     if ( getnameinfo( &usaP->sa, sockaddr_len( usaP ), str, sizeof(str), 0, 0, NI_NUMERICHOST ) != 0 )
2009         {
2010         str[0] = '?';
2011         str[1] = '\0';
2012         }
2013     return str;
2014
2015 #else /* HAVE_GETNAMEINFO */
2016
2017     return inet_ntoa( usaP->sa_in.sin_addr );
2018
2019 #endif /* HAVE_GETNAMEINFO */
2020     }
2021
2022
2023 static size_t
2024 sockaddr_len( usockaddr* usaP )
2025     {
2026     switch ( usaP->sa.sa_family )
2027         {
2028         case AF_INET: return sizeof(struct sockaddr_in);
2029 #if defined(AF_INET6) && defined(HAVE_SOCKADDR_IN6)
2030         case AF_INET6: return sizeof(struct sockaddr_in6);
2031 #endif /* AF_INET6 && HAVE_SOCKADDR_IN6 */
2032         default:
2033         (void) fprintf(
2034             stderr, "unknown sockaddr family - %d\n", usaP->sa.sa_family );
2035         exit( 1 );
2036         }
2037     }
2038
2039
2040 /* Copies and decodes a string.  It's ok for from and to to be the
2041 ** same string.
2042 */
2043 static void
2044 strdecode( char* to, char* from )
2045     {
2046     for ( ; *from != '\0'; ++to, ++from )
2047         {
2048         if ( from[0] == '%' && isxdigit( from[1] ) && isxdigit( from[2] ) )
2049             {
2050             *to = hexit( from[1] ) * 16 + hexit( from[2] );
2051             from += 2;
2052             }
2053         else
2054             *to = *from;
2055         }
2056     *to = '\0';
2057     }
2058
2059
2060 static int
2061 hexit( char c )
2062     {
2063     if ( c >= '0' && c <= '9' )
2064         return c - '0';
2065     if ( c >= 'a' && c <= 'f' )
2066         return c - 'a' + 10;
2067     if ( c >= 'A' && c <= 'F' )
2068         return c - 'A' + 10;
2069     return 0;           /* shouldn't happen, we're guarded by isxdigit() */
2070     }
2071
2072
2073 /* Base-64 decoding.  This represents binary data as printable ASCII
2074 ** characters.  Three 8-bit binary bytes are turned into four 6-bit
2075 ** values, like so:
2076 **
2077 **   [11111111]  [22222222]  [33333333]
2078 **
2079 **   [111111] [112222] [222233] [333333]
2080 **
2081 ** Then the 6-bit values are represented using the characters "A-Za-z0-9+/".
2082 */
2083
2084 static int b64_decode_table[256] = {
2085     -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* 00-0F */
2086     -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* 10-1F */
2087     -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63,  /* 20-2F */
2088     52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1,  /* 30-3F */
2089     -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,  /* 40-4F */
2090     15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,  /* 50-5F */
2091     -1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,  /* 60-6F */
2092     41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1,  /* 70-7F */
2093     -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* 80-8F */
2094     -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* 90-9F */
2095     -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* A0-AF */
2096     -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* B0-BF */
2097     -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* C0-CF */
2098     -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* D0-DF */
2099     -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* E0-EF */
2100     -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1   /* F0-FF */
2101     };
2102
2103 /* Do base-64 decoding on a string.  Ignore any non-base64 bytes.
2104 ** Return the actual number of bytes generated.  The decoded size will
2105 ** be at most 3/4 the size of the encoded, and may be smaller if there
2106 ** are padding characters (blanks, newlines).
2107 */
2108 static int
2109 b64_decode( const char* str, unsigned char* space, int size )
2110     {
2111     const char* cp;
2112     int space_idx, phase;
2113     int d, prev_d;
2114     unsigned char c;
2115
2116     space_idx = 0;
2117     phase = 0;
2118     for ( cp = str; *cp != '\0'; ++cp )
2119         {
2120         d = b64_decode_table[*cp];
2121         if ( d != -1 )
2122             {
2123             switch ( phase )
2124                 {
2125                 case 0:
2126                 ++phase;
2127                 break;
2128                 case 1:
2129                 c = ( ( prev_d << 2 ) | ( ( d & 0x30 ) >> 4 ) );
2130                 if ( space_idx < size )
2131                     space[space_idx++] = c;
2132                 ++phase;
2133                 break;
2134                 case 2:
2135                 c = ( ( ( prev_d & 0xf ) << 4 ) | ( ( d & 0x3c ) >> 2 ) );
2136                 if ( space_idx < size )
2137                     space[space_idx++] = c;
2138                 ++phase;
2139                 break;
2140                 case 3:
2141                 c = ( ( ( prev_d & 0x03 ) << 6 ) | d );
2142                 if ( space_idx < size )
2143                     space[space_idx++] = c;
2144                 phase = 0;
2145                 break;
2146                 }
2147             prev_d = d;
2148             }
2149         }
2150     return space_idx;
2151     }
2152
2153
2154 /* Simple shell-style filename matcher.  Only does ? * and **, and multiple
2155 ** patterns separated by |.  Returns 1 or 0.
2156 */
2157 int
2158 match( const char* pattern, const char* string )
2159     {
2160     const char* or;
2161
2162     for (;;)
2163         {
2164         or = strchr( pattern, '|' );
2165         if ( or == (char*) 0 )
2166             return match_one( pattern, strlen( pattern ), string );
2167         if ( match_one( pattern, or - pattern, string ) )
2168             return 1;
2169         pattern = or + 1;
2170         }
2171     }
2172
2173
2174 static int
2175 match_one( const char* pattern, int patternlen, const char* string )
2176     {
2177     const char* p;
2178
2179     for ( p = pattern; p - pattern < patternlen; ++p, ++string )
2180         {
2181         if ( *p == '?' && *string != '\0' )
2182             continue;
2183         if ( *p == '*' )
2184             {
2185             int i, pl;
2186             ++p;
2187             if ( *p == '*' )
2188                 {
2189                 /* Double-wildcard matches anything. */
2190                 ++p;
2191                 i = strlen( string );
2192                 }
2193             else
2194                 /* Single-wildcard matches anything but slash. */
2195                 i = strcspn( string, "/" );
2196             pl = patternlen - ( p - pattern );
2197             for ( ; i >= 0; --i )
2198                 if ( match_one( p, pl, &(string[i]) ) )
2199                     return 1;
2200             return 0;
2201             }
2202         if ( *p != *string )
2203             return 0;
2204         }
2205     if ( *string == '\0' )
2206         return 1;
2207     return 0;
2208     }