upnpmediaserver/contentdirectoryservice/src/dlna/upnpdlnafilter.cpp
author samhuttu
Mon, 01 Nov 2010 12:37:49 +0200
branchnew development branch with rendering state machine and other goodies
changeset 38 5360b7ddc251
parent 0 7f85d04be362
permissions -rw-r--r--
New development branch with e.g. rendering state machine and a simple Qt example application using it.

/** @file
 * Copyright (c) 2005-2009 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:  CUpnpDlnaFilter implementation.
 *
 */
// INCLUDES
#include <bautils.h> 
#include "upnpdlnafilter.h"
#include "upnpstring.h"
#include "upnpcons.h"
#include "upnpcontentdirectorydatafinder.h"
#include "upnphttpmessage.h"
#include "upnpdlnaprotocolinfo.h"
#include "upnpfileutils.h"
#include "upnpsecuritymanager.h"
#define KLogFile _L("DLNAWebServer.txt")
#include "upnpcustomlog.h"
#include "upnphttpfilereceivetransaction.h"
#include "upnphttpdataservetransaction.h"
#include "upnpdlnafilterheaders.h"
#include "upnpcommonupnplits.h"
#include "upnpdlnacorelation.h"


// CONSTANTS
_LIT8( KDlnaFilter, "DLNA");
_LIT8( KIpPortPlaceholder8, "___.___.___.___:_____" );

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

// -----------------------------------------------------------------------------
// CUpnpDlnaFilter::NewL
// Two-phased constructor.
// -----------------------------------------------------------------------------
//
EXPORT_C CUpnpDlnaFilter* CUpnpDlnaFilter::NewL(
    MUpnpContentDirectoryDataFinder* aFinder,
    CUpnpSecurityManager* aSecurityManager )
    {
    CUpnpDlnaFilter* self =
            CUpnpDlnaFilter::NewLC( aFinder, aSecurityManager );
    CleanupStack::Pop( self );
    return self;
    }

// -----------------------------------------------------------------------------
//  CUpnpDlnaFilter::NewTransactionL
//
// -----------------------------------------------------------------------------
//
EXPORT_C void CUpnpDlnaFilter::NewTransactionL( const TDesC8& aMethod, const TDesC8& aUri, 
    const TInetAddr& aSender, CUpnpHttpServerTransaction*& aResultTrans )
    {
    if ( aMethod == KHttpPost() )
        {
        aResultTrans = CUpnpHttpFileReceiveTransaction::NewL( *this, aSender, aUri );
        }
    else
        {
        aResultTrans = CUpnpHttpDataServeTransaction::NewL( *this, aSender, aUri );
        }
    }

// -----------------------------------------------------------------------------
// CUpnpDlnaFilter::NewLC
// Two-phased constructor.
// -----------------------------------------------------------------------------
//
EXPORT_C CUpnpDlnaFilter* CUpnpDlnaFilter::NewLC(
    MUpnpContentDirectoryDataFinder* aFinder,
    CUpnpSecurityManager* aSecurityManager )
    {
    CUpnpDlnaFilter* self = new (ELeave) CUpnpDlnaFilter( aFinder,
        aSecurityManager );
    CleanupStack::PushL( self );
    self->ConstructL();
    return self;
    }

// -----------------------------------------------------------------------------
// CUpnpDlnaFilter::CUpnpDlnaFilter
// C++ default constructor.
// -----------------------------------------------------------------------------
//
CUpnpDlnaFilter::CUpnpDlnaFilter( MUpnpContentDirectoryDataFinder* aFinder,
    CUpnpSecurityManager* aSecurityManager ) :
    iCdDataFinder( aFinder ), iSecurityManager( aSecurityManager )
    {
    }

// -----------------------------------------------------------------------------
// CUpnpDlnaFilter::~CUpnpDlnaFilter
// C++ default destructor.
// -----------------------------------------------------------------------------
//
CUpnpDlnaFilter::~CUpnpDlnaFilter()
    {
    iFs.Close();
    delete iProtocolInfo;
    }

// -----------------------------------------------------------------------------
// CUpnpDlnaFilter::ConstructL
// EPOC default constructor for performing 2nd stage construction.
// -----------------------------------------------------------------------------
//
void CUpnpDlnaFilter::ConstructL()
    {
    User::LeaveIfError( iFs.Connect() );
    User::LeaveIfNull(iCdDataFinder);
    }

// -----------------------------------------------------------------------------
// CUpnpDlnaFilter::SecurityManager
// 
// -----------------------------------------------------------------------------
//
CUpnpSecurityManager* CUpnpDlnaFilter::SecurityManager()
    {
    return iSecurityManager;
    }

