/*
* Copyright (c) 2008-2010 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:  CCFContextService class implementation.
 *
*/


// SYSTEM INCLUDES
#include <cfcontextdataobject.h>
#include <s32mem.h>
#include <f32file.h>
#include <centralrepository.h>

// USER INCLUDES
#include "cfcontextservice.h"
#include "cftrace.h"
#include "cfcommon.h"
#include "CFContextObjectImpl.h"
#include "cfcontextsubscriptionlistener.h"
#include "CFClientSession.h"
#include "cfservermessagelistener.h"
#include "ContextFrameworkPrivateCRKeys.h"
#include "CFContextQueryImpl.h"
#include "CFContextSubscriptionImpl.h"

// CONSTANTS
static const TInt KDefaultBufferSize = 1024;

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

// ---------------------------------------------------------------------------
// C++ constructor.
// ---------------------------------------------------------------------------
//
CCFContextService::CCFContextService( RCFClientSession& aSession,
    MCFListener& aListener ):
    CCFServiceBase( CCFServiceBase::ECFContextService, aSession, aListener ),
    iContextDataMaxSize( KContextDataObjectDefaultSize )
    {
    FUNC_LOG;
    }

// ---------------------------------------------------------------------------
// Symbian 2nd phase constructor.
// ---------------------------------------------------------------------------
//
void CCFContextService::ConstructL( )
    {
    FUNC_LOG;

    // Create context subscription listener
    iContextSubscriptionListener = CCFContextSubscriptionListener::NewL(
        iListener,
        iSession );
    
    // Create server message listener
    iServerMessageListener = CCFServerMessageListener::NewL(
      iListener,
      iSession );

    // Get context data object maximum size
    CRepository* cenrep = NULL;
    TRAPD( err, cenrep = CRepository::NewL( KCRUidCFCommonConf ) );
    if( cenrep && err == KErrNone )
        {
        INFO( "Context data object size read from cenrep." );
        err = cenrep->Get( KContextDataObjectMaxSize, iContextDataMaxSize );
        if( err != KErrNone )
            {
            iContextDataMaxSize = KContextDataObjectDefaultSize;
            }
        delete cenrep;
        }
    INFO_1( "Context data object max size set to: [0x%x].",
        iContextDataMaxSize );
    }

// ---------------------------------------------------------------------------
// Symbian two phased constructor.
// ---------------------------------------------------------------------------
//
CCFContextService* CCFContextService::NewL( RCFClientSession& aSession,
    MCFListener& aListener )
    {
    FUNC_LOG;

    CCFContextService* self = CCFContextService::NewLC( aSession, aListener );
    CleanupStack::Pop ( self );
    return self;
    }

// ---------------------------------------------------------------------------
// Symbian two phased constructor.
// Leaves pointer in the cleanup stack.
// ---------------------------------------------------------------------------
//
CCFContextService* CCFContextService::NewLC( RCFClientSession& aSession,
    MCFListener& aListener )
    {
    FUNC_LOG;

    CCFContextService* self = new( ELeave ) CCFContextService(
        aSession,
        aListener );
    CleanupStack::PushL ( self );
    self->ConstructL ( );
    return self;
    }

// ---------------------------------------------------------------------------
// C++ destructor.
// ---------------------------------------------------------------------------
//
CCFContextService::~CCFContextService( )
    {
    FUNC_LOG;

    delete iContextSubscriptionListener;
    delete iServerMessageListener;
    }

//----------------------------------------------------------------------------
// CCFContextService::DefineContext
//----------------------------------------------------------------------------
//
TInt CCFContextService::DefineContext( const TDesC& aContextSource,
    const TDesC& aContextType,
    const TSecurityPolicy& aReadSecurityPolicy,
    const TSecurityPolicy& aWriteSecurityPolicy )
    {
    FUNC_LOG;
    
    TInt err = KErrNone;
    if( aContextSource.Length() && aContextType.Length() )
        {
        TSecurityPolicyBuf readSecPolicyBuf( aReadSecurityPolicy );
        TSecurityPolicyBuf writeSecPolicyBuf( aWriteSecurityPolicy );
        TIpcArgs args( &aContextSource, &aContextType, &readSecPolicyBuf,
                &writeSecPolicyBuf );
        err = iSession.SendSync( EDefineContext, args );
        }
    else
        {
        // Incorrect context definition
        err = KErrArgument;        
        }

    if( err == KErrNone )
        {
        if( !iServerMessageListener->IsActive() )
            {
            // Activate listening
            iServerMessageListener->ReceiveMessages();
            }
        }

    return err;
    }

