/*
* Copyright (c) 2003-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: Operation independent remote file handling functions
*
*/
#include <apgcli.h>
#include <bautils.h>
#include "rsfwfileentry.h"
#include "rsfwfiletable.h"
#include "rsfwvolumetable.h"
#include "rsfwvolume.h"
#include "rsfwrfestatemachine.h"
#include "rsfwinterface.h"
#include "rsfwcontrol.h"
#include "rsfwremoteaccess.h"
#include "rsfwfileengine.h"
#include "rsfwrfeserver.h"
#include "rsfwlockmanager.h"
#include "mdebug.h"
#include "rsfwdirent.h"
#include "rsfwdirentattr.h"
#include "rsfwinterface.h"
// ----------------------------------------------------------------------------
// CRsfwFileEngine::NewL
// ----------------------------------------------------------------------------
//
CRsfwFileEngine* CRsfwFileEngine::NewL(CRsfwVolume* aVolume)
{
CRsfwFileEngine* self = CRsfwFileEngine::NewLC(aVolume);
CleanupStack::Pop(self);
return self;
}
// ----------------------------------------------------------------------------
// CRsfwFileEngine::NewLC
// ----------------------------------------------------------------------------
//
CRsfwFileEngine* CRsfwFileEngine::NewLC(CRsfwVolume* aVolume)
{
DEBUGSTRING(("CRsfwFileEngine::NewLC"));
CRsfwFileEngine* self = new (ELeave) CRsfwFileEngine();
DEBUGSTRING(("CRsfwFileEngine: in NewLC 0x%x", self));
CleanupStack::PushL(self);
self->ConstructL(aVolume);
return self;
}
// ----------------------------------------------------------------------------
// CRsfwFileEngine::ConstructL
// ----------------------------------------------------------------------------
//
void CRsfwFileEngine::ConstructL(CRsfwVolume* aVolume)
{
iRemoteAccess = NULL;
iRootFid = NULL;
iRootFep = NULL;
iVolume = aVolume;
iFs = CRsfwRfeServer::Env()->iFs;
iConnectionState = KMountNotConnected;
__ASSERT_ALWAYS(iVolume != NULL, User::Panic(KRfeServer, EConstructingServerStructs));
iInactivityTimeout =
iVolume->iMountInfo.iMountConfig.iInactivityTimeout * 1000000;
PrepareCacheL();
// Create file table
iFileTable = CRsfwFileTable::NewL(aVolume, iCacheRoot);
__ASSERT_ALWAYS(iVolume->iVolumeTable != NULL, User::Panic(KRfeServer,
EConstructingServerStructs));
SetupRootL(iVolume->iVolumeTable->iPermanence);
}
// ----------------------------------------------------------------------------
// CRsfwFileEngine::~CRsfwFileEngine
// ----------------------------------------------------------------------------
//
CRsfwFileEngine::~CRsfwFileEngine()
{
DEBUGSTRING(("CRsfwFileEngine destructor"));
delete iFileTable;
delete iRemoteAccess;
delete iLockManager;
StopInactivityTimer();
delete iInactivityTimer;
}
// ----------------------------------------------------------------------------
// CRsfwFileEngine::DispatchL
// we should only come here with some synchronous requests
// ----------------------------------------------------------------------------
//
void CRsfwFileEngine::DispatchL(TRfeInArgs& aIn, TRfeOutArgs& aOut)
{
switch(aIn.iOpCode)
{
case EFsIoctl:
DEBUGSTRING(("IOCTL"));
DoIoctlL(static_cast<TRfeIoctlInArgs&>(aIn),
aOut);
break;
case EFsRoot:
DEBUGSTRING(("ROOT"));
DoRootL(static_cast<TRfeRootInArgs&>(aIn),
static_cast<TRfeRootOutArgs&>(aOut));
break;
case ESetAttr:
DEBUGSTRING(("SETATTR"));
DoSetAttrL(static_cast<TRfeSetAttrInArgs&>(aIn),
aOut);
break;
default:
DEBUGSTRING(("WHAT??? - %d", aIn.iOpCode));
User::Leave(KErrArgument);
break;
}
}
// ----------------------------------------------------------------------------
// CRsfwFileEngine::FullNameLC
// ----------------------------------------------------------------------------
//
HBufC* CRsfwFileEngine::FullNameLC(CRsfwFileEntry& aFe)
{
HBufC* fn = aFe.FullNameLC();
return fn;
}
// ----------------------------------------------------------------------------
// CRsfwFileEngine::FullNameL
// ----------------------------------------------------------------------------
//
HBufC* CRsfwFileEngine::FullNameL(CRsfwFileEntry& aFe)
{
HBufC* fn = FullNameLC(aFe);
CleanupStack::Pop(fn);
return fn;
}
// ----------------------------------------------------------------------------
// CRsfwFileEngine::SetupAttributes
// ----------------------------------------------------------------------------
//
void CRsfwFileEngine::SetupAttributes(CRsfwFileEntry& aFe)
{
DEBUGSTRING(("CRsfwFileEngine::SetupAttributes"));
// Construct the attributes for a newly created file or directory,
// or a file that that was locally modified and just written to the server,
// based on local knowledge of time and file size.
// We assume that either the file is cached or it is an empty file.
// We do not touch the local or protection attributes.
TUint att;
// Assume that the file type has already been setup
if (aFe.Type() == KNodeTypeDir)
{
att = KEntryAttDir;
}
else
{
att = 0;
}
TTime time;
if (aFe.IsCached())
{
TDesC* cacheNamep = aFe.CacheFileName();
RFile f;
if (f.Open(iFs, *cacheNamep, EFileShareAny) == KErrNone)
{
// attribute bits
TUint a;
f.Att(a);
att |= a & KEntryAttReadOnly;
if (aFe.Type() == KNodeTypeDir)
{
aFe.SetSize(0);
}
else
{
if (aFe.IsFullyCached())
{
// size
TInt siz;
f.Size(siz);
DEBUGSTRING(("File is fully cached, setting size to %d", siz));
aFe.SetSize(siz);
aFe.SetCachedSize(siz);
}
else
{
DEBUGSTRING(("File is not fully cached, not touching the size"));
// file is not fully cached
// the size cannot be set from the local cache container
}
}
// modification time
f.Modified(time);
f.Close();
aFe.iUseCachedData = ETrue;
}
else
{
// No cache
aFe.SetSize(0);
time.HomeTime();
}
}
else
{
// No cache
aFe.SetSize(0);
time.HomeTime();
}
aFe.SetAtt(att);
aFe.SetModified(time);
aFe.SetAttribValidationTime();
}
// ----------------------------------------------------------------------------
// CRsfwFileEngine::MakeDirectoryEntry
// ----------------------------------------------------------------------------
//
void CRsfwFileEngine::MakeDirectoryEntry(CRsfwFileEntry& aFe, TDirEnt& aDirEnt)
{
DEBUGSTRING(("CRsfwFileEngine::MakeDirectoryEntry"));
DEBUGSTRING16(("name %S, att %d, size %d", aFe.Name(), aFe.Att(), aFe.Size()));;
aDirEnt.Clear();
aDirEnt.iName.Copy(*aFe.Name());
aDirEnt.iAttr.iAtt = aFe.Att();
aDirEnt.iAttr.iSize = aFe.Size();
aDirEnt.iAttr.iModified = aFe.Modified();
aDirEnt.iAttr.iUid3 = aFe.iUid;
}
// ----------------------------------------------------------------------------
// CRsfwFileEngine::UpdateDirectoryContainerL
// ----------------------------------------------------------------------------
//
void CRsfwFileEngine::UpdateDirectoryContainerL(CRsfwFileEntry& aFe)
{
// Construct the directory container based on
// file table information
DEBUGSTRING16(("Update directory container of %d (%S)", aFe.Fid().iNodeId, aFe.Name()));
TDesC* cacheNamep = aFe.CacheFileName();
if (!cacheNamep)
{
// There was no prior cache.
DEBUGSTRING(("Cache missing!"));
User::Leave(KErrGeneral);
}
RFile f;
CleanupClosePushL(f);
User::LeaveIfError(f.Replace(iFs,
*cacheNamep,
EFileShareAny | EFileWrite));
RFileWriteStream fStream(f);
CleanupClosePushL(fStream);
RPointerArray<CRsfwFileEntry>* kidsp = aFe.Kids();
TInt i;
if (!(iVolume->iVolumeTable->EnsureCacheCanBeAddedL(
sizeof(TEntry) * kidsp->Count())))
{ // pessimistic estimate
User::Leave(KErrDiskFull);
}
for (i = 0; i < kidsp->Count(); i++)
{
CRsfwFileEntry* kidFep = (*kidsp)[i];
TDirEnt dirEnt;
MakeDirectoryEntry(*kidFep, dirEnt);
dirEnt.ExternalizeL(fStream);
}
CleanupStack::PopAndDestroy(2, &f); // f
aFe.ResetLocallyDirty();
}
// ----------------------------------------------------------------------------
// CRsfwFileEngine::DataChanged
// ----------------------------------------------------------------------------
//
TInt CRsfwFileEngine::DataChanged(const CRsfwDirEntAttr& aOldAttr,
const CRsfwDirEntAttr& aNewAttr)
{
// Based on attributes or metadata in general,
// tell whether the actual data, if cached,
// should be updated
if (aOldAttr.Att() == KEntryAttDir)
{
// use Last Modified (a weak entity tag)
if (aOldAttr.Modified() == aNewAttr.Modified())
{
return EFalse;
}
else
{
return ETrue;
}
}
else
{
// use ETags if available
// a strong entity tag
if (aOldAttr.ETag() && aNewAttr.ETag())
{
if (*aOldAttr.ETag() == *aNewAttr.ETag())
{
return EFalse;
}
else
{
return ETrue;
}
}
// use Last Modified (a weak entity tag)
// we assume it's file and compare also iSize...
if ((aOldAttr.Modified() == aNewAttr.Modified()) &&
(aOldAttr.Size() == aNewAttr.Size()))
{
return EFalse;
}
else
{
return ETrue;
}
}
}
// ----------------------------------------------------------------------------
// CRsfwFileEngine::UseCachedData
// ----------------------------------------------------------------------------
//
TBool CRsfwFileEngine::UseCachedData(CRsfwFileEntry& aFe)
{
if (!Disconnected())
{
return aFe.UseCachedData();
}
else
{
return ETrue;
}
}
// ----------------------------------------------------------------------------
// CRsfwFileEngine::UseCachedAttributes
// ----------------------------------------------------------------------------
//
TBool CRsfwFileEngine::UseCachedAttributes(CRsfwFileEntry& aFe)
{
if (!Disconnected())
{
if (aFe.Type() == KNodeTypeDir)
{
return iFileTable->Volume()->iVolumeTable->
IsCachedAttrStillValid(aFe.iAttribValidation);
}
else
{ // file
return iFileTable->Volume()->iVolumeTable->
IsCachedDataStillValid(aFe.iAttribValidation);
}
}
else
{
return ETrue;
}
}
// ----------------------------------------------------------------------------
// CRsfwFileEngine::GetAttributesL
// ----------------------------------------------------------------------------
//
void CRsfwFileEngine::GetAttributesL(CRsfwFileEntry& aFe,
CRsfwDirEntAttr*& aAttr,
TUint aNodeType,
CRsfwRfeStateMachine* aCaller)
{
// Gets attributes for File Entry aFe.
// Uses either cached attributes (if they are still deemed to be valid), or
// fetches the attributes from the server*/
DEBUGSTRING(("GetAttributesL"));
if ((aFe.Type() == aNodeType) && UseCachedAttributes(aFe))
{
// Nothing to do
if (aFe.IsOpenedForWriting())
{
// update attributes when we are writing to the file
DEBUGSTRING(("volatile attributes"));
SetupAttributes(aFe);
}
else
{
DEBUGSTRING(("using cached attributes"));
}
aCaller->HandleRemoteAccessResponse(0, KErrNone); // "file exists"
}
else
{
// Refresh attributes
UpdateAttributesL(aFe, aAttr, aNodeType, aCaller);
}
}
// ----------------------------------------------------------------------------
// CRsfwFileEngine::UpdateAttributesL
// ----------------------------------------------------------------------------
//
void CRsfwFileEngine::UpdateAttributesL(CRsfwFileEntry& aFe,
CRsfwDirEntAttr*& aAttr,
TUint aNodeType,
MRsfwRemoteAccessResponseHandler* aCaller)
{
// UpdateAttributes doesn't attempt to use cached attributes
HBufC* path = FullNameLC(aFe);
TPtr p = path->Des();
DEBUGSTRING16(("UpdateAttributesL of '%S'", &p));
UpdateAttributesL(*path, aAttr, aNodeType, aCaller);
CleanupStack::PopAndDestroy(path); // path
}
// ----------------------------------------------------------------------------
// CRsfwFileEngine::UpdateAttributesL
// ----------------------------------------------------------------------------
//
void CRsfwFileEngine::UpdateAttributesL(TDesC& aPath,
TDesC& aName,
CRsfwDirEntAttr*& aAttr,
TUint aNodeType,
MRsfwRemoteAccessResponseHandler* aCaller)
{
HBufC* pn = HBufC::NewLC(KMaxPath);
TPtr pnPtr = pn->Des();
if (aPath.Length())
{
pnPtr.Copy(aPath);
pnPtr.Append('/');
}
pnPtr.Append(aName);
DEBUGSTRING16(("UpdateKidAttributes of '%S'", &pnPtr));
UpdateAttributesL(pnPtr, aAttr, aNodeType, aCaller);
CleanupStack::PopAndDestroy(pn);
}
// ----------------------------------------------------------------------------
// CRsfwFileEngine::UpdateAttributesL
// ----------------------------------------------------------------------------
//
void CRsfwFileEngine::UpdateAttributesL(TDesC& aFullPath,
CRsfwDirEntAttr*& aAttr,
TUint aNodeType,
MRsfwRemoteAccessResponseHandler* aCaller)
{
// If we have "recently" found out that this file/dir does NOT exist
// we cache even this negative result. Time limit is cache expiry for
// directory attributes
if ((aFullPath.Length() > 0) && // do not compare root folder (always exists)
(iLastFailedLookup == aFullPath) &&
(iFileTable->Volume()->iVolumeTable->
IsCachedAttrStillValid(iLookupTime)))
{
if (aNodeType == KNodeTypeDir)
{
aCaller->HandleRemoteAccessResponse(0, KErrPathNotFound);
}
else if (aNodeType == KNodeTypeFile)
{
aCaller->HandleRemoteAccessResponse(0, KErrNotFound);
}
return;
}
if (!Disconnected())
{
if (aNodeType == KNodeTypeDir)
{
RemoteAccessL()->GetDirectoryAttributesL(aFullPath, aAttr, aCaller);
}
else if (aNodeType == KNodeTypeFile)
{
RemoteAccessL()->GetFileAttributesL(aFullPath, aAttr, aCaller);
}
}
else
{
User::Leave(KErrNotFound);
}
}
// ----------------------------------------------------------------------------
// CRsfwFileEngine::CreateContainerFileL
// ----------------------------------------------------------------------------
//
void CRsfwFileEngine::CreateContainerFileL(CRsfwFileEntry& aFe)
{
// Create a container file for the Fid.
// If the cache file already exists, it will be deleted
RFile f;
HBufC* cachePath = HBufC::NewMaxLC(KMaxPath);
TPtr pathPtr = cachePath->Des();
BuildContainerPathL(aFe, pathPtr);
TInt err = f.Replace(iFs, *cachePath, EFileShareAny | EFileWrite);
f.Close();
if (err != KErrNone)
{
DEBUGSTRING(("Error when creating container file! err=%d", err));
User::Leave(KErrGeneral);
}
aFe.SetCacheFileName(cachePath);
CleanupStack::PopAndDestroy(cachePath);
}
// ----------------------------------------------------------------------------
// CRsfwFileEngine::FetchAndCacheL
// ----------------------------------------------------------------------------
//
TUint CRsfwFileEngine::FetchAndCacheL(CRsfwFileEntry& aFe,
TInt aFirstByte,
TInt* aLength,
RPointerArray<CRsfwDirEnt>* aDirEntsp,
CRsfwRfeStateMachine* aCaller)
{
// Fetch a file from the remote store and decrypt it if necessary.
// The assumption is that the file has not yet been fetched
// or has been cached up to the byte indicated by (aFe.iCachedSize - 1)
// and filling the cache will continue linearly
// i.e. aFirstByte = 0 || aFirstByte = aFe.iCachedSize
// Access modules can fetch more than requested, so aLastByte might change.
DEBUGSTRING(("Fetch fid %d, bytes %d - %d",
aFe.Fid().iNodeId,
aFirstByte,
aFirstByte + *aLength));
TUint transactionId = 0;
RFile f;
HBufC* fullName = NULL;
HBufC* cacheName = HBufC::NewMaxLC(KMaxPath);
TPtr cachePtr = cacheName->Des();
TInt err;
TInt usedCache = iVolume->iVolumeTable->TotalCachedSize();
// This much will be added to the cache by this fetch
if (!iVolume->iVolumeTable->EnsureCacheCanBeAddedL(*aLength))
{
User::Leave(KErrDiskFull);
}
if (!Disconnected())
{
if (aFe.CacheFileName())
{
// modify an existing cachefile ...
cachePtr = *(aFe.CacheFileName());
if (aFe.Type() == KNodeTypeFile)
{
// If the cache file exists,
// we will just continue filling it...
err = f.Open(iFs, *cacheName, EFileShareAny | EFileWrite);
if (err)
{
User::LeaveIfError(f.Replace(iFs,
*cacheName,
EFileShareAny | EFileWrite));
}
}
else
{
User::LeaveIfError(f.Replace(iFs,
*cacheName,
EFileShareAny | EFileWrite));
}
}
else
{
// create a new cache file
CreateContainerFileL(aFe, cachePtr, f);
}
CleanupClosePushL(f);
fullName = FullNameLC(aFe);
if (aFe.Type() == KNodeTypeDir)
{
transactionId = GetDirectoryL(aFe,
*fullName,
f,
aDirEntsp,
aCaller);
}
else if (aFe.Type() == KNodeTypeFile)
{
f.Close();
transactionId = RemoteAccessL()->GetFileL(*fullName,
*cacheName,
aFirstByte,
aLength,
0,
aCaller);
}
// fullName, f (duplicate close in the case of files)
CleanupStack::PopAndDestroy(2, &f);
}
CleanupStack::PopAndDestroy(cacheName);
return transactionId;
}
// ----------------------------------------------------------------------------
// CRsfwFileEngine::RequestConnectionStateL
// ----------------------------------------------------------------------------
//
TUint CRsfwFileEngine::RequestConnectionStateL(TUint aConnectionState,
CRsfwRfeStateMachine* aCaller)
{
DEBUGSTRING16(("CRsfwFileEngine::RequestConnectionStateL %d", aConnectionState));
DEBUGSTRING16(("current connection state is %d", iConnectionState));
TUint transactionId = 0;
if (aConnectionState != iConnectionState)
{
switch (aConnectionState)
{
case KMountNotConnected:
DisconnectL();
break;
case KMountStronglyConnected:
transactionId = ConnectL(ETrue, aCaller);
break;
default:
break;
}
}
// else does not do anything (if iConnectionState == aConnectionState)
return transactionId;
}
// ----------------------------------------------------------------------------
// CRsfwFileEngine::EnteredConnectionStateL
// ----------------------------------------------------------------------------
//
void CRsfwFileEngine::EnteredConnectionStateL(TUint aConnectionState,
TBool aRequested)
{
DEBUGSTRING16(("CRsfwFileEngine::EnteredConnectionStateL %d", aConnectionState));
DEBUGSTRING16(("current connection state is %d", iConnectionState));
if (aConnectionState != iConnectionState)
{
iConnectionState = aConnectionState;
iVolume->ConnectionStateChanged(iConnectionState);
switch (aConnectionState)
{
case KMountNotConnected:
if (!aRequested)
{
iRemoteAccess->Cancel(0);
}
break;
case KMountStronglyConnected:
if (aRequested)
{
if (iLockManager)
{
iLockManager->PopulateExternalLockTokenCacheL(iRootFep);
}
}
break;
default:
break;
}
}
}
// ----------------------------------------------------------------------------
// CRsfwFileEngine::ConnectionState
// ----------------------------------------------------------------------------
//
TUint CRsfwFileEngine::ConnectionState()
{
return iConnectionState;
}
// ----------------------------------------------------------------------------
// CRsfwFileEngine::LockManager
// ----------------------------------------------------------------------------
//
CRsfwLockManager* CRsfwFileEngine::LockManager()
{
return iLockManager;
}
// ----------------------------------------------------------------------------
// CRsfwFileEngine::SetPermanenceL
// ----------------------------------------------------------------------------
//
void CRsfwFileEngine::SetPermanenceL(TBool aPermanence)
{
iFileTable->SetPermanenceL(aPermanence);
}
// ----------------------------------------------------------------------------
// CRsfwFileEngine::Disconnected
// ----------------------------------------------------------------------------
//
TBool CRsfwFileEngine::Disconnected()
{
return (iConnectionState == KMountNotConnected);
}
// ----------------------------------------------------------------------------
// CRsfwFileEngine::WriteDisconnected
// ----------------------------------------------------------------------------
//
TBool CRsfwFileEngine::WriteDisconnected()
{
// This also encompasses disconnected mode
return (iConnectionState != KMountStronglyConnected);
}
// ----------------------------------------------------------------------------
// CRsfwFileEngine::AddToCacheL
// ----------------------------------------------------------------------------
//
TInt CRsfwFileEngine::AddToCacheL(CRsfwFileEntry& aFe,
RPointerArray<CRsfwDirEnt>* aDirEnts,
CRsfwFileEngine *aFileEngine,
TUint cachedSize)
{
// returns the size of the cached data
RFs fs = CRsfwRfeServer::Env()->iFs;
TInt err;
TInt kidsCount = 0;
TInt containerSize = cachedSize;
// holds true for files, will be overwritten for directories
if (aFe.Type() == KNodeTypeDir)
{
// *********** originally from CRsfwFileEngine::GetDirectoryL()
// **********************************************************
// Unmark and mark kids only when getdirectory returns KErrNone
// otherwise (i.e. KErrUpdateNotRequired) let's just keep
// the cached kids...
aFe.UnmarkKids();
RApaLsSession lsSession;
User::LeaveIfError(lsSession.Connect());
CleanupClosePushL(lsSession);
RFileWriteStream fStream;
// Dump to the local cache
User::LeaveIfError(
fStream.Open(fs,
*(aFe.CacheFileName()),
EFileWrite | EFileShareAny));
CleanupClosePushL(fStream);
containerSize = fStream.Sink()->SizeL();
TInt i;
TLex lex;
for (i = 0; i < aDirEnts->Count(); i++)
{
CRsfwDirEnt* d = (*aDirEnts)[i];
TUid appUid;
// For each TDirEnt we just read...
// ... if the server returned content-type
if (d->Attr()->MimeType() && d->Attr()->MimeType()->Length())
{
err = lsSession.AppForDataType(*(d->Attr()->MimeType()),
appUid);
if (err == KErrNone)
{
d->Attr()->SetUid(appUid);
}
}
d->Attr()->SetAttFlags(KEntryAttRemote);
CRsfwFileEntry* kidFep = aFe.FindKidByName(*d->Name());
if (kidFep)
{
// We already know this kid
// However we must check whether the kid has been modified
CRsfwDirEntAttr* oldAttr = CRsfwDirEntAttr::NewLC();
kidFep->GetAttributesL(*oldAttr);
if (DataChanged(*oldAttr, *d->Attr()))
{
kidFep->RemoveCacheFile();
}
CleanupStack::PopAndDestroy(oldAttr);
if (kidFep->IsFullyCached())
{
// Mark the kid as cached
d->Attr()->ResetAttFlags(KEntryAttRemote);
}
// as this entry is "used", move it to the back of metadata LRU list
iVolume->iVolumeTable->MoveToTheBackOfMetadataLRUPriorityListL(kidFep);
}
// As a side effect,
// insert this kid into the file table and
// set its attributes
if (!kidFep)
{
if (!iVolume->iVolumeTable->EnsureMetadataCanBeAddedL(&aFe))
{
User::Leave(KErrNoMemory);
}
kidFep = CRsfwFileEntry::NewL(*d->Name(), &aFe);
// Attach the new kid
aFileEngine->iFileTable->AddL(kidFep);
aFe.AddKid(*kidFep);
}
kidFep->Mark();
// set attributes if getting directory listing also supports getting file attributes
if (DirectoryListingContainsFileMetadata())
{
kidFep->SetAttributesL(*d->Attr(), ETrue);
}
else
{
kidFep->SetAttributesL(*d->Attr(), EFalse);
}
TDirEnt dirEnt;
MakeDirectoryEntry(*kidFep, dirEnt);
dirEnt.ExternalizeL(fStream);
kidsCount++;
}
aFe.DropUnmarkedKidsL();
containerSize = fStream.Sink()->SizeL();
// assumes that this fetch will write the whole directory,
// i.e. there is no partial fetching for the directories
if(!iFileTable->Volume()->iVolumeTable->
EnsureCacheCanBeAddedL(containerSize))
{
User::Leave(KErrDiskFull);
}
fStream.CommitL();
CleanupStack::PopAndDestroy(2, &lsSession); // fStream, lsSession
// if the directory appeared to be childless add it to metadata LRU list
if ( aDirEnts->Count() == 0 )
{
iVolume->iVolumeTable->AddToMetadataLRUPriorityListL(&aFe, ECachePriorityNormal);
}
}// if directory
// assumes the files are cached in continuos chunks,
// i.e. always cached up to the last byte fetched
aFe.SetCachedSize(containerSize);
aFe.SetCached(ETrue);
// We have to update locally dirty bit for the parent container
if (aFe.Parent())
{
aFe.Parent()->SetLocallyDirty();
}
// But the object itself cannot be remotely dirty any more
aFe.ResetRemotelyDirty();
// *** from CRsfwFileEngine::DoFetch ***
if (aFe.Type() == KNodeTypeDir)
{
// the reason why kidsCount may be different than aFe.Kids.Count is that for big directories
// some kids could have been removed when adding the others to memory. this is due to memory management cap.
// however this should not happen so often
aFe.KidsCount() == kidsCount ? aFe.iUseCachedData = ETrue : aFe.iUseCachedData = EFalse;
}
else
{
aFe.iUseCachedData = ETrue;
}
return containerSize;
}
// ----------------------------------------------------------------------------
// CRsfwFileEngine::RemoteAccessL
// ----------------------------------------------------------------------------
//
CRsfwRemoteAccess* CRsfwFileEngine::RemoteAccessL()
{
DEBUGSTRING(("CRsfwFileEngine::RemoteAccessL"));
if (!iRemoteAccess)
{
User::Leave(KErrNotReady);
}
// Prevent the inactivity timer from triggering
// in the middle of a remote access operation
StopInactivityTimer();
return iRemoteAccess;
}
// ----------------------------------------------------------------------------
// CRsfwFileEngine::OperationCompleted
// ----------------------------------------------------------------------------
//
void CRsfwFileEngine::OperationCompleted()
{
DEBUGSTRING(("File engine operation completed"));
if (iVolume->iVolumeTable->iPermanence)
{
iFileTable->SaveMetaDataDelta();
}
if (iLockManager && (iLockManager->LockedCount() == 0))
{
// Start timer only if we don't have files open for writing
StartInactivityTimer();
}
iVolume->OperationCompleted();
}
// ----------------------------------------------------------------------------
// CRsfwFileEngine::CancelTransaction
// ----------------------------------------------------------------------------
//
void CRsfwFileEngine::CancelTransaction(TUint iTransactionId)
{
DEBUGSTRING(("CRsfwFileEngine::CancelTransactionL"));
if (iRemoteAccess)
{
iRemoteAccess->Cancel(iTransactionId);
}
}
// ----------------------------------------------------------------------------
// CRsfwFileEngine::CancelTransaction
// ----------------------------------------------------------------------------
//
void CRsfwFileEngine::CancelTransactionL(TDesC& aPathName)
{
DEBUGSTRING(("CRsfwFileEngine::CancelTransactionL"));
TPtrC testPtr;
testPtr.Set(aPathName.Right(aPathName.Length() - 3));
HBufC* cancelPath = HBufC::NewLC(KMaxPath);
TPtr cancelPathPtr = cancelPath->Des();
// change '\\' to '/' so the path matches
TLex parser(testPtr);
TChar theChar;
for (int i = 0; i < testPtr.Length(); i++)
{
theChar = parser.Get();
if (theChar == 0)
{
break;
}
// assumes that the input string always has "\\" and not just "\"
// this is true as the input is a file path
if (theChar != '\\')
{
cancelPathPtr.Append(theChar);
}
else
{
cancelPathPtr.Append('/');
}
}
if (iRemoteAccess)
{
iRemoteAccess->Cancel(*cancelPath);
}
CleanupStack::PopAndDestroy(cancelPath);
}
// ----------------------------------------------------------------------------
// CRsfwFileEngine::SetFailedLookup
// Caches the last failed lookup result
// ----------------------------------------------------------------------------
//
void CRsfwFileEngine::SetFailedLookup(TDesC& aPath, TDesC& aKidName)
{
iLastFailedLookup = aPath;
iLastFailedLookup.Append('/');
iLastFailedLookup.Append(aKidName);
iLookupTime.HomeTime();
DEBUGSTRING16(("SetFailedLookup: %S", &iLastFailedLookup));
}
// ----------------------------------------------------------------------------
// CRsfwFileEngine::ResetFailedLookup
// Clears the last failed lookup result
// ----------------------------------------------------------------------------
//
void CRsfwFileEngine::ResetFailedLookup()
{
DEBUGSTRING16(("ResetFailedLookup: %S", &iLastFailedLookup));
iLastFailedLookup.Zero();
}
// ----------------------------------------------------------------------------
// CRsfwFileEngine::Volume
// ----------------------------------------------------------------------------
//
CRsfwVolume* CRsfwFileEngine::Volume()
{
return iVolume;
}
// ----------------------------------------------------------------------------
// CRsfwFileEngine::PrepareCacheL
// ----------------------------------------------------------------------------
//
void CRsfwFileEngine::PrepareCacheL()
{
// make sure the file cache (of this volume) exists
iCacheRoot.Copy(CRsfwRfeServer::Env()->iCacheRoot);
iCacheRoot.Append('C');
iCacheRoot.AppendNum(iVolume->iMountInfo.iMountStatus.iVolumeId);
iCacheRoot.Append('\\');
if (! BaflUtils::FileExists(iFs, iCacheRoot))
{
// There was no prior cache directory
TInt err = iFs.MkDirAll(iCacheRoot);
DEBUGSTRING(("Cache directory created with err=%d", err));
User::LeaveIfError(err);
}
}
// ----------------------------------------------------------------------------
// CRsfwFileEngine::GetDirectoryL
// ----------------------------------------------------------------------------
//
TUint CRsfwFileEngine::GetDirectoryL(CRsfwFileEntry& /*aFe*/,
TDesC& aFullName,
RFile& /*aF*/,
RPointerArray<CRsfwDirEnt>* aDirEntsp,
MRsfwRemoteAccessResponseHandler* aCaller)
{
return RemoteAccessL()->GetDirectoryL(aFullName, *aDirEntsp, aCaller);
}
// ----------------------------------------------------------------------------
// CRsfwFileEngine::BuildContainerPathL
// ----------------------------------------------------------------------------
//
void CRsfwFileEngine::BuildContainerPathL(CRsfwFileEntry& aFe, TDes& aPath)
{
if (aPath.MaxLength() < (aPath.Length() + iCacheRoot.Length()))
{
aPath.Copy(iCacheRoot);
}
else
{
User::Leave(KErrOverflow);
}
ApplyMultiDirCacheL(aPath);
// This filename tagging based on container type is just for convenience
if (aFe.Type() == KNodeTypeFile)
{
aPath.Append('F');
}
else
{
aPath.Append('D');
}
aPath.AppendNum((TInt)aFe.Fid().iNodeId);
}
// ----------------------------------------------------------------------------
// CRsfwFileEngine::ApplyMultiDirCachePathL
// Due to Symbian performance problems with huge directories, items will not
// be stored in one directory in the cache.
// Now instead one dir like:
// C:\system\data\rsfw_cache\C16
// there will be dirs like:
// C:\system\data\rsfw_cache\C16\M0
// C:\system\data\rsfw_cache\C16\M1
// ... and so on
// ----------------------------------------------------------------------------
//
void CRsfwFileEngine::ApplyMultiDirCacheL(TDes& aPath)
{
// maximum number of items in a single dir in the cache
const TInt KRsfwMaxItemsInDir = 100;
TInt i;
// this loop will surely break (or leave) at some point
for ( i = 0; ; i++ )
{
// create path like "C:\system\data\rsfw_cache\C16\M0"
HBufC* trypath = HBufC::NewMaxL(KMaxPath);
TPtr pathPtr = trypath->Des();
pathPtr.Copy(aPath);
pathPtr.Append('M');
pathPtr.AppendNum(i);
pathPtr.Append('\\');
// check whether dir exists and if so, how many items it contains
CDir* dir = NULL;
// note that KEntryAttDir att means files & directories
TInt err = iFs.GetDir(*trypath, KEntryAttDir, ESortNone, dir);
if ( err == KErrNone )
{
// count the items
TInt count = dir->Count();
delete dir;
dir = NULL;
//limit is not exceeded -> return the path
if ( count < KRsfwMaxItemsInDir )
{
aPath.Copy(pathPtr);
delete trypath;
break;
}
// limit exceeded -> let's try the next dir
else
{
delete trypath;
continue;
}
}
else if ( err == KErrPathNotFound )
{
// create dir and return the path to empty dir
err = iFs.MkDir(*trypath);
if (!err)
{
aPath.Copy(pathPtr);
delete trypath;
}
else
{
delete trypath;
DEBUGSTRING(("Error when creating cache dir! err=%d", err));
User::Leave(KErrGeneral);
}
break;
}
else
{
delete trypath;
DEBUGSTRING(("Cache directory cannot be created! err=%d", err));
User::Leave(KErrGeneral);
}
}
}
// ----------------------------------------------------------------------------
// CRsfwFileEngine::CreateContainerFileL
// ----------------------------------------------------------------------------
//
void CRsfwFileEngine::CreateContainerFileL(CRsfwFileEntry& aFe,
TDes& aPath,
RFile& aF)
{
// Create a container file for the Fid.
// If the cache file already exists, it will be deleted
BuildContainerPathL(aFe, aPath);
TInt err = aF.Replace(iFs, aPath, EFileShareAny | EFileWrite);
if (err != KErrNone)
{
User::Leave(KErrGeneral);
}
aF.Close();
aFe.SetCacheFileName(&aPath);
}
// ----------------------------------------------------------------------------
// CRsfwFileEngine::DoIoctlL
// ----------------------------------------------------------------------------
//
void CRsfwFileEngine::DoIoctlL(TRfeIoctlInArgs& aIn, TRfeOutArgs& /* aOut */)
{
TFid fidp = aIn.iFid;
TInt cmd = aIn.iCmd;
TInt err = KErrNone;
DEBUGSTRING(("ioctl fid %d - command=%d, data=%d",
fidp.iNodeId,
cmd,
aIn.iData32[0]));
CRsfwFileEntry* fep = iFileTable->Lookup(fidp);
if (fep)
{
switch (cmd)
{
case ERemoteFsIoctlRefresh:
if (fep->Type() == KNodeTypeFile)
{
fep->SetCacheFileName(NULL);
fep->SetCached(EFalse);
// There is a change in the parent's container
fep->Parent()->SetLocallyDirty();
}
break;
case ERemoteFsHighCachePriority:
default:
err = KErrArgument;
break;
}
}
else
{
err = KErrNotFound;
}
if (err != KErrNone)
{
User::Leave(err);
}
return;
}
// ----------------------------------------------------------------------------
// CRsfwFileEngine::DoRootL
// ----------------------------------------------------------------------------
//
void CRsfwFileEngine::DoRootL(TRfeRootInArgs& /* aIn */, TRfeRootOutArgs& aOut)
{
SetupRootL(iVolume->iVolumeTable->iPermanence);
aOut.iFid.iVolumeId = iRootFid->iVolumeId;
aOut.iFid.iNodeId = iRootFid->iNodeId;
return;
}
// ----------------------------------------------------------------------------
// CRsfwFileEngine::DoSetAttrL
// ----------------------------------------------------------------------------
//
void CRsfwFileEngine::DoSetAttrL(TRfeSetAttrInArgs& aIn, TRfeOutArgs& /* aOut */)
// We cannot really set anything but this is the way to implement this
// note that if this is implemented, it should really be a state machine
{
TInt err = KErrNone;
TFid fidp = aIn.iFid;
#ifdef _DEBUG
TDirEntAttr* attrp = &(aIn.iAttr);
#endif
DEBUGSTRING(("setting attributes of fid %d, attr=0x%x, size=%d, time=",
fidp.iNodeId,
attrp->iAtt,
attrp->iSize));
DEBUGTIME((attrp->iModified));
// Get the file or directory to setattr
CRsfwFileEntry* fep = iFileTable->Lookup(fidp);
if (fep)
{
err = KErrNotSupported;
}
else
{
err = KErrNotFound;
}
User::Leave(err);
return;
}
// ----------------------------------------------------------------------------
// CRsfwFileEngine::SetupRootL
// ----------------------------------------------------------------------------
//
void CRsfwFileEngine::SetupRootL(TBool aPermanence)
{
_LIT(KRootPath, "."); // dummy
if (!iRootFid)
{
CRsfwFileEntry* root = NULL;
TInt err;
if (aPermanence)
{
TRAP(err, root = iFileTable->LoadMetaDataL());
}
if (err == KErrCorrupt)
{
DEBUGSTRING(("Metadata corrupted! Recreating cache file..."));
// corrupted cache file, recreate filetable and cache file
delete iFileTable;
iFileTable = NULL;
CleanupCorruptedCacheL();
PrepareCacheL();
iFileTable = CRsfwFileTable::NewL(iVolume, iCacheRoot);
}
if (!aPermanence || (err != KErrNone))
{
root = CRsfwFileEntry::NewL(KRootPath, NULL);
// Insert root into the file table
iFileTable->AddL(root);
root->SetType(KNodeTypeDir);
}
if (aPermanence)
{
iFileTable->SaveMetaDataDelta();
}
iRootFep = root;
iRootFid = &(root->Fid());
}
}
// ----------------------------------------------------------------------------
// CRsfwFileEngine::CleanupCorruptedCacheL
// ----------------------------------------------------------------------------
//
void CRsfwFileEngine::CleanupCorruptedCacheL()
{
// delete everything from the cache
TFileName cachepath;
cachepath.Copy(iCacheRoot);
CFileMan* fileMan = CFileMan::NewL(iFs);
fileMan->Delete(cachepath, CFileMan::ERecurse);
delete fileMan;
}
// ----------------------------------------------------------------------------
// CRsfwFileEngine::ConnectL
// ----------------------------------------------------------------------------
//
TUint CRsfwFileEngine::ConnectL(TBool aRestart, CRsfwRfeStateMachine* aCaller)
{
// Assume parameter format:
// protocol://username:password@server:port/rootdir or
// The ":password", ":port", and "[/]rootdir" can be omitted.
// If the length of password parameter is bigger than 1,
// it overrides the one in uri, if any.
// Characters can be quoted with %<hexdigit><hexdigit> format
TUint transactionId = 0;
if (iRemoteAccess)
{
// We already have a remote accessor
if (aRestart)
{
// Restarting
delete iLockManager;
iLockManager = NULL;
delete iRemoteAccess;
iRemoteAccess = NULL;
}
else
{
User::Leave(KErrAlreadyExists);
}
}
DEBUGSTRING16(("ConnectL(): '%S'",
&iVolume->iMountInfo.iMountConfig.iUri));
TUriParser uriParser;
User::LeaveIfError(uriParser.Parse(iVolume->iMountInfo.iMountConfig.iUri));
TPtrC userName;
TPtrC password;
TPtrC friendlyName;
if (uriParser.IsPresent(EUriUserinfo))
{
TPtrC userInfo(uriParser.Extract(EUriUserinfo));
// Split the user info into user name and password (seprated by ':')
TInt pos = userInfo.Locate(':');
if (pos != KErrNotFound)
{
password.Set(userInfo.Mid(pos + 1));
userName.Set(userInfo.Left(pos));
}
else
{
userName.Set(userInfo);
}
}
HBufC* userNameBuf = NULL;
if (!userName.Length() &&
iVolume->iMountInfo.iMountConfig.iUserName.Length())
{
// separate user name overwrites the username embedded in the URI
userName.Set(iVolume->iMountInfo.iMountConfig.iUserName);
}
HBufC* passwordBuf = NULL;
if (!password.Length() &&
(iVolume->iMountInfo.iMountConfig.iPassword.Length() > 1))
{
// separate password overwrites the password embedded in the URI
password.Set(iVolume->iMountInfo.iMountConfig.iPassword);
}
friendlyName.Set(iVolume->iMountInfo.iMountConfig.iName);
TPtrC scheme(uriParser.Extract(EUriScheme));
HBufC8* protocol = HBufC8::NewLC(scheme.Length());
TPtr8 protocolPtr = protocol->Des();
protocolPtr.Copy(scheme);
iRemoteAccess = CRsfwRemoteAccess::NewL(protocolPtr);
CleanupStack::PopAndDestroy(protocol);
// user name and password are conveyed separately from the URI
CUri* uri = CUri::NewLC(uriParser);
uri->RemoveComponentL(EUriUserinfo);
// leaves if error
iRemoteAccess->SetupL(this);
transactionId = iRemoteAccess->
OpenL(uri->Uri(),
friendlyName,
userName,
password,
iVolume->iMountInfo.iMountConfig.iAuxData,
aCaller);
CleanupStack::PopAndDestroy(uri);
if (passwordBuf)
{
CleanupStack::PopAndDestroy(passwordBuf);
}
if (userNameBuf)
{
CleanupStack::PopAndDestroy(userNameBuf);
}
// lock manager can be created before we know whether connecting was
// succesful - however it must be deleted upon unsuccesful connect
if (!iLockManager)
{
iLockManager = CRsfwLockManager::NewL(iRemoteAccess);
}
if ((iInactivityTimeout > 0) && !iInactivityTimer)
{
iInactivityTimer = CPeriodic::NewL(CActive::EPriorityLow);
}
return transactionId;
}
// ----------------------------------------------------------------------------
// CRsfwFileEngine::DisconnectL
// ----------------------------------------------------------------------------
//
void CRsfwFileEngine::DisconnectL()
{
DEBUGSTRING(("CRsfwFileEngine::DisconnectL"));
if (iRemoteAccess)
{
iRemoteAccess->Cancel(0);
delete iRemoteAccess;
iRemoteAccess = NULL;
}
if (iLockManager)
{
delete iLockManager;
iLockManager = NULL;
}
// Set open file count to zero
// If there are open files, after disconnecting we do not necessarily
// get close events.
// Note that this variable is not "dirty bit" (file has currently
// uncommitted modifications), so it is safe to set it to zero
TInt openfiles = iFileTable->OpenFileCount();
iFileTable->UpdateOpenFileCount(-openfiles);
EnteredConnectionStateL(KMountNotConnected, ETrue);
// publish connection status when disconnecting
iVolume->iVolumeTable->PublishConnectionStatus(iVolume);
}
// ----------------------------------------------------------------------------
// CRsfwFileEngine::StartInactivityTimer
// ----------------------------------------------------------------------------
//
void CRsfwFileEngine::StartInactivityTimer()
{
if (iInactivityTimer)
{
DEBUGSTRING(("inactivity timer started (%d us)",
iInactivityTimeout));
iInactivityTimer->Cancel();
TCallBack callBack(CRsfwFileEngine::InactivityTimerExpired, this);
iInactivityTimer->Start(iInactivityTimeout,
iInactivityTimeout,
callBack);
}
}
// ----------------------------------------------------------------------------
// CRsfwFileEngine::StopInactivityTimer
// ----------------------------------------------------------------------------
//
void CRsfwFileEngine::StopInactivityTimer()
{
DEBUGSTRING(("CRsfwFileEngine::StopInactivityTimer"));
if (iInactivityTimer)
{
DEBUGSTRING(("inactivity timer stopped"));
iInactivityTimer->Cancel();
}
}
// ----------------------------------------------------------------------------
// CRsfwFileEngine::InactivityTimerExpired
// ----------------------------------------------------------------------------
//
TInt CRsfwFileEngine::InactivityTimerExpired(TAny* aArg)
{
DEBUGSTRING(("CRsfwFileEngine::InactivityTimerExpired"));
CRsfwFileEngine* fileEngine = static_cast<CRsfwFileEngine*>(aArg);
if (fileEngine->iFileTable->OpenFileCount() == 0)
{
fileEngine->StopInactivityTimer();
TRAP_IGNORE(fileEngine->DisconnectL());
// "Simulate" operation completion (which may result in RFE shutdown)
fileEngine->OperationCompleted();
}
else
{
// if there are open files on this volume, just restart the inactivity timer
fileEngine->StartInactivityTimer();
}
return 0;
}
// ----------------------------------------------------------------------------
// CRsfwFileEngine::HandleRemoteAccessEventL
// ----------------------------------------------------------------------------
//
void CRsfwFileEngine::HandleRemoteAccessEventL(TInt aEventType,
TInt aEvent,
TAny* /* aArg */)
{
DEBUGSTRING(("Handle remote access event: %d/%d in connection state %d",
aEventType,
aEvent,
iConnectionState));
switch (aEventType)
{
case ERsfwRemoteAccessObserverEventConnection:
switch (aEvent)
{
case ERsfwRemoteAccessObserverEventConnectionDisconnected:
EnteredConnectionStateL(KMountNotConnected, EFalse);
break;
case ERsfwRemoteAccessObserverEventConnectionWeaklyConnected:
#if 0
// This event does not appear
EnteredConnectionStateL(KMountWeaklyConnected, EFalse);
#endif
break;
case ERsfwRemoteAccessObserverEventConnectionStronglyConnected:
#if 0
// This event does not appear
EnteredConnectionStateL(KMountStronglyConnected, EFalse);
#endif
break;
default:
break;
}
break;
default:
break;
}
}
// ----------------------------------------------------------------------------
// CRsfwFileEngine::PurgeFromCacheL
// ----------------------------------------------------------------------------
//
TInt CRsfwFileEngine::PurgeFromCache(const TDesC& aPath)
{
// get the fid of the entry for which the cached data is removed
CRsfwFileEntry* targetFid = FetchFep(aPath);
if (!targetFid)
{
return KErrPathNotFound;
}
// only directories can be refreshed currently
if (targetFid->Type() != KNodeTypeDir)
{
return KErrArgument;
}
targetFid->SetCached(EFalse);
return KErrNone;
}
CRsfwFileEntry* CRsfwFileEngine::FetchFep(const TDesC& aPath)
{
DEBUGSTRING16(("CRsfwFileEngine::FetchFep for file %S", &aPath));
if (aPath.Length() <= 1)
{
DEBUGSTRING(("returning rootFep"));
return iRootFep;
}
else
{
TInt delimiterPos = aPath.LocateReverse(KPathDelimiter);
if (delimiterPos == (aPath.Length() - 1))
{
// The path ends with a slash,
//i.e. this is a directory - continue parsing
TPtrC nextdelimiter;
nextdelimiter.Set(aPath.Left(delimiterPos));
delimiterPos = nextdelimiter.LocateReverse(KPathDelimiter);
}
TPtrC entry(aPath.Right(aPath.Length() - (delimiterPos + 1)));
TPtrC path(aPath.Left(delimiterPos + 1));
// strip a trailing backslash if found
delimiterPos = entry.LocateReverse(KPathDelimiter);
if (delimiterPos == (entry.Length() - 1))
{
TPtrC stripped(entry.Left(entry.Length() - 1));
return (FetchFep(path)->FindKidByName(stripped));
}
else
{
return (FetchFep(path)->FindKidByName(entry));
}
}
}
HBufC8* CRsfwFileEngine::GetContentType(TDesC& aName)
{
TInt err;
RApaLsSession lsSession;
err = lsSession.Connect();
if (err)
{
return NULL;
}
RFs fsSession;
err = fsSession.Connect();
if (err)
{
lsSession.Close();
return NULL;
}
fsSession.ShareProtected();
TDataRecognitionResult dataType;
RFile theFile;
// the mode must mach the mode that is used in the file system plugin
// (EFileWrite|EFileShareAny)
err = theFile.Open(fsSession, aName, EFileWrite|EFileShareAny);
if (err)
{
lsSession.Close();
fsSession.Close();
return NULL;
}
err = lsSession.RecognizeData(theFile, dataType);
lsSession.Close();
theFile.Close();
fsSession.Close();
if (err)
{
return NULL;
}
return dataType.iDataType.Des8().Alloc();
}
// The purpose of these functions is to give capability info
// for the access protocol plugin used.
// Currently this information is hard coded and takes into account webdav and upnp
// access modules. New function should be added to the access plugin api to get
// this information from the protocol module
// whether getting the directory listing also gives reliable file metadata
TBool CRsfwFileEngine::DirectoryListingContainsFileMetadata()
{
_LIT(KUPnP, "upnp");
if (iVolume->MountInfo()->iMountConfig.iUri.Left(4) == KUPnP)
{
return EFalse;
}
else
{
return ETrue;
}
}