pnpmobileservices/pnpms/PnpPaosFilter/src/PnpPaosFilter.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Thu, 17 Dec 2009 08:40:12 +0200
changeset 0 3ce708148e4d
permissions -rw-r--r--
Revision: 200949 Kit: 200951

/*
* Copyright (c) 2005-2006 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:  PnP Paos filter implementation
*
*/


#include <e32std.h>
#include <http.h>
#include <apgcli.h>             // for RApaLsSession
#include <apacmdln.h>           // for CApaCommandLine
#include <centralrepository.h>  // link against centralrepository.lib

#include "PnpPaosFilter.h"
#include "PnpPaosLogger.h"
#include "PnpPaosXml.h"
#include "PnpPaosFilterPrivateCRKeys.h"



_LIT8( KPaosContentType, "application/vnd.paos+xml" );
_LIT8( KPnpPaosFilterName, "PnpPaosFilter" );
_LIT8( KPaosHeaderName, "PAOS" );
_LIT8( KPaosHeader, "ver=\"urn:liberty:paos:2003-08\"; urn:\"http://pnpms.nokia.com/signkey\"" );
_LIT8( KDummyResponse, "<!-- Dummy -->" );
_LIT8( KHttp, "http://" );
_LIT8( KHttps, "https://" );

CEComFilter* CPnpPaosFilter::InstantiateL( TAny* aSession )
    {
    LOGSTRING("CPnpPaosFilter::InstantiateL()");
    // Cast TAny to RHTTPSession
    RHTTPSession* pSession = static_cast<RHTTPSession*>(aSession);
    CPnpPaosFilter* filter = CPnpPaosFilter::NewL( pSession );
    LOGSTRING("CPnpPaosFilter::InstantiateL() - done");
    return filter;
    }

CPnpPaosFilter* CPnpPaosFilter::NewL( RHTTPSession* pSession )
    {
    LOGSTRING("CPnpPaosFilter::NewL()");
    CPnpPaosFilter* pNew = new (ELeave) CPnpPaosFilter( pSession);
    CleanupStack::PushL(pNew);
    pNew->ConstructL();
    CleanupStack::Pop();
    LOGSTRING("CPnpPaosFilter::NewL() - done");
    return pNew;
    }

CPnpPaosFilter::CPnpPaosFilter( RHTTPSession* pSession ) :
    iTransactionId(-1),
    iSession( pSession ),
    iMakePaosResponse( EFalse )
#ifndef __SERIES60_ 
    ,iHdcPaosPostDone( EFalse )
#endif
    {
    LOGSTRING("constructor CPnpPaosFilter()");
    }

void CPnpPaosFilter::ReadAllowedDomainsL()
    {
    LOGSTRING("CPnpPaosFilter::ReadAllowedDomainsL()");
    RLibrary library;
    const TUidType hdcuid( KNullUid, KNullUid, KHdcUtilDllUid );
    TInt result = library.Load( KHdcDllFileName, hdcuid );
    LOGSTRING2( "Hdc Library load result: %i", result );
    // If there is no HDC installed, cannot add allowed HDC domains
    if( result == KErrNone )
        {
        CleanupClosePushL( library );
        LOGSTRING("CPnpPaosFilter::ReadAllowedDomainsL() 2");
        // Function at ordinal 1 is NewLC
        TLibraryFunction entry = library.Lookup(1);
        // Call the function to create new hdc dll object
        LOGSTRING("CPnpPaosFilter::ReadAllowedDomainsL() 3");
        CHdcToPaosInterface* hdcUtil = ( CHdcToPaosInterface* ) entry();
        hdcUtil->TrustedDomainsL( iTrustedHdcDomains );
        LOGSTRING("CPnpPaosFilter::ReadAllowedDomainsL() 4");
        CleanupStack::PopAndDestroy( hdcUtil );
        CleanupStack::PopAndDestroy(); // library.Close()
        LOGSTRING("CPnpPaosFilter::ReadAllowedDomainsL() 5");
        }

    // PnP trusted domains could be handled as in HDC case, by maintaining
    // a list of trusted domains as browser bookmarks (changeable only 
    // in a Device Managament session, not by the user)
    //AllowedDomainsArray pnpDomains = pnpUtil->AllowedDomainsL();
    LOGSTRING("CPnpPaosFilter::ReadAllowedDomainsL() - done");
    }
    
