remotestoragefw/webdavaccessplugin/src/rsfwdavsession.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Thu, 17 Dec 2009 09:07:59 +0200
changeset 0 3ad9d5175a89
permissions -rw-r--r--
Revision: 200949 Kit: 200951

/*
* Copyright (c) 2002-2004 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:  API for WebDAV operations
 *
*/


// INCLUDE FILES
#include <httpstringconstants.h>
#include <http/rhttpheaders.h>
#include <escapeutils.h>
#include <xml/matchdata.h>

#include "rsfwdavsession.h"
#include "rsfwdavtransaction.h"
#include "rsfwconnectionmanager.h"
#include "rsfwpropfindparser.h"
#include "rsfwlockqueryparser.h"
#include "mdebug.h"

// CONSTANTS
// characters that will be encoded in the url
_LIT8(KSpecials8, " \"<>#%{}|\\^~[]`");

// ============================ MEMBER FUNCTIONS ==============================
CRsfwDavSession* CRsfwDavSession::NewL(
    MRsfwDavResponseObserver* aWebDavResponseObserver,
    MRsfwConnectionObserver* aRsfwConnectionObserver)
    {
    CRsfwDavSession* self = new (ELeave) CRsfwDavSession;
    CleanupStack::PushL(self);
    self->ConstructL(aWebDavResponseObserver, aRsfwConnectionObserver);
    CleanupStack::Pop(self);
    return self;
    }

void CRsfwDavSession::ConstructL(
    MRsfwDavResponseObserver* aWebDavResponseObserver,
    MRsfwConnectionObserver* aRsfwConnectionObserver)
    {
    DEBUGSTRING(("DavSess: ConstructL: enter"));
    User::LeaveIfError(iFs.Connect());
    iWebDavResponseObserver = aWebDavResponseObserver;
    iRsfwConnectionObserver = aRsfwConnectionObserver;

    // Create classes needed for parsing PropFind and Lock request replies
    // Creating these later seems to cause emulator hang-ups.
    // If the problem does not exist in the real device we may want to
    // delay especially Lock parser creation as locking may not be used at all.
    Xml::CMatchData* matchData = Xml::CMatchData::NewLC();
	matchData->SetMimeTypeL(KTextXml);
	// Select libxml2 parsesr
	matchData->SetVariantL(_L8("libxml2"));
	// Select Symbian XML Parser (=Expat)
//	matchData->SetVariantL(_L8("Symbian"));
    iPropFindParserImpl = CRsfwPropFindParser::NewL();
    iPropFindParser = Xml::CParser::NewL(*matchData, *iPropFindParserImpl);
    iLockQueryParserImpl = CRsfwLockQueryParser::NewL();
    iLockQueryParser = Xml::CParser::NewL(*matchData, *iLockQueryParserImpl);
    CleanupStack::PopAndDestroy(matchData);

    // Open the RHTTPSession
    iHttpSession.OpenL();
    iHttpSession.FilterCollection().RemoveFilter( 
            iHttpSession.StringPool().StringF( HTTP::EAuthentication, RHTTPSession::GetTable() ));
    // Install this class as the callback for authentication requests:
    // it will take care of basic/digest auth, SSL
    InstallAuthenticationL(iHttpSession);
    DEBUGSTRING(("auth filter installed"));
    }

CRsfwDavSession::~CRsfwDavSession()
    {
    DEBUGSTRING(("CRsfwDavSession::~CRsfwDavSession"));
    delete iPropFindParser;
    delete iPropFindParserImpl;
    delete iLockQueryParser;
    delete iLockQueryParserImpl;
    delete iEncodedHost;
    if (iUserName) 
    {
    	delete iUserName;	
    }
    if (iPassword) 
    {
    	delete iPassword;
    }
  	iHttpSession.Close();
  	delete iRsfwConnectionManager;
    iFs.Close();
    }

// ----------------------------------------------------------------------------
// CRsfwDavSession::SetConnected
// ----------------------------------------------------------------------------
//
void CRsfwDavSession::SetConnected(TBool aConnected)
    {
    iConnected = aConnected;
    }

// ----------------------------------------------------------------------------
// CRsfwDavSession::SetWebDavSupportClass
// ----------------------------------------------------------------------------
//
void CRsfwDavSession::SetWebDavSupportClass(TInt aWebDavSupportClass)
    {
    iWebDavSupportClass = aWebDavSupportClass;
    }
    
// ----------------------------------------------------------------------------
// CRsfwDavSession::WebDavSupportClass
// ----------------------------------------------------------------------------
//
TInt CRsfwDavSession::WebDavSupportClass()
    {
    return iWebDavSupportClass;
    }


// ----------------------------------------------------------------------------
// CRsfwDavSession::OpenL
// After calling this function, use options query
// to trigger TCP level connection.
// ----------------------------------------------------------------------------
//
void CRsfwDavSession::OpenL(const TDesC& aUri,
                           TInt aPort,
                           const TDesC& aUserName,
                           const TDesC& aPassword,
                           const TDesC& aAuxData)
    {
    // Store connection parameters
    iHost.Zero();
    iDavRoot.Zero();

    // Here we add the port using a simple approach:
    // it needs to go after http://name/
    TInt slashCnt = 0;
    TInt cnt = 0;
    while (cnt < aUri.Length())
        {
        TChar ch = aUri[cnt++];
        if (ch == '/')
            {
            slashCnt++;
            if (slashCnt == 3)
                {
                iHost.Append(':');
                iHost.AppendNum(aPort);
                // At this point we know that
                // the remainder of the uri is the root directory
                }  
            }

        if (slashCnt > 2)
            {
            iDavRoot.Append(ch);
            }
        else
            {
            iHost.Append(ch);
            }
        }
    // We elso need an encoded form of the host part
    iEncodedHost = EncodeL(iHost.Right(iHost.Length() - KProtocolPrefix));

    // iDavRoot must be a directory, and thus should end with a slash
    Slashify(iDavRoot);

    // Make the pair
    iHostRoot.Copy(iHost);
    iHostRoot.Append(iDavRoot);

    // Assume that the parameters are constant and stable across the session
    iUserName = EncodeL(aUserName);
    iPassword = EncodeL(aPassword);
    iAuxData.Copy(aAuxData);

    DEBUGSTRING16(("connecting to host='%S', port=%d, root='%S', data=%S",
                   &iHost,
                   aPort,
                   &iDavRoot,
                   &iAuxData));

   if (iAuxData.Length() == 0) 
   {
   		// in case of empty access point info, set it to '?' (ask user)
   		_LIT(KAskUser, "?");
   		iAuxData.Copy(KAskUser);
   }
   
   if (!iRsfwConnectionManager)
   		{
        iRsfwConnectionManager =
             CRsfwConnectionManager::NewL(iRsfwConnectionObserver);
        iRsfwConnectionManager->UseIapL(aAuxData);
        }
        
    }

