/*
 * Copyright (C) 2012-2015 Tobias Brunner
 * Copyright (C) 2016-2019 Andreas Steffen
 * HSR Hochschule fuer Technik Rapperswil
 *
 * Copyright (C) 2011 Martin Willi
 * Copyright (C) 2011 revosec AG
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2 of the License, or (at your
 * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * for more details.
 */

/**
 * @defgroup plugin_feature plugin_feature
 * @{ @ingroup plugins
 */

#ifndef PLUGIN_FEATURE_H_
#define PLUGIN_FEATURE_H_

typedef struct plugin_feature_t plugin_feature_t;

#include <library.h>
#include <eap/eap.h>
#include <plugins/plugin.h>
#include <credentials/containers/container.h>

/**
 * Callback function of a plugin to (un-)register a specified feature.
 *
 * @param plugin			plugin instance
 * @param feature			feature to register
 * @param reg				TRUE to register, FALSE to unregister
 * @param cb_data			user data passed with callback function
 * @return					TRUE if registered successfully
 */
typedef bool (*plugin_feature_callback_t)(plugin_t *plugin,
										  plugin_feature_t *feature,
										  bool reg,void *cb_data);

/**
 * Feature a plugin provides or depends on, including registration functions.
 *
 * Each plugin returns a list of plugin features, allowing the plugin loader
 * to resolve dependencies and register the feature. FEATURE_PROVIDE defines
 * features provided by the plugin, hard (DEPENDS) or soft (SDEPEND) dependency
 * specified is related to the previously defined PROVIDE feature.
 * If a plugin feature requires to hook in functionality into the library
 * or a daemon, it can use REGISTER or CALLBACK entries. Each PROVIDE feature
 * uses the REGISTER/CALLBACK entry defined previously. The REGISTER entry
 * defines a common feature registration function directly passed to the
 * associated manager or factory (crypto/credential factory etc.). A callback
 * function is more generic allows the loader to invoke a callback to do
 * the registration. PROVIDE features that do not use a registration or callback
 * function must be listed before any REGISTER/CALLBACK entry, or use the NOOP
 * helper macro.
 *
 * To conveniently create feature lists, use the macros PLUGIN_REGISTER,
 * PLUGIN_CALLBACK, PLUGIN_NOOP, PLUGIN_PROVIDE, PLUGIN_DEPENDS and
 * PLUGIN_SDEPEND. Use indentation to show how the registration functions
 * and dependencies are related to a provided feature, such as:
 *
 * @verbatim
	// two features, one with two dependencies, both use a callback to register
	PLUGIN_CALLBACK(...),
		PLUGIN_PROVIDE(...),
			PLUGIN_DEPENDS(...),
			PLUGIN_SDEPEND(...),
		PLUGIN_PROVIDE(...),
	// common constructor to register for a feature with one dependency
	PLUGIN_REGISTER(...),
		PLUGIN_PROVIDE(...),
			PLUGIN_DEPENDS(...),
	// feature that does not use a registration function
	PLUGIN_NOOP,
		PLUGIN_PROVIDE(...),
	@endverbatim
 */
