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