// ----------------------------------------------------------------------------
// CRsfwDavSession::OptionsL
// ----------------------------------------------------------------------------
//
CRsfwDavTransaction* CRsfwDavSession::OptionsL()
    {
    TPtrC null;
    TUriParser8 uriParser;
    HBufC8* uri = BuildUriLC(null, EFalse, &uriParser);

    DEBUGSTRING8(("OPTIONS '%S'", &uriParser.UriDes()));

    // Establish a link-layer connection
    if (iRsfwConnectionManager)
        {
        SetupConnectionL();
        }

    RStringPool stringPool = StringPool();

    // Introducing the webdav headers for the propfind
    RStringF mOptions = stringPool.OpenFStringL(KWebDavOptions);
    CleanupClosePushL(mOptions);
    CRsfwDavTransaction* webDavTransaction =
        CRsfwDavTransaction::NewL(this,
                                 EWebDavOpOptions,
                                 uriParser,
                                 mOptions,
                                 NextWebDavTransactionId());
    CleanupStack::PopAndDestroy(); // mOptions
    CleanupStack::PushL(webDavTransaction);

    RHTTPHeaders hdr = webDavTransaction->
        HttpTransaction().Request().GetHeaderCollection(); 
 
 	SetBasicHeadersL(hdr, uriParser, ETrue);
 	
    CleanupStack::Pop(2); //webDavTransaction, uri
    delete uri;
    return webDavTransaction;
    }

// ----------------------------------------------------------------------------
// CRsfwDavSession::PropFindL
// Implements WebDAV PROPFIND method.
// Parameters: Name of the directory or file
//             CRsfwDirEnt pointer array to be filled with directory metadata
//             PROPFIND depth
//             aIsDir, is this directory or file,
//                     some extra checks are made based on this...
// ----------------------------------------------------------------------------
//
CRsfwDavTransaction* CRsfwDavSession::PropFindL(const TDesC &aPath,
                                              TInt aDepth,
                                              TBool aIsDir,
                                              RPointerArray<CRsfwDirEnt>& aDirEnts)
    {
    if (!IsConnected())
        {
        User::Leave(KErrNotReady);
        }

    TUriParser8 uriParser;
    HBufC8* uri = BuildUriLC(aPath, aIsDir, &uriParser);

    DEBUGSTRING8(("PROPFIND '%S'", &uriParser.UriDes()));
 
    // This function is used from several places
    TWebDavOp op;
    if (aDepth == 1)
        {
        op = EWebDavOpPropFindMulti;
        }
    else // (aDepth == 0)
        {
        op = EWebDavOpPropFindSingle;  
        }

    RStringPool stringPool = StringPool();
    RStringF mPropFind = stringPool.OpenFStringL(KWebDavPropFind);
    CleanupClosePushL(mPropFind);
    CRsfwDavTransaction* webDavTransaction =
        CRsfwDavTransaction::NewL(this,
                                 op,
                                 uriParser,
                                 mPropFind,
                                 NextWebDavTransactionId());
    CleanupStack::PopAndDestroy(); // mPropFind
    CleanupStack::PushL(webDavTransaction);

    RHTTPHeaders hdr = webDavTransaction->
        HttpTransaction().Request().GetHeaderCollection();

    // Add headers appropriate to all methods
    SetBasicHeadersL(hdr, uriParser, ETrue);

    SetHeaderL(hdr, HTTP::EContentType, KTextXml);

    // Assumes that the target uri has been cached in an earlier connect
    SetDepthHeaderL(hdr, aDepth);

    // XML body
    HBufC8* requestBodyBuffer = HBufC8::NewL(KDefaultSubmitSize);
    TPtr8 requestBodyBufferPtr = requestBodyBuffer->Des();

    // To make things at least little bit faster,
    // let's try to get "minimal" set
    // Maybe useful one day:
    // - <D:getcontentlanguage/>
    // - <D:creationdate/>
    // Apache mod_dav 1.0.3 doesn't support:
    //  - <D:displayname/>
    _LIT8(KPropFindRequestBody, "\
<?xml version=\"1.0\"?>\
<propfind xmlns=\"DAV:\">\
<prop>\
<getcontenttype/>\
<getlastmodified/>\
<getcontentlength/>\
<resourcetype/>\
<getetag/>\
</prop>\
</propfind>\
");
    requestBodyBufferPtr.Append(KPropFindRequestBody);
    webDavTransaction->SetBodyData(requestBodyBuffer);

    webDavTransaction->SetPropFindDirEntryArray(aDirEnts);
    // We must remember the work directory,
    // as we don't want to list that when building directory listing.
    HBufC* propFindPath = HBufC::NewL(iHostRoot.Length() +
                                       KMaxPath +
                                       1);
    TPtr propFindPathPtr = propFindPath->Des();
    propFindPathPtr.Copy(iDavRoot);
    propFindPathPtr.Append(aPath);
    // The whole path must end with a slash
    Slashify(propFindPathPtr);
    // Before comparing the path from the server is decoded,
    // so we can compare against the original 16-bit string.
    webDavTransaction->SetPropFindPath(propFindPath);
    CleanupStack::Pop(2); // webdavtransaction, uri
    delete uri;
    return webDavTransaction;
    }