//----------------------------------------------------------------------------
// CCFContextService::PublishContextL
//----------------------------------------------------------------------------
//
void CCFContextService::PublishContextL( CCFContextObject& aContext )
    {
    FUNC_LOG;

    TInt err = KErrNone;
    if( IsContextLegalForAdd( aContext ) )
        {
        HBufC8* buffer = WriteStreamContextObjectLC( aContext );
        TIpcArgs args( buffer );
        err = iSession.SendSync( EPublishContext, args );
        CleanupStack::PopAndDestroy( buffer );
        }
    else
        {
        err = KErrArgument;
        }
    
    User::LeaveIfError( err );
    }

//----------------------------------------------------------------------------
// CCFContextService::PublishContextL
//----------------------------------------------------------------------------
//
void CCFContextService::PublishContextL( CCFContextObject& aContext,
    CCFContextDataObject& aData )
    {
    FUNC_LOG;

    TInt err = KErrNone;
    
    if( IsContextLegalForAdd( aContext ) )
        {
        err = IsContextDataLegalForAdd( aData );
        if( err == KErrNone )
            {
            // Define IPC arguments
            HBufC8* contextBuffer = WriteStreamContextObjectLC( aContext );
            HBufC8* contextDataBuffer = WriteStreamContextDataObjectLC( aData );
            TPckg<TUid> uidPckg( aData.Uid() );
            TIpcArgs args( contextBuffer, contextDataBuffer, &uidPckg );
            
            // Send message
            err = iSession.SendSync( EPublishContextWithData, args );

            // Clean up
            CleanupStack::PopAndDestroy( contextDataBuffer );
            CleanupStack::PopAndDestroy( contextBuffer );
            }
        }
    else
        {
        err = KErrArgument;
        }
    
    User::LeaveIfError( err );
    }

//----------------------------------------------------------------------------
// CCFContextService::SubscribeContextL
//----------------------------------------------------------------------------
//
void CCFContextService::SubscribeContextL(
    CCFContextSubscription& aSubscription)
    {
    FUNC_LOG;
    
    TInt err = KErrNone;

    // Stream subscription and send
    HBufC8* buffer = WriteStreamContextSubscriptionLC( aSubscription );
    TIpcArgs args( buffer );
    err = iSession.SendSync( EAddSubscription, args );
    CleanupStack::PopAndDestroy( buffer );
    
    User::LeaveIfError( err );

    if( err == KErrNone )
        {
        if( !iContextSubscriptionListener->IsActive() )
            {
            // Activate listening
            iContextSubscriptionListener->ReceiveContextIndications();
            }
        }
    }

//----------------------------------------------------------------------------
// CCFContextService::UnsubscribeContextL
//----------------------------------------------------------------------------
//
void CCFContextService::UnsubscribeContextL(
    CCFContextSubscription& aSubscription)
    {
    FUNC_LOG;
    
    TInt err = KErrNone;

    // Stream subscription and send
    HBufC8* buffer = WriteStreamContextSubscriptionLC( aSubscription );
    TIpcArgs args( buffer );
    err = iSession.SendSync( ERemoveSubscription, args );
    CleanupStack::PopAndDestroy( buffer );
    
    User::LeaveIfError( err );
    }

