applayerprotocols/wapbase/wutil/URLBASE.cpp
changeset 0 b16258d2340f
--- /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 <Utf.h>
+
+// 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; i<fullPath.Length(); ++i)
+			{
+			if (fullPath[i] == KPathDelimiter)
+				fullPath[i] = '/';
+			}
+		escapedFullPath = EscapeEncodeL(fullPath, EUrlPath);
+		CleanupStack::PopAndDestroy();	// escapedFullPath (old version)
+		CleanupStack::PushL(escapedFullPath);	// new version
+
+		// Replace ':' with '|'
+		fullPath.Set(escapedFullPath->Des());
+		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<aString.Length(); ++i)
+		{
+		//	Check if current character must be escaped, will leave if non 8-bit character
+		TChar currentChar = aString[i];
+		if (currentChar > 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<aString.Length(); ++i)
+		{
+		//	See if at start of an escape triple
+		TChar currentChar = aString[i];
+		if (currentChar == '%')
+			{
+			//	Check that next two characters are valid
+			TChar msNibble = aString[i+1];
+			if (msNibble.IsAlpha())
+				msNibble.UpperCase();
+			TChar lsNibble = aString[i+2];
+			if (lsNibble.IsAlpha())
+				lsNibble.UpperCase();
+			TInt msNibbleValue = HexDigit.Locate(msNibble);
+			TInt lsNibbleValue = HexDigit.Locate(lsNibble);
+			if (msNibbleValue == KErrNotFound || lsNibbleValue == KErrNotFound)
+				{
+				User::Leave(EWapErrCorruptUrl);
+				}
+			else
+				{
+				//	Convert characters into hex value
+				TInt hex = 0x10*msNibbleValue + 0x01*lsNibbleValue;
+				unescapedBuf.Append(hex);
+				}
+			//	Move index to get next character
+			i+=2;
+			}
+		else
+			{
+			unescapedBuf.Append(currentChar);
+			}
+		}
+	// Create new HBufC object
+	HBufC* encodedString = unescapedBuf.AllocL();
+
+	CleanupStack::PopAndDestroy();	//	buf
+	return encodedString;
+	}
+
+EXPORT_C HBufC8* CUrl::ConvertFromUnicodeToUtf8L(const TDesC& aString)
+//
+// Converts input string from unicode to UTF8 format - converted string is returned in 8-bit format
+	{
+	// Return an empty buffer straight-away
+	if( aString.Compare(KNullDesC) == 0 )
+		return KNullDesC8().AllocL();
+
+	// Convert from Unicode to UTF8
+	TPtrC unicode = aString;
+	TBuf8<KUrlConversionBufferSize> 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<KUrlConversionBufferSize> 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 - .<end> -> 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 - ..<end> 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;
+			}
+		}
+	}