/* ***** BEGIN LICENSE BLOCK *****
 * Source last modified: $Id: hxcloakedtcp.cpp,v 1.7.8.2 2004/07/13 18:06:24 bobclark Exp $
 * 
 * Portions Copyright (c) 1995-2004 RealNetworks, Inc. All Rights Reserved.
 * 
 * The contents of this file, and the files included with this file,
 * are subject to the current version of the RealNetworks Public
 * Source License (the "RPSL") available at
 * http://www.helixcommunity.org/content/rpsl unless you have licensed
 * the file under the current version of the RealNetworks Community
 * Source License (the "RCSL") available at
 * http://www.helixcommunity.org/content/rcsl, in which case the RCSL
 * will apply. You may also obtain the license terms directly from
 * RealNetworks.  You may not use this file except in compliance with
 * the RPSL or, if you have a valid RCSL with RealNetworks applicable
 * to this file, the RCSL.  Please see the applicable RPSL or RCSL for
 * the rights, obligations and limitations governing use of the
 * contents of the file.
 * 
 * Alternatively, the contents of this file may be used under the
 * terms of the GNU General Public License Version 2 or later (the
 * "GPL") in which case the provisions of the GPL are applicable
 * instead of those above. If you wish to allow use of your version of
 * this file only under the terms of the GPL, and not to allow others
 * to use your version of this file under the terms of either the RPSL
 * or RCSL, indicate your decision by deleting the provisions above
 * and replace them with the notice and other provisions required by
 * the GPL. If you do not delete the provisions above, a recipient may
 * use your version of this file under the terms of any one of the
 * RPSL, the RCSL or the GPL.
 * 
 * This file is part of the Helix DNA Technology. RealNetworks is the
 * developer of the Original Code and owns the copyrights in the
 * portions it created.
 * 
 * This file, and the files included with this file, is distributed
 * and made available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY
 * KIND, EITHER EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS
 * ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET
 * ENJOYMENT OR NON-INFRINGEMENT.
 * 
 * Technology Compatibility Kit Test Suite(s) Location:
 *    http://www.helixcommunity.org/content/tck
 * 
 * Contributor(s):
 * 
 * ***** END LICENSE BLOCK ***** */
#include "hlxclib/stdio.h"   
#include "hxcloakedtcp.h"

#include "safestring.h"

#include "hxnetutil.h"

#include "ihxpckts.h"
#include "hxccf.h" // IHXCommonClassFactory

#include "hxthread.h" // HXMutex
#include "hxbuffer.h" // CHXBuffer
#include "conn.h"     // TCP_BUF_SIZE
#include "dbcs.h"     // HXFindString
#include "hxmarsh.h"  // getshort()
#include "chxuuid.h"  // CHXuuid
#include "hxtick.h"   // HX_GET_TICKCOUNT
#include "rtsputil.h" // BinTo64
#include "hxfiles.h"  // CLSID_IHXRequest
#include "hxplnsp.h"  // _CIHXObjectConfiguration_SP
#include "hxcomsp.h"  // _CIHXCommonClassFactory_SP

// necessary for authentication
#include "hxmon.h"
#include "httppars.h"
#include "hxplgns.h"
#include "hxcore.h"

#include "httpclk.h"

#ifdef _MACINTOSH
#include "hx_moreprocesses.h"
#endif

/******************* HTTP Cloaking*****************/

#define QUEUE_START_SIZE    512

#define HTTPCLOAK_PUTRESPONSE_TIMEOUT	2000

#define MAX_HTTP_METHOD_BUFSIZE           2048

#define HXGUID_SIZE 	16 // 16 byte GUID plus \r\n

#define ENQUEUE_DATA(x,y,z) (x)->EnQueue((y),(z))

#define ENQUEUE_BYTE(x,y) {UCHAR uChar = (y); \
		(x)->EnQueue(&uChar,sizeof(uChar));}

#define ENQUEUE_WORD(x,y) {UINT16 wTemp = (y); \
		wTemp = WToNet(wTemp); \
		(x)->EnQueue(&wTemp,sizeof(UINT16));}

#define ENQUEUE_DWORD(x,y) {ULONG32 dTemp = (y); \
		dTemp = DwToNet(dTemp); \
		(x)->EnQueue(&dTemp,sizeof(ULONG32));}

#define DEFAULT_HTTP_HEADER_LENGTH	256
#define DEFAULT_OPTION_PADDING_LENGTH	16381

#ifdef _WINCE
#define SCHED_GRANULARITY 10
#else
#define SCHED_GRANULARITY 50
#endif

enum
{
    HTTP_OK = 0,
    HTTP_GENERAL_ERROR, // for any error that is not defined below
    POST_NOT_RECEIVED, 	// POST message was not received
    INVALID_GUID 	// sent only if the GUID from the Player is already in use
};


/* HXClientCloakedTCPSocket */
HXClientCloakedTCPSocket::HXClientCloakedTCPSocket(IUnknown* pContext):
     m_lRefCount(0)
    ,m_pContext(pContext)
    ,m_pTCPResponse(0)
    ,m_pNetworkServices(0)
    ,m_pGetCtrl(0)
    ,m_pPutCtrl(0)
    ,m_pGetCtrlResponse(0)
    ,m_pPutCtrlResponse(0)
    ,m_bGetReadPending(FALSE)
    ,m_bPutReadPending(FALSE)
    ,m_bPutWantWritePending(FALSE)
    ,m_lForeignAddress(0)
    ,m_nForeignPort(0)
    ,m_bReadPending(FALSE)
    ,m_nRequired(0)
    ,m_pSendTCP(0)
    ,m_pReceiveGetTCP(0)
    ,m_pReceivePutTCP(0)
    ,m_pPreEncodedSendHTTP(0)
    ,m_pPostEncodedSendHTTP(0)
    ,m_pOutBuf(0)
    ,m_pOutEncodedBuf(0)
    ,m_pInBuf(0)
    ,m_bConnected(FALSE)
    ,m_pForiegnHost(0)
    ,m_pGuid(0)
    ,m_bGetConnectDone(FALSE)
    ,m_bPutConnectDone(FALSE)
    ,m_bGetConnectSuccessful(FALSE)
    ,m_bPutConnectSuccessful(FALSE)
    ,m_bConnectResponsePending(TRUE)
    ,m_bOptionsReceived(FALSE)
    ,m_bUseExactContentLength(FALSE)
    ,m_bCloseHttpAfterWrite(FALSE)
    ,m_bMustCloseHTTP(FALSE)
    ,m_bHttpInitialized(FALSE)
    ,m_LastError(HXR_OK)
    ,m_pHTTPHeaderBuffer(0)
    ,m_nHTTPHeaderBufferLength(0)
    ,m_bHTTPGetHeaderReadDone(FALSE)
    ,m_bHTTPPutHeaderReadDone(FALSE)
    ,m_pProxyHostName(0)
    ,m_nProxyPortNumber(0)
    ,m_bInDestructor(FALSE)
    ,m_pScheduler(0)
    ,m_pSchedulerCallback(0)
    ,m_pNonInterruptCallback(0)
    ,m_bInDoRead(FALSE)
    ,m_bInDoGetWrite(FALSE)
    ,m_bInDoPutWrite(FALSE)
    ,m_bInTransferBuffers(FALSE)
    ,m_bInitComplete(FALSE)
    ,m_bDeletePadding(FALSE)
    ,m_uPadLength(10+DEFAULT_OPTION_PADDING_LENGTH) // 10 for the opts and 16381 for the padding
    ,m_pInterruptState(NULL)
    ,m_pResponseInterruptSafe(NULL)
    ,m_pMutex(NULL)
    ,m_pCloakValues(NULL)
    ,m_pCloakContext(NULL)
    ,m_bGetResponsed(FALSE)
    ,m_bPutResponsed(FALSE)
    ,m_pszGetServerIP(NULL)
    ,m_pszPutServerIP(NULL)
    ,m_bReconnectToSameServerIP(FALSE)
    ,m_bConnectToSameServerIP(FALSE)
    ,m_pPreferredTransport(NULL)
    ,m_pPreferredTransportManager(NULL)
#ifdef _MACINTOSH
    ,m_pAuthenticationCallback(NULL)
#endif
    ,m_bInAuthenticationKludge(FALSE)
{
    if(m_pContext)
    {
	m_pContext->AddRef();
	m_pContext->QueryInterface(IID_IHXScheduler, (void**) &m_pScheduler);
	m_pContext->QueryInterface(IID_IHXNetworkServices, (void**) &m_pNetworkServices);
	m_pContext->QueryInterface(IID_IHXInterruptState, (void**) &m_pInterruptState);
	m_pContext->QueryInterface(IID_IHXPreferredTransportManager, (void**)&m_pPreferredTransportManager);
    }

#if defined(THREADS_SUPPORTED) 
    HXMutex::MakeMutex(m_pMutex);
#elif defined(_UNIX_THREADED_NETWORK_IO)
    if( ReadNetworkThreadingPref((IUnknown*)pContext) )
    {
        HXMutex::MakeMutex(m_pMutex);
    }
    else
    { 
        HXMutex::MakeStubMutex(m_pMutex);
    }
#else
    HXMutex::MakeStubMutex(m_pMutex);
#endif

#ifdef _MACINTOSH
    m_pAuthenticationCallback = new MacCloakedTCPSocketAuthenticationCallback(this);
    m_pAuthenticationCallback->AddRef();
#endif
}

HXClientCloakedTCPSocket::~HXClientCloakedTCPSocket()
{
    m_bInDestructor = TRUE;
    
    m_pMutex->Lock();

#ifdef _MACINTOSH
    if (m_pAuthenticationCallback && m_pAuthenticationCallback->m_ulPendingCallbackID &&
		m_pScheduler)
    {
	m_pScheduler->Remove(m_pAuthenticationCallback->m_ulPendingCallbackID);
	m_pAuthenticationCallback->m_ulPendingCallbackID = NULL;
    }
    HX_RELEASE(m_pAuthenticationCallback);
#endif

    while (m_PendingWriteBuffers.GetCount() > 0)
    {
	IHXBuffer* pBuffer = (IHXBuffer*) m_PendingWriteBuffers.RemoveHead();
	pBuffer->Release();
    }

    FlushQueues();

    /* Send a final HTTP done message */
    if (m_bHttpInitialized)
    {
	SendHTTPDone();
    }

    if (m_pSchedulerCallback)
    {
	m_pSchedulerCallback->Unschedule(m_pScheduler);
	HX_RELEASE(m_pSchedulerCallback);
    }

    if (m_pNonInterruptCallback)
    {
	m_pNonInterruptCallback->Unschedule(m_pScheduler);
	HX_RELEASE(m_pNonInterruptCallback);
    }

    HX_RELEASE(m_pGetCtrl);
    HX_RELEASE(m_pPutCtrl);
    HX_RELEASE(m_pGetCtrlResponse);
    HX_RELEASE(m_pPutCtrlResponse);

    HX_RELEASE(m_pCloakValues);
    HX_RELEASE(m_pCloakContext);
    HX_RELEASE(m_pTCPResponse);
    HX_RELEASE(m_pNetworkServices);

    HX_DELETE(m_pSendTCP);
    HX_DELETE(m_pReceiveGetTCP);
    HX_DELETE(m_pReceivePutTCP);
    HX_DELETE(m_pPreEncodedSendHTTP);
    HX_DELETE(m_pPostEncodedSendHTTP);

    HX_VECTOR_DELETE(m_pInBuf);
    HX_VECTOR_DELETE(m_pOutBuf);
    HX_VECTOR_DELETE(m_pOutEncodedBuf);
    HX_VECTOR_DELETE(m_pForiegnHost);
    HX_VECTOR_DELETE(m_pGuid);
    HX_VECTOR_DELETE(m_pHTTPHeaderBuffer);
    HX_VECTOR_DELETE(m_pProxyHostName);
    HX_VECTOR_DELETE(m_pszGetServerIP);
    HX_VECTOR_DELETE(m_pszPutServerIP);

    HX_RELEASE(m_pPreferredTransport);
    HX_RELEASE(m_pPreferredTransportManager);
    HX_RELEASE(m_pInterruptState);
    HX_RELEASE(m_pResponseInterruptSafe);
    HX_RELEASE(m_pScheduler);

    HX_RELEASE(m_pContext);
    m_pMutex->Unlock();
    HX_DELETE(m_pMutex);
}