//----------------------------------------------------------------------------
// CCFContextService::RequestContextL
//----------------------------------------------------------------------------
//
void CCFContextService::RequestContextL( CCFContextQuery& aContextQuery,
    RContextObjectArray& aRequestResults )
    {
    FUNC_LOG;
    
    TInt err = KErrNone;

    // Stream context query set into buffer
    HBufC8* buffer = WriteStreamContextQueryLC( aContextQuery );
    HBufC8* resultBuffer = HBufC8::NewLC( iRequestBufferSize );
    TPtr8 resultBufferPtr = resultBuffer->Des();
    TPckg<TInt> bufferSizePtr( iRequestBufferSize );

    // Send request to server
    TIpcArgs args( &resultBufferPtr, buffer, &bufferSizePtr );
    err = iSession.SendSync( ERequestContext, args );
    if( err == KErrNone )
        {
        // Results succesfully received
        ReadStreamContextObjectL( *resultBuffer, aRequestResults );
        }
    else if( err == KErrTooBig )
        {
        // Insufficient buffer size, increase buffer and try again
        CleanupStack::PopAndDestroy( resultBuffer );
        resultBuffer = HBufC8::NewLC( iRequestBufferSize );
        resultBufferPtr.Set( resultBuffer->Des() );
        TIpcArgs args( &resultBufferPtr );
        err = iSession.SendSync( ERequestContext, args );
        if( err == KErrNone )
            {
            ReadStreamContextObjectL( *resultBuffer, aRequestResults );
            }
        else
            {
            err = KErrNoMemory;
            }
        }
    CleanupStack::PopAndDestroy( resultBuffer );
    CleanupStack::PopAndDestroy( buffer );

    // If requst buffer size grows too big, zero it
    if( iRequestBufferSize > KDefaultBufferSize )
        {
        iRequestBufferSize = 0;
        }

    User::LeaveIfError( err );
    }

//----------------------------------------------------------------------------
// CCFContextService::RequestContextSetL
//----------------------------------------------------------------------------
//
void CCFContextService::RequestContextSetL(
    RContextQueryArray& aContextQuerySet,
    RContextObjectArray& aRequestResults)
    {
    FUNC_LOG;
    
    TInt err = KErrNone;
    
    // Stream context query set into buffer
    HBufC8* buffer = WriteStreamContextQueryLC( aContextQuerySet );
    HBufC8* resultBuffer = HBufC8::NewLC( iRequestBufferSize );
    TPtr8 resultBufferPtr = resultBuffer->Des();
    resultBufferPtr.Set( resultBuffer->Des() );
    
    // Send request to server
    TPckg<TInt> bufferSizePtr( iRequestBufferSize );
    TIpcArgs args( &resultBufferPtr, buffer, &bufferSizePtr );
    err = iSession.SendSync( ERequestContextSet, args );
    if( err == KErrNone )
        {
        // Results succesfully received
        ReadStreamContextObjectL( *resultBuffer, aRequestResults );
        }
    else if( err == KErrTooBig )
        {
        // Insufficient buffer size, increase buffer and try again
        CleanupStack::PopAndDestroy( resultBuffer );
        resultBuffer = HBufC8::NewLC( iRequestBufferSize );
        resultBufferPtr.Set( resultBuffer->Des() );
        TIpcArgs args( &resultBufferPtr );
        err = iSession.SendSync( ERequestContextSet, args );
        if( err == KErrNone )
            {
            ReadStreamContextObjectL( *resultBuffer, aRequestResults );
            }
        else
            {
            err = KErrNoMemory;
            }
        }
    CleanupStack::PopAndDestroy( resultBuffer );
    CleanupStack::PopAndDestroy( buffer );

    // If requst buffer size grows too big, zero it
    if( iRequestBufferSize > KDefaultBufferSize )
        {
        iRequestBufferSize = 0;
        }

    User::LeaveIfError( err );
    }

