/*****************************************************************************/ /* Error.c Error reporting functions. All errors are reported using HTML formatting. The assumptions being: o That most errors will occur before a document begins being transfered (e.g. file not found), and so its content type has not been set, and the error can sent with a "text/html" type. o HTML documents are probably more extensive than plain-text documents. Hence if an error occurs during transfer (e.g. record too big for user's buffer) it can be reported within the document with correct formatting (document context notwithstanding). o If an error occurs while transfering a plain-text document the HTML message is still readable, albeit a little cluttered. Additional, obvious text is included to try and highlight the message. o In non-"text/..." transfers the message text is not included anyway. After a request thread is created and an error is generated the storage 'rqptr->rqResponse.ErrorReportPtr' becomes non-NULL. This mechanism can be used to detect whether an error has occured. Most common errors create an error message in heap memory and point at this using 'rqptr->rqResponse.ErrorReportPtr'. This message can be output at some time convenient to the task underway. After an error message is sent to the client the 'rqptr->rqResponse.ErrorReportPtr' is returned to a NULL value to indicate no outstanding error (but usually the thread is disposed of after an error occurs). Notification of other, more critical errors are sent directly to the client at the time they occur (for instance, failed heap memory allocation). If ErrorGeneral() is called without first setting rqptr->rqResponse.HttpStatus it will default to 500, or "internal server error". Therefore string overflows and the like can be called without setting this code. Query string errors should set to 403 or 404 indication request cannot be processed. If ErrorVmsStatus() is called without first setting rqptr->rqResponse.HttpStatus the function will attempt to determine the most appropriate HTTP status code from the VMS status value. It will then default to 500, or "internal server error", indicating some unsual (e.g. non file system) condition. The server configuration can supply additional information included whenever an error report is generated. This includes a URL for contacting the system administrator (note: URL, not text, etc.) when an implementation, configuration, process quota, etc., error is reported. It also can provide text to be included against any other error report. This text can contain HTML markup, carriage control, etc., and is designed for a brief message containing a link to an explanation page. Error reporting can be performed by local (server-internal) redirection. This provides the opportunity for greater local-site control over content and format. When an error is redirected essential error information is encoded into a query-string formatted (URL-encoded) string. The redirection merely creates a new request with the redirection document (SSI or CGI script) as the path and this information as the query string. The reporting document environment can then use this information via the following CGI variables: FORM_ERROR_ABOUT item message is about (if applicable) FORM_ERROR_ABOUT2 additional information (if applicable) FORM_ERROR_BASIC basic authentication challenge FORM_ERROR_DIGEST digest authentication challenge FORM_ERROR_LINE name of source code line (e.g. "1732") FORM_ERROR_MODULE name of source code module (e.g. "REQUEST") FORM_ERROR_REALM authentication realm FORM_ERROR_STATUS HTTP status code (e.g. "404") FORM_ERROR_STATUS_TEXT text meaning of above status code (e.g. "Not Found") FORM_ERROR_STATUS_EXPLANATION more detailed explanation of above status code FORM_ERROR_TEXT server-generated error message FORM_ERROR_TEXT2 server suggestion about what to do about it :^) FORM_ERROR_TYPE "basic" or "detailed" FORM_ERROR_URI URI of original request FORM_ERROR_VMS VMS status value in decimal (if applicable) VERSION HISTORY --------------- 22-SEP-2020 MGD bugfix; ErrorReportFooter() use request heap for signature 20-JAN-2019 MGD ErrorNoticed() extended to allow $FAO formatting 15-JUN-2019 MGD ErrorNoticed() tweak NULL explanation from "" to "(none)" 30-JUN-2016 MGD ErrorRedirectQueryString() ERROR_URI variable 29-APR-2015 MGD bugfix; Error..() earlier and broader detection of WebDAV 10-MAY-2010 JPP bugfix; do not use REDIRECT for WebDAV request error report 18-MAY-2008 MGD bugfix; ErrorNoticed() use of 'rqptr' (from 16-NOV-2007) 16-NOV-2007 MGD ErrorNoticed() add script/CGI data to OPCOM message 09-JUN-2007 MGD use STR_DSC 17-SEP-2006 MGD ErrorExitVmsStatus() add '-HTTPD-E-WHEN, ' add '-HTTPD-E-STATUS, %X99999999' and restrict exit with error status to the instance supervisor (as applicable) to restrict image restart to the supervisor instance 16-JUL-2005 MGD add selected request data to ErrorNoticed() report 14-JUL-2005 MGD provide "Allow:" header with 405 (method not supported) 11-JUN-2005 MGD allow report path to exclude using negative codes 12-AUG-2004 MGD simple tunnel suitable messages 02-AUG-2004 MGD error reports now have the response header generated using the report buffer length, where possible, as a content length (allowing connections to persist) 01-JUN-2004 MGD detailed report explicitly set by mapping rule 13-JAN-2004 MGD bugfix; ErrorGeneral() always get module name and number 26-AUG-2003 MGD remove suppression of detail for success messages 10-JUN-2003 MGD SS$_BAD.. moved from 500 to 403 response 01-MAR-2003 MGD set html= header and footer 12-OCT-2002 MGD tweak RMS error HTTP status slightly 16-SEP-2002 MGD rqPathSet.Report4NNas 04-SEP-2002 MGD add end-of-tag markup to the divider in ErrorSendToClient() 04-AUG-2001 MGD support module WATCHing 16-FEB-2001 MGD add service-based error report status code handling 17-JAN-2001 MGD bugfix; "!&%AZ" formatting for basic/digest challenge 03-DEC-2000 MGD bugfix; ErrorGeneral() 23-NOV-2000 MGD bugfix; ErrorRedirectQueryString() 08-APR-2000 MGD rework error functions (including redirect) 29-MAR-2000 MGD bugfix; ErrorExitVmsStatus() line number from !AZ to !UL 04-MAR-2000 MGD use FaoToBuffer/l(), add ErrorNoticed(), ErrorRedirectQueryString() 04-DEC-1999 MGD divider handling 02-JAN-1999 MGD proxy service 18-OCT-1998 MGD error report redirection 30-AUG-1997 MGD 401 without a realm is now converted into a 403 09-AUG-1997 MGD message database, with considerable rewrite! 01-FEB-1997 MGD HTTPd version 4 06-JUN-1996 MGD added authentication failure 01-DEC-1995 MGD HTTPd version 3 25-MAR-1995 MGD minor functional and cosmetic changes 20-DEC-1994 MGD multi-threaded daemon */ /*****************************************************************************/ #ifdef WASD_VMS_V7 #undef _VMS__V6__SOURCE #define _VMS__V6__SOURCE #undef __VMS_VER #define __VMS_VER 70000000 #undef __CRTL_VER #define __CRTL_VER 70000000 #endif /* standard C header files */ #include #include #include #include /* VMS related header files */ #include #include #include #include /* application related header files */ #include "wasd.h" #define WASD_MODULE "ERROR" /******************/ /* global storage */ /******************/ $DESCRIPTOR (ErrorNumberFaoDsc, "!UL\0"); $DESCRIPTOR (ErrorVmsStatusFaoDsc, "!&S\0"); BOOL ErrorStatusReportFormatAbi; int ErrorsNoticedCount; int64 ErrorsNoticedTime64; char ErrorSanityCheck [] = "sanity check failure!", ErrorServerFao [] = "significant HTTPd sys$fao() failed.", ErrorSslFailure [] = "secure connection failure", ErrorXvalNotValid [] = "reverting to pre-V8.2 locking,"; char *ErrorRecommendNotifyPtr; /********************/ /* external storage */ /********************/ #ifdef DBUG extern BOOL Debug; #else #define Debug 0 #endif extern BOOL InstanceNodeSupervisor, LoggingEnabled; extern int InstanceNodeConfig, OpcomMessages; extern int64 HttpdTime64; extern int ToLowerCase[], ToUpperCase[]; extern char HttpProtocol[], ResponseAllowResource[], ResponseAllowServer[], SoftwareID[]; extern CONFIG_STRUCT Config; extern MSG_STRUCT Msgs; extern WATCH_STRUCT Watch; /*****************************************************************************/ /* Check that the quasi-FAO directives expected in a possibly user-configured report format are those that are expected, and no more/no less. This obviously provides some pre-runtime integrity confidence, reducing the chance of a server ACCVIO or some such. */ void ErrorCheckReportFormats () { static struct { char *ReportName; char *CheckString; char *ReportFormat; } FormatCheck [] = { { "Report Format", "!AZ!AZ!UL!AZ!**!AZ!UL!AZ!&@!&@!&@", NULL }, { "Signature Format", "!AZ", NULL }, { "Server Signature", "!AZ!AZ!AZ!UL", NULL }, { NULL, NULL, NULL, } }; BOOL ErrorReported; int idx; char *cptr, *sptr; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD__OTHER)) WatchThis (WATCHALL, WATCH_MOD__OTHER, "ErrorCheckReportFormats()"); FormatCheck[0].ReportFormat = MsgFor(NULL,MSG_STATUS_REPORT_FORMAT); FormatCheck[1].ReportFormat = MsgFor(NULL,MSG_STATUS_REPORT_FORMAT_SIG); FormatCheck[2].ReportFormat = MsgFor(NULL,MSG_STATUS_SIGNATURE); ErrorReported = false; for (idx = 0; FormatCheck[idx].ReportName; idx++) { cptr = FormatCheck[idx].ReportFormat; sptr = FormatCheck[idx].CheckString; while (*cptr) { while (*cptr && *cptr != '!') cptr++; if (SAME2(cptr,'!!')) { cptr += 2; continue; } if (*sptr != '!') break; if (SAME2(cptr,'!+')) { sptr++; if (SAME2(sptr,'AZ') || SAME2(sptr,'UL') || SAME2(sptr,'&@')) { cptr += 2; sptr += 2; continue; } break; } if (*cptr == '!' && SAME2(cptr+1,'%%') && *sptr == '!' && SAME2(sptr+1,'&@')) { /* '%%' as <-7.3 backward compatible with 7.3-> '&@' */ cptr += 3; sptr += 3; continue; } if ((*cptr == '!' && SAME2(cptr+1,'AZ') || *cptr == '!' && SAME2(cptr+1,'%%') || *cptr == '!' && SAME2(cptr+1,'&@')) && *sptr == '!' && SAME2(sptr+1,'**')) { /* allow either "!AZ" or "!%%" in report format string */ cptr += 3; sptr += 3; continue; } while (*sptr && *cptr && *sptr == *cptr) { cptr++; sptr++; if (*sptr == '!') break; } if (*sptr && *sptr != '!') break; } if (*cptr && *sptr) sptr = "problem detected"; else if (*sptr) sptr = "too few directives"; else if (*cptr) sptr = "too many directives"; else sptr = ""; if (*sptr) { FaoToStdout ("%HTTPD-E-MSG, !AZ in \"!AZ\"\n \\!AZ\\\n", sptr, FormatCheck[idx].ReportName, *cptr ? cptr : "(end-of-string)"); ErrorReported = true; } } if (ErrorReported) { if (OpcomMessages & OPCOM_HTTPD) FaoToOpcom ("%HTTPD-E-MSG, problem(s) with reports format"); exit (SS$_ABORT); } /* check whether the fifth directive is a !AZ (<-8.2) or !%%/!&@ (8.2->) */ cptr = FormatCheck[0].ReportFormat; for (idx = 0; idx < 5; idx++) { while (*cptr && *cptr != '!') cptr++; if (*cptr == '!') cptr++; } if (SAME2(cptr,'%%') || SAME2(cptr,'&@')) ErrorStatusReportFormatAbi = true; if (WATCH_MODULE(WATCH_MOD__OTHER)) WatchThis (WATCHALL, WATCH_MOD__OTHER, "!&B !&Z", ErrorStatusReportFormatAbi, cptr); } /*****************************************************************************/ /* Exit the application after output of the module and line location reporting the error, a brief explanation, and then exiting generating a DCL-reported message. */ void ErrorExitVmsStatus ( int StatusValue, char *ExplanationPtr, char *SourceModule, int SourceLine ) { /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD__OTHER)) WatchThis (WATCHALL, WATCH_MOD__OTHER, "ErrorExitVmsStatus()"); if (!ExplanationPtr) ExplanationPtr = "(null)"; FaoToStdout ( "%HTTPD-F-SOFTWAREID, !AZ\n\ -HTTPD-F-WHEN, !%D\n\ -HTTPD-F-WHERE, module:!AZ line:!UL\n\ -HTTPD-F-WHAT, !AZ\n", SoftwareID, 0, SourceModule, SourceLine, ExplanationPtr); exit (StatusValue | STS$M_INHIB_MSG); } /*****************************************************************************/ /* Report to the server process log, and if enabled to OPCOM, a non-fatal error that has been noticed. Add selected request data to the process log in an effort to provide additional, pertinent information related to the report. */ void ErrorNoticed ( REQUEST_STRUCT *rqptr, int StatusValue, char *FaoPtr, char *SourceModule, int SourceLine, ... ) { int argcnt, hlen, llen, tlen, status; ushort slen; ulong *vecptr; ulong FaoVector [32]; char *cptr, *sptr; char Buffer [4096]; va_list argptr; /*********/ /* begin */ /*********/ va_count (argcnt); if (WATCHMOD (rqptr, WATCH_MOD__OTHER)) WatchThis (WATCHITM(rqptr), WATCH_MOD__OTHER, "ErrorNoticed() !UL", argcnt); ErrorsNoticedCount++; ErrorsNoticedTime64 = HttpdTime64; if (argcnt > 5 + 32) { ErrorNoticed (rqptr, SS$_OVRMAXARG, "!AZ", FI_LI, FaoPtr); return; } if (!FaoPtr) FaoPtr = "(none)"; if (argcnt > 5) { vecptr = FaoVector; va_start (argptr, SourceLine); for (argcnt -= 5; argcnt; argcnt--) *vecptr++ = va_arg (argptr, ulong); va_end (argptr); status = FaolToBuffer (Buffer, sizeof(Buffer), &slen, FaoPtr, &FaoVector); if (VMSnok (status) || status == SS$_BUFFEROVF) { ErrorNoticed (rqptr, status, "!AZ", FI_LI, FaoPtr); return; } FaoPtr = Buffer; } if (StatusValue) { if (WATCH_CAT && Watch.Category) { WatchThis (WATCHALL, WATCH_INTERNAL, "!AZ:!UL, !AZ!AZ%X!XL", SourceModule, SourceLine, FaoPtr, FaoPtr[0] ? " " : "", StatusValue); WatchDataFormatted ("%!&M\n", StatusValue); } FaoToStdout ( "%HTTPD-W-NOTICED, !20%D, !AZ:!UL, !AZ!AZ%X!XL\n-!-!&M\n", 0, SourceModule, SourceLine, FaoPtr, FaoPtr[0] ? " " : "", StatusValue); FaoToOpcom ( "%HTTPD-W-NOTICED, !AZ:!UL, !AZ!AZ%X!XL\r\n-!-!&M", SourceModule, SourceLine, FaoPtr, FaoPtr[0] ? " " : "", StatusValue); } else { if (WATCH_CAT && Watch.Category) WatchThis (WATCHALL, WATCH_INTERNAL, "!AZ:!UL, !AZ", SourceModule, SourceLine, FaoPtr); FaoToStdout ("%HTTPD-W-NOTICED, !20%D, !AZ:!UL, !AZ\n", 0, SourceModule, SourceLine, FaoPtr); if (rqptr && rqptr->ScriptName[0]) { if (rqptr->rqCgi.HeaderPtr && rqptr->rqCgi.HeaderPtr[0]) { for (sptr = cptr = rqptr->rqCgi.HeaderPtr; *sptr; sptr++); tlen = hlen = sptr - cptr; if (hlen > 24) hlen = 24; sptr = cptr; /* in case there is leading carriage-control */ if (*sptr == '\r') sptr++; if (*sptr == '\n') sptr++; while (*sptr && *sptr != '\n') sptr++; llen = sptr - cptr; if (llen < hlen) llen = hlen; FaoToBuffer (Buffer, sizeof(Buffer), 0, "!AZ\r\n\ -HTTPD-I-SCRIPT, !AZ (!AZ) !AZ\r\n\ -HTTPD-I-CGI, !#&H (!UL bytes) !#&.AZ", FaoPtr, rqptr->ScriptName, rqptr->RequestMappedScript, rqptr->RequestMappedRunTime, hlen, cptr, tlen, llen, cptr); FaoPtr = Buffer; } } FaoToOpcom ("%HTTPD-W-NOTICED, !AZ:!UL, !AZ", SourceModule, SourceLine, FaoPtr); } if (!rqptr) return; if (rqptr->RequestState >= REQUEST_STATE_SHUTDOWN) return; /****************/ /* request data */ /****************/ if (rqptr->DclTaskPtr && rqptr->DclTaskPtr->NextTaskFunction == &DclScriptProctorAst) FaoToStdout ("-NOTICED-I-PROCTOR, !UL+!UL !AZ!AZ!AZ!AZ !AZ!AZ!AZ\n", rqptr->DclTaskPtr->ProctorPtr->NumberMin, rqptr->DclTaskPtr->ProctorPtr->NumberIdle, *rqptr->DclTaskPtr->ProctorPtr->RunTimePtr ? "(" : "", rqptr->DclTaskPtr->ProctorPtr->RunTimePtr, *rqptr->DclTaskPtr->ProctorPtr->RunTimePtr ? ")" : "", rqptr->DclTaskPtr->ProctorPtr->ScriptPtr, rqptr->DclTaskPtr->ProctorPtr->ActivatePtr, *rqptr->DclTaskPtr->ProctorPtr->NotePadPtr ? " " : "", rqptr->DclTaskPtr->ProctorPtr->NotePadPtr); if (rqptr->ServicePtr && rqptr->ServicePtr->RequestSchemeNamePtr) FaoToStdout ("-NOTICED-I-SERVICE, !AZ//!AZ\n", rqptr->ServicePtr->RequestSchemeNamePtr, rqptr->ServicePtr->ServerHostPort); if (rqptr->ClientPtr && &rqptr->ClientPtr->IpAddressString[0]) FaoToStdout ("-NOTICED-I-CLIENT, !AZ\n", ClientHostString(rqptr)); FaoToStdout ("-NOTICED-I-HTTP, !UL\n", rqptr->Http2Stream.Http2Ptr ? 2 : 1); if (rqptr->rqHeader.RequestUriPtr && rqptr->rqHeader.RequestUriLength) FaoToStdout ("-NOTICED-I-URI, !AZ (!UL bytes) !AZ\n", rqptr->rqHeader.MethodName, rqptr->rqHeader.RequestUriLength, rqptr->rqHeader.RequestUriPtr); if (rqptr->FileTaskPtr) if (rqptr->FileTaskPtr->FileNameLength) FaoToStdout ("-NOTICED-I-FILE, !#AZ\n", rqptr->FileTaskPtr->FileNameLength, rqptr->FileTaskPtr->FileName); if (rqptr->ScriptName[0]) { FaoToStdout ("-NOTICED-I-SCRIPT, !AZ !AZ (!AZ) !AZ\n", rqptr->ScriptName, rqptr->RequestMappedScript, rqptr->RequestMappedRunTime, rqptr->rqCgi.ScriptFileNamePtr ? rqptr->rqCgi.ScriptFileNamePtr : ""); if (rqptr->rqCgi.HeaderPtr && rqptr->rqCgi.HeaderPtr[0]) { for (sptr = cptr = rqptr->rqCgi.HeaderPtr; *sptr; sptr++); tlen = hlen = sptr - cptr; if (hlen > 24) hlen = 24; sptr = cptr; /* in case there is leading carriage-control */ if (*sptr == '\r') sptr++; if (*sptr == '\n') sptr++; while (*sptr && *sptr != '\n') sptr++; llen = sptr - cptr; if (llen < hlen) llen = hlen; FaoToStdout ("-NOTICED-I-CGI, !#&H (!UL bytes) !#&.AZ\n", hlen, cptr, tlen, llen, cptr); } } FaoToStdout ("-NOTICED-I-RXTX, err:!UL/!UL raw:!@SQ/!@SQ net:!@SQ/!@SQ\n", rqptr->rqNet.ReadErrorCount, rqptr->rqNet.WriteErrorCount, &rqptr->NetIoPtr->BytesRawRx64, &rqptr->NetIoPtr->BytesRawTx64, &rqptr->BytesRx64, &rqptr->BytesTx64); } /****************************************************************************/ /* It is a fatal error to call this function without an error message. If a response header and output has already been generated insert a plain-text and HTML obvious divider into the output (hopefully it's text ... it doesn't really matter anyway, it's an error condition after all :^) and then send the actual error message. If no response has yet been made then either redirect error processing appropriately (using the query-string format error information generated by ErrorVmsStatus() and ErrorGeneral()), or generate an response header then send the message. */ void ErrorSendToClient (REQUEST_STRUCT *rqptr) { /* contains HTML, also enough to make it obvious in plain text */ static char DividerFao [] = "\ \n\


