upnp/upnpstack/ssdpserver/src/upnpudpserver.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Tue, 02 Feb 2010 01:12:20 +0200
changeset 0 f5a58ecadc66
child 31 b51a6f4f15ba
permissions -rw-r--r--
Revision: 201003

/** @file
* Copyright (c) 2005-2008 Nokia Corporation and/or its subsidiary(-ies). 
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of "Eclipse Public License v1.0"
* which accompanies  this distribution, and is available 
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description:  CUpnpUdpServer
*
*/


// INCLUDE FILES
#include <in_iface.h>
#include <e32math.h>
#include <commdb.h>

#include "upnpcons.h"
#include "upnpudpsendrequest.h"
#include "upnpudpserver.h"
#define KLogFile _L("upnpudpserver.txt")
#include "upnpcustomlog.h"
#include "upnpstring.h"
#include "upnplist.h"
#include "upnpconnectionmanagerproxy.h"


// CONSTANTS
const TUint32 KUdpMessageSize = 2048;
const TInt KTTLValue = 4;
const TInt KMicrosecond = 1000000;
const TInt KMXMaxValue = 120;
const TInt KInetAddrPrefixMatchLen = 16;

// ============================= LOCAL FUNCTIONS ===============================
// -----------------------------------------------------------------------------
// CleanupArray
// Used by TCleanupItem to clean a RPointerArray<TPtrC>
// -----------------------------------------------------------------------------
//
void CleanupArray( TAny* aArray )
    {
    ( reinterpret_cast<RPointerArray<TPtrC8>*>( aArray ) )->ResetAndDestroy();
    }

// ============================ MEMBER FUNCTIONS ===============================

// -----------------------------------------------------------------------------
// CUpnpUdpServer::CUpnpUdpServer
// C++ default constructor can NOT contain any code, that
// might leave.
// -----------------------------------------------------------------------------
//
CUpnpUdpServer::CUpnpUdpServer( RSocketServ* aSocketServ,
                        MUpnpUdpServerObserver* aObserver,
                        TInt aListeningPort )
    : CActive( EPriorityHigh ),
      iMulticastFlags( 0 ),
      iMessagePtr( NULL, 0,0 ),
      iIsStarted( EFalse )
    {
    LOGS( "SSDP *** CUpnpUdpServer::CUpnpUdpServer");

    CActiveScheduler::Add( this );
    iSocketServ = aSocketServ;
    iObserver = aObserver;
    iServerPort = aListeningPort;

    iRandomSeed = KErrNone;
    }

// -----------------------------------------------------------------------------
// CUpnpUdpServer::UdpConstructL
// Symbian 2nd phase constructor can leave.
// -----------------------------------------------------------------------------
//
void CUpnpUdpServer::UdpConstructL()
    {
    iSendMessage = NULL;

    iPendingTimerMessages = new (ELeave) CArrayPtrSeg<CUpnpSsdpMessage>( 1 );

    // Create local timer
    User::LeaveIfError( iSendTimer.CreateLocal() );

    iConnectionManagerProxy = CUpnpConnectionManagerProxy::NewL( *iSocketServ );
    TInt error = iConnectionManagerProxy->EnsureStart();

    if ( error )
        {
        LOGS1( "CUpnpTcpServer::ListenL *** Error in attaching RConnection: error: %d",
            error );
        // Nothing else to do but leave, connection is not possible and
        // can't access network
        User::LeaveIfError( error );
        }

    iActiveIap = iConnectionManagerProxy->ActiveIap();
    iLocalInterfaceAddress = iConnectionManagerProxy->LocalAddress();

    RefreshLocalAddressInfo();
    }