// -----------------------------------------------------------------------------
// CUpnpDlnaFilter::CheckImportUriL
// Checks if specified URI exists in database and returns object id for
//      given URI or KErrNotFound if URI is no registered in database.
// -----------------------------------------------------------------------------
//
TInt CUpnpDlnaFilter::CheckImportUriL( TDesC8& aImportUri )
    {
    TInt lastPosOfSlash = aImportUri.LocateReverse( '/' );
    if ( lastPosOfSlash < 0 )
        {
        return KErrGeneral;
        }
    HBufC8* path = aImportUri.AllocLC();
    TInt result = KErrNone;
    result = iCdDataFinder->CheckImportUriL( path->Des() );
    CleanupStack::PopAndDestroy( path );
    return result;
    }

// -----------------------------------------------------------------------------
// CUpnpDlnaFilter::Get3rdFieldFromCdL
// Find protocolInfo by contentUri (Not by importUri) and extract 3rd field,
//      using ContentDirectory.
// -----------------------------------------------------------------------------
//
HBufC8* CUpnpDlnaFilter::ThirdFieldFromCdL( const TDesC8& aContentUri )
    {
    HBufC8* result = NULL;
    CUpnpDlnaProtocolInfo* protocolInfo = ProtocolInfoL( aContentUri );
    CleanupStack::PushL(protocolInfo);
    if ( protocolInfo )
        {
        TPtrC8 thirdField = protocolInfo->ThirdField();
        if ( thirdField != KNullDesC8() )
            {
            result = thirdField.AllocL();
            }
        }
    CleanupStack::PopAndDestroy(protocolInfo);
    return result;
    }

// -----------------------------------------------------------------------------
// CUpnpDlnaFilter::ProtocolInfoL
// 
// -----------------------------------------------------------------------------
//
CUpnpDlnaProtocolInfo* CUpnpDlnaFilter::ProtocolInfoL( const TDesC8& aContentUri )
    {
    //  "http:/:/" prefix is added to content path to provide backward 
    // compatibility with old testing tools
    HBufC8* fullContentUri = HBufC8::NewL( UpnpHTTP::KHTTPUrl().Length()
            + KIpPortPlaceholder8().Length() + aContentUri.Length() );
    CleanupStack::PushL( fullContentUri );
    
    fullContentUri->Des().Append( UpnpHTTP::KHTTPUrl );
    fullContentUri->Des().Append( KIpPortPlaceholder8 );
    fullContentUri->Des().Append( aContentUri );
    
    CUpnpDlnaProtocolInfo* protocolInfo = NULL;
    if ( iCdDataFinder->GetProtocolInfoL( *fullContentUri, protocolInfo ) != KErrNone )
        {
        delete protocolInfo;
        protocolInfo = NULL;
        }
    CleanupStack::PopAndDestroy( fullContentUri );
    return protocolInfo;
    }

// -----------------------------------------------------------------------------
// CUpnpDlnaFilter::GetMediaFileNameL
// Gets name of file with content for given object's id.
// -----------------------------------------------------------------------------
//
void CUpnpDlnaFilter::GetMediaFileNameL( TInt aObjectId, TPtr& aFileName )
    {
    if ( aObjectId <= KErrNone )
        {
        return;
        }
    iCdDataFinder->GetTitleForUriL( aObjectId, aFileName );
    }

// -----------------------------------------------------------------------------
// CUpnpDlnaFilter::FindSharedFolderDBL
// Find a folder shared from DB (ContentDirectory).
// -----------------------------------------------------------------------------
//
TInt CUpnpDlnaFilter::FindSharedFolderDBL( const TDesC8& aUrlPath,
    const TDesC8& aFileName, HBufC8*& aSystemPath )
    {
    HBufC* unicodeSharedFolder = NULL;
    TInt result = KErrNone;
    HBufC16* urlPathUnicode = UpnpString::ToUnicodeL( aUrlPath );
    CleanupStack::PushL( urlPathUnicode );
    HBufC16* fileNameUnicode = UpnpString::ToUnicodeL( aFileName );
    CleanupStack::PushL( fileNameUnicode );

    result = iCdDataFinder->FindSharedFolderL( *urlPathUnicode,
        *fileNameUnicode, unicodeSharedFolder );
    if ( unicodeSharedFolder )
        {
        CleanupStack::PushL( unicodeSharedFolder );
        HBufC8* sharedFolder =
                UpnpString::FromUnicodeL( *unicodeSharedFolder );
        CleanupStack::PopAndDestroy( unicodeSharedFolder );
        aSystemPath = sharedFolder;
        }

    CleanupStack::PopAndDestroy( fileNameUnicode );
    CleanupStack::PopAndDestroy( urlPathUnicode );
    return result;
    }

