/*
* Copyright (c) 2006-2010 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 "catalogshttpdownload.h"
#include <bautils.h>
#include <e32err.h>
#include <DocumentHandler.h>
#include <apmstd.h> // TDataType
#include <escapeutils.h>
#include "catalogshttpdownloadmanagerimpl.h"
#include "catalogshttpobserver.h"
#include "catalogshttpconfigimpl.h"
#include "catalogskeyvaluepair.h"
#include "catalogshttpheadersimpl.h"
#include "catalogsutils.h"
#include "catalogsdebug.h"
#include "catalogsconstants.h"
#include "catalogserrors.h"
#include "catalogshttpconnectionmanager.h"
#include "catalogsconnectionmethod.h"
#include "catalogsconnection.h"
#include "catalogshttputils.h"
// Number of retries before giving up the download
const TInt KReconnectAttempts = 3;
const TInt KHttpErrorStatus = 400;
const TInt KGenericSymbianHttpError = -20000;
#define CATALOGS_DISABLE_DOWNLOAD_RESPONSE_HEADERS
// HTTP header strings
_LIT8( KHttpRequestAcceptHeader, "Accept" );
_LIT8( KHttpRequestAcceptCharSetHeader, "Accept-Charset" );
_LIT8( KHttpRequestAcceptLanguageHeader, "Accept-Language" );
_LIT8( KHttpRequestExpectHeader, "Expect" );
_LIT8( KHttpRequestFromHeader, "From" );
_LIT8( KHttpRequestHostHeader, "Host" );
_LIT8( KHttpRequestMaxForwardsHeader, "Max-Forwards" );
_LIT8( KHttpRequestPragmaHeader, "Pragma" );
_LIT8( KHttpRequestRefererHeader, "Referer" );
_LIT8( KHttpRequestUserAgentHeader, "User-Agent" );
_LIT8( KHttpRequestVaryHeader, "Vary" );
_LIT8( KHttpGeneralCacheControlHeader, "Cache-Control" );
_LIT8( KHttpGeneralDateHeader, "Date" );
_LIT8( KHttpGeneralPragmaHeader, "Pragma" );
_LIT8( KHttpGeneralViaHeader, "Via" );
_LIT8( KHttpGeneralWarningHeader, "Warning" );
_LIT8( KHttpEntityAllowHeader, "Allow" );
_LIT8( KHttpEntityContentEncodingHeader, "Content-Encoding" );
_LIT8( KHttpEntityContentLanguageHeader, "Content-Language" );
_LIT8( KHttpEntityContentLocationHeader, "Content-Location" );
_LIT8( KHttpEntityExpiresHeader, "Expires" );
_LIT8( KHttpEntityLastModifiedHeader, "Last-Modified" );
#ifndef CATALOGS_DISABLE_DOWNLOAD_RESPONSE_HEADERS
_LIT8( KHttpResponseCharSet, "Charset" );
_LIT8( KHttpResponseAge, "Age" );
_LIT8( KHttpResponseETag, "ETag" );
_LIT8( KHttpResponseLocation, "Location" );
_LIT8( KHttpResponseRetryAfter, "Retry-After" );
_LIT8( KHttpResponseServer, "Server" );
_LIT8( KHttpResponseVary, "Vary" );
#endif
// ======== MEMBER FUNCTIONS ========
// ---------------------------------------------------------------------------
// Creator
// ---------------------------------------------------------------------------
//
CCatalogsHttpDownload* CCatalogsHttpDownload::NewLC(
CCatalogsHttpDownloadManager& aOwner,
RHttpDownload* aDownload,
const CCatalogsHttpConfig& aConfig )
{
CCatalogsHttpDownload* self = new( ELeave ) CCatalogsHttpDownload(
aOwner,
aDownload );
CleanupStack::PushL( self );
self->ConstructL( &aConfig );
return self;
}
// ---------------------------------------------------------------------------
// Destructor
// ---------------------------------------------------------------------------
//
CCatalogsHttpDownload::~CCatalogsHttpDownload()
{
DLTRACEIN(( "this-ptr: %X", this ));
SetTransferring( EFalse );
if ( iTransaction )
{
DLTRACE(("Cancelling transaction"));
iTransaction->Cancel();
}
// Normally, iDownload is not deleted since it would also remove the
// incomplete files etc.
if ( iDownload )
{
DLTRACE(("RHttpDownload-ptr: %x", iDownload ));
if ( iNormalDelete &&
iState.iOperationState != ECatalogsHttpOpCompleted &&
iState.iOperationState != ECatalogsHttpOpFailed )
{
DLTRACE(("Setting download as paused"));
SetDeleteState( EDownloadPaused );
}
else
{
DLTRACE(( "Deleting download" ));
iDownload->Delete();
if ( iState.iOperationState != ECatalogsHttpOpCompleted )
{
// Ensure that temp files are really deleted
DeleteFiles();
}
}
}
ReleasePtr( iConnection );
DLTRACE(( "Removing from owner" ));
if ( iState.iOperationState != ECatalogsHttpOpCompleted &&
iState.iOperationState != ECatalogsHttpOpFailed )
{
iOwner.CompleteOperation( this );
}
else if ( iState.iOperationState == ECatalogsHttpOpFailed
&& iState.iProgressState != ECatalogsHttpDone )
{
iOwner.CompleteOperation( this );
}
iOwner.RemoveDownload( this );
delete iConfig;
delete iResponseHeaders;
delete iAddedRequestHeaders;
delete iUri;
delete iEncodedUri;
delete iTempFilename;
iDdFile.Close();
}
// ---------------------------------------------------------------------------
// Add a reference
// ---------------------------------------------------------------------------
//
TInt CCatalogsHttpDownload::AddRef()
{
iRefCount++;
return iRefCount;
}
// ---------------------------------------------------------------------------
// Release a reference
// ---------------------------------------------------------------------------
//
TInt CCatalogsHttpDownload::Release()
{
DLTRACEIN(( "" ));
iRefCount--;
if ( !iRefCount )
{
// Ensures that iDownload is not deleted and therefore paused/ongoing
// downloads will be handled as determined by the quit operation
iNormalDelete = ETrue;
delete this;
return 0;
}
return iRefCount;
}
// ---------------------------------------------------------------------------
// Reference count
// ---------------------------------------------------------------------------
//
TInt CCatalogsHttpDownload::RefCount() const
{
return iRefCount;
}
// ---------------------------------------------------------------------------
// Cancel download
// ---------------------------------------------------------------------------
//
TInt CCatalogsHttpDownload::Cancel()
{
DLTRACEIN( ( "this: %x, ID: %d, Operation state: %i, Progress state: %d",
this, iId.Id(),
iState.iOperationState, iState.iProgressState ) );
iTransferredSize = 0;
if ( iTransaction )
{
iTransaction->Cancel();
iTransaction = NULL;
}
#ifdef __SERIES60_31__
// Have to pause downloads before deleting. Otherwise progress events
// will stop coming because DL manager's event queue gets stuck in 3.1.
iCancelled = ETrue;
if ( iDownload )
{
iObserver = NULL;
TInt32 state = 0;
iDownload->GetIntAttribute( EDlAttrState, state );
if ( IsOneOf(
static_cast<THttpDownloadState>( state ),
EHttpDlCreated,
EHttpDlPaused,
EHttpDlCompleted,
EHttpDlFailed ) )
{
DLTRACE(("Deleting download"));
DeletePlatformDownload();
}
else
{
DLTRACE(("Pausing download"));
iDownload->Pause();
return RefCount();
}
}
DLTRACE(("No iDownload"));
ReleasePtr( iConnection );
SetTransferring( EFalse );
DeleteFiles();
iOwner.CompleteOperation( this );
iState.iOperationState = ECatalogsHttpOpDeleting;
iState.iProgressState = ECatalogsHttpDone;
return Release();
#else
// DLMAIN-546, if the download was completed before cancel
// was executed then DL manager's download has already been deleted
DeletePlatformDownload();
ReleasePtr( iConnection );
SetTransferring( EFalse );
DeleteFiles();
// call CancelOperation only if the download had been started (or at least
// tried)
if ( iState.iProgressState != ECatalogsHttpNone &&
iState.iProgressState != ECatalogsHttpDone )
{
DLTRACE(("Cancelling the operation"));
iOwner.CompleteOperation( this );
iState.iOperationState = ECatalogsHttpOpDeleting;
iState.iProgressState = ECatalogsHttpDone;
}
return Release();
#endif
}
// ---------------------------------------------------------------------------
// Download progress
// ---------------------------------------------------------------------------
//
TCatalogsTransportProgress CCatalogsHttpDownload::Progress() const
{
if ( iState.iOperationState == ECatalogsHttpOpInProgress )
{
TInt32 contentSize = ContentSize();
if ( !contentSize )
{
// Prevent division by zero problems
contentSize = 1;
}
return TCatalogsTransportProgress( iState.iOperationState, TransferredSize(),
contentSize );
}
return TCatalogsTransportProgress( iState.iOperationState, 0, 1 );
}
// ---------------------------------------------------------------------------
// Start download
// ---------------------------------------------------------------------------
//
TInt CCatalogsHttpDownload::Start( TResumeStartInformation aResumeOrigin )
{
DLTRACEIN(( "op state: %d, prog state: %d", iState.iOperationState,
iState.iProgressState ));
TInt err = KErrNone;
if ( iState.iProgressState == ECatalogsHttpNone )
{
DLTRACE(("Updating dl config"));
TRAP( err, UpdateDownloadConfigurationL() );
if ( err != KErrNone )
{
return err;
}
}
if ( aResumeOrigin == EAutomaticResume && iPaused )
{
// Download is continued automatically but
// it is paused, and we do not resume it
}
else if( iPausePending )
{
// Pausing is an asynchronous operation therefore we need to enqueue
// resume while pause is pending (we get an event from dlmgr when pause is done)
DLTRACE(("Enqueuing resume"));
iQueuedResume = ETrue;
}
// Start from the beginning or continue from paused
else if ( ( iState.iProgressState == ECatalogsHttpNone ||
iState.iOperationState == ECatalogsHttpOpPaused ||
iState.iOperationState == ECatalogsHttpOpQueued ) &&
!iTransaction )
{
DLTRACE(("Starting the operation"));
// Check if the download can be started or put it to queue
err = iOwner.StartOperation( this );
if ( err == KErrNone )
{
DLTRACE(("Starting the actual download"));
err = StartDownload();
if ( err == KErrNone )
{
DLTRACE(("DL started"));
iState.iOperationState = ECatalogsHttpOpInProgress;
iState.iProgressState = ECatalogsHttpStarted;
iPaused = EFalse;
if ( iObserver )
{
TRAP( err, iObserver->HandleHttpEventL(
*this,
iState ) );
}
}
else
{
DLTRACE(( "Start failed with: %i", err ));
}
}
else if ( err == KCatalogsHttpOperationQueued )
{
// Set state as queued and notify the observer
err = KErrNone;
iState.iOperationState = ECatalogsHttpOpQueued;
iPaused = EFalse;
if ( iObserver )
{
TRAP( err, iObserver->HandleHttpEventL( *this, iState ) );
}
}
}
return err;
}
// ---------------------------------------------------------------------------
// Pause download
// ---------------------------------------------------------------------------
//
TInt CCatalogsHttpDownload::Pause()
{
DLTRACEIN( ( "" ) );
if( iQueuedResume )
{
DLTRACE(("Removing queued resume"));
iQueuedResume = EFalse;
return KErrNone;
}
else if ( iTransaction )
{
DLTRACE(("Was getting the headers"));
iTransaction->Cancel();
iTransaction = NULL;
if ( iDdDownload )
{
DLTRACE(("DD download. Cannot really pause but we fake it"));
TFileName name;
iDdFile.FullName( name );
iDdFile.Close();
iFs.Delete( name );
// Reseting filename so that HTTP headers are taken again
// and DD download is restarted correctly.
//
// This is not the optimal solution but otherwise we would need
// to handle DD download resuming after client restart
// differently from resuming normal downloads
//
// SetFilenameL can't actually leave here because it just deletes
// the old filename without any new memory allocations
TRAP_IGNORE( iConfig->SetFilenameL( KNullDesC() ) );
iDdDownload = EFalse;
}
ReleasePtr( iConnection );
// Notify the session manager that there's room for executing
// another operation
iOwner.PauseOperation( this );
iState.iOperationState = ECatalogsHttpOpPaused;
iState.iProgressState = ECatalogsHttpNone;
iPaused = ETrue;
SetTransferring( EFalse );
return KErrNone;
}
else
{
// Set pending flag
if( iState.iOperationState == ECatalogsHttpOpInProgress)
{
DLTRACE(("Set pause pending flag"));
iPausePending = ETrue;
}
// It's possible that pause is not supported but then the download is
// just restarted when it's resumed
DLTRACE(("Thread semaphore count: %d", RThread().RequestCount() ));
TInt err = KErrNone;
if ( iDownload )
{
err = iDownload->Pause();
}
DLTRACE(("Pause err: %d", err ));
if ( err == KErrNone )
{
ReleasePtr( iConnection );
if ( !iPaused )
{
// Notify the session manager that there's room for executing
// another operation
iOwner.PauseOperation( this );
}
iState.iOperationState = ECatalogsHttpOpPaused;
iPaused = ETrue;
SetTransferring( EFalse );
}
DLTRACEOUT(("Operation paused"));
return err;
}
}
// ---------------------------------------------------------------------------
//
// ---------------------------------------------------------------------------
//
void CCatalogsHttpDownload::NotifyCancel()
{
DLTRACEIN((""));
if ( iObserver )
{
iObserver->HandleHttpError( *this, TCatalogsHttpError(
ECatalogsHttpErrorGeneral, KErrCancel ) );
}
else
{
DLINFO(("No observer"));
}
}
// ---------------------------------------------------------------------------
// NOT SUPPORTED
// ---------------------------------------------------------------------------
//
void CCatalogsHttpDownload::SetBodyL( const TDesC8& /* aBody */ )
{
User::Leave( KErrNotSupported );
}
// ---------------------------------------------------------------------------
// NOT SUPPORTED
// ---------------------------------------------------------------------------
//
void CCatalogsHttpDownload::SetBodyL( const TDesC16& /* aBody */ )
{
User::Leave( KErrNotSupported );
}
// ---------------------------------------------------------------------------
// Body getter
// ---------------------------------------------------------------------------
//
const TDesC8& CCatalogsHttpDownload::Body() const
{
return KNullDesC8;
}
// ---------------------------------------------------------------------------
// URI setter
// ---------------------------------------------------------------------------
//
void CCatalogsHttpDownload::SetUriL( const TDesC8& aUri )
{
delete iUri;
iUri = NULL;
iUri = aUri.AllocL();
EncodeUriL();
}
// ---------------------------------------------------------------------------
// URI setter
// ---------------------------------------------------------------------------
//
void CCatalogsHttpDownload::SetUriL( const TDesC16& aUri )
{
delete iUri;
iUri = NULL;
iUri = ConvertUnicodeToUtf8L( aUri );
EncodeUriL();
}
// ---------------------------------------------------------------------------
// URI getter
// ---------------------------------------------------------------------------
//
const TDesC8& CCatalogsHttpDownload::Uri() const
{
if( iUri )
{
return *iUri;
}
return KNullDesC8();
}
// ---------------------------------------------------------------------------
// Configuration
// ---------------------------------------------------------------------------
//
MCatalogsHttpConfig& CCatalogsHttpDownload::Config() const
{
return *iConfig;
}
// ---------------------------------------------------------------------------
// Request headers
// ---------------------------------------------------------------------------
//
MCatalogsHttpHeaders& CCatalogsHttpDownload::RequestHeadersL() const
{
return iConfig->RequestHeaders();
}
// ---------------------------------------------------------------------------
// Response headers
// ---------------------------------------------------------------------------
//
const MCatalogsHttpHeaders& CCatalogsHttpDownload::ResponseHeadersL() const
{
return *iResponseHeaders;
}
// ---------------------------------------------------------------------------
// Operation type
// ---------------------------------------------------------------------------
//
TCatalogsHttpOperationType CCatalogsHttpDownload::OperationType() const
{
return ECatalogsHttpDownload;
}
// ---------------------------------------------------------------------------
// Operation ID
// ---------------------------------------------------------------------------
//
const TCatalogsTransportOperationId&
CCatalogsHttpDownload::OperationId() const
{
return iId;
}
// ---------------------------------------------------------------------------
// Content type setter
// ---------------------------------------------------------------------------
//
void CCatalogsHttpDownload::SetContentTypeL( const TDesC8& aContentType )
{
DLTRACEIN(( "Content-type: %S", &aContentType ));
iContentType = aContentType;
}
// ---------------------------------------------------------------------------
// Content type setter
// ---------------------------------------------------------------------------
//
void CCatalogsHttpDownload::SetContentTypeL( const TDesC16& aContentType )
{
DLTRACEIN(( _L("Content-type: %S"), &aContentType ));
HBufC8* contentType = ConvertUnicodeToUtf8L( aContentType );
iContentType = *contentType;
delete contentType;
}
// ---------------------------------------------------------------------------
// Content type getter
// ---------------------------------------------------------------------------
//
const TDesC8& CCatalogsHttpDownload::ContentType() const
{
return iContentType;
}
// ---------------------------------------------------------------------------
// Content size getter
// ---------------------------------------------------------------------------
//
TInt32 CCatalogsHttpDownload::ContentSize() const
{
DLTRACEIN((""));
// We get the content size only once in order to minimize
// client-server communication with the Download manager server
if ( iDownload && iContentSize <= 0 )
{
iDownload->GetIntAttribute( EDlAttrLength, iContentSize );
DLTRACE(("Content size from download: %i", iContentSize ));
}
return iContentSize;
}
// ---------------------------------------------------------------------------
// Downloaded size getter
// ---------------------------------------------------------------------------
//
TInt32 CCatalogsHttpDownload::TransferredSize() const
{
return iTransferredSize;
}
// ---------------------------------------------------------------------------
// Is download pausable
// ---------------------------------------------------------------------------
//
TBool CCatalogsHttpDownload::IsPausable() const
{
TBool pausable = ETrue;
if ( iDownload )
{
iDownload->GetBoolAttribute( EDlAttrPausable, pausable );
}
return pausable;
}
// ---------------------------------------------------------------------------
// Download status code
// ---------------------------------------------------------------------------
//
TInt CCatalogsHttpDownload::StatusCode() const
{
return 0;
}
// ---------------------------------------------------------------------------
// Download status text
// ---------------------------------------------------------------------------
//
const TDesC8& CCatalogsHttpDownload::StatusText() const
{
return KNullDesC8();
}
// ---------------------------------------------------------------------------
//
// ---------------------------------------------------------------------------
//
void CCatalogsHttpDownload::SetConnection( CCatalogsConnection& aConnection )
{
DLTRACEIN((""));
ReleasePtr( iConnection );
iConnection = &aConnection;
iConnection->AddRef();
const TCatalogsConnectionMethod& method( aConnection.ConnectionMethod() );
DLTRACE(("Connection: %d, %u, %u",
method.iType, method.iId, method.iApnId ));
Config().SetConnectionMethod( method );
}
// ---------------------------------------------------------------------------
//
// ---------------------------------------------------------------------------
//
void CCatalogsHttpDownload::ReportConnectionError( TInt aError )
{
DLTRACEIN(("aError: %d", aError));
iObserver->HandleHttpError( *this,
TCatalogsHttpError( ECatalogsHttpErrorGeneral, aError ) );
}
// ---------------------------------------------------------------------------
//
// ---------------------------------------------------------------------------
//
CCatalogsHttpConnectionManager& CCatalogsHttpDownload::ConnectionManager()
{
return iOwner.ConnectionManager();
}
// ---------------------------------------------------------------------------
//
// ---------------------------------------------------------------------------
//
void CCatalogsHttpDownload::ExternalizeL( RWriteStream& aStream ) const
{
DLTRACEIN((""));
iConfig->ExternalizeL( aStream );
ExternalizeDesL( ContentType(), aStream );
ExternalizeDesL( *iTempFilename, aStream );
ExternalizeEnumL( iMode, aStream );
iId.ExternalizeL( aStream );
}
// ---------------------------------------------------------------------------
//
// ---------------------------------------------------------------------------
//
void CCatalogsHttpDownload::InternalizeL( RReadStream& aStream )
{
DLTRACEIN((""));
iConfig->InternalizeL( aStream );
HBufC8* contentType = NULL;
InternalizeDesL( contentType, aStream );
if ( contentType->Length() > KMaxContentTypeLength )
{
DeletePtr( contentType );
DLERROR(("Content type was too long, leaving with KErrCorrupt"));
User::Leave( KErrCorrupt );
}
iContentType = *contentType;
DeletePtr( contentType );
InternalizeDesL( iTempFilename, aStream );
InternalizeEnumL( iMode, aStream );
iId.InternalizeL( aStream );
}
// ---------------------------------------------------------------------------
// Operation state
// ---------------------------------------------------------------------------
//
TCatalogsHttpOperationState CCatalogsHttpDownload::State() const
{
return iState.iOperationState;
}
// ---------------------------------------------------------------------------
// Handles events from the configuration
// ---------------------------------------------------------------------------
//
TInt CCatalogsHttpDownload::HandleHttpConfigEvent(
MCatalogsHttpConfig* /* aConfig */,
const TCatalogsHttpConfigEvent& aEvent )
{
if ( aEvent == ECatalogsHttpCfgPriorityChanged )
{
return iOwner.OperationPriorityChanged( this );
}
return KErrNone;
}
// ---------------------------------------------------------------------------
// Handles events from the transaction
// ---------------------------------------------------------------------------
//
void CCatalogsHttpDownload::HandleHttpEventL(
MCatalogsHttpOperation& aOperation,
TCatalogsHttpEvent aEvent )
{
DLTRACEIN((""));
DLINFO( ("OP ID: %i, event op: %i, pr: %i, this-ptr: %X",
aOperation.OperationId().Id(), aEvent.iOperationState,
aEvent.iProgressState, this ) );
switch ( aEvent.iOperationState )
{
case ECatalogsHttpOpCompleted:
{
if ( !iDdDownload )
{
DLTRACE( ("Op completed") );
UpdateResponseHeadersL( aOperation );
DLTRACE(("Content type from op: %S", &aOperation.ContentType() ));
// Copy content type if didn't receive any from protocol
// or if the type received from HTTP headers is OMA DD when
// the type from protocol/previous purchase might not be
if ( !iContentType.Length() ||
aOperation.ContentType().MatchF( KMimeTypeMatchOdd8 ) == 0 )
{
DLTRACE(("Updating content type"));
SetContentTypeL( aOperation.ContentType() );
}
iTransaction = NULL;
// Tries to parse the target filename from the URI if it hasn't
// been set already. Doing this here because if it were done
// in UpdateDownloadConfigurationL, pausing before HTTP
// headers are received would prevent new HTTP HEAD request
// from being sent.
ParseFilenameFromUriL();
TRAPD( err, UpdateFilenameFromContentDispositionL() );
if ( err != KErrNone )
{
// Failed to update from content disposition,
// use tempfilename unless some name has already been set
UpdateFilenameL();
}
// No need to get headers again
iMode = ECatalogsHttpHeaderModeNoHead;
// Check if the download is a DD and if it is, use a
// transaction to download the descriptor so that Download
// manager doesn't try to mess with things
if ( iContentType.MatchF( KMimeTypeMatchOdd8 ) == 0 )
{
DLTRACE(("Content type matches DD download"));
iDdDownload = ETrue;
StartDescriptorDownloadL();
}
else
{
// Start the normal file download
User::LeaveIfError( Start() );
}
aOperation.Release();
}
else // iDdDownload == ETrue
{
DLTRACE(("Closing dd"));
iDdFile.Close();
iTransaction = NULL;
iState = aEvent;
iState.iOperationState = ECatalogsHttpOpCompleted;
iState.iProgressState = ECatalogsHttpDone;
iObserver->HandleHttpEventL( *this, iState );
aOperation.Release();
}
break;
}
case ECatalogsHttpOpInProgress:
{
if ( iDdDownload )
{
DLTRACE(("DD download"));
iState = aEvent;
switch ( aEvent.iProgressState )
{
case ECatalogsHttpResponseBodyReceived:
{
DLTRACE(("Received a body part"));
User::LeaveIfError( iDdFile.Write( iTransaction->Body() ) );
iObserver->HandleHttpEventL( *this, aEvent );
break;
}
default:
{
DLTRACE(("Default"));
}
}
}
break;
}
default:
{
}
}
DLTRACEOUT((""));
}
// ---------------------------------------------------------------------------
// Handles events from the transaction
// ---------------------------------------------------------------------------
//
TBool CCatalogsHttpDownload::HandleHttpError(
MCatalogsHttpOperation& aOperation, TCatalogsHttpError aError )
{
DLTRACEIN((""));
DLERROR( ("Error type: %i, code: %i", aError.iType,
aError.iError ) );
iTransaction = NULL;
aOperation.Release();
iOwner.CompleteOperation( this );
iState.iOperationState = ECatalogsHttpOpFailed;
iState.iProgressState = ECatalogsHttpDone;
iObserver->HandleHttpError( *this, aError );
return ETrue;
}
// ---------------------------------------------------------------------------
// Handles events from the download manager
// ---------------------------------------------------------------------------
//
void CCatalogsHttpDownload::HandleEventL( THttpDownloadEvent aEvent )
{
DLTRACEIN((""));
DLINFO( ("DL id: %d, Dl state: %i, Pr: %i, this-ptr: %X, iState: %d, iProgress: %d, dl-ptr: %X",
OperationId().Id(),
aEvent.iDownloadState, aEvent.iProgressState,
this, iState.iOperationState, iState.iProgressState,
iDownload ) );
#ifdef __SERIES60_31__
if ( IsCancelled() )
{
if ( IsOneOf(
aEvent.iDownloadState,
EHttpDlPaused,
EHttpDlCompleted,
EHttpDlFailed ) )
{
Cancel();
}
return;
}
#endif
if ( iState.iProgressState == ECatalogsHttpNone )
{
DLTRACEOUT(("Not yet in progress"));
return;
}
TInt32 statusCode = -1;
iDownload->GetIntAttribute( EDlAttrStatusCode, statusCode );
DLINFO( ("Response status: %i", statusCode ) );
#ifdef CATALOGS_BUILD_CONFIG_DEBUG
TInt32 errorId = 0;
iDownload->GetIntAttribute( EDlAttrErrorId, errorId );
DLINFO( ("Error id: %i", errorId ) );
#endif
TInt32 globalErrorId = 0;
iDownload->GetIntAttribute(
EDlAttrGlobalErrorId, globalErrorId );
DLINFO( ("Global error id: %i", globalErrorId ) );
// was >=, DL manager should give us proper errors but it doesn't always
if ( ( statusCode == KHttpErrorStatus ||
globalErrorId == KGenericSymbianHttpError ||
globalErrorId == KErrDisMounted) &&
aEvent.iDownloadState != EHttpDlFailed )
{
DLTRACE(("Setting download as failed because received a response >= 400" ));
aEvent.iDownloadState = EHttpDlFailed;
}
switch ( aEvent.iDownloadState )
{
case EHttpDlCreated:
{
iState.iOperationState = ECatalogsHttpOpCreated;
break;
}
case EHttpDlInprogress:
{
iState.iOperationState = ECatalogsHttpOpInProgress;
// Update progress info
HandleEventProgressL( aEvent );
break;
}
case EHttpDlPaused:
{
DLTRACE(("EHttpDlPaused"));
// For some reason restored downloads receive unwanted EHttpDlPaused
// event when they are started. Hopefully this doesn't break something else
if ( iState.iProgressState == ECatalogsHttpStarted )
{
DLTRACE(("Something funny happened, skip the event"));
break;
}
SetTransferring( EFalse );
if ( iReconnectWhenFail &&
iState.iProgressState == ECatalogsHttpDisconnected )
{
DLTRACE(("Try to reconnect"));
iReconnectWhenFail = EFalse;
iDownload->Start();
break;
}
if ( iState.iOperationState != ECatalogsHttpOpQueued )
{
iState.iOperationState = ECatalogsHttpOpPaused;
iPaused = ETrue;
}
ReleasePtr( iConnection );
if( aEvent.iProgressState == EHttpProgNone )
{
DLTRACE(("got pause event, resetting pause pending flag"));
iPausePending = EFalse;
if( iQueuedResume )
{
DLTRACE(("Handling queued resume"));
iQueuedResume = EFalse;
Start();
}
}
else
{
HandleEventProgressL( aEvent );
}
break;
}
case EHttpDlCompleted:
{
DLINFO( ("EHttpDlCompleted, progress state: %d", aEvent.iProgressState ) );
SetTransferring( EFalse );
// Release connection as soon as possible so that other APs
// can be raised in the observer callbacks if necessary
ReleasePtr( iConnection );
if ( iPaused )
{
DLTRACE(("iPaused == ETrue, setting state to paused"));
iState.iOperationState = ECatalogsHttpOpPaused;
}
// This ensures that CompleteOperation() works correctly if
// the download was just paused when it completes
if ( iState.iOperationState != ECatalogsHttpOpPaused )
{
iState.iOperationState = ECatalogsHttpOpCompleted;
}
// move/rename temp file as the target file
TRAPD( err, MoveFileL() );
// DLMAIN-546, delete DL manager's download before starting
// the next one so that downloads don't jam, again
if ( iDownload )
{
iDownload->GetIntAttribute(
EDlAttrDownloadedSize, iTransferredSize );
DLTRACE(("Deleting download"));
DeletePlatformDownload();
}
iState.iProgressState = ECatalogsHttpDone;
iOwner.CompleteOperation( this );
iState.iOperationState = ECatalogsHttpOpCompleted;
if ( err != KErrNone && iObserver )
{
DLERROR(("Error %d occurred when moving", err));
iState.iOperationState = ECatalogsHttpOpFailed;
iObserver->HandleHttpError(
*this, TCatalogsHttpError(
ECatalogsHttpErrorGeneral,
err ) );
}
else if ( iObserver )
{
iObserver->HandleHttpEventL( *this, iState );
}
DLINFO(("EHttpDlCompletedDone"));
break;
}
case EHttpDlFailed:
{
DLTRACE(( "Download failed" ));
TInt32 errorId = -1;
SetTransferring( EFalse );
iDownload->GetIntAttribute( EDlAttrErrorId, errorId );
DLINFO( ("Error id: %i", errorId ) );
DLINFO( ("Global error id: %i", globalErrorId ) );
if ( iReconnectWhenFail &&
( errorId == EConnectionFailed ||
errorId == ETransactionFailed ))
{
DLTRACE(("Try to reconnect"));
iReconnectWhenFail = EFalse;
iDownload->Start();
break;
}
else if ( errorId == EContentExpired ||
errorId == EPartialContentModified )
{
DLTRACE(("Content has changed, reset and restart"));
iReconnectWhenFail = EFalse;
iDownload->Reset();
iDownload->Start();
break;
}
ReleasePtr( iConnection );
iState.iOperationState = ECatalogsHttpOpFailed;
TBool deleted = EFalse;
if ( iObserver )
{
// Determine whether failure was due to a HTTP error or some
// other error. HTTP errors are mapped to a negative range
if ( statusCode >= 400 )
{
deleted = iObserver->HandleHttpError(
*this, TCatalogsHttpError(
ECatalogsHttpErrorHttp,
KCatalogsErrorHttpBase - statusCode ) );
}
else
{
// 5.0 issue: some downloads fail without any error code
// so we have to give them some
if ( globalErrorId == KErrNone )
{
DLERROR(("Download failed without an error"));
globalErrorId = KErrUnknown;
}
AddRef();
iOwner.ConnectionManager().ReportConnectionError(
TCatalogsConnectionMethod(), globalErrorId );
if ( iRefCount > 1 )
{
deleted = iObserver->HandleHttpError(
*this, TCatalogsHttpError(
ECatalogsHttpErrorGeneral, globalErrorId ) );
}
else
{
deleted = ETrue;
}
Release();
}
}
if ( !deleted )
{
iOwner.CompleteOperation( this );
iState.iProgressState = ECatalogsHttpDone;
}
break;
}
/// MMC card or other storage media is removed from the phone.
/*
case EHttpDlMediaRemoved:
{
iState.iOperationState = ECatalogsHttpOpMediaRemoved;
break;
}
*/
/**
* MMC card or other storage media inserted and
* downloaded content file found on it.
* If MMC card inserted, but (partially) downloaded content file
* is not found on it, download is failed with error reason
* EContentFileIntegrity.
*/
/*
case EHttpDlMediaInserted:
{
iState.iOperationState = ECatalogsHttpOpMediaInserted;
break;
}
*/
/**
* Download process can be paused again. This event only occurs after
* EHttpDlNonPausable.
*/
case EHttpDlPausable:
{
DLTRACE(( "Pausable" ));
iState.iOperationState = ECatalogsHttpOpPausable;
HandleEventProgressL( aEvent );
break;
}
/// Download process cannot be paused, or the content will be lost.
case EHttpDlNonPausable:
{
iState.iOperationState = ECatalogsHttpOpNonPausable;
DLTRACE(( "Nonpausable" ));
// Update the download UI data.
HandleEventProgressL( aEvent );
break;
}
/// Download is started when it's already progressing
case EHttpDlAlreadyRunning:
{
DLTRACE(( "Already running" ));
iState.iOperationState = ECatalogsHttpOpAlreadyRunning;
HandleEventProgressL( aEvent );
// Nothing to do.
break;
}
default:
{
break;
}
}
}
// ---------------------------------------------------------------------------
//
// ---------------------------------------------------------------------------
//
void CCatalogsHttpDownload::HandleEventProgressL( const
THttpDownloadEvent& aEvent )
{
DLTRACEIN(( "" ));
switch ( aEvent.iProgressState )
{
case EHttpStarted:
{
DLTRACE(( "Progress: Started" ));
// If the download was paused immediately, iPaused flag will be
// ETrue but the dl operation is not paused
// Try again to pause the download correctly
if ( iPaused )
{
Pause();
}
else
{
iState.iProgressState = ECatalogsHttpStarted;
iPaused = EFalse;
}
break;
}
case EHttpProgCreatingConnection:
{
DLTRACE(( "Progress: Creating connection" ));
iState.iProgressState = ECatalogsHttpCreatingConnection;
break;
}
case EHttpProgConnected:
{
DLTRACE(( "Progress: Connected" ));
iState.iProgressState = ECatalogsHttpConnected;
SetTransferring( ETrue );
break;
}
case EHttpProgConnectionSuspended:
{
DLTRACE(( "Progress: Connection suspended" ));
iState.iProgressState = ECatalogsHttpConnectionSuspended;
break;
}
case EHttpProgDisconnected:
{
DLINFO(( "Progress: Disconnected" ));
if ( iState.iProgressState == ECatalogsHttpCreatingConnection )
{
DLERROR(("Connection creation failed, no reconnecting"));
iReconnectCount = 0;
}
iState.iProgressState = ECatalogsHttpDisconnected;
DASSERT( iObserver );
SetTransferring( EFalse );
// Don't restart paused downloads or notify about disconnection
if ( !iPaused )
{
TInt32 globalErrorId = 0;
iDownload->GetIntAttribute(
EDlAttrGlobalErrorId, globalErrorId );
DLINFO( ("Global error id: %i", globalErrorId ) );
if ( globalErrorId == KErrCancel )
{
DLTRACE(("Cancelled"));
iState.iOperationState = ECatalogsHttpOpFailed;
iOwner.ConnectionManager().ReportConnectionError(
TCatalogsConnectionMethod(), globalErrorId );
}
else //if ( globalErrorId != KErrNone )
{
if ( iReconnectCount )
{
DLTRACE(("Reconnect, count left: %d", iReconnectCount ));
iReconnectCount--;
iReconnectWhenFail = ETrue;
}
else if ( iObserver )
{
DLTRACE(("No more reconnect attempts"));
TCatalogsHttpError error( ECatalogsHttpErrorGeneral, KErrDisconnected );
iObserver->HandleHttpError( *this, error );
}
}
}
break;
}
case EHttpProgDownloadStarted:
{
DLTRACE(( "Progress: Download started" ));
iState.iProgressState = ECatalogsHttpDownloadStarted;
DASSERT( iObserver );
if ( iObserver )
{
iObserver->HandleHttpEventL( *this,
iState );
}
break;
}
case EHttpContentTypeReceived:
// Start download again if content-type is acceptable
// and UiLib is not installed
{
// State is paused. The observer should continue
// the download if the content type is acceptable
// otherwise he should cancel it
DLTRACE( ( "Progress: Content type received" ) );
iState.iProgressState = ECatalogsHttpContentTypeReceived;
UpdateContentType();
if ( iObserver )
{
iObserver->HandleHttpEventL( *this,
iState );
}
break;
}
case EHttpProgSubmitIssued:
{
iState.iProgressState = ECatalogsHttpSubmitIssued;
break;
}
case EHttpProgResponseHeaderReceived:
{
DLTRACE(( "Progress: Response header received" ));
iState.iProgressState = ECatalogsHttpResponseHeaderReceived;
// Reset reconnect count
iReconnectCount = KReconnectAttempts;
// Read the response headers from platform DL manager
UpdateResponseHeadersL();
UpdateContentType();
if ( iObserver )
{
iObserver->HandleHttpEventL( *this,
iState );
}
break;
}
case EHttpProgResponseBodyReceived:
{
DLTRACE(( "Progress: Response body received" ));
iState.iProgressState = ECatalogsHttpResponseBodyReceived;
// Reset reconnect count
iReconnectCount = KReconnectAttempts;
TInt32 size = 0;
iDownload->GetIntAttribute( EDlAttrDownloadedSize, size );
DLTRACE(("Transferred size from download: %i", size));
if ( size != iTransferredSize )
{
DLTRACE(("Updating transferred size"));
iTransferredSize = size;
if ( iObserver )
{
iObserver->HandleHttpEventL( *this,
iState );
}
}
break;
}
case EHttpProgRedirectedPermanently:
{
DLTRACE(( "Progress: Redirected permanently" ));
iState.iProgressState = ECatalogsHttpRedirectedPermanently;
TRAPD( err,
{
UpdateUriL();
ParseFilenameFromUriL();
} );
if ( iObserver )
{
if ( err == KErrNone )
{
iObserver->HandleHttpEventL( *this, iState );
}
else
{
iObserver->HandleHttpError( *this, TCatalogsHttpError(
ECatalogsHttpErrorGeneral, err ) );
}
}
break;
}
case EHttpProgRedirectedTemporarily:
{
DLTRACE(( "Progress: Redirected temporarily" ));
iState.iProgressState = ECatalogsHttpRedirectedTemporarily;
TRAPD( err,
{
UpdateUriL();
ParseFilenameFromUriL();
} );
if ( iObserver )
{
if ( err == KErrNone )
{
iObserver->HandleHttpEventL( *this, iState );
}
else
{
iObserver->HandleHttpError( *this, TCatalogsHttpError(
ECatalogsHttpErrorGeneral, err ) );
}
}
break;
}
case EHttpProgContentTypeChanged:
{
iState.iProgressState = ECatalogsHttpContentTypeChanged;
break;
}
case EHttpProgMovingContentFile:
{
DLINFO( ("Progress: Moving content file") );
iState.iProgressState = ECatalogsHttpMovingContentFile;
break;
}
case EHttpProgContentFileMoved:
{
DLINFO( ("Progress: Content file moved") );
iState.iProgressState = ECatalogsHttpContentFileMoved;
iOwner.CompleteOperation( this );
break;
}
default:
DLTRACE(("Default"));
DLTRACE(( "state: %d, progress: %d",aEvent.iDownloadState, aEvent.iProgressState));
break;
}
}
// ---------------------------------------------------------------------------
// Sets file server session
// ---------------------------------------------------------------------------
//
void CCatalogsHttpDownload::SetFileServerSession( RFs& aFs )
{
DLTRACEIN((""));
iFs = aFs;
}
#ifdef __SERIES60_31__
TBool CCatalogsHttpDownload::IsCancelled() const
{
return iCancelled;
}
#endif
// ---------------------------------------------------------------------------
// Sets header mode
// ---------------------------------------------------------------------------
//
void CCatalogsHttpDownload::SetHeaderMode( TCatalogsHttpHeaderMode aMode )
{
iMode = aMode;
}
// ---------------------------------------------------------------------------
// Constructor
// ---------------------------------------------------------------------------
//
CCatalogsHttpDownload::CCatalogsHttpDownload(
CCatalogsHttpDownloadManager& aOwner,
RHttpDownload* aDownload ) :
iOwner( aOwner ),
iDownload( aDownload ),
iState( ECatalogsHttpOpCreated, ECatalogsHttpNone ),
iRefCount( 1 ),
iNormalDelete( EFalse ),
iReconnectCount( KReconnectAttempts )
{
DLTRACEIN(( "this-ptr: %X", this ));
}
// ---------------------------------------------------------------------------
// 2nd phase constructor
// ---------------------------------------------------------------------------
//
void CCatalogsHttpDownload::ConstructL( const CCatalogsHttpConfig* aConfig )
{
// Get the id for the download and update iUri
if ( iDownload )
{
// id part will be updated from disk
iId = TCatalogsTransportOperationId( iOwner.SessionId(), KErrNotFound );
UpdateSecondaryIdL();
User::LeaveIfError( SetDeleteState( EDownloadCanBeDeleted ) );
// Update iUri to match the current URI
UpdateUriL();
iResponseHeaders = CCatalogsHttpHeaders::NewL();
}
else
{
iId = TCatalogsTransportOperationId(
iOwner.SessionId(),
iOwner.NewDownloadId() );
}
// Create a copy of the configuration
if ( aConfig )
{
iConfig = aConfig->CloneL();
}
else
{
// Or create a new configuration
iConfig = CCatalogsHttpConfig::NewL();
}
AssignDesL( iTempFilename, KNullDesC() );
}
// ---------------------------------------------------------------------------
// Updates the content type
// ---------------------------------------------------------------------------
//
void CCatalogsHttpDownload::UpdateContentType()
{
DLTRACEIN(( "" ));
if ( !iContentType.Length() )
{
// Download always exists when this is called so no need to check
DLTRACE(("Updating the content type"));
iDownload->GetStringAttribute( EDlAttrContentType,
iContentType );
}
DLTRACEOUT(("Content-type: %S", &iContentType ));
}
// ---------------------------------------------------------------------------
// Updates the filename either from content-disposition header or DL manager
// ---------------------------------------------------------------------------
//
void CCatalogsHttpDownload::UpdateFilenameFromContentDispositionL()
{
DLTRACEIN(( "" ));
const TDesC8& value = iResponseHeaders->HeaderByKeyL(
KCatalogsHttpHeaderContentDisposition );
DLTRACE( ( "Content-disposition: %S", &value ) );
TCatalogsContentDispositionParser parser( value );
//
HBufC* filename = parser.FilenameLC();
iConfig->SetFilenameL( *filename );
CleanupStack::PopAndDestroy( filename );
}
// ---------------------------------------------------------------------------
//
// ---------------------------------------------------------------------------
//
void CCatalogsHttpDownload::UpdateFilenameL()
{
DLTRACEIN( ("") );
if ( ContainsData( iTempFilename ) &&
// We don't want to overwrite existing name
!iConfig->Filename().Length() )
{
DLTRACE(( _L("Setting filename as temp filename: %S"), iTempFilename ));
TParsePtrC parse( *iTempFilename );
iConfig->SetFilenameL( parse.NameAndExt() );
}
}
// ---------------------------------------------------------------------------
// Updates the platform download object to match the configuration
// ---------------------------------------------------------------------------
//
void CCatalogsHttpDownload::UpdateDownloadConfigurationL()
{
DASSERT( iEncodedUri );
DLTRACEIN(( "URI: %S", iEncodedUri ));
DASSERT( iConfig );
TCatalogsConnectionMethod currentAp = iConfig->ConnectionMethod();
// If no APN id is set, we need to use the default
if ( !currentAp.iApnId &&
!currentAp.iId )
{
currentAp = iOwner.ConnectionManager().DefaultConnectionMethod();
iConfig->SetConnectionMethod( currentAp );
}
// Ask for confirmation
if ( !iOwner.ConnectionManager().AskConnectionConfirmation( currentAp ) )
{
DLINFO(("Connection denied by the user"));
User::Leave( KCatalogsErrorHttpConnectionDenied );
}
// note that actual connection setting is done by the
// session manager according to operation's config
// so we don't set the accesspoint to DL manager here anymore
// Update the member variable
iObserver = iConfig->Observer();
// Directory MUST be set
if ( !iObserver || !iConfig->Directory().Length() )
{
DLERROR(("No directory path or observer set, leaving with KErrArgument"));
User::Leave( KErrArgument );
}
// Check if HEAD is required
UpdateHeadRequirement();
// Update the filename only if it has not been set yet and header mode
// is forced (like content downloads and previews) and
// HEAD is not prohibited by download options
if ( iMode == ECatalogsHttpHeaderModeForceHead &&
!iConfig->Filename().Length() &&
!( iConfig->Options() & ECatalogsHttpDisableHeadRequest ) )
{
DLTRACE(( "Forcing header getting with transactions" ) );
iTransaction = iOwner.CreateDlTransactionL( Uri(),
*this, *iConfig );
iTransaction->Config().SetHttpMethod( ECatalogsHttpHead );
iTransaction->Start();
DLTRACE(( "Transaction started" ));
}
}
// ---------------------------------------------------------------------------
// Updates the URI from the DL manager to iUri
// ---------------------------------------------------------------------------
//
void CCatalogsHttpDownload::UpdateUriL()
{
DLTRACEIN( ("") );
// Get the current url of the download
RBuf8 buf;
CleanupClosePushL( buf );
buf.CreateL( KMaxUrlLength );
User::LeaveIfError( iDownload->GetStringAttribute( EDlAttrCurrentUrl,
buf ) );
// DL manager has the encoded URI
AssignDesL( iEncodedUri, buf );
CleanupStack::PopAndDestroy( &buf );
DeletePtr( iUri );
iUri = EscapeUtils::EscapeDecodeL( EncodedUri() );
DLTRACE(( "URI: %S", &EncodedUri() ));
}
// ---------------------------------------------------------------------------
// Updates the request headers to the platform DL manager
// ---------------------------------------------------------------------------
//
void CCatalogsHttpDownload::UpdateRequestHeadersL()
{
DLTRACEIN( ("") );
RPointerArray<CCatalogsKeyValuePair>& headers(
iConfig->RequestHeaders().Headers() );
// Iterate through headers and add them either to the request headers
// in DL manager or iAddedRequestHeaders if they don't have an enumeration
// in DL manager
for ( TInt i = 0; i < headers.Count(); ++i )
{
TUint predefined = MatchWithPredefinedRequestHeader(
headers[i]->Key() );
if ( !predefined )
{
predefined = MatchWithPredefinedGeneralHeader(
headers[i]->Key() );
}
if ( !predefined )
{
AddRequestHeaderL( iAddedRequestHeaders, *headers[i] );
}
else
{
User::LeaveIfError( iDownload->SetStringAttribute(
predefined, headers[i]->Value() ) );
}
}
// Add the headers that don't have a predefined enumeration in
// the platform DL manager
if ( iAddedRequestHeaders )
{
DLTRACE(( "added request headers: %S", iAddedRequestHeaders ));
User::LeaveIfError( iDownload->SetStringAttribute(
EDlAttrRequestHeaderAddon, *iAddedRequestHeaders ) );
delete iAddedRequestHeaders;
iAddedRequestHeaders = NULL;
}
DLTRACEOUT((""));
}
// ---------------------------------------------------------------------------
// Update the response headers from DL manager to the download
// ---------------------------------------------------------------------------
//
void CCatalogsHttpDownload::UpdateResponseHeadersL()
{
DLTRACEIN( ("") );
#ifndef CATALOGS_DISABLE_DOWNLOAD_RESPONSE_HEADERS
RBuf8 buf;
buf.CreateL( KMaxGeneralHeaderFieldLength );
buf.CleanupClosePushL();
if ( KErrNone == iDownload->GetStringAttribute( EDlAttrResponseCharSet, buf ) )
{
DLTRACE( ( "CharSet: %S", &static_cast<TDesC8&>(buf) ) );
iResponseHeaders->AddHeaderL( KHttpResponseCharSet, buf );
}
if ( KErrNone == iDownload->GetStringAttribute( EDlAttrResponseAge, buf ) )
{
DLTRACE( ( "Age: %S", &static_cast<TDesC8&>(buf) ) );
iResponseHeaders->AddHeaderL( KHttpResponseAge, buf );
}
if ( KErrNone == iDownload->GetStringAttribute( EDlAttrResponseETag, buf ) )
{
DLTRACE( ( "ETag: %S", &static_cast<TDesC8&>(buf) ) );
iResponseHeaders->AddHeaderL( KHttpResponseETag, buf );
}
if ( KErrNone == iDownload->GetStringAttribute( EDlAttrResponseLocation, buf ) )
{
DLTRACE( ( "Location: %S", &static_cast<TDesC8&>(buf) ) );
iResponseHeaders->AddHeaderL( KHttpResponseLocation, buf );
}
if ( KErrNone == iDownload->GetStringAttribute( EDlAttrResponseRetryAfter, buf ) )
{
DLTRACE( ( "RetryAfter: %S", &static_cast<TDesC8&>(buf) ) );
iResponseHeaders->AddHeaderL( KHttpResponseRetryAfter, buf );
}
if ( KErrNone == iDownload->GetStringAttribute( EDlAttrResponseServer, buf ) )
{
DLTRACE( ( "ResponseServer: %S", &static_cast<TDesC8&>(buf) ) );
iResponseHeaders->AddHeaderL( KHttpResponseServer, buf );
}
if ( KErrNone == iDownload->GetStringAttribute( EDlAttrResponseVary, buf ) )
{
DLTRACE( ( "ResponseVary: %S", &static_cast<TDesC8&>(buf) ) );
iResponseHeaders->AddHeaderL( KHttpResponseVary, buf );
}
if ( KErrNone == iDownload->GetStringAttribute( EDlAttrGeneralCacheControl, buf ) )
{
DLTRACE( ( "GeneralCacheControl: %S", &static_cast<TDesC8&>(buf) ) );
iResponseHeaders->AddHeaderL( KHttpGeneralCacheControlHeader, buf );
}
if ( KErrNone == iDownload->GetStringAttribute( EDlAttrGeneralDate, buf ) )
{
DLTRACE( ( "Date: %S", &static_cast<TDesC8&>(buf) ) );
iResponseHeaders->AddHeaderL( KHttpGeneralDateHeader, buf );
}
if ( KErrNone == iDownload->GetStringAttribute( EDlAttrGeneralPragma, buf ) )
{
DLTRACE( ( "Pragma: %S", &static_cast<TDesC8&>(buf) ) );
iResponseHeaders->AddHeaderL( KHttpGeneralPragmaHeader, buf );
}
if ( KErrNone == iDownload->GetStringAttribute( EDlAttrGeneralVia, buf ) )
{
DLTRACE( ( "Via: %S", &static_cast<TDesC8&>(buf) ) );
iResponseHeaders->AddHeaderL( KHttpGeneralViaHeader, buf );
}
if ( KErrNone == iDownload->GetStringAttribute( EDlAttrGeneralWarning, buf ) )
{
DLTRACE( ( "Warning: %S", &static_cast<TDesC8&>(buf) ) );
iResponseHeaders->AddHeaderL( KHttpGeneralWarningHeader, buf );
}
if ( KErrNone == iDownload->GetStringAttribute( EDlAttrEntityAllow, buf ) )
{
DLTRACE( ( "Allow: %S", &static_cast<TDesC8&>(buf) ) );
iResponseHeaders->AddHeaderL( KHttpEntityAllowHeader, buf );
}
if ( KErrNone == iDownload->GetStringAttribute( EDlAttrEntityContentEncoding, buf ) )
{
DLTRACE( ( "Content-Encoding: %S", &static_cast<TDesC8&>(buf) ) );
iResponseHeaders->AddHeaderL( KHttpEntityContentEncodingHeader, buf );
}
if ( KErrNone == iDownload->GetStringAttribute( EDlAttrEntityContentLanguage, buf ) )
{
DLTRACE( ( "Content-Language: %S", &static_cast<TDesC8&>(buf) ) );
iResponseHeaders->AddHeaderL( KHttpEntityContentLanguageHeader, buf );
}
if ( KErrNone == iDownload->GetStringAttribute( EDlAttrEntityContentLocation, buf ) )
{
DLTRACE( ( "Content-Location: %S", &static_cast<TDesC8&>(buf) ) );
iResponseHeaders->AddHeaderL( KHttpEntityContentLocationHeader, buf );
}
if ( KErrNone == iDownload->GetStringAttribute( EDlAttrEntityExpires, buf ) )
{
DLTRACE( ( "Expires: %S", &static_cast<TDesC8&>(buf) ) );
iResponseHeaders->AddHeaderL( KHttpEntityExpiresHeader, buf );
}
if ( KErrNone == iDownload->GetStringAttribute( EDlAttrEntityLastModified, buf ) )
{
DLTRACE( ( "Last modified: %S", &static_cast<TDesC8&>(buf) ) );
iResponseHeaders->AddHeaderL( KHttpEntityLastModifiedHeader, buf );
}
CleanupStack::PopAndDestroy( buf );
#endif
DLTRACEOUT( ("") );
}
// ---------------------------------------------------------------------------
// Update the response headers from the transaction to the download
// ---------------------------------------------------------------------------
//
void CCatalogsHttpDownload::UpdateResponseHeadersL(
MCatalogsHttpOperation& aOperation )
{
DLTRACEIN( ("") );
CCatalogsHttpHeaders* headers =
static_cast<const CCatalogsHttpHeaders&>(
aOperation.ResponseHeadersL() ).CloneL();
delete iResponseHeaders;
iResponseHeaders = headers;
DLTRACEOUT( ("") );
}
// ---------------------------------------------------------------------------
// Checks if the given header matches any of the request headers that have
// an predefined enumeration in the platform DL manager
// ---------------------------------------------------------------------------
//
TUint CCatalogsHttpDownload::MatchWithPredefinedRequestHeader(
const TDesC8& aHeader ) const
{
if ( aHeader.CompareF( KHttpRequestAcceptHeader ) == 0 )
{
return EDlAttrRequestAccept;
}
else if ( aHeader.CompareF( KHttpRequestAcceptCharSetHeader ) == 0 )
{
return EDlAttrRequestAcceptCharSet;
}
else if ( aHeader.CompareF( KHttpRequestAcceptLanguageHeader ) == 0 )
{
return EDlAttrRequestAcceptLanguage;
}
else if ( aHeader.CompareF( KHttpRequestExpectHeader ) == 0 )
{
return EDlAttrRequestExpect;
}
else if ( aHeader.CompareF( KHttpRequestFromHeader ) == 0 )
{
return EDlAttrRequestFrom;
}
else if ( aHeader.CompareF( KHttpRequestHostHeader ) == 0 )
{
return EDlAttrRequestHost;
}
else if ( aHeader.CompareF( KHttpRequestMaxForwardsHeader ) == 0 )
{
return EDlAttrRequestMaxForwards;
}
else if ( aHeader.CompareF( KHttpRequestPragmaHeader ) == 0 )
{
return EDlAttrRequestPragma;
}
else if ( aHeader.CompareF( KHttpRequestRefererHeader ) == 0 )
{
return EDlAttrRequestReferer;
}
else if ( aHeader.CompareF( KHttpRequestUserAgentHeader ) == 0 )
{
return EDlAttrRequestUserAgent;
}
else if ( aHeader.CompareF( KHttpRequestVaryHeader ) == 0 )
{
return EDlAttrRequestVary;
}
return 0;
}
// ---------------------------------------------------------------------------
// Checks if the given header matches any of the general headers that have
// an predefined enumeration in the platform DL manager
// ---------------------------------------------------------------------------
//
TUint CCatalogsHttpDownload::MatchWithPredefinedGeneralHeader(
const TDesC8& aHeader ) const
{
if ( aHeader.CompareF( KHttpGeneralCacheControlHeader ) == 0 )
{
return EDlAttrGeneralCacheControl;
}
else if ( aHeader.CompareF( KHttpGeneralDateHeader ) == 0 )
{
return EDlAttrGeneralDate;
}
else if ( aHeader.CompareF( KHttpGeneralPragmaHeader ) == 0 )
{
return EDlAttrGeneralPragma;
}
else if ( aHeader.CompareF( KHttpGeneralViaHeader ) == 0 )
{
return EDlAttrGeneralVia;
}
else if ( aHeader.CompareF( KHttpGeneralWarningHeader ) == 0 )
{
return EDlAttrGeneralWarning;
}
return 0;
}
// ---------------------------------------------------------------------------
// Checks if the given header matches any of the entity headers that have
// an predefined enumeration in the platform DL manager
// ---------------------------------------------------------------------------
//
TUint CCatalogsHttpDownload::MatchWithPredefinedEntityHeader(
const TDesC8& aHeader ) const
{
if ( aHeader.CompareF( KHttpEntityAllowHeader ) == 0 )
{
return EDlAttrEntityAllow;
}
else if ( aHeader.CompareF( KHttpEntityContentEncodingHeader ) == 0 )
{
return EDlAttrEntityContentEncoding;
}
else if ( aHeader.CompareF( KHttpEntityContentLanguageHeader ) == 0 )
{
return EDlAttrEntityContentLanguage;
}
else if ( aHeader.CompareF( KHttpEntityContentLocationHeader ) == 0 )
{
return EDlAttrEntityContentLocation;
}
else if ( aHeader.CompareF( KHttpEntityExpiresHeader ) == 0 )
{
return EDlAttrEntityExpires;
}
else if ( aHeader.CompareF( KHttpEntityLastModifiedHeader ) == 0 )
{
return EDlAttrEntityLastModified;
}
return 0;
}
// ---------------------------------------------------------------------------
// Adds the header from the pair to the target in the format used by
// platform's DL manager
// ---------------------------------------------------------------------------
//
void CCatalogsHttpDownload::AddRequestHeaderL( HBufC8*& aTarget,
const CCatalogsKeyValuePair& aPair )
{
DLTRACEIN((""));
TInt headerLength = aPair.Key().Length() +
aPair.Value().Length() + 1;
TInt newSize = 0;
// Allocate or reallocate the buffer if necessary
if( !aTarget )
{
DLTRACE( ("Creating target buffer") );
aTarget = HBufC8::NewL( headerLength );
}
else if( aTarget->Length() )
{
DLTRACE( ( "Reallocating target buffer with new length" ) );
newSize = aTarget->Length() + headerLength + 1;
aTarget = aTarget->ReAllocL( newSize );
}
else
{
DLTRACE( ("Reallocating target buffer") );
aTarget = aTarget->ReAllocL( headerLength );
}
TPtr8 ptr( aTarget->Des() );
if( newSize )
{
DLTRACE( ("Appending to existing headers") );
// Append after the existing headers
ptr.Append( KHttpFieldSeparator );
ptr.Append( aPair.Key() );
ptr.Append( KColon );
ptr.Append( aPair.Value() );
}
else
{
DLTRACE( ("Creating the first header") );
// Add the first header
ptr.Append( aPair.Key() );
ptr.Append( KColon );
ptr.Append( aPair.Value() );
}
}
// ---------------------------------------------------------------------------
//
// ---------------------------------------------------------------------------
//
void CCatalogsHttpDownload::SetTransferring( TBool aTransferring )
{
DLTRACEIN(("aTransferring: %d, iTransferring: %d",
aTransferring,
iTransferring ));
if ( iTransferring != aTransferring )
{
iTransferring = aTransferring;
iOwner.ReportConnectionStatus( iTransferring );
}
}
// ---------------------------------------------------------------------------
// Updates the connection to download manager and starts the download
// ---------------------------------------------------------------------------
//
TInt CCatalogsHttpDownload::StartDownload()
{
DLTRACEIN((""));
DASSERT( iConnection );
TRAPD( err,
{
iOwner.SetConnectionL( *iConnection );
// Update the configuration
if ( iState.iProgressState == ECatalogsHttpNone )
{
DLTRACE(("Updating dl config"));
InitializeDownloadL();
}
});
if ( err == KErrNone )
{
err = iDownload->Start();
}
return err;
}
// ---------------------------------------------------------------------------
// Starts a OMA DD download if necessary
// ---------------------------------------------------------------------------
//
TBool CCatalogsHttpDownload::StartDescriptorDownloadL()
{
DLTRACEIN((""));
if ( iDdDownload )
{
// This ensures that headers are re-get if the op is paused
iMode = ECatalogsHttpHeaderModeForceHead;
DLTRACE(("Content type matches DD download"));
HBufC* fullName = iConfig->FullPathLC();
DLTRACE(("Opening target file"));
User::LeaveIfError( iDdFile.Replace( iFs, *fullName, EFileWrite ) );
CleanupStack::PopAndDestroy( fullName );
DLTRACE(("Creating the transaction"));
// Download the descriptor without DL manager
// Use normal URI because transaction encodes it
iTransaction = iOwner.CreateDlTransactionL(
Uri(),
*this,
*iConfig );
iTransaction->Config().SetHttpMethod( ECatalogsHttpGet );
iTransaction->Start();
return ETrue;
}
DLTRACEOUT(("Not a DD download"));
return EFalse;
}
// ---------------------------------------------------------------------------
// Delete downloaded files
// ---------------------------------------------------------------------------
//
void CCatalogsHttpDownload::DeleteFiles()
{
DLTRACEIN((""));
TInt err = KErrNotFound;
if ( ContainsData( iTempFilename ) )
{
DLTRACE(( _L("Deleting the temp file: %S"), iTempFilename ));
err = BaflUtils::DeleteFile( iFs, *iTempFilename );
}
// only delete target file if the temp file was not found in case
// we were re-downloading something
if ( err == KErrNotFound &&
// ensures that we don't accidentally delete whole directories
iConfig->Directory().Length() &&
iConfig->Filename().Length() )
{
TPath path;
path.Append( iConfig->Directory() );
path.Append( iConfig->Filename() );
DLTRACE(( _L("Deleting the final file: %S"), &path ));
BaflUtils::DeleteFile( iFs, path );
}
}
// ---------------------------------------------------------------------------
//
// ---------------------------------------------------------------------------
//
void CCatalogsHttpDownload::MoveFileL()
{
DLTRACEIN((""));
if ( !iTempFilename )
{
DLERROR(("Tempfilename has not been set, leaving" ));
User::Leave( KErrGeneral );
}
if ( !iConfig->Filename().Length() )
{
// Update filename extension by using dochandler
if ( iConfig->Options() & ECatalogsHttpDisableHeadRequest )
{
UpdateExtensionL();
}
else
{
DLTRACE(("No target filename, no need to move" ));
UpdateFilenameL();
return;
}
}
HBufC* target = iConfig->FullPathLC();
if ( *target != *iTempFilename )
{
DLTRACE(( _L("Moving %S to %S"), iTempFilename, target ));
CFileMan* fileMan = CFileMan::NewL( iFs );
CleanupStack::PushL( fileMan );
User::LeaveIfError(
fileMan->Move( *iTempFilename, *target, CFileMan::EOverWrite ) );
CleanupStack::PopAndDestroy( fileMan );
}
CleanupStack::PopAndDestroy( target );
}
// ---------------------------------------------------------------------------
//
// ---------------------------------------------------------------------------
//
void CCatalogsHttpDownload::ParseFilenameFromUriL()
{
DLTRACEIN((""));
if ( iConfig->Filename().Length() )
{
DLTRACEOUT(("Filename already set"));
return;
}
DASSERT( iUri );
// Note: Download manager actually does this same stuff, more or less,
//(done still in 3.1.50) but it's a bit clearer if we do it by ourselves.
TUriParser8 uriParser;
User::LeaveIfError( uriParser.Parse( Uri() ) );
HBufC* filename = uriParser.GetFileNameL( EUriFileNameTail );
DLTRACE(( _L("Parsed filename %S"), filename ));
CleanupStack::PushL( filename );
iConfig->SetFilenameL( *filename );
CleanupStack::PopAndDestroy( filename );
}
// ---------------------------------------------------------------------------
//
// ---------------------------------------------------------------------------
//
TBool CCatalogsHttpDownload::ContainsData( const HBufC* aDes ) const
{
return aDes && aDes->Length();
}
// ---------------------------------------------------------------------------
// Updates file extension by using dochandler
// ---------------------------------------------------------------------------
//
void CCatalogsHttpDownload::UpdateExtensionL()
{
DLTRACEIN((""));
// UpdateFilenameL parses the filename from iTempFilename which contains
// the whole path and sets it to iConfig->Filename()
UpdateFilenameL();
TFileName filename = iConfig->Filename();
TDataType type( ContentType() );
// Doc handler does not yet support Widget extension change
TFileName mimeType;
mimeType.Copy(type.Des8());
if ( mimeType.Compare(KMimeTypeMatchWidget) == 0 )
{
ReplaceExtension( filename, KWidgetExtension );
}
else
iOwner.DocumentHandler().CheckFileNameExtension( filename, type );
iConfig->SetFilenameL( filename );
}
// ---------------------------------------------------------------------------
// Forces HEAD if content-type requires so
// ---------------------------------------------------------------------------
//
void CCatalogsHttpDownload::UpdateHeadRequirement()
{
DLTRACEIN((""));
if ( ContentType().MatchF( KMimeTypeMatchOdd8 ) != KErrNotFound ||
ContentType().MatchF( KMimeTypeMatchDrm8 ) != KErrNotFound ||
ContentType().MatchF( KMimeTypeMatchJad8 ) != KErrNotFound )
{
DLTRACE(("Content is DD/JAD/DRM, forcing HEAD"));
// Clear HEAD flag
iConfig->SetOptions(
iConfig->Options() & ~ECatalogsHttpDisableHeadRequest );
SetHeaderMode( ECatalogsHttpHeaderModeForceHead );
}
}
// ---------------------------------------------------------------------------
// Sets delete status and id to the platform download as user data
// ---------------------------------------------------------------------------
//
TInt CCatalogsHttpDownload::SetDeleteState(
const TDownloadDeleteState aStatus )
{
DLTRACEIN(( "aStatus: %x, id: %x", aStatus, OperationId().Id() ));
TInt err = KErrNone;
if ( iDownload )
{
err = iDownload->SetIntAttribute( EDlAttrUserData, aStatus );
}
return err;
}
// ---------------------------------------------------------------------------
// Gets delete status and download id from the platform download
// ---------------------------------------------------------------------------
//
TInt CCatalogsHttpDownload::GetDeleteState(
TDownloadDeleteState& aStatus )
{
DLTRACEIN((""));
TInt32 data = 0;
TInt err = KErrNotReady;
if ( iDownload )
{
err = iDownload->GetIntAttribute( EDlAttrUserData, data );
DLTRACE(("Data: %d", data));
aStatus = static_cast<TDownloadDeleteState>( data );
}
DLTRACEOUT(("status: %x", aStatus ));
return err;
}
// ---------------------------------------------------------------------------
// Initializes download
// ---------------------------------------------------------------------------
//
void CCatalogsHttpDownload::InitializeDownloadL()
{
DLTRACEIN((""));
if ( !iFileIsSet )
{
RFile file;
CleanupClosePushL( file );
BaflUtils::EnsurePathExistsL( iFs, iConfig->Directory() );
if ( !iTempFilename->Length() )
{
// Create a temp name & file
DLTRACE(("Creating temp file"));
TFileName filename;
// Create a temp name
User::LeaveIfError( file.Temp(
iFs, iConfig->Directory(), filename, EFileWrite ) );
DLTRACE(( _L("Temp filename: %S"), &filename ));
AssignDesL( iTempFilename, filename );
}
else // tempfilename has been created, use it
{
DLTRACE(( _L("Using path: %S"), iTempFilename ));
TRAPD( err,
OpenOrCreateFileL( iFs, file, *iTempFilename, EFileWrite ) );
DLTRACE(("File open err: %d", err));
if ( err == KErrNone )
{
// Seek to end so that download manager doesn't overwrite
// already downloaded parts
TInt pos = 0;
file.Seek( ESeekEnd, pos );
}
else if ( err == KErrInUse )
{
// File is (hopefully) used by DL manager because it hasn't
// exited after we paused the download and thus we can't
// set the file handle to the download because it already exists
iFileIsSet = ETrue;
}
else
{
User::Leave( err );
}
}
if ( !iDownload )
{
DLTRACE(("Creating RHttpDownload"));
iDownload = &iOwner.CreatePlatformDownloadL( EncodedUri() );
// Platform download's id is used as a secondary id
UpdateSecondaryIdL();
// Sets delete status and also updates id to the download
User::LeaveIfError( SetDeleteState( EDownloadCanBeDeleted ) );
// Update the request headers to the download manager but do it
// only once
UpdateRequestHeadersL();
}
// Check if the download has been set progressive
TBool progressive = EFalse;
User::LeaveIfError( iDownload->GetBoolAttribute(
EDlAttrProgressive, progressive ) );
// We want to have progressive downloads because then DL manager uses
// a smaller download buffer which makes progress events much more
// frequent. However, something goes wrong if the progressive flag is
// set twice so we have to prevent that. Also we don't need progress
// events for icons so we use the download priority to determine whether
// we use progressive dls or not
if ( !progressive &&
iConfig->Priority() >= ECatalogsPriorityMedium &&
// for some reason EDlAttrProgressive returns EFalse after
// we restart engine but DL manager stays up. We must not
// set the flag if it has already been set because it
// messes the download
!iFileIsSet )
{
DLTRACE(("Setting download as progressive"));
User::LeaveIfError( iDownload->SetBoolAttribute(
EDlAttrProgressive, ETrue ) );
}
// Doesn't pause the download after content type has been received
User::LeaveIfError( iDownload->SetBoolAttribute(
EDlAttrNoContentTypeCheck, ETrue ) );
if ( !iFileIsSet )
{
DLTRACE(("Setting filehandle to download"));
User::LeaveIfError( iDownload->SetFileHandleAttribute( file ) );
iFileIsSet = ETrue;
}
CleanupStack::PopAndDestroy( &file );
}
}
// ---------------------------------------------------------------------------
// Delete platform download and reset secondary id
// ---------------------------------------------------------------------------
//
void CCatalogsHttpDownload::DeletePlatformDownload()
{
if ( iDownload )
{
iDownload->Delete();
iDownload = NULL;
iId.SetSecondaryId( KErrNotFound );
}
}
// ---------------------------------------------------------------------------
// Updates secondary id from the platform download
// ---------------------------------------------------------------------------
//
void CCatalogsHttpDownload::UpdateSecondaryIdL()
{
TInt32 secondaryId = KErrNotFound;
User::LeaveIfError(
iDownload->GetIntAttribute( EDlAttrId, secondaryId ) );
iId.SetSecondaryId( secondaryId );
}
// ---------------------------------------------------------------------------
// Encodes the URI
// ---------------------------------------------------------------------------
//
void CCatalogsHttpDownload::EncodeUriL()
{
DeletePtr( iEncodedUri );
iEncodedUri = CatalogsHttpUtils::EncodeUriL( Uri() );
}
// ---------------------------------------------------------------------------
// Returns the encoded URI
// ---------------------------------------------------------------------------
//
const TDesC8& CCatalogsHttpDownload::EncodedUri() const
{
DASSERT( iEncodedUri );
return *iEncodedUri;
}
// ---------------------------------------------------------
// CCatalogsHttpDownload::ReplaceExtension()
// Replace current extension at aName with extension given (eExt).
// ---------------------------------------------------------
//
void CCatalogsHttpDownload::ReplaceExtension( TDes& aName, const TDesC& aExt )
{
TInt dotPos = aName.LocateReverse( '.' );
if ( dotPos != KErrNotFound )
{
aName.Delete( dotPos, aName.Length()- dotPos );
aName.Append( aExt );
}
}