void CPnpPaosFilter::ReadAllowedPnpDomainsL()
    {
    LOGSTRING("CPnpPaosFilter::ReadAllowedPnpDomainsL()");
    // Create PnP-MS home domain filter
    // Read allowed pnp domains from cenrep
    CRepository* repository = CRepository::NewLC( KCRUidPnpPaosFilter );
    
    TBuf<KMaxURLLength> buffer;
    TUint32 domainNbr;
    TrustedDomain domain;
    for (domainNbr = KPnPPaosFilterHomeDomain1; domainNbr <= KPnPPaosFilterHomeDomainLast; domainNbr++)
        {
        User::LeaveIfError( repository->Get( domainNbr, buffer ) );
        if(buffer.Length() > 0)
            {
            domain.Copy( buffer );
            iTrustedPnpDomains.Append( domain );
            }
        }
    CleanupStack::PopAndDestroy( repository );
    LOGSTRING("CPnpPaosFilter::ReadAllowedPnpDomainsL() - done");
    }

void CPnpPaosFilter::ConstructL()
    {
    LOGSTRING("CPnpPaosFilter::ConstructL()");

    iSessionStringPool = iSession->StringPool();
    iFilterName = iSessionStringPool.OpenFStringL( KPnpPaosFilterName );
    TInt err( KErrNone );
    TRAP( err, iPnpPaosXml = CPnpPaosXml::NewL() );
    LOGSTRING2( "iPnpPaosXml err: %i", err );

    TRAP( err, ReadAllowedDomainsL() );
    if( err != KErrNone )
        {
        LOGSTRING2( "Could not read allowed domains for HelpDeskConnect PAOS: %i", err );
        }
        
    TRAP( err, ReadAllowedPnpDomainsL() );
    if( err != KErrNone )
        {
        LOGSTRING2( "Could not read allowed domains for PnP PAOS: %i", err );
        }


    LOGSTRING("CPnpPaosFilter::ConstructL() add filters");

    // Add the filter to the queue and register for selected incoming and
    // outgoing events.
    iSession->FilterCollection().AddFilterL(
        *this,
        THTTPEvent::ESubmit,              // Transaction event
        RStringF(),                       // Any header field
        KAnyStatusCode,                   // HTTP status code
        MHTTPFilter::EClientFilters,      // Position in filter chain
        //MHTTPFilter::EProtocolHandler,      // Position in filter chain
        iFilterName );                    // Filter name

    iSession->FilterCollection().AddFilterL(
        *this,
        THTTPEvent::EGotResponseHeaders,
        RStringF(),
        200,
        MHTTPFilter::EClientFilters,      // Position in filter chain
        //MHTTPFilter::EProtocolHandler,      // Position in filter chain
        iFilterName );

    iSession->FilterCollection().AddFilterL(
        *this,
        THTTPEvent::EGotResponseBodyData,
        RStringF(),
        200,
        MHTTPFilter::EClientFilters,      // Position in filter chain
        //MHTTPFilter::EProtocolHandler,      // Position in filter chain
        iFilterName );

    iSession->FilterCollection().AddFilterL(
        *this,
        THTTPEvent::EResponseComplete,
        RStringF(),
        200,
        MHTTPFilter::EClientFilters,      // Position in filter chain
        //MHTTPFilter::EProtocolHandler,      // Position in filter chain
        iFilterName );

    LOGSTRING("CPnpPaosFilter::ConstructL() - done");
    }

