www.pudn.com > pueblo.zip > ChHTPriv.cpp


/*---------------------------------------------------------------------------- 
                        _                              _ _        
        /\             | |                            | (_)       
       /  \   _ __   __| |_ __ ___  _ __ ___   ___  __| |_  __ _  
      / /\ \ | '_ \ / _` | '__/ _ \| '_ ` _ \ / _ \/ _` | |/ _` | 
     / ____ \| | | | (_| | | | (_) | | | | | |  __/ (_| | | (_| | 
    /_/    \_\_| |_|\__,_|_|  \___/|_| |_| |_|\___|\__,_|_|\__,_| 
 
    The contents of this file are subject to the Andromedia Public 
	License Version 1.0 (the "License"); you may not use this file 
	except in compliance with the License. You may obtain a copy of 
	the License at http://www.andromedia.com/APL/ 
 
    Software distributed under the License is distributed on an 
	"AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or 
	implied. See the License for the specific language governing 
	rights and limitations under the License. 
 
    The Original Code is Pueblo client code, released November 4, 1998. 
 
    The Initial Developer of the Original Code is Andromedia Incorporated. 
	Portions created by Andromedia are Copyright (C) 1998 Andromedia 
	Incorporated.  All Rights Reserved. 
 
	Andromedia Incorporated                         415.365.6700 
	818 Mission Street - 2nd Floor                  415.365.6701 fax 
	San Francisco, CA 94103 
 
    Contributor(s): 
	-------------------------------------------------------------------------- 
	   Chaco team:  Dan Greening, Glenn Crocker, Jim Doubek, 
	                Coyote Lussier, Pritham Shetty. 
 
					Wrote and designed original codebase. 
 
------------------------------------------------------------------------------ 
 
	This file contains the implementation of the ChHTTPSocketConn class, used to 
	manage a connection for downloading modules and data from the server. 
 
----------------------------------------------------------------------------*/ 
 
 
#include "headers.h" 
 
#ifdef CH_UNIX 
#include  
#include  
#include  
#define _STREAM_COMPAT 1 
#endif 
 
#include  
#include  
#include  
#ifdef CH_MSW 
#if defined( CH_ARCH_16 ) 
#include  
#include  
#include  
#endif 
#include  
#include  
#include  
#endif 
 
#include  
#include  
#include  
#include  
#include  
 
#include  
#include  
#include  
#include  
#include  
 
#include "ChHttpCookie.h" 
#include "ChHTPriv.h" 
#include "ChHttpThreadMgr.h" 
 
 
#if defined( CH_VRML_VIEWER ) 
#include "ChGrType.h" 
#endif 
											// Tokens processed 
char* pstrHTTPTokens[] = 
{ 
	"HTTP", 
	"Content-type", 
	"Last-modified", 
	"Content-length", 
	"Date", 
	"Content-encoding", 
	"Location", 
	"Set-Cookie", 
}; 
 
											/* Initialize static member 
												variables */ 
 
ChDataBase* 		ChHTTPInfo::m_pURLDB = 0;			 // URL cache database object 
int 				ChHTTPInfo::m_iActiveConn = 0;		 // Total active connections 
 
 
int					ChHTTPInfo::m_boolThreaded = 0; 
string				ChHTTPInfo::m_strCacheDir; 
 
ChHTTPVisitedList	ChHTTPInfo::m_httpVisited;	 		// Current session visited 
 
 
 
 
#if	defined( CH_MSW ) && defined( CH_ARCH_32 ) 
 
CRITICAL_SECTION	ChHTTPInfo::m_httpSync; 
 
#endif	// defined( CH_MSW ) && defined( CH_ARCH_32 ) 
 
											// Request queue 
 
#if !defined( CH_UNIX ) 
 
ChHTTPInfoList 		ChHTTPInfo::m_httpRequestQueue; 
 
											// Currently active connection list 
 
ChHTTPInfoList 		ChHTTPInfo::m_httpConnList; 
 
ChSocketAddrList 	ChHTTPInfo::m_socketAddrList; 
ChSocketDelList* 	ChHTTPInfo::m_psocketDelList = 0; 
ChHttpThreadMgr*	ChHTTPInfo::m_pThreadMgr = 0; 
 
#endif	// !defined( CH_UNIX ) 
 
 
/*---------------------------------------------------------------------------- 
	Constants 
----------------------------------------------------------------------------*/ 
 
#ifdef CH_MSW 
#define	NAME_BASE_LEN	8 
#define NAME_EXT_LEN	3 
#else 
#define	NAME_BASE_LEN	255 
#define NAME_EXT_LEN	255 
#endif 
 
 
 
#define old_url "http://www2.chaco.com/~glenn/pueblo/" 
#define new_url "http://www.chaco.com/pueblo/" 
 
/*---------------------------------------------------------------------------- 
	Functions: 
----------------------------------------------------------------------------*/ 
 
 
 
 
/*---------------------------------------------------------------------------- 
	ChHTTPInfo class  static initializer called by ChHTTPSocketConn 
----------------------------------------------------------------------------*/ 
 
 
void ChHTTPInfo::InitHTTPInfo(  ) 
{ 
											/* Check if the system we are running under 
											  supports multithreading */ 
	m_boolThreaded = !!(ChUtil::GetSystemProperties() & CH_PROP_MULTITHREADED); 
 
	// open the database 
	OpenCacheDataBase(); 
 
	#if	defined(CH_MSW ) && defined( WIN32 ) 
	if (  IsThreaded() ) 
	{									// Initialize the critcal section 
 
		::InitializeCriticalSection( &m_httpSync ); 
 
		m_pThreadMgr = new ChHttpThreadMgr(); 
 
		AfxBeginThread( HTTPRequestThread, m_pThreadMgr ); //,  
							//THREAD_PRIORITY_BELOW_NORMAL ); 
 
	} 
	else 
	{ 
 
		// Create the deferred delete list for Win16 and Win32s,  
		// Deleting socket connection inside a sockent handler causes an ASSERT 
		// if there is a pending connection. 
 
		m_psocketDelList = new ChSocketDelList; 
	} 
	#endif 
} 
 
void ChHTTPInfo::OpenCacheDataBase() 
{ 
	#if defined( CH_VRML_VIEWER ) 
	ChRegistry reg( CH_COMPANY_NAME, CH_VRML_PRODUCT_NAME, CH_CACHE_GROUP ); 
	#else 
	ChRegistry	reg( CH_CACHE_GROUP ); 
	#endif 
 
	string		strDir; 
											/* Get the local cache directory. 
												Start with the app path. */ 
	ChUtil::GetAppDirectory( strDir ); 
 
	#if defined( CH_MSW ) 
	{ 
		strDir += CACHE_DIR; 
 
		reg.Read( CH_CACHE_DIR, m_strCacheDir, strDir ); 
	} 
	#elif defined( CH_UNIX ) 
	{ 
		strDir = ChUtil::GetPuebloDir() + "/" + CACHE_DIR; 
		m_strCacheDir = strDir; 
	} 
	#else	// defined( CH_MSW ) 
	{ 
		#error "ChHTTPInfo::OpenCacheDataBase : OS not defined" 
	} 
	#endif	// defined( CH_MSW ) 
 
	if (!ChUtil::FileExists( m_strCacheDir )) 
	{ 
		ChUtil::CreateDirectoryTree( m_strCacheDir ); 
 
		if (!ChUtil::FileExists( m_strCacheDir )) 
		{ 
			ASSERT( "Unable to create cache directory" ); 
		} 
	} 
 
	// data base name and location 
	string strDB = m_strCacheDir; 
	strDB += PATH_SEPARATOR_CHAR; 
	strDB += URL_DBNAME; 
										// open a new data base 
	m_pURLDB = new ChDataBase( strDB ); 
 
	if (m_pURLDB->IsValidDB()) 
	{									// open the database 
		m_pURLDB->OpenDB( CHDB_READWRITE ); 
	} 
	else 
	{ 
		m_pURLDB->OpenDB( CHDB_CREATE | CHDB_READWRITE ); 
	} 
} 
 
/*---------------------------------------------------------------------------- 
	ChHTTPInfo class 
----------------------------------------------------------------------------*/ 
ChHTTPInfo::ChHTTPInfo( ChHTTPSocketConn*	pHTTPConn, const string& strURL, 
						chparam userData, const char* pstrHostName, 
						chuint32 flOptions, ChHTTPStreamManager* pStreamMgr ) 
{ 
											/* Check the data base if we have 
												the module or data locally */ 
											// Initialize all member variables 
	#if	defined(CH_MSW ) && defined( WIN32 ) 
	m_pThread = NULL; 
	#endif 
	m_pAltStreamMgr			= pStreamMgr; 
	m_pHTTPConn 			= pHTTPConn; 
	m_userData				= userData; 
	m_pSocket 				= 0; 
	m_luTotalLen 			= 0; 
	m_pstrBuffer 			= 0; 
	m_luBufferSize          = 0; 
	m_luToProcess 			= 0; 
	m_CurrState 			= 0; 
	m_luBodyLen 			= 0; 
	m_flOptions 			= flOptions; 
	m_pFile					= 0; 
	m_boolCache 			= false; 
	m_boolCreateByMe 		= false;		// socket create by this object 
	m_bbPrefetched			= false; 		// this is set to true if we find this in our 
											// cache but has not been visited 
	m_bbAbort				= false;		// set this to true to abort operation 
	m_bbCompleted			= false;		// set to true when the HTTP request 
											// is successful : 
	//m_boolNotitfied			= false;		// Not notified yet 
	m_nZipType				= ChUnzip::typeUnknown;	  
	m_iError 				= 0; 
	m_boolUpdateInfo		= false; 
 
	// save the current pointer in HTTP stream 
	m_httpStream.m_pHTTPInfo = this; 
	m_iStreamState = streamUnInitialized; 
 
	SetState( HTTP_BEGIN_PROCESS );	// Current state of my HTTP state machine 
 
 
 
											// Get the different URL parts 
   	if ( !m_urlParts.GetURLParts( strURL, pstrHostName ) ) 
	{  // bad URL 
	 	m_bbAbort = true; 
		SetError( CH_HTTP_ERROR_BAD_URL ); 
		return; 
	} 
 
	switch( m_urlParts.GetScheme() ) 
	{ 
		case ChURLParts::typeFile : 
		{ 
			HandleFileScheme(); 
			break; 
		} 
		default : 
		{ 
			HandleScheme(); 
			break; 
		} 
	} 
 
	return; 
} 
 
void ChHTTPInfo::HandleFileScheme() 
{ 
	if ( ChURLParts::MapURLToHostFile( m_urlParts.GetURL(), m_strLocalName ) ) 
	{ 
		if ( ChUtil::FileExists( m_strLocalName )) 
		{ 
			m_boolCache = true;	 
			ChHTTPConn::GetMimeTypeByFileExtn( m_strLocalName, m_strMimeType  ); 
 
			return; 
		} 
	} 
	// Failed  
 	m_bbAbort = true; 
	SetError( CH_HTTP_ERROR_BAD_URL ); 
} 
 