STDMETHODIMP HXClientCloakedTCPSocket::QueryInterface(REFIID riid, void** ppvObj)
{
    QInterfaceList qiList[] =
        {
            { GET_IIDHANDLE(IID_IHXTCPSocket), (IHXTCPSocket*)this },
            { GET_IIDHANDLE(IID_IHXCloakedTCPSocket), (IHXCloakedTCPSocket*)this },
            { GET_IIDHANDLE(IID_IHXHTTPProxy), (IHXHTTPProxy*)this },
            { GET_IIDHANDLE(IID_IUnknown), (IUnknown*)(IHXTCPSocket*)this },
        };
    
    return ::QIFind(qiList, QILISTSIZE(qiList), riid, ppvObj);
}

STDMETHODIMP_(ULONG32) HXClientCloakedTCPSocket::AddRef()
{
    return InterlockedIncrement(&m_lRefCount);
}

STDMETHODIMP_(ULONG32) HXClientCloakedTCPSocket::Release()
{
    if (InterlockedDecrement(&m_lRefCount) > 0)
    {
        return m_lRefCount;
    }

    delete this;
    return 0;
}

STDMETHODIMP HXClientCloakedTCPSocket::Init(IHXTCPResponse* pTCPResponse)
{
    HX_RESULT rc = HXR_OK;

    if (!pTCPResponse)
    {
	return HXR_UNEXPECTED;
    }

    if (!m_pSchedulerCallback)
    {
	m_pSchedulerCallback = new ScheduledSocketCallback(this, TRUE);
	m_pSchedulerCallback->AddRef();
    }

    if (!m_pNonInterruptCallback)
    {
	m_pNonInterruptCallback = new ScheduledSocketCallback(this, TRUE);
	m_pNonInterruptCallback->AddRef();
    }

    m_pTCPResponse = pTCPResponse;
    m_pTCPResponse->AddRef();

    m_pTCPResponse->QueryInterface(IID_IHXInterruptSafe, 
				   (void**) &m_pResponseInterruptSafe);

    if (!m_pNetworkServices)
    {
	return HXR_FAILED;
    }
    
    if (HXR_OK != m_pNetworkServices->CreateTCPSocket(&m_pGetCtrl))
    {
	return HXR_FAILED;
    }

    if (HXR_OK != m_pNetworkServices->CreateTCPSocket(&m_pPutCtrl))
    {
	return HXR_FAILED;
    }

    m_pGetCtrlResponse = new HTTPCloakTCPResponse(this, TRUE);
    m_pPutCtrlResponse = new HTTPCloakTCPResponse(this, FALSE);
    if (!m_pGetCtrlResponse || !m_pPutCtrlResponse)
    {
	return HXR_OUTOFMEMORY;
    }

    m_pGetCtrlResponse->AddRef();
    m_pPutCtrlResponse->AddRef();

    if (HXR_OK != m_pGetCtrl->Init(m_pGetCtrlResponse) || 
		HXR_OK != m_pPutCtrl->Init(m_pPutCtrlResponse))
    {
	return HXR_FAILED;
    }

    if (HXR_OK != m_pGetCtrl->Bind(HXR_INADDR_ANY, 0) || 
		HXR_OK != m_pPutCtrl->Bind(HXR_INADDR_ANY, 0))
    {
    	return HXR_FAILED;
    }

    // allocate TCP send and receive queue
    m_pSendTCP = new CByteGrowingQueue(QUEUE_START_SIZE,1);
    if (!m_pSendTCP || !m_pSendTCP->IsQueueValid())
    {
	return HXR_OUTOFMEMORY;
    }
    m_pSendTCP->SetMaxSize(TCP_BUF_SIZE);

    m_pReceiveGetTCP = new CByteGrowingQueue(QUEUE_START_SIZE,1);
    if (!m_pReceiveGetTCP || !m_pReceiveGetTCP->IsQueueValid())
    {
	return HXR_OUTOFMEMORY;
    }
    m_pReceiveGetTCP->SetMaxSize(TCP_BUF_SIZE);

    m_pPreEncodedSendHTTP = new CByteGrowingQueue(QUEUE_START_SIZE,1);
    if (!m_pPreEncodedSendHTTP || !m_pPreEncodedSendHTTP->IsQueueValid())
    {
	return HXR_OUTOFMEMORY;
    }

    /* Approx. MAX POST header size : 3000*/
    m_pPreEncodedSendHTTP ->SetMaxSize((TCP_BUF_SIZE-1)/2 - 3000);

    m_pPostEncodedSendHTTP = new CByteGrowingQueue(QUEUE_START_SIZE,1);
    if (!m_pPostEncodedSendHTTP || !m_pPostEncodedSendHTTP->IsQueueValid())
    {
	return HXR_OUTOFMEMORY;
    }
    m_pPostEncodedSendHTTP->SetMaxSize(TCP_BUF_SIZE);
    
    m_pInBuf = new char[TCP_BUF_SIZE];
    if (!m_pInBuf)
    {
	return HXR_OUTOFMEMORY;
    }

    m_pOutBuf = new char[TCP_BUF_SIZE];
    if (!m_pOutBuf)
    {
	return HXR_OUTOFMEMORY;
    }

    m_pOutEncodedBuf = new char[TCP_BUF_SIZE];
    if (!m_pOutEncodedBuf)
    {
	return HXR_OUTOFMEMORY;
    }

    CreateGuid();

    return HXR_OK;
}

STDMETHODIMP HXClientCloakedTCPSocket::SetResponse(IHXTCPResponse* pTCPResponse)
{
    m_pMutex->Lock();
    HX_RELEASE(m_pTCPResponse);
    m_pTCPResponse = pTCPResponse;
    m_pTCPResponse->AddRef();

    HX_RELEASE(m_pResponseInterruptSafe);
    m_pTCPResponse->QueryInterface(IID_IHXInterruptSafe, 
				   (void**) &m_pResponseInterruptSafe);
    m_pMutex->Unlock();
    return HXR_OK;
}

STDMETHODIMP HXClientCloakedTCPSocket::Bind(UINT32 ulLocalAddr, UINT16 nPort)
{
    return HXR_NOTIMPL;
}

STDMETHODIMP HXClientCloakedTCPSocket::Connect(const char* pDestination,
						UINT16	    nPort)
{
    m_nForeignPort = nPort;

    HX_VECTOR_DELETE(m_pForiegnHost);
    m_pForiegnHost = new char [strlen(pDestination) + 1];
    if (!m_pForiegnHost)
    {
	return HXR_OUTOFMEMORY;
    }

    ::strcpy(m_pForiegnHost, pDestination); /* Flawfinder: ignore */

    // m_pCloakValues is only set by RTSP
    if (m_pPreferredTransportManager && m_pCloakValues)
    {
	HX_RELEASE(m_pPreferredTransport);
	m_pPreferredTransportManager->GetPrefTransport(m_pForiegnHost,
						       PTP_RTSP,
						       m_pPreferredTransport);

	HX_ASSERT(m_pPreferredTransport);
	if (m_pPreferredTransport)
	{
	    m_bConnectToSameServerIP = m_pPreferredTransport->GetHTTPNG();
	}
    }

    return  ActualConnect();
}

STDMETHODIMP HXClientCloakedTCPSocket::Read(UINT16 uSize)
{
    HX_RESULT	theErr	= HXR_OK;
    HX_RESULT	lResult = HXR_OK;

    if (m_bReadPending)
    {
	return HXR_UNEXPECTED;
    }

    m_bReadPending  = TRUE;
    m_nRequired	    = uSize;

    m_pMutex->Lock();
    theErr  = DoRead();
    m_pMutex->Unlock();
    lResult = ConvertNetworkError(theErr);

    return lResult;
}

STDMETHODIMP HXClientCloakedTCPSocket::Write(IHXBuffer* pBuffer)
{
    HX_RESULT	theErr	= HXR_OK;
    HX_RESULT	lResult = HXR_OK;

    pBuffer->AddRef();
    m_PendingWriteBuffers.AddTail((void*) pBuffer);
    
    m_pMutex->Lock();
    theErr = DoPutWrite();
    m_pMutex->Unlock();
    lResult = ConvertNetworkError(theErr);

    return lResult;
}

STDMETHODIMP HXClientCloakedTCPSocket::WantWrite()
{
    m_pTCPResponse->WriteReady(HXR_OK);

    return HXR_OK;
}

STDMETHODIMP HXClientCloakedTCPSocket::GetLocalAddress(ULONG32& lAddress)
{
    return HXR_NOTIMPL;
}

STDMETHODIMP HXClientCloakedTCPSocket::GetForeignAddress(ULONG32& lAddress)
{
    if(m_bConnected && m_lForeignAddress)
    {
	lAddress = m_lForeignAddress;
	return HXR_OK;
    }
    return HXR_FAIL;
}

STDMETHODIMP HXClientCloakedTCPSocket::GetLocalPort(UINT16& port)
{
    return HXR_NOTIMPL;
}

STDMETHODIMP HXClientCloakedTCPSocket::GetForeignPort(UINT16& port)
{
    if(m_bConnected)
    {
	port = m_nForeignPort;
	return HXR_OK;
    }
    return HXR_FAIL;
}

/*
 *	IHXHTTPProxy methods
 *
 *  Network addresses and ports are in native byte order
 *  
 */

STDMETHODIMP HXClientCloakedTCPSocket::SetProxy(
				    const char*	    /*IN*/  pProxyHostName,
				    UINT16	    /*IN*/  nPort)
{
    if(pProxyHostName == 0 || *pProxyHostName == 0) 
    {
	return HXR_FAILED;
    }
    
    HX_VECTOR_DELETE(m_pProxyHostName);
    m_pProxyHostName = new char[::strlen(pProxyHostName) + 1];
    if (!m_pProxyHostName)
    {
	return HXR_OUTOFMEMORY;
    }

    ::strcpy(m_pProxyHostName, pProxyHostName); /* Flawfinder: ignore */
    m_nProxyPortNumber = nPort;

    return HXR_OK;
}

/************************************************************************
 *	Method:
 *	    IHXCloakedTCPSocket::InitCloak
 */
STDMETHODIMP
HXClientCloakedTCPSocket::InitCloak(IHXValues* /*IN*/  pValues, IUnknown* pUnknown)
{
    HX_RESULT	rc = HXR_OK;

    if (pValues)
    {
	m_pCloakValues = pValues;
	m_pCloakValues->AddRef();
    }
    if (pUnknown)
    {
	m_pCloakContext = pUnknown;
	m_pCloakContext->AddRef();
    }

    return rc;
}

