/*
* Copyright (c) 2007-2007 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 "ocspclient.h"

using namespace java::security;
using namespace std;
using namespace java::util;

const TInt KCertStoreUIDForSWInstallOCSPSigning = 268478646;

OcspClient* OcspClient::createInstance(long iap, const char* defaultUrl)
{
    OcspClient* self = new OcspClient(iap);
    TRAPD(err, self->ConstructL(defaultUrl));
    if (err != KErrNone)
    {
        delete self;
        self = NULL;
    }
    return self;
}

OcspClient::OcspClient(long iap)
        : CActive(EPriorityNormal), iIap(TUint32(iap)), iDefaultUrl(NULL), iOcspClient(NULL), iCertStore(NULL), iInitialized(false), iOcspResponse(NULL), iCertArray(NULL), iMonitor(NULL)
{
    CActiveScheduler::Add(this);
}

void OcspClient::ConstructL(const char* defaultUrl)
{
    User::LeaveIfError(iRfs.Connect());
    iCertStore = CUnifiedCertStore::NewL(iRfs, false /*aOpenForWrite*/);
    iOcspResponse = OcspResponse::NewL();
    // create the monitor
    iMonitor = Monitor::createMonitor();
    if (defaultUrl)
    {
        int len = strlen(defaultUrl);
        iDefaultUrl = HBufC8::NewL(len);
        TPtr8 defaultUrlPtr = iDefaultUrl->Des();
        TPtr8 ptr((TUint8 *)defaultUrl, len);
        ptr.SetLength(len);
        defaultUrlPtr.Copy(ptr);
    }
}

void OcspClient::RunL()
{
    switch (iState)
    {
    case ESendOcspRequest:
        SendOcspRequestL();
        iState = EProcessOcspResponse;
        break;
    case EProcessOcspResponse:
        ProcessOcspResponse();
        iMonitor->notify();
        return;
    }
    // re-issue a new request
    SetActive();
}

void OcspClient::DoCancel()
{
    if (iCertStore)
    {
        iCertStore->CancelInitialize();
    }
    if (iOcspClient)
    {
        iOcspClient->CancelCheck();
    }
}

TInt OcspClient::RunError(TInt /*aError*/)
{
    return KErrNone;
}

OcspClient::~OcspClient()
{
    if (IsActive())
    {
        Cancel();
    }
    if (iCertStore)
    {
        delete iCertStore;
        iCertStore = NULL;
    }
    if (iOcspClient)
    {
        delete iOcspClient;
        iOcspClient = NULL;
    }
    if (iOcspResponse)
    {
        delete iOcspResponse;
        iOcspResponse = NULL;
    }
    if (iCertArray)
    {
        iCertArray->ResetAndDestroy();
        delete iCertArray;
    }
    if (iDefaultUrl)
    {
        delete iDefaultUrl;
        iDefaultUrl = NULL;
    }
    if (iMonitor)
    {
        delete iMonitor;
        iMonitor = NULL;
    }
    iRfs.Close();
}

void OcspClient::startOcspCheck(const char ** aCertChain, int aCertChainLen)
{

    // clear the response
    iOcspResponse->Clear();
    // build up the chain
    iCertChain = aCertChain;
    iCertChainLen = aCertChainLen;
    // kick off the state machine
    Start();
}

OcspResponse OcspClient::getOcspCheckResponse()
{
    // wait for completion
    iMonitor->wait();
    // return the result
    return *iOcspResponse;
}

void OcspClient::cancelOcspCheck(bool doCleanup)
{
    // cancel the outstanding request (if any)
    if (doCleanup)
    {
        if (IsActive())
        {
            Cancel();
        }
    }
    iMonitor->notify();
}

void OcspClient::Start()
{
    // cancel the outstanding request (if any)
    if (IsActive())
    {
        Cancel();
    }

    iState = ESendOcspRequest;
    SetActive();
    if (iInitialized)
    {
        // already initialized
        CompleteRequest();
        return;
    }

    // do the initialization
    iCertStore->Initialize(iStatus);
    iInitialized = true;
}

void OcspClient::SendOcspRequestL()
{
    InitOcspClientL();
    iOcspClient->Check(iStatus);
}