// -----------------------------------------------------------------------------
//  CUpnpDlnaFilter::CheckDLNAPostCorrelationsL
//
// -----------------------------------------------------------------------------
//
TInt CUpnpDlnaFilter::CheckDLNAPostCorrelationsL( CUpnpHttpFileReceiveTransaction& aTransaction )
    {
    HBufC8* decodedContentURI = DecodeContentUriLC(aTransaction.SenderUri());

    TBool streamingSupport = 0;
    TBool interactiveSupport = 0;

    CUpnpDlnaProtocolInfo* protocolInfo = NULL;
    TRAPD( error, protocolInfo =
            iCdDataFinder->GetProtocolInfoByImportUriL( *decodedContentURI ) );
    CleanupStack::PopAndDestroy( decodedContentURI );
    if ( error )
        {
        if ( error == ERestrictedObject || error == ERestrictedParentObject )
            {
            return -EHttpForbidden;
            }
        else if ( error == ENoSuchObject )
            {
            return -EHttpNotFound;
            }
        else
            {
            return -EHttpBadRequest;
            }
        }
    CleanupStack::PushL( protocolInfo );
    if ( protocolInfo->FourthField().Find( KDlnaFilter ) == KErrNotFound )
        {
        aTransaction.FilterHeaders().RemoveHeaderL( UpnpDLNA::KHdrTransferMode );
        CleanupStack::PopAndDestroy( protocolInfo );
        return KErrNone;
        }
    else
        {
        streamingSupport = protocolInfo->DlnaFlag(
            UpnpDlnaProtocolInfo::TM_S_FLAG );
        interactiveSupport = protocolInfo->DlnaFlag(
            UpnpDlnaProtocolInfo::TM_I_FLAG );
        CleanupStack::PopAndDestroy( protocolInfo );
        }

    TDesC8& transferMode = aTransaction.FilterHeaders().QueryHeader(
                           UpnpDLNA::KHdrTransferMode );

    if ( transferMode.Length() > 0 )
        {
        if ( (transferMode.CompareC( UpnpDLNA::KTransferModeStreaming ) == 0
                && !streamingSupport) || (transferMode.CompareC(
            UpnpDLNA::KTransferModeInteractive ) == 0 && !interactiveSupport) )
            {
            return -EHttpNotAcceptable;
            }

        if ( transferMode.CompareC( UpnpDLNA::KTransferModeStreaming ) != 0
                && transferMode.CompareC( UpnpDLNA::KTransferModeInteractive )
                        != 0 && transferMode.CompareC(
            UpnpDLNA::KTransferModeBackground ) != 0 )
            {
            return -EHttpBadRequest;
            }
        }

    return KErrNone;
    }

// -----------------------------------------------------------------------------
//  CUpnpDlnaFilter::CheckDLNACorrelations
//
// -----------------------------------------------------------------------------
//
TInt CUpnpDlnaFilter::CheckDLNACorrelationsL( CUpnpHttpDataServeTransaction& aTransaction )
    {
    HBufC8* decodedContentURI = DecodeContentUriLC(aTransaction.SenderUri());
    delete iProtocolInfo;
    iProtocolInfo = NULL;
    iProtocolInfo = ProtocolInfoL( *decodedContentURI );
    CleanupStack::PopAndDestroy( decodedContentURI );    
    TPtrC8 fourthField( KNullDesC8 );
    if ( iProtocolInfo )
        {
        fourthField.Set( iProtocolInfo->FourthField() );
        }

    if ( fourthField.Find( KDlnaFilter ) == KErrNotFound )
        {
        aTransaction.FilterHeaders().RemoveHeaderL( UpnpDLNA::KHdrTransferMode );
        return KErrNone;
        }

    TUpnpDlnaCorelation dlnaCorelation;
    dlnaCorelation.iFourthField.Set( fourthField );

    TInt corelationError = CheckCorelationL( aTransaction, dlnaCorelation );
    if ( corelationError != KErrNone )
        {
        return corelationError;
        }
    corelationError = CheckTransferModeL( aTransaction, dlnaCorelation );
    if ( corelationError != KErrNone )
        {
        return corelationError;
        }    
    return KErrNone;
    }