// -----------------------------------------------------------------------------
// CUpnpUdpServer::~CUpnpUdpServer()
// Destructor.
// -----------------------------------------------------------------------------
//
CUpnpUdpServer::~CUpnpUdpServer()
    {
    LOGS( "SSDP *** CUpnpUdpServer::~CUpnpUdpServer");
    Cancel();
    Close();

    iSendTimer.Close();

    iSendRequestList.ResetAndDestroy();
    iSendRequestList.Close();

    delete iSendMessage;

    delete iMessage;

    if( iPendingTimerMessages )
        {
        // Go through the messages and cancel timers
        for(TInt i=0; i< iPendingTimerMessages->Count(); i++)
            {
            CUpnpSsdpMessage* arrayMsg = iPendingTimerMessages->At( i );
            if( arrayMsg )
                {
                // Cancel the timer in the message
                arrayMsg->CancelMessageTimeout();
                }
            }
        // Destroy the whole message array
        iPendingTimerMessages->ResetAndDestroy();
        delete iPendingTimerMessages;
        iPendingTimerMessages = NULL;
        }

    // close network connection
    delete iConnectionManagerProxy;
    }

// -----------------------------------------------------------------------------
// CUpnpUdpServer::JoinMulticastGroupL
// -----------------------------------------------------------------------------
//
void CUpnpUdpServer::JoinMulticastGroupL()
    {
    TIp6Mreq mreq;
    // the ipv4 multicast address that should use
    TInetAddr addr;
    addr.SetAddress( KDefaultMulticastAddress );
    addr.SetFamily( KAfInet );
    addr.ConvertToV4Mapped();

    mreq.iAddr = addr.Ip6Address();
    mreq.iInterface = 0;
    TPckgBuf< TIp6Mreq > opt( mreq );

    LOGS( "SSDP CUpnpUdpServer::JoinMulticastGroupL" );
    User::LeaveIfError( iSocket.SetOpt( KSoIp6JoinGroup, KSolInetIp, opt ) );

    }

// -----------------------------------------------------------------------------
// CUpnpUdpServer::ApplyMulticastSocketSettingsL
// -----------------------------------------------------------------------------
//
void CUpnpUdpServer::ApplyMulticastSocketSettingsL()
    {
    LOGS( "SSDP CUpnpUdpServer::ApplyMulticastSocketSettingsL" );

    User::LeaveIfError( iSocket.SetOpt( KSoIp6MulticastLoop, KProtocolInetIp, 0 ) );
    User::LeaveIfError( iSocket.SetOpt( KSoReuseAddr, KProtocolInetIp, 1 ) );

    JoinMulticastGroupL();
    }

// -----------------------------------------------------------------------------
// CUpnpUdpServer::ApplyCommonSocketSettingsL
// -----------------------------------------------------------------------------
//
void CUpnpUdpServer::ApplyCommonSocketSettingsL()
    {
    LOGS( "SSDP CUpnpUdpServer::ApplyCommonSocketSettingsL Socket TTL" );

    User::LeaveIfError( iSocket.SetOpt( KSoIpTTL, KSolInetIp, KTTLValue ) );
    User::LeaveIfError( iSocket.SetOpt( KSoIp6MulticastHops, KSolInetIp,
                                        KTTLValue ) );
    }

// -----------------------------------------------------------------------------
// CUpnpUdpServer::OpenL
// -----------------------------------------------------------------------------
//
void CUpnpUdpServer::OpenL( )
    {
    if ( !IsStarted() )
        {
        TRAPD( error, OpenSocketAndStartReceiveL() )
        if ( error )
            {
            LOGS1( "SSDP CUpnpUdpServer::OpenL error: %d", error );
            Close();
            User::Leave( error );
            }
        else
            {
            iIsStarted = ETrue;
            }
        }
    }