//----------------------------------------------------------------------------
// CCFContextService::InstallContextSourceSetting
//----------------------------------------------------------------------------
//
TInt CCFContextService::InstallContextSourceSetting(
    const TDesC& aSettingFilename,
    const TUid& aContextSourceUid )
    {
    FUNC_LOG;
    
    RFs fs;
    TInt err = fs.Connect();
    if ( err != KErrNone )
        {
        return err; 
        }
    
    // Make possible to share the file server session with the CF server.
    err = fs.ShareProtected();
    if( err != KErrNone )
        {
        fs.Close();
        return err;
        }
    
    // Open the file.
    RFile settingFile;
    err = settingFile.Open( fs, aSettingFilename, EFileRead );
    if( err != KErrNone )
        {
        fs.Close();
        return err;
        }
    
    // Deliver the file handle to the server.
    TIpcArgs args;
    err = settingFile.TransferToServer( args, 0, 1 );
    if( err != KErrNone )
        {
        settingFile.Close();
        fs.Close();
        return err;
        }
    
    TPckgC< TUid > uidPckgC( aContextSourceUid );
    args.Set( 2, &uidPckgC );
    
    err = iSession.SendSync( EInstallSourceSetting, args );
    
    settingFile.Close();
    fs.Close();
    
    return err;
    }

//----------------------------------------------------------------------------
// CCFContextService::UninstallContextSourceSetting
//----------------------------------------------------------------------------
//
TInt CCFContextService::UninstallContextSourceSetting(
    const TDesC& aSettingFilename,
    const TUid& aContextSourceUid )
    {
    FUNC_LOG;

    // Deliver setting filename and context source UID to the server.
    TPckgC< TUid > uidPckgC( aContextSourceUid );
    TIpcArgs args( &aSettingFilename, &uidPckgC );

    TInt err = iSession.SendSync( EUninstallSourceSetting, args );

    return err;
    }

//----------------------------------------------------------------------------
// CCFContextService::UninstallContextSourceSettings
//----------------------------------------------------------------------------
//
TInt CCFContextService::UninstallContextSourceSettings(
    const TUid& aContextSourceUid )
    {
    FUNC_LOG;

    // Deliver context source UID to the server.
    TPckgC< TUid > uidPckgC( aContextSourceUid );
    TIpcArgs args( &uidPckgC );

    TInt err = iSession.SendSync( EUninstallSourceSettings, args );

    return err;
    }

//----------------------------------------------------------------------------
// CCFContextService::IsContextLegalForAdd
//----------------------------------------------------------------------------
//
TBool CCFContextService::IsContextLegalForAdd(
    const CCFContextObject& aContext)
    {
    FUNC_LOG;
    
    TBool retVal = EFalse;
    
    if( aContext.Type().Length() > 0 &&
        aContext.Source().Length() > 0 &&
        aContext.Value().Length() > 0 )
        {
        retVal = ETrue;
        }
        
    return retVal;
    }

//----------------------------------------------------------------------------
// CCFContextService::IsContextDataLegalForAdd
//----------------------------------------------------------------------------
//
TInt CCFContextService::IsContextDataLegalForAdd(
    const CCFContextDataObject& aContextData )
    {
    FUNC_LOG;
    
    TInt retVal = KErrNone;
    if( aContextData.Size() > iContextDataMaxSize )
        {
        retVal = KErrTooBig;
        }
    else if( aContextData.Uid() == KNullUid )
        {
        retVal = KErrNotSupported;
        }
    
    return retVal;
    }

//----------------------------------------------------------------------------
// CCFContextService::ReadStreamContextObjectL
//----------------------------------------------------------------------------
//
void CCFContextService::ReadStreamContextObjectL( const TDesC8& aData,
    RContextObjectArray& aArray )
    {
    FUNC_LOG;

    // Check that we have received data from server    
    if( aData.Length() )
        {
        CCFContextObjectImpl* context = NULL;
        RDesReadStream stream( aData );
        stream.PushL();
        TInt count = stream.ReadInt16L();
        for( TInt i = 0; i < count; i++ )
            {
            context = CCFContextObjectImpl::NewLC();
            context->InternalizeL( stream );
            aArray.AppendL( context );
            CleanupStack::Pop( context );
            }
        
        // Clean up
        CleanupStack::PopAndDestroy( &stream );
        }
    }