// -----------------------------------------------------------------------------
// CUpnpHttpServer::CheckCorelationL
// -----------------------------------------------------------------------------
//    
TInt CUpnpDlnaFilter::CheckCorelationL(  CUpnpHttpDataServeTransaction& aTransaction,
                                         TUpnpDlnaCorelation& aDlnaCorelation )
    {
    //-------------Checking DLNA correlations, response with HTTPerror if some problem occurs

    aDlnaCorelation.iStreamingSupport = EFalse;
    aDlnaCorelation.iInteractiveSupport = EFalse;
    aDlnaCorelation.iBackgrondSupport = EFalse;
    aDlnaCorelation.iGetContentFeaturesExist = EFalse;
    aDlnaCorelation.iGetContentFeaturesIsOK = ETrue;
    
    /* We can only check for getcontentFeaturesExist if we have cdDataFinder 
     * ( protocolInfo is not null )*/
    if ( iProtocolInfo )
        {
        aDlnaCorelation.iBackgrondSupport = 1;
        aDlnaCorelation.iStreamingSupport = iProtocolInfo->DlnaFlag(
            UpnpDlnaProtocolInfo::TM_S_FLAG );
        aDlnaCorelation.iInteractiveSupport = iProtocolInfo->DlnaFlag(
            UpnpDlnaProtocolInfo::TM_I_FLAG );
        if( aTransaction.QueryRequestHeader( UpnpDLNA::KHdrGetcontentFeatures() ) != KNullDesC8() )
            {
            aDlnaCorelation.iGetContentFeaturesExist = ETrue;
            }
        if( aDlnaCorelation.iGetContentFeaturesExist )
            {
            TDesC8& cntHeaderValue = aTransaction.QueryRequestHeader(
                                 UpnpDLNA::KHdrGetcontentFeatures() );
            if ( cntHeaderValue != UpnpDLNA::KHdrGetcontentFeaturesValue() )
                {
                aDlnaCorelation.iGetContentFeaturesIsOK = EFalse;
                }
            }
        }

    // DLNA v0.75, 7.3.31.1 checking Operations Parameter    
    if (
		/* We don't support timeSeek and playSpeed at the moment, so according to 
		 * point 7.4.71.1 (ver 1.5 rev 0.96)*/
            aDlnaCorelation.iGetContentFeaturesIsOK &&
		((	(aTransaction.QueryRequestHeader( UpnpDLNA::KHdrTimeSeekRange ) != KNullDesC8()
			|| aTransaction.QueryRequestHeader( UpnpDLNA::KHdrPlaySpeed )!= KNullDesC8()) 
			&&
			// 7.4.71.2 - range takes precedence
			aTransaction.QueryRequestHeader( UpnpHTTP::KHdrRange ) == KNullDesC8()	)
		||
		// or if request mode is Streaming and
		(	aTransaction.QueryRequestHeader( UpnpDLNA::KHdrTransferMode ). CompareC(
			UpnpDLNA::KTransferModeStreaming ) == 0 
			&& 
			(
			/* if we have no protocolInfo (because for example there is 
			no cdDataFinder )*/
			!iProtocolInfo ||
			// Streaming is not supported for this content type
			iProtocolInfo -> DlnaFlag(UpnpDlnaProtocolInfo::TM_S_FLAG ) == EFalse)	)) 
		)
        {
        // we respond with 406 error code - Not Acceptable.
        return -EHttpNotAcceptable;
        }

    
    // Append contentFeatures.dlna.org
    if ( aDlnaCorelation.iGetContentFeaturesExist )
        {
        if ( aDlnaCorelation.iFourthField.Length() > 0 )
            {
            aTransaction.FilterHeaders().AddHeaderL( UpnpDLNA::KHdrContentFeatures,
                                                     aDlnaCorelation.iFourthField );
            }
        }
    return KErrNone;    
    }        
        