// -----------------------------------------------------------------------------
// CUpnpUdpServer::OpenSocketAndStartReceiveL
// -----------------------------------------------------------------------------
//
void CUpnpUdpServer::OpenSocketAndStartReceiveL( )
    {
    LOGS( "SSDP CUpnpUdpServer::OpenSocketAndStartReceiveL");
    TInt error = iSocket.Open( *iSocketServ, KAfInet,
        KSockDatagram, KProtocolInetUdp, iConnectionManagerProxy->ConnectionL() );
    User::LeaveIfError( error );

    User::LeaveIfError( iSocket.SetLocalPort( iServerPort ) );
    // for multicast start
    if ( KDefaultMulticastPort == iServerPort )
        {
        ApplyMulticastSocketSettingsL();
        }

    ApplyCommonSocketSettingsL();

    TInetAddr bindAddr;
    error = iSocket.Bind( bindAddr ) ;
    if( KErrNone != error && KErrAlreadyExists != error )
        {
        LOGS( "SSDP CUpnpUdpServer::OpenSocketAnd.. bind error: %d");
        User::Leave( error );
        }

    ReceiveL();
    }

// -----------------------------------------------------------------------------
// CUpnpUdpServer::IsStarted
// -----------------------------------------------------------------------------
//
TBool CUpnpUdpServer::IsStarted()
    {
    return iIsStarted;
    }

// -----------------------------------------------------------------------------
// CUpnpUdpServer::SendL
// Send a message.
// -----------------------------------------------------------------------------
//
void CUpnpUdpServer::SendL( TDesC8& aBuffer, TInetAddr& anAddr )
    {
    LOGS( "SSDP *** CUpnpUdpServer::SendL");

    if ( !IsStarted() )
        {
        return;
        }

    if ( IsActive() && ( iState == ESendingTimer || iState == ESendDone ) )
        {
        CUpnpUdpSendRequest* req = CUpnpUdpSendRequest::NewL( aBuffer, anAddr );
        CleanupStack::PushL( req );
        iSendRequestList.AppendL( req );
        CleanupStack::Pop( req );
        }
    else
        {
        Cancel();

        delete iSendMessage;
        iSendMessage = NULL;
        iSendMessage = aBuffer.AllocL();

        iSenderAddress = anAddr;
        iSenderAddress.SetFamily( KAfInet );

        iSenderAddress.ConvertToV4Mapped();

        iSocket.SendTo( *iSendMessage,
                        iSenderAddress,
                        iMulticastFlags,
                        iStatus );

        iState = ESendingTimer; // in this state when RunL will be called after sending
                                // message successfuly, the delay timer will start
        SetActive();
        }
    }

// -----------------------------------------------------------------------------
// CUpnpUdpServer::SendDoneL
// Sending is done.
// -----------------------------------------------------------------------------
//
void CUpnpUdpServer::SendDoneL()
    {
    LOGS( "SSDP *** CUpnpUdpServer::SendDoneL");

    delete iSendMessage;
    iSendMessage = NULL;

    if ( iSendRequestList.Count() > 0 )
        {
        CUpnpUdpSendRequest* request = iSendRequestList[0];
        iSendRequestList.Remove(0);
        iSendRequestList.Compress();
        CleanupStack::PushL( request );
        SendL( *(request->iBuffer), request->iAddr );
        CleanupStack::PopAndDestroy( request );
        }
    else
        {
        ReceiveL();
        }
    }

// -----------------------------------------------------------------------------
// CUpnpUdpServer::ReceiveL
// Receive message.
// -----------------------------------------------------------------------------
//
void CUpnpUdpServer::ReceiveL()
    {
    LOGS( "SSDP *** CUpnpUdpServer::ReceiveL");

    if ( !IsActive() )
        {
        // If iMessage is not yet created, do it now.
        // This pointer will be used until SSDP server is closed down.
        if ( !iMessage )
            {
            iMessage = HBufC8::NewL( KUdpMessageSize );
            }

        // Set data length to zero for the next RecvFrom
        iMessage->Des().SetLength( 0 );

        iMessagePtr.Set( (unsigned char*) iMessage->Ptr(), 0, KUdpMessageSize );

        iClientAddr.SetPort( iServerPort );

        iSocket.RecvFrom( iMessagePtr, iClientAddr, iMulticastFlags, iStatus );

        SetActive();
        iState = EReceiving;
        }
    }

