/*____________________________________________________________________________
	pgpContext.c
	
	Copyright (C) 1997 Pretty Good Privacy, Inc.
	All rights reserved.

	$Id: pgpContext.c,v 1.65 1997/10/14 01:48:39 heller Exp $
____________________________________________________________________________*/
#include <stdio.h>

#include "pgpErrors.h"
#include "pgpMem.h"

#include "pgpContext.h"
#include "pgpEnv.h"
#include "pgpRandomPoolPriv.h"
#include "pgpRandomContext.h"
#include "pgpRandomX9_17.h"
#include "pgpRnd.h"
#include "pgpRndSeed.h"
#include "pgpRngPub.h"
#include "pgpDEBUGStartup.h"
#include "pflPrefs.h"
#include "pflContextPriv.h"
#include "pgpUtilitiesPriv.h"

#include "pgpTimeBomb.h"


enum
{
	kPGPContextMagic	= 0x436e7478	/* 'Cntx' */
};

struct PGPContext
{
	PFLContext		pflContext;
	
	PGPUInt32		magic;				/* Always kPGPContextMagic */
	PGPUInt32		clientAPIVersion;	/* version client compiled against */
	PGPUserValue	userValue;			/* Client storage */
	PGPKeySetRef	defaultKeySet;		/* Set by pgpOpenDefualtKeyRings() */
	
	PGPEnv		   *	pgpEnvironment;		/* State for within library */
	
	PGPRandomContext *	randomPoolContext;	/* ctxt around global rand pool*/
	PGPRandomContext *	randomPoolX9_17;	/* uses 'randomPoolContext' */
	PGPRandomContext *	dummyRandomContext;
	
	PGPBoolean			prefsChanged;		/* Have changed internal prefs */
	PGPPrefRef			prefsRef;
	
	RingPool	   *	ringPool;			/* Keyring data structures */
	
};

typedef struct PGPContext	PGPContext;


/*____________________________________________________________________________
	This is the default PGPsdk memory allocator. Note that context will be
	NULL when allocating the PGPContext structure itself.
____________________________________________________________________________*/

	static void *
pgpDefaultMemoryAllocationProc(
	PFLContextRef	context,
	PGPSize 		allocationSize,
	PGPMemoryFlags	flags,
	PGPUserValue 	userValue)
{
	void *	result	= NULL;
	
	(void) context;	/* Not used */
	(void) userValue;	/* Not used */
	(void) flags;		/* Not used */
	
	pgpAssertMsg( allocationSize != 0,
		"pgpDefaultMemoryAllocationProc: attempt to allocate 0 bytes" );

	result	= pgpAlloc( allocationSize );
	if ( IsntNull( result ) &&
		(flags & kPGPMemoryFlags_Clear ) != 0 )
	{
		pgpClearMemory( result, allocationSize );
	}
	
	return( result );
}

/*____________________________________________________________________________
	This is the default PGPsdk memory reallocator. 
____________________________________________________________________________*/

	static PGPError
pgpDefaultMemoryReallocationProc(
	PFLContextRef	context,
	void			**allocation,
	PGPSize 		newAllocationSize,
	PGPMemoryFlags	flags,
	PGPUserValue 	userValue)
{
	PGPError	err;
	
	(void) context;	/* Not used */
	(void) userValue;	/* Not used */
	(void) flags;		/* Not used */

	pgpAssertMsg( newAllocationSize != 0,
		"pgpDefaultMemoryAllocationProc: attempt to allocate 0 bytes" );
		
	err = pgpRealloc( allocation, newAllocationSize );
	
	return( err );
}

/*____________________________________________________________________________
	This is the default PGPsdk memory deallocator. Note that context will
	be NULL when deallocating the PGPContext structure itself.
____________________________________________________________________________*/

	static PGPError