void ChHTTPInfo::HandleScheme() 
{ 
												/* Do we have the URL locally? 
												If we have the data locally 
												then get the last modified 
												date in GMT */ 
	string strKey = m_urlParts.GetURL(); 
 
	strKey.MakeLower();	   
	ChDBKey	dbKey( strKey ); 
 
	// Serialize HTTP access 	 
	LockHTTP(); 
 
 
	ChDBData dbData = m_pURLDB->GetData( dbKey ); 
 
	// Unlock serialization 
	UnlockHTTP(); 
 
	if ( m_flOptions & ChHTTPConn::returnCache && 0 == dbData.GetDataSize() ) 
	{ 	// not in our cache 
		return; 
	} 
 
	chuint32 luFileSize = 0; 
 
	if (dbData.GetDataSize()) 
	{										/* We have data, the data format is 
												"Local file path" "\0" 
												"Time in HTTP format" "\0" 
												"Size " "\0"*/ 
		char*   pstrEnd = (char*)dbData.GetData(); 
		pstrEnd += dbData.GetDataSize(); 
 
 
		char*	pstrDate; 
 
		// database stores only the file name, add the full path of the current database 
		m_strLocalName = m_strCacheDir; 
		m_strLocalName += PATH_SEPARATOR_CHAR; 
		m_strLocalName += (char*)dbData.GetData(); 
		pstrDate = (char*)dbData.GetData(); 
		pstrDate += (lstrlen( pstrDate ) + 1); 
	  	m_strLastModified = pstrDate; 
		pstrDate += (lstrlen( pstrDate ) + 1); 
		luFileSize = atol( pstrDate ); 
 
		m_boolCache = true; 
   		pstrDate += (lstrlen( pstrDate ) + 1); 
 
		m_strMimeType = ""; 
		if ( pstrDate < pstrEnd && *pstrDate ) 
		{ 
 
			chuint32 luOptions = atol( pstrDate ); 
			if ( luOptions & ChHTTPConn::PrefetchURL ) 
			{ 
				m_bbPrefetched = true; 
			} 
 
 
			if ( !( m_flOptions & ChHTTPConn::PrefetchURL) && 
						( luOptions & ChHTTPConn::PrefetchURL ) ) 
			{ 
				char strOptions[25]; 
				m_flOptions &= ~ChHTTPConn::PrefetchURL; 
				#if defined( CH_MSW ) 
				_ltoa( m_flOptions , strOptions, 10 ); 
				#else 
				ltoa( m_flOptions , strOptions, 10 ); 
				#endif 
 
				if ( MAX_OPTION_BLK > lstrlen( strOptions ) ) 
				{ 
					ChMemCopy( pstrDate, strOptions, lstrlen( strOptions ) ); 
				} 
				else 
				{ 
					ASSERT( false ); 
				} 
 
				// Serialize HTTP access 	 
				LockHTTP(); 
 
				m_pURLDB->SetData( dbKey, dbData, CHDB_REPLACE ); 
 
				// Unlock serialization 
				UnlockHTTP(); 
 
			} 
			// get the mime type 
			pstr pstrMime = pstrDate + MAX_OPTION_BLK; 
 
			if ( pstrMime < pstrEnd && *pstrMime ) 
			{ 
		   		m_strMimeType = pstrMime; 
				m_strMimeType.TrimLeft(); 
				m_strMimeType.TrimRight(); 
			} 
		} 
	} 
	else 
	{	 
		m_strLocalName.Empty(); 
	} 
	 
	if ( m_strLocalName.IsEmpty() ) 
	{ 
		m_boolCache = false; 
	} 
	else 
	{										// check if the local path is valid 
		if ( !ChUtil::FileExists( GetFileName() ) || luFileSize == 0 ) 
		{										/* File does not exist; check if 
													the directory is valid */ 
			m_boolCache = false; 
			m_strLastModified.Empty(); 
 
			char*	pstrDir = strrchr( GetFileName(), PATH_SEPARATOR_CHAR ); 
			*pstrDir = 0; 
 
			if (!ChUtil::FileExists( GetFileName() )) 
			{ 
				ChUtil::CreateDirectoryTree( GetFileName() ); 
			} 
												// Restore 
			*pstrDir = PATH_SEPARATOR_CHAR; 
		} 
		else 
		{ // verify the file size 
 
			#if defined( CH_MSW ) 
 
			struct _stat temp_stat; 
			_stat( GetFileName(), &temp_stat ); 
 
			#else 
 
			struct stat temp_stat; 
			stat( GetFileName(), &temp_stat ); 
 
			#endif 
 
			if ( luFileSize != (chuint32)temp_stat.st_size ) 
			{ 
				m_boolCache = false; 
				m_strLastModified.Empty(); 
			} 
		} 
	} 
} 
 
 
 
 
/*---------------------------------------------------------------------------- 
 
	FUNCTION	||	ChHTTPInfo::~ChHTTPInfo( ) 
 
------------------------------------------------------------------------------ 
 
	Destructor for ChHTTPInfo class. 
 
----------------------------------------------------------------------------*/ 
 
ChHTTPInfo::~ChHTTPInfo( ) 
{ 
 
	fstream*	pFile; 
 
	if (m_pstrBuffer) 
	{ 
		delete []m_pstrBuffer; 
	} 
 
 
	if (pFile = GetFile()) 
	{ 
		::delete pFile; // Cannot use MFC version of delete with MSCRT dll classes 
	} 
 
 
	if (m_pSocket && m_boolCreateByMe) 
	{ 
		if ( m_psocketDelList ) 
		{	// add it to the delete list, we will clean up this list  
			// when we get a chance to connect again. 
			// Some times we come here when we are inside a socket handler,  
			// deleteing inside the handler causes problems. 
			m_psocketDelList->AddTail( m_pSocket ); 
 
		} 
		else 
		{ 
			delete m_pSocket; 
		} 
	} 
} 
 
 
void ChHTTPInfo::TermHTTPInfo(  ) 
{ 
 
	if (  !IsThreaded() ) 
	{ 
		sockasyncinfo::CancelBlocking(); 
	} 
 
    // close all connections 
	CloseAllConnections( false, true ); 
 
 
	if (  IsThreaded() ) 
	{ 
		#if	defined(CH_MSW ) && defined( WIN32 ) 
		::ResetEvent( m_pThreadMgr->GetShutdownAckEvent() ); 
		m_pThreadMgr->TriggerShutdown(); 
		#endif // CH_MSW 
		// wait till the thread terminates 
		DWORD dwStatus = ::WaitForSingleObject( m_pThreadMgr->GetShutdownAckEvent(), INFINITE ); 
		ASSERT( dwStatus == WAIT_OBJECT_0 ); 
 
		TRACE( "Request thread termiantes\n" ); 
 
	} 
 
 
	#if	defined(CH_MSW ) && defined( WIN32 ) 
	if (  IsThreaded() ) 
	{									// Terminate the critical section 
 
		::DeleteCriticalSection( &m_httpSync ); 
 
		delete m_pThreadMgr; 
	} 
	#endif 
 
	delete m_pURLDB; 
	m_pURLDB = 0; 
 
	m_strCacheDir.Empty(); 
} 
 
 
void ChHTTPInfo::CloseAllConnections( bool boolOnlyGlobal, bool boolShutDown /* = false */ ) 
{ 
 
	// Serialize HTTP access 	 
	LockHTTP(); 
 
 
	if (ChHTTPInfo::GetConnectionList().GetCount()) 
	{ 
 
 
		ChPosition pos = ChHTTPInfo::GetConnectionList().GetHeadPosition(); 
 
												/* Delete records for all threads 
													with the END_PROCESS state */ 
		while (pos != 0) 
		{ 
			ChHTTPInfo*	pInfo = ChHTTPInfo::GetConnectionList().GetNext( pos ); 
 
			if ( !boolShutDown &&  boolOnlyGlobal ) 
			{ 
				if ( pInfo->GetHTTPConn()->GetConnOptions() & ChHTTPConn::connLocalState ) 
				{	// this is a local state connection do not mess with this 
					continue; 
				} 
 
			} 
 
			// we will put a;; connections to end process state 
			// and delete them all in DeleteConnection 
			pInfo->SetState( HTTP_END_PROCESS ); 
			pInfo->AbortRequest(); 
			pInfo->SetError( CH_HTTP_ERROR_USER_ABORT ); 
			pInfo->CloseStream(); 
 
		} 
 
	} 
 
	if (ChHTTPInfo::GetRequestQueue().GetCount()) 
	{ 
 
		ChPosition pos = ChHTTPInfo::GetRequestQueue().GetHeadPosition(); 
 
												/* Delete records for all threads 
													with the END_PROCESS state */ 
		while (pos != 0) 
		{ 
			ChPosition	prevPos = pos; 
			ChHTTPInfo*	pInfo = ChHTTPInfo::GetRequestQueue().GetNext( pos ); 
										// We have a pending connection 
			bool boolDelete = false; 
			if ( !boolShutDown &&  boolOnlyGlobal ) 
			{ 
				if ( !(pInfo->GetHTTPConn()->GetConnOptions() & ChHTTPConn::connLocalState) ) 
				{	// delete only non local 
					boolDelete = true; 
				} 
 
			} 
			else 
			{ 
				boolDelete = true; 
			} 
 
			if ( boolDelete ) 
			{ 
				ChHTTPInfo::GetRequestQueue().Remove( prevPos ); 
				{ 	//Set the abort state and stream 
					pInfo->SetState( HTTP_END_PROCESS ); 
					pInfo->AbortRequest(); 
					pInfo->SetError( CH_HTTP_ERROR_USER_ABORT ); 
					pInfo->CloseStream(); 
 
				} 
				delete pInfo; 
				pos = ChHTTPInfo::GetRequestQueue().GetHeadPosition(); 
			} 
 
		} 
	} 
 
	// Unlock serialization 
	UnlockHTTP(); 
 
	if ( !IsThreaded() ) 
	{  // for non-threaded we need to delete the connections 
		DeleteConnection(); 
	} 
 
} 
 
 
void ChHTTPInfo::CloseConnections( ChHTTPSocketConn* phttpConn, bool boolAbortPrefetch, 
						ChHTTPStreamManager* pStreamMgr /* = 0 */ ) 
{ 
 
	// Serialize HTTP access 	 
	LockHTTP(); 
 
 
	if (ChHTTPInfo::GetConnectionList().GetCount()) 
	{ 
 
 
		ChPosition pos = ChHTTPInfo::GetConnectionList().GetHeadPosition(); 
 
												/* Delete records for all threads 
													with the END_PROCESS state */ 
		while (pos != 0) 
		{ 
			ChHTTPInfo*	pInfo = ChHTTPInfo::GetConnectionList().GetNext( pos ); 
 
			if ( pInfo->GetHTTPConn() == phttpConn ) 
			{ 
				bool bbDelete = true; 
			   	if ( pInfo->GetURLOptions() & ChHTTPConn::PrefetchURL ) 
				{ 
					bbDelete = boolAbortPrefetch; 
				} 
 
				if ( bbDelete && pStreamMgr )  
				{	// if there is a stream manager then delete only ig it mathches 
					if(	pStreamMgr != pInfo->GetStreamMgr() ) 
					{ 
						bbDelete = false; 
					} 
				}  
 
				if ( bbDelete ) 
				{ 
					TRACE( "Setting state to abort\n" ); 
					{ 	//Set the abort state and stream 
						pInfo->SetState( HTTP_END_PROCESS ); 
						pInfo->AbortRequest(); 
						pInfo->SetError( CH_HTTP_ERROR_USER_ABORT ); 
						pInfo->CloseStream(); 
 
					} 
				} 
			} 
												// We have a connection 
 
		} 
 
	} 
 
	if (ChHTTPInfo::GetRequestQueue().GetCount()) 
	{ 
 
		ChPosition pos = ChHTTPInfo::GetRequestQueue().GetHeadPosition(); 
 
												/* Delete records for all threads 
													with the END_PROCESS state */ 
		while (pos != 0) 
		{ 
			ChPosition	prevPos = pos; 
			ChHTTPInfo*	pInfo = ChHTTPInfo::GetRequestQueue().GetNext( pos ); 
										// We have a pending connection 
 
			if ( pInfo->GetHTTPConn() == phttpConn ) 
			{ 
				bool bbDelete = true; 
			   	if ( pInfo->GetURLOptions() & ChHTTPConn::PrefetchURL ) 
				{ 
					bbDelete = boolAbortPrefetch; 
				} 
 
				if ( bbDelete && pStreamMgr )  
				{	// if there is a stream manager then delete only ig it mathches 
					if(	pStreamMgr != pInfo->GetStreamMgr() ) 
					{ 
						bbDelete = false; 
					} 
				}  
 
				if ( bbDelete ) 
				{ 
					ChHTTPInfo::GetRequestQueue().Remove( prevPos ); 
					{ 	//Set the abort state and stream 
						pInfo->SetState( HTTP_END_PROCESS ); 
						pInfo->AbortRequest(); 
						pInfo->SetError( CH_HTTP_ERROR_USER_ABORT ); 
						pInfo->CloseStream(); 
 
					} 
					delete pInfo; 
					pos = ChHTTPInfo::GetRequestQueue().GetHeadPosition(); 
				} 
			} 
 
		} 
	} 
 
	// Unlock serialization 
	UnlockHTTP(); 
 
	if ( !IsThreaded() ) 
	{  // for non-threaded we need to delete the connections 
		DeleteConnection(); 
	} 
 
} 
 