// -----------------------------------------------------------------------------
// CUpnpUdpServer::RunL
// -----------------------------------------------------------------------------
//
void CUpnpUdpServer::RunL()
    {

    if ( iStatus.Int() != KErrNone )
        {
        LOGS1( "SSDP *** CUpnpUdpServer::RunL - UDPServer error, error code: %d",
            iStatus.Int() );

        if ( iStatus.Int() != KErrCancel )
            {
            ReceiveL();
            }
        else
            {
            RestartOrContinueSendProcessingL();
            }
        }
    else
        {
        switch ( iState )
            {
            case ESendingTimer:                                // Introduce delay between sending messages to prevent from
                // Introduce delay between sending messages to prevent from
                iSendTimer.Cancel();               
                iSendTimer.After( iStatus, KSendingDelay ); // SSDP notification storm.
                iState = ESendDone;
                SetActive();
                break;
            case EReceiving:
                {
                TUint temp = iClientAddr.Address();

                TUint port = iClientAddr.Port();

                TInetAddr addr = TInetAddr( temp, port );

                addr.SetFamily( KAfInet );
#ifdef _DEBUG
                TBuf8<KAddressLength> address;
                CUpnpHttpMessage::AddrOutput(addr, address);

                TBuf<KMaxName> logBuf;
                    logBuf.Copy( address.Left( logBuf.MaxLength() ) );
                LOGS1( "SSDP *** CUpnpUdpServer::RunL - Received buffer from: %S",
                    &logBuf );
#endif //_DEBUG

                CUpnpSsdpMessage* msg = NULL;

                TRAPD( err, msg = CUpnpSsdpMessage::NewL(
                    (TDesC8&) iMessagePtr, addr ) );
                if ( err )
                    {
                    delete msg;
                    msg = NULL;
                    LOGS( "SSDP *** CUpnpUdpServer::RunL - Ssdp message parse failed. Ingoring message.");
                    RestartOrContinueProcessingL();
                    return;
                    }
                CleanupStack::PushL(msg);
                // check that given message is valid.
                TInt error = ValidateSsdpMessageL( msg );
                if( error == KErrNone )
                    {

                    // no error, message is put to receive queue, and it gets
                    // handled when the timer expires.
                    // KErrNone used for constant, because it always remains
                    // the same.
                    if( msg->IsSsdpMSearch() )
                        {
                        CleanupStack::Pop( msg );

                        AddPendingTimerMessageL( msg );

                        // no delete, message in array
                        msg = NULL;
                        }
                    else
                        {
                        //checks wether the Source IP is in same network i.e. auto-IP(169.254.X.X) or non-auto-IP range

                        TInetAddr autoIPAddr = TInetAddr( KDefaultAutoIPAddress,
                                                          KDefaultMulticastPort );
                        autoIPAddr.SetFamily( KAfInet );

                        if ( iAutoIP && autoIPAddr.Match( addr, KInetAddrPrefixMatchLen ) )
                            {
                            LOGS( "SSDP *** MESSAGE_FROM_AUTO_VERIFIED_NETWORK");
                            iObserver->UdpMessageReceivedL( msg );
                            }
                        else if ( !autoIPAddr.Match( addr, KInetAddrPrefixMatchLen ) )
                            {
                            LOGS( "SSDP *** MESSAGE_FROM_NON_AUTO_VERIFIED_NETWORK");
                            iObserver->UdpMessageReceivedL( msg );
                            }

                        CleanupStack::PopAndDestroy(msg);
                        }
                    }
                else
                    {
                    // message not valid, delete it.
                    LOGS( "SSDP *** CUpnpUdpServer::RunL - Invalid SSDP message. Ingoring message.");
                    CleanupStack::PopAndDestroy(msg);
                    }

                RestartOrContinueProcessingL();

                }
                break;
            case ESending:
                delete iSendMessage;
                iSendMessage = NULL;
                RestartOrContinueProcessingL();
                break;

            case ESendDone:
                SendDoneL();
                break;

            default:
                // message not valid, delete it.
                LOGS( "SSDP *** CUpnpUdpServer::RunL - DEFAULT CASE, NOTHING TO DO!");
                break;
            }
        }
    }

