/****************************************************************************
 *
 *  Copyright (C) 2000-2001 RealNetworks, Inc. All rights reserved.
 *
 *  This program is free software.  It may be distributed under the terms
 *  in the file LICENSE, found in the top level of the source distribution.
 *
 */

#include "dbg.h"
#include "sock.h"
#include "tranhdr.h"

/**************************************
 *
 * CSingleTransportHdr
 *
 **************************************/
CSingleTransportHdr::CSingleTransportHdr( const CString& strValue ):
    m_strHdr( strValue ),
    m_nServerPorts( 0 ),
    m_nClientPorts( 0 ),
    m_serverBasePort( 0 ),
    m_clientBasePort( 0 ),
    m_bInterleaved( false )
{
    Parse( m_strHdr );
}

void CSingleTransportHdr::Parse( const CString& strValue )
{
    CPCHAR pCur, pNext;
    CPCHAR pVal;
    size_t nValLen;
    CRtspTransportHdrField field;

    // Get first section as transport and check for tcp
    pCur = strValue;
    assert_or_ret( pCur != NULL );

    field.type = RTSP_TRANSPORTHDR_OTHER;
    pNext = strchr( pCur, ';' );
    if( pNext != NULL )
    {
        field.strField.Set( pCur, (pNext-pCur) );
        pNext++;
    }
    else
    {
        field.strField = pCur;
    }
    m_listFields.InsertTail( field );
    pCur = pNext;

    // Parse transport options
    while( pCur )
    {
        field.type = RTSP_TRANSPORTHDR_OTHER;

        pNext = strchr( pCur, ';' );
        if( pNext != NULL )
        {
            field.strField.Set( pCur, (pNext-pCur) );
            pVal = (CPCHAR)memchr( pCur, '=', (pNext-pCur) );
            if( pVal )
            {
                pVal++;
                nValLen = pNext - pVal;
            }
            pNext++;
        }
        else
        {
            field.strField = pCur;
            pVal = strchr( pCur, '=' );
            if( pVal )
            {
                pVal++;
                nValLen = strlen( pVal );
            }
        }

        if( pVal != NULL )
        {
            if( strncasecmp( pCur, "client_port=", 12 ) == 0 )
            {
                field.type |= RTSP_TRANSPORTHDR_CLIENTPORT;

                m_nClientPorts = 1;
                //XXX: really cheezy parsing here
                if( memchr( pVal, '-', nValLen ) != NULL )
                { 
                    m_nClientPorts = 2;
                }

                //XXX: verify we have a valid port
                m_clientBasePort = atoi( pVal );
            }
            else if( strncasecmp( pCur, "server_port=", 12 ) == 0 )
            {
                field.type |= RTSP_TRANSPORTHDR_SERVERPORT;

                m_nServerPorts = 1;
                //XXX: really cheezy parsing here
                if( memchr( pVal, '-', nValLen ) != NULL )
                {
                    m_nServerPorts = 2;
                }
                //XXX: verify we have a valid port
                m_serverBasePort = atoi( pVal );
            }
            else if( strncasecmp( pCur, "source=", 7 ) == 0 )
            {
                field.type |= RTSP_TRANSPORTHDR_SOURCE;

                CString strHost( pVal, nValLen );
                m_addrSource.SetHost( strHost );
            }
            else if( strncasecmp( pCur, "interleaved=", 12 ) == 0 )
            {
                field.type |= RTSP_TRANSPORTHDR_INTERLEAVED;

                m_bInterleaved = true;
                m_nServerPorts = m_nClientPorts = 1;
                //XXX: really cheezy parsing here
                if( memchr( pVal, '-', nValLen ) != NULL )
                { 
                    m_nServerPorts = m_nClientPorts = 2;
                }
                //XXX: verify we have a valid port
                m_clientBasePort = m_serverBasePort = atoi( pVal );
            }
        }
        m_listFields.InsertTail( field );
        pCur = pNext;
    }
}

void CSingleTransportHdr::GetServerBasePort( UINT16 * pBasePort, int * nPorts )
{
    *pBasePort = m_serverBasePort;
    *nPorts = m_nServerPorts;
}

void CSingleTransportHdr::GetClientBasePort( UINT16 * pBasePort, int * nPorts )
{
    *pBasePort = m_clientBasePort;
    *nPorts = m_nClientPorts;
}
void CSingleTransportHdr::SetServerBasePort( UINT16 basePort )
{
    m_serverBasePort = basePort;
}
void CSingleTransportHdr::SetClientBasePort( UINT16 basePort )
{
    m_clientBasePort = basePort;
}

void CSingleTransportHdr::SetSourceAddr( const CInetAddr& addrSource )
{
    m_addrSource = addrSource;
}

bool CSingleTransportHdr::IsInterleaved( void )
{
    return m_bInterleaved;
}