HX_RESULT
HXClientCloakedTCPSocket::DoRead()
{
    HX_RESULT theErr = HXR_OK;
    char*   pStartAfterDoubleReturn = 0;
    UINT16 count = 0;

    if(m_bInDoRead)
    {
	return HXR_OK;
    }

    m_bInDoRead = TRUE;

    if (m_LastError)
    {
	goto exit;
    }

    // m_pReceivePutTCP is only initialized if we receive POST response
    // from the server
    if (m_pReceivePutTCP)
    {
	count = m_pReceivePutTCP->GetMaxAvailableElements();
    }
    else
    {
	count = TCP_BUF_SIZE;
    }

    // attempt to read from PUT if:
    // * we haven't initiated PUT read already AND
    // * we haven't received the complete POST response header AND
    // * we are not in multi-POST mode
    if (count > 0		    && 
	!m_bPutReadPending	    && 
	!m_bHTTPPutHeaderReadDone   &&
	!m_bMustCloseHTTP)
    {
	// attempt to read data from TCP link
	m_bPutReadPending = TRUE;
	theErr = m_pPutCtrl->Read(count);
    }

    // check how much room we have in TCP receive queue
    count = m_pReceiveGetTCP->GetMaxAvailableElements();
    if (count > 0 && !m_bGetReadPending)
    {
	// attempt to read data from TCP link
	m_bGetReadPending = TRUE;
	theErr = m_pGetCtrl->Read(count);
	switch(theErr)
	{
	    case HXR_AT_INTERRUPT:			
	    case HXR_WOULD_BLOCK:
	    case HXR_OK:
		// mask out these errors
		theErr = HXR_OK;
		break;

	    default:
		theErr = ConvertNetworkError(theErr);
		break;
	}
    }

exit:
    if (theErr && !m_LastError)
    {
	m_LastError = theErr;
    }

    if (m_LastError)
    {
	if (m_bReadPending)
	{
	    if (IsSafe())
	    {
		m_bReadPending = FALSE;
		m_pTCPResponse->ReadDone(m_LastError, 0);
	    }
	}
    }
    else
    {
	count = m_pReceiveGetTCP->GetQueuedItemCount();

	if (m_bReadPending && count > 0 && m_bOptionsReceived 
	    && m_bHTTPGetHeaderReadDone)
	{
	    /* mask any kind of errors */
	    theErr = HXR_OK;

	    if (IsSafe())
	    {
		m_bReadPending = FALSE;
		if (m_nRequired < count)
		{
		    count = m_nRequired;
		}

		CHXBuffer* pBuffer = new CHXBuffer;
		pBuffer->AddRef();
		m_pReceiveGetTCP->DeQueue(m_pInBuf, count);
		pBuffer->Set((UCHAR*) m_pInBuf, count);
		
		m_pTCPResponse->ReadDone(HXR_OK, pBuffer);
		pBuffer->Release();
	    }
	}

	if (m_pSchedulerCallback && m_bReadPending)
	{
	    m_pSchedulerCallback->ScheduleCallback(CLOAKED_TCP_READ_COMMAND, m_pScheduler, SCHED_GRANULARITY);
	}
    }
 
    m_bInDoRead = FALSE;
    return theErr;
}

HX_RESULT
HXClientCloakedTCPSocket::DoGetWrite()
{
    HX_RESULT theErr = HXR_OK;

    if (m_bInDoGetWrite)
    {
	return HXR_OK;
    }

    m_bInDoGetWrite = TRUE;

    // check how data we have in TCP send queue
    UINT16 count    = m_pSendTCP->GetQueuedItemCount();
    UINT16 actual   = count;
    
    if(count > 0) 
    {
	m_pSendTCP->DeQueue(m_pOutBuf,count);
	CHXBuffer* pBuffer = new CHXBuffer;
	pBuffer->AddRef();
	pBuffer->Set((UCHAR*) m_pOutBuf, (ULONG32) count);
	theErr = m_pGetCtrl->Write(pBuffer);
	pBuffer->Release();
    }
    
    if (theErr)
    {
	switch(theErr)
	{
	    case HXR_AT_INTERRUPT:			
	    case HXR_WOULD_BLOCK:
		m_pSendTCP->EnQueue(m_pOutBuf, count);
		// mask out these errors
		theErr = HXR_OK;
		break;

	    default:
		theErr = ConvertNetworkError(theErr);
		break;
	}
    }

    if (!theErr && !m_bInDestructor &&
	m_pSendTCP->GetQueuedItemCount() > 0 ) {
	    m_pSchedulerCallback->ScheduleCallback(CLOAKED_TCP_GETWRITE_COMMAND, m_pScheduler, SCHED_GRANULARITY);
    }

    m_bInDoGetWrite = FALSE;
    return theErr;
}

HX_RESULT
HXClientCloakedTCPSocket::DoPutWrite()
{
    HX_RESULT theErr = HXR_OK;

    if (m_bInDoPutWrite)
    {
	return HXR_OK;
    }

    m_bInDoPutWrite = TRUE;

    /* Transfer pending buffers to TCP send queue */
    if (m_bOptionsReceived && m_pPostEncodedSendHTTP->GetQueuedItemCount() == 0 && !m_bPutWantWritePending)
    {
	TransferBuffers();
    }

    if (!m_bPutConnectDone)
    {
	goto exit;
    }

    // check how data we have in TCP send queue
    UINT16 count;
    UINT16 actual;
    
    count    = m_pPostEncodedSendHTTP->GetQueuedItemCount();
    actual   = count;

    if(count > 0) 
    {
	m_pPostEncodedSendHTTP->DeQueue(m_pOutEncodedBuf,count);
	CHXBuffer* pBuffer = new CHXBuffer;
	pBuffer->AddRef();
	pBuffer->Set((UCHAR*) m_pOutEncodedBuf, (ULONG32) count);
	theErr = m_pPutCtrl->Write(pBuffer);
	pBuffer->Release();
	m_bPutWantWritePending	= TRUE;
	m_pPutCtrl->WantWrite();
    }
    
    if (theErr)
    {
	switch(theErr)
	{
	    case HXR_AT_INTERRUPT:			
	    case HXR_WOULD_BLOCK:
		m_pPostEncodedSendHTTP->EnQueue(m_pOutEncodedBuf, count);
		// mask out these errors
		theErr = HXR_OK;
		break;

	    default:
		theErr = ConvertNetworkError(theErr);
		break;
	}
    }

exit:

    if (!theErr && !m_bInDestructor &&
	(m_pPostEncodedSendHTTP->GetQueuedItemCount() > 0 ||
	 m_PendingWriteBuffers.GetCount() > 0)){
	    m_pSchedulerCallback->ScheduleCallback(CLOAKED_TCP_PUTWRITE_COMMAND, m_pScheduler, SCHED_GRANULARITY);
    }

    if (!theErr)
    {
	theErr = DoGetWrite();
    }

    m_bInDoPutWrite = FALSE;

    return theErr;
}

void 
HXClientCloakedTCPSocket::GetConnectDone(BOOL bResult)
{
    m_bGetConnectDone = TRUE;

    if (!m_bInDestructor)
    {
	AddRef();
    }

    if (bResult == TRUE)
    {
	m_bGetConnectSuccessful = TRUE;
	m_pGetCtrl->GetForeignAddress(m_lForeignAddress);
    }
    else
    {
	m_bGetConnectSuccessful = FALSE;
    }

    // PUT connection has to been done at this point
    if (m_bPutConnectDone && m_bConnectResponsePending)
    {
	m_bConnectResponsePending = FALSE;

	// either its HTTP-NG server or we are in re-GET mode
	// where we send GET to the same serverIP returned from
	// POST response
	if (m_bConnectToSameServerIP || m_bReconnectToSameServerIP)
	{
	    if (m_bPutConnectSuccessful && m_bGetConnectSuccessful)
	    {
		m_bConnected = TRUE;
		m_pMutex->Lock();
		PrepareGetMessage();
		DoGetWrite();
		Read(TCP_BUF_SIZE);
		m_pMutex->Unlock();
		if (m_bConnectToSameServerIP)
		{
		    // we haven't call ConnectDone yet
		    m_pTCPResponse->ConnectDone(HXR_OK);
		}
		else if (m_bReconnectToSameServerIP)
		{
		    // we already called ConnectDone upon the initial
		    // GET/POST connect done
		    Read(TCP_BUF_SIZE);
		}
	    }
	    else
	    {
		m_pTCPResponse->ConnectDone(HXR_NET_CONNECT);
	    }
	}
	// don't know the server type yet, keep the old behavior
	// where we send both GET and POST
	else
	{
	    if (m_bPutConnectSuccessful && m_bGetConnectSuccessful)
	    {
		m_bConnected = TRUE;
		m_pMutex->Lock();
		PreparePostMessage(NULL, 0);
		DoPutWrite();
		PrepareGetMessage();
		DoGetWrite();
		m_pMutex->Unlock();
		m_pTCPResponse->ConnectDone(HXR_OK);
	    }
	    else
	    {
		m_pTCPResponse->ConnectDone(HXR_NET_CONNECT);
	    }

	}
    }
	
    if (!m_bInDestructor)
    {
        Release();
    }
}

void 
HXClientCloakedTCPSocket::PutConnectDone(BOOL bResult)
{
    HX_RESULT theErr = HXR_OK;
    m_bPutConnectDone = TRUE;

    if (!m_bInDestructor)
    {
	AddRef();
    }

    if (bResult == TRUE)
    {
	m_bPutConnectSuccessful = TRUE;
    }
    else
    {
	m_bPutConnectSuccessful = FALSE;
    }

    // it's HTTP-NG server, we send POST first
    if (m_bConnectToSameServerIP)
    {
	if (m_bPutConnectSuccessful)
	{
	    m_pMutex->Lock();
	    PreparePostMessage(NULL, 0);
	    DoPutWrite();
	    if (!m_bPutReadPending)
	    {
		m_bPutReadPending = TRUE;
		m_pPutCtrl->Read(TCP_BUF_SIZE);
	    }
	    m_pMutex->Unlock();
	}
	else
	{
	    m_pTCPResponse->ConnectDone(HXR_NET_CONNECT);
	}
    }
    // don't know the server type yet, so we send both POST
    // and GET
    else if (m_bGetConnectDone && m_bConnectResponsePending)
    {
	m_bConnectResponsePending = FALSE;
	if (m_bPutConnectSuccessful && m_bGetConnectSuccessful)
	{
	    m_bConnected = TRUE;
	    m_pMutex->Lock();
	    PreparePostMessage(NULL, 0);
	    DoPutWrite();
	    PrepareGetMessage();
	    theErr = DoGetWrite();
	    m_pMutex->Unlock();
	    m_pTCPResponse->ConnectDone(HXR_OK);
	}
	else
	{
	    m_pTCPResponse->ConnectDone(HXR_NET_CONNECT);
	}
    }

    if (!m_bInDestructor)
    {
	Release();
    }

    if (m_bConnected && m_pPostEncodedSendHTTP->GetQueuedItemCount() > 0)
    {
	m_pMutex->Lock();
	DoPutWrite();
	m_pMutex->Unlock();
    }
}