CPnpPaosFilter::~CPnpPaosFilter()
    {
    LOGSTRING("CPnpPaosFilter::~CPnpPaosFilter()");

    if( iLoadCount )
        {
        // As we're already in a destructor, MHFUnload must not delete us again
        iLoadCount = -1;
        if (iSession)
            {
            // Removes all registrations of this filter:
            iSession->FilterCollection().RemoveFilter( iFilterName );
            }
        }
    iFilterName.Close();
    iTrustedPnpDomains.Close();

    delete iPaosPostUrl;
    delete iPnpPaosXml;

    // RArray must be closed before destructing
    iTrustedHdcDomains.Close();

    // we do not own iSessionStringPool, we do not close it.
    // we do not own iSession

    LOGSTRING("CPnpPaosFilter::~CPnpPaosFilter() - done");
    }

void CPnpPaosFilter::HostFromUriL( CUri8* aUri )
    {
    LOGSTRING( "Original URI:" );
    LOGTEXT( aUri->Uri().UriDes() );

    /** The userinfo component specifier */
    aUri->RemoveComponentL( EUriUserinfo );
    LOGTEXT( aUri->Uri().UriDes() );
    /** The path component specifier */
    aUri->RemoveComponentL( EUriPath );
    LOGTEXT( aUri->Uri().UriDes() );
    /** The query component specifier */
    aUri->RemoveComponentL( EUriQuery );
    LOGTEXT( aUri->Uri().UriDes() );
    /** The fragment component specifier */
    aUri->RemoveComponentL( EUriFragment );
    LOGTEXT( aUri->Uri().UriDes() );

    // Only EUriHost (The host component specifier) and
    // EUriPort (The port component specifier) are left
    }

void CPnpPaosFilter::MHFRunL( RHTTPTransaction aTransaction,
                             const THTTPEvent& aEvent)
    {
    LOGSTRING3( "CPnpPaosFilter::MHFRunL( TxnId: %d, event: %d )", aTransaction.Id(), aEvent.iStatus );
    switch( aEvent.iStatus )
        {
        case THTTPEvent::ESubmit:
            {
            LOGSTRING( "CPnpPaosFilter::MHFRunL:ESubmit");

            // Generate as little overhead as possible:

            // If this is the case maybe PAOS requests should be allowed only from certain URLs...
            // Now only nokia.com or help-portal.com are allowed URLs
            CUri8* uri = CUri8::NewLC( aTransaction.Request().URI() );
            HostFromUriL( uri );
            TPtrC8 host = uri->Uri().UriDes();

            // Check if a PAOS query is allowed from the domain
            
            if( IsPaosHomeDomainL( host ) )
                {
                LOGSTRING("PnP Mobile Services Allows PAOS-requests from the domain");
                RHTTPHeaders headers = aTransaction.Request().GetHeaderCollection();
                AddPaosHeadersL( headers );
                // From HTTP stack documentation:
                // It's important to note that a filter object is per-session, and so might be shared by several 
                // transactions. This means that if you have per-transaction state that you need to store, it 
                // must be stored in the transaction's property set. DO NOT STORE ANY PER-TRANSACTION INFORMATION 
                // IN THE FILTER OBJECT. 

                iMakePaosResponse = ETrue;
                }
            else
                {		    				
                if( host.Size() <= 255) 	   
                    { 
                    if( iTrustedHdcDomains.Find( host ) != KErrNotFound )
                        {
                        LOGSTRING("HelpDeskConnect Allows PAOS-requests from the domain");
                        RHTTPHeaders headers = aTransaction.Request().GetHeaderCollection();
                        AddPaosHeadersL( headers );
                        iMakePaosResponse = ETrue;
                        }
                    else
                        {
                        iMakePaosResponse = EFalse;
                        }
                    }
                } 
            CleanupStack::PopAndDestroy( uri );
            break;
            }
        case THTTPEvent::EGotResponseHeaders:
            {
            LOGSTRING( "CPnpPaosFilter::MHFRunL:EGotResponseHeaders" );

            if( !iMakePaosResponse ) break;

            RHTTPHeaders headers = aTransaction.Response().GetHeaderCollection();
            if( IsPaosContentTypeL( headers ) )
                {
                iTransactionId = aTransaction.Id();

                delete iPaosPostUrl;
                iPaosPostUrl = 0;
                iPaosPostUrl = CUri8::NewL( aTransaction.Request().URI() );
                HostFromUriL( iPaosPostUrl );
                }
            else
                {
                iMakePaosResponse = EFalse;
                }
            break;
            }
        case THTTPEvent::EGotResponseBodyData:
            {
            LOGSTRING( "CPnpPaosFilter::MHFRunL:EGotResponseBodyData" );

            // Check if we are allowed to make a PAOS response
            if( iMakePaosResponse && aTransaction.Id() == iTransactionId )
                {
                if( !iPnpPaosXml ) 
                    {
                    User::Leave( KErrGeneral );
                    }
                TBool lastPart( EFalse );
                // Received another body data chunk
                MHTTPDataSupplier* pBody = aTransaction.Response().Body();
                if( pBody )
                    {
                    lastPart = iPnpPaosXml->CollectResponseBodyL( *pBody );
                    }

                // check if more data is expected
                if( lastPart )
                    {
                    PostPaosResponseL( aTransaction );
                    }
                }
#ifndef __SERIES60_ 
            // check if expecting a HDC trigger file
            else if( iHdcPaosPostDone )
                {
                // Received another body data chunk
                MHTTPDataSupplier* pBody = aTransaction.Response().Body();
                if( pBody )
                    {
                    iPnpPaosXml->CollectResponseBodyL( *pBody );
                    }
                }
#endif
            break;
            }
        case THTTPEvent::EResponseComplete:
            {
            LOGSTRING( "CPnpPaosFilter::MHFRunL:EResponseComplete" );

#ifndef __SERIES60_ 
            // if( contenttype == trigger )
            if( iHdcPaosPostDone )
                {
                HandleHdcTriggerL();
                aTransaction.Cancel( THTTPFilterHandle::ECurrentFilter );
                }
#endif
            break;
            }
        default:
            {
            // We ignore other events. We shouldn't receive other events though.
            break;
            }
        }
    LOGSTRING( "CPnpPaosFilter::MHFRunL - done" );
    }


