Refactor extensions

- Extensions are now compiled as shared libraries and loaded at runtime
  according to the TALER configuration.

- So far, only age restriction is loaded as extension.

- Groundwork for extension auction_brandt started.
This commit is contained in:
Özgür Kesim 2022-10-02 18:05:58 +02:00
parent 165b85ddd5
commit 04c7e0bb33
Signed by: oec
GPG Key ID: 3D76A56D79EDD9D7
25 changed files with 983 additions and 516 deletions

View File

@ -32,5 +32,7 @@ SUBDIRS = \
auditor \ auditor \
lib \ lib \
exchange-tools \ exchange-tools \
extensions/auction_brandt \
extensions/age_restriction \
testing \ testing \
benchmark benchmark

View File

@ -137,6 +137,11 @@ static struct GNUNET_CURL_RescheduleContext *rc;
*/ */
static const struct GNUNET_CONFIGURATION_Handle *kcfg; static const struct GNUNET_CONFIGURATION_Handle *kcfg;
/**
* Age restriction configuration
*/
static struct TALER_AgeRestrictionConfig ar_config = {0};
/** /**
* Return value from main(). * Return value from main().
*/ */
@ -163,11 +168,6 @@ static char *currency;
*/ */
static char *CFG_exchange_url; static char *CFG_exchange_url;
/**
* If age restriction is enabled, the age mask to be used
*/
static struct TALER_AgeMask age_mask = {0};
/** /**
* A subcommand supported by this program. * A subcommand supported by this program.
*/ */
@ -2386,6 +2386,7 @@ do_upload (char *const *args)
return; return;
} }
trigger_upload (CFG_exchange_url); trigger_upload (CFG_exchange_url);
json_decref (out); json_decref (out);
out = NULL; out = NULL;
} }
@ -3888,7 +3889,7 @@ load_age_mask (const char*section_name)
static const struct TALER_AgeMask null_mask = {0}; static const struct TALER_AgeMask null_mask = {0};
enum GNUNET_GenericReturnValue ret; enum GNUNET_GenericReturnValue ret;
if (age_mask.bits == 0) if (! ar_config.enabled)
return null_mask; return null_mask;
if (GNUNET_OK != (GNUNET_CONFIGURATION_have_value ( if (GNUNET_OK != (GNUNET_CONFIGURATION_have_value (
@ -3900,14 +3901,14 @@ load_age_mask (const char*section_name)
ret = GNUNET_CONFIGURATION_get_value_yesno (kcfg, ret = GNUNET_CONFIGURATION_get_value_yesno (kcfg,
section_name, section_name,
"AGE_RESTRICTED"); "AGE_RESTRICTED");
if (GNUNET_YES == ret)
return age_mask;
if (GNUNET_SYSERR == ret) if (GNUNET_SYSERR == ret)
GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
section_name, section_name,
"AGE_RESTRICTED", "AGE_RESTRICTED",
"Value must be YES or NO\n"); "Value must be YES or NO\n");
if (GNUNET_YES == ret)
return ar_config.mask;
return null_mask; return null_mask;
} }
@ -4248,7 +4249,7 @@ do_setup (char *const *args)
static void static void
do_extensions_show (char *const *args) do_extensions_show (char *const *args)
{ {
const struct TALER_Extension *it; const struct TALER_Extensions *it;
json_t *exts = json_object (); json_t *exts = json_object ();
json_t *obj; json_t *obj;
@ -4258,8 +4259,9 @@ do_extensions_show (char *const *args)
it = it->next) it = it->next)
GNUNET_assert (0 == GNUNET_assert (0 ==
json_object_set_new (exts, json_object_set_new (exts,
it->name, it->extension->name,
it->config_to_json (it))); it->extension->config_to_json (
it->extension)));
obj = GNUNET_JSON_PACK ( obj = GNUNET_JSON_PACK (
GNUNET_JSON_pack_object_steal ("extensions", GNUNET_JSON_pack_object_steal ("extensions",
exts)); exts));
@ -4268,7 +4270,7 @@ do_extensions_show (char *const *args)
json_dumps (obj, json_dumps (obj,
JSON_INDENT (2))); JSON_INDENT (2)));
json_decref (obj); json_decref (obj);
next (args + 1); next (args);
} }
@ -4281,25 +4283,29 @@ do_extensions_sign (char *const *args)
json_t *extensions = json_object (); json_t *extensions = json_object ();
struct TALER_ExtensionConfigHashP h_config; struct TALER_ExtensionConfigHashP h_config;
struct TALER_MasterSignatureP sig; struct TALER_MasterSignatureP sig;
const struct TALER_Extension *it; const struct TALER_Extensions *it;
bool found = false;
json_t *obj; json_t *obj;
GNUNET_assert (NULL != extensions); GNUNET_assert (NULL != extensions);
if (GNUNET_OK !=
TALER_extensions_load_taler_config (kcfg))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"error while loading taler config for extensions\n");
json_decref (extensions);
return;
}
for (it = TALER_extensions_get_head (); for (it = TALER_extensions_get_head ();
NULL != it; NULL != it;
it = it->next) it = it->next)
{
const struct TALER_Extension *ext = it->extension;
GNUNET_assert (ext);
found = true;
GNUNET_assert (0 == GNUNET_assert (0 ==
json_object_set_new (extensions, json_object_set_new (extensions,
it->name, ext->name,
it->config_to_json (it))); ext->config_to_json (
ext)));
}
if (! found)
return;
if (GNUNET_OK != if (GNUNET_OK !=
TALER_JSON_extensions_config_hash (extensions, TALER_JSON_extensions_config_hash (extensions,
@ -4327,9 +4333,10 @@ do_extensions_sign (char *const *args)
GNUNET_JSON_pack_data_auto ( GNUNET_JSON_pack_data_auto (
"extensions_sig", "extensions_sig",
&sig)); &sig));
output_operation (OP_EXTENSIONS, output_operation (OP_EXTENSIONS,
obj); obj);
next (args + 1); next (args);
} }
@ -4396,6 +4403,7 @@ do_work_extensions (char *const *args)
} }
}; };
if (NULL == args[0]) if (NULL == args[0])
{ {
GNUNET_log (GNUNET_ERROR_TYPE_ERROR, GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
@ -4530,6 +4538,24 @@ run (void *cls,
(void) cls; (void) cls;
(void) cfgfile; (void) cfgfile;
kcfg = cfg; kcfg = cfg;
/* load extensions */
GNUNET_assert (GNUNET_OK ==
TALER_extensions_load (kcfg));
/* setup age restriction, if applicable */
{
const struct TALER_AgeRestrictionConfig *arc;
if (NULL !=
(arc = TALER_extensions_get_age_restriction_config ()))
{
ar_config = *arc;
ar_config.enabled = true;
}
}
if (GNUNET_OK != if (GNUNET_OK !=
TALER_config_get_currency (kcfg, TALER_config_get_currency (kcfg,
&currency)) &currency))
@ -4538,18 +4564,6 @@ run (void *cls,
return; return;
} }
/* load age mask, if age restriction is enabled */
GNUNET_assert (GNUNET_OK ==
TALER_extension_age_restriction_register ());
if (GNUNET_OK != TALER_extensions_load_taler_config (kcfg))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"error while loading taler config for extensions\n");
return;
}
age_mask = TALER_extensions_age_restriction_ageMask ();
ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule, ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule,
&rc); &rc);
rc = GNUNET_CURL_gnunet_rc_create (ctx); rc = GNUNET_CURL_gnunet_rc_create (ctx);
@ -4579,7 +4593,6 @@ main (int argc,
GNUNET_GETOPT_OPTION_END GNUNET_GETOPT_OPTION_END
}; };
enum GNUNET_GenericReturnValue ret; enum GNUNET_GenericReturnValue ret;
/* force linker to link against libtalerutil; if we do /* force linker to link against libtalerutil; if we do
not do this, the linker may "optimize" libtalerutil not do this, the linker may "optimize" libtalerutil
away and skip #TALER_OS_init(), which we do need */ away and skip #TALER_OS_init(), which we do need */

View File

