Annotation of embedaddon/thttpd/libhttpd.c, revision 1.1

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

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>