home *** CD-ROM | disk | FTP | other *** search
- //
- // IS2WCGI.CPP
- //
- // This sample Web Server Application allows a Windows CGI extension
- // to run as an ISAPI extension. The sample translates the inbound
- // ISAPI environment into the private profile that the Windows CGI
- // app is expecting. This sample implements Windows CGI version 1.3.
- //
- // The Windows CGI spec (authored by Robert B. Denny
- // <rdenny@netcom.com>) can be found at:
- //
- // http://website.ora.com/wsdocs/32demo/windows-cgi.html
- //
- // After building this DLL, copy it to the same directory your
- // Windows CGI application is in. Rename the DLL to the same
- // name your application has, but keep the DLL extension.
- //
- // See LibMain for details on why you must rename the DLL.
- //
-
- //
- // BETA NOTE: Only the 1.2 spec is implemented here. The capabilities
- // of spec version 1.3 will likely be added before product
- // release.
- //
-
- #define WIN32_LEAN_AND_MEAN
- #include <windows.h>
- #include <httpext.h>
-
- #include "keys.h"
-
- #define WAIT_EXT_TIMEOUT 120000 // 120 secs
- extern TCHAR gszAppName[MAX_PATH];
-
- //
- // This struct is used to pass profile information,
- // file names for temporary files, and an extension
- // control block pointer.
- //
- // One instance of this structure is delcared in
- // HttpExtensionProc, and its address is passed to
- // all the helper functions.
- //
-
- typedef struct
- {
- // Profile section names
- LPCSTR szCGI;
- LPCSTR szAccept;
- LPCSTR szSystem;
- LPCSTR szExtraHeaders;
- LPCSTR szFormLiteral;
- LPCSTR szFormExternal;
- LPCSTR szFormHuge;
- LPCSTR szFormFile;
-
- // File and path names
- TCHAR szProfileName[MAX_PATH];
- TCHAR szOutputFileName[MAX_PATH];
- TCHAR szTempDir[MAX_PATH];
- TCHAR szDataFileName[MAX_PATH];
- LPCTSTR lpszContentFile;
-
- // Key list and content file
- HKEYLIST hKeyList;
-
- // Info given by the www service
- EXTENSION_CONTROL_BLOCK *pECB;
- } WCGIPARAMS, *PWCGIPARAMS;
-
-
- // Prototypes
- void LogError (LPCTSTR lpszError, EXTENSION_CONTROL_BLOCK *pECB);
- void FillCGI (PWCGIPARAMS pParam);
- void FillAccept (PWCGIPARAMS pParam);
- void FillSystem (PWCGIPARAMS pParam);
- void FillExtraHeaders (PWCGIPARAMS pParam);
- BOOL FillFormData (PWCGIPARAMS pParam);
- HANDLE ExecuteChildProc (PWCGIPARAMS pParam);
- BOOL GetDataFromFile (PWCGIPARAMS pParam);
-
- //
- // Error messages (for the log file)
- //
-
- static TCHAR gszInDiskError[] = TEXT("Error writing inbound client data to disk.");
- static TCHAR gszOutDiskError[] = TEXT("Error reading outbound client data from disk.");
- static TCHAR gszBadProc[] = TEXT("Could not start Windows CGI executable.");
- static TCHAR gszNoProcEnd[] = TEXT("Windows CGI application never terminated.");
- static TCHAR gszParseDiskError[] = TEXT("Error reading data file during form decoding.");
- static TCHAR gszContentDiskError[] = TEXT("Could not save inbound data to content file.");
- static TCHAR gszOutOfMemory[] = TEXT("Memory allocation request failed.");
- static TCHAR gszTempFileError[] = TEXT("Could not create a temporary file.");
- static TCHAR gszWriteError[] = TEXT("Error writing to a temporary file.");
- static TCHAR gszNoDataDecoded[] = TEXT("No data sent by form.");
-
- //
- // Debug mode - make "Yes" or "No"
- //
-
- static TCHAR gszDebugMode[] = TEXT("No");
-
- //
- // Macro is used in Fill routines below.
- //
-
- #define MACRO_WriteKey(key,val) WritePrivateProfileString (szSection, key, val, szProfile)
- #define MACRO_WriteKeyInt(key,val) wsprintf(szVal,TEXT("%i"),val);WritePrivateProfileString(szSection, key, szVal, szProfile)
-
- //
- // BOOL WINAPI GetExtensionVersion (HSE_VERSION_INFO *pVersionInfo)
- //
- // Return the version this server is built for. See httpext.h for
- // a prototype. This function is required by the spec.
- //
-
- BOOL WINAPI GetExtensionVersion (HSE_VERSION_INFO *pVersionInfo)
- {
- // set version to httpext.h version constants
- pVersionInfo->dwExtensionVersion = MAKELONG (HSE_VERSION_MINOR, HSE_VERSION_MAJOR);
-
- lstrcpyn (pVersionInfo->lpszExtensionDesc,
- TEXT("Sample Web Server Application"),
- HSE_MAX_EXT_DLL_NAME_LEN);
-
- return TRUE;
- } // GetExtensionVersion()
-
-
- //
- // BOOL WINAPI HttpExtensionProc (EXTENSION_CONTROL_BLOCK *pECB)
- //
- // This function does all of the work. Once called it retrieves all of
- // the environment data from the server via the GetServerVariable
- // server function, reads all of the client data if it's not already
- // available, packs it all up in accordance with the Windows CGI standard,
- // calls the Windows CGI app, and then passes the returned data to the
- // web client.
- //
-
- DWORD WINAPI HttpExtensionProc (EXTENSION_CONTROL_BLOCK *pECB)
- {
- WCGIPARAMS param;
- HANDLE hProcess;
- DWORD dwResult;
- BOOL bError;
-
- memset (¶m, 0, sizeof (WCGIPARAMS));
-
- param.szCGI = "CGI";
- param.szAccept = "Accept";
- param.szSystem = "System";
- param.szExtraHeaders = "Extra Headers";
- param.szFormLiteral = "Form Literal";
- param.szFormExternal = "Form External";
- param.szFormHuge = "Form Huge";
-
- param.pECB = pECB;
-
- //
- // Windows CGI 1.3 apps are called with a command line of:
- // <WinCGIAppName> <WinCGIProfilePath>
- //
- // Most of the information goes in <WinCGIProfile>.
- //
- // This extension has the following tasks:
- //
- // - Create a temporary file to hold the profile
- // - Fill all the profile keys
- // - Collect inbound client data and save it to disk
- // - Start the child process
- // - Wait for child to finish
- // - Write data back to the web client
- // - Clean up
- //
-
- //
- // First, we have to create a temporary file. We use Win32 APIs.
- //
-
- // Get path
- GetTempPath (MAX_PATH, param.szTempDir);
-
- // Create profile and output file (always req'd by WCGI app)
- if (!GetTempFileName (param.szTempDir,
- param.szCGI, 0,
- param.szProfileName))
- {
- // Unexpected error creating temp file. Log an error.
- LogError (gszTempFileError, pECB);
-
- return HSE_STATUS_ERROR;
- }
-
- if (!GetTempFileName (param.szTempDir,
- param.szCGI, 0,
- param.szOutputFileName))
- {
- // Unexpected error creating temp file. Log an error.
- LogError (gszTempFileError, pECB);
- DeleteFile (param.szProfileName);
-
- return HSE_STATUS_ERROR;
- }
-
- //
- // Generate all sections
- //
-
- FillCGI (¶m);
- FillAccept (¶m);
- FillSystem (¶m);
- FillExtraHeaders (¶m);
-
- bError = !FillFormData (¶m);
-
- if (!bError)
- {
- //
- // Execute child process
- //
-
- hProcess = ExecuteChildProc (¶m);
- if (hProcess == INVALID_HANDLE_VALUE)
- {
- // Process may not exist. Log an error.
- LogError (gszBadProc, pECB);
- bError = TRUE;
- }
-
- if (!bError)
- {
- //
- // Allow it to finish - give it WAIT_EXT_TIMEOUT seconds
- //
-
- dwResult = WaitForSingleObject (hProcess, WAIT_EXT_TIMEOUT);
-
- if (dwResult == WAIT_FAILED)
- {
- dwResult = GetLastError ();
- if (dwResult == WAIT_TIMEOUT)
- {
- // App never finished! Log an error.
- LogError (gszNoProcEnd, pECB);
- bError = TRUE;
- }
-
- // else must be WAIT_OBJECT_0, and that's fine (but unusual)
- }
- }
-
-
- //
- // Move app output to the web server
- //
-
- if (!bError)
- {
- if (!GetDataFromFile (¶m))
- {
- // Error reading from disk. Log an error.
- LogError (gszOutDiskError, pECB);
- bError = TRUE;
- }
- }
- }
-
-
- //
- // Clean up
- //
-
- // Delete the temp files made in form decoding
- DWORD dwBufSize;
- LPTSTR lpszDelBuf;
- LPTSTR lpszThisStr;
- LPTSTR lpszNextStr;
- LPTSTR lpszEndOfStr;
- dwBufSize = 0x10000; // (assume this is big enough for now)
- lpszDelBuf = (LPTSTR) GlobalAlloc (GPTR, dwBufSize);
- if (lpszDelBuf)
- {
- dwBufSize = GetPrivateProfileSection (param.szFormExternal,
- lpszDelBuf,
- dwBufSize,
- param.szProfileName);
-
- lpszNextStr = lpszDelBuf;
- while (lpszNextStr[0])
- {
- //
- // Set lpszThisStr to start of file name.
- // Split pathname and length with a NULL.
- // Set lpszNextStr to the next string.
- //
-
- lpszNextStr = strchr (lpszNextStr, TEXT('='));
- lpszNextStr++;
- lpszEndOfStr = lpszNextStr + lstrlen (lpszNextStr);
- do {
- lpszEndOfStr--;
- } while (*lpszEndOfStr != TEXT(' '));
- lpszThisStr = lpszNextStr;
- lpszNextStr += lstrlen (lpszNextStr) + 1;
- *lpszEndOfStr = 0;
-
- DeleteFile (lpszThisStr);
- }
-
- GlobalFree ((HGLOBAL) lpszDelBuf);
- }
-
- // Clean up key list resources & delete content file
- FreeKeyList (param.hKeyList);
-
- // Delete the temp files we made
- DeleteFile (param.szProfileName);
- DeleteFile (param.szOutputFileName);
-
- return bError ? HSE_STATUS_ERROR : HSE_STATUS_SUCCESS;
- }
-
-
- //
- // GetVarAndWriteKey obtains an environment variable and saves it
- // to the specified key in the profile. This function cleans
- // up the Fill code a lot.
- //
-
- void GetVarAndWriteKey (PWCGIPARAMS pParam, LPCTSTR lpszSection,
- LPCTSTR lpszVar, LPCTSTR lpszKey)
- {
- TCHAR szBuffer[MAX_PATH];
- DWORD dwBufferSize;
- BOOL bReturn;
-
- // Call server to get environment variable
- dwBufferSize = MAX_PATH;
- bReturn = pParam->pECB->GetServerVariable (pParam->pECB->ConnID,
- (LPTSTR) lpszVar,
- szBuffer,
- &dwBufferSize);
-
- if (!bReturn)
- {
- // expected symbol is missing
- return;
- }
-
- // Write variable to profile if data exists
- if (szBuffer[0])
- {
- WritePrivateProfileString (lpszSection,
- lpszKey,
- szBuffer,
- pParam->szProfileName);
- }
- }
-
-
- //
- // Fill routines are used to move data from the server's environment
- // into the profile string.
- //
- // FillCGI handles the [CGI] section of the profile.
- //
-
- void FillCGI (PWCGIPARAMS pParam)
- {
- // Everything comes in pParam. We'll use
- // shorter variable names make things readable.
-
- LPCTSTR szProfile;
- LPCTSTR szSection;
- EXTENSION_CONTROL_BLOCK *ecb;
-
- szProfile = pParam->szProfileName;
- szSection = pParam->szCGI;
- ecb = pParam->pECB;
-
-
- //
- // Write information not kept as a server varaible.
- //
-
- MACRO_WriteKey (TEXT("Request Method"), ecb->lpszMethod);
- MACRO_WriteKey (TEXT("Query String"), ecb->lpszQueryString);
- MACRO_WriteKey (TEXT("Logical Path"), ecb->lpszPathInfo);
- MACRO_WriteKey (TEXT("Physical Path"), ecb->lpszPathTranslated);
- MACRO_WriteKey (TEXT("CGI Version"), TEXT("CGI/1.2 (Win)"));
-
-
- //
- // Get server variables and write the values to the profile
- //
-
- GetVarAndWriteKey (pParam, szSection,
- TEXT("SERVER_PROTOCOL"), TEXT("Request Protocol"));
-
- GetVarAndWriteKey (pParam, szSection,
- TEXT("SCRIPT_NAME"), TEXT("Referer"));
-
- GetVarAndWriteKey (pParam, szSection,
- TEXT("SERVER_SOFTWARE"), TEXT("Server Software"));
-
- GetVarAndWriteKey (pParam, szSection,
- TEXT("SERVER_NAME"), TEXT("Server Name"));
-
- GetVarAndWriteKey (pParam, szSection,
- TEXT("SERVER_PORT"), TEXT("Server Port"));
-
- GetVarAndWriteKey (pParam, szSection,
- TEXT("REMOTE_HOST"), TEXT("Remote Host"));
-
- GetVarAndWriteKey (pParam, szSection,
- TEXT("REMOTE_ADDR"), TEXT("Remote Address"));
-
- GetVarAndWriteKey (pParam, szSection,
- TEXT("AUTHTEXTYPE"), TEXT("Authentication Method"));
-
- GetVarAndWriteKey (pParam, szSection,
- TEXT("REMOTE_USER"), TEXT("Authenticated Username"));
-
-
- // Keys not supported:
- //
- // Executable Path
- // From
- // Server Admin
- // Authentication Realm (goes with Authenticated Username)
- }
-
- void FillAccept (PWCGIPARAMS pParam)
- {
- //
- // Accept section provides info about the client's capabilities. We use
- // the header information stored in the HTTP_ACCEPT envirnoment variable.
- //
- // The format of this variable is:
- //
- // type/subtype [;opt. parameters] [, type/subtype [;params]] [, ...]
- //
- // For example:
- // */*; q=0.300, audio/x-aiff, audio/basic, image/jpeg, image/gif, text/plain, text/html
- //
- // Windows CGI 1.2 breaks this into the [Accept] section of the profile.
- // The above example becomes:
- //
- // [Accept]
- // */*=q=0.300
- // audio/x-aiff=Yes
- // audio/basic=Yes
- // image/jpeg=Yes
- // image/gif=Yes
- // text/plain=Yes
- // text/html=Yes
- //
-
- DWORD dwBufferSize;
- BOOL bReturn;
- TCHAR *pChar, *pOpts;
- TCHAR szBuffer[MAX_PATH];
-
- // We'll use shorter variable names make things readable.
- LPCTSTR szProfile;
- LPCTSTR szSection;
- EXTENSION_CONTROL_BLOCK *ecb;
-
- szProfile = pParam->szProfileName;
- szSection = pParam->szAccept;
- ecb = pParam->pECB;
-
- //
- // Get the inbound accept line
- //
-
- dwBufferSize = MAX_PATH;
- bReturn = ecb->GetServerVariable (ecb->ConnID,
- TEXT("HTTP_ACCEPT"),
- szBuffer,
- &dwBufferSize);
-
-
- if (!bReturn)
- {
- // expected symbol is missing
- return;
- }
-
- //
- // Skip leading spaces and grab entire type/subtype[;opts] string
- //
-
- pChar = strtok (szBuffer, TEXT(" ,"));
- while (pChar)
- {
- pOpts = strchr (pChar, TEXT(';')); // look for opts, if any
-
- MACRO_WriteKey (pChar, pOpts == NULL ? TEXT("Yes") : pOpts);
-
- pChar = strtok (NULL, TEXT(" ,")); // get next type/subtype pair
- }
- }
-
- void FillSystem (PWCGIPARAMS pParam)
- {
- // MACRO_WriteKeyInt buffer
- char szVal[8];
-
- // Again we'll use shorter variable names make things readable.
- LPCTSTR szProfile;
- LPCTSTR szSection;
- EXTENSION_CONTROL_BLOCK *ecb;
-
- szProfile = pParam->szProfileName;
- szSection = pParam->szSystem;
- ecb = pParam->pECB;
-
- //
- // The [System] section must be filled out with GMT Offset, Debug Mode,
- // Output File and Content File. The Content File key is written in
- // PutDataInFile() below.
- //
- // GMT offset is the number of seconds added to GMT time to reach local
- // time. For example, PST = GMT - 8 hours; GMT offset would equal
- // -28,800. Win32 call GetTimeZoneInformation returns the number of
- // minutes to subtract from GMT (UTC) to get local time.
- //
- // So, GMT Offset = -60*TZI.Bias.
- //
-
- TIME_ZONE_INFORMATION tzi = {0};
- GetTimeZoneInformation (&tzi);
- MACRO_WriteKeyInt (TEXT("GMT Offset"), -60 * tzi.Bias);
-
- // See top of file for gszDebugMode setting.
-
- MACRO_WriteKey (TEXT("Debug Mode"), gszDebugMode);
- MACRO_WriteKey (TEXT("Output File"), pParam->szOutputFileName);
- }
-
- void FillExtraHeaders (PWCGIPARAMS pParam)
- {
- TCHAR *pChar, *pOpts, *pEnd;
- DWORD dwBufferSize;
- TCHAR szBuffer[4096];
- BOOL bReturn;
-
- // Use shorter variable names make things readable.
- LPCTSTR szProfile;
- LPCTSTR szSection;
- EXTENSION_CONTROL_BLOCK *ecb;
-
- szProfile = pParam->szProfileName;
- szSection = pParam->szExtraHeaders;
- ecb = pParam->pECB;
-
- // Any extra HTTP headers go in ALL_HTTP. We need to parse these out and
- // put them in the [Extra Headers] Section. The format of the ALL_HTTP
- // variable is:
- // varname: <varvalue>\r\n{...}\0
-
- // Retrieve ALL_HTTP
-
- dwBufferSize = sizeof (szBuffer);
- bReturn = ecb->GetServerVariable (ecb->ConnID,
- TEXT("ALL_HTTP"),
- szBuffer,
- &dwBufferSize);
-
- if (!bReturn)
- {
- // expected symbol is missing
- return;
- }
-
-
- //
- // Find lines, split key/data pair and write them to profile
- //
-
- pChar = szBuffer;
- while (*pChar)
- {
- if (*pChar == TEXT('\r') || *pChar == TEXT ('\n'))
- {
- pChar++;
- continue;
- }
-
- pOpts = strchr (pChar, TEXT(':')); // look for separator
- if (!pOpts)
- return;
- if (!*pOpts)
- return;
-
- pEnd = pOpts;
- while (*pEnd && *pEnd != TEXT('\r') && *pEnd != TEXT('\n'))
- pEnd++;
-
- *pOpts = 0; // split strings
- *pEnd = 0;
-
- MACRO_WriteKey (pChar, pOpts + 1);
-
- pChar = pEnd + 1;
- }
- }
-
-
- //
- // We have to parse the inbound data in a special way. The Windows CGI
- // spec says that a set of form data keys are to be set up based on
- // the inbound data. Four sections separate the inbound data:
- //
- // [Form Literal] holds short, text-based form keys.
- // [Form External] holds information about form keys 255 to 65535
- // bytes long, or those that have characters < value 32.
- // [Form Huge] holds information about from keys > 65536 bytes long.
- // [Form File] holds pathnames to files uploaded as form data.
- //
- // For [Form External], the inbound form key is stored in yet another
- // temporary file. A profile key is added specifying the file name and
- // length of the file to the Windows CGI application.
- //
- // For [Form Huge], the inbound form key is left in the content file,
- // and a profile key is added specifying the offset from the start
- // of the content file, as well as the length of the form key data.
- //
-
- //
- // First, a cleanup function for FillFormData.
- //
-
- void FFD_Cleanup (HKEYLIST hKeyList, HANDLE hContent, LPBYTE lpbyMem)
- {
- if (hKeyList)
- FreeKeyList (hKeyList);
-
- if (hContent != INVALID_HANDLE_VALUE)
- CloseHandle (hContent);
-
- if (lpbyMem)
- GlobalFree ((HGLOBAL) lpbyMem);
- }
-
-
- BOOL FillFormData (PWCGIPARAMS pParam)
- {
- HANDLE hContent;
- HANDLE hExternalFile;
- HKEYLIST hKeyList, hStepKey;
- LPCTSTR lpszKeyName;
- DWORD dwOffset, dwLength;
- BOOL bHasCtrlChars;
- int nInstance;
- TCHAR szKeyNameBuf[256];
- BYTE byKeyDataBuf[254];
- DWORD dwRead, dwWritten;
- LPBYTE lpbyExternalBuf = NULL;
- BOOL bReturn;
- TCHAR szExternalFileName[MAX_PATH];
-
- // MACRO_WriteKeyInt buffer
- char szVal[8];
-
- // Use shorter variable names make things readable.
- EXTENSION_CONTROL_BLOCK *ecb;
- LPCTSTR szSection;
- LPCTSTR szProfile;
-
- szProfile = pParam->szProfileName;
- ecb = pParam->pECB;
-
- if (ecb->cbTotalBytes == 0)
- LogError (gszNoDataDecoded, ecb);
-
- // Get the data sent as a form
- hKeyList = GetKeyList (ecb);
-
- // If NULL was returned, an error occured or there is no data
- if (!hKeyList)
- {
- return (ecb->cbTotalBytes == 0);
- }
-
- //
- // Open our content file
- //
- pParam->lpszContentFile = GetContentPath (hKeyList);
- hContent = CreateFile (pParam->lpszContentFile,
- GENERIC_READ,
- 0, // No sharing mode
- NULL, // Default security attribs
- OPEN_EXISTING,
- FILE_ATTRIBUTE_NORMAL,
- NULL // No template file
- );
-
- if (hContent == INVALID_HANDLE_VALUE)
- {
- FreeKeyList (hKeyList);
- LogError (gszParseDiskError, ecb);
- return FALSE;
- }
-
-
- //
- // Next we step through the keys, determining if they go in
- // [Form Literal], [Form External], [Form Huge], or
- // [Form File].
- //
-
- hStepKey = hKeyList;
- while (hStepKey)
- {
- hStepKey = GetKeyInfo (hStepKey, &lpszKeyName,
- &dwOffset, &dwLength,
- &bHasCtrlChars, &nInstance);
-
- //
- // If nInstance > 0, we must generate a new key name.
- //
- if (nInstance > 0)
- {
- wsprintf (szKeyNameBuf, TEXT("%s_%i"), lpszKeyName, nInstance);
- lpszKeyName = szKeyNameBuf;
- }
-
- //
- // If length < 255 and data is straight text, we put the
- // key in [Form Literal]
- //
- if (dwLength < 255 && !bHasCtrlChars)
- {
- // Move to data in the file
- SetFilePointer (hContent, dwOffset, NULL, FILE_BEGIN);
-
- // Read it
- bReturn = ReadFile (hContent, byKeyDataBuf, dwLength, &dwRead, NULL);
- if (!bReturn || dwRead != dwLength)
- {
- // Handle abnormal errors
- FFD_Cleanup (hKeyList, hContent, lpbyExternalBuf);
- LogError (gszParseDiskError, ecb);
- return FALSE;
- }
-
- // Write profile key
- byKeyDataBuf[dwLength] = 0;
- WritePrivateProfileString (pParam->szFormLiteral,
- lpszKeyName, (LPTSTR) byKeyDataBuf,
- szProfile);
- }
-
- //
- // If length < 65536 bytes, we put the data into another
- // temporary file, and we note the new temporary file
- // in [Form External].
- //
- else if (dwLength < 65535)
- {
- // Lazy memory allocation
- if (!lpbyExternalBuf)
- {
- lpbyExternalBuf = (LPBYTE) GlobalAlloc (GPTR, 65536);
-
- if (!lpbyExternalBuf)
- {
- // Handle abnormal errors
- FFD_Cleanup (hKeyList, hContent, lpbyExternalBuf);
- LogError (gszOutOfMemory, ecb);
- return FALSE;
- }
- }
-
- // Move to data in the file
- SetFilePointer (hContent, dwOffset, NULL, FILE_BEGIN);
-
- // Read it
- bReturn = ReadFile (hContent, lpbyExternalBuf, dwLength, &dwRead, NULL);
- if (!bReturn || dwRead != dwLength)
- {
- // Handle abnormal errors
- FFD_Cleanup (hKeyList, hContent, lpbyExternalBuf);
- LogError (gszParseDiskError, ecb);
- return FALSE;
- }
-
- // Create another temporary file
- if (GetTempFileName (pParam->szTempDir,
- pParam->szCGI, 0,
- szExternalFileName))
- {
- // Open temp file for writing
- hExternalFile = CreateFile (szExternalFileName,
- GENERIC_WRITE,
- 0, // No sharing mode
- NULL, // Default security attribs
- CREATE_ALWAYS,
- FILE_ATTRIBUTE_NORMAL |
- FILE_FLAG_SEQUENTIAL_SCAN,
- NULL // No template file
- );
- }
- else
- hExternalFile = INVALID_HANDLE_VALUE;
-
- // Check for errors
- if (hExternalFile == INVALID_HANDLE_VALUE)
- {
- // Handle abnormal errors
- FFD_Cleanup (hKeyList, hContent, lpbyExternalBuf);
- LogError (gszTempFileError, ecb);
- return FALSE;
- }
-
- // Write the data to this new file
- bReturn = WriteFile (hExternalFile, lpbyExternalBuf, dwLength,
- &dwWritten, NULL);
-
- // Check for errors
- if (!bReturn || dwWritten != dwLength)
- {
- FFD_Cleanup (hKeyList, hContent, lpbyExternalBuf);
- CloseHandle (hExternalFile);
- DeleteFile (szExternalFileName);
- LogError (gszWriteError, ecb);
- return FALSE;
- }
-
- //
- // Close temp file. See HttpExtensionProc for deletion.
- //
- CloseHandle (hExternalFile);
-
- //
- // Add key to [Form External] section
- //
- wsprintf ((LPTSTR) byKeyDataBuf, TEXT("%s %u"),
- szExternalFileName, dwLength);
-
- WritePrivateProfileString (pParam->szFormExternal,
- lpszKeyName, (LPTSTR) byKeyDataBuf,
- szProfile);
- }
-
- //
- // If length is 65536 or greater, just mark the location of
- // that data within the content file.
- //
- else
- {
- wsprintf ((LPTSTR) byKeyDataBuf, TEXT("%u %u"),
- dwOffset, dwLength);
-
- WritePrivateProfileString (pParam->szFormHuge,
- lpszKeyName, (LPTSTR) byKeyDataBuf,
- szProfile);
- }
- }
-
- //
- // Cleanup
- //
-
- CloseHandle (hContent);
- if (lpbyExternalBuf)
- GlobalFree ((HGLOBAL) lpbyExternalBuf);
-
- // See HttpExtensionProc for cleanup of key list
- pParam->hKeyList = hKeyList;
-
- //
- // Add to [CGI] and [System] sections to profile
- //
- szSection = pParam->szCGI;
- MACRO_WriteKey (TEXT("Content File"), pParam->lpszContentFile);
- MACRO_WriteKeyInt (TEXT("Content Length"), ecb->cbTotalBytes);
- MACRO_WriteKey (TEXT("Content Type"), ecb->lpszContentType);
-
- szSection = pParam->szSystem;
- MACRO_WriteKey (TEXT("Content File"), pParam->lpszContentFile);
-
- return TRUE;
- }
-
-
- //
- // ExecuteChildProc builds a command line string and starts the
- // Windows CGI app. It returns a process handle if successful.
- //
-
- HANDLE ExecuteChildProc (PWCGIPARAMS pParam)
- {
- STARTUPINFO si;
- PROCESS_INFORMATION pi;
- TCHAR szCmdLine[MAX_PATH * 2 + 2]; // two args, a space, and a null
- BOOL bReturn;
-
- // Build command line
- wsprintf (szCmdLine, "%s %s", gszAppName, pParam->szProfileName);
-
- ZeroMemory (&si, sizeof (STARTUPINFO));
-
- si.cb = sizeof (STARTUPINFO);
-
- // Create process, return success or failure
- bReturn = CreateProcess (gszAppName,
- szCmdLine,
- NULL, // default process security attrbs
- NULL, // default primary thread security
- FALSE, // don't inherit handles
- CREATE_DEFAULT_ERROR_MODE|CREATE_NEW_PROCESS_GROUP,
- NULL,
- NULL,
- &si,
- &pi);
-
- if (!bReturn)
- return INVALID_HANDLE_VALUE;
-
- return pi.hProcess;
- }
-
-
- //
- // GetDataFromFile reads the output file and sends all
- // data to the web client.
- //
-
- BOOL GetDataFromFile (PWCGIPARAMS pParam)
- {
- HANDLE hOutputFile;
- LPBYTE lpbyBuf;
- DWORD dwBufSize;
- DWORD dwRead;
- DWORD dwSend;
-
- // Once again, readability...
- EXTENSION_CONTROL_BLOCK *ecb;
- ecb = pParam->pECB;
-
- // Allocate a buffer for moving data
- dwBufSize = 16384;
- lpbyBuf = (LPBYTE) GlobalAlloc (GPTR, dwBufSize);
- if (!lpbyBuf)
- return FALSE;
-
- // Open output file
- hOutputFile = CreateFile (pParam->szOutputFileName,
- GENERIC_READ,
- 0, // No sharing mode
- NULL, // Default security attribs
- OPEN_EXISTING,
- FILE_ATTRIBUTE_NORMAL|
- FILE_FLAG_SEQUENTIAL_SCAN,
- NULL // No template file
- );
-
- // Handle errors
- if (hOutputFile == INVALID_HANDLE_VALUE)
- {
- GlobalFree ((HGLOBAL) lpbyBuf);
- return FALSE;
- }
-
- // Loop until all data is read from the file
- do {
- ReadFile (hOutputFile, lpbyBuf, dwBufSize, &dwRead, NULL);
-
- if (dwRead)
- {
- dwSend = dwRead;
- ecb->WriteClient (ecb->ConnID, lpbyBuf, &dwSend, 0);
- if (dwSend != dwRead)
- {
- // Handle communication error
- CloseHandle (hOutputFile);
- GlobalFree ((HGLOBAL) lpbyBuf);
- return FALSE;
- }
- }
- } while (dwRead);
-
- // Cleanup
- CloseHandle (hOutputFile);
- GlobalFree ((HGLOBAL) lpbyBuf);
-
- return TRUE;
- }
-
-
- //
- // LogError appends an error string to the application name,
- // and then copies a string into the log buffer. We use this
- // function to support UNICODE builds of this DLL.
- //
-
- void LogError (LPCTSTR lpszError, EXTENSION_CONTROL_BLOCK *pECB)
- {
- LPTSTR lptsBuf; // ts Hungarian means 'text string', safe for UNICODE
-
- // Alloc a buffer big enough for error string.
- // (add three for colon, space and null)
- lptsBuf = (LPTSTR) GlobalAlloc (GPTR,
- lstrlen (lpszError) +
- lstrlen (gszAppName) + 3);
- if (!lptsBuf)
- return; // can't log because of error
-
- // Build the error string
- wsprintf (lptsBuf, TEXT("%s: %s"), gszAppName, lpszError);
-
- #ifdef UNICODE
- WideCharToMultiByte (CP_ACP, WC_COMPOSITECHECK|WC_DEFAULTCHAR,
- lptsBuf, lstrlen (lptsError) + 1,
- pECB->lpszLogData, HSE_LOG_BUFFER_LEN);
- #else
- lstrcpyn (pECB->lpszLogData, lptsBuf, HSE_LOG_BUFFER_LEN);
- #endif
-
- GlobalFree ((HGLOBAL) lptsBuf);
- }
-
-