// -----------------------------------------------------------------------------
// CUpnpHttpServer::CheckTransferMode
// -----------------------------------------------------------------------------
//        
TInt CUpnpDlnaFilter::CheckTransferModeL( CUpnpHttpDataServeTransaction& aTransaction,
                                         TUpnpDlnaCorelation& aDlnaCorelation )
    {
     TDesC8& transferMode = aTransaction.QueryRequestHeader( UpnpDLNA::KHdrTransferMode );
    // Check if requested transfer mode is handled
    if (
    // if client requested for transfer is not empty
    (transferMode.Length() > 0 && (
    // and if client requested for transfer is different than Background, Streaming or Interactive mode, reply with 400 error
            (transferMode.CompareC( UpnpDLNA::KTransferModeStreaming ) != 0
                    && transferMode.CompareC(
                        UpnpDLNA::KTransferModeInteractive ) != 0
                    && transferMode.CompareC(
                        UpnpDLNA::KTransferModeBackground ) != 0) ||
            // If Background or Interactive mode was requested, check if request doesn;t contain forbidden headers
                    ((transferMode.CompareC(
                        UpnpDLNA::KTransferModeBackground ) == 0
                            || transferMode.CompareC(
                                UpnpDLNA::KTransferModeInteractive ) == 0)
                            && (aTransaction.QueryRequestHeader(
                                UpnpDLNA::KHdrTimeSeekRange ) != KNullDesC8()
                                    || aTransaction.QueryRequestHeader(
                                        UpnpDLNA::KHdrPlaySpeed )
                                            != KNullDesC8()
                                    || aTransaction.QueryRequestHeader(
                                        UpnpDLNA::KHdrRealTimeInfo )
                                            != KNullDesC8())))) || (
    
        aTransaction.QueryRequestHeader( UpnpHTTP::KHdrRange ) == KNullDesC8()
                && transferMode.Length() == 0 && (aTransaction.QueryRequestHeader(
            UpnpDLNA::KHdrTimeSeekRange ) != KNullDesC8()
                || aTransaction.QueryRequestHeader( UpnpDLNA::KHdrPlaySpeed )
                        != KNullDesC8() || aTransaction.QueryRequestHeader(
            UpnpDLNA::KHdrRealTimeInfo ) != KNullDesC8()))
                || !aDlnaCorelation.iGetContentFeaturesIsOK
    
        )
            {
            return -EHttpBadRequest ;
            }
    
    return AppendCorelationHeadersL( aTransaction, aDlnaCorelation, transferMode );    
    }    

// -----------------------------------------------------------------------------
// CUpnpHttpServer::AppendCorelationHeaders
// -----------------------------------------------------------------------------
//        
TInt CUpnpDlnaFilter::AppendCorelationHeadersL( CUpnpHttpDataServeTransaction& aTransaction,
                             TUpnpDlnaCorelation& aDlnaCorelation, TDesC8& aTransferMode )
    {
    if ( aTransferMode.Length() > 0 && ((aTransferMode.CompareC(
        UpnpDLNA::KTransferModeStreaming ) == 0 && !aDlnaCorelation.iStreamingSupport)
            || (aTransferMode.CompareC( UpnpDLNA::KTransferModeInteractive )
                    == 0 && !aDlnaCorelation.iInteractiveSupport)) )
        {
        return -EHttpNotAcceptable ;
        }
    else if ( aTransferMode.Length() <= 0 )
        {
        if ( aDlnaCorelation.iStreamingSupport )
            {
            aTransaction.FilterHeaders().AddHeaderL( UpnpDLNA::KHdrTransferMode,
                    UpnpDLNA::KTransferModeStreaming );
            }
        else if ( aDlnaCorelation.iInteractiveSupport )
            {
            aTransaction.FilterHeaders().AddHeaderL( UpnpDLNA::KHdrTransferMode,
                UpnpDLNA::KTransferModeInteractive );
            }
        else if ( aDlnaCorelation.iBackgrondSupport )
            {
            aTransaction.FilterHeaders().AddHeaderL( UpnpDLNA::KHdrTransferMode,
                UpnpDLNA::KTransferModeBackground );
            }
        }
     return KErrNone;   
    }

// -----------------------------------------------------------------------------
// CUpnpHttpSession::FormatPathL
//
// -----------------------------------------------------------------------------
//
void CUpnpDlnaFilter::FormatPathL( CUpnpHttpDataServeTransaction *aTransaction, TDes &aPath )
    {
    LOGS( "%i, CUpnpHttpSession::FormatPathL " );

    TPtrC8 contentURI = aTransaction->SenderUri();

    HBufC8* decodedContentURI = HBufC8::NewL( contentURI.Length() );
    TPtr8 ptrDecodedContentURI = decodedContentURI->Des();
    ptrDecodedContentURI.Copy( contentURI );
    UpnpString::ReplaceHttpCharacters( ptrDecodedContentURI );
    CleanupStack::PushL( decodedContentURI );

    //extracting URLpath (so removing IP, port )
    TPtrC8 fileName;
    TInt parseError( KErrNone );
    TPtrC8 urlPath = UpnpFileUtil::ExtractUrlPath( ptrDecodedContentURI,
        fileName, parseError );
    if ( parseError )
        {
        User::Leave( -EHttpBadRequest );
        }

    HBufC8* sharedFolder = NULL;
    TInt error = FindSharedFolderDBL( urlPath, fileName, sharedFolder );
    CleanupStack::PushL( sharedFolder );            
    if ( error == KErrNotFound || !sharedFolder )
        {
        User::Leave( -EHttpNotFound );
        }
    
    //sharedFolder includes file name  
    ASSERT( (*sharedFolder).Mid( (*sharedFolder).Length()
            - UpnpString::KDoubleBackSlash().Length() )
            != UpnpString::KDoubleBackSlash() );

    HBufC* path16 = UpnpString::ToUnicodeL( *sharedFolder );
    aPath.Copy( *path16 );
    delete path16;
    
    CleanupStack::PopAndDestroy( sharedFolder );
    CleanupStack::PopAndDestroy( decodedContentURI );
    }


