/* 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 */ /** * @file extension_age_restriction.c * @brief Utility functions regarding age restriction * @author Özgür Kesim */ #include "platform.h" #include "taler_util.h" #include "taler_extensions.h" #include "stdint.h" /** * @param groups String representation of the age groups. Must be of the form * a:b:...:n:m * with * 0 < a < b <...< n < m < 32 * @param[out] mask Bit representation of the age groups. * @return Error if string was invalid, OK otherwise. */ 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->mask |= 1 << val; prev = val; val = 0; continue; } if ('0'>c || '9'=val || 32<=val) return GNUNET_SYSERR; } if (0>val || 32<=val || prev>=val) return GNUNET_SYSERR; mask->mask |= (1 << val); mask->mask |= 1; // mark zeroth group, too return GNUNET_OK; } /** * 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 *m) { uint32_t mask = m->mask; unsigned int n = 0; char *buf = GNUNET_malloc (32 * 3); // max characters possible char *pos = buf; if (NULL == buf) { return buf; } while (mask != 0) { mask >>= 1; n++; if (0 == (mask & 1)) { continue; } if (n > 9) { *(pos++) = '0' + n / 10; } *(pos++) = '0' + n % 10; if (0 != (mask >> 1)) { *(pos++) = ':'; } } return buf; } /* ================================================== * * Age Restriction TALER_Extension imlementation * * ================================================== */ /** * @brief implements the TALER_Extension.disable interface. */ void age_restriction_disable ( struct TALER_Extension *this) { if (NULL == this) return; this->config = NULL; if (NULL != this->config_json) { json_decref (this->config_json); this->config_json = NULL; } } /** * @brief implements the TALER_Extension.load_taler_config interface. * @param cfg Handle to the GNUNET configuration * @param[out] enabled Set to true if age restriction is enabled in the config, false otherwise. * @param[out] mask Mask for age restriction. Will be 0 if age restriction was not enabled in the config. * @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 *this, 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 */ this->config = NULL; this->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.mask = 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.mask = TALER_EXTENSION_AGE_RESTRICTION_DEFAULT_AGE_MASK; } if (GNUNET_OK == ret) this->config = (void *) (size_t) mask.mask; GNUNET_free (groups); return ret; } /** * @brief implements the TALER_Extension.load_json_config interface. * @param this if NULL, only tests the configuration * @param config the configuration as json */ static enum GNUNET_GenericReturnValue age_restriction_load_json_config ( struct TALER_Extension *this, json_t *config) { struct TALER_AgeMask mask = {0}; enum GNUNET_GenericReturnValue ret; ret = TALER_JSON_parse_agemask (config, &mask); if (GNUNET_OK != ret) return ret; /* only testing the parser */ if (this == NULL) return GNUNET_OK; if (TALER_Extension_AgeRestriction != this->type) return GNUNET_SYSERR; if (NULL != this->config) GNUNET_free (this->config); this->config = GNUNET_malloc (sizeof(struct TALER_AgeMask)); GNUNET_memcpy (this->config, &mask, sizeof(struct TALER_AgeMask)); if (NULL != this->config_json) json_decref (this->config_json); this->config_json = config; return GNUNET_OK; } /** * @brief implements the TALER_Extension.load_json_config interface. * @param this if NULL, only tests the configuration * @param config the configuration as json */ json_t * age_restriction_config_to_json ( const struct TALER_Extension *this) { struct TALER_AgeMask mask; char *mask_str; json_t *conf; GNUNET_assert (NULL != this); GNUNET_assert (NULL != this->config); if (NULL != this->config_json) { return json_copy (this->config_json); } mask.mask = (uint32_t) (size_t) this->config; mask_str = TALER_age_mask_to_string (&mask); conf = GNUNET_JSON_PACK ( GNUNET_JSON_pack_string ("age_groups", mask_str) ); return GNUNET_JSON_PACK ( GNUNET_JSON_pack_bool ("critical", this->critical), GNUNET_JSON_pack_string ("version", this->version), GNUNET_JSON_pack_object_steal ("config", conf) ); } /** * @brief implements the TALER_Extension.test_json_config interface. */ static enum GNUNET_GenericReturnValue age_restriction_test_json_config ( const json_t *config) { struct TALER_AgeMask mask = {0}; return TALER_JSON_parse_agemask (config, &mask); } /* The extension for age restriction */ struct TALER_Extension _extension_age_restriction = { .next = NULL, .type = TALER_Extension_AgeRestriction, .name = "age_restriction", .critical = false, .version = "1", .config = NULL, // disabled per default .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, }; /* end of extension_age_restriction.c */