root/sapi/isapi/php5isapi.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. php_info_isapi
  2. sapi_isapi_ub_write
  3. sapi_isapi_header_handler
  4. accumulate_header_length
  5. concat_header
  6. sapi_isapi_send_headers
  7. php_isapi_startup
  8. sapi_isapi_read_post
  9. sapi_isapi_read_cookies
  10. sapi_isapi_register_zeus_ssl_variables
  11. sapi_isapi_register_zeus_variables
  12. sapi_isapi_register_iis_variables
  13. sapi_isapi_register_server_variables2
  14. sapi_isapi_register_server_variables
  15. GetFilterVersion
  16. HttpFilterProc
  17. init_request_info
  18. php_isapi_report_exception
  19. GetExtensionVersion
  20. my_endthread
  21. exceptionhandler
  22. HttpExtensionProc
  23. __declspec

/*
   +----------------------------------------------------------------------+
   | PHP Version 5                                                        |
   +----------------------------------------------------------------------+
   | Copyright (c) 1997-2013 The PHP Group                                |
   +----------------------------------------------------------------------+
   | This source file is subject to version 3.01 of the PHP license,      |
   | that is bundled with this package in the file LICENSE, and is        |
   | available through the world-wide-web at the following url:           |
   | http://www.php.net/license/3_01.txt                                  |
   | If you did not receive a copy of the PHP license and are unable to   |
   | obtain it through the world-wide-web, please send a note to          |
   | license@php.net so we can mail you a copy immediately.               |
   +----------------------------------------------------------------------+
   | Authors: Zeev Suraski <zeev@zend.com>                                |
   |          Ben Mansell <ben@zeus.com> (Zeus Support)                   |
   +----------------------------------------------------------------------+
 */
/* $Id$ */

#include "php.h"
#include <httpext.h>
#include <httpfilt.h>
#include <httpext.h>
#include "php_main.h"
#include "SAPI.h"
#include "php_globals.h"
#include "ext/standard/info.h"
#include "php_variables.h"
#include "php_ini.h"

#ifdef PHP_WIN32
# include <process.h>
#else
# define __try
# define __except(val)
# define __declspec(foo)
#endif


#ifdef WITH_ZEUS
# include "httpext.h"
# include <errno.h>
# define GetLastError() errno
#endif

#ifdef PHP_WIN32
#define PHP_ENABLE_SEH
#endif

/* 
uncomment the following lines to turn off 
exception trapping when running under a debugger 

#ifdef _DEBUG
#undef PHP_ENABLE_SEH
#endif
*/

#define MAX_STATUS_LENGTH sizeof("xxxx LONGEST POSSIBLE STATUS DESCRIPTION")
#define ISAPI_SERVER_VAR_BUF_SIZE 1024
#define ISAPI_POST_DATA_BUF 1024

static zend_bool bFilterLoaded=0;
static zend_bool bTerminateThreadsOnError=0;

static char *isapi_special_server_variable_names[] = {
        "ALL_HTTP",
        "HTTPS",
#ifndef WITH_ZEUS
        "SCRIPT_NAME",
#endif
        NULL
};

#define NUM_SPECIAL_VARS                (sizeof(isapi_special_server_variable_names)/sizeof(char *))
#define SPECIAL_VAR_ALL_HTTP    0
#define SPECIAL_VAR_HTTPS               1
#define SPECIAL_VAR_PHP_SELF    2

static char *isapi_server_variable_names[] = {
        "AUTH_PASSWORD",
        "AUTH_TYPE",
        "AUTH_USER",
        "CONTENT_LENGTH",
        "CONTENT_TYPE",
        "PATH_TRANSLATED",
        "QUERY_STRING",
        "REMOTE_ADDR",
        "REMOTE_HOST",
        "REMOTE_USER",
        "REQUEST_METHOD",
        "SERVER_NAME",
        "SERVER_PORT",
        "SERVER_PROTOCOL",
        "SERVER_SOFTWARE",
#ifndef WITH_ZEUS
        "APPL_MD_PATH",
        "APPL_PHYSICAL_PATH",
        "INSTANCE_ID",
        "INSTANCE_META_PATH",
        "LOGON_USER",
        "REQUEST_URI",
        "URL",
#else
        "DOCUMENT_ROOT",
#endif
        NULL
};


static char *isapi_secure_server_variable_names[] = {
        "CERT_COOKIE",
        "CERT_FLAGS",
        "CERT_ISSUER",
        "CERT_KEYSIZE",
        "CERT_SECRETKEYSIZE",
        "CERT_SERIALNUMBER",
        "CERT_SERVER_ISSUER",
        "CERT_SERVER_SUBJECT",
        "CERT_SUBJECT",
        "HTTPS_KEYSIZE",
        "HTTPS_SECRETKEYSIZE",
        "HTTPS_SERVER_ISSUER",
        "HTTPS_SERVER_SUBJECT",
        "SERVER_PORT_SECURE",
#ifdef WITH_ZEUS
        "SSL_CLIENT_CN",
        "SSL_CLIENT_EMAIL",
        "SSL_CLIENT_OU",
        "SSL_CLIENT_O",
        "SSL_CLIENT_L",
        "SSL_CLIENT_ST",
        "SSL_CLIENT_C",
        "SSL_CLIENT_I_CN",
        "SSL_CLIENT_I_EMAIL",
        "SSL_CLIENT_I_OU",
        "SSL_CLIENT_I_O",
        "SSL_CLIENT_I_L",
        "SSL_CLIENT_I_ST",
        "SSL_CLIENT_I_C",       
#endif
        NULL
};