// ----------------------------------------------------------------------------
// CRsfwDavSession::GetL
// ----------------------------------------------------------------------------
//
CRsfwDavTransaction* CRsfwDavSession::GetL(const TDesC& aSrcPath,
                                         const TDesC& aDstPath,
                                         TInt aOffset,
                                         TInt* aLength,
                                         TUint aFlags)
    {
    // Basically just a HTTP get with some local processing
    if (!IsConnected())
        {
        User::Leave(KErrNotReady);
        }

    TUriParser8 uriParser;
    HBufC8* uri = BuildUriLC(aSrcPath, EFalse, &uriParser);

#ifdef _DEBUG
    {
    TInt length;
    if (aLength)
        {
        length = *aLength;
        }
    else
        {
        length = 0;
        }
    DEBUGSTRING8(("GET '%S' (off=%d, len=%d)",
                  &uriParser.UriDes(),
                  aOffset,
                  length));
    }
#endif // DEBUG

    // Introducing the webdav headers for GET
    RStringPool stringPool = StringPool();
    RStringF mGet = stringPool.StringF(HTTP::EGET,
                                       RHTTPSession::GetTable());  
    CRsfwDavTransaction* webDavTransaction =
        CRsfwDavTransaction::NewL(this,
                                 EWebDavOpGet,
                                 uriParser,
                                 mGet,
                                 NextWebDavTransactionId());
    CleanupStack::PushL(webDavTransaction);

    // Not sure if this is needed: we are setting conditions
    // which mod_dav never gets, cos this is a GET..
    RHTTPHeaders hdr =
        webDavTransaction->HttpTransaction().Request().GetHeaderCollection();

    // Add headers appropriate to all methods
    SetBasicHeadersL(hdr, uriParser, ETrue);

    if (aLength && (*aLength > 0)) // partial get
        {
        TBuf8<KMaxFieldValueLength> rangeHeader;
        _LIT8(KBytesEquals, "bytes=");
        rangeHeader.Append(KBytesEquals);
        rangeHeader.AppendNum(aOffset);
        rangeHeader.Append('-');
        rangeHeader.AppendNum(aOffset + *aLength - 1);
        SetHeaderL(hdr, HTTP::ERange, rangeHeader);
        }

    webDavTransaction->SetBodyFileL(aDstPath, aOffset, aLength, aFlags);
    CleanupStack::Pop(webDavTransaction);
    CleanupStack::PopAndDestroy(uri);
    return webDavTransaction;
    }

// ----------------------------------------------------------------------------
// CRsfwDavSession::PutL
// This amounts to a PUT sending the src data to the destination.
// Expects that the aSrcPath param is relative to iDavRoot.
// If aSrcPath is empty, an empty file will be created.
// ----------------------------------------------------------------------------
//
CRsfwDavTransaction* CRsfwDavSession::PutL(const TDesC& aSrcPath,
                                         const TDesC& aDstPath,
                                         const TDesC8& aMimeType,
                                         TInt aOffset,
                                         TInt aLength,
                                         TInt aTotalLength,
                                         TBool aUseContentRange,
                                         const TDesC8* aLockToken)
    {  
    if (!IsConnected())
        {
        User::Leave(KErrNotReady);
        }

    TUriParser8 uriParser;
    HBufC8* uri = BuildUriLC(aDstPath, EFalse, &uriParser);

    DEBUGSTRING8(("PUT '%S'", &uriParser.UriDes()));

    RStringPool stringPool = StringPool();
    RStringF mPut = stringPool.OpenFStringL(KWebDavPut);
    CleanupClosePushL(mPut);
    CRsfwDavTransaction* webDavTransaction =
        CRsfwDavTransaction::NewL(this,
                                 EWebDavOpPut,
                                 uriParser,
                                 mPut,
                                 NextWebDavTransactionId());
    CleanupStack::PopAndDestroy(); // mPut
    CleanupStack::PushL(webDavTransaction);

    RHTTPHeaders hdr =
        webDavTransaction->HttpTransaction().Request().GetHeaderCollection(); 

    // Add headers appropriate to all methods
    SetBasicHeadersL(hdr, uriParser, ETrue);  
    SetHeaderL(hdr, HTTP::EContentType, aMimeType);

    if (aLength > 0) // partial put
        {
        if (aUseContentRange)
            {
            TBuf8<KMaxFieldValueLength> rangeHeader;
            _LIT8(KBytes, "bytes ");
            rangeHeader.Append(KBytes);
            rangeHeader.AppendNum(aOffset);
            rangeHeader.Append('-');
            rangeHeader.AppendNum(aOffset + aLength - 1);
            rangeHeader.Append('/');
            if (aTotalLength == 0) 
                {
                // The asterisk "*" character means that 
                // the instance-length is unknown at the time when 
                // the message was generated. 
                rangeHeader.Append('*');
                }
            else
                {
                rangeHeader.AppendNum(aTotalLength);
                }
            SetHeaderL(hdr, HTTP::EContentRange, rangeHeader);
            }
        else
            {
            // server doesn't support Content-Range
            // Leave with KrrNotSupported
            // *aLength = aTotalLength;
            }  
        }

    if (aLockToken)
        {  
        SetLockTokenHeaderL(hdr, uri, aLockToken, ETrue);
        }

    webDavTransaction->SetBodyFileL(aSrcPath, aOffset, &aLength, 0);
    CleanupStack::Pop(webDavTransaction);
    CleanupStack::PopAndDestroy(uri);
    return webDavTransaction;
    }