pgpDefaultMemoryDeallocationProc(
	PFLContextRef	context,
	void *			allocation,
	PGPUserValue 	userValue)
{
	(void) context;	/* Not used */
	(void) userValue;	/* Not used */

	pgpFree( allocation );
	
	return( kPGPError_NoErr );
}

/*____________________________________________________________________________
	This function validates a context data structure.
____________________________________________________________________________*/

	PGPBoolean
pgpContextIsValid(PGPContextRef context)
{
	return( context != NULL &&
			context->magic == kPGPContextMagic );
}


/*____________________________________________________________________________
	Allocate a new PGPContext using custom memory allocators.
	This function es exported for the client library and should never be
	called by the shared library.
____________________________________________________________________________*/
	static PGPError
FinishInitContext( PGPContextRef  context )
{
	PGPError	err = kPGPError_NoErr;
		
	err = pgpenvCreate( context, &context->pgpEnvironment );
	if ( IsntPGPError( err ) )
	{
		context->ringPool = ringPoolCreate( context->pgpEnvironment );
		if ( IsNull( context->ringPool ) )
		{
			err	= kPGPError_OutOfMemory;
		}
	}
	
	/* only actually seeds it the first time */
	(void)pgpSeedGlobalRandomPool( context );
	
	return( err );
}

/*____________________________________________________________________________
	Allocate a new PGPContext using custom memory allocators.
	This function es exported for the client library and should never be
	called by the shared library.
____________________________________________________________________________*/

	PGPError
pgpNewContextCustomInternal(
	PGPUInt32					clientAPIVersion,
	PGPNewContextStruct const *	custom,
	PGPContextRef*				newContext)
{
	PGPError			err = kPGPError_NoErr;
	PGPContextRef		context	= NULL;
	PFLContextRef		temp	= NULL;
	PFLNewContextStruct	pflCustom;

	PGPValidatePtr( newContext );
	*newContext	= NULL;
	PGPValidatePtr( custom );
	/*	Note: if the struct grows, then
		sizeof( PGPNewContextStruct ) will need to be a fixed number */ 
	PGPValidateParam( custom->sizeofStruct >= sizeof( PGPNewContextStruct ) );
	PGPValidatePtr( custom->allocProc );
	PGPValidatePtr( custom->reallocProc );
	PGPValidatePtr( custom->deallocProc );
	
	if ( ! pgpsdkIsInited() )
	{
		return( kPGPError_ImproperInitialization );
	}

	if ( PGPMajorVersion( clientAPIVersion ) >
			PGPMajorVersion( kPGPsdkAPIVersion ) ||
			PGPMajorVersion( clientAPIVersion ) == 0  )
	{
		return( kPGPError_IncompatibleAPI );
	}
	
#if PGP_TIME_BOMB
	if ( pgpTimeBombHasExpired() )
	{
		*newContext	= NULL;
		return( kPGPError_FeatureNotAvailable );
	}
#endif

	PGPDebugStartup();

	
	pgpClearMemory( &pflCustom, sizeof( pflCustom ) );
	pflCustom.allocProc		= (PFLMemoryAllocationProc)custom->allocProc;
	pflCustom.reallocProc	=
			(PFLMemoryReallocationProc)custom->reallocProc;
	pflCustom.deallocProc		=
			(PFLMemoryDeallocationProc)custom->deallocProc;
	pflCustom.allocUserValue	= custom->allocUserValue;
	
	err	= PFLNewContextCustom( sizeof( PGPContext ), &pflCustom, &temp );
	context	= (PGPContextRef)temp;
	
	if( IsntPGPError( err ) )
	{
		context->magic				= kPGPContextMagic;
		context->clientAPIVersion	= clientAPIVersion;
		context->defaultKeySet		= kPGPInvalidRef;
		
		err	= FinishInitContext( context );

		if( IsPGPError( err ) )
		{
			PGPFreeContext( context );
			context = kPGPInvalidRef;
		}
	}
	else
	{
		err = kPGPError_OutOfMemory;
	}
	
	*newContext	= context;
	
	return( err );
}

