/* ***** BEGIN LICENSE BLOCK *****
 * Source last modified: $Id: rtspclnt.cpp,v 1.63.2.9 2004/12/01 23:56:37 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 "hxcom.h"
#if defined _UNIX
#if defined _SOLARIS || defined _IRIX || defined _FREEBSD || defined _OPENBSD || defined _NETBSD
#include <sys/types.h>
#endif
#include <sys/socket.h>
#include <netinet/in.h>
#ifndef _BEOS
#include <arpa/inet.h>
#endif
#endif

#include <stdlib.h>
#include "hxtypes.h"
#include "timeval.h"
#include "hxstring.h"
#include "hxslist.h"
#include "hxmarsh.h"
#include "hxtick.h"
#include "netbyte.h"
#include "hxengin.h"
#include "hxcore.h"
#include "hxpnets.h"
#include "ihxpckts.h"
#include "hxcomm.h"
#include "hxprefs.h"
#include "hxpref.h"
#include "hxplugn.h"
#include "hxencod.h"
#include "hxrsdbf.h"
#include "plghand2.h"
#ifdef HELIX_FEATURE_SERVER
#include "plgnhand.h"
#endif
#include "hxplugn.h"
#include "hxsdesc.h"
#include "netbyte.h"
#include "chxpckts.h"
#include "asmrulep.h"
#include "growingq.h"
#include "mimehead.h"
#include "mimescan.h"
#include "timerep.h"
#include "hxthread.h"
#include "rtspmsg.h"
#include "rtsppars.h"
#include "rtspmdsc.h"
#include "basepkt.h"
#include "servrsnd.h"
#include "portaddr.h"
#include "chxkeepalive.h"
#include "rtspclnt.h"
#include "rtsputil.h"
#include "sdptools.h"
#include "hxurl.h"
#include "hxstrutl.h"
#include "trmimemp.h"
#include "rtptypes.h"
#include "stream_desc_hlpr.h"

#include "hxheap.h"
#ifdef _DEBUG
#undef HX_THIS_FILE
static const char HX_THIS_FILE[] = __FILE__;
#endif

#define RN_COMMON_MIME_TYPE_FRAGMENT	"x-pn-"

#ifndef BUFFER_DEPTH_UNDEFINED
#define BUFFER_DEPTH_UNDEFINED 0xffffffff
#endif

#define MULTICAST_ADDRESS_RANGE_LOW     3758096384  // 224.0.0.0
#define MULTICAST_ADDRESS_RANGE_HIGH    4026531839  // 239.255.255.255

#define DEFAULT_SERVER_TIMEOUT          90          // in seconds
#define MINIMUM_TIMEOUT                 5           // in seconds

const RTSPTableEntry RTSPClientProtocol::zm_pRTSPTable[RTSP_TABLE_SIZE] =
{
    { "SETUP",		SETUP	    },
    { "REDIRECT",	REDIRECT    },
    { "PLAY",		PLAY	    },
    { "PAUSE",		PAUSE	    },
    { "SET_PARAMETER",	SET_PARAM   },
    { "GET_PARAMETER",	GET_PARAM   },
    { "OPTIONS",	OPTIONS	    },
    { "DESCRIBE",	DESCRIBE    },
    { "TEARDOWN",	TEARDOWN    },
    { "RECORD",		RECORD	    },
    { "ANNOUNCE",	ANNOUNCE    }
};

/*
 * RTSPTransportInfo methods
 */

RTSPTransportInfo::RTSPTransportInfo():
    m_pTransport(0),
    m_pRTCPTransport(0)
{
}

RTSPTransportInfo::~RTSPTransportInfo()
{
    if(m_pTransport)
    {
	m_pTransport->Done();
    }

    if(m_pRTCPTransport)
    {
	m_pRTCPTransport->Done();
    }

    HX_RELEASE(m_pTransport);
    HX_RELEASE(m_pRTCPTransport);
}

void
RTSPTransportInfo::addStreamNumber(UINT16 streamNumber)
{
    m_streamNumberList.AddTail((void*)streamNumber);
}

BOOL
RTSPTransportInfo::containsStreamNumber(UINT16 streamNumber)
{
    CHXSimpleList::Iterator i;
    for(i=m_streamNumberList.Begin();i!=m_streamNumberList.End();++i)
    {
	UINT16 sNumber = (UINT16)(PTR_INT)(*i);
	if(sNumber == streamNumber)
	{
	    return TRUE;
	}
    }
    return FALSE;
}


/*
 * RTSPTransportRequest methods
 */

RTSPTransportRequest::RTSPTransportRequest(RTSPTransportTypeEnum tType,
    UINT16 sPort):
	m_lTransportType(tType),
	m_sPort(sPort),
	m_bDelete(FALSE)
{
}

RTSPTransportRequest::~RTSPTransportRequest()
{
    CHXSimpleList::Iterator i;
    for(i=m_transportInfoList.Begin();i!=m_transportInfoList.End();++i)
    {
	RTSPTransportInfo* pInfo = (RTSPTransportInfo*)(*i);
	delete pInfo;
    }
}

RTSPTransportInfo*
RTSPTransportRequest::getTransportInfo(UINT16 streamNumber)
{
    CHXSimpleList::Iterator i;
    for(i=m_transportInfoList.Begin();i!=m_transportInfoList.End();++i)
    {
	RTSPTransportInfo* pInfo = (RTSPTransportInfo*)(*i);
	if(pInfo->containsStreamNumber(streamNumber))
	{
	    return pInfo;
	}
    }
    return 0;
}

RTSPTransportInfo*
RTSPTransportRequest::getFirstTransportInfo()
{
    m_lListPos = m_transportInfoList.GetHeadPosition();
    if(m_lListPos)
    {
	return (RTSPTransportInfo*)m_transportInfoList.GetNext(m_lListPos);
    }
    return 0;
}

RTSPTransportInfo*
RTSPTransportRequest::getNextTransportInfo()
{
    if(m_lListPos)
    {
	return (RTSPTransportInfo*)m_transportInfoList.GetNext(m_lListPos);
    }
    return 0;
}

HX_RESULT
RTSPTransportRequest::addTransportInfo(RTSPTransport* pTrans,
    RTCPBaseTransport* pRTCPTrans, UINT16 streamNumber, UINT16 sPort)
{
    HX_RESULT retVal = HXR_OK;
    RTSPTransportInfo* pTransInfo = new RTSPTransportInfo;
    if(pTransInfo)
    {
        pTransInfo->m_pTransport = pTrans;	// already AddRef'd
        pTransInfo->m_pRTCPTransport = pRTCPTrans;
        pTransInfo->addStreamNumber(streamNumber);
        pTransInfo->m_sPort = sPort;

        LISTPOSITION listRet = m_transportInfoList.AddTail(pTransInfo);
        if( listRet == NULL )
        {
            HX_DELETE(pTransInfo);
            retVal = HXR_OUTOFMEMORY;
        }
    }
    else
    {
        retVal = HXR_OUTOFMEMORY;
    }
    return retVal;
}


// static initializations
RTSPClientSessionManagerType RTSPClientSessionManager::zm_pSessionManager = 0;

/*
 * RTSPClientProtocol methods
 */


/*
 * IUnknown methods
 */

STDMETHODIMP
RTSPClientProtocol::QueryInterface(REFIID riid, void** ppvObj)
{
    QInterfaceList qiList[] =
    {
	{ GET_IIDHANDLE(IID_IUnknown), this },
	{ GET_IIDHANDLE(IID_IHXPendingStatus), (IHXPendingStatus*) this },
	{ GET_IIDHANDLE(IID_IHXStatistics), (IHXStatistics*) this },
	{ GET_IIDHANDLE(IID_IHXResolverResponse), (IHXResolverResponse*) this },
	{ GET_IIDHANDLE(IID_IHXInterruptSafe), (IHXInterruptSafe*) this },
	{ GET_IIDHANDLE(IID_IHXResendBufferControl), (IHXResendBufferControl*) this },
	{ GET_IIDHANDLE(IID_IHXThinnableSource), (IHXThinnableSource*) this },
	{ GET_IIDHANDLE(IID_IHXTransportSyncServer), (IHXTransportSyncServer*) this },
	{ GET_IIDHANDLE(IID_IHXTransportBufferLimit), (IHXTransportBufferLimit*) this },
    };
    if(QIFind(qiList, QILISTSIZE(qiList), riid, ppvObj) == HXR_OK)
    {
	return HXR_OK;
    }
    else if (m_pTransportStreamMap &&
	     !m_pTransportStreamMap->IsEmpty() &&
	     ((void *)((*m_pTransportStreamMap)[0])) &&
	     (HXR_OK == ((RTSPTransport*)(*m_pTransportStreamMap)[0])->
		QueryInterface(riid, ppvObj)))
    {
	return HXR_OK;
    }
    *ppvObj = NULL;
    return HXR_NOINTERFACE;
}

STDMETHODIMP_(UINT32)
RTSPClientProtocol::AddRef()
{
    return InterlockedIncrement(&m_lRefCount);
}

STDMETHODIMP_(UINT32)
RTSPClientProtocol::Release()
{
    if (InterlockedDecrement(&m_lRefCount) > 0)
    {
        return m_lRefCount;
    }

    delete this;
    return 0;
}

/*
 * RTSPClientProtocol methods
 */

RTSPClientProtocol::RTSPClientProtocol():
    m_foreignPort(0),
    m_pTransportStreamMap(0),
    m_pTransportPortMap(0),
    m_pTransportMPortMap(0),
    m_pTransportChannelMap(0),
    m_pControlToStreamNoMap(0),
    m_foreignAddr(0),
    m_ulConnectToAddr(0),
    m_lRefCount(0),
    m_pResp(0),
    m_pFileHeader(0),
    m_bSetupRecord(FALSE),
    m_setupResponseCount(0),
    m_pSessionHeaders(0),
    m_pResponseHeaders(0),
    m_pCloakValues(NULL),
    m_pSession(0),
    m_pRegistry(0),
    m_bClientDone(FALSE),
    m_bMessageDebug(FALSE),
    m_bUseProxy(FALSE),
    m_bHTTPOnly(FALSE),
    m_pUDPSocketStreamMap(0),
    m_pRTCPSocketStreamMap(0),
    m_pResolver(0),
    m_bSeqValueReceived(FALSE),
    m_bNoReuseConnection(FALSE),
    m_bLoadTest(FALSE),
    m_bEntityRequired(TRUE),
    m_pMutex(NULL),
    m_uProtocolType(0),
    m_pConnectionlessControl(0),
    m_bConnectionlessControl(FALSE),
    m_pConnectionCheckCallback(0),
    m_uConnectionCheckCallbackHandle(0),
    m_bConnectionAlive(FALSE),
    m_uConnectionTimeout(DEFAULT_CONN_TIMEOUT),
    m_ulBufferDepth(BUFFER_DEPTH_UNDEFINED),
    m_pInterruptState(NULL),
    m_pScheduler(NULL),
    m_bUseHTTPProxy(FALSE),
    m_bSplitterConsumer(FALSE),
    m_pPacketFilter(NULL),
    // workaround...
    m_bNonRSRTP(FALSE),
    m_pSetupRequestHeader(NULL),
    m_bPlayJustSent(FALSE),
    m_bIPTV(FALSE),
    m_bColumbia(FALSE),
    m_bNoKeepAlive(FALSE),
    m_bForceUCaseTransportMimeType(FALSE),
    m_bPrefetch(FALSE),
    m_bFastStart(FALSE),
    m_pCloakPorts(NULL),
    m_nCloakPorts(0),
    m_currentTransport(TCPMode),
    m_bReportedSuccessfulTransport(FALSE),
    m_bSDPInitiated(FALSE),
    m_bMulticast(FALSE),
    m_ulMulticastAddress(0),
    m_pSDPFileHeader(NULL),
    m_pSDPStreamHeaders(NULL),
    m_bSessionSucceeded(FALSE),
    m_bHasSyncMasterStream(FALSE),
    m_pNetworkServices(NULL),
    m_pPreferences(NULL),
    m_pSessionTimeout(NULL),
    m_pTimeoutCallback(NULL),
    m_bUseLegacyTimeOutMsg(TRUE),
    m_bKeepAlivePending(FALSE),
    m_bPaused(FALSE),
    m_ulServerTimeOut(DEFAULT_SERVER_TIMEOUT),
    m_ulCurrentTimeOut(0),
    m_pUAProfURI(NULL),
    m_pUAProfDiff(NULL)
#if defined(_MACINTOSH)
    , m_pCallback(NULL)
#endif /* _MACINTOSH */
{
    /*
     * Byte queue must be as large as possible because messages may be
     * bigger than MAX_RTSP_MSG
     */

    m_state = RTSPClientProtocol::INIT;

    // all methods supported...
    memset(m_pIsMethodSupported, 1, sizeof(BOOL) * RTSP_TABLE_SIZE);

#ifdef THREADS_SUPPORTED
	HXMutex::MakeMutex(m_pMutex);
#else
	HXMutex::MakeStubMutex(m_pMutex);
#endif
}

RTSPClientProtocol::~RTSPClientProtocol()
{
    clearStreamInfoList();
    clearTransportRequestList();
    clearUDPResponseHelperList();

    reset();

    HX_DELETE(m_pMutex);

    HX_RELEASE(m_pUAProfDiff);
    HX_RELEASE(m_pUAProfURI);
    HX_RELEASE(m_pPreferences);
    HX_RELEASE(m_pNetworkServices);
    HX_RELEASE(m_pRegistry);
    HX_RELEASE(m_pFileHeader);
    HX_RELEASE(m_pSessionHeaders);
    HX_RELEASE(m_pResponseHeaders);
    HX_RELEASE(m_pCloakValues);
    HX_RELEASE(m_pInterruptState);
    HX_RELEASE(m_pScheduler);
}

/*
 * IHXRTSPClientProtocol methods
 */

void
RTSPClientProtocol::SetSplitterConsumer(BOOL b)
{
    m_bSplitterConsumer = b;
}

STDMETHODIMP
RTSPClientProtocol::Init(IUnknown* pContext,
			const char* pHostName,
			UINT16 foreignPort,
			IHXRTSPClientProtocolResponse* pClient,
			UINT32 initializationType,
			IHXValues* pSessionHeaders,
			IHXValues* pInfo,
			BOOL bHTTPCloak,
			UINT16 uCloakPort,
			BOOL bNoReuseConnection)
{
    m_pSessionManager = RTSPClientSessionManager::instance();

    return InitExt(pContext,
		   pHostName,
		   foreignPort,
		   pClient,
		   initializationType,
		   pSessionHeaders,
		   pInfo,
		   bHTTPCloak,
		   uCloakPort,
		   bNoReuseConnection);
}

HX_RESULT
RTSPClientProtocol::InitExt(IUnknown*   pContext,
			    const char* pHostName,
			    UINT16	foreignPort,
			    IHXRTSPClientProtocolResponse* pClient,
			    UINT32	initializationType,
			    IHXValues* pSessionHeaders,
			    IHXValues* pInfo,
			    BOOL	bHTTPCloak,
			    UINT16	uCloakPort,
			    BOOL	bNoReuseConnection)
{
    HX_RESULT       hresult = HXR_OK;
    IUnknown*	    pUnknown = NULL;
    IHXResolver*    pResolver = NULL;
    IHXBuffer*      pBuffer = NULL;
    IHXBuffer*      pHostBuffer = NULL;
    IHXBuffer*      pSrcBuffer = NULL;
    IHXValues*      pURLProps = NULL;
    CHXURL*         pURL = NULL;

    if (!m_pContext)
    {
	m_pContext = pContext;
	m_pContext->AddRef();
    }

    if (!m_pResp)
    {
	m_pResp = pClient;
	m_pResp->AddRef();
    }

    HX_RELEASE(m_pCommonClassFactory);

    hresult = m_pContext->QueryInterface(IID_IHXCommonClassFactory,
                                         (void**) &m_pCommonClassFactory);

    if (HXR_OK != hresult)
    {
        goto cleanup;
    }

    HX_RELEASE(m_pInterruptState);
    hresult = m_pContext->QueryInterface(IID_IHXInterruptState,
                                         (void**) &m_pInterruptState);

    if (HXR_OK != m_pContext->QueryInterface(IID_IHXNetworkServices, (void**)&m_pNetworkServices) ||
	HXR_OK != m_pContext->QueryInterface(IID_IHXPreferences, (void**)&m_pPreferences))
    {
	hresult = HXR_FAILED;
        goto cleanup;
    }

    if (NULL == pHostName &&
        NULL != pInfo &&
        HXR_OK == pInfo->GetPropertyCString("helix-sdp", pSrcBuffer))
    {
        m_bSDPInitiated = TRUE;
        if (HXR_OK != ParseSDP("application/sdp", pSrcBuffer))
        {
            hresult = HXR_FAILED;
            goto cleanup;
        }

        if (!m_sessionHost.IsEmpty())
        {
	    UINT32 addr = HXinet_addr((const char*)m_sessionHost);
	    UINT32 ulHostAddr = DwToHost(addr);

            if (ulHostAddr >= MULTICAST_ADDRESS_RANGE_LOW &&
                ulHostAddr <= MULTICAST_ADDRESS_RANGE_HIGH)
            {
                m_bMulticast = TRUE;
                m_ulMulticastAddress = ulHostAddr;

                pHostName = (const char*)m_sessionHost;

                pInfo->SetPropertyULONG32("MulticastOnly", 1);
                m_pSDPFileHeader->SetPropertyULONG32("LiveStream", 1);
            }
        }

        if (!m_bMulticast)
        {
            pURL = new CHXURL(m_headerControl);

            if (!(pURLProps = pURL->GetProperties()))
            {
                hresult = HXR_FAILED;
                goto cleanup;
            }

            ULONG32 ulForeignPort = 0;
            pURLProps->GetPropertyULONG32(PROPERTY_PORT, ulForeignPort);
            foreignPort = (UINT16)ulForeignPort;

            if (HXR_OK != pURLProps->GetPropertyBuffer(PROPERTY_HOST, pHostBuffer))
            {
                hresult = HXR_FAILED;
                goto cleanup;
            }
            pHostName = (char*)pHostBuffer->GetBuffer();
        }
    }

    ReadPrefINT32(m_pPreferences, "ConnectionTimeOut", m_uConnectionTimeout);

    ReadPrefINT32(m_pPreferences, "ServerTimeOut", m_ulServerTimeOut);
    if (m_ulServerTimeOut < MINIMUM_TIMEOUT)
    {
        m_ulServerTimeOut = MINIMUM_TIMEOUT;
    }
    m_ulServerTimeOut *= MILLISECS_PER_SECOND;

    if (bHTTPCloak && m_bUseProxy)
    {
	m_bUseHTTPProxy = TRUE;
    }

    ReadPrefBOOL(m_pPreferences, "RTSPMessageDebug", m_bMessageDebug);
    if (m_bMessageDebug)
    {
	if (HXR_OK == m_pPreferences->ReadPref("RTSPMessageDebugFile", pBuffer))
	{
	    if (pBuffer->GetSize() <= 0)
	    {
		// no file name, no log
		m_bMessageDebug = FALSE;
	    }
	    else
	    {
		m_messageDebugFileName = (const char*)pBuffer->GetBuffer();
	    }
	}
        HX_RELEASE(pBuffer);
    }

    // XXXGo - Interop workaround
    ReadPrefBOOL(m_pPreferences, "NonRS", m_bNonRSRTP);
    if (m_bNonRSRTP)
    {
	m_pIsMethodSupported[SET_PARAM] = FALSE;
	m_pIsMethodSupported[GET_PARAM] = FALSE;
	m_pIsMethodSupported[RECORD] = FALSE;
	m_pIsMethodSupported[ANNOUNCE] = FALSE;
    }

    ReadPrefBOOL(m_pPreferences, "RTSPNoReuseConnection", bNoReuseConnection);
    ReadPrefBOOL(m_pPreferences, "LoadTest", m_bLoadTest);

    if (!m_pTransportStreamMap)
    {
	m_pTransportStreamMap = new CHXMapLongToObj;
        if(!m_pTransportStreamMap)
        {
            hresult = HXR_OUTOFMEMORY;
            goto cleanup;
        }
    }

    if (!m_pTransportPortMap)
    {
	m_pTransportPortMap = new CHXMapLongToObj;
        if(!m_pTransportPortMap)
        {
            hresult = HXR_OUTOFMEMORY;
            goto cleanup;
        }
    }

    if (!m_pTransportMPortMap)
    {
	m_pTransportMPortMap = new CHXMapLongToObj;
        if(!m_pTransportMPortMap)
        {
            hresult = HXR_OUTOFMEMORY;
            goto cleanup;
        }
    }

    m_hostName = pHostName;
    m_foreignPort = foreignPort;

    m_bHTTPOnly	= bHTTPCloak;
    m_uCloakPort = uCloakPort;
    m_bNoReuseConnection = bNoReuseConnection;

    if (m_bHTTPOnly)
    {
	m_currentTransport = HTTPCloakMode;
    }

    if(pSessionHeaders && !m_pSessionHeaders)
    {
	m_pSessionHeaders = pSessionHeaders;
	m_pSessionHeaders->AddRef();
    }

    HX_RELEASE(m_pResponseHeaders);

    if (HXR_OK != m_pCommonClassFactory->CreateInstance(CLSID_IHXKeyValueList, (void**)&pUnknown))
    {
	hresult = HXR_FAILED;
	goto cleanup;
    }

    if (HXR_OK != pUnknown->QueryInterface(IID_IHXKeyValueList, (void**)&m_pResponseHeaders))
    {
	hresult = HXR_FAILED;
	goto cleanup;
    }

    if(!m_pRegistry)
    {
#if defined(HELIX_FEATURE_REGISTRY)
	// get registry ptr
	IHXRegistry* pRegistry = 0;

	hresult = m_pContext->QueryInterface(IID_IHXRegistry,
					(void**)&pRegistry);
	HX_VERIFY(HXR_OK == hresult);

	if(hresult == HXR_OK)
	{
	    m_pRegistry = pRegistry;
	}
#endif /* HELIX_FEATURE_REGISTRY */
    }

    if(!m_pResolver)
    {
	m_pNetworkServices->CreateResolver(&pResolver);
	if(!pResolver)
	{
	    hresult = HXR_OUTOFMEMORY;
	    goto cleanup;
	}

	HX_RELEASE(m_pResolver);
	m_pResolver = pResolver;

	m_pResolver->Init(this);

        CHXString pHost;
	if (m_bUseHTTPProxy || m_bUseProxy)
	{
            pHost = m_proxyHost;
        }
        else
        {
            pHost = m_hostName;
        }

	if(IsNumericAddr(pHost, pHost.GetLength()))
	{
	    UINT32 addr = HXinet_addr((const char*)pHost);
	    UINT32 ulHostAddr = DwToHost(addr);

	    hresult = GetHostByNameDone(HXR_OK, ulHostAddr);
	}
	else
	{
	    hresult = m_pResolver->GetHostByName(pHost);
	}
    }

    HX_RELEASE(m_pScheduler);
    if (HXR_OK != m_pContext->QueryInterface(IID_IHXScheduler,
                                             (void**)&m_pScheduler))

    {
	hresult = HXR_OUTOFMEMORY;
	goto cleanup;
    }

    // check for UAProf headers
    if (m_pPreferences)
    {
	HX_RELEASE(m_pUAProfURI);
	HX_RELEASE(m_pUAProfDiff);
	m_pPreferences->ReadPref("XWapProfileURI", m_pUAProfURI);
	m_pPreferences->ReadPref("XWapProfileDiff", m_pUAProfDiff);
    }

#if defined(_MACINTOSH)
    if (m_pCallback &&
	m_pCallback->m_bIsCallbackPending &&
	m_pScheduler)
    {
	m_pCallback->m_bIsCallbackPending = FALSE;
	m_pScheduler->Remove(m_pCallback->m_Handle);
    }
#endif /* _MACINTOSH */

cleanup:

    HX_RELEASE(pSrcBuffer);
    HX_RELEASE(pURLProps);
    HX_RELEASE(pUnknown);
    HX_RELEASE(pHostBuffer);            

    HX_DELETE(pURL);

    return hresult;
}

STDMETHODIMP
RTSPClientProtocol::SetProxy(const char* pProxyHost, UINT16 uProxyPort)
{
    m_bUseProxy = TRUE;
    m_proxyHost = pProxyHost;
    m_proxyPort = uProxyPort;
    return HXR_OK;
}

STDMETHODIMP
RTSPClientProtocol::SetResponse(IHXRTSPClientProtocolResponse* pResp)
{
    HX_RELEASE(m_pResp);

    m_pResp = pResp;
    m_pResp->AddRef();

    return HXR_OK;
}

STDMETHODIMP
RTSPClientProtocol::SetBuildVersion(const char* pVersionString)
{
    m_versionString = pVersionString;
    return HXR_OK;
}

STDMETHODIMP
RTSPClientProtocol::Done()
{
    m_bClientDone = TRUE;

    if (m_pMutex)
    {
	m_pMutex->Lock();
    }

#if defined(_MACINTOSH)
    if (m_pCallback &&
	m_pCallback->m_bIsCallbackPending &&
	m_pScheduler)
    {
	m_pCallback->m_bIsCallbackPending = FALSE;
	m_pScheduler->Remove(m_pCallback->m_Handle);
    }
    HX_RELEASE(m_pCallback);
#endif /* _MACINTOSH */

    while (!m_sessionList.IsEmpty())
    {
	RTSPClientSession* pTempSession = (RTSPClientSession*)m_sessionList.RemoveHead();
	m_pSessionManager->removeFromSession(this, pTempSession);
    }

    HX_ASSERT(m_pSessionManager && m_pSessionManager->isValid());
    if (m_pSession)
    {
	m_pSessionManager->removeFromSession(this, m_pSession);
        m_pSession = NULL;
    }
    HX_RELEASE(m_pSessionManager);

    clearSocketStreamMap(m_pUDPSocketStreamMap);
    clearSocketStreamMap(m_pRTCPSocketStreamMap);

    reset();

    if (m_pMutex)
    {
	m_pMutex->Unlock();
    }

    return HXR_OK;
}

STDMETHODIMP
RTSPClientProtocol::SendStreamDescriptionRequest(const char* pURL,
    IHXValues* pRequestHeaders)
{
#ifdef _MACINTOSH
    if (m_pInterruptState && m_pInterruptState->AtInterruptTime())
    {
	/*
	* We cannot load DLLs (Stream Desc Plugins) at deferred time.
	* Also, encodeURL calls HXIsLeadByte->CharType->...internally
	* calls GetPort. Mac does not like that either at deferred time.
	* Schedule a callback and do this operation at system time
	*/

	if (!m_pCallback)
	{
	    m_pCallback = new RTSPClientProtocolCallback(this);
            if(!m_pCallback)
            {
                return HXR_OUTOFMEMORY;
            }
	    m_pCallback->AddRef();
	}

	HX_ASSERT(m_pScheduler);

	if (!m_pCallback->m_bIsCallbackPending)
	{
	    m_pCallback->m_bIsCallbackPending = TRUE;
	    m_pCallback->m_Handle =
		m_pScheduler->RelativeEnter(m_pCallback, 0);
	}

	/*
	* If we receive a SendStreamDescriptionRequest when
	* one is already pending, we do not queue them up.
	* The last one wins
	*/
	m_pCallback->m_PendingDescURL = pURL;
	HX_RELEASE(m_pCallback->m_pPendingRequestHeaders);
	m_pCallback->m_pPendingRequestHeaders = pRequestHeaders;
	m_pCallback->m_pPendingRequestHeaders->AddRef();

	return HXR_OK;
    }

    HX_ASSERT(m_pScheduler);
    /* remove any previously scheduled callback */
    if (m_pCallback &&
	m_pCallback->m_bIsCallbackPending)
    {
	m_pCallback->m_bIsCallbackPending = FALSE;
	m_pScheduler->Remove(m_pCallback->m_Handle);
    }
#endif

    return sendPendingStreamDescription(pURL, pRequestHeaders);
}


HX_RESULT
RTSPClientProtocol::sendPendingStreamDescription(const char* pURL,
    IHXValues* pRequestHeaders)
{
    m_pMutex->Lock();
    HX_RESULT rc = HXR_OK;

    if (HXR_OUTOFMEMORY == extractExistingAuthorizationInformation(pRequestHeaders))
    {
        m_pMutex->Unlock();
        return HXR_OUTOFMEMORY;
    }

    RTSPDescribeMessage* pMsg = new RTSPDescribeMessage;
    if(!pMsg)
    {
        m_pMutex->Unlock();
        return HXR_OUTOFMEMORY;
    }

    CHXString encodedURL;
    CHXURL::encodeURL(pURL, encodedURL);

    UINT32 stringSize = 7 + m_hostName.GetLength() + 1 + 5 + 1 + encodedURL.GetLength() + 1;
    char* pURLBuffer = new char[ stringSize ];

    if(!pURLBuffer)
    {
        HX_DELETE(pMsg);
        m_pMutex->Unlock();
        return HXR_OUTOFMEMORY;
    }
    SafeSprintf( pURLBuffer, stringSize, "rtsp://%s:%u/%s", (const char*)m_hostName, m_foreignPort, (const char*)encodedURL ); /* Flawfinder: ignore */

    m_url = pURLBuffer;
    delete [] pURLBuffer;

    pMsg->setURL(m_url);

    IHXValues* pValuesRequestHeaders = NULL;

    pValuesRequestHeaders = new CHXHeader();
    if(!pValuesRequestHeaders)
    {
        HX_DELETE(pMsg);
        m_pMutex->Unlock();
        return HXR_OUTOFMEMORY;
    }
    pValuesRequestHeaders->AddRef();

    if (m_bEntityRequired)
    {
	CHXString CHXStringRequireValue
	(
	    RTSPClientProtocol::RTSPRequireOptionsTable
	    [
		RTSPClientProtocol::RTSP_REQUIRE_ENTITY
	    ].pcharOption
	);

	IHXBuffer* pBufferRequireValue = NULL;

	CHXBuffer::FromCharArray
	(
	    CHXStringRequireValue.GetBuffer(0),
	    &pBufferRequireValue
	);

	// Set require header
	pValuesRequestHeaders->SetPropertyCString
	(
	    "Require",
	    pBufferRequireValue
	);

	HX_RELEASE(pBufferRequireValue);
    }

    addUAProfHeaders(pValuesRequestHeaders);

    CHXHeader::mergeHeaders
    (
	pValuesRequestHeaders,
	pRequestHeaders
    );

    // get all StreamDescription plugin mime types
    CHXString mimeTypes;
    IHXCommonClassFactory*	pClassFactory	    = NULL;
    IHXPluginGroupEnumerator*	pPluginGroupEnum    = NULL;

    // ok we are going to get an IRMA PluginGroupEnumerator from the core.
    if
    (
	SUCCEEDED
	(
	    m_pContext->QueryInterface
	    (
		IID_IHXCommonClassFactory,
		(void**) &pClassFactory
	    )
	)
    )
    {
	pClassFactory->CreateInstance
	(
	    CLSID_IHXPluginGroupEnumerator,
	    (void**)&pPluginGroupEnum
	);

	HX_RELEASE(pClassFactory);
    }

    if(pPluginGroupEnum &&
       (HXR_OK == pPluginGroupEnum->Init(IID_IHXStreamDescription)))
    {
	IUnknown* pUnknown = NULL;
	ULONG32 ulNumPlugins = pPluginGroupEnum->GetNumOfPlugins();
	for(ULONG32 i=0;i<ulNumPlugins;++i)
	{
	    if (SUCCEEDED(pPluginGroupEnum->GetPlugin(i, pUnknown)))
	    {
                GetStreamDescriptionInfo(pUnknown, mimeTypes);
                HX_RELEASE(pUnknown);
	    }
	}
    }
    else
    {
	// if we don't have a CCF or we don't have a pluginGroupEnumerator
	// then we will have to use a plugin Enumerator.
	IHXPluginEnumerator* pPluginEnumerator = NULL;
	m_pContext->QueryInterface
	(
	    IID_IHXPluginEnumerator,
	    (void**)&pPluginEnumerator
	);

	if(pPluginEnumerator)
	{
	    IUnknown* pUnknown = NULL;
            ULONG32 ulNumPlugins = pPluginEnumerator->GetNumOfPlugins();
	    for(ULONG32 i=0;i<ulNumPlugins;++i)
	    {
		if(SUCCEEDED(pPluginEnumerator->GetPlugin(i, pUnknown)))
		{
                    GetStreamDescriptionInfo(pUnknown, mimeTypes);
                    HX_RELEASE(pUnknown);
                }
	    }
	    pPluginEnumerator->Release();
	}
    }

    HX_RELEASE(pPluginGroupEnum);

    pMsg->addHeader("Accept", (const char*)mimeTypes);
    AddCommonHeaderToMsg(pMsg);

    addRFC822Headers(pMsg, pValuesRequestHeaders);

    appendAuthorizationHeaders(pMsg);

    HX_RELEASE(pValuesRequestHeaders);

    UINT32 seqNo = m_pSession->getNextSeqNo(this);
    rc = sendRequest(pMsg, seqNo);
    m_pMutex->Unlock();
    return rc;
}