static void php_info_isapi(ZEND_MODULE_INFO_FUNC_ARGS)
{
        char **p;
        char variable_buf[ISAPI_SERVER_VAR_BUF_SIZE];
        DWORD variable_len;
        char **all_variables[] = {
                isapi_server_variable_names,
                isapi_special_server_variable_names,
                isapi_secure_server_variable_names,
                NULL
        };
        char ***server_variable_names;
        LPEXTENSION_CONTROL_BLOCK lpECB;

        lpECB = (LPEXTENSION_CONTROL_BLOCK) SG(server_context);

        php_info_print_table_start();
        php_info_print_table_header(2, "Server Variable", "Value");
        server_variable_names = all_variables;
        while (*server_variable_names) {
                p = *server_variable_names;
                while (*p) {
                        variable_len = ISAPI_SERVER_VAR_BUF_SIZE;
                        if (lpECB->GetServerVariable(lpECB->ConnID, *p, variable_buf, &variable_len)
                                && variable_buf[0]) {
                                php_info_print_table_row(2, *p, variable_buf);
                        } else if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
                                char *tmp_variable_buf;

                                tmp_variable_buf = (char *) emalloc(variable_len);
                                if (lpECB->GetServerVariable(lpECB->ConnID, *p, tmp_variable_buf, &variable_len)
                                        && variable_buf[0]) {
                                        php_info_print_table_row(2, *p, tmp_variable_buf);
                                }
                                efree(tmp_variable_buf);
                        }
                        p++;
                }
                server_variable_names++;
        }
        php_info_print_table_end();
}


static zend_module_entry php_isapi_module = {
    STANDARD_MODULE_HEADER,
        "ISAPI",
        NULL,
        NULL,
        NULL,
        NULL,
        NULL,
        php_info_isapi,
    NULL,
        STANDARD_MODULE_PROPERTIES
};


static int sapi_isapi_ub_write(const char *str, uint str_length TSRMLS_DC)
{
        DWORD num_bytes = str_length;
        LPEXTENSION_CONTROL_BLOCK ecb;
        
        ecb = (LPEXTENSION_CONTROL_BLOCK) SG(server_context);
        if (ecb->WriteClient(ecb->ConnID, (char *) str, &num_bytes, HSE_IO_SYNC) == FALSE) {
                php_handle_aborted_connection();
        }
        return num_bytes;
}


static int sapi_isapi_header_handler(sapi_header_struct *sapi_header, sapi_header_op_enum op, sapi_headers_struct *sapi_headers TSRMLS_DC)
{
        return SAPI_HEADER_ADD;
}



static void accumulate_header_length(sapi_header_struct *sapi_header, uint *total_length TSRMLS_DC)
{
        *total_length += sapi_header->header_len+2;
}


static void concat_header(sapi_header_struct *sapi_header, char **combined_headers_ptr TSRMLS_DC)
{
        memcpy(*combined_headers_ptr, sapi_header->header, sapi_header->header_len);
        *combined_headers_ptr += sapi_header->header_len;
        **combined_headers_ptr = '\r';
        (*combined_headers_ptr)++;
        **combined_headers_ptr = '\n';
        (*combined_headers_ptr)++;
}


static int sapi_isapi_send_headers(sapi_headers_struct *sapi_headers TSRMLS_DC)
{
        uint total_length = 2;          /* account for the trailing \r\n */
        char *combined_headers, *combined_headers_ptr;
        LPEXTENSION_CONTROL_BLOCK lpECB = (LPEXTENSION_CONTROL_BLOCK) SG(server_context);
        HSE_SEND_HEADER_EX_INFO header_info;
        sapi_header_struct default_content_type;
        char *status_buf = NULL;

        /* Obtain headers length */
        if (SG(sapi_headers).send_default_content_type) {
                sapi_get_default_content_type_header(&default_content_type TSRMLS_CC);
                accumulate_header_length(&default_content_type, (void *) &total_length TSRMLS_CC);
        }
        zend_llist_apply_with_argument(&SG(sapi_headers).headers, (llist_apply_with_arg_func_t) accumulate_header_length, (void *) &total_length TSRMLS_CC);

        /* Generate headers */
        combined_headers = (char *) emalloc(total_length+1);
        combined_headers_ptr = combined_headers;
        if (SG(sapi_headers).send_default_content_type) {
                concat_header(&default_content_type, (void *) &combined_headers_ptr TSRMLS_CC);
                sapi_free_header(&default_content_type); /* we no longer need it */
        }
        zend_llist_apply_with_argument(&SG(sapi_headers).headers, (llist_apply_with_arg_func_t) concat_header, (void *) &combined_headers_ptr TSRMLS_CC);
        *combined_headers_ptr++ = '\r';
        *combined_headers_ptr++ = '\n';
        *combined_headers_ptr = 0;

        switch (SG(sapi_headers).http_response_code) {
                case 200:
                        header_info.pszStatus = "200 OK";
                        break;
                case 302:
                        header_info.pszStatus = "302 Moved Temporarily";
                        break;
                case 401:
                        header_info.pszStatus = "401 Authorization Required";
                        break;
                default: {
                        const char *sline = SG(sapi_headers).http_status_line;
                        int sline_len;
                        
                        /* httpd requires that r->status_line is set to the first digit of
                         * the status-code: */
                        if (sline && ((sline_len = strlen(sline)) > 12) && strncmp(sline, "HTTP/1.", 7) == 0 && sline[8] == ' ') {
                                if ((sline_len - 9) > MAX_STATUS_LENGTH) {
                                        status_buf = estrndup(sline + 9, MAX_STATUS_LENGTH);
                                } else {
                                        status_buf = estrndup(sline + 9, sline_len - 9);
                                }
                        } else {
                                status_buf = emalloc(MAX_STATUS_LENGTH + 1);
                                snprintf(status_buf, MAX_STATUS_LENGTH, "%d Undescribed", SG(sapi_headers).http_response_code);
                        }
                        header_info.pszStatus = status_buf;
                        break;
                }
        }
        header_info.cchStatus = strlen(header_info.pszStatus);
        header_info.pszHeader = combined_headers;
        header_info.cchHeader = total_length;
        header_info.fKeepConn = FALSE;
        lpECB->dwHttpStatusCode = SG(sapi_headers).http_response_code;

        lpECB->ServerSupportFunction(lpECB->ConnID, HSE_REQ_SEND_RESPONSE_HEADER_EX, &header_info, NULL, NULL);

        efree(combined_headers);
        if (status_buf) {
                efree(status_buf);
        }
        return SAPI_HEADER_SENT_SUCCESSFULLY;
}