bool ChHTTPInfo::IsZipped()	   
{  
	return m_nZipType != ChUnzip::typeUnknown;  
} 
 
 
/*---------------------------------------------------------------------------- 
	ChHTTPInfo::Connect 
		Connect to the server. 
----------------------------------------------------------------------------*/ 
 
bool ChHTTPInfo::Connect() 
{ 
	sockinetaddr sa;	 
 
	if (ChHTTPInfo::FindSocketAddr( GetParts().GetHostName(), sa.m_sockAddrIn ) ) 
	{ 
		return Connect( sa ); 
	} 
	else 
	{ 
 
		if ( (sa.m_sockAddrIn.sin_addr.s_addr = inet_addr( GetParts().GetHostName() )) == -1) 
		{ 
												/* If that didn't work, then try 
													to look up the host by name */ 
 
			hostent * pHostEntry = gethostbyname( GetParts().GetHostName() ); 
 
			if (pHostEntry == 0) 
			{ 
				return false; 
			} 
 
	        #if defined( CH_ARCH_32 ) 
			memcpy( &sa.m_sockAddrIn.sin_addr, pHostEntry->h_addr, pHostEntry->h_length ); 
			#else 
			sa.m_sockAddrIn.sin_addr.s_addr = ((LPIN_ADDR)pHostEntry->h_addr)->s_addr; 
			#endif 
			sa.m_sockAddrIn.sin_family = pHostEntry->h_addrtype; 
		} 
		else 
		{ 
			sa.m_sockAddrIn.sin_family = sockinetbuf::af_inet; 
		} 
		ChHTTPInfo::AddToSocketAddrList( GetParts().GetHostName(),  
													sa.m_sockAddrIn ); 
		return Connect( sa ); 
	} 
 
} 
 
 
bool ChHTTPInfo::Connect( sockinetaddr& sa ) 
{ 
	bool boolSuccess; 
 
	#if defined( CH_DEBUG ) 
	{										/* Truncate the URL so that the 
												trace statement works */ 
 
		string		strAbbrevURL = m_urlParts.GetURL(); 
		int			iLen = strAbbrevURL.GetLength(); 
 
		strAbbrevURL = strAbbrevURL.Left( 80 ); 
		if (iLen > 80) 
		{ 
			strAbbrevURL += "[...]"; 
		} 
		TRACE1( "Connecting ... %s\n", (const char*)strAbbrevURL ); 
	} 
	#endif	// defined( CH_DEBUG ) 
 
 
	if ( !IsThreaded() ) 
	{ 
		m_pSocket = new sockinetbuf( sockbuf::sock_stream, httpSocketHandler ); 
		ASSERT( m_pSocket ); 
		m_pSocket->SetUserData( (chparam)this ); 
	} 
	else 
	{ 
		m_pSocket = new sockinetbuf( sockbuf::sock_stream ); 
   		ASSERT( m_pSocket ); 
	} 
 
	m_boolCreateByMe = true; 
 
	// Increment the total connect state connections 
	if ( !(GetURLOptions() &  ChHTTPConn::PrefetchURL ) ) 
	{ 
 
		LockHTTP(); 
		// Update the global state and local connection state 
		if ( GetHTTPConn()->GetConnOptions() & ChHTTPConn::connLocalState) 
		{ 
		 	GetHTTPConn()->m_connState.iTotalWaiting++; 
		} 
		else 
		{ 
		 	GetHTTPConn()->m_connState.iTotalWaiting++; 
		 	GetHTTPConn()->m_connGlobalState.iTotalWaiting++; 
		} 
		UnlockHTTP(); 
	} 
 
	{ 	// try to connect to the current host, if we fail 
	   // see if there is a mirror site. try again 
 
		#if defined( CH_MSW ) && defined( CH_ARCH_16 ) 
		TRY 
		#else 
		try 
		#endif 
		{ 
			// do the connect to remote server 
			// connect to the server 
			// Set the port number in to the address struct  
 
			sa.m_sockAddrIn.sin_port = htons( m_urlParts.GetPortNumber() ); 
 
			if ( m_urlParts.UsingProxy() ) 
			{  // connect to socks server 
				m_pSocket->connect( sa ); 
			} 
			else 
			{ 
				m_pSocket->SOCKSconnect( sa ); 
			} 
			boolSuccess = true; 
		} 
		#if defined( CH_MSW ) && defined( CH_ARCH_16 ) 
		CATCH( ChSocketEx, socketEx ) 
		#else 
		catch( ChSocketEx socketEx ) 
		#endif 
		{ 
			#if defined( CH_EXCEPTIONS ) 
			boolSuccess = false; 
 
			if ( !IsAborted() && socketEx.GetCause() == ChEx::connectFailed) 
			{ 
											// Connection failed! 
				// try the mirror site if any 
				if (m_urlParts.GetURL().Left(strlen(old_url)) == old_url)  
				{ 
					string strNewURL = ((string) new_url) + 
						m_urlParts.GetURL().Right(strlen(old_url) - 1);	 
 
					TRACE( "Connection failed, trying different host\n" ); 
 
					// Update any waits to the new URL 
					UpdateWaitRequest( this, strNewURL ); 
 
 
				  	if ( m_urlParts.GetURLParts( strNewURL ) ) 
					{	// Get the address  of the new domain 
						boolSuccess = 2; // Retry	 
					} 
				} 
 
			} 
			#endif 
		} 
		#if defined( CH_MSW ) && defined( CH_ARCH_16 ) 
		END_CATCH 
		#endif 
	} 
	// coming out of wait state 
	if ( !(GetURLOptions() &  ChHTTPConn::PrefetchURL ) ) 
	{ 
		LockHTTP(); 
		// Update the global state and local connection state 
		if ( GetHTTPConn()->GetConnOptions() & ChHTTPConn::connLocalState) 
		{ 
		 	GetHTTPConn()->m_connState.iTotalWaiting--; 
		} 
		else 
		{ 
		 	GetHTTPConn()->m_connState.iTotalWaiting--; 
		 	GetHTTPConn()->m_connGlobalState.iTotalWaiting--; 
		} 
		UnlockHTTP(); 
	} 
 
	if ( IsAborted() ) 
	{ 
		boolSuccess = false;	 
	} 
 
	return boolSuccess; 
} 
 
 
/*---------------------------------------------------------------------------- 
 
	FUNCTION	||	ChHTTPInfo::SendRequest( ) 
 
------------------------------------------------------------------------------ 
 
	Construct the HTTP request command and send it to the server. 
 
----------------------------------------------------------------------------*/ 
 
bool ChHTTPInfo::SendRequest( ) 
{ 
	if ( IsAborted() ) 
	{ 
		return false; 
	} 
 
	if (0 == m_pstrBuffer) 
	{ 
		m_luBufferSize   = minBufferSize; // should be enough for headers, realloc on content length 
		m_pstrBuffer = new char[m_luBufferSize + 1]; 
		ASSERT( m_pstrBuffer ); 
		m_luToProcess = 0; 
		m_luBodyLen   = 0; 
		m_luTotalLen  = 0; 
		SetState( HTTP_BEGIN_PROCESS );	// Current state of my HTTP state machine 
	} 
 
	// Build  the Requst command 
	BuildRequestLine( ); 
	BuildGeneralHeader( ); 
	BuildRequestHeader( ); 
	BuildObjectHeader( ); 
 
	TerminateCommand( ); 
 
	// send the command 
	if ( (GetURLOptions() & ChHTTPConn::UsePostMethod ) && m_strPostData.GetLength() ) 
	{ 
		m_strRequestBuffer += m_strPostData;	 
	} 
 
	m_strRequestBuffer += TEXT( "\r\n" ); 
	int iLen =  m_strRequestBuffer.GetLength(); 
	int iCount = 0; 
	const char* pstrBuffer =  m_strRequestBuffer; 
 
 
	while (iLen != 0) 
	{ 
		int wcnt = m_pSocket->write( pstrBuffer + iCount, iLen ); 
 
		if (wcnt == -1) 
		{ 
			TRACE( "Post : Write to socket failed\n" ); 
			return false; 
		} 
 
		iLen -= wcnt; 
		iCount += wcnt; 
	} 
 
	m_strRequestBuffer.Empty(); 
 
	return true; 
} 
 
 
/*---------------------------------------------------------------------------- 
 
	FUNCTION	||	ChHTTPInfo::MapURLToLocalName() 
 
------------------------------------------------------------------------------ 
 
	Map a given URL to a local file name relative to the URL cache directory. 
 
----------------------------------------------------------------------------*/ 
 
#if 0 
 
void ChHTTPInfo::MapURLToLocalName( const char* pstrRemotePath ) 
{ 
	m_strLocalName = m_strCacheDir; 
 
	if ( 0 == pstrRemotePath || 0 == *pstrRemotePath ) 
	{ 
		m_strLocalName += PATH_SEPARATOR_CHAR; 
		m_strLocalName += DEF_FILE_NAME; 
	} 
	else 
	{ 
 
		char *pstrStart = strrchr( pstrRemotePath, TEXT( '/' )); 
												/* Append the remote path. 
												Truncate any file name which 
												exceeds 8.3 limitations */ 
 
											// Loop through the source path 
		//while (*pstrStart) 
		if ( !pstrStart ) 
		{ 
			#if defined( CH_MSW ) 
			char   strPath[MAX_PATH]; 
				#if defined(CH_ARCH_16 ) 
					GetTempFileName( GetTempDrive( 0 ), "Ch", 0, strPath ); 
					char *pstrFileName = _fstrrchr( strPath, PATH_SEPARATOR_CHAR ); 
					ASSERT( pstrFileName ); 
					m_strLocalName = m_strCacheDir; 
					m_strLocalName += pstrFileName; 
 
				#else 
					GetTempFileName( m_strCacheDir, "Ch", 0, strPath ); 
					m_strLocalName = strPath; 
				#endif 
			#else 
			char* pstrTemp; 
 
			pstrTemp = mktemp( ChUtil::GetPuebloDir() + "/" + CACHE_DIR + 
								"/ChXXXXXX" ); 
			ASSERT( pstrTemp ); 
			m_strLocalName = pstrTemp; 
			#endif 
 
		} 
		else 
		{ 
			int		iBaseCount; 
			bool	boolDone; 
 
			m_strLocalName += PATH_SEPARATOR_CHAR; 
 
											/* Strip leading slashes and 
												backslashes */ 
 
			while ((*pstrStart == TEXT( '/' )) || (*pstrStart == TEXT( '\\' ))) 
			{ 
				pstrStart++; 
			} 
											/* Copy the directory or file 
												base name */ 
			iBaseCount = 0; 
			boolDone = false; 
			while (!boolDone) 
			{ 
				if ((*pstrStart == 0) || (*pstrStart == TEXT( '.' )) || 
					(*pstrStart == TEXT( '/' )) || (*pstrStart == TEXT( '\\' ))) 
				{ 
					boolDone = true; 
				} 
				else 
				{							/* Copy another char of the 
												base name */ 
					if ( isalnum( *pstrStart ) ) 
					{ 
						m_strLocalName += *pstrStart++; 
					} 
					else 
					{ 
						m_strLocalName += '_'; 
						pstrStart++; 
					} 
					iBaseCount++; 
 
					if (iBaseCount == NAME_BASE_LEN) 
					{ 
						boolDone = true; 
					} 
				} 
			} 
											// Skip until we hit a delimiter 
 
			while (*pstrStart && (*pstrStart != TEXT( '.' )) && 
					(*pstrStart != TEXT( '/' )) && (*pstrStart != TEXT( '\\' ))) 
			{ 
			 	++pstrStart; 
			} 
											// Copy the extension if any 
 
			if (*pstrStart && *pstrStart == TEXT( '.' )) 
			{ 
				int		iExtCount; 
				bool	boolDone; 
											// Copy the extension delimiter 
				m_strLocalName += *pstrStart++; 
 
				iExtCount = 0; 
				boolDone = false; 
				while (!boolDone) 
				{ 
					if ((*pstrStart == 0) || (*pstrStart == TEXT( '.' )) || 
						(*pstrStart == TEXT( '/' )) || (*pstrStart == TEXT( '\\' ))) 
					{ 
						boolDone = true; 
					} 
					else 
					{							/* Copy another char of the 
													extension */ 
 
						if ( isalnum( *pstrStart ) ) 
						{ 
							m_strLocalName += *pstrStart++; 
						} 
						else 
						{ 
							m_strLocalName += '_'; 
							pstrStart++; 
						} 
						iExtCount++; 
 
						if (iExtCount == NAME_EXT_LEN) 
						{ 
							boolDone = true; 
						} 
					} 
				} 
			} 
 
		} 
	} 
} 
 
