applayerprotocols/wapbase/wutil/URLBASE.cpp
changeset 0 b16258d2340f
equal deleted inserted replaced
-1:000000000000 0:b16258d2340f
       
     1 // Copyright (c) 2000-2009 Nokia Corporation and/or its subsidiary(-ies).
       
     2 // All rights reserved.
       
     3 // This component and the accompanying materials are made available
       
     4 // under the terms of "Eclipse Public License v1.0"
       
     5 // which accompanies this distribution, and is available
       
     6 // at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     7 //
       
     8 // Initial Contributors:
       
     9 // Nokia Corporation - initial contribution.
       
    10 //
       
    11 // Contributors:
       
    12 //
       
    13 // Description:
       
    14 // Purpose:		Definition of CUrl class - Url processor based on RFC2396.
       
    15 // CUrl encapsulates a url and provides access to its components. On creation, the contents of 
       
    16 // the url is in unescaped mode - excluded characters (as defined by RFC2396) have not been coded into their 
       
    17 // escape triples. Two NewL() functions are provided. The first takes any descriptor and encapsulates it into
       
    18 // the CUrl object, and leaves if the the url begins with a ':' (EWapErrCorruptUrl) or if the scheme is 
       
    19 // 
       
    20 //
       
    21 
       
    22 //				corrupted (EWapErrCorruptScheme). The second NewL() creates a url with a file scheme (file://) from a 
       
    23 //				TParseBase argument, and will leave if the url is invalid (EWapErrCorruptUrl). Two static functions have 
       
    24 //				been provided that each take a descriptor argument which is then escape encoded/decoded.
       
    25 //
       
    26 //				NOTE - maintainer needs detailed knowledge of url parsing (RFC2396)
       
    27 //
       
    28 
       
    29 // System includes
       
    30 //
       
    31 #include <Utf.h>
       
    32 
       
    33 // CUrl class signature
       
    34 //
       
    35 #include "urlbase.h"
       
    36 
       
    37 // Static data used in this class
       
    38 //
       
    39 const TInt KCUrlInvalidCharPos		= -1;
       
    40 const TInt KUrlConversionBufferSize = 50;
       
    41 
       
    42 //	Constants 
       
    43 _LIT(KHexDigit, "0123456789ABCDEF");
       
    44 _LIT(KUnwiseData, "{}|\\^[]\'");
       
    45 _LIT(KDelimsData, "<>#%\"");
       
    46 _LIT(KReservedData, ";/?:@&=+$,");
       
    47 _LIT(KReservedDataForPath, ";?@&=+$,");
       
    48 
       
    49 //	Delimiters and scheme identifiers for urls
       
    50 _LIT(KUrlPathDelimiter,"/");		
       
    51 _LIT(KUrlFileSchemeSignature,"file://"); 
       
    52 _LIT(KUrlLoc,"//");
       
    53 _LIT(KUrlColon,":");
       
    54 _LIT(KUrlQMark,"?");
       
    55 _LIT(KUrlHash,"#");
       
    56 
       
    57 // Implementation of CUrl
       
    58 //
       
    59 
       
    60 EXPORT_C CUrl* CUrl::NewL(const TDesC& aUrl)
       
    61 //
       
    62 //	Static factory c'tor.
       
    63 	{
       
    64 	CUrl* url = new(ELeave) CUrl();
       
    65 	CleanupStack::PushL(url);
       
    66 	url->ConstructL(aUrl);
       
    67 	CleanupStack::Pop();
       
    68 	return url;
       
    69 	}
       
    70 
       
    71 EXPORT_C CUrl* CUrl::NewL(const TParseBase& aFileName)
       
    72 //
       
    73 //	Static factory c'tor. Used for creating CUrl object for a file on local file system
       
    74 	{
       
    75 	CUrl* url = new(ELeave) CUrl();
       
    76 	CleanupStack::PushL(url);
       
    77 	url->ConstructL(aFileName);
       
    78 	CleanupStack::Pop();
       
    79 	return url;	
       
    80 	}
       
    81 
       
    82 CUrl::CUrl()
       
    83 //
       
    84 //	Default c'tor
       
    85 	{
       
    86 	}
       
    87 
       
    88 EXPORT_C CUrl::~CUrl()
       
    89 //
       
    90 //	D'tor
       
    91 	{
       
    92 	delete iUrlDes;
       
    93 	}
       
    94 
       
    95 void CUrl::ConstructL(const TDesC& aUrl)
       
    96 //
       
    97 //	Non-trivial c'tor - can be used for all general urls
       
    98 	{
       
    99 	// Stripe any leading whitespace
       
   100 	TPtrC url = aUrl;
       
   101 	while( url.Locate(' ') == 0 )
       
   102 		{
       
   103 		// Remove the leading whitespace -> set pointer to second character
       
   104 		url.Set(url.Mid(1));
       
   105 		}
       
   106 	iUrlDes = url.AllocL();
       
   107 
       
   108 	// Check to see if there's ':' at start of aUrl
       
   109 	TInt colonPos = aUrl.Locate(':');
       
   110 	if (colonPos == 0)
       
   111 		User::Leave(EWapErrCorruptUrl);
       
   112 	TPtrC scheme(Component(EUrlScheme));
       
   113 	CheckSchemeValidL(scheme);
       
   114 	}
       
   115 
       
   116 void CUrl::CheckSchemeValidL(const TDesC& aScheme) const
       
   117 	{ // Check the scheme, leave if corrupt
       
   118 	for (TInt i=0; i < aScheme.Length(); i++)
       
   119 		{
       
   120 		TChar c = aScheme[i];
       
   121 		TBool ok =  (c>='a' &&  c<='z') || (c>='A'  && c <='Z');
       
   122 
       
   123 		if (!ok && i > 0)
       
   124 			ok = (c>='0' && c<='9') || c == '+' || c == '-' || c == '.';
       
   125 
       
   126 		if (!ok)
       
   127 			User::Leave(EWapErrCorruptScheme);
       
   128 		}
       
   129 	}
       
   130 
       
   131 void CUrl::ConstructL(const TParseBase& aFileName)
       
   132 //
       
   133 //	Non-trivial c'tor - used to create Urls for local file space only. NO scheme in aFileName
       
   134 	{
       
   135 	if( aFileName.IsWild() ) 
       
   136 		User::Leave(EWapErrCorruptUrl);
       
   137 
       
   138 	// Convert Unicode file name into UTF8 format
       
   139 	HBufC8* utf8 = ConvertFromUnicodeToUtf8L(aFileName.FullName());
       
   140 	CleanupStack::PushL(utf8);
       
   141 
       
   142 	// Convert to 16-bit format
       
   143 	HBufC* escapedFullPath = HBufC::NewLC(utf8->Length());
       
   144 	escapedFullPath->Des().Copy(*utf8);
       
   145 	
       
   146 	// Need to escape encode all the components - start with filename, then path and last the drive
       
   147 	TPtr fullPath = escapedFullPath->Des();
       
   148 	if ( fullPath.Compare(KNullDesC) )
       
   149 		{
       
   150 		// Convert '\' to '/' then escape encode with EUrlPath - this doesn't encode '/' and ':'
       
   151 		for (TInt i=0; i<fullPath.Length(); ++i)
       
   152 			{
       
   153 			if (fullPath[i] == KPathDelimiter)
       
   154 				fullPath[i] = '/';
       
   155 			}
       
   156 		escapedFullPath = EscapeEncodeL(fullPath, EUrlPath);
       
   157 		CleanupStack::PopAndDestroy();	// escapedFullPath (old version)
       
   158 		CleanupStack::PushL(escapedFullPath);	// new version
       
   159 
       
   160 		// Replace ':' with '|'
       
   161 		fullPath.Set(escapedFullPath->Des());
       
   162 		TInt colonPos = fullPath.Locate(KDriveDelimiter);
       
   163 		if( colonPos != KErrNotFound )
       
   164 			fullPath[colonPos] = '|';
       
   165 		}
       
   166 	// Form the url
       
   167 	TInt size = KUrlFileSchemeSignature().Length() + KUrlPathDelimiter().Length() + fullPath.Length();
       
   168 
       
   169 	iUrlDes = HBufC::NewL(size);
       
   170 	iUrlDes->Des().Append(KUrlFileSchemeSignature());
       
   171 	iUrlDes->Des().Append(KUrlPathDelimiter());
       
   172 	iUrlDes->Des().Append(fullPath);
       
   173 
       
   174 	CleanupStack::PopAndDestroy(2);	// escapedFullPath, utf8
       
   175 	}
       
   176 
       
   177 EXPORT_C CUrl* CUrl::ResolveL(CUrl& aBaseUrl, CUrl& aRelativeUrl)
       
   178 //
       
   179 //	Based on the relative parsing algorithm in RFC2396
       
   180 	{
       
   181 	//	Return copy of aRelativeUrl if aBaseUrl is empty
       
   182 	if (aBaseUrl.UrlDes().Compare(KNullDesC)==0)
       
   183 		return aRelativeUrl.AllocL();
       
   184 
       
   185 	TPtrC relativeUrl(aRelativeUrl.UrlDes());
       
   186 
       
   187 	TPtrC relPath(aRelativeUrl.Component(EUrlPath));
       
   188 	TPtrC relAuth(aRelativeUrl.Component(EUrlAuthority));
       
   189 	TPtrC relScheme(aRelativeUrl.Component(EUrlScheme));
       
   190 	TPtrC relQuery(aRelativeUrl.Component(EUrlQuery));
       
   191 	TPtrC relFragment(aRelativeUrl.Component(EUrlFragment));
       
   192 
       
   193 	if (relScheme.Length() > 0)
       
   194 		{
       
   195 		// LOOPHOLE in RFC 1630 if schemes match then remove the scheme from the relative url
       
   196 		if (aBaseUrl.UrlDes().FindF(relScheme) == 0)
       
   197 			relativeUrl.Set(relativeUrl.Mid(relScheme.Length() + 1)); // remove the ':' as well
       
   198 		else // the relative url is absolute
       
   199 			return NewL(relativeUrl);
       
   200 		}
       
   201 
       
   202 	TBool useBaseAuth = ETrue;
       
   203 	TBool useRelQuery = ETrue;
       
   204 	TPtrC basePath(aBaseUrl.Component(EUrlPath));
       
   205 	HBufC* resolvedPath = NULL;
       
   206 	if (relPath.Compare(KNullDesC)==0 && relAuth.Compare(KNullDesC)==0 
       
   207 		&& relScheme.Compare(KNullDesC)==0 && relQuery.Compare(KNullDesC)==0) // relative URL could just be a fragment
       
   208 		{
       
   209 		// Use current document url (assume that it is aBaseUrl), including query, then add relative URL fragment
       
   210 		useRelQuery = EFalse;
       
   211 		resolvedPath = basePath.AllocLC();
       
   212 		}
       
   213 	else if (relativeUrl.Find(KUrlLoc) == 0) // relative url is a network path
       
   214 		{
       
   215 		// Set resolved path to be the relative path
       
   216 		useBaseAuth = EFalse;
       
   217 		resolvedPath = relPath.AllocLC();
       
   218 		}	
       
   219 	else if (relPath.Locate('/') == 0) // relative url is an absolute path
       
   220 		{
       
   221 		resolvedPath = relPath.AllocLC();
       
   222 		}
       
   223 	else 
       
   224 		{
       
   225 		//	Do path resolution, merge the base path and relative path
       
   226 		if (relPath.Length() != 0)
       
   227 			// if the relative path is a query or fragment then shouldn't strip the document from the basePath
       
   228 			{
       
   229 			TInt endBasePath = basePath.LocateReverse('/');
       
   230 			if (endBasePath != KErrNotFound)
       
   231 				basePath.Set(basePath.Left(endBasePath + 1)); // keep the '/' 
       
   232 			else
       
   233 				basePath.Set(_L("/"));	//	Create path of just '/'
       
   234 			}
       
   235 		//	Resolve relative path against base path
       
   236 		resolvedPath = HBufC::NewLC(relPath.Length() + basePath.Length());
       
   237 		TRelativePaths relativePaths(basePath, relPath, resolvedPath->Des());
       
   238 		relativePaths.ResolveRelativePaths();
       
   239 		}
       
   240 
       
   241 	// put the url together
       
   242 	TPtrC baseScheme(aBaseUrl.Component(EUrlScheme));
       
   243 	TPtrC baseAuth(aBaseUrl.Component(EUrlAuthority));
       
   244 	TPtrC baseQuery(aBaseUrl.Component(EUrlQuery));
       
   245 
       
   246 	HBufC* resolvedUrl = HBufC::NewLC(aBaseUrl.UrlDes().Length()  
       
   247 										 + relativeUrl.Length()
       
   248 										 + KUrlColon().Length() 
       
   249 										 + KUrlLoc().Length()
       
   250 										 + KUrlQMark().Length()
       
   251 										 + KUrlHash().Length()
       
   252 										 + 1); // this will be long enough - extra 1 just in case basePath was empty
       
   253 	TPtr resolvedBuf = resolvedUrl->Des();
       
   254 
       
   255 	if (baseScheme.Length() > 0)
       
   256 		{
       
   257 		resolvedBuf.Append(baseScheme);
       
   258 		resolvedBuf.Append(KUrlColon);
       
   259 		}
       
   260 
       
   261 	resolvedBuf.Append(KUrlLoc);	
       
   262 
       
   263 	if (useBaseAuth && baseAuth.Length() >0)
       
   264 		{
       
   265 		resolvedBuf.Append(baseAuth);
       
   266 		}
       
   267 	else if (relAuth.Length() > 0)
       
   268 		{
       
   269 		resolvedBuf.Append(relAuth);
       
   270 		}
       
   271 
       
   272 	resolvedBuf.Append(*resolvedPath);
       
   273 
       
   274 	if (useRelQuery && relQuery.Length() >0)
       
   275 		{
       
   276 		resolvedBuf.Append(KUrlQMark);
       
   277 		resolvedBuf.Append(relQuery);
       
   278 		}
       
   279 	else if (!useRelQuery && baseQuery.Length() >0)
       
   280 		{
       
   281 		resolvedBuf.Append(KUrlQMark);
       
   282 		resolvedBuf.Append(baseQuery);
       
   283 		}
       
   284 
       
   285 	if (relFragment.Length() >0)
       
   286 		{
       
   287 		resolvedBuf.Append(KUrlHash);
       
   288 		resolvedBuf.Append(relFragment);
       
   289 		}
       
   290 
       
   291 	CUrl * url = CUrl::NewL(*resolvedUrl);
       
   292 	CleanupStack::PopAndDestroy(2); // resolvedUrl, resolvedPath
       
   293 
       
   294 	return url;
       
   295 	}
       
   296 
       
   297 void CUrl::Panic(TPanicCode aPanicCode) const
       
   298 	{
       
   299 	_LIT(KWapCUrl,"Wap - CUrl");
       
   300 	User::Panic(KWapCUrl, aPanicCode);
       
   301 	}
       
   302 
       
   303 void CUrl::Part(TComponent aComponent, const TDesC& aUrl, TInt& aStartPos, TInt& aEndPos) const
       
   304 //
       
   305 // Takes a descriptor as a url and parses it for the start and end positions of a particular component. 
       
   306 // KCUrlInvalidCharPos is used to indicate that component is not in url.
       
   307 // 
       
   308 // Based on RFC2396
       
   309 	{
       
   310 	aStartPos = aEndPos = KCUrlInvalidCharPos;
       
   311 
       
   312 	TPtrC url = aUrl;
       
   313 	// search for first of ':' | '/' | '?' | '#'
       
   314 	TInt stripped =0;
       
   315 
       
   316 	TInt colonPos = aUrl.Locate(':');
       
   317 	__ASSERT_ALWAYS(colonPos != 0,Panic(EInvalidUrl));		// if ':' is first character then it is invalid
       
   318 	TInt hashPos = aUrl.Locate('#');
       
   319 	TInt slashPos = aUrl.Locate('/');
       
   320 	TInt queryPos = aUrl.Locate('?');
       
   321 
       
   322 	colonPos = colonPos == KErrNotFound ? KMaxTInt : colonPos;
       
   323 	hashPos = hashPos == KErrNotFound ? KMaxTInt : hashPos;
       
   324 	slashPos = slashPos == KErrNotFound ? KMaxTInt : slashPos;
       
   325 	queryPos = queryPos == KErrNotFound ? KMaxTInt : queryPos;
       
   326 
       
   327 	// if ':' is before others then there is a scheme so extract it
       
   328 	if (colonPos < hashPos && colonPos < slashPos && colonPos < queryPos)
       
   329 		{
       
   330 		if (aComponent == EUrlScheme)
       
   331 			{
       
   332 			aStartPos = stripped;
       
   333 			aEndPos = colonPos -1;
       
   334 			return;
       
   335 			}
       
   336 
       
   337 		if (url.Length() == colonPos + 1) // reached the end of the url
       
   338 			return;	
       
   339 		url.Set(url.Mid(colonPos+1));
       
   340 		stripped += colonPos+1;
       
   341 		hashPos -= colonPos+1;
       
   342 		slashPos -= colonPos+1;
       
   343 		queryPos -= colonPos+1;
       
   344 		}
       
   345 
       
   346 	// if first 2 characters are '//' then a host exists, 
       
   347 	if (url.Find(KUrlLoc) == 0)
       
   348 		{
       
   349 		TInt urlLocLength = KUrlLoc().Length();
       
   350 		if (url.Length() == urlLocLength) // reached the end of the url
       
   351 			return;
       
   352 		// extract '//' and rescan for '/'
       
   353 		url.Set(url.Mid(urlLocLength));
       
   354 		stripped += urlLocLength;
       
   355 		hashPos -= urlLocLength;
       
   356 		queryPos -= urlLocLength;
       
   357 		slashPos = url.Locate('/');
       
   358 		slashPos = slashPos == KErrNotFound ? KMaxTInt : slashPos;
       
   359 
       
   360 		// host is ended by first of '#' | '?' | '/' | end of url
       
   361 		TInt hostEndCharPos = slashPos;
       
   362 		hostEndCharPos = hashPos < hostEndCharPos ? hashPos : hostEndCharPos;
       
   363 		hostEndCharPos = queryPos < hostEndCharPos ? queryPos : hostEndCharPos;
       
   364 		hostEndCharPos = url.Length() < hostEndCharPos ? url.Length() : hostEndCharPos;
       
   365 	
       
   366 		if (aComponent == EUrlAuthority)
       
   367 			{
       
   368 			aStartPos = stripped;
       
   369 			aEndPos = hostEndCharPos+stripped-1;
       
   370 			return;
       
   371 			}
       
   372 		if (aComponent == EUrlLocation ||
       
   373 			aComponent == EUrlUsername ||
       
   374 			aComponent == EUrlPassword)
       
   375 			{
       
   376 			aStartPos = stripped;
       
   377 			aEndPos = hostEndCharPos+stripped-1;
       
   378 			// We need part of the authority. Extract it
       
   379 			PartOfAuthority(aComponent, aUrl, aStartPos, aEndPos);
       
   380 			return;
       
   381 			}
       
   382 		//	Have we reached the end of the url
       
   383 		if (url.Length() == hostEndCharPos)
       
   384 			return; 
       
   385 		// extract host	
       
   386 		url.Set(url.Mid(hostEndCharPos));
       
   387 		stripped += hostEndCharPos;
       
   388 		slashPos -= hostEndCharPos;
       
   389 		hashPos -= hostEndCharPos;
       
   390 		queryPos -= hostEndCharPos;		
       
   391 		}
       
   392 
       
   393 	//	If first character is '/' | this is the start of a relative url | there is no authority then path exists
       
   394 	//	as long as it isn't just a query or fragment
       
   395 	if ((slashPos == 0 || stripped == 0 || stripped == colonPos +1 ) && hashPos != 0 && queryPos !=0)
       
   396 		{ 
       
   397 		TInt pathEndCharPos = queryPos;
       
   398 		pathEndCharPos = hashPos < pathEndCharPos ? hashPos : pathEndCharPos;
       
   399 		pathEndCharPos = url.Length() < pathEndCharPos ? url.Length() : pathEndCharPos;
       
   400 		if (aComponent == EUrlPath)
       
   401 			{
       
   402 			aStartPos = stripped;
       
   403 			aEndPos = pathEndCharPos+stripped-1;
       
   404 			return;
       
   405 			}
       
   406 		if (url.Length() == pathEndCharPos) // reached the end of the url
       
   407 			return;
       
   408 		//	extractPath
       
   409 		url.Set(url.Mid(pathEndCharPos));
       
   410 		stripped += pathEndCharPos ;
       
   411 		queryPos -= pathEndCharPos ;
       
   412 		hashPos -= pathEndCharPos ;
       
   413 		}
       
   414 
       
   415 	// if first is '?' then query exists
       
   416 	if (queryPos == 0) 	
       
   417 		{
       
   418 		// extract ?, query is ended by '#' | end of url
       
   419 		if (url.Length() == 1)	// reached the end of the url
       
   420 			return;
       
   421 		url.Set(url.Mid(1));
       
   422 		stripped += 1;
       
   423 		hashPos -= 1;
       
   424 		// extract query
       
   425 		TInt queryEndCharPos = hashPos;
       
   426 		queryEndCharPos = url.Length() < queryEndCharPos ? url.Length() : queryEndCharPos;
       
   427 		if (aComponent == EUrlQuery)
       
   428 			{
       
   429 			aStartPos = stripped;
       
   430 			aEndPos = queryEndCharPos+stripped-1;
       
   431 			return;
       
   432 			}
       
   433 		if (url.Length() == queryEndCharPos) // reached the end of the url
       
   434 			return;
       
   435 		url.Set(url.Mid(queryEndCharPos));
       
   436 		stripped += queryEndCharPos;
       
   437 		hashPos -= queryEndCharPos;
       
   438 		}
       
   439 
       
   440 	if (hashPos == 0)
       
   441 		{
       
   442 		if (url.Length() == 1)	// reached the end of the url
       
   443 			return;
       
   444 		// extract hash
       
   445 		url.Set(url.Mid(1));
       
   446 		stripped += 1;
       
   447 		// fragment left
       
   448 		if (aComponent == EUrlFragment)
       
   449 			{
       
   450 			aStartPos = stripped;
       
   451 			aEndPos = stripped + url.Length() -1;
       
   452 			return;
       
   453 			}
       
   454 		}
       
   455 	__ASSERT_DEBUG(	aStartPos == aEndPos && aEndPos == KCUrlInvalidCharPos, Panic(EInvalidUrl));
       
   456 	}
       
   457 
       
   458 void CUrl::PartOfAuthority(TComponent aComponent, const TDesC& aUrl, TInt& aStartPos, TInt& aEndPos) const
       
   459 	{
       
   460 	if (aEndPos < aStartPos)
       
   461 		{
       
   462 		// We don't have what the user asked for
       
   463 		aStartPos = KCUrlInvalidCharPos;
       
   464 		aEndPos = KCUrlInvalidCharPos;
       
   465 		return;
       
   466 		}
       
   467 
       
   468 	TPtrC authority = aUrl.Mid(aStartPos, aEndPos - aStartPos);
       
   469 	TInt endPos = aEndPos - aStartPos;
       
   470 	TInt colonPos = authority.Locate(':');
       
   471 	TInt atPos = authority.Locate('@');
       
   472 	if (atPos == KErrNotFound)
       
   473 		{
       
   474 		// There isn't a username or password.
       
   475 		if (aComponent != EUrlLocation)
       
   476 			{
       
   477 			// We don't have what the user asked for
       
   478 			aStartPos = KCUrlInvalidCharPos;
       
   479 			aEndPos = KCUrlInvalidCharPos;
       
   480 			}
       
   481 		// Else all we've got is the location, so we can return
       
   482 		// without doing anything
       
   483 		return;
       
   484 		}
       
   485 
       
   486 	// We have an @.
       
   487 	if (aComponent == EUrlLocation)
       
   488 		{
       
   489 		if (atPos != endPos - 1)
       
   490 			{
       
   491 			// We have a location of non-zero length
       
   492 			aStartPos += atPos + 1;
       
   493 			return;
       
   494 			}
       
   495 		else
       
   496 			{
       
   497 			aStartPos = KCUrlInvalidCharPos;
       
   498 			aEndPos = KCUrlInvalidCharPos;			
       
   499 			}
       
   500 		}
       
   501 	else
       
   502 		{
       
   503 		// Either username or password
       
   504 		if (aComponent == EUrlUsername)
       
   505 			{
       
   506 			if (colonPos == KErrNotFound || colonPos > atPos)
       
   507 				{
       
   508 				// No password
       
   509 				aEndPos = aStartPos + atPos - 1;
       
   510 				return;
       
   511 				}
       
   512 			else
       
   513 				{
       
   514 				aEndPos = aStartPos + colonPos - 1;
       
   515 				return;
       
   516 				}
       
   517 			}
       
   518 		else
       
   519 			{
       
   520 			// They want the password
       
   521 			if (colonPos == KErrNotFound || colonPos > atPos)
       
   522 				{
       
   523 				// There isn't a password
       
   524 				aStartPos = KCUrlInvalidCharPos;
       
   525 				aEndPos = KCUrlInvalidCharPos;
       
   526 				}
       
   527 			else
       
   528 				{
       
   529 				aEndPos = aStartPos + atPos - 1;
       
   530 				aStartPos += colonPos + 1;
       
   531 				}
       
   532 			}
       
   533 		}
       
   534 	}
       
   535 
       
   536 EXPORT_C const TPtrC CUrl::Component(TComponent aType) const
       
   537 	{
       
   538 	TInt start = KCUrlInvalidCharPos;
       
   539 	TInt end = KCUrlInvalidCharPos;
       
   540 
       
   541 	TComponent type(aType);
       
   542 	if (aType == EUrlFileName)
       
   543 		type = EUrlPath;
       
   544 
       
   545 
       
   546 	Part(type, *iUrlDes, start,end);
       
   547 	if  (start == KCUrlInvalidCharPos || end == KCUrlInvalidCharPos)
       
   548 			return KNullDesC();
       
   549 
       
   550 	TInt length = end - start +1; 
       
   551 	if (end == (*iUrlDes).Length())
       
   552 		length--;
       
   553 
       
   554 	TPtrC component((*iUrlDes).Mid(start, length));
       
   555 
       
   556 	if (aType == EUrlFileName)
       
   557 		{
       
   558 		TInt slashPos = component.LocateReverse('/');
       
   559 		if (slashPos == KErrNotFound)
       
   560 			return KNullDesC();
       
   561 		else
       
   562 			component.Set(component.Right(component.Length() - (slashPos +1)));
       
   563 		}
       
   564 
       
   565 	return component;
       
   566 	}
       
   567 
       
   568 
       
   569 EXPORT_C void CUrl::SetComponentL(TComponent aComponent, const TDesC& aValue)
       
   570 	{
       
   571 	TInt start, end;
       
   572 	// As a crude first step, we always resize the URL descriptor to
       
   573 	// be longer than it currently is by 4 characters longer than the
       
   574 	// value. Then at the end, we'll resize it to be the right length.
       
   575 	// 4 allows :// to be inserted if a scheme is set when none is present
       
   576 	iUrlDes=iUrlDes->ReAllocL(iUrlDes->Length() + aValue.Length() + 4);
       
   577 
       
   578 	Part(aComponent, *iUrlDes, start, end);
       
   579 		
       
   580 	if (aComponent == EUrlScheme && start == KCUrlInvalidCharPos && end == KCUrlInvalidCharPos)
       
   581 		{ // We're trying to add a scheme, but there isn't one already. Check if scheme valid
       
   582 		  // There could already be a // at the start, but there can't be a : at the start
       
   583 		CheckSchemeValidL(aValue);
       
   584 		// insert location if required
       
   585 		TPtr wptr(iUrlDes->Des());
       
   586 		// insert // at the start
       
   587 		if(wptr.Find(KUrlLoc)!=0)
       
   588 			wptr.Insert(0,KUrlLoc);
       
   589 		// insert scheme and a colon
       
   590 		wptr.Insert(0,KUrlColon);
       
   591 		wptr.Insert(0,aValue);
       
   592 		}
       
   593 	else if ((aComponent == EUrlUsername || aComponent == EUrlPassword) && 
       
   594 		(start == KCUrlInvalidCharPos || end == KCUrlInvalidCharPos))
       
   595 		{
       
   596 		// We're trying to add a username or password, but there isn't
       
   597 		// one already, so the surrounding punctuation won't be there.
       
   598 		if (aComponent == EUrlUsername)
       
   599 			{
       
   600 			Part(EUrlAuthority, *iUrlDes, start, end);
       
   601 			// If there isn't an authority, this is too complex.
       
   602 			if (start == KCUrlInvalidCharPos)
       
   603 				User::Leave(KErrNotSupported);
       
   604 
       
   605 			// We've now found the correct start position. Check if
       
   606 			// the password exists
       
   607 			TInt scratch;
       
   608 			Part(EUrlPassword, *iUrlDes, end, scratch);
       
   609 			if (end == KCUrlInvalidCharPos)
       
   610 				{
       
   611 				// No username or password. This means we need to add the @
       
   612 				iUrlDes->Des().Insert(start, _L("@"));
       
   613 				end = start;
       
   614 				}
       
   615 			else
       
   616 				{
       
   617 				// There is a password, so end currently points after
       
   618 				// the colon.
       
   619 				end--;
       
   620 				}
       
   621 			}
       
   622 		else
       
   623 			{
       
   624 			// aComponent == EUrlPassword
       
   625 			TInt scratch;
       
   626 			Part(EUrlUsername, *iUrlDes, scratch, start);
       
   627 			if (start == KCUrlInvalidCharPos)
       
   628 				{
       
   629 				// No username. We need to find the start of the
       
   630 				// location, and add :@ after it and insert the
       
   631 				// password between them.
       
   632 
       
   633 				
       
   634 				Part(EUrlLocation, *iUrlDes, start, scratch);
       
   635 				iUrlDes->Des().Insert(start, _L(":@"));
       
   636 				}
       
   637 			else
       
   638 				{
       
   639 				// There is a username but no password. Add a colon at
       
   640 				// the end of it and then add the password afterwards.
       
   641 				iUrlDes->Des().Insert(++start, KUrlColon);
       
   642 				}
       
   643 			start++;
       
   644 			end = start;
       
   645 			}
       
   646 		}
       
   647 	else if (start == KCUrlInvalidCharPos || end == KCUrlInvalidCharPos)
       
   648 		{
       
   649 		// A complex case we can't deal with
       
   650 		User::Leave(KErrNotSupported);
       
   651 		}
       
   652 	else
       
   653 		{
       
   654 		// The easy case; end currently points at the last character;
       
   655 		// we want it to point one after.
       
   656 		end++;
       
   657 		}
       
   658 	if (start != KCUrlInvalidCharPos && end != KCUrlInvalidCharPos)
       
   659 		{ // this check is necessary to catch the case where
       
   660 		  // we set a scheme and there wasn't one already
       
   661 		iUrlDes->Des().Replace(start, end - start, aValue);
       
   662 		}
       
   663 	iUrlDes=iUrlDes->ReAllocL(iUrlDes->Length());
       
   664 	}
       
   665 
       
   666 EXPORT_C TBool CUrl::operator==(CUrl& aUrl) const
       
   667 	{
       
   668 	return !Compare(aUrl,EUrlGenericCompare);
       
   669 	}
       
   670 
       
   671 EXPORT_C TInt CUrl::Compare(CUrl& aUrl, TInt aCompareComps) const
       
   672 //
       
   673 //	Scheme is case insensitive, rest of url is case sensitive
       
   674 	{
       
   675 	TInt result =0;
       
   676 	if (aCompareComps & EUrlScheme)
       
   677 		{
       
   678 		result += Component(EUrlScheme).CompareF(aUrl.Component(EUrlScheme));
       
   679 		if (result !=0)
       
   680 			return result;
       
   681 		}
       
   682 		
       
   683 	if (aCompareComps & EUrlLocation)
       
   684 		{
       
   685 		result += Component(EUrlLocation).Compare(aUrl.Component(EUrlLocation));
       
   686 		if (result !=0)
       
   687 			return result;
       
   688 		}
       
   689 
       
   690 	if (aCompareComps & EUrlUsername)
       
   691 		{
       
   692 		result += Component(EUrlUsername).Compare(aUrl.Component(EUrlUsername));
       
   693 		if (result !=0)
       
   694 			return result;
       
   695 		}
       
   696 
       
   697 	if (aCompareComps & EUrlPassword)
       
   698 		{
       
   699 		result += Component(EUrlPassword).Compare(aUrl.Component(EUrlPassword));
       
   700 		if (result !=0)
       
   701 			return result;
       
   702 		}
       
   703 
       
   704 	if (aCompareComps & EUrlPath)
       
   705 		{
       
   706 		result += Component(EUrlPath).Compare(aUrl.Component(EUrlPath));
       
   707 		if (result !=0)
       
   708 			return result;
       
   709 		}
       
   710 
       
   711 	if (aCompareComps & EUrlQuery)
       
   712 		{
       
   713 		result += Component(EUrlQuery).Compare(aUrl.Component(EUrlQuery));
       
   714 		if (result !=0)
       
   715 			return result;
       
   716 		}
       
   717 
       
   718 	if (aCompareComps & EUrlFragment)
       
   719 		{
       
   720 		result += Component(EUrlFragment).Compare(aUrl.Component(EUrlFragment));
       
   721 		if (result !=0)
       
   722 			return result;
       
   723 		}
       
   724 	return result;
       
   725 	}
       
   726 
       
   727 EXPORT_C void CUrl::SetL(CUrl& aUrl)
       
   728 	{
       
   729 	HBufC* url = aUrl.UrlDes().AllocL();
       
   730 	delete iUrlDes;
       
   731 	iUrlDes = url;	
       
   732 	}
       
   733 
       
   734 EXPORT_C CUrl* CUrl::AllocL() const
       
   735 	{
       
   736 	return CUrl::NewL(*iUrlDes);
       
   737 	}
       
   738 
       
   739 EXPORT_C CUrl* CUrl::AllocL(TComponent aComponent) const
       
   740 	{
       
   741 	if (aComponent == EUrlGenericCompare)
       
   742 		return AllocL();
       
   743 	else if (aComponent == EUrlNoCredentials)
       
   744 		{
       
   745 		TInt usernameStart;
       
   746 		TInt locationStart;
       
   747 		TInt scratch;
       
   748 		Part(EUrlUsername, *iUrlDes, usernameStart, scratch);
       
   749 		Part(EUrlLocation, *iUrlDes, locationStart, scratch);
       
   750 		// If there isn't a location and a username, we can just
       
   751 		// return the current URL.
       
   752 		if (locationStart == KCUrlInvalidCharPos || 
       
   753 			usernameStart == KCUrlInvalidCharPos)
       
   754 			return AllocL();
       
   755 		// Make a CUrl of the right length and then copy the correct
       
   756 		// data into it.
       
   757 		TInt lengthOfReturnedUrl = iUrlDes->Length() - locationStart + 
       
   758 			usernameStart;
       
   759 		CUrl* result = CUrl::NewL(iUrlDes->Left(lengthOfReturnedUrl));
       
   760 		result->iUrlDes->Des().Replace(usernameStart,
       
   761 									   lengthOfReturnedUrl - usernameStart,
       
   762 									   iUrlDes->Right(lengthOfReturnedUrl - 
       
   763 													  usernameStart));
       
   764 		return result;
       
   765 		}
       
   766 	else return CUrl::NewL(Component(aComponent));
       
   767 	}
       
   768 
       
   769 EXPORT_C CUrl* CUrl::UrlEscapedL() const
       
   770 //
       
   771 //	Create and return an escaped version of the current unescaped url
       
   772 	{
       
   773 	User::Panic(_L("Function Not Used"), KErrNotSupported);
       
   774 	return NULL;
       
   775 	}
       
   776 
       
   777 EXPORT_C CUrl* CUrl::UrlUnescapedL() const
       
   778 //
       
   779 //	Create and return an unescaper version of the current escaped url
       
   780 	{
       
   781 	User::Panic(_L("Function Not Used"), KErrNotSupported);
       
   782 	return NULL;
       
   783 	}
       
   784 
       
   785 EXPORT_C HBufC* CUrl::EscapeEncodeL(const TDesC& aString)
       
   786 //
       
   787 // Deprecated function - should use overload function.
       
   788 	{
       
   789 	return EscapeEncodeL(aString, EUrlGenericCompare);
       
   790 	}
       
   791 
       
   792 EXPORT_C HBufC* CUrl::EscapeEncodeL(const TDesC& aString, TInt aEscapeMode)
       
   793 //
       
   794 // Encodes any excluded characters in aString as escape triples - excluded characters set by aEscapeMode
       
   795 	{
       
   796 	// Need to create an HBufC with our excluded characters
       
   797 	HBufC* excludedBuf = NULL;
       
   798 
       
   799 	switch (aEscapeMode)
       
   800 		{
       
   801 	case EUrlGenericCompare:
       
   802 		{
       
   803 		// This is normal operation - escape all sets of data other than KReservedData
       
   804 		excludedBuf = HBufC::NewLC(KUnwiseData().Length() + KDelimsData().Length());
       
   805 		TPtr excluded = excludedBuf->Des();
       
   806 		excluded.Append(KUnwiseData);
       
   807 		excluded.Append(KDelimsData);
       
   808 		} break;
       
   809 	case EUrlScheme:
       
   810 		{
       
   811 		// Escaping data in scheme - reserved chars have no special meaning so escape as well.
       
   812 		excludedBuf = HBufC::NewLC(KUnwiseData().Length() + KDelimsData().Length() + KReservedData().Length());
       
   813 		TPtr excluded = excludedBuf->Des();
       
   814 		excluded.Append(KUnwiseData);
       
   815 		excluded.Append(KDelimsData);
       
   816 		excluded.Append(KReservedData);
       
   817 		} break;
       
   818 	case EUrlPath:
       
   819 		{
       
   820 		// Escaping data in a local file path - same as EUrlScheme but don't escape '/' and ':'
       
   821 		excludedBuf = HBufC::NewLC(KUnwiseData().Length() + KDelimsData().Length() + KReservedDataForPath().Length());
       
   822 		TPtr excluded = excludedBuf->Des();
       
   823 		excluded.Append(KUnwiseData);
       
   824 		excluded.Append(KDelimsData);
       
   825 		excluded.Append(KReservedDataForPath);
       
   826 		} break;
       
   827 	default:
       
   828 		// Not supported return NULL
       
   829 		return NULL;
       
   830 		break;
       
   831 		}
       
   832 
       
   833 	//	Descriptor to hex digits
       
   834 	const TDesC& HexDigit = KHexDigit;
       
   835 
       
   836 	//	Allocate space to build escaped url - consider worse case, where all characters are excluded => length x 3
       
   837 	HBufC* buf = HBufC::NewLC(aString.Length()*3);	//	CS
       
   838 	TPtr escapedBuf = buf->Des();
       
   839 
       
   840 	for (TInt i=0; i<aString.Length(); ++i)
       
   841 		{
       
   842 		//	Check if current character must be escaped, will leave if non 8-bit character
       
   843 		TChar currentChar = aString[i];
       
   844 		if (currentChar > 0xff)
       
   845 			User::Leave(EWapErrCorruptUrl);
       
   846 		//	Check if aChar is a member of DelimsData or UnwiseData or a control character
       
   847 		if (excludedBuf->Locate(currentChar)!=KErrNotFound || (currentChar>=0x00 && currentChar<=0x1F) || currentChar==' ' || currentChar > 0x7E )
       
   848 			{
       
   849 			//	Escaped character will be 8-bit
       
   850 			escapedBuf.Append('%');
       
   851 			TInt msNibble = (currentChar & 0xf0) >> 4;	//	Get msNibble by masking against 11110000 and dividing by 16 (>>4)
       
   852 			escapedBuf.Append(HexDigit[msNibble]);
       
   853 			TInt lsNibble = (currentChar & 0x0f);	//	Get lsNibble by masking against 00001111
       
   854 			escapedBuf.Append(HexDigit[lsNibble]);
       
   855 			}
       
   856 		else
       
   857 			{
       
   858 			escapedBuf.Append(currentChar);
       
   859 			}
       
   860 		}
       
   861 	// Create new HBufC object
       
   862 	HBufC* encodedString = escapedBuf.AllocL();
       
   863 
       
   864 	CleanupStack::PopAndDestroy(2);	//	buf, excludedBuf
       
   865 	return encodedString;
       
   866 	}
       
   867 
       
   868 EXPORT_C HBufC* CUrl::EscapeDecodeL(const TDesC& aString)
       
   869 //
       
   870 // Decodes any escape triples in aString back into excluded characters
       
   871 	{
       
   872 	//	Descriptor to hex digits
       
   873 	const TDesC& HexDigit = KHexDigit;
       
   874 
       
   875 	//	Allocate space for to build unescaped url
       
   876 	HBufC* buf = HBufC::NewLC(aString.Length());	//	CS
       
   877 	TPtr unescapedBuf = buf->Des();
       
   878 
       
   879 	//	Go through url
       
   880 	for (TInt i=0; i<aString.Length(); ++i)
       
   881 		{
       
   882 		//	See if at start of an escape triple
       
   883 		TChar currentChar = aString[i];
       
   884 		if (currentChar == '%')
       
   885 			{
       
   886 			//	Check that next two characters are valid
       
   887 			TChar msNibble = aString[i+1];
       
   888 			if (msNibble.IsAlpha())
       
   889 				msNibble.UpperCase();
       
   890 			TChar lsNibble = aString[i+2];
       
   891 			if (lsNibble.IsAlpha())
       
   892 				lsNibble.UpperCase();
       
   893 			TInt msNibbleValue = HexDigit.Locate(msNibble);
       
   894 			TInt lsNibbleValue = HexDigit.Locate(lsNibble);
       
   895 			if (msNibbleValue == KErrNotFound || lsNibbleValue == KErrNotFound)
       
   896 				{
       
   897 				User::Leave(EWapErrCorruptUrl);
       
   898 				}
       
   899 			else
       
   900 				{
       
   901 				//	Convert characters into hex value
       
   902 				TInt hex = 0x10*msNibbleValue + 0x01*lsNibbleValue;
       
   903 				unescapedBuf.Append(hex);
       
   904 				}
       
   905 			//	Move index to get next character
       
   906 			i+=2;
       
   907 			}
       
   908 		else
       
   909 			{
       
   910 			unescapedBuf.Append(currentChar);
       
   911 			}
       
   912 		}
       
   913 	// Create new HBufC object
       
   914 	HBufC* encodedString = unescapedBuf.AllocL();
       
   915 
       
   916 	CleanupStack::PopAndDestroy();	//	buf
       
   917 	return encodedString;
       
   918 	}
       
   919 
       
   920 EXPORT_C HBufC8* CUrl::ConvertFromUnicodeToUtf8L(const TDesC& aString)
       
   921 //
       
   922 // Converts input string from unicode to UTF8 format - converted string is returned in 8-bit format
       
   923 	{
       
   924 	// Return an empty buffer straight-away
       
   925 	if( aString.Compare(KNullDesC) == 0 )
       
   926 		return KNullDesC8().AllocL();
       
   927 
       
   928 	// Convert from Unicode to UTF8
       
   929 	TPtrC unicode = aString;
       
   930 	TBuf8<KUrlConversionBufferSize> buf;
       
   931 	HBufC8* utf8Buffer = HBufC8::NewLC(unicode.Length());
       
   932 	TPtr8 utf8 = utf8Buffer->Des();
       
   933 
       
   934 	// Loop until all of the filename is converted
       
   935 	FOREVER
       
   936 		{
       
   937 		const TInt returnValue = CnvUtfConverter::ConvertFromUnicodeToUtf8(buf, unicode);
       
   938 		if( returnValue == CnvUtfConverter::EErrorIllFormedInput || returnValue < 0)
       
   939 			User::Leave(EWapErrCorruptUrl);
       
   940 
       
   941 		// Is escapedFullPath too small?
       
   942 		if( utf8.Length() + buf.Length() > utf8.MaxLength() )
       
   943 			{
       
   944 			utf8Buffer = utf8Buffer->ReAllocL(utf8.Length() + buf.Length());
       
   945 			CleanupStack::Pop();	// utf8Buffer (old version)
       
   946 			CleanupStack::PushL(utf8Buffer);	// new version
       
   947 			utf8.Set(utf8Buffer->Des());
       
   948 			}
       
   949 		// Copy converted characters
       
   950 		utf8.Append(buf);
       
   951 
       
   952 		if( returnValue==0 )
       
   953 			break; // All of aUnicodeText has been converted and handled
       
   954 
       
   955 		// Set input descriptor to remaining characters
       
   956 		unicode.Set(unicode.Right(returnValue));
       
   957 		}
       
   958 	CleanupStack::Pop();	// utf8Buffer
       
   959 	return utf8Buffer;		// Ownership transfered to caller
       
   960 	}
       
   961 
       
   962 EXPORT_C HBufC* CUrl::ConvertToUnicodeFromUtf8L(const TDesC8& aString)
       
   963 //
       
   964 // Converts input string from UTF8 to Unicode format - converted string is returned in 16-bit format
       
   965 	{
       
   966 	// Return an empty buffer straight-away
       
   967 	if( aString.Compare(KNullDesC8) == 0 )
       
   968 		return KNullDesC().AllocL();
       
   969 
       
   970 	// Convert from Unicode to UTF8
       
   971 	TPtrC8 utf8 = aString;
       
   972 	TBuf<KUrlConversionBufferSize> buf;
       
   973 	HBufC* unicodeBuffer = HBufC::NewLC(utf8.Length());
       
   974 	TPtr unicode = unicodeBuffer->Des();
       
   975 
       
   976 	// Loop until all of the filename is converted
       
   977 	FOREVER
       
   978 		{
       
   979 		const TInt returnValue = CnvUtfConverter::ConvertToUnicodeFromUtf8(buf, utf8);
       
   980 		if( returnValue == CnvUtfConverter::EErrorIllFormedInput || returnValue < 0)
       
   981 			User::Leave(EWapErrCorruptUrl);
       
   982 
       
   983 		// Is escapedFullPath too small?
       
   984 		if( unicode.Length() + buf.Length() > unicode.MaxLength() )
       
   985 			{
       
   986 			unicodeBuffer = unicodeBuffer->ReAllocL(unicode.Length() + buf.Length());
       
   987 			CleanupStack::Pop();	// unicodeBuffer (old version)
       
   988 			CleanupStack::PushL(unicodeBuffer);	// new version
       
   989 			unicode.Set(unicodeBuffer->Des());
       
   990 			}
       
   991 		// Copy converted characters
       
   992 		unicode.Append(buf);
       
   993 
       
   994 		if( returnValue==0 )
       
   995 			break; // All of utf8 has been converted and handled
       
   996 
       
   997 		// Set input descriptor to remaining characters
       
   998 		utf8.Set(utf8.Right(returnValue));
       
   999 		}
       
  1000 	CleanupStack::Pop();	// unicodeBuffer
       
  1001 	return unicodeBuffer;	// Ownership transfered to caller
       
  1002 	}
       
  1003 
       
  1004 //
       
  1005 //	TRelativePaths
       
  1006 //
       
  1007 CUrl::TRelativePaths::TRelativePaths(TPtrC aBasePath, TPtrC aRelativePath, TPtr aResolvedPath)
       
  1008 	: iBasePath(aBasePath), iRelativePath(aRelativePath), iResolvedPath(aResolvedPath)
       
  1009 //
       
  1010 //	Trivial c'tor
       
  1011 	{
       
  1012 	}
       
  1013 
       
  1014 void CUrl::TRelativePaths::ResolveRelativePaths()
       
  1015 //
       
  1016 //	Resolves iRelativePath against iBasePath, algorithm based on RFC2396 - result in iResolvedPath
       
  1017 	{
       
  1018 	TBool finished = EFalse;
       
  1019 	while (!finished && iRelativePath.Length()>0)
       
  1020 		{
       
  1021 		//	This applies only in cases such as '..\etc'
       
  1022 		if (iRelativePath.Locate('/')==0)
       
  1023 			iRelativePath.Set(iRelativePath.Mid(1));
       
  1024 		TInt relPathLength = iRelativePath.Length();
       
  1025 		if (relPathLength>0 && iRelativePath[0] == '.')
       
  1026 			{
       
  1027 			//	Case 1 - .<end> -> remove '.'
       
  1028 			if (relPathLength == 1)
       
  1029 				{
       
  1030 				iRelativePath.Set(KNullDesC);
       
  1031 				finished = ETrue;
       
  1032 				}
       
  1033 			//	Length of iRelative MUST be > 1, otherwise would not have got here
       
  1034 			//	Case 2 - ./ -> remove './' and continue
       
  1035 			else if (iRelativePath[1] == '/')
       
  1036 				{
       
  1037 				iRelativePath.Set(iRelativePath.Mid(2));
       
  1038 				}
       
  1039 			//	Case 3 & 4 - ..<end> or ../ -> remove a path from iBasePath
       
  1040 			else if (iRelativePath[1] == '.')
       
  1041 				{
       
  1042 				if (relPathLength == 2 || (relPathLength>2 && iRelativePath[2] == '/'))
       
  1043 					{
       
  1044 					//	Try to remove a directory from iBasePath
       
  1045 					TInt basePathLength = iBasePath.Length();
       
  1046 					//	If base path empty or just '/' -> finish
       
  1047 					if (basePathLength==0 || (basePathLength==1 && iBasePath[0] == '/'))
       
  1048 						{
       
  1049 						finished = ETrue;
       
  1050 						}
       
  1051 					else
       
  1052 						{
       
  1053 						TInt endBasePath = iBasePath.LocateReverse('/');
       
  1054 						if (endBasePath == basePathLength - 1)
       
  1055 							{
       
  1056 							//	Have found / on end of path - remove and rescan for '/'
       
  1057 							iBasePath.Set(iBasePath.Left(endBasePath));
       
  1058 							endBasePath = iBasePath.LocateReverse('/');
       
  1059 							}
       
  1060 						if (endBasePath > KErrNotFound)
       
  1061 							{
       
  1062 							//	Remove a directory, keeping the end '/'
       
  1063 							iBasePath.Set(iBasePath.Left(endBasePath+1));
       
  1064 							iRelativePath.Set(iRelativePath.Mid(2)); //	Remove .. at start
       
  1065 							}
       
  1066 						}
       
  1067 					}
       
  1068 				else
       
  1069 					{
       
  1070 					//	iRelative now of form '..etc'
       
  1071 					finished = ETrue;
       
  1072 					}
       
  1073 				}
       
  1074 			else
       
  1075 				{
       
  1076 				//	iRelative now of form '.etc'
       
  1077 				finished = ETrue;
       
  1078 				}
       
  1079 			}
       
  1080 		else
       
  1081 			{
       
  1082 			//	iRelativePath now of form 'etc'
       
  1083 			finished = ETrue;
       
  1084 			}
       
  1085 		}	//	while (!finished && iRelativePath.Length>0)
       
  1086 	//	Form the resolved path 
       
  1087 	iResolvedPath.Append(iBasePath);
       
  1088 	iResolvedPath.Append(iRelativePath);
       
  1089 	CleanResolvedPath();
       
  1090 	}
       
  1091 
       
  1092 void CUrl::TRelativePaths::CleanResolvedPath()
       
  1093 //
       
  1094 //	Deal with any /. and /../ in iResolvedPath - produced if the relative url is of the form g/./h or g/../h
       
  1095 	{
       
  1096 	TInt pos = 0;
       
  1097 	//	Deal with /../ by locating the previous / and removing everything between
       
  1098 	//	Eg /data/.../stuff -> /stuff
       
  1099 	while (pos > KErrNotFound)
       
  1100 		{
       
  1101 		_LIT(KSlashDotDotSlash,"/../");
       
  1102 		pos = iResolvedPath.Find(KSlashDotDotSlash);
       
  1103 		if (pos > KErrNotFound)
       
  1104 			{
       
  1105 			TPtrC leftHandSide(iResolvedPath.Left(pos));
       
  1106 			TInt slashPos = leftHandSide.LocateReverse('/');
       
  1107 			if (slashPos > KErrNotFound)
       
  1108 				iResolvedPath.Delete(slashPos, pos - slashPos +3);
       
  1109 			else 
       
  1110 				pos = KErrNotFound;
       
  1111 			}
       
  1112 		}
       
  1113 	//	Remove any /. in iResolvedPath
       
  1114 	pos = 0;
       
  1115 	while (pos > KErrNotFound)
       
  1116 		{
       
  1117 		_LIT(KSlashDot,"/.");
       
  1118 		pos = iResolvedPath.Find(KSlashDot);
       
  1119 		TInt resolvedLength = iResolvedPath.Length();
       
  1120 		if (pos > KErrNotFound)
       
  1121 			{ 
       
  1122 			if (resolvedLength == pos+2) // just remove the .
       
  1123 				iResolvedPath.Delete(pos+1,1);
       
  1124 			else if  (resolvedLength > pos +1 && iResolvedPath[pos+2] == '/') // is of the form /./
       
  1125 				iResolvedPath.Delete(pos,2);
       
  1126 			else
       
  1127 				pos = KErrNotFound;
       
  1128 			}
       
  1129 		}
       
  1130 	}