Back to index

php5  5.3.10
nsapi.c
Go to the documentation of this file.
00001 /*
00002    +----------------------------------------------------------------------+
00003    | PHP Version 5                                                        |
00004    +----------------------------------------------------------------------+
00005    | Copyright (c) 1997-2012 The PHP Group                                |
00006    +----------------------------------------------------------------------+
00007    | This source file is subject to version 3.01 of the PHP license,      |
00008    | that is bundled with this package in the file LICENSE, and is        |
00009    | available through the world-wide-web at the following url:           |
00010    | http://www.php.net/license/3_01.txt                                  |
00011    | If you did not receive a copy of the PHP license and are unable to   |
00012    | obtain it through the world-wide-web, please send a note to          |
00013    | license@php.net so we can mail you a copy immediately.               |
00014    +----------------------------------------------------------------------+
00015    | Author: Jayakumar Muthukumarasamy <jk@kasenna.com>                   |
00016    |         Uwe Schindler <uwe@thetaphi.de>                              |
00017    +----------------------------------------------------------------------+
00018 */
00019 
00020 /* $Id: nsapi.c 321634 2012-01-01 13:15:04Z felipe $ */
00021 
00022 /*
00023  * PHP includes
00024  */
00025 #define NSAPI 1
00026 
00027 #ifdef HAVE_CONFIG_H
00028 #include "config.h"
00029 #endif
00030 
00031 #include "php.h"
00032 #include "php_variables.h"
00033 #include "ext/standard/info.h"
00034 #include "php_ini.h"
00035 #include "php_globals.h"
00036 #include "SAPI.h"
00037 #include "php_main.h"
00038 #include "php_version.h"
00039 #include "TSRM.h"
00040 #include "ext/standard/php_standard.h"
00041 #include <sys/types.h>
00042 #include <sys/stat.h>
00043 
00044 #ifndef RTLD_DEFAULT
00045 #define RTLD_DEFAULT NULL
00046 #endif
00047 
00048 /*
00049  * If neither XP_UNIX not XP_WIN32 is defined use PHP_WIN32
00050  */
00051 #if !defined(XP_UNIX) && !defined(XP_WIN32)
00052 #ifdef PHP_WIN32
00053 #define XP_WIN32
00054 #else
00055 #define XP_UNIX
00056 #endif
00057 #endif
00058 
00059 /*
00060  * The manual define of HPUX is to fix bug #46020, nsapi.h needs this to detect HPUX
00061  */
00062 #ifdef __hpux
00063 #define HPUX
00064 #endif
00065  
00066 /*
00067  * NSAPI includes
00068  */
00069 #include "nsapi.h"
00070 
00071 /* fix for gcc4 visibility issue */
00072 #ifndef PHP_WIN32
00073 # undef NSAPI_PUBLIC
00074 # define NSAPI_PUBLIC PHPAPI
00075 #endif
00076 
00077 #define NSLS_D              struct nsapi_request_context *request_context
00078 #define NSLS_DC             , NSLS_D
00079 #define NSLS_C              request_context
00080 #define NSLS_CC             , NSLS_C
00081 #define NSG(v)              (request_context->v)
00082 
00083 /*
00084  * ZTS needs to be defined for NSAPI to work
00085  */
00086 #if !defined(ZTS)
00087 #error "NSAPI module needs ZTS to be defined"
00088 #endif
00089 
00090 /*
00091  * Structure to encapsulate the NSAPI request in SAPI
00092  */
00093 typedef struct nsapi_request_context {
00094        pblock *pb;
00095        Session       *sn;
00096        Request       *rq;
00097        int    read_post_bytes;
00098        char *path_info;
00099        int fixed_script; /* 0 if script is from URI, 1 if script is from "script" parameter */
00100        short http_error; /* 0 in normal mode; for errors the HTTP error code */
00101 } nsapi_request_context;
00102 
00103 /*
00104  * Mappings between NSAPI names and environment variables. This
00105  * mapping was obtained from the sample programs at the iplanet
00106  * website.
00107  */
00108 typedef struct nsapi_equiv {
00109        const char *env_var;
00110        const char *nsapi_eq;
00111 } nsapi_equiv;
00112 
00113 static nsapi_equiv nsapi_reqpb[] = {
00114        { "QUERY_STRING",           "query" },
00115        { "REQUEST_LINE",           "clf-request" },
00116        { "REQUEST_METHOD",         "method" },
00117        { "PHP_SELF",               "uri" },
00118        { "SERVER_PROTOCOL", "protocol" }
00119 };
00120 static size_t nsapi_reqpb_size = sizeof(nsapi_reqpb)/sizeof(nsapi_reqpb[0]);
00121 
00122 static nsapi_equiv nsapi_vars[] = {
00123        { "AUTH_TYPE",                     "auth-type" },
00124        { "CLIENT_CERT",            "auth-cert" },
00125        { "REMOTE_USER",            "auth-user" }
00126 };
00127 static size_t nsapi_vars_size = sizeof(nsapi_vars)/sizeof(nsapi_vars[0]);
00128 
00129 static nsapi_equiv nsapi_client[] = {
00130        { "HTTPS_KEYSIZE",          "keysize" },
00131        { "HTTPS_SECRETSIZE",       "secret-keysize" },
00132        { "REMOTE_ADDR",            "ip" },
00133        { "REMOTE_HOST",            "ip" }
00134 };
00135 static size_t nsapi_client_size = sizeof(nsapi_client)/sizeof(nsapi_client[0]);
00136 
00137 /* this parameters to "Service"/"Error" are NSAPI ones which should not be php.ini keys and are excluded */
00138 static char *nsapi_exclude_from_ini_entries[] = { "fn", "type", "method", "directive", "code", "reason", "script", "bucket", NULL };
00139 
00140 static void nsapi_free(void *addr)
00141 {
00142        if (addr != NULL) {
00143               FREE(addr);
00144        }
00145 }
00146 
00147 
00148 /*******************/
00149 /* PHP module part */
00150 /*******************/
00151 
00152 PHP_MINIT_FUNCTION(nsapi);
00153 PHP_MSHUTDOWN_FUNCTION(nsapi);
00154 PHP_RINIT_FUNCTION(nsapi);
00155 PHP_RSHUTDOWN_FUNCTION(nsapi);
00156 PHP_MINFO_FUNCTION(nsapi);
00157 
00158 PHP_FUNCTION(nsapi_virtual);
00159 PHP_FUNCTION(nsapi_request_headers);
00160 PHP_FUNCTION(nsapi_response_headers);
00161 
00162 ZEND_BEGIN_MODULE_GLOBALS(nsapi)
00163        long read_timeout;
00164 ZEND_END_MODULE_GLOBALS(nsapi)
00165 
00166 ZEND_DECLARE_MODULE_GLOBALS(nsapi)
00167 
00168 #define NSAPI_G(v) TSRMG(nsapi_globals_id, zend_nsapi_globals *, v)
00169 
00170 
00171 /* {{{ arginfo */
00172 ZEND_BEGIN_ARG_INFO_EX(arginfo_nsapi_virtual, 0, 0, 1)
00173        ZEND_ARG_INFO(0, uri)
00174 ZEND_END_ARG_INFO()
00175 
00176 ZEND_BEGIN_ARG_INFO(arginfo_nsapi_request_headers, 0)
00177 ZEND_END_ARG_INFO()
00178 
00179 ZEND_BEGIN_ARG_INFO(arginfo_nsapi_response_headers, 0)
00180 ZEND_END_ARG_INFO()
00181 /* }}} */
00182 
00183 /* {{{ nsapi_functions[]
00184  *
00185  * Every user visible function must have an entry in nsapi_functions[].
00186  */
00187 const zend_function_entry nsapi_functions[] = {
00188        PHP_FE(nsapi_virtual,       arginfo_nsapi_virtual)                                         /* Make subrequest */
00189        PHP_FALIAS(virtual, nsapi_virtual, arginfo_nsapi_virtual)                    /* compatibility */
00190        PHP_FE(nsapi_request_headers, arginfo_nsapi_request_headers)          /* get request headers */
00191        PHP_FALIAS(getallheaders, nsapi_request_headers, arginfo_nsapi_request_headers)     /* compatibility */
00192        PHP_FALIAS(apache_request_headers, nsapi_request_headers, arginfo_nsapi_request_headers)   /* compatibility */
00193        PHP_FE(nsapi_response_headers, arginfo_nsapi_response_headers)        /* get response headers */
00194        PHP_FALIAS(apache_response_headers, nsapi_response_headers, arginfo_nsapi_response_headers)       /* compatibility */
00195        {NULL, NULL, NULL}
00196 };
00197 /* }}} */
00198 
00199 /* {{{ nsapi_module_entry
00200  */
00201 zend_module_entry nsapi_module_entry = {
00202        STANDARD_MODULE_HEADER,
00203        "nsapi",
00204        nsapi_functions,
00205        PHP_MINIT(nsapi),
00206        PHP_MSHUTDOWN(nsapi),
00207        NULL,
00208        NULL,
00209        PHP_MINFO(nsapi),
00210        NO_VERSION_YET,
00211        STANDARD_MODULE_PROPERTIES
00212 };
00213 /* }}} */
00214 
00215 /* {{{ PHP_INI
00216  */
00217 PHP_INI_BEGIN()
00218     STD_PHP_INI_ENTRY("nsapi.read_timeout", "60", PHP_INI_ALL, OnUpdateLong, read_timeout, zend_nsapi_globals, nsapi_globals)
00219 PHP_INI_END()
00220 /* }}} */
00221 
00222 /* newer servers hide this functions from the programmer so redefine the functions dynamically
00223    thanks to Chris Elving from Sun for the function declarations */
00224 typedef int (*nsapi_servact_prototype)(Session *sn, Request *rq);
00225 nsapi_servact_prototype nsapi_servact_uri2path = NULL;
00226 nsapi_servact_prototype nsapi_servact_pathchecks = NULL;
00227 nsapi_servact_prototype nsapi_servact_fileinfo = NULL;
00228 nsapi_servact_prototype nsapi_servact_service = NULL;
00229 
00230 #ifdef PHP_WIN32
00231 /* The following dll-names for nsapi are in use at this time. The undocumented
00232  * servact_* functions are always in the newest one, older ones are supported by
00233  * the server only by wrapping the function table nothing else. So choose
00234  * the newest one found in process space for dynamic linking */
00235 static char *nsapi_dlls[] = { "ns-httpd40.dll", "ns-httpd36.dll", "ns-httpd35.dll", "ns-httpd30.dll", NULL };
00236 /* if user specifies an other dll name by server_lib parameter 
00237  * it is placed in the following variable and only this DLL is
00238  * checked for the servact_* functions */
00239 char *nsapi_dll = NULL;
00240 #endif
00241 
00242 /* {{{ php_nsapi_init_dynamic_symbols
00243  */
00244 static void php_nsapi_init_dynamic_symbols(void)
00245 {
00246        /* find address of internal NSAPI functions */
00247 #ifdef PHP_WIN32
00248        register int i;
00249        DL_HANDLE module = NULL;
00250        if (nsapi_dll) {
00251               /* try user specified server_lib */
00252               module = GetModuleHandle(nsapi_dll);
00253               if (!module) {
00254                      log_error(LOG_WARN, "php5_init", NULL, NULL, "Cannot find DLL specified by server_lib parameter: %s", nsapi_dll);
00255               }
00256        } else {
00257               /* find a LOADED dll module from nsapi_dlls */
00258               for (i=0; nsapi_dlls[i]; i++) {
00259                      if (module = GetModuleHandle(nsapi_dlls[i])) {
00260                             break;
00261                      }
00262               }
00263        }
00264        if (!module) return;
00265 #else
00266        DL_HANDLE module = RTLD_DEFAULT;
00267 #endif
00268        nsapi_servact_uri2path = (nsapi_servact_prototype)DL_FETCH_SYMBOL(module, "INTservact_uri2path");
00269        nsapi_servact_pathchecks = (nsapi_servact_prototype)DL_FETCH_SYMBOL(module, "INTservact_pathchecks");
00270        nsapi_servact_fileinfo = (nsapi_servact_prototype)DL_FETCH_SYMBOL(module, "INTservact_fileinfo");
00271        nsapi_servact_service = (nsapi_servact_prototype)DL_FETCH_SYMBOL(module, "INTservact_service");
00272        if (!(nsapi_servact_uri2path && nsapi_servact_pathchecks && nsapi_servact_fileinfo && nsapi_servact_service)) {
00273               /* not found - could be cause they are undocumented */
00274               nsapi_servact_uri2path = NULL;
00275               nsapi_servact_pathchecks = NULL;
00276               nsapi_servact_fileinfo = NULL;
00277               nsapi_servact_service = NULL;
00278        }
00279 }
00280 /* }}} */
00281 
00282 /* {{{ php_nsapi_init_globals
00283  */
00284 static void php_nsapi_init_globals(zend_nsapi_globals *nsapi_globals)
00285 {
00286        nsapi_globals->read_timeout = 60;
00287 }
00288 /* }}} */
00289 
00290 /* {{{ PHP_MINIT_FUNCTION
00291  */
00292 PHP_MINIT_FUNCTION(nsapi)
00293 {
00294        php_nsapi_init_dynamic_symbols();
00295        ZEND_INIT_MODULE_GLOBALS(nsapi, php_nsapi_init_globals, NULL);
00296        REGISTER_INI_ENTRIES();
00297        return SUCCESS;
00298 }
00299 /* }}} */
00300 
00301 /* {{{ PHP_MSHUTDOWN_FUNCTION
00302  */
00303 PHP_MSHUTDOWN_FUNCTION(nsapi)
00304 {
00305        UNREGISTER_INI_ENTRIES();
00306        return SUCCESS;
00307 }
00308 /* }}} */
00309 
00310 /* {{{ PHP_MINFO_FUNCTION
00311  */
00312 PHP_MINFO_FUNCTION(nsapi)
00313 {
00314        php_info_print_table_start();
00315        php_info_print_table_row(2, "NSAPI Module Revision", "$Revision: 321634 $");
00316        php_info_print_table_row(2, "Server Software", system_version());
00317        php_info_print_table_row(2, "Sub-requests with nsapi_virtual()",
00318         (nsapi_servact_service)?((zend_ini_long("zlib.output_compression", sizeof("zlib.output_compression"), 0))?"not supported with zlib.output_compression":"enabled"):"not supported on this platform" );
00319        php_info_print_table_end();
00320 
00321        DISPLAY_INI_ENTRIES();
00322 }
00323 /* }}} */
00324 
00325 /* {{{ proto bool nsapi_virtual(string uri)
00326    Perform an NSAPI sub-request */
00327 /* This function is equivalent to <!--#include virtual...-->
00328  * in SSI. It does an NSAPI sub-request. It is useful
00329  * for including CGI scripts or .shtml files, or anything else
00330  * that you'd parse through webserver.
00331  */
00332 PHP_FUNCTION(nsapi_virtual)
00333 {
00334        int uri_len,rv;
00335        char *uri,*value;
00336        Request *rq;
00337        nsapi_request_context *rc = (nsapi_request_context *)SG(server_context);
00338 
00339        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &uri, &uri_len) == FAILURE) {
00340               return;
00341        }
00342 
00343        if (!nsapi_servact_service) {
00344               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to include uri '%s' - Sub-requests not supported on this platform", uri);
00345               RETURN_FALSE;
00346        } else if (zend_ini_long("zlib.output_compression", sizeof("zlib.output_compression"), 0)) {
00347               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to include uri '%s' - Sub-requests do not work with zlib.output_compression", uri);
00348               RETURN_FALSE;
00349        } else {
00350               php_end_ob_buffers(1 TSRMLS_CC);
00351               php_header(TSRMLS_C);
00352 
00353               /* do the sub-request */
00354               /* thanks to Chris Elving from Sun for this code sniplet */
00355               if ((rq = request_restart_internal(uri, NULL)) == NULL) {
00356                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to include uri '%s' - Internal request creation failed", uri);
00357                      RETURN_FALSE;
00358               }
00359 
00360               /* insert host of current request to get page from same vhost */
00361               param_free(pblock_remove("host", rq->headers));
00362               if (value = pblock_findval("host", rc->rq->headers)) {
00363                      pblock_nvinsert("host", value, rq->headers);
00364               }
00365 
00366               /* go through the normal request stages as given in obj.conf,
00367                  but leave out the logging/error section */
00368               do {
00369                      rv = (*nsapi_servact_uri2path)(rc->sn, rq);
00370                      if (rv != REQ_PROCEED) {
00371                             continue;
00372                      }
00373 
00374                      rv = (*nsapi_servact_pathchecks)(rc->sn, rq);
00375                      if (rv != REQ_PROCEED) {
00376                             continue;
00377                      }
00378 
00379                      rv = (*nsapi_servact_fileinfo)(rc->sn, rq);
00380                      if (rv != REQ_PROCEED) {
00381                             continue;
00382                      }
00383 
00384                      rv = (*nsapi_servact_service)(rc->sn, rq);
00385               } while (rv == REQ_RESTART);
00386 
00387               if (rq->status_num != 200) {
00388                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to include uri '%s' - HTTP status code %d during subrequest", uri, rq->status_num);
00389                      request_free(rq);
00390                      RETURN_FALSE;
00391               }
00392 
00393               request_free(rq);
00394 
00395               RETURN_TRUE;
00396        }
00397 }
00398 /* }}} */
00399 
00400 /* {{{ proto array nsapi_request_headers(void)
00401    Get all headers from the request */
00402 PHP_FUNCTION(nsapi_request_headers)
00403 {
00404        register int i;
00405        struct pb_entry *entry;
00406        nsapi_request_context *rc = (nsapi_request_context *)SG(server_context);
00407 
00408        if (zend_parse_parameters_none() == FAILURE) {
00409               return;
00410        }
00411        
00412        array_init(return_value);
00413 
00414        for (i=0; i < rc->rq->headers->hsize; i++) {
00415               entry=rc->rq->headers->ht[i];
00416               while (entry) {
00417                      if (!PG(safe_mode) || strncasecmp(entry->param->name, "authorization", 13)) {
00418                             add_assoc_string(return_value, entry->param->name, entry->param->value, 1);
00419                      }
00420                      entry=entry->next;
00421               }
00422        }
00423 }
00424 /* }}} */
00425 
00426 /* {{{ proto array nsapi_response_headers(void)
00427    Get all headers from the response */
00428 PHP_FUNCTION(nsapi_response_headers)
00429 {
00430        register int i;
00431        struct pb_entry *entry;
00432        nsapi_request_context *rc = (nsapi_request_context *)SG(server_context);
00433 
00434        if (zend_parse_parameters_none() == FAILURE) {
00435               return;
00436        }
00437        
00438        array_init(return_value);
00439 
00440        for (i=0; i < rc->rq->srvhdrs->hsize; i++) {
00441               entry=rc->rq->srvhdrs->ht[i];
00442               while (entry) {
00443                      add_assoc_string(return_value, entry->param->name, entry->param->value, 1);
00444                      entry=entry->next;
00445               }
00446        }
00447 }
00448 /* }}} */
00449 
00450 
00451 /*************/
00452 /* SAPI part */
00453 /*************/
00454 
00455 static int sapi_nsapi_ub_write(const char *str, unsigned int str_length TSRMLS_DC)
00456 {
00457        int retval;
00458        nsapi_request_context *rc = (nsapi_request_context *)SG(server_context);
00459        
00460        if (!SG(headers_sent)) {
00461               sapi_send_headers(TSRMLS_C);
00462        }
00463 
00464        retval = net_write(rc->sn->csd, (char *)str, str_length);
00465        if (retval == IO_ERROR /* -1 */ || retval == IO_EOF /* 0 */) {
00466               php_handle_aborted_connection();
00467        }
00468        return retval;
00469 }
00470 
00471 /* modified version of apache2 */
00472 static void sapi_nsapi_flush(void *server_context)
00473 {
00474        nsapi_request_context *rc = (nsapi_request_context *)server_context;
00475        TSRMLS_FETCH();
00476        
00477        if (!rc) {
00478               /* we have no context, so no flushing needed. This fixes a SIGSEGV on shutdown */
00479               return;
00480        }
00481 
00482        if (!SG(headers_sent)) {
00483               sapi_send_headers(TSRMLS_C);
00484        }
00485 
00486        /* flushing is only supported in iPlanet servers from version 6.1 on, make it conditional */
00487 #if NSAPI_VERSION >= 302
00488        if (net_flush(rc->sn->csd) < 0) {
00489               php_handle_aborted_connection();
00490        }
00491 #endif
00492 }
00493 
00494 /* callback for zend_llist_apply on SAPI_HEADER_DELETE_ALL operation */
00495 static int php_nsapi_remove_header(sapi_header_struct *sapi_header TSRMLS_DC)
00496 {
00497        char *header_name, *p;
00498        nsapi_request_context *rc = (nsapi_request_context *)SG(server_context);
00499        
00500        /* copy the header, because NSAPI needs reformatting and we do not want to change the parameter */
00501        header_name = pool_strdup(rc->sn->pool, sapi_header->header);
00502 
00503        /* extract name, this works, if only the header without ':' is given, too */
00504        if (p = strchr(header_name, ':')) {
00505               *p = 0;
00506        }
00507        
00508        /* header_name to lower case because NSAPI reformats the headers and wants lowercase */
00509        for (p=header_name; *p; p++) {
00510               *p=tolower(*p);
00511        }
00512        
00513        /* remove the header */
00514        param_free(pblock_remove(header_name, rc->rq->srvhdrs));
00515        pool_free(rc->sn->pool, header_name);
00516        
00517        return ZEND_HASH_APPLY_KEEP;
00518 }
00519 
00520 static int sapi_nsapi_header_handler(sapi_header_struct *sapi_header, sapi_header_op_enum op, sapi_headers_struct *sapi_headers TSRMLS_DC)
00521 {
00522        char *header_name, *header_content, *p;
00523        nsapi_request_context *rc = (nsapi_request_context *)SG(server_context);
00524 
00525        switch(op) {
00526               case SAPI_HEADER_DELETE_ALL:
00527                      /* this only deletes headers set or overwritten by PHP, headers previously set by NSAPI are left intact */
00528                      zend_llist_apply(&sapi_headers->headers, (llist_apply_func_t) php_nsapi_remove_header TSRMLS_CC);
00529                      return 0;
00530 
00531               case SAPI_HEADER_DELETE:
00532                      /* reuse the zend_llist_apply callback function for this, too */
00533                      php_nsapi_remove_header(sapi_header TSRMLS_CC);
00534                      return 0;
00535 
00536               case SAPI_HEADER_ADD:
00537               case SAPI_HEADER_REPLACE:
00538                      /* copy the header, because NSAPI needs reformatting and we do not want to change the parameter */
00539                      header_name = pool_strdup(rc->sn->pool, sapi_header->header);
00540 
00541                      /* split header and align pointer for content */
00542                      header_content = strchr(header_name, ':');
00543                      if (header_content) {
00544                             *header_content = 0;
00545                             do {
00546                                    header_content++;
00547                             } while (*header_content==' ');
00548                             
00549                             /* header_name to lower case because NSAPI reformats the headers and wants lowercase */
00550                             for (p=header_name; *p; p++) {
00551                                    *p=tolower(*p);
00552                             }
00553 
00554                             /* if REPLACE, remove first.  "Content-type" is always removed, as SAPI has a bug according to this */
00555                             if (op==SAPI_HEADER_REPLACE || strcmp(header_name, "content-type")==0) {
00556                                    param_free(pblock_remove(header_name, rc->rq->srvhdrs));
00557                             }
00558                             /* ADD header to nsapi table */
00559                             pblock_nvinsert(header_name, header_content, rc->rq->srvhdrs);
00560                      }
00561                      
00562                      pool_free(rc->sn->pool, header_name);
00563                      return SAPI_HEADER_ADD;
00564                      
00565               default:
00566                      return 0;
00567        }
00568 }
00569 
00570 static int sapi_nsapi_send_headers(sapi_headers_struct *sapi_headers TSRMLS_DC)
00571 {
00572        int retval;
00573        nsapi_request_context *rc = (nsapi_request_context *)SG(server_context);
00574 
00575        if (SG(sapi_headers).send_default_content_type) {
00576               char *hd;
00577               param_free(pblock_remove("content-type", rc->rq->srvhdrs));
00578               hd = sapi_get_default_content_type(TSRMLS_C);
00579               pblock_nvinsert("content-type", hd, rc->rq->srvhdrs);
00580               efree(hd);
00581        }
00582 
00583        protocol_status(rc->sn, rc->rq, SG(sapi_headers).http_response_code, NULL);
00584        retval = protocol_start_response(rc->sn, rc->rq);
00585 
00586        if (retval == REQ_PROCEED || retval == REQ_NOACTION) {
00587               return SAPI_HEADER_SENT_SUCCESSFULLY;
00588        } else {
00589               return SAPI_HEADER_SEND_FAILED;
00590        }
00591 }
00592 
00593 static int sapi_nsapi_read_post(char *buffer, uint count_bytes TSRMLS_DC)
00594 {
00595        nsapi_request_context *rc = (nsapi_request_context *)SG(server_context);
00596        char *read_ptr = buffer, *content_length_str = NULL;
00597        uint bytes_read = 0;
00598        int length, content_length = 0;
00599        netbuf *nbuf = rc->sn->inbuf;
00600 
00601        /*
00602         *     Yesss!
00603         */
00604        count_bytes = MIN(count_bytes, SG(request_info).content_length-rc->read_post_bytes);
00605        content_length = SG(request_info).content_length;
00606 
00607        if (content_length <= 0) {
00608               return 0;
00609        }
00610 
00611        /*
00612         * Gobble any pending data in the netbuf.
00613         */
00614        length = nbuf->cursize - nbuf->pos;
00615        length = MIN(count_bytes, length);
00616        if (length > 0) {
00617               memcpy(read_ptr, nbuf->inbuf + nbuf->pos, length);
00618               bytes_read += length;
00619               read_ptr += length;
00620               content_length -= length;
00621               nbuf->pos += length;
00622        }
00623 
00624        /*
00625         * Read the remaining from the socket.
00626         */
00627        while (content_length > 0 && bytes_read < count_bytes) {
00628               int bytes_to_read = count_bytes - bytes_read;
00629 
00630               if (content_length < bytes_to_read) {
00631                      bytes_to_read = content_length;
00632               }
00633 
00634               length = net_read(rc->sn->csd, read_ptr, bytes_to_read, NSAPI_G(read_timeout));
00635 
00636               if (length == IO_ERROR || length == IO_EOF) {
00637                      break;
00638               }
00639 
00640               bytes_read += length;
00641               read_ptr += length;
00642               content_length -= length;
00643        }
00644 
00645        if ( bytes_read > 0 ) {
00646               rc->read_post_bytes += bytes_read;
00647        }
00648        return bytes_read;
00649 }
00650 
00651 static char *sapi_nsapi_read_cookies(TSRMLS_D)
00652 {
00653        char *cookie_string;
00654        nsapi_request_context *rc = (nsapi_request_context *)SG(server_context);
00655 
00656        cookie_string = pblock_findval("cookie", rc->rq->headers);
00657        return cookie_string;
00658 }
00659 
00660 static void sapi_nsapi_register_server_variables(zval *track_vars_array TSRMLS_DC)
00661 {
00662        nsapi_request_context *rc = (nsapi_request_context *)SG(server_context);
00663        register size_t i;
00664        int pos;
00665        char *value,*p;
00666        char buf[32];
00667        struct pb_entry *entry;
00668 
00669        for (i = 0; i < nsapi_reqpb_size; i++) {
00670               value = pblock_findval(nsapi_reqpb[i].nsapi_eq, rc->rq->reqpb);
00671               if (value) {
00672                      php_register_variable((char *)nsapi_reqpb[i].env_var, value, track_vars_array TSRMLS_CC);
00673               }
00674        }
00675 
00676        for (i=0; i < rc->rq->headers->hsize; i++) {
00677               entry=rc->rq->headers->ht[i];
00678               while (entry) {
00679                      if (!PG(safe_mode) || strncasecmp(entry->param->name, "authorization", 13)) {
00680                             if (strcasecmp(entry->param->name, "content-length")==0 || strcasecmp(entry->param->name, "content-type")==0) {
00681                                    value=estrdup(entry->param->name);
00682                                    pos = 0;
00683                             } else {
00684                                    spprintf(&value, 0, "HTTP_%s", entry->param->name);
00685                                    pos = 5;
00686                             }
00687                             if (value) {
00688                                    for(p = value + pos; *p; p++) {
00689                                           *p = toupper(*p);
00690                                           if (!isalnum(*p)) {
00691                                                  *p = '_';
00692                                           }
00693                                    }
00694                                    php_register_variable(value, entry->param->value, track_vars_array TSRMLS_CC);
00695                                    efree(value);
00696                             }
00697                      }
00698                      entry=entry->next;
00699               }
00700        }
00701 
00702        for (i = 0; i < nsapi_vars_size; i++) {
00703               value = pblock_findval(nsapi_vars[i].nsapi_eq, rc->rq->vars);
00704               if (value) {
00705                      php_register_variable((char *)nsapi_vars[i].env_var, value, track_vars_array TSRMLS_CC);
00706               }
00707        }
00708 
00709        for (i = 0; i < nsapi_client_size; i++) {
00710               value = pblock_findval(nsapi_client[i].nsapi_eq, rc->sn->client);
00711               if (value) {
00712                      php_register_variable((char *)nsapi_client[i].env_var, value, track_vars_array TSRMLS_CC);
00713               }
00714        }
00715 
00716        if (value = session_dns(rc->sn)) {
00717               php_register_variable("REMOTE_HOST", value, track_vars_array TSRMLS_CC);
00718               nsapi_free(value);
00719        }
00720 
00721        slprintf(buf, sizeof(buf), "%d", conf_getglobals()->Vport);
00722        php_register_variable("SERVER_PORT", buf, track_vars_array TSRMLS_CC);
00723        php_register_variable("SERVER_NAME", conf_getglobals()->Vserver_hostname, track_vars_array TSRMLS_CC);
00724 
00725        value = http_uri2url_dynamic("", "", rc->sn, rc->rq);
00726        php_register_variable("SERVER_URL", value, track_vars_array TSRMLS_CC);
00727        nsapi_free(value);
00728 
00729        php_register_variable("SERVER_SOFTWARE", system_version(), track_vars_array TSRMLS_CC);
00730        if (security_active) {
00731               php_register_variable("HTTPS", "ON", track_vars_array TSRMLS_CC);
00732        }
00733        php_register_variable("GATEWAY_INTERFACE", "CGI/1.1", track_vars_array TSRMLS_CC);
00734 
00735        /* DOCUMENT_ROOT */
00736        if (value = request_translate_uri("/", rc->sn)) {
00737               pos = strlen(value);
00738               php_register_variable_safe("DOCUMENT_ROOT", value, pos-1, track_vars_array TSRMLS_CC);
00739               nsapi_free(value);
00740        }
00741 
00742        /* PATH_INFO / PATH_TRANSLATED */
00743        if (rc->path_info) {
00744               if (value = request_translate_uri(rc->path_info, rc->sn)) {
00745                      php_register_variable("PATH_TRANSLATED", value, track_vars_array TSRMLS_CC);
00746                      nsapi_free(value);
00747               }
00748               php_register_variable("PATH_INFO", rc->path_info, track_vars_array TSRMLS_CC);
00749        }
00750 
00751        /* Create full Request-URI & Script-Name */
00752        if (SG(request_info).request_uri) {
00753               pos = strlen(SG(request_info).request_uri);
00754               
00755               if (SG(request_info).query_string) {
00756                      spprintf(&value, 0, "%s?%s", SG(request_info).request_uri, SG(request_info).query_string);
00757                      if (value) {
00758                             php_register_variable("REQUEST_URI", value, track_vars_array TSRMLS_CC);
00759                             efree(value);
00760                      }
00761               } else {
00762                      php_register_variable_safe("REQUEST_URI", SG(request_info).request_uri, pos, track_vars_array TSRMLS_CC);
00763               }
00764 
00765               if (rc->path_info) {
00766                      pos -= strlen(rc->path_info);
00767                      if (pos<0) {
00768                             pos = 0;
00769                      }
00770               }
00771               php_register_variable_safe("SCRIPT_NAME", SG(request_info).request_uri, pos, track_vars_array TSRMLS_CC);
00772        }
00773        php_register_variable("SCRIPT_FILENAME", SG(request_info).path_translated, track_vars_array TSRMLS_CC);
00774 
00775        /* special variables in error mode */
00776        if (rc->http_error) {
00777               slprintf(buf, sizeof(buf), "%d", rc->http_error);
00778               php_register_variable("ERROR_TYPE", buf, track_vars_array TSRMLS_CC);
00779        }
00780 }
00781 
00782 static void nsapi_log_message(char *message)
00783 {
00784        TSRMLS_FETCH();
00785        nsapi_request_context *rc = (nsapi_request_context *)SG(server_context);
00786 
00787        if (rc) {
00788               log_error(LOG_INFORM, pblock_findval("fn", rc->pb), rc->sn, rc->rq, "%s", message);
00789        } else {
00790               log_error(LOG_INFORM, "php5", NULL, NULL, "%s", message);
00791        }
00792 }
00793 
00794 static time_t sapi_nsapi_get_request_time(TSRMLS_D)
00795 {
00796        return REQ_TIME( ((nsapi_request_context *)SG(server_context))->rq );
00797 }
00798 
00799 static int php_nsapi_startup(sapi_module_struct *sapi_module)
00800 {
00801        if (php_module_startup(sapi_module, &nsapi_module_entry, 1)==FAILURE) {
00802               return FAILURE;
00803        }
00804        return SUCCESS;
00805 }
00806 
00807 static struct stat* sapi_nsapi_get_stat(TSRMLS_D)
00808 {
00809        return request_stat_path(
00810               SG(request_info).path_translated,
00811               ((nsapi_request_context *)SG(server_context))->rq
00812        );
00813 }
00814 
00815 static sapi_module_struct nsapi_sapi_module = {
00816        "nsapi",                                /* name */
00817        "NSAPI",                                /* pretty name */
00818 
00819        php_nsapi_startup,                      /* startup */
00820        php_module_shutdown_wrapper,            /* shutdown */
00821 
00822        NULL,                                   /* activate */
00823        NULL,                                   /* deactivate */
00824 
00825        sapi_nsapi_ub_write,                    /* unbuffered write */
00826        sapi_nsapi_flush,                       /* flush */
00827        sapi_nsapi_get_stat,                    /* get uid/stat */
00828        NULL,                                   /* getenv */
00829 
00830        php_error,                              /* error handler */
00831 
00832        sapi_nsapi_header_handler,              /* header handler */
00833        sapi_nsapi_send_headers,                /* send headers handler */
00834        NULL,                                   /* send header handler */
00835 
00836        sapi_nsapi_read_post,                   /* read POST data */
00837        sapi_nsapi_read_cookies,                /* read Cookies */
00838 
00839        sapi_nsapi_register_server_variables,   /* register server variables */
00840        nsapi_log_message,                      /* Log message */
00841        sapi_nsapi_get_request_time,                     /* Get request time */
00842        NULL,                                                          /* Child terminate */
00843 
00844        NULL,                                   /* Block interruptions */
00845        NULL,                                   /* Unblock interruptions */
00846 
00847        STANDARD_SAPI_MODULE_PROPERTIES
00848 };
00849 
00850 static void nsapi_php_ini_entries(NSLS_D TSRMLS_DC)
00851 {
00852        struct pb_entry *entry;
00853        register int i,j,ok;
00854 
00855        for (i=0; i < NSG(pb)->hsize; i++) {
00856               entry=NSG(pb)->ht[i];
00857               while (entry) {
00858                      /* exclude standard entries given to "Service" which should not go into ini entries */
00859                      ok=1;
00860                      for (j=0; nsapi_exclude_from_ini_entries[j]; j++) {
00861                             ok&=(strcasecmp(entry->param->name, nsapi_exclude_from_ini_entries[j])!=0);
00862                      }
00863 
00864                      if (ok) {
00865                             /* change the ini entry */
00866                             if (zend_alter_ini_entry(entry->param->name, strlen(entry->param->name)+1,
00867                              entry->param->value, strlen(entry->param->value),
00868                              PHP_INI_SYSTEM, PHP_INI_STAGE_ACTIVATE)==FAILURE) {
00869                                    log_error(LOG_WARN, pblock_findval("fn", NSG(pb)), NSG(sn), NSG(rq), "Cannot change php.ini key \"%s\" to \"%s\"", entry->param->name, entry->param->value);
00870                             }
00871                      }
00872                      entry=entry->next;
00873               }
00874        }
00875 }
00876 
00877 void NSAPI_PUBLIC php5_close(void *vparam)
00878 {
00879        if (nsapi_sapi_module.shutdown) {
00880               nsapi_sapi_module.shutdown(&nsapi_sapi_module);
00881        }
00882 
00883        if (nsapi_sapi_module.php_ini_path_override) {
00884               free(nsapi_sapi_module.php_ini_path_override);
00885        }
00886        
00887 #ifdef PHP_WIN32
00888        if (nsapi_dll) {
00889               free(nsapi_dll);
00890               nsapi_dll = NULL;
00891        }
00892 #endif 
00893 
00894        sapi_shutdown();
00895        tsrm_shutdown();
00896 
00897        log_error(LOG_INFORM, "php5_close", NULL, NULL, "Shutdown PHP Module");
00898 }
00899 
00900 /*********************************************************
00901 / init SAF
00902 /
00903 / Init fn="php5_init" [php_ini="/path/to/php.ini"] [server_lib="ns-httpdXX.dll"]
00904 /   Initialize the NSAPI module in magnus.conf
00905 /
00906 / php_ini: gives path to php.ini file
00907 / server_lib: (only Win32) gives name of DLL (without path) to look for
00908 /  servact_* functions
00909 /
00910 /*********************************************************/
00911 int NSAPI_PUBLIC php5_init(pblock *pb, Session *sn, Request *rq)
00912 {
00913        php_core_globals *core_globals;
00914        char *strval;
00915        int threads=128; /* default for server */
00916 
00917        /* fetch max threads from NSAPI and initialize TSRM with it */
00918        threads=conf_getglobals()->Vpool_maxthreads;
00919        if (threads<1) {
00920               threads=128; /* default for server */
00921        }
00922        tsrm_startup(threads, 1, 0, NULL);
00923 
00924        core_globals = ts_resource(core_globals_id);
00925 
00926        /* look if php_ini parameter is given to php5_init */
00927        if (strval = pblock_findval("php_ini", pb)) {
00928               nsapi_sapi_module.php_ini_path_override = strdup(strval);
00929        }
00930        
00931 #ifdef PHP_WIN32
00932        /* look if server_lib parameter is given to php5_init
00933         * (this disables the automatic search for the newest ns-httpdXX.dll) */
00934        if (strval = pblock_findval("server_lib", pb)) {
00935               nsapi_dll = strdup(strval);
00936        }
00937 #endif 
00938 
00939        /* start SAPI */
00940        sapi_startup(&nsapi_sapi_module);
00941        nsapi_sapi_module.startup(&nsapi_sapi_module);
00942 
00943        daemon_atrestart(&php5_close, NULL);
00944 
00945        log_error(LOG_INFORM, pblock_findval("fn", pb), sn, rq, "Initialized PHP Module (%d threads expected)", threads);
00946        return REQ_PROCEED;
00947 }
00948 
00949 /*********************************************************
00950 / normal use in Service directive:
00951 /
00952 / Service fn="php5_execute" type=... method=... [inikey=inivalue inikey=inivalue...]
00953 /
00954 / use in Service for a directory to supply a php-made directory listing instead of server default:
00955 /
00956 / Service fn="php5_execute" type="magnus-internal/directory" script="/path/to/script.php" [inikey=inivalue inikey=inivalue...]
00957 /
00958 / use in Error SAF to display php script as error page:
00959 /
00960 / Error fn="php5_execute" code=XXX script="/path/to/script.php" [inikey=inivalue inikey=inivalue...]
00961 / Error fn="php5_execute" reason="Reason" script="/path/to/script.php" [inikey=inivalue inikey=inivalue...]
00962 /
00963 /*********************************************************/
00964 int NSAPI_PUBLIC php5_execute(pblock *pb, Session *sn, Request *rq)
00965 {
00966        int retval;
00967        nsapi_request_context *request_context;
00968        zend_file_handle file_handle = {0};
00969        struct stat *fst;
00970 
00971        char *path_info;
00972        char *query_string    = pblock_findval("query", rq->reqpb);
00973        char *uri             = pblock_findval("uri", rq->reqpb);
00974        char *request_method  = pblock_findval("method", rq->reqpb);
00975        char *content_type    = pblock_findval("content-type", rq->headers);
00976        char *content_length  = pblock_findval("content-length", rq->headers);
00977        char *directive       = pblock_findval("Directive", pb);
00978        int error_directive   = (directive && !strcasecmp(directive, "error"));
00979        int fixed_script      = 1;
00980 
00981        /* try to use script parameter -> Error or Service for directory listing */
00982        char *path_translated = pblock_findval("script", pb);
00983 
00984        TSRMLS_FETCH();
00985 
00986        /* if script parameter is missing: normal use as Service SAF  */
00987        if (!path_translated) {
00988               path_translated = pblock_findval("path", rq->vars);
00989               path_info       = pblock_findval("path-info", rq->vars);
00990               fixed_script = 0;
00991               if (error_directive) {
00992                      /* go to next error directive if script parameter is missing */
00993                      log_error(LOG_WARN, pblock_findval("fn", pb), sn, rq, "Missing 'script' parameter");
00994                      return REQ_NOACTION;
00995               }
00996        } else {
00997               /* in error the path_info is the uri to the requested page */
00998               path_info = pblock_findval("uri", rq->reqpb);
00999        }
01000 
01001        /* check if this uri was included in an other PHP script with nsapi_virtual()
01002           by looking for a request context in the current thread */
01003        if (SG(server_context)) {
01004               /* send 500 internal server error */
01005               log_error(LOG_WARN, pblock_findval("fn", pb), sn, rq, "Cannot make nesting PHP requests with nsapi_virtual()");
01006               if (error_directive) {
01007                      return REQ_NOACTION;
01008               } else {
01009                      protocol_status(sn, rq, 500, NULL);
01010                      return REQ_ABORTED;
01011               }
01012        }
01013 
01014        request_context = (nsapi_request_context *)pool_malloc(sn->pool, sizeof(nsapi_request_context));
01015        if (!request_context) {
01016               log_error(LOG_CATASTROPHE, pblock_findval("fn", pb), sn, rq, "Insufficient memory to process PHP request!");
01017               return REQ_ABORTED;
01018        }
01019        request_context->pb = pb;
01020        request_context->sn = sn;
01021        request_context->rq = rq;
01022        request_context->read_post_bytes = 0;
01023        request_context->fixed_script = fixed_script;
01024        request_context->http_error = (error_directive) ? rq->status_num : 0;
01025        request_context->path_info = path_info;
01026 
01027        SG(server_context) = request_context;
01028        SG(request_info).query_string = query_string;
01029        SG(request_info).request_uri = uri;
01030        SG(request_info).request_method = request_method;
01031        SG(request_info).path_translated = path_translated;
01032        SG(request_info).content_type = content_type;
01033        SG(request_info).content_length = (content_length == NULL) ? 0 : strtoul(content_length, 0, 0);
01034        SG(sapi_headers).http_response_code = (error_directive) ? rq->status_num : 200;
01035        
01036        nsapi_php_ini_entries(NSLS_C TSRMLS_CC);
01037 
01038        if (!PG(safe_mode)) php_handle_auth_data(pblock_findval("authorization", rq->headers) TSRMLS_CC);
01039 
01040        file_handle.type = ZEND_HANDLE_FILENAME;
01041        file_handle.filename = SG(request_info).path_translated;
01042        file_handle.free_filename = 0;
01043        file_handle.opened_path = NULL;
01044 
01045        fst = request_stat_path(SG(request_info).path_translated, rq);
01046        if (fst && S_ISREG(fst->st_mode)) {
01047               if (php_request_startup(TSRMLS_C) == SUCCESS) {
01048                      php_execute_script(&file_handle TSRMLS_CC);
01049                      php_request_shutdown(NULL);
01050                      retval=REQ_PROCEED;
01051               } else {
01052                      /* send 500 internal server error */
01053                      log_error(LOG_WARN, pblock_findval("fn", pb), sn, rq, "Cannot prepare PHP engine!");
01054                      if (error_directive) {
01055                             retval=REQ_NOACTION;
01056                      } else {
01057                             protocol_status(sn, rq, 500, NULL);
01058                             retval=REQ_ABORTED;
01059                      }
01060               }
01061        } else {
01062               /* send 404 because file not found */
01063               log_error(LOG_WARN, pblock_findval("fn", pb), sn, rq, "Cannot execute PHP script: %s (File not found)", SG(request_info).path_translated);
01064               if (error_directive) {
01065                      retval=REQ_NOACTION;
01066               } else {
01067                      protocol_status(sn, rq, 404, NULL);
01068                      retval=REQ_ABORTED;
01069               }
01070        }
01071 
01072        pool_free(sn->pool, request_context);
01073        SG(server_context) = NULL;
01074 
01075        return retval;
01076 }
01077 
01078 /*********************************************************
01079 / authentication
01080 /
01081 / we have to make a 'fake' authenticator for netscape so it
01082 / will pass authentication through to php, and allow us to
01083 / check authentication with our scripts.
01084 /
01085 / php5_auth_trans
01086 /   main function called from netscape server to authenticate
01087 /   a line in obj.conf:
01088 /             funcs=php5_auth_trans shlib="path/to/this/phpnsapi.dll"
01089 /      and:
01090 /             <Object ppath="path/to/be/authenticated/by/php/*">
01091 /             AuthTrans fn="php5_auth_trans"
01092 /*********************************************************/
01093 int NSAPI_PUBLIC php5_auth_trans(pblock * pb, Session * sn, Request * rq)
01094 {
01095        /* This is a DO NOTHING function that allows authentication
01096         * information
01097         * to be passed through to PHP scripts.
01098         */
01099        return REQ_PROCEED;
01100 }
01101 
01102 /*
01103  * Local variables:
01104  * tab-width: 4
01105  * c-basic-offset: 4
01106  * End:
01107  * vim600: sw=4 ts=4 fdm=marker
01108  * vim<600: sw=4 ts=4
01109  */