genericservices/httputils/UriParser/TEquiv.cpp
changeset 0 e4d67989cc36
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/genericservices/httputils/UriParser/TEquiv.cpp	Tue Feb 02 02:01:42 2010 +0200
@@ -0,0 +1,447 @@
+// Copyright (c) 2004-2009 Nokia Corporation and/or its subsidiary(-ies).
+// All rights reserved.
+// This component and the accompanying materials are made available
+// under the terms of "Eclipse Public License v1.0"
+// which accompanies this distribution, and is available
+// at the URL "http://www.eclipse.org/legal/epl-v10.html".
+//
+// Initial Contributors:
+// Nokia Corporation - initial contribution.
+//
+// Contributors:
+//
+// Description:
+//
+
+#include "TEquiv.h"
+#include <uriutils.h>
+#include <uriutilscommon.h>
+#include "UriUtilsInternal.h"
+#include <delimitedpathsegment8.h>
+#include <delimitedquery8.h>
+#include <escapeutils.h>
+
+_LIT8(KParamUserFull,	";user=");
+_LIT8(KParamTtlFull,	";ttl=");
+_LIT8(KParamMethodFull,	";method=");
+_LIT8(KParamMaddrFull,	";maddr=");
+_LIT8(KParamMaddr,		"maddr");
+
+_LIT8(KHeaderId, "call-id");
+_LIT8(KHeaderIdAbbr, "i");
+_LIT8(KHeaderContact, "contact");
+_LIT8(KHeaderContactAbbr, "m");
+_LIT8(KHeaderEncoding, "content-encoding");
+_LIT8(KHeaderEncodingAbbr, "e");
+_LIT8(KHeaderLength, "content-length");
+_LIT8(KHeaderLengthAbbr, "l");
+_LIT8(KHeaderType, "content-type");
+_LIT8(KHeaderTypeAbbr, "c");
+_LIT8(KHeaderFrom, "from");
+_LIT8(KHeaderFromAbbr, "f");
+_LIT8(KHeaderSubject, "subject");
+_LIT8(KHeaderSubjectAbbr, "s");
+_LIT8(KHeaderTo, "to");
+_LIT8(KHeaderToAbbr, "t");
+
+TEquiv::TEquiv(const TUriC8& aLhs, const TUriC8& aRhs)
+: iLhs(aLhs), iRhs(aRhs)
+	{
+	}
+
+TBool TEquiv::EquivalentL() const
+	{
+	if (!IsMatchSchemeL())
+		return KUriUtilsErrDifferentScheme;
+	
+	if (!IsMatchUserInfoL())
+		return KUriUtilsErrDifferentUserInfo;
+	
+	if (!IsMatchHostL())
+		return KUriUtilsErrDifferentHost;
+	
+	if (!IsMatchPortL())
+		return KUriUtilsErrDifferentPort;
+	
+	if (!IsMatchPathL())
+		return KUriUtilsErrDifferentPath;
+	
+	if (!IsMatchQueryL())
+		return KUriUtilsErrDifferentQuery;
+	
+	if (!IsMatchFragmentL())
+		return KUriUtilsErrDifferentFragment;
+	
+	return KErrNone;
+	}
+
+TBool TEquiv::IsMatchSchemeL() const
+	{
+	HBufC8* lhsDes = DecodedSegmentLC(iLhs, EUriScheme);
+	HBufC8* rhsDes = DecodedSegmentLC(iRhs, EUriScheme);
+	TBool result = IsMatchCaseless(*lhsDes, *rhsDes);
+	CleanupStack::PopAndDestroy(2);
+	return result;
+	}
+
+TBool TEquiv::IsMatchUserInfoL() const
+	{
+	HBufC8* lhsDes = DecodedSegmentLC(iLhs, EUriUserinfo);
+	HBufC8* rhsDes = DecodedSegmentLC(iRhs, EUriUserinfo);
+	TBool result = IsMatchCaseSensitive(*lhsDes, *rhsDes);
+	CleanupStack::PopAndDestroy(2);
+	return result;
+	}
+
+TBool TEquiv::IsMatchHostL() const
+	{
+	HBufC8* lhsDes = DecodedSegmentLC(iLhs, EUriHost);
+	HBufC8* rhsDes = DecodedSegmentLC(iRhs, EUriHost);
+	TBool result = IsMatchCaseless(*lhsDes, *rhsDes);
+	CleanupStack::PopAndDestroy(2);
+	return result;
+	}
+
+TBool TEquiv::IsMatchPortL() const
+	{
+	HBufC8* lhsDes = DecodedSegmentLC(iLhs, EUriPort);
+	HBufC8* rhsDes = DecodedSegmentLC(iRhs, EUriPort);
+	TBool result = IsMatchCaseSensitive(*lhsDes, *rhsDes);
+	CleanupStack::PopAndDestroy(2);
+	return result;
+	}
+
+TBool TEquiv::IsMatchPathL() const
+	{
+	HBufC8* lhsDes = DecodedSegmentLC(iLhs, EUriPath);
+	HBufC8* rhsDes = DecodedSegmentLC(iRhs, EUriPath);
+	TBool result = IsMatchCaseSensitive(*lhsDes, *rhsDes);
+	CleanupStack::PopAndDestroy(2);
+	return result;
+	}
+
+TBool TEquiv::IsMatchQueryL() const
+	{
+	HBufC8* lhsDes = DecodedSegmentLC(iLhs, EUriQuery);
+	HBufC8* rhsDes = DecodedSegmentLC(iRhs, EUriQuery);
+	TBool result = IsMatchCaseSensitive(*lhsDes, *rhsDes);
+	CleanupStack::PopAndDestroy(2);
+	return result;
+	}
+
+TBool TEquiv::IsMatchFragmentL() const
+	{
+	HBufC8* lhsDes = DecodedSegmentLC(iLhs, EUriFragment);
+	HBufC8* rhsDes = DecodedSegmentLC(iRhs, EUriFragment);
+	TBool result = IsMatchCaseSensitive(*lhsDes, *rhsDes);
+	CleanupStack::PopAndDestroy(2);
+	return result;
+	}
+
+HBufC8* TEquiv::DecodedSegmentLC(const TUriC8& aUri, TUriComponent aSegmentType) const
+	{
+	HBufC8* decoded = EscapeUtils::EscapeDecodeL(aUri.Extract(aSegmentType));
+	CleanupStack::PushL(decoded);
+	return decoded;
+	}
+
+TEquivSip::TEquivSip(const TUriC8& aLhs, const TUriC8& aRhs)
+: TEquiv(aLhs, aRhs)
+	{
+	}
+
+void TEquivSip::RemoveLeadingZeros(TPtr8 aHost) const
+	{
+	for (TInt i=0; i < aHost.Length(); i++)
+		{
+		TBool startOfNumber = (i == 0 || aHost[i-1] < '0' || aHost[i-1] > '9');
+		if (aHost[i] == '0' && startOfNumber)
+			{
+			// The character is a zero and is either at the start of the string
+			// or the fist number in a sequence.
+			aHost.Delete(i--,1);
+			}
+		}
+	}
+
+TBool TEquivSip::IsMatchHostL(const TDesC8& aLhs, const TDesC8& aRhs) const
+	{
+	UriUtils::TUriHostType lhsType = UriUtils::HostType(aLhs);
+	UriUtils::TUriHostType rhsType = UriUtils::HostType(aRhs);
+	if (lhsType != rhsType)
+		return EFalse;
+	if (lhsType != UriUtils::ETextHost)
+		{
+		// Host is IPv4 or IPv6
+		// Create a copy of the hosts and remove any leading '0's
+		HBufC8* lhsCopy = aLhs.AllocLC();
+		HBufC8* rhsCopy = aRhs.AllocLC();
+		RemoveLeadingZeros(lhsCopy->Des());
+		RemoveLeadingZeros(rhsCopy->Des());
+		TBool result = IsMatchCaseSensitive(*lhsCopy, *rhsCopy);
+		CleanupStack::PopAndDestroy(2);
+		return result;
+		}
+
+	return IsMatchCaseless(aLhs, aRhs);
+	}
+
+TBool TEquivSip::IsMatchHostL() const
+	{
+	HBufC8* lhsDes = DecodedSegmentLC(iLhs, EUriHost);
+	HBufC8* rhsDes = DecodedSegmentLC(iRhs, EUriHost);
+	TBool result = IsMatchHostL(*lhsDes, *rhsDes);
+	CleanupStack::PopAndDestroy(2);
+	return result;
+	}
+
+TBool TEquivSip::IsParamCompatibleL(const TDesC8& aLhsName, const TDesC8& aLhsValue, const TDesC8& aRhsName, const TDesC8& aRhsValue) const
+	{
+	// Were're just checking that if the segments have the same name part
+	// that their values are the same. This method only returns false if the lhs and rhs
+	// have the same name but their values are different.
+	if (!IsMatchCaseless(aLhsName, aRhsName))
+		{
+		// Names don't match so we're OK
+		return ETrue;
+		}
+
+	// The maddr parameter needs to be handled differently from the others
+	// The address is checked for equivalence not just a straight string compare.
+	if (IsMatchCaseless(aLhsName, KParamMaddr))
+		{
+			return IsMatchHostL(aLhsValue, aRhsValue);
+		}
+	else
+		{
+		if (!IsMatchCaseless(aLhsValue, aRhsValue))
+			{
+			// Names are the same but the values are different
+			// We have a incompatible parameter
+			return EFalse;
+			}
+		}
+
+	return ETrue;
+	}
+
+TBool TEquivSip::IsParamListCompatibleL(const TDelimitedParserBase8& aLhsParser, const TDelimitedParserBase8& aRhsParser) const
+
+	{
+	TPtrC8 lhsName;
+	TPtrC8 lhsValue;
+	TPtrC8 rhsName;
+	TPtrC8 rhsValue;
+
+	TPtrC8 lhsSegment;
+	TPtrC8 rhsSegment;
+
+	// roll back to the start of the lhs segment parser
+	aLhsParser.Reset();
+	
+	while( aLhsParser.GetNext(lhsSegment) == KErrNone )
+		{
+		// roll back to the start of the rhs segment parser
+		aRhsParser.Reset();
+
+		GetNameValuePair(lhsSegment, lhsName, lhsValue);
+		while( aRhsParser.GetNext(rhsSegment) == KErrNone )
+			{
+			GetNameValuePair(rhsSegment, rhsName, rhsValue);
+			if (!IsParamCompatibleL(lhsName, lhsValue, rhsName, rhsValue))
+				return EFalse;
+			}
+		}
+
+	return ETrue;
+	}
+
+TInt TEquivSip::ListLength(const TDelimitedParserBase8& aParser) const
+	{
+	aParser.Reset();
+	
+	TInt result = 0;
+	while (!aParser.Eos())
+		{
+		aParser.Inc();
+		++result;
+		}
+		
+	aParser.Reset();
+	return result;
+	}
+
+TBool TEquivSip::IsMatchPathL() const
+	{
+	HBufC8* lhs = DecodedSegmentLC(iLhs, EUriPath);
+	HBufC8* rhs = DecodedSegmentLC(iRhs, EUriPath);
+
+	TDelimitedPathSegmentParser8 lhsParser;
+	lhsParser.Parse(*lhs);
+	TDelimitedPathSegmentParser8 rhsParser;
+	rhsParser.Parse(*rhs);
+	
+	// Check each parameter in the lhs parameter list with those in
+	// the rhs parameter list. If at any point a parameter is incompatible
+	// we'll return false.
+	TBool result = ETrue;
+	
+	// Alway check the parameter list with the most parameters against the other
+	// so that we don't miss any
+	TInt lhsLength = ListLength(lhsParser);
+	TInt rhsLength = ListLength(rhsParser);
+	
+	if (lhsLength > rhsLength)
+		{
+		result = IsParamListCompatibleL(lhsParser, rhsParser);
+		}
+	else
+		{
+		result = IsParamListCompatibleL(rhsParser, lhsParser);
+		}
+
+	// check that the special parameters, if present, are present in both
+	if (result)
+		{
+		if ((lhs->Find(KParamUserFull) == KErrNotFound) != (rhs->Find(KParamUserFull) == KErrNotFound) ||
+		    (lhs->Find(KParamTtlFull) == KErrNotFound) != (rhs->Find(KParamTtlFull) == KErrNotFound) ||
+		    (lhs->Find(KParamMethodFull) == KErrNotFound) != (rhs->Find(KParamMethodFull) == KErrNotFound) ||
+		    (lhs->Find(KParamMaddrFull) == KErrNotFound) != (rhs->Find(KParamMaddrFull) == KErrNotFound) )
+			{
+			result = EFalse;
+			}
+		}
+	
+	CleanupStack::PopAndDestroy(2);
+	return result;
+	}
+
+
+TEquivSip::THeaderType TEquivSip::HeaderType(const TDesC8& aHeaderName) const
+	{
+	if (IsMatchCaseless(aHeaderName, KHeaderId) || IsMatchCaseless(aHeaderName, KHeaderIdAbbr))
+		return EHeaderId;
+	if (IsMatchCaseless(aHeaderName, KHeaderContact) || IsMatchCaseless(aHeaderName, KHeaderContactAbbr))
+		return EHeaderContact;
+	if (IsMatchCaseless(aHeaderName, KHeaderEncoding) || IsMatchCaseless(aHeaderName, KHeaderEncodingAbbr))
+		return EHeaderEncoding;
+	if (IsMatchCaseless(aHeaderName, KHeaderLength) || IsMatchCaseless(aHeaderName, KHeaderLengthAbbr))
+		return EHeaderLength;
+	if (IsMatchCaseless(aHeaderName, KHeaderType) || IsMatchCaseless(aHeaderName, KHeaderTypeAbbr))
+		return EHeaderType;
+	if (IsMatchCaseless(aHeaderName, KHeaderFrom) || IsMatchCaseless(aHeaderName, KHeaderFromAbbr))
+		return EHeaderFrom;
+	if (IsMatchCaseless(aHeaderName, KHeaderSubject) || IsMatchCaseless(aHeaderName, KHeaderSubjectAbbr))
+		return EHeaderSubject;
+	if (IsMatchCaseless(aHeaderName, KHeaderTo) || IsMatchCaseless(aHeaderName, KHeaderToAbbr))
+		return EHeaderTo;
+
+	return EHeaderNormal;
+	}
+
+TBool TEquivSip::IsMatchHeader(const TDesC8& aLhs, const TDesC8& aRhs) const
+	{
+	if (IsMatchCaseless(aLhs, aRhs))
+		{
+		// identical headers are always OK
+		return ETrue;
+		}
+
+	// We now need to check for abbreviated headers
+	TPtrC8 lhsName;
+	TPtrC8 lhsValue;
+	TPtrC8 rhsName;
+	TPtrC8 rhsValue;
+
+	GetNameValuePair(aLhs, lhsName, lhsValue);
+	GetNameValuePair(aRhs, rhsName, rhsValue);
+
+	if (!IsMatchCaseless(lhsValue, rhsValue))
+		{
+		// headers with different values can never match
+		return EFalse;
+		}
+
+	// We now have only those with different header names but with the same value
+	// The last check is to see if the headers are in abbreviated forms
+	THeaderType lhsType = HeaderType(lhsName);
+	THeaderType rhsType = HeaderType(rhsName);
+	if (lhsType != EHeaderNormal && (lhsType == rhsType))
+		{
+		// They are both special headers of the same type
+		// Everything matches
+		return ETrue;
+		}
+
+	return EFalse;
+	}
+
+TBool TEquivSip::IsQueryListCompatible(const TDelimitedParserBase8& aLhsParser, const TDelimitedParserBase8& aRhsParser) const
+
+	{
+	TPtrC8 lhsSegment;
+	TPtrC8 rhsSegment;
+
+	TBool found = EFalse;
+	// roll back to the start of the lhs segment parser
+	aLhsParser.Reset();
+
+	while( aLhsParser.GetNext(lhsSegment) == KErrNone )
+		{
+		// roll back to the start of the rhs segment parser
+		aRhsParser.Reset();
+		
+		found = EFalse;
+
+		while( aRhsParser.GetNext(rhsSegment) == KErrNone )
+			{
+			if (IsMatchHeader(lhsSegment, rhsSegment))
+				{
+				// a match has been found for this header so move to the next
+				// header in the lhs list
+				found = ETrue;
+				break;
+				}
+			}
+		if (!found)
+			{
+			// no match has been found so the headers are not equvalent
+			return EFalse;
+			}
+		}
+
+	return ETrue;
+	}
+
+TBool TEquivSip::IsMatchQueryL() const
+	{
+	HBufC8* lhs = DecodedSegmentLC(iLhs, EUriQuery);
+	HBufC8* rhs = DecodedSegmentLC(iRhs, EUriQuery);
+
+	TDelimitedQueryParser8 lhsParser;
+	lhsParser.Parse(*lhs);
+	TDelimitedQueryParser8 rhsParser;
+	rhsParser.Parse(*rhs);
+
+	TBool result = EFalse;
+	
+	// first check that the number of headers are the same in both lists.
+	TInt lhsLength = ListLength(lhsParser);
+	TInt rhsLength = ListLength(rhsParser);
+	if (lhsLength == rhsLength)
+		{
+		// Check each parameter in the lhs parameter list with those in
+		// the rhs parameter list. If at any point a parameter is incompatible
+		// we'll return false.
+		result = IsQueryListCompatible(lhsParser, rhsParser);
+		}
+	
+	CleanupStack::PopAndDestroy(2);
+	return result;
+	}
+
+TBool TEquivSip::IsMatchFragmentL() const
+	{
+	// We don't care about the fragment for SIP URIs
+	return ETrue;
+	}