/*
 * Copyright (c) 2000-2001 Apple Computer, Inc. All Rights Reserved.
 * 
 * The contents of this file constitute Original Code as defined in and are
 * subject to the Apple Public Source License Version 1.2 (the 'License').
 * You may not use this file except in compliance with the License. Please obtain
 * a copy of the License at http://www.apple.com/publicsource and read it before
 * using this file.
 * 
 * This Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS
 * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT
 * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
 * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the
 * specific language governing rights and limitations under the License.
 */


//
// Session_Cert.cpp - cert-related session functions.
//

#include "AppleX509CLSession.h"
#include "DecodedCert.h"
#include "CLCachedEntry.h"
#include "cldebugging.h"
#include <Security/oidscert.h>

void
AppleX509CLSession::CertDescribeFormat(
	uint32 &NumberOfFields,
	CSSM_OID_PTR &OidList)
{
	DecodedCert::describeFormat(*this, NumberOfFields, OidList);
}

void
AppleX509CLSession::CertGetAllFields(
	const CssmData &Cert,
	uint32 &NumberOfFields,
	CSSM_FIELD_PTR &CertFields)
{
	class DecodedCert decodedCert(*this, Cert);
	decodedCert.getAllParsedCertFields(NumberOfFields, CertFields);
}


CSSM_HANDLE
AppleX509CLSession::CertGetFirstFieldValue(
	const CssmData &EncodedCert,
	const CssmData &CertField,
	uint32 &NumberOfMatchedFields,
	CSSM_DATA_PTR &Value)
{
	NumberOfMatchedFields = 0;
	Value = NULL;
	CssmAutoData aData(*this);
	
	DecodedCert *decodedCert = new DecodedCert(*this, EncodedCert);
	uint32 numMatches;
	
	/* this returns false if field not there, throws on bad OID */
	bool brtn;
	try {
		brtn = decodedCert->getCertFieldData(CertField, 
			0, 				// index
			numMatches, 
			aData);
	}
	catch (...) {
		delete decodedCert;
		throw;
	}
	if(!brtn) {
		delete decodedCert;
		return CSSM_INVALID_HANDLE;
	}

	/* cook up a CLCachedCert, stash it in cache */
	CLCachedCert *cachedCert = new CLCachedCert(*decodedCert);
	cacheMap.addEntry(*cachedCert, cachedCert->handle());
	
	/* cook up a CLQuery, stash it */
	CLQuery *query = new CLQuery(
		CLQ_Cert, 
		CertField, 
		numMatches,
		false,				// isFromCache
		cachedCert->handle());
	queryMap.addEntry(*query, query->handle());
	
	/* success - copy field data to outgoing Value */
	Value = (CSSM_DATA_PTR)malloc(sizeof(CSSM_DATA));
	*Value = aData.release();
	NumberOfMatchedFields = numMatches;
	return query->handle();
}


bool
AppleX509CLSession::CertGetNextFieldValue(
	CSSM_HANDLE ResultsHandle,
	CSSM_DATA_PTR &Value)
{
	/* fetch & validate the query */
	CLQuery *query = queryMap.lookupEntry(ResultsHandle);
	if(query == NULL) {
		CssmError::throwMe(CSSMERR_CL_INVALID_RESULTS_HANDLE);
	}
	if(query->nextIndex() >= query->numFields()) {
		return false;
	}

	/* fetch the associated cached cert */
	CLCachedCert *cachedCert = lookupCachedCert(query->cachedObject());
	uint32 dummy;
	CssmAutoData aData(*this);
	if(!cachedCert->cert().getCertFieldData(query->fieldId(), 
		query->nextIndex(), 
		dummy,
		aData))  {
		return false;
	}
		
	/* success - copy field data to outgoing Value */
	Value = (CSSM_DATA_PTR)malloc(sizeof(CSSM_DATA));
	*Value = aData.release();
	query->incrementIndex();
	return true;
}

void
AppleX509CLSession::CertCache(
	const CssmData &EncodedCert,
	CSSM_HANDLE &CertHandle)
{
	DecodedCert *decodedCert = new DecodedCert(*this, EncodedCert);
	
	/* cook up a CLCachedCert, stash it in cache */
	CLCachedCert *cachedCert = new CLCachedCert(*decodedCert);
	cacheMap.addEntry(*cachedCert, cachedCert->handle());
	CertHandle = cachedCert->handle();
}