void CPnpPaosFilter::PostPaosResponseL( RHTTPTransaction& aTransaction )
    {
    // For some mysterious reason the http transaction has to be cancelled here (EGotResponseBodyData),
    // otherwise browser will have the PAOS request concatenated with the resulting
    // html page (or at least the resulting html page shows an extra text "setOfKeys"
    // so it seems to concatenate at least part of the PAOS request)

    // Since transaction is cancelled in EGotResponseBodyData event, there is no
    // point in listening for EResponseComplete event anymore

    CPnpPaosXml::TPaosStates status( CPnpPaosXml::EPaosStatusUnknown );

    TRAPD( err, iPnpPaosXml->ParseL( status ) );
    if( err == KErrNone )
        {
        iMakePaosResponse = EFalse;
        iTransactionId = -1;
        LOGSTRING( "PAOS post URL:" );
        /*
        "responseConsumerURL attribute, with a URL as its value.This URL SHOULD be relative to the URL that
        was requested by the user agent (in the HTTP request that resulted in the creation of the SOAP message). If the
        URL nevertheless is absolute it MUST have http or https as the protocol and SHOULD have a domain that is
        owned by the same party as the owner of the domain in the URL of the HTTP request."
        [Liberty Reverse HTTP binding for SOAP Specification]
        */
        const TDesC8& paosPostUrl = iPnpPaosXml->GetPaosPostUrlL();
        if( paosPostUrl.Find( KHttp ) != KErrNotFound || paosPostUrl.Find( KHttps ) != KErrNotFound )
            {
            LOGSTRING( "Absolute URL" );
            delete iPaosPostUrl;
            iPaosPostUrl = 0;
            TUriParser8 uriParser;
            User::LeaveIfError( uriParser.Parse( paosPostUrl ) );
            iPaosPostUrl = CUri8::NewL( uriParser );
            }
        else
            {
            LOGSTRING( "Relative URL" );
            iPaosPostUrl->SetComponentL( paosPostUrl, EUriPath );
            }
        LOGTEXT( iPaosPostUrl->Uri().UriDes() );

        switch( status )
            {
        case CPnpPaosXml::EPaosStatusUnknown:
            LOGSTRING( "EPaosStatusUnknown" );
            // Do nothing, was: User::Leave( KErrArgument );
            break;
        case CPnpPaosXml::EPaosStatusRequestingPnPKeys:
            {
            LOGSTRING( "EPaosStatusRequestingPnPKeys" );
            CUri8* uri = CUri8::NewLC( aTransaction.Request().URI() );
            HostFromUriL( uri );
            // Also remove scheme
            uri->RemoveComponentL( EUriScheme );
            LOGTEXT( uri->Uri().UriDes() );

            TPtrC8 host = uri->Uri().UriDes();
            // check the domain was an allowed domain of PnP-MS home domain filter
            if( IsPaosHomeDomainL( host ) )
                {
                PaosPostL( aTransaction );
                }
            CleanupStack::PopAndDestroy( uri );
            break;
            }
        case CPnpPaosXml::EPaosStatusRequestingHdcKeys:
            {
            LOGSTRING( "EPaosStatusRequestingHdcKeys" );
            CUri8* uri = CUri8::NewLC( aTransaction.Request().URI() );
            HostFromUriL( uri );
            TPtrC8 host = uri->Uri().UriDes();
            
            TBool isTrusted( EFalse );
            for( TInt i(0); i < iTrustedHdcDomains.Count(); i++ )
                {
                HdcTrustedDomain trustedDomain = iTrustedHdcDomains[i];
                if( host.Find( trustedDomain ) != KErrNotFound )
                    isTrusted = ETrue;
                }
            // check the domain was an allowed domain of HDC home domain filter
            if( isTrusted )
                {
                PaosPostL( aTransaction );
#ifndef __SERIES60_ 
                iHdcPaosPostDone = ETrue;
#endif
                }
#ifdef _DEBUG
            else
                {
                LOGSTRING("Not an allowed domain!");
                LOGTEXT( host );
                LOGSTRING("domains:");
                for( TInt j(0); j < iTrustedHdcDomains.Count(); j++ )
                    {
                    LOGTEXT( iTrustedHdcDomains[j] );
                    }
                }
#endif
            CleanupStack::PopAndDestroy( uri );
            break;
            }
        default:
            {
            // We ignore other events. We shouldn't receive other events though.
            break;
            }
            }
        }
    else
        {
        LOGSTRING2( "Error in ParseL: %i", err );
        }

    LOGSTRING("iPnpPaosXml->ResetPaosRequest()");
    iPnpPaosXml->ResetPaosRequest();
    }

