/*
* Copyright (c) 2002 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of the License "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: 
*      Implementation of class CodUtil.   
*      
*
*/


// INCLUDE FILES

#include "CodUtil.h"
#include "CodLogger.h"
#include "CodPanic.h"
#include <uri8.h>
#include <utf.h>
#include <CharConv.h>
#include <f32file.h>
#include <commdb.h>
#include <ApSelect.h>
#include <BodyPart.h>
#include <AiwGenericParam.h>
#include <caf/caf.h>

// ================= CONSTANTS =======================

/// Conversion buffer size.
LOCAL_D const TInt KCodConversionBufferSize = 256;
/// Blank IP address.
_LIT( KCodBlankIpAdress, "0.0.0.0" );
/// Zero width non-breaking space character.
LOCAL_D const TUint16 KZeroWidthNbsp = 0xfeff;
/// cid: scheme.
_LIT8( KCodCidScheme, "cid" );

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

// ----------------------------------------------------------
// CodUtil::CanOpenL()
// ----------------------------------------------------------
//
EXPORT_C TBool CodUtil::CanOpenL( const TDesC& aFname )
    {
    CLOG(( ECodEng, 4, _L("-> CodUtil::CanOpenL <%S>"), &aFname ));
    __ASSERT_DEBUG( aFname.Length(), CodPanic( ECodInternal ) );
    TBool canOpen( EFalse );
    using namespace ContentAccess;
    CVirtualPath* vPath = CVirtualPath::NewL( aFname, KDefaultContentObject );
    CleanupStack::PushL( vPath );
    TInt err;
    CData* data = NULL;
    // 'No right' style errors leave, need to TRAP.
    TRAP( err, data = CData::NewL( *vPath, EPeek, EContentShareReadOnly ) );
    // Note, 'data' not pushed. No leaving calls below.
    if ( !err &&
                 (
                 !data->EvaluateIntent( EPlay ) ||
                 !data->EvaluateIntent( EView ) ||
                 !data->EvaluateIntent( EExecute )
                 )
       )
        {
        canOpen = ETrue;
        }
    delete data;
    CleanupStack::PopAndDestroy( vPath );
    CLOG(( ECodEng, 4, _L("<- CodUtil::CanOpenL (%d)"), canOpen ));
    return canOpen;
    }

// ----------------------------------------------------------
// CodUtil::CanOpenL()
// ----------------------------------------------------------
//
EXPORT_C TBool CodUtil::CanOpenL( RFile& aFile )
    {
    CLOG(( ECodEng, 4, _L("-> CodUtil::CanOpenL (RFile)") ));
    TBool canOpen( EFalse );
    using namespace ContentAccess;
    TInt err;
    CData* data = NULL;
    // 'No right' style errors leave, need to TRAP.
    TRAP( err, data = CData::NewL( aFile, KDefaultContentObject, EPeek ) );
    // Note, 'data' not pushed. No leaving calls below.
    if ( !err &&
                 (
                 !data->EvaluateIntent( EPlay ) ||
                 !data->EvaluateIntent( EView ) ||
                 !data->EvaluateIntent( EExecute )
                 )
       )
        {
        canOpen = ETrue;
        }
    delete data;
    CLOG(( ECodEng, 4, _L("<- CodUtil::CanOpenL (%d)"), canOpen ));
    return canOpen;
    }

// ---------------------------------------------------------
// CodUtil::GetIntParam()
// ---------------------------------------------------------
//
EXPORT_C void CodUtil::GetIntParam
        (
        TInt& aParam,
        TGenericParamId aSemanticId,
        const CAiwGenericParamList& aParams
        )
    {
    TInt index = 0;
    const TAiwGenericParam* param =
        aParams.FindFirst( index, aSemanticId, EVariantTypeTInt32 );
    if ( param )
        {
        aParam = param->Value().AsTInt32();
        CLOG(( ECodEng, 4, _L("CodUtil::GetIntParam id(%d) value(%d)"), \
            aSemanticId, aParam ));
        }
    }

