/*
* Copyright (c) 2007 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of the License "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 <Uri8.h>
#include <EscapeUtils.h>
#include <http/rhttpheaders.h>
#include <http/mhttpdatasupplier.h>
#include <thttpfields.h>
#include "ResourceHandle.h"
#include "ResourceHandleInternal.h"
#include "ResourceRequest.h"
#include "HttpConnection.h"
#include "ResourceHandleManagerSymbian.h"
#include "StaticObjectsContainer.h"
#include "ResourceLoaderDelegate.h"
#include "HttpCacheSupply.h"
#include "HttpPostDataSupplier.h"
#include <HttpFilterCommonStringsExt.h>
#include <BrCtlDefs.h>
#include "BrCtl.h"
#include "BrCtlSpecialLoadObserver.h"
#include "Frame.h"
#include "FrameLoader.h"
#include "DocumentLoader.h"
#include "HttpUiCallbacks.h"
#include "HttpRequestHeaderManager.h"
#include "HttpConnUtils.h"
#include "MultipartContentHandler.h"
#include "UnknownContentHandler.h"
#include <wtf/Vector.h>

_LIT8(KHttps, "https");
_LIT8( KAppUid, "Appuid" );

using namespace WebCore;

DefersData::DefersData(void* ctx, DefersDataCallback callback) : CActive(CActive::EPriorityStandard)
{
    m_ctx = ctx;
    m_callback = callback;
    CActiveScheduler::Add(this);
}

DefersData::~DefersData()
{
    Cancel();
    delete m_response;

    Vector<HBufC8*>::const_iterator it = m_bodyParts.begin();
    Vector<HBufC8*>::const_iterator end = m_bodyParts.end();
    while (it != end) {
        HBufC8* buf = m_bodyParts.first();
        m_bodyParts.remove(0);
        delete buf;
        it = m_bodyParts.begin();
        end = m_bodyParts.end();
    }
}

void DefersData::RunL()
{
    m_callback(m_ctx);
}

TInt DefersData::RunError(TInt aError)
{
    return KErrNone;
}

void DefersData::Activate()
{
    SetActive();
    iStatus = KRequestPending;
    TRequestStatus* status = &iStatus;
    User::RequestComplete( status, KErrNone );
}


ReceivedFinished::ReceivedFinished(void* ctx, ReceivedFinishedCallback callback) : CActive(CActive::EPriorityStandard)
{
    m_ctx = ctx;
    m_callback = callback;
    m_done = true;
    m_error = KErrNone;
    CActiveScheduler::Add(this);
}

ReceivedFinished::~ReceivedFinished()
{
    Cancel();
}

void ReceivedFinished::RunL()
{
    m_done = true;    
    /*
     * this callback will call deRef on HttpConnection which
     * will delete ReceivedFinished object since it's a 
     * member of HttpConnection. Therefore this call has to 
     * the last one.
     */
    m_callback(m_ctx, m_error);
}

TInt ReceivedFinished::RunError(TInt aError)
{
    return KErrNone;
}

void ReceivedFinished::Activate(TInt errorCode)
{
    m_done = false;
    m_error = errorCode;
    SetActive();
    iStatus = KRequestPending;
    TRequestStatus* status = &iStatus;
    User::RequestComplete( status, KErrNone );
}

void ReceivedFinished::done(bool status)
{
    m_done = status;
}

HttpConnection::HttpConnection(ResourceHandle* _handle, Frame* _frame) : MUrlConnection(_handle)
{
    HttpSessionManager* httpSessionMgr = StaticObjectsContainer::instance()->resourceLoaderDelegate()->httpSessionManager();
    httpSessionMgr->addRequest(this, m_handle);
    m_frag = NULL;
    m_transaction = NULL;
    m_urlResponse = NULL;
    m_contentType = NULL;
    m_encoding = NULL;
    m_cacheSupply = NULL;
    m_postDataSupplier = NULL;
    m_maxSize = 0;
    m_frame = _frame;
    m_IsMultipart = false;
    m_isDone = false;
    m_certInfo = NULL;
    m_accumulatedSize = 0;
    m_defersData = NULL; // requests will not be propagated if loading is blocked
    m_receivedFinished = NULL;
    m_unknownContentHandler = NULL;
}

HttpConnection::~HttpConnection()
{
    HttpSessionManager* httpSessionMgr = StaticObjectsContainer::instance()->resourceLoaderDelegate()->httpSessionManager();
    if (m_transaction) {
        RHTTPSession& session = httpSessionMgr->httpSession();
        // remove own address from transaction properties
        m_transaction->PropertySet().RemoveProperty( session.StringPool().StringF(HttpFilterCommonStringsExt::ESelfPtr,
            HttpFilterCommonStringsExt::GetTable()));
    }
    httpSessionMgr->removeRequest(this);
    delete m_frag;
    delete m_urlResponse;
    delete m_contentType;
    delete m_encoding;
    delete m_defersData;
    delete m_receivedFinished;
    delete m_unknownContentHandler;
    delete m_cacheSupply;
    delete m_postDataSupplier;
    delete m_transaction;
}

HttpConnection* HttpConnection::connectionFromTransaction(RHTTPTransaction& transaction)
{
    RHTTPTransactionPropertySet propSet = transaction.PropertySet();
    THTTPHdrVal propRetVal;
    // Get the name of the property
    HttpSessionManager* httpSessionMgr = StaticObjectsContainer::instance()->resourceLoaderDelegate()->httpSessionManager();
    RHTTPSession& session = httpSessionMgr->httpSession();
    RStringF propName = session.StringPool().StringF( HttpFilterCommonStringsExt::ESelfPtr, HttpFilterCommonStringsExt::GetTable() );
    // if it is set . .
    if( propSet.Property( propName, propRetVal ) ){
        return (HttpConnection*)(TInt(propRetVal));
    }
    return NULL;
}

int HttpConnection::submit()
{
    TRAPD(error, submitL());
    return error;
}

