/*****************************************************************************/ /* ReportError.c A CGI-compliant script. The greater efficiencies mean it's much better used as a CGIplus script! Provided to work in with the WASD HTTPd [ErrorReportScript] configuration parameter, this script is designed to be both a basic work-horse for implementing a local error reporting format as well as a template and/or basis for a local reporting script. Configuration directives (either/or): [ErrorReportScript] /cgi-bin/reporterror [ErrorReportScript] /cgiplus-bin/reporterror The /DEFAULT qualifier generates an error report much like that supplied by native WASD HTTPd error reporting. LOCALLY IMPLEMENTED REPORTING CODE ---------------------------------- Using the /HEADER qualifier this program can be called from a DCL procedure to supply an appropriate (and correct) HTTP response header, IMPORTANTLY providing a correct challenge for 401 errors (authorization required). The procedure could then go on to provide the body of the error report. The /LOCAL qualifier results in the LocalReport() function being called. This function stub is provided as a simple place-holder for a locally implemented error report. ANY LOCAL VERSION MUST BE CAREFUL TO SUPPLY CORRECT RESPONSE HEADERS FOR 401 ERRORS (authorization required) ... using the HttpHeader() function is strongly recommended. The "WWW_FORM_ERROR..." CGI variables listed below supply relevant information specially generated by the HTTPd for error reporting. When developing a local version be sure to move the source code out of the WASD_ROOT:[SRC.MISC] so it's not lost in some future update! CGI VARIABLES ------------- WWW_FORM_ERROR_ABOUT item message is about (if applicable) WWW_FORM_ERROR_ABOUT2 additional information (if applicable) WWW_FORM_ERROR_BASIC basic authentication challenge WWW_FORM_ERROR_DIGEST digest authentication challenge WWW_FORM_ERROR_LINE name of source code line (e.g. "1732") WWW_FORM_ERROR_MODULE name of source code module (e.g. "REQUEST") WWW_FORM_ERROR_STATUS HTTP status code (e.g. "404") WWW_FORM_ERROR_STATUS_TEXT brief description of status code (e.g. "Not Found") WWW_FORM_ERROR_STATUS_EXPLANATION longer description of status code WWW_FORM_ERROR_TEXT server-generated error message WWW_FORM_ERROR_TEXT2 server suggestion about what to do about it :^) WWW_FORM_ERROR_TYPE "basic" or "detailed" WWW_FORM_ERROR_VMS VMS status value, decimal (if applicable) WWW_HTTP_PRAGMA "no-cache" (optional) WWW_HTTP_REFERER "close" button becomes active WWW_PATH_INFO URL path to shelf WWW_PATH_TRANSLATED VMS file specification for shelf WWW_REQUEST_METHOD any method WWW_REQUEST_CHARSET request determined character set WWW_SCRIPT_NAME path to script WWW_SERVER_CHARSET server default character set WWW_SERVER_NAME host on which the script is executing WWW_SERVER_PORT port from which the script is executing WWW_SERVER_SIGNATURE server's "signature" WWW_SERVER_SOFTWARE HTTPd identifying string QUALIFIERS ---------- /CHARSET= "Content-Type: text/html; charset=...", empty suppress charset /DBUG turns on all "if (Debug)" statements (don't use with OSU) /DEFAULT output the default error report /HEADER output an appropriate HTTP response header only /LOCAL execute the error reporting function ProcessLocal() /MORE= string added to bottom of default report (can be any HTML) LOGICAL NAMES ------------- REPORTERROR$DBUG turns on all "if (Debug)" statements REPORTERROR$PARAM equivalent to (overrides) the command line parameters/qualifiers (define as a system-wide logical) BUILD DETAILS ------------- See BUILD_REPORTERROR.COM procedure. COPYRIGHT --------- Copyright (C) 1998-2021 Mark G.Daniel Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. VERSION HISTORY (update SoftwareID as well!) --------------- 23-DEC-2003 MGD v1.3.1, minor conditional mods to support IA64 28-OCT-2000 MGD v1.3.0, use CGILIB object module 12-APR-2000 MGD v1.2.0, reflect changes in WASD v7.0 report format 24-APR-1999 MGD v1.1.0, use CGILIB.C 18-OCT-1998 MGD v1.0.0, initial (with HTTPd v5.3 [ErrorReportPath]) */ /*****************************************************************************/ #define SOFTWAREVN "1.3.1" #define SOFTWARENM "REPORTERROR" #ifdef __ALPHA # define SOFTWAREID SOFTWARENM " AXP-" SOFTWAREVN #endif #ifdef __ia64 # define SOFTWAREID SOFTWARENM " IA64-" SOFTWAREVN #endif #ifdef __VAX # define SOFTWAREID SOFTWARENM " VAX-" SOFTWAREVN #endif #ifdef __x86_64 # define SOFTWAREID SOFTWARENM " X86-" SOFTWAREVN #endif /* standard C header files */ #include #include #include #include #include #include #include /* VMS-related header files */ #include #include #include /* application header file */ #include #define boolean int #define true 1 #define false 0 #ifndef __VAX # pragma nomember_alignment #endif #define VMSok(x) ((x) & STS$M_SUCCESS) #define VMSnok(x) (!((x) & STS$M_SUCCESS)) #define DEFAULT_CHARSET "ISO-8859-1" #define HTML_BODY_TAG "" char Utility [] = "REPORTERROR"; boolean Debug, DoDefaultReport, DoLocalReport, HeaderOnly, HttpHasBeenOutput, IsCgiPlus, MethodHead, PragmaNoCache, ReportDetailed; int StatusClass, StatusCode; char *CgiEnvironmentPtr, *CgiFormErrorBasicPtr, *CgiFormErrorDigestPtr, *CgiFormErrorAboutPtr, *CgiFormErrorAbout2Ptr, *CgiFormErrorLinePtr, *CgiFormErrorModulePtr, *CgiFormErrorTextPtr, *CgiFormErrorText2Ptr, *CgiFormErrorStatusPtr, *CgiFormErrorStatusTextPtr, *CgiFormErrorStatusExplPtr, *CgiFormErrorTypePtr, *CgiFormErrorVmsPtr, *CgiHttpPragmaPtr, *CgiPathInfoPtr, *CgiPathTranslatedPtr, *CgiRequestMethodPtr, *CgiRequestTimeGmtPtr, *CgiScriptNamePtr, *CgiServerNamePtr, *CgiServerPortPtr, *CgiServerProtocolPtr, *CgiServerSignaturePtr, *CgiServerSoftwarePtr, *CgiTypePtr, *CharsetPtr, *CliCharsetPtr, *MoreInfoPtr = "", *PageFileNamePtr; char BasicChallenge [256], ContentTypeCharset [64], DigestChallenge [256], SoftwareID [48]; /*****************************************************************************/ /* */ main () { char *cptr; /*********/ /* begin */ /*********/ sprintf (SoftwareID, "%s (%s)", SOFTWAREID, CgiLibEnvironmentVersion()); if (getenv ("REPORTERROR$DBUG") != NULL) Debug = true; CgiLibEnvironmentSetDebug (Debug); CgiLibEnvironmentInit (0, NULL, false); GetParameters (); CgiLibResponseSetSoftwareID (SoftwareID); CgiLibResponseSetErrorMessage ("Reported by reportERROR"); IsCgiPlus = CgiLibEnvironmentIsCgiPlus (); /***********/ /* process */ /***********/ if (IsCgiPlus) { for (;;) { /* block waiting for next request */ CgiLibVar (""); ProcessRequest (); CgiLibCgiPlusEOF (); } } else ProcessRequest (); exit (SS$_NORMAL); } /*****************************************************************************/ /* */ ProcessRequest () { /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "ProcessRequest()\n"); CgiEnvironmentPtr = CgiLibEnvironmentName (); HttpHasBeenOutput = false; CgiHttpPragmaPtr = CgiLibVar ("WWW_HTTP_PRAGMA"); CgiPathInfoPtr = CgiLibVar ("WWW_PATH_INFO"); CgiPathTranslatedPtr = CgiLibVar ("WWW_PATH_TRANSLATED"); CgiRequestMethodPtr = CgiLibVar ("WWW_REQUEST_METHOD"); CgiRequestTimeGmtPtr = CgiLibVar ("WWW_REQUEST_TIME_GMT"); CgiScriptNamePtr = CgiLibVar ("WWW_SCRIPT_NAME"); CgiServerNamePtr = CgiLibVar ("WWW_SERVER_NAME"); CgiServerPortPtr = CgiLibVar ("WWW_SERVER_PORT"); CgiServerSignaturePtr = CgiLibVar ("WWW_SERVER_SIGNATURE"); CgiServerSoftwarePtr = CgiLibVar ("WWW_SERVER_SOFTWARE"); CgiServerProtocolPtr = CgiLibVar ("WWW_SERVER_PROTOCOL"); if ((CharsetPtr = CliCharsetPtr) == NULL) { CharsetPtr = CgiLibVar ("WWW_REQUEST_CHARSET"); if (!CharsetPtr[0]) CharsetPtr = CgiLibVar ("WWW_SERVER_CHARSET"); if (!CharsetPtr[0]) CharsetPtr = DEFAULT_CHARSET; } if (CharsetPtr[0]) sprintf (ContentTypeCharset, "; charset=%s", CharsetPtr); else ContentTypeCharset[0] = '\0'; if (!strcmp (CgiRequestMethodPtr, "HEAD")) MethodHead = true; else MethodHead = false; if (strsame (CgiHttpPragmaPtr, "no-cache", -1)) PragmaNoCache = true; else PragmaNoCache = true; CgiFormErrorBasicPtr = CgiLibVar ("WWW_FORM_ERROR_BASIC"); CgiFormErrorDigestPtr = CgiLibVar ("WWW_FORM_ERROR_DIGEST"); CgiFormErrorAboutPtr = CgiLibVar ("WWW_FORM_ERROR_ABOUT"); CgiFormErrorAbout2Ptr = CgiLibVar ("WWW_FORM_ERROR_ABOUT2"); CgiFormErrorLinePtr = CgiLibVar ("WWW_FORM_ERROR_LINE"); CgiFormErrorModulePtr = CgiLibVar ("WWW_FORM_ERROR_MODULE"); CgiFormErrorStatusPtr = CgiLibVar ("WWW_FORM_ERROR_STATUS"); CgiFormErrorStatusTextPtr = CgiLibVar ("WWW_FORM_ERROR_STATUS_TEXT"); CgiFormErrorStatusExplPtr = CgiLibVar ("WWW_FORM_ERROR_STATUS_EXPLANATION"); CgiFormErrorTextPtr = CgiLibVar ("WWW_FORM_ERROR_TEXT"); CgiFormErrorText2Ptr = CgiLibVar ("WWW_FORM_ERROR_TEXT2"); CgiFormErrorTypePtr = CgiLibVar ("WWW_FORM_ERROR_TYPE"); CgiFormErrorVmsPtr = CgiLibVar ("WWW_FORM_ERROR_VMS"); if (strsame (CgiFormErrorTypePtr, "detailed", -1)) ReportDetailed = true; else ReportDetailed = false; StatusCode = atoi(CgiFormErrorStatusPtr); StatusClass = StatusCode / 100; /* if it's an authorization challenge */ if (StatusCode == 401 || StatusCode == 407) { if (CgiFormErrorBasicPtr[0]) sprintf (BasicChallenge, "%s\r\n", CgiFormErrorBasicPtr); else BasicChallenge[0] = '\0'; if (CgiFormErrorDigestPtr[0]) sprintf (DigestChallenge, "%s\r\n", CgiFormErrorDigestPtr); else DigestChallenge[0] = '\0'; } else BasicChallenge[0] = DigestChallenge[0] = '\0'; if (HeaderOnly) HttpHeader (); else if (DoDefaultReport || strsame (CgiPathInfoPtr, "/default", -1)) DefaultReport (); else if (DoLocalReport || strsame (CgiPathInfoPtr, "/local", -1)) LocalReport (); else DefaultReport (); } /*****************************************************************************/ /* Output a text/html HTTP response header complete with 401 basic and/or digest authorization challenge if necessary. */ HttpHeader () { /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "HttpHeader()\n"); fprintf (stdout, "%s %s %s\r\n\ Server: %s\r\n\ Date: %s\r\n\ %s\ %s\ Content-Type: text/html%s\r\n\ \r\n", CgiServerProtocolPtr, CgiFormErrorStatusPtr, CgiFormErrorStatusPtr, CgiServerSoftwarePtr, CgiRequestTimeGmtPtr, BasicChallenge, DigestChallenge, ContentTypeCharset); HttpHasBeenOutput = true; } /*****************************************************************************/ /* This function will be called when the script is executed with /LOCAL qualifier. Place any locally implemented error reporter here. The DefaultReport() could be used as a template. */ LocalReport () { /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "LocalReport()\n"); /* of course remove this when a local reporter is implemented! */ DefaultReport (); } /*****************************************************************************/ /* Produce an error report that looks a lot like the WASD HTTPd native one! (This code is based laregly on that in CGILIB.C CgiLibresponseError()) */ DefaultReport () { /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "DefaultReport()\n"); HttpHeader (); if (MethodHead) return; fputs ( "\n\ \n", stdout); if (ReportDetailed) fprintf (stdout, "\n\ \n\ \n", CgiServerSoftwarePtr, SoftwareID, CgiFormErrorModulePtr, CgiFormErrorLinePtr); fprintf (stdout, "%s %s %s\n\ \n\ %s\n\ %s %s  -  %s\n", (StatusCode >= 200 && StatusCode < 299) ? "SUCCESS" : "ERROR", CgiFormErrorStatusPtr, CgiFormErrorStatusTextPtr, HTML_BODY_TAG, (StatusCode >= 200 && StatusCode < 299) ? "SUCCESS" : "ERROR", CgiFormErrorStatusPtr, CgiFormErrorStatusExplPtr); if (ReportDetailed) { fprintf (stdout, "