/*____________________________________________________________________________
	Allocate a new PGPContext using the default PGPsdk memory allocators.
	This function es exported for the client library and should never be
	called by the shared library.
____________________________________________________________________________*/

	PGPError
pgpNewContextInternal(
	PGPUInt32 		clientAPIVersion,
	PGPContextRef *	newContext)
{
	PGPNewContextStruct		custom;
	PGPError				err	= kPGPError_NoErr;
	
	pgpAssert( IsntNull( newContext ) );
	*newContext	= NULL;
	
	pgpClearMemory( &custom, sizeof( custom ) );
	custom.sizeofStruct	= sizeof( custom );
	custom.allocProc	= (PGPMemoryAllocationProc)
							pgpDefaultMemoryAllocationProc;
	custom.reallocProc	= (PGPMemoryReallocationProc)
							pgpDefaultMemoryReallocationProc;
	custom.deallocProc	= (PGPMemoryDeallocationProc)
							pgpDefaultMemoryDeallocationProc;
	
	err	= pgpNewContextCustomInternal( clientAPIVersion, &custom, newContext);
	
	return( err );
}




/*____________________________________________________________________________
	Allocate a new PGPContext using the default PGPsdk memory allocators.
____________________________________________________________________________*/

	PGPError 
PGPNewContext(
	PGPUInt32		clientAPIVersion,
	PGPContextRef *	newContext)
{
	PGPValidatePtr( newContext );
	*newContext	= NULL;
	
	return( pgpNewContextInternal( clientAPIVersion, newContext ) );
}


/*____________________________________________________________________________
	Allocate a new custom PGPContext.
____________________________________________________________________________*/
	PGPError
PGPNewContextCustom(
	PGPUInt32						sdkVersion,
	PGPNewContextStruct const *		custom,
	PGPContextRef 					*newContext)
{
	PGPError	err	= kPGPError_NoErr;
	
	PGPValidatePtr( newContext );
	*newContext	= kPGPInvalidRef;
	PGPValidatePtr( custom );
	
	err	= pgpNewContextCustomInternal( sdkVersion, custom, newContext );
	
	return( err );
}


	static void
sDestroyContext( PGPContextRef	context )
{
	if ( IsntNull( context->ringPool ) )
	{
		ringPoolDestroy ( context->ringPool );
		context->ringPool	= NULL;
	}
		
	if( IsntNull( context->pgpEnvironment ) )
	{
		pgpenvDestroy( context->pgpEnvironment );
		context->pgpEnvironment	= NULL;
	}
	
	if ( IsntNull( context->randomPoolContext ) )
	{
		pgpRandomDestroy( context->randomPoolContext );
		context->randomPoolContext	= NULL;
	}
	
	if ( IsntNull( context->randomPoolX9_17 ) )
	{
		pgpRandomDestroy( context->randomPoolX9_17 );
		context->randomPoolX9_17	= NULL;
	}
	
	if ( IsntNull( context->dummyRandomContext ) )
	{
		pgpRandomDestroy( context->dummyRandomContext );
		context->dummyRandomContext	= NULL;
	}
	
	if ( IsntNull( context->prefsRef ) )
	{
		PGPClosePrefFile( context->prefsRef );
	}
}

/*____________________________________________________________________________
	Delete an existing PGPContext and all resources associated with it.
____________________________________________________________________________*/

	PGPError 
PGPFreeContext(PGPContextRef context)
{
	PGPError	err = kPGPError_NoErr;
	
	PGPValidateContext( context );
	
	/* Get current time into rand pool */
	PGPGlobalRandomPoolAddKeystroke( PGPGetTime() );
	err	= pgpSaveGlobalRandomPool( context );
	
	sDestroyContext( context );
	
	PFLFreeContext( (PFLContextRef)context );
	
	return( err );
}



/*____________________________________________________________________________
	Allocate a block of memory using the allocator stored in a PGPContext.
____________________________________________________________________________*/

	void *
