initial capture of my stuff
[rrq/thttpd.git] / libhttpd.c
1 /* libhttpd.c - HTTP protocol library
2 **
3 ** Copyright © 1995,1998,1999,2000,2001,2015 by
4 ** Jef Poskanzer <jef@mail.acme.com>. 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 "config.h"
30 #include "version.h"
31
32 #ifdef SHOW_SERVER_VERSION
33 #define EXPOSED_SERVER_SOFTWARE SERVER_SOFTWARE
34 #else /* SHOW_SERVER_VERSION */
35 #define EXPOSED_SERVER_SOFTWARE "thttpd"
36 #endif /* SHOW_SERVER_VERSION */
37
38 #include <sys/types.h>
39 #include <sys/param.h>
40 #include <sys/stat.h>
41
42 #include <ctype.h>
43 #include <errno.h>
44 #include <fcntl.h>
45 #include <time.h>
46 #ifdef HAVE_MEMORY_H
47 #include <memory.h>
48 #endif /* HAVE_MEMORY_H */
49 #include <pwd.h>
50 #include <signal.h>
51 #include <stdio.h>
52 #include <stdlib.h>
53 #include <string.h>
54 #include <syslog.h>
55 #include <unistd.h>
56 #include <stdarg.h>
57
58 #ifdef HAVE_OSRELDATE_H
59 #include <osreldate.h>
60 #endif /* HAVE_OSRELDATE_H */
61
62 #ifdef HAVE_DIRENT_H
63 # include <dirent.h>
64 # define NAMLEN(dirent) strlen((dirent)->d_name)
65 #else
66 # define dirent direct
67 # define NAMLEN(dirent) (dirent)->d_namlen
68 # ifdef HAVE_SYS_NDIR_H
69 #  include <sys/ndir.h>
70 # endif
71 # ifdef HAVE_SYS_DIR_H
72 #  include <sys/dir.h>
73 # endif
74 # ifdef HAVE_NDIR_H
75 #  include <ndir.h>
76 # endif
77 #endif
78
79 #include "libhttpd.h"
80 #include "mmc.h"
81 #include "timers.h"
82 #include "match.h"
83 #include "tdate_parse.h"
84
85 #ifndef STDIN_FILENO
86 #define STDIN_FILENO 0
87 #endif
88 #ifndef STDOUT_FILENO
89 #define STDOUT_FILENO 1
90 #endif
91 #ifndef STDERR_FILENO
92 #define STDERR_FILENO 2
93 #endif
94
95 #ifndef SHUT_WR
96 #define SHUT_WR 1
97 #endif
98
99 #ifndef HAVE_INT64T
100 typedef long long int64_t;
101 #endif
102
103 #ifndef HAVE_SOCKLENT
104 typedef int socklen_t;
105 #endif
106
107 #ifdef __CYGWIN__
108 #define timezone  _timezone
109 #endif
110
111 #ifndef MAX
112 #define MAX(a,b) ((a) > (b) ? (a) : (b))
113 #endif
114 #ifndef MIN
115 #define MIN(a,b) ((a) < (b) ? (a) : (b))
116 #endif
117
118
119 /* Forwards. */
120 static void check_options( void );
121 static void free_httpd_server( httpd_server* hs );
122 static int initialize_listen_socket( httpd_sockaddr* saP );
123 static void add_response( httpd_conn* hc, char* str );
124 static void send_mime( httpd_conn* hc, int status, char* title, char* encodings, char* extraheads, char* type, off_t length, time_t mod );
125 static void send_response( httpd_conn* hc, int status, char* title, char* extraheads, char* form, char* arg );
126 static void send_response_tail( httpd_conn* hc );
127 static void defang( char* str, char* dfstr, int dfsize );
128 #ifdef ERR_DIR
129 static int send_err_file( httpd_conn* hc, int status, char* title, char* extraheads, char* filename );
130 #endif /* ERR_DIR */
131 #ifdef AUTH_FILE
132 static void send_authenticate( httpd_conn* hc, char* realm );
133 static int b64_decode( const char* str, unsigned char* space, int size );
134 static int auth_check( httpd_conn* hc, char* dirname  );
135 static int auth_check2( httpd_conn* hc, char* dirname  );
136 #endif /* AUTH_FILE */
137 static void send_dirredirect( httpd_conn* hc );
138 static int hexit( char c );
139 static void strdecode( char* to, char* from );
140 #ifdef GENERATE_INDEXES
141 static void strencode( char* to, int tosize, char* from );
142 #endif /* GENERATE_INDEXES */
143 #ifdef TILDE_MAP_1
144 static int tilde_map_1( httpd_conn* hc );
145 #endif /* TILDE_MAP_1 */
146 #ifdef TILDE_MAP_2
147 static int tilde_map_2( httpd_conn* hc );
148 #endif /* TILDE_MAP_2 */
149 static int vhost_map( httpd_conn* hc );
150 static char* expand_symlinks( char* path, char** restP, int no_symlink_check, int tildemapped );
151 static char* bufgets( httpd_conn* hc );
152 static void de_dotdot( char* file );
153 static void init_mime( void );
154 static void figure_mime( httpd_conn* hc );
155 #ifdef CGI_TIMELIMIT
156 static void cgi_kill2( ClientData client_data, struct timeval* nowP );
157 static void cgi_kill( ClientData client_data, struct timeval* nowP );
158 #endif /* CGI_TIMELIMIT */
159 #ifdef GENERATE_INDEXES
160 static int ls( httpd_conn* hc );
161 #endif /* GENERATE_INDEXES */
162 static char* build_env( char* fmt, char* arg );
163 #ifdef SERVER_NAME_LIST
164 static char* hostname_map( char* hostname );
165 #endif /* SERVER_NAME_LIST */
166 static char** make_envp( httpd_conn* hc );
167 static char** make_argp( httpd_conn* hc );
168 static void cgi_interpose_input( httpd_conn* hc, int wfd );
169 static void post_post_garbage_hack( httpd_conn* hc );
170 static void cgi_interpose_output( httpd_conn* hc, int rfd );
171 static void cgi_child( httpd_conn* hc );
172 static int cgi( httpd_conn* hc );
173 static int really_start_request( httpd_conn* hc, struct timeval* nowP );
174 static void make_log_entry( httpd_conn* hc, struct timeval* nowP );
175 static int check_referrer( httpd_conn* hc );
176 static int really_check_referrer( httpd_conn* hc );
177 static int sockaddr_check( httpd_sockaddr* saP );
178 static size_t sockaddr_len( httpd_sockaddr* saP );
179 static int my_snprintf( char* str, size_t size, const char* format, ... );
180 #ifndef HAVE_ATOLL
181 static long long atoll( const char* str );
182 #endif /* HAVE_ATOLL */
183
184
185 /* This global keeps track of whether we are in the main process or a
186 ** sub-process.  The reason is that httpd_write_response() can get called
187 ** in either context; when it is called from the main process it must use
188 ** non-blocking I/O to avoid stalling the server, but when it is called
189 ** from a sub-process it wants to use blocking I/O so that the whole
190 ** response definitely gets written.  So, it checks this variable.  A bit
191 ** of a hack but it seems to do the right thing.
192 */
193 static int sub_process = 0;
194
195
196 static void
197 check_options( void )
198     {
199 #if defined(TILDE_MAP_1) && defined(TILDE_MAP_2)
200     syslog( LOG_CRIT, "both TILDE_MAP_1 and TILDE_MAP_2 are defined" );
201     exit( 1 );
202 #endif /* both */
203     }
204
205
206 static void
207 free_httpd_server( httpd_server* hs )
208     {
209     if ( hs->binding_hostname != (char*) 0 )
210         free( (void*) hs->binding_hostname );
211     if ( hs->cwd != (char*) 0 )
212         free( (void*) hs->cwd );
213     if ( hs->cgi_pattern != (char*) 0 )
214         free( (void*) hs->cgi_pattern );
215     if ( hs->charset != (char*) 0 )
216         free( (void*) hs->charset );
217     if ( hs->p3p != (char*) 0 )
218         free( (void*) hs->p3p );
219     if ( hs->url_pattern != (char*) 0 )
220         free( (void*) hs->url_pattern );
221     if ( hs->local_pattern != (char*) 0 )
222         free( (void*) hs->local_pattern );
223     free( (void*) hs );
224     }
225
226
227 httpd_server*
228 httpd_initialize(
229     char* hostname, httpd_sockaddr* sa4P, httpd_sockaddr* sa6P,
230     unsigned short port, char* cgi_pattern, int cgi_limit, char* charset,
231     char* p3p, int max_age, char* cwd, int no_log, FILE* logfp,
232     int no_symlink_check, int vhost, int global_passwd, char* url_pattern,
233     char* local_pattern, int no_empty_referrers )
234     {
235     httpd_server* hs;
236     static char ghnbuf[256];
237     char* cp;
238
239     check_options();
240
241     hs = NEW( httpd_server, 1 );
242     if ( hs == (httpd_server*) 0 )
243         {
244         syslog( LOG_CRIT, "out of memory allocating an httpd_server" );
245         return (httpd_server*) 0;
246         }
247
248     if ( hostname != (char*) 0 )
249         {
250         hs->binding_hostname = strdup( hostname );
251         if ( hs->binding_hostname == (char*) 0 )
252             {
253             syslog( LOG_CRIT, "out of memory copying hostname" );
254             return (httpd_server*) 0;
255             }
256         hs->server_hostname = hs->binding_hostname;
257         }
258     else
259         {
260         hs->binding_hostname = (char*) 0;
261         hs->server_hostname = (char*) 0;
262         if ( gethostname( ghnbuf, sizeof(ghnbuf) ) < 0 )
263             ghnbuf[0] = '\0';
264 #ifdef SERVER_NAME_LIST
265         if ( ghnbuf[0] != '\0' )
266             hs->server_hostname = hostname_map( ghnbuf );
267 #endif /* SERVER_NAME_LIST */
268         if ( hs->server_hostname == (char*) 0 )
269             {
270 #ifdef SERVER_NAME
271             hs->server_hostname = SERVER_NAME;
272 #else /* SERVER_NAME */
273             if ( ghnbuf[0] != '\0' )
274                 hs->server_hostname = ghnbuf;
275 #endif /* SERVER_NAME */
276             }
277         }
278
279     hs->port = port;
280     if ( cgi_pattern == (char*) 0 )
281         hs->cgi_pattern = (char*) 0;
282     else
283         {
284         /* Nuke any leading slashes. */
285         if ( cgi_pattern[0] == '/' )
286             ++cgi_pattern;
287         hs->cgi_pattern = strdup( cgi_pattern );
288         if ( hs->cgi_pattern == (char*) 0 )
289             {
290             syslog( LOG_CRIT, "out of memory copying cgi_pattern" );
291             return (httpd_server*) 0;
292             }
293         /* Nuke any leading slashes in the cgi pattern. */
294         while ( ( cp = strstr( hs->cgi_pattern, "|/" ) ) != (char*) 0 )
295             (void) ol_strcpy( cp + 1, cp + 2 );
296         }
297     hs->cgi_limit = cgi_limit;
298     hs->cgi_count = 0;
299     hs->charset = strdup( charset );
300     hs->p3p = strdup( p3p );
301     hs->max_age = max_age;
302     hs->cwd = strdup( cwd );
303     if ( hs->cwd == (char*) 0 )
304         {
305         syslog( LOG_CRIT, "out of memory copying cwd" );
306         return (httpd_server*) 0;
307         }
308     if ( url_pattern == (char*) 0 )
309         hs->url_pattern = (char*) 0;
310     else
311         {
312         hs->url_pattern = strdup( url_pattern );
313         if ( hs->url_pattern == (char*) 0 )
314             {
315             syslog( LOG_CRIT, "out of memory copying url_pattern" );
316             return (httpd_server*) 0;
317             }
318         }
319     if ( local_pattern == (char*) 0 )
320         hs->local_pattern = (char*) 0;
321     else
322         {
323         hs->local_pattern = strdup( local_pattern );
324         if ( hs->local_pattern == (char*) 0 )
325             {
326             syslog( LOG_CRIT, "out of memory copying local_pattern" );
327             return (httpd_server*) 0;
328             }
329         }
330     hs->no_log = no_log;
331     hs->logfp = (FILE*) 0;
332     httpd_set_logfp( hs, logfp );
333     hs->no_symlink_check = no_symlink_check;
334     hs->vhost = vhost;
335     hs->global_passwd = global_passwd;
336     hs->no_empty_referrers = no_empty_referrers;
337
338     /* Initialize listen sockets.  Try v6 first because of a Linux peculiarity;
339     ** like some other systems, it has magical v6 sockets that also listen for
340     ** v4, but in Linux if you bind a v4 socket first then the v6 bind fails.
341     */
342     if ( sa6P == (httpd_sockaddr*) 0 )
343         hs->listen6_fd = -1;
344     else
345         hs->listen6_fd = initialize_listen_socket( sa6P );
346     if ( sa4P == (httpd_sockaddr*) 0 )
347         hs->listen4_fd = -1;
348     else
349         hs->listen4_fd = initialize_listen_socket( sa4P );
350     /* If we didn't get any valid sockets, fail. */
351     if ( hs->listen4_fd == -1 && hs->listen6_fd == -1 )
352         {
353         free_httpd_server( hs );
354         return (httpd_server*) 0;
355         }
356
357     init_mime();
358
359     /* Done initializing. */
360     if ( hs->binding_hostname == (char*) 0 )
361         syslog(
362             LOG_NOTICE, "%.80s starting on port %d", SERVER_SOFTWARE,
363             (int) hs->port );
364     else
365         syslog(
366             LOG_NOTICE, "%.80s starting on %.80s, port %d", SERVER_SOFTWARE,
367             httpd_ntoa( hs->listen4_fd != -1 ? sa4P : sa6P ),
368             (int) hs->port );
369     return hs;
370     }
371
372
373 static int
374 initialize_listen_socket( httpd_sockaddr* saP )
375     {
376     int listen_fd;
377     int on, flags;
378
379     /* Check sockaddr. */
380     if ( ! sockaddr_check( saP ) )
381         {
382         syslog( LOG_CRIT, "unknown sockaddr family on listen socket" );
383         return -1;
384         }
385
386     /* Create socket. */
387     listen_fd = socket( saP->sa.sa_family, SOCK_STREAM, 0 );
388     if ( listen_fd < 0 )
389         {
390         syslog( LOG_CRIT, "socket %.80s - %m", httpd_ntoa( saP ) );
391         return -1;
392         }
393     (void) fcntl( listen_fd, F_SETFD, 1 );
394
395     /* Allow reuse of local addresses. */
396     on = 1;
397     if ( setsockopt(
398              listen_fd, SOL_SOCKET, SO_REUSEADDR, (char*) &on,
399              sizeof(on) ) < 0 )
400         syslog( LOG_CRIT, "setsockopt SO_REUSEADDR - %m" );
401
402     /* Bind to it. */
403     if ( bind( listen_fd, &saP->sa, sockaddr_len( saP ) ) < 0 )
404         {
405         syslog(
406             LOG_CRIT, "bind %.80s - %m", httpd_ntoa( saP ) );
407         (void) close( listen_fd );
408         return -1;
409         }
410
411     /* Set the listen file descriptor to no-delay / non-blocking mode. */
412     flags = fcntl( listen_fd, F_GETFL, 0 );
413     if ( flags == -1 )
414         {
415         syslog( LOG_CRIT, "fcntl F_GETFL - %m" );
416         (void) close( listen_fd );
417         return -1;
418         }
419     if ( fcntl( listen_fd, F_SETFL, flags | O_NDELAY ) < 0 )
420         {
421         syslog( LOG_CRIT, "fcntl O_NDELAY - %m" );
422         (void) close( listen_fd );
423         return -1;
424         }
425
426     /* Start a listen going. */
427     if ( listen( listen_fd, LISTEN_BACKLOG ) < 0 )
428         {
429         syslog( LOG_CRIT, "listen - %m" );
430         (void) close( listen_fd );
431         return -1;
432         }
433
434     /* Use accept filtering, if available. */
435 #ifdef SO_ACCEPTFILTER
436     {
437 #if ( __FreeBSD_version >= 411000 )
438 #define ACCEPT_FILTER_NAME "httpready"
439 #else
440 #define ACCEPT_FILTER_NAME "dataready"
441 #endif
442     struct accept_filter_arg af;
443     (void) bzero( &af, sizeof(af) );
444     (void) strcpy( af.af_name, ACCEPT_FILTER_NAME );
445     (void) setsockopt(
446         listen_fd, SOL_SOCKET, SO_ACCEPTFILTER, (char*) &af, sizeof(af) );
447     }
448 #endif /* SO_ACCEPTFILTER */
449
450     return listen_fd;
451     }
452
453
454 void
455 httpd_set_logfp( httpd_server* hs, FILE* logfp )
456     {
457     if ( hs->logfp != (FILE*) 0 )
458         (void) fclose( hs->logfp );
459     hs->logfp = logfp;
460     }
461
462
463 void
464 httpd_terminate( httpd_server* hs )
465     {
466     httpd_unlisten( hs );
467     if ( hs->logfp != (FILE*) 0 )
468         (void) fclose( hs->logfp );
469     free_httpd_server( hs );
470     }
471
472
473 void
474 httpd_unlisten( httpd_server* hs )
475     {
476     if ( hs->listen4_fd != -1 )
477         {
478         (void) close( hs->listen4_fd );
479         hs->listen4_fd = -1;
480         }
481     if ( hs->listen6_fd != -1 )
482         {
483         (void) close( hs->listen6_fd );
484         hs->listen6_fd = -1;
485         }
486     }
487
488
489 /* Conditional macro to allow two alternate forms for use in the built-in
490 ** error pages.  If EXPLICIT_ERROR_PAGES is defined, the second and more
491 ** explicit error form is used; otherwise, the first and more generic
492 ** form is used.
493 */
494 #ifdef EXPLICIT_ERROR_PAGES
495 #define ERROR_FORM(a,b) b
496 #else /* EXPLICIT_ERROR_PAGES */
497 #define ERROR_FORM(a,b) a
498 #endif /* EXPLICIT_ERROR_PAGES */
499
500
501 static char* ok200title = "OK";
502 static char* ok206title = "Partial Content";
503
504 static char* err302title = "Found";
505 static char* err302form = "The actual URL is '%.80s'.\n";
506
507 static char* err304title = "Not Modified";
508
509 char* httpd_err400title = "Bad Request";
510 char* httpd_err400form =
511     "Your request has bad syntax or is inherently impossible to satisfy.\n";
512
513 #ifdef AUTH_FILE
514 static char* err401title = "Unauthorized";
515 static char* err401form =
516     "Authorization required for the URL '%.80s'.\n";
517 #endif /* AUTH_FILE */
518
519 static char* err403title = "Forbidden";
520 #ifndef EXPLICIT_ERROR_PAGES
521 static char* err403form =
522     "You do not have permission to get URL '%.80s' from this server.\n";
523 #endif /* !EXPLICIT_ERROR_PAGES */
524
525 static char* err404title = "Not Found";
526 static char* err404form =
527     "The requested URL '%.80s' was not found on this server.\n";
528
529 char* httpd_err408title = "Request Timeout";
530 char* httpd_err408form =
531     "No request appeared within a reasonable time period.\n";
532
533 static char* err451title = "Unavailable For Legal Reasons";
534 static char* err451form =
535     "You do not have legal permission to get URL '%.80s' from this server.\n";
536
537 static char* err500title = "Internal Error";
538 static char* err500form =
539     "There was an unusual problem serving the requested URL '%.80s'.\n";
540
541 static char* err501title = "Not Implemented";
542 static char* err501form =
543     "The requested method '%.80s' is not implemented by this server.\n";
544
545 char* httpd_err503title = "Service Temporarily Overloaded";
546 char* httpd_err503form =
547     "The requested URL '%.80s' is temporarily overloaded.  Please try again later.\n";
548
549
550 /* Append a string to the buffer waiting to be sent as response. */
551 static void
552 add_response( httpd_conn* hc, char* str )
553     {
554     size_t len;
555
556     len = strlen( str );
557     httpd_realloc_str( &hc->response, &hc->maxresponse, hc->responselen + len );
558     (void) memmove( &(hc->response[hc->responselen]), str, len );
559     hc->responselen += len;
560     }
561
562 /* Send the buffered response. */
563 void
564 httpd_write_response( httpd_conn* hc )
565     {
566     /* If we are in a sub-process, turn off no-delay mode. */
567     if ( sub_process )
568         httpd_clear_ndelay( hc->conn_fd );
569     /* Send the response, if necessary. */
570     if ( hc->responselen > 0 )
571         {
572         (void) httpd_write_fully( hc->conn_fd, hc->response, hc->responselen );
573         hc->responselen = 0;
574         }
575     }
576
577
578 /* Set no-delay / non-blocking mode on a socket. */
579 void
580 httpd_set_ndelay( int fd )
581     {
582     int flags, newflags;
583
584     flags = fcntl( fd, F_GETFL, 0 );
585     if ( flags != -1 )
586         {
587         newflags = flags | (int) O_NDELAY;
588         if ( newflags != flags )
589             (void) fcntl( fd, F_SETFL, newflags );
590         }
591     }
592
593
594 /* Clear no-delay / non-blocking mode on a socket. */
595 void
596 httpd_clear_ndelay( int fd )
597     {
598     int flags, newflags;
599
600     flags = fcntl( fd, F_GETFL, 0 );
601     if ( flags != -1 )
602         {
603         newflags = flags & ~ (int) O_NDELAY;
604         if ( newflags != flags )
605             (void) fcntl( fd, F_SETFL, newflags );
606         }
607     }
608
609
610 static void
611 send_mime( httpd_conn* hc, int status, char* title, char* encodings, char* extraheads, char* type, off_t length, time_t mod )
612     {
613     time_t now, expires;
614     const char* rfc1123fmt = "%a, %d %b %Y %H:%M:%S GMT";
615     char nowbuf[100];
616     char modbuf[100];
617     char expbuf[100];
618     char fixed_type[500];
619     char buf[1000];
620     int partial_content;
621     int s100;
622
623     hc->status = status;
624     hc->bytes_to_send = length;
625     if ( hc->mime_flag )
626         {
627         if ( status == 200 && hc->got_range &&
628              ( hc->last_byte_index >= hc->first_byte_index ) &&
629              ( ( hc->last_byte_index != length - 1 ) ||
630                ( hc->first_byte_index != 0 ) ) &&
631              ( hc->range_if == (time_t) -1 ||
632                hc->range_if == hc->sb.st_mtime ) )
633             {
634             partial_content = 1;
635             hc->status = status = 206;
636             title = ok206title;
637             }
638         else
639             {
640             partial_content = 0;
641             hc->got_range = 0;
642             }
643
644         now = time( (time_t*) 0 );
645         if ( mod == (time_t) 0 )
646             mod = now;
647         (void) strftime( nowbuf, sizeof(nowbuf), rfc1123fmt, gmtime( &now ) );
648         (void) strftime( modbuf, sizeof(modbuf), rfc1123fmt, gmtime( &mod ) );
649         (void) my_snprintf(
650             fixed_type, sizeof(fixed_type), type, hc->hs->charset );
651         (void) my_snprintf( buf, sizeof(buf),
652             "%.20s %d %s\015\012Server: %s\015\012Content-Type: %s\015\012Date: %s\015\012Last-Modified: %s\015\012Accept-Ranges: bytes\015\012Connection: close\015\012",
653             hc->protocol, status, title, EXPOSED_SERVER_SOFTWARE, fixed_type,
654             nowbuf, modbuf );
655         add_response( hc, buf );
656         s100 = status / 100;
657         if ( s100 != 2 && s100 != 3 )
658             {
659             (void) my_snprintf( buf, sizeof(buf),
660                 "Cache-Control: no-cache,no-store\015\012" );
661             add_response( hc, buf );
662             }
663         if ( encodings[0] != '\0' )
664             {
665             (void) my_snprintf( buf, sizeof(buf),
666                 "Content-Encoding: %s\015\012", encodings );
667             add_response( hc, buf );
668             }
669         if ( partial_content )
670             {
671             (void) my_snprintf( buf, sizeof(buf),
672                 "Content-Range: bytes %lld-%lld/%lld\015\012Content-Length: %lld\015\012",
673                 (long long) hc->first_byte_index,
674                 (long long) hc->last_byte_index,
675                 (long long) length,
676                 (long long) ( hc->last_byte_index - hc->first_byte_index + 1 ) );
677             add_response( hc, buf );
678             }
679         else if ( length >= 0 )
680             {
681             (void) my_snprintf( buf, sizeof(buf),
682                 "Content-Length: %lld\015\012", (long long) length );
683             add_response( hc, buf );
684             }
685         if ( hc->hs->p3p[0] != '\0' )
686             {
687             (void) my_snprintf( buf, sizeof(buf), "P3P: %s\015\012", hc->hs->p3p );
688             add_response( hc, buf );
689             }
690         if ( hc->hs->max_age >= 0 )
691             {
692             expires = now + hc->hs->max_age;
693             (void) strftime(
694                 expbuf, sizeof(expbuf), rfc1123fmt, gmtime( &expires ) );
695             (void) my_snprintf( buf, sizeof(buf),
696                 "Cache-Control: max-age=%d\015\012Expires: %s\015\012",
697                 hc->hs->max_age, expbuf );
698             add_response( hc, buf );
699             }
700         if ( extraheads[0] != '\0' )
701             add_response( hc, extraheads );
702         add_response( hc, "\015\012" );
703         }
704     }
705
706
707 static int str_alloc_count = 0;
708 static size_t str_alloc_size = 0;
709
710 void
711 httpd_realloc_str( char** strP, size_t* maxsizeP, size_t size )
712     {
713     if ( *maxsizeP == 0 )
714         {
715         *maxsizeP = MAX( 200, size + 100 );
716         *strP = NEW( char, *maxsizeP + 1 );
717         ++str_alloc_count;
718         str_alloc_size += *maxsizeP;
719         }
720     else if ( size > *maxsizeP )
721         {
722         str_alloc_size -= *maxsizeP;
723         *maxsizeP = MAX( *maxsizeP * 2, size * 5 / 4 );
724         *strP = RENEW( *strP, char, *maxsizeP + 1 );
725         str_alloc_size += *maxsizeP;
726         }
727     else
728         return;
729     if ( *strP == (char*) 0 )
730         {
731         syslog(
732             LOG_ERR, "out of memory reallocating a string to %ld bytes",
733             (long) *maxsizeP );
734         exit( 1 );
735         }
736     }
737
738
739 static void
740 send_response( httpd_conn* hc, int status, char* title, char* extraheads, char* form, char* arg )
741     {
742     char defanged_arg[1000], buf[2000];
743
744     send_mime(
745         hc, status, title, "", extraheads, "text/html; charset=%s", (off_t) -1,
746         (time_t) 0 );
747     (void) my_snprintf( buf, sizeof(buf), "\
748 <!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\n\
749 \n\
750 <html>\n\
751 \n\
752   <head>\n\
753     <meta http-equiv=\"Content-type\" content=\"text/html;charset=UTF-8\">\n\
754     <title>%d %s</title>\n\
755   </head>\n\
756 \n\
757   <body bgcolor=\"#cc9999\" text=\"#000000\" link=\"#2020ff\" vlink=\"#4040cc\">\n\
758 \n\
759     <h2>%d %s</h2>\n",
760         status, title, status, title );
761     add_response( hc, buf );
762     defang( arg, defanged_arg, sizeof(defanged_arg) );
763     (void) my_snprintf( buf, sizeof(buf), form, defanged_arg );
764     add_response( hc, buf );
765     if ( match( "**MSIE**", hc->useragent ) )
766         {
767         int n;
768         add_response( hc, "<!--\n" );
769         for ( n = 0; n < 6; ++n )
770             add_response( hc, "Padding so that MSIE deigns to show this error instead of its own canned one.\n");
771         add_response( hc, "-->\n" );
772         }
773     send_response_tail( hc );
774     }
775
776
777 static void
778 send_response_tail( httpd_conn* hc )
779     {
780     char buf[1000];
781
782     (void) my_snprintf( buf, sizeof(buf), "\
783     <hr>\n\
784 \n\
785     <address><a href=\"%s\">%s</a></address>\n\
786 \n\
787   </body>\n\
788 \n\
789 </html>\n",
790         SERVER_ADDRESS, EXPOSED_SERVER_SOFTWARE );
791     add_response( hc, buf );
792     }
793
794
795 static void
796 defang( char* str, char* dfstr, int dfsize )
797     {
798     char* cp1;
799     char* cp2;
800
801     for ( cp1 = str, cp2 = dfstr;
802           *cp1 != '\0' && cp2 - dfstr < dfsize - 5;
803           ++cp1, ++cp2 )
804         {
805         switch ( *cp1 )
806             {
807             case '<':
808             *cp2++ = '&';
809             *cp2++ = 'l';
810             *cp2++ = 't';
811             *cp2 = ';';
812             break;
813             case '>':
814             *cp2++ = '&';
815             *cp2++ = 'g';
816             *cp2++ = 't';
817             *cp2 = ';';
818             break;
819             default:
820             *cp2 = *cp1;
821             break;
822             }
823         }
824     *cp2 = '\0';
825     }
826
827
828 void
829 httpd_send_err( httpd_conn* hc, int status, char* title, char* extraheads, char* form, char* arg )
830     {
831 #ifdef ERR_DIR
832
833     char filename[1000];
834
835     /* Try virtual host error page. */
836     if ( hc->hs->vhost && hc->hostdir[0] != '\0' )
837         {
838         (void) my_snprintf( filename, sizeof(filename),
839             "%s/%s/err%d.html", hc->hostdir, ERR_DIR, status );
840         if ( send_err_file( hc, status, title, extraheads, filename ) )
841             return;
842         }
843
844     /* Try server-wide error page. */
845     (void) my_snprintf( filename, sizeof(filename),
846         "%s/err%d.html", ERR_DIR, status );
847     if ( send_err_file( hc, status, title, extraheads, filename ) )
848         return;
849
850     /* Fall back on built-in error page. */
851     send_response( hc, status, title, extraheads, form, arg );
852
853 #else /* ERR_DIR */
854
855     send_response( hc, status, title, extraheads, form, arg );
856
857 #endif /* ERR_DIR */
858     }
859
860
861 #ifdef ERR_DIR
862 static int
863 send_err_file( httpd_conn* hc, int status, char* title, char* extraheads, char* filename )
864     {
865     FILE* fp;
866     char buf[1000];
867     size_t r;
868
869     fp = fopen( filename, "r" );
870     if ( fp == (FILE*) 0 )
871         return 0;
872     send_mime(
873         hc, status, title, "", extraheads, "text/html; charset=%s", (off_t) -1,
874         (time_t) 0 );
875     for (;;)
876         {
877         r = fread( buf, 1, sizeof(buf) - 1, fp );
878         if ( r == 0 )
879             break;
880         buf[r] = '\0';
881         add_response( hc, buf );
882         }
883     (void) fclose( fp );
884
885 #ifdef ERR_APPEND_SERVER_INFO
886     send_response_tail( hc );
887 #endif /* ERR_APPEND_SERVER_INFO */
888
889     return 1;
890     }
891 #endif /* ERR_DIR */
892
893
894 #ifdef AUTH_FILE
895
896 static void
897 send_authenticate( httpd_conn* hc, char* realm )
898     {
899     static char* header;
900     static size_t maxheader = 0;
901     static char headstr[] = "WWW-Authenticate: Basic realm=\"";
902
903     httpd_realloc_str(
904         &header, &maxheader, sizeof(headstr) + strlen( realm ) + 3 );
905     (void) my_snprintf( header, maxheader, "%s%s\"\015\012", headstr, realm );
906     httpd_send_err( hc, 401, err401title, header, err401form, hc->encodedurl );
907     /* If the request was a POST then there might still be data to be read,
908     ** so we need to do a lingering close.
909     */
910     if ( hc->method == METHOD_POST )
911         hc->should_linger = 1;
912     }
913
914
915 /* Base-64 decoding.  This represents binary data as printable ASCII
916 ** characters.  Three 8-bit binary bytes are turned into four 6-bit
917 ** values, like so:
918 **
919 **   [11111111]  [22222222]  [33333333]
920 **
921 **   [111111] [112222] [222233] [333333]
922 **
923 ** Then the 6-bit values are represented using the characters "A-Za-z0-9+/".
924 */
925
926 static int b64_decode_table[256] = {
927     -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* 00-0F */
928     -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* 10-1F */
929     -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63,  /* 20-2F */
930     52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1,  /* 30-3F */
931     -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,  /* 40-4F */
932     15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,  /* 50-5F */
933     -1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,  /* 60-6F */
934     41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1,  /* 70-7F */
935     -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* 80-8F */
936     -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* 90-9F */
937     -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* A0-AF */
938     -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* B0-BF */
939     -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* C0-CF */
940     -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* D0-DF */
941     -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* E0-EF */
942     -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1   /* F0-FF */
943     };
944
945 /* Do base-64 decoding on a string.  Ignore any non-base64 bytes.
946 ** Return the actual number of bytes generated.  The decoded size will
947 ** be at most 3/4 the size of the encoded, and may be smaller if there
948 ** are padding characters (blanks, newlines).
949 */
950 static int
951 b64_decode( const char* str, unsigned char* space, int size )
952     {
953     const char* cp;
954     int space_idx, phase;
955     int d, prev_d = 0;
956     unsigned char c;
957
958     space_idx = 0;
959     phase = 0;
960     for ( cp = str; *cp != '\0'; ++cp )
961         {
962         d = b64_decode_table[(int) ((unsigned char) *cp)];
963         if ( d != -1 )
964             {
965             switch ( phase )
966                 {
967                 case 0:
968                 ++phase;
969                 break;
970                 case 1:
971                 c = ( ( prev_d << 2 ) | ( ( d & 0x30 ) >> 4 ) );
972                 if ( space_idx < size )
973                     space[space_idx++] = c;
974                 ++phase;
975                 break;
976                 case 2:
977                 c = ( ( ( prev_d & 0xf ) << 4 ) | ( ( d & 0x3c ) >> 2 ) );
978                 if ( space_idx < size )
979                     space[space_idx++] = c;
980                 ++phase;
981                 break;
982                 case 3:
983                 c = ( ( ( prev_d & 0x03 ) << 6 ) | d );
984                 if ( space_idx < size )
985                     space[space_idx++] = c;
986                 phase = 0;
987                 break;
988                 }
989             prev_d = d;
990             }
991         }
992     return space_idx;
993     }
994
995
996 /* Returns -1 == unauthorized, 0 == no auth file, 1 = authorized. */
997 static int
998 auth_check( httpd_conn* hc, char* dirname  )
999     {
1000     if ( hc->hs->global_passwd )
1001         {
1002         char* topdir;
1003         if ( hc->hs->vhost && hc->hostdir[0] != '\0' )
1004             topdir = hc->hostdir;
1005         else
1006             topdir = ".";
1007         switch ( auth_check2( hc, topdir ) )
1008             {
1009             case -1:
1010             return -1;
1011             case 1:
1012             return 1;
1013             }
1014         }
1015     return auth_check2( hc, dirname );
1016     }
1017
1018
1019 /* Returns -1 == unauthorized, 0 == no auth file, 1 = authorized. */
1020 static int
1021 auth_check2( httpd_conn* hc, char* dirname  )
1022     {
1023     static char* authpath;
1024     static size_t maxauthpath = 0;
1025     struct stat sb;
1026     char authinfo[500];
1027     char* authpass;
1028     char* colon;
1029     int l;
1030     FILE* fp;
1031     char line[500];
1032     char* cryp;
1033     static char* prevauthpath;
1034     static size_t maxprevauthpath = 0;
1035     static time_t prevmtime;
1036     static char* prevuser;
1037     static size_t maxprevuser = 0;
1038     static char* prevcryp;
1039     static size_t maxprevcryp = 0;
1040
1041     /* Construct auth filename. */
1042     httpd_realloc_str(
1043         &authpath, &maxauthpath, strlen( dirname ) + 1 + sizeof(AUTH_FILE) );
1044     (void) my_snprintf( authpath, maxauthpath, "%s/%s", dirname, AUTH_FILE );
1045
1046     /* Does this directory have an auth file? */
1047     if ( stat( authpath, &sb ) < 0 )
1048         /* Nope, let the request go through. */
1049         return 0;
1050
1051     /* Does this request contain basic authorization info? */
1052     if ( hc->authorization[0] == '\0' ||
1053          strncmp( hc->authorization, "Basic ", 6 ) != 0 )
1054         {
1055         /* Nope, return a 401 Unauthorized. */
1056         send_authenticate( hc, dirname );
1057         return -1;
1058         }
1059
1060     /* Decode it. */
1061     l = b64_decode(
1062         &(hc->authorization[6]), (unsigned char*) authinfo,
1063         sizeof(authinfo) - 1 );
1064     authinfo[l] = '\0';
1065     /* Split into user and password. */
1066     authpass = strchr( authinfo, ':' );
1067     if ( authpass == (char*) 0 )
1068         {
1069         /* No colon?  Bogus auth info. */
1070         send_authenticate( hc, dirname );
1071         return -1;
1072         }
1073     *authpass++ = '\0';
1074     /* If there are more fields, cut them off. */
1075     colon = strchr( authpass, ':' );
1076     if ( colon != (char*) 0 )
1077         *colon = '\0';
1078
1079     /* See if we have a cached entry and can use it. */
1080     if ( maxprevauthpath != 0 &&
1081          strcmp( authpath, prevauthpath ) == 0 &&
1082          sb.st_mtime == prevmtime &&
1083          strcmp( authinfo, prevuser ) == 0 )
1084         {
1085         /* Yes.  Check against the cached encrypted password. */
1086         if ( strcmp( crypt( authpass, prevcryp ), prevcryp ) == 0 )
1087             {
1088             /* Ok! */
1089             httpd_realloc_str(
1090                 &hc->remoteuser, &hc->maxremoteuser, strlen( authinfo ) );
1091             (void) strcpy( hc->remoteuser, authinfo );
1092             return 1;
1093             }
1094         else
1095             {
1096             /* No. */
1097             send_authenticate( hc, dirname );
1098             return -1;
1099             }
1100         }
1101
1102     /* Open the password file. */
1103     fp = fopen( authpath, "r" );
1104     if ( fp == (FILE*) 0 )
1105         {
1106         /* The file exists but we can't open it?  Disallow access. */
1107         syslog(
1108             LOG_ERR, "%.80s auth file %.80s could not be opened - %m",
1109             httpd_ntoa( &hc->client_addr ), authpath );
1110         httpd_send_err(
1111             hc, 403, err403title, "",
1112             ERROR_FORM( err403form, "The requested URL '%.80s' is protected by an authentication file, but the authentication file cannot be opened.\n" ),
1113             hc->encodedurl );
1114         return -1;
1115         }
1116
1117     /* Read it. */
1118     while ( fgets( line, sizeof(line), fp ) != (char*) 0 )
1119         {
1120         /* Nuke newline. */
1121         l = strlen( line );
1122         if ( line[l - 1] == '\n' )
1123             line[l - 1] = '\0';
1124         /* Split into user and encrypted password. */
1125         cryp = strchr( line, ':' );
1126         if ( cryp == (char*) 0 )
1127             continue;
1128         *cryp++ = '\0';
1129         /* Is this the right user? */
1130         if ( strcmp( line, authinfo ) == 0 )
1131             {
1132             /* Yes. */
1133             (void) fclose( fp );
1134             /* So is the password right? */
1135             if ( strcmp( crypt( authpass, cryp ), cryp ) == 0 )
1136                 {
1137                 /* Ok! */
1138                 httpd_realloc_str(
1139                     &hc->remoteuser, &hc->maxremoteuser, strlen( line ) );
1140                 (void) strcpy( hc->remoteuser, line );
1141                 /* And cache this user's info for next time. */
1142                 httpd_realloc_str(
1143                     &prevauthpath, &maxprevauthpath, strlen( authpath ) );
1144                 (void) strcpy( prevauthpath, authpath );
1145                 prevmtime = sb.st_mtime;
1146                 httpd_realloc_str(
1147                     &prevuser, &maxprevuser, strlen( authinfo ) );
1148                 (void) strcpy( prevuser, authinfo );
1149                 httpd_realloc_str( &prevcryp, &maxprevcryp, strlen( cryp ) );
1150                 (void) strcpy( prevcryp, cryp );
1151                 return 1;
1152                 }
1153             else
1154                 {
1155                 /* No. */
1156                 send_authenticate( hc, dirname );
1157                 return -1;
1158                 }
1159             }
1160         }
1161
1162     /* Didn't find that user.  Access denied. */
1163     (void) fclose( fp );
1164     send_authenticate( hc, dirname );
1165     return -1;
1166     }
1167
1168 #endif /* AUTH_FILE */
1169
1170
1171 static void
1172 send_dirredirect( httpd_conn* hc )
1173     {
1174     static char* location;
1175     static char* header;
1176     static size_t maxlocation = 0, maxheader = 0;
1177     static char headstr[] = "Location: ";
1178
1179     if ( hc->query[0] != '\0')
1180         {
1181         char* cp = strchr( hc->encodedurl, '?' );
1182         if ( cp != (char*) 0 )  /* should always find it */
1183             *cp = '\0';
1184         httpd_realloc_str(
1185             &location, &maxlocation,
1186             strlen( hc->encodedurl ) + 2 + strlen( hc->query ) );
1187         (void) my_snprintf( location, maxlocation,
1188             "%s/?%s", hc->encodedurl, hc->query );
1189         }
1190     else
1191         {
1192         httpd_realloc_str(
1193             &location, &maxlocation, strlen( hc->encodedurl ) + 1 );
1194         (void) my_snprintf( location, maxlocation,
1195             "%s/", hc->encodedurl );
1196         }
1197     httpd_realloc_str(
1198         &header, &maxheader, sizeof(headstr) + strlen( location ) );
1199     (void) my_snprintf( header, maxheader,
1200         "%s%s\015\012", headstr, location );
1201     send_response( hc, 302, err302title, header, err302form, location );
1202     }
1203
1204
1205 char*
1206 httpd_method_str( int method )
1207     {
1208     switch ( method )
1209         {
1210         case METHOD_GET: return "GET";
1211         case METHOD_HEAD: return "HEAD";
1212         case METHOD_POST: return "POST";
1213         case METHOD_PUT: return "PUT";
1214         case METHOD_DELETE: return "DELETE";
1215         case METHOD_TRACE: return "TRACE";
1216         default: return "UNKNOWN";
1217         }
1218     }
1219
1220
1221 static int
1222 hexit( char c )
1223     {
1224     if ( c >= '0' && c <= '9' )
1225         return c - '0';
1226     if ( c >= 'a' && c <= 'f' )
1227         return c - 'a' + 10;
1228     if ( c >= 'A' && c <= 'F' )
1229         return c - 'A' + 10;
1230     return 0;           /* shouldn't happen, we're guarded by isxdigit() */
1231     }
1232
1233
1234 /* Copies and decodes a string.  It's ok for from and to to be the
1235 ** same string.
1236 */
1237 static void
1238 strdecode( char* to, char* from )
1239     {
1240     for ( ; *from != '\0'; ++to, ++from )
1241         {
1242         if ( from[0] == '%' && isxdigit( from[1] ) && isxdigit( from[2] ) )
1243             {
1244             *to = hexit( from[1] ) * 16 + hexit( from[2] );
1245             from += 2;
1246             }
1247         else
1248             *to = *from;
1249         }
1250     *to = '\0';
1251     }
1252
1253
1254 #ifdef GENERATE_INDEXES
1255 /* Copies and encodes a string. */
1256 static void
1257 strencode( char* to, int tosize, char* from )
1258     {
1259     int tolen;
1260
1261     for ( tolen = 0; *from != '\0' && tolen + 4 < tosize; ++from )
1262         {
1263         if ( isalnum(*from) || strchr( "/_.-~", *from ) != (char*) 0 )
1264             {
1265             *to = *from;
1266             ++to;
1267             ++tolen;
1268             }
1269         else
1270             {
1271             (void) sprintf( to, "%%%02x", (int) *from & 0xff );
1272             to += 3;
1273             tolen += 3;
1274             }
1275         }
1276     *to = '\0';
1277     }
1278 #endif /* GENERATE_INDEXES */
1279
1280
1281 #ifdef TILDE_MAP_1
1282 /* Map a ~username/whatever URL into <prefix>/username. */
1283 static int
1284 tilde_map_1( httpd_conn* hc )
1285     {
1286     static char* temp;
1287     static size_t maxtemp = 0;
1288     int len;
1289     static char* prefix = TILDE_MAP_1;
1290
1291     len = strlen( hc->expnfilename ) - 1;
1292     httpd_realloc_str( &temp, &maxtemp, len );
1293     (void) strcpy( temp, &hc->expnfilename[1] );
1294     httpd_realloc_str(
1295         &hc->expnfilename, &hc->maxexpnfilename, strlen( prefix ) + 1 + len );
1296     (void) strcpy( hc->expnfilename, prefix );
1297     if ( prefix[0] != '\0' )
1298         (void) strcat( hc->expnfilename, "/" );
1299     (void) strcat( hc->expnfilename, temp );
1300     return 1;
1301     }
1302 #endif /* TILDE_MAP_1 */
1303
1304 #ifdef TILDE_MAP_2
1305 /* Map a ~username/whatever URL into <user's homedir>/<postfix>. */
1306 static int
1307 tilde_map_2( httpd_conn* hc )
1308     {
1309     static char* temp;
1310     static size_t maxtemp = 0;
1311     static char* postfix = TILDE_MAP_2;
1312     char* cp;
1313     struct passwd* pw;
1314     char* alt;
1315     char* rest;
1316
1317     /* Get the username. */
1318     httpd_realloc_str( &temp, &maxtemp, strlen( hc->expnfilename ) - 1 );
1319     (void) strcpy( temp, &hc->expnfilename[1] );
1320     cp = strchr( temp, '/' );
1321     if ( cp != (char*) 0 )
1322         *cp++ = '\0';
1323     else
1324         cp = "";
1325
1326     /* Get the passwd entry. */
1327     pw = getpwnam( temp );
1328     if ( pw == (struct passwd*) 0 )
1329         return 0;
1330
1331     /* Set up altdir. */
1332     httpd_realloc_str(
1333         &hc->altdir, &hc->maxaltdir,
1334         strlen( pw->pw_dir ) + 1 + strlen( postfix ) );
1335     (void) strcpy( hc->altdir, pw->pw_dir );
1336     if ( postfix[0] != '\0' )
1337         {
1338         (void) strcat( hc->altdir, "/" );
1339         (void) strcat( hc->altdir, postfix );
1340         }
1341     alt = expand_symlinks( hc->altdir, &rest, 0, 1 );
1342     if ( rest[0] != '\0' )
1343         return 0;
1344     httpd_realloc_str( &hc->altdir, &hc->maxaltdir, strlen( alt ) );
1345     (void) strcpy( hc->altdir, alt );
1346
1347     /* And the filename becomes altdir plus the post-~ part of the original. */
1348     httpd_realloc_str(
1349         &hc->expnfilename, &hc->maxexpnfilename,
1350         strlen( hc->altdir ) + 1 + strlen( cp ) );
1351     (void) my_snprintf( hc->expnfilename, hc->maxexpnfilename,
1352         "%s/%s", hc->altdir, cp );
1353
1354     /* For this type of tilde mapping, we want to defeat vhost mapping. */
1355     hc->tildemapped = 1;
1356
1357     return 1;
1358     }
1359 #endif /* TILDE_MAP_2 */
1360
1361
1362 /* Virtual host mapping. */
1363 static int
1364 vhost_map( httpd_conn* hc )
1365     {
1366     httpd_sockaddr sa;
1367     socklen_t sz;
1368     static char* tempfilename;
1369     static size_t maxtempfilename = 0;
1370     char* cp1;
1371     int len;
1372 #ifdef VHOST_DIRLEVELS
1373     int i;
1374     char* cp2;
1375 #endif /* VHOST_DIRLEVELS */
1376
1377     /* Figure out the virtual hostname. */
1378     if ( hc->reqhost[0] != '\0' )
1379         hc->hostname = hc->reqhost;
1380     else if ( hc->hdrhost[0] != '\0' )
1381         hc->hostname = hc->hdrhost;
1382     else
1383         {
1384         sz = sizeof(sa);
1385         if ( getsockname( hc->conn_fd, &sa.sa, &sz ) < 0 )
1386             {
1387             syslog( LOG_ERR, "getsockname - %m" );
1388             return 0;
1389             }
1390         hc->hostname = httpd_ntoa( &sa );
1391         }
1392     /* Pound it to lower case. */
1393     for ( cp1 = hc->hostname; *cp1 != '\0'; ++cp1 )
1394         if ( isupper( *cp1 ) )
1395             *cp1 = tolower( *cp1 );
1396
1397     if ( hc->tildemapped )
1398         return 1;
1399
1400     /* Figure out the host directory. */
1401 #ifdef VHOST_DIRLEVELS
1402     httpd_realloc_str(
1403         &hc->hostdir, &hc->maxhostdir,
1404         strlen( hc->hostname ) + 2 * VHOST_DIRLEVELS );
1405     if ( strncmp( hc->hostname, "www.", 4 ) == 0 )
1406         cp1 = &hc->hostname[4];
1407     else
1408         cp1 = hc->hostname;
1409     for ( cp2 = hc->hostdir, i = 0; i < VHOST_DIRLEVELS; ++i )
1410         {
1411         /* Skip dots in the hostname.  If we don't, then we get vhost
1412         ** directories in higher level of filestructure if dot gets
1413         ** involved into path construction.  It's `while' used here instead
1414         ** of `if' for it's possible to have a hostname formed with two
1415         ** dots at the end of it.
1416         */
1417         while ( *cp1 == '.' )
1418             ++cp1;
1419         /* Copy a character from the hostname, or '_' if we ran out. */
1420         if ( *cp1 != '\0' )
1421             *cp2++ = *cp1++;
1422         else
1423             *cp2++ = '_';
1424         /* Copy a slash. */
1425         *cp2++ = '/';
1426         }
1427     (void) strcpy( cp2, hc->hostname );
1428 #else /* VHOST_DIRLEVELS */
1429     httpd_realloc_str( &hc->hostdir, &hc->maxhostdir, strlen( hc->hostname ) );
1430     (void) strcpy( hc->hostdir, hc->hostname );
1431 #endif /* VHOST_DIRLEVELS */
1432
1433     /* Prepend hostdir to the filename. */
1434     len = strlen( hc->expnfilename );
1435     httpd_realloc_str( &tempfilename, &maxtempfilename, len );
1436     (void) strcpy( tempfilename, hc->expnfilename );
1437     httpd_realloc_str(
1438         &hc->expnfilename, &hc->maxexpnfilename,
1439         strlen( hc->hostdir ) + 1 + len );
1440     (void) strcpy( hc->expnfilename, hc->hostdir );
1441     (void) strcat( hc->expnfilename, "/" );
1442     (void) strcat( hc->expnfilename, tempfilename );
1443     return 1;
1444     }
1445
1446
1447 /* Expands all symlinks in the given filename, eliding ..'s and leading /'s.
1448 ** Returns the expanded path (pointer to static string), or (char*) 0 on
1449 ** errors.  Also returns, in the string pointed to by restP, any trailing
1450 ** parts of the path that don't exist.
1451 **
1452 ** This is a fairly nice little routine.  It handles any size filenames
1453 ** without excessive mallocs.
1454 */
1455 static char*
1456 expand_symlinks( char* path, char** restP, int no_symlink_check, int tildemapped )
1457     {
1458     static char* checked;
1459     static char* rest;
1460     char lnk[5000];
1461     static size_t maxchecked = 0, maxrest = 0;
1462     size_t checkedlen, restlen, linklen, prevcheckedlen, prevrestlen;
1463     int nlinks, i;
1464     char* r;
1465     char* cp1;
1466     char* cp2;
1467
1468     if ( no_symlink_check )
1469         {
1470         /* If we are chrooted, we can actually skip the symlink-expansion,
1471         ** since it's impossible to get out of the tree.  However, we still
1472         ** need to do the pathinfo check, and the existing symlink expansion
1473         ** code is a pretty reasonable way to do this.  So, what we do is
1474         ** a single stat() of the whole filename - if it exists, then we
1475         ** return it as is with nothing in restP.  If it doesn't exist, we
1476         ** fall through to the existing code.
1477         **
1478         ** One side-effect of this is that users can't symlink to central
1479         ** approved CGIs any more.  The workaround is to use the central
1480         ** URL for the CGI instead of a local symlinked one.
1481         */
1482         struct stat sb;
1483         if ( stat( path, &sb ) != -1 )
1484             {
1485             checkedlen = strlen( path );
1486             httpd_realloc_str( &checked, &maxchecked, checkedlen );
1487             (void) strcpy( checked, path );
1488             /* Trim trailing slashes. */
1489             while ( checked[checkedlen - 1] == '/' )
1490                 {
1491                 checked[checkedlen - 1] = '\0';
1492                 --checkedlen;
1493                 }
1494             httpd_realloc_str( &rest, &maxrest, 0 );
1495             rest[0] = '\0';
1496             *restP = rest;
1497             return checked;
1498             }
1499         }
1500
1501     /* Start out with nothing in checked and the whole filename in rest. */
1502     httpd_realloc_str( &checked, &maxchecked, 1 );
1503     checked[0] = '\0';
1504     checkedlen = 0;
1505     restlen = strlen( path );
1506     httpd_realloc_str( &rest, &maxrest, restlen );
1507     (void) strcpy( rest, path );
1508     if ( rest[restlen - 1] == '/' )
1509         rest[--restlen] = '\0';         /* trim trailing slash */
1510     if ( ! tildemapped )
1511         /* Remove any leading slashes. */
1512         while ( rest[0] == '/' )
1513             {
1514             (void) ol_strcpy( rest, &(rest[1]) );
1515             --restlen;
1516             }
1517     r = rest;
1518     nlinks = 0;
1519
1520     /* While there are still components to check... */
1521     while ( restlen > 0 )
1522         {
1523         /* Save current checkedlen in case we get a symlink.  Save current
1524         ** restlen in case we get a non-existant component.
1525         */
1526         prevcheckedlen = checkedlen;
1527         prevrestlen = restlen;
1528
1529         /* Grab one component from r and transfer it to checked. */
1530         cp1 = strchr( r, '/' );
1531         if ( cp1 != (char*) 0 )
1532             {
1533             i = cp1 - r;
1534             if ( i == 0 )
1535                 {
1536                 /* Special case for absolute paths. */
1537                 httpd_realloc_str( &checked, &maxchecked, checkedlen + 1 );
1538                 (void) strncpy( &checked[checkedlen], r, 1 );
1539                 checkedlen += 1;
1540                 }
1541             else if ( strncmp( r, "..", MAX( i, 2 ) ) == 0 )
1542                 {
1543                 /* Ignore ..'s that go above the start of the path. */
1544                 if ( checkedlen != 0 )
1545                     {
1546                     cp2 = strrchr( checked, '/' );
1547                     if ( cp2 == (char*) 0 )
1548                         checkedlen = 0;
1549                     else if ( cp2 == checked )
1550                         checkedlen = 1;
1551                     else
1552                         checkedlen = cp2 - checked;
1553                     }
1554                 }
1555             else
1556                 {
1557                 httpd_realloc_str( &checked, &maxchecked, checkedlen + 1 + i );
1558                 if ( checkedlen > 0 && checked[checkedlen-1] != '/' )
1559                     checked[checkedlen++] = '/';
1560                 (void) strncpy( &checked[checkedlen], r, i );
1561                 checkedlen += i;
1562                 }
1563             checked[checkedlen] = '\0';
1564             r += i + 1;
1565             restlen -= i + 1;
1566             }
1567         else
1568             {
1569             /* No slashes remaining, r is all one component. */
1570             if ( strcmp( r, ".." ) == 0 )
1571                 {
1572                 /* Ignore ..'s that go above the start of the path. */
1573                 if ( checkedlen != 0 )
1574                     {
1575                     cp2 = strrchr( checked, '/' );
1576                     if ( cp2 == (char*) 0 )
1577                         checkedlen = 0;
1578                     else if ( cp2 == checked )
1579                         checkedlen = 1;
1580                     else
1581                         checkedlen = cp2 - checked;
1582                     checked[checkedlen] = '\0';
1583                     }
1584                 }
1585             else
1586                 {
1587                 httpd_realloc_str(
1588                     &checked, &maxchecked, checkedlen + 1 + restlen );
1589                 if ( checkedlen > 0 && checked[checkedlen-1] != '/' )
1590                     checked[checkedlen++] = '/';
1591                 (void) strcpy( &checked[checkedlen], r );
1592                 checkedlen += restlen;
1593                 }
1594             r += restlen;
1595             restlen = 0;
1596             }
1597
1598         /* Try reading the current filename as a symlink */
1599         if ( checked[0] == '\0' )
1600             continue;
1601         linklen = readlink( checked, lnk, sizeof(lnk) - 1 );
1602         if ( linklen == -1 )
1603             {
1604             if ( errno == EINVAL )
1605                 continue;               /* not a symlink */
1606             if ( errno == EACCES || errno == ENOENT || errno == ENOTDIR )
1607                 {
1608                 /* That last component was bogus.  Restore and return. */
1609                 *restP = r - ( prevrestlen - restlen );
1610                 if ( prevcheckedlen == 0 )
1611                     (void) strcpy( checked, "." );
1612                 else
1613                     checked[prevcheckedlen] = '\0';
1614                 return checked;
1615                 }
1616             syslog( LOG_ERR, "readlink %.80s - %m", checked );
1617             return (char*) 0;
1618             }
1619         ++nlinks;
1620         if ( nlinks > MAX_LINKS )
1621             {
1622             syslog( LOG_ERR, "too many symlinks in %.80s", path );
1623             return (char*) 0;
1624             }
1625         lnk[linklen] = '\0';
1626         if ( lnk[linklen - 1] == '/' )
1627             lnk[--linklen] = '\0';     /* trim trailing slash */
1628
1629         /* Insert the link contents in front of the rest of the filename. */
1630         if ( restlen != 0 )
1631             {
1632             (void) ol_strcpy( rest, r );
1633             httpd_realloc_str( &rest, &maxrest, restlen + linklen + 1 );
1634             for ( i = restlen; i >= 0; --i )
1635                 rest[i + linklen + 1] = rest[i];
1636             (void) strcpy( rest, lnk );
1637             rest[linklen] = '/';
1638             restlen += linklen + 1;
1639             r = rest;
1640             }
1641         else
1642             {
1643             /* There's nothing left in the filename, so the link contents
1644             ** becomes the rest.
1645             */
1646             httpd_realloc_str( &rest, &maxrest, linklen );
1647             (void) strcpy( rest, lnk );
1648             restlen = linklen;
1649             r = rest;
1650             }
1651
1652         if ( rest[0] == '/' )
1653             {
1654             /* There must have been an absolute symlink - zero out checked. */
1655             checked[0] = '\0';
1656             checkedlen = 0;
1657             }
1658         else
1659             {
1660             /* Re-check this component. */
1661             checkedlen = prevcheckedlen;
1662             checked[checkedlen] = '\0';
1663             }
1664         }
1665
1666     /* Ok. */
1667     *restP = r;
1668     if ( checked[0] == '\0' )
1669         (void) strcpy( checked, "." );
1670     return checked;
1671     }
1672
1673
1674 int
1675 httpd_get_conn( httpd_server* hs, int listen_fd, httpd_conn* hc )
1676     {
1677     httpd_sockaddr sa;
1678     socklen_t sz;
1679
1680     if ( ! hc->initialized )
1681         {
1682         hc->read_size = 0;
1683         httpd_realloc_str( &hc->read_buf, &hc->read_size, 500 );
1684         hc->maxdecodedurl =
1685             hc->maxorigfilename = hc->maxexpnfilename = hc->maxencodings =
1686             hc->maxpathinfo = hc->maxquery = hc->maxaccept =
1687             hc->maxaccepte = hc->maxreqhost = hc->maxhostdir =
1688             hc->maxremoteuser = hc->maxresponse = 0;
1689 #ifdef TILDE_MAP_2
1690         hc->maxaltdir = 0;
1691 #endif /* TILDE_MAP_2 */
1692         httpd_realloc_str( &hc->decodedurl, &hc->maxdecodedurl, 1 );
1693         httpd_realloc_str( &hc->origfilename, &hc->maxorigfilename, 1 );
1694         httpd_realloc_str( &hc->expnfilename, &hc->maxexpnfilename, 0 );
1695         httpd_realloc_str( &hc->encodings, &hc->maxencodings, 0 );
1696         httpd_realloc_str( &hc->pathinfo, &hc->maxpathinfo, 0 );
1697         httpd_realloc_str( &hc->query, &hc->maxquery, 0 );
1698         httpd_realloc_str( &hc->accept, &hc->maxaccept, 0 );
1699         httpd_realloc_str( &hc->accepte, &hc->maxaccepte, 0 );
1700         httpd_realloc_str( &hc->reqhost, &hc->maxreqhost, 0 );
1701         httpd_realloc_str( &hc->hostdir, &hc->maxhostdir, 0 );
1702         httpd_realloc_str( &hc->remoteuser, &hc->maxremoteuser, 0 );
1703         httpd_realloc_str( &hc->response, &hc->maxresponse, 0 );
1704 #ifdef TILDE_MAP_2
1705         httpd_realloc_str( &hc->altdir, &hc->maxaltdir, 0 );
1706 #endif /* TILDE_MAP_2 */
1707         hc->initialized = 1;
1708         }
1709
1710     /* Accept the new connection. */
1711     sz = sizeof(sa);
1712     hc->conn_fd = accept( listen_fd, &sa.sa, &sz );
1713     if ( hc->conn_fd < 0 )
1714         {
1715         if ( errno == EWOULDBLOCK )
1716             return GC_NO_MORE;
1717         /* ECONNABORTED means the connection was closed by the client while
1718         ** it was waiting in the listen queue.  It's not worth logging.
1719         */
1720         if ( errno != ECONNABORTED )
1721             syslog( LOG_ERR, "accept - %m" );
1722         return GC_FAIL;
1723         }
1724     if ( ! sockaddr_check( &sa ) )
1725         {
1726         syslog( LOG_ERR, "unknown sockaddr family" );
1727         close( hc->conn_fd );
1728         hc->conn_fd = -1;
1729         return GC_FAIL;
1730         }
1731     (void) fcntl( hc->conn_fd, F_SETFD, 1 );
1732     hc->hs = hs;
1733     (void) memset( &hc->client_addr, 0, sizeof(hc->client_addr) );
1734     (void) memmove( &hc->client_addr, &sa, sockaddr_len( &sa ) );
1735     hc->read_idx = 0;
1736     hc->checked_idx = 0;
1737     hc->checked_state = CHST_FIRSTWORD;
1738     hc->method = METHOD_UNKNOWN;
1739     hc->status = 0;
1740     hc->bytes_to_send = 0;
1741     hc->bytes_sent = 0;
1742     hc->encodedurl = "";
1743     hc->decodedurl[0] = '\0';
1744     hc->protocol = "UNKNOWN";
1745     hc->origfilename[0] = '\0';
1746     hc->expnfilename[0] = '\0';
1747     hc->encodings[0] = '\0';
1748     hc->pathinfo[0] = '\0';
1749     hc->query[0] = '\0';
1750     hc->referrer = "";
1751     hc->useragent = "";
1752     hc->accept[0] = '\0';
1753     hc->accepte[0] = '\0';
1754     hc->acceptl = "";
1755     hc->cookie = "";
1756     hc->contenttype = "";
1757     hc->reqhost[0] = '\0';
1758     hc->hdrhost = "";
1759     hc->hostdir[0] = '\0';
1760     hc->authorization = "";
1761     hc->remoteuser[0] = '\0';
1762     hc->response[0] = '\0';
1763 #ifdef TILDE_MAP_2
1764     hc->altdir[0] = '\0';
1765 #endif /* TILDE_MAP_2 */
1766     hc->responselen = 0;
1767     hc->if_modified_since = (time_t) -1;
1768     hc->range_if = (time_t) -1;
1769     hc->contentlength = -1;
1770     hc->type = "";
1771     hc->hostname = (char*) 0;
1772     hc->mime_flag = 1;
1773     hc->one_one = 0;
1774     hc->got_range = 0;
1775     hc->tildemapped = 0;
1776     hc->first_byte_index = 0;
1777     hc->last_byte_index = -1;
1778     hc->keep_alive = 0;
1779     hc->should_linger = 0;
1780     hc->file_address = (char*) 0;
1781     return GC_OK;
1782     }
1783
1784
1785 /* Checks hc->read_buf to see whether a complete request has been read so far;
1786 ** either the first line has two words (an HTTP/0.9 request), or the first
1787 ** line has three words and there's a blank line present.
1788 **
1789 ** hc->read_idx is how much has been read in; hc->checked_idx is how much we
1790 ** have checked so far; and hc->checked_state is the current state of the
1791 ** finite state machine.
1792 */
1793 int
1794 httpd_got_request( httpd_conn* hc )
1795     {
1796     char c;
1797
1798     for ( ; hc->checked_idx < hc->read_idx; ++hc->checked_idx )
1799         {
1800         c = hc->read_buf[hc->checked_idx];
1801         switch ( hc->checked_state )
1802             {
1803             case CHST_FIRSTWORD:
1804             switch ( c )
1805                 {
1806                 case ' ': case '\t':
1807                 hc->checked_state = CHST_FIRSTWS;
1808                 break;
1809                 case '\012': case '\015':
1810                 hc->checked_state = CHST_BOGUS;
1811                 return GR_BAD_REQUEST;
1812                 }
1813             break;
1814             case CHST_FIRSTWS:
1815             switch ( c )
1816                 {
1817                 case ' ': case '\t':
1818                 break;
1819                 case '\012': case '\015':
1820                 hc->checked_state = CHST_BOGUS;
1821                 return GR_BAD_REQUEST;
1822                 default:
1823                 hc->checked_state = CHST_SECONDWORD;
1824                 break;
1825                 }
1826             break;
1827             case CHST_SECONDWORD:
1828             switch ( c )
1829                 {
1830                 case ' ': case '\t':
1831                 hc->checked_state = CHST_SECONDWS;
1832                 break;
1833                 case '\012': case '\015':
1834                 /* The first line has only two words - an HTTP/0.9 request. */
1835                 return GR_GOT_REQUEST;
1836                 }
1837             break;
1838             case CHST_SECONDWS:
1839             switch ( c )
1840                 {
1841                 case ' ': case '\t':
1842                 break;
1843                 case '\012': case '\015':
1844                 hc->checked_state = CHST_BOGUS;
1845                 return GR_BAD_REQUEST;
1846                 default:
1847                 hc->checked_state = CHST_THIRDWORD;
1848                 break;
1849                 }
1850             break;
1851             case CHST_THIRDWORD:
1852             switch ( c )
1853                 {
1854                 case ' ': case '\t':
1855                 hc->checked_state = CHST_THIRDWS;
1856                 break;
1857                 case '\012':
1858                 hc->checked_state = CHST_LF;
1859                 break;
1860                 case '\015':
1861                 hc->checked_state = CHST_CR;
1862                 break;
1863                 }
1864             break;
1865             case CHST_THIRDWS:
1866             switch ( c )
1867                 {
1868                 case ' ': case '\t':
1869                 break;
1870                 case '\012':
1871                 hc->checked_state = CHST_LF;
1872                 break;
1873                 case '\015':
1874                 hc->checked_state = CHST_CR;
1875                 break;
1876                 default:
1877                 hc->checked_state = CHST_BOGUS;
1878                 return GR_BAD_REQUEST;
1879                 }
1880             break;
1881             case CHST_LINE:
1882             switch ( c )
1883                 {
1884                 case '\012':
1885                 hc->checked_state = CHST_LF;
1886                 break;
1887                 case '\015':
1888                 hc->checked_state = CHST_CR;
1889                 break;
1890                 }
1891             break;
1892             case CHST_LF:
1893             switch ( c )
1894                 {
1895                 case '\012':
1896                 /* Two newlines in a row - a blank line - end of request. */
1897                 return GR_GOT_REQUEST;
1898                 case '\015':
1899                 hc->checked_state = CHST_CR;
1900                 break;
1901                 default:
1902                 hc->checked_state = CHST_LINE;
1903                 break;
1904                 }
1905             break;
1906             case CHST_CR:
1907             switch ( c )
1908                 {
1909                 case '\012':
1910                 hc->checked_state = CHST_CRLF;
1911                 break;
1912                 case '\015':
1913                 /* Two returns in a row - end of request. */
1914                 return GR_GOT_REQUEST;
1915                 default:
1916                 hc->checked_state = CHST_LINE;
1917                 break;
1918                 }
1919             break;
1920             case CHST_CRLF:
1921             switch ( c )
1922                 {
1923                 case '\012':
1924                 /* Two newlines in a row - end of request. */
1925                 return GR_GOT_REQUEST;
1926                 case '\015':
1927                 hc->checked_state = CHST_CRLFCR;
1928                 break;
1929                 default:
1930                 hc->checked_state = CHST_LINE;
1931                 break;
1932                 }
1933             break;
1934             case CHST_CRLFCR:
1935             switch ( c )
1936                 {
1937                 case '\012': case '\015':
1938                 /* Two CRLFs or two CRs in a row - end of request. */
1939                 return GR_GOT_REQUEST;
1940                 default:
1941                 hc->checked_state = CHST_LINE;
1942                 break;
1943                 }
1944             break;
1945             case CHST_BOGUS:
1946             return GR_BAD_REQUEST;
1947             }
1948         }
1949     return GR_NO_REQUEST;
1950     }
1951
1952
1953 int
1954 httpd_parse_request( httpd_conn* hc )
1955     {
1956     char* buf;
1957     char* method_str;
1958     char* url;
1959     char* protocol;
1960     char* reqhost;
1961     char* eol;
1962     char* cp;
1963     char* pi;
1964
1965     hc->checked_idx = 0;        /* reset */
1966     method_str = bufgets( hc );
1967     url = strpbrk( method_str, " \t\012\015" );
1968     if ( url == (char*) 0 )
1969         {
1970         httpd_send_err( hc, 400, httpd_err400title, "", httpd_err400form, "" );
1971         return -1;
1972         }
1973     *url++ = '\0';
1974     url += strspn( url, " \t\012\015" );
1975     protocol = strpbrk( url, " \t\012\015" );
1976     if ( protocol == (char*) 0 )
1977         {
1978         protocol = "HTTP/0.9";
1979         hc->mime_flag = 0;
1980         }
1981     else
1982         {
1983         *protocol++ = '\0';
1984         protocol += strspn( protocol, " \t\012\015" );
1985         if ( *protocol != '\0' )
1986             {
1987             eol = strpbrk( protocol, " \t\012\015" );
1988             if ( eol != (char*) 0 )
1989                 *eol = '\0';
1990             if ( strcasecmp( protocol, "HTTP/1.0" ) != 0 )
1991                 hc->one_one = 1;
1992             }
1993         }
1994     hc->protocol = protocol;
1995
1996     /* Check for HTTP/1.1 absolute URL. */
1997     if ( strncasecmp( url, "http://", 7 ) == 0 )
1998         {
1999         if ( ! hc->one_one )
2000             {
2001             httpd_send_err( hc, 400, httpd_err400title, "", httpd_err400form, "" );
2002             return -1;
2003             }
2004         reqhost = url + 7;
2005         url = strchr( reqhost, '/' );
2006         if ( url == (char*) 0 )
2007             {
2008             httpd_send_err( hc, 400, httpd_err400title, "", httpd_err400form, "" );
2009             return -1;
2010             }
2011         *url = '\0';
2012         if ( strchr( reqhost, '/' ) != (char*) 0 || reqhost[0] == '.' )
2013             {
2014             httpd_send_err( hc, 400, httpd_err400title, "", httpd_err400form, "" );
2015             return -1;
2016             }
2017         httpd_realloc_str( &hc->reqhost, &hc->maxreqhost, strlen( reqhost ) );
2018         (void) strcpy( hc->reqhost, reqhost );
2019         *url = '/';
2020         }
2021
2022     if ( *url != '/' )
2023         {
2024         httpd_send_err( hc, 400, httpd_err400title, "", httpd_err400form, "" );
2025         return -1;
2026         }
2027
2028     if ( strcasecmp( method_str, httpd_method_str( METHOD_GET ) ) == 0 )
2029         hc->method = METHOD_GET;
2030     else if ( strcasecmp( method_str, httpd_method_str( METHOD_HEAD ) ) == 0 )
2031         hc->method = METHOD_HEAD;
2032     else if ( strcasecmp( method_str, httpd_method_str( METHOD_POST ) ) == 0 )
2033         hc->method = METHOD_POST;
2034     else if ( strcasecmp( method_str, httpd_method_str( METHOD_PUT ) ) == 0 )
2035         hc->method = METHOD_PUT;
2036     else if ( strcasecmp( method_str, httpd_method_str( METHOD_DELETE ) ) == 0 )
2037         hc->method = METHOD_DELETE;
2038     else if ( strcasecmp( method_str, httpd_method_str( METHOD_TRACE ) ) == 0 )
2039         hc->method = METHOD_TRACE;
2040     else
2041         {
2042         httpd_send_err( hc, 501, err501title, "", err501form, method_str );
2043         return -1;
2044         }
2045
2046     hc->encodedurl = url;
2047     httpd_realloc_str(
2048         &hc->decodedurl, &hc->maxdecodedurl, strlen( hc->encodedurl ) );
2049     strdecode( hc->decodedurl, hc->encodedurl );
2050
2051     httpd_realloc_str(
2052         &hc->origfilename, &hc->maxorigfilename, strlen( hc->decodedurl ) );
2053     (void) strcpy( hc->origfilename, &hc->decodedurl[1] );
2054     /* Special case for top-level URL. */
2055     if ( hc->origfilename[0] == '\0' )
2056         (void) strcpy( hc->origfilename, "." );
2057
2058     /* Extract query string from encoded URL. */
2059     cp = strchr( hc->encodedurl, '?' );
2060     if ( cp != (char*) 0 )
2061         {
2062         ++cp;
2063         httpd_realloc_str( &hc->query, &hc->maxquery, strlen( cp ) );
2064         (void) strcpy( hc->query, cp );
2065         /* Remove query from (decoded) origfilename. */
2066         cp = strchr( hc->origfilename, '?' );
2067         if ( cp != (char*) 0 )
2068             *cp = '\0';
2069         }
2070
2071     de_dotdot( hc->origfilename );
2072     if ( hc->origfilename[0] == '/' ||
2073          ( hc->origfilename[0] == '.' && hc->origfilename[1] == '.' &&
2074            ( hc->origfilename[2] == '\0' || hc->origfilename[2] == '/' ) ) )
2075         {
2076         httpd_send_err( hc, 400, httpd_err400title, "", httpd_err400form, "" );
2077         return -1;
2078         }
2079
2080     if ( hc->mime_flag )
2081         {
2082         /* Read the MIME headers. */
2083         while ( ( buf = bufgets( hc ) ) != (char*) 0 )
2084             {
2085             if ( buf[0] == '\0' )
2086                 break;
2087             if ( strncasecmp( buf, "Referer:", 8 ) == 0 )
2088                 {
2089                 cp = &buf[8];
2090                 cp += strspn( cp, " \t" );
2091                 hc->referrer = cp;
2092                 }
2093             else if ( strncasecmp( buf, "Referrer:", 9 ) == 0 )
2094                 {
2095                 cp = &buf[9];
2096                 cp += strspn( cp, " \t" );
2097                 hc->referrer = cp;
2098                 }
2099             else if ( strncasecmp( buf, "User-Agent:", 11 ) == 0 )
2100                 {
2101                 cp = &buf[11];
2102                 cp += strspn( cp, " \t" );
2103                 hc->useragent = cp;
2104                 }
2105             else if ( strncasecmp( buf, "Host:", 5 ) == 0 )
2106                 {
2107                 cp = &buf[5];
2108                 cp += strspn( cp, " \t" );
2109                 hc->hdrhost = cp;
2110                 cp = strchr( hc->hdrhost, ':' );
2111                 if ( cp != (char*) 0 )
2112                     *cp = '\0';
2113                 if ( strchr( hc->hdrhost, '/' ) != (char*) 0 || hc->hdrhost[0] == '.' )
2114                     {
2115                     httpd_send_err( hc, 400, httpd_err400title, "", httpd_err400form, "" );
2116                     return -1;
2117                     }
2118                 }
2119             else if ( strncasecmp( buf, "Accept:", 7 ) == 0 )
2120                 {
2121                 cp = &buf[7];
2122                 cp += strspn( cp, " \t" );
2123                 if ( hc->accept[0] != '\0' )
2124                     {
2125                     if ( strlen( hc->accept ) > 5000 )
2126                         {
2127                         syslog(
2128                             LOG_ERR, "%.80s way too much Accept: data",
2129                             httpd_ntoa( &hc->client_addr ) );
2130                         continue;
2131                         }
2132                     httpd_realloc_str(
2133                         &hc->accept, &hc->maxaccept,
2134                         strlen( hc->accept ) + 2 + strlen( cp ) );
2135                     (void) strcat( hc->accept, ", " );
2136                     }
2137                 else
2138                     httpd_realloc_str(
2139                         &hc->accept, &hc->maxaccept, strlen( cp ) );
2140                 (void) strcat( hc->accept, cp );
2141                 }
2142             else if ( strncasecmp( buf, "Accept-Encoding:", 16 ) == 0 )
2143                 {
2144                 cp = &buf[16];
2145                 cp += strspn( cp, " \t" );
2146                 if ( hc->accepte[0] != '\0' )
2147                     {
2148                     if ( strlen( hc->accepte ) > 5000 )
2149                         {
2150                         syslog(
2151                             LOG_ERR, "%.80s way too much Accept-Encoding: data",
2152                             httpd_ntoa( &hc->client_addr ) );
2153                         continue;
2154                         }
2155                     httpd_realloc_str(
2156                         &hc->accepte, &hc->maxaccepte,
2157                         strlen( hc->accepte ) + 2 + strlen( cp ) );
2158                     (void) strcat( hc->accepte, ", " );
2159                     }
2160                 else
2161                     httpd_realloc_str(
2162                         &hc->accepte, &hc->maxaccepte, strlen( cp ) );
2163                 (void) strcpy( hc->accepte, cp );
2164                 }
2165             else if ( strncasecmp( buf, "Accept-Language:", 16 ) == 0 )
2166                 {
2167                 cp = &buf[16];
2168                 cp += strspn( cp, " \t" );
2169                 hc->acceptl = cp;
2170                 }
2171             else if ( strncasecmp( buf, "If-Modified-Since:", 18 ) == 0 )
2172                 {
2173                 cp = &buf[18];
2174                 hc->if_modified_since = tdate_parse( cp );
2175                 if ( hc->if_modified_since == (time_t) -1 )
2176                     syslog( LOG_DEBUG, "unparsable time: %.80s", cp );
2177                 }
2178             else if ( strncasecmp( buf, "Cookie:", 7 ) == 0 )
2179                 {
2180                 cp = &buf[7];
2181                 cp += strspn( cp, " \t" );
2182                 hc->cookie = cp;
2183                 }
2184             else if ( strncasecmp( buf, "Range:", 6 ) == 0 )
2185                 {
2186                 /* Only support %d- and %d-%d, not %d-%d,%d-%d or -%d. */
2187                 if ( strchr( buf, ',' ) == (char*) 0 )
2188                     {
2189                     char* cp_dash;
2190                     cp = strpbrk( buf, "=" );
2191                     if ( cp != (char*) 0 )
2192                         {
2193                         cp_dash = strchr( cp + 1, '-' );
2194                         if ( cp_dash != (char*) 0 && cp_dash != cp + 1 )
2195                             {
2196                             *cp_dash = '\0';
2197                             hc->got_range = 1;
2198                             hc->first_byte_index = atoll( cp + 1 );
2199                             if ( hc->first_byte_index < 0 )
2200                                 hc->first_byte_index = 0;
2201                             if ( isdigit( (int) cp_dash[1] ) )
2202                                 {
2203                                 hc->last_byte_index = atoll( cp_dash + 1 );
2204                                 if ( hc->last_byte_index < 0 )
2205                                     hc->last_byte_index = -1;
2206                                 }
2207                             }
2208                         }
2209                     }
2210                 }
2211             else if ( strncasecmp( buf, "Range-If:", 9 ) == 0 ||
2212                       strncasecmp( buf, "If-Range:", 9 ) == 0 )
2213                 {
2214                 cp = &buf[9];
2215                 hc->range_if = tdate_parse( cp );
2216                 if ( hc->range_if == (time_t) -1 )
2217                     syslog( LOG_DEBUG, "unparsable time: %.80s", cp );
2218                 }
2219             else if ( strncasecmp( buf, "Content-Type:", 13 ) == 0 )
2220                 {
2221                 cp = &buf[13];
2222                 cp += strspn( cp, " \t" );
2223                 hc->contenttype = cp;
2224                 }
2225             else if ( strncasecmp( buf, "Content-Length:", 15 ) == 0 )
2226                 {
2227                 cp = &buf[15];
2228                 hc->contentlength = atol( cp );
2229                 }
2230             else if ( strncasecmp( buf, "Authorization:", 14 ) == 0 )
2231                 {
2232                 cp = &buf[14];
2233                 cp += strspn( cp, " \t" );
2234                 hc->authorization = cp;
2235                 }
2236             else if ( strncasecmp( buf, "Connection:", 11 ) == 0 )
2237                 {
2238                 cp = &buf[11];
2239                 cp += strspn( cp, " \t" );
2240                 if ( strcasecmp( cp, "keep-alive" ) == 0 )
2241                     hc->keep_alive = 1;
2242                 }
2243 #ifdef LOG_UNKNOWN_HEADERS
2244             else if ( strncasecmp( buf, "Accept-Charset:", 15 ) == 0 ||
2245                       strncasecmp( buf, "Accept-Language:", 16 ) == 0 ||
2246                       strncasecmp( buf, "Agent:", 6 ) == 0 ||
2247                       strncasecmp( buf, "Cache-Control:", 14 ) == 0 ||
2248                       strncasecmp( buf, "Cache-Info:", 11 ) == 0 ||
2249                       strncasecmp( buf, "Charge-To:", 10 ) == 0 ||
2250                       strncasecmp( buf, "Client-IP:", 10 ) == 0 ||
2251                       strncasecmp( buf, "Date:", 5 ) == 0 ||
2252                       strncasecmp( buf, "Extension:", 10 ) == 0 ||
2253                       strncasecmp( buf, "Forwarded:", 10 ) == 0 ||
2254                       strncasecmp( buf, "From:", 5 ) == 0 ||
2255                       strncasecmp( buf, "HTTP-Version:", 13 ) == 0 ||
2256                       strncasecmp( buf, "Max-Forwards:", 13 ) == 0 ||
2257                       strncasecmp( buf, "Message-Id:", 11 ) == 0 ||
2258                       strncasecmp( buf, "MIME-Version:", 13 ) == 0 ||
2259                       strncasecmp( buf, "Negotiate:", 10 ) == 0 ||
2260                       strncasecmp( buf, "Pragma:", 7 ) == 0 ||
2261                       strncasecmp( buf, "Proxy-Agent:", 12 ) == 0 ||
2262                       strncasecmp( buf, "Proxy-Connection:", 17 ) == 0 ||
2263                       strncasecmp( buf, "Security-Scheme:", 16 ) == 0 ||
2264                       strncasecmp( buf, "Session-Id:", 11 ) == 0 ||
2265                       strncasecmp( buf, "UA-Color:", 9 ) == 0 ||
2266                       strncasecmp( buf, "UA-CPU:", 7 ) == 0 ||
2267                       strncasecmp( buf, "UA-Disp:", 8 ) == 0 ||
2268                       strncasecmp( buf, "UA-OS:", 6 ) == 0 ||
2269                       strncasecmp( buf, "UA-Pixels:", 10 ) == 0 ||
2270                       strncasecmp( buf, "User:", 5 ) == 0 ||
2271                       strncasecmp( buf, "Via:", 4 ) == 0 ||
2272                       strncasecmp( buf, "X-", 2 ) == 0 )
2273                 ; /* ignore */
2274             else
2275                 syslog( LOG_DEBUG, "unknown request header: %.80s", buf );
2276 #endif /* LOG_UNKNOWN_HEADERS */
2277             }
2278         }
2279
2280     if ( hc->one_one )
2281         {
2282         /* Check that HTTP/1.1 requests specify a host, as required. */
2283         if ( hc->reqhost[0] == '\0' && hc->hdrhost[0] == '\0' )
2284             {
2285             httpd_send_err( hc, 400, httpd_err400title, "", httpd_err400form, "" );
2286             return -1;
2287             }
2288
2289         /* If the client wants to do keep-alives, it might also be doing
2290         ** pipelining.  There's no way for us to tell.  Since we don't
2291         ** implement keep-alives yet, if we close such a connection there
2292         ** might be unread pipelined requests waiting.  So, we have to
2293         ** do a lingering close.
2294         */
2295         if ( hc->keep_alive )
2296             hc->should_linger = 1;
2297         }
2298
2299     /* Ok, the request has been parsed.  Now we resolve stuff that
2300     ** may require the entire request.
2301     */
2302
2303     /* Copy original filename to expanded filename. */
2304     httpd_realloc_str(
2305         &hc->expnfilename, &hc->maxexpnfilename, strlen( hc->origfilename ) );
2306     (void) strcpy( hc->expnfilename, hc->origfilename );
2307
2308     /* Tilde mapping. */
2309     if ( hc->expnfilename[0] == '~' )
2310         {
2311 #ifdef TILDE_MAP_1
2312         if ( ! tilde_map_1( hc ) )
2313             {
2314             httpd_send_err( hc, 404, err404title, "", err404form, hc->encodedurl );
2315             return -1;
2316             }
2317 #endif /* TILDE_MAP_1 */
2318 #ifdef TILDE_MAP_2
2319         if ( ! tilde_map_2( hc ) )
2320             {
2321             httpd_send_err( hc, 404, err404title, "", err404form, hc->encodedurl );
2322             return -1;
2323             }
2324 #endif /* TILDE_MAP_2 */
2325         }
2326
2327     /* Virtual host mapping. */
2328     if ( hc->hs->vhost )
2329         if ( ! vhost_map( hc ) )
2330             {
2331             httpd_send_err( hc, 500, err500title, "", err500form, hc->encodedurl );
2332             return -1;
2333             }
2334
2335     /* Expand all symbolic links in the filename.  This also gives us
2336     ** any trailing non-existing components, for pathinfo.
2337     */
2338     cp = expand_symlinks( hc->expnfilename, &pi, hc->hs->no_symlink_check, hc->tildemapped );
2339     if ( cp == (char*) 0 )
2340         {
2341         httpd_send_err( hc, 500, err500title, "", err500form, hc->encodedurl );
2342         return -1;
2343         }
2344     httpd_realloc_str( &hc->expnfilename, &hc->maxexpnfilename, strlen( cp ) );
2345     (void) strcpy( hc->expnfilename, cp );
2346     httpd_realloc_str( &hc->pathinfo, &hc->maxpathinfo, strlen( pi ) );
2347     (void) strcpy( hc->pathinfo, pi );
2348
2349     /* Remove pathinfo stuff from the original filename too. */
2350     if ( hc->pathinfo[0] != '\0' )
2351         {
2352         int i;
2353         i = strlen( hc->origfilename ) - strlen( hc->pathinfo );
2354         if ( i > 0 && strcmp( &hc->origfilename[i], hc->pathinfo ) == 0 )
2355             hc->origfilename[i - 1] = '\0';
2356         }
2357
2358     /* If the expanded filename is an absolute path, check that it's still
2359     ** within the current directory or the alternate directory.
2360     */
2361     if ( hc->expnfilename[0] == '/' )
2362         {
2363         if ( strncmp(
2364                  hc->expnfilename, hc->hs->cwd, strlen( hc->hs->cwd ) ) == 0 )
2365             {
2366             /* Elide the current directory. */
2367             (void) ol_strcpy(
2368                 hc->expnfilename, &hc->expnfilename[strlen( hc->hs->cwd )] );
2369             }
2370 #ifdef TILDE_MAP_2
2371         else if ( hc->altdir[0] != '\0' &&
2372                   ( strncmp(
2373                        hc->expnfilename, hc->altdir,
2374                        strlen( hc->altdir ) ) == 0 &&
2375                     ( hc->expnfilename[strlen( hc->altdir )] == '\0' ||
2376                       hc->expnfilename[strlen( hc->altdir )] == '/' ) ) )
2377             {}
2378 #endif /* TILDE_MAP_2 */
2379         else
2380             {
2381             syslog(
2382                 LOG_NOTICE, "%.80s URL \"%.80s\" goes outside the web tree",
2383                 httpd_ntoa( &hc->client_addr ), hc->encodedurl );
2384             httpd_send_err(
2385                 hc, 403, err403title, "",
2386                 ERROR_FORM( err403form, "The requested URL '%.80s' resolves to a file outside the permitted web server directory tree.\n" ),
2387                 hc->encodedurl );
2388             return -1;
2389             }
2390         }
2391
2392     return 0;
2393     }
2394
2395
2396 static char*
2397 bufgets( httpd_conn* hc )
2398     {
2399     int i;
2400     char c;
2401
2402     for ( i = hc->checked_idx; hc->checked_idx < hc->read_idx; ++hc->checked_idx )
2403         {
2404         c = hc->read_buf[hc->checked_idx];
2405         if ( c == '\012' || c == '\015' )
2406             {
2407             hc->read_buf[hc->checked_idx] = '\0';
2408             ++hc->checked_idx;
2409             if ( c == '\015' && hc->checked_idx < hc->read_idx &&
2410                  hc->read_buf[hc->checked_idx] == '\012' )
2411                 {
2412                 hc->read_buf[hc->checked_idx] = '\0';
2413                 ++hc->checked_idx;
2414                 }
2415             return &(hc->read_buf[i]);
2416             }
2417         }
2418     return (char*) 0;
2419     }
2420
2421
2422 static void
2423 de_dotdot( char* file )
2424     {
2425     char* cp;
2426     char* cp2;
2427     int l;
2428
2429     /* Collapse any multiple / sequences. */
2430     while ( ( cp = strstr( file, "//") ) != (char*) 0 )
2431         {
2432         for ( cp2 = cp + 2; *cp2 == '/'; ++cp2 )
2433             continue;
2434         (void) ol_strcpy( cp + 1, cp2 );
2435         }
2436
2437     /* Remove leading ./ and any /./ sequences. */
2438     while ( strncmp( file, "./", 2 ) == 0 )
2439         (void) ol_strcpy( file, file + 2 );
2440     while ( ( cp = strstr( file, "/./") ) != (char*) 0 )
2441         (void) ol_strcpy( cp, cp + 2 );
2442
2443     /* Alternate between removing leading ../ and removing xxx/../ */
2444     for (;;)
2445         {
2446         while ( strncmp( file, "../", 3 ) == 0 )
2447             (void) ol_strcpy( file, file + 3 );
2448         cp = strstr( file, "/../" );
2449         if ( cp == (char*) 0 )
2450             break;
2451         for ( cp2 = cp - 1; cp2 >= file && *cp2 != '/'; --cp2 )
2452             continue;
2453         (void) ol_strcpy( cp2 + 1, cp + 4 );
2454         }
2455
2456     /* Also elide any xxx/.. at the end. */
2457     while ( ( l = strlen( file ) ) > 3 &&
2458             strcmp( ( cp = file + l - 3 ), "/.." ) == 0 )
2459         {
2460         for ( cp2 = cp - 1; cp2 >= file && *cp2 != '/'; --cp2 )
2461             continue;
2462         if ( cp2 < file )
2463             break;
2464         *cp2 = '\0';
2465         }
2466     }
2467
2468
2469 void
2470 httpd_close_conn( httpd_conn* hc, struct timeval* nowP )
2471     {
2472     make_log_entry( hc, nowP );
2473
2474     if ( hc->file_address != (char*) 0 )
2475         {
2476         mmc_unmap( hc->file_address, &(hc->sb), nowP );
2477         hc->file_address = (char*) 0;
2478         }
2479     if ( hc->conn_fd >= 0 )
2480         {
2481         (void) close( hc->conn_fd );
2482         hc->conn_fd = -1;
2483         }
2484     }
2485
2486 void
2487 httpd_destroy_conn( httpd_conn* hc )
2488     {
2489     if ( hc->initialized )
2490         {
2491         free( (void*) hc->read_buf );
2492         free( (void*) hc->decodedurl );
2493         free( (void*) hc->origfilename );
2494         free( (void*) hc->expnfilename );
2495         free( (void*) hc->encodings );
2496         free( (void*) hc->pathinfo );
2497         free( (void*) hc->query );
2498         free( (void*) hc->accept );
2499         free( (void*) hc->accepte );
2500         free( (void*) hc->reqhost );
2501         free( (void*) hc->hostdir );
2502         free( (void*) hc->remoteuser );
2503         free( (void*) hc->response );
2504 #ifdef TILDE_MAP_2
2505         free( (void*) hc->altdir );
2506 #endif /* TILDE_MAP_2 */
2507         hc->initialized = 0;
2508         }
2509     }
2510
2511
2512 struct mime_entry {
2513     char* ext;
2514     size_t ext_len;
2515     char* val;
2516     size_t val_len;
2517     };
2518 static struct mime_entry enc_tab[] = {
2519 #include "mime_encodings.h"
2520     };
2521 static const int n_enc_tab = sizeof(enc_tab) / sizeof(*enc_tab);
2522 static struct mime_entry typ_tab[] = {
2523 #include "mime_types.h"
2524     };
2525 static const int n_typ_tab = sizeof(typ_tab) / sizeof(*typ_tab);
2526
2527
2528 /* qsort comparison routine */
2529 static int
2530 ext_compare( const void* v1, const void* v2 )
2531     {
2532     const struct mime_entry* m1 = (const struct mime_entry*) v1;
2533     const struct mime_entry* m2 = (const struct mime_entry*) v2;
2534
2535     return strcmp( m1->ext, m2->ext );
2536     }
2537
2538
2539 static void
2540 init_mime( void )
2541     {
2542     int i;
2543
2544     /* Sort the tables so we can do binary search. */
2545     qsort( enc_tab, n_enc_tab, sizeof(*enc_tab), ext_compare );
2546     qsort( typ_tab, n_typ_tab, sizeof(*typ_tab), ext_compare );
2547
2548     /* Fill in the lengths. */
2549     for ( i = 0; i < n_enc_tab; ++i )
2550         {
2551         enc_tab[i].ext_len = strlen( enc_tab[i].ext );
2552         enc_tab[i].val_len = strlen( enc_tab[i].val );
2553         }
2554     for ( i = 0; i < n_typ_tab; ++i )
2555         {
2556         typ_tab[i].ext_len = strlen( typ_tab[i].ext );
2557         typ_tab[i].val_len = strlen( typ_tab[i].val );
2558         }
2559
2560     }
2561
2562
2563 /* Figure out MIME encodings and type based on the filename.  Multiple
2564 ** encodings are separated by commas, and are listed in the order in
2565 ** which they were applied to the file.
2566 */
2567 static void
2568 figure_mime( httpd_conn* hc )
2569     {
2570     char* prev_dot;
2571     char* dot;
2572     char* ext;
2573     int me_indexes[100], n_me_indexes;
2574     size_t ext_len, encodings_len;
2575     int i, top, bot, mid;
2576     int r;
2577     char* default_type = "text/plain; charset=%s";
2578
2579     /* Peel off encoding extensions until there aren't any more. */
2580     n_me_indexes = 0;
2581     for ( prev_dot = &hc->expnfilename[strlen(hc->expnfilename)]; ; prev_dot = dot )
2582         {
2583         for ( dot = prev_dot - 1; dot >= hc->expnfilename && *dot != '.'; --dot )
2584             ;
2585         if ( dot < hc->expnfilename )
2586             {
2587             /* No dot found.  No more encoding extensions, and no type
2588             ** extension either.
2589             */
2590             hc->type = default_type;
2591             goto done;
2592             }
2593         ext = dot + 1;
2594         ext_len = prev_dot - ext;
2595         /* Search the encodings table.  Linear search is fine here, there
2596         ** are only a few entries.
2597         */
2598         for ( i = 0; i < n_enc_tab; ++i )
2599             {
2600             if ( ext_len == enc_tab[i].ext_len && strncasecmp( ext, enc_tab[i].ext, ext_len ) == 0 )
2601                 {
2602                 if ( n_me_indexes < sizeof(me_indexes)/sizeof(*me_indexes) )
2603                     {
2604                     me_indexes[n_me_indexes] = i;
2605                     ++n_me_indexes;
2606                     }
2607                 goto next;
2608                 }
2609             }
2610         /* No encoding extension found.  Break and look for a type extension. */
2611         break;
2612
2613         next: ;
2614         }
2615
2616     /* Binary search for a matching type extension. */
2617     top = n_typ_tab - 1;
2618     bot = 0;
2619     while ( top >= bot )
2620         {
2621         mid = ( top + bot ) / 2;
2622         r = strncasecmp( ext, typ_tab[mid].ext, ext_len );
2623         if ( r < 0 )
2624             top = mid - 1;
2625         else if ( r > 0 )
2626             bot = mid + 1;
2627         else
2628             if ( ext_len < typ_tab[mid].ext_len )
2629                 top = mid - 1;
2630             else if ( ext_len > typ_tab[mid].ext_len )
2631                 bot = mid + 1;
2632             else
2633                 {
2634                 hc->type = typ_tab[mid].val;
2635                 goto done;
2636                 }
2637         }
2638     hc->type = default_type;
2639
2640     done:
2641
2642     /* The last thing we do is actually generate the mime-encoding header. */
2643     hc->encodings[0] = '\0';
2644     encodings_len = 0;
2645     for ( i = n_me_indexes - 1; i >= 0; --i )
2646         {
2647         httpd_realloc_str(
2648             &hc->encodings, &hc->maxencodings,
2649             encodings_len + enc_tab[me_indexes[i]].val_len + 1 );
2650         if ( hc->encodings[0] != '\0' )
2651             {
2652             (void) strcpy( &hc->encodings[encodings_len], "," );
2653             ++encodings_len;
2654             }
2655         (void) strcpy( &hc->encodings[encodings_len], enc_tab[me_indexes[i]].val );
2656         encodings_len += enc_tab[me_indexes[i]].val_len;
2657         }
2658
2659     }
2660
2661
2662 #ifdef CGI_TIMELIMIT
2663 static void
2664 cgi_kill2( ClientData client_data, struct timeval* nowP )
2665     {
2666     pid_t pid;
2667
2668     pid = (pid_t) client_data.i;
2669     if ( kill( pid, SIGKILL ) == 0 )
2670         syslog( LOG_WARNING, "hard-killed CGI process %d", pid );
2671     }
2672
2673 static void
2674 cgi_kill( ClientData client_data, struct timeval* nowP )
2675     {
2676     pid_t pid;
2677
2678     pid = (pid_t) client_data.i;
2679     if ( kill( pid, SIGINT ) == 0 )
2680         {
2681         syslog( LOG_WARNING, "killed CGI process %d", pid );
2682         /* In case this isn't enough, schedule an uncatchable kill. */
2683         if ( tmr_create( nowP, cgi_kill2, client_data, 5 * 1000L, 0 ) == (Timer*) 0 )
2684             {
2685             syslog( LOG_CRIT, "tmr_create(cgi_kill2) failed" );
2686             exit( 1 );
2687             }
2688         }
2689     }
2690 #endif /* CGI_TIMELIMIT */
2691
2692
2693 #ifdef GENERATE_INDEXES
2694
2695 /* qsort comparison routine */
2696 static int
2697 name_compare( const void* v1, const void* v2 )
2698     {
2699     const char** c1 = (const char**) v1;
2700     const char** c2 = (const char**) v2;
2701     return strcmp( *c1, *c2 );
2702     }
2703
2704
2705 static int
2706 ls( httpd_conn* hc )
2707     {
2708     DIR* dirp;
2709     struct dirent* de;
2710     int namlen;
2711     static int maxnames = 0;
2712     int nnames;
2713     static char* names;
2714     static char** nameptrs;
2715     static char* name;
2716     static size_t maxname = 0;
2717     static char* rname;
2718     static size_t maxrname = 0;
2719     static char* encrname;
2720     static size_t maxencrname = 0;
2721     FILE* fp;
2722     int i, r;
2723     struct stat sb;
2724     struct stat lsb;
2725     char modestr[20];
2726     char* linkprefix;
2727     char lnk[MAXPATHLEN+1];
2728     int linklen;
2729     char* fileclass;
2730     time_t now;
2731     char* timestr;
2732     ClientData client_data;
2733
2734     dirp = opendir( hc->expnfilename );
2735     if ( dirp == (DIR*) 0 )
2736         {
2737         syslog( LOG_ERR, "opendir %.80s - %m", hc->expnfilename );
2738         httpd_send_err( hc, 404, err404title, "", err404form, hc->encodedurl );
2739         return -1;
2740         }
2741
2742     if ( hc->method == METHOD_HEAD )
2743         {
2744         closedir( dirp );
2745         send_mime(
2746             hc, 200, ok200title, "", "", "text/html; charset=%s", (off_t) -1,
2747             hc->sb.st_mtime );
2748         }
2749     else if ( hc->method == METHOD_GET )
2750         {
2751         if ( hc->hs->cgi_limit != 0 && hc->hs->cgi_count >= hc->hs->cgi_limit )
2752             {
2753             closedir( dirp );
2754             httpd_send_err(
2755                 hc, 503, httpd_err503title, "", httpd_err503form,
2756                 hc->encodedurl );
2757             return -1;
2758             }
2759         ++hc->hs->cgi_count;
2760         r = fork( );
2761         if ( r < 0 )
2762             {
2763             syslog( LOG_ERR, "fork - %m" );
2764             closedir( dirp );
2765             httpd_send_err(
2766                 hc, 500, err500title, "", err500form, hc->encodedurl );
2767             return -1;
2768             }
2769         if ( r == 0 )
2770             {
2771             /* Child process. */
2772             sub_process = 1;
2773             httpd_unlisten( hc->hs );
2774             send_mime(
2775                 hc, 200, ok200title, "", "", "text/html; charset=%s",
2776                 (off_t) -1, hc->sb.st_mtime );
2777             httpd_write_response( hc );
2778
2779 #ifdef CGI_NICE
2780             /* Set priority. */
2781             (void) nice( CGI_NICE );
2782 #endif /* CGI_NICE */
2783
2784             /* Open a stdio stream so that we can use fprintf, which is more
2785             ** efficient than a bunch of separate write()s.  We don't have
2786             ** to worry about double closes or file descriptor leaks cause
2787             ** we're in a subprocess.
2788             */
2789             fp = fdopen( hc->conn_fd, "w" );
2790             if ( fp == (FILE*) 0 )
2791                 {
2792                 syslog( LOG_ERR, "fdopen - %m" );
2793                 httpd_send_err(
2794                     hc, 500, err500title, "", err500form, hc->encodedurl );
2795                 httpd_write_response( hc );
2796                 closedir( dirp );
2797                 exit( 1 );
2798                 }
2799
2800             (void) fprintf( fp, "\
2801 <!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\n\
2802 \n\
2803 <html>\n\
2804 \n\
2805   <head>\n\
2806     <meta http-equiv=\"Content-type\" content=\"text/html;charset=UTF-8\">\n\
2807     <title>Index of %.80s</title>\n\
2808   </head>\n\
2809 \n\
2810   <body bgcolor=\"#99cc99\" text=\"#000000\" link=\"#2020ff\" vlink=\"#4040cc\">\n\
2811 \n\
2812     <h2>Index of %.80s</h2>\n\
2813 \n\
2814     <pre>\n\
2815 mode  links    bytes  last-changed  name\n\
2816     <hr>",
2817                 hc->encodedurl, hc->encodedurl );
2818
2819             /* Read in names. */
2820             nnames = 0;
2821             while ( ( de = readdir( dirp ) ) != 0 )     /* dirent or direct */
2822                 {
2823                 if ( nnames >= maxnames )
2824                     {
2825                     if ( maxnames == 0 )
2826                         {
2827                         maxnames = 100;
2828                         names = NEW( char, maxnames * ( MAXPATHLEN + 1 ) );
2829                         nameptrs = NEW( char*, maxnames );
2830                         }
2831                     else
2832                         {
2833                         maxnames *= 2;
2834                         names = RENEW( names, char, maxnames * ( MAXPATHLEN + 1 ) );
2835                         nameptrs = RENEW( nameptrs, char*, maxnames );
2836                         }
2837                     if ( names == (char*) 0 || nameptrs == (char**) 0 )
2838                         {
2839                         syslog( LOG_ERR, "out of memory reallocating directory names" );
2840                         exit( 1 );
2841                         }
2842                     for ( i = 0; i < maxnames; ++i )
2843                         nameptrs[i] = &names[i * ( MAXPATHLEN + 1 )];
2844                     }
2845                 namlen = NAMLEN(de);
2846                 (void) strncpy( nameptrs[nnames], de->d_name, namlen );
2847                 nameptrs[nnames][namlen] = '\0';
2848                 ++nnames;
2849                 }
2850             closedir( dirp );
2851
2852             /* Sort the names. */
2853             qsort( nameptrs, nnames, sizeof(*nameptrs), name_compare );
2854
2855             /* Generate output. */
2856             for ( i = 0; i < nnames; ++i )
2857                 {
2858                 httpd_realloc_str(
2859                     &name, &maxname,
2860                     strlen( hc->expnfilename ) + 1 + strlen( nameptrs[i] ) );
2861                 httpd_realloc_str(
2862                     &rname, &maxrname,
2863                     strlen( hc->origfilename ) + 1 + strlen( nameptrs[i] ) );
2864                 if ( hc->expnfilename[0] == '\0' ||
2865                      strcmp( hc->expnfilename, "." ) == 0 )
2866                     {
2867                     (void) strcpy( name, nameptrs[i] );
2868                     (void) strcpy( rname, nameptrs[i] );
2869                     }
2870                 else
2871                     {
2872                     (void) my_snprintf( name, maxname,
2873                         "%s/%s", hc->expnfilename, nameptrs[i] );
2874                     if ( strcmp( hc->origfilename, "." ) == 0 )
2875                         (void) my_snprintf( rname, maxrname,
2876                             "%s", nameptrs[i] );
2877                     else
2878                         (void) my_snprintf( rname, maxrname,
2879                             "%s%s", hc->origfilename, nameptrs[i] );
2880                     }
2881                 httpd_realloc_str(
2882                     &encrname, &maxencrname, 3 * strlen( rname ) + 1 );
2883                 strencode( encrname, maxencrname, rname );
2884
2885                 if ( stat( name, &sb ) < 0 || lstat( name, &lsb ) < 0 )
2886                     continue;
2887
2888                 linkprefix = "";
2889                 lnk[0] = '\0';
2890                 /* Break down mode word.  First the file type. */
2891                 switch ( lsb.st_mode & S_IFMT )
2892                     {
2893                     case S_IFIFO:  modestr[0] = 'p'; break;
2894                     case S_IFCHR:  modestr[0] = 'c'; break;
2895                     case S_IFDIR:  modestr[0] = 'd'; break;
2896                     case S_IFBLK:  modestr[0] = 'b'; break;
2897                     case S_IFREG:  modestr[0] = '-'; break;
2898                     case S_IFSOCK: modestr[0] = 's'; break;
2899                     case S_IFLNK:  modestr[0] = 'l';
2900                     linklen = readlink( name, lnk, sizeof(lnk) - 1 );
2901                     if ( linklen != -1 )
2902                         {
2903                         lnk[linklen] = '\0';
2904                         linkprefix = " -&gt; ";
2905                         }
2906                     break;
2907                     default:       modestr[0] = '?'; break;
2908                     }
2909                 /* Now the world permissions.  Owner and group permissions
2910                 ** are not of interest to web clients.
2911                 */
2912                 modestr[1] = ( lsb.st_mode & S_IROTH ) ? 'r' : '-';
2913                 modestr[2] = ( lsb.st_mode & S_IWOTH ) ? 'w' : '-';
2914                 modestr[3] = ( lsb.st_mode & S_IXOTH ) ? 'x' : '-';
2915                 modestr[4] = '\0';
2916
2917                 /* We also leave out the owner and group name, they are
2918                 ** also not of interest to web clients.  Plus if we're
2919                 ** running under chroot(), they would require a copy
2920                 ** of /etc/passwd and /etc/group, which we want to avoid.
2921                 */
2922
2923                 /* Get time string. */
2924                 now = time( (time_t*) 0 );
2925                 timestr = ctime( &lsb.st_mtime );
2926                 timestr[ 0] = timestr[ 4];
2927                 timestr[ 1] = timestr[ 5];
2928                 timestr[ 2] = timestr[ 6];
2929                 timestr[ 3] = ' ';
2930                 timestr[ 4] = timestr[ 8];
2931                 timestr[ 5] = timestr[ 9];
2932                 timestr[ 6] = ' ';
2933                 if ( now - lsb.st_mtime > 60*60*24*182 )        /* 1/2 year */
2934                     {
2935                     timestr[ 7] = ' ';
2936                     timestr[ 8] = timestr[20];
2937                     timestr[ 9] = timestr[21];
2938                     timestr[10] = timestr[22];
2939                     timestr[11] = timestr[23];
2940                     }
2941                 else
2942                     {
2943                     timestr[ 7] = timestr[11];
2944                     timestr[ 8] = timestr[12];
2945                     timestr[ 9] = ':';
2946                     timestr[10] = timestr[14];
2947                     timestr[11] = timestr[15];
2948                     }
2949                 timestr[12] = '\0';
2950
2951                 /* The ls -F file class. */
2952                 switch ( sb.st_mode & S_IFMT )
2953                     {
2954                     case S_IFDIR:  fileclass = "/"; break;
2955                     case S_IFSOCK: fileclass = "="; break;
2956                     case S_IFLNK:  fileclass = "@"; break;
2957                     default:
2958                     fileclass = ( sb.st_mode & S_IXOTH ) ? "*" : "";
2959                     break;
2960                     }
2961
2962                 /* And print. */
2963                 (void)  fprintf( fp,
2964                    "%s %3ld  %10lld  %s  <a href=\"/%.500s%s\">%.80s</a>%s%s%s\n",
2965                     modestr, (long) lsb.st_nlink, (long long) lsb.st_size,
2966                     timestr, encrname, S_ISDIR(sb.st_mode) ? "/" : "",
2967                     nameptrs[i], linkprefix, lnk, fileclass );
2968                 }
2969
2970             (void) fprintf( fp, "    </pre>\n  </body>\n</html>\n" );
2971             (void) fclose( fp );
2972             exit( 0 );
2973             }
2974
2975         /* Parent process. */
2976         closedir( dirp );
2977         syslog( LOG_DEBUG, "spawned indexing process %d for directory '%.200s'", r, hc->expnfilename );
2978 #ifdef CGI_TIMELIMIT
2979         /* Schedule a kill for the child process, in case it runs too long */
2980         client_data.i = r;
2981         if ( tmr_create( (struct timeval*) 0, cgi_kill, client_data, CGI_TIMELIMIT * 1000L, 0 ) == (Timer*) 0 )
2982             {
2983             syslog( LOG_CRIT, "tmr_create(cgi_kill ls) failed" );
2984             exit( 1 );
2985             }
2986 #endif /* CGI_TIMELIMIT */
2987         hc->status = 200;
2988         hc->bytes_sent = CGI_BYTECOUNT;
2989         hc->should_linger = 0;
2990         }
2991     else
2992         {
2993         closedir( dirp );
2994         httpd_send_err(
2995             hc, 501, err501title, "", err501form, httpd_method_str( hc->method ) );
2996         return -1;
2997         }
2998
2999     return 0;
3000     }
3001
3002 #endif /* GENERATE_INDEXES */
3003
3004
3005 static char*
3006 build_env( char* fmt, char* arg )
3007     {
3008     char* cp;
3009     size_t size;
3010     static char* buf;
3011     static size_t maxbuf = 0;
3012
3013     size = strlen( fmt ) + strlen( arg );
3014     if ( size > maxbuf )
3015         httpd_realloc_str( &buf, &maxbuf, size );
3016     (void) my_snprintf( buf, maxbuf, fmt, arg );
3017     cp = strdup( buf );
3018     if ( cp == (char*) 0 )
3019         {
3020         syslog( LOG_ERR, "out of memory copying environment variable" );
3021         exit( 1 );
3022         }
3023     return cp;
3024     }
3025
3026
3027 #ifdef SERVER_NAME_LIST
3028 static char*
3029 hostname_map( char* hostname )
3030     {
3031     int len, n;
3032     static char* list[] = { SERVER_NAME_LIST };
3033
3034     len = strlen( hostname );
3035     for ( n = sizeof(list) / sizeof(*list) - 1; n >= 0; --n )
3036         if ( strncasecmp( hostname, list[n], len ) == 0 )
3037             if ( list[n][len] == '/' )  /* check in case of a substring match */
3038                 return &list[n][len + 1];
3039     return (char*) 0;
3040     }
3041 #endif /* SERVER_NAME_LIST */
3042
3043
3044 /* Set up environment variables. Be real careful here to avoid
3045 ** letting malicious clients overrun a buffer.  We don't have
3046 ** to worry about freeing stuff since we're a sub-process.
3047 */
3048 static char**
3049 make_envp( httpd_conn* hc )
3050     {
3051     static char* envp[50];
3052     int envn;
3053     char* cp;
3054     char buf[256];
3055
3056     envn = 0;
3057     envp[envn++] = build_env( "PATH=%s", CGI_PATH );
3058 #ifdef CGI_LD_LIBRARY_PATH
3059     envp[envn++] = build_env( "LD_LIBRARY_PATH=%s", CGI_LD_LIBRARY_PATH );
3060 #endif /* CGI_LD_LIBRARY_PATH */
3061     envp[envn++] = build_env( "SERVER_SOFTWARE=%s", SERVER_SOFTWARE );
3062     if ( hc->hs->vhost && hc->hostname != (char*) 0 && hc->hostname[0] != '\0' )
3063         cp = hc->hostname;
3064     else if ( hc->hdrhost != (char*) 0 && hc->hdrhost[0] != '\0' )
3065         cp = hc->hdrhost;
3066     else if ( hc->reqhost != (char*) 0 && hc->reqhost[0] != '\0' )
3067         cp = hc->reqhost;
3068     else
3069         cp = hc->hs->server_hostname;
3070     if ( cp != (char*) 0 )
3071         envp[envn++] = build_env( "SERVER_NAME=%s", cp );
3072     envp[envn++] = "GATEWAY_INTERFACE=CGI/1.1";
3073     envp[envn++] = build_env("SERVER_PROTOCOL=%s", hc->protocol);
3074     (void) my_snprintf( buf, sizeof(buf), "%d", (int) hc->hs->port );
3075     envp[envn++] = build_env( "SERVER_PORT=%s", buf );
3076     envp[envn++] = build_env(
3077         "REQUEST_METHOD=%s", httpd_method_str( hc->method ) );
3078     if ( hc->pathinfo[0] != '\0' )
3079         {
3080         char* cp2;
3081         size_t l;
3082         envp[envn++] = build_env( "PATH_INFO=/%s", hc->pathinfo );
3083         l = strlen( hc->hs->cwd ) + strlen( hc->pathinfo ) + 1;
3084         cp2 = NEW( char, l );
3085         if ( cp2 != (char*) 0 )
3086             {
3087             (void) my_snprintf( cp2, l, "%s%s", hc->hs->cwd, hc->pathinfo );
3088             envp[envn++] = build_env( "PATH_TRANSLATED=%s", cp2 );
3089             }
3090         }
3091     envp[envn++] = build_env(
3092         "SCRIPT_NAME=/%s", strcmp( hc->origfilename, "." ) == 0 ?
3093         "" : hc->origfilename );
3094     if ( hc->query[0] != '\0')
3095         envp[envn++] = build_env( "QUERY_STRING=%s", hc->query );
3096     envp[envn++] = build_env(
3097         "REMOTE_ADDR=%s", httpd_ntoa( &hc->client_addr ) );
3098     if ( hc->referrer[0] != '\0' )
3099         {
3100         envp[envn++] = build_env( "HTTP_REFERER=%s", hc->referrer );
3101         envp[envn++] = build_env( "HTTP_REFERRER=%s", hc->referrer );
3102         }
3103     if ( hc->useragent[0] != '\0' )
3104         envp[envn++] = build_env( "HTTP_USER_AGENT=%s", hc->useragent );
3105     if ( hc->accept[0] != '\0' )
3106         envp[envn++] = build_env( "HTTP_ACCEPT=%s", hc->accept );
3107     if ( hc->accepte[0] != '\0' )
3108         envp[envn++] = build_env( "HTTP_ACCEPT_ENCODING=%s", hc->accepte );
3109     if ( hc->acceptl[0] != '\0' )
3110         envp[envn++] = build_env( "HTTP_ACCEPT_LANGUAGE=%s", hc->acceptl );
3111     if ( hc->cookie[0] != '\0' )
3112         envp[envn++] = build_env( "HTTP_COOKIE=%s", hc->cookie );
3113     if ( hc->contenttype[0] != '\0' )
3114         envp[envn++] = build_env( "CONTENT_TYPE=%s", hc->contenttype );
3115     if ( hc->hdrhost[0] != '\0' )
3116         envp[envn++] = build_env( "HTTP_HOST=%s", hc->hdrhost );
3117     if ( hc->contentlength != -1 )
3118         {
3119         (void) my_snprintf(
3120             buf, sizeof(buf), "%lu", (unsigned long) hc->contentlength );
3121         envp[envn++] = build_env( "CONTENT_LENGTH=%s", buf );
3122         }
3123     if ( hc->remoteuser[0] != '\0' )
3124         envp[envn++] = build_env( "REMOTE_USER=%s", hc->remoteuser );
3125     if ( hc->authorization[0] != '\0' )
3126         envp[envn++] = build_env( "AUTH_TYPE=%s", "Basic" );
3127         /* We only support Basic auth at the moment. */
3128     if ( getenv( "TZ" ) != (char*) 0 )
3129         envp[envn++] = build_env( "TZ=%s", getenv( "TZ" ) );
3130     envp[envn++] = build_env( "CGI_PATTERN=%s", hc->hs->cgi_pattern );
3131
3132     envp[envn] = (char*) 0;
3133     return envp;
3134     }
3135
3136
3137 /* Set up argument vector.  Again, we don't have to worry about freeing stuff
3138 ** since we're a sub-process.  This gets done after make_envp() because we
3139 ** scribble on hc->query.
3140 */
3141 static char**
3142 make_argp( httpd_conn* hc )
3143     {
3144     char** argp;
3145     int argn;
3146     char* cp1;
3147     char* cp2;
3148
3149     /* By allocating an arg slot for every character in the query, plus
3150     ** one for the filename and one for the NULL, we are guaranteed to
3151     ** have enough.  We could actually use strlen/2.
3152     */
3153     argp = NEW( char*, strlen( hc->query ) + 2 );
3154     if ( argp == (char**) 0 )
3155         return (char**) 0;
3156
3157     argp[0] = strrchr( hc->expnfilename, '/' );
3158     if ( argp[0] != (char*) 0 )
3159         ++argp[0];
3160     else
3161         argp[0] = hc->expnfilename;
3162
3163     argn = 1;
3164     /* According to the CGI spec at http://hoohoo.ncsa.uiuc.edu/cgi/cl.html,
3165     ** "The server should search the query information for a non-encoded =
3166     ** character to determine if the command line is to be used, if it finds
3167     ** one, the command line is not to be used."
3168     */
3169     if ( strchr( hc->query, '=' ) == (char*) 0 )
3170         {
3171         for ( cp1 = cp2 = hc->query; *cp2 != '\0'; ++cp2 )
3172             {
3173             if ( *cp2 == '+' )
3174                 {
3175                 *cp2 = '\0';
3176                 strdecode( cp1, cp1 );
3177                 argp[argn++] = cp1;
3178                 cp1 = cp2 + 1;
3179                 }
3180             }
3181         if ( cp2 != cp1 )
3182             {
3183             strdecode( cp1, cp1 );
3184             argp[argn++] = cp1;
3185             }
3186         }
3187
3188     argp[argn] = (char*) 0;
3189     return argp;
3190     }
3191
3192
3193 /* This routine is used only for POST requests.  It reads the data
3194 ** from the request and sends it to the child process.  The only reason
3195 ** we need to do it this way instead of just letting the child read
3196 ** directly is that we have already read part of the data into our
3197 ** buffer.
3198 */
3199 static void
3200 cgi_interpose_input( httpd_conn* hc, int wfd )
3201     {
3202     size_t c;
3203     ssize_t r;
3204     char buf[1024];
3205
3206     c = hc->read_idx - hc->checked_idx;
3207     if ( c > 0 )
3208         {
3209         if ( httpd_write_fully( wfd, &(hc->read_buf[hc->checked_idx]), c ) != c )
3210             return;
3211         }
3212     while ( c < hc->contentlength )
3213         {
3214         r = read( hc->conn_fd, buf, MIN( sizeof(buf), hc->contentlength - c ) );
3215         if ( r < 0 && ( errno == EINTR || errno == EAGAIN ) )
3216             {
3217             sleep( 1 );
3218             continue;
3219             }
3220         if ( r <= 0 )
3221             return;
3222         if ( httpd_write_fully( wfd, buf, r ) != r )
3223             return;
3224         c += r;
3225         }
3226     post_post_garbage_hack( hc );
3227     }
3228
3229
3230 /* Special hack to deal with broken browsers that send a LF or CRLF
3231 ** after POST data, causing TCP resets - we just read and discard up
3232 ** to 2 bytes.  Unfortunately this doesn't fix the problem for CGIs
3233 ** which avoid the interposer process due to their POST data being
3234 ** short.  Creating an interposer process for all POST CGIs is
3235 ** unacceptably expensive.  The eventual fix will come when interposing
3236 ** gets integrated into the main loop as a tasklet instead of a process.
3237 */
3238 static void
3239 post_post_garbage_hack( httpd_conn* hc )
3240     {
3241     char buf[2];
3242
3243     /* If we are in a sub-process, turn on no-delay mode in case we
3244     ** previously cleared it.
3245     */
3246     if ( sub_process )
3247         httpd_set_ndelay( hc->conn_fd );
3248     /* And read up to 2 bytes. */
3249     (void) read( hc->conn_fd, buf, sizeof(buf) );
3250     }
3251
3252
3253 /* This routine is used for parsed-header CGIs.  The idea here is that the
3254 ** CGI can return special headers such as "Status:" and "Location:" which
3255 ** change the return status of the response.  Since the return status has to
3256 ** be the very first line written out, we have to accumulate all the headers
3257 ** and check for the special ones before writing the status.  Then we write
3258 ** out the saved headers and proceed to echo the rest of the response.
3259 */
3260 static void
3261 cgi_interpose_output( httpd_conn* hc, int rfd )
3262     {
3263     int r;
3264     char buf[1024];
3265     size_t headers_size, headers_len;
3266     char* headers;
3267     char* br;
3268     int status;
3269     char* title;
3270     char* cp;
3271
3272     /* Make sure the connection is in blocking mode.  It should already
3273     ** be blocking, but we might as well be sure.
3274     */
3275     httpd_clear_ndelay( hc->conn_fd );
3276
3277     /* Slurp in all headers. */
3278     headers_size = 0;
3279     httpd_realloc_str( &headers, &headers_size, 500 );
3280     headers_len = 0;
3281     for (;;)
3282         {
3283         r = read( rfd, buf, sizeof(buf) );
3284         if ( r < 0 && ( errno == EINTR || errno == EAGAIN ) )
3285             {
3286             sleep( 1 );
3287             continue;
3288             }
3289         if ( r <= 0 )
3290             {
3291             br = &(headers[headers_len]);
3292             break;
3293             }
3294         httpd_realloc_str( &headers, &headers_size, headers_len + r );
3295         (void) memmove( &(headers[headers_len]), buf, r );
3296         headers_len += r;
3297         headers[headers_len] = '\0';
3298         if ( ( br = strstr( headers, "\015\012\015\012" ) ) != (char*) 0 ||
3299              ( br = strstr( headers, "\012\012" ) ) != (char*) 0 )
3300             break;
3301         }
3302
3303     /* If there were no headers, bail. */
3304     if ( headers[0] == '\0' )
3305         return;
3306
3307     /* Figure out the status.  Look for a Status: or Location: header;
3308     ** else if there's an HTTP header line, get it from there; else
3309     ** default to 200.
3310     */
3311     status = 200;
3312     if ( strncmp( headers, "HTTP/", 5 ) == 0 )
3313         {
3314         cp = headers;
3315         cp += strcspn( cp, " \t" );
3316         status = atoi( cp );
3317         }
3318     if ( ( cp = strstr( headers, "Location:" ) ) != (char*) 0 &&
3319          cp < br &&
3320          ( cp == headers || *(cp-1) == '\012' ) )
3321         status = 302;
3322     if ( ( cp = strstr( headers, "Status:" ) ) != (char*) 0 &&
3323          cp < br &&
3324          ( cp == headers || *(cp-1) == '\012' ) )
3325         {
3326         cp += 7;
3327         cp += strspn( cp, " \t" );
3328         status = atoi( cp );
3329         }
3330
3331     /* Write the status line. */
3332     switch ( status )
3333         {
3334         case 200: title = ok200title; break;
3335         case 302: title = err302title; break;
3336         case 304: title = err304title; break;
3337         case 400: title = httpd_err400title; break;
3338 #ifdef AUTH_FILE
3339         case 401: title = err401title; break;
3340 #endif /* AUTH_FILE */
3341         case 403: title = err403title; break;
3342         case 404: title = err404title; break;
3343         case 408: title = httpd_err408title; break;
3344         case 451: title = err451title; break;
3345         case 500: title = err500title; break;
3346         case 501: title = err501title; break;
3347         case 503: title = httpd_err503title; break;
3348         default: title = "Something"; break;
3349         }
3350     (void) my_snprintf( buf, sizeof(buf), "HTTP/1.0 %d %s\015\012", status, title );
3351     (void) httpd_write_fully( hc->conn_fd, buf, strlen( buf ) );
3352
3353     /* Write the saved headers. */
3354     (void) httpd_write_fully( hc->conn_fd, headers, headers_len );
3355
3356     /* Echo the rest of the output. */
3357     for (;;)
3358         {
3359         r = read( rfd, buf, sizeof(buf) );
3360         if ( r < 0 && ( errno == EINTR || errno == EAGAIN ) )
3361             {
3362             sleep( 1 );
3363             continue;
3364             }
3365         if ( r <= 0 )
3366             break;
3367         if ( httpd_write_fully( hc->conn_fd, buf, r ) != r )
3368             break;
3369         }
3370     shutdown( hc->conn_fd, SHUT_WR );
3371     }
3372
3373
3374 /* CGI child process. */
3375 static void
3376 cgi_child( httpd_conn* hc )
3377     {
3378     int r;
3379     char** argp;
3380     char** envp;
3381     char* binary;
3382     char* directory;
3383
3384     /* Unset close-on-exec flag for this socket.  This actually shouldn't
3385     ** be necessary, according to POSIX a dup()'d file descriptor does
3386     ** *not* inherit the close-on-exec flag, its flag is always clear.
3387     ** However, Linux messes this up and does copy the flag to the
3388     ** dup()'d descriptor, so we have to clear it.  This could be
3389     ** ifdeffed for Linux only.
3390     */
3391     (void) fcntl( hc->conn_fd, F_SETFD, 0 );
3392
3393     /* Close the syslog descriptor so that the CGI program can't
3394     ** mess with it.  All other open descriptors should be either
3395     ** the listen socket(s), sockets from accept(), or the file-logging
3396     ** fd, and all of those are set to close-on-exec, so we don't
3397     ** have to close anything else.
3398     */
3399     closelog();
3400
3401     /* If the socket happens to be using one of the stdin/stdout/stderr
3402     ** descriptors, move it to another descriptor so that the dup2 calls
3403     ** below don't screw things up.  We arbitrarily pick fd 3 - if there
3404     ** was already something on it, we clobber it, but that doesn't matter
3405     ** since at this point the only fd of interest is the connection.
3406     ** All others will be closed on exec.
3407     */
3408     if ( hc->conn_fd == STDIN_FILENO || hc->conn_fd == STDOUT_FILENO || hc->conn_fd == STDERR_FILENO )
3409         {
3410         int newfd = dup2( hc->conn_fd, STDERR_FILENO + 1 );
3411         if ( newfd >= 0 )
3412             hc->conn_fd = newfd;
3413         /* If the dup2 fails, shrug.  We'll just take our chances.
3414         ** Shouldn't happen though.
3415         */
3416         }
3417
3418     /* Make the environment vector. */
3419     envp = make_envp( hc );
3420
3421     /* Make the argument vector. */
3422     argp = make_argp( hc );
3423
3424     /* Set up stdin.  For POSTs we may have to set up a pipe from an
3425     ** interposer process, depending on if we've read some of the data
3426     ** into our buffer.
3427     */
3428     if ( hc->method == METHOD_POST && hc->read_idx > hc->checked_idx )
3429         {
3430         int p[2];
3431
3432         if ( pipe( p ) < 0 )
3433             {
3434             syslog( LOG_ERR, "pipe - %m" );
3435             httpd_send_err( hc, 500, err500title, "", err500form, hc->encodedurl );
3436             httpd_write_response( hc );
3437             exit( 1 );
3438             }
3439         r = fork( );
3440         if ( r < 0 )
3441             {
3442             syslog( LOG_ERR, "fork - %m" );
3443             httpd_send_err( hc, 500, err500title, "", err500form, hc->encodedurl );
3444             httpd_write_response( hc );
3445             exit( 1 );
3446             }
3447         if ( r == 0 )
3448             {
3449             /* Interposer process. */
3450             sub_process = 1;
3451             (void) close( p[0] );
3452             cgi_interpose_input( hc, p[1] );
3453             exit( 0 );
3454             }
3455         /* Need to schedule a kill for process r; but in the main process! */
3456         (void) close( p[1] );
3457         if ( p[0] != STDIN_FILENO )
3458             {
3459             (void) dup2( p[0], STDIN_FILENO );
3460             (void) close( p[0] );
3461             }
3462         }
3463     else
3464         {
3465         /* Otherwise, the request socket is stdin. */
3466         if ( hc->conn_fd != STDIN_FILENO )
3467             (void) dup2( hc->conn_fd, STDIN_FILENO );
3468         }
3469
3470     /* Set up stdout/stderr.  If we're doing CGI header parsing,
3471     ** we need an output interposer too.
3472     */
3473     if ( strncmp( argp[0], "nph-", 4 ) != 0 && hc->mime_flag )
3474         {
3475         int p[2];
3476
3477         if ( pipe( p ) < 0 )
3478             {
3479             syslog( LOG_ERR, "pipe - %m" );
3480             httpd_send_err( hc, 500, err500title, "", err500form, hc->encodedurl );
3481             httpd_write_response( hc );
3482             exit( 1 );
3483             }
3484         r = fork( );
3485         if ( r < 0 )
3486             {
3487             syslog( LOG_ERR, "fork - %m" );
3488             httpd_send_err( hc, 500, err500title, "", err500form, hc->encodedurl );
3489             httpd_write_response( hc );
3490             exit( 1 );
3491             }
3492         if ( r == 0 )
3493             {
3494             /* Interposer process. */
3495             sub_process = 1;
3496             (void) close( p[1] );
3497             cgi_interpose_output( hc, p[0] );
3498             exit( 0 );
3499             }
3500         /* Need to schedule a kill for process r; but in the main process! */
3501         (void) close( p[0] );
3502         if ( p[1] != STDOUT_FILENO )
3503             (void) dup2( p[1], STDOUT_FILENO );
3504         if ( p[1] != STDERR_FILENO )
3505             (void) dup2( p[1], STDERR_FILENO );
3506         if ( p[1] != STDOUT_FILENO && p[1] != STDERR_FILENO )
3507             (void) close( p[1] );
3508         }
3509     else
3510         {
3511         /* Otherwise, the request socket is stdout/stderr. */
3512         if ( hc->conn_fd != STDOUT_FILENO )
3513             (void) dup2( hc->conn_fd, STDOUT_FILENO );
3514         if ( hc->conn_fd != STDERR_FILENO )
3515             (void) dup2( hc->conn_fd, STDERR_FILENO );
3516         }
3517
3518     /* At this point we would like to set close-on-exec again for hc->conn_fd
3519     ** (see previous comments on Linux's broken behavior re: close-on-exec
3520     ** and dup.)  Unfortunately there seems to be another Linux problem, or
3521     ** perhaps a different aspect of the same problem - if we do this
3522     ** close-on-exec in Linux, the socket stays open but stderr gets
3523     ** closed - the last fd duped from the socket.  What a mess.  So we'll
3524     ** just leave the socket as is, which under other OSs means an extra
3525     ** file descriptor gets passed to the child process.  Since the child
3526     ** probably already has that file open via stdin stdout and/or stderr,
3527     ** this is not a problem.
3528     */
3529     /* (void) fcntl( hc->conn_fd, F_SETFD, 1 ); */
3530
3531 #ifdef CGI_NICE
3532     /* Set priority. */
3533     (void) nice( CGI_NICE );
3534 #endif /* CGI_NICE */
3535
3536     /* Split the program into directory and binary, so we can chdir()
3537     ** to the program's own directory.  This isn't in the CGI 1.1
3538     ** spec, but it's what other HTTP servers do.
3539     */
3540     directory = strdup( hc->expnfilename );
3541     if ( directory == (char*) 0 )
3542         binary = hc->expnfilename;      /* ignore errors */
3543     else
3544         {
3545         binary = strrchr( directory, '/' );
3546         if ( binary == (char*) 0 )
3547             binary = hc->expnfilename;
3548         else
3549             {
3550             *binary++ = '\0';
3551             (void) chdir( directory );  /* ignore errors */
3552             }
3553         }
3554
3555     /* Default behavior for SIGPIPE. */
3556 #ifdef HAVE_SIGSET
3557     (void) sigset( SIGPIPE, SIG_DFL );
3558 #else /* HAVE_SIGSET */
3559     (void) signal( SIGPIPE, SIG_DFL );
3560 #endif /* HAVE_SIGSET */
3561
3562     /* Run the program. */
3563     (void) execve( binary, argp, envp );
3564
3565     /* Something went wrong. */
3566     syslog( LOG_ERR, "execve %.80s - %m", hc->expnfilename );
3567     httpd_send_err( hc, 500, err500title, "", err500form, hc->encodedurl );
3568     httpd_write_response( hc );
3569     _exit( 1 );
3570     }
3571
3572
3573 static int
3574 cgi( httpd_conn* hc )
3575     {
3576     int r;
3577     ClientData client_data;
3578
3579     if ( hc->hs->cgi_limit != 0 && hc->hs->cgi_count >= hc->hs->cgi_limit )
3580         {
3581         httpd_send_err(
3582             hc, 503, httpd_err503title, "", httpd_err503form,
3583             hc->encodedurl );
3584         return -1;
3585         }
3586     ++hc->hs->cgi_count;
3587     httpd_clear_ndelay( hc->conn_fd );
3588     r = fork( );
3589     if ( r < 0 )
3590         {
3591         syslog( LOG_ERR, "fork - %m" );
3592         httpd_send_err(
3593             hc, 500, err500title, "", err500form, hc->encodedurl );
3594         return -1;
3595         }
3596     if ( r == 0 )
3597         {
3598         /* Child process. */
3599         sub_process = 1;
3600         httpd_unlisten( hc->hs );
3601         cgi_child( hc );
3602         }
3603
3604     /* Parent process. */
3605     syslog( LOG_DEBUG, "spawned CGI process %d for file '%.200s'", r, hc->expnfilename );
3606 #ifdef CGI_TIMELIMIT
3607     /* Schedule a kill for the child process, in case it runs too long */
3608     client_data.i = r;
3609     if ( tmr_create( (struct timeval*) 0, cgi_kill, client_data, CGI_TIMELIMIT * 1000L, 0 ) == (Timer*) 0 )
3610         {
3611         syslog( LOG_CRIT, "tmr_create(cgi_kill child) failed" );
3612         exit( 1 );
3613         }
3614 #endif /* CGI_TIMELIMIT */
3615     hc->status = 200;
3616     hc->bytes_sent = CGI_BYTECOUNT;
3617     hc->should_linger = 0;
3618
3619     return 0;
3620     }
3621
3622
3623 static int
3624 really_start_request( httpd_conn* hc, struct timeval* nowP )
3625     {
3626     static char* indexname;
3627     static size_t maxindexname = 0;
3628     static const char* index_names[] = { INDEX_NAMES };
3629     int i;
3630 #ifdef AUTH_FILE
3631     static char* dirname;
3632     static size_t maxdirname = 0;
3633 #endif /* AUTH_FILE */
3634     size_t expnlen, indxlen;
3635     char* cp;
3636     char* pi;
3637
3638     expnlen = strlen( hc->expnfilename );
3639
3640     /* Stat the file. */
3641     if ( stat( hc->expnfilename, &hc->sb ) < 0 )
3642         {
3643         httpd_send_err( hc, 500, err500title, "", err500form, hc->encodedurl );
3644         return -1;
3645         }
3646
3647     /* Is it world-readable or world-executable?  We check explicitly instead
3648     ** of just trying to open it, so that no one ever gets surprised by
3649     ** a file that's not set world-readable and yet somehow is
3650     ** readable by the HTTP server and therefore the *whole* world.
3651     */
3652     if ( ! ( hc->sb.st_mode & ( S_IROTH | S_IXOTH ) ) )
3653         {
3654         syslog(
3655             LOG_INFO,
3656             "%.80s URL \"%.80s\" resolves to a non world-readable file",
3657             httpd_ntoa( &hc->client_addr ), hc->encodedurl );
3658         httpd_send_err(
3659             hc, 403, err403title, "",
3660             ERROR_FORM( err403form, "The requested URL '%.80s' resolves to a file that is not world-readable.\n" ),
3661             hc->encodedurl );
3662         return -1;
3663         }
3664
3665     /* Is it a directory? */
3666     if ( S_ISDIR(hc->sb.st_mode) )
3667         {
3668         /* If there's pathinfo, it's just a non-existent file. */
3669         if ( hc->pathinfo[0] != '\0' )
3670             {
3671             httpd_send_err( hc, 404, err404title, "", err404form, hc->encodedurl );
3672             return -1;
3673             }
3674
3675         /* Special handling for directory URLs that don't end in a slash.
3676         ** We send back an explicit redirect with the slash, because
3677         ** otherwise many clients can't build relative URLs properly.
3678         */
3679         if ( strcmp( hc->origfilename, "" ) != 0 &&
3680              strcmp( hc->origfilename, "." ) != 0 &&
3681              hc->origfilename[strlen( hc->origfilename ) - 1] != '/' )
3682             {
3683             send_dirredirect( hc );
3684             return -1;
3685             }
3686
3687         /* Check for an index file. */
3688         for ( i = 0; i < sizeof(index_names) / sizeof(char*); ++i )
3689             {
3690             httpd_realloc_str(
3691                 &indexname, &maxindexname,
3692                 expnlen + 1 + strlen( index_names[i] ) );
3693             (void) strcpy( indexname, hc->expnfilename );
3694             indxlen = strlen( indexname );
3695             if ( indxlen == 0 || indexname[indxlen - 1] != '/' )
3696                 (void) strcat( indexname, "/" );
3697             if ( strcmp( indexname, "./" ) == 0 )
3698                 indexname[0] = '\0';
3699             (void) strcat( indexname, index_names[i] );
3700             if ( stat( indexname, &hc->sb ) >= 0 )
3701                 goto got_one;
3702             }
3703
3704         /* Nope, no index file, so it's an actual directory request. */
3705 #ifdef GENERATE_INDEXES
3706         /* Directories must be readable for indexing. */
3707         if ( ! ( hc->sb.st_mode & S_IROTH ) )
3708             {
3709             syslog(
3710                 LOG_INFO,
3711                 "%.80s URL \"%.80s\" tried to index a directory with indexing disabled",
3712                 httpd_ntoa( &hc->client_addr ), hc->encodedurl );
3713             httpd_send_err(
3714                 hc, 403, err403title, "",
3715                 ERROR_FORM( err403form, "The requested URL '%.80s' resolves to a directory that has indexing disabled.\n" ),
3716                 hc->encodedurl );
3717             return -1;
3718             }
3719 #ifdef AUTH_FILE
3720         /* Check authorization for this directory. */
3721         if ( auth_check( hc, hc->expnfilename ) == -1 )
3722             return -1;
3723 #endif /* AUTH_FILE */
3724         /* Referrer check. */
3725         if ( ! check_referrer( hc ) )
3726             return -1;
3727         /* Ok, generate an index. */
3728         return ls( hc );
3729 #else /* GENERATE_INDEXES */
3730         syslog(
3731             LOG_INFO, "%.80s URL \"%.80s\" tried to index a directory",
3732             httpd_ntoa( &hc->client_addr ), hc->encodedurl );
3733         httpd_send_err(
3734             hc, 403, err403title, "",
3735             ERROR_FORM( err403form, "The requested URL '%.80s' is a directory, and directory indexing is disabled on this server.\n" ),
3736             hc->encodedurl );
3737         return -1;
3738 #endif /* GENERATE_INDEXES */
3739
3740         got_one: ;
3741         /* Got an index file.  Expand symlinks again.  More pathinfo means
3742         ** something went wrong.
3743         */
3744         cp = expand_symlinks( indexname, &pi, hc->hs->no_symlink_check, hc->tildemapped );
3745         if ( cp == (char*) 0 || pi[0] != '\0' )
3746             {
3747             httpd_send_err( hc, 500, err500title, "", err500form, hc->encodedurl );
3748             return -1;
3749             }
3750         expnlen = strlen( cp );
3751         httpd_realloc_str( &hc->expnfilename, &hc->maxexpnfilename, expnlen );
3752         (void) strcpy( hc->expnfilename, cp );
3753
3754         /* Now, is the index version world-readable or world-executable? */
3755         if ( ! ( hc->sb.st_mode & ( S_IROTH | S_IXOTH ) ) )
3756             {
3757             syslog(
3758                 LOG_INFO,
3759                 "%.80s URL \"%.80s\" resolves to a non-world-readable index file",
3760                 httpd_ntoa( &hc->client_addr ), hc->encodedurl );
3761             httpd_send_err(
3762                 hc, 403, err403title, "",
3763                 ERROR_FORM( err403form, "The requested URL '%.80s' resolves to an index file that is not world-readable.\n" ),
3764                 hc->encodedurl );
3765             return -1;
3766             }
3767         }
3768
3769 #ifdef AUTH_FILE
3770     /* Check authorization for this directory. */
3771     httpd_realloc_str( &dirname, &maxdirname, expnlen );
3772     (void) strcpy( dirname, hc->expnfilename );
3773     cp = strrchr( dirname, '/' );
3774     if ( cp == (char*) 0 )
3775         (void) strcpy( dirname, "." );
3776     else
3777         *cp = '\0';
3778     if ( auth_check( hc, dirname ) == -1 )
3779         return -1;
3780
3781     /* Check if the filename is the AUTH_FILE itself - that's verboten. */
3782     if ( expnlen == sizeof(AUTH_FILE) - 1 )
3783         {
3784         if ( strcmp( hc->expnfilename, AUTH_FILE ) == 0 )
3785             {
3786             syslog(
3787                 LOG_NOTICE,
3788                 "%.80s URL \"%.80s\" tried to retrieve an auth file",
3789                 httpd_ntoa( &hc->client_addr ), hc->encodedurl );
3790             httpd_send_err(
3791                 hc, 403, err403title, "",
3792                 ERROR_FORM( err403form, "The requested URL '%.80s' is an authorization file, retrieving it is not permitted.\n" ),
3793                 hc->encodedurl );
3794             return -1;
3795             }
3796         }
3797     else if ( expnlen >= sizeof(AUTH_FILE) &&
3798               strcmp( &(hc->expnfilename[expnlen - sizeof(AUTH_FILE) + 1]), AUTH_FILE ) == 0 &&
3799               hc->expnfilename[expnlen - sizeof(AUTH_FILE)] == '/' )
3800         {
3801         syslog(
3802             LOG_NOTICE,
3803             "%.80s URL \"%.80s\" tried to retrieve an auth file",
3804             httpd_ntoa( &hc->client_addr ), hc->encodedurl );
3805         httpd_send_err(
3806             hc, 403, err403title, "",
3807             ERROR_FORM( err403form, "The requested URL '%.80s' is an authorization file, retrieving it is not permitted.\n" ),
3808             hc->encodedurl );
3809         return -1;
3810         }
3811 #endif /* AUTH_FILE */
3812
3813     /* Referrer check. */
3814     if ( ! check_referrer( hc ) )
3815         return -1;
3816
3817     /* Is it world-executable and in the CGI area? */
3818     if ( hc->hs->cgi_pattern != (char*) 0 &&
3819          ( hc->sb.st_mode & S_IXOTH ) &&
3820          match( hc->hs->cgi_pattern, hc->expnfilename ) )
3821         return cgi( hc );
3822
3823     /* It's not CGI.  If it's executable or there's pathinfo, someone's
3824     ** trying to either serve or run a non-CGI file as CGI.   Either case
3825     ** is prohibited.
3826     */
3827     if ( hc->sb.st_mode & S_IXOTH )
3828         {
3829         syslog(
3830             LOG_NOTICE, "%.80s URL \"%.80s\" is executable but isn't CGI",
3831             httpd_ntoa( &hc->client_addr ), hc->encodedurl );
3832         httpd_send_err(
3833             hc, 403, err403title, "",
3834             ERROR_FORM( err403form, "The requested URL '%.80s' resolves to a file which is marked executable but is not a CGI file; retrieving it is forbidden.\n" ),
3835             hc->encodedurl );
3836         return -1;
3837         }
3838     if ( hc->pathinfo[0] != '\0' )
3839         {
3840         syslog(
3841             LOG_INFO, "%.80s URL \"%.80s\" has pathinfo but isn't CGI",
3842             httpd_ntoa( &hc->client_addr ), hc->encodedurl );
3843         httpd_send_err(
3844             hc, 403, err403title, "",
3845             ERROR_FORM( err403form, "The requested URL '%.80s' resolves to a file plus CGI-style pathinfo, but the file is not a valid CGI file.\n" ),
3846             hc->encodedurl );
3847         return -1;
3848         }
3849
3850     if ( hc->method != METHOD_GET && hc->method != METHOD_HEAD )
3851         {
3852         httpd_send_err(
3853             hc, 501, err501title, "", err501form, httpd_method_str( hc->method ) );
3854         return -1;
3855         }
3856
3857     /* Fill in last_byte_index, if necessary. */
3858     if ( hc->got_range &&
3859          ( hc->last_byte_index == -1 || hc->last_byte_index >= hc->sb.st_size ) )
3860         hc->last_byte_index = hc->sb.st_size - 1;
3861
3862     figure_mime( hc );
3863
3864     if ( hc->method == METHOD_HEAD )
3865         {
3866         send_mime(
3867             hc, 200, ok200title, hc->encodings, "", hc->type, hc->sb.st_size,
3868             hc->sb.st_mtime );
3869         }
3870     else if ( hc->if_modified_since != (time_t) -1 &&
3871          hc->if_modified_since >= hc->sb.st_mtime )
3872         {
3873         send_mime(
3874             hc, 304, err304title, hc->encodings, "", hc->type, (off_t) -1,
3875             hc->sb.st_mtime );
3876         }
3877     else
3878         {
3879         hc->file_address = mmc_map( hc->expnfilename, &(hc->sb), nowP );
3880         if ( hc->file_address == (char*) 0 )
3881             {
3882             httpd_send_err( hc, 500, err500title, "", err500form, hc->encodedurl );
3883             return -1;
3884             }
3885         send_mime(
3886             hc, 200, ok200title, hc->encodings, "", hc->type, hc->sb.st_size,
3887             hc->sb.st_mtime );
3888         }
3889
3890     return 0;
3891     }
3892
3893
3894 int
3895 httpd_start_request( httpd_conn* hc, struct timeval* nowP )
3896     {
3897     int r;
3898
3899     /* Really start the request. */
3900     r = really_start_request( hc, nowP );
3901
3902     /* And return the status. */
3903     return r;
3904     }
3905
3906
3907 static void
3908 make_log_entry( httpd_conn* hc, struct timeval* nowP )
3909     {
3910     char* ru;
3911     char url[305];
3912     char bytes[40];
3913
3914     if ( hc->hs->no_log )
3915         return;
3916
3917     /* This is straight CERN Combined Log Format - the only tweak
3918     ** being that if we're using syslog() we leave out the date, because
3919     ** syslogd puts it in.  The included syslogtocern script turns the
3920     ** results into true CERN format.
3921     */
3922
3923     /* Format remote user. */
3924     if ( hc->remoteuser[0] != '\0' )
3925         ru = hc->remoteuser;
3926     else
3927         ru = "-";
3928     /* If we're vhosting, prepend the hostname to the url.  This is
3929     ** a little weird, perhaps writing separate log files for
3930     ** each vhost would make more sense.
3931     */
3932     if ( hc->hs->vhost && ! hc->tildemapped )
3933         (void) my_snprintf( url, sizeof(url),
3934             "/%.100s%.200s",
3935             hc->hostname == (char*) 0 ? hc->hs->server_hostname : hc->hostname,
3936             hc->encodedurl );
3937     else
3938         (void) my_snprintf( url, sizeof(url),
3939             "%.200s", hc->encodedurl );
3940     /* Format the bytes. */
3941     if ( hc->bytes_sent >= 0 )
3942         (void) my_snprintf(
3943             bytes, sizeof(bytes), "%lld", (long long) hc->bytes_sent );
3944     else
3945         (void) strcpy( bytes, "-" );
3946
3947     /* Logfile or syslog? */
3948     if ( hc->hs->logfp != (FILE*) 0 )
3949         {
3950         time_t now;
3951         struct tm* t;
3952         const char* cernfmt_nozone = "%d/%b/%Y:%H:%M:%S";
3953         char date_nozone[100];
3954         int zone;
3955         char sign;
3956         char date[100];
3957
3958         /* Get the current time, if necessary. */
3959         if ( nowP != (struct timeval*) 0 )
3960             now = nowP->tv_sec;
3961         else
3962             now = time( (time_t*) 0 );
3963         /* Format the time, forcing a numeric timezone (some log analyzers
3964         ** are stoooopid about this).
3965         */
3966         t = localtime( &now );
3967         (void) strftime( date_nozone, sizeof(date_nozone), cernfmt_nozone, t );
3968 #ifdef HAVE_TM_GMTOFF
3969         zone = t->tm_gmtoff / 60L;
3970 #else
3971         zone = -timezone / 60L;
3972         /* Probably have to add something about daylight time here. */
3973 #endif
3974         if ( zone >= 0 )
3975             sign = '+';
3976         else
3977             {
3978             sign = '-';
3979             zone = -zone;
3980             }
3981         zone = ( zone / 60 ) * 100 + zone % 60;
3982         (void) my_snprintf( date, sizeof(date),
3983             "%s %c%04d", date_nozone, sign, zone );
3984         /* And write the log entry. */
3985         (void) fprintf( hc->hs->logfp,
3986             "%.80s - %.80s [%s] \"%.80s %.300s %.80s\" %d %s \"%.200s\" \"%.200s\"\n",
3987             httpd_ntoa( &hc->client_addr ), ru, date,
3988             httpd_method_str( hc->method ), url, hc->protocol,
3989             hc->status, bytes, hc->referrer, hc->useragent );
3990 #ifdef FLUSH_LOG_EVERY_TIME
3991         (void) fflush( hc->hs->logfp );
3992 #endif
3993         }
3994     else
3995         syslog( LOG_INFO,
3996             "%.80s - %.80s \"%.80s %.200s %.80s\" %d %s \"%.200s\" \"%.200s\"",
3997             httpd_ntoa( &hc->client_addr ), ru,
3998             httpd_method_str( hc->method ), url, hc->protocol,
3999             hc->status, bytes, hc->referrer, hc->useragent );
4000     }
4001
4002
4003 /* Returns 1 if ok to serve the url, 0 if not. */
4004 static int
4005 check_referrer( httpd_conn* hc )
4006     {
4007     int r;
4008     char* cp;
4009
4010     /* Are we doing referrer checking at all? */
4011     if ( hc->hs->url_pattern == (char*) 0 )
4012         return 1;
4013
4014     r = really_check_referrer( hc );
4015
4016     if ( ! r )
4017         {
4018         if ( hc->hs->vhost && hc->hostname != (char*) 0 )
4019             cp = hc->hostname;
4020         else
4021             cp = hc->hs->server_hostname;
4022         if ( cp == (char*) 0 )
4023             cp = "";
4024         syslog(
4025             LOG_INFO, "%.80s non-local referrer \"%.80s%.80s\" \"%.80s\"",
4026             httpd_ntoa( &hc->client_addr ), cp, hc->encodedurl, hc->referrer );
4027         httpd_send_err(
4028             hc, 403, err403title, "",
4029             ERROR_FORM( err403form, "You must supply a local referrer to get URL '%.80s' from this server.\n" ),
4030             hc->encodedurl );
4031         }
4032     return r;
4033     }
4034
4035
4036 /* Returns 1 if ok to serve the url, 0 if not. */
4037 static int
4038 really_check_referrer( httpd_conn* hc )
4039     {
4040     httpd_server* hs;
4041     char* cp1;
4042     char* cp2;
4043     char* cp3;
4044     static char* refhost = (char*) 0;
4045     static size_t refhost_size = 0;
4046     char *lp;
4047
4048     hs = hc->hs;
4049
4050     /* Check for an empty referrer. */
4051     if ( hc->referrer == (char*) 0 || hc->referrer[0] == '\0' ||
4052          ( cp1 = strstr( hc->referrer, "//" ) ) == (char*) 0 )
4053         {
4054         /* Disallow if we require a referrer and the url matches. */
4055         if ( hs->no_empty_referrers && match( hs->url_pattern, hc->origfilename ) )
4056             return 0;
4057         /* Otherwise ok. */
4058         return 1;
4059         }
4060
4061     /* Extract referrer host. */
4062     cp1 += 2;
4063     for ( cp2 = cp1; *cp2 != '/' && *cp2 != ':' && *cp2 != '\0'; ++cp2 )
4064         continue;
4065     httpd_realloc_str( &refhost, &refhost_size, cp2 - cp1 );
4066     for ( cp3 = refhost; cp1 < cp2; ++cp1, ++cp3 )
4067         if ( isupper(*cp1) )
4068             *cp3 = tolower(*cp1);
4069         else
4070             *cp3 = *cp1;
4071     *cp3 = '\0';
4072
4073     /* Local pattern? */
4074     if ( hs->local_pattern != (char*) 0 )
4075         lp = hs->local_pattern;
4076     else
4077         {
4078         /* No local pattern.  What's our hostname? */
4079         if ( ! hs->vhost )
4080             {
4081             /* Not vhosting, use the server name. */
4082             lp = hs->server_hostname;
4083             if ( lp == (char*) 0 )
4084                 /* Couldn't figure out local hostname - give up. */
4085                 return 1;
4086             }
4087         else
4088             {
4089             /* We are vhosting, use the hostname on this connection. */
4090             lp = hc->hostname;
4091             if ( lp == (char*) 0 )
4092                 /* Oops, no hostname.  Maybe it's an old browser that
4093                 ** doesn't send a Host: header.  We could figure out
4094                 ** the default hostname for this IP address, but it's
4095                 ** not worth it for the few requests like this.
4096                 */
4097                 return 1;
4098             }
4099         }
4100
4101     /* If the referrer host doesn't match the local host pattern, and
4102     ** the filename does match the url pattern, it's an illegal reference.
4103     */
4104     if ( ! match( lp, refhost ) && match( hs->url_pattern, hc->origfilename ) )
4105         return 0;
4106     /* Otherwise ok. */
4107     return 1;
4108     }
4109
4110
4111 char*
4112 httpd_ntoa( httpd_sockaddr* saP )
4113     {
4114 #ifdef USE_IPV6
4115     static char str[200];
4116
4117     if ( getnameinfo( &saP->sa, sockaddr_len( saP ), str, sizeof(str), 0, 0, NI_NUMERICHOST ) != 0 )
4118         {
4119         str[0] = '?';
4120         str[1] = '\0';
4121         }
4122     else if ( IN6_IS_ADDR_V4MAPPED( &saP->sa_in6.sin6_addr ) && strncmp( str, "::ffff:", 7 ) == 0 )
4123         /* Elide IPv6ish prefix for IPv4 addresses. */
4124         (void) ol_strcpy( str, &str[7] );
4125
4126     return str;
4127
4128 #else /* USE_IPV6 */
4129
4130     return inet_ntoa( saP->sa_in.sin_addr );
4131
4132 #endif /* USE_IPV6 */
4133     }
4134
4135
4136 static int
4137 sockaddr_check( httpd_sockaddr* saP )
4138     {
4139     switch ( saP->sa.sa_family )
4140         {
4141         case AF_INET: return 1;
4142 #ifdef USE_IPV6
4143         case AF_INET6: return 1;
4144 #endif /* USE_IPV6 */
4145         default:
4146         return 0;
4147         }
4148     }
4149
4150
4151 static size_t
4152 sockaddr_len( httpd_sockaddr* saP )
4153     {
4154     switch ( saP->sa.sa_family )
4155         {
4156         case AF_INET: return sizeof(struct sockaddr_in);
4157 #ifdef USE_IPV6
4158         case AF_INET6: return sizeof(struct sockaddr_in6);
4159 #endif /* USE_IPV6 */
4160         default:
4161         return 0;       /* shouldn't happen */
4162         }
4163     }
4164
4165
4166 /* Some systems don't have snprintf(), so we make our own that uses
4167 ** either vsnprintf() or vsprintf().  If your system doesn't have
4168 ** vsnprintf(), it is probably vulnerable to buffer overruns.
4169 ** Upgrade!
4170 */
4171 static int
4172 my_snprintf( char* str, size_t size, const char* format, ... )
4173     {
4174     va_list ap;
4175     int r;
4176
4177     va_start( ap, format );
4178 #ifdef HAVE_VSNPRINTF
4179     r = vsnprintf( str, size, format, ap );
4180 #else /* HAVE_VSNPRINTF */
4181     r = vsprintf( str, format, ap );
4182 #endif /* HAVE_VSNPRINTF */
4183     va_end( ap );
4184     return r;
4185     }
4186
4187
4188 #ifndef HAVE_ATOLL
4189 static long long
4190 atoll( const char* str )
4191     {
4192     long long value;
4193     long long sign;
4194
4195     while ( isspace( *str ) )
4196         ++str;
4197     switch ( *str )
4198         {
4199         case '-': sign = -1; ++str; break;
4200         case '+': sign = 1; ++str; break;
4201         default: sign = 1; break;
4202         }
4203     value = 0;
4204     while ( isdigit( *str ) )
4205         {
4206         value = value * 10 + ( *str - '0' );
4207         ++str;
4208         }
4209     return sign * value;
4210     }
4211 #endif /* HAVE_ATOLL */
4212
4213
4214 /* Read the requested buffer completely, accounting for interruptions. */
4215 int
4216 httpd_read_fully( int fd, void* buf, size_t nbytes )
4217     {
4218     int nread;
4219
4220     nread = 0;
4221     while ( nread < nbytes )
4222         {
4223         int r;
4224
4225         r = read( fd, (char*) buf + nread, nbytes - nread );
4226         if ( r < 0 && ( errno == EINTR || errno == EAGAIN ) )
4227             {
4228             sleep( 1 );
4229             continue;
4230             }
4231         if ( r < 0 )
4232             return r;
4233         if ( r == 0 )
4234             break;
4235         nread += r;
4236         }
4237
4238     return nread;
4239     }
4240
4241
4242 /* Write the requested buffer completely, accounting for interruptions. */
4243 int
4244 httpd_write_fully( int fd, const char* buf, size_t nbytes )
4245     {
4246     int nwritten;
4247
4248     nwritten = 0;
4249     while ( nwritten < nbytes )
4250         {
4251         int r;
4252
4253         r = write( fd, buf + nwritten, nbytes - nwritten );
4254         if ( r < 0 && ( errno == EINTR || errno == EAGAIN ) )
4255             {
4256             sleep( 1 );
4257             continue;
4258             }
4259         if ( r < 0 )
4260             return r;
4261         if ( r == 0 )
4262             break;
4263         nwritten += r;
4264         }
4265
4266     return nwritten;
4267     }
4268
4269
4270 /* Generate debugging statistics syslog message. */
4271 void
4272 httpd_logstats( long secs )
4273     {
4274     if ( str_alloc_count > 0 )
4275         syslog( LOG_NOTICE,
4276             "  libhttpd - %d strings allocated, %lu bytes (%g bytes/str)",
4277             str_alloc_count, (unsigned long) str_alloc_size,
4278             (float) str_alloc_size / str_alloc_count );
4279     }