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