Logo Search packages:      
Sourcecode: helix-player version File versions

rtspclnt.cpp

/* ***** 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;
}

Generated by  Doxygen 1.6.0   Back to index