// -----------------------------------------------------------------------------
// CUpnpDlnaFilter::GetContentTypeL()
// Retrieves a mime type from the third field of the protocol info read from CD
// -----------------------------------------------------------------------------
//
TInt CUpnpDlnaFilter::GetContentTypeL( CUpnpHttpDataServeTransaction &aTransaction,
    HBufC8*& aMime, const TDesC16& aFilename )
    {
    TInt error = KErrNone;

    TParse parse;
    parse.Set( aFilename, NULL, NULL );
    TBufC16<KMaxName> ext( parse.Ext() );
    //XML mime type has to be set seperately
    if ( ext.FindC( KXml16 ) == 0 && ext.Length() == KXml16().Length() )
        {
        // Extension says that's XML but we check content to be sure and get encoding
        _LIT8( KXmlUtf8, "text/xml; charset=\"utf-8\"" );
        aMime = HBufC8::NewL( KXmlUtf8().Length() );
        aMime->Des().Zero();
        aMime->Des().Append( KXmlUtf8() );
        return error;
        }

    //decoding content URI
    HBufC8* decodedContentURI = DecodeContentUriLC( aTransaction.SenderUri());

    //getting 3rd field
    aMime = ThirdFieldFromCdL( *decodedContentURI );
    CleanupStack::PopAndDestroy( decodedContentURI );
    // asterick as mime type is not good - so error is KErrNotFound
    _LIT8( KAseriskType, "*" );
    if ( (NULL == aMime) || ((*aMime) == KAseriskType()) )
        {
        delete aMime;
        aMime = NULL;
        error = KErrNotFound;
        }
    return error;
    }

// -----------------------------------------------------------------------------
// CUpnpDlnaFilter::DetermineDownloadPathL
//
// -----------------------------------------------------------------------------
//
HBufC* CUpnpDlnaFilter::DetermineDownloadPathL(
    CUpnpHttpFileReceiveTransaction& aTransaction )
    {
    TPtrC8 path = aTransaction.SenderUri();
    TInt parseError( KErrNone );
    TPtrC8 urlPath = UpnpFileUtil::ExtractUrlPath( path, parseError );
    if ( parseError )
        {
        User::Leave( EHttpBadRequest );
        }

    // if importURI is bad then doesn't check shared folder just return error.
    TInt result = CheckImportUriL( path );
    if ( KErrNone == result )
        {
        return KNullDesC().AllocL();
        }
    if ( result < KErrNone && result != KErrGeneral )
        {
        return NULL;
        }
    HBufC* fileName = HBufC::NewLC( KMaxFileName );
    TPtr fileNamePtr( fileName->Des() );
    GetMediaFileNameL( result, fileNamePtr );
    
    HBufC8* sharedFolder = NULL;
    FindSharedFolderDBL( urlPath, KNullDesC8, sharedFolder );
    CleanupStack::PushL( sharedFolder );
    
    HBufC* folder = UpnpString::ToUnicodeL( *sharedFolder );
    CleanupStack::PushL( folder );

    if ( fileName->Length() == 0)
       {
       _LIT(KNoDcTitle, "no_dc_title");
       fileName->Des().Copy(KNoDcTitle);
       }
    
    HBufC* resultFileName = HBufC::NewL( folder->Length() + fileName->Length() );   
    resultFileName->Des().Zero();
    resultFileName->Des().Append( *folder );
    resultFileName->Des().Append( *fileName );
    
    CleanupStack::PopAndDestroy( folder );
    CleanupStack::PopAndDestroy( sharedFolder );
    CleanupStack::PopAndDestroy( fileName );
    
    CleanupStack::PushL( resultFileName );        
    HBufC* uniqueFileName = MakeFileNameUniqueL(*resultFileName, iFs );
    CleanupStack::PopAndDestroy( resultFileName );
        
    return uniqueFileName;
    }
        
