// Copyright (c) 2001-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:
//

#include "agsmain.h"

#include <bacline.h>
#include <e32property.h>

#include "agsalarm.h"
#include "agmtlsproxy.h"
#include "agmserv.h"
#include "agssess.h"
#include "agsentrymodel.h"
#include "agsfilemanager.h"
#include "agssignal.h"
#ifdef SYMBIAN_SYSTEM_STATE_MANAGEMENT
#include "agssystemstateobserver.h"
#endif
#include <calnotification.h>

static _LIT_SECURITY_POLICY_PASS(KReadPolicy);
static _LIT_SECURITY_POLICY_S0(KWritePolicy, 0x10003a5b);

/**
 @internalComponent
 */
CAgnServUndertaker* CAgnServUndertaker::NewL()
	{
	CAgnServUndertaker* self = new(ELeave)CAgnServUndertaker();
	CleanupStack::PushL(self);
	self->ConstructL();
	CleanupStack::Pop(self);
	return self;
	}



// policy element constants
#define KPolicyElementReadUserData  		0
#define KPolicyElementWriteUserData  		1

const TUint KRangeCount = 4; 

const TInt KOpCodeRanges[KRangeCount] = 
	{	
	KCapabilityNone,
	KCapabilityReadUserData,
	KCapabilityWriteUserData,
	EAgnNotSupported,
	};

const TUint8 KElementsIndex[KRangeCount] =
	{
	CPolicyServer::EAlwaysPass,		
	KPolicyElementReadUserData,		
	KPolicyElementWriteUserData,	
	CPolicyServer::ENotSupported	
	};

const CPolicyServer::TPolicyElement KPolicyElements[] = 
	{
	{_INIT_SECURITY_POLICY_C1(ECapabilityReadUserData), CPolicyServer::EFailClient}, // KPolicyElementReadUserData
	{_INIT_SECURITY_POLICY_C1(ECapabilityWriteUserData), CPolicyServer::EFailClient}, // KPolicyElementWriteUserData
	};


const CPolicyServer::TPolicy KAgnSrvrPolicy =
	{
	CPolicyServer::EAlwaysPass, //specifies all connect attempts should pass
	KRangeCount,
	KOpCodeRanges,
	KElementsIndex, 	// what each range is compared to 
	KPolicyElements 	// what policies range is compared to
	};

void CAgnServUndertaker::ConstructL()
	{
	CActiveScheduler* scheduler = new(ELeave)CActiveScheduler;
	CActiveScheduler::Install(scheduler);

	iServer = CAgnServer::InitL(this);
	CActiveScheduler::Add(this);
	}

void CAgnServUndertaker::DoCancel()
	{
	}
void CAgnServUndertaker::RunL()
	{
	if (!iCancelled)
		{
		Deque();
		delete iServer;

		CActiveScheduler::Stop();
		}
	}


void CAgnServUndertaker::DestroyServer()
	{
	// Set the undertaker active, and make a request
	if (!IsActive())
		{
		SetActive();
		TRequestStatus* status = &iStatus;
		User::RequestComplete(status,KErrNone);
		}

	iCancelled = EFalse;
	}

void CAgnServUndertaker::CancelUndertaker()
	{
	iCancelled = ETrue;
	}

// CAgnAlarmServerChangeListener
//
CAgnAlarmServerChangeListener* CAgnAlarmServerChangeListener::NewL(RASCliSession& aAlarmServer, CAgnServFileMgr& aFileMgr)
    {
    CAgnAlarmServerChangeListener* self = new(ELeave) CAgnAlarmServerChangeListener(aAlarmServer, aFileMgr);
    CleanupStack::PushL(self);
    self->ConstructL();
    CleanupStack::Pop(self);
    return (self);
    }

CAgnAlarmServerChangeListener::CAgnAlarmServerChangeListener(RASCliSession& aAlarmServer, CAgnServFileMgr& aFileMgr)
    :CActive(EPriorityLow), iAlarmServer(aAlarmServer), iFileMgr(aFileMgr)
    {
    CActiveScheduler::Add(this);
    }
    
void CAgnAlarmServerChangeListener::ConstructL()
    {
    SetActive();
    iAlarmServer.NotifyChange(iStatus, iAlarmId);
    }


CAgnAlarmServerChangeListener::~CAgnAlarmServerChangeListener()
    {
    Cancel();
    }

#ifdef __CAL_VERBOSE_LOGGING__
TInt CAgnAlarmServerChangeListener::RunError(TInt aError)
    {
    AgmDebug::DebugLog("CAgnAlarmServerChangeListener::RunError has error %d", aError);
    return ( KErrNone );
    }
#else
TInt CAgnAlarmServerChangeListener::RunError(TInt /*aError*/)
    {
    return ( KErrNone );
    }
#endif


void CAgnAlarmServerChangeListener::RunL()
    {  
    TInt change(iStatus.Int());
    
    if (iStatus.Int() != KErrServerTerminated)
        {
        SetActive();
        iAlarmServer.NotifyChange(iStatus, iAlarmId);
        }
    
    // Tell each file that there has been a change in the alarm server
    const TInt KFileCount(iFileMgr.Count());
    
    for (TInt i(0) ; i < KFileCount ; ++i)
        {
        iFileMgr.File(i)->Model()->Alarm().ChangeObserver().HandleAlarmServerChangeL(change);
        }
    }


void CAgnAlarmServerChangeListener::DoCancel()
    {
    if (iStatus == KRequestPending)
        {
        iAlarmServer.NotifyChangeCancel();
        }
    }



// Initialize the server - create a CAgnServer object, and an
// active scheduler