CSSM_HANDLE
AppleX509CLSession::CertGetFirstCachedFieldValue(
	CSSM_HANDLE CertHandle,
	const CssmData &CertField,
	uint32 &NumberOfMatchedFields,
	CSSM_DATA_PTR &Value)
{
	/* fetch the associated cached cert */
	CLCachedCert *cachedCert = lookupCachedCert(CertHandle);
	if(cachedCert == NULL) {
		CssmError::throwMe(CSSMERR_CL_INVALID_CACHE_HANDLE);
	}
	
	CssmAutoData aData(*this);
	uint32 numMatches;

	/* this returns false if field not there, throws on bad OID */
	if(!cachedCert->cert().getCertFieldData(CertField, 
			0, 				// index
			numMatches, 
			aData)) {
		return CSSM_INVALID_HANDLE;
	}

	/* cook up a CLQuery, stash it */
	CLQuery *query = new CLQuery(
		CLQ_Cert, 
		CertField, 
		numMatches,
		true,				// isFromCache
		cachedCert->handle());
	queryMap.addEntry(*query, query->handle());
	
	/* success - copy field data to outgoing Value */
	Value = (CSSM_DATA_PTR)malloc(sizeof(CSSM_DATA));
	*Value = aData.release();
	NumberOfMatchedFields = numMatches;
	return query->handle();
}


bool
AppleX509CLSession::CertGetNextCachedFieldValue(
	CSSM_HANDLE ResultsHandle,
	CSSM_DATA_PTR &Value)
{
	/* Identical to, so just call... */
	return CertGetNextFieldValue(ResultsHandle, Value);
}

void
AppleX509CLSession::CertAbortCache(
	CSSM_HANDLE CertHandle)
{
	/* fetch the associated cached cert, remove from map, delete it */
	CLCachedCert *cachedCert = lookupCachedCert(CertHandle);
	if(cachedCert == NULL) {
		errorLog0("CertAbortCache: cachedCert not found\n");
		CssmError::throwMe(CSSMERR_CL_INVALID_CACHE_HANDLE);
	}
	cacheMap.removeEntry(cachedCert->handle());
	delete cachedCert;
}

/*
 * Abort either type of cert field query (cache based or non-cache based)
 */
void
AppleX509CLSession::CertAbortQuery(
	CSSM_HANDLE ResultsHandle)
{
	/* fetch & validate the query */
	CLQuery *query = queryMap.lookupEntry(ResultsHandle);
	if(query == NULL) {
		CssmError::throwMe(CSSMERR_CL_INVALID_RESULTS_HANDLE);
	}

	if(!query->fromCache()) {
		/* the associated cached cert was created just for this query; dispose */
		CLCachedCert *cachedCert = lookupCachedCert(query->cachedObject());
		if(cachedCert == NULL) {
			/* should never happen */
			errorLog0("CertAbortQuery: cachedCert not found\n");
			CssmError::throwMe(CSSMERR_CL_INTERNAL_ERROR);
		}
		cacheMap.removeEntry(cachedCert->handle());
		delete cachedCert;
	}
	queryMap.removeEntry(query->handle());
	delete query;
}

void
AppleX509CLSession::CertCreateTemplate(
	uint32 NumberOfFields,
	const CSSM_FIELD CertFields[],
	CssmData &CertTemplate)
{
	/* cook up an empty Cert */
	DecodedCert cert(*this);

	/* grind thru specified fields; exceptions are fatal */
	for(uint32 dex=0; dex<NumberOfFields; dex++) {
		cert.setCertField(
			CssmOid::overlay(CertFields[dex].FieldOid), 
			CssmData::overlay(CertFields[dex].FieldValue));
	}
	
	/* TBD - ensure all required fields are set? We do this 
	 * when we sign the cert; maybe we should do it here. */
	
	/* 
	 * We have the CertificateToSign in snacc format. Encode.
	 */
	CssmRemoteData rData(*this, CertTemplate);
	cert.encodeTbs(rData);
	rData.release();
}


void
AppleX509CLSession::CertGetAllTemplateFields(
	const CssmData &CertTemplate,
	uint32 &NumberOfFields,
	CSSM_FIELD_PTR &CertFields)
{
	DecodedCert	cert(*this);		// empty
	cert.decodeTbs(CertTemplate);
	cert.getAllParsedCertFields(NumberOfFields, CertFields);
}