\n\ \n\ \n\ \n"; static int DividerLength; static char *DividerPtr; int status, ContentLength; ushort Length; ulong *vecptr; ulong FaoVector [32]; char *cptr, *sptr, *ResponseAllowPtr; char Buffer [1024]; /*********/ /* begin */ /*********/ if (WATCHMOD (rqptr, WATCH_MOD__OTHER)) WatchThis (WATCHITM(rqptr), WATCH_MOD__OTHER, "ErrorSendToClient()"); if (!rqptr->rqResponse.ErrorReportPtr) ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, FI_LI); if (rqptr->rqPathSet.ReportType == ERROR_REPORT_TUNNEL || rqptr->ServicePtr->ProxyTunnel == PROXY_TUNNEL_FIREWALL || rqptr->ServicePtr->ProxyTunnel == PROXY_TUNNEL_RAW) { /******************/ /* tunnel message */ /******************/ if (rqptr->rqPathSet.ReportType == ERROR_REPORT_TUNNEL) ResponseHeader (rqptr, rqptr->rqResponse.HttpStatus, "text/plain", -1, NULL, NULL); /* errors are always sent as the request is finalized */ NetWrite (rqptr, &RequestEnd, rqptr->rqResponse.ErrorReportPtr, rqptr->rqResponse.ErrorReportLength); /* indicate the error message has been sent */ rqptr->rqResponse.ErrorReportPtr = NULL; rqptr->rqResponse.ErrorReportLength = 0; return; } if (rqptr->rqResponse.HeaderGenerated && STR_DSC_PTR(&rqptr->NetWriteBufferDsc)) { /*******************/ /* provide divider */ /*******************/ if (!DividerPtr) { status = FaoToBuffer (Buffer, sizeof(Buffer), &Length, DividerFao, MsgFor(rqptr,MSG_STATUS_ERROR)); if (VMSnok (status) || status == SS$_BUFFEROVF) ErrorNoticed (rqptr, status, NULL, FI_LI); DividerPtr = VmGet (Length+1); memcpy (DividerPtr, Buffer, (DividerLength=Length)+1); } NetWrite (rqptr, &ErrorMessageDividerAst, DividerPtr, DividerLength); return; } if (rqptr->rqResponse.ErrorReportByRedirect) { /******************************/ /* redirect to error reporter */ /******************************/ /* log the original request */ if (LoggingEnabled) Logging (rqptr, LOGGING_ENTRY); status = FaoToBuffer (Buffer, sizeof(Buffer), &Length, rqptr->ServicePtr->ErrorReportPath, rqptr->rqResponse.HttpStatus); if (VMSnok (status) || status == SS$_BUFFEROVF) ErrorNoticed (rqptr, status, NULL, FI_LI); /* combine the redirection path and the error query string */ Length += rqptr->rqResponse.ErrorReportLength; /* reserve space in the dictionary then build in situ */ sptr = ResponseLocation (rqptr, NULL, Length); for (cptr = Buffer; *cptr; *sptr++ = *cptr++); for (cptr = rqptr->rqResponse.ErrorReportPtr; *cptr; *sptr++ = *cptr++); /* indicate the error message has been sent */ rqptr->rqResponse.ErrorReportPtr = NULL; rqptr->rqResponse.ErrorReportLength = 0; /* indicate error redirection in this thread-permanent storage */ rqptr->RedirectErrorStatusCode = rqptr->rqResponse.HttpStatus; rqptr->RedirectErrorAuthRealmDescrPtr = rqptr->rqAuth.RealmDescrPtr; /* errors are always sent as the request is finalized, continue that */ RequestEnd (rqptr); return; } /***********************/ /* provide HTTP header */ /***********************/ if (!rqptr->rqResponse.HeaderGenerated && !rqptr->rqResponse.HeaderSent) ContentLength = rqptr->rqResponse.ErrorReportLength; else ContentLength = -1; if (rqptr->rqResponse.HttpStatus == 405) ResponseAllowPtr = ResponseAllowResource; else ResponseAllowPtr = ""; ResponseHeader (rqptr, rqptr->rqResponse.HttpStatus, "text/html", ContentLength, NULL, ResponseAllowPtr); /* errors are always sent as the request is finalized */ NetWrite (rqptr, &RequestEnd, rqptr->rqResponse.ErrorReportPtr, rqptr->rqResponse.ErrorReportLength); /* indicate the error message has been sent */ rqptr->rqResponse.ErrorReportPtr = NULL; rqptr->rqResponse.ErrorReportLength = 0; } /*****************************************************************************/ /* Divider delivered, now output the error report. */ void ErrorMessageDividerAst (REQUEST_STRUCT *rqptr) { int status; /*********/ /* begin */ /*********/ if (WATCHMOD (rqptr, WATCH_MOD__OTHER)) WatchThis (WATCHITM(rqptr), WATCH_MOD__OTHER, "ErrorMessageDividerAst() !&F", &ErrorMessageDividerAst); /* errors are always sent as the request is finalized, continue that */ NetWrite (rqptr, &RequestEnd, rqptr->rqResponse.ErrorReportPtr, rqptr->rqResponse.ErrorReportLength); /* indicate the error message has been sent */ rqptr->rqResponse.ErrorReportPtr = NULL; rqptr->rqResponse.ErrorReportLength = 0; } /*****************************************************************************/ /* Set html= body and/or header. */ void ErrorReportHeader ( REQUEST_STRUCT *rqptr, ulong **vecptrptr ) { ulong *vecptr; /*********/ /* begin */ /*********/ if (WATCHMOD (rqptr, WATCH_MOD__OTHER)) WatchThis (WATCHITM(rqptr), WATCH_MOD__OTHER, "ErrorReportHeader()"); vecptr = *vecptrptr; if (ErrorStatusReportFormatAbi) { if (rqptr->rqPathSet.HtmlBodyTagPtr) { if (rqptr->rqPathSet.HtmlBodyTagPtr[0] == '<') *vecptr++ = "!AZ!&@"; else *vecptr++ = "\n!&@"; *vecptr++ = rqptr->rqPathSet.HtmlBodyTagPtr; } else if (rqptr->ServicePtr->BodyTag[0]) { *vecptr++ = "!AZ!&@"; *vecptr++ = rqptr->ServicePtr->BodyTag; } else { *vecptr++ = "!AZ!&@"; *vecptr++ = REPORT_BODY_TAG; } if (rqptr->rqPathSet.HtmlHeaderPtr || rqptr->rqPathSet.HtmlHeaderTagPtr) { if (rqptr->rqPathSet.HtmlHeaderTagPtr && rqptr->rqPathSet.HtmlHeaderTagPtr[0] == '<') *vecptr++ = "!AZ\n!&@"; else *vecptr++ = "\n!&@"; *vecptr++ = rqptr->rqPathSet.HtmlHeaderTagPtr; *vecptr++ = "!AZ

