remotestoragefw/remotefileengine/src/rsfwfileengine.cpp
branchRCL_3
changeset 19 88ee4cf65e19
parent 16 87c71b25c937
child 20 1aa8c82cb4cb
equal deleted inserted replaced
16:87c71b25c937 19:88ee4cf65e19
     1 /*
       
     2 * Copyright (c) 2003-2006 Nokia Corporation and/or its subsidiary(-ies).
       
     3 * All rights reserved.
       
     4 * This component and the accompanying materials are made available
       
     5 * under the terms of "Eclipse Public License v1.0"
       
     6 * which accompanies this distribution, and is available
       
     7 * at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     8 *
       
     9 * Initial Contributors:
       
    10 * Nokia Corporation - initial contribution.
       
    11 *
       
    12 * Contributors:
       
    13 *
       
    14 * Description:  Operation independent remote file handling functions
       
    15 *
       
    16 */
       
    17 
       
    18 
       
    19 #include <apgcli.h>
       
    20 #include <bautils.h>
       
    21 
       
    22 #include "rsfwfileentry.h"
       
    23 #include "rsfwfiletable.h"
       
    24 #include "rsfwvolumetable.h"
       
    25 #include "rsfwvolume.h"
       
    26 #include "rsfwrfestatemachine.h"
       
    27 #include "rsfwinterface.h"
       
    28 #include "rsfwcontrol.h"
       
    29 #include "rsfwremoteaccess.h"
       
    30 #include "rsfwfileengine.h"
       
    31 #include "rsfwrfeserver.h"
       
    32 #include "rsfwlockmanager.h"
       
    33 #include "mdebug.h"
       
    34 #include "rsfwdirent.h"
       
    35 #include "rsfwdirentattr.h"
       
    36 #include "rsfwinterface.h"
       
    37 
       
    38 // ----------------------------------------------------------------------------
       
    39 // CRsfwFileEngine::NewL
       
    40 // ----------------------------------------------------------------------------
       
    41 //
       
    42 CRsfwFileEngine* CRsfwFileEngine::NewL(CRsfwVolume* aVolume)
       
    43     {
       
    44     CRsfwFileEngine* self = CRsfwFileEngine::NewLC(aVolume);
       
    45     CleanupStack::Pop(self);
       
    46     return self;
       
    47     }
       
    48 
       
    49 // ----------------------------------------------------------------------------
       
    50 // CRsfwFileEngine::NewLC
       
    51 // ----------------------------------------------------------------------------
       
    52 //
       
    53 CRsfwFileEngine* CRsfwFileEngine::NewLC(CRsfwVolume* aVolume)
       
    54     {
       
    55     DEBUGSTRING(("CRsfwFileEngine::NewLC"));
       
    56     CRsfwFileEngine* self = new (ELeave) CRsfwFileEngine();
       
    57     DEBUGSTRING(("CRsfwFileEngine: in NewLC 0x%x", self));
       
    58     CleanupStack::PushL(self);
       
    59     self->ConstructL(aVolume);
       
    60     return self;
       
    61     }
       
    62 
       
    63 // ----------------------------------------------------------------------------
       
    64 // CRsfwFileEngine::ConstructL
       
    65 // ----------------------------------------------------------------------------
       
    66 //
       
    67 void CRsfwFileEngine::ConstructL(CRsfwVolume* aVolume)
       
    68     {
       
    69     iRemoteAccess = NULL;
       
    70     iRootFid = NULL;
       
    71     iRootFep = NULL;
       
    72     iVolume = aVolume;
       
    73     iFs = CRsfwRfeServer::Env()->iFs;
       
    74     iConnectionState = KMountNotConnected;
       
    75     __ASSERT_ALWAYS(iVolume != NULL, User::Panic(KRfeServer, EConstructingServerStructs));
       
    76     iInactivityTimeout =
       
    77        iVolume->iMountInfo.iMountConfig.iInactivityTimeout * 1000000;
       
    78     PrepareCacheL();
       
    79     // Create file table
       
    80     iFileTable = CRsfwFileTable::NewL(aVolume, iCacheRoot);
       
    81     __ASSERT_ALWAYS(iVolume->iVolumeTable != NULL, User::Panic(KRfeServer, 
       
    82     			EConstructingServerStructs));
       
    83     SetupRootL(iVolume->iVolumeTable->iPermanence);
       
    84     }
       
    85 
       
    86 // ----------------------------------------------------------------------------
       
    87 // CRsfwFileEngine::~CRsfwFileEngine
       
    88 // ----------------------------------------------------------------------------
       
    89 //
       
    90 CRsfwFileEngine::~CRsfwFileEngine()
       
    91     {
       
    92     DEBUGSTRING(("CRsfwFileEngine destructor"));
       
    93     delete iFileTable;
       
    94     delete iRemoteAccess;
       
    95     delete iLockManager;
       
    96     StopInactivityTimer();
       
    97     delete iInactivityTimer;
       
    98     }
       
    99 
       
   100 // ----------------------------------------------------------------------------
       
   101 // CRsfwFileEngine::DispatchL
       
   102 // we should only come here with some synchronous requests
       
   103 // ----------------------------------------------------------------------------
       
   104 //
       
   105 void CRsfwFileEngine::DispatchL(TRfeInArgs& aIn, TRfeOutArgs& aOut)
       
   106     {
       
   107 
       
   108     switch(aIn.iOpCode)
       
   109         {
       
   110     case EFsIoctl:
       
   111         DEBUGSTRING(("IOCTL"));
       
   112         DoIoctlL(static_cast<TRfeIoctlInArgs&>(aIn),
       
   113                  aOut);
       
   114         break;
       
   115 
       
   116     case EFsRoot:
       
   117         DEBUGSTRING(("ROOT"));
       
   118         DoRootL(static_cast<TRfeRootInArgs&>(aIn),
       
   119                 static_cast<TRfeRootOutArgs&>(aOut));
       
   120         break;
       
   121 
       
   122     case ESetAttr:
       
   123         DEBUGSTRING(("SETATTR"));
       
   124         DoSetAttrL(static_cast<TRfeSetAttrInArgs&>(aIn),
       
   125                    aOut);
       
   126         break;
       
   127 
       
   128     default:
       
   129         DEBUGSTRING(("WHAT??? - %d", aIn.iOpCode));
       
   130         User::Leave(KErrArgument);
       
   131         break;
       
   132         }
       
   133     }
       
   134 
       
   135 // ----------------------------------------------------------------------------
       
   136 // CRsfwFileEngine::FullNameLC
       
   137 // ----------------------------------------------------------------------------
       
   138 //
       
   139 HBufC* CRsfwFileEngine::FullNameLC(CRsfwFileEntry& aFe)
       
   140     {
       
   141     HBufC* fn = aFe.FullNameLC();
       
   142     return fn;
       
   143     }
       
   144 
       
   145 // ----------------------------------------------------------------------------
       
   146 // CRsfwFileEngine::FullNameL
       
   147 // ----------------------------------------------------------------------------
       
   148 //
       
   149 HBufC* CRsfwFileEngine::FullNameL(CRsfwFileEntry& aFe)
       
   150     {
       
   151     HBufC* fn = FullNameLC(aFe);
       
   152     CleanupStack::Pop(fn);
       
   153     return fn;
       
   154     }
       
   155 
       
   156 // ----------------------------------------------------------------------------
       
   157 // CRsfwFileEngine::SetupAttributes
       
   158 // ----------------------------------------------------------------------------
       
   159 //
       
   160 void CRsfwFileEngine::SetupAttributes(CRsfwFileEntry& aFe)
       
   161     {
       
   162     DEBUGSTRING(("CRsfwFileEngine::SetupAttributes"));
       
   163     // Construct the attributes for a newly created file or directory,
       
   164     // or a file that that was locally modified and just written to the server,
       
   165     // based on local knowledge of time and file size.
       
   166     // We assume that either the file is cached or it is an empty file.
       
   167     // We do not touch the local or protection attributes.
       
   168 
       
   169     TUint att;
       
   170 
       
   171     // Assume that the file type has already been setup
       
   172     if (aFe.Type() == KNodeTypeDir)
       
   173         {
       
   174         att = KEntryAttDir;
       
   175         }
       
   176     else
       
   177         {
       
   178         att = 0;
       
   179         }
       
   180 
       
   181     TTime time;
       
   182     if (aFe.IsCached())
       
   183         {
       
   184         TDesC* cacheNamep = aFe.CacheFileName();
       
   185         RFile f;
       
   186         if (f.Open(iFs, *cacheNamep, EFileShareAny) == KErrNone)
       
   187             {
       
   188             // attribute bits
       
   189             TUint a;
       
   190             f.Att(a);
       
   191 
       
   192             att |= a & KEntryAttReadOnly;
       
   193 
       
   194             if (aFe.Type() == KNodeTypeDir)
       
   195                 {
       
   196                 aFe.SetSize(0);
       
   197                 }
       
   198             else
       
   199                 {
       
   200                 if (aFe.IsFullyCached())
       
   201                     {
       
   202                     // size
       
   203                     TInt siz;
       
   204                     f.Size(siz);
       
   205                     DEBUGSTRING(("File is fully cached, setting size to %d", siz));
       
   206                     aFe.SetSize(siz);
       
   207                     aFe.SetCachedSize(siz);
       
   208                     }
       
   209                 else
       
   210                 	{
       
   211                 	DEBUGSTRING(("File is not fully cached, not touching the size"));
       
   212                 	// file is not fully cached
       
   213                 	// the size cannot be set from the local cache container	
       
   214                 	}
       
   215                 }
       
   216             // modification time
       
   217             f.Modified(time);
       
   218 
       
   219             f.Close();
       
   220             aFe.iUseCachedData = ETrue;
       
   221           }
       
   222         else 
       
   223           {
       
   224           // No cache
       
   225           aFe.SetSize(0);
       
   226           time.HomeTime();        
       
   227           }
       
   228         
       
   229         }
       
   230     else
       
   231         {
       
   232         // No cache
       
   233         aFe.SetSize(0);
       
   234         time.HomeTime();
       
   235         }
       
   236 
       
   237     aFe.SetAtt(att);
       
   238 
       
   239     aFe.SetModified(time);
       
   240     aFe.SetAttribValidationTime();
       
   241     }
       
   242 
       
   243 // ----------------------------------------------------------------------------
       
   244 // CRsfwFileEngine::MakeDirectoryEntry
       
   245 // ----------------------------------------------------------------------------
       
   246 //
       
   247 void CRsfwFileEngine::MakeDirectoryEntry(CRsfwFileEntry& aFe, TDirEnt& aDirEnt)
       
   248     {
       
   249     DEBUGSTRING(("CRsfwFileEngine::MakeDirectoryEntry"));
       
   250     DEBUGSTRING16(("name %S, att %d, size %d", aFe.Name(), aFe.Att(), aFe.Size()));;
       
   251     aDirEnt.Clear();
       
   252     aDirEnt.iName.Copy(*aFe.Name());
       
   253     aDirEnt.iAttr.iAtt = aFe.Att();
       
   254     aDirEnt.iAttr.iSize = aFe.Size();
       
   255     aDirEnt.iAttr.iModified = aFe.Modified();
       
   256     aDirEnt.iAttr.iUid3 = aFe.iUid;
       
   257     }
       
   258 
       
   259 // ----------------------------------------------------------------------------
       
   260 // CRsfwFileEngine::UpdateDirectoryContainerL
       
   261 // ----------------------------------------------------------------------------
       
   262 //
       
   263 void CRsfwFileEngine::UpdateDirectoryContainerL(CRsfwFileEntry& aFe)
       
   264     {
       
   265     // Construct the directory container based on
       
   266     // file table information
       
   267     DEBUGSTRING16(("Update directory container of %d (%S)", aFe.Fid().iNodeId, aFe.Name()));
       
   268 
       
   269     TDesC* cacheNamep = aFe.CacheFileName();
       
   270     if (!cacheNamep)
       
   271         {
       
   272         // There was no prior cache.
       
   273         DEBUGSTRING(("Cache missing!"));
       
   274         User::Leave(KErrGeneral);
       
   275         }
       
   276 
       
   277     RFile f;
       
   278     CleanupClosePushL(f);
       
   279     User::LeaveIfError(f.Replace(iFs,
       
   280                                  *cacheNamep,
       
   281                                  EFileShareAny | EFileWrite));
       
   282     RFileWriteStream fStream(f);
       
   283     CleanupClosePushL(fStream);
       
   284 
       
   285     RPointerArray<CRsfwFileEntry>* kidsp = aFe.Kids();
       
   286     TInt i;
       
   287     if (!(iVolume->iVolumeTable->EnsureCacheCanBeAddedL(
       
   288               sizeof(TEntry) * kidsp->Count())))      
       
   289         {   // pessimistic estimate
       
   290         User::Leave(KErrDiskFull);
       
   291         }
       
   292     for (i = 0; i < kidsp->Count(); i++)
       
   293         {
       
   294         CRsfwFileEntry* kidFep = (*kidsp)[i];
       
   295         TDirEnt dirEnt;
       
   296         MakeDirectoryEntry(*kidFep, dirEnt);
       
   297         dirEnt.ExternalizeL(fStream);      
       
   298         }
       
   299     CleanupStack::PopAndDestroy(2, &f); // f
       
   300 
       
   301     aFe.ResetLocallyDirty();
       
   302     }
       
   303 
       
   304 // ----------------------------------------------------------------------------
       
   305 // CRsfwFileEngine::DataChanged
       
   306 // ----------------------------------------------------------------------------
       
   307 //
       
   308 TInt CRsfwFileEngine::DataChanged(const CRsfwDirEntAttr& aOldAttr,
       
   309                               const CRsfwDirEntAttr& aNewAttr)
       
   310     {
       
   311     // Based on attributes or metadata in general,
       
   312     // tell whether the actual data, if cached,
       
   313     // should be updated
       
   314     if (aOldAttr.Att() == KEntryAttDir)
       
   315         {
       
   316         // use Last Modified (a weak entity tag)
       
   317         if (aOldAttr.Modified() == aNewAttr.Modified())
       
   318             {
       
   319             return EFalse;
       
   320             }
       
   321         else
       
   322             {
       
   323             return ETrue;
       
   324             }
       
   325         }
       
   326     else
       
   327         {
       
   328         // use ETags if available
       
   329         // a strong entity tag
       
   330         if (aOldAttr.ETag() && aNewAttr.ETag())
       
   331             {
       
   332             if (*aOldAttr.ETag() == *aNewAttr.ETag())
       
   333                 {
       
   334                 return EFalse;
       
   335                 }
       
   336             else
       
   337                 {
       
   338                 return ETrue;
       
   339                 }
       
   340             }
       
   341 
       
   342         // use Last Modified (a weak entity tag)
       
   343         // we assume it's file and compare also iSize...
       
   344         if ((aOldAttr.Modified() == aNewAttr.Modified()) &&
       
   345             (aOldAttr.Size() == aNewAttr.Size()))
       
   346             {
       
   347             return EFalse;
       
   348             }
       
   349         else
       
   350             {
       
   351             return ETrue;
       
   352             }
       
   353         }
       
   354     }
       
   355 
       
   356 // ----------------------------------------------------------------------------
       
   357 // CRsfwFileEngine::UseCachedData
       
   358 // ----------------------------------------------------------------------------
       
   359 //
       
   360 TBool CRsfwFileEngine::UseCachedData(CRsfwFileEntry& aFe)
       
   361     {
       
   362     if (!Disconnected())
       
   363         {
       
   364         return aFe.UseCachedData();
       
   365         }
       
   366     else
       
   367         {
       
   368         return ETrue;
       
   369         }
       
   370     }
       
   371 
       
   372 // ----------------------------------------------------------------------------
       
   373 // CRsfwFileEngine::UseCachedAttributes
       
   374 // ----------------------------------------------------------------------------
       
   375 //
       
   376 TBool CRsfwFileEngine::UseCachedAttributes(CRsfwFileEntry& aFe)
       
   377     {
       
   378       if (!Disconnected())
       
   379         {
       
   380         if (aFe.Type() == KNodeTypeDir)
       
   381 			{
       
   382 			return iFileTable->Volume()->iVolumeTable->
       
   383 				IsCachedAttrStillValid(aFe.iAttribValidation);
       
   384 			}
       
   385 		else
       
   386 			{ // file
       
   387 			return iFileTable->Volume()->iVolumeTable->
       
   388 				IsCachedDataStillValid(aFe.iAttribValidation);
       
   389 
       
   390 			}
       
   391         }
       
   392     else
       
   393         {
       
   394         return ETrue;
       
   395         }
       
   396     }
       
   397 
       
   398 // ----------------------------------------------------------------------------
       
   399 // CRsfwFileEngine::GetAttributesL
       
   400 // ----------------------------------------------------------------------------
       
   401 //
       
   402 void CRsfwFileEngine::GetAttributesL(CRsfwFileEntry& aFe,
       
   403                                  CRsfwDirEntAttr*& aAttr,
       
   404                                  TUint aNodeType,
       
   405                                  CRsfwRfeStateMachine* aCaller)
       
   406     {
       
   407     // Gets attributes for File Entry aFe.
       
   408     // Uses either cached attributes (if they are still deemed to be valid), or
       
   409     // fetches the attributes from the server*/
       
   410     DEBUGSTRING(("GetAttributesL"));
       
   411     if ((aFe.Type() == aNodeType) && UseCachedAttributes(aFe))
       
   412         {
       
   413         // Nothing to do
       
   414 
       
   415         if (aFe.IsOpenedForWriting())
       
   416             {
       
   417             // update attributes when we are writing to the file
       
   418             DEBUGSTRING(("volatile attributes"));
       
   419             SetupAttributes(aFe);
       
   420             }
       
   421         else
       
   422             {
       
   423             DEBUGSTRING(("using cached attributes"));
       
   424             }
       
   425         aCaller->HandleRemoteAccessResponse(0, KErrNone); // "file exists"
       
   426         }
       
   427     else
       
   428         {
       
   429         // Refresh attributes
       
   430         UpdateAttributesL(aFe, aAttr, aNodeType, aCaller);
       
   431         }
       
   432     }
       
   433 
       
   434 // ----------------------------------------------------------------------------
       
   435 // CRsfwFileEngine::UpdateAttributesL
       
   436 // ----------------------------------------------------------------------------
       
   437 //
       
   438 void CRsfwFileEngine::UpdateAttributesL(CRsfwFileEntry& aFe,
       
   439                                     CRsfwDirEntAttr*& aAttr,
       
   440                                     TUint aNodeType,
       
   441                                     MRsfwRemoteAccessResponseHandler* aCaller)
       
   442     {
       
   443     // UpdateAttributes doesn't attempt to use cached attributes
       
   444     HBufC* path = FullNameLC(aFe);
       
   445     TPtr p = path->Des();
       
   446     DEBUGSTRING16(("UpdateAttributesL of '%S'", &p));
       
   447 
       
   448 
       
   449     UpdateAttributesL(*path, aAttr, aNodeType, aCaller);
       
   450 
       
   451     CleanupStack::PopAndDestroy(path); // path
       
   452     }
       
   453 
       
   454 // ----------------------------------------------------------------------------
       
   455 // CRsfwFileEngine::UpdateAttributesL
       
   456 // ----------------------------------------------------------------------------
       
   457 //
       
   458 void CRsfwFileEngine::UpdateAttributesL(TDesC& aPath,
       
   459                                     TDesC& aName,
       
   460                                     CRsfwDirEntAttr*& aAttr,
       
   461                                     TUint aNodeType,
       
   462                                     MRsfwRemoteAccessResponseHandler* aCaller)
       
   463     {
       
   464     HBufC* pn = HBufC::NewLC(KMaxPath);
       
   465     TPtr pnPtr = pn->Des();
       
   466 
       
   467     if (aPath.Length())
       
   468         {
       
   469         pnPtr.Copy(aPath);
       
   470         pnPtr.Append('/');
       
   471         }
       
   472     pnPtr.Append(aName);
       
   473 
       
   474     DEBUGSTRING16(("UpdateKidAttributes of '%S'", &pnPtr));
       
   475 
       
   476    	UpdateAttributesL(pnPtr, aAttr, aNodeType, aCaller);
       
   477 
       
   478     CleanupStack::PopAndDestroy(pn);
       
   479     }
       
   480 
       
   481 // ----------------------------------------------------------------------------
       
   482 // CRsfwFileEngine::UpdateAttributesL
       
   483 // ----------------------------------------------------------------------------
       
   484 //
       
   485 void CRsfwFileEngine::UpdateAttributesL(TDesC& aFullPath,
       
   486                                     CRsfwDirEntAttr*& aAttr,
       
   487                                     TUint aNodeType,
       
   488                                     MRsfwRemoteAccessResponseHandler* aCaller)
       
   489 	{
       
   490 
       
   491 	// If we have "recently" found out that this file/dir does NOT exist
       
   492 	// we cache even this negative result. Time limit is cache expiry for
       
   493 	// directory attributes
       
   494 	 if ((aFullPath.Length() > 0) &&    // do not compare root folder (always exists)
       
   495 	 (iLastFailedLookup == aFullPath) &&
       
   496 	 	  (iFileTable->Volume()->iVolumeTable->
       
   497 				IsCachedAttrStillValid(iLookupTime)))
       
   498     	{
       
   499     	if (aNodeType == KNodeTypeDir)
       
   500             {
       
   501             aCaller->HandleRemoteAccessResponse(0, KErrPathNotFound);
       
   502             }
       
   503         else if (aNodeType == KNodeTypeFile)
       
   504             {
       
   505             aCaller->HandleRemoteAccessResponse(0, KErrNotFound);
       
   506             }
       
   507         return;
       
   508 
       
   509     	}
       
   510 
       
   511 	if (!Disconnected())
       
   512         {
       
   513         if (aNodeType == KNodeTypeDir)
       
   514             {
       
   515             RemoteAccessL()->GetDirectoryAttributesL(aFullPath, aAttr, aCaller);
       
   516             }
       
   517         else if (aNodeType == KNodeTypeFile)
       
   518             {
       
   519             RemoteAccessL()->GetFileAttributesL(aFullPath, aAttr, aCaller);
       
   520             }
       
   521         }
       
   522     else
       
   523         {
       
   524         User::Leave(KErrNotFound);
       
   525         }
       
   526 
       
   527 	}
       
   528 
       
   529 // ----------------------------------------------------------------------------
       
   530 // CRsfwFileEngine::CreateContainerFileL
       
   531 // ----------------------------------------------------------------------------
       
   532 //
       
   533 void CRsfwFileEngine::CreateContainerFileL(CRsfwFileEntry& aFe)
       
   534     {
       
   535     // Create a container file for the Fid.
       
   536     // If the cache file already exists, it will be deleted
       
   537 
       
   538     RFile f;
       
   539     HBufC* cachePath = HBufC::NewMaxLC(KMaxPath);
       
   540     TPtr pathPtr = cachePath->Des();
       
   541     BuildContainerPathL(aFe, pathPtr);
       
   542 
       
   543     TInt err = f.Replace(iFs, *cachePath, EFileShareAny | EFileWrite);
       
   544     f.Close();
       
   545     if (err != KErrNone)
       
   546         {
       
   547         DEBUGSTRING(("Error when creating container file! err=%d", err));
       
   548         User::Leave(KErrGeneral);
       
   549         }  
       
   550     aFe.SetCacheFileName(cachePath);
       
   551     CleanupStack::PopAndDestroy(cachePath);
       
   552     }
       
   553 
       
   554 // ----------------------------------------------------------------------------
       
   555 // CRsfwFileEngine::FetchAndCacheL
       
   556 // ----------------------------------------------------------------------------
       
   557 //
       
   558 TUint CRsfwFileEngine::FetchAndCacheL(CRsfwFileEntry& aFe,
       
   559                                   TInt aFirstByte,
       
   560                                   TInt* aLength,
       
   561                                   RPointerArray<CRsfwDirEnt>* aDirEntsp,
       
   562                                   CRsfwRfeStateMachine* aCaller)
       
   563     {
       
   564     // Fetch a file from the remote store and decrypt it if necessary.
       
   565     // The assumption is that the file has not yet been fetched
       
   566     // or has been cached up to the byte indicated by (aFe.iCachedSize - 1)
       
   567     // and filling the cache will continue linearly
       
   568     // i.e. aFirstByte = 0 || aFirstByte = aFe.iCachedSize
       
   569     // Access modules can fetch more than requested, so aLastByte might change.
       
   570 
       
   571     DEBUGSTRING(("Fetch fid %d, bytes %d - %d",
       
   572                  aFe.Fid().iNodeId,
       
   573                  aFirstByte,
       
   574                  aFirstByte + *aLength));
       
   575 
       
   576     TUint transactionId = 0;
       
   577     RFile f;
       
   578     HBufC* fullName = NULL;
       
   579     HBufC* cacheName = HBufC::NewMaxLC(KMaxPath);
       
   580     TPtr cachePtr = cacheName->Des();
       
   581     TInt err;
       
   582     
       
   583     TInt usedCache = iVolume->iVolumeTable->TotalCachedSize();
       
   584 
       
   585     // This much will be added to the cache by this fetch
       
   586     if (!iVolume->iVolumeTable->EnsureCacheCanBeAddedL(*aLength))
       
   587         {
       
   588         User::Leave(KErrDiskFull);
       
   589         }
       
   590 
       
   591     if (!Disconnected())
       
   592         {
       
   593         if (aFe.CacheFileName())
       
   594             {
       
   595             // modify an existing cachefile ...
       
   596             cachePtr = *(aFe.CacheFileName());
       
   597 
       
   598             if (aFe.Type() == KNodeTypeFile)
       
   599                 {
       
   600                 // If the cache file exists,
       
   601                 // we will just continue filling it...
       
   602                 err = f.Open(iFs, *cacheName, EFileShareAny | EFileWrite);
       
   603                 if (err)
       
   604                     {
       
   605                     User::LeaveIfError(f.Replace(iFs,
       
   606                                                  *cacheName,
       
   607                                                  EFileShareAny | EFileWrite));
       
   608                     }
       
   609                 }
       
   610             else
       
   611                 {
       
   612                 User::LeaveIfError(f.Replace(iFs,
       
   613                                              *cacheName,
       
   614                                              EFileShareAny | EFileWrite));
       
   615                 }      
       
   616             }
       
   617         else
       
   618             {
       
   619             // create a new cache file
       
   620             CreateContainerFileL(aFe, cachePtr, f);
       
   621             }
       
   622 
       
   623         CleanupClosePushL(f);
       
   624         fullName = FullNameLC(aFe);
       
   625         if (aFe.Type() == KNodeTypeDir)
       
   626             {
       
   627             transactionId = GetDirectoryL(aFe,
       
   628                                           *fullName,
       
   629                                           f,
       
   630                                           aDirEntsp,
       
   631                                           aCaller);
       
   632             }
       
   633         else if (aFe.Type() == KNodeTypeFile)
       
   634             {
       
   635             f.Close();
       
   636             transactionId = RemoteAccessL()->GetFileL(*fullName,
       
   637                                                       *cacheName,
       
   638                                                       aFirstByte,
       
   639                                                       aLength,
       
   640                                                       0,
       
   641                                                       aCaller);
       
   642             }
       
   643 
       
   644         // fullName, f (duplicate close in the case of files)
       
   645         CleanupStack::PopAndDestroy(2, &f);
       
   646         }
       
   647     CleanupStack::PopAndDestroy(cacheName);    
       
   648     return transactionId;
       
   649     }
       
   650 
       
   651 // ----------------------------------------------------------------------------
       
   652 // CRsfwFileEngine::RequestConnectionStateL
       
   653 // ----------------------------------------------------------------------------
       
   654 //
       
   655 TUint CRsfwFileEngine::RequestConnectionStateL(TUint aConnectionState,
       
   656                                            CRsfwRfeStateMachine* aCaller)
       
   657     {
       
   658     DEBUGSTRING16(("CRsfwFileEngine::RequestConnectionStateL %d", aConnectionState));
       
   659     DEBUGSTRING16(("current connection state is %d", iConnectionState));
       
   660     TUint transactionId = 0;
       
   661     if (aConnectionState != iConnectionState)
       
   662         {
       
   663         switch (aConnectionState)
       
   664             {
       
   665         case KMountNotConnected:
       
   666             DisconnectL();
       
   667             break;
       
   668         case KMountStronglyConnected:
       
   669             transactionId = ConnectL(ETrue, aCaller);
       
   670             break;
       
   671 
       
   672         default:
       
   673             break;
       
   674             }
       
   675         }
       
   676     // else does not do anything (if iConnectionState == aConnectionState)    
       
   677     return transactionId;
       
   678     }
       
   679 
       
   680 // ----------------------------------------------------------------------------
       
   681 // CRsfwFileEngine::EnteredConnectionStateL
       
   682 // ----------------------------------------------------------------------------
       
   683 //
       
   684 void CRsfwFileEngine::EnteredConnectionStateL(TUint aConnectionState,
       
   685                                           TBool aRequested)
       
   686     {
       
   687     DEBUGSTRING16(("CRsfwFileEngine::EnteredConnectionStateL %d", aConnectionState));
       
   688     DEBUGSTRING16(("current connection state is %d", iConnectionState));
       
   689     if (aConnectionState != iConnectionState)
       
   690         {
       
   691         iConnectionState = aConnectionState;
       
   692         iVolume->ConnectionStateChanged(iConnectionState);
       
   693 
       
   694         switch (aConnectionState)
       
   695             {
       
   696         case KMountNotConnected:
       
   697             if (!aRequested)
       
   698                 {
       
   699                 iRemoteAccess->Cancel(0);
       
   700                 }
       
   701             break;
       
   702 
       
   703         case KMountStronglyConnected:
       
   704             if (aRequested)
       
   705                 {
       
   706                 if (iLockManager)
       
   707                     {
       
   708                     iLockManager->PopulateExternalLockTokenCacheL(iRootFep);
       
   709                     }
       
   710                 }
       
   711             break;
       
   712 
       
   713         default:
       
   714             break;
       
   715             }
       
   716         }
       
   717     }
       
   718 
       
   719 // ----------------------------------------------------------------------------
       
   720 // CRsfwFileEngine::ConnectionState
       
   721 // ----------------------------------------------------------------------------
       
   722 //
       
   723 TUint CRsfwFileEngine::ConnectionState()
       
   724     {
       
   725     return iConnectionState;
       
   726     }
       
   727 
       
   728 // ----------------------------------------------------------------------------
       
   729 // CRsfwFileEngine::LockManager
       
   730 // ----------------------------------------------------------------------------
       
   731 //
       
   732 CRsfwLockManager* CRsfwFileEngine::LockManager()
       
   733     {
       
   734     return iLockManager;
       
   735     }
       
   736 
       
   737 // ----------------------------------------------------------------------------
       
   738 // CRsfwFileEngine::SetPermanenceL
       
   739 // ----------------------------------------------------------------------------
       
   740 //
       
   741 void CRsfwFileEngine::SetPermanenceL(TBool aPermanence)
       
   742     {
       
   743     iFileTable->SetPermanenceL(aPermanence);
       
   744     }
       
   745 
       
   746 // ----------------------------------------------------------------------------
       
   747 // CRsfwFileEngine::Disconnected
       
   748 // ----------------------------------------------------------------------------
       
   749 //
       
   750 TBool CRsfwFileEngine::Disconnected()
       
   751     {
       
   752     return (iConnectionState == KMountNotConnected);
       
   753     }
       
   754 
       
   755 // ----------------------------------------------------------------------------
       
   756 // CRsfwFileEngine::WriteDisconnected
       
   757 // ----------------------------------------------------------------------------
       
   758 //
       
   759 TBool CRsfwFileEngine::WriteDisconnected()
       
   760     {
       
   761     // This also encompasses disconnected mode
       
   762     return (iConnectionState != KMountStronglyConnected);
       
   763     }
       
   764 
       
   765 // ----------------------------------------------------------------------------
       
   766 // CRsfwFileEngine::AddToCacheL
       
   767 // ----------------------------------------------------------------------------
       
   768 //
       
   769 TInt CRsfwFileEngine::AddToCacheL(CRsfwFileEntry& aFe,
       
   770                               RPointerArray<CRsfwDirEnt>* aDirEnts,
       
   771                               CRsfwFileEngine *aFileEngine,
       
   772                               TUint cachedSize)
       
   773     {
       
   774     // returns the size of the cached data
       
   775     RFs fs = CRsfwRfeServer::Env()->iFs;
       
   776     TInt err;
       
   777     TInt kidsCount = 0;
       
   778     TInt containerSize = cachedSize;
       
   779     // holds true for files, will be overwritten for directories
       
   780 
       
   781     if (aFe.Type() == KNodeTypeDir)
       
   782         {
       
   783         // *********** originally from CRsfwFileEngine::GetDirectoryL()
       
   784         // **********************************************************
       
   785         // Unmark and mark kids only when getdirectory returns KErrNone
       
   786         // otherwise (i.e. KErrUpdateNotRequired) let's just keep
       
   787         // the cached kids...
       
   788         aFe.UnmarkKids();
       
   789 
       
   790         RApaLsSession lsSession;
       
   791         User::LeaveIfError(lsSession.Connect());
       
   792         CleanupClosePushL(lsSession);
       
   793 
       
   794         RFileWriteStream fStream;
       
   795         // Dump to the local cache
       
   796         User::LeaveIfError(
       
   797             fStream.Open(fs,
       
   798                          *(aFe.CacheFileName()),
       
   799                          EFileWrite | EFileShareAny));
       
   800         CleanupClosePushL(fStream);
       
   801 
       
   802         containerSize = fStream.Sink()->SizeL();
       
   803         TInt i;
       
   804         TLex lex;
       
   805         for (i = 0; i < aDirEnts->Count(); i++)
       
   806             {
       
   807 		    CRsfwDirEnt* d = (*aDirEnts)[i];
       
   808             TUid appUid;
       
   809             // For each TDirEnt we just read...
       
   810             // ... if the server returned content-type
       
   811             if (d->Attr()->MimeType() && d->Attr()->MimeType()->Length())
       
   812                 {
       
   813                 err = lsSession.AppForDataType(*(d->Attr()->MimeType()),
       
   814                                                appUid);
       
   815                 if (err == KErrNone)
       
   816                     {
       
   817                     d->Attr()->SetUid(appUid);
       
   818                     }
       
   819                 }
       
   820 
       
   821             d->Attr()->SetAttFlags(KEntryAttRemote);
       
   822             CRsfwFileEntry* kidFep = aFe.FindKidByName(*d->Name());
       
   823             if (kidFep)
       
   824                 {
       
   825                 // We already know this kid
       
   826                 // However we must check whether the kid has been modified
       
   827                 CRsfwDirEntAttr* oldAttr = CRsfwDirEntAttr::NewLC();
       
   828                 kidFep->GetAttributesL(*oldAttr);
       
   829                 if (DataChanged(*oldAttr, *d->Attr()))
       
   830                     {
       
   831                     kidFep->RemoveCacheFile();
       
   832                     }
       
   833                 CleanupStack::PopAndDestroy(oldAttr);
       
   834                 if (kidFep->IsFullyCached())
       
   835                     {
       
   836                     // Mark the kid as cached
       
   837                     d->Attr()->ResetAttFlags(KEntryAttRemote);
       
   838                     }
       
   839                  // as this entry is "used", move it to the back of metadata LRU list
       
   840                  iVolume->iVolumeTable->MoveToTheBackOfMetadataLRUPriorityListL(kidFep);
       
   841                 }
       
   842 
       
   843             // As a side effect,
       
   844             // insert this kid into the file table and
       
   845             // set its attributes
       
   846             if (!kidFep)
       
   847                 {
       
   848                 if (!iVolume->iVolumeTable->EnsureMetadataCanBeAddedL(&aFe))
       
   849                     {
       
   850                     User::Leave(KErrNoMemory);
       
   851                     }
       
   852                 kidFep = CRsfwFileEntry::NewL(*d->Name(), &aFe);
       
   853                 // Attach the new kid
       
   854                 aFileEngine->iFileTable->AddL(kidFep);
       
   855                 aFe.AddKid(*kidFep);
       
   856                 }
       
   857 
       
   858             kidFep->Mark();
       
   859             
       
   860             // set attributes if getting directory listing also supports getting file attributes
       
   861             if (DirectoryListingContainsFileMetadata()) 
       
   862                 {
       
   863                 kidFep->SetAttributesL(*d->Attr(), ETrue);
       
   864                 }
       
   865              else 
       
   866                 {
       
   867                 kidFep->SetAttributesL(*d->Attr(), EFalse);
       
   868                 }
       
   869 
       
   870             TDirEnt dirEnt;
       
   871             MakeDirectoryEntry(*kidFep, dirEnt);
       
   872             dirEnt.ExternalizeL(fStream);
       
   873             kidsCount++;
       
   874             }
       
   875 
       
   876         aFe.DropUnmarkedKidsL();
       
   877 
       
   878         containerSize = fStream.Sink()->SizeL();
       
   879         // assumes that this fetch will write the whole directory,
       
   880         
       
   881         // i.e. there is no partial fetching for the directories
       
   882         if(!iFileTable->Volume()->iVolumeTable->
       
   883            EnsureCacheCanBeAddedL(containerSize))
       
   884             {
       
   885             User::Leave(KErrDiskFull);
       
   886             }
       
   887         fStream.CommitL();
       
   888 
       
   889         CleanupStack::PopAndDestroy(2, &lsSession); // fStream, lsSession
       
   890 
       
   891         // if the directory appeared to be childless add it to metadata LRU list
       
   892         if ( aDirEnts->Count() == 0 )
       
   893             {
       
   894             iVolume->iVolumeTable->AddToMetadataLRUPriorityListL(&aFe, ECachePriorityNormal);
       
   895             }
       
   896 
       
   897         }// if directory
       
   898 
       
   899     // assumes the files are cached in continuos chunks,
       
   900     // i.e. always cached up to the last byte fetched
       
   901     aFe.SetCachedSize(containerSize);
       
   902 
       
   903     aFe.SetCached(ETrue);
       
   904 
       
   905     // We have to update locally dirty bit for the parent container
       
   906     if (aFe.Parent())
       
   907         {
       
   908         aFe.Parent()->SetLocallyDirty();
       
   909         }
       
   910     // But the object itself cannot be remotely dirty any more
       
   911     aFe.ResetRemotelyDirty();
       
   912 
       
   913     // *** from CRsfwFileEngine::DoFetch ***
       
   914     if (aFe.Type() == KNodeTypeDir)
       
   915         {
       
   916         // the reason why kidsCount may be different than aFe.Kids.Count is that for big directories
       
   917         // some kids could have been removed when adding the others to memory. this is due to memory management cap.
       
   918         // however this should not happen so often
       
   919         aFe.KidsCount() == kidsCount ? aFe.iUseCachedData = ETrue : aFe.iUseCachedData = EFalse;
       
   920         }
       
   921     else
       
   922         {
       
   923         aFe.iUseCachedData = ETrue;
       
   924         }
       
   925 
       
   926     return containerSize;
       
   927     }
       
   928 
       
   929 // ----------------------------------------------------------------------------
       
   930 // CRsfwFileEngine::RemoteAccessL
       
   931 // ----------------------------------------------------------------------------
       
   932 //
       
   933 CRsfwRemoteAccess* CRsfwFileEngine::RemoteAccessL()
       
   934     {
       
   935     DEBUGSTRING(("CRsfwFileEngine::RemoteAccessL"));
       
   936     if (!iRemoteAccess)
       
   937         {
       
   938         User::Leave(KErrNotReady);
       
   939         }
       
   940 
       
   941     // Prevent the inactivity timer from triggering
       
   942     // in the middle of a remote access operation
       
   943     StopInactivityTimer();
       
   944 
       
   945     return iRemoteAccess;
       
   946     }
       
   947 
       
   948 // ----------------------------------------------------------------------------
       
   949 // CRsfwFileEngine::OperationCompleted
       
   950 // ----------------------------------------------------------------------------
       
   951 //
       
   952 void CRsfwFileEngine::OperationCompleted()
       
   953     {
       
   954     DEBUGSTRING(("File engine operation completed"));
       
   955     if (iVolume->iVolumeTable->iPermanence)
       
   956         {
       
   957         iFileTable->SaveMetaDataDelta();
       
   958         }
       
   959         
       
   960     if (iLockManager && (iLockManager->LockedCount() == 0))
       
   961         {
       
   962         // Start timer only if we don't have files open for writing
       
   963         StartInactivityTimer();
       
   964         }
       
   965 
       
   966     iVolume->OperationCompleted();
       
   967     }
       
   968 
       
   969 // ----------------------------------------------------------------------------
       
   970 // CRsfwFileEngine::CancelTransaction
       
   971 // ----------------------------------------------------------------------------
       
   972 //
       
   973 void CRsfwFileEngine::CancelTransaction(TUint iTransactionId)
       
   974     {
       
   975     DEBUGSTRING(("CRsfwFileEngine::CancelTransactionL"));
       
   976     if (iRemoteAccess) 
       
   977         {
       
   978         iRemoteAccess->Cancel(iTransactionId);
       
   979         }
       
   980 
       
   981     }
       
   982 
       
   983 // ----------------------------------------------------------------------------
       
   984 // CRsfwFileEngine::CancelTransaction
       
   985 // ----------------------------------------------------------------------------
       
   986 //
       
   987 void CRsfwFileEngine::CancelTransactionL(TDesC& aPathName)
       
   988     {
       
   989     DEBUGSTRING(("CRsfwFileEngine::CancelTransactionL"));
       
   990     TPtrC testPtr;
       
   991     testPtr.Set(aPathName.Right(aPathName.Length() - 3));
       
   992     HBufC* cancelPath = HBufC::NewLC(KMaxPath);
       
   993     TPtr cancelPathPtr = cancelPath->Des();
       
   994     // change '\\' to '/' so the path matches
       
   995     TLex parser(testPtr);
       
   996     TChar theChar;
       
   997     
       
   998     for (int i = 0; i < testPtr.Length(); i++)
       
   999         {
       
  1000         theChar = parser.Get();
       
  1001         if (theChar == 0) 
       
  1002             {
       
  1003             break;
       
  1004             }
       
  1005         // assumes that the input string always has "\\" and not just "\"
       
  1006         // this is true as the input is a file path
       
  1007         if (theChar != '\\') 
       
  1008             {
       
  1009             cancelPathPtr.Append(theChar);
       
  1010             }
       
  1011         else 
       
  1012             {
       
  1013             cancelPathPtr.Append('/');
       
  1014             }        
       
  1015         }
       
  1016     
       
  1017     if (iRemoteAccess) 
       
  1018         {
       
  1019         iRemoteAccess->Cancel(*cancelPath);
       
  1020         }
       
  1021         
       
  1022     CleanupStack::PopAndDestroy(cancelPath);    
       
  1023     
       
  1024     }
       
  1025 
       
  1026     
       
  1027 
       
  1028     
       
  1029 // ----------------------------------------------------------------------------
       
  1030 // CRsfwFileEngine::SetFailedLookup
       
  1031 // Caches the last failed lookup result
       
  1032 // ----------------------------------------------------------------------------
       
  1033 //
       
  1034 void CRsfwFileEngine::SetFailedLookup(TDesC& aPath, TDesC& aKidName)
       
  1035 	{
       
  1036 	iLastFailedLookup = aPath;
       
  1037 	iLastFailedLookup.Append('/');
       
  1038 	iLastFailedLookup.Append(aKidName);
       
  1039 	iLookupTime.HomeTime();
       
  1040 	DEBUGSTRING16(("SetFailedLookup: %S", &iLastFailedLookup));
       
  1041 	}
       
  1042 
       
  1043 // ----------------------------------------------------------------------------
       
  1044 // CRsfwFileEngine::ResetFailedLookup
       
  1045 // Clears the last failed lookup result
       
  1046 // ----------------------------------------------------------------------------
       
  1047 //
       
  1048 void CRsfwFileEngine::ResetFailedLookup()
       
  1049 	{
       
  1050 	DEBUGSTRING16(("ResetFailedLookup: %S", &iLastFailedLookup));
       
  1051 	iLastFailedLookup.Zero();
       
  1052 	}
       
  1053 
       
  1054 // ----------------------------------------------------------------------------
       
  1055 // CRsfwFileEngine::Volume
       
  1056 // ----------------------------------------------------------------------------
       
  1057 //
       
  1058 CRsfwVolume* CRsfwFileEngine::Volume()
       
  1059     {
       
  1060     return iVolume;
       
  1061     }
       
  1062 
       
  1063 // ----------------------------------------------------------------------------
       
  1064 // CRsfwFileEngine::PrepareCacheL
       
  1065 // ----------------------------------------------------------------------------
       
  1066 //
       
  1067 void CRsfwFileEngine::PrepareCacheL()
       
  1068     {
       
  1069     // make sure the file cache (of this volume) exists
       
  1070     iCacheRoot.Copy(CRsfwRfeServer::Env()->iCacheRoot);
       
  1071     iCacheRoot.Append('C');
       
  1072     iCacheRoot.AppendNum(iVolume->iMountInfo.iMountStatus.iVolumeId);
       
  1073     iCacheRoot.Append('\\');
       
  1074     
       
  1075     if (! BaflUtils::FileExists(iFs, iCacheRoot))
       
  1076         {
       
  1077         // There was no prior cache directory
       
  1078         TInt err = iFs.MkDirAll(iCacheRoot);
       
  1079         DEBUGSTRING(("Cache directory created with err=%d", err));
       
  1080         User::LeaveIfError(err);
       
  1081         }
       
  1082     }
       
  1083 
       
  1084 // ----------------------------------------------------------------------------
       
  1085 // CRsfwFileEngine::GetDirectoryL
       
  1086 // ----------------------------------------------------------------------------
       
  1087 //
       
  1088 TUint CRsfwFileEngine::GetDirectoryL(CRsfwFileEntry& /*aFe*/,
       
  1089                                  TDesC& aFullName,
       
  1090                                  RFile& /*aF*/,
       
  1091                                  RPointerArray<CRsfwDirEnt>* aDirEntsp,
       
  1092                                  MRsfwRemoteAccessResponseHandler* aCaller)
       
  1093     {
       
  1094     return RemoteAccessL()->GetDirectoryL(aFullName, *aDirEntsp, aCaller);
       
  1095     }
       
  1096 
       
  1097 // ----------------------------------------------------------------------------
       
  1098 // CRsfwFileEngine::BuildContainerPathL
       
  1099 // ----------------------------------------------------------------------------
       
  1100 //
       
  1101 void CRsfwFileEngine::BuildContainerPathL(CRsfwFileEntry& aFe, TDes& aPath)
       
  1102     {
       
  1103     if (aPath.MaxLength() < (aPath.Length() + iCacheRoot.Length()))
       
  1104         {
       
  1105         aPath.Copy(iCacheRoot);
       
  1106         }
       
  1107     else 
       
  1108         {
       
  1109         User::Leave(KErrOverflow);
       
  1110         }
       
  1111 
       
  1112     ApplyMultiDirCacheL(aPath);
       
  1113     // This filename tagging based on container type is just for convenience
       
  1114     if (aFe.Type() == KNodeTypeFile)
       
  1115         {
       
  1116         aPath.Append('F');
       
  1117         }
       
  1118     else
       
  1119         {
       
  1120         aPath.Append('D');
       
  1121         }
       
  1122     aPath.AppendNum((TInt)aFe.Fid().iNodeId);
       
  1123     }
       
  1124 
       
  1125 // ----------------------------------------------------------------------------
       
  1126 // CRsfwFileEngine::ApplyMultiDirCachePathL
       
  1127 // Due to Symbian performance problems with huge directories, items will not
       
  1128 // be stored in one directory in the cache.
       
  1129 // Now instead one dir like:
       
  1130 // C:\system\data\rsfw_cache\C16
       
  1131 // there will be dirs like:
       
  1132 // C:\system\data\rsfw_cache\C16\M0
       
  1133 // C:\system\data\rsfw_cache\C16\M1
       
  1134 // ... and so on
       
  1135 // ----------------------------------------------------------------------------
       
  1136 //
       
  1137 void CRsfwFileEngine::ApplyMultiDirCacheL(TDes& aPath)
       
  1138     {
       
  1139     // maximum number of items in a single dir in the cache
       
  1140     const TInt KRsfwMaxItemsInDir = 100;
       
  1141     TInt i;
       
  1142     // this loop will surely break (or leave) at some point
       
  1143     for ( i = 0; ; i++ )
       
  1144         {
       
  1145         // create path like "C:\system\data\rsfw_cache\C16\M0"
       
  1146         HBufC* trypath = HBufC::NewMaxL(KMaxPath);
       
  1147         TPtr pathPtr = trypath->Des();
       
  1148         pathPtr.Copy(aPath);
       
  1149         pathPtr.Append('M');
       
  1150         pathPtr.AppendNum(i);
       
  1151         pathPtr.Append('\\');
       
  1152 
       
  1153         // check whether dir exists and if so, how many items it contains
       
  1154         CDir* dir = NULL;
       
  1155         // note that KEntryAttDir att means files & directories
       
  1156         TInt err = iFs.GetDir(*trypath, KEntryAttDir, ESortNone, dir);
       
  1157         if ( err == KErrNone )
       
  1158             {
       
  1159             // count the items
       
  1160             TInt count = dir->Count();
       
  1161             delete dir;
       
  1162             dir = NULL;
       
  1163             
       
  1164             //limit is not exceeded -> return the path
       
  1165             if ( count < KRsfwMaxItemsInDir )
       
  1166                 {
       
  1167                 aPath.Copy(pathPtr);
       
  1168                 delete trypath;
       
  1169                 break;
       
  1170                 }
       
  1171             // limit exceeded -> let's try the next dir
       
  1172             else
       
  1173                 {
       
  1174                 delete trypath;
       
  1175                 continue;
       
  1176                 }    
       
  1177             }        
       
  1178         else if ( err == KErrPathNotFound )
       
  1179             {
       
  1180             // create dir and return the path to empty dir
       
  1181             err = iFs.MkDir(*trypath);
       
  1182             if (!err) 
       
  1183                 {
       
  1184                 aPath.Copy(pathPtr);
       
  1185                 delete trypath;
       
  1186                 }
       
  1187             else 
       
  1188                 {
       
  1189                 delete trypath;
       
  1190                 DEBUGSTRING(("Error when creating cache dir! err=%d", err));
       
  1191                 User::Leave(KErrGeneral);
       
  1192                 }
       
  1193    
       
  1194             break;
       
  1195             }
       
  1196         else
       
  1197             {
       
  1198             delete trypath;
       
  1199             DEBUGSTRING(("Cache directory cannot be created! err=%d", err));        
       
  1200             User::Leave(KErrGeneral);
       
  1201             }    
       
  1202         }
       
  1203     }
       
  1204 
       
  1205 // ----------------------------------------------------------------------------
       
  1206 // CRsfwFileEngine::CreateContainerFileL
       
  1207 // ----------------------------------------------------------------------------
       
  1208 //
       
  1209 void CRsfwFileEngine::CreateContainerFileL(CRsfwFileEntry& aFe,
       
  1210                                        TDes& aPath,
       
  1211                                        RFile& aF)
       
  1212     {
       
  1213     // Create a container file for the Fid.
       
  1214     // If the cache file already exists, it will be deleted
       
  1215 
       
  1216     BuildContainerPathL(aFe, aPath);
       
  1217 
       
  1218     TInt err = aF.Replace(iFs, aPath, EFileShareAny | EFileWrite);
       
  1219     if (err != KErrNone)
       
  1220         {
       
  1221         User::Leave(KErrGeneral);
       
  1222         }
       
  1223     aF.Close();
       
  1224 
       
  1225     aFe.SetCacheFileName(&aPath);
       
  1226     }
       
  1227 
       
  1228 // ----------------------------------------------------------------------------
       
  1229 // CRsfwFileEngine::DoIoctlL
       
  1230 // ----------------------------------------------------------------------------
       
  1231 //
       
  1232 void CRsfwFileEngine::DoIoctlL(TRfeIoctlInArgs& aIn, TRfeOutArgs& /* aOut */)
       
  1233     {
       
  1234     TFid fidp = aIn.iFid;
       
  1235     TInt cmd = aIn.iCmd;
       
  1236 
       
  1237     TInt err = KErrNone;
       
  1238 
       
  1239     DEBUGSTRING(("ioctl fid %d - command=%d, data=%d",
       
  1240                  fidp.iNodeId,
       
  1241                  cmd,
       
  1242                  aIn.iData32[0]));
       
  1243 
       
  1244     CRsfwFileEntry* fep = iFileTable->Lookup(fidp);
       
  1245     if (fep)
       
  1246         {
       
  1247         switch (cmd)
       
  1248             {
       
  1249         case ERemoteFsIoctlRefresh:
       
  1250 
       
  1251 
       
  1252             if (fep->Type() == KNodeTypeFile)
       
  1253                 {
       
  1254 
       
  1255                 fep->SetCacheFileName(NULL);
       
  1256                 fep->SetCached(EFalse);
       
  1257 
       
  1258                 // There is a change in the parent's container
       
  1259                 fep->Parent()->SetLocallyDirty();
       
  1260                 }
       
  1261             break;
       
  1262 
       
  1263         case ERemoteFsHighCachePriority:
       
  1264         default:
       
  1265             err = KErrArgument;
       
  1266             break;
       
  1267             }
       
  1268         }
       
  1269     else
       
  1270         {
       
  1271         err = KErrNotFound;
       
  1272         }
       
  1273 
       
  1274     if (err != KErrNone)
       
  1275         {
       
  1276         User::Leave(err);
       
  1277         }
       
  1278 
       
  1279     return;
       
  1280     }
       
  1281 
       
  1282 // ----------------------------------------------------------------------------
       
  1283 // CRsfwFileEngine::DoRootL
       
  1284 // ----------------------------------------------------------------------------
       
  1285 //
       
  1286 void CRsfwFileEngine::DoRootL(TRfeRootInArgs& /* aIn */, TRfeRootOutArgs& aOut)
       
  1287     {
       
  1288     SetupRootL(iVolume->iVolumeTable->iPermanence);
       
  1289     aOut.iFid.iVolumeId = iRootFid->iVolumeId;
       
  1290     aOut.iFid.iNodeId = iRootFid->iNodeId;
       
  1291     return;
       
  1292     }
       
  1293 
       
  1294 // ----------------------------------------------------------------------------
       
  1295 // CRsfwFileEngine::DoSetAttrL
       
  1296 // ----------------------------------------------------------------------------
       
  1297 //
       
  1298 void CRsfwFileEngine::DoSetAttrL(TRfeSetAttrInArgs& aIn, TRfeOutArgs& /* aOut */)
       
  1299     // We cannot really set anything but this is the way to implement this
       
  1300     // note that if this is implemented, it should really be a state machine
       
  1301     {
       
  1302     TInt err = KErrNone;
       
  1303     TFid fidp = aIn.iFid;
       
  1304 #ifdef _DEBUG
       
  1305     TDirEntAttr* attrp = &(aIn.iAttr);
       
  1306 #endif
       
  1307 
       
  1308     DEBUGSTRING(("setting attributes of fid %d, attr=0x%x, size=%d, time=",
       
  1309                  fidp.iNodeId,
       
  1310                  attrp->iAtt,
       
  1311                  attrp->iSize));
       
  1312     DEBUGTIME((attrp->iModified));
       
  1313 
       
  1314     // Get the file or directory to setattr
       
  1315     CRsfwFileEntry* fep = iFileTable->Lookup(fidp);
       
  1316     if (fep)
       
  1317         {
       
  1318         err = KErrNotSupported;
       
  1319         }
       
  1320     else
       
  1321         {
       
  1322         err = KErrNotFound;
       
  1323         }
       
  1324 
       
  1325     User::Leave(err);
       
  1326     return;
       
  1327     }
       
  1328 
       
  1329 // ----------------------------------------------------------------------------
       
  1330 // CRsfwFileEngine::SetupRootL
       
  1331 // ----------------------------------------------------------------------------
       
  1332 //
       
  1333 void CRsfwFileEngine::SetupRootL(TBool aPermanence)
       
  1334     {
       
  1335     _LIT(KRootPath, ".");  // dummy
       
  1336 
       
  1337     if (!iRootFid)
       
  1338         {
       
  1339         CRsfwFileEntry* root = NULL;
       
  1340         TInt err;
       
  1341         if (aPermanence)
       
  1342             {
       
  1343             TRAP(err, root = iFileTable->LoadMetaDataL());
       
  1344             }
       
  1345         if (err == KErrCorrupt)    
       
  1346             {
       
  1347             DEBUGSTRING(("Metadata corrupted! Recreating cache file..."));
       
  1348             // corrupted cache file, recreate filetable and cache file
       
  1349             delete iFileTable;
       
  1350             iFileTable = NULL;
       
  1351             CleanupCorruptedCacheL();
       
  1352             PrepareCacheL();
       
  1353             iFileTable = CRsfwFileTable::NewL(iVolume, iCacheRoot);
       
  1354             }
       
  1355         if (!aPermanence || (err != KErrNone))
       
  1356             {
       
  1357             root = CRsfwFileEntry::NewL(KRootPath, NULL);
       
  1358             // Insert root into the file table
       
  1359             iFileTable->AddL(root);
       
  1360             root->SetType(KNodeTypeDir);
       
  1361             }
       
  1362         if (aPermanence)
       
  1363             {
       
  1364             iFileTable->SaveMetaDataDelta();
       
  1365             }
       
  1366         iRootFep = root;
       
  1367         iRootFid = &(root->Fid());
       
  1368         }
       
  1369     }
       
  1370 
       
  1371 // ----------------------------------------------------------------------------
       
  1372 // CRsfwFileEngine::CleanupCorruptedCacheL
       
  1373 // ----------------------------------------------------------------------------
       
  1374 //
       
  1375 void CRsfwFileEngine::CleanupCorruptedCacheL()
       
  1376     {    
       
  1377     // delete everything from the cache
       
  1378     TFileName cachepath;
       
  1379     cachepath.Copy(iCacheRoot);
       
  1380     CFileMan* fileMan = CFileMan::NewL(iFs);
       
  1381     fileMan->Delete(cachepath, CFileMan::ERecurse);
       
  1382     delete fileMan;
       
  1383     }
       
  1384 
       
  1385 // ----------------------------------------------------------------------------
       
  1386 // CRsfwFileEngine::ConnectL
       
  1387 // ----------------------------------------------------------------------------
       
  1388 //
       
  1389 TUint CRsfwFileEngine::ConnectL(TBool aRestart, CRsfwRfeStateMachine* aCaller)
       
  1390     {
       
  1391     // Assume parameter format:
       
  1392     // protocol://username:password@server:port/rootdir or
       
  1393     // The ":password", ":port", and "[/]rootdir" can be omitted.
       
  1394     // If the length of password parameter is bigger than 1,
       
  1395     // it overrides the one in uri, if any.
       
  1396     // Characters can be quoted with %<hexdigit><hexdigit> format
       
  1397     TUint transactionId = 0;
       
  1398 
       
  1399     if (iRemoteAccess)
       
  1400         {
       
  1401         // We already have a remote accessor
       
  1402         if (aRestart)
       
  1403             {
       
  1404             // Restarting
       
  1405             delete iLockManager;
       
  1406             iLockManager = NULL;
       
  1407             delete iRemoteAccess;
       
  1408             iRemoteAccess = NULL;
       
  1409             }
       
  1410         else
       
  1411             {
       
  1412             User::Leave(KErrAlreadyExists);
       
  1413             }
       
  1414         }
       
  1415 
       
  1416     DEBUGSTRING16(("ConnectL(): '%S'",
       
  1417                    &iVolume->iMountInfo.iMountConfig.iUri));
       
  1418 
       
  1419     TUriParser uriParser;
       
  1420     User::LeaveIfError(uriParser.Parse(iVolume->iMountInfo.iMountConfig.iUri));
       
  1421 
       
  1422     TPtrC userName;
       
  1423     TPtrC password;
       
  1424     TPtrC friendlyName;
       
  1425 
       
  1426     if (uriParser.IsPresent(EUriUserinfo))
       
  1427         {
       
  1428         TPtrC userInfo(uriParser.Extract(EUriUserinfo));
       
  1429         // Split the user info into user name and password (seprated by ':')
       
  1430         TInt pos = userInfo.Locate(':');
       
  1431         if (pos != KErrNotFound)
       
  1432             {
       
  1433             password.Set(userInfo.Mid(pos + 1));
       
  1434             userName.Set(userInfo.Left(pos));
       
  1435             }
       
  1436         else
       
  1437             {
       
  1438             userName.Set(userInfo);
       
  1439             }
       
  1440         }
       
  1441 
       
  1442     HBufC* userNameBuf = NULL;
       
  1443     if (!userName.Length() &&
       
  1444         iVolume->iMountInfo.iMountConfig.iUserName.Length())
       
  1445         {
       
  1446         // separate user name overwrites the username embedded in the URI
       
  1447         userName.Set(iVolume->iMountInfo.iMountConfig.iUserName);
       
  1448         }
       
  1449 
       
  1450     HBufC* passwordBuf = NULL;
       
  1451     if (!password.Length() &&
       
  1452         (iVolume->iMountInfo.iMountConfig.iPassword.Length() > 1))
       
  1453         {
       
  1454         // separate password overwrites the password embedded in the URI
       
  1455         password.Set(iVolume->iMountInfo.iMountConfig.iPassword);
       
  1456         }
       
  1457 
       
  1458     friendlyName.Set(iVolume->iMountInfo.iMountConfig.iName);
       
  1459 
       
  1460     TPtrC scheme(uriParser.Extract(EUriScheme));
       
  1461     HBufC8* protocol = HBufC8::NewLC(scheme.Length());
       
  1462     TPtr8 protocolPtr = protocol->Des();
       
  1463     protocolPtr.Copy(scheme);
       
  1464     iRemoteAccess = CRsfwRemoteAccess::NewL(protocolPtr);
       
  1465     CleanupStack::PopAndDestroy(protocol);
       
  1466 
       
  1467     // user name and password are conveyed separately from the URI
       
  1468     CUri* uri = CUri::NewLC(uriParser);
       
  1469     uri->RemoveComponentL(EUriUserinfo);
       
  1470 
       
  1471     // leaves if error
       
  1472     iRemoteAccess->SetupL(this);
       
  1473     transactionId = iRemoteAccess->
       
  1474         OpenL(uri->Uri(),
       
  1475               friendlyName,
       
  1476               userName,
       
  1477               password,
       
  1478               iVolume->iMountInfo.iMountConfig.iAuxData,
       
  1479               aCaller);
       
  1480 
       
  1481     CleanupStack::PopAndDestroy(uri);
       
  1482     if (passwordBuf)
       
  1483         {
       
  1484         CleanupStack::PopAndDestroy(passwordBuf);
       
  1485         }
       
  1486     if (userNameBuf)
       
  1487         {
       
  1488         CleanupStack::PopAndDestroy(userNameBuf);
       
  1489         }
       
  1490 
       
  1491     // lock manager can be created before we know whether connecting was
       
  1492     // succesful - however it must be deleted upon unsuccesful connect
       
  1493     if (!iLockManager)
       
  1494         {
       
  1495         iLockManager = CRsfwLockManager::NewL(iRemoteAccess);
       
  1496         }
       
  1497 
       
  1498     if ((iInactivityTimeout > 0) && !iInactivityTimer)
       
  1499         {
       
  1500         iInactivityTimer = CPeriodic::NewL(CActive::EPriorityLow);
       
  1501         }
       
  1502     return transactionId;
       
  1503     }
       
  1504 
       
  1505 // ----------------------------------------------------------------------------
       
  1506 // CRsfwFileEngine::DisconnectL
       
  1507 // ----------------------------------------------------------------------------
       
  1508 //
       
  1509 void CRsfwFileEngine::DisconnectL()
       
  1510     {
       
  1511     DEBUGSTRING(("CRsfwFileEngine::DisconnectL"));
       
  1512     if (iRemoteAccess) 
       
  1513         {
       
  1514         iRemoteAccess->Cancel(0);
       
  1515         delete iRemoteAccess;
       
  1516         iRemoteAccess = NULL;
       
  1517         }
       
  1518 
       
  1519     if (iLockManager) 
       
  1520         {
       
  1521         delete iLockManager;
       
  1522         iLockManager = NULL;
       
  1523         }
       
  1524 
       
  1525     
       
  1526     // Set open file count to zero
       
  1527     // If there are open files, after disconnecting we do not necessarily
       
  1528     // get close events.
       
  1529     // Note that this variable is not "dirty bit" (file has currently
       
  1530     // uncommitted modifications), so it is safe to set it to zero
       
  1531     TInt openfiles = iFileTable->OpenFileCount();
       
  1532     iFileTable->UpdateOpenFileCount(-openfiles);
       
  1533         
       
  1534     EnteredConnectionStateL(KMountNotConnected, ETrue);
       
  1535     
       
  1536     // publish connection status when disconnecting
       
  1537     iVolume->iVolumeTable->PublishConnectionStatus(iVolume);
       
  1538    
       
  1539     }
       
  1540 
       
  1541 // ----------------------------------------------------------------------------
       
  1542 // CRsfwFileEngine::StartInactivityTimer
       
  1543 // ----------------------------------------------------------------------------
       
  1544 //
       
  1545 void CRsfwFileEngine::StartInactivityTimer()
       
  1546     {
       
  1547     if (iInactivityTimer)
       
  1548         {
       
  1549         DEBUGSTRING(("inactivity timer started (%d us)",
       
  1550                      iInactivityTimeout));
       
  1551         iInactivityTimer->Cancel();
       
  1552         TCallBack callBack(CRsfwFileEngine::InactivityTimerExpired, this);
       
  1553         iInactivityTimer->Start(iInactivityTimeout,
       
  1554                                 iInactivityTimeout,
       
  1555                                 callBack);
       
  1556         }
       
  1557     }
       
  1558 
       
  1559 // ----------------------------------------------------------------------------
       
  1560 // CRsfwFileEngine::StopInactivityTimer
       
  1561 // ----------------------------------------------------------------------------
       
  1562 //
       
  1563 void CRsfwFileEngine::StopInactivityTimer()
       
  1564     {
       
  1565     DEBUGSTRING(("CRsfwFileEngine::StopInactivityTimer"));
       
  1566     if (iInactivityTimer)
       
  1567         {
       
  1568         DEBUGSTRING(("inactivity timer stopped"));
       
  1569         iInactivityTimer->Cancel();
       
  1570         }
       
  1571     }
       
  1572 
       
  1573 // ----------------------------------------------------------------------------
       
  1574 // CRsfwFileEngine::InactivityTimerExpired
       
  1575 // ----------------------------------------------------------------------------
       
  1576 //
       
  1577 TInt CRsfwFileEngine::InactivityTimerExpired(TAny* aArg)
       
  1578     {
       
  1579     DEBUGSTRING(("CRsfwFileEngine::InactivityTimerExpired"));
       
  1580     CRsfwFileEngine* fileEngine = static_cast<CRsfwFileEngine*>(aArg);
       
  1581     if (fileEngine->iFileTable->OpenFileCount() == 0) 
       
  1582         {
       
  1583         fileEngine->StopInactivityTimer();
       
  1584         TRAP_IGNORE(fileEngine->DisconnectL());
       
  1585         // "Simulate" operation completion (which may result in RFE shutdown)
       
  1586         fileEngine->OperationCompleted();
       
  1587         }
       
  1588     else 
       
  1589         {
       
  1590         // if there are open files on this volume, just restart the inactivity timer
       
  1591         fileEngine->StartInactivityTimer();
       
  1592         }
       
  1593 
       
  1594     return 0;
       
  1595     }
       
  1596 
       
  1597 // ----------------------------------------------------------------------------
       
  1598 // CRsfwFileEngine::HandleRemoteAccessEventL
       
  1599 // ----------------------------------------------------------------------------
       
  1600 //
       
  1601 void CRsfwFileEngine::HandleRemoteAccessEventL(TInt aEventType,
       
  1602                                            TInt aEvent,
       
  1603                                            TAny* /* aArg */)
       
  1604     {
       
  1605     DEBUGSTRING(("Handle remote access event: %d/%d in connection state %d",
       
  1606                  aEventType,
       
  1607                  aEvent,
       
  1608                  iConnectionState));
       
  1609     switch (aEventType)
       
  1610         {
       
  1611     case ERsfwRemoteAccessObserverEventConnection:
       
  1612         switch (aEvent)
       
  1613             {
       
  1614         case ERsfwRemoteAccessObserverEventConnectionDisconnected:
       
  1615             EnteredConnectionStateL(KMountNotConnected, EFalse);
       
  1616             break;
       
  1617 
       
  1618         case ERsfwRemoteAccessObserverEventConnectionWeaklyConnected:
       
  1619 #if 0
       
  1620             // This event does not appear
       
  1621             EnteredConnectionStateL(KMountWeaklyConnected, EFalse);
       
  1622 #endif
       
  1623             break;
       
  1624 
       
  1625         case ERsfwRemoteAccessObserverEventConnectionStronglyConnected:
       
  1626 #if 0
       
  1627             // This event does not appear
       
  1628             EnteredConnectionStateL(KMountStronglyConnected, EFalse);
       
  1629 #endif
       
  1630             break;
       
  1631 
       
  1632         default:
       
  1633             break;
       
  1634             }
       
  1635         break;
       
  1636 
       
  1637     default:
       
  1638         break;
       
  1639         }
       
  1640     }
       
  1641 
       
  1642 // ----------------------------------------------------------------------------
       
  1643 // CRsfwFileEngine::PurgeFromCacheL
       
  1644 // ----------------------------------------------------------------------------
       
  1645 //
       
  1646 TInt CRsfwFileEngine::PurgeFromCache(const TDesC& aPath) 
       
  1647     {
       
  1648     // get the fid of the entry for which the cached data is removed
       
  1649     CRsfwFileEntry* targetFid = FetchFep(aPath);
       
  1650     if (!targetFid) 
       
  1651         {
       
  1652         return KErrPathNotFound;
       
  1653         }
       
  1654     // only directories can be refreshed currently    
       
  1655     if (targetFid->Type() != KNodeTypeDir) 
       
  1656         {
       
  1657         return KErrArgument;
       
  1658         }
       
  1659     targetFid->SetCached(EFalse);    
       
  1660     return KErrNone;
       
  1661     }
       
  1662                                            
       
  1663 CRsfwFileEntry* CRsfwFileEngine::FetchFep(const TDesC& aPath) 
       
  1664     {
       
  1665     DEBUGSTRING16(("CRsfwFileEngine::FetchFep for file %S", &aPath));
       
  1666     if (aPath.Length() <= 1)
       
  1667         {
       
  1668         DEBUGSTRING(("returning rootFep"));
       
  1669         return iRootFep;
       
  1670         }
       
  1671     else 
       
  1672         {
       
  1673         TInt delimiterPos = aPath.LocateReverse(KPathDelimiter);
       
  1674         if (delimiterPos == (aPath.Length() - 1))
       
  1675             {
       
  1676             // The path ends with a slash,
       
  1677             //i.e. this is a directory - continue parsing
       
  1678             TPtrC nextdelimiter;
       
  1679             nextdelimiter.Set(aPath.Left(delimiterPos));
       
  1680             delimiterPos = nextdelimiter.LocateReverse(KPathDelimiter);
       
  1681             }
       
  1682         TPtrC entry(aPath.Right(aPath.Length() - (delimiterPos + 1)));
       
  1683         TPtrC path(aPath.Left(delimiterPos + 1));
       
  1684         
       
  1685         // strip a trailing backslash if found
       
  1686         delimiterPos = entry.LocateReverse(KPathDelimiter);
       
  1687         if (delimiterPos == (entry.Length() - 1)) 
       
  1688             {
       
  1689             TPtrC stripped(entry.Left(entry.Length() - 1));
       
  1690             return (FetchFep(path)->FindKidByName(stripped));
       
  1691             }
       
  1692            else 
       
  1693             {
       
  1694             return (FetchFep(path)->FindKidByName(entry));
       
  1695             }
       
  1696         
       
  1697         }
       
  1698 
       
  1699     }
       
  1700     
       
  1701 HBufC8* CRsfwFileEngine::GetContentType(TDesC& aName)
       
  1702      {
       
  1703      TInt err;
       
  1704      RApaLsSession lsSession;
       
  1705      err = lsSession.Connect();
       
  1706      if (err) 
       
  1707          {
       
  1708          return NULL;
       
  1709          }
       
  1710          
       
  1711      RFs fsSession;
       
  1712      err = fsSession.Connect();
       
  1713      if (err) 
       
  1714          {
       
  1715          lsSession.Close();
       
  1716          return NULL;
       
  1717          }
       
  1718      fsSession.ShareProtected();
       
  1719      TDataRecognitionResult dataType;
       
  1720      RFile theFile;
       
  1721      // the mode must mach the mode that is used in the file system plugin
       
  1722      // (EFileWrite|EFileShareAny)
       
  1723      err = theFile.Open(fsSession, aName, EFileWrite|EFileShareAny);
       
  1724      if (err) 
       
  1725          {
       
  1726          lsSession.Close();
       
  1727          fsSession.Close();
       
  1728          return NULL;
       
  1729          }
       
  1730      err = lsSession.RecognizeData(theFile, dataType);
       
  1731      lsSession.Close();
       
  1732      theFile.Close();
       
  1733      fsSession.Close();
       
  1734      if (err) 
       
  1735          {
       
  1736          return NULL;
       
  1737          }
       
  1738       
       
  1739      return dataType.iDataType.Des8().Alloc();
       
  1740      }    
       
  1741 
       
  1742  // The purpose of these functions is to give capability info
       
  1743  // for the access protocol plugin used.
       
  1744  
       
  1745  // Currently this information is hard coded and takes into account webdav and upnp
       
  1746  // access modules. New function should be added to the access plugin api to get
       
  1747  // this information from the protocol module
       
  1748     
       
  1749   // whether getting the directory listing also gives reliable file metadata
       
  1750 TBool CRsfwFileEngine::DirectoryListingContainsFileMetadata() 
       
  1751     {
       
  1752     _LIT(KUPnP, "upnp");
       
  1753     if (iVolume->MountInfo()->iMountConfig.iUri.Left(4) == KUPnP)
       
  1754         {
       
  1755         return EFalse;
       
  1756         }
       
  1757     else 
       
  1758         {
       
  1759         return ETrue;
       
  1760         }
       
  1761     
       
  1762     }
       
  1763