diff -r 000000000000 -r 164170e6151a pkiutilities/ocsp/transport/transporthttp.cpp --- /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 +#include +#include "securitypolicy.h" +#include "ocsppolicy.h" +#include "ocsptransport.h" +#include "panic.h" +#include +#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(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; + } + } +