// -----------------------------------------------------------------------------
// CUpnpDlnaFilter::PrepareHeaderL
//
// -----------------------------------------------------------------------------
//
TInt CUpnpDlnaFilter::PrepareHeaderL( CUpnpHttpDataServeTransaction& aTransaction )
    {    
    HBufC8* mimetype = NULL;
    HBufC16* fileName = aTransaction.PathWithNewMethodL();
    CleanupStack::PushL( fileName );
    
    if ( GetContentTypeL( aTransaction, mimetype, *fileName ) != KErrNone )
        {
        //getting mime type from Symbian or by the extension
        mimetype = UpnpFileUtil::GetMimeTypeForFileL( *fileName );
        }
    CleanupStack::PushL( mimetype );
    aTransaction.AddResponseHeaderL( UpnpGENA::KContentType(), *mimetype );
    CleanupStack::PopAndDestroy( mimetype );
    
    // Checks if all DLNA correlations are ok and adds proper headers
    // only for GET response

    TInt dlnaCorrelationsError = CheckDLNACorrelationsL( aTransaction );
    if ( dlnaCorrelationsError < KErrNone )
        {
        CleanupStack::PopAndDestroy( fileName );
        return dlnaCorrelationsError;
        }

    
    AddHeaderIfNotEmptyL( UpnpDLNA::KHdrContentFeatures(), aTransaction );
    
        // 7.4.42.2 HTTP Server Endpoints that transfer Non-Cacheable Content using ?HTTP/1.0, and?GET responses.
        // These devices must prevent intermediate caching by including among the HTTP response headers
        // the directive:?Pragma:  no-cache
    aTransaction.AddResponseHeaderL( UpnpHTTP::KHdrPragma(),
                                     UpnpHTTP::KNoCache() );   
    aTransaction.AddResponseHeaderL( UpnpHTTP::KHdrCacheControl(),
                                     UpnpHTTP::KNoCache() );        


    // If Accept-Language header is present
    if ( aTransaction.QueryRequestHeader( UpnpHTTP::KHdrAcceptLanguage() ).Length() > 0 )
        {
        aTransaction.AddResponseHeaderL( UpnpHTTP::KHdrContentLanguage(),
                                         UpnpHTTP::KLanguageEn() );        
        }

    // Transfer Mode
    // If Transfer Mode header is present
    AddHeaderIfNotEmptyL( UpnpDLNA::KHdrTransferMode, aTransaction );

    CleanupStack::PopAndDestroy( fileName );
    return KErrNone;
    }
    
// -----------------------------------------------------------------------------
//  CUpnpDlnaFilter::AddHeaderIfNotEmptyL
//
// -----------------------------------------------------------------------------
//
void CUpnpDlnaFilter::AddHeaderIfNotEmptyL( const TDesC8& aHeaderName, 
    CUpnpHttpDataServeTransaction& aTransaction )
    {
    if ( aTransaction.FilterHeaders().QueryHeader( aHeaderName ).Length() > 0 )
        {
        aTransaction.AddResponseHeaderL( aHeaderName,
            aTransaction.FilterHeaders().QueryHeader( aHeaderName ) );            
        }
    }

// -----------------------------------------------------------------------------
//  CUpnpDlnaFilter::AuthorizeRequestL
//
// -----------------------------------------------------------------------------
//
TInt CUpnpDlnaFilter::AuthorizeRequestL(
    const TDesC& aFileName, const TInetAddr& aSender )
    {
    TInt result( KErrNone );
    CUpnpHttpMessage* tempMessage = CUpnpHttpMessage::NewL( aSender );
    CleanupStack::PushL( tempMessage );
    if ( SecurityManager()
            && SecurityManager()->AuthorizeMessage( tempMessage,
                    (TFileName&) aFileName  ) != KErrNone )
        { //not showing why resource is refused
        result = -EHttpNotFound;
        }
    CleanupStack::PopAndDestroy( tempMessage );
    return result;
    }

// -----------------------------------------------------------------------------
// CUpnpDlnaFilter::FileSession
// -----------------------------------------------------------------------------
//
RFs& CUpnpDlnaFilter::FileSession()
    {
    return iFs;
    }

// -----------------------------------------------------------------------------
// CUpnpDlnaFilter::PreparePostfixToMakeFileUniqueL
// -----------------------------------------------------------------------------
//
HBufC* CUpnpDlnaFilter::PreparePostfixToMakeFileUniqueL( const TDesC& aFilename, RFs& aFs )
    {
    _LIT( KUnderScore, "_" );
    const TInt KDotLength = 1;  // when aFileName is without extension

    HBufC16* newFile = HBufC16::NewLC( aFilename.Length() + KUnderScore().Length() 
                                       + UpnpString::KMaxTUintLength + KDotLength );

    TParse parse;
    parse.Set( aFilename, NULL, NULL );
    TPtrC fileName = parse.Name();
    TPtrC fileExt = parse.Ext();
    TPtrC filePath = parse.DriveAndPath();

    TUint64 postfixNumber = 1;

    do
        {
        TBuf<UpnpString::KMaxTUintLength + 1> buf;
        buf.AppendNum( postfixNumber++ );
        if ( buf.Length() > UpnpString::KMaxTUintLength )
            {
            User::Leave( KErrOverflow );
            }
        newFile->Des().Copy( filePath );
        newFile->Des().Append( fileName );
        newFile->Des().Append( KUnderScore() );
        newFile->Des().Append( buf );
        newFile->Des().Append( fileExt );
        }
    while ( BaflUtils::FileExists( aFs, newFile->Des() ) );

    CleanupStack::Pop( newFile );
    return newFile;
    }

