/*****************************************************************************/ /* phpRTE.c PHP: Hypertext Preprocessor: Run-Time Environment 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. PHPRTE COPYRIGHT ---------------- Copyright (C) 2002-2024 Mark G.Daniel This package comes with ABSOLUTELY NO WARRANTY. This is free software, and you are welcome to redistribute. (Given up on trying to meld the vaious hands of licenses dealt.) 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. -------------------------------------------------------------------- The PHP License, version 3.01 Copyright (c) 1999 - 2019 The PHP Group. All rights reserved. -------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without modification, is permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 8< snip 8< Full license at: https://www.php.net/license/3_01.txt 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]PHPRTE.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, PHPRTE 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 PHPRTE_WATCH to any non-empty value o Defining the logical name PHPRTE_WATCH_SCRIPT_NAME to contain a (case-sensitive) string from the SCRIPT_NAME variable (e.g. "php_info").. o Defining the logical name PHPRTE_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() PHPRTE_NO_TRANSLATE when defined suppresses automatic ODS-5 VMS to *nix file-system syntax for PATH_TRANSLATED and SCRIPT_FILENAME PHPRTE_QUERIES allows the likes of "?wasd=info" (see above) PHPRTE_TYPE comma separated list of allowed PHP script types (e.g. ".PHP" or ".PHP,.PHTML") PHPRTE_WATCH turns on all WATCH statements if non-empty (defined) PHPRTE_WATCH_SCRIPT_NAME turns on all WATCH statements if this string is found in the SCRIPT_NAME path PHPRTE_WATCH_REMOTE_ADDR turns on all WATCH statements if this string matches the IP address found in REMOTE_ADDR variable PHPRTE_ABSORB_PRE_WRITESAPI see version log entry 02-JUN-2009 PHPRTESHR sharable image containing PHP engine BUILD DETAILS ------------- $ @BUILD_PHPRTE BUILD !compile+link $ @BUILD_PHPRTE LINK !link-only VERSION HISTORY (update SOFTWAREVN as well!) --------------- 02-AUG-2024 MGD v2.0.0, VSI PHP V8.0 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 #if defined(__ia64) || defined(__x86_64) #define ELVEN #include #endif /* PHP-specific includes */ #define php_register_internal_extensions_func \ php_register_internal_exts_func #define php_import_environment_variables \ php_import_environment_vars #pragma message disable UNSTRUCTMEM #include "php.h" #include "php_main.h" #include "php_variables.h" #include "basic_functions.h" #include "dl.h" #pragma message enable UNSTRUCTMEM #include "info.h" #include "sapi.h" /* 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 PHPRTE is used here. */ #pragma message disable UNSTRUCTMEM #pragma message disable LONGEXTERN #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" #pragma message enable UNSTRUCTMEM #pragma message enable LONGEXTERN /**********/ /* macros */ /**********/ #ifndef BUILD_DATETIME # define BUILD_DATETIME "(undefined)" #endif #ifndef PHPRTE_CRTL_VER # define PHPRTE_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 dbug, 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* ImgIdnt(); 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 (void); static int SapiDeactivate (void); static void SapiFlush (void*); static char* SapiGetEnv (const char*, uint); static void SapiLogMessage (const char*, int); static char *SapiReadCookies (void); static uint SapiReadPost (char*, uint); static void SapiRegisterVariables (zval*); static void SapiRegisterCgiVariables (zval*); static void SapiSendHeader (sapi_header_struct*, void*); static uint SapiWrite (const char*, uint); /*****************************/ /* PHP SAPI module structure */ /*****************************/ static sapi_module_struct SapiModule = { "phpwasd", /* name */ SoftwareId, /* pretty name */ (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 */ 0, /* terminate process */ NULL, /* php ini path override */ NULL, /* default post reader */ NULL, /* treat data */ 0, /* executable location */ 0, /* php ini ignore */ 0, /* php ini ignore cwd */ 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 */ 0, /* additional_functions */ 0 /* input_filter_init */ }; /*****************************************************************************/ /* */ main (int argc, char *argv[]) { char *cptr; /*********/ /* begin */ /*********/ sprintf (SoftwareId, "%s, %d, %s", SOFTWAREID, PHPRTE_CRTL_VER, BUILD_DATETIME); if (argc > 1 && !strcasecmp (argv[1], "/VERSION")) { fprintf (stdout, "%s\n%s\n", SoftwareId, ImgIdnt()); exit (SS$_NORMAL); } if (dbug = WatchEnabled = (TrnLnm ("PHPRTE_DBUG", NULL) != NULL)) fprintf (stdout, "Content-Type: text/plain\n\n"); NoNphMunge = 0; if (TrnLnm ("PHPRTE_NO_NPH_MUNGE", NULL)) NoNphMunge = 1; AbsorbPreWriteSapi = 0; if (TrnLnm ("PHPRTE_ABSORB_PRE_WRITESAPI", NULL)) AbsorbPreWriteSapi = STD_NL; #if CHECK_FILE_TYPE if (!(PhpTypeListPtr = TrnLnm ("PHPRTE_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 (!dbug) { /* 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 ("PHPRTE_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 ("PHPRTE_INI", PhpWasdIni); if (dbug) fprintf (stdout, "PHPRTE_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 ("PHPRTE_INI2", PhpWasdIni2); if (dbug) fprintf (stdout, "PHPRTE_INI2 |%s|\n", cptr); cptr = PhpIniStringPtr = TrnLnm ("PHPRTE_INIS", PhpWasdInis); if (dbug) fprintf (stdout, "PHPRTE_INIS |%s|\n", cptr); if (IsCgiPlus) { for (;;) { if (dbug) 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 (dbug) 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 (dbug) 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 (dbug) 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 ("PHPRTE_NO_TRANSLATE", NULL)) Ods5Translate = 0; else Ods5Translate = 1; } #if PHPRTE_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 /* PHPRTE_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 (dbug) 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 ("PHPRTE_WATCH", NULL)) WatchEnabled = 1; else if (cptr = TrnLnm ("PHPRTE_WATCH_SCRIPT_NAME", NULL)) { if (dbug) fprintf (stdout, "PHPRTE_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 ("PHPRTE_WATCH_REMOTE_ADDR", NULL)) { if (dbug) fprintf (stdout, "PHPRTE_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 ("PHPRTE_WATCH_REMOTE_ADDR", NULL)) { if (dbug) fprintf (stdout, "PHPRTE_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 (dbug) 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, ImgIdnt()); } 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); if (php_request_startup () == FAILURE) { PrePhpError ("PHP request startup has failed!!"); return; } PHPinfo = PHPsyntax = PHPlint = 0; #if ALLOW_WASD_QUERIES if (QueryStringPtr[0] == '!') { if (TrnLnm ("PHPRTE_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); memset (&ZendFileHandle, 0, sizeof(ZendFileHandle)); ZendFileHandle.type = ZEND_HANDLE_FP; 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) == 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) == 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); } 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 PHPRTESHR 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* ImgIdnt () { #ifdef ELVEN /* ever'thin's null-terminated in this brave new world? */ static $DESCRIPTOR (FormatFaoDsc, "!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 ELVEN int cnt; unsigned long ImageDate [2]; char *cptr, *secptr = NULL; ELF64_EHDR ElfHeader; ELF64_SHDR *shptr = NULL; ELF64_NHDR *nhptr; #endif /*********/ /* begin */ /*********/ if (dbug) fprintf (stdout, "ImgIdnt()\n"); if (ImageIdent[0] != '?') return (ImageIdent); ImageFile = fopen ("PHP$SHR", "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 ELVEN 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); if (strlen(cptr)) { 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); if (strlen(cptr)) { 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, ImageDatePtr); if (status & 1) ImageIdent[ShortLength] = '\0'; if (dbug) fprintf (stdout, "|%s|\n", ImageIdent); return (ImageIdent); #ifdef ELVEN ImageIdentFailed: { free (shptr); fclose (ImageFile); if (dbug) 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 (dbug) 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 (dbug) 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=\"PHPRTE\"\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, ImgIdnt(), cptr, FaoString, sptr ? "