pgpContextMemAlloc(
	PGPContextRef 	context,
	PGPSize 		requestSize,
	PGPMemoryFlags	flags)
{
	void	*	allocation = NULL;
	PGPSize		allocationSize	= requestSize;
	
	pgpAssert( pgpContextIsValid( context ) );
	
	allocation = PFLContextMemAlloc( &context->pflContext,
					allocationSize, flags );
	
	return( allocation );
}

/*____________________________________________________________________________
	Same as pgpContextMemAlloc(), but copies data into newly allocated block.
____________________________________________________________________________*/

	void *
PGPContextMemAllocCopy(
	PGPContextRef 	context,
	PGPSize 		requestSize,
	PGPMemoryFlags	flags,
	const void *	dataToCopy )
{
	pgpAssert( pgpContextIsValid( context ) );
	
	/* note that clear flag is unneeded since we'll copy all the data */
	return( PFLContextMemAllocCopy( &context->pflContext,
				requestSize, flags & ~kPGPMemoryFlags_Clear,
				dataToCopy) );
}


/*____________________________________________________________________________
	Allocate a block of memory using the allocator stored in a PGPContext.
____________________________________________________________________________*/

	PGPError
pgpContextMemRealloc(
	PGPContextRef 	context,
	void			**allocation,
	PGPSize 		requestSize,
	PGPMemoryFlags	flags)
{
	PGPError	err	= kPGPError_NoErr;
	
	err = PFLContextMemRealloc( &context->pflContext,
			allocation, requestSize, flags );
	return( err );
}

/*____________________________________________________________________________
	Free a block of memory using the deallocator stored in a PGPContext.
____________________________________________________________________________*/

	void
pgpContextMemFree(
	PGPContextRef 	context,
	void * 			allocation)
{
	pgpAssert( pgpContextIsValid( context ) );
	
	PFLContextMemFree( &context->pflContext, allocation );
}
	
	PGPError 
PGPGetContextUserValue(
	PGPContextRef 	context,
	PGPUserValue 	*userValue)
{
	PGPError	err = kPGPError_NoErr;
	
	PGPValidatePtr( userValue );
	*userValue	= NULL;
	PGPValidateContext( context );
	
	*userValue = context->userValue;
	
	return( err );
}


	PFLContextRef
pgpContextGetPFLContext( PGPContextRef context )
{
	if ( ! pgpContextIsValid( context ) )
		return( NULL );
		
	return( &context->pflContext );
}


	PGPError 
PGPSetContextUserValue(
	PGPContextRef 	context,
	PGPUserValue 	userValue)
{
	PGPError	err = kPGPError_NoErr;
	
	PGPValidateContext( context );
	
	context->userValue = userValue;
	
	return( err );
}

	PGPEnv *
pgpContextGetEnvironment(PGPContextRef context)
{
	pgpAssert( pgpContextIsValid( context ) );

	return( context->pgpEnvironment );
}


	static PGPError
sCreateRandomContext( 
	PGPContextRef			context,
	PGPRandomVTBL const *	vtbl,
	PGPRandomContext **		outRandom )
{
	PGPError			err	= kPGPError_NoErr;
	PGPRandomContext *	newRandom	= NULL;
	
	pgpAssert( pgpContextIsValid( context ) );
	
		
	newRandom	= (PGPRandomContext *)
		pgpContextMemAlloc( context, sizeof( *newRandom ),
		kPGPMemoryFlags_Clear );
	if ( IsntNull( newRandom ) )
	{
		newRandom->context	= context;
		newRandom->vtbl		= vtbl;
		newRandom->priv		= NULL;
		newRandom->destroy	= NULL;
	}
	else
	{
		err	= kPGPError_OutOfMemory;
	}

	*outRandom	= newRandom;
	return( err );
}



	PGPRandomContext *