HX_RESULT			
HXClientCloakedTCPSocket::DoGetReadDone(HX_RESULT   status, IHXBuffer* pInBuffer)
{
    HX_RESULT	theErr			= HXR_OK;
    char*	pStartAfterDoubleReturn = 0;
    UINT16	count = 0;

    if (AuthenticationRequired(status, pInBuffer))
    {
	return HXR_OK;
    }

    m_pMutex->Lock();

    m_bGetReadPending = FALSE;
    if (status != HXR_OK && m_LastError == HXR_OK)
    {
	m_LastError = status;
    }

    if (m_LastError)
    {
	goto exit;
    }

    if (pInBuffer)
    {
	m_pReceiveGetTCP->EnQueue(pInBuffer->GetBuffer(), (UINT16) pInBuffer->GetSize());
    }

    count = m_pReceiveGetTCP->GetQueuedItemCount();

    /*
     * if the 16k padding was not received in one big packet
     * delete the remaining padding
     */
    if (m_bDeletePadding && m_uPadLength > 0)
    {
	UINT16 len = (m_uPadLength <= count ? m_uPadLength : count); 
	BYTE* tmpBuf = new BYTE[len];
	
	m_pReceiveGetTCP->DeQueue(tmpBuf, len);
	m_uPadLength -= len;

	if (m_uPadLength == 0)
	{
	    m_bDeletePadding = FALSE;
	}
	HX_VECTOR_DELETE(tmpBuf);
	count = m_pReceiveGetTCP->GetQueuedItemCount();
    }

    if (!theErr && !m_bHTTPGetHeaderReadDone && count >= 2 )
    {
	char* pEndLineLocation = 0;

	if (!m_pHTTPHeaderBuffer || m_nHTTPHeaderBufferLength < count+1)
	{
	    HX_VECTOR_DELETE(m_pHTTPHeaderBuffer);
	    m_nHTTPHeaderBufferLength = count+1 > DEFAULT_HTTP_HEADER_LENGTH ?
					count+1 : DEFAULT_HTTP_HEADER_LENGTH;
	    m_pHTTPHeaderBuffer = new char[m_nHTTPHeaderBufferLength];
	    if (!m_pHTTPHeaderBuffer)
	    {
		theErr = HXR_OUTOFMEMORY;
		goto exit;
	    }
	}

	m_pReceiveGetTCP->DeQueue(m_pHTTPHeaderBuffer, count);
	m_pHTTPHeaderBuffer[count] = '\0';

	if ((pEndLineLocation = (char*) HXFindString(m_pHTTPHeaderBuffer, "\n\n")) ||
	    (pEndLineLocation = (char*) HXFindString(m_pHTTPHeaderBuffer, "\r\r")) )
	{
	    pStartAfterDoubleReturn = pEndLineLocation+2;
	    m_bHTTPGetHeaderReadDone = TRUE;
	}
	else if (count >= 4 &&
	    (pEndLineLocation = (char*) HXFindString(m_pHTTPHeaderBuffer, "\r\n\r\n")))
	{
	    pStartAfterDoubleReturn = pEndLineLocation+4;
	    m_bHTTPGetHeaderReadDone = TRUE;
	}
	else
	{
	    /* Put back everything in receive queue*/
	    m_pReceiveGetTCP->EnQueue(m_pHTTPHeaderBuffer, count);
	    goto exit;
	}

	// determine whether we need to issue re-GET to the same serverIP
	// from the POST response
	if (!m_bGetResponsed && !m_bConnectToSameServerIP)
	{
	    m_bGetResponsed = TRUE;
	    GetServerIPFromResponse(TRUE, m_pHTTPHeaderBuffer);

	    if (m_pszGetServerIP && m_pszPutServerIP)
	    {
		m_bReconnectToSameServerIP = strcasecmp(m_pszGetServerIP, m_pszPutServerIP);
	    }

	    if (m_bReconnectToSameServerIP)
	    {
		theErr = ReconnectToSameServerIP();
		goto quit;
	    }
	}

	/* If we have read the complete header, see if the content is OK..*/
	char* pFound = (char*) HXFindString(m_pHTTPHeaderBuffer, "HTTP/1.0 200 OK");
	if (!pFound)
	{
	    pFound = (char*) HXFindString(m_pHTTPHeaderBuffer, "200 OK");
	}
	
	// XXX HP we are phasing out PNM protocol
	// m_pCloakValues is only set in RTSP so we use this to exclude
	// support for redirect in PNM Cloaking
	if (!pFound)
	{
	    if (m_pCloakValues)
	    {
		pFound = (char*) HXFindString(m_pHTTPHeaderBuffer, "HTTP/1.0 302");
		if (pFound)
		{
		    strncpy(pFound, "RTSP", 4); /* Flawfinder: ignore */

		    IHXBuffer* pBuffer = new CHXBuffer;
		    pBuffer->AddRef();
		    pBuffer->Set((UCHAR*) m_pHTTPHeaderBuffer, count);
		    
		    m_pTCPResponse->ReadDone(HXR_OK, pBuffer);
		    pBuffer->Release();
		    goto exit;
		}
	    }

	    theErr = HXR_DOC_MISSING;
	}

	if (!theErr)
	{
	    m_pReceiveGetTCP->EnQueue(pStartAfterDoubleReturn, 
			    (m_pHTTPHeaderBuffer + count) - pStartAfterDoubleReturn);
	    
	    count = m_pReceiveGetTCP->GetQueuedItemCount();
	}
    }

    /* hmmm... time to read options */ 
    if (!theErr && count > 0 && !m_bOptionsReceived && m_bHTTPGetHeaderReadDone)
    {
	/* There needs to be atleast 4 bytes to have the response
	 * reponse_opcode(HTTP_RV_RESPONSE), response len, 
	 * actual response(minimum one byte)
	 */
	if (count < 3)
	{
	    goto exit;
	}

	m_pReceiveGetTCP->DeQueue(m_pInBuf, count);

	if (m_pInBuf[0] == HTTP_RESPONSE)
	{
	    m_bOptionsReceived = TRUE;
	    
	    UINT16 nOptionLen = m_pInBuf[1];
	    /* Place any data after the response back in Receive queue.
	     * This data needs to be sent to the user*/
	    if (count > 2+nOptionLen)
	    {
		m_pReceiveGetTCP->EnQueue(m_pInBuf+2+nOptionLen, (count - (2+nOptionLen)));
	    }

	    theErr = HandleHTTPResponse((UCHAR) m_pInBuf[2]);

	    if (!theErr)
	    {
		count = m_pReceiveGetTCP->GetQueuedItemCount();
	    }
	}
	else if (m_pInBuf[0] == HTTP_RV_RESPONSE)
	{
	    if (count < 9)
	    {
		m_pReceiveGetTCP->EnQueue(m_pInBuf, count);
		goto exit;
	    }
	    /*
	     * this involves receiving a message from the server of the format:
	     * HTTP/1.0 200 OK
	     * ...
	     * Content-length:2147483000
	     * r...O?...16k padding of ZEROs...e
	     *
	     * r -- HTTP_RV_RESPONSE (same as the 5.0 player)
	     * O -- HTTP_OPTION_RESPONSE
	     * e -- HTTP_OPT_RESPONSE_END
	     */
	    BYTE* ptr = (BYTE *)m_pInBuf;
	    m_bOptionsReceived = TRUE;
	    
	    ptr += 2;
	    UINT16 nOptionLen = *ptr;
	    ptr++;
	    
	    theErr = HandleHTTPResponse((UCHAR)*ptr);
	    ptr++;

	    if (!theErr)
	    {
		ptr++;  // get past the HTTP_OPTION_RESPONSE char 'O'
		m_uPadLength = (UINT16)getshort(ptr); 
		ptr += 2;

		// skip over the short padding option
		m_uPadLength += 1; // for the 'e' at the end of the 16k padding

		m_pReceiveGetTCP->EnQueue(ptr, 
		    (m_uPadLength <= (count - (ptr - (BYTE *)m_pInBuf)) 
		    ? m_uPadLength : (count - (ptr - (BYTE *)m_pInBuf))));

		count = m_pReceiveGetTCP->GetQueuedItemCount();

		// get rid of the padding sent with the first response
		UINT16 len = (m_uPadLength <= count ? m_uPadLength : count); 
		BYTE* tmpBuf = new BYTE[len];
		
		m_pReceiveGetTCP->DeQueue(tmpBuf, len);
		m_uPadLength -= len;

		if (m_uPadLength > 0)
		{
		    m_bDeletePadding = TRUE;
		}
		// if we got more than the 16k initial buffer, put it back
		if (len && len < count)
		    m_pReceiveGetTCP->EnQueue(&m_pInBuf[len], count-len);
		HX_VECTOR_DELETE(tmpBuf);
	    	count = m_pReceiveGetTCP->GetQueuedItemCount();
	    }
	}
	else
	{
	    theErr = HXR_BAD_SERVER;
	    goto exit;
	}

    }

    if (!theErr && m_bReadPending && count > 0 && m_bOptionsReceived 
	&& m_bHTTPGetHeaderReadDone)
    {
	/* mask any kind of errors */
	theErr = HXR_OK;

	if (IsSafe())
	{
	    m_bReadPending = FALSE;
	    if (m_nRequired < count)
	    {
		count = m_nRequired;
	    }

	    CHXBuffer* pBuffer = new CHXBuffer;
	    pBuffer->AddRef();
	    m_pReceiveGetTCP->DeQueue(m_pInBuf, count);
	    pBuffer->Set((UCHAR*) m_pInBuf, count);
	    
	    m_pTCPResponse->ReadDone(HXR_OK, pBuffer);
	    pBuffer->Release();
	}
    }

exit:
    if (theErr && !m_LastError)
    {
	m_LastError = theErr;
    }

    if (m_LastError)
    {
	if (m_bReadPending)
	{
	    if (IsSafe())
	    {
		m_bReadPending = FALSE;
		m_pTCPResponse->ReadDone(m_LastError, 0);
	    }
	}
    }

    if (!m_LastError)
    {
	DoPutWrite();
	DoRead();
    }

quit:
    m_pMutex->Unlock();

    return HXR_OK;
}

HX_RESULT			
HXClientCloakedTCPSocket::DoPutReadDone(HX_RESULT   status, IHXBuffer* pInBuffer)
{
    HX_RESULT	rc = HXR_OK;
    UINT16	count = 0;

    m_bPutReadPending = FALSE;

    if (HXR_OK != status)
    {
	rc = status;
	goto cleanup;
    }

    // stop processing either we already processed one PUT response or
    // we are in multi-post mode
    if (!m_bPutResponsed && !m_bMustCloseHTTP)
    {
	if (!m_pReceivePutTCP)
	{
	    m_pReceivePutTCP = new CByteGrowingQueue(QUEUE_START_SIZE,1);
	    if (!m_pReceivePutTCP || !m_pReceivePutTCP->IsQueueValid())
	    {
		rc = HXR_OUTOFMEMORY;
		goto cleanup;
	    }
	    m_pReceivePutTCP->SetMaxSize(TCP_BUF_SIZE);
	}

	if (pInBuffer)
	{
	    m_pReceivePutTCP->EnQueue(pInBuffer->GetBuffer(), (UINT16) pInBuffer->GetSize());
	}

	count = m_pReceivePutTCP->GetQueuedItemCount();

	if (count >= 2)
	{
	    char* pEndLineLocation = 0;
	    if (!m_pHTTPHeaderBuffer || m_nHTTPHeaderBufferLength < count+1)
	    {
		HX_VECTOR_DELETE(m_pHTTPHeaderBuffer);
		m_nHTTPHeaderBufferLength = count+1 > DEFAULT_HTTP_HEADER_LENGTH ?
					    count+1 : DEFAULT_HTTP_HEADER_LENGTH;
		m_pHTTPHeaderBuffer = new char[m_nHTTPHeaderBufferLength];
		if (!m_pHTTPHeaderBuffer)
		{
		    rc = HXR_OUTOFMEMORY;
		    goto cleanup;
		}
	    }

	    m_pReceivePutTCP->DeQueue(m_pHTTPHeaderBuffer, count);
	    m_pHTTPHeaderBuffer[count] = '\0';

	    if ((pEndLineLocation = (char*) HXFindString(m_pHTTPHeaderBuffer, "\n\n")) ||
    		(pEndLineLocation = (char*) HXFindString(m_pHTTPHeaderBuffer, "\r\r")) )
	    {
		m_bHTTPPutHeaderReadDone = TRUE;
	    }
	    else if (count >= 4 &&
		    (pEndLineLocation = (char*) HXFindString(m_pHTTPHeaderBuffer, "\r\n\r\n")))
	    {
		m_bHTTPPutHeaderReadDone = TRUE;
	    }
	    else
	    {
		/* Put back everything in receive queue*/
		m_pReceivePutTCP->EnQueue(m_pHTTPHeaderBuffer, count);
		goto cleanup;
	    }

	    m_bPutResponsed = TRUE;
	    GetServerIPFromResponse(FALSE, m_pHTTPHeaderBuffer);

	    if (m_bConnectToSameServerIP)
	    {
		rc = ActualConnect();
	    }
	    else
	    {
		if (m_pszPutServerIP && m_pPreferredTransport)
		{
		    m_pPreferredTransport->SetHTTPNG(TRUE);
		}

		// determine whether we need to issue re-GET to the same serverIP
		// from the POST response
		if (m_pszGetServerIP && m_pszPutServerIP)
		{
		    m_bReconnectToSameServerIP = strcasecmp(m_pszGetServerIP, m_pszPutServerIP);
		}

		if (m_bReconnectToSameServerIP)
		{
		    rc = ReconnectToSameServerIP();
		}
	    }
	}
    }

cleanup:

    return rc;	
}