//----------------------------------------------------------------------------
// CCFContextService::WriteStreamContextSubscriptionLC
//----------------------------------------------------------------------------
//
HBufC8* CCFContextService::WriteStreamContextSubscriptionLC(
    CCFContextSubscription& aSubscription )
    {
    FUNC_LOG;
    
    CCFContextSubscriptionImpl& subscription =
        static_cast<CCFContextSubscriptionImpl&>( aSubscription );

    HBufC8* buffer = HBufC8::NewLC( subscription.Size() );
    TPtr8 bufferPtr = buffer->Des();

    RDesWriteStream stream( bufferPtr );
    stream.PushL();
    subscription.ExternalizeL( stream );

    // Cleanup
    CleanupStack::PopAndDestroy( &stream );

    return buffer;
    }

//----------------------------------------------------------------------------
// CCFContextService::WriteStreamContextObjectLC
//----------------------------------------------------------------------------
//
HBufC8* CCFContextService::WriteStreamContextObjectLC(
    CCFContextObject& aContext )
    {
    FUNC_LOG;
    
    CCFContextObjectImpl& context =
        static_cast<CCFContextObjectImpl&>( aContext );
    
    HBufC8* buffer = HBufC8::NewLC( context.Size() );
    TPtr8 bufferPtr = buffer->Des();

    RDesWriteStream stream( bufferPtr );
    stream.PushL();
    context.ExternalizeL( stream );

    // Cleanup
    CleanupStack::PopAndDestroy( &stream );

    return buffer;
    }

//----------------------------------------------------------------------------
// CCFContextService::WriteStreamContextQueryLC
//----------------------------------------------------------------------------
//
HBufC8* CCFContextService::WriteStreamContextQueryLC(
    RContextQueryArray& aQueryArray )
    {
    FUNC_LOG;

    // Calculate buffer size
    // Count of array and queries
    CCFContextQueryImpl* query = NULL;
    TInt size = 0;
    size += sizeof( TInt );
    TInt count = aQueryArray.Count();
    for( TInt i = 0; i < count; i++ )
        {
        query = static_cast<CCFContextQueryImpl*>( aQueryArray[i] );
        size += query->Size();
        }
    
    HBufC8* buffer = HBufC8::NewLC( size );
    TPtr8 bufferPtr = buffer->Des();

    RDesWriteStream stream( bufferPtr );
    stream.PushL();
    stream.WriteInt16L( count );
    for( TInt i = 0; i < count; i++ )
        {
        query = static_cast<CCFContextQueryImpl*>( aQueryArray[i] );
        query->ExternalizeL( stream );
        }

    // Commit stream
    stream.CommitL();

    // Cleanup
    CleanupStack::PopAndDestroy( &stream );

    return buffer;
    }

//----------------------------------------------------------------------------
// CCFContextService::WriteStreamContextQueryLC
//----------------------------------------------------------------------------
//
HBufC8* CCFContextService::WriteStreamContextQueryLC( CCFContextQuery& aQuery )
    {
    FUNC_LOG;

    CCFContextQueryImpl& query =
        static_cast<CCFContextQueryImpl&>( aQuery );
    
    HBufC8* buffer = HBufC8::NewLC( query.Size() );
    TPtr8 bufferPtr = buffer->Des();

    RDesWriteStream stream( bufferPtr );
    stream.PushL();
    query.ExternalizeL( stream );

    // Cleanup
    CleanupStack::PopAndDestroy( &stream );
    
    return buffer;
    }

//----------------------------------------------------------------------------
// CCFContextService::WriteStreamContextDataObjectLC
//----------------------------------------------------------------------------
//
HBufC8* CCFContextService::WriteStreamContextDataObjectLC(
    CCFContextDataObject& aContextData )
    {
    FUNC_LOG;
    
    HBufC8* streamBuf = HBufC8::NewLC( aContextData.Size() );
    TPtr8 streamBufPtr( streamBuf->Des() );
    RDesWriteStream writeStream( streamBufPtr );
    CleanupClosePushL( writeStream );
    aContextData.ExternalizeL( writeStream );
    
    // Clean up
    CleanupStack::PopAndDestroy( &writeStream );
    
    return streamBuf;
    }

// End of file