// ----------------------------------------------------------------------------
// CRsfwDavSession::DeleteL
// ----------------------------------------------------------------------------
//
CRsfwDavTransaction* CRsfwDavSession::DeleteL(const TDesC& aPath,
                                            TBool aIsDir,
                                            const TDesC8* aLockToken)
    {
    // Needs to take locking into account
    if (!IsConnected())
        {
        User::Leave(KErrNotReady);
        }

    TUriParser8 uriParser;
    HBufC8* uri = BuildUriLC(aPath, aIsDir, &uriParser);

    DEBUGSTRING8(("DELETE '%S'", &uriParser.UriDes()));

    RStringPool stringPool = StringPool();
    RStringF mDelete = stringPool.OpenFStringL(KWebDavDelete);
    CleanupClosePushL(mDelete);
    CRsfwDavTransaction* webDavTransaction =
        CRsfwDavTransaction::NewL(this,
                                 EWebDavOpDelete,
                                 uriParser,
                                 mDelete,
                                 NextWebDavTransactionId());
    CleanupStack::PopAndDestroy(); // mDelete
    CleanupStack::PushL(webDavTransaction);

    // need to add a special dir on the i
    RHTTPHeaders hdr =
        webDavTransaction->HttpTransaction().Request().GetHeaderCollection(); 
    // Add headers appropriate to all methods
    SetBasicHeadersL(hdr, uriParser, ETrue);

    if (aLockToken)
        {
        SetLockTokenHeaderL(hdr, uri, aLockToken, ETrue); 
        }

    CleanupStack::Pop(webDavTransaction);
    CleanupStack::PopAndDestroy(uri);
    return webDavTransaction;
    }

// ----------------------------------------------------------------------------
// CRsfwDavSession::MkDirL
// ----------------------------------------------------------------------------
//
CRsfwDavTransaction* CRsfwDavSession::MkDirL(const TDesC& aPath)
    {
    // Executes a MKCOL with the specified name
    if (!IsConnected())
        {
        User::Leave(KErrNotReady);
        }

    TUriParser8 uriParser;
    HBufC8* uri = BuildUriLC(aPath, ETrue, &uriParser);

    DEBUGSTRING8(("MKCOL '%S'", &uriParser.UriDes()));

    RStringPool stringPool = StringPool();
    RStringF mMkCol = stringPool.OpenFStringL(KWebDavMkCol);
    CleanupClosePushL(mMkCol);
    CRsfwDavTransaction* webDavTransaction =
        CRsfwDavTransaction::NewL(this,
                                 EWebDavOpMkCol,
                                 uriParser,
                                 mMkCol,
                                 NextWebDavTransactionId());
    CleanupStack::PopAndDestroy(1); // mMkCol
    CleanupStack::PushL(webDavTransaction);

    // Neeed to add a special dir on the i
    RHTTPHeaders hdr =
        webDavTransaction->HttpTransaction().Request().GetHeaderCollection(); 

    // Add headers appropriate to all methods
    SetBasicHeadersL(hdr, uriParser, ETrue);

    CleanupStack::Pop(2); // webDavTransaction, uri
    delete uri;
    return webDavTransaction;
    }

// ----------------------------------------------------------------------------
// CRsfwDavSession::MoveL
// ----------------------------------------------------------------------------
//
CRsfwDavTransaction* CRsfwDavSession::MoveL(const TDesC& aOldPath,
                                          const TDesC& aNewPath,
                                          TBool aOverwrite,
                                          const TDesC8* aSrcLockToken,
                                          const TDesC8* aDstLockToken)
    {
    if (!IsConnected())
        {
        User::Leave(KErrNotReady);
        }

    TUriParser8 uriParserNew;
    HBufC8* uriNew = BuildUriLC(aNewPath, EFalse, &uriParserNew);
    TUriParser8 uriParserOld;
    HBufC8* uriOld = BuildUriLC(aOldPath, EFalse, &uriParserOld);

    DEBUGSTRING8(("MOVE '%S' to '%S'",
                  &uriParserOld.UriDes(),
                  &uriParserNew.UriDes()));

    RStringPool stringPool = StringPool();
    RStringF mMove = stringPool.OpenFStringL(KWebDavMove);
    CleanupClosePushL(mMove);
    CRsfwDavTransaction* webDavTransaction =
        CRsfwDavTransaction::NewL(this,
                                 EWebDavOpMove,
                                 uriParserOld,
                                 mMove,
                                 NextWebDavTransactionId());
    CleanupStack::PopAndDestroy(); // mMove
    CleanupStack::PushL(webDavTransaction);

    RHTTPHeaders hdr =
        webDavTransaction->HttpTransaction().Request().GetHeaderCollection();

    // Add headers appropriate to all methods
    SetBasicHeadersL(hdr, uriParserOld, ETrue);
    
    if (aSrcLockToken)
        {
        SetLockTokenHeaderL(hdr, uriOld, aSrcLockToken, ETrue);
        }
    
    
     if (aDstLockToken)
        {
        SetLockTokenHeaderL(hdr, uriNew, aDstLockToken, ETrue);
        }      
        
    SetHeaderL(hdr, KWebDavDest, *uriNew);
    if (aOverwrite)
        {
        SetHeaderL(hdr, KWebDavOverwrite, KWebDavOverwriteY);
        }
    else
        {
        SetHeaderL(hdr, KWebDavOverwrite, KWebDavOverwriteN);
        }

    CleanupStack::Pop(webDavTransaction);
    CleanupStack::PopAndDestroy(2, uriNew);   // uriOld, uriNew
    return webDavTransaction;
    }