"; *vecptr++ = rqptr->rqPathSet.HtmlHeaderPtr; } else *vecptr++ = ""; } else if (rqptr->ServicePtr->BodyTag[0]) *vecptr++ = rqptr->ServicePtr->BodyTag; else *vecptr++ = REPORT_BODY_TAG; *vecptrptr = vecptr; } /*****************************************************************************/ /* Set html= body and/or header. */ void ErrorReportEndHeader ( REQUEST_STRUCT *rqptr, ulong **vecptrptr ) { ulong *vecptr; /*********/ /* begin */ /*********/ if (WATCHMOD (rqptr, WATCH_MOD__OTHER)) WatchThis (WATCHITM(rqptr), WATCH_MOD__OTHER, "ErrorReportEndHeader()"); vecptr = *vecptrptr; if (ErrorStatusReportFormatAbi && (rqptr->rqPathSet.HtmlHeaderPtr || rqptr->rqPathSet.HtmlHeaderTagPtr)) *vecptr++ = "

\n!&@"; *vecptrptr = vecptr; } /*****************************************************************************/ /* Set html= footer. */ void ErrorReportFooter ( REQUEST_STRUCT *rqptr, ulong **vecptrptr ) { #define SIG_SIZE 256 ulong *vecptr; char *sptr; /*********/ /* begin */ /*********/ if (WATCHMOD (rqptr, WATCH_MOD__OTHER)) WatchThis (WATCHITM(rqptr), WATCH_MOD__OTHER, "ErrorReportFooter()"); if (Config.cfServer.Signature) sptr = VmGetHeap (rqptr, SIG_SIZE); vecptr = *vecptrptr; if (ErrorStatusReportFormatAbi && (rqptr->rqPathSet.HtmlFooterPtr || rqptr->rqPathSet.HtmlFooterTagPtr)) { if (rqptr->rqPathSet.HtmlFooterTagPtr && rqptr->rqPathSet.HtmlFooterTagPtr[0] == '<') *vecptr++ = "\n

