/*****************************************************************************/ /* 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. 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 /* 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); } /*****************************************************************************/