STDMETHODIMP
RTSPClientProtocol::SendStreamRecordDescriptionRequest(
    const char* pURL,
    IHXValues* pFileHeader,
    CHXSimpleList* pStreams,
    IHXValues* pRequestHeaders)
{
    HX_RESULT rc = HXR_OK;
    IHXBuffer* pDescription = 0;

    if (!m_pIsMethodSupported[ANNOUNCE])
    {
	return HXR_OK;
    }

    // Let's make sure we have enough mem before we do the mutex lock.
    RTSPAnnounceMessage* pMsg = new RTSPAnnounceMessage;
    if(!pMsg)
    {
        return HXR_OUTOFMEMORY;
    }
    m_pMutex->Lock();
    pMsg->setURL(pURL);
    m_url = pURL;
    const char* pDesc = 0;

    addRFC822Headers(pMsg, pRequestHeaders);

    clearStreamInfoList();

    // use the first stream description plugin found...
    char* pMimeType = 0;
    if(HXR_OK == getStreamDescriptionMimeType(pMimeType))
    {
	IHXStreamDescription* pSD = 
	    getStreamDescriptionInstance(pMimeType);

	if(pSD)
	{
	    UINT32 streamNumber;
	    UINT32 needReliable;
	    UINT32 rtpPayloadType;
	    UINT32 ulIsLive;
	    IHXBuffer* pControlString;
	    UINT32 ulIsSessionLive = 0;

	    // if we are talking to a realserver, we will make an old sdpfile
	    // for now...We need to check version and send the spec complient
	    // sdp!
	    IHXValues* pResponseHeaders = NULL;
	    if (HXR_OK ==
		m_pResponseHeaders->QueryInterface(IID_IHXValues, (void**)&pResponseHeaders))
	    {
		pFileHeader->SetPropertyULONG32("SdpFileType",
		    GetSdpFileTypeWeNeed(pResponseHeaders));
	    }
	    HX_RELEASE(pResponseHeaders);

	    UINT16 nStreams = pStreams->GetCount();
	    IHXValues** ppValues = new IHXValues*[nStreams+2];
            if(!ppValues)
            {
                rc = HXR_OUTOFMEMORY;
                HX_DELETE(pMsg);
                goto overandout;
            }
	    ppValues[0] = pFileHeader;
	    ppValues[1] = 0;    // no options

	    pFileHeader->GetPropertyULONG32("LiveStream", ulIsSessionLive);

	    CHXSimpleList::Iterator i;
	    INT16 j=2;
	    for(i=pStreams->Begin();i!=pStreams->End();++i,++j)
	    {
		// reset...
		streamNumber	= 0;
		needReliable	= 0;
		rtpPayloadType	= (UINT32)-1;
		ulIsLive	= ulIsSessionLive;
		pControlString	= 0;

		ppValues[j] = (IHXValues*)(*i);

		// build stream info list
		RTSPStreamInfo* pInfo = new RTSPStreamInfo;
                if(!pInfo)
                {
                    rc = HXR_OUTOFMEMORY;
                    HX_DELETE(pMsg);
                    HX_VECTOR_DELETE(ppValues);
                    goto overandout;
                }
		ppValues[j]->GetPropertyULONG32("StreamNumber",
		    streamNumber);
		ppValues[j]->GetPropertyULONG32("NeedReliablePackets",
		    needReliable);
		ppValues[j]->GetPropertyULONG32("RTPPayloadType",
		    rtpPayloadType);
		ppValues[j]->GetPropertyCString("Control",
		    pControlString);
		ppValues[j]->GetPropertyULONG32("LiveStream", ulIsLive);

		pInfo->m_streamNumber = (UINT16)streamNumber;
		pInfo->m_bNeedReliablePackets = needReliable? TRUE: FALSE;
		pInfo->m_rtpPayloadType = (INT16)rtpPayloadType;
		pInfo->m_bIsLive = ulIsLive ? TRUE : FALSE;
		pInfo->m_sPort = 0;
		if(pControlString)
		{
		    pInfo->m_streamControl = pControlString->GetBuffer();

		    // done with the buffer
		    pControlString->Release();
		    pControlString = NULL;
		}
		else
		{
		    char tmp[32];
		    SafeSprintf(tmp,32, "streamid=%u", (UINT16)streamNumber);
		    pInfo->m_streamControl = tmp;
		}
		m_streamInfoList.AddTail(pInfo);
	    }
	    pSD->GetDescription(nStreams+2, ppValues, pDescription);
	    pDesc = (const char*)pDescription->GetBuffer();
	    pSD->Release();
	    delete[] ppValues;
	}
    }
    if(pDesc)
    {
	m_bSetupRecord = TRUE;
#ifdef _MACINTOSH
// someday, someone in core should look at why m_pSession is NULL when
// an invalid port is used, and yet m_pSession is assumed to be valid, and subsequently
// crashes macs HARD. I would attempt to investigate this further, however
// the core deferred task keeps crashing the debugger while I am attempting
// to trace through why m_pSession never gets assigned. since Mac Producer goes
// beta in 2 weeks, I'm putting this 'fix' in here now.. rlovejoy 2/16/00

		if (m_pSession == NULL) {
			rc = HXR_PORT_IN_USE;
		} else
#endif
		{
        UINT32 seqNo = m_pSession->getNextSeqNo(this);
	rc = sendRequest(pMsg, pDesc, pMimeType, seqNo);
		}
	// done with the description, we need to clean it up
	pDescription->Release();
    }
    else
    {
	rc = HXR_FAIL;
    }
overandout:
    delete[] pMimeType;
    m_pMutex->Unlock();
    return rc;
}


STDMETHODIMP
RTSPClientProtocol::SendSetupRequest
(
    RTSPTransportType* pTransType,
    UINT16 nTransTypes,
    IHXValues* pIHXValuesRequestHeaders
)
{
    m_pMutex->Lock();

    HX_RESULT           rc = HXR_OK;
    IHXUDPSocket*       pUDPSocket = NULL;
    IHXUDPSocket*       pRTCPUDPSocket = NULL;
    RTSPTransport*      pTrans = NULL;
    RTCPUDPTransport*   pRTCPTrans = NULL;
    RTSPStreamInfo*     pStreamInfo = NULL;
    IHXSetSocketOption* pSockOpt = NULL;
    CHXSimpleList::Iterator i;

#if defined(HELIX_FEATURE_TRANSPORT_MULTICAST)
    if (m_bSDPInitiated && m_bMulticast)
    {
        RTSPTransportRequest* pRequest = new RTSPTransportRequest(RTSP_TR_RTP_MCAST, 0);
        if(pRequest)
        {
	    m_transportRequestList.AddTail(pRequest);
	}
        else
        {
            rc = HXR_OUTOFMEMORY;
            goto cleanup;
        }

        for (i=m_streamInfoList.Begin();i!=m_streamInfoList.End();++i)
        {
            pStreamInfo = (RTSPStreamInfo*)(*i);

            pUDPSocket = (IHXUDPSocket*)(*m_pUDPSocketStreamMap)[pStreamInfo->m_streamNumber];
            pRTCPUDPSocket = (IHXUDPSocket*)(*m_pRTCPSocketStreamMap)[pStreamInfo->m_streamNumber];

            // create a new transport for each setup
            pTrans = new RTPUDPTransport(m_bSetupRecord);
            if(!pTrans)
            {
                rc = HXR_OUTOFMEMORY;
                goto cleanup;
            }
            pTrans->AddRef();

            if (HXR_OK != ((RTPUDPTransport*)pTrans)->init(m_pContext,
	                                                   pUDPSocket,
	                                                   (IHXRTSPTransportResponse*)this))
            {
                rc = HXR_BAD_TRANSPORT;
                goto cleanup;
            }
            pTrans->notifyEmptyRTPInfo();

            // create an RTCP transport for this stream
            pRTCPTrans = new RTCPUDPTransport(m_bSetupRecord);
            if (!pRTCPTrans)
            {
                rc = HXR_OUTOFMEMORY;
                goto cleanup;
            }
            pRTCPTrans->AddRef();

            if (HXR_OK != pRTCPTrans->init(m_pContext, pRTCPUDPSocket, (RTPUDPTransport*)pTrans,
		                           (IHXRTSPTransportResponse*) this, pStreamInfo->m_streamNumber))
            {
                rc = HXR_BAD_TRANSPORT;
                goto cleanup;
            }
            pRTCPTrans->notifyEmptyRTPInfo();

            ((RTPUDPTransport*)pTrans)->setRTCPTransport(pRTCPTrans);

            pTrans->addStreamInfo(pStreamInfo);
	    (*m_pTransportStreamMap)[pStreamInfo->m_streamNumber] = pTrans;

	    (*m_pTransportMPortMap)[pStreamInfo->m_sPort] = pTrans;
            pTrans->JoinMulticast(m_ulConnectToAddr, pStreamInfo->m_sPort, pUDPSocket);

	    (*m_pTransportMPortMap)[pStreamInfo->m_sPort+1] = pRTCPTrans;
            pRTCPTrans->JoinMulticast(m_ulConnectToAddr, pStreamInfo->m_sPort + 1, pRTCPUDPSocket);

	    if ((!m_bHasSyncMasterStream) && 
		(pStreamInfo->m_eMediaType == RTSPMEDIA_TYPE_AUDIO))
	    {
	        pStreamInfo->m_bIsSyncMaster = TRUE;
		m_bHasSyncMasterStream = TRUE;
            }

	    mapControlToStreamNo(pStreamInfo->m_streamControl, pStreamInfo->m_streamNumber);

	    rc = pRequest->addTransportInfo(pTrans, (RTCPBaseTransport*)pRTCPTrans, pStreamInfo->m_streamNumber, 0);
            if( rc == HXR_OUTOFMEMORY )
            {
                goto cleanup;
            }
            
 	    rc = pUDPSocket->Read(HX_SAFEUINT(MAX_UDP_PACKET));
            if( rc == HXR_OUTOFMEMORY )
            {
                goto cleanup;
            }
            rc = pRTCPUDPSocket->Read(HX_SAFEUINT(MAX_UDP_PACKET));
            if( rc == HXR_OUTOFMEMORY )
            {
                goto cleanup;
            }
        }

        m_uProtocolType = 1;
        m_pResp->HandleSetupResponse(HXR_OK);

        goto cleanup;
    }
#endif /* HELIX_FEATURE_TRANSPORT_MULTICAST */

    if( HXR_OUTOFMEMORY == extractExistingAuthorizationInformation(pIHXValuesRequestHeaders))
    {
        rc = HXR_OUTOFMEMORY;
        goto cleanup;
    }

    if(pTransType)
    {
	for(int i=0;i<nTransTypes;++i)
	{
	    RTSPTransportRequest* pRequest =
		new RTSPTransportRequest(pTransType[i].m_lTransportType,
		    pTransType[i].m_sPort);
            if(pRequest)
            {
	        m_transportRequestList.AddTail(pRequest);
	    }
            else
            {
                rc = HXR_OUTOFMEMORY;
                break;
            }
	}
    }

    if (m_bIPTV && rc == HXR_OK)
    {
	// we are going to keep this around for now to send cookie in all SETUP.
	m_pSetupRequestHeader = pIHXValuesRequestHeaders;
	m_pSetupRequestHeader->AddRef();
    }


    if (rc == HXR_OK)
    {
        rc =  sendFirstSetupRequest(pIHXValuesRequestHeaders);
    }

cleanup:

    if (HXR_OK != rc)
    {
        HX_RELEASE(pRTCPTrans);
        HX_RELEASE(pTrans);
    }

    m_pMutex->Unlock();

    return rc;
}

HX_RESULT
RTSPClientProtocol::sendFirstSetupRequest
(
    IHXValues* pIHXValuesRequestHeaders
)
{
    m_setupResponseCount = 0;

    if (!m_streamInfoList.IsEmpty())
    {
	RTSPStreamInfo* pStreamInfo = (RTSPStreamInfo*)m_streamInfoList.GetHead();
	if(pStreamInfo)
	{
	    return sendSetupRequestMessage
	    (
		pStreamInfo,
		pIHXValuesRequestHeaders,
		TRUE
	    );
	}
    }
    return HXR_FAIL;
}

HX_RESULT
RTSPClientProtocol::sendRemainingSetupRequests()
{
    HX_RESULT status = HXR_OK;
    CHXSimpleList::Iterator i;
    BOOL bFirst = TRUE;
    for(i=m_streamInfoList.Begin();
	    status == HXR_OK && (i!=m_streamInfoList.End());++i)
    {
	if(bFirst)
	{
	    bFirst = FALSE;
	}
	else
	{
	    RTSPStreamInfo* pStreamInfo = (RTSPStreamInfo*)(*i);
	    status = sendSetupRequestMessage(pStreamInfo, NULL, FALSE);
	}
    }
    return status;
}

HX_RESULT
RTSPClientProtocol::sendSetupRequestMessage(RTSPStreamInfo* pStreamInfo,
    IHXValues* pIHXValuesRequestHeaders, BOOL bFirstSetup)
{
    m_pMutex->Lock();
    RTSPSetupMessage* pMsg = new RTSPSetupMessage;
    if(!pMsg)
    {
        m_pMutex->Unlock();
        return HXR_OUTOFMEMORY;
    }

    HX_RESULT status = HXR_OK;
    status = sendSetupRequestMessageExt(pStreamInfo,
					pIHXValuesRequestHeaders,
					bFirstSetup,
					pMsg);

    pMsg->addHeader("User-Agent", m_versionString);

    if (bFirstSetup && !m_sessionID.IsEmpty())
    {
        pMsg->addHeader("If-Match", m_sessionID);
    }
    else if (!m_sessionID.IsEmpty())
    {
        pMsg->addHeader("Session", m_sessionID);
    }

    // append stream control string to request
    setSetupRequestURL(pMsg, pStreamInfo);

    if (pIHXValuesRequestHeaders)
    {
	addUAProfHeaders(pIHXValuesRequestHeaders);
	addRFC822Headers(pMsg, pIHXValuesRequestHeaders);
    }

    UINT32 seqNo;
    seqNo = m_pSession->getNextSeqNo(this);

    /* Why are we not checking for any error code from above ??? */
    status = sendRequest(pMsg, seqNo);

    m_pMutex->Unlock();
    return status;
}

HX_RESULT
RTSPClientProtocol::sendSetupRequestMessageExt(RTSPStreamInfo* pStreamInfo,
					       IHXValues*& pIHXValuesRequestHeaders,
					       BOOL bFirstSetup,
					       RTSPSetupMessage*& pMsg)
{
    MIMEHeader* pHeader = new MIMEHeader("Transport");
    if(!pHeader)
    {
        return HXR_OUTOFMEMORY;
    }

    HX_RESULT status = HXR_OK;

    CHXSimpleList::Iterator i;

    for(i=m_transportRequestList.Begin();i!=m_transportRequestList.End();++i)
    {
	RTSPTransportRequest* pRequest = (RTSPTransportRequest*)(*i);
	UINT16 streamNumber = pStreamInfo->m_streamNumber;
	UINT16 nUDPPort = 0;

#if defined(HELIX_FEATURE_RTP)
	switch(pRequest->m_lTransportType)
	{
	    case RTSP_TR_RTP_UDP:
	    {
		// create a new transport for each setup
		RTSPTransport* pTrans =
				    new RTPUDPTransport(m_bSetupRecord);
                if(!pTrans)
                {
                    HX_DELETE(pHeader);
                    return HXR_OUTOFMEMORY;
                }
		pTrans->AddRef();

		if (m_bPrefetch)
		{
		    pTrans->EnterPrefetch();
		}

		IHXUDPSocket* pUDPSocket = (IHXUDPSocket*)
		    (*m_pUDPSocketStreamMap)[streamNumber];
		pUDPSocket->GetLocalPort(nUDPPort);

		IHXUDPSocket* pRTCPSocket = (IHXUDPSocket*)
		    (*m_pRTCPSocketStreamMap)[streamNumber];

		if (HXR_OK !=
		    ((RTPUDPTransport*)pTrans)->init(
			m_pContext,
			pUDPSocket,
			(IHXRTSPTransportResponse*) this))
		{
		    pTrans->Release();
		    return HXR_BAD_TRANSPORT;
		}

		// create an RTCP transport for this stream
		RTCPUDPTransport* pRTCPTran = new RTCPUDPTransport(m_bSetupRecord);
                if(!pRTCPTran)
                {
                    HX_DELETE(pHeader);
                    HX_RELEASE(pTrans);
		    m_pMutex->Unlock();
                    return HXR_OUTOFMEMORY;
                }
		pRTCPTran->AddRef();
		pRTCPTran->init(m_pContext,
				pRTCPSocket,
				(RTPUDPTransport*)pTrans,
				(IHXRTSPTransportResponse*) this,
				streamNumber);

		((RTPUDPTransport*)pTrans)->setRTCPTransport(pRTCPTran);

		status = pRequest->addTransportInfo(pTrans, (RTCPBaseTransport*)pRTCPTran, streamNumber,
		    nUDPPort);

		if (m_bIPTV && m_pSetupRequestHeader && status != HXR_OUTOFMEMORY)
		{
		    addRFC822Headers(pMsg, m_pSetupRequestHeader);
		    // don't add it twice...
		    pIHXValuesRequestHeaders = NULL;
		}
	    }
	    break;

	    case RTSP_TR_RTP_TCP:
	    {
		RTSPTransport* pTrans = new RTPTCPTransport(m_bSetupRecord);
                if(!pTrans)
                {
                    HX_DELETE(pHeader);
                    return HXR_OUTOFMEMORY;
                }
		pTrans->AddRef();

		if (m_bPrefetch)
		{
		    pTrans->EnterPrefetch();
		}

		if (HXR_OK != ((RTPTCPTransport*)pTrans)->init(
                    m_pContext, m_pSocket, (IHXRTSPTransportResponse*)this))
		{
		    status = HXR_BAD_TRANSPORT;
		}

	    	// create an RTCP transport for this stream
	    	RTCPTCPTransport* pRTCPTran = new RTCPTCPTransport(m_bSetupRecord);
                if(!pRTCPTran)
                {
                    HX_DELETE(pHeader);
                    HX_DELETE(pTrans);
                    return HXR_OUTOFMEMORY;
                }
	    	pRTCPTran->AddRef();
	    	pRTCPTran->init(m_pContext,
			    	m_pSocket,
			    	(RTPTCPTransport*)pTrans,
			    	(IHXRTSPTransportResponse*)this,
			    	streamNumber);

	    	((RTPTCPTransport*)pTrans)->setRTCPTransport((RTCPBaseTransport*)pRTCPTran);

	    	status = pRequest->addTransportInfo(pTrans, (RTCPBaseTransport*)pRTCPTran, streamNumber,
		    	nUDPPort);

		if (m_bIPTV && m_pSetupRequestHeader)
		{
		    addRFC822Headers(pMsg, m_pSetupRequestHeader);
		    // don't add it twice...
		    pIHXValuesRequestHeaders = NULL;
		}
	    }
	    break;

	    default:
	    {
	    }
	    break;
	}
#endif /* HELIX_FEATURE_RTP */

	char* pModifiedMimeType = NULL;
	const char* pMimeType =
	    RTSPTransportMimeMapper::getTransportMimeType
	    (
		pRequest->m_lTransportType
	    );


	// Accomodate incompliant servers that understand only upper case
	// transport mime-types
	if (m_bForceUCaseTransportMimeType)
	{
	    ULONG32 ulMimeTypeLength = strlen(pMimeType);

	    if (ulMimeTypeLength != 0)
	    {
		pModifiedMimeType = new char [ulMimeTypeLength + 1];
                if(!pModifiedMimeType)
                {
                    HX_DELETE(pHeader);
                    return HXR_OUTOFMEMORY;
                }
	    }

	    if (pModifiedMimeType)
	    {
		strcpy(pModifiedMimeType, pMimeType); /* Flawfinder: ignore */
		StrToUpper(pModifiedMimeType);
		pMimeType = pModifiedMimeType;
	    }
	}

#if defined(HELIX_FEATURE_RTP)
	switch(pRequest->m_lTransportType)
	{
	    case RTSP_TR_RTP_UDP:
	    case RTSP_TR_RTP_TCP:
	    {
		char portValue[32]; /* Flawfinder: ignore */

		MIMEHeaderValue* pHeaderValue = new MIMEHeaderValue(pMimeType);

                if(!pHeaderValue)
                {
                    HX_DELETE(pHeader);
                    HX_DELETE(pModifiedMimeType);
                    return HXR_OUTOFMEMORY;
                }

                if (RTSP_TR_RTP_UDP == pRequest->m_lTransportType)
                {
		    SafeSprintf(portValue, 32, "%u-%u", nUDPPort, nUDPPort+1);
		    pHeaderValue->addParameter("client_port", (const char*)portValue);
#ifdef XXXtbradleyTEST_RTSP_DESTINATION
                    pHeaderValue->addParameter("destination", "127.0.0.1");
#endif /* XXXtbradleyTEST_RTSP_DESTINATION */
                }

                if(m_bSetupRecord)
		{
		    pHeaderValue->addParameter("mode", "record");
		}
		else
		{
		    pHeaderValue->addParameter("mode", "play");
		}
		pHeader->addHeaderValue(pHeaderValue);
	    }
	    break;

	    case RTSP_TR_RTP_MCAST:
            default:
	    {
	    }
	    break;
	}
#endif /* HELIX_FEATURE_RTP */

	HX_VECTOR_DELETE(pModifiedMimeType);
    }
    pMsg->addHeader(pHeader);

    return status;
}

STDMETHODIMP
RTSPClientProtocol::SendPlayRequest(UINT32 lFrom, UINT32 lTo,
    CHXSimpleList* pASMRules)
{
    /*
     * Flush the data packets out of the transport buffers
     */

    m_pMutex->Lock();

    m_bPaused = FALSE;

#if defined(HELIX_FEATURE_TRANSPORT_MULTICAST)
    if (m_bSDPInitiated && m_bMulticast)
    {
        m_pMutex->Unlock();
        return m_pResp->HandlePlayResponse(HXR_OK);
    }
#endif /* HELIX_FEATURE_TRANSPORT_MULTICAST */

    /*
     * XXXGH...I believe we should be iterating through m_transportRequestList
     *         here and for SendPauseRequest, SendResumeRequest, etc.
     */

    // only used when m_bNonRSRTP is TRUE
    m_bPlayJustSent = TRUE;

    if (!m_transportRequestList.IsEmpty())
    {
	RTSPTransportRequest* pRequest =
	    (RTSPTransportRequest*)m_transportRequestList.GetHead();
	RTSPTransportInfo* pTransInfo = pRequest->getFirstTransportInfo();
	while(pTransInfo)
	{
	    pTransInfo->m_pTransport->playReset();
	    // set the range in transport...only for RTP
	    pTransInfo->m_pTransport->setPlayRange(lFrom, lTo);
            pTransInfo->m_pTransport->SetPlayRequestSent(TRUE);
	    pTransInfo->m_pTransport->resumeBuffers();
	    pTransInfo = pRequest->getNextTransportInfo();
	}
    }

    HX_RESULT rc = HXR_OK;
    RTSPPlayMessage* pMsg = new RTSPPlayMessage;
    if(pMsg)
    {
        RTSPRange range(lFrom, lTo, RTSPRange::TR_NPT);

        pMsg->setURL(m_url);
        AddCommonHeaderToMsg(pMsg);

        pMsg->addHeader("Range", (const char*)range.asString());
        UINT32 seqNo = m_pSession->getNextSeqNo(this);

        rc = sendRequest(pMsg, seqNo);
    }
    else
    {
        rc = HXR_OUTOFMEMORY;
    }
    m_pMutex->Unlock();
    return rc;
}

STDMETHODIMP
RTSPClientProtocol::SendRecordRequest()
{
    if (!m_pIsMethodSupported[RECORD] || !m_pSession)
    {
	return HXR_OK;
    }

    HX_RESULT rc = HXR_OK;
    m_pMutex->Lock();
    // Declaring these here so I can use a goto below!
    CHXString streamSequenceNumbers;
    BOOL bIsFirst = TRUE;
    CHXMapLongToObj::Iterator i;

    RTSPRecordMessage* pMsg = new RTSPRecordMessage;
    if(!pMsg)
    {
        rc =  HXR_OUTOFMEMORY;
        goto overandout;
    }

    pMsg->setURL(m_url);
    AddCommonHeaderToMsg(pMsg);

    /*
     * Add header for sequence numbers
     */

    for(i=m_pTransportStreamMap->Begin(); i!=m_pTransportStreamMap->End(); ++i)
    {

	int lenTmpBuf = 100 + strlen(m_url);
	char* tmpBuf = new char[lenTmpBuf];
        if(!tmpBuf)
        {
            HX_DELETE(pMsg);
            rc =  HXR_OUTOFMEMORY;
            goto overandout;
        }

	RTSPTransport* pTransport = (RTSPTransport*)(*i);
	pTransport->m_bHackedRecordFlag = TRUE;
	UINT16 streamNumber = (UINT16)i.get_key();
	UINT16 seqNum = pTransport->getSeqNum(streamNumber);
	UINT32 ulTimestamp = pTransport->getTimestamp(streamNumber);
	SafeSprintf(tmpBuf, lenTmpBuf, "url=" + m_url +
	    "/streamid=%d;seq=%d;rtptime=%ld", streamNumber, seqNum,
	    ulTimestamp);
	if(!bIsFirst)
	{
	    streamSequenceNumbers += ", " + CHXString(tmpBuf);
	}
	else
	{
	    bIsFirst = FALSE;
	    streamSequenceNumbers = tmpBuf;
	}

	delete[] tmpBuf;
    }
    pMsg->addHeader("RTP-Info", streamSequenceNumbers);
    if( m_pSession )
    {
        UINT32 seqNo = m_pSession->getNextSeqNo(this);
        rc = sendRequest(pMsg, seqNo);
    }
overandout:
    m_pMutex->Unlock();
    return rc;
}

STDMETHODIMP
RTSPClientProtocol::SendPauseRequest()
{
    m_bPaused = TRUE;

    /*
     * Stop the internal buffer timers
     */
    if (!m_pIsMethodSupported[PAUSE] || m_transportRequestList.IsEmpty() ||
        !m_pSession)
    {
	return HXR_OK;
    }

    m_pMutex->Lock();

    // only used when m_bNonRSRTP is TRUE
    m_bPlayJustSent = FALSE;

    SendMsgToTransport(PAUSE_BUFFER);

    HX_RESULT rc = SendMsgToServer(RTSP_PAUSE);

    m_pMutex->Unlock();
    return rc;
}

STDMETHODIMP
RTSPClientProtocol::SendResumeRequest()
{
    m_bPaused = FALSE;

    if (!m_pSession)
    {
	return HXR_OK;
    }

    /*
     * Restart the internal buffer timers
     */

    m_pMutex->Lock();

    SendMsgToTransport(RESUME_BUFFER);

    /*
     * Man, iptv, teracast, and darwin server don't like this even though
     * this is perfetly legal...
     */
    if (m_bNonRSRTP && m_bPlayJustSent)
    {
	m_pResp->HandlePlayResponse(HXR_OK);
	m_pMutex->Unlock();
	return HXR_OK;
    }

    HX_RESULT rc = SendMsgToServer(RTSP_PLAY);

    m_pMutex->Unlock();
    return rc;
}

STDMETHODIMP
RTSPClientProtocol::SendTeardownRequest()
{
    // make sure not to send a TEARDOWN unless SETUP succeeded
    if (m_setupResponseCount <= 0 || !m_pSession)
    {
	// no successful SETUP response received...
	return HXR_OK;
    }

    // it's ok if there is no session by spec.
    m_pMutex->Lock();
    HX_RESULT rc = SendMsgToServer(RTSP_TEARDOWN);
    m_pMutex->Unlock();
    return rc;
}

STDMETHODIMP
RTSPClientProtocol::SendPlayerStats(const char* pStats)
{
#if defined(HELIX_FEATURE_STATS) && defined(HELIX_FEATURE_REGISTRY)
    if (!m_pIsMethodSupported[SET_PARAM])
    {
	return HXR_OK;
    }

    if(m_pSession && !m_sessionID.IsEmpty())
    {
	HX_RESULT rc = HXR_OK;
	m_pMutex->Lock();
	RTSPSetParamMessage* pMsg = new RTSPSetParamMessage;
        if(pMsg)
        {
	    pMsg->setURL(m_url);
	    pMsg->addHeader("Session", m_sessionID);
	    pMsg->addHeader("PlayerStats", pStats);
	    UINT32 seqNo = m_pSession->getNextSeqNo(this);
	    rc = sendRequest(pMsg, seqNo);
        }
        else
        {
            rc = HXR_OUTOFMEMORY;
        }
	m_pMutex->Unlock();
	return rc;
    }
#endif /* HELIX_FEATURE_STATS && HELIX_FEATURE_REGISTRY */

    return HXR_OK;
}

STDMETHODIMP
RTSPClientProtocol::SendKeepAlive()
{
    HX_RESULT rc = HXR_OK;

    // XXXSMP - Not right! :-)
    m_pMutex->Lock();

    if (!m_pSession)
    {
	// just say alive!
	m_pMutex->Unlock();
	return HXR_OK;
    }

    // If using session timeout code, send an Options message,
    // otherwise, send a SetParam.  The SetParam approach is
    // is for servers that do not specify a session timeout value.
    if (!m_bUseLegacyTimeOutMsg ||
        !m_pIsMethodSupported[SET_PARAM] ||
        m_bNoKeepAlive)
    {
        m_bKeepAlivePending = TRUE;
        rc = SendMsgToServer(RTSP_OPTIONS);
    }
    else
    {
        RTSPSetParamMessage* pMsg = new RTSPSetParamMessage;
        if(pMsg)
        {
            pMsg->setURL("*");
            MIMEHeader* pAlertHeader = new MIMEHeader("Ping");
            if(pAlertHeader)
            {
                pAlertHeader->addHeaderValue("Pong");
                pMsg->addHeader(pAlertHeader);

                AddCommonHeaderToMsg(pMsg);

                UINT32 seqNo = m_pSession->getNextSeqNo(this);
                sendRequest(pMsg, seqNo);
            }
            else
            {
                rc = HXR_OUTOFMEMORY;
            }
        }
        else
        {
            rc = HXR_OUTOFMEMORY;
        }
    }

    m_pMutex->Unlock();
    return rc;
}

STDMETHODIMP
RTSPClientProtocol::SendPacket(BasePacket* pPacket)
{
    m_pMutex->Lock();
    HX_RESULT rc = HXR_UNEXPECTED;
    RTSPTransport* pTrans =
	(RTSPTransport*)(*m_pTransportStreamMap)[pPacket->GetStreamNumber()];
    if(pTrans)
    {
	rc = pTrans->sendPacket(pPacket);
    }
    m_pMutex->Unlock();
    return rc;
}

STDMETHODIMP
RTSPClientProtocol::SendStreamDone(UINT16 streamNumber)
{
    m_pMutex->Lock();
    HX_RESULT rc = HXR_UNEXPECTED;
    RTSPTransport* pTrans =
	(RTSPTransport*)(*m_pTransportStreamMap)[streamNumber];
    if(pTrans)
    {
	rc = pTrans->streamDone(streamNumber);
    }
    m_pMutex->Unlock();
    return rc;
}

STDMETHODIMP
RTSPClientProtocol::GetPacket(UINT16 uStreamNumber, REF(IHXPacket*) pPacket)
{
    m_pMutex->Lock();

    /*
     * Must not return HXR_FAIL because player may request a packet
     * before the transport is set up
     */
    HX_RESULT rc = HXR_NO_DATA;

    RTSPTransport* pTrans =
	(RTSPTransport*)(*m_pTransportStreamMap)[uStreamNumber];
    if (pTrans)
    {
	rc = pTrans->getPacket(uStreamNumber, pPacket);
    }

    m_pMutex->Unlock();
    return rc;
}

STDMETHODIMP
RTSPClientProtocol::StartPackets(UINT16 uStreamNumber)
{
    m_pMutex->Lock();
    HX_RESULT rc = HXR_FAIL;
    RTSPTransport* pTrans =
	(RTSPTransport*)(*m_pTransportStreamMap)[uStreamNumber];
    if (pTrans)
    {
	rc = pTrans->startPackets(uStreamNumber);
    }

    m_pMutex->Unlock();
    return rc;
}

STDMETHODIMP
RTSPClientProtocol::StopPackets(UINT16 uStreamNumber)
{
    m_pMutex->Lock();
    HX_RESULT rc = HXR_FAIL;
    RTSPTransport* pTrans =
	(RTSPTransport*)(*m_pTransportStreamMap)[uStreamNumber];
    if (pTrans)
    {
	/*
	* Must not return HXR_FAIL because player may request a packet
	* before the transport is set up
	*/

	rc = pTrans->stopPackets(uStreamNumber);
    }

    m_pMutex->Unlock();
    return rc;
}

/*
 * XXX...This had BETTER GET FIXED when we go to full IRMA
 */

STDMETHODIMP_(IHXPendingStatus*)
RTSPClientProtocol::GetPendingStatus()
{
    AddRef();
    return (IHXPendingStatus*)this;
}

STDMETHODIMP_(IHXStatistics*)
RTSPClientProtocol::GetStatistics()
{
    AddRef();
    return (IHXStatistics*)this;
}

STDMETHODIMP_(BOOL)
RTSPClientProtocol::HttpOnly()
{
    if(m_pSession)
    {
	return m_pSession->HttpOnly();
    }

    return FALSE;
}

/*
 * IHXResolverResponse methods
 */

