/*****************************************************************************/ /* PHPwasd.c PHP: Hypertext Preprocessor: WASD A large element of this code is "monkey-see-monkey-do", but it seems to work! This is *not* a port or implementation of PHP on VMS. It is a PHP 'interface', the part of PHP that talks to the Web server. WASD CGILIB.C library has not been used here because this is only an 'interface' to the actual script processor PHP, and only the CGI variable code would actually be employed, so this has been provided as a stand-alone (somewhat tailored) function. PHPWASD COPYRIGHT ----------------- Copyright (C) 2002-2014 Mark G.Daniel This package comes with ABSOLUTELY NO WARRANTY. This is free software, and you are welcome to redistribute it under the conditions of the GNU GENERAL PUBLIC LICENSE, version 3, or any later version. http://www.gnu.org/licenses/gpl.txt This code module along with sections of this code are based on code examples, actual code and of course the underlying PHP engine, which are copyright by The PHP Group. +----------------------------------------------------------------------+ | PHP Version 5 | +----------------------------------------------------------------------+ | Copyright (c) 1997-2010 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: various | +----------------------------------------------------------------------+ MAPPING RULES ------------- There are various ways to employ the WASD PHP interpreter. It can be used in vanilla CGI mode, or in persistent CGIplus/RTE mode. Benchmarking indicates the CGIplus/RTE use reduces activation time to 10% of CGI (yes, 10x). There are subtle differences in the way CGIplus and RTE parse and provide the PATH_INFO data. See the "WASD Scripting Overview" for more detail. The following rules require the PHP script files to be located in the site administrator controlled /cgi-bin/ path. This is of course the most secure. # HTTPD$MAP for RTE usage # this configuration probable works as well as any map /cgi-bin/*.php* /php-bin/*.php* exec+ /php-bin/* (cgi-bin:[000000]phpwasd.exe)/cgi-bin/* \ script=query=relaxed or # HTTPD$MAP for CGI, CGIplus or RTE usage (perhaps for comparison) exec+ /php-bin/* (cgi-bin:[000000]phpwasd.exe)/cgi-bin/* \ script=query=relaxed .. exec /cgi-bin/* /cgi-bin/* exec+ /cgiplus-bin/* /cgi-bin/* The following rules allow .PHP type files anywhere in the mapped directory structure to be executed. This means that any document author can script using PHP. This may be what is desired but can be dangerous. PHP provides for this type of usage. Please familiarise yourself with it's requirements and controls. As an additional safeguard it is suggested that PHP scripts be executed under a non-server account using the WASD PERSONA capabilities (see the Technical Overview if unfamiliar with this functionality and configuration). set /web/*.php* script=as=OTHER-ACCOUNT exec+ /web/*.php* /web/*.php* For scripts requiring extended file specification (and located on ODS-5 volumes) the script path needs to be mapped as ODS-5. # HTTPD$MAP for RTE usage for extended file specification # (a minimum of WASD 8.4.2 is required for this to work fully) exec+ /php-bin/* (cgi-bin:[000000]phpwasd.exe)/cgi-bin/* \ script=query=relaxed ods=5 The engine will by default chdir() to the a *nix syntax equivalent of the directory containing the PHP script file. It also setenv()s the environment variable PATH to this same string. This location may be explicitly provided using the value of CGI variable SCRIPT_DEFAULT and set on a per-script or general basis using the mapping rule 'script=default='. It will accept either VMS and *nix specifications depending on the requirements of the script itself. set /php-bin/mumble.php* script=default="/mumble_device/000000" set /php-bin/mumble.php* script=default="mumble_device:[000000]" OTHER CONFIGURATION ------------------- # HTTPD$CONFIG [ScriptRunTime] .PHP $CGI-BIN:[000000]PHPWASD.EXE [AddType] .INI text/plain initialization file .PHP text/plain PHP source .PHPS text/plain PHP source .PHTML text/plain PHP source WARNING! -------- Don't forget that when using persistant environments such as CGIplus/RTE, especially during development, once changes have been made to source code and the environment rebuilt any currently executing instances of the previous build must be purged from the server environment (wish I had a dollar for every time I'd been caught like this myself!) $ HTTPD/DO=DCL=PURGE WATCH MODE ---------- This is a crude mode to assist in debugging the interactions between the WASD server, PHPWASD scripting engine, script activation and input/output. It does not really allow the debugging of the script itself but may be of some elementary assistance when investigating the environment the script is activating under. It can be activated in a number of ways. o Defining the logical name PHPWASD$WATCH_SCRIPT_NAME to contain a (case-sensitive) string from the SCRIPT_NAME variable (e.g. "php_info").. o Defining the logical name PHPWASD$WATCH_REMOTE_ADDR to contain the IP address of the watching (debugging) browser host (e.g. "192.168.0.2") o Adding the string "#watch" to the first line of the PHP script (e.g. "CGI response conversion (munge) performed by SapiSendHeader() PHPWASD$NO_TRANSLATE when defined suppresses automatic ODS-5 VMS to *nix file-system syntax for PATH_TRANSLATED and SCRIPT_FILENAME PHPWASD$QUERIES allows the likes of "?wasd=info" (see above) PHPWASD$TYPE comma separated list of allowed PHP script types (e.g. ".PHP" or ".PHP,.PHTML") PHPWASD$WATCH_SCRIPT_NAME turns on all WATCH statements if this string is found in the SCRIPT_NAME path PHPWASD$WATCH_REMOTE_ADDR turns on all WATCH statements if this string matches the IP address found in REMOTE_ADDR variable PHPWASD$ABSORB_PRE_WRITESAPI see version log entry 02-JUN-2009 PHPWASDSHR sharable image containing PHP engine BUILD DETAILS ------------- $ @BUILD_PHPWASD BUILD !compile+link $ @BUILD_PHPWASD LINK !link-only VERSION HISTORY (update SOFTWAREVN as well!) --------------- 25-JAN-2014 MVB v1.4.5, add support for PHP V5.4 24-MAY-2011 MVB v1.4.4, add support for PHP V5.3 10-FEB-2011 MGD v1.4.3, extend PHPWASD$ABSORB_PRE_WRITESAPI to 17-JUL-2010 MGD v1.4.2, WASD v10.1 ProctorDetect() bugfix; initialise SG(request_info), SG(sapi_headers) bugfix; ImageIdent() for __ia64 bugfix; UsageLimit >= rather than > 02-JUN-2009 MGD v1.4.1, The CSWS PHP v2.0 kit (May 2009) session management extension outputs spurious debug(?) data which as an interim measure has been suppressed using the PHPWASD$ABSORB_PRE_WRITESAPI logical name 20-MAY-2009 MGD v1.4.0, CPQ AXPVMS CSWS_PHP V2.0 (based on PHP v5.2.6) PHP 5 sapi_module_struct 04-MAY-2008 MGD v1.3.5, bugfix; CSWS_PHP13_UPDATE-V0200 requires that sapi_module_struct 'log message' be populated (and I throw in some more with fingers crossed) 28-FEB-2007 MGD v1.3.4, bugfix; SapiSendHeader() need to transmogrify an NPH response into a CGI status response 01-DEC-2005 MGD v1.3.3, maintenance; build against CSWS PHP 1.3, primary PHP.INI via script parameter PHP_INI 18-AUG-2005 MGD v1.3.2, maintenance; build against CSWS PHP 1.2-1 source kit and ensure IA64 build and install OK 01-JUL-2005 MGD v1.3.1, bugfix; set error handler to php_error() 29-JAN-2005 MGD v1.3.0, SapiSendHeader() absorb NPH part of header to allow WASD v9.0.2 and later to make the most of HTTP/1.1, PHPWASD$WATCH_... logical names and script-embedded " #include #include #include #include #include /* VMS-specific header files */ #include #include #include #include #include #include #ifdef __ia64 #include #endif /* PHP-specific includes */ /* Originally based on a source kit CSWS_PHP-V0102-SRC.BCK dated 6-NOV-2003. Then against [BUILD.SOURCE_CODE.ALPHA-21]PHP-SOURCE.BCK;1 from the source kit APACHE-SRC-KIT.BCK dated 10-NOV-2005 10:04:06.95. Just enough of the PHP build environment to compile PHPWASD is used here. */ #include "zend.h" #include "zend_api.h" #include "zend_compile.h" #include "zend_extensions.h" #include "zend_highlight.h" #include "zend_ini.h" #include "zend_modules.h" #define PHP_FUNCTION ZEND_FUNCTION #define PHPAPI #define TLS_C #define TLS_CC typedef zval pval; #include "info.h" #include "sapi.h" /* required PHP prototypes */ #ifdef IS_CONSTANT_IN_NAMESPACE int php_output_write(const char *str, size_t str_length TSRMLS_DC); #define PUTS(str) do { \ const char *__str = (str); \ php_output_write(__str, strlen(__str) TSRMLS_CC); \ } while (0) #else int php_body_write(const char *str, uint str_length TSRMLS_DC); #define PUTS(str) do { \ const char *__str = (str); \ php_body_write(__str, strlen(__str) TSRMLS_CC); \ } while (0) #endif void php_dl (pval *file, int type, pval *return_value TSRMLS_DC); #define php_error zend_error void php_error(int type, const char *format, ...); int php_execute_script (zend_file_handle *primary_file TSRMLS_DC); void php_get_highlight_struct (zend_syntax_highlighter_ini *syntax_highlighter_ini); int php_handle_auth_data (const char *auth TSRMLS_DC); void php_handle_aborted_connection(void); int php_lint_script(zend_file_handle *file TSRMLS_DC); int php_module_startup (sapi_module_struct *sf, zend_module_entry *additional_modules, unsigned int num_additional_modules); int php_module_shutdown_wrapper(sapi_module_struct *sapi_globals); int php_printf(const char *format, ...); void php_register_variable (char *var, char *val, pval *track_vars_array TSRMLS_DC); int php_request_startup(TSRMLS_D); void php_request_shutdown(void *dummy); /**********/ /* macros */ /**********/ #ifndef BUILD_DATETIME # define BUILD_DATETIME "(undefined)" #endif #ifndef PHPWASD_CRTL_VER # define PHPWASD_CRTL_VER __CRTL_VER #endif #define FI_LI __FILE__, __LINE__ /* comma-separated list of file types allowed to be interpreted */ #define DEFAULT_PHP_TYPE_LIST ".PHP,.PHTML" /* includes the code for "!wasd=info", etc. */ #define ALLOW_WASD_QUERIES 1 /* includes the code to check for a ".PHP" file type, etc. */ #define CHECK_FILE_TYPE 1 /* size of buffer for PHP output */ #define WRITE_BUFFER_SIZE 4096 /* initial size and increment of response header buffer */ #define SEND_HEADER_BUFFER_CHUNK 1024 /* whether and are redirected to NL: */ #define STD_NL 1 #define STD_OUT -1 /******************/ /* global storage */ /******************/ int Debug, AbsorbPreWriteSapi, IsCgiPlus, IsOds5, NoNphMunge, Ods5Translate, WatchEnabled, ServerVersion, UsageCount, UsageLimit; char *CgiPlusEofPtr, *CgiPlusEotPtr, *CgiPlusEscPtr, *PhpIni2Ptr, *PhpIniStringPtr, *PhpTypeListPtr; char CgiPlusEof [64], CgiPlusEot [64], CgiPlusEsc [64], PhpTypeList [256], PhpWasdIni [256], PhpWasdIni2 [256], PhpWasdInis [256], SoftwareId [64]; /**************/ /* prototypes */ /**************/ void at_exit (); char* CgiVar (char*); static void InitRequestInfo (); unsigned int lib$get_symbol (void*, void*, unsigned short*, unsigned long*); int ConfiguredFileType (char*); char* ImageIdent(); int PhpIni2 (char*); int PhpIniDirective (char*); int PhpIniString (char*); int ProctorDetect (); int WatchThis (char*, ...); int WatchDump (char*, int); void ProcessRequest (); int PrePhpError (char*, ...); int SetCrtlFeature (char*, int); char* TrnLnm (char*, char*); int VmsVersion (); static int SapiActivate (SLS_D); static int SapiDeactivate (SLS_D); static void SapiFlush (void*); static char* SapiGetEnv (char*, uint SLS_DC); static void SapiLogMessage (char*); static char *SapiReadCookies (SLS_D); static int SapiReadPost (char*, uint SLS_DC); static void SapiRegisterVariables (zval* ELS_DC SLS_DC PLS_DC); static void SapiRegisterCgiVariables (zval* ELS_DC SLS_DC PLS_DC); static void SapiSendHeader (sapi_header_struct*, void*); static int SapiWrite (const char*, uint); /*****************************/ /* PHP SAPI module structure */ /*****************************/ static sapi_module_struct SapiModule = { "phpwasd", /* name */ SoftwareId, /* pretty name */ /* cast to void because of an annoying prototype mismatch */ (void*)php_module_startup, /* startup */ php_module_shutdown_wrapper, /* shutdown */ SapiActivate, /* activate */ SapiDeactivate, /* deactivate */ SapiWrite, /* unbuffered write */ SapiFlush, /* flush stdout */ NULL, /* get stat */ SapiGetEnv, /* getenv */ php_error, /* error handler */ 0, /* header handler */ 0, /* send header */ SapiSendHeader, /* send header handler */ SapiReadPost, /* read POSTed data */ SapiReadCookies, /* read cookies */ SapiRegisterVariables, /* register variables */ SapiLogMessage, /* log message */ NULL, /* get request time */ #ifdef IS_LEXICAL_REF 0, /* terminate process */ #endif NULL, /* php ini path override */ NULL, /* block interruptions */ NULL, /* unblock interruptions */ NULL, /* default post reader */ NULL, /* treat data */ 0, /* executable location */ 0, /* php ini ignore */ 0, /* get fd */ 0, /* force http 10 */ 0, /* get target uid */ 0, /* get target gid */ 0, /* input filter */ 0, /* ini defaults */ 0, /* phpinfo as text */ NULL, /* ini entries */ #ifdef IS_LEXICAL_REF 0, /* additional_functions */ 0, /* input_filter_init */ #endif STANDARD_SAPI_MODULE_PROPERTIES }; /*****************************************************************************/ /* */ main (int argc, char *argv[]) { char *cptr; /*********/ /* begin */ /*********/ sprintf (SoftwareId, "%s, %d, %s", SOFTWAREID, PHPWASD_CRTL_VER, BUILD_DATETIME); if (argc > 1 && !strcasecmp (argv[1], "/VERSION")) { fprintf (stdout, "%s\n%s\n", SoftwareId, ImageIdent()); exit (SS$_NORMAL); } if (Debug = WatchEnabled = (TrnLnm ("PHPWASD$DBUG", NULL) != NULL)) fprintf (stdout, "Content-Type: text/plain\n\n"); NoNphMunge = 0; if (TrnLnm ("PHPWASD$NO_NPH_MUNGE", NULL)) NoNphMunge = 1; AbsorbPreWriteSapi = 0; if (TrnLnm ("PHPWASD$ABSORB_PRE_WRITESAPI", NULL)) AbsorbPreWriteSapi = STD_NL; #if CHECK_FILE_TYPE if (!(PhpTypeListPtr = TrnLnm ("PHPWASD$TYPE", PhpTypeList))) PhpTypeListPtr = DEFAULT_PHP_TYPE_LIST; #endif /* CHECK_FILE_TYPE */ /* if it doesn't look like CGI environment then forget it */ if (TrnLnm ("HTTP$INPUT", NULL) == NULL) exit (SS$_ABORT); if (AbsorbPreWriteSapi) { /* binary mode to eliminate carriage-control */ if (!(stdin = freopen ("HTTP$INPUT:", "r", stdin, "ctx=bin"))) exit (vaxc$errno); /* bit-bucket anything to before first SapiWrite() */ if (!(stdout = freopen ("NL:", "w", stdout, "ctx=bin"))) exit (vaxc$errno); if (!(stderr = freopen ("NL:", "w", stderr, "ctx=bin"))) exit (vaxc$errno); } else if (!Debug) { /* binary mode to eliminate carriage-control */ if (!(stdin = freopen ("HTTP$INPUT:", "r", stdin, "ctx=bin"))) exit (vaxc$errno); if (!(stdout = freopen ("SYS$OUTPUT:", "w", stdout, "ctx=bin"))) exit (vaxc$errno); if (!(stderr = freopen ("SYS$OUTPUT:", "w", stderr, "ctx=bin"))) exit (vaxc$errno); } cptr = TrnLnm ("PHPWASD$LIMIT", NULL); if (cptr) UsageLimit = atoi(cptr); IsCgiPlus = ((CgiPlusEofPtr = TrnLnm ("CGIPLUSEOF", CgiPlusEof)) != NULL); CgiPlusEotPtr = TrnLnm ("CGIPLUSEOT", CgiPlusEot); CgiPlusEscPtr = TrnLnm ("CGIPLUSESC", CgiPlusEsc); sapi_startup (&SapiModule); cptr = TrnLnm ("PHPWASD$INI", PhpWasdIni); if (Debug) fprintf (stdout, "PHPWASD$INI |%s|\n", cptr); if (cptr) SapiModule.php_ini_path_override = strdup(cptr); if (php_module_startup (&SapiModule, NULL, 0) == FAILURE) exit (SS$_BUGCHECK); SG(server_context) = (void*)1; atexit (at_exit); cptr = PhpIni2Ptr = TrnLnm ("PHPWASD$INI2", PhpWasdIni2); if (Debug) fprintf (stdout, "PHPWASD$INI2 |%s|\n", cptr); cptr = PhpIniStringPtr = TrnLnm ("PHPWASD$INIS", PhpWasdInis); if (Debug) fprintf (stdout, "PHPWASD$INIS |%s|\n", cptr); if (IsCgiPlus) { for (;;) { if (Debug) fprintf (stdout, "Content-Type: text/plain\n\n"); /* bit-bucket anything to before first SapiWrite() */ if (AbsorbPreWriteSapi && AbsorbPreWriteSapi != STD_NL) { if (!(stdout = freopen ("NL:", "w", stdout, "ctx=bin"))) exit (vaxc$errno); if (!(stderr = freopen ("NL:", "w", stderr, "ctx=bin"))) exit (vaxc$errno); AbsorbPreWriteSapi = STD_NL; } /* block waiting for the first/next request */ CgiVar (""); UsageCount++; ProcessRequest (); /* reset watch mode as necessary */ if (WatchEnabled) WatchThis (NULL); if (AbsorbPreWriteSapi && AbsorbPreWriteSapi != STD_OUT) { /* stop bit-bucketing to allow CGIplus EOF sentinal */ if (!(stdout = freopen ("SYS$OUTPUT:", "w", stdout, "ctx=bin"))) exit (vaxc$errno); if (!(stderr = freopen ("SYS$ERROR:", "w", stderr, "ctx=bin"))) exit (vaxc$errno); AbsorbPreWriteSapi = STD_OUT; } fflush (stdout); fputs (CgiPlusEofPtr, stdout); fflush (stdout); if (UsageLimit && UsageCount >= UsageLimit) break; } } else { if (Debug) fprintf (stdout, "Content-Type: text/plain\n\n"); /* bit-bucket anything to before the first SapiWrite() */ if (AbsorbPreWriteSapi && AbsorbPreWriteSapi != STD_NL) { if (!(stdout = freopen ("NL:", "w", stdout, "ctx=bin"))) exit (vaxc$errno); if (!(stderr = freopen ("NL:", "w", stderr, "ctx=bin"))) exit (vaxc$errno); AbsorbPreWriteSapi = STD_NL; } ProcessRequest (); } exit (SS$_NORMAL); } /*****************************************************************************/ /* Ensure anything written by PHP is output to the server before exiting (e.g. any error or status messages in the case of an error exit). */ void at_exit () { /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "at_exit()\n"); SapiFlush (NULL); if (WatchEnabled) { WatchThis ("PHP INTERPRETER EXITING"); WatchThis (NULL); } } /*****************************************************************************/ /* Process a single CGI/CGIplus/RTE request. All request processing occurs within this function. */ void ProcessRequest () { int status, value, PHPinfo, PHPlint, PHPsyntax; char *cptr, *sptr, *zptr, *QueryStringPtr, *ScriptDefaultPtr, *ScriptFileNamePtr, *ScriptNamePtr; const char *auth; char Line [256], ScriptDefault [256]; FILE *fp; zend_file_handle ZendFileHandle; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "ProcessRequest() %d/%d\n", UsageCount, UsageLimit); if (!ServerVersion) { /* must be here because CGIplus variables don't exist before!! */ cptr = CgiVar ("SERVER_SIGNATURE"); if (cptr) { /* should be something like "HTTPd-WASD/8.2.1 OpenVMS/AXP SSL" */ while (*cptr && !isdigit(*cptr)) cptr++; ServerVersion = atoi(cptr) * 10000; while (*cptr && isdigit(*cptr)) cptr++; if (*cptr) cptr++; ServerVersion += atoi(cptr) * 100; while (*cptr && isdigit(*cptr)) cptr++; if (*cptr) cptr++; ServerVersion += atoi(cptr); /* resulting in a number like 80,201 */ if (ServerVersion < 80000) ServerVersion = 0; } } if (CgiVar ("PHP_NO_NPH_MUNGE")) NoNphMunge = 1; if (!(cptr = CgiVar ("PATH_ODS"))) cptr = ""; if (*cptr == '5') IsOds5 = 1; else IsOds5 = 0; if (IsOds5) { /* Automatically translate any VMS file-system specification in PATH_TRANSLATED and SCRIPT_FILENAME to *nix file-system syntax. (a little like WASD v9.0 script=syntax=unix for pre-v9.0) */ if (CgiVar ("PHP_NO_TRANSLATE")) Ods5Translate = 0; else if (TrnLnm ("PHPWASD$NO_TRANSLATE", NULL)) Ods5Translate = 0; else Ods5Translate = 1; } #if PHPWASD_CRTL_VER >= 70300000 if (VmsVersion() >= 731) { if (IsOds5) value = 1; else value = 0; SetCrtlFeature ("DECC$ARGV_PARSE_STYLE", value); SetCrtlFeature ("DECC$EFS_CHARSET", value); SetCrtlFeature ("DECC$EFS_CASE_PRESERVE", value); SetCrtlFeature ("DECC$EFS_CASE_SPECIAL", value); SetCrtlFeature ("DECC$ENABLE_GETENV_CACHE", value); SetCrtlFeature ("DECC$FILENAME_UNIX_REPORT", value); SetCrtlFeature ("DECC$POSIX_SEEK_STREAM_FILE", value); SetCrtlFeature ("DECC$FILE_SHARING", value); SetCrtlFeature ("DECC$STREAM_PIPE", value); SetCrtlFeature ("DECC$STDIO_CTX_EOL", value); SetCrtlFeature ("DECC$ACL_ACCESS_CHECK", value); SetCrtlFeature ("DECC$READDIR_DROPDOTNOTYPE", value); } #endif /* PHPWASD_CRTL_VER >= 70300000 */ cptr = CgiVar ("PHP_INI"); if (cptr) SapiModule.php_ini_path_override = strdup(cptr); if (cptr = SapiModule.php_ini_path_override) { if (!(fp = fopen (cptr, "r", "shr=get,put,upd"))) { PrePhpError ("Cannot access PHP.INI, !AZ!AZ.", strerror(errno, vaxc$errno), vaxc$errno == 98962 ? " (protection?)" : "."); return; } fclose (fp); } ScriptFileNamePtr = CgiVar ("SCRIPT_FILENAME"); #if CHECK_FILE_TYPE if (!ConfiguredFileType (ScriptFileNamePtr)) { PrePhpError ("Script file type has not been configured as PHP."); return; } #endif /* CHECK_FILE_TYPE */ if (!(fp = fopen (ScriptFileNamePtr, "r", "shr=get,put,upd"))) { if (ProctorDetect ()) return; PrePhpError ("Cannot access script, !AZ!AZ.", strerror(errno, vaxc$errno), vaxc$errno == 98962 ? " (protection?)" : "."); return; } /* get the first line of the script so it can be checked for "#watch#" */ fgets (Line, sizeof(Line), fp); rewind (fp); if (Debug) fprintf (stdout, "Line |%s|\n", Line); /* check if 'watch' mode should be enabled */ if (cptr = strstr (Line, "#watch")) { WatchEnabled = 1; if (!strncmp (cptr, "#watch:remote_addr=", 19)) { /* if it doesn't match the remote address then disable again */ cptr += 19; for (sptr = cptr; isdigit(*sptr) || *sptr == '.'; sptr++); *sptr = '\0'; if (!(sptr = CgiVar ("REMOTE_ADDR"))) sptr = ""; if (strcmp (cptr, sptr)) WatchEnabled = 0; } } else if (cptr = TrnLnm ("PHPWASD$WATCH_SCRIPT_NAME", NULL)) { if (Debug) fprintf (stdout, "PHPWASD$WATCH_SCRIPT_NAME |%s|\n", cptr); /* enable if the string in the above occurs in the script filename */ ScriptNamePtr = CgiVar ("SCRIPT_NAME"); if (strstr (ScriptNamePtr, cptr)) { WatchEnabled = 1; if (cptr = TrnLnm ("PHPWASD$WATCH_REMOTE_ADDR", NULL)) { if (Debug) fprintf (stdout, "PHPWASD$WATCH_REMOTE_ADDR |%s|\n", cptr); /* if it doesn't match the remote address then disable again */ if (!(sptr = CgiVar ("REMOTE_ADDR"))) sptr = ""; if (strcmp (cptr, sptr)) WatchEnabled = 0; } } } else if (cptr = TrnLnm ("PHPWASD$WATCH_REMOTE_ADDR", NULL)) { if (Debug) fprintf (stdout, "PHPWASD$WATCH_REMOTE_ADDR |%s|\n", cptr); /* enable if it matches the remote address */ if (!(sptr = CgiVar ("REMOTE_ADDR"))) sptr = ""; if (!strcmp (cptr, sptr)) WatchEnabled = 1; } if (Debug) WatchEnabled = 1; if (WatchEnabled) { /* initialize watch mode */ WatchThis (NULL); if (CgiVar ("SCRIPT_RTE")) cptr = "RTE"; else if (IsCgiPlus) cptr = "CGIplus"; else cptr = "CGI"; WatchThis ("BEGIN !AZ (!UL); !AZ; !AZ", cptr, UsageCount, SoftwareId, ImageIdent()); } ScriptDefaultPtr = CgiVar ("SCRIPT_DEFAULT"); if (ScriptDefaultPtr) status = chdir (cptr = ScriptDefaultPtr); else if (ScriptFileNamePtr[0] == '/') { /* script file name in *nix style syntax */ zptr = (sptr = ScriptDefault) + sizeof(ScriptDefault)-1; for (cptr = ScriptFileNamePtr; *cptr && sptr < zptr; *sptr++ = *cptr++); sptr--; while (sptr > ScriptDefault && *sptr != '/') sptr--; *sptr = '\0'; cptr = ScriptDefault; if (WatchEnabled) WatchThis ("CHDIR !AZ", cptr); status = chdir (cptr); } else { /* script file name in VMS syntax */ zptr = (sptr = ScriptDefault) + sizeof(ScriptDefault)-1; for (cptr = ScriptFileNamePtr; *cptr && sptr < zptr; *sptr++ = *cptr++); sptr--; while (sptr > ScriptDefault && *sptr != ']') sptr--; if (*sptr == ']') sptr++; *sptr = '\0'; cptr = decc$translate_vms (ScriptDefault); if ((int)cptr > 0) { if (WatchEnabled) WatchThis ("CHDIR !AZ", cptr); status = chdir (cptr); } else { status = vaxc$errno; cptr = ScriptDefault; } } if (status) { status = vaxc$errno; PrePhpError ("Cannot chdir("!AZ"), !AZ.", cptr, strerror(errno, status)); return; } if (WatchEnabled) WatchThis ("SETENV PATH !AZ", cptr); status = setenv ("PATH", cptr, 1); if (status) { status = vaxc$errno; PrePhpError ("Cannot setenv("PATH","!AZ"), !AZ.", cptr, strerror(errno, status)); return; } QueryStringPtr = CgiVar ("QUERY_STRING"); memset (&SG(request_info), 0, sizeof(SG(request_info))); SG(request_info).argv0 = ScriptFileNamePtr; SG(request_info).request_method = CgiVar("REQUEST_METHOD"); SG(request_info).request_uri = CgiVar("REQUEST_URI"); SG(request_info).query_string = QueryStringPtr; cptr = CgiVar("CONTENT_TYPE"); SG(request_info).content_type = (cptr ? cptr : "" ); cptr = CgiVar("CONTENT_LENGTH"); SG(request_info).content_length = (cptr ? atoi(cptr) : 0); memset (&SG(sapi_headers), 0, sizeof(SG(sapi_headers))); /* assume there's a problem to start with */ SG(sapi_headers).http_response_code = 502; if (auth = CgiVar("HTTP_AUTHORIZATION")) php_handle_auth_data (auth SLS_CC); if (php_request_startup (CLS_C ELS_CC PLS_CC SLS_CC) == FAILURE) { PrePhpError ("PHP request startup has failed!!"); return; } PHPinfo = PHPsyntax = PHPlint = 0; #if ALLOW_WASD_QUERIES if (QueryStringPtr[0] == '!') { if (TrnLnm ("PHPWASD$QUERIES", NULL)) { if (!strncmp (QueryStringPtr, "!wasd=info", 10)) PHPinfo = 1; if (!strncmp (QueryStringPtr, "!wasd=lint", 10)) PHPlint = 1; if (!strncmp (QueryStringPtr, "!wasd=syntax", 12)) PHPsyntax = 1; } } if (PHPinfo) { int flag = PHP_INFO_GENERAL | PHP_INFO_CREDITS | PHP_INFO_CONFIGURATION | PHP_INFO_MODULES | PHP_INFO_ENVIRONMENT | PHP_INFO_VARIABLES | PHP_INFO_LICENSE; php_print_info (flag); php_request_shutdown (NULL); return; } #endif /* ALLOW_WASD_QUERIES */ /* note in [.SAPI]CGI_MAIN.C explains the estrdup() */ SG(request_info).path_translated = estrdup (ScriptFileNamePtr); ZendFileHandle.type = ZEND_HANDLE_FP; ZendFileHandle.handle.fd = 0; ZendFileHandle.handle.fp = fp; ZendFileHandle.filename = SG(request_info).path_translated; ZendFileHandle.free_filename = 0; ZendFileHandle.opened_path = NULL; /* OK, we'll assume everythings OK from here (even if it's not) */ SG(sapi_headers).http_response_code = 200; if (PHPsyntax) { zend_syntax_highlighter_ini ZendHighlight; if (WatchEnabled) WatchThis ("HIGHLIGHT"); if (open_file_for_scanning (&ZendFileHandle CLS_CC) == SUCCESS) { php_get_highlight_struct (&ZendHighlight); zend_highlight (&ZendHighlight); } } else if (PHPlint) { if (WatchEnabled) WatchThis ("LINT"); cptr = CgiVar("SCRIPT_NAME"); php_printf ("\n\nLint %s\n\n\n", cptr); if (php_lint_script (&ZendFileHandle CLS_CC ELS_CC PLS_CC) == SUCCESS) PUTS ("lint PASSED ... "); else PUTS ("lint FAILED ... "); php_printf ("syntax\n\n", cptr); } else { /* restore the *real* translated path (see above) */ SG(request_info).path_translated = CgiVar("PATH_TRANSLATED"); if (WatchEnabled) WatchThis ("EXECUTE"); php_execute_script (&ZendFileHandle CLS_CC ELS_CC PLS_CC); } if (WatchEnabled) WatchThis ("SHUTDOWN"); php_request_shutdown (NULL); } /*****************************************************************************/ /* There's gotta be an easier way to get shareable image information than this! Some quick-and-dirty image analysis. Open the PHPWASDSHR shareable image and read selected fields to get the image identification, build, and linking date. IA64 ELF image analysis plagiarised from: // File: elf_imginfo.c // Author: Hartmut Becker // Creation Date: 24-Sep-2004 */ char* ImageIdent () { #ifdef __ia64 /* ever'thin's null-terminated in this brave new world? */ static $DESCRIPTOR (FormatFaoDsc, "!AZ, !AZ!AZ!%D"); static const char MagicPlus [] = { EHDR$K_ELFMAG0, EHDR$K_ELFMAG1, EHDR$K_ELFMAG2, EHDR$K_ELFMAG3, EHDR$K_ELFCLASS64, EHDR$K_ELFDATA2LSB }; #else static $DESCRIPTOR (FormatFaoDsc, "!AC, !AC!AZ!%D"); #endif static char ImageIdent [48] = "?"; int status; unsigned short ShortLength; unsigned long *ImageDatePtr; char *ImageIdentPtr, *ImageNamePtr; char ImageRecord [512]; FILE *ImageFile; $DESCRIPTOR (ImageIdentDsc, ImageIdent); #ifdef __ia64 int cnt; unsigned long ImageDate [2]; char *cptr, *secptr = NULL; ELF64_EHDR ElfHeader; ELF64_SHDR *shptr = NULL; ELF64_NHDR *nhptr; #endif /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "ImageIdent()\n"); if (ImageIdent[0] != '?') return (ImageIdent); ImageFile = fopen ("PHPWASDSHR", "r", "shr=get"); if (!ImageFile) { fclose (ImageFile); sprintf (ImageIdent, "error: %%X%08.08X (%d)", vaxc$errno, __LINE__); return (ImageIdent); } #ifdef __ALPHA if (!fread (&ImageRecord, sizeof(ImageRecord), 1, ImageFile)) { fclose (ImageFile); sprintf (ImageIdent, "error: %%X%08.08X (%d)", vaxc$errno, __LINE__); return (ImageIdent); } ImageNamePtr = ImageRecord + 200; ImageIdentPtr = ImageRecord + 240; ImageDatePtr = (unsigned long*)(ImageRecord + 192); #endif #ifdef __ia64 ImageNamePtr = ImageIdentPtr = "?"; ImageDatePtr = 0; if (fread (&ElfHeader, sizeof(ElfHeader), 1, ImageFile) != 1) { sprintf (ImageIdent, "error: %%X%08.08X (%d)", vaxc$errno, __LINE__); goto ImageIdentFailed; } if (strncmp ((char*)&ElfHeader.ehdr$t_e_ident, MagicPlus, sizeof(MagicPlus)-1)) { sprintf (ImageIdent, "error: ELF file format? (%d)", __LINE__); goto ImageIdentFailed; } shptr = (ELF64_SHDR*) malloc (sizeof *shptr *ElfHeader.ehdr$w_e_shnum); if (fseek (ImageFile, ElfHeader.ehdr$q_e_shoff, SEEK_SET)) { sprintf (ImageIdent, "error: %%X%08.08X (%d)", vaxc$errno, __LINE__); goto ImageIdentFailed; } if (fread (shptr, sizeof *shptr*ElfHeader.ehdr$w_e_shnum, 1, ImageFile) != 1) { sprintf (ImageIdent, "error: %%X%08.08X (%d)", vaxc$errno, __LINE__); goto ImageIdentFailed; } for (cnt = 1; cnt < ElfHeader.ehdr$w_e_shnum; cnt++) { if (shptr[cnt].shdr$l_sh_type == SHDR$K_SHT_NOTE) { secptr = malloc (shptr[cnt].shdr$q_sh_size); if (fseek (ImageFile, shptr[cnt].shdr$q_sh_offset, SEEK_SET)) { sprintf (ImageIdent, "error: %%X%08.08X (%d)", vaxc$errno, __LINE__); goto ImageIdentFailed; } if (fread (secptr, shptr[cnt].shdr$q_sh_size, 1, ImageFile) != 1) { sprintf (ImageIdent, "error: %%X%08.08X (%d)", vaxc$errno, __LINE__); goto ImageIdentFailed; } for (nhptr = (ELF64_NHDR*)secptr; (char*)nhptr-secptr < shptr[cnt].shdr$q_sh_size; nhptr = (ELF64_NHDR*)((char*)nhptr+sizeof(ELF64_NHDR)+ ((nhptr->nhdr$q_nh_namesz+7)&~7)+ ((nhptr->nhdr$q_nh_descsz+7)&~7))) { if (nhptr->nhdr$q_nh_descsz > 0) { switch (nhptr->nhdr$q_nh_type) { case NHDR$K_NT_VMS_LINKTIME: ImageDatePtr = (unsigned long*) ((char*)nhptr+sizeof(ELF64_NHDR) + ((nhptr->nhdr$q_nh_namesz+7)&~7)); memcpy (ImageDate, ImageDatePtr, 8); ImageDatePtr = ImageDate; break ; case NHDR$K_NT_VMS_IMGNAM: cptr = (char*)nhptr+sizeof(ELF64_NHDR) + ((nhptr->nhdr$q_nh_namesz+7)&~7); ImageNamePtr = malloc (strlen(cptr)+1); strcpy (ImageNamePtr, cptr); break ; case NHDR$K_NT_VMS_IMGID: break ; case NHDR$K_NT_VMS_GSTNAM: break ; case NHDR$K_NT_VMS_IMGBID: cptr = (char*)nhptr+sizeof(ELF64_NHDR) + ((nhptr->nhdr$q_nh_namesz+7)&~7); ImageIdentPtr = malloc (strlen(cptr)+1); strcpy (ImageIdentPtr, cptr); break ; case NHDR$K_NT_VMS_LINKID: break ; default: break ; } } } free (secptr); } } free (shptr); #endif fclose (ImageFile); status = sys$fao (&FormatFaoDsc, &ShortLength, &ImageIdentDsc, ImageIdentPtr, ImageNamePtr, ImageNamePtr[0] ? ", " : "", ImageDatePtr); if (status & 1) ImageIdent[ShortLength] = '\0'; if (Debug) fprintf (stdout, "|%s|\n", ImageIdent); return (ImageIdent); #ifdef __ia64 ImageIdentFailed: { free (shptr); fclose (ImageFile); if (Debug) fprintf (stdout, "|%s|\n", ImageIdent); return (ImageIdent); } #endif } /*****************************************************************************/ /* Generate a standard WASD-like error message *before* PHP gets up an running. WASD 8.2 or later, use "Script-Control:". */ int PrePhpError ( char *FaoString, ... ) { static $DESCRIPTOR (FaoDsc, "Status: 502\r\n\ \r\n\ \n\ \n\ \n\ \n\ ERROR 502\n\ \n\ \n\ ERROR 502  -  Bad Gateway\n\