void HttpConnection::submitL()
{
    __ASSERT_DEBUG( !m_transaction, THttpConnUtils::PanicLoader( KErrArgument ) );

    HttpSessionManager* httpSessionMgr = StaticObjectsContainer::instance()->resourceLoaderDelegate()->httpSessionManager();
    User::LeaveIfNull(httpSessionMgr);
    httpSessionMgr->openHttpSessionIfNeededL();
    TPtrC8 urlPtr( m_handle->request().url().des() );
    m_transaction = new (ELeave) RHTTPTransaction;
    RHTTPSession& session = httpSessionMgr->httpSession();
    RStringPool stringPool = session.StringPool();
    const TStringTable& stringTable = RHTTPSession::GetTable();
    TUriParser8 uriParser;
    uriParser.Parse(urlPtr);
    // fragment
    if(uriParser.IsPresent(EUriFragment)) {
        m_frag = uriParser.Extract(EUriFragment).AllocL();
        // url without frag
        uriParser.UriWithoutFragment(urlPtr);
        // and reparse url
        uriParser.Parse(urlPtr);
        }

    // open transaction
    RStringF method;
    if (m_handle->request().httpMethod() == "GET") {
        method = stringPool.StringF( HTTP::EGET, stringTable );
    }
    else if (m_handle->request().httpMethod() == "POST") {
        method = stringPool.StringF( HTTP::EPOST, stringTable );
    }
    else if (m_handle->request().httpMethod() == "PUT") {
        method = stringPool.StringF( HTTP::EPUT, stringTable );
    }
    else if (m_handle->request().httpMethod() == "DELETE") {
        method = stringPool.StringF( HTTP::EDELETE, stringTable );
    }
    else {
        User::Leave(KErrArgument);
    }
    *m_transaction = session.OpenTransactionL( uriParser, *(httpSessionMgr->transactionCallback()), method );
    // add transaction attributes such as default headers, cache properties
    addRequestHeadersL();
    RHTTPTransactionPropertySet propSet = m_transaction->PropertySet();
    
    RStringF appuid = stringPool.OpenFStringL( KAppUid );
    
    TUint appuidValue = control(m_frame)->webView()->getWidgetId();
    if(appuidValue){
        propSet.SetPropertyL(appuid,THTTPHdrVal(appuidValue));
    }
    
    appuid.Close();
    // Add IMEI Notify property to the transaction
    // It is not needed, as it is done in UA-Prof filter
    // addIMEINotifyPropertiesL();

    // add http request headers
    RHTTPHeaders httpHeaders = m_transaction->Request().GetHeaderCollection();
    httpSessionMgr->requestHeaderManager()->AddAllHeadersL(httpHeaders, m_handle->request());
    // Create a dataSupplier object to supply the request body to http stack.
    if ((m_handle->request().httpMethod() == "POST" ||
        m_handle->request().httpMethod() == "PUT") && m_handle->request().httpBody()){
        // 2 steps constructor
        m_postDataSupplier = new(ELeave)HttpPostDataSupplier( m_transaction, control(m_frame));
        m_postDataSupplier->initL(m_handle->request().httpBody());
        m_transaction->Request().SetBody( *m_postDataSupplier);
    }
    // create cache supplier
    m_cacheSupply = CHttpCacheSupply::NewL( this );
    TBrCtlDefs::TBrCtlCacheMode cacheMode;
    switch (m_handle->request().cachePolicy())
    {
    default:
    case UseProtocolCachePolicy:
        cacheMode = TBrCtlDefs::ECacheModeNormal;
        break;
    case ReloadIgnoringCacheData:
        cacheMode = TBrCtlDefs::ECacheModeNoCache;
        break;
    case ReturnCacheDataElseLoad:
        cacheMode = TBrCtlDefs::ECacheModeHistory;
        break;
    case ReturnCacheDataDontLoad:
        cacheMode = TBrCtlDefs::ECacheModeOnlyCache;
        break;
    };
	IsUrlInCacheL(m_transaction->Request().URI().UriDes() );
    if( m_cacheSupply->StartRequestL( cacheMode ) != KErrNone ) {
        // check if the response must come from the cache
        if( cacheMode != TBrCtlDefs::ECacheModeOnlyCache ) {
            m_transaction->SubmitL();
        }
        else {
            // cache failed
            complete( KErrGeneral );
        }
    }
}

void HttpConnection::cancel()
{
    complete( KErrCancel );
}

void HttpConnection::download(WebCore::ResourceHandle* handle,
                              const WebCore::ResourceRequest& request,
                              const WebCore::ResourceResponse& response)
{
    StaticObjectsContainer::instance()->resourceLoaderDelegate()->httpSessionManager()->download(handle,
        request, response, this);
}

void HttpConnection::setDefersLoading(bool defers)
{
    if (m_receivedFinished && !m_receivedFinished->isDone()) {
        // Don't set defer loading when in ReceivedFinished process
        return;
    }

    if (defers) {
        if (m_defersData) {
            m_defersData ->Cancel(); // This would happen if we did not finish sending the accumulated content, and a second JavaScript dialog is displayed.
        } else {
            m_defersData = new DefersData(this, processDefersData);
        }
        if (m_cacheSupply) {
            m_cacheSupply->PauseSupply();
        }
    }
    else {
        if (m_defersData) {
            m_defersData->Activate();
        }
        if (m_cacheSupply) {
            m_cacheSupply->ResumeSupply();
        }
    }
}

void HttpConnection::handleError(int error)
{
    complete(error);
}

