aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am2
-rwxr-xr-xsrc/auditor/test-auditor.sh4
-rwxr-xr-xsrc/auditor/test-revocation.sh2
-rwxr-xr-xsrc/auditor/test-sync.sh2
-rw-r--r--src/exchange-tools/taler-exchange-offline.c100
-rw-r--r--src/exchange/taler-exchange-httpd.c111
-rw-r--r--src/exchange/taler-exchange-httpd.h9
-rw-r--r--src/exchange/taler-exchange-httpd_extensions.c64
-rw-r--r--src/exchange/taler-exchange-httpd_keys.c83
-rw-r--r--src/exchange/taler-exchange-httpd_management_extensions.c3
-rw-r--r--src/exchange/taler-exchange-httpd_refreshes_reveal.c9
-rw-r--r--src/extensions/Makefile.am5
-rw-r--r--src/extensions/age_restriction/Makefile.am33
-rw-r--r--src/extensions/age_restriction/extension_age_restriction.c (renamed from src/extensions/extension_age_restriction.c)345
-rw-r--r--src/extensions/age_restriction_helper.c74
-rw-r--r--src/extensions/extensions.c125
-rw-r--r--src/extensions/policy_auction/Makefile.am34
-rw-r--r--src/extensions/policy_auction/policy_auction.c731
-rw-r--r--src/include/taler_extensions.h169
-rw-r--r--src/include/taler_util.h59
-rw-r--r--src/lib/exchange_api_handle.c16
-rw-r--r--src/testing/test_exchange_api.c3
-rw-r--r--src/testing/test_exchange_auction.conf160
-rw-r--r--src/testing/test_exchange_p2p.c3
-rw-r--r--src/testing/testing_api_cmd_batch_withdraw.c2
-rw-r--r--src/testing/testing_api_cmd_withdraw.c2
-rw-r--r--src/testing/testing_api_helpers_exchange.c14
-rw-r--r--src/util/Makefile.am1
-rw-r--r--src/util/age_restriction.c111
29 files changed, 1763 insertions, 513 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 9fa748d7..9f064665 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -32,5 +32,7 @@ SUBDIRS = \
auditor \
lib \
exchange-tools \
+ extensions/policy_auction \
+ extensions/age_restriction \
testing \
benchmark
diff --git a/src/auditor/test-auditor.sh b/src/auditor/test-auditor.sh
index c9895b6f..859b9944 100755
--- a/src/auditor/test-auditor.sh
+++ b/src/auditor/test-auditor.sh
@@ -102,7 +102,7 @@ function cleanup()
function exit_cleanup()
{
echo "Running exit-cleanup"
- if test ! -z ${POSTGRES_PATH:-}
+ if ! [[ -z ${POSTGRES_PATH:-} ]]
then
echo "Stopping Postgres at ${POSTGRES_PATH}"
${POSTGRES_PATH}/pg_ctl -D $TMPDIR -l /dev/null stop &> /dev/null || true
@@ -2050,7 +2050,7 @@ INITDB_BIN=$(command -v initdb) || true
if [[ ! -z $INITDB_BIN ]]; then
echo " FOUND (in path) at" $INITDB_BIN
else
- HAVE_INITDB=`find /usr -name "initdb" 2> /dev/null | grep postgres` || exit_skip " MISSING"
+ HAVE_INITDB=`find /usr -name "initdb" 2> /dev/null | grep postgres | head -1` || exit_skip " MISSING"
echo " FOUND at" `dirname $HAVE_INITDB`
INITDB_BIN=`echo $HAVE_INITDB | grep bin/initdb | grep postgres | sort -n | tail -n1`
fi
diff --git a/src/auditor/test-revocation.sh b/src/auditor/test-revocation.sh
index e21b9487..c25f15e8 100755
--- a/src/auditor/test-revocation.sh
+++ b/src/auditor/test-revocation.sh
@@ -96,7 +96,7 @@ function cleanup()
function exit_cleanup()
{
echo "Running exit-cleanup"
- if test ! -z ${POSTGRES_PATH:-}
+ if [[ ! -z ${POSTGRES_PATH:-} ]]
then
echo "Stopping Postgres at ${POSTGRES_PATH}"
${POSTGRES_PATH}/pg_ctl -D $TMPDIR -l /dev/null stop &> /dev/null || true
diff --git a/src/auditor/test-sync.sh b/src/auditor/test-sync.sh
index 174e03c6..10a2e3f7 100755
--- a/src/auditor/test-sync.sh
+++ b/src/auditor/test-sync.sh
@@ -32,7 +32,7 @@ function exit_fail() {
# Cleanup to run whenever we exit
function cleanup() {
- if test ! -z ${POSTGRES_PATH:-}
+ if ! [[ -z ${POSTGRES_PATH:-} ]]
then
${POSTGRES_PATH}/pg_ctl -D $TMPDIR stop &> /dev/null || true
fi
diff --git a/src/exchange-tools/taler-exchange-offline.c b/src/exchange-tools/taler-exchange-offline.c
index 60a39df9..93cf05bd 100644
--- a/src/exchange-tools/taler-exchange-offline.c
+++ b/src/exchange-tools/taler-exchange-offline.c
@@ -138,6 +138,11 @@ static struct GNUNET_CURL_RescheduleContext *rc;
static const struct GNUNET_CONFIGURATION_Handle *kcfg;
/**
+ * Age restriction configuration
+ */
+static struct TALER_AgeRestrictionConfig ar_config = {0};
+
+/**
* Return value from main().
*/
static int global_ret;
@@ -164,11 +169,6 @@ static char *currency;
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.
*/
struct SubCommand
@@ -2386,6 +2386,7 @@ do_upload (char *const *args)
return;
}
trigger_upload (CFG_exchange_url);
+
json_decref (out);
out = NULL;
}
@@ -3888,7 +3889,7 @@ load_age_mask (const char*section_name)
static const struct TALER_AgeMask null_mask = {0};
enum GNUNET_GenericReturnValue ret;
- if (age_mask.bits == 0)
+ if (! ar_config.enabled)
return null_mask;
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,
section_name,
"AGE_RESTRICTED");
- if (GNUNET_YES == ret)
- return age_mask;
-
if (GNUNET_SYSERR == ret)
GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
section_name,
"AGE_RESTRICTED",
"Value must be YES or NO\n");
+ if (GNUNET_YES == ret)
+ return ar_config.mask;
+
return null_mask;
}
@@ -4248,18 +4249,24 @@ do_setup (char *const *args)
static void
do_extensions_show (char *const *args)
{
- const struct TALER_Extension *it;
+ const struct TALER_Extensions *it;
json_t *exts = json_object ();
json_t *obj;
GNUNET_assert (NULL != exts);
for (it = TALER_extensions_get_head ();
- NULL != it;
+ NULL != it && NULL != it->extension;
it = it->next)
- GNUNET_assert (0 ==
- json_object_set_new (exts,
- it->name,
- it->config_to_json (it)));
+ {
+ const struct TALER_Extension *extension = it->extension;
+ int ret;
+
+ ret = json_object_set_new (exts,
+ extension->name,
+ extension->config_to_json (extension));
+ GNUNET_assert (-1 != ret);
+ }
+
obj = GNUNET_JSON_PACK (
GNUNET_JSON_pack_object_steal ("extensions",
exts));
@@ -4268,7 +4275,7 @@ do_extensions_show (char *const *args)
json_dumps (obj,
JSON_INDENT (2)));
json_decref (obj);
- next (args + 1);
+ next (args);
}
@@ -4281,25 +4288,29 @@ do_extensions_sign (char *const *args)
json_t *extensions = json_object ();
struct TALER_ExtensionConfigHashP h_config;
struct TALER_MasterSignatureP sig;
- const struct TALER_Extension *it;
+ const struct TALER_Extensions *it;
+ bool found = false;
json_t *obj;
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 ();
NULL != it;
it = it->next)
+ {
+ const struct TALER_Extension *ext = it->extension;
+ GNUNET_assert (ext);
+
+ found = true;
+
GNUNET_assert (0 ==
json_object_set_new (extensions,
- it->name,
- it->config_to_json (it)));
+ ext->name,
+ ext->config_to_json (
+ ext)));
+ }
+
+ if (! found)
+ return;
if (GNUNET_OK !=
TALER_JSON_extensions_config_hash (extensions,
@@ -4327,9 +4338,10 @@ do_extensions_sign (char *const *args)
GNUNET_JSON_pack_data_auto (
"extensions_sig",
&sig));
+
output_operation (OP_EXTENSIONS,
obj);
- next (args + 1);
+ next (args);
}
@@ -4396,6 +4408,7 @@ do_work_extensions (char *const *args)
}
};
+
if (NULL == args[0])
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
@@ -4530,6 +4543,24 @@ run (void *cls,
(void) cls;
(void) cfgfile;
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 !=
TALER_config_get_currency (kcfg,
&currency))
@@ -4538,18 +4569,6 @@ run (void *cls,
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,
&rc);
rc = GNUNET_CURL_gnunet_rc_create (ctx);
@@ -4579,7 +4598,6 @@ main (int argc,
GNUNET_GETOPT_OPTION_END
};
enum GNUNET_GenericReturnValue ret;
-
/* force linker to link against libtalerutil; if we do
not do this, the linker may "optimize" libtalerutil
away and skip #TALER_OS_init(), which we do need */
diff --git a/src/exchange/taler-exchange-httpd.c b/src/exchange/taler-exchange-httpd.c
index 9349a5a2..39f383af 100644
--- a/src/exchange/taler-exchange-httpd.c
+++ b/src/exchange/taler-exchange-httpd.c
@@ -98,6 +98,13 @@ static int allow_address_reuse;
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.
*/
static struct MHD_Daemon *mhd;
@@ -139,11 +146,6 @@ char *TEH_currency;
char *TEH_base_url;
/**
- * Age restriction flags and mask
- */
-bool TEH_age_restriction_enabled = true;
-
-/**
* Default timeout in seconds for HTTP requests.
*/
static unsigned int connection_timeout = 30;
@@ -170,6 +172,7 @@ bool TEH_suicide;
* TALER_SIGNATURE_MASTER_EXTENSION.
*/
struct TALER_MasterSignatureP TEH_extensions_sig;
+bool TEH_extensions_signed = false;
/**
* Value to return from main()
@@ -1039,6 +1042,89 @@ handle_post_auditors (struct TEH_RequestContext *rc,
/**
+ * Handle GET "/extensions/..." requests.
+ *
+ * @param rc request context
+ * @param args array of additional options
+ * @return MHD result code
+ */
+static MHD_RESULT
+handle_get_extensions (struct TEH_RequestContext *rc,
+ 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_get_handler)
+ return TALER_MHD_reply_with_error (rc->connection,
+ MHD_HTTP_NOT_IMPLEMENTED,
+ TALER_EC_EXCHANGE_GENERIC_OPERATION_UNKNOWN,
+ "GET /extensions/$EXTENSION not supported");
+
+ return ext->http_get_handler (
+ rc->connection,
+ &args[1]);
+}
+
+
+/**
+ * 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 TALER_MHD_reply_with_error (rc->connection,
+ MHD_HTTP_NOT_IMPLEMENTED,
+ TALER_EC_EXCHANGE_GENERIC_OPERATION_UNKNOWN,
+ "POST /extensions/$EXTENSION not supported");
+
+ return ext->http_post_handler (
+ rc->connection,
+ root,
+ &args[1]);
+}
+
+
+/**
* Handle incoming HTTP request.
*
* @param cls closure for MHD daemon (unused)
@@ -1255,6 +1341,21 @@ handle_mhd_request (void *cls,
.nargs = 4,
.nargs_is_upper_bound = true
},
+ /* extensions endpoints */
+ {
+ .url = "extensions",
+ .method = MHD_HTTP_METHOD_GET,
+ .handler.get = &handle_get_extensions,
+ .nargs = 4, /* Arbitrary upper bound */
+ .nargs_is_upper_bound = true,
+ },
+ {
+ .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 */
{
.url = NULL
diff --git a/src/exchange/taler-exchange-httpd.h b/src/exchange/taler-exchange-httpd.h
index 0fda5ed8..77aab249 100644
--- a/src/exchange/taler-exchange-httpd.h
+++ b/src/exchange/taler-exchange-httpd.h
@@ -197,11 +197,6 @@ extern struct TALER_EXCHANGEDB_Plugin *TEH_plugin;
*/
extern char *TEH_currency;
-/*
- * Age restriction extension state
- */
-extern bool TEH_age_restriction_enabled;
-
/**
* 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
*/
extern struct TALER_MasterSignatureP TEH_extensions_sig;
+extern bool TEH_extensions_signed;
/**
* @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
diff --git a/src/exchange/taler-exchange-httpd_extensions.c b/src/exchange/taler-exchange-httpd_extensions.c
index d6c26f6f..2aee1b5c 100644
--- a/src/exchange/taler-exchange-httpd_extensions.c
+++ b/src/exchange/taler-exchange-httpd_extensions.c
@@ -78,6 +78,7 @@ extension_update_event_cb (void *cls,
}
// Get the config from the database as string
+ if (extension->has_config)
{
char *config_str = NULL;
enum GNUNET_DB_QueryStatus qs;
@@ -100,6 +101,9 @@ extension_update_event_cb (void *cls,
// No config found -> disable extension
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);
return;
}
@@ -114,28 +118,40 @@ extension_update_event_cb (void *cls,
err.text,
err.source);
GNUNET_break (0);
+ free (config_str);
return;
}
// Call the parser for the extension
ret = extension->load_json_config (
(struct TALER_Extension *) extension,
- config);
+ json_object_get (config, "config"));
if (GNUNET_OK != ret)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Couldn't parse configuration for extension %s from the database",
- extension->name);
+ "Couldn't parse configuration for extension %s from the database: %s\n",
+ extension->name,
+ config_str);
GNUNET_break (0);
}
+
+ free (config_str);
+ json_decref (config);
}
/* Special case age restriction: Update global flag and mask */
if (TALER_Extension_AgeRestriction == type)
{
- TEH_age_restriction_enabled =
- TALER_extensions_age_restriction_is_enabled ();
+ const struct TALER_AgeRestrictionConfig *conf =
+ 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 +159,21 @@ extension_update_event_cb (void *cls,
enum GNUNET_GenericReturnValue
TEH_extensions_init ()
{
- GNUNET_assert (GNUNET_OK ==
- TALER_extension_age_restriction_register ());
-
/* Set the event handler for updates */
struct GNUNET_DB_EventHeaderP ev = {
.size = htons (sizeof (ev)),
.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,
GNUNET_TIME_UNIT_FOREVER_REL,
&ev,
@@ -162,17 +185,24 @@ TEH_extensions_init ()
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 */
- for (const struct TALER_Extension *it = TALER_extensions_get_head ();
- NULL != it->next;
+ for (const struct TALER_Extensions *it = TALER_extensions_get_head ();
+ NULL != it && NULL != it->extension;
it = it->next)
- extension_update_event_cb (NULL, &it->type, sizeof(it->type));
+ {
+ const struct TALER_Extension *ext = it->extension;
+ uint32_t typ = htonl (ext->type);
+ 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,
+ &typ,
+ sizeof(typ));
+ free (conf);
+ }
return GNUNET_OK;
}
diff --git a/src/exchange/taler-exchange-httpd_keys.c b/src/exchange/taler-exchange-httpd_keys.c
index a6ad9976..4761f314 100644
--- a/src/exchange/taler-exchange-httpd_keys.c
+++ b/src/exchange/taler-exchange-httpd_keys.c
@@ -815,10 +815,7 @@ static struct TALER_AgeMask
load_age_mask (const char*section_name)
{
static const struct TALER_AgeMask null_mask = {0};
- struct TALER_AgeMask age_mask = TALER_extensions_age_restriction_ageMask ();
-
- if (age_mask.bits == 0)
- return null_mask;
+ enum GNUNET_GenericReturnValue ret;
if (GNUNET_OK != (GNUNET_CONFIGURATION_have_value (
TEH_cfg,
@@ -826,22 +823,29 @@ load_age_mask (const char*section_name)
"AGE_RESTRICTED")))
return null_mask;
+ if (GNUNET_SYSERR ==
+ (ret = GNUNET_CONFIGURATION_get_value_yesno (TEH_cfg,
+ section_name,
+ "AGE_RESTRICTED")))
{
- enum GNUNET_GenericReturnValue ret;
+ GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
+ section_name,
+ "AGE_RESTRICTED",
+ "Value must be YES or NO\n");
+ return null_mask;
+ }
- if (GNUNET_SYSERR ==
- (ret = GNUNET_CONFIGURATION_get_value_yesno (TEH_cfg,
- section_name,
- "AGE_RESTRICTED")))
- {
- GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
- section_name,
- "AGE_RESTRICTED",
- "Value must be YES or NO\n");
- 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,37 +1902,37 @@ create_krd (struct TEH_KeyStateHandle *ksh,
bool has_extensions = false;
/* Fill in the configurations of the enabled extensions */
- for (const struct TALER_Extension *extension = TALER_extensions_get_head ();
- NULL != extension;
- extension = extension->next)
+ for (const struct TALER_Extensions *iter = TALER_extensions_get_head ();
+ NULL != iter && NULL != iter->extension;
+ iter = iter->next)
{
+ const struct TALER_Extension *extension = iter->extension;
json_t *ext;
- json_t *config_json;
int r;
- /* skip if not configured == disabled */
- if (NULL == extension->config ||
- NULL == extension->config_json)
+ /* skip if not enabled */
+ if (! extension->enabled)
continue;
/* flag our findings so far */
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)
+ extension->version)
);
GNUNET_assert (NULL != ext);
+ if (extension->has_config)
+ {
+ GNUNET_assert (extension->config_json);
+ json_object_set_new (ext,
+ "config",
+ extension->config_json);
+ }
+
r = json_object_set_new (
extensions,
extension->name,
@@ -1948,12 +1952,16 @@ create_krd (struct TEH_KeyStateHandle *ksh,
extensions);
GNUNET_assert (0 == r);
- sig = GNUNET_JSON_PACK (
- GNUNET_JSON_pack_data_auto ("extensions_sig",
- &TEH_extensions_sig));
+ /* Add the signature of the extensions, if it is not zero */
+ if (TEH_extensions_signed)
+ {
+ sig = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_data_auto ("extensions_sig",
+ &TEH_extensions_sig));
- r = json_object_update (keys, sig);
- GNUNET_assert (0 == r);
+ r = json_object_update (keys, sig);
+ GNUNET_assert (0 == r);
+ }
}
else
{
@@ -2560,6 +2568,7 @@ build_key_state (struct HelperState *hs,
true);
return NULL;
}
+
/* NOTE: ONLY fetches non-revoked AND master-signed signkeys! */
qs = TEH_plugin->iterate_active_signkeys (TEH_plugin->cls,
&signkey_info_cb,
diff --git a/src/exchange/taler-exchange-httpd_management_extensions.c b/src/exchange/taler-exchange-httpd_management_extensions.c
index a663b1b0..c70ad25e 100644
--- a/src/exchange/taler-exchange-httpd_management_extensions.c
+++ b/src/exchange/taler-exchange-httpd_management_extensions.c
@@ -108,6 +108,8 @@ set_extensions (void *cls,
taler_ext->name,
config);
+ free (config);
+
if (qs < 0)
{
if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
@@ -137,6 +139,7 @@ set_extensions (void *cls,
/* All extensions configured, update the signature */
TEH_extensions_sig = sec->extensions_sig;
+ TEH_extensions_signed = true;
return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; /* only 'success', so >=0, matters here */
}
diff --git a/src/exchange/taler-exchange-httpd_refreshes_reveal.c b/src/exchange/taler-exchange-httpd_refreshes_reveal.c
index a25d6ff4..09ab572b 100644
--- a/src/exchange/taler-exchange-httpd_refreshes_reveal.c
+++ b/src/exchange/taler-exchange-httpd_refreshes_reveal.c
@@ -603,7 +603,7 @@ resolve_refreshes_reveal_denominations (
&rrc->coin_envelope_hash);
}
- if (TEH_age_restriction_enabled &&
+ if (TEH_age_restriction_config.enabled &&
((NULL == old_age_commitment_json) !=
TALER_AgeCommitmentHash_isNullOrZero (
&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
* from the melt request */
- if (TEH_age_restriction_enabled &&
+ if (TEH_age_restriction_config.enabled &&
(NULL != old_age_commitment_json))
{
enum GNUNET_GenericReturnValue res;
@@ -623,8 +623,7 @@ resolve_refreshes_reveal_denominations (
bool failed = true;
/* Has been checked in handle_refreshes_reveal_json() */
- GNUNET_assert (ng ==
- TALER_extensions_age_restriction_num_groups ());
+ GNUNET_assert (ng == TEH_age_restriction_config.num_groups);
rctx->old_age_commitment = GNUNET_new (struct TALER_AgeCommitment);
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
* of the size the # of age groups */
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))
{
GNUNET_break_op (0);
diff --git a/src/extensions/Makefile.am b/src/extensions/Makefile.am
index 5d4ed128..c867a951 100644
--- a/src/extensions/Makefile.am
+++ b/src/extensions/Makefile.am
@@ -11,7 +11,7 @@ if USE_COVERAGE
endif
-# Libraries
+# Basic extension handling library
lib_LTLIBRARIES = \
libtalerextensions.la
@@ -22,7 +22,7 @@ libtalerextensions_la_LDFLAGS = \
libtalerextensions_la_SOURCES = \
extensions.c \
- extension_age_restriction.c
+ age_restriction_helper.c
libtalerextensions_la_LIBADD = \
$(top_builddir)/src/json/libtalerjson.la \
@@ -31,3 +31,4 @@ libtalerextensions_la_LIBADD = \
-lgnunetutil \
-ljansson \
$(XLIB)
+
diff --git a/src/extensions/age_restriction/Makefile.am b/src/extensions/age_restriction/Makefile.am
new file mode 100644
index 00000000..e90c1962
--- /dev/null
+++ b/src/extensions/age_restriction/Makefile.am
@@ -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_age_restriction_la_LIBADD = \
+ $(top_builddir)/src/json/libtalerjson.la \
+ $(top_builddir)/src/util/libtalerutil.la \
+ -lgnunetjson \
+ -lgnunetutil \
+ -ljansson \
+ $(XLIB)
diff --git a/src/extensions/extension_age_restriction.c b/src/extensions/age_restriction/extension_age_restriction.c
index 00a03841..697d066f 100644
--- a/src/extensions/extension_age_restriction.c
+++ b/src/extensions/age_restriction/extension_age_restriction.c
@@ -23,102 +23,6 @@
#include "taler_extensions.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
@@ -127,6 +31,12 @@ TALER_age_mask_to_string (
*/
/**
+ * @brief local configuration
+ */
+
+static struct TALER_AgeRestrictionConfig AR_config = {0};
+
+/**
* @brief implements the TALER_Extension.disable interface.
*
* @param ext Pointer to the current extension
@@ -138,6 +48,7 @@ age_restriction_disable (
if (NULL == ext)
return;
+ ext->enabled = false;
ext->config = NULL;
if (NULL != ext->config_json)
@@ -146,86 +57,9 @@ age_restriction_disable (
ext->config_json = NULL;
}
- TE_age_restriction_config.mask.bits = 0;
- TE_age_restriction_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;
+ AR_config.enabled = false;
+ AR_config.mask.bits = 0;
+ AR_config.num_groups = 0;
}
@@ -254,24 +88,25 @@ age_restriction_load_json_config (
if (TALER_Extension_AgeRestriction != ext->type)
return GNUNET_SYSERR;
- TE_age_restriction_config.mask.bits = mask.bits;
- TE_age_restriction_config.num_groups = 0;
-
if (mask.bits > 0)
{
/* if the mask is not zero, the first bit MUST be set */
if (0 == (mask.bits & 1))
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)
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,
"loaded new age restriction config with age groups: %s\n",
@@ -296,6 +131,13 @@ age_restriction_config_to_json (
GNUNET_assert (NULL != ext);
+ if (! ext->enabled)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "age restriction not enabled");
+ return json_null ();
+ }
+
if (NULL == ext->config)
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
@@ -308,11 +150,13 @@ age_restriction_config_to_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 (
GNUNET_JSON_pack_string ("age_groups", mask_str)
);
+ free (mask_str);
+
return GNUNET_JSON_PACK (
GNUNET_JSON_pack_bool ("critical", ext->critical),
GNUNET_JSON_pack_string ("version", ext->version),
@@ -338,71 +182,120 @@ age_restriction_test_json_config (
/* The extension for age restriction */
-struct TALER_Extension TE_age_restriction = {
- .next = NULL,
+struct TALER_Extension TE_extension_age_restriction = {
.type = TALER_Extension_AgeRestriction,
.name = "age_restriction",
.critical = false,
.version = "1",
- .config = NULL, // disabled per default
+ .enabled = false, /* disabled per default */
+ .has_config = true, /* we need to store configuration */
+ .config = NULL,
.config_json = NULL,
.disable = &age_restriction_disable,
.test_json_config = &age_restriction_test_json_config,
.load_json_config = &age_restriction_load_json_config,
.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);
-bool
-TALER_extensions_age_restriction_is_configured ()
-{
- return (0 != TE_age_restriction_config.mask.bits);
-}
+ 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;
+ }
-struct TALER_AgeMask
-TALER_extensions_age_restriction_ageMask ()
-{
- return TE_age_restriction_config.mask;
-}
+ 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;
+ }
-size_t
-TALER_extensions_age_restriction_num_groups ()
-{
- return TE_age_restriction_config.num_groups;
+ 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;
}
-enum GNUNET_GenericReturnValue
-TALER_JSON_parse_age_groups (const json_t *root,
- struct TALER_AgeMask *mask)
+/**
+ * @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)
{
- 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;
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "[age restriction] disabling and unloading");
+ AR_config.enabled = 0;
+ AR_config.mask.bits = 0;
+ AR_config.num_groups = 0;
+ return NULL;
}
diff --git a/src/extensions/age_restriction_helper.c b/src/extensions/age_restriction_helper.c
new file mode 100644
index 00000000..2cd77515
--- /dev/null
+++ b/src/extensions/age_restriction_helper.c
@@ -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 */
diff --git a/src/extensions/extensions.c b/src/extensions/extensions.c
index 0df0bae3..95fb8cf0 100644
--- a/src/extensions/extensions.c
+++ b/src/extensions/extensions.c
@@ -24,21 +24,22 @@
#include "taler_extensions.h"
#include "stdint.h"
-
/* 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_Extension *
+const struct TALER_Extensions *
TALER_extensions_get_head ()
{
- return TE_extensions;
+ return &TE_extensions;
}
-enum GNUNET_GenericReturnValue
-TALER_extensions_add (
- struct TALER_Extension *extension)
+static enum GNUNET_GenericReturnValue
+add_extension (
+ const struct TALER_Extension *extension)
{
/* Sanity checks */
if ((NULL == extension) ||
@@ -47,28 +48,30 @@ TALER_extensions_add (
(NULL == extension->disable) ||
(NULL == extension->test_json_config) ||
(NULL == extension->load_json_config) ||
- (NULL == extension->config_to_json) ||
- (NULL == extension->load_taler_config))
+ (NULL == extension->config_to_json))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"invalid extension\n");
return GNUNET_SYSERR;
}
- if (NULL == TE_extensions) /* first extension ?*/
- TE_extensions = (struct TALER_Extension *) extension;
+ if (NULL == TE_extensions.extension) /* first extension ?*/
+ TE_extensions.extension = extension;
else
{
- struct TALER_Extension *iter;
- struct TALER_Extension *last;
+ struct TALER_Extensions *iter;
+ struct TALER_Extensions *last;
/* 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;
- if (extension->type == iter->type ||
+ if (extension->type == ext->type ||
0 == strcasecmp (extension->name,
- iter->name))
+ ext->name))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"extension collision for `%s'\n",
@@ -78,7 +81,11 @@ TALER_extensions_add (
}
/* 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;
@@ -89,12 +96,12 @@ const struct TALER_Extension *
TALER_extensions_get_by_type (
enum TALER_Extension_Type type)
{
- for (const struct TALER_Extension *it = TE_extensions;
- NULL != it;
+ for (const struct TALER_Extensions *it = &TE_extensions;
+ NULL != it && NULL != it->extension;
it = it->next)
{
- if (it->type == type)
- return it;
+ if (it->extension->type == type)
+ return it->extension;
}
/* No extension found. */
@@ -109,8 +116,7 @@ TALER_extensions_is_enabled_type (
const struct TALER_Extension *ext =
TALER_extensions_get_by_type (type);
- return (NULL != ext &&
- TALER_extensions_is_enabled (ext));
+ return (NULL != ext && ext->enabled);
}
@@ -118,14 +124,15 @@ const struct TALER_Extension *
TALER_extensions_get_by_name (
const char *name)
{
- for (const struct TALER_Extension *it = TE_extensions;
+ for (const struct TALER_Extensions *it = &TE_extensions;
NULL != it;
it = it->next)
{
- if (0 == strcasecmp (name, it->name))
- return it;
+ if (0 == strcasecmp (name, it->extension->name))
+ return it->extension;
}
- /* No extension found. */
+ /* No extension found, try to load it. */
+
return NULL;
}
@@ -178,7 +185,8 @@ configure_extension (
{
struct LoadConfClosure *col = cls;
const char *name;
- const struct TALER_Extension *extension;
+ char *lib_name;
+ struct TALER_Extension *extension;
if (GNUNET_OK != col->error)
return;
@@ -190,33 +198,49 @@ configure_extension (
name = section + sizeof(TALER_EXTENSION_SECTION_PREFIX) - 1;
- if (NULL ==
- (extension = TALER_extensions_get_by_name (name)))
+
+ /* Load the extension library */
+ GNUNET_asprintf (&lib_name,
+ "libtaler_extension_%s",
+ name);
+ extension = GNUNET_PLUGIN_load (
+ lib_name,
+ (void *) col->cfg);
+ if (NULL == extension)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Unsupported extension `%s` (section [%s]).\n", name,
+ "Couldn't load extension library to `%s` (section [%s]).\n",
+ name,
section);
col->error = GNUNET_SYSERR;
return;
}
- if (GNUNET_OK !=
- extension->load_taler_config (
- (struct TALER_Extension *) extension,
- col->cfg))
+
+ if (GNUNET_OK != add_extension (extension))
{
+ /* TODO: Ignoring return values here */
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Couldn't parse configuration for extension `%s` (section [%s]).\n",
+ "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
-TALER_extensions_load_taler_config (
+TALER_extensions_load (
const struct GNUNET_CONFIGURATION_Handle *cfg)
{
struct LoadConfClosure col = {
@@ -224,9 +248,16 @@ TALER_extensions_load_taler_config (
.error = GNUNET_OK,
};
+ if (extensions_loaded)
+ return GNUNET_OK;
+
GNUNET_CONFIGURATION_iterate_sections (cfg,
&configure_extension,
&col);
+
+ if (GNUNET_OK == col.error)
+ extensions_loaded = true;
+
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 */
- for (const struct TALER_Extension *it = TALER_extensions_get_head ();
+ for (const struct TALER_Extensions *it = TALER_extensions_get_head ();
NULL != it;
it = it->next)
{
- if (NULL == json_object_get (extensions, it->name))
- it->disable ((struct TALER_Extension *) it);
+ if (NULL == json_object_get (extensions, it->extension->name))
+ it->extension->disable ((struct TALER_Extension *) it);
}
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 */
diff --git a/src/extensions/policy_auction/Makefile.am b/src/extensions/policy_auction/Makefile.am
new file mode 100644
index 00000000..cf44b95e
--- /dev/null
+++ b/src/extensions/policy_auction/Makefile.am
@@ -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 an extension library
+
+plugindir = $(libdir)/taler
+
+plugin_LTLIBRARIES = \
+ libtaler_extension_policy_auction.la
+
+libtaler_extension_policy_auction_la_LDFLAGS = \
+ -version-info 0:0:0 \
+ -no-undefined
+
+libtaler_extension_policy_auction_la_SOURCES = \
+ policy_auction.c
+
+libtaler_extension_policy_auction_la_LIBADD = \
+ $(top_builddir)/src/json/libtalerjson.la \
+ $(top_builddir)/src/util/libtalerutil.la \
+ -lgnunetjson \
+ -lgnunetutil \
+ -ljansson \
+ $(XLIB)
diff --git a/src/extensions/policy_auction/policy_auction.c b/src/extensions/policy_auction/policy_auction.c
new file mode 100644
index 00000000..d1c3237c
--- /dev/null
+++ b/src/extensions/policy_auction/policy_auction.c
@@ -0,0 +1,731 @@
+/*
+ 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 policy_auction.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 POLICY_AUCTION "policy_auction"
+#define LOG_PREFIX "[policy_auction] "
+#define MAX_RESULT_SIZE 10 * 1024
+
+/* Path to the replay program. */
+static char *replay_program;
+
+/* supported currency */
+static char *currency;
+
+/* This is basically BRANDT_Result with an extra string field */
+struct result
+{
+ uint16_t bidder;
+ uint16_t price_idx;
+ const char *price;
+};
+
+/*
+ * @brief Transcript information
+ *
+ */
+struct transcript
+{
+ /*
+ * The first couple of fields are from a JSON transcript
+ */
+
+ /* Public key of seller */
+ struct GNUNET_CRYPTO_EddsaPublicKey seller_pub;
+
+ /* Payto URL */
+ const char *payto;
+
+ /* Number of bidders + 1 (for seller) */
+ uint16_t n;
+
+ /* (n-1) public keys of bidders */
+ struct GNUNET_CRYPTO_EddsaPublicKey *bidder_pub;
+
+ /* Type of auction, see libbrandt */
+ uint16_t m;
+
+ /* Auction public outcome? */
+ bool public;
+
+ /* Start date of the auction */
+ struct GNUNET_TIME_Timestamp time_start;
+
+ /* End date of the auction */
+ struct GNUNET_TIME_Relative time_round;
+
+ /* Number of prices */
+ uint16_t k;
+
+ /* Prices, must be length k */
+ struct TALER_Amount *prices;
+
+ /* Expected winner(s), maybe NULL */
+ struct result *expected;
+ size_t expected_len;
+
+ /*
+ * These are the results from the replay via the external program.
+ */
+ struct result *results;
+ size_t results_len;
+};
+
+/**
+ * @brief returns an JSON with the error
+ */
+static enum GNUNET_GenericReturnValue
+json_error (json_t **output,
+ char *error, ...)
+{
+ va_list ap;
+ int n = 0;
+ char buf[4096];
+ GNUNET_assert (error);
+
+ va_start (ap, error);
+ n = vsprintf (buf, error, ap);
+ va_end (ap);
+
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ LOG_PREFIX "got error: %s\n",
+ n < 0 ? error: buf);
+
+ *output = json_pack ("{s:s}",
+ "error",
+ n < 0 ? error : buf);
+ GNUNET_assert (*output);
+
+ return GNUNET_SYSERR;
+};
+
+/**
+ * @brief returns an JSON with the result
+ */
+#if 0
+static enum GNUNET_GenericReturnValue
+json_result (json_t **output,
+ const struct transcript *tr)
+{
+ json_t *results;
+
+ GNUNET_assert (NULL != tr);
+
+ *output = json_object ();
+ results = json_array ();
+ GNUNET_assert (*output);
+ GNUNET_assert (results);
+
+ for (size_t i = 0; i < tr->results_len; i++)
+ {
+ json_t *result = json_pack ("{s:i, s:s}",
+ "bidder", tr->results[i].bidder,
+ "price", tr->results[i].price);
+ GNUNET_assert (result);
+
+ GNUNET_assert (-1 !=
+ json_array_append_new (results, result));
+ }
+
+ GNUNET_assert (-1 !=
+ json_object_set_new (*output,
+ "winners",
+ results));
+
+ return GNUNET_OK;
+}
+
+
+#endif
+
+
+/*
+ * @brief Parses a given json as transcript.
+ *
+ * @param[in] jtr JSON input
+ * @param[out] tr Parsed transcript data
+ * @param[out] jerror JSON output for errors
+ * @return GNUNET_OK on succes
+ *
+ * TODO:
+ * - parse and verify signatures
+ */
+static enum GNUNET_GenericReturnValue
+parse_transcript (const json_t *jtr,
+ struct transcript *tr,
+ json_t **jerror)
+{
+ json_t *auc;
+
+ // TODO: struct GNUNET_CRYPTO_EddsaSignature sig;
+
+ GNUNET_assert (jtr);
+ GNUNET_assert (tr);
+
+ // Parse auction
+ {
+ char *perr;
+ unsigned int eline;
+ struct GNUNET_JSON_Specification au_spec[] = {
+ GNUNET_JSON_spec_bool ("public", &tr->public),
+ GNUNET_JSON_spec_uint16 ("type", &tr->m),
+ GNUNET_JSON_spec_fixed_auto ("pubkey", &tr->seller_pub),
+ GNUNET_JSON_spec_timestamp ("time_start", &tr->time_start),
+ GNUNET_JSON_spec_relative_time ("time_round", &tr->time_round),
+ GNUNET_JSON_spec_string ("payto", &tr->payto),
+ GNUNET_JSON_spec_end ()
+ };
+
+ auc = json_object_get (jtr, "auction");
+ if (NULL == auc)
+ return json_error (jerror,
+ "no auction found in transcript");
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (auc,
+ au_spec,
+ (const char **) &perr,
+ &eline))
+ return json_error (jerror,
+ perr);
+
+ // Prices...
+ {
+ size_t idx;
+ json_t *val;
+ json_t *prices;
+
+ prices = json_object_get (auc, "prices");
+ if (! json_is_array (prices))
+ return json_error (jerror,
+ "no prices found");
+
+ tr->k = json_array_size (prices);
+
+ tr->prices = GNUNET_new_array (tr->k, struct TALER_Amount);
+ json_array_foreach (prices, idx, val)
+ {
+ struct GNUNET_JSON_Specification spec[] = {
+ TALER_JSON_spec_amount (NULL,
+ currency,
+ &(tr->prices[idx])),
+ GNUNET_JSON_spec_end (),
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (val,
+ spec,
+ NULL,
+ NULL))
+ return json_error (jerror,
+ "price no. %ld couldn't be parsed",
+ idx + 1);
+ }
+ }
+ }
+
+ // Bidders
+ {
+ size_t idx;
+ json_t *val;
+ json_t *bidders;
+
+ bidders = json_object_get (jtr, "bidders");
+ if (! bidders || ! json_is_array (bidders))
+ return json_error (jerror,
+ "no bidders found");
+
+ tr->n = json_array_size (bidders);
+
+ tr->bidder_pub = GNUNET_new_array (tr->n, struct
+ GNUNET_CRYPTO_EddsaPublicKey);
+ json_array_foreach (bidders, idx, val)
+ {
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_fixed_auto (NULL,
+ &(tr->bidder_pub[idx])),
+ GNUNET_JSON_spec_end (),
+ };
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (val,
+ spec,
+ NULL,
+ NULL))
+ return json_error (jerror,
+ "bidder no %ld public key couldn't be parsed",
+ idx + 1);
+ }
+ }
+
+ // TODO: parse and verify signatures from bidders of the auction
+
+
+ // Messages
+ {
+ size_t nm;
+ json_t *messages = json_object_get (jtr, "transcript");
+
+ if (! json_is_array (messages))
+ return json_error (jerror,
+ "no messages found");
+
+
+ nm = json_array_size (messages);
+
+ if (nm != (4 * tr->n))
+ return json_error (jerror,
+ "not the right no. of messages found");
+
+ /* TODO: parse and evaluate signatures */
+ }
+
+ // Winners
+ {
+ size_t idx;
+ json_t *val;
+ json_t *winners = json_object_get (jtr, "winners");
+
+ if (! json_is_array (winners))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ LOG_PREFIX "winners not provided, continuing without\n");
+ goto DONE;
+ }
+
+ tr->expected_len = json_array_size (winners);
+ tr->expected = GNUNET_new_array (tr->expected_len,
+ struct result);
+
+ json_array_foreach (winners, idx, val) {
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_uint16 ("bidder",
+ &(tr->expected[idx].bidder)),
+ GNUNET_JSON_spec_uint16 ("price_idx",
+ &(tr->expected[idx].price_idx)),
+ GNUNET_JSON_spec_string ("price",
+ &(tr->expected[idx].price)),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (val,
+ spec,
+ NULL,
+ NULL))
+ return json_error (jerror,
+ "couldn't parse winner no. %ld",
+ idx + 1);
+ }
+ }
+
+ // TODO: parse and evalue sig of seller
+
+// TODO: check for max values
+
+DONE:
+
+ *jerror = NULL;
+ return GNUNET_OK;
+}
+
+
+/**
+ * @brief replay an auction using the external program
+ *
+ * @param[in] root The original JSON transcript
+ * @param[in] transcript The transcript object parsed so far
+ * @param[out] result The JSON result from the program
+ * @return GNUNET_OK on success
+ *
+ * TODO: Make this resumable
+ */
+static enum GNUNET_GenericReturnValue
+replay_transcript (const json_t*root,
+ struct transcript *tr,
+ json_t **result)
+{
+ struct GNUNET_DISK_PipeHandle *pi;
+ struct GNUNET_DISK_PipeHandle *po;
+ const struct GNUNET_DISK_FileHandle *fd;
+ struct GNUNET_OS_Process *proc;
+
+ pi = GNUNET_DISK_pipe (GNUNET_DISK_PF_BLOCKING_WRITE);
+ po = GNUNET_DISK_pipe (GNUNET_DISK_PF_BLOCKING_READ);
+ proc = GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_ERR,
+ pi, po, NULL,
+ replay_program,
+ replay_program,
+ NULL);
+ if (NULL == proc)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ LOG_PREFIX "couldn't create auction replay program '%s'\n",
+ replay_program);
+
+ return json_error (result, "internal error");
+ }
+
+ // Write original transcript JSON to stdin
+ {
+ ssize_t sz;
+ char *str;
+ size_t str_len;
+
+
+ fd = GNUNET_DISK_pipe_handle (pi, GNUNET_DISK_PIPE_END_WRITE);
+ str = json_dumps (root, JSON_COMPACT);
+ str_len = strlen (str);
+ sz = GNUNET_DISK_file_write (fd,
+ str,
+ str_len);
+ free (str);
+ if (sz != str_len)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ LOG_PREFIX "couldn't write all data to replay_program\n");
+ }
+ }
+
+ // Read output from stdout
+ {
+ ssize_t sz;
+ char buf[MAX_RESULT_SIZE];
+ json_error_t error;
+ json_t *res;
+
+ fd = GNUNET_DISK_pipe_handle (po, GNUNET_DISK_PIPE_END_READ);
+
+ sz = GNUNET_DISK_file_read (fd,
+ buf,
+ sizeof(buf));
+ if (GNUNET_SYSERR == sz)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ LOG_PREFIX "couldn't read data from replay_program\n");
+ return json_error (result, "internal error");
+ }
+
+ buf[sz] = 0;
+ res = json_loads (buf,
+ JSON_DECODE_ANY,
+ &error);
+
+ if (! res)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ LOG_PREFIX
+ "couldn't parse response from replay_program: %s (for '%s')\n",
+ error.text,
+ buf);
+ return json_error (result, error.text);
+ }
+
+ // Handle error case first
+ {
+ json_t *err = json_object_get (res,
+ "error");
+ if (NULL != err)
+ {
+ *result = json_copy (res);
+ json_decref (res);
+ return GNUNET_SYSERR;
+ }
+ }
+
+ // Parse the result
+ {
+ json_t *winners = json_object_get (res,
+ "winners");
+ if ((NULL == winners) ||
+ (! json_is_array (winners)))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ LOG_PREFIX
+ "replay program didn't return a known result type, instead: '%s'\n",
+ json_dumps (res, JSON_INDENT (2)));
+ return json_error (result, "internal error");
+ }
+
+ {
+ // TODO: check each winner with tr->expected, if applicable
+ json_object_set (res, "exchange_sig", json_string (
+ "sig(priv_E, winners)"));
+
+ }
+
+ // TODO: return own result object.
+ *result = json_copy (res);
+ json_decref (res);
+ }
+
+ }
+
+ if (GNUNET_OK != GNUNET_OS_process_wait (proc))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ LOG_PREFIX "error while launching auction replay program '%s'\n",
+ replay_program);
+
+ json_object_clear (*result);
+ return json_error (result, "internal error");
+ }
+
+
+ return GNUNET_OK;
+}
+
+
+/**
+ * @brief implements the TALER_Extension.disable interface.
+ *
+ * @param ext Pointer to the current extension
+ */
+static void
+auction_disable (
+ struct TALER_Extension *ext)
+{
+ /* TODO: cleanup configuration */
+ 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)
+{
+ /* TODO: add configuration */
+ return GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_bool ("critical", ext->critical),
+ GNUNET_JSON_pack_string ("version", ext->version));
+}
+
+
+/**
+ * @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)
+{
+ /* TODO: add configuration */
+ ext->enabled = true;
+ return GNUNET_OK;
+}
+
+
+/**
+ * @brief implements the TALER_Extension.http_get_handler
+ */
+static MHD_RESULT
+auction_http_get_handler (
+ struct MHD_Connection *connection,
+ const char *const args[])
+{
+ /* TODO: return some meta-data about supported version, limits, etc.*/
+
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ LOG_PREFIX "auction_http_get_handler not implemented yet\n");
+
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_NOT_IMPLEMENTED,
+ TALER_EC_EXCHANGE_GENERIC_OPERATION_UNKNOWN,
+ "auction_http_get_handler not implemented yet\n");
+
+}
+
+
+/**
+ * @brief implements the TALER_Extension.http_post_handler
+ *
+ * TODO: make this non-blocking
+ */
+static MHD_RESULT
+auction_http_post_handler (
+ struct MHD_Connection *connection,
+ const json_t *root,
+ const char *const args[])
+{
+ struct transcript tr = {};
+ enum GNUNET_GenericReturnValue ret;
+ json_t *result1;
+ json_t *result2;
+
+ ret = parse_transcript (root,
+ &tr,
+ &result1);
+ if (GNUNET_OK != ret)
+ return TALER_MHD_reply_json_steal (connection,
+ result1,
+ MHD_HTTP_BAD_REQUEST);
+ GNUNET_assert (NULL == result1);
+
+ ret = replay_transcript (root,
+ &tr,
+ &result2);
+
+ return TALER_MHD_reply_json_steal (connection,
+ result2,
+ GNUNET_OK == ret?
+ MHD_HTTP_OK :
+ MHD_HTTP_BAD_REQUEST);
+}
+
+
+/* The extension struct for auctions of brandt-style */
+struct TALER_Extension TE_auction_brandt = {
+ .type = TALER_Extension_PolicyAuction,
+ .name = POLICY_AUCTION,
+ .critical = false,
+ .version = "0",
+ .enabled = false, /* disabled per default */
+ .has_config = true,
+ .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_get_handler = &auction_http_get_handler,
+ .http_post_handler = &auction_http_post_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 *
+libtaler_extension_policy_auction_init (void *arg)
+{
+ const struct GNUNET_CONFIGURATION_Handle *cfg = arg;
+
+
+ if (GNUNET_OK !=
+ TALER_config_get_currency (cfg,
+ &currency))
+ return NULL;
+
+ if (GNUNET_SYSERR ==
+ GNUNET_CONFIGURATION_get_value_string (cfg,
+ TALER_EXTENSION_SECTION_PREFIX
+ POLICY_AUCTION,
+ "REPLAY_PROGRAM",
+ &replay_program))
+ {
+ GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+ TALER_EXTENSION_SECTION_PREFIX POLICY_AUCTION,
+ "REPLAY_PROGRAM");
+ return NULL;
+ }
+
+ /* check if replay_program is actually an executable */
+ {
+ struct stat sb;
+
+ if (0 != stat (replay_program, &sb))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ LOG_PREFIX "replay_program '%s' not found\n",
+ replay_program);
+ return NULL;
+ }
+
+ if ( (sb.st_mode & S_IFDIR) ||
+ ! (sb.st_mode & S_IXUSR))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ LOG_PREFIX "replay_program '%s' is not an executable\n",
+ replay_program);
+ return NULL;
+ }
+
+ }
+
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ LOG_PREFIX "loading... using replay_program '%s'\n",
+ replay_program);
+
+ /* TODO: read other config parameters and generate configuration */
+
+
+ return &TE_auction_brandt;
+}
+
+
+/**
+ * @brief Tear-down function for the extension.
+ * Will be called by GNUNET_PLUGIN_unload.
+ *
+ * @param ignored
+ * @return null
+ */
+void *
+libtaler_extension_policy_auction_done (void *arg)
+{
+ auction_disable (&TE_auction_brandt);
+ GNUNET_free (replay_program);
+ replay_program = NULL;
+
+ return NULL;
+}
+
+
+/* end of policy_auction.c */
diff --git a/src/include/taler_extensions.h b/src/include/taler_extensions.h
index 8e1823cc..f81b9653 100644
--- a/src/include/taler_extensions.h
+++ b/src/include/taler_extensions.h
@@ -24,6 +24,7 @@
#include <gnunet/gnunet_util_lib.h>
#include "taler_crypto_lib.h"
#include "taler_json_lib.h"
+#include "taler_mhd_lib.h"
#define TALER_EXTENSION_SECTION_PREFIX "exchange-extension-"
@@ -31,7 +32,21 @@
enum TALER_Extension_Type
{
TALER_Extension_AgeRestriction = 0,
- TALER_Extension_MaxPredefined = 1 // Must be last of the predefined
+ TALER_Extension_PolicyRefund = 1,
+ TALER_Extension_PolicyAuction = 2,
+ TALER_Extension_PolicyEscrow = 3,
+ TALER_Extension_MaxPredefined = 4 // Must be last of the predefined
+};
+
+
+/*
+ * @brief simply linked list of extensions
+ */
+
+struct TALER_Extensions
+{
+ struct TALER_Extensions *next;
+ const struct TALER_Extension *extension;
};
/*
@@ -41,14 +56,13 @@ enum TALER_Extension_Type
*/
struct TALER_Extension
{
- /* simple linked list */
- struct TALER_Extension *next;
-
enum TALER_Extension_Type type;
char *name;
bool critical;
char *version;
void *config;
+ bool enabled;
+ bool has_config; /* some extension might not have a configuration */
json_t *config_json;
void (*disable)(struct TALER_Extension *ext);
@@ -63,9 +77,15 @@ struct TALER_Extension
json_t *(*config_to_json)(
const struct TALER_Extension *ext);
- enum GNUNET_GenericReturnValue (*load_taler_config)(
- struct TALER_Extension *ext,
- const struct GNUNET_CONFIGURATION_Handle *cfg);
+ MHD_RESULT (*http_post_handler)(
+ struct MHD_Connection *connection,
+ const json_t *root,
+ const char *const args[]);
+
+ MHD_RESULT (*http_get_handler)(
+ struct MHD_Connection *connection,
+ const char *const args[]);
+
};
/**
@@ -73,15 +93,15 @@ struct TALER_Extension
*/
/*
- * @brief Sets the configuration of the extensions from the given TALER
- * configuration.
+ * @brief Loads the extensions as shared libraries, as specified in the given
+ * TALER configuration.
*
* @param cfg Handle to the TALER configuration
* @return GNUNET_OK on success, GNUNET_SYSERR if unknown extensions were found
* or any particular configuration couldn't be parsed.
*/
enum GNUNET_GenericReturnValue
-TALER_extensions_load_taler_config (
+TALER_extensions_load (
const struct GNUNET_CONFIGURATION_Handle *cfg);
/*
@@ -118,25 +138,13 @@ TALER_extensions_load_json_config (
/*
* @brief Returns the head of the linked list of extensions.
*/
-const struct TALER_Extension *
+const struct TALER_Extensions *
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.
*
- * @param type type of the extension to lookup
+ * @param type of the extension to lookup
* @return extension found, or NULL (should not happen!)
*/
const struct TALER_Extension *
@@ -154,8 +162,6 @@ const struct TALER_Extension *
TALER_extensions_get_by_name (
const char *name);
-#define TALER_extensions_is_enabled(ext) (NULL != (ext)->config)
-
/**
* @brief Check if a given type of an extension is enabled
*
@@ -166,6 +172,15 @@ bool
TALER_extensions_is_enabled_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
@@ -189,6 +204,9 @@ TALER_extensions_verify_json_config_signature (
/*
* 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 \
@@ -204,98 +222,41 @@ TALER_extensions_verify_json_config_signature (
| 1 << 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
- */
-enum GNUNET_GenericReturnValue
-TALER_extension_age_restriction_register ();
-/**
- * @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.
+/*
+ * @brief Configuration for Age Restriction
*/
-enum GNUNET_GenericReturnValue
-TALER_parse_age_group_string (
- const char *groups,
- struct TALER_AgeMask *mask);
+struct TALER_AgeRestrictionConfig
+{
+ bool enabled;
+ struct TALER_AgeMask mask;
+ uint8_t num_groups;
+};
-/**
- * @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 Returns true when age restriction is configured and enabled.
+ * @brief Retrieve the age restriction configuration
+ *
+ * @return age restriction configuration if present, otherwise NULL.
*/
-bool
-TALER_extensions_age_restriction_is_enabled ();
+const struct TALER_AgeRestrictionConfig *
+TALER_extensions_get_age_restriction_config ();
/**
- * @brief Returns true when age restriction is configured (might not be
- * _enabled_, though).
+ * @brief Check if age restriction is enabled
+ *
+ * @return true, if age restriction is loaded, configured and enabled; otherwise false.
*/
bool
-TALER_extensions_age_restriction_is_configured ();
+TALER_extensions_is_age_restriction_enabled ();
/**
- * @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
-TALER_extensions_age_restriction_ageMask ();
-
-
-/**
- * @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" }.
+ * @brief Return the age mask for age restriction
*
- * @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.
+ * @return configured age mask, if age restriction is loaded, configured and enabled; otherwise zero mask.
*/
-enum GNUNET_GenericReturnValue
-TALER_JSON_parse_age_groups (const json_t *root,
- struct TALER_AgeMask *mask);
+struct TALER_AgeMask
+TALER_extensions_get_age_restriction_mask ();
/*
diff --git a/src/include/taler_util.h b/src/include/taler_util.h
index 079f72ed..e821fc19 100644
--- a/src/include/taler_util.h
+++ b/src/include/taler_util.h
@@ -492,5 +492,64 @@ char *strchrnul (const char *s, int c);
#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
diff --git a/src/lib/exchange_api_handle.c b/src/lib/exchange_api_handle.c
index 707e5cf5..add37a80 100644
--- a/src/lib/exchange_api_handle.c
+++ b/src/lib/exchange_api_handle.c
@@ -902,16 +902,19 @@ decode_keys_json (const json_t *resp_obj,
{
struct TALER_MasterSignatureP extensions_sig = {0};
json_t *extensions = NULL;
+ bool no_extensions = false;
+ bool no_signature = false;
+
struct GNUNET_JSON_Specification ext_spec[] = {
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_json ("extensions",
&extensions),
- NULL),
+ &no_extensions),
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_fixed_auto (
"extensions_sig",
&extensions_sig),
- NULL),
+ &no_signature),
GNUNET_JSON_spec_end ()
};
@@ -921,7 +924,12 @@ decode_keys_json (const json_t *resp_obj,
ext_spec,
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. */
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 */
- key_data->age_mask = TALER_extensions_age_restriction_ageMask ();
+ key_data->age_mask = TALER_extensions_get_age_restriction_mask ();
}
/**
diff --git a/src/testing/test_exchange_api.c b/src/testing/test_exchange_api.c
index f30701c3..2dfda067 100644
--- a/src/testing/test_exchange_api.c
+++ b/src/testing/test_exchange_api.c
@@ -1315,9 +1315,6 @@ main (int argc,
"INFO",
NULL);
- GNUNET_assert (GNUNET_OK ==
- TALER_extension_age_restriction_register ());
-
cipher = GNUNET_TESTING_get_testname_from_underscore (argv[0]);
GNUNET_assert (NULL != cipher);
uses_cs = (0 == strcmp (cipher, "cs"));
diff --git a/src/testing/test_exchange_auction.conf b/src/testing/test_exchange_auction.conf
new file mode 100644
index 00000000..89345d97
--- /dev/null
+++ b/src/testing/test_exchange_auction.conf
@@ -0,0 +1,160 @@
+# This file is in the public domain.
+#
+
+[PATHS]
+# Persistent data storage for the testcase
+TALER_TEST_HOME = test_exchange_api_home/
+TALER_RUNTIME_DIR = ${TMPDIR:-${TMP:-/tmp}}/${USER:-}/taler-system-runtime/
+
+[taler-exchange-secmod-rsa]
+# Reduce from 1 year to speed up test
+LOOKAHEAD_SIGN = 24 days
+
+[taler-exchange-secmod-eddsa]
+# Reduce from 1 year to speed up test
+LOOKAHEAD_SIGN = 24 days
+# Reduce from 12 weeks to ensure we have multiple
+DURATION = 14 days
+
+[taler]
+# Currency supported by the exchange (can only be one)
+CURRENCY = EUR
+CURRENCY_ROUND_UNIT = EUR:0.01
+
+[auditor]
+BASE_URL = "http://localhost:8083/"
+
+# HTTP port the auditor listens to
+PORT = 8083
+
+[exchange]
+
+TERMS_ETAG = 0
+PRIVACY_ETAG = 0
+
+# HTTP port the exchange listens to
+PORT = 8081
+
+# Master public key used to sign the exchange's various keys
+MASTER_PUBLIC_KEY = 98NJW3CQHZQGQXTY3K85K531XKPAPAVV4Q5V8PYYRR00NJGZWNVG
+
+# How to access our database
+DB = postgres
+
+# Base URL of the exchange. Must be set to a URL where the
+# exchange (or the twister) is actually listening.
+BASE_URL = "http://localhost:8081/"
+
+# How big is an individual shard to be processed
+# by taler-exchange-expire (in time). It may take
+# this much time for an expired purse to be really
+# cleaned up and the coins refunded.
+EXPIRE_SHARD_SIZE = 300 ms
+
+EXPIRE_IDLE_SLEEP_INTERVAL = 1 s
+
+
+[exchangedb-postgres]
+CONFIG = "postgres:///talercheck"
+
+[auditordb-postgres]
+CONFIG = "postgres:///talercheck"
+
+# Sections starting with "exchange-account-" configure the bank accounts
+# of the exchange. The "URL" specifies the account in
+# payto://-format.
+[exchange-account-1]
+# What is the URL of our account?
+PAYTO_URI = "payto://x-taler-bank/localhost/42?receiver-name=42"
+# ENABLE_CREDIT = YES
+
+[exchange-accountcredentials-1]
+WIRE_GATEWAY_URL = "http://localhost:9081/42/"
+
+[exchange-account-2]
+# What is the bank account (with the "Taler Bank" demo system)?
+PAYTO_URI = "payto://x-taler-bank/localhost/2?receiver-name=2"
+ENABLE_DEBIT = YES
+ENABLE_CREDIT = YES
+
+[exchange-accountcredentials-2]
+WIRE_GATEWAY_AUTH_METHOD = basic
+USERNAME = Exchange
+PASSWORD = x
+WIRE_GATEWAY_URL = "http://localhost:9081/2/"
+
+[bank]
+HTTP_PORT = 9081
+
+# Enabled extensions
+#[exchange-extension-age_restriction]
+#ENABLED = YES
+# default age groups:
+#AGE_GROUPS = "8:10:12:14:16:18:21"
+
+# Enable policy of type auction for deposits.
+[exchange-extension-policy_auction]
+ENABLED = YES
+REPLAY_PROGRAM = "/usr/local/bin/taler-exchange-auction_brandt-replay"
+
+# Sections starting with "coin_" specify which denominations
+# the exchange should support (and their respective fee structure)
+[coin_eur_ct_1]
+value = EUR:0.01
+duration_withdraw = 7 days
+duration_spend = 2 years
+duration_legal = 3 years
+fee_withdraw = EUR:0.00
+fee_deposit = EUR:0.00
+fee_refresh = EUR:0.01
+fee_refund = EUR:0.01
+CIPHER = RSA
+rsa_keysize = 1024
+
+[coin_eur_ct_10]
+value = EUR:0.10
+duration_withdraw = 7 days
+duration_spend = 2 years
+duration_legal = 3 years
+fee_withdraw = EUR:0.01
+fee_deposit = EUR:0.01
+fee_refresh = EUR:0.03
+fee_refund = EUR:0.01
+CIPHER = RSA
+rsa_keysize = 1024
+
+[coin_eur_1]
+value = EUR:1
+duration_withdraw = 7 days
+duration_spend = 2 years
+duration_legal = 3 years
+fee_withdraw = EUR:0.01
+fee_deposit = EUR:0.01
+fee_refresh = EUR:0.03
+fee_refund = EUR:0.01
+CIPHER = RSA
+rsa_keysize = 1024
+
+[coin_eur_5]
+value = EUR:5
+duration_withdraw = 7 days
+duration_spend = 2 years
+duration_legal = 3 years
+fee_withdraw = EUR:0.01
+fee_deposit = EUR:0.01
+fee_refresh = EUR:0.03
+fee_refund = EUR:0.01
+CIPHER = RSA
+rsa_keysize = 1024
+
+[coin_eur_10]
+value = EUR:10
+duration_withdraw = 7 days
+duration_spend = 2 years
+duration_legal = 3 years
+fee_withdraw = EUR:0.01
+fee_deposit = EUR:0.01
+fee_refresh = EUR:0.03
+fee_refund = EUR:0.01
+CIPHER = RSA
+rsa_keysize = 1024
diff --git a/src/testing/test_exchange_p2p.c b/src/testing/test_exchange_p2p.c
index 726dc315..da723ba7 100644
--- a/src/testing/test_exchange_p2p.c
+++ b/src/testing/test_exchange_p2p.c
@@ -417,9 +417,6 @@ main (int argc,
"INFO",
NULL);
- GNUNET_assert (GNUNET_OK ==
- TALER_extension_age_restriction_register ());
-
cipher = GNUNET_TESTING_get_testname_from_underscore (argv[0]);
GNUNET_assert (NULL != cipher);
uses_cs = (0 == strcmp (cipher, "cs"));
diff --git a/src/testing/testing_api_cmd_batch_withdraw.c b/src/testing/testing_api_cmd_batch_withdraw.c
index b68ca99f..a5229ae9 100644
--- a/src/testing/testing_api_cmd_batch_withdraw.c
+++ b/src/testing/testing_api_cmd_batch_withdraw.c
@@ -490,7 +490,7 @@ TALER_TESTING_cmd_batch_withdraw (const char *label,
acp = GNUNET_new (struct TALER_AgeCommitmentProof);
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,
&seed,
sizeof(seed));
diff --git a/src/testing/testing_api_cmd_withdraw.c b/src/testing/testing_api_cmd_withdraw.c
index ce2c49cc..7a81d1c3 100644
--- a/src/testing/testing_api_cmd_withdraw.c
+++ b/src/testing/testing_api_cmd_withdraw.c
@@ -590,7 +590,7 @@ TALER_TESTING_cmd_withdraw_amount (const char *label,
acp = GNUNET_new (struct TALER_AgeCommitmentProof);
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,
&seed,
sizeof(seed));
diff --git a/src/testing/testing_api_helpers_exchange.c b/src/testing/testing_api_helpers_exchange.c
index d813021b..f31ef274 100644
--- a/src/testing/testing_api_helpers_exchange.c
+++ b/src/testing/testing_api_helpers_exchange.c
@@ -314,8 +314,13 @@ sign_keys_for_exchange (void *cls,
char *exchange_master_pub;
int ret;
- /* Load the age restriction mask from the configuration */
- TALER_extensions_load_taler_config (cfg);
+ /* Load the extensions */
+ if (GNUNET_OK != TALER_extensions_load (cfg))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "couldn't load extensions");
+ return GNUNET_SYSERR;
+ }
if (GNUNET_OK !=
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))
return GNUNET_NO;
}
+
if (GNUNET_OK !=
GNUNET_CONFIGURATION_parse_and_run (config_filename,
&sign_keys_for_exchange,
@@ -709,6 +715,10 @@ TALER_TESTING_setup_with_exchange_cfg (
char *base_url;
int result;
+ /* Load extensions */
+ if (GNUNET_OK != TALER_extensions_load (cfg))
+ return GNUNET_SYSERR;
+
if (GNUNET_OK !=
GNUNET_CONFIGURATION_get_value_string (cfg,
"exchange",
diff --git a/src/util/Makefile.am b/src/util/Makefile.am
index 94edac02..acafdae5 100644
--- a/src/util/Makefile.am
+++ b/src/util/Makefile.am
@@ -105,6 +105,7 @@ libtalerutil_la_SOURCES = \
libtalerutil_la_LIBADD = \
-lgnunetutil \
+ -lgnunetjson \
-lsodium \
-ljansson \
$(LIBGCRYPT_LIBS) \
diff --git a/src/util/age_restriction.c b/src/util/age_restriction.c
index b87c8543..f4ac9abe 100644
--- a/src/util/age_restriction.c
+++ b/src/util/age_restriction.c
@@ -21,6 +21,7 @@
#include "platform.h"
#include "taler_util.h"
#include "taler_signatures.h"
+#include <gnunet/gnunet_json_lib.h>
#include <gcrypt.h>
void
@@ -436,3 +437,113 @@ TALER_age_commitment_proof_free (
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 */