// ---------------------------------------------------------
// CodUtil::GetUint32Param()
// ---------------------------------------------------------
//
EXPORT_C void CodUtil::GetUint32Param
        (
        TUint32& aParam,
        TGenericParamId aSemanticId,
        const CAiwGenericParamList& aParams
        )
    {
    TInt index = 0;
    const TAiwGenericParam* param =
        aParams.FindFirst( index, aSemanticId, EVariantTypeTInt32 );
    if ( param )
        {
        aParam = STATIC_CAST( TUint32, param->Value().AsTInt32() );
        CLOG(( ECodEng, 4, _L("CodUtil::GetUint32Param id(%d) value(%d)"), \
            aSemanticId, aParam ));
        }
    }

// ---------------------------------------------------------
// CodUtil::GetBoolParam()
// ---------------------------------------------------------
//
EXPORT_C void CodUtil::GetBoolParam
        (
        TBool& aParam,
        TGenericParamId aSemanticId,
        const CAiwGenericParamList& aParams
        )
    {
    TInt index = 0;
    const TAiwGenericParam* param =
        aParams.FindFirst( index, aSemanticId, EVariantTypeTInt32 );
    if ( param )
        {
        aParam = STATIC_CAST( TBool, param->Value().AsTInt32() );
        CLOG(( ECodEng, 4, _L("CodUtil::GetBoolParam id(%d) value(%d)"), \
            aSemanticId, aParam ));
        }
    }

// ---------------------------------------------------------
// CodUtil::GetDesParamLC()
// ---------------------------------------------------------
//
EXPORT_C void CodUtil::GetDesParamLC
        (
        TPtrC8& aParam,
        TGenericParamId aSemanticId,
        const CAiwGenericParamList& aParams
        )
    {
    TInt index = 0;
    HBufC8* buf = NULL;
    // First check 8 bit variant.
    const TAiwGenericParam* param =
        aParams.FindFirst( index, aSemanticId, EVariantTypeDesC8 );
    if ( param )
        {
        aParam.Set( param->Value().AsData() );
        CLOG(( ECodEng, 4, \
            _L8("CodUtil::GetDesParamLC 8-bit id(%d) value<%S>)"), \
            aSemanticId, &aParam ));
        }
    else
        {
        // No 8-bit, check 16-bit.
        param = aParams.FindFirst( index, aSemanticId, EVariantTypeDesC );
        if ( param )
            {
            buf = ConvertL( param->Value().AsDes() );   // No leave until push.
            aParam.Set( *buf );
            CLOG(( ECodEng, 4, \
                _L8("CodUtil::GetDesParamLC 16-bit id(%d) value<%S>)"), \
                aSemanticId, &aParam ));
            }
        }
    CleanupStack::PushL( buf ); // May be NULL.
    }

// ---------------------------------------------------------
// CodUtil::GetDesParam()
// ---------------------------------------------------------
//
EXPORT_C void CodUtil::GetDesParam
        (
        TPtrC& aParam,
        TGenericParamId aSemanticId,
        const CAiwGenericParamList& aParams
        )
    {
    TInt index = 0;
    const TAiwGenericParam* param =
        aParams.FindFirst( index, aSemanticId, EVariantTypeDesC );
    if ( param )
        {
        aParam.Set( param->Value().AsDes() );
        CLOG(( ECodEng, 4, \
            _L8("CodUtil::GetDesParam 16-bit id(%d) value<%S>)"), \
            aSemanticId, &aParam ));
        }
    }

// ---------------------------------------------------------
// CodUtil::IsCidSchemeL()
// ---------------------------------------------------------
//
TBool CodUtil::IsCidSchemeL( const TDesC8& aUrl )
    {
    TUriParser8 uri;
    User::LeaveIfError( uri.Parse( aUrl ) );
    return uri.Extract( EUriScheme ).Compare( KCodCidScheme ) == 0;
    }

// ---------------------------------------------------------
// CodUtil::PartByCidL()
// ---------------------------------------------------------
//
CBodyPart* CodUtil::PartByCidL
( const RPointerArray<CBodyPart>& aParts, const TDesC8& aUrl )
    {
    CBodyPart* part = NULL;
    TUriParser8 uri;
    User::LeaveIfError( uri.Parse( aUrl ) );
    __ASSERT_DEBUG( uri.Extract( EUriScheme ).Compare( KCodCidScheme ) == 0,
        CodPanic( ECodInternal ) ); // Expecting CID scheme!
    CUri8* cUri = CUri8::NewLC( uri );
    cUri->RemoveComponentL( EUriScheme );
    TPtrC8 cid( cUri->Uri().UriDes() );
    for ( TInt i = 0; i < aParts.Count(); i++ )
        {
        if ( !aParts[i]->ContentID().Compare( cid ) )
            {
            part = aParts[i];
            break;
            }
        }
    CleanupStack::PopAndDestroy( cUri );
    return part;
    }