#else 
 
void ChHTTPInfo::MapURLToLocalName( int iMimeType ) 
{ 
	m_strLocalName = m_strCacheDir;	 
	 
	string strTmp; 
	ChHTTPConn::GetFileExtnByMimeType( iMimeType, strTmp ); 
 
	if ( strTmp.CompareNoCase(  TEXT( "tmp" )) == 0 ) 
	{ // map it based on the url extension 
		int iIndex = m_urlParts.GetURL().ReverseFind( TEXT( '.' ) ); 
		if ( iIndex != -1 ) 
		{ 
			strTmp = m_urlParts.GetURL().Right(  
						m_urlParts.GetURL().GetLength() - iIndex - 1 ); 
			if ( strTmp.GetLength() > 3 ) 
			{ 
			  	strTmp = strTmp.Left( 3 ); 
			}								 
		} 
	} 
 
	string strExtn( TEXT( "." ) ); 
	strExtn += 	strTmp; 
		 
 
	// check if someone has deleted our cache directory, create it 
	if (!ChUtil::FileExists( m_strLocalName )) 
	{ 
		ChUtil::CreateDirectoryTree( m_strLocalName ); 
	} 
 
	// Serialize HTTP access 	 
	LockHTTP(); 
 
 
	#if defined( CH_MSW ) 
	char   strPath[MAX_PATH]; 
		#if defined(CH_ARCH_16 ) 
			GetTempFileName( GetTempDrive( 0 ), "Ch", 0, strPath ); 
 
			::DeleteFile( strPath ); 
 
			char *pstrFileName = _fstrrchr( strPath, PATH_SEPARATOR_CHAR ); 
			ASSERT( pstrFileName ); 
			m_strLocalName = m_strCacheDir; 
			char *pstrTmp = _fstrrchr( pstrFileName, TEXT( '.' ) ); 
			if ( pstrTmp ) 
			{ 
				*pstrTmp = 0; 
			} 
			m_strLocalName += pstrFileName; 
 
		#else 
			GetTempFileName( m_strCacheDir, "Ch", 0, strPath ); 
			::DeleteFile( strPath ); 
	 
			char *pstrTmp = strrchr( strPath, TEXT( '.' ) ); 
			if ( pstrTmp ) 
			{ 
				*pstrTmp = 0; 
			} 
			m_strLocalName = strPath; 
			m_strLocalName += strExtn; 
 
			if ( ChUtil::FileExists( m_strLocalName ) ) 
			{ 
				m_strLocalName = strPath; 
				m_strLocalName += TEXT( ".tmp" ); 
			} 
 
		#endif 
	#else 
	char* pstrTemp; 
	char* pstrTempIn; 
 
	pstrTempIn = new char[MAX_PATH]; 
	sprintf( pstrTempIn, "%s/%s/ChXXXXXX",  
				(const char *)ChUtil::GetPuebloDir(), CACHE_DIR); 
	pstrTemp = mktemp( pstrTempIn ); 
	ASSERT( pstrTemp ); 
	m_strLocalName = pstrTemp; 
	::DeleteFile( m_strLocalName ); 
 
	delete []pstrTempIn;   
 
	m_strLocalName += strExtn; 
	#endif 
 
	// Unlock serialization 
	UnlockHTTP(); 
 
} 
#endif 
 
 
 
 
 
/*---------------------------------------------------------------------------- 
 
	FUNCTION	||	ChHTTPInfo::BuildRequestLine( ) 
 
------------------------------------------------------------------------------ 
 
Construct HTTP request line. Version 1.0 > 
----------------------------------------------------------------------------*/ 
 
inline void ChHTTPInfo::BuildRequestLine(  ) 
{ 
 
	if ( IsAborted() ) 
	{ 
		return; 
	} 
 
	string	strPath =  m_urlParts.GetAbsPath() ?  m_urlParts.GetAbsPath() : TEXT( "" ); 
 
	if ( m_urlParts.UsingProxy() ) 
	{ 
		strPath	= m_urlParts.GetAbsPath(); 
	} 
	else 
	{ 
	 	strPath = TEXT( "/"  ); 
		strPath +=  (m_urlParts.GetAbsPath() ?  m_urlParts.GetAbsPath() : TEXT( "" )); 
	} 
 
	if ( m_urlParts.GetRelPath() ) 
	{ 
		if ( strPath.GetLength() && 
				TEXT( '/' ) != strPath[strPath.GetLength()-1] ) 
		{ 
			strPath += TEXT( '/' ); 
		} 
 
		strPath += m_urlParts.GetRelPath(); 
	} 
 
 
	if ( GetURLOptions() & ChHTTPConn::UsePostMethod && m_strPostData.GetLength() ) 
	{ 
		m_strRequestBuffer = "POST " ; 
	} 
	else 
	{ 
		m_strRequestBuffer = "GET "; 
	} 
 	char	strVersion[25]; 
	wsprintf( strVersion, " %s%c%c",  HTTP_VERSION, CR, LF ); 
	 
	ChURLParts::EscapeSpecialChars( strPath ); 
 
	m_strRequestBuffer += strPath; 
	m_strRequestBuffer += strVersion; 
 
} 
 
 
void ChHTTPInfo::BuildRequestHeader() 
{ 
	string		strUserAgent; 
 
	if ( IsAborted() ) 
	{ 
		return; 
	} 
 
	m_strRequestBuffer += TEXT( "User-Agent: " ); 
	GetHTTPConn()->GetUserAgent( strUserAgent ); 
	m_strRequestBuffer += strUserAgent; 
 
	TerminateCommand(); 
 
	if (IsCached() && !m_strLastModified.IsEmpty()) 
	{ 
											/* if we have a date, check if we 
												need to update our local 
												cache */ 
 
		m_strRequestBuffer += TEXT( "If-Modified-Since:" ); 
		m_strRequestBuffer += m_strLastModified; 
		TerminateCommand(); 
	} 
 
	string		strCookie; 
	bool		boolSecure = false; 
 
	if (ChHTTPSocketConn::GetHTTPCookie()) 
	{										/* Cookie access needs to be 
												serialized */ 
		LockHTTP(); 
		if (ChHTTPSocketConn::GetHTTPCookie()-> 
					GetCookie( m_urlParts.GetURL(), strCookie, boolSecure )) 
		{ 
			m_strRequestBuffer += TEXT( "Cookie: " ); 
			m_strRequestBuffer += strCookie; 
			TerminateCommand(); 
		} 
 
		UnlockHTTP(); 
	} 
 
	m_strRequestBuffer += TEXT( "Accept:*/*" ); 
	TerminateCommand(); 
	m_strRequestBuffer += TEXT( "Accept: text/html" ); 
	TerminateCommand(); 
	m_strRequestBuffer += TEXT( "Accept: text/plain" ); 
	TerminateCommand(); 
	m_strRequestBuffer += TEXT( "Accept: image/*" ); 
	TerminateCommand(); 
	m_strRequestBuffer += TEXT( "Accept: www/source" ); 
	TerminateCommand(); 
	 
	if (!(GetURLOptions() & ChHTTPConn::UsePostMethod)) 
	{ 
		#if defined( CH_MSW ) 
		{ 
			m_strRequestBuffer += 
					TEXT( "Accept-Encoding:x-gzip, gzip, x-zip" ); 
			TerminateCommand(); 
		} 
		#endif 
	} 
} 
 
 
void ChHTTPInfo::BuildObjectHeader() 
{ 
	if ( IsAborted() ) 
	{ 
		return; 
	} 
 
	if ((GetURLOptions() & ChHTTPConn::UsePostMethod) && 
			m_strPostData.GetLength()) 
	{ 
											/* If we have a date, check if we 
												need to update our local 
												cache */ 
 
		m_strRequestBuffer	+= TEXT( "Content-type: " ); 
		m_strRequestBuffer += TEXT( "application/x-www-form-urlencoded" ); 
		TerminateCommand( ); 
		m_strRequestBuffer += TEXT( "Content-Length: " ); 
		char strTmp[25]; 
		#if defined( CH_MSW ) 
		_ltoa( m_strPostData.GetLength(), strTmp, 10 ); 
		#else 
		ltoa( m_strPostData.GetLength(), strTmp, 10 ); 
		#endif 
		m_strRequestBuffer += strTmp; 
		TerminateCommand(); 
	} 
} 
 
 
/*---------------------------------------------------------------------------- 
 
	FUNCTION	||	ChHTTPInfo::ProcessInput 
 
------------------------------------------------------------------------------ 
 
	Reads the contents of the socket. 
 
----------------------------------------------------------------------------*/ 
 
void ChHTTPInfo::ProcessInput() 
{ 
 
	if ( !IsThreaded() ) 
	{ 
		chuint32	luLen; 
 
		luLen = m_pSocket->GetBytesAvailable(); 
 
											// process in chunks if required 
		while ( luLen ) 
		{ 
			chuint32 luToRead = (m_luToProcess + luLen > m_luBufferSize) ? 
									m_luBufferSize - m_luToProcess : luLen; 
 
											/* Read the contents of the socket 
												to the end of 'inputbuf' */ 
 
			m_pSocket->read( m_pstrBuffer + m_luToProcess, (int)luToRead ); 
			m_luToProcess += luToRead;		// Total data still to be processed 
			luLen -= luToRead; 				// number of bytes to read 
 
											/* Process the contents of the 
												internal buffer  and update 
												the m_ToProcess*/ 
			ProcessBuffer(); 
		} 
	} 
	else 
	{ 
		chuint32	luMaxRead = m_luBufferSize - m_luToProcess; 
		chint32		lRead; 
 
		while ((lRead = m_pSocket->read( m_pstrBuffer + m_luToProcess, (int)luMaxRead )) > 0) 
		{ 
			m_luToProcess += lRead; 
			ProcessBuffer(); 
			luMaxRead = m_luBufferSize	- m_luToProcess; 
		} 
	} 
} 
 
 
/*---------------------------------------------------------------------------- 
 
	FUNCTION	||	ChHTTPInfo::ProcessInput 
 
------------------------------------------------------------------------------ 
 
	Interpret the data read from the socket. 
 
----------------------------------------------------------------------------*/ 
void ChHTTPInfo::ProcessBuffer() 
{ 
 
	m_pstrBuffer[m_luToProcess] = 0; 
	{ 
		switch ( GetState() ) 
		{ 
			case HTTP_IN_BODY : 
			{ 
				//TRACE( "In Body:" ); 
				//TRACE( 	GetFileName() ); 
				//TRACE( "\n" ); 
				ProcessBody(); 
				break; 
			} 
			case HTTP_BEGIN_PROCESS : 
			{ 
				//TRACE( "In Status line:" ); 
				//TRACE( 	GetFileName() ); 
				//TRACE( "\n" ); 
				ProcessStatusLine(); 
				break; 
			} 
			case HTTP_IN_HEADER : 
			{ 
				//TRACE( "In Header:" ); 
				//TRACE( 	GetFileName() ); 
				//TRACE( "\n" ); 
				ProcessHeaders(); 
				break; 
			} 
			case HTTP_USE_LOCAL : 
			{ 
				break; 
			} 
			default : 
			{ // error occured or 
				m_luToProcess  = 0; 
			} 
		} 
 
	} 
} 
 