CPCHAR CSingleTransportHdr::GetHdrString( void )
{
    char strRet[255];
    char * p = strRet;
    CRtspTransportHdrField field;
    CRtspTransportHdrFieldList::Iterator itr( m_listFields.Begin() );
    while( itr )
    {
        field = *itr;
        if( field.type & RTSP_TRANSPORTHDR_CLIENTPORT )
        {
            if( m_nClientPorts == 2 )
                p += sprintf( p, "client_port=%hu-%hu", m_clientBasePort, m_clientBasePort+1 );
            else if( m_nClientPorts == 1 )
                p += sprintf( p, "client_port=%hu", m_clientBasePort );
        }
        else if( field.type & RTSP_TRANSPORTHDR_SERVERPORT )
        {
            if( m_nServerPorts == 2 )
                p += sprintf( p, "server_port=%hu-%hu", m_serverBasePort, m_serverBasePort+1 );
            else if( m_nServerPorts == 1 )
                p += sprintf( p, "server_port=%hu", m_serverBasePort );
        }
        else if( field.type & RTSP_TRANSPORTHDR_SOURCE )
        {
            p += sprintf( p, "source=%s", inet_ntoa(m_addrSource) );
        }
        else if( field.type & RTSP_TRANSPORTHDR_INTERLEAVED )
        {
            if( m_nServerPorts == 2 )
                p += sprintf( p, "interleaved=%hu-%hu", m_serverBasePort, m_serverBasePort+1 );
            else if( m_nServerPorts == 1 )
                p += sprintf( p, "interleaved=%hu", m_serverBasePort );
        }
        else
        {
            p += sprintf( p, "%s", ( CPCHAR )field.strField );
        }
        itr++;
        if( itr )
        {
            *( p++ ) = ';';
        }
    }
    m_strHdr = strRet;
    return m_strHdr;
}

/**************************************
 *
 * CRequestTransportHdr
 *
 **************************************/
CRequestTransportHdr::CRequestTransportHdr(  const CString& strValue ) :
    m_strHdr( strValue ),
    m_bIsInterleaved( true )
{
    Parse();    
}

CRequestTransportHdr::~CRequestTransportHdr( void )
{
    while( !m_listTransports.IsEmpty() )
    {
        CSingleTransportHdr* pHdr = m_listTransports.RemoveHead();
        delete pHdr;
    }
}

void CRequestTransportHdr::Parse( void )
{
    CPCHAR pCur, pNext;
    CSingleTransportHdr* pSingleHdr = NULL;

    pCur = m_strHdr;
    assert_or_ret( pCur != NULL );
    while( pCur )
    {
        CString strSingleTran;
        pNext = strchr( pCur, ',' );
        if( pNext )
        {
            strSingleTran.Set( pCur, (pNext-pCur) );
            pNext++;
        }
        else
        {
            strSingleTran = pCur;
        }
        pSingleHdr = new CSingleTransportHdr( strSingleTran );

        m_bIsInterleaved = m_bIsInterleaved && pSingleHdr->IsInterleaved();
        m_listTransports.InsertTail( pSingleHdr );

        pCur = pNext;
    }
}

void CRequestTransportHdr::GetBasePort( UINT16 * pBasePort, int * pnPorts )
{
    int nPorts = 0;
    UINT16 basePort = 0;
    CTransportHdrList::Iterator itr( m_listTransports.Begin() );
    
    *pnPorts = 0;
    while( itr )
    {
        CSingleTransportHdr * pHdr = *itr;
        pHdr->GetClientBasePort( &basePort, &nPorts );
        if( nPorts == 2 )
        {
            *pnPorts = nPorts;
            *pBasePort = basePort;
            return;
        }
        else if( nPorts > *pnPorts )
        {
            *pnPorts = nPorts;
            *pBasePort = basePort;
        }
        itr++;
    }
}

void CRequestTransportHdr::SetPort( UINT16 basePort )
{
    CTransportHdrList::Iterator itr( m_listTransports.Begin() );
    
    while( itr )
    {
        CSingleTransportHdr * pHdr = *itr;
        pHdr->SetClientBasePort( basePort );
        itr++;
    }
}

bool CRequestTransportHdr::IsInterleaved( void )
{
    return m_bIsInterleaved;
}

CPCHAR CRequestTransportHdr::GetHdrString( void )
{
    m_strHdr = "";
    CTransportHdrList::Iterator itr( m_listTransports.Begin() );   
    while( itr )
    {
        CSingleTransportHdr * pHdr = *itr;
        m_strHdr.Append( pHdr->GetHdrString() );
        itr++;
        if( itr )
        {
            m_strHdr.Append( "," );
        }
    }
    return (CPCHAR)m_strHdr;
}


syntax highlighted by Code2HTML, v. 0.9.1