diff -r 0aa8cc770c8a -r 4a793f564d72 connectivitymodules/SeCon/services/ftp/src/sconftp.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/connectivitymodules/SeCon/services/ftp/src/sconftp.cpp Wed Sep 01 12:20:56 2010 +0100 @@ -0,0 +1,1588 @@ +/* +* Copyright (c) 2005-2010 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: File Transfer Controller implementation +* +*/ + + +// INCLUDE FILES + +#include +#include +#include +#include +#include +#include +#include + +#include "debug.h" +#include "sconftp.h" +#include "sconfshandler.h" +#include "sconinboxhandler.h" +#include "sconconsts.h" + +const TInt KPackageSize = 65536; + +// ============================= MEMBER FUNCTIONS =============================== + + +// ----------------------------------------------------------------------------- +// CreateCSConFTPL() +// Entry to CSConFTP +// ----------------------------------------------------------------------------- +// +EXPORT_C CSConFTP* CreateCSConFTPL() + { + TRACE_FUNC; + return CSConFTP::NewL(); + } + +// ----------------------------------------------------------------------------- +// CSConFTP::NewL() +// Two-phase constructor +// ----------------------------------------------------------------------------- +// +CSConFTP* CSConFTP::NewL() + { + TRACE_FUNC_ENTRY; + CSConFTP* self = new (ELeave) CSConFTP(); + CleanupStack::PushL( self ); + self->ConstructL(); + CleanupStack::Pop( self ); + TRACE_FUNC_EXIT; + return self; + } + +// ----------------------------------------------------------------------------- +// CSConFTP::~CSConFTP() +// Destructor +// ----------------------------------------------------------------------------- +// +CSConFTP::~CSConFTP() + { + TRACE_FUNC_ENTRY; + delete iSConInboxHandler; + iSConInboxHandler = NULL; + delete iSConFsHandler; + iSConFsHandler = NULL; + delete iBuffer; + iBuffer = NULL; + + iFs.Delete( iTempFileName ); + iFs.Close(); + TRACE_FUNC_EXIT; + } + +// ----------------------------------------------------------------------------- +// CSConFTP::GetFolderObjectL( CObexBufObject*& aObjectList ) +// Gets object that contains folder listing from inbox or file system +// ----------------------------------------------------------------------------- +// +TInt CSConFTP::GetFolderObjectL( CObexBufObject*& aObjectList ) + { + TRACE_FUNC_ENTRY; + + TInt ret( KErrNone ); + TInt offset = 0; + TChar driveLetter; + TBuf8 tmpBuf8; + + iBuffer->Reset(); + + //default folder list - lists all available drives, inbox + if( iPathName.Length() == 0 ) + { + TInt devDriveCount = 0; + TInt mmcDriveCount = 0; + + iBuffer->ResizeL( KSConXmlDocBegin().Length() ); + iBuffer->Write( offset, KSConXmlDocBegin ); + offset += KSConXmlDocBegin().Length(); + + //Write all drivers to folderlisting object + TDriveList driveList; + // Get all drives that are visible to the user. + TInt driveCount; + User::LeaveIfError( DriveInfo::GetUserVisibleDrives( iFs, driveList, driveCount ) ); + + for( TInt i = EDriveA; i < KMaxDrives; i++ ) + { + if( driveList[i] ) + { + TUint driveStatus; + User::LeaveIfError( DriveInfo::GetDriveStatus( iFs, i, driveStatus ) ); + LOGGER_WRITE_1( "DriveInfo for drive: %d", i); + + if( !(driveStatus & DriveInfo::EDrivePresent ) + || driveStatus & DriveInfo::EDriveCorrupt + || (driveStatus & DriveInfo::EDriveRemote) ) + { + LOGGER_WRITE_1( "skip drive %d", i); + continue; + } + + User::LeaveIfError( iFs.DriveToChar( i, driveLetter ) ); + + //Letter to uppercase form. + driveLetter.UpperCase(); + + //Folder begin + iBuffer->ExpandL( offset, + KSConXmlFolderNameBegin().Length() ); + iBuffer->Write( offset, KSConXmlFolderNameBegin ); + offset += KSConXmlFolderNameBegin().Length(); + + iBuffer->ExpandL( offset, KSConDriveCName().Length() ); + + tmpBuf8.Delete( 0, tmpBuf8.Length() ); + tmpBuf8.Append( driveLetter ); + tmpBuf8.Append( KDriveDelimiter ); + + iBuffer->Write( offset, tmpBuf8 ); + offset += KSConDriveNameLength; + + //permission + iBuffer->ExpandL( offset, + KSConXmlUserAttributes().Length() ); + iBuffer->Write( offset, KSConXmlUserAttributes ); + offset += KSConXmlUserAttributes().Length(); + if( driveStatus & DriveInfo::EDriveInternal && devDriveCount==0 ) + { + //permission always R for first internal drive + iBuffer->ExpandL( offset, + KSConXmlUserEntryReadOnly().Length() ); + iBuffer->Write( offset, KSConXmlUserEntryReadOnly ); + offset += KSConXmlUserEntryReadOnly().Length(); + } + else + { + //permission always RW + iBuffer->ExpandL( offset, + KSConXmlUserEntryDrive().Length() ); + iBuffer->Write( offset, KSConXmlUserEntryDrive ); + offset += KSConXmlUserEntryDrive().Length(); + } + + //memory type + iBuffer->ExpandL( offset, + KSConXmlMemoryType().Length() ); + iBuffer->Write( offset, KSConXmlMemoryType ); + offset += KSConXmlMemoryType().Length(); + if( driveStatus & DriveInfo::EDriveInternal ) + { + LOGGER_WRITE( "DriveInfo::EDriveInternal" ); + // DEV type + devDriveCount++; + if( devDriveCount > 1 ) + { + // DEV2 + TBuf8 memType; + memType.Copy( KSConMemoryTypeDev ); + memType.AppendNum(devDriveCount); + iBuffer->ExpandL( offset, memType.Length() ); + iBuffer->Write( offset, memType ); + offset += memType.Length(); + } + else + { + // DEV + iBuffer->ExpandL( offset, + KSConMemoryTypeDev().Length() ); + iBuffer->Write( offset, KSConMemoryTypeDev ); + offset += KSConMemoryTypeDev().Length(); + } + } + if( driveStatus & DriveInfo::EDriveRemovable ) + { + LOGGER_WRITE( "DriveInfo::EDriveRemovable" ); + // MMC type + mmcDriveCount++; + if( mmcDriveCount > 1 ) + { + // MMC2 + TBuf8 memType; + memType.Copy( KSConMemoryTypeMmc ); + memType.AppendNum(mmcDriveCount); + iBuffer->ExpandL( offset, memType.Length() ); + iBuffer->Write( offset, memType ); + offset += memType.Length(); + } + else + { + // MMC + iBuffer->ExpandL( offset, + KSConMemoryTypeMmc().Length() ); + iBuffer->Write( offset, KSConMemoryTypeMmc ); + offset += KSConMemoryTypeMmc().Length(); + } + } + + //memory label + iBuffer->ExpandL( offset, + KSConXmlMemoryLabel().Length() ); + iBuffer->Write( offset, KSConXmlMemoryLabel ); + offset += KSConXmlMemoryLabel().Length(); + + TInt err( KErrNotFound ); + TBuf8 volumeName; + if( driveStatus & DriveInfo::EDriveRemovable || devDriveCount == 1 ) + { + // get Volume name for C-drive(Phone) and E-drive(MMC) + err = GetVolumeNameL( i, volumeName ); + } + else + { + // Get localized name for DEV2 (Mass memory) + //Read memory string and convert it + TFileName file( KSConResourceName ); + + BaflUtils::NearestLanguageFile( iFs, file ); + + CStringResourceReader* reader = CStringResourceReader::NewL( file ); + + CnvUtfConverter::ConvertFromUnicodeToUtf8( volumeName, + reader->ReadResourceString( R_SECON_VALUE_MASS_STORAGE ) ); + + delete reader; + err = KErrNone; + } + + if ( err == KErrNone ) + { + iBuffer->ExpandL( offset, + volumeName.Length() ); + iBuffer->Write( offset, volumeName ); + offset += volumeName.Length(); + } + + //Write the end of filelist to folderlisting object + iBuffer->ExpandL( offset, KSConXmlFileEnd().Length() ); + iBuffer->Write( offset, KSConXmlFileEnd ); + offset += KSConXmlFileEnd().Length(); + } + } + //Write the end of folderlist to folderlisting object + iBuffer->ExpandL( offset, KSConXmlFolderListEnd().Length() ); + iBuffer->Write( offset, KSConXmlFolderListEnd ); + iBuffer->Compress(); + } + else + { + if( IsCurrentVolumeOK() ) + { + ret = iSConFsHandler->ParseFolderListL( iBuffer, iPathName, iCurrentDriveTypeNumber ); + } + else + { + ret = KErrNotFound; + } + } + + //Put data to aObjectList + delete aObjectList; + aObjectList = NULL; + + aObjectList = CObexBufObject::NewL( iBuffer ); + aObjectList->SetTypeL( KSConFolderListType ); + aObjectList->SetLengthL( iBuffer->Size() ); + + LOGGER_WRITE_1( "CSConFTP::GetFolderObjectL( CObexBufObject*& aObjectList ) : returned %d", ret ); + return ret; + } + +// ----------------------------------------------------------------------------- +// CSConFTP::GetFileObjectL( CObexFileObject*& aFileObject ) +// Gets object that contains a file from file system +// ----------------------------------------------------------------------------- +// +TInt CSConFTP::GetFileObjectL( CObexFileObject*& aFileObject ) + { + TRACE_FUNC_ENTRY; + + TInt ret( KErrNone ); + + if( aFileObject->Name().Length() == 0 ) + { + return KErrBadName; + } + + // check is current drive ok + if( iPathName.Length() > 0 ) + { + TPath path( KSConAllowedPath ); + path.Append( KPathDelimiter ); + if( iPathName.CompareF( path ) != 0 ) + { + // was normal path (not KSConAllowedPath) + if( !IsCurrentVolumeOK() ) + { + return KErrNotFound; + } + } + } + + HBufC* nameBuf = HBufC::NewLC( KMaxFileName ); + TPtr namePtr = nameBuf->Des(); + namePtr.Append( aFileObject->Name() ); + + if( namePtr.LocateReverse( KPathDelimiter ) == namePtr.Length()-1 ) + { + namePtr.Delete( namePtr.Length()-1, 1 ); + } + + ret = iSConFsHandler->GetFileObjectL( aFileObject, iPathName, namePtr ); + + CleanupStack::PopAndDestroy( nameBuf ); + + LOGGER_WRITE_1( "CSConFTP::GetFileObjectL() : returned %d", ret ); + return ret; + } + +// ----------------------------------------------------------------------------- +// CSConFTP::PutFileObjectInitL( CObexBufObject*& aObject, +// CBufFlat*& aBuffer ) +// Initializes the receiving from the client. +// ----------------------------------------------------------------------------- +// +TInt CSConFTP::PutFileObjectInitL( CObexBufObject*& aObject, + CBufFlat*& aBuffer ) + { + TRACE_FUNC_ENTRY; + TInt ret( KErrNone ); + + RFile file; + + TBool lowMemory = IsCurrentDiskSpaceBelowCritical(); + + //if Inbox then save temp file to default drive + if( iPathName.Length() == 0 ) + { + //When proprietary profile (PC Suite), doesn't allow to use Inbox + if( iProfile == EProprietary ) + { + LOGGER_WRITE( "CSConFTP::PutFileObjectInitL() : iProfile EProprietary" ); + ret = file.Temp( iFs, PathInfo::PhoneMemoryRootPath(), iTempFileName, + EFileWrite | EFileShareExclusive ); + file.Close(); + + if( ret == KErrNone ) + { + CreateObexBufObjectL( aObject, aBuffer ); + } + } + else + { + LOGGER_WRITE( "CSConFTP::PutFileObjectInitL() : not EProprietary" ); + if( lowMemory ) + { + // do not even try to save + LOGGER_WRITE( "CSConFTP::PutFileObjectInitL() : disk full" ); + ret = KErrNoMemory; + } + else + { + ret = iSConInboxHandler->CreateInboxAttachmentL( aObject, aBuffer ); + } + } + + return ret; + } + else + { + LOGGER_WRITE_1("iPathName: %S", &iPathName); + // check is current drive ok + if( !IsCurrentVolumeOK() ) + { + LOGGER_WRITE( "CSConFTP::PutFileObjectInitL() : drive not visible" ); + ret = KErrNotFound; + } + else + { + ret = file.Temp( iFs, iPathName, iTempFileName, EFileWrite | + EFileShareExclusive ); + } + } + + file.Close(); + + if( ret == KErrNone ) + { + CreateObexBufObjectL( aObject, aBuffer ); + + if( lowMemory ) + { + // return error. it's up to pccon/ftp plugin howto handle it.. + LOGGER_WRITE( "CSConFTP::PutFileObjectInitL() : low memory" ); + ret = KErrNoMemory; + } + } + + LOGGER_WRITE_1( "CSConFTP::PutFileObjectInitL( CObexBufObject*& aObject, CBufFlat*& aBuffer ) : returned %d", ret ); + return ret; + } + +void CSConFTP::CreateObexBufObjectL( CObexBufObject*& aObject, CBufFlat*& aBuffer ) + { + delete aObject; + aObject = NULL; + aObject = CObexBufObject::NewL( NULL ); + + delete aBuffer; + aBuffer = NULL; + + aBuffer = CBufFlat::NewL( KSConBufferSize ); + aBuffer->ResizeL( KSConBufferSize ); + + TObexFilenameBackedBuffer bufferdetails( *aBuffer, iTempFileName, + CObexBufObject::EDoubleBuffering ); + TRAPD( err, aObject->SetDataBufL( bufferdetails )); + if ( err == KErrNoMemory ) + { + LOGGER_WRITE( "KErrNoMemory, Using singe buffer strategy"); + // If fails, use singe buffer strategy to save RAM + TObexFilenameBackedBuffer lowMemBufferdetails( *aBuffer, iTempFileName, + CObexBufObject::ESingleBuffering ); + aObject->SetDataBufL( lowMemBufferdetails ); + } + } + +// ----------------------------------------------------------------------------- +// CSConFTP::PutFileObjectFinalizeL( CObexBufObject*& aObject ) +// Stores the relayed file object to inbox or file system. +// ----------------------------------------------------------------------------- +// +TInt CSConFTP::PutFileObjectFinalizeL( CObexBufObject*& aObject ) + { + TRACE_FUNC_ENTRY; + TInt ret( KErrNone ); + + // if iPathName is not defined trying to save to inbox, no need to call IsCurrentVolumeOK() + if( iPathName.Length() > 0 && !IsCurrentVolumeOK() ) + { + // Memory card removed + LOGGER_WRITE( "CSConFTP::PutFileObjectFinalizeL() : disk err, return KErrNotFound" ); + return KErrNotFound; + } + else if ( iPathName.Length() + aObject->Name().Length() > KMaxFileName ) + { + LOGGER_WRITE( "CSConFTP::PutFileObjectFinalizeL() : Too long filename, return KErrBadName"); + return KErrBadName; + } + + TBool lowMemory = IsCurrentDiskSpaceBelowCritical(); + if ( lowMemory ) + { + LOGGER_WRITE( "CSConFTP::PutFileObjectFinalizeL() : Low memory, return KErrNoMemory"); + return KErrNoMemory; + } + + HBufC* fullPathBuf = HBufC::NewLC( KMaxFileName ); + TPtr fullPathPtr = fullPathBuf->Des(); + fullPathPtr.Copy( iPathName ); + fullPathPtr.Append( aObject->Name() ); + + // Save to inbox if path is not set. + if( iPathName.Length() == 0 ) + { + LOGGER_WRITE( "CSConFTP::PutFileObjectFinalizeL before SaveObjectToInbox" ); + ret = iSConInboxHandler->SaveObjToInboxL( aObject ); + LOGGER_WRITE_1( "CSConFTP::PutFileObjectFinalize SaveObjectToInbox returned %d", ret ); + } + else if( iPathName.CompareF( K_C_ROOT ) == 0 ) + { + // Saving to root of C:\ is not allowed + ret = KErrAccessDenied; + } + //Save to file system + else + { + TTime time = aObject->Time(); + + // UTC offset + time -= User::UTCOffset(); + + delete aObject; + aObject = NULL; + + ret = iSConFsHandler->SaveFileObjectL( fullPathPtr, time, + iTempFileName ); + } + + CleanupStack::PopAndDestroy( fullPathBuf ); + + LOGGER_WRITE_1( "CSConFTP::PutFileObjectFinalizeL( CObexBufObject*& aObject ) : returned %d", ret ); + + return ret; + } + +// ----------------------------------------------------------------------------- +// CSConFTP::SetPathL( const TPtrC aPath, const TUint8 aFlags ) +// Changes the current path. The path can point to inbox or file system +// ----------------------------------------------------------------------------- +// +TInt CSConFTP::SetPathL( const TPtrC aPath, const TUint8 aFlags ) + { + TRACE_FUNC_ENTRY; + LOGGER_WRITE_1( "aPath: %S", &aPath ); + TInt ret( KErrNone ); + + if( ( aPath.Length() > 0 && aFlags != KSConSetPathRoot ) + || ( iPathName.Length() == KDriveLength && aFlags == KSConSetPathBack) ) + { + // check is current drive ok + if( !IsCurrentVolumeOK() ) + { + LOGGER_WRITE( "CSConFTP::SetPathL() : drive not found" ); + return KErrNotFound; + } + } + + ret = KErrNotFound; + + //path forward, deny access to forbidden directories and drives + if( aPath.Length() > 0 + && ( iPathName.Length() + aPath.Length() < KMaxFileName ) + && ( aFlags == KSConSetPathForward || aFlags == KSConSetPathDontCreate ) ) + { + ret = SetPathForwardL( aPath, aFlags ); + } + //path backward + else if( aFlags == KSConSetPathBack ) + { + ret = SetPathBackwardL( aPath ); + } + //path root + else if( aPath.Length() == 0 && aFlags == KSConSetPathRoot ) + { + LOGGER_WRITE( "CSConFTP::SetPathL() : KSConSetPathRoot" ); + if( iPathName.Length() > 0 ) + { + iPathName.Delete( 0, iPathName.Length() ); + } + iCurrentDrive = KErrNotFound; + ret = KErrNone; + } + + if( ret == KErrNone ) + { + // drive may have changed, update info + UpdateDriveTypeInfoL(); + } + + LOGGER_WRITE_1( "CSConFTP::CSConFTP::SetPathL( const TPtrC aPath, const TUint8 aFlags ) : returned %d", ret ); + return ret; + } + +// ----------------------------------------------------------------------------- +// CSConFTP::SetPathForwardL( const TPtrC aPath, const TUint8 aFlags ) +// Set path forward +// ----------------------------------------------------------------------------- +// +TInt CSConFTP::SetPathForwardL( const TPtrC aPath, const TUint8 aFlags ) + { + TRACE_FUNC_ENTRY; + TInt ret(KErrNone); + HBufC* fullPath = HBufC::NewLC( KMaxFileName ); + TPtr fullPathPtr = fullPath->Des(); + + if( iPathName.Length() == 0 ) + { + fullPathPtr.Copy( aPath ); + } + else + { + fullPathPtr.Copy( iPathName ); + fullPathPtr.Append( aPath ); + } + LOGGER_WRITE_1( "fullPathPtr: %S", &fullPathPtr ); + // now fullpath is new path + // check validity + if( fullPathPtr.Length() < 2 || fullPathPtr[1] != KDriveDelimiter ) + { + LOGGER_WRITE( "CSConFTP::SetPathForwardL() : not valid name" ); + CleanupStack::PopAndDestroy( fullPath ); + return KErrNotFound; + } + + if( fullPathPtr.CompareF( KSConAllowedPath ) == 0 ) + { + // Always access to PC Suite configuration file on ROM + LOGGER_WRITE( "CSConFTP::SetPathForwardL() : KSConAllowedPath" ); + iPathName.Copy( fullPathPtr ); + if( iPathName[iPathName.Length()-1] != KPathDelimiter ) + { + iPathName.Append( KPathDelimiter ); + } + + CleanupStack::PopAndDestroy( fullPath ); + return KErrNone; + } + + // check drive + TInt driveNumber; + TInt err = iFs.CharToDrive( fullPathPtr[0], driveNumber ); + if( err != KErrNone || !IsDriveVisible( driveNumber ) ) + { + LOGGER_WRITE( "CSConFTP::SetPathForwardL() : drive err" ); + CleanupStack::PopAndDestroy( fullPath ); + return KErrNotFound; + } + + // check folder and create it if needed + if( fullPathPtr.Length() > 2 ) + { + TBool isFolder = EFalse; + err = BaflUtils::IsFolder( iFs, fullPathPtr, isFolder ); + if( err != KErrNone ) + { + isFolder = EFalse; + } + LOGGER_WRITE_1( "CSConFTP::SetPathForwardL() IsFolder: %d", (TInt)isFolder ); + + if( !isFolder ) + { + //if "Don't create" is 1 + if( aFlags & 2 ) + { + ret = KErrNotFound; + } + else + { + CreateFolderL( fullPathPtr ); + ret = KErrNone; + } + } + else + { + ret = KErrNone; + } + } + else + { + ret = KErrNone; + } + + if( ret == KErrNone ) + { + iPathName.Copy( fullPathPtr ); + + if( iPathName[iPathName.Length()-1] != KPathDelimiter ) + { + iPathName.Append( KPathDelimiter ); + } + } + + CleanupStack::PopAndDestroy( fullPath ); + LOGGER_WRITE_1( "CSConFTP::SetPathForwardL() : end, ret: %d", ret ); + return ret; + + } + +// ----------------------------------------------------------------------------- +// CSConFTP::SetPathBackwardL( const TPtrC aPath ) +// Set path backward +// ----------------------------------------------------------------------------- +// +TInt CSConFTP::SetPathBackwardL( const TPtrC aPath ) + { + TRACE_FUNC_ENTRY; + TInt ret(KErrNotFound); + if( iPathName.Length() == 0 ) + { + // already in root, exit + LOGGER_WRITE( "CSConFTP::SetPathBackwardL() : already in root" ); + LOGGER_WRITE_1( "CSConFTP::SetPathBackwardL() : end, ret: %d", ret ); + return ret; + } + TInt pos; + TBool isFolder(EFalse); + HBufC* fullPath = HBufC::NewLC( KMaxFileName ); + TPtr fullPathPtr = fullPath->Des(); + + //no directory/folder name + if( aPath.Length() == 0 ) + { + LOGGER_WRITE( "CSConFTP::SetPathBackwardL() : no new path, goto parent" ); + iPathName.Delete( iPathName.Length()-1, 1 ); + pos = iPathName.LocateReverse( KPathDelimiter ); + iPathName.Delete( pos+1, iPathName.Length()-pos ); + ret = KErrNone; + } + //if folder name + else if( aPath.Locate( KDriveDelimiter ) != 1 && iPathName.Length() > KDriveLength ) + { + LOGGER_WRITE( "CSConFTP::SetPathBackwardL() : goto parent and new path" ); + fullPathPtr.Copy( iPathName ); + fullPathPtr.Delete( fullPathPtr.Length()-1, 1 ); + pos = fullPathPtr.LocateReverse( KPathDelimiter ); + fullPathPtr.Delete( pos+1, fullPathPtr.Length()-pos ); + fullPathPtr.Append( aPath ); + + if( fullPathPtr.CompareF( KSConAllowedPath ) == 0 ) + { + // Always access to PC Suite configuration file on ROM + isFolder = ETrue; + } + else + { + TInt driveNumber; + TInt err = iFs.CharToDrive(fullPathPtr[0], driveNumber); + if( err != KErrNone ) + { + LOGGER_WRITE( "CSConFTP::SetPathForwardL() : drive err" ); + isFolder = EFalse; + } + else if( !IsDriveVisible(driveNumber) ) + { + isFolder = EFalse; + } + else + { + // drive ok, check folder. + err = BaflUtils::IsFolder( iFs, fullPathPtr, isFolder ); + if( err != KErrNone ) + { + isFolder = EFalse; + } + } + } + + if( isFolder ) + { + iPathName.Copy( fullPathPtr ); + + if( aPath.LocateReverse( KPathDelimiter ) != aPath.Length()-1 ) + { + iPathName.Append( KPathDelimiter ); + } + + ret = KErrNone; + } + else + { + ret = KErrNotFound; + } + + } + //drive + else if( aPath.Locate( KDriveDelimiter ) == 1 ) + { + fullPathPtr.Copy( aPath ); + if( fullPathPtr.LocateReverse( KPathDelimiter ) + != fullPathPtr.Length()-1 ) + { + fullPathPtr.Append( KPathDelimiter ); + } + + TFileName tempPath( KSConAllowedPath ); + tempPath.Append(KPathDelimiter); + if( fullPathPtr.CompareF( tempPath ) == 0 ) + { + // Always access to PC Suite configuration file on ROM + isFolder = ETrue; + } + else + { + TInt driveNumber; + TInt err = iFs.CharToDrive(fullPathPtr[0], driveNumber); + if( err != KErrNone ) + { + LOGGER_WRITE( "CSConFTP::SetPathForwardL() : drive err" ); + isFolder = EFalse; + } + else if( !IsDriveVisible(driveNumber) ) + { + isFolder = EFalse; + } + else + { + // drive ok, check folder. + err = BaflUtils::IsFolder( iFs, fullPathPtr, isFolder ); + if( err != KErrNone ) + { + isFolder = EFalse; + } + } + } + + if( isFolder ) + { + iPathName.Copy( fullPathPtr ); + + if( aPath.LocateReverse( KPathDelimiter ) != aPath.Length()-1 ) + { + iPathName.Append( KPathDelimiter ); + } + + ret = KErrNone; + } + else + { + ret = KErrNotFound; + } + } + CleanupStack::PopAndDestroy( fullPath ); + LOGGER_WRITE_1( "CSConFTP::SetPathBackwardL() : end, ret: %d", ret ); + return ret; + } + +// ----------------------------------------------------------------------------- +// CSConFTP::CreateFolderL( const TPtrC aFolderName ) +// Creates a new folder to file system +// ----------------------------------------------------------------------------- +// +TInt CSConFTP::CreateFolderL( const TPtrC aFolderName ) + { + TRACE_FUNC_ENTRY; + LOGGER_WRITE_1( "aFolderName: %S", &aFolderName ); + TInt ret( KErrNone ); + if( aFolderName.Length() == 0 ) + { + return KErrArgument; + } + + HBufC* pathBuf = HBufC::NewLC( KMaxFileName ); + TPtr pathBufPtr = pathBuf->Des(); + //absolute folder path + if( aFolderName.Length() > 1 && aFolderName[1] == KDriveDelimiter ) + { + if ( iPathName.Length()>0 ) + { + LOGGER_WRITE( "CSConFTP::CreateFolderL() : KErrBadName" ); + CleanupStack::PopAndDestroy( pathBuf ); + return KErrBadName; + } + pathBufPtr.Copy( aFolderName ); + } + else + { + pathBufPtr.Copy( iPathName ); + pathBufPtr.Append( aFolderName ); + } + + if( !iFs.IsValidName( pathBufPtr ) ) + { + ret = KErrBadName; + } + else + { + // check drive + TInt driveNumber; + ret = iFs.CharToDrive(pathBufPtr[0], driveNumber); + if( ret != KErrNone || !IsDriveVisible(driveNumber) ) + { + LOGGER_WRITE( "CSConFTP::CreateFolderL() : drive err" ); + ret = KErrNotFound; + } + } + + if ( ret == KErrNone ) + { + if( pathBufPtr.LocateReverse( KPathDelimiter ) != pathBufPtr.Length()-1 ) + { + pathBufPtr.Append( KPathDelimiter ); + } + ret = iSConFsHandler->CreateFolderL( pathBufPtr ); + } + + if( ret == KErrNone || ret == KErrAlreadyExists ) + { + ret = SetPathL( aFolderName, KSConSetPathForward ); + } + + CleanupStack::PopAndDestroy( pathBuf ); + + LOGGER_WRITE_1( "CSConFTP::CreateFolderL( const TPtrC aFolderName ) : returned %d", ret ); + + return ret; + } + +// ----------------------------------------------------------------------------- +// CSConFTP::DeleteObjectL( const TPtrC aObjectName ) +// Deletes file/folder from inbox or file system +// ----------------------------------------------------------------------------- +// +TInt CSConFTP::DeleteObjectL( const TPtrC aObjectName ) + { + TRACE_FUNC_ENTRY; + LOGGER_WRITE_1( "aObjectName: %S", &aObjectName ); + TInt ret( KErrNone ); + TFileName tmpTarget; + HBufC* targetBuf = HBufC::NewLC( KMaxFileName ); + TPtr target = targetBuf->Des(); + + //absolute folder path + if( aObjectName.Length() > 1 && aObjectName[1] == KDriveDelimiter ) + { + if ( iPathName.Length()>0 ) + { + LOGGER_WRITE( "CSConFTP::DeleteObjectL() : KErrBadName" ); + CleanupStack::PopAndDestroy( targetBuf ); + return KErrBadName; + } + target.Copy( aObjectName ); + } + else + { + target.Copy( iPathName ); + target.Append( aObjectName ); + } + + if( !iFs.IsValidName( target ) ) + { + ret = KErrBadName; + } + else + { + TInt driveNumber; + ret = iFs.CharToDrive(target[0], driveNumber); + if( ret != KErrNone || !IsDriveVisible(driveNumber) ) + { + LOGGER_WRITE( "CSConFTP::DeleteObjectL() : drive err" ); + ret = KErrNotFound; + } + } + + + if ( ret == KErrNone ) + { + ret = iSConFsHandler->DeleteObjectL( target ); + } + + CleanupStack::PopAndDestroy( targetBuf ); + + LOGGER_WRITE_1( "CSConFTP::DeleteObjectL( const TPtrC aObjectName ) : returned %d", ret ); + return ret; + } + +// ----------------------------------------------------------------------------- +// CSConFTP::GetPath( TDes& aPath ) +// Gets the current path +// ----------------------------------------------------------------------------- +// +TInt CSConFTP::GetPath( TDes& aPath ) + { + TRACE_FUNC; + aPath.Copy( iPathName ); + return KErrNone; + } + +// ----------------------------------------------------------------------------- +// CSConFTP::AbortFileTransfer( CObexBufObject*& aObject ) +// Abort file transfer +// ----------------------------------------------------------------------------- +// +TInt CSConFTP::AbortFileTransfer( CObexBufObject*& aObject ) + { + TRACE_FUNC_ENTRY; + TInt err( KErrNone ); + + if( iPathName.Length() == 0 ) + { + iSConInboxHandler->AbortInboxOperation( aObject ); + } + + //aObject has to be deleted, otherwise the temp file is locked + delete aObject; + aObject = NULL; + + err = iFs.Delete( iTempFileName ); + + LOGGER_WRITE_1( "CSConFTP::AbortFileTransfer( CObexBufObject*& aObject ) : iFs.Delete() err: %d", err ); + return err; + } + +// ----------------------------------------------------------------------------- +// CSConFTP::SetProfile( TInt aProfile ) +// Set used transfer profile +// ----------------------------------------------------------------------------- +// +void CSConFTP::SetProfile( TSConProfile aProfile ) + { + TRACE_FUNC; + iProfile = aProfile; + } + +// ----------------------------------------------------------------------------- +// CSConFTP::SetBackupStarted( TBool aValue ) +// Set backup status +// ----------------------------------------------------------------------------- +// +void CSConFTP::SetBackupStarted( TBool aValue ) + { + TRACE_FUNC; + iBackupStarted = aValue; + } + +// ----------------------------------------------------------------------------- +// CSConFTP::SetUsedMedia( TSConUsedMedia aMedia ) +// Set the used media information +// ----------------------------------------------------------------------------- +// +void CSConFTP::SetUsedMedia( TSConUsedMedia aMedia ) + { + TRACE_FUNC; + iMedia = aMedia; + } + +// ----------------------------------------------------------------------------- +// CSConFTP::IsCurrentDiskSpaceBelowCritical( TUint32 aFilesize ) +// Check is current disk space below critical level. +// ----------------------------------------------------------------------------- +// +TBool CSConFTP::IsCurrentDiskSpaceBelowCritical( TUint32 aFilesize ) + { + TRACE_FUNC_ENTRY; + LOGGER_WRITE_1( "aFilesize: %d", aFilesize ); + TInt drive; + TInt err; + + if( iCurrentDrive != KErrNotFound ) + { + drive = iCurrentDrive; + } + else + { + LOGGER_WRITE( "CSConFTP::IsCurrentDiskSpaceBelowCritical() : drive not specified, use default drive" ); + err = DriveInfo::GetDefaultDrive( DriveInfo::EDefaultPhoneMemory, drive ); + if( err ) + { + LOGGER_WRITE_1( "CSConFTP::IsCurrentDiskSpaceBelowCriticaL() : DriveInfo::GetDefaultDrive err %d", err ); + return ETrue; //exit + } + } + + // check disk space + LOGGER_WRITE_1( "drive: %d", drive ); + TVolumeInfo volumeInfo; + err = iFs.Volume(volumeInfo, drive); + if( err != KErrNone ) + { + LOGGER_WRITE_1( "CSConFTP::IsCurrentDiskSpaceBelowCritical() : iFs.Volume err %d", err ); + LOGGER_WRITE( "CSConFTP::IsCurrentDiskSpaceBelowCritical() : End" ); + return ETrue; //exit + } + LOGGER_WRITE_1( "volumeInfo.iFree: %Ld", volumeInfo.iFree ); + TBool diskLevelCritical( EFalse ); + if ( volumeInfo.iFree - aFilesize <= iCriticalDiskLevel ) + { + // Can not write the data, there's not enough free space on disk. + diskLevelCritical = ETrue; + } + + LOGGER_WRITE_1( "CSConFTP::IsCurrentDiskSpaceBelowCritical() : ret %d", + (TInt)diskLevelCritical ); + return diskLevelCritical; + } + +// ----------------------------------------------------------------------------- +// CSConFTP::ReadWBXMLDataL( CBufFlat*& aBuffer ) +// Read received ConML protocol packet +// ----------------------------------------------------------------------------- +// +TInt CSConFTP::ReadWBXMLDataL( CBufFlat*& aBuffer ) + { + TRACE_FUNC_ENTRY; + TInt ret( KErrNone ); + + RFile file; + TInt fileSize; + + ret = file.Open( iFs, iTempFileName, EFileRead|EFileShareAny ); + CleanupClosePushL( file ); + if( ret == KErrNone ) + { + file.Size( fileSize ); + + delete aBuffer; + aBuffer = NULL; + + HBufC8* wbxmlDataBuf = HBufC8::NewLC( fileSize ); + TPtr8 wbxmlDataPtr = wbxmlDataBuf->Des(); + + file.Read( wbxmlDataPtr ); + + aBuffer = CBufFlat::NewL( KSConBufSize ); + aBuffer->ExpandL( 0, fileSize ); + aBuffer->Write( 0, wbxmlDataPtr ); + + CleanupStack::PopAndDestroy( wbxmlDataBuf ); + } + CleanupStack::PopAndDestroy( &file ); + LOGGER_WRITE_1( "CSConFTP::ReadWBXMLData( CBufFlat*& aBuffer ) : WBXML packet size: %d", fileSize ); + + LOGGER_WRITE_1( "CSConFTP::ReadWBXMLData( CBufFlat*& aBuffer ) : returned %d", ret ); + return ret; + } + +// ----------------------------------------------------------------------------- +// CSConFTP::DeleteTempFile() +// Delete OBEX stack's temp file +// ----------------------------------------------------------------------------- +// +void CSConFTP::DeleteTempFile() + { + TRACE_FUNC_ENTRY; + iFs.Delete( iTempFileName ); + TRACE_FUNC_EXIT; + } + +// ----------------------------------------------------------------------------- +// CSConFTP::MoveFile() +// Move file/folder +// ----------------------------------------------------------------------------- +// +TInt CSConFTP::MoveFile(const TDesC& aSource, const TDesC& aTarget) + { + TRACE_FUNC_ENTRY; + TInt err; + TFileName tmpSource; + TFileName tmpTarget; + err = GetAbsolutePath( aSource, tmpSource ); + if( err == KErrNone ) + { + err = GetAbsolutePath( aTarget, tmpTarget ); + } + + if( err == KErrNone ) + { + TInt sourceDrive; + TInt targetDrive; + + err = iFs.CharToDrive( tmpSource[0], sourceDrive ); + TInt err2 = iFs.CharToDrive( tmpTarget[0], targetDrive ); + + if( err != KErrNone || err2 != KErrNone + || !IsDriveVisible( sourceDrive ) + || !IsDriveVisible( targetDrive ) ) + { + // drive not visible to user + err = KErrNotFound; + } + } + + if( err == KErrNone ) + { + TRAP( err, + iSConFsHandler->DoCopyOrMoveFileL( tmpSource, tmpTarget, EFalse ) ); + } + + LOGGER_WRITE_1( "CSConFTP::MoveFile() : end, err: %d", err ); + return err; + } + +// ----------------------------------------------------------------------------- +// CSConFTP::CopyFile() +// Copy file/folder +// ----------------------------------------------------------------------------- +// +TInt CSConFTP::CopyFile(const TDesC& aSource, const TDesC& aTarget) + { + TRACE_FUNC_ENTRY; + TInt err; + TFileName tmpSource; + TFileName tmpTarget; + err = GetAbsolutePath( aSource, tmpSource ); + if (err == KErrNone) + { + err = GetAbsolutePath( aTarget, tmpTarget ); + } + + if( err == KErrNone ) + { + TInt sourceDrive; + TInt targetDrive; + + err = iFs.CharToDrive( tmpSource[0], sourceDrive ); + TInt err2 = iFs.CharToDrive( tmpTarget[0], targetDrive ); + + if( err != KErrNone || err2 != KErrNone + || !IsDriveVisible( sourceDrive ) + || !IsDriveVisible( targetDrive ) ) + { + // drive not visible to user + err = KErrNotFound; + } + } + + if (err == KErrNone) + { + TRAP( err, + iSConFsHandler->DoCopyOrMoveFileL( tmpSource, tmpTarget, ETrue ) ); + } + + LOGGER_WRITE_1( "CSConFTP::CopyFile() : end, ret: %d", err ); + return err; + } + +// ----------------------------------------------------------------------------- +// CSConFTP::SetReadOnly() +// Set read-only permissions to file or folder +// ----------------------------------------------------------------------------- +// +TInt CSConFTP::SetReadOnly(const TDesC& aTarget, const TBool aReadOnly) + { + TRACE_FUNC_ENTRY; + TInt err; + TFileName tmpTarget; + err = GetAbsolutePath( aTarget, tmpTarget ); + if( err != KErrNone ) + { + return err; + } + else + { + TInt targetDrive; + err = iFs.CharToDrive( tmpTarget[0], targetDrive ); + + if( err != KErrNone + || !IsDriveVisible( targetDrive ) ) + { + // drive not visible to user + return KErrNotFound; + } + } + + TBool isFolder(EFalse); + err = BaflUtils::IsFolder( iFs, tmpTarget, isFolder ); + if ( err == KErrNone && isFolder ) + { + tmpTarget.Append(KPathDelimiter); + } + if ( !iSConFsHandler->IsFolderVisible( tmpTarget ) ) + { + // folder is not visible to user + err = KErrAccessDenied; + } + else + { + if( aReadOnly ) + { + err = iFs.SetAtt( tmpTarget, KEntryAttReadOnly, KEntryAttArchive ); + } + else + { + err = iFs.SetAtt( tmpTarget, KEntryAttNormal, KEntryAttReadOnly ); + } + } + + LOGGER_WRITE_1( "CSConFTP::SetReadOnly() : ret: %d", err ); + return err; + } + +// ----------------------------------------------------------------------------- +// CSConFTP::SetHidden() +// Set hidden permissions to file or folder +// ----------------------------------------------------------------------------- +// +TInt CSConFTP::SetHidden( const TDesC& /*aTarget*/, const TBool /*aHidden*/ ) + { + // This is currently not supported. + LOGGER_WRITE("CSConFTP::SetHidden return KErrNotSupported"); + return KErrNotSupported; + } + +// ----------------------------------------------------------------------------- +// CSConFTP::GetAbsolutePath() +// Get absolute path from relative file/folder +// ----------------------------------------------------------------------------- +// +TInt CSConFTP::GetAbsolutePath( const TDesC& aFolderName, TDes &aFullPath ) + { + if (aFolderName.Length() == 0) + { + return KErrBadName; + } + //absolute folder path + if( aFolderName.Length() > 1 && aFolderName[1] == KDriveDelimiter ) + { + aFullPath.Copy( aFolderName ); + } + // relative to the root folder + else if( aFolderName[0] == KPathDelimiter ) + { + if( iPathName.Length() < 2 ) + { + return KErrBadName; + } + aFullPath.Copy( iPathName.Left(2) ); + aFullPath.Append( aFolderName ); + } + // relative to the current folder + else + { + aFullPath.Copy( iPathName ); + if( aFullPath.LocateReverse( KPathDelimiter ) != aFullPath.Length()-1 ) + { + aFullPath.Append( KPathDelimiter ); + } + aFullPath.Append( aFolderName ); + // no need to check internal root, because iPathName is real target. + } + + return KErrNone; + } + +// ----------------------------------------------------------------------------- +// CSConFTP::CSConFTP() +// Default constructor +// ----------------------------------------------------------------------------- +// +CSConFTP::CSConFTP() : iProfile( EStandard ), iCurrentDrive( KErrNotFound ) + { + } + +// ----------------------------------------------------------------------------- +// CSConFTP::ConstructL() +// Initializes member data +// ----------------------------------------------------------------------------- +// +void CSConFTP::ConstructL() + { + TRACE_FUNC_ENTRY; + + iBuffer = CBufFlat::NewL( KSConBufSize ); + User::LeaveIfError( iFs.Connect() ); + iSConFsHandler = CSConFsHandler::NewL( iFs ); + iSConInboxHandler = CSConInboxHandler::NewL(); + + CRepository* repository = CRepository::NewLC( KCRUidDiskLevel ); + User::LeaveIfError( repository->Get( KDiskCriticalThreshold, iCriticalDiskLevel ) ); + CleanupStack::PopAndDestroy( repository ); + // inlcude package size + iCriticalDiskLevel += KPackageSize; + LOGGER_WRITE_1( "criticalLevel: %d", iCriticalDiskLevel ); + + TRACE_FUNC_EXIT; + } + +// ----------------------------------------------------------------------------- +// CSConFTP::GetVolumeNameL() +// Get volume name +// ----------------------------------------------------------------------------- +// +TInt CSConFTP::GetVolumeNameL(const TInt aDriveNumber, TDes8& aVolumeName) + { + TRACE_FUNC_ENTRY; + TVolumeInfo volumeInfo; + TInt ret = iFs.Volume( volumeInfo, aDriveNumber ); + + if ( ret == KErrNone) + { + CnvUtfConverter::ConvertFromUnicodeToUtf8( aVolumeName, + volumeInfo.iName ); + //Replace special characters + for( TInt i = 0; i < aVolumeName.Length(); i++ ) + { + switch( aVolumeName[i] ) + { + case '&': + aVolumeName.Delete( i, 1 ); + aVolumeName.Insert( i, KReplace1 ); + break; + case '<': + aVolumeName.Delete( i, 1 ); + aVolumeName.Insert( i, KReplace2 ); + break; + case '>': + aVolumeName.Delete( i, 1 ); + aVolumeName.Insert( i, KReplace3 ); + break; + case '"': + aVolumeName.Delete( i, 1 ); + aVolumeName.Insert( i, KReplace4 ); + break; + case '\'': + aVolumeName.Delete( i, 1 ); + aVolumeName.Insert( i, KReplace5 ); + break; + default: + break; + } + } + + //No name + if( aVolumeName.Length() == 0 ) + { + LOGGER_WRITE( "Volume has no name, use default localized name" ); + //Read memory string and convert it + TFileName file( KSConResourceName ); + + BaflUtils::NearestLanguageFile( iFs, file ); + + CStringResourceReader* reader = CStringResourceReader::NewL( file ); + CleanupStack::PushL( reader ); + TUint driveStatus; + User::LeaveIfError( DriveInfo::GetDriveStatus( iFs, aDriveNumber, driveStatus ) ); + if( driveStatus & DriveInfo::EDriveRemovable ) + { + // read default MMC name + CnvUtfConverter::ConvertFromUnicodeToUtf8( aVolumeName, + reader->ReadResourceString( R_SECON_VALUE_MMC ) ); + } + else + { + // read default DEV name + CnvUtfConverter::ConvertFromUnicodeToUtf8( aVolumeName, + reader->ReadResourceString( R_SECON_VALUE_DEVICE ) ); + } + CleanupStack::PopAndDestroy( reader ); + } + } + LOGGER_WRITE_1("GetVolumeNameL returned: %d", ret); + return ret; + } + +// ----------------------------------------------------------------------------- +// CSConFTP::IsDriveVisible() +// Check is drive visible for user +// ----------------------------------------------------------------------------- +// +TBool CSConFTP::IsDriveVisible( const TInt aDriveNumber ) + { + TUint driveStatus; + TInt err = DriveInfo::GetDriveStatus( iFs, aDriveNumber, driveStatus); + if( err ) + { + LOGGER_WRITE_1( "CSConFTP::IsDriveVisible() : DriveInfo::GetDriveStatus err: %d", err ); + return EFalse; + } + if( driveStatus & DriveInfo::EDriveRemote ) + { + LOGGER_WRITE( "CSConFTP::IsDriveVisible() : remote drive" ); + return EFalse; + } + if( !(driveStatus & DriveInfo::EDriveUserVisible) ) + { + LOGGER_WRITE( "CSConFTP::IsDriveVisible() : not visible" ); + return EFalse; + } + if( !(driveStatus & DriveInfo::EDrivePresent ) ) + { + LOGGER_WRITE( "CSConFTP::IsDriveVisible() : not present" ); + return EFalse; + } + if( driveStatus & DriveInfo::EDriveCorrupt ) + { + LOGGER_WRITE( "CSConFTP::IsDriveVisible() : corrupted" ); + return EFalse; + } + else + { + return ETrue; + } + } + +// ----------------------------------------------------------------------------- +// CSConFTP::UpdateDriveTypeInfoL() +// Test is current volume still ok. +// ----------------------------------------------------------------------------- +// +TBool CSConFTP::IsCurrentVolumeOK() + { + if( iCurrentDrive == KErrNotFound ) + { + LOGGER_WRITE( "CSConFTP::IsCurrentVolumeOK() : not set" ); + return EFalse; + } + + TUint driveStatus; + TInt err = DriveInfo::GetDriveStatus( iFs, iCurrentDrive, driveStatus); + if( err ) + { + LOGGER_WRITE_1( "CSConFTP::IsCurrentVolumeOK() : DriveInfo::GetDriveStatus err: %d", err ); + return EFalse; + } + if( !(driveStatus & DriveInfo::EDrivePresent )) + { + LOGGER_WRITE( "CSConFTP::IsCurrentVolumeOK() : not present" ); + return EFalse; + } + if( driveStatus & DriveInfo::EDriveCorrupt ) + { + LOGGER_WRITE( "CSConFTP::IsCurrentVolumeOK() : corrupted" ); + return EFalse; + } + else + { + return ETrue; + } + } + +// ----------------------------------------------------------------------------- +// CSConFTP::UpdateDriveTypeInfoL() +// Updates drive information (iCurrentDrive and iCurrentDriveTypeNumber) +// ----------------------------------------------------------------------------- +// +void CSConFTP::UpdateDriveTypeInfoL() + { + TRACE_FUNC_ENTRY; + TInt driveNumber; + iCurrentDriveTypeNumber = 0; + + if( iPathName.Length() == 0 ) + { + LOGGER_WRITE( "CSConFTP::UpdateDriveTypeInfoL() : drive not specified" ); + iCurrentDrive = KErrNotFound; + return; + } + + User::LeaveIfError( iFs.CharToDrive(iPathName[0], driveNumber) ); + iCurrentDrive = driveNumber; + + TUint driveStatus; + User::LeaveIfError( DriveInfo::GetDriveStatus( iFs, iCurrentDrive, driveStatus ) ); + // if true, search internal drives, else search removable drives + TBool searchInternalDrives = (driveStatus & DriveInfo::EDriveInternal); + + TInt driveCount; + TDriveList driveList; + + User::LeaveIfError( DriveInfo::GetUserVisibleDrives( iFs, driveList, driveCount ) ); + + for( TInt i = EDriveA; i <= iCurrentDrive; i++ ) + { + if( driveList[i] ) + { + TUint driveStatus; + User::LeaveIfError( DriveInfo::GetDriveStatus( iFs, i, driveStatus ) ); + + if( !(driveStatus & DriveInfo::EDrivePresent ) + || driveStatus & DriveInfo::EDriveCorrupt ) + { + LOGGER_WRITE( "not present or corrupted" ); + continue; + } + + if( driveStatus & DriveInfo::EDriveInternal ) + { + if( searchInternalDrives ) + { + iCurrentDriveTypeNumber++; + } + } + else if( driveStatus & DriveInfo::EDriveRemovable ) + { + if( !searchInternalDrives ) + { + iCurrentDriveTypeNumber++; + } + } + } + } + TRACE_FUNC_EXIT; + } + +// End of file +