token_family_parse.c (12436B)
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 GNUNET_array_grow (family->details.subscription.trusted_domains, 181 family->details.subscription.trusted_domains_len, 182 json_array_size (trusted_domains)); 183 184 for (unsigned int i = 0; 185 i < family->details.subscription.trusted_domains_len; 186 i++) 187 { 188 const json_t *jdomain; 189 jdomain = json_array_get (trusted_domains, i); 190 191 if (! json_is_string (jdomain)) 192 { 193 GNUNET_break_op (0); 194 return GNUNET_SYSERR; 195 } 196 197 family->details.subscription.trusted_domains[i] = 198 GNUNET_strdup (json_string_value (jdomain)); 199 } 200 201 return GNUNET_OK; 202 } 203 case TALER_MERCHANT_CONTRACT_TOKEN_KIND_DISCOUNT: 204 { 205 const json_t *expected_domains; 206 struct GNUNET_JSON_Specification spec[] = { 207 GNUNET_JSON_spec_array_const ("expected_domains", 208 &expected_domains), 209 GNUNET_JSON_spec_end () 210 }; 211 212 if (GNUNET_OK != 213 GNUNET_JSON_parse (root, 214 spec, 215 &ename, 216 &eline)) 217 { 218 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 219 "Failed to parse %s at %u: %s\n", 220 spec[eline].field, 221 eline, 222 ename); 223 GNUNET_break_op (0); 224 return GNUNET_SYSERR; 225 } 226 227 GNUNET_array_grow (family->details.discount.expected_domains, 228 family->details.discount.expected_domains_len, 229 json_array_size (expected_domains)); 230 231 for (unsigned int i = 0; 232 i < family->details.discount.expected_domains_len; 233 i++) 234 { 235 const json_t *jdomain; 236 237 jdomain = json_array_get (expected_domains, 238 i); 239 if (! json_is_string (jdomain)) 240 { 241 GNUNET_break_op (0); 242 return GNUNET_SYSERR; 243 } 244 245 family->details.discount.expected_domains[i] = 246 GNUNET_strdup (json_string_value (jdomain)); 247 } 248 249 return GNUNET_OK; 250 } 251 } 252 253 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 254 "Field 'type' invalid in token family\n"); 255 GNUNET_break_op (0); 256 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 spec_token_details (const char *name, 269 struct TALER_MERCHANT_ContractTokenFamily *family) 270 { 271 struct GNUNET_JSON_Specification ret = { 272 .parser = &parse_token_details, 273 .field = name, 274 .ptr = family, 275 }; 276 277 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 parse_token_families (void *cls, 292 json_t *root, 293 struct GNUNET_JSON_Specification *ospec) 294 { 295 struct TALER_MERCHANT_ContractTokenFamily **families = ospec->ptr; 296 unsigned int *families_len = cls; 297 json_t *jfamily; 298 const char *slug; 299 300 if (! json_is_object (root)) 301 { 302 GNUNET_break_op (0); 303 return GNUNET_SYSERR; 304 } 305 306 json_object_foreach (root, slug, jfamily) 307 { 308 const json_t *keys; 309 struct TALER_MERCHANT_ContractTokenFamily family = { 310 .slug = GNUNET_strdup (slug) 311 }; 312 struct GNUNET_JSON_Specification spec[] = { 313 GNUNET_JSON_spec_string_copy ("name", 314 &family.name), 315 GNUNET_JSON_spec_string_copy ("description", 316 &family.description), 317 GNUNET_JSON_spec_mark_optional ( 318 GNUNET_JSON_spec_object_copy ("description_i18n", 319 &family.description_i18n), 320 NULL), 321 GNUNET_JSON_spec_array_const ("keys", 322 &keys), 323 spec_token_details ("details", 324 &family), 325 GNUNET_JSON_spec_bool ("critical", 326 &family.critical), 327 GNUNET_JSON_spec_end () 328 }; 329 const char *error_name; 330 unsigned int error_line; 331 332 if (GNUNET_OK != 333 GNUNET_JSON_parse (jfamily, 334 spec, 335 &error_name, 336 &error_line)) 337 { 338 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 GNUNET_break_op (0); 344 TALER_MERCHANT_contract_token_family_free (&family); 345 return GNUNET_SYSERR; 346 } 347 348 GNUNET_array_grow (family.keys, 349 family.keys_len, 350 json_array_size (keys)); 351 352 for (unsigned int i = 0; i<family.keys_len; i++) 353 { 354 struct TALER_MERCHANT_ContractTokenFamilyKey *key = &family.keys[i]; 355 struct GNUNET_JSON_Specification key_spec[] = { 356 TALER_JSON_spec_token_pub ( 357 NULL, 358 &key->pub), 359 GNUNET_JSON_spec_timestamp ( 360 "signature_validity_start", 361 &key->valid_after), 362 GNUNET_JSON_spec_timestamp ( 363 "signature_validity_end", 364 &key->valid_before), 365 GNUNET_JSON_spec_end () 366 }; 367 const char *ierror_name; 368 unsigned int ierror_line; 369 370 if (GNUNET_OK != 371 GNUNET_JSON_parse (json_array_get (keys, 372 i), 373 key_spec, 374 &ierror_name, 375 &ierror_line)) 376 { 377 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 GNUNET_break_op (0); 383 TALER_MERCHANT_contract_token_family_free (&family); 384 return GNUNET_SYSERR; 385 } 386 } 387 388 GNUNET_array_append (*families, 389 *families_len, 390 family); 391 } 392 393 return GNUNET_OK; 394 } 395 396 397 struct GNUNET_JSON_Specification 398 TALER_MERCHANT_spec_token_families ( 399 const char *name, 400 struct TALER_MERCHANT_ContractTokenFamily **families, 401 unsigned int *families_len) 402 { 403 struct GNUNET_JSON_Specification ret = { 404 .cls = (void *) families_len, 405 .parser = &parse_token_families, 406 .field = name, 407 .ptr = families, 408 }; 409 410 return ret; 411 }