home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / msdn_vcb / samples / vc98 / mfc / internet / httpsvr / reqsock.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1998-03-27  |  35.4 KB  |  1,448 lines

  1. // ReqSock.cpp : implementation of the CRequestSocket class
  2. //
  3. // This is a part of the Microsoft Foundation Classes C++ library.
  4. // Copyright (C) 1997-1998 Microsoft Corporation
  5. // All rights reserved.
  6. //
  7. // This source code is only intended as a supplement to the
  8. // Microsoft Foundation Classes Reference and related
  9. // electronic documentation provided with the library.
  10. // See these sources for detailed information regarding the
  11. // Microsoft Foundation Classes product.
  12.  
  13. #include "stdafx.h"
  14. #include "HttpSvr.h"
  15. #include "HttpDoc.h"
  16. #include "Http.h"
  17. #include "ReqSock.h"
  18. #include "Request.h"
  19. #include "resource.h"
  20.  
  21. #ifdef _DEBUG
  22. #define new DEBUG_NEW
  23. #undef THIS_FILE
  24. static char THIS_FILE[] = __FILE__;
  25. #endif
  26.  
  27. IMPLEMENT_DYNCREATE(CRequestSocket, CAsyncSocket)
  28.  
  29. #define CRLF    "\x0d\x0a"
  30.  
  31. SECURITY_ATTRIBUTES g_sa = {sizeof(SECURITY_ATTRIBUTES),NULL,TRUE};
  32.  
  33. CRequestSocket::CRequestSocket( void )
  34. {
  35. }
  36.  
  37. CRequestSocket::CRequestSocket( CHttpSvrDoc* pDoc )
  38. {
  39. #ifdef IMPL_CGI
  40.     m_pThread = NULL;
  41.     m_pCancel = NULL;
  42. #endif // IMPL_CGI
  43.     m_bKilled = FALSE;
  44.     m_nRefs = 1;
  45.     m_reqStatus = REQ_REQUEST;
  46.     m_buf.SetSize( 1024 );
  47.     m_cbOut = 0;
  48.     m_hFile = INVALID_HANDLE_VALUE;
  49.     m_pRequest = NULL;
  50.     m_pDoc = pDoc;
  51. }
  52.  
  53. CRequestSocket::~CRequestSocket( void )
  54. {
  55.     // JIC....
  56. #ifdef IMPL_CGI
  57.     if ( m_pCancel )
  58.     {
  59.         if ( m_pThread )
  60.         {
  61.             DWORD dwCode;
  62.             // signal a cancel if still running....
  63.             if ( ::GetExitCodeThread( m_pThread->m_hThread, &dwCode )
  64.                 && dwCode == STILL_ACTIVE )
  65.             {
  66.                 // signal a cancel....
  67.                 m_pCancel->SetEvent();
  68.                 // wait for the thread to die....
  69.                 WaitForSingleObject( m_pThread->m_hThread, INFINITE );
  70.             }
  71.             // kill the object...
  72.             delete m_pThread;
  73.         }
  74.         delete m_pCancel;
  75.     }
  76. #endif // IMPL_CGI
  77.  
  78.     if ( m_hFile )
  79.         CloseHandle( m_hFile );
  80.  
  81.     if ( m_pRequest )
  82.     {
  83.         // release our hold on the request object....
  84.         m_pRequest->m_bDone = TRUE;
  85.         m_pRequest->Release();
  86.     }
  87. }
  88.  
  89. void CRequestSocket::OnReceive(int nErrorCode)
  90. {
  91.     if ( m_pRequest == NULL )
  92.     {
  93.         // new request....
  94.         m_pRequest = new CRequest;
  95.         m_bKeepOpen = m_bWantKeepOpen = FALSE;
  96.     }
  97.     if ( m_pRequest )
  98.     {
  99.         // get the bytes....
  100.         int nBytes = Receive( m_buf.GetData(), m_buf.GetSize() );
  101.         if ( nBytes != SOCKET_ERROR )
  102.         {
  103.             int ndx = 0;
  104.             switch ( m_reqStatus )
  105.             {
  106.             case REQ_REQUEST:
  107.             case REQ_HEADER:
  108.                 while( GetLine( m_buf, nBytes, ndx ) == TRUE )
  109.                 {
  110.                     if ( !m_strLine.IsEmpty() )
  111.                         ProcessLine();
  112.                     else
  113.                     {
  114.                         m_reqStatus = REQ_BODY;
  115.                         break;
  116.                     }
  117.                 }
  118.                 // break if we're not looking for the body....
  119.                 if ( m_reqStatus != REQ_BODY )
  120.                     break;
  121.                 // stop if no body sent....
  122.                 if ( !BodySent() )
  123.                 {
  124.                     m_reqStatus = REQ_DONE;
  125.                     break;
  126.                 }
  127.                 // else fall through....
  128.             case REQ_BODY:
  129.                 AddToBody( nBytes, ndx );
  130.                 break;
  131.             }
  132.             if ( m_reqStatus == REQ_DONE )
  133.             {
  134.                 m_buf.SetSize(0);
  135.                 if ( !StartResponse() )
  136.                     AsyncSelect( FD_WRITE | FD_CLOSE );
  137.             }
  138.         }
  139.         else
  140.             nBytes = GetLastError();
  141.     }
  142.     else
  143.     {
  144.         // couldn't allocate request object....
  145.         ShutDown( both );
  146.         m_bKilled = TRUE;
  147.         Release();
  148.     }
  149. }
  150.  
  151. void CRequestSocket::OnSend(int nErrorCode)
  152. {
  153.     int nBytes = Send( m_buf.GetData(), m_cbOut );
  154.     if ( nBytes == SOCKET_ERROR )
  155.     {
  156.         if ( GetLastError() != WSAEWOULDBLOCK )
  157.         {
  158.             ShutDown( both );
  159.             m_bKilled = TRUE;
  160.             Release();
  161.         }
  162.         else
  163.             AsyncSelect( FD_WRITE | FD_CLOSE );
  164.     }
  165.     else if ( nBytes < m_cbOut )
  166.     {
  167.         // still got some left....
  168.         m_buf.RemoveAt( 0, nBytes );
  169.         m_cbOut -= nBytes;
  170.         // adjust the bytes-sent value for the request....
  171.         m_pRequest->m_cbSent += nBytes;
  172.         // set up for next write....
  173.         AsyncSelect( FD_WRITE | FD_CLOSE );
  174.     }
  175.     else
  176.     {
  177.         // adjust the bytes-sent value for the request....
  178.         m_pRequest->m_cbSent += nBytes;
  179.         // if we're not done with the file....
  180.         if ( m_hFile != INVALID_HANDLE_VALUE )
  181.         {
  182.             DWORD dwRead = 0;
  183.             // read next chunk....
  184.             ReadFile( m_hFile, m_buf.GetData(),
  185.                 m_buf.GetSize(), &dwRead, NULL );
  186.             if ( dwRead > 0 )
  187.                 m_cbOut = dwRead;
  188.             else
  189.             {
  190.                 // no more data to send....
  191.                 CloseHandle( m_hFile );
  192.                 m_hFile = INVALID_HANDLE_VALUE;
  193.             }
  194.         }
  195.         // see if we need to keep going....
  196.         if ( m_hFile != INVALID_HANDLE_VALUE )
  197.             AsyncSelect( FD_WRITE | FD_CLOSE );
  198.         else
  199.         {
  200.             // eh, we're outta here....
  201.             ShutDown( both );
  202.             m_bKilled = TRUE;
  203.             Release();
  204.         }
  205.     }
  206. }
  207.  
  208. void CRequestSocket::OnClose(int nErrorCode)
  209. {
  210.     m_bKilled = TRUE;
  211.     Release();
  212. }
  213.  
  214. BOOL CRequestSocket::GetLine( const CByteArray& bytes, int nBytes, int& ndx )
  215. {
  216.     BOOL bLine = FALSE;
  217.     while ( bLine == FALSE && ndx < nBytes )
  218.     {
  219.         char ch = (char)(bytes.GetAt( ndx ));
  220.         switch( ch )
  221.         {
  222.         case '\r': // ignore
  223.             break;
  224.         case '\n': // end-of-line
  225.             bLine = TRUE;
  226.             break;
  227.         default:   // other....
  228.             m_strLine += ch;
  229.             break;
  230.         }
  231.         ++ndx;
  232.     }
  233.     return bLine;
  234. }
  235.  
  236. void CRequestSocket::ProcessLine( void )
  237. {
  238.     int ndx;
  239.     switch( m_reqStatus )
  240.     {
  241.     case REQ_REQUEST:
  242.         ndx = m_strLine.Find( ' ' );
  243.         if ( ndx != -1 )
  244.         {
  245.             m_pRequest->m_strMethod = m_strLine.Left( ndx );
  246.             m_strLine = m_strLine.Mid( ndx+1 );
  247.             m_strLine.TrimLeft();
  248.             ndx = m_strLine.Find( ' ' );
  249.             if ( ndx == -1 )
  250.             {
  251.                 m_pRequest->m_strURL = m_strLine;
  252.                 m_pRequest->m_strURL.TrimRight();
  253.                 m_reqStatus = REQ_SIMPLE;
  254.             }
  255.             else
  256.             {
  257.                 m_pRequest->m_strURL = m_strLine.Left( ndx );
  258.                 m_pRequest->m_strVersion = m_strLine.Mid( ndx+1 );
  259.                 m_pRequest->m_strVersion.TrimLeft();
  260.             }
  261.             // check for execution arguments....
  262.             ndx = m_pRequest->m_strURL.Find( '?' );
  263.             if ( ndx != -1 )
  264.             {
  265.                 // yup; save the args....
  266.                 m_pRequest->m_strArgs = m_pRequest->m_strURL.Mid( ndx+1 );
  267.                 // strip from file name....
  268.                 m_pRequest->m_strURL = m_pRequest->m_strURL.Left( ndx );
  269.                 m_pRequest->m_dwExecute = CRequest::APP_EXECUTE;
  270.             }
  271.  
  272.             // change any "%xx"s to the appropriate char....
  273.             m_pRequest->m_strURL = Decode( m_pRequest->m_strURL );
  274.         }
  275.         m_reqStatus = REQ_HEADER;
  276.         break;
  277.     case REQ_HEADER:
  278.         ndx = m_strLine.Find( ':' );
  279.         if ( ndx != -1 )
  280.         {
  281.             CString strName = m_strLine.Left( ndx );
  282.             CString strValue = m_strLine.Mid( ndx+1 );
  283.             strName.MakeLower();
  284.             strValue.TrimLeft();
  285.             m_pRequest->m_mapHeaders.SetAt( strName, strValue );
  286.         }
  287.         break;
  288.     };
  289.     m_strLine.Empty();
  290. }
  291.  
  292. BOOL CRequestSocket::BodySent( void )
  293. {
  294.     BOOL bSent = FALSE;
  295.     CString strValue = m_pRequest->GetHeaderValue( "Content-Length" );
  296.     if ( !strValue.IsEmpty() )
  297.     {
  298.         m_pRequest->m_cbBody = atoi( strValue );
  299.         bSent = TRUE;
  300.     }
  301.     return bSent;
  302. }
  303.  
  304. void CRequestSocket::AddToBody( int nBytes, int ndx )
  305. {
  306.     // save the buffer size....
  307.     int nOldSize = m_buf.GetSize();
  308.     // get rid of old stuff; append to body data....
  309.     m_buf.RemoveAt( 0, ndx );
  310.     m_buf.SetSize( nBytes - ndx );
  311.     m_pRequest->m_baBody.Append( m_buf );
  312.     // restore the buffer size....
  313.     m_buf.SetSize( nOldSize );
  314.     // see if we're done....
  315.     if ( m_pRequest->m_baBody.GetSize() >= m_pRequest->m_cbBody )
  316.     {
  317.         m_pRequest->m_baBody.SetSize( m_pRequest->m_cbBody );
  318.         m_reqStatus = REQ_DONE;
  319.     }
  320. }
  321.  
  322. BOOL CRequestSocket::StartResponse( void )
  323. {
  324.     BOOL bWait = FALSE;
  325.     CString strFile;
  326.     UINT uPort;
  327.     // save the host address....
  328.     GetPeerName( m_pRequest->m_strHost, uPort );
  329.     // switch on the method....
  330.     if ( m_pRequest->m_cbBody == 0 &&
  331.         m_pRequest->m_strMethod.CompareNoCase( "GET" ) == 0 )
  332.     {
  333.         FindTarget( strFile );
  334.         if( m_pRequest->m_uStatus == 0 )
  335.         {
  336.             if ( m_pRequest->m_dwExecute )
  337.                 bWait=StartSvrApp();
  338.             else
  339.             {
  340.                 if ( StuffHeading() )
  341.                     StartTargetStuff();
  342.             }
  343.         }
  344.     }
  345.     else if ( m_pRequest->m_cbBody == 0 && m_reqStatus == REQ_DONE &&
  346.         m_pRequest->m_strMethod.CompareNoCase( "HEAD" ) == 0 )
  347.     {
  348.         FindTarget( strFile );
  349.         if( m_pRequest->m_uStatus == 0 )
  350.         {
  351.             if ( m_pRequest->m_dwExecute )
  352.                 bWait=StartSvrApp();
  353.             else
  354.             {
  355.                 StuffHeading();
  356.                 // we don't send the file for HEAD reqs....
  357.                 if ( m_hFile != INVALID_HANDLE_VALUE)
  358.                 {
  359.                     CloseHandle( m_hFile );
  360.                     m_hFile = INVALID_HANDLE_VALUE;
  361.                 }
  362.             }
  363.         }
  364.     }
  365.     else if ( m_pRequest->m_cbBody > 0 && m_reqStatus == REQ_DONE &&
  366.         m_pRequest->m_strMethod.CompareNoCase( "POST" ) == 0 )
  367.     {
  368.         // assume an executable....
  369.         m_pRequest->m_dwExecute = CRequest::APP_EXECUTE;
  370.         FindTarget( strFile );
  371.         if ( m_pRequest->m_uStatus == 0 )
  372.         {
  373.             bWait=StartSvrApp();
  374.         }
  375.     }
  376.     else
  377.         StuffError( IDS_STATUS_NOTIMPL );
  378.  
  379.     // notify the active view of the hit....
  380.     m_pDoc->DocHit( m_pRequest );
  381.     return bWait;
  382. }
  383.  
  384. BOOL CRequestSocket::FindTarget( CString& strFile )
  385. {
  386.     BOOL bFound = FALSE;
  387.     strFile = m_pRequest->m_strURL;
  388.     // change from URL to local file system path....
  389.     if ( URLtoPath( strFile ) )
  390.     {
  391.         CString strExtra; // extra path info
  392.         m_pRequest->m_dwAttr = GetFileAttributes( strFile );
  393.         if ( m_pRequest->m_dwAttr != -1 )
  394.             bFound = TRUE;
  395.         else
  396.         {
  397.             // rip off the last portion....
  398.             strExtra = StripLast( strFile );
  399.             while( !strFile.IsEmpty() )
  400.             {
  401.                 // anything there?
  402.                 m_pRequest->m_dwAttr = GetFileAttributes( strFile );
  403.                 if ( m_pRequest->m_dwAttr != -1 )
  404.                 {
  405.                     // found something; better not be a folder....
  406.                     if( (m_pRequest->m_dwAttr&FILE_ATTRIBUTE_DIRECTORY) == 0 )
  407.                         bFound = TRUE;
  408.                     break;
  409.                 }
  410.                 // rip off the next portion....
  411.                 strExtra = StripLast( strFile ) + strExtra;
  412.             }
  413.         }
  414.  
  415.         if ( bFound )
  416.         {
  417.             // strip any trailing SEPCHAR....
  418.             if ( strFile.GetAt( strFile.GetLength()-1) == SEPCHAR )
  419.                 m_pRequest->m_strFullPath = strFile.Left( strFile.GetLength()-1 );
  420.             else
  421.                 m_pRequest->m_strFullPath = strFile;
  422.  
  423.             // see if we need to set the extra path info....
  424.             if ( !strExtra.IsEmpty() )
  425.             {
  426.                 m_pRequest->m_strPathInfo = strExtra;
  427.                 if ( URLtoPath( strExtra ) )
  428.                     m_pRequest->m_strPathTranslated = strExtra;
  429.             }
  430.  
  431.             // if it's a folder, see if we can redirect to
  432.             // on of the default docs or apps....
  433.             if ( m_pRequest->m_dwAttr & FILE_ATTRIBUTE_DIRECTORY )
  434.             {
  435.                 // check for existence of a default doc or app....
  436.                 if ( !CheckDefault( IDS_DEFAULTDOC, FALSE ) )
  437.                     CheckDefault( IDS_DEFAULTAPP, TRUE );
  438.             }
  439.             else if ( m_pRequest->m_dwExecute && !IsSvrApp() )
  440.             {
  441.                 StuffError( IDS_STATUS_BADREQUEST );
  442.             }
  443.         }
  444.         else
  445.             StuffError( IDS_STATUS_NOTFOUND );
  446.     }
  447.     else
  448.         StuffError( IDS_STATUS_BADREQUEST );
  449.  
  450.     return bFound;
  451. }
  452.  
  453. BOOL CRequestSocket::URLtoPath( CString& strFile )
  454. {
  455.     BOOL bLegal = FALSE;
  456.     CString& strRoot = m_pDoc->m_strRoot;
  457.  
  458.     // start with the root, append the abs path....
  459.     CString strTemp = strRoot + strFile;
  460.     // now canonicalize it....
  461.     DWORD dwSize = GetFullPathName( strTemp, MAX_PATH, strFile.GetBuffer(MAX_PATH+1), NULL );
  462.     strFile.ReleaseBuffer();
  463.  
  464.     // get the full path okay?
  465.     if ( dwSize )
  466.     {
  467.         int cchRoot = strRoot.GetLength();
  468.         int cchFile = strFile.GetLength();
  469.         // must be the same or longer than the root....
  470.         if ( cchRoot == cchFile )
  471.         {
  472.             // must be exactly the same....
  473.             if ( strRoot.Compare( strFile ) == 0 )
  474.                 bLegal = TRUE;
  475.         }
  476.         else if ( cchRoot < cchFile )
  477.         {
  478.             // must have the root as the base....
  479.             if ( strRoot.Compare( strFile.Left(cchRoot) ) == 0
  480.                 && strFile.GetAt( cchRoot ) == SEPCHAR )
  481.                 bLegal = TRUE;
  482.         }
  483.     }
  484.  
  485.     return bLegal;
  486. }
  487.  
  488. BOOL CRequestSocket::PathToURL( CString& strFile )
  489. {
  490.     int ndx;
  491.     // a local reference to the root....
  492.     CString& strRoot = m_pDoc->m_strRoot;
  493.     // change all SEPCHARs to forward slashes....
  494.     while ( (ndx=strFile.Find( SEPCHAR )) != -1 )
  495.         strFile = strFile.Left( ndx ) + '/' + strFile.Mid(ndx+1);
  496.     // now add the prefix and server, and cut the root....
  497.     CString strPort;
  498.     UINT uPort = m_pDoc->m_uPort;
  499.     if ( uPort != PORT_HTTP )
  500.         strPort.Format( ":%d", uPort );
  501.  
  502.     strFile = CString("http://")
  503.         + m_pDoc->m_strServer
  504.         + strPort
  505.         + strFile.Mid(strRoot.GetLength());
  506.  
  507.     return TRUE;
  508. }
  509.  
  510. int CRequestSocket::StuffString( const CString& strData )
  511. {
  512.     int nLen = strData.GetLength()*sizeof(TCHAR);
  513.     // make sure there's enough room....
  514.     if ( m_cbOut + nLen > m_buf.GetSize() )
  515.     {
  516.         int nChunks = nLen/1024 + 1;
  517.         m_buf.SetSize( m_cbOut + nChunks*1024 );
  518.     }
  519.     // copy the data....
  520.     MoveMemory( m_buf.GetData() + m_cbOut, (LPCSTR)strData, nLen );
  521.     m_cbOut += nLen;
  522.     // return amount of space left....
  523.     return (m_buf.GetSize() - m_cbOut);
  524. }
  525.  
  526. int CRequestSocket::StuffString( UINT uId )
  527. {
  528.     CString str;
  529.     str.LoadString( uId );
  530.     return StuffString( str );
  531. }
  532.  
  533. int CRequestSocket::StuffStatus( const CString& strStatus )
  534. {
  535.     CString strVer = "HTTP/1.0 ";
  536.     StuffString( strVer );
  537.     StuffString( strStatus );
  538.     StuffString( CRLF );
  539.  
  540.     // stuff the server name....
  541.     CString strServer;
  542.     if ( strServer.LoadString( IDS_SERVER_NAME ) && !strServer.IsEmpty() )
  543.         StuffHeader( "Server", strServer );
  544.  
  545.     // stuff the date....
  546.     return StuffHeader( "Date", GetHttpDate() );
  547. }
  548.  
  549. int CRequestSocket::StuffStatus( UINT uStatus )
  550. {
  551.     CString strStatus;
  552.     strStatus.LoadString( uStatus );
  553.     // save the status for this request....
  554.     m_pRequest->m_uStatus = uStatus;
  555.     // stuff the HTTP status line....
  556.     return StuffStatus( strStatus );
  557. }
  558.  
  559. int CRequestSocket::StuffError( UINT uMsg )
  560. {
  561.     StuffStatus( uMsg );
  562.     return StuffString( CRLF );
  563. }
  564.  
  565. int CRequestSocket::StuffHeader( CString strName, CString strValue )
  566. {
  567.     StuffString( strName );
  568.     StuffString( ": " );
  569.     StuffString( strValue );
  570.     return StuffString( CRLF );
  571. }
  572.  
  573. int CRequestSocket::StuffHeader( CString strName, int nValue )
  574. {
  575.     CString strValue;
  576.     StuffString( strName );
  577.     StuffString( ": " );
  578.     strValue.Format( "%d", nValue );
  579.     StuffString( strValue );
  580.     return StuffString( CRLF );
  581. }
  582.  
  583. BOOL CRequestSocket::StuffHeading( void )
  584. {
  585.     BOOL bContinue = FALSE;
  586.     if ( m_pRequest->m_dwAttr & FILE_ATTRIBUTE_HIDDEN )
  587.     {
  588.         // never show hidden files....
  589.         StuffError( IDS_STATUS_FORBIDDEN );
  590.     }
  591.     else if ( m_pRequest->m_dwAttr & FILE_ATTRIBUTE_DIRECTORY )
  592.     {
  593.         if ( m_pDoc->m_bAllowListing )
  594.         {
  595.             // create a directory listing....
  596.             StuffStatus( IDS_STATUS_OK );
  597.             StuffString( CRLF );
  598.             bContinue = TRUE;
  599.         }
  600.         else
  601.             StuffError( IDS_STATUS_FORBIDDEN );
  602.     }
  603. #ifdef IMPL_CGI
  604.     else if ( m_hFile != INVALID_HANDLE_VALUE )
  605.     {
  606.         // cgi's output file will be opened already....
  607.         CString strStatus, strHeaders;
  608.         // loop until we find a blank line....
  609.         DWORD dwRead = 0;
  610.         CByteArray baFile;
  611.         baFile.SetSize( 1024 );
  612.         // read next chunk....
  613.         BOOL bRead = ReadFile( m_hFile, baFile.GetData(),
  614.             baFile.GetSize(), &dwRead, NULL );
  615.         while ( dwRead > 0 )
  616.         {
  617.             int ndx = 0;
  618.             while( GetLine( baFile, dwRead, ndx ) == TRUE )
  619.             {
  620.                 BOOL bSave = TRUE;
  621.                 // stuff any non-empty lines.....
  622.                 if ( m_strLine.IsEmpty() )
  623.                 {
  624.                     // we found our empty line;
  625.                     // back up to where we left off....
  626.                     DWORD dwPos = SetFilePointer( m_hFile,
  627.                         ndx - dwRead,
  628.                         NULL, FILE_CURRENT );
  629.  
  630.                     // and we're off....
  631.                     bContinue = TRUE;
  632.                     break;
  633.                 }
  634.                 else
  635.                 {
  636.                     int nPos = m_strLine.Find( ':' );
  637.                     if ( nPos != -1 )
  638.                     {
  639.                         CString strName = m_strLine.Left( nPos );
  640.                         strName.TrimLeft();
  641.                         strName.TrimRight();
  642.                         CString strVal  = m_strLine.Mid( nPos+1 );
  643.                         strVal.TrimLeft();
  644.                         strVal.TrimRight();
  645.                         if ( strName.CompareNoCase("Status") == 0 )
  646.                         {
  647.                             strStatus = strVal;
  648.                             bSave = FALSE;
  649.                         }
  650.                         else if ( strName.CompareNoCase("Location") == 0 )
  651.                         {
  652.                             strStatus.LoadString( IDS_STATUS_MOVEDTEMP );
  653.                         }
  654.                     }
  655.                 }
  656.  
  657.                 // save the header (if we want to)....
  658.                 if ( bSave )
  659.                     strHeaders += m_strLine + CRLF;
  660.  
  661.                 m_strLine.Empty();
  662.             }
  663.             // read next chunk if we're not done....
  664.             if ( bContinue )
  665.                 break;
  666.             else
  667.                 ReadFile( m_hFile, baFile.GetData(),
  668.                     baFile.GetSize(), &dwRead, NULL );
  669.         }
  670.         if ( strStatus.IsEmpty() )
  671.             StuffStatus( IDS_STATUS_OK );
  672.         else
  673.             StuffStatus( strStatus );
  674.  
  675.         // stuff the headers....
  676.         StuffString( strHeaders );
  677.         // stuff the blank line....
  678.         StuffString( CRLF );
  679.     }
  680. #endif // IMPL_CGI
  681.     else
  682.     {
  683.         // open the file....
  684.         m_hFile = CreateFile( m_pRequest->m_strFullPath,
  685.             GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
  686.             FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,
  687.             NULL );
  688.         if ( m_hFile != INVALID_HANDLE_VALUE )
  689.         {
  690.             if ( m_reqStatus != REQ_SIMPLE )
  691.             {
  692.                 CTime timeIfMod;
  693.                 CString strIfMod = m_pRequest->GetHeaderValue( "If-Modified-Since" );
  694.                 if ( strIfMod.GetLength() > 0 &&
  695.                     FromHttpTime( strIfMod, timeIfMod ) &&
  696.                     !IfModSince( timeIfMod ) )
  697.                 {
  698.                     // eh, it hasn't been modified....
  699.                     StuffStatus( IDS_STATUS_NOTMODIFIED );
  700.                     // don't need it anymore....
  701.                     CloseHandle( m_hFile );
  702.                     m_hFile = INVALID_HANDLE_VALUE;
  703.                 }
  704.                 else
  705.                 {
  706.                     // send it off....
  707.                     StuffStatus( IDS_STATUS_OK );
  708.                     // any other header info....
  709.                     StuffFileType();
  710.                     StuffHeader( "Content-length", GetFileSize( m_hFile, NULL ) );
  711.                     // get the last modified time....
  712.                     FILETIME ft;
  713.                     if ( GetFileTime( m_hFile, NULL, NULL, &ft ) )
  714.                     {
  715.                         StuffHeader( "Last-Modified", GetHttpDate( &ft ) );
  716.                     }
  717.                     bContinue = TRUE;
  718.                 }
  719.                 // blank line....
  720.                 StuffString( CRLF );
  721.             }
  722.             else
  723.                 bContinue = TRUE;
  724.         }
  725.         else
  726.         {
  727.             // couldn't open; try again later....
  728.             StuffError( IDS_STATUS_SVCUNAVAIL );
  729.         }
  730.     }
  731.     return bContinue;
  732. }
  733.  
  734. void CRequestSocket::StartTargetStuff( void )
  735. {
  736.     if ( m_hFile != INVALID_HANDLE_VALUE)
  737.     {
  738.         DWORD dwRead = 0;
  739.         // read the first chunk....
  740.         ReadFile( m_hFile, m_buf.GetData() + m_cbOut,
  741.             m_buf.GetSize()-m_cbOut, &dwRead, NULL );
  742.         if ( dwRead > 0 )
  743.             m_cbOut += dwRead;
  744.         else
  745.         {
  746.             // nothing read.... close the file....
  747.             CloseHandle( m_hFile );
  748.             m_hFile = INVALID_HANDLE_VALUE;
  749.         }
  750.     }
  751.     else if ( m_pRequest->m_dwAttr & FILE_ATTRIBUTE_DIRECTORY )
  752.         StuffListing();
  753.     else
  754.         StuffString( CRLF );
  755. }
  756.  
  757. void CRequestSocket::StuffListing( void )
  758. {
  759.     BOOL bRoot = FALSE;
  760.     BOOL bIcons = m_pDoc->m_bListIcon;
  761.     CString strIcon;
  762.     CString strLine = CString("http://")
  763.         + m_pDoc->m_strServer
  764.         + m_pRequest->m_strURL;
  765.     CString strDir = m_pRequest->m_strURL;
  766.     CString strMask = m_pRequest->m_strFullPath;
  767.  
  768.     // make sure URL ends in a slash....
  769.     if ( strDir.GetAt( strDir.GetLength()-1 ) != '/' )
  770.         strDir += '/';
  771.     // is this the server's root folder?
  772.     else if ( strDir.Compare( "/" ) == 0 )
  773.         bRoot = TRUE;
  774.  
  775.     // create the file search mask....
  776.     AddFile( strMask, IDS_DIRMASK );
  777.     StuffString( IDS_CONTENTS_PRE );
  778.     StuffString( strLine );
  779.     StuffString( IDS_CONTENTS_POST );
  780.     if ( bRoot )
  781.         StuffString( IDS_CONTENTS_DESC );
  782.  
  783.     if ( bIcons )
  784.         strIcon.LoadString( IDS_ICON_BLANK );
  785.     strLine.Format( IDS_CONTENTS_HEADING, strIcon );
  786.     StuffString( strLine );
  787.  
  788.     int nFiles = 0;
  789.  
  790.     WIN32_FIND_DATA fd;
  791.     // find the first file that matches the mask....
  792.     HANDLE fh = FindFirstFile( strMask, &fd );
  793.     if ( fh != INVALID_HANDLE_VALUE )
  794.     {
  795.         // create a line for the found file....
  796.         nFiles += StuffListingFile( &fd, strDir, bIcons );
  797.         // loop through all other files....
  798.         while ( FindNextFile( fh, &fd ) )
  799.             nFiles += StuffListingFile( &fd, strDir, bIcons );
  800.     }
  801.  
  802.     if ( nFiles == 0 )
  803.         StuffString( IDS_CONTENTS_EMPTY );
  804.  
  805.     StuffString( IDS_CONTENTS_FOOTER );
  806.     // only add the parent link if there is one....
  807.     if ( !bRoot )
  808.     {
  809.         if ( bIcons )
  810.             strIcon.LoadString( IDS_ICON_PARENT );
  811.         strLine.Format( IDS_CONTENTS_PARENT, strIcon );
  812.         StuffString( strLine );
  813.     }
  814.     // add the note and end it....
  815.     StuffString( IDS_CONTENTS_NOTE );
  816.     StuffString( CRLF );
  817. }
  818.  
  819. int CRequestSocket::StuffListingFile( WIN32_FIND_DATA* pfd, const CString& strDir, BOOL bIcons )
  820. {
  821.     int nFile = 0;
  822.     // don't include '.', '..' or hidden files....
  823.     if ( lstrcmp( pfd->cFileName, "." ) && lstrcmp( pfd->cFileName, ".." )
  824.         && (pfd->dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) == 0 )
  825.     {
  826.         CString strSize, strIcon = "";
  827.         CString strLine, strFile = pfd->cFileName;
  828.         CTime timeFile( pfd->ftLastWriteTime );
  829.         BOOL bFolder = ((pfd->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0);
  830.         if ( bIcons && bFolder )
  831.             strIcon.LoadString( IDS_ICON_FOLDER );
  832.         else if ( bIcons )
  833.             strIcon.LoadString( IDS_ICON_FILE );
  834.  
  835.         // create the link string....
  836.         CString strLink = strDir + strFile;
  837.         // make sure spaces are replaced with '%20'...
  838.         int ndx;
  839.         while ( (ndx=strLink.Find(' ')) != -1 )
  840.             strLink = strLink.Left(ndx) + "%20" + strLink.Mid( ndx+1 );
  841.  
  842.         // format the size string....
  843.         if ( bFolder )
  844.             strSize = "  Folder";
  845.         else if ( pfd->nFileSizeHigh > 0 )
  846.             strSize = "   > 4GB"; // yeah, right.
  847.         else if ( pfd->nFileSizeLow < 1024 )
  848.             strSize = "    < 1K";
  849.         else
  850.             strSize.Format( "%7dK", pfd->nFileSizeLow/1024 );
  851.  
  852.         strLine.Format( IDS_CONTENTS_FORMAT,
  853.             timeFile.Format( IDS_FILETIMEFMT ),
  854.             strSize, strLink, strIcon, strFile );
  855.         StuffString( strLine );
  856.         nFile = 1;
  857.     }
  858.     return nFile;
  859. }
  860.  
  861. BOOL CRequestSocket::StartSvrApp( void )
  862. {
  863. #ifdef IMPL_CGI
  864.     if ( m_pRequest->m_dwExecute != CRequest::APP_ISAPI )
  865.         return CGIStart();
  866.     else
  867.     {
  868.         StuffError( IDS_STATUS_NOTIMPL );
  869.         return FALSE;
  870.     }
  871. #else //  IMPL_CGI
  872.     StuffError( IDS_STATUS_NOTIMPL );
  873.     return FALSE;
  874. #endif // IMPL_CGI
  875. }
  876.  
  877. #ifdef IMPL_CGI
  878. BOOL CRequestSocket::CGIStart( void )
  879. {
  880.     BOOL bOk = FALSE;
  881.     // get the temp path...
  882.     CString strTempPath;
  883.     GetTempPath( MAX_PATH, strTempPath.GetBuffer(MAX_PATH) );
  884.     strTempPath.ReleaseBuffer();
  885.     // create a temporary file for the output....
  886.     CString strTempName;
  887.     GetTempFileName( strTempPath, "CGI", 0, strTempName.GetBuffer(MAX_PATH) );
  888.     strTempName.ReleaseBuffer();
  889.     m_hFile = CreateFile( strTempName, GENERIC_READ|GENERIC_WRITE,
  890.         FILE_SHARE_READ|FILE_SHARE_WRITE, &g_sa,
  891.         CREATE_ALWAYS, FILE_ATTRIBUTE_TEMPORARY|FILE_FLAG_DELETE_ON_CLOSE, NULL );
  892.     if ( m_hFile != INVALID_HANDLE_VALUE )
  893.     {
  894.         // create the cancel event....
  895.         m_pCancel = new CEvent;
  896.         if ( m_pCancel )
  897.         {
  898.             // make sure the event is reset....
  899.             m_pCancel->ResetEvent();
  900.             // create the CGI thread suspended....
  901.             m_pThread = AfxBeginThread( (AFX_THREADPROC)CGIThread,
  902.                 (LPVOID)this, THREAD_PRIORITY_NORMAL, 0,
  903.                 CREATE_SUSPENDED, NULL );
  904.             if ( m_pThread )
  905.             {
  906.                 // don't self-destruct (we must delete)....
  907.                 m_pThread->m_bAutoDelete = FALSE;
  908.                 // resume...
  909.                 m_pThread->ResumeThread();
  910.                 bOk = TRUE;
  911.             }
  912.         }
  913.     }
  914.  
  915.     if ( bOk == FALSE )
  916.     {
  917.         StuffError( IDS_STATUS_SVRERROR );
  918.         if( m_hFile != INVALID_HANDLE_VALUE )
  919.         { // JIC....
  920.             CloseHandle( m_hFile );
  921.             m_hFile = INVALID_HANDLE_VALUE;
  922.         }
  923.     }
  924.     return bOk;
  925. }
  926.  
  927. void AddEnvVar( CString& strEnv, CString strName, CString strVal )
  928. {
  929.     // add the name=val pair to the env in alphabetical order....
  930.     strEnv += strName + '=' + strVal + '\a';
  931. }
  932.  
  933. UINT CGIThread( LPVOID pvParam )
  934. {
  935.     CRequestSocket* pReqSock = (CRequestSocket*)pvParam;
  936.     CRequest* pRequest = pReqSock->m_pRequest;
  937.     BOOL bOk = FALSE;
  938.     DWORD dwErr;
  939.     HANDLE hWritePipe, hReadPipe;
  940.     // create a pipe we'll use for STDIN....
  941.     if ( CreatePipe( &hReadPipe, &hWritePipe, &g_sa, 0 ) )
  942.     {
  943.         // get the command line together....
  944.         CString strCmdLine = pRequest->m_strFullPath
  945.             + ' '
  946.             + Decode( pRequest->m_strArgs, TRUE );
  947.         // get the directory....
  948.         CString strDir = pRequest->m_strFullPath;
  949.         int ndx = strDir.ReverseFind( SEPCHAR );
  950.         // assume we found it....
  951.         strDir = strDir.Left( ndx+1 );
  952.  
  953.         // create an environment for the CGI process....
  954.         DWORD dwCreateFlags = 0;
  955. #ifdef UNICODE
  956.         dwCreateFlags = CREATE_UNICODE_ENVIRONMENT;
  957. #endif // UNICODE
  958.         CEnvironment cEnv;
  959.  
  960.         CString strValue;
  961.         strValue.LoadString( IDS_SERVER_NAME );
  962.         cEnv.Add( "SERVER_SOFTWARE", strValue );
  963.         cEnv.Add( "SERVER_NAME", pReqSock->m_pDoc->m_strServer );
  964.         cEnv.Add( "GATEWAY_INTERFACE", "CGI/1.1" );
  965.         cEnv.Add( "SERVER_PROTOCOL", "HTTP/1.0" );
  966.         strValue.Format( "%d", pReqSock->m_pDoc->m_uPort );
  967.         cEnv.Add( "SERVER_PORT", strValue );
  968.  
  969.         cEnv.Add( "REQUEST_METHOD", pRequest->m_strMethod );
  970.         cEnv.Add( "SCRIPT_NAME", pRequest->m_strURL );
  971.         cEnv.Add( "QUERY_STRING", pRequest->m_strArgs );
  972.         cEnv.Add( "REMOTE_ADDR", pRequest->m_strHost );
  973.         if ( pRequest->m_cbBody > 0 )
  974.         {
  975.             cEnv.Add( "CONTENT_LENGTH", pRequest->GetHeaderValue("Content-Length") );
  976.             cEnv.Add( "CONTENT_TYPE", pRequest->GetHeaderValue("Content-Type") );
  977.         }
  978.         if ( !pRequest->m_strPathInfo.IsEmpty() )
  979.         {
  980.             cEnv.Add( "PATH_INFO", pRequest->m_strPathInfo );
  981.             cEnv.Add( "PATH_TRANSLATED", pRequest->m_strPathTranslated );
  982.         }
  983.  
  984.         // all the passed headers prefixed with "HTTP_"....
  985.         POSITION pos = pRequest->m_mapHeaders.GetStartPosition();
  986.         while ( pos != NULL )
  987.         {
  988.             // get the name/value pair....
  989.             CString strName, strValue;
  990.             pRequest->m_mapHeaders.GetNextAssoc( pos, strName, strValue );
  991.             HeaderToEnvVar( strName );
  992.             // set the environment variable....
  993.             cEnv.Add( strName, strValue );
  994.         }
  995.  
  996.         // create the process....
  997.         LPVOID pEnv = (LPVOID)cEnv.GetBlock();
  998.         PROCESS_INFORMATION pi;
  999.         STARTUPINFO si = {0};
  1000.         si.cb = sizeof(si);
  1001.         si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
  1002.         si.wShowWindow = SW_HIDE;
  1003.         si.hStdInput = hReadPipe;
  1004.         si.hStdOutput = pReqSock->m_hFile;
  1005.         si.hStdError = pReqSock->m_hFile;
  1006.         bOk = CreateProcess( NULL, strCmdLine.GetBuffer(1),
  1007.             NULL, NULL, TRUE,
  1008.             dwCreateFlags, pEnv,
  1009.             strDir, &si, &pi );
  1010.         strCmdLine.ReleaseBuffer();
  1011.         // if created....
  1012.         if ( bOk )
  1013.         {
  1014.             // release our hold on the thread....
  1015.             CloseHandle( pi.hThread );
  1016.             // send the body of the post to the stdin....
  1017.             if ( pRequest->m_cbBody > 0 )
  1018.             {
  1019.                 DWORD dwWritten = 0;
  1020.                 WriteFile( hWritePipe, pRequest->m_baBody.GetData(),
  1021.                     pRequest->m_cbBody, &dwWritten, NULL );
  1022.             }
  1023.             // wait for either cancel or process done....
  1024.             HANDLE aHandles[2];
  1025.             aHandles[0] = pi.hProcess;
  1026.             aHandles[1] = pReqSock->m_pCancel->m_hObject;
  1027.             if ( WaitForMultipleObjects( 2, aHandles, FALSE, INFINITE ) == WAIT_OBJECT_0 )
  1028.             {
  1029.                 // process finished; notify main thread....
  1030.                 AfxGetApp()->m_pMainWnd->PostMessage( WSM_CGIDONE, 0, (LPARAM)pReqSock );
  1031.             }
  1032.             else
  1033.             {
  1034.                 // canceled or some other error....
  1035.                 bOk = FALSE;
  1036.             }
  1037.             // close our hold on it....
  1038.             CloseHandle( pi.hProcess );
  1039.         }
  1040.         else
  1041.             dwErr = GetLastError();
  1042.  
  1043.         // close the stdin pipe....
  1044.         CloseHandle( hWritePipe );
  1045.         CloseHandle( hReadPipe );
  1046.         delete pEnv;
  1047.     }
  1048.     if ( bOk == FALSE && pReqSock->m_hFile != INVALID_HANDLE_VALUE )
  1049.     { // JIC....
  1050.         CloseHandle( pReqSock->m_hFile );
  1051.         pReqSock->m_hFile = INVALID_HANDLE_VALUE;
  1052.     }
  1053.  
  1054.     return (bOk?0:1);
  1055. }
  1056.  
  1057. void CRequestSocket::CGIDone( void )
  1058. {
  1059.     if ( !m_bKilled )
  1060.     {
  1061.         // flush the temp file's buffers....
  1062.         BOOL bSucceed = FlushFileBuffers( m_hFile );
  1063.         // go to start of file....
  1064.         DWORD dwPos = SetFilePointer( m_hFile, 0, NULL, FILE_BEGIN );
  1065.         // output the header....
  1066.         StuffHeading();
  1067.         if ( m_pRequest->m_strMethod.Compare( "HEAD" ) )
  1068.             StartTargetStuff();
  1069.         else
  1070.         {
  1071.             CloseHandle( m_hFile );
  1072.             m_hFile = INVALID_HANDLE_VALUE;
  1073.         }
  1074.         AsyncSelect( FD_WRITE | FD_CLOSE );
  1075.     }
  1076.     else
  1077.     {
  1078.         CloseHandle( m_hFile );
  1079.         m_hFile = INVALID_HANDLE_VALUE;
  1080.     }
  1081. }
  1082.  
  1083. void HeaderToEnvVar( CString& strVar )
  1084. {
  1085.     int ndx;
  1086.     // make upper case, change '-' to '_', and prefix....
  1087.     strVar.MakeUpper();
  1088.     while( (ndx = strVar.Find('-')) != -1 )
  1089.         strVar = strVar.Left(ndx) + '_' + strVar.Mid(ndx+1);
  1090.     strVar = "HTTP_" + strVar;
  1091. }
  1092.  
  1093. CEnvironment::CEnvironment( void )
  1094. {
  1095.     m_nSize = 2;
  1096. }
  1097.  
  1098. CEnvironment::~CEnvironment( void )
  1099. {
  1100. }
  1101.  
  1102. BOOL CEnvironment::Add( CString name, CString value )
  1103. {
  1104.     BOOL bOk = TRUE;
  1105.     // create the entry pair string....
  1106.     CString strPair = name + __TEXT('=') + value;
  1107.     m_nSize += strPair.GetLength() + 1;
  1108.     POSITION pos = m_list.GetHeadPosition();
  1109.  
  1110.     // find the first item bigger than this string....
  1111.     while( pos != NULL )
  1112.     {
  1113.         if ( m_list.GetAt(pos).CompareNoCase(strPair) > 0 )
  1114.         {
  1115.             m_list.InsertBefore( pos, strPair );
  1116.             break;
  1117.         }
  1118.         m_list.GetNext( pos );
  1119.     }
  1120.     if ( pos == NULL )
  1121.         m_list.AddTail( strPair );
  1122.  
  1123.     return bOk;
  1124. }
  1125.  
  1126. LPVOID CEnvironment::GetBlock( void )
  1127. {
  1128.     // allocate a block....
  1129.     PTCHAR pBlock = new TCHAR[m_nSize];
  1130.     if ( pBlock )
  1131.     {
  1132.         // iterate through the list....
  1133.         PTCHAR pPos = pBlock;
  1134.         POSITION pos = m_list.GetHeadPosition();
  1135.         while( pos != NULL )
  1136.         {
  1137.             CString& str = m_list.GetNext( pos );
  1138.             // copy the string....
  1139.             lstrcpy( pPos, str );
  1140.             pPos += str.GetLength() + 1;
  1141.         }
  1142.         // NULL for the whole list....
  1143.         *pPos = __TEXT('\0');
  1144.     }
  1145.     return pBlock;
  1146. }
  1147. #endif // IMPL_CGI
  1148.  
  1149. CString CRequestSocket::GetHttpDate( LPFILETIME pft )
  1150. {
  1151.     SYSTEMTIME st;
  1152.     if ( pft )
  1153.         FileTimeToSystemTime( pft, &st );
  1154.     else
  1155.         GetSystemTime( &st );
  1156.  
  1157.     CTime timeHttp( st );
  1158.     return timeHttp.Format( IDS_HTTPTIME );
  1159. }
  1160.  
  1161. BOOL CRequestSocket::IfModSince( const CTime& timeIfMod )
  1162. {
  1163.     // assume it has been modified....
  1164.     BOOL bOk = TRUE;
  1165.     FILETIME ft;
  1166.     if ( GetFileTime( m_hFile, NULL, NULL, &ft ) )
  1167.     {
  1168.         SYSTEMTIME st;
  1169.         if ( FileTimeToSystemTime( &ft, &st ) )
  1170.         {
  1171.             CTime timeFile( st );
  1172.             if ( timeFile <= timeIfMod )
  1173.                 bOk = FALSE;
  1174.         }
  1175.     }
  1176.     return bOk;
  1177. }
  1178.  
  1179. static int IntVal( CString strVal )
  1180. {
  1181.     int nVal = 0;
  1182.     strVal.TrimLeft();
  1183.     for( int ndx = 0; ndx < strVal.GetLength(); ++ndx )
  1184.         nVal = nVal*10 + strVal.GetAt(ndx) - '0';
  1185.  
  1186.     return nVal;
  1187. }
  1188.  
  1189. static int MonthFromStr( const CString& str )
  1190. {
  1191.     LPSTR aMonths[] = {
  1192.         "xxx", "jan", "feb", "mar", "apr", "may", "jun",
  1193.         "jul", "aug", "sep", "oct", "nov", "dec" };
  1194.     for( int nMonth=1; nMonth <= 12; ++nMonth )
  1195.     {
  1196.         if ( str.CompareNoCase( aMonths[nMonth] ) == 0 )
  1197.             break;
  1198.     }
  1199.  
  1200.     return nMonth;
  1201. }
  1202.  
  1203. // Dow, dd Mon year hh:mm:ss GMT
  1204. BOOL CRequestSocket::FromHttpTime( const CString& strHttp, CTime& timeHttp )
  1205. {
  1206.     // assume we couldn't get a good time conversion....
  1207.     BOOL bOk = FALSE;
  1208.     SYSTEMTIME st = {0};
  1209.     int ndx;
  1210.     switch( strHttp.GetAt(3) )
  1211.     {
  1212.     case ',':
  1213.         // read RFC-1123 (preferred)....
  1214.         st.wDay = IntVal( strHttp.Mid(5,2) );
  1215.         st.wMonth = MonthFromStr( strHttp.Mid(8,3) );
  1216.         st.wYear = IntVal( strHttp.Mid(12,4) );
  1217.         st.wHour = IntVal( strHttp.Mid(17,2) );
  1218.         st.wMinute = IntVal( strHttp.Mid(20,2) );
  1219.         st.wSecond = IntVal( strHttp.Mid(23,2) );
  1220.         break;
  1221.     case ' ':
  1222.         // read ANSI-C time format....
  1223.         st.wDay = IntVal( strHttp.Mid(8,2) );
  1224.         st.wMonth = MonthFromStr( strHttp.Mid(4,3) );
  1225.         st.wYear = IntVal( strHttp.Mid(20,4) );
  1226.         st.wHour = IntVal( strHttp.Mid(11,2) );
  1227.         st.wMinute = IntVal( strHttp.Mid(14,2) );
  1228.         st.wSecond = IntVal( strHttp.Mid(17,2) );
  1229.         break;
  1230.     default:
  1231.         if ( (ndx = strHttp.Find( ", " )) != -1 )
  1232.         {
  1233.             st.wDay = IntVal( strHttp.Mid(ndx+2,2) );
  1234.             st.wMonth = MonthFromStr( strHttp.Mid(ndx+5,3) );
  1235.             st.wYear = IntVal( strHttp.Mid(ndx+9,2) );
  1236.             st.wHour = IntVal( strHttp.Mid(ndx+12,2) );
  1237.             st.wMinute = IntVal( strHttp.Mid(ndx+15,2) );
  1238.             st.wSecond = IntVal( strHttp.Mid(ndx+18,2) );
  1239.             // add the correct century....
  1240.             st.wYear += (st.wYear > 50)?1900:2000;
  1241.         }
  1242.         break;
  1243.     }
  1244.     // if year not zero, we pulled the info out of the string....
  1245.     if ( st.wYear != 0 )
  1246.     {
  1247.         // assume GMT....
  1248.         CTime strTime( st );
  1249.         // check to see if the minutes are the same....
  1250.         if ( strTime.GetMinute() == st.wMinute )
  1251.         {
  1252.             // assume it worked....
  1253.             timeHttp = strTime;
  1254.             bOk = TRUE;
  1255.         }
  1256.     }
  1257.     return bOk;
  1258. }
  1259.  
  1260. int CRequestSocket::AddRef( void )
  1261. {
  1262.     return ++m_nRefs;
  1263. }
  1264.  
  1265. int CRequestSocket::Release( void )
  1266. {
  1267.     int nRefs = --m_nRefs;
  1268.     if ( nRefs == 0 )
  1269.         delete this;
  1270.     return nRefs;
  1271. }
  1272.  
  1273.  
  1274.  
  1275. void CRequestSocket::StuffFileType( void )
  1276. {
  1277.     // get the extension....
  1278.     CString strExt = m_pRequest->m_strFullPath.Mid(
  1279.         m_pRequest->m_strFullPath.ReverseFind('.') );
  1280.     // find it in the registry....
  1281.     HKEY hKey = NULL;
  1282.     if ( RegOpenKeyEx( HKEY_CLASSES_ROOT, strExt,
  1283.         0, KEY_READ, &hKey ) == ERROR_SUCCESS )
  1284.     {
  1285.         DWORD dwSize = 0;
  1286.         // see how long the data is....
  1287.         if ( RegQueryValueEx( hKey, "Content Type", NULL, NULL,
  1288.             NULL, &dwSize ) == ERROR_SUCCESS )
  1289.         {
  1290.             CString strType;
  1291.             LONG lRet = RegQueryValueEx( hKey, "Content Type", NULL, NULL,
  1292.                 (LPBYTE)strType.GetBuffer( dwSize ), &dwSize );
  1293.             strType.ReleaseBuffer();
  1294.             if ( lRet == ERROR_SUCCESS )
  1295.                 StuffHeader( "Content-type", strType );
  1296.         }
  1297.         RegCloseKey( hKey );
  1298.     }
  1299. }
  1300.  
  1301. CString Decode( const CString& str, BOOL bQuery )
  1302. {
  1303.     int ndx;
  1304.     CString strDecoded = str;
  1305.     // special processing or query strings....
  1306.     if ( bQuery )
  1307.     {
  1308.         // change all '+' to ' '....
  1309.         while( (ndx=strDecoded.Find('+')) != -1 )
  1310.             strDecoded = strDecoded.Left(ndx) + ' ' + strDecoded.Mid(ndx+1);
  1311.     }
  1312.  
  1313.     // first see if there are any %s to decode....
  1314.     if ( strDecoded.Find( '%' ) != -1 )
  1315.     {
  1316.         // iterate through the string, changing %dd to special char....
  1317.         for( ndx=0; ndx < strDecoded.GetLength(); ndx++ )
  1318.         {
  1319.             char ch = strDecoded.GetAt( ndx );
  1320.             if ( ch == '%' )
  1321.             {
  1322.                 if ( strDecoded.GetAt( ndx+1 ) == '%' )
  1323.                 {
  1324.                     // wanna keep one percent sign....
  1325.                     strDecoded = strDecoded.Left(ndx) + strDecoded.Mid(ndx+1);
  1326.                 }
  1327.                 else
  1328.                 {
  1329.                     // assume we have a hex value....
  1330.                     char ch1 = strDecoded.GetAt(ndx+1);
  1331.                     char ch2 = strDecoded.GetAt(ndx+2);
  1332.                     ch1 = ch1 >= 'A' ? (ch1&0xdf)-'A' : ch1-'0';
  1333.                     ch2 = ch2 >= 'A' ? (ch2&0xdf)-'A' : ch2-'0';
  1334.                     // replace the escape sequence with the char....
  1335.                     strDecoded = strDecoded.Left(ndx)
  1336.                         + (char)(ch1*16 + ch2)
  1337.                         + strDecoded.Mid( ndx+3 );
  1338.                 }
  1339.             }
  1340.         }
  1341.     }
  1342.     return strDecoded;
  1343. }
  1344.  
  1345. CString CRequestSocket::StripLast( CString& strPath )
  1346. {
  1347.     CString strExtra;
  1348.     if ( !strPath.IsEmpty() )
  1349.     {
  1350.         int ndx = strPath.ReverseFind( SEPCHAR );
  1351.         if ( ndx < 0 )
  1352.             ndx = 0;
  1353.         strExtra = strPath.Mid( ndx );
  1354.         strPath = strPath.Left( ndx );
  1355.     }
  1356.     return strExtra;
  1357. }
  1358.  
  1359. BOOL CRequestSocket::CheckDefault( UINT uList, BOOL bExecute )
  1360. {
  1361.     BOOL bFound = FALSE;
  1362.     DWORD dwAttr;
  1363.     CString strDefault, strDefList;
  1364.     strDefList.LoadString( uList );
  1365.     while ( !strDefList.IsEmpty() )
  1366.     {
  1367.         int ndx;
  1368.         strDefault = m_pRequest->m_strFullPath;
  1369.         if ( (ndx=strDefList.Find('\n')) == -1 )
  1370.         {
  1371.             AddFile( strDefault, strDefList );
  1372.             strDefList.Empty();
  1373.         }
  1374.         else
  1375.         {
  1376.             AddFile( strDefault, strDefList.Left(ndx) );
  1377.             strDefList = strDefList.Mid( ndx+1 );
  1378.         }
  1379.         if ( (dwAttr=GetFileAttributes(strDefault)) != -1 &&
  1380.             (dwAttr & FILE_ATTRIBUTE_DIRECTORY) == 0 )
  1381.         {
  1382.             bFound = TRUE;
  1383.             break;
  1384.         }
  1385.     }
  1386.     if ( bFound )
  1387.     {
  1388.         // redirect to the default file....
  1389.         PathToURL( strDefault );
  1390.         if ( bExecute )
  1391.             strDefault += '?';
  1392.  
  1393.         StuffStatus( IDS_STATUS_MOVEDTEMP );
  1394.         StuffHeader( "Location", strDefault );
  1395.         StuffString( CRLF );
  1396.     }
  1397.     return bFound;
  1398. }
  1399.  
  1400. BOOL CRequestSocket::IsSvrApp( void )
  1401. {
  1402.     BOOL bOk = FALSE;
  1403.     int ndx = m_pRequest->m_strFullPath.ReverseFind( '.' );
  1404.     if ( ndx != -1 )
  1405.     {
  1406.         CString strExt = m_pRequest->m_strFullPath.Mid( ndx+1 );
  1407.         CString strAvail;
  1408.         // check if CGI app....
  1409.         strAvail.LoadString( IDS_APP_CGI );
  1410.         bOk = CheckExt( strExt, strAvail, CRequest::APP_CGI );
  1411.         if ( !bOk )
  1412.         {
  1413.             strAvail.LoadString( IDS_APP_ISAPI );
  1414.             bOk = CheckExt( strExt, strAvail, CRequest::APP_ISAPI );
  1415.         }
  1416.     }
  1417.  
  1418.     return bOk;
  1419. }
  1420.  
  1421. BOOL CRequestSocket::CheckExt( const CString& strExt, CString& strAvail, DWORD dwType )
  1422. {
  1423.     BOOL bMatch = FALSE;
  1424.     CString strPossible;
  1425.     // loop through all possible exts....
  1426.     while( !strAvail.IsEmpty() )
  1427.     {
  1428.         int ndx = strAvail.ReverseFind('\n');
  1429.         if ( ndx == -1 )
  1430.         {
  1431.             strPossible = strAvail;
  1432.             strAvail.Empty();
  1433.         }
  1434.         else
  1435.         {
  1436.             strPossible = strAvail.Mid( ndx+1 );
  1437.             strAvail = strAvail.Left( ndx );
  1438.         }
  1439.         if ( strExt.CompareNoCase( strPossible ) == 0 )
  1440.         {
  1441.             m_pRequest->m_dwExecute = dwType;
  1442.             bMatch = TRUE;
  1443.             break;
  1444.         }
  1445.     }
  1446.     return bMatch;
  1447. }
  1448.