// -----------------------------------------------------------------------------
// CUpnpSsdpServer::RunError
// Active Object RunError called when RunL leaves.
// -----------------------------------------------------------------------------
//
TInt CUpnpUdpServer::RunError( TInt aError )
    {
LOGS1( "SSDP *** CUpnpUdpServer::RunError: %d", aError );
    TRAP_IGNORE( RestartOrContinueProcessingL() );
    return KErrNone;
    }

// -----------------------------------------------------------------------------
// CUpnpUdpServer::DoCancel
// Active object DoCancel cancels active request.
// -----------------------------------------------------------------------------
//
void CUpnpUdpServer::DoCancel()
    {
    iSocket.CancelAll();
    iSendTimer.Cancel();    
    }

// -----------------------------------------------------------------------------
// CUpnpUdpServer::CloseL
// -----------------------------------------------------------------------------
//
void CUpnpUdpServer::Close()
    {
    LOGS( "SSDP *** CUpnpUdpServer::Close");

    if ( IsStarted() ) // socket can be not open correctly
        {
        Cancel();
        // for multicast start
        if(iServerPort == KDefaultMulticastPort)
            {
            TIp6Mreq mreq;

            TInetAddr addr;
            addr.SetAddress( KDefaultMulticastAddress );
            addr.SetPort(KDefaultMulticastPort);
            addr.SetFamily(KAfInet);
            addr.ConvertToV4Mapped();
            mreq.iAddr = addr.Ip6Address();

            mreq.iInterface = 0;
            TPckgBuf< TIp6Mreq > opt(mreq);
            iSocket.SetOpt(KSoIp6LeaveGroup, KSolInetIp, opt);
            }
        // for multicast end.
        }
    iSocket.Close();
    iIsStarted = EFalse;
    }