void HttpConnection::MHFRunL(const THTTPEvent &aEvent)
    {
    if (m_cancelled) {
        return;
        }

    __ASSERT_DEBUG( m_transaction, THttpConnUtils::PanicLoader( KErrArgument ) );

    // Using this flag to prevent caching of secure items
    TBool nonsecure = ETrue;
    TUriParser8 requestedParser;
    if(requestedParser.Parse(m_transaction->Request().URI().UriDes()) == KErrNone) {
        if( requestedParser.Extract( EUriScheme ).Compare(KHttps) == 0 ) {
            nonsecure = EFalse;
            }
        }
    switch( aEvent.iStatus )
        {
        case THTTPEvent::EGotResponseHeaders:
            {
            if ( nonsecure ) {
                // pass headers to the cache first
                TRAP_IGNORE(m_cacheSupply->HeadersReceivedL());
                }
            int httpStatus( m_transaction->Response().StatusCode() );
            if (httpStatus == KErrCompletion) {
                return;
            }

            // Add certificate only if https, and top level request
            if( m_handle->request().mainLoad() ) {
                if ( m_transaction->Request().URI().Extract( EUriScheme ).FindF( KHttps ) == 0 && !m_isInCache ) {
                    m_certInfo = new(ELeave)TCertInfo;
                    m_transaction->ServerCert( *m_certInfo );
                    }
                // certinfo will be set/overwritten only if top load request
                control(m_frame)->setCertInfo( m_certInfo );
            }

            // authentication
            bool handled( EFalse );
            //in case of bad Auth Header(aError = -7276), transaction is cancelled already in the filter,
            //should be passed to Error Handler, otherwise handle Auth request normally.
            if( ( httpStatus == 401 /*EHttpUnauthorized*/ ) ||
                ( httpStatus == 407 /*EHttpProxyAuthenticationRequired*/ ) )
                {
                // Move the transaction to the Auth queue,
                // and wait for Authentication callback
                // This is proventing the transaction from being deleted
                // in case the network dropped automatically for example
                // during the data call connection.
                m_transaction->Cancel();
                HttpSessionManager* httpSessionMgr = StaticObjectsContainer::instance()->resourceLoaderDelegate()->httpSessionManager();
                httpSessionMgr->removeRequest(this);
                httpSessionMgr->addAuthRequest(this, m_handle);
                TInt authRet = handleAuthRequestL( httpStatus );
                handled = (authRet == KErrNone);
                // trans is cancelled at this point. if
                // authentication is failed (not handled) then
                // just complete this transaction
                if( !handled )
                    {
                    //
                    complete( authRet );
                    return;
                    }
                }
            if ( !handled )
                {
                // url
                m_urlResponse = NULL;
                if(m_frag) {
                    m_urlResponse = HBufC8::NewL(m_transaction->Request().URI().UriDes().Length() + m_frag->Length() + 1);
                    TPtr8 responsePtr(m_urlResponse->Des());
                    responsePtr.Copy(m_transaction->Request().URI().UriDes());
                    responsePtr.Append(_L("#"));
                    responsePtr.Append(*m_frag);
                }
                else {
                    m_urlResponse = HBufC8::NewL(m_transaction->Request().URI().UriDes().Length());
                    m_urlResponse->Des().Copy(m_transaction->Request().URI().UriDes());
                }
                // content type
                THTTPHdrVal hdrVal;
                RHTTPHeaders httpHeaders = m_transaction->Response().GetHeaderCollection();
                RStringPool stringPool = m_transaction->Session().StringPool();
                
                THTTPHdrFieldIter it = httpHeaders.Fields();  
                
                HBufC8* headerStr;  
                TBool isRefresh = EFalse;
                TPtrC8 headerValue;   ;
                while( it.AtEnd() == EFalse )
                {
                // Get name of next header field
                RStringTokenF fieldName = it();
                RStringF fieldNameStr = stringPool.StringF( fieldName );
                httpHeaders.GetRawField( fieldNameStr, headerValue );
                headerStr = HBufC8::NewLC( fieldNameStr.DesC().Length() );
                TPtr8 headerPtr( headerStr->Des() );
                headerPtr.Copy( fieldNameStr.DesC() );
                if (equalIgnoringCase(headerPtr, "refresh"))
                {
                  isRefresh = ETrue;
                	CleanupStack::PopAndDestroy(); // headerStr
                	break;
                }

                CleanupStack::PopAndDestroy(); // headerStr
                ++it;
                }            
         
                const TStringTable& stringTable = RHTTPSession::GetTable();
                if( httpHeaders.GetField( stringPool.StringF( HTTP::EContentType, stringTable ), 0,
                    hdrVal) == KErrNone ) {
                    m_contentType = hdrVal.StrF().DesC().AllocL();
                }
                else {
                    m_contentType = KNullDesC8().AllocL();
                }
                // content encoding
                if( httpHeaders.GetParam( stringPool.StringF( HTTP::EContentType, stringTable ),
                    stringPool.StringF( HTTP::ECharset, stringTable ), hdrVal ) == KErrNone ) {
                    m_encoding = hdrVal.StrF().DesC().AllocL();
                }
                else {
                    m_encoding = KNullDesC8().AllocL();
                }
                // content length
                if( httpHeaders.GetField( stringPool.StringF( HTTP::EContentLength,
                    stringTable ), 0, hdrVal ) == KErrNone ) {
                    m_maxSize = hdrVal.Int();
                }
                String encoding;
                if (m_encoding && m_encoding->Length()) {
                    encoding = m_encoding->Des();
                }
                ResourceResponse response(m_urlResponse->Des(), m_contentType->Des(), m_maxSize, encoding, String() );
                response.setHTTPStatusCode(m_transaction->Response().StatusCode());
                //HTTP status text
                response.setHTTPStatusText(((m_transaction->Response()).StatusText().DesC()));
                
                

                if (m_contentType && m_contentType->Length()) {
                    response.setHTTPHeaderField("Content-Type", *m_contentType);
                }
                
                // Add Refresh field only when Refresh existed in recieved header. 
                if (isRefresh)
                    {
                        response.setHTTPHeaderField("Refresh",headerValue);
                    }
				
                TPtrC8 result;

                if( httpHeaders.GetRawField( stringPool.StringF( HTTP::EContentDisposition, stringTable ), result) == KErrNone )
                	response.setHTTPHeaderField("Content-Disposition", result);

                if (m_handle->request().mainLoad()) {
                    if(m_handle->request().url() != response.url()) {
                        // Relative URLs are resolved based on the request URL, not response URL.
                        // If a redirect happens, the request URL must be updated.
                        m_handle->getInternal()->m_request.setURL(response.url());
                        m_frame->loader()->activeDocumentLoader()->request().setURL(response.url());
                    }
                    if(MultipartContentHandler::IsSupported(response)) {
                        m_IsMultipart = true;
                        m_MultipartContentHandler = MultipartContentHandler::NewL();
                        m_MultipartContentHandler->HandleResponseHeadersL(response, *m_transaction);
                        return;
                    }
                    if (UnknownContentHandler::isUnknownContent(response)) {
                        m_unknownContentHandler = UnknownContentHandler::NewL(this, response);
                        return;
                    }
                }
                // If loading is defered, don't send the headers now
                if (m_defersData) {
                    ResourceResponse* resp = new ResourceResponse(m_urlResponse->Des(), m_contentType->Des(), m_maxSize, encoding, String());
                    if (resp == NULL) {
                        complete(KErrNoMemory);
                    }
                    else {
                        m_defersData->m_response = resp;
                    }
                }
                else {
                    CResourceHandleManager::self()->receivedResponse(m_handle, response, this);
                }
                // transaction is complete (must have been cancelled in receivedResponse call
                if (m_isDone)
                    return;
                // transaction is taken, we need to cleanup this resource.
                if (!m_transaction) {
                    derefHandle();
                    // this object might be invalid at this point
                    return;
                    }
                }
            break;
        }
        case THTTPEvent::EGotResponseBodyData:
            {
            if ( nonsecure ) {
                // pass chunk to the cache first
                TRAP_IGNORE( m_cacheSupply->BodyReceivedL() );
                }
            MHTTPDataSupplier* body = m_transaction->Response().Body();
            // get it from the transaction
            TPtrC8 chunkPtr;
            body->GetNextDataPart( chunkPtr );
            m_accumulatedSize += chunkPtr.Length();

            if (chunkPtr.Length()) {
                if(m_IsMultipart) {
                    m_MultipartContentHandler->HandleResponseBodyL(chunkPtr);
                } else {
                    if (m_unknownContentHandler) {
                        m_unknownContentHandler->updateContentTypeL(chunkPtr);
                        if (m_defersData) {
                            m_defersData ->m_response = m_unknownContentHandler->handOffResponse();
                        }
                        else {
                            ResourceResponse* response = m_unknownContentHandler->handOffResponse();
                            CResourceHandleManager::self()->receivedResponse(m_handle, *response, this);
                            delete response;
                            // transaction is complete (must have been cancelled in receivedResponse call
                            if (m_isDone)
                                return;
                            // transaction is taken, we need to cleanup this resource.
                            if (!m_transaction) {
                                derefHandle();
                                // this object might be invalid at this point
                                return;
                            }
                        }
                        delete m_unknownContentHandler;
                        m_unknownContentHandler = NULL;
                    }
                    // If loading is defered, don't send the body now
                    if (m_defersData) {
                        HBufC8* buf = NULL;
                        buf = chunkPtr.AllocL(); // ok to leave on error because it will trigger HandleError
                        m_defersData->m_bodyParts.append(buf);
                    }
                    else {
                        WebCore::ResourceHandle* rh = handle();
                        if (rh) { rh->ref(); }
                            
                        CResourceHandleManager::self()->receivedData(m_handle, chunkPtr, 
                        m_maxSize == 0 ? chunkPtr.Length() : m_maxSize, this);
                        
                        if ( m_transaction) 
                            body->ReleaseData();
                                    
                        if (rh) { rh->deref(); }
                        
                        // this object might be invalid at this point
                        return;
                    }
                }
            }
            /* If the transaction is closed - through extensive processing through receivedData(), above - the transaction may
               already have been deleted (by HttpConnection::complete).  Check for existence of transaction
               before releasing data (ReleaseData()) to prevent attempts to double delete memory. */
            if ( m_transaction)
                body->ReleaseData();
            break;
            }
        case THTTPEvent::EResponseComplete:
            {
            // do not mix it up with the ESucceeded
            // The transaction's response is complete. An incoming event.
            if ( nonsecure ) {
                TRAP_IGNORE( m_cacheSupply->ResponseCompleteL() );
                }
            break;
            }
        case THTTPEvent::ESucceeded:
            {
            complete(KErrNone);
            break;
            }
        case THTTPEvent::ERequestComplete:
            {
            // request is all set
            if ( nonsecure ) {
                m_cacheSupply->CloseRequest();
                }
            break;
            }
        case THTTPEvent::EFailed:
        case THTTPEvent::EMoreDataReceivedThanExpected:
        case THTTPEvent::EUnrecoverableError:
        case KErrAbort:
            {
                if (!m_transaction) {
                    // this object might be invalid at this point.
                    return;
                    }
                int statusCode = m_transaction->Response().StatusCode();
                if ( statusCode != 200) {
                    complete(-25000 - m_transaction->Response().StatusCode());
                }
                else if (statusCode == 200 && aEvent.iStatus == THTTPEvent::EFailed) {
                    // this is a weird combination, it is caused by cached supplier when a response only has the
                    // response header, but no response body is sent by the HTTP stack.  It is a legitimate senario though.
                    complete(KErrNone);
                }
                else {
                    complete(aEvent.iStatus);
                }

            break;
            }
        case THTTPEvent::ERedirectRequiresConfirmation:
            {
            TInt method = m_transaction->Request().Method().Index(RHTTPSession::GetTable());
            TInt statusCode = m_transaction->Response().StatusCode();

            if( (statusCode == 301 ) &&
                !((method==HTTP::EGET) || (method==HTTP::EHEAD)) )
                 {
                    RHTTPRequest request = m_transaction->Request();
                    RHTTPHeaders requestHeaders(request.GetHeaderCollection());
                    RStringPool p = m_transaction->Session().StringPool();
                    requestHeaders.RemoveField(p.StringF(HTTP::EContentLength,RHTTPSession::GetTable()));
                    requestHeaders.RemoveField(p.StringF(HTTP::EContentType,RHTTPSession::GetTable()));
                    m_transaction->Request().RemoveBody();
                    m_transaction->Response().RemoveBody();
                    m_transaction->Request().SetMethod(p.StringF(HTTP::EGET, RHTTPSession::GetTable()));
                }
            int ret = HandleSpecialEvent(THTTPEvent::ERedirectRequiresConfirmation);
            if (ret != KErrNone) {
                handleError(ret);
                }
            break;
            }
        case THTTPEvent::ERedirectedPermanently:
        case THTTPEvent::ERedirectedTemporarily:
            {
            // for redirects, we must ensure that we properly handle
            // any new or changed uri fragment
            TUriParser8 uriParser;
            uriParser.Parse( m_transaction->Request().URI().UriDes() );
            // check for fragment
            if(uriParser.IsPresent(EUriFragment)) {
                // first extract the portion without fragment to
                // return to Http framework
                TPtrC8 uriNoFrag;
                // extract non-fragment portion into uriNoFrag
                uriParser.UriWithoutFragment( uriNoFrag );
                TUriParser8 parserNoFrag;
                parserNoFrag.Parse( uriNoFrag );
                m_transaction->Request().SetURIL( parserNoFrag );
                // now save the fragment for later use
                const TDesC8& fragment = uriParser.Extract( EUriFragment );
                delete m_frag;
                m_frag = NULL;
                m_frag = fragment.AllocL();
                }
            HandleSpecialEvent(aEvent.iStatus);
            break;
            }
        case THTTPEvent::EGotResponseTrailerHeaders:
            {
            break;// We don't process this event
            }
        default:
            {
            // error handling
            handleError(aEvent.iStatus);
            break;
            }
        }
    }

