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