// ----------------------------------------------------------------------------
// CRsfwDavSession::LockL
// ----------------------------------------------------------------------------
//
CRsfwDavTransaction* CRsfwDavSession::LockL(const TDesC& aPath,
                                          TUint aFlags,
                                          TUint aTimeout,
                                          CRsfwDavFileInfo** aDavFileInfo)
    {
    // Opens LOCK transaction
    if (!IsConnected())
        {
        User::Leave(KErrNotReady);
        }

    TUriParser8 uriParser;
    HBufC8* uri = BuildUriLC(aPath, EFalse, &uriParser);

    DEBUGSTRING8(("LOCK '%S' (%d seconds)", &uriParser.UriDes(), aTimeout));

    RStringPool stringPool = StringPool();
    RStringF mLock = stringPool.OpenFStringL(KWebDavLock);
    CleanupClosePushL(mLock);
    CRsfwDavTransaction* webDavTransaction =
        CRsfwDavTransaction::NewL(this,
                                 EWebDavOpLock,
                                 uriParser,
                                 mLock,
                                 NextWebDavTransactionId());
    CleanupStack::PopAndDestroy(&mLock);
    CleanupStack::PushL(webDavTransaction);

    // headers
    RHTTPHeaders hdr =
        webDavTransaction->HttpTransaction().Request().GetHeaderCollection(); 

    // Add headers appropriate to all methods
    SetBasicHeadersL(hdr, uriParser, ETrue);

    SetHeaderL(hdr, HTTP::EContentType, KTextXml);

    HBufC8* timeoutBuffer = HBufC8::NewLC(KMaxFieldValueLength);
    TPtr8 timeoutBufferPtr = timeoutBuffer->Des();
    timeoutBufferPtr.Append(KSecondDash);
    if (aTimeout != 0)
        {
        timeoutBufferPtr.AppendNum(aTimeout);
        }
    SetHeaderL(hdr, KWebDavTimeout, timeoutBufferPtr);
    CleanupStack::PopAndDestroy(timeoutBuffer);

    // XML body
    HBufC8* requestBodyBuffer = HBufC8::NewL(KDefaultSubmitSize);
    TPtr8 requestBodyBufferPtr = requestBodyBuffer->Des();

    // Note: locktype "write" is currently the only legal value
    _LIT8(KLockHeaderFormat, "\
<?xml version=\"1.0\" encoding=\"utf-8\" ?>\
<D:lockinfo xmlns:D=\"DAV:\">\
<D:lockscope><D:%S/></D:lockscope>\
<D:locktype><D:write/></D:locktype>\
<D:owner xmlns:x=\"http://www.webdav.org/\">\
<x:lock-user>%S</x:lock-user>\
<x:created-by>%S</x:created-by>\
</D:owner>\
</D:lockinfo>");

    _LIT8(KLockScopeShared, "shared");
    _LIT8(KLockScopeExclusive, "exclusive");
    TPtrC8 lockScope;
    if (aFlags & EFileShareAny)
        {
        lockScope.Set(KLockScopeShared);
        }
    else
        {
        lockScope.Set(KLockScopeExclusive);
        }
     
    requestBodyBufferPtr.Format(KLockHeaderFormat, &lockScope, iUserName, iUserName);
    webDavTransaction->SetBodyData(requestBodyBuffer);

    HBufC* fileInfoPath = BuildFullPathLC(aPath, EFalse);
    webDavTransaction->SetDavFileInfoL(aDavFileInfo, *fileInfoPath);
    CleanupStack::PopAndDestroy(fileInfoPath);
    CleanupStack::Pop(webDavTransaction);
    CleanupStack::PopAndDestroy(uri);
    return webDavTransaction;
    }

// ----------------------------------------------------------------------------
// CRsfwDavSession::UnlockL
// ----------------------------------------------------------------------------
//
CRsfwDavTransaction* CRsfwDavSession::UnlockL(const TDesC& aPath,
                                            const TDesC8* aLockToken)
    {
    // Opens LOCK transaction
    if (!IsConnected())
        {
        User::Leave(KErrNotReady);
        }

    if (iWebDavSupportClass < KDavVersionTwo)
        {
        User::Leave(KErrNotSupported);
        }

    TUriParser8 uriParser;
    HBufC8* uri = BuildUriLC(aPath, EFalse, &uriParser);

    DEBUGSTRING8(("UNLOCK '%S'", &uriParser.UriDes()));

    RStringPool stringPool = StringPool();
    RStringF mUnlock = stringPool.OpenFStringL(KWebDavUnlock);
    CleanupClosePushL(mUnlock);
    CRsfwDavTransaction* webDavTransaction =
        CRsfwDavTransaction::NewL(this,
                                 EWebDavOpUnlock,
                                 uriParser,
                                 mUnlock,
                                 NextWebDavTransactionId());
    CleanupStack::PopAndDestroy(); // mUnlock
    CleanupStack::PushL(webDavTransaction);

    RHTTPHeaders hdr =
        webDavTransaction->HttpTransaction().Request().GetHeaderCollection(); 

    // Add headers appropriate to all methods
    SetBasicHeadersL(hdr, uriParser, ETrue);

    HBufC8* lockToken = HBufC8::NewLC(aLockToken->Length() +
                                      KLockTokenOverhead);
    TPtr8 lockTokenPtr = lockToken->Des();
    lockTokenPtr.Append('<');
    lockTokenPtr.Append(*aLockToken);
    lockTokenPtr.Append('>');
    SetHeaderL(hdr, KWedDavLockToken, lockTokenPtr);
    CleanupStack::PopAndDestroy(lockToken);

    CleanupStack::Pop(2); // webdavtransaction , uri
    delete uri;
    return webDavTransaction;
    }