%s", CgiFormErrorTextPtr); if (CgiFormErrorAboutPtr[0]) fprintf (stdout, " ... %s\n", CgiFormErrorAboutPtr); else fputc ('\n', stdout); if (CgiFormErrorVmsPtr[0]) fprintf (stdout, "\n", CgiFormErrorVmsPtr, CgiFormErrorAbout2Ptr); if (CgiFormErrorText2Ptr[0]) fprintf (stdout, "%s\n", CgiFormErrorText2Ptr); } fprintf (stdout, "


\n"); if (CgiServerSignaturePtr[0]) fprintf (stdout, "%s\n", CgiServerSignaturePtr); fputs ( "\n\ \n", stdout); } /*****************************************************************************/ /* Get "command-line" parameters, whether from the command-line or from a configuration symbol or logical containing the equivalent. */ GetParameters () { static char CommandLine [256]; static unsigned long Flags = 0; int status; unsigned short Length; char ch; char *aptr, *cptr, *clptr, *sptr; $DESCRIPTOR (CommandLineDsc, CommandLine); /*********/ /* begin */ /*********/ if ((clptr = getenv ("REPORTERROR$PARAM")) == NULL) { /* get the entire command line following the verb */ if (VMSnok (status = lib$get_foreign (&CommandLineDsc, 0, &Length, &Flags))) exit (status); (clptr = CommandLine)[Length] = '\0'; } aptr = NULL; ch = *clptr; for (;;) { if (aptr != NULL) *aptr = '\0'; if (!ch) break; *clptr = ch; if (Debug) fprintf (stdout, "clptr |%s|\n", clptr); while (*clptr && isspace(*clptr)) *clptr++ = '\0'; aptr = clptr; if (*clptr == '/') clptr++; while (*clptr && !isspace (*clptr) && *clptr != '/') { if (*clptr != '\"') { clptr++; continue; } cptr = clptr; clptr++; while (*clptr) { if (*clptr == '\"') if (*(clptr+1) == '\"') clptr++; else break; *cptr++ = *clptr++; } *cptr = '\0'; if (*clptr) clptr++; } ch = *clptr; if (*clptr) *clptr = '\0'; if (Debug) fprintf (stdout, "aptr |%s|\n", aptr); if (!*aptr) continue; if (strsame (aptr, "/CHARSET=", 4)) { for (cptr = aptr; *cptr && *cptr != '='; cptr++); if (*cptr) cptr++; CliCharsetPtr = cptr; continue; } if (strsame (aptr, "/DBUG", -1)) { Debug = true; continue; } if (strsame (aptr, "/DEFAULT", 4)) { DoDefaultReport = true; continue; } if (strsame (aptr, "/HEADER", 4)) { HeaderOnly = true; continue; } if (strsame (aptr, "/LOCAL", 4)) { DoLocalReport = true; continue; } if (strsame (aptr, "/MORE=", 4)) { for (cptr = aptr; *cptr && *cptr != '='; cptr++); if (*cptr) cptr++; MoreInfoPtr = cptr; continue; } if (*aptr != '/') { fprintf (stdout, "%%%s-E-MAXPARM, too many parameters\n \\%s\\\n", Utility, aptr); exit (STS$K_ERROR | STS$M_INHIB_MSG); } else { fprintf (stdout, "%%%s-E-IVQUAL, unrecognized qualifier\n \\%s\\\n", Utility, aptr+1); exit (STS$K_ERROR | STS$M_INHIB_MSG); } } } /****************************************************************************/ /* Does a case-insensitive, character-by-character string compare and returns true if two strings are the same, or false if not. If a maximum number of characters are specified only those will be compared, if the entire strings should be compared then specify the number of characters as 0. */ boolean strsame ( char *sptr1, char *sptr2, int count ) { while (*sptr1 && *sptr2) { if (toupper (*sptr1++) != toupper (*sptr2++)) return (false); if (count) if (!--count) return (true); } if (*sptr1 || *sptr2) return (false); else return (true); } /*****************************************************************************/