void ChHTTPInfo::ProcessBody() 
{ 
	// if we are reading the body of the message, there is 
	// nothing to process, just write to the file, or notify user of 
	// the new data that is available 
 
 
	if ( IsAborted() ) 
	{ 
		m_luToProcess = 0; 
		return; 
	} 
 
	m_luTotalLen +=  m_luToProcess; 
 
 
	// Notify progress 
	NotifyProgress( ); 
 
 
	// we keep the file open for all systems except win16 and win32s 
	// due to open file handle limitations 
											// Write to file 
	if (  !IsThreaded() ) 
	{ 
		const char* pstrFile; 
 
		if ( m_nZipType != ChUnzip::typeUnknown ) 
		{ 
			pstrFile = GetZipFileName(); 
		} 
		else 
		{ 
			pstrFile = GetFileName(); 
		} 
 
		GetFile()->open( pstrFile, ios::app | IOS_BINARY ); 
	} 
 
	GetFile()->write( m_pstrBuffer, (int)m_luToProcess ); 
 
	if ( m_httpStream.m_uStreamOption & ChHTTPStreamManager::streamNormal  
			&& GetStreamMgr() ) 
	{ 
		m_iStreamState = streamInData; 
 
		chint32 lBytes = GetStreamMgr()->WriteReady( GetUserData(),  
								&m_httpStream, m_luToProcess ); 
		if ( lBytes > 0 ) 
		{   
			GetStreamMgr()->Write( GetUserData(), &m_httpStream,  
										0, m_luToProcess, m_pstrBuffer ); 
		} 
	} 
 
	if (  !IsThreaded() ) 
	{ 
		GetFile()->close(); 
	} 
 
	m_luToProcess = 0; 
	return; 
 
} 
 
 
/*---------------------------------------------------------------------------- 
 
	FUNCTION	||	ChHTTPInfo::OnDataComplete 
 
------------------------------------------------------------------------------ 
 
----------------------------------------------------------------------------*/ 
 
 
void ChHTTPInfo::OnDataComplete() 
{ 
	// Notify we are done  
	//AsyncNotifyProgress( ChHTTPInfo::msgEndProgress ); 
 
		// close the local file  
	if ( IsThreaded() ) 
	{ 
		GetFile()->close(); 
	} 
 
 
	#if defined( CH_MSW )	  
	if ( m_nZipType != ChUnzip::typeUnknown ) 
	{ 
		ChUnzip unzip; 
 
		if ( unzip.UnzipFileTo( m_strZipName,  
						m_nZipType == ChUnzip::typeGZIP ? UNZIP_OPT_GZIP_FILE : UNZIP_DEF_OPTION, 
						0, GetFileName() ) ) 
		{ // get the size of uncomppressed file 
			TRACE2( "Unzip successful %s -> \n", LPCSTR(m_strZipName), LPCSTR(GetFileName()) ); 
 
			#if defined( CH_MSW ) 
 
			struct _stat temp_stat; 
			_stat( GetFileName(), &temp_stat ); 
 
			#else 
 
			struct stat temp_stat; 
			stat( GetFileName(), &temp_stat ); 
 
			#endif 
			m_luTotalLen = temp_stat.st_size; 
 
		  	//  delete the zip file 
		  	::DeleteFile( m_strZipName ); 
		} 
		else 
		{ 
			TRACE1( "Unzip failed %s\n", LPCSTR(m_strZipName) ); 
			m_luToProcess = 0; 
		  	::DeleteFile( m_strZipName ); 
			SetState( HTTP_ERROR ); 
			SetError( CH_HTTP_ERROR_INVALID_DATA ); 
			return; 
		} 
 
	} 
	#endif 
 
 
	if ( !(GetURLOptions() & ChHTTPConn::DoNotCache) ) 
	{ 
		char *		pstrFile = strrchr( GetFileName(), PATH_SEPARATOR_CHAR ); 
		ASSERT( pstrFile ); 
		// point to the file name 
		pstrFile++; 
 
		const char * pstrMimeType = m_strMimeType; 
 
		int 	iPathLen = lstrlen( pstrFile ) + 1; 
		int 	iDateLen = m_strLastModified.GetLength() + 1; 
		int		iMimeTypeLen = lstrlen( pstrMimeType ) + 1; 
		char	strSize[25], strOptions[25]; 
 
		#if defined( CH_MSW ) 
		_ltoa( m_luTotalLen, strSize, 10 ); 
		#else 
		ltoa( m_luTotalLen, strSize, 10 ); 
		#endif 
 
		int  iSizeLen = lstrlen( strSize ) + 1; 
 
		#if defined( CH_MSW ) 
		_ltoa( GetURLOptions() , strOptions, 10 ); 
		#else 
		ltoa( GetURLOptions() , strOptions, 10 ); 
		#endif 
 
		int  iOptionLen = lstrlen( strOptions ) + 1; 
 
		int iTotalLen =  iPathLen + iDateLen + iSizeLen + MAX_OPTION_BLK + iMimeTypeLen + 1; 
		ptr pDBData = new char[ iTotalLen ]; 
 
		ChMemClear( pDBData, iTotalLen ); 
 
		ChMemCopy( pDBData, pstrFile, iPathLen ); 
		ChMemCopy( (char*)pDBData + iPathLen, LPCSTR(m_strLastModified), iDateLen ); 
		ChMemCopy( (char*)pDBData + (iPathLen + iDateLen), strSize, iSizeLen ); 
		ChMemCopy( (char*)pDBData + (iPathLen + iDateLen + iSizeLen ), 
						strOptions, iOptionLen ); 
		ChMemCopy( (char*)pDBData + 
						(iPathLen + iDateLen + iSizeLen + MAX_OPTION_BLK ), 
							pstrMimeType, iMimeTypeLen ); 
 
		ChDBData dbData( pDBData, iTotalLen ); 
 
		string strKey = m_urlParts.GetURL(); 
		strKey.MakeLower();	   
 
		ChDBKey  dbKey( strKey ); 
 
		// Serialize HTTP access 	 
		LockHTTP(); 
 
		ChDBData dbCacheData = m_pURLDB->GetData( dbKey ); 
							// Set the data 
		m_pURLDB->SetData( dbKey, dbData, 
						dbCacheData.GetDataSize() ? CHDB_REPLACE : CHDB_INSERT ); 
 
		if ( 1 ||  !m_strLastModified.IsEmpty() ) 
		{ // store it only if it is persistant on the server 
			// add it to the current session 
	       int iTemp = 1; // we need to do this for Win16, the compiler cannot 
	        // convert const int to const int& 
			ChHTTPInfo::m_httpVisited.Insert( m_urlParts.GetURL(), iTemp ); 
		} 
 
		// Unlock serialization 
		UnlockHTTP(); 
 
		delete []pDBData; 
	} 
 
 
	// We are done with processing data 
	SetState( HTTP_DATA_COMPLETE ); 
 
	// Do the load complete notification 
	SetError( 0 ); 
} 
 
 
/*---------------------------------------------------------------------------- 
 
	FUNCTION	||	ChHTTPInfo::ProcessStatusLine 
 
------------------------------------------------------------------------------ 
Process the HTTP status line. 
----------------------------------------------------------------------------*/ 
 
 
void ChHTTPInfo::ProcessStatusLine() 
{											// Our HTTP response state machine 
 
											// Terminate the buffer 
	m_pstrBuffer[m_luToProcess] = TEXT( '\0' ); 
 
	char* pstrNext = strchr( m_pstrBuffer, LF ); 
 
	if (pstrNext) 
	{										// Looks like we have a status line 
											// Do we have the version 
		chuint32  i = 0; 
 
		while( i < m_luToProcess && m_pstrBuffer[i] != TEXT( '/' ))   
								 //&&	m_pstrBuffer[i] != TEXT( '<' )) 
		{ 
			i++; 
		} 
 
 
		pstrNext = &m_pstrBuffer[i + 1]; 
		*(pstrNext - 1) = TEXT ('\0' ); 
 
		if ( lstrcmpi( pstrHTTPTokens[HTTP], m_pstrBuffer )) 
		{ // invalid status line 
			ProcessNoHeaderReply(); 
			return; 
		} 
		else 
		{ // get the status code 
			while( pstrNext < &m_pstrBuffer[m_luToProcess]   && *pstrNext++ != TEXT( ' ' ) ); 
 
			int iStatus = (int)atol( pstrNext ); 
			switch ( iStatus ) 
			{ 
				case 200 : 
				{  // successful 
					pstrNext = strchr( pstrNext, LF ) + 1; 
					// now lets get to process header mode 
					SetState( HTTP_IN_HEADER ); 
 
					m_luToProcess -= (chuint32)( pstrNext - m_pstrBuffer ); 
					// copy the rest to be processed 
					ChMemCopy( m_pstrBuffer, pstrNext, m_luToProcess ); 
					if ( m_luToProcess ) 
					{ 
						ProcessHeaders(); 
					} 
					return; 
				} 
				case 304 : 
				{ 
					SetState( HTTP_USE_LOCAL ); 
					m_luToProcess = 0; 
					TRACE( "Use local :" ); 
					TRACE( 	GetFileName() ); 
					TRACE( "\n" ); 
			 		// notify the module of the new data 
 
					// Serialize HTTP access 	 
					LockHTTP(); 
 
                    int iTemp = 1; // we need to do this for Win16, the compiler cannot 
                    // convert const int to const int& 
					ChHTTPInfo::m_httpVisited.Insert( m_urlParts.GetURL(), iTemp ); 
 
					// Unlock serialization 
					UnlockHTTP(); 
 
					if ( IsAborted() ) 
					{ 
						m_luToProcess = 0; 
						return; 
					} 
 
					// Set the time of the file to current time for LRU implementation 
					ChUtil::SetFileToCurrentTime( GetFileName() ); 
 
					// What we have is no different from the one on the server 
					UpdateStatusInfo(); 
					StreamURL(); 
					UpdateStatusInfo( false ); 
					return; 
				} 
				default : 
				{ 
					pstrNext = strchr( pstrNext, LF ) + 1; 
					if ( pstrNext ) 
					{ 
						m_luToProcess -= (chuint32)( pstrNext - m_pstrBuffer ); 
						// copy the rest to be processed 
						ChMemCopy( m_pstrBuffer, pstrNext, m_luToProcess ); 
					} 
					else 
					{ 
					 	m_pstrBuffer[0] = 0; 
						m_luToProcess = 0; 
					} 
					HandleError( iStatus ); 
					return; 
				} 
 
			} 
 
		} 
	} 
	else 
	{ 
		ProcessNoHeaderReply(); 
		return; 
	} 
} 
 
 
#define STANDARD		1  			//  with \r\n delimiter 
#define NON_STANDARD	2			// with  \n delimiter 
 
