diff -r 000000000000 -r e4d67989cc36 genericservices/httputils/UriParser/CUri.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/genericservices/httputils/UriParser/CUri.cpp Tue Feb 02 02:01:42 2010 +0200 @@ -0,0 +1,1462 @@ +// Copyright (c) 2001-2009 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: +// + +#include +#include +#include +#include +#include +#include +#include + +#include "UriUtilsInternal.h" +#include "TUriCInternal.h" +#include "CUriInternal.h" + +// Panic category +// +#ifdef _DEBUG + _LIT(KUriPanicCategory,"URI-CURI"); +#endif + +// Constants +// +_LIT(KFileUriPanicCategory,"FILEURI-CURI"); + + + + +// +// +// Implementation of CUri8 +// +// + +/** + Static factory constructor. Uses two phase construction and leaves nothing on the + CleanupStack. Creates a uri object which is a copy of the input parameter aUri. + + @since 6.0 + @param aUri A reference to a parsed uri object. + @return A pointer to the newly created CUri8 object. + @post A fully constructed and initialized CUri8 object. + */ +EXPORT_C CUri8* CUri8::NewL(const TUriC8& aUri) + { + CUri8* self = CUri8::NewLC(aUri); + CleanupStack::Pop(self); + return self; + } + +/** + Static factory constructor. Uses two phase construction and leaves a pointer to + created object on the CleanupStack. Creates a uri object which is a copy of the + input parameter aUri. + + @since 6.0 + @param aUri A reference to a parsed uri object. + @return A pointer to the newly created CUri8 object. + @post A fully constructed and initialized CUri8 object. + */ +EXPORT_C CUri8* CUri8::NewLC(const TUriC8& aUri) + { + CUri8* self = new (ELeave) CUri8(aUri); + CleanupStack::PushL(self); + self->ConstructL(); + return self; + } + +/** + Static factory constructor. Uses two phase construction and leaves nothing on the + CleanupStack. Creates a uri object which is empty. + + @since 6.0 + @return A pointer to the newly created CUri8 object. + @post A fully constructed and initialized CUri8 object. + */ +EXPORT_C CUri8* CUri8::NewL() + { + CUri8* self = CUri8::NewLC(); + CleanupStack::Pop(self); + return self; + } + +/** + Static factory constructor. Uses two phase construction and leaves a pointer to created + object on the CleanupStack. Creates a uri object which is empty. + + @since 6.0 + @return A pointer to the newly created CUri8 object. + @post A fully constructed and initialized CUri8 object. + */ +EXPORT_C CUri8* CUri8::NewLC() + { + CUri8* self = new (ELeave) CUri8(TUriC8()); + CleanupStack::PushL(self); + self->ConstructL(); + return self; + } + +/** + Static factory constructor. This creates a CUri8 object that is an absolute uri resulting + from a reference uri being resolved against a base uri. + + @warning Ownership of created CUri8 object is transferred to the caller. + @leave KUriErrBadBasePath if the base path is not an absolute path and not empty. + @since 6.0 + @param aBaseUri A referece to the parsed base uri. + @param aRefUri A referece to the parsed reference uri. + @return A pointer to the newly created CUri8 object. + @pre The base uri must have an absolute or empty path, otherwise will leave + with KUriErrBadBasePath. + @post A fully constructed and initialized CUri8 object. + */ +EXPORT_C CUri8* CUri8::ResolveL(const TUriC8& aBaseUri, const TUriC8& aRefUri) + { + // Check for a base Uri + if( aBaseUri.UriDes().Compare(KNullDesC8) == 0 ) + { + // Empty base Uri - resolved Uri is the reference Uri + return NewL(aRefUri); + } + // See if ref has scheme and it is the same as base Uri + if( aRefUri.IsPresent(EUriScheme) && (aRefUri.Compare(aBaseUri, EUriScheme) != 0) ) + { + // Ref has a scheme different to base Uri's - it is an absolute Uri + return NewL(aRefUri); + } + // Check for presence of components + TBool useBaseQuery = EFalse; + HBufC8* resolvedPath = FormResolvedPathLC(aBaseUri, aRefUri, useBaseQuery); + + //Removes dot segemnts in Resolved uri as specified in RFC3986 section 5.2. + RemoveExtraneousDotSegmentsL(resolvedPath); + + // Put the Uri together + TUriC8 uri; + FormResolvedUri(uri.iComponent, aBaseUri, aRefUri, resolvedPath, useBaseQuery); + + // Create the resolved Uri and cleanup + CUri8* resolvedUri = NewL(uri); + CleanupStack::PopAndDestroy(resolvedPath); + + return resolvedUri; + } + +/** + Destructor. + + @since 6.0 + */ +EXPORT_C CUri8::~CUri8() + { + delete iUriBuf; + } + +/** + Provides a reference to the parsed uri. Allows access to the non-modifying API for TUriC8. + + @since 6.0 + @return A const reference to the parsed uri object. + */ +EXPORT_C const TUriC8& CUri8::Uri() const + { + return iUri; + } + +/** + Intended Usage : Sets the specified component in the uri. The component is set to the value + given in the argument aData. If the specified component already exists then it is replaced + with the new value. + + @warning The userinfo and port components can only be set if the host component + is present. Setting these components without a host component present will have no + effect on the uri. + @since 6.0 + @param aData A descriptor pointer to the new value for the uri component. + @param aComponent An enum specifying the component to be set. + @pre Object is fully constructed. + @post The uri has the specified component set to the new value. + @Leave KErrArgument If aComponent goes out of range. + */ +EXPORT_C void CUri8::SetComponentL(const TDesC8& aData, TUriComponent aComponent) + { + // Update the appropriate component table entry + iUri.iComponent[aComponent].Set(aData); + + // Copy to the buffer by forming the uri + FormUriL(); + } + +/** + Removes the specified component from the uri. If the component does not exist then this function + does nothing. + + @warning If host is removed, then userinfo and port components will also + be removed. + @since 6.0 + @param aComponent An enum specifying the component to be removed. + @pre Object is fully constructed. + @post The uri is updated to exclude the specified component. + */ +EXPORT_C void CUri8::RemoveComponentL(TUriComponent aComponent) + { + if( iUri.IsPresent(aComponent) ) + { + // Remove the component - set pointer to NULL and length to zero + iUri.iComponent[aComponent].Set(NULL,0); + + // Re-form buffer and component table + FormUriL(); + } + } + +/** + Constructor. First phase of two-phase construction method. Does non-allocating construction. + + @since 6.0 + @param aUri The parsed uri component information from which to create + the uri. +*/ +CUri8::CUri8(const TUriC8& aUri) +: CBase(), iUri(aUri) + { + } + +/** + Second phase of two-phase construction method. Does any allocations required to fully construct + the object. + + @since 6.0 + @pre First phase of construction is complete. + @post The object is fully constructed and initialized. + */ +void CUri8::ConstructL() + { + // Create the HBufC + FormUriL(); + } + +/** + Forms the uri from the parsed uri information. A copy of the parsed uri is created. The parsed uri + is changed to refer to the copy. + + @since 6.0 + @pre The parsed uri information is set. + @post The uri buffer is updated with the parsed uri information. + */ +void CUri8::FormUriL() + { + TBool isIPv6Host; + + // Calculate length of of the Uri + TInt length = CalculateUriLength(iUri.iComponent, isIPv6Host); + + // Create a temporary buffer and descriptor pointer to it + HBufC8* buf = HBufC8::NewL(length); + TPtr8 uri = buf->Des(); + + // Create the uri, updating the internal uri object + DoFormUri(uri, iUri.iComponent, isIPv6Host); + + // Update the internal buffer and descriptor pointer + delete iUriBuf; + iUriBuf = buf; + iUri.iUriDes.Set(iUriBuf->Des()); + } + +// +// +// Implementation of CUri16 +// +// + +/** + Static factory constructor. Uses two phase construction and leaves nothing on the CleanupStack. + Creates a uri object which is a copy of the input parameter aUri. + + @deprecated Deprecated in 9.1 + @since 6.0 + @param aUri A reference to a parsed uri object. + @return A pointer to the newly created CUri16 object. + @post A fully constructed and initialized CUri16 object. + */ +EXPORT_C CUri16* CUri16::NewL(const TUriC16& aUri) + { + CUri16* self = CUri16::NewLC(aUri); + CleanupStack::Pop(self); + return self; + } + +/** + Static factory constructor. Uses two phase construction and leaves a pointer to created object on + the CleanupStack. Creates a uri object which is a copy of the input parameter aUri. + + @since 6.0 + @deprecated Deprecated in 9.1 + @param aUri A reference to a parsed uri object. + @return A pointer to the newly created CUri16 object. + @post A fully constructed and initialized CUri16 object. + */ +EXPORT_C CUri16* CUri16::NewLC(const TUriC16& aUri) + { + CUri16* self = new (ELeave) CUri16(aUri); + CleanupStack::PushL(self); + self->ConstructL(); + return self; + } + +/** + Static factory constructor. Uses two phase construction and leaves nothing on the CleanupStack. + Creates a uri object which is empty. + + @since 6.0 + @deprecated Deprecated in 9.1 + @return A pointer to the newly created CUri16 object. + @post A fully constructed and initialized CUri16 object. + */ +EXPORT_C CUri16* CUri16::NewL() + { + CUri16* self = CUri16::NewLC(); + CleanupStack::Pop(self); + return self; + } + +/** + Static factory constructor. Uses two phase construction and leaves a pointer to created object on + the CleanupStack. Creates a uri object which is empty. + + @since 6.0 + @deprecated Deprecated in 9.1 + @return A pointer to the newly created CUri16 object. + @post A fully constructed and initialized CUri16 object. + */ +EXPORT_C CUri16* CUri16::NewLC() + { + CUri16* self = new (ELeave) CUri16(TUriC16()); + CleanupStack::PushL(self); + self->ConstructL(); + return self; + } + +/** + Static factory constructor. This creates a CUri16 object that is an absolute uri resulting from a + reference uri being resolved against a base uri. + + @warning Ownership of created CUri16 object is transferred to caller. + @leave KUriErrBadBasePath if the base path is not an absolute path and not empty. + @since 6.0 + @deprecated Deprecated in 9.1 + @param aBaseUri A referece to the parsed base uri. + @param aRefUri A referece to the parsed reference uri. + @return A pointer to the newly created CUri16 object. + @pre The base uri must have an absolute or empty path, otherwise will leave + with KUriErrBadBasePath. + @post A fully constructed and initialized CUri16 object. + */ +EXPORT_C CUri16* CUri16::ResolveL(const TUriC16& aBaseUri, const TUriC16& aRefUri) + { + // Check for a base Uri + if( aBaseUri.UriDes().Compare(KNullDesC16) == 0 ) + { + // Empty base Uri - resolved Uri is the reference Uri + return NewL(aRefUri); + } + // See if ref has scheme and it is the same as base Uri + if( aRefUri.IsPresent(EUriScheme) && aRefUri.Compare(aBaseUri, EUriScheme) != 0 ) + { + // Ref has a scheme different to base Uri's - it is an absolute Uri + return NewL(aRefUri); + } + // Check for presence of components + TBool useBaseQuery = EFalse; + HBufC16* resolvedPath = FormResolvedPathLC(aBaseUri, aRefUri, useBaseQuery); + + // Put the Uri together + TUriC16 uri; + FormResolvedUri(uri.iComponent, aBaseUri, aRefUri, resolvedPath, useBaseQuery); + + // Create the resolved Uri and cleanup + CUri16* resolvedUri = NewL(uri); + CleanupStack::PopAndDestroy(resolvedPath); + + return resolvedUri; + } + +/** + Destructor. + + @since 6.0 + @deprecated Deprecated in 9.1 + */ +EXPORT_C CUri16::~CUri16() + { + delete iUriBuf; + } + +/** + Provides a reference to the parsed uri. Allows access to the non-modifying API for TUriC16. + + @since 6.0 + @deprecated Deprecated in 9.1 + @return A const reference to the parsed uri object. + */ +EXPORT_C const TUriC16& CUri16::Uri() const + { + return iUri; + } + +/** + Sets the specified component in the uri. The component is set to the value given in the argument + aData. If the specified component already exists then it is replaced with the new value. + + @warning The userinfo and port components can only be set if the host component + is present. Setting these components without a host component present will have no + effect on the uri. + @since 6.0 + @deprecated Deprecated in 9.1 + @param aData A descriptor pointer to the new value for the uri component. + @param aComponent An enum specifying the component to be set. + @pre Object is fully constructed. + @post The uri has the specified component set to the new value. + @Leave KErrArgument If aComponent goes out of range. + */ +EXPORT_C void CUri16::SetComponentL(const TDesC16& aData, TUriComponent aComponent) + { + // Update the appropriate component table entry + iUri.iComponent[aComponent].Set(aData); + + // Copy to the buffer by forming the uri + FormUriL(); + } + +/** + Removes the specified component from the uri. If the component does not exist then this function + does nothing. + + @warning If host is removed, then userinfo and port components will also + be removed. + @since 6.0 + @deprecated Deprecated in 9.1 + @param aComponent An enum specifying the component to be removed. + @pre Object is fully constructed. + @post The uri is updated to exclude the specified component. + */ +EXPORT_C void CUri16::RemoveComponentL(TUriComponent aComponent) + { + if( iUri.IsPresent(aComponent) ) + { + // Remove the component - set pointer to NULL and length to zero + iUri.iComponent[aComponent].Set(NULL,0); + + // Re-form buffer and component table + FormUriL(); + } + } + +/** + Constructor. First phase of two-phase construction method. Does non-allocating construction. + + @since 6.0 + @param aUri The parsed uri component information from which to create + the uri. + */ + +CUri16::CUri16(const TUriC16& aUri) +: CBase(), iUri(aUri) + { + } + +/** + Second phase of two-phase construction method. Does any allocations required to fully construct + the object. + + @since 6.0 + @pre First phase of construction is complete. + @post The object is fully constructed and initialized. + */ +void CUri16::ConstructL() + { + // Create the HBufC + FormUriL(); + } + +/** + Forms the uri from the parsed uri information. A copy of the parsed uri is created. The parsed uri + is changed to refer to the copy. + + @since 6.0 + @pre The parsed uri information is set. + @post The uri buffer is updated with the parsed uri information. + */ +void CUri16::FormUriL() + { + TBool isIPv6Host; + + // Calculate length of of the Uri + TInt length = CalculateUriLength(iUri.iComponent, isIPv6Host); + + // Create a temporary buffer and descriptor pointer to it + HBufC16* buf = HBufC16::NewL(length); + TPtr16 uri = buf->Des(); + + // Create the uri, updating the internal uri object + DoFormUri(uri, iUri.iComponent, isIPv6Host); + + // Update the internal buffer and descriptor pointer + delete iUriBuf; + iUriBuf = buf; + iUri.iUriDes.Set(iUriBuf->Des()); + } + +// +// +// Implementation of templated LOCAL functions +// +// + +/** + Calculates the length of the uri from a list of the components. + + @since 6.0 + @param aComponent The array of descriptor pointers to the uri + components. + @param aIsIPv6Host ETrue if an IPv6 format host is used + @return The length of the uri including the required delimiters. +*/ +template +TInt CalculateUriLength(const TPtrCType aComponent[], TBool& aIsIPv6Host) + { + TBool noAuthority = ETrue; + TInt length=0; + aIsIPv6Host=EFalse; + for( TInt i=0; i +void DoFormUri(TPtrType& aUri, TPtrCType aComponent[], TBool& aIsIPv6Host) + { + TBool isNetworkScheme = ETrue; + if( aComponent[EUriScheme].Ptr() ) + { + // Update the scheme + SetScheme(aUri, aComponent[EUriScheme]); + isNetworkScheme = IsNetworkScheme(aComponent[EUriScheme]); + } + if( aComponent[EUriHost].Ptr() ) + { + // Update the authority - only needed if there is a host; update userinfo, host and port + SetAuthority(aUri, aComponent[EUriUserinfo], aComponent[EUriHost], aComponent[EUriPort], aIsIPv6Host, isNetworkScheme); + } + else + { + // Ensure that there is no userinfo or port components if there is no host + // - set pointer to NULL and length to zero + aComponent[EUriUserinfo].Set(NULL,0); + aComponent[EUriPort].Set(NULL,0); + } + if( aComponent[EUriPath].Ptr() ) + { + // Update the path + SetPath(aUri, aComponent[EUriPath]); + } + if( aComponent[EUriQuery].Ptr() ) + { + // Update the query + SetQuery(aUri, aComponent[EUriQuery]); + } + if( aComponent[EUriFragment].Ptr() ) + { + // Update the fragment + SetFragment(aUri, aComponent[EUriFragment]); + } + } + +/** + Templated function to set the scheme in a uri. The output argument aUri points to the descriptor + buffer into which aScheme will be copied.The argument aScheme is then updated to point to the + copied version in aUri. + + @warning This function will panic with KUriErrBufferOverflow if there is not + enough space in the descriptor to append the scheme and the required delimiter. + @since 6.0 + @param aUri The descriptor pointer to buffer to be appended. + @param aScheme The descriptor pointer to the scheme component to be copied + and then updated. + @pre The buffer pointed to by aUri should be large enough to have aNewScheme + appended to it with the required delimiter. This can be obtained using CalculateUriLength(). + @post The uri buffer now includes a copy of aScheme and aScheme points to the + copy of the scheme component in aUri. +*/ +template +void SetScheme(TPtrType& aUri, TPtrCType& aScheme) + { + __ASSERT_DEBUG(aUri.Length() + aScheme.Length() + 1 <= aUri.MaxLength(), User::Panic(KUriPanicCategory, KUriUtilsErrBufferOverflow)); + + // Append the scheme and delimiter + aUri.Append(aScheme); + aUri.Append(KSchemeDelimiter); + + // Update the component table to use the copy + aScheme.Set(aUri.Left(aScheme.Length())); + } + +/** + Templated function to set the authority in a uri. The output argument aUri points to the descriptor + buffer into which aUserinfo, aHost and aPort will be copied. The arguments aUserinfo, aHost and aPort + are updated to point to the copied versions in aUri. + + @warning This function will panic with KUriErrBufferOverflow if there + is not enough space in the descriptor to append the components and any required + delimiters. + @since 6.0 + @param aUri The descriptor pointer to buffer to be appended. + @param aUserinfo The descriptor pointer to the userinfo component to + be copied and then updated. + @param aHost The descriptor pointer to the host component to + be copied and then updated. + @param aPort The descriptor pointer to the port component to + be copied and then updated. + @param aIsIPv6Host ETrue if an IPv6 format host is used + @param aUseNetworkDelimiter EFalse if this is a SIP scheme otherwise ETrue + @pre The buffer pointed to by aUri should be large enough to have + aUserinfo, aHost and aPort appended to it with the required delimiters. + This can be obtained using CalculateUriLength(). + @post The uri buffer now includes a copy of aUserinfo, aHost and + aPort, and aUserinfo, aHost and aPort will refer to the copies versions in aUri. +*/ +template +void SetAuthority(TPtrType& aUri, TPtrCType& aUserinfo, TPtrCType& aHost, TPtrCType& aPort, TBool& aIsIPv6Host, TBool aUseNetworkDelimiter) + { + __ASSERT_DEBUG(aUri.Length() + aHost.Length() + (aUseNetworkDelimiter ? KUriNetworkAuthorityDelimiterLength:0) <= aUri.MaxLength(), User::Panic(KUriPanicCategory, KUriUtilsErrBufferOverflow)); + + if (aUseNetworkDelimiter) + { + // If a network scheme append authority delimiter (TWO slash delimiters!) + aUri.Append(KSlashDelimiter); + aUri.Append(KSlashDelimiter); + } + + // Check for userinfo + if( aUserinfo.Ptr() ) + { + __ASSERT_DEBUG(aUri.Length() + aUserinfo.Length() + aHost.Length() + 1 <= aUri.MaxLength(), User::Panic(KUriPanicCategory, KUriUtilsErrBufferOverflow)); + + // Append userinfo and update the component table to use copy + aUri.Append(aUserinfo); + aUserinfo.Set(aUri.Right(aUserinfo.Length())); + + // Append delimiter + aUri.Append(KUserinfoDelimiter); + } + // There's always a host - append and update the component table to use the copy + + // Check if it's an IPv6 address + if ( aIsIPv6Host ) + { + aUri.Append(KIPv6UriOpenBrace); + aUri.Append(aHost); + aUri.Append(KIPv6UriCloseBrace); + // Dont include the braces in the host + // Position = (length of uri - length of host) - length of end brace + aHost.Set( aUri.Mid((aUri.Length()-aHost.Length())-1, aHost.Length()) ); + } + else + { + aUri.Append(aHost); + aHost.Set(aUri.Right(aHost.Length())); + } + + // Check for a port + if( aPort.Ptr() ) + { + __ASSERT_DEBUG(aUri.Length() + aPort.Length() + 1 <= aUri.MaxLength(), User::Panic(KUriPanicCategory, KUriUtilsErrBufferOverflow)); + + // Append delimiter + aUri.Append(KPortDelimiter); + + // Append port and update the component table to use copy + aUri.Append(aPort); + aPort.Set(aUri.Right(aPort.Length())); + } + } +/** + Templated function to set the path in a uri. The output argument aUri points to the descriptor + buffer into which aPath will be copied.The argument aPath is then updated to point to the copied + version in aUri. + + @warning This function will panic with KUriErrBufferOverflow if there + is not enough space in the descriptor to append the path. + @since 6.0 + @param aUri The descriptor pointer to buffer to be appended. + @param aPath The descriptor pointer to the path component to be copied + and then updated. + @pre The buffer pointed to by aUri should be large enough to have + aPath appended to it. This can be obtained using CalculateUriLength(). + @post The uri buffer now includes a copy of aPath and aPath points to the + copy of the path component in aUri. +*/ +template +void SetPath(TPtrType& aUri, TPtrCType& aPath) + { + __ASSERT_DEBUG(aUri.Length() + aPath.Length() <= aUri.MaxLength(), User::Panic(KUriPanicCategory, KUriUtilsErrBufferOverflow)); + + // Append the path + aUri.Append(aPath); + + // Update the component table + aPath.Set(aUri.Right(aPath.Length())); + } + +/** + Templated function to set the query in a uri. The output argument aUri points to the descriptor + buffer into which aQuery will be copied. The argument aQuery is then updated to point to the copied + version in aUri. + + @warning This function will panic with KUriErrBufferOverflow if there + is not enough space in the descriptor to append the query and the delimiter. + @since 6.0 + @param aUri The descriptor pointer to buffer to be appended. + @param aQuery The descriptor pointer to the query component to be copied + and then updated. + @pre The buffer pointed to by aUri should be large enough to have + aQuery appended to it. This can be obtained using CalculateUriLength(). + @post The uri buffer now includes a copy of aQuery and aQuery points to the + copy of the query component in aUri. +*/ +template +void SetQuery(TPtrType& aUri, TPtrCType& aQuery) + { + __ASSERT_DEBUG(aUri.Length() + aQuery.Length() + 1 <= aUri.MaxLength(), User::Panic(KUriPanicCategory, KUriUtilsErrBufferOverflow)); + + // Append delimiter and the query + aUri.Append(KQueryDelimiter); + aUri.Append(aQuery); + + // Update the component table + aQuery.Set(aUri.Right(aQuery.Length())); + } + +/** + Templated function to set the fragment in a uri. The output argument aUri points to the descriptor + buffer into which aFragment will be copied. The argument aFragment is then updated to point to the + copied version in aUri. + + @warning This function will panic with KUriErrBufferOverflow if there + is not enough space in the descriptor to append the fragment and the delimiter. + @since 6.0 + @param aUri The descriptor pointer to buffer to be appended. + @param aFragment The descriptor pointer to the fragment component + to be copied and then updated. + @pre The buffer pointed to by aUri should be large enough to have + aFragment appended to it. This can be obtained using CalculateUriLength(). + @post The uri buffer now includes a copy of aFragment and aFragment points + to the copy of the fragment component in aUri. +*/ +template +void SetFragment(TPtrType& aUri, TPtrCType& aFragment) + { + __ASSERT_DEBUG(aUri.Length() + aFragment.Length() + 1 <= aUri.MaxLength(), User::Panic(KUriPanicCategory, KUriUtilsErrBufferOverflow)); + + // Append delimiter and the fragment + aUri.Append(KFragmentDelimiter); + aUri.Append(aFragment); + + // Update the component table + aFragment.Set(aUri.Right(aFragment.Length())); + } + +/** + Forms the resolved path. Checks to see if the base query needs to be used in the resolved uri. + The pointer to the resolved path is left on the cleanup stack. + + @since 6.0 + @param aBaseUri The base uri. + @param aRefUri The reference uri. + @param aUseBaseQuery An output argument specifying whether the base + query should be used in the resolved uri. + @return A pointer to a buffer that contains the resolved path. +*/ +template +HBufCType* FormResolvedPathLC(const TUriCType& aBaseUri, const TUriCType& aRefUri, TBool& aUseBaseQuery) + { + HBufCType* resolvedPath = NULL; + if( !aRefUri.IsPresent(EUriScheme) && !aRefUri.IsPresent(EUriHost) && !aRefUri.Extract(EUriPath).Length() && !aRefUri.IsPresent(EUriQuery) ) + { + // Ref is just a fragment + aUseBaseQuery = ETrue; + resolvedPath = aBaseUri.Extract(EUriPath).AllocLC(); + } + else if( aRefUri.IsPresent(EUriHost) ) + { + // Ref is a network path + resolvedPath = aRefUri.Extract(EUriPath).AllocLC(); + } + else + { + // Need to some path resolving... + resolvedPath = ResolvePathsL(aBaseUri.Extract(EUriPath), aRefUri.Extract(EUriPath)); + CleanupStack::PushL(resolvedPath); + } + return resolvedPath; + } + +/** + Cleans up a resolved path. This deals with occurences of '.' and '..' where these are complete + path segments. + + @since 6.0 + @param aResolvedPath The delimited data object that contains the + resolved path. + @pre The input/output argument contains the path to be cleaned. + @post The resolved path has had all the occurences of '.' and '..' + processed and has been updated to contain the cleaned path. + */ +template +void CleanResolvedPathL(CDelimitedDataBaseType* aResolvedPath) + { + // Create a modifiable path object for resolved path + aResolvedPath->Parse(); + + TBool done = EFalse; + while( !done ) + { + // Get the next segment + TPtrCType segment; + TInt more = aResolvedPath->Parser().Peek(segment); + + if( more == KErrNotFound ) + { + // No more segments - done + done = ETrue; + } + else if( IsParentDir(segment) ) + { + // Found a '..' - remove '..' from path, and remove previous segment + aResolvedPath->RemoveCurrentL(); + if( aResolvedPath->Parser().Dec() == KErrNotFound ) + { + // No previous directory - put back '..' and stop + InsertParentDirL(aResolvedPath); + done = ETrue; + } + else + { + // Remove the parent directory + aResolvedPath->RemoveCurrentL(); + if( aResolvedPath->Parser().Eos() ) + { + // '..' is the last segment - add a '/' to the path (add empty segment) + aResolvedPath->AddBackDelimiterL(); + done = ETrue; + } + } + } + else if( IsSameDir(segment) ) + { + // Found a '.' - remove -.- from the path + aResolvedPath->RemoveCurrentL(); + if( aResolvedPath->Parser().Eos() ) + { + // '..' is the last segment - add a '/' to the path (add empty segment) + aResolvedPath->AddBackDelimiterL(); + done = ETrue; + } + } + else + { + // Segment wasn't '.' or '..' - parse to next segment + aResolvedPath->Parser().Inc(); + } + } + } + +/** + Forms the resolved uri. Sets the components for the resolved uri from those in the base uri and + the reference uri. The resolved path is given by the input argument aResolvedPath + + @since 6.0 + @param aComponent The array of components to be set for the resolved uri. + @param aBaseUri The base uri. + @param aRefUri The reference uri. + @param aResolvedPath The resolved path. + @param aUseBaseQuery A boolean indicating whether the base query + should be used. +*/ +template +void FormResolvedUri(TPtrCType aComponent[], const TUriCType& aBaseUri, const TUriCType& aRefUri, const HBufCType* aResolvedPath, TBool aUseBaseQuery) + { + // Scheme... + if( aBaseUri.IsPresent(EUriScheme) ) + { + // Use the base scheme + aComponent[EUriScheme].Set(aBaseUri.Extract(EUriScheme)); + } + // Authority + if( aRefUri.IsPresent(EUriHost) ) + { + // Use the ref host, userinfo and port - must set host first + aComponent[EUriHost].Set(aRefUri.Extract(EUriHost)); + aComponent[EUriUserinfo].Set(aRefUri.Extract(EUriUserinfo)); + aComponent[EUriPort].Set(aRefUri.Extract(EUriPort)); + } + else if( aBaseUri.IsPresent(EUriHost) ) + { + // Use the base host, userinfo and port - must set host first + aComponent[EUriHost].Set(aBaseUri.Extract(EUriHost)); + aComponent[EUriUserinfo].Set(aBaseUri.Extract(EUriUserinfo)); + aComponent[EUriPort].Set(aBaseUri.Extract(EUriPort)); + } + // Path... + aComponent[EUriPath].Set(*aResolvedPath); + + // Query... + if( aUseBaseQuery && aBaseUri.IsPresent(EUriQuery) ) + { + // Use the query from the base + aComponent[EUriQuery].Set(aBaseUri.Extract(EUriQuery)); + } + else if( aRefUri.IsPresent(EUriQuery) ) + { + // Use the query from the ref + aComponent[EUriQuery].Set(aRefUri.Extract(EUriQuery)); + } + // Fragment + if( aRefUri.IsPresent(EUriFragment) ) + { + // Use the fragment from the ref + aComponent[EUriFragment].Set(aRefUri.Extract(EUriFragment)); + } + } + +// +// +// Implemetation of LOCAL functions +// +// + +/** + Function used to resolve a base path (aBasePath) against a reference path (aRefPath), + as described by RFC2396. + + @since 6.0 + @param aBasePath A descriptor reference to the base path. + @param aRefPath A descriptor reference to the reference path. + @return A pointer to a buffer containing the resolve path. + @leave KUriErrBadBasePath if the base path is not an absolute path and not empty. +*/ +HBufC8* ResolvePathsL(const TDesC8& aBasePath, const TDesC8& aRefPath) + { + TInt refLength = aRefPath.Length(); + if( refLength && aRefPath[0] == KSlashDelimiter ) + { + // Relative path is absolute - that is the resolved path + return aRefPath.AllocL(); + } + // Ok got work to do - base path must be absolute (check 1st char) or empty + if( aBasePath.Length() && aBasePath[0] != KSlashDelimiter ) + { + // Base path not empty and not abosolute - bad base path + User::Leave(KUriUtilsErrBadBasePath); + } + // Create a modifiable path object for resolved path + CDelimitedPath8* resolvedPath = CDelimitedPath8::NewLC(aBasePath); + + // Check for empty ref path - use all of base path if empty + if( refLength ) + { + // Not empty - ensure that base path's last segment is removed and add reference + resolvedPath->PopBackL(); + resolvedPath->PushBackL(aRefPath); + } + // Clean up the path to resolve occurences of '..' and '.' - parser path first + CleanResolvedPathL(resolvedPath); + + // Return pointer to HBufC with path + HBufC8* path = resolvedPath->Parser().Des().AllocL(); + CleanupStack::PopAndDestroy(resolvedPath); + return path; + } + +/** + Function used to resolve a base path (aBasePath) against a reference path (aRefPath), + as described by RFC2396. + + @since 6.0 + @param aBasePath A descriptor reference to the base path. + @param aRefPath A descriptor reference to the reference path. + @return A pointer to a buffer containing the resolve path. + @leave KUriErrBadBasePath if the base path is not an absolute path and not empty. +*/ +HBufC16* ResolvePathsL(const TDesC16& aBasePath, const TDesC16& aRefPath) + { + TInt refLength = aRefPath.Length(); + if( refLength && aRefPath[0] == KSlashDelimiter ) + { + // Relative path is absolute - that is the resolved path + return aRefPath.AllocL(); + } + // Ok got work to do - base path must be absolute (check 1st char) or empty + if( aBasePath.Length() && aBasePath[0] != KSlashDelimiter ) + { + // Base path not empty and not abosolute - bad base path + User::Leave(KUriUtilsErrBadBasePath); + } + // Create a modifiable path object for resolved path + CDelimitedPath16* resolvedPath = CDelimitedPath16::NewLC(aBasePath); + + // Check for empty ref path - use all of base path if empty + if( refLength ) + { + // Not empty - ensure that base path's last segment is removed and add reference + resolvedPath->PopBackL(); + resolvedPath->PushBackL(aRefPath); + } + // Clean up the path to resolve occurences of '..' and '.' - parser path first + CleanResolvedPathL(resolvedPath); + + // Return pointer to HBufC with path + HBufC16* path = resolvedPath->Parser().Des().AllocL(); + CleanupStack::PopAndDestroy(resolvedPath); + return path; + } + +/** + Checks if the segment is '.' (8-bit version). + + @since 6.0 + @param aSegment A descriptor with the segment to check. + @return A boolean value of ETrue if the segment is '.', EFalse if not. +*/ +TBool IsSameDir(const TDesC8& aSegment) + { + _LIT8(KSameDir, "."); + return (aSegment.Compare(KSameDir) == 0); + } + +/** + Checks if the segment is '.' (16-bit version). + + @since 6.0 + @param aSegment A descriptor with the segment to check. + @return A boolean value of ETrue if the segment is '.', EFalse if not. +*/ +TBool IsSameDir(const TDesC16& aSegment) + { + _LIT16(KSameDir, "."); + return (aSegment.Compare(KSameDir) == 0); + } + +/** + Checks if the segment is '..' (8-bit version). + + @since 6.0 + @param aSegment A descriptor with the segment to check. + @return A boolean value of ETrue if the segment is '..', EFalse if not. +*/ +TBool IsParentDir(const TDesC8& aSegment) + { + _LIT8(KParentDir, ".."); + return (aSegment.Compare(KParentDir) == 0); + } + +/** + Checks if the segment is '..' (16-bit version). + + @since 6.0 + @param aSegment A descriptor with the segment to check. + @return A boolean value of ETrue if the segment is '..', EFalse if not. +*/ +TBool IsParentDir(const TDesC16& aSegment) + { + _LIT16(KParentDir, ".."); + return (aSegment.Compare(KParentDir) == 0); + } + +/** + Inserts the segment '..' at the current parsed position (8-bit version). + + @since 6.0 + @param aResolvedPath The delimited data object to have the segment + inserted. + @pre The delimited data object must be parsed to the position where + the segment is to be inserted. + @post The segment '..' has been inserted at the current position. +*/ +void InsertParentDirL(CDelimitedDataBase8* aResolvedPath) + { + _LIT8(KParentDir, ".."); + aResolvedPath->InsertCurrentL(KParentDir); + } + +/** + Inserts the segment '..' at the current parsed position (16-bit version). + + @since 6.0 + @param aResolvedPath The delimited data object to have the segment + inserted. + @pre The delimited data object must be parsed to the position where + the segment is to be inserted. + @post The segment '..' has been inserted at the current position. +*/ +void InsertParentDirL(CDelimitedDataBase16* aResolvedPath) + { + _LIT16(KParentDir, ".."); + aResolvedPath->InsertCurrentL(KParentDir); + } + + +// +// +// File URI Implementation - CUri8 +// +// + +/** + Initializes the file URI components (scheme, empty hostname and path). + + It uses GenerateFileUriPathL() to generate a file Uri path using the filename and drive. + + @since 9.1 + @param aFileName A reference to a filename + @param aDrive A drive number. This is a TFileUriFlags value. + @param aFlags A flag to indicate if the file exists on a fixed drive or removable media drive. + This is a TFileUriFlags value. + @pre Object fully constructed + @post The object is initialized with file URI components. + */ +void CUri8::InitializeFileUriComponentsL(const TDesC& aFileName, TDriveNumber aDrive, TUint aFlags) + { + HBufC* uriPath16 = GenerateFileUriPathL(aFileName, aDrive, aFlags); + CleanupStack::PushL(uriPath16); + HBufC8* uriPath = EscapeUtils::ConvertFromUnicodeToUtf8L(*uriPath16); + CleanupStack::PopAndDestroy(uriPath16); + CleanupStack::PushL(uriPath); + HBufC8* escpedUriPath = EscapeUtils::EscapeEncodeL(*uriPath, EscapeUtils::EEscapeNormal); + CleanupStack::PopAndDestroy(uriPath); + CleanupStack::PushL(escpedUriPath); + + //SetComponent is not used in order to increase efficiency, by avoiding overhead of length calculation, + //tmp buffer allocation and updation of internal uri object, internal buffer & descriptor pointer + //for each SetComponent call + iUri.iComponent[EUriPath].Set(*escpedUriPath); + iUri.iComponent[EUriHost].Set(KNullDesC8); + iUri.iComponent[EUriScheme].Set(KFileUriScheme8); + FormUriL(); + + CleanupStack::PopAndDestroy(escpedUriPath); + } + +/** + Allocates and constructs a file URI object for a specified file. + + - If the file exists on a fixed drive, then the file URI takes the form: 'file://\/\'. + - If the file exists on a removable media drive, then the file URI takes the form: 'file://ext-media/\'. + + @since 9.1 + @param aFullFileName A reference to a fully qualified filename + @param aFlags A flag to indicate if the file exists on a fixed drive or removable media drive. + This is a TFileUriFlags value. + @return A pointer to the newly created file URI (CUri8) object. + @post A fully constructed and initialized file URI (CUri8) object. + */ +EXPORT_C CUri8* CUri8::CreateFileUriL(const TDesC& aFullFileName, TUint aFlags) + { + //It should be called to construct a file URI for a public file stored on a fix drive + // or on a removable media drive only + __ASSERT_ALWAYS( ((aFlags == 0) || (aFlags & EExtMedia)), User::Panic(KFileUriPanicCategory, KUriUtilsCannotConvert) ); + + CUri8* self = CUri8::NewLC(); + self->InitializeFileUriComponentsL(aFullFileName, EDriveA, aFlags); + CleanupStack::Pop(self); + return self; + } + +/** + Allocates and constructs a file URI object for a file that is private to the application. + + - If the file exists on a fixed drive, then the file URI takes the form 'file://private/\/'. + - If the file exists on a removable media drive, then the file URI takes the form 'file://private/ext-media/\'. + + @since 9.1 + @param aRelativeFileName A reference to the filename relative to the application's private directory. + @param aDrive Drive number, if the private file stored on fixed drive, otherwise not used + This is a TDriveNumber value + @param aFlags A flag to indicate if the file exists on a fixed drive or removable media drive. + This is a TFileUriFlags value. + @return A pointer to the newly created file URI (CUri8) object. + @post A fully constructed and initialized file URI (CUri8) object. +*/ + + +EXPORT_C CUri8* CUri8::CreatePrivateFileUriL(const TDesC& aRelativeFileName, TDriveNumber aDrive, TInt aFlags) + { + //It should be called to construct a file URI for the application's private file stored on a fix drive + // or on a removable media drive only + __ASSERT_ALWAYS( (((aFlags == 0) || (aFlags & EExtMedia)) && (aDrive >= EDriveA && aDrive <= EDriveZ)), User::Panic(KFileUriPanicCategory, KUriUtilsCannotConvert) ); + + CUri8* self = CUri8::NewLC(); + self->InitializeFileUriComponentsL(aRelativeFileName, aDrive, aFlags|EPrivate); + CleanupStack::Pop(self); + return self; + } + + +// +// +// File URI Implementation - CUri16 +// +// + +/** + Initializes the file URI components (scheme, empty hostname and path). + + It uses GenerateFileUriPathL() to generate a file Uri path using the filename and drive. + + @since 9.1 + @param aFileName A reference to a filename + @param aDrive A drive number. This is a TFileUriFlags value. + @param aFlags A flag to indicate if the file exists on a fixed drive or removable media drive. + This is a TFileUriFlags value. + @pre Object fully constructed + @post The object is initialized with file URI components. + */ +void CUri16::InitializeFileUriComponentsL(const TDesC& aFileName, TDriveNumber aDrive, TUint aFlags) + { + HBufC* uriPath = GenerateFileUriPathL(aFileName, aDrive, aFlags); + CleanupStack::PushL(uriPath); + + HBufC8* uriPath8 = EscapeUtils::ConvertFromUnicodeToUtf8L(*uriPath); + CleanupStack::PopAndDestroy(uriPath); + CleanupStack::PushL(uriPath8); + + HBufC8* escapedUriPath8 = EscapeUtils::EscapeEncodeL(*uriPath8, EscapeUtils::EEscapeNormal); + CleanupStack::PopAndDestroy(uriPath8); + CleanupStack::PushL(escapedUriPath8); + + HBufC* escapedUriPath = HBufC::NewLC(escapedUriPath8->Length()); + escapedUriPath->Des().Copy(*escapedUriPath8); + + //SetComponent is not used in order to increase efficiency, by avoiding overhead of length calculation, + //tmp buffer allocation and updation of internal uri object, internal buffer & descriptor pointer + //for each SetComponent call + iUri.iComponent[EUriPath].Set(*escapedUriPath); + iUri.iComponent[EUriHost].Set(KNullDesC16); + iUri.iComponent[EUriScheme].Set(KFileUriScheme16); + FormUriL(); + + CleanupStack::PopAndDestroy(escapedUriPath); + CleanupStack::PopAndDestroy(escapedUriPath8); + } + +/** + Allocates and constructs a file URI object for a specified file. + + - If the file exists on a fixed drive, then the file URI takes the form: 'file://\/\'. + - If the file exists on a removable media drive, then the file URI takes the form: 'file://ext-media/\'. + + @since 9.1 + @param aFullFileName A reference to a fully qualified filename + @param aFlags A flag to indicate if the file exists on a fixed drive or removable media drive. + This is a TFileUriFlags value. + @return A pointer to the newly created file URI (CUri16) object. + @post A fully constructed and initialized file URI (CUri16) object. + */ +EXPORT_C CUri16* CUri16::CreateFileUriL(const TDesC& aFullFileName, TUint aFlags) + { + //It should be called to construct a file URI for a public file stored on a fix drive + // or on a removable media drive only + __ASSERT_ALWAYS( ((aFlags == 0) || (aFlags & EExtMedia)), User::Panic(KFileUriPanicCategory, KUriUtilsCannotConvert) ); + + CUri16* self = CUri16::NewLC(); + self->InitializeFileUriComponentsL(aFullFileName, EDriveA, aFlags); + CleanupStack::Pop(self); + return self; + } + +/** + Allocates and constructs a file URI object for a file that is private to the application. + + - If the file exists on a fixed drive, then the file URI takes the form 'file://private/\/'. + - If the file exists on a removable media drive, then the file URI takes the form 'file://private/ext-media/\'. + + @since 9.1 + @param aRelativeFileName A reference to the filename relative to the application's private directory. + @param aDrive Drive number, if the private file stored on fixed drive, otherwise not used + This is a TDriveNumber value + @param aFlags A flag to indicate if the file exists on a fixed drive or removable media drive. + This is a TFileUriFlags value. + @return A pointer to the newly created file URI (CUri16) object. + @post A fully constructed and initialized file URI (CUri16) object. +*/ + + +EXPORT_C CUri16* CUri16::CreatePrivateFileUriL(const TDesC& aRelativeFileName, TDriveNumber aDrive, TInt aFlags) + { + //It should be called to construct a file URI for the application's private file stored on a fix drive + // or on a removable media drive only + __ASSERT_ALWAYS( (((aFlags == 0) || (aFlags & EExtMedia)) && (aDrive >= EDriveA && aDrive <= EDriveZ)), User::Panic(KFileUriPanicCategory, KUriUtilsCannotConvert) ); + + CUri16* self = CUri16::NewLC(); + self->InitializeFileUriComponentsL(aRelativeFileName, aDrive, aFlags|EPrivate); + CleanupStack::Pop(self); + return self; + } + + +// +// +// Implemetation of LOCAL functions for the File URI +// +// + +/** + Function used to generate 16bit file uri using 1st parameter aFileName and + 2nd parameter aDrive for the application's private or a public file. + + This is called by API CreateFileUri() and CreatePrivateFileUri()to + generate a filename. + + Note: The space allocated for the returned descriptor will likely be larger + than the length of the descriptor + + @leave KErrBadName A provided Drivename or filename is not valid + @since 9.1 + @param aFileName A descriptor reference to the filename. + @param aDrive A descriptor reference to drive letter. + @param aFlags A flag to indicate the private or a public file exists on removable media or a fixed drive. + @return A pointer to a buffer containing the resolved fully qualified filename. + */ +HBufC* GenerateFileUriPathL(const TDesC& aFileName, TDriveNumber aDrive, TUint aFlags) + { + TInt origLength = aFileName.Length(); + + //Leaves with KErrBadName if filename length is out of range + if (origLength == 0 || origLength > KMaxFileName) + { + User::Leave(KErrBadName); + } + + TPtrC filename(aFileName); + + //extract drive letter and remove drive "x:" from filename + TUint drive = filename[0]; + + // hasDrive means it begins with a drive, e.g. "c:" + const TBool hasDrive = ((drive >= 'A' && drive <= 'Z') || (drive >= 'a' && drive <= 'z')) && (filename[1] == KDriveSeparator); + // hasTopPath means it begins with a \ (possibly after the drive) + const TBool hasTopPath = (hasDrive && (filename[2] == KFilePathSeparator)) || (!hasDrive && (drive == TUint(KFilePathSeparator) )); + + TInt skip = KDefaultPath().Length(); // skip leading ":\" by default + if(aFlags & TUint(EPrivate)) + { + skip = (hasDrive ? (KDefaultPath().Length() - 1) : 0) + (hasTopPath ? 1 : 0) ; + } + else + { + // if not private then it should have valid drive i.e. ":\" + if (!(hasDrive && hasTopPath)) + { + User::Leave(KErrBadName); + } + } + + if(skip) + { + filename.Set(aFileName.Right(origLength - skip)); + } + + TInt uriLen = aFileName.Length() + KExtMedia().Length() + KPrivate().Length() + 1 /* for drive letter */; + + HBufC* fileUri = HBufC::NewLC(uriLen); + TPtr fileUriPtr = fileUri->Des(); + fileUriPtr.Append(KUriPathSeparator); + + if (aFlags & TUint(EPrivate)) + { + fileUriPtr.Append(KPrivate); + drive = TInt16('A' + aDrive); + } + + if (aFlags & EExtMedia) + { + fileUriPtr.Append(KExtMedia); + } + else + { + fileUriPtr.Append(drive); + fileUriPtr.Append(KUriPathSeparator); + } + + fileUriPtr.Append(filename); + + //Convert "\" to "/" + ChangePathSeparator(fileUriPtr, KFilePathSeparator, KUriPathSeparator); + + //Handling "./" and "../" in the file URI path or resolving the URI path + CDelimitedPath16* resolvedPath = CDelimitedPath16::NewLC(fileUriPtr); + // Clean up the path to resolve occurences of '..' and '.' + CleanResolvedPathL(resolvedPath); + fileUriPtr.Copy(resolvedPath->Parser().Des()); // new path will always be shorter than old one + CleanupStack::PopAndDestroy(resolvedPath); + + CleanupStack::Pop(fileUri); + return fileUri; + }