/*
* Copyright (c) 2005-2006 Nokia Corporation and/or its subsidiary(-ies). 
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description:  Methods that allows to process X509 certificates.
*
*/



/** A wrapper of OpenSSL x509.c functions to Symbian **/

#ifndef XMLSEC_NO_X509
#include <stdlib.h>
#include <string.h>
#include <x509cert.h>
#include <e32std.h>

#include "xmlsecc_config.h"
#include "xmlsecc_x509wrapper.h"
#include "xmlsecc_evpwrapper.h"

#include "xmlsecmsymbiancertchain.h"
#include "xmlsecmsymbiancertstore.h"
#include "xmlsec_error_flag.h"

struct ScX509Store
{	
	RPointerArray< CX509Certificate>	iRootCerts;
	CSymbianCertChain			*iCertChain;	
	CSymbianCertStore                             *iSymbianCertStore;	
	RPointerArray< CX509Certificate>    iRootCertsSCS;
};

void X509_free(X509* aCert)
{
	if (aCert)
	{
		if (aCert->der)
		{
			memset(aCert->der, 0, aCert->derlen);
			free (aCert->der);
		}
		
		free(aCert);
	}
}

void X509_crl_free(X509_CRL* aCrl)
{
}


X509* X509_dup(X509* aCert)
{
	return aCert;
}


/* Get the public key */
EVP_PKEY* X509_get_pubkey(X509* aCert)
{
	EVP_PKEY *pKey = NULL;
	TInt err;
	
	// Create the pKey structure
	pKey = sc_pkey_new(EVP_PKEY_UNKNOWN, NULL);
	if (!pKey)
	{
		return NULL;
	}
	
	//Assign the public key
	err = sc_pkey_setPublic(pKey, aCert);
	if (err != KErrNone)
	{
		sc_pkey_free(pKey);
		return NULL;
	}

	return pKey;
}

TInt testValidityPeriodL(X509 *aCert)
{
	// convert aCert to TDesC8
	TPtrC8 certPtr((const unsigned char*)aCert->der, aCert->derlen);   

	CX509Certificate *cert = CX509Certificate::NewLC(certPtr);
	CValidityPeriod validPeriod = cert->ValidityPeriod();	
	
	TTime currentTime;
	currentTime.HomeTime();	
	
	TBool res = validPeriod.Valid(currentTime);
	CleanupStack::PopAndDestroy(cert);
	
	return (TInt)res;
	
}


/* Test the validity period from the certificate */
TInt X509_test_validityPeriod(X509* aCert)
{
	TInt err;
	TBool res=FALSE;

	TRAP(err, res = testValidityPeriodL(aCert));
	   
	if (err)
	    {
	    xmlSecSetErrorFlag( err );
	    return err;    
	    }
	else
		return res;
}

/* Read the certificate from DER format */
X509* d2i_X509_bio(BIO *aBio)
{
	X509 *cert = (X509 *)malloc(sizeof(X509));
	if (!cert)
	    {
	    xmlSecSetErrorFlag( KErrNoMemory );
		return NULL;
	    }
	
	// Duplicate the certificate
	cert->der = (char *)malloc(aBio->len * sizeof(char));
    if (!cert->der)
        {
        free( cert );
        xmlSecSetErrorFlag( KErrNoMemory );
        return NULL;    
        }
	memcpy(cert->der, aBio->mem, aBio->len);
	
	// Set length
	cert->derlen = aBio->len;
	
	return cert;
		 
}


X509_STORE *X509_STORE_new( void )
{
	X509_STORE *certStore = (X509_STORE *)malloc(sizeof(X509_STORE));
	
	if (certStore)
	{
            certStore->iRootCerts = RPointerArray<CX509Certificate> (2);
            certStore->iRootCertsSCS = RPointerArray<CX509Certificate> (2);
            certStore->iCertChain = NULL;
            certStore->iSymbianCertStore = NULL;
	}
	else
	    {
	    xmlSecSetErrorFlag( KErrNoMemory );
	    }
	
	
	return certStore;
}

void X509_STORE_free(X509_STORE *aCertStore)
{
	if (aCertStore)
	    {
	    if (aCertStore->iCertChain)
	        {
	        delete aCertStore->iCertChain;
	        aCertStore->iCertChain = NULL;
	        }
	    if (aCertStore->iSymbianCertStore)
	        {
	        delete aCertStore->iSymbianCertStore;
	        aCertStore->iSymbianCertStore = NULL;
	        }
            aCertStore->iRootCertsSCS.ResetAndDestroy();
            aCertStore->iRootCerts.ResetAndDestroy();
            free(aCertStore);
	}
}