/*---------------------------------------------------------------------------- 
 
	FUNCTION	||	ChHTTPInfo::ProcessHeaders 
 
------------------------------------------------------------------------------ 
Process HTTP headers 
----------------------------------------------------------------------------*/ 
void ChHTTPInfo::ProcessHeaders() 
{ 
 
	int		iDelimType = NON_STANDARD; 
											// Do we have the complete header 
											// this is the right delimitor 
 
	char*	pstrBody = strstr( m_pstrBuffer, "\r\n\r\n" ); 
 
	if (pstrBody) 
	{ 
		iDelimType = STANDARD; 
	} 
	else 
	{ 
		pstrBody = strstr( m_pstrBuffer, "\n\n" ); 
		if (pstrBody) 
		{ 
			iDelimType = NON_STANDARD; 
		} 
	} 
 
	if (!pstrBody) 
	{										/* We have a partial body, go back 
												and read the rest */ 
		return; 
	} 
 
	m_strMimeType.Empty(); 
											// process the header; 
 
	char*	pstrType = m_pstrBuffer, *pstrNext, *pstrData; 
 
	while (GetNextHdr( pstrType, pstrData, pstrNext, iDelimType )) 
	{ 
		int		iType = GetHdrType( pstrType ); 
 
		switch( iType ) 
		{ 
			case CONTENT_TYPE : 
			{ 
				m_strMimeType = pstrData; 
				m_strMimeType.TrimLeft(); 
				m_strMimeType.TrimRight(); 
				break; 
			} 
 
			case LAST_MODIFIED : 
			{ 
				m_strLastModified = pstrData; 
				break; 
			} 
 
			case CONTENT_LENGTH : 
			{ 
				m_luBodyLen = atol( pstrData ); 
				break; 
			} 
			case  CONTENT_ENCODING : 
			{ 
				string strData = pstrData; 
				strData.MakeLower(); 
 
				if (  strData.Find( "x-gzip" ) != -1 ) 
				{ 
					m_nZipType = ChUnzip::typeGZIP; 
				} 
				else if (  strData.Find( "gzip" ) != -1 ) 
				{ 
					m_nZipType = ChUnzip::typeGZIP; 
				} 
				else if	( strData.Find( "x-zip" ) != -1 )  
				{ 
					m_nZipType = ChUnzip::typePKZIP; 
				} 
				break; 
			} 
			case SET_COOKIE : 
			{ 
				if ( ChHTTPSocketConn::GetHTTPCookie() ) 
				{	// Serialize access to cookie list 
					LockHTTP(); 
 
					ChHTTPSocketConn::GetHTTPCookie()-> 
							SetCookie( m_urlParts.GetURL(), pstrData); 
					// if this was a persistant cookie then save it 
					if ( !ChHTTPSocketConn::GetHTTPCookie()->IsSaved() ) 
					{ 
						ChHTTPSocketConn::GetHTTPCookie()->WriteCookieFile(); 
					} 
					 
					UnlockHTTP(); 
 
				} 
				break; 
			} 
			default: 
			{								// ignore for now 
				break; 
			} 
		} 
		pstrType = pstrNext; 
	} 
 
	if ( m_strMimeType.IsEmpty() ) 
	{ 
		pstr pstrData = pstrBody; 
		int i = 0; 
 
		if (iDelimType == STANDARD) 
		{ 
			pstrData += 4;						/* Points to the begining of 
													the data */ 
		} 
		else 
		{ 
			pstrData += 2;						/* Points to the begining of 
													the data */ 
 
		} 
 
		if ( pstrData[i] == TEXT( '<' ) ) 
		{  
			m_strMimeType = "text/html"; 
		} 
		else if ( pstrData[i] ==   TEXT( 'G' )  &&  
				  pstrData[i+1] == TEXT( 'I' ) && 
				  pstrData[i+2] == TEXT( 'F' ) ) 
		{ 
			m_strMimeType = "image/gif"; 
		} 
		else if ( pstrData[i + 6] == TEXT( 'J' )  &&  
				  pstrData[i + 7] == TEXT( 'F' ) && 
				  pstrData[i + 8] == TEXT( 'I' ) && 
				  pstrData[i + 9] == TEXT( 'F' ) ) 
		{ 
			m_strMimeType = "image/jpeg"; 
		} 
		else if ( pstrData[i] == TEXT( '#' )  &&  
				  pstrData[i+1] == TEXT( 'V' ) && 
				  pstrData[i+2] == TEXT( 'R' ) && 
				  pstrData[i+3] == TEXT( 'M' ) && 
				  pstrData[i+4] == TEXT( 'L' ) ) 
		{ 
			m_strMimeType = "x-world/x-vrml"; 
		} 
		else 
		{  // what is it ??? 
			m_strMimeType = "text/html"; 
		} 
	} 
 
	if ( !m_strMimeType.IsEmpty() ) 
	{ 
		fstream*	pFile; 
 
		SetState( HTTP_IN_BODY ); 
 
		if (0 == m_luBodyLen) 
		{									// max we can get 
			m_luBodyLen = 0xFFFFFFFF; 
			m_bbCompleted = true;	// We don't know how much data we have, 
									//  what ever we get till the socket is closed 
									//  is our data 
		} 
 
		if ( m_strLocalName.IsEmpty() ) 
		{ // get the local file name 
			MapURLToLocalName( ChHTTPSocketConn::GetMimeType( m_strMimeType ) ); 
		} 
 
		const char* pstrOutFile; 
		if ( m_nZipType != ChUnzip::typeUnknown ) 
		{ 
			// download the zipped file under a temp name, after unzipping the file 
			// to its original name, delete the tempp file 
			#if defined( CH_MSW ) 
			char   strPath[MAX_PATH]; 
				#if defined(CH_ARCH_16 ) 
					GetTempFileName( GetTempDrive( 0 ), "Ch", 0, strPath ); 
					char *pstrFileName = _fstrrchr( strPath, PATH_SEPARATOR_CHAR ); 
					ASSERT( pstrFileName ); 
					m_strZipName = m_strCacheDir; 
					m_strZipName += pstrFileName; 
 
				#else 
					GetTempFileName( m_strCacheDir, "Ch", 0, strPath ); 
					m_strZipName = strPath; 
				#endif 
			#else 
			char* pstrTemp; 
 
			pstrTemp = mktemp( ChUtil::GetPuebloDir() + "/" + CACHE_DIR + 
								"/ChXXXXXX" ); 
			ASSERT( pstrTemp ); 
			m_strZipName = pstrTemp; 
			#endif 
 
			// if the OS creates a temp file delete it 
			if ( ChUtil::FileExists( m_strZipName ) ) 
			{ 
				::DeleteFile( m_strZipName ); 
			} 
 
			pstrOutFile = m_strZipName; 
 
		} 
		else 
		{ 
			pstrOutFile = GetFileName(); 
		} 
											// create the file locally 
 
		#ifdef CH_MSW 
				pFile = ::new fstream( pstrOutFile, ios::out | IOS_BINARY, filebuf::sh_none ); 
		#else 
				pFile = ::new fstream( pstrOutFile, ios::out | IOS_BINARY ); 
		#endif 
		SetFile( pFile ); 
 
		ASSERT( GetFile() ); 
 
		if ( !GetFile()->is_open() ) 
		{ 
			SetState( HTTP_ERROR ); 
			SetError( CH_HTTP_ERROR_LOCAL_IN_USE ); 
			CloseStream(); 
 
 
		} 
		else if ( !IsThreaded() ) 
		{ 
											/* The file is opened to write 
												each block */ 
			GetFile()->close(); 
		} 
 
	} 
	else 
	{ 
		SetState( HTTP_ERROR ); 
		SetError( CH_HTTP_ERROR_INVALID_HDR ); 
		CloseStream(); 
		m_luToProcess = 0; 
 
		return; 
	} 
 
	if (iDelimType == STANDARD) 
	{ 
		pstrBody += 4;						/* Points to the begining of 
												the data */ 
	} 
	else 
	{ 
		pstrBody += 2;						/* Points to the begining of 
												the data */ 
 
	} 
 
	m_luToProcess -= (pstrBody - m_pstrBuffer); 
 
	if ( m_luBodyLen > maxBufferSize ) 
	{ 
		m_luBufferSize =  maxBufferSize; 
		char * pstrTemp = new char[ m_luBufferSize + 1]; 
		ASSERT( pstrTemp ); 
		ChMemCopy( pstrTemp, pstrBody, m_luToProcess ); 
		delete [] m_pstrBuffer; 
		m_pstrBuffer = pstrTemp; 
	} 
	else if ( m_luBodyLen > midBufferSize ) 
	{ 
		m_luBufferSize =  midBufferSize; 
		char * pstrTemp = new char[ m_luBufferSize + 1]; 
		ASSERT( pstrTemp ); 
		ChMemCopy( pstrTemp, pstrBody, m_luToProcess ); 
		delete [] m_pstrBuffer; 
		m_pstrBuffer = pstrTemp; 
	} 
	else 
	{ 
		ChMemCopy( m_pstrBuffer, pstrBody, m_luToProcess ); 
	} 
 
	// Update the active conn status 
	UpdateStatusInfo(); 
 
	// Initalize the stream 
	InitStream(); 
 
	if ( m_luToProcess ) 
	{ 
		ProcessBody(); 
	} 
} 
 
