home *** CD-ROM | disk | FTP | other *** search
/ Tutto per Internet / Internet.iso / soft95 / Varie / server / is2wcgi.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1995-12-04  |  31.0 KB  |  1,028 lines

  1. //
  2. // IS2WCGI.CPP
  3. //
  4. // This sample Web Server Application allows a Windows CGI extension
  5. // to run as an ISAPI extension.  The sample translates the inbound
  6. // ISAPI environment into the private profile that the Windows CGI
  7. // app is expecting.  This sample implements Windows CGI version 1.3.
  8. //
  9. // The Windows CGI spec (authored by Robert B. Denny 
  10. // <rdenny@netcom.com>) can be found at:
  11. //
  12. //   http://website.ora.com/wsdocs/32demo/windows-cgi.html
  13. //
  14. // After building this DLL, copy it to the same directory your
  15. // Windows CGI application is in.  Rename the DLL to the same
  16. // name your application has, but keep the DLL extension.
  17. //
  18. // See LibMain for details on why you must rename the DLL.
  19. //
  20.  
  21. //
  22. // BETA NOTE: Only the 1.2 spec is implemented here.  The capabilities
  23. //            of spec version 1.3 will likely be added before product
  24. //            release.
  25. //
  26.  
  27. #define WIN32_LEAN_AND_MEAN  
  28. #include <windows.h>
  29. #include <httpext.h>
  30.  
  31. #include "keys.h"
  32.  
  33. #define WAIT_EXT_TIMEOUT 120000     // 120 secs
  34. extern TCHAR gszAppName[MAX_PATH];
  35.  
  36. //
  37. // This struct is used to pass profile information,
  38. // file names for temporary files, and an extension
  39. // control block pointer.
  40. //
  41. // One instance of this structure is delcared in
  42. // HttpExtensionProc, and its address is passed to 
  43. // all the helper functions.
  44. //
  45.  
  46. typedef struct
  47.     {
  48.     // Profile section names
  49.     LPCSTR szCGI;
  50.     LPCSTR szAccept;
  51.     LPCSTR szSystem;
  52.     LPCSTR szExtraHeaders;
  53.     LPCSTR szFormLiteral;
  54.     LPCSTR szFormExternal;
  55.     LPCSTR szFormHuge;
  56.     LPCSTR szFormFile;
  57.  
  58.     // File and path names
  59.     TCHAR szProfileName[MAX_PATH];
  60.     TCHAR szOutputFileName[MAX_PATH];
  61.     TCHAR szTempDir[MAX_PATH];
  62.     TCHAR szDataFileName[MAX_PATH];
  63.     LPCTSTR lpszContentFile;
  64.  
  65.     // Key list and content file
  66.     HKEYLIST hKeyList;
  67.  
  68.     // Info given by the www service
  69.     EXTENSION_CONTROL_BLOCK *pECB;
  70.     } WCGIPARAMS, *PWCGIPARAMS;
  71.  
  72.  
  73. // Prototypes
  74. void LogError (LPCTSTR lpszError, EXTENSION_CONTROL_BLOCK *pECB);
  75. void FillCGI (PWCGIPARAMS pParam);
  76. void FillAccept (PWCGIPARAMS pParam);
  77. void FillSystem (PWCGIPARAMS pParam);
  78. void FillExtraHeaders (PWCGIPARAMS pParam);
  79. BOOL FillFormData (PWCGIPARAMS pParam);
  80. HANDLE ExecuteChildProc (PWCGIPARAMS pParam);
  81. BOOL GetDataFromFile (PWCGIPARAMS pParam);
  82.  
  83. //
  84. // Error messages (for the log file)
  85. //
  86.  
  87. static TCHAR gszInDiskError[]       = TEXT("Error writing inbound client data to disk.");
  88. static TCHAR gszOutDiskError[]      = TEXT("Error reading outbound client data from disk.");
  89. static TCHAR gszBadProc[]           = TEXT("Could not start Windows CGI executable.");
  90. static TCHAR gszNoProcEnd[]         = TEXT("Windows CGI application never terminated.");
  91. static TCHAR gszParseDiskError[]    = TEXT("Error reading data file during form decoding.");
  92. static TCHAR gszContentDiskError[]  = TEXT("Could not save inbound data to content file.");
  93. static TCHAR gszOutOfMemory[]       = TEXT("Memory allocation request failed.");
  94. static TCHAR gszTempFileError[]     = TEXT("Could not create a temporary file.");
  95. static TCHAR gszWriteError[]        = TEXT("Error writing to a temporary file.");
  96. static TCHAR gszNoDataDecoded[]     = TEXT("No data sent by form.");
  97.  
  98. //
  99. // Debug mode - make "Yes" or "No"
  100. //
  101.  
  102. static TCHAR gszDebugMode[] = TEXT("No");
  103.  
  104. //
  105. // Macro is used in Fill routines below.
  106. //
  107.  
  108. #define MACRO_WriteKey(key,val) WritePrivateProfileString (szSection, key, val, szProfile)
  109. #define MACRO_WriteKeyInt(key,val) wsprintf(szVal,TEXT("%i"),val);WritePrivateProfileString(szSection, key, szVal, szProfile)
  110.  
  111. //
  112. //  BOOL WINAPI GetExtensionVersion (HSE_VERSION_INFO *pVersionInfo)
  113. //
  114. //  Return the version this server is built for.  See httpext.h for
  115. //  a prototype.  This function is required by the spec.
  116. //
  117.  
  118. BOOL WINAPI GetExtensionVersion (HSE_VERSION_INFO *pVersionInfo)
  119.     {
  120.     // set version to httpext.h version constants
  121.     pVersionInfo->dwExtensionVersion = MAKELONG (HSE_VERSION_MINOR, HSE_VERSION_MAJOR);
  122.  
  123.     lstrcpyn (pVersionInfo->lpszExtensionDesc,
  124.             TEXT("Sample Web Server Application"),
  125.             HSE_MAX_EXT_DLL_NAME_LEN);
  126.  
  127.     return TRUE;
  128.     } // GetExtensionVersion()
  129.  
  130.  
  131. //
  132. //  BOOL WINAPI HttpExtensionProc (EXTENSION_CONTROL_BLOCK *pECB)
  133. //
  134. //  This function does all of the work.  Once called it retrieves all of
  135. //  the environment data from the server via the GetServerVariable
  136. //  server function, reads all of the client data if it's not already
  137. //  available, packs it all up in accordance with the Windows CGI standard,
  138. //  calls the Windows CGI app, and then passes the returned data to the
  139. //  web client.
  140. //
  141.  
  142. DWORD WINAPI HttpExtensionProc (EXTENSION_CONTROL_BLOCK *pECB)
  143.     {
  144.     WCGIPARAMS param;
  145.     HANDLE hProcess;
  146.     DWORD dwResult;
  147.     BOOL bError;
  148.  
  149.     memset (¶m, 0, sizeof (WCGIPARAMS));
  150.  
  151.     param.szCGI = "CGI";
  152.     param.szAccept = "Accept";
  153.     param.szSystem = "System";
  154.     param.szExtraHeaders = "Extra Headers";
  155.     param.szFormLiteral = "Form Literal";
  156.     param.szFormExternal = "Form External";
  157.     param.szFormHuge = "Form Huge";
  158.  
  159.     param.pECB = pECB;
  160.  
  161.     //
  162.     // Windows CGI 1.3 apps are called with a command line of:
  163.     //     <WinCGIAppName> <WinCGIProfilePath>
  164.     //
  165.     // Most of the information goes in <WinCGIProfile>.
  166.     //
  167.     // This extension has the following tasks:
  168.     //
  169.     //  - Create a temporary file to hold the profile
  170.     //  - Fill all the profile keys
  171.     //  - Collect inbound client data and save it to disk
  172.     //  - Start the child process
  173.     //  - Wait for child to finish
  174.     //  - Write data back to the web client
  175.     //  - Clean up
  176.     //
  177.  
  178.     //
  179.     // First, we have to create a temporary file.  We use Win32 APIs.
  180.     //
  181.  
  182.     // Get path
  183.     GetTempPath (MAX_PATH, param.szTempDir);
  184.  
  185.     // Create profile and output file (always req'd by WCGI app)
  186.     if (!GetTempFileName (param.szTempDir, 
  187.                           param.szCGI, 0, 
  188.                           param.szProfileName))
  189.         {
  190.         // Unexpected error creating temp file.  Log an error.
  191.         LogError (gszTempFileError, pECB);
  192.  
  193.         return HSE_STATUS_ERROR;
  194.         }
  195.  
  196.     if (!GetTempFileName (param.szTempDir, 
  197.                           param.szCGI, 0, 
  198.                           param.szOutputFileName))
  199.         {
  200.         // Unexpected error creating temp file.  Log an error.
  201.         LogError (gszTempFileError, pECB);
  202.         DeleteFile (param.szProfileName);
  203.  
  204.         return HSE_STATUS_ERROR;
  205.         }
  206.  
  207.     //
  208.     // Generate all sections
  209.     //
  210.  
  211.     FillCGI (¶m);
  212.     FillAccept (¶m);
  213.     FillSystem (¶m);
  214.     FillExtraHeaders (¶m);
  215.     
  216.     bError = !FillFormData (¶m);
  217.  
  218.     if (!bError)
  219.         {
  220.         //
  221.         // Execute child process
  222.         //
  223.  
  224.         hProcess = ExecuteChildProc (¶m);
  225.         if (hProcess == INVALID_HANDLE_VALUE)
  226.             {
  227.             // Process may not exist.  Log an error.
  228.             LogError (gszBadProc, pECB);
  229.             bError = TRUE;
  230.             }
  231.  
  232.         if (!bError)
  233.             {
  234.             //
  235.             // Allow it to finish - give it WAIT_EXT_TIMEOUT seconds
  236.             //
  237.  
  238.             dwResult = WaitForSingleObject (hProcess, WAIT_EXT_TIMEOUT);
  239.  
  240.             if (dwResult == WAIT_FAILED)
  241.                 {
  242.                 dwResult = GetLastError ();
  243.                 if (dwResult == WAIT_TIMEOUT)
  244.                     {
  245.                     // App never finished!  Log an error.
  246.                     LogError (gszNoProcEnd, pECB);
  247.                     bError = TRUE;
  248.                     }
  249.  
  250.                 // else must be WAIT_OBJECT_0, and that's fine (but unusual)
  251.                 }
  252.             }
  253.  
  254.  
  255.         //
  256.         // Move app output to the web server
  257.         //
  258.  
  259.         if (!bError)
  260.             {
  261.             if (!GetDataFromFile (¶m))
  262.                 {
  263.                 // Error reading from disk.  Log an error.
  264.                 LogError (gszOutDiskError, pECB);
  265.                 bError = TRUE;
  266.                 }
  267.             }
  268.         }
  269.  
  270.  
  271.     //
  272.     // Clean up
  273.     //
  274.  
  275.     // Delete the temp files made in form decoding
  276.     DWORD dwBufSize;
  277.     LPTSTR lpszDelBuf;
  278.     LPTSTR lpszThisStr;
  279.     LPTSTR lpszNextStr;
  280.     LPTSTR lpszEndOfStr;
  281.     dwBufSize = 0x10000;        // (assume this is big enough for now)
  282.     lpszDelBuf = (LPTSTR) GlobalAlloc (GPTR, dwBufSize);
  283.     if (lpszDelBuf)
  284.         {
  285.         dwBufSize = GetPrivateProfileSection (param.szFormExternal, 
  286.                                               lpszDelBuf, 
  287.                                               dwBufSize, 
  288.                                               param.szProfileName);
  289.  
  290.         lpszNextStr = lpszDelBuf;
  291.         while (lpszNextStr[0])
  292.             {
  293.             //
  294.             // Set lpszThisStr to start of file name.
  295.             // Split pathname and length with a NULL.
  296.             // Set lpszNextStr to the next string.
  297.             //
  298.  
  299.             lpszNextStr = strchr (lpszNextStr, TEXT('='));
  300.             lpszNextStr++;
  301.             lpszEndOfStr = lpszNextStr + lstrlen (lpszNextStr);
  302.             do  {
  303.                 lpszEndOfStr--;
  304.                 } while (*lpszEndOfStr != TEXT(' '));
  305.             lpszThisStr = lpszNextStr;
  306.             lpszNextStr += lstrlen (lpszNextStr) + 1;
  307.             *lpszEndOfStr = 0;
  308.             
  309.             DeleteFile (lpszThisStr);
  310.             }
  311.  
  312.         GlobalFree ((HGLOBAL) lpszDelBuf);
  313.         }
  314.  
  315.     // Clean up key list resources & delete content file
  316.     FreeKeyList (param.hKeyList);
  317.  
  318.     // Delete the temp files we made
  319.     DeleteFile (param.szProfileName);
  320.     DeleteFile (param.szOutputFileName);
  321.  
  322.     return bError ? HSE_STATUS_ERROR : HSE_STATUS_SUCCESS;
  323.     }
  324.  
  325.  
  326. //
  327. // GetVarAndWriteKey obtains an environment variable and saves it
  328. // to the specified key in the profile.  This function cleans
  329. // up the Fill code a lot.
  330. //
  331.  
  332. void GetVarAndWriteKey (PWCGIPARAMS pParam, LPCTSTR lpszSection,
  333.                         LPCTSTR lpszVar, LPCTSTR lpszKey)
  334.     {
  335.     TCHAR szBuffer[MAX_PATH];
  336.     DWORD dwBufferSize;
  337.     BOOL bReturn;
  338.  
  339.     // Call server to get environment variable
  340.     dwBufferSize = MAX_PATH;
  341.     bReturn = pParam->pECB->GetServerVariable (pParam->pECB->ConnID,
  342.                                                (LPTSTR) lpszVar,
  343.                                                szBuffer,
  344.                                                &dwBufferSize);
  345.  
  346.     if (!bReturn)
  347.         {
  348.         // expected symbol is missing
  349.         return;
  350.         }
  351.  
  352.     // Write variable to profile if data exists
  353.     if (szBuffer[0])
  354.         {
  355.         WritePrivateProfileString (lpszSection,
  356.                                    lpszKey, 
  357.                                    szBuffer, 
  358.                                    pParam->szProfileName);
  359.         }
  360.     }
  361.  
  362.  
  363. //
  364. // Fill routines are used to move data from the server's environment
  365. // into the profile string.
  366. //
  367. // FillCGI handles the [CGI] section of the profile.
  368. //
  369.  
  370. void FillCGI (PWCGIPARAMS pParam)
  371.     {
  372.     // Everything comes in pParam.  We'll use
  373.     // shorter variable names make things readable.
  374.  
  375.     LPCTSTR szProfile;
  376.     LPCTSTR szSection;
  377.     EXTENSION_CONTROL_BLOCK *ecb;
  378.  
  379.     szProfile = pParam->szProfileName;
  380.     szSection = pParam->szCGI;
  381.     ecb = pParam->pECB;
  382.  
  383.  
  384.     //
  385.     // Write information not kept as a server varaible.
  386.     //
  387.  
  388.     MACRO_WriteKey (TEXT("Request Method"), ecb->lpszMethod);
  389.     MACRO_WriteKey (TEXT("Query String"),   ecb->lpszQueryString);
  390.     MACRO_WriteKey (TEXT("Logical Path"),   ecb->lpszPathInfo);
  391.     MACRO_WriteKey (TEXT("Physical Path"),  ecb->lpszPathTranslated);
  392.     MACRO_WriteKey (TEXT("CGI Version"),    TEXT("CGI/1.2 (Win)"));
  393.  
  394.  
  395.     //
  396.     // Get server variables and write the values to the profile
  397.     //
  398.  
  399.     GetVarAndWriteKey (pParam, szSection,
  400.                        TEXT("SERVER_PROTOCOL"), TEXT("Request Protocol"));
  401.  
  402.     GetVarAndWriteKey (pParam, szSection,
  403.                        TEXT("SCRIPT_NAME"), TEXT("Referer"));
  404.  
  405.     GetVarAndWriteKey (pParam, szSection,
  406.                        TEXT("SERVER_SOFTWARE"), TEXT("Server Software"));
  407.  
  408.     GetVarAndWriteKey (pParam, szSection,
  409.                        TEXT("SERVER_NAME"), TEXT("Server Name"));
  410.  
  411.     GetVarAndWriteKey (pParam, szSection,
  412.                        TEXT("SERVER_PORT"), TEXT("Server Port"));
  413.  
  414.     GetVarAndWriteKey (pParam, szSection,
  415.                        TEXT("REMOTE_HOST"), TEXT("Remote Host"));
  416.  
  417.     GetVarAndWriteKey (pParam, szSection,
  418.                        TEXT("REMOTE_ADDR"), TEXT("Remote Address"));
  419.  
  420.     GetVarAndWriteKey (pParam, szSection,
  421.                        TEXT("AUTHTEXTYPE"), TEXT("Authentication Method"));
  422.  
  423.     GetVarAndWriteKey (pParam, szSection,
  424.                        TEXT("REMOTE_USER"), TEXT("Authenticated Username"));
  425.  
  426.  
  427.     // Keys not supported:
  428.     //
  429.     // Executable Path
  430.     // From
  431.     // Server Admin
  432.     // Authentication Realm (goes with Authenticated Username)
  433.     }
  434.  
  435. void FillAccept (PWCGIPARAMS pParam)
  436.     {
  437.     //
  438.     // Accept section provides info about the client's capabilities.  We use
  439.     // the header information stored in the HTTP_ACCEPT envirnoment variable.
  440.     //
  441.     // The format of this variable is:
  442.     //
  443.     // type/subtype [;opt. parameters] [, type/subtype [;params]] [, ...]
  444.     //
  445.     // For example:
  446.     // */*; q=0.300, audio/x-aiff, audio/basic, image/jpeg, image/gif, text/plain, text/html
  447.     //
  448.     // Windows CGI 1.2 breaks this into the [Accept] section of the profile.
  449.     // The above example becomes:
  450.     //
  451.     // [Accept]
  452.     // */*=q=0.300
  453.     // audio/x-aiff=Yes
  454.     // audio/basic=Yes
  455.     // image/jpeg=Yes
  456.     // image/gif=Yes
  457.     // text/plain=Yes
  458.     // text/html=Yes
  459.     //
  460.  
  461.     DWORD dwBufferSize;
  462.     BOOL bReturn;
  463.     TCHAR *pChar, *pOpts;
  464.     TCHAR szBuffer[MAX_PATH];
  465.  
  466.     // We'll use shorter variable names make things readable.
  467.     LPCTSTR szProfile;
  468.     LPCTSTR szSection;
  469.     EXTENSION_CONTROL_BLOCK *ecb;
  470.  
  471.     szProfile = pParam->szProfileName;
  472.     szSection = pParam->szAccept;
  473.     ecb = pParam->pECB;
  474.  
  475.     //
  476.     // Get the inbound accept line
  477.     //
  478.  
  479.     dwBufferSize = MAX_PATH;
  480.     bReturn = ecb->GetServerVariable (ecb->ConnID,
  481.                                       TEXT("HTTP_ACCEPT"),
  482.                                       szBuffer,
  483.                                       &dwBufferSize);
  484.  
  485.     
  486.     if (!bReturn)
  487.         {
  488.         // expected symbol is missing
  489.         return;
  490.         }
  491.  
  492.     //
  493.     // Skip leading spaces and grab entire type/subtype[;opts] string
  494.     //
  495.  
  496.     pChar = strtok (szBuffer, TEXT(" ,"));
  497.     while (pChar)
  498.         {
  499.         pOpts = strchr (pChar, TEXT(';'));  // look for opts, if any
  500.  
  501.         MACRO_WriteKey (pChar, pOpts == NULL ? TEXT("Yes") : pOpts);
  502.  
  503.         pChar = strtok (NULL, TEXT(" ,")); // get next type/subtype pair
  504.         }
  505.     }
  506.  
  507. void FillSystem (PWCGIPARAMS pParam)
  508.     {
  509.     // MACRO_WriteKeyInt buffer
  510.     char szVal[8];
  511.  
  512.     // Again we'll use shorter variable names make things readable.
  513.     LPCTSTR szProfile;
  514.     LPCTSTR szSection;
  515.     EXTENSION_CONTROL_BLOCK *ecb;
  516.  
  517.     szProfile = pParam->szProfileName;
  518.     szSection = pParam->szSystem;
  519.     ecb = pParam->pECB;
  520.  
  521.     //
  522.     // The [System] section must be filled out with GMT Offset, Debug Mode,
  523.     // Output File and Content File.  The Content File key is written in
  524.     // PutDataInFile() below.
  525.     //
  526.     // GMT offset is the number of seconds added to GMT time to reach local
  527.     // time.  For example, PST = GMT - 8 hours; GMT offset would equal 
  528.     // -28,800.  Win32 call GetTimeZoneInformation returns the number of
  529.     // minutes to subtract from GMT (UTC) to get local time.
  530.     //
  531.     // So, GMT Offset = -60*TZI.Bias.
  532.     //
  533.     
  534.     TIME_ZONE_INFORMATION tzi = {0};
  535.     GetTimeZoneInformation (&tzi);
  536.     MACRO_WriteKeyInt (TEXT("GMT Offset"), -60 * tzi.Bias);
  537.  
  538.     // See top of file for gszDebugMode setting.
  539.     
  540.     MACRO_WriteKey (TEXT("Debug Mode"), gszDebugMode);
  541.     MACRO_WriteKey (TEXT("Output File"), pParam->szOutputFileName);
  542.     }
  543.  
  544. void FillExtraHeaders (PWCGIPARAMS pParam)
  545.     {
  546.     TCHAR *pChar, *pOpts, *pEnd;
  547.     DWORD dwBufferSize;
  548.     TCHAR szBuffer[4096];
  549.     BOOL bReturn;
  550.  
  551.     // Use shorter variable names make things readable.
  552.     LPCTSTR szProfile;
  553.     LPCTSTR szSection;
  554.     EXTENSION_CONTROL_BLOCK *ecb;
  555.  
  556.     szProfile = pParam->szProfileName;
  557.     szSection = pParam->szExtraHeaders;
  558.     ecb = pParam->pECB;
  559.  
  560.     // Any extra HTTP headers go in ALL_HTTP.  We need to parse these out and
  561.     // put them in the [Extra Headers] Section.  The format of the ALL_HTTP
  562.     // variable is:
  563.     //     varname: <varvalue>\r\n{...}\0
  564.  
  565.     // Retrieve ALL_HTTP
  566.  
  567.     dwBufferSize = sizeof (szBuffer);
  568.     bReturn = ecb->GetServerVariable (ecb->ConnID,
  569.                                       TEXT("ALL_HTTP"),
  570.                                       szBuffer,
  571.                                       &dwBufferSize);
  572.  
  573.     if (!bReturn)
  574.         {
  575.         // expected symbol is missing
  576.         return;
  577.         }
  578.  
  579.  
  580.     //
  581.     // Find lines, split key/data pair and write them to profile
  582.     //
  583.  
  584.     pChar = szBuffer;
  585.     while (*pChar)
  586.         {
  587.         if (*pChar == TEXT('\r') || *pChar == TEXT ('\n'))
  588.             {
  589.             pChar++;
  590.             continue;
  591.             }
  592.  
  593.         pOpts = strchr (pChar, TEXT(':'));  // look for separator
  594.         if (!pOpts)
  595.             return;
  596.         if (!*pOpts)
  597.             return;
  598.  
  599.         pEnd = pOpts;
  600.         while (*pEnd && *pEnd != TEXT('\r') && *pEnd != TEXT('\n'))
  601.             pEnd++;
  602.  
  603.         *pOpts = 0;     // split strings
  604.         *pEnd = 0;
  605.  
  606.         MACRO_WriteKey (pChar, pOpts + 1);
  607.         
  608.         pChar = pEnd + 1;
  609.         }
  610.     }
  611.  
  612.  
  613. //
  614. // We have to parse the inbound data in a special way.  The Windows CGI
  615. // spec says that a set of form data keys are to be set up based on
  616. // the inbound data.  Four sections separate the inbound data:
  617. //
  618. //  [Form Literal] holds short, text-based form keys.
  619. //  [Form External] holds information about form keys 255 to 65535 
  620. //                  bytes long, or those that have characters < value 32.
  621. //  [Form Huge] holds information about from keys > 65536 bytes long.
  622. //  [Form File] holds pathnames to files uploaded as form data.
  623. //
  624. // For [Form External], the inbound form key is stored in yet another
  625. // temporary file.  A profile key is added specifying the file name and
  626. // length of the file to the Windows CGI application.
  627. //
  628. // For [Form Huge], the inbound form key is left in the content file,
  629. // and a profile key is added specifying the offset from the start
  630. // of the content file, as well as the length of the form key data.
  631. //
  632.  
  633. //
  634. // First, a cleanup function for FillFormData.
  635. //
  636.  
  637. void FFD_Cleanup (HKEYLIST hKeyList, HANDLE hContent, LPBYTE lpbyMem)
  638.     {
  639.     if (hKeyList)
  640.         FreeKeyList (hKeyList);
  641.  
  642.     if (hContent != INVALID_HANDLE_VALUE)
  643.         CloseHandle (hContent);
  644.  
  645.     if (lpbyMem)
  646.         GlobalFree ((HGLOBAL) lpbyMem);
  647.     }
  648.  
  649.  
  650. BOOL FillFormData (PWCGIPARAMS pParam)
  651.     {
  652.     HANDLE hContent;
  653.     HANDLE hExternalFile;
  654.     HKEYLIST hKeyList, hStepKey;
  655.     LPCTSTR lpszKeyName;
  656.     DWORD dwOffset, dwLength;
  657.     BOOL bHasCtrlChars;
  658.     int nInstance;
  659.     TCHAR szKeyNameBuf[256];
  660.     BYTE byKeyDataBuf[254];
  661.     DWORD dwRead, dwWritten;
  662.     LPBYTE lpbyExternalBuf = NULL;
  663.     BOOL bReturn;
  664.     TCHAR szExternalFileName[MAX_PATH];
  665.  
  666.     // MACRO_WriteKeyInt buffer
  667.     char szVal[8];
  668.  
  669.     // Use shorter variable names make things readable.
  670.     EXTENSION_CONTROL_BLOCK *ecb;
  671.     LPCTSTR szSection;
  672.     LPCTSTR szProfile;
  673.  
  674.     szProfile = pParam->szProfileName;
  675.     ecb = pParam->pECB;
  676.  
  677.     if (ecb->cbTotalBytes == 0)
  678.         LogError (gszNoDataDecoded, ecb);
  679.  
  680.     // Get the data sent as a form
  681.     hKeyList = GetKeyList (ecb);
  682.  
  683.     // If NULL was returned, an error occured or there is no data
  684.     if (!hKeyList)
  685.         {
  686.         return (ecb->cbTotalBytes == 0);
  687.         }
  688.  
  689.     //
  690.     // Open our content file
  691.     //
  692.     pParam->lpszContentFile = GetContentPath (hKeyList);
  693.     hContent = CreateFile (pParam->lpszContentFile,
  694.                        GENERIC_READ,
  695.                        0,                       // No sharing mode
  696.                        NULL,                    // Default security attribs
  697.                        OPEN_EXISTING,
  698.                        FILE_ATTRIBUTE_NORMAL,   
  699.                        NULL                     // No template file
  700.                        );
  701.  
  702.     if (hContent == INVALID_HANDLE_VALUE)
  703.         {
  704.         FreeKeyList (hKeyList);
  705.         LogError (gszParseDiskError, ecb);
  706.         return FALSE;
  707.         }
  708.  
  709.  
  710.     //
  711.     // Next we step through the keys, determining if they go in
  712.     // [Form Literal], [Form External], [Form Huge], or 
  713.     // [Form File].
  714.     //
  715.  
  716.     hStepKey = hKeyList;
  717.     while (hStepKey)
  718.         {
  719.         hStepKey = GetKeyInfo (hStepKey, &lpszKeyName, 
  720.                                &dwOffset, &dwLength,
  721.                                &bHasCtrlChars, &nInstance);
  722.  
  723.         //
  724.         // If nInstance > 0, we must generate a new key name.
  725.         //
  726.         if (nInstance > 0)
  727.             {
  728.             wsprintf (szKeyNameBuf, TEXT("%s_%i"), lpszKeyName, nInstance);
  729.             lpszKeyName = szKeyNameBuf;
  730.             }
  731.  
  732.         //
  733.         // If length < 255 and data is straight text, we put the
  734.         // key in [Form Literal]
  735.         //
  736.         if (dwLength < 255 && !bHasCtrlChars)
  737.             {
  738.             // Move to data in the file
  739.             SetFilePointer (hContent, dwOffset, NULL, FILE_BEGIN);
  740.  
  741.             // Read it
  742.             bReturn = ReadFile (hContent, byKeyDataBuf, dwLength, &dwRead, NULL);
  743.             if (!bReturn || dwRead != dwLength)
  744.                 {
  745.                 // Handle abnormal errors
  746.                 FFD_Cleanup (hKeyList, hContent, lpbyExternalBuf);
  747.                 LogError (gszParseDiskError, ecb);
  748.                 return FALSE;
  749.                 }
  750.  
  751.             // Write profile key
  752.             byKeyDataBuf[dwLength] = 0;
  753.             WritePrivateProfileString (pParam->szFormLiteral, 
  754.                                         lpszKeyName, (LPTSTR) byKeyDataBuf,
  755.                                         szProfile);
  756.             }
  757.  
  758.         //
  759.         // If length < 65536 bytes, we put the data into another
  760.         // temporary file, and we note the new temporary file
  761.         // in [Form External].
  762.         //
  763.         else if (dwLength < 65535)
  764.             {
  765.             // Lazy memory allocation
  766.             if (!lpbyExternalBuf)
  767.                 {
  768.                 lpbyExternalBuf = (LPBYTE) GlobalAlloc (GPTR, 65536);
  769.  
  770.                 if (!lpbyExternalBuf)
  771.                     {
  772.                     // Handle abnormal errors
  773.                     FFD_Cleanup (hKeyList, hContent, lpbyExternalBuf);
  774.                     LogError (gszOutOfMemory, ecb);
  775.                     return FALSE;
  776.                     }
  777.                 }
  778.  
  779.             // Move to data in the file
  780.             SetFilePointer (hContent, dwOffset, NULL, FILE_BEGIN);
  781.  
  782.             // Read it
  783.             bReturn = ReadFile (hContent, lpbyExternalBuf, dwLength, &dwRead, NULL);
  784.             if (!bReturn || dwRead != dwLength)
  785.                 {
  786.                 // Handle abnormal errors
  787.                 FFD_Cleanup (hKeyList, hContent, lpbyExternalBuf);
  788.                 LogError (gszParseDiskError, ecb);
  789.                 return FALSE;
  790.                 }
  791.  
  792.             // Create another temporary file
  793.             if (GetTempFileName (pParam->szTempDir, 
  794.                                   pParam->szCGI, 0, 
  795.                                   szExternalFileName))
  796.                 {
  797.                 // Open temp file for writing
  798.                 hExternalFile = CreateFile (szExternalFileName,
  799.                                           GENERIC_WRITE,
  800.                                           0,                 // No sharing mode
  801.                                           NULL,              // Default security attribs
  802.                                           CREATE_ALWAYS,
  803.                                           FILE_ATTRIBUTE_NORMAL |
  804.                                           FILE_FLAG_SEQUENTIAL_SCAN,
  805.                                           NULL               // No template file
  806.                                           );
  807.                 }
  808.             else
  809.                 hExternalFile = INVALID_HANDLE_VALUE;
  810.  
  811.             // Check for errors
  812.             if (hExternalFile == INVALID_HANDLE_VALUE)
  813.                 {
  814.                 // Handle abnormal errors
  815.                 FFD_Cleanup (hKeyList, hContent, lpbyExternalBuf);
  816.                 LogError (gszTempFileError, ecb);
  817.                 return FALSE;
  818.                 }
  819.  
  820.             // Write the data to this new file
  821.             bReturn = WriteFile (hExternalFile, lpbyExternalBuf, dwLength, 
  822.                                  &dwWritten, NULL);
  823.  
  824.             // Check for errors
  825.             if (!bReturn || dwWritten != dwLength)
  826.                 {
  827.                 FFD_Cleanup (hKeyList, hContent, lpbyExternalBuf);
  828.                 CloseHandle (hExternalFile);
  829.                 DeleteFile (szExternalFileName);
  830.                 LogError (gszWriteError, ecb);
  831.                 return FALSE;
  832.                 }
  833.  
  834.             //
  835.             // Close temp file.  See HttpExtensionProc for deletion.
  836.             //
  837.             CloseHandle (hExternalFile);
  838.  
  839.             //
  840.             // Add key to [Form External] section
  841.             //
  842.             wsprintf ((LPTSTR) byKeyDataBuf, TEXT("%s %u"), 
  843.                       szExternalFileName, dwLength);
  844.  
  845.             WritePrivateProfileString (pParam->szFormExternal,
  846.                                        lpszKeyName, (LPTSTR) byKeyDataBuf,
  847.                                        szProfile);
  848.             }
  849.  
  850.         //
  851.         // If length is 65536 or greater, just mark the location of
  852.         // that data within the content file.
  853.         //
  854.         else
  855.             {
  856.             wsprintf ((LPTSTR) byKeyDataBuf, TEXT("%u %u"),
  857.                       dwOffset, dwLength);
  858.  
  859.             WritePrivateProfileString (pParam->szFormHuge,
  860.                                        lpszKeyName, (LPTSTR) byKeyDataBuf,
  861.                                        szProfile);
  862.             }
  863.         }
  864.  
  865.     //
  866.     // Cleanup
  867.     //
  868.  
  869.     CloseHandle (hContent);
  870.     if (lpbyExternalBuf)
  871.         GlobalFree ((HGLOBAL) lpbyExternalBuf);
  872.  
  873.     // See HttpExtensionProc for cleanup of key list
  874.     pParam->hKeyList = hKeyList;
  875.  
  876.     //
  877.     // Add to [CGI] and [System] sections to profile
  878.     //
  879.     szSection = pParam->szCGI;
  880.     MACRO_WriteKey (TEXT("Content File"), pParam->lpszContentFile);
  881.     MACRO_WriteKeyInt (TEXT("Content Length"), ecb->cbTotalBytes);
  882.     MACRO_WriteKey (TEXT("Content Type"), ecb->lpszContentType);
  883.  
  884.     szSection = pParam->szSystem;
  885.     MACRO_WriteKey (TEXT("Content File"), pParam->lpszContentFile);
  886.  
  887.     return TRUE;
  888.     }
  889.  
  890.  
  891. //
  892. // ExecuteChildProc builds a command line string and starts the
  893. // Windows CGI app.  It returns a process handle if successful.
  894. //
  895.  
  896. HANDLE ExecuteChildProc (PWCGIPARAMS pParam)
  897.     {
  898.     STARTUPINFO si;
  899.     PROCESS_INFORMATION pi;
  900.     TCHAR szCmdLine[MAX_PATH * 2 + 2];  // two args, a space, and a null
  901.     BOOL bReturn;
  902.  
  903.     // Build command line
  904.     wsprintf (szCmdLine, "%s %s", gszAppName, pParam->szProfileName);
  905.  
  906.     ZeroMemory (&si, sizeof (STARTUPINFO));
  907.  
  908.     si.cb = sizeof (STARTUPINFO);
  909.  
  910.     // Create process, return success or failure
  911.     bReturn = CreateProcess (gszAppName, 
  912.                           szCmdLine,
  913.                           NULL,         // default process security attrbs
  914.                           NULL,         // default primary thread security
  915.                           FALSE,        // don't inherit handles
  916.                           CREATE_DEFAULT_ERROR_MODE|CREATE_NEW_PROCESS_GROUP,
  917.                           NULL,
  918.                           NULL,
  919.                           &si,
  920.                           &pi);
  921.     
  922.     if (!bReturn)
  923.         return INVALID_HANDLE_VALUE;
  924.  
  925.     return pi.hProcess;
  926.     }
  927.  
  928.  
  929. //
  930. // GetDataFromFile reads the output file and sends all
  931. // data to the web client.
  932. //
  933.  
  934. BOOL GetDataFromFile (PWCGIPARAMS pParam)
  935.     {
  936.     HANDLE hOutputFile;
  937.     LPBYTE lpbyBuf;
  938.     DWORD dwBufSize;
  939.     DWORD dwRead;
  940.     DWORD dwSend;
  941.  
  942.     // Once again, readability...
  943.     EXTENSION_CONTROL_BLOCK *ecb;
  944.     ecb = pParam->pECB;
  945.  
  946.     // Allocate a buffer for moving data
  947.     dwBufSize = 16384;
  948.     lpbyBuf = (LPBYTE) GlobalAlloc (GPTR, dwBufSize);
  949.     if (!lpbyBuf)
  950.         return FALSE;
  951.  
  952.     // Open output file
  953.     hOutputFile = CreateFile (pParam->szOutputFileName,
  954.                        GENERIC_READ,
  955.                        0,                       // No sharing mode
  956.                        NULL,                    // Default security attribs
  957.                        OPEN_EXISTING,
  958.                        FILE_ATTRIBUTE_NORMAL|
  959.                        FILE_FLAG_SEQUENTIAL_SCAN,
  960.                        NULL                     // No template file
  961.                        );
  962.  
  963.     // Handle errors
  964.     if (hOutputFile == INVALID_HANDLE_VALUE)
  965.         {
  966.         GlobalFree ((HGLOBAL) lpbyBuf);
  967.         return FALSE;
  968.         }
  969.  
  970.     // Loop until all data is read from the file
  971.     do  {
  972.         ReadFile (hOutputFile, lpbyBuf, dwBufSize, &dwRead, NULL);
  973.  
  974.         if (dwRead)
  975.             {
  976.             dwSend = dwRead;
  977.             ecb->WriteClient (ecb->ConnID, lpbyBuf, &dwSend, 0);
  978.             if (dwSend != dwRead)
  979.                 {
  980.                 // Handle communication error
  981.                 CloseHandle (hOutputFile);
  982.                 GlobalFree ((HGLOBAL) lpbyBuf);
  983.                 return FALSE;
  984.                 }
  985.             }
  986.         } while (dwRead);
  987.  
  988.     // Cleanup
  989.     CloseHandle (hOutputFile);                                   
  990.     GlobalFree ((HGLOBAL) lpbyBuf);
  991.  
  992.     return TRUE;
  993.     }
  994.  
  995.  
  996. //
  997. // LogError appends an error string to the application name,
  998. // and then copies a string into the log buffer.  We use this
  999. // function to support UNICODE builds of this DLL.
  1000. //
  1001.  
  1002. void LogError (LPCTSTR lpszError, EXTENSION_CONTROL_BLOCK *pECB)
  1003.     {
  1004.     LPTSTR lptsBuf;     // ts Hungarian means 'text string', safe for UNICODE
  1005.  
  1006.     // Alloc a buffer big enough for error string.
  1007.     // (add three for colon, space and null)
  1008.     lptsBuf = (LPTSTR) GlobalAlloc (GPTR, 
  1009.                                     lstrlen (lpszError) + 
  1010.                                     lstrlen (gszAppName) + 3);
  1011.     if (!lptsBuf)
  1012.             return;     // can't log because of error
  1013.  
  1014.     // Build the error string
  1015.     wsprintf (lptsBuf, TEXT("%s: %s"), gszAppName, lpszError);
  1016.  
  1017. #ifdef UNICODE
  1018.     WideCharToMultiByte (CP_ACP, WC_COMPOSITECHECK|WC_DEFAULTCHAR,
  1019.                         lptsBuf, lstrlen (lptsError) + 1,
  1020.                         pECB->lpszLogData, HSE_LOG_BUFFER_LEN);
  1021. #else
  1022.     lstrcpyn (pECB->lpszLogData, lptsBuf, HSE_LOG_BUFFER_LEN);
  1023. #endif
  1024.  
  1025.     GlobalFree ((HGLOBAL) lptsBuf);
  1026.     }
  1027.  
  1028.