void
HXClientCloakedTCPSocket::TransferBuffers()
{
    IHXBuffer* pBuffer = 0;
    HX_RESULT theErr = HXR_OK;

    if (m_bInTransferBuffers)
    {
	return;
    }

    m_bInTransferBuffers = TRUE;

    /*This is where we need to add POST header for multiple POST case*/

    while (m_PendingWriteBuffers.GetCount() > 0)
    {
	pBuffer = (IHXBuffer*) m_PendingWriteBuffers.GetHead();
	if ((UINT16) pBuffer->GetSize() < m_pPreEncodedSendHTTP->GetMaxAvailableElements())
	{
	    m_pPreEncodedSendHTTP->EnQueue(pBuffer->GetBuffer(), 
				(UINT16) pBuffer->GetSize());
	    pBuffer->Release();
	    m_PendingWriteBuffers.RemoveHead();
	}
	else
	{
	    break;
	}
    }

    UINT16 nCount = m_pPreEncodedSendHTTP->GetQueuedItemCount();
    if (nCount > 0)
    {
	if (m_bCloseHttpAfterWrite)
	{
	    ENQUEUE_BYTE(m_pPreEncodedSendHTTP, HTTP_POSTDONE);
	    nCount++;
	}

	if (m_bMustCloseHTTP && m_pPutCtrl)
	{
	    m_pPutCtrl->Release();
	    m_pPutCtrl = 0;
	    m_bPutConnectDone = FALSE;

	    m_pNetworkServices->CreateTCPSocket(&m_pPutCtrl);
	    if (!m_pPutCtrl)
	    {
		m_LastError = HXR_OUTOFMEMORY;
		m_bInTransferBuffers = FALSE;
		return;
	    }

	    m_pPutCtrl->Init(m_pPutCtrlResponse);

	    const char* pActualHost = m_pForiegnHost;
	    UINT16 	nActualPort = m_nForeignPort;
	    if (m_pProxyHostName)
	    {
		pActualHost = m_pProxyHostName;
		nActualPort = m_nProxyPortNumber;
	    }

	    m_pPutCtrl->Connect(pActualHost,nActualPort);
	}

	m_pPreEncodedSendHTTP->DeQueue(m_pOutBuf, nCount);
	UINT16 nEncodedCount = TCP_BUF_SIZE;
	EncodeBase64((UCHAR*) m_pOutBuf, nCount, (UCHAR*) m_pOutEncodedBuf, nEncodedCount);

        HX_ASSERT(nEncodedCount <= TCP_BUF_SIZE);

	if (m_bCloseHttpAfterWrite)
	{
	    theErr = PreparePostMessage((UCHAR*) m_pOutEncodedBuf, nEncodedCount);
	}
	else
	{
	    ENQUEUE_DATA(m_pPostEncodedSendHTTP,m_pOutEncodedBuf, nEncodedCount);
	}
    }

    m_bInTransferBuffers = FALSE;
}

/***************************General routines*******/

HX_RESULT
HXClientCloakedTCPSocket::PreparePostMessage(const UCHAR *inData, UINT16 inLength) 
{
    HX_RESULT theErr = HXR_OK;
    int count = 0;
    UINT16 postLength = inLength;
    
    // create a temp buffer to help build the HTTP POST message
    char* s = new char[MAX_HTTP_METHOD_BUFSIZE];
    if(s == NULL)
    {
	return HXR_OUTOFMEMORY;
    }
    // build the HTTP POST message
    if(m_pProxyHostName)
    {
	if (m_nForeignPort)
	{
	    count = SafeSprintf(s,MAX_HTTP_METHOD_BUFSIZE,"POST http://%s:%d/SmpDsBhgRl",m_pForiegnHost, m_nForeignPort);
	}
	else
	{
	    count = SafeSprintf(s,MAX_HTTP_METHOD_BUFSIZE,"POST http://%s/SmpDsBhgRl",m_pForiegnHost);
	}
    }
    else
    {
		//count = sprintf(s,"POST /SmpDsBhgRl"); /* Flawfinder: ignore */
		count = SafeSprintf(s,MAX_HTTP_METHOD_BUFSIZE,"POST /SmpDsBhgRl"); /* Flawfinder: ignore */
    }
    
    ENQUEUE_DATA(m_pPostEncodedSendHTTP,s,count);

    // enqueue the remainder of the POST line
    count = SafeSprintf(s,MAX_HTTP_METHOD_BUFSIZE," HTTP/1.0\r\n"); /* Flawfinder: ignore */
    ENQUEUE_DATA(m_pPostEncodedSendHTTP,s,count);

    count = SafeSprintf(s,MAX_HTTP_METHOD_BUFSIZE,"User-Agent: RealPlayer G2\r\n"); /* Flawfinder: ignore */
    ENQUEUE_DATA(m_pPostEncodedSendHTTP,s,count);

    count = SafeSprintf(s,MAX_HTTP_METHOD_BUFSIZE,"Pragma: no-cache\r\n"); /* Flawfinder: ignore */
    ENQUEUE_DATA(m_pPostEncodedSendHTTP,s,count);

    count = SafeSprintf(s,MAX_HTTP_METHOD_BUFSIZE,"Expires: Mon, 18 May 1974 00:00:00 GMT\r\n"); /* Flawfinder: ignore */
    ENQUEUE_DATA(m_pPostEncodedSendHTTP,s,count);

    count = SafeSprintf(s,MAX_HTTP_METHOD_BUFSIZE,"Accept: application/x-rtsp-tunnelled, */*\r\n"); /* Flawfinder: ignore */
    ENQUEUE_DATA(m_pPostEncodedSendHTTP,s,count);

    count = SafeSprintf(s,MAX_HTTP_METHOD_BUFSIZE,"Content-type: application/x-pncmd\r\n"); /* Flawfinder: ignore */
    ENQUEUE_DATA(m_pPostEncodedSendHTTP,s,count);

    CHXString strAuth;
    ObtainAuthenticationInformation(strAuth);
    
    if (!strAuth.IsEmpty())
    {
	strAuth += "\r\n";    
	ENQUEUE_DATA(m_pPostEncodedSendHTTP, (void*)(const char*)strAuth, 
		     (UINT16)strAuth.GetLength());
    }

    UINT16 guidSize = ::strlen(m_pGuid);
	
    if(m_bUseExactContentLength)
    {
	// add in the size of the GUID (which was not base64 encoded)
	postLength += guidSize + 2;	// 2 is for the \r\n at the end of the GUID!
	
	// we must report the exact size of the post data in the Content-length parameter
	count = SafeSprintf(s,MAX_HTTP_METHOD_BUFSIZE,"Content-length: %hu\r\n",postLength); /* Flawfinder: ignore */
    }
    else
    {
	count = SafeSprintf(s,MAX_HTTP_METHOD_BUFSIZE,"Content-length: 32767\r\n"); /* Flawfinder: ignore */
    }

    ENQUEUE_DATA(m_pPostEncodedSendHTTP,s,count);

    // enqueue the CR LF to indicate end of the POST header
    count = SafeSprintf(s,MAX_HTTP_METHOD_BUFSIZE,"\r\n"); /* Flawfinder: ignore */
    ENQUEUE_DATA(m_pPostEncodedSendHTTP,s,count);

    // enqueue the GUID (Must be sent with every POST and not base64 encoded)
    ENQUEUE_DATA(m_pPostEncodedSendHTTP,&m_pGuid[0],guidSize);
    
    // enqueue the CR LF to indicate end of the GUID
    count = SafeSprintf(s,MAX_HTTP_METHOD_BUFSIZE,"\r\n"); /* Flawfinder: ignore */
    ENQUEUE_DATA(m_pPostEncodedSendHTTP,s,count);

    if(inLength > 0)
    {
	// enqueue the actual POST data
	ENQUEUE_DATA(m_pPostEncodedSendHTTP,(char *)inData,inLength);
    }
    
    // clean up allocated buffers
    if(s)
    {
	delete [] s;
    }

    return theErr;
}

HX_RESULT
HXClientCloakedTCPSocket::PrepareGetMessage(void) 
{
    HX_RESULT	theErr = HXR_OK;
    IHXBuffer* pBuffer = NULL;

    // create a temp buffer for the HTTP GET message
    char* s = new char[MAX_HTTP_METHOD_BUFSIZE];
    
    if(s == NULL)
    {
	theErr = HXR_OUTOFMEMORY;
    }
    
    /* Flush any prior data in the send queue */
    m_pSendTCP->FlushQueue();

    /* Create a fresh GUID */
    CreateGuid();

    // format the HTTP GET message
    if(!theErr)
    {
	int count = 0;

	// build the HTTP POST message
	if(m_pProxyHostName)
	{   
	    if (m_nForeignPort)
	    {
		count = SafeSprintf(s,MAX_HTTP_METHOD_BUFSIZE,"GET http://%s:%d/SmpDsBhgRl",m_pForiegnHost, m_nForeignPort);
	    }
	    else
	    {
		count = SafeSprintf(s,MAX_HTTP_METHOD_BUFSIZE,"GET http://%s/SmpDsBhgRl",m_pForiegnHost);
	    }
	}
	else
	{
	    count = SafeSprintf(s,MAX_HTTP_METHOD_BUFSIZE,"GET /SmpDsBhgRl"); /* Flawfinder: ignore */
	}

	ENQUEUE_DATA(m_pSendTCP,s,count);
	
#if 0	// XXX HP enable this to test re-GET after initial GET/POST response 
	// returned with different serverIP
	if (!m_bReconnectToSameServerIP)
	{
	    char* pGuid = new char[strlen(m_pGuid)+1];
	    memcpy(pGuid, m_pGuid, strlen(m_pGuid)); /* Flawfinder: ignore */
	    pGuid[0] = 'z';
	    // enqueue the GUID directly after the SmpDsBhgRl tag
	    ENQUEUE_DATA(m_pSendTCP,&pGuid[0],::strlen(pGuid));
	    HX_VECTOR_DELETE(pGuid);
	}
	else
#endif
	{
	    // enqueue the GUID directly after the SmpDsBhgRl tag
	    ENQUEUE_DATA(m_pSendTCP,&m_pGuid[0],::strlen(m_pGuid));
	}

	if (m_pProxyHostName)
	{
	    /* 
	     * enqueue dummy option to tell the server to send a padding
	     * of 16k of ZEROs with the first response
	     */
	    count = SafeSprintf(s,MAX_HTTP_METHOD_BUFSIZE,"?1=\"1\""); /* Flawfinder: ignore */
	    ENQUEUE_DATA(m_pSendTCP,s,count);
	}

	// enqueue the HTTP 1.0 and CR LF
	count = SafeSprintf(s,MAX_HTTP_METHOD_BUFSIZE," HTTP/1.0\r\n"); /* Flawfinder: ignore */
	ENQUEUE_DATA(m_pSendTCP,s,count);

	count = SafeSprintf(s,MAX_HTTP_METHOD_BUFSIZE,"User-Agent: RealPlayer G2\r\n"); /* Flawfinder: ignore */
	ENQUEUE_DATA(m_pSendTCP,s,count);

	count = SafeSprintf(s,MAX_HTTP_METHOD_BUFSIZE,"Expires: Mon, 18 May 1974 00:00:00 GMT\r\n"); /* Flawfinder: ignore */
	ENQUEUE_DATA(m_pSendTCP,s,count);

	count = SafeSprintf(s,MAX_HTTP_METHOD_BUFSIZE,"Pragma: no-cache\r\n"); /* Flawfinder: ignore */
	ENQUEUE_DATA(m_pSendTCP,s,count);

	count = SafeSprintf(s,MAX_HTTP_METHOD_BUFSIZE,"Accept: application/x-rtsp-tunnelled, */*\r\n"); /* Flawfinder: ignore */
	ENQUEUE_DATA(m_pSendTCP,s,count);

	CHXString strAuth;
	ObtainAuthenticationInformation(strAuth);

	if (!strAuth.IsEmpty())
	{
	    strAuth += "\r\n";    
	    ENQUEUE_DATA(m_pSendTCP, (void*)(const char*)strAuth, 
			 (UINT16)strAuth.GetLength());
	}

	// send client information so that GoldPass Admin
	// can generate redirect URL via HTTPCloaking
	if (m_pCloakValues)
	{
	    if (HXR_OK == m_pCloakValues->GetPropertyCString("ClientID", pBuffer))
	    {
		UINT32 ulNewSize = pBuffer->GetSize()+25;
		s = (char*)realloc(s, ulNewSize);
                if(s)
                {
                    count = SafeSprintf(s,ulNewSize,"ClientID: %s\r\n", pBuffer->GetBuffer()); /* Flawfinder: ignore */
                    ENQUEUE_DATA(m_pSendTCP,s,count);
                }
                else
                {
                    theErr = HXR_OUTOFMEMORY;
                }
                
	    }
	    HX_RELEASE(pBuffer);

	    if (HXR_OK == m_pCloakValues->GetPropertyCString("Cookie", pBuffer))
	    {
		UINT32 ulNewSize = pBuffer->GetSize()+25;
		s = (char*)realloc(s, ulNewSize);
                if(s)
                {
                    count = SafeSprintf(s,ulNewSize,"Cookie: %s\r\n", pBuffer->GetBuffer()); /* Flawfinder: ignore */
                    ENQUEUE_DATA(m_pSendTCP,s,count);
                }
                else
                {
                    theErr = HXR_OUTOFMEMORY;
                }
            }
	    HX_RELEASE(pBuffer);

	    if (HXR_OK == m_pCloakValues->GetPropertyCString("url", pBuffer))
	    {
		UINT32 ulNewSize = pBuffer->GetSize()+25;
                s = (char*)realloc(s, ulNewSize);
                if(s)
                {
                    count = SafeSprintf(s,ulNewSize,"X-Actual-URL: %s\r\n", pBuffer->GetBuffer()); /* Flawfinder: ignore */
                    ENQUEUE_DATA(m_pSendTCP,s,count);
                }
                else
                {
                    theErr = HXR_OUTOFMEMORY;
                }
	    }
	    HX_RELEASE(pBuffer);
	}

	// enqueue the CR LF to indicate the end of the HTTP GET header
        s = (char*)realloc(s, 25);
        if(s)
        {
            count = SafeSprintf(s,25,"\r\n"); /* Flawfinder: ignore */
            ENQUEUE_DATA(m_pSendTCP,s,count);
        }
        else
        {
            theErr = HXR_OUTOFMEMORY;
        }
    }
    
    // clean up
    HX_DELETE(s);
    
    return theErr;
}