#ifndef __SERIES60_ 
void CPnpPaosFilter::HandleHdcTriggerL()
    {
    LOGSTRING("Create file");
    iHdcPaosPostDone = EFalse;

    _LIT( KTempDocumentName, "C:\\Temp\\trigger.trg" );

    RFs rfs;
    User::LeaveIfError( rfs.Connect() );
    CleanupClosePushL( rfs );
    TInt err = rfs.MkDir( KTempDocumentName );
    // The folder may already exist, do not leave in that case
    if( err != KErrNone && err != KErrAlreadyExists )
        {
        User::Leave( err ); 
        }

    RFile file;
    User::LeaveIfError( file.Replace( rfs, KTempDocumentName, EFileWrite ) );
    CleanupClosePushL( file );
    LOGSTRING("File created");
    // A HDC PAOS post was made and we expect the response to be a HDC trigger file
    const TPtrC8 hdcTriggerData = iPnpPaosXml->ResponseBodyL();
    file.Write( hdcTriggerData );
    LOGSTRING("File written");
    iPnpPaosXml->ResetPaosRequest();

    CleanupStack::PopAndDestroy(); // rfs
    CleanupStack::PopAndDestroy(); // file

    LOGSTRING("Starting app");
    RApaLsSession appArcSession;
    User::LeaveIfError( appArcSession.Connect() );
    CleanupClosePushL( appArcSession );

    CApaCommandLine* cmdLine = CApaCommandLine::NewLC();
    TApaAppInfo info;
    const TUid KHdcUid = { 0x1020433F };
    User::LeaveIfError( appArcSession.GetAppInfo( info, KHdcUid ) );
#ifdef RD_APPS_TO_EXES
    cmdLine->SetExecutableNameL( info.iFullName );
#else
    cmdLine->SetLibraryNameL( info.iFullName );
#endif
    cmdLine->SetCommandL( EApaCommandOpen );
    cmdLine->SetDocumentNameL( KTempDocumentName );
    err = appArcSession.StartApp( *cmdLine );
    if( err != KErrNone )
        {
        LOGSTRING2( "StartApp err %i", err );
        User::Leave( err );
        }
    LOGSTRING("Cancel transaction");
    CleanupStack::PopAndDestroy( cmdLine );
    CleanupStack::PopAndDestroy(); // appArcSession
    LOGSTRING("done");
    }