@ -97,6 +97,13 @@ static int allow_address_reuse;
*/ */
const struct GNUNET_CONFIGURATION_Handle *TEH_cfg; const struct GNUNET_CONFIGURATION_Handle *TEH_cfg;
/**
* Configuration of age restriction
*
* Set after loading the library, enabled in database event handler.
*/
struct TALER_AgeRestrictionConfig TEH_age_restriction_config = {0};
/** /**
* Handle to the HTTP server. * Handle to the HTTP server.
*/ */
@ -138,11 +145,6 @@ char *TEH_currency;
*/ */
char *TEH_base_url; char *TEH_base_url;
/**
* Age restriction flags and mask
*/
bool TEH_age_restriction_enabled = true;
/** /**
* Default timeout in seconds for HTTP requests. * Default timeout in seconds for HTTP requests.
*/ */
@ -170,6 +172,7 @@ bool TEH_suicide;
* TALER_SIGNATURE_MASTER_EXTENSION. * TALER_SIGNATURE_MASTER_EXTENSION.
*/ */
struct TALER_MasterSignatureP TEH_extensions_sig; struct TALER_MasterSignatureP TEH_extensions_sig;
bool TEH_extensions_signed = false;
/** /**
* Value to return from main() * Value to return from main()
@ -1038,6 +1041,46 @@ handle_post_auditors (struct TEH_RequestContext *rc,
} }
/**
* Handle POST "/extensions/..." requests.
*
* @param rc request context
* @param root uploaded JSON data
* @param args array of additional options
* @return MHD result code
*/
static MHD_RESULT
handle_post_extensions (struct TEH_RequestContext *rc,
const json_t *root,
const char *const args[])
{
const struct TALER_Extension *ext = NULL;
if (NULL == args[0])
{
GNUNET_break_op (0);
return r404 (rc->connection,
"/extensions/$EXTENSION");
}
ext = TALER_extensions_get_by_name (args[0]);
if (NULL == ext)
{
GNUNET_break_op (0);
return r404 (rc->connection,
"/extensions/$EXTENSION unknown");
}
if (NULL == ext->http_post_handler)
return MHD_HTTP_NOT_IMPLEMENTED;
return ext->http_post_handler (
rc->connection,
root,
&args[1]);
}
/** /**
* Handle incoming HTTP request. * Handle incoming HTTP request.
* *
@ -1255,6 +1298,14 @@ handle_mhd_request (void *cls,
.nargs = 4, .nargs = 4,
.nargs_is_upper_bound = true .nargs_is_upper_bound = true
}, },
/* extensions endpoints */
{
.url = "extensions",
.method = MHD_HTTP_METHOD_POST,
.handler.post = &handle_post_extensions,
.nargs = 4, /* Arbitrary upper bound */
.nargs_is_upper_bound = true,
},
/* mark end of list */ /* mark end of list */
{ {
.url = NULL .url = NULL

View File

@ -197,11 +197,6 @@ extern struct TALER_EXCHANGEDB_Plugin *TEH_plugin;
*/ */
extern char *TEH_currency; extern char *TEH_currency;
/*
* Age restriction extension state
*/
extern bool TEH_age_restriction_enabled;
/** /**
* Our (externally visible) base URL. * Our (externally visible) base URL.
*/ */
@ -221,6 +216,7 @@ extern struct GNUNET_CURL_Context *TEH_curl_ctx;
* Signature of the offline master key of all enabled extensions' configuration * Signature of the offline master key of all enabled extensions' configuration
*/ */
extern struct TALER_MasterSignatureP TEH_extensions_sig; extern struct TALER_MasterSignatureP TEH_extensions_sig;
extern bool TEH_extensions_signed;
/** /**
* @brief Struct describing an URL and the handler for it. * @brief Struct describing an URL and the handler for it.
@ -366,4 +362,7 @@ struct TEH_RequestHandler
}; };
/* Age restriction configuration */
extern struct TALER_AgeRestrictionConfig TEH_age_restriction_config;
#endif #endif

View File

@ -100,6 +100,9 @@ extension_update_event_cb (void *cls,
// No config found -> disable extension // No config found -> disable extension
if (NULL == config_str) if (NULL == config_str)
{ {
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"No configuration found for extension %s, disabling it\n",
extension->name);
extension->disable ((struct TALER_Extension *) extension); extension->disable ((struct TALER_Extension *) extension);
return; return;
} }
@ -114,28 +117,40 @@ extension_update_event_cb (void *cls,
err.text, err.text,
err.source); err.source);
GNUNET_break (0); GNUNET_break (0);
free(config_str);
return; return;
} }
// Call the parser for the extension // Call the parser for the extension
ret = extension->load_json_config ( ret = extension->load_json_config (
(struct TALER_Extension *) extension, (struct TALER_Extension *) extension,
config); json_object_get(config, "config"));
if (GNUNET_OK != ret) if (GNUNET_OK != ret)
{ {
GNUNET_log (GNUNET_ERROR_TYPE_ERROR, GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Couldn't parse configuration for extension %s from the database", "Couldn't parse configuration for extension %s from the database: %s\n",
extension->name); extension->name,
config_str);
GNUNET_break (0); GNUNET_break (0);
} }
free(config_str);
json_decref(config);
} }
/* Special case age restriction: Update global flag and mask */ /* Special case age restriction: Update global flag and mask */
if (TALER_Extension_AgeRestriction == type) if (TALER_Extension_AgeRestriction == type)
{ {
TEH_age_restriction_enabled = const struct TALER_AgeRestrictionConfig *conf =
TALER_extensions_age_restriction_is_enabled (); TALER_extensions_get_age_restriction_config ();
if (NULL != conf)
TEH_age_restriction_config = *conf;
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"[age restriction] DB event has changed the config to %s with mask: %s\n",
conf->enabled ? "enabled": "disabled",
TALER_age_mask_to_string (&conf->mask));
} }
} }
@ -143,14 +158,21 @@ extension_update_event_cb (void *cls,
enum GNUNET_GenericReturnValue enum GNUNET_GenericReturnValue
TEH_extensions_init () TEH_extensions_init ()
{ {
GNUNET_assert (GNUNET_OK ==
TALER_extension_age_restriction_register ());
/* Set the event handler for updates */ /* 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),
}; };
/* Load the shared libraries first */
if (GNUNET_OK !=
TALER_extensions_load (TEH_cfg))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"failed to load extensions");
return GNUNET_SYSERR;
}
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,
@ -162,17 +184,20 @@ TEH_extensions_init ()
return GNUNET_SYSERR; return GNUNET_SYSERR;
} }
/* FIXME #7270: shall we load the extensions from the config right away?
* We do have to for now, as otherwise denominations with age restriction
* will not have the age mask set right upon initial generation.
*/
TALER_extensions_load_taler_config (TEH_cfg);
/* Trigger the initial load of configuration from the db */ /* Trigger the initial load of configuration from the db */
for (const struct TALER_Extension *it = TALER_extensions_get_head (); for (const struct TALER_Extensions *it = TALER_extensions_get_head ();
NULL != it->next; NULL != it && NULL != it->extension;
it = it->next) it = it->next)
extension_update_event_cb (NULL, &it->type, sizeof(it->type)); {
const struct TALER_Extension *ext = it->extension;
char *conf = json_dumps (ext->config_to_json (ext), JSON_COMPACT);
TEH_plugin->set_extension_config (TEH_plugin->cls,
ext->name,
conf);
extension_update_event_cb (NULL, &ext->type, sizeof(ext->type));
free (conf);
}
return GNUNET_OK; return GNUNET_OK;
} }

View File