int HttpConnection::HandleSpecialEvent(int event)
{
    int ret( KErrNone );
    HttpSessionManager* httpSessionMgr = StaticObjectsContainer::instance()->resourceLoaderDelegate()->httpSessionManager();
    switch( event )
        {
        case THTTPEvent::ERedirectRequiresConfirmation:
            {
                // Display a Redirect Confirmation dialog, before proceeding
                ret = httpSessionMgr->uiCallback()->aboutToLoadPage(control(m_frame), HttpUiCallbacks::ERedirectConfirmation);
                if (ret == KErrNone) {
                    // submit redirected transaction
                    TRAP(ret, m_transaction->SubmitL());
                    return ret;
                }
                else {
                    return KErrCancel;
                }
                break;
            }
        case THTTPEvent::ERedirectedPermanently:
        case THTTPEvent::ERedirectedTemporarily:
            {
            // Cancel transaction first and resubmit it if the user accepted a secure connection
            m_transaction->Cancel();
            if (CheckForSecurityStatusChange() != KErrNone) {
                return KErrCancel;
                }
            else {
                ret = CheckForNonHttpRedirect();
                if (ret == KErrNone) {
                    // submit redirected transaction only in case of http request.
                    TRAP(ret, m_transaction->SubmitL());
                    return ret;
                    }
                else {
                    return KErrCancel;
                    }
                }
            break;
            }
        case KErrNotSupported:
        default:
            {
                break;
            }
        }
    return ret;
}