STDMETHODIMP
RTSPClientProtocol::GetHostByNameDone(HX_RESULT status,
    UINT32 ulAddr)
{
    UINT16	uPort = 0;
    BOOL	bUseProxy = FALSE;
    CHXString	host;

    HX_RELEASE(m_pResolver);

    /* We may have been deleted by now */
    if (!m_pResp)
    {
	return HXR_OK;
    }

    m_pMutex->Lock();
    HX_RESULT rc = HXR_OK;

#if defined(HELIX_FEATURE_TRANSPORT_MULTICAST)
    if (m_bSDPInitiated && m_bMulticast)
    {
        m_ulConnectToAddr = ulAddr;
	m_pResp->InitDone(HXR_OK);

        IHXValues* pResponseHeaders = NULL;
        if (HXR_OK == m_pResponseHeaders->QueryInterface(IID_IHXValues, (void**)&pResponseHeaders))
        {
	    rc = m_pResp->HandleStreamDescriptionResponse
	    (
	        HXR_OK,
	        m_pSDPFileHeader,
	        m_pSDPStreamHeaders,
	        pResponseHeaders
	    );
        }
        HX_RELEASE(pResponseHeaders);

        RemoveSDPHeaders();

        goto exit;
    }
#endif /* HELIX_FEATURE_TRANSPORT_MULTICAST */

    if(status == HXR_OK)
    {
	if (!m_pSessionManager->isValid())
	{
	    // just return, this puppy's going away...
	    goto exit;
	}

	if (!m_pSession && !m_pSocket)
	{
	    RTSPClientSession* pSession = 0;
	    IUnknown* pContextToMatch = NULL;

	    /* Do not reuse connection for connections coming from the
	     * same player. Check for context (IHXPlayer) equality test
	     * Context check is needed to simulate real-world scenario
	     * for SMIL load testing
	     */
	    if (m_bNoReuseConnection && m_bLoadTest)
	    {
		pContextToMatch = m_pContext;
	    }

	    /* We share established connections if
	     * 1. m_bNoReuseConnection is FALSE OR
	     * 2. m_bLoadTest is set to TRUE (in which case we share
	     *    connections ONLY for the same context (see above)
	     * AND
	     * 3. m_bHTTPOnly is FALSE OR
	     * 4. m_pCloakPorts == NULL which means we *not* gonna
	     *    attempt cloakport scanning
	     *
	     * NOTE: Splitter Plugin uses m_bNoReuseConnection = TRUE
	     */
	    if ((!m_bHTTPOnly || !m_pCloakPorts) &&
		(!m_bNoReuseConnection || m_bLoadTest))
	    {
		if(m_bUseProxy || m_bUseHTTPProxy)
		{
		    uPort = m_proxyPort;
		}
		else if (m_bHTTPOnly)
		{
		    uPort = m_uCloakPort;
		}
		else
		{
		    uPort = m_foreignPort;
		}

		pSession = m_pSessionManager->findSession
		(
		    ulAddr,
		    uPort,
		    (m_bUseProxy | m_bUseHTTPProxy),
		    (const char*)m_hostName,
		    (m_bUseHTTPProxy?m_uCloakPort:m_foreignPort),
		    pContextToMatch
		);
	    }

	    if(pSession)
	    {
		pSession->addProtocol(this);

		m_bSessionSucceeded = TRUE;
		m_pSession = pSession;
		m_pSocket = pSession->getSocket();

		sendInitialMessage(m_pSession, m_pSocket);

		IHXPreferredTransportSink* pPreferredTransportSink = NULL;
		if (m_pResp &&
		    HXR_OK == m_pResp->QueryInterface(IID_IHXPreferredTransportSink,
						      (void**)&pPreferredTransportSink))
		{
		    pPreferredTransportSink->TransportSucceeded(m_currentTransport, m_uCloakPort);
		}
		HX_RELEASE(pPreferredTransportSink);

		m_pResp->InitDone(HXR_OK);
	    }
	    else
	    {
		if(m_bUseProxy || m_bUseHTTPProxy)
		{
		    host = m_proxyHost;
		    uPort = m_proxyPort;
		    bUseProxy = TRUE;
		}
		else
		{
		    host = m_hostName;
		    uPort = m_foreignPort;
		}

		// XXX HP:	the new session is created based on
		//		the actual(foreign) host and port to
		//		fix SMIL containing diff. servers
		//		the better fix will be send a new
		//		Challenge but still sharing the same
		//.		session
		if (m_pCloakPorts)
		{
		    HX_ASSERT(m_bHTTPOnly);

		    // initiating cloakport scanning
		    for (int i = 0; i < m_nCloakPorts; i++)
		    {
			rc = m_pSessionManager->newSession(m_pContext,
							  this,
							  host,
							  uPort,
							  ulAddr,
							  bUseProxy,
							  m_bHTTPOnly,
							  m_pCloakPorts[i]);

			HX_ASSERT(HXR_OK == rc);
		    }

		    goto exit;
		}
		else
		{
		    rc = m_pSessionManager->newSession
		    (
			m_pContext,
			this,
			host,
			uPort,
			ulAddr,
			bUseProxy,
			m_bHTTPOnly,
			m_uCloakPort
		    );

		    goto exit;
		}
	    }
	}
	else
	{
	    // being re-inited..
	    //sendInitialMessage();
	    m_pResp->InitDone(HXR_OK);
	}
    }
    else
    {
	m_pResp->InitDone(status);
    }

exit:
    m_pMutex->Unlock();
    return rc;
}

STDMETHODIMP
RTSPClientProtocol::SendGetParameterRequest(UINT32 lParamType,
    const char* pParamName)
{
    if (!m_pIsMethodSupported[GET_PARAM])
    {
	return HXR_OK;
    }

    HX_RESULT rc = HXR_OK;
    m_pMutex->Lock();
    RTSPGetParamMessage* pMsg = new RTSPGetParamMessage;
    if(pMsg)
    {
        pMsg->setURL("*");
        AddCommonHeaderToMsg(pMsg);
        UINT32 seqNo = m_pSession->getNextSeqNo(this);
        rc = sendRequest(pMsg, pParamName, "text/rtsp-parameters", seqNo);
    }
    else
    {
        rc = HXR_OUTOFMEMORY;
    }
    m_pMutex->Unlock();
    return rc;
}

STDMETHODIMP
RTSPClientProtocol::SendSetParameterRequest(UINT32 lParamType,
			    const char* pParamName, IHXBuffer* pParamValue)
{
    if (!m_pIsMethodSupported[SET_PARAM])
    {
	return HXR_OK;
    }

    m_pMutex->Lock();
    RTSPSetParamMessage* pMsg = new RTSPSetParamMessage;
    pMsg->setURL(m_url);
    AddCommonHeaderToMsg(pMsg);

    pMsg->addHeader(pParamName, (const char*)pParamValue->GetBuffer());
    UINT32 seqNo = m_pSession->getNextSeqNo(this);

    HX_RESULT rc = sendRequest(pMsg, seqNo);
    m_pMutex->Unlock();
    return rc;
}

STDMETHODIMP
RTSPClientProtocol::SendSetParameterRequest(const char* pParamName,
	const char* pParamValue, const char* pMimeType,
	const char* pContent)
{
    if (!m_pIsMethodSupported[SET_PARAM])
    {
	return HXR_OK;
    }

    m_pMutex->Lock();
    RTSPSetParamMessage* pMsg = new RTSPSetParamMessage;
    pMsg->setURL(m_url);
    AddCommonHeaderToMsg(pMsg);

    pMsg->addHeader(pParamName, pParamValue);
    UINT32 seqNo = m_pSession->getNextSeqNo(this);

    HX_RESULT rc = sendRequest(pMsg, pContent, pMimeType, seqNo);
    m_pMutex->Unlock();
    return rc;
}

STDMETHODIMP
RTSPClientProtocol::PacketReady(HX_RESULT status, const char* pSessionID,
    IHXPacket* pPacket)
{
    m_pMutex->Lock();
    HX_RESULT rc = m_pResp->HandlePacket(status, pSessionID, pPacket);
    m_pMutex->Unlock();
    return rc;
}

/*
 * OnRTTRequest() and OnBWReport() are server-side functions
 */

STDMETHODIMP
RTSPClientProtocol::OnRTTRequest(HX_RESULT status, const char* pSessionID)
{
    return HXR_UNEXPECTED;
}

STDMETHODIMP
RTSPClientProtocol::OnRTTResponse(HX_RESULT status, const char* pSessionID,
    UINT32 ulSecs, UINT32 ulUSecs)
{
    m_pMutex->Lock();
    HX_RESULT rc = m_pResp->HandleRTTResponse(status, pSessionID,
					      ulSecs, ulUSecs);
    m_pMutex->Unlock();
    return rc;
}

STDMETHODIMP
RTSPClientProtocol::OnBWReport(HX_RESULT status, const char* pSessionID,
    INT32 aveBandwidth, INT32 packetLoss, INT32 bandwidthWanted)
{
    return HXR_UNEXPECTED;
}

STDMETHODIMP
RTSPClientProtocol::OnCongestion(HX_RESULT status, const char* pSessionID,
    INT32 xmitMultiplier, INT32 recvMultiplier)
{
    m_pMutex->Lock();
    HX_RESULT rc = m_pResp->HandleCongestion(status, pSessionID,
					xmitMultiplier, recvMultiplier);
    m_pMutex->Unlock();
    return rc;
}

STDMETHODIMP
RTSPClientProtocol::OnACK(HX_RESULT status, RTSPResendBuffer* pResendBuffer,
    UINT16 uStreamNumber, const char* pSessionID,
    UINT16* pAckList, UINT32 uAckListCount,
    UINT16* pNakList, UINT32 uNakListCount)
{
    /*
     * While it's ACKing, remote client is alive
     */

    m_bConnectionAlive = TRUE;

    m_pMutex->Lock();
    HX_RESULT rc = handleACK((IHXPacketResend*)this, pResendBuffer,
			     uStreamNumber,
			     pAckList, uAckListCount,
			     pNakList, uNakListCount,
			     FALSE);
    m_pMutex->Unlock();
    return rc;
};

STDMETHODIMP
RTSPClientProtocol::OnStreamDone(HX_RESULT status, UINT16 uStreamNumber)
{
    m_pMutex->Lock();
    HX_RESULT rc = m_pResp->HandleStreamDone(status, uStreamNumber);
    m_pMutex->Unlock();
    return rc;
}

STDMETHODIMP
RTSPClientProtocol::OnSourceDone(void)
{
    m_pMutex->Lock();
    HX_RESULT rc = m_pResp->HandleSourceDone();
    m_pMutex->Unlock();
    return rc;
}

STDMETHODIMP
RTSPClientProtocol::OnProtocolError(HX_RESULT status)
{
    HX_RESULT rc = HXR_OK;
    // called from transport layer
    m_pMutex->Lock();
    if (m_sessionList.IsEmpty() || m_bSessionSucceeded)
    {
	rc = m_pResp->HandleProtocolError(status);
    }
    m_pMutex->Unlock();
    return rc;
}

STDMETHODIMP
RTSPClientProtocol::GetStatus
(
    REF(UINT16) uStatusCode,
    REF(IHXBuffer*) pStatusDesc,
    REF(UINT16) ulPercentDone
)
{
#if 0
    m_pMutex->Lock();
    HX_RESULT rc = HXR_OK;

    CHXMapLongToObj::Iterator i = m_pTransportStreamMap->Begin();
    if(i != m_pTransportStreamMap->End())
    {
	RTSPTransport* pTrans = (RTSPTransport*)(*i);
	rc = pTrans ? pTrans->getStatus(uStatusCode, pStatusDesc, ulPercentDone) : HXR_OK;
    }
    else
    {
	uStatusCode = HX_STATUS_BUFFERING;
	pStatusDesc = 0;
	ulPercentDone = 0;
    }

    m_pMutex->Unlock();
    return rc;
#else
    return HXR_NOTIMPL;
#endif
}

STDMETHODIMP
RTSPClientProtocol::InitializeStatistics
(
    UINT32 ulRegistryID
)
{
    HX_RESULT rc = HXR_FAIL;
    m_pMutex->Lock();
    CHXMapLongToObj::Iterator i = m_pTransportStreamMap->Begin();
    if(i != m_pTransportStreamMap->End())
    {
	RTSPTransport* pTrans = (RTSPTransport*)(*i);
	rc = pTrans ? pTrans->initializeStatistics(ulRegistryID) : HXR_FAIL;
    }
    m_pMutex->Unlock();
    return rc;
}

STDMETHODIMP
RTSPClientProtocol::UpdateStatistics()
{
    HX_RESULT rc = HXR_FAIL;
    m_pMutex->Lock();
    CHXMapLongToObj::Iterator i = m_pTransportStreamMap->Begin();
    if(i != m_pTransportStreamMap->End())
    {
	RTSPTransport* pTrans = (RTSPTransport*)(*i);
	rc = pTrans ? pTrans->updateStatistics() : HXR_FAIL;
    }
    m_pMutex->Unlock();
    return rc;
}

STDMETHODIMP
RTSPClientProtocol::OnPacket(UINT16 uStreamNumber, BasePacket** ppPacket)
{
    BasePacket* pPacket;

    m_pMutex->Lock();
    for (; (pPacket = *ppPacket); ppPacket++)
    {
	SendPacket(pPacket);
    }
    m_pMutex->Unlock();
    return HXR_OK;
}

/*
 * RTSPClientProtocol methods
 */
HX_RESULT
RTSPClientProtocol::HandleUnexpected(RTSPMessage* pMsg)
{
    m_pMutex->Lock();
    RTSPResponseMessage* pRspMsg = makeResponseMessage(pMsg->seqNo(), "405");
    pRspMsg->addHeader("Allow", allowedMethods());
    sendResponse(pRspMsg);
    delete pRspMsg;
    m_pMutex->Unlock();
    return HXR_OK;
}

HX_RESULT
RTSPClientProtocol::HandleBadVersion(RTSPMessage* pMsg)
{
    m_pMutex->Lock();
    RTSPResponseMessage* pRspMsg = makeResponseMessage(pMsg->seqNo(), "505");
    sendResponse(pRspMsg);
    delete pRspMsg;
    m_pMutex->Unlock();
    return HXR_OK;
}

HX_RESULT
RTSPClientProtocol::HandleOptions(RTSPOptionsMessage* pMsg)
{
    sendResponse(pMsg->seqNo(), "200");
    return HXR_OK;
}

HX_RESULT
RTSPClientProtocol::HandleTeardown(RTSPTeardownMessage* pMsg)
{
    m_pMutex->Lock();
    RTSPResponseMessage* pRespMsg = makeResponseMessage(pMsg->seqNo(), "200");
    sendResponse(pRespMsg);
    delete pRespMsg;
    m_pMutex->Unlock();
    return HXR_OK;
}

HX_RESULT
RTSPClientProtocol::HandleGetParam(RTSPGetParamMessage* pMsg)
{
    IHXBuffer* pBuffer = 0;

    m_pMutex->Lock();
    const char* pParamName = pMsg->getContent();
    HX_RESULT rc = m_pResp->HandleGetParameterRequest(
	RTSP_PARAM_STRING, pParamName, &pBuffer);

    RTSPResponseMessage* pRespMsg = 0;
    if(rc == HXR_OK)
    {
	pRespMsg = makeResponseMessage(pMsg->seqNo(), "200");
	sendResponse(pRespMsg, (const char*)pBuffer->GetBuffer(),
	    "text/rtsp-parameters");
    }
    else
    {
	pRespMsg = makeResponseMessage(pMsg->seqNo(), "451");
	sendResponse(pRespMsg);
    }
    delete pRespMsg;
    m_pMutex->Unlock();
    return HXR_OK;
}

HX_RESULT
RTSPClientProtocol::HandleSetParam(RTSPSetParamMessage* pMsg)
{

    RTSPResponseMessage* pRespMsg = 0;
    HX_RESULT rc = HXR_OK;
    IHXValues* pReconnectValues = NULL;
    BOOL paramOK = FALSE;

    m_pMutex->Lock();
    MIMEHeader* pAlert = pMsg->getHeader("Alert");
    MIMEHeader* pMaxASMBW = pMsg->getHeader("MaximumASMBandwidth");
    MIMEHeader* pDataConvert = pMsg->getHeader("DataConvertBuffer");
    MIMEHeader* pReconnect = pMsg->getHeader("Reconnect");
    MIMEHeader* pAlternateServer = pMsg->getHeader("Alternate-Server");
    MIMEHeader* pAlternateProxy = pMsg->getHeader("Alternate-Proxy");

    if(pAlert)
    {
	paramOK = TRUE;
	MIMEHeaderValue* pHeaderValue = pAlert->getFirstHeaderValue();
	if(pHeaderValue)
	{
	    MIMEParameter* pParam = pHeaderValue->getFirstParameter();
	    if(pParam)
	    {
		const char* pAlertNumber = (const char*)pParam->m_attribute;
		pParam = pHeaderValue->getNextParameter();
		if(pParam)
		{
		    const char* pAlertText = (const char*)pParam->m_attribute;
		    rc = m_pResp->HandleAlertRequest(HXR_OK,
			strtol(pAlertNumber, 0, 10), pAlertText);
		}
	    }
	}
    }
    else if(pMaxASMBW)
    {
	paramOK = TRUE;
	MIMEHeaderValue* pHeaderValue = pMaxASMBW->getFirstHeaderValue();
	if(pHeaderValue)
	{
	    MIMEParameter* pParam = pHeaderValue->getFirstParameter();
	    if(pParam)
	    {
		IHXBuffer * pBuffer;
		pBuffer = new CHXBuffer();
                if(pBuffer)
                {
		    rc = pBuffer->Set((const unsigned char *)(pParam->m_attribute).
				GetBuffer(1), strlen((const char*)pParam->
				m_attribute)+1);
                    if( rc != HXR_OUTOFMEMORY )
                    {
		        pBuffer->AddRef();
		        rc = m_pResp->HandleSetParameterRequest(
			    RTSP_PARAM_STRING, "MaximumASMBandwidth", pBuffer);
		        pBuffer->Release();
	            }
                    else
                    {
                        HX_DELETE(pBuffer);
                    }
                }
                else
                {
                    rc = HXR_OUTOFMEMORY;
                }
	    }
	}
    }
    else if (pDataConvert)
    {
	rc = m_pResp->HandleSetParameterRequest("DataConvertBuffer",
						"1", pMsg->getContent());
    }
    else if (pReconnect)
    {
	CHXString reconnectFlag = pMsg->getHeaderValue("Reconnect");
	if (reconnectFlag != "" && strcasecmp((const char*)reconnectFlag, "false") == 0)
	{
	    pReconnectValues = new CHXHeader();
	    pReconnectValues->AddRef();

    	    pReconnectValues->SetPropertyULONG32("Reconnect", 0);
	    rc = m_pResp->HandleSetParameterResponseWithValues(HXR_OK, pReconnectValues);
	    HX_RELEASE(pReconnectValues);
	}
    }
    else if (pAlternateServer)
    {
	rc = RetrieveReconnectInfo(pAlternateServer, ALTERNATE_SERVER, pReconnectValues);
	rc = m_pResp->HandleSetParameterResponseWithValues(HXR_OK, pReconnectValues);
	HX_RELEASE(pReconnectValues);
    }
    else if (pAlternateProxy)
    {
	rc = RetrieveReconnectInfo(pAlternateProxy, ALTERNATE_PROXY, pReconnectValues);
	rc = m_pResp->HandleSetParameterResponseWithValues(HXR_OK, pReconnectValues);
	HX_RELEASE(pReconnectValues);
    }
    else
    {
	rc = HXR_UNEXPECTED;
    }

    if(rc == HXR_OK)
    {
	pRespMsg = makeResponseMessage(pMsg->seqNo(), "200");
    }
    else
    {
	pRespMsg = makeResponseMessage(pMsg->seqNo(), "451");
    }

    sendResponse(pRespMsg);
    delete pRespMsg;
    m_pMutex->Unlock();
    return HXR_OK;
}

HX_RESULT
RTSPClientProtocol::HandleUseProxy(RTSPResponseMessage* pMsg)
{
    m_pMutex->Lock();
    HX_RESULT rc = HXR_OK;

    MIMEHeader* pLocation = pMsg->getHeader("Location");
    if(pLocation)
    {
	MIMEHeaderValue* pURLValue = pLocation->getFirstHeaderValue();
	if(pURLValue)
	{
	    CHXString proxyURL = pURLValue->value();
	    if(proxyURL.GetLength() > 0)
	    {
		rc = m_pResp->HandleUseProxyRequest((const char*)proxyURL);
		goto exit;
	    }
	}
    }
    // bad redirect, inform the response object
    rc = m_pResp->HandleUseProxyRequest(NULL);
exit:
    m_pMutex->Unlock();
    return rc;
}

HX_RESULT
RTSPClientProtocol::HandleRedirect(RTSPRedirectMessage* pMsg)
{
    m_pMutex->Lock();
    HX_RESULT rc = HXR_OK;
    RTSPResponseMessage* pRspMsg = makeResponseMessage(pMsg->seqNo(), "200");
    sendResponse(pRspMsg);
    delete pRspMsg;

    UINT32 msFromNow = 0;

    MIMEHeader* pLocation = pMsg->getHeader("Location");
    if(pLocation)
    {
	MIMEHeader* pRangeHeader = pMsg->getHeader("Range");
	if(pRangeHeader)
	{
	    RTSPRange* pRange = (RTSPRange*)pRangeHeader->getFirstHeaderValue();
	    if(pRange)
	    {
		msFromNow = pRange->m_begin;
	    }
	}
	MIMEHeaderValue* pURLValue = pLocation->getFirstHeaderValue();
	if(pURLValue)
	{
	    CHXString redirectURL = pURLValue->value();
	    if(redirectURL.GetLength() > 0)
	    {
		rc = m_pResp->HandleRedirectRequest((const char*)redirectURL,
		    msFromNow);
		goto exit;
	    }
	}
    }
    rc = m_pResp->HandleRedirectRequest(0, 0);
exit:
    m_pMutex->Unlock();
    return rc;
}

HX_RESULT
RTSPClientProtocol::HandleRedirectResponse(RTSPResponseMessage* pMsg)
{
    m_pMutex->Lock();

    HX_RESULT rc = HXR_OK;
    IHXValues* pRFC822Headers = NULL;
    getRFC822Headers(pMsg, pRFC822Headers);

    if(pRFC822Headers)
    {
	IHXKeyValueList* pRFC822List = NULL;

	if (HXR_OK == pRFC822Headers->QueryInterface(IID_IHXKeyValueList, (void**)&pRFC822List))
	{
	    m_pResponseHeaders->AppendAllListItems(pRFC822List);
	}
	HX_RELEASE(pRFC822List);
    }
    HX_RELEASE(pRFC822Headers);

    // tell them this is a redirect...
    IHXValues* pResponseHeaders = NULL;

    if (HXR_OK == m_pResponseHeaders->QueryInterface(IID_IHXValues, (void**)&pResponseHeaders))
    {
	m_pResp->HandleOptionsResponse(HXR_REDIRECTION, pResponseHeaders);
	HX_RELEASE(pResponseHeaders);
    }
    else
    {
	HX_ASSERT(pResponseHeaders);
	m_pResp->HandleOptionsResponse(HXR_REDIRECTION, NULL);
    }

    UINT32 msFromNow;
    msFromNow = 0;

    MIMEHeader* pLocation;
    pLocation = pMsg->getHeader("Location");
    if(pLocation)
    {
	MIMEHeader* pRangeHeader = pMsg->getHeader("Range");
	if(pRangeHeader)
	{
	    RTSPRange* pRange = (RTSPRange*)pRangeHeader->getFirstHeaderValue();
	    if(pRange)
	    {
		msFromNow = pRange->m_begin;
	    }
	}
	MIMEHeaderValue* pURLValue = pLocation->getFirstHeaderValue();
	if(pURLValue)
	{
	    CHXString redirectURL = pURLValue->value();
	    if(redirectURL.GetLength() > 0)
	    {
		rc = m_pResp->HandleRedirectRequest((const char*)redirectURL,
		    msFromNow);
		goto exit;
	    }
	}
    }

    rc = m_pResp->HandleRedirectRequest(0, 0);
exit:
    m_pMutex->Unlock();
    return rc;
}

void
RTSPClientProtocol::SessionCreated(RTSPClientSession* pSession)
{
    m_sessionList.AddTail(pSession);
}

void
RTSPClientProtocol::SessionSucceeded(RTSPClientSession* pSession,
				     IHXTCPSocket* pSocket)
{
    RTSPClientSession* pTempSession = NULL;

    m_pMutex->Lock();

    m_bSessionSucceeded = TRUE;

    while (!m_sessionList.IsEmpty())
    {
	pTempSession = (RTSPClientSession*)m_sessionList.RemoveHead();
	if (pTempSession != pSession)
	{
	    m_pSessionManager->removeFromSession(this, pTempSession);
	}
    }

    m_pSession = pSession;
    m_pSocket = pSocket;
    m_uCloakPort = pSession->m_uCloakPort;

    m_pMutex->Unlock();

    return;
}

void
RTSPClientProtocol::SessionFailed(RTSPClientSession* pSession,
				  IHXTCPSocket* pSocket)
{
    LISTPOSITION lPos = NULL;

    m_pMutex->Lock();
    lPos = m_sessionList.Find((void*)pSession);
    if (lPos)
    {
	m_sessionList.RemoveAt(lPos);
    }
    m_pMutex->Unlock();

    return;
}

HX_RESULT
RTSPClientProtocol::InitDone(HX_RESULT status)
{
    HX_RESULT rc = HXR_OK;

    AddRef();
    m_pMutex->Lock();
    if (m_sessionList.IsEmpty()	||
	m_bSessionSucceeded	||
	HXR_OK == status)
    {
	rc = m_pResp->InitDone(status);
    }
    m_pMutex->Unlock();
    Release();
    return rc;
}

STDMETHODIMP
RTSPClientProtocol::InitSockets()
{
    HX_RESULT		    hr = HXR_OK;
    UINT32		    nMaxUDPPort = MAX_UDP_PORT;
    IHXBuffer*		    pBuffer = 0;
    RTSPStreamInfo*	    pStreamInfo = NULL;
    BOOL		    bGotSocket = FALSE;
    BOOL		    bUseUDPPort = FALSE;
    UINT16		    datagramPort = 0;
    UDP_PORTS*		    pUDPPort = NULL;
    CHXSimpleList*	    pUDPPortList = new CHXSimpleList();
    CHXSimpleList::Iterator i;

    AddRef();

    if (!m_pNetworkServices || !m_pPreferences)
    {
	hr = HXR_OUTOFMEMORY;
	goto cleanup;
    }

    m_pUDPSocketStreamMap = new CHXMapLongToObj;
    m_pRTCPSocketStreamMap = new CHXMapLongToObj;

#if defined(HELIX_FEATURE_TRANSPORT_MULTICAST)
    if (m_bSDPInitiated && m_bMulticast)
    {
        for(i=m_streamInfoList.Begin();i!=m_streamInfoList.End() && HXR_OK == hr;++i)
        {
	    pStreamInfo = (RTSPStreamInfo*)(*i);
            hr = CreateUDPSockets(pStreamInfo->m_streamNumber, pStreamInfo->m_sPort);
        }
    }
    else
#endif /* HELIX_FEATURE_TRANSPORT_MULTICAST */
    {
        /////////////////////////////////////////////////////////////
        //
        // Handle Specific UDP Port Preferences here....
        //
        ReadPrefBOOL(m_pPreferences, "UseUDPPort", bUseUDPPort);
        if(!bUseUDPPort)
        {
            // If the MaxUDPPort Preference is set, use that instead of our defined limit
            if (HXR_OK == ReadPrefINT32(m_pPreferences, "MaxUDPPort", nMaxUDPPort))
            {
	        if(nMaxUDPPort < MIN_UDP_PORT)
	        {
		    nMaxUDPPort = MAX_UDP_PORT;
	        }
	    }

	    pUDPPort = new UDP_PORTS;
	    pUDPPort->uFrom = MIN_UDP_PORT;
	    pUDPPort->uTo = nMaxUDPPort;

	    pUDPPortList->AddTail((void*)pUDPPort);
        }
        else
        {
	    if(m_pPreferences->ReadPref("UDPPort", pBuffer) == HXR_OK)
	    {
	        ReadUDPPorts(pBuffer, pUDPPortList);
	    }
        }
        HX_RELEASE(pBuffer);

        for(i=m_streamInfoList.Begin();i!=m_streamInfoList.End();++i)
        {
	    pStreamInfo = (RTSPStreamInfo*)(*i);

    	    CHXSimpleList::Iterator lIterator = pUDPPortList->Begin();
	    for (; lIterator != pUDPPortList->End(); ++lIterator)
	    {
	        pUDPPort = (UDP_PORTS*) (*lIterator);

	        if ((pUDPPort->uTo - pUDPPort->uFrom + 1) < 2)
	        {
		    continue;
	        }

	        for (datagramPort = pUDPPort->uFrom; datagramPort <= pUDPPort->uTo; datagramPort += 2)
	        {
		    if (datagramPort % 2)
		    {
		        datagramPort = datagramPort + 1;
		    }

		    if ((pUDPPort->uTo - datagramPort + 1) < 2)
		    {
		        break;
		    }

                    if (HXR_OK == CreateUDPSockets(pStreamInfo->m_streamNumber, datagramPort))
                    {
                        bGotSocket = TRUE;
                        break;
                    }
	        }

	        if (bGotSocket)
	        {
		    break;
	        }
	    }
        }

        m_currentTransport = UDPMode;
    }

cleanup:

    if (HXR_OK != hr)
    {
	HX_DELETE(m_pUDPSocketStreamMap);
	HX_DELETE(m_pRTCPSocketStreamMap);
    }

    while (pUDPPortList->GetCount())
    {
	pUDPPort = (UDP_PORTS*)pUDPPortList->RemoveHead();
	HX_DELETE(pUDPPort);
    }
    HX_DELETE(pUDPPortList);

    Release();
    return hr;
}

STDMETHODIMP
RTSPClientProtocol::GetCurrentBuffering(UINT16	    uStreamNumber,
				        REF(INT64)  llLowestTimestamp,
				        REF(INT64)  llHighestTimestamp,
				        REF(UINT32) ulNumBytes,
				        REF(BOOL)   bDone)
{
    llLowestTimestamp	= 0;
    llHighestTimestamp	= 0;
    ulNumBytes		= 0;
    bDone		= FALSE;

    HX_ASSERT(m_pTransportStreamMap);

    if (!m_pTransportStreamMap)
    {
	return HXR_OK;
    }

    HX_RESULT rc = HXR_OK;
    m_pMutex->Lock();
    RTSPTransport* pTrans =
	(RTSPTransport*)(*m_pTransportStreamMap)[uStreamNumber];

    HX_ASSERT(pTrans);

    rc = pTrans ?
	    pTrans->GetCurrentBuffering(uStreamNumber,
					llLowestTimestamp,
					llHighestTimestamp,
					ulNumBytes,
					bDone) : HXR_OK;
    m_pMutex->Unlock();
    return rc;
}

STDMETHODIMP
RTSPClientProtocol::SeekFlush()
{
    HX_RESULT rc = HXR_OK;
    m_pMutex->Lock();

    CHXMapLongToObj::Iterator i;
    for(i=m_pTransportStreamMap->Begin();
	    (rc == HXR_OK) &&  i!=m_pTransportStreamMap->End(); ++i)
    {
	RTSPTransport* pTransport = (RTSPTransport*)(*i);
	UINT16 streamNumber = (UINT16)i.get_key();

	HX_ASSERT(pTransport);

	rc = pTransport ? pTransport->SeekFlush(streamNumber) : HXR_OK;
    }

    m_pMutex->Unlock();
    return rc;
}

STDMETHODIMP_(BOOL)
RTSPClientProtocol::IsDataReceived(void)
{
    m_pMutex->Lock();
    BOOL bReceived = FALSE;
    CHXMapLongToObj::Iterator i = m_pTransportStreamMap->Begin();
    if(i != m_pTransportStreamMap->End())
    {

	RTSPTransport* pTrans = (RTSPTransport*)(*i);
	bReceived = pTrans ? pTrans->IsDataReceived() : FALSE;
    }

    m_pMutex->Unlock();
    return bReceived;
}

STDMETHODIMP_(BOOL)
RTSPClientProtocol::IsSourceDone(void)
{
    m_pMutex->Lock();
    BOOL bDone = FALSE;
    CHXMapLongToObj::Iterator i = m_pTransportStreamMap->Begin();
    if(i != m_pTransportStreamMap->End())
    {
	RTSPTransport* pTrans = (RTSPTransport*)(*i);
	bDone = pTrans ? pTrans->IsSourceDone() : FALSE;
    }

    m_pMutex->Unlock();
    return bDone;
}