@ -815,10 +815,7 @@ static struct TALER_AgeMask
load_age_mask (const char*section_name) load_age_mask (const char*section_name)
{ {
static const struct TALER_AgeMask null_mask = {0}; static const struct TALER_AgeMask null_mask = {0};
struct TALER_AgeMask age_mask = TALER_extensions_age_restriction_ageMask (); enum GNUNET_GenericReturnValue ret;
if (age_mask.bits == 0)
return null_mask;
if (GNUNET_OK != (GNUNET_CONFIGURATION_have_value ( if (GNUNET_OK != (GNUNET_CONFIGURATION_have_value (
TEH_cfg, TEH_cfg,
@ -826,9 +823,6 @@ load_age_mask (const char*section_name)
"AGE_RESTRICTED"))) "AGE_RESTRICTED")))
return null_mask; return null_mask;
{
enum GNUNET_GenericReturnValue ret;
if (GNUNET_SYSERR == if (GNUNET_SYSERR ==
(ret = GNUNET_CONFIGURATION_get_value_yesno (TEH_cfg, (ret = GNUNET_CONFIGURATION_get_value_yesno (TEH_cfg,
section_name, section_name,
@ -840,8 +834,18 @@ load_age_mask (const char*section_name)
"Value must be YES or NO\n"); "Value must be YES or NO\n");
return null_mask; return null_mask;
} }
if (GNUNET_OK == ret)
{
if (! TEH_age_restriction_config.enabled)
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"age restriction set in section %s, yet, age restriction is not enabled\n",
section_name);
return TEH_age_restriction_config.mask;
} }
return age_mask;
return null_mask;
} }
@ -1898,34 +1902,29 @@ create_krd (struct TEH_KeyStateHandle *ksh,
bool has_extensions = false; bool has_extensions = false;
/* Fill in the configurations of the enabled extensions */ /* Fill in the configurations of the enabled extensions */
for (const struct TALER_Extension *extension = TALER_extensions_get_head (); for (const struct TALER_Extensions *iter = TALER_extensions_get_head ();
NULL != extension; NULL != iter && NULL != iter->extension;
extension = extension->next) iter = iter->next)
{ {
const struct TALER_Extension *extension = iter->extension;
json_t *ext; json_t *ext;
json_t *config_json;
int r; int r;
/* skip if not configured == disabled */ /* skip if not enabled */
if (NULL == extension->config || if (! extension->enabled)
NULL == extension->config_json)
continue; continue;
/* flag our findings so far */ /* flag our findings so far */
has_extensions = true; has_extensions = true;
GNUNET_assert (NULL != extension->config_json); GNUNET_assert (NULL != extension->config_json);
config_json = json_copy (extension->config_json);
GNUNET_assert (NULL != config_json);
ext = GNUNET_JSON_PACK ( ext = GNUNET_JSON_PACK (
GNUNET_JSON_pack_bool ("critical", GNUNET_JSON_pack_bool ("critical",
extension->critical), extension->critical),
GNUNET_JSON_pack_string ("version", GNUNET_JSON_pack_string ("version",
extension->version), extension->version),
GNUNET_JSON_pack_object_steal ("config", GNUNET_JSON_pack_object_incref ("config",
config_json) extension->config_json)
); );
GNUNET_assert (NULL != ext); GNUNET_assert (NULL != ext);
@ -1948,6 +1947,9 @@ create_krd (struct TEH_KeyStateHandle *ksh,
extensions); extensions);
GNUNET_assert (0 == r); GNUNET_assert (0 == r);
/* Add the signature of the extensions, if it is not zero */
if (TEH_extensions_signed)
{
sig = GNUNET_JSON_PACK ( sig = GNUNET_JSON_PACK (
GNUNET_JSON_pack_data_auto ("extensions_sig", GNUNET_JSON_pack_data_auto ("extensions_sig",
&TEH_extensions_sig)); &TEH_extensions_sig));
@ -1955,6 +1957,7 @@ create_krd (struct TEH_KeyStateHandle *ksh,
r = json_object_update (keys, sig); r = json_object_update (keys, sig);
GNUNET_assert (0 == r); GNUNET_assert (0 == r);
} }
}
else else
{ {
json_decref (extensions); json_decref (extensions);
@ -2560,6 +2563,7 @@ build_key_state (struct HelperState *hs,
true); true);
return NULL; return NULL;
} }
/* NOTE: ONLY fetches non-revoked AND master-signed signkeys! */ /* NOTE: ONLY fetches non-revoked AND master-signed signkeys! */
qs = TEH_plugin->iterate_active_signkeys (TEH_plugin->cls, qs = TEH_plugin->iterate_active_signkeys (TEH_plugin->cls,
&signkey_info_cb, &signkey_info_cb,

View File

@ -108,6 +108,8 @@ set_extensions (void *cls,
taler_ext->name, taler_ext->name,
config); config);
free (config);
if (qs < 0) if (qs < 0)
{ {
if (GNUNET_DB_STATUS_SOFT_ERROR == qs) if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
@ -137,6 +139,7 @@ set_extensions (void *cls,
/* All extensions configured, update the signature */ /* All extensions configured, update the signature */
TEH_extensions_sig = sec->extensions_sig; TEH_extensions_sig = sec->extensions_sig;
TEH_extensions_signed = true;
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 */
} }

View File

@ -603,7 +603,7 @@ resolve_refreshes_reveal_denominations (
&rrc->coin_envelope_hash); &rrc->coin_envelope_hash);
} }
if (TEH_age_restriction_enabled && if (TEH_age_restriction_config.enabled &&
((NULL == old_age_commitment_json) != ((NULL == old_age_commitment_json) !=
TALER_AgeCommitmentHash_isNullOrZero ( TALER_AgeCommitmentHash_isNullOrZero (
&rctx->melt.session.coin.h_age_commitment))) &rctx->melt.session.coin.h_age_commitment)))
@ -614,7 +614,7 @@ resolve_refreshes_reveal_denominations (
/* Reconstruct the old age commitment and verify its hash matches the one /* Reconstruct the old age commitment and verify its hash matches the one
* from the melt request */ * from the melt request */
if (TEH_age_restriction_enabled && if (TEH_age_restriction_config.enabled &&
(NULL != old_age_commitment_json)) (NULL != old_age_commitment_json))
{ {
enum GNUNET_GenericReturnValue res; enum GNUNET_GenericReturnValue res;
@ -623,8 +623,7 @@ resolve_refreshes_reveal_denominations (
bool failed = true; bool failed = true;
/* Has been checked in handle_refreshes_reveal_json() */ /* Has been checked in handle_refreshes_reveal_json() */
GNUNET_assert (ng == GNUNET_assert (ng == TEH_age_restriction_config.num_groups);
TALER_extensions_age_restriction_num_groups ());
rctx->old_age_commitment = GNUNET_new (struct TALER_AgeCommitment); rctx->old_age_commitment = GNUNET_new (struct TALER_AgeCommitment);
oac = rctx->old_age_commitment; oac = rctx->old_age_commitment;
@ -931,7 +930,7 @@ handle_refreshes_reveal_json (struct MHD_Connection *connection,
/* Sanity check of age commitment: If it was provided, it _must_ be an array /* Sanity check of age commitment: If it was provided, it _must_ be an array
* of the size the # of age groups */ * of the size the # of age groups */
if (NULL != old_age_commitment_json if (NULL != old_age_commitment_json
&& TALER_extensions_age_restriction_num_groups () != && TEH_age_restriction_config.num_groups !=
json_array_size (old_age_commitment_json)) json_array_size (old_age_commitment_json))
{ {
GNUNET_break_op (0); GNUNET_break_op (0);

View File

@ -11,7 +11,7 @@ if USE_COVERAGE
endif endif
# Libraries # Basic extension handling library
lib_LTLIBRARIES = \ lib_LTLIBRARIES = \
libtalerextensions.la libtalerextensions.la
@ -22,7 +22,7 @@ libtalerextensions_la_LDFLAGS = \
libtalerextensions_la_SOURCES = \ libtalerextensions_la_SOURCES = \
extensions.c \ extensions.c \
extension_age_restriction.c age_restriction_helper.c
libtalerextensions_la_LIBADD = \ libtalerextensions_la_LIBADD = \
$(top_builddir)/src/json/libtalerjson.la \ $(top_builddir)/src/json/libtalerjson.la \
@ -31,3 +31,4 @@ libtalerextensions_la_LIBADD = \
-lgnunetutil \ -lgnunetutil \
-ljansson \ -ljansson \
$(XLIB) $(XLIB)

View File

@ -0,0 +1,33 @@
# This Makefile.am is in the public domain
AM_CPPFLAGS = \
-I$(top_srcdir)/src/include \
$(LIBGCRYPT_CFLAGS) \
$(POSTGRESQL_CPPFLAGS)
if USE_COVERAGE
AM_CFLAGS = --coverage -O0
XLIB = -lgcov
endif
# Age restriction as extension library
plugindir = $(libdir)/taler
plugin_LTLIBRARIES = \
libtaler_extension_age_restriction.la
libtaler_extension_age_restriction_la_LDFLAGS = \
-version-info 0:0:0 \
-no-undefined
libtaler_extension_age_restriction_la_SOURCES = \
extension_age_restriction.c
libtaler_extension_auctionbrandt_la_LIBADD = \
$(top_builddir)/src/json/libtalerjson.la \
$(top_builddir)/src/util/libtalerutil.la \
-lgnunetjson \
-lgnunetutil \
-ljansson \
$(XLIB)

View File

@ -23,102 +23,6 @@
#include "taler_extensions.h" #include "taler_extensions.h"
#include "stdint.h" #include "stdint.h"
/**
* Carries all the information we need for age restriction
*/
struct age_restriction_config
{
struct TALER_AgeMask mask;
size_t num_groups;
};
/**
* Global config for this extension
*/
static struct age_restriction_config TE_age_restriction_config = {0};
enum GNUNET_GenericReturnValue
TALER_parse_age_group_string (
const char *groups,
struct TALER_AgeMask *mask)
{
const char *pos = groups;
unsigned int prev = 0;
unsigned int val = 0;
char c;
while (*pos)
{
c = *pos++;
if (':' == c)
{
if (prev >= val)
return GNUNET_SYSERR;
mask->bits |= 1 << val;
prev = val;
val = 0;
continue;
}
if ('0'>c || '9'<c)
return GNUNET_SYSERR;
val = 10 * val + c - '0';
if (0>=val || 32<=val)
return GNUNET_SYSERR;
}
if (32<=val || prev>=val)
return GNUNET_SYSERR;
mask->bits |= (1 << val);
mask->bits |= 1; // mark zeroth group, too
return GNUNET_OK;
}
char *
TALER_age_mask_to_string (
const struct TALER_AgeMask *mask)
{
uint32_t bits = mask->bits;
unsigned int n = 0;
char *buf = GNUNET_malloc (32 * 3); // max characters possible
char *pos = buf;
if (NULL == buf)
{
return buf;
}
while (bits != 0)
{
bits >>= 1;
n++;
if (0 == (bits & 1))
{
continue;
}
if (n > 9)
{
*(pos++) = '0' + n / 10;
}
*(pos++) = '0' + n % 10;
if (0 != (bits >> 1))
{
*(pos++) = ':';
}
}
return buf;
}
/* ================================================== /* ==================================================
* *
* Age Restriction TALER_Extension implementation * Age Restriction TALER_Extension implementation
@ -126,6 +30,12 @@ TALER_age_mask_to_string (
* ================================================== * ==================================================
*/ */
/**
* @brief local configuration
*/
static struct TALER_AgeRestrictionConfig AR_config = {0};
/** /**
* @brief implements the TALER_Extension.disable interface. * @brief implements the TALER_Extension.disable interface.
* *
@ -138,6 +48,7 @@ age_restriction_disable (
if (NULL == ext) if (NULL == ext)
return; return;
ext->enabled = false;
ext->config = NULL; ext->config = NULL;
if (NULL != ext->config_json) if (NULL != ext->config_json)
@ -146,86 +57,9 @@ age_restriction_disable (
ext->config_json = NULL; ext->config_json = NULL;
} }
TE_age_restriction_config.mask.bits = 0; AR_config.enabled = false;
TE_age_restriction_config.num_groups = 0; AR_config.mask.bits = 0;
} AR_config.num_groups = 0;
/**
* @brief implements the TALER_Extension.load_taler_config interface.
*
* @param ext Pointer to the current extension
* @param cfg Handle to the GNUNET configuration
* @return Error if extension for age restriction was set, but age groups were
* invalid, OK otherwise.
*/
static enum GNUNET_GenericReturnValue
age_restriction_load_taler_config (
struct TALER_Extension *ext,
const struct GNUNET_CONFIGURATION_Handle *cfg)
{
char *groups = NULL;
enum GNUNET_GenericReturnValue ret = GNUNET_SYSERR;
struct TALER_AgeMask mask = {0};
if ((GNUNET_YES !=
GNUNET_CONFIGURATION_have_value (cfg,
TALER_EXTENSION_SECTION_AGE_RESTRICTION,
"ENABLED"))
||
(GNUNET_YES !=
GNUNET_CONFIGURATION_get_value_yesno (cfg,
TALER_EXTENSION_SECTION_AGE_RESTRICTION,
"ENABLED")))
{
/* Age restriction is not enabled */
ext->config = NULL;
ext->config_json = NULL;
return GNUNET_OK;
}
/* Age restriction is enabled, extract age groups */
if ((GNUNET_YES ==
GNUNET_CONFIGURATION_have_value (cfg,
TALER_EXTENSION_SECTION_AGE_RESTRICTION,
"AGE_GROUPS"))
&&
(GNUNET_YES !=
GNUNET_CONFIGURATION_get_value_string (cfg,
TALER_EXTENSION_SECTION_AGE_RESTRICTION,
"AGE_GROUPS",
&groups)))
return GNUNET_SYSERR;
mask.bits = TALER_EXTENSION_AGE_RESTRICTION_DEFAULT_AGE_MASK;
ret = GNUNET_OK;
if (groups != NULL)
{
ret = TALER_parse_age_group_string (groups, &mask);
if (GNUNET_OK != ret)
mask.bits = TALER_EXTENSION_AGE_RESTRICTION_DEFAULT_AGE_MASK;
}
if (GNUNET_OK == ret)
{
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"setting age mask to %x with #groups: %d\n", mask.bits,
__builtin_popcount (mask.bits) - 1);
TE_age_restriction_config.mask.bits = mask.bits;
TE_age_restriction_config.num_groups = __builtin_popcount (mask.bits) - 1; /* no underflow, first bit always set */
ext->config = &TE_age_restriction_config;
/* Note: we do now have TE_age_restriction_config set, however
* ext->config_json is NOT set, i.e. the extension is not yet active! For
* age restriction to become active, load_json_config must have been
* called. */
}
GNUNET_free (groups);
return ret;
} }
@ -254,24 +88,25 @@ age_restriction_load_json_config (
if (TALER_Extension_AgeRestriction != ext->type) if (TALER_Extension_AgeRestriction != ext->type)
return GNUNET_SYSERR; return GNUNET_SYSERR;
TE_age_restriction_config.mask.bits = mask.bits;
TE_age_restriction_config.num_groups = 0;
if (mask.bits > 0) if (mask.bits > 0)
{ {
/* if the mask is not zero, the first bit MUST be set */ /* if the mask is not zero, the first bit MUST be set */
if (0 == (mask.bits & 1)) if (0 == (mask.bits & 1))
return GNUNET_SYSERR; return GNUNET_SYSERR;
TE_age_restriction_config.num_groups = __builtin_popcount (mask.bits) - 1; AR_config.mask.bits = mask.bits;
AR_config.num_groups = __builtin_popcount (mask.bits) - 1;
} }
ext->config = &TE_age_restriction_config; AR_config.enabled = true;
ext->config = &AR_config;
if (NULL != ext->config_json) if (NULL != ext->config_json)
json_decref (ext->config_json); json_decref (ext->config_json);
ext->config_json = jconfig; ext->enabled = true;
ext->config_json = json_copy(jconfig);
json_decref(jconfig);
GNUNET_log (GNUNET_ERROR_TYPE_WARNING, GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"loaded new age restriction config with age groups: %s\n", "loaded new age restriction config with age groups: %s\n",
@ -296,6 +131,13 @@ age_restriction_config_to_json (
GNUNET_assert (NULL != ext); GNUNET_assert (NULL != ext);
if (! ext->enabled)
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"age restriction not enabled");
return json_null ();
}
if (NULL == ext->config) if (NULL == ext->config)
{ {
GNUNET_log (GNUNET_ERROR_TYPE_WARNING, GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
@ -308,11 +150,13 @@ age_restriction_config_to_json (
return json_copy (ext->config_json); return json_copy (ext->config_json);
} }
mask_str = TALER_age_mask_to_string (&TE_age_restriction_config.mask); mask_str = TALER_age_mask_to_string (&AR_config.mask);
conf = GNUNET_JSON_PACK ( conf = GNUNET_JSON_PACK (
GNUNET_JSON_pack_string ("age_groups", mask_str) GNUNET_JSON_pack_string ("age_groups", mask_str)
); );
free(mask_str);
return GNUNET_JSON_PACK ( return GNUNET_JSON_PACK (
GNUNET_JSON_pack_bool ("critical", ext->critical), GNUNET_JSON_pack_bool ("critical", ext->critical),
GNUNET_JSON_pack_string ("version", ext->version), GNUNET_JSON_pack_string ("version", ext->version),
@ -338,71 +182,119 @@ age_restriction_test_json_config (
/* The extension for age restriction */ /* The extension for age restriction */
struct TALER_Extension TE_age_restriction = { struct TALER_Extension TE_extension_age_restriction = {
.next = NULL,
.type = TALER_Extension_AgeRestriction, .type = TALER_Extension_AgeRestriction,
.name = "age_restriction", .name = "age_restriction",
.critical = false, .critical = false,
.version = "1", .version = "1",
.config = NULL, // disabled per default .enabled = false, /* disabled per default */
.config = NULL,
.config_json = NULL, .config_json = NULL,
.disable = &age_restriction_disable, .disable = &age_restriction_disable,
.test_json_config = &age_restriction_test_json_config, .test_json_config = &age_restriction_test_json_config,
.load_json_config = &age_restriction_load_json_config, .load_json_config = &age_restriction_load_json_config,
.config_to_json = &age_restriction_config_to_json, .config_to_json = &age_restriction_config_to_json,
.load_taler_config = &age_restriction_load_taler_config, .http_post_handler = NULL,
}; };
enum GNUNET_GenericReturnValue
TALER_extension_age_restriction_register () /**
* @brief implements the init() function for GNUNET_PLUGIN_load
*
* @param arg Pointer to the GNUNET_CONFIGURATION_Handle
* @return pointer to TALER_Extension on success or NULL otherwise.
*/
void *
libtaler_extension_age_restriction_init (void *arg)
{ {
return TALER_extensions_add (&TE_age_restriction); const struct GNUNET_CONFIGURATION_Handle *cfg = arg;
char *groups = NULL;
struct TALER_AgeMask mask = {0};
if ((GNUNET_YES !=
GNUNET_CONFIGURATION_have_value (cfg,
TALER_EXTENSION_SECTION_AGE_RESTRICTION,
"ENABLED"))
||
(GNUNET_YES !=
GNUNET_CONFIGURATION_get_value_yesno (cfg,
TALER_EXTENSION_SECTION_AGE_RESTRICTION,
"ENABLED")))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"[age restriction] no section %s found in configuration\n",
TALER_EXTENSION_SECTION_AGE_RESTRICTION);
return NULL;
}
/* Age restriction is enabled, extract age groups */
if ((GNUNET_YES ==
GNUNET_CONFIGURATION_have_value (cfg,
TALER_EXTENSION_SECTION_AGE_RESTRICTION,
"AGE_GROUPS"))
&&
(GNUNET_YES !=
GNUNET_CONFIGURATION_get_value_string (cfg,
TALER_EXTENSION_SECTION_AGE_RESTRICTION,
"AGE_GROUPS",
&groups)))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"[age restriction] AGE_GROUPS in %s is not a string\n",
TALER_EXTENSION_SECTION_AGE_RESTRICTION);
return NULL;
}
mask.bits = TALER_EXTENSION_AGE_RESTRICTION_DEFAULT_AGE_MASK;
if ((groups != NULL) &&
(GNUNET_OK != TALER_parse_age_group_string (groups, &mask)))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"[age restriction] couldn't parse age groups: '%s'\n",
groups);
return NULL;
}
AR_config.mask = mask;
AR_config.num_groups = __builtin_popcount (mask.bits) - 1; /* no underflow, first bit always set */
AR_config.enabled = true;
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"[age restriction] setting age mask to %s with #groups: %d\n",
TALER_age_mask_to_string(&AR_config.mask),
__builtin_popcount (AR_config.mask.bits) - 1);
TE_extension_age_restriction.config = &AR_config;
TE_extension_age_restriction.enabled = true;
/* Note: we do now have TE_age_restriction_config set, however
* ext->config_json is NOT set, i.e. the extension is not yet active! For
* age restriction to become active, load_json_config must have been
* called. */
GNUNET_free (groups);
return &TE_extension_age_restriction;
} }
bool /**
TALER_extensions_age_restriction_is_configured () * @brief implements the done() function for GNUNET_PLUGIN_load
*
* @param cfg unsued
* @return pointer to TALER_Extension on success or NULL otherwise.
*/
void *
libtaler_extension_age_restriction_done (void *arg)
{ {
return (0 != TE_age_restriction_config.mask.bits); GNUNET_log (GNUNET_ERROR_TYPE_INFO,
} "[age restriction] disabling and unloading");
AR_config.enabled = 0;
AR_config.mask.bits = 0;
struct TALER_AgeMask AR_config.num_groups = 0;
TALER_extensions_age_restriction_ageMask () return NULL;
{
return TE_age_restriction_config.mask;
}
size_t
TALER_extensions_age_restriction_num_groups ()
{
return TE_age_restriction_config.num_groups;
}
enum GNUNET_GenericReturnValue
TALER_JSON_parse_age_groups (const json_t *root,
struct TALER_AgeMask *mask)
{
enum GNUNET_GenericReturnValue ret;
const char *str;
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_string ("age_groups",
&str),
GNUNET_JSON_spec_end ()
};
ret = GNUNET_JSON_parse (root,
spec,
NULL,
NULL);
if (GNUNET_OK == ret)
TALER_parse_age_group_string (str, mask);
GNUNET_JSON_parse_free (spec);
return ret;
} }

View File

@ -0,0 +1,74 @@
/*
This file is part of TALER
Copyright (C) 2022- Taler Systems SA
TALER 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 3, or (at your option) any later version.
TALER 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.
You should have received a copy of the GNU General Public License along with
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
/**
* @file age_restriction_helper.c
* @brief Helper functions for age restriction
* @author Özgür Kesim
*/
#include "platform.h"
#include "taler_util.h"
#include "taler_signatures.h"
#include "taler_extensions.h"
#include "stdint.h"
const struct TALER_AgeRestrictionConfig *
TALER_extensions_get_age_restriction_config ()
{
const struct TALER_Extension *ext;
ext = TALER_extensions_get_by_type (TALER_Extension_AgeRestriction);
if (NULL == ext)
return NULL;
return ext->config;
}
bool
TALER_extensions_is_age_restriction_enabled ()
{
const struct TALER_Extension *ext;
ext = TALER_extensions_get_by_type (TALER_Extension_AgeRestriction);
if (NULL == ext)
return false;
return ext->enabled;
}
struct TALER_AgeMask
TALER_extensions_get_age_restriction_mask ()
{
const struct TALER_Extension *ext;
const struct TALER_AgeRestrictionConfig *conf;
ext = TALER_extensions_get_by_type (TALER_Extension_AgeRestriction);
if ((NULL == ext) ||
(NULL == ext->config) ||
(! ext->enabled))
return (struct TALER_AgeMask) {0}
;
conf = ext->config;
return conf->mask;
}
/* end age_restriction_helper.c */

View File

@ -0,0 +1,34 @@
# This Makefile.am is in the public domain
AM_CPPFLAGS = \
-I$(top_srcdir)/src/include \
$(LIBGCRYPT_CFLAGS) \
$(POSTGRESQL_CPPFLAGS)
if USE_COVERAGE
AM_CFLAGS = --coverage -O0
XLIB = -lgcov
endif
# Auction of Brandt type as extension library
plugindir = $(libdir)/taler
plugin_LTLIBRARIES = \
libtaler_extension_auction_brandt.la
libtaler_extension_auction_brandt_la_LDFLAGS = \
-version-info 0:0:0 \
-no-undefined
libtaler_extension_auction_brandt_la_SOURCES = \
extension_auction_brandt.c
libtaler_extension_auction_brandt_la_LIBADD = \
$(top_builddir)/src/json/libtalerjson.la \
$(top_builddir)/src/util/libtalerutil.la \
-lgnunetjson \
-lgnunetutil \
-ljansson \
$(XLIB)

View File

@ -0,0 +1,182 @@
/*
This file is part of TALER
Copyright (C) 2021-2022 Taler Systems SA
TALER 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 3, or (at your option) any later version.
TALER 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.
You should have received a copy of the GNU General Public License along with
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
/**
* @file extension_auction_brandt.c
* @brief Extension for replay of auctions of type brandt
* @author Özgür Kesim
*/
#include "platform.h"
#include "taler_util.h"
#include "taler_extensions.h"
#include "../../exchange/taler-exchange-httpd.h"
#include "taler_mhd_lib.h"
#include "stdint.h"
#include <microhttpd.h>
#define EXTENSION_NAME "auction_brandt"
/* Path to the replay program. */
static char *replay_program;
/**
* @brief implements the TALER_Extension.disable interface.
*
* @param ext Pointer to the current extension
*/
static void
auction_disable (
struct TALER_Extension *ext)
{
ext->enabled = false;
}
/**
* @brief implements the TALER_Extension.test_json_config interface.
*
* @param config configuration as json_t* to test
* @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise.
*/
static enum GNUNET_GenericReturnValue
auction_test_json_config (
const json_t *config)
{
/* This extension has no configuration */
return GNUNET_OK;
}
/**
* @brief implements the TALER_Extension.config_to_json interface.
*
* @param ext if NULL, only tests the configuration
* @return configuration as json_t* object, maybe NULL
*/
static json_t *
auction_config_to_json (
const struct TALER_Extension *ext)
{
/* This extension has no configuration */
return json_null ();
}
/**
* @brief implements the TALER_Extension.load_json_config interface.
*
* @param ext if NULL, only tests the configuration
* @param jconfig the configuration as json
*/
static enum GNUNET_GenericReturnValue
auction_load_json_config (
struct TALER_Extension *ext,
json_t *jconfig)
{
/* This extension has no configuration */
ext->enabled = true;
return GNUNET_OK;
}
/**
* @brief implements the TALER_Extension.http_post_handler
*/
static MHD_RESULT
auction_http_post_handler (
struct MHD_Connection *connection,
const json_t *root,
const char *const args[])
{
/* TODO */
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"auction_http_post_handler not implemented yet");
return MHD_HTTP_NOT_IMPLEMENTED;
}
/* The extension struct for auctions of brandt-style */
struct TALER_Extension TE_auction_brandt = {
.type = 0x0815, /* TODO: where do we get this from? */
.name = EXTENSION_NAME,
.critical = false,
.version = "0",
.enabled = false, /* disabled per default */
.config = NULL,
.config_json = NULL,
.disable = &auction_disable,
.test_json_config = &auction_test_json_config,
.load_json_config = &auction_load_json_config,
.config_to_json = &auction_config_to_json,
.http_post_handler = &auction_http_post_handler,
};
/* TODO: sql handler */
/**
* ===========================================
* Handler for GNUNET_PLUGIN_load and _unload
* ===========================================
*/
/**
* @brief Initialization function for the extension.
* Will be called by GNUNET_PLUGIN_load.
*
* @param arg Configuration - ptr to GNUNET_CONFIGURATION_Handle
* @return Pointer to TE_auction_brandt
*/
struct TALER_Extension *
init (void *arg)
{
const struct GNUNET_CONFIGURATION_Handle *cfg = arg;
if (GNUNET_SYSERR ==
GNUNET_CONFIGURATION_get_value_string (cfg,
"extension_" EXTENSION_NAME,
"replay_program",
&replay_program))
{
GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
"extension_" EXTENSION_NAME,
"replay_program");
return NULL;
}
return &TE_auction_brandt;
}
/**
* @brief Tear-down function for the extension.
* Will be called by GNUNET_PLUGIN_unload.
*
* @param ignored
* @return null
*/
void *
done (void *arg)
{
auction_disable(&TE_auction_brandt);
GNUNET_free(replay_program);
replay_program=NULL;
return NULL;
}
/* end of extension_auction_brandt.c */

View File

@ -24,21 +24,22 @@
#include "taler_extensions.h" #include "taler_extensions.h"
#include "stdint.h" #include "stdint.h"
/* head of the list of all registered extensions */ /* head of the list of all registered extensions */
static struct TALER_Extension *TE_extensions = NULL; static struct TALER_Extensions TE_extensions = {
.next = NULL,
.extension = NULL,
};
const struct TALER_Extensions *
const struct TALER_Extension *
TALER_extensions_get_head () TALER_extensions_get_head ()
{ {
return TE_extensions; return &TE_extensions;
} }
enum GNUNET_GenericReturnValue static enum GNUNET_GenericReturnValue
TALER_extensions_add ( add_extension (
struct TALER_Extension *extension) const struct TALER_Extension *extension)
{ {
/* Sanity checks */ /* Sanity checks */
if ((NULL == extension) || if ((NULL == extension) ||
@ -47,28 +48,30 @@ TALER_extensions_add (
(NULL == extension->disable) || (NULL == extension->disable) ||
(NULL == extension->test_json_config) || (NULL == extension->test_json_config) ||
(NULL == extension->load_json_config) || (NULL == extension->load_json_config) ||
(NULL == extension->config_to_json) || (NULL == extension->config_to_json))
(NULL == extension->load_taler_config))
{ {
GNUNET_log (GNUNET_ERROR_TYPE_ERROR, GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"invalid extension\n"); "invalid extension\n");
return GNUNET_SYSERR; return GNUNET_SYSERR;
} }
if (NULL == TE_extensions) /* first extension ?*/ if (NULL == TE_extensions.extension) /* first extension ?*/
TE_extensions = (struct TALER_Extension *) extension; TE_extensions.extension = extension;
else else
{ {
struct TALER_Extension *iter; struct TALER_Extensions *iter;
struct TALER_Extension *last; struct TALER_Extensions *last;
/* Check for collisions */ /* Check for collisions */
for (iter = TE_extensions; NULL != iter; iter = iter->next) for (iter = &TE_extensions;
NULL != iter && NULL != iter->extension;
iter = iter->next)
{ {
const struct TALER_Extension *ext = iter->extension;
last = iter; last = iter;
if (extension->type == iter->type || if (extension->type == ext->type ||
0 == strcasecmp (extension->name, 0 == strcasecmp (extension->name,
iter->name)) ext->name))
{ {
GNUNET_log (GNUNET_ERROR_TYPE_ERROR, GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"extension collision for `%s'\n", "extension collision for `%s'\n",
@ -78,7 +81,11 @@ TALER_extensions_add (
} }
/* No collisions found, so add this extension to the list */ /* No collisions found, so add this extension to the list */
last->next = extension; {
struct TALER_Extensions *extn = GNUNET_new (struct TALER_Extensions);
extn->extension = extension;
last->next = extn;
}
} }
return GNUNET_OK; return GNUNET_OK;
@ -89,12 +96,12 @@ const struct TALER_Extension *
TALER_extensions_get_by_type ( TALER_extensions_get_by_type (
enum TALER_Extension_Type type) enum TALER_Extension_Type type)
{ {
for (const struct TALER_Extension *it = TE_extensions; for (const struct TALER_Extensions *it = &TE_extensions;
NULL != it; NULL != it && NULL != it->extension;
it = it->next) it = it->next)
{ {
if (it->type == type) if (it->extension->type == type)
return it; return it->extension;
} }
/* No extension found. */ /* No extension found. */
@ -109,8 +116,7 @@ TALER_extensions_is_enabled_type (
const struct TALER_Extension *ext = const struct TALER_Extension *ext =
TALER_extensions_get_by_type (type); TALER_extensions_get_by_type (type);
return (NULL != ext && return (NULL != ext && ext->enabled);
TALER_extensions_is_enabled (ext));
} }
@ -118,14 +124,15 @@ const struct TALER_Extension *
TALER_extensions_get_by_name ( TALER_extensions_get_by_name (
const char *name) const char *name)
{ {
for (const struct TALER_Extension *it = TE_extensions; for (const struct TALER_Extensions *it = &TE_extensions;
NULL != it; NULL != it;
it = it->next) it = it->next)
{ {
if (0 == strcasecmp (name, it->name)) if (0 == strcasecmp (name, it->extension->name))
return it; return it->extension;
} }
/* No extension found. */ /* No extension found, try to load it. */
return NULL; return NULL;
} }
@ -178,7 +185,8 @@ configure_extension (
{ {
struct LoadConfClosure *col = cls; struct LoadConfClosure *col = cls;
const char *name; const char *name;
const struct TALER_Extension *extension; char *lib_name;
struct TALER_Extension *extension;
if (GNUNET_OK != col->error) if (GNUNET_OK != col->error)
return; return;
@ -190,33 +198,49 @@ configure_extension (
name = section + sizeof(TALER_EXTENSION_SECTION_PREFIX) - 1; name = section + sizeof(TALER_EXTENSION_SECTION_PREFIX) - 1;
if (NULL ==
(extension = TALER_extensions_get_by_name (name)))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Unsupported extension `%s` (section [%s]).\n", name,
section);
col->error = GNUNET_SYSERR;
return;
}
if (GNUNET_OK != /* Load the extension library */
extension->load_taler_config ( GNUNET_asprintf (&lib_name,
(struct TALER_Extension *) extension, "libtaler_extension_%s",
col->cfg)) name);
extension = GNUNET_PLUGIN_load (
lib_name,
(void *) col->cfg);
if (NULL == extension)
{ {
GNUNET_log (GNUNET_ERROR_TYPE_ERROR, GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Couldn't parse configuration for extension `%s` (section [%s]).\n", "Couldn't load extension library to `%s` (section [%s]).\n",
name, name,
section); section);
col->error = GNUNET_SYSERR; col->error = GNUNET_SYSERR;
return; return;
} }
if (GNUNET_OK != add_extension (extension))
{
/* TODO: Ignoring return values here */
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Couldn't add extension `%s` (section [%s]).\n",
name,
section);
col->error = GNUNET_SYSERR;
GNUNET_PLUGIN_unload (
lib_name,
(void *) col->cfg);
return;
}
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"extension library '%s' loaded\n",
lib_name);
} }
static bool extensions_loaded = false;
enum GNUNET_GenericReturnValue enum GNUNET_GenericReturnValue
TALER_extensions_load_taler_config ( TALER_extensions_load (
const struct GNUNET_CONFIGURATION_Handle *cfg) const struct GNUNET_CONFIGURATION_Handle *cfg)
{ {
struct LoadConfClosure col = { struct LoadConfClosure col = {
@ -224,9 +248,16 @@ TALER_extensions_load_taler_config (
.error = GNUNET_OK, .error = GNUNET_OK,
}; };
if (extensions_loaded)
return GNUNET_OK;
GNUNET_CONFIGURATION_iterate_sections (cfg, GNUNET_CONFIGURATION_iterate_sections (cfg,
&configure_extension, &configure_extension,
&col); &col);
if (GNUNET_OK == col.error)
extensions_loaded = true;
return col.error; return col.error;
} }
@ -309,28 +340,16 @@ TALER_extensions_load_json_config (
} }
/* make sure to disable all extensions that weren't mentioned in the json */ /* make sure to disable all extensions that weren't mentioned in the json */
for (const struct TALER_Extension *it = TALER_extensions_get_head (); for (const struct TALER_Extensions *it = TALER_extensions_get_head ();
NULL != it; NULL != it;
it = it->next) it = it->next)
{ {
if (NULL == json_object_get (extensions, it->name)) if (NULL == json_object_get (extensions, it->extension->name))
it->disable ((struct TALER_Extension *) it); it->extension->disable ((struct TALER_Extension *) it);
} }
return GNUNET_OK; return GNUNET_OK;
} }
bool
TALER_extensions_age_restriction_is_enabled ()
{
const struct TALER_Extension *age =
TALER_extensions_get_by_type (TALER_Extension_AgeRestriction);
return (NULL != age &&
NULL != age->config_json &&
TALER_extensions_age_restriction_is_configured ());
}
/* end of extensions.c */ /* end of extensions.c */

View File

@ -24,6 +24,7 @@
#include <gnunet/gnunet_util_lib.h> #include <gnunet/gnunet_util_lib.h>
#include "taler_crypto_lib.h" #include "taler_crypto_lib.h"
#include "taler_json_lib.h" #include "taler_json_lib.h"
#include "taler_mhd_lib.h"
#define TALER_EXTENSION_SECTION_PREFIX "exchange-extension-" #define TALER_EXTENSION_SECTION_PREFIX "exchange-extension-"
@ -34,6 +35,17 @@ enum TALER_Extension_Type
TALER_Extension_MaxPredefined = 1 // Must be last of the predefined TALER_Extension_MaxPredefined = 1 // Must be last of the predefined
}; };
/*
* @brief simply linked list of extensions
*/
struct TALER_Extensions
{
struct TALER_Extensions *next;
const struct TALER_Extension *extension;
};
/* /*
* @brief Represents the implementation of an extension. * @brief Represents the implementation of an extension.
* *
@ -41,14 +53,12 @@ enum TALER_Extension_Type
*/ */
struct TALER_Extension struct TALER_Extension
{ {
/* simple linked list */
struct TALER_Extension *next;
enum TALER_Extension_Type type; enum TALER_Extension_Type type;
char *name; char *name;
bool critical; bool critical;
char *version; char *version;
void *config; void *config;
bool enabled;
json_t *config_json; json_t *config_json;
void (*disable)(struct TALER_Extension *ext); void (*disable)(struct TALER_Extension *ext);
@ -63,9 +73,11 @@ struct TALER_Extension
json_t *(*config_to_json)( json_t *(*config_to_json)(
const struct TALER_Extension *ext); const struct TALER_Extension *ext);
enum GNUNET_GenericReturnValue (*load_taler_config)( MHD_RESULT (*http_post_handler)(
struct TALER_Extension *ext, struct MHD_Connection *connection,
const struct GNUNET_CONFIGURATION_Handle *cfg); const json_t *root,
const char *const args[]);
}; };
/** /**
@ -73,15 +85,15 @@ struct TALER_Extension
*/ */
/* /*
* @brief Sets the configuration of the extensions from the given TALER * @brief Loads the extensions as shared libraries, as specified in the given
* configuration. * TALER configuration.
* *
* @param cfg Handle to the TALER configuration * @param cfg Handle to the TALER configuration
* @return GNUNET_OK on success, GNUNET_SYSERR if unknown extensions were found * @return GNUNET_OK on success, GNUNET_SYSERR if unknown extensions were found
* or any particular configuration couldn't be parsed. * or any particular configuration couldn't be parsed.
*/ */
enum GNUNET_GenericReturnValue enum GNUNET_GenericReturnValue
TALER_extensions_load_taler_config ( TALER_extensions_load (
const struct GNUNET_CONFIGURATION_Handle *cfg); const struct GNUNET_CONFIGURATION_Handle *cfg);
/* /*
@ -118,25 +130,13 @@ TALER_extensions_load_json_config (
/* /*
* @brief Returns the head of the linked list of extensions. * @brief Returns the head of the linked list of extensions.
*/ */
const struct TALER_Extension * const struct TALER_Extensions *
TALER_extensions_get_head (); TALER_extensions_get_head ();
/*
* @brief Adds an extension to the linked list of extensions.
*
* @param new_extension the new extension to be added
* @return GNUNET_OK on success, GNUNET_SYSERR if the extension is invalid
* (missing fields), GNUNET_NO if there is already an extension with that name
* or type.
*/
enum GNUNET_GenericReturnValue
TALER_extensions_add (
struct TALER_Extension *new_extension);
/** /**
* @brief Finds and returns a supported extension by a given type. * @brief Finds and returns a supported extension by a given type.
* *
* @param type type of the extension to lookup * @param type of the extension to lookup
* @return extension found, or NULL (should not happen!) * @return extension found, or NULL (should not happen!)
*/ */
const struct TALER_Extension * const struct TALER_Extension *
@ -154,8 +154,6 @@ const struct TALER_Extension *
TALER_extensions_get_by_name ( TALER_extensions_get_by_name (
const char *name); const char *name);
#define TALER_extensions_is_enabled(ext) (NULL != (ext)->config)
/** /**
* @brief Check if a given type of an extension is enabled * @brief Check if a given type of an extension is enabled
* *
@ -166,6 +164,15 @@ bool
TALER_extensions_is_enabled_type ( TALER_extensions_is_enabled_type (
enum TALER_Extension_Type type); enum TALER_Extension_Type type);
/**
* @brief Check if an extension is enabled
*
* @param extension The extension handler.
* @return true enabled, false if not enabled, will assert if type is not found.
*/
bool
TALER_extensions_is_enabled (
const struct TALER_Extension *extension);
/* /*
* Verify the signature of a given JSON object for extensions with the master * Verify the signature of a given JSON object for extensions with the master
@ -189,6 +196,9 @@ TALER_extensions_verify_json_config_signature (
/* /*
* TALER Age Restriction Extension * TALER Age Restriction Extension
*
* This extension is special insofar as it directly interacts with coins and
* denominations.
*/ */
#define TALER_EXTENSION_SECTION_AGE_RESTRICTION (TALER_EXTENSION_SECTION_PREFIX \ #define TALER_EXTENSION_SECTION_AGE_RESTRICTION (TALER_EXTENSION_SECTION_PREFIX \
@ -204,98 +214,41 @@ TALER_extensions_verify_json_config_signature (
| 1 << 21) | 1 << 21)
#define TALER_EXTENSION_AGE_RESTRICTION_DEFAULT_AGE_GROUPS "8:10:12:14:16:18:21" #define TALER_EXTENSION_AGE_RESTRICTION_DEFAULT_AGE_GROUPS "8:10:12:14:16:18:21"
/**
* @brief Registers the extension for age restriction to the list extensions /*
* @brief Configuration for Age Restriction
*/ */
enum GNUNET_GenericReturnValue struct TALER_AgeRestrictionConfig
TALER_extension_age_restriction_register (); {
bool enabled;
struct TALER_AgeMask mask;
uint8_t num_groups;
};
/** /**
* @brief Parses a string as a list of age groups. * @brief Retrieve the age restriction configuration
* *
* The string must consist of a colon-separated list of increasing integers * @return age restriction configuration if present, otherwise NULL.
* between 0 and 31. Each entry represents the beginning of a new age group.
* F.e. the string
*
* "8:10:12:14:16:18:21"
*
* represents the following list of eight age groups:
*
* | Group | Ages |
* | -----:|:------------- |
* | 0 | 0, 1, ..., 7 |
* | 1 | 8, 9 |
* | 2 | 10, 11 |
* | 3 | 12, 13 |
* | 4 | 14, 15 |
* | 5 | 16, 17 |
* | 6 | 18, 19, 20 |
* | 7 | 21, ... |
*
* which is then encoded as a bit mask with the corresponding bits set:
*
* 31 24 16 8 0
* | | | | |
* oooooooo oo1oo1o1 o1o1o1o1 ooooooo1
*
* @param groups String representation of age groups
* @param[out] mask Mask representation for age restriction.
* @return Error, if age groups were invalid, OK otherwise.
*/ */
enum GNUNET_GenericReturnValue const struct TALER_AgeRestrictionConfig *
TALER_parse_age_group_string ( TALER_extensions_get_age_restriction_config ();
const char *groups,
struct TALER_AgeMask *mask);
/** /**
* @brief Encodes the age mask into a string, like "8:10:12:14:16:18:21" * @brief Check if age restriction is enabled
* *
* @param mask Age mask * @return true, if age restriction is loaded, configured and enabled; otherwise false.
* @return String representation of the age mask, allocated by GNUNET_malloc.
* Can be used as value in the TALER config.
*/
char *
TALER_age_mask_to_string (
const struct TALER_AgeMask *mask);
/**
* @brief Returns true when age restriction is configured and enabled.
*/ */
bool bool
TALER_extensions_age_restriction_is_enabled (); TALER_extensions_is_age_restriction_enabled ();
/** /**
* @brief Returns true when age restriction is configured (might not be * @brief Return the age mask for age restriction
* _enabled_, though). *
*/ * @return configured age mask, if age restriction is loaded, configured and enabled; otherwise zero mask.
bool
TALER_extensions_age_restriction_is_configured ();
/**
* @brief Returns the currently set age mask. Note that even if age
* restriction is not enabled, the age mask might be have a non-zero value.
*/ */
struct TALER_AgeMask struct TALER_AgeMask
TALER_extensions_age_restriction_ageMask (); TALER_extensions_get_age_restriction_mask ();
/**
* @brief Returns the amount of age groups defined. 0 means no age restriction
* enabled.
*/
size_t
TALER_extensions_age_restriction_num_groups ();
/**
* @brief Parses a JSON object { "age_groups": "a:b:...y:z" }.
*
* @param root is the json object
* @param[out] mask on success, will contain the age mask
* @return #GNUNET_OK on success and #GNUNET_SYSERR on failure.
*/
enum GNUNET_GenericReturnValue
TALER_JSON_parse_age_groups (const json_t *root,
struct TALER_AgeMask *mask);
/* /*

View File

@ -492,5 +492,64 @@ char *strchrnul (const char *s, int c);
#endif #endif
/**
* @brief Parses a string as a list of age groups.
*
* The string must consist of a colon-separated list of increasing integers
* between 0 and 31. Each entry represents the beginning of a new age group.
* F.e. the string
*
* "8:10:12:14:16:18:21"
*
* represents the following list of eight age groups:
*
* | Group | Ages |
* | -----:|:------------- |
* | 0 | 0, 1, ..., 7 |
* | 1 | 8, 9 |
* | 2 | 10, 11 |
* | 3 | 12, 13 |
* | 4 | 14, 15 |
* | 5 | 16, 17 |
* | 6 | 18, 19, 20 |
* | 7 | 21, ... |
*
* which is then encoded as a bit mask with the corresponding bits set:
*
* 31 24 16 8 0
* | | | | |
* oooooooo oo1oo1o1 o1o1o1o1 ooooooo1
*
* @param groups String representation of age groups
* @param[out] mask Mask representation for age restriction.
* @return Error, if age groups were invalid, OK otherwise.
*/
enum GNUNET_GenericReturnValue
TALER_parse_age_group_string (
const char *groups,
struct TALER_AgeMask *mask);
/**
* @brief Encodes the age mask into a string, like "8:10:12:14:16:18:21"
*
* @param mask Age mask
* @return String representation of the age mask, allocated by GNUNET_malloc.
* Can be used as value in the TALER config.
*/
char *
TALER_age_mask_to_string (
const struct TALER_AgeMask *mask);
/**
* @brief Parses a JSON object { "age_groups": "a:b:...y:z" }.
*
* @param root is the json object
* @param[out] mask on success, will contain the age mask
* @return #GNUNET_OK on success and #GNUNET_SYSERR on failure.
*/
enum GNUNET_GenericReturnValue
TALER_JSON_parse_age_groups (const json_t *root,
struct TALER_AgeMask *mask);
#endif #endif