void OcspClient::ProcessOcspResponse()
{
    int summary = RESPONSE_CANNOT_OBTAIN_CERT_STATUS;
    if (iStatus.Int() == KErrNone)
    {
        // set the summary
        switch (iOcspClient->SummaryResult())
        {
        case OCSP::EGood:
            summary = RESPONSE_GOOD;
            break;
        case OCSP::EUnknown:
            summary = RESPONSE_UNKNOWN;
            break;
        case OCSP::ERevoked:
            summary = RESPONSE_REVOKED;
            break;
        }
        // set the individual responses
        for (TInt index = 0 ; index < iOcspClient->TransactionCount() ; ++index)
        {
            const TOCSPOutcome& outcome = iOcspClient->Outcome(index);
            switch (outcome.iStatus)
            {
            case OCSP::ETransportError:
            case OCSP::EClientInternalError:
            case OCSP::EMalformedRequest:
            case OCSP::EServerInternalError:
            case OCSP::ETryLater:
            case OCSP::ESignatureRequired:
            case OCSP::EClientUnauthorised:
            case OCSP::EUnknownResponseType:
                iOcspResponse->iIndividualResponses.push_back(RESPONSE_CANNOT_OBTAIN_CERT_STATUS);
                break;
            case OCSP::ENoServerSpecified:
            case OCSP::EInvalidURI:
                iOcspResponse->iIndividualResponses.push_back(RESPONSE_INVALID_REVOCATION_SERVER_URI);
                break;
            case OCSP::EResponseSignatureValidationFailure:
                iOcspResponse->iIndividualResponses.push_back(RESPONSE_SIGNATURE_VALIDATION_FAILURE);
                break;
            case OCSP::EThisUpdateTooLate:
            case OCSP::EThisUpdateTooEarly:
            case OCSP::ENextUpdateTooEarly:
            case OCSP::ENonceMismatch:
            case OCSP::EMalformedResponse:
            case OCSP::EUnknownCriticalExtension:
            case OCSP::EMissingCertificates:
                iOcspResponse->iIndividualResponses.push_back(RESPONSE_INVALID_REVOCATION_SERVER_RESPONSE);
                break;
            case OCSP::EMissingNonce:
                iOcspResponse->iIndividualResponses.push_back(RESPONSE_MISSING_NONCE);
                break;
            case OCSP::ECertificateNotValidAtValidationTime:
                iOcspResponse->iIndividualResponses.push_back(RESPONSE_INVALID_CERT_STATUS_INFO);
                break;
            case OCSP::EValid:
                switch (outcome.iResult)
                {
                case OCSP::EGood:
                    iOcspResponse->iIndividualResponses.push_back(RESPONSE_GOOD);
                    break;
                case OCSP::EUnknown:
                    iOcspResponse->iIndividualResponses.push_back(RESPONSE_UNKNOWN);
                    break;
                case OCSP::ERevoked:
                    iOcspResponse->iIndividualResponses.push_back(RESPONSE_REVOKED);
                    break;
                default:
                    ASSERT(EFalse);
                }
                break;
            default:
                ASSERT(EFalse);
                break;
            }
        }
    }
    iOcspResponse->iSummary = summary;
}

void OcspClient::CompleteRequest()
{
    TRequestStatus* status = &iStatus;
    User::RequestComplete(status,KErrNone);
}

void OcspClient::InitOcspClientL()
{
    if (iOcspClient)
    {
        delete iOcspClient;
        iOcspClient = NULL;
    }
    if (iCertArray)
    {
        iCertArray->ResetAndDestroy();
        delete iCertArray;
    }
    COCSPParameters* ocspParams = COCSPParameters::NewL();
    if (iDefaultUrl)
    {
        ocspParams->SetURIL(*iDefaultUrl, ETrue);
    }
    ocspParams->SetTransport(COCSPTransportDefault::NewL(iIap));
    ocspParams->AddAllAuthorisationSchemesL(
        TUid::Uid(KCertStoreUIDForSWInstallOCSPSigning), *iCertStore);
    // add the certs
    if (iCertChainLen >= 2)
    {
        iCertArray = new(ELeave) CArrayPtrFlat<CX509Certificate>(1);
        std::string cert = JavaCommonUtils::base64decode(std::string(iCertChain[0], strlen(iCertChain[0])));
        TPtr8 ptr8((TUint8 *)(cert.c_str()), cert.size());
        ptr8.SetLength(cert.size());
        CX509Certificate* issuerCert = CX509Certificate::NewL(ptr8);
        iCertArray->AppendL(issuerCert);
        CX509Certificate* subjectCert = NULL;
        for (int i = 1; i < iCertChainLen; i++)
        {
            subjectCert = issuerCert;
            cert = JavaCommonUtils::base64decode(std::string(iCertChain[i], strlen(iCertChain[i])));
            TPtr8 ptr((TUint8 *)(cert.c_str()), cert.size());
            ptr.SetLength(cert.size());
            issuerCert = CX509Certificate::NewL(ptr);
            iCertArray->AppendL(issuerCert);
            ocspParams->AddCertificateL(*subjectCert, *issuerCert);
        }
    }
    iOcspClient = COCSPClient::NewL(ocspParams);
}

OcspResponse* OcspResponse::NewL()
{
    OcspResponse* self = new(ELeave) OcspResponse();
    CleanupStack::PushL(self);
    self->ConstructL();
    CleanupStack::Pop(self);
    return self;
}

OcspResponse::OcspResponse()
{
    iIndividualResponses.clear();
    iSummary = RESPONSE_CANNOT_OBTAIN_CERT_STATUS;
}

void OcspResponse::ConstructL()
{
}

OcspResponse::~OcspResponse()
{
}

void OcspResponse::Clear()
{
    iIndividualResponses.clear();
    iSummary = RESPONSE_CANNOT_OBTAIN_CERT_STATUS;
}

OcspResponse& OcspResponse::operator=(const OcspResponse& x)
{
    iSummary = x.iSummary;
    iIndividualResponses = x.iIndividualResponses;
    return *this;
}

OcspResponse::OcspResponse(const OcspResponse& x)
{
    *this = x;
}