#endif 


TBool CPnpPaosFilter::GetNextDataPart( TPtrC8& aDataPart )
    {
    LOGSTRING("CPnpPaosFilter::GetNextDataPart");
    aDataPart.Set( KDummyResponse );
    return ETrue;
    }

void CPnpPaosFilter::ReleaseData()
    {
    LOGSTRING("CPnpPaosFilter::ReleaseData");
    }

TInt CPnpPaosFilter::OverallDataSize()
    {
    LOGSTRING("CPnpPaosFilter::OverallDataSize");
    return KDummyResponse().Length();
    }

TInt CPnpPaosFilter::Reset()
    {
    LOGSTRING("CPnpPaosFilter::Reset");
    return KErrNone;
    }

void CPnpPaosFilter::AddPaosHeadersL( RHTTPHeaders& requestHeaders )
    {
    LOGSTRING( "CPnpPaosFilter::AddPaosHeadersL" );

    // Add PAOS header
    THTTPHdrVal paosVer;
    RStringF paosVerStr = iSessionStringPool.OpenFStringL( KPaosHeader );
    CleanupClosePushL( paosVerStr );
    paosVer.SetStrF( paosVerStr );
    RStringF paosStr = iSessionStringPool.OpenFStringL( KPaosHeaderName );
    CleanupClosePushL( paosStr );
    requestHeaders.SetFieldL( paosStr, paosVerStr );
    CleanupStack::PopAndDestroy(); // paosStr.Close()
    CleanupStack::PopAndDestroy(); // paosVerStr.Close()

    // Add PAOS content type to accept header
    THTTPHdrVal acceptHdr;
    requestHeaders.GetField(
        iSessionStringPool.StringF( HTTP::EAccept, RHTTPSession::GetTable() ), 0, acceptHdr );

    if (acceptHdr.Type() == 0x04) //Worst case scenario, if transaction does not have accept header.
    {
        RStringF valStr = iSessionStringPool.OpenFStringL(_L8("*/*"));
   		THTTPHdrVal val(valStr);
    	requestHeaders.SetFieldL(iSessionStringPool.StringF(HTTP::EAccept, RHTTPSession::GetTable() ), val);
    	valStr.Close();
   	    requestHeaders.GetField(
 	       iSessionStringPool.StringF( HTTP::EAccept, RHTTPSession::GetTable() ), 0, acceptHdr );

    }


    RStringF acceptStr = acceptHdr.StrF();
    TPtrC8 accept = acceptStr.DesC();
    if( accept.Find( KPaosContentType ) == KErrNotFound )
        {
        HBufC8* acceptBuf = HBufC8::NewLC( KPaosContentType().Length() );
        TPtr8 acceptBufPtr = acceptBuf->Des();
        acceptBufPtr.Append( KPaosContentType );
        SetHttpHeaderL( requestHeaders, HTTP::EAccept, *acceptBuf );

#ifdef LOGGING_ENABLED
        LOGSTRING( "Accept header:" );
            for( TInt i(0); i < acceptBufPtr.Length(); i += 128 )
                {
                TPtrC8 logText = acceptBufPtr.Right( acceptBufPtr.Length() - i );
                LOGTEXT( logText );
                }
#endif
        CleanupStack::PopAndDestroy( acceptBuf );
        }
#ifdef LOGGING_ENABLED
    else
        {
        LOGSTRING( "Accept header already includes PAOS:" );
        for( TInt i(0); i < accept.Length(); i += 128 )
            {
            TPtrC8 logText = accept.Right( accept.Length() - i );
            LOGTEXT( logText );
            }
        }
#endif

    LOGSTRING( "CPnpPaosFilter::AddPaosHeadersL - done" );
    }

