Line data Source code
1 : /*
2 : This file is part of TALER
3 : Copyright (C) 2021-2022 Taler Systems SA
4 :
5 : TALER is free software; you can redistribute it and/or modify it under the
6 : terms of the GNU General Public License as published by the Free Software
7 : Foundation; either version 3, or (at your option) any later version.
8 :
9 : TALER is distributed in the hope that it will be useful, but WITHOUT ANY
10 : WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
11 : A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12 :
13 : You should have received a copy of the GNU General Public License along with
14 : TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
15 : */
16 : /**
17 : * @file extension_age_restriction.c
18 : * @brief Utility functions regarding age restriction
19 : * @author Özgür Kesim
20 : */
21 : #include "platform.h"
22 : #include "taler_util.h"
23 : #include "taler_extensions.h"
24 : #include "stdint.h"
25 :
26 : /**
27 : * Carries all the information we need for age restriction
28 : */
29 : struct age_restriction_config
30 : {
31 : struct TALER_AgeMask mask;
32 : size_t num_groups;
33 : };
34 :
35 : /**
36 : * Global config for this extension
37 : */
38 : static struct age_restriction_config TE_age_restriction_config = {0};
39 :
40 : enum GNUNET_GenericReturnValue
41 0 : TALER_parse_age_group_string (
42 : const char *groups,
43 : struct TALER_AgeMask *mask)
44 : {
45 :
46 0 : const char *pos = groups;
47 0 : unsigned int prev = 0;
48 0 : unsigned int val = 0;
49 : char c;
50 :
51 0 : while (*pos)
52 : {
53 0 : c = *pos++;
54 0 : if (':' == c)
55 : {
56 0 : if (prev >= val)
57 0 : return GNUNET_SYSERR;
58 :
59 0 : mask->bits |= 1 << val;
60 0 : prev = val;
61 0 : val = 0;
62 0 : continue;
63 : }
64 :
65 0 : if ('0'>c || '9'<c)
66 0 : return GNUNET_SYSERR;
67 :
68 0 : val = 10 * val + c - '0';
69 :
70 0 : if (0>=val || 32<=val)
71 0 : return GNUNET_SYSERR;
72 : }
73 :
74 0 : if (32<=val || prev>=val)
75 0 : return GNUNET_SYSERR;
76 :
77 0 : mask->bits |= (1 << val);
78 0 : mask->bits |= 1; // mark zeroth group, too
79 :
80 0 : return GNUNET_OK;
81 : }
82 :
83 :
84 : char *
85 0 : TALER_age_mask_to_string (
86 : const struct TALER_AgeMask *mask)
87 : {
88 0 : uint32_t bits = mask->bits;
89 0 : unsigned int n = 0;
90 0 : char *buf = GNUNET_malloc (32 * 3); // max characters possible
91 0 : char *pos = buf;
92 :
93 0 : if (NULL == buf)
94 : {
95 0 : return buf;
96 : }
97 :
98 0 : while (bits != 0)
99 : {
100 0 : bits >>= 1;
101 0 : n++;
102 0 : if (0 == (bits & 1))
103 : {
104 0 : continue;
105 : }
106 :
107 0 : if (n > 9)
108 : {
109 0 : *(pos++) = '0' + n / 10;
110 : }
111 0 : *(pos++) = '0' + n % 10;
112 :
113 0 : if (0 != (bits >> 1))
114 : {
115 0 : *(pos++) = ':';
116 : }
117 : }
118 0 : return buf;
119 : }
120 :
121 :
122 : /* ==================================================
123 : *
124 : * Age Restriction TALER_Extension implementation
125 : *
126 : * ==================================================
127 : */
128 :
129 : /**
130 : * @brief implements the TALER_Extension.disable interface.
131 : *
132 : * @param ext Pointer to the current extension
133 : */
134 : void
135 0 : age_restriction_disable (
136 : struct TALER_Extension *ext)
137 : {
138 0 : if (NULL == ext)
139 0 : return;
140 :
141 0 : ext->config = NULL;
142 :
143 0 : if (NULL != ext->config_json)
144 : {
145 0 : json_decref (ext->config_json);
146 0 : ext->config_json = NULL;
147 : }
148 :
149 0 : TE_age_restriction_config.mask.bits = 0;
150 0 : TE_age_restriction_config.num_groups = 0;
151 : }
152 :
153 :
154 : /**
155 : * @brief implements the TALER_Extension.load_taler_config interface.
156 : *
157 : * @param ext Pointer to the current extension
158 : * @param cfg Handle to the GNUNET configuration
159 : * @return Error if extension for age restriction was set, but age groups were
160 : * invalid, OK otherwise.
161 : */
162 : static enum GNUNET_GenericReturnValue
163 0 : age_restriction_load_taler_config (
164 : struct TALER_Extension *ext,
165 : const struct GNUNET_CONFIGURATION_Handle *cfg)
166 : {
167 0 : char *groups = NULL;
168 0 : enum GNUNET_GenericReturnValue ret = GNUNET_SYSERR;
169 0 : struct TALER_AgeMask mask = {0};
170 :
171 0 : if ((GNUNET_YES !=
172 0 : GNUNET_CONFIGURATION_have_value (cfg,
173 : TALER_EXTENSION_SECTION_AGE_RESTRICTION,
174 : "ENABLED"))
175 0 : ||
176 : (GNUNET_YES !=
177 0 : GNUNET_CONFIGURATION_get_value_yesno (cfg,
178 : TALER_EXTENSION_SECTION_AGE_RESTRICTION,
179 : "ENABLED")))
180 : {
181 : /* Age restriction is not enabled */
182 0 : ext->config = NULL;
183 0 : ext->config_json = NULL;
184 0 : return GNUNET_OK;
185 : }
186 :
187 : /* Age restriction is enabled, extract age groups */
188 0 : if ((GNUNET_YES ==
189 0 : GNUNET_CONFIGURATION_have_value (cfg,
190 : TALER_EXTENSION_SECTION_AGE_RESTRICTION,
191 : "AGE_GROUPS"))
192 0 : &&
193 : (GNUNET_YES !=
194 0 : GNUNET_CONFIGURATION_get_value_string (cfg,
195 : TALER_EXTENSION_SECTION_AGE_RESTRICTION,
196 : "AGE_GROUPS",
197 : &groups)))
198 0 : return GNUNET_SYSERR;
199 :
200 :
201 0 : mask.bits = TALER_EXTENSION_AGE_RESTRICTION_DEFAULT_AGE_MASK;
202 0 : ret = GNUNET_OK;
203 :
204 0 : if (groups != NULL)
205 : {
206 0 : ret = TALER_parse_age_group_string (groups, &mask);
207 0 : if (GNUNET_OK != ret)
208 0 : mask.bits = TALER_EXTENSION_AGE_RESTRICTION_DEFAULT_AGE_MASK;
209 : }
210 :
211 0 : if (GNUNET_OK == ret)
212 : {
213 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
214 : "setting age mask to %x with #groups: %d\n", mask.bits,
215 : __builtin_popcount (mask.bits) - 1);
216 0 : TE_age_restriction_config.mask.bits = mask.bits;
217 0 : TE_age_restriction_config.num_groups = __builtin_popcount (mask.bits) - 1; /* no underflow, first bit always set */
218 0 : ext->config = &TE_age_restriction_config;
219 :
220 : /* Note: we do now have TE_age_restriction_config set, however
221 : * ext->config_json is NOT set, i.e. the extension is not yet active! For
222 : * age restriction to become active, load_json_config must have been
223 : * called. */
224 : }
225 :
226 :
227 0 : GNUNET_free (groups);
228 0 : return ret;
229 : }
230 :
231 :
232 : /**
233 : * @brief implements the TALER_Extension.load_json_config interface.
234 : *
235 : * @param ext if NULL, only tests the configuration
236 : * @param jconfig the configuration as json
237 : */
238 : static enum GNUNET_GenericReturnValue
239 0 : age_restriction_load_json_config (
240 : struct TALER_Extension *ext,
241 : json_t *jconfig)
242 : {
243 0 : struct TALER_AgeMask mask = {0};
244 : enum GNUNET_GenericReturnValue ret;
245 :
246 0 : ret = TALER_JSON_parse_age_groups (jconfig, &mask);
247 0 : if (GNUNET_OK != ret)
248 0 : return ret;
249 :
250 : /* only testing the parser */
251 0 : if (ext == NULL)
252 0 : return GNUNET_OK;
253 :
254 0 : if (TALER_Extension_AgeRestriction != ext->type)
255 0 : return GNUNET_SYSERR;
256 :
257 0 : TE_age_restriction_config.mask.bits = mask.bits;
258 0 : TE_age_restriction_config.num_groups = 0;
259 :
260 0 : if (mask.bits > 0)
261 : {
262 : /* if the mask is not zero, the first bit MUST be set */
263 0 : if (0 == (mask.bits & 1))
264 0 : return GNUNET_SYSERR;
265 :
266 0 : TE_age_restriction_config.num_groups = __builtin_popcount (mask.bits) - 1;
267 : }
268 :
269 0 : ext->config = &TE_age_restriction_config;
270 :
271 0 : if (NULL != ext->config_json)
272 0 : json_decref (ext->config_json);
273 :
274 0 : ext->config_json = jconfig;
275 :
276 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
277 : "loaded new age restriction config with age groups: %s\n",
278 : TALER_age_mask_to_string (&mask));
279 :
280 0 : return GNUNET_OK;
281 : }
282 :
283 :
284 : /**
285 : * @brief implements the TALER_Extension.load_json_config interface.
286 : *
287 : * @param ext if NULL, only tests the configuration
288 : * @return configuration as json_t* object
289 : */
290 : json_t *
291 0 : age_restriction_config_to_json (
292 : const struct TALER_Extension *ext)
293 : {
294 : char *mask_str;
295 : json_t *conf;
296 :
297 0 : GNUNET_assert (NULL != ext);
298 0 : GNUNET_assert (NULL != ext->config);
299 :
300 0 : if (NULL != ext->config_json)
301 : {
302 0 : return json_copy (ext->config_json);
303 : }
304 :
305 0 : mask_str = TALER_age_mask_to_string (&TE_age_restriction_config.mask);
306 0 : conf = GNUNET_JSON_PACK (
307 : GNUNET_JSON_pack_string ("age_groups", mask_str)
308 : );
309 :
310 0 : return GNUNET_JSON_PACK (
311 : GNUNET_JSON_pack_bool ("critical", ext->critical),
312 : GNUNET_JSON_pack_string ("version", ext->version),
313 : GNUNET_JSON_pack_object_steal ("config", conf)
314 : );
315 : }
316 :
317 :
318 : /**
319 : * @brief implements the TALER_Extension.test_json_config interface.
320 : *
321 : * @param config configuration as json_t* to test
322 : * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise.
323 : */
324 : static enum GNUNET_GenericReturnValue
325 0 : age_restriction_test_json_config (
326 : const json_t *config)
327 : {
328 0 : struct TALER_AgeMask mask = {0};
329 :
330 0 : return TALER_JSON_parse_age_groups (config, &mask);
331 : }
332 :
333 :
334 : /* The extension for age restriction */
335 : struct TALER_Extension TE_age_restriction = {
336 : .next = NULL,
337 : .type = TALER_Extension_AgeRestriction,
338 : .name = "age_restriction",
339 : .critical = false,
340 : .version = "1",
341 : .config = NULL, // disabled per default
342 : .config_json = NULL,
343 : .disable = &age_restriction_disable,
344 : .test_json_config = &age_restriction_test_json_config,
345 : .load_json_config = &age_restriction_load_json_config,
346 : .config_to_json = &age_restriction_config_to_json,
347 : .load_taler_config = &age_restriction_load_taler_config,
348 : };
349 :
350 : enum GNUNET_GenericReturnValue
351 4 : TALER_extension_age_restriction_register ()
352 : {
353 4 : return TALER_extensions_add (&TE_age_restriction);
354 : }
355 :
356 :
357 : bool
358 0 : TALER_extensions_age_restriction_is_configured ()
359 : {
360 0 : return (0 != TE_age_restriction_config.mask.bits);
361 : }
362 :
363 :
364 : struct TALER_AgeMask
365 0 : TALER_extensions_age_restriction_ageMask ()
366 : {
367 0 : return TE_age_restriction_config.mask;
368 : }
369 :
370 :
371 : size_t
372 0 : TALER_extensions_age_restriction_num_groups ()
373 : {
374 0 : return TE_age_restriction_config.num_groups;
375 : }
376 :
377 :
378 : enum GNUNET_GenericReturnValue
379 0 : TALER_JSON_parse_age_groups (const json_t *root,
380 : struct TALER_AgeMask *mask)
381 : {
382 : enum GNUNET_GenericReturnValue ret;
383 : const char *str;
384 : struct GNUNET_JSON_Specification spec[] = {
385 0 : GNUNET_JSON_spec_string ("age_groups",
386 : &str),
387 0 : GNUNET_JSON_spec_end ()
388 : };
389 :
390 0 : ret = GNUNET_JSON_parse (root,
391 : spec,
392 : NULL,
393 : NULL);
394 0 : if (GNUNET_OK == ret)
395 0 : TALER_parse_age_group_string (str, mask);
396 :
397 0 : GNUNET_JSON_parse_free (spec);
398 :
399 0 : return ret;
400 : }
401 :
402 :
403 : /* end of extension_age_restriction.c */
|