struct plugin_feature_t {
	/** kind of entry */
	enum {
		/* plugin provides this feature */
		FEATURE_PROVIDE,
		/* a feature depends on this feature, hard dependency */
		FEATURE_DEPENDS,
		/* a feature can optionally use this feature, soft dependency */
		FEATURE_SDEPEND,
		/* register the specified function for all following features */
		FEATURE_REGISTER,
		/* use a callback to register all following features */
		FEATURE_CALLBACK,
	} kind;
	/* type of feature */
	enum {
		/** not a feature */
		FEATURE_NONE,
		/** crypter_t */
		FEATURE_CRYPTER,
		/** aead_t */
		FEATURE_AEAD,
		/** signer_t */
		FEATURE_SIGNER,
		/** hasher_t */
		FEATURE_HASHER,
		/** prf_t */
		FEATURE_PRF,
		/** xof_t */
		FEATURE_XOF,
		/** drbg_t */
		FEATURE_DRBG,
		/** diffie_hellman_t */
		FEATURE_DH,
		/** rng_t */
		FEATURE_RNG,
		/** nonce_gen_t */
		FEATURE_NONCE_GEN,
		/** generic private key support */
		FEATURE_PRIVKEY,
		/** generating new private keys */
		FEATURE_PRIVKEY_GEN,
		/** private_key_t->sign() */
		FEATURE_PRIVKEY_SIGN,
		/** private_key_t->decrypt() */
		FEATURE_PRIVKEY_DECRYPT,
		/** generic public key support */
		FEATURE_PUBKEY,
		/** public_key_t->verify() */
		FEATURE_PUBKEY_VERIFY,
		/** public_key_t->encrypt() */
		FEATURE_PUBKEY_ENCRYPT,
		/** parsing certificates */
		FEATURE_CERT_DECODE,
		/** generating certificates */
		FEATURE_CERT_ENCODE,
		/** parsing containers */
		FEATURE_CONTAINER_DECODE,
		/** generating containers */
		FEATURE_CONTAINER_ENCODE,
		/** EAP server implementation */
		FEATURE_EAP_SERVER,
		/** EAP peer implementation */
		FEATURE_EAP_PEER,
		/** XAuth server implementation */
		FEATURE_XAUTH_SERVER,
		/** XAuth peer implementation */
		FEATURE_XAUTH_PEER,
		/** database_t */
		FEATURE_DATABASE,
		/** fetcher_t */
		FEATURE_FETCHER,
		/** resolver_t */
		FEATURE_RESOLVER,
		/** custom feature, described with a string */
		FEATURE_CUSTOM,
	} type;
	/** More specific data for each type */
	union {
		/** FEATURE_CRYPTER */
		struct {
			encryption_algorithm_t alg;
			size_t key_size;
		} crypter;
		/** FEATURE_AEAD */
		struct {
			encryption_algorithm_t alg;
			size_t key_size;
		} aead;
		/** FEATURE_SIGNER */
		integrity_algorithm_t signer;
		/** FEATURE_PRF */
		pseudo_random_function_t prf;
		/** FEATURE_XOFF */
		ext_out_function_t xof;
		/** FEATURE_DRBG */
		drbg_type_t drbg;
		/** FEATURE_HASHER */
		hash_algorithm_t hasher;
		/** FEATURE_DH */
		diffie_hellman_group_t dh_group;
		/** FEATURE_RNG */
		rng_quality_t rng_quality;
		/** FEATURE_PRIVKEY */
		key_type_t privkey;
		/** FEATURE_PRIVKEY_GEN */
		key_type_t privkey_gen;
		/** FEATURE_PRIVKEY_SIGN */
		signature_scheme_t privkey_sign;
		/** FEATURE_PRIVKEY_DECRYPT */
		encryption_scheme_t privkey_decrypt;
		/** FEATURE_PUBKEY */
		key_type_t pubkey;
		/** FEATURE_PUBKEY_VERIFY */
		signature_scheme_t pubkey_verify;
		/** FEATURE_PUBKEY_ENCRYPT */
		encryption_scheme_t pubkey_encrypt;
		/** FEATURE_CERT_DECODE/ENCODE */
		certificate_type_t cert;
		/** FEATURE_CONTAINER_DECODE/ENCODE */
		container_type_t container;
		/** FEATURE_EAP_SERVER/CLIENT */
		eap_vendor_type_t eap;
		/** FEATURE_DATABASE */
		db_driver_t database;
		/** FEATURE_FETCHER */
		char *fetcher;
		/** FEATURE_CUSTOM */
		char *custom;
		/** FEATURE_XAUTH_SERVER/CLIENT */
		char *xauth;

		/** FEATURE_REGISTER */
		struct {
			/** final flag to pass for builder_function_t */
			bool final;
			/** feature specific function to register for this type */
			void *f;
		} reg;

		/** FEATURE_CALLBACK */
		struct {
			/** callback function to invoke for registration */
			plugin_feature_callback_t f;
			/** data to pass to callback */
			void *data;
		} cb;
	} arg;
};

#define FEATURE(kind, type, ...) _PLUGIN_FEATURE_##type(kind, __VA_ARGS__)

/**
 * Define function to register directly for all upcoming features.
 *
 * @param type		feature type to register
 * @param f			type specific function to register
 * @param ...		type specific additional arguments
 */
#define PLUGIN_REGISTER(type, f, ...) _PLUGIN_FEATURE_REGISTER_##type(type, f, ##__VA_ARGS__)

/**
 * Define a callback to invoke for registering all upcoming features.
 *
 * @param cb		type specific callback function to register
 * @param data		data pointer to pass to callback
 */