// -----------------------------------------------------------------------------
// CUpnpUdpServer::ValidateSsdpMessageL
// Function validates SSDP messages. It checks that correct headers are
// found etc.
// (other items were commented in a header).
// -----------------------------------------------------------------------------
//
CUpnpUdpServer::TSsdpValidity CUpnpUdpServer::ValidateSsdpMessageL( CUpnpSsdpMessage* aMsg )
    {
    CUpnpUdpServer::TSsdpValidity validity = CUpnpUdpServer::EMessageOk;

    // Message is search message
    if( aMsg->IsSsdpMSearch() )
        {
        // checking first line of message
        CUpnpHttpHeaderList* list=aMsg->HeaderList();
        CUpnpHttpHeader* header = list->First();
        if( header )
            {
            if(!CompareLinesL(header->Name(),UpnpSSDP::KMethodMSearch()))
                {
                // First line of message not "M-SEARCH * HTTP/1.1".
                // Invalid message.
               validity = CUpnpUdpServer::EHeaderNotMSearch;
                }
            }
        else
            {
            // no headers. Invalid message. no further handling.
            validity = CUpnpUdpServer::EHeadersMissing;
            return validity;
            }

        // no delete, owned by message.
        header=NULL;
        // no delete, owned by message.
        list=NULL;

        TDesC8& mxString = aMsg->GetHeaderValue( UpnpSSDP::KHdrMx() );

        // if no MX header found, not valid message.
        if( mxString != KNullDesC8() )
            {
            // try to parse value to TInt. If possible, then message is valid for MX check.
            TInt mxValue = 0;
            TInt errorInConversion = 0;
            TLex8 lex( mxString );
            errorInConversion = lex.Val(mxValue);
            // check that there are no errors in conversion. If error in conversion,
            // not valid SSDP MX header.
            if( errorInConversion != KErrNone || lex.Remainder().Length() > 0)
                {
                validity = CUpnpUdpServer::EErrorInMxConversion;
                }
            else if( mxValue < 0 )
                {
                // check that MX value is positive. Negative value not valid.
                validity = CUpnpUdpServer::EInvalidMXValue;
                }

            // Checking if MAN header is found.
            TDesC8& manString = aMsg->GetHeaderValue( UpnpSSDP::KHdrMan() );
            if( manString != KNullDesC8() )
                {
                // MAN header found. Checking that is has valid value.
                if( manString != UpnpSSDP::KNotificationDiscover() )
                    {
                    // MAN header value not "ssdp:discover". Invalid header.
                    validity = CUpnpUdpServer::EInvalidManHeader;
                    }
                }
            else
                {
                // MAN header not found. Invalid message.
                validity = CUpnpUdpServer::EInvalidManHeader;
                }

            // Check that ST header is found.
            TDesC8& stString = aMsg->GetHeaderValue( UpnpSSDP::KHdrSt() );
            if( stString == KNullDesC8() )
                {
                // no ST header found. invalid SSDP search.
                validity = CUpnpUdpServer::EInvalidStHeader;
                }

            TDesC8& hostString = aMsg->GetHeaderValue( UpnpSSDP::KHdrHost() );
            if( hostString != UpnpSSDP::KDefaultHost() )
                {
                // HOST header not "239.255.255.250:1900". Invalid message.
                validity = CUpnpUdpServer::EInvalidHostHeader;
                }
            }
        else
            {
            // no MX header.
            validity = CUpnpUdpServer::EInvalidMXValue;
            }
        }


    // Message is SSDP response
    else if( aMsg->IsSsdpResponse() )
        {
        // if message is SSDP response, header should
        // be "HTTP/1.1 200 OK".
        CUpnpHttpHeaderList* list=aMsg->HeaderList();
        CUpnpHttpHeader* header = list->First();
        if( header )
            {
            if (!CompareLinesL(header->Name(),UpnpHTTP::KHTTPOk()))
                {
                // First line of message not "HTTP/1.1 200 OK".
                // Invalid message.
                LOGS( "SSDP *** CUpnpUdpServer::EBadSsdpResponse");
                validity = CUpnpUdpServer::EBadSsdpResponse;
                }
            }
        else
            {
            // no headers. Invalid message. no further handling.
            LOGS( "SSDP *** CUpnpUdpServer::EHeadersMissing");
            validity = CUpnpUdpServer::EHeadersMissing;
            return validity;
            }

        // no delete, owned by message.
        header=NULL;
        // no delete, owned by message.
        list=NULL;

        TDesC8& cache = aMsg->GetHeaderValue( UpnpSSDP::KHdrCacheControl() );
        if( cache != KNullDesC8() )
            {
            TPtrC8 cachePtr;
            cachePtr.Set( cache );

            TInt posOfMaxAge = cachePtr.FindC( UpnpSSDP::KSsdpMaxAge() );
            TInt posOfEqual = cachePtr.FindC( UpnpString::KEqual() );
            if (posOfMaxAge != KErrNotFound && posOfEqual!= KErrNotFound
                                            && posOfMaxAge < posOfEqual)
                {
                cachePtr.Set( cachePtr.Right((cachePtr.Length()
                    - UpnpString::KEqual().Length()) - posOfEqual) );
                }

            TInt cacheValue = 0;
            TInt errorInConversion = 0;
            TLex8 lex( cachePtr );
            lex.SkipSpace();
            errorInConversion = lex.Val(cacheValue);
            // check that there are no errors in conversion.
            // If error in conversion, not valid
            // SSDP CACHE-CONTROL header.
            if( errorInConversion < KErrNone )
                {
                LOGS( "SSDP *** CUpnpUdpServer::EInvalidCacheControlHeader");
                validity = CUpnpUdpServer::EInvalidCacheControlHeader;
                }
            else if ( cacheValue < 0 )
                {
                // check that CACHE-CONTROL value is positive.
                // Negative value not valid.
                LOGS( "SSDP *** CUpnpUdpServer::EInvalidCacheControlHeader");
                validity = CUpnpUdpServer::EInvalidCacheControlHeader;
                }
            }
        else
            {
            // no CACHE-CONTROL header. Invalid message.
            LOGS( "SSDP *** CUpnpUdpServer::EInvalidCacheControlHeader");
            validity = CUpnpUdpServer::EInvalidCacheControlHeader;
            }

        }

    return validity;
    }