void HttpConnection::complete(int error)
{
    if (m_defersData) {
        m_defersData->m_done = true;
        m_defersData->m_error = error;
        return;
    }
    
    // protect this function from re-entry
    if (m_isDone)
        return;
    m_isDone = true;

    __ASSERT_DEBUG( m_transaction, THttpConnUtils::PanicLoader( KErrArgument ) );

    if(m_IsMultipart && error == KErrNone) {
        TInt status = m_MultipartContentHandler->ResponseComplete();
        if(status == KErrNone) {
            TPtrC8 encoding(*m_encoding);
            if (m_MultipartContentHandler->MarkupCharset().Length()) {
                encoding.Set(m_MultipartContentHandler->MarkupCharset());
            }
            ResourceResponse response(m_urlResponse->Des(), m_MultipartContentHandler->MarkupContentType(),
                m_maxSize, encoding, String() );
            response.setHTTPStatusCode(m_transaction->Response().StatusCode());
            response.setHTTPStatusText(m_transaction->Response().StatusText().DesC());
            if(m_handle->request().url() != response.url()) {
                // Relative URLs are resolved based on the request URL, not response URL.
                // If a redirect happens, the request URL must be updated.
                m_handle->getInternal()->m_request.setURL(response.url());
                m_frame->loader()->activeDocumentLoader()->request().setURL(response.url());
            }
            CResourceHandleManager::self()->receivedResponse(m_handle, response, this);
            CResourceHandleManager::self()->receivedData(m_handle,
                m_MultipartContentHandler->MarkupContent(), m_maxSize, this);
        }
        delete m_MultipartContentHandler;
    }
    if (!error) {
        // Spawn active object for call to return immediately, to avoid blocking
        activateReceivedFinished(error);  
    }

    if (m_cacheSupply) {
        delete m_cacheSupply;
        m_cacheSupply = NULL;
    }
    if (m_postDataSupplier) {
        delete m_postDataSupplier;
        m_postDataSupplier = NULL;
    }
    if (m_transaction) {
        m_transaction->Cancel();
        m_transaction->Close();
        delete m_transaction;
        m_transaction = NULL;
    }
    if (error) {
        CResourceHandleManager::self()->receivedFinished(m_handle, error, this);
        derefHandle();
    }
}

