/*
   +----------------------------------------------------------------------+
   | Copyright (c) 2002-2005 J Smith <dark_panda@hushmail.com>            |
   | Crypto++ sources (not included) copyright (c) 1995-2003 Wei Dai      |
   +----------------------------------------------------------------------+
   | Check out COPYING for the exact license.                             |
   +----------------------------------------------------------------------+

$Id: jobjects.h,v 1.13 2005/01/06 18:16:42 jay Exp $
*/

#ifndef __JOBJECTS_H__
#define __JOBJECTS_H__


// This stuff is for PHP 5/ZE2. If it's not available,
// the macros are empty.

#if PHP_MAJOR_VERSION == 5

#define USE_PHP5_OBJECTS

zend_class_entry *cryptopp_ce_cipher;
zend_class_entry *cryptopp_ce_hash;
zend_class_entry *cryptopp_ce_exception;

static zend_object_handlers cryptopp_object_handlers_cipher;
static zend_object_handlers cryptopp_object_handlers_hash;
static zend_object_handlers cryptopp_object_handlers_exception;

function_entry cryptopp_cipher_methods[] = {
	PHP_ME_MAPPING(CryptoppCipher,		cryptopp_create_cipher,				NULL)
	PHP_ME_MAPPING(setRandIV,			cryptopp_set_rand_iv,				NULL)
	PHP_ME_MAPPING(setIV,				cryptopp_set_iv,					NULL)
	PHP_ME_MAPPING(getIV,				cryptopp_get_iv,					NULL)
	PHP_ME_MAPPING(setMode,				cryptopp_set_mode,					NULL)
	PHP_ME_MAPPING(getMode,				cryptopp_get_mode,					NULL)
	PHP_ME_MAPPING(setPadding,			cryptopp_set_padding,				NULL)
	PHP_ME_MAPPING(getPadding,			cryptopp_get_padding,				NULL)
	PHP_ME_MAPPING(setRNG,				cryptopp_set_rng,					NULL)
	PHP_ME_MAPPING(getRNG,				cryptopp_get_rng,					NULL)
	PHP_ME_MAPPING(setPlaintext,		cryptopp_set_plaintext,				NULL)
	PHP_ME_MAPPING(getPlaintext,		cryptopp_get_plaintext,				NULL)
	PHP_ME_MAPPING(setCiphertext,		cryptopp_set_ciphertext,			NULL)
	PHP_ME_MAPPING(getCiphertext,		cryptopp_get_ciphertext,			NULL)
	PHP_ME_MAPPING(setKey,				cryptopp_set_key,					NULL)
	PHP_ME_MAPPING(getKey,				cryptopp_get_key,					NULL)
	PHP_ME_MAPPING(setKeyLength,		cryptopp_set_key_length,			NULL)
	PHP_ME_MAPPING(getKeyLength,		cryptopp_get_key_length,			NULL)
	PHP_ME_MAPPING(getDefaultKeyLength,	cryptopp_get_default_key_length,	NULL)
	PHP_ME_MAPPING(getMaxKeyLength,		cryptopp_get_max_key_length,		NULL)
	PHP_ME_MAPPING(getMinKeyLength,		cryptopp_get_min_key_length,		NULL)
	PHP_ME_MAPPING(getMultKeyLength,	cryptopp_get_mult_key_length,		NULL)
	PHP_ME_MAPPING(getValidKeyLength,	cryptopp_get_valid_key_length,		NULL)
	PHP_ME_MAPPING(getBlockSize,		cryptopp_get_block_size,			NULL)
	PHP_ME_MAPPING(setRounds,			cryptopp_set_rounds,				NULL)
	PHP_ME_MAPPING(getRounds,			cryptopp_get_rounds,				NULL)
	PHP_ME_MAPPING(getCipherName,		cryptopp_get_cipher_name,			NULL)
	PHP_ME_MAPPING(getModeName,			cryptopp_get_mode_name,				NULL)
	PHP_ME_MAPPING(getPaddingName,		cryptopp_get_padding_name,			NULL)
	PHP_ME_MAPPING(getRNGName,			cryptopp_get_rng_name,				NULL)
	PHP_ME_MAPPING(getCipherType,		cryptopp_get_cipher_type,			NULL)
	PHP_ME_MAPPING(encrypt,				cryptopp_encrypt,					NULL)
	PHP_ME_MAPPING(decrypt,				cryptopp_decrypt,					NULL)
	PHP_ME_MAPPING(encryptFile,			cryptopp_encrypt_file,				NULL)
	PHP_ME_MAPPING(decryptFile,			cryptopp_decrypt_file,				NULL)


	// just some aliases, as 'stream' is now more accurate than 'file'...

	PHP_ME_MAPPING(encryptStream,		cryptopp_encrypt_file,				NULL)
	PHP_ME_MAPPING(decryptStream,		cryptopp_decrypt_file,				NULL)


	// functions specific to the RC2 algorithm...

	PHP_ME_MAPPING(setEffectiveKeyLength,	cryptopp_set_effective_key_length,	NULL)
	PHP_ME_MAPPING(getEffectiveKeyLength,	cryptopp_get_effective_key_length,	NULL)
	{NULL, NULL, NULL}
};