STDMETHODIMP
RTSPClientProtocol::RuleChange(CHXSimpleList* pSubList)
{
    if (!m_pIsMethodSupported[SET_PARAM] || !m_pSession)
    {
	return HXR_OK;
    }

    m_pMutex->Lock();

    RTSPSetParamMessage* pMsg = new RTSPSetParamMessage;
    pMsg->setURL(m_url);

    CHXString SubString;
    CHXString UnSubString;

    CHXSimpleList::Iterator i;
    BOOL bFirstSub = TRUE;
    BOOL bFirstUnSub = TRUE;
    for(i=pSubList->Begin(); i!=pSubList->End(); ++i)
    {
	char tmp[64];
        RTSPSubscription* pSub = (RTSPSubscription*)(*i);

	SafeSprintf(tmp, 64, "stream=%d;rule=%ld", pSub->m_streamNumber,
	    pSub->m_ruleNumber);

	if (pSub->m_bIsSubscribe)
	{
	    if(!bFirstSub)
	    {
		SubString += "," + CHXString(tmp);
	    }
	    else
	    {
		SubString += tmp;
		bFirstSub = FALSE;
	    }
	}
	else
	{
	    if(!bFirstUnSub)
	    {
		UnSubString += "," + CHXString(tmp);
	    }
	    else
	    {
		UnSubString += tmp;
		bFirstUnSub = FALSE;
	    }
	}
    }
    if (!bFirstSub)
    {
	pMsg->addHeader("Subscribe", (const char*)SubString);
    }
    if (!bFirstUnSub)
    {
	pMsg->addHeader("UnSubscribe", (const char*)UnSubString);
    }
    if (!m_sessionID.IsEmpty())
    {
	pMsg->addHeader("Session", m_sessionID);
    }

    UINT32 seqNo = m_pSession->getNextSeqNo(this);
    HX_RESULT rc = sendRequest(pMsg, seqNo);
    m_pMutex->Unlock();
    return rc;
}

STDMETHODIMP
RTSPClientProtocol::Subscribe(CHXSimpleList* pSubList)
{
    return RuleChange(pSubList);
}

STDMETHODIMP
RTSPClientProtocol::Unsubscribe(CHXSimpleList* pUnsubList)
{
    return RuleChange(pUnsubList);
}

STDMETHODIMP
RTSPClientProtocol::BackChannelPacketReady(IHXPacket* pPacket)
{
    if (!m_pIsMethodSupported[SET_PARAM])
    {
	return HXR_OK;
    }
    m_pMutex->Lock();

    RTSPSetParamMessage* pMsg = new RTSPSetParamMessage;
    IHXBuffer* pBuffer = pPacket->GetBuffer();

    pMsg->setURL(m_url);

    char* pEncodedBuffer =
	new char[pBuffer->GetSize() * 2 + 4]; // XXXSMP Overkill

    BinTo64(pBuffer->GetBuffer(), pBuffer->GetSize(), pEncodedBuffer);
	int lenTmpBuf = strlen(pEncodedBuffer)+12;
    char* tmpBuf = new char[lenTmpBuf];
    SafeSprintf(tmpBuf, lenTmpBuf, "\"%s\"", pEncodedBuffer);
    pMsg->addHeader("BackChannel", tmpBuf);
    SafeSprintf(tmpBuf, lenTmpBuf, "%d", pPacket->GetStreamNumber());
    pMsg->addHeader("StreamNumber", tmpBuf);
    if (!m_sessionID.IsEmpty())
    {
	pMsg->addHeader("Session", m_sessionID);
    }
    delete[] tmpBuf;

    UINT32 seqNo = m_pSession->getNextSeqNo(this);

    pBuffer->Release();
    delete[] pEncodedBuffer;

    HX_RESULT rc = sendRequest(pMsg, seqNo);
    m_pMutex->Unlock();
    return rc;
}

STDMETHODIMP
RTSPClientProtocol::SendRTTRequest()
{
    return DoSendRTTRequest();
}

STDMETHODIMP
RTSPClientProtocol::SendBWReport(INT32 aveBandwidth,
				INT32 packetLoss,
				INT32 bandwidthWanted)
{
    return DoSendBWReport(aveBandwidth, packetLoss, bandwidthWanted);
}

HX_RESULT
RTSPClientProtocol::DoSendRTTRequest(void)
{
    return HXR_OK;
}

HX_RESULT
RTSPClientProtocol::DoSendBWReport(INT32 aveBandwidth,
				   INT32 packetLoss,
				   INT32 bandwidthWanted)
{
    return HXR_OK;
}

HX_RESULT
RTSPClientProtocol::sendRequest(RTSPRequestMessage* pMsg,
                                UINT32 seqNo)
{
    messageDebugFileOut((const char*)pMsg->asString(), FALSE); 

    // Our legacy timeout approach was to periodically send messages
    // to the server.  Currently, we only send a keep alive message
    // if we have not sent an rtsp message for the duration
    // of the timeout value.
    if (m_pSessionTimeout && !m_bUseLegacyTimeOutMsg)
    {
        m_pSessionTimeout->OnActivity();
    }

    return RTSPBaseProtocol::sendRequest(pMsg, seqNo);
}

HX_RESULT
RTSPClientProtocol::sendRequest(RTSPRequestMessage* pMsg,
                                const char* pContent,
                                const char* pMimeType,
                                UINT32 seqNo)
{
    messageDebugFileOut((const char*)pMsg->asString(), FALSE); 

    if (m_pSessionTimeout && !m_bUseLegacyTimeOutMsg)
    {
        m_pSessionTimeout->OnActivity();
    }

    return RTSPBaseProtocol::sendRequest(pMsg, pContent, pMimeType, seqNo);
}

BOOL
RTSPClientProtocol::IsRealServer(void)
{
    return FALSE;
}

STDMETHODIMP
RTSPClientProtocol::SetFirstSeqNum(UINT16 uStreamNumber, UINT16 uSeqNum)
{
    m_pMutex->Lock();
    RTSPTransport* pTrans =
	(RTSPTransport*)(*m_pTransportStreamMap)[uStreamNumber];

    if(pTrans)
    {
	pTrans->setFirstSeqNum(uStreamNumber, uSeqNum);
//	pTrans->setFirstTimeStamp(uStreamNumber, ulTimeStamp);
    }

    m_pMutex->Unlock();

    return HXR_OK;
}

STDMETHODIMP
RTSPClientProtocol::SetRTPInfo(UINT16 uStreamNumber, UINT16 uSeqNum,
    UINT32 ulRTPTime, RTPInfoEnum info)
{
    m_pMutex->Lock();
    HX_ASSERT(RTPINFO_ERROR != info);

    RTSPTransport* pTrans =
	(RTSPTransport*)(*m_pTransportStreamMap)[uStreamNumber];

    if(pTrans)
    {
	/*
	*  RTPTransport needs to know exactly what's in RTP-Info
	*/
	if (RTPINFO_SEQ_RTPTIME == info)
	{
	    pTrans->setFirstSeqNum(uStreamNumber, uSeqNum);
	    pTrans->setFirstTimeStamp(uStreamNumber, ulRTPTime);
	}
	else if (RTPINFO_SEQ == info)
	{
	    pTrans->setFirstSeqNum(uStreamNumber, uSeqNum);
	}
	else if (RTPINFO_RTPTIME == info)
	{
	    pTrans->setFirstTimeStamp(uStreamNumber, ulRTPTime);
	}
	else if (RTPINFO_EMPTY == info)
	{
	    pTrans->notifyEmptyRTPInfo();
	}
    }

    m_pMutex->Unlock();

    return HXR_OK;
}

HX_RESULT
RTSPClientProtocol::ReadDoneWithToPort(HX_RESULT status, IHXBuffer* pBuffer,
				       UINT32 ulAddr, UINT16 nFromPort, UINT16 nToPort)
{
    HX_RESULT hresult = HXR_OK;
    BOOL bMCastPort = FALSE;
    RTSPTransport* pTrans = NULL;

    /*
     * XXX HP: While handling the m_pData->done it's possible for the
     *         DispatchMessage call in CancelSelect to cause an
     *         asynchronous DoRead to occur. m_pTransportPortMap has
     *	       been deleted inside Done() and we should add checkpoint
     *	       here!!
     */
    if (m_bClientDone)
    {
	return hresult;
    }

    m_pMutex->Lock();

    if(status == HXR_OK)
    {
	//XXXBAB - need to get transport by port
	pTrans = (RTSPTransport*)(*m_pTransportPortMap)[nToPort];
	if (!pTrans)
	{
	    pTrans = (RTSPTransport*)(*m_pTransportMPortMap)[nToPort];
	    bMCastPort = TRUE;
	    m_currentTransport = MulticastMode;
	}

        if (pTrans)
        {
            // make sure the unicast packets received are coming from the same server
            // we are connecting to
            if ((m_ulConnectToAddr == ulAddr) || bMCastPort)
            {
	        if (!m_bReportedSuccessfulTransport)
	        {
		    m_bReportedSuccessfulTransport = TRUE;

		    IHXPreferredTransportSink* pPreferredTransportSink = NULL;
		    if (m_pResp &&
		        HXR_OK == m_pResp->QueryInterface(IID_IHXPreferredTransportSink,
						          (void**)&pPreferredTransportSink))
		    {
		        pPreferredTransportSink->TransportSucceeded(m_currentTransport, m_uCloakPort);
		    }
		    HX_RELEASE(pPreferredTransportSink);
	        }

                // XXX HP we use PacketReady() to indicate the liveness of UDP connection
                // for server timeout detection
                PacketReady(HXR_OK, m_sessionID, NULL);

                // drop all the scalable multicast packets when we are paused
                if ((MulticastMode != m_currentTransport) || !m_bSDPInitiated || !m_bPaused)
                {
	            hresult = pTrans->handlePacket(pBuffer);
	            if (m_bSplitterConsumer)
	            {
		        pTrans->releasePackets();
	            }
                }
            }
            else
            {
                // XXX HP, invalid packets such as sent from 3rd party box
                HX_ASSERT(FALSE);
            }

	    if (hresult == HXR_OK || hresult == HXR_UNEXPECTED)
	    {
	        if (bMCastPort)
	        {
		    IHXUDPSocket* pSocket = pTrans->getMulticastSocket();
		    hresult = pSocket->Read(HX_SAFEUINT(MAX_UDP_PACKET));
	        }
	        else
	        {
		    IHXUDPSocket* pSocket = pTrans->getUDPSocket();
		    hresult = pSocket->Read(HX_SAFEUINT(MAX_UDP_PACKET));
	        }
	    }
        }
    }
    else
    {
	hresult = PacketReady(HXR_FAIL, m_sessionID, 0);
    }

    m_pMutex->Unlock();
    return hresult;
}

STDMETHODIMP
RTSPClientProtocol::SetConnectionTimeout(UINT32 uSeconds)
{
    m_uConnectionTimeout = uSeconds;

    return HXR_OK;
}

STDMETHODIMP
RTSPClientProtocol::SetResendBufferDepth(UINT32 uSeconds)
{
    m_ulBufferDepth = uSeconds * 1000;

    return HXR_OK;
}


/*
 * IHXTransportSyncServer methods
 */
STDMETHODIMP
RTSPClientProtocol::DistributeSyncAnchor(ULONG32 ulHXTime,
					ULONG32 ulNTPTime)
{
    m_pMutex->Lock();

    if (!m_transportRequestList.IsEmpty())
    {
	RTSPTransportRequest* pRequest =
	    (RTSPTransportRequest*)m_transportRequestList.GetHead();
	RTSPTransportInfo* pTransInfo = pRequest->getFirstTransportInfo();
	while(pTransInfo)
	{
	    pTransInfo->m_pTransport->anchorSync(ulHXTime, ulNTPTime);
	    pTransInfo = pRequest->getNextTransportInfo();
	}
    }

    m_pMutex->Unlock();

    return HXR_OK;
}

STDMETHODIMP
RTSPClientProtocol::DistributeSync(ULONG32 ulHXTime,
				   LONG32 lHXTimeOffset)
{
    m_pMutex->Lock();

    if (!m_transportRequestList.IsEmpty())
    {
	RTSPTransportRequest* pRequest =
	    (RTSPTransportRequest*)m_transportRequestList.GetHead();
	RTSPTransportInfo* pTransInfo = pRequest->getFirstTransportInfo();
	while(pTransInfo)
	{
	    pTransInfo->m_pTransport->handleMasterSync(ulHXTime,
						       lHXTimeOffset);
	    pTransInfo = pRequest->getNextTransportInfo();
	}
    }

    m_pMutex->Unlock();

    return HXR_OK;
}

STDMETHODIMP
RTSPClientProtocol::DistributeStartTime(ULONG32 ulHXRefTime)
{
    return HXR_NOTIMPL;
}

/*
 * IHXTransportBufferLimit methods
 */

/************************************************************************
 *	Method:
 *	    IHXTransportBufferLimit::SetByteLimit
 *	Purpose:
 *      Sets the maximum number of bytes that can be buffered in the
 *      transport buffer. If incomming packets would put us over this
 *      limit, then they are replaced with lost packets. A byte limit
 *      of 0 means unlimited buffering.
 */
STDMETHODIMP
RTSPClientProtocol::SetByteLimit(UINT16 uStreamNumber, UINT32 uByteLimit)
{
    HX_RESULT res = HXR_FAILED;

    m_pMutex->Lock();

    RTSPTransportBuffer* pTransBuf = getTransportBuffer(uStreamNumber);

    if (pTransBuf)
    {
	pTransBuf->SetByteLimit(uByteLimit);
	res = HXR_OK;
    }

    m_pMutex->Unlock();

    return res;
}

/************************************************************************
 *	Method:
 *	    IHXTransportBufferLimit::GetByteLimit
 *	Purpose:
 *      Returns the current byte limit in effect. A value of 0 means
 *      unlimited buffering is allowed
 */
STDMETHODIMP_(UINT32)
RTSPClientProtocol::GetByteLimit(UINT16 uStreamNumber)
{
    UINT32 ulRet = 0;

    m_pMutex->Lock();

    RTSPTransportBuffer* pTransBuf = getTransportBuffer(uStreamNumber);

    if (pTransBuf)
    {
	ulRet = pTransBuf->GetByteLimit();
    }

    m_pMutex->Unlock();

    return ulRet;
}

/*
 * private RTSPClientProtocol methods
 */

HX_RESULT
RTSPClientProtocol::handleMessage(RTSPMessage* pMsg)
{
    HX_RESULT rc = HXR_OK;

    m_pMutex->Lock();
    messageDebugFileOut((const char*)pMsg->asString(), TRUE);
    if(pMsg->tag() != RTSPMessage::T_RESP)
    {
	int majorVersion = pMsg->majorVersion();
	int minorVersion = pMsg->minorVersion();
	if((majorVersion == 0 && minorVersion == 0) ||
	   (majorVersion > RTSPMessage::MAJ_VERSION))
	{
	    rc = HandleBadVersion(pMsg);
	    goto exit;
	}
	else if(minorVersion > RTSPMessage::MIN_VERSION)
	{
	    rc = HandleBadVersion(pMsg);
	    goto exit;
	}
    }

    // XXX HP we use PacketReady() to indicate the liveness of TCP connection
    // for server timeout detection
    if (TCPMode == m_currentTransport ||
        HTTPCloakMode == m_currentTransport)
    {
        PacketReady(HXR_OK, m_sessionID, NULL);
    }

    switch(pMsg->tag())
    {
	case RTSPMessage::T_OPTIONS:
	{
	    rc = HandleOptions((RTSPOptionsMessage*)pMsg);
	    goto exit;
	}

	case RTSPMessage::T_SET_PARAM:
	{
	    rc = HandleSetParam((RTSPSetParamMessage*)pMsg);
	    goto exit;
	}

	case RTSPMessage::T_REDIRECT:
	{
	    rc = HandleRedirect((RTSPRedirectMessage*)pMsg);
	    goto exit;
	}

	case RTSPMessage::T_RESP:
	{
	    // check for proxy(305) redirect
	    const char* pErrorCode = ((RTSPResponseMessage*)pMsg)->errorCode();
	    if(strcmp(pErrorCode, "305") == 0)
	    {
		rc = HandleUseProxy((RTSPResponseMessage*)pMsg);
		goto exit;
	    }
	    // check for URL redirect
	    else if (strcmp(pErrorCode, "302") == 0 ||
		     strcmp(pErrorCode, "303") == 0)
	    {
	    	rc = HandleRedirectResponse((RTSPResponseMessage*)pMsg);
	    	goto exit;
	    }

	    RTSPMessage* pReqMsg = dequeueMessage(pMsg->seqNo());
	    if(pReqMsg)
	    {
		switch(pReqMsg->tag())
		{
		    case RTSPMessage::T_OPTIONS:
		    {
			rc = handleOptionsResponse((RTSPResponseMessage*)pMsg);
		    }
		    break;

		    case RTSPMessage::T_GET_PARAM:
		    {
			rc = handleGetParamResponse((RTSPResponseMessage*)pMsg);
		    }
		    break;

		    case RTSPMessage::T_SET_PARAM:
		    {
			rc = handleSetParamResponse((RTSPResponseMessage*)pMsg);
		    }
		    break;

		    case RTSPMessage::T_TEARDOWN:
		    {
			rc = handleTeardownResponse((RTSPResponseMessage*)pMsg);
			m_state = RTSPClientProtocol::INIT;
		    }
		    break;

		    case RTSPMessage::T_DESCRIBE:
		    {
			rc = handleDescribeResponse((RTSPResponseMessage*)pMsg);
		    }
		    break;

		    case RTSPMessage::T_ANNOUNCE:
		    {
			rc = handleAnnounceResponse((RTSPResponseMessage*)pMsg);
		    }
		    break;

		    default:
		    {
			switch(m_state)
			{
			    case RTSPClientProtocol::INIT:
			    {
				switch(pReqMsg->tag())
				{
				    case RTSPMessage::T_SETUP:
				    {
					rc = handleSetupResponse(
					    (RTSPResponseMessage*)pMsg,
					    (RTSPSetupMessage*)pReqMsg);
					if(rc == HXR_OK)
					{
					    m_state = RTSPClientProtocol::READY;
					}
				    }
				    break;

				    default:
				    {
					rc = HandleUnexpected(pMsg);
				    }
				    break;
				}
			    }
			    break;

			    case RTSPClientProtocol::READY:
			    {
				switch(pReqMsg->tag())
				{
				    case RTSPMessage::T_SETUP:
				    {
					rc = handleSetupResponse(
					    (RTSPResponseMessage*)pMsg,
					    (RTSPSetupMessage*)pReqMsg);
				    }
				    break;

				    case RTSPMessage::T_PLAY:
				    {
					rc = handlePlayResponse(
					    (RTSPResponseMessage*)pMsg,
					    (RTSPPlayMessage*)pReqMsg);
					if(rc == HXR_OK)
					{
					    m_state =
						RTSPClientProtocol::PLAYING;
					}
				    }
				    break;

				    case RTSPMessage::T_RECORD:
				    {
					rc = handleRecordResponse(
					    (RTSPResponseMessage*)pMsg);
					if(rc == HXR_OK)
					{
					    m_state =
						RTSPClientProtocol::RECORDING;
					}
				    }
				    break;

				    default:
				    {
					rc = HandleUnexpected(pMsg);
				    }
				    break;
				}
			    }
			    break;

			    case RTSPClientProtocol::PLAYING:
			    {
				switch(pReqMsg->tag())
				{
				    case RTSPMessage::T_PLAY:
				    {
					rc = handlePlayResponse(
					    (RTSPResponseMessage*)pMsg,
					    (RTSPPlayMessage*)pReqMsg);
				    }
				    break;

				    case RTSPMessage::T_PAUSE:
				    {
					rc = handlePauseResponse(
					    (RTSPResponseMessage*)pMsg);
					if(rc == HXR_OK)
					{
					    m_state =
						RTSPClientProtocol::READY;
					}
				    }
				    break;

				    default:
				    {
					rc = HandleUnexpected(pMsg);
				    }
				    break;
				}
			    }
			    break;

			    case RTSPClientProtocol::RECORDING:
			    {
				switch(pReqMsg->tag())
				{
				    case RTSPMessage::T_RECORD:
				    {
					rc = handleRecordResponse(
					    (RTSPResponseMessage*)pMsg);
				    }
				    break;

				    case RTSPMessage::T_PAUSE:
				    {
					rc = handlePauseResponse(
					    (RTSPResponseMessage*)pMsg);
					if(rc == HXR_OK)
					{
					    m_state =
						RTSPClientProtocol::READY;
					}
				    }
				    break;

				    default:
				    {
					rc = HandleUnexpected(pMsg);
				    }
				    break;
				}
			    }
			    break;
			}
		    }
		    break;
		}

                // Create and init out server timeout object
                if (!m_pTimeoutCallback)
                {
                    m_pTimeoutCallback = new TimeoutCallback(this);
                    m_pTimeoutCallback->AddRef();
                }

                UINT32 nTimeOut = 0;

                // Set to server timeout value on creation
                if (!m_pSessionTimeout)
                {
                    m_pSessionTimeout = new CHXKeepAlive;
                    nTimeOut = m_ulServerTimeOut;
                }

                // Check for session timeout
                CHXString sessionID = pMsg->getHeaderValue("Session");
                if(sessionID != "")
                {
	            int i;
	            if (-1 != (i = sessionID.Find('=')))
	            {
                        // Wake up early for session timeout since servers will
                        // disconnect if they do not receive messages on time.
                        nTimeOut = atoi(sessionID.Right(sessionID.GetLength()-(i+1)));

                        // Some servers specify timeout in ms not secs
                        if (nTimeOut < 1000)
                            nTimeOut *= 1000;

                        // If session timeout is present, use options message to alert the
                        // server we are still alive.  If not, use setparam.
                        m_bUseLegacyTimeOutMsg = FALSE;

                        // If session timeout value differnt than our current value, we
                        // need to reinit the scheduer.
                        if (nTimeOut == m_ulCurrentTimeOut)
                        {
                            nTimeOut = 0;
                        }
                        // Use the lower of our default timeout and the session timeout
                        else if (m_ulServerTimeOut < nTimeOut)
                        {
                            nTimeOut = m_ulServerTimeOut;
                        }
	            }
                }

		if (nTimeOut && m_pSessionTimeout)
                {
                    m_pSessionTimeout->Init(m_pScheduler,
                                            nTimeOut/2,
                                            (IHXCallback*)m_pTimeoutCallback);

                    m_ulCurrentTimeOut = nTimeOut;
                }

                delete pReqMsg;
	    }
	}
	break;

	default:
	{
	    rc =  HandleUnexpected(pMsg);
	    goto exit;
	}
    }

exit:
    m_pMutex->Unlock();
    return rc;
}

const char*
RTSPClientProtocol::allowedMethods()
{
    return "OPTIONS";
}

HX_RESULT
RTSPClientProtocol::handleTCPData(BYTE* pData, UINT16 dataLen, UINT16 channel)
{
    if (!m_pTransportChannelMap)
	return HXR_FAIL;

    m_pMutex->Lock();
    HX_RESULT rc = HXR_OK;
    CHXBuffer* pBuffer = new CHXBuffer;
    if(!pBuffer)
    {
        rc = HXR_OUTOFMEMORY;
        goto overandout;
    }
    pBuffer->AddRef();
    rc = pBuffer->Set(pData, dataLen);
    if( rc == HXR_OUTOFMEMORY )
    {
        pBuffer->Release();
        goto overandout;
    }

    RTSPTransport* pTrans;
    if (m_pTransportChannelMap->Lookup(channel, (void*&)pTrans))
    {
        if (!m_bReportedSuccessfulTransport)
        {
            m_bReportedSuccessfulTransport = TRUE;
            IHXPreferredTransportSink* pPreferredTransportSink = NULL;
            if (m_pResp &&
                HXR_OK == m_pResp->QueryInterface(IID_IHXPreferredTransportSink,
                                                (void**)&pPreferredTransportSink))
            {
                pPreferredTransportSink->TransportSucceeded(m_currentTransport, m_uCloakPort);
            }
            HX_RELEASE(pPreferredTransportSink);
        }
        
	rc = pTrans->handlePacket(pBuffer);
    }
#ifdef _DEBUG
    else
    {
	HX_ASSERT(!"make sure TransportChannelMap has been set up right...");
    }
#endif

    pBuffer->Release();

overandout:
    m_pMutex->Unlock();
    return rc;
}

/*
 */
SdpFileType
RTSPClientProtocol::GetSdpFileTypeWeNeed(IHXValues* pHeaders)
{
    IHXBuffer* pAgent = NULL;
    SdpFileType sdpType = NONE_SDP;

    /*
     *	Better make sure to come up with a better way to check
     */
    if (FAILED(pHeaders->GetPropertyCString("Server", pAgent)))
    {
	return NONE_SDP;
    }

    if (strstr((const char*)pAgent->GetBuffer(), "RealMedia"))
    {
	sdpType = BACKWARD_COMP_SDP;
    }
    else
    {
	sdpType = INTEROP_SDP;
    }

    HX_RELEASE(pAgent);
    return sdpType;
}

HX_RESULT
RTSPClientProtocol::GetStreamDescriptionInfo(IUnknown* pUnknown, CHXString& mimeTypes)
{
    HX_RESULT               rc = HXR_OK;
    const char*             pMimeType = NULL;
    BOOL                    bIsFirst = TRUE;
    IHXStreamDescription*   pStreamDesc = NULL;

    if (HXR_OK == pUnknown->QueryInterface(IID_IHXStreamDescription,(void**)&pStreamDesc) &&
        pStreamDesc)
    {
	pStreamDesc->GetStreamDescriptionInfo(pMimeType);
	if(bIsFirst)
	{
	    mimeTypes += pMimeType;
	    bIsFirst = FALSE;
	}
	else
	{
	    mimeTypes += ", " + (CHXString)pMimeType;
	}
	pStreamDesc->Release();
    }

    return rc;
}

void
RTSPClientProtocol::SendMsgToTransport(TRANSPORT_MSG msg)
{
    RTSPTransportRequest*   pRequest = NULL;
    RTSPTransportInfo*	    pTransInfo = NULL;

    if (!m_transportRequestList.IsEmpty())
    {
        pRequest = (RTSPTransportRequest*)m_transportRequestList.GetHead();
        pTransInfo = pRequest->getFirstTransportInfo();
        while(pTransInfo)
        {
            switch (msg)
            {
            case ENTER_PREFETCH:
	        pTransInfo->m_pTransport->EnterPrefetch();
                break;
            case LEAVE_PREFETCH:
                pTransInfo->m_pTransport->LeavePrefetch();
                break;
            case ENTER_FASTSTART:
                pTransInfo->m_pTransport->EnterFastStart();
                break;
            case LEAVE_FASTSTART:
                pTransInfo->m_pTransport->LeaveFastStart();
                break;
            case PAUSE_BUFFER:
                pTransInfo->m_pTransport->pauseBuffers();
                break;
            case RESUME_BUFFER:
                pTransInfo->m_pTransport->resumeBuffers();
                break;
            default:
                break;
            }
	    pTransInfo = pRequest->getNextTransportInfo();
        }
    }
}

void
RTSPClientProtocol::AddCommonHeaderToMsg(RTSPRequestMessage* pMsg)
{
    if (pMsg)
    {
        pMsg->addHeader("User-Agent", m_versionString);
        if (!m_sessionID.IsEmpty())
        {
	    pMsg->addHeader("Session", m_sessionID);
        }
    }
}

HX_RESULT
RTSPClientProtocol::SendMsgToServer(RTSPMethod msg)
{
    HX_RESULT           rc = HXR_OK;
    RTSPRequestMessage* pMsg = NULL;

    switch(msg)
    {
    case RTSP_PLAY:
        pMsg = new RTSPPlayMessage;
        break;
    case RTSP_PAUSE:
        pMsg = new RTSPPauseMessage;
        break;
    case RTSP_OPTIONS:
        pMsg = new RTSPOptionsMessage;
        break;
    case RTSP_TEARDOWN:
        pMsg = new RTSPTeardownMessage;
        break;
    default:
        break;
    }

    if (pMsg)
    {
        pMsg->setURL(m_url);
        AddCommonHeaderToMsg(pMsg);

        UINT32 seqNo = m_pSession->getNextSeqNo(this);
        rc = sendRequest(pMsg, seqNo);
    }
    else
    {
        rc = HXR_OUTOFMEMORY;
    }

    return rc;
}

HX_RESULT
RTSPClientProtocol::handleOptionsResponse
(
    RTSPResponseMessage* pRTSPResponseMessageIncoming
)
{
    HX_RESULT   rc = HXR_OK;

    if (pRTSPResponseMessageIncoming->errorCodeAsUINT32() == 551)
    {
	/* Quite poor, but the client only supports this one require for now */
	return m_pResp->HandleOptionsResponse(HXR_LOADTEST_NOT_SUPPORTED,
	    NULL);
    }
    else if(pRTSPResponseMessageIncoming->errorCodeAsUINT32() != 200)
    {
	return m_pResp->HandleOptionsResponse(HXR_FAIL, NULL);
    }
    else
    {
        // Filter out session timeout message reponses
        if (m_bKeepAlivePending)
        {
	    m_bKeepAlivePending = FALSE;
            return HXR_OK;
        }

        /*
	* XXXGH...I added this just for the stats mask, but the
	* authentication should be available from the 822 headers too
	*/

	IHXValues* pRFC822Headers = NULL;
	getRFC822Headers(pRTSPResponseMessageIncoming, pRFC822Headers);

	if (pRFC822Headers)
	{
	    IHXKeyValueList* pRFC822List = NULL;

	    if (HXR_OK == pRFC822Headers->QueryInterface(IID_IHXKeyValueList, (void**)&pRFC822List))
	    {
		m_pResponseHeaders->AppendAllListItems(pRFC822List);
	    }
	    HX_RELEASE(pRFC822List);
	}

	if (m_sessionID.IsEmpty())
	{
    	    m_sessionID = pRTSPResponseMessageIncoming->getHeaderValue("Session");
	}

	// Respond to Client Challenge to prove that we are a RealClient
	if (!m_pSession->m_bChallengeDone &&
	    HXR_OK != RetrieveChallenge(pRTSPResponseMessageIncoming))
	{
	    IHXValues* pResponseHeaders = NULL;

	    // check for supported RTSP methods if the server could be non-RS
            if (m_pResponseHeaders &&
		HXR_OK == m_pResponseHeaders->QueryInterface(IID_IHXValues, (void**)&pResponseHeaders))
	    {
                IHXBuffer* pCmds = NULL;

                // Thanks IPTV for adding a space after Public
                if (HXR_OK == pResponseHeaders->GetPropertyCString("Allow", pCmds) ||
                    HXR_OK == pResponseHeaders->GetPropertyCString("Public", pCmds) ||
                    HXR_OK == pResponseHeaders->GetPropertyCString("Public ", pCmds))
                {
                    // all methods are supported by default
                    if (!strstr((char*)pCmds->GetBuffer(), "SETUP"))
                        m_pIsMethodSupported[SETUP] = FALSE;

                    // Is redirect supported
                    if (!strstr((char*)pCmds->GetBuffer(), "REDIRECT"))
                        m_pIsMethodSupported[REDIRECT] = FALSE;

                    // Is play supported
                    if (!strstr((char*)pCmds->GetBuffer(), "PLAY"))
                        m_pIsMethodSupported[PLAY] = FALSE;

                    // Is pause supported
                    if (!strstr((char*)pCmds->GetBuffer(), "PAUSE"))
                        m_pIsMethodSupported[PAUSE] = FALSE;

                    // Is set_param supported
                    if (!strstr((char*)pCmds->GetBuffer(), "SET_PARAMETER"))
                        m_pIsMethodSupported[SET_PARAM] = FALSE;

                    // Is get_param supported
                    if (!strstr((char*)pCmds->GetBuffer(), "GET_PARAMETER"))
                        m_pIsMethodSupported[GET_PARAM] = FALSE;

                    // Is describe supported
                    if (!strstr((char*)pCmds->GetBuffer(), "DESCRIBE"))
                        m_pIsMethodSupported[DESCRIBE] = FALSE;

                    // Is teardown supported
                    if (!strstr((char*)pCmds->GetBuffer(), "TEARDOWN"))
                        m_pIsMethodSupported[TEARDOWN] = FALSE;

                    // Is record supported
                    if (!strstr((char*)pCmds->GetBuffer(), "RECORD"))
                        m_pIsMethodSupported[RECORD] = FALSE;

                    // Is announce supported
                    if (!strstr((char*)pCmds->GetBuffer(), "ANNOUNCE"))
                        m_pIsMethodSupported[ANNOUNCE] = FALSE;
                }

                HX_RELEASE(pCmds);
            }
            HX_RELEASE(pResponseHeaders);
        }

        if (m_bSDPInitiated)
        {
	    IHXValues* pResponseHeaders = NULL;
	    if (HXR_OK == m_pResponseHeaders->QueryInterface(IID_IHXValues, (void**)&pResponseHeaders))
	    {
		if (!IsRealServer())
		{
		    BOOL bForceRTP = TRUE;

                    ReadPrefBOOL(m_pPreferences, "NonRS", bForceRTP);
		    if (bForceRTP)
		    {
			pResponseHeaders->SetPropertyULONG32("UseRTP", TRUE);
		    }
		}

                m_url = m_headerControl;

		rc = m_pResp->HandleStreamDescriptionResponse
		(
		    HXR_OK,
		    m_pSDPFileHeader,
		    m_pSDPStreamHeaders,
		    pResponseHeaders
		);
	    }
	    HX_RELEASE(pResponseHeaders);

            RemoveSDPHeaders();
        }
        else
        {
	    rc = m_pResp->HandleOptionsResponse(HXR_OK, pRFC822Headers);
	    HX_RELEASE(pRFC822Headers);
        }

	return rc;
    }
}