void doAddCertL(X509_STORE *aCertStore, X509 *aCert)
{
	// convert aCert to TDesC8
	TPtrC8 certPtr((const unsigned char*)aCert->der, aCert->derlen);   		
	CX509Certificate *cert = CX509Certificate::NewLC(certPtr);
	aCertStore->iRootCerts.AppendL((CX509Certificate *)cert);	
	CleanupStack::Pop(cert);
}

/* Add certificate to the cert store */
int X509_STORE_add_cert(X509_STORE *aCertStore, X509 *aCert)
{
	TInt err;

	TRAP(err, doAddCertL(aCertStore, aCert));
	
	return err;

}

void doCertChainInitL(X509_STORE *aCertStore, STACK_OF(X509) *aCert)
{
	if (aCertStore->iCertChain)
	{
		delete aCertStore->iCertChain;
		aCertStore->iCertChain = NULL;
	}
	aCertStore->iCertChain = CSymbianCertChain::NewL();
	aCertStore->iCertChain->InitializeL((unsigned char*)aCert->der, 
	                                                    (unsigned int)aCert->derlen, 
	                                                    aCertStore->iRootCerts);
}

/* Init certchain using certs from iRootCerts */
int X509_STORE_certchain_init (X509_STORE *aCertStore, STACK_OF(X509) *aCert)
{
	TInt err;

	TRAP(err, doCertChainInitL(aCertStore, aCert));
    if ( err != KErrNone )
        {
        xmlSecSetErrorFlag( err );            
        }
	return err;		   		
}

void doCertChainInitfromCertStoreL(X509_STORE *aCertStore, STACK_OF(X509) *aCert)
{
        TInt numCert ;
        RMPointerArray<CCTCertInfo> listCerts;        
        HBufC8* certdata;
        CX509Certificate* certX509;
        
        if (aCertStore->iCertChain)
            {
            delete aCertStore->iCertChain;
            aCertStore->iCertChain = NULL;
            }   
        if(!aCertStore->iSymbianCertStore)
            {
            aCertStore->iSymbianCertStore = CSymbianCertStore::NewL();
            aCertStore->iSymbianCertStore->CreateUnifiedCertStoreL();
            CActiveScheduler::Start();   
            User::LeaveIfError( aCertStore->iSymbianCertStore->GetError() );
            }
       if (aCertStore->iRootCertsSCS.Count())
            {
            aCertStore->iRootCertsSCS.ResetAndDestroy();    
            }
        aCertStore->iSymbianCertStore->ListCertL();
        CActiveScheduler::Start();
        User::LeaveIfError( aCertStore->iSymbianCertStore->GetError() );
        listCerts=aCertStore->iSymbianCertStore->GetCertList();
        numCert = listCerts.Count();
   	
        for (int i=0;i<numCert;i++)
            {
            CCTCertInfo* cert = (CCTCertInfo *)listCerts[i];          
            aCertStore->iSymbianCertStore->SetCert(cert);      
            aCertStore->iSymbianCertStore->RetrieveCertDataL();
            CActiveScheduler::Start();
            User::LeaveIfError( aCertStore->iSymbianCertStore->GetError() );
            certdata=aCertStore->iSymbianCertStore->GetRetrieveCertData();      
            certX509 = CX509Certificate::NewLC(*certdata);
            aCertStore->iRootCertsSCS.Append((CX509Certificate *)certX509);     
            CleanupStack::Pop(certX509);   
            }	
	aCertStore->iCertChain = CSymbianCertChain::NewL();
	aCertStore->iCertChain->InitializeL((unsigned char*)aCert->der, 
	                                                (unsigned int)aCert->derlen,
	                                                aCertStore->iRootCertsSCS);
}

/*Init certchain using root certs from SymbianCertStore stored in iRootCertsSCS */
int X509_STORE_certchain_init_fromCertStore (X509_STORE *aCertStore, STACK_OF(X509) *aCert)
{
	TInt err;
	TRAP(err, doCertChainInitfromCertStoreL(aCertStore, aCert));
	if ( err != KErrNone )
	    {
	    xmlSecSetErrorFlag( err );
	    }
	return err;		   		
}

int X509_STORE_certchain_validate (X509_STORE *aCertStore)
{
	TInt err;

	TRAP(err, aCertStore->iCertChain->ValidateL());
	
	if (err==KErrNone)
	    {
		CActiveScheduler::Start();
		err = aCertStore->iCertChain->GetError();
		if ( err != KErrNone )
		    {
		    xmlSecSetErrorFlag( err );
		    }
	    }
	else
	    {
	    xmlSecSetErrorFlag( err );
	    }
	
	return err;
		   		
}


int X509_STORE_certchain_getValidateResult (X509_STORE *aCertStore)
{
	TInt ret;

	ret = aCertStore->iCertChain->GetValidateResult();
	
	return ret;
		   		
}

#endif /* XMLSEC_NO_X509 */