// -----------------------------------------------------------------------------
// HttpConnection::handleAuthRequestL
//
// -----------------------------------------------------------------------------
//
int HttpConnection::handleAuthRequestL(
    int status )
    {
    int ret( KErrNone );
    THTTPHdrVal usernameVal;
    THTTPHdrVal passwordVal;
    THTTPHdrVal realmVal;
    THTTPHdrVal staleVal;
    RHTTPTransactionPropertySet propSet = m_transaction->PropertySet();
    RHTTPSession& session = StaticObjectsContainer::instance()->resourceLoaderDelegate()->httpSessionManager()->httpSession();
    RStringPool strP = session.StringPool();
    const TStringTable& stringTable = RHTTPSession::GetTable();

    bool isProxy( EFalse );
    bool needClose( EFalse );
    bool pwdClose( EFalse );
    bool realmClose( EFalse );
    bool stale( propSet.Property( strP.StringF( HTTP::EStale, stringTable ), staleVal ) );

    _LIT8( KStrNull, "" );
    //
    switch( status )
        {
        case EHttpUnauthorized:
            {
            if( !propSet.Property( strP.StringF( HTTP::ERealm, stringTable ), realmVal ) )
                {
                realmClose = ETrue;
                realmVal = strP.OpenStringL( KStrNull );
                }
            if( !propSet.Property( strP.StringF( HTTP::EUsername, stringTable ), usernameVal ) )
                {
                needClose = ETrue;
                usernameVal = strP.OpenStringL( KStrNull );
                }
            if( !propSet.Property(strP.StringF( HTTP::EPassword, stringTable ), passwordVal ) )
                {
                pwdClose = ETrue;
                passwordVal = strP.OpenStringL( KStrNull );
                }
            break;
            }
        case EHttpProxyAuthenticationRequired:
            {
            const TStringTable& stringTableEx = HttpFilterCommonStringsExt::GetTable();
            //
            isProxy = ETrue;
            if( !propSet.Property(strP.StringF( HttpFilterCommonStringsExt::EProxyRealm,
                stringTableEx ), realmVal ) )
                {
                return KErrHttpDecodeUnknownAuthScheme;
                }
            if( !propSet.Property( strP.StringF( HttpFilterCommonStringsExt::EProxyUsername,
                stringTableEx ), usernameVal ) )
                {
                needClose = ETrue;
                usernameVal = strP.OpenStringL( KStrNull );
                }
            if( !propSet.Property( strP.StringF( HTTP::EPassword, stringTable ), passwordVal ) )
                {
                pwdClose = ETrue;
                passwordVal = strP.OpenStringL( KStrNull );
                }
            break;
            }
        default:
            {
            __ASSERT_DEBUG( EFalse, THttpConnUtils::PanicLoader( KErrArgument ) );
            break;
            }
        }

    TRAP( ret, SendAuthRequestL( usernameVal, realmVal, isProxy, stale, passwordVal ) );
    if (realmClose)
      {
        realmVal.Str().Close();
        }
    if( needClose )
        {
        usernameVal.Str().Close();
        }
    if( pwdClose )
        {
        passwordVal.Str().Close();
        }
    return(ret);
    }

// -----------------------------------------------------------------------------
// HttpConnection::SendAuthRequestL
//
// -----------------------------------------------------------------------------
//
void HttpConnection::SendAuthRequestL(
    THTTPHdrVal& aUsernameVal,
    THTTPHdrVal& aRealmVal,
    bool aIsProxy,
    bool aStale,
    THTTPHdrVal& aPasswordVal )
    {

    HttpSessionManager* httpSessionMgr = StaticObjectsContainer::instance()->resourceLoaderDelegate()->httpSessionManager();
    RHTTPSession& session = httpSessionMgr->httpSession();
    int popCount( 0 );

    // prepare uri param
    int len = m_transaction->Request().URI().UriDes().Length() + 1;
    TUint16* uri = new(ELeave) TUint16 [ len ];
    CleanupStack::PushL( uri );
    popCount++;
    TPtr uriPtr( uri, len );
    uriPtr.Copy( m_transaction->Request().URI().UriDes() );
    uriPtr.ZeroTerminate();

    // prepare username param
    len = aUsernameVal.Str().DesC().Length() + 1;
    TUint16* username = new(ELeave) TUint16 [ len ];
    CleanupStack::PushL( username );
    popCount++;
    TPtr usernamePtr( username, len );
    usernamePtr.Copy( aUsernameVal.Str().DesC() );
    usernamePtr.ZeroTerminate();

    // prepare realm param
    len = aRealmVal.Str().DesC().Length() + 1;
    TUint16* realm = new(ELeave) TUint16 [ len ];
    CleanupStack::PushL( realm );
    popCount++;
    TPtr realmPtr( realm, len );
    realmPtr.Copy( aRealmVal.Str().DesC() );
    realmPtr.ZeroTerminate();

    // prepare password param
    len = aPasswordVal.Str().DesC().Length() + 1;
    TUint16* password = new(ELeave) TUint16 [ len ];
    CleanupStack::PushL( password );
    popCount++;
    TPtr passwordPtr( password, len );
    passwordPtr.Copy( aPasswordVal.Str().DesC() );
    passwordPtr.ZeroTerminate();

    // Get the authentication type
    bool basicAuthentication( EFalse );
    RHTTPTransactionPropertySet propSet = m_transaction->PropertySet();
    THTTPHdrVal propRetVal;

    // Get the name of the property
    RStringF propName = session.StringPool().StringF( HTTP::EBasic, RHTTPSession::GetTable() );

    // if it is set . .
    if( propSet.Property( propName, propRetVal ) )
        {
        // . . then we have basic auth
        basicAuthentication = ETrue;
        }

    // send data to dialog layer for user input.  Data from user is provided
    // back to http transaction. by UiCallbacks calling HttpConnection::AuthenticationResponse
    // Completion of this authentication process will therefore occur before
    // the call below returns.
    httpSessionMgr->uiCallback()->AuthenticationRequest( this, uriPtr, usernamePtr,
        realmPtr, aIsProxy, aStale, passwordPtr, basicAuthentication );
    CleanupStack::PopAndDestroy( popCount ); // password, realm, username, uri
    }

// -----------------------------------------------------------------------------
// HttpConnection::AuthenticationResponse
//
//
// -----------------------------------------------------------------------------
//
void HttpConnection::AuthenticationResponse(
    TPtr& aUsername,
    TPtr& aPassword,
    bool aProxy,
    int aError)
    {
    TUint16* username = (TUint16*) aUsername.Ptr();
    TUint16* password = (TUint16*) aPassword.Ptr();

    __ASSERT_DEBUG( m_transaction, THttpConnUtils::PanicLoader( KErrArgument ) );

    int ret = KErrNone;

    // Execution is back from the dialog, move the transaction
    // from the authentication listback into active list
    HttpSessionManager* httpSessionMgr = StaticObjectsContainer::instance()->resourceLoaderDelegate()->httpSessionManager();
    httpSessionMgr->removeAuthRequest(this);
    httpSessionMgr->addRequest(this, m_handle);

    switch (aError)
        {
        case KErrNone:
            {
            TRAP( ret, this->AddAuthenticationPropertiesL( aUsername, aPassword, aProxy) );
            if (ret == KErrNone)
                {
                m_transaction->SubmitL();
                break;
                }
            aError = ret;
            }
        case KErrCancel:
        case KErrNoMemory:
        default:
            {
            delete username;
            delete password;
            this->complete( aError );
            break;
            }
        }
    }

