home *** CD-ROM | disk | FTP | other *** search
/ Tutto per Internet / Internet.iso / soft95 / Varie / server / KEYS.CPP < prev    next >
Encoding:
C/C++ Source or Header  |  1995-12-03  |  24.6 KB  |  854 lines

  1. #define WIN32_LEAN_AND_MEAN         // the bare essential Win32 API
  2. #include <windows.h>
  3. #include <httpext.h>
  4.  
  5. #include "keys.h"
  6.  
  7. //
  8. // If you want to record errors, modify this macro definition to
  9. // call your own logging function.  This sample does not save
  10. // error strings.
  11. //
  12. #define LOG(errorstring)
  13.  
  14. //
  15. // This file contains four externally used functions, and a bunch of 
  16. // helper functions used only in this file.
  17. //
  18. // Functions called in other source files:
  19. //
  20. //  GetKeyList      Determines if data was sent, and if it was, the
  21. //                  data is extracted by GetPostKeys or GetUrlKeys,
  22. //                  two private functions within this file.  A 
  23. //                  pointer to a linked list is returned (as a 
  24. //                  handle).
  25. //
  26. //  GetKeyInfo      Returns a pointer to the key name, an offset to 
  27. //                  the start of the data within the content file, 
  28. //                  the length of the data, a flag indicating if
  29. //                  the data has control characters in it, and an
  30. //                  instance number for duplicate key names.
  31. //
  32. //  FindKey         Sequentially searches linked list for key name.
  33. //
  34. //  FreeKeyList     Deallocates memory used by the linked list of
  35. //                  keys.  Also deletes content file.
  36. //
  37. //  GetContentPath  Returns a pointer to the content file's full 
  38. //                  path name.
  39. //
  40. // Helper functions called only in this source file:
  41. //
  42. //  GetPostedByte   Manages buffer fragmentation, an inherent
  43. //                  characteristic of ReadClient.  Blocks of data
  44. //                  sent by a client are retrieved one byte at a
  45. //                  time.  This works almost like _getch(), except
  46. //                  it returns web data, not keyboard data.
  47. //
  48. //  GetQueryByte    Similar to GetPostedByte, this function
  49. //                  extracts data from the query string.
  50. //
  51. //  HexDigitToInt   Returns the decimal value of a hex character.
  52. //
  53. //  GetFirstByte    Sets up POSDATA struct and calls GetNextByte.
  54. //                  Caller specifies function used to retrieve
  55. //                  data, either GetPostedByte or GetQueryByte.
  56. //
  57. //  GetNextByte     Uses GetInboundByte (specified in GetFirstByte)
  58. //                  to retrieve inbound data, and decodes it using
  59. //                  the URL encoding rules.
  60. //
  61. //  BKL_Alloc       Allocates memory used in GetPostKeys.
  62. //
  63. //  BKL_Dealloc     Deallocates memory used in GetPostKeys.
  64. //
  65. //  BKL_Abort       Cleans up all resources for abnormal exits from
  66. //                  GetPostKeys.
  67. //
  68. //  IsKeySeparator  Returns TRUE if character is one of "=\r\n&\0".
  69. //
  70. //  BuildKeyList    Given a data extraction function (i.e.
  71. //                  GetPostedByte or GetQueryByte), this function
  72. //                  converts all keys into a linked list of POSTKEY
  73. //                  structures.
  74. //
  75. //  GetPostKeys     Takes inbound data off the wire by calling
  76. //                  BuildKeyList with GetPostedByte as the extraction
  77. //                  function.
  78. //
  79. //  GetUrlKeys      Extracts data from the query string by calling
  80. //                  BuildKeyList with GetQueryByte as the extraction
  81. //                  function.
  82. //
  83. // The typedef for the linked list is kept privately in this file,
  84. // and GetKeyInfo isolates other source files from the implementation
  85. // details.
  86. //
  87.  
  88.  
  89. //
  90. // Constants for this source file only
  91. //
  92.  
  93. #define MAX_KEY_NAME_LENGTH 256     // maximum size of an inbound key name
  94. #define CONTENT_BUF_LENGTH 8192     // amount of content buffered before WriteFile call
  95.  
  96. #define GNB_NOTHING_LEFT 0          // GetNextByte return values
  97. #define GNB_DECODED_CHAR 1
  98. #define GNB_NORMAL_CHAR 2
  99.  
  100.  
  101. //
  102. // POSDATA struct is used with GetInboundByte to keep
  103. // track of the position within incoming data.
  104. // GETINBOUNDBYTE is a function pointer type.
  105. //
  106.  
  107. typedef struct _tagPOSDATA
  108.     {
  109.     EXTENSION_CONTROL_BLOCK *pECB;
  110.     int nCurrentPos;        // overall position
  111.     int nBufferLength;      // length of buffer
  112.     int nBufferPos;         // position within buffer
  113.     int nAllocLength;       // size of buffer as allocated
  114.     LPBYTE pData;
  115.     int (*GetInboundByte)(struct _tagPOSDATA *p);
  116.     } POSDATA, *PPOSDATA;
  117.  
  118. typedef int(*GETINBOUNDBYTE)(PPOSDATA p);
  119.  
  120.  
  121.  
  122. //
  123. // GetPostedByte returns a waiting character that is not
  124. // decoded yet.  We have this function to smooth out the
  125. // inbound data: the server gives us blocks of data, one at
  126. // a time, and there can be any number of blocks.
  127. //
  128. // For the first call, pPosData->nAllocLength must be zero,
  129. // and pECB must be set.
  130. //
  131.  
  132. int GetPostedByte (PPOSDATA pPosData)
  133.     {
  134.     int nBytesToCopy;
  135.  
  136.     // For readability only...
  137.     EXTENSION_CONTROL_BLOCK *pECB;
  138.     pECB = pPosData->pECB;
  139.  
  140.     //
  141.     // Initialize position struct on first call.
  142.     //
  143.  
  144.     if (!pPosData->nAllocLength)
  145.         {
  146.         // Initialize the members
  147.         pPosData->nCurrentPos = 0;
  148.         pPosData->nBufferPos = 0;
  149.         pPosData->nBufferLength = 0;
  150.         pPosData->nAllocLength = 0x10000;   // 65536 bytes
  151.  
  152.         // Allocate the memory
  153.         pPosData->pData = (LPBYTE) GlobalAlloc (GPTR, pPosData->nAllocLength);
  154.         }
  155.  
  156.     //
  157.     // Was memory allocated?  Is it still allocated?
  158.     // If not, return right away.
  159.     //
  160.  
  161.     if (!pPosData->pData)
  162.         {
  163.         LOG ("GetPostedByte: Buffer not allocated.");
  164.         return -1;
  165.         }
  166.  
  167.     //
  168.     // Check for end.  Deallocate and return if we're done.
  169.     //
  170.  
  171.     if ((DWORD) pPosData->nCurrentPos == pECB->cbTotalBytes)
  172.         {
  173.         GlobalFree ((HGLOBAL) pPosData->pData);
  174.         pPosData->pData = 0;
  175.         return -1;
  176.         }
  177.  
  178.     //
  179.     // Check for buffer not loaded.  Load if necessary.
  180.     //
  181.  
  182.     if (pPosData->nBufferPos == pPosData->nBufferLength)
  183.         {
  184.         //
  185.         // Fill the buffer with new inbound data.
  186.         // Request it via ReadClient if necessary.
  187.         //
  188.  
  189.         if (pECB->cbAvailable < 1)
  190.             {
  191.             // Calculate how much we should go and get
  192.             nBytesToCopy = pECB->cbTotalBytes - pPosData->nCurrentPos;
  193.             if (nBytesToCopy > pPosData->nAllocLength)
  194.                 nBytesToCopy = pPosData->nAllocLength;
  195.  
  196.             // Let's go get the data
  197.             if (!pECB->ReadClient (pECB->ConnID, pPosData->pData, (LPDWORD) &nBytesToCopy))
  198.                 {
  199.                 GlobalFree ((HGLOBAL) pPosData->pData);
  200.                 pPosData->pData = 0;
  201.  
  202.                 LOG ("GetPostedByte: Error reading data via ReadClient");
  203.                 return -1;
  204.                 }
  205.             }
  206.         else
  207.             {
  208.             // Take at most nAllocLength bytes of data
  209.             if (pECB->cbAvailable > (DWORD) (pPosData->nAllocLength))
  210.                 nBytesToCopy = pPosData->nAllocLength;
  211.             else
  212.                 nBytesToCopy = pECB->cbAvailable;
  213.  
  214.             // Copy the inbound data to our buffer
  215.             memcpy (pPosData->pData, 
  216.                     &pECB->lpbData[pPosData->nCurrentPos], 
  217.                     nBytesToCopy);
  218.  
  219.             // Account for removed data
  220.             pECB->cbAvailable -= nBytesToCopy;
  221.             }
  222.  
  223.         // Our buffer is now full
  224.         pPosData->nBufferLength = nBytesToCopy;
  225.         pPosData->nBufferPos = 0;
  226.  
  227.         // Make sure we have something
  228.         if (!nBytesToCopy)
  229.             {
  230.             GlobalFree ((HGLOBAL) pPosData->pData);
  231.             pPosData->pData = 0;
  232.             return -1;
  233.             }
  234.         }
  235.  
  236.     //
  237.     // Inc current pos, buffer pos, and return a character
  238.     //
  239.  
  240.     pPosData->nCurrentPos++;
  241.     pPosData->nBufferPos++;
  242.     return ((int) pPosData->pData[pPosData->nBufferPos - 1]);
  243.     }
  244.  
  245.  
  246. //
  247. // GetQueryByte returns a waiting character that is not
  248. // decoded yet.  We have this function to match GetPostedData.
  249. //
  250. // For the first call, pPosData->nAllocLength must be zero,
  251. // and pECB must be set.
  252. //
  253.  
  254. int GetQueryByte (PPOSDATA pPosData)
  255.     {
  256.     // For readability only...
  257.     EXTENSION_CONTROL_BLOCK *pECB;
  258.     pECB = pPosData->pECB;
  259.  
  260.     //
  261.     // Initialize position struct on first call.
  262.     //
  263.  
  264.     if (!pPosData->nAllocLength)
  265.         {
  266.         // Initialize the useful members
  267.         pPosData->nBufferPos = 0;
  268.         pPosData->nBufferLength = lstrlen ((LPCTSTR) pECB->lpszQueryString);
  269.         pPosData->nAllocLength = -1;
  270.         }
  271.  
  272.     //
  273.     // Check for end.  Deallocate and return if we're done.
  274.     //
  275.  
  276.     if (pPosData->nBufferPos == pPosData->nBufferLength)
  277.         return -1;
  278.  
  279.     //
  280.     // Inc buffer pos and return a character
  281.     //
  282.  
  283.     pPosData->nBufferPos++;
  284.     return ((int) pECB->lpszQueryString[pPosData->nBufferPos - 1]);
  285.     }
  286.  
  287.  
  288. //
  289. // Now that we have GetPostedByte, and GetQueryByte, we can 
  290. // build a more useful function that decodes URL-style 
  291. // encoded characters.
  292. //
  293. // Recall that there are two special cases for this encoding:
  294. //
  295. //  1. Each plus sign must be converted to a space
  296. //  2. A percent sign denotes a hex value-encoded character
  297. //
  298. // Percents are used to specify characters that are otherwise
  299. // illegal.  This includes percents themselves, ampersands,
  300. // control characters, and so on.
  301. //
  302. // GetNextByte returns the decoded byte, plus a flag indicating
  303. // normal character, decoded character, or failure.  See top of  
  304. // file for return value constants.
  305. //
  306.  
  307. //
  308. // HexDigitToInt simply converts a hex-based character to an int.
  309. //
  310.  
  311. int HexDigitToInt (TCHAR tc)
  312.     {
  313.     if (tc >= TEXT('0') && tc <= TEXT('9'))
  314.         return (tc - TEXT('0'));
  315.  
  316.     if (tolower (tc) >= TEXT('a') && tolower (tc) <= TEXT('f'))
  317.         return (tc - TEXT('a') + 10);
  318.  
  319.     return -1;
  320.     }
  321.  
  322. //
  323. // GetFirstByte eliminates the guesswork from initialization.
  324. // We call GetFirstByte with an uninitialized POSDATA structure,
  325. // and we call GetNextByte from there on.
  326. //
  327.  
  328. // forward declaration
  329. int GetNextByte (PPOSDATA pPosData, TCHAR *ptc);
  330.  
  331. int GetFirstByte (PPOSDATA pPosData, 
  332.                   EXTENSION_CONTROL_BLOCK *pECB, 
  333.                   TCHAR *ptc, GETINBOUNDBYTE GetInboundByte)
  334.     {
  335.     // Initialize struct
  336.     pPosData->nAllocLength = 0;
  337.     pPosData->pECB = pECB;
  338.     pPosData->GetInboundByte = GetInboundByte;
  339.  
  340.     // Make the call as usual
  341.     return GetNextByte (pPosData, ptc);
  342.     }
  343.  
  344. int GetNextByte (PPOSDATA pPosData, TCHAR *ptc)
  345.     {
  346.     int nChar;
  347.  
  348.     // Initialize character pointer
  349.     *ptc = 0;
  350.  
  351.     // Fetch the next inbound character
  352.     nChar = pPosData->GetInboundByte (pPosData);
  353.     if (nChar == -1)
  354.         return GNB_NOTHING_LEFT;
  355.  
  356.     // Plus signs: convert to spaces
  357.     if (nChar == '+')
  358.         {
  359.         *ptc = TEXT(' ');
  360.         return GNB_DECODED_CHAR;
  361.         }
  362.  
  363.     // Percent signs: convert hex values
  364.     else if (nChar == '%')
  365.         {
  366.         nChar = pPosData->GetInboundByte (pPosData);
  367.         if (nChar == -1 || HexDigitToInt (nChar) == -1)
  368.             return GNB_NOTHING_LEFT;
  369.  
  370.         *ptc = (TCHAR) (HexDigitToInt (nChar) << 4);
  371.  
  372.         nChar = pPosData->GetInboundByte (pPosData);
  373.         if (nChar == -1 || HexDigitToInt (nChar) == -1)
  374.             {
  375.             *ptc = 0;       // incomplete
  376.             return GNB_NOTHING_LEFT;
  377.             }
  378.  
  379.         *ptc |= (TCHAR) HexDigitToInt (nChar);
  380.  
  381.         return GNB_DECODED_CHAR;
  382.         }
  383.  
  384.     // Must be normal character then
  385.     *ptc = (TCHAR) nChar;
  386.  
  387.     return GNB_NORMAL_CHAR;
  388.     }
  389.  
  390.  
  391. //
  392. // Structure used in data processing - the elements of the
  393. // key list.
  394. //
  395.  
  396. typedef struct _tagPOSTKEY
  397.     {
  398.     int nInstance;      // used when key name is the same as another, normally 0
  399.     DWORD dwOffset;     // offset into content file
  400.     DWORD dwLength;     // length of data
  401.     BOOL bHasCtrlChars; // a character value < 32 is in data
  402.     struct _tagPOSTKEY *pNext;  // linked list
  403.     // key string appended to structure
  404.     // for the head key, the content file name is also appended
  405.     } POSTKEY, *PPOSTKEY;
  406.  
  407.  
  408.  
  409. //
  410. // These three helper functions isolates the memory allocation, 
  411. // deallocation and abnormal exit code.  They are used only to 
  412. // keep BuildKeyList readable.
  413. //
  414.  
  415. BOOL BKL_Alloc (LPTSTR *plpszKey, LPBYTE *plpbyBuf)
  416.     {
  417.     // Allocate a buffer for the key name
  418.     *plpszKey = (LPTSTR) GlobalAlloc (GPTR, MAX_KEY_NAME_LENGTH);
  419.     
  420.     if (!*plpszKey)
  421.         return FALSE;
  422.  
  423.     // Allocate a buffer for the content
  424.     *plpbyBuf = (LPBYTE) GlobalAlloc (GPTR, MAX_KEY_NAME_LENGTH);
  425.  
  426.     if (!*plpbyBuf)
  427.         {
  428.         GlobalFree ((HGLOBAL) *plpszKey);
  429.         return FALSE;
  430.         }
  431.     
  432.     return TRUE;
  433.     }
  434.  
  435. void BKL_Dealloc (LPTSTR *plpsz, LPBYTE *plpby)
  436.     {
  437.     if (*plpsz)
  438.         GlobalFree ((HGLOBAL) *plpsz);
  439.     if (*plpby)
  440.         GlobalFree ((HGLOBAL) *plpby);
  441.     }
  442.  
  443. void BKL_Abort (PPOSTKEY pHead, HANDLE hFile, LPTSTR lpszKey, LPBYTE lpbyBuf)
  444.     {
  445.     if (pHead)
  446.         FreeKeyList ((HKEYLIST) pHead);
  447.  
  448.     if (hFile != INVALID_HANDLE_VALUE)
  449.         CloseHandle (hFile);
  450.  
  451.     BKL_Dealloc (&lpszKey, &lpbyBuf);
  452.     }
  453.  
  454. //
  455. // Function used to identify key separators
  456. //
  457.  
  458. BOOL IsKeySeparator (TCHAR tc)
  459.     {
  460.     return (tc == '=' || tc == '\r' || tc == '\n' || tc == '&' || !tc);
  461.     }
  462.  
  463.  
  464. //
  465. // Now that we have a way to get a decoded byte from the stream,
  466. // we can parse POST data.  POST data comes in as:
  467. //
  468. //  key=data&key=data&key=data\r\n
  469. //
  470. // A linked list of keys is established, and the head node
  471. // of the list is returned.  A NULL indicates no keys or
  472. // an error.
  473. //
  474.  
  475. PPOSTKEY BuildKeyList (EXTENSION_CONTROL_BLOCK *pECB, 
  476.                        GETINBOUNDBYTE GetInboundByte)
  477.     {
  478.     PPOSTKEY pHead = NULL;      // head of linked list (the return val)
  479.     PPOSTKEY pTail = NULL;      // last member in linked list
  480.     PPOSTKEY pNewPostKey;       // pointer for unlinked, newly allocated objects
  481.     PPOSTKEY pListWalk;         // linked list walking pointer
  482.  
  483.     LPTSTR lpszKeyNameBuf;      // pointer to buffer, used in obtaining key name
  484.     int nPos;                   // position within key name buffer
  485.  
  486.     DWORD dwOffset;             // offset from start of content file
  487.     DWORD dwLength;             // length of key
  488.  
  489.     TCHAR tc;                   // general-purpose character
  490.     int nReturn;                // general-purpose return code
  491.  
  492.     POSDATA pd;                 // POSDATA struct needed in GetInboundByte
  493.  
  494.     LPBYTE lpbyContentBuf;      // buffer used in writing content to file
  495.     int nContentPos;            // position within content buffer
  496.     TCHAR szTempDir[MAX_PATH];  // directory of temporary files
  497.     TCHAR szTempPath[MAX_PATH]; // path of content file
  498.     HANDLE hDataFile;           // handle to content file
  499.     DWORD dwBytesWritten;       // used with WriteFile
  500.  
  501.     BOOL bHasCtrlChars;         // flag to detect ctrl chars
  502.  
  503.     // Call helper to allocate a buffer
  504.     if (!BKL_Alloc (&lpszKeyNameBuf, &lpbyContentBuf))
  505.         {
  506.         LOG ("BuildKeyList: Memory allocation failure");
  507.         return NULL;
  508.         }
  509.  
  510.     nContentPos = dwOffset = 0;
  511.  
  512.     // Get a temp file name
  513.     GetTempPath (MAX_PATH, szTempDir);
  514.     if (!GetTempFileName (szTempDir, 
  515.                          TEXT("postdata"), 0, 
  516.                          szTempPath))
  517.         {
  518.         LOG ("BuildKeyList: Error creating temporary file");
  519.         BKL_Dealloc (&lpszKeyNameBuf, &lpbyContentBuf);
  520.         return NULL;
  521.         }
  522.  
  523.     // Create the content file
  524.     hDataFile = CreateFile (szTempPath,
  525.                   GENERIC_READ | GENERIC_WRITE,
  526.                   0,                           // No sharing mode
  527.                   NULL,                        // Default security attribs
  528.                   CREATE_ALWAYS,
  529.                   FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,
  530.                   NULL                         // No template file
  531.                   );
  532.  
  533.     // Return if an error occured
  534.     if (hDataFile == INVALID_HANDLE_VALUE)
  535.         {
  536.         LOG ("BuildKeyList: Error opening temporary file");
  537.         BKL_Abort (pHead, hDataFile, lpszKeyNameBuf, lpbyContentBuf);
  538.         return NULL;
  539.         }
  540.  
  541.     //
  542.     // 'for' statement detects the start of a valid key name.
  543.     //
  544.     // To do inside 'for' loop:
  545.     //   Obtain key name
  546.     //   Write data to content file
  547.     //   Create POSTKEY object
  548.     //   Update links
  549.     //
  550.  
  551.     for (nReturn = GetFirstByte (&pd, pECB, &tc, GetInboundByte);
  552.          nReturn != GNB_NOTHING_LEFT;
  553.          nReturn = GetNextByte (&pd, &tc))
  554.         {
  555.         // If \r or \n, ignore and continue
  556.         if (tc == TEXT('\r') || tc == TEXT('\n'))
  557.             continue;
  558.  
  559.         // Get a key name
  560.         nPos = 0;
  561.         while (!IsKeySeparator (tc))
  562.             {
  563.             if (nPos < MAX_KEY_NAME_LENGTH)
  564.                 {
  565.                 lpszKeyNameBuf[nPos] = tc;
  566.                 nPos++;
  567.                 }
  568.  
  569.             nReturn = GetNextByte (&pd, &tc);
  570.             if (nReturn == GNB_NOTHING_LEFT)        // abrupt end!
  571.                 break;
  572.             }
  573.  
  574.         // If no equals sign or name too long,
  575.         // we have a browser formatting error
  576.         if (tc != '=' || nPos == MAX_KEY_NAME_LENGTH)
  577.             {
  578.             LOG ("BuildKeyList: Browser formatting error");
  579.  
  580.             BKL_Abort (pHead, hDataFile, lpszKeyNameBuf, lpbyContentBuf);
  581.             return NULL;
  582.             }
  583.  
  584.         // Truncate the name string, reset data info variables
  585.         lpszKeyNameBuf[nPos] = 0;
  586.         nPos++;
  587.         dwLength = 0;
  588.         bHasCtrlChars = FALSE;
  589.  
  590.         //
  591.         // Write data to the content file.
  592.         //
  593.         for (nReturn = GetNextByte (&pd, &tc);
  594.              !IsKeySeparator (tc) || nReturn == GNB_DECODED_CHAR;
  595.              nReturn = GetNextByte (&pd, &tc))
  596.             {
  597.             // Copy inbound data to a content buffer,
  598.             // include support for UNICODE
  599.  
  600.         #ifdef UNICODE
  601.             *((WORD *) (&lpbyContentBuf[nContentPos])) = tc;
  602.         #else
  603.             lpbyContentBuf[nContentPos] = tc;
  604.         #endif
  605.  
  606.             nContentPos += sizeof (TCHAR);
  607.             dwLength++;
  608.  
  609.             // Check for ctrl chars
  610.             if (tc < 0x20 || tc > 0x7e)
  611.                 bHasCtrlChars = TRUE;
  612.  
  613.             // If we have enough data, write buffer to disk
  614.             if (nContentPos == CONTENT_BUF_LENGTH)
  615.                 {
  616.                 if (!WriteFile (hDataFile, lpbyContentBuf, 
  617.                             nContentPos, &dwBytesWritten, NULL))
  618.                     {
  619.                     LOG ("BuildKeyList: Error writing to content file");
  620.                     BKL_Abort (pHead, hDataFile, lpszKeyNameBuf, lpbyContentBuf);
  621.                     return NULL;
  622.                     }
  623.  
  624.                 nContentPos = 0;
  625.                 }
  626.             }
  627.  
  628.         // Drain buffer
  629.         if (nContentPos)
  630.             {
  631.             if (!WriteFile (hDataFile, lpbyContentBuf, 
  632.                             nContentPos, &dwBytesWritten, NULL))
  633.                 {
  634.                 LOG ("BuildKeyList: Error writing to content file");
  635.                 BKL_Abort (pHead, hDataFile, lpszKeyNameBuf, lpbyContentBuf);
  636.                 return NULL;
  637.                 }
  638.  
  639.             nContentPos = 0;
  640.             }
  641.  
  642.         // Allocate a POSTKEY object, allocate extra for first key
  643.         if (pHead)
  644.             pNewPostKey = (PPOSTKEY) GlobalAlloc (GPTR, 
  645.                                                   sizeof (POSTKEY) + nPos);
  646.         else
  647.             pNewPostKey = (PPOSTKEY) GlobalAlloc (GPTR, 
  648.                                                   sizeof (POSTKEY) + nPos +
  649.                                                   lstrlen (szTempPath) + 1);
  650.         
  651.         // Check for valid pointer
  652.         if (!pNewPostKey)
  653.             {
  654.             LOG ("BuildKeyList: POSTKEY memory allocation failure");
  655.             BKL_Abort (pHead, hDataFile, lpszKeyNameBuf, lpbyContentBuf);
  656.             return NULL;
  657.             }
  658.  
  659.         //
  660.         // Set pNewPostKey members
  661.         //
  662.  
  663.         // Set nInstance
  664.         pNewPostKey->nInstance = 0;
  665.         pListWalk = pHead;
  666.         while (pListWalk)
  667.             {
  668.             // Check for duplicate key names
  669.             if (!lstrcmpi ((LPCTSTR) (&pListWalk[1]), lpszKeyNameBuf))
  670.                 pNewPostKey->nInstance++;
  671.             pListWalk = pListWalk->pNext;
  672.             }
  673.  
  674.         // Set dwOffset, dwLength, bHasCtrlChars
  675.         pNewPostKey->dwOffset = dwOffset;
  676.         pNewPostKey->dwLength = dwLength;
  677.         dwOffset += dwLength;
  678.         pNewPostKey->bHasCtrlChars = bHasCtrlChars;
  679.  
  680.         // Copy key name
  681.         lstrcpy ((LPTSTR) (&pNewPostKey[1]), lpszKeyNameBuf);
  682.  
  683.         // Link
  684.         if (pTail)
  685.             pTail->pNext = pNewPostKey;
  686.         else
  687.             {
  688.             // Append content file name to head key
  689.             lstrcpy (((LPTSTR) (&pNewPostKey[1])) + 
  690.                      lstrlen (lpszKeyNameBuf) + 1,
  691.                      szTempPath);
  692.  
  693.             // Set head
  694.             pHead = pNewPostKey;
  695.             }
  696.  
  697.         pNewPostKey->pNext = NULL;
  698.         pTail = pNewPostKey;
  699.         }
  700.  
  701.     CloseHandle (hDataFile);
  702.  
  703.     return pHead;
  704.     }
  705.  
  706. //
  707. // We are now pretty much done with anything complex. BuildKeyList 
  708. // will do all our parse work, so now we need a few wrappers to
  709. // make a nice, clean external interface.
  710. //
  711. // GetPostKeys calls BuildKeyList with GetPostedByte.
  712. //
  713. // GetUrlKeys calls BuildKeyList with GetQueryByte.
  714. //
  715.  
  716. PPOSTKEY GetPostKeys (EXTENSION_CONTROL_BLOCK *pECB)
  717.     {
  718.     return BuildKeyList (pECB, GetPostedByte);
  719.     }
  720.  
  721. PPOSTKEY GetUrlKeys (EXTENSION_CONTROL_BLOCK *pECB)
  722.     {
  723.     return BuildKeyList (pECB, GetQueryByte);
  724.     }
  725.  
  726.  
  727. //
  728. // And now we implement the external interface.  GetKeyList
  729. // examines the method and calls GetPostKeys or GetUrlKeys,
  730. // which ever is important.
  731. //
  732.  
  733. HKEYLIST GetKeyList (EXTENSION_CONTROL_BLOCK *pECB)
  734.     {
  735.     if (!lstrcmpi ((LPCTSTR) pECB->lpszMethod, TEXT("POST")))
  736.         {
  737.         LOG ("Method=POST");
  738.         return (HKEYLIST) GetPostKeys (pECB);
  739.         }
  740.  
  741.     else if (!lstrcmpi ((LPCTSTR) pECB->lpszMethod, TEXT("GET")))
  742.         {
  743.         LOG ("Method=GET");
  744.         return (HKEYLIST) GetUrlKeys (pECB);
  745.         }
  746.  
  747.     LOG ("Unknown method");
  748.     return NULL;
  749.     }
  750.  
  751.  
  752. //
  753. // GetKeyInfo is a wrapper for the POSTKEY linked list.
  754. // It returns the members of the supplied POSTKEY object.
  755. //
  756.  
  757. HKEYLIST GetKeyInfo (HKEYLIST hKey, LPCTSTR *plpszKeyName, 
  758.                      LPDWORD pdwOffset, LPDWORD pdwLength,
  759.                      BOOL *pbHasCtrlChars, LPINT pnInstance)
  760.     {
  761.     PPOSTKEY pPostKey;
  762.  
  763.     // Safety
  764.     if (!hKey)
  765.         return NULL;
  766.  
  767.     pPostKey = (PPOSTKEY) hKey;
  768.  
  769.     // Set the data members
  770.     *plpszKeyName = (LPCTSTR) (&pPostKey[1]);
  771.     *pdwOffset = pPostKey->dwOffset;
  772.     *pdwLength = pPostKey->dwLength;
  773.     *pbHasCtrlChars = pPostKey->bHasCtrlChars;
  774.     *pnInstance = pPostKey->nInstance;
  775.  
  776.     // Return a handle to the next object in the list
  777.     return ((HKEYLIST) pPostKey->pNext);
  778.     }
  779.  
  780.  
  781. //
  782. // FindKey sequentially searches the linked list for a given key.
  783. // The return handle points to the element within the linked list.
  784. // Use it in GetKeyInfo, but not FreeKeyList.
  785. //
  786.  
  787. HKEYLIST FindKey (HKEYLIST hKeyList, LPCTSTR lpszSearchName)
  788.     {
  789.     PPOSTKEY pFindKey;
  790.  
  791.     pFindKey = (PPOSTKEY) hKeyList;
  792.     while (pFindKey)
  793.         {
  794.         if (!lstrcmpi (lpszSearchName, (LPCTSTR) (&pFindKey[1])))
  795.             return ((HKEYLIST) pFindKey);
  796.  
  797.         pFindKey = pFindKey->pNext;
  798.         }
  799.  
  800.     return NULL;
  801.     }
  802.  
  803.  
  804. //
  805. // FreeKeyList deallocates all the objects in the key list.
  806. // The content file is also deleted.
  807. //
  808.  
  809. void FreeKeyList (HKEYLIST hHeadKey)
  810.     {
  811.     PPOSTKEY pObject;
  812.     PPOSTKEY pDel;
  813.  
  814.     // Safety
  815.     if (!hHeadKey)
  816.         return;
  817.  
  818.     // delete the content file
  819.     DeleteFile (GetContentPath (hHeadKey));
  820.  
  821.     // delete all objects in the list
  822.     pObject = (PPOSTKEY) hHeadKey;
  823.     while (pObject)
  824.         {
  825.         pDel = pObject;
  826.         pObject = pObject->pNext;
  827.  
  828.         GlobalFree ((HGLOBAL) pDel);
  829.         }
  830.     }
  831.  
  832.  
  833. //
  834. // GetContentPath returns the name of the content file associated
  835. // with a key list.  The name is a full path, suitable for CreateFile.
  836. //
  837.  
  838. LPCTSTR GetContentPath (HKEYLIST hHeadKey)
  839.     {
  840.     LPCTSTR lpszKeyName;
  841.     PPOSTKEY pHead;
  842.  
  843.     // Safety
  844.     if (!hHeadKey)
  845.         return NULL;
  846.  
  847.     // ContentPath follows POSTKEY struct and key name
  848.     pHead = (PPOSTKEY) hHeadKey;
  849.     lpszKeyName = (LPCTSTR) (&pHead[1]);
  850.  
  851.     return (lpszKeyName + lstrlen (lpszKeyName) + 1);
  852.     }
  853.  
  854.