/*---------------------------------------------------------------------------- 
 
	FUNCTION	||	ChHTTPInfo::HandleError 
 
------------------------------------------------------------------------------ 
Process HTTP headers on error 
----------------------------------------------------------------------------*/ 
void ChHTTPInfo::HandleError( int iError ) 
{ 
 
	int		iDelimType = NON_STANDARD; 
											// Do we have the complete header 
											// this is the right delimitor 
 
	char*	pstrBody = strstr( m_pstrBuffer, "\r\n\r\n" ); 
 
	if (pstrBody) 
	{ 
		iDelimType = STANDARD; 
	} 
	else 
	{ 
		pstrBody = strstr( m_pstrBuffer, "\n\n" ); 
		if (pstrBody) 
		{ 
			iDelimType = NON_STANDARD; 
		} 
	} 
 
 
	string strNewLocation; 
 
										// process the header; 
	if ( pstrBody ) 
	{										/* We have a partial body, go back 
												and read the rest */ 
		char*	pstrType = m_pstrBuffer, *pstrNext, *pstrData; 
 
		while (GetNextHdr( pstrType, pstrData, pstrNext, iDelimType )) 
		{ 
			int		iType = GetHdrType( pstrType ); 
 
			switch( iType ) 
			{ 
				case LOCATION : 
				{ 
					strNewLocation =  pstrData; 
					strNewLocation.TrimLeft(); 
					strNewLocation.TrimRight(); 
					break; 
				} 
				default: 
				{								// ignore for now 
					break; 
				} 
			} 
			pstrType = pstrNext; 
		} 
 
 
		if (iDelimType == STANDARD) 
		{ 
			pstrBody += 4;						/* Points to the begining of 
													the data */ 
		} 
		else 
		{ 
			pstrBody += 2;						/* Points to the begining of 
													the data */ 
		} 
 
		m_luToProcess -= (pstrBody - m_pstrBuffer); 
 
		ChMemCopy( m_pstrBuffer, pstrBody, m_luToProcess ); 
		m_pstrBuffer[ m_luToProcess ] = 0; 
	} 
	else 
	{ 
		m_pstrBuffer[0] = 0;	 
	} 
 
	switch ( iError ) 
	{ 
		case 302 : 
		case 304 : 
		{ 
			if ( !strNewLocation.IsEmpty() ) 
			{ 
				break; 
			} 
		} 
		case 404 :	// not found 
		case 408 :	// request timeout 
		case 500 :  // internal server error 
		case 502 :  // bad gateway 
		case 504 :  // gateway timeout 
		{ 
 
			if ( !(m_flOptions & ChHTTPConn::PrefetchURL ) && 
					 m_urlParts.GetURL().Left(strlen(old_url)) == old_url)  
			{ 
				string	strTempURL( m_urlParts.GetURL() ); 
 
				strNewLocation = ((string)new_url) + 
							strTempURL.Right( strlen( old_url ) - 1 ); 
				#if !defined( CH_MSW ) 
				cerr << "Replacing " << strTempURL << " with " 
					<< strNewLocation << endl; 
				#endif 
			} 
		} 
		default : 
		{ 
			break; 
		} 
	} 
 
	SetState( HTTP_ERROR ); 
	SetError( iError ); 
 
	if ( !strNewLocation.IsEmpty() ) 
	{ 
 
		SetState( HTTP_RETRY ); 
 
		// if there is any requests waiting for this URL 
		// update them to the new URL 
		UpdateWaitRequest( this, strNewLocation ); 
 
 
		ChHTTPInfo* pHTTPInfo; 
		pHTTPInfo = new ChHTTPInfo( GetHTTPConn(),  
									strNewLocation, 
									m_userData, 0, m_flOptions , m_pAltStreamMgr ); 
 
		if ( !pHTTPInfo->GetError() ) 
		{	// if the url is bad then abort flag is set 
 
 
			// Serialize HTTP access 	 
			LockHTTP(); 
 
			ChHTTPInfo::GetRequestQueue().AddTail( pHTTPInfo ); 
 
			// Unlock serialization 
			UnlockHTTP(); 
 
			if (  ChHTTPInfo::IsThreaded() )  
			{ 
											/* Wake up the thread to process 
												new connection */ 
				ChHTTPInfo::GetThreadMgr()->TriggerRequestEvent(); 
			} 
			else 
			{ 
 
 
 
									/* If we have less than max 
									   connections, connect and send 
									   the request */ 
				ProcessNonThreadedRequest(); 
			} 
		} 
	} 
 
	if ( GetState() == HTTP_ERROR ) 
	{ 
		// close stream 
		CloseStream(); 
	} 
	m_luToProcess = 0; 
 
 
} 
 
 
/*---------------------------------------------------------------------------- 
 
	FUNCTION	||	ChHTTPInfo::GetNextHdr 
 
------------------------------------------------------------------------------ 
Get the next HTTP header to process 
----------------------------------------------------------------------------*/ 
bool ChHTTPInfo::GetNextHdr( char* &pstrHdr, char* &pstrData, char* &pstrNext, int iDelimType ) 
{ 
	if (!pstrHdr) 
	{ 
		return false; 
	} 
 
	pstrNext = strchr( pstrHdr, TEXT( ':' )); 
 
	if ( !pstrNext ) 
	{ 
		return false; 
	} 
	*pstrNext = 0; 
 
											// Remove any white space 
	while( *pstrHdr == TEXT( ' ' )) 
	{ 
		pstrHdr++; 
	} 
 
	pstrData = pstrNext + 1; 
	pstrNext = strchr( pstrData, LF ); 
 
	if (STANDARD == iDelimType) 
	{ 
		*(pstrNext - 1) = 0;				// remove CR 
	} 
 
	*pstrNext = 0;							// remove LF 
	pstrNext++;								// next command 
 
	if (STANDARD == iDelimType) 
	{ 
		if (*pstrNext == CR && *(pstrNext + 1) == LF) 
		{ 
			pstrNext = 0; 
		} 
	} 
	else if (LF == *pstrNext) 
	{										// this is the last command 
	 	pstrNext = 0; 
	} 
 
	return true; 
} 
 
/*---------------------------------------------------------------------------- 
 
	FUNCTION	||	ChHTTPInfo::GetHdrType 
 
------------------------------------------------------------------------------ 
Get the  HTTP header type. returns > 0 if we are interested in the header else -1 
----------------------------------------------------------------------------*/ 
 
int ChHTTPInfo::GetHdrType( char* pstrHdr  ) 
{ 
	for ( int i = 0; i < sizeof( pstrHTTPTokens )/sizeof( char* ); i++ ) 
	{ 
		if ( !lstrcmpi( pstrHdr, pstrHTTPTokens[i] )) 
		{ 
			return( i ); 
		} 
	} 
	return ( - 1 ); 
} 
 
 
/*---------------------------------------------------------------------------- 
 
	FUNCTION	||	ChHTTPInfo::ProcessNoHeaderReply 
 
------------------------------------------------------------------------------ 
Process HTTP headers 
----------------------------------------------------------------------------*/ 
void ChHTTPInfo::ProcessNoHeaderReply() 
{ 
	// since we dont have the content-encoding lets make 
	// a guess based on data we have 
 
	chuint32  i = 0; 
 
	while( i < m_luToProcess && ( m_pstrBuffer[i] == TEXT( '\n' ) 
							 ||	m_pstrBuffer[i] == TEXT( '\r' ) )) 
	{ 
		i++; 
	} 
 
	if ( m_pstrBuffer[i] == TEXT( '<' ) ) 
	{  
		m_strMimeType = "text/html"; 
	} 
	else if ( m_pstrBuffer[i] ==   TEXT( 'G' )  &&  
			  m_pstrBuffer[i+1] == TEXT( 'I' ) && 
			  m_pstrBuffer[i+2] == TEXT( 'F' ) ) 
	{ 
		m_strMimeType = "image/gif"; 
	} 
	else if ( m_pstrBuffer[i + 6] == TEXT( 'J' )  &&  
			  m_pstrBuffer[i + 7] == TEXT( 'F' ) && 
			  m_pstrBuffer[i + 8] == TEXT( 'I' ) && 
			  m_pstrBuffer[i + 9] == TEXT( 'F' ) ) 
	{ 
		m_strMimeType = "image/jpeg"; 
	} 
	else if ( m_pstrBuffer[i] == TEXT( '#' )  &&  
			  m_pstrBuffer[i+1] == TEXT( 'V' ) && 
			  m_pstrBuffer[i+2] == TEXT( 'R' ) && 
			  m_pstrBuffer[i+3] == TEXT( 'M' ) && 
			  m_pstrBuffer[i+4] == TEXT( 'L' ) ) 
	{ 
		m_strMimeType = "x-world/x-vrml"; 
	} 
	else 
	{  // what is it ??? 
		m_strMimeType = "text/html"; 
	} 
 
	m_strLastModified.Empty(); 
	m_luBodyLen = 0xFFFFFFFF; 
	m_bbCompleted = true;	// We don't know how much data we have, 
 
	fstream*	pFile; 
 
	SetState( HTTP_IN_BODY ); 
 
 
	if ( m_strLocalName.IsEmpty() ) 
	{ // get the local file name 
		MapURLToLocalName( ChHTTPSocketConn::GetMimeType( m_strMimeType ) ); 
	} 
 
	// create the local file  
 
	#ifdef CH_MSW 
		pFile = ::new fstream( GetFileName(), ios::out | IOS_BINARY, filebuf::sh_none ); 
	#else 
		pFile = ::new fstream( GetFileName(), ios::out | IOS_BINARY ); 
	#endif 
 
	SetFile( pFile ); 
 
	ASSERT( GetFile() ); 
 
	if ( !GetFile()->is_open() ) 
	{ 
		SetError( CH_HTTP_ERROR_LOCAL_IN_USE ); 
		SetState( HTTP_ERROR ); 
		CloseStream(); 
		m_luToProcess = 0; 
		return; 
	} 
	else if ( !IsThreaded() ) 
	{ 
										/* The file is opened to write 
											each block */ 
		GetFile()->close(); 
	} 
 
	// Update the active conn status 
	UpdateStatusInfo(); 
 
	// Initalize the stream 
	InitStream(); 
 
 
	if ( m_luToProcess ) 
	{ 
		ProcessBody(); 
	} 
 
	// Allocate a bigger buffer 
	m_luBufferSize =  midBufferSize; 
	char * pstrTemp = new char[ m_luBufferSize + 1]; 
	ASSERT( pstrTemp ); 
	delete [] m_pstrBuffer; 
	m_pstrBuffer = pstrTemp; 
 
} 
 
 
/*---------------------------------------------------------------------------- 
 
	FUNCTION	||	ChHTTPInfo::IsStatusLine 
 
------------------------------------------------------------------------------ 
Determines if the given buffer is a HTTP status line. 
----------------------------------------------------------------------------*/ 
 
bool ChHTTPInfo::IsStatusLine( char* pstrBuffer, chuint iLen ) 
{ 
 
	char* pstrNext = strchr( pstrBuffer, LF ); 
 
	if (  pstrNext ) 
	{ // Looks like we have a status line 
			// do we have the version 
		chuint32  i = 0; 
		while( i < iLen && pstrBuffer[i++] != TEXT( '/' ) ); 
 
		pstrNext = &pstrBuffer[i]; 
		if ( *(pstrNext - 1) == TEXT( '/' ) ) 
		{ 
			*(pstrNext - 1) = TEXT ('\0' ); 
 
			if ( lstrcmpi( pstrHTTPTokens[HTTP], m_pstrBuffer )) 
			{ // invalid status line 
				*(pstrNext - 1) = TEXT( '/' ); 
 
				return false; 
			} 
		} 
		else 
		{  // invalid 
			return false; 
		} 
	} 
	return ( false ); 
} 
 
 
void ChHTTPInfo::SyncInfo( ChHTTPInfo* pInfo ) 
{ 
	m_strLocalName = pInfo->GetFileName(); 
	m_strMimeType  = pInfo->GetMimeType(); 
	m_luBodyLen    = pInfo->GetDataLength(); 
	m_iError	   = pInfo->GetError();	  
} 
 
 
void ChHTTPInfo::AddToConnList( ) 
{ 
	if ( GetState() != HTTP_WAIT ) 
	{ 
	 	m_iActiveConn++; 
	} 
	m_httpConnList.AddHead( this ); 
} 
 
void ChHTTPInfo::RemoveFromConnList( ChPosition posConn ) 
{ 
	if ( GetState() != HTTP_WAIT ) 
	{ 
	 	m_iActiveConn--; 
	} 
	m_httpConnList.Remove( posConn ); 
} 
 
 
void ChHTTPInfo::RemoveConnection( ) 
{ 
	// Serialize HTTP access 	 
	ChHTTPInfo::LockHTTP(); 
 
	ChPosition pos = ChHTTPInfo::GetConnectionList().GetHeadPosition(); 
											/* Delete records for all threads 
												with the END_PROCESS state */ 
	while (0 != pos) 
	{ 
		ChPosition	prevPos = pos; 
		ChHTTPInfo*	pInfo = ChHTTPInfo::GetConnectionList().GetNext( pos ); 
 
		if (pInfo == this ) 
		{ 
			 
			if ( pInfo->IsAborted() || !pInfo->CompletedRequest() )  
		  	{ 
				TRACE1( "Aborting connection %s\n",  (const char *)pInfo->GetURL() ); 
 
				if (!pInfo->GetFileName().IsEmpty()) 
				{  
					if (pInfo->GetFile() && pInfo->GetFile()->is_open()) 
					{ 
						pInfo->GetFile()->close(); 
					} 
 
					if (pInfo->IsZipped() && ChUtil::FileExists( pInfo->GetZipFileName() )) 
					{ 
						::DeleteFile( pInfo->GetZipFileName() ); 
					} 
 
					if (ChUtil::FileExists( pInfo->GetFileName() )) 
					{ 
						::DeleteFile( pInfo->GetFileName() ); 
					}   
				} 
			} 
									// We have a connection 
			pInfo->RemoveFromConnList( prevPos ); 
 
		} 
	} 
	// Unlock serialization 
	ChHTTPInfo::UnlockHTTP(); 
} 
 
/*---------------------------------------------------------------------------- 
 
	FUNCTION	||	ChHTTPInfo::AddToSocketAddrList 
 
------------------------------------------------------------------------------ 
----------------------------------------------------------------------------*/ 
 