// -----------------------------------------------------------------------------
// CUpnpUdpServer::CompareLines
// Compare two line ignoring number of space between elements
// -----------------------------------------------------------------------------
//
TBool CUpnpUdpServer::CompareLinesL(const TDesC8& aValue, const TDesC8& aPattern)
    {
    TBool result = EFalse;
    TPtrC8 value(aValue);
    TPtrC8 pattern(aPattern);
    RPointerArray<TPtrC8> values;
    RPointerArray<TPtrC8> patterns;
    CleanupStack::PushL( TCleanupItem( CleanupArray, &values ) );
    CleanupStack::PushL( TCleanupItem( CleanupArray, &patterns ) );

    UpnpString::CutToPiecesL(value,UpnpString::KSpace()[0], values);
    UpnpString::CutToPiecesL(pattern,UpnpString::KSpace()[0], patterns);

    if (values.Count() == patterns.Count())
        {
        result = ETrue;
        for (TInt i = 0; i < values.Count(); i++)
            {
            if (patterns[i]->Compare(*(values[i])) != 0)
                {
                result = EFalse;
                break;
                }
            }
        }

    CleanupStack::PopAndDestroy( &patterns );
    CleanupStack::PopAndDestroy( &values );
    return result;
    }

// -----------------------------------------------------------------------------
// CUpnpUdpServer::RestartOrContinueSendProcessingL()
// -----------------------------------------------------------------------------
//
TBool CUpnpUdpServer::RestartOrContinueSendProcessingL()
    {
    if ( iSendRequestList.Count() > 0 )
        {
        CUpnpUdpSendRequest* request = iSendRequestList[0];
        CleanupStack::PushL( request );
        iSendRequestList.Remove( 0 );
        iSendRequestList.Compress();
        SendL( *( request->iBuffer ), request->iAddr );
        CleanupStack::PopAndDestroy( request );
        return ETrue;
        }
    return EFalse;
    }

// -----------------------------------------------------------------------------
// CUpnpUdpServer::RestartOrContinueProcessing
// -----------------------------------------------------------------------------
//
void CUpnpUdpServer::RestartOrContinueProcessingL()
    {
    if ( !RestartOrContinueSendProcessingL() )
        {
        ReceiveL();
        }
    }

// -----------------------------------------------------------------------------
// CUpnpUdpServer::MessageExpiredL
// Function gets called when message's timer expires. This means that this
// message should be sent.
// -----------------------------------------------------------------------------
//
void CUpnpUdpServer::MessageExpiredL( CUpnpHttpMessage* aMessage )
    {
    LOGS( "SSDP *** CUpnpUdpServer::MessageExpiredL" );

    CUpnpSsdpMessage* msg = static_cast<CUpnpSsdpMessage*>( aMessage );

    // going thru array and removing current message from it.
    for ( TInt i = 0; i < iPendingTimerMessages->Count(); i++ )
        {
        CUpnpSsdpMessage* arrayMsg = iPendingTimerMessages->At( i );
        if ( arrayMsg == msg )
            {
            // Remove message from array and compress array.
            iPendingTimerMessages->Delete( i );
            iPendingTimerMessages->Compress();
            break;
            }
        }
    // Prevents WLan lost MessageHandler panic 
    // don't remove because if this function leaves msg is destroyed. 
    // Msg has CActive responsible for RunError as member so it will be destroyed and therefore
    // CActiveSheduler will cause unhandled exception which is seen as messagehandler panic
    TRAP_IGNORE( iObserver->UdpMessageReceivedL( msg ) );
    delete msg; 
    }

