/*
* Copyright (c) 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:   Implementation of CCatalogsClientServerServer
*
*/


#include "catalogsclientserverserver.h"
#include "catalogsclientserverserversession.h"
#include "catalogsserverdefines.h"
#include "catalogsserverengine.h"
#include "catalogscontext.h"

#include "catalogsdebug.h"

CCatalogsClientServerServer* CCatalogsClientServerServer::iCatalogsServer = NULL;

// Replaced with USE_BUILD_SCRIPT when using build script
#define DUMMY_DEFINE

#ifdef CATALOGS_UNDERTAKER
#ifdef USE_BUILD_SCRIPT
_LIT( KCatalogsUndertakerFilename, "ncdundertaker_APP_NAME_POSTFIX" );
#else
_LIT( KCatalogsUndertakerFilename, "ncdundertaker_20019119" );
#endif // USE_BUILD_SCRIPT
#endif

// Capability set required from clients. Consists of CAP_APPLICATION capability set.
static _LIT_SECURITY_POLICY_C7(
    KCatalogsServerConnectPolicy1,
    ECapabilityNetworkServices,
    ECapabilityLocalServices,
    ECapabilityLocation,
    ECapabilityReadUserData,
    ECapabilityWriteUserData,
    ECapabilityReadDeviceData,
    ECapabilityWriteDeviceData );
    
static _LIT_SECURITY_POLICY_C2(
    KCatalogsServerConnectPolicy2,
    ECapabilitySwEvent,
    ECapabilityUserEnvironment );

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


// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
// 
CCatalogsClientServerServer* CCatalogsClientServerServer::NewLC()
    {
    CCatalogsClientServerServer* self = new( ELeave )
        CCatalogsClientServerServer( CActive::EPriorityLow - 20 );
    CleanupStack::PushL( self );
    self->ConstructL();
    return self;
    }

// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
// 
CCatalogsClientServerServer::CCatalogsClientServerServer( TInt aPriority ) :
    CServer2( aPriority )
    {
    iCatalogsServer = this;
    }

// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
// 
CCatalogsClientServerServer::~CCatalogsClientServerServer()
    {
    DLTRACEIN((""));
    // Usually at this point all sessions are already destroyed.
    // If not, we have to go through all sessions and destroy
    // all references to the objects to which references might be lost
    // if the engine was destroyed first.
    // Also when doing it this way when the engine is destroyed,
    // provider can do the final killing of those objects
    iSessionIter.SetToFirst();
    CSession2* baseSession = NULL;
    CCatalogsClientServerServerSession* session = NULL;
    baseSession = iSessionIter++;
    while ( baseSession != NULL )
        {
        DLTRACE(("Calling ClientSideSessionDown"));
        // When going down, act as the client side would have died
        // ->call ClientSideSessionDown for each session
        session = 
            static_cast<CCatalogsClientServerServerSession*>( baseSession );
        session->ClientSideSessionDown();
        baseSession = iSessionIter++;
        DLTRACE(("ClientSideSessionDown called"));
        }
    
    DLTRACE(("Delete engine"));
    // This is destroyed before destroying container, so possible
    // resources are freed in its destructor and from the container 
    // in here.
    // Other way would end up destroying already destroyed stuff.
    delete iEngine;

    DLTRACE(("Engine deleted"));
    iShutdownOperations.ResetAndDestroy();
    
    // If container is going to be removed, it has to be done before
    // it is destroyed by destroying the container index.
    if ( iContainer != NULL )
        {
        DLTRACE(("Remove container"));
        RemoveContainer( iContainer );        
        DLTRACE(("Container removed"));
        }
    iContainer = NULL;
    
    DLTRACE(("Deleting container index"));
    delete iContainerIndex;

#ifdef CATALOGS_UNDERTAKER

    iUndertakerProcess.Close();

#endif 

    DLTRACEOUT((""));
    }


// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
// 
void CCatalogsClientServerServer::ConstructL()
    {
    DLTRACEIN((""));
    User::LeaveIfError( User::RenameThread( KCatalogsServerName ) );
#ifdef CATALOGS_UNDERTAKER

    DLINFO(( "Creating undertaker process" ));
    TInt err = iUndertakerProcess.Create( KCatalogsUndertakerFilename, KNullDesC() );
    if( err != KErrNone )
        {
        DLERROR(( "Creating undertaker process failed with %d", err ));
        }
    iUndertakerProcess.SetPriority( (TProcessPriority)450 );  // EPriorityHigh
    
    RThread thisThread;
    DLINFO(( _L("Setting undertaker parameter 15 as %S"), &thisThread.FullName() ));
    
    err = iUndertakerProcess.SetParameter( 15, thisThread.FullName() );
    if( err != KErrNone )
        {
        DLERROR(( "Parameter setting failed with %d", err ));
        }
    thisThread.Close();
    iUndertakerProcess.Resume();

#endif

    iContainerIndex = CObjectConIx::NewL();
    iContainer = NewContainerL();

	iEngine = CCatalogsServerEngine::NewL();
    // Add the server to the active scheduler
    StartL( KCatalogsServerName );
    DLTRACEOUT((""));
    }

// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
// 
void CCatalogsClientServerServer::IncrementSessions()
    {
    iSessionCount++;
    }

// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
// 
void CCatalogsClientServerServer::DecrementSessions()
    {
    iSessionCount--;

    // No more clients, shutdown
    if ( iSessionCount <= 0 )
        {
        CActiveScheduler::Stop();
        }
    }
    
// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
// 
void CCatalogsClientServerServer::HandleSessionRemoval( 
    MCatalogsSession& aSession )
    {
    DASSERT( iEngine );
    
    // We don't want to go further unless the context has been created
    if ( aSession.ContextPtr() )
        {
        iEngine->HandleSessionRemoval( aSession );
        // Execute shutdown operations
        OperateShutdownOperations( aSession.Context(), ETrue );
        }
    }

// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
// 
CObjectCon* CCatalogsClientServerServer::NewContainerL()
	{
    // Return a new object container
	return iContainerIndex->CreateL();
	}

// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
// 
void CCatalogsClientServerServer::RemoveContainer( CObjectCon* aCon )
	{
	DASSERT( iContainerIndex );
	iContainerIndex->Remove( aCon );
	}

// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
// 
void CCatalogsClientServerServer::CreateProviderL( 
    MCatalogsSession& aSession, 
    TInt aProviderIdentifier,
    TInt& aHandle,
    TUint32 aProviderOptions )
    {
    DLTRACEIN((""));
    
    iEngine->CreateProviderL( aSession,
                              aProviderIdentifier, 
                              aHandle,
                              aProviderOptions );

    }

// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
// 
TInt CCatalogsClientServerServer::NewInstanceId()
    {
    DLTRACEIN((""));
    return iSessionInstanceIdCounter++;
    }

// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
// 
void CCatalogsClientServerServer::AddObjectToContainerL( CObject* aObject )
    {    
    // If possible, change trap-implementation to something else.
    // This is just a simple way to do this.
    TRAPD( err, iContainer->AddL( aObject ) );
    DLTRACE(("err: %d", err ));
    if ( err == KErrAlreadyExists || err == KErrNone )
        {        
        return;
        }
    else
        {
        User::Leave( err );
        }
    }

// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
// 
void CCatalogsClientServerServer::ThreadFunctionL()
    {
    DLTRACEIN((""));
    DCHECK_HEAP
    // Construct an active scheduler for the server

    DLINFO(( "Creating active schduler" ));
    CActiveScheduler* activeScheduler = new (ELeave) CActiveScheduler;
    CleanupStack::PushL( activeScheduler );

    // Install active scheduler
    // No need to check whether an active scheduler is already installed
    // as this is a new thread, so there won't be one
    DLINFO(( "Installing active schduler" ));
    CActiveScheduler::Install( activeScheduler );

    // Construct our server
    CCatalogsClientServerServer::NewLC(); // anonymous

    DLINFO(( "Opening semaphore" ));
    RSemaphore semaphore;
    User::LeaveIfError(
        semaphore.OpenGlobal( KCatalogsServerSemaphoreName ));
    
    DLINFO(( "Signalling and closing semaphore" ));
    // Semaphore opened ok
    semaphore.Signal();
    semaphore.Close();

    // Start handling requests, operation will stop here until
    // the server dies
    CActiveScheduler::Start();

    DLINFO(("Server has shut down"));

    // The server has been shut down, clean up
    CleanupStack::PopAndDestroy( 2, activeScheduler );
    DLTRACEOUT((""));
    }

// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
// 
TInt CCatalogsClientServerServer::ThreadFunction( TAny* /*aNone*/ )
    {
#ifdef CATALOGS_BUILD_CONFIG_HEAP_MARKS    
    __UHEAP_MARK;
#endif    
    DLINIT;
    
    DLTRACEIN((""));
    
    CTrapCleanup* cleanupStack = CTrapCleanup::New();
    TRAPD( err, ThreadFunctionL() );
    
    DLTRACE(( "err: %d", err ));
    delete cleanupStack;
    cleanupStack = NULL;
    
    DLTRACEOUT((""));

    DLUNINIT;
#ifdef CATALOGS_BUILD_CONFIG_HEAP_MARKS    
    __UHEAP_MARKEND; 
#endif    
    return err;    
    }

// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
// 
TInt CCatalogsClientServerServer::RunError( TInt aError )
    {
    DLTRACEIN(( "aError=%d", aError ));    

    // Complete the message being processed, although the state of
    // the server is undetermined
    DLERROR(( "Completing last message with %d", aError ));
    Message().Complete( aError );

    DLTRACEOUT(("KErrNone"));
    return KErrNone;
    }

// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
// 
CSession2* CCatalogsClientServerServer::NewSessionL( 
    const TVersion& aVersion,
    const RMessage2& aMsg ) const
    {
    DLTRACEIN((""));
        
    // Check whether the server is compatible with the 
    // requested version
    if ( !User::QueryVersionSupported(
           TVersion(
               KCatalogsServerMajorVersionNumber,
               KCatalogsServerMinorVersionNumber,
               KCatalogsServerBuildVersionNumber
               ),
           aVersion))
        {        
        User::Leave( KErrNotSupported );
        }

    DLINFO(("Checking client capabilities"));
    // Check the client for required capabilities
    if (!KCatalogsServerConnectPolicy1().CheckPolicy(
        aMsg, __PLATSEC_DIAGNOSTIC_STRING("CCatalogsClientServerServer::NewSessionL, KCatalogsServerConnectPolicy1") ) ||
        !KCatalogsServerConnectPolicy2().CheckPolicy(
        aMsg, __PLATSEC_DIAGNOSTIC_STRING("CCatalogsClientServerServer::NewSessionL, KCatalogsServerConnectPolicy2" ) ) )
        {
        DLINFO(("Client capability check failed"));
        User::Leave( KErrPermissionDenied );
        }

    // Context is not created here for the session, because
    // user given uid cannot be received by this function from
    // the client-side and it is nicer to create the whole
    // context-object in one place.

    DLTRACEOUT((""));
        
    // Create a new session
    RThread client;
    return CCatalogsClientServerServerSession::NewL(
        client,
        *const_cast<CCatalogsClientServerServer*>( this ) );
    }


/**
 * Platform dependant entry points
 */

IMPORT_C TInt WinsMain();
EXPORT_C TInt WinsMain()
	{
	// WINS DLL entry-point. Just return the real thread function 
    // cast to TInt
	return reinterpret_cast<TInt>( 
	    &CCatalogsClientServerServer::ThreadFunction );
	}

// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
// 
GLDEF_C TInt E32Main()
    {
    return CCatalogsClientServerServer::ThreadFunction( NULL );
    }


// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
// 
TInt CCatalogsClientServerServer::AddShutdownOperation( 
    CCatalogsShutdownOperation* aOperation )
    {
    DLTRACEIN((""))
    DASSERT( aOperation );
    TInt err = iCatalogsServer->iShutdownOperations.Append( aOperation );

    if ( err == KErrNone )
        {
        aOperation->SetObserver( *iCatalogsServer );
        iCatalogsServer->IncrementSessions();
        }
    else
        {
        DLERROR(("Appending failed: %d", err));
        delete aOperation;
        }
    
    return err;    
    }


// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
// 
void CCatalogsClientServerServer::ShutdownOperationComplete( 
    CCatalogsShutdownOperation* aOperation, 
    TInt aError )
    {
    DLTRACEIN(("aError: %d", aError));
    (void) aError;
    iShutdownOperations.Remove( iShutdownOperations.Find( aOperation ) );
    delete aOperation;
    DecrementSessions();
    }


// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
// 
void CCatalogsClientServerServer::OperateShutdownOperations( 
    const MCatalogsContext& aContext,
    TBool aExecute )
    {
    DLTRACEIN((""));
    TInt count = iShutdownOperations.Count();
    const TUid uid( aContext.FamilyId() );
    // Iterating backwards because an executed operation may be removed
    // from the queue while we are iterating through it
    while( count-- )
        {
        if ( iShutdownOperations[ count ]->FamilyUid() == uid ) 
            {
            if ( aExecute )
                {
                iShutdownOperations[ count ]->Execute();
                }
            else
                {
                iShutdownOperations[ count ]->Cancel();
                }
            }
        }
    }