void
HXClientCloakedTCPSocket::CreateGuid(void)
{
    CHXuuid theGuid;
    uuid_tt uuid;
    
    if (m_pGuid)
    {
	return;
    }

    if(m_pGuid)
    {
	delete [] m_pGuid;
	m_pGuid = NULL;
    }
    
    // generate a new GUID
    HX_RESULT theErr = theGuid.GetUuid(&uuid);
//    HX_RESULT theErr = HXR_OK;

    if(!theErr)
    {
	CHXString theString;
	
	CHXuuid::HXUuidToString((const uuid_tt*)&uuid,&theString);
	
	int length = theString.GetLength();
	
	m_pGuid = new char[length + 1];
	
	if(!m_pGuid)
	{
		theErr = HXR_OUTOFMEMORY;
	}
	
	if(!theErr)
	{
		::strcpy(m_pGuid,(const char *)theString); /* Flawfinder: ignore */
		m_pGuid[length] = '\0';
	}
	
	if(theErr && m_pGuid)
	{
		delete [] m_pGuid;
		m_pGuid = NULL;
	}
    }
    
    if(theErr)  // use our own GUID generator
    {
	theErr = HXR_OK;
	ULONG32 temp = HX_GET_TICKCOUNT();

	if(m_pGuid)
	{
	    delete [] m_pGuid;
	    m_pGuid = NULL;
	}
    
	m_pGuid = new char[HXGUID_SIZE + 1];

	UINT16 length = SafeSprintf(m_pGuid,HXGUID_SIZE + 1,"%ld",temp);
	
	while(length < HXGUID_SIZE)
	{
		m_pGuid[length++] = '1';
	}
	
	m_pGuid[HXGUID_SIZE] = '\0';
    }
}

HX_RESULT 
HXClientCloakedTCPSocket::EncodeBase64(const UCHAR* inData, UINT16 inLength, UCHAR* outData, UINT16& outLength)
{
    HX_RESULT theErr = HXR_OK;
    
    HX_ASSERT(inData != NULL);
    HX_ASSERT(outData != NULL);
    HX_ASSERT(inLength != 0);
    
    // first base64 encode the buffer
    outLength = (UINT16) BinTo64((const UCHAR*) inData, (ULONG32) inLength,(char *)outData);

    HX_ASSERT(outLength >= inLength);

    return HXR_OK;
}


HX_RESULT
HXClientCloakedTCPSocket::HandleHTTPResponse(UCHAR response)
{
    HX_RESULT theErr = HXR_OK;
    
    switch(response)
    {
	case HTTP_OK:
	{
	    m_bUseExactContentLength = FALSE;
	    m_bCloseHttpAfterWrite = FALSE;
	    m_bHttpInitialized = TRUE;
	    /*force a write of the cached data to be sent to the server*/
//	    theErr = control_write();	
	}
	break;
		
	case POST_NOT_RECEIVED:		// POST message was not received
	{
	    if (m_pszGetServerIP)
	    {
		HX_VECTOR_DELETE(m_pForiegnHost);
		// use serverIP from GET response for multi-POST
		m_pForiegnHost = new char [strlen(m_pszGetServerIP) + 1];
		if (m_pForiegnHost)
		{
		    ::strcpy(m_pForiegnHost, m_pszGetServerIP); /* Flawfinder: ignore */
		}
		else
		{
		    theErr = HXR_OUTOFMEMORY;
		}
	    }
 
	    m_bUseExactContentLength = TRUE;
	    m_bCloseHttpAfterWrite = TRUE;
	    m_bHttpInitialized = TRUE;
	    m_bMustCloseHTTP = TRUE;

	    /*force a write of the cached data to be sent to the server*/
//	    theErr = control_write();
	}
	break;
		
	case INVALID_GUID: 			
	{
	    /* sent only if the GUID from the Player is already in use
	     * Need to regenerate GUID and send everything again
	     */
	    /// TBD - Rahul	    
	    if (m_pGuid)
	    {
		delete [] m_pGuid;
		m_pGuid = 0;
	    }

	    PrepareGetMessage();
	    theErr = DoGetWrite();
	}
	break;

	default:
	{
	    // shut this clip down and report an appropriate error
	    theErr = HXR_HTTP_CONTENT_NOT_FOUND;
	}
	break;
    }
    
    return theErr;
}

/* If we are at interrupt time and the response object is not interrupt safe,
 * schedule a callback to return back the data
 */
BOOL
HXClientCloakedTCPSocket::IsSafe()
{
    if (m_pInterruptState && m_pInterruptState->AtInterruptTime() && 
	(!m_pResponseInterruptSafe || 
	 !m_pResponseInterruptSafe->IsInterruptSafe()))
    {
	if (m_pNonInterruptCallback){
	    m_pNonInterruptCallback->ScheduleCallback(CLOAKED_TCP_READ_COMMAND, m_pScheduler, 0);
	}

	return FALSE;
    }

    return TRUE;
}

void
HXClientCloakedTCPSocket::FlushQueues(void)
{
    if (m_pSendTCP)
    {
	m_pSendTCP->FlushQueue();
    }

    if (m_pReceiveGetTCP)
    {
	m_pReceiveGetTCP->FlushQueue();
    }

    if (m_pReceivePutTCP)
    {
	m_pReceivePutTCP->FlushQueue();
    }

    if (m_pPreEncodedSendHTTP)
    {
	m_pPreEncodedSendHTTP->FlushQueue();
    }

    if (m_pPostEncodedSendHTTP)
    {
	m_pPostEncodedSendHTTP->FlushQueue();
    }
}

void			
HXClientCloakedTCPSocket::SendHTTPDone(void)
{
    CHXBuffer* pBuffer = new CHXBuffer;
    pBuffer->AddRef();

    BYTE http_done = HTTP_DONE;
    pBuffer->Set((UCHAR*) &http_done, 1);
    m_PendingWriteBuffers.AddTail((void*) pBuffer);

    TransferBuffers();
    DoPutWrite();
}

HX_RESULT
HXClientCloakedTCPSocket::ActualConnect(void)
{
    HX_RESULT	rc = HXR_OK;

    const char* pActualHost = m_pForiegnHost;
    UINT16 	nActualPort = m_nForeignPort;
    if (m_pProxyHostName)
    {
	pActualHost = m_pProxyHostName;
	nActualPort = m_nProxyPortNumber;
    }

    if (m_bConnectToSameServerIP)
    {
	if (m_bPutConnectSuccessful)
	{
	    rc = m_pGetCtrl->Connect(pActualHost,nActualPort);
	}
	else
	{
	    rc = m_pPutCtrl->Connect(pActualHost,nActualPort);
	}
	rc = ConvertNetworkError(rc);
    }
    else
    {
	rc = m_pGetCtrl->Connect(pActualHost,nActualPort);
	rc = ConvertNetworkError(rc);

	if (rc == HXR_OK && !m_bReconnectToSameServerIP)
	{
	    rc = m_pPutCtrl->Connect(pActualHost,nActualPort);
	    rc = ConvertNetworkError(rc);
	}
    }

    return rc;
}

HX_RESULT
HXClientCloakedTCPSocket::GetServerIPFromResponse(BOOL bGetResponse, const char* pszInBuffer)
{
    HX_RESULT	rc = HXR_OK;
    UINT8	nLength = 0;
    char*	pszServerIPStart = NULL;    
    char*	pszServerIPEnd = NULL;
    char*	pszServerIP = NULL;

    if (!pszInBuffer)
    {
	rc = HXR_FAILED;
	goto cleanup;
    }

    pszServerIPStart = (char*)HXFindString(pszInBuffer, "x-server-ip-address:");

    if (pszServerIPStart)
    {
	pszServerIPStart += strlen("x-server-ip-address:");

	// removing leading spaces
	while (*pszServerIPStart == ' ')
	{
	    pszServerIPStart++;
	}

	pszServerIPEnd = (char*)HXFindString(pszServerIPStart, "\r\n");	
	if (pszServerIPEnd)
	{
	    nLength = pszServerIPEnd - pszServerIPStart;

	    pszServerIP = new char[nLength + 1];
	    memset(pszServerIP, 0, nLength + 1);

	    strncpy(pszServerIP, pszServerIPStart, nLength); /* Flawfinder: ignore */

	    if (bGetResponse)
	    {
		HX_VECTOR_DELETE(m_pszGetServerIP);
		m_pszGetServerIP = pszServerIP;
	    }
	    else
	    {
		HX_VECTOR_DELETE(m_pszPutServerIP);
		m_pszPutServerIP = pszServerIP;
	    }
	}
    }

cleanup:

    return rc;
}


HX_RESULT
HXClientCloakedTCPSocket::CleanUpAndReInitializeStuff(void)
{
    HX_RESULT	rc = HXR_OK;
    IHXBuffer* pBuffer = NULL;

    m_pMutex->Lock();

    const char* pActualHost = m_pForiegnHost;
    UINT16 	nActualPort = m_nForeignPort;
    if (m_pProxyHostName)
    {
	pActualHost = m_pProxyHostName;
	nActualPort = m_nProxyPortNumber;
    }

    if (m_pReceiveGetTCP)
    {
	m_pReceiveGetTCP->FlushQueue();
    }

    /* Send a final HTTP done message */
    if (m_bHttpInitialized)
    {
	SendHTTPDone();
    }

    if (m_pSchedulerCallback)
    {
	m_pSchedulerCallback->Unschedule(m_pScheduler);
    }

    if (m_pNonInterruptCallback)
    {
	m_pNonInterruptCallback->Unschedule(m_pScheduler);
    }

    HX_RELEASE(m_pGetCtrl);
    HX_RELEASE(m_pPutCtrl);
    
    m_bHTTPGetHeaderReadDone = FALSE;
    m_bOptionsReceived = FALSE;
    m_bReadPending = FALSE;
    m_bGetReadPending = FALSE;
    m_bGetConnectDone = FALSE;
    m_bGetConnectSuccessful = FALSE;
    m_bConnectResponsePending = TRUE;
    m_bConnected = FALSE;

    m_bPutConnectDone = FALSE;
    m_bPutReadPending = FALSE;

    m_bInAuthenticationKludge = TRUE;
    
    // OK, clean up is done so now we re-initialize.
    
    
    // use serverIP from PUT response for GET reconnect
    
    if (HXR_OK != m_pNetworkServices->CreateTCPSocket(&m_pGetCtrl))
    {
	rc = HXR_FAILED;
	goto cleanup;
    }

    if (HXR_OK != m_pGetCtrl->Init(m_pGetCtrlResponse))
    {
	rc = HXR_FAILED;
	goto cleanup;
    }

    if (HXR_OK != m_pGetCtrl->Bind(HXR_INADDR_ANY, 0))
    {
	rc = HXR_FAILED;
	goto cleanup;
    }
    
    rc = m_pGetCtrl->Connect(pActualHost, nActualPort);    
    if (HXR_OK != rc)
    {
	goto cleanup;
    }

    if (HXR_OK != m_pNetworkServices->CreateTCPSocket(&m_pPutCtrl))
    {
	rc = HXR_FAILED;
	goto cleanup;
    }

    if (HXR_OK != m_pPutCtrl->Init(m_pPutCtrlResponse))
    {
	rc = HXR_FAILED;
	goto cleanup;
    }

    if (HXR_OK != m_pPutCtrl->Bind(HXR_INADDR_ANY, 0))
    {
	rc = HXR_FAILED;
	goto cleanup;
    }

    rc = m_pPutCtrl->Connect(pActualHost, nActualPort);
    if (HXR_OK != rc) goto cleanup;


cleanup:
    m_pMutex->Unlock();

    return rc;
}