static int php_isapi_startup(sapi_module_struct *sapi_module)
{
        if (php_module_startup(sapi_module, &php_isapi_module, 1)==FAILURE) {
                return FAILURE;
        } else {
                bTerminateThreadsOnError = (zend_bool) INI_INT("isapi.terminate_threads_on_error");
                return SUCCESS;
        }
}


static int sapi_isapi_read_post(char *buffer, uint count_bytes TSRMLS_DC)
{
        LPEXTENSION_CONTROL_BLOCK lpECB = (LPEXTENSION_CONTROL_BLOCK) SG(server_context);
        DWORD read_from_buf=0;
        DWORD read_from_input=0;
        DWORD total_read=0;

        if ((DWORD) SG(read_post_bytes) < lpECB->cbAvailable) {
                read_from_buf = MIN(lpECB->cbAvailable-SG(read_post_bytes), count_bytes);
                memcpy(buffer, lpECB->lpbData+SG(read_post_bytes), read_from_buf);
                total_read += read_from_buf;
        }
        if (read_from_buf<count_bytes
                && (SG(read_post_bytes)+read_from_buf) < lpECB->cbTotalBytes) {
                DWORD cbRead=0, cbSize;

                read_from_input = MIN(count_bytes-read_from_buf, lpECB->cbTotalBytes-SG(read_post_bytes)-read_from_buf);
                while (cbRead < read_from_input) {
                        cbSize = read_from_input - cbRead;
                        if (!lpECB->ReadClient(lpECB->ConnID, buffer+read_from_buf+cbRead, &cbSize) || cbSize==0) {
                                break;
                        }
                        cbRead += cbSize;
                }
                total_read += cbRead;
        }
        return total_read;
}


static char *sapi_isapi_read_cookies(TSRMLS_D)
{
        LPEXTENSION_CONTROL_BLOCK lpECB = (LPEXTENSION_CONTROL_BLOCK) SG(server_context);
        char variable_buf[ISAPI_SERVER_VAR_BUF_SIZE];
        DWORD variable_len = ISAPI_SERVER_VAR_BUF_SIZE;

        if (lpECB->GetServerVariable(lpECB->ConnID, "HTTP_COOKIE", variable_buf, &variable_len)) {
                return estrndup(variable_buf, variable_len);
        } else if (GetLastError()==ERROR_INSUFFICIENT_BUFFER) {
                char *tmp_variable_buf = (char *) emalloc(variable_len+1);

                if (lpECB->GetServerVariable(lpECB->ConnID, "HTTP_COOKIE", tmp_variable_buf, &variable_len)) {
                        tmp_variable_buf[variable_len] = 0;
                        return tmp_variable_buf;
                } else {
                        efree(tmp_variable_buf);
                }
        }
        return STR_EMPTY_ALLOC();
}


#ifdef WITH_ZEUS

static void sapi_isapi_register_zeus_ssl_variables(LPEXTENSION_CONTROL_BLOCK lpECB, zval *track_vars_array TSRMLS_DC)
{
        char static_variable_buf[ISAPI_SERVER_VAR_BUF_SIZE];
        DWORD variable_len = ISAPI_SERVER_VAR_BUF_SIZE;
        char static_cons_buf[ISAPI_SERVER_VAR_BUF_SIZE];
        /*
         * We need to construct the /C=.../ST=...
         * DN's for SSL_CLIENT_DN and SSL_CLIENT_I_DN
         */
        strcpy( static_cons_buf, "/C=" );
        if( lpECB->GetServerVariable( lpECB->ConnID, "SSL_CLIENT_C", static_variable_buf, &variable_len ) && static_variable_buf[0] ) {
                strlcat( static_cons_buf, static_variable_buf,  ISAPI_SERVER_VAR_BUF_SIZE);
        }
        strlcat( static_cons_buf, "/ST=",  ISAPI_SERVER_VAR_BUF_SIZE);
        variable_len = ISAPI_SERVER_VAR_BUF_SIZE;
        if( lpECB->GetServerVariable( lpECB->ConnID, "SSL_CLIENT_ST", static_variable_buf, &variable_len ) && static_variable_buf[0] ) {
                strlcat( static_cons_buf, static_variable_buf, ISAPI_SERVER_VAR_BUF_SIZE );
        }
        php_register_variable( "SSL_CLIENT_DN", static_cons_buf, track_vars_array TSRMLS_CC );
        
        strcpy( static_cons_buf, "/C=" );
        variable_len = ISAPI_SERVER_VAR_BUF_SIZE;
        if( lpECB->GetServerVariable( lpECB->ConnID, "SSL_CLIENT_I_C", static_variable_buf, &variable_len ) && static_variable_buf[0] ) {
                strlcat( static_cons_buf, static_variable_buf, ISAPI_SERVER_VAR_BUF_SIZE );
        }
        strlcat( static_cons_buf, "/ST=", ISAPI_SERVER_VAR_BUF_SIZE);
        variable_len = ISAPI_SERVER_VAR_BUF_SIZE;
        if( lpECB->GetServerVariable( lpECB->ConnID, "SSL_CLIENT_I_ST", static_variable_buf, &variable_len ) && static_variable_buf[0] ) {
                strlcat( static_cons_buf, static_variable_buf, ISAPI_SERVER_VAR_BUF_SIZE );
        }
        php_register_variable( "SSL_CLIENT_I_DN", static_cons_buf, track_vars_array TSRMLS_CC );        
}