// ----------------------------------------------------------------------------
// CRsfwDavSession::RefreshLockL
// ----------------------------------------------------------------------------
//
CRsfwDavTransaction* CRsfwDavSession::RefreshLockL(const TDesC& aPath,
                                                 TUint aTimeout,
                                                 const TDesC8* aLockToken,
                                                 CRsfwDavFileInfo** aDavFileInfo)
    {
    // Opens LOCK transaction
    if (!IsConnected())
        {
        User::Leave(KErrNotReady);
        }

    if (iWebDavSupportClass < KDavVersionTwo)
        {
        User::Leave(KErrNotSupported);
        }

    TUriParser8 uriParser;
    HBufC8* uri = BuildUriLC(aPath, EFalse, &uriParser);

    DEBUGSTRING8(("LOCK (refresh) '%S' (%d seconds)",
                  &uriParser.UriDes(),
                  aTimeout));

    RStringPool stringPool = StringPool();
    RStringF mLock = stringPool.OpenFStringL(KWebDavLock);
    CleanupClosePushL(mLock);
    CRsfwDavTransaction* webDavTransaction =
        CRsfwDavTransaction::NewL(this,
                                 EWebDavOpRefreshLock,
                                 uriParser,
                                 mLock,
                                 NextWebDavTransactionId());
    CleanupStack::PopAndDestroy(&mLock);
    CleanupStack::PushL(webDavTransaction);

    RHTTPHeaders hdr =
        webDavTransaction->HttpTransaction().Request().GetHeaderCollection(); 

    // Add headers appropriate to all methods
    SetBasicHeadersL(hdr, uriParser, ETrue);

    // do not use tagged lock token, as refresh 'If' header 
    // should always contain only a single lock token 
    // (only one lock may be refreshed at a time).
    SetLockTokenHeaderL(hdr, uri, aLockToken, EFalse);

    HBufC8* timeoutBuffer = HBufC8::NewLC(KMaxFieldValueLength);
    TPtr8 timeoutBufferPtr = timeoutBuffer->Des();
    timeoutBufferPtr.Append(KSecondDash);
    if (aTimeout != 0)
        {
        timeoutBufferPtr.AppendNum(aTimeout);
        }
    SetHeaderL(hdr, KWebDavTimeout, timeoutBufferPtr);
    CleanupStack::PopAndDestroy(timeoutBuffer);

    HBufC* fileInfoPath = BuildFullPathLC(aPath, EFalse);
    webDavTransaction->SetDavFileInfoL(aDavFileInfo, *fileInfoPath);
    CleanupStack::PopAndDestroy(fileInfoPath);
    CleanupStack::Pop(webDavTransaction);
    CleanupStack::PopAndDestroy(uri);
    return webDavTransaction;
    }

// ----------------------------------------------------------------------------
// CRsfwDavSession::GetCredentialsL
// From MHTTPAuthenticationCallback
// ----------------------------------------------------------------------------
//
TBool CRsfwDavSession::GetCredentialsL(const TUriC8& /* aURI */,
                                      RString aRealm,
                                      RStringF /*aAuthenticationType*/,
                                      RString& aUserName,
                                      RString& aPassword)
    {
    // if we have not tried to send the current credentials once,
    // and we have at least username proceed, othwise return KErrAccessDenied
    if (iCredentialRequestCount || (!iUserName))
        {
        iCredentialRequestCount = 0;
        User::Leave(KErrAccessDenied);
        }
    iCredentialRequestCount++;

    TRAPD(err, aUserName = aRealm.Pool().OpenStringL(*iUserName));
    if (!err)
        {
        TRAP(err, aPassword = aRealm.Pool().OpenStringL(*iPassword));
        if (!err)
            {
            return ETrue;
            }
        }  
    return EFalse;
    }

// ----------------------------------------------------------------------------
// CRsfwDavSession::SetHeaderL
// Convenience method for setting up the header.
// ----------------------------------------------------------------------------
//
void CRsfwDavSession::SetHeaderL(RHTTPHeaders aHeaders,
                                TInt aHdrField,
                                const TDesC8& aHdrValue)
    {
    RStringF valStr = StringPool().OpenFStringL(aHdrValue);
    CleanupClosePushL(valStr);
    THTTPHdrVal val(valStr);
    aHeaders.SetFieldL(
        StringPool().StringF(aHdrField, RHTTPSession::GetTable()), val);
    CleanupStack::PopAndDestroy(&valStr);
    }

// ----------------------------------------------------------------------------
// CRsfwDavSession::SetHeaderL
// Convenience method for setting up the header.
// ----------------------------------------------------------------------------
//
void CRsfwDavSession::SetHeaderL(RHTTPHeaders aHeaders,
                                const TDesC8& aHdrName,
                                const TDesC8& aHdrValue)
    {
    RStringF nameStr = StringPool().OpenFStringL(aHdrName);
    CleanupClosePushL(nameStr);
    RStringF valueStr = StringPool().OpenFStringL(aHdrValue);
    CleanupClosePushL(valueStr);
    THTTPHdrVal value(valueStr);
    aHeaders.SetFieldL(nameStr, value);
    CleanupStack::PopAndDestroy(2); // valueStr, nameStr
    }

// ----------------------------------------------------------------------------
// CRsfwDavSession::SetBasicHeadersL
// Convenience method for setting up the header.
// ----------------------------------------------------------------------------
//
void CRsfwDavSession::SetBasicHeadersL(RHTTPHeaders aHeaders, 
                                       const TUriC8& aUri,
                                       TBool aNoProxy)
    {
    // Add headers appropriate to all methods
    SetHeaderL(aHeaders, HTTP::EUserAgent, KUserAgent);
    SetHeaderL(aHeaders, HTTP::EAccept, KAccept);
    // do not send host header if using SSL (not supported currently)
    TPtrC8 scheme;
    if (aUri.IsPresent(EUriScheme))
        {
        scheme.Set(aUri.Extract(EUriScheme));
        }
    if (scheme.CompareF(KHttpsScheme8) != 0) 
    	{
    	SetHeaderL(aHeaders, HTTP::EHost, *iEncodedHost);
    	}
    SetHeaderL(aHeaders, HTTP::EConnection, KKeepAlive);
    if (aNoProxy) 
        {
        // see RFC 2518 10.4.5 If Header and Non-DAV Aware Proxies
        // "As in general clients may not be able to reliably detect 
        // non-DAV aware intermediates, they are advised to always 
        // prevent caching using the request directives mentioned above."
        SetHeaderL(aHeaders, HTTP::EPragma, KWebDavNoProxy);
        SetHeaderL(aHeaders, HTTP::ECacheControl, KWebDavNoProxy);
        }
    }

