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