static void sapi_isapi_register_zeus_variables(LPEXTENSION_CONTROL_BLOCK lpECB, zval *track_vars_array TSRMLS_DC)
{
        char static_variable_buf[ISAPI_SERVER_VAR_BUF_SIZE];
        DWORD variable_len = ISAPI_SERVER_VAR_BUF_SIZE;
        DWORD scriptname_len = ISAPI_SERVER_VAR_BUF_SIZE;
        DWORD pathinfo_len = 0;
        char *strtok_buf = NULL;

        /* Get SCRIPT_NAME, we use this to work out which bit of the URL
         * belongs in PHP's version of PATH_INFO
         */
        lpECB->GetServerVariable(lpECB->ConnID, "SCRIPT_NAME", static_variable_buf, &scriptname_len);

        /* Adjust Zeus' version of PATH_INFO, set PHP_SELF,
         * and generate REQUEST_URI
         */
        if ( lpECB->GetServerVariable(lpECB->ConnID, "PATH_INFO", static_variable_buf, &variable_len) && static_variable_buf[0] ) {

                /* PHP_SELF is just PATH_INFO */
                php_register_variable( "PHP_SELF", static_variable_buf, track_vars_array TSRMLS_CC );

                /* Chop off filename to get just the 'real' PATH_INFO' */
                pathinfo_len = variable_len - scriptname_len;
                php_register_variable( "PATH_INFO", static_variable_buf + scriptname_len - 1, track_vars_array TSRMLS_CC );
                /* append query string to give url... extra byte for '?' */
                if ( strlen(lpECB->lpszQueryString) + variable_len + 1 < ISAPI_SERVER_VAR_BUF_SIZE ) {
                        /* append query string only if it is present... */
                        if ( strlen(lpECB->lpszQueryString) ) {
                                static_variable_buf[ variable_len - 1 ] = '?';
                                strcpy( static_variable_buf + variable_len, lpECB->lpszQueryString );
                        }
                        php_register_variable( "URL", static_variable_buf, track_vars_array TSRMLS_CC );
                        php_register_variable( "REQUEST_URI", static_variable_buf, track_vars_array TSRMLS_CC );
                }
        }

        /* Get and adjust PATH_TRANSLATED to what PHP wants */
        variable_len = ISAPI_SERVER_VAR_BUF_SIZE;
        if ( lpECB->GetServerVariable(lpECB->ConnID, "PATH_TRANSLATED", static_variable_buf, &variable_len) && static_variable_buf[0] ) {
                static_variable_buf[ variable_len - pathinfo_len - 1 ] = '\0';
                php_register_variable( "PATH_TRANSLATED", static_variable_buf, track_vars_array TSRMLS_CC );
        }

        /* Bring in the AUTHENTICATION stuff as needed */
        variable_len = ISAPI_SERVER_VAR_BUF_SIZE;
        if ( lpECB->GetServerVariable(lpECB->ConnID, "AUTH_USER", static_variable_buf, &variable_len) && static_variable_buf[0] )  {
                php_register_variable( "PHP_AUTH_USER", static_variable_buf, track_vars_array TSRMLS_CC );
        }
        variable_len = ISAPI_SERVER_VAR_BUF_SIZE;
        if ( lpECB->GetServerVariable(lpECB->ConnID, "AUTH_PASSWORD", static_variable_buf, &variable_len) && static_variable_buf[0] )  {
                php_register_variable( "PHP_AUTH_PW", static_variable_buf, track_vars_array TSRMLS_CC );
        }
        variable_len = ISAPI_SERVER_VAR_BUF_SIZE;
        if ( lpECB->GetServerVariable(lpECB->ConnID, "AUTH_TYPE", static_variable_buf, &variable_len) && static_variable_buf[0] )  {
                php_register_variable( "AUTH_TYPE", static_variable_buf, track_vars_array TSRMLS_CC );
        }
        
        /* And now, for the SSL variables (if applicable) */
        variable_len = ISAPI_SERVER_VAR_BUF_SIZE;
        if ( lpECB->GetServerVariable(lpECB->ConnID, "CERT_COOKIE", static_variable_buf, &variable_len) && static_variable_buf[0] ) {
                sapi_isapi_register_zeus_ssl_variables( lpECB, track_vars_array TSRMLS_CC );
        }
        /* Copy some of the variables we need to meet Apache specs */
        variable_len = ISAPI_SERVER_VAR_BUF_SIZE;
        if ( lpECB->GetServerVariable(lpECB->ConnID, "SERVER_SOFTWARE", static_variable_buf, &variable_len) && static_variable_buf[0] )  {
                php_register_variable( "SERVER_SIGNATURE", static_variable_buf, track_vars_array TSRMLS_CC );
        }
}
#else