pgpContextGetGlobalPoolRandomContext(PGPContextRef context)
{
	pgpAssert( pgpContextIsValid( context ) );
	
	if ( IsNull( context->randomPoolContext ) )
	{
		PGPRandomContext *	randomContext	= NULL;
		
		(void)sCreateRandomContext( context,
			pgpGetGlobalRandomPoolVTBL(), &randomContext );
		context->randomPoolContext	= randomContext;
		
		if ( IsntNull( context->randomPoolContext ) )
		{
			/*
			* in case there is a problem in reading the random pool,
			* feed in some amount of "random" data to avoid a totally
			* predictible pool
			*/
			pgpRandomCollectEntropy( randomContext );
			pgpRandomCollectEntropy( randomContext );
			pgpRandomCollectEntropy( randomContext );
			pgpRandomCollectEntropy( randomContext );
		}
	}
	/* NOTE: someone must still call pgpSeedGlobalRandomPool() */

	return( context->randomPoolContext );
}



	PGPRandomContext *
pgpContextGetDummyRandomContext(PGPContextRef context)
{
	pgpAssert( pgpContextIsValid( context ) );
	
	if ( IsNull( context->dummyRandomContext ) )
	{
		(void)sCreateRandomContext( context,
			pgpGetGlobalDummyRandomPoolVTBL(),
			&context->dummyRandomContext );
	}

	return( context->dummyRandomContext );
}



	PGPRandomContext *
pgpContextGetX9_17RandomContext(PGPContextRef context)
{
	pgpAssert( pgpContextIsValid( context ) );
	
	if ( IsNull( context->randomPoolX9_17 ) )
	{
		context->randomPoolX9_17	= pgpRandomCreate( context );
	}

	return( context->randomPoolX9_17 );
}


/*____________________________________________________________________________
    Fill the user-specified buffer with random values from the global
    random pool.  If we were not able to initialize the random pool
    we return kPGPError_OutOfEntropy.
____________________________________________________________________________*/
	PGPError
PGPContextGetRandomBytes(
	PGPContextRef	context,
	void *			buf,
	PGPSize			len
	)
{
	PGPRandomContext *	randomContext;

	PGPValidatePtr( buf );
	PGPValidateParam( pgpContextIsValid( context ) );

	randomContext = pgpContextGetX9_17RandomContext( context );
	if( IsNull( randomContext )
		|| !PGPGlobalRandomPoolHasMinimumEntropy() )
	{
		return kPGPError_OutOfEntropy;
	}

	pgpRandomGetBytes( randomContext, buf, len );
	
	return kPGPError_NoErr;
}


	RingPool *
pgpContextGetRingPool(PGPContextRef context)
{
	pgpAssert( pgpContextIsValid( context ) );

	return( context->ringPool );
}

	void
pgpContextSetRingPool(
	PGPContextRef 		context,
	RingPool			*ringPool)
{
	pgpAssert( pgpContextIsValid( context ) );

	context->ringPool = ringPool;
}


	PGPKeySetRef
pgpContextGetDefaultKeySet(PGPContextRef context)
{
	pgpAssert( pgpContextIsValid( context ) );

	return( context->defaultKeySet );
}

/* We may retire the prefs stuff, but this is for transition at least */
	void
pgpContextSetPrefsChanged(
	PGPContextRef	context,
	PGPBoolean		prefsChanged
	)
{
	context->prefsChanged = prefsChanged;
}

	PGPBoolean
pgpContextArePrefsChanged(
	PGPContextRef	context
	)
{
	return context->prefsChanged;
}


	PGPPrefRef
pgpContextGetPrefs( PGPContextRef	context)
{
	return( context->prefsRef );
}

	void
pgpContextSetPrefs(
	PGPContextRef	context,
	PGPPrefRef		prefs )
{
	context->prefsRef = prefs;
}


/*__Editor_settings____

	Local Variables:
	tab-width: 4
	End:
	vi: ts=4 sw=4
	vim: si
_____________________*/
