Line data Source code
1 : /*
2 : This file is part of TALER
3 : (C) 2014-2025 Taler Systems SA
4 :
5 : TALER is free software; you can redistribute it and/or modify
6 : it under the terms of the GNU Affero General Public License as
7 : published by the Free Software Foundation; either version 3,
8 : or (at your option) any later version.
9 :
10 : TALER is distributed in the hope that it will be useful, but
11 : WITHOUT ANY WARRANTY; without even the implied warranty of
12 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 : GNU General Public License for more details.
14 :
15 : You should have received a copy of the GNU General Public
16 : License along with TALER; see the file COPYING. If not,
17 : see <http://www.gnu.org/licenses/>
18 : */
19 :
20 : /**
21 : * @file taler-merchant-httpd_private-post-orders.c
22 : * @brief the POST /orders handler
23 : * @author Christian Grothoff
24 : * @author Marcello Stanisci
25 : * @author Christian Blättler
26 : */
27 : #include "platform.h"
28 : #include <gnunet/gnunet_common.h>
29 : #include <gnunet/gnunet_db_lib.h>
30 : #include <gnunet/gnunet_json_lib.h>
31 : #include <gnunet/gnunet_time_lib.h>
32 : #include <jansson.h>
33 : #include <microhttpd.h>
34 : #include <string.h>
35 : #include <taler/taler_error_codes.h>
36 : #include <taler/taler_signatures.h>
37 : #include <taler/taler_json_lib.h>
38 : #include <taler/taler_dbevents.h>
39 : #include <taler/taler_util.h>
40 : #include <taler_merchant_util.h>
41 : #include <time.h>
42 : #include "taler-merchant-httpd.h"
43 : #include "taler-merchant-httpd_private-post-orders.h"
44 : #include "taler-merchant-httpd_exchanges.h"
45 : #include "taler-merchant-httpd_contract.h"
46 : #include "taler-merchant-httpd_helper.h"
47 : #include "taler-merchant-httpd_private-get-orders.h"
48 :
49 : #include "taler_merchantdb_plugin.h"
50 :
51 :
52 : /**
53 : * How often do we retry the simple INSERT database transaction?
54 : */
55 : #define MAX_RETRIES 3
56 :
57 : /**
58 : * Maximum number of inventory products per order.
59 : */
60 : #define MAX_PRODUCTS 1024
61 :
62 : /**
63 : * What is the label under which we find/place the merchant's
64 : * jurisdiction in the locations list by default?
65 : */
66 : #define STANDARD_LABEL_MERCHANT_JURISDICTION "_mj"
67 :
68 : /**
69 : * What is the label under which we find/place the merchant's
70 : * address in the locations list by default?
71 : */
72 : #define STANDARD_LABEL_MERCHANT_ADDRESS "_ma"
73 :
74 : /**
75 : * How long do we wait at most for /keys from the exchange(s)?
76 : * Ensures that we do not block forever just because some exchange
77 : * fails to respond *or* because our taler-merchant-keyscheck
78 : * refuses a forced download.
79 : */
80 : #define MAX_KEYS_WAIT \
81 : GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, 2500)
82 :
83 : /**
84 : * Generate the base URL for the given merchant instance.
85 : *
86 : * @param connection the MHD connection
87 : * @param instance_id the merchant instance ID
88 : * @returns the merchant instance's base URL
89 : */
90 : static char *
91 75 : make_merchant_base_url (struct MHD_Connection *connection,
92 : const char *instance_id)
93 : {
94 : struct GNUNET_Buffer buf;
95 :
96 75 : if (GNUNET_OK !=
97 75 : TMH_base_url_by_connection (connection,
98 : instance_id,
99 : &buf))
100 0 : return NULL;
101 75 : GNUNET_buffer_write_path (&buf,
102 : "");
103 75 : return GNUNET_buffer_reap_str (&buf);
104 : }
105 :
106 :
107 : /**
108 : * Information about a product we are supposed to add to the order
109 : * based on what we know it from our inventory.
110 : */
111 : struct InventoryProduct
112 : {
113 : /**
114 : * Identifier of the product in the inventory.
115 : */
116 : const char *product_id;
117 :
118 : /**
119 : * Number of units of the product to add to the order (integer part).
120 : */
121 : uint64_t quantity;
122 :
123 : /**
124 : * Fractional part of the quantity in units of 1/1000000 of the base value.
125 : */
126 : uint32_t quantity_frac;
127 :
128 : /**
129 : * True if the integer quantity field was missing in the request.
130 : */
131 : bool quantity_missing;
132 :
133 : /**
134 : * String representation of the quantity, if supplied.
135 : */
136 : const char *unit_quantity;
137 :
138 : /**
139 : * True if the string quantity field was missing in the request.
140 : */
141 : bool unit_quantity_missing;
142 :
143 : /**
144 : * Money pot associated with the product. 0 for none.
145 : */
146 : uint64_t product_money_pot;
147 :
148 : };
149 :
150 :
151 : /**
152 : * Handle for a rekey operation where we (re)request
153 : * the /keys from the exchange.
154 : */
155 : struct RekeyExchange
156 : {
157 : /**
158 : * Kept in a DLL.
159 : */
160 : struct RekeyExchange *prev;
161 :
162 : /**
163 : * Kept in a DLL.
164 : */
165 : struct RekeyExchange *next;
166 :
167 : /**
168 : * order this is for.
169 : */
170 : struct OrderContext *oc;
171 :
172 : /**
173 : * Base URL of the exchange.
174 : */
175 : char *url;
176 :
177 : /**
178 : * Request for keys.
179 : */
180 : struct TMH_EXCHANGES_KeysOperation *fo;
181 :
182 : };
183 :
184 :
185 : /**
186 : * Data structure where we evaluate the viability of a given
187 : * wire method for this order.
188 : */
189 : struct WireMethodCandidate
190 : {
191 : /**
192 : * Kept in a DLL.
193 : */
194 : struct WireMethodCandidate *next;
195 :
196 : /**
197 : * Kept in a DLL.
198 : */
199 : struct WireMethodCandidate *prev;
200 :
201 : /**
202 : * The wire method we are evaluating.
203 : */
204 : const struct TMH_WireMethod *wm;
205 :
206 : /**
207 : * List of exchanges to use when we use this wire method.
208 : */
209 : json_t *exchanges;
210 :
211 : /**
212 : * Array of maximum amounts that could be paid over all available exchanges
213 : * for this @a wm. Used to determine if this order creation requests exceeds
214 : * legal limits.
215 : */
216 : struct TALER_Amount *total_exchange_limits;
217 :
218 : /**
219 : * Length of the @e total_exchange_limits array.
220 : */
221 : unsigned int num_total_exchange_limits;
222 :
223 : };
224 :
225 :
226 : /**
227 : * Information we keep per order we are processing.
228 : */
229 : struct OrderContext
230 : {
231 : /**
232 : * Information set in the #ORDER_PHASE_PARSE_REQUEST phase.
233 : */
234 : struct
235 : {
236 : /**
237 : * Order field of the request
238 : */
239 : json_t *order;
240 :
241 : /**
242 : * Set to how long refunds will be allowed.
243 : */
244 : struct GNUNET_TIME_Relative refund_delay;
245 :
246 : /**
247 : * RFC8905 payment target type to find a matching merchant account
248 : */
249 : const char *payment_target;
250 :
251 : /**
252 : * Shared key to use with @e pos_algorithm.
253 : */
254 : char *pos_key;
255 :
256 : /**
257 : * Selected algorithm (by template) when we are to
258 : * generate an OTP code for payment confirmation.
259 : */
260 : enum TALER_MerchantConfirmationAlgorithm pos_algorithm;
261 :
262 : /**
263 : * Hash of the POST request data, used to detect
264 : * idempotent requests.
265 : */
266 : struct TALER_MerchantPostDataHashP h_post_data;
267 :
268 : /**
269 : * Length of the @e inventory_products array.
270 : */
271 : unsigned int inventory_products_length;
272 :
273 : /**
274 : * Specifies that some products are to be included in the
275 : * order from the inventory. For these inventory management
276 : * is performed (so the products must be in stock).
277 : */
278 : struct InventoryProduct *inventory_products;
279 :
280 : /**
281 : * Length of the @e uuids array.
282 : */
283 : unsigned int uuids_length;
284 :
285 : /**
286 : * array of UUIDs used to reserve products from @a inventory_products.
287 : */
288 : struct GNUNET_Uuid *uuids;
289 :
290 : /**
291 : * Claim token for the request.
292 : */
293 : struct TALER_ClaimTokenP claim_token;
294 :
295 : /**
296 : * Session ID (optional) to use for the order.
297 : */
298 : const char *session_id;
299 :
300 : } parse_request;
301 :
302 : /**
303 : * Information set in the #ORDER_PHASE_PARSE_ORDER phase.
304 : */
305 : struct
306 : {
307 :
308 : /**
309 : * Our order ID.
310 : */
311 : char *order_id;
312 :
313 : /**
314 : * Summary of the contract.
315 : */
316 : const char *summary;
317 :
318 : /**
319 : * Internationalized summary.
320 : */
321 : const json_t *summary_i18n;
322 :
323 : /**
324 : * URL that will show that the contract was successful
325 : * after it has been paid for.
326 : */
327 : const char *fulfillment_url;
328 :
329 : /**
330 : * Message shown to the customer after paying for the contract.
331 : * Either fulfillment_url or fulfillment_message must be specified.
332 : */
333 : const char *fulfillment_message;
334 :
335 : /**
336 : * Map from IETF BCP 47 language tags to localized fulfillment messages.
337 : */
338 : const json_t *fulfillment_message_i18n;
339 :
340 : /**
341 : * Length of the @e products array.
342 : */
343 : size_t products_len;
344 :
345 : /**
346 : * Array of products that are being sold.
347 : */
348 : struct TALER_MERCHANT_ProductSold *products;
349 :
350 : /**
351 : * URL where the same contract could be ordered again (if available).
352 : */
353 : const char *public_reorder_url;
354 :
355 : /**
356 : * Merchant base URL.
357 : */
358 : char *merchant_base_url;
359 :
360 : /**
361 : * Timestamp of the order.
362 : */
363 : struct GNUNET_TIME_Timestamp timestamp;
364 :
365 : /**
366 : * Deadline for refunds.
367 : */
368 : struct GNUNET_TIME_Timestamp refund_deadline;
369 :
370 : /**
371 : * Payment deadline.
372 : */
373 : struct GNUNET_TIME_Timestamp pay_deadline;
374 :
375 : /**
376 : * Wire transfer deadline.
377 : */
378 : struct GNUNET_TIME_Timestamp wire_deadline;
379 :
380 : /**
381 : * Wire transfer round-up interval to apply.
382 : */
383 : enum GNUNET_TIME_RounderInterval wire_deadline_rounder;
384 :
385 : /**
386 : * Delivery date.
387 : */
388 : struct GNUNET_TIME_Timestamp delivery_date;
389 :
390 : /**
391 : * Delivery location.
392 : */
393 : const json_t *delivery_location;
394 :
395 : /**
396 : * Specifies for how long the wallet should try to get an
397 : * automatic refund for the purchase.
398 : */
399 : struct GNUNET_TIME_Relative auto_refund;
400 :
401 : /**
402 : * Nonce generated by the wallet and echoed by the merchant
403 : * in this field when the proposal is generated.
404 : */
405 : const char *nonce;
406 :
407 : /**
408 : * Extra data that is only interpreted by the merchant frontend.
409 : */
410 : const json_t *extra;
411 :
412 : /**
413 : * Minimum age required by the order.
414 : */
415 : uint32_t minimum_age;
416 :
417 : /**
418 : * Money pot to increment for whatever order payment amount
419 : * is not yet assigned to a pot via the Product.
420 : */
421 : uint64_t order_default_money_pot;
422 :
423 : /**
424 : * Version of the contract terms.
425 : */
426 : enum TALER_MERCHANT_ContractVersion version;
427 :
428 : /**
429 : * Details present depending on @e version.
430 : */
431 : union
432 : {
433 : /**
434 : * Details only present for v0.
435 : */
436 : struct
437 : {
438 : /**
439 : * Gross amount value of the contract. Used to
440 : * compute @e max_stefan_fee.
441 : */
442 : struct TALER_Amount brutto;
443 :
444 : /**
445 : * Maximum fee as given by the client request.
446 : */
447 : struct TALER_Amount max_fee;
448 : } v0;
449 :
450 : /**
451 : * Details only present for v1.
452 : */
453 : struct
454 : {
455 : /**
456 : * Array of contract choices. Is null for v0 contracts.
457 : */
458 : const json_t *choices;
459 : } v1;
460 : } details;
461 :
462 : } parse_order;
463 :
464 : /**
465 : * Information set in the #ORDER_PHASE_PARSE_CHOICES phase.
466 : */
467 : struct
468 : {
469 : /**
470 : * Array of possible specific contracts the wallet/customer may choose
471 : * from by selecting the respective index when signing the deposit
472 : * confirmation.
473 : */
474 : struct TALER_MERCHANT_ContractChoice *choices;
475 :
476 : /**
477 : * Length of the @e choices array.
478 : */
479 : unsigned int choices_len;
480 :
481 : /**
482 : * Array of token families referenced in the contract.
483 : */
484 : struct TALER_MERCHANT_ContractTokenFamily *token_families;
485 :
486 : /**
487 : * Length of the @e token_families array.
488 : */
489 : unsigned int token_families_len;
490 : } parse_choices;
491 :
492 : /**
493 : * Information set in the #ORDER_PHASE_MERGE_INVENTORY phase.
494 : */
495 : struct
496 : {
497 : /**
498 : * Merged array of products in the @e order.
499 : */
500 : json_t *products;
501 : } merge_inventory;
502 :
503 : /**
504 : * Information set in the #ORDER_PHASE_ADD_PAYMENT_DETAILS phase.
505 : */
506 : struct
507 : {
508 :
509 : /**
510 : * DLL of wire methods under evaluation.
511 : */
512 : struct WireMethodCandidate *wmc_head;
513 :
514 : /**
515 : * DLL of wire methods under evaluation.
516 : */
517 : struct WireMethodCandidate *wmc_tail;
518 :
519 : /**
520 : * Array of maximum amounts that appear in the contract choices
521 : * per currency.
522 : * Determines the maximum amounts that a client could pay for this
523 : * order and which we must thus make sure is acceptable for the
524 : * selected wire method/account if possible.
525 : */
526 : struct TALER_Amount *max_choice_limits;
527 :
528 : /**
529 : * Length of the @e max_choice_limits array.
530 : */
531 : unsigned int num_max_choice_limits;
532 :
533 : /**
534 : * Set to true if we may need an exchange. True if any amount is non-zero.
535 : */
536 : bool need_exchange;
537 :
538 : } add_payment_details;
539 :
540 : /**
541 : * Information set in the #ORDER_PHASE_SELECT_WIRE_METHOD phase.
542 : */
543 : struct
544 : {
545 :
546 : /**
547 : * Array of exchanges we find acceptable for this order and wire method.
548 : */
549 : json_t *exchanges;
550 :
551 : /**
552 : * Wire method (and our bank account) we have selected
553 : * to be included for this order.
554 : */
555 : const struct TMH_WireMethod *wm;
556 :
557 : } select_wire_method;
558 :
559 : /**
560 : * Information set in the #ORDER_PHASE_SET_EXCHANGES phase.
561 : */
562 : struct
563 : {
564 :
565 : /**
566 : * Forced requests to /keys to update our exchange
567 : * information.
568 : */
569 : struct RekeyExchange *pending_reload_head;
570 :
571 : /**
572 : * Forced requests to /keys to update our exchange
573 : * information.
574 : */
575 : struct RekeyExchange *pending_reload_tail;
576 :
577 : /**
578 : * How long do we wait at most until giving up on getting keys?
579 : */
580 : struct GNUNET_TIME_Absolute keys_timeout;
581 :
582 : /**
583 : * Task to wake us up on @e keys_timeout.
584 : */
585 : struct GNUNET_SCHEDULER_Task *wakeup_task;
586 :
587 : /**
588 : * Did we previously force reloading of /keys from
589 : * all exchanges? Set to 'true' to prevent us from
590 : * doing it again (and again...).
591 : */
592 : bool forced_reload;
593 :
594 : /**
595 : * Did we find a working exchange?
596 : */
597 : bool exchange_ok;
598 :
599 : /**
600 : * Did we find an exchange that justifies
601 : * reloading keys?
602 : */
603 : bool promising_exchange;
604 :
605 : /**
606 : * Set to true once we have attempted to load exchanges
607 : * for the first time.
608 : */
609 : bool exchanges_tried;
610 :
611 : /**
612 : * Details depending on the contract version.
613 : */
614 : union
615 : {
616 :
617 : /**
618 : * Details for contract v0.
619 : */
620 : struct
621 : {
622 : /**
623 : * Maximum fee for @e order based on STEFAN curves.
624 : * Used to set @e max_fee if not provided as part of
625 : * @e order.
626 : */
627 : struct TALER_Amount max_stefan_fee;
628 :
629 : } v0;
630 :
631 : /**
632 : * Details for contract v1.
633 : */
634 : struct
635 : {
636 : /**
637 : * Maximum fee for @e order based on STEFAN curves by
638 : * contract choice.
639 : * Used to set @e max_fee if not provided as part of
640 : * @e order.
641 : */
642 : struct TALER_Amount *max_stefan_fees;
643 :
644 : } v1;
645 :
646 : } details;
647 :
648 : } set_exchanges;
649 :
650 : /**
651 : * Information set in the #ORDER_PHASE_SET_MAX_FEE phase.
652 : */
653 : struct
654 : {
655 :
656 : /**
657 : * Details depending on the contract version.
658 : */
659 : union
660 : {
661 :
662 : /**
663 : * Details for contract v0.
664 : */
665 : struct
666 : {
667 : /**
668 : * Maximum fee
669 : */
670 : struct TALER_Amount max_fee;
671 : } v0;
672 :
673 : /**
674 : * Details for contract v1.
675 : */
676 : struct
677 : {
678 : /**
679 : * Maximum fees by contract choice.
680 : */
681 : struct TALER_Amount *max_fees;
682 :
683 : } v1;
684 :
685 : } details;
686 : } set_max_fee;
687 :
688 : /**
689 : * Information set in the #ORDER_PHASE_EXECUTE_ORDER phase.
690 : */
691 : struct
692 : {
693 : /**
694 : * Which product (by offset) is out of stock, UINT_MAX if all were in-stock.
695 : */
696 : unsigned int out_of_stock_index;
697 :
698 : /**
699 : * Set to a previous claim token *if* @e idempotent
700 : * is also true.
701 : */
702 : struct TALER_ClaimTokenP token;
703 :
704 : /**
705 : * Set to true if the order was idempotent and there
706 : * was an equivalent one before.
707 : */
708 : bool idempotent;
709 :
710 : /**
711 : * Set to true if the order is in conflict with a
712 : * previous order with the same order ID.
713 : */
714 : bool conflict;
715 : } execute_order;
716 :
717 : struct
718 : {
719 : /**
720 : * Contract terms to store in the database.
721 : */
722 : json_t *contract;
723 : } serialize_order;
724 :
725 : /**
726 : * Connection of the request.
727 : */
728 : struct MHD_Connection *connection;
729 :
730 : /**
731 : * Kept in a DLL while suspended.
732 : */
733 : struct OrderContext *next;
734 :
735 : /**
736 : * Kept in a DLL while suspended.
737 : */
738 : struct OrderContext *prev;
739 :
740 : /**
741 : * Handler context for the request.
742 : */
743 : struct TMH_HandlerContext *hc;
744 :
745 : /**
746 : * #GNUNET_YES if suspended.
747 : */
748 : enum GNUNET_GenericReturnValue suspended;
749 :
750 : /**
751 : * Current phase of setting up the order.
752 : */
753 : enum
754 : {
755 : ORDER_PHASE_PARSE_REQUEST,
756 : ORDER_PHASE_PARSE_ORDER,
757 : ORDER_PHASE_PARSE_CHOICES,
758 : ORDER_PHASE_MERGE_INVENTORY,
759 : ORDER_PHASE_ADD_PAYMENT_DETAILS,
760 : ORDER_PHASE_SET_EXCHANGES,
761 : ORDER_PHASE_SELECT_WIRE_METHOD,
762 : ORDER_PHASE_SET_MAX_FEE,
763 : ORDER_PHASE_SERIALIZE_ORDER,
764 : ORDER_PHASE_SALT_FORGETTABLE,
765 : ORDER_PHASE_CHECK_CONTRACT,
766 : ORDER_PHASE_EXECUTE_ORDER,
767 :
768 : /**
769 : * Processing is done, we should return #MHD_YES.
770 : */
771 : ORDER_PHASE_FINISHED_MHD_YES,
772 :
773 : /**
774 : * Processing is done, we should return #MHD_NO.
775 : */
776 : ORDER_PHASE_FINISHED_MHD_NO
777 : } phase;
778 :
779 :
780 : };
781 :
782 :
783 : /**
784 : * Kept in a DLL while suspended.
785 : */
786 : static struct OrderContext *oc_head;
787 :
788 : /**
789 : * Kept in a DLL while suspended.
790 : */
791 : static struct OrderContext *oc_tail;
792 :
793 :
794 : void
795 15 : TMH_force_orders_resume ()
796 : {
797 : struct OrderContext *oc;
798 :
799 15 : while (NULL != (oc = oc_head))
800 : {
801 0 : GNUNET_CONTAINER_DLL_remove (oc_head,
802 : oc_tail,
803 : oc);
804 0 : oc->suspended = GNUNET_SYSERR;
805 0 : MHD_resume_connection (oc->connection);
806 : }
807 15 : }
808 :
809 :
810 : /**
811 : * Add the given @a val to the @a array. Adds the
812 : * amount to a given entry in @a array if one with the same
813 : * currency exists, otherwise extends the @a array.
814 : *
815 : * @param[in,out] array pointer to array of amounts
816 : * @param[in,out] array_len length of @a array
817 : * @param val amount to add
818 : * @param cap cap for the sums to enforce, can be NULL
819 : */
820 : static void
821 66 : add_to_currency_vector (struct TALER_Amount **array,
822 : unsigned int *array_len,
823 : const struct TALER_Amount *val,
824 : const struct TALER_Amount *cap)
825 : {
826 66 : for (unsigned int i = 0; i<*array_len; i++)
827 : {
828 0 : struct TALER_Amount *ai = &(*array)[i];
829 :
830 0 : if (GNUNET_OK ==
831 0 : TALER_amount_cmp_currency (ai,
832 : val))
833 : {
834 : enum TALER_AmountArithmeticResult aar;
835 :
836 0 : aar = TALER_amount_add (ai,
837 : ai,
838 : val);
839 : /* If we have a cap, we tolerate the overflow */
840 0 : GNUNET_assert ( (aar >= 0) ||
841 : ( (TALER_AAR_INVALID_RESULT_OVERFLOW == aar) &&
842 : (NULL != cap) ) );
843 0 : if (TALER_AAR_INVALID_RESULT_OVERFLOW == aar)
844 : {
845 0 : *ai = *cap;
846 : }
847 0 : else if (NULL != cap)
848 0 : GNUNET_assert (GNUNET_OK ==
849 : TALER_amount_min (ai,
850 : ai,
851 : cap));
852 0 : return;
853 : }
854 : }
855 66 : GNUNET_array_append (*array,
856 : *array_len,
857 : *val);
858 66 : if (NULL != cap)
859 : {
860 66 : struct TALER_Amount *ai = &(*array)[(*array_len) - 1];
861 :
862 66 : GNUNET_assert (GNUNET_OK ==
863 : TALER_amount_min (ai,
864 : ai,
865 : cap));
866 : }
867 : }
868 :
869 :
870 : /**
871 : * Update the phase of @a oc based on @a mret.
872 : *
873 : * @param[in,out] oc order to update phase for
874 : * @param mret #MHD_NO to close with #MHD_NO
875 : * #MHD_YES to close with #MHD_YES
876 : */
877 : static void
878 77 : finalize_order (struct OrderContext *oc,
879 : MHD_RESULT mret)
880 : {
881 77 : oc->phase = (MHD_YES == mret)
882 : ? ORDER_PHASE_FINISHED_MHD_YES
883 77 : : ORDER_PHASE_FINISHED_MHD_NO;
884 77 : }
885 :
886 :
887 : /**
888 : * Update the phase of @a oc based on @a ret.
889 : *
890 : * @param[in,out] oc order to update phase for
891 : * @param ret #GNUNET_SYSERR to close with #MHD_NO
892 : * #GNUNET_NO to close with #MHD_YES
893 : * #GNUNET_OK is not allowed!
894 : */
895 : static void
896 0 : finalize_order2 (struct OrderContext *oc,
897 : enum GNUNET_GenericReturnValue ret)
898 : {
899 0 : GNUNET_assert (GNUNET_OK != ret);
900 0 : oc->phase = (GNUNET_NO == ret)
901 : ? ORDER_PHASE_FINISHED_MHD_YES
902 0 : : ORDER_PHASE_FINISHED_MHD_NO;
903 0 : }
904 :
905 :
906 : /**
907 : * Generate an error response for @a oc.
908 : *
909 : * @param[in,out] oc order context to respond to
910 : * @param http_status HTTP status code to set
911 : * @param ec error code to set
912 : * @param detail error message detail to set
913 : */
914 : static void
915 10 : reply_with_error (struct OrderContext *oc,
916 : unsigned int http_status,
917 : enum TALER_ErrorCode ec,
918 : const char *detail)
919 : {
920 : MHD_RESULT mret;
921 :
922 10 : mret = TALER_MHD_reply_with_error (oc->connection,
923 : http_status,
924 : ec,
925 : detail);
926 10 : finalize_order (oc,
927 : mret);
928 10 : }
929 :
930 :
931 : /**
932 : * Clean up memory used by @a wmc.
933 : *
934 : * @param[in,out] oc order context the WMC is part of
935 : * @param[in] wmc wire method candidate to free
936 : */
937 : static void
938 70 : free_wmc (struct OrderContext *oc,
939 : struct WireMethodCandidate *wmc)
940 : {
941 70 : GNUNET_CONTAINER_DLL_remove (oc->add_payment_details.wmc_head,
942 : oc->add_payment_details.wmc_tail,
943 : wmc);
944 70 : GNUNET_array_grow (wmc->total_exchange_limits,
945 : wmc->num_total_exchange_limits,
946 : 0);
947 70 : json_decref (wmc->exchanges);
948 70 : GNUNET_free (wmc);
949 70 : }
950 :
951 :
952 : /**
953 : * Clean up memory used by @a cls.
954 : *
955 : * @param[in] cls the `struct OrderContext` to clean up
956 : */
957 : static void
958 77 : clean_order (void *cls)
959 : {
960 77 : struct OrderContext *oc = cls;
961 : struct RekeyExchange *rx;
962 :
963 147 : while (NULL != oc->add_payment_details.wmc_head)
964 70 : free_wmc (oc,
965 : oc->add_payment_details.wmc_head);
966 77 : while (NULL != (rx = oc->set_exchanges.pending_reload_head))
967 : {
968 0 : GNUNET_CONTAINER_DLL_remove (oc->set_exchanges.pending_reload_head,
969 : oc->set_exchanges.pending_reload_tail,
970 : rx);
971 0 : TMH_EXCHANGES_keys4exchange_cancel (rx->fo);
972 0 : GNUNET_free (rx->url);
973 0 : GNUNET_free (rx);
974 : }
975 77 : GNUNET_array_grow (oc->add_payment_details.max_choice_limits,
976 : oc->add_payment_details.num_max_choice_limits,
977 : 0);
978 77 : if (NULL != oc->set_exchanges.wakeup_task)
979 : {
980 0 : GNUNET_SCHEDULER_cancel (oc->set_exchanges.wakeup_task);
981 0 : oc->set_exchanges.wakeup_task = NULL;
982 : }
983 77 : if (NULL != oc->select_wire_method.exchanges)
984 : {
985 69 : json_decref (oc->select_wire_method.exchanges);
986 69 : oc->select_wire_method.exchanges = NULL;
987 : }
988 77 : switch (oc->parse_order.version)
989 : {
990 68 : case TALER_MERCHANT_CONTRACT_VERSION_0:
991 68 : break;
992 9 : case TALER_MERCHANT_CONTRACT_VERSION_1:
993 9 : GNUNET_free (oc->set_max_fee.details.v1.max_fees);
994 9 : GNUNET_free (oc->set_exchanges.details.v1.max_stefan_fees);
995 9 : break;
996 : }
997 77 : if (NULL != oc->merge_inventory.products)
998 : {
999 75 : json_decref (oc->merge_inventory.products);
1000 75 : oc->merge_inventory.products = NULL;
1001 : }
1002 87 : for (unsigned int i = 0; i<oc->parse_choices.choices_len; i++)
1003 : {
1004 10 : TALER_MERCHANT_contract_choice_free (&oc->parse_choices.choices[i]);
1005 : }
1006 77 : GNUNET_array_grow (oc->parse_choices.choices,
1007 : oc->parse_choices.choices_len,
1008 : 0);
1009 82 : for (size_t i = 0; i<oc->parse_order.products_len; i++)
1010 : {
1011 5 : TALER_MERCHANT_product_sold_free (&oc->parse_order.products[i]);
1012 : }
1013 77 : GNUNET_free (oc->parse_order.products);
1014 77 : oc->parse_order.products_len = 0;
1015 86 : for (unsigned int i = 0; i<oc->parse_choices.token_families_len; i++)
1016 : {
1017 9 : struct TALER_MERCHANT_ContractTokenFamily *mctf
1018 9 : = &oc->parse_choices.token_families[i];
1019 :
1020 9 : GNUNET_free (mctf->slug);
1021 9 : GNUNET_free (mctf->name);
1022 9 : GNUNET_free (mctf->description);
1023 9 : json_decref (mctf->description_i18n);
1024 9 : switch (mctf->kind)
1025 : {
1026 0 : case TALER_MERCHANT_CONTRACT_TOKEN_KIND_INVALID:
1027 0 : GNUNET_break (0);
1028 0 : break;
1029 8 : case TALER_MERCHANT_CONTRACT_TOKEN_KIND_SUBSCRIPTION:
1030 8 : for (size_t j = 0; j<mctf->details.subscription.trusted_domains_len; j++)
1031 0 : GNUNET_free (mctf->details.subscription.trusted_domains[j]);
1032 8 : GNUNET_free (mctf->details.subscription.trusted_domains);
1033 8 : break;
1034 1 : case TALER_MERCHANT_CONTRACT_TOKEN_KIND_DISCOUNT:
1035 1 : for (size_t j = 0; j<mctf->details.discount.expected_domains_len; j++)
1036 0 : GNUNET_free (mctf->details.discount.expected_domains[j]);
1037 1 : GNUNET_free (mctf->details.discount.expected_domains);
1038 1 : break;
1039 : }
1040 17 : for (unsigned int j = 0; j<mctf->keys_len; j++)
1041 : {
1042 8 : GNUNET_CRYPTO_blind_sign_pub_decref (mctf->keys[j].pub.public_key);
1043 : }
1044 9 : GNUNET_array_grow (mctf->keys,
1045 : mctf->keys_len,
1046 : 0);
1047 : }
1048 77 : GNUNET_array_grow (oc->parse_choices.token_families,
1049 : oc->parse_choices.token_families_len,
1050 : 0);
1051 77 : GNUNET_array_grow (oc->parse_request.inventory_products,
1052 : oc->parse_request.inventory_products_length,
1053 : 0);
1054 77 : GNUNET_array_grow (oc->parse_request.uuids,
1055 : oc->parse_request.uuids_length,
1056 : 0);
1057 77 : GNUNET_free (oc->parse_request.pos_key);
1058 77 : json_decref (oc->parse_request.order);
1059 77 : json_decref (oc->serialize_order.contract);
1060 77 : GNUNET_free (oc->parse_order.order_id);
1061 77 : GNUNET_free (oc->parse_order.merchant_base_url);
1062 77 : GNUNET_free (oc);
1063 77 : }
1064 :
1065 :
1066 : /* ***************** ORDER_PHASE_EXECUTE_ORDER **************** */
1067 :
1068 : /**
1069 : * Compute remaining stock (integer and fractional parts) for a product.
1070 : *
1071 : * @param pd product details with current totals/sold/lost
1072 : * @param[out] available_value remaining whole units (normalized, non-negative)
1073 : * @param[out] available_frac remaining fractional units (0..TALER_MERCHANT_UNIT_FRAC_BASE-1)
1074 : */
1075 : static void
1076 3 : compute_available_quantity (const struct TALER_MERCHANTDB_ProductDetails *pd,
1077 : uint64_t *available_value,
1078 : uint32_t *available_frac)
1079 : {
1080 : int64_t value;
1081 : int64_t frac;
1082 :
1083 3 : GNUNET_assert (NULL != available_value);
1084 3 : GNUNET_assert (NULL != available_frac);
1085 :
1086 3 : if ( (INT64_MAX == pd->total_stock) &&
1087 0 : (INT32_MAX == pd->total_stock_frac) )
1088 : {
1089 0 : *available_value = pd->total_stock;
1090 0 : *available_frac = pd->total_stock_frac;
1091 0 : return;
1092 : }
1093 :
1094 3 : value = (int64_t) pd->total_stock
1095 3 : - (int64_t) pd->total_sold
1096 3 : - (int64_t) pd->total_lost;
1097 3 : frac = (int64_t) pd->total_stock_frac
1098 3 : - (int64_t) pd->total_sold_frac
1099 3 : - (int64_t) pd->total_lost_frac;
1100 :
1101 3 : if (frac < 0)
1102 : {
1103 2 : int64_t borrow = ((-frac) + TALER_MERCHANT_UNIT_FRAC_BASE - 1)
1104 : / TALER_MERCHANT_UNIT_FRAC_BASE;
1105 2 : value -= borrow;
1106 2 : frac += borrow * (int64_t) TALER_MERCHANT_UNIT_FRAC_BASE;
1107 : }
1108 1 : else if (frac >= TALER_MERCHANT_UNIT_FRAC_BASE)
1109 : {
1110 0 : int64_t carry = frac / TALER_MERCHANT_UNIT_FRAC_BASE;
1111 0 : value += carry;
1112 0 : frac -= carry * (int64_t) TALER_MERCHANT_UNIT_FRAC_BASE;
1113 : }
1114 :
1115 3 : if (value < 0)
1116 : {
1117 0 : value = 0;
1118 0 : frac = 0;
1119 : }
1120 :
1121 3 : *available_value = (uint64_t) value;
1122 3 : *available_frac = (uint32_t) frac;
1123 : }
1124 :
1125 :
1126 : /**
1127 : * Execute the database transaction to setup the order.
1128 : *
1129 : * @param[in,out] oc order context
1130 : * @return transaction status, #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if @a uuids were insufficient to reserve required inventory
1131 : */
1132 : static enum GNUNET_DB_QueryStatus
1133 69 : execute_transaction (struct OrderContext *oc)
1134 : {
1135 : enum GNUNET_DB_QueryStatus qs;
1136 : struct GNUNET_TIME_Timestamp timestamp;
1137 : uint64_t order_serial;
1138 :
1139 69 : if (GNUNET_OK !=
1140 69 : TMH_db->start (TMH_db->cls,
1141 : "insert_order"))
1142 : {
1143 0 : GNUNET_break (0);
1144 0 : return GNUNET_DB_STATUS_HARD_ERROR;
1145 : }
1146 :
1147 : /* Test if we already have an order with this id */
1148 : {
1149 : json_t *contract_terms;
1150 : struct TALER_MerchantPostDataHashP orig_post;
1151 :
1152 69 : qs = TMH_db->lookup_order (TMH_db->cls,
1153 69 : oc->hc->instance->settings.id,
1154 69 : oc->parse_order.order_id,
1155 : &oc->execute_order.token,
1156 : &orig_post,
1157 : &contract_terms);
1158 : /* If yes, check for idempotency */
1159 69 : if (0 > qs)
1160 : {
1161 0 : GNUNET_break (0);
1162 0 : TMH_db->rollback (TMH_db->cls);
1163 4 : return qs;
1164 : }
1165 69 : if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
1166 : {
1167 4 : TMH_db->rollback (TMH_db->cls);
1168 4 : json_decref (contract_terms);
1169 : /* Comparing the contract terms is sufficient because all the other
1170 : params get added to it at some point. */
1171 4 : if (0 == GNUNET_memcmp (&orig_post,
1172 : &oc->parse_request.h_post_data))
1173 : {
1174 2 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1175 : "Order creation idempotent\n");
1176 2 : oc->execute_order.idempotent = true;
1177 2 : return qs;
1178 : }
1179 2 : GNUNET_break_op (0);
1180 2 : oc->execute_order.conflict = true;
1181 2 : return qs;
1182 : }
1183 : }
1184 :
1185 : /* Setup order */
1186 65 : qs = TMH_db->insert_order (TMH_db->cls,
1187 65 : oc->hc->instance->settings.id,
1188 65 : oc->parse_order.order_id,
1189 : oc->parse_request.session_id,
1190 65 : &oc->parse_request.h_post_data,
1191 : oc->parse_order.pay_deadline,
1192 65 : &oc->parse_request.claim_token,
1193 65 : oc->serialize_order.contract, /* called 'contract terms' at database. */
1194 65 : oc->parse_request.pos_key,
1195 : oc->parse_request.pos_algorithm);
1196 65 : if (qs <= 0)
1197 : {
1198 : /* qs == 0: probably instance does not exist (anymore) */
1199 0 : TMH_db->rollback (TMH_db->cls);
1200 0 : return qs;
1201 : }
1202 : /* Migrate locks from UUIDs to new order: first release old locks */
1203 68 : for (unsigned int i = 0; i<oc->parse_request.uuids_length; i++)
1204 : {
1205 3 : qs = TMH_db->unlock_inventory (TMH_db->cls,
1206 3 : &oc->parse_request.uuids[i]);
1207 3 : if (qs < 0)
1208 : {
1209 0 : TMH_db->rollback (TMH_db->cls);
1210 0 : return qs;
1211 : }
1212 : /* qs == 0 is OK here, that just means we did not HAVE any lock under this
1213 : UUID */
1214 : }
1215 : /* Migrate locks from UUIDs to new order: acquire new locks
1216 : (note: this can basically ONLY fail on serializability OR
1217 : because the UUID locks were insufficient for the desired
1218 : quantities). */
1219 75 : for (unsigned int i = 0; i<oc->parse_request.inventory_products_length; i++)
1220 : {
1221 13 : qs = TMH_db->insert_order_lock (
1222 13 : TMH_db->cls,
1223 13 : oc->hc->instance->settings.id,
1224 13 : oc->parse_order.order_id,
1225 13 : oc->parse_request.inventory_products[i].product_id,
1226 13 : oc->parse_request.inventory_products[i].quantity,
1227 13 : oc->parse_request.inventory_products[i].quantity_frac);
1228 13 : if (qs < 0)
1229 : {
1230 0 : TMH_db->rollback (TMH_db->cls);
1231 0 : return qs;
1232 : }
1233 13 : if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
1234 : {
1235 : /* qs == 0: lock acquisition failed due to insufficient stocks */
1236 3 : TMH_db->rollback (TMH_db->cls);
1237 3 : oc->execute_order.out_of_stock_index = i; /* indicate which product is causing the issue */
1238 3 : return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
1239 : }
1240 : }
1241 62 : oc->execute_order.out_of_stock_index = UINT_MAX;
1242 :
1243 : /* Get the order serial and timestamp for the order we just created to
1244 : update long-poll clients. */
1245 62 : qs = TMH_db->lookup_order_summary (TMH_db->cls,
1246 62 : oc->hc->instance->settings.id,
1247 62 : oc->parse_order.order_id,
1248 : ×tamp,
1249 : &order_serial);
1250 62 : if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
1251 : {
1252 0 : TMH_db->rollback (TMH_db->cls);
1253 0 : return qs;
1254 : }
1255 :
1256 : {
1257 : json_t *jhook;
1258 :
1259 62 : jhook = GNUNET_JSON_PACK (
1260 : GNUNET_JSON_pack_string ("order_id",
1261 : oc->parse_order.order_id),
1262 : GNUNET_JSON_pack_object_incref ("contract",
1263 : oc->serialize_order.contract),
1264 : GNUNET_JSON_pack_string ("instance_id",
1265 : oc->hc->instance->settings.id)
1266 : );
1267 62 : GNUNET_assert (NULL != jhook);
1268 62 : qs = TMH_trigger_webhook (oc->hc->instance->settings.id,
1269 : "order_created",
1270 : jhook);
1271 62 : json_decref (jhook);
1272 62 : if (0 > qs)
1273 : {
1274 0 : TMH_db->rollback (TMH_db->cls);
1275 0 : if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
1276 0 : return qs;
1277 0 : GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs);
1278 0 : reply_with_error (oc,
1279 : MHD_HTTP_INTERNAL_SERVER_ERROR,
1280 : TALER_EC_GENERIC_DB_STORE_FAILED,
1281 : "failed to trigger webhooks");
1282 0 : return qs;
1283 : }
1284 : }
1285 :
1286 62 : TMH_notify_order_change (oc->hc->instance,
1287 : TMH_OSF_NONE,
1288 : timestamp,
1289 : order_serial);
1290 : /* finally, commit transaction (note: if it fails, we ALSO re-acquire
1291 : the UUID locks, which is exactly what we want) */
1292 62 : qs = TMH_db->commit (TMH_db->cls);
1293 62 : if (0 > qs)
1294 0 : return qs;
1295 62 : return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; /* 1 == success! */
1296 : }
1297 :
1298 :
1299 : /**
1300 : * Transform an order into a proposal and store it in the
1301 : * database. Write the resulting proposal or an error message
1302 : * of a MHD connection.
1303 : *
1304 : * @param[in,out] oc order context
1305 : */
1306 : static void
1307 69 : phase_execute_order (struct OrderContext *oc)
1308 : {
1309 69 : const struct TALER_MERCHANTDB_InstanceSettings *settings =
1310 69 : &oc->hc->instance->settings;
1311 : enum GNUNET_DB_QueryStatus qs;
1312 :
1313 69 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1314 : "Executing database transaction to create order '%s' for instance '%s'\n",
1315 : oc->parse_order.order_id,
1316 : settings->id);
1317 69 : for (unsigned int i = 0; i<MAX_RETRIES; i++)
1318 : {
1319 69 : TMH_db->preflight (TMH_db->cls);
1320 69 : qs = execute_transaction (oc);
1321 69 : if (GNUNET_DB_STATUS_SOFT_ERROR != qs)
1322 69 : break;
1323 : }
1324 69 : if (0 >= qs)
1325 : {
1326 : /* Special report if retries insufficient */
1327 0 : if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
1328 : {
1329 0 : GNUNET_break (0);
1330 0 : reply_with_error (oc,
1331 : MHD_HTTP_INTERNAL_SERVER_ERROR,
1332 : TALER_EC_GENERIC_DB_SOFT_FAILURE,
1333 : NULL);
1334 0 : return;
1335 : }
1336 0 : if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
1337 : {
1338 : /* should be: contract (!) with same order ID
1339 : already exists */
1340 0 : reply_with_error (
1341 : oc,
1342 : MHD_HTTP_CONFLICT,
1343 : TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_ALREADY_EXISTS,
1344 0 : oc->parse_order.order_id);
1345 0 : return;
1346 : }
1347 : /* Other hard transaction error (disk full, etc.) */
1348 0 : GNUNET_break (0);
1349 0 : reply_with_error (
1350 : oc,
1351 : MHD_HTTP_INTERNAL_SERVER_ERROR,
1352 : TALER_EC_GENERIC_DB_COMMIT_FAILED,
1353 : NULL);
1354 0 : return;
1355 : }
1356 :
1357 : /* DB transaction succeeded, check for idempotent */
1358 69 : if (oc->execute_order.idempotent)
1359 : {
1360 : MHD_RESULT ret;
1361 :
1362 2 : ret = TALER_MHD_REPLY_JSON_PACK (
1363 : oc->connection,
1364 : MHD_HTTP_OK,
1365 : GNUNET_JSON_pack_string ("order_id",
1366 : oc->parse_order.order_id),
1367 : GNUNET_JSON_pack_timestamp ("pay_deadline",
1368 : oc->parse_order.pay_deadline),
1369 : GNUNET_JSON_pack_allow_null (
1370 : GNUNET_JSON_pack_data_varsize (
1371 : "token",
1372 : GNUNET_is_zero (&oc->execute_order.token)
1373 : ? NULL
1374 : : &oc->execute_order.token,
1375 : sizeof (oc->execute_order.token))));
1376 2 : finalize_order (oc,
1377 : ret);
1378 2 : return;
1379 : }
1380 67 : if (oc->execute_order.conflict)
1381 : {
1382 2 : reply_with_error (
1383 : oc,
1384 : MHD_HTTP_CONFLICT,
1385 : TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_ALREADY_EXISTS,
1386 2 : oc->parse_order.order_id);
1387 2 : return;
1388 : }
1389 :
1390 : /* DB transaction succeeded, check for out-of-stock */
1391 65 : if (oc->execute_order.out_of_stock_index < UINT_MAX)
1392 : {
1393 : /* We had a product that has insufficient quantities,
1394 : generate the details for the response. */
1395 : struct TALER_MERCHANTDB_ProductDetails pd;
1396 : MHD_RESULT ret;
1397 : const struct InventoryProduct *ip;
1398 3 : size_t num_categories = 0;
1399 3 : uint64_t *categories = NULL;
1400 : uint64_t available_quantity;
1401 : uint32_t available_quantity_frac;
1402 : char requested_quantity_buf[64];
1403 : char available_quantity_buf[64];
1404 :
1405 3 : ip = &oc->parse_request.inventory_products[
1406 3 : oc->execute_order.out_of_stock_index];
1407 3 : memset (&pd,
1408 : 0,
1409 : sizeof (pd));
1410 3 : qs = TMH_db->lookup_product (
1411 3 : TMH_db->cls,
1412 3 : oc->hc->instance->settings.id,
1413 3 : ip->product_id,
1414 : &pd,
1415 : &num_categories,
1416 : &categories);
1417 3 : switch (qs)
1418 : {
1419 3 : case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
1420 3 : GNUNET_free (categories);
1421 3 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1422 : "Order creation failed: product out of stock\n");
1423 :
1424 3 : compute_available_quantity (&pd,
1425 : &available_quantity,
1426 : &available_quantity_frac);
1427 3 : TALER_MERCHANT_vk_format_fractional_string (
1428 : TALER_MERCHANT_VK_QUANTITY,
1429 3 : ip->quantity,
1430 3 : ip->quantity_frac,
1431 : sizeof (requested_quantity_buf),
1432 : requested_quantity_buf);
1433 3 : TALER_MERCHANT_vk_format_fractional_string (
1434 : TALER_MERCHANT_VK_QUANTITY,
1435 : available_quantity,
1436 : available_quantity_frac,
1437 : sizeof (available_quantity_buf),
1438 : available_quantity_buf);
1439 3 : ret = TALER_MHD_REPLY_JSON_PACK (
1440 : oc->connection,
1441 : MHD_HTTP_GONE,
1442 : GNUNET_JSON_pack_string (
1443 : "product_id",
1444 : ip->product_id),
1445 : GNUNET_JSON_pack_uint64 (
1446 : "requested_quantity",
1447 : ip->quantity),
1448 : GNUNET_JSON_pack_string (
1449 : "unit_requested_quantity",
1450 : requested_quantity_buf),
1451 : GNUNET_JSON_pack_uint64 (
1452 : "available_quantity",
1453 : available_quantity),
1454 : GNUNET_JSON_pack_string (
1455 : "unit_available_quantity",
1456 : available_quantity_buf),
1457 : GNUNET_JSON_pack_allow_null (
1458 : GNUNET_JSON_pack_timestamp (
1459 : "restock_expected",
1460 : pd.next_restock)));
1461 3 : TALER_MERCHANTDB_product_details_free (&pd);
1462 3 : finalize_order (oc,
1463 : ret);
1464 3 : return;
1465 0 : case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
1466 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1467 : "Order creation failed: unknown product out of stock\n");
1468 0 : finalize_order (oc,
1469 0 : TALER_MHD_REPLY_JSON_PACK (
1470 : oc->connection,
1471 : MHD_HTTP_GONE,
1472 : GNUNET_JSON_pack_string (
1473 : "product_id",
1474 : ip->product_id),
1475 : GNUNET_JSON_pack_uint64 (
1476 : "requested_quantity",
1477 : ip->quantity),
1478 : GNUNET_JSON_pack_uint64 (
1479 : "available_quantity",
1480 : 0)));
1481 0 : return;
1482 0 : case GNUNET_DB_STATUS_SOFT_ERROR:
1483 0 : GNUNET_break (0);
1484 0 : reply_with_error (
1485 : oc,
1486 : MHD_HTTP_INTERNAL_SERVER_ERROR,
1487 : TALER_EC_GENERIC_DB_SOFT_FAILURE,
1488 : NULL);
1489 0 : return;
1490 0 : case GNUNET_DB_STATUS_HARD_ERROR:
1491 0 : GNUNET_break (0);
1492 0 : reply_with_error (
1493 : oc,
1494 : MHD_HTTP_INTERNAL_SERVER_ERROR,
1495 : TALER_EC_GENERIC_DB_FETCH_FAILED,
1496 : NULL);
1497 0 : return;
1498 : }
1499 0 : GNUNET_break (0);
1500 0 : oc->phase = ORDER_PHASE_FINISHED_MHD_NO;
1501 0 : return;
1502 : } /* end 'out of stock' case */
1503 :
1504 : /* Everything in-stock, generate positive response */
1505 62 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1506 : "Order creation succeeded\n");
1507 :
1508 : {
1509 : MHD_RESULT ret;
1510 :
1511 62 : ret = TALER_MHD_REPLY_JSON_PACK (
1512 : oc->connection,
1513 : MHD_HTTP_OK,
1514 : GNUNET_JSON_pack_string ("order_id",
1515 : oc->parse_order.order_id),
1516 : GNUNET_JSON_pack_allow_null (
1517 : GNUNET_JSON_pack_data_varsize (
1518 : "token",
1519 : GNUNET_is_zero (&oc->parse_request.claim_token)
1520 : ? NULL
1521 : : &oc->parse_request.claim_token,
1522 : sizeof (oc->parse_request.claim_token))));
1523 62 : finalize_order (oc,
1524 : ret);
1525 : }
1526 : }
1527 :
1528 :
1529 : /* ***************** ORDER_PHASE_CHECK_CONTRACT **************** */
1530 :
1531 :
1532 : /**
1533 : * Check that the contract is now well-formed. Upon success, continue
1534 : * processing with execute_order().
1535 : *
1536 : * @param[in,out] oc order context
1537 : */
1538 : static void
1539 69 : phase_check_contract (struct OrderContext *oc)
1540 : {
1541 : struct TALER_PrivateContractHashP h_control;
1542 :
1543 69 : switch (TALER_JSON_contract_hash (oc->serialize_order.contract,
1544 : &h_control))
1545 : {
1546 0 : case GNUNET_SYSERR:
1547 0 : GNUNET_break (0);
1548 0 : reply_with_error (
1549 : oc,
1550 : MHD_HTTP_INTERNAL_SERVER_ERROR,
1551 : TALER_EC_GENERIC_FAILED_COMPUTE_JSON_HASH,
1552 : "could not compute hash of serialized order");
1553 69 : return;
1554 0 : case GNUNET_NO:
1555 0 : GNUNET_break_op (0);
1556 0 : reply_with_error (
1557 : oc,
1558 : MHD_HTTP_BAD_REQUEST,
1559 : TALER_EC_GENERIC_FAILED_COMPUTE_JSON_HASH,
1560 : "order contained unallowed values");
1561 0 : return;
1562 69 : case GNUNET_OK:
1563 69 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1564 : "Contract hash is %s\n",
1565 : GNUNET_h2s (&h_control.hash));
1566 69 : oc->phase++;
1567 69 : return;
1568 : }
1569 0 : GNUNET_assert (0);
1570 : }
1571 :
1572 :
1573 : /* ***************** ORDER_PHASE_SALT_FORGETTABLE **************** */
1574 :
1575 :
1576 : /**
1577 : * Modify the final contract terms adding salts for
1578 : * items that are forgettable.
1579 : *
1580 : * @param[in,out] oc order context
1581 : */
1582 : static void
1583 69 : phase_salt_forgettable (struct OrderContext *oc)
1584 : {
1585 69 : if (GNUNET_OK !=
1586 69 : TALER_JSON_contract_seed_forgettable (oc->parse_request.order,
1587 : oc->serialize_order.contract))
1588 : {
1589 0 : GNUNET_break_op (0);
1590 0 : reply_with_error (
1591 : oc,
1592 : MHD_HTTP_BAD_REQUEST,
1593 : TALER_EC_GENERIC_JSON_INVALID,
1594 : "could not compute hash of order due to bogus forgettable fields");
1595 0 : return;
1596 : }
1597 69 : oc->phase++;
1598 : }
1599 :
1600 :
1601 : /* ***************** ORDER_PHASE_SERIALIZE_ORDER **************** */
1602 :
1603 : /**
1604 : * Get rounded time interval. @a start is calculated by rounding
1605 : * @a ts down to the nearest multiple of @a precision.
1606 : *
1607 : * @param precision rounding precision.
1608 : * year, month, day, hour, minute are supported.
1609 : * @param ts timestamp to round
1610 : * @param[out] start start of the interval
1611 : * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1612 : */
1613 : static enum GNUNET_GenericReturnValue
1614 8 : get_rounded_time_interval_down (struct GNUNET_TIME_Relative precision,
1615 : struct GNUNET_TIME_Timestamp ts,
1616 : struct GNUNET_TIME_Timestamp *start)
1617 : {
1618 : enum GNUNET_TIME_RounderInterval ri;
1619 :
1620 8 : ri = GNUNET_TIME_relative_to_round_interval (precision);
1621 8 : if ( (GNUNET_TIME_RI_NONE == ri) &&
1622 0 : (! GNUNET_TIME_relative_is_zero (precision)) )
1623 : {
1624 0 : *start = ts;
1625 0 : return GNUNET_SYSERR;
1626 : }
1627 8 : *start = GNUNET_TIME_absolute_to_timestamp (
1628 : GNUNET_TIME_round_down (ts.abs_time,
1629 : ri));
1630 8 : return GNUNET_OK;
1631 : }
1632 :
1633 :
1634 : /**
1635 : * Get rounded time interval. @a start is calculated by rounding
1636 : * @a ts up to the nearest multiple of @a precision.
1637 : *
1638 : * @param precision rounding precision.
1639 : * year, month, day, hour, minute are supported.
1640 : * @param ts timestamp to round
1641 : * @param[out] start start of the interval
1642 : * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1643 : */
1644 : static enum GNUNET_GenericReturnValue
1645 2 : get_rounded_time_interval_up (struct GNUNET_TIME_Relative precision,
1646 : struct GNUNET_TIME_Timestamp ts,
1647 : struct GNUNET_TIME_Timestamp *start)
1648 : {
1649 : enum GNUNET_TIME_RounderInterval ri;
1650 :
1651 2 : ri = GNUNET_TIME_relative_to_round_interval (precision);
1652 2 : if ( (GNUNET_TIME_RI_NONE == ri) &&
1653 0 : (! GNUNET_TIME_relative_is_zero (precision)) )
1654 : {
1655 0 : *start = ts;
1656 0 : return GNUNET_SYSERR;
1657 : }
1658 2 : *start = GNUNET_TIME_absolute_to_timestamp (
1659 : GNUNET_TIME_round_up (ts.abs_time,
1660 : ri));
1661 2 : return GNUNET_OK;
1662 : }
1663 :
1664 :
1665 : /**
1666 : * Find the family entry for the family of the given @a slug
1667 : * in @a oc.
1668 : *
1669 : * @param[in] oc order context to search
1670 : * @param slug slug to search for
1671 : * @return NULL if @a slug was not found
1672 : */
1673 : static struct TALER_MERCHANT_ContractTokenFamily *
1674 21 : find_family (const struct OrderContext *oc,
1675 : const char *slug)
1676 : {
1677 21 : for (unsigned int i = 0; i<oc->parse_choices.token_families_len; i++)
1678 : {
1679 8 : if (0 == strcmp (oc->parse_choices.token_families[i].slug,
1680 : slug))
1681 : {
1682 8 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1683 : "Token family %s already in order\n",
1684 : slug);
1685 8 : return &oc->parse_choices.token_families[i];
1686 : }
1687 : }
1688 13 : return NULL;
1689 : }
1690 :
1691 :
1692 : /**
1693 : * Function called with each applicable family key that should
1694 : * be added to the respective token family of the order.
1695 : *
1696 : * @param cls a `struct OrderContext *` to expand
1697 : * @param tfkd token family key details to add to the contract
1698 : */
1699 : static void
1700 9 : add_family_key (void *cls,
1701 : const struct TALER_MERCHANTDB_TokenFamilyKeyDetails *tfkd)
1702 : {
1703 9 : struct OrderContext *oc = cls;
1704 9 : const struct TALER_MERCHANTDB_TokenFamilyDetails *tf = &tfkd->token_family;
1705 : struct TALER_MERCHANT_ContractTokenFamily *family;
1706 :
1707 9 : family = find_family (oc,
1708 9 : tf->slug);
1709 9 : if (NULL == family)
1710 : {
1711 : /* Family not yet in our contract terms, create new entry */
1712 45 : struct TALER_MERCHANT_ContractTokenFamily new_family = {
1713 9 : .slug = GNUNET_strdup (tf->slug),
1714 9 : .name = GNUNET_strdup (tf->name),
1715 9 : .description = GNUNET_strdup (tf->description),
1716 9 : .description_i18n = json_incref (tf->description_i18n),
1717 : };
1718 :
1719 9 : switch (tf->kind)
1720 : {
1721 8 : case TALER_MERCHANTDB_TFK_Subscription:
1722 : {
1723 8 : json_t *tdomains = json_object_get (tf->extra_data,
1724 : "trusted_domains");
1725 : json_t *dom;
1726 : size_t i;
1727 :
1728 8 : new_family.kind = TALER_MERCHANT_CONTRACT_TOKEN_KIND_SUBSCRIPTION;
1729 8 : new_family.critical = true;
1730 : new_family.details.subscription.trusted_domains_len
1731 8 : = json_array_size (tdomains);
1732 8 : GNUNET_assert (new_family.details.subscription.trusted_domains_len
1733 : < UINT_MAX);
1734 : new_family.details.subscription.trusted_domains
1735 8 : = GNUNET_new_array (
1736 : new_family.details.subscription.trusted_domains_len,
1737 : char *);
1738 8 : json_array_foreach (tdomains, i, dom)
1739 : {
1740 : const char *val;
1741 :
1742 0 : val = json_string_value (dom);
1743 0 : GNUNET_break (NULL != val);
1744 0 : if (NULL != val)
1745 0 : new_family.details.subscription.trusted_domains[i]
1746 0 : = GNUNET_strdup (val);
1747 : }
1748 8 : break;
1749 : }
1750 1 : case TALER_MERCHANTDB_TFK_Discount:
1751 : {
1752 1 : json_t *edomains = json_object_get (tf->extra_data,
1753 : "expected_domains");
1754 : json_t *dom;
1755 : size_t i;
1756 :
1757 1 : new_family.kind = TALER_MERCHANT_CONTRACT_TOKEN_KIND_DISCOUNT;
1758 1 : new_family.critical = false;
1759 : new_family.details.discount.expected_domains_len
1760 1 : = json_array_size (edomains);
1761 1 : GNUNET_assert (new_family.details.discount.expected_domains_len
1762 : < UINT_MAX);
1763 : new_family.details.discount.expected_domains
1764 1 : = GNUNET_new_array (
1765 : new_family.details.discount.expected_domains_len,
1766 : char *);
1767 1 : json_array_foreach (edomains, i, dom)
1768 : {
1769 : const char *val;
1770 :
1771 0 : val = json_string_value (dom);
1772 0 : GNUNET_break (NULL != val);
1773 0 : if (NULL != val)
1774 0 : new_family.details.discount.expected_domains[i]
1775 0 : = GNUNET_strdup (val);
1776 : }
1777 1 : break;
1778 : }
1779 : }
1780 9 : GNUNET_array_append (oc->parse_choices.token_families,
1781 : oc->parse_choices.token_families_len,
1782 : new_family);
1783 9 : family = &oc->parse_choices.token_families[
1784 9 : oc->parse_choices.token_families_len - 1];
1785 : }
1786 9 : if (NULL == tfkd->pub.public_key)
1787 5 : return;
1788 4 : for (unsigned int i = 0; i<family->keys_len; i++)
1789 : {
1790 0 : if (TALER_token_issue_pub_cmp (&family->keys[i].pub,
1791 : &tfkd->pub))
1792 : {
1793 : /* A matching key is already in the list. */
1794 0 : return;
1795 : }
1796 : }
1797 :
1798 : {
1799 : struct TALER_MERCHANT_ContractTokenFamilyKey key;
1800 :
1801 4 : TALER_token_issue_pub_copy (&key.pub,
1802 : &tfkd->pub);
1803 4 : key.valid_after = tfkd->signature_validity_start;
1804 4 : key.valid_before = tfkd->signature_validity_end;
1805 4 : GNUNET_array_append (family->keys,
1806 : family->keys_len,
1807 : key);
1808 : }
1809 : }
1810 :
1811 :
1812 : /**
1813 : * Check if the token family with the given @a slug is already present in the
1814 : * list of token families for this order. If not, fetch its details and add it
1815 : * to the list.
1816 : *
1817 : * @param[in,out] oc order context
1818 : * @param slug slug of the token family
1819 : * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1820 : */
1821 : static enum GNUNET_GenericReturnValue
1822 5 : add_input_token_family (struct OrderContext *oc,
1823 : const char *slug)
1824 : {
1825 5 : struct GNUNET_TIME_Timestamp now = GNUNET_TIME_timestamp_get ();
1826 5 : struct GNUNET_TIME_Timestamp end = oc->parse_order.pay_deadline;
1827 : enum GNUNET_DB_QueryStatus qs;
1828 5 : enum TALER_ErrorCode ec = TALER_EC_INVALID; /* make compiler happy */
1829 5 : unsigned int http_status = 0; /* make compiler happy */
1830 :
1831 5 : qs = TMH_db->lookup_token_family_keys (TMH_db->cls,
1832 5 : oc->hc->instance->settings.id,
1833 : slug,
1834 : now,
1835 : end,
1836 : &add_family_key,
1837 : oc);
1838 5 : switch (qs)
1839 : {
1840 0 : case GNUNET_DB_STATUS_HARD_ERROR:
1841 0 : GNUNET_break (0);
1842 0 : http_status = MHD_HTTP_INTERNAL_SERVER_ERROR;
1843 0 : ec = TALER_EC_GENERIC_DB_FETCH_FAILED;
1844 0 : break;
1845 0 : case GNUNET_DB_STATUS_SOFT_ERROR:
1846 0 : GNUNET_break (0);
1847 0 : http_status = MHD_HTTP_INTERNAL_SERVER_ERROR;
1848 0 : ec = TALER_EC_GENERIC_DB_SOFT_FAILURE;
1849 0 : break;
1850 0 : case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
1851 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1852 : "Input token family slug %s unknown\n",
1853 : slug);
1854 0 : http_status = MHD_HTTP_NOT_FOUND;
1855 0 : ec = TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_TOKEN_FAMILY_SLUG_UNKNOWN;
1856 0 : break;
1857 5 : case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
1858 5 : return GNUNET_OK;
1859 : }
1860 0 : reply_with_error (oc,
1861 : http_status,
1862 : ec,
1863 : slug);
1864 0 : return GNUNET_SYSERR;
1865 : }
1866 :
1867 :
1868 : /**
1869 : * Find the index of a key in the @a family that is valid at
1870 : * the time @a valid_at.
1871 : *
1872 : * @param family to search
1873 : * @param valid_at time when the key must be valid
1874 : * @param[out] key_index index to initialize
1875 : * @return #GNUNET_OK if a matching key was found
1876 : */
1877 : static enum GNUNET_GenericReturnValue
1878 4 : find_key_index (struct TALER_MERCHANT_ContractTokenFamily *family,
1879 : struct GNUNET_TIME_Timestamp valid_at,
1880 : unsigned int *key_index)
1881 : {
1882 4 : for (unsigned int i = 0; i<family->keys_len; i++)
1883 : {
1884 4 : if ( (GNUNET_TIME_timestamp_cmp (family->keys[i].valid_after,
1885 : <=,
1886 4 : valid_at)) &&
1887 4 : (GNUNET_TIME_timestamp_cmp (family->keys[i].valid_before,
1888 : >=,
1889 : valid_at)) )
1890 : {
1891 : /* The token family and a matching key already exist. */
1892 4 : *key_index = i;
1893 4 : return GNUNET_OK;
1894 : }
1895 : }
1896 0 : return GNUNET_NO;
1897 : }
1898 :
1899 :
1900 : /**
1901 : * Create fresh key pair based on @a cipher_spec.
1902 : *
1903 : * @param cipher_spec which kind of key pair should we generate
1904 : * @param[out] priv set to new private key
1905 : * @param[out] pub set to new public key
1906 : * @return #GNUNET_OK on success
1907 : */
1908 : static enum GNUNET_GenericReturnValue
1909 4 : create_key (const char *cipher_spec,
1910 : struct TALER_TokenIssuePrivateKey *priv,
1911 : struct TALER_TokenIssuePublicKey *pub)
1912 : {
1913 : unsigned int len;
1914 : char dummy;
1915 :
1916 4 : if (0 == strcmp ("cs",
1917 : cipher_spec))
1918 : {
1919 0 : GNUNET_CRYPTO_blind_sign_keys_create (
1920 : &priv->private_key,
1921 : &pub->public_key,
1922 : GNUNET_CRYPTO_BSA_CS);
1923 0 : return GNUNET_OK;
1924 : }
1925 4 : if (1 ==
1926 4 : sscanf (cipher_spec,
1927 : "rsa(%u)%c",
1928 : &len,
1929 : &dummy))
1930 : {
1931 4 : GNUNET_CRYPTO_blind_sign_keys_create (
1932 : &priv->private_key,
1933 : &pub->public_key,
1934 : GNUNET_CRYPTO_BSA_RSA,
1935 : len);
1936 4 : return GNUNET_OK;
1937 : }
1938 0 : return GNUNET_SYSERR;
1939 : }
1940 :
1941 :
1942 : /**
1943 : * Check if the token family with the given @a slug is already present in the
1944 : * list of token families for this order. If not, fetch its details and add it
1945 : * to the list. Also checks if there is a public key with that expires after
1946 : * the payment deadline. If not, generates a new key pair and stores it in
1947 : * the database.
1948 : *
1949 : * @param[in,out] oc order context
1950 : * @param slug slug of the token family
1951 : * @param valid_at time when the token returned must be valid
1952 : * @param[out] key_index set to the index of the respective public
1953 : * key in the @a slug's token family keys array.
1954 : * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1955 : */
1956 : static enum GNUNET_GenericReturnValue
1957 8 : add_output_token_family (struct OrderContext *oc,
1958 : const char *slug,
1959 : struct GNUNET_TIME_Timestamp valid_at,
1960 : unsigned int *key_index)
1961 : {
1962 : struct TALER_MERCHANTDB_TokenFamilyKeyDetails key_details;
1963 : struct TALER_MERCHANT_ContractTokenFamily *family;
1964 : enum GNUNET_DB_QueryStatus qs;
1965 :
1966 8 : family = find_family (oc,
1967 : slug);
1968 12 : if ( (NULL != family) &&
1969 : (GNUNET_OK ==
1970 4 : find_key_index (family,
1971 : valid_at,
1972 : key_index)) )
1973 4 : return GNUNET_OK;
1974 4 : qs = TMH_db->lookup_token_family_key (TMH_db->cls,
1975 4 : oc->hc->instance->settings.id,
1976 : slug,
1977 : valid_at,
1978 : oc->parse_order.pay_deadline,
1979 : &key_details);
1980 4 : switch (qs)
1981 : {
1982 0 : case GNUNET_DB_STATUS_HARD_ERROR:
1983 0 : GNUNET_break (0);
1984 0 : reply_with_error (oc,
1985 : MHD_HTTP_INTERNAL_SERVER_ERROR,
1986 : TALER_EC_GENERIC_DB_FETCH_FAILED,
1987 : "lookup_token_family_key");
1988 0 : return GNUNET_SYSERR;
1989 0 : case GNUNET_DB_STATUS_SOFT_ERROR:
1990 : /* Single-statement transaction shouldn't possibly cause serialization errors.
1991 : Thus treating like a hard error. */
1992 0 : GNUNET_break (0);
1993 0 : reply_with_error (oc,
1994 : MHD_HTTP_INTERNAL_SERVER_ERROR,
1995 : TALER_EC_GENERIC_DB_SOFT_FAILURE,
1996 : "lookup_token_family_key");
1997 0 : return GNUNET_SYSERR;
1998 0 : case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
1999 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2000 : "Output token family slug %s unknown\n",
2001 : slug);
2002 0 : reply_with_error (oc,
2003 : MHD_HTTP_NOT_FOUND,
2004 : TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_TOKEN_FAMILY_SLUG_UNKNOWN,
2005 : slug);
2006 0 : return GNUNET_SYSERR;
2007 4 : case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
2008 4 : break;
2009 : }
2010 :
2011 4 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2012 : "Lookup of token family %s at %llu yielded %s\n",
2013 : slug,
2014 : (unsigned long long) valid_at.abs_time.abs_value_us,
2015 : NULL == key_details.pub.public_key ? "no key" : "a key");
2016 :
2017 4 : if (NULL == family)
2018 : {
2019 4 : add_family_key (oc,
2020 : &key_details);
2021 4 : family = find_family (oc,
2022 : slug);
2023 4 : GNUNET_assert (NULL != family);
2024 : }
2025 : /* we don't need the full family details anymore */
2026 4 : GNUNET_free (key_details.token_family.slug);
2027 4 : GNUNET_free (key_details.token_family.name);
2028 4 : GNUNET_free (key_details.token_family.description);
2029 4 : json_decref (key_details.token_family.description_i18n);
2030 4 : json_decref (key_details.token_family.extra_data);
2031 :
2032 4 : if (NULL != key_details.pub.public_key)
2033 : {
2034 : /* lookup_token_family_key must have found a matching key,
2035 : and it must have been added. Find and use the index. */
2036 0 : GNUNET_CRYPTO_blind_sign_pub_decref (key_details.pub.public_key);
2037 0 : GNUNET_CRYPTO_blind_sign_priv_decref (key_details.priv.private_key);
2038 0 : GNUNET_free (key_details.token_family.cipher_spec);
2039 0 : GNUNET_assert (GNUNET_OK ==
2040 : find_key_index (family,
2041 : valid_at,
2042 : key_index));
2043 0 : return GNUNET_OK;
2044 : }
2045 :
2046 : /* No suitable key exists, create one! */
2047 : {
2048 : struct TALER_MERCHANT_ContractTokenFamilyKey key;
2049 : enum GNUNET_DB_QueryStatus iqs;
2050 : struct TALER_TokenIssuePrivateKey token_priv;
2051 : struct GNUNET_TIME_Timestamp key_expires;
2052 : struct GNUNET_TIME_Timestamp round_start;
2053 :
2054 4 : if (GNUNET_OK !=
2055 4 : get_rounded_time_interval_down (
2056 : key_details.token_family.validity_granularity,
2057 : valid_at,
2058 : &round_start))
2059 : {
2060 0 : GNUNET_break (0);
2061 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2062 : "Unsupported validity granularity interval %s found in database for token family %s!\n",
2063 : GNUNET_TIME_relative2s (
2064 : key_details.token_family.validity_granularity,
2065 : false),
2066 : slug);
2067 0 : GNUNET_free (key_details.token_family.cipher_spec);
2068 0 : reply_with_error (oc,
2069 : MHD_HTTP_INTERNAL_SERVER_ERROR,
2070 : TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
2071 : "get_rounded_time_interval_down failed");
2072 0 : return GNUNET_SYSERR;
2073 : }
2074 4 : if (GNUNET_TIME_relative_cmp (
2075 : key_details.token_family.duration,
2076 : <,
2077 : GNUNET_TIME_relative_add (
2078 : key_details.token_family.validity_granularity,
2079 : key_details.token_family.start_offset)))
2080 : {
2081 0 : GNUNET_break (0);
2082 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2083 : "Inconsistent duration %s found in database for token family %s (below validity granularity plus start_offset)!\n",
2084 : GNUNET_TIME_relative2s (key_details.token_family.duration,
2085 : false),
2086 : slug);
2087 0 : GNUNET_free (key_details.token_family.cipher_spec);
2088 0 : reply_with_error (oc,
2089 : MHD_HTTP_INTERNAL_SERVER_ERROR,
2090 : TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
2091 : "duration, validty_granularity and start_offset inconsistent for token family");
2092 0 : return GNUNET_SYSERR;
2093 : }
2094 : key.valid_after
2095 4 : = GNUNET_TIME_timestamp_max (
2096 : GNUNET_TIME_absolute_to_timestamp (
2097 : GNUNET_TIME_absolute_subtract (
2098 : round_start.abs_time,
2099 : key_details.token_family.start_offset)),
2100 : key_details.token_family.valid_after);
2101 : key.valid_before
2102 4 : = GNUNET_TIME_timestamp_min (
2103 : GNUNET_TIME_absolute_to_timestamp (
2104 : GNUNET_TIME_absolute_add (
2105 : key.valid_after.abs_time,
2106 : key_details.token_family.duration)),
2107 : key_details.token_family.valid_before);
2108 4 : GNUNET_assert (GNUNET_OK ==
2109 : get_rounded_time_interval_down (
2110 : key_details.token_family.validity_granularity,
2111 : key.valid_before,
2112 : &key_expires));
2113 4 : if (GNUNET_TIME_timestamp_cmp (
2114 : key_expires,
2115 : ==,
2116 : round_start))
2117 : {
2118 : /* valid_before does not actually end after the
2119 : next rounded validity period would start;
2120 : determine next rounded validity period
2121 : start point and extend valid_before to cover
2122 : the full validity period */
2123 2 : GNUNET_assert (
2124 : GNUNET_OK ==
2125 : get_rounded_time_interval_up (
2126 : key_details.token_family.validity_granularity,
2127 : key.valid_before,
2128 : &key_expires));
2129 : /* This should basically always end up being key_expires */
2130 2 : key.valid_before = GNUNET_TIME_timestamp_max (key.valid_before,
2131 : key_expires);
2132 : }
2133 4 : if (GNUNET_OK !=
2134 4 : create_key (key_details.token_family.cipher_spec,
2135 : &token_priv,
2136 : &key.pub))
2137 : {
2138 0 : GNUNET_break (0);
2139 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2140 : "Unsupported cipher family %s found in database for token family %s!\n",
2141 : key_details.token_family.cipher_spec,
2142 : slug);
2143 0 : GNUNET_free (key_details.token_family.cipher_spec);
2144 0 : reply_with_error (oc,
2145 : MHD_HTTP_INTERNAL_SERVER_ERROR,
2146 : TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
2147 : "invalid cipher stored in local database for token family");
2148 0 : return GNUNET_SYSERR;
2149 : }
2150 4 : GNUNET_free (key_details.token_family.cipher_spec);
2151 4 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2152 : "Storing new key for slug %s of %s\n",
2153 : slug,
2154 : oc->hc->instance->settings.id);
2155 4 : iqs = TMH_db->insert_token_family_key (TMH_db->cls,
2156 4 : oc->hc->instance->settings.id,
2157 : slug,
2158 : &key.pub,
2159 : &token_priv,
2160 : key_expires,
2161 : key.valid_after,
2162 : key.valid_before);
2163 4 : GNUNET_CRYPTO_blind_sign_priv_decref (token_priv.private_key);
2164 4 : switch (iqs)
2165 : {
2166 0 : case GNUNET_DB_STATUS_HARD_ERROR:
2167 0 : GNUNET_break (0);
2168 0 : reply_with_error (oc,
2169 : MHD_HTTP_INTERNAL_SERVER_ERROR,
2170 : TALER_EC_GENERIC_DB_STORE_FAILED,
2171 : NULL);
2172 0 : return GNUNET_SYSERR;
2173 0 : case GNUNET_DB_STATUS_SOFT_ERROR:
2174 : /* Single-statement transaction shouldn't possibly cause serialization errors.
2175 : Thus treating like a hard error. */
2176 0 : GNUNET_break (0);
2177 0 : reply_with_error (oc,
2178 : MHD_HTTP_INTERNAL_SERVER_ERROR,
2179 : TALER_EC_GENERIC_DB_SOFT_FAILURE,
2180 : NULL);
2181 0 : return GNUNET_SYSERR;
2182 0 : case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
2183 0 : GNUNET_break (0);
2184 0 : reply_with_error (oc,
2185 : MHD_HTTP_INTERNAL_SERVER_ERROR,
2186 : TALER_EC_GENERIC_DB_STORE_FAILED,
2187 : NULL);
2188 0 : return GNUNET_SYSERR;
2189 4 : case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
2190 4 : break;
2191 : }
2192 4 : *key_index = family->keys_len;
2193 4 : GNUNET_array_append (family->keys,
2194 : family->keys_len,
2195 : key);
2196 : }
2197 4 : return GNUNET_OK;
2198 : }
2199 :
2200 :
2201 : /**
2202 : * Build JSON array that represents all of the token families
2203 : * in the contract.
2204 : *
2205 : * @param[in] oc v1-style order context
2206 : * @return JSON array with token families for the contract
2207 : */
2208 : static json_t *
2209 9 : output_token_families (struct OrderContext *oc)
2210 : {
2211 9 : json_t *token_families = json_object ();
2212 :
2213 9 : GNUNET_assert (NULL != token_families);
2214 18 : for (unsigned int i = 0; i<oc->parse_choices.token_families_len; i++)
2215 : {
2216 9 : const struct TALER_MERCHANT_ContractTokenFamily *family
2217 9 : = &oc->parse_choices.token_families[i];
2218 : json_t *jfamily;
2219 :
2220 9 : jfamily = TALER_MERCHANT_json_from_token_family (family);
2221 :
2222 9 : GNUNET_assert (jfamily != NULL);
2223 :
2224 9 : GNUNET_assert (0 ==
2225 : json_object_set_new (token_families,
2226 : family->slug,
2227 : jfamily));
2228 : }
2229 9 : return token_families;
2230 : }
2231 :
2232 :
2233 : /**
2234 : * Build JSON array that represents all of the contract choices
2235 : * in the contract.
2236 : *
2237 : * @param[in] oc v1-style order context
2238 : * @return JSON array with token families for the contract
2239 : */
2240 : static json_t *
2241 9 : output_contract_choices (struct OrderContext *oc)
2242 : {
2243 9 : json_t *choices = json_array ();
2244 :
2245 9 : GNUNET_assert (NULL != choices);
2246 19 : for (unsigned int i = 0; i<oc->parse_choices.choices_len; i++)
2247 : {
2248 10 : oc->parse_choices.choices[i].max_fee =
2249 10 : oc->set_max_fee.details.v1.max_fees[i];
2250 10 : GNUNET_assert (0 == json_array_append_new (
2251 : choices,
2252 : TALER_MERCHANT_json_from_contract_choice (
2253 : &oc->parse_choices.choices[i],
2254 : false)));
2255 : }
2256 :
2257 9 : return choices;
2258 : }
2259 :
2260 :
2261 : /**
2262 : * Serialize order into @a oc->serialize_order.contract,
2263 : * ready to be stored in the database. Upon success, continue
2264 : * processing with check_contract().
2265 : *
2266 : * @param[in,out] oc order context
2267 : */
2268 : static void
2269 69 : phase_serialize_order (struct OrderContext *oc)
2270 : {
2271 69 : const struct TALER_MERCHANTDB_InstanceSettings *settings =
2272 69 : &oc->hc->instance->settings;
2273 : json_t *merchant;
2274 :
2275 69 : merchant = GNUNET_JSON_PACK (
2276 : GNUNET_JSON_pack_string ("name",
2277 : settings->name),
2278 : GNUNET_JSON_pack_allow_null (
2279 : GNUNET_JSON_pack_string ("website",
2280 : settings->website)),
2281 : GNUNET_JSON_pack_allow_null (
2282 : GNUNET_JSON_pack_string ("email",
2283 : settings->email)),
2284 : GNUNET_JSON_pack_allow_null (
2285 : GNUNET_JSON_pack_string ("logo",
2286 : settings->logo)));
2287 69 : GNUNET_assert (NULL != merchant);
2288 : {
2289 : json_t *loca;
2290 :
2291 : /* Handle merchant address */
2292 69 : loca = settings->address;
2293 69 : if (NULL != loca)
2294 : {
2295 69 : loca = json_deep_copy (loca);
2296 69 : GNUNET_assert (NULL != loca);
2297 69 : GNUNET_assert (0 ==
2298 : json_object_set_new (merchant,
2299 : "address",
2300 : loca));
2301 : }
2302 : }
2303 : {
2304 : json_t *juri;
2305 :
2306 : /* Handle merchant jurisdiction */
2307 69 : juri = settings->jurisdiction;
2308 69 : if (NULL != juri)
2309 : {
2310 69 : juri = json_deep_copy (juri);
2311 69 : GNUNET_assert (NULL != juri);
2312 69 : GNUNET_assert (0 ==
2313 : json_object_set_new (merchant,
2314 : "jurisdiction",
2315 : juri));
2316 : }
2317 : }
2318 :
2319 69 : oc->serialize_order.contract = GNUNET_JSON_PACK (
2320 : GNUNET_JSON_pack_int64 ("version",
2321 : oc->parse_order.version),
2322 : GNUNET_JSON_pack_string ("summary",
2323 : oc->parse_order.summary),
2324 : GNUNET_JSON_pack_allow_null (
2325 : GNUNET_JSON_pack_object_incref (
2326 : "summary_i18n",
2327 : (json_t *) oc->parse_order.summary_i18n)),
2328 : GNUNET_JSON_pack_allow_null (
2329 : GNUNET_JSON_pack_string ("public_reorder_url",
2330 : oc->parse_order.public_reorder_url)),
2331 : GNUNET_JSON_pack_allow_null (
2332 : GNUNET_JSON_pack_string ("fulfillment_message",
2333 : oc->parse_order.fulfillment_message)),
2334 : GNUNET_JSON_pack_allow_null (
2335 : GNUNET_JSON_pack_object_incref (
2336 : "fulfillment_message_i18n",
2337 : (json_t *) oc->parse_order.fulfillment_message_i18n)),
2338 : GNUNET_JSON_pack_allow_null (
2339 : GNUNET_JSON_pack_string ("fulfillment_url",
2340 : oc->parse_order.fulfillment_url)),
2341 : GNUNET_JSON_pack_allow_null (
2342 : GNUNET_JSON_pack_uint64 ("minimum_age",
2343 : oc->parse_order.minimum_age)),
2344 : GNUNET_JSON_pack_allow_null (
2345 : GNUNET_JSON_pack_uint64 ("default_money_pot",
2346 : oc->parse_order.order_default_money_pot)),
2347 : GNUNET_JSON_pack_array_incref ("products",
2348 : oc->merge_inventory.products),
2349 : GNUNET_JSON_pack_data_auto ("h_wire",
2350 : &oc->select_wire_method.wm->h_wire),
2351 : GNUNET_JSON_pack_string ("wire_method",
2352 : oc->select_wire_method.wm->wire_method),
2353 : GNUNET_JSON_pack_string ("order_id",
2354 : oc->parse_order.order_id),
2355 : GNUNET_JSON_pack_timestamp ("timestamp",
2356 : oc->parse_order.timestamp),
2357 : GNUNET_JSON_pack_timestamp ("pay_deadline",
2358 : oc->parse_order.pay_deadline),
2359 : GNUNET_JSON_pack_timestamp ("wire_transfer_deadline",
2360 : oc->parse_order.wire_deadline),
2361 : GNUNET_JSON_pack_allow_null (
2362 : GNUNET_JSON_pack_timestamp ("delivery_date",
2363 : oc->parse_order.delivery_date)),
2364 : GNUNET_JSON_pack_allow_null (
2365 : GNUNET_JSON_pack_object_incref (
2366 : "delivery_location",
2367 : (json_t *) oc->parse_order.delivery_location)),
2368 : GNUNET_JSON_pack_string ("merchant_base_url",
2369 : oc->parse_order.merchant_base_url),
2370 : GNUNET_JSON_pack_object_steal ("merchant",
2371 : merchant),
2372 : GNUNET_JSON_pack_data_auto ("merchant_pub",
2373 : &oc->hc->instance->merchant_pub),
2374 : GNUNET_JSON_pack_array_incref ("exchanges",
2375 : oc->select_wire_method.exchanges),
2376 : GNUNET_JSON_pack_allow_null (
2377 : GNUNET_JSON_pack_object_incref ("extra",
2378 : (json_t *) oc->parse_order.extra))
2379 : );
2380 :
2381 : {
2382 : json_t *xtra;
2383 :
2384 69 : switch (oc->parse_order.version)
2385 : {
2386 60 : case TALER_MERCHANT_CONTRACT_VERSION_0:
2387 60 : xtra = GNUNET_JSON_PACK (
2388 : TALER_JSON_pack_amount ("max_fee",
2389 : &oc->set_max_fee.details.v0.max_fee),
2390 : TALER_JSON_pack_amount ("amount",
2391 : &oc->parse_order.details.v0.brutto));
2392 60 : break;
2393 9 : case TALER_MERCHANT_CONTRACT_VERSION_1:
2394 : {
2395 9 : json_t *token_families = output_token_families (oc);
2396 9 : json_t *choices = output_contract_choices (oc);
2397 :
2398 9 : if ( (NULL == token_families) ||
2399 : (NULL == choices) )
2400 : {
2401 0 : GNUNET_break (0);
2402 0 : return;
2403 : }
2404 9 : xtra = GNUNET_JSON_PACK (
2405 : GNUNET_JSON_pack_array_steal ("choices",
2406 : choices),
2407 : GNUNET_JSON_pack_object_steal ("token_families",
2408 : token_families));
2409 9 : break;
2410 : }
2411 0 : default:
2412 0 : GNUNET_assert (0);
2413 : }
2414 69 : GNUNET_assert (0 ==
2415 : json_object_update (oc->serialize_order.contract,
2416 : xtra));
2417 69 : json_decref (xtra);
2418 : }
2419 :
2420 :
2421 : /* Pack does not work here, because it doesn't set zero-values for timestamps */
2422 69 : GNUNET_assert (0 ==
2423 : json_object_set_new (oc->serialize_order.contract,
2424 : "refund_deadline",
2425 : GNUNET_JSON_from_timestamp (
2426 : oc->parse_order.refund_deadline)));
2427 : /* auto_refund should only be set if it is not 0 */
2428 69 : if (! GNUNET_TIME_relative_is_zero (oc->parse_order.auto_refund))
2429 : {
2430 : /* Pack does not work here, because it sets zero-values for relative times */
2431 0 : GNUNET_assert (0 ==
2432 : json_object_set_new (oc->serialize_order.contract,
2433 : "auto_refund",
2434 : GNUNET_JSON_from_time_rel (
2435 : oc->parse_order.auto_refund)));
2436 : }
2437 :
2438 69 : oc->phase++;
2439 : }
2440 :
2441 :
2442 : /* ***************** ORDER_PHASE_SET_MAX_FEE **************** */
2443 :
2444 :
2445 : /**
2446 : * Set @a max_fee in @a oc based on @a max_stefan_fee value if not overridden
2447 : * by @a client_fee. If neither is set, set the fee to zero using currency
2448 : * from @a brutto.
2449 : *
2450 : * @param[in,out] oc order context
2451 : * @param brutto brutto amount to compute fee for
2452 : * @param client_fee client-given fee override (or invalid)
2453 : * @param max_stefan_fee maximum STEFAN fee of any exchange
2454 : * @param max_fee set to the maximum stefan fee
2455 : */
2456 : static void
2457 70 : compute_fee (struct OrderContext *oc,
2458 : const struct TALER_Amount *brutto,
2459 : const struct TALER_Amount *client_fee,
2460 : const struct TALER_Amount *max_stefan_fee,
2461 : struct TALER_Amount *max_fee)
2462 : {
2463 70 : const struct TALER_MERCHANTDB_InstanceSettings *settings
2464 70 : = &oc->hc->instance->settings;
2465 :
2466 70 : if (GNUNET_OK ==
2467 70 : TALER_amount_is_valid (client_fee))
2468 : {
2469 0 : *max_fee = *client_fee;
2470 0 : return;
2471 : }
2472 70 : if ( (settings->use_stefan) &&
2473 66 : (NULL != max_stefan_fee) &&
2474 : (GNUNET_OK ==
2475 66 : TALER_amount_is_valid (max_stefan_fee)) )
2476 : {
2477 66 : *max_fee = *max_stefan_fee;
2478 66 : return;
2479 : }
2480 4 : GNUNET_assert (
2481 : GNUNET_OK ==
2482 : TALER_amount_set_zero (brutto->currency,
2483 : max_fee));
2484 : }
2485 :
2486 :
2487 : /**
2488 : * Initialize "set_max_fee" in @a oc based on STEFAN value or client
2489 : * preference. Upon success, continue processing in next phase.
2490 : *
2491 : * @param[in,out] oc order context
2492 : */
2493 : static void
2494 69 : phase_set_max_fee (struct OrderContext *oc)
2495 : {
2496 69 : switch (oc->parse_order.version)
2497 : {
2498 60 : case TALER_MERCHANT_CONTRACT_VERSION_0:
2499 60 : compute_fee (oc,
2500 60 : &oc->parse_order.details.v0.brutto,
2501 60 : &oc->parse_order.details.v0.max_fee,
2502 60 : &oc->set_exchanges.details.v0.max_stefan_fee,
2503 : &oc->set_max_fee.details.v0.max_fee);
2504 60 : break;
2505 9 : case TALER_MERCHANT_CONTRACT_VERSION_1:
2506 : oc->set_max_fee.details.v1.max_fees
2507 9 : = GNUNET_new_array (oc->parse_choices.choices_len,
2508 : struct TALER_Amount);
2509 19 : for (unsigned int i = 0; i<oc->parse_choices.choices_len; i++)
2510 4 : compute_fee (oc,
2511 10 : &oc->parse_choices.choices[i].amount,
2512 10 : &oc->parse_choices.choices[i].max_fee,
2513 10 : NULL != oc->set_exchanges.details.v1.max_stefan_fees
2514 6 : ? &oc->set_exchanges.details.v1.max_stefan_fees[i]
2515 : : NULL,
2516 10 : &oc->set_max_fee.details.v1.max_fees[i]);
2517 9 : break;
2518 0 : default:
2519 0 : GNUNET_break (0);
2520 0 : break;
2521 : }
2522 69 : oc->phase++;
2523 69 : }
2524 :
2525 :
2526 : /* ***************** ORDER_PHASE_SELECT_WIRE_METHOD **************** */
2527 :
2528 : /**
2529 : * Check that the @a brutto amount is at or below the
2530 : * limits we have for the respective wire method candidate.
2531 : *
2532 : * @param wmc wire method candidate to check
2533 : * @param brutto amount to check
2534 : * @return true if the amount is OK, false if it is too high
2535 : */
2536 : static bool
2537 67 : check_limits (struct WireMethodCandidate *wmc,
2538 : const struct TALER_Amount *brutto)
2539 : {
2540 67 : for (unsigned int i = 0; i<wmc->num_total_exchange_limits; i++)
2541 : {
2542 67 : const struct TALER_Amount *total_exchange_limit
2543 67 : = &wmc->total_exchange_limits[i];
2544 :
2545 67 : if (GNUNET_OK !=
2546 67 : TALER_amount_cmp_currency (brutto,
2547 : total_exchange_limit))
2548 0 : continue;
2549 67 : if (1 !=
2550 67 : TALER_amount_cmp (brutto,
2551 : total_exchange_limit))
2552 67 : return true;
2553 : }
2554 0 : return false;
2555 : }
2556 :
2557 :
2558 : /**
2559 : * Phase to select a wire method that will be acceptable for the order.
2560 : * If none is "perfect" (allows all choices), might jump back to the
2561 : * previous phase to force "/keys" downloads to see if that helps.
2562 : *
2563 : * @param[in,out] oc order context
2564 : */
2565 : static void
2566 65 : phase_select_wire_method (struct OrderContext *oc)
2567 : {
2568 : const struct TALER_Amount *ea;
2569 65 : struct WireMethodCandidate *best = NULL;
2570 65 : unsigned int max_choices = 0;
2571 65 : unsigned int want_choices = 0;
2572 :
2573 65 : for (struct WireMethodCandidate *wmc = oc->add_payment_details.wmc_head;
2574 131 : NULL != wmc;
2575 66 : wmc = wmc->next)
2576 : {
2577 66 : unsigned int num_choices = 0;
2578 :
2579 66 : switch (oc->parse_order.version)
2580 : {
2581 61 : case TALER_MERCHANT_CONTRACT_VERSION_0:
2582 61 : want_choices = 1;
2583 61 : ea = &oc->parse_order.details.v0.brutto;
2584 122 : if (TALER_amount_is_zero (ea) ||
2585 61 : check_limits (wmc,
2586 : ea))
2587 61 : num_choices++;
2588 61 : break;
2589 5 : case TALER_MERCHANT_CONTRACT_VERSION_1:
2590 5 : want_choices = oc->parse_choices.choices_len;
2591 11 : for (unsigned int i = 0; i<oc->parse_choices.choices_len; i++)
2592 : {
2593 6 : ea = &oc->parse_choices.choices[i].amount;
2594 12 : if (TALER_amount_is_zero (ea) ||
2595 6 : check_limits (wmc,
2596 : ea))
2597 6 : num_choices++;
2598 : }
2599 5 : break;
2600 0 : default:
2601 0 : GNUNET_assert (0);
2602 : }
2603 66 : if (num_choices > max_choices)
2604 : {
2605 65 : best = wmc;
2606 65 : max_choices = num_choices;
2607 : }
2608 : }
2609 :
2610 65 : if ( (want_choices > max_choices) &&
2611 0 : (oc->set_exchanges.promising_exchange) &&
2612 0 : (! oc->set_exchanges.forced_reload) )
2613 : {
2614 0 : oc->set_exchanges.exchange_ok = false;
2615 : /* Not all choices in the contract can work with these
2616 : exchanges, try again with forcing /keys download */
2617 0 : for (struct WireMethodCandidate *wmc = oc->add_payment_details.wmc_head;
2618 0 : NULL != wmc;
2619 0 : wmc = wmc->next)
2620 : {
2621 0 : json_array_clear (wmc->exchanges);
2622 0 : GNUNET_array_grow (wmc->total_exchange_limits,
2623 : wmc->num_total_exchange_limits,
2624 : 0);
2625 : }
2626 0 : oc->phase = ORDER_PHASE_SET_EXCHANGES;
2627 0 : return;
2628 : }
2629 :
2630 65 : if ( (NULL == best) &&
2631 0 : (NULL != oc->parse_request.payment_target) )
2632 : {
2633 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2634 : "Cannot create order: lacking suitable exchanges for payment target `%s'\n",
2635 : oc->parse_request.payment_target);
2636 0 : reply_with_error (
2637 : oc,
2638 : MHD_HTTP_CONFLICT,
2639 : TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_NO_EXCHANGES_FOR_WIRE_METHOD,
2640 : oc->parse_request.payment_target);
2641 0 : return;
2642 : }
2643 :
2644 65 : if (NULL == best)
2645 : {
2646 : /* We actually do not have ANY workable exchange(s) */
2647 0 : reply_with_error (
2648 : oc,
2649 : MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS,
2650 : TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_AMOUNT_EXCEEDS_LEGAL_LIMITS,
2651 : NULL);
2652 0 : return;
2653 : }
2654 :
2655 65 : if (want_choices > max_choices)
2656 : {
2657 : /* Some choices are unpayable */
2658 0 : GNUNET_log (
2659 : GNUNET_ERROR_TYPE_WARNING,
2660 : "Creating order, but some choices do not work with the selected wire method\n");
2661 : }
2662 65 : if ( (0 == json_array_size (best->exchanges)) &&
2663 0 : (oc->add_payment_details.need_exchange) )
2664 : {
2665 : /* We did not find any reasonable exchange */
2666 0 : GNUNET_log (
2667 : GNUNET_ERROR_TYPE_WARNING,
2668 : "Creating order, but only for choices without payment\n");
2669 : }
2670 :
2671 : oc->select_wire_method.wm
2672 65 : = best->wm;
2673 : oc->select_wire_method.exchanges
2674 65 : = json_incref (best->exchanges);
2675 65 : oc->phase++;
2676 : }
2677 :
2678 :
2679 : /* ***************** ORDER_PHASE_SET_EXCHANGES **************** */
2680 :
2681 : /**
2682 : * Exchange `/keys` processing is done, resume handling
2683 : * the order.
2684 : *
2685 : * @param[in,out] oc context to resume
2686 : */
2687 : static void
2688 65 : resume_with_keys (struct OrderContext *oc)
2689 : {
2690 65 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2691 : "Resuming order processing after /keys downloads\n");
2692 65 : GNUNET_assert (GNUNET_YES == oc->suspended);
2693 65 : GNUNET_CONTAINER_DLL_remove (oc_head,
2694 : oc_tail,
2695 : oc);
2696 65 : oc->suspended = GNUNET_NO;
2697 65 : MHD_resume_connection (oc->connection);
2698 65 : TALER_MHD_daemon_trigger (); /* we resumed, kick MHD */
2699 65 : }
2700 :
2701 :
2702 : /**
2703 : * Given a @a brutto amount for exchange with @a keys, set the
2704 : * @a stefan_fee. Note that @a stefan_fee is updated to the maximum
2705 : * of the input and the computed fee.
2706 : *
2707 : * @param[in,out] keys exchange keys
2708 : * @param brutto some brutto amount the client is to pay
2709 : * @param[in,out] stefan_fee set to STEFAN fee to be paid by the merchant
2710 : */
2711 : static void
2712 66 : compute_stefan_fee (const struct TALER_EXCHANGE_Keys *keys,
2713 : const struct TALER_Amount *brutto,
2714 : struct TALER_Amount *stefan_fee)
2715 : {
2716 : struct TALER_Amount net;
2717 :
2718 66 : if (GNUNET_SYSERR !=
2719 66 : TALER_EXCHANGE_keys_stefan_b2n (keys,
2720 : brutto,
2721 : &net))
2722 : {
2723 : struct TALER_Amount fee;
2724 :
2725 66 : TALER_EXCHANGE_keys_stefan_round (keys,
2726 : &net);
2727 66 : if (-1 == TALER_amount_cmp (brutto,
2728 : &net))
2729 : {
2730 : /* brutto < netto! */
2731 : /* => after rounding, there is no real difference */
2732 0 : net = *brutto;
2733 : }
2734 66 : GNUNET_assert (0 <=
2735 : TALER_amount_subtract (&fee,
2736 : brutto,
2737 : &net));
2738 66 : if ( (GNUNET_OK !=
2739 66 : TALER_amount_is_valid (stefan_fee)) ||
2740 0 : (-1 == TALER_amount_cmp (stefan_fee,
2741 : &fee)) )
2742 : {
2743 66 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2744 : "Updated STEFAN-based fee to %s\n",
2745 : TALER_amount2s (&fee));
2746 66 : *stefan_fee = fee;
2747 : }
2748 : }
2749 66 : }
2750 :
2751 :
2752 : /**
2753 : * Update MAX STEFAN fees based on @a keys.
2754 : *
2755 : * @param[in,out] oc order context to update
2756 : * @param keys keys to derive STEFAN fees from
2757 : */
2758 : static void
2759 65 : update_stefan (struct OrderContext *oc,
2760 : const struct TALER_EXCHANGE_Keys *keys)
2761 : {
2762 65 : switch (oc->parse_order.version)
2763 : {
2764 60 : case TALER_MERCHANT_CONTRACT_VERSION_0:
2765 60 : compute_stefan_fee (keys,
2766 60 : &oc->parse_order.details.v0.brutto,
2767 : &oc->set_exchanges.details.v0.max_stefan_fee);
2768 60 : break;
2769 5 : case TALER_MERCHANT_CONTRACT_VERSION_1:
2770 : oc->set_exchanges.details.v1.max_stefan_fees
2771 5 : = GNUNET_new_array (oc->parse_choices.choices_len,
2772 : struct TALER_Amount);
2773 11 : for (unsigned int i = 0; i<oc->parse_choices.choices_len; i++)
2774 6 : if (0 == strcasecmp (keys->currency,
2775 6 : oc->parse_choices.choices[i].amount.currency))
2776 6 : compute_stefan_fee (keys,
2777 6 : &oc->parse_choices.choices[i].amount,
2778 6 : &oc->set_exchanges.details.v1.max_stefan_fees[i]);
2779 5 : break;
2780 0 : default:
2781 0 : GNUNET_assert (0);
2782 : }
2783 65 : }
2784 :
2785 :
2786 : /**
2787 : * Check our KYC status at all exchanges as our current limit is
2788 : * too low and we failed to create an order.
2789 : *
2790 : * @param oc order context
2791 : * @param wmc wire method candidate to notify for
2792 : * @param exchange_url exchange to notify about
2793 : */
2794 : static void
2795 0 : notify_kyc_required (const struct OrderContext *oc,
2796 : const struct WireMethodCandidate *wmc,
2797 : const char *exchange_url)
2798 : {
2799 0 : struct GNUNET_DB_EventHeaderP es = {
2800 0 : .size = htons (sizeof (es)),
2801 0 : .type = htons (TALER_DBEVENT_MERCHANT_EXCHANGE_KYC_RULE_TRIGGERED)
2802 : };
2803 : char *hws;
2804 : char *extra;
2805 :
2806 0 : hws = GNUNET_STRINGS_data_to_string_alloc (
2807 0 : &wmc->wm->h_wire,
2808 : sizeof (wmc->wm->h_wire));
2809 :
2810 0 : GNUNET_asprintf (&extra,
2811 : "%s %s",
2812 : hws,
2813 : exchange_url);
2814 0 : TMH_db->event_notify (TMH_db->cls,
2815 : &es,
2816 : extra,
2817 0 : strlen (extra) + 1);
2818 0 : GNUNET_free (extra);
2819 0 : GNUNET_free (hws);
2820 0 : }
2821 :
2822 :
2823 : /**
2824 : * Checks the limits that apply for this @a exchange and
2825 : * the @a wmc and if the exchange is acceptable at all, adds it
2826 : * to the list of exchanges for the @a wmc.
2827 : *
2828 : * @param oc context of the order
2829 : * @param exchange internal handle for the exchange
2830 : * @param exchange_url base URL of this exchange
2831 : * @param wmc wire method to evaluate this exchange for
2832 : * @return true if the exchange is acceptable for the contract
2833 : */
2834 : static bool
2835 66 : get_acceptable (struct OrderContext *oc,
2836 : const struct TMH_Exchange *exchange,
2837 : const char *exchange_url,
2838 : struct WireMethodCandidate *wmc)
2839 : {
2840 66 : const struct TALER_Amount *max_needed = NULL;
2841 66 : unsigned int priority = 42; /* make compiler happy */
2842 : json_t *j_exchange;
2843 : enum TMH_ExchangeStatus res;
2844 : struct TALER_Amount max_amount;
2845 :
2846 66 : for (unsigned int i = 0; i<oc->add_payment_details.num_max_choice_limits; i++)
2847 : {
2848 66 : struct TALER_Amount *val = &oc->add_payment_details.max_choice_limits[i];
2849 :
2850 66 : if (0 == strcasecmp (val->currency,
2851 : TMH_EXCHANGES_get_currency (exchange)))
2852 : {
2853 66 : max_needed = val;
2854 66 : break;
2855 : }
2856 : }
2857 66 : if (NULL == max_needed)
2858 : {
2859 : /* exchange currency not relevant for any of our choices, skip it */
2860 0 : return false;
2861 : }
2862 :
2863 66 : max_amount = *max_needed;
2864 66 : res = TMH_exchange_check_debit (
2865 66 : oc->hc->instance->settings.id,
2866 : exchange,
2867 : wmc->wm,
2868 : &max_amount);
2869 66 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2870 : "Exchange %s evaluated at %d with max %s\n",
2871 : exchange_url,
2872 : res,
2873 : TALER_amount2s (&max_amount));
2874 66 : if (TALER_amount_is_zero (&max_amount))
2875 : {
2876 0 : if (! TALER_amount_is_zero (max_needed))
2877 : {
2878 : /* Trigger re-checking the current deposit limit when
2879 : * paying non-zero amount with zero deposit limit */
2880 0 : notify_kyc_required (oc,
2881 : wmc,
2882 : exchange_url);
2883 : }
2884 : /* If deposit is impossible, we don't list the
2885 : * exchange in the contract terms. */
2886 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2887 : "Exchange %s deposit limit is zero, skipping it\n",
2888 : exchange_url);
2889 0 : return false;
2890 : }
2891 66 : switch (res)
2892 : {
2893 66 : case TMH_ES_OK:
2894 : case TMH_ES_RETRY_OK:
2895 66 : priority = 1024; /* high */
2896 66 : oc->set_exchanges.exchange_ok = true;
2897 66 : break;
2898 0 : case TMH_ES_NO_ACC:
2899 0 : if (oc->set_exchanges.forced_reload)
2900 0 : priority = 0; /* fresh negative response */
2901 : else
2902 0 : priority = 512; /* stale negative response */
2903 0 : break;
2904 0 : case TMH_ES_NO_CURR:
2905 0 : if (oc->set_exchanges.forced_reload)
2906 0 : priority = 0; /* fresh negative response */
2907 : else
2908 0 : priority = 512; /* stale negative response */
2909 0 : break;
2910 0 : case TMH_ES_NO_KEYS:
2911 0 : if (oc->set_exchanges.forced_reload)
2912 0 : priority = 256; /* fresh, no accounts yet */
2913 : else
2914 0 : priority = 768; /* stale, no accounts yet */
2915 0 : break;
2916 0 : case TMH_ES_NO_ACC_RETRY_OK:
2917 0 : if (oc->set_exchanges.forced_reload)
2918 : {
2919 0 : priority = 0; /* fresh negative response */
2920 : }
2921 : else
2922 : {
2923 0 : oc->set_exchanges.promising_exchange = true;
2924 0 : priority = 512; /* stale negative response */
2925 : }
2926 0 : break;
2927 0 : case TMH_ES_NO_CURR_RETRY_OK:
2928 0 : if (oc->set_exchanges.forced_reload)
2929 0 : priority = 0; /* fresh negative response */
2930 : else
2931 0 : priority = 512; /* stale negative response */
2932 0 : break;
2933 0 : case TMH_ES_NO_KEYS_RETRY_OK:
2934 0 : if (oc->set_exchanges.forced_reload)
2935 : {
2936 0 : priority = 256; /* fresh, no accounts yet */
2937 : }
2938 : else
2939 : {
2940 0 : oc->set_exchanges.promising_exchange = true;
2941 0 : priority = 768; /* stale, no accounts yet */
2942 : }
2943 0 : break;
2944 : }
2945 66 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2946 : "Exchange %s deposit limit is %s, adding it!\n",
2947 : exchange_url,
2948 : TALER_amount2s (&max_amount));
2949 :
2950 66 : j_exchange = GNUNET_JSON_PACK (
2951 : GNUNET_JSON_pack_string ("url",
2952 : exchange_url),
2953 : GNUNET_JSON_pack_uint64 ("priority",
2954 : priority),
2955 : TALER_JSON_pack_amount ("max_contribution",
2956 : &max_amount),
2957 : GNUNET_JSON_pack_data_auto ("master_pub",
2958 : TMH_EXCHANGES_get_master_pub (exchange)));
2959 66 : GNUNET_assert (NULL != j_exchange);
2960 : /* Add exchange to list of exchanges for this wire method
2961 : candidate */
2962 66 : GNUNET_assert (0 ==
2963 : json_array_append_new (wmc->exchanges,
2964 : j_exchange));
2965 66 : add_to_currency_vector (&wmc->total_exchange_limits,
2966 : &wmc->num_total_exchange_limits,
2967 : &max_amount,
2968 : max_needed);
2969 66 : return true;
2970 : }
2971 :
2972 :
2973 : /**
2974 : * Function called with the result of a #TMH_EXCHANGES_keys4exchange()
2975 : * operation.
2976 : *
2977 : * @param cls closure with our `struct RekeyExchange *`
2978 : * @param keys the keys of the exchange
2979 : * @param exchange representation of the exchange
2980 : */
2981 : static void
2982 65 : keys_cb (
2983 : void *cls,
2984 : struct TALER_EXCHANGE_Keys *keys,
2985 : struct TMH_Exchange *exchange)
2986 : {
2987 65 : struct RekeyExchange *rx = cls;
2988 65 : struct OrderContext *oc = rx->oc;
2989 65 : const struct TALER_MERCHANTDB_InstanceSettings *settings =
2990 65 : &oc->hc->instance->settings;
2991 65 : bool applicable = false;
2992 :
2993 65 : rx->fo = NULL;
2994 65 : GNUNET_CONTAINER_DLL_remove (oc->set_exchanges.pending_reload_head,
2995 : oc->set_exchanges.pending_reload_tail,
2996 : rx);
2997 65 : if (NULL == keys)
2998 : {
2999 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
3000 : "Failed to download %skeys\n",
3001 : rx->url);
3002 0 : oc->set_exchanges.promising_exchange = true;
3003 0 : goto cleanup;
3004 : }
3005 65 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3006 : "Got response for %skeys\n",
3007 : rx->url);
3008 :
3009 : /* Evaluate the use of this exchange for each wire method candidate */
3010 130 : for (unsigned int j = 0; j<keys->accounts_len; j++)
3011 : {
3012 65 : struct TALER_FullPayto full_payto = keys->accounts[j].fpayto_uri;
3013 65 : char *wire_method = TALER_payto_get_method (full_payto.full_payto);
3014 :
3015 65 : for (struct WireMethodCandidate *wmc = oc->add_payment_details.wmc_head;
3016 131 : NULL != wmc;
3017 66 : wmc = wmc->next)
3018 : {
3019 66 : if (0 == strcmp (wmc->wm->wire_method,
3020 : wire_method) )
3021 : {
3022 66 : applicable |= get_acceptable (oc,
3023 : exchange,
3024 66 : rx->url,
3025 : wmc);
3026 : }
3027 : }
3028 65 : GNUNET_free (wire_method);
3029 : }
3030 65 : if (applicable &&
3031 65 : settings->use_stefan)
3032 65 : update_stefan (oc,
3033 : keys);
3034 0 : cleanup:
3035 65 : GNUNET_free (rx->url);
3036 65 : GNUNET_free (rx);
3037 65 : if (NULL != oc->set_exchanges.pending_reload_head)
3038 0 : return;
3039 65 : resume_with_keys (oc);
3040 : }
3041 :
3042 :
3043 : /**
3044 : * Force re-downloading of /keys from @a exchange,
3045 : * we currently have no acceptable exchange, so we
3046 : * should try to get one.
3047 : *
3048 : * @param cls closure with our `struct OrderContext`
3049 : * @param url base URL of the exchange
3050 : * @param exchange internal handle for the exchange
3051 : */
3052 : static void
3053 65 : get_exchange_keys (void *cls,
3054 : const char *url,
3055 : const struct TMH_Exchange *exchange)
3056 : {
3057 65 : struct OrderContext *oc = cls;
3058 : struct RekeyExchange *rx;
3059 :
3060 65 : rx = GNUNET_new (struct RekeyExchange);
3061 65 : rx->oc = oc;
3062 65 : rx->url = GNUNET_strdup (url);
3063 65 : GNUNET_CONTAINER_DLL_insert (oc->set_exchanges.pending_reload_head,
3064 : oc->set_exchanges.pending_reload_tail,
3065 : rx);
3066 65 : if (oc->set_exchanges.forced_reload)
3067 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3068 : "Forcing download of %skeys\n",
3069 : url);
3070 130 : rx->fo = TMH_EXCHANGES_keys4exchange (url,
3071 65 : oc->set_exchanges.forced_reload,
3072 : &keys_cb,
3073 : rx);
3074 65 : }
3075 :
3076 :
3077 : /**
3078 : * Task run when we are timing out on /keys and will just
3079 : * proceed with what we got.
3080 : *
3081 : * @param cls our `struct OrderContext *` to resume
3082 : */
3083 : static void
3084 0 : wakeup_timeout (void *cls)
3085 : {
3086 0 : struct OrderContext *oc = cls;
3087 :
3088 0 : oc->set_exchanges.wakeup_task = NULL;
3089 0 : GNUNET_assert (GNUNET_YES == oc->suspended);
3090 0 : GNUNET_CONTAINER_DLL_remove (oc_head,
3091 : oc_tail,
3092 : oc);
3093 0 : MHD_resume_connection (oc->connection);
3094 0 : oc->suspended = GNUNET_NO;
3095 0 : TALER_MHD_daemon_trigger (); /* we resumed, kick MHD */
3096 0 : }
3097 :
3098 :
3099 : /**
3100 : * Set list of acceptable exchanges in @a oc. Upon success, continues
3101 : * processing with add_payment_details().
3102 : *
3103 : * @param[in,out] oc order context
3104 : * @return true to suspend execution
3105 : */
3106 : static bool
3107 134 : phase_set_exchanges (struct OrderContext *oc)
3108 : {
3109 134 : if (NULL != oc->set_exchanges.wakeup_task)
3110 : {
3111 65 : GNUNET_SCHEDULER_cancel (oc->set_exchanges.wakeup_task);
3112 65 : oc->set_exchanges.wakeup_task = NULL;
3113 : }
3114 :
3115 134 : if (! oc->add_payment_details.need_exchange)
3116 : {
3117 : /* Total amount is zero, so we don't actually need exchanges! */
3118 4 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3119 : "Order total is zero, no need for exchanges\n");
3120 4 : oc->select_wire_method.exchanges = json_array ();
3121 4 : GNUNET_assert (NULL != oc->select_wire_method.exchanges);
3122 : /* Pick first one, doesn't matter as the amount is zero */
3123 4 : oc->select_wire_method.wm = oc->hc->instance->wm_head;
3124 4 : oc->phase = ORDER_PHASE_SET_MAX_FEE;
3125 4 : return false;
3126 : }
3127 130 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3128 : "Trying to find exchanges\n");
3129 130 : if (NULL == oc->set_exchanges.pending_reload_head)
3130 : {
3131 130 : if (! oc->set_exchanges.exchanges_tried)
3132 : {
3133 65 : oc->set_exchanges.exchanges_tried = true;
3134 : oc->set_exchanges.keys_timeout
3135 65 : = GNUNET_TIME_relative_to_absolute (MAX_KEYS_WAIT);
3136 65 : TMH_exchange_get_trusted (&get_exchange_keys,
3137 : oc);
3138 : }
3139 65 : else if ( (! oc->set_exchanges.forced_reload) &&
3140 65 : (oc->set_exchanges.promising_exchange) &&
3141 0 : (! oc->set_exchanges.exchange_ok) )
3142 : {
3143 0 : for (struct WireMethodCandidate *wmc = oc->add_payment_details.wmc_head;
3144 0 : NULL != wmc;
3145 0 : wmc = wmc->next)
3146 0 : GNUNET_break (0 ==
3147 : json_array_clear (wmc->exchanges));
3148 : /* Try one more time with forcing /keys download */
3149 0 : oc->set_exchanges.forced_reload = true;
3150 0 : TMH_exchange_get_trusted (&get_exchange_keys,
3151 : oc);
3152 : }
3153 : }
3154 130 : if (GNUNET_TIME_absolute_is_past (oc->set_exchanges.keys_timeout))
3155 : {
3156 : struct RekeyExchange *rx;
3157 :
3158 0 : while (NULL != (rx = oc->set_exchanges.pending_reload_head))
3159 : {
3160 0 : GNUNET_CONTAINER_DLL_remove (oc->set_exchanges.pending_reload_head,
3161 : oc->set_exchanges.pending_reload_tail,
3162 : rx);
3163 0 : TMH_EXCHANGES_keys4exchange_cancel (rx->fo);
3164 0 : GNUNET_free (rx->url);
3165 0 : GNUNET_free (rx);
3166 : }
3167 : }
3168 130 : if (NULL != oc->set_exchanges.pending_reload_head)
3169 : {
3170 65 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3171 : "Still trying to (re)load %skeys\n",
3172 : oc->set_exchanges.pending_reload_head->url);
3173 : oc->set_exchanges.wakeup_task
3174 65 : = GNUNET_SCHEDULER_add_at (oc->set_exchanges.keys_timeout,
3175 : &wakeup_timeout,
3176 : oc);
3177 65 : MHD_suspend_connection (oc->connection);
3178 65 : oc->suspended = GNUNET_YES;
3179 65 : GNUNET_CONTAINER_DLL_insert (oc_head,
3180 : oc_tail,
3181 : oc);
3182 65 : return true; /* reloads pending */
3183 : }
3184 65 : oc->phase++;
3185 65 : return false;
3186 : }
3187 :
3188 :
3189 : /* ***************** ORDER_PHASE_ADD_PAYMENT_DETAILS **************** */
3190 :
3191 : /**
3192 : * Process the @a payment_target and add the details of how the
3193 : * order could be paid to @a order. On success, continue
3194 : * processing with add_payment_fees().
3195 : *
3196 : * @param[in,out] oc order context
3197 : */
3198 : static void
3199 71 : phase_add_payment_details (struct OrderContext *oc)
3200 : {
3201 : /* First, determine the maximum amounts that could be paid per currency */
3202 71 : switch (oc->parse_order.version)
3203 : {
3204 62 : case TALER_MERCHANT_CONTRACT_VERSION_0:
3205 62 : GNUNET_array_append (oc->add_payment_details.max_choice_limits,
3206 : oc->add_payment_details.num_max_choice_limits,
3207 : oc->parse_order.details.v0.brutto);
3208 62 : if (! TALER_amount_is_zero (
3209 62 : &oc->parse_order.details.v0.brutto))
3210 : {
3211 62 : oc->add_payment_details.need_exchange = true;
3212 : }
3213 62 : break;
3214 9 : case TALER_MERCHANT_CONTRACT_VERSION_1:
3215 19 : for (unsigned int i = 0; i<oc->parse_choices.choices_len; i++)
3216 : {
3217 10 : const struct TALER_Amount *amount
3218 10 : = &oc->parse_choices.choices[i].amount;
3219 10 : bool found = false;
3220 :
3221 10 : if (! TALER_amount_is_zero (amount))
3222 : {
3223 6 : oc->add_payment_details.need_exchange = true;
3224 : }
3225 10 : for (unsigned int j = 0; j<oc->add_payment_details.num_max_choice_limits;
3226 0 : j++)
3227 : {
3228 1 : struct TALER_Amount *mx = &oc->add_payment_details.max_choice_limits[j];
3229 1 : if (GNUNET_YES ==
3230 1 : TALER_amount_cmp_currency (mx,
3231 : amount))
3232 : {
3233 1 : TALER_amount_max (mx,
3234 : mx,
3235 : amount);
3236 1 : found = true;
3237 1 : break;
3238 : }
3239 : }
3240 10 : if (! found)
3241 : {
3242 9 : GNUNET_array_append (oc->add_payment_details.max_choice_limits,
3243 : oc->add_payment_details.num_max_choice_limits,
3244 : *amount);
3245 : }
3246 : }
3247 9 : break;
3248 0 : default:
3249 0 : GNUNET_assert (0);
3250 : }
3251 :
3252 : /* Then, create a candidate for each available wire method */
3253 71 : for (struct TMH_WireMethod *wm = oc->hc->instance->wm_head;
3254 149 : NULL != wm;
3255 78 : wm = wm->next)
3256 : {
3257 : struct WireMethodCandidate *wmc;
3258 :
3259 : /* Locate wire method that has a matching payment target */
3260 78 : if (! wm->active)
3261 6 : continue; /* ignore inactive methods */
3262 72 : if ( (NULL != oc->parse_request.payment_target) &&
3263 19 : (0 != strcasecmp (oc->parse_request.payment_target,
3264 19 : wm->wire_method) ) )
3265 2 : continue; /* honor client preference */
3266 70 : wmc = GNUNET_new (struct WireMethodCandidate);
3267 70 : wmc->wm = wm;
3268 70 : wmc->exchanges = json_array ();
3269 70 : GNUNET_assert (NULL != wmc->exchanges);
3270 70 : GNUNET_CONTAINER_DLL_insert (oc->add_payment_details.wmc_head,
3271 : oc->add_payment_details.wmc_tail,
3272 : wmc);
3273 : }
3274 :
3275 71 : if (NULL == oc->add_payment_details.wmc_head)
3276 : {
3277 2 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
3278 : "No wire method available for instance '%s'\n",
3279 : oc->hc->instance->settings.id);
3280 2 : reply_with_error (oc,
3281 : MHD_HTTP_NOT_FOUND,
3282 : TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_INSTANCE_CONFIGURATION_LACKS_WIRE,
3283 : oc->parse_request.payment_target);
3284 2 : return;
3285 : }
3286 :
3287 : /* next, we'll evaluate available exchanges */
3288 69 : oc->phase++;
3289 : }
3290 :
3291 :
3292 : /* ***************** ORDER_PHASE_MERGE_INVENTORY **************** */
3293 :
3294 :
3295 : /**
3296 : * Helper function to sort uint64_t array with qsort().
3297 : *
3298 : * @param a pointer to element to compare
3299 : * @param b pointer to element to compare
3300 : * @return 0 on equal, -1 on smaller, 1 on larger
3301 : */
3302 : static int
3303 900 : uint64_cmp (const void *a,
3304 : const void *b)
3305 : {
3306 900 : uint64_t ua = *(const uint64_t *) a;
3307 900 : uint64_t ub = *(const uint64_t *) b;
3308 :
3309 900 : if (ua < ub)
3310 0 : return -1;
3311 900 : if (ua > ub)
3312 0 : return 1;
3313 900 : return 0;
3314 : }
3315 :
3316 :
3317 : /**
3318 : * Merge the inventory products into products, querying the
3319 : * database about the details of those products. Upon success,
3320 : * continue processing by calling add_payment_details().
3321 : *
3322 : * @param[in,out] oc order context to process
3323 : */
3324 : static void
3325 75 : phase_merge_inventory (struct OrderContext *oc)
3326 75 : {
3327 75 : uint64_t pots[oc->parse_order.products_len + 1];
3328 75 : size_t pots_off = 0;
3329 :
3330 75 : if (0 != oc->parse_order.order_default_money_pot)
3331 0 : pots[pots_off++] = oc->parse_order.order_default_money_pot;
3332 : /**
3333 : * parse_request.inventory_products => instructions to add products to contract terms
3334 : * parse_order.products => contains products that are not from the backend-managed inventory.
3335 : */
3336 75 : oc->merge_inventory.products = json_array ();
3337 80 : for (size_t i = 0; i<oc->parse_order.products_len; i++)
3338 : {
3339 5 : GNUNET_assert (
3340 : 0 ==
3341 : json_array_append_new (
3342 : oc->merge_inventory.products,
3343 : TALER_MERCHANT_product_sold_serialize (&oc->parse_order.products[i])));
3344 5 : if (0 != oc->parse_order.products[i].product_money_pot)
3345 0 : pots[pots_off++] = oc->parse_order.products[i].product_money_pot;
3346 : }
3347 :
3348 : /* make sure pots array only has distinct elements */
3349 75 : qsort (pots,
3350 : sizeof (uint64_t),
3351 : pots_off,
3352 : &uint64_cmp);
3353 : {
3354 75 : size_t e = 0;
3355 :
3356 75 : for (size_t i = 1; i<pots_off; i++)
3357 : {
3358 0 : if (pots[e] != pots[i])
3359 0 : pots[++e] = pots[i];
3360 : }
3361 75 : if (pots_off > 0)
3362 0 : e++;
3363 75 : pots_off = e;
3364 : }
3365 :
3366 : /* check if all money pots exist; note that we do NOT treat
3367 : the inventory products to this check, as (1) the foreign key
3368 : constraint should ensure this, and (2) if the money pot
3369 : were deleted (concurrently), the value is specified to be
3370 : considered 0 (aka none) and so we can proceed anyway. */
3371 75 : if (pots_off > 0)
3372 : {
3373 : enum GNUNET_DB_QueryStatus qs;
3374 : uint64_t pot_missing;
3375 :
3376 0 : qs = TMH_db->check_money_pots (TMH_db->cls,
3377 0 : oc->hc->instance->settings.id,
3378 : pots_off,
3379 : pots,
3380 : &pot_missing);
3381 0 : switch (qs)
3382 : {
3383 0 : case GNUNET_DB_STATUS_HARD_ERROR:
3384 : case GNUNET_DB_STATUS_SOFT_ERROR:
3385 0 : GNUNET_break (0);
3386 0 : reply_with_error (oc,
3387 : MHD_HTTP_INTERNAL_SERVER_ERROR,
3388 : TALER_EC_GENERIC_DB_FETCH_FAILED,
3389 : "check_money_pots");
3390 0 : return;
3391 0 : case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
3392 : /* great, good case! */
3393 0 : break;
3394 0 : case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
3395 : {
3396 : char mstr[32];
3397 :
3398 0 : GNUNET_snprintf (mstr,
3399 : sizeof (mstr),
3400 : "%llu",
3401 : (unsigned long long) pot_missing);
3402 0 : reply_with_error (oc,
3403 : MHD_HTTP_NOT_FOUND,
3404 : TALER_EC_MERCHANT_GENERIC_MONEY_POT_UNKNOWN,
3405 : mstr);
3406 0 : return;
3407 : }
3408 : }
3409 : }
3410 :
3411 : /* Populate products from inventory product array and database */
3412 : {
3413 75 : GNUNET_assert (NULL != oc->merge_inventory.products);
3414 90 : for (unsigned int i = 0; i<oc->parse_request.inventory_products_length; i++)
3415 : {
3416 19 : struct InventoryProduct *ip
3417 19 : = &oc->parse_request.inventory_products[i];
3418 : struct TALER_MERCHANTDB_ProductDetails pd;
3419 : enum GNUNET_DB_QueryStatus qs;
3420 19 : size_t num_categories = 0;
3421 19 : uint64_t *categories = NULL;
3422 :
3423 19 : qs = TMH_db->lookup_product (TMH_db->cls,
3424 19 : oc->hc->instance->settings.id,
3425 : ip->product_id,
3426 : &pd,
3427 : &num_categories,
3428 : &categories);
3429 19 : if (qs <= 0)
3430 : {
3431 2 : enum TALER_ErrorCode ec = TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
3432 2 : unsigned int http_status = 0;
3433 :
3434 2 : switch (qs)
3435 : {
3436 0 : case GNUNET_DB_STATUS_HARD_ERROR:
3437 0 : GNUNET_break (0);
3438 0 : http_status = MHD_HTTP_INTERNAL_SERVER_ERROR;
3439 0 : ec = TALER_EC_GENERIC_DB_FETCH_FAILED;
3440 0 : break;
3441 0 : case GNUNET_DB_STATUS_SOFT_ERROR:
3442 0 : GNUNET_break (0);
3443 0 : http_status = MHD_HTTP_INTERNAL_SERVER_ERROR;
3444 0 : ec = TALER_EC_GENERIC_DB_SOFT_FAILURE;
3445 0 : break;
3446 2 : case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
3447 2 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
3448 : "Product %s from order unknown\n",
3449 : ip->product_id);
3450 2 : http_status = MHD_HTTP_NOT_FOUND;
3451 2 : ec = TALER_EC_MERCHANT_GENERIC_PRODUCT_UNKNOWN;
3452 2 : break;
3453 0 : case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
3454 : /* case listed to make compilers happy */
3455 0 : GNUNET_assert (0);
3456 : }
3457 2 : reply_with_error (oc,
3458 : http_status,
3459 : ec,
3460 : ip->product_id);
3461 4 : return;
3462 : }
3463 17 : GNUNET_free (categories);
3464 : oc->parse_order.minimum_age
3465 17 : = GNUNET_MAX (oc->parse_order.minimum_age,
3466 : pd.minimum_age);
3467 : {
3468 : const char *eparam;
3469 :
3470 17 : if ( (! ip->quantity_missing) &&
3471 0 : (ip->quantity > (uint64_t) INT64_MAX) )
3472 : {
3473 0 : GNUNET_break_op (0);
3474 0 : reply_with_error (oc,
3475 : MHD_HTTP_BAD_REQUEST,
3476 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
3477 : "quantity");
3478 0 : TALER_MERCHANTDB_product_details_free (&pd);
3479 2 : return;
3480 : }
3481 17 : if (GNUNET_OK !=
3482 17 : TALER_MERCHANT_vk_process_quantity_inputs (
3483 : TALER_MERCHANT_VK_QUANTITY,
3484 17 : pd.allow_fractional_quantity,
3485 17 : ip->quantity_missing,
3486 17 : (int64_t) ip->quantity,
3487 17 : ip->unit_quantity_missing,
3488 : ip->unit_quantity,
3489 : &ip->quantity,
3490 : &ip->quantity_frac,
3491 : &eparam))
3492 : {
3493 2 : GNUNET_break_op (0);
3494 2 : reply_with_error (oc,
3495 : MHD_HTTP_BAD_REQUEST,
3496 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
3497 : eparam);
3498 2 : TALER_MERCHANTDB_product_details_free (&pd);
3499 2 : return;
3500 : }
3501 : }
3502 : {
3503 30 : struct TALER_MERCHANT_ProductSold ps = {
3504 15 : .product_id = (char *) ip->product_id,
3505 15 : .product_name = pd.product_name,
3506 15 : .description = pd.description,
3507 15 : .description_i18n = pd.description_i18n,
3508 15 : .unit_quantity.integer = ip->quantity,
3509 15 : .unit_quantity.fractional = ip->quantity_frac,
3510 15 : .prices_length = pd.price_array_length,
3511 15 : .prices = GNUNET_new_array (pd.price_array_length,
3512 : struct TALER_Amount),
3513 15 : .prices_are_net = pd.price_is_net,
3514 15 : .image = pd.image,
3515 15 : .taxes = pd.taxes,
3516 : .delivery_date = oc->parse_order.delivery_date,
3517 15 : .product_money_pot = pd.money_pot_id,
3518 15 : .unit = pd.unit,
3519 :
3520 : };
3521 : json_t *p;
3522 : char unit_quantity_buf[64];
3523 :
3524 30 : for (size_t j = 0; j<pd.price_array_length; j++)
3525 : {
3526 : struct TALER_Amount atomic_amount;
3527 :
3528 15 : TALER_amount_set_zero (pd.price_array[j].currency,
3529 : &atomic_amount);
3530 15 : atomic_amount.fraction = 1;
3531 15 : GNUNET_assert (
3532 : GNUNET_OK ==
3533 : TALER_MERCHANT_amount_multiply_by_quantity (
3534 : &ps.prices[j],
3535 : &pd.price_array[j],
3536 : &ps.unit_quantity,
3537 : TALER_MERCHANT_ROUND_UP,
3538 : &atomic_amount));
3539 : }
3540 :
3541 15 : TALER_MERCHANT_vk_format_fractional_string (
3542 : TALER_MERCHANT_VK_QUANTITY,
3543 : ip->quantity,
3544 : ip->quantity_frac,
3545 : sizeof (unit_quantity_buf),
3546 : unit_quantity_buf);
3547 15 : if (0 != pd.money_pot_id)
3548 0 : pots[pots_off++] = pd.money_pot_id;
3549 15 : p = TALER_MERCHANT_product_sold_serialize (&ps);
3550 15 : GNUNET_assert (NULL != p);
3551 15 : GNUNET_free (ps.prices);
3552 15 : GNUNET_assert (0 ==
3553 : json_array_append_new (oc->merge_inventory.products,
3554 : p));
3555 : }
3556 15 : TALER_MERCHANTDB_product_details_free (&pd);
3557 : }
3558 : }
3559 :
3560 : /* check if final product list is well-formed */
3561 71 : if (! TMH_products_array_valid (oc->merge_inventory.products))
3562 : {
3563 0 : GNUNET_break_op (0);
3564 0 : reply_with_error (oc,
3565 : MHD_HTTP_BAD_REQUEST,
3566 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
3567 : "order:products");
3568 0 : return;
3569 : }
3570 71 : oc->phase++;
3571 : }
3572 :
3573 :
3574 : /* ***************** ORDER_PHASE_PARSE_CHOICES **************** */
3575 :
3576 : #ifdef HAVE_DONAU_DONAU_SERVICE_H
3577 : /**
3578 : * Callback function that is called for each donau instance.
3579 : * It simply adds the provided donau_url to the json.
3580 : *
3581 : * @param cls closure with our `struct TALER_MERCHANT_ContractOutput *`
3582 : * @param donau_url the URL of the donau instance
3583 : */
3584 : static void
3585 : add_donau_url (void *cls,
3586 : const char *donau_url)
3587 : {
3588 : struct TALER_MERCHANT_ContractOutput *output = cls;
3589 :
3590 : GNUNET_array_append (output->details.donation_receipt.donau_urls,
3591 : output->details.donation_receipt.donau_urls_len,
3592 : GNUNET_strdup (donau_url));
3593 : }
3594 :
3595 :
3596 : /**
3597 : * Add the donau output to the contract output.
3598 : *
3599 : * @param oc order context
3600 : * @param output contract output to add donau URLs to
3601 : */
3602 : static bool
3603 : add_donau_output (struct OrderContext *oc,
3604 : struct TALER_MERCHANT_ContractOutput *output)
3605 : {
3606 : enum GNUNET_DB_QueryStatus qs;
3607 :
3608 : qs = TMH_db->select_donau_instances_filtered (
3609 : TMH_db->cls,
3610 : output->details.donation_receipt.amount.currency,
3611 : &add_donau_url,
3612 : output);
3613 : if (qs < 0)
3614 : {
3615 : GNUNET_break (0);
3616 : reply_with_error (oc,
3617 : MHD_HTTP_INTERNAL_SERVER_ERROR,
3618 : TALER_EC_GENERIC_DB_FETCH_FAILED,
3619 : "donau url parsing db call");
3620 : for (unsigned int i = 0;
3621 : i < output->details.donation_receipt.donau_urls_len;
3622 : i++)
3623 : GNUNET_free (output->details.donation_receipt.donau_urls[i]);
3624 : GNUNET_array_grow (output->details.donation_receipt.donau_urls,
3625 : output->details.donation_receipt.donau_urls_len,
3626 : 0);
3627 : return false;
3628 : }
3629 : return true;
3630 : }
3631 :
3632 :
3633 : #endif
3634 :
3635 : /**
3636 : * Parse contract choices. Upon success, continue
3637 : * processing with merge_inventory().
3638 : *
3639 : * @param[in,out] oc order context
3640 : */
3641 : static void
3642 75 : phase_parse_choices (struct OrderContext *oc)
3643 : {
3644 : const json_t *jchoices;
3645 :
3646 75 : switch (oc->parse_order.version)
3647 : {
3648 66 : case TALER_MERCHANT_CONTRACT_VERSION_0:
3649 66 : oc->phase++;
3650 66 : return;
3651 9 : case TALER_MERCHANT_CONTRACT_VERSION_1:
3652 : /* handle below */
3653 9 : break;
3654 0 : default:
3655 0 : GNUNET_assert (0);
3656 : }
3657 :
3658 9 : jchoices = oc->parse_order.details.v1.choices;
3659 :
3660 9 : if (! json_is_array (jchoices))
3661 0 : GNUNET_assert (0);
3662 9 : if (0 == json_array_size (jchoices))
3663 : {
3664 0 : reply_with_error (oc,
3665 : MHD_HTTP_BAD_REQUEST,
3666 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
3667 : "choices");
3668 0 : return;
3669 : }
3670 9 : GNUNET_array_grow (oc->parse_choices.choices,
3671 : oc->parse_choices.choices_len,
3672 : json_array_size (jchoices));
3673 19 : for (unsigned int i = 0; i<oc->parse_choices.choices_len; i++)
3674 : {
3675 10 : struct TALER_MERCHANT_ContractChoice *choice
3676 10 : = &oc->parse_choices.choices[i];
3677 : const char *error_name;
3678 : unsigned int error_line;
3679 : const json_t *jinputs;
3680 : const json_t *joutputs;
3681 : bool no_fee;
3682 : struct GNUNET_JSON_Specification spec[] = {
3683 10 : TALER_JSON_spec_amount_any ("amount",
3684 : &choice->amount),
3685 10 : GNUNET_JSON_spec_mark_optional (
3686 : TALER_JSON_spec_amount_any ("max_fee",
3687 : &choice->max_fee),
3688 : &no_fee),
3689 10 : GNUNET_JSON_spec_mark_optional (
3690 : GNUNET_JSON_spec_string_copy ("description",
3691 : &choice->description),
3692 : NULL),
3693 10 : GNUNET_JSON_spec_mark_optional (
3694 : GNUNET_JSON_spec_object_copy ("description_i18n",
3695 : &choice->description_i18n),
3696 : NULL),
3697 10 : GNUNET_JSON_spec_mark_optional (
3698 : GNUNET_JSON_spec_array_const ("inputs",
3699 : &jinputs),
3700 : NULL),
3701 10 : GNUNET_JSON_spec_mark_optional (
3702 : GNUNET_JSON_spec_array_const ("outputs",
3703 : &joutputs),
3704 : NULL),
3705 10 : GNUNET_JSON_spec_end ()
3706 : };
3707 : enum GNUNET_GenericReturnValue ret;
3708 :
3709 10 : ret = GNUNET_JSON_parse (json_array_get (jchoices,
3710 : i),
3711 : spec,
3712 : &error_name,
3713 : &error_line);
3714 10 : if (GNUNET_OK != ret)
3715 : {
3716 0 : GNUNET_break_op (0);
3717 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
3718 : "Choice parsing failed: %s:%u\n",
3719 : error_name,
3720 : error_line);
3721 0 : reply_with_error (oc,
3722 : MHD_HTTP_BAD_REQUEST,
3723 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
3724 : "choice");
3725 0 : return;
3726 : }
3727 10 : if ( (! no_fee) &&
3728 : (GNUNET_OK !=
3729 0 : TALER_amount_cmp_currency (&choice->amount,
3730 0 : &choice->max_fee)) )
3731 : {
3732 0 : GNUNET_break_op (0);
3733 0 : GNUNET_JSON_parse_free (spec);
3734 0 : reply_with_error (oc,
3735 : MHD_HTTP_BAD_REQUEST,
3736 : TALER_EC_GENERIC_CURRENCY_MISMATCH,
3737 : "different currencies used for 'max_fee' and 'amount' currency");
3738 0 : return;
3739 : }
3740 :
3741 10 : if (! TMH_test_exchange_configured_for_currency (
3742 10 : choice->amount.currency))
3743 : {
3744 0 : GNUNET_break_op (0);
3745 0 : GNUNET_JSON_parse_free (spec);
3746 0 : reply_with_error (oc,
3747 : MHD_HTTP_CONFLICT,
3748 : TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_NO_EXCHANGE_FOR_CURRENCY,
3749 0 : choice->amount.currency);
3750 0 : return;
3751 : }
3752 :
3753 10 : if (NULL != jinputs)
3754 : {
3755 : const json_t *jinput;
3756 : size_t idx;
3757 18 : json_array_foreach ((json_t *) jinputs, idx, jinput)
3758 : {
3759 9 : struct TALER_MERCHANT_ContractInput input = {
3760 : .details.token.count = 1
3761 : };
3762 :
3763 9 : if (GNUNET_OK !=
3764 9 : TALER_MERCHANT_parse_choice_input ((json_t *) jinput,
3765 : &input,
3766 : idx,
3767 : true))
3768 : {
3769 0 : GNUNET_break_op (0);
3770 0 : reply_with_error (oc,
3771 : MHD_HTTP_BAD_REQUEST,
3772 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
3773 : "input");
3774 0 : return;
3775 : }
3776 :
3777 9 : switch (input.type)
3778 : {
3779 0 : case TALER_MERCHANT_CONTRACT_INPUT_TYPE_INVALID:
3780 0 : GNUNET_assert (0);
3781 : break;
3782 9 : case TALER_MERCHANT_CONTRACT_INPUT_TYPE_TOKEN:
3783 : /* Ignore inputs tokens with 'count' field set to 0 */
3784 9 : if (0 == input.details.token.count)
3785 9 : continue;
3786 :
3787 5 : if (GNUNET_OK !=
3788 5 : add_input_token_family (oc,
3789 : input.details.token.token_family_slug))
3790 :
3791 : {
3792 0 : GNUNET_break_op (0);
3793 0 : reply_with_error (oc,
3794 : MHD_HTTP_BAD_REQUEST,
3795 : TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_TOKEN_FAMILY_SLUG_UNKNOWN,
3796 : input.details.token.token_family_slug);
3797 0 : return;
3798 : }
3799 :
3800 5 : GNUNET_array_append (choice->inputs,
3801 : choice->inputs_len,
3802 : input);
3803 5 : continue;
3804 : }
3805 0 : GNUNET_assert (0);
3806 : }
3807 : }
3808 :
3809 10 : if (NULL != joutputs)
3810 : {
3811 : const json_t *joutput;
3812 : size_t idx;
3813 17 : json_array_foreach ((json_t *) joutputs, idx, joutput)
3814 : {
3815 8 : struct TALER_MERCHANT_ContractOutput output = {
3816 : .details.token.count = 1
3817 : };
3818 :
3819 8 : if (GNUNET_OK !=
3820 8 : TALER_MERCHANT_parse_choice_output ((json_t *) joutput,
3821 : &output,
3822 : idx,
3823 : true))
3824 : {
3825 0 : reply_with_error (oc,
3826 : MHD_HTTP_BAD_REQUEST,
3827 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
3828 : "output");
3829 0 : return;
3830 : }
3831 :
3832 8 : switch (output.type)
3833 : {
3834 0 : case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_INVALID:
3835 0 : GNUNET_assert (0);
3836 : break;
3837 0 : case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_DONATION_RECEIPT:
3838 : #ifdef HAVE_DONAU_DONAU_SERVICE_H
3839 : output.details.donation_receipt.amount = choice->amount;
3840 : if (! add_donau_output (oc,
3841 : &output))
3842 : {
3843 : GNUNET_break (0);
3844 : return;
3845 : }
3846 : GNUNET_array_append (choice->outputs,
3847 : choice->outputs_len,
3848 : output);
3849 : #endif
3850 8 : continue;
3851 8 : case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_TOKEN:
3852 : /* Ignore inputs tokens with 'count' field set to 0 */
3853 8 : if (0 == output.details.token.count)
3854 0 : continue;
3855 :
3856 8 : if (0 == output.details.token.valid_at.abs_time.abs_value_us)
3857 : output.details.token.valid_at
3858 8 : = GNUNET_TIME_timestamp_get ();
3859 8 : if (GNUNET_OK !=
3860 8 : add_output_token_family (oc,
3861 : output.details.token.token_family_slug,
3862 : output.details.token.valid_at,
3863 : &output.details.token.key_index))
3864 :
3865 : {
3866 : /* note: reply_with_error() was already called */
3867 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
3868 : "Could not handle output token family `%s'\n",
3869 : output.details.token.token_family_slug);
3870 0 : return;
3871 : }
3872 :
3873 8 : GNUNET_array_append (choice->outputs,
3874 : choice->outputs_len,
3875 : output);
3876 8 : continue;
3877 : }
3878 0 : GNUNET_assert (0);
3879 : }
3880 : }
3881 : }
3882 9 : oc->phase++;
3883 : }
3884 :
3885 :
3886 : /* ***************** ORDER_PHASE_PARSE_ORDER **************** */
3887 :
3888 :
3889 : /**
3890 : * Parse the order field of the request. Upon success, continue
3891 : * processing with parse_choices().
3892 : *
3893 : * @param[in,out] oc order context
3894 : */
3895 : static void
3896 77 : phase_parse_order (struct OrderContext *oc)
3897 : {
3898 77 : const struct TALER_MERCHANTDB_InstanceSettings *settings =
3899 77 : &oc->hc->instance->settings;
3900 77 : const char *merchant_base_url = NULL;
3901 77 : uint64_t version = 0;
3902 77 : const json_t *jmerchant = NULL;
3903 77 : const json_t *products = NULL;
3904 77 : const char *order_id = NULL;
3905 : struct GNUNET_JSON_Specification spec[] = {
3906 77 : GNUNET_JSON_spec_mark_optional (
3907 : GNUNET_JSON_spec_uint64 ("version",
3908 : &version),
3909 : NULL),
3910 77 : GNUNET_JSON_spec_string ("summary",
3911 : &oc->parse_order.summary),
3912 77 : GNUNET_JSON_spec_mark_optional (
3913 : GNUNET_JSON_spec_array_const ("products",
3914 : &products),
3915 : NULL),
3916 77 : GNUNET_JSON_spec_mark_optional (
3917 : GNUNET_JSON_spec_object_const ("summary_i18n",
3918 : &oc->parse_order.summary_i18n),
3919 : NULL),
3920 77 : GNUNET_JSON_spec_mark_optional (
3921 : GNUNET_JSON_spec_string ("order_id",
3922 : &order_id),
3923 : NULL),
3924 77 : GNUNET_JSON_spec_mark_optional (
3925 : GNUNET_JSON_spec_string ("fulfillment_message",
3926 : &oc->parse_order.fulfillment_message),
3927 : NULL),
3928 77 : GNUNET_JSON_spec_mark_optional (
3929 : GNUNET_JSON_spec_object_const ("fulfillment_message_i18n",
3930 : &oc->parse_order.fulfillment_message_i18n),
3931 : NULL),
3932 77 : GNUNET_JSON_spec_mark_optional (
3933 : GNUNET_JSON_spec_string ("fulfillment_url",
3934 : &oc->parse_order.fulfillment_url),
3935 : NULL),
3936 77 : GNUNET_JSON_spec_mark_optional (
3937 : GNUNET_JSON_spec_string ("public_reorder_url",
3938 : &oc->parse_order.public_reorder_url),
3939 : NULL),
3940 77 : GNUNET_JSON_spec_mark_optional (
3941 : TALER_JSON_spec_web_url ("merchant_base_url",
3942 : &merchant_base_url),
3943 : NULL),
3944 : /* For sanity check, this field must NOT be present */
3945 77 : GNUNET_JSON_spec_mark_optional (
3946 : GNUNET_JSON_spec_object_const ("merchant",
3947 : &jmerchant),
3948 : NULL),
3949 77 : GNUNET_JSON_spec_mark_optional (
3950 : GNUNET_JSON_spec_timestamp ("timestamp",
3951 : &oc->parse_order.timestamp),
3952 : NULL),
3953 77 : GNUNET_JSON_spec_mark_optional (
3954 : GNUNET_JSON_spec_timestamp ("refund_deadline",
3955 : &oc->parse_order.refund_deadline),
3956 : NULL),
3957 77 : GNUNET_JSON_spec_mark_optional (
3958 : GNUNET_JSON_spec_timestamp ("pay_deadline",
3959 : &oc->parse_order.pay_deadline),
3960 : NULL),
3961 77 : GNUNET_JSON_spec_mark_optional (
3962 : GNUNET_JSON_spec_timestamp ("wire_transfer_deadline",
3963 : &oc->parse_order.wire_deadline),
3964 : NULL),
3965 77 : GNUNET_JSON_spec_mark_optional (
3966 : GNUNET_JSON_spec_object_const ("delivery_location",
3967 : &oc->parse_order.delivery_location),
3968 : NULL),
3969 77 : GNUNET_JSON_spec_mark_optional (
3970 : GNUNET_JSON_spec_timestamp ("delivery_date",
3971 : &oc->parse_order.delivery_date),
3972 : NULL),
3973 77 : GNUNET_JSON_spec_mark_optional (
3974 : GNUNET_JSON_spec_uint32 ("minimum_age",
3975 : &oc->parse_order.minimum_age),
3976 : NULL),
3977 77 : GNUNET_JSON_spec_mark_optional (
3978 : GNUNET_JSON_spec_relative_time ("auto_refund",
3979 : &oc->parse_order.auto_refund),
3980 : NULL),
3981 77 : GNUNET_JSON_spec_mark_optional (
3982 : GNUNET_JSON_spec_object_const ("extra",
3983 : &oc->parse_order.extra),
3984 : NULL),
3985 77 : GNUNET_JSON_spec_mark_optional (
3986 : GNUNET_JSON_spec_uint64 ("order_default_money_pot",
3987 : &oc->parse_order.order_default_money_pot),
3988 : NULL),
3989 77 : GNUNET_JSON_spec_end ()
3990 : };
3991 : enum GNUNET_GenericReturnValue ret;
3992 : bool computed_refund_deadline;
3993 :
3994 77 : oc->parse_order.refund_deadline = GNUNET_TIME_UNIT_FOREVER_TS;
3995 77 : oc->parse_order.wire_deadline = GNUNET_TIME_UNIT_FOREVER_TS;
3996 77 : ret = TALER_MHD_parse_json_data (oc->connection,
3997 77 : oc->parse_request.order,
3998 : spec);
3999 77 : if (GNUNET_OK != ret)
4000 : {
4001 0 : GNUNET_break_op (0);
4002 0 : finalize_order2 (oc,
4003 : ret);
4004 2 : return;
4005 : }
4006 77 : if ( (NULL != products) &&
4007 5 : (0 != (oc->parse_order.products_len = json_array_size (products))) )
4008 : {
4009 : size_t i;
4010 : json_t *p;
4011 :
4012 : oc->parse_order.products
4013 5 : = GNUNET_new_array (oc->parse_order.products_len,
4014 : struct TALER_MERCHANT_ProductSold);
4015 10 : json_array_foreach (products, i, p)
4016 : {
4017 5 : if (GNUNET_OK !=
4018 5 : TALER_MERCHANT_parse_product_sold (p,
4019 5 : &oc->parse_order.products[i]))
4020 : {
4021 0 : GNUNET_break_op (0);
4022 0 : reply_with_error (oc,
4023 : MHD_HTTP_BAD_REQUEST,
4024 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
4025 : "order.products");
4026 0 : return;
4027 : }
4028 : }
4029 : }
4030 77 : if (NULL != order_id)
4031 : {
4032 53 : size_t len = strlen (order_id);
4033 :
4034 491 : for (size_t i = 0; i<len; i++)
4035 : {
4036 438 : char c = order_id[i];
4037 :
4038 438 : if (! ( ( (c >= 'A') &&
4039 438 : (c <= 'Z') ) ||
4040 337 : ( (c >= 'a') &&
4041 101 : (c <= 'z') ) ||
4042 54 : ( (c >= '0') &&
4043 47 : (c <= '9') ) ||
4044 0 : (c == '-') ||
4045 0 : (c == '_') ||
4046 : (c == '.') ||
4047 : (c == ':') ) )
4048 : {
4049 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
4050 : "Invalid character `%c' in order ID `%s'\n",
4051 : c,
4052 : order_id);
4053 0 : reply_with_error (oc,
4054 : MHD_HTTP_BAD_REQUEST,
4055 : TALER_EC_GENERIC_CURRENCY_MISMATCH,
4056 : "Invalid character in order_id");
4057 0 : return;
4058 : }
4059 : }
4060 : }
4061 77 : switch (version)
4062 : {
4063 68 : case 0:
4064 : {
4065 : bool no_fee;
4066 68 : const json_t *choices = NULL;
4067 : struct GNUNET_JSON_Specification specv0[] = {
4068 68 : TALER_JSON_spec_amount_any (
4069 : "amount",
4070 : &oc->parse_order.details.v0.brutto),
4071 68 : GNUNET_JSON_spec_mark_optional (
4072 : TALER_JSON_spec_amount_any (
4073 : "max_fee",
4074 : &oc->parse_order.details.v0.max_fee),
4075 : &no_fee),
4076 : /* for sanity check, must be *absent*! */
4077 68 : GNUNET_JSON_spec_mark_optional (
4078 : GNUNET_JSON_spec_array_const ("choices",
4079 : &choices),
4080 : NULL),
4081 68 : GNUNET_JSON_spec_end ()
4082 : };
4083 :
4084 68 : ret = TALER_MHD_parse_json_data (oc->connection,
4085 68 : oc->parse_request.order,
4086 : specv0);
4087 68 : if (GNUNET_OK != ret)
4088 : {
4089 0 : GNUNET_break_op (0);
4090 0 : finalize_order2 (oc,
4091 : ret);
4092 2 : return;
4093 : }
4094 68 : if ( (! no_fee) &&
4095 : (GNUNET_OK !=
4096 0 : TALER_amount_cmp_currency (&oc->parse_order.details.v0.brutto,
4097 0 : &oc->parse_order.details.v0.max_fee)) )
4098 : {
4099 0 : GNUNET_break_op (0);
4100 0 : GNUNET_JSON_parse_free (spec);
4101 0 : reply_with_error (oc,
4102 : MHD_HTTP_BAD_REQUEST,
4103 : TALER_EC_GENERIC_CURRENCY_MISMATCH,
4104 : "different currencies used for 'max_fee' and 'amount' currency");
4105 0 : return;
4106 : }
4107 68 : if (! TMH_test_exchange_configured_for_currency (
4108 68 : oc->parse_order.details.v0.brutto.currency))
4109 : {
4110 2 : GNUNET_break_op (0);
4111 2 : GNUNET_JSON_parse_free (spec);
4112 2 : reply_with_error (oc,
4113 : MHD_HTTP_CONFLICT,
4114 : TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_NO_EXCHANGE_FOR_CURRENCY,
4115 2 : oc->parse_order.details.v0.brutto.currency);
4116 2 : return;
4117 : }
4118 66 : if (NULL != choices)
4119 : {
4120 0 : GNUNET_break_op (0);
4121 0 : GNUNET_JSON_parse_free (spec);
4122 0 : reply_with_error (oc,
4123 : MHD_HTTP_BAD_REQUEST,
4124 : TALER_EC_GENERIC_UNEXPECTED_REQUEST_ERROR,
4125 : "choices array must be null for v0 contracts");
4126 0 : return;
4127 : }
4128 66 : oc->parse_order.version = TALER_MERCHANT_CONTRACT_VERSION_0;
4129 66 : break;
4130 : }
4131 9 : case 1:
4132 : {
4133 : struct GNUNET_JSON_Specification specv1[] = {
4134 9 : GNUNET_JSON_spec_array_const (
4135 : "choices",
4136 : &oc->parse_order.details.v1.choices),
4137 9 : GNUNET_JSON_spec_end ()
4138 : };
4139 :
4140 9 : ret = TALER_MHD_parse_json_data (oc->connection,
4141 9 : oc->parse_request.order,
4142 : specv1);
4143 9 : if (GNUNET_OK != ret)
4144 : {
4145 0 : GNUNET_break_op (0);
4146 0 : finalize_order2 (oc,
4147 : ret);
4148 0 : return;
4149 : }
4150 9 : oc->parse_order.version = TALER_MERCHANT_CONTRACT_VERSION_1;
4151 9 : break;
4152 : }
4153 0 : default:
4154 0 : GNUNET_break_op (0);
4155 0 : GNUNET_JSON_parse_free (spec);
4156 0 : reply_with_error (oc,
4157 : MHD_HTTP_BAD_REQUEST,
4158 : TALER_EC_GENERIC_VERSION_MALFORMED,
4159 : "invalid version specified in order, supported are null, '0' or '1'");
4160 0 : return;
4161 : }
4162 :
4163 : /* Add order_id if it doesn't exist. */
4164 75 : if (NULL != order_id)
4165 : {
4166 51 : oc->parse_order.order_id = GNUNET_strdup (order_id);
4167 : }
4168 : else
4169 : {
4170 : char buf[256];
4171 : time_t timer;
4172 : struct tm *tm_info;
4173 : size_t off;
4174 : uint64_t rand;
4175 : char *last;
4176 :
4177 24 : time (&timer);
4178 24 : tm_info = localtime (&timer);
4179 24 : if (NULL == tm_info)
4180 : {
4181 0 : GNUNET_JSON_parse_free (spec);
4182 0 : reply_with_error (
4183 : oc,
4184 : MHD_HTTP_INTERNAL_SERVER_ERROR,
4185 : TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_NO_LOCALTIME,
4186 : NULL);
4187 0 : return;
4188 : }
4189 24 : off = strftime (buf,
4190 : sizeof (buf) - 1,
4191 : "%Y.%j",
4192 : tm_info);
4193 : /* Check for error state of strftime */
4194 24 : GNUNET_assert (0 != off);
4195 24 : buf[off++] = '-';
4196 24 : rand = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
4197 : UINT64_MAX);
4198 24 : last = GNUNET_STRINGS_data_to_string (&rand,
4199 : sizeof (uint64_t),
4200 : &buf[off],
4201 : sizeof (buf) - off);
4202 24 : GNUNET_assert (NULL != last);
4203 24 : *last = '\0';
4204 :
4205 24 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
4206 : "Assigning order ID `%s' server-side\n",
4207 : buf);
4208 :
4209 24 : oc->parse_order.order_id = GNUNET_strdup (buf);
4210 24 : GNUNET_assert (NULL != oc->parse_order.order_id);
4211 : }
4212 :
4213 : /* Patch fulfillment URL with order_id (implements #6467). */
4214 75 : if (NULL != oc->parse_order.fulfillment_url)
4215 : {
4216 : const char *pos;
4217 :
4218 54 : pos = strstr (oc->parse_order.fulfillment_url,
4219 : "${ORDER_ID}");
4220 54 : if (NULL != pos)
4221 : {
4222 : /* replace ${ORDER_ID} with the real order_id */
4223 : char *nurl;
4224 :
4225 : /* We only allow one placeholder */
4226 0 : if (strstr (pos + strlen ("${ORDER_ID}"),
4227 : "${ORDER_ID}"))
4228 : {
4229 0 : GNUNET_break_op (0);
4230 0 : reply_with_error (oc,
4231 : MHD_HTTP_BAD_REQUEST,
4232 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
4233 : "fulfillment_url");
4234 0 : return;
4235 : }
4236 :
4237 0 : GNUNET_asprintf (&nurl,
4238 : "%.*s%s%s",
4239 : /* first output URL until ${ORDER_ID} */
4240 0 : (int) (pos - oc->parse_order.fulfillment_url),
4241 : oc->parse_order.fulfillment_url,
4242 : /* replace ${ORDER_ID} with the right order_id */
4243 : oc->parse_order.order_id,
4244 : /* append rest of original URL */
4245 : pos + strlen ("${ORDER_ID}"));
4246 :
4247 0 : oc->parse_order.fulfillment_url = GNUNET_strdup (nurl);
4248 :
4249 0 : GNUNET_free (nurl);
4250 : }
4251 : }
4252 :
4253 129 : if ( (GNUNET_TIME_absolute_is_zero (oc->parse_order.pay_deadline.abs_time)) ||
4254 54 : (GNUNET_TIME_absolute_is_never (oc->parse_order.pay_deadline.abs_time)) )
4255 : {
4256 74 : oc->parse_order.pay_deadline = GNUNET_TIME_relative_to_timestamp (
4257 : settings->default_pay_delay);
4258 74 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
4259 : "Pay deadline was zero (or never), setting to %s\n",
4260 : GNUNET_TIME_timestamp2s (oc->parse_order.pay_deadline));
4261 : }
4262 1 : else if (GNUNET_TIME_absolute_is_past (oc->parse_order.pay_deadline.abs_time))
4263 : {
4264 0 : GNUNET_break_op (0);
4265 0 : reply_with_error (
4266 : oc,
4267 : MHD_HTTP_BAD_REQUEST,
4268 : TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_PAY_DEADLINE_IN_PAST,
4269 : NULL);
4270 0 : return;
4271 : }
4272 75 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
4273 : "Pay deadline is %s\n",
4274 : GNUNET_TIME_timestamp2s (oc->parse_order.pay_deadline));
4275 :
4276 : /* Check soundness of refund deadline, and that a timestamp
4277 : * is actually present. */
4278 : {
4279 75 : struct GNUNET_TIME_Timestamp now = GNUNET_TIME_timestamp_get ();
4280 :
4281 : /* Add timestamp if it doesn't exist (or is zero) */
4282 75 : if (GNUNET_TIME_absolute_is_zero (oc->parse_order.timestamp.abs_time))
4283 : {
4284 75 : oc->parse_order.timestamp = now;
4285 : }
4286 :
4287 : /* If no refund_deadline given, set one based on refund_delay. */
4288 75 : if (GNUNET_TIME_absolute_is_never (
4289 : oc->parse_order.refund_deadline.abs_time))
4290 : {
4291 22 : if (GNUNET_TIME_relative_is_zero (oc->parse_request.refund_delay))
4292 : {
4293 10 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
4294 : "Refund delay is zero, no refunds are possible for this order\n");
4295 10 : oc->parse_order.refund_deadline = GNUNET_TIME_UNIT_ZERO_TS;
4296 : }
4297 : else
4298 : {
4299 12 : computed_refund_deadline = true;
4300 : oc->parse_order.refund_deadline
4301 12 : = GNUNET_TIME_absolute_to_timestamp (
4302 : GNUNET_TIME_absolute_add (oc->parse_order.pay_deadline.abs_time,
4303 : oc->parse_request.refund_delay));
4304 : }
4305 : }
4306 :
4307 75 : if ( (! GNUNET_TIME_absolute_is_zero (
4308 0 : oc->parse_order.delivery_date.abs_time)) &&
4309 0 : (GNUNET_TIME_absolute_is_past (
4310 : oc->parse_order.delivery_date.abs_time)) )
4311 : {
4312 0 : GNUNET_break_op (0);
4313 0 : reply_with_error (
4314 : oc,
4315 : MHD_HTTP_BAD_REQUEST,
4316 : TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_DELIVERY_DATE_IN_PAST,
4317 : NULL);
4318 0 : return;
4319 : }
4320 : }
4321 :
4322 75 : if ( (! GNUNET_TIME_absolute_is_zero (
4323 16 : oc->parse_order.refund_deadline.abs_time)) &&
4324 16 : (GNUNET_TIME_absolute_is_past (
4325 : oc->parse_order.refund_deadline.abs_time)) )
4326 : {
4327 0 : GNUNET_break_op (0);
4328 0 : reply_with_error (
4329 : oc,
4330 : MHD_HTTP_BAD_REQUEST,
4331 : TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_REFUND_DEADLINE_IN_PAST,
4332 : NULL);
4333 0 : return;
4334 : }
4335 :
4336 75 : if (GNUNET_TIME_absolute_is_never (oc->parse_order.wire_deadline.abs_time))
4337 : {
4338 : struct GNUNET_TIME_Absolute start;
4339 :
4340 75 : start = GNUNET_TIME_absolute_max (
4341 : oc->parse_order.refund_deadline.abs_time,
4342 : oc->parse_order.pay_deadline.abs_time);
4343 : oc->parse_order.wire_deadline
4344 75 : = GNUNET_TIME_absolute_to_timestamp (
4345 : GNUNET_TIME_round_up (
4346 : GNUNET_TIME_absolute_add (
4347 : start,
4348 : settings->default_wire_transfer_delay),
4349 75 : settings->default_wire_transfer_rounding_interval));
4350 75 : if (GNUNET_TIME_absolute_is_never (
4351 : oc->parse_order.wire_deadline.abs_time))
4352 : {
4353 0 : GNUNET_break_op (0);
4354 0 : reply_with_error (
4355 : oc,
4356 : MHD_HTTP_BAD_REQUEST,
4357 : TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_WIRE_DEADLINE_IS_NEVER,
4358 : "order:wire_transfer_deadline");
4359 0 : return;
4360 : }
4361 : }
4362 0 : else if (computed_refund_deadline)
4363 : {
4364 : /* if we computed the refund_deadline from default settings
4365 : and did have a configured wire_deadline, make sure that
4366 : the refund_deadline is at or below the wire_deadline. */
4367 : oc->parse_order.refund_deadline
4368 0 : = GNUNET_TIME_timestamp_min (oc->parse_order.refund_deadline,
4369 : oc->parse_order.wire_deadline);
4370 : }
4371 75 : if (GNUNET_TIME_timestamp_cmp (oc->parse_order.wire_deadline,
4372 : <,
4373 : oc->parse_order.refund_deadline))
4374 : {
4375 0 : GNUNET_break_op (0);
4376 0 : reply_with_error (
4377 : oc,
4378 : MHD_HTTP_BAD_REQUEST,
4379 : TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_REFUND_AFTER_WIRE_DEADLINE,
4380 : "order:wire_transfer_deadline;order:refund_deadline");
4381 0 : return;
4382 : }
4383 :
4384 75 : if (NULL != merchant_base_url)
4385 : {
4386 0 : if (('\0' == *merchant_base_url) ||
4387 0 : ('/' != merchant_base_url[strlen (merchant_base_url) - 1]))
4388 : {
4389 0 : GNUNET_break_op (0);
4390 0 : reply_with_error (
4391 : oc,
4392 : MHD_HTTP_BAD_REQUEST,
4393 : TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_PROPOSAL_PARSE_ERROR,
4394 : "merchant_base_url is not valid");
4395 0 : return;
4396 : }
4397 : oc->parse_order.merchant_base_url
4398 0 : = GNUNET_strdup (merchant_base_url);
4399 : }
4400 : else
4401 : {
4402 : char *url;
4403 :
4404 75 : url = make_merchant_base_url (oc->connection,
4405 75 : settings->id);
4406 75 : if (NULL == url)
4407 : {
4408 0 : GNUNET_break_op (0);
4409 0 : reply_with_error (
4410 : oc,
4411 : MHD_HTTP_BAD_REQUEST,
4412 : TALER_EC_GENERIC_PARAMETER_MISSING,
4413 : "order:merchant_base_url");
4414 0 : return;
4415 : }
4416 75 : oc->parse_order.merchant_base_url = url;
4417 : }
4418 :
4419 : /* Merchant information must not already be present */
4420 75 : if (NULL != jmerchant)
4421 : {
4422 0 : GNUNET_break_op (0);
4423 0 : reply_with_error (
4424 : oc,
4425 : MHD_HTTP_BAD_REQUEST,
4426 : TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_PROPOSAL_PARSE_ERROR,
4427 : "'merchant' field already set, but must be provided by backend");
4428 0 : return;
4429 : }
4430 :
4431 75 : if ( (NULL != oc->parse_order.delivery_location) &&
4432 0 : (! TMH_location_object_valid (oc->parse_order.delivery_location)) )
4433 : {
4434 0 : GNUNET_break_op (0);
4435 0 : reply_with_error (oc,
4436 : MHD_HTTP_BAD_REQUEST,
4437 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
4438 : "delivery_location");
4439 0 : return;
4440 : }
4441 :
4442 75 : oc->phase++;
4443 : }
4444 :
4445 :
4446 : /* ***************** ORDER_PHASE_PARSE_REQUEST **************** */
4447 :
4448 : /**
4449 : * Parse the client request. Upon success,
4450 : * continue processing by calling parse_order().
4451 : *
4452 : * @param[in,out] oc order context to process
4453 : */
4454 : static void
4455 77 : phase_parse_request (struct OrderContext *oc)
4456 : {
4457 77 : const json_t *ip = NULL;
4458 77 : const json_t *uuid = NULL;
4459 77 : const char *otp_id = NULL;
4460 77 : bool create_token = true; /* default */
4461 : struct GNUNET_JSON_Specification spec[] = {
4462 77 : GNUNET_JSON_spec_json ("order",
4463 : &oc->parse_request.order),
4464 77 : GNUNET_JSON_spec_mark_optional (
4465 : GNUNET_JSON_spec_relative_time ("refund_delay",
4466 : &oc->parse_request.refund_delay),
4467 : NULL),
4468 77 : GNUNET_JSON_spec_mark_optional (
4469 : GNUNET_JSON_spec_string ("payment_target",
4470 : &oc->parse_request.payment_target),
4471 : NULL),
4472 77 : GNUNET_JSON_spec_mark_optional (
4473 : GNUNET_JSON_spec_array_const ("inventory_products",
4474 : &ip),
4475 : NULL),
4476 77 : GNUNET_JSON_spec_mark_optional (
4477 : GNUNET_JSON_spec_string ("session_id",
4478 : &oc->parse_request.session_id),
4479 : NULL),
4480 77 : GNUNET_JSON_spec_mark_optional (
4481 : GNUNET_JSON_spec_array_const ("lock_uuids",
4482 : &uuid),
4483 : NULL),
4484 77 : GNUNET_JSON_spec_mark_optional (
4485 : GNUNET_JSON_spec_bool ("create_token",
4486 : &create_token),
4487 : NULL),
4488 77 : GNUNET_JSON_spec_mark_optional (
4489 : GNUNET_JSON_spec_string ("otp_id",
4490 : &otp_id),
4491 : NULL),
4492 77 : GNUNET_JSON_spec_end ()
4493 : };
4494 : enum GNUNET_GenericReturnValue ret;
4495 :
4496 : oc->parse_request.refund_delay
4497 77 : = oc->hc->instance->settings.default_refund_delay;
4498 77 : ret = TALER_MHD_parse_json_data (oc->connection,
4499 77 : oc->hc->request_body,
4500 : spec);
4501 77 : if (GNUNET_OK != ret)
4502 : {
4503 0 : GNUNET_break_op (0);
4504 0 : finalize_order2 (oc,
4505 : ret);
4506 0 : return;
4507 : }
4508 77 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
4509 : "Refund delay is %s\n",
4510 : GNUNET_TIME_relative2s (oc->parse_request.refund_delay,
4511 : false));
4512 77 : TMH_db->expire_locks (TMH_db->cls);
4513 77 : if (NULL != otp_id)
4514 : {
4515 : struct TALER_MERCHANTDB_OtpDeviceDetails td;
4516 : enum GNUNET_DB_QueryStatus qs;
4517 :
4518 2 : memset (&td,
4519 : 0,
4520 : sizeof (td));
4521 2 : qs = TMH_db->select_otp (TMH_db->cls,
4522 2 : oc->hc->instance->settings.id,
4523 : otp_id,
4524 : &td);
4525 2 : switch (qs)
4526 : {
4527 0 : case GNUNET_DB_STATUS_HARD_ERROR:
4528 0 : GNUNET_break (0);
4529 0 : reply_with_error (oc,
4530 : MHD_HTTP_INTERNAL_SERVER_ERROR,
4531 : TALER_EC_GENERIC_DB_FETCH_FAILED,
4532 : "select_otp");
4533 0 : return;
4534 0 : case GNUNET_DB_STATUS_SOFT_ERROR:
4535 0 : GNUNET_break (0);
4536 0 : reply_with_error (oc,
4537 : MHD_HTTP_INTERNAL_SERVER_ERROR,
4538 : TALER_EC_GENERIC_DB_SOFT_FAILURE,
4539 : "select_otp");
4540 0 : return;
4541 0 : case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
4542 0 : reply_with_error (oc,
4543 : MHD_HTTP_NOT_FOUND,
4544 : TALER_EC_MERCHANT_GENERIC_OTP_DEVICE_UNKNOWN,
4545 : otp_id);
4546 0 : return;
4547 2 : case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
4548 2 : break;
4549 : }
4550 2 : oc->parse_request.pos_key = td.otp_key;
4551 2 : oc->parse_request.pos_algorithm = td.otp_algorithm;
4552 2 : GNUNET_free (td.otp_description);
4553 : }
4554 77 : if (create_token)
4555 : {
4556 70 : GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE,
4557 70 : &oc->parse_request.claim_token,
4558 : sizeof (oc->parse_request.claim_token));
4559 : }
4560 : /* Compute h_post_data (for idempotency check) */
4561 : {
4562 : char *req_body_enc;
4563 :
4564 : /* Dump normalized JSON to string. */
4565 77 : if (NULL == (req_body_enc
4566 77 : = json_dumps (oc->hc->request_body,
4567 : JSON_ENCODE_ANY
4568 : | JSON_COMPACT
4569 : | JSON_SORT_KEYS)))
4570 : {
4571 0 : GNUNET_break (0);
4572 0 : GNUNET_JSON_parse_free (spec);
4573 0 : reply_with_error (oc,
4574 : MHD_HTTP_INTERNAL_SERVER_ERROR,
4575 : TALER_EC_GENERIC_ALLOCATION_FAILURE,
4576 : "request body normalization for hashing");
4577 0 : return;
4578 : }
4579 77 : GNUNET_CRYPTO_hash (req_body_enc,
4580 : strlen (req_body_enc),
4581 : &oc->parse_request.h_post_data.hash);
4582 77 : GNUNET_free (req_body_enc);
4583 : }
4584 :
4585 : /* parse the inventory_products (optionally given) */
4586 77 : if (NULL != ip)
4587 : {
4588 19 : unsigned int ipl = (unsigned int) json_array_size (ip);
4589 :
4590 19 : if ( (json_array_size (ip) != (size_t) ipl) ||
4591 : (ipl > MAX_PRODUCTS) )
4592 : {
4593 0 : GNUNET_break (0);
4594 0 : GNUNET_JSON_parse_free (spec);
4595 0 : reply_with_error (oc,
4596 : MHD_HTTP_INTERNAL_SERVER_ERROR,
4597 : TALER_EC_GENERIC_ALLOCATION_FAILURE,
4598 : "inventory products too long");
4599 0 : return;
4600 : }
4601 19 : GNUNET_array_grow (oc->parse_request.inventory_products,
4602 : oc->parse_request.inventory_products_length,
4603 : (unsigned int) json_array_size (ip));
4604 38 : for (unsigned int i = 0; i<oc->parse_request.inventory_products_length; i++)
4605 : {
4606 19 : struct InventoryProduct *ipr = &oc->parse_request.inventory_products[i];
4607 : const char *error_name;
4608 : unsigned int error_line;
4609 : struct GNUNET_JSON_Specification ispec[] = {
4610 19 : GNUNET_JSON_spec_string ("product_id",
4611 : &ipr->product_id),
4612 19 : GNUNET_JSON_spec_mark_optional (
4613 : GNUNET_JSON_spec_uint64 ("quantity",
4614 : &ipr->quantity),
4615 : &ipr->quantity_missing),
4616 19 : GNUNET_JSON_spec_mark_optional (
4617 : GNUNET_JSON_spec_string ("unit_quantity",
4618 : &ipr->unit_quantity),
4619 : &ipr->unit_quantity_missing),
4620 19 : GNUNET_JSON_spec_mark_optional (
4621 : GNUNET_JSON_spec_uint64 ("product_money_pot",
4622 : &ipr->product_money_pot),
4623 : &ipr->quantity_missing),
4624 19 : GNUNET_JSON_spec_end ()
4625 : };
4626 :
4627 19 : ret = GNUNET_JSON_parse (json_array_get (ip,
4628 : i),
4629 : ispec,
4630 : &error_name,
4631 : &error_line);
4632 19 : if (GNUNET_OK != ret)
4633 : {
4634 0 : GNUNET_break_op (0);
4635 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
4636 : "Product parsing failed at #%u: %s:%u\n",
4637 : i,
4638 : error_name,
4639 : error_line);
4640 0 : reply_with_error (oc,
4641 : MHD_HTTP_BAD_REQUEST,
4642 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
4643 : "inventory_products");
4644 0 : return;
4645 : }
4646 19 : if (ipr->quantity_missing && ipr->unit_quantity_missing)
4647 : {
4648 0 : ipr->quantity = 1;
4649 0 : ipr->quantity_missing = false;
4650 : }
4651 : }
4652 : }
4653 :
4654 : /* parse the lock_uuids (optionally given) */
4655 77 : if (NULL != uuid)
4656 : {
4657 3 : GNUNET_array_grow (oc->parse_request.uuids,
4658 : oc->parse_request.uuids_length,
4659 : json_array_size (uuid));
4660 6 : for (unsigned int i = 0; i<oc->parse_request.uuids_length; i++)
4661 : {
4662 3 : json_t *ui = json_array_get (uuid,
4663 : i);
4664 :
4665 3 : if (! json_is_string (ui))
4666 : {
4667 0 : GNUNET_break_op (0);
4668 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
4669 : "UUID parsing failed at #%u\n",
4670 : i);
4671 0 : reply_with_error (oc,
4672 : MHD_HTTP_BAD_REQUEST,
4673 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
4674 : "lock_uuids");
4675 0 : return;
4676 : }
4677 3 : TMH_uuid_from_string (json_string_value (ui),
4678 3 : &oc->parse_request.uuids[i]);
4679 : }
4680 : }
4681 77 : oc->phase++;
4682 : }
4683 :
4684 :
4685 : /* ***************** Main handler **************** */
4686 :
4687 :
4688 : MHD_RESULT
4689 142 : TMH_private_post_orders (
4690 : const struct TMH_RequestHandler *rh,
4691 : struct MHD_Connection *connection,
4692 : struct TMH_HandlerContext *hc)
4693 : {
4694 142 : struct OrderContext *oc = hc->ctx;
4695 :
4696 142 : if (NULL == oc)
4697 : {
4698 77 : oc = GNUNET_new (struct OrderContext);
4699 77 : hc->ctx = oc;
4700 77 : hc->cc = &clean_order;
4701 77 : oc->connection = connection;
4702 77 : oc->hc = hc;
4703 : }
4704 : while (1)
4705 : {
4706 1850 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
4707 : "Processing order in phase %d\n",
4708 : oc->phase);
4709 996 : switch (oc->phase)
4710 : {
4711 77 : case ORDER_PHASE_PARSE_REQUEST:
4712 77 : phase_parse_request (oc);
4713 77 : break;
4714 77 : case ORDER_PHASE_PARSE_ORDER:
4715 77 : phase_parse_order (oc);
4716 77 : break;
4717 75 : case ORDER_PHASE_PARSE_CHOICES:
4718 75 : phase_parse_choices (oc);
4719 75 : break;
4720 75 : case ORDER_PHASE_MERGE_INVENTORY:
4721 75 : phase_merge_inventory (oc);
4722 75 : break;
4723 71 : case ORDER_PHASE_ADD_PAYMENT_DETAILS:
4724 71 : phase_add_payment_details (oc);
4725 71 : break;
4726 134 : case ORDER_PHASE_SET_EXCHANGES:
4727 134 : if (phase_set_exchanges (oc))
4728 65 : return MHD_YES;
4729 69 : break;
4730 65 : case ORDER_PHASE_SELECT_WIRE_METHOD:
4731 65 : phase_select_wire_method (oc);
4732 65 : break;
4733 69 : case ORDER_PHASE_SET_MAX_FEE:
4734 69 : phase_set_max_fee (oc);
4735 69 : break;
4736 69 : case ORDER_PHASE_SERIALIZE_ORDER:
4737 69 : phase_serialize_order (oc);
4738 69 : break;
4739 69 : case ORDER_PHASE_CHECK_CONTRACT:
4740 69 : phase_check_contract (oc);
4741 69 : break;
4742 69 : case ORDER_PHASE_SALT_FORGETTABLE:
4743 69 : phase_salt_forgettable (oc);
4744 69 : break;
4745 69 : case ORDER_PHASE_EXECUTE_ORDER:
4746 69 : phase_execute_order (oc);
4747 69 : break;
4748 77 : case ORDER_PHASE_FINISHED_MHD_YES:
4749 77 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
4750 : "Finished processing order (1)\n");
4751 77 : return MHD_YES;
4752 0 : case ORDER_PHASE_FINISHED_MHD_NO:
4753 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
4754 : "Finished processing order (0)\n");
4755 0 : return MHD_NO;
4756 : }
4757 : }
4758 : }
4759 :
4760 :
4761 : /* end of taler-merchant-httpd_private-post-orders.c */
|