// ---------------------------------------------------------
// CodUtil::FileNameFromUrl()
// ---------------------------------------------------------
//
TPtrC8 CodUtil::FileNameFromUrl( const TDesC8& aUrl )
    {
    TPtrC8 ptr;
    TUriParser8 uri; 
    if ( uri.Parse( aUrl ) == KErrNone )
        {
        ptr.Set( uri.Extract( EUriPath ) );
        TInt slash = ptr.LocateReverse( TChar('/') );
        if( slash >= 0 )
            {
            // We have a slash - keep only what is after it.
            // Position 'slash + 1' can always be passed to Mid(); length is
            // one greater than any resulting index. (If last character is
            // a slash, we get an empty string as expected.)
            ptr.Set( ptr.Mid( slash + 1 ) );
            }
        }
    return ptr;
    }

// ---------------------------------------------------------
// CodUtil::AbsoluteUrlLC()
// ---------------------------------------------------------
//
EXPORT_C HBufC8* CodUtil::AbsoluteUrlLC
( const TDesC8& aBase, const TDesC8& aUrl )
    {
    HBufC8* absUrl;
    TUriParser8 url;
    User::LeaveIfError( url.Parse( aUrl ) );
    if ( url.IsPresent( EUriScheme ) )
        {
        // aUrl is already absolute, use it "as-is".
        absUrl = aUrl.AllocLC();
        }
    else
        {
        // aUrl is relative, resolve it against the aBase.
        TUriParser8 base;
        User::LeaveIfError( base.Parse( aBase ) );
        CUri8* resolved = CUri8::ResolveL( base, url );
        CleanupStack::PushL( resolved );
        absUrl = resolved->Uri().UriDes().AllocL();     // Not pushed here...
        CleanupStack::PopAndDestroy( resolved );        // (no leaving here)
        CleanupStack::PushL( absUrl );                  // ...only here.
        }
    return absUrl;
    }