HX_RESULT
HXClientCloakedTCPSocket::ReconnectToSameServerIP(void)
{
    HX_RESULT	rc = HXR_OK;
    IHXBuffer* pBuffer = NULL;

    m_pMutex->Lock();

    if (m_pReceiveGetTCP)
    {
	m_pReceiveGetTCP->FlushQueue();
    }

    /* Send a final HTTP done message */
    if (m_bHttpInitialized)
    {
	SendHTTPDone();
    }

    if (m_pSchedulerCallback)
    {
	m_pSchedulerCallback->Unschedule(m_pScheduler);
    }

    if (m_pNonInterruptCallback)
    {
	m_pNonInterruptCallback->Unschedule(m_pScheduler);
    }

    HX_RELEASE(m_pGetCtrl);
    HX_RELEASE(m_pGetCtrlResponse);
    
    m_bHTTPGetHeaderReadDone = FALSE;
    m_bOptionsReceived = FALSE;
    m_bReadPending = FALSE;
    m_bGetReadPending = FALSE;
    m_bGetConnectDone = FALSE;
    m_bGetConnectSuccessful = FALSE;
    m_bConnectResponsePending = TRUE;
    m_bConnected = FALSE;

    HX_VECTOR_DELETE(m_pForiegnHost);
    // use serverIP from PUT response for GET reconnect
    m_pForiegnHost = new char [strlen(m_pszPutServerIP) + 1];
    if (!m_pForiegnHost)
    {
	rc = HXR_OUTOFMEMORY;
	goto cleanup;
    }
    ::strcpy(m_pForiegnHost, m_pszPutServerIP); /* Flawfinder: ignore */
    
    if (HXR_OK != m_pNetworkServices->CreateTCPSocket(&m_pGetCtrl))
    {
	rc = HXR_FAILED;
	goto cleanup;
    }

    m_pGetCtrlResponse = new HTTPCloakTCPResponse(this, TRUE);
    if (!m_pGetCtrlResponse)
    {
	rc = HXR_FAILED;
	goto cleanup;
    }

    m_pGetCtrlResponse->AddRef();

    if (HXR_OK != m_pGetCtrl->Init(m_pGetCtrlResponse))
    {
	rc = HXR_FAILED;
	goto cleanup;
    }

    if (HXR_OK != m_pGetCtrl->Bind(HXR_INADDR_ANY, 0))
    {
	rc = HXR_FAILED;
	goto cleanup;
    }

    rc = ActualConnect();

cleanup:

    m_pMutex->Unlock();

    return rc;
}

BOOL
HXClientCloakedTCPSocket::AuthenticationRequired(HX_RESULT   status, IHXBuffer* pInBuffer)
{
    if (!pInBuffer)
    {
	return FALSE;
    }

    // start of authenticated proxy logic.

    HTTPParser Parser;
    char* pBufferContents = (char*)(const char*)pInBuffer->GetBuffer();
    ULONG32 nMsgLen = pInBuffer->GetSize();
    HTTPResponseMessage* pMessage = (HTTPResponseMessage*)Parser.parse( pBufferContents, nMsgLen );

    // ignore non-HTTP responses which will be processed by the response object:
    // RTSPClientProtocol
    if (HTTPMessage::T_UNKNOWN == pMessage->tag())
    {
	HX_DELETE(pMessage);
	return FALSE;
    }

    ULONG32 ulHTTPStatus = 0;
    if (strlen(pMessage->errorCode()) > 0)
    {
	ulHTTPStatus = atoi(pMessage->errorCode());
    }

    if (ulHTTPStatus == 401 || ulHTTPStatus == 407)
    {
#ifdef _MACINTOSH
	if (!IsMacInCooperativeThread())
	{
	    // xxxbobclark Since there's UI involved with authentication,
	    // we'll ensure that we're not at interrupt time.
	    if (m_pAuthenticationCallback && !m_pAuthenticationCallback->m_ulPendingCallbackID)
	    {
		m_pAuthenticationCallback->m_ulPendingCallbackID  =
			m_pScheduler->RelativeEnter(m_pAuthenticationCallback, 0);
		m_pAuthenticationCallback->m_Status = status;
		pInBuffer->AddRef();
		m_pAuthenticationCallback->m_pInBuffer = pInBuffer;
	    }

	    HX_DELETE(pMessage);
	    return TRUE;  
	}
#endif
	IHXRequest* pRequest = NULL;

	IHXCommonClassFactory* pCCF;
	HX_RESULT retVal = m_pContext->QueryInterface(IID_IHXCommonClassFactory, (void**)&pCCF);
	
	if (SUCCEEDED(retVal))
	{
	    retVal = pCCF->CreateInstance(CLSID_IHXRequest, (void**)&pRequest);

	    if(retVal == HXR_OK)
	    {
		PrepareGetMessage(); // set up m_pSendTCP

		UINT16 count    = m_pSendTCP->GetQueuedItemCount();
		m_pSendTCP->DeQueue(m_pOutBuf,count);

		retVal = pRequest->SetURL(m_pOutBuf);

		IHXKeyValueList* pResponseHeaders = NULL;

		pCCF->CreateInstance(CLSID_IHXKeyValueList, (void**)&pResponseHeaders);

		MIMEHeaderValue* pHeaderValue = NULL;
		MIMEHeader* pHeader = pMessage->getFirstHeader();
		while (pHeader)
		{
		    pHeaderValue = pHeader->getFirstHeaderValue();
		    CHXString strHeader;
		    while (pHeaderValue)
		    {
			CHXString strTemp;
			pHeaderValue->asString(strTemp);
			strHeader += strTemp;
			pHeaderValue = pHeader->getNextHeaderValue();
			if (pHeaderValue)
			{
			    strHeader += ", ";
			}
		    }
		    IHXBuffer* pBuffer = NULL;
		    CHXBuffer::FromCharArray((const char*)strHeader, &pBuffer);
		    pResponseHeaders->AddKeyValue(pHeader->name(), pBuffer);
		    HX_RELEASE(pBuffer);

		    pHeader = pMessage->getNextHeader();
		}

		IHXValues* pResponseValues = NULL;             

		if (HXR_OK == pResponseHeaders->QueryInterface(IID_IHXValues, (void**)&pResponseValues))
		{
		    pRequest->SetResponseHeaders(pResponseValues);
		}

		HandleAuthentication(pRequest, pMessage, m_pForiegnHost, m_pProxyHostName);

		HX_RELEASE(pResponseValues);
		HX_RELEASE(pResponseHeaders);

	    }

	    HX_RELEASE(pCCF);
	}
	HX_DELETE(pMessage);
	return TRUE;
    }
    HX_DELETE(pMessage);
    return FALSE;
}

#define CLOAKED_WWW_AUTHENTICATION_RECENT_KEY "authentication.http.realm.recent"
#define CLOAKED_PROXY_AUTHENTICATION_RECENT_KEY "proxy-authentication.http.realm.recent"

void
HXClientCloakedTCPSocket::ObtainAuthenticationInformation(CHXString& strAuth)
{
    IHXBuffer* pBuffer = NULL;

    CHXString key("no-authentication-information");

    CHXString recentAuthRealmInfo;
    CHXString recentProxyAuthRealmInfo;

    IHXBuffer* pHeaderBuffer = NULL;
    
    HX_RESULT theErr = HXR_OK;

    IHXRegistry* pRegistry = NULL;
    
    m_pContext->QueryInterface(IID_IHXRegistry, (void**)&pRegistry);
    HX_ASSERT(pRegistry);
    
    if (!pRegistry) return;
    
    theErr = pRegistry->GetStrByName(CLOAKED_WWW_AUTHENTICATION_RECENT_KEY, pHeaderBuffer);
    if (SUCCEEDED(theErr))
    {
	HX_ASSERT(pHeaderBuffer);
	recentAuthRealmInfo = CHXString((const char*)pHeaderBuffer->GetBuffer(), pHeaderBuffer->GetSize());
	HX_RELEASE(pHeaderBuffer);
    }

    theErr = pRegistry->GetStrByName(CLOAKED_PROXY_AUTHENTICATION_RECENT_KEY, pHeaderBuffer);
    if (SUCCEEDED(theErr))
    {
	HX_ASSERT(pHeaderBuffer);
	recentProxyAuthRealmInfo = CHXString((const char*)pHeaderBuffer->GetBuffer(), pHeaderBuffer->GetSize());
	HX_RELEASE(pHeaderBuffer);
    }

    key = "proxy-authentication.http:";
    key += m_pProxyHostName;
    key += ":";
    key += recentProxyAuthRealmInfo;

    if (HXR_OK == pRegistry->GetStrByName((const char*)key, pBuffer) )
    {
	if (pBuffer)
	{
	    CHXString authString((const char*)pBuffer->GetBuffer(), pBuffer->GetSize());

	    strAuth = "Proxy-Authorization: ";
	    strAuth += (const char*)authString;
	}
    }
    HX_RELEASE(pBuffer);

    HX_RELEASE(pRegistry);
}

HX_RESULT
HXClientCloakedTCPSocket::HandleAuthentication(IHXRequest* pRequest, HTTPResponseMessage* pMessage,
		const char* pHost, const char* pProxyHost)
{
    HX_RESULT   ResultStatus = HXR_OK;
    UINT32      ulAltURL = 0;
    CHXString   sConnection;
    IHXValues* pNewHeaders = NULL;


    // xxxbobclark The reason we need to extract the IHXPlayer is that
    // later on the authenticator smart pointer needs a context which knows
    // about authentication conversations. this->m_pContext does not know
    // about the authentication conversation. That's why we have to iterate
    // through the players looking for someone. Sheesh.

    if (!pRequest)
    {
        return HXR_UNEXPECTED;
    }

    HX_RESULT retVal = HXR_OK;
    IHXRegistry* pRegistry = NULL;
    retVal = m_pContext->QueryInterface(IID_IHXRegistry, (void**)&pRegistry);
    if (SUCCEEDED(retVal))
    {
	IHXCommonClassFactory* pCCF;
	retVal = m_pCloakContext->QueryInterface(IID_IHXCommonClassFactory, (void**)&pCCF);
	
	if (SUCCEEDED(retVal))
	{
	    IHXValues* pResponseHeaders = NULL;
	    
	    HX_ASSERT(pRequest);
	    if (HXR_OK == pRequest->GetResponseHeaders(pResponseHeaders))
	    {
		IHXBuffer* pServerHeaderBuffer = NULL;

		HX_ASSERT(pHost);
		if (pHost)
		{
		    retVal = pCCF->CreateInstance(CLSID_IHXBuffer,
		    		(void**)&pServerHeaderBuffer);
		    if (SUCCEEDED(retVal))
		    {
			UINT32 ulHTTPStatus = atoi(pMessage->errorCode());
			if (ulHTTPStatus == 407 && pProxyHost)
			{
			    pServerHeaderBuffer->Set((UCHAR*)pProxyHost, strlen(pProxyHost)+1);
			}
			else
			{
			    pServerHeaderBuffer->Set((UCHAR*)pHost, strlen(pHost)+1);
			}
			pResponseHeaders->SetPropertyCString("_server", pServerHeaderBuffer);
			HX_RELEASE(pServerHeaderBuffer);
		    }
		}

		    // Add the protocol to the response headers because TLC needs it
		    IHXBuffer* pProtocol = NULL;
		    if (SUCCEEDED(pCCF->CreateInstance(CLSID_IHXBuffer, (void**)&pProtocol)))
		    {
			pProtocol->Set((UCHAR*)"http", strlen("http") + 1);
			pResponseHeaders->SetPropertyCString("_protocol", pProtocol);
			HX_RELEASE(pProtocol);
		    }
	    }

	    if (!spClientAuthConversationAuthenticator.IsValid())
	    {
		DECLARE_SMART_POINTER_UNKNOWN spUnknownAuthenticator;
		DECLARE_SMART_POINTER
			(
			IHXObjectConfiguration
			) spObjectConfigurationAuthenticator;
		DECLARE_SMART_POINTER
			(
			IHXCommonClassFactory
			) spCommonClassFactoryHXCore;

		spCommonClassFactoryHXCore = m_pCloakContext;

		// Starting conversation
		ResultStatus = spCommonClassFactoryHXCore->CreateInstance
					(
					CLSID_CHXClientAuthenticator,
					(void**)&spUnknownAuthenticator
					);

		if ( SUCCEEDED(ResultStatus) && spUnknownAuthenticator.IsValid() )
		{
		    spObjectConfigurationAuthenticator = 
			(
			spUnknownAuthenticator
			);

		    spObjectConfigurationAuthenticator->SetContext(m_pCloakContext);

		    spClientAuthConversationAuthenticator = 
			(
			spUnknownAuthenticator
			);
		}
	    }

	    if ( spClientAuthConversationAuthenticator.IsValid()
			&& !spClientAuthConversationAuthenticator->IsDone() )
	    {
		HX_ASSERT(pRequest);
		if (pRequest)
		{
		    ResultStatus = 
			(
			spClientAuthConversationAuthenticator->MakeResponse
				(
				this,
				pRequest
				)
			);

		// Flow continues in ResponseReady()
		}
		else
		{
		    // Auth Failed!
		    spClientAuthConversationAuthenticator->Authenticated(FALSE);
		    ResponseReady(HXR_NOT_AUTHORIZED, pRequest);
		}
	    }

	    HX_RELEASE(pCCF);
	}
	
	HX_RELEASE(pRegistry);
    }
    

    return ResultStatus;
}