HX_RESULT
RTSPClientProtocol::handleGetParamResponse(RTSPResponseMessage* pMsg)
{
    if(strcmp(pMsg->errorCode(), "200") != 0)
    {
	return m_pResp->HandleGetParameterResponse(HXR_FAIL, 0);
    }

    CHXBuffer* pBuffer = new CHXBuffer;
    if(!pBuffer)
    {
        return HXR_OUTOFMEMORY;
    }
    pBuffer->AddRef();
    const char* pContent = (char*)pMsg->getContent();
    HX_RESULT ret = pBuffer->Set((BYTE*)pContent, strlen(pContent) + 1);
    if( ret != HXR_OUTOFMEMORY )
    {
        ret = m_pResp->HandleGetParameterResponse(HXR_OK, pBuffer);
    }
    pBuffer->Release();
    return ret;
}

HX_RESULT
RTSPClientProtocol::handleSetParamResponse(RTSPResponseMessage* pMsg)
{
    const char* pPIP = 0;
    const char* pPPort = 0;
    const char* pPSourcePort = 0;
    IHXValues* pValues = NULL;
    HX_RESULT theErr = HXR_FAIL;

    if (m_pResp)
    {
	if(strcmp(pMsg->errorCode(), "200") != 0)
	{
	    if (m_bNonRSRTP)
	    {
		return m_pResp->HandleSetParameterResponse(HXR_OK);
	    }
	    else
	    {
	    	return m_pResp->HandleSetParameterResponse(HXR_FAIL);
	    }
	}

#if defined(HELIX_FEATURE_TRANSPORT_MULTICAST)
        MIMEHeader* pIP = pMsg->getHeader("MulticastIP");
	if (pIP)
	{
	    MIMEHeaderValue* pHeaderValue = pIP->getFirstHeaderValue();
	    if (pHeaderValue)
	    {
	        MIMEParameter* pParam = pHeaderValue->getFirstParameter();
	        if (pParam)
		{
		    pPIP = (const char*)pParam->m_attribute;
		}
	    }
	}

        MIMEHeader* pPort = pMsg->getHeader("MulticastPort");
	if (pPort)
	{
	    MIMEHeaderValue* pHeaderValue = pPort->getFirstHeaderValue();
	    if (pHeaderValue)
	    {
	        MIMEParameter* pParam = pHeaderValue->getFirstParameter();
	        if (pParam)
		{
		    pPPort = (const char*)pParam->m_attribute;
		}
	    }
	}

	MIMEHeader* pSourcePort = pMsg->getHeader("MulticastSourcePort");
	if (pSourcePort)
	{
	    MIMEHeaderValue* pHeaderValue = pSourcePort->getFirstHeaderValue();
	    if (pHeaderValue)
	    {
	        MIMEParameter* pParam = pHeaderValue->getFirstParameter();
	        if (pParam)
		{
		    pPSourcePort = (const char*)pParam->m_attribute;
		}
	    }
	}

	if (pPIP && pPPort && pPSourcePort)
	{
	    // multicast!!!
	    UINT16 nToPort = atoi(pPPort);

	    (*m_pTransportMPortMap)[nToPort] =
		(*m_pTransportStreamMap)[0];

	    IHXUDPSocket*	    pUDPSocket = 0;
	    UDPResponseHelper*	    pUDPResponseHelper = NULL;
	    IHXSetSocketOption*    pSockOpt = NULL;

	    if (!m_pNetworkServices)
	    {
		return HXR_OUTOFMEMORY;
	    }

	    if (HXR_OK != m_pNetworkServices->CreateUDPSocket(&pUDPSocket))
	    {
		return HXR_FAIL;
	    }

	    pUDPResponseHelper = new UDPResponseHelper(this, nToPort);

	    if (!pUDPResponseHelper)
	    {
		return HXR_OUTOFMEMORY;
	    }

	    pUDPResponseHelper->AddRef();
	    m_UDPResponseHelperList.AddTail(pUDPResponseHelper);

	    pUDPSocket->Init(0, nToPort, pUDPResponseHelper);

	    // set option before it binds
    	    if (HXR_OK == pUDPSocket->QueryInterface(IID_IHXSetSocketOption,
						     (void**)&pSockOpt))
    	    {
	    	pSockOpt->SetOption(HX_SOCKOPT_REUSE_ADDR, TRUE);
	    	pSockOpt->SetOption(HX_SOCKOPT_REUSE_PORT, TRUE);
	    	HX_RELEASE(pSockOpt);
    	    }

	    pUDPSocket->Bind(HXR_INADDR_ANY, nToPort);
	    theErr = pUDPSocket->Read(HX_SAFEUINT(MAX_UDP_PACKET));

	    ((RTSPTransport*)((*m_pTransportMPortMap)[nToPort]))->
		JoinMulticast(DwToHost(HXinet_addr(pPIP)), nToPort, pUDPSocket);

	    HX_RELEASE(pUDPSocket);
	}
#endif /* HELIX_FEATURE_TRANSPORT_MULTICAST */

#if defined(HELIX_FEATURE_STATS) && defined(HELIX_FEATURE_REGISTRY)
        MIMEHeader* pStatsInterval = pMsg->getHeader("UpdateStatsInterval");
	if (pStatsInterval)
	{
	    MIMEHeaderValue* pHeaderValue = pStatsInterval->getFirstHeaderValue();
	    if (pHeaderValue)
	    {
	        MIMEParameter* pParam = pHeaderValue->getFirstParameter();
	        if (pParam)
		{
		    // stats interval
		    UINT32 ulStatsInterval = (UINT32) atoi((const char*)pParam->m_attribute);
		    pValues = new CHXHeader;
                    if( pValues )
                    {
		        pValues->AddRef();
		        pValues->SetPropertyULONG32("UpdateStatsInterval", ulStatsInterval);
                    }
                    else
                    {
                        theErr = HXR_OUTOFMEMORY;
                    }
		}
	    }
	}
#endif /* HELIX_FEATURE_STATS && HELIX_FEATURE_REGISTRY */

        if( theErr != HXR_OUTOFMEMORY )
        {
	    theErr = m_pResp->HandleSetParameterResponseWithValues(HXR_OK, pValues);
        }
	HX_RELEASE(pValues);
    }

    return theErr;
}

HX_RESULT
RTSPClientProtocol::handleTeardownResponse(RTSPResponseMessage* pMsg)
{
    if(m_pResp)
    {
	if(strcmp(pMsg->errorCode(), "200") != 0)
	{
	    m_pResp->HandleTeardownResponse(HXR_FAIL);
	}
	else
	{
	    m_pResp->HandleTeardownResponse(HXR_OK);
	}
    }

    /*
     * The control channel is now closed
     */

    return HXR_NET_SOCKET_INVALID;
}

HX_RESULT
RTSPClientProtocol::handleRecordResponse(RTSPResponseMessage* pMsg)
{
    if(strcmp(pMsg->errorCode(), "200") != 0)
    {
	return m_pResp->HandleRecordResponse(HXR_FAIL);
    }

    if (m_bConnectionlessControl)
    {
	closeSocket();

	if (HXR_OK ==
	    m_pResp->QueryInterface(IID_IHXConnectionlessControl,
	                            (void**)&m_pConnectionlessControl))
	{
	    m_pConnectionCheckCallback = new ConnectionCheckCallback(this);
	    m_pConnectionCheckCallback->AddRef();
	    m_uConnectionCheckCallbackHandle =
	        m_pScheduler->RelativeEnter(m_pConnectionCheckCallback,
	        m_uConnectionTimeout * 1000);
	}
    }

    return m_pResp->HandleRecordResponse(HXR_OK);
}

HX_RESULT
RTSPClientProtocol::handlePauseResponse(RTSPResponseMessage* pMsg)
{
    /*
     * XXX...Bruce is there anything to do here?
     */

    return HXR_OK;
}

HX_RESULT
RTSPClientProtocol::handlePlayResponse(RTSPResponseMessage* pMsg,
    RTSPPlayMessage* pPlayMsg)
{
    /*  Message Format:
     *  RTSP/0.5 200 302 OK
     *  RTP-Info: url=foo/streamid=0;seq=32;rtptime=40182123,
     *	   url=foo/streamid=1;seq=410;rtptime=40199211
     */

    if(strcmp(pMsg->errorCode(), "200") != 0)
    {
	HX_RESULT hr = HXR_FAIL;

        if(pMsg->errorCodeAsUINT32() == 456)
            hr = HXR_INVALID_OPERATION;

        return m_pResp->HandlePlayResponse(hr);
    }

    MIMEHeader* pSequence = pMsg->getHeader("RTP-Info");
    MIMEHeaderValue* pSeqValue = 0;
    UINT16 streamID = 0;
    UINT16 seqNum = 0;
    UINT32 ulRTPTime = 0;
    const char* pControl = 0;
    RTPInfoEnum RTPErr;
    BOOL bSomeRTPInfoSet = FALSE;

    if (pSequence)
    {
	pSeqValue = pSequence->getFirstHeaderValue();
    }

    // per spec., "RTP-Info" has to be present in the 1st Play
    // response header received, regardless whether the transport
    // is TNG or RTP
    if (!pSeqValue && !m_bSeqValueReceived)
    {
	// XXXGo - interop hack
	if ((!(m_bIPTV || m_bColumbia)) || (m_pControlToStreamNoMap == NULL))
	{
	    return m_pResp->HandlePlayResponse(HXR_BAD_SERVER);
	}
    }

    if (pSeqValue)
    {
	do
	{
	    RTPErr = parseRTPInfoHeader(pSeqValue, streamID, seqNum,
					ulRTPTime, pControl);

	    // if m_pControlToStreamNoMap, don't trust the parseRTPInfoHeader
	    // because RTP-Info url could be not what we expect and still be ok with
	    // spec
	    HX_ASSERT(pControl);

            RTSPStreamInfo* pInfo = getStreamInfoFromSetupRequestURL(pControl);

            if (pInfo)
            {
                streamID = pInfo->m_streamNumber;
            }

	    pControl = 0;

	    if (RTPINFO_ERROR != RTPErr)
	    {
		SetRTPInfo(streamID, seqNum, ulRTPTime, RTPErr);
		bSomeRTPInfoSet = TRUE;
	    }

	    pSeqValue = pSequence->getNextHeaderValue();
	} while (pSeqValue);
    }

    // If we did not receive any RTP Info, 
    // just tell all streams not to wait for it
    if (!bSomeRTPInfoSet && m_pControlToStreamNoMap)
    {
	CHXMapStringToOb::Iterator i;
	UINT32* pul;

	for (i = m_pControlToStreamNoMap->Begin();
	     i != m_pControlToStreamNoMap->End();
	     ++i)
	{
	    pul = (UINT32*)(*i);
	    streamID = (UINT16) (*pul);
	    SetRTPInfo(streamID, 0, 0, RTPINFO_EMPTY);
	}
    }

    m_bSeqValueReceived = TRUE;

    if (m_bConnectionlessControl)
    {
	closeSocket();
    }

    MIMEHeader* pXPredecPeriod = pMsg->getHeader("x-initpredecbufperiod");

    if (pXPredecPeriod)
    {
	MIMEHeaderValue* pPrerollValue = pXPredecPeriod->getFirstHeaderValue();

	if (pPrerollValue)
	{
	    const char* pStart = pPrerollValue->value();
	    char* pEnd = 0;
	    ULONG32 ulValue = strtoul(pStart, &pEnd, 10);

	    if (*pStart && !*pEnd)
	    {
		// Handle updated preroll condition
		m_pResp->HandlePrerollChange(RTSP_PREROLL_PREDECBUFPERIOD,
					     ulValue);
	    }
	}
    }

    // Update range entries
    MIMEHeader* pRange = pMsg->getHeader("Range");
    if (pRange)
    {
        pSeqValue = pRange->getFirstHeaderValue();

        INT32 nFrom = 0, nTo = 0;

        if (pSeqValue)
        {
            MIMEParameter* pParam = pSeqValue->getFirstParameter();

            if (pParam)
            {
	        const char* pRange = (const char*) pParam->m_attribute;
                const char* pDash = NULL;
                char* pStopString;
                double dTemp;

	        if (pRange)
                {
		    dTemp = strtod(pRange, &pStopString);
                    nFrom = (INT32)(dTemp * 1000);

                    pDash  = strrchr(pRange, '-');
                }

                if (pDash)
                {
                    dTemp = strtod(pDash + 1, &pStopString);
                    nTo = (INT32)(dTemp * 1000);
                }
            }
        }

        if (!m_transportRequestList.IsEmpty())
        {
	    RTSPTransportRequest* pRequest =
	        (RTSPTransportRequest*)m_transportRequestList.GetHead();

            RTSPTransportInfo* pTransInfo = pRequest->getFirstTransportInfo();

            while(pTransInfo && nTo)
	    {
                // set the range in transport...only for RTP
	        pTransInfo->m_pTransport->RTSPTransport::setPlayRange(nFrom, nTo);
	        pTransInfo = pRequest->getNextTransportInfo();
	    }
        }
    }

    return m_pResp->HandlePlayResponse(HXR_OK);
}

HX_RESULT
RTSPClientProtocol::handleSetupResponse(RTSPResponseMessage* pMsg,
    RTSPSetupMessage* pSetupMsg)
{
    HX_RESULT status = HXR_OK;
    UINT16 streamNumber = 0;
    IHXValues* pReconnectValues = NULL;
    RTSPStreamInfo* pStreamInfo = 0;

    if(pMsg->errorCodeAsUINT32() == 401 || pMsg->errorCodeAsUINT32() == 407)
    {
	status = handleAuthentication(pMsg);
	return status;
    }
    else if(strcmp(pMsg->errorCode(), "200") != 0)
    {
	return m_pResp->HandleSetupResponse(HXR_BAD_TRANSPORT);
    }

    /* SETUP succeeded */
    m_setupResponseCount++;

    // we need to find the right StreamInfo obj..
    pStreamInfo = getStreamInfoFromSetupRequestURL(pSetupMsg->url());

    if(!pStreamInfo)
    {
	return m_pResp->HandleSetupResponse(HXR_BAD_TRANSPORT);
    }
    else
    {
	streamNumber = pStreamInfo->m_streamNumber;
    }

    CHXString reconnectFlag = pMsg->getHeaderValue("Reconnect");
    if (reconnectFlag != "" && strcasecmp((const char*)reconnectFlag, "false") == 0)
    {
	pReconnectValues = new CHXHeader();
	pReconnectValues->AddRef();

    	pReconnectValues->SetPropertyULONG32("Reconnect", 0);
    }
    else
    {
	MIMEHeader* pHeader = pMsg->getHeader("Alternate-Server");
	if (pHeader)
	{
	    RetrieveReconnectInfo(pHeader, ALTERNATE_SERVER, pReconnectValues);
	}

	pHeader = pMsg->getHeader("Alternate-Proxy");
	if (pHeader)
	{
	    RetrieveReconnectInfo(pHeader, ALTERNATE_PROXY, pReconnectValues);
	}
    }

    if (pReconnectValues)
    {
	m_pResp->HandleSetParameterResponseWithValues(HXR_OK, pReconnectValues);
    }
    HX_RELEASE(pReconnectValues);

    CHXString sessionID = pMsg->getHeaderValue("Session");
    if(sessionID != "")
    {
	int i;
	if (-1 != (i = sessionID.Find(';')))
	{
	    m_sessionID = sessionID.Left(i);
	}
	else
	{
	    m_sessionID = sessionID;
	}
    }

    status = handleSetupResponseExt(pStreamInfo, pMsg, pSetupMsg);

    UINT16 nStreamCount = (UINT16)m_streamInfoList.GetCount();
    if(m_setupResponseCount == 1 && (nStreamCount > 1))
    {
	// first time, send the rest...
	sendRemainingSetupRequests();
    }

    if(m_setupResponseCount == nStreamCount)
    {
	// all done!

	CHXSimpleList::Iterator i;

	for(i=m_transportRequestList.Begin();i!=m_transportRequestList.End();++i)
	{
	    RTSPTransportRequest* pRequest = (RTSPTransportRequest*)(*i);
	    RTSPTransportInfo* pTransInfo = pRequest->getFirstTransportInfo();

	    while(pTransInfo)
	    {
		if (BUFFER_DEPTH_UNDEFINED == m_ulBufferDepth)
		{
		    break;
		}

		HX_RESULT hresult =
		    pTransInfo->m_pTransport->SetResendBufferDepth
		    (
			m_ulBufferDepth
		    );

		if (HXR_OK != hresult)
		{
		    return hresult;
		}

		pTransInfo = pRequest->getNextTransportInfo();
	    }
	}

	// we don't need this anymore...
	HX_RELEASE(m_pSetupRequestHeader);

	return m_pResp->HandleSetupResponse(status);
    }
    else
    {
	return status;
    }
}

HX_RESULT
RTSPClientProtocol::handleSetupResponseExt(RTSPStreamInfo* pStreamInfo,
					   RTSPResponseMessage* pMsg,
					   RTSPSetupMessage* pSetupMsg)
{
    HX_RESULT status = HXR_OK;

    // get transport info
    CHXString transportType;
    UINT16 localPort = 0;
    UINT16 streamNumber = pStreamInfo->m_streamNumber;

    MIMEHeader* pTransport = pMsg->getHeader("Transport");
    if(pTransport)
    {
	MIMEHeaderValue* pValue = pTransport->getFirstHeaderValue();
	if(!pValue)
	{
	    // return some awful error
	}
	RTSPTransportRequest* pRequest = getTransportRequest(pValue);
	if(!pRequest)
	{
	    // return another awful error
	    return HXR_FAIL;
	}

	m_pSession->m_bChallengeMet = TRUE;
	m_pSession->m_bChallengeDone = TRUE;

	RTSPTransportInfo* pTransInfo = pRequest->getTransportInfo(streamNumber);
	UINT16 resendPort = 0;

        // get the server address we are connecting to
        // used to filter out any UDP packets received from 3rd party
        if (m_pSocket->GetForeignAddress(m_ulConnectToAddr) != HXR_OK)
	{
            HX_ASSERT(FALSE);
	    status = HXR_BAD_TRANSPORT;
	}

	UINT32 foreignAddr = 0;
        if (m_foreignAddr != 0)
        {
            foreignAddr = m_foreignAddr;
        }
        else
	{
            foreignAddr = m_ulConnectToAddr;
	}
	resendPort = pRequest->m_sResendPort;

	RTSPTransport* pTrans = pTransInfo->m_pTransport;
	RTCPBaseTransport* pRTCPTrans = pTransInfo->m_pRTCPTransport;

	pStreamInfo->m_sPort = pTransInfo->m_sPort;

	HX_ASSERT(pTrans);

#if defined(HELIX_FEATURE_RTP)
	switch(pRequest->m_lTransportType)
	{
	    case RTSP_TR_RTP_TCP:
	    {
		if ((!m_bHasSyncMasterStream) &&
		    (pStreamInfo->m_eMediaType == RTSPMEDIA_TYPE_AUDIO))
		{
		    pStreamInfo->m_bIsSyncMaster = TRUE;
		    m_bHasSyncMasterStream = TRUE;
		}

		pTrans->addStreamInfo(pStreamInfo);
		(*m_pTransportStreamMap)[pStreamInfo->m_streamNumber] = pTrans;

		m_pSession->setProtocolInterleave(this,
		    pRequest->m_tcpInterleave);
		m_pSession->setProtocolInterleave(this,
		    pRequest->m_tcpInterleave+1);

		((RTPTCPTransport*)pTrans)->
		    setInterleaveChannel(pRequest->m_tcpInterleave);
		((RTCPTCPTransport*)pRTCPTrans)->
		    setInterleaveChannel(pRequest->m_tcpInterleave+1);
		if (!m_sessionID.IsEmpty())
		{
		    pTrans->setSessionID(m_sessionID);
		}

		mapTransportChannel(pTrans, pRequest->m_tcpInterleave);
		mapTransportChannel(pRTCPTrans, pRequest->m_tcpInterleave+1);

		mapControlToStreamNo(pStreamInfo->m_streamControl,
                                     pStreamInfo->m_streamNumber);

		/* Temporary */
		m_uProtocolType = 3;
	    }
	    break;

	    case RTSP_TR_RTP_UDP:
	    {
		if ((!m_bHasSyncMasterStream) &&
		    (pStreamInfo->m_eMediaType == RTSPMEDIA_TYPE_AUDIO))
		{
		    pStreamInfo->m_bIsSyncMaster = TRUE;
		    m_bHasSyncMasterStream = TRUE;
		}

		pTrans->addStreamInfo(pStreamInfo);
		(*m_pTransportStreamMap)[pStreamInfo->m_streamNumber] = pTrans;
		(*m_pTransportPortMap)[pTransInfo->m_sPort] = pTrans;
		(*m_pTransportPortMap)[pTransInfo->m_sPort+1] = pRTCPTrans;

		mapControlToStreamNo(pStreamInfo->m_streamControl, pStreamInfo->m_streamNumber);

		((RTPUDPTransport*)pTrans)->setForeignAddress(foreignAddr, resendPort);
		((RTCPUDPTransport*)pRTCPTrans)->setForeignAddress(foreignAddr, resendPort+1);
		if (!m_sessionID.IsEmpty())
		{
		    pTrans->setSessionID(m_sessionID);
		}

		/* Temporary */
		m_uProtocolType = 2;
	    }
	    break;

	    default:
	    {
		status = HXR_BAD_TRANSPORT;
	    }
	    break;
	}
#endif /* HELIX_FEATURE_RTP */
    }

    return status;
}

HX_RESULT
RTSPClientProtocol::handleAnnounceResponse(RTSPResponseMessage* pMsg)
{
    HX_RESULT rc = HXR_OK;

    if(!m_bSetupRecord)	// better only get one of these if recording...
    {
	return HXR_FAIL;
    }

    if(pMsg->errorCodeAsUINT32() == 401 || pMsg->errorCodeAsUINT32() == 407)
    {
	rc = handleAuthentication(pMsg);
	return rc;
    }
    else if(strcmp(pMsg->errorCode(), "409") == 0)
    {
	return m_pResp->HandleStreamRecordDescriptionResponse(HXR_ALREADY_OPEN, 0);
    }
    else if(strcmp(pMsg->errorCode(), "200") != 0)
    {
	return m_pResp->HandleStreamRecordDescriptionResponse(HXR_FAIL, 0);
    }

    IHXValues* pRFC822Headers = NULL;
    getRFC822Headers(pMsg, pRFC822Headers);

    if(pRFC822Headers )
    {
	IHXKeyValueList* pRFC822List = NULL;

	if (HXR_OK == pRFC822Headers->QueryInterface(IID_IHXKeyValueList, (void**)&pRFC822List))
	{
	    m_pResponseHeaders->AppendAllListItems(pRFC822List);
	}
	HX_RELEASE(pRFC822List);
    }
    HX_RELEASE(pRFC822Headers);

    IHXValues* pResponseHeaders = NULL;

    if (HXR_OK == m_pResponseHeaders->QueryInterface(IID_IHXValues, (void**)&pResponseHeaders))
    {
	rc = m_pResp->HandleStreamRecordDescriptionResponse(HXR_OK,
							    pResponseHeaders);
    }
    else
    {
	rc = m_pResp->HandleStreamRecordDescriptionResponse(HXR_FAILED,
							    NULL);
    }
    HX_RELEASE(pResponseHeaders);

    return rc;
}

HX_RESULT
RTSPClientProtocol::handleDescribeResponse(RTSPResponseMessage* pMsg)
{
    HX_RESULT rc = HXR_OK;

    if(m_bSetupRecord)	// better only get one of these if playing...
    {
	return HXR_FAIL;
    }

    if(pMsg->errorCodeAsUINT32() == 401 || pMsg->errorCodeAsUINT32() == 407)
    {
	rc = handleAuthentication(pMsg);
	return rc;
    }
    else if(pMsg->errorCodeAsUINT32() == 551)
    {
	// A Require option was not supported.
	// We could check the Unsupported header
	// to see which one(s) failed, but
	// since we only support one right now..

	m_bEntityRequired = FALSE;

	// Re-Send the describe w/o the Require..
	return m_pResp->HandleStreamDescriptionResponse
	(
	    HXR_OK,
	    0,
	    0,
	    0
	);
    }
    else if(strcmp(pMsg->errorCode(), "200") != 0)
    {
	return m_pResp->HandleStreamDescriptionResponse
	(
	    HXR_DOC_MISSING,
	    0,
	    0,
	    0
	);
    }

    // We do not handle content-encoding
    MIMEHeader* pContentEncoding = pMsg->getHeader("Content-Encoding");
    if(pContentEncoding)
    {
        if (pContentEncoding->getFirstHeaderValue())
        {
            return HXR_UNEXPECTED_MSG;
        }
    }

    // Set Context for use by sendSetupRequestMessage()
    CHXString sessionID = pMsg->getHeaderValue("ETag");
    if(!sessionID.IsEmpty())
    {
	m_sessionID = sessionID;
    }

    IHXValues* pRFC822Headers = NULL;
    getRFC822Headers(pMsg, pRFC822Headers);

    if(pRFC822Headers)
    {
	// XXXGo - interop hack...It should be "Server", but IPTV put this
	// string in "User-Agent"...
    	IHXBuffer* pAgent  = NULL;
    	if (pRFC822Headers->GetPropertyCString("Server", pAgent) != HXR_OK)
    	{
    	    // try this...
    	    pRFC822Headers->GetPropertyCString("User-Agent", pAgent);
    	}

    	if (pAgent)
    	{
	    if (strncasecmp((const char*)pAgent->GetBuffer(), "Columbia RTSP Server",
	    	20) == 0)
	    {
		m_bColumbia = TRUE;
		m_bNoKeepAlive = TRUE;
	    }
	    else if (strncasecmp((const char*)pAgent->GetBuffer(), "Cisco IPTV",
	    	10) == 0)
	    {
	    	m_bIPTV = TRUE;
	    }
	    else if (strncasecmp((const char*)pAgent->GetBuffer(), "Cisco IP/TV",
	    	11) == 0)
	    {
	    	m_bIPTV = TRUE;
	    }
	    else if (strncasecmp((const char*)pAgent->GetBuffer(), "QTSS",
	    	4) == 0)
	    {
		// once we send SET_PARAM for a keep alive, QTS won't be
		// responsive for any other request...so don't send
		// keep alive.
		m_bNoKeepAlive = TRUE;
		m_bForceUCaseTransportMimeType = TRUE;
	    }
	    else if (strncasecmp((const char*)pAgent->GetBuffer(), "DSS",
	    	3) == 0)
	    {
		m_bForceUCaseTransportMimeType = TRUE;
	    }
    	    HX_RELEASE(pAgent);
    	}

	IHXKeyValueList* pRFC822List = NULL;

	if (HXR_OK == pRFC822Headers->QueryInterface(IID_IHXKeyValueList, (void**)&pRFC822List))
	{
	    m_pResponseHeaders->AppendAllListItems(pRFC822List);
	}
	HX_RELEASE(pRFC822List);
    }
    HX_RELEASE(pRFC822Headers);

    // Respond to Client Challenge to prove that we are a RealClient
    if (!m_pSession->m_bChallengeDone)
    {
	RetrieveChallenge(pMsg);
    }

    // We need a content base entry to handle relative urls.
    // Check for one in the order specified in:
    // http://www.zvon.org/tmRFC/RFC2326/Output/chapter19.html
    MIMEHeader* pContentBaseHeader = pMsg->getHeader("Content-Base");
    if(pContentBaseHeader)
    {
        MIMEHeaderValue* pValue = pContentBaseHeader->getFirstHeaderValue();
        m_contentBase = pValue->value();
    }

    if (m_contentBase.IsEmpty())
    {
        pContentBaseHeader = pMsg->getHeader("Content-Location");
        if(pContentBaseHeader)
        {
	        MIMEHeaderValue* pValue = pContentBaseHeader->getFirstHeaderValue();
	        m_contentBase = pValue->value();
        }
    }

    if (m_contentBase.IsEmpty())
    {
        INT32 nOffset = m_url.ReverseFind('/');
        m_contentBase = m_url.Left(nOffset+1);
    }

    // Format the content base member
    if (m_contentBase[m_contentBase.GetLength()-1] != '/')
    {
        INT32 nOffset = m_contentBase.ReverseFind('/');
        m_contentBase.SetAt(nOffset+1, '\0');
        m_contentBase.GetBufferSetLength(nOffset+1);
    }

    MIMEHeader* pContentTypeHeader = pMsg->getHeader("Content-type");
    MIMEHeader* pContentLengthHeader = pMsg->getHeader("Content-length");

    if(pContentTypeHeader && pContentLengthHeader)
    {
	MIMEHeaderValue* pContentValue =
	    pContentTypeHeader->getFirstHeaderValue();
	if(!pContentValue)
	{
	    // error
	    rc = HXR_FAIL;
	}
	else
	{
	    CHXBuffer* pBuffer = new CHXBuffer;
	    if(pBuffer)
	    {
		pBuffer->AddRef();
		rc = pBuffer->Set((BYTE*)pMsg->getContent(), strlen(pMsg->getContent())+1);
                if( rc == HXR_OUTOFMEMORY )
                {
                    HX_RELEASE(pBuffer);
                    goto cleanup;
                }
            }
            else
            {
                rc = HXR_OUTOFMEMORY;
                goto cleanup;
            }

            rc = ParseSDP(pContentValue->value(), pBuffer);
            if (HXR_OK == rc)
            {
		IHXValues* pResponseHeaders = NULL;
		if (HXR_OK == m_pResponseHeaders->QueryInterface(IID_IHXValues, (void**)&pResponseHeaders))
		{
		    if (!IsRealServer())
		    {
			BOOL bForceRTP = TRUE;

                        ReadPrefBOOL(m_pPreferences, "NonRS", bForceRTP);
			if (bForceRTP)
			{
			    pResponseHeaders->SetPropertyULONG32("UseRTP", TRUE);
			}
		    }

		    rc = m_pResp->HandleStreamDescriptionResponse
		    (
			HXR_OK,
			m_pSDPFileHeader,
			m_pSDPStreamHeaders,
			pResponseHeaders
		    );
		}
		HX_RELEASE(pResponseHeaders);

                RemoveSDPHeaders();
	    }
            HX_RELEASE(pBuffer);
	}
    }
    else
    {
	rc = HXR_FAILED;
    }

cleanup:

    if (HXR_OK != rc)
    {
	rc = m_pResp->HandleStreamDescriptionResponse
	(
	    rc,
	    0,
	    0,
	    0
	);
    }
    return rc;
}

HX_RESULT
RTSPClientProtocol::sendInitialMessage(RTSPClientSession* pSession,
				       IHXTCPSocket* pSocket)
{
    HX_RESULT		rc = HXR_OK;
    UINT32		seqNo = 0;
    RTSPOptionsMessage*	pMsg = NULL;
    IHXBuffer*		pBuffer = NULL;

    m_pMutex->Lock();

    // Assumption is we will send no options messages while waiting
    // for our session time out options response.
    HX_ASSERT(!m_bKeepAlivePending);

    if (m_bNonRSRTP)
    {
	rc = m_pResp->HandleOptionsResponse(HXR_OK, NULL);
	goto cleanup;
    }

    if (!m_bSessionSucceeded)
    {
	HX_ASSERT(!m_pSession && !m_pSocket);
	m_pSession = pSession;
	m_pSocket = pSocket;
    }

    pMsg = new RTSPOptionsMessage;

    // construct "rtsp://%-.200s:%u"
    m_url = "rtsp://";
    m_url += m_hostName.Left(200);
    m_url += ':';
    m_url.AppendULONG(m_foreignPort);

    pMsg->setURL(m_url);

    pMsg->addHeader("User-Agent", m_versionString);

    /*
     * XXXSMP m_pSessionHeaders can include a "Require" tag from rmacore.
     * Yes this is ugly, and needs fixing when we want to send more options
     */
    if (m_pSessionHeaders &&
        HXR_OK == m_pSessionHeaders->GetPropertyCString("ConnectionlessControl",
							pBuffer))
    {
	m_bConnectionlessControl =
	    (strcasecmp((const char*)pBuffer->GetBuffer(), "on") == 0) ?
	    TRUE : FALSE;
	pBuffer->Release();
    }

    addRFC822Headers(pMsg, m_pSessionHeaders);

    seqNo = m_pSession->getNextSeqNo(this);

    rc = sendRequest(pMsg, seqNo);

    if (!m_bSessionSucceeded)
    {
	m_pSession = NULL;
	m_pSocket = NULL;
    }

cleanup:

    m_pMutex->Unlock();

    return rc;
}