// ---------------------------------------------------------
// CodUtil::ConvertToUcs2L()
// ---------------------------------------------------------
//
HBufC16* CodUtil::ConvertToUcs2L
( const TDesC8& aSrc, const TDesC8& aEncoding, RFs& aFs )
    {
    CLOG(( ECodEng | ECharset, 2, _L("-> CodUtil::ConvertToUcs2L") ));
    CCnvCharacterSetConverter* conv = CCnvCharacterSetConverter::NewLC();
    CArrayFix<CCnvCharacterSetConverter::SCharacterSet>* charsets =
        CCnvCharacterSetConverter::CreateArrayOfCharacterSetsAvailableLC( aFs );
    TUint id = 0;
    if ( aEncoding.Length() )
        {
        // Encoding is specified, use it.
        CLOG(( ECodEng | ECharset, 4, _L8("charset name given <%S>"), \
            &aEncoding ));
        id = conv->ConvertStandardNameOfCharacterSetToIdentifierL
            ( aEncoding, aFs );
        CLOG(( ECodEng | ECharset, 4, _L("charset id 0x%x"), id ));
        }
    else 
        {
        // Autodetect.
        TInt confidence = 0;
        conv->AutoDetectCharacterSetL( confidence, id, *charsets, aSrc );
        CLOG(( ECodEng | ECharset, 4, \
            _L("detected charset id 0x%x confidence 0x%x"), id, confidence ));
        if ( confidence == 0 || id == 0 )
            {
            // Failed to detect.
            User::Leave( KErrNotSupported );
            }
        }

    HBufC16* ucs2buffer = NULL;
    TBool resultOnStack = EFalse;

    // If id is 0 then we don't know what encoding the file is or it is
    // already UCS2.
    if ( id == 0 )
        {
        // If the source is too small we don't even have a unicode header or
        // have a header with no data so just leave.
        if ( aSrc.Length() < 3 )
            {
            User::Leave( KErrCorrupt );
            }
        
        const TUint8* srcData = aSrc.Ptr();
        if ( srcData[0] == 0xFF && srcData[1] == 0xFE )
            {
            // Little endian, good, just drop the unicode header and return
            // the rest.
            TPtrC result( (TUint16*)(srcData + 2) );
            ucs2buffer = result.AllocL();
            }
        else if ( srcData[0] == 0xFE && srcData[1] == 0xFF )
            {
            // Big endian, we don't support it.
            User::Leave( KErrNotSupported );
            }
        else
            {
            // No Unicode header, assume little endian-ness.
            TPtrC result( (TUint16*)srcData );
            ucs2buffer = result.AllocL();
            }
        }
    else 
        {
        HBufC16* buffer = HBufC::NewLC( KCodConversionBufferSize );
        TPtr16 ptr( buffer->Des() );
        // Prepare conversion for the given charset ID.
        conv->PrepareToConvertToOrFromL( id, *charsets, aFs );

        TInt state = 0;
        TInt remaining = conv->ConvertToUnicode( ptr, aSrc, state );
        while ( remaining >= 0 )
            {
            if ( ucs2buffer == NULL )
                {
                ucs2buffer = HBufC::NewLC( ptr.Length() );
                resultOnStack = ETrue;
                }
            else
                {
                __ASSERT_DEBUG( resultOnStack, CodPanic( ECodInternal ));
                // This may change the address of ucs2buffer so we need to put
                // it on the cleanup stack again!!
                ucs2buffer = ucs2buffer->ReAllocL
                    ( ucs2buffer->Length() + ptr.Length() );
                CleanupStack::Pop();    // old ucs2buffer
                CleanupStack::PushL( ucs2buffer );  // possibly new copy
                }
            TPtr16 ucs2ptr( ucs2buffer->Des() );
            ucs2ptr.Append( ptr );
            if ( remaining > 0 )
                {
                // Try to convert all remaining characters.
                ptr.Zero();
                remaining = conv->ConvertToUnicode
                    ( ptr, aSrc.Right( remaining ), state );
                }
            else
                {
                break;
                }
            }

        if ( resultOnStack )
            {
            CleanupStack::Pop();    // ucs2buffer
            resultOnStack = EFalse;
            }

        CleanupStack::PopAndDestroy( buffer );
        }

    if ( resultOnStack )
        {
        CleanupStack::Pop();        // ucs2buffer
        }

    CleanupStack::PopAndDestroy( 2 );   // charsets, conv

    // Check if first character is a Zero-width nbsp.
    TPtrC16 ptr( ucs2buffer->Des() );
    if ( ptr.Length() >= 1 && ptr[0] == KZeroWidthNbsp )
        {
        // First character is a Zero-width NBSP. This character is used as
        // BOM in some encodings and should not be present at this point.
        // But we are tolerant and remove it.
        // (Not expecting big-endianness here.)
        CLOG(( ECodEng | ECharset, 4, _L("dropping BOM"), id ));
        HBufC16* temp = ucs2buffer;
        CleanupStack::PushL( temp );
        ucs2buffer = ptr.Mid( 1 ).AllocL();
        CleanupStack::PopAndDestroy();  // temp
        }

    CLOG(( ECodEng | ECharset, 2, _L("<- CodUtil::ConvertToUcs2L") ));
    return ucs2buffer;
    }

// ---------------------------------------------------------
// CodUtil::ConvertUcs2ToUtf8L()
// ---------------------------------------------------------
//
HBufC8* CodUtil::ConvertUcs2ToUtf8L( const TDesC& aUcs2Src )
    {
    HBufC8* utf8buffer = NULL;
    TBool resultOnStack = EFalse;
    HBufC8* buffer = HBufC8::NewLC( KCodConversionBufferSize );
    TPtr8 ptr( buffer->Des() );
    TInt remaining =
        CnvUtfConverter::ConvertFromUnicodeToUtf8( ptr, aUcs2Src );
    while ( remaining >= 0 )
        {
        if ( utf8buffer == NULL )
            {
            utf8buffer = HBufC8::NewLC( ptr.Length() );
            resultOnStack = ETrue;
            }
        else
            {
            __ASSERT_DEBUG( resultOnStack, CodPanic( ECodInternal ) );
            // This may change the address of utf8buffer so we need to put it
            // on the cleanup stack again!!
            utf8buffer = utf8buffer->ReAllocL
                ( utf8buffer->Length() + ptr.Length() );
            CleanupStack::Pop();    // old utf8buffer
            CleanupStack::PushL( utf8buffer );  // possibly new copy
            }
        TPtr8 utf8ptr( utf8buffer->Des() );
        utf8ptr.Append( ptr );
        if ( remaining > 0 )
            {
            // Try to convert all remaining characters.
            ptr.Zero();
            remaining = CnvUtfConverter::ConvertFromUnicodeToUtf8
                ( ptr, aUcs2Src.Right( remaining ) );
            }
        else
            {
            break;
            }
        }
    
    if ( resultOnStack )
        {
        CleanupStack::Pop();    // utf8buffer
        }
    CleanupStack::PopAndDestroy( buffer );
    return utf8buffer;
    }