static void sapi_isapi_register_iis_variables(LPEXTENSION_CONTROL_BLOCK lpECB, zval *track_vars_array TSRMLS_DC)
{
        char static_variable_buf[ISAPI_SERVER_VAR_BUF_SIZE];
        char path_info_buf[ISAPI_SERVER_VAR_BUF_SIZE];
        DWORD variable_len = ISAPI_SERVER_VAR_BUF_SIZE;
        DWORD scriptname_len = ISAPI_SERVER_VAR_BUF_SIZE;
        DWORD pathinfo_len = 0;
        HSE_URL_MAPEX_INFO humi;

        /* Get SCRIPT_NAME, we use this to work out which bit of the URL
         * belongs in PHP's version of PATH_INFO.  SCRIPT_NAME also becomes PHP_SELF.
         */
        lpECB->GetServerVariable(lpECB->ConnID, "SCRIPT_NAME", static_variable_buf, &scriptname_len);
        php_register_variable("SCRIPT_FILENAME", SG(request_info).path_translated, track_vars_array TSRMLS_CC);

        /* Adjust IIS' version of PATH_INFO, set PHP_SELF,
         * and generate REQUEST_URI
         * Get and adjust PATH_TRANSLATED to what PHP wants
         */
        if ( lpECB->GetServerVariable(lpECB->ConnID, "PATH_INFO", static_variable_buf, &variable_len) && static_variable_buf[0] ) {

                /* Chop off filename to get just the 'real' PATH_INFO' */
                php_register_variable( "ORIG_PATH_INFO", static_variable_buf, track_vars_array TSRMLS_CC );
                pathinfo_len = variable_len - scriptname_len;
                strncpy(path_info_buf, static_variable_buf + scriptname_len - 1, sizeof(path_info_buf)-1);
                php_register_variable( "PATH_INFO", path_info_buf, track_vars_array TSRMLS_CC );
                /* append query string to give url... extra byte for '?' */
                if ( strlen(lpECB->lpszQueryString) + variable_len + 1 < ISAPI_SERVER_VAR_BUF_SIZE ) {
                        /* append query string only if it is present... */
                        if ( strlen(lpECB->lpszQueryString) ) {
                                static_variable_buf[ variable_len - 1 ] = '?';
                                strcpy( static_variable_buf + variable_len, lpECB->lpszQueryString );
                        }
                        php_register_variable( "URL", static_variable_buf, track_vars_array TSRMLS_CC );
                        php_register_variable( "REQUEST_URI", static_variable_buf, track_vars_array TSRMLS_CC );
                }
                variable_len = ISAPI_SERVER_VAR_BUF_SIZE;
                if ( lpECB->GetServerVariable(lpECB->ConnID, "PATH_TRANSLATED", static_variable_buf, &variable_len) && static_variable_buf[0] ) {
                        php_register_variable( "ORIG_PATH_TRANSLATED", static_variable_buf, track_vars_array TSRMLS_CC );
                }
                if (lpECB->ServerSupportFunction(lpECB->ConnID, HSE_REQ_MAP_URL_TO_PATH_EX, path_info_buf, &pathinfo_len, (LPDWORD) &humi)) {
                        /* Remove trailing \  */
                        if (humi.lpszPath[variable_len-2] == '\\') {
                                humi.lpszPath[variable_len-2] = 0;
                        }
                        php_register_variable("PATH_TRANSLATED", humi.lpszPath, track_vars_array TSRMLS_CC);
                }
        }

        static_variable_buf[0] = '/';
        static_variable_buf[1] = 0;
        variable_len = 2;
        if (lpECB->ServerSupportFunction(lpECB->ConnID, HSE_REQ_MAP_URL_TO_PATH_EX, static_variable_buf, &variable_len, (LPDWORD) &humi)) {
                /* Remove trailing \  */
                if (humi.lpszPath[variable_len-2] == '\\') {
                        humi.lpszPath[variable_len-2] = 0;
                }
                php_register_variable("DOCUMENT_ROOT", humi.lpszPath, track_vars_array TSRMLS_CC);
        }

        if (!SG(request_info).auth_user || !SG(request_info).auth_password || 
                !SG(request_info).auth_user[0] || !SG(request_info).auth_password[0]) {
                variable_len = ISAPI_SERVER_VAR_BUF_SIZE;
                if (lpECB->GetServerVariable(lpECB->ConnID, "HTTP_AUTHORIZATION", static_variable_buf, &variable_len)
                        && static_variable_buf[0]) {
                        php_handle_auth_data(static_variable_buf TSRMLS_CC);
                }
        }

        if (SG(request_info).auth_user)  {
                php_register_variable("PHP_AUTH_USER", SG(request_info).auth_user, track_vars_array TSRMLS_CC );
        }
        if (SG(request_info).auth_password) {
                php_register_variable("PHP_AUTH_PW", SG(request_info).auth_password, track_vars_array TSRMLS_CC );
        }
}
#endif

