REQSOCK.CPP
上传用户:btxinjin
上传日期:2007-01-04
资源大小:83k
文件大小:35k
- // ReqSock.cpp : implementation of the CRequestSocket class
- //
- // This is a part of the Microsoft Foundation Classes C++ library.
- // Copyright (C) 1997-1998 Microsoft Corporation
- // All rights reserved.
- //
- // This source code is only intended as a supplement to the
- // Microsoft Foundation Classes Reference and related
- // electronic documentation provided with the library.
- // See these sources for detailed information regarding the
- // Microsoft Foundation Classes product.
- #include "stdafx.h"
- #include "HttpSvr.h"
- #include "HttpDoc.h"
- #include "Http.h"
- #include "ReqSock.h"
- #include "Request.h"
- #include "resource.h"
- #ifdef _DEBUG
- #define new DEBUG_NEW
- #undef THIS_FILE
- static char THIS_FILE[] = __FILE__;
- #endif
- IMPLEMENT_DYNCREATE(CRequestSocket, CAsyncSocket)
- #define CRLF "x0dx0a"
- SECURITY_ATTRIBUTES g_sa = {sizeof(SECURITY_ATTRIBUTES),NULL,TRUE};
- CRequestSocket::CRequestSocket( void )
- {
- }
- CRequestSocket::CRequestSocket( CHttpSvrDoc* pDoc )
- {
- #ifdef IMPL_CGI
- m_pThread = NULL;
- m_pCancel = NULL;
- #endif // IMPL_CGI
- m_bKilled = FALSE;
- m_nRefs = 1;
- m_reqStatus = REQ_REQUEST;
- m_buf.SetSize( 1024 );
- m_cbOut = 0;
- m_hFile = INVALID_HANDLE_VALUE;
- m_pRequest = NULL;
- m_pDoc = pDoc;
- }
- CRequestSocket::~CRequestSocket( void )
- {
- // JIC....
- #ifdef IMPL_CGI
- if ( m_pCancel )
- {
- if ( m_pThread )
- {
- DWORD dwCode;
- // signal a cancel if still running....
- if ( ::GetExitCodeThread( m_pThread->m_hThread, &dwCode )
- && dwCode == STILL_ACTIVE )
- {
- // signal a cancel....
- m_pCancel->SetEvent();
- // wait for the thread to die....
- WaitForSingleObject( m_pThread->m_hThread, INFINITE );
- }
- // kill the object...
- delete m_pThread;
- }
- delete m_pCancel;
- }
- #endif // IMPL_CGI
- if ( m_hFile )
- CloseHandle( m_hFile );
- if ( m_pRequest )
- {
- // release our hold on the request object....
- m_pRequest->m_bDone = TRUE;
- m_pRequest->Release();
- }
- }
- void CRequestSocket::OnReceive(int nErrorCode)
- {
- if ( m_pRequest == NULL )
- {
- // new request....
- m_pRequest = new CRequest;
- m_bKeepOpen = m_bWantKeepOpen = FALSE;
- }
- if ( m_pRequest )
- {
- // get the bytes....
- int nBytes = Receive( m_buf.GetData(), m_buf.GetSize() );
- if ( nBytes != SOCKET_ERROR )
- {
- int ndx = 0;
- switch ( m_reqStatus )
- {
- case REQ_REQUEST:
- case REQ_HEADER:
- while( GetLine( m_buf, nBytes, ndx ) == TRUE )
- {
- if ( !m_strLine.IsEmpty() )
- ProcessLine();
- else
- {
- m_reqStatus = REQ_BODY;
- break;
- }
- }
- // break if we're not looking for the body....
- if ( m_reqStatus != REQ_BODY )
- break;
- // stop if no body sent....
- if ( !BodySent() )
- {
- m_reqStatus = REQ_DONE;
- break;
- }
- // else fall through....
- case REQ_BODY:
- AddToBody( nBytes, ndx );
- break;
- }
- if ( m_reqStatus == REQ_DONE )
- {
- m_buf.SetSize(0);
- if ( !StartResponse() )
- AsyncSelect( FD_WRITE | FD_CLOSE );
- }
- }
- else
- nBytes = GetLastError();
- }
- else
- {
- // couldn't allocate request object....
- ShutDown( both );
- m_bKilled = TRUE;
- Release();
- }
- }
- void CRequestSocket::OnSend(int nErrorCode)
- {
- int nBytes = Send( m_buf.GetData(), m_cbOut );
- if ( nBytes == SOCKET_ERROR )
- {
- if ( GetLastError() != WSAEWOULDBLOCK )
- {
- ShutDown( both );
- m_bKilled = TRUE;
- Release();
- }
- else
- AsyncSelect( FD_WRITE | FD_CLOSE );
- }
- else if ( nBytes < m_cbOut )
- {
- // still got some left....
- m_buf.RemoveAt( 0, nBytes );
- m_cbOut -= nBytes;
- // adjust the bytes-sent value for the request....
- m_pRequest->m_cbSent += nBytes;
- // set up for next write....
- AsyncSelect( FD_WRITE | FD_CLOSE );
- }
- else
- {
- // adjust the bytes-sent value for the request....
- m_pRequest->m_cbSent += nBytes;
- // if we're not done with the file....
- if ( m_hFile != INVALID_HANDLE_VALUE )
- {
- DWORD dwRead = 0;
- // read next chunk....
- ReadFile( m_hFile, m_buf.GetData(),
- m_buf.GetSize(), &dwRead, NULL );
- if ( dwRead > 0 )
- m_cbOut = dwRead;
- else
- {
- // no more data to send....
- CloseHandle( m_hFile );
- m_hFile = INVALID_HANDLE_VALUE;
- }
- }
- // see if we need to keep going....
- if ( m_hFile != INVALID_HANDLE_VALUE )
- AsyncSelect( FD_WRITE | FD_CLOSE );
- else
- {
- // eh, we're outta here....
- ShutDown( both );
- m_bKilled = TRUE;
- Release();
- }
- }
- }
- void CRequestSocket::OnClose(int nErrorCode)
- {
- m_bKilled = TRUE;
- Release();
- }
- BOOL CRequestSocket::GetLine( const CByteArray& bytes, int nBytes, int& ndx )
- {
- BOOL bLine = FALSE;
- while ( bLine == FALSE && ndx < nBytes )
- {
- char ch = (char)(bytes.GetAt( ndx ));
- switch( ch )
- {
- case 'r': // ignore
- break;
- case 'n': // end-of-line
- bLine = TRUE;
- break;
- default: // other....
- m_strLine += ch;
- break;
- }
- ++ndx;
- }
- return bLine;
- }
- void CRequestSocket::ProcessLine( void )
- {
- int ndx;
- switch( m_reqStatus )
- {
- case REQ_REQUEST:
- ndx = m_strLine.Find( ' ' );
- if ( ndx != -1 )
- {
- m_pRequest->m_strMethod = m_strLine.Left( ndx );
- m_strLine = m_strLine.Mid( ndx+1 );
- m_strLine.TrimLeft();
- ndx = m_strLine.Find( ' ' );
- if ( ndx == -1 )
- {
- m_pRequest->m_strURL = m_strLine;
- m_pRequest->m_strURL.TrimRight();
- m_reqStatus = REQ_SIMPLE;
- }
- else
- {
- m_pRequest->m_strURL = m_strLine.Left( ndx );
- m_pRequest->m_strVersion = m_strLine.Mid( ndx+1 );
- m_pRequest->m_strVersion.TrimLeft();
- }
- // check for execution arguments....
- ndx = m_pRequest->m_strURL.Find( '?' );
- if ( ndx != -1 )
- {
- // yup; save the args....
- m_pRequest->m_strArgs = m_pRequest->m_strURL.Mid( ndx+1 );
- // strip from file name....
- m_pRequest->m_strURL = m_pRequest->m_strURL.Left( ndx );
- m_pRequest->m_dwExecute = CRequest::APP_EXECUTE;
- }
- // change any "%xx"s to the appropriate char....
- m_pRequest->m_strURL = Decode( m_pRequest->m_strURL );
- }
- m_reqStatus = REQ_HEADER;
- break;
- case REQ_HEADER:
- ndx = m_strLine.Find( ':' );
- if ( ndx != -1 )
- {
- CString strName = m_strLine.Left( ndx );
- CString strValue = m_strLine.Mid( ndx+1 );
- strName.MakeLower();
- strValue.TrimLeft();
- m_pRequest->m_mapHeaders.SetAt( strName, strValue );
- }
- break;
- };
- m_strLine.Empty();
- }
- BOOL CRequestSocket::BodySent( void )
- {
- BOOL bSent = FALSE;
- CString strValue = m_pRequest->GetHeaderValue( "Content-Length" );
- if ( !strValue.IsEmpty() )
- {
- m_pRequest->m_cbBody = atoi( strValue );
- bSent = TRUE;
- }
- return bSent;
- }
- void CRequestSocket::AddToBody( int nBytes, int ndx )
- {
- // save the buffer size....
- int nOldSize = m_buf.GetSize();
- // get rid of old stuff; append to body data....
- m_buf.RemoveAt( 0, ndx );
- m_buf.SetSize( nBytes - ndx );
- m_pRequest->m_baBody.Append( m_buf );
- // restore the buffer size....
- m_buf.SetSize( nOldSize );
- // see if we're done....
- if ( m_pRequest->m_baBody.GetSize() >= m_pRequest->m_cbBody )
- {
- m_pRequest->m_baBody.SetSize( m_pRequest->m_cbBody );
- m_reqStatus = REQ_DONE;
- }
- }
- BOOL CRequestSocket::StartResponse( void )
- {
- BOOL bWait = FALSE;
- CString strFile;
- UINT uPort;
- // save the host address....
- GetPeerName( m_pRequest->m_strHost, uPort );
- // switch on the method....
- if ( m_pRequest->m_cbBody == 0 &&
- m_pRequest->m_strMethod.CompareNoCase( "GET" ) == 0 )
- {
- FindTarget( strFile );
- if( m_pRequest->m_uStatus == 0 )
- {
- if ( m_pRequest->m_dwExecute )
- bWait=StartSvrApp();
- else
- {
- if ( StuffHeading() )
- StartTargetStuff();
- }
- }
- }
- else if ( m_pRequest->m_cbBody == 0 && m_reqStatus == REQ_DONE &&
- m_pRequest->m_strMethod.CompareNoCase( "HEAD" ) == 0 )
- {
- FindTarget( strFile );
- if( m_pRequest->m_uStatus == 0 )
- {
- if ( m_pRequest->m_dwExecute )
- bWait=StartSvrApp();
- else
- {
- StuffHeading();
- // we don't send the file for HEAD reqs....
- if ( m_hFile != INVALID_HANDLE_VALUE)
- {
- CloseHandle( m_hFile );
- m_hFile = INVALID_HANDLE_VALUE;
- }
- }
- }
- }
- else if ( m_pRequest->m_cbBody > 0 && m_reqStatus == REQ_DONE &&
- m_pRequest->m_strMethod.CompareNoCase( "POST" ) == 0 )
- {
- // assume an executable....
- m_pRequest->m_dwExecute = CRequest::APP_EXECUTE;
- FindTarget( strFile );
- if ( m_pRequest->m_uStatus == 0 )
- {
- bWait=StartSvrApp();
- }
- }
- else
- StuffError( IDS_STATUS_NOTIMPL );
- // notify the active view of the hit....
- m_pDoc->DocHit( m_pRequest );
- return bWait;
- }
- BOOL CRequestSocket::FindTarget( CString& strFile )
- {
- BOOL bFound = FALSE;
- strFile = m_pRequest->m_strURL;
- // change from URL to local file system path....
- if ( URLtoPath( strFile ) )
- {
- CString strExtra; // extra path info
- m_pRequest->m_dwAttr = GetFileAttributes( strFile );
- if ( m_pRequest->m_dwAttr != -1 )
- bFound = TRUE;
- else
- {
- // rip off the last portion....
- strExtra = StripLast( strFile );
- while( !strFile.IsEmpty() )
- {
- // anything there?
- m_pRequest->m_dwAttr = GetFileAttributes( strFile );
- if ( m_pRequest->m_dwAttr != -1 )
- {
- // found something; better not be a folder....
- if( (m_pRequest->m_dwAttr&FILE_ATTRIBUTE_DIRECTORY) == 0 )
- bFound = TRUE;
- break;
- }
- // rip off the next portion....
- strExtra = StripLast( strFile ) + strExtra;
- }
- }
- if ( bFound )
- {
- // strip any trailing SEPCHAR....
- if ( strFile.GetAt( strFile.GetLength()-1) == SEPCHAR )
- m_pRequest->m_strFullPath = strFile.Left( strFile.GetLength()-1 );
- else
- m_pRequest->m_strFullPath = strFile;
- // see if we need to set the extra path info....
- if ( !strExtra.IsEmpty() )
- {
- m_pRequest->m_strPathInfo = strExtra;
- if ( URLtoPath( strExtra ) )
- m_pRequest->m_strPathTranslated = strExtra;
- }
- // if it's a folder, see if we can redirect to
- // on of the default docs or apps....
- if ( m_pRequest->m_dwAttr & FILE_ATTRIBUTE_DIRECTORY )
- {
- // check for existence of a default doc or app....
- if ( !CheckDefault( IDS_DEFAULTDOC, FALSE ) )
- CheckDefault( IDS_DEFAULTAPP, TRUE );
- }
- else if ( m_pRequest->m_dwExecute && !IsSvrApp() )
- {
- StuffError( IDS_STATUS_BADREQUEST );
- }
- }
- else
- StuffError( IDS_STATUS_NOTFOUND );
- }
- else
- StuffError( IDS_STATUS_BADREQUEST );
- return bFound;
- }
- BOOL CRequestSocket::URLtoPath( CString& strFile )
- {
- BOOL bLegal = FALSE;
- CString& strRoot = m_pDoc->m_strRoot;
- // start with the root, append the abs path....
- CString strTemp = strRoot + strFile;
- // now canonicalize it....
- DWORD dwSize = GetFullPathName( strTemp, MAX_PATH, strFile.GetBuffer(MAX_PATH+1), NULL );
- strFile.ReleaseBuffer();
- // get the full path okay?
- if ( dwSize )
- {
- int cchRoot = strRoot.GetLength();
- int cchFile = strFile.GetLength();
- // must be the same or longer than the root....
- if ( cchRoot == cchFile )
- {
- // must be exactly the same....
- if ( strRoot.Compare( strFile ) == 0 )
- bLegal = TRUE;
- }
- else if ( cchRoot < cchFile )
- {
- // must have the root as the base....
- if ( strRoot.Compare( strFile.Left(cchRoot) ) == 0
- && strFile.GetAt( cchRoot ) == SEPCHAR )
- bLegal = TRUE;
- }
- }
- return bLegal;
- }
- BOOL CRequestSocket::PathToURL( CString& strFile )
- {
- int ndx;
- // a local reference to the root....
- CString& strRoot = m_pDoc->m_strRoot;
- // change all SEPCHARs to forward slashes....
- while ( (ndx=strFile.Find( SEPCHAR )) != -1 )
- strFile = strFile.Left( ndx ) + '/' + strFile.Mid(ndx+1);
- // now add the prefix and server, and cut the root....
- CString strPort;
- UINT uPort = m_pDoc->m_uPort;
- if ( uPort != PORT_HTTP )
- strPort.Format( ":%d", uPort );
- strFile = CString("http://")
- + m_pDoc->m_strServer
- + strPort
- + strFile.Mid(strRoot.GetLength());
- return TRUE;
- }
- int CRequestSocket::StuffString( const CString& strData )
- {
- int nLen = strData.GetLength()*sizeof(TCHAR);
- // make sure there's enough room....
- if ( m_cbOut + nLen > m_buf.GetSize() )
- {
- int nChunks = nLen/1024 + 1;
- m_buf.SetSize( m_cbOut + nChunks*1024 );
- }
- // copy the data....
- MoveMemory( m_buf.GetData() + m_cbOut, (LPCSTR)strData, nLen );
- m_cbOut += nLen;
- // return amount of space left....
- return (m_buf.GetSize() - m_cbOut);
- }
- int CRequestSocket::StuffString( UINT uId )
- {
- CString str;
- str.LoadString( uId );
- return StuffString( str );
- }
- int CRequestSocket::StuffStatus( const CString& strStatus )
- {
- CString strVer = "HTTP/1.0 ";
- StuffString( strVer );
- StuffString( strStatus );
- StuffString( CRLF );
- // stuff the server name....
- CString strServer;
- if ( strServer.LoadString( IDS_SERVER_NAME ) && !strServer.IsEmpty() )
- StuffHeader( "Server", strServer );
- // stuff the date....
- return StuffHeader( "Date", GetHttpDate() );
- }
- int CRequestSocket::StuffStatus( UINT uStatus )
- {
- CString strStatus;
- strStatus.LoadString( uStatus );
- // save the status for this request....
- m_pRequest->m_uStatus = uStatus;
- // stuff the HTTP status line....
- return StuffStatus( strStatus );
- }
- int CRequestSocket::StuffError( UINT uMsg )
- {
- StuffStatus( uMsg );
- return StuffString( CRLF );
- }
- int CRequestSocket::StuffHeader( CString strName, CString strValue )
- {
- StuffString( strName );
- StuffString( ": " );
- StuffString( strValue );
- return StuffString( CRLF );
- }
- int CRequestSocket::StuffHeader( CString strName, int nValue )
- {
- CString strValue;
- StuffString( strName );
- StuffString( ": " );
- strValue.Format( "%d", nValue );
- StuffString( strValue );
- return StuffString( CRLF );
- }
- BOOL CRequestSocket::StuffHeading( void )
- {
- BOOL bContinue = FALSE;
- if ( m_pRequest->m_dwAttr & FILE_ATTRIBUTE_HIDDEN )
- {
- // never show hidden files....
- StuffError( IDS_STATUS_FORBIDDEN );
- }
- else if ( m_pRequest->m_dwAttr & FILE_ATTRIBUTE_DIRECTORY )
- {
- if ( m_pDoc->m_bAllowListing )
- {
- // create a directory listing....
- StuffStatus( IDS_STATUS_OK );
- StuffString( CRLF );
- bContinue = TRUE;
- }
- else
- StuffError( IDS_STATUS_FORBIDDEN );
- }
- #ifdef IMPL_CGI
- else if ( m_hFile != INVALID_HANDLE_VALUE )
- {
- // cgi's output file will be opened already....
- CString strStatus, strHeaders;
- // loop until we find a blank line....
- DWORD dwRead = 0;
- CByteArray baFile;
- baFile.SetSize( 1024 );
- // read next chunk....
- BOOL bRead = ReadFile( m_hFile, baFile.GetData(),
- baFile.GetSize(), &dwRead, NULL );
- while ( dwRead > 0 )
- {
- int ndx = 0;
- while( GetLine( baFile, dwRead, ndx ) == TRUE )
- {
- BOOL bSave = TRUE;
- // stuff any non-empty lines.....
- if ( m_strLine.IsEmpty() )
- {
- // we found our empty line;
- // back up to where we left off....
- DWORD dwPos = SetFilePointer( m_hFile,
- ndx - dwRead,
- NULL, FILE_CURRENT );
- // and we're off....
- bContinue = TRUE;
- break;
- }
- else
- {
- int nPos = m_strLine.Find( ':' );
- if ( nPos != -1 )
- {
- CString strName = m_strLine.Left( nPos );
- strName.TrimLeft();
- strName.TrimRight();
- CString strVal = m_strLine.Mid( nPos+1 );
- strVal.TrimLeft();
- strVal.TrimRight();
- if ( strName.CompareNoCase("Status") == 0 )
- {
- strStatus = strVal;
- bSave = FALSE;
- }
- else if ( strName.CompareNoCase("Location") == 0 )
- {
- strStatus.LoadString( IDS_STATUS_MOVEDTEMP );
- }
- }
- }
- // save the header (if we want to)....
- if ( bSave )
- strHeaders += m_strLine + CRLF;
- m_strLine.Empty();
- }
- // read next chunk if we're not done....
- if ( bContinue )
- break;
- else
- ReadFile( m_hFile, baFile.GetData(),
- baFile.GetSize(), &dwRead, NULL );
- }
- if ( strStatus.IsEmpty() )
- StuffStatus( IDS_STATUS_OK );
- else
- StuffStatus( strStatus );
- // stuff the headers....
- StuffString( strHeaders );
- // stuff the blank line....
- StuffString( CRLF );
- }
- #endif // IMPL_CGI
- else
- {
- // open the file....
- m_hFile = CreateFile( m_pRequest->m_strFullPath,
- GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
- FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,
- NULL );
- if ( m_hFile != INVALID_HANDLE_VALUE )
- {
- if ( m_reqStatus != REQ_SIMPLE )
- {
- CTime timeIfMod;
- CString strIfMod = m_pRequest->GetHeaderValue( "If-Modified-Since" );
- if ( strIfMod.GetLength() > 0 &&
- FromHttpTime( strIfMod, timeIfMod ) &&
- !IfModSince( timeIfMod ) )
- {
- // eh, it hasn't been modified....
- StuffStatus( IDS_STATUS_NOTMODIFIED );
- // don't need it anymore....
- CloseHandle( m_hFile );
- m_hFile = INVALID_HANDLE_VALUE;
- }
- else
- {
- // send it off....
- StuffStatus( IDS_STATUS_OK );
- // any other header info....
- StuffFileType();
- StuffHeader( "Content-length", GetFileSize( m_hFile, NULL ) );
- // get the last modified time....
- FILETIME ft;
- if ( GetFileTime( m_hFile, NULL, NULL, &ft ) )
- {
- StuffHeader( "Last-Modified", GetHttpDate( &ft ) );
- }
- bContinue = TRUE;
- }
- // blank line....
- StuffString( CRLF );
- }
- else
- bContinue = TRUE;
- }
- else
- {
- // couldn't open; try again later....
- StuffError( IDS_STATUS_SVCUNAVAIL );
- }
- }
- return bContinue;
- }
- void CRequestSocket::StartTargetStuff( void )
- {
- if ( m_hFile != INVALID_HANDLE_VALUE)
- {
- DWORD dwRead = 0;
- // read the first chunk....
- ReadFile( m_hFile, m_buf.GetData() + m_cbOut,
- m_buf.GetSize()-m_cbOut, &dwRead, NULL );
- if ( dwRead > 0 )
- m_cbOut += dwRead;
- else
- {
- // nothing read.... close the file....
- CloseHandle( m_hFile );
- m_hFile = INVALID_HANDLE_VALUE;
- }
- }
- else if ( m_pRequest->m_dwAttr & FILE_ATTRIBUTE_DIRECTORY )
- StuffListing();
- else
- StuffString( CRLF );
- }
- void CRequestSocket::StuffListing( void )
- {
- BOOL bRoot = FALSE;
- BOOL bIcons = m_pDoc->m_bListIcon;
- CString strIcon;
- CString strLine = CString("http://")
- + m_pDoc->m_strServer
- + m_pRequest->m_strURL;
- CString strDir = m_pRequest->m_strURL;
- CString strMask = m_pRequest->m_strFullPath;
- // make sure URL ends in a slash....
- if ( strDir.GetAt( strDir.GetLength()-1 ) != '/' )
- strDir += '/';
- // is this the server's root folder?
- else if ( strDir.Compare( "/" ) == 0 )
- bRoot = TRUE;
- // create the file search mask....
- AddFile( strMask, IDS_DIRMASK );
- StuffString( IDS_CONTENTS_PRE );
- StuffString( strLine );
- StuffString( IDS_CONTENTS_POST );
- if ( bRoot )
- StuffString( IDS_CONTENTS_DESC );
- if ( bIcons )
- strIcon.LoadString( IDS_ICON_BLANK );
- strLine.Format( IDS_CONTENTS_HEADING, strIcon );
- StuffString( strLine );
- int nFiles = 0;
- WIN32_FIND_DATA fd;
- // find the first file that matches the mask....
- HANDLE fh = FindFirstFile( strMask, &fd );
- if ( fh != INVALID_HANDLE_VALUE )
- {
- // create a line for the found file....
- nFiles += StuffListingFile( &fd, strDir, bIcons );
- // loop through all other files....
- while ( FindNextFile( fh, &fd ) )
- nFiles += StuffListingFile( &fd, strDir, bIcons );
- }
- if ( nFiles == 0 )
- StuffString( IDS_CONTENTS_EMPTY );
- StuffString( IDS_CONTENTS_FOOTER );
- // only add the parent link if there is one....
- if ( !bRoot )
- {
- if ( bIcons )
- strIcon.LoadString( IDS_ICON_PARENT );
- strLine.Format( IDS_CONTENTS_PARENT, strIcon );
- StuffString( strLine );
- }
- // add the note and end it....
- StuffString( IDS_CONTENTS_NOTE );
- StuffString( CRLF );
- }
- int CRequestSocket::StuffListingFile( WIN32_FIND_DATA* pfd, const CString& strDir, BOOL bIcons )
- {
- int nFile = 0;
- // don't include '.', '..' or hidden files....
- if ( lstrcmp( pfd->cFileName, "." ) && lstrcmp( pfd->cFileName, ".." )
- && (pfd->dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) == 0 )
- {
- CString strSize, strIcon = "";
- CString strLine, strFile = pfd->cFileName;
- CTime timeFile( pfd->ftLastWriteTime );
- BOOL bFolder = ((pfd->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0);
- if ( bIcons && bFolder )
- strIcon.LoadString( IDS_ICON_FOLDER );
- else if ( bIcons )
- strIcon.LoadString( IDS_ICON_FILE );
- // create the link string....
- CString strLink = strDir + strFile;
- // make sure spaces are replaced with '%20'...
- int ndx;
- while ( (ndx=strLink.Find(' ')) != -1 )
- strLink = strLink.Left(ndx) + "%20" + strLink.Mid( ndx+1 );
- // format the size string....
- if ( bFolder )
- strSize = " Folder";
- else if ( pfd->nFileSizeHigh > 0 )
- strSize = " > 4GB"; // yeah, right.
- else if ( pfd->nFileSizeLow < 1024 )
- strSize = " < 1K";
- else
- strSize.Format( "%7dK", pfd->nFileSizeLow/1024 );
- strLine.Format( IDS_CONTENTS_FORMAT,
- timeFile.Format( IDS_FILETIMEFMT ),
- strSize, strLink, strIcon, strFile );
- StuffString( strLine );
- nFile = 1;
- }
- return nFile;
- }
- BOOL CRequestSocket::StartSvrApp( void )
- {
- #ifdef IMPL_CGI
- if ( m_pRequest->m_dwExecute != CRequest::APP_ISAPI )
- return CGIStart();
- else
- {
- StuffError( IDS_STATUS_NOTIMPL );
- return FALSE;
- }
- #else // IMPL_CGI
- StuffError( IDS_STATUS_NOTIMPL );
- return FALSE;
- #endif // IMPL_CGI
- }
- #ifdef IMPL_CGI
- BOOL CRequestSocket::CGIStart( void )
- {
- BOOL bOk = FALSE;
- // get the temp path...
- CString strTempPath;
- GetTempPath( MAX_PATH, strTempPath.GetBuffer(MAX_PATH) );
- strTempPath.ReleaseBuffer();
- // create a temporary file for the output....
- CString strTempName;
- GetTempFileName( strTempPath, "CGI", 0, strTempName.GetBuffer(MAX_PATH) );
- strTempName.ReleaseBuffer();
- m_hFile = CreateFile( strTempName, GENERIC_READ|GENERIC_WRITE,
- FILE_SHARE_READ|FILE_SHARE_WRITE, &g_sa,
- CREATE_ALWAYS, FILE_ATTRIBUTE_TEMPORARY|FILE_FLAG_DELETE_ON_CLOSE, NULL );
- if ( m_hFile != INVALID_HANDLE_VALUE )
- {
- // create the cancel event....
- m_pCancel = new CEvent;
- if ( m_pCancel )
- {
- // make sure the event is reset....
- m_pCancel->ResetEvent();
- // create the CGI thread suspended....
- m_pThread = AfxBeginThread( (AFX_THREADPROC)CGIThread,
- (LPVOID)this, THREAD_PRIORITY_NORMAL, 0,
- CREATE_SUSPENDED, NULL );
- if ( m_pThread )
- {
- // don't self-destruct (we must delete)....
- m_pThread->m_bAutoDelete = FALSE;
- // resume...
- m_pThread->ResumeThread();
- bOk = TRUE;
- }
- }
- }
- if ( bOk == FALSE )
- {
- StuffError( IDS_STATUS_SVRERROR );
- if( m_hFile != INVALID_HANDLE_VALUE )
- { // JIC....
- CloseHandle( m_hFile );
- m_hFile = INVALID_HANDLE_VALUE;
- }
- }
- return bOk;
- }
- void AddEnvVar( CString& strEnv, CString strName, CString strVal )
- {
- // add the name=val pair to the env in alphabetical order....
- strEnv += strName + '=' + strVal + 'a';
- }
- UINT CGIThread( LPVOID pvParam )
- {
- CRequestSocket* pReqSock = (CRequestSocket*)pvParam;
- CRequest* pRequest = pReqSock->m_pRequest;
- BOOL bOk = FALSE;
- DWORD dwErr;
- HANDLE hWritePipe, hReadPipe;
- // create a pipe we'll use for STDIN....
- if ( CreatePipe( &hReadPipe, &hWritePipe, &g_sa, 0 ) )
- {
- // get the command line together....
- CString strCmdLine = pRequest->m_strFullPath
- + ' '
- + Decode( pRequest->m_strArgs, TRUE );
- // get the directory....
- CString strDir = pRequest->m_strFullPath;
- int ndx = strDir.ReverseFind( SEPCHAR );
- // assume we found it....
- strDir = strDir.Left( ndx+1 );
- // create an environment for the CGI process....
- DWORD dwCreateFlags = 0;
- #ifdef UNICODE
- dwCreateFlags = CREATE_UNICODE_ENVIRONMENT;
- #endif // UNICODE
- CEnvironment cEnv;
- CString strValue;
- strValue.LoadString( IDS_SERVER_NAME );
- cEnv.Add( "SERVER_SOFTWARE", strValue );
- cEnv.Add( "SERVER_NAME", pReqSock->m_pDoc->m_strServer );
- cEnv.Add( "GATEWAY_INTERFACE", "CGI/1.1" );
- cEnv.Add( "SERVER_PROTOCOL", "HTTP/1.0" );
- strValue.Format( "%d", pReqSock->m_pDoc->m_uPort );
- cEnv.Add( "SERVER_PORT", strValue );
- cEnv.Add( "REQUEST_METHOD", pRequest->m_strMethod );
- cEnv.Add( "SCRIPT_NAME", pRequest->m_strURL );
- cEnv.Add( "QUERY_STRING", pRequest->m_strArgs );
- cEnv.Add( "REMOTE_ADDR", pRequest->m_strHost );
- if ( pRequest->m_cbBody > 0 )
- {
- cEnv.Add( "CONTENT_LENGTH", pRequest->GetHeaderValue("Content-Length") );
- cEnv.Add( "CONTENT_TYPE", pRequest->GetHeaderValue("Content-Type") );
- }
- if ( !pRequest->m_strPathInfo.IsEmpty() )
- {
- cEnv.Add( "PATH_INFO", pRequest->m_strPathInfo );
- cEnv.Add( "PATH_TRANSLATED", pRequest->m_strPathTranslated );
- }
- // all the passed headers prefixed with "HTTP_"....
- POSITION pos = pRequest->m_mapHeaders.GetStartPosition();
- while ( pos != NULL )
- {
- // get the name/value pair....
- CString strName, strValue;
- pRequest->m_mapHeaders.GetNextAssoc( pos, strName, strValue );
- HeaderToEnvVar( strName );
- // set the environment variable....
- cEnv.Add( strName, strValue );
- }
- // create the process....
- LPVOID pEnv = (LPVOID)cEnv.GetBlock();
- PROCESS_INFORMATION pi;
- STARTUPINFO si = {0};
- si.cb = sizeof(si);
- si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
- si.wShowWindow = SW_HIDE;
- si.hStdInput = hReadPipe;
- si.hStdOutput = pReqSock->m_hFile;
- si.hStdError = pReqSock->m_hFile;
- bOk = CreateProcess( NULL, strCmdLine.GetBuffer(1),
- NULL, NULL, TRUE,
- dwCreateFlags, pEnv,
- strDir, &si, &pi );
- strCmdLine.ReleaseBuffer();
- // if created....
- if ( bOk )
- {
- // release our hold on the thread....
- CloseHandle( pi.hThread );
- // send the body of the post to the stdin....
- if ( pRequest->m_cbBody > 0 )
- {
- DWORD dwWritten = 0;
- WriteFile( hWritePipe, pRequest->m_baBody.GetData(),
- pRequest->m_cbBody, &dwWritten, NULL );
- }
- // wait for either cancel or process done....
- HANDLE aHandles[2];
- aHandles[0] = pi.hProcess;
- aHandles[1] = pReqSock->m_pCancel->m_hObject;
- if ( WaitForMultipleObjects( 2, aHandles, FALSE, INFINITE ) == WAIT_OBJECT_0 )
- {
- // process finished; notify main thread....
- AfxGetApp()->m_pMainWnd->PostMessage( WSM_CGIDONE, 0, (LPARAM)pReqSock );
- }
- else
- {
- // canceled or some other error....
- bOk = FALSE;
- }
- // close our hold on it....
- CloseHandle( pi.hProcess );
- }
- else
- dwErr = GetLastError();
- // close the stdin pipe....
- CloseHandle( hWritePipe );
- CloseHandle( hReadPipe );
- delete pEnv;
- }
- if ( bOk == FALSE && pReqSock->m_hFile != INVALID_HANDLE_VALUE )
- { // JIC....
- CloseHandle( pReqSock->m_hFile );
- pReqSock->m_hFile = INVALID_HANDLE_VALUE;
- }
- return (bOk?0:1);
- }
- void CRequestSocket::CGIDone( void )
- {
- if ( !m_bKilled )
- {
- // flush the temp file's buffers....
- BOOL bSucceed = FlushFileBuffers( m_hFile );
- // go to start of file....
- DWORD dwPos = SetFilePointer( m_hFile, 0, NULL, FILE_BEGIN );
- // output the header....
- StuffHeading();
- if ( m_pRequest->m_strMethod.Compare( "HEAD" ) )
- StartTargetStuff();
- else
- {
- CloseHandle( m_hFile );
- m_hFile = INVALID_HANDLE_VALUE;
- }
- AsyncSelect( FD_WRITE | FD_CLOSE );
- }
- else
- {
- CloseHandle( m_hFile );
- m_hFile = INVALID_HANDLE_VALUE;
- }
- }
- void HeaderToEnvVar( CString& strVar )
- {
- int ndx;
- // make upper case, change '-' to '_', and prefix....
- strVar.MakeUpper();
- while( (ndx = strVar.Find('-')) != -1 )
- strVar = strVar.Left(ndx) + '_' + strVar.Mid(ndx+1);
- strVar = "HTTP_" + strVar;
- }
- CEnvironment::CEnvironment( void )
- {
- m_nSize = 2;
- }
- CEnvironment::~CEnvironment( void )
- {
- }
- BOOL CEnvironment::Add( CString name, CString value )
- {
- BOOL bOk = TRUE;
- // create the entry pair string....
- CString strPair = name + __TEXT('=') + value;
- m_nSize += strPair.GetLength() + 1;
- POSITION pos = m_list.GetHeadPosition();
- // find the first item bigger than this string....
- while( pos != NULL )
- {
- if ( m_list.GetAt(pos).CompareNoCase(strPair) > 0 )
- {
- m_list.InsertBefore( pos, strPair );
- break;
- }
- m_list.GetNext( pos );
- }
- if ( pos == NULL )
- m_list.AddTail( strPair );
- return bOk;
- }
- LPVOID CEnvironment::GetBlock( void )
- {
- // allocate a block....
- PTCHAR pBlock = new TCHAR[m_nSize];
- if ( pBlock )
- {
- // iterate through the list....
- PTCHAR pPos = pBlock;
- POSITION pos = m_list.GetHeadPosition();
- while( pos != NULL )
- {
- CString& str = m_list.GetNext( pos );
- // copy the string....
- lstrcpy( pPos, str );
- pPos += str.GetLength() + 1;
- }
- // NULL for the whole list....
- *pPos = __TEXT(' ');
- }
- return pBlock;
- }
- #endif // IMPL_CGI
- CString CRequestSocket::GetHttpDate( LPFILETIME pft )
- {
- SYSTEMTIME st;
- if ( pft )
- FileTimeToSystemTime( pft, &st );
- else
- GetSystemTime( &st );
- CTime timeHttp( st );
- return timeHttp.Format( IDS_HTTPTIME );
- }
- BOOL CRequestSocket::IfModSince( const CTime& timeIfMod )
- {
- // assume it has been modified....
- BOOL bOk = TRUE;
- FILETIME ft;
- if ( GetFileTime( m_hFile, NULL, NULL, &ft ) )
- {
- SYSTEMTIME st;
- if ( FileTimeToSystemTime( &ft, &st ) )
- {
- CTime timeFile( st );
- if ( timeFile <= timeIfMod )
- bOk = FALSE;
- }
- }
- return bOk;
- }
- static int IntVal( CString strVal )
- {
- int nVal = 0;
- strVal.TrimLeft();
- for( int ndx = 0; ndx < strVal.GetLength(); ++ndx )
- nVal = nVal*10 + strVal.GetAt(ndx) - '0';
- return nVal;
- }
- static int MonthFromStr( const CString& str )
- {
- LPSTR aMonths[] = {
- "xxx", "jan", "feb", "mar", "apr", "may", "jun",
- "jul", "aug", "sep", "oct", "nov", "dec" };
- for( int nMonth=1; nMonth <= 12; ++nMonth )
- {
- if ( str.CompareNoCase( aMonths[nMonth] ) == 0 )
- break;
- }
- return nMonth;
- }
- // Dow, dd Mon year hh:mm:ss GMT
- BOOL CRequestSocket::FromHttpTime( const CString& strHttp, CTime& timeHttp )
- {
- // assume we couldn't get a good time conversion....
- BOOL bOk = FALSE;
- SYSTEMTIME st = {0};
- int ndx;
- switch( strHttp.GetAt(3) )
- {
- case ',':
- // read RFC-1123 (preferred)....
- st.wDay = IntVal( strHttp.Mid(5,2) );
- st.wMonth = MonthFromStr( strHttp.Mid(8,3) );
- st.wYear = IntVal( strHttp.Mid(12,4) );
- st.wHour = IntVal( strHttp.Mid(17,2) );
- st.wMinute = IntVal( strHttp.Mid(20,2) );
- st.wSecond = IntVal( strHttp.Mid(23,2) );
- break;
- case ' ':
- // read ANSI-C time format....
- st.wDay = IntVal( strHttp.Mid(8,2) );
- st.wMonth = MonthFromStr( strHttp.Mid(4,3) );
- st.wYear = IntVal( strHttp.Mid(20,4) );
- st.wHour = IntVal( strHttp.Mid(11,2) );
- st.wMinute = IntVal( strHttp.Mid(14,2) );
- st.wSecond = IntVal( strHttp.Mid(17,2) );
- break;
- default:
- if ( (ndx = strHttp.Find( ", " )) != -1 )
- {
- st.wDay = IntVal( strHttp.Mid(ndx+2,2) );
- st.wMonth = MonthFromStr( strHttp.Mid(ndx+5,3) );
- st.wYear = IntVal( strHttp.Mid(ndx+9,2) );
- st.wHour = IntVal( strHttp.Mid(ndx+12,2) );
- st.wMinute = IntVal( strHttp.Mid(ndx+15,2) );
- st.wSecond = IntVal( strHttp.Mid(ndx+18,2) );
- // add the correct century....
- st.wYear += (st.wYear > 50)?1900:2000;
- }
- break;
- }
- // if year not zero, we pulled the info out of the string....
- if ( st.wYear != 0 )
- {
- // assume GMT....
- CTime strTime( st );
- // check to see if the minutes are the same....
- if ( strTime.GetMinute() == st.wMinute )
- {
- // assume it worked....
- timeHttp = strTime;
- bOk = TRUE;
- }
- }
- return bOk;
- }
- int CRequestSocket::AddRef( void )
- {
- return ++m_nRefs;
- }
- int CRequestSocket::Release( void )
- {
- int nRefs = --m_nRefs;
- if ( nRefs == 0 )
- delete this;
- return nRefs;
- }
- void CRequestSocket::StuffFileType( void )
- {
- // get the extension....
- CString strExt = m_pRequest->m_strFullPath.Mid(
- m_pRequest->m_strFullPath.ReverseFind('.') );
- // find it in the registry....
- HKEY hKey = NULL;
- if ( RegOpenKeyEx( HKEY_CLASSES_ROOT, strExt,
- 0, KEY_READ, &hKey ) == ERROR_SUCCESS )
- {
- DWORD dwSize = 0;
- // see how long the data is....
- if ( RegQueryValueEx( hKey, "Content Type", NULL, NULL,
- NULL, &dwSize ) == ERROR_SUCCESS )
- {
- CString strType;
- LONG lRet = RegQueryValueEx( hKey, "Content Type", NULL, NULL,
- (LPBYTE)strType.GetBuffer( dwSize ), &dwSize );
- strType.ReleaseBuffer();
- if ( lRet == ERROR_SUCCESS )
- StuffHeader( "Content-type", strType );
- }
- RegCloseKey( hKey );
- }
- }
- CString Decode( const CString& str, BOOL bQuery )
- {
- int ndx;
- CString strDecoded = str;
- // special processing or query strings....
- if ( bQuery )
- {
- // change all '+' to ' '....
- while( (ndx=strDecoded.Find('+')) != -1 )
- strDecoded = strDecoded.Left(ndx) + ' ' + strDecoded.Mid(ndx+1);
- }
- // first see if there are any %s to decode....
- if ( strDecoded.Find( '%' ) != -1 )
- {
- // iterate through the string, changing %dd to special char....
- for( ndx=0; ndx < strDecoded.GetLength(); ndx++ )
- {
- char ch = strDecoded.GetAt( ndx );
- if ( ch == '%' )
- {
- if ( strDecoded.GetAt( ndx+1 ) == '%' )
- {
- // wanna keep one percent sign....
- strDecoded = strDecoded.Left(ndx) + strDecoded.Mid(ndx+1);
- }
- else
- {
- // assume we have a hex value....
- char ch1 = strDecoded.GetAt(ndx+1);
- char ch2 = strDecoded.GetAt(ndx+2);
- ch1 = ch1 >= 'A' ? (ch1&0xdf)-'A' : ch1-'0';
- ch2 = ch2 >= 'A' ? (ch2&0xdf)-'A' : ch2-'0';
- // replace the escape sequence with the char....
- strDecoded = strDecoded.Left(ndx)
- + (char)(ch1*16 + ch2)
- + strDecoded.Mid( ndx+3 );
- }
- }
- }
- }
- return strDecoded;
- }
- CString CRequestSocket::StripLast( CString& strPath )
- {
- CString strExtra;
- if ( !strPath.IsEmpty() )
- {
- int ndx = strPath.ReverseFind( SEPCHAR );
- if ( ndx < 0 )
- ndx = 0;
- strExtra = strPath.Mid( ndx );
- strPath = strPath.Left( ndx );
- }
- return strExtra;
- }
- BOOL CRequestSocket::CheckDefault( UINT uList, BOOL bExecute )
- {
- BOOL bFound = FALSE;
- DWORD dwAttr;
- CString strDefault, strDefList;
- strDefList.LoadString( uList );
- while ( !strDefList.IsEmpty() )
- {
- int ndx;
- strDefault = m_pRequest->m_strFullPath;
- if ( (ndx=strDefList.Find('n')) == -1 )
- {
- AddFile( strDefault, strDefList );
- strDefList.Empty();
- }
- else
- {
- AddFile( strDefault, strDefList.Left(ndx) );
- strDefList = strDefList.Mid( ndx+1 );
- }
- if ( (dwAttr=GetFileAttributes(strDefault)) != -1 &&
- (dwAttr & FILE_ATTRIBUTE_DIRECTORY) == 0 )
- {
- bFound = TRUE;
- break;
- }
- }
- if ( bFound )
- {
- // redirect to the default file....
- PathToURL( strDefault );
- if ( bExecute )
- strDefault += '?';
- StuffStatus( IDS_STATUS_MOVEDTEMP );
- StuffHeader( "Location", strDefault );
- StuffString( CRLF );
- }
- return bFound;
- }
- BOOL CRequestSocket::IsSvrApp( void )
- {
- BOOL bOk = FALSE;
- int ndx = m_pRequest->m_strFullPath.ReverseFind( '.' );
- if ( ndx != -1 )
- {
- CString strExt = m_pRequest->m_strFullPath.Mid( ndx+1 );
- CString strAvail;
- // check if CGI app....
- strAvail.LoadString( IDS_APP_CGI );
- bOk = CheckExt( strExt, strAvail, CRequest::APP_CGI );
- if ( !bOk )
- {
- strAvail.LoadString( IDS_APP_ISAPI );
- bOk = CheckExt( strExt, strAvail, CRequest::APP_ISAPI );
- }
- }
- return bOk;
- }
- BOOL CRequestSocket::CheckExt( const CString& strExt, CString& strAvail, DWORD dwType )
- {
- BOOL bMatch = FALSE;
- CString strPossible;
- // loop through all possible exts....
- while( !strAvail.IsEmpty() )
- {
- int ndx = strAvail.ReverseFind('n');
- if ( ndx == -1 )
- {
- strPossible = strAvail;
- strAvail.Empty();
- }
- else
- {
- strPossible = strAvail.Mid( ndx+1 );
- strAvail = strAvail.Left( ndx );
- }
- if ( strExt.CompareNoCase( strPossible ) == 0 )
- {
- m_pRequest->m_dwExecute = dwType;
- bMatch = TRUE;
- break;
- }
- }
- return bMatch;
- }