View File

@ -902,16 +902,19 @@ decode_keys_json (const json_t *resp_obj,
{ {
struct TALER_MasterSignatureP extensions_sig = {0}; struct TALER_MasterSignatureP extensions_sig = {0};
json_t *extensions = NULL; json_t *extensions = NULL;
bool no_extensions = false;
bool no_signature = false;
struct GNUNET_JSON_Specification ext_spec[] = { struct GNUNET_JSON_Specification ext_spec[] = {
GNUNET_JSON_spec_mark_optional ( GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_json ("extensions", GNUNET_JSON_spec_json ("extensions",
&extensions), &extensions),
NULL), &no_extensions),
GNUNET_JSON_spec_mark_optional ( GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_fixed_auto ( GNUNET_JSON_spec_fixed_auto (
"extensions_sig", "extensions_sig",
&extensions_sig), &extensions_sig),
NULL), &no_signature),
GNUNET_JSON_spec_end () GNUNET_JSON_spec_end ()
}; };
@ -921,7 +924,12 @@ decode_keys_json (const json_t *resp_obj,
ext_spec, ext_spec,
NULL, NULL)); NULL, NULL));
if (NULL != extensions)
if (! no_extensions && no_signature)
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"found extensions without signature\n");
if (! no_extensions && ! no_signature)
{ {
/* 2. We have an extensions object. Verify its signature. */ /* 2. We have an extensions object. Verify its signature. */
EXITIF (GNUNET_OK != EXITIF (GNUNET_OK !=
@ -936,7 +944,7 @@ decode_keys_json (const json_t *resp_obj,
} }
/* 4. assuming we might have now a new value for age_mask, set it in key_data */ /* 4. assuming we might have now a new value for age_mask, set it in key_data */
key_data->age_mask = TALER_extensions_age_restriction_ageMask (); key_data->age_mask = TALER_extensions_get_age_restriction_mask ();
} }
/** /**

View File

@ -1315,9 +1315,6 @@ main (int argc,
"INFO", "INFO",
NULL); NULL);
GNUNET_assert (GNUNET_OK ==
TALER_extension_age_restriction_register ());
cipher = GNUNET_TESTING_get_testname_from_underscore (argv[0]); cipher = GNUNET_TESTING_get_testname_from_underscore (argv[0]);
GNUNET_assert (NULL != cipher); GNUNET_assert (NULL != cipher);
uses_cs = (0 == strcmp (cipher, "cs")); uses_cs = (0 == strcmp (cipher, "cs"));

View File

@ -417,9 +417,6 @@ main (int argc,
"INFO", "INFO",
NULL); NULL);
GNUNET_assert (GNUNET_OK ==
TALER_extension_age_restriction_register ());
cipher = GNUNET_TESTING_get_testname_from_underscore (argv[0]); cipher = GNUNET_TESTING_get_testname_from_underscore (argv[0]);
GNUNET_assert (NULL != cipher); GNUNET_assert (NULL != cipher);
uses_cs = (0 == strcmp (cipher, "cs")); uses_cs = (0 == strcmp (cipher, "cs"));

View File

@ -490,7 +490,7 @@ TALER_TESTING_cmd_batch_withdraw (const char *label,
acp = GNUNET_new (struct TALER_AgeCommitmentProof); acp = GNUNET_new (struct TALER_AgeCommitmentProof);
hac = GNUNET_new (struct TALER_AgeCommitmentHash); hac = GNUNET_new (struct TALER_AgeCommitmentHash);
mask = TALER_extensions_age_restriction_ageMask (); mask = TALER_extensions_get_age_restriction_mask();
GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
&seed, &seed,
sizeof(seed)); sizeof(seed));

View File

@ -590,7 +590,7 @@ TALER_TESTING_cmd_withdraw_amount (const char *label,
acp = GNUNET_new (struct TALER_AgeCommitmentProof); acp = GNUNET_new (struct TALER_AgeCommitmentProof);
hac = GNUNET_new (struct TALER_AgeCommitmentHash); hac = GNUNET_new (struct TALER_AgeCommitmentHash);
mask = TALER_extensions_age_restriction_ageMask (); mask = TALER_extensions_get_age_restriction_mask();
GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
&seed, &seed,
sizeof(seed)); sizeof(seed));

View File

@ -314,8 +314,13 @@ sign_keys_for_exchange (void *cls,
char *exchange_master_pub; char *exchange_master_pub;
int ret; int ret;
/* Load the age restriction mask from the configuration */ /* Load the extensions */
TALER_extensions_load_taler_config (cfg); if (GNUNET_OK != TALER_extensions_load (cfg))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"couldn't load extensions");
return GNUNET_SYSERR;
}
if (GNUNET_OK != if (GNUNET_OK !=
GNUNET_CONFIGURATION_get_value_string (cfg, GNUNET_CONFIGURATION_get_value_string (cfg,
@ -396,6 +401,7 @@ TALER_TESTING_prepare_exchange (const char *config_filename,
TALER_TESTING_auditor_db_reset (config_filename)) TALER_TESTING_auditor_db_reset (config_filename))
return GNUNET_NO; return GNUNET_NO;
} }
if (GNUNET_OK != if (GNUNET_OK !=
GNUNET_CONFIGURATION_parse_and_run (config_filename, GNUNET_CONFIGURATION_parse_and_run (config_filename,
&sign_keys_for_exchange, &sign_keys_for_exchange,
@ -709,6 +715,10 @@ TALER_TESTING_setup_with_exchange_cfg (
char *base_url; char *base_url;
int result; int result;
/* Load extensions */
if (GNUNET_OK != TALER_extensions_load (cfg))
return GNUNET_SYSERR;
if (GNUNET_OK != if (GNUNET_OK !=
GNUNET_CONFIGURATION_get_value_string (cfg, GNUNET_CONFIGURATION_get_value_string (cfg,
"exchange", "exchange",

View File

@ -105,6 +105,7 @@ libtalerutil_la_SOURCES = \
libtalerutil_la_LIBADD = \ libtalerutil_la_LIBADD = \
-lgnunetutil \ -lgnunetutil \
-lgnunetjson \
-lsodium \ -lsodium \
-ljansson \ -ljansson \
$(LIBGCRYPT_LIBS) \ $(LIBGCRYPT_LIBS) \

View File

@ -21,6 +21,7 @@
#include "platform.h" #include "platform.h"
#include "taler_util.h" #include "taler_util.h"
#include "taler_signatures.h" #include "taler_signatures.h"
#include <gnunet/gnunet_json_lib.h>
#include <gcrypt.h> #include <gcrypt.h>
void void
@ -436,3 +437,113 @@ TALER_age_commitment_proof_free (
cp->commitment.keys = NULL; cp->commitment.keys = NULL;
} }
} }
enum GNUNET_GenericReturnValue
TALER_JSON_parse_age_groups (const json_t *root,
struct TALER_AgeMask *mask)
{
enum GNUNET_GenericReturnValue ret;
const char *str;
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_string ("age_groups",
&str),
GNUNET_JSON_spec_end ()
};
ret = GNUNET_JSON_parse (root,
spec,
NULL,
NULL);
if (GNUNET_OK == ret)
TALER_parse_age_group_string (str, mask);
GNUNET_JSON_parse_free (spec);
return ret;
}
enum GNUNET_GenericReturnValue
TALER_parse_age_group_string (
const char *groups,
struct TALER_AgeMask *mask)
{
const char *pos = groups;
unsigned int prev = 0;
unsigned int val = 0;
char c;
while (*pos)
{
c = *pos++;
if (':' == c)
{
if (prev >= val)
return GNUNET_SYSERR;
mask->bits |= 1 << val;
prev = val;
val = 0;
continue;
}
if ('0'>c || '9'<c)
return GNUNET_SYSERR;
val = 10 * val + c - '0';
if (0>=val || 32<=val)
return GNUNET_SYSERR;
}
if (32<=val || prev>=val)
return GNUNET_SYSERR;
mask->bits |= (1 << val);
mask->bits |= 1; // mark zeroth group, too
return GNUNET_OK;
}
char *
TALER_age_mask_to_string (
const struct TALER_AgeMask *mask)
{
uint32_t bits = mask->bits;
unsigned int n = 0;
char *buf = GNUNET_malloc (32 * 3); // max characters possible
char *pos = buf;
if (NULL == buf)
{
return buf;
}
while (bits != 0)
{
bits >>= 1;
n++;
if (0 == (bits & 1))
{
continue;
}
if (n > 9)
{
*(pos++) = '0' + n / 10;
}
*(pos++) = '0' + n % 10;
if (0 != (bits >> 1))
{
*(pos++) = ':';
}
}
return buf;
}
/* end util/age_restriction.c */