!AZ\n!&@"; else *vecptr++ = "\n

\n!&@"; *vecptr++ = rqptr->rqPathSet.HtmlFooterTagPtr; if (Config.cfServer.Signature) { *vecptr++ = "!AZ\n

!&@"; *vecptr++ = ServerSignature (rqptr, sptr, SIG_SIZE); } else *vecptr++ = "!&@"; *vecptr++ = "!AZ\n

"; *vecptr++ = rqptr->rqPathSet.HtmlFooterPtr; } else if (Config.cfServer.Signature) { *vecptr++ = "\n!&@"; *vecptr++ = MsgFor(rqptr,MSG_STATUS_REPORT_FORMAT_SIG); *vecptr++ = ServerSignature (rqptr, sptr, SIG_SIZE); } else *vecptr++ = ""; *vecptrptr = vecptr; } /*****************************************************************************/ /* Yes! Report success from the error module. Most consistant place to put it. Perhaps the ERROR module should be renamed to REPORT! */ void ErrorReportSuccess ( REQUEST_STRUCT *rqptr, char *ExplanationPtr, ... ) { int status, argcnt, StatusCode; ushort Length; ulong *vecptr; ulong FaoVector [32]; char *cptr, *sptr, *zptr, *CategoryPtr, *ReportPtr; char Buffer [4096]; va_list argptr; /*********/ /* begin */ /*********/ va_count (argcnt); if (WATCHMOD (rqptr, WATCH_MOD__OTHER)) WatchThis (WATCHITM(rqptr), WATCH_MOD__OTHER, "ErrorReportSuccess() !UL", argcnt); if (argcnt > 16) { ErrorNoticed (rqptr, SS$_OVRMAXARG, NULL, FI_LI); return; } if (!rqptr->rqResponse.HttpStatus) rqptr->rqResponse.HttpStatus = 200; StatusCode = rqptr->rqResponse.HttpStatus; /* HTTP status 201 inhibits the response informational message (in at least Netscape Navigator 3.0), so let's revert to 200! */ if (rqptr->rqResponse.HttpStatus == 201) rqptr->rqResponse.HttpStatus = 200; if (WATCHING (rqptr, WATCH_RESPONSE)) { cptr = "default"; switch (rqptr->rqPathSet.ReportType) { case ERROR_REPORT_BASIC : cptr = "basic"; break; case ERROR_REPORT_DETAILED : cptr = "detailed"; break; case ERROR_REPORT_TUNNEL : cptr = "tunnel"; break; default: if (Config.cfReport.BasicOnly) cptr = "basic-only"; } WatchThis (WATCHITM(rqptr), WATCH_RESPONSE, "SUCCESS (!AZ) !3ZL \"!AZ\"", cptr, StatusCode, ExplanationPtr); } vecptr = FaoVector; *vecptr++ = ""; *vecptr++ = CategoryPtr = MsgFor(rqptr,MSG_STATUS_SUCCESS); *vecptr++ = StatusCode; *vecptr++ = HttpStatusCodeText(StatusCode); ErrorReportHeader (rqptr, &vecptr); *vecptr++ = CategoryPtr; *vecptr++ = StatusCode; *vecptr++ = HttpStatusCodeExplanation (rqptr,StatusCode); ErrorReportEndHeader (rqptr, &vecptr); *vecptr++ = "

