Annotation of embedaddon/php/sapi/isapi/php5isapi.c, revision 1.1
1.1 ! misho 1: /*
! 2: +----------------------------------------------------------------------+
! 3: | PHP Version 5 |
! 4: +----------------------------------------------------------------------+
! 5: | Copyright (c) 1997-2012 The PHP Group |
! 6: +----------------------------------------------------------------------+
! 7: | This source file is subject to version 3.01 of the PHP license, |
! 8: | that is bundled with this package in the file LICENSE, and is |
! 9: | available through the world-wide-web at the following url: |
! 10: | http://www.php.net/license/3_01.txt |
! 11: | If you did not receive a copy of the PHP license and are unable to |
! 12: | obtain it through the world-wide-web, please send a note to |
! 13: | license@php.net so we can mail you a copy immediately. |
! 14: +----------------------------------------------------------------------+
! 15: | Authors: Zeev Suraski <zeev@zend.com> |
! 16: | Ben Mansell <ben@zeus.com> (Zeus Support) |
! 17: +----------------------------------------------------------------------+
! 18: */
! 19: /* $Id: php5isapi.c 321634 2012-01-01 13:15:04Z felipe $ */
! 20:
! 21: #include "php.h"
! 22: #include <httpext.h>
! 23: #include <httpfilt.h>
! 24: #include <httpext.h>
! 25: #include "php_main.h"
! 26: #include "SAPI.h"
! 27: #include "php_globals.h"
! 28: #include "ext/standard/info.h"
! 29: #include "php_variables.h"
! 30: #include "php_ini.h"
! 31:
! 32: #ifdef PHP_WIN32
! 33: # include <process.h>
! 34: #else
! 35: # define __try
! 36: # define __except(val)
! 37: # define __declspec(foo)
! 38: #endif
! 39:
! 40:
! 41: #ifdef WITH_ZEUS
! 42: # include "httpext.h"
! 43: # include <errno.h>
! 44: # define GetLastError() errno
! 45: #endif
! 46:
! 47: #ifdef PHP_WIN32
! 48: #define PHP_ENABLE_SEH
! 49: #endif
! 50:
! 51: /*
! 52: uncomment the following lines to turn off
! 53: exception trapping when running under a debugger
! 54:
! 55: #ifdef _DEBUG
! 56: #undef PHP_ENABLE_SEH
! 57: #endif
! 58: */
! 59:
! 60: #define MAX_STATUS_LENGTH sizeof("xxxx LONGEST POSSIBLE STATUS DESCRIPTION")
! 61: #define ISAPI_SERVER_VAR_BUF_SIZE 1024
! 62: #define ISAPI_POST_DATA_BUF 1024
! 63:
! 64: static zend_bool bFilterLoaded=0;
! 65: static zend_bool bTerminateThreadsOnError=0;
! 66:
! 67: static char *isapi_special_server_variable_names[] = {
! 68: "ALL_HTTP",
! 69: "HTTPS",
! 70: #ifndef WITH_ZEUS
! 71: "SCRIPT_NAME",
! 72: #endif
! 73: NULL
! 74: };
! 75:
! 76: #define NUM_SPECIAL_VARS (sizeof(isapi_special_server_variable_names)/sizeof(char *))
! 77: #define SPECIAL_VAR_ALL_HTTP 0
! 78: #define SPECIAL_VAR_HTTPS 1
! 79: #define SPECIAL_VAR_PHP_SELF 2
! 80:
! 81: static char *isapi_server_variable_names[] = {
! 82: "AUTH_PASSWORD",
! 83: "AUTH_TYPE",
! 84: "AUTH_USER",
! 85: "CONTENT_LENGTH",
! 86: "CONTENT_TYPE",
! 87: "PATH_TRANSLATED",
! 88: "QUERY_STRING",
! 89: "REMOTE_ADDR",
! 90: "REMOTE_HOST",
! 91: "REMOTE_USER",
! 92: "REQUEST_METHOD",
! 93: "SERVER_NAME",
! 94: "SERVER_PORT",
! 95: "SERVER_PROTOCOL",
! 96: "SERVER_SOFTWARE",
! 97: #ifndef WITH_ZEUS
! 98: "APPL_MD_PATH",
! 99: "APPL_PHYSICAL_PATH",
! 100: "INSTANCE_ID",
! 101: "INSTANCE_META_PATH",
! 102: "LOGON_USER",
! 103: "REQUEST_URI",
! 104: "URL",
! 105: #else
! 106: "DOCUMENT_ROOT",
! 107: #endif
! 108: NULL
! 109: };
! 110:
! 111:
! 112: static char *isapi_secure_server_variable_names[] = {
! 113: "CERT_COOKIE",
! 114: "CERT_FLAGS",
! 115: "CERT_ISSUER",
! 116: "CERT_KEYSIZE",
! 117: "CERT_SECRETKEYSIZE",
! 118: "CERT_SERIALNUMBER",
! 119: "CERT_SERVER_ISSUER",
! 120: "CERT_SERVER_SUBJECT",
! 121: "CERT_SUBJECT",
! 122: "HTTPS_KEYSIZE",
! 123: "HTTPS_SECRETKEYSIZE",
! 124: "HTTPS_SERVER_ISSUER",
! 125: "HTTPS_SERVER_SUBJECT",
! 126: "SERVER_PORT_SECURE",
! 127: #ifdef WITH_ZEUS
! 128: "SSL_CLIENT_CN",
! 129: "SSL_CLIENT_EMAIL",
! 130: "SSL_CLIENT_OU",
! 131: "SSL_CLIENT_O",
! 132: "SSL_CLIENT_L",
! 133: "SSL_CLIENT_ST",
! 134: "SSL_CLIENT_C",
! 135: "SSL_CLIENT_I_CN",
! 136: "SSL_CLIENT_I_EMAIL",
! 137: "SSL_CLIENT_I_OU",
! 138: "SSL_CLIENT_I_O",
! 139: "SSL_CLIENT_I_L",
! 140: "SSL_CLIENT_I_ST",
! 141: "SSL_CLIENT_I_C",
! 142: #endif
! 143: NULL
! 144: };
! 145:
! 146:
! 147: static void php_info_isapi(ZEND_MODULE_INFO_FUNC_ARGS)
! 148: {
! 149: char **p;
! 150: char variable_buf[ISAPI_SERVER_VAR_BUF_SIZE];
! 151: DWORD variable_len;
! 152: char **all_variables[] = {
! 153: isapi_server_variable_names,
! 154: isapi_special_server_variable_names,
! 155: isapi_secure_server_variable_names,
! 156: NULL
! 157: };
! 158: char ***server_variable_names;
! 159: LPEXTENSION_CONTROL_BLOCK lpECB;
! 160:
! 161: lpECB = (LPEXTENSION_CONTROL_BLOCK) SG(server_context);
! 162:
! 163: php_info_print_table_start();
! 164: php_info_print_table_header(2, "Server Variable", "Value");
! 165: server_variable_names = all_variables;
! 166: while (*server_variable_names) {
! 167: p = *server_variable_names;
! 168: while (*p) {
! 169: variable_len = ISAPI_SERVER_VAR_BUF_SIZE;
! 170: if (lpECB->GetServerVariable(lpECB->ConnID, *p, variable_buf, &variable_len)
! 171: && variable_buf[0]) {
! 172: php_info_print_table_row(2, *p, variable_buf);
! 173: } else if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
! 174: char *tmp_variable_buf;
! 175:
! 176: tmp_variable_buf = (char *) emalloc(variable_len);
! 177: if (lpECB->GetServerVariable(lpECB->ConnID, *p, tmp_variable_buf, &variable_len)
! 178: && variable_buf[0]) {
! 179: php_info_print_table_row(2, *p, tmp_variable_buf);
! 180: }
! 181: efree(tmp_variable_buf);
! 182: }
! 183: p++;
! 184: }
! 185: server_variable_names++;
! 186: }
! 187: php_info_print_table_end();
! 188: }
! 189:
! 190:
! 191: static zend_module_entry php_isapi_module = {
! 192: STANDARD_MODULE_HEADER,
! 193: "ISAPI",
! 194: NULL,
! 195: NULL,
! 196: NULL,
! 197: NULL,
! 198: NULL,
! 199: php_info_isapi,
! 200: NULL,
! 201: STANDARD_MODULE_PROPERTIES
! 202: };
! 203:
! 204:
! 205: static int sapi_isapi_ub_write(const char *str, uint str_length TSRMLS_DC)
! 206: {
! 207: DWORD num_bytes = str_length;
! 208: LPEXTENSION_CONTROL_BLOCK ecb;
! 209:
! 210: ecb = (LPEXTENSION_CONTROL_BLOCK) SG(server_context);
! 211: if (ecb->WriteClient(ecb->ConnID, (char *) str, &num_bytes, HSE_IO_SYNC) == FALSE) {
! 212: php_handle_aborted_connection();
! 213: }
! 214: return num_bytes;
! 215: }
! 216:
! 217:
! 218: static int sapi_isapi_header_handler(sapi_header_struct *sapi_header, sapi_header_op_enum op, sapi_headers_struct *sapi_headers TSRMLS_DC)
! 219: {
! 220: return SAPI_HEADER_ADD;
! 221: }
! 222:
! 223:
! 224:
! 225: static void accumulate_header_length(sapi_header_struct *sapi_header, uint *total_length TSRMLS_DC)
! 226: {
! 227: *total_length += sapi_header->header_len+2;
! 228: }
! 229:
! 230:
! 231: static void concat_header(sapi_header_struct *sapi_header, char **combined_headers_ptr TSRMLS_DC)
! 232: {
! 233: memcpy(*combined_headers_ptr, sapi_header->header, sapi_header->header_len);
! 234: *combined_headers_ptr += sapi_header->header_len;
! 235: **combined_headers_ptr = '\r';
! 236: (*combined_headers_ptr)++;
! 237: **combined_headers_ptr = '\n';
! 238: (*combined_headers_ptr)++;
! 239: }
! 240:
! 241:
! 242: static int sapi_isapi_send_headers(sapi_headers_struct *sapi_headers TSRMLS_DC)
! 243: {
! 244: uint total_length = 2; /* account for the trailing \r\n */
! 245: char *combined_headers, *combined_headers_ptr;
! 246: LPEXTENSION_CONTROL_BLOCK lpECB = (LPEXTENSION_CONTROL_BLOCK) SG(server_context);
! 247: HSE_SEND_HEADER_EX_INFO header_info;
! 248: sapi_header_struct default_content_type;
! 249: char *status_buf = NULL;
! 250:
! 251: /* Obtain headers length */
! 252: if (SG(sapi_headers).send_default_content_type) {
! 253: sapi_get_default_content_type_header(&default_content_type TSRMLS_CC);
! 254: accumulate_header_length(&default_content_type, (void *) &total_length TSRMLS_CC);
! 255: }
! 256: zend_llist_apply_with_argument(&SG(sapi_headers).headers, (llist_apply_with_arg_func_t) accumulate_header_length, (void *) &total_length TSRMLS_CC);
! 257:
! 258: /* Generate headers */
! 259: combined_headers = (char *) emalloc(total_length+1);
! 260: combined_headers_ptr = combined_headers;
! 261: if (SG(sapi_headers).send_default_content_type) {
! 262: concat_header(&default_content_type, (void *) &combined_headers_ptr TSRMLS_CC);
! 263: sapi_free_header(&default_content_type); /* we no longer need it */
! 264: }
! 265: zend_llist_apply_with_argument(&SG(sapi_headers).headers, (llist_apply_with_arg_func_t) concat_header, (void *) &combined_headers_ptr TSRMLS_CC);
! 266: *combined_headers_ptr++ = '\r';
! 267: *combined_headers_ptr++ = '\n';
! 268: *combined_headers_ptr = 0;
! 269:
! 270: switch (SG(sapi_headers).http_response_code) {
! 271: case 200:
! 272: header_info.pszStatus = "200 OK";
! 273: break;
! 274: case 302:
! 275: header_info.pszStatus = "302 Moved Temporarily";
! 276: break;
! 277: case 401:
! 278: header_info.pszStatus = "401 Authorization Required";
! 279: break;
! 280: default: {
! 281: const char *sline = SG(sapi_headers).http_status_line;
! 282: int sline_len;
! 283:
! 284: /* httpd requires that r->status_line is set to the first digit of
! 285: * the status-code: */
! 286: if (sline && ((sline_len = strlen(sline)) > 12) && strncmp(sline, "HTTP/1.", 7) == 0 && sline[8] == ' ') {
! 287: if ((sline_len - 9) > MAX_STATUS_LENGTH) {
! 288: status_buf = estrndup(sline + 9, MAX_STATUS_LENGTH);
! 289: } else {
! 290: status_buf = estrndup(sline + 9, sline_len - 9);
! 291: }
! 292: } else {
! 293: status_buf = emalloc(MAX_STATUS_LENGTH + 1);
! 294: snprintf(status_buf, MAX_STATUS_LENGTH, "%d Undescribed", SG(sapi_headers).http_response_code);
! 295: }
! 296: header_info.pszStatus = status_buf;
! 297: break;
! 298: }
! 299: }
! 300: header_info.cchStatus = strlen(header_info.pszStatus);
! 301: header_info.pszHeader = combined_headers;
! 302: header_info.cchHeader = total_length;
! 303: header_info.fKeepConn = FALSE;
! 304: lpECB->dwHttpStatusCode = SG(sapi_headers).http_response_code;
! 305:
! 306: lpECB->ServerSupportFunction(lpECB->ConnID, HSE_REQ_SEND_RESPONSE_HEADER_EX, &header_info, NULL, NULL);
! 307:
! 308: efree(combined_headers);
! 309: if (status_buf) {
! 310: efree(status_buf);
! 311: }
! 312: return SAPI_HEADER_SENT_SUCCESSFULLY;
! 313: }
! 314:
! 315:
! 316: static int php_isapi_startup(sapi_module_struct *sapi_module)
! 317: {
! 318: if (php_module_startup(sapi_module, &php_isapi_module, 1)==FAILURE) {
! 319: return FAILURE;
! 320: } else {
! 321: bTerminateThreadsOnError = (zend_bool) INI_INT("isapi.terminate_threads_on_error");
! 322: return SUCCESS;
! 323: }
! 324: }
! 325:
! 326:
! 327: static int sapi_isapi_read_post(char *buffer, uint count_bytes TSRMLS_DC)
! 328: {
! 329: LPEXTENSION_CONTROL_BLOCK lpECB = (LPEXTENSION_CONTROL_BLOCK) SG(server_context);
! 330: DWORD read_from_buf=0;
! 331: DWORD read_from_input=0;
! 332: DWORD total_read=0;
! 333:
! 334: if ((DWORD) SG(read_post_bytes) < lpECB->cbAvailable) {
! 335: read_from_buf = MIN(lpECB->cbAvailable-SG(read_post_bytes), count_bytes);
! 336: memcpy(buffer, lpECB->lpbData+SG(read_post_bytes), read_from_buf);
! 337: total_read += read_from_buf;
! 338: }
! 339: if (read_from_buf<count_bytes
! 340: && (SG(read_post_bytes)+read_from_buf) < lpECB->cbTotalBytes) {
! 341: DWORD cbRead=0, cbSize;
! 342:
! 343: read_from_input = MIN(count_bytes-read_from_buf, lpECB->cbTotalBytes-SG(read_post_bytes)-read_from_buf);
! 344: while (cbRead < read_from_input) {
! 345: cbSize = read_from_input - cbRead;
! 346: if (!lpECB->ReadClient(lpECB->ConnID, buffer+read_from_buf+cbRead, &cbSize) || cbSize==0) {
! 347: break;
! 348: }
! 349: cbRead += cbSize;
! 350: }
! 351: total_read += cbRead;
! 352: }
! 353: return total_read;
! 354: }
! 355:
! 356:
! 357: static char *sapi_isapi_read_cookies(TSRMLS_D)
! 358: {
! 359: LPEXTENSION_CONTROL_BLOCK lpECB = (LPEXTENSION_CONTROL_BLOCK) SG(server_context);
! 360: char variable_buf[ISAPI_SERVER_VAR_BUF_SIZE];
! 361: DWORD variable_len = ISAPI_SERVER_VAR_BUF_SIZE;
! 362:
! 363: if (lpECB->GetServerVariable(lpECB->ConnID, "HTTP_COOKIE", variable_buf, &variable_len)) {
! 364: return estrndup(variable_buf, variable_len);
! 365: } else if (GetLastError()==ERROR_INSUFFICIENT_BUFFER) {
! 366: char *tmp_variable_buf = (char *) emalloc(variable_len+1);
! 367:
! 368: if (lpECB->GetServerVariable(lpECB->ConnID, "HTTP_COOKIE", tmp_variable_buf, &variable_len)) {
! 369: tmp_variable_buf[variable_len] = 0;
! 370: return tmp_variable_buf;
! 371: } else {
! 372: efree(tmp_variable_buf);
! 373: }
! 374: }
! 375: return STR_EMPTY_ALLOC();
! 376: }
! 377:
! 378:
! 379: #ifdef WITH_ZEUS
! 380:
! 381: static void sapi_isapi_register_zeus_ssl_variables(LPEXTENSION_CONTROL_BLOCK lpECB, zval *track_vars_array TSRMLS_DC)
! 382: {
! 383: char static_variable_buf[ISAPI_SERVER_VAR_BUF_SIZE];
! 384: DWORD variable_len = ISAPI_SERVER_VAR_BUF_SIZE;
! 385: char static_cons_buf[ISAPI_SERVER_VAR_BUF_SIZE];
! 386: /*
! 387: * We need to construct the /C=.../ST=...
! 388: * DN's for SSL_CLIENT_DN and SSL_CLIENT_I_DN
! 389: */
! 390: strcpy( static_cons_buf, "/C=" );
! 391: if( lpECB->GetServerVariable( lpECB->ConnID, "SSL_CLIENT_C", static_variable_buf, &variable_len ) && static_variable_buf[0] ) {
! 392: strlcat( static_cons_buf, static_variable_buf, ISAPI_SERVER_VAR_BUF_SIZE);
! 393: }
! 394: strlcat( static_cons_buf, "/ST=", ISAPI_SERVER_VAR_BUF_SIZE);
! 395: variable_len = ISAPI_SERVER_VAR_BUF_SIZE;
! 396: if( lpECB->GetServerVariable( lpECB->ConnID, "SSL_CLIENT_ST", static_variable_buf, &variable_len ) && static_variable_buf[0] ) {
! 397: strlcat( static_cons_buf, static_variable_buf, ISAPI_SERVER_VAR_BUF_SIZE );
! 398: }
! 399: php_register_variable( "SSL_CLIENT_DN", static_cons_buf, track_vars_array TSRMLS_CC );
! 400:
! 401: strcpy( static_cons_buf, "/C=" );
! 402: variable_len = ISAPI_SERVER_VAR_BUF_SIZE;
! 403: if( lpECB->GetServerVariable( lpECB->ConnID, "SSL_CLIENT_I_C", static_variable_buf, &variable_len ) && static_variable_buf[0] ) {
! 404: strlcat( static_cons_buf, static_variable_buf, ISAPI_SERVER_VAR_BUF_SIZE );
! 405: }
! 406: strlcat( static_cons_buf, "/ST=", ISAPI_SERVER_VAR_BUF_SIZE);
! 407: variable_len = ISAPI_SERVER_VAR_BUF_SIZE;
! 408: if( lpECB->GetServerVariable( lpECB->ConnID, "SSL_CLIENT_I_ST", static_variable_buf, &variable_len ) && static_variable_buf[0] ) {
! 409: strlcat( static_cons_buf, static_variable_buf, ISAPI_SERVER_VAR_BUF_SIZE );
! 410: }
! 411: php_register_variable( "SSL_CLIENT_I_DN", static_cons_buf, track_vars_array TSRMLS_CC );
! 412: }
! 413:
! 414: static void sapi_isapi_register_zeus_variables(LPEXTENSION_CONTROL_BLOCK lpECB, zval *track_vars_array TSRMLS_DC)
! 415: {
! 416: char static_variable_buf[ISAPI_SERVER_VAR_BUF_SIZE];
! 417: DWORD variable_len = ISAPI_SERVER_VAR_BUF_SIZE;
! 418: DWORD scriptname_len = ISAPI_SERVER_VAR_BUF_SIZE;
! 419: DWORD pathinfo_len = 0;
! 420: char *strtok_buf = NULL;
! 421:
! 422: /* Get SCRIPT_NAME, we use this to work out which bit of the URL
! 423: * belongs in PHP's version of PATH_INFO
! 424: */
! 425: lpECB->GetServerVariable(lpECB->ConnID, "SCRIPT_NAME", static_variable_buf, &scriptname_len);
! 426:
! 427: /* Adjust Zeus' version of PATH_INFO, set PHP_SELF,
! 428: * and generate REQUEST_URI
! 429: */
! 430: if ( lpECB->GetServerVariable(lpECB->ConnID, "PATH_INFO", static_variable_buf, &variable_len) && static_variable_buf[0] ) {
! 431:
! 432: /* PHP_SELF is just PATH_INFO */
! 433: php_register_variable( "PHP_SELF", static_variable_buf, track_vars_array TSRMLS_CC );
! 434:
! 435: /* Chop off filename to get just the 'real' PATH_INFO' */
! 436: pathinfo_len = variable_len - scriptname_len;
! 437: php_register_variable( "PATH_INFO", static_variable_buf + scriptname_len - 1, track_vars_array TSRMLS_CC );
! 438: /* append query string to give url... extra byte for '?' */
! 439: if ( strlen(lpECB->lpszQueryString) + variable_len + 1 < ISAPI_SERVER_VAR_BUF_SIZE ) {
! 440: /* append query string only if it is present... */
! 441: if ( strlen(lpECB->lpszQueryString) ) {
! 442: static_variable_buf[ variable_len - 1 ] = '?';
! 443: strcpy( static_variable_buf + variable_len, lpECB->lpszQueryString );
! 444: }
! 445: php_register_variable( "URL", static_variable_buf, track_vars_array TSRMLS_CC );
! 446: php_register_variable( "REQUEST_URI", static_variable_buf, track_vars_array TSRMLS_CC );
! 447: }
! 448: }
! 449:
! 450: /* Get and adjust PATH_TRANSLATED to what PHP wants */
! 451: variable_len = ISAPI_SERVER_VAR_BUF_SIZE;
! 452: if ( lpECB->GetServerVariable(lpECB->ConnID, "PATH_TRANSLATED", static_variable_buf, &variable_len) && static_variable_buf[0] ) {
! 453: static_variable_buf[ variable_len - pathinfo_len - 1 ] = '\0';
! 454: php_register_variable( "PATH_TRANSLATED", static_variable_buf, track_vars_array TSRMLS_CC );
! 455: }
! 456:
! 457: /* Bring in the AUTHENTICATION stuff as needed */
! 458: variable_len = ISAPI_SERVER_VAR_BUF_SIZE;
! 459: if ( lpECB->GetServerVariable(lpECB->ConnID, "AUTH_USER", static_variable_buf, &variable_len) && static_variable_buf[0] ) {
! 460: php_register_variable( "PHP_AUTH_USER", static_variable_buf, track_vars_array TSRMLS_CC );
! 461: }
! 462: variable_len = ISAPI_SERVER_VAR_BUF_SIZE;
! 463: if ( lpECB->GetServerVariable(lpECB->ConnID, "AUTH_PASSWORD", static_variable_buf, &variable_len) && static_variable_buf[0] ) {
! 464: php_register_variable( "PHP_AUTH_PW", static_variable_buf, track_vars_array TSRMLS_CC );
! 465: }
! 466: variable_len = ISAPI_SERVER_VAR_BUF_SIZE;
! 467: if ( lpECB->GetServerVariable(lpECB->ConnID, "AUTH_TYPE", static_variable_buf, &variable_len) && static_variable_buf[0] ) {
! 468: php_register_variable( "AUTH_TYPE", static_variable_buf, track_vars_array TSRMLS_CC );
! 469: }
! 470:
! 471: /* And now, for the SSL variables (if applicable) */
! 472: variable_len = ISAPI_SERVER_VAR_BUF_SIZE;
! 473: if ( lpECB->GetServerVariable(lpECB->ConnID, "CERT_COOKIE", static_variable_buf, &variable_len) && static_variable_buf[0] ) {
! 474: sapi_isapi_register_zeus_ssl_variables( lpECB, track_vars_array TSRMLS_CC );
! 475: }
! 476: /* Copy some of the variables we need to meet Apache specs */
! 477: variable_len = ISAPI_SERVER_VAR_BUF_SIZE;
! 478: if ( lpECB->GetServerVariable(lpECB->ConnID, "SERVER_SOFTWARE", static_variable_buf, &variable_len) && static_variable_buf[0] ) {
! 479: php_register_variable( "SERVER_SIGNATURE", static_variable_buf, track_vars_array TSRMLS_CC );
! 480: }
! 481: }
! 482: #else
! 483:
! 484: static void sapi_isapi_register_iis_variables(LPEXTENSION_CONTROL_BLOCK lpECB, zval *track_vars_array TSRMLS_DC)
! 485: {
! 486: char static_variable_buf[ISAPI_SERVER_VAR_BUF_SIZE];
! 487: char path_info_buf[ISAPI_SERVER_VAR_BUF_SIZE];
! 488: DWORD variable_len = ISAPI_SERVER_VAR_BUF_SIZE;
! 489: DWORD scriptname_len = ISAPI_SERVER_VAR_BUF_SIZE;
! 490: DWORD pathinfo_len = 0;
! 491: HSE_URL_MAPEX_INFO humi;
! 492:
! 493: /* Get SCRIPT_NAME, we use this to work out which bit of the URL
! 494: * belongs in PHP's version of PATH_INFO. SCRIPT_NAME also becomes PHP_SELF.
! 495: */
! 496: lpECB->GetServerVariable(lpECB->ConnID, "SCRIPT_NAME", static_variable_buf, &scriptname_len);
! 497: php_register_variable("SCRIPT_FILENAME", SG(request_info).path_translated, track_vars_array TSRMLS_CC);
! 498:
! 499: /* Adjust IIS' version of PATH_INFO, set PHP_SELF,
! 500: * and generate REQUEST_URI
! 501: * Get and adjust PATH_TRANSLATED to what PHP wants
! 502: */
! 503: if ( lpECB->GetServerVariable(lpECB->ConnID, "PATH_INFO", static_variable_buf, &variable_len) && static_variable_buf[0] ) {
! 504:
! 505: /* Chop off filename to get just the 'real' PATH_INFO' */
! 506: php_register_variable( "ORIG_PATH_INFO", static_variable_buf, track_vars_array TSRMLS_CC );
! 507: pathinfo_len = variable_len - scriptname_len;
! 508: strncpy(path_info_buf, static_variable_buf + scriptname_len - 1, sizeof(path_info_buf)-1);
! 509: php_register_variable( "PATH_INFO", path_info_buf, track_vars_array TSRMLS_CC );
! 510: /* append query string to give url... extra byte for '?' */
! 511: if ( strlen(lpECB->lpszQueryString) + variable_len + 1 < ISAPI_SERVER_VAR_BUF_SIZE ) {
! 512: /* append query string only if it is present... */
! 513: if ( strlen(lpECB->lpszQueryString) ) {
! 514: static_variable_buf[ variable_len - 1 ] = '?';
! 515: strcpy( static_variable_buf + variable_len, lpECB->lpszQueryString );
! 516: }
! 517: php_register_variable( "URL", static_variable_buf, track_vars_array TSRMLS_CC );
! 518: php_register_variable( "REQUEST_URI", static_variable_buf, track_vars_array TSRMLS_CC );
! 519: }
! 520: variable_len = ISAPI_SERVER_VAR_BUF_SIZE;
! 521: if ( lpECB->GetServerVariable(lpECB->ConnID, "PATH_TRANSLATED", static_variable_buf, &variable_len) && static_variable_buf[0] ) {
! 522: php_register_variable( "ORIG_PATH_TRANSLATED", static_variable_buf, track_vars_array TSRMLS_CC );
! 523: }
! 524: if (lpECB->ServerSupportFunction(lpECB->ConnID, HSE_REQ_MAP_URL_TO_PATH_EX, path_info_buf, &pathinfo_len, (LPDWORD) &humi)) {
! 525: /* Remove trailing \ */
! 526: if (humi.lpszPath[variable_len-2] == '\\') {
! 527: humi.lpszPath[variable_len-2] = 0;
! 528: }
! 529: php_register_variable("PATH_TRANSLATED", humi.lpszPath, track_vars_array TSRMLS_CC);
! 530: }
! 531: }
! 532:
! 533: static_variable_buf[0] = '/';
! 534: static_variable_buf[1] = 0;
! 535: variable_len = 2;
! 536: if (lpECB->ServerSupportFunction(lpECB->ConnID, HSE_REQ_MAP_URL_TO_PATH_EX, static_variable_buf, &variable_len, (LPDWORD) &humi)) {
! 537: /* Remove trailing \ */
! 538: if (humi.lpszPath[variable_len-2] == '\\') {
! 539: humi.lpszPath[variable_len-2] = 0;
! 540: }
! 541: php_register_variable("DOCUMENT_ROOT", humi.lpszPath, track_vars_array TSRMLS_CC);
! 542: }
! 543:
! 544: if (!SG(request_info).auth_user || !SG(request_info).auth_password ||
! 545: !SG(request_info).auth_user[0] || !SG(request_info).auth_password[0]) {
! 546: variable_len = ISAPI_SERVER_VAR_BUF_SIZE;
! 547: if (lpECB->GetServerVariable(lpECB->ConnID, "HTTP_AUTHORIZATION", static_variable_buf, &variable_len)
! 548: && static_variable_buf[0]) {
! 549: php_handle_auth_data(static_variable_buf TSRMLS_CC);
! 550: }
! 551: }
! 552:
! 553: if (SG(request_info).auth_user) {
! 554: php_register_variable("PHP_AUTH_USER", SG(request_info).auth_user, track_vars_array TSRMLS_CC );
! 555: }
! 556: if (SG(request_info).auth_password) {
! 557: php_register_variable("PHP_AUTH_PW", SG(request_info).auth_password, track_vars_array TSRMLS_CC );
! 558: }
! 559: }
! 560: #endif
! 561:
! 562: static void sapi_isapi_register_server_variables2(char **server_variables, LPEXTENSION_CONTROL_BLOCK lpECB, zval *track_vars_array, char **recorded_values TSRMLS_DC)
! 563: {
! 564: char **p=server_variables;
! 565: DWORD variable_len;
! 566: char static_variable_buf[ISAPI_SERVER_VAR_BUF_SIZE];
! 567: char *variable_buf;
! 568:
! 569: while (*p) {
! 570: variable_len = ISAPI_SERVER_VAR_BUF_SIZE;
! 571: if (lpECB->GetServerVariable(lpECB->ConnID, *p, static_variable_buf, &variable_len)
! 572: && static_variable_buf[0]) {
! 573: php_register_variable(*p, static_variable_buf, track_vars_array TSRMLS_CC);
! 574: if (recorded_values) {
! 575: recorded_values[p-server_variables] = estrndup(static_variable_buf, variable_len);
! 576: }
! 577: } else if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
! 578: variable_buf = (char *) emalloc(variable_len+1);
! 579: if (lpECB->GetServerVariable(lpECB->ConnID, *p, variable_buf, &variable_len)
! 580: && variable_buf[0]) {
! 581: php_register_variable(*p, variable_buf, track_vars_array TSRMLS_CC);
! 582: }
! 583: if (recorded_values) {
! 584: recorded_values[p-server_variables] = variable_buf;
! 585: } else {
! 586: efree(variable_buf);
! 587: }
! 588: } else { /* for compatibility with Apache SAPIs */
! 589: php_register_variable(*p, "", track_vars_array TSRMLS_CC);
! 590: }
! 591: p++;
! 592: }
! 593: }
! 594:
! 595:
! 596: static void sapi_isapi_register_server_variables(zval *track_vars_array TSRMLS_DC)
! 597: {
! 598: DWORD variable_len = ISAPI_SERVER_VAR_BUF_SIZE;
! 599: char *variable;
! 600: char *strtok_buf = NULL;
! 601: char *isapi_special_server_variables[NUM_SPECIAL_VARS];
! 602: LPEXTENSION_CONTROL_BLOCK lpECB;
! 603:
! 604: lpECB = (LPEXTENSION_CONTROL_BLOCK) SG(server_context);
! 605:
! 606: /* Register the special ISAPI variables */
! 607: memset(isapi_special_server_variables, 0, sizeof(isapi_special_server_variables));
! 608: sapi_isapi_register_server_variables2(isapi_special_server_variable_names, lpECB, track_vars_array, isapi_special_server_variables TSRMLS_CC);
! 609: if (SG(request_info).cookie_data) {
! 610: php_register_variable("HTTP_COOKIE", SG(request_info).cookie_data, track_vars_array TSRMLS_CC);
! 611: }
! 612:
! 613: /* Register the standard ISAPI variables */
! 614: sapi_isapi_register_server_variables2(isapi_server_variable_names, lpECB, track_vars_array, NULL TSRMLS_CC);
! 615:
! 616: if (isapi_special_server_variables[SPECIAL_VAR_HTTPS]
! 617: && (atoi(isapi_special_server_variables[SPECIAL_VAR_HTTPS])
! 618: || !strcasecmp(isapi_special_server_variables[SPECIAL_VAR_HTTPS], "on"))
! 619: ) {
! 620: /* Register SSL ISAPI variables */
! 621: sapi_isapi_register_server_variables2(isapi_secure_server_variable_names, lpECB, track_vars_array, NULL TSRMLS_CC);
! 622: }
! 623:
! 624: if (isapi_special_server_variables[SPECIAL_VAR_HTTPS]) {
! 625: efree(isapi_special_server_variables[SPECIAL_VAR_HTTPS]);
! 626: }
! 627:
! 628:
! 629: #ifdef WITH_ZEUS
! 630: sapi_isapi_register_zeus_variables(lpECB, track_vars_array TSRMLS_CC);
! 631: #else
! 632: sapi_isapi_register_iis_variables(lpECB, track_vars_array TSRMLS_CC);
! 633: #endif
! 634:
! 635: /* PHP_SELF support */
! 636: if (isapi_special_server_variables[SPECIAL_VAR_PHP_SELF]) {
! 637: php_register_variable("PHP_SELF", isapi_special_server_variables[SPECIAL_VAR_PHP_SELF], track_vars_array TSRMLS_CC);
! 638: efree(isapi_special_server_variables[SPECIAL_VAR_PHP_SELF]);
! 639: }
! 640:
! 641: if (isapi_special_server_variables[SPECIAL_VAR_ALL_HTTP]) {
! 642: /* Register the internal bits of ALL_HTTP */
! 643: variable = php_strtok_r(isapi_special_server_variables[SPECIAL_VAR_ALL_HTTP], "\r\n", &strtok_buf);
! 644: while (variable) {
! 645: char *colon = strchr(variable, ':');
! 646:
! 647: if (colon) {
! 648: char *value = colon+1;
! 649:
! 650: while (*value==' ') {
! 651: value++;
! 652: }
! 653: *colon = 0;
! 654: php_register_variable(variable, value, track_vars_array TSRMLS_CC);
! 655: *colon = ':';
! 656: }
! 657: variable = php_strtok_r(NULL, "\r\n", &strtok_buf);
! 658: }
! 659: efree(isapi_special_server_variables[SPECIAL_VAR_ALL_HTTP]);
! 660: }
! 661: }
! 662:
! 663:
! 664: static sapi_module_struct isapi_sapi_module = {
! 665: "isapi", /* name */
! 666: "ISAPI", /* pretty name */
! 667:
! 668: php_isapi_startup, /* startup */
! 669: php_module_shutdown_wrapper, /* shutdown */
! 670:
! 671: NULL, /* activate */
! 672: NULL, /* deactivate */
! 673:
! 674: sapi_isapi_ub_write, /* unbuffered write */
! 675: NULL, /* flush */
! 676: NULL, /* get uid */
! 677: NULL, /* getenv */
! 678:
! 679: php_error, /* error handler */
! 680:
! 681: sapi_isapi_header_handler, /* header handler */
! 682: sapi_isapi_send_headers, /* send headers handler */
! 683: NULL, /* send header handler */
! 684:
! 685: sapi_isapi_read_post, /* read POST data */
! 686: sapi_isapi_read_cookies, /* read Cookies */
! 687:
! 688: sapi_isapi_register_server_variables, /* register server variables */
! 689: NULL, /* Log message */
! 690: NULL, /* Get request time */
! 691: NULL, /* Child terminate */
! 692:
! 693: STANDARD_SAPI_MODULE_PROPERTIES
! 694: };
! 695:
! 696:
! 697: BOOL WINAPI GetFilterVersion(PHTTP_FILTER_VERSION pFilterVersion)
! 698: {
! 699: bFilterLoaded = 1;
! 700: pFilterVersion->dwFilterVersion = HTTP_FILTER_REVISION;
! 701: strcpy(pFilterVersion->lpszFilterDesc, isapi_sapi_module.pretty_name);
! 702: pFilterVersion->dwFlags= (SF_NOTIFY_AUTHENTICATION | SF_NOTIFY_PREPROC_HEADERS);
! 703: return TRUE;
! 704: }
! 705:
! 706:
! 707: DWORD WINAPI HttpFilterProc(PHTTP_FILTER_CONTEXT pfc, DWORD notificationType, LPVOID pvNotification)
! 708: {
! 709: TSRMLS_FETCH();
! 710:
! 711: switch (notificationType) {
! 712: case SF_NOTIFY_PREPROC_HEADERS:
! 713: SG(request_info).auth_user = NULL;
! 714: SG(request_info).auth_password = NULL;
! 715: SG(request_info).auth_digest = NULL;
! 716: break;
! 717: case SF_NOTIFY_AUTHENTICATION: {
! 718: char *auth_user = ((HTTP_FILTER_AUTHENT *) pvNotification)->pszUser;
! 719: char *auth_password = ((HTTP_FILTER_AUTHENT *) pvNotification)->pszPassword;
! 720:
! 721: if (auth_user && auth_user[0]) {
! 722: SG(request_info).auth_user = estrdup(auth_user);
! 723: }
! 724: if (auth_password && auth_password[0]) {
! 725: SG(request_info).auth_password = estrdup(auth_password);
! 726: }
! 727: return SF_STATUS_REQ_HANDLED_NOTIFICATION;
! 728: }
! 729: break;
! 730: }
! 731: return SF_STATUS_REQ_NEXT_NOTIFICATION;
! 732: }
! 733:
! 734:
! 735: static void init_request_info(LPEXTENSION_CONTROL_BLOCK lpECB TSRMLS_DC)
! 736: {
! 737: DWORD variable_len = ISAPI_SERVER_VAR_BUF_SIZE;
! 738: char static_variable_buf[ISAPI_SERVER_VAR_BUF_SIZE];
! 739: #ifndef WITH_ZEUS
! 740: HSE_URL_MAPEX_INFO humi;
! 741: #endif
! 742:
! 743: SG(request_info).request_method = lpECB->lpszMethod;
! 744: SG(request_info).query_string = lpECB->lpszQueryString;
! 745: SG(request_info).request_uri = lpECB->lpszPathInfo;
! 746: SG(request_info).content_type = lpECB->lpszContentType;
! 747: SG(request_info).content_length = lpECB->cbTotalBytes;
! 748: SG(sapi_headers).http_response_code = 200; /* I think dwHttpStatusCode is invalid at this stage -RL */
! 749: if (!bFilterLoaded) { /* we don't have valid ISAPI Filter information */
! 750: SG(request_info).auth_user = SG(request_info).auth_password = SG(request_info).auth_digest = NULL;
! 751: }
! 752:
! 753: #ifdef WITH_ZEUS
! 754: /* PATH_TRANSLATED can contain extra PATH_INFO stuff after the
! 755: * file being loaded, so we must use SCRIPT_FILENAME instead
! 756: */
! 757: if(lpECB->GetServerVariable(lpECB->ConnID, "SCRIPT_FILENAME", static_variable_buf, &variable_len)) {
! 758: SG(request_info).path_translated = estrdup(static_variable_buf);
! 759: } else
! 760: #else
! 761: /* happily, IIS gives us SCRIPT_NAME which is correct (without PATH_INFO stuff)
! 762: so we can just map that to the physical path and we have our filename */
! 763:
! 764: lpECB->GetServerVariable(lpECB->ConnID, "SCRIPT_NAME", static_variable_buf, &variable_len);
! 765: if (lpECB->ServerSupportFunction(lpECB->ConnID, HSE_REQ_MAP_URL_TO_PATH_EX, static_variable_buf, &variable_len, (LPDWORD) &humi)) {
! 766: SG(request_info).path_translated = estrdup(humi.lpszPath);
! 767: } else
! 768: #endif
! 769: /* if mapping fails, default to what the server tells us */
! 770: SG(request_info).path_translated = estrdup(lpECB->lpszPathTranslated);
! 771:
! 772: /* some server configurations allow '..' to slip through in the
! 773: translated path. We'll just refuse to handle such a path. */
! 774: if (strstr(SG(request_info).path_translated,"..")) {
! 775: SG(sapi_headers).http_response_code = 404;
! 776: efree(SG(request_info).path_translated);
! 777: SG(request_info).path_translated = NULL;
! 778: }
! 779: }
! 780:
! 781:
! 782: static void php_isapi_report_exception(char *message, int message_len TSRMLS_DC)
! 783: {
! 784: if (!SG(headers_sent)) {
! 785: HSE_SEND_HEADER_EX_INFO header_info;
! 786: LPEXTENSION_CONTROL_BLOCK lpECB = (LPEXTENSION_CONTROL_BLOCK) SG(server_context);
! 787:
! 788: header_info.pszStatus = "500 Internal Server Error";
! 789: header_info.cchStatus = strlen(header_info.pszStatus);
! 790: header_info.pszHeader = "Content-Type: text/html\r\n\r\n";
! 791: header_info.cchHeader = strlen(header_info.pszHeader);
! 792:
! 793: lpECB->dwHttpStatusCode = 500;
! 794: lpECB->ServerSupportFunction(lpECB->ConnID, HSE_REQ_SEND_RESPONSE_HEADER_EX, &header_info, NULL, NULL);
! 795: SG(headers_sent)=1;
! 796: }
! 797: sapi_isapi_ub_write(message, message_len TSRMLS_CC);
! 798: }
! 799:
! 800:
! 801: BOOL WINAPI GetExtensionVersion(HSE_VERSION_INFO *pVer)
! 802: {
! 803: pVer->dwExtensionVersion = HSE_VERSION;
! 804: #ifdef WITH_ZEUS
! 805: strncpy( pVer->lpszExtensionDesc, isapi_sapi_module.name, HSE_MAX_EXT_DLL_NAME_LEN);
! 806: #else
! 807: lstrcpyn(pVer->lpszExtensionDesc, isapi_sapi_module.name, HSE_MAX_EXT_DLL_NAME_LEN);
! 808: #endif
! 809: return TRUE;
! 810: }
! 811:
! 812:
! 813: static void my_endthread()
! 814: {
! 815: #ifdef PHP_WIN32
! 816: if (bTerminateThreadsOnError) {
! 817: _endthread();
! 818: }
! 819: #endif
! 820: }
! 821:
! 822: #ifdef PHP_WIN32
! 823: /* ep is accessible only in the context of the __except expression,
! 824: * so we have to call this function to obtain it.
! 825: */
! 826: BOOL exceptionhandler(LPEXCEPTION_POINTERS *e, LPEXCEPTION_POINTERS ep)
! 827: {
! 828: *e=ep;
! 829: return TRUE;
! 830: }
! 831: #endif
! 832:
! 833: DWORD WINAPI HttpExtensionProc(LPEXTENSION_CONTROL_BLOCK lpECB)
! 834: {
! 835: zend_file_handle file_handle;
! 836: zend_bool stack_overflown=0;
! 837: int retval = FAILURE;
! 838: #ifdef PHP_ENABLE_SEH
! 839: LPEXCEPTION_POINTERS e;
! 840: #endif
! 841: TSRMLS_FETCH();
! 842:
! 843: zend_first_try {
! 844: #ifdef PHP_ENABLE_SEH
! 845: __try {
! 846: #endif
! 847: init_request_info(lpECB TSRMLS_CC);
! 848: SG(server_context) = lpECB;
! 849:
! 850: php_request_startup(TSRMLS_C);
! 851:
! 852: file_handle.filename = SG(request_info).path_translated;
! 853: file_handle.free_filename = 0;
! 854: file_handle.type = ZEND_HANDLE_FILENAME;
! 855: file_handle.opened_path = NULL;
! 856:
! 857: /* open the script here so we can 404 if it fails */
! 858: if (file_handle.filename)
! 859: retval = php_fopen_primary_script(&file_handle TSRMLS_CC);
! 860:
! 861: if (!file_handle.filename || retval == FAILURE) {
! 862: SG(sapi_headers).http_response_code = 404;
! 863: PUTS("No input file specified.\n");
! 864: } else {
! 865: php_execute_script(&file_handle TSRMLS_CC);
! 866: }
! 867:
! 868: if (SG(request_info).cookie_data) {
! 869: efree(SG(request_info).cookie_data);
! 870: }
! 871: if (SG(request_info).path_translated)
! 872: efree(SG(request_info).path_translated);
! 873: #ifdef PHP_ENABLE_SEH
! 874: } __except(exceptionhandler(&e, GetExceptionInformation())) {
! 875: char buf[1024];
! 876: if (_exception_code()==EXCEPTION_STACK_OVERFLOW) {
! 877: LPBYTE lpPage;
! 878: static SYSTEM_INFO si;
! 879: static MEMORY_BASIC_INFORMATION mi;
! 880: static DWORD dwOldProtect;
! 881:
! 882: GetSystemInfo(&si);
! 883:
! 884: /* Get page ESP is pointing to */
! 885: _asm mov lpPage, esp;
! 886:
! 887: /* Get stack allocation base */
! 888: VirtualQuery(lpPage, &mi, sizeof(mi));
! 889:
! 890: /* Go to the page below the current page */
! 891: lpPage = (LPBYTE) (mi.BaseAddress) - si.dwPageSize;
! 892:
! 893: /* Free pages below current page */
! 894: if (!VirtualFree(mi.AllocationBase, (LPBYTE)lpPage - (LPBYTE) mi.AllocationBase, MEM_DECOMMIT)) {
! 895: _endthread();
! 896: }
! 897:
! 898: /* Restore the guard page */
! 899: if (!VirtualProtect(lpPage, si.dwPageSize, PAGE_GUARD | PAGE_READWRITE, &dwOldProtect)) {
! 900: _endthread();
! 901: }
! 902:
! 903: CG(unclean_shutdown)=1;
! 904: _snprintf(buf, sizeof(buf)-1,"PHP has encountered a Stack overflow");
! 905: php_isapi_report_exception(buf, strlen(buf) TSRMLS_CC);
! 906: } else if (_exception_code()==EXCEPTION_ACCESS_VIOLATION) {
! 907: _snprintf(buf, sizeof(buf)-1,"PHP has encountered an Access Violation at %p", e->ExceptionRecord->ExceptionAddress);
! 908: php_isapi_report_exception(buf, strlen(buf) TSRMLS_CC);
! 909: my_endthread();
! 910: } else {
! 911: _snprintf(buf, sizeof(buf)-1,"PHP has encountered an Unhandled Exception Code %d at %p", e->ExceptionRecord->ExceptionCode , e->ExceptionRecord->ExceptionAddress);
! 912: php_isapi_report_exception(buf, strlen(buf) TSRMLS_CC);
! 913: my_endthread();
! 914: }
! 915: }
! 916: #endif
! 917: #ifdef PHP_ENABLE_SEH
! 918: __try {
! 919: php_request_shutdown(NULL);
! 920: } __except(EXCEPTION_EXECUTE_HANDLER) {
! 921: my_endthread();
! 922: }
! 923: #else
! 924: php_request_shutdown(NULL);
! 925: #endif
! 926: } zend_catch {
! 927: zend_try {
! 928: php_request_shutdown(NULL);
! 929: } zend_end_try();
! 930: return HSE_STATUS_ERROR;
! 931: } zend_end_try();
! 932:
! 933: return HSE_STATUS_SUCCESS;
! 934: }
! 935:
! 936:
! 937:
! 938: __declspec(dllexport) BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
! 939: {
! 940: switch (fdwReason) {
! 941: case DLL_PROCESS_ATTACH:
! 942: #ifdef WITH_ZEUS
! 943: tsrm_startup(128, 1, TSRM_ERROR_LEVEL_CORE, "TSRM.log");
! 944: #else
! 945: tsrm_startup(128, 1, TSRM_ERROR_LEVEL_CORE, "C:\\TSRM.log");
! 946: #endif
! 947: sapi_startup(&isapi_sapi_module);
! 948: if (isapi_sapi_module.startup) {
! 949: isapi_sapi_module.startup(&sapi_module);
! 950: }
! 951: break;
! 952: case DLL_THREAD_ATTACH:
! 953: break;
! 954: case DLL_THREAD_DETACH:
! 955: ts_free_thread();
! 956: break;
! 957: case DLL_PROCESS_DETACH:
! 958: if (isapi_sapi_module.shutdown) {
! 959: isapi_sapi_module.shutdown(&sapi_module);
! 960: }
! 961: sapi_shutdown();
! 962: tsrm_shutdown();
! 963: break;
! 964: }
! 965: return TRUE;
! 966: }
! 967:
! 968: /*
! 969: * Local variables:
! 970: * tab-width: 4
! 971: * c-basic-offset: 4
! 972: * End:
! 973: */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>