// IHXClientAuthResponse
STDMETHODIMP 
HXClientCloakedTCPSocket::ResponseReady( HX_RESULT ResultStatus, IHXRequest* pRequestResponse)
{
    // now if it's an OK result, then I can use pRequestResponse in a new get.

    if (FAILED(ResultStatus))
    {
	return ResultStatus;
    }

    IHXValues* pHeaders = NULL;

    HX_ASSERT(pRequestResponse);
    if (HXR_OK == pRequestResponse->GetRequestHeaders(pHeaders))
    {
	const char* pName;
	IHXBuffer* pBuf;
	HX_RESULT res = pHeaders->GetFirstPropertyCString(pName, pBuf);
	while (res == HXR_OK)
	{

	    if (!strcasecmp(pName, "Proxy-Authorization"))
	    {
		HX_RESULT retVal = HXR_OK;
		IHXRegistry* pRegistry = NULL;
		retVal = m_pContext->QueryInterface(IID_IHXRegistry, (void**)&pRegistry);

		IHXCommonClassFactory* pCCF = NULL;

		if (m_pContext)
		{
		    m_pContext->AddRef();

		    // get the CCF
		    m_pContext->QueryInterface(IID_IHXCommonClassFactory, (void**)&pCCF);
		}
	
		if (SUCCEEDED(retVal))
		{
		    IHXBuffer* pBuffer = NULL;
		    retVal = pCCF->CreateInstance(CLSID_IHXBuffer, (void**)&pBuffer);
		    UINT32 regid = 0;
	    
		    HX_ASSERT(SUCCEEDED(retVal));
		    if (SUCCEEDED(retVal))
		    {
			IHXBuffer* pHeaderBuffer = NULL;

			CHXString key;
			CHXString recentRealmInfo = "";

			if (!strcasecmp(pName, "Proxy-Authorization"))
			{
			    key = "proxy-authentication.http:";
			    retVal = pRegistry->GetStrByName(CLOAKED_PROXY_AUTHENTICATION_RECENT_KEY,
				pHeaderBuffer);

			    if (SUCCEEDED(retVal))
			    {
				HX_ASSERT(pHeaderBuffer);
				recentRealmInfo = CHXString((const char*)pHeaderBuffer->GetBuffer(), pHeaderBuffer->GetSize());
				HX_RELEASE(pHeaderBuffer);
			    }

			key += m_pProxyHostName;
			}

			key += ":";
			key += recentRealmInfo;

			HX_ASSERT(!key.IsEmpty());
			pBuffer->Set(pBuf->GetBuffer(), pBuf->GetSize());

			regid = pRegistry->GetId((const char*)key);
			if (!regid)
			{
			    pRegistry->AddStr((const char*)key, pBuffer);
			}
			else
			{
			    pRegistry->SetStrByName((const char*)key, pBuffer);
			}

			HX_RELEASE(pBuffer);
			HX_RELEASE(pHeaderBuffer);
		    }
		}
		HX_RELEASE(pCCF);
	    }
	    HX_RELEASE(pBuf);

	    res = pHeaders->GetNextPropertyCString(pName, pBuf);
	}
    }

    CleanUpAndReInitializeStuff();

    return HXR_OK;
}

STDMETHODIMP
HXClientCloakedTCPSocket::HandleCallback(INT32	theCommand, HX_RESULT theError)
{
    if (!m_bInDestructor)
    {
	m_pMutex->Lock();

	DoGetWrite();
	DoPutWrite();
	DoRead();

	m_pMutex->Unlock();
    }

    return HXR_OK;
}

HXClientCloakedTCPSocket::HTTPCloakTCPResponse::HTTPCloakTCPResponse(HXClientCloakedTCPSocket* pOwner, BOOL bIsRead) :
     m_pOwner(pOwner)
    ,m_lRefCount(0) 
    ,m_bIsRead(bIsRead)
{
}

HXClientCloakedTCPSocket::HTTPCloakTCPResponse::~HTTPCloakTCPResponse()
{
}

/*
 *  IUnknown methods
 */

/////////////////////////////////////////////////////////////////////////
//  Method:
//	IUnknown::QueryInterface
//  Purpose:
//	Implement this to export the interfaces supported by your 
//	object.
//
STDMETHODIMP HXClientCloakedTCPSocket::HTTPCloakTCPResponse::QueryInterface(REFIID riid, void** ppvObj)
{
    QInterfaceList qiList[] =
        {
            { GET_IIDHANDLE(IID_IHXTCPResponse), (IHXTCPResponse*)this },
            { GET_IIDHANDLE(IID_IHXInterruptSafe), (IHXInterruptSafe*)this },
            { GET_IIDHANDLE(IID_IUnknown), (IUnknown*)(IHXTCPResponse*)this },
        };
    
    return ::QIFind(qiList, QILISTSIZE(qiList), riid, ppvObj);
}

/////////////////////////////////////////////////////////////////////////
//  Method:
//	IUnknown::AddRef
//  Purpose:
//	Everyone usually implements this the same... feel free to use
//	this implementation.
//
STDMETHODIMP_(ULONG32) HXClientCloakedTCPSocket::HTTPCloakTCPResponse::AddRef()
{
    return InterlockedIncrement(&m_lRefCount);
}

/////////////////////////////////////////////////////////////////////////
//  Method:
//	IUnknown::Release
//  Purpose:
//	Everyone usually implements this the same... feel free to use
//	this implementation.
//
STDMETHODIMP_(ULONG32) HXClientCloakedTCPSocket::HTTPCloakTCPResponse::Release()
{
    if (InterlockedDecrement(&m_lRefCount) > 0)
    {
        return m_lRefCount;
    }

    delete this;
    return 0;
}


/*
 *	IHXTCPResponse methods
 */

STDMETHODIMP HXClientCloakedTCPSocket::HTTPCloakTCPResponse::ConnectDone(HX_RESULT status)
{

    // xxxbobclark this authentication kludge is to ensure that layers "above" me
    // never have to care whether they're using an authenticated proxy or not.
    // So since this cloaked socket has already called its owner with ConnectDone
    // messages, it won't resend those... rather, it will reconstruct the same state
    // that the above layer expects.
    // So we set it up so the next ReadDone will work through the proxy and pass
    // through to the above layer and everything should be cool.

    if (m_pOwner->m_bInAuthenticationKludge)
    {
	m_pOwner->m_bInAuthenticationKludge = FALSE;

	if (m_bIsRead)
	{
	    m_pOwner->PreparePostMessage(NULL, 0);
	    m_pOwner->DoPutWrite();

	    m_pOwner->PrepareGetMessage();
	    m_pOwner->DoGetWrite();
	}

	m_pOwner->Read(4096);


	m_pOwner->m_bConnected = TRUE;
	return HXR_OK;
    }

    if (m_bIsRead)
    {
	m_pOwner->GetConnectDone(status == HXR_OK ? TRUE : FALSE);/*m_pGetCtrl*/
    }
    else
    {
	m_pOwner->PutConnectDone(status == HXR_OK ? TRUE : FALSE);/*m_pPutCtrl*/
    }

    return HXR_OK;
}


STDMETHODIMP HXClientCloakedTCPSocket::HTTPCloakTCPResponse::ReadDone(	HX_RESULT   status,
					IHXBuffer* pBuffer)
{
    if (m_bIsRead)
    {
	m_pOwner->DoGetReadDone(status, pBuffer); /*m_pGetCtrl*/
    }
    else 
    {
	m_pOwner->DoPutReadDone(status, pBuffer); /*m_pPutCtrl*/
    }

    return HXR_OK;
}

STDMETHODIMP HXClientCloakedTCPSocket::HTTPCloakTCPResponse::WriteReady(HX_RESULT status)
{
    if (m_bIsRead)
    {
	m_pOwner->m_pMutex->Lock();
	m_pOwner->DoGetWrite(); /*m_pGetCtrl*/
	m_pOwner->m_pMutex->Unlock();
    }
    else
    {
	m_pOwner->m_bPutWantWritePending = FALSE;
	m_pOwner->m_pMutex->Lock();
	m_pOwner->DoPutWrite(); /*m_pPutCtrl*/
	m_pOwner->m_pMutex->Unlock();
    }

    return HXR_OK;
}

STDMETHODIMP HXClientCloakedTCPSocket::HTTPCloakTCPResponse::Closed(HX_RESULT	status)
{
    return HXR_OK;
}

#ifdef _MACINTOSH

HXClientCloakedTCPSocket::MacCloakedTCPSocketAuthenticationCallback::MacCloakedTCPSocketAuthenticationCallback (
	HXClientCloakedTCPSocket* pSocket)
    : m_lRefCount(0)
    , m_ulPendingCallbackID(NULL)
    , m_pSocket(pSocket)
    , m_Status(HXR_OK)
    , m_pInBuffer(NULL)
{
}

HXClientCloakedTCPSocket::MacCloakedTCPSocketAuthenticationCallback::~MacCloakedTCPSocketAuthenticationCallback()
{
}

STDMETHODIMP
HXClientCloakedTCPSocket::MacCloakedTCPSocketAuthenticationCallback::QueryInterface(
						REFIID riid, void** ppvObj)
{
    QInterfaceList qiList[] =
        {
            { GET_IIDHANDLE(IID_IHXCallback), (IHXCallback*)this },
            { GET_IIDHANDLE(IID_IUnknown), (IUnknown*)(IHXCallback*)this },
        };
    
    return ::QIFind(qiList, QILISTSIZE(qiList), riid, ppvObj);
}

STDMETHODIMP_(ULONG32)
HXClientCloakedTCPSocket::MacCloakedTCPSocketAuthenticationCallback::AddRef()
{
    return InterlockedIncrement( &m_lRefCount );
}

STDMETHODIMP_(ULONG32)
HXClientCloakedTCPSocket::MacCloakedTCPSocketAuthenticationCallback::Release()
{
    if ( InterlockedDecrement( &m_lRefCount ) > 0 )
    {
	return m_lRefCount;
    }
    
    delete this;
    return 0;
}

STDMETHODIMP
HXClientCloakedTCPSocket::MacCloakedTCPSocketAuthenticationCallback::Func()
{
    m_ulPendingCallbackID = NULL;
    
    if (m_pInBuffer)
    {
	m_pSocket->DoGetReadDone(m_Status, m_pInBuffer);
	HX_RELEASE(m_pInBuffer);
    }
    
    return HXR_OK;
}

#endif