!AZ\n\ !AZ!AZ!AZ\ \n\n"); int argcnt, status; unsigned short ShortLength; char *cptr, *sptr; char Buffer [2048], FaoBuffer [1024], Format [256]; $DESCRIPTOR (BufferDsc, Buffer); $DESCRIPTOR (FaoBufferDsc, FaoBuffer); $DESCRIPTOR (FaoStringDsc, ""); unsigned long *vecptr; unsigned long FaoVector [32]; va_list argptr; /*********/ /* begin */ /*********/ va_count (argcnt); if (Debug) fprintf (stdout, "PrePhpError() %d |%s|\n", argcnt, FaoString); if (ServerVersion >= 80200) { /* WASD 8.2 or later */ vecptr = FaoVector; va_start (argptr, FaoString); for (argcnt -= 1; argcnt; argcnt--) *vecptr++ = (unsigned long)va_arg (argptr, unsigned long); va_end (argptr); FaoStringDsc.dsc$a_pointer = FaoString; FaoStringDsc.dsc$w_length = strlen(FaoString); status = sys$faol (&FaoStringDsc, &ShortLength, &BufferDsc, (unsigned long*)&FaoVector); if (Debug) fprintf (stdout, "sys$fao() %%X%08.08X\n", status); if (!(status & 1)) return (status); Buffer[ShortLength] = '\0'; fprintf (stdout, "Status: 502\r\n\ Script-Control: X-error-text=\"%s\"\r\n\ Script-Control: X-error-module=\"PHPWASD\"\r\n\ Script-Control: X-error-line=%d\r\n\ \r\n", Buffer, __LINE__); } else { if (!(cptr = CgiVar("SERVER_SOFTWARE"))) cptr = "?"; sptr = CgiVar ("SERVER_SIGNATURE"); status = sys$fao (&FaoDsc, &ShortLength, &FaoBufferDsc, SoftwareId, ImageIdent(), cptr, FaoString, sptr ? "


\n" : "", sptr ? sptr : "", sptr ? "\n" : ""); if (Debug) fprintf (stdout, "sys$fao() %%X%08.08X\n", status); if (!(status & 1)) return (status); FaoBuffer[FaoBufferDsc.dsc$w_length = ShortLength] = '\0'; vecptr = FaoVector; va_start (argptr, FaoString); for (argcnt -= 1; argcnt; argcnt--) *vecptr++ = (unsigned long)va_arg (argptr, unsigned long); va_end (argptr); status = sys$faol (&FaoBufferDsc, &ShortLength, &BufferDsc, (unsigned long*)&FaoVector); if (Debug) fprintf (stdout, "sys$fao() %%X%08.08X\n", status); if (!(status & 1)) return (status); Buffer[ShortLength] = '\0'; fputs (Buffer, stdout); } return (status); } /*****************************************************************************/ /* Check if the type (extension) of the supplied file name can be found in the list of types allowed to be interpreted as PHP. Return true if allowed. */ #if CHECK_FILE_TYPE int ConfiguredFileType (char *FileNamePtr) { char *cptr, *sptr, *tptr; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "ConfiguredFileType() |%s|\n", FileNamePtr); if (!FileNamePtr) return (0); /* is it a permitted file type? */ for (cptr = FileNamePtr; *cptr; *cptr++); while (cptr > FileNamePtr && *cptr != '.') cptr--; sptr = PhpTypeListPtr; while (*sptr) { tptr = cptr; while (*tptr && *sptr && toupper(*tptr) == toupper(*sptr)) { tptr++; sptr++; } if (!*tptr && (!*sptr || *sptr == ',')) break; while (*sptr && *sptr != ',') sptr++; if (*sptr) sptr++; } if (!*tptr && (!*sptr || *sptr == ',')) return (1); return (0); } #endif /* CHECK_FILE_TYPE */ /*****************************************************************************/ /* Called by PHP as the request is activated. */ static int SapiActivate (SLS_D) { char *cptr; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "SapiActivate()\n"); /* a script=param='PHP_INI2="dev:[dir]PHP.INI"' has precedence */ cptr = CgiVar ("PHP_INI2"); if (cptr) PhpIni2 (cptr); else if (PhpIni2Ptr) PhpIni2 (PhpIni2Ptr); /* a script=param='PHP_INIS="include_path=/path/here"' has precedence */ cptr = CgiVar ("PHP_INIS"); if (cptr) PhpIniString (cptr); else if (PhpIniStringPtr) PhpIniString (PhpIniStringPtr); return (SUCCESS); } /*****************************************************************************/ /* Called by PHP as the request is finalized. */ static int SapiDeactivate (SLS_D) { /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "SapiDeactivate()\n"); /* flush anything in the SapiWrite() buffer */ SapiWrite (NULL, 0); return (SUCCESS); } /*****************************************************************************/ /* In common with many *nix sourced utilities PHP has a habit of outputting only a few characters at a time (often a single character). The DECC RTL turns each one of these fwrite()s into a $QIO ... slow and inefficient. Create our own buffering here. */ static int SapiWrite ( const char *String, uint StringLength ) { static char *sptr, *zptr; static char Buffer [WRITE_BUFFER_SIZE]; size_t ret; uint cnt; char *cptr; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "SapiWrite() %d %d\n|%*.*s|\n", String, StringLength, StringLength, StringLength, String); if (AbsorbPreWriteSapi && AbsorbPreWriteSapi != STD_OUT) { /* stop bit-bucketing now the application started to SapiWrite() */ if (!(stdout = freopen ("SYS$OUTPUT:", "w", stdout, "ctx=bin"))) exit (vaxc$errno); if (!(stderr = freopen ("SYS$ERROR:", "w", stderr, "ctx=bin"))) exit (vaxc$errno); AbsorbPreWriteSapi = STD_OUT; } /* initialize static buffer space if necessary */ if (!zptr) zptr = (sptr = Buffer) + sizeof(Buffer); if (!String) { /* explicit flush buffer */ if (WatchEnabled) WatchThis ("WRITE FLUSH"); if (sptr == Buffer) return StringLength; ret = fwrite (Buffer, sptr - Buffer, 1, stdout); if (!ret) php_handle_aborted_connection(); zptr = (sptr = Buffer) + sizeof(Buffer); return (StringLength); } if (WatchEnabled) { WatchThis ("WRITE !UL bytes", StringLength); WatchDump ((char*)String, StringLength); } cptr = (char*)String; cnt = StringLength; while (cnt) { if (sptr < zptr) { *sptr++ = *cptr++; cnt--; continue; } /* implicit flush buffer */ ret = fwrite (Buffer, sptr - Buffer, 1, stdout); if (!ret) php_handle_aborted_connection(); zptr = (sptr = Buffer) + sizeof(Buffer); } return (StringLength); } /*****************************************************************************/ /* Flush anything in the SapiWrite() buffer. */ void SapiFlush (void *ServerContext) { /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "SapiFlush()\n"); SapiWrite (NULL, 0); } /*****************************************************************************/ /* As this is a CGI/CGIplus, and WASD (pre-v8.2) can be rather strict about it's intepretation of CGI response headers, and the PHP engine outputs it response headers in no particular order, buffer the header fields, then provide to WASD in an order that is acceptable. Algorithm is a bit crude but didn't take much to knock together. WASD 8.2 is much more liberal in permitted CGI response field order so save a few CPU cycles by eliminating the above processing. */ static void SapiSendHeader ( sapi_header_struct *shptr, void *ServerContext ) { static int BufferLength, BufferSize, FieldCount; static char *BufferPtr, *CurrentPtr; int cnt; char *cptr, *sptr, *zptr; char CgiStatus [32]; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "SapiSendHeader()\n"); if (ServerVersion >= 90002) { /* WASD 9.0.2 or later */ if (shptr && !NoNphMunge) { if (!memcmp (shptr->header, "HTTP/1.", 7)) { /* transmogrify an NPH header into a CGI status! */ for (cptr = shptr->header+7; *cptr && *cptr != ' '; cptr++); while (*cptr && *cptr == ' ') cptr++; if (!isdigit(*cptr)) return; SG(sapi_headers).http_response_code = atoi(cptr); cnt = sprintf (CgiStatus, "Status: %d\r\n", atoi(cptr)); SapiWrite (CgiStatus, cnt); FieldCount++; return; } } } if (ServerVersion >= 80200) { /* WASD 8.2 or later */ if (shptr) { cptr = shptr->header; /* write this field (with carriage-control) */ cnt = shptr->header_len; SapiWrite (cptr, cnt); SapiWrite ("\r\n", 2); FieldCount++; return; } /* end of response header */ if (!FieldCount) SapiWrite ("Content-Type: text/plain\r\n", 26); /* ensure the server processes in stream mode */ SapiWrite ("Script-control: X-stream-mode\r\n\r\n", 33); /* flush the SapiWrite() buffer (the response header) */ SapiWrite (NULL, 0); FieldCount = 0; return; } /* WASD v8.1 or earlier */ if (!BufferPtr) { BufferSize = SEND_HEADER_BUFFER_CHUNK; if (!(BufferPtr = calloc (1, BufferSize))) exit (vaxc$errno); CurrentPtr = BufferPtr; } if (shptr) { /* buffer this header field */ if (WatchEnabled) WatchThis ("HEADER !AZ", shptr->header); zptr = BufferPtr + BufferSize - 3; sptr = CurrentPtr; cptr = shptr->header; cnt = shptr->header_len; while (cnt--) { if (sptr < zptr) { *sptr++ = *cptr++; continue; } /* buffer full, increase it's size, reset the pointers */ BufferLength = sptr - BufferPtr; BufferSize += SEND_HEADER_BUFFER_CHUNK; BufferPtr = realloc (BufferPtr, BufferSize); if (!BufferPtr) exit (vaxc$errno); zptr = BufferPtr + BufferSize - 3; sptr = BufferPtr + BufferLength; *sptr++ = *cptr++; } *sptr++ = '\r'; *sptr++ = '\n'; *sptr++ = '\0'; BufferLength = sptr - BufferPtr; if (Debug) fprintf (stdout, "|%s|\n", CurrentPtr); CurrentPtr = sptr; FieldCount++; } else { /* search for the first CGI-compliant response field */ if (WatchEnabled) WatchThis ("SEND HEADERS"); zptr = NULL; cptr = BufferPtr; for (cnt = FieldCount; cnt; cnt--) { for (sptr = cptr; *cptr; cptr++); if (!strncasecmp (sptr, "Content-Type:", 13) || !strncasecmp (sptr, "Status:", 7) || !strncasecmp (sptr, "Location:", 9)) { /* note it's address and output */ SapiWrite (zptr = sptr, cptr - sptr); break; } cptr++; } /* should never happen, but you never know */ if (!zptr) SapiWrite ("Content-Type: text/html\r\n", 25); /* output all the rest of the fields */ cptr = BufferPtr; for (cnt = FieldCount; cnt; cnt--) { for (sptr = cptr; *cptr; cptr++); /* if it's not the field that's already been output */ if (sptr != zptr) SapiWrite (sptr, cptr - sptr); cptr++; } /* end of header, ensure the server processes in stream mode */ SapiWrite ("Script-control: X-stream-mode\r\n\r\n", 33); /* flush the SapiWrite() buffer (the response header) */ SapiWrite (NULL, 0); if (IsCgiPlus) { /* reset the buffer storage */ CurrentPtr = BufferPtr; BufferLength = FieldCount = 0; } } } /*****************************************************************************/ /* Wrapper only. */ static void SapiLogMessage (char *LogMessage) { /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "SapiLogMessage()\n"); fprintf (stderr, "%s\n", LogMessage); } /*****************************************************************************/ /* Get 'enviroinment' variable. */ static char* SapiGetEnv ( char *EnvName, unsigned int EnvNameLength ) { char *cptr; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "SapiGetEnv() |%s|\n", EnvName); if (cptr = CgiVar (EnvName)) return (cptr); return (getenv (EnvName)); } /*****************************************************************************/ /* Read the POSTed body from (which with WASD is actually HTTP$INPUT). */ static int SapiReadPost ( char *BufferPtr, uint BufferSize SLS_DC ) { int retval, BufferCount; /*********/ /* begin */ /*********/ if (BufferSize > (uint)SG(request_info).content_length-SG(read_post_bytes)) BufferSize = (uint)SG(request_info).content_length-SG(read_post_bytes); if (Debug) fprintf (stdout, "%d\n", BufferSize); if (WatchEnabled && BufferSize) WatchThis ("POST !UL bytes", BufferSize); BufferCount = 0; while (BufferCount < BufferSize) { retval = read (fileno(stdin), BufferPtr + BufferCount, BufferSize - BufferCount); if (retval <= 0) break; BufferCount += retval; if (Debug) fprintf (stdout, "%d %d\n", BufferCount, BufferSize); } if (WatchEnabled) { if (BufferCount) WatchDump (BufferPtr, BufferCount); if (retval < 0) WatchThis ("POST errno !UL", errno); } return (BufferCount); } /*****************************************************************************/ /* Just get any HTTP cookie. */ static char *SapiReadCookies (SLS_D) { char *cptr; /*********/ /* begin */ /*********/ cptr = CgiVar ("HTTP_COOKIE"); if (WatchEnabled) WatchThis ("COOKIE !AZ", cptr ? cptr : "(none)"); return (cptr); } /*****************************************************************************/ /* Read CGI variables into PHP run-time from the CGIplus stream or from DCL symbols as appropriate to the operating mode being standard CGI or CGIplus/RTE. */ static void SapiRegisterVariables (zval *tvaptr ELS_DC SLS_DC PLS_DC) { int GatewayDone; char *cptr, *sptr, *tptr; char String [32]; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "SapiRegisterVariables()\n"); if (IsCgiPlus) { GatewayDone = 0; /* successively read each CGIplus variable */ while (sptr = CgiVar("*")) { /* won't see "FORM_" or "KEY_" with CGI, suppress with CGIplus */ if (toupper(*sptr) == 'F' && !strncmp (sptr, "FORM_", 5)) continue; if (toupper(*sptr) == 'K' && !strncmp (sptr, "KEY_", 4)) continue; if (toupper(*sptr) == 'P' && !strncmp (sptr, "PHP_", 4)) continue; if (toupper(*sptr) == 'G' && (!strncmp (sptr, "GATEWAY_EOF", 11) || !strncmp (sptr, "GATEWAY_EOT", 11) || !strncmp (sptr, "GATEWAY_ESC", 11))) { if (!GatewayDone) { GatewayDone = 1; if (WatchEnabled) WatchThis ("VAR GATEWAY_API=!AZ", SoftwareId); php_register_variable ("GATEWAY_API", SoftwareId, tvaptr ELS_CC PLS_CC); if (WatchEnabled) WatchThis ("VAR GATEWAY_PHP=!AZ", ImageIdent()); php_register_variable ("GATEWAY_PHP", ImageIdent(), tvaptr ELS_CC PLS_CC); sprintf (String, "%d", UsageCount); if (WatchEnabled) WatchThis ("VAR GATEWAY_USAGE=!AZ", String); php_register_variable ("GATEWAY_USAGE", String, tvaptr ELS_CC PLS_CC); } continue; } for (cptr = sptr; *cptr && *cptr != '='; cptr++); *cptr = '\0'; /* if the volume is indicated ODS-5 and it's not already in *nix */ if (Ods5Translate && *(cptr+1) != '/' && ((toupper(*sptr) == 'P' && !strcmp (sptr, "PATH_TRANSLATED")) || (toupper(*sptr) == 'S' && !strcmp (sptr, "SCRIPT_FILENAME")))) { tptr = decc$translate_vms (cptr+1); if ((int)tptr <= 0) tptr = cptr+1; } else tptr = cptr+1; if (WatchEnabled) WatchThis ("VAR !AZ=!AZ", sptr, tptr); php_register_variable (sptr, tptr, tvaptr ELS_CC PLS_CC); *cptr = '='; } } else SapiRegisterCgiVariables (tvaptr); /* only if WASD has configured this realm as EXTERNAL */ if (sptr = CgiVar ("AUTH_PASSWORD")) { /* supply these Apache mod_php-expected global variables */ if (WatchEnabled) WatchThis ("VAR PHP_AUTH_PW=!AZ", sptr); php_register_variable ("PHP_AUTH_PW", sptr, tvaptr ELS_CC PLS_CC); if (sptr = CgiVar ("AUTH_TYPE")) { if (WatchEnabled) WatchThis ("VAR PHP_AUTH_TYPE=!AZ", sptr); php_register_variable ("PHP_AUTH_TYPE", sptr, tvaptr ELS_CC PLS_CC); } if (sptr = CgiVar ("REMOTE_USER")) { if (WatchEnabled) WatchThis ("VAR PHP_AUTH_USER=!AZ", sptr); php_register_variable ("PHP_AUTH_USER", sptr, tvaptr ELS_CC PLS_CC); } } if (!(sptr = CgiVar ("SCRIPT_NAME"))) sptr = ""; if (WatchEnabled) WatchThis ("VAR PHP_SELF=!AZ", sptr); php_register_variable ("PHP_SELF", sptr, tvaptr ELS_CC PLS_CC); } /*****************************************************************************/ /* Standard CGI environment. Clunky, but what else can we do with DCL symbols? */ static void SapiRegisterCgiVariables (zval *tvaptr ELS_DC SLS_DC PLS_DC) { static char *cgi_var_names [] = { "AUTH_ACCESS", "AUTH_AGENT", "AUTH_GROUP", "AUTH_PASSWORD", "AUTH_REALM", "AUTH_REALM_DESCRIPTION", "AUTH_REMOTE_USER", "AUTH_TYPE", "AUTH_USER", "CONTENT_LENGTH", "CONTENT_TYPE", "DOCUMENT_ROOT", "GATEWAY_BG", /** "GATEWAY_EOF", "GATEWAY_EOT", "GATEWAY_ESC", **/ "GATEWAY_INTERFACE", "GATEWAY_MRS", "HTML_BODYTAG", "HTML_FOOTER", "HTML_FOOTERTAG", "HTML_HEADER", "HTML_HEADERTAG", "HTTP_ACCEPT", "HTTP_ACCEPT_CHARSET", "HTTP_ACCEPT_ENCODING", "HTTP_ACCEPT_LANGUAGE", "HTTP_AUTHORIZATION", "HTTP_CONNECTION", "HTTP_CACHE_CONTROL", "HTTP_CONTENT_LENGTH", "HTTP_CONTENT_ENCODING", "HTTP_CONTENT_TYPE", "HTTP_COOKIE", "HTTP_FORWARDED", "HTTP_HOST", "HTTP_IF_MATCH", "HTTP_IF_NOT_MODIFIED", "HTTP_IF_NONE_MATCH", "HTTP_IF_MODIFIED_SINCE", "HTTP_IF_UNMODIFIED_SINCE", "HTTP_IF_RANGE", "HTTP_KEEP_ALIVE", "HTTP_RANGE", "HTTP_TRAILER", "HTTP_TRANSFER_ENCODING", "HTTP_PRAGMA", "HTTP_REFERER", "HTTP_USER_AGENT", "HTTP_X_FORWARDED_FOR", "PATH_INFO", "PATH_ODS", "PATH_TRANSLATED", "QUERY_STRING", "REMOTE_ADDR", "REMOTE_HOST", "REMOTE_PORT", "REMOTE_USER", "REQUEST_CHARSET", "REQUEST_CONTENT_TYPE", "REQUEST_METHOD", "REQUEST_SCHEME", "REQUEST_TIME_GMT", "REQUEST_TIME_LOCAL", "REQUEST_URI", "SCRIPT_DEFAULT", "SCRIPT_FILENAME", "SCRIPT_NAME", "SCRIPT_RTE", "SERVER_ADDR", "SERVER_ADMIN", "SERVER_CHARSET", "SERVER_GMT", "SERVER_NAME", "SERVER_PROTOCOL", "SERVER_PORT", "SERVER_SOFTWARE", "SERVER_SIGNATURE", "UNQIUE_ID", NULL }; static char *cgi_modssl_names [] = { "HTTPS", "SSL_PROTOCOL", "SSL_SESSION_ID", "SSL_CIPHER", "SSL_CIPHER_EXPORT", "SSL_CIPHER_USEKEYSIZE", "SSL_CIPHER_ALGKEYSIZE", "SSL_CLIENT_M_VERSION", "SSL_CLIENT_M_SERIAL", "SSL_CLIENT_S_DN", "SSL_CLIENT_S_DN_x509", "SSL_CLIENT_I_DN", "SSL_CLIENT_I_DN_x509", "SSL_CLIENT_V_START", "SSL_CLIENT_V_END", "SSL_CLIENT_A_SIG", "SSL_CLIENT_A_KEY", "SSL_CLIENT_CERT", "SSL_SERVER_M_VERSION", "SSL_SERVER_M_SERIAL", "SSL_SERVER_S_DN", "SSL_SERVER_S_DN_x509", "SSL_SERVER_I_DN", "SSL_SERVER_I_DN_x509", "SSL_SERVER_V_START", "SSL_SERVER_V_END", "SSL_SERVER_A_SIG", "SSL_SERVER_A_KEY", "SSL_SERVER_CERT", "SSL_VERSION_INTERFACE", "SSL_VERSION_LIBRARY", NULL }; static char *cgi_purveyor_names [] = { "SECURITY_STATUS", "SSL_CIPHER", "SSL_CIPHER_KEYSIZE", "SSL_CLIENT_CA", "SSL_CLIENT_DN", "SSL_SERVER_CA", "SSL_SERVER_DN", "SSL_VERSION", NULL }; static char *cgi_x509_names [] = { "AUTH_X509_CIPHER", "AUTH_X509_FINGERPRINT", "AUTH_X509_ISSUER", "AUTH_X509_KEYSIZE", "AUTH_X509_SUBJECT", NULL }; int idx, GatewayDone; char *cptr, *sptr, *tptr; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "SapiRegisterCgiVariables()\n"); GatewayDone = 0; /* standard CGI variables */ for (idx = 0; cptr = cgi_var_names[idx]; idx++) { if (!(sptr = CgiVar (cptr))) continue; if (toupper(*cptr) == 'G' && !strncmp (cptr, "GATEWAY_", 8)) { if (!GatewayDone) { GatewayDone = 1; if (WatchEnabled) WatchThis ("VAR GATEWAY_API=!AZ", SoftwareId); php_register_variable ("GATEWAY_API", SoftwareId, tvaptr ELS_CC PLS_CC); if (WatchEnabled) WatchThis ("VAR GATEWAY_PHP=!AZ", ImageIdent()); php_register_variable ("GATEWAY_PHP", ImageIdent(), tvaptr ELS_CC PLS_CC); if (WatchEnabled) WatchThis ("VAR GATEWAY_USAGE=0"); php_register_variable ("GATEWAY_USAGE", "0", tvaptr ELS_CC PLS_CC); } } /* if the volume is indicated ODS-5 and it's not already in *nix */ if (Ods5Translate && *sptr != '/' && ((toupper(*cptr) == 'P' && !strcmp (cptr, "PATH_TRANSLATED")) || (toupper(*cptr) == 'S' && !strcmp (cptr, "SCRIPT_FILENAME")))) { tptr = decc$translate_vms (sptr); if ((int)tptr <= 0) tptr = sptr; } else tptr = sptr; if (WatchEnabled) WatchThis ("VAR !AZ=!AZ", cptr, tptr); php_register_variable (cptr, tptr, tvaptr ELS_CC PLS_CC); } /* Apache mod_ssl-like SSL CGI variables */ if (CgiVar ("SSL_VERSION_INTERFACE")) { for (idx = 0; cptr = cgi_modssl_names[idx]; idx++) { if (!(sptr = CgiVar (cptr))) continue; if (WatchEnabled) WatchThis ("VAR !AZ=!AZ", cptr, sptr); php_register_variable (cptr, sptr, tvaptr ELS_CC PLS_CC); } } /* Purveyor-like SSL CGI variables */ if (CgiVar ("SECURITY_STATUS")) { for (idx = 0; cptr = cgi_purveyor_names[idx]; idx++) { if (!(sptr = CgiVar (cptr))) continue; if (WatchEnabled) WatchThis ("VAR !AZ=!AZ", cptr, sptr); php_register_variable (cptr, sptr, tvaptr ELS_CC PLS_CC); } } /* X.509 client certificate authentication CGI variables */ if (CgiVar ("AUTH_X509_CIPHER")) { for (idx = 0; cptr = cgi_x509_names[idx]; idx++) { if (!(sptr = CgiVar (cptr))) continue; if (WatchEnabled) WatchThis ("VAR !AZ=!AZ", cptr, sptr); php_register_variable (cptr, sptr, tvaptr ELS_CC PLS_CC); } } sptr = CgiVar ("SCRIPT_NAME"); if (WatchEnabled) WatchThis ("VAR SCRIPT_NAME=!AZ", sptr); php_register_variable ("PHP_SELF", sptr ? sptr : "", tvaptr ELS_CC PLS_CC); SG(request_info).request_uri = CgiVar("SCRIPT_NAME"); } /*****************************************************************************/ /* Return the value of a CGI variable regardless of whether it is used in a standard CGI environment or a WASD CGIplus (RTE) environment. Also automatically switches WASD V7.2 and later servers into 'struct' mode for significantly improved performance. WASD by default supplies CGI variables prefixed by "WWW_" to differentiate them from any other DCL symbols (or "env"ironment logicals). PHP scripts expect CGI variables without this. This function is somewhat tailored to the PHP environment to allow for this, ignoring it with the CGIplus streamed variable, adding it when access DCL symbols. Notice also it uses the required PHP emalloc(). Returns a pointer to an emalloc()ed string if the variable exists, or NULL. */ char* CgiVar (char *VarName) { # ifndef CGIVAR_STRUCT_SIZE # define CGIVAR_STRUCT_SIZE 8192 # endif # define SOUS sizeof(unsigned short) static int CalloutDone, StructLength; static char *NextVarNamePtr; static char StructBuffer [CGIVAR_STRUCT_SIZE]; static FILE *CgiPlusIn; int status; int Length; char *bptr, *cptr, *sptr; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "CgiVar() |%s|\n", !VarName ? "NULL" : VarName); if (!VarName || !VarName[0]) { /* initialize */ StructLength = 0; NextVarNamePtr = StructBuffer; if (!VarName) return (NULL); } if (VarName[0]) { /***************************/ /* return a variable value */ /***************************/ if (!IsCgiPlus) { /* standard CGI environment */ static char WwwName [256] = "WWW_"; static $DESCRIPTOR (NameDsc, WwwName); static $DESCRIPTOR (ValueDsc, ""); unsigned short ShortLength; char Value [1024]; /* by default WASD CGI variable names are prefixed by "WWW_", add */ strncpy (WwwName+4, VarName, sizeof(WwwName)-5); NameDsc.dsc$w_length = strlen(WwwName); ValueDsc.dsc$a_pointer = Value; ValueDsc.dsc$w_length = 1023; status = lib$get_symbol (&NameDsc, &ValueDsc, &ShortLength, NULL); if (status & 1) { cptr = emalloc (ShortLength+1); memcpy (cptr, Value, ShortLength); cptr[ShortLength] = '\0'; } else cptr = NULL; return (cptr); } /* hmmm, CGIplus not initialized */ if (!StructLength) return (NULL); if (VarName[0] == '*') { /* return each CGIplus variable in successive calls */ if (!(Length = *(unsigned short*)NextVarNamePtr)) { NextVarNamePtr = StructBuffer; return (NULL); } sptr = (NextVarNamePtr += SOUS); NextVarNamePtr += Length; /* by default WASD CGI variable name are prefixed by "WWW_", ignore */ return (sptr + 4); } /* return a pointer to this CGIplus variable's value */ for (bptr = StructBuffer; Length = *(unsigned short*)bptr; bptr += Length) { /* by default WASD CGI variable name are prefixed by "WWW_", ignore */ sptr = (bptr += SOUS) + 4; for (cptr = VarName; *cptr && *sptr && *sptr != '='; cptr++, sptr++) if (toupper(*cptr) != toupper(*sptr)) break; /* if found return a pointer to the value */ if (!*cptr && *sptr == '=') { cptr = emalloc (strlen(sptr)); strcpy (cptr, sptr+1); return (cptr); } } /* not found */ return (NULL); } /*****************************/ /* get the CGIplus variables */ /*****************************/ /* cannot "sync" in a non-CGIplus environment */ if (!VarName[0] && !IsCgiPlus) return (NULL); /* the CGIPLUSIN stream can be left open */ if (!CgiPlusIn) if (!(CgiPlusIn = fopen (TrnLnm("CGIPLUSIN", NULL), "r"))) exit (vaxc$errno); /* get the starting record (the essentially discardable one) */ for (;;) { cptr = fgets (StructBuffer, sizeof(StructBuffer), CgiPlusIn); if (!cptr) exit (vaxc$errno); /* if the starting sentinal is detected then break */ if (*(unsigned short*)cptr == '!\0' || *(unsigned short*)cptr == '!\n' || (*(unsigned short*)cptr == '!!' && isdigit(*(cptr+2)))) break; } /* MUST be done after reading the synchronizing starting record */ if (Debug) fprintf (stdout, "Content-Type: text/plain\n\n%s", StructBuffer); else if (WatchEnabled) fprintf (stdout, "Content-Type: text/plain\n\n"); /* detect the CGIplus "force" record-mode environment variable (once) */ if (*(unsigned short*)cptr == '!!') { /********************/ /* CGIplus 'struct' */ /********************/ /* get the size of the binary structure */ StructLength = atoi(cptr+2); if (StructLength <= 0 || StructLength > sizeof(StructBuffer)) exit (SS$_BUGCHECK); if (!fread (StructBuffer, 1, StructLength, CgiPlusIn)) exit (vaxc$errno); } else { /*********************/ /* CGIplus 'records' */ /*********************/ /* reconstructs the original 'struct'ure from the records */ sptr = (bptr = StructBuffer) + sizeof(StructBuffer); while (fgets (bptr+SOUS, sptr-(bptr+SOUS), CgiPlusIn)) { /* first empty record (line) terminates variables */ if (bptr[SOUS] == '\n') break; /* note the location of the length word */ cptr = bptr; for (bptr += SOUS; *bptr && *bptr != '\n'; bptr++); if (*bptr != '\n') exit (SS$_BUGCHECK); *bptr++ = '\0'; if (bptr >= sptr) exit (SS$_BUGCHECK); /* update the length word */ *(unsigned short*)cptr = bptr - (cptr + SOUS); } if (bptr >= sptr) exit (SS$_BUGCHECK); /* terminate with a zero-length entry */ *(unsigned short*)bptr = 0; StructLength = (bptr + SOUS) - StructBuffer; } if (WatchEnabled) { fprintf (stdout, "%d\n", StructLength); for (bptr = StructBuffer; Length = *(unsigned short*)bptr; bptr += Length) if (WatchEnabled) WatchThis ("CGI \'!AZ\'", bptr += SOUS); } if (!CalloutDone) { /* provide the CGI callout to set CGIplus into 'struct' mode */ fflush (stdout); fputs (CgiPlusEscPtr, stdout); fflush (stdout); /* the leading '!' indicates we're not going to read the response */ fputs ("!CGIPLUS: struct", stdout); fflush (stdout); fputs (CgiPlusEotPtr, stdout); fflush (stdout); /* don't need to do this again (the '!!' tells us what mode) */ CalloutDone = 1; } return (NULL); # undef SOUS } /*****************************************************************************/ /* Self-contained functionality. Designed to be called if the script is not found. If found the script needs to instantiate whatever resources it is proctored for and then return an HTTP 204 to the server. If not found then call this function and if this is the first call then check if there is a REQUEST_METHOD. If there is then return false. If not assume WASD is proactively starting the RTE. Then respond with an HTTP 204 and return true. If the calling routine receives a false then it continues processing, if a true then it concludes and waits for the next request. */ int ProctorDetect () { static int DetectCount; char *cptr; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "ProctorDetect()\n"); if (DetectCount++) return (0); /* if this CGI variable does not exist then probably not scripting */ cptr = CgiVar ("SERVER_SOFTWARE"); if (!(cptr && *cptr)) return (0); cptr = CgiVar ("REQUEST_METHOD"); if (cptr && *cptr) return (0); fputs ("Status: 204 RTE Proctor Response\r\n\r\n", stdout); return (1); } /****************************************************************************/ /* Process the secondary PHP.INI file. */ int PhpIni2 (char *FileName) { char Line [256]; FILE *fp; /*********/ /* begin */ /*********/ if (WatchEnabled) WatchThis ("PHP INI2 !AZ", FileName); if (!(fp = fopen (FileName, "r", "shr=get,put,upd"))) { PrePhpError ("Cannot access PHP.INI2, !AZ!AZ.", strerror(errno, vaxc$errno), vaxc$errno == 98962 ? " (protection?)" : "."); return (0); } while (fgets (Line, sizeof(Line), fp)) PhpIniDirective (Line); fclose (fp); return (1); } /****************************************************************************/ /* Takes a single string containing one or more (semicolon delimited) PHP.INI directives and uses them to continue overriding current directive settings. */ int PhpIniString (char *String) { #define MODULE_PERSISTENT 1 char *cptr, *sptr, *zptr; char Line [1024]; /*********/ /* begin */ /*********/ if (WatchEnabled) WatchThis ("PHP INIS !AZ", String); cptr = String; while (*cptr) { zptr = (sptr = Line) + sizeof(Line)-1; while (*cptr && *cptr != ';') { if (*cptr == '\"') { while (*cptr && *cptr != '\"') { if (*cptr == '\\') { cptr++; if (*cptr && sptr < zptr) *sptr++ = *cptr++; continue; } if (*cptr && sptr < zptr) *sptr++ = *cptr++; } } if (*cptr == '\\') { cptr++; if (*cptr && sptr < zptr) *sptr++ = *cptr++; continue; } if (*cptr && sptr < zptr) *sptr++ = *cptr++; } *sptr = '\0'; if (*cptr) cptr++; PhpIniDirective (Line); } return (1); } /****************************************************************************/ /* A single, PHP.INI entry such as 'include_path = "/path/to/stuff"'. Overrides the existing PHP.INI directive value, or loads a PHP extension. */ int PhpIniDirective (char *String) { #define MODULE_PERSISTENT 1 int ncnt, vcnt, zstatus; char *cptr, *nptr, *vptr; char Line [256]; zval zext, zscr; /*********/ /* begin */ /*********/ cptr = String; while (isspace(*cptr)) cptr++; if (!*cptr || *cptr == ';' || *cptr == '[') return (SUCCESS); nptr = cptr; while (*cptr && !isspace(*cptr) && *cptr != '=') cptr++; ncnt = cptr - nptr; if (*cptr == '=') *cptr++ = '\0'; else { *cptr++ = '\0'; while (isspace(*cptr)) cptr++; if (*cptr == '=') cptr++; } while (isspace(*cptr)) cptr++; if (*cptr == '\"') { vptr = ++cptr; while (*cptr && *cptr != '\"') { if (*cptr == '\\') cptr++; if (*cptr) cptr++; } } else { vptr = cptr; while (*cptr && !isspace(*cptr)) cptr++; } vcnt = cptr - vptr; if (*cptr) *cptr++ = '\0'; if (!strcmp (nptr, "extension")) { /* dynamically load PHP extension */ if (WatchEnabled) WatchThis ("INI !AZ=!AZ", nptr, vptr); zext.value.str.val = vptr; zext.value.str.len = vcnt; zext.type = IS_STRING; php_dl (&zext, MODULE_PERSISTENT, &zscr TSRMLS_DC); if (Debug) fprintf (stdout, "|%s|%s|\n", nptr, vptr); zstatus = SUCCESS; } else if (!strcmp (nptr, "zend_extension")) { zstatus = zend_load_extension (vptr); if (WatchEnabled) WatchThis ("INI !AZ=!AZ (!SL !AZ)", nptr, vptr, zstatus, zstatus ? "FAILURE" : "SUCCESS"); } else { zstatus = zend_alter_ini_entry (nptr, ncnt+1, vptr, vcnt, ZEND_INI_SYSTEM, ZEND_INI_STAGE_ACTIVATE); if (WatchEnabled) WatchThis ("INI !AZ=!AZ (!SL !AZ)", nptr, vptr, zstatus, zstatus ? "FAILURE" : "SUCCESS"); } return (zstatus); } /*****************************************************************************/ /* Hmmm, idea look familiar? :-) */ int WatchThis ( char *FaoString, ... ) { static unsigned long LibStatTimerReal = 1, LibStatTimerCpu = 2; static int WatchCount; int argcnt, status; char *cptr, *sptr, *zptr; char Buffer [1024], FaoBuffer [256]; unsigned long CpuBinTime; unsigned long RealBinTime [2]; unsigned long *vecptr; $DESCRIPTOR (BufferDsc, Buffer); $DESCRIPTOR (FaoBufferDsc, FaoBuffer); unsigned long FaoVector [32]; va_list argptr; /*********/ /* begin */ /*********/ va_count (argcnt); if (Debug) fprintf (stdout, "WatchThis() %d |%s|\n", argcnt, FaoString); if (!FaoString) { if (WatchCount) { /* post-processing reset */ lib$stat_timer (&LibStatTimerReal, &RealBinTime, 0); lib$stat_timer (&LibStatTimerCpu, &CpuBinTime, 0); WatchThis ("TIME REAL=!%T CPU=!UL.!2ZL", &RealBinTime, CpuBinTime/100, CpuBinTime%100); WatchThis ("WATCH end"); WatchEnabled = WatchCount = 0; /* back to binary mode (remove if necessary) */ if (!(stdout = freopen ("SYS$OUTPUT:", "w", stdout, "ctx=bin"))) exit (vaxc$errno); if (!(stderr = freopen ("SYS$OUTPUT:", "w", stderr, "ctx=bin"))) exit (vaxc$errno); } else { /* pre-processing initialize */ WatchCount = 1; /* back to record mode as WATCH is line-oriented output */ if (!(stdout = freopen ("SYS$OUTPUT:", "w", stdout, "ctx=rec"))) exit (vaxc$errno); if (!(stderr = freopen ("SYS$OUTPUT:", "w", stderr, "ctx=rec"))) exit (vaxc$errno); lib$init_timer (0); fprintf (stdout, "Content-Type: text/plain\n\n"); WatchThis ("WATCH begin"); } return (SS$_NORMAL); } vecptr = FaoVector; *vecptr++ = WatchCount++; *vecptr++ = 0; va_start (argptr, FaoString); for (argcnt -= 1; argcnt; argcnt--) *vecptr++ = (unsigned long)va_arg (argptr, unsigned long); va_end (argptr); zptr = (sptr = FaoBuffer) + sizeof(FaoBuffer)-3; for (cptr = "|!4ZL|!%T|"; *cptr; *sptr++ = *cptr++); for (cptr = FaoString; *cptr && sptr < zptr; *sptr++ = *cptr++); *sptr++ = '|'; *sptr++ = '\n'; *sptr++ = '\0'; FaoBufferDsc.dsc$a_pointer = FaoBuffer; FaoBufferDsc.dsc$w_length = sptr - FaoBuffer; status = sys$faol (&FaoBufferDsc, 0, &BufferDsc, (unsigned long*)&FaoVector); if (Debug) fprintf (stdout, "sys$fao() %%X%08.08X\n", status); if (!(status & 1)) exit (status); fputs (Buffer, stdout); return (status); } /*****************************************************************************/ /* Ditto? :-) */ int WatchDump ( char *DataPtr, int DataLength ) { int cnt, len; char *cptr, *lptr, *sptr; char WatchBuffer [256]; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "WatchDump() %d\n", DataLength); cptr = DataPtr; len = DataLength; while (len) { sptr = WatchBuffer; lptr = cptr; cnt = 0; while (cnt < 32) { if (cnt < len) sptr += sprintf (sptr, "%02.02X", (unsigned char)*cptr++); else { *sptr++ = ' '; *sptr++ = ' '; } if (!(++cnt % 4)) *sptr++ = ' '; } *sptr++ = ' '; cptr = lptr; cnt = 0; while (cnt < 32 && cnt < len) { if (isprint(*cptr)) *sptr++ = *cptr; else *sptr++ = '.'; cptr++; cnt++; } if (len > 32) len -= 32; else len = 0; *sptr++ = '\n'; *sptr = '\0'; fputs (WatchBuffer, stdout); } return (1); } /*****************************************************************************/ /* Translate a logical name using LNM$FILE_DEV. Returns a pointer to the value string, or NULL if the name does not exist. If 'LogValue' is supplied the logical name is translated into that (assumed to be large enough), otherwise it's translated into an internal static buffer. */ char* TrnLnm ( char *LogName, char *LogValue ) { static unsigned short ShortLength; static char StaticLogValue [256]; static $DESCRIPTOR (LogNameDsc, ""); static $DESCRIPTOR (LnmFileDevDsc, "LNM$FILE_DEV"); static struct { short int buf_len; short int item; void *buf_addr; unsigned short *ret_len; } LnmItems [] = { { 255, LNM$_STRING, 0, &ShortLength }, { 0,0,0,0 } }; int status; char *cptr; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "TrnLnm() |%s|\n", LogName); LogNameDsc.dsc$a_pointer = LogName; LogNameDsc.dsc$w_length = strlen(LogName); if (LogValue) cptr = LnmItems[0].buf_addr = LogValue; else cptr = LnmItems[0].buf_addr = StaticLogValue; status = sys$trnlnm (0, &LnmFileDevDsc, &LogNameDsc, 0, &LnmItems); if (Debug) fprintf (stdout, "sys$trnlnm() %%X%08.08X\n", status); if (!(status & 1)) { if (Debug) fprintf (stdout, "|(null)|\n"); return (NULL); } cptr[ShortLength] = '\0'; if (Debug) fprintf (stdout, "|%s|\n", cptr); return (cptr); } /****************************************************************************/ /* Return an integer reflecting the major, minor and other VMS version number. For example, return 600 for "V6.0", 730 for "V7.3" and 732 for "V7.3-2". */ #if PHPWASD_CRTL_VER >= 70300000 int VmsVersion () { static int VersionInteger; static char SyiVersion [8+1]; static struct { short int buf_len; short int item; void *buf_addr; unsigned short *ret_len; } SyiItems [] = { { 8, SYI$_VERSION, &SyiVersion, 0 }, { 0,0,0,0 } }; int status; char *cptr; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "VmsVersion() %d\n", VersionInteger); if (VersionInteger) return (VersionInteger); if (cptr = getenv("WASD_VMS_VERSION")) strncpy (SyiVersion, cptr, sizeof(SyiVersion)); else { status = sys$getsyiw (0, 0, 0, &SyiItems, 0, 0, 0); if (!(status & 1)) exit (status); } SyiVersion[sizeof(SyiVersion)-1] = '\0'; if (Debug) fprintf (stdout, "SyiVersion |%s|\n", SyiVersion); if (SyiVersion[0] == 'V' && isdigit(SyiVersion[1]) && SyiVersion[2] == '.' && isdigit(SyiVersion[3])) { /* e.g. "V7.3" */ VersionInteger = ((SyiVersion[1]-48) * 100) + ((SyiVersion[3]-48) * 10); /* if something like "V7.3-2" */ if (SyiVersion[4] == '-') VersionInteger += SyiVersion[5]-48; } else VersionInteger = 0; if (Debug) fprintf (stdout, "%d\n", VersionInteger); return (VersionInteger); } #endif /* PHPWASD_CRTL_VER >= 70300000 */ /*****************************************************************************/ /* */ #if PHPWASD_CRTL_VER >= 70300000 int SetCrtlFeature ( char *name, int value ) { int idx; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "SetCrtlFeature() |%s| %d\n", name, value); idx = decc$feature_get_index(name); if (idx == -1) { if (Debug) perror(name); return (-1); } if (decc$feature_set_value(idx, 1, value) == -1) { if (Debug) perror(name); return (-1); } if (decc$feature_get_value(idx, 1) != value) { if (Debug) perror(name); return (-1); } return (0); } #endif /* PHPWASD_CRTL_VER >= 70300000 */ /*****************************************************************************/