// hash functions...

function_entry cryptopp_hash_methods[] = {
	PHP_ME_MAPPING(CryptoppHash, 			cryptopp_create_hash,			NULL)
	PHP_ME_MAPPING(setPlaintext,			cryptopp_set_hash_plaintext,	NULL)
	PHP_ME_MAPPING(getPlaintext,			cryptopp_get_hash_plaintext,	NULL)
	PHP_ME_MAPPING(setHash,					cryptopp_set_hashtext,			NULL)
	PHP_ME_MAPPING(getHash,					cryptopp_get_hashtext,			NULL)
	PHP_ME_MAPPING(getDigestSize,			cryptopp_get_digest_size,		NULL)
	PHP_ME_MAPPING(hash,					cryptopp_hash,					NULL)
	PHP_ME_MAPPING(hashFile,				cryptopp_hash_file,				NULL)
	PHP_ME_MAPPING(validate,				cryptopp_validate_hash,			NULL)
	PHP_ME_MAPPING(getHashName,				cryptopp_get_hash_name,			NULL)
	PHP_ME_MAPPING(getHashType,				cryptopp_get_hash_type,			NULL)


	// another alias...

	PHP_ME_MAPPING(hashStream,				cryptopp_hash_file,				NULL)


	// additional functions for setting HMAC keys...

	PHP_ME_MAPPING(setHMACKey,				cryptopp_set_hmac_key,			NULL)
	PHP_ME_MAPPING(getHMACKey,				cryptopp_get_hmac_key,			NULL)
	PHP_ME_MAPPING(setHMACKeyLength,		cryptopp_set_hmac_key_length,	NULL)
	PHP_ME_MAPPING(getHMACKeyLength,		cryptopp_get_hmac_key_length,	NULL)
	{NULL, NULL, NULL}
};


// exception class...

function_entry cryptopp_exception_methods[] = {
	{NULL, NULL, NULL}
};

typedef enum {
	is_cipher,
	is_hash
} cryptopp_obj_type;


struct cryptopp_obj {
	zend_object std;
	int rsrc_id;
	cryptopp_obj_type type;
	union {
		JBase* cipher;
		JHash* hash;
	} u;
};


static void cryptopp_object_free_storage(zend_object *object TSRMLS_DC)
{
	cryptopp_obj *obj = (cryptopp_obj *) object;
	zend_hash_destroy(obj->std.properties);
	FREE_HASHTABLE(obj->std.properties);
	if (obj->u.cipher || obj->u.hash) {
		zend_list_delete(obj->rsrc_id);
	}
	efree(obj);
}

static void cryptopp_object_new(zend_class_entry *class_type, zend_object_handlers *handlers, zend_object_value *retval TSRMLS_DC)
{
	zval *tmp;
	cryptopp_obj *obj = (cryptopp_obj *) emalloc(sizeof(cryptopp_obj));
	memset(obj, 0, sizeof(cryptopp_obj));
	obj->std.ce = class_type;

	ALLOC_HASHTABLE(obj->std.properties);
	zend_hash_init(obj->std.properties, 0, NULL, ZVAL_PTR_DTOR, 0);
	zend_hash_copy(obj->std.properties, &class_type->default_properties, (copy_ctor_func_t) zval_add_ref, (void *) &tmp, sizeof(zval *));
	retval->handle = zend_objects_store_put(obj, NULL, cryptopp_object_free_storage, NULL TSRMLS_CC);
	retval->handlers = handlers;
}


static zend_object_value cryptopp_object_new_cipher(zend_class_entry *class_type TSRMLS_DC)
{
	zend_object_value retval;
	cryptopp_object_new(class_type, &cryptopp_object_handlers_cipher, &retval TSRMLS_CC);
	return retval;
}

static zend_object_value cryptopp_object_new_hash(zend_class_entry *class_type TSRMLS_DC)
{
	zend_object_value retval;
	cryptopp_object_new(class_type, &cryptopp_object_handlers_hash, &retval TSRMLS_CC);
	return retval;
}

static zend_object_value cryptopp_object_new_exception(zend_class_entry *class_type TSRMLS_DC)
{
	zend_object_value retval;
	cryptopp_object_new(class_type, &cryptopp_object_handlers_exception, &retval TSRMLS_CC);
	return retval;
}


#define CRYPTOPP_REGISTER_CLASS(name, obj_name, parent) \
	{ \
		zend_class_entry ce; \
		INIT_CLASS_ENTRY(ce, obj_name, cryptopp_ ## name ##_methods); \
		ce.create_object = cryptopp_object_new_ ## name; \
		cryptopp_ce_ ## name = zend_register_internal_class_ex(&ce, parent, NULL TSRMLS_CC);  \
		memcpy(&cryptopp_object_handlers_ ## name, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); \
		cryptopp_object_handlers_ ## name.clone_obj = NULL; \
		cryptopp_ce_ ## name->ce_flags |= ZEND_ACC_FINAL_CLASS; \
	}


// Register resources. If we're using an object, put it into the object store.

#define CRYPTOPP_REGISTER_RESOURCE(obj_type, return_value, res, le) \
	{ \
		int rsrc_id = ZEND_REGISTER_RESOURCE(object ? NULL : return_value, res, le); \
		if (object) { \
			cryptopp_obj *obj = (cryptopp_obj *) zend_object_store_get_object(object TSRMLS_CC); \
			obj->u.obj_type = res; \
			obj->rsrc_id = rsrc_id; \
			obj->type = is_ ## obj_type; \
		} \
	}

#define CRYPTOPP_REGISTER_CIPHER_RESOURCE(return_value, res, le) \
	CRYPTOPP_REGISTER_RESOURCE(cipher, return_value, res, le)

#define CRYPTOPP_REGISTER_HASH_RESOURCE(return_value, res, le) \
	CRYPTOPP_REGISTER_RESOURCE(hash, return_value, res, le)


// These are for parsing parameters and getting the actual object from the store.

#define CRYPTOPP_GET_THIS() \
	zval* object = getThis();

#define CRYPTOPP_SET_OBJ(type) \
	cryptopp_obj *obj = (cryptopp_obj *) zend_object_store_get_object(object TSRMLS_CC); \
	type = obj->u.type;

#define CRYPTOPP_OBJ_PARAMS(type, params) \
	CRYPTOPP_GET_THIS(); \
	if (object) { \
		if (params == FAILURE) { \
			RETURN_FALSE; \
		} \
		CRYPTOPP_SET_OBJ(type) \
	} \
	else

#define CRYPTOPP_OBJ_NO_PARAMS(type) \
	CRYPTOPP_GET_THIS(); \
	if (object) { \
		if (ZEND_NUM_ARGS() != 0) { \
			php_error(E_WARNING, "didn't expect any arguments in %s()", get_active_function_name(TSRMLS_C)); \
		} \
		CRYPTOPP_SET_OBJ(type) \
	} \
	else

#define CRYPTOPP_CIPHER_OBJ_PARAMS(params)	CRYPTOPP_OBJ_PARAMS(cipher, params)
#define CRYPTOPP_CIPHER_OBJ_NO_PARAMS()		CRYPTOPP_OBJ_NO_PARAMS(cipher)

#define CRYPTOPP_HASH_OBJ_PARAMS(params)	CRYPTOPP_OBJ_PARAMS(hash, params)
#define CRYPTOPP_HASH_OBJ_NO_PARAMS()		CRYPTOPP_OBJ_NO_PARAMS(hash)


// For throwing exceptions. If exceptions are disabled or we're in the
// structured API, fallback to PHP's normal error handling...

#define CRYPTOPP_SET_ERROR_HANDLING() \
	if (!CRYPTOPP_GET_INI(ini_disable_exceptions) && object) { \
		php_set_error_handling(EH_THROW, cryptopp_ce_exception TSRMLS_CC); \
	}

#define CRYPTOPP_RESTORE_ERROR_HANDLING() \
	php_std_error_handling();


// The level is for PHP's error system. If we aren't in the OO API or
// exceptions are disabled, we fall back to php_error()...

#define CRYPTOPP_THROW(level, message) \
	if (!CRYPTOPP_GET_INI(ini_disable_exceptions) && object) { \
		zend_throw_exception_ex(cryptopp_ce_exception, 0 TSRMLS_CC, message); \
	} \
	else { \
		php_error(level, message); \
	}

#define CRYPTOPP_THROW_ARG(level, message, arg) \
	if (!CRYPTOPP_GET_INI(ini_disable_exceptions) && object) { \
		zend_throw_exception_ex(cryptopp_ce_exception, 0 TSRMLS_CC, message, arg); \
	} \
	else { \
		php_error(level, message, arg); \
	}


#else // End of PHP5-specific macros


// This stuff is for PHP 4. They're empty on purpose, obviously.

#define CRYPTOPP_GET_THIS()
#define CRYPTOPP_CIPHER_OBJ_PARAMS(params)
#define CRYPTOPP_CIPHER_OBJ_NO_PARAMS()
#define CRYPTOPP_HASH_OBJ_PARAMS(params)
#define CRYPTOPP_HASH_OBJ_NO_PARAMS()
#define CRYPTOPP_REGISTER_CLASS(name, obj_name, parent)
#define CRYPTOPP_SET_ERROR_HANDLING()
#define CRYPTOPP_RESTORE_ERROR_HANDLING()

#define CRYPTOPP_REGISTER_CIPHER_RESOURCE(return_value, res, le) \
	ZEND_REGISTER_RESOURCE(return_value, res, le);

#define CRYPTOPP_REGISTER_HASH_RESOURCE(return_value, res, le) \
	ZEND_REGISTER_RESOURCE(return_value, res, le);

#define CRYPTOPP_THROW(level, message) \
	php_error(level, message);

#define CRYPTOPP_THROW_ARG(level, message, arg) \
	php_error(level, message, arg);

#endif // End of PHP4-specific macros


// These are for both PHP 4 and 5

#define CRYPTOPP_CIPHER_RSRC_PARAMS(params) \
	if (params == FAILURE) { \
		RETURN_FALSE; \
	} \
	else { \
		ZEND_FETCH_RESOURCE(cipher, JBase*, &resource, -1, "cryptopp cipher", le_cryptopp_cipher); \
	}

#define CRYPTOPP_CIPHER_RSRC_NO_PARAMS() \
	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &resource) == FAILURE) { \
		RETURN_FALSE; \
	} \
	else { \
		ZEND_FETCH_RESOURCE(cipher, JBase*, &resource, -1, "cryptopp cipher", le_cryptopp_cipher); \
	}


#define CRYPTOPP_HASH_RSRC_PARAMS(params) \
	if (params == FAILURE) { \
		RETURN_FALSE; \
	} \
	else { \
		ZEND_FETCH_RESOURCE(hash, JHash*, &resource, -1, "cryptopp hash", le_cryptopp_hash); \
	}

#define CRYPTOPP_HASH_RSRC_NO_PARAMS() \
	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &resource) == FAILURE) { \
		RETURN_FALSE; \
	} \
	else { \
		ZEND_FETCH_RESOURCE(hash, JHash*, &resource, -1, "cryptopp hash", le_cryptopp_hash); \
	}


static ZEND_RSRC_DTOR_FUNC(destroy_cipher)
{
	if (rsrc->ptr) {
		delete (JBase*) rsrc->ptr;
		rsrc->ptr = NULL;
	}
}

static ZEND_RSRC_DTOR_FUNC(destroy_hash)
{
	if (rsrc->ptr) {
		delete (JHash*) rsrc->ptr;
		rsrc->ptr = NULL;
	}
}


#endif

