diff -r 000000000000 -r b16258d2340f applayerprotocols/wapbase/wutil/URLBASE.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/applayerprotocols/wapbase/wutil/URLBASE.cpp Tue Feb 02 01:09:52 2010 +0200 @@ -0,0 +1,1130 @@ +// Copyright (c) 2000-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: +// Purpose: Definition of CUrl class - Url processor based on RFC2396. +// CUrl encapsulates a url and provides access to its components. On creation, the contents of +// the url is in unescaped mode - excluded characters (as defined by RFC2396) have not been coded into their +// escape triples. Two NewL() functions are provided. The first takes any descriptor and encapsulates it into +// the CUrl object, and leaves if the the url begins with a ':' (EWapErrCorruptUrl) or if the scheme is +// +// + +// corrupted (EWapErrCorruptScheme). The second NewL() creates a url with a file scheme (file://) from a +// TParseBase argument, and will leave if the url is invalid (EWapErrCorruptUrl). Two static functions have +// been provided that each take a descriptor argument which is then escape encoded/decoded. +// +// NOTE - maintainer needs detailed knowledge of url parsing (RFC2396) +// + +// System includes +// +#include + +// CUrl class signature +// +#include "urlbase.h" + +// Static data used in this class +// +const TInt KCUrlInvalidCharPos = -1; +const TInt KUrlConversionBufferSize = 50; + +// Constants +_LIT(KHexDigit, "0123456789ABCDEF"); +_LIT(KUnwiseData, "{}|\\^[]\'"); +_LIT(KDelimsData, "<>#%\""); +_LIT(KReservedData, ";/?:@&=+$,"); +_LIT(KReservedDataForPath, ";?@&=+$,"); + +// Delimiters and scheme identifiers for urls +_LIT(KUrlPathDelimiter,"/"); +_LIT(KUrlFileSchemeSignature,"file://"); +_LIT(KUrlLoc,"//"); +_LIT(KUrlColon,":"); +_LIT(KUrlQMark,"?"); +_LIT(KUrlHash,"#"); + +// Implementation of CUrl +// + +EXPORT_C CUrl* CUrl::NewL(const TDesC& aUrl) +// +// Static factory c'tor. + { + CUrl* url = new(ELeave) CUrl(); + CleanupStack::PushL(url); + url->ConstructL(aUrl); + CleanupStack::Pop(); + return url; + } + +EXPORT_C CUrl* CUrl::NewL(const TParseBase& aFileName) +// +// Static factory c'tor. Used for creating CUrl object for a file on local file system + { + CUrl* url = new(ELeave) CUrl(); + CleanupStack::PushL(url); + url->ConstructL(aFileName); + CleanupStack::Pop(); + return url; + } + +CUrl::CUrl() +// +// Default c'tor + { + } + +EXPORT_C CUrl::~CUrl() +// +// D'tor + { + delete iUrlDes; + } + +void CUrl::ConstructL(const TDesC& aUrl) +// +// Non-trivial c'tor - can be used for all general urls + { + // Stripe any leading whitespace + TPtrC url = aUrl; + while( url.Locate(' ') == 0 ) + { + // Remove the leading whitespace -> set pointer to second character + url.Set(url.Mid(1)); + } + iUrlDes = url.AllocL(); + + // Check to see if there's ':' at start of aUrl + TInt colonPos = aUrl.Locate(':'); + if (colonPos == 0) + User::Leave(EWapErrCorruptUrl); + TPtrC scheme(Component(EUrlScheme)); + CheckSchemeValidL(scheme); + } + +void CUrl::CheckSchemeValidL(const TDesC& aScheme) const + { // Check the scheme, leave if corrupt + for (TInt i=0; i < aScheme.Length(); i++) + { + TChar c = aScheme[i]; + TBool ok = (c>='a' && c<='z') || (c>='A' && c <='Z'); + + if (!ok && i > 0) + ok = (c>='0' && c<='9') || c == '+' || c == '-' || c == '.'; + + if (!ok) + User::Leave(EWapErrCorruptScheme); + } + } + +void CUrl::ConstructL(const TParseBase& aFileName) +// +// Non-trivial c'tor - used to create Urls for local file space only. NO scheme in aFileName + { + if( aFileName.IsWild() ) + User::Leave(EWapErrCorruptUrl); + + // Convert Unicode file name into UTF8 format + HBufC8* utf8 = ConvertFromUnicodeToUtf8L(aFileName.FullName()); + CleanupStack::PushL(utf8); + + // Convert to 16-bit format + HBufC* escapedFullPath = HBufC::NewLC(utf8->Length()); + escapedFullPath->Des().Copy(*utf8); + + // Need to escape encode all the components - start with filename, then path and last the drive + TPtr fullPath = escapedFullPath->Des(); + if ( fullPath.Compare(KNullDesC) ) + { + // Convert '\' to '/' then escape encode with EUrlPath - this doesn't encode '/' and ':' + for (TInt i=0; iDes()); + TInt colonPos = fullPath.Locate(KDriveDelimiter); + if( colonPos != KErrNotFound ) + fullPath[colonPos] = '|'; + } + // Form the url + TInt size = KUrlFileSchemeSignature().Length() + KUrlPathDelimiter().Length() + fullPath.Length(); + + iUrlDes = HBufC::NewL(size); + iUrlDes->Des().Append(KUrlFileSchemeSignature()); + iUrlDes->Des().Append(KUrlPathDelimiter()); + iUrlDes->Des().Append(fullPath); + + CleanupStack::PopAndDestroy(2); // escapedFullPath, utf8 + } + +EXPORT_C CUrl* CUrl::ResolveL(CUrl& aBaseUrl, CUrl& aRelativeUrl) +// +// Based on the relative parsing algorithm in RFC2396 + { + // Return copy of aRelativeUrl if aBaseUrl is empty + if (aBaseUrl.UrlDes().Compare(KNullDesC)==0) + return aRelativeUrl.AllocL(); + + TPtrC relativeUrl(aRelativeUrl.UrlDes()); + + TPtrC relPath(aRelativeUrl.Component(EUrlPath)); + TPtrC relAuth(aRelativeUrl.Component(EUrlAuthority)); + TPtrC relScheme(aRelativeUrl.Component(EUrlScheme)); + TPtrC relQuery(aRelativeUrl.Component(EUrlQuery)); + TPtrC relFragment(aRelativeUrl.Component(EUrlFragment)); + + if (relScheme.Length() > 0) + { + // LOOPHOLE in RFC 1630 if schemes match then remove the scheme from the relative url + if (aBaseUrl.UrlDes().FindF(relScheme) == 0) + relativeUrl.Set(relativeUrl.Mid(relScheme.Length() + 1)); // remove the ':' as well + else // the relative url is absolute + return NewL(relativeUrl); + } + + TBool useBaseAuth = ETrue; + TBool useRelQuery = ETrue; + TPtrC basePath(aBaseUrl.Component(EUrlPath)); + HBufC* resolvedPath = NULL; + if (relPath.Compare(KNullDesC)==0 && relAuth.Compare(KNullDesC)==0 + && relScheme.Compare(KNullDesC)==0 && relQuery.Compare(KNullDesC)==0) // relative URL could just be a fragment + { + // Use current document url (assume that it is aBaseUrl), including query, then add relative URL fragment + useRelQuery = EFalse; + resolvedPath = basePath.AllocLC(); + } + else if (relativeUrl.Find(KUrlLoc) == 0) // relative url is a network path + { + // Set resolved path to be the relative path + useBaseAuth = EFalse; + resolvedPath = relPath.AllocLC(); + } + else if (relPath.Locate('/') == 0) // relative url is an absolute path + { + resolvedPath = relPath.AllocLC(); + } + else + { + // Do path resolution, merge the base path and relative path + if (relPath.Length() != 0) + // if the relative path is a query or fragment then shouldn't strip the document from the basePath + { + TInt endBasePath = basePath.LocateReverse('/'); + if (endBasePath != KErrNotFound) + basePath.Set(basePath.Left(endBasePath + 1)); // keep the '/' + else + basePath.Set(_L("/")); // Create path of just '/' + } + // Resolve relative path against base path + resolvedPath = HBufC::NewLC(relPath.Length() + basePath.Length()); + TRelativePaths relativePaths(basePath, relPath, resolvedPath->Des()); + relativePaths.ResolveRelativePaths(); + } + + // put the url together + TPtrC baseScheme(aBaseUrl.Component(EUrlScheme)); + TPtrC baseAuth(aBaseUrl.Component(EUrlAuthority)); + TPtrC baseQuery(aBaseUrl.Component(EUrlQuery)); + + HBufC* resolvedUrl = HBufC::NewLC(aBaseUrl.UrlDes().Length() + + relativeUrl.Length() + + KUrlColon().Length() + + KUrlLoc().Length() + + KUrlQMark().Length() + + KUrlHash().Length() + + 1); // this will be long enough - extra 1 just in case basePath was empty + TPtr resolvedBuf = resolvedUrl->Des(); + + if (baseScheme.Length() > 0) + { + resolvedBuf.Append(baseScheme); + resolvedBuf.Append(KUrlColon); + } + + resolvedBuf.Append(KUrlLoc); + + if (useBaseAuth && baseAuth.Length() >0) + { + resolvedBuf.Append(baseAuth); + } + else if (relAuth.Length() > 0) + { + resolvedBuf.Append(relAuth); + } + + resolvedBuf.Append(*resolvedPath); + + if (useRelQuery && relQuery.Length() >0) + { + resolvedBuf.Append(KUrlQMark); + resolvedBuf.Append(relQuery); + } + else if (!useRelQuery && baseQuery.Length() >0) + { + resolvedBuf.Append(KUrlQMark); + resolvedBuf.Append(baseQuery); + } + + if (relFragment.Length() >0) + { + resolvedBuf.Append(KUrlHash); + resolvedBuf.Append(relFragment); + } + + CUrl * url = CUrl::NewL(*resolvedUrl); + CleanupStack::PopAndDestroy(2); // resolvedUrl, resolvedPath + + return url; + } + +void CUrl::Panic(TPanicCode aPanicCode) const + { + _LIT(KWapCUrl,"Wap - CUrl"); + User::Panic(KWapCUrl, aPanicCode); + } + +void CUrl::Part(TComponent aComponent, const TDesC& aUrl, TInt& aStartPos, TInt& aEndPos) const +// +// Takes a descriptor as a url and parses it for the start and end positions of a particular component. +// KCUrlInvalidCharPos is used to indicate that component is not in url. +// +// Based on RFC2396 + { + aStartPos = aEndPos = KCUrlInvalidCharPos; + + TPtrC url = aUrl; + // search for first of ':' | '/' | '?' | '#' + TInt stripped =0; + + TInt colonPos = aUrl.Locate(':'); + __ASSERT_ALWAYS(colonPos != 0,Panic(EInvalidUrl)); // if ':' is first character then it is invalid + TInt hashPos = aUrl.Locate('#'); + TInt slashPos = aUrl.Locate('/'); + TInt queryPos = aUrl.Locate('?'); + + colonPos = colonPos == KErrNotFound ? KMaxTInt : colonPos; + hashPos = hashPos == KErrNotFound ? KMaxTInt : hashPos; + slashPos = slashPos == KErrNotFound ? KMaxTInt : slashPos; + queryPos = queryPos == KErrNotFound ? KMaxTInt : queryPos; + + // if ':' is before others then there is a scheme so extract it + if (colonPos < hashPos && colonPos < slashPos && colonPos < queryPos) + { + if (aComponent == EUrlScheme) + { + aStartPos = stripped; + aEndPos = colonPos -1; + return; + } + + if (url.Length() == colonPos + 1) // reached the end of the url + return; + url.Set(url.Mid(colonPos+1)); + stripped += colonPos+1; + hashPos -= colonPos+1; + slashPos -= colonPos+1; + queryPos -= colonPos+1; + } + + // if first 2 characters are '//' then a host exists, + if (url.Find(KUrlLoc) == 0) + { + TInt urlLocLength = KUrlLoc().Length(); + if (url.Length() == urlLocLength) // reached the end of the url + return; + // extract '//' and rescan for '/' + url.Set(url.Mid(urlLocLength)); + stripped += urlLocLength; + hashPos -= urlLocLength; + queryPos -= urlLocLength; + slashPos = url.Locate('/'); + slashPos = slashPos == KErrNotFound ? KMaxTInt : slashPos; + + // host is ended by first of '#' | '?' | '/' | end of url + TInt hostEndCharPos = slashPos; + hostEndCharPos = hashPos < hostEndCharPos ? hashPos : hostEndCharPos; + hostEndCharPos = queryPos < hostEndCharPos ? queryPos : hostEndCharPos; + hostEndCharPos = url.Length() < hostEndCharPos ? url.Length() : hostEndCharPos; + + if (aComponent == EUrlAuthority) + { + aStartPos = stripped; + aEndPos = hostEndCharPos+stripped-1; + return; + } + if (aComponent == EUrlLocation || + aComponent == EUrlUsername || + aComponent == EUrlPassword) + { + aStartPos = stripped; + aEndPos = hostEndCharPos+stripped-1; + // We need part of the authority. Extract it + PartOfAuthority(aComponent, aUrl, aStartPos, aEndPos); + return; + } + // Have we reached the end of the url + if (url.Length() == hostEndCharPos) + return; + // extract host + url.Set(url.Mid(hostEndCharPos)); + stripped += hostEndCharPos; + slashPos -= hostEndCharPos; + hashPos -= hostEndCharPos; + queryPos -= hostEndCharPos; + } + + // If first character is '/' | this is the start of a relative url | there is no authority then path exists + // as long as it isn't just a query or fragment + if ((slashPos == 0 || stripped == 0 || stripped == colonPos +1 ) && hashPos != 0 && queryPos !=0) + { + TInt pathEndCharPos = queryPos; + pathEndCharPos = hashPos < pathEndCharPos ? hashPos : pathEndCharPos; + pathEndCharPos = url.Length() < pathEndCharPos ? url.Length() : pathEndCharPos; + if (aComponent == EUrlPath) + { + aStartPos = stripped; + aEndPos = pathEndCharPos+stripped-1; + return; + } + if (url.Length() == pathEndCharPos) // reached the end of the url + return; + // extractPath + url.Set(url.Mid(pathEndCharPos)); + stripped += pathEndCharPos ; + queryPos -= pathEndCharPos ; + hashPos -= pathEndCharPos ; + } + + // if first is '?' then query exists + if (queryPos == 0) + { + // extract ?, query is ended by '#' | end of url + if (url.Length() == 1) // reached the end of the url + return; + url.Set(url.Mid(1)); + stripped += 1; + hashPos -= 1; + // extract query + TInt queryEndCharPos = hashPos; + queryEndCharPos = url.Length() < queryEndCharPos ? url.Length() : queryEndCharPos; + if (aComponent == EUrlQuery) + { + aStartPos = stripped; + aEndPos = queryEndCharPos+stripped-1; + return; + } + if (url.Length() == queryEndCharPos) // reached the end of the url + return; + url.Set(url.Mid(queryEndCharPos)); + stripped += queryEndCharPos; + hashPos -= queryEndCharPos; + } + + if (hashPos == 0) + { + if (url.Length() == 1) // reached the end of the url + return; + // extract hash + url.Set(url.Mid(1)); + stripped += 1; + // fragment left + if (aComponent == EUrlFragment) + { + aStartPos = stripped; + aEndPos = stripped + url.Length() -1; + return; + } + } + __ASSERT_DEBUG( aStartPos == aEndPos && aEndPos == KCUrlInvalidCharPos, Panic(EInvalidUrl)); + } + +void CUrl::PartOfAuthority(TComponent aComponent, const TDesC& aUrl, TInt& aStartPos, TInt& aEndPos) const + { + if (aEndPos < aStartPos) + { + // We don't have what the user asked for + aStartPos = KCUrlInvalidCharPos; + aEndPos = KCUrlInvalidCharPos; + return; + } + + TPtrC authority = aUrl.Mid(aStartPos, aEndPos - aStartPos); + TInt endPos = aEndPos - aStartPos; + TInt colonPos = authority.Locate(':'); + TInt atPos = authority.Locate('@'); + if (atPos == KErrNotFound) + { + // There isn't a username or password. + if (aComponent != EUrlLocation) + { + // We don't have what the user asked for + aStartPos = KCUrlInvalidCharPos; + aEndPos = KCUrlInvalidCharPos; + } + // Else all we've got is the location, so we can return + // without doing anything + return; + } + + // We have an @. + if (aComponent == EUrlLocation) + { + if (atPos != endPos - 1) + { + // We have a location of non-zero length + aStartPos += atPos + 1; + return; + } + else + { + aStartPos = KCUrlInvalidCharPos; + aEndPos = KCUrlInvalidCharPos; + } + } + else + { + // Either username or password + if (aComponent == EUrlUsername) + { + if (colonPos == KErrNotFound || colonPos > atPos) + { + // No password + aEndPos = aStartPos + atPos - 1; + return; + } + else + { + aEndPos = aStartPos + colonPos - 1; + return; + } + } + else + { + // They want the password + if (colonPos == KErrNotFound || colonPos > atPos) + { + // There isn't a password + aStartPos = KCUrlInvalidCharPos; + aEndPos = KCUrlInvalidCharPos; + } + else + { + aEndPos = aStartPos + atPos - 1; + aStartPos += colonPos + 1; + } + } + } + } + +EXPORT_C const TPtrC CUrl::Component(TComponent aType) const + { + TInt start = KCUrlInvalidCharPos; + TInt end = KCUrlInvalidCharPos; + + TComponent type(aType); + if (aType == EUrlFileName) + type = EUrlPath; + + + Part(type, *iUrlDes, start,end); + if (start == KCUrlInvalidCharPos || end == KCUrlInvalidCharPos) + return KNullDesC(); + + TInt length = end - start +1; + if (end == (*iUrlDes).Length()) + length--; + + TPtrC component((*iUrlDes).Mid(start, length)); + + if (aType == EUrlFileName) + { + TInt slashPos = component.LocateReverse('/'); + if (slashPos == KErrNotFound) + return KNullDesC(); + else + component.Set(component.Right(component.Length() - (slashPos +1))); + } + + return component; + } + + +EXPORT_C void CUrl::SetComponentL(TComponent aComponent, const TDesC& aValue) + { + TInt start, end; + // As a crude first step, we always resize the URL descriptor to + // be longer than it currently is by 4 characters longer than the + // value. Then at the end, we'll resize it to be the right length. + // 4 allows :// to be inserted if a scheme is set when none is present + iUrlDes=iUrlDes->ReAllocL(iUrlDes->Length() + aValue.Length() + 4); + + Part(aComponent, *iUrlDes, start, end); + + if (aComponent == EUrlScheme && start == KCUrlInvalidCharPos && end == KCUrlInvalidCharPos) + { // We're trying to add a scheme, but there isn't one already. Check if scheme valid + // There could already be a // at the start, but there can't be a : at the start + CheckSchemeValidL(aValue); + // insert location if required + TPtr wptr(iUrlDes->Des()); + // insert // at the start + if(wptr.Find(KUrlLoc)!=0) + wptr.Insert(0,KUrlLoc); + // insert scheme and a colon + wptr.Insert(0,KUrlColon); + wptr.Insert(0,aValue); + } + else if ((aComponent == EUrlUsername || aComponent == EUrlPassword) && + (start == KCUrlInvalidCharPos || end == KCUrlInvalidCharPos)) + { + // We're trying to add a username or password, but there isn't + // one already, so the surrounding punctuation won't be there. + if (aComponent == EUrlUsername) + { + Part(EUrlAuthority, *iUrlDes, start, end); + // If there isn't an authority, this is too complex. + if (start == KCUrlInvalidCharPos) + User::Leave(KErrNotSupported); + + // We've now found the correct start position. Check if + // the password exists + TInt scratch; + Part(EUrlPassword, *iUrlDes, end, scratch); + if (end == KCUrlInvalidCharPos) + { + // No username or password. This means we need to add the @ + iUrlDes->Des().Insert(start, _L("@")); + end = start; + } + else + { + // There is a password, so end currently points after + // the colon. + end--; + } + } + else + { + // aComponent == EUrlPassword + TInt scratch; + Part(EUrlUsername, *iUrlDes, scratch, start); + if (start == KCUrlInvalidCharPos) + { + // No username. We need to find the start of the + // location, and add :@ after it and insert the + // password between them. + + + Part(EUrlLocation, *iUrlDes, start, scratch); + iUrlDes->Des().Insert(start, _L(":@")); + } + else + { + // There is a username but no password. Add a colon at + // the end of it and then add the password afterwards. + iUrlDes->Des().Insert(++start, KUrlColon); + } + start++; + end = start; + } + } + else if (start == KCUrlInvalidCharPos || end == KCUrlInvalidCharPos) + { + // A complex case we can't deal with + User::Leave(KErrNotSupported); + } + else + { + // The easy case; end currently points at the last character; + // we want it to point one after. + end++; + } + if (start != KCUrlInvalidCharPos && end != KCUrlInvalidCharPos) + { // this check is necessary to catch the case where + // we set a scheme and there wasn't one already + iUrlDes->Des().Replace(start, end - start, aValue); + } + iUrlDes=iUrlDes->ReAllocL(iUrlDes->Length()); + } + +EXPORT_C TBool CUrl::operator==(CUrl& aUrl) const + { + return !Compare(aUrl,EUrlGenericCompare); + } + +EXPORT_C TInt CUrl::Compare(CUrl& aUrl, TInt aCompareComps) const +// +// Scheme is case insensitive, rest of url is case sensitive + { + TInt result =0; + if (aCompareComps & EUrlScheme) + { + result += Component(EUrlScheme).CompareF(aUrl.Component(EUrlScheme)); + if (result !=0) + return result; + } + + if (aCompareComps & EUrlLocation) + { + result += Component(EUrlLocation).Compare(aUrl.Component(EUrlLocation)); + if (result !=0) + return result; + } + + if (aCompareComps & EUrlUsername) + { + result += Component(EUrlUsername).Compare(aUrl.Component(EUrlUsername)); + if (result !=0) + return result; + } + + if (aCompareComps & EUrlPassword) + { + result += Component(EUrlPassword).Compare(aUrl.Component(EUrlPassword)); + if (result !=0) + return result; + } + + if (aCompareComps & EUrlPath) + { + result += Component(EUrlPath).Compare(aUrl.Component(EUrlPath)); + if (result !=0) + return result; + } + + if (aCompareComps & EUrlQuery) + { + result += Component(EUrlQuery).Compare(aUrl.Component(EUrlQuery)); + if (result !=0) + return result; + } + + if (aCompareComps & EUrlFragment) + { + result += Component(EUrlFragment).Compare(aUrl.Component(EUrlFragment)); + if (result !=0) + return result; + } + return result; + } + +EXPORT_C void CUrl::SetL(CUrl& aUrl) + { + HBufC* url = aUrl.UrlDes().AllocL(); + delete iUrlDes; + iUrlDes = url; + } + +EXPORT_C CUrl* CUrl::AllocL() const + { + return CUrl::NewL(*iUrlDes); + } + +EXPORT_C CUrl* CUrl::AllocL(TComponent aComponent) const + { + if (aComponent == EUrlGenericCompare) + return AllocL(); + else if (aComponent == EUrlNoCredentials) + { + TInt usernameStart; + TInt locationStart; + TInt scratch; + Part(EUrlUsername, *iUrlDes, usernameStart, scratch); + Part(EUrlLocation, *iUrlDes, locationStart, scratch); + // If there isn't a location and a username, we can just + // return the current URL. + if (locationStart == KCUrlInvalidCharPos || + usernameStart == KCUrlInvalidCharPos) + return AllocL(); + // Make a CUrl of the right length and then copy the correct + // data into it. + TInt lengthOfReturnedUrl = iUrlDes->Length() - locationStart + + usernameStart; + CUrl* result = CUrl::NewL(iUrlDes->Left(lengthOfReturnedUrl)); + result->iUrlDes->Des().Replace(usernameStart, + lengthOfReturnedUrl - usernameStart, + iUrlDes->Right(lengthOfReturnedUrl - + usernameStart)); + return result; + } + else return CUrl::NewL(Component(aComponent)); + } + +EXPORT_C CUrl* CUrl::UrlEscapedL() const +// +// Create and return an escaped version of the current unescaped url + { + User::Panic(_L("Function Not Used"), KErrNotSupported); + return NULL; + } + +EXPORT_C CUrl* CUrl::UrlUnescapedL() const +// +// Create and return an unescaper version of the current escaped url + { + User::Panic(_L("Function Not Used"), KErrNotSupported); + return NULL; + } + +EXPORT_C HBufC* CUrl::EscapeEncodeL(const TDesC& aString) +// +// Deprecated function - should use overload function. + { + return EscapeEncodeL(aString, EUrlGenericCompare); + } + +EXPORT_C HBufC* CUrl::EscapeEncodeL(const TDesC& aString, TInt aEscapeMode) +// +// Encodes any excluded characters in aString as escape triples - excluded characters set by aEscapeMode + { + // Need to create an HBufC with our excluded characters + HBufC* excludedBuf = NULL; + + switch (aEscapeMode) + { + case EUrlGenericCompare: + { + // This is normal operation - escape all sets of data other than KReservedData + excludedBuf = HBufC::NewLC(KUnwiseData().Length() + KDelimsData().Length()); + TPtr excluded = excludedBuf->Des(); + excluded.Append(KUnwiseData); + excluded.Append(KDelimsData); + } break; + case EUrlScheme: + { + // Escaping data in scheme - reserved chars have no special meaning so escape as well. + excludedBuf = HBufC::NewLC(KUnwiseData().Length() + KDelimsData().Length() + KReservedData().Length()); + TPtr excluded = excludedBuf->Des(); + excluded.Append(KUnwiseData); + excluded.Append(KDelimsData); + excluded.Append(KReservedData); + } break; + case EUrlPath: + { + // Escaping data in a local file path - same as EUrlScheme but don't escape '/' and ':' + excludedBuf = HBufC::NewLC(KUnwiseData().Length() + KDelimsData().Length() + KReservedDataForPath().Length()); + TPtr excluded = excludedBuf->Des(); + excluded.Append(KUnwiseData); + excluded.Append(KDelimsData); + excluded.Append(KReservedDataForPath); + } break; + default: + // Not supported return NULL + return NULL; + break; + } + + // Descriptor to hex digits + const TDesC& HexDigit = KHexDigit; + + // Allocate space to build escaped url - consider worse case, where all characters are excluded => length x 3 + HBufC* buf = HBufC::NewLC(aString.Length()*3); // CS + TPtr escapedBuf = buf->Des(); + + for (TInt i=0; i 0xff) + User::Leave(EWapErrCorruptUrl); + // Check if aChar is a member of DelimsData or UnwiseData or a control character + if (excludedBuf->Locate(currentChar)!=KErrNotFound || (currentChar>=0x00 && currentChar<=0x1F) || currentChar==' ' || currentChar > 0x7E ) + { + // Escaped character will be 8-bit + escapedBuf.Append('%'); + TInt msNibble = (currentChar & 0xf0) >> 4; // Get msNibble by masking against 11110000 and dividing by 16 (>>4) + escapedBuf.Append(HexDigit[msNibble]); + TInt lsNibble = (currentChar & 0x0f); // Get lsNibble by masking against 00001111 + escapedBuf.Append(HexDigit[lsNibble]); + } + else + { + escapedBuf.Append(currentChar); + } + } + // Create new HBufC object + HBufC* encodedString = escapedBuf.AllocL(); + + CleanupStack::PopAndDestroy(2); // buf, excludedBuf + return encodedString; + } + +EXPORT_C HBufC* CUrl::EscapeDecodeL(const TDesC& aString) +// +// Decodes any escape triples in aString back into excluded characters + { + // Descriptor to hex digits + const TDesC& HexDigit = KHexDigit; + + // Allocate space for to build unescaped url + HBufC* buf = HBufC::NewLC(aString.Length()); // CS + TPtr unescapedBuf = buf->Des(); + + // Go through url + for (TInt i=0; i buf; + HBufC8* utf8Buffer = HBufC8::NewLC(unicode.Length()); + TPtr8 utf8 = utf8Buffer->Des(); + + // Loop until all of the filename is converted + FOREVER + { + const TInt returnValue = CnvUtfConverter::ConvertFromUnicodeToUtf8(buf, unicode); + if( returnValue == CnvUtfConverter::EErrorIllFormedInput || returnValue < 0) + User::Leave(EWapErrCorruptUrl); + + // Is escapedFullPath too small? + if( utf8.Length() + buf.Length() > utf8.MaxLength() ) + { + utf8Buffer = utf8Buffer->ReAllocL(utf8.Length() + buf.Length()); + CleanupStack::Pop(); // utf8Buffer (old version) + CleanupStack::PushL(utf8Buffer); // new version + utf8.Set(utf8Buffer->Des()); + } + // Copy converted characters + utf8.Append(buf); + + if( returnValue==0 ) + break; // All of aUnicodeText has been converted and handled + + // Set input descriptor to remaining characters + unicode.Set(unicode.Right(returnValue)); + } + CleanupStack::Pop(); // utf8Buffer + return utf8Buffer; // Ownership transfered to caller + } + +EXPORT_C HBufC* CUrl::ConvertToUnicodeFromUtf8L(const TDesC8& aString) +// +// Converts input string from UTF8 to Unicode format - converted string is returned in 16-bit format + { + // Return an empty buffer straight-away + if( aString.Compare(KNullDesC8) == 0 ) + return KNullDesC().AllocL(); + + // Convert from Unicode to UTF8 + TPtrC8 utf8 = aString; + TBuf buf; + HBufC* unicodeBuffer = HBufC::NewLC(utf8.Length()); + TPtr unicode = unicodeBuffer->Des(); + + // Loop until all of the filename is converted + FOREVER + { + const TInt returnValue = CnvUtfConverter::ConvertToUnicodeFromUtf8(buf, utf8); + if( returnValue == CnvUtfConverter::EErrorIllFormedInput || returnValue < 0) + User::Leave(EWapErrCorruptUrl); + + // Is escapedFullPath too small? + if( unicode.Length() + buf.Length() > unicode.MaxLength() ) + { + unicodeBuffer = unicodeBuffer->ReAllocL(unicode.Length() + buf.Length()); + CleanupStack::Pop(); // unicodeBuffer (old version) + CleanupStack::PushL(unicodeBuffer); // new version + unicode.Set(unicodeBuffer->Des()); + } + // Copy converted characters + unicode.Append(buf); + + if( returnValue==0 ) + break; // All of utf8 has been converted and handled + + // Set input descriptor to remaining characters + utf8.Set(utf8.Right(returnValue)); + } + CleanupStack::Pop(); // unicodeBuffer + return unicodeBuffer; // Ownership transfered to caller + } + +// +// TRelativePaths +// +CUrl::TRelativePaths::TRelativePaths(TPtrC aBasePath, TPtrC aRelativePath, TPtr aResolvedPath) + : iBasePath(aBasePath), iRelativePath(aRelativePath), iResolvedPath(aResolvedPath) +// +// Trivial c'tor + { + } + +void CUrl::TRelativePaths::ResolveRelativePaths() +// +// Resolves iRelativePath against iBasePath, algorithm based on RFC2396 - result in iResolvedPath + { + TBool finished = EFalse; + while (!finished && iRelativePath.Length()>0) + { + // This applies only in cases such as '..\etc' + if (iRelativePath.Locate('/')==0) + iRelativePath.Set(iRelativePath.Mid(1)); + TInt relPathLength = iRelativePath.Length(); + if (relPathLength>0 && iRelativePath[0] == '.') + { + // Case 1 - . -> remove '.' + if (relPathLength == 1) + { + iRelativePath.Set(KNullDesC); + finished = ETrue; + } + // Length of iRelative MUST be > 1, otherwise would not have got here + // Case 2 - ./ -> remove './' and continue + else if (iRelativePath[1] == '/') + { + iRelativePath.Set(iRelativePath.Mid(2)); + } + // Case 3 & 4 - .. or ../ -> remove a path from iBasePath + else if (iRelativePath[1] == '.') + { + if (relPathLength == 2 || (relPathLength>2 && iRelativePath[2] == '/')) + { + // Try to remove a directory from iBasePath + TInt basePathLength = iBasePath.Length(); + // If base path empty or just '/' -> finish + if (basePathLength==0 || (basePathLength==1 && iBasePath[0] == '/')) + { + finished = ETrue; + } + else + { + TInt endBasePath = iBasePath.LocateReverse('/'); + if (endBasePath == basePathLength - 1) + { + // Have found / on end of path - remove and rescan for '/' + iBasePath.Set(iBasePath.Left(endBasePath)); + endBasePath = iBasePath.LocateReverse('/'); + } + if (endBasePath > KErrNotFound) + { + // Remove a directory, keeping the end '/' + iBasePath.Set(iBasePath.Left(endBasePath+1)); + iRelativePath.Set(iRelativePath.Mid(2)); // Remove .. at start + } + } + } + else + { + // iRelative now of form '..etc' + finished = ETrue; + } + } + else + { + // iRelative now of form '.etc' + finished = ETrue; + } + } + else + { + // iRelativePath now of form 'etc' + finished = ETrue; + } + } // while (!finished && iRelativePath.Length>0) + // Form the resolved path + iResolvedPath.Append(iBasePath); + iResolvedPath.Append(iRelativePath); + CleanResolvedPath(); + } + +void CUrl::TRelativePaths::CleanResolvedPath() +// +// Deal with any /. and /../ in iResolvedPath - produced if the relative url is of the form g/./h or g/../h + { + TInt pos = 0; + // Deal with /../ by locating the previous / and removing everything between + // Eg /data/.../stuff -> /stuff + while (pos > KErrNotFound) + { + _LIT(KSlashDotDotSlash,"/../"); + pos = iResolvedPath.Find(KSlashDotDotSlash); + if (pos > KErrNotFound) + { + TPtrC leftHandSide(iResolvedPath.Left(pos)); + TInt slashPos = leftHandSide.LocateReverse('/'); + if (slashPos > KErrNotFound) + iResolvedPath.Delete(slashPos, pos - slashPos +3); + else + pos = KErrNotFound; + } + } + // Remove any /. in iResolvedPath + pos = 0; + while (pos > KErrNotFound) + { + _LIT(KSlashDot,"/."); + pos = iResolvedPath.Find(KSlashDot); + TInt resolvedLength = iResolvedPath.Length(); + if (pos > KErrNotFound) + { + if (resolvedLength == pos+2) // just remove the . + iResolvedPath.Delete(pos+1,1); + else if (resolvedLength > pos +1 && iResolvedPath[pos+2] == '/') // is of the form /./ + iResolvedPath.Delete(pos,2); + else + pos = KErrNotFound; + } + } + }