--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/pkiutilities/ocsp/transport/transporthttp.cpp Tue Jan 26 15:20:08 2010 +0200
@@ -0,0 +1,558 @@
+// Copyright (c) 2001-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:
+// Implement the transport class for using HTTP GET and POST.
+// Links the OCSP module to the HTTP module.
+//
+//
+
+#include <http/cecomfilter.h>
+#include <escapeutils.h>
+#include "securitypolicy.h"
+#include "ocsppolicy.h"
+#include "ocsptransport.h"
+#include "panic.h"
+#include <tconvbase64.h>
+#include "callbacktimer.h"
+
+_LIT8(KOCSPContentTypeRequest, "application/ocsp-request");
+_LIT8(KOCSPContentTypeResponse, "application/ocsp-response");
+_LIT8(KSlash, "/");
+
+// Min request size for the use of POST method (else use GET method)
+const TInt KMinReqSizeForHTTPPOST = 255;
+
+const TInt KMillSecsToMicroSecs = 1000;
+
+// Default value of timeout means it's disabled
+const TInt KTimeoutDisabledValue = KTransportDefaultRequestTimeout;
+
+EXPORT_C COCSPTransportHttp* COCSPTransportHttp::NewL(const TDesC8& aUri, TUint32& aIap)
+ {
+ COCSPTransportHttp* self = new (ELeave) COCSPTransportHttp(aIap);
+ CleanupStack::PushL(self);
+ self->ConstructL(aUri);
+ CleanupStack::Pop(self);
+ return self;
+ }
+
+COCSPTransportHttp::COCSPTransportHttp(TUint32& aIap)
+ : CActive(CActive::EPriorityStandard), iIap(aIap),
+ iTimeout(KTimeoutDisabledValue)
+ {
+ CActiveScheduler::Add(this);
+ }
+
+void COCSPTransportHttp::ConstructL(const TDesC8& aUri)
+ {
+ // Create the timer object
+ iTimer = CCallbackTimer::NewL(*this);
+
+ TUriParser8 uri;
+ uri.Parse(aUri);
+ iHTTPSession.OpenL(uri, iIap, EFalse);
+
+ // Install the HTTP filter (filter UID is specified in swipolicy.ini file)
+ InstallHttpFilterL();
+ }
+
+COCSPTransportHttp::~COCSPTransportHttp()
+ {
+ Deque();
+ iHTTPTransaction.Close();
+ iHTTPSession.Close();
+
+ delete iUri;
+ delete iRequest;
+ delete iResponseData;
+ delete iTimer;
+ }
+
+// From MOCSPTransport
+void COCSPTransportHttp::SendRequest(const TDesC8& aURI,
+ const TDesC8& aRequest,
+ const TInt aTimeout,
+ TRequestStatus& aStatus)
+ {
+ iCallBack = &aStatus;
+ aStatus = KRequestPending;
+ iServerFound = ETrue;
+
+ delete iUri;
+ delete iRequest;
+
+ iTimeout = aTimeout;
+ iUri = aURI.Alloc();
+ iRequest = aRequest.Alloc();
+
+ // Check for out of memory
+ if (iUri == NULL || iRequest == NULL)
+ {
+ Complete(KErrNoMemory);
+ }
+
+ // Take action depending on current state
+ if (iState == ETransportConnectingState)
+ {
+ // Establish a connection
+ // Note: Timeout and retries don't apply to this operation
+ iState = ETransportConnectingState;
+ iHTTPSession.StartConnection(iStatus);
+ SetActive();
+ }
+ else
+ {
+ // We've connected already, send the request.
+ iStatus = KRequestPending;
+ TRAPD(err, DoSendRequestL(*iUri, *iRequest));
+ if (err == KErrNone)
+ {
+ // Start the timer (if enabled)
+ if (iTimeout > KTimeoutDisabledValue)
+ {
+ iTimer->After(iTimeout * KMillSecsToMicroSecs);
+ }
+ SetActive();
+ }
+ else
+ {
+ Complete(err);
+ }
+ }
+ }
+
+void COCSPTransportHttp::InstallHttpFilterL()
+ {
+ // Install filters (if any)
+ Swi::RSecPolHandle secPol;
+ secPol.OpenLC();
+ TUint32 filterId = secPol().OcspHttpHeaderFilter();
+ if (filterId != 0)
+ {
+ TUid headerFilterUid = {filterId};
+ CEComFilter::InstallFilterL(iHTTPSession.HTTPSession(), headerFilterUid);
+ }
+ CleanupStack::PopAndDestroy(&secPol);
+ }
+
+void COCSPTransportHttp::DoSendRequestL(const TDesC8& aURI,
+ const TDesC8& aRequest)
+ {
+ delete iResponseData;
+ iResponseData = NULL;
+ iOCSPRequest.Set(aRequest);
+
+ // Close previous transaction, if any
+ iHTTPTransaction.Close();
+
+ // If the size of the DER encoded request is less than 255 bytes use the GET method (if enabled)
+ // else use the POST method
+ RBuf8 url8;
+ CleanupClosePushL(url8);
+ TInt reqSize = aRequest.Length();
+
+ COcspPolicy* ocspPolicy = COcspPolicy::NewL();
+ TBool useHTTPGETMethod = ocspPolicy->IsHttpGETMethodEnabled();
+
+ delete ocspPolicy;
+
+ if (useHTTPGETMethod && reqSize < KMinReqSizeForHTTPPOST)
+ {
+ // Set the OCSP request as part of the url after Base64 and URL encoding
+
+ TBase64 base64;
+
+ TInt destLen = ((iOCSPRequest.Length() - 1 ) / 3 + 1) * 4;
+ HBufC8* encodedBuf = HBufC8::NewMaxLC(destLen); // to get the decoded string
+ TPtr8 encodedPtr = encodedBuf->Des();
+
+ base64.Encode(iOCSPRequest, encodedPtr);
+
+ HBufC8* escEncodedReq = EscapeUtils::EscapeEncodeL(encodedPtr, EscapeUtils::EEscapeUrlEncoded);
+
+ CleanupStack::PushL(escEncodedReq);
+ url8.CreateL(aURI, aURI.Length() + KSlash().Length() + escEncodedReq->Length());
+ // Append a slash only if it's already not present
+ if ((aURI.Length() > 0) && (aURI.Right(1) != KSlash))
+ {
+ url8.Append(KSlash);
+ }
+ url8.Append(*escEncodedReq);
+ CleanupStack::PopAndDestroy(2, encodedBuf);
+
+ if (iURI.Parse(url8))
+ {
+ User::Leave(OCSP::KErrInvalidURI);
+ }
+
+ // Create HTTP transaction, method = GET (default)
+ iHTTPTransaction = iHTTPSession().OpenTransactionL(iURI, *this);
+
+ // Add the appropriate Http headers
+ AddHttpHeadersL();
+ }
+ else
+ {
+ // Use the passed in URL as is
+ if (iURI.Parse(aURI))
+ {
+ User::Leave(OCSP::KErrInvalidURI);
+ }
+
+ // Create HTTP transaction, method = POST
+ RStringPool stringPool = iHTTPSession().StringPool();
+ iHTTPTransaction = iHTTPSession().OpenTransactionL(iURI, *this, stringPool.StringF(HTTP::EPOST,RHTTPSession::GetTable()));
+
+ // Add the appropriate Http headers
+ AddHttpHeadersL();
+
+ // Setup the data supplier
+ iHTTPTransaction.Request().SetBody(*this);
+ }
+
+ // Finally submit the transaction
+ iHTTPTransaction.SubmitL();
+ CleanupStack::PopAndDestroy(&url8);
+ }
+
+void COCSPTransportHttp::AddHttpHeadersL()
+ {
+ // Get request + it's headers
+ RStringPool stringPool = iHTTPSession().StringPool();
+ RHTTPRequest request = iHTTPTransaction.Request();
+ RHTTPHeaders headers = request.GetHeaderCollection();
+
+ // Host header: done by HTTP module
+ // Content-Length header: done by HTTP module
+
+ // Connection:close header:
+ headers.SetFieldL(stringPool.StringF(HTTP::EConnection, RHTTPSession::GetTable()), stringPool.StringF(HTTP::EClose, RHTTPSession::GetTable()));
+
+ // Content-Type header:
+ RStringF ocspRequest = stringPool.OpenFStringL(KOCSPContentTypeRequest);
+ CleanupClosePushL(ocspRequest);
+ THTTPHdrVal contentTypeVal(ocspRequest);
+ headers.SetFieldL(stringPool.StringF(HTTP::EContentType,RHTTPSession::GetTable()), contentTypeVal);
+ CleanupStack::PopAndDestroy(&ocspRequest);
+ }
+
+// From MOCSPTransport
+void COCSPTransportHttp::CancelRequest()
+ {
+ Cancel();
+ }
+
+
+// From MOCSPTransport
+TPtrC8 COCSPTransportHttp::GetResponse() const
+ {
+ __ASSERT_ALWAYS(iResponseData, Panic(KErrNotReady));
+
+ return iResponseData->Des();
+ }
+
+
+// From MHTTPDataSupplier
+TBool COCSPTransportHttp::GetNextDataPart(TPtrC8& aDataPart)
+ {
+ aDataPart.Set(iOCSPRequest);
+ return ETrue; // This is the last chunk
+ }
+
+
+// From MHTTPDataSupplier
+void COCSPTransportHttp::ReleaseData()
+ {
+ // Nothing to do - from the OCSP side, ownership of the outgoing data is sorted already
+ }
+
+
+// From MHTTPDataSupplier
+TInt COCSPTransportHttp::OverallDataSize()
+ {
+ return iOCSPRequest.Length();
+ }
+
+
+// From MHTTPDataSupplier
+TInt COCSPTransportHttp::Reset()
+ {
+ return KErrNotSupported;
+ }
+
+// From MHTTPTransactionCallback
+void COCSPTransportHttp::MHFRunL(RHTTPTransaction aTransaction, const THTTPEvent& aEvent)
+ {
+ __ASSERT_ALWAYS(aTransaction == iHTTPTransaction, Panic(KErrArgument));
+
+ // Either ESucceeded or EFailed *will* happen
+ switch (aEvent.iStatus)
+ {
+ case THTTPEvent::EGotResponseHeaders:
+ ProcessHeadersL();
+ break;
+ case THTTPEvent::EGotResponseBodyData:
+ ProcessDataL();
+ break;
+ case THTTPEvent::EResponseComplete:
+ CheckDataCompleteL();
+ break;
+ case THTTPEvent::EFailed:
+ {
+ // Stop the timer
+ iTimer->Cancel();
+ TRequestStatus* reqstatus = &iStatus;
+ if (iServerFound)
+ {
+ User::RequestComplete(reqstatus, OCSP::KErrTransportFailure);
+ }
+ else
+ {
+ User::RequestComplete(reqstatus, OCSP::KErrServerNotFound);
+ }
+ }
+ break;
+ case THTTPEvent::ESucceeded:
+ {
+ // Stop the timer
+ iTimer->Cancel();
+ CheckDataCompleteL();
+ SetIAPL();
+ TRequestStatus* status = &iStatus;
+ User::RequestComplete(status, KErrNone);
+ }
+ break;
+ case KErrTimedOut:
+ // HTTP timed out - this really means it couldn't find the server mentioned in the url
+ // Remember this error so we pass it up correctly when we receive "THTTPEvent::EFailed" event
+ iServerFound = EFalse;
+ break;
+ default:
+ // Some other event (or error).
+ // Do nothing - HTTP module will cause EFailed or ESucceeded to occur
+ break;
+ }
+ }
+
+void COCSPTransportHttp::SetIAPL()
+ {
+ const RStringPool strPool = iHTTPSession().StringPool();
+ RHTTPConnectionInfo connectionInfo = iHTTPSession().ConnectionInfo();
+ THTTPHdrVal hdrVal;
+ if (connectionInfo.Property(strPool.StringF(HTTP::EHttpSocketConnection, RHTTPSession::GetTable()),
+ hdrVal))
+ {
+ RConnection* connection = reinterpret_cast<RConnection*>(hdrVal.Int());
+ _LIT(KIapId, "IAP\\Id");
+ User::LeaveIfError(connection->GetIntSetting(KIapId, iIap));
+ }
+ }
+
+
+// From MHTTPTransactionCallback
+TInt COCSPTransportHttp::MHFRunError(TInt aError, RHTTPTransaction aTransaction, const THTTPEvent& /*aEvent*/)
+ {
+ __ASSERT_ALWAYS(aTransaction == iHTTPTransaction, Panic(KErrArgument));
+
+ // Process error (otherwise, we get a panic)
+ AbortTransaction(aError);
+ return KErrNone;
+ }
+
+// Methods from MCallbackTimer
+void COCSPTransportHttp::TimerRun(TInt aError)
+ {
+ if (aError == KErrNone)
+ {
+ // Handle the timeout
+ iHTTPTransaction.Cancel();
+ TRequestStatus* status = &iStatus;
+ User::RequestComplete(status, OCSP::KErrTransportTimeout);
+ }
+ else
+ {
+ // Propagate the error upward
+ TRequestStatus* status = &iStatus;
+ User::RequestComplete(status, aError);
+ }
+ }
+
+// The headers have been received - check them out
+void COCSPTransportHttp::ProcessHeadersL()
+ {
+ RHTTPResponse response = iHTTPTransaction.Response();
+ RHTTPHeaders headers = response.GetHeaderCollection();
+ RStringPool stringPool = iHTTPSession().StringPool();
+
+ // Check content type
+ RStringF ocspResponse = stringPool.OpenFStringL(KOCSPContentTypeResponse);
+ CleanupClosePushL(ocspResponse);
+ RStringF contentTypeString = stringPool.StringF(HTTP::EContentType,RHTTPSession::GetTable());
+ THTTPHdrVal contentTypeVal;
+ TInt error = headers.GetField(contentTypeString, 0, contentTypeVal);
+ if (error != KErrNone
+ || contentTypeVal.Type() != THTTPHdrVal::KStrFVal
+ || contentTypeVal.StrF() != ocspResponse)
+ {
+ User::Leave(OCSP::KErrTransportFailure);
+ }
+ CleanupStack::PopAndDestroy(); // close ocspResponse
+
+ // Check content length - make descriptor ready to recieve data
+ RStringF contentLengthString = stringPool.StringF(HTTP::EContentLength,RHTTPSession::GetTable());
+ THTTPHdrVal contentLengthVal;
+ error = headers.GetField(contentLengthString, 0, contentLengthVal);
+ if (error == KErrNone
+ && contentLengthVal.Type() == THTTPHdrVal::KTIntVal)
+ {
+ // Make descriptor ready to receive response data
+ __ASSERT_ALWAYS(iResponseData == NULL, Panic(KErrAlreadyExists));
+ iResponseLength = contentLengthVal.Int();
+ iResponseData = HBufC8::NewL(iResponseLength);
+ }
+ else
+ {
+ // No Contents-Length field in headers, or wrong data type
+ User::Leave(OCSP::KErrTransportFailure);
+ }
+ }
+
+void COCSPTransportHttp::ProcessDataL()
+ {
+ if (iResponseData)
+ {
+ // Some data has come in - copy it into our descriptor
+ MHTTPDataSupplier* body = iHTTPTransaction.Response().Body();
+ if (!body)
+ {
+ User::Leave(OCSP::KErrTransportFailure);
+ }
+
+ TPtrC8 dataChunk;
+ TBool finished = body->GetNextDataPart(dataChunk);
+ if (iResponseLength - iResponseData->Length() >= dataChunk.Length())
+ {
+ iResponseData->Des().Append(dataChunk);
+ body->ReleaseData();
+ }
+ else
+ {
+ // Data is longer than Contents-Length header said it would be - error
+ body->ReleaseData();
+ User::Leave(OCSP::KErrTransportFailure);
+ }
+
+ if (finished)
+ {
+ CheckDataCompleteL();
+ }
+ }
+ }
+
+void COCSPTransportHttp::CheckDataCompleteL() const
+ {
+ if (iResponseLength != iResponseData->Length())
+ {
+ User::Leave(OCSP::KErrTransportFailure);
+ }
+ }
+
+void COCSPTransportHttp::Complete(TInt aError)
+ {
+ if (iCallBack)
+ {
+ // If something went wrong, remove the dodgy response data
+ if (aError != KErrNone)
+ {
+ delete iResponseData;
+ iResponseData = NULL;
+ }
+
+ // Sets iCallBack back to NULL, so we can only do this once
+ User::RequestComplete(iCallBack, aError);
+ }
+ }
+
+void COCSPTransportHttp::RunL()
+ {
+ User::LeaveIfError(iStatus.Int());
+ switch(iState)
+ {
+ case ETransportConnectingState:
+ iState = ETransportSendRequestState;
+ iStatus = KRequestPending;
+ DoSendRequestL(*iUri, *iRequest);
+ // Start the timer (if enabled)
+ if (iTimeout > KTimeoutDisabledValue)
+ {
+ iTimer->After(iTimeout * KMillSecsToMicroSecs);
+ }
+ SetActive();
+ break;
+
+ case ETransportSendRequestState:
+ // All OK
+ Complete(KErrNone);
+ break;
+ default:
+ User::Leave(KErrNotSupported);
+ break;
+ };
+ }
+
+TInt COCSPTransportHttp::RunError(TInt aError)
+ {
+ // If we failed in the transport connection state, return a
+ // transport error. Otherwise, just propogate the error recieved.
+ if (iState == ETransportConnectingState)
+ {
+ Complete(OCSP::KErrTransportFailure);
+ }
+ else
+ {
+ Complete(aError);
+ }
+ return KErrNone;
+ }
+
+void COCSPTransportHttp::DoCancel()
+ {
+ AbortTransaction(KErrCancel);
+ Complete(KErrCancel);
+ }
+
+void COCSPTransportHttp::AbortTransaction(TInt aError)
+ {
+ switch(iState)
+ {
+ case ETransportConnectingState:
+ iHTTPSession.CancelStart();
+ break;
+
+ case ETransportSendRequestState:
+ iTimer->Cancel();
+ iHTTPTransaction.Cancel();
+
+ //DEF101099 Fix - In a very rare situation after starting invocation of this DoCancel(), the asynchronous request
+ //is getting completed normally. In that case(iStatus!=KRequestPending) no need call this request complete codes,
+ //otherwise which leads to a "Stray Signal" and ending up in a Panic - E32USER-CBase 46.
+ if(iStatus == KRequestPending)
+ {
+ // Thanks to HTTP not exporting a callback for "DoCancel()" we complete the request here
+ TRequestStatus* status = &iStatus;
+ User::RequestComplete(status, aError);
+ }
+ break;
+ }
+ }
+