static void sapi_isapi_register_server_variables2(char **server_variables, LPEXTENSION_CONTROL_BLOCK lpECB, zval *track_vars_array, char **recorded_values TSRMLS_DC)
{
        char **p=server_variables;
        DWORD variable_len;
        char static_variable_buf[ISAPI_SERVER_VAR_BUF_SIZE];
        char *variable_buf;

        while (*p) {
                variable_len = ISAPI_SERVER_VAR_BUF_SIZE;
                if (lpECB->GetServerVariable(lpECB->ConnID, *p, static_variable_buf, &variable_len)
                        && static_variable_buf[0]) {
                        php_register_variable(*p, static_variable_buf, track_vars_array TSRMLS_CC);
                        if (recorded_values) {
                                recorded_values[p-server_variables] = estrndup(static_variable_buf, variable_len);
                        }
                } else if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
                        variable_buf = (char *) emalloc(variable_len+1);
                        if (lpECB->GetServerVariable(lpECB->ConnID, *p, variable_buf, &variable_len)
                                && variable_buf[0]) {
                                php_register_variable(*p, variable_buf, track_vars_array TSRMLS_CC);
                        }
                        if (recorded_values) {
                                recorded_values[p-server_variables] = variable_buf;
                        } else {
                                efree(variable_buf);
                        }
                } else { /* for compatibility with Apache SAPIs */
                        php_register_variable(*p, "", track_vars_array TSRMLS_CC);
                }
                p++;
        }
}


static void sapi_isapi_register_server_variables(zval *track_vars_array TSRMLS_DC)
{
        DWORD variable_len = ISAPI_SERVER_VAR_BUF_SIZE;
        char *variable;
        char *strtok_buf = NULL;
        char *isapi_special_server_variables[NUM_SPECIAL_VARS];
        LPEXTENSION_CONTROL_BLOCK lpECB;

        lpECB = (LPEXTENSION_CONTROL_BLOCK) SG(server_context);

        /* Register the special ISAPI variables */
        memset(isapi_special_server_variables, 0, sizeof(isapi_special_server_variables));
        sapi_isapi_register_server_variables2(isapi_special_server_variable_names, lpECB, track_vars_array, isapi_special_server_variables TSRMLS_CC);
        if (SG(request_info).cookie_data) {
                php_register_variable("HTTP_COOKIE", SG(request_info).cookie_data, track_vars_array TSRMLS_CC);
        }

        /* Register the standard ISAPI variables */
        sapi_isapi_register_server_variables2(isapi_server_variable_names, lpECB, track_vars_array, NULL TSRMLS_CC);

        if (isapi_special_server_variables[SPECIAL_VAR_HTTPS]
                && (atoi(isapi_special_server_variables[SPECIAL_VAR_HTTPS])
                || !strcasecmp(isapi_special_server_variables[SPECIAL_VAR_HTTPS], "on"))
        ) {
                /* Register SSL ISAPI variables */
                sapi_isapi_register_server_variables2(isapi_secure_server_variable_names, lpECB, track_vars_array, NULL TSRMLS_CC);
        }

        if (isapi_special_server_variables[SPECIAL_VAR_HTTPS]) {
                efree(isapi_special_server_variables[SPECIAL_VAR_HTTPS]);
        }


#ifdef WITH_ZEUS
        sapi_isapi_register_zeus_variables(lpECB, track_vars_array TSRMLS_CC);
#else
        sapi_isapi_register_iis_variables(lpECB, track_vars_array TSRMLS_CC);
#endif

        /* PHP_SELF support */
        if (isapi_special_server_variables[SPECIAL_VAR_PHP_SELF]) {
                php_register_variable("PHP_SELF", isapi_special_server_variables[SPECIAL_VAR_PHP_SELF], track_vars_array TSRMLS_CC);
                efree(isapi_special_server_variables[SPECIAL_VAR_PHP_SELF]);
        }

        if (isapi_special_server_variables[SPECIAL_VAR_ALL_HTTP]) {
                /* Register the internal bits of ALL_HTTP */
                variable = php_strtok_r(isapi_special_server_variables[SPECIAL_VAR_ALL_HTTP], "\r\n", &strtok_buf);
                while (variable) {
                        char *colon = strchr(variable, ':');

                        if (colon) {
                                char *value = colon+1;

                                while (*value==' ') {
                                        value++;
                                }
                                *colon = 0;
                                php_register_variable(variable, value, track_vars_array TSRMLS_CC);
                                *colon = ':';
                        }
                        variable = php_strtok_r(NULL, "\r\n", &strtok_buf);
                }
                efree(isapi_special_server_variables[SPECIAL_VAR_ALL_HTTP]);
        }
}


static sapi_module_struct isapi_sapi_module = {
        "isapi",                                                /* name */
        "ISAPI",                                                /* pretty name */
                                                                        
        php_isapi_startup,                              /* startup */
        php_module_shutdown_wrapper,    /* shutdown */

        NULL,                                                   /* activate */
        NULL,                                                   /* deactivate */

        sapi_isapi_ub_write,                    /* unbuffered write */
        NULL,                                                   /* flush */
        NULL,                                                   /* get uid */
        NULL,                                                   /* getenv */

        php_error,                                              /* error handler */

        sapi_isapi_header_handler,              /* header handler */
        sapi_isapi_send_headers,                /* send headers handler */
        NULL,                                                   /* send header handler */

        sapi_isapi_read_post,                   /* read POST data */
        sapi_isapi_read_cookies,                /* read Cookies */

        sapi_isapi_register_server_variables,   /* register server variables */
        NULL,                                                   /* Log message */
        NULL,                                                   /* Get request time */
        NULL,                                                   /* Child terminate */

        STANDARD_SAPI_MODULE_PROPERTIES
};


