/*=========================================================================== * * File: DL_CGI.CPP * Author: Dave Humphrey (uesp@m0use.net) * Created On: Wednesday, May 09, 2001 * * Implements various CGI related routines. * *=========================================================================*/ /* Include Files */ #include "dl_cgi.h" #include "dl_math.h" #include "dl_str.h" #include "listfile.h" #include <stdarg.h> /*=========================================================================== * * Begin Local Variables * *=========================================================================*/ DEFINE_FILE(); /*=========================================================================== * End of Local Variables *=========================================================================*/ /*=========================================================================== * * Function - cgireferer_t cgiCheckReferers (pValidReferersFile); * * Checks the current Referers environment variable with the valid referers * listed in the given list file. Returns: * CGI_REFERER_OK : The current referer is valid * CGI_REFERER_BAD : An invalid referer address * CGI_REFERER_ERROR : An error occured while trying to validate the referer * A valid referer is one which matches all, or a part of, a referer in * in the valid list file. For example, "http://www.yahoo.com" would be * a valid referer if the list file contains "http://www.yahoo.com", "yahoo", * "www.yahoo.com", etc... Another example, "somesite.net" would not be * a valid referer if the list file contained "http://somesite.net", * "www.somesite.net", or "somesite.net/~user". The valid referers list * file should be created carefully to allow the proper referers access * to the script. The comparisons are case insensitive. * *=========================================================================*/ cgireferer_t cgiCheckReferers (const char* pValidReferersFile) { DEFINE_FUNCTION("cgiCheckReferers()"); CListFile ReferersList; boolean Result; char* pReferer; /* Attempt to get the current referer address */ pReferer = getenv(HTTP_REFERER_ENV); if (pReferer == NULL || strlen(pReferer) == 0) return (CGI_REFERER_ERROR); /* Attempt to open referer list file */ Result = ReferersList.Open(pValidReferersFile); if (!Result) return (CGI_REFERER_ERROR); /* Check current referer with valid referers in list file */ while (ReferersList.IsValidLine()) { if (stristr(pReferer, ReferersList.GetCurrentLine()) != NULL) return (CGI_REFERER_OK); ReferersList.ReadNextLine(); } return (CGI_REFERER_BAD); } /*=========================================================================== * End of Function cgiCheckReferers() *=========================================================================*/ /*=========================================================================== * * Function - boolean cgiErrorOutputFunc (Format, pOutputFile, pUserData); * * The custom user output function for error pages. Accepts the format * information and outputs the appropiate data to the give file stream. * Returns FALSE on any error. * %e or %E - Output last error message * %m of %M - Output the user message. * The user data is of the type erroroutput_userdata_t. * *=========================================================================*/ boolean cgiErrorOutputFunc (const COutputFormat& Format, FILE* pOutputFile, void* pUserData) { DEFINE_FUNCTION("cgiErrorOutputFunc()"); int Result; switch (Format.Type) { case 'E': case 'e': /* Output the last error message */ Result = fprintf (pOutputFile, "%s", ErrorHandler.GetLastErrorMsg()); break; case 'M': case 'm': { /* Output the user message */ erroroutput_userdata_t* pData = (erroroutput_userdata_t *)pUserData; if (pData == NULL) Result = fprintf (pOutputFile, "No user message given!"); else Result = vfprintf(pOutputFile, pData->pString, pData->Args); break; } default: /* Output just the type character */ Result = fprintf (pOutputFile, "%c", Format.Type); break; } if (Result < 0) return (FALSE); return (TRUE); } /*=========================================================================== * End of Function cgiErrorOutputFunc() *=========================================================================*/ /*=========================================================================== * * Function - void cgiOutputError (pErrFilename, pString, ...); * * Outputs an error page using the the given printf() style input as a * user message. First it attempts to use the given HTML error filename, * outputting it using the output function cgiErrorOutputFunc(). If * the file is not found, the function cgiOutputErrorPage() is used * to display a simple error page. * *=========================================================================*/ void cgiOutputError (const char* pErrFilename, const char* pString, ...) { DEFINE_FUNCTION("cgiOutputError()"); CGenOutput ErrorOutput(cgiErrorOutputFunc); boolean Result; /* Ensure valid input */ ASSERT(pString != NULL); /* Ensure the HTML header has been output */ cgiOutputHTMLHeader(); /* Attempt to read the output error file */ Result = ErrorOutput.ReadFile(pErrFilename); if (Result) { erroroutput_userdata_t UserData; /* Initialize the user data structure */ va_start(UserData.Args, pString); UserData.pString = pString; /* Output the HTML error file to stdout */ Result = ErrorOutput.OutputFile(stdout, (void *)&UserData); va_end(UserData.Args); } /* Failed to load/output the output error file */ if (!Result) { va_list Args; /* Output simple error HTML page to stdout */ va_start(Args, pString); cgiOutputErrorPage(pString, Args); va_end(Args); } } /*=========================================================================== * End of Function cgiOutputError() *=========================================================================*/ /*=========================================================================== * * Function - void cgiOutputErrorPageV (pString, Args); * * Outputs a simple error page using a variable argument list message. * *=========================================================================*/ void cgiOutputErrorPageV (const char* pString, va_list Args) { DEFINE_FUNCTION("cgiOutputErrorPageV(char*, va_list)"); /* Ensure HTML header has been output */ cgiOutputHTMLHeader(); printf ("<HTML><HEAD><TITLE>CGI Error!</TITLE></HEAD><BODY>\n"); printf ("<P><CENTER><H2>CGI Error!</H2></CENTER><HR NOSHADE>\n"); printf ("An error has occured in a CGI script, causing it to abort. "); printf ("This probably wasn't supposed to happen. "); /* Output the user message, if any */ if (pString != NULL && Args != NULL) { printf ("The reported error message is:\n"); printf ("<P><FONT COLOR='#ff3333'><B><DL><DD>"); vprintf(pString, Args); printf ("</DL></FONT></B><P>\n"); } /* Output the last error in incident list */ printf ("The last recorded error incident was:\n<P><FONT COLOR='#ff3333'><B><DL><DD>"); printf ("Message: %s<BR>", ErrorHandler.GetLastErrorMsg()); printf ("Error: %s<BR>", ErrorHandler.GetLastErrorDBMsg()); printf ("</DL></FONT></B><P>\n"); printf ("For more information, please return to the previous web pages and contact "); printf ("its author(s).<HR NOSHADE>\n"); printf ("<ADDRESS><I><SMALL><FONT COLOR='#666666'>"); printf ("Module %s written by %s, <A HREF='mailto:%s'>%s</A>, May 2001", ThisFile, DL_BASE_AUTHOR, DL_BASE_EMAIL, DL_BASE_EMAIL); printf ("</FONT></SMALL></I></ADDRESS>"); printf ("</BODY></HTML>\n"); fflush (stdout); } /*=========================================================================== * End of Function cgiOutputErrorPageV() *=========================================================================*/ /*=========================================================================== * * Function - void cgiOutputErrorPage (pString, ...); * * Outputs a simple error page to stdout with the given printf() formatted * error message. Ensures the HTML header has been output. * *=========================================================================*/ void cgiOutputErrorPage (const char* pString, ...) { DEFINE_FUNCTION("cgiOutputErrorPage(char*, ...)"); va_list Args; if (pString != NULL) { va_start(Args, pString); cgiOutputErrorPageV(pString, Args); va_end(Args); } else cgiOutputErrorPageV(pString, NULL); } /*=========================================================================== * End of Function cgiOutputErrorPage() *=========================================================================*/ /*=========================================================================== * * Function - boolean cgiOutputHTML (pFilename, UserFunction, pUserData); * * Attempts to output the given HTML file to stdout, parsing out any * '%' variables, giving them to the supplied user function. Returns FALSE * on any error outputting the file. ASSERTs if given invalid input. * Does not output the HTML header. * *=========================================================================*/ boolean cgiOutputHTML (const char* pFilename, PUSER_OUTPUT_FUNC UserFunction, void* pUserData) { DEFINE_FUNCTION("cgiOutputHTML()"); CGenOutput OutputFile(UserFunction); boolean Result; /* Ensure valid inputs */ ASSERT(pFilename != NULL && UserFunction != NULL); /* Attempt to input the file and output HTML to stdout */ Result = OutputFile.ReadFile(pFilename); if (Result) Result = OutputFile.OutputFile(stdout, pUserData); return (Result); } /*=========================================================================== * End of Function cgiOutputHTML() *=========================================================================*/ /*=========================================================================== * * Function - void cgiOutputHTMLHeader (void); * * Outputs the HTML header to stdout, indicating an HTML document is to * be following. Can only be output once per application run. * *=========================================================================*/ void cgiOutputHTMLHeader (void) { //DEFINE_FUNCTION("cgiOutputHTMLHeader()"); static boolean HaveOutputHeader = FALSE; /* Ensure we only output the header once */ if (!HaveOutputHeader) { printf ("Content-type: text/html\n\n"); fflush (stdout); HaveOutputHeader = TRUE; } } /*=========================================================================== * End of Function cgiOutputHTMLHeader() *=========================================================================*/ /*=========================================================================== * * Function - void cgiOutputRedirect (pWebSite); * * Outputs a redirect request to stdout. ASSERTs if given an invalid * string pointer. Does not work if the HTML header has already been * output. * *=========================================================================*/ void cgiOutputRedirect (const char* pWebSite) { DEFINE_FUNCTION("cgiOutputRedirect()"); /* Ensure valid input */ ASSERT(pWebSite != NULL); printf ("Location: %s\n\n", pWebSite); fflush(stdout); } /*=========================================================================== * End of Function cgiOutputRedirect() *=========================================================================*/ /*=========================================================================== * * Function - boolean cgiParseFormHexCode (OutputChar, ppString); * * Attempts to parse a '%##' hexcode from the given position in a form * output string. Returns FALSE on any error. ASSERTs if given * invalid input. * *=========================================================================*/ boolean cgiParseFormHexCode (char& OutputChar, char** ppString) { DEFINE_FUNCTION("cgiParseFormHexCode()"); /* Ensure valid input */ ASSERT(ppString != NULL && *ppString != NULL); if (**ppString != '%') return (FALSE); (*ppString)++; if (**ppString == NULL_CHAR) return (FALSE); OutputChar = (char)(16*HexCharToInt(**ppString)); (*ppString)++; if (**ppString == NULL_CHAR) return (FALSE); OutputChar += (char)(HexCharToInt(**ppString)); return (TRUE); } /*=========================================================================== * End of Function cgiParseFormHexCode() *=========================================================================*/ /*=========================================================================== * * Function - boolean cgiUnWebFormOutput (pString); * * Converts a string output from a HTML form into a regular ASCII string. * Returns FALSE on any error and sets the appropiate code with ErrorHandler. * The function converts the following: * - Converts all '+' characters to spaces * - Converts % hex codes into ASCII characters. The hex codes have * the format '%##' where ## is some hex number (0-255). * ASSERTs if it received invalid input. The string is converted in * place and may end up shorted than when it started. * *=========================================================================*/ boolean cgiUnWebFormOutput (char* pString) { DEFINE_FUNCTION("cgiUnWebFormOutput()"); char* pParse = pString; boolean Result; /* Ensure valid input */ ASSERT(pString != NULL); /* Parse the entire input string */ while (*pString != NULL_CHAR) { switch (*pString) { case '%': /* Check for a hex code character */ Result = cgiParseFormHexCode(*pParse, &pString); if (!Result) { ErrorHandler.AddError(ERR_BADINPUT, "Error parsing form output hex code (stopped at character %d, '%c')!", (int)*pString, *pString); return (FALSE); } break; case '+': /* Convert the space character */ *pParse = ' '; break; default: /* Convert a regular character */ *pParse = *pString; break; } pParse++; pString++; } /* Terminate the new string */ *pParse = NULL_CHAR; return (TRUE); } /*=========================================================================== * End of Function cgiUnWebFormOutput() *=========================================================================*/ /*=========================================================================== * * Begin Module Test Routines * * Functions to test all routines in this module available only in DEBUG * builds. * *=========================================================================*/ #if defined(_DEBUG) /* Structure used for testing the cgiOutputHTML() function */ typedef struct { char* pPointer; int Number; long LongNumber; float FloatNumber; } CTestType; /*=========================================================================== * * Function - boolean Test_UserFunction (Format, pOutputFile, pUserData); * * Test user function for parsing '%' variables from an output file. * pUserData is of type CTestType. * *=========================================================================*/ boolean Test_UserFunction (const COutputFormat& Format, FILE* pOutputFile, void* pUserData) { DEFINE_FUNCTION("Test_UserFunction()"); CTestType* pTestData = (CTestType *) pUserData; int Result; switch (Format.Type) { case 'I': Result = fprintf(pOutputFile, "***I***"); break; case 'i': Result = fprintf(pOutputFile, "%d", pTestData->Number); break; case 's': case 'S': Result = fprintf(pOutputFile, "%s", pTestData->pPointer); break; case 'l': case 'L': Result = fprintf(pOutputFile, "%ld", pTestData->LongNumber); break; case 'f': case 'F': Result = fprintf(pOutputFile, "%f", pTestData->FloatNumber); break; case 'E': case 'e': ErrorHandler.AddError(ERR_BADINPUT, "Test_UserFunction() - Testing returning FALSE"); return (FALSE); default: Result = fprintf(pOutputFile, "%c", Format.Type); break; } if (Result < 0) { ErrorHandler.AddError(ERR_SYSTEM, errno, "Test_UserFunction() - Error writing to file stream!"); return (FALSE); } return (TRUE); } /*=========================================================================== * End of Function Test_UserFunction() *=========================================================================*/ /*=========================================================================== * * Function - void Test_cgiCheckReferers (void); * * Tests the cgiCheckReferers() function. Assumes that a test referers * file has been created. * 1. Tests with typical valid referers * 2. Test with some invalid referers * *=========================================================================*/ void Test_cgiCheckReferers (void) { DEFINE_FUNCTION("Test_cgiCheckReferers()"); SystemLog.Printf (stdout, "=============== Testing cgiCheckReferers() ================="); /* Test several valid referers */ putenv("HTTP_REFERER=http://www.m0use.net"); ASSERT(cgiCheckReferers("referers.lst") == CGI_REFERER_OK); putenv("HTTP_REFERER=http://m0use.net/~uesp/somescript"); ASSERT(cgiCheckReferers("referers.lst") == CGI_REFERER_OK); putenv("HTTP_REFERER=http://130.15.193.126/~uesp/somescript"); ASSERT(cgiCheckReferers("referers.lst") == CGI_REFERER_OK); /* Test several invalid referers */ putenv("HTTP_REFERER=m0use.net"); ASSERT(cgiCheckReferers("referers.lst") == CGI_REFERER_BAD); putenv("HTTP_REFERER="); ASSERT(cgiCheckReferers("referers.lst") == CGI_REFERER_ERROR); putenv("HTTP_REFERER=http://"); ASSERT(cgiCheckReferers("referers.lst") == CGI_REFERER_BAD); ASSERT(cgiCheckReferers("nofile.lst") == CGI_REFERER_ERROR); } /*=========================================================================== * End of Function Test_cgiCheckReferers() *=========================================================================*/ /*=========================================================================== * * Function - void Test_cgiOutputHTML (void); * * Tests the cgiOutputHTML() function. * 1. Outputs a sample HTML file with a variety of '%' codes * 2. Tests outputting an invalid filename * 3. Test outputting a HTML containing known errors * *=========================================================================*/ void Test_cgiOutputHTML (void) { DEFINE_FUNCTION("Test_cgiOutputHTML()"); CTestType TestData; char TestString[101] = "***This is a test string!***"; SystemLog.Printf (stdout, "=============== Testing cgiOutputHTML() ================="); TestData.FloatNumber = (float)9.12345678e27; TestData.LongNumber = 987654321; TestData.Number = 1234567890; TestData.pPointer = TestString; /* Test outputting a valid HTML file */ ASSERT(cgiOutputHTML("testhtml1.html", Test_UserFunction, &TestData) == TRUE); /* Test outputting an invalid HTML file */ ASSERT(cgiOutputHTML("nofile.html", Test_UserFunction, &TestData) == FALSE); /* Test outputting a HTML with errors */ ASSERT(cgiOutputHTML("testhtml2.html", Test_UserFunction, &TestData) == FALSE); } /*=========================================================================== * End of Function Test_cgiOutputHTML() *=========================================================================*/ /*=========================================================================== * * Function - void Test_cgiParseFormHexCode (void); * * Tests the cgiParseFormHexCode() function * 1. Tests with typical inputs * 2. Test with invalid inputs * *=========================================================================*/ void Test_cgiParseFormHexCode (void) { DEFINE_FUNCTION("Test_cgiParseFormHexCode()"); char TestChar; char TestString[101]; char* pString; SystemLog.Printf (stdout, "=============== Testing cgiParseFormHexCode() ================="); /* Test typical function inputs */ strcpy(TestString, "%10"); pString = &TestString[0]; ASSERT(cgiParseFormHexCode(TestChar, &pString) == TRUE); ASSERT(*pString == '0'); ASSERT(TestChar == 0x10); strcpy(TestString, "%00"); pString = &TestString[0]; ASSERT(cgiParseFormHexCode(TestChar, &pString) == TRUE); ASSERT(TestChar == 0x00); strcpy(TestString, "%0a"); pString = &TestString[0]; ASSERT(cgiParseFormHexCode(TestChar, &pString) == TRUE); ASSERT(*pString == 'a'); ASSERT(TestChar == 0x0a); strcpy(TestString, "%23"); pString = &TestString[0]; ASSERT(cgiParseFormHexCode(TestChar, &pString) == TRUE); ASSERT(*pString == '3'); ASSERT(TestChar == 0x23); strcpy(TestString, "%3F"); pString = &TestString[0]; ASSERT(cgiParseFormHexCode(TestChar, &pString) == TRUE); ASSERT(*pString == 'F'); ASSERT(TestChar == 0x3f); strcpy(TestString, "%E4"); pString = &TestString[0]; ASSERT(cgiParseFormHexCode(TestChar, &pString) == TRUE); ASSERT(*pString == '4'); ASSERT(TestChar == (byte)0xE4); strcpy(TestString, "%D5"); pString = &TestString[0]; ASSERT(cgiParseFormHexCode(TestChar, &pString) == TRUE); ASSERT(TestChar == (byte)0xd5); strcpy(TestString, "%C6"); pString = &TestString[0]; ASSERT(cgiParseFormHexCode(TestChar, &pString) == TRUE); ASSERT(TestChar == (byte)0xc6); strcpy(TestString, "%b7"); pString = &TestString[0]; ASSERT(cgiParseFormHexCode(TestChar, &pString) == TRUE); ASSERT(TestChar == (byte)0xb7); strcpy(TestString, "%a8"); pString = &TestString[0]; ASSERT(cgiParseFormHexCode(TestChar, &pString) == TRUE); ASSERT(TestChar == (byte)0xa8); strcpy(TestString, "%99"); pString = &TestString[0]; ASSERT(cgiParseFormHexCode(TestChar, &pString) == TRUE); ASSERT(TestChar == (byte)0x99); /* Test with invalid inputs */ strcpy(TestString, "99"); pString = &TestString[0]; ASSERT(cgiParseFormHexCode(TestChar, &pString) == FALSE); strcpy(TestString, ""); pString = &TestString[0]; ASSERT(cgiParseFormHexCode(TestChar, &pString) == FALSE); strcpy(TestString, "%"); pString = &TestString[0]; ASSERT(cgiParseFormHexCode(TestChar, &pString) == FALSE); strcpy(TestString, "%1"); pString = &TestString[0]; ASSERT(cgiParseFormHexCode(TestChar, &pString) == FALSE); } /*=========================================================================== * End of Function Test_cgiParseFormHexCode() *=========================================================================*/ /*=========================================================================== * * Function - void Test_cgiUnWebFormOutput (void); * * Tests the cgiUnWebFormOutput() function. * 1. Test with typical inputs * 2. Test with invalid input * *=========================================================================*/ void Test_cgiUnWebFormOutput (void) { DEFINE_FUNCTION("Test_cgiUnWebFormOutput()"); char TestString[101]; SystemLog.Printf (stdout, "=============== Testing cgiUnWebFormOutput() ================="); /* Test typical function inputs */ strcpy(TestString, "This+is+a+test"); ASSERT(cgiUnWebFormOutput(TestString) == TRUE); ASSERT(strcmp(TestString, "This is a test") == 0); strcpy(TestString, "This+is%20a+test"); ASSERT(cgiUnWebFormOutput(TestString) == TRUE); ASSERT(strcmp(TestString, "This is a test") == 0); /* Test with invalid inputs */ strcpy(TestString, "This+is+a+test%"); ASSERT(cgiUnWebFormOutput(TestString) == FALSE); strcpy(TestString, "This+is+a+test%2"); ASSERT(cgiUnWebFormOutput(TestString) == FALSE); } /*=========================================================================== * End of Function Test_cgiUnWebFormOutput() *=========================================================================*/ /*=========================================================================== * * Function - void Test_DL_Cgi (void); * * Tests all functions in this module. * 1. Tests the cgiParseFormHexCode() function * 2. Test the cgiUnWebFormOutput() function * 3. Test the cgiCheckReferers() function * 4. Test the cgiOutputHTML() function * *=========================================================================*/ void Test_DL_Cgi (void) { DEFINE_FUNCTION("Test_DL_Cgi()"); Test_cgiParseFormHexCode(); Test_cgiUnWebFormOutput(); Test_cgiCheckReferers(); Test_cgiOutputHTML(); //cgiOutputError("uesperr.shtml", "Just testing the error output, %s, %d", "another test", 111111); //cgiOutputErrorPage("Just testing the error output, %s, %d", "another test", 111111); } /*=========================================================================== * End of Function Test_DL_Cgi() *=========================================================================*/ #endif /*=========================================================================== * End of Module Test Routines *=========================================================================*/