TBool CPnpPaosFilter::IsPaosContentTypeL( RHTTPHeaders& aHeaders )
    {
    LOGSTRING( "CPnpPaosFilter::IsPaosContentTypeL" );

    // get the Content-Type string
    RStringF content = iSessionStringPool.StringF( HTTP::EContentType,
                                            RHTTPSession::GetTable() );

    THTTPHdrVal fieldVal;
    // now retrieve the Content-Type field
    if( KErrNone == aHeaders.GetField( content, 0, fieldVal ) )
        {
        TPtrC8 contentType;
        // get the field value
        if( THTTPHdrVal::KStrFVal == fieldVal.Type() )
            {
            contentType.Set( fieldVal.StrF().DesC() );
            LOGSTRING( "ContentType:" );
            LOGTEXT( contentType );
            }
        else if( THTTPHdrVal::KStrVal == fieldVal.Type() )
            {
            contentType.Set( fieldVal.Str().DesC() );
            LOGSTRING( "ContentType:" );
            LOGTEXT( contentType );
            }
        else
            {
            return EFalse;
            }

        if( contentType.Compare( KPaosContentType ) == 0 )
            {
            LOGSTRING("Content type matches");

            // 3.0 does not handle the resulting html right, it is stored by DL manager.
            // This means that it does not recognize the incoming text/html data correctly.
            // So try replacing content type field with text/html, maybe PAOS content type is
            // cached somewhere even if the transaction has been canceled.
            LOGSTRING("Resetting Content type to text/html");
            aHeaders.RemoveField( iSessionStringPool.StringF( HTTP::EContentType , RHTTPSession::GetTable() ) );
            SetHttpHeaderL( aHeaders, HTTP::EContentType, _L8("text/html") );

            return ETrue;
            }
        }
    return EFalse;
    }