// ----------------------------------------------------------------------------
// CRsfwDavSession::SetDepthHeaderL
// Some DAV requests require this for specifying depth of copies etc.
// ----------------------------------------------------------------------------
//
void CRsfwDavSession::SetDepthHeaderL(RHTTPHeaders aHeaders, TInt aDepth)
    {
    RStringF depthStr = StringPool().OpenFStringL(KWebDavDepth);
    CleanupClosePushL(depthStr);
    THTTPHdrVal depth(aDepth);
    aHeaders.SetFieldL(depthStr, depth);
    CleanupStack::PopAndDestroy(&depthStr);
    }

// ----------------------------------------------------------------------------
// CRsfwDavSession::SetLockTokenHeaderL
// The lock token header can be tagged with the resource (file) URI
// This is especially important for DELETE and MOVE of a file,
// as they will also affect the container (directory), which is not locked
// (in which case supplying a lock token is an error).
// ----------------------------------------------------------------------------
//
void CRsfwDavSession::SetLockTokenHeaderL(RHTTPHeaders aHeaders,
                                         const TDesC8* aUri,
                                         const TDesC8* aLockToken,
                                         TBool aUseTaggedLockToken)
    {
    DEBUGSTRING(("using a tagged lock token"));
    // KLockTokenOverhead for 'tagged' lock token is 2 chars around the uri,
    // one space, and 4 chars around the locktoken
    // i.e. <target-url> (<target-token>)
    TInt tagoverhead;
    if (aUseTaggedLockToken) 
        {
        tagoverhead = KTaggedLockTokenOverhead;
        }
    else 
        {
        tagoverhead = KLockTokenOverhead;
        }
    HBufC8* lockToken = HBufC8::NewLC(aUri->Length()+ aLockToken->Length() +
                                      tagoverhead);
    TPtr8 lockTokenPtr = lockToken->Des();
    if (aUseTaggedLockToken) 
        {
        lockTokenPtr.Format(KTaggedParenthAngleFormat, aUri, aLockToken);
        }
    else 
        {
        lockTokenPtr.Format(KParenthAngleFormat, aLockToken);
        }

    DEBUGSTRING8(("lt='%S'", &lockTokenPtr));
    SetHeaderL(aHeaders, KWebDavIf, lockTokenPtr);
    CleanupStack::PopAndDestroy(lockToken);
    }

// ----------------------------------------------------------------------------
// CRsfwDavSession::WebDavTransactionCompleteL
// ----------------------------------------------------------------------------
//
void CRsfwDavSession::WebDavTransactionCompleteL(
    CRsfwDavTransaction* aWebDavTransaction)
    {
    TUint webDavTransactionId = aWebDavTransaction->Id();
    delete aWebDavTransaction;
    iWebDavResponseObserver->RequestCompleteL(webDavTransactionId);
    }

// ----------------------------------------------------------------------------
// CRsfwDavSession::WebDavTransactionError
// ----------------------------------------------------------------------------
//
void CRsfwDavSession::WebDavTransactionError(
    CRsfwDavTransaction* aWebDavTransaction)
    {
    TUint webDavTransactionId = aWebDavTransaction->Id();
    TInt status = aWebDavTransaction->Status();
    delete aWebDavTransaction;
    iWebDavResponseObserver->RequestError(webDavTransactionId, status);
    }

// ----------------------------------------------------------------------------
// CRsfwDavSession::SetPropFindParameters
// ----------------------------------------------------------------------------
//
void CRsfwDavSession::SetPropFindParametersL(
    RPointerArray<CRsfwDirEnt>* aDirEntArray,
    const TDesC& aPropFindPath,
    TInt aDepth)
    {
    iPropFindParserImpl->SetDirEntArray(aDirEntArray);
    iPropFindParserImpl->SetTargetDirectory(aPropFindPath, aDepth);
    iPropFindParser->ParseBeginL();
    }

// ----------------------------------------------------------------------------
// CRsfwDavSession::SetLockQueryParameters
// ----------------------------------------------------------------------------
//
void CRsfwDavSession::SetLockQueryParameters(CRsfwDavFileInfo* aDavFileInfo)
    {
    iLockQueryParserImpl->SetDavFileInfo(aDavFileInfo);
    }

// ----------------------------------------------------------------------------
// CRsfwDavSession::ParsePropFindResponseL
// ----------------------------------------------------------------------------
//
void CRsfwDavSession::ParsePropFindResponseL(const TDesC8& aResponse)
    {
    // only first call to this function really initiates data structures in the XML parser
    iPropfindParsingActive = ETrue;
    iPropFindParser->ParseL(aResponse);
    }

// ----------------------------------------------------------------------------
// CRsfwDavSession::ParseLockResponseL
// ----------------------------------------------------------------------------
//
void CRsfwDavSession::ParseLockResponseL(const TDesC8& aResponse)
    {
    iLockQueryParser->ParseL(aResponse);
    }

// ----------------------------------------------------------------------------
// CRsfwDavSession::PropFindResponseEndL
// ----------------------------------------------------------------------------
//
void CRsfwDavSession::PropFindResponseEndL()
    {
    iPropFindParser->ParseEndL();
    iPropfindParsingActive = EFalse;
    TInt err = iPropFindParserImpl->GetLastError();
    if (err) 
   	 	{
    	User::Leave(err);
    	}
    }

// ----------------------------------------------------------------------------
// CRsfwDavSession::LockResponseEndL
// ----------------------------------------------------------------------------
//
void CRsfwDavSession::LockResponseEndL()
    {
    iLockQueryParser->ParseEndL();
    TInt err = iPropFindParserImpl->GetLastError();
    if (err) 
   	 	{
    	User::Leave(err);
    	}
    }