CAgnServer* CAgnServer::InitL(CAgnServUndertaker* aUndertaker)
	{
	// start server
	CAgnServer* server = CAgnServer::NewL();
	server->iUndertaker = aUndertaker;
	return server;
	}


CAgnServer::CAgnServer(TInt aPriority)
 : CPolicyServer(aPriority, KAgnSrvrPolicy, ESharableSessions), iServerMode(ETransientServer)
	{
	}


// Create & start a new Agenda server
CAgnServer* CAgnServer::NewL()
	{
	CAgnServer* self = new (ELeave) CAgnServer(KServerPriority);
	CleanupStack::PushL(self);
	self->ConstructL();



	CleanupStack::Pop();		// self

	return self;
	}

CAgnTlsProxy* CAgnServer::TimeZoneConverter()
	{
	return iProxy;
	}

// Create a file manager and connect to the file server
// Rename the server process to "AgendaServer"
// Set up the log file, if logging is required
// Start the server
//
void CAgnServer::ConstructL()
	{
	CCommandLineArguments* args = CCommandLineArguments::NewLC();
	_LIT(KCmdLineArgNonTransientServer, "-nontransient");
	const TInt count = args->Count();
	for (TInt i = 1; i < count; i++)
		{
		if (args->Arg(i).CompareF(KCmdLineArgNonTransientServer) == 0)
			{
			iServerMode = ENonTransientServer;
			break;
			}
		}
	CleanupStack::PopAndDestroy(args);	
	
	iProxy = CAgnTlsProxy::CreateL(CAgnTlsProxy::TAgnTlsTzRulesType_Server);
			
	User::LeaveIfError(iFs.Connect());
	User::LeaveIfError(iAlarmServer.Connect());
	iFileMgr = CAgnServFileMgr::NewL(iFs, *this);
	iFileMgr->CreatePermanentDataL();
	iBackupRestoreAgent = CAgnServBackupRestoreAgent::NewL(*iFileMgr);
	iBackupRestoreAgent->Start();
	iAlarmServerChangeListener = CAgnAlarmServerChangeListener::NewL(iAlarmServer, *iFileMgr);


#ifdef SYMBIAN_SYSTEM_STATE_MANAGEMENT
	TRAP_IGNORE(iSystemStateObserver = CCAgnSystemStateObserver::NewL(*iFileMgr));
#endif
	User::LeaveIfError(RThread().RenameMe(KAgendaServerName));

	StartL(KAgendaServerName);

	TInt err = RProperty::Define(KCalPubSubCategory, ECalPubSubTodoNotification, RProperty::EByteArray, KReadPolicy, KWritePolicy);   
	if (err != KErrNone && err != KErrAlreadyExists)
	    {
	    User::Leave(err);
	    }
	    
	err = RProperty::Define(KCalPubSubCategory, ECalPubSubEventNotification, RProperty::EByteArray, KReadPolicy, KWritePolicy);   
	if (err != KErrNone && err != KErrAlreadyExists)
	    {
	    User::Leave(err);
	    }
	}


CAgnServer::~CAgnServer()
	{
	delete iAlarmServerChangeListener;
	
	iAlarmServer.Close();
#ifdef SYMBIAN_SYSTEM_STATE_MANAGEMENT
	delete iSystemStateObserver;
#endif
	delete iBackupRestoreAgent;
	delete iFileMgr;
	iFs.Close();
	

	__ASSERT_DEBUG(iProxy != NULL, User::Invariant());
	CAgnTlsProxy::Release(NULL);
	}


// Create a new server session

CSession2* CAgnServer::NewSessionL(const TVersion& aVersion,const RMessage2& /*aMessage*/) const
	{
	TVersion ver(KAgnServerMajorVersion, KAgnServerMinorVersion, KAgnServerBuildVersion);
	if (!User::QueryVersionSupported(ver,aVersion))
		User::Leave(KErrNotSupported);

	// Call this just in case a previous session caused the undertaker to run
	iUndertaker->CancelUndertaker();

	CAgnServer* nonConstPtr = const_cast<CAgnServer*>(this);
	return CAgnServerSession::NewL(*nonConstPtr);
	}


void CAgnServer::SessionClosed()
	{
	// Called when a session exits
	// If this is the last session for the server, then close down the active object
	iSessionCount--;

	ShutdownServer();
	}

// Try to shutdown the server making sure that no sessions are connected to it, its in transient mode and no file delay timers are running
void CAgnServer::ShutdownServer()
	{
	if (iSessionCount == 0 && (iServerMode == ETransientServer) && !(iFileMgr->FileCloseTimersRunning()))
		{
		// Destroy the server
		iUndertaker->DestroyServer();
		}
	}

void CAgnServer::SessionAdded()
	{
	iSessionCount++;
	}

RFs* CAgnServer::FsSession()
	{
	return &iFs;
	}

void CAgnServer::FetchSessionsL(RPointerArray<CAgnServerSession>& aSessions)
  	{
  	iSessionIter.SetToFirst();
  	
  	while (iSessionIter != NULL)
  		{
  		aSessions.AppendL(static_cast<CAgnServerSession*>(iSessionIter++));
  		}
  	}

TAgnServerMode CAgnServer::ServerMode()
	{ 
	return iServerMode; 
	}
	
const CAgnServBackupRestoreAgent& CAgnServer::BackupRestoreAgent() const
	{
	return *iBackupRestoreAgent;	
	}

RASCliSession& CAgnServer::AlarmServer()
    {
    return iAlarmServer;    
    }

#ifdef SYMBIAN_SYSTEM_STATE_MANAGEMENT
const CCAgnSystemStateObserver& CAgnServer::SystemStateObserver() const
    {
    return *iSystemStateObserver;
    }
#endif