BOOL WINAPI GetFilterVersion(PHTTP_FILTER_VERSION pFilterVersion)
{
        bFilterLoaded = 1;
        pFilterVersion->dwFilterVersion = HTTP_FILTER_REVISION;
        strcpy(pFilterVersion->lpszFilterDesc, isapi_sapi_module.pretty_name);
        pFilterVersion->dwFlags= (SF_NOTIFY_AUTHENTICATION | SF_NOTIFY_PREPROC_HEADERS);
        return TRUE;
}


DWORD WINAPI HttpFilterProc(PHTTP_FILTER_CONTEXT pfc, DWORD notificationType, LPVOID pvNotification)
{
        TSRMLS_FETCH();

        switch (notificationType) {
                case SF_NOTIFY_PREPROC_HEADERS:
                        SG(request_info).auth_user = NULL;
                        SG(request_info).auth_password = NULL;
                        SG(request_info).auth_digest = NULL;
                        break;
                case SF_NOTIFY_AUTHENTICATION: {
                                char *auth_user = ((HTTP_FILTER_AUTHENT *) pvNotification)->pszUser;
                                char *auth_password = ((HTTP_FILTER_AUTHENT *) pvNotification)->pszPassword;

                                if (auth_user && auth_user[0]) {
                                        SG(request_info).auth_user = estrdup(auth_user);
                                }       
                                if (auth_password && auth_password[0]) {
                                        SG(request_info).auth_password = estrdup(auth_password);
                                }
                                return SF_STATUS_REQ_HANDLED_NOTIFICATION;
                        }
                        break;
        }
        return SF_STATUS_REQ_NEXT_NOTIFICATION;
}


static void init_request_info(LPEXTENSION_CONTROL_BLOCK lpECB TSRMLS_DC)
{
        DWORD variable_len = ISAPI_SERVER_VAR_BUF_SIZE;
        char static_variable_buf[ISAPI_SERVER_VAR_BUF_SIZE];
#ifndef WITH_ZEUS
        HSE_URL_MAPEX_INFO humi;
#endif

        SG(request_info).request_method = lpECB->lpszMethod;
        SG(request_info).query_string = lpECB->lpszQueryString;
        SG(request_info).request_uri = lpECB->lpszPathInfo;
        SG(request_info).content_type = lpECB->lpszContentType;
        SG(request_info).content_length = lpECB->cbTotalBytes;
        SG(sapi_headers).http_response_code = 200;  /* I think dwHttpStatusCode is invalid at this stage -RL */
        if (!bFilterLoaded) { /* we don't have valid ISAPI Filter information */
                SG(request_info).auth_user = SG(request_info).auth_password = SG(request_info).auth_digest = NULL;
        }

#ifdef WITH_ZEUS
        /* PATH_TRANSLATED can contain extra PATH_INFO stuff after the
         * file being loaded, so we must use SCRIPT_FILENAME instead
         */
        if(lpECB->GetServerVariable(lpECB->ConnID, "SCRIPT_FILENAME", static_variable_buf, &variable_len)) {
                SG(request_info).path_translated = estrdup(static_variable_buf);
        } else 
#else
        /* happily, IIS gives us SCRIPT_NAME which is correct (without PATH_INFO stuff)
           so we can just map that to the physical path and we have our filename */

        lpECB->GetServerVariable(lpECB->ConnID, "SCRIPT_NAME", static_variable_buf, &variable_len);
        if (lpECB->ServerSupportFunction(lpECB->ConnID, HSE_REQ_MAP_URL_TO_PATH_EX, static_variable_buf, &variable_len, (LPDWORD) &humi)) {
                SG(request_info).path_translated = estrdup(humi.lpszPath);
        } else 
#endif
                /* if mapping fails, default to what the server tells us */
                SG(request_info).path_translated = estrdup(lpECB->lpszPathTranslated);

        /* some server configurations allow '..' to slip through in the
           translated path.   We'll just refuse to handle such a path. */
        if (strstr(SG(request_info).path_translated,"..")) {
                SG(sapi_headers).http_response_code = 404;
                efree(SG(request_info).path_translated);
                SG(request_info).path_translated = NULL;
        }
}


static void php_isapi_report_exception(char *message, int message_len TSRMLS_DC)
{
        if (!SG(headers_sent)) {
                HSE_SEND_HEADER_EX_INFO header_info;
                LPEXTENSION_CONTROL_BLOCK lpECB = (LPEXTENSION_CONTROL_BLOCK) SG(server_context);

                header_info.pszStatus = "500 Internal Server Error";
                header_info.cchStatus = strlen(header_info.pszStatus);
                header_info.pszHeader = "Content-Type: text/html\r\n\r\n";
                header_info.cchHeader = strlen(header_info.pszHeader);

                lpECB->dwHttpStatusCode = 500;
                lpECB->ServerSupportFunction(lpECB->ConnID, HSE_REQ_SEND_RESPONSE_HEADER_EX, &header_info, NULL, NULL);
                SG(headers_sent)=1;
        }
        sapi_isapi_ub_write(message, message_len TSRMLS_CC);
}


BOOL WINAPI GetExtensionVersion(HSE_VERSION_INFO *pVer)
{
        pVer->dwExtensionVersion = HSE_VERSION;
#ifdef WITH_ZEUS
        strncpy( pVer->lpszExtensionDesc, isapi_sapi_module.name, HSE_MAX_EXT_DLL_NAME_LEN);
#else
        lstrcpyn(pVer->lpszExtensionDesc, isapi_sapi_module.name, HSE_MAX_EXT_DLL_NAME_LEN);
#endif
        return TRUE;
}


static void my_endthread()
{
#ifdef PHP_WIN32
        if (bTerminateThreadsOnError) {
                _endthread();
        }
#endif
}