// ----------------------------------------------------------------------------
// CRsfwDavSession::CancelParsing
// ----------------------------------------------------------------------------
//
void CRsfwDavSession::CancelParsing(TWebDavOp aOp)
    {
    // Only will do something if this operation is PROPFIND,
    // and we have started to parse the response.
    // UI does not allow to cancel LOCK requests.
    // If this would be possible this mechanism should be expanded
    // to also cover LOCK parsing.
    if ((aOp == EWebDavOpPropFindSingle) || 
    (aOp == EWebDavOpPropFindMulti)) 
        {
        if (iPropfindParsingActive)   
            {
            // When XML parsing is cancelled when the request is cancelled, 
            // there is some XML error (invalid token etc.), ignore the error
            TRAP_IGNORE(iPropFindParser->ParseEndL());
            iPropfindParsingActive = EFalse;
            }
        }
    }
    

// ----------------------------------------------------------------------------
// CRsfwDavSession::HttpSession
// ----------------------------------------------------------------------------
//
RHTTPSession& CRsfwDavSession::HttpSession()
    {
    return iHttpSession;
    }

// ----------------------------------------------------------------------------
// CRsfwDavSession::StringPool
// ----------------------------------------------------------------------------
//
RStringPool CRsfwDavSession::StringPool()
    {
    return iHttpSession.StringPool();
    }

// ----------------------------------------------------------------------------
// CRsfwDavSession::Slashify
// ----------------------------------------------------------------------------
//
void CRsfwDavSession::Slashify(TDes& aStr)
    {
    if (aStr.Length() &&
        (aStr[aStr.Length() - 1] != '/') &&
        aStr.Length() < aStr.MaxLength())
        {
        aStr.Append('/');
        }
    }

// ----------------------------------------------------------------------------
// CRsfwDavSession::BuildPathLC
// This function constructs a file path from davroot + path
// ----------------------------------------------------------------------------
//
HBufC* CRsfwDavSession::BuildPathLC(const TDesC& aRoot,
                                   const TDesC& aPath,
                                   TBool aEndSlash)
    {
    // 1 is for a possible slash added to the end of the uri...
    HBufC* path = HBufC::NewLC(aRoot.Length() +
                               aPath.Length() +
                               1);
    TPtr pathPtr = path->Des();
    pathPtr.Append(aRoot);
    pathPtr.Append(aPath);
    if (aEndSlash)
        {
        Slashify(pathPtr);
        }
    return path;
    }

// ----------------------------------------------------------------------------
// CRsfwDavSession::BuildFullPathLC
// This function constructs a file path from davroot + path
// ----------------------------------------------------------------------------
//
HBufC* CRsfwDavSession::BuildFullPathLC(const TDesC& aPath,
                                       TBool aEndSlash)
    {
    return BuildPathLC(iDavRoot, aPath, aEndSlash);
    }

// ----------------------------------------------------------------------------
// CRsfwDavSession::BuildUriLC
// This function constructs an URI from hostname + davroot + path
// The URI is first escape encoded and then UTF-8 encoded.
// Note that in addition to returning the uri string, this function
// will also "populate" aUriParser with the full URI
// ----------------------------------------------------------------------------
//
HBufC8* CRsfwDavSession::BuildUriLC(const TDesC& aPath,
                                   TBool aEndSlash,
                                   TUriParser8* aUriParser)
    {
    // 1 is for a possible slash added to the end of the uri...
    HBufC* uri = BuildPathLC(iHostRoot, aPath, aEndSlash);
    HBufC8* utf8Path = EncodeL(*uri);
    CleanupStack::PopAndDestroy(uri);
    CleanupStack::PushL(utf8Path);
    TPtr8 utf8PathPtr = utf8Path->Des();
    if (aUriParser)
        {
        if (aUriParser->Parse(utf8PathPtr) != KErrNone)
            {
            User::Leave(KErrBadName);
            }
        }
    return utf8Path;
    }

// ----------------------------------------------------------------------------
// CRsfwDavSession::EncodeL
// First escape encode and then UTF-8 encode data.
// ----------------------------------------------------------------------------
//
HBufC8* CRsfwDavSession::EncodeL(const TDesC& aData)
    {
    HBufC8* utf8Data = EscapeUtils::ConvertFromUnicodeToUtf8L(aData);
    CleanupStack::PushL(utf8Data);
    HBufC8* escapedData = EscapeUtils::EscapeEncodeL(*utf8Data, KSpecials8);
    CleanupStack::PopAndDestroy(utf8Data);
    return escapedData;
    }

// ----------------------------------------------------------------------------
// CRsfwDavSession::IsConnected
// ----------------------------------------------------------------------------
//
TBool CRsfwDavSession::IsConnected()
    {
    return iConnected;
    }

// ----------------------------------------------------------------------------
// CRsfwDavSession::NextWebDavTransactionId
// ----------------------------------------------------------------------------
//
TUint CRsfwDavSession::NextWebDavTransactionId()
    {
    return ++iCurrentWebDavTransactionId;
    }

// ----------------------------------------------------------------------------
// CRsfwDavSession::SetupConnectionL
// ----------------------------------------------------------------------------
//
void CRsfwDavSession::SetupConnectionL()
    {
    RSocketServ* socketServ;
    RConnection* connection;

    DEBUGSTRING(("SetupConnection - start"));
    User::LeaveIfError(iRsfwConnectionManager->GetConnection(socketServ, 
                                                             connection));
    DEBUGSTRING(("iRsfwConnectionManager->GetConnection called"));                                                        
    // Now bind the HTTP session with the socket server connection
    RStringPool stringPool = iHttpSession.StringPool();
    RHTTPConnectionInfo connInfo = iHttpSession.ConnectionInfo();
    connInfo.SetPropertyL(
        stringPool.StringF(HTTP::EHttpSocketServ, RHTTPSession::GetTable()),
        THTTPHdrVal(socketServ->Handle()));
    connInfo.SetPropertyL(
        stringPool.StringF(HTTP::EHttpSocketConnection,
                           RHTTPSession::GetTable()), 
        THTTPHdrVal(reinterpret_cast<TInt>(connection)));
    DEBUGSTRING(("SetupConnection - done"));
    }
    
//  End of File