void CPnpPaosFilter::PaosPostL( RHTTPTransaction& aTransaction )
    {
    LOGSTRING( "CPnpPaosFilter::PaosPostL" );

    RHTTPRequest request = aTransaction.Request();

    aTransaction.Cancel( THTTPFilterHandle::ECurrentFilter );
    RHTTPHeaders requestHeaders = request.GetHeaderCollection();
    request.RemoveBody();
    // Remove Content-Type header
    requestHeaders.RemoveField( iSessionStringPool.StringF( HTTP::EContentType, RHTTPSession::GetTable() ) );
    // Remove Content-Length header
    requestHeaders.RemoveField( iSessionStringPool.StringF( HTTP::EContentLength, RHTTPSession::GetTable() ) );
    // Remove Host header
    requestHeaders.RemoveField( iSessionStringPool.StringF( HTTP::EHost, RHTTPSession::GetTable() ) );

    // PAOS header
    THTTPHdrVal paosVer;
    RStringF paosVerStr = iSessionStringPool.OpenFStringL( KPaosHeader );
    CleanupClosePushL( paosVerStr );
    paosVer.SetStrF( paosVerStr );
    RStringF paosStr = iSessionStringPool.OpenFStringL( KPaosHeaderName );
    CleanupClosePushL( paosStr );
    requestHeaders.SetFieldL( paosStr, paosVerStr );
    CleanupStack::PopAndDestroy(); // paosStr
    CleanupStack::PopAndDestroy(); // paosVerStr

    // Content headers
    SetHttpHeaderL( requestHeaders, HTTP::EContentType, _L8("application/vnd.paos+xml") );

    request.SetMethod( iSessionStringPool.StringF( HTTP::EPOST, RHTTPSession::GetTable() ) );

    // Set the URI of the request
    request.SetURIL( iPaosPostUrl->Uri() );

    LOGSTRING( "Uri:" );
    LOGTEXT( iPaosPostUrl->Uri().UriDes() );

    // Provide Response
    if( !iPnpPaosXml )
        {
        User::Leave( KErrGeneral );
        }
    request.SetBody( *iPnpPaosXml );

    aTransaction.Cancel();
    aTransaction.SubmitL();
    LOGSTRING( "CPnpPaosFilter::PaosPostL - done" );
    }

void CPnpPaosFilter::SetHttpHeaderL( RHTTPHeaders& aMessage, const HTTP::TStrings aIndex, const TDesC8& aString )
    {
    LOGSTRING( "CPnpPaosFilter::SetHttpHeaderL" );
    
    THTTPHdrVal hdrVal;
    RStringF str = iSessionStringPool.OpenFStringL( aString );
    CleanupClosePushL( str );
    hdrVal.SetStrF( str );
    aMessage.SetFieldL( iSessionStringPool.StringF( aIndex, RHTTPSession::GetTable() ), hdrVal );
    CleanupStack::PopAndDestroy();
    
    LOGSTRING( "CPnpPaosFilter::SetHttpHeaderL - done" );
    }

TInt CPnpPaosFilter::MHFRunError( TInt aError,
                                 RHTTPTransaction aTransaction,
                                 const THTTPEvent& aEvent )
    {
    LOGSTRING( "CPnpPaosFilter::MHFRunError()" );

    LOGSTRING3( "error: %d, event: %d", aError, aEvent.iStatus );
    // If anything left, we've run out of memory or something
    // similarly catastrophic has gone wrong.
    // Remove the body to prevent other client from accessing
    // the contents.
    aTransaction.Response().RemoveBody();
    // Set the transaction to failed
    aTransaction.Fail();
    // Keep compiler happy
    (void)aError;
    (void)aEvent;
    LOGSTRING( "CPnpPaosFilter::MHFRunError() - done" );
    return KErrNone;
    }

void CPnpPaosFilter::MHFLoad(RHTTPSession /*aSession*/,
                                         THTTPFilterHandle /*aHandle*/)
    {
    LOGSTRING("CPnpPaosFilter::MHFLoad");
    iLoadCount++;
    }

void CPnpPaosFilter::MHFUnload(RHTTPSession /*aSession*/,
                                           THTTPFilterHandle /*aHandle*/)
    {
    LOGSTRING("CPnpPaosFilter::MHFUnload");
    if( --iLoadCount > 0 )
        {
        LOGSTRING("CPnpPaosFilter::MHFUnload - done");
        return;
        }
    delete this; // Delete object itself
    LOGSTRING("CPnpPaosFilter::MHFUnload - done");
    }

TBool CPnpPaosFilter::IsPaosHomeDomainL( const TPtrC8 aHost )
    {
    LOGSTRING("CPnpPaosFilter::IsPaosHomeDomainL");
    // check is the host in trusted domains list
    for (TInt i=0; i < iTrustedPnpDomains.Count(); i++)
        {
        if (aHost.Find(iTrustedPnpDomains[i]) != KErrNotFound )
            {
            return ETrue;
            }
        }
    return EFalse;
    }