\n" : "", sptr ? sptr : "", sptr ? "\n" : ""); if (dbug) 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 (dbug) 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 (dbug) 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 (void) { char *cptr; /*********/ /* begin */ /*********/ if (dbug) 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 () { /*********/ /* begin */ /*********/ if (dbug) 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 uint 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 (dbug) 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 (dbug) 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 (dbug) 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 (dbug) 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 ( const char *LogMessage, int type ) { /*********/ /* begin */ /*********/ if (dbug) fprintf (stdout, "SapiLogMessage()\n"); fprintf (stderr, "%s\n", LogMessage); } /*****************************************************************************/ /* Get 'enviroinment' variable. */ static char* SapiGetEnv ( const char *EnvName, uint EnvNameLength ) { char *cptr; /*********/ /* begin */ /*********/ if (dbug) fprintf (stdout, "SapiGetEnv() |%s|\n", EnvName); if (cptr = CgiVar ((char*)EnvName)) return (cptr); return (getenv ((char*)EnvName)); } /*****************************************************************************/ /* Read the POSTed body from (which with WASD is actually HTTP$INPUT). */ static uint SapiReadPost ( char *BufferPtr, uint BufferSize ) { 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 (dbug) 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 (dbug) 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 () { 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) { int GatewayDone; char *cptr, *sptr, *tptr; char String [32]; /*********/ /* begin */ /*********/ if (dbug) 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); if (WatchEnabled) WatchThis ("VAR GATEWAY_PHP=!AZ", ImgIdnt()); php_register_variable ("GATEWAY_PHP", ImgIdnt(), tvaptr); sprintf (String, "%d", UsageCount); if (WatchEnabled) WatchThis ("VAR GATEWAY_USAGE=!AZ", String); php_register_variable ("GATEWAY_USAGE", String, tvaptr); } 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); *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); if (sptr = CgiVar ("AUTH_TYPE")) { if (WatchEnabled) WatchThis ("VAR PHP_AUTH_TYPE=!AZ", sptr); php_register_variable ("PHP_AUTH_TYPE", sptr, tvaptr); } if (sptr = CgiVar ("REMOTE_USER")) { if (WatchEnabled) WatchThis ("VAR PHP_AUTH_USER=!AZ", sptr); php_register_variable ("PHP_AUTH_USER", sptr, tvaptr); } } if (!(sptr = CgiVar ("SCRIPT_NAME"))) sptr = ""; if (WatchEnabled) WatchThis ("VAR PHP_SELF=!AZ", sptr); php_register_variable ("PHP_SELF", sptr, tvaptr); } /*****************************************************************************/ /* Standard CGI environment. WASD v12.2.0 and later otherwise undefined. */ static void SapiRegisterCgiVariables (zval *tvaptr) { char *aptr, *cptr, *sptr; /*********/ /* begin */ /*********/ if (dbug) fprintf (stdout, "SapiRegisterCgiVariables()\n"); if (!(cptr = CgiVar ("GATEWAY_SYMBOLS"))) { if (WatchEnabled) WatchThis ("VAR GATEWAY_SYMBOLS=?"); php_register_variable ("GATEWAY_SYMBOLS", "?", tvaptr); cptr = ""; } if (dbug) fprintf (stdout, "GATEWAY_SYMBOLS |%s|\n", cptr); while (*cptr) { for (aptr = cptr; *aptr && *aptr != ','; aptr++); if (*aptr) *aptr = '\0'; else aptr = NULL; if (*(ulong*)cptr == 'WWW_') cptr += 4; if (sptr = CgiVar (cptr)) { if (WatchEnabled) WatchThis ("VAR !AZ=!AZ", cptr, sptr); php_register_variable (cptr, sptr, tvaptr); } if (aptr) { *aptr++ = ','; cptr = aptr; } else while (*cptr) cptr++; } sptr = CgiVar ("SCRIPT_NAME"); if (WatchEnabled) WatchThis ("VAR SCRIPT_NAME=!AZ", sptr); php_register_variable ("PHP_SELF", sptr ? sptr : "", tvaptr); 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 (dbug) 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 (dbug) 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 (dbug) 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, zstatus; char *cptr, *nptr, *vptr; char Line [256]; zval zext, zscr; zend_string *zname, *zvalue; /*********/ /* 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++; } if (*cptr) *cptr++ = '\0'; ZVAL_STRING(&zext,vptr); zvalue = zend_string_init (vptr, strlen(vptr), 0); zname = zend_string_init (nptr, strlen(nptr), 0); if (!strcmp (nptr, "extension")) { /* dynamically load PHP extension */ if (WatchEnabled) WatchThis ("INI !AZ=!AZ", nptr, vptr); php_dl (vptr, MODULE_PERSISTENT, &zscr, 1); if (dbug) 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 (zname, zvalue, 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 (dbug) 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 !AZ", IsCgiPlus ? "CGIplus" : "CGI"); } 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 (dbug) 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 (dbug) 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 (dbug) 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 (dbug) fprintf (stdout, "sys$trnlnm() %%X%08.08X\n", status); if (!(status & 1)) { if (dbug) fprintf (stdout, "|(null)|\n"); return (NULL); } cptr[ShortLength] = '\0'; if (dbug) 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 PHPRTE_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 (dbug) 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 (dbug) 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 (dbug) fprintf (stdout, "%d\n", VersionInteger); return (VersionInteger); } #endif /* PHPRTE_CRTL_VER >= 70300000 */ /*****************************************************************************/ /* */ #if PHPRTE_CRTL_VER >= 70300000 int SetCrtlFeature ( char *name, int value ) { int idx; /*********/ /* begin */ /*********/ if (dbug) fprintf (stdout, "SetCrtlFeature() |%s| %d\n", name, value); idx = decc$feature_get_index(name); if (idx == -1) { if (dbug) perror(name); return (-1); } if (decc$feature_set_value(idx, 1, value) == -1) { if (dbug) perror(name); return (-1); } if (decc$feature_get_value(idx, 1) != value) { if (dbug) perror(name); return (-1); } return (0); } #endif /* PHPRTE_CRTL_VER >= 70300000 */ /*****************************************************************************/