--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/httpfilters/httpfilterproxy/Src/HttpFilterProxy.cpp Tue Feb 02 01:09:52 2010 +0200
@@ -0,0 +1,651 @@
+/*
+* Copyright (c) 2003 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: Proxy filter
+*
+*/
+
+
+
+// INCLUDE FILES
+#include <http.h>
+#include <es_sock.h>
+#include <cdbstore.h>
+#include <commdbconnpref.h>
+
+// User Includes
+#include "httpfilterproxy.h"
+#include "httpfiltercommonstringsext.h"
+
+// EXTERNAL DATA STRUCTURES
+
+// EXTERNAL FUNCTION PROTOTYPES
+void PanicHttpFiltersProxy(TInt aErr = 0);
+
+// CONSTANTS
+const TInt KProxyOrderOffset = 20;
+// MACROS
+
+// LOCAL CONSTANTS AND MACROS
+
+// MODULE DATA STRUCTURES
+
+// LOCAL FUNCTION PROTOTYPES
+
+// FORWARD DECLARATIONS
+
+// ============================= LOCAL FUNCTIONS ===============================
+
+// -----------------------------------------------------------------------------
+// LocalHostCheckL
+// If the host is a local host, then remove the proxy property. Returns ETrue
+// if the transaction request URI was for a localhost.
+// -----------------------------------------------------------------------------
+//
+TBool LocalHostCheckL(RHTTPTransaction& aTransaction, RStringPool& aStringPool)
+ {
+ _LIT8(KLoopbackIPv4Url, "http://127.0.0.1");
+
+ RHTTPRequest request = aTransaction.Request();
+ TUriC8 uri = request.URI();
+
+ TUriParser8 parserLoopBack;
+ parserLoopBack.Parse(KLoopbackIPv4Url());
+ TInt match = parserLoopBack.Compare(uri, EUriHost);
+
+ if (KErrNone != match)
+ {
+ _LIT8(KLocalHostUrl, "http://localhost");
+
+ // try another compare - compare against the "localhost".
+ TUriParser8 parserLocalHost;
+ parserLocalHost.Parse(KLocalHostUrl());
+ match = parserLocalHost.Compare(uri, EUriHost);
+
+ if (KErrNone == match)
+ {
+ _LIT8(KLoopbackIPv4, "127.0.0.1");
+
+ // "localhost" resolves to "::1", manually, replace with "127.0.0.1"
+ CUri8* newUri = CUri8::NewLC(uri);
+ newUri->SetComponentL(KLoopbackIPv4(), EUriHost);
+ request.SetURIL(newUri->Uri());
+ CleanupStack::PopAndDestroy(newUri);
+ }
+ }
+
+ if (KErrNone == match)
+ {
+ // request is for localhost, explicitly state that this transaction
+ // must not be sent to proxy.
+ RStringF proxyUsageStrF = aStringPool.StringF(HTTP::EProxyUsage,
+ RHTTPSession::GetTable());
+
+ RStringF dontUseProxyStrF = aStringPool.StringF(HTTP::EDoNotUseProxy,
+ RHTTPSession::GetTable());
+
+ aTransaction.PropertySet().RemoveProperty(proxyUsageStrF);
+ aTransaction.PropertySet().SetPropertyL(proxyUsageStrF, dontUseProxyStrF);
+
+ //RStringF proxyAddrStrF = aStringPool.StringF(HTTP::EProxyAddress,
+ // RHTTPSession::GetTable());
+
+ //aTransaction.PropertySet().RemoveProperty(proxyAddrStrF);
+ return ETrue;
+ }
+ return EFalse;
+ }
+
+// ============================ MEMBER FUNCTIONS ===============================
+
+// -----------------------------------------------------------------------------
+// CHttpFilterProxy::CHttpFilterProxy
+// C++ default constructor can NOT contain any code, that
+// might leave.
+// -----------------------------------------------------------------------------
+//
+CHttpFilterProxy::CHttpFilterProxy(RHTTPSession* aSession)
+{
+ __ASSERT_DEBUG(aSession != NULL, PanicHttpFiltersProxy());
+ iSession = aSession;
+}
+
+
+// ------------------------------------------------------------------------------------------
+// CHttpFilterProxy::ConstructL
+// Symbian 2nd phase constructor can leave.
+// ------------------------------------------------------------------------------------------
+//
+void CHttpFilterProxy::ConstructL()
+{
+ iStringPool = iSession->StringPool();
+ iConnInfo = iSession->ConnectionInfo();
+
+ // Added to fix panic when Proxy filter is used outside the browser initiated HTTP Framework.
+ iSession->StringPool().OpenL(HttpFilterCommonStringsExt::GetTable());
+
+ // Register the filter for submit events,
+ // Adds a filter to the session's filter queue.
+
+ iSession->FilterCollection().AddFilterL(*this, // The filter to add
+ THTTPEvent::ESubmit, // The event that triggers this filter
+ RStringF(), // The header whose presence triggers this filter, or KNullDesC to trigger on any header
+ KAnyStatusCode, // The status code that triggers this filter, or KAnyStatusCode to trigger on any status code
+ ECache - KProxyOrderOffset,// The position of the filter in the queue
+ iStringPool.StringF(HttpFilterCommonStringsExt::EProxyFilter,RHTTPSession::GetTable())); //The name of the filter to add
+
+ // register for notification of KErrNotReady error codes
+ // this allows us to re-start the connection if it fails
+ iSession->FilterCollection().AddFilterL(*this, KErrNotReady, MHTTPFilter::ETidyUp -1, iStringPool.StringF(HttpFilterCommonStringsExt::EProxyFilter,RHTTPSession::GetTable()));
+}
+
+//---------------------------------------------------------------------------------------------
+// Destructor
+//----------------------------------------------------------------------------------------------
+//
+CHttpFilterProxy::~CHttpFilterProxy()
+{
+ // Cleanup strings.
+ iProxyAddress.Close();
+ iExceptions.Close();
+
+/*
+// Closing RSocketServ is causing crash in SDK Applications. SDK Apps will have to close RSocketServ and RConnection after closing CHTTPSession.
+// It is not needed to close connection as soon as the CHttpConnectionManager close connection by
+// calling CHttpConnectionManager::CloseConnection()
+ if(iFilterOwnsConnection)
+ {
+ // Close ESOCK handles
+ iConnection.Close();
+ iSocketServ.Close();
+ }
+*/
+}
+
+// ---------------------------------------------------------------------------------------------
+// CHttpFilterProxy::InstallFilterL
+// Two-phased constructor. This function replaces NewL.
+// ---------------------------------------------------------------------------------------------
+//
+CHttpFilterProxy* CHttpFilterProxy::InstallFilterL(TAny* aSession)
+{
+ __ASSERT_DEBUG(aSession != NULL, PanicHttpFiltersProxy());
+ RHTTPSession* session = REINTERPRET_CAST(RHTTPSession*, aSession);
+ CHttpFilterProxy* filter = new (ELeave) CHttpFilterProxy( session );
+ CleanupStack::PushL(filter);
+ filter->ConstructL();
+ CleanupStack::Pop(filter);
+ return filter;
+}
+
+// ---------------------------------------------------------------------------------------------
+// CHttpFilterProxy::MHFLoad
+// Called when the filter is being added to the session's filter queue.
+// ---------------------------------------------------------------------------------------------
+//
+void CHttpFilterProxy::MHFLoad(RHTTPSession, THTTPFilterHandle)
+{
+ ++iLoadCount;
+}
+
+// ----------------------------------------------------------------------------------------------
+// CHttpFilterProxy::MHFUnload
+// Called when the filter is being removed from a session's filter queue.
+// ----------------------------------------------------------------------------------------------
+//
+void CHttpFilterProxy::MHFUnload(RHTTPSession /*aSession*/, THTTPFilterHandle /*aFilterHandler*/)
+{
+ __ASSERT_DEBUG(iLoadCount >= 0, PanicHttpFiltersProxy());
+ if (--iLoadCount)
+ {
+ return;
+ }
+ delete this;
+}
+
+// ------------------------------------------------------------------------------------------------
+// CHttpFilterProxy::MHFRunL
+// Process a transaction event.
+// ------------------------------------------------------------------------------------------------
+//
+void CHttpFilterProxy::MHFRunL(RHTTPTransaction aTransaction,
+ const THTTPEvent& aEvent)
+ {
+ switch(aEvent.iStatus)
+ {
+ case THTTPEvent::ESubmit:
+ {
+ if (LocalHostCheckL(aTransaction, iStringPool))
+ {
+ return;
+ }
+ SetProxyL(aTransaction);
+ }
+ break;
+
+ case KErrNotReady:
+ {
+ if (LocalHostCheckL(aTransaction, iStringPool))
+ {
+ return;
+ }
+ if(iFilterOwnsConnection)
+ {
+ // we must re-start the RConnection
+ TInt err = iConnection.Start();
+ if (err == KErrAlreadyExists)
+ {
+ // the KErrNotReady must have come from elsewhere
+ return;
+ }
+
+ User::LeaveIfError(err);
+
+ // re-submit the transaction
+ aTransaction.Cancel();
+ aTransaction.SubmitL();
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ }
+
+// -----------------------------------------------------------------------------
+// CHttpFilterProxy::MHFRunError
+// Process an error that occured while processing the transaction.
+// -----------------------------------------------------------------------------
+//
+TInt CHttpFilterProxy::MHFRunError(TInt /*aError*/,
+ RHTTPTransaction aTransaction,
+ const THTTPEvent& /*aEvent*/)
+{
+ // Cleanup strings.
+ iProxyAddress.Close();
+ iExceptions.Close();
+ aTransaction.Fail();
+ return KErrNone;
+}
+
+// -----------------------------------------------------------------------------
+// CHttpFilterProxy::MHFSessionRunL
+// Process a session event.
+// -----------------------------------------------------------------------------
+//
+void CHttpFilterProxy::MHFSessionRunL(const THTTPSessionEvent& /*aEvent*/)
+{
+}
+
+// -----------------------------------------------------------------------------
+// CHttpFilterProxy::MHFSessionRunError
+// Called when MHFRunL leaves from a session event.
+// -----------------------------------------------------------------------------
+//
+TInt CHttpFilterProxy::MHFSessionRunError(TInt aError, const THTTPSessionEvent& /*aEvent*/)
+{
+ return aError;
+}
+
+// -----------------------------------------------------------------------------
+// CHttpFilterProxy::SetProxyL
+// Function to handle Submit events.
+// Set proxy properties (EUseProxy and EProxyAddress) in order to get connected throught
+// proxy when a new HTTP session will be in effect. The Proxy data will be taken from
+// the CommDb if a new connection has been used. If a proxy property has already been
+// set by a higher filter or by the client, then those settings are preferred over
+// CommsDB.
+// -----------------------------------------------------------------------------
+//
+void CHttpFilterProxy::SetProxyL(const RHTTPTransaction aTransaction)
+{
+ THTTPHdrVal isNewConn, proxyAddress;
+
+ // If connection has been disconnected the user could change Access Point, so let's
+ // check if a new connection has been used.
+ TBool ret = iConnInfo.Property (iStringPool.StringF(HttpFilterCommonStringsExt::EHttpNewConnFlag,
+ HttpFilterCommonStringsExt::GetTable()), isNewConn);
+
+ if (ret && isNewConn.Type() == THTTPHdrVal::KTIntVal && (isNewConn.Int() == 1))
+ {
+ iUsage = EFalse;
+ // Read proxy info from the CommDB
+ ReadProxyInfoL(iStringPool,
+ iUsage,
+ iProxyAddress,
+ iExceptions);
+ }
+ else
+ {
+ TBool useProxy( ETrue );
+ THTTPHdrVal proxyUsageVal;
+
+ // Is the property for proxy usage set?
+ TBool hasUsageValue = iConnInfo.Property(iStringPool.StringF(HTTP::EProxyUsage,
+ RHTTPSession::GetTable()), proxyUsageVal);
+
+ // If the property is set, is it EUseProxy
+ if (hasUsageValue)
+ {
+ useProxy = ( proxyUsageVal.StrF().Index(RHTTPSession::GetTable()) == HTTP::EUseProxy);
+ }
+
+ // Use proxy if the property value is EUseProxy or no property is set.
+ if (useProxy || !hasUsageValue)
+ {
+ THTTPHdrVal proxyAddressVal;
+ TBool hasValue = iConnInfo.Property(iStringPool.StringF(HTTP::EProxyAddress,
+ RHTTPSession::GetTable()), proxyAddressVal);
+ if (!hasValue)
+ {
+ // The proxyAddress has not been set, so Read proxy info from the CommDB
+ ReadProxyInfoL(iStringPool,
+ iUsage,
+ iProxyAddress,
+ iExceptions);
+
+ }
+ else
+ {
+ // Now get the proxy address...
+ iProxyAddress = proxyAddressVal.StrF().Copy();
+ iUsage = ETrue;
+ }
+ }
+// }
+ }
+
+ // We should not use proxy if the uri matches to one of exceptions from the exception list
+ // In this case the Origing server will be used
+
+ ExcptionsCompare(aTransaction);
+
+ // Respect proxy settings already defined by client or higher HTTP filter
+ THTTPHdrVal proxyUsage; // Check if property present or not present, not the value.
+ if ( !iConnInfo.Property(iStringPool.StringF(HTTP::EProxyUsage,
+ RHTTPSession::GetTable()), proxyUsage))
+ {
+
+ // Set the proxy address and proxy Usage
+ proxyAddress = THTTPHdrVal(iProxyAddress);
+
+ iConnInfo.RemoveProperty(iStringPool.StringF(HTTP::EProxyAddress,
+ RHTTPSession::GetTable()));
+ iConnInfo.SetPropertyL(iStringPool.StringF(HTTP::EProxyAddress,
+ RHTTPSession::GetTable()), proxyAddress);
+
+ iConnInfo.RemoveProperty(iStringPool.StringF(HTTP::EProxyUsage,
+ RHTTPSession::GetTable()));
+ if (iUsage)
+ {
+ iConnInfo.SetPropertyL(iStringPool.StringF(HTTP::EProxyUsage,
+ RHTTPSession::GetTable()), iStringPool.StringF(HTTP::EUseProxy,
+ RHTTPSession::GetTable()));
+ }
+ else
+ {
+ iConnInfo.SetPropertyL(iStringPool.StringF(HTTP::EProxyUsage,
+ RHTTPSession::GetTable()), iStringPool.StringF(HTTP::EDoNotUseProxy,
+ RHTTPSession::GetTable()));
+ }
+
+ }
+
+
+ // Cleanup strings.
+ iProxyAddress.Close();
+ iExceptions.Close();
+}
+
+
+// -----------------------------------------------------------------------------
+// CHttpFilterProxy::ReadProxyInfoL
+// Purpose: Reads the Proxy information from the comms database.
+// Gives the Proxy usage, a proxy address (<proxy name>:<proxy port>),
+// and proxy exceptions through the output arguments.
+// -----------------------------------------------------------------------------
+//
+void CHttpFilterProxy::ReadProxyInfoL(const RStringPool aStringPool,
+ TBool& aUsage,
+ RStringF& aProxyAddress,
+ RStringF& aExceptions)
+
+{
+ // Let's find Internet Access point ID (ServiceId) in use from the RConnection associated with the HTTP framework
+ THTTPHdrVal connValue;
+ TUint32 serviceId;
+ TUint pushCount = 0;
+ RConnection* connPtr = NULL;
+ THTTPHdrVal iapId;
+ TCommDbConnPref pref;
+ TBool hasValue = iConnInfo.Property (aStringPool.StringF(HTTP::EHttpSocketConnection, RHTTPSession::GetTable()),
+ connValue);
+
+ if (hasValue && connValue.Type() == THTTPHdrVal::KTIntVal)
+ {
+ connPtr = REINTERPRET_CAST(RConnection*, connValue.Int());
+ }
+ else
+ {
+ // Connect to ESOCK and open connection handle
+ User::LeaveIfError(iSocketServ.Connect());
+ User::LeaveIfError(iConnection.Open(iSocketServ));
+
+ TBool ret = iConnInfo.Property (aStringPool.StringF(HttpFilterCommonStringsExt::EAccessPointID, HttpFilterCommonStringsExt::GetTable()),
+ iapId);
+ if (ret && (iapId.Type() == THTTPHdrVal::KTIntVal) && (iapId.Int() != 0) )
+ {
+ pref.SetDialogPreference( ECommDbDialogPrefDoNotPrompt );
+ pref.SetIapId( iapId.Int() );
+ //pref.SetBearerSet( ECommDbBearerGPRS );
+ pref.SetDirection(ECommDbConnectionDirectionOutgoing);
+ TInt err = iConnection.Start(pref);
+ if ( err != KErrNone )
+ User::LeaveIfError(iConnection.Start());
+ }
+ else
+ {
+ // start the connection - note that this thread will be blocked until the connection is established.
+ User::LeaveIfError(iConnection.Start());
+ }
+
+ connPtr = &iConnection;
+
+ // set the ESOCK handles as session properties after successful connection
+ iConnInfo.SetPropertyL(aStringPool.StringF(HTTP::EHttpSocketServ, RHTTPSession::GetTable()), iSocketServ.Handle());
+ iConnInfo.SetPropertyL(aStringPool.StringF(HTTP::EHttpSocketConnection, RHTTPSession::GetTable()), reinterpret_cast<TInt>(&iConnection));
+
+ iFilterOwnsConnection = ETrue;
+ }
+
+ // Retrieve the service Id from the RConnection ; "<table name>/<field name>
+ User::LeaveIfError(connPtr->GetIntSetting(_L("IAP\\IAPService"), serviceId));
+
+ // Retrieve the service Type from the RConnection
+ HBufC* serviceType16 = HBufC::NewL(KCommsDbSvrMaxFieldLength);
+ CleanupStack::PushL(serviceType16);
+ pushCount++;
+ TPtr serviceTypePtr16 = serviceType16->Des();
+ User::LeaveIfError(connPtr->GetDesSetting(_L("IAP\\IAPServiceType"), serviceTypePtr16));
+
+ // Open the Comms DB with the IAP style
+ CCommsDatabase* db=CCommsDatabase::NewL(EDatabaseTypeUnspecified);
+ CleanupStack::PushL(db);
+ pushCount++;
+
+ // Opens a view on records in the Proxies table with a specified range of service types and service IDs.
+ // Proxies records are included that have matching service types and IDs.
+ // When the use of the view object is complete, it should be popped from the cleanup stack, and deleted.
+ CCommsDbTableView* pView = db->OpenViewOnProxyRecordLC(serviceId, serviceTypePtr16);
+
+ pushCount++;
+ TInt err = pView->GotoFirstRecord();
+ aUsage = ETrue;
+ // if there is some other DB error.
+ if ( err != KErrNotFound && err != KErrNone )
+ {
+ User::LeaveIfError( err );
+ }
+ if ( err == KErrNotFound )
+ {// setting to EFalse because proxy table view does not exisit, was not created during Access point creation?
+ aUsage = EFalse;
+ }
+ else
+ // no need to keep going as not Proxy will be used.
+ {
+ // Read the proxy usage flag from the selected Proxy record.
+ pView->ReadBoolL(TPtrC(PROXY_USE_PROXY_SERVER), aUsage);
+
+ // COntinue only if the proxy usage flag is set
+ if (aUsage)
+ {
+ // Read the server name specified in the selected Proxy record.
+ // The proxyAddress will be build based on the serverName and the port number
+ // separated with ':'
+ HBufC* serverName = pView->ReadLongTextLC(TPtrC(PROXY_SERVER_NAME));
+ pushCount++;
+ TPtr serverNamePtr16 = serverName->Des();
+ serverNamePtr16.Trim();
+
+ // Continue only if we have a proxy name
+ if (serverNamePtr16.Length() > 0 && serverNamePtr16.Ptr() != NULL)
+ {
+ // Read the port number specified in the selected Proxy record
+ TUint32 portNumber = 8080; // default port number
+ TInt length = 1; // add 1 for the separator ':'
+ pView->ReadUintL(TPtrC(PROXY_PORT_NUMBER), portNumber);
+ // calculate the length of portNumber
+
+ if (portNumber == 0)
+ {
+ portNumber = 8080;
+ }
+
+ TUint32 portNumSave = portNumber;
+ while (portNumSave > 0)
+ {
+ portNumSave = portNumSave/10;
+ length++;
+ }
+ // convert number to string & add to the server name. We should get <serverName1>:<port number>
+ HBufC8* proxyAddress8 = HBufC8::NewL(serverNamePtr16.Length() + length);
+ CleanupStack::PushL(proxyAddress8);
+ pushCount++;
+ TPtr8 proxyAddressPtr8 = proxyAddress8->Des();
+ proxyAddressPtr8.Copy(serverNamePtr16);
+ _LIT(KSeparator, ":");
+ proxyAddressPtr8.Append(KSeparator);
+ proxyAddressPtr8.AppendNum((TInt)portNumber);
+
+ // Set gateway as a service number - this is the proxy address
+ aProxyAddress = aStringPool.OpenFStringL(proxyAddressPtr8);
+
+ // Read the server name specified in the selected Proxy record.
+ HBufC* exceptions16 = pView->ReadLongTextLC(TPtrC(PROXY_EXCEPTIONS));
+ pushCount++;
+ TPtr exceptionsPtr16 = exceptions16->Des();
+ // Copy to 8-bits...
+ HBufC8* exceptions8 = HBufC8::NewL(exceptionsPtr16.Length());
+ CleanupStack::PushL(exceptions8);
+ pushCount++;
+ TPtr8 exceptionsPtr8 = exceptions8->Des();
+ exceptionsPtr8.Copy(exceptionsPtr16);
+
+ // Set gateway as a service number - this is the proxy address
+ aExceptions = aStringPool.OpenFStringL(exceptionsPtr8);
+ }
+ else
+ {
+ aUsage = EFalse;
+ }
+ }
+ }
+ // All done - clean up
+ CleanupStack::PopAndDestroy(pushCount); //pView, serverType16, db, serverName, proxyAddress8, exceptions8 exceptions16
+}
+
+//---------------------------------------------------------------------------------
+//
+// Get exception from the list of exceptions
+// Returns ETrue if there is at least one exception in the list.
+// Returns EFalse if list of exceptions is empty or there is no exceptions in the list any more.
+//---------------------------------------------------------------------------------
+TBool CHttpFilterProxy::GetException(TPtr8& aException,TInt& aStartIndex)
+{
+ TBool done;
+ TPtrC8 exceptionsDesC = iExceptions.DesC();
+ TPtr8 exceptionsDes((TUint8*)exceptionsDesC.Ptr(), exceptionsDesC.Length(),exceptionsDesC.Length());
+
+ exceptionsDes.TrimAll();
+ // get part of string starting from aStartIndex. aStartIndex =0 when it is called the first time.
+ TPtrC8 desPtr = exceptionsDes.Mid(aStartIndex);
+
+ //The offset of the exception position from the beginning of the exception list
+ const TChar separator(';');
+ TInt position = desPtr.Locate(separator);
+ if (position != KErrNotFound)
+ {
+ // Extracts a portion of the data starting from the beggining
+ // Get the rest of the descriptor without the separator that we have found
+ aException.Set((unsigned char*)desPtr.Ptr(), position, position);
+ aStartIndex = position + 1;
+ done = ETrue;
+ }
+ else
+ {
+ aException.Set((unsigned char*)desPtr.Ptr(), desPtr.Length(), desPtr.Length());
+ done = EFalse;
+ }
+ return done;
+}
+
+// -----------------------------------------------------------------------------
+// CHttpFilterProxy::ExcptionsCompare
+// Compare each exception from the exception list against the current uri.
+// -----------------------------------------------------------------------------
+//
+void CHttpFilterProxy::ExcptionsCompare(const RHTTPTransaction aTransaction)
+{
+ if (iUsage)
+ {
+ TBool isFound = ETrue;
+ TInt index = 0;
+ TInt loopCount = 0;
+ TPtr8 exception (NULL, 0,0);
+ // Let's get uri for this transaction to comapre the current uri with an exception.
+ // If the current uri has an exception for proxy set proxy usage to EDoNotUseProxy.
+ RHTTPRequest req = aTransaction.Request();
+ TUriC8 originalUri = req.URI();
+ const TDesC8& uriHost = originalUri.UriDes();
+
+ TPtrC8 exceptionsDes = iExceptions.DesC();
+ if (exceptionsDes.Length() != 0)
+ {
+ // at least one exception has been found in the list of exceptions
+ while (isFound)
+ {
+ loopCount++;
+ isFound = GetException(exception, index);
+ if (exception.Compare(uriHost) == NULL)
+ {
+ iUsage = EFalse;
+ isFound = EFalse;
+ }
+ } //while
+ }
+ }
+}
+
+// End of File