#ifdef PHP_WIN32
/* ep is accessible only in the context of the __except expression,
 * so we have to call this function to obtain it.
 */
BOOL exceptionhandler(LPEXCEPTION_POINTERS *e, LPEXCEPTION_POINTERS ep)
{
        *e=ep;
        return TRUE;
}
#endif

DWORD WINAPI HttpExtensionProc(LPEXTENSION_CONTROL_BLOCK lpECB)
{
        zend_file_handle file_handle;
        zend_bool stack_overflown=0;
        int retval = FAILURE;
#ifdef PHP_ENABLE_SEH
        LPEXCEPTION_POINTERS e;
#endif
        TSRMLS_FETCH();

        zend_first_try {
#ifdef PHP_ENABLE_SEH
                __try {
#endif
                        init_request_info(lpECB TSRMLS_CC);
                        SG(server_context) = lpECB;

                        php_request_startup(TSRMLS_C);

                        file_handle.filename = SG(request_info).path_translated;
                        file_handle.free_filename = 0;
                        file_handle.type = ZEND_HANDLE_FILENAME;
                        file_handle.opened_path = NULL;

                        /* open the script here so we can 404 if it fails */
                        if (file_handle.filename)
                                retval = php_fopen_primary_script(&file_handle TSRMLS_CC);

                        if (!file_handle.filename || retval == FAILURE) {
                                SG(sapi_headers).http_response_code = 404;
                                PUTS("No input file specified.\n");
                        } else {
                                php_execute_script(&file_handle TSRMLS_CC);
                        }

                        if (SG(request_info).cookie_data) {
                                efree(SG(request_info).cookie_data);
                        }
                        if (SG(request_info).path_translated)
                                efree(SG(request_info).path_translated);
#ifdef PHP_ENABLE_SEH
                } __except(exceptionhandler(&e, GetExceptionInformation())) {
                        char buf[1024];
                        if (_exception_code()==EXCEPTION_STACK_OVERFLOW) {
                                LPBYTE lpPage;
                                static SYSTEM_INFO si;
                                static MEMORY_BASIC_INFORMATION mi;
                                static DWORD dwOldProtect;

                                GetSystemInfo(&si);

                                /* Get page ESP is pointing to */
                                _asm mov lpPage, esp;

                                /* Get stack allocation base */
                                VirtualQuery(lpPage, &mi, sizeof(mi));

                                /* Go to the page below the current page */
                                lpPage = (LPBYTE) (mi.BaseAddress) - si.dwPageSize;

                                /* Free pages below current page */
                                if (!VirtualFree(mi.AllocationBase, (LPBYTE)lpPage - (LPBYTE) mi.AllocationBase, MEM_DECOMMIT)) {
                                        _endthread();
                                }

                                /* Restore the guard page */
                                if (!VirtualProtect(lpPage, si.dwPageSize, PAGE_GUARD | PAGE_READWRITE, &dwOldProtect)) {
                                        _endthread();
                                }

                                CG(unclean_shutdown)=1;
                                _snprintf(buf, sizeof(buf)-1,"PHP has encountered a Stack overflow");
                                php_isapi_report_exception(buf, strlen(buf) TSRMLS_CC);
                        } else if (_exception_code()==EXCEPTION_ACCESS_VIOLATION) {
                                _snprintf(buf, sizeof(buf)-1,"PHP has encountered an Access Violation at %p", e->ExceptionRecord->ExceptionAddress);
                                php_isapi_report_exception(buf, strlen(buf) TSRMLS_CC);
                                my_endthread();
                        } else {
                                _snprintf(buf, sizeof(buf)-1,"PHP has encountered an Unhandled Exception Code %d at %p", e->ExceptionRecord->ExceptionCode , e->ExceptionRecord->ExceptionAddress);
                                php_isapi_report_exception(buf, strlen(buf) TSRMLS_CC);
                                my_endthread();
                        }
                }
#endif
#ifdef PHP_ENABLE_SEH
                __try {
                        php_request_shutdown(NULL);
                } __except(EXCEPTION_EXECUTE_HANDLER) {
                        my_endthread();
                }
#else
                php_request_shutdown(NULL);
#endif
        } zend_catch {
                zend_try {
                        php_request_shutdown(NULL);
                } zend_end_try();
                return HSE_STATUS_ERROR;
        } zend_end_try();

        return HSE_STATUS_SUCCESS;
}



__declspec(dllexport) BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
        switch (fdwReason) {
                case DLL_PROCESS_ATTACH:
#ifdef WITH_ZEUS
                        tsrm_startup(128, 1, TSRM_ERROR_LEVEL_CORE, "TSRM.log");
#else
                        tsrm_startup(128, 1, TSRM_ERROR_LEVEL_CORE, "C:\\TSRM.log");
#endif
                        sapi_startup(&isapi_sapi_module);
                        if (isapi_sapi_module.startup) {
                                isapi_sapi_module.startup(&sapi_module);
                        }
                        break;
                case DLL_THREAD_ATTACH:
                        break;
                case DLL_THREAD_DETACH:
                        ts_free_thread();
                        break;
                case DLL_PROCESS_DETACH:
                        if (isapi_sapi_module.shutdown) {
                                isapi_sapi_module.shutdown(&sapi_module);
                        }
                        sapi_shutdown();
                        tsrm_shutdown();
                        break;
        }
        return TRUE;
}

/*
 * Local variables:
 * tab-width: 4
 * c-basic-offset: 4
 * End:
 */

/* [<][>][^][v][top][bottom][index][help] */