// -----------------------------------------------------------------------------
// CUpnpDlnaFilter::PrepareBaseFileNameL
// -----------------------------------------------------------------------------
//
HBufC* CUpnpDlnaFilter::PrepareBaseFileNameL( const TDesC& aFilename, RFs& aFs )
    {
    HBufC* fileToServe;
    if ( aFilename.LocateReverse( '\\' ) == aFilename.Length() - 1 )
        {
        _LIT( KNoName0, "noName_0" );
        const TInt KZeroPostfixLen = 2;  //_0
        
        fileToServe = HBufC::NewL( aFilename.Length() + KNoName0().Length() );
        fileToServe->Des().Copy( aFilename );
        fileToServe->Des().Append( KNoName0() );
        
        if ( BaflUtils::FileExists( aFs, *fileToServe ) )
            {
            fileToServe->Des().Delete( fileToServe->Length() - KZeroPostfixLen, KZeroPostfixLen );
            }
        }
    else
        {
        fileToServe = HBufC::NewL( aFilename.Length() );
        fileToServe->Des().Copy( aFilename );
        }

    if ( BaflUtils::FileExists( aFs, *fileToServe ) )
        {
        CleanupStack::PushL( fileToServe );
        HBufC* newFileName = PreparePostfixToMakeFileUniqueL( *fileToServe, aFs );
        CleanupStack::PopAndDestroy( fileToServe );
        fileToServe = newFileName;
        }
    return fileToServe;
    }
    
// -----------------------------------------------------------------------------
// CUpnpDlnaFilter::MakeFileNameUniqueL
// -----------------------------------------------------------------------------
//
HBufC* CUpnpDlnaFilter::MakeFileNameUniqueL( const TDesC& aFilename, RFs& aFs )
    {
    HBufC* fileToServe = PrepareBaseFileNameL( aFilename, aFs );
    
    TPtrC questionmark;
    questionmark.Set( *fileToServe );
    TInt lastSlash = questionmark.LocateReverse( '\\' );
    TInt lastQuestionMark = questionmark.LocateReverse( '?' );
    if ( lastQuestionMark != KErrNotFound && lastSlash < lastQuestionMark )
        {
        CleanupStack::PushL( fileToServe );
        questionmark.Set( questionmark.Left( lastQuestionMark ) );

        // now setting new name for file        
        HBufC* newFileName; //necessary not to loose current fileToServe
        newFileName = HBufC::NewL( questionmark.Length() );
        newFileName->Des().Copy( questionmark  );
        CleanupStack::PopAndDestroy( fileToServe );
        fileToServe = newFileName;
        }
    
    TPtrC path;
    path.Set( *fileToServe );
    TInt lastPosOfSlash = 0;
    TInt posOfBackSlash = path.Find( KDoubleBackSlash );
    
    while ( posOfBackSlash != KErrNotFound )
        {
        path.Set( path.Mid( posOfBackSlash + 1 ) );
        lastPosOfSlash = lastPosOfSlash + posOfBackSlash + 1;
        posOfBackSlash = path.Find( KDoubleBackSlash );
        }
    
    if ( lastPosOfSlash > 0 )
        {
        path.Set( *fileToServe );
        path.Set( path.Left( lastPosOfSlash ) );
        }
        
    return fileToServe;
    }

// -----------------------------------------------------------------------------
// CUpnpDlnaFilter::DecodeContentUriLC
// -----------------------------------------------------------------------------
//
HBufC8* CUpnpDlnaFilter::DecodeContentUriLC( const TPtrC8& contentURI)
	{
	HBufC8* decodedContentURI = HBufC8::NewL( contentURI.Length() );
	TPtr8 ptrDecodedContentURI = decodedContentURI->Des();
	ptrDecodedContentURI.Copy( contentURI );
	UpnpString::ReplaceHttpCharacters( ptrDecodedContentURI );
	CleanupStack::PushL( decodedContentURI );
	return decodedContentURI;
	
	}
//  End of File