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