Line data Source code
1 : /*
2 : This file is part of TALER
3 : (C) 2026 Taler Systems SA
4 :
5 : TALER is free software; you can redistribute it and/or modify it under the
6 : terms of the GNU Lesser 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 src/util/token_family_parse.c
18 : * @brief parser for data about token families
19 : * @author Christian Grothoff
20 : */
21 : #include "platform.h"
22 : #include <string.h>
23 : #include "taler/taler_merchant_util.h"
24 : #include <taler/taler_json_lib.h>
25 : #include <gnunet/gnunet_json_lib.h>
26 :
27 :
28 : void
29 20 : TALER_MERCHANT_contract_token_family_free (
30 : struct TALER_MERCHANT_ContractTokenFamily *family)
31 : {
32 20 : GNUNET_free (family->slug);
33 20 : GNUNET_free (family->name);
34 20 : GNUNET_free (family->description);
35 20 : if (NULL != family->description_i18n)
36 : {
37 20 : json_decref (family->description_i18n);
38 20 : family->description_i18n = NULL;
39 : }
40 38 : for (unsigned int i = 0; i < family->keys_len; i++)
41 18 : TALER_token_issue_pub_free (&family->keys[i].pub);
42 20 : GNUNET_array_grow (family->keys,
43 : family->keys_len,
44 : 0);
45 20 : switch (family->kind)
46 : {
47 0 : case TALER_MERCHANT_CONTRACT_TOKEN_KIND_INVALID:
48 0 : GNUNET_break (0);
49 0 : break;
50 4 : case TALER_MERCHANT_CONTRACT_TOKEN_KIND_DISCOUNT:
51 10 : for (unsigned int i = 0; i < family->details.discount.expected_domains_len;
52 6 : i++)
53 6 : GNUNET_free (family->details.discount.expected_domains[i]);
54 4 : GNUNET_array_grow (family->details.discount.expected_domains,
55 : family->details.discount.expected_domains_len,
56 : 0);
57 4 : break;
58 16 : case TALER_MERCHANT_CONTRACT_TOKEN_KIND_SUBSCRIPTION:
59 22 : for (unsigned int i = 0; i < family->details.subscription.
60 6 : trusted_domains_len; i++)
61 6 : GNUNET_free (family->details.subscription.trusted_domains[i]);
62 16 : GNUNET_array_grow (family->details.subscription.trusted_domains,
63 : family->details.subscription.trusted_domains_len,
64 : 0);
65 16 : break;
66 : }
67 20 : }
68 :
69 :
70 : enum GNUNET_GenericReturnValue
71 2 : TALER_MERCHANT_find_token_family_key (
72 : const char *slug,
73 : struct GNUNET_TIME_Timestamp valid_after,
74 : const struct TALER_MERCHANT_ContractTokenFamily *families,
75 : unsigned int families_len,
76 : struct TALER_MERCHANT_ContractTokenFamily *family,
77 : struct TALER_MERCHANT_ContractTokenFamilyKey *key)
78 : {
79 3 : for (unsigned int i = 0; i < families_len; i++)
80 : {
81 3 : const struct TALER_MERCHANT_ContractTokenFamily *fami
82 3 : = &families[i];
83 :
84 3 : if (0 != strcmp (fami->slug,
85 : slug))
86 1 : continue;
87 2 : if (NULL != family)
88 2 : *family = *fami;
89 2 : for (unsigned int k = 0; k < fami->keys_len; k++)
90 : {
91 2 : struct TALER_MERCHANT_ContractTokenFamilyKey *ki = &fami->keys[k];
92 :
93 2 : if (GNUNET_TIME_timestamp_cmp (ki->valid_after,
94 : ==,
95 : valid_after))
96 : {
97 2 : if (NULL != key)
98 2 : *key = *ki;
99 2 : return GNUNET_OK;
100 : }
101 : }
102 : /* matching family found, but no key. */
103 0 : return GNUNET_NO;
104 : }
105 :
106 : /* no matching family found */
107 0 : return GNUNET_SYSERR;
108 : }
109 :
110 :
111 : /**
112 : * Parse token details of the given JSON contract token family.
113 : *
114 : * @param cls closure, unused parameter
115 : * @param root the JSON object representing data
116 : * @param[out] ospec where to write the data
117 : * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error
118 : */
119 : static enum GNUNET_GenericReturnValue
120 11 : parse_token_details (void *cls,
121 : json_t *root,
122 : struct GNUNET_JSON_Specification *ospec)
123 : {
124 11 : struct TALER_MERCHANT_ContractTokenFamily *family = ospec->ptr;
125 : const char *class;
126 : const char *ename;
127 : unsigned int eline;
128 : struct GNUNET_JSON_Specification ispec[] = {
129 11 : GNUNET_JSON_spec_string ("class",
130 : &class),
131 11 : GNUNET_JSON_spec_end ()
132 : };
133 :
134 : (void) cls;
135 11 : if (GNUNET_OK !=
136 11 : GNUNET_JSON_parse (root,
137 : ispec,
138 : &ename,
139 : &eline))
140 : {
141 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
142 : "Failed to parse %s at %u: %s\n",
143 : ispec[eline].field,
144 : eline,
145 : ename);
146 0 : GNUNET_break_op (0);
147 0 : return GNUNET_SYSERR;
148 : }
149 :
150 11 : family->kind = TALER_MERCHANT_contract_token_kind_from_string (class);
151 :
152 11 : switch (family->kind)
153 : {
154 0 : case TALER_MERCHANT_CONTRACT_TOKEN_KIND_INVALID:
155 0 : break;
156 8 : case TALER_MERCHANT_CONTRACT_TOKEN_KIND_SUBSCRIPTION:
157 : {
158 : const json_t *trusted_domains;
159 : struct GNUNET_JSON_Specification spec[] = {
160 8 : GNUNET_JSON_spec_array_const ("trusted_domains",
161 : &trusted_domains),
162 8 : GNUNET_JSON_spec_end ()
163 : };
164 :
165 8 : if (GNUNET_OK !=
166 8 : GNUNET_JSON_parse (root,
167 : spec,
168 : &ename,
169 : &eline))
170 : {
171 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
172 : "Failed to parse %s at %u: %s\n",
173 : spec[eline].field,
174 : eline,
175 : ename);
176 0 : GNUNET_break_op (0);
177 0 : return GNUNET_SYSERR;
178 : }
179 :
180 8 : GNUNET_array_grow (family->details.subscription.trusted_domains,
181 : family->details.subscription.trusted_domains_len,
182 : json_array_size (trusted_domains));
183 :
184 8 : for (unsigned int i = 0;
185 14 : i < family->details.subscription.trusted_domains_len;
186 6 : i++)
187 : {
188 : const json_t *jdomain;
189 6 : jdomain = json_array_get (trusted_domains, i);
190 :
191 6 : if (! json_is_string (jdomain))
192 : {
193 0 : GNUNET_break_op (0);
194 0 : return GNUNET_SYSERR;
195 : }
196 :
197 6 : family->details.subscription.trusted_domains[i] =
198 6 : GNUNET_strdup (json_string_value (jdomain));
199 : }
200 :
201 8 : return GNUNET_OK;
202 : }
203 3 : case TALER_MERCHANT_CONTRACT_TOKEN_KIND_DISCOUNT:
204 : {
205 : const json_t *expected_domains;
206 : struct GNUNET_JSON_Specification spec[] = {
207 3 : GNUNET_JSON_spec_array_const ("expected_domains",
208 : &expected_domains),
209 3 : GNUNET_JSON_spec_end ()
210 : };
211 :
212 3 : if (GNUNET_OK !=
213 3 : GNUNET_JSON_parse (root,
214 : spec,
215 : &ename,
216 : &eline))
217 : {
218 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
219 : "Failed to parse %s at %u: %s\n",
220 : spec[eline].field,
221 : eline,
222 : ename);
223 0 : GNUNET_break_op (0);
224 0 : return GNUNET_SYSERR;
225 : }
226 :
227 3 : GNUNET_array_grow (family->details.discount.expected_domains,
228 : family->details.discount.expected_domains_len,
229 : json_array_size (expected_domains));
230 :
231 3 : for (unsigned int i = 0;
232 9 : i < family->details.discount.expected_domains_len;
233 6 : i++)
234 : {
235 : const json_t *jdomain;
236 :
237 6 : jdomain = json_array_get (expected_domains,
238 : i);
239 6 : if (! json_is_string (jdomain))
240 : {
241 0 : GNUNET_break_op (0);
242 0 : return GNUNET_SYSERR;
243 : }
244 :
245 6 : family->details.discount.expected_domains[i] =
246 6 : GNUNET_strdup (json_string_value (jdomain));
247 : }
248 :
249 3 : return GNUNET_OK;
250 : }
251 : }
252 :
253 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
254 : "Field 'type' invalid in token family\n");
255 0 : GNUNET_break_op (0);
256 0 : return GNUNET_SYSERR;
257 : }
258 :
259 :
260 : /**
261 : * Provide specification to parse given JSON object to contract token details
262 : * in the contract token family. All fields from @a family are copied.
263 : *
264 : * @param name name of the token details field in the JSON
265 : * @param[out] family token_family where the token details have to be written
266 : */
267 : static struct GNUNET_JSON_Specification
268 11 : spec_token_details (const char *name,
269 : struct TALER_MERCHANT_ContractTokenFamily *family)
270 : {
271 11 : struct GNUNET_JSON_Specification ret = {
272 : .parser = &parse_token_details,
273 : .field = name,
274 : .ptr = family,
275 : };
276 :
277 11 : return ret;
278 : }
279 :
280 :
281 : /**
282 : * Parse given JSON object to token families array.
283 : *
284 : * @param cls closure, pointer to array length
285 : * @param root the json object representing the token families. The keys are
286 : * the token family slugs.
287 : * @param[out] ospec where to write the data
288 : * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error
289 : */
290 : static enum GNUNET_GenericReturnValue
291 27 : parse_token_families (void *cls,
292 : json_t *root,
293 : struct GNUNET_JSON_Specification *ospec)
294 : {
295 27 : struct TALER_MERCHANT_ContractTokenFamily **families = ospec->ptr;
296 27 : unsigned int *families_len = cls;
297 : json_t *jfamily;
298 : const char *slug;
299 :
300 27 : if (! json_is_object (root))
301 : {
302 0 : GNUNET_break_op (0);
303 0 : return GNUNET_SYSERR;
304 : }
305 :
306 38 : json_object_foreach (root, slug, jfamily)
307 : {
308 : const json_t *keys;
309 22 : struct TALER_MERCHANT_ContractTokenFamily family = {
310 11 : .slug = GNUNET_strdup (slug)
311 : };
312 : struct GNUNET_JSON_Specification spec[] = {
313 11 : GNUNET_JSON_spec_string_copy ("name",
314 : &family.name),
315 11 : GNUNET_JSON_spec_string_copy ("description",
316 : &family.description),
317 11 : GNUNET_JSON_spec_mark_optional (
318 : GNUNET_JSON_spec_object_copy ("description_i18n",
319 : &family.description_i18n),
320 : NULL),
321 11 : GNUNET_JSON_spec_array_const ("keys",
322 : &keys),
323 11 : spec_token_details ("details",
324 : &family),
325 11 : GNUNET_JSON_spec_bool ("critical",
326 : &family.critical),
327 11 : GNUNET_JSON_spec_end ()
328 : };
329 : const char *error_name;
330 : unsigned int error_line;
331 :
332 11 : if (GNUNET_OK !=
333 11 : GNUNET_JSON_parse (jfamily,
334 : spec,
335 : &error_name,
336 : &error_line))
337 : {
338 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
339 : "Failed to parse %s at %u: %s\n",
340 : spec[error_line].field,
341 : error_line,
342 : error_name);
343 0 : GNUNET_break_op (0);
344 0 : TALER_MERCHANT_contract_token_family_free (&family);
345 0 : return GNUNET_SYSERR;
346 : }
347 :
348 11 : GNUNET_array_grow (family.keys,
349 : family.keys_len,
350 : json_array_size (keys));
351 :
352 21 : for (unsigned int i = 0; i<family.keys_len; i++)
353 : {
354 10 : struct TALER_MERCHANT_ContractTokenFamilyKey *key = &family.keys[i];
355 : struct GNUNET_JSON_Specification key_spec[] = {
356 10 : TALER_JSON_spec_token_pub (
357 : NULL,
358 : &key->pub),
359 10 : GNUNET_JSON_spec_timestamp (
360 : "signature_validity_start",
361 : &key->valid_after),
362 10 : GNUNET_JSON_spec_timestamp (
363 : "signature_validity_end",
364 : &key->valid_before),
365 10 : GNUNET_JSON_spec_end ()
366 : };
367 : const char *ierror_name;
368 : unsigned int ierror_line;
369 :
370 10 : if (GNUNET_OK !=
371 10 : GNUNET_JSON_parse (json_array_get (keys,
372 : i),
373 : key_spec,
374 : &ierror_name,
375 : &ierror_line))
376 : {
377 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
378 : "Failed to parse %s at %u: %s\n",
379 : key_spec[ierror_line].field,
380 : ierror_line,
381 : ierror_name);
382 0 : GNUNET_break_op (0);
383 0 : TALER_MERCHANT_contract_token_family_free (&family);
384 0 : return GNUNET_SYSERR;
385 : }
386 : }
387 :
388 11 : GNUNET_array_append (*families,
389 : *families_len,
390 : family);
391 : }
392 :
393 27 : return GNUNET_OK;
394 : }
395 :
396 :
397 : struct GNUNET_JSON_Specification
398 27 : TALER_MERCHANT_spec_token_families (
399 : const char *name,
400 : struct TALER_MERCHANT_ContractTokenFamily **families,
401 : unsigned int *families_len)
402 : {
403 27 : struct GNUNET_JSON_Specification ret = {
404 : .cls = (void *) families_len,
405 : .parser = &parse_token_families,
406 : .field = name,
407 : .ptr = families,
408 : };
409 :
410 27 : return ret;
411 : }
|