// -----------------------------------------------------------------------------
// CUpnpUdpServer::AddressChangedL()
// Function gets called when the IP address of server is changed
// -----------------------------------------------------------------------------
//
EXPORT_C void CUpnpUdpServer::AddressChangedL( TInetAddr& aAddr )
    {
    Close();
    iLocalInterfaceAddress = aAddr;
    RefreshLocalAddressInfo();
    OpenL();
    }

// -----------------------------------------------------------------------------
// CUpnpUdpServer::UpdateLocalAddressInfoL()
// -----------------------------------------------------------------------------
//
void CUpnpUdpServer::RefreshLocalAddressInfo()
    {
#ifdef _DEBUG
    TBuf<KAddressLength> buf;
    iLocalInterfaceAddress.Output( buf );
    LOGS1( "SSDP AddressChangedL IP: %S", &buf );
#endif //_DEBUG

    TInetAddr addr = TInetAddr( KDefaultAutoIPAddress, KDefaultMulticastPort );
    addr.SetFamily( KAfInet );

    if ( iLocalInterfaceAddress.Match( addr, KInetAddrPrefixMatchLen ) )
        {
        iAutoIP = ETrue;
        LOGS( "SSDP AddressChangedL AUTO_IP" );
        }
    else
        {
        iAutoIP = EFalse;
        LOGS( "SSDP AddressChangedL NO_AUTO_IP" );
        }
    }

// -----------------------------------------------------------------------------
// CUpnpUdpServer::CalculateMxValue()
// -----------------------------------------------------------------------------
//
TInt CUpnpUdpServer::CalculateMxValue( const TDesC8& aMxString )
    {
    TReal rand = Math::FRand( iRandomSeed );

    TLex8 lex( aMxString );
    TInt mxValue( 0 );

    TInt lexError = lex.Val( mxValue );
    if (( lexError != KErrNone ) || ( mxValue > KMXMaxValue )) // if delay in responsing more than 120 sec then it should be decreased until 120 sec
        {
        mxValue = KMXMaxValue;
        }

    // divide by two to ensure that the responses
    // come before the time expires
    TReal mx = rand * KMicrosecond * mxValue / 2;
    return mx;
    }

// -----------------------------------------------------------------------------
// CUpnpUdpServer::AddPendingTimerMessageL()
// -----------------------------------------------------------------------------
//
void CUpnpUdpServer::AddPendingTimerMessageL( CUpnpSsdpMessage* aMessage )
    {
    // only 20 messages allowed from one IP
    const TInt KMaxMessageNumberFromOneIP = 20;
    
    TInt cnt = 0;
    for ( TInt i = 0; i < iPendingTimerMessages->Count(); i++ )
        {
        CUpnpSsdpMessage* msg = iPendingTimerMessages->At( i );
        if ( msg->Sender().Address() == aMessage->Sender().Address() )
            {
            if ( ++cnt > KMaxMessageNumberFromOneIP )
                {
                delete aMessage;
                return;
                }
            }
        }

    CleanupStack::PushL( aMessage );

    // parsing value for wait timer
    TInt mxUse = CalculateMxValue(
                        aMessage->GetHeaderValue( UpnpSSDP::KHdrMx() ) );

    // set message timeout so when timout occurs, message
    // get's handled.
    aMessage->SetMessageTimeoutL( this, mxUse );

    // appending message to array
    iPendingTimerMessages->AppendL( aMessage );

    CleanupStack::Pop( aMessage );
    }

//  End of File