#define PLUGIN_CALLBACK(cb, data) _PLUGIN_FEATURE_CALLBACK(cb, data)

/**
 * The upcoming features use neither a callback nor a register function.
 */
#define PLUGIN_NOOP _PLUGIN_FEATURE_CALLBACK(NULL, NULL)

/**
 * Define a feature the plugin provides.
 *
 * @param type		feature type to provide
 * @param ...		type specific arguments
 */
#define PLUGIN_PROVIDE(type, ...) _PLUGIN_FEATURE_##type(PROVIDE, __VA_ARGS__)

/**
 * Define a hard dependency for the previously defined feature.
 *
 * @param type		feature type to provide
 * @param ...		type specific arguments
 */
#define PLUGIN_DEPENDS(type, ...) _PLUGIN_FEATURE_##type(DEPENDS, __VA_ARGS__)

/**
 * Define a soft dependency for the previously defined feature.
 *
 * @param type		feature type to provide
 * @param ...		type specific arguments
 */
#define PLUGIN_SDEPEND(type, ...) _PLUGIN_FEATURE_##type(SDEPEND, __VA_ARGS__)

#define __PLUGIN_FEATURE(kind, type, ...)					(plugin_feature_t){ FEATURE_##kind, FEATURE_##type, { __VA_ARGS__ }}
#define _PLUGIN_FEATURE_CRYPTER(kind, alg, size)			__PLUGIN_FEATURE(kind, CRYPTER, .crypter = { alg, size })
#define _PLUGIN_FEATURE_AEAD(kind, alg, size)				__PLUGIN_FEATURE(kind, AEAD, .aead = { alg, size })
#define _PLUGIN_FEATURE_SIGNER(kind, alg)					__PLUGIN_FEATURE(kind, SIGNER, .signer = alg)
#define _PLUGIN_FEATURE_HASHER(kind, alg)					__PLUGIN_FEATURE(kind, HASHER, .hasher = alg)
#define _PLUGIN_FEATURE_PRF(kind, alg)						__PLUGIN_FEATURE(kind, PRF, .prf = alg)
#define _PLUGIN_FEATURE_XOF(kind, alg)						__PLUGIN_FEATURE(kind, XOF, .xof = alg)
#define _PLUGIN_FEATURE_DRBG(kind, type)					__PLUGIN_FEATURE(kind, DRBG, .drbg = type)
#define _PLUGIN_FEATURE_DH(kind, group)						__PLUGIN_FEATURE(kind, DH, .dh_group = group)
#define _PLUGIN_FEATURE_RNG(kind, quality)					__PLUGIN_FEATURE(kind, RNG, .rng_quality = quality)
#define _PLUGIN_FEATURE_NONCE_GEN(kind, ...)				__PLUGIN_FEATURE(kind, NONCE_GEN, .custom = NULL)
#define _PLUGIN_FEATURE_PRIVKEY(kind, type)					__PLUGIN_FEATURE(kind, PRIVKEY, .privkey = type)
#define _PLUGIN_FEATURE_PRIVKEY_GEN(kind, type)				__PLUGIN_FEATURE(kind, PRIVKEY_GEN, .privkey_gen = type)
#define _PLUGIN_FEATURE_PRIVKEY_SIGN(kind, scheme)			__PLUGIN_FEATURE(kind, PRIVKEY_SIGN, .privkey_sign = scheme)
#define _PLUGIN_FEATURE_PRIVKEY_DECRYPT(kind, scheme)		__PLUGIN_FEATURE(kind, PRIVKEY_DECRYPT, .privkey_decrypt = scheme)
#define _PLUGIN_FEATURE_PUBKEY(kind, type)					__PLUGIN_FEATURE(kind, PUBKEY, .pubkey = type)
#define _PLUGIN_FEATURE_PUBKEY_VERIFY(kind, scheme)			__PLUGIN_FEATURE(kind, PUBKEY_VERIFY, .pubkey_verify = scheme)
#define _PLUGIN_FEATURE_PUBKEY_ENCRYPT(kind, scheme)		__PLUGIN_FEATURE(kind, PUBKEY_ENCRYPT, .pubkey_encrypt = scheme)
#define _PLUGIN_FEATURE_CERT_DECODE(kind, type)				__PLUGIN_FEATURE(kind, CERT_DECODE, .cert = type)
#define _PLUGIN_FEATURE_CERT_ENCODE(kind, type)				__PLUGIN_FEATURE(kind, CERT_ENCODE, .cert = type)
#define _PLUGIN_FEATURE_CONTAINER_DECODE(kind, type)		__PLUGIN_FEATURE(kind, CONTAINER_DECODE, .container = type)
#define _PLUGIN_FEATURE_CONTAINER_ENCODE(kind, type)		__PLUGIN_FEATURE(kind, CONTAINER_ENCODE, .container = type)
#define _PLUGIN_FEATURE_EAP_SERVER(kind, type)				_PLUGIN_FEATURE_EAP_SERVER_VENDOR(kind, type, 0)
#define _PLUGIN_FEATURE_EAP_PEER(kind, type)				_PLUGIN_FEATURE_EAP_PEER_VENDOR(kind, type, 0)
#define _PLUGIN_FEATURE_EAP_SERVER_VENDOR(kind, type, vendor)__PLUGIN_FEATURE(kind, EAP_SERVER, .eap = { type, vendor })
#define _PLUGIN_FEATURE_EAP_PEER_VENDOR(kind, type, vendor)	__PLUGIN_FEATURE(kind, EAP_PEER, .eap = { type, vendor })
#define _PLUGIN_FEATURE_DATABASE(kind, type)				__PLUGIN_FEATURE(kind, DATABASE, .database = type)
#define _PLUGIN_FEATURE_FETCHER(kind, type)					__PLUGIN_FEATURE(kind, FETCHER, .fetcher = type)
#define _PLUGIN_FEATURE_RESOLVER(kind, ...)					__PLUGIN_FEATURE(kind, RESOLVER, .custom = NULL)
#define _PLUGIN_FEATURE_CUSTOM(kind, name)					__PLUGIN_FEATURE(kind, CUSTOM, .custom = name)
#define _PLUGIN_FEATURE_XAUTH_SERVER(kind, name)			__PLUGIN_FEATURE(kind, XAUTH_SERVER, .xauth = name)
#define _PLUGIN_FEATURE_XAUTH_PEER(kind, name)				__PLUGIN_FEATURE(kind, XAUTH_PEER, .xauth = name)