HX_RESULT
RTSPClientProtocol::getStreamDescriptionMimeType(char*& pMimeType)
{
    HX_RESULT rc = HXR_OK;

    IHXStreamDescription* pSD = 0;
    IHXPlugin2Handler* pPlugin2Handler = NULL;

    // we have to have either an IHXPluginHandler or an IHXPlugin2Handler

    m_pContext->QueryInterface(IID_IHXPlugin2Handler,
                               (void**)&pPlugin2Handler);

    if(pPlugin2Handler)
    {
	UINT32 unIndex;
	if (HXR_OK == pPlugin2Handler->FindIndexUsingStrings(PLUGIN_CLASS,
	    PLUGIN_STREAM_DESC_TYPE,
	    NULL,
	    NULL,
	    NULL,
	    NULL,
	    unIndex))
	{
	    IHXValues* pValues;
	    pPlugin2Handler->GetPluginInfo(unIndex, pValues);
	    IHXBuffer* pBuffer;
	    pValues->GetPropertyCString(PLUGIN_STREAMDESCRIPTION, pBuffer);
	    pValues->Release();
	    const char* pTemp = (const char*)pBuffer->GetBuffer();
	    pMimeType = new_string(pTemp);
	    pBuffer->Release();
	}
	else
	{
	    rc = HXR_FAIL;
	}
	HX_RELEASE(pPlugin2Handler);
    }
#if defined(HELIX_FEATURE_SERVER)
    else
    {
	// ok we do not have an IHXPlugin2Handler (we must be in the server)
	// so get the PluginHandler
	PluginHandler* pPHandler = 0;
        m_pContext->QueryInterface(IID_IHXPluginHandler,
                               (void**)&pPHandler);

	if(pPHandler)
	{
	    PluginHandler::StreamDescription* pSDHandler;

	    pSDHandler = pPHandler->m_stream_description_handler;
	    UINT32 ulNumPlugins = pSDHandler->GetNumOfPlugins();
	    if(ulNumPlugins > 0)
	    {
		// get the first one...
		char* ppszDllPath = 0;
		char* ppszDescription = 0;
		char* ppszCopyright = 0;
		char* ppszMoreInfo = 0;
		BOOL  pbMultiple = FALSE;
		char* ppszMimeType = 0;
		pSDHandler->GetPluginInfo(0, &ppszDllPath, &ppszDescription,
		    &ppszCopyright, &ppszMoreInfo, &pbMultiple,
		    &ppszMimeType);
		pMimeType = new_string(ppszMimeType);
		rc = HXR_OK;
	    }
	    else
	    {
		rc = HXR_FAIL;
	    }
	    pPHandler->Release();
	}
	else
	{
	    rc = HXR_FAIL;
	}
    }
#endif /* HELIX_FEATURE_SERVER */

    return rc;
}
IHXStreamDescription*
RTSPClientProtocol::getStreamDescriptionInstance(const char* pMimeType)
{
    IHXStreamDescription* pSD = 
	HXStreamDescriptionHelper::GetInstance(m_pContext, pMimeType);

#if defined(HELIX_FEATURE_SERVER)
    if (!pSD)
    {
	// we don't have a plugin2handler ... we must be in the
	// server ... ask for a plugin handler
	
	PluginHandler* pPHandler = 0;
	m_pContext->QueryInterface(IID_IHXPluginHandler, (void**)&pPHandler);
	
	const char* pFindMimeType = pMimeType;
	if(pPHandler)
	{
	    PluginHandler::StreamDescription* pSDHandler;
	    PluginHandler::Errors             pluginResult;
	    PluginHandler::Plugin*            pPlugin;

	    pSDHandler = pPHandler->m_stream_description_handler;
	    pluginResult = pSDHandler->Find(pFindMimeType, pPlugin);
	    if(PluginHandler::NO_ERRORS == pluginResult)
	    {
		IUnknown* pInstance = 0;
		pPlugin->GetInstance(&pInstance);
		if(pInstance)
		{
		    HX_RESULT rc;
		    rc = pInstance->QueryInterface(IID_IHXStreamDescription,
						   (void**)&pSD);
		    if(rc == HXR_OK)
		    {
			IHXPlugin* pSDPlugin = 0;
			rc = pSD->QueryInterface(IID_IHXPlugin,
						 (void**)&pSDPlugin);
			if(rc == HXR_OK)
			{
			    pSDPlugin->InitPlugin(m_pContext);
			    pSDPlugin->Release();
			}
		    }
		    pInstance->Release();
		}
		pPlugin->ReleaseInstance();
	    }
	    pPHandler->Release();
	}
    }
#endif

    return pSD;
}

void
RTSPClientProtocol::reset()
{
    HX_DELETE(m_pTransportStreamMap);
    HX_DELETE(m_pTransportPortMap);
    HX_DELETE(m_pTransportMPortMap);
    HX_DELETE(m_pTransportChannelMap);

    if (m_pControlToStreamNoMap)
    {
        CHXMapStringToOb::Iterator i;
	for(i=m_pControlToStreamNoMap->Begin();i!=m_pControlToStreamNoMap->End();++i)
	{
	    UINT32* pul = (UINT32*)(*i);
	    delete pul;
	}
	m_pControlToStreamNoMap->RemoveAll();
	HX_DELETE(m_pControlToStreamNoMap);
    }

    CHXSimpleList::Iterator i;
    for(i=m_transportRequestList.Begin();i!=m_transportRequestList.End();++i)
    {
	RTSPTransportRequest* pRequest = (RTSPTransportRequest*)(*i);
	delete pRequest;
    }
    m_transportRequestList.RemoveAll();

    HX_RELEASE(m_pSetupRequestHeader);
    HX_RELEASE(m_pResolver);
    HX_RELEASE(m_pResp);
    HX_RELEASE(m_pConnectionlessControl);
    HX_RELEASE(m_pConnectionCheckCallback);
    HX_RELEASE(m_pContext);

    if (m_uConnectionCheckCallbackHandle)
    {
        m_pScheduler->Remove(m_uConnectionCheckCallbackHandle);
        m_uConnectionCheckCallbackHandle = 0;
    }

    HX_RELEASE(m_pTimeoutCallback);
    HX_DELETE(m_pSessionTimeout);
}

void
RTSPClientProtocol::clearStreamInfoList()
{
    CHXSimpleList::Iterator i;
    for(i=m_streamInfoList.Begin();
	i!=m_streamInfoList.End();
	++i)
    {
	RTSPStreamInfo* pInfo = (RTSPStreamInfo*)(*i);
	delete pInfo;
    }
    m_streamInfoList.RemoveAll();
}

void
RTSPClientProtocol::clearTransportRequestList()
{
    CHXSimpleList::Iterator i;

    for (i = m_transportRequestList.Begin(); i != m_transportRequestList.End(); 	++i)
    {
	RTSPTransportRequest* pRequest = (RTSPTransportRequest*)(*i);
	delete pRequest;
    }
    m_transportRequestList.RemoveAll();
}

void
RTSPClientProtocol::clearUDPResponseHelperList()
{
    CHXSimpleList::Iterator i;
    for(i=m_UDPResponseHelperList.Begin();
	i!=m_UDPResponseHelperList.End();
	++i)
    {
	UDPResponseHelper* pHelper = (UDPResponseHelper*)(*i);
	HX_RELEASE(pHelper);
    }
    m_UDPResponseHelperList.RemoveAll();
}

void
RTSPClientProtocol::clearSocketStreamMap(CHXMapLongToObj*& pSocketStreamMap)
{
    if(pSocketStreamMap)
    {
	CHXMapLongToObj::Iterator i;
	for(i=pSocketStreamMap->Begin();
	    i!=pSocketStreamMap->End();++i)
	{
	    IHXUDPSocket* pSocket = (IHXUDPSocket*)(*i);
	    pSocket->Release();
	}
	delete pSocketStreamMap;
	pSocketStreamMap = 0;
    }
}

RTSPTransportRequest*
RTSPClientProtocol::getTransportRequest(MIMEHeaderValue* pValue)
{
    RTSPTransportRequest* pTransportRequest = 0;
    if(pValue)
    {
	UINT16 requestPort = 0;
	UINT16 resendPort = 0;
	INT8 tcpInterleave = 0;

	MIMEParameter* pParam = pValue->getFirstParameter();
	char pTransValue[256]; /* Flawfinder: ignore */
	strncpy(pTransValue, pParam->m_attribute,255);
	pTransValue[255] = '\0';

	pParam = pValue->getNextParameter();
	while(pParam)
	{
	    if(0 == pParam->m_attribute.CompareNoCase("client_port"))
	    {
		// value range in rtp
		const char* portString = (const char*)pParam->m_value;
		char* pFirstValue = (char*)strchr(portString, '-');
		if(pFirstValue)
		{
		    *pFirstValue = 0;  // get rid of second port value
		}
		requestPort = (UINT16)strtol(portString, 0, 10);
	    }
	    else if(0 == pParam->m_attribute.CompareNoCase("server_port"))
	    {
		// value range in rtp
		const char* portString = (const char*)pParam->m_value;
		char* pFirstValue = (char*)strchr(portString, '-');
		if(pFirstValue)
		{
		    *pFirstValue = 0;  // get rid of second port value
		}
		resendPort = (UINT16)strtol(portString, 0, 10);
	    }
            else if(0 == pParam->m_attribute.CompareNoCase("source"))
            {
                const char* serverAddress = (const char*) pParam->m_value;
                if ((m_foreignAddr = HXinet_addr(serverAddress)) ==
                    INADDR_NONE)
                {
                    /*
                     * XXXtbradley should log a warning about invalid source
                     * address.
                     */
                    m_foreignAddr = 0;
                }
                else
                {
                    m_foreignAddr = DwToHost(m_foreignAddr);
                }
            }
	    else if(0 == pParam->m_attribute.CompareNoCase("interleaved"))
	    {
		// it could be a range in RTP (i.e. RTP-RTCP)
		const char* channelString = (const char*)pParam->m_value;
		char* pFirstValue = (char*)strchr(channelString, '-');
		if (pFirstValue)
		{
		    //get rid of second channel value since the second value is
		    //always one higher than the first
		    *pFirstValue = 0;
    		}

		tcpInterleave = (UINT8)strtol(channelString, 0, 10);
	    }
	    else if(0 == pParam->m_attribute.CompareNoCase("unicast"))
	    {
		SafeStrCat(pTransValue, ";unicast", 256);
	    }
	    else if(0 == pParam->m_attribute.CompareNoCase("multicast"))
	    {
		SafeStrCat(pTransValue, ";multicast", 256);
	    }
	    pParam = pValue->getNextParameter();
	}

	RTSPTransportTypeEnum transportType =
	    RTSPTransportMimeMapper::getTransportType(pTransValue);

	CHXSimpleList::Iterator i;
	for(i=m_transportRequestList.Begin();
	    i!=m_transportRequestList.End();++i)
	{
	    RTSPTransportRequest* pRequest = (RTSPTransportRequest*)(*i);
	    if(pRequest->m_lTransportType == transportType)
	    {
		pRequest->m_sPort = requestPort;
		pRequest->m_sResendPort = resendPort;
		pRequest->m_tcpInterleave = tcpInterleave;

		pTransportRequest = pRequest;
	    }
	    else
	    {
		pRequest->m_bDelete = TRUE;
	    }
	}
    }

    // remove transport requests marked as delete
    LISTPOSITION pos = m_transportRequestList.GetTailPosition();
    while(pos)
    {
	RTSPTransportRequest* pRequest =
	    (RTSPTransportRequest*)m_transportRequestList.GetAt(pos);
	if(pRequest->m_bDelete)
	{
	    delete pRequest;
	    pos = m_transportRequestList.RemoveAt(pos);
	}
	else
	{
	    m_transportRequestList.GetPrev(pos);
	}
    }

    return pTransportRequest;
}

void
RTSPClientProtocol::messageDebugFileOut(const char* pMsg, BOOL bInbound)
{
    if(m_bMessageDebug)
    {
	FILE* fp = fopen(m_messageDebugFileName, "a");
	if(!fp)
	{
	    return;
	}
	if(bInbound)
	{
	    fprintf(fp, "IN:\n");
	}
	else
	{
	    fprintf(fp, "OUT:\n");
	}
	fprintf(fp, "%s\n", pMsg);
	fclose(fp);
    }
}

/*
 * IHXThinnableSource methods.
 */

/************************************************************************
 *	Method:
 *	    IHXThinnableSource::LimitBandwidthByDropping
 *	Purpose:
 *
 *	    Implemented by protocols that allow infinite thinnability through
 *	    LimitBandwidthByDropping
 */

STDMETHODIMP
RTSPClientProtocol::LimitBandwidthByDropping(UINT32 ulStreamNo,
					    UINT32 ulBandwidthLimit)
{
    if (!m_pIsMethodSupported[SET_PARAM])
    {
	return HXR_OK;
    }
    m_pMutex->Lock();

    RTSPSetParamMessage* pMsg = new RTSPSetParamMessage;
    pMsg->setURL(m_url);

    char tmp[128];
    SafeSprintf(tmp, 128, "stream=%d;LimitBandwidthByDropping=%d", ulStreamNo,
	ulBandwidthLimit);
    pMsg->addHeader("FrameControl", tmp);
    if (!m_sessionID.IsEmpty())
    {
	pMsg->addHeader("Session", m_sessionID);
    }
    UINT32 seqNo = m_pSession->getNextSeqNo(this);
    HX_RESULT hr = sendRequest(pMsg, seqNo);
    m_pMutex->Unlock();
    return hr;
}

STDMETHODIMP
RTSPClientProtocol::SetDeliveryBandwidth(UINT32 ulBandwidth, UINT32 ulMsBackOff)
{
    if (!m_pIsMethodSupported[SET_PARAM] || !m_pSession)
    {
	return HXR_OK;
    }
    m_pMutex->Lock();

    RTSPSetParamMessage* pMsg = new RTSPSetParamMessage;
    pMsg->setURL(m_url);

    char tmp[64];
    SafeSprintf(tmp, 64, "Bandwidth=%d;BackOff=%d", ulBandwidth, ulMsBackOff);
    pMsg->addHeader("SetDeliveryBandwidth", tmp);
    if (!m_sessionID.IsEmpty())
    {
	pMsg->addHeader("Session", m_sessionID);
    }
    UINT32 seqNo = m_pSession->getNextSeqNo(this);

    HX_RESULT hr = sendRequest(pMsg, seqNo);
    m_pMutex->Unlock();
    return hr;
}

HX_RESULT
RTSPClientProtocol::closeSocket()
{
    m_pSocket = 0;

    return m_pSession->closeSocket();
}

HX_RESULT
RTSPClientProtocol::reopenSocket()
{
    m_pMutex->Lock();
    m_pSession->m_bReopenSocket = TRUE;
    HX_RESULT hr = m_pSession->reopenSocket(this);
    m_pMutex->Unlock();
    return hr;
}

HX_RESULT
RTSPClientProtocol::ReopenSocketDone(HX_RESULT status)
{
    m_pMutex->Lock();

    HX_RESULT hresult = HXR_OK;

    m_pSession->m_bReopenSocket = FALSE;

    if (HXR_OK != status)
    {
	hresult = m_pResp->HandleProtocolError(status);
	goto exit;
    }

    HX_ASSERT(m_pControlBuffer);

    if (!m_pControlBuffer)
    {
	hresult = HXR_FAIL;
	goto exit;
    }

    hresult = sendControlMessage(m_pControlBuffer);

    m_pControlBuffer->Release();
    m_pControlBuffer = 0;

exit:
    m_pMutex->Unlock();
    return hresult;
}

void
RTSPClientProtocol::DoConnectionCheck()
{
    m_uConnectionCheckCallbackHandle = 0;

    HX_ASSERT(m_pConnectionlessControl);

    if (!m_bConnectionAlive)
    {
	m_pConnectionlessControl->ConnectionCheckFailed(HXR_SERVER_TIMEOUT);
	return;
    }

    m_bConnectionAlive = FALSE;

    m_uConnectionCheckCallbackHandle =
        m_pScheduler->RelativeEnter(m_pConnectionCheckCallback,
	m_uConnectionTimeout * 1000);
}

void
RTSPClientProtocol::GetForeignHostPort(char*& pHost, UINT16* pPort)
{
    pHost = NULL;
    *pPort = 0;

    if (m_bUseProxy | m_bUseHTTPProxy)
    {
	pHost = new_string(m_hostName);

	if (m_bHTTPOnly)
	{
	    *pPort = m_uCloakPort;
	}
	else
	{
	    *pPort = m_foreignPort;
	}
    }
}

void
RTSPClientProtocol::mapTransportChannel(RTSPTransport* pTran, UINT16 nChannel)
{
    if (!m_pTransportChannelMap)
    {
	m_pTransportChannelMap = new CHXMapLongToObj();
    }
    (*m_pTransportChannelMap)[nChannel] = pTran;
}

/* Interop */
void
RTSPClientProtocol::mapControlToStreamNo(const char* pControl, UINT16 uStreamNo)
{
    if (!m_pControlToStreamNoMap)
    {
	m_pControlToStreamNoMap = new CHXMapStringToOb();
    }

    UINT16* pu = new UINT16;
    *pu = uStreamNo;
    (*m_pControlToStreamNoMap)[pControl] = pu;
}

BOOL
RTSPClientProtocol::getStreamNoFromControl(const char* pControl, REF(UINT16) uStreamNo)
{
    // don't call if there is no map
    HX_ASSERT(m_pControlToStreamNoMap);

    UINT16* ul = NULL;
    if (m_pControlToStreamNoMap->Lookup(pControl, (void*&)ul))
    {
	uStreamNo = (UINT16)*ul;
	return TRUE;
    }
    else
    {
	return FALSE;
    }
}

void
RTSPClientProtocol::setSetupRequestURL(RTSPSetupMessage* pMsg,
    RTSPStreamInfo* pStreamInfo)
{
    HX_ASSERT(pMsg && pStreamInfo);

    if (pStreamInfo->m_streamControl.Find("rtsp:") != -1)
    {
	// absolute url....just use this!
	pMsg->setURL(pStreamInfo->m_streamControl);
    }
    else if (!m_contentBase.IsEmpty())
    {
	// we have Content-Base RTSP header
	pMsg->setURL(m_contentBase + pStreamInfo->m_streamControl);
    }
    else if(!m_headerControl.IsEmpty())
    {
	// this is a=control in session description...
	pMsg->setURL(m_headerControl);
    }
    else
    {
	int lenURL = m_url.GetLength()+pStreamInfo->m_streamControl.GetLength()+15;
	char* setupURL = new char[lenURL];
	SafeSprintf(setupURL, lenURL,"%s/%s", (const char*)m_url,
	(const char*)pStreamInfo->m_streamControl);
	pMsg->setURL(setupURL);
	delete[] setupURL;
    }
}

RTSPStreamInfo*
RTSPClientProtocol::getStreamInfoFromSetupRequestURL(const char* pUrl)
{
    HX_ASSERT(pUrl);

    // we've created a Setup request url above.  this is a reverse
    char* setupURL = NULL;
    BOOL bFoundIt = FALSE;
    RTSPStreamInfo* pStreamInfo = NULL;
    CHXSimpleList::Iterator i;

    // If we do not have control URL, 
    // we can still proceed as long there is only one stream
    if (pUrl == NULL)
    {
	if (m_streamInfoList.GetCount() == 1)
	{
	    return (RTSPStreamInfo*) m_streamInfoList.GetHead();
	}

	return NULL;
    }

    for(i=m_streamInfoList.Begin();i!=m_streamInfoList.End();++i)
    {
	pStreamInfo = (RTSPStreamInfo*)(*i);
	if(pStreamInfo->m_streamControl == pUrl)
	{
	    // well, this is my lucky day
	    bFoundIt = TRUE;
	    break;
	}

        // - get stream identifier by parsing URL
	char* pStream = (char*)strrchr(pUrl, '/');
	if(pStream)
	{
	    ++pStream;	// pass '/'
	    if(pStreamInfo->m_streamControl == (const char*)pStream)
	    {
	        bFoundIt = TRUE;
	        break;
	    }
	}

	if (!m_contentBase.IsEmpty())
	{
	    if ((m_contentBase + pStreamInfo->m_streamControl) == pUrl)
	    {
		// not so bad...
		bFoundIt = TRUE;
		break;
	    }
	}

	if (!m_headerControl.IsEmpty())
	{
	    if (m_headerControl == pStreamInfo->m_streamControl)
	    {
		// phew....
		bFoundIt = TRUE;
		break;
	    }
	}

        // Compare each portion of the url w/ our control
        pStream = (char*)strchr(pUrl, '/');
        while (pStream)
        {
            ++pStream;	// pass '/'
            if(pStreamInfo->m_streamControl == (const char*)pStream)
            {
                bFoundIt = TRUE;
                break;
            }

            pStream = (char*)strchr(pStream, '/');
        }

        if (bFoundIt)
        {
            break;
        }

        // following the recommendation posted at:
        // http://www1.ietf.org/mail-archive/working-groups/mmusic/current/msg01245.html
        int lenURL = m_url.GetLength()+strlen(pUrl)+2;
	setupURL = new char[lenURL];
	SafeSprintf(setupURL, lenURL, "%s/%s", (const char*)m_url, pUrl);
	if (pStreamInfo->m_streamControl == (const char*)setupURL)
	{
    	    delete[] setupURL;
    	    bFoundIt = TRUE;
    	    break;
	}
	delete[] setupURL;
    }

    if (bFoundIt)
    {
	return pStreamInfo;
    }
    else
    {
	HX_ASSERT(!"streaminfo not found from setup request url");
	return NULL;
    }
}

STDMETHODIMP
RTSPClientProtocol::InitPacketFilter(RawPacketFilter* pFilter)
{
    m_pPacketFilter = pFilter;
    return HXR_OK;
}

void
RTSPClientProtocol::LeavePrefetch(void)
{
    m_bPrefetch = FALSE;
    SendMsgToTransport(LEAVE_PREFETCH);

    return;
}

void
RTSPClientProtocol::EnterFastStart(void)
{
    m_bFastStart = TRUE;
    SendMsgToTransport(ENTER_FASTSTART);

    return;
}

void
RTSPClientProtocol::LeaveFastStart(void)
{
    m_bFastStart = FALSE;
    SendMsgToTransport(LEAVE_FASTSTART);

    return;
}

