order_choice_parse.c (13476B)
1 /* 2 This file is part of TALER 3 (C) 2024, 2025 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/order_choice_parse.c 18 * @brief shared logic for order choice parsing 19 * @author Iván Ávalos 20 * @author Christian Grothoff 21 */ 22 #include "platform.h" 23 #include <gnunet/gnunet_common.h> 24 #include <gnunet/gnunet_json_lib.h> 25 #include <jansson.h> 26 #include <stdbool.h> 27 #include <stdint.h> 28 #include <taler/taler_json_lib.h> 29 #include <taler/taler_util.h> 30 #include "taler/taler_merchant_util.h" 31 32 33 /** 34 * Parse JSON order choice input. 35 * 36 * @param[in] root JSON object containing choice input 37 * @param[out] input parsed choice input, NULL if @a input is malformed 38 * @param index index of choice input in inputs array 39 * @return #GNUNET_SYSERR if @a input is malformed; #GNUNET_OK otherwise 40 */ 41 static enum GNUNET_GenericReturnValue 42 parse_order_choice_input ( 43 json_t *root, 44 struct TALER_MERCHANT_OrderInput *input, 45 size_t index) 46 { 47 const char *ename; 48 unsigned int eline; 49 struct GNUNET_JSON_Specification ispec[] = { 50 TALER_MERCHANT_json_spec_cit ("type", 51 &input->type), 52 GNUNET_JSON_spec_end () 53 }; 54 55 if (GNUNET_OK != 56 GNUNET_JSON_parse (root, 57 ispec, 58 &ename, 59 &eline)) 60 { 61 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 62 "Failed to parse %s at %u: %s\n", 63 ispec[eline].field, 64 eline, 65 ename); 66 GNUNET_break_op (0); 67 return GNUNET_SYSERR; 68 } 69 70 switch (input->type) 71 { 72 case TALER_MERCHANT_CONTRACT_INPUT_TYPE_INVALID: 73 GNUNET_break (0); 74 break; 75 case TALER_MERCHANT_CONTRACT_INPUT_TYPE_TOKEN: 76 { 77 struct GNUNET_JSON_Specification spec[] = { 78 GNUNET_JSON_spec_string ("token_family_slug", 79 &input->details.token.token_family_slug), 80 GNUNET_JSON_spec_mark_optional ( 81 GNUNET_JSON_spec_uint ("count", 82 &input->details.token.count), 83 NULL), 84 GNUNET_JSON_spec_end () 85 }; 86 87 if (GNUNET_OK != 88 GNUNET_JSON_parse (root, 89 spec, 90 &ename, 91 &eline)) 92 { 93 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 94 "Failed to parse %s at %u: %s\n", 95 spec[eline].field, 96 eline, 97 ename); 98 GNUNET_break_op (0); 99 return GNUNET_SYSERR; 100 } 101 102 return GNUNET_OK; 103 } 104 } 105 106 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 107 "Field 'type' invalid in input #%u\n", 108 (unsigned int) index); 109 GNUNET_break_op (0); 110 return GNUNET_SYSERR; 111 } 112 113 114 /** 115 * Parse JSON order choice output. 116 * 117 * @param[in] root JSON object containing choice output 118 * @param[out] output parsed choice output, NULL if @a output is malformed 119 * @param index index of choice output in outputs array 120 * @return #GNUNET_SYSERR if @a output is malformed; #GNUNET_OK otherwise 121 */ 122 static enum GNUNET_GenericReturnValue 123 parse_order_choice_output ( 124 json_t *root, 125 struct TALER_MERCHANT_OrderOutput *output, 126 size_t index) 127 { 128 const char *ename; 129 unsigned int eline; 130 struct GNUNET_JSON_Specification ispec[] = { 131 TALER_MERCHANT_json_spec_cot ("type", 132 &output->type), 133 GNUNET_JSON_spec_end () 134 }; 135 136 if (GNUNET_OK != 137 GNUNET_JSON_parse (root, 138 ispec, 139 &ename, 140 &eline)) 141 { 142 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 143 "Failed to parse %s at %u: %s\n", 144 ispec[eline].field, 145 eline, 146 ename); 147 GNUNET_break_op (0); 148 return GNUNET_SYSERR; 149 } 150 151 switch (output->type) 152 { 153 case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_INVALID: 154 GNUNET_break (0); 155 break; 156 case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_TOKEN: 157 { 158 struct GNUNET_JSON_Specification spec[] = { 159 GNUNET_JSON_spec_string ("token_family_slug", 160 &output->details.token.token_family_slug), 161 GNUNET_JSON_spec_mark_optional ( 162 GNUNET_JSON_spec_uint ("count", 163 &output->details.token.count), 164 NULL), 165 GNUNET_JSON_spec_mark_optional ( 166 GNUNET_JSON_spec_timestamp ("valid_at", 167 &output->details.token.valid_at), 168 NULL), 169 GNUNET_JSON_spec_end () 170 }; 171 172 if (GNUNET_OK != 173 GNUNET_JSON_parse (root, 174 spec, 175 &ename, 176 &eline)) 177 { 178 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 179 "Failed to parse %s at %u: %s\n", 180 spec[eline].field, 181 eline, 182 ename); 183 GNUNET_break_op (0); 184 return GNUNET_SYSERR; 185 } 186 187 return GNUNET_OK; 188 } 189 case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_DONATION_RECEIPT: 190 { 191 struct GNUNET_JSON_Specification spec[] = { 192 GNUNET_JSON_spec_mark_optional ( 193 TALER_JSON_spec_amount_any ("amount", 194 &output->details.donation_receipt.amount), 195 &output->details.donation_receipt.no_amount), 196 GNUNET_JSON_spec_end () 197 }; 198 199 if (GNUNET_OK != 200 GNUNET_JSON_parse (root, 201 spec, 202 &ename, 203 &eline)) 204 { 205 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 206 "Failed to parse %s at %u: %s\n", 207 spec[eline].field, 208 eline, 209 ename); 210 GNUNET_break_op (0); 211 return GNUNET_SYSERR; 212 } 213 return GNUNET_OK; 214 } 215 } 216 217 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 218 "Field 'type' invalid in output #%u\n", 219 (unsigned int) index); 220 GNUNET_break_op (0); 221 return GNUNET_SYSERR; 222 } 223 224 225 /** 226 * Parse given JSON object to choices array. 227 * 228 * @param cls closure, pointer to array length 229 * @param root the json array representing the choices 230 * @param[out] ospec where to write the data 231 * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error 232 */ 233 static enum GNUNET_GenericReturnValue 234 parse_order_choices ( 235 void *cls, 236 json_t *root, 237 struct GNUNET_JSON_Specification *ospec) 238 { 239 struct TALER_MERCHANT_OrderChoice **choices = ospec->ptr; 240 unsigned int *choices_len = cls; 241 242 if (! json_is_array (root)) 243 { 244 GNUNET_break_op (0); 245 return GNUNET_SYSERR; 246 } 247 if (0 == json_array_size (root)) 248 { 249 /* empty list of choices is not allowed */ 250 GNUNET_break_op (0); 251 return GNUNET_SYSERR; 252 } 253 *choices = NULL; 254 *choices_len = 0; 255 GNUNET_array_grow (*choices, 256 *choices_len, 257 json_array_size (root)); 258 259 for (unsigned int i = 0; i < *choices_len; i++) 260 { 261 struct TALER_MERCHANT_OrderChoice *choice = &(*choices)[i]; 262 const json_t *jinputs = NULL; 263 const json_t *joutputs = NULL; 264 struct GNUNET_JSON_Specification spec[] = { 265 TALER_JSON_spec_amount_any ("amount", 266 &choice->amount), 267 GNUNET_JSON_spec_mark_optional ( 268 TALER_JSON_spec_amount_any ("tip", 269 &choice->tip), 270 &choice->no_tip), 271 GNUNET_JSON_spec_mark_optional ( 272 GNUNET_JSON_spec_string_copy ("description", 273 &choice->description), 274 NULL), 275 GNUNET_JSON_spec_mark_optional ( 276 GNUNET_JSON_spec_object_copy ("description_i18n", 277 &choice->description_i18n), 278 NULL), 279 GNUNET_JSON_spec_mark_optional ( 280 TALER_JSON_spec_amount_any ("max_fee", 281 &choice->max_fee), 282 &choice->no_max_fee), 283 GNUNET_JSON_spec_mark_optional ( 284 GNUNET_JSON_spec_array_const ("inputs", 285 &jinputs), 286 NULL), 287 GNUNET_JSON_spec_mark_optional ( 288 GNUNET_JSON_spec_array_const ("outputs", 289 &joutputs), 290 NULL), 291 GNUNET_JSON_spec_end () 292 }; 293 const char *ename; 294 unsigned int eline; 295 296 if (GNUNET_OK != 297 GNUNET_JSON_parse (json_array_get (root, i), 298 spec, 299 &ename, 300 &eline)) 301 { 302 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 303 "Failed to parse %s at %u: %s\n", 304 spec[eline].field, 305 eline, 306 ename); 307 GNUNET_break_op (0); 308 return GNUNET_SYSERR; 309 } 310 if ( (! choice->no_max_fee) && 311 (GNUNET_OK != 312 TALER_amount_cmp_currency (&choice->amount, 313 &choice->max_fee)) ) 314 { 315 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 316 "Fee currency does not match amount currency in choice #%u\n", 317 i); 318 GNUNET_break_op (0); 319 return GNUNET_SYSERR; 320 } 321 if ( (! choice->no_tip) && 322 (GNUNET_OK != 323 TALER_amount_cmp_currency (&choice->amount, 324 &choice->tip)) ) 325 { 326 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 327 "Tip currency does not match amount currency in choice #%u\n", 328 i); 329 GNUNET_break_op (0); 330 return GNUNET_SYSERR; 331 } 332 333 if (NULL != jinputs) 334 { 335 const json_t *jinput; 336 size_t idx; 337 338 json_array_foreach ((json_t *) jinputs, idx, jinput) 339 { 340 struct TALER_MERCHANT_OrderInput input = { 341 .details.token.count = 1 342 }; 343 344 if (GNUNET_OK != 345 parse_order_choice_input ((json_t *) jinput, 346 &input, 347 idx)) 348 { 349 GNUNET_break (0); 350 return GNUNET_SYSERR; 351 } 352 switch (input.type) 353 { 354 case TALER_MERCHANT_CONTRACT_INPUT_TYPE_INVALID: 355 GNUNET_break_op (0); 356 return GNUNET_SYSERR; 357 case TALER_MERCHANT_CONTRACT_INPUT_TYPE_TOKEN: 358 /* Ignore inputs tokens with 'count' field set to 0 */ 359 if (0 == input.details.token.count) 360 continue; 361 break; 362 } 363 GNUNET_array_append (choice->inputs, 364 choice->inputs_len, 365 input); 366 } 367 } 368 369 if (NULL != joutputs) 370 { 371 const json_t *joutput; 372 size_t idx; 373 json_array_foreach ((json_t *) joutputs, idx, joutput) 374 { 375 struct TALER_MERCHANT_OrderOutput output = { 376 .details.token.count = 1 377 }; 378 379 if (GNUNET_OK != 380 parse_order_choice_output ((json_t *) joutput, 381 &output, 382 idx)) 383 { 384 GNUNET_break (0); 385 return GNUNET_SYSERR; 386 } 387 switch (output.type) 388 { 389 case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_INVALID: 390 GNUNET_break_op (0); 391 return GNUNET_SYSERR; 392 case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_TOKEN: 393 /* Ignore output tokens with 'count' field set to 0 */ 394 if (0 == output.details.token.count) 395 continue; 396 break; 397 case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_DONATION_RECEIPT: 398 break; 399 } 400 GNUNET_array_append (choice->outputs, 401 choice->outputs_len, 402 output); 403 } 404 } 405 } 406 407 return GNUNET_OK; 408 } 409 410 411 struct GNUNET_JSON_Specification 412 TALER_MERCHANT_spec_order_choices ( 413 const char *name, 414 struct TALER_MERCHANT_OrderChoice **choices, 415 unsigned int *choices_len) 416 { 417 struct GNUNET_JSON_Specification ret = { 418 .cls = (void *) choices_len, 419 .parser = &parse_order_choices, 420 .field = name, 421 .ptr = choices, 422 }; 423 424 return ret; 425 } 426 427 428 void 429 TALER_MERCHANT_order_choice_free ( 430 struct TALER_MERCHANT_OrderChoice *choice) 431 { 432 for (unsigned int i = 0; i < choice->outputs_len; i++) 433 { 434 struct TALER_MERCHANT_OrderOutput *output = &choice->outputs[i]; 435 436 switch (output->type) 437 { 438 case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_INVALID: 439 GNUNET_break (0); 440 break; 441 case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_TOKEN: 442 break; 443 case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_DONATION_RECEIPT: 444 break; 445 #if FUTURE 446 case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_COIN: 447 GNUNET_free (output->details.coin.exchange_url); 448 break; 449 #endif 450 } 451 } 452 GNUNET_free (choice->description); 453 if (NULL != choice->description_i18n) 454 { 455 json_decref (choice->description_i18n); 456 choice->description_i18n = NULL; 457 } 458 GNUNET_free (choice->inputs); 459 GNUNET_free (choice->outputs); 460 }