contract_parse.c (10473B)
1 /* 2 This file is part of TALER 3 (C) 2024, 2025, 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/contract_parse.c 18 * @brief shared logic for contract terms 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 v0-specific fields of @a input JSON into @a contract. 35 * 36 * @param[in] input the JSON contract terms 37 * @param[out] contract where to write the data 38 * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error 39 */ 40 static enum GNUNET_GenericReturnValue 41 parse_contract_v0 ( 42 json_t *input, 43 struct TALER_MERCHANT_ProtoContract *contract) 44 { 45 struct GNUNET_JSON_Specification espec[] = { 46 TALER_JSON_spec_amount_any ("amount", 47 &contract->details.v0.brutto), 48 GNUNET_JSON_spec_mark_optional ( 49 TALER_JSON_spec_amount_any ("tip", 50 &contract->details.v0.tip), 51 &contract->details.v0.no_tip), 52 TALER_JSON_spec_amount_any ("max_fee", 53 &contract->details.v0.max_fee), 54 GNUNET_JSON_spec_end () 55 }; 56 enum GNUNET_GenericReturnValue res; 57 const char *ename; 58 unsigned int eline; 59 60 res = GNUNET_JSON_parse (input, 61 espec, 62 &ename, 63 &eline); 64 if (GNUNET_OK != res) 65 { 66 GNUNET_break (0); 67 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 68 "Failed to parse contract v0 at field %s\n", 69 ename); 70 return GNUNET_SYSERR; 71 } 72 73 if (GNUNET_OK != 74 TALER_amount_cmp_currency (&contract->details.v0.max_fee, 75 &contract->details.v0.brutto)) 76 { 77 GNUNET_break (0); 78 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 79 "'max_fee' does not match currency of contract price"); 80 return GNUNET_SYSERR; 81 } 82 if ( (! contract->details.v0.no_tip) && 83 (GNUNET_OK != 84 TALER_amount_cmp_currency (&contract->details.v0.tip, 85 &contract->details.v0.brutto)) ) 86 { 87 GNUNET_break (0); 88 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 89 "'tip' does not match currency of contract price"); 90 return GNUNET_SYSERR; 91 } 92 93 return res; 94 } 95 96 97 /** 98 * Parse v1-specific fields of @a input JSON into @a pc. 99 * 100 * @param[in] input the JSON contract terms 101 * @param[out] pc where to write the data 102 * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error 103 */ 104 static enum GNUNET_GenericReturnValue 105 parse_contract_v1 ( 106 json_t *input, 107 struct TALER_MERCHANT_ProtoContract *pc) 108 { 109 struct GNUNET_JSON_Specification espec[] = { 110 TALER_MERCHANT_spec_contract_choices ( 111 "choices", 112 &pc->details.v1.choices, 113 &pc->details.v1.choices_len), 114 TALER_MERCHANT_spec_token_families ( 115 "token_families", 116 &pc->details.v1.token_authorities, 117 &pc->details.v1.token_authorities_len), 118 GNUNET_JSON_spec_end () 119 }; 120 121 enum GNUNET_GenericReturnValue res; 122 const char *ename; 123 unsigned int eline; 124 125 res = GNUNET_JSON_parse (input, 126 espec, 127 &ename, 128 &eline); 129 if (GNUNET_OK != res) 130 { 131 GNUNET_break (0); 132 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 133 "Failed to parse contract v1 at field %s\n", 134 ename); 135 return GNUNET_SYSERR; 136 } 137 138 return res; 139 } 140 141 142 struct TALER_MERCHANT_ProtoContract * 143 TALER_MERCHANT_proto_contract_parse ( 144 json_t *input) 145 { 146 struct TALER_MERCHANT_ContractBaseTerms *base; 147 struct TALER_MERCHANT_ProtoContract *pc; 148 enum GNUNET_GenericReturnValue res; 149 150 base = TALER_MERCHANT_base_terms_parse (input); 151 if (NULL == base) 152 { 153 GNUNET_break_op (0); 154 return NULL; 155 } 156 pc = GNUNET_new (struct TALER_MERCHANT_ProtoContract); 157 pc->base = base; 158 { 159 const json_t *products = NULL; 160 struct GNUNET_JSON_Specification espec[] = { 161 GNUNET_JSON_spec_string_copy ("order_id", 162 &pc->order_id), 163 GNUNET_JSON_spec_mark_optional ( 164 GNUNET_JSON_spec_array_const ("products", 165 &products), 166 NULL), 167 GNUNET_JSON_spec_timestamp ("timestamp", 168 &pc->timestamp), 169 GNUNET_JSON_spec_timestamp ("refund_deadline", 170 &pc->refund_deadline), 171 GNUNET_JSON_spec_timestamp ("pay_deadline", 172 &pc->pay_deadline), 173 GNUNET_JSON_spec_timestamp ("wire_transfer_deadline", 174 &pc->wire_deadline), 175 GNUNET_JSON_spec_fixed_auto ("merchant_pub", 176 &pc->merchant_pub), 177 GNUNET_JSON_spec_fixed_auto ("h_wire", 178 &pc->h_wire), 179 GNUNET_JSON_spec_string_copy ("merchant_base_url", 180 &pc->merchant_base_url), 181 TALER_MERCHANT_spec_merchant_details ("merchant", 182 &pc->merchant), 183 GNUNET_JSON_spec_string_copy ("wire_method", 184 &pc->wire_method), 185 GNUNET_JSON_spec_array_copy ("exchanges", 186 &pc->exchanges), 187 188 GNUNET_JSON_spec_end () 189 }; 190 const char *ename; 191 unsigned int eline; 192 193 res = GNUNET_JSON_parse (input, 194 espec, 195 &ename, 196 &eline); 197 if (GNUNET_OK != res) 198 { 199 GNUNET_break_op (0); 200 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 201 "Failed to parse proto contract at field %s\n", 202 ename); 203 goto cleanup; 204 } 205 if (NULL != products) 206 { 207 pc->products_len = json_array_size (products); 208 if (0 != pc->products_len) 209 { 210 size_t i; 211 json_t *p; 212 213 pc->products = GNUNET_new_array ( 214 pc->products_len, 215 struct TALER_MERCHANT_ProductSold); 216 json_array_foreach (products, i, p) 217 { 218 if (GNUNET_OK != 219 TALER_MERCHANT_parse_product_sold (p, 220 &pc->products[i], 221 false)) 222 { 223 GNUNET_break (0); 224 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 225 "Failed to parse product at offset %u\n", 226 (unsigned int) i); 227 goto cleanup; 228 } 229 } 230 } 231 } 232 } 233 switch (base->version) 234 { 235 case TALER_MERCHANT_CONTRACT_VERSION_0: 236 if (GNUNET_OK == 237 parse_contract_v0 (input, 238 pc)) 239 return pc; 240 GNUNET_break_op (0); 241 break; 242 case TALER_MERCHANT_CONTRACT_VERSION_1: 243 if (GNUNET_OK == 244 parse_contract_v1 (input, 245 pc)) 246 return pc; 247 GNUNET_break_op (0); 248 break; 249 } 250 cleanup: 251 TALER_MERCHANT_proto_contract_free (pc); 252 return NULL; 253 } 254 255 256 /** 257 * Free the proto-contract at @a pc 258 * 259 * @param[in] pc proto-contract to free 260 */ 261 void 262 TALER_MERCHANT_proto_contract_free ( 263 struct TALER_MERCHANT_ProtoContract *pc) 264 { 265 if (NULL != pc->products) 266 { 267 for (size_t i = 0; i<pc->products_len; i++) 268 TALER_MERCHANT_product_sold_free (&pc->products[i]); 269 GNUNET_free (pc->products); 270 } 271 if (NULL != pc->base) 272 { 273 switch (pc->base->version) 274 { 275 case TALER_MERCHANT_CONTRACT_VERSION_0: 276 break; 277 case TALER_MERCHANT_CONTRACT_VERSION_1: 278 for (unsigned int i = 0; 279 i < pc->details.v1.choices_len; 280 i++) 281 TALER_MERCHANT_contract_choice_free ( 282 &pc->details.v1.choices[i]); 283 GNUNET_free (pc->details.v1.choices); 284 for (unsigned int i = 0; 285 i < pc->details.v1.token_authorities_len; 286 i++) 287 TALER_MERCHANT_contract_token_family_free ( 288 &pc->details.v1.token_authorities[i]); 289 GNUNET_free (pc->details.v1.token_authorities); 290 break; 291 } 292 TALER_MERCHANT_base_terms_free (pc->base); 293 pc->base = NULL; 294 } 295 GNUNET_free (pc->wire_method); 296 GNUNET_free (pc->merchant_base_url); 297 TALER_MERCHANT_metadata_free (&pc->merchant); 298 if (NULL != pc->exchanges) 299 { 300 json_decref (pc->exchanges); 301 pc->exchanges = NULL; 302 } 303 GNUNET_free (pc->order_id); 304 GNUNET_free (pc); 305 } 306 307 308 struct TALER_MERCHANT_Contract * 309 TALER_MERCHANT_contract_parse (json_t *input) 310 { 311 struct TALER_MERCHANT_Contract *contract 312 = GNUNET_new (struct TALER_MERCHANT_Contract); 313 struct GNUNET_JSON_Specification espec[] = { 314 GNUNET_JSON_spec_string_copy ("nonce", 315 &contract->nonce), 316 GNUNET_JSON_spec_end () 317 }; 318 enum GNUNET_GenericReturnValue res; 319 const char *ename; 320 unsigned int eline; 321 322 GNUNET_assert (NULL != input); 323 contract->pc = TALER_MERCHANT_proto_contract_parse (input); 324 if (NULL == contract->pc) 325 { 326 GNUNET_break_op (0); 327 GNUNET_free (contract); 328 return NULL; 329 } 330 res = GNUNET_JSON_parse (input, 331 espec, 332 &ename, 333 &eline); 334 if (GNUNET_OK != res) 335 { 336 GNUNET_break_op (0); 337 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 338 "Failed to parse contract at field %s\n", 339 ename); 340 goto cleanup; 341 } 342 return contract; 343 344 cleanup: 345 TALER_MERCHANT_contract_free (contract); 346 return NULL; 347 } 348 349 350 void 351 TALER_MERCHANT_contract_free ( 352 struct TALER_MERCHANT_Contract *contract) 353 { 354 if (NULL == contract) 355 return; 356 if (NULL != contract->pc) 357 { 358 TALER_MERCHANT_proto_contract_free (contract->pc); 359 contract->pc = NULL; 360 } 361 GNUNET_free (contract->nonce); 362 GNUNET_free (contract); 363 }