void
AppleX509CLSession::FreeFields(
	uint32 NumberOfFields,
	CSSM_FIELD_PTR &FieldArray)
{
	unsigned 		i;
	CSSM_FIELD_PTR 	thisField;
	CSSM_OID_PTR	thisOid;
	
	for(i=0; i<NumberOfFields; i++) {
		thisField = &FieldArray[i];
		thisOid = &thisField->FieldOid;
		
		/* oid-specific handling of value */
		/* TBD - if this fails, call tbd DecodedCRL::freeCertFieldData */
		/* BUG - the CssmRemoteData constructor clears the referent,
		 * iff the referent is a CSSSM_DATA (as opposed to a CssmData).
		 */
		CssmData &cData = CssmData::overlay(thisField->FieldValue);
		CssmRemoteData rData(*this, cData);
		DecodedCert::freeCertFieldData(CssmOid::overlay(*thisOid), rData);
		
		/* and the oid itself */
		free(thisOid->Data);
		thisOid->Data = NULL;
		thisOid->Length = 0;
	}
	free(FieldArray);
}

void
AppleX509CLSession::FreeFieldValue(
	const CssmData &CertOrCrlOid,
	CssmData *Value)
{
	if(Value == NULL) {
		CssmError::throwMe(CSSM_ERRCODE_INVALID_FIELD_POINTER);
	}
	CssmRemoteData cd(*this, *Value);
	/* TBD - if this fails, call tbd DecodedCRL::freeCertFieldData */
	DecodedCert::freeCertFieldData(CertOrCrlOid, cd);
	free(Value);
}

void
AppleX509CLSession::CertGroupFromVerifiedBundle(
	CSSM_CC_HANDLE CCHandle,
	const CSSM_CERT_BUNDLE &CertBundle,
	const CssmData *SignerCert,
	CSSM_CERTGROUP_PTR &CertGroup)
{
	unimplemented();
}

void
AppleX509CLSession::CertGroupToSignedBundle(
	CSSM_CC_HANDLE CCHandle,
	const CSSM_CERTGROUP &CertGroupToBundle,
	const CSSM_CERT_BUNDLE_HEADER *BundleInfo,
	CssmData &SignedBundle)
{
	unimplemented();
}

void
AppleX509CLSession::PassThrough(
	CSSM_CC_HANDLE CCHandle,
	uint32 PassThroughId,
	const void *InputParams,
	void **OutputParams)
{
	switch(PassThroughId) {
		case CSSM_APPLEX509CL_OBTAIN_CSR:
		{
			/*
			 * Create a Cert Signing Request (CSR).
			 * Input is a CSSM_APPLE_CL_CSR_REQUEST.
			 * Output is a PEM-encoded CertSigningRequest (SNACC type
			 * CertificationRequest from pkcs10). 
			 */
			if(InputParams == NULL) {
				CssmError::throwMe(CSSMERR_CL_INVALID_INPUT_POINTER);
			}
			if(OutputParams == NULL) {
				CssmError::throwMe(CSSMERR_CL_INVALID_OUTPUT_POINTER);
			}
			CSSM_APPLE_CL_CSR_REQUEST *csrReq = 
				(CSSM_APPLE_CL_CSR_REQUEST *)InputParams;
			if((csrReq->subjectNameX509 == NULL) ||
			(csrReq->signatureOid.Data == NULL) ||
			(csrReq->subjectPublicKey == NULL) ||
			(csrReq->subjectPrivateKey == NULL)) {
				CssmError::throwMe(CSSMERR_CL_INVALID_INPUT_POINTER);
			}
			CSSM_DATA_PTR csrPtr = NULL;
			generateCsr(CCHandle, csrReq, csrPtr);
			*OutputParams = csrPtr;
			break;
		}	
		case CSSM_APPLEX509CL_VERIFY_CSR:
		{
			/*
			 * Perform signature verify of a CSR.
			 * Input:  CSSM_DATA referring to a DER-encoded CSR.
			 * Output: Nothing, throws CSSMERR_CL_VERIFICATION_FAILURE
			 *         on failure.
			 */
			if(InputParams == NULL) {
				CssmError::throwMe(CSSMERR_CL_INVALID_INPUT_POINTER);
			}
			const CSSM_DATA *csrPtr = (const CSSM_DATA *)InputParams;
			verifyCsr(csrPtr);
			break;
		}	
		default:
			CssmError::throwMe(CSSMERR_CL_INVALID_PASSTHROUGH_ID);
	}
}

