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