// 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:
//
#include "connect/sbdefs.h" // For conn::EBURNormal etc.
#include <utf.h> // For CnvUtfConverter::ConvertFromUnicodeToUtf8().
#include "CCntBackupRestoreAgent.h"
#include "CIniFileManager.h"
#include "CCntDbManagerController.h"
#include "CCntServer.h" // For KCntNullConnectionId.
#include "CCntLogger.h"
const TInt CCntBackupRestoreAgent::KMaxFileNameLength = 256;
_LIT(KSqLiteFilePrefix, "SQLite__");
/** Object factory method for CCntBackupRestoreAgent.
*/
CCntBackupRestoreAgent* CCntBackupRestoreAgent::NewL(RFs& aFs, CCntDbManagerController& aDbMgrCtrlr)
{
CCntBackupRestoreAgent* self = new(ELeave) CCntBackupRestoreAgent(aFs, aDbMgrCtrlr);
return self;
}
/** Destructor.
*/
CCntBackupRestoreAgent::~CCntBackupRestoreAgent()
{
Cancel();
iBackupRestoreNotification.Close();
}
/** Process any initial notification and start monitoring for changes in the
Backup/Restore property used by the SBEngine component.
*/
void CCntBackupRestoreAgent::StartL()
{
TInt newState = 0;
// Check to see if a Backup/Restore is currently in progress.
if (iBackupRestoreNotification.Get(newState) != KErrNotFound)
{
ProcessInitialStateL(newState);
}
SetActive();
}
/** First phase default constructor.
*/
CCntBackupRestoreAgent::CCntBackupRestoreAgent(RFs& aFs, CCntDbManagerController& aDbMgrCtrlr)
: CActive(CActive::EPriorityStandard), iFs(aFs), iDbMgrCtrlr(aDbMgrCtrlr)
{
CActiveScheduler::Add(this);
iEvent.iType = EContactDbObserverEventNull; // Changed as required.
iEvent.iContactId = KNullContactId; // Always this value.
iEvent.iConnectionId = KCntNullConnectionId; // Always this value.
// Attach and subscribe to the Backup/Restore property used by the SBEngine
// component.
iBackupRestoreNotification.Attach(KUidSystemCategory, conn::KUidBackupRestoreKey);
iBackupRestoreNotification.Subscribe(iStatus);
}
/** Indicates if a Backup operation is currently in progress.
@return ETrue if operation in progress else EFalse.
*/
TBool CCntBackupRestoreAgent::BackupInProgress() const
{
return (iCurrentState & (conn::EBURBackupPartial | conn::EBURBackupFull));
}
/** Indicates if a Restore operation is currently in progress.
@return ETrue if operation in progress else EFalse.
*/
TBool CCntBackupRestoreAgent::RestoreInProgress() const
{
return (iCurrentState & (conn::EBURRestorePartial | conn::EBURRestoreFull));
}
/** Create a new XML backup registration file for the file aFileName. The file
will be created on the same drive as aFileName in the Contacts Model private
data directory.
@param aFileName File name including drive letter i.e. <drive>:<file>.
*/
void CCntBackupRestoreAgent::CreateBackupRegistrationFileL(const TDesC& aFileName)
{
_LIT8(KXmlFilePart1, "<?xml version=\"1.0\" standalone=\"yes\"?>\r\n"
"<backup_registration>\r\n"
" <passive_backup base_backup_only=\"yes\">\r\n"
" <include_file name=\"");
_LIT8(KXmlFilePart2, "\"/>\r\n"
" </passive_backup>\r\n"
"</backup_registration>\r\n");
// Find out the drive held in aFileName. If a drive letter is not present
// then the method leaves.
TParsePtrC parseFileName(aFileName);
User::LeaveIfError(parseFileName.DrivePresent());
TPath privatePath;
User::LeaveIfError(iFs.PrivatePath(privatePath));
User::LeaveIfError(iFs.SetSessionPath(parseFileName.Drive()));
User::LeaveIfError(iFs.SetSessionPath(privatePath));
// If the path does not exist create it.
TInt err = iFs.MkDirAll(privatePath);
if (err != KErrAlreadyExists && err != KErrNone)
{
User::Leave(err);
}
HBufC* newFileName = CreateBackupRegistrationFileNameLC(parseFileName.NameAndExt());
TPtr newFileNamePtr(newFileName->Des());
// Create registration file.
RFile file;
CleanupClosePushL(file);
err = file.Create(iFs, *newFileName, EFileWrite);
// Registration file already exists so we're done.
if (err == KErrAlreadyExists)
{
CleanupStack::PopAndDestroy(2, newFileName); // file, newFileName
return;
}
if (err)
{
User::Leave(err);
}
HBufC* nameAndExt = NULL;
nameAndExt = HBufC::NewLC(KSqLiteFilePrefix().Length() + parseFileName.NameAndExt().Length());
if (parseFileName.NameAndExt().Compare(KContactsIniFileName) != 0)
{
nameAndExt->Des().Append(KSqLiteFilePrefix);
}
nameAndExt->Des().Append(parseFileName.NameAndExt());
// Convert filename and extension to UTF8 before writing to file.
HBufC8* fileNameAndExt8 = HBufC8::NewLC(nameAndExt->Length());
TPtr8 pFileNameAndExt8(fileNameAndExt8->Des());
User::LeaveIfError(CnvUtfConverter::ConvertFromUnicodeToUtf8(pFileNameAndExt8,
*nameAndExt));
// Write data into file.
User::LeaveIfError(file.Write(KXmlFilePart1()));
User::LeaveIfError(file.Write(pFileNameAndExt8));
User::LeaveIfError(file.Write(KXmlFilePart2()));
User::LeaveIfError(file.Flush());
CleanupStack::PopAndDestroy(4, newFileName); // fileNameAndExt8, file, nameAndExt, newFileName
}
/** Delete the XML backup registration file associated with the filename. The
registration file will be found on the same drive as aFileName.
@param aFileName File name including drive letter i.e. <drive>:<file>.
*/
void CCntBackupRestoreAgent::DeleteBackupRegistrationFileL(const TDesC& aFileName)
{
TParsePtrC parseFileName(aFileName);
// Find out the drive held in aFileName. If a drive letter is not present
// then the method leaves.
User::LeaveIfError(parseFileName.DrivePresent());
TPath privatePath;
User::LeaveIfError(iFs.PrivatePath(privatePath));
User::LeaveIfError(iFs.SetSessionPath(parseFileName.Drive()));
User::LeaveIfError(iFs.SetSessionPath(privatePath));
HBufC* regFileName = CreateBackupRegistrationFileNameLC(parseFileName.NameAndExt());
TPtrC regFileNamePtr(regFileName->Des());
// Check if registration file exists and if so delete it.
TEntry entry;
TInt err = iFs.Entry(regFileNamePtr, entry);
if((err == KErrNone) && !entry.IsDir())
{
User::LeaveIfError(iFs.Delete(regFileNamePtr));
}
CleanupStack::PopAndDestroy(regFileName);
}
/** Handle change in value of the property KUidBackupRestoreKey (used to notify
subscribers of Backup/Restore events).
@see CActive
*/
void CCntBackupRestoreAgent::RunL()
{
// Resubscribe before dealing with the current notification.
iBackupRestoreNotification.Subscribe(iStatus);
SetActive();
TInt newState = 0;
// Flag updated. Decide what to do in ReceivedNotificationL().
if (iBackupRestoreNotification.Get(newState) != KErrNotFound)
{
ReceivedNotificationL(newState);
}
}
/** Recover if RunL() leaves.
@see CActive
*/
TInt CCntBackupRestoreAgent::RunError(TInt /*aError*/)
{
DEBUG_PRINT1(__VERBOSE_DEBUG__,_L("[CNTMODEL] CCntBackupRestoreAgent::RunError(): RunL() has left!\n"));
return (KErrNone);
}
/**
@see CActive
*/
void CCntBackupRestoreAgent::DoCancel()
{
iBackupRestoreNotification.Cancel();
}
/** Determines what operation is starting or has completed and calls the
relevant observer function.
@param aNewState Flags indicating what the new Backup/Restore state is:
1. No Backup/Restore is taking place = EBURNormal | ENoBackup
2. Backup operation starting = (EBURBackupFull || EBURBackupPartial) &&
EBackupBase
3. Restore operation starting = (EBURRestoreFull || EBURRestorePartial) &&
EBackupBase
*/
void CCntBackupRestoreAgent::ReceivedNotificationL(TInt aNewState)
{
// We are starting a a new operation...
// Was there a previous Restore which failed to complete?
if (!(aNewState & (conn::EBURNormal | conn::EBURUnset)) && RestoreInProgress())
{
iCurrentState = aNewState;
RestoreCompletedL(KErrAbort);
}
// A Base Backup or Restore taking place.
if (aNewState & (conn::EBURBackupPartial | conn::EBURBackupFull) )
{
// Backup is taking place (either partial or full)
iCurrentState = aNewState;
BackupBeginningL();
}
else if (aNewState & (conn::EBURRestorePartial | conn::EBURRestoreFull))
{
// Restore is taking place (either partial or full)
iCurrentState = aNewState;
RestoreBeginningL();
}
else if (aNewState & (conn::EBURNormal | conn::EBURUnset))
{
// The state has changed to no Backup/Restore. Decide which operation
// has just completed.
if (BackupInProgress())
{
iCurrentState = aNewState;
BackupCompletedL();
}
else if (RestoreInProgress())
{
iCurrentState = aNewState;
RestoreCompletedL(KErrNone);
}
}
}
void CCntBackupRestoreAgent::ProcessInitialStateL(TInt aNewState)
{
// A Base Backup or Restore is taking place.
if (aNewState & (conn::EBURBackupPartial | conn::EBURBackupFull) )
{
// Backup is taking place (either partial or full).
BackupBeginningL();
}
else if (aNewState & (conn::EBURRestorePartial | conn::EBURRestoreFull))
{
// Restore is taking place (either partial or full).
RestoreBeginningL();
}
iCurrentState = aNewState;
}
void CCntBackupRestoreAgent::BackupBeginningL()
{
iEvent.iType = EContactDbObserverEventBackupBeginning;
iDbMgrCtrlr.HandleBackupRestoreEventL(iEvent);
iDbMgrCtrlr.IniFileManager().SaveIniFileSettingsIfRequestedL();
}
void CCntBackupRestoreAgent::BackupCompletedL()
{
iEvent.iType = EContactDbObserverEventBackupRestoreCompleted;
iDbMgrCtrlr.HandleBackupRestoreEventL(iEvent);
// In case Store has been waiting for backup to finish. Now attempt to
// perform deferred operation.
iDbMgrCtrlr.IniFileManager().RetryStoreOperation();
}
void CCntBackupRestoreAgent::RestoreBeginningL()
{
iEvent.iType = EContactDbObserverEventRestoreBeginning;
iDbMgrCtrlr.HandleBackupRestoreEventL(iEvent);
iDbMgrCtrlr.IniFileManager().SaveIniFileSettingsIfRequestedL();
}
void CCntBackupRestoreAgent::RestoreCompletedL(TInt aRestoreResult)
{
if (aRestoreResult == KErrNone)
{
iDbMgrCtrlr.IniFileManager().StartRestoreIniFileSettings();
iEvent.iType = EContactDbObserverEventBackupRestoreCompleted;
iDbMgrCtrlr.HandleBackupRestoreEventL(iEvent);
}
else
{
iEvent.iType = EContactDbObserverEventRestoreBadDatabase;
iDbMgrCtrlr.HandleBackupRestoreEventL(iEvent);
// A restore of the Contacts Model initialisation file failed so save
// initialisation file.
iDbMgrCtrlr.IniFileManager().ScheduleSaveIniFileSettings(CIniFileManager::ESaveAll);
}
}
/** Create an XML backup registration file name using aFileName.
@param aFileName File name without drive letter.
*/
HBufC* CCntBackupRestoreAgent::CreateBackupRegistrationFileNameLC(const TDesC& aFileName)
{
_LIT(KContactsBackupFileName, "backup_registration");
_LIT(KContactsBackupFileExtension, ".xml");
const TInt newFileNameLength = KContactsBackupFileName().Length() +
KSqLiteFilePrefix().Length() +
aFileName.Length() + KContactsBackupFileExtension().Length();
if (newFileNameLength > KMaxFileNameLength)
{
User::Leave(KErrArgument);
}
HBufC* newFileName = HBufC::NewL(newFileNameLength);
CleanupStack::PushL(newFileName);
TPtr newFileNamePtr(newFileName->Des());
// New filename will be of the form backup_registration<aFileName>.xml.
// If aFileName has an extension then the '.' will be replaced with '_'.
newFileNamePtr.Append(KContactsBackupFileName);
// if the it is the cntModel.ini file
// then do not append it in the file name
// this ensures that there is one file
// with the name backup_registration.xml file
// which is required for the Backup & Restore engine.
if (aFileName.Compare(KContactsIniFileName) != 0)
{
newFileNamePtr.Append(KSqLiteFilePrefix);
newFileNamePtr.Append(aFileName);
}
TInt dotPos = newFileNamePtr.LocateReverse('.');
if (dotPos != KErrNotFound )
{
newFileNamePtr[dotPos] = '_';
}
newFileNamePtr.Append(KContactsBackupFileExtension);
return newFileName;
}