void
RTSPClientProtocol::InitCloak(UINT16*	    pCloakPorts,
			      UINT8	    nCloakPorts,
			      IHXValues*   pValues)
{
    m_pCloakPorts = pCloakPorts;
    m_nCloakPorts = nCloakPorts;

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

UINT16
RTSPClientProtocol::GetCloakPortSucceeded(void)
{
    return m_uCloakPort;
}

HX_RESULT
RTSPClientProtocol::RetrieveChallenge(RTSPResponseMessage* pMessage)
{
    return HXR_FAILED;
}

HX_RESULT
RTSPClientProtocol::SetStatistics(UINT16	uStreamNumber,
				  STREAM_STATS*	pStats)
{
    HX_RESULT	rc = HXR_OK;

#if defined(HELIX_FEATURE_STATS) && defined(HELIX_FEATURE_REGISTRY)
    RTSPTransport* pTrans = (RTSPTransport*)(*m_pTransportStreamMap)[uStreamNumber];

    if (pTrans)
    {
	rc = pTrans->SetStatistics(uStreamNumber, pStats);
    }
#endif /* HELIX_FEATURE_STATS && HELIX_FEATURE_REGISTRY */

    return rc;
}

HX_RESULT
RTSPClientProtocol::extractRealmInformation(RTSPResponseMessage* pMsg)
{
#if defined(HELIX_FEATURE_AUTHENTICATION)
    // this is called from several places where authentication errors
    // are likely to occur. It then pulls out the realm for future reference.

    IHXRegistry* pRegistry = NULL;
    HX_RESULT retVal = m_pContext->QueryInterface(IID_IHXRegistry, (void**)&pRegistry);

    if (SUCCEEDED(retVal))
    {
	CHXString authString;

	authString = pMsg->getHeaderValue("Proxy-Authenticate");

	if (m_bUseProxy && !authString.IsEmpty())
	{
	    IHXBuffer* pBuffer = NULL;
	    retVal = m_pCommonClassFactory->CreateInstance(CLSID_IHXBuffer, (void**)&pBuffer);

	    if (SUCCEEDED(retVal))
	    {
		HX_ASSERT(pMsg->errorCodeAsUINT32() == 407);
		retVal = pBuffer->Set((const unsigned char*)(const char*)authString,
			authString.GetLength()+1);
		UINT32 regid = pRegistry->GetId("proxy-authentication.rtsp.realm.recent");
                if( retVal == HXR_OUTOFMEMORY )
                {
                    HX_RELEASE(pBuffer);
                    HX_RELEASE(pRegistry);
                    return retVal;
                }
		if (!regid)
		{
		    pRegistry->AddStr("proxy-authentication.rtsp.realm.recent", pBuffer);
		}
		else
		{
		    pRegistry->SetStrByName("proxy-authentication.rtsp.realm.recent", pBuffer);
		}

		HX_RELEASE(pBuffer);
	    }
	}

	authString = pMsg->getHeaderValue("WWW-Authenticate");

	if (!authString.IsEmpty())
	{
	    IHXBuffer* pBuffer = NULL;
	    retVal = m_pCommonClassFactory->CreateInstance(CLSID_IHXBuffer, (void**)&pBuffer);

	    if (SUCCEEDED(retVal))
	    {
		HX_ASSERT(pMsg->errorCodeAsUINT32() == 401);
		retVal = pBuffer->Set((const unsigned char*)(const char*)authString,
			authString.GetLength()+1);
                if( retVal == HXR_OUTOFMEMORY )
                {
                    HX_RELEASE(pBuffer);
                    HX_RELEASE(pRegistry);
                    return retVal;
                }
		UINT32 regid = pRegistry->GetId("authentication.rtsp.realm.recent");
		if (!regid)
		{
		    pRegistry->AddStr("authentication.rtsp.realm.recent", pBuffer);
		}
		else
		{
		    pRegistry->SetStrByName("authentication.rtsp.realm.recent", pBuffer);
		}

		HX_RELEASE(pBuffer);
	    }
	}
	HX_RELEASE(pRegistry);
    }
    return retVal;
#else
    return HXR_NOTIMPL;
#endif /* HELIX_FEATURE_AUTHENTICATION */
}

HX_RESULT
RTSPClientProtocol::extractExistingAuthorizationInformation(IHXValues* pIHXValuesRequestHeaders)
{
#if defined(HELIX_FEATURE_AUTHENTICATION)
    if (pIHXValuesRequestHeaders)
    {
	const char* pName = NULL;
	IHXBuffer* pValue = NULL;

	HX_RESULT result = pIHXValuesRequestHeaders->GetFirstPropertyCString(pName, pValue);

	while (SUCCEEDED(result))
	{
	    // check for proxy and www authentication stuff separately because
	    // it's plausible that one request will have both of these.

	    if (m_bUseProxy && !strcasecmp(pName, "Proxy-Authorization"))
	    {
		HX_RESULT retVal = HXR_OK;
		IHXRegistry* pRegistry = NULL;

		retVal = m_pContext->QueryInterface(IID_IHXRegistry, (void**)&pRegistry);

		if (SUCCEEDED(retVal))
		{
		    IHXBuffer* pBuffer = NULL;
		    retVal = m_pCommonClassFactory->CreateInstance(CLSID_IHXBuffer, (void**)&pBuffer);
		    UINT32 regid = 0;

		    if (SUCCEEDED(retVal))
		    {
			IHXBuffer* pHeaderBuffer = NULL;

			CHXString key;
			CHXString recentRealmInfo = "";

			key = "proxy-authentication.rtsp:";
			retVal = pRegistry->GetStrByName("proxy-authentication.rtsp.realm.recent",
					pHeaderBuffer);

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

			key += "proxy-host:";
			key += recentRealmInfo;

			HX_ASSERT(!key.IsEmpty());
			retVal = pBuffer->Set(pValue->GetBuffer(), pValue->GetSize());
                        if( retVal == HXR_OUTOFMEMORY )
                        {
                            HX_RELEASE(pBuffer);
                            HX_RELEASE(pHeaderBuffer);
                            HX_RELEASE(pRegistry);
                            return retVal;
                        }

			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(pRegistry);
	    	}
	    }

	    if (!strcasecmp(pName, "Authorization"))
	    {
		HX_RESULT retVal = HXR_OK;
		IHXRegistry* pRegistry = NULL;

		retVal = m_pContext->QueryInterface(IID_IHXRegistry, (void**)&pRegistry);

		if (SUCCEEDED(retVal))
		{
		    IHXBuffer* pBuffer = NULL;
		    retVal = m_pCommonClassFactory->CreateInstance(CLSID_IHXBuffer, (void**)&pBuffer);
		    UINT32 regid = 0;

		    if (SUCCEEDED(retVal))
		    {
			IHXBuffer* pHeaderBuffer = NULL;

			CHXString key;
			CHXString recentRealmInfo = "";

			key = "authentication.rtsp:";
			retVal = pRegistry->GetStrByName("authentication.rtsp.realm.recent",
					pHeaderBuffer);

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

			key += m_hostName;

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

			HX_ASSERT(!key.IsEmpty());
			retVal = pBuffer->Set(pValue->GetBuffer(), pValue->GetSize());
                        if( retVal == HXR_OUTOFMEMORY )
                        {
                            return retVal;
                        }

			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(pRegistry);
	    	}
	    }
            HX_RELEASE(pValue);
	    result = pIHXValuesRequestHeaders->GetNextPropertyCString(pName, pValue);
	}
    }
    return HXR_OK;
#else
    return HXR_NOTIMPL;
#endif /* HELIX_FEATURE_AUTHENTICATION */
}

void
RTSPClientProtocol::appendAuthorizationHeaders(RTSPMessage* pMsg)
{
#if defined(HELIX_FEATURE_AUTHENTICATION)
    // This is currently getting called from sendPendingStreamDescription
    // although it's feasible that other rtsp messages will need authentication.
    // At this writing none of our servers or proxies do anything other than
    // DESCRIBE, so I'm omitting scattered calls to this routine so I don't
    // waste bandwidth. It's plausible that calling this from everywhere it calls
    // addRFC822Headers would be fully compliant, if a touch wasteful.
    // xxxbobclark

    IHXRegistry* pRegistry = NULL;
    HX_RESULT retVal = m_pContext->QueryInterface(IID_IHXRegistry, (void**)&pRegistry);
    if (SUCCEEDED(retVal))
    {
	CHXString strExistingAuthorizationHeader = pMsg->getHeaderValue("Authorization");
	CHXString strExistingProxyAuthorizationHeader = pMsg->getHeaderValue("Proxy-Authorization");

	if (strExistingAuthorizationHeader.IsEmpty())
	{
	    // if it doesn't exist, see if we've remembered one we can
	    // plop down here.

	    CHXString key = "authentication.rtsp:";
	    IHXBuffer* pFoundRealmBuffer = NULL;
	    IHXBuffer* pBuffer = NULL;

	    key += m_hostName;
	    key += ":";

	    retVal = pRegistry->GetStrByName("authentication.rtsp.realm.recent",
	    	pBuffer);

	    if (SUCCEEDED(retVal))
	    {
		key += CHXString((const char*)pBuffer->GetBuffer(), pBuffer->GetSize());

		retVal = pRegistry->GetStrByName((const char*)key, pFoundRealmBuffer);

		if (SUCCEEDED(retVal))
		{
		    CHXString strAuthHeader((const char*)pFoundRealmBuffer->GetBuffer(),
		    	pFoundRealmBuffer->GetSize());

		    pMsg->addHeader("Authorization", (const char*)strAuthHeader);
		}
	    }
	}

	if (m_bUseProxy && strExistingProxyAuthorizationHeader.IsEmpty())
	{
	    // if it doesn't exist, see if we've remembered one we can
	    // plop down here.

	    CHXString key = "proxy-authentication.rtsp:";
	    IHXBuffer* pFoundRealmBuffer = NULL;
	    IHXBuffer* pBuffer = NULL;

	    key += "proxy-host:";

	    retVal = pRegistry->GetStrByName("proxy-authentication.rtsp.realm.recent",
	    	pBuffer);

	    if (SUCCEEDED(retVal))
	    {
		key += CHXString((const char*)pBuffer->GetBuffer(), pBuffer->GetSize());

		retVal = pRegistry->GetStrByName((const char*)key, pFoundRealmBuffer);

		if (SUCCEEDED(retVal))
		{
		    CHXString strAuthHeader((const char*)pFoundRealmBuffer->GetBuffer(),
		    	pFoundRealmBuffer->GetSize());

		    pMsg->addHeader("Proxy-Authorization", (const char*)strAuthHeader);
		}
	    }
	}

	HX_RELEASE(pRegistry);
    }
#endif /* HELIX_FEATURE_AUTHENTICATION */
}

HX_RESULT
RTSPClientProtocol::handleAuthentication(RTSPResponseMessage* pMsg)
{
#if defined(HELIX_FEATURE_AUTHENTICATION)
    HX_RESULT rc = HXR_OK;

    rc = extractRealmInformation(pMsg);
    if( rc == HXR_OUTOFMEMORY )
    {
        return rc;
    }

    IHXValues* pIHXValuesResponseHeaders = NULL;

    pMsg->AsValues(pIHXValuesResponseHeaders);

    if(pIHXValuesResponseHeaders)
    {
	HX_RESULT retVal = HXR_OK;
	IHXBuffer* pServerHeaderBuffer = NULL;

	// Add the fake _server value that's used
	// in IHXAuthenticationManager2 implementations. xxxbobclark
	HX_ASSERT(!m_hostName.IsEmpty());
	if (!m_hostName.IsEmpty())
	{
	    retVal = m_pCommonClassFactory->CreateInstance(CLSID_IHXBuffer,
		    	(void**)&pServerHeaderBuffer);
	    if (SUCCEEDED(retVal))
	    {
		if (pMsg->errorCodeAsUINT32() == 407 && m_proxyHost.GetLength() > 0)
		{
		    rc = pServerHeaderBuffer->Set((UCHAR*)(const char*)m_proxyHost, m_proxyHost.GetLength()+1);
		}
		else
		{
		    rc = pServerHeaderBuffer->Set((UCHAR*)(const char*)m_hostName, m_hostName.GetLength()+1);
		}
		pIHXValuesResponseHeaders->SetPropertyCString("_server", pServerHeaderBuffer);
		HX_RELEASE(pServerHeaderBuffer);
	    }
	}

	rc = m_pResp->HandleWWWAuthentication
	(
	    HXR_NOT_AUTHORIZED,
	    pIHXValuesResponseHeaders
	);
    }
    else
    {
	rc = m_pResp->HandleWWWAuthentication
	(
	    HXR_FAIL,
	    NULL
	);
    }

    HX_RELEASE(pIHXValuesResponseHeaders);

    return rc;
#else
    return HXR_NOTIMPL;
#endif /* HELIX_FEATURE_AUTHENTICATION */
}

//
// add x-wap-profile and x-wap-profile-diff headers if they exists
//
void
RTSPClientProtocol::addUAProfHeaders(IHXValues* pHeaders)
{
    if (pHeaders)
    {
	if (m_pUAProfURI && m_pUAProfURI->GetSize() > 0)
	{
	    pHeaders->SetPropertyCString("x-wap-profile", m_pUAProfURI);

	    if (m_pUAProfDiff && m_pUAProfDiff->GetSize() > 0)
	    {
		pHeaders->SetPropertyCString("x-wap-profile-diff",
					     m_pUAProfDiff);
	    }
	}
    }
}

RTSPTransportBuffer*
RTSPClientProtocol::getTransportBuffer(UINT16 uStreamNumber)
{
    RTSPTransportBuffer* pRet = NULL;

    if (m_pTransportStreamMap)
    {
	RTSPTransport* pTrans =
	    (RTSPTransport*)(*m_pTransportStreamMap)[uStreamNumber];

	if (pTrans)
	{
	    pRet = pTrans->getTransportBuffer(uStreamNumber);
	}
    }

    return pRet;
}

HX_RESULT
RTSPClientProtocol::ParseSDP(const char* pszContentType, IHXBuffer* pSDPBuffer)
{
    HX_RESULT   rc = HXR_OK;
    UINT16      nValues = 0;
    UINT32      ulNumStreams = 0;
    IHXValues** ppValues = NULL;
    IHXValues** ppRealHeaders = NULL;;// headers of right BW
    UINT32*     pulSubscriptionBW = NULL;

    IHXStreamDescription* pSD = getStreamDescriptionInstance(pszContentType);

    if (!pSD)
    {
	rc = HXR_FAIL;
        goto cleanup;
    }

    rc = pSD->GetValues(pSDPBuffer, nValues, ppValues);
    if(HXR_REQUEST_UPGRADE == rc)
    {
	// request upgrade...sdpplin has added itself for
	// upgradecollection
	// nothing to do...
    }
    else if (HXR_OK != rc)
    {
	// error
	HX_ASSERT(!"bad sdp file 0");
    }
    else if (nValues <= 1)
    {
	// clean up..
	for(UINT16 i=0;i<nValues;++i)
	{
	    // don't need IHXValues anymore...
	    ppValues[i]->Release();
	}
	HX_VECTOR_DELETE(ppValues);

	// error
	rc = HXR_FAIL;
	HX_ASSERT("!bad sdp file 1");
    }
    else
    {
	// get header info
	IHXBuffer*  pControl = NULL;
        IHXBuffer*  pIPAddress = NULL;
	UINT32      ulIsSessionLive = 0;
	UINT32      ulAvgBitRate   = 0;
	UINT32      ulRtpRRBitRate = (ULONG32)-1;
	UINT32      ulRtpRSBitRate = (ULONG32)-1;
        UINT32      ulIPAddress = 0;
        BOOL        bRealMedia = FALSE;

        m_pSDPFileHeader = ppValues[0];

	ppValues[0]->GetPropertyCString("Control", pControl);
	if(pControl)
	{
	    if (!strcmp((const char*)pControl->GetBuffer(),"*"))
                m_headerControl = m_contentBase;
            else
                m_headerControl = pControl->GetBuffer();

            HX_RELEASE(pControl);
        }

	ppValues[0]->GetPropertyULONG32("LiveStream", ulIsSessionLive);
	ppValues[0]->GetPropertyULONG32("AvgBitRate", ulAvgBitRate);
	// Get session level RTP bandwidth modifiers
	ppValues[0]->GetPropertyULONG32("RtcpRRRate", ulRtpRRBitRate);
	ppValues[0]->GetPropertyULONG32("RtcpRSRate", ulRtpRSBitRate);

        /*
         * Get a number of streams in this presentation
         */
    
        // don't trust the "StreamCount"
        // cHeaders will represent a number of m= line in SDP
        // ulNumStreams is actual number of streams in this presentation
        // They will be different on sure stream presentation becacause we are
        // duplicating headers for each bandwidth.    
        // we have to iterate over the stream headers...        
        if (!m_bSDPInitiated || GetStreamCountNoTrust(&ppValues[1], nValues - 1, ulNumStreams) != TRUE)
        {
	    // Well, This is not a SDP file generated by pplyfobj.cpp.
	    // Trust the StreamCount!
	    m_pSDPFileHeader->GetPropertyULONG32("StreamCount", ulNumStreams);	    
	    ppRealHeaders = new IHXValues*[ulNumStreams];
	    for (int i = 0; i < (int)ulNumStreams; i++)
	    {
	        ppRealHeaders[i] = ppValues[i+1];
	        ppRealHeaders[i]->AddRef();
	    }
        }
#if defined(HELIX_FEATURE_TRANSPORT_MULTICAST)
        else
        {
    	    // we should have at least one stream
    	    HX_ASSERT(ulNumStreams > 0 && m_bSDPInitiated);
    
            // set the StreamCount
	    m_pSDPFileHeader->SetPropertyULONG32("StreamCount", ulNumStreams);	    
    
    	    /*
     	     * Get a BW for each stream to "subscribe" to
     	     */
    	    pulSubscriptionBW = new UINT32[ulNumStreams];
    	    memset(pulSubscriptionBW, 0, sizeof(UINT32) * ulNumStreams);

	    if (GetSubscriptionBW(m_pSDPFileHeader, 
			          &ppValues[1], 
			          nValues - 1, 
			          pulSubscriptionBW, 
			          ulNumStreams) != TRUE)
	    {              
	        // this should never happen
                HX_ASSERT(FALSE);
	        rc = HXR_UNEXPECTED;
	        goto cleanup;
	    }
	
    	    /**************************
     	    * Get right stream headers to pass to CPurePlaySource
     	    * We have figured out BW to use for each stream
     	    * This is expensive, but...hey, it's done only once
     	    * Iterate over all the stream headers AGAIN, and Get right stream headers
     	    * for each stream depending on subscription BW
     	    */
     	    if (GetRightHeaders(ppRealHeaders,
     			        ulNumStreams,
     			        &ppValues[1],
     			        nValues - 1,
     			        pulSubscriptionBW) != TRUE)
     	    {
     	        // this should never happen
                HX_ASSERT(FALSE);
	        rc = HXR_UNEXPECTED;
	        goto cleanup;
    	    }
       }

       if (HXR_OK == ppValues[0]->GetPropertyCString("MulticastAddress", pIPAddress))
        {
            m_sessionHost = pIPAddress->GetBuffer();
        }
        HX_RELEASE(pIPAddress);

        if (m_bSDPInitiated)
        {
            bRealMedia = DetermineIfRMPresentation(&ppRealHeaders[0], ulNumStreams);
        }
#endif /* HELIX_FEATURE_TRANSPORT_MULTICAST */

	for (int i=0;i<ulNumStreams;++i)
	{
	    // reset...
	    BOOL    bHasMarkerRule = 0;
            UINT16  markerRule = 0;
	    UINT32  streamNumber = 0;
	    UINT32  needReliable = 0;
	    UINT32  rtpPayloadType = (ULONG32)-1;
	    UINT32  sampleSize = 0;
	    UINT32  sampleRate = 0;
	    UINT32  RTPFactor = 0;
	    UINT32  HXFactor = 0;
	    UINT32  ulIsLive = ulIsSessionLive;
	    UINT32  ulHasOutOfOrderTS = 0;
            UINT32  ulPort = 0;
	    IHXBuffer*      pMimeType = NULL;
	    IHXBuffer*      pRawRules = NULL;
	    RTSPMediaType   eMediaType = RTSPMEDIA_TYPE_UNKNOWN;

            pControl = NULL;

            if (!m_pSDPStreamHeaders)
            {
                m_pSDPStreamHeaders = new CHXSimpleList();
            }

	    // build header list
	    m_pSDPStreamHeaders->AddTail(ppRealHeaders[i]);

	    // determine stream type
	    ppRealHeaders[i]->GetPropertyCString("MimeType", pMimeType);
	    if (pMimeType)
	    {
		eMediaType = SDPMapMimeToMediaType((const char*) pMimeType->GetBuffer());
		HX_RELEASE(pMimeType);
	    }

#if defined(HELIX_FEATURE_ASM)
	    // deal with marker rule...
	    ppRealHeaders[i]->GetPropertyCString("ASMRuleBook", pRawRules);
	    if (pRawRules)
	    {
		ASMRuleBook* pRuleBook = new ASMRuleBook((const char*)pRawRules->GetBuffer());

    	    	BOOL bHasRule = FALSE;
    	    	IHXValues* pRuleProps = NULL;
    	    	IHXBuffer* pBuffer = NULL;

    	    	for(UINT16 nRule=0; nRule < pRuleBook->GetNumRules(); ++nRule)
    	    	{
	    	    pRuleProps = NULL;
	    	    pBuffer = NULL;

	    	    pRuleBook->GetProperties(nRule, pRuleProps);
	    	    pRuleProps->GetPropertyCString("marker", pBuffer);

	    	    if(pBuffer)
	    	    {
	    	    	int marker = atoi((const char*)pBuffer->GetBuffer());
	    	    	if (1 == marker)
	    	    	{
		    	    /* we don't allow more than one marker rule */
		    	    markerRule = (UINT16)nRule;
		    	    bHasMarkerRule = TRUE;

		    	    pBuffer->Release();
		    	    pRuleProps->Release();
		    	    break;
	    	    	}
                        HX_RELEASE(pBuffer);
	    	    }
                    HX_RELEASE(pRuleProps);
    	    	}

		HX_DELETE(pRuleBook);
		HX_RELEASE(pRawRules);
	    }
#endif /* HELIX_FEATURE_ASM */

	    // build stream info list
	    RTSPStreamInfo* pInfo = new RTSPStreamInfo;
	    ppRealHeaders[i]->GetPropertyULONG32("StreamNumber", streamNumber);
	    ppRealHeaders[i]->GetPropertyULONG32("NeedReliablePackets", needReliable);
	    ppRealHeaders[i]->GetPropertyULONG32("SamplesPerSecond", sampleRate);
	    ppRealHeaders[i]->GetPropertyULONG32("BitsPerSample", sampleSize);
	    ppRealHeaders[i]->GetPropertyCString("Control", pControl);
	    ppRealHeaders[i]->GetPropertyULONG32("RTPPayloadType", rtpPayloadType);
	    ppRealHeaders[i]->GetPropertyULONG32("RTPTimestampConversionFactor", RTPFactor);
	    ppRealHeaders[i]->GetPropertyULONG32("HXTimestampConversionFactor", HXFactor);
	    ppRealHeaders[i]->GetPropertyULONG32("LiveStream", ulIsLive);
	    ppRealHeaders[i]->GetPropertyULONG32("HasOutOfOrderTS", ulHasOutOfOrderTS);
	    // Override session level average bitrate
	    ppRealHeaders[i]->GetPropertyULONG32("AvgBitRate", ulAvgBitRate);
	    // Overide session level RTP bandwidth modifiers
	    // with media level modifiers
	    ppRealHeaders[i]->GetPropertyULONG32("RtcpRRRate", ulRtpRRBitRate);
	    ppRealHeaders[i]->GetPropertyULONG32("RtcpRSRate", ulRtpRSBitRate);
    	    ppRealHeaders[i]->GetPropertyULONG32("port", ulPort);

            if(pControl)
	    {
		pInfo->m_streamControl = pControl->GetBuffer();
                HX_RELEASE(pControl);
	    }
	    else
	    {
		char tmp[32];
		SafeSprintf(tmp, 32, "streamid=%u", (UINT16)streamNumber);
		pInfo->m_streamControl = tmp;
	    }
	    pInfo->m_streamNumber = (UINT16)streamNumber;
	    pInfo->m_bNeedReliablePackets = needReliable ? TRUE: FALSE;
	    pInfo->m_rtpPayloadType = (INT16)rtpPayloadType;
	    pInfo->m_sampleRate = sampleRate;
	    pInfo->m_sampleSize = sampleSize / 8;
	    pInfo->m_RTPFactor = RTPFactor;
	    pInfo->m_HXFactor = HXFactor;
	    pInfo->m_bHasMarkerRule = bHasMarkerRule;
	    pInfo->m_markerRule = markerRule;
	    pInfo->m_sPort = (UINT16)ulPort;
	    pInfo->m_bIsLive = ulIsLive ? TRUE : FALSE;
	    pInfo->m_bHasOutOfOrderTS = ulHasOutOfOrderTS ? TRUE : FALSE;
	    pInfo->m_eMediaType = eMediaType;
	    pInfo->m_bIsSyncMaster = FALSE;	// decison will be made on setup response
	    pInfo->m_ulAvgBitRate = ulAvgBitRate;
	    pInfo->m_ulRtpRRBitRate = ulRtpRRBitRate;
	    pInfo->m_ulRtpRSBitRate = ulRtpRSBitRate;
            pInfo->m_bRealMedia = bRealMedia;

#if defined(HELIX_FEATURE_TRANSPORT_MULTICAST)
            if ((m_sessionHost.IsEmpty() || HXR_INADDR_ANY == HXinet_addr((const char*)m_sessionHost)) &&
                HXR_OK == ppRealHeaders[i]->GetPropertyCString("MulticastAddress", pIPAddress))
            {
                m_sessionHost = pIPAddress->GetBuffer();
            }
            HX_RELEASE(pIPAddress);
#endif /* HELIX_FEATURE_TRANSPORT_MULTICAST */

	    m_streamInfoList.AddTail(pInfo);
	}
    }

cleanup:

    HX_VECTOR_DELETE(ppValues);
    HX_VECTOR_DELETE(pulSubscriptionBW);

    // release ppRealHeaders too
    if (NULL != ppRealHeaders)
    {
	for (int i = 0; i < (int)ulNumStreams; i++)
	{
	    HX_RELEASE(ppRealHeaders[i]);
	}
	HX_VECTOR_DELETE(ppRealHeaders);
    }

    HX_RELEASE(pSD);

    return rc;
}

void
RTSPClientProtocol::RemoveSDPHeaders(void)
{
    HX_RELEASE(m_pSDPFileHeader);

    if (m_pSDPStreamHeaders)
    {
        CHXSimpleList::Iterator i;
        for(i=m_pSDPStreamHeaders->Begin();i!=m_pSDPStreamHeaders->End();++i)
        {
            IHXValues* pStreamHeader = (IHXValues*)(*i);
            HX_RELEASE(pStreamHeader);
        }
        HX_DELETE(m_pSDPStreamHeaders);
    }
}

#if defined(HELIX_FEATURE_TRANSPORT_MULTICAST)
BOOL
RTSPClientProtocol::DetermineIfRMPresentation(IHXValues** ppStrmHeaders,
				              UINT32 ulNumStreams)
{
    BOOL bIsRMPresentation = FALSE;

    if (ppStrmHeaders && ulNumStreams)
    {
	IHXValues* pStrmHdr = NULL;
	IHXBuffer* pASMRuleBook = NULL;
	IHXBuffer* pMimeType = NULL;
	UINT32 ulRTPPayload = RTP_PAYLOAD_RTSP + 1;
	BOOL bIsRMStream = FALSE;
	UINT32 ulIdx = 0;

	bIsRMPresentation = TRUE;

	for (ulIdx = 0; bIsRMPresentation && (ulIdx < ulNumStreams); ulIdx++)
	{
	    pStrmHdr = ppStrmHeaders[ulIdx];

	    bIsRMStream = FALSE;

	    if (pStrmHdr)
	    {
		if (HXR_OK == pStrmHdr->GetPropertyULONG32("RTPPayloadType", ulRTPPayload) &&
		    ulRTPPayload == RTP_PAYLOAD_RTSP)
		{
		    bIsRMStream = TRUE;
		}
		ulRTPPayload = RTP_PAYLOAD_RTSP + 1;

		if (bIsRMStream)
		{
		    bIsRMStream = FALSE;
		    
		    if (HXR_OK == pStrmHdr->GetPropertyCString("ASMRuleBook", pASMRuleBook) &&
		        pASMRuleBook)
		    {
			bIsRMStream = TRUE;
		    }
		}
		HX_RELEASE(pASMRuleBook);

		if (bIsRMStream)
		{
		    bIsRMStream = FALSE;

		    if (HXR_OK == pStrmHdr->GetPropertyCString("MimeType", pMimeType) &&
		        pMimeType)
		    {
			if (strstr((const char*) pMimeType->GetBuffer(), RN_COMMON_MIME_TYPE_FRAGMENT))
			{
			    bIsRMStream = TRUE;
			}
		    }
		}
		HX_RELEASE(pMimeType);
	    }

	    bIsRMPresentation = (bIsRMStream && bIsRMPresentation);
	}
    }

    return bIsRMPresentation;
}

BOOL
RTSPClientProtocol::GetSubscriptionBW(IHXValues*    pFileHeader, 
				      IHXValues**   ppStrmHeaders,
				      UINT16        unNumStrmHeaders,
				      REF(UINT32*)  pulSubscriptionBW,
				      UINT32        ulNumStreams)					  
{
    HX_ASSERT(pFileHeader);
    HX_ASSERT(ppStrmHeaders);
    HX_ASSERT(unNumStrmHeaders >= 1);
    HX_ASSERT(pulSubscriptionBW);
    HX_ASSERT(ulNumStreams >= 1);

    IHXBuffer*	pRuleBuf = NULL;
    IHXBuffer* pBandwidth = NULL;

    pFileHeader->AddRef();

    if (!m_pPreferences || HXR_OK != m_pPreferences->ReadPref("Bandwidth", pBandwidth))
    {
	HX_ASSERT(FALSE);
	pBandwidth = new CHXBuffer();
	pBandwidth->AddRef();
	pBandwidth->Set((const unsigned char*)"64000", strlen("64000"));
    }

    if (HXR_OK != pFileHeader->GetPropertyCString("ASMRuleBook", pRuleBuf))
    {
	// OK, this is a single stream presentation.  Take an ASMRuleBook from 
	// any of stream headers (they are all the same), and use it to decide
	// which stream header to use depending on bit rate
	HX_ASSERT(1 == ulNumStreams);

	// get ASMRuleBook
	HX_ASSERT(NULL != ppStrmHeaders[0]);
	IHXValues* pHeader = ppStrmHeaders[0];
	pHeader->AddRef();

	if (HXR_OK == pHeader->GetPropertyCString("ASMRuleBook", pRuleBuf))
	{
	    IHXBuffer*	    pBuffer = NULL;
	    UINT16	    unRules = 0;
    	    ASMRuleBook	    rules((char*)pRuleBuf->GetBuffer());

	    unRules = rules.GetNumRules();
    
	    // get subscriptions for this bandwidth
	    BOOL bSubInfo[256];
	    UINT16 unRuleNum = 0;

	    IHXValues* pValues = new CHXHeader();
    	    pValues->AddRef();
	    
	    pValues->SetPropertyCString("Bandwidth", pBandwidth);
	    rules.GetSubscription(bSubInfo, pValues);
	    
	    HX_RELEASE(pValues);

	    // get a rule number that we are interested in
	    int y;
	    for (y = 0; y < (int)unRules; y++)
	    {
	    	if (TRUE == bSubInfo[y])
	    	{
	    	    IHXBuffer* pBw = 0;
		    unRuleNum = y;
		    
		    // make sure AverageBandwidth != 0
		    rules.GetProperties(y, pValues);		    
		    
		    if (HXR_OK == pValues->GetPropertyCString("AverageBandwidth", 
							      pBw))
		    {
		    	pulSubscriptionBW[0] += atol((const char*)pBw->GetBuffer());
		    	HX_RELEASE(pBw);		    	
		    }
		    else
		    {
			// TimeStampDelivery only stream
			pulSubscriptionBW[0] = 0;
		    }
		    HX_RELEASE(pValues);
	    	}
	    }
            HX_RELEASE(pRuleBuf);
	}
	else
	{
	    // There is no ASMRuleBook at all...
	    // This should never happen.
	    HX_RELEASE(pFileHeader);
	    HX_RELEASE(pBandwidth);
	    HX_RELEASE(pHeader);
	    HX_ASSERT(FALSE);
	    return FALSE;
	}

	HX_RELEASE(pHeader);
    }
    else
    {
	// this is a multiple stream presentation.
	// take ASMRuleBook for a file and figure out BW to use for 
	// each stream
	IHXBuffer*	pBuffer	    = NULL;
	UINT16		unRules     = 0;

	ASMRuleBook	rules((char*)pRuleBuf->GetBuffer());

	unRules = rules.GetNumRules();    

	// get subscriptions for this bandwidth
	BOOL bSubInfo[256];
	UINT16 unRuleNum = 0;

	IHXValues* pValues = new CHXHeader();
    	pValues->AddRef();
	
	pValues->SetPropertyCString("Bandwidth", pBandwidth);
	rules.GetSubscription(bSubInfo, pValues);
	HX_RELEASE(pValues);
	
	// get a rule number that we are interested in
	// Assuming there is only one TRUE
	int y;
	for (y = 0; y < (int)unRules; y++)
	{
	    if (TRUE == bSubInfo[y])
	    {
	    	// there should be only one
		unRuleNum = y;
		break;
	    }
	}

	// Get a BW for each stream
        rules.GetProperties((int)unRuleNum, pValues);
        for (int i = 0; i < (int)ulNumStreams; i++)
        {
            char rgStreamBW[32];
            sprintf(rgStreamBW, "Stream%dBandwidth", i);
            if (HXR_OK == pValues->GetPropertyCString((const char*)rgStreamBW, 
						      pBuffer))
	    {
		pulSubscriptionBW[i] = (UINT32)atol((const char*)pBuffer->GetBuffer());
		HX_RELEASE(pBuffer);
	    }
        }
	HX_RELEASE(pValues);	
	HX_RELEASE(pRuleBuf);
    }

    HX_RELEASE(pFileHeader);
    HX_RELEASE(pBandwidth);

    return TRUE;
}

BOOL
RTSPClientProtocol::GetRightHeaders(REF(IHXValues**)    ppRealHeaders, // out
     				    UINT32	        ulNumStreams,
     				    IHXValues**         ppHeaders,
     				    UINT32	        cHeaders,
     				    UINT32*             pulSubscriptionBW)
{
    HX_ASSERT(ulNumStreams >= 1);
    HX_ASSERT(ppHeaders);
    HX_ASSERT(pulSubscriptionBW);
    
    ppRealHeaders = new IHXValues*[ulNumStreams];
    memset(ppRealHeaders, NULL, sizeof(IHXValues*) * ulNumStreams);    
    
    for (int i = 0; i < (int)ulNumStreams; i++)
    {
	ULONG32 ulID = 0;
	ULONG32 ulBW = 0;;
	BOOL    bFound = FALSE;

	for (int j = 0; j < (int)cHeaders; j++)
    	{
    	    HX_ASSERT(NULL != ppHeaders[j]);

    	    IHXValues* pSrcH = ppHeaders[j];
	    pSrcH->AddRef();

	    if ((HXR_OK == pSrcH->GetPropertyULONG32("AvgBitRate", ulBW)) &&
		(ulBW == pulSubscriptionBW[i]))
	    {
		// this one has the right BW, how about stream number?
		if ((HXR_OK == pSrcH->GetPropertyULONG32("StreamId", ulID)) &&
		    ((int)ulID == i))
		{
		    bFound = TRUE;
		    
		    // This is the right heaader, 
		    ppRealHeaders[i] = pSrcH;
		    ppRealHeaders[i]->AddRef();
		    HX_RELEASE(pSrcH);		    
		    break; // we found for this stream, go to next one
		}
	    }
	    HX_RELEASE(pSrcH);
	}

	if (!bFound)
	{
	    // this should never happen
	    ppRealHeaders[i] = NULL;
	    HX_ASSERT(FALSE);	    
	    return FALSE;
	}
    }

    return TRUE;
}
#endif /* HELIX_FEATURE_TRANSPORT_MULTICAST */

BOOL
RTSPClientProtocol::GetStreamCountNoTrust(IHXValues**   ppHeaders, 
					  UINT16        unNumHeader,
					  REF(UINT32)   ulNumStreams)					  
{
    HX_ASSERT(NULL != ppHeaders);

    UINT32	ulID = 0;
    BOOL	rgFound[256];    
    
    memset(rgFound, 0, 256);

    for (UINT16 i = 0; i < unNumHeader; i++)
    {
	HX_ASSERT(ppHeaders[i] != NULL);
	IHXValues* pSrcHeader = NULL;

	pSrcHeader = ppHeaders[i];
	pSrcHeader->AddRef();

	// "StreamId" is the field that ppfobj.cpp puts for a group of
	// streams 
	if (HXR_OK == pSrcHeader->GetPropertyULONG32("StreamId", ulID))
	{
	    if (!rgFound[ulID])
	    {   
		rgFound[ulID] = TRUE;
		ulNumStreams++;
	    }
	}
	else
	{
	    // OK, trust the "StreamCount".  This is not a SDP file generated
	    // by pplyfobj.cpp
	    ulNumStreams = 0;
	    HX_RELEASE(pSrcHeader);
	    return FALSE;
	}
	HX_RELEASE(pSrcHeader);
    }

    return TRUE;
}

HX_RESULT
RTSPClientProtocol::CreateUDPSockets(UINT32 ulStream, UINT16 ulPort)
{
    HX_RESULT           rc = HXR_OK;
    IHXUDPSocket*       pUDPSocket1 = NULL;
    IHXUDPSocket*       pUDPSocket2 = NULL;
    IHXSetSocketOption* pSockOpt = NULL;
    UDPResponseHelper*  pUDPResponseHelper1 = NULL;
    UDPResponseHelper*  pUDPResponseHelper2 = NULL;

    if((HXR_OK != m_pNetworkServices->CreateUDPSocket(&pUDPSocket1)) ||
       (HXR_OK != m_pNetworkServices->CreateUDPSocket(&pUDPSocket2)))
    {
	rc = HXR_FAIL;
	goto cleanup;
    }

    pUDPResponseHelper1 = new UDPResponseHelper(this, ulPort);
    pUDPResponseHelper2 = new UDPResponseHelper(this, ulPort+1);

    if (!pUDPResponseHelper1 || !pUDPResponseHelper2)
    {
	rc = HXR_OUTOFMEMORY;
	goto cleanup;
    }

    pUDPResponseHelper1->AddRef();
    pUDPResponseHelper2->AddRef();

    m_UDPResponseHelperList.AddTail(pUDPResponseHelper1);
    m_UDPResponseHelperList.AddTail(pUDPResponseHelper2);

    if((HXR_OK != pUDPSocket1->Init(0, 0, pUDPResponseHelper1)) ||
       (HXR_OK != pUDPSocket2->Init(0, 0, pUDPResponseHelper2)))
    {
        rc = HXR_FAILED;
        goto cleanup;
    }

#if defined(HELIX_FEATURE_TRANSPORT_MULTICAST)
    if (m_bMulticast)
    {
        if (HXR_OK != pUDPSocket1->QueryInterface(IID_IHXSetSocketOption, (void**)&pSockOpt))
        {
            rc = HXR_FAILED;
            goto cleanup;
        }

	pSockOpt->SetOption(HX_SOCKOPT_REUSE_ADDR, TRUE);
	pSockOpt->SetOption(HX_SOCKOPT_REUSE_PORT, TRUE);
        HX_RELEASE(pSockOpt);

        if (HXR_OK != pUDPSocket2->QueryInterface(IID_IHXSetSocketOption, (void**)&pSockOpt))
        {
            rc = HXR_FAILED;
            goto cleanup;
        }

        pSockOpt->SetOption(HX_SOCKOPT_REUSE_ADDR, TRUE);
	pSockOpt->SetOption(HX_SOCKOPT_REUSE_PORT, TRUE);
        HX_RELEASE(pSockOpt);
    }
#endif /* HELIX_FEATURE_TRANSPORT_MULTICAST */

    if (HXR_OK != pUDPSocket1->Bind(HXR_INADDR_ANY, ulPort) ||
	HXR_OK != pUDPSocket2->Bind(HXR_INADDR_ANY, ulPort+1))
    {
        rc = HXR_FAILED;
        goto cleanup;
    }

cleanup:

    if (HXR_OK == rc)
    {
        (*m_pUDPSocketStreamMap)[ulStream] = pUDPSocket1;
        (*m_pRTCPSocketStreamMap)[ulStream] = pUDPSocket2;

        if (!m_bMulticast)
        {
	    // hang a read on these guys...
	    rc = pUDPSocket1->Read(HX_SAFEUINT(MAX_UDP_PACKET));
            if( rc != HXR_OUTOFMEMORY )
            {
	        rc = pUDPSocket2->Read(HX_SAFEUINT(MAX_UDP_PACKET));
            }
        }
    }
    else
    {
        HX_RELEASE(pUDPSocket1);
        HX_RELEASE(pUDPSocket2);
    }

    return rc;
}

void
RTSPClientProtocol::ReportError(
    HX_RESULT theErr
)
{
    if( m_pSession )
    {
        m_pSession->ReportError(theErr);
    }
}

void
RTSPClientSession::ReportError(
    HX_RESULT theErr
)
{
    IHXErrorMessages * pErrorNotifier = NULL;
    IUnknown * pPlayer = NULL;
    IHXClientEngine* pEngine = NULL;
    UINT32 nNumPlayers = 0;

    m_pContext->QueryInterface(IID_IHXClientEngine, (void**)&pEngine);
    if( pEngine )
    {
	nNumPlayers = pEngine->GetPlayerCount();
	for( int ii=0; ii<nNumPlayers; ii++ )
	{
	    pEngine->GetPlayer(ii,pPlayer);
	    if( pPlayer )
	    {
		pPlayer->QueryInterface( IID_IHXErrorMessages, (void**)&pErrorNotifier );
	    }
	    if( pErrorNotifier )
	    {
		pErrorNotifier->Report( HXLOG_ERR, theErr, 0, NULL, NULL );
		pErrorNotifier->Release();
            }
            HX_RELEASE( pPlayer );
	}
    }
    HX_RELEASE( pEngine );
}

HX_RESULT
RTSPClientProtocol::RetrieveReconnectInfo(MIMEHeader*	pHeader,
					  ReconnectType	reconnectType,
					  IHXValues*&	pReconnectValues)
{
    HX_RESULT		rc = HXR_OK;
    UINT32		ulRand = 0;
    IHXBuffer*		pServer = NULL;
    MIMEHeaderValue*	pHeaderValue = NULL;
    MIMEParameter*	pParam0 = NULL;
    MIMEParameter*	pParam1 = NULL;
    ReconnectInfo*	pReconnectInfo = NULL;
    CHXSimpleList	reconnectInfoList;
    CHXSimpleList::Iterator i;

    if (!pReconnectValues)
    {
	pReconnectValues = new CHXHeader();
	pReconnectValues->AddRef();
    }

    pReconnectValues->SetPropertyULONG32("Reconnect", 1);

    pHeaderValue = pHeader->getFirstHeaderValue();
    while (pHeaderValue)
    {
	pParam0 = pHeaderValue->getFirstParameter();
	pParam1 = pHeaderValue->getNextParameter();

	if (pParam0)
	{
	    pReconnectInfo = new ReconnectInfo;
	    pReconnectInfo->m_server = (const char*)pParam0->m_value;
	    if (pParam1)
	    {
		pReconnectInfo->m_ulPort = atoi((const char*)pParam1->m_value);
	    }
	}
	reconnectInfoList.AddTail(pReconnectInfo);

	pHeaderValue = pHeader->getNextHeaderValue();
    }

    if (!reconnectInfoList.IsEmpty())
    {
	ulRand = (HX_GET_TICKCOUNT() % reconnectInfoList.GetCount()) + 1;

	// random select the alternate server\port AND cleanup the list
	for(i = reconnectInfoList.Begin(); i != reconnectInfoList.End(); ++i)
	{
	    pReconnectInfo = (ReconnectInfo*)*i;
	    switch (ulRand)
	    {
	    case 0:
		break;
	    case 1:
		pServer = new CHXBuffer();
		pServer->AddRef();

		pServer->Set((const unsigned char*)(const char*)pReconnectInfo->m_server,
			     pReconnectInfo->m_server.GetLength()+1);

		if (reconnectType == ALTERNATE_SERVER)
		{
		    pReconnectValues->SetPropertyCString("Alternate-Server", pServer);
		    pReconnectValues->SetPropertyULONG32("Alternate-ServerPort", pReconnectInfo->m_ulPort);
		}
		else if (reconnectType == ALTERNATE_PROXY)
		{
		    pReconnectValues->SetPropertyCString("Alternate-Proxy", pServer);
		    pReconnectValues->SetPropertyULONG32("Alternate-ProxyPort", pReconnectInfo->m_ulPort);
		}

		HX_RELEASE(pServer);
		ulRand--;
		break;
	    default:
		ulRand--;
		break;
	    }
	    HX_DELETE(pReconnectInfo);
	}
	reconnectInfoList.RemoveAll();
    }

    return rc;
}

RTSPClientProtocol::UDPResponseHelper::UDPResponseHelper(RTSPClientProtocol* pParent, UINT16 nPort)
{
    m_lRefCount = 0;
    m_nPort = nPort;

    m_pOwner = pParent;
}

RTSPClientProtocol::UDPResponseHelper::~UDPResponseHelper()
{
}

STDMETHODIMP
RTSPClientProtocol::UDPResponseHelper::QueryInterface(REFIID riid, void** ppvObj)
{
	QInterfaceList qiList[] =
	{
		{ GET_IIDHANDLE(IID_IUnknown), this },
		{ GET_IIDHANDLE(IID_IHXUDPResponse), (IHXUDPResponse*) this },
		{ GET_IIDHANDLE(IID_IHXInterruptSafe), (IHXInterruptSafe*) 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)
RTSPClientProtocol::UDPResponseHelper::AddRef()
{
    return InterlockedIncrement(&m_lRefCount);
}

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

    delete this;
    return 0;
}

STDMETHODIMP
RTSPClientProtocol::UDPResponseHelper::ReadDone(HX_RESULT status, IHXBuffer* pBuffer,
						UINT32 ulAddr, UINT16 nPort)
{
    HX_RESULT retVal = HXR_FAILED;
    if (m_pOwner)
    {
        retVal = m_pOwner->ReadDoneWithToPort(status,
					    pBuffer,
					    ulAddr,
					    nPort,
					    m_nPort);
        if( retVal == HXR_OUTOFMEMORY )
        {
	    m_pOwner->ReportError( retVal );
        }
    }

    return retVal;
}

RTSPClientProtocol::ConnectionCheckCallback::ConnectionCheckCallback
(
    RTSPClientProtocol* pOwner
) : m_lRefCount(0)
  , m_pOwner(pOwner)
{
    if (m_pOwner)
    {
	m_pOwner->AddRef();
    }
}

RTSPClientProtocol::ConnectionCheckCallback::~ConnectionCheckCallback()
{
    HX_RELEASE(m_pOwner);
}

STDMETHODIMP
RTSPClientProtocol::ConnectionCheckCallback::QueryInterface(REFIID riid, void** ppvObj)
{
	QInterfaceList qiList[] =
	{
		{ GET_IIDHANDLE(IID_IUnknown), this },
		{ GET_IIDHANDLE(IID_IHXCallback), (IHXCallback*) 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)
RTSPClientProtocol::ConnectionCheckCallback::AddRef()
{
    return InterlockedIncrement(&m_lRefCount);
}

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

    delete this;
    return 0;
}

STDMETHODIMP
RTSPClientProtocol::ConnectionCheckCallback::Func()
{
    m_pOwner->AddRef();
    m_pOwner->DoConnectionCheck();
    m_pOwner->Release();
    return HXR_OK;
}

RTSPClientProtocol::TimeoutCallback::TimeoutCallback
(
    RTSPClientProtocol* pOwner
) : m_lRefCount(0)
  , m_pOwner(pOwner)
{
    if (m_pOwner)
    {
	m_pOwner->AddRef();
    }
}

RTSPClientProtocol::TimeoutCallback::~TimeoutCallback()
{
    // DON'T use HX_RELEASE
    // m_pOwner isn't derived from a COM interface
    if (m_pOwner)
    {
	m_pOwner->Release();
	m_pOwner = NULL;
    }
}

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

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

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

    delete this;
    return 0;
}

STDMETHODIMP
RTSPClientProtocol::TimeoutCallback::Func()
{
    m_pOwner->AddRef();

    IHXRTSPClientProtocol* pCP = (IHXRTSPClientProtocol*)m_pOwner;
    pCP->SendKeepAlive();

    m_pOwner->Release();
    return HXR_OK;
}

#if defined(_MACINTOSH)
RTSPClientProtocol::RTSPClientProtocolCallback::RTSPClientProtocolCallback
(
    RTSPClientProtocol* pOwner
) : m_lRefCount(0)
  , m_pOwner(pOwner)
  , m_bIsCallbackPending(FALSE)
  , m_Handle(0)
  , m_pPendingRequestHeaders(NULL)
{
    if (m_pOwner)
    {
	m_pOwner->AddRef();
    }
}

RTSPClientProtocol::RTSPClientProtocolCallback::~RTSPClientProtocolCallback()
{
    HX_RELEASE(m_pOwner);
    HX_RELEASE(m_pPendingRequestHeaders);
}

STDMETHODIMP
RTSPClientProtocol::RTSPClientProtocolCallback::QueryInterface(REFIID riid, void** ppvObj)
{
	QInterfaceList qiList[] =
	{
		{ GET_IIDHANDLE(IID_IUnknown), this },
		{ GET_IIDHANDLE(IID_IHXCallback), (IHXCallback*) 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)
RTSPClientProtocol::RTSPClientProtocolCallback::AddRef()
{
    return InterlockedIncrement(&m_lRefCount);
}

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

    delete this;
    return 0;
}

STDMETHODIMP
RTSPClientProtocol::RTSPClientProtocolCallback::Func()
{
    m_bIsCallbackPending = FALSE;
    m_Handle		= 0;

    m_pOwner->AddRef();
    m_pOwner->sendPendingStreamDescription(m_PendingDescURL, m_pPendingRequestHeaders);
    HX_RELEASE(m_pPendingRequestHeaders);
    m_pOwner->Release();
    return HXR_OK;
}
#endif /* _MACINTOSH */

RTSPClientSessionManager::RTSPClientSessionManager():
    m_lRefCount(0),
    m_pMutex(NULL)

{
#ifdef THREADS_SUPPORTED
	HXMutex::MakeMutex(m_pMutex);
#else
	HXMutex::MakeStubMutex(m_pMutex);
#endif
}

RTSPClientSessionManager::~RTSPClientSessionManager()
{
    CHXSimpleList::Iterator i;
    for(i=m_sessionList.Begin();i!=m_sessionList.End();++i)
    {
	RTSPClientSession* pSession = (RTSPClientSession*)(*i);
	pSession->Release();
    }
    m_sessionList.RemoveAll();

    HX_DELETE(m_pMutex);
}

/*
 * IUnknown methods
 */

STDMETHODIMP
RTSPClientSessionManager::QueryInterface(REFIID riid, void** ppvObj)
{
    if (IsEqualIID(riid, IID_IUnknown))
    {
	AddRef();
	*ppvObj = (IUnknown*)this;
	return HXR_OK;
    }

    *ppvObj = NULL;
    return HXR_NOINTERFACE;
}

STDMETHODIMP_(UINT32)
RTSPClientSessionManager::AddRef()
{
    return InterlockedIncrement(&m_lRefCount);
}

STDMETHODIMP_(UINT32)
RTSPClientSessionManager::Release()
{
    if (InterlockedDecrement(&m_lRefCount) > 0)
    {
	return m_lRefCount;
    }

    delete this;
    SessionManGlobal() = 0;

    return 0;
}

/*
 *  RTSPClientSessionManager methods
 */

RTSPClientSessionManager*& RTSPClientSessionManager::SessionManGlobal()
{
#if defined(HELIX_CONFIG_NOSTATICS)
    GlobalID globalID = (GlobalID)RTSPClientSessionManager::zm_pSessionManager;
    return (RTSPClientSessionManager*&)HXGlobalPtr::Get(globalID);
#else
    return RTSPClientSessionManager::zm_pSessionManager;
#endif
}

RTSPClientSessionManager*
RTSPClientSessionManager::instance()
{
    RTSPClientSessionManager*& pSessionManager = SessionManGlobal();

    if(!pSessionManager)
    {
	pSessionManager = new RTSPClientSessionManager;
    }
    pSessionManager->AddRef();

    return pSessionManager;
}

HX_RESULT
RTSPClientSessionManager::newSession(IUnknown* pContext,
				    RTSPClientProtocol* pProt,
				    const char* pHostName,
				    UINT16 uPort,
				    UINT32 ulActualAddr,
				    BOOL bUseProxy,
				    BOOL bHTTPOnly,
				    UINT16 uCloakPort)
{
    m_pMutex->Lock();
    RTSPClientSession* pSession = new RTSPClientSession;
    pSession->AddRef();
    m_sessionList.AddTail(pSession);

    if (pProt)
    {
	pProt->SessionCreated(pSession);
    }

    HX_RESULT hr = pSession->Init(pContext,
				  pProt,
				  pHostName,
				  uPort,
				  ulActualAddr,
				  bUseProxy,
				  bHTTPOnly,
				  uCloakPort);
    m_pMutex->Unlock();
    return hr;
}

int
RTSPClientSessionManager::getSessionCount()
{
    return m_sessionList.GetCount();
}

BOOL
RTSPClientSessionManager::MatchPlayerContext(IUnknown* pNewContext,
					     IUnknown* pKnownContext)
{
    BOOL	bResult = FALSE;
    IHXPlayer*	pNewHXPlayer = NULL;
    IHXPlayer* pKnownHXPlayer = NULL;

    if (!pNewContext || !pKnownContext)
    {
	goto cleanup;
    }

    if (HXR_OK == pNewContext->QueryInterface(IID_IHXPlayer, (void**)&pNewHXPlayer) &&
	HXR_OK == pKnownContext->QueryInterface(IID_IHXPlayer, (void**)&pKnownHXPlayer))
    {
	if (pNewHXPlayer == pKnownHXPlayer)
	{
	    bResult = TRUE;
	}
    }

cleanup:

    HX_RELEASE(pNewHXPlayer);
    HX_RELEASE(pKnownHXPlayer);

    return bResult;
}

HX_RESULT
RTSPClientSessionManager::removeFromSession(RTSPClientProtocol* pProt,
					    RTSPClientSession* pSessionRemoved)
{
    HX_RESULT hr = HXR_OK;

    LISTPOSITION pos = m_sessionList.GetHeadPosition();
    while(pos)
    {
	RTSPClientSession* pSession =
	    (RTSPClientSession*)m_sessionList.GetAt(pos);

	if(pSession == pSessionRemoved &&
	   HXR_OK == pSession->removeProtocol(pProt))
	{
	    if(pSession->isEmpty())
	    {
		pSession->Done();
		pSession->Release();
		pos = m_sessionList.RemoveAt(pos);
	    }
	    break;
	}
	m_sessionList.GetNext(pos);
    }

    return hr;
}

RTSPClientSession*
RTSPClientSessionManager::findSession(UINT32	    ulActualAddr,
				      UINT16	    uActualPort,
				      BOOL	    bUseProxy,
				      const char*   pForeignHost,
				      UINT16	    uForeignPort,
				      IUnknown*	    pContext /*= NULL*/)
{
    m_pMutex->Lock();
    RTSPClientSession* pSession = NULL;
    CHXSimpleList::Iterator i;
    for(i=m_sessionList.Begin();i!=m_sessionList.End();++i)
    {
	pSession = (RTSPClientSession*)(*i);

	if((pSession->m_ulActualAddr == ulActualAddr) &&
	   (pSession->m_uActualPort == uActualPort) &&

	   /* Either the context passed in is NULL OR it matches
	    * the current session context
	    */
	   (!pContext || MatchPlayerContext(pContext, pSession->m_pContext)))
	{
	    if (bUseProxy && pForeignHost)
	    {
		if (strcasecmp(pForeignHost, pSession->m_pForeignHost) != 0 ||
		    uForeignPort != pSession->m_uForeignPort)
		{
		    pSession = NULL;
		    continue;
		}
	    }

	    goto exit;
	}

	pSession = NULL;
    }

exit:
    m_pMutex->Unlock();
    return pSession;
}


#define QUEUE_START_SIZE    512

/*
 * RTSPClientSession methods
 */

RTSPClientSession::RTSPClientSession():
    m_lRefCount(0),
    m_pNetworkServices(0),
    m_pSessionSocket(0),
    m_bIgnoreSession(FALSE),
    m_bUseProxy(FALSE),
    m_pForeignHost(0),
    m_uForeignPort(0),
    m_ulActualAddr(0),
    m_pActualHost(NULL),
    m_uActualPort(0),
    m_uCloakPort(0),
    m_ulLastSeqNo(0),
    m_bSessionDone(FALSE),
    m_bHTTPOnly(FALSE),
    m_bReopenSocket(FALSE),
    m_pMutex(NULL),
    m_pContext(NULL),
    m_bChallengeDone(FALSE),
    m_bChallengeMet(FALSE),
    m_bIsValidChallengeT(FALSE),
    m_bSetSessionCalled(FALSE)
{
    m_pInQueue = new CByteGrowingQueue(QUEUE_START_SIZE);
    m_pInQueue->SetMaxSize(MAX_QUEUE_SIZE);
    m_pParser = new RTSPParser;
#ifdef THREADS_SUPPORTED
    HXMutex::MakeMutex(m_pMutex);
#else
    HXMutex::MakeStubMutex(m_pMutex);
#endif
}

RTSPClientSession::~RTSPClientSession()
{
    CHXSimpleList::Iterator i;
    for(i=m_protList.Begin();i!=m_protList.End();++i)
    {
	RTSPClientProtocolInfo* pInfo = (RTSPClientProtocolInfo*)(*i);
	delete pInfo;
    }

    delete m_pInQueue;
    delete m_pParser;
    HX_VECTOR_DELETE(m_pActualHost);
    HX_VECTOR_DELETE(m_pForeignHost);

    HX_RELEASE(m_pNetworkServices);
    HX_RELEASE(m_pSessionSocket);
    HX_RELEASE(m_pContext);

    HX_DELETE(m_pMutex);
}

HX_RESULT
RTSPClientSession::Init(IUnknown* pContext,
    			RTSPClientProtocol* pProt,
    			const char* pHostName,
			UINT16 uPort,
			UINT32 ulActualAddr,
			BOOL bUseProxy,
			BOOL bHTTPOnly,
			UINT16 uCloakPort)
{
    HX_RESULT	hr = HXR_OK;

    m_ulActualAddr = ulActualAddr;
    m_pActualHost = new_string(pHostName);
    m_bUseProxy = bUseProxy;
    m_bHTTPOnly = bHTTPOnly;

    // this port assignment logic should matches to the logic in
    // finding session at ::GetHostByNameDone()
    if (m_bUseProxy)
    {
	// if there is proxy, we need to also get the foreign host/port
	// in order to find the RTSP session which shares not only the
	// proxy host/port but also the foreign host/port
	if (pProt)
	{
	    pProt->GetForeignHostPort(m_pForeignHost, &(m_uForeignPort));
	}
	m_uActualPort = uPort;	    // proxy port
    }
    // if it is HTTP cloaking, we need to set the actual port
    // to the cloaking port
    else if (m_bHTTPOnly)
    {
	m_uActualPort = uCloakPort; // cloaking port
    }
    else
    {
	m_uActualPort = uPort;	    // server host port
    }

    m_bHTTPOnly = bHTTPOnly;

    addProtocol(pProt);

    m_pContext = pContext;
    if (m_pContext)
    {
	m_pContext->AddRef();
    }

    IHXTCPSocket* pSocket = 0;
    IHXCloakedNetworkServices* pCloakedNetServices = 0;
    IHXPreferences* pPreferences = 0;
    IHXBuffer* pBuffer = 0;

    if (HXR_OK != pContext->QueryInterface(IID_IHXPreferences,
					(void**)&pPreferences))
    {
	hr = HXR_INVALID_PARAMETER;
	goto cleanup;
    }

    if (HXR_OK != pContext->QueryInterface(IID_IHXNetworkServices,
					(void**)&m_pNetworkServices))
    {
	hr = HXR_OUTOFMEMORY;
	goto cleanup;
    }

    if (m_bHTTPOnly)
    {
	m_uCloakPort = uCloakPort;

	if (HXR_OK != pContext->QueryInterface(IID_IHXCloakedNetworkServices,
					(void**)&pCloakedNetServices))
	{
	    hr = HXR_OUTOFMEMORY;
	    goto cleanup;
	}

	pCloakedNetServices->CreateClientCloakedSocket(&pSocket);

	if (!pSocket)
	{
	    hr = HXR_OUTOFMEMORY;
	    goto cleanup;
	}

	IHXCloakedTCPSocket* pCloakedTCPSocket = NULL;
	if (HXR_OK == pSocket->QueryInterface(IID_IHXCloakedTCPSocket, (void**)&pCloakedTCPSocket))
	{
	    pCloakedTCPSocket->InitCloak(pProt->m_pCloakValues, pContext);
	}
	HX_RELEASE(pCloakedTCPSocket);

	if (pProt->m_bUseHTTPProxy)
	{
	    IHXHTTPProxy* pHTTPProxyInterface = NULL;

	    if (HXR_OK == pSocket->QueryInterface(IID_IHXHTTPProxy, (void**) &pHTTPProxyInterface) &&
		pHTTPProxyInterface)
	    {
		pHTTPProxyInterface->SetProxy(pProt->m_proxyHost, pProt->m_proxyPort);
	    }
	    HX_RELEASE(pHTTPProxyInterface);
	}

	// Connect() in HXClientCloakedTCPSocket requires the foreign host NOT
	// the proxy host
	pHostName = pProt->m_hostName;

	if (m_uCloakPort)
	    uPort = m_uCloakPort;
    }
    else
    {
	m_pNetworkServices->CreateTCPSocket(&pSocket);
    }

    hr = pSocket->Init((IHXTCPResponse*)this);
    if (hr != HXR_OK)
    {
	goto cleanup;
    }

    // already AddRef()'d in CreateTCPSocket
    m_pSessionSocket = pSocket;
    m_pConnectingProt = pProt;

    hr = m_pSessionSocket->Connect(pHostName, uPort);
    if(FAILED(hr))
    {
        ConnectDone(hr);
    }

cleanup:

    HX_RELEASE(pCloakedNetServices);
    HX_RELEASE(pPreferences);

    return hr;
}

HX_RESULT
RTSPClientSession::Done()
{
    m_pMutex->Lock();
    m_bSessionDone = TRUE;

    if(m_pSessionSocket)
    {
	m_pSessionSocket->Release();
	m_pSessionSocket = 0;
    }

    m_pMutex->Unlock();
    return HXR_OK;
}

HX_RESULT
RTSPClientSession::handleInput(BYTE* pData, UINT32 dataLen)
{
    INT32 ret = HXR_OK;

    if (dataLen)
    {
	ret = m_pInQueue->EnQueue(pData, (UINT16)dataLen);
        if( ret == 0 )
	{
	    // Why ABORT? Why not OUTOFMEMORY?
	    return HXR_ABORT;
	}
    }

    UINT16 bytesAvail = m_pInQueue->GetQueuedItemCount();
    UINT32 bytesUsed = 0;

    if(bytesAvail == 0)
    {
	return HXR_OK;
    }

    BYTE* pBuf = new BYTE[bytesAvail];
    if(!pBuf)
    {
        return HXR_OUTOFMEMORY;
    }

    for(;;)
    {
	RTSPClientProtocol* pProt = NULL;

	if (!bytesAvail || m_bSessionDone)
	{
	    break;
	}
	m_pInQueue->DeQueue(pBuf, bytesAvail);

	if(pBuf[0] == '$')
	{
	    bytesUsed = 0;
	    BOOL bGotData = FALSE;
	    if(bytesAvail >= 4)
	    {
		// handle TCP data
		INT8 interleave = pBuf[1];
		UINT16 tcpDataLen = (UINT16)getshort(&pBuf[2]);
		UINT32 currentDataLen = bytesAvail - 4;
		if(currentDataLen >= tcpDataLen)
		{
		    pProt = findProtocolFromInterleave(interleave);
		    if(pProt)
		    {
			ret = pProt->handleTCPData(&pBuf[4], tcpDataLen, interleave);
		    }
		    bytesUsed = tcpDataLen+4;
		    bytesAvail -= (UINT16)bytesUsed;
		    bGotData = TRUE;
		}
	    }
	    m_pInQueue->EnQueue(&pBuf[bytesUsed], (UINT16)bytesAvail);
	    if(!bGotData)
	    {
		break;
	    }
	}
	else
	{
	    bytesUsed = bytesAvail;
	    RTSPMessage* pMsg = m_pParser->parse((const char*)pBuf,
		bytesUsed);
	    bytesAvail -= (UINT16)bytesUsed;
	    m_pInQueue->EnQueue(&pBuf[bytesUsed], (UINT16)bytesAvail);
	    if (!pMsg)
	    {
		break;
	    }

	    // first always find protocol based on its sessionID
	    CHXString pszSessionID = "";

	    getSessionID(pMsg, &pszSessionID);
	    if (!pszSessionID.IsEmpty())
	    {
		pProt = findProtocolFromSessionID(&pszSessionID);
	    }

	    // then, based on the seq No.
	    if (!pProt)
	    {
		pProt = findProtocolFromSeqNo(pMsg->seqNo());
	    }

	    // then, we just grab the head from our protocol list
	    if (!pProt)
	    {
		RTSPClientProtocolInfo* pInfo =
		    (RTSPClientProtocolInfo*)m_protList.GetHead();
		pProt = pInfo?pInfo->m_pProt:NULL;
	    }

	    if(pProt)
	    {
		AddRef();
		pProt->AddRef();
		ret = pProt->handleMessage(pMsg);
		if(ret == HXR_OK)
		{
		    removeProtocolSeqNo(pProt, pMsg->seqNo());
		}
		pProt->Release();
		Release();
	    }
	    delete pMsg;
	}
    }
    delete[] pBuf;
    return ret;
}

UINT32
RTSPClientSession::getNextSeqNo(RTSPClientProtocol* pProt)
{
    m_pMutex->Lock();
    UINT32 seqNo = ++m_ulLastSeqNo;
    setProtocolSeqNo(pProt, seqNo);
    m_pMutex->Unlock();
    return seqNo;
}

RTSPClientProtocol*
RTSPClientSession::findProtocolFromInterleave(INT8 interleave)
{
    CHXSimpleList::Iterator i;
    for(i=m_protList.Begin();i!=m_protList.End();++i)
    {
	RTSPClientProtocolInfo* pInfo = (RTSPClientProtocolInfo*)(*i);
	void* pDummy;
	if(pInfo->m_interleaveMap.Lookup(interleave, pDummy))
	{
	    return pInfo->m_pProt;
	}
    }
    return 0;
}

RTSPClientProtocol*
RTSPClientSession::findProtocolFromSeqNo(UINT32 seqNo)
{
    CHXSimpleList::Iterator i;
    for(i=m_protList.Begin();i!=m_protList.End();++i)
    {
	RTSPClientProtocolInfo* pInfo = (RTSPClientProtocolInfo*)(*i);
	LISTPOSITION pos = pInfo->m_seqNoList.Find((void*)seqNo);
	if(pos)
	{
	    return pInfo->m_pProt;
	}
    }
    return 0;
}

RTSPClientProtocol*
RTSPClientSession::findProtocolFromSessionID(CHXString* pszSessionID)
{
    CHXSimpleList::Iterator i;
    for(i=m_protList.Begin();i!=m_protList.End();++i)
    {
	RTSPClientProtocolInfo* pInfo = (RTSPClientProtocolInfo*)(*i);
	if (pInfo->m_pProt &&
	    !pInfo->m_pProt->m_sessionID.IsEmpty())
	{
	    if(pszSessionID->CompareNoCase(pInfo->m_pProt->m_sessionID) == 0)
	    {
		return pInfo->m_pProt;
	    }
	}
    }

    return NULL;
}

void
RTSPClientSession::getSessionID(RTSPMessage* pMsg, CHXString* pszSessionID)
{
    MIMEHeader* pSessionID = pMsg->getHeader("Session");
    if(pSessionID)
    {
	MIMEHeaderValue* pHeaderValue = pSessionID->getFirstHeaderValue();
	if(pHeaderValue)
	{
	    MIMEParameter* pParam = pHeaderValue->getFirstParameter();
	    *pszSessionID = (const char*)pParam->m_attribute;
	}
    }

    return;
}

HX_RESULT
RTSPClientSession::addProtocol(RTSPClientProtocol* pProt)
{
    HX_RESULT rc = HXR_OK;
    m_pMutex->Lock();
    RTSPClientProtocolInfo* pInfo = new RTSPClientProtocolInfo;
    if(pInfo)
    {
        pInfo->m_pProt = pProt;
        m_protList.AddTail(pInfo);
    }
    else
    {
        rc = HXR_OUTOFMEMORY;
    }
    m_pMutex->Unlock();
    return rc;
}

HX_RESULT
RTSPClientSession::removeProtocol(RTSPClientProtocol* pProt)
{
    m_pMutex->Lock();
    HX_RESULT hr = HXR_FAIL;
    LISTPOSITION pos = m_protList.GetHeadPosition();
    while(pos)
    {
	RTSPClientProtocolInfo* pInfo =
	    (RTSPClientProtocolInfo*)m_protList.GetAt(pos);
	if(pInfo->m_pProt == pProt)
	{
	    delete pInfo;
	    m_protList.RemoveAt(pos);
	    hr = HXR_OK;
	    goto exit;
	}
	m_protList.GetNext(pos);
    }

exit:
    m_pMutex->Unlock();
    return hr;
}

BOOL
RTSPClientSession::isEmpty()
{
    return (m_protList.GetCount() == 0);
}

BOOL
RTSPClientSession::HttpOnly()
{
    return m_bHTTPOnly;
}

HX_RESULT
RTSPClientSession::setProtocolInterleave(
    RTSPClientProtocol* pProt, INT8 interleave)
{
    CHXSimpleList::Iterator i;
    for(i=m_protList.Begin();i!=m_protList.End();++i)
    {
	RTSPClientProtocolInfo* pInfo = (RTSPClientProtocolInfo*)(*i);
	if(pInfo->m_pProt == pProt)
	{
	    pInfo->m_interleaveMap[(UINT32)interleave] = pProt;
	    return HXR_OK;
	}
    }
    return HXR_FAIL;
}

HX_RESULT
RTSPClientSession::setProtocolSeqNo(RTSPClientProtocol* pProt,
    UINT32 seqNo)
{
    CHXSimpleList::Iterator i;
    for(i=m_protList.Begin();i!=m_protList.End();++i)
    {
	RTSPClientProtocolInfo* pInfo = (RTSPClientProtocolInfo*)(*i);
	if(pInfo->m_pProt == pProt)
	{
	    pInfo->m_seqNoList.AddTail((void*)seqNo);
	    return HXR_OK;
	}
    }
    return HXR_FAIL;
}

HX_RESULT
RTSPClientSession::removeProtocolSeqNo(RTSPClientProtocol* pProt,
    UINT32 seqNo)
{
    CHXSimpleList::Iterator i;
    for(i=m_protList.Begin();i!=m_protList.End();++i)
    {
	RTSPClientProtocolInfo* pInfo = (RTSPClientProtocolInfo*)(*i);
	if(pInfo->m_pProt == pProt)
	{
	    LISTPOSITION pos = pInfo->m_seqNoList.Find((void*)seqNo);
	    if(pos)
	    {
		pInfo->m_seqNoList.RemoveAt(pos);
		return HXR_OK;
	    }
	}
    }
    return HXR_FAIL;
}

IHXTCPSocket*
RTSPClientSession::getSocket()
{
    return m_pSessionSocket;
}

HX_RESULT
RTSPClientSession::closeSocket()
{
    m_pMutex->Lock();
    if(m_pSessionSocket)
    {
	m_pSessionSocket->Release();
	m_pSessionSocket = 0;
    }

    m_pMutex->Unlock();
    return HXR_OK;
}

HX_RESULT
RTSPClientSession::reopenSocket(RTSPClientProtocol* pProt)
{
    m_pMutex->Lock();

    IHXTCPSocket* pSocket = 0;
    HX_RESULT rc = HXR_OK;

    /*
     * Not checking for HTTP because connectionless control channel is not
     * currently slated to work with HTTP cloaking
     */

    m_pNetworkServices->CreateTCPSocket(&pSocket);

    if (!pSocket)
    {
	rc = HXR_OUTOFMEMORY;
	goto exit;
    }

    rc = pSocket->Init((IHXTCPResponse*)this);

    if (rc != HXR_OK)
    {
	goto exit;
    }

    HX_ASSERT(!m_pSessionSocket && !m_pConnectingProt);

    // already AddRef()'d in CreateTCPSocket
    m_pSessionSocket = pSocket;

    m_pConnectingProt = pProt;
    rc = m_pSessionSocket->Connect(m_pActualHost, m_uActualPort);

exit:
    m_pMutex->Unlock();
    return rc;
}

/*
 * IUnknown methods
 */

STDMETHODIMP
RTSPClientSession::QueryInterface(REFIID riid, void** ppvObj)
{
	QInterfaceList qiList[] =
	{
		{ GET_IIDHANDLE(IID_IUnknown), this },
		{ GET_IIDHANDLE(IID_IHXTCPResponse), (IHXTCPResponse*) this },
		{ GET_IIDHANDLE(IID_IHXInterruptSafe), (IHXInterruptSafe*) this },
	};
    return QIFind(qiList, QILISTSIZE(qiList), riid, ppvObj);
}

STDMETHODIMP_(UINT32)
RTSPClientSession::AddRef()
{
    return InterlockedIncrement(&m_lRefCount);
}

STDMETHODIMP_(UINT32)
RTSPClientSession::Release()
{
    if (InterlockedDecrement(&m_lRefCount) > 0)
    {
        return m_lRefCount;
    }

    delete this;
    return 0;
}

/*
 * IHXTCPResponse methods
 */

STDMETHODIMP
RTSPClientSession::ConnectDone(HX_RESULT status)
{
    HX_ASSERT(m_pConnectingProt);
#ifdef _MACINTOSH
    if (!m_pConnectingProt)
    {
	return HXR_FAIL;
    }
#endif

    if(HXR_OK == status)
    {
	if (!m_bHTTPOnly && !m_bUseProxy)
	{
	    m_bSetSessionCalled = TRUE;
	    m_pConnectingProt->SessionSucceeded(this, m_pSessionSocket);
	}

	if (m_bReopenSocket)
	{
	    m_pConnectingProt->ReopenSocketDone(HXR_OK);
	}
	else
	{
	    m_pConnectingProt->sendInitialMessage(this, m_pSessionSocket);
	    m_pConnectingProt->InitDone(HXR_OK);
	}
	m_pConnectingProt = 0;
	// start handleInput process
	return m_pSessionSocket->Read(MAX_RTSP_MSG);
    }

    m_bSetSessionCalled = TRUE;
    m_pConnectingProt->SessionFailed(this, m_pSessionSocket);

    m_pConnectingProt->AddRef();
    if (m_bReopenSocket)
    {
	m_pConnectingProt->ReopenSocketDone(HXR_NET_CONNECT);
    }
    else
    {
	m_pConnectingProt->InitDone(HXR_NET_CONNECT);
    }
    m_pConnectingProt->Release();
    m_pConnectingProt = 0;
    return HXR_FAIL;
}

STDMETHODIMP
RTSPClientSession::ReadDone(HX_RESULT status,
    IHXBuffer* pBuffer)
{
    HX_RESULT hresult = HXR_OK;

    if (m_bIgnoreSession)
    {
        return HXR_OK;
    }

    if(status == HXR_OK)
    {
	AddRef();
	m_pMutex->Lock();

	if (!m_bSetSessionCalled)
	{
	    m_bSetSessionCalled = TRUE;

	    LISTPOSITION pos = m_protList.GetHeadPosition();
	    while (pos)
	    {
		RTSPClientProtocolInfo* pInfo =
		    (RTSPClientProtocolInfo*)m_protList.GetNext(pos);

                if (pInfo->m_pProt->IsSessionSucceeded())
                {
                    m_bIgnoreSession = TRUE;
                    goto ignoreexit;
                }

                pInfo->m_pProt->SessionSucceeded(this, m_pSessionSocket);
	    }
	}

	hresult = handleInput(pBuffer->GetBuffer(), pBuffer->GetSize());
        if( hresult == HXR_OUTOFMEMORY )
        {
	    m_pMutex->Unlock();
	    Release();
            return hresult;
        }

	if (m_pSessionSocket && !m_bSessionDone)
	{
	    hresult = m_pSessionSocket->Read(MAX_RTSP_MSG);
	}
ignoreexit:
	m_pMutex->Unlock();
	Release();
    }
    else
    {
	AddRef();
	m_pMutex->Lock();

	LISTPOSITION pos = m_protList.GetHeadPosition();
	while (pos)
	{
	    RTSPClientProtocolInfo* pInfo =
		(RTSPClientProtocolInfo*)m_protList.GetNext(pos);
	    if (!m_bSetSessionCalled)
	    {
		pInfo->m_pProt->SessionFailed(this, m_pSessionSocket);
	    }
	    hresult = pInfo->m_pProt->OnProtocolError(status);
	}

	m_bSetSessionCalled = TRUE;

	m_pMutex->Unlock();
	Release();
    }

    if( hresult == HXR_OUTOFMEMORY )
    {
        ReportError( hresult );
    }
    // We have handled OOM errors, and it is not the responsibility of the
    // caller to handle our other errors, so we return HXR_OK.
    return HXR_OK;
}

STDMETHODIMP
RTSPClientSession::WriteReady(HX_RESULT status)
{
    return HXR_NOTIMPL;
}

STDMETHODIMP
RTSPClientSession::Closed(HX_RESULT status)
{
    return HXR_NOTIMPL;
}