// -----------------------------------------------------------------------------
// HttpConnection::AddAuthenticationPropertiesL
// Add username and password properties to the transaction.
// -----------------------------------------------------------------------------
//
void HttpConnection::AddAuthenticationPropertiesL(
    TPtr& aUsername,
    TPtr& aPassword,
    bool aProxy )
    {
    TUint16* username = (TUint16*) aUsername.Ptr();
    TUint16* password = (TUint16*) aPassword.Ptr();
    __ASSERT_DEBUG( username != NULL && password != NULL, THttpConnUtils::PanicLoader( KErrArgument ) );

    __ASSERT_DEBUG( m_transaction, THttpConnUtils::PanicLoader( KErrArgument ) );

    RString usernameStr;
    RString passwordStr;
    RStringPool stringPool = m_transaction->Session().StringPool();
    RHTTPTransactionPropertySet propSet = m_transaction->PropertySet();
    //
    User::LeaveIfError( THttpConnUtils::OpenStringFromUnicode( stringPool, username, usernameStr ) );
    CleanupClosePushL( usernameStr );
    User::LeaveIfError( THttpConnUtils::OpenStringFromUnicode( stringPool, password, passwordStr ) );
    CleanupClosePushL( passwordStr );
    // set proxy authentication
    if( aProxy )
        {
        propSet.RemoveProperty( stringPool.StringF( HttpFilterCommonStringsExt::EProxyUsername,
            HttpFilterCommonStringsExt::GetTable() ) );

        propSet.SetPropertyL( stringPool.StringF( HttpFilterCommonStringsExt::EProxyUsername,
            HttpFilterCommonStringsExt::GetTable() ), usernameStr);

        propSet.RemoveProperty( stringPool.StringF( HttpFilterCommonStringsExt::EProxyPassword,
            HttpFilterCommonStringsExt::GetTable() ) );

        propSet.SetPropertyL( stringPool.StringF( HttpFilterCommonStringsExt::EProxyPassword,
            HttpFilterCommonStringsExt::GetTable()), passwordStr );
        }
    else
        {
        propSet.RemoveProperty( stringPool.StringF( HTTP::EUsername, RHTTPSession::GetTable() ) );

        propSet.SetPropertyL( stringPool.StringF( HTTP::EUsername, RHTTPSession::GetTable() ),
            usernameStr );

        propSet.RemoveProperty( stringPool.StringF( HTTP::EPassword, RHTTPSession::GetTable() ) );

        propSet.SetPropertyL( stringPool.StringF( HTTP::EPassword, RHTTPSession::GetTable() ),
            passwordStr );
        }
    CleanupStack::PopAndDestroy( 2 ); // passwordStr, usernameStr
    }

// -----------------------------------------------------------------------------
// HttpConnection::CheckForSecurityStatusChange
//
// -----------------------------------------------------------------------------
//
int HttpConnection::CheckForSecurityStatusChange()
    {
    int error(KErrNone);
    bool requestedSecScheme(EFalse);
    bool redirectedSecScheme(EFalse);

    __ASSERT_DEBUG( m_transaction, THttpConnUtils::PanicLoader( KErrArgument ) );

    if (m_handle->request().mainLoad() && !m_frame->ownerElement())
        {
        TUriParser8 redirectedParser;
        if(redirectedParser.Parse( m_transaction->Request().URI().UriDes() ) == KErrNone)
            {
            //
            if( redirectedParser.Extract( EUriScheme ).Compare(KHttps) == 0 )
                {
                    redirectedSecScheme = ETrue;
                }
            }
        TUriParser8 requestedParser;
        if(m_handle->request().url().des().Length() != 0 )
            {
            error = requestedParser.Parse(m_handle->request().url().des());
            }
        else
            {
            error = requestedParser.Parse(m_transaction->Request().URI().UriDes());
            }
        if(error == KErrNone)
            {
            //
            if( requestedParser.Extract( EUriScheme ).Compare(KHttps) == 0 )
                {
                requestedSecScheme = ETrue;
                }
            }
        //When submitting the request iSecurePage was set based on the request url
        //Check the redirect url and see if the scheme has changed
        HttpSessionManager* httpSessionMgr = StaticObjectsContainer::instance()->resourceLoaderDelegate()->httpSessionManager();
        if(requestedSecScheme && !redirectedSecScheme) //redirection from a secure page to an unsecure one
            {
            error = httpSessionMgr->uiCallback()->aboutToLoadPage(control(m_frame), HttpUiCallbacks::EExitingSecurePage);
            }
        else if(redirectedSecScheme && !requestedSecScheme) //redirection to unsecurepage when secure page was requested
            {
            error = httpSessionMgr->uiCallback()->aboutToLoadPage(control(m_frame), HttpUiCallbacks::EEnteringSecurePage );
            }
        }
    return error;
    }

