order_parse.c (11419B)
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_parse.c 18 * @brief shared logic for order 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 * Maximum number of entries we accept in the @e products array of an 35 * order. The array is allocated from its JSON length before any entry 36 * is parsed, and phase_merge_inventory() of POST /private/orders puts 37 * @e products_len + 1 words on the stack, so the length must be bounded 38 * independently of the maximum upload size. Same limit as the 39 * MAX_PRODUCTS that bounds the inventory_products of the same request. 40 */ 41 #define MAX_PRODUCTS 1024 42 43 44 /** 45 * Parse v0-specific fields of @a input JSON into @a order. 46 * 47 * @param[in] input the JSON order terms 48 * @param[out] order where to write the data 49 * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error 50 */ 51 static enum GNUNET_GenericReturnValue 52 parse_order_v0 ( 53 json_t *input, 54 struct TALER_MERCHANT_Order *order) 55 { 56 struct GNUNET_JSON_Specification espec[] = { 57 TALER_JSON_spec_amount_any ("amount", 58 &order->details.v0.brutto), 59 GNUNET_JSON_spec_mark_optional ( 60 TALER_JSON_spec_amount_any ("tip", 61 &order->details.v0.tip), 62 &order->details.v0.no_tip), 63 GNUNET_JSON_spec_mark_optional ( 64 TALER_JSON_spec_amount_any ("max_fee", 65 &order->details.v0.max_fee), 66 &order->details.v0.no_max_fee), 67 GNUNET_JSON_spec_end () 68 }; 69 enum GNUNET_GenericReturnValue res; 70 const char *ename; 71 unsigned int eline; 72 73 res = GNUNET_JSON_parse (input, 74 espec, 75 &ename, 76 &eline); 77 if (GNUNET_OK != res) 78 { 79 GNUNET_break (0); 80 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 81 "Failed to parse order v0 at field %s\n", 82 ename); 83 return GNUNET_SYSERR; 84 } 85 86 if ( (! order->details.v0.no_max_fee) && 87 (GNUNET_OK != 88 TALER_amount_cmp_currency (&order->details.v0.max_fee, 89 &order->details.v0.brutto)) ) 90 { 91 GNUNET_break (0); 92 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 93 "'max_fee' does not match currency of order price"); 94 return GNUNET_SYSERR; 95 } 96 if ( (! order->details.v0.no_tip) && 97 (GNUNET_OK != 98 TALER_amount_cmp_currency (&order->details.v0.tip, 99 &order->details.v0.brutto)) ) 100 { 101 GNUNET_break (0); 102 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 103 "'tip' does not match currency of order price"); 104 return GNUNET_SYSERR; 105 } 106 if (NULL != order->base->amount_external) 107 { 108 if (! TALER_MERCHANT_amount_external_valid ( 109 order->base->amount_external)) 110 { 111 GNUNET_break_op (0); 112 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 113 "'amount_external' is not a valid set of external payments"); 114 return GNUNET_SYSERR; 115 } 116 if (! TALER_MERCHANT_amount_external_currency_valid ( 117 order->base->amount_external, 118 &order->details.v0.brutto)) 119 { 120 GNUNET_break_op (0); 121 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 122 "'amount_external' does not match currency of order price"); 123 return GNUNET_SYSERR; 124 } 125 if (! TALER_MERCHANT_amount_external_total_valid ( 126 order->base->amount_external, 127 &order->details.v0.brutto)) 128 { 129 GNUNET_break_op (0); 130 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 131 "total of 'amount_external' and order price is too large"); 132 return GNUNET_SYSERR; 133 } 134 } 135 136 return res; 137 } 138 139 140 /** 141 * Parse v1-specific fields of @a input JSON into @a order. 142 * 143 * @param[in] input the JSON order terms 144 * @param[out] order where to write the data 145 * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error 146 */ 147 static enum GNUNET_GenericReturnValue 148 parse_order_v1 ( 149 json_t *input, 150 struct TALER_MERCHANT_Order *order) 151 { 152 struct GNUNET_JSON_Specification espec[] = { 153 TALER_MERCHANT_spec_order_choices ( 154 "choices", 155 &order->details.v1.choices, 156 &order->details.v1.choices_len), 157 GNUNET_JSON_spec_end () 158 }; 159 160 enum GNUNET_GenericReturnValue res; 161 const char *ename; 162 unsigned int eline; 163 164 res = GNUNET_JSON_parse (input, 165 espec, 166 &ename, 167 &eline); 168 if (GNUNET_OK != res) 169 { 170 GNUNET_break (0); 171 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 172 "Failed to parse order v1 at field %s\n", 173 ename); 174 return GNUNET_SYSERR; 175 } 176 if (NULL != order->base->amount_external) 177 { 178 if (! TALER_MERCHANT_amount_external_valid (order->base->amount_external)) 179 { 180 GNUNET_break_op (0); 181 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 182 "'amount_external' is not a valid set of external payments\n"); 183 return GNUNET_SYSERR; 184 } 185 for (unsigned int i = 0; i<order->details.v1.choices_len; i++) 186 { 187 if (! TALER_MERCHANT_amount_external_currency_valid ( 188 order->base->amount_external, 189 &order->details.v1.choices[i].amount)) 190 { 191 GNUNET_break_op (0); 192 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 193 "'amount_external' does not match currency of choice #%u\n", 194 i); 195 return GNUNET_SYSERR; 196 } 197 if (! TALER_MERCHANT_amount_external_total_valid ( 198 order->base->amount_external, 199 &order->details.v1.choices[i].amount)) 200 { 201 GNUNET_break_op (0); 202 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 203 "total of 'amount_external' and choice #%u is too large\n", 204 i); 205 return GNUNET_SYSERR; 206 } 207 } 208 } 209 210 return res; 211 } 212 213 214 struct TALER_MERCHANT_Order * 215 TALER_MERCHANT_order_parse (json_t *input) 216 { 217 struct TALER_MERCHANT_Order *order 218 = GNUNET_new (struct TALER_MERCHANT_Order); 219 enum GNUNET_GenericReturnValue res 220 = GNUNET_SYSERR; 221 222 GNUNET_assert (NULL != input); 223 order->base = TALER_MERCHANT_base_terms_parse (input); 224 if (NULL == order->base) 225 { 226 GNUNET_break (0); 227 GNUNET_free (order); 228 return NULL; 229 } 230 order->refund_deadline = GNUNET_TIME_UNIT_FOREVER_TS; 231 order->wire_transfer_deadline = GNUNET_TIME_UNIT_FOREVER_TS; 232 { 233 const json_t *products; 234 struct GNUNET_JSON_Specification espec[] = { 235 GNUNET_JSON_spec_mark_optional ( 236 GNUNET_JSON_spec_array_const ("products", 237 &products), 238 NULL), 239 GNUNET_JSON_spec_mark_optional ( 240 TALER_JSON_spec_slug_copy ("order_id", 241 &order->order_id), 242 NULL), 243 GNUNET_JSON_spec_mark_optional ( 244 GNUNET_JSON_spec_timestamp ("timestamp", 245 &order->timestamp), 246 NULL), 247 GNUNET_JSON_spec_mark_optional ( 248 GNUNET_JSON_spec_timestamp ("refund_deadline", 249 &order->refund_deadline), 250 NULL), 251 GNUNET_JSON_spec_mark_optional ( 252 GNUNET_JSON_spec_timestamp ("pay_deadline", 253 &order->pay_deadline), 254 NULL), 255 GNUNET_JSON_spec_mark_optional ( 256 GNUNET_JSON_spec_timestamp ("wire_transfer_deadline", 257 &order->wire_transfer_deadline), 258 NULL), 259 GNUNET_JSON_spec_end () 260 }; 261 const char *ename; 262 unsigned int eline; 263 264 res = GNUNET_JSON_parse (input, 265 espec, 266 &ename, 267 &eline); 268 if (GNUNET_OK != res) 269 { 270 GNUNET_break_op (0); 271 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 272 "Failed to parse proto contract at field %s\n", 273 ename); 274 goto cleanup; 275 } 276 if (NULL != products) 277 { 278 order->products_len = json_array_size (products); 279 if (MAX_PRODUCTS < order->products_len) 280 { 281 /* 'products' is untrusted input; reject rather than abort in 282 GNUNET_xmalloc_() (which asserts above 40 MiB). */ 283 GNUNET_break_op (0); 284 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 285 "Too many products in order (%u, at most %u allowed)\n", 286 (unsigned int) order->products_len, 287 (unsigned int) MAX_PRODUCTS); 288 goto cleanup; 289 } 290 if (0 != order->products_len) 291 { 292 size_t i; 293 json_t *p; 294 295 order->products = GNUNET_new_array ( 296 order->products_len, 297 struct TALER_MERCHANT_ProductSold); 298 json_array_foreach (products, i, p) 299 { 300 if (GNUNET_OK != 301 TALER_MERCHANT_parse_product_sold (p, 302 &order->products[i], 303 true)) 304 { 305 GNUNET_break (0); 306 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 307 "Failed to parse product at offset %u\n", 308 (unsigned int) i); 309 goto cleanup; 310 } 311 } 312 } 313 } 314 } 315 switch (order->base->version) 316 { 317 case TALER_MERCHANT_CONTRACT_VERSION_0: 318 res = parse_order_v0 (input, 319 order); 320 break; 321 case TALER_MERCHANT_CONTRACT_VERSION_1: 322 res = parse_order_v1 (input, 323 order); 324 break; 325 } 326 327 if (GNUNET_OK != res) 328 { 329 GNUNET_break (0); 330 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 331 "Failed to parse order\n"); 332 goto cleanup; 333 } 334 return order; 335 336 cleanup: 337 TALER_MERCHANT_order_free (order); 338 return NULL; 339 } 340 341 342 void 343 TALER_MERCHANT_order_free ( 344 struct TALER_MERCHANT_Order *order) 345 { 346 if (NULL == order) 347 return; 348 if (NULL != order->products) 349 { 350 for (size_t i = 0; i<order->products_len; i++) 351 TALER_MERCHANT_product_sold_free (&order->products[i]); 352 GNUNET_free (order->products); 353 order->products_len = 0; 354 } 355 GNUNET_free (order->order_id); 356 if (NULL != order->base) 357 { 358 switch (order->base->version) 359 { 360 case TALER_MERCHANT_CONTRACT_VERSION_0: 361 break; 362 case TALER_MERCHANT_CONTRACT_VERSION_1: 363 for (unsigned int i = 0; 364 i < order->details.v1.choices_len; 365 i++) 366 TALER_MERCHANT_order_choice_free (&order->details.v1.choices[i]); 367 GNUNET_free (order->details.v1.choices); 368 break; 369 } 370 TALER_MERCHANT_base_terms_free (order->base); 371 order->base = NULL; 372 } 373 GNUNET_free (order); 374 }