/*
* Copyright (c) 2004 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 CFavouritesSrvDb
*
*
*/
// INCLUDE FILES
#include <s32std.h>
#include <eikenv.h>
#include <rfsApMapper.h>
#include <commdb.h>
#include "FavouritesSrvDb.h"
#include "FavouritesSrvTable.h"
#include "FavouritesItemImpl.h"
#include "FavouritesItemImplList.h"
#include "FavouritesPanic.h"
#include "favouriteslimits.h"
#include "favouritesitemdata.h"
#include "FavouritesFilter.h"
#include "UidMap.h"
#include "FavouritesLogger.h"
#include "FavouritesFolder.h"
// CONSTANTS
/// Uid list granularity.
LOCAL_D const TInt KUidListGranularity = 4;
/// Postfix list granularity.
LOCAL_D const TInt KPostfixListGranularity = 4;
/// Number of updates needed to trigger automatic compaction.
LOCAL_D const TInt KUpdatesNeededForAutoCompact = 32;
/// Length of filename: 8 char.
LOCAL_D const TInt KFnameLength = 8;
// Secure policy ID of the Faveng database files.
LOCAL_D const TUint KUidFavengDbPolicy = 0x101FD685;
// ==================== LOCAL FUNCTIONS ====================
/**
* Given aName in the format <prefix> or <prefix><integer in parenthesis>,
* return a pointer to the leading part. That is, if there is trailing
* <integer in parenthesis>, then that is excluded; if there is no trailing
* part, then the original decriptor is returned.
* Examples:
* - "Foo" returns "Foo";
* - "Foo(12)" returns "Foo";
* - "Foo(12 34)" returns "Foo(12 34)";
*
* @param aName Name to get prefix from.
* @return Pointer to prefix part.
*/
LOCAL_C TPtrC GetPrefix( const TDesC& aName )
{
TPtrC prefix = aName;
TInt length = aName.Length();
if ( length >= 3 && aName[length - 1] == ')' )
{
// Any name shorter than 3 chars cannot have a postfix.
TInt openPar = aName.LocateReverse('(');
if ( openPar != KErrNotFound )
{
// aName looks like "<prefix>(<something>)".
// See if <something> is an integer number.
TPtrC num = aName.Mid( openPar + 1, length - openPar - 2);
TInt val;
TLex lex( num );
if ( lex.Val( val ) == KErrNone )
{
// Yes, the trailer is a parenthesized integer.
prefix.Set( aName.Left( openPar ) );
}
}
}
return prefix;
}
/**
* If aName is constructed from aPrefix with a positive integer postfix,
* get the numeric value of the postfix, e.g:
* - GetPostfix( "Foo(3)", "Foo" ) == 3
* If aName is the same as aPrefix, return 0, e.g.:
* - GetPostfix( "Foo", "Foo" ) == 0
* If aName is not constructed from aPrefix, return -1, e.g.:
* - GetPostfix( "Foobar", "Foo" ) == -1
* - GetPostfix( "Foo(23 45)", "Foo" ) == -1
* If postfix is 0 or negative, return -1, e.g.:
* - GetPostfix( "Foo(00)", "Foo" ) == -1
* - GetPostfix( "Foo(-8)", "Foo" ) == -1
*
* Return values for the following cases cannot be differentiated (-1 for all):
* - postfix is 0;
* - postfix is negative;
* - aName not constructed from aPrefix.
*
* @param aName Name to get postfix from.
* @param aPrefix Prefix to be searched.
* @return Postfix value.
*/
LOCAL_C TInt GetPostfix( const TDesC& aName, const TDesC& aPrefix )
{
TInt postfix = -1;
TInt nameLength = aName.Length();
TInt prefixLength = aPrefix.Length();
if ( nameLength >= prefixLength && aName.FindF( aPrefix ) == 0 )
{
// aName is longer or equal length, and
// aPrefix can be found in the beginning of aName.
if ( nameLength == prefixLength )
{
// They have the same length; they equal.
postfix = 0;
}
else if (
nameLength - prefixLength >= 3 && // Because of this...
aName[prefixLength] == '(' &&
aName[nameLength - 1] == ')' // ... this is safe (*)
)
{
// (*) "aName[nameLength - 1]" no need to check whether this
// index is valid. nameLength is at least 3 characters longer
// than prefixLength (a positive integer) -> nameLength >= 3.
// aName looks like "aPrefix(<something>)".
// See if <something> is an integer number.
TPtrC num = aName.Mid
( prefixLength + 1, nameLength - prefixLength - 2 ); // (**)
// (**) These are also safe because nameLength - prefixLength >= 3.
TInt val;
TLex lex( num );
if ( lex.Val( val ) == KErrNone && val > 0)
{
// Yes, the trailer is a positive integer.
postfix = val;
}
}
}
return postfix;
}
/**
* Generate filename for uid: return number as 8 character hex string.
* @param aUid Uid.
* @param aName Filename returned here. Minimum length KFnameLength.
*/
LOCAL_C void FilenameForUid( TInt aUid, TDes& aName )
{
__ASSERT_DEBUG( aName.MaxLength() >= KFnameLength, \
FavouritesPanic( EFavouritesInternal ) );
aName.NumFixedWidth( STATIC_CAST( TUint, aUid ), EHex, KFnameLength );
}
/**
* Get uid for filename; reverse of FilenameForUid.
* @param aName Filename.
* @return Uid or KFavouritesNullUid.
*/
LOCAL_C TInt UidForFilename( const TDesC& aName )
{
// Parse the supplied text as hex number.
TInt uid = KFavouritesNullUid;
if ( aName.Length() == KFnameLength )
{
// Length is OK, check contents.
TUint val;
TLex lex( aName );
if ( !lex.Val( val, EHex ) && lex.Eos() )
{
// OK, this filename can be parsed as hex TUint.
uid = STATIC_CAST( TInt, val );
}
}
return uid;
}
// ================= MEMBER FUNCTIONS =======================
// ---------------------------------------------------------
// CBookmarkSrvDb::NewL
// ---------------------------------------------------------
//
CFavouritesSrvDb* CFavouritesSrvDb::NewL
( RFs& aFs, RDbs& aDbs, const TDesC& aPath, const TDesC& aDBName )
{
CFavouritesSrvDb* db = new (ELeave) CFavouritesSrvDb( aFs, aDbs );
CleanupStack::PushL( db );
db->ConstructL( aPath , aDBName );
CleanupStack::Pop(); // db
return db;
}
// ---------------------------------------------------------
// CFavouritesSrvDb::~CFavouritesSrvDb
// ---------------------------------------------------------
//
CFavouritesSrvDb::~CFavouritesSrvDb()
{
if ( iOpen )
{
if ( iInTransaction )
{
Rollback();
}
TRAP_IGNORE( DeleteOrphanedFilesL() );
// TODO move compacting to server side, when last client exits.
(void)iDatabase.Compact(); // Any error is silently ignored.
iDatabase.Close();
iOpen = EFalse;
}
delete iPath;
delete iSavedFilePath;
iSecureDbFormat.Close();
}
// ---------------------------------------------------------
// CFavouritesSrvDb::IsDamagedL
// ---------------------------------------------------------
//
TBool CFavouritesSrvDb::IsDamagedL()
{
TBool damaged = EFalse;
// We must open a table to make RDbNameDatabase::IsDamaged() work!.
RFavouritesSrvTable table;
table.OpenL( iDatabase );
damaged = iDatabase.IsDamaged();
table.Close();
return damaged;
}
// ---------------------------------------------------------
// CFavouritesSrvDb::RecoverL
// ---------------------------------------------------------
//
void CFavouritesSrvDb::RecoverL()
{
User::LeaveIfError( iDatabase.Recover() );
}
// ---------------------------------------------------------
// CFavouritesSrvDb::CompactL
// ---------------------------------------------------------
//
void CFavouritesSrvDb::CompactL()
{
User::LeaveIfError( iDatabase.Compact() );
iUpdatesSinceLastCompact = 0;
}
// ---------------------------------------------------------
// CFavouritesSrvDb::Size
// ---------------------------------------------------------
//
RDbDatabase::TSize CFavouritesSrvDb::Size() const
{
return iDatabase.Size();
}
// ---------------------------------------------------------
// CFavouritesSrvDb::UpdateStatsL
// ---------------------------------------------------------
//
void CFavouritesSrvDb::UpdateStatsL()
{
User::LeaveIfError( iDatabase.UpdateStats() );
}
// ---------------------------------------------------------
// CFavouritesSrvDb::BeginL
// ---------------------------------------------------------
//
void CFavouritesSrvDb::BeginL( TBool aWrite /*=EFalse*/ )
{
__ASSERT_DEBUG( !iInTransaction, \
FavouritesPanic( EFavouritesNestedTransaction ) );
// Three parts: table open + transacion + (optional) write lock.
// Temp use of cleanup items make the three atomic.
iTable.OpenL( iDatabase );
CleanupClosePushL<RFavouritesSrvTable>( iTable );
User::LeaveIfError( iDatabase.Begin() );
iInTransaction = ETrue;
CleanupStack::PushL( TCleanupItem( StaticRollback, this ) );
if ( aWrite )
{
iTable.PutWriteLockL();
}
CleanupStack::Pop( 2 ); // Rollback, Closing iTable
}
// ---------------------------------------------------------
// CFavouritesSrvDb::CommitL
// ---------------------------------------------------------
//
void CFavouritesSrvDb::CommitL()
{
__ASSERT_DEBUG( iInTransaction, \
FavouritesPanic( EFavouritesNoTransaction ) );
User::LeaveIfError( iDatabase.Commit() );
iInTransaction = EFalse;
iTable.Close();
if ( iUpdatesSinceLastCompact > KUpdatesNeededForAutoCompact )
{
// Auto-compact, to avoid overgrowth.
TRAP_IGNORE( CompactL() );
}
}
// ---------------------------------------------------------
// CFavouritesSrvDb::Rollback
// ---------------------------------------------------------
//
void CFavouritesSrvDb::Rollback()
{
__ASSERT_DEBUG( iInTransaction, \
FavouritesPanic( EFavouritesNoTransaction ) );
iDatabase.Rollback();
iInTransaction = EFalse;
iTable.Close();
}
// ---------------------------------------------------------
// CFavouritesSrvDb::GetL
// ---------------------------------------------------------
//
void CFavouritesSrvDb::GetL( TInt aUid, CFavouritesItemImpl& aItem )
{
TBool ownTransaction;
BeginIfNeededL( /*aWrite=*/EFalse, ownTransaction );
iTable.GotoToUidL( aUid );
iTable.GetL();
iTable.ReadItemDataL( aItem );
CommitIfNeededL( ownTransaction );
}
// ---------------------------------------------------------
// CFavouritesSrvDb::GetAllL
// ---------------------------------------------------------
//
void CFavouritesSrvDb::GetAllL
( CFavouritesItemImplList& aItemList, const TFavouritesFilter& aFilter )
{
TBool ownTransaction;
BeginIfNeededL( /*aWrite=*/EFalse, ownTransaction );
iTable.SetFiltersL( aFilter );
CFavouritesItemImpl* item;
iTable.BeginningL();
while( iTable.NextL() )
{
item = CFavouritesItemImpl::NewLC();
iTable.GetL();
iTable.ReadItemDataL( *item );
aItemList.AppendL( item );
CleanupStack::Pop(); // item; owner is the list now.
}
iTable.ClearFilters();
CommitIfNeededL( ownTransaction );
}
// ---------------------------------------------------------
// CFavouritesSrvDb::PreferredUidL
// ---------------------------------------------------------
//
TInt CFavouritesSrvDb::PreferredUidL( TInt aFolder )
{
TInt uid( KFavouritesNullUid );
TBool ownTransaction;
BeginIfNeededL( /*aWrite=*/EFalse, ownTransaction );
if ( iTable.SeekToUidL( aFolder ) )
{
iTable.GetL();
if ( iTable.Type() == CFavouritesItem::EFolder )
{
uid = iTable.PreferredUid();
}
}
CommitIfNeededL( ownTransaction );
return uid;
}
// ---------------------------------------------------------
// CFavouritesSrvDb::GetUidsL
// ---------------------------------------------------------
//
void CFavouritesSrvDb::GetUidsL
( CArrayFix<TInt>& aUids, const TFavouritesFilter& aFilter )
{
TBool ownTransaction;
BeginIfNeededL( /*aWrite=*/EFalse, ownTransaction );
iTable.SetFiltersL( aFilter );
iTable.BeginningL();
while( iTable.NextL() )
{
iTable.GetL();
aUids.AppendL( iTable.Uid() );
}
iTable.ClearFilters();
CommitIfNeededL( ownTransaction );
}
// ---------------------------------------------------------
// CFavouritesSrvDb::DeleteL
// ---------------------------------------------------------
//
void CFavouritesSrvDb::DeleteL( TInt aUid )
{
if ( aUid == KFavouritesRootUid || aUid == KFavouritesHomepageUid )
{
// Cannot delete these.
User::Leave( KErrAccessDenied );
}
// Make a list, to remember which items were deleted.
CArrayFix<TInt>* deletedUids =
new (ELeave) CArrayFixFlat<TInt>( KUidListGranularity );
CleanupStack::PushL( deletedUids );
TBool ownTransaction;
BeginIfNeededL( /*aWrite=*/ETrue, ownTransaction );
iTable.GotoToUidL( aUid );
DeleteCurrentL( *deletedUids );
CommitIfNeededL( ownTransaction );
// Delete associated files.
TInt i;
TBuf<KFnameLength> name;
TParse parse;
for ( i = 0; i < deletedUids->Count(); i++ )
{
FilenameForUid( deletedUids->At( i ), name );
User::LeaveIfError( parse.SetNoWild( name, iPath, NULL ) );
(void)iFs.Delete( parse.FullName() );
// Errors ignored; the file may be locked etc. etc.
// Orphaned file cleanup will remove them eventually.
}
CleanupStack::PopAndDestroy(); // deletedUids
}
// ---------------------------------------------------------
// CFavouritesSrvDb::UpdateL
// ---------------------------------------------------------
//
void CFavouritesSrvDb::UpdateL
( CFavouritesItemImpl& aItem, TInt aUid, TBool aAutoRename )
{
if ( aUid == KFavouritesRootUid ||
aUid == KFavouritesHomepageUid ||
aUid == KFavouritesLastVisitedUid )
{
// Cannot modify these.
// (Root cannot be modified at all; the others can be modified but
// not through this method.)
User::Leave( KErrAccessDenied );
}
TBool ownTransaction;
BeginIfNeededL( /*aWrite=*/ETrue, ownTransaction );
iTable.GotoToUidL( aUid );
UpdateCurrentL( aItem, aAutoRename ? EAutoRename : EDontRename );
CommitIfNeededL( ownTransaction );
}
// ---------------------------------------------------------
// CFavouritesSrvDb::AddL
// ---------------------------------------------------------
//
void CFavouritesSrvDb::AddL( CFavouritesItemImpl& aItem, TBool aAutoRename )
{
TBool ownTransaction;
BeginIfNeededL( /*aWrite=*/ETrue, ownTransaction );
DoAddL( aItem, aAutoRename ? EAutoRename : EDontRename );
CommitIfNeededL( ownTransaction );
}
// ---------------------------------------------------------
// CFavouritesSrvDb::SetSpecialItemL
// ---------------------------------------------------------
//
void CFavouritesSrvDb::SetSpecialItemL( CFavouritesItemImpl& aItem, TInt aUid )
{
__ASSERT_DEBUG( \
aUid == KFavouritesHomepageUid || aUid == KFavouritesLastVisitedUid, \
FavouritesPanic( EFavouritesInternal )
);
if ( aItem.ParentFolder() != KFavouritesRootUid )
{
// Must be in root.
User::Leave( KErrArgument );
}
TBool ownTransaction;
BeginIfNeededL( /*aWrite=*/ETrue, ownTransaction );
if ( iTable.SeekToUidL( aUid ) )
{
UpdateCurrentL( aItem, ESuppressCheck );
}
else
{
DoAddL( aItem, ESuppressCheck );
// Cursor is still on the new record. Write uid back.
iTable.UpdateLC();
iTable.SetUidL( aUid );
iTable.PutL();
aItem.SetUid( aUid ); // Get uid back.
aItem.SetModified( iTable.Modified() ); // Get modtime.
}
CommitIfNeededL( ownTransaction );
}
// ---------------------------------------------------------
// CFavouritesSrvDb::SetFactoryItemL
// ---------------------------------------------------------
//
void CFavouritesSrvDb::SetFactoryItemL( TInt aUid, TBool aFactoryItem )
{
TBool ownTransaction;
BeginIfNeededL( /*aWrite=*/ETrue, ownTransaction );
iTable.GotoToUidL( aUid );
iTable.UpdateLC();
iTable.SetFactoryItemL( aFactoryItem );
iTable.PutL();
CommitIfNeededL( ownTransaction );
}
// ---------------------------------------------------------
// CFavouritesSrvDb::SetReadOnlyL
// ---------------------------------------------------------
//
void CFavouritesSrvDb::SetReadOnlyL( TInt aUid, TBool aReadOnly )
{
TBool ownTransaction;
BeginIfNeededL( /*aWrite=*/ETrue, ownTransaction );
iTable.GotoToUidL( aUid );
iTable.UpdateLC();
iTable.SetReadOnlyL( aReadOnly );
iTable.PutL();
CommitIfNeededL( ownTransaction );
}
// ---------------------------------------------------------
// CFavouritesSrvDb::SetModifiedL
// ---------------------------------------------------------
//
void CFavouritesSrvDb::SetModifiedL( TInt aUid, TTime aModified )
{
TBool ownTransaction;
BeginIfNeededL( /*aWrite=*/ETrue, ownTransaction );
iTable.GotoToUidL( aUid );
iTable.UpdateLC();
iTable.SetModifiedL( aModified );
iTable.PutL( /*aTouch=*/ EFalse );
CommitIfNeededL( ownTransaction );
}
// ---------------------------------------------------------
// CFavouritesSrvDb::SetPreferredUidL
// ---------------------------------------------------------
//
void CFavouritesSrvDb::SetPreferredUidL( TInt aFolder, TInt aUid )
{
TBool ownTransaction;
BeginIfNeededL( /*aWrite=*/ETrue, ownTransaction );
iTable.GotoToUidL( aFolder );
iTable.GetL();
if ( iTable.Type() == CFavouritesItem::EFolder )
{
iTable.UpdateLC();
iTable.SetPreferredUidL( aUid );
iTable.PutL();
}
else
{
User::Leave( KErrArgument );
}
CommitIfNeededL( ownTransaction );
}
// ---------------------------------------------------------
// CFavouritesSrvDb::ItemExistsL
// ---------------------------------------------------------
//
TBool CFavouritesSrvDb::ItemExistsL( TInt aUid )
{
TBool ownTransaction;
BeginIfNeededL( /*aWrite=*/EFalse, ownTransaction );
TBool res = iTable.SeekToUidL( aUid );
CommitIfNeededL( ownTransaction );
return res;
}
// ---------------------------------------------------------
// CFavouritesSrvDb::FolderExistsL
// ---------------------------------------------------------
//
TBool CFavouritesSrvDb::FolderExistsL( TInt aFolder )
{
TBool res = EFalse;
TBool ownTransaction;
BeginIfNeededL( /*aWrite=*/EFalse, ownTransaction );
if ( iTable.SeekToUidL( aFolder ) )
{
iTable.GetL();
if ( iTable.Type() == CFavouritesItem::EFolder )
{
res = ETrue;
}
}
CommitIfNeededL( ownTransaction );
return res;
}
// ---------------------------------------------------------
// CFavouritesSrvDb::CountL
// ---------------------------------------------------------
//
TInt CFavouritesSrvDb::CountL( const TFavouritesFilter& aFilter )
{
TInt count = 0;
TBool ownTransaction;
BeginIfNeededL( /*aWrite=*/EFalse, ownTransaction );
iTable.SetFiltersL( aFilter );
// Get all items in the aFolder.
iTable.BeginningL();
while( iTable.NextL() )
{
count++;
}
iTable.ClearFilters();
CommitIfNeededL( ownTransaction );
return count;
}
// ---------------------------------------------------------
// CFavouritesSrvDb::SetDataL
// ---------------------------------------------------------
//
void CFavouritesSrvDb::SetDataL( TInt aUid, MStreamBuf& aSource )
{
TBool ownTransaction;
BeginIfNeededL( /*aWrite=*/ETrue, ownTransaction );
iTable.GotoToUidL( aUid );
iTable.UpdateLC();
iTable.SetExtraDataL( aSource );
iTable.PutL();
CommitIfNeededL( ownTransaction );
}
// ---------------------------------------------------------
// CFavouritesSrvDb::GetDataL
// ---------------------------------------------------------
//
void CFavouritesSrvDb::GetDataL( TInt aUid, MStreamBuf& aSink )
{
TBool ownTransaction;
BeginIfNeededL( /*aWrite=*/EFalse, ownTransaction );
iTable.GotoToUidL( aUid );
iTable.GetL();
iTable.GetExtraDataL( aSink );
CommitIfNeededL( ownTransaction );
}
// ---------------------------------------------------------
// CFavouritesSrvDb::SetBrowserDataL
// ---------------------------------------------------------
//
void CFavouritesSrvDb::SetBrowserDataL( TInt aUid, MStreamBuf& aSource )
{
TBool ownTransaction;
BeginIfNeededL( /*aWrite=*/ETrue, ownTransaction );
iTable.GotoToUidL( aUid );
iTable.UpdateLC();
iTable.SetBrowserDataL( aSource );
iTable.PutL();
CommitIfNeededL( ownTransaction );
}
// ---------------------------------------------------------
// CFavouritesSrvDb::GetBrowserDataL
// ---------------------------------------------------------
//
void CFavouritesSrvDb::GetBrowserDataL( TInt aUid, MStreamBuf& aSink )
{
TBool ownTransaction;
BeginIfNeededL( /*aWrite=*/EFalse, ownTransaction );
iTable.GotoToUidL( aUid );
iTable.GetL();
iTable.GetBrowserDataL( aSink );
CommitIfNeededL( ownTransaction );
}
// ---------------------------------------------------------
// CFavouritesSrvDb::MakeUniqueNameL
// ---------------------------------------------------------
//
void CFavouritesSrvDb::MakeUniqueNameL( TDes& aName, TInt aFolder )
{
// Trim aName first.
HBufC* buf = HBufC::NewLC( aName.Length() + KFavouritesMaxPostfix );
TPtr ptr( buf->Des() );
CFavouritesItemImpl::MakeName( aName, ptr );
if ( !CFavouritesItemImpl::IsValidName( ptr ) )
{
User::Leave( KErrBadName ); // aName is invalid.
}
TBool ownTransaction;
BeginIfNeededL( /*aWrite=*/EFalse, ownTransaction );
if ( !FolderExistsL( aFolder ) )
{
User::Leave( KErrArgument ); // No parent folder.
}
if ( !IsUniqueNameL( ptr, aFolder, KFavouritesNullUid ) )
{
MakeUniqueNameL( ptr, aFolder, KFavouritesNullUid );
}
// Always set it back, even if it was unique in the first place:
// It may be trimmed.
aName = ptr;
CommitIfNeededL( ownTransaction );
CleanupStack::PopAndDestroy(); // buf
}
// ---------------------------------------------------------
// CFavouritesSrvDb::RestoreFactorySettingsL
// ---------------------------------------------------------
//
void CFavouritesSrvDb::RestoreFactorySettingsL
( const TDesC& aReferenceDbPath, CUidMap& aApMap )
{
__ASSERT_DEBUG( InTransaction(), FavouritesPanic( EFavouritesInternal ) );
FLOG(( _L("restoer: FavoritesSvrDb.cpp CFavouritesSrvDb::RestoreFactorySettingsL") ));
// Open a read-only database on the reference db (client-side access).
RDbNamedDatabase refDb;
FLOG(( _L("CFavouritesSrvDb::RestoreFactorySettingsL before Leave call") ));
RFs fsSession;
User::LeaveIfError(fsSession.Connect());
CleanupClosePushL(fsSession);
User::LeaveIfError(refDb.Open(fsSession, aReferenceDbPath, TPtrC(), RDbNamedDatabase::EReadOnly));
FLOG(( _L("CFavouritesSrvDb::RestoreFactorySettingsL after Leave call") ));
CleanupClosePushL<RDbNamedDatabase>( refDb );
// Open a read-only table on the database.
RFavouritesSrvTable refTable;
refTable.OpenL( refDb, RDbRowSet::EReadOnly );
CleanupClosePushL<RFavouritesSrvTable>( refTable );
// No transaction needed - the database is opened read-only
// with exclusive access.
RestoreFactorySettingsL( refTable, aApMap );
CleanupStack::PopAndDestroy( 2 ); // close refTable, close refDb
CleanupStack::PopAndDestroy(); // close fsSession
}
// ---------------------------------------------------------
// CFavouritesSession::InitSavedFilePathL
// ---------------------------------------------------------
//
void CFavouritesSrvDb::InitSavedFilePathL( const TDesC& aDBName )
{
TParse parse;
TDriveName cDrive = TDriveUnit( EDriveC ).Name();
TFileName path;
User::LeaveIfError( iFs.PrivatePath( path ) );
User::LeaveIfError( parse.SetNoWild( path, &cDrive, NULL ) );
parse.AddDir(aDBName);
delete iSavedFilePath;
iSavedFilePath = NULL;
iSavedFilePath = parse.FullName().AllocL();
iFs.CreatePrivatePath(EDriveC);
iFs.MkDirAll(*iSavedFilePath);
}
// ---------------------------------------------------------
// CFavouritesSrvDb::FilenameL
// ---------------------------------------------------------
//
void CFavouritesSrvDb::FilenameL( TInt aUid, TParse& aParse )
{
// Check if item exists.
TBool ownTransaction;
BeginIfNeededL( /*aWrite=*/EFalse, ownTransaction );
iTable.GotoToUidL( aUid );
CommitIfNeededL( ownTransaction );
// Generate filename.
TBuf<KFnameLength> name;
FilenameForUid( aUid, name );
// Append filename to session path.
User::LeaveIfError( aParse.SetNoWild( name, iSavedFilePath, NULL ) );
}
// ---------------------------------------------------------
// CFavouritesSrvDb::CFavouritesSrvDb
// ---------------------------------------------------------
//
CFavouritesSrvDb::CFavouritesSrvDb( RFs& aFs, RDbs& aDbs )
: iFs( aFs ),
iDbs( aDbs ),
iOpen( EFalse ),
iInTransaction( EFalse ),
iUpdatesSinceLastCompact( 0 )
{
}
// ---------------------------------------------------------
// CFavouritesSrvDb::ConstructL
// ---------------------------------------------------------
//
void CFavouritesSrvDb::ConstructL( const TDesC& aPath, const TDesC& aDBName )
{
const TUid KDbPolicyUid = TUid::Uid(KUidFavengDbPolicy);
iPath = aPath.AllocL();
InitSavedFilePathL(aDBName );
_LIT( KSecureKeyword, "SECURE" );
iSecureDbFormat.CreateL( KSecureKeyword(), KSecureKeyword().Length() +
KDbPolicyUid.Name().Length() );
iSecureDbFormat += KDbPolicyUid.Name();
// Check if we have the database.
TBool isDbExists = EFalse;
CDbDatabaseNames *dbNames = iDbs.DatabaseNamesL(EDriveC, KDbPolicyUid);
CleanupStack::PushL(dbNames);
TParse dbFile;
User::LeaveIfError( dbFile.SetNoWild( aPath, NULL, NULL ) );
for(int i = 0; i < dbNames->Count(); i++)
{
if( (*dbNames)[i] == dbFile.NameAndExt() )
{
isDbExists = ETrue;
break;
}
}
CleanupStack::PopAndDestroy(dbNames);
if ( !isDbExists )
{
// No such database. Make it now.
TInt err = KErrNone;
TBool doImport = ( aDBName.Compare( KBrowserBookmarks ) == 0 );
TRAP( err, CreateDatabaseL( dbFile, doImport ) );
// Bookmark import failed. Create an empty DB
if ( err && doImport )
{
iDbs.DeleteDatabase( aPath, KDbPolicyUid );
TRAP( err, CreateDatabaseL( dbFile, EFalse ) );
}
if ( err )
{
// Should anything go wrong during the database creation,
// delete the database. This ensures that we never have
// half-constructed database (e.g. database created but no
// root folder because of leave). No data is lost by deleting
// the file; it has just been created.
iDbs.DeleteDatabase( aPath, KDbPolicyUid );
User::Leave( err );
}
}
// Here we have the database, successfully created. It is closed now.
// Open it and make some repairs if needed.
TInt error;
error = iDatabase.Open( iDbs, dbFile.FullName(), iSecureDbFormat );
if (error != KErrNone)
{
User::Leave( error );
}
iOpen = ETrue;
if ( IsDamagedL() )
{
RecoverL();
}
// Verify structure and upgrade if needed.
RFavouritesSrvTable::VerifyStructureL( iDatabase, ETrue );
}
// ---------------------------------------------------------
// CFavouritesSrvDb::DeleteOrphanedFilesL
// ---------------------------------------------------------
//
void CFavouritesSrvDb::DeleteOrphanedFilesL()
{
// Get all uids and sort them by uid.
CArrayFix<TInt>* uids =
new (ELeave) CArrayFixFlat<TInt>( KUidListGranularity );
CleanupStack::PushL( uids );
GetUidsL( *uids, TFavouritesFilter() );
TKeyArrayFix key( 0, ECmpTInt32 );
uids->Sort( key );
// Get a directory listing.
RDir dir;
User::LeaveIfError( dir.Open( iFs, *iSavedFilePath, KEntryAttNormal ) );
CleanupClosePushL<RDir>( dir );
TEntryArray files;
TInt err = dir.Read( files );
if( err != KErrEof ) // KErrEof means all files read.
{
User::Leave( err );
}
// Now check if any of the files is orphaned.
TParse parse;
TInt dummy;
TInt uid;
TInt i;
const TInt KElementFound = 0;
for ( i = 0; i < files.Count(); i++ )
{
uid = UidForFilename( files[i].iName );
if ( uid == KFavouritesNullUid ||
uids->FindIsq( uid, key, dummy ) != KElementFound )
{
// The filename is in the required format (8 char hex number),
// but no item for this file -> an orphaned file.
User::LeaveIfError
( parse.SetNoWild( files[i].iName, iSavedFilePath, NULL ) );
(void)iFs.Delete( parse.FullName() );
}
}
CleanupStack::PopAndDestroy( 2, uids ); // dir, uids
}
// ---------------------------------------------------------
// CFavouritesSrvDb::BeginIfNeededL
// ---------------------------------------------------------
//
void CFavouritesSrvDb::BeginIfNeededL( TBool aWrite, TBool& aOwnTransaction )
{
aOwnTransaction = EFalse;
if ( !iInTransaction )
{
// Not in transaction yet.
BeginL( aWrite );
aOwnTransaction = ETrue;
CleanupStack::PushL( TCleanupItem( StaticRollback, this ) );
}
}
// ---------------------------------------------------------
// CFavouritesSrvDb::CommitIfNeeded
// ---------------------------------------------------------
//
void CFavouritesSrvDb::CommitIfNeededL( TBool aOwnTransaction )
{
if ( aOwnTransaction )
{
CommitL();
CleanupStack::Pop(); // StaticRollback, pushed in BeginL
}
}
// ---------------------------------------------------------
// CFavouritesSrvDb::CreateDatabaseL
// ---------------------------------------------------------
//
void CFavouritesSrvDb::CreateDatabaseL( TParse& aDbFile, TBool aDoImport )
{
// Make the database.
//
TInt errorCode = iDatabase.Create( iDbs, aDbFile.FullName(), iSecureDbFormat);
User::LeaveIfError( errorCode );
CleanupClosePushL<RDbNamedDatabase>( iDatabase );
// Create table.
RFavouritesSrvTable::CreateStructureL( iDatabase );
// Create the root folder. No need to close the database after schema
// modifications in ER5.
CreateRootFolderL();
// If the bookmark import file exists on the z: drive, re-import the bookmarks.
#ifndef __WINSCW__
_LIT(KBookmarkImportFile,"z:\\data\\BookmarkImportSample.txt"); // armv5
#else
_LIT(KBookmarkImportFile,"c:\\private\\10008d38\\BookmarkImportSample.txt"); // winscw
#endif
if( aDoImport )
{
TUint attImport;
User::LeaveIfError( iFs.Connect() );
TInt errImport = iFs.Att( KBookmarkImportFile(), attImport );
if ( errImport == KErrNone )
{
// Bookmark import file exists. Import to browserbookmarks.db database.
ImportL( KBookmarkImportFile() );
}
}
CleanupStack::PopAndDestroy(); // close iDatabase
}
// ---------------------------------------------------------
// CFavouritesSrvDb::CreateRootFolderL
// ---------------------------------------------------------
//
void CFavouritesSrvDb::CreateRootFolderL()
{
// Database just created; we have exclusive access.
iTable.OpenL( iDatabase );
CleanupClosePushL<RFavouritesSrvTable>( iTable );
CFavouritesItemImpl* root = CFavouritesItemImpl::NewLC();
// Add a new empty folder to the database.
root->SetType( CFavouritesItem::EFolder );
root->SetParentFolder( KFavouritesNullUid );
iTable.InsertLC();
iTable.WriteItemDataL( *root );
// Now the cursor is positioned over the new entry; change its Uid
// so it becomes the root folder.
iTable.SetUidL( KFavouritesRootUid );
iTable.PutL();
// Now we have the root. The first entry added came with 0 Uid, so this
// value will never be used again.
CleanupStack::PopAndDestroy( 2 ); // root, close iTable
}
// ---------------------------------------------------------
// CFavouritesSrvDb::DeleteCurrentL
// ---------------------------------------------------------
//
void CFavouritesSrvDb::DeleteCurrentL( CArrayFix<TInt>& aDeletedUids )
{
TInt uid;
iTable.GetL();
if( iTable.ReadOnly() )
{
User::Leave( KErrAccessDenied );
}
if ( iTable.Type() == CFavouritesItem::EFolder )
{
// This is a folder. Delete contents.
// Save cursor pos, since the cursor will be moved.
TDbBookmark originalPos = iTable.Bookmark();
iTable.SetFiltersL( TFavouritesFilter( iTable.Uid(),
CFavouritesItem::ENone, NULL, KFavouritesNullContextId ) );
iTable.BeginningL();
while ( iTable.NextL() )
{
iTable.GetL();
if( iTable.ReadOnly() )
{
// Read-only item in a non-read-only folder.
User::Leave( KErrAccessDenied );
}
else
{
// Get uid of item to be deleted
uid = iTable.Uid();
iTable.DeleteL();
aDeletedUids.AppendL( uid );
iUpdatesSinceLastCompact++;
}
}
// Restore cursor pos.
iTable.ClearFilters();
iTable.GotoL( originalPos );
iTable.GetL();
}
// Now delete the item itself. If it's a folder and there is no error
// so far, it is empty by now. Error indicates read-only item in this
// non-read-only folder, so keep the folder as well.
uid = iTable.Uid();
iTable.DeleteL();
aDeletedUids.AppendL( uid );
iUpdatesSinceLastCompact++;
}
// ---------------------------------------------------------
// CFavouritesSrvDb::UpdateCurrentL
// ---------------------------------------------------------
//
void CFavouritesSrvDb::UpdateCurrentL
( CFavouritesItemImpl& aItem, CFavouritesSrvDb::TRenameType aRenameType )
{
if ( !CFavouritesItemImpl::IsValidName( aItem.Name() ) )
{
// CFavouritesItemImpl::IsValidL also checks this; but we want to
// return different error code for this case, so manually check this
// first.
User::Leave( KErrBadName );
}
if ( !aItem.IsValid() )
{
User::Leave( KErrArgument );
}
HBufC* newName = NULL;
// Save cursor pos; parent folder and name checking will move it.
TDbBookmark originalPos = iTable.Bookmark();
iTable.GetL();
TInt oldParent = iTable.ParentFolder();
TInt uid = iTable.Uid();
if ( iTable.Type() != aItem.Type() )
{
// Cannot change type.
User::Leave( KErrArgument );
}
if ( iTable.ReadOnly() )
{
// Read-only item.
User::Leave( KErrAccessDenied );
}
if ( aItem.Type() == CFavouritesItem::EFolder &&
aItem.ParentFolder() != KFavouritesRootUid )
{
// Folders must be in the root folder
// (folder hierarchy is only one-level deep).
User::Leave( KErrArgument );
}
if ( oldParent != aItem.ParentFolder() &&
!FolderExistsL( aItem.ParentFolder() ) )
{
// No parent folder.
// (Not checked if the parent folder has not changed.)
User::Leave( KErrArgument );
}
if ( aRenameType != ESuppressCheck )
{
if ( !IsUniqueNameL( aItem.Name(), aItem.ParentFolder(), uid ) )
{
if ( aRenameType == EAutoRename )
{
// Name is conflicting, rename automatically.
newName = AllocUniqueNameLC
( aItem.Name(), aItem.ParentFolder(), uid );
}
else
{
User::Leave( KErrAlreadyExists );
}
}
}
// All is well so far; update is valid.
iTable.PutWriteLockL();
// If there is a new name, set it.
if ( newName )
{
aItem.SetNameL( *newName );
}
// Validation may have moved the cursor; set it back.
iTable.GotoL( originalPos );
iTable.UpdateLC(); // Prepare for update.
iTable.WriteItemDataL( aItem ); // Put the data.
iTable.SetFactoryItemL( EFalse );
iTable.PutL(); // Apply changes.
aItem.SetUid( iTable.Uid() ); // Get Uid.
aItem.SetModified( iTable.Modified() ); // Get modtime.
// Sanity check; it must be the specified Uid.
__ASSERT_DEBUG( aItem.Uid() == uid,
FavouritesPanic( EFavouritesInternal ) );
iUpdatesSinceLastCompact++;
if ( newName )
{
CleanupStack::PopAndDestroy(); // newName
}
}
// ---------------------------------------------------------
// CFavouritesSrvDb::DoAddL
// ---------------------------------------------------------
//
void CFavouritesSrvDb::DoAddL
(
CFavouritesItemImpl& aItem,
CFavouritesSrvDb::TRenameType aRenameType,
TBool aFactoryItem /*=EFalse*/,
TBool aReadOnly /*=EFalse*/
)
{
if ( !CFavouritesItemImpl::IsValidName( aItem.Name() ) )
{
// CFavouritesItemImpl::IsValidL also checks this; but we want to
// return different error code for this case, so manually check this
// first.
User::Leave( KErrBadName );
}
if ( !aItem.IsValid() )
{
User::Leave( KErrArgument );
}
HBufC* newName = NULL;
if ( aItem.Type() == CFavouritesItem::EFolder &&
aItem.ParentFolder() != KFavouritesRootUid )
{
// Folders must be in the root folder
// (folder hierarchy is only one-level deep).
User::Leave( KErrArgument );
}
if ( !FolderExistsL( aItem.ParentFolder() ) )
{
// No parent folder.
User::Leave( KErrArgument );
}
if ( aRenameType != ESuppressCheck )
{
if ( !IsUniqueNameL
( aItem.Name(), aItem.ParentFolder(), KFavouritesNullUid ) )
{
if ( aRenameType == EAutoRename )
{
// Name is conflicting, rename automatically.
newName = AllocUniqueNameLC
( aItem.Name(), aItem.ParentFolder(), KFavouritesNullUid );
}
else
{
User::Leave( KErrAlreadyExists );
}
}
}
// All is well so far; update is valid.
iTable.PutWriteLockL();
// If there is a new name, set it.
if ( newName )
{
aItem.SetNameL( *newName );
}
// Validation may have moved the cursor; set it back.
iTable.InsertLC(); // Prepare for update.
iTable.WriteItemDataL( aItem ); // Put the data.
iTable.SetFactoryItemL( aFactoryItem );
iTable.SetReadOnlyL( aReadOnly );
iTable.PutL(); // Apply changes.
aItem.SetUid( iTable.Uid() ); // Get Uid.
aItem.SetModified( iTable.Modified() ); // Get modtime.
// Sanity check; no item can exist with the Null uid.
__ASSERT_DEBUG( aItem.Uid() != KFavouritesNullUid,
FavouritesPanic( EFavouritesNullUidInDatabase ) );
iUpdatesSinceLastCompact++;
if ( newName )
{
CleanupStack::PopAndDestroy(); // newName
}
}
// ---------------------------------------------------------
// CFavouritesSrvDb::IsUniqueNameL
// ---------------------------------------------------------
//
TBool CFavouritesSrvDb::IsUniqueNameL
( const TDesC& aName, TInt aFolder, TInt aAcceptUid )
{
__ASSERT_DEBUG( CFavouritesItemImpl::IsValidName( aName ), \
FavouritesPanic( EFavouritesInternal ) );
TBool unique = ETrue;
TInt uid;
iTable.SetFiltersL( TFavouritesFilter( aFolder, CFavouritesItem::ENone,
&aName, KFavouritesNullContextId ) );
iTable.BeginningL();
while ( iTable.NextL() )
{
// We have an item with this name; check if it is aAcceptUid or
// one of the special items (for which names need not be unique).
iTable.GetL();
uid = iTable.Uid();
if (
uid != aAcceptUid &&
uid != KFavouritesHomepageUid &&
uid != KFavouritesLastVisitedUid &&
uid != KFavouritesStartPageUid &&
uid != KFavouritesAdaptiveItemsFolderUid
)
{
// It's not aAcceptUid or special item. We have a name clash.
unique = EFalse;
break;
}
}
iTable.ClearFilters();
return unique;
}
// ---------------------------------------------------------
// CFavouritesSrvDb::MakeNameUniqueL
// ---------------------------------------------------------
//
void CFavouritesSrvDb::MakeUniqueNameL
( TDes& aName, TInt aFolder, TInt aAcceptUid )
{
TBool forcePostfix = EFalse;
__ASSERT_DEBUG( CFavouritesItemImpl::IsValidName( aName ), \
FavouritesPanic( EFavouritesInternal ) );
// Make sure the result fits.
__ASSERT_DEBUG( aName.MaxLength() >= Min\
( aName.Length() + KFavouritesMaxPostfix, KFavouritesMaxName ), \
FavouritesPanic( EFavouritesBufferTooSmall ) );
TPtrC prefix = GetPrefix( aName );
if ( prefix.Length() + KFavouritesMaxPostfix > KFavouritesMaxName )
{
// Oops, this prefix is so long that if we append the longest possible
// postfix to it, the result will not fit into a bookmark name!
// In this case, we truncate the prefix. The truncated prefix is most
// likely not conflicting as is, but we do the postfix appending
// anyway.
prefix.Set
( prefix.Left( KFavouritesMaxName - KFavouritesMaxPostfix ) );
forcePostfix = ETrue;
}
HBufC* buf = HBufC::NewLC( prefix.Length() + KFavouritesMaxPostfix );
_LIT( KFormatPrefix, "%S*" );
buf->Des().Format( KFormatPrefix, &prefix );
iTable.SetFiltersL( TFavouritesFilter
( aFolder, CFavouritesItem::ENone, buf, KFavouritesNullContextId ) );
CleanupStack::PopAndDestroy(); // buf
TInt uid;
TInt postfix;
CArrayFix<TInt>* postfixes =
new (ELeave) CArrayFixFlat<TInt>( KPostfixListGranularity );
CleanupStack::PushL( postfixes );
iTable.BeginningL();
while ( iTable.NextL() )
{
iTable.GetL();
uid = iTable.Uid();
if (
uid != aAcceptUid &&
uid != KFavouritesHomepageUid &&
uid != KFavouritesLastVisitedUid &&
uid != KFavouritesStartPageUid &&
uid != KFavouritesAdaptiveItemsFolderUid
)
{
// Appending all values (incl. negative values and duplicates).
postfixes->AppendL( GetPostfix( iTable.Name(), prefix ) );
}
}
iTable.ClearFilters();
// Sort postfixes ascending.
TKeyArrayFix key( 0, ECmpTInt );
User::LeaveIfError( postfixes->Sort( key ) );
// Initial postfix candidate is 0 ("no postfix"), or 1 if forcePostfix
// is ETrue.
postfix = forcePostfix ? 1 : 0;
// Find the first free postfix, above the starting candidate (0 or 1).
// The loop skips negative values and duplicates.
TInt i;
for ( i = 0; i < postfixes->Count(); i++ )
{
if ( postfixes->At( i ) < postfix )
{
// This value is smaller than the current candidate;
// candidate stays the current one (do nothing); but keep checking
// forthcoming bigger values (continue).
;
}
else if ( postfixes->At( i ) == postfix )
{
// This postfix already taken. Candidate is next one.
postfix++;
}
else
{
// postfixes->At( i ) > postfix: a "hole" is found.
//
// This case could be the condition in the controlling "for" (and
// the "break" statement could be avoided); but I intentionally
// placed it here so the logic of the "hole-search" can be seen
// more clearly.
break;
}
}
CleanupStack::PopAndDestroy(); // postfixes
if ( postfix == 0 )
{
// First unique name is the prefix without any postfix.
// Simply set length to the prefix length.
aName.SetLength( prefix.Length() );
}
else
{
// Append a new postfix to the prefix.
// It seems we have to copy the prefix into a buffer.
// The prefix points to aName, so Format would format aName
// using the prefix from itself. But Format does some weird
// tricks (filling the buffer with some rubbish before writing
// into it). It's not sprintf :-(.
buf = prefix.AllocLC();
_LIT( KFormatSmallPostfix, "%S(0%d)" );
_LIT( KFormatLargePostfix, "%S(%d)" );
if ( postfix < 10 )
{
aName.Format( KFormatSmallPostfix, buf, postfix );
}
else
{
aName.Format( KFormatLargePostfix, buf, postfix );
}
CleanupStack::PopAndDestroy(); // buf
}
// Sanity check. The generated name should really be unique.
__ASSERT_DEBUG \
( \
IsUniqueNameL( aName, aFolder, aAcceptUid ), \
FavouritesPanic( EFavouritesInternal ) \
);
}
// ---------------------------------------------------------
// CFavouritesSrvDb::AllocUniqueNameLC
// ---------------------------------------------------------
//
HBufC* CFavouritesSrvDb::AllocUniqueNameLC
( const TDesC& aName, TInt aFolder, TInt aAcceptUid )
{
HBufC* uniqueName = HBufC::NewLC( aName.Length() + KFavouritesMaxPostfix );
*uniqueName = aName;
TPtr ptr( uniqueName->Des() );
MakeUniqueNameL( ptr, aFolder, aAcceptUid );
return uniqueName;
}
// ---------------------------------------------------------
// CFavouritesSrvDb::StaticRollback
// ---------------------------------------------------------
//
void CFavouritesSrvDb::StaticRollback( TAny* aDb )
{
STATIC_CAST( CFavouritesSrvDb*, aDb )->Rollback();
}
// ---------------------------------------------------------
// CFavouritesSrvDb::RestoreFactorySettingsL
// ---------------------------------------------------------
//
void CFavouritesSrvDb::RestoreFactorySettingsL
( RFavouritesSrvTable& aReferenceTable, CUidMap& aApMap )
{
__ASSERT_DEBUG( InTransaction(), FavouritesPanic( EFavouritesInternal ) );
// 1. Check ContextId-s of folders in reference table, and clear all those
// ContextId-s from our folders.
//
// This part is a bit "out of line" for the Engine, as here we do deal
// with ContextId semantics: we ensure that we don't create more folders
// with a given ContextId. This ensures the expected correct behaviour
// of Seamless Link functionality after an RFS: the newly restored
// folders become the Seamless Link folders.
// (Note: if the reference table contains more folders with the same
// ContextId, they will be mechanically copied. Checking that is really
// not our business.)
TInt32 contextId;
aReferenceTable.BeginningL();
while ( aReferenceTable.NextL() )
{
aReferenceTable.GetL();
contextId = aReferenceTable.ContextId();
if ( contextId != KFavouritesNullContextId )
{
// Folders from root with given context id.
iTable.SetFiltersL( TFavouritesFilter( KFavouritesRootUid,
CFavouritesItem::EFolder, NULL, contextId ) );
iTable.BeginningL();
while( iTable.NextL() )
{
iTable.UpdateLC();
iTable.SetContextIdL( KFavouritesNullContextId );
iTable.PutL( /*aTouch=*/EFalse );
}
iTable.ClearFilters();
}
}
// 2. Delete all items that have the "factory item" flag.
// (These are the unmodified factory items - any modification
// clears the flag.) The "factory item" and read-only flag of floders are cleared.
iTable.BeginningL();
while ( iTable.NextL() )
{
iTable.GetL();
if ( iTable.FactoryItem() )
{
switch ( iTable.Type() )
{
case CFavouritesItem::EItem:
{
// Delete unmodified factory item
iTable.DeleteL();
break;
}
case CFavouritesItem::EFolder:
{
// Clear flags for folders.
iTable.UpdateLC();
iTable.SetFactoryItemL( EFalse );
iTable.SetReadOnlyL( EFalse );
iTable.PutL( /*aTouch=*/EFalse );
break;
}
default:
{
FavouritesPanic( EFavouritesInternal );
break;
}
}
}
}
// 3. Lookup folders by name, creating new ones if necessary. The target
// folder gets the attributes of the source folder (read-only and
// factory item flag), regardless of whether it is existing or newly
// created. During this process, a mapping table
// (source folder uid -> target folder uid) is built.
TTime modTime;
SUidPair mapping;
TPtrC name;
CFavouritesItemImpl* item = CFavouritesItemImpl::NewLC();
CUidMap* parentMap = new (ELeave) CUidMap( 1 );
CleanupStack::PushL( parentMap );
// Add the "root->root" mapping to the map.
mapping.iUid1 = KFavouritesRootUid;
mapping.iUid2 = KFavouritesRootUid;
parentMap->AppendL( mapping );
// All folder of reference table in root.
aReferenceTable.SetFiltersL( TFavouritesFilter( KFavouritesRootUid,
CFavouritesItem::EFolder, NULL, KFavouritesNullContextId ) );
aReferenceTable.BeginningL();
while( aReferenceTable.NextL() )
{
aReferenceTable.GetL();
name.Set( aReferenceTable.Name() );
__ASSERT_DEBUG( name.Length(), FavouritesPanic( EFavouritesInternal ) );
// Folders matching the name.
iTable.SetFiltersL( TFavouritesFilter( KFavouritesRootUid,
CFavouritesItem::EFolder, &name, KFavouritesNullContextId ) );
iTable.BeginningL();
if( iTable.NextL() )
{
// Folder exists with this name.
// Copy read-only and factory item flags from reference table.
// Also copy ContextId (possibly we cleared that earlier).
iTable.UpdateLC();
iTable.SetReadOnlyL( aReferenceTable.ReadOnly() );
iTable.SetFactoryItemL( aReferenceTable.FactoryItem() );
iTable.SetContextIdL( aReferenceTable.ContextId() );
iTable.PutL( /*aTouch=*/EFalse );
}
else
{
// No folder exists with this name.
// Add now by copying from reference table (incl. modtime).
aReferenceTable.ReadItemDataL( *item );
modTime = item->Modified();
DoAddL
(
*item,
EDontRename,
/*aFactoryItem=*/item->IsFactoryItem(),
/*aReadOnly=*/item->IsReadOnly()
);
// Cursor is still on the added record. Set modtime.
// Not using CFavouritesSrvDb::SetModifiedL() - that would mess up
// the filters.
iTable.UpdateLC();
iTable.SetModifiedL( modTime );
iTable.PutL( /*aTouch=*/ EFalse );
}
// In both cases (existing folder updated, or new added), the cursor
// is now on the target folder. Add a new mapping entry.
mapping.iUid1 = aReferenceTable.Uid();
mapping.iUid2 = iTable.Uid();
parentMap->AppendL( mapping );
iTable.ClearFilters();
}
aReferenceTable.ClearFilters();
// 4. Replace the unmodified factory items of our table with corresponding items from reference table and
// add remaining items in the reference table to our table,
// with AP mapping and parent folder mapping applied.
TInt i;
TFavouritesWapAp ap;
TInt origId;
TInt newId;
// Get all items.
aReferenceTable.SetFiltersL( TFavouritesFilter ( KFavouritesNullUid,
CFavouritesItem::EItem, NULL, KFavouritesNullContextId ) );
aReferenceTable.BeginningL();
while ( aReferenceTable.NextL() )
{
aReferenceTable.GetL();
aReferenceTable.ReadItemDataL( *item );
modTime = item->Modified();
origId = item->Uid();
// Remap parent folder.
for( i = 0; i < parentMap->Count(); i++ )
{
if( item->ParentFolder() == parentMap->At( i ).iUid1 )
{
item->SetParentFolder( parentMap->At( i ).iUid2 );
break;
}
// Parent folder mapping should always be successful.
__ASSERT_DEBUG( i < parentMap->Count(), \
FavouritesPanic( EFavouritesInternal ) );
}
// Add the item.
DoAddL
(
*item,
EAutoRename,
/*aFactoryItem=*/ETrue,
/*aReadOnly=*/item->IsReadOnly()
);
// Cursor is still on the added record. Set modtime.
// Not using CFavouritesSrvDb::SetModifiedL() - that would mess up
// the filters.
iTable.UpdateLC();
iTable.SetModifiedL( modTime );
iTable.PutL( /*aTouch=*/ EFalse );
// Get the new item UID
newId = item->Uid();
if( !iTable.SeekToUidL( origId ) )
{
iTable.GotoToUidL(newId);
iTable.UpdateLC();
iTable.SetUidL( origId );
iTable.PutL( /*aTouch=*/ EFalse );
item->SetUid( origId );
}
// Check AP, enrol for mapping if needed.
ap = item->WapAp();
if ( !ap.IsDefault() && !ap.IsNull() )
{
mapping.iUid1 = item->Uid();
mapping.iUid2 = STATIC_CAST( TInt, ap.ApId() );
aApMap.AppendL( mapping );
}
}
aReferenceTable.ClearFilters();
CleanupStack::PopAndDestroy( 2 ); // parentMap, item
}
// ---------------------------------------------------------
// CFavouritesSrvDb::SetAccessPointsL
// ---------------------------------------------------------
//
void CFavouritesSrvDb::SetAccessPointsL( const CUidMap& aApMap )
{
__ASSERT_DEBUG( InTransaction(), FavouritesPanic( EFavouritesInternal ) );
TInt i;
TFavouritesWapAp ap;
for ( i = 0; i < aApMap.Count(); i++ )
{
ap.SetApId( STATIC_CAST( TUint32, aApMap[i].iUid2 ) );
iTable.GotoToUidL( aApMap[i].iUid1 );
iTable.GetL();
iTable.UpdateLC();
iTable.SetWapApL( ap );
iTable.PutL( /*aTouch=*/ EFalse );
}
}
// ---------------------------------------------------------
// IMPORT BOOKMARKS CODE INTEGRATED FROM BOOKMARKS UTILITY
// ---------------------------------------------------------
// CONSTANTS
/// ',' character.
LOCAL_C const TUint KComma = ',';
/// '#' character.
LOCAL_C const TUint KHash = '#';
/// EOF (0) character.
LOCAL_C const TUint KEof = 0;
/// '\r' character.
LOCAL_C const TUint KCr = '\r';
/// '\n' character.
LOCAL_C const TUint KLf = '\n';
/// LF-EOF stop token set.
_LIT( KStopLfEof, "\n\0" );
/// Comma stop token set.
_LIT( KStopComma, "," );
/// Comma-LF-EOF stop token set.
_LIT( KStopCommaLfEof, ",\n\0" );
/// "Folder" kewyword.
_LIT( KFolder, "Folder" );
/// "Item" kewyword.
_LIT( KItem, "Item" );
// ---------------------------------------------------------
// CFavouritesSrvDb::ImportL
// ---------------------------------------------------------
//
void CFavouritesSrvDb::ImportL( const TDesC& aFileName )
{
User::LeaveIfError( iFile.Open( iFs, aFileName,
EFileRead | EFileShareReadersOnly ) );
CleanupClosePushL<RUnicodeFile>( iFile );
iFolderNames = new (ELeave) CArrayFixFlat<CFavouritesFolder>( 1 );
User::LeaveIfNull( iFolderNames );
CleanupStack::PushL( iFolderNames );
iBuf = new (ELeave) TText16[KFavouritesMaxUrl];
User::LeaveIfNull( iBuf );
CleanupStack::PushL( iBuf );
iMaxCh = iBuf + KFavouritesMaxUrl;
GetCharL();
while( NextLineL() )
{;}
TUint count = iFolderNames->Count();
for (TUint i = 0; i < count; i++)
{
CFavouritesFolder folder = iFolderNames->At(i);
delete folder.Name();
}
CleanupStack::Pop(iBuf); //iBuf
delete iBuf;
CleanupStack::Pop(iFolderNames); //iFolderNames
delete iFolderNames;
CleanupStack::PopAndDestroy(); // close iFile
}
// ---------------------------------------------------------
// CFavouritesSrvDb::GetCharL
// ---------------------------------------------------------
//
void CFavouritesSrvDb::GetCharL()
{
iCurCh = iFile.GetCharL();
if ( iCurCh == KCr )
{
// CR character found - ignore it. Not expecting CR to appear anywhere
// else than before an LF.
iCurCh = iFile.GetCharL();
}
}
// ---------------------------------------------------------
// CFavouritesSrvDb::NextLineL
// ---------------------------------------------------------
//
TBool CFavouritesSrvDb::NextLineL()
{
switch( iCurCh )
{
case KEof:
// EOF
return EFalse;
case KHash:
// Comment line; skip over.
SkipL( KStopLfEof );
GetCharL();
break;
case KCr:
case KLf:
// Empty line; skip over.
GetCharL();
break;
default:
// Parse bookmark attributes.
AttrsL();
break;
}
return ETrue;
}
// ---------------------------------------------------------
// CFavouritesSrvDb::NextTokenL
// ---------------------------------------------------------
//
TPtrC CFavouritesSrvDb::NextTokenL( const TDesC& aStopCharSet )
{
iNextCh = iBuf; // Start storing token at start of buffer.
while ( iNextCh < iMaxCh )
{
if ( aStopCharSet.Locate( iCurCh ) != KErrNotFound )
{
// Stop character found - return what we have stored so far. This
// may be an empty string as well.
return TPtrC( iBuf, iNextCh - iBuf );
}
*iNextCh = STATIC_CAST( TText16, iCurCh );
iNextCh++;
GetCharL();
}
// No more space in buffer to store token.
User::Leave( KErrOverflow );
/*NOTREACHED*/
return TPtrC();
}
// ---------------------------------------------------------
// CFavouritesSrvDb::NextIntTokenL
// ---------------------------------------------------------
//
TInt CFavouritesSrvDb::NextIntTokenL( const TDesC& aStopCharSet )
{
TInt ret = 0;
TPtrC token( NextTokenL( aStopCharSet ) );
if ( token.Length() )
{
TLex lex( token );
User::LeaveIfError( lex.Val( ret ) );
}
return ret;
}
// ---------------------------------------------------------
// CFavouritesSrvDb::NextHexTokenL
// ---------------------------------------------------------
//
TInt32 CFavouritesSrvDb::NextHexTokenL( const TDesC& aStopCharSet )
{
TUint32 ret = 0;
TPtrC token( NextTokenL( aStopCharSet ) );
if ( token.Length() )
{
TLex lex( token );
User::LeaveIfError( lex.Val( ret, EHex ) );
}
return STATIC_CAST( TInt32, ret );
}
// ---------------------------------------------------------
// CFavouritesSrvDb::SkipL
// ---------------------------------------------------------
//
void CFavouritesSrvDb::SkipL( const TDesC& aStopCharSet )
{
// Note that EOF also can be a stop character; aStopChar check has
// precendence over EOF check. That is the 'expected EOF' case.
while( aStopCharSet.Locate( iCurCh ) == KErrNotFound )
{
if ( iCurCh == KEof )
{
// Unexpected EOF.
User::Leave( KErrEof );
}
GetCharL();
}
}
// ---------------------------------------------------------
// CFavouritesSrvDb::AttrsL
// ---------------------------------------------------------
//
void CFavouritesSrvDb::AttrsL()
{
TPtrC token;
TInt num = 0;
TBool readOnly( EFalse );
// TBool factoryItem;
TBool preferred( EFalse );
TBool hidden( EFalse );
TFavouritesWapAp ap;
iTable.OpenL( iDatabase );
CleanupClosePushL<RFavouritesSrvTable>( iTable );
iTable.BeginningL();
iTable.InsertLC();
// Parse the line and fill item.
// Type (including special items).
token.Set( NextTokenL( KStopComma ) );
if ( !token.Compare( KFolder ) )
{
iTable.SetTypeL( CFavouritesItem::EFolder );
}
else if ( !token.Compare( KItem ) )
{
iTable.SetTypeL( CFavouritesItem::EItem );
}
else
{
// Expected "Folder", "Item"
User::Leave( KErrCorrupt );
}
GetCharL();
// Name.
iTable.SetNameL( NextTokenL( KStopComma ) );
if (iTable.Type() == CFavouritesItem::EFolder) // Remember the folder name for UID lookup.
{
HBufC* buf = HBufC::NewLC( iTable.Name().Length() );
TPtr ptr( buf->Des() );
ptr.Copy( iTable.Name() );
CFavouritesFolder folder( buf, iTable.Uid() );
iFolderNames->AppendL( folder );
CleanupStack::Pop(); // for newlc of buf
}
GetCharL();
// Parent folder.
iTable.SetParentFolderL( FolderByNameL( NextTokenL( KStopComma ) ) );
GetCharL();
// URL.
iTable.SetUrlL( NextTokenL( KStopComma ) );
GetCharL();
// WAP AP.
token.Set( NextTokenL( KStopComma ) );
if ( token.Length() )
{
TLex lex( token );
if ( lex.Val( num ) != KErrNone )
{
IAPIdByNameL( token, num );
}
ap = num;
}
else
{
ap.SetDefault();
}
iTable.SetWapApL( ap );
GetCharL();
// User name.
iTable.SetUsernameL( NextTokenL( KStopComma ) );
GetCharL();
// Password.
iTable.SetPasswordL( NextTokenL( KStopComma ) );
GetCharL();
// Read-only flag.
num = NextIntTokenL( KStopComma );
if ( num == 0 )
{
readOnly = EFalse;
}
else if ( num == 1 )
{
readOnly = ETrue;
}
else
{
// Expected "0" or "1".
User::Leave( KErrCorrupt );
}
GetCharL();
// Factory item flag.
num = NextIntTokenL( KStopComma );
if ( num == 0 )
{
// factoryItem = EFalse;
}
else if ( num == 1 )
{
// factoryItem = ETrue;
}
else
{
// Expected "0" or "1".
User::Leave( KErrCorrupt );
}
GetCharL();
// Context id.
iTable.SetContextIdL( NextHexTokenL( KStopCommaLfEof ) );
GetCharL();
num = NextIntTokenL( KStopCommaLfEof );
if ( num == 0 )
{
hidden = EFalse;
}
else if ( num == 1 )
{
hidden = ETrue;
}
else
{
// Expected "0" or "1".
User::Leave( KErrCorrupt );
}
iTable.SetHiddenL(hidden);
// No GetCharL yet - PreferredUid is optional, so we need to investigate
// lookeahed character first.
// Preferred flag (optional).
if ( iCurCh == KComma )
{
GetCharL();
num = NextIntTokenL( KStopLfEof );
if ( num == 0 )
{
preferred = EFalse;
}
else if ( num == 1 )
{
preferred = ETrue;
}
else
{
// Expected "0" or "1".
User::Leave( KErrCorrupt );
}
}
GetCharL();
if ( preferred )
{
iTable.SetPreferredUidL( iTable.Uid() );
}
iTable.SetReadOnlyL( readOnly );
iTable.PutL();
// Now we have the root. The first entry added came with 0 Uid, so this
// value will never be used again.
iTable.Close();
CleanupStack::Pop( ); // close iTable
}
// ---------------------------------------------------------
// CFavouritesSrvDb::FolderByNameL
// ---------------------------------------------------------
//
TInt CFavouritesSrvDb::FolderByNameL( const TDesC& aName )
{
TInt folderUid = KFavouritesRootUid;
if ( aName.Length() )
{
// loop through iFolderNames and if bookmark name is found, return the index/UID where found
// else return root UID.
for (TUint x = 0; x < iFolderNames->Count(); ++x)
{
CFavouritesFolder folder = iFolderNames->At(x);
HBufC* fn = folder.Name();
TPtr ptr( fn->Des());
if (aName.Compare(ptr) == 0)
{
folderUid = folder.Uid();
return folderUid;
}
}
}
return folderUid;
}
// ---------------------------------------------------------
// CFavouritesSrvDb::IAPIdByNameL
// ---------------------------------------------------------
//
TInt CFavouritesSrvDb::IAPIdByNameL( TDesC& aName, TInt& aIapId )
{
TUint pushCount = 0;
TInt retVal = KErrNone;
TBool found = EFalse;
TUint32 iapId = 0;
TInt err = KErrNone;
CCommsDatabase* db = CCommsDatabase::NewL();
CleanupStack::PushL(db);
pushCount++;
CCommsDbTableView* tableView = db->OpenTableLC( TPtrC(WAP_ACCESS_POINT) );
pushCount++;
retVal = tableView->GotoFirstRecord();
while( retVal == KErrNone && !found )
{
TBuf< KCommsDbSvrMaxFieldLength > iapName;
tableView->ReadTextL( TPtrC( COMMDB_NAME ), iapName );
if (iapName.CompareF( aName ) == 0 )
{
TRAP( err, tableView->ReadUintL( TPtrC(COMMDB_ID), iapId ) );
found = ETrue;
}
retVal = tableView->GotoNextRecord();
}
aIapId = iapId;
CleanupStack::PopAndDestroy( pushCount );
return retVal;
}
// End of File