improvements in extension handling
- extensions_sig is needed globally - keep original json with config of extension - fixed various bugs re: extension handling
This commit is contained in:
parent
0b56de6c99
commit
1962ed6b0b
@ -152,6 +152,13 @@ bool TEH_suicide;
|
|||||||
*/
|
*/
|
||||||
struct TALER_Extension **TEH_extensions;
|
struct TALER_Extension **TEH_extensions;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Signature of the configuration of all enabled extensions,
|
||||||
|
* signed by the exchange's offline master key with purpose
|
||||||
|
* TALER_SIGNATURE_MASTER_EXTENSION.
|
||||||
|
*/
|
||||||
|
struct TALER_MasterSignatureP TEH_extensions_sig;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Value to return from main()
|
* Value to return from main()
|
||||||
*/
|
*/
|
||||||
|
@ -206,6 +206,11 @@ extern struct GNUNET_CURL_Context *TEH_curl_ctx;
|
|||||||
*/
|
*/
|
||||||
extern struct TALER_Extension **TEH_extensions;
|
extern struct TALER_Extension **TEH_extensions;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Signature of the offline master key of all enabled extensions' configuration
|
||||||
|
*/
|
||||||
|
extern struct TALER_MasterSignatureP TEH_extensions_sig;
|
||||||
|
|
||||||
/* TODO: this will not work anymore, once we have plugable extensions */
|
/* TODO: this will not work anymore, once we have plugable extensions */
|
||||||
#define TEH_extension_enabled(ext) (0 <= ext && TALER_Extension_MaxPredefined > \
|
#define TEH_extension_enabled(ext) (0 <= ext && TALER_Extension_MaxPredefined > \
|
||||||
ext && \
|
ext && \
|
||||||
|
@ -27,29 +27,58 @@
|
|||||||
#include "taler_extensions.h"
|
#include "taler_extensions.h"
|
||||||
#include <jansson.h>
|
#include <jansson.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief implements the TALER_Extension.disable interface.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
age_restriction_disable (struct TALER_Extension *this)
|
||||||
|
{
|
||||||
|
if (NULL == this)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this->config = NULL;
|
||||||
|
|
||||||
|
if (NULL != this->config_json)
|
||||||
|
{
|
||||||
|
json_decref (this->config_json);
|
||||||
|
this->config_json = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief implements the TALER_Extension.parse_and_set_config interface.
|
* @brief implements the TALER_Extension.parse_and_set_config interface.
|
||||||
|
* @param this if NULL, only tests the configuration
|
||||||
|
* @param config the configuration as json
|
||||||
*/
|
*/
|
||||||
static enum GNUNET_GenericReturnValue
|
static enum GNUNET_GenericReturnValue
|
||||||
age_restriction_parse_and_set_config (struct TALER_Extension *this,
|
age_restriction_parse_and_set_config (struct TALER_Extension *this,
|
||||||
const json_t *config)
|
json_t *config)
|
||||||
{
|
{
|
||||||
enum GNUNET_GenericReturnValue ret;
|
|
||||||
struct TALER_AgeMask mask = {0};
|
struct TALER_AgeMask mask = {0};
|
||||||
|
enum GNUNET_GenericReturnValue ret;
|
||||||
|
|
||||||
ret = TALER_agemask_parse_json (config, &mask);
|
ret = TALER_agemask_parse_json (config, &mask);
|
||||||
if (GNUNET_OK != ret)
|
if (GNUNET_OK != ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
if (this != NULL && TALER_Extension_AgeRestriction == this->type)
|
/* only testing the parser */
|
||||||
{
|
if (this == NULL)
|
||||||
|
return GNUNET_OK;
|
||||||
|
|
||||||
|
if (TALER_Extension_AgeRestriction != this->type)
|
||||||
|
return GNUNET_SYSERR;
|
||||||
|
|
||||||
if (NULL != this->config)
|
if (NULL != this->config)
|
||||||
{
|
|
||||||
GNUNET_free (this->config);
|
GNUNET_free (this->config);
|
||||||
}
|
|
||||||
this->config = GNUNET_malloc (sizeof(struct TALER_AgeMask));
|
this->config = GNUNET_malloc (sizeof(struct TALER_AgeMask));
|
||||||
GNUNET_memcpy (this->config, &mask, sizeof(struct TALER_AgeMask));
|
GNUNET_memcpy (this->config, &mask, sizeof(struct TALER_AgeMask));
|
||||||
}
|
|
||||||
|
if (NULL != this->config_json)
|
||||||
|
json_decref (this->config_json);
|
||||||
|
|
||||||
|
this->config_json = config;
|
||||||
|
|
||||||
return GNUNET_OK;
|
return GNUNET_OK;
|
||||||
}
|
}
|
||||||
@ -61,28 +90,9 @@ age_restriction_parse_and_set_config (struct TALER_Extension *this,
|
|||||||
static enum GNUNET_GenericReturnValue
|
static enum GNUNET_GenericReturnValue
|
||||||
age_restriction_test_config (const json_t *config)
|
age_restriction_test_config (const json_t *config)
|
||||||
{
|
{
|
||||||
return age_restriction_parse_and_set_config (NULL, config);
|
struct TALER_AgeMask mask = {0};
|
||||||
}
|
|
||||||
|
|
||||||
|
return TALER_agemask_parse_json (config, &mask);
|
||||||
/**
|
|
||||||
* @brief implements the TALER_Extension.config_to_json interface.
|
|
||||||
*/
|
|
||||||
static json_t *
|
|
||||||
age_restriction_config_to_json (const struct TALER_Extension *this)
|
|
||||||
{
|
|
||||||
const struct TALER_AgeMask *mask;
|
|
||||||
if (NULL == this || TALER_Extension_AgeRestriction != this->type)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
mask = (struct TALER_AgeMask *) this->config;
|
|
||||||
json_t *config = GNUNET_JSON_PACK (
|
|
||||||
GNUNET_JSON_pack_string ("extension", this->name),
|
|
||||||
GNUNET_JSON_pack_string ("mask",
|
|
||||||
TALER_age_mask_to_string (mask))
|
|
||||||
);
|
|
||||||
|
|
||||||
return config;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -91,26 +101,17 @@ static struct TALER_Extension extension_age_restriction = {
|
|||||||
.type = TALER_Extension_AgeRestriction,
|
.type = TALER_Extension_AgeRestriction,
|
||||||
.name = "age_restriction",
|
.name = "age_restriction",
|
||||||
.critical = false,
|
.critical = false,
|
||||||
|
.version = "1",
|
||||||
.config = NULL, // disabled per default
|
.config = NULL, // disabled per default
|
||||||
|
.config_json = NULL,
|
||||||
|
.disable = &age_restriction_disable,
|
||||||
.test_config = &age_restriction_test_config,
|
.test_config = &age_restriction_test_config,
|
||||||
.parse_and_set_config = &age_restriction_parse_and_set_config,
|
.parse_and_set_config = &age_restriction_parse_and_set_config,
|
||||||
.config_to_json = &age_restriction_config_to_json,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/* TODO: The extension for peer2peer */
|
|
||||||
static struct TALER_Extension extension_peer2peer = {
|
|
||||||
.type = TALER_Extension_Peer2Peer,
|
|
||||||
.name = "peer2peer",
|
|
||||||
.critical = false,
|
|
||||||
.config = NULL, // disabled per default
|
|
||||||
.test_config = NULL, // TODO
|
|
||||||
.parse_and_set_config = NULL, // TODO
|
|
||||||
.config_to_json = NULL, // TODO
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a list with the extensions for Age Restriction and Peer2Peer
|
* Create a list with the extensions for Age Restriction (and later Peer2Peer,
|
||||||
|
* ...)
|
||||||
*/
|
*/
|
||||||
static struct TALER_Extension **
|
static struct TALER_Extension **
|
||||||
get_known_extensions ()
|
get_known_extensions ()
|
||||||
@ -120,7 +121,6 @@ get_known_extensions ()
|
|||||||
TALER_Extension_MaxPredefined + 1,
|
TALER_Extension_MaxPredefined + 1,
|
||||||
struct TALER_Extension *);
|
struct TALER_Extension *);
|
||||||
list[TALER_Extension_AgeRestriction] = &extension_age_restriction;
|
list[TALER_Extension_AgeRestriction] = &extension_age_restriction;
|
||||||
list[TALER_Extension_Peer2Peer] = &extension_peer2peer;
|
|
||||||
list[TALER_Extension_MaxPredefined] = NULL;
|
list[TALER_Extension_MaxPredefined] = NULL;
|
||||||
|
|
||||||
return list;
|
return list;
|
||||||
@ -172,14 +172,13 @@ extension_update_event_cb (void *cls,
|
|||||||
|
|
||||||
// Get the config from the database as string
|
// Get the config from the database as string
|
||||||
{
|
{
|
||||||
char *config_str;
|
char *config_str = NULL;
|
||||||
enum GNUNET_DB_QueryStatus qs;
|
enum GNUNET_DB_QueryStatus qs;
|
||||||
struct TALER_Extension *extension;
|
struct TALER_Extension *extension;
|
||||||
json_error_t err;
|
json_error_t err;
|
||||||
json_t *config;
|
json_t *config;
|
||||||
enum GNUNET_GenericReturnValue ret;
|
enum GNUNET_GenericReturnValue ret;
|
||||||
|
|
||||||
// TODO: make this a safe lookup
|
|
||||||
extension = TEH_extensions[type];
|
extension = TEH_extensions[type];
|
||||||
|
|
||||||
qs = TEH_plugin->get_extension_config (TEH_plugin->cls,
|
qs = TEH_plugin->get_extension_config (TEH_plugin->cls,
|
||||||
@ -194,6 +193,13 @@ extension_update_event_cb (void *cls,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// No config found -> extension is disabled
|
||||||
|
if (NULL == config_str)
|
||||||
|
{
|
||||||
|
extension->disable (extension);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Parse the string as JSON
|
// Parse the string as JSON
|
||||||
config = json_loads (config_str, JSON_DECODE_ANY, &err);
|
config = json_loads (config_str, JSON_DECODE_ANY, &err);
|
||||||
if (NULL == config)
|
if (NULL == config)
|
||||||
@ -223,14 +229,14 @@ extension_update_event_cb (void *cls,
|
|||||||
enum GNUNET_GenericReturnValue
|
enum GNUNET_GenericReturnValue
|
||||||
TEH_extensions_init ()
|
TEH_extensions_init ()
|
||||||
{
|
{
|
||||||
|
/* Populate the known extensions. */
|
||||||
TEH_extensions = get_known_extensions ();
|
TEH_extensions = get_known_extensions ();
|
||||||
|
|
||||||
{
|
/* Set the event handler for updates */
|
||||||
struct GNUNET_DB_EventHeaderP ev = {
|
struct GNUNET_DB_EventHeaderP ev = {
|
||||||
.size = htons (sizeof (ev)),
|
.size = htons (sizeof (ev)),
|
||||||
.type = htons (TALER_DBEVENT_EXCHANGE_EXTENSIONS_UPDATED),
|
.type = htons (TALER_DBEVENT_EXCHANGE_EXTENSIONS_UPDATED),
|
||||||
};
|
};
|
||||||
|
|
||||||
extensions_eh = TEH_plugin->event_listen (TEH_plugin->cls,
|
extensions_eh = TEH_plugin->event_listen (TEH_plugin->cls,
|
||||||
GNUNET_TIME_UNIT_FOREVER_REL,
|
GNUNET_TIME_UNIT_FOREVER_REL,
|
||||||
&ev,
|
&ev,
|
||||||
@ -241,7 +247,11 @@ TEH_extensions_init ()
|
|||||||
GNUNET_break (0);
|
GNUNET_break (0);
|
||||||
return GNUNET_SYSERR;
|
return GNUNET_SYSERR;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
/* Trigger the initial load of configuration from the db */
|
||||||
|
for (struct TALER_Extension **it = TEH_extensions; NULL != *it; it++)
|
||||||
|
extension_update_event_cb (NULL, &(*it)->type, sizeof((*it)->type));
|
||||||
|
|
||||||
return GNUNET_OK;
|
return GNUNET_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1703,29 +1703,79 @@ create_krd (struct TEH_KeyStateHandle *ksh,
|
|||||||
&TEH_kyc_config.wallet_balance_limit)));
|
&TEH_kyc_config.wallet_balance_limit)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Signal support for the age-restriction extension, if so configured, and
|
// Signal support for the configured, enabled extensions.
|
||||||
// add the array of age-restricted denominations.
|
{
|
||||||
|
json_t *extensions = json_object ();
|
||||||
|
bool has_extensions;
|
||||||
|
|
||||||
|
/* Fill in the configurations of the enabled extensions */
|
||||||
|
for (struct TALER_Extension **it = TEH_extensions;
|
||||||
|
NULL != *it;
|
||||||
|
it++)
|
||||||
|
{
|
||||||
|
const struct TALER_Extension *extension = *it;
|
||||||
|
json_t *ext;
|
||||||
|
json_t *config_json;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
/* skip if not configured == disabled */
|
||||||
|
if (NULL == extension->config)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
has_extensions = true;
|
||||||
|
|
||||||
|
GNUNET_assert (NULL != extension->config_json);
|
||||||
|
|
||||||
|
config_json = json_copy (extension->config_json);
|
||||||
|
GNUNET_assert (NULL != config_json);
|
||||||
|
|
||||||
|
ext = GNUNET_JSON_PACK (
|
||||||
|
GNUNET_JSON_pack_bool ("critical",
|
||||||
|
extension->critical),
|
||||||
|
GNUNET_JSON_pack_string ("version",
|
||||||
|
extension->version),
|
||||||
|
GNUNET_JSON_pack_object_steal ("config",
|
||||||
|
config_json)
|
||||||
|
);
|
||||||
|
GNUNET_assert (NULL != ext);
|
||||||
|
|
||||||
|
r = json_object_set_new (
|
||||||
|
extensions,
|
||||||
|
extension->name,
|
||||||
|
ext);
|
||||||
|
|
||||||
|
GNUNET_assert (0 == r);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Update the keys object with the extensions */
|
||||||
|
if (has_extensions)
|
||||||
|
{
|
||||||
|
json_t *sig;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
r = json_object_set_new (
|
||||||
|
keys,
|
||||||
|
"extensions",
|
||||||
|
extensions);
|
||||||
|
GNUNET_assert (0 == r);
|
||||||
|
|
||||||
|
/* add extensions_sig */
|
||||||
|
sig = GNUNET_JSON_PACK (
|
||||||
|
GNUNET_JSON_pack_data_auto ("extensions_sig",
|
||||||
|
&TEH_extensions_sig));
|
||||||
|
|
||||||
|
/* update the keys object with extensions_sig */
|
||||||
|
r = json_object_update (keys, sig);
|
||||||
|
GNUNET_assert (0 == r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Special case for age restrictions: if enabled, provide the lits of
|
||||||
|
// age-restricted denominations.
|
||||||
if (TEH_extension_enabled (TALER_Extension_AgeRestriction) &&
|
if (TEH_extension_enabled (TALER_Extension_AgeRestriction) &&
|
||||||
NULL != age_restricted_denoms)
|
NULL != age_restricted_denoms)
|
||||||
{
|
{
|
||||||
struct TALER_AgeMask *mask;
|
|
||||||
json_t *config;
|
|
||||||
|
|
||||||
mask = (struct
|
|
||||||
TALER_AgeMask *) TEH_extensions[TALER_Extension_AgeRestriction]->
|
|
||||||
config;
|
|
||||||
config = GNUNET_JSON_PACK (
|
|
||||||
GNUNET_JSON_pack_bool ("critical", false),
|
|
||||||
GNUNET_JSON_pack_string ("version", "1"),
|
|
||||||
GNUNET_JSON_pack_string ("age_groups", TALER_age_mask_to_string (mask)));
|
|
||||||
GNUNET_assert (NULL != config);
|
|
||||||
GNUNET_assert (
|
|
||||||
0 ==
|
|
||||||
json_object_set_new (
|
|
||||||
keys,
|
|
||||||
"age_restriction",
|
|
||||||
config));
|
|
||||||
|
|
||||||
GNUNET_assert (
|
GNUNET_assert (
|
||||||
0 ==
|
0 ==
|
||||||
json_object_set_new (
|
json_object_set_new (
|
||||||
@ -1734,8 +1784,6 @@ create_krd (struct TEH_KeyStateHandle *ksh,
|
|||||||
age_restricted_denoms));
|
age_restricted_denoms));
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: signal support and configuration for the P2P extension, once
|
|
||||||
// implemented.
|
|
||||||
|
|
||||||
{
|
{
|
||||||
char *keys_json;
|
char *keys_json;
|
||||||
|
@ -49,6 +49,7 @@ struct SetExtensionsContext
|
|||||||
{
|
{
|
||||||
uint32_t num_extensions;
|
uint32_t num_extensions;
|
||||||
struct Extension *extensions;
|
struct Extension *extensions;
|
||||||
|
struct TALER_MasterSignatureP extensions_sig;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -132,6 +133,9 @@ set_extensions (void *cls,
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* All extensions configured, update the signature */
|
||||||
|
TEH_extensions_sig = sec->extensions_sig;
|
||||||
|
|
||||||
return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; /* only 'success', so >=0, matters here */
|
return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; /* only 'success', so >=0, matters here */
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -143,15 +147,14 @@ TEH_handler_management_post_extensions (
|
|||||||
{
|
{
|
||||||
MHD_RESULT ret;
|
MHD_RESULT ret;
|
||||||
json_t *extensions;
|
json_t *extensions;
|
||||||
struct TALER_MasterSignatureP sig = {0};
|
struct SetExtensionsContext sec = {0};
|
||||||
struct GNUNET_JSON_Specification top_spec[] = {
|
struct GNUNET_JSON_Specification top_spec[] = {
|
||||||
GNUNET_JSON_spec_json ("extensions",
|
GNUNET_JSON_spec_json ("extensions",
|
||||||
&extensions),
|
&extensions),
|
||||||
GNUNET_JSON_spec_fixed_auto ("extensions_sig",
|
GNUNET_JSON_spec_fixed_auto ("extensions_sig",
|
||||||
&sig),
|
&sec.extensions_sig),
|
||||||
GNUNET_JSON_spec_end ()
|
GNUNET_JSON_spec_end ()
|
||||||
};
|
};
|
||||||
struct SetExtensionsContext sec = {0};
|
|
||||||
|
|
||||||
/* Parse the top level json structure */
|
/* Parse the top level json structure */
|
||||||
{
|
{
|
||||||
@ -193,7 +196,7 @@ TEH_handler_management_post_extensions (
|
|||||||
if (GNUNET_OK != TALER_exchange_offline_extension_config_hash_verify (
|
if (GNUNET_OK != TALER_exchange_offline_extension_config_hash_verify (
|
||||||
&h_config,
|
&h_config,
|
||||||
&TEH_master_public_key,
|
&TEH_master_public_key,
|
||||||
&sig))
|
&sec.extensions_sig))
|
||||||
{
|
{
|
||||||
GNUNET_JSON_parse_free (top_spec);
|
GNUNET_JSON_parse_free (top_spec);
|
||||||
return TALER_MHD_reply_with_error (
|
return TALER_MHD_reply_with_error (
|
||||||
@ -248,7 +251,6 @@ TEH_handler_management_post_extensions (
|
|||||||
TALER_EC_GENERIC_PARAMETER_MALFORMED,
|
TALER_EC_GENERIC_PARAMETER_MALFORMED,
|
||||||
"invalid configuration for extension");
|
"invalid configuration for extension");
|
||||||
goto CLEANUP;
|
goto CLEANUP;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* We have a validly signed JSON object for the extension. Increment its
|
/* We have a validly signed JSON object for the extension. Increment its
|
||||||
|
@ -11455,6 +11455,7 @@ postgres_get_extension_config (void *cls,
|
|||||||
GNUNET_PQ_query_param_end
|
GNUNET_PQ_query_param_end
|
||||||
};
|
};
|
||||||
bool is_null;
|
bool is_null;
|
||||||
|
*config = NULL;
|
||||||
struct GNUNET_PQ_ResultSpec rs[] = {
|
struct GNUNET_PQ_ResultSpec rs[] = {
|
||||||
GNUNET_PQ_result_spec_allow_null (
|
GNUNET_PQ_result_spec_allow_null (
|
||||||
GNUNET_PQ_result_spec_string ("config", config),
|
GNUNET_PQ_result_spec_string ("config", config),
|
||||||
@ -11467,10 +11468,6 @@ postgres_get_extension_config (void *cls,
|
|||||||
"get_extension_config",
|
"get_extension_config",
|
||||||
params,
|
params,
|
||||||
rs);
|
rs);
|
||||||
if (is_null)
|
|
||||||
{
|
|
||||||
*config = NULL;
|
|
||||||
}
|
|
||||||
return qs;
|
return qs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,26 +31,27 @@
|
|||||||
enum TALER_Extension_Type
|
enum TALER_Extension_Type
|
||||||
{
|
{
|
||||||
TALER_Extension_AgeRestriction = 0,
|
TALER_Extension_AgeRestriction = 0,
|
||||||
TALER_Extension_Peer2Peer = 1,
|
TALER_Extension_MaxPredefined = 1 // Must be last of the predefined
|
||||||
TALER_Extension_MaxPredefined = 2 // Must be last
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Represents the implementation of an extension.
|
* Represents the implementation of an extension.
|
||||||
|
* TODO: add documentation
|
||||||
*/
|
*/
|
||||||
struct TALER_Extension
|
struct TALER_Extension
|
||||||
{
|
{
|
||||||
enum TALER_Extension_Type type;
|
enum TALER_Extension_Type type;
|
||||||
char *name;
|
char *name;
|
||||||
bool critical;
|
bool critical;
|
||||||
bool enabled;
|
char *version;
|
||||||
void *config;
|
void *config;
|
||||||
|
json_t *config_json;
|
||||||
|
|
||||||
|
void (*disable)(struct TALER_Extension *this);
|
||||||
enum GNUNET_GenericReturnValue (*test_config)(const json_t *config);
|
enum GNUNET_GenericReturnValue (*test_config)(const json_t *config);
|
||||||
enum GNUNET_GenericReturnValue (*parse_and_set_config)(struct
|
enum GNUNET_GenericReturnValue (*parse_and_set_config)(struct
|
||||||
TALER_Extension *this,
|
TALER_Extension *this,
|
||||||
const json_t *config);
|
json_t *config);
|
||||||
json_t *(*config_to_json)(const struct TALER_Extension *this);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -132,8 +133,7 @@ TALER_get_age_mask (const struct GNUNET_CONFIGURATION_Handle *cfg,
|
|||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* TALER Peer2Peer Extension
|
* TODO: Add Peer2Peer Extension
|
||||||
* TODO oec
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
Loading…
Reference in New Issue
Block a user