// -----------------------------------------------------------------------------
// HttpConnection::CheckForNonHttpRedirect
//
// -----------------------------------------------------------------------------
//
TInt HttpConnection::CheckForNonHttpRedirect()
    {
    TUriParser8 uriParser;

    __ASSERT_DEBUG( m_transaction, THttpConnUtils::PanicLoader( KErrArgument ) );

    if(uriParser.Parse( m_transaction->Request().URI().UriDes() ) == KErrNone)
        {
        if (uriParser.IsPresent(EUriHost) && uriParser.IsPresent(EUriScheme)) // looking only for absolue Url path and schemes other than http(s)
            {
            const TDesC8& scheme = uriParser.Extract(EUriScheme);
            if (scheme.FindF(_L8("http")) == KErrNotFound) // everything but http(s)
                {
                TPtrC8 ptr(uriParser.UriDes());
                // these arrays are pushed into CleanupStack in case leave
                // if no leave, they will be freed below
                RArray<TUint>* typeArray = new (ELeave) RArray<TUint>(1);
                CleanupStack::PushL(typeArray);

                CDesCArrayFlat* desArray = new (ELeave) CDesCArrayFlat(1);
                CleanupStack::PushL(desArray);

                User::LeaveIfError(typeArray->Append(EParamRequestUrl));

                HBufC16* urlbuf = HBufC16::NewLC( ptr.Length()  + 1); // +1 for zero terminate
                urlbuf->Des().Copy( ptr );
                TPtr16 bufDes16 = urlbuf->Des();
                bufDes16.ZeroTerminate();

                desArray->AppendL(bufDes16);
                CleanupStack::Pop();

                MBrCtlSpecialLoadObserver* loadObserver = control(m_frame)->brCtlSpecialLoadObserver();

                if (loadObserver)
                    {
                    TRAP_IGNORE(loadObserver->HandleRequestL(typeArray, desArray));
                    }

                // No leave, so pop here and clean up
                CleanupStack::Pop(desArray);
                CleanupStack::Pop(typeArray);

                // cleanup arrays
                if (typeArray)
                    {
                    // Closes the array and frees all memory allocated to the array
                    typeArray->Close();
                    delete typeArray;
                    }

                if (desArray)
                    {
                    // Deletes all descriptors from the array and frees the memory allocated to the array buffer
                    desArray->Reset();
                    delete desArray;
                    }

                return KErrCancel;
                }
            }
        }
    return KErrNone;
    }

RHTTPTransaction* HttpConnection::takeOwnershipHttpTransaction()
{
    __ASSERT_DEBUG( m_transaction, THttpConnUtils::PanicLoader( KErrArgument ) );

    RHTTPTransaction* trans = m_transaction;
    RHTTPSession& session = StaticObjectsContainer::instance()->resourceLoaderDelegate()->httpSessionManager()->httpSession();
    // remove own address from transaction properties
    m_transaction->PropertySet().RemoveProperty(session.StringPool().StringF(HttpFilterCommonStringsExt::ESelfPtr,
        HttpFilterCommonStringsExt::GetTable()));
    m_transaction = NULL;
    return trans;
}

// -----------------------------------------------------------------------------
// HttpConnection::addIMEINotifyPropertiesL
// Keeping for completeness, but currently is not needed as functionality is implemented
// in User-Agent Profile Filter
// Add IMEINotify properties to the transaction.
// -----------------------------------------------------------------------------
//
/*
void HttpConnection::addIMEINotifyPropertiesL()
{
    // Get IMEI Notify enable/disable setting
    HttpSessionManager* httpSessionMgr = StaticObjectsContainer::instance()->resourceLoaderDelegate()->httpSessionManager();
    bool imeiNotify = httpSessionMgr->imeiEnabled();

    RHTTPTransactionPropertySet propSet = m_transaction->PropertySet();

    RStringPool stringPool = m_transaction->Session().StringPool();
    propSet.RemoveProperty( stringPool.StringF(
        HttpFilterCommonStringsExt::EIMEINotify, HttpFilterCommonStringsExt::GetTable() ) );
    //
    propSet.SetPropertyL( stringPool.StringF( HttpFilterCommonStringsExt::EIMEINotify,
        HttpFilterCommonStringsExt::GetTable() ), THTTPHdrVal( imeiNotify ) );
}
*/

// -----------------------------------------------------------------------------
// HttpLoaderUtils::IsUrlInCache
//
// Returns ETrue if Cache Manager finds the Url in cache, EFalse otherwise
// -----------------------------------------------------------------------------
//
bool HttpConnection::IsUrlInCacheL(
    const TDesC8& aUrl )
{
    TBool inCache( EFalse );  // not in cache by default
	m_isInCache = EFalse;

    CHttpCacheManager* cache = StaticObjectsContainer::instance()->resourceLoaderDelegate()->httpSessionManager()->cacheManager();

    if ( cache )
    {
        // call cache manager to check for url in cache
        inCache = cache->Find( aUrl );
    }
	m_isInCache = inCache;
    return inCache;
}

void HttpConnection::processDefersData(void* ctx)
{
    HttpConnection* self = static_cast<HttpConnection*>(ctx);
    DefersData* defersData = self->m_defersData;
    if (self->m_cancelled)
        return;
    if (self->m_defersData->m_response) {
        CResourceHandleManager::self()->receivedResponse(self->m_handle, *(self->m_defersData->m_response), self);
        // transaction is complete (must have been cancelled in receivedResponse call
        if (self->m_isDone)
            return;
        // transaction is taken, we need to cleanup this resource.
        if (!self->m_transaction) {
            self->derefHandle();
            // this object might be invalid at this point
            return;
        }
        delete self->m_defersData->m_response;
        self->m_defersData->m_response = NULL;
        self->m_defersData->Activate();
        return;
    }
    if (self->m_defersData->m_bodyParts.size()) {
        HBufC8* buf = self->m_defersData->m_bodyParts.first();
        self->m_defersData->m_bodyParts.remove(0);
        CResourceHandleManager::self()->receivedData(self->m_handle, *buf, self->m_maxSize, self);
        delete buf;
        self->m_defersData->Activate();
        return;
    }
    if (self->m_defersData->m_done) {
        self->m_defersData = NULL;
        self->complete(defersData->m_error);
    }
    self->m_defersData = NULL;
    delete defersData;
}


void HttpConnection::activateReceivedFinished(TInt errorCode) 
{
        if (m_receivedFinished) {
            if (m_receivedFinished->isDone()) {
                delete m_receivedFinished;
            } else {
                // This would happen if processing of a previous transaction is hung.
                // We should never get in here.
                m_receivedFinished ->Cancel();
                return;
            }
        }
            
        m_receivedFinished = new ReceivedFinished(this, processReceivedFinished);
        m_receivedFinished->Activate(errorCode);
}

void HttpConnection::processReceivedFinished(void* ctx, TInt errorCode)
{
    HttpConnection* self = static_cast<HttpConnection*>(ctx);
    CResourceHandleManager::self()->receivedFinished(self->m_handle, errorCode, self);

    self->derefHandle();
}


// end of file
