--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mmsengine/mmshttptransport/src/mmstransaction.cpp Thu Dec 17 08:44:11 2009 +0200
@@ -0,0 +1,1264 @@
+/*
+* Copyright (c) 2002-2006 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:
+* Class implementing transaction within WAP session
+*
+*/
+
+
+
+// INCLUDE FILES
+#include <uriutils.h>
+#include <httpstringconstants.h>
+#include <uaproffilter_interface.h>
+
+#include "mmstransaction.h"
+#include "mmsservercommon.h"
+#include "mmsconst.h"
+#include "mmssession.h"
+#include "mmserrors.h"
+#include "MmsServerDebugLogging.h"
+#include "mmscodecdatasupplier.h"
+#include "mmscodecdatasink.h"
+
+
+// EXTERNAL FUNCTION PROTOTYPES
+extern void gPanic(TMmsPanic aPanic);
+
+// CONSTANTS
+const TInt KMmsMaxRetryCount = 4;
+// HTTP error ranges
+const TInt KMmsHTTP100 = 100; // 1xx informative
+const TInt KMmsHTTP200 = 200; //
+const TInt KMmsHTTP300 = 300; //
+const TInt KMmsHTTP400 = 400; //
+const TInt KMmsHTTP500 = 500; //
+const TInt KMmsHTTP404 = 404; // Not Found
+const TInt KMmsHTTP413 = 413; // Request entity too large
+
+const TInt KMmsReserve = 10000; // extra for buffer
+
+// MACROS
+// LOCAL CONSTANTS AND MACROS
+// MODULE DATA STRUCTURES
+// LOCAL FUNCTION PROTOTYPES
+// ==================== LOCAL FUNCTIONS ====================
+// ================= MEMBER FUNCTIONS =======================
+
+// ---------------------------------------------------------
+// CMmsTransaction
+// ---------------------------------------------------------
+//
+CMmsTransaction::CMmsTransaction()
+ : CMsgActive ( KMmsActiveObjectPriority ),
+ iState ( EMmsIdle ),
+ iUri ( NULL ),
+ iHTTPSession( NULL ),
+ iTimer( NULL ),
+ iTransferControl( NULL ),
+ iTransactionOpened( EFalse )
+ {
+ }
+
+// ---------------------------------------------------------
+// ConstructL
+// ---------------------------------------------------------
+//
+void CMmsTransaction::ConstructL()
+ {
+ iTimer = CMmsOperationTimer::NewL();
+ CActiveScheduler::Add( this );
+ }
+
+// ---------------------------------------------------------
+// NewL
+// ---------------------------------------------------------
+//
+CMmsTransaction* CMmsTransaction::NewL()
+ {
+ CMmsTransaction* self = new (ELeave) CMmsTransaction();
+ CleanupStack::PushL( self );
+ self->ConstructL();
+ CleanupStack::Pop( self );
+ return self;
+ }
+
+
+// ---------------------------------------------------------
+// ~CMmsTransaction
+// ---------------------------------------------------------
+//
+CMmsTransaction::~CMmsTransaction()
+ {
+ Cancel();
+ if ( iTransactionOpened )
+ {
+ RHTTPHeaders hdr = iTransaction.Request().GetHeaderCollection();
+ hdr.RemoveAllFields(); // clean up
+ iTransaction.Close();
+ }
+ delete iUri;
+ delete iTimer;
+ }
+
+// ---------------------------------------------------------
+// ExecuteL
+// ---------------------------------------------------------
+//
+void CMmsTransaction::ExecuteL(
+ RHTTPSession& aSession,
+ CMmsBearerStatus& aTransferControl,
+ const TDesC& aUri,
+ const TInt aMethod,
+ CBufFlat& aMessageBuffer,
+ const TInt aTransactionTimer,
+ TInt32 aMaxReceiveSize,
+ TInt32 aExpectedReceiveSize,
+ MMmsCodecDataSupplier& aDataSupplier,
+ MMmsCodecDataSink& aDataSink,
+ TRequestStatus& aStatus )
+ {
+ LOG( _L("CMmsTransaction::ExecuteL") );
+ __ASSERT_DEBUG( iState == EMmsIdle, gPanic( EMmsAlreadyBusy ) );
+
+ iError = KErrNone;
+
+ iHTTPSession = &aSession;
+ iDataSupplier = &aDataSupplier;
+ iDataSink = &aDataSink;
+
+ iMaxReceiveSize = aMaxReceiveSize;
+ if( aMethod == HTTP::EGET )
+ {
+ //
+ // Setting adequate receivebuffer size for receive transactions
+ //
+ TInt32 reserve = KMmsReserve; // fixed reserve for unexpected surplus data
+ if ( aExpectedReceiveSize == KMmsChunkedBufferSize )
+ {
+ // we don't need extra when receiving in chunked mode
+ reserve = 0;
+ }
+ if( ( aExpectedReceiveSize + reserve ) > aMaxReceiveSize )
+ {
+ iExpectedReceiveSize = aMaxReceiveSize;
+ }
+ else
+ {
+ iExpectedReceiveSize = aExpectedReceiveSize + reserve;
+ }
+ }
+ iTransactionOpened = EFalse;
+
+ delete iUri;
+ iUri = NULL;
+ HBufC* newUri = aUri.AllocL();
+ iUri = newUri;
+
+ iBuffer = &aMessageBuffer;
+ iMethod = aMethod;
+
+ iTransactionTimeout = aTransactionTimer;
+ iTransferControl = &aTransferControl;
+
+ // clear the flags, because we are reusing the class,
+ // we don't create a new instance every time
+ iRetryCount = 0;
+ iRequestOngoing = EFalse;
+ iSuspendOccurred = EFalse;
+ iGotBodyData = EFalse;
+ iDataChunkNumber = 0; // no data obtained yet
+ iCumulativeSize = 0; // received size or sent size, cumulative
+ iChunkSize = 0;
+ iEvent = THTTPEvent::ESubmit;
+
+ Queue( aStatus ); // aStatus = iStatus = KRequestPending
+
+ //
+ // Start transaction timer
+ //
+ if ( iTimer->IsActive() )
+ {
+ iTimer->Cancel();
+ }
+ iTimer->Start( (MMmsTransportObserver*) this, iTransactionTimeout );
+
+ //
+ // If bearer is already blocked when starting, execution enters a wait
+ //
+ if( iTransferControl->IsSuspended() )
+ {
+ LOG( _L(" - GPRS is Suspended -> waiting for resume before sending") );
+
+ // Transfer cancellation
+ iTransferControl->SubscribeNotification( (MMmsTransportObserver*) this );
+ // Start needed operations in suspend
+ GprsSuspended();
+
+ // someone must set our status to KRequestPending
+ iStatus = KRequestPending;
+
+#ifndef _NO_MMSS_LOGGING_
+ if ( IsActive() )
+ {
+ LOG( _L(" - already active") );
+ }
+#endif //_NO_MMSS_LOGGING_
+ SetActive();
+ }
+ else // Bearer not blocked, continuing directly
+ {
+ iStatus = KRequestPending;
+ SetActive();
+ TRequestStatus* status = &iStatus;
+ User::RequestComplete( status, KErrNone );
+ }
+ }
+
+
+// ---------------------------------------------------------
+// RunL
+// ---------------------------------------------------------
+//
+void CMmsTransaction::RunL()
+ {
+ // DoRunL() takes the AO through the states, queuing another
+ // asynch step as required.
+ // if DoRunL() detects the end of the cycle it returns
+ // without queuing another cycle.
+
+ // If Run() would exit without queuing another cycle it reports
+ // completion to the client.
+ // In CMsgActive this is true if the asynch step or DoRunL fails,
+ // or the state cycle is complete
+ // However, we must keep our cycle going in spite of error(s).
+
+ // The CMmsOperationTimer reports the exceed situation with error
+ // This must be handled in CMmsTransaction's DoRunL before returning to
+ // client
+
+ // Also the bearer suspend/resume status is informed via error
+ // Also handled in CMmsTransaction.
+
+ // If we have completed ourselves, the error is already in iError.
+ // However, if an asynchronous operation completed us, the error
+ // comes back to us in iStatus.
+ // As we always complete ourselves by returning iError as the
+ // status, we get back either our original iError, or a new
+ // error value set by an asynchronous operation.
+
+ TInt status = iStatus.Int();
+
+ if ( iError == KErrNone )
+ {
+ iError = status;
+ }
+
+ // Only cancel can exit.
+ if ( status != KErrCancel || iError != KErrNone )
+ {
+ // iError contains the possible error from previous step.
+
+ TRAPD( error, DoRunL() ); // continue operations, may re-queue
+ // must not requeue in error situations
+ // If we want to continue the loop in spite of error (yes, sir),
+ // we must not leave, but return the error in iError.
+ // Symbian code may leave, so we must trap the leaves here,
+ // as we want to keep the loop going unless something
+ // is really fatally wrong (we are completely out of memory,
+ // or the system has crashed)
+ __ASSERT_DEBUG( error == KErrNone || !IsActive(), User::Invariant() );
+ // active means AO has been requeued, just return
+ if ( IsActive() )
+ {
+ return;
+ }
+
+ status = error;
+ if ( error == KErrNone )
+ {
+ // If DoRunL did not leave, possible error is in iError
+ status = iError;
+ }
+ }
+ LOG2( _L("CMmsTransaction::RunL: Complete with %d"), status );
+ Complete( status );
+ }
+
+// ---------------------------------------------------------
+// DoRunL
+// ---------------------------------------------------------
+//
+void CMmsTransaction::DoRunL()
+ {
+ //
+ // This routine takes the state machine through the states
+ // until an error is encountered or the cycle completes.
+ //
+ LOG3( _L("CMmsTransaction::DoRunL status %d, iError %d"), iStatus.Int(), iError );
+ // If we got last chunk already, do not cancel, let HTTP complete
+ if ( iError != KErrNone && iError != KMmsErrorBearerSuspended )
+ {
+ if ( iRequestOngoing )
+ {
+ iTransaction.Cancel();
+ iRequestOngoing = EFalse;
+ }
+ // Error code copied to status code
+ iStatus = iError;
+ }
+
+ // We continue to next state to finish properly
+ SelectNextState();
+ // If appropriate, ChangeState makes us active again
+ ChangeStateL();
+ }
+
+// ---------------------------------------------------------
+// SelectNextState
+// ---------------------------------------------------------
+//
+void CMmsTransaction::SelectNextState()
+ {
+ //
+ // The purpose of the switch statements is to cycle through
+ // states. These states handle certain task(s) and after
+ // completed CMmsTransaction is finished.
+ //
+ switch ( iState )
+ {
+ case EMmsIdle:
+ // start the loop
+ iState = EMmsSendingTransactionRequest;
+ break;
+ case EMmsSendingTransactionRequest:
+ iState = EMmsFinished;
+ break;
+ case EMmsFinished:
+ // No more states
+ iError = KErrUnknown;
+ break;
+ default:
+ // Illegal state, we should never get here
+ iError = KErrUnknown;
+ break;
+ }
+ }
+
+// ---------------------------------------------------------
+// ChangeStateL
+// ---------------------------------------------------------
+//
+void CMmsTransaction::ChangeStateL()
+ {
+ switch ( iState )
+ {
+ case EMmsSendingTransactionRequest:
+ MakeTransactionL();
+ break;
+
+ case EMmsFinished:
+ FinishL();
+ break;
+
+ default:
+ // All other states are illegal
+ LOG( _L("CMmsTransaction::ChangeStateL(): ERROR: Illegal state") );
+ iError = KErrUnknown;
+ break;
+ }
+ }
+
+// ---------------------------------------------------------
+// MakeTransactionL
+// ---------------------------------------------------------
+//
+void CMmsTransaction::MakeTransactionL()
+ {
+ LOG( _L("CMmsTransaction::MakeTransactionL") );
+
+ // Useragent has been added to session headers:
+ // It should always be present
+ HBufC8* uri = HBufC8::NewL( iUri->Length() );
+ CleanupStack::PushL( uri );
+ uri->Des().Copy( iUri->Des() );
+ TUriParser8 uri2;
+ uri2.Parse( uri->Des() );
+
+ RStringPool stringPool = iHTTPSession->StringPool();
+
+ iTransaction = iHTTPSession->OpenTransactionL( uri2,
+ *this,
+ stringPool.StringF( iMethod, RHTTPSession::GetTable() ) );
+
+ iTransactionOpened = ETrue;
+ CleanupStack::PopAndDestroy( uri );
+
+ // Set headers. If GET, no content type needed as no content present
+ // common accept headers are set as session headers
+ // these are done only on the first time. In retry cases, they already exist.
+ RHTTPHeaders hdr = iTransaction.Request().GetHeaderCollection();
+
+ // Adding accept headers has been moved to transaction,
+ // as automatic filters seemed to override session headers
+ // (session headers are not added if transaction headers already exist)
+
+ // NOTE: Currently Accept header contains the following line on purpose:
+ // "Accept: */*, application/vnd.wap.mms-message, application/vnd.wap.sic"
+ // in order to be compliant with some server vendors.
+
+ // Accept: "*/*"
+ SetHeaderL( hdr, HTTP::EAccept, KMmsAny );
+ // Accept: "application/vnd.wap.mms-message"
+ SetHeaderL( hdr, HTTP::EAccept, KMmsMessageContentType );
+ // Accept: "application/vnd.wap.sic"
+ SetHeaderL( hdr, HTTP::EAccept, KMmsWapSicContentType );
+
+ // Accept-Language: en
+ SetHeaderL( hdr, HTTP::EAcceptLanguage, KHTTPEnglish );
+
+ // Accept-Charset: utf-8
+ SetHeaderL( hdr, HTTP::EAcceptCharset, KHTTPUtf8 );
+ // end of headers moved from session to transaction
+
+ if( iMethod == HTTP::EPOST )
+ {
+ // Add contenttype header
+ SetHeaderL( hdr, HTTP::EContentType, KMmsMessageContentType );
+
+ // Give payload supplier for stack in case of http post
+ iTransaction.Request().SetBody( *this );
+ }
+
+ // Set the 'Host:' header
+ SetHostHeaderL( hdr );
+
+ // Set UA headers
+ RStringF uaClient = iHTTPSession->StringPool().OpenFStringL( KUserAgentClientId );
+ CleanupClosePushL<RStringF>( uaClient );
+ iTransaction.PropertySet().SetPropertyL( uaClient, THTTPHdrVal( KUserAgentCliMMS ) );
+ CleanupStack::PopAndDestroy( &uaClient );
+
+ // no response headers yet
+ iGotResponseHeaders = EFalse;
+
+ // Perform the actual sending
+ iTransaction.SubmitL();
+
+ // Start observer to monitor notifications
+ iTransferControl->SubscribeNotification( (MMmsTransportObserver*) this );
+
+ iRequestOngoing = ETrue;
+
+ iStatus = KRequestPending;
+ SetActive();
+ }
+
+// ---------------------------------------------------------
+// FinishL
+// ---------------------------------------------------------
+//
+void CMmsTransaction::FinishL()
+ {
+ LOG( _L("CMmsTransaction::FinishL") );
+
+ //
+ // Cancel timer and observer
+ //
+ iTransferControl->Cancel();
+
+ //
+ // Completion came from own timers
+ //
+ if( iStatus == KMmsErrorTimeout || iStatus == KMmsErrorSuspendTimeout )
+ {
+ LOG( _L(" - MmsEngine's timer expired") );
+ if( iRequestOngoing )
+ {
+ iTransaction.Cancel();
+ }
+
+ if( iGotBodyData && iLastChunk )
+ {
+ LOG( _L(" - Data has been received, returning OK") );
+ iError = KErrNone;
+ }
+ else // no body data, emptying
+ {
+ iBuffer->Reset();
+ }
+ return;
+ }
+
+ //
+ // Pause the timer (later it will be put back on, or cancelled)
+ //
+ iTimer->Pause();
+
+ //
+ // Completion came from HTTP stack
+ // go through switch based on event status
+ //
+ switch ( iEvent.iStatus )
+ {
+ // Successfull case
+ case THTTPEvent::ESucceeded:
+ {
+ iRetryCount = 0;
+ iSuspendOccurred = EFalse;
+ iError = KErrNone; // successful.
+ LOG( _L(" - HTTP status OK") );
+ // If there was response data, we've already got it
+ break;
+ }
+
+ // Failure cases
+ case THTTPEvent::EFailed:
+ {
+ LOG2( _L(" - HTTP failed with eventcode %d"), iEvent.iStatus );
+
+ //
+ // Check for suspend cases:
+ // Errors due to GPRS suspend are handled either with immediate retry
+ // or immediatedly after suspend has resumed. Anyway, attempt is not given up.
+ //
+ if( ( iTransferControl->IsSuspended() || iSuspendOccurred ) &&
+ !iGotResponseHeaders && iRetryCount < KMmsMaxRetryCount )
+ {
+ // If we haven't got any response headers yet, no need to reset the
+ // data sink as it has not been given any data yet
+ iRetryCount++;
+ iTransaction.Close();
+ iTransactionOpened = EFalse; // closed already
+ iRequestOngoing = EFalse;
+ iState = EMmsIdle;
+ iError = KErrNone;
+ iSuspendOccurred = EFalse;
+
+ if( !iTransferControl->IsSuspended() ) // i.e. iSuspendOccurred
+ {
+ LOG( _L(" - Suspend has occurred, but has resumed -> trying again immediatedly!") );
+ TRequestStatus* status = &iStatus;
+ User::RequestComplete( status, KErrNone );
+ iTimer->Continue();
+ SetActive();
+ return;
+ }
+ else // GPRS is now suspended
+ {
+ LOG( _L(" - GPRS is now Suspended -> waiting for resume before retry.") );
+ // Subscribe notifications from transferControl watcher
+ // Also starting timer
+ iTransferControl->SubscribeNotification( (MMmsTransportObserver*) this );
+ iTimer->Continue();
+
+ // Not completing here.. it will be done by the observer or the timer
+ iStatus = KRequestPending;
+ SetActive();
+ return;
+ }
+ }
+
+ //
+ // For some reason connection to network has been cut off after the first transaction.
+ // The following code tries immediate resend for the transaction if this has been the case.
+ //
+ if( iStatus == KErrDisconnected // IF connection has been cut off
+ && iRetryCount < 1 ) // AND no retries have been tried yet
+ {
+ LOG( _L(" - Connection has been disconnected, re-submitting.") );
+
+ iRetryCount++;
+ iState = EMmsSendingTransactionRequest;
+
+ iCumulativeSize = 0;
+ // reset data sink in case we got partial data.
+ // If the data sink is not in chunked mode, this does nothing
+ iDataSink->ResetDataSink();
+ // Just re-submitting the transaction
+ iTransaction.SubmitL();
+
+ iRequestOngoing = ETrue;
+ iError = KErrNone;
+
+ // Start timer running
+ iTimer->Continue();
+ // start bearer observer
+ iTransferControl->SubscribeNotification( (MMmsTransportObserver*) this );
+
+ iStatus = KRequestPending;
+ SetActive();
+ return;
+ }
+
+ //
+ // Timer can now be cancelled
+ //
+ iTimer->Cancel();
+
+ RHTTPResponse resp = iTransaction.Response();
+ LOG2( _L(" - HTTP status code %d "), resp.StatusCode() );
+
+ TInt error = KErrNone;
+ // crude mapping only based on first number
+ const TInt KMms100 = 100;
+ switch ( ( resp.StatusCode() / KMms100 ) * KMms100 )
+ {
+ case KMmsHTTP100: // 1xx Informative
+ // We map "informative" to KErrNone.
+ // Further analysis is needed to tell if there was a real error or not
+ // Decoding of response from MMSC will determine the final error.
+ error = KErrNone;
+ break;
+ case KMmsHTTP200: // 2xx OK
+ error = KErrNone;
+ break;
+ case KMmsHTTP300: // 3xx Multiple choices
+ error = KMmsErrorHTTPConfiguration;
+ break;
+ case KMmsHTTP400: // 4xx Client error
+ if( resp.StatusCode() == KMmsHTTP404 )
+ {
+ error = KMmsErrorHTTPNotFound;
+ }
+ else if ( resp.StatusCode() == KMmsHTTP413 )
+ {
+ error = KMmsErrorEMRUExceeded;
+ }
+ else
+ {
+ error = KMmsErrorHTTPClientError;
+ }
+ break;
+ case KMmsHTTP500: // 5xx Server error
+ error = KMmsErrorHTTPServerDown;
+ break;
+ default:
+ error = KErrUnknown;
+ break;
+ }
+
+ // If we get clear HTTP error code, we use it.
+ // Usually iError is KErrUnknown at this point if we have got an error event.
+ if ( error != KErrNone )
+ {
+ // If we have some previous error, KErrUnknown does not override
+ if ( error != KErrUnknown || iError == KErrNone )
+ {
+ iError = error;
+ }
+ }
+
+ TBool mmsMessage = EFalse; // check if response data is of correct type
+ if( iGotResponseHeaders )
+ {
+ RHTTPHeaders hdr = resp.GetHeaderCollection();
+ RStringPool stringPool = iHTTPSession->StringPool();
+ RStringF contentType = stringPool.StringF(
+ HTTP::EContentType, RHTTPSession::GetTable() );
+ THTTPHdrVal fieldVal;
+ if( hdr.GetField( contentType, 0, fieldVal ) == KErrNone )
+ {
+ // content type must match with KMmsMessageContentType
+ RStringF valStr = stringPool.OpenFStringL( KMmsMessageContentType );
+ CleanupClosePushL( valStr );
+ if ( valStr == fieldVal.StrF() )
+ {
+ mmsMessage = ETrue;
+ }
+ CleanupStack::PopAndDestroy( &valStr );
+ }
+ }
+
+ // If we got body data, we clear error in order to make
+ // mms server mtm to decode what we got.
+ // - But only if the content type really was "application/vnd.wap.mms-message"
+ // It is possible that the message is incomplete, but in some cases
+ // HTTP stack is not sure if it got all the data or not, so we must check
+ // if what we got can be decoded without problems.
+ // Because chunked decoding would cause problems otherwise,
+ // We can declare "no error" only if we got the last chunk
+ if( iGotBodyData && mmsMessage && iLastChunk )
+ {
+ iError = KErrNone;
+ }
+ break;
+ }
+
+ default:
+ LOG( _L("CMmsTransaction::FinishL(): Unknown response") );
+ // Other event = error
+ // - but don't override the error if we already got something.
+ if ( iError == KErrNone )
+ {
+ iError = KMmsErrorUnknownRespFromGw;
+ }
+ break;
+ }
+
+ iSuspendOccurred = EFalse;
+ iRequestOngoing = EFalse;
+ iStatus = iError;
+ iState = EMmsIdle;
+ }
+
+// ---------------------------------------------------------
+// DoCancel
+// ---------------------------------------------------------
+//
+void CMmsTransaction::DoCancel()
+ {
+ LOG( _L("CMmsTransaction::DoCancel") );
+ if ( iRequestOngoing )
+ {
+ iTransaction.Cancel();
+ iRequestOngoing = EFalse;
+ }
+ iTimer->Cancel();
+ iTransferControl->Cancel();
+ iState = EMmsIdle;
+
+ // Complete ourselves as no-one else is going to do it
+ TRequestStatus* status = &iStatus;
+ User::RequestComplete( status, KErrCancel );
+
+ // This completes the caller (session)
+ CMsgActive::DoCancel();
+ }
+
+// ---------------------------------------------------------
+// DoComplete
+// ---------------------------------------------------------
+//
+void CMmsTransaction::DoComplete( TInt& )
+ {
+ // just to be sure that nothing is left active, cancel
+ // all operations that may still be pending.
+ LOG( _L("CMmsTransaction::DoComplete") );
+
+ iTimer->Cancel();
+ iTransferControl->Cancel();
+
+ // transaction is automatically cancelled during close
+ if ( iTransactionOpened )
+ {
+ RHTTPHeaders hdr = iTransaction.Request().GetHeaderCollection();
+ hdr.RemoveAllFields(); // clean up
+ iTransaction.Close();
+ iTransactionOpened = EFalse; // closed already
+ }
+ if ( iDataSink )
+ {
+ // release data sink does nothing if there is nothing to release
+ // it is safe to call it.
+ iDataSink->RelaseDataSink();
+ iDataSink = NULL;
+ }
+
+ // in case we have run out of memory or code leaves for some other
+ // fatal reason, we make sure we are idle for the next loop.
+ iState = EMmsIdle;
+ }
+
+// ---------------------------------------------------------
+// TimerExpired
+// ---------------------------------------------------------
+//
+void CMmsTransaction::TimerExpired()
+ {
+ LOG( _L("CMmsTransaction::TimerExpired") );
+ // No transfer cancellation any more
+ iTransferControl->Cancel();
+
+ if( !IsActive() )
+ {
+ iStatus = KRequestPending;
+ SetActive();
+ }
+
+ // Setting the state so that we end up to FinishL() method
+ iState = EMmsSendingTransactionRequest;
+
+ // We complete with MMS Engine's own Timeout value in order
+ // to avoid confusion with timeouts coming from lower layers
+ TRequestStatus* status = &iStatus;
+ User::RequestComplete( status, KMmsErrorTimeout );
+
+ iRetryCount = 0;
+ }
+
+// ---------------------------------------------------------
+// GprsSuspended
+// ---------------------------------------------------------
+//
+void CMmsTransaction::GprsSuspended()
+ {
+ LOG( _L("CMmsTransaction::GprsSuspended") );
+ }
+
+// ---------------------------------------------------------
+// GprsResumed
+// ---------------------------------------------------------
+//
+void CMmsTransaction::GprsResumed()
+ {
+ //
+ // GPRS context has resumed
+ //
+ LOG( _L("CMmsTransaction::GprsResumed") );
+ if( iRequestOngoing )
+ {
+ // Set flag indicating that gprs was suspended at some stage
+ iSuspendOccurred = ETrue;
+ iTransferControl->SubscribeNotification( (MMmsTransportObserver*) this );
+ }
+ else
+ {
+ // No request ongoing yet, start state machine by completing ourselves
+ iTransferControl->Cancel();
+ TRequestStatus* status = &iStatus;
+ User::RequestComplete( status, KErrNone );
+ }
+ }
+
+// ---------------------------------------------------------
+// TransferCancelled
+// ---------------------------------------------------------
+//
+void CMmsTransaction::TransferCancelled()
+ {
+ LOG( _L("CMmsTransaction::TransferCancelled") );
+ iTimer->Cancel();
+
+ // It is possible that this method is called when there is no
+ // transaction going i.e. object is not active.
+ // It means that in that case we have complete ourselves
+ if( !IsActive() )
+ {
+ iStatus = KRequestPending;
+ SetActive();
+ TRequestStatus* status = &iStatus;
+ User::RequestComplete( status, KMmsErrorTransferCancelled );
+ }
+ else
+ {
+ // cancel getting events from the stack
+ if ( iRequestOngoing )
+ {
+ iTransaction.Cancel();
+ iRequestOngoing = EFalse;
+ }
+ iError = KMmsErrorTransferCancelled;
+ TRequestStatus* status = &iStatus;
+ User::RequestComplete( status, KMmsErrorTransferCancelled );
+ }
+ }
+
+// ---------------------------------------------------------
+// SetHeaderL
+// ---------------------------------------------------------
+//
+void CMmsTransaction::SetHeaderL( RHTTPHeaders aHeaders, TInt aHdrField, const TDesC8& aHdrValue )
+ {
+ RStringF valStr = iHTTPSession->StringPool().OpenFStringL( aHdrValue );
+ THTTPHdrVal val( valStr );
+ CleanupClosePushL( valStr );
+ aHeaders.SetFieldL(
+ iHTTPSession->StringPool().StringF( aHdrField, RHTTPSession::GetTable() ), val );
+ CleanupStack::PopAndDestroy( &valStr );
+ }
+
+// ---------------------------------------------------------
+// SetHostHeaderL
+// ---------------------------------------------------------
+//
+void CMmsTransaction::SetHostHeaderL( RHTTPHeaders aHeaders )
+ {
+ LOG( _L("CMmsTransaction::SetHostHeaderL()") );
+ //
+ // Get needed URI parts
+ //
+ TUriC8 uri3 = iTransaction.Request().URI();
+ TPtrC8 hostPtr = uri3.Extract(EUriHost);
+ TPtrC8 portPtr = uri3.Extract(EUriPort);
+
+ // If empty, nothing can be done
+ if( hostPtr.Length() == 0 )
+ {
+ return;
+ }
+
+ //
+ // If host-type == IPv6 -> include '[' and ']' characters to URI
+ //
+ UriUtils::TUriHostType hosttype = UriUtils::HostType( hostPtr );
+ if( hosttype == UriUtils::EIPv6Host )
+ {
+ LOG( _L(" - host type == IPv6") );
+ const TInt KMmsRoomForBrackets = 2;
+ hostPtr.Set( hostPtr.Ptr()-1, hostPtr.Length() + KMmsRoomForBrackets );
+ }
+
+ //
+ // If port not equal to 80 -> include it
+ //
+ _LIT8( KMmsPort, "80" );
+ if ( portPtr.Length() > 0 && portPtr.Compare( KMmsPort ) )
+ {
+ hostPtr.Set( hostPtr.Ptr(), hostPtr.Length() + 1 + portPtr.Length() );
+ }
+
+ //
+ // Remove possible old host-header and set new one
+ //
+ aHeaders.RemoveField( iHTTPSession->StringPool()
+ .StringF( HTTP::EHost, RHTTPSession::GetTable() ) );
+ SetHeaderL( aHeaders, HTTP::EHost, hostPtr );
+ }
+
+// ---------------------------------------------------------
+// MHFRunL
+// ---------------------------------------------------------
+//
+void CMmsTransaction::MHFRunL( RHTTPTransaction aTransaction, const THTTPEvent& aEvent )
+ {
+ TRequestStatus* status = &iStatus;
+ iEvent = aEvent;
+
+ // Switch through the events
+ switch (aEvent.iStatus)
+ {
+ case THTTPEvent::EGotResponseHeaders:
+ LOG( _L("CMmsTransaction::MHFRunL: Got response headers") );
+ {
+ // Clear buffer but do not compress
+ // We get the data in small chunks, and reallocating all the time
+ // may make us to run out of memory too soon.
+ iBuffer->Delete( 0, iBuffer->Size() );
+ iBuffer->ResizeL( 0 );
+ iGotResponseHeaders = ETrue; // Got response headers, can check content type
+ iDataChunkNumber = 0; // reset in case of retry
+ }
+ break;
+
+ case THTTPEvent::EGotResponseBodyData:
+ {
+ LOG( _L("CMmsTransaction::MHFRunL: Got response body data") );
+ // Get the data
+ TPtrC8 bodyData;
+ MHTTPDataSupplier* dataSupplier = aTransaction.Response().Body();
+ iLastChunk = EFalse;
+ iLastChunk = dataSupplier->GetNextDataPart( bodyData );
+
+ // We must remove CR and LF characters from the beginning of
+ // the first data chunk representing message body because some
+ // network proxies seem to add an extra CR-LF between headers and body.
+ // (A well formed MMS PDU always begins with 0x8C, so that we
+ // never lose anything from a well formed PDU, and if the PDU
+ // is not well formed, it is a mess anyway.)
+ if ( iDataChunkNumber == 0 )
+ {
+ iPosition = 0;
+ iCumulativeSize = 0; // first data chunk, start counting from here
+ TInt overallDataSize = dataSupplier->OverallDataSize();
+#ifndef _NO_MMSS_LOGGING_
+ if ( iMethod == HTTP::EGET && iMaxReceiveSize != 0)
+ {
+ LOG2( _L("- can accept only %d bytes"), iMaxReceiveSize );
+ }
+ if ( overallDataSize != KErrNotFound )
+ {
+ LOG2( _L("- going to get at least %d bytes"), overallDataSize );
+ }
+ else
+ {
+ LOG( _L("- chunked transfer encoding used") );
+ }
+#endif //_NO_MMSS_LOGGING_
+ TInt removedChars = 0;
+ while ( bodyData.Find( KCr8 ) == 0 || bodyData.Find( KLf8 ) == 0 )
+ {
+#ifndef _NO_MMSS_LOGGING_
+ if ( bodyData.Find( KCr8 ) == 0 )
+ {
+ LOG( _L("- removed CR") );
+ }
+ else
+ {
+ LOG( _L("- removed LF") );
+ }
+#endif //_NO_MMSS_LOGGING_
+ // remove first character
+ bodyData.Set( bodyData.Mid( 1 ) );
+ removedChars++;
+ }
+
+ //
+ // Check if we are about to receive too much data
+ //
+ if ( iMethod == HTTP::EGET &&
+ iMaxReceiveSize != 0 &&
+ overallDataSize != KErrNotFound &&
+ overallDataSize - removedChars > iMaxReceiveSize )
+ {
+ // The overall data size is more than what we want
+ iError = KMmsErrorEMRUExceeded;
+ }
+
+ //
+ // Size buffer to max message size
+ //
+ if ( iMethod == HTTP::EGET && iError != KMmsErrorEMRUExceeded )
+ {
+ LOG2( _L("- reserving %d bytes for receiving."), iExpectedReceiveSize );
+ iBuffer->ResizeL( iExpectedReceiveSize );
+ iBuffer->ResizeL( 0 );
+ }
+ } // (iDataChunkNumber == 0) block
+
+ //
+ // Add the chunk number counter every time body data is received
+ //
+ iDataChunkNumber++;
+
+ // When sending MMS, message size is not checked
+ // When receiving MMS, size is checked against iMaxReceiveSize
+ // If iMaxReceiveSize == 0 -> no check
+ // If size > iMaxReceiveSize: "KMmsErrorEMRUExceeded"
+ iCumulativeSize += bodyData.Length();
+ if( iMethod == HTTP::EGET &&
+ iMaxReceiveSize > 0 &&
+ iCumulativeSize > iMaxReceiveSize )
+ {
+ iError = KMmsErrorEMRUExceeded;
+ }
+
+ //
+ // Check if we are getting too much data (compared to iMaxReceiveSize)
+ //
+ if ( iError == KMmsErrorEMRUExceeded )
+ {
+ // Release data and cancel transaction
+ // The following two statements have to be executed in this order
+ dataSupplier->ReleaseData();
+ aTransaction.Cancel();
+
+ // we say we did not get anything even if we might have fetched
+ // part of the data
+ iGotBodyData = EFalse;
+ iDataSink->RelaseDataSink();
+
+ // transaction was cancelled -> not getting any more events:
+ User::RequestComplete( status, iError );
+ }
+ else // (iError not equal to KMmsErrorEMRUExceeded)
+ {
+ iBuffer->ResizeL( bodyData.Length() + iPosition );
+ TInt currentData = bodyData.Length();
+ iBuffer->Write( iPosition, bodyData, currentData );
+ // release the databuffer
+ dataSupplier->ReleaseData();
+#ifndef _NO_MMSS_LOGGING_
+ LOG3( _L("- chunk size %d (cumulative %d)"), currentData, iCumulativeSize );
+ if ( iLastChunk )
+ {
+ LOG( _L("- last data chunk received!") );
+ }
+#endif //_NO_MMSS_LOGGING_
+
+ iPosition = 0;
+/////
+ iError = iDataSink->NextDataPart( *iBuffer, iPosition, iLastChunk );
+////
+ // if something goes wrong when saving the data, the transaction should be
+ // cancelled
+ // But if we got last chunk already, let the HTTP complete normally
+ if ( iError != KErrNone && !iLastChunk )
+ {
+ // We can only get an error here if we are in chunked mode
+ // cancel transaction
+ aTransaction.Cancel();
+ iDataSink->RelaseDataSink();
+
+ // transaction was cancelled -> not getting any more events:
+ User::RequestComplete( status, iError );
+ return;
+ }
+
+ TInt amount = iBuffer->Size() - iPosition;
+ if ( iPosition != 0 )
+ {
+ // some data handled
+ iBuffer->Delete( 0, iPosition );
+ }
+ iBuffer->ResizeL( amount );
+ iPosition = amount; // continue writing from here
+
+ if ( iLastChunk )
+ {
+ iDataSink->RelaseDataSink();
+ }
+
+ // If we get at least some body data, we continue to decode it even if
+ // it might not be complete.
+ // The HTTP session may disconnect too soon, and HTTP stack may report error
+ // even if we already got all our body data.
+ iGotBodyData = ETrue;
+ }
+ }
+ break;
+ case THTTPEvent::EResponseComplete:
+ {
+ LOG( _L("CMmsTransaction::MHFRunL: response complete") );
+ // never mind, ESucceeded or EFailed will follow
+ break;
+ }
+
+ case THTTPEvent::ESucceeded:
+ {
+ LOG( _L("CMmsTransaction::MHFRunL: Succeeded") );
+ LOG2( _L("- Got %d data chunks"), iDataChunkNumber );
+ User::RequestComplete( status, KErrNone );
+ break;
+ }
+
+ case THTTPEvent::EFailed:
+ {
+ LOG( _L("CMmsTransaction::MHFRunL: Failed") );
+ LOG2( _L("- Got %d data chunks"), iDataChunkNumber );
+ if ( iError == KErrNone )
+ {
+ iError = KErrUnknown;
+ }
+ User::RequestComplete( status, iError );
+ break;
+ }
+
+ default:
+ {
+ LOG2( _L("CMmsTransaction::MHFRunL: unknown event %d"), aEvent.iStatus );
+ // save the error, but don't override a possible earlier error
+ if ( aEvent.iStatus < 0 && iError == KErrNone )
+ {
+ iError = aEvent.iStatus;
+ }
+ break;
+ }
+ }
+ }
+
+// ---------------------------------------------------------
+// MHFRunError
+// ---------------------------------------------------------
+//
+TInt CMmsTransaction::MHFRunError(
+ TInt aError,
+ RHTTPTransaction aTransaction,
+ const THTTPEvent& aEvent )
+ {
+ LOG2( _L("CMmsTransaction::MHFRunError: %d"), aError );
+ iEvent = aEvent;
+ iError = aError;
+ aTransaction.Close();
+ iTransactionOpened = EFalse;
+ iDataSink->RelaseDataSink();
+ TRequestStatus* status = &iStatus;
+ User::RequestComplete( status, aEvent.iStatus );
+ return KErrNone;
+ }
+
+// ---------------------------------------------------------
+// GetNextDataPart
+// ---------------------------------------------------------
+//
+TBool CMmsTransaction::GetNextDataPart( TPtrC8& aDataPart )
+ {
+ iLastChunk = EFalse;
+ iError = iDataSupplier->GetNextDataPart( aDataPart, iLastChunk );
+ iChunkSize = aDataPart.Size();
+ LOG2( _L("CMmsTransaction::GetNextDataPart, Chunk size %d"), iChunkSize );
+#ifndef _NO_MMSS_LOGGING_
+ if ( iLastChunk )
+ {
+ LOG( _L("CMmsTransaction:: last chunk") );
+ }
+#endif //_NO_MMSS_LOGGING_
+
+
+ if ( iError != KErrNone )
+ {
+ // could not get data - cancel the transaction
+ iTransaction.Cancel();
+ }
+
+ return iLastChunk;
+ }
+
+// ---------------------------------------------------------
+// ReleaseData
+// ---------------------------------------------------------
+//
+void CMmsTransaction::ReleaseData()
+ {
+ iCumulativeSize += iChunkSize;
+ iChunkSize = 0;
+ LOG2( _L("CMmsTransaction::ReleaseData, cumulative %d"), iCumulativeSize );
+
+ TInt error = iDataSupplier->ReleaseData();
+ if ( iError != KErrNone )
+ {
+ iError = error;
+ }
+
+ if ( iError != KErrNone )
+ {
+ // could not get more data - cancel the transaction
+ iTransaction.Cancel();
+ }
+ else if ( !iLastChunk )
+ {
+ // If we have sent last chunk already, there is no more body data
+ // If the function leaves, there is nothing we can do about it.
+ // ReleaseData() must not leave.
+ TRAP_IGNORE( iTransaction.NotifyNewRequestBodyPartL() );
+ }
+ // else nont needed - no operation
+ }
+
+// ---------------------------------------------------------
+// OverallDataSize
+// ---------------------------------------------------------
+//
+TInt CMmsTransaction::OverallDataSize()
+ {
+ TInt dataSize = iDataSupplier->OverallDataSize();
+ LOG2( _L("CMmsTransaction::OverallDataSize %d"), dataSize );
+
+ return dataSize;
+ }
+
+// ---------------------------------------------------------
+// Reset
+// ---------------------------------------------------------
+//
+TInt CMmsTransaction::Reset()
+ {
+ LOG( _L("CMmsTransaction::Reset") );
+ // We always point to the start of data
+// return KErrNone;
+ return iDataSupplier->ResetSupplier();
+
+ }
+
+// End of File