Purpose: Process a HTTP event This function is called whenever data comes in from a connected HTTP client. Should the data contain a complete HTTP request, the request is processed.

Return TypeFunction nameArguments
hzEcodehzHttpEvent::ProcessEvent(hzChain&,)

Declared in file: hzHttpServer.h
Defined in file : hzHttpServer.cpp

Function Logic:

0:START 1:unknown 2:items 3:unknown 4:items 5:unknown 6:items 7:items items 8:unknown 9:items 10:Goto stage_two 11:unknown 12:zi 13:unknown 14:items items 15:Return E_FORMAT 16:items 17:Return E_OK 18:m_ClientIP items 19:unknown 20:hSofar 21:nSnd 22:nFst 23:unknown 24:unknown 25:items zi 26:items 27:hSofar 28:unknown 29:unknown 30:items m_nHeaderLen 31:unknown 32:items items 33:unknown 34:items 35:unknown 36:items 37:items 38:unknown 39:items items 40:Return E_RANGE 41:unknown 42:items items 43:Return E_RANGE 44:unknown 45:hSofar m_pBuf 46:ph zi 47:unknown 48:zi m_eMethod 49:zi==HEAD 50:zi m_eMethod 51:zi==POST 52:zi m_eMethod 53:zi==OPTIONS 54:zi m_eMethod 55:zi==PUT 56:zi m_eMethod 57:zi==DELETE 58:zi m_eMethod 59:zi==TRACE 60:zi m_eMethod 61:zi==CONNECT 62:zi m_eMethod 63:bErr 64:unknown 65:unknown 66:* 67:* 68:unknown 69:items m_nQueryLen 70:unknown 71:unknown 72:* 73:* 74:unknown 75:bErr 76:unknown 77:bErr 78:items 79:unknown 80:unknown 81:bErr 82:zi 83:unknown 84:zi m_nVersion 85:zi==1.1\r\n 86:zi m_nVersion 87:zi==2.0\r\n 88:zi m_nVersion 89:bErr 90:unknown 91:unknown 92:unknown 93:unknown 94:zi 95:unknown 96:unknown 97:mkC zi 98:unknown 99:bErr 100:toupper(*mkA) 101:'A' 102:unknown 103:unknown 104:* 105:* 106:unknown 107:unknown 108:* 109:* 110:unknown 111:unknown 112:* 113:* 114:unknown 115:unknown 116:* 117:* 118:unknown 119:unknown 120:* 121:* items m_Auth 122:'C' 123:unknown 124:unknown 125:* 126:* 127:unknown 128:unknown 129:* 130:* 131:unknown 132:unknown 133:* 134:* 135:unknown 136:unknown 137:* 138:* items 139:unknown 140:items 141:unknown 142:unknown 143:unknown 144:xi 145:unknown 146:* 147:* items m_CookieSub 148:'F' 149:unknown 150:unknown 151:* 152:* 153:'H' 154:unknown 155:unknown 156:* 157:* 158:unknown 159:unknown 160:'I' 161:unknown 162:unknown 163:* 164:* m_LastMod 165:unknown 166:unknown 167:* 168:* 169:'K' 170:unknown 171:m_nConnection 172:'M' 173:unknown 174:unknown 175:* 176:* items 177:'P' 178:unknown 179:unknown 180:* 181:* 182:'R' 183:unknown 184:unknown 185:* 186:* m_Referer 187:'U' 188:unknown 189:unknown 190:* 191:* 192:unknown 193:unknown 194:* 195:* 196:'V' 197:unknown 198:unknown 199:* 200:* 201:'x' 202:'X' 203:unknown 204:unknown 205:* 206:* 207:unknown 208:unknown 209:* 210:* 211:unknown 212:unknown 213:* 214:* 215:unknown 216:unknown 217:* 218:*

Function body:

hzEcode hzHttpEvent::ProcessEvent (hzChain& ZI)
{
   //  Purpose: Process a HTTP event
   //  
   //  This function is called whenever data comes in from a connected HTTP client. Should the data contain a complete HTTP request, the request is processed.
   //  
   //  Arguments: 1) ZI  Input chain poplated by the incomming HTTP request or submission
   //  
   //  Returns: E_FORMAT If the request is malformed in some way
   //     E_ARGUMENT If the request does not indicate a URL or a host.
   //     E_OK  If the operation was successful.
   _hzfunc("hzHttpEvent::ProcessEvent") ;
   hzChain::BlkIter    bi ;        //  To get directly at input chain inner buffer
   hzChain         Head ;          //  The HTTP header
   hzChain         Word ;          //  For building tokens
   hzChain         deco ;          //  For decoding base64 values
   chIter          zi ;            //  Chain iterator
   chIter          xi ;            //  Chain iterator
   chIter          mkA ;           //  1st part of header line
   chIter          mkB ;           //  2nd part of header line
   chIter          mkC ;           //  End 2nd part of header line
   hzHttpFile      upload ;        //  To cope with file uploads
   hzPair          Pair ;          //  Name value pair
   const char*     i ;             //  Loop control
   char*           j ;             //  For string iteration
   char*           ph ;            //  Offset into m_pBuf ;
   uint64_t        cookie ;        //  Cookie value
   hzString        S ;             //  Temp string
   hzString        boundary ;      //  MIME boundary for multipart
   uint32_t        nLine = 0;      //  Header line number
   uint32_t        bErr = 0;       //  Format error
   uint32_t        hSofar ;        //  For header value extraction
   uint32_t        n ;             //  For cookie extraction
   uint32_t        len ;           //  Offset into buffer
   uint32_t        nFst ;          //  No of colon-space sequences in header
   uint32_t        nSnd ;          //  No of CR-NL sequences in header (excluding the last pair)
   if (!this)      Fatal("No Instance\n") ;
   if (!m_pLog)    Fatal("Cannot process requests. No logfile\n") ;
   if (!m_pCx)     Fatal("Cannot process requests. No client info\n") ;
   //  If we already have completed header part, skip
   m_Report.Printf("ProcessEvents: Input chain of %d bytes\n", ZI.Size()) ;
   m_Report << ZI ;
   //  Header complete?
   if (m_bHdrComplete)
   {
       //  if (_hzGlobal_Debug & HZ_DEBUG_SERVER)
           m_Report.Printf("HTTP HEADER COMPLETE: Goto stage 2 with Header of %d bytes (conn=%p)\n", bi.Data(), m_nHeaderLen, m_pCx) ;
       goto stage_two ;
   }
   //  Input too small?
   if (ZI.Size() < 80)
   {
       zi = ZI ;
       //  CONNECT attempt?
       if (!(*zi >&eq; ''A''&&*zi <&eq; ''Z'')||zi == "CONNECT ")
       {
           m_Report << "Invalid Header [" << ZI << "]\n" ;
           SetStatusIP(m_pCx->ClientIP(), HZ_IPSTATUS_BLACK_HTTP, 9000);
           return E_FORMAT ;
       }
       //  Return and wait for more data
       m_Report << "Incomplete Header [" << ZI << "]\n" ;
       return E_OK ;
   }
   m_ClientIP = m_pCx->ClientIP() ;
   m_Occur.SysDateTime() ;
   //  Establish header size
   if (!m_nHeaderLen)
   {
       //  Read up to end of first line to measure length required to accommodate the request path and any query or fragment
       nFst = nSnd = hSofar = 0;
       for (zi = ZI ; !zi.eof() && Head.Size() < HZ_MAX_HTTP_HDR ; zi++)
       {
           if (*zi == CHAR_CR && zi == "\r\n")
               { Head << "\r\n" ; zi += 2; break ; }
           Head.AddByte(*zi) ;
       }
       hSofar = Head.Size() ;
       //  Then get rest of HTTP header
       for (; !zi.eof() && Head.Size() < HZ_MAX_HTTP_HDR ; zi++)
       {
           if (*zi == CHAR_CR && zi == "\r\n\r\n")
               { Head << "\r\n\r\n" ; m_nHeaderLen = Head.Size() ; break ; }
           if (zi == ": ")
               { nFst++ ; hSofar++ ; }
           if (nFst > nSnd)
               hSofar++ ;
           if (zi == "\r\n")
               nSnd++ ;
           Head.AddByte(*zi) ;
       }
       if (!m_nHeaderLen)
       {
           m_Report.Printf("\n%s: REJECTED REQUEST (Excessive HTTP Header)\n", *m_Occur) ;
           SendError(HTTPMSG_NOTFOUND, "Excessive HTTP Header\n") ;
           return E_RANGE ;
       }
       if (m_nHeaderLen >&eq; HZ_MAX_HTTP_HDR)
       {
           m_Report.Printf("\n%s: REJECTED REQUEST (too large)\n", *m_Occur) ;
           SendError(HTTPMSG_ENTITY_TOO_LARGE, "Excessive HTTP Header\n") ;
           return E_RANGE ;
       }
       //  Extract HTTP header values
       if (hSofar < 1024)
           hSofar = 1024;
       ph = m_pBuf = new char[hSofar] ;
       zi = Head ;
       if      (zi == "GET ")      { zi += 4; m_eMethod = HTTP_GET ; }
       else if (zi == "HEAD ")     { zi += 5; m_eMethod = HTTP_HEAD ; }
       else if (zi == "POST ")     { zi += 5; m_eMethod = HTTP_POST ; }
       else if (zi == "OPTIONS ")  { zi += 8; m_eMethod = HTTP_OPTIONS ; }
       else if (zi == "PUT ")      { zi += 4; m_eMethod = HTTP_PUT ; }
       else if (zi == "DELETE ")   { zi += 7; m_eMethod = HTTP_DELETE ; }
       else if (zi == "TRACE ")    { zi += 6; m_eMethod = HTTP_TRACE ; }
       else if (zi == "CONNECT ")  { zi += 8; m_eMethod = HTTP_CONNECT ; }
       else
           bErr |= 0x01;
       //  Obtain the requested path and if present, the query and the fragment
       if (!bErr)
       {
           //  Get the requested path first
           for (m_pReqPATH = ph ; !zi.eof() && *zi != CHAR_SPACE && *zi != CHAR_QUERY && *zi != CHAR_HASH ; ph++, zi++)
               *ph = *zi ;
           *ph++ = 0;
           if (*zi == CHAR_QUERY)
           {
               //  Read until either a SPACE or a HASH
               zi++ ;
               m_nQueryLen = _setnvpairs(zi) ;
           }
           if (*zi == CHAR_HASH)
           {
               //  Read until a SPACE
               for (m_pReqFRAG = ph ; !zi.eof() && *zi == CHAR_SPACE ; ph++, zi++)
                   *ph = *zi ;
               *ph++ = 0;
           }
           if (*zi != CHAR_SPACE)
               bErr |= 0x02;
           if (!m_pReqPATH[0])
               bErr |= 0x02;
           zi++ ;
       }
       //  Obtain the HTTP version
       if (!bErr)
       {
           if (zi != "HTTP/")
               bErr |= 0x04;
           else
           {
               zi += 5;
               if      (zi == "1.0\r\n")   { zi += 5; m_nVersion = 0; }
               else if (zi == "1.1\r\n")   { zi += 5; m_nVersion = 1; }
               else if (zi == "2.0\r\n")   { zi += 5; m_nVersion = 2; }
               else
                   bErr |= 0x08;
           }
       }
       /*
       **  ** Now grab the other headers of interest. Note headers not of interest are ignored. The main objective is to reject HTTP requests that are malformed. The process assumes
       **    ** each header is in it's own line and is of the form "header_name: value\r\n".
       **           */
       //  for (nLine = 2 ; !zi.eof() && !bErr && nLine <= nSnd ; nLine++)
       for (nLine = 2; !zi.eof() && !bErr ; nLine++)
       {
           //  Should be at the start of a line so establish line contents
           if (zi == "\r\n")
               break ;
           //  Discover 1st half of header line (upto colon and space)
           for (mkA = mkB = mkC = zi ; !zi.eof() ; zi++)
           {
               if (*zi == CHAR_COLON && zi[1]== CHAR_SPACE)
               {
                   //  Discover 2nd part of line (from colon and space to end of line)
                   zi += 2;
                   for (len = 0,mkB = zi ; !zi.eof() ; len++, zi++)
                   {
                       if (*zi == CHAR_CR && zi[1]== CHAR_NL)
                           { mkC = zi ; zi += 2; break ; }
                   }
                   if (!len)
                       bErr |= 0x10;
                   break ;
               }
           }
           //  Now have a complete line
           switch  (toupper(*mkA))
           {
           case ''A'':
               if (mkA.Equiv("Accept"))            { for (m_pAccept = ph,          xi = mkB ; xi != mkC ; ph++, xi++) *ph = *xi ; *ph++ = 0; break ; }
               if (mkA.Equiv("Accept-Charset"))    { for (m_pAcceptCharset = ph,   xi = mkB ; xi != mkC ; ph++, xi++) *ph = *xi ; *ph++ = 0; break ; }
               if (mkA.Equiv("Accept-Language"))   { for (m_pAcceptLang = ph,      xi = mkB ; xi != mkC ; ph++, xi++) *ph = *xi ; *ph++ = 0; break ; }
               if (mkA.Equiv("Accept-Encoding"))   { for (m_pAcceptCode = ph,      xi = mkB ; xi != mkC ; ph++, xi++) *ph = *xi ; *ph++ = 0; break ; }
               if (mkA.Equiv("Authorization: basic"))
               {
                   for (j = ph, xi = mkB ; xi != mkC ; j++, xi++)
                       *j = *xi ;
                   *j++ = 0;
                   Base64Decode(deco, ph) ;
                   m_Auth = deco ;
               }
               break ;
           case ''C'':
               if (mkA.Equiv("Cache-Control"))     { for (m_pCacheControl = ph,    xi = mkB ; xi != mkC ; ph++, xi++) *ph = *xi ; *ph++ = 0; break ; }
               if (mkA.Equiv("Content-Type"))      { for (m_pContentType = ph,     xi = mkB ; xi != mkC ; ph++, xi++) *ph = *xi ; *ph++ = 0; break ; }
               if (mkA.Equiv("Client-ip"))         { for (m_pCliIP = ph,           xi = mkB ; xi != mkC ; ph++, xi++) *ph = *xi ; *ph++ = 0; break ; }
               if (mkA.Equiv("Content-Length"))    { for (j = ph, xi = mkB ; xi != mkC ; j++, xi++) *j = *xi ; *j++ = 0; m_nContentLen = *ph ? atoi(ph) : 0; break ; }
               if (mkA.Equiv("Connection"))        { m_nConnection = mkB.Equiv("keep-alive") ? 0: 15;break ; }
               if (mkA.Equiv("Cookie"))
               {
                   //  Only interested in cookies with names matching the _hz_ prefix
                   for (xi = mkB ; *xi ; xi++)
                   {
                       //  Cookie match?
                       if (xi == "_hz_")   //  _hzGlobal_Dissemino->m_CookieName)
                       {
                           //  Found a cookie begining with _hz so copy upto the semicolon
                           //  xi += _hzGlobal_HtmlApp->m_CookieName.Length() + 1 ;
                           xi += 5;    //  _hzGlobal_Dissemino->m_CookieName.Length() + 1 ;
                           for (j = ph, n = 0; *xi && n < 32&&*xi > CHAR_SPACE && *xi != CHAR_SCOLON ; j++, xi++, n++)
                               *j = *xi ;
                           *j++ = 0;
                           IsHexnum(cookie, ph) ;
                           m_CookieSub = cookie ;
                           break ;
                       }
                   }
               }
               break ;
           case ''F'': if (mkA.Equiv("From"))  { for (m_pFrom = ph, xi = mkB ; xi != mkC ; ph++, xi++) *ph = *xi ; *ph++ = 0; }    break ;
           case ''H'': if (mkA.Equiv("Host"))
                       {
                           for (m_pHost = ph, xi = mkB ; *xi != CHAR_COLON && xi != mkC ; ph++, xi++)
                               *ph = *xi ;
                           *ph++ = 0;
                       }
                       if (xi == CHAR_COLON)
                           for (; xi != mkC ; xi++) ;
                       break ;
           case ''I'': if (mkA.Equiv("If-Modified-Since")) { for (j = ph, xi = mkB ; xi != mkC ; j++, xi++) *j = *xi ; *j++ = 0; m_LastMod = ph ; break ; }
                       if (mkA.Equiv("If-None-Match"))     { for (m_pETag = ph, xi = mkB ; xi != mkC ; ph++, xi++) *ph = *xi ; *ph++ = 0; }
                       break ;
           case ''K'': if (mkA.Equiv("Keep-Alive"))
                           m_nConnection = 15;
                       break ;
           case ''M'': if (mkA.Equiv("Max-Forwards"))  { for (j = ph, xi = mkB ; xi != mkC ; j++, xi++) *j = *xi ; *j++ = 0; m_nMaxForwards = *ph ? atoi(ph) : 0; }    break ;
           case ''P'': if (mkA.Equiv("Pragma"))        { for (m_pPragma = ph,  xi = mkB ; xi != mkC ; ph++, xi++) *ph = *xi ; *ph++ = 0; } break ;
           case ''R'': if (mkA.Equiv("Referer"))       { for (m_pReferer = ph, xi = mkB ; xi != mkC ; ph++, xi++) *ph = *xi ; *ph++ = 0; m_Referer = m_pReferer ; }    break ;
           case ''U'': if (mkA.Equiv("User-Agent"))    { for (m_pUserAgent = ph, xi = mkB ; xi != mkC ; ph++, xi++) *ph = *xi ; *ph++ = 0; break ; }
                       if (mkA.Equiv("UA-CPU"))        { for (m_pProcessor = ph, xi = mkB ; xi != mkC ; ph++, xi++) *ph = *xi ; *ph++ = 0; }
                       break ;
           case ''V'': if (mkA.Equiv("Via"))
                           { for (m_pVia = ph, xi = mkB ; xi != mkC ; ph++, xi++) *ph = *xi ; *ph++ = 0; }
                       break ;
           case ''x'':
           case ''X'':
               if (mkA.Equiv("X-Forwarded-For"))       { for (m_pFwrdIP = ph,  xi = mkB ; xi != mkC ; ph++, xi++) *ph = *xi ; *ph++ = 0; break ; }
               if (mkA.Equiv("X-ProxyUser-IP"))        { for (m_pProxIP = ph,  xi = mkB ; xi != mkC ; ph++, xi++) *ph = *xi ; *ph++ = 0; break ; }
               if (mkA.Equiv("X-Forwarded-Host"))      { for (m_pXost = ph,    xi = mkB ; xi != mkC ; ph++, xi++) *ph = *xi ; *ph++ = 0; break ; }
               if (mkA.Equiv("X-Forwarded-Server"))    { for (m_pServer = ph,  xi = mkB ; xi != mkC ; ph++, xi++) *ph = *xi ; *ph++ = 0; }
               break ;
           default:
               break ;
           }
       }
       //  If no other errors, check we have an IP address
       if (m_ClientIP == IPADDR_BAD || m_ClientIP == IPADDR_NULL || m_ClientIP == IPADDR_LOCAL)
       {
           if (m_pCliIP)
               m_ClientIP = m_pCliIP ;
       }
       if (m_ClientIP == IPADDR_BAD || m_ClientIP == IPADDR_NULL || m_ClientIP == IPADDR_LOCAL)
       {
           if (m_pFwrdIP)
               m_ClientIP = m_pFwrdIP ;
       }
       if (m_ClientIP == IPADDR_BAD || m_ClientIP == IPADDR_NULL || m_ClientIP == IPADDR_LOCAL)
       {
           if (m_pProxIP)
               m_ClientIP = m_pProxIP ;
       }
       if (m_ClientIP == IPADDR_BAD || m_ClientIP == IPADDR_NULL || m_ClientIP == IPADDR_LOCAL)
       {
           //  bErr |= 0x20 ;
       }
       if (!m_pHost)
           bErr |= 0x40;
       if (bErr)
       {
           m_bMsgComplete = true ;
           m_Report.Printf("%s Sock %d/%d REQUEST [\n", *m_Occur, m_pCx->CliSocket(), m_pCx->CliPort()) ;
           m_Report << ZI ;
           m_Report << "]\n" ;
           if (bErr & 0x01)m_Report.Printf("Line 1: Failed to find HTTP Method\n") ;
           if (bErr & 0x02)m_Report.Printf("Line 1: Malformed HTTP resource request\n") ;
           if (bErr & 0x04)m_Report.Printf("Line 1: Invalid HTTP Version\n") ;
           if (bErr & 0x08)m_Report.Printf("Line %d: No space after colon\n", nLine) ;
           if (bErr & 0x10)m_Report.Printf("Line %d: Could not evaluate line\n", nLine) ;
           if (bErr & 0x20)m_Report.Printf("Could not detect client IP\n") ;
           if (bErr & 0x40)m_Report.Printf("No Host header supplied\n") ;
           if (m_eMethod == HTTP_CONNECT && (!m_pHost || m_pHost != _hzGlobal_Hostname))
           {
               //  This is an automatic ban
               if (!(bErr & 0x20))
               {
                   SetStatusIP(m_ClientIP, HZ_IPSTATUS_BLACK_PROT, 9000);
                   return E_FORMAT ;
               }
           }
           if (m_pBuf)             m_Report.Printf("m_pBuf           = %s\n", m_pBuf) ;
           if (m_pAccept)          m_Report.Printf("m_pAccept        = %s\n", m_pAccept) ;
           if (m_pAcceptCharset)   m_Report.Printf("m_pAcceptCharset = %s\n", m_pAcceptCharset) ;
           if (m_pAcceptLang)      m_Report.Printf("m_pAcceptLang    = %s\n", m_pAcceptLang) ;
           if (m_pAcceptCode)      m_Report.Printf("m_pAcceptCode    = %s\n", m_pAcceptCode) ;
           if (m_pCacheControl)    m_Report.Printf("m_pCacheControl  = %s\n", m_pCacheControl) ;
           if (m_pConnection)      m_Report.Printf("m_pConnection    = %s\n", m_pConnection) ;
           if (m_pContentType)     m_Report.Printf("m_pContentType   = %s\n", m_pContentType) ;
           if (m_pETag)            m_Report.Printf("m_pETag          = %s\n", m_pETag) ;
           if (m_pPragma)          m_Report.Printf("m_pPragma        = %s\n", m_pPragma) ;
           if (m_pUserAgent)       m_Report.Printf("m_pUserAgent     = %s\n", m_pUserAgent) ;
           if (m_pProcessor)       m_Report.Printf("m_pProcessor     = %s\n", m_pProcessor) ;
           if (m_pVia)             m_Report.Printf("m_pVia           = %s\n", m_pVia) ;
           if (m_pCliIP)           m_Report.Printf("m_pCliIP         = %s\n", m_pCliIP) ;
           if (m_pHost)            m_Report.Printf("m_pHost          = %s\n", m_pHost) ;
           if (m_pXost)            m_Report.Printf("m_pXost          = %s\n", m_pXost) ;
           if (m_pFwrdIP)          m_Report.Printf("m_pFwrdIP        = %s\n", m_pFwrdIP) ;
           if (m_pProxIP)          m_Report.Printf("m_pProxIP        = %s\n", m_pProxIP) ;
           if (m_pServer)          m_Report.Printf("m_pServer        = %s\n", m_pServer) ;
           if (m_pFrom)            m_Report.Printf("m_pFrom          = %s\n", m_pFrom) ;
           if (m_pReferer)         m_Report.Printf("m_pReferer       = %s\n", m_pReferer) ;
           if (m_pReqPATH)         m_Report.Printf("m_pReqPATH       = %s\n", m_pReqPATH) ;
           if (m_pReqFRAG)         m_Report.Printf("m_pReqFRAG       = %s\n", m_pReqFRAG) ;
           SendError(HTTPMSG_NOTFOUND, "SORRY! INTERNAL ERROR\n") ;
           return E_FORMAT ;
       }
       if (_hzGlobal_Debug & HZ_DEBUG_SERVER)
       {
           m_Report.Printf("%s Sock %d/%d REQUEST [\n", *m_Occur, m_pCx->CliSocket(), m_pCx->CliPort()) ;
           m_Report << ZI ;
           m_Report << "]\n" ;
       }
   }
   //  Header complete
   m_bHdrComplete = true ;
   //  The header has been processed and the content-length is known. The whole hit may not be in but we can test the header.
   //  m_Resource.UrlDecode() ;
stage_two:
   //  We now can test that the hit has been sent in full
   if (ZI.Size() < (m_nHeaderLen + m_nContentLen))
   {
       m_Report.Printf("Not recv in full. Hdr %d, cont %d, actual %d\n", m_nHeaderLen, m_nContentLen, ZI.Size()) ;
       return E_OK ;
   }
   if (ZI.Size() > (m_nHeaderLen + m_nContentLen))
       m_Report.Printf("Msg over: Hdr %d, cont %d, actual %d\n", m_nHeaderLen, m_nContentLen, ZI.Size()) ;
   m_bMsgComplete = true ;
   //  Obtain POST data if applicable
   zi = ZI ;
   zi += m_nHeaderLen ;
   if (m_eMethod == HTTP_POST)
   {
       if (m_pContentType)
       {
           for (n = 0,i = m_pContentType ; *i ; i++)
           {
               if (*i == CHAR_SCOLON)
                   { n = 1; continue ; }
               if (n)
               {
                   if (!memcmp(i, "boundary=", 9))
                       { i += 9; boundary = i ; break ; }
               }
           }
       }
       if (!boundary)
           _setnvpairs(zi) ;
       else
       {
           for (; !zi.eof() ;)
           {
               if (zi != boundary)
                   { zi++ ; continue ; }
               /*
               **  ** In the general case, data occurs in the form:-
               **      **  boundary\nContent-Disposition: form-data; name="fldname"
               **      **  data
               **      **  blank line.
               **      ** Note that in the empty field case, there are two blank lines (3 \r\n sequences)
               **      **
               **      ** In the file upload case in the form:-
               **      **  boundary\nContent-Disposition: form-data; name="fldname"; filename="filename"
               **      **  Content-Type: ...
               **      **  blank line
               **      **  filedata
               **      ** Note that the filedata is terminated by the appearence of the boundary on a line by itself and that the whole submission is
               **      ** terminated by the boundary followed directly by two minus signs.
               **                     */
               zi += boundary.Length() ;
               if (zi == "--")
                   break ;
               zi.Skipwhite() ;
               //  Content-Disposition?
               if (zi != "Content-Disposition: form-data; name=")
               {
                   m_Report.Printf("Malformed multipart form submission (case 1)\n") ;
                   for (; !zi.eof() && *zi != CHAR_NL ; zi++) ;
                   break ;
               }
               //  Get name part of name-value pair
               for (zi += 38;!zi.eof() && *zi != CHAR_DQUOTE ; zi++)
                   Word.AddByte(*zi) ;
               zi++ ;
               Pair.name = Word ;
               Word.Clear() ;
               //  Get value part of name-value pair
               if (*zi == CHAR_SCOLON)
               {
                   //  Malformed filename indicator?
                   if (zi != "; filename=")
                   {
                       m_Report.Printf("Malformed multipart form submission (case 2)\n") ;
                       for (; !zi.eof() && *zi != CHAR_NL ; zi++) ;
                       break ;
                   }
                   for (zi += 12;!zi.eof() && *zi != CHAR_DQUOTE ; zi++)
                       Word.AddByte(*zi) ;
                   zi++ ;
                   Pair.value = Word ;
                   Word.Clear() ;
                   upload.m_fldname = Pair.name ;
                   upload.m_filename = Pair.value ;
                   //  Now get file content
                   zi.Skipwhite() ;
                   if (zi != "Content-Type: ")
                   {
                       m_Report.Printf("Expected Content-Type for submitted file\n") ;
                       break ;
                   }
                   for (zi += 14;!zi.eof() && *zi != CHAR_NL ; zi++)
                   {
                       if (*zi == CHAR_CR)
                           continue ;
                       if (*zi == CHAR_NL)
                           break ;
                       Word.AddByte(*zi) ;
                   }
                   S = Word ;
                   Word.Clear() ;
                   upload.m_mime = Str2Mimetype(S) ;
                   if (_hzGlobal_Debug & HZ_DEBUG_SERVER)
                       m_Report.Printf("MIME type of %s is %d (%s)\n", *Pair.value, upload.m_mime, *S) ;
                   zi++ ;
                   if (zi == "\r\n")
                       zi += 2;
                   for (; !zi.eof() ; zi++)
                   {
                       if (zi == "\r\n--")
                       {
                           zi += 4;
                           if (zi == boundary)
                               break ;
                           upload.m_file << "\r\n--" ;
                           continue ;
                       }
                       upload.m_file.AddByte(*zi) ;
                   }
                   //  m_Files.Add(W) ;
                   m_Inputs.Add(Pair) ;
                   m_mapStrings.Insert(Pair.name, Pair.value) ;
                   m_Report.Printf("Field name/value %s=%s\n", *Pair.name, *Pair.value) ;
                   m_Uploads.Insert(upload.m_fldname, upload) ;
                   m_Report.Printf("Got file of %d bytes\n", upload.m_file.Size()) ;
               }
               else
               {
                   //  Expect \r\n then a line of data terminated by \r\n\r\n
                   //  Carrige return?
                   if (*zi != CHAR_CR)
                       m_Report.Printf("Warning fld not at CR, char=%c instead\n", *zi) ;
                   zi.Skipwhite() ;
                   for (; !zi.eof() ; zi++)
                   {
                       if (zi == "\r\n")
                       {
                           zi += 2;
                           if (zi == "--")
                           {
                               zi += 2;
                               if (zi == boundary)
                                   break ;
                           }
                           Word.AddByte(CHAR_NL) ;
                           continue ;
                       }
                       Word.AddByte(*zi) ;
                   }
                   Pair.value = Word ;
                   Word.Clear() ;
                   m_Inputs.Add(Pair) ;
                   m_mapStrings.Insert(Pair.name, Pair.value) ;
                   m_Report.Printf("Field name/value %s=%s\n", *Pair.name, *Pair.value) ;
               }
           }
       }
   }
   return E_OK ;
}