// ---------------------------------------------------------
// CodUtil::ConvertLC()
// ---------------------------------------------------------
//
EXPORT_C HBufC8* CodUtil::ConvertLC( const TDesC& aDes )
    {
    HBufC8* buf = HBufC8::NewLC( aDes.Length() );
    buf->Des().Copy( aDes );
    return buf;
    }

// ---------------------------------------------------------
// CodUtil::ConvertLC()
// ---------------------------------------------------------
//
EXPORT_C HBufC* CodUtil::ConvertLC( const TDesC8& aDes )
    {
    HBufC* buf = HBufC::NewLC( aDes.Length() );
    buf->Des().Copy( aDes );
    return buf;
    }

// ---------------------------------------------------------
// CodUtil::ConvertL()
// ---------------------------------------------------------
//
EXPORT_C HBufC8* CodUtil::ConvertL( const TDesC& aDes )
    {
    HBufC8* buf = HBufC8::NewL( aDes.Length() );
    buf->Des().Copy( aDes );
    return buf;
    }

// ---------------------------------------------------------
// CodUtil::ConvertL()
// ---------------------------------------------------------
//
EXPORT_C HBufC* CodUtil::ConvertL( const TDesC8& aDes )
    {
    HBufC* buf = HBufC::NewL( aDes.Length() );
    buf->Des().Copy( aDes );
    return buf;
    }

// ---------------------------------------------------------
// CodUtil::ApCountL()
// ---------------------------------------------------------
//
TUint32 CodUtil::ApCountL()
    {
    TUint32 count( 0 );
    CCommsDatabase* cdb = CCommsDatabase::NewL( EDatabaseTypeIAP );
    CleanupStack::PushL( cdb );
    CApSelect* apSel = CApSelect::NewLC
        (
        *cdb,
        KEApIspTypeAll,
        EApBearerTypeAll,
        KEApSortUidAscending
        );
    count = apSel->Count();
    CleanupStack::PopAndDestroy( 2 );   // apSel, cdb
    return count;
    }

// ---------------------------------------------------------
// CodUtil::WapGatewayL()
// ---------------------------------------------------------
//
HBufC8* CodUtil::WapGatewayL( TUint32 aIap )
    {
    HBufC8* gateway8 = NULL;

    // Open a CommDB database server session.
    // This has to be local and initialised every time or we get
    // strange access denied leaves happening.
    CCommsDatabase* cdb = CCommsDatabase::NewL( EDatabaseTypeIAP );
    CleanupStack::PushL( cdb );

    // Now access the commdb to see whether we have an associated
    // WAP AP and if so determine the gateway that we are using.
    TPtrC wapApTable( WAP_IP_BEARER );
    TPtrC wapApIapCol( WAP_IAP );
    CCommsDbTableView* view =
        cdb->OpenViewMatchingUintLC( wapApTable, wapApIapCol, aIap );
    TInt err = view->GotoFirstRecord();
    if ( !err )
        {
        // There is a matching WAP AP table entry, does it have a
        // valid gateway?
        TPtrC wapApGwCol( WAP_GATEWAY_ADDRESS );
        HBufC* gateway16 = view->ReadLongTextLC( wapApGwCol );
        if ( gateway16->Compare( KCodBlankIpAdress ) )
            {
            // There is a valid WAP gateway.
            gateway8 = ConvertL( *gateway16 );  // Not pushed, no leave below.
            }
        CleanupStack::PopAndDestroy( gateway16 );
        }
    CleanupStack::PopAndDestroy( 2 );   // view, cdb
    return gateway8;
    }