void ChHTTPInfo::AddToSocketAddrList( const string& strHost,  
									const sockaddr_in& addrIn ) 
{ 
	LockHTTP(); 
 
	sockaddr_in* pAddr = m_socketAddrList.Find( strHost ); 
 
	if ( 0 == pAddr ) 
	{ 
		m_socketAddrList.Insert( strHost, addrIn );			 
	} 
 
	UnlockHTTP(); 
} 
 
/*---------------------------------------------------------------------------- 
 
	FUNCTION	||	ChHTTPInfo::FindSocketAddr 
 
------------------------------------------------------------------------------ 
----------------------------------------------------------------------------*/ 
bool ChHTTPInfo::FindSocketAddr( const string& strHost, sockaddr_in& addrIn ) 
{ 
	bool boolFound = false; 
 
	LockHTTP();		  
 
	sockaddr_in* pAddr = m_socketAddrList.Find( strHost ); 
 
	if ( pAddr ) 
	{ 
		boolFound = true;		 
		addrIn = *pAddr; 
	} 
 
	UnlockHTTP();  
 
	return boolFound; 
} 
 
 
 
/*---------------------------------------------------------------------------- 
 
	FUNCTION	||	ChHTTPInfo::DeleteCache 
 
------------------------------------------------------------------------------ 
Delete the local cached URL. 
----------------------------------------------------------------------------*/ 
 
bool ChHTTPInfo::DeleteCache( const string& strURL ) 
{ 
	bool	 	boolReturn; 
	string strKey = strURL; 
	strKey.MakeLower();	   
	ChDBKey		dbKey( strKey ); 
 
	// Serialize HTTP access 	 
	LockHTTP(); 
 
											// Delete the cache 
	boolReturn = m_pURLDB->Delete( dbKey ); 
 
	if ( boolReturn ) 
	{ 
		if ( ChUtil::FileExists( GetFileName() ) ) 
		{ 
											// file does not exists 
			::DeleteFile( GetFileName() ); 
		} 
	} 
 
	// Unlock serialization 
	UnlockHTTP(); 
 
	return boolReturn; 
} 
 
 
/*---------------------------------------------------------------------------- 
 
	FUNCTION	||	ChHTTPInfo::ClearCache 
 
------------------------------------------------------------------------------ 
Delete the local cached URL. 
----------------------------------------------------------------------------*/ 
 
bool ChHTTPInfo::ClearCache( ) 
{ 
	bool		boolDBOpen = false; 
 
	{ 
 
		if ( !m_pURLDB ) 
		{ 
			OpenCacheDataBase(); 
			boolDBOpen = true; 
		} 
 
		if ( !m_pURLDB ) 
		{ 
			// close the database 
			delete m_pURLDB; 
		} 
 
		// delete all the files in the cache directory 
		void * pDir = ChUtil::OpenDirectory( m_strCacheDir, TEXT( "*.*" ), 0XFFFFFFFF ); 
 
		if ( pDir ) 
		{ 
			ChFileAttrs attrs; 
 
			while( ChUtil::ReadDirectory( pDir,  &attrs, ChUtil::reqPath ) ) 
			{ 
				if ( attrs.uFileType == ChUtil::typeFile ) 
				{ 
					TRACE1( "Deleteing file %s\n", attrs.astrName ); 
					::DeleteFile( attrs.astrName ); 
				} 
			} 
 
			ChUtil::CloseDirectory( pDir ); 
		} 
 
		if ( boolDBOpen ) 
		{ 
			delete m_pURLDB; 
			m_pURLDB = NULL; 
		} 
		else 
		{  // create a new database 
			OpenCacheDataBase(); 
		} 
	} 
 
	return true; 
} 
 
#if defined( _DEBUG ) 
bool ChHTTPInfo::DisplayAllDBKeys( ) 
{ 
 
	{ 
		if ( !m_pURLDB ) 
		{ 
			return false; 
		} 
 
		TRACE( "Display all DB keys\n\n" ); 
 
		ChDBKey dbKey = m_pURLDB->GetFirstKey(); 
 
		while( dbKey.GetKeySize() ) 
		{ 
			string strURL( dbKey.GetKey(), (int)dbKey.GetKeySize() ); 
			ChDBData dbData = m_pURLDB->GetData( dbKey ); 
 
			string strFile = (char*)dbData.GetData() ; 
			strFile = (char*)dbData.GetData() ; 
			TRACE2( "URL %s, Local file %s\n",  
						((const char*)strURL), ((const char*)strFile) ); 
 
			dbKey = m_pURLDB->GetNextKey( dbKey ); 
		} 
 
		TRACE( "End of all DB keys\n\n" ); 
 
 
	} 
 
	return true; 
} 
 
#endif 
 
 
/*---------------------------------------------------------------------------- 
 
	FUNCTION	||	ChHTTPInfo::EnforceCacheLimit 
 
------------------------------------------------------------------------------ 
This method prunes the cache to the user requested size 
----------------------------------------------------------------------------*/ 
 
bool ChHTTPInfo::EnforceCacheLimit( ) 
{ 
	bool		boolDBOpen = false; 
 
	#if defined( CH_VRML_VIEWER ) 
	ChRegistry  reg( CH_COMPANY_NAME, CH_VRML_PRODUCT_NAME, CH_CACHE_GROUP ); 
	#else 
	ChRegistry	reg( CH_CACHE_GROUP ); 
	#endif 
 
	{ 
		if ( !m_pURLDB ) 
		{ 
			OpenCacheDataBase(); 
			boolDBOpen = true; 
		} 
  
 		chuint32 luTotalSize = 0; 
 
		void * pDir = ChUtil::OpenDirectory( m_strCacheDir, TEXT( "*.*" ), 0XFFFFFFFF ); 
 
		if ( pDir ) 
		{ 
			ChFileAttrs attrs; 
 
			while( ChUtil::ReadDirectory( pDir,  &attrs, ChUtil::reqSize) ) 
			{ 
				if ( attrs.uFileType == ChUtil::typeFile ) 
				{ 
					luTotalSize += attrs.luSize; 
				} 
			} 
 
			ChUtil::CloseDirectory( pDir ); 
		} 
 
		if ( luTotalSize ) 
		{ 
			chuint32	luCacheSize; 
 
	 		reg.Read( CH_CACHE_SIZE, luCacheSize, CH_CACHE_SIZE_DEF ); 
			luCacheSize *= 1024;  // size specified in kilobytes 
 
			if ( luTotalSize > luCacheSize  ) 
			{ // build a list of files and delete the oldest files first till  
			  //we go under the cache limit 
			   	ChParamList fileList; 
				ChPosition   pPos; 
 
				string strDir = m_strCacheDir; 
				strDir += PATH_SEPARATOR_CHAR; 
				strDir+= URL_DBNAME; 
 
				string strPag = strDir; 
 
				strPag += ".pag"; 
				strDir += ".dir"; 
				bool boolCheckPag = true, boolCheckDir = true; 
 
 
 
				void * pDir = ChUtil::OpenDirectory( m_strCacheDir, TEXT( "*.*" ), 0XFFFFFFFF ); 
 
				if ( pDir ) 
				{ 
					ChFileAttrs attrs; 
 
					while( ChUtil::ReadDirectory( pDir,  &attrs,  
									ChUtil::reqSize | ChUtil::reqTime | ChUtil::reqPath ) ) 
					{ 
						if ( attrs.uFileType == ChUtil::typeFile ) 
						{ 
							// if the file is part of database don't add the file 
							if ( boolCheckDir && strDir.CompareNoCase( attrs.astrName ) == 0 ) 
							{ 
								boolCheckDir = false; 
								continue; 
							} 
 
							if ( boolCheckPag && strPag.CompareNoCase( attrs.astrName ) == 0 ) 
							{ 
								boolCheckPag = false; 
								continue; 
							} 
 
							pChFileAttrs pAttrs; 
							pChFileAttrs pElem; 
							pAttrs = new ChFileAttrs; 
							*pAttrs = attrs; 
 
							if ( fileList.IsEmpty() ) 
							{ 
								fileList.AddHead( (chparam)pAttrs ); 
							} 
							else 
							{ 
								 
								pPos = fileList.GetHeadPosition();  
 
								while( pPos ) 
								{ 
									pElem = (pChFileAttrs)fileList.Get( pPos );	 
 
									if ( ChUtil::CompareFileTime( pAttrs->mtime, pElem->mtime )  <= 0 ) 
									{ 
										break; 
									} 
									fileList.GetNext( pPos );	 
								} 
								// add it to the list 
								if ( pPos ) 
								{ 
									fileList.InsertBefore( pPos, (chparam)pAttrs );	 
								} 
								else 
								{ 
									fileList.AddTail( (chparam)pAttrs ); 
								} 
 
							} 
						} 
					} 
					ChUtil::CloseDirectory( pDir ); 
				} 
				// we have a list of files sorted by time in ascending order, we will start deleting file 
				// in ascending order till we go below the limit 
				 
				pPos = fileList.GetHeadPosition();  
 
				while( pPos && luTotalSize > luCacheSize ) 
				{ 
					pChFileAttrs pElem; 
					pElem = (pChFileAttrs)fileList.GetNext( pPos );	 
 
					if ( ::DeleteFile( pElem->astrName ) ) 
					{ 
						luTotalSize -= pElem->luSize;	 
					} 
				} 
				// Delete all the attrs structs allocated 
				pPos = fileList.GetHeadPosition();  
 
				while( pPos && luTotalSize > luCacheSize ) 
				{ 
					pChFileAttrs pElem; 
					pElem = (pChFileAttrs)fileList.GetNext( pPos );	 
					delete pElem; 
				} 
				fileList.Empty(); 
 
				// update the database 
				ChDBKey dbKey = m_pURLDB->GetFirstKey(); 
 
				while (  dbKey.GetKeySize() ) 
				{ 
 
					ChDBData dbData = m_pURLDB->GetData( dbKey ); 
 
					string strFile = m_strCacheDir; 
					strFile += PATH_SEPARATOR_CHAR; 
					strFile += (char*)dbData.GetData() ; 
 
 
					if (  !ChUtil::FileExists( strFile ) ) 
					{ 
						ChDBKey dbOldKey = dbKey; 
 
						dbKey = m_pURLDB->GetNextKey( dbKey ); 
 
						m_pURLDB->Delete( dbOldKey ); 
					} 
					else 
					{ 
						dbKey = m_pURLDB->GetNextKey( dbKey ); 
					} 
				} 
			} 
		} 
 
		if ( boolDBOpen ) 
		{ 
			delete m_pURLDB; 
			m_pURLDB = NULL; 
		} 
	} 
 
	return true; 
} 
 
/*---------------------------------------------------------------------------- 
 
	FUNCTION	||	ChHTTPInfo::Visited 
 
------------------------------------------------------------------------------ 
This method returns true if the URL has been visted during the current session 
----------------------------------------------------------------------------*/ 
bool ChHTTPInfo::Visited() 
{ 
	return(bool)ChHTTPInfo::m_httpVisited.Find( m_urlParts.GetURL() ); 
} 
 
/*---------------------------------------------------------------------------- 
 
	FUNCTION	||	ChHTTPInfo::CreateAndValidateCacheDir 
 
------------------------------------------------------------------------------ 
This method creates the cache directory 
----------------------------------------------------------------------------*/ 
 
bool ChHTTPInfo::CreateAndValidateCacheDir( string& strDir ) 
{ 
	if (!ChUtil::FileExists( strDir )) 
	{ 
		ChUtil::CreateDirectoryTree( strDir ); 
		if (!ChUtil::FileExists( strDir )) 
		{ 
			return false; 
		} 
 
	} 
	return true; 
} 
 
// Local Variables: *** 
// tab-width:4 *** 
// End: ***