!&@"; /* a leading dollar indicates a server administration report */ if (ExplanationPtr[0] == '$') *vecptr++ = ExplanationPtr+1; else *vecptr++ = ExplanationPtr; /* now add the variable length arguments to the vector */ va_start (argptr, ExplanationPtr); for (argcnt -= 2; argcnt; argcnt--) *vecptr++ = va_arg (argptr, ulong); va_end (argptr); *vecptr++ = MsgFor(rqptr,MSG_STATUS_INFO); ErrorReportFooter (rqptr, &vecptr); ReportPtr = MsgFor(rqptr,MSG_STATUS_REPORT_FORMAT); status = FaolToBuffer (Buffer, sizeof(Buffer), &Length, ReportPtr, &FaoVector); if (VMSnok (status) || status == SS$_BUFFEROVF) ErrorExitVmsStatus (status, ErrorServerFao, FI_LI); ResponseHeader (rqptr, 200, "text/html", Length, NULL, NULL); /* dollar indicates a server administration report, write immediately! */ if (ExplanationPtr[0] == '$') NetWrite (rqptr, NULL, Buffer, Length); else NetWriteBuffered (rqptr, NULL, Buffer, Length); } /*****************************************************************************/ /* Generate an error message about a general (non-VMS status) problem for subsequent reporting to the client. If error redirection applies to the request then generate the error information in a form suitable for provision as a request query-string. */ void ErrorGeneral ( REQUEST_STRUCT *rqptr, char *ExplanationPtr, ... ) { BOOL StandardReport; int idx, status, argcnt, OriginalStatusCode, SourceLine, StatusCode; ushort Length; ulong *vecptr; ulong FaoVector [32]; char *cptr, *sptr, *zptr, *CategoryPtr, *ReportPtr, *SourceModule; char Buffer [4096]; va_list argptr; /*********/ /* begin */ /*********/ va_count (argcnt); if (WATCHMOD (rqptr, WATCH_MOD__OTHER)) WatchThis (WATCHITM(rqptr), WATCH_MOD__OTHER, "ErrorGeneral() !UL", argcnt); if (argcnt > 16) { ErrorNoticed (rqptr, SS$_OVRMAXARG, NULL, FI_LI); return; } /* don't overwrite any existing error message */ if (ERROR_REPORTED (rqptr)) return; /* not if WebDAV is involved */ if (rqptr->WebDavRequest || rqptr->WhiffOfWebDav || rqptr->WebDavTaskPtr) rqptr->rqResponse.ErrorReportByRedirect = false; /* if this request is processing an error report redirect */ if (rqptr->rqResponse.ErrorReportByRedirect && rqptr->RedirectErrorStatusCode) { /* problem processing the error report, no infinite loops here please! */ rqptr->rqResponse.ErrorReportByRedirect = false; rqptr->RedirectErrorStatusCode = 0; rqptr->RedirectErrorAuthRealmDescrPtr = NULL; } if (rqptr->rqResponse.HeaderGenerated && STR_DSC_PTR(&rqptr->NetWriteBufferDsc)) { /* some output has already occured, internal error reporting */ rqptr->rqResponse.ErrorReportByRedirect = false; } OriginalStatusCode = rqptr->rqResponse.HttpStatus; if (!rqptr->rqResponse.HttpStatus) rqptr->rqResponse.HttpStatus = 500; /* when mapping error status by implication its a basic report! */ if ((rqptr->rqResponse.HttpStatus == 400 || rqptr->rqResponse.HttpStatus == 409) && rqptr->rqPathSet.Report400as) { rqptr->rqResponse.HttpStatus = rqptr->rqPathSet.Report400as; rqptr->rqPathSet.ReportType = ERROR_REPORT_BASIC; } else if ((rqptr->rqResponse.HttpStatus == 403 || rqptr->rqResponse.HttpStatus == 409) && rqptr->rqPathSet.Report403as) { rqptr->rqResponse.HttpStatus = rqptr->rqPathSet.Report403as; rqptr->rqPathSet.ReportType = ERROR_REPORT_BASIC; } else if ((rqptr->rqResponse.HttpStatus == 404 || rqptr->rqResponse.HttpStatus == 409) && rqptr->rqPathSet.Report404as) { rqptr->rqResponse.HttpStatus = rqptr->rqPathSet.Report404as; rqptr->rqPathSet.ReportType = ERROR_REPORT_BASIC; } StatusCode = rqptr->rqResponse.HttpStatus; if (StatusCode == 401 || StatusCode == 407) ResponseHeaderChallenge (rqptr); /* get the reporting module name and line number */ va_start (argptr, ExplanationPtr); for (argcnt -= 2; argcnt; argcnt--) { /* the last two arguments must be the macro "FI_LI" */ if (argcnt == 2) SourceModule = va_arg (argptr, ulong); else if (argcnt == 1) SourceLine = va_arg (argptr, ulong); else va_arg (argptr, ulong); } va_end (argptr); if (WATCHING (rqptr, WATCH_ERROR)) { cptr = "default"; switch (rqptr->rqPathSet.ReportType) { case ERROR_REPORT_BASIC : cptr = "basic"; break; case ERROR_REPORT_DETAILED : cptr = "detailed"; break; case ERROR_REPORT_TUNNEL : cptr = "tunnel"; break; default: if (Config.cfReport.BasicOnly) cptr = "basic-only"; } WatchThis (WATCHITM(rqptr), WATCH_RESPONSE, "!AZ:!UL (!AZ) !3ZL(!3ZL) \"!AZ\"", SourceModule, SourceLine, cptr, StatusCode, OriginalStatusCode, ExplanationPtr); } if (rqptr->rqPathSet.ReportType == ERROR_REPORT_TUNNEL || rqptr->ServicePtr->ProxyTunnel == PROXY_TUNNEL_FIREWALL || rqptr->ServicePtr->ProxyTunnel == PROXY_TUNNEL_RAW) { /******************/ /* tunnel message */ /******************/ va_count (argcnt); if (argcnt <= 4) cptr = "!UL !AZ - !AZ\r\n"; else cptr = "!UL !AZ - !&@\r\n"; vecptr = FaoVector; *vecptr++ = StatusCode; *vecptr++ = HttpStatusCodeText(StatusCode); *vecptr++ = ExplanationPtr; /* add any variable length arguments to the vector */ va_start (argptr, ExplanationPtr); for (argcnt -= 4; argcnt; argcnt--) *vecptr++ = va_arg (argptr, ulong); va_end (argptr); status = FaolToBuffer (Buffer, sizeof(Buffer), &Length, cptr, &FaoVector); if (VMSnok (status) || status == SS$_BUFFEROVF) ErrorExitVmsStatus (status, ErrorServerFao, FI_LI); Buffer[rqptr->rqResponse.ErrorReportLength = Length] = '\0'; rqptr->rqResponse.ErrorReportPtr = VmGetHeap (rqptr, Length+1); memcpy (rqptr->rqResponse.ErrorReportPtr, Buffer, Length+1); return; } if (rqptr->rqResponse.ErrorReportByRedirect) { /*************************/ /* by-redirect available */ /*************************/ if (rqptr->ServicePtr->ErrorReportPathCodesCount) { /* check for handled error code */ StandardReport = true; for (idx = 0; idx < rqptr->ServicePtr->ErrorReportPathCodesCount; idx++) { if (rqptr->ServicePtr->ErrorReportPathCodes[idx] >= 0) { /* included HTTP status code */ if (rqptr->ServicePtr->ErrorReportPathCodes[idx] == StatusCode) { StandardReport = false; break; } } else { /* excluded HTTP status code */ StandardReport = false; if (rqptr->ServicePtr->ErrorReportPathCodes[idx] == -StatusCode) { StandardReport = true; break; } } } if (StandardReport) { /* use standard error reporting */ rqptr->rqResponse.ErrorReportByRedirect = false; rqptr->RedirectErrorStatusCode = 0; rqptr->RedirectErrorAuthRealmDescrPtr = NULL; } } if (rqptr->rqResponse.ErrorReportByRedirect) { /******************************************/ /* redirect report, generate query string */ /******************************************/ va_count (argcnt); if (argcnt <= 4) cptr = "

!AZ"; else cptr = "

!&@"; vecptr = FaoVector; *vecptr++ = ExplanationPtr; /* add any variable length arguments to the vector */ va_start (argptr, ExplanationPtr); for (argcnt -= 4; argcnt; argcnt--) *vecptr++ = va_arg (argptr, ulong); va_end (argptr); status = FaolToBuffer (Buffer, sizeof(Buffer), &Length, cptr, &FaoVector); if (VMSnok (status) || status == SS$_BUFFEROVF) ErrorExitVmsStatus (status, ErrorServerFao, FI_LI); ErrorRedirectQueryString (rqptr, 0, Buffer, "", SourceModule, SourceLine); return; } } /*******************/ /* generate report */ /*******************/ va_count (argcnt); vecptr = FaoVector; if (rqptr->rqPathSet.ReportType == ERROR_REPORT_DETAILED) *vecptr++ = ErrorSourceInfo (SourceModule, SourceLine); else if (rqptr->rqPathSet.ReportType == ERROR_REPORT_BASIC || Config.cfReport.BasicOnly) *vecptr++ = ""; else *vecptr++ = ErrorSourceInfo (SourceModule, SourceLine); *vecptr++ = CategoryPtr = MsgFor(rqptr,MSG_STATUS_ERROR); *vecptr++ = StatusCode; *vecptr++ = HttpStatusCodeText(StatusCode); ErrorReportHeader (rqptr, &vecptr); *vecptr++ = CategoryPtr; *vecptr++ = StatusCode; *vecptr++ = HttpStatusCodeExplanation (rqptr,StatusCode); ErrorReportEndHeader (rqptr, &vecptr); if (rqptr->rqPathSet.ReportType != ERROR_REPORT_DETAILED && (rqptr->rqPathSet.ReportType == ERROR_REPORT_BASIC || Config.cfReport.BasicOnly)) *vecptr++ = ""; else if (argcnt <= 4) { *vecptr++ = "

!AZ"; *vecptr++ = ExplanationPtr; } else { *vecptr++ = "

!&@"; *vecptr++ = ExplanationPtr; /* add any variable length arguments to the vector */ va_start (argptr, ExplanationPtr); for (argcnt -= 4; argcnt; argcnt--) *vecptr++ = va_arg (argptr, ulong); va_end (argptr); } *vecptr++ = MsgFor(rqptr,MSG_STATUS_INFO); ErrorReportFooter (rqptr, &vecptr); ReportPtr = MsgFor(rqptr,MSG_STATUS_REPORT_FORMAT); status = FaolToBuffer (Buffer, sizeof(Buffer), &Length, ReportPtr, &FaoVector); if (VMSnok (status) || status == SS$_BUFFEROVF) ErrorExitVmsStatus (status, ErrorServerFao, FI_LI); Buffer[rqptr->rqResponse.ErrorReportLength = Length] = '\0'; rqptr->rqResponse.ErrorReportPtr = VmGetHeap (rqptr, Length+1); memcpy (rqptr->rqResponse.ErrorReportPtr, Buffer, Length+1); } /*****************************************************************************/ /* Generate an error message about a VMS status problem for subsequent reporting to the client. Generally most errors are reported as documents in their own right, making the full HTML document desirable. If reported part-way through some action the redundant tags should be ignored anyway. If error redirection applies to the request then generate the error information in a form suitable for provision as a request query-string. */ void ErrorVmsStatus ( REQUEST_STRUCT *rqptr, int StatusValue, char *SourceModule, int SourceLine ) { BOOL StandardReport; int idx, status, OriginalStatusCode, StatusCode; ushort Length; ushort ShortLength; ulong *vecptr; ulong FaoVector [32]; char *cptr, *sptr, *zptr, *CategoryPtr, *ContentTypePtr, *ExplanationPtr, *RecommendPtr, *ReportPtr; char Message [256], Buffer [4096]; $DESCRIPTOR (MessageDsc, Message); /*********/ /* begin */ /*********/ if (WATCHMOD (rqptr, WATCH_MOD__OTHER)) WatchThis (WATCHITM(rqptr), WATCH_MOD__OTHER, "ErrorVmsStatus()"); /* don't overwrite any existing error message */ if (ERROR_REPORTED (rqptr)) return; /* not if WebDAV is involved */ if (rqptr->WebDavRequest || rqptr->WhiffOfWebDav || rqptr->WebDavTaskPtr) rqptr->rqResponse.ErrorReportByRedirect = false; /* if this request is processing an error report redirect */ if (rqptr->rqResponse.ErrorReportByRedirect && rqptr->RedirectErrorStatusCode) { /* problem processing the error report, no infinite loops here please! */ rqptr->rqResponse.ErrorReportByRedirect = false; rqptr->RedirectErrorStatusCode = 0; rqptr->RedirectErrorAuthRealmDescrPtr = NULL; } if (rqptr->rqResponse.HeaderGenerated && STR_DSC_PTR(&rqptr->NetWriteBufferDsc)) { /* some output has already occured, internal error reporting */ rqptr->rqResponse.ErrorReportByRedirect = false; } OriginalStatusCode = rqptr->rqResponse.HttpStatus; /* if this error occured after header generation turn it into an error! */ if (rqptr->rqResponse.HttpStatus == 200) rqptr->rqResponse.HttpStatus = 0; if (!rqptr->rqResponse.HttpStatus) { if (StatusValue == RMS$_FNF || StatusValue == RMS$_DNF || StatusValue == RMS$_DEV || StatusValue == SS$_NOSUCHDEV || StatusValue == SS$_NOSUCHFILE) rqptr->rqResponse.HttpStatus = 404; else if (StatusValue == RMS$_SYN || StatusValue == RMS$_FNM || StatusValue == RMS$_TYP || StatusValue == RMS$_VER || StatusValue == RMS$_DIR || StatusValue == SS$_BADFILENAME || StatusValue == SS$_BADFILEVER || StatusValue == SS$_BADIRECTORY) rqptr->rqResponse.HttpStatus = 403; else if (StatusValue == RMS$_FLK || StatusValue == SS$_DIRNOTEMPTY || StatusValue == SS$_EXDISKQUOTA) rqptr->rqResponse.HttpStatus = 409; else if (StatusValue == RMS$_PRV || StatusValue == SS$_NOPRIV) rqptr->rqResponse.HttpStatus = 403; else rqptr->rqResponse.HttpStatus = 500; } /* when mapping error status by implication its a basic report! */ if ((rqptr->rqResponse.HttpStatus == 400 || rqptr->rqResponse.HttpStatus == 409) && rqptr->rqPathSet.Report400as) { rqptr->rqResponse.HttpStatus = rqptr->rqPathSet.Report400as; rqptr->rqPathSet.ReportType = ERROR_REPORT_BASIC; } else if ((rqptr->rqResponse.HttpStatus == 403 || rqptr->rqResponse.HttpStatus == 409) && rqptr->rqPathSet.Report403as) { rqptr->rqResponse.HttpStatus = rqptr->rqPathSet.Report403as; rqptr->rqPathSet.ReportType = ERROR_REPORT_BASIC; } else if ((rqptr->rqResponse.HttpStatus == 404 || rqptr->rqResponse.HttpStatus == 409) && rqptr->rqPathSet.Report404as) { rqptr->rqResponse.HttpStatus = rqptr->rqPathSet.Report404as; rqptr->rqPathSet.ReportType = ERROR_REPORT_BASIC; } StatusCode = rqptr->rqResponse.HttpStatus; if (StatusCode == 401 || StatusCode == 407) ResponseHeaderChallenge (rqptr); if (rqptr->rqContentInfo.ContentTypePtr) ContentTypePtr = rqptr->rqContentInfo.ContentTypePtr; else ContentTypePtr = ""; if (StatusValue == RMS$_FNF) { if (ConfigSameContentType (ContentTypePtr, "text/", 5)) ExplanationPtr = MsgFor(rqptr,MSG_STATUS_DOC_NOT_FOUND); else ExplanationPtr = MsgFor(rqptr,MSG_STATUS_FILE_NOT_FOUND); } else if (StatusValue == RMS$_PRV || StatusValue == SS$_NOPRIV) { if (ConfigSameContentType (ContentTypePtr, "text/", 5)) ExplanationPtr = MsgFor(rqptr,MSG_STATUS_DOC_PROTECTION); else ExplanationPtr = MsgFor(rqptr,MSG_STATUS_FILE_PROTECTION); } else if (VMSok (status = sys$getmsg (StatusValue, &ShortLength, &MessageDsc, 1, 0))) { Message[ShortLength] = '\0'; cptr = sptr = ExplanationPtr = Message; *cptr = TOUP(*cptr); /* don't allow access violation to be confused with forbidden access! */ if (StatusValue == SS$_ACCVIO) { while (*cptr && *cptr != ',') cptr++; if (*cptr) strcpy (cptr, " (segmentation fault)"); cptr = sptr; } /* improve the look by removing any embedded sys$fao() formatting */ while (*cptr) { if (*cptr == '!') { cptr++; *sptr++ = '?'; /* step over any field width digits */ while (isdigit(*cptr)) cptr++; /* usually two formatting characters */ if (isalpha(*cptr)) cptr++; if (isalpha(*cptr)) cptr++; } else *sptr++ = *cptr++; } *sptr = '\0'; } else strcpy (Message, "*ERROR* sys$getmsg()"); if (!rqptr->rqResponse.ErrorTextPtr) { if (rqptr->rqHeader.PathInfoPtr) rqptr->rqResponse.ErrorTextPtr = rqptr->rqHeader.PathInfoPtr; else rqptr->rqResponse.ErrorTextPtr = MsgFor(rqptr, MSG_STATUS_NO_INFORMATION); } if (!rqptr->rqResponse.ErrorOtherTextPtr || !Config.cfReport.MetaInfoEnabled) rqptr->rqResponse.ErrorOtherTextPtr = MsgFor(rqptr,MSG_STATUS_NO_INFORMATION); RecommendPtr = NULL; if (!Config.cfReport.ErrorRecommend || StatusCode == 401 || StatusCode == 407) RecommendPtr = ""; else if (StatusValue == RMS$_FNF || StatusValue == RMS$_DNF || StatusValue == SS$_NOSUCHFILE) RecommendPtr = MsgFor(rqptr,MSG_STATUS_ADVISE_NOSUCHFILE); else if (StatusValue == RMS$_PRV) RecommendPtr = MsgFor(rqptr,MSG_STATUS_ADVISE_PRV); else if (StatusValue == SS$_NOPRIV) RecommendPtr = MsgFor(rqptr,MSG_STATUS_ADVISE_NOPRIV); else if (StatusValue == RMS$_SYN || StatusValue == RMS$_DEV || StatusValue == RMS$_DIR || StatusValue == RMS$_FNM || StatusValue == RMS$_TYP) RecommendPtr = MsgFor(rqptr,MSG_STATUS_ADVISE_SYNTAX); else if (StatusValue == RMS$_FLK) RecommendPtr = MsgFor(rqptr,MSG_STATUS_ADVISE_FLK); else if (StatusValue == SS$_DIRNOTEMPTY || StatusValue == SS$_EXDISKQUOTA) RecommendPtr = MsgFor(rqptr,MSG_STATUS_ADVISE_CORRECT); if (!RecommendPtr) RecommendPtr = ""; if (WATCHING (rqptr, WATCH_ERROR)) { cptr = "default"; switch (rqptr->rqPathSet.ReportType) { case ERROR_REPORT_BASIC : cptr = "basic"; break; case ERROR_REPORT_DETAILED : cptr = "detailed"; break; case ERROR_REPORT_TUNNEL : cptr = "tunnel"; break; default: if (Config.cfReport.BasicOnly) cptr = "basic-only"; } WatchThis (WATCHITM(rqptr), WATCH_RESPONSE, "!AZ:!UL (!AZ) !3ZL(!3ZL) !&S \"!AZ\" \"!AZ\" \"!AZ\"", SourceModule, SourceLine, cptr, StatusCode, OriginalStatusCode, StatusValue, ExplanationPtr, rqptr->rqResponse.ErrorTextPtr, rqptr->rqResponse.ErrorOtherTextPtr); } if (rqptr->rqPathSet.ReportType == ERROR_REPORT_TUNNEL || rqptr->ServicePtr->ProxyTunnel == PROXY_TUNNEL_FIREWALL || rqptr->ServicePtr->ProxyTunnel == PROXY_TUNNEL_RAW) { /******************/ /* tunnel message */ /******************/ vecptr = FaoVector; *vecptr++ = StatusCode; *vecptr++ = HttpStatusCodeText(StatusCode); *vecptr++ = ExplanationPtr; *vecptr++ = rqptr->rqResponse.ErrorTextPtr; status = FaolToBuffer (Buffer, sizeof(Buffer), &Length, "!UL !AZ - !AZ ... !AZ\r\n", &FaoVector); if (VMSnok (status) || status == SS$_BUFFEROVF) ErrorExitVmsStatus (status, ErrorServerFao, FI_LI); Buffer[rqptr->rqResponse.ErrorReportLength = Length] = '\0'; rqptr->rqResponse.ErrorReportPtr = VmGetHeap (rqptr, Length+1); memcpy (rqptr->rqResponse.ErrorReportPtr, Buffer, Length+1); return; } if (rqptr->rqResponse.ErrorReportByRedirect) { /*************************/ /* by-redirect available */ /*************************/ if (rqptr->ServicePtr->ErrorReportPathCodesCount) { /* check for handled error code */ StandardReport = true; for (idx = 0; idx < rqptr->ServicePtr->ErrorReportPathCodesCount; idx++) { if (rqptr->ServicePtr->ErrorReportPathCodes[idx] >= 0) { /* included HTTP status code */ if (rqptr->ServicePtr->ErrorReportPathCodes[idx] == StatusCode) { StandardReport = false; break; } } else { /* excluded HTTP status code */ StandardReport = false; if (rqptr->ServicePtr->ErrorReportPathCodes[idx] == -StatusCode) { StandardReport = true; break; } } } if (StandardReport) { /* use standard error reporting */ rqptr->rqResponse.ErrorReportByRedirect = false; rqptr->RedirectErrorStatusCode = 0; rqptr->RedirectErrorAuthRealmDescrPtr = NULL; } } if (rqptr->rqResponse.ErrorReportByRedirect) { /******************************************/ /* redirect report, generate query string */ /******************************************/ ErrorRedirectQueryString (rqptr, StatusValue, ExplanationPtr, RecommendPtr, SourceModule, SourceLine); return; } } /*******************/ /* generate report */ /*******************/ vecptr = FaoVector; if (rqptr->rqPathSet.ReportType == ERROR_REPORT_DETAILED) *vecptr++ = ErrorSourceInfo (SourceModule, SourceLine); else if (rqptr->rqPathSet.ReportType == ERROR_REPORT_BASIC || Config.cfReport.BasicOnly) *vecptr++ = ""; else *vecptr++ = ErrorSourceInfo (SourceModule, SourceLine); *vecptr++ = CategoryPtr = MsgFor(rqptr,MSG_STATUS_ERROR); *vecptr++ = StatusCode; *vecptr++ = HttpStatusCodeText(StatusCode); ErrorReportHeader (rqptr, &vecptr); *vecptr++ = CategoryPtr; *vecptr++ = StatusCode; *vecptr++ = HttpStatusCodeExplanation (rqptr,StatusCode); ErrorReportEndHeader (rqptr, &vecptr); if (rqptr->rqPathSet.ReportType != ERROR_REPORT_DETAILED && (rqptr->rqPathSet.ReportType == ERROR_REPORT_BASIC || Config.cfReport.BasicOnly)) *vecptr++ = ""; else { *vecptr++ = "

!AZ  ...  !&@\n\ \n\ !AZ"; *vecptr++ = ExplanationPtr; /* if it looks like it starts with a path forward-slash */ if (rqptr->rqResponse.ErrorTextPtr[0] == '/') *vecptr++ = "!&;&_AZ"; else *vecptr++ = "!AZ"; *vecptr++ = rqptr->rqResponse.ErrorTextPtr; *vecptr++ = StatusValue; *vecptr++ = rqptr->rqResponse.ErrorOtherTextPtr; *vecptr++ = RecommendPtr; } *vecptr++ = MsgFor(rqptr,MSG_STATUS_INFO); ErrorReportFooter (rqptr, &vecptr); ReportPtr = MsgFor(rqptr,MSG_STATUS_REPORT_FORMAT); status = FaolToBuffer (Buffer, sizeof(Buffer), &Length, ReportPtr, &FaoVector); if (VMSnok (status) || status == SS$_BUFFEROVF) ErrorNoticed (rqptr, status, NULL, FI_LI); Buffer[rqptr->rqResponse.ErrorReportLength = Length] = '\0'; rqptr->rqResponse.ErrorReportPtr = VmGetHeap (rqptr, Length+1); memcpy (rqptr->rqResponse.ErrorReportPtr, Buffer, Length+1); } /*****************************************************************************/ /* Internal redirect error report, generate a query string containing all the information required to generate an error report. */ void ErrorRedirectQueryString ( REQUEST_STRUCT *rqptr, int StatusValue, char *ExplanationPtr, char *RecommendPtr, char *SourceModule, int SourceLine ) { int status; ushort Length; ulong *vecptr; ulong FaoVector [32]; char Buffer [16384]; /*********/ /* begin */ /*********/ if (WATCHMOD (rqptr, WATCH_MOD__OTHER)) WatchThis (WATCHITM(rqptr), WATCH_MOD__OTHER, "ErrorRedirectQueryString()"); vecptr = FaoVector; *vecptr++ = rqptr->rqPathSet.ReportType != ERROR_REPORT_DETAILED && (rqptr->rqPathSet.ReportType == ERROR_REPORT_BASIC || Config.cfReport.BasicOnly) ? "basic" : "detailed", *vecptr++ = rqptr->rqResponse.HttpStatus; *vecptr++ = HttpStatusCodeText(rqptr->rqResponse.HttpStatus); *vecptr++ = HttpStatusCodeExplanation(rqptr,rqptr->rqResponse.HttpStatus); /* '_vms' is empty to indicate no VMS status code */ if (StatusValue) { *vecptr++ = "!UL"; *vecptr++ = StatusValue; } else *vecptr++ = ""; *vecptr++ = rqptr->rqHeader.RequestUriPtr; if (rqptr->rqResponse.ErrorTextPtr) *vecptr++ = rqptr->rqResponse.ErrorTextPtr; else *vecptr++ = ""; if (rqptr->rqResponse.ErrorOtherTextPtr) *vecptr++ = rqptr->rqResponse.ErrorOtherTextPtr; else *vecptr++ = ""; *vecptr++ = ExplanationPtr; *vecptr++ = RecommendPtr; *vecptr++ = SourceModule; *vecptr++ = SourceLine; *vecptr++ = rqptr->ServicePtr->ServerHostPort; if (rqptr->rqResponse.HttpStatus == 401 || rqptr->rqResponse.HttpStatus == 407) { *vecptr++ = "&error_realm=!AZ!&@!&@"; *vecptr++ = rqptr->rqAuth.RealmPtr; if (rqptr->rqAuth.BasicChallengePtr && rqptr->rqAuth.BasicChallengePtr[0]) { *vecptr++ = "&error_basic=!&%AZ"; *vecptr++ = rqptr->rqAuth.BasicChallengePtr; } else *vecptr++ = ""; if (rqptr->rqAuth.DigestChallengePtr && rqptr->rqAuth.DigestChallengePtr[0]) { *vecptr++ = "&error_digest=!&%AZ"; *vecptr++ = rqptr->rqAuth.DigestChallengePtr; } else *vecptr++ = ""; } else *vecptr++ = ""; status = FaolToBuffer (Buffer, sizeof(Buffer), &Length, "?httpd=error\ &error_type=!AZ\ &error_status=!UL\ &error_status_text=!&%AZ\ &error_status_explanation=!&%AZ\ &error_vms=!&@\ &error_uri=!&%AZ\ &error_about=!&%AZ\ &error_about2=!&%AZ\ &error_text=!&%AZ\ &error_text2=!&%AZ\ &error_module=!&%AZ\ &error_line=!UL\ &error_proxy=!&%AZ\ !&@", &FaoVector); if (VMSnok (status) || status == SS$_BUFFEROVF) ErrorNoticed (rqptr, status, NULL, FI_LI); rqptr->rqResponse.ErrorReportLength = Length; rqptr->rqResponse.ErrorReportPtr = VmGetHeap (rqptr, Length+1); memcpy (rqptr->rqResponse.ErrorReportPtr, Buffer, Length+1); } /*****************************************************************************/ /* Report general string overflow. */ void ErrorGeneralOverflow ( REQUEST_STRUCT *rqptr, char *SourceModule, int SourceLine ) { int status; /*********/ /* begin */ /*********/ if (WATCHMOD (rqptr, WATCH_MOD__OTHER)) WatchThis (WATCHITM(rqptr), WATCH_MOD__OTHER, "ErrorGeneralOverflow()"); if (!rqptr->rqResponse.HttpStatus) rqptr->rqResponse.HttpStatus = 500; ErrorGeneral (rqptr, MsgFor(rqptr,MSG_GENERAL_OVERFLOW), SourceModule, SourceLine); } /*****************************************************************************/ /* */ void ErrorInternal ( REQUEST_STRUCT *rqptr, int StatusValue, char *ExplanationPtr, char *SourceModule, int SourceLine ) { /*********/ /* begin */ /*********/ if (WATCHMOD (rqptr, WATCH_MOD__OTHER)) WatchThis (WATCHITM(rqptr), WATCH_MOD__OTHER, "ErrorInternal()"); rqptr->rqResponse.HttpStatus = 500; if (ExplanationPtr) ErrorGeneral (rqptr, ExplanationPtr, SourceModule, SourceLine); else ErrorVmsStatus (rqptr, StatusValue, SourceModule, SourceLine); } /*****************************************************************************/ /* If the server is configured for it return a pointer to some META information containing softwareID, source code module and line in which the error occured, otherwise pointer to an empty string. This function is not used reentrantly and so provided the contents of the static buffer are used before it is recalled it will continue to work. The source file format provided by the "__FILE__" macro will be "device:[directory]name.type;ver". Reduce that to "name". The "__LINE__" macro just provides an integer. */ char* ErrorSourceInfo ( char *SourceModule, int SourceLine ) { static char Buffer [256]; int status; /*********/ /* begin */ /*********/ if (WATCH_MODULE(WATCH_MOD__OTHER)) WatchThis (WATCHALL, WATCH_MOD__OTHER, "ErrorSourceInfo()"); if (!Config.cfReport.MetaInfoEnabled) return (""); status = FaoToBuffer (Buffer, sizeof(Buffer), NULL, "\n\ \n\ \n", SoftwareID, SourceModule, SourceLine); if (VMSnok (status) || status == SS$_BUFFEROVF) ErrorExitVmsStatus (status, ErrorServerFao, FI_LI); return (Buffer); } /****************************************************************************/