#define __PLUGIN_FEATURE_REGISTER(type, _f)					(plugin_feature_t){ FEATURE_REGISTER, FEATURE_##type, .arg.reg.f = _f }
#define __PLUGIN_FEATURE_REGISTER_BUILDER(type, _f, _final)	(plugin_feature_t){ FEATURE_REGISTER, FEATURE_##type, .arg.reg = {.f = _f, .final = _final, }}
#define _PLUGIN_FEATURE_REGISTER_CRYPTER(type, f)			__PLUGIN_FEATURE_REGISTER(type, f)
#define _PLUGIN_FEATURE_REGISTER_AEAD(type, f)				__PLUGIN_FEATURE_REGISTER(type, f)
#define _PLUGIN_FEATURE_REGISTER_SIGNER(type, f)			__PLUGIN_FEATURE_REGISTER(type, f)
#define _PLUGIN_FEATURE_REGISTER_HASHER(type, f)			__PLUGIN_FEATURE_REGISTER(type, f)
#define _PLUGIN_FEATURE_REGISTER_PRF(type, f)				__PLUGIN_FEATURE_REGISTER(type, f)
#define _PLUGIN_FEATURE_REGISTER_XOF(type, f)				__PLUGIN_FEATURE_REGISTER(type, f)
#define _PLUGIN_FEATURE_REGISTER_DRBG(type, f)				__PLUGIN_FEATURE_REGISTER(type, f)
#define _PLUGIN_FEATURE_REGISTER_DH(type, f)				__PLUGIN_FEATURE_REGISTER(type, f)
#define _PLUGIN_FEATURE_REGISTER_RNG(type, f)				__PLUGIN_FEATURE_REGISTER(type, f)
#define _PLUGIN_FEATURE_REGISTER_NONCE_GEN(type, f)			__PLUGIN_FEATURE_REGISTER(type, f)
#define _PLUGIN_FEATURE_REGISTER_PRIVKEY(type, f, final)	__PLUGIN_FEATURE_REGISTER_BUILDER(type, f, final)
#define _PLUGIN_FEATURE_REGISTER_PRIVKEY_GEN(type, f, final)__PLUGIN_FEATURE_REGISTER_BUILDER(type, f, final)
#define _PLUGIN_FEATURE_REGISTER_PUBKEY(type, f, final)		__PLUGIN_FEATURE_REGISTER_BUILDER(type, f, final)
#define _PLUGIN_FEATURE_REGISTER_CERT_DECODE(type, f, final)__PLUGIN_FEATURE_REGISTER_BUILDER(type, f, final)
#define _PLUGIN_FEATURE_REGISTER_CERT_ENCODE(type, f, final)__PLUGIN_FEATURE_REGISTER_BUILDER(type, f, final)
#define _PLUGIN_FEATURE_REGISTER_CONTAINER_DECODE(type, f, final)__PLUGIN_FEATURE_REGISTER_BUILDER(type, f, final)
#define _PLUGIN_FEATURE_REGISTER_CONTAINER_ENCODE(type, f, final)__PLUGIN_FEATURE_REGISTER_BUILDER(type, f, final)
#define _PLUGIN_FEATURE_REGISTER_DATABASE(type, f)			__PLUGIN_FEATURE_REGISTER(type, f)
#define _PLUGIN_FEATURE_REGISTER_FETCHER(type, f)			__PLUGIN_FEATURE_REGISTER(type, f)
#define _PLUGIN_FEATURE_REGISTER_RESOLVER(type, f)			__PLUGIN_FEATURE_REGISTER(type, f)

#define _PLUGIN_FEATURE_CALLBACK(_cb, _data) (plugin_feature_t){ FEATURE_CALLBACK, FEATURE_NONE, .arg.cb = { .f = _cb, .data = _data } }

/**
 * Names for plugin_feature_t types.
 */
extern enum_name_t *plugin_feature_names;

/**
 * Add a set of plugin features to the given array, which must have enough space
 * to store the added features.
 *
 * @param features		the array of plugin features to extend
 * @param to_add		the features to add
 * @param count			number of features to add
 * @param pos			current position in the features array, gets advanced
 */
static inline void plugin_features_add(plugin_feature_t *features,
									   plugin_feature_t *to_add,
									   int count, int *pos)
{
	int i;

	for (i = 0; i < count; i++)
	{
		features[(*pos)++] = to_add[i];
	}
}

/**
 * Calculates a hash value for the given feature.
 *
 * Since this is intended to be used with the plugin_features_matches function
 * the hash is not really unique for all types of features (e.g. RNGs are all
 * mapped to the same value because they are loosely matched by said function).
 *
 * @param feature	feature to hash
 * @return			hash value of the feature
 */
uint32_t plugin_feature_hash(plugin_feature_t *feature);

/**
 * Check if feature a matches to feature b.
 *
 * This is no check for equality.  For instance, for FEATURE_RNG a matches b if
 * a's strength is at least the strength of b.  Or for FEATURE_SQL if a is
 * DB_ANY it will match b if it is of the same type.
 *
 * @param a			feature to check
 * @param b			feature to match against
 * @return			TRUE if a matches b
 */
bool plugin_feature_matches(plugin_feature_t *a, plugin_feature_t *b);

/**
 * Check if feature a equals feature b.
 *
 * @param a			feature
 * @param b			feature to compare
 * @return			TRUE if a equals b
 */
bool plugin_feature_equals(plugin_feature_t *a, plugin_feature_t *b);

/**
 * Get a string describing feature.
 *
 * @param feature	feature to describe
 * @return			allocated string describing feature
 */
char* plugin_feature_get_string(plugin_feature_t *feature);

/**
 * Load a plugin feature using a REGISTER/CALLBACK feature entry.
 *
 * @param plugin	plugin providing feature
 * @param feature	feature to load
 * @param reg		REGISTER/CALLBACK feature entry to use for registration
 */
bool plugin_feature_load(plugin_t *plugin, plugin_feature_t *feature,
						 plugin_feature_t *reg);

/**
 * Unload a plugin feature using a REGISTER/CALLBACK feature entry.
 *
 * @param plugin	plugin providing feature
 * @param feature	feature to unload
 * @param reg		REGISTER/CALLBACK feature entry to use for deregistration
 */
bool plugin_feature_unload(plugin_t *plugin, plugin_feature_t *feature,
						   plugin_feature